Merge branch 'v4l_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab...
authorLinus Torvalds <torvalds@linux-foundation.org>
Sun, 7 Oct 2012 08:49:05 +0000 (17:49 +0900)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sun, 7 Oct 2012 08:49:05 +0000 (17:49 +0900)
Pull media updates from Mauro Carvalho Chehab:
 "The first part of the media updates for Kernel 3.7.

  This series contain:

   - A major tree renaming patch series: now, drivers are organized
     internally by their used bus, instead of by V4L2 and/or DVB API,
     providing a cleaner driver location for hybrid drivers that
     implement both APIs, and allowing to cleanup the Kconfig items and
     make them more intuitive for the end user;

   - Media Kernel developers are typically very lazy with their duties
     of keeping the MAINTAINERS entries for their drivers updated.  As
     now the tree is more organized, we're doing an effort to add/update
     those entries for the drivers that aren't currently orphan;

   - Several DVB USB drivers got moved to a new DVB USB v2 core; the new
     core fixes several bugs (as the existing one that got bitroted).
     Now, suspend/resume finally started to work fine (at least with
     some devices - we should expect more work with regards to it);

   - added multistream support for DVB-T2, and unified the API for
     DVB-S2 and ISDB-S.  Backward binary support is preserved;

   - as usual, a few new drivers, some V4L2 core improvements and lots
     of drivers improvements and fixes.

  There are some points to notice on this series:

   1) you should expect a trivial merge conflict on your tree, with the
      removal of Documentation/feature-removal-schedule.txt: this series
      would be adding two additional entries there.  I opted to not
      rebase it due to this recent change;

   2) With regards to the PCTV 520e udev-related breakage, I opted to
      fix it in a way that the patches can be backported to 3.5 even
      without your firmware fix patch.  This way, Greg doesn't need to
      rush backporting your patch (as there are still the firmware cache
      and firmware path customization issues to be addressed there).

      I'll send later a patch (likely after the end of the merge window)
      reverting the rest of the DRX-K async firmware request, fully
      restoring its original behaviour to allow media drivers to
      initialize everything serialized as before for 3.7 and upper.

   3) I'm planning to work on this weekend to test the DMABUF patches
      for V4L2.  The patches are on my queue for several Kernel cycles,
      but, up to now, there is/was no way to test the series locally.

      I have some concerns about this particular changeset with regards
      to security issues, and with regards to the replacement of the old
      VIDIOC_OVERLAY ioctl's that is broken on modern systems, due to
      GPU drivers change.  The Overlay API allows direct PCI2PCI
      transfers from a media capture card into the GPU framebuffer, but
      its API is crappy.  Also, the only existing X11 driver that
      implements it requires a XV extension that is not available
      anymore on modern drivers.  The DMABUF can do the same thing, but
      with it is promising to be a properly-designed API.  If I can
      successfully test this series and be happy with it, I should be
      asking you to pull them next week."

* 'v4l_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (717 commits)
  em28xx: regression fix: use DRX-K sync firmware requests on em28xx
  drxk: allow loading firmware synchrousnously
  em28xx: Make all em28xx extensions to be initialized asynchronously
  [media] tda18271: properly report read errors in tda18271_get_id
  [media] tda18271: delay IR & RF calibration until init() if delay_cal is set
  [media] MAINTAINERS: add Michael Krufky as tda827x maintainer
  [media] MAINTAINERS: add Michael Krufky as tda8290 maintainer
  [media] MAINTAINERS: add Michael Krufky as cxusb maintainer
  [media] MAINTAINERS: add Michael Krufky as lg2160 maintainer
  [media] MAINTAINERS: add Michael Krufky as lgdt3305 maintainer
  [media] MAINTAINERS: add Michael Krufky as mxl111sf maintainer
  [media] MAINTAINERS: add Michael Krufky as mxl5007t maintainer
  [media] MAINTAINERS: add Michael Krufky as tda18271 maintainer
  [media] s5p-tv: Report only multi-plane capabilities in vidioc_querycap
  [media] s5p-mfc: Fix misplaced return statement in s5p_mfc_suspend()
  [media] exynos-gsc: Add missing static storage class specifiers
  [media] exynos-gsc: Remove <linux/version.h> header file inclusion
  [media] s5p-fimc: Fix incorrect condition in fimc_lite_reqbufs()
  [media] s5p-tv: Fix potential NULL pointer dereference error
  [media] s5k6aa: Fix possible NULL pointer dereference
  ...

27 files changed:
1  2 
MAINTAINERS
arch/arm/mach-omap2/board-rx51-peripherals.c
arch/arm/plat-mxc/include/mach/devices-common.h
drivers/gpio/gpio-bt8xx.c
drivers/media/dvb-core/dvb_net.c
drivers/media/pci/bt8xx/bttv-driver.c
drivers/media/pci/cx18/cx18-driver.c
drivers/media/pci/cx23885/cx23885-input.c
drivers/media/pci/cx88/cx88-mpeg.c
drivers/media/pci/mantis/mantis_evm.c
drivers/media/pci/mantis/mantis_uart.c
drivers/media/pci/ngene/ngene-cards.c
drivers/media/pci/saa7134/saa7134-core.c
drivers/media/pci/saa7134/saa7134-empress.c
drivers/media/platform/davinci/vpbe_venc.c
drivers/media/platform/omap/omap_vout.c
drivers/media/platform/omap24xxcam.c
drivers/media/platform/omap3isp/isp.c
drivers/media/platform/s5p-fimc/mipi-csis.c
drivers/media/platform/soc_camera/mx1_camera.c
drivers/media/platform/soc_camera/mx2_camera.c
drivers/media/platform/soc_camera/mx3_camera.c
drivers/media/platform/soc_camera/pxa_camera.c
drivers/media/usb/cx231xx/cx231xx-cards.c
drivers/media/usb/em28xx/em28xx-cards.c
drivers/media/usb/tm6000/tm6000-cards.c
include/linux/Kbuild

diff --cc MAINTAINERS
Simple merge
  #include "common.h"
  #include <plat/dma.h>
  #include <plat/gpmc.h>
 -#include <plat/onenand.h>
 -#include <plat/gpmc-smc91x.h>
+ #include <plat/omap-pm.h>
 +#include "gpmc-smc91x.h"
  
 -#include <mach/board-rx51.h>
 +#include "board-rx51.h"
  
  #include <sound/tlv320aic3x.h>
  #include <sound/tpa6130a2-plat.h>
Simple merge
Simple merge
index 0000000,16f5ca2..b68918c
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,4630 +1,4630 @@@
 -      flush_work_sync(&dev->request_module_wk);
+ /*
+     bttv - Bt848 frame grabber driver
+     Copyright (C) 1996,97,98 Ralph  Metzler <rjkm@thp.uni-koeln.de>
+                          & Marcus Metzler <mocm@thp.uni-koeln.de>
+     (c) 1999-2002 Gerd Knorr <kraxel@bytesex.org>
+     some v4l2 code lines are taken from Justin's bttv2 driver which is
+     (c) 2000 Justin Schoeman <justin@suntiger.ee.up.ac.za>
+     V4L1 removal from:
+     (c) 2005-2006 Nickolay V. Shmyrev <nshmyrev@yandex.ru>
+     Fixes to be fully V4L2 compliant by
+     (c) 2006 Mauro Carvalho Chehab <mchehab@infradead.org>
+     Cropping and overscan support
+     Copyright (C) 2005, 2006 Michael H. Schimek <mschimek@gmx.at>
+     Sponsored by OPQ Systems AB
+     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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+ #include <linux/init.h>
+ #include <linux/module.h>
+ #include <linux/delay.h>
+ #include <linux/slab.h>
+ #include <linux/errno.h>
+ #include <linux/fs.h>
+ #include <linux/kernel.h>
+ #include <linux/sched.h>
+ #include <linux/interrupt.h>
+ #include <linux/kdev_t.h>
+ #include "bttvp.h"
+ #include <media/v4l2-common.h>
+ #include <media/v4l2-ioctl.h>
+ #include <media/tvaudio.h>
+ #include <media/msp3400.h>
+ #include <linux/dma-mapping.h>
+ #include <asm/io.h>
+ #include <asm/byteorder.h>
+ #include <media/saa6588.h>
+ #define BTTV_VERSION "0.9.19"
+ unsigned int bttv_num;                        /* number of Bt848s in use */
+ struct bttv *bttvs[BTTV_MAX];
+ unsigned int bttv_debug;
+ unsigned int bttv_verbose = 1;
+ unsigned int bttv_gpio;
+ /* config variables */
+ #ifdef __BIG_ENDIAN
+ static unsigned int bigendian=1;
+ #else
+ static unsigned int bigendian;
+ #endif
+ static unsigned int radio[BTTV_MAX];
+ static unsigned int irq_debug;
+ static unsigned int gbuffers = 8;
+ static unsigned int gbufsize = 0x208000;
+ static unsigned int reset_crop = 1;
+ static int video_nr[BTTV_MAX] = { [0 ... (BTTV_MAX-1)] = -1 };
+ static int radio_nr[BTTV_MAX] = { [0 ... (BTTV_MAX-1)] = -1 };
+ static int vbi_nr[BTTV_MAX] = { [0 ... (BTTV_MAX-1)] = -1 };
+ static int debug_latency;
+ static int disable_ir;
+ static unsigned int fdsr;
+ /* options */
+ static unsigned int combfilter;
+ static unsigned int lumafilter;
+ static unsigned int automute    = 1;
+ static unsigned int chroma_agc;
+ static unsigned int adc_crush   = 1;
+ static unsigned int whitecrush_upper = 0xCF;
+ static unsigned int whitecrush_lower = 0x7F;
+ static unsigned int vcr_hack;
+ static unsigned int irq_iswitch;
+ static unsigned int uv_ratio    = 50;
+ static unsigned int full_luma_range;
+ static unsigned int coring;
+ /* API features (turn on/off stuff for testing) */
+ static unsigned int v4l2        = 1;
+ /* insmod args */
+ module_param(bttv_verbose,      int, 0644);
+ module_param(bttv_gpio,         int, 0644);
+ module_param(bttv_debug,        int, 0644);
+ module_param(irq_debug,         int, 0644);
+ module_param(debug_latency,     int, 0644);
+ module_param(disable_ir,        int, 0444);
+ module_param(fdsr,              int, 0444);
+ module_param(gbuffers,          int, 0444);
+ module_param(gbufsize,          int, 0444);
+ module_param(reset_crop,        int, 0444);
+ module_param(v4l2,              int, 0644);
+ module_param(bigendian,         int, 0644);
+ module_param(irq_iswitch,       int, 0644);
+ module_param(combfilter,        int, 0444);
+ module_param(lumafilter,        int, 0444);
+ module_param(automute,          int, 0444);
+ module_param(chroma_agc,        int, 0444);
+ module_param(adc_crush,         int, 0444);
+ module_param(whitecrush_upper,  int, 0444);
+ module_param(whitecrush_lower,  int, 0444);
+ module_param(vcr_hack,          int, 0444);
+ module_param(uv_ratio,          int, 0444);
+ module_param(full_luma_range,   int, 0444);
+ module_param(coring,            int, 0444);
+ module_param_array(radio,       int, NULL, 0444);
+ module_param_array(video_nr,    int, NULL, 0444);
+ module_param_array(radio_nr,    int, NULL, 0444);
+ module_param_array(vbi_nr,      int, NULL, 0444);
+ MODULE_PARM_DESC(radio,"The TV card supports radio, default is 0 (no)");
+ MODULE_PARM_DESC(bigendian,"byte order of the framebuffer, default is native endian");
+ MODULE_PARM_DESC(bttv_verbose,"verbose startup messages, default is 1 (yes)");
+ MODULE_PARM_DESC(bttv_gpio,"log gpio changes, default is 0 (no)");
+ MODULE_PARM_DESC(bttv_debug,"debug messages, default is 0 (no)");
+ MODULE_PARM_DESC(irq_debug,"irq handler debug messages, default is 0 (no)");
+ MODULE_PARM_DESC(disable_ir, "disable infrared remote support");
+ MODULE_PARM_DESC(gbuffers,"number of capture buffers. range 2-32, default 8");
+ MODULE_PARM_DESC(gbufsize,"size of the capture buffers, default is 0x208000");
+ MODULE_PARM_DESC(reset_crop,"reset cropping parameters at open(), default "
+                "is 1 (yes) for compatibility with older applications");
+ MODULE_PARM_DESC(automute,"mute audio on bad/missing video signal, default is 1 (yes)");
+ MODULE_PARM_DESC(chroma_agc,"enables the AGC of chroma signal, default is 0 (no)");
+ MODULE_PARM_DESC(adc_crush,"enables the luminance ADC crush, default is 1 (yes)");
+ MODULE_PARM_DESC(whitecrush_upper,"sets the white crush upper value, default is 207");
+ MODULE_PARM_DESC(whitecrush_lower,"sets the white crush lower value, default is 127");
+ MODULE_PARM_DESC(vcr_hack,"enables the VCR hack (improves synch on poor VCR tapes), default is 0 (no)");
+ MODULE_PARM_DESC(irq_iswitch,"switch inputs in irq handler");
+ MODULE_PARM_DESC(uv_ratio,"ratio between u and v gains, default is 50");
+ MODULE_PARM_DESC(full_luma_range,"use the full luma range, default is 0 (no)");
+ MODULE_PARM_DESC(coring,"set the luma coring level, default is 0 (no)");
+ MODULE_PARM_DESC(video_nr, "video device numbers");
+ MODULE_PARM_DESC(vbi_nr, "vbi device numbers");
+ MODULE_PARM_DESC(radio_nr, "radio device numbers");
+ MODULE_DESCRIPTION("bttv - v4l/v4l2 driver module for bt848/878 based cards");
+ MODULE_AUTHOR("Ralph Metzler & Marcus Metzler & Gerd Knorr");
+ MODULE_LICENSE("GPL");
+ MODULE_VERSION(BTTV_VERSION);
+ /* ----------------------------------------------------------------------- */
+ /* sysfs                                                                   */
+ static ssize_t show_card(struct device *cd,
+                        struct device_attribute *attr, char *buf)
+ {
+       struct video_device *vfd = container_of(cd, struct video_device, dev);
+       struct bttv *btv = video_get_drvdata(vfd);
+       return sprintf(buf, "%d\n", btv ? btv->c.type : UNSET);
+ }
+ static DEVICE_ATTR(card, S_IRUGO, show_card, NULL);
+ /* ----------------------------------------------------------------------- */
+ /* dvb auto-load setup                                                     */
+ #if defined(CONFIG_MODULES) && defined(MODULE)
+ static void request_module_async(struct work_struct *work)
+ {
+       request_module("dvb-bt8xx");
+ }
+ static void request_modules(struct bttv *dev)
+ {
+       INIT_WORK(&dev->request_module_wk, request_module_async);
+       schedule_work(&dev->request_module_wk);
+ }
+ static void flush_request_modules(struct bttv *dev)
+ {
++      flush_work(&dev->request_module_wk);
+ }
+ #else
+ #define request_modules(dev)
+ #define flush_request_modules(dev)
+ #endif /* CONFIG_MODULES */
+ /* ----------------------------------------------------------------------- */
+ /* static data                                                             */
+ /* special timing tables from conexant... */
+ static u8 SRAM_Table[][60] =
+ {
+       /* PAL digital input over GPIO[7:0] */
+       {
+               45, // 45 bytes following
+               0x36,0x11,0x01,0x00,0x90,0x02,0x05,0x10,0x04,0x16,
+               0x12,0x05,0x11,0x00,0x04,0x12,0xC0,0x00,0x31,0x00,
+               0x06,0x51,0x08,0x03,0x89,0x08,0x07,0xC0,0x44,0x00,
+               0x81,0x01,0x01,0xA9,0x0D,0x02,0x02,0x50,0x03,0x37,
+               0x37,0x00,0xAF,0x21,0x00
+       },
+       /* NTSC digital input over GPIO[7:0] */
+       {
+               51, // 51 bytes following
+               0x0C,0xC0,0x00,0x00,0x90,0x02,0x03,0x10,0x03,0x06,
+               0x10,0x04,0x12,0x12,0x05,0x02,0x13,0x04,0x19,0x00,
+               0x04,0x39,0x00,0x06,0x59,0x08,0x03,0x83,0x08,0x07,
+               0x03,0x50,0x00,0xC0,0x40,0x00,0x86,0x01,0x01,0xA6,
+               0x0D,0x02,0x03,0x11,0x01,0x05,0x37,0x00,0xAC,0x21,
+               0x00,
+       },
+       // TGB_NTSC392 // quartzsight
+       // This table has been modified to be used for Fusion Rev D
+       {
+               0x2A, // size of table = 42
+               0x06, 0x08, 0x04, 0x0a, 0xc0, 0x00, 0x18, 0x08, 0x03, 0x24,
+               0x08, 0x07, 0x02, 0x90, 0x02, 0x08, 0x10, 0x04, 0x0c, 0x10,
+               0x05, 0x2c, 0x11, 0x04, 0x55, 0x48, 0x00, 0x05, 0x50, 0x00,
+               0xbf, 0x0c, 0x02, 0x2f, 0x3d, 0x00, 0x2f, 0x3f, 0x00, 0xc3,
+               0x20, 0x00
+       }
+ };
+ /* minhdelayx1        first video pixel we can capture on a line and
+    hdelayx1   start of active video, both relative to rising edge of
+               /HRESET pulse (0H) in 1 / fCLKx1.
+    swidth     width of active video and
+    totalwidth total line width, both in 1 / fCLKx1.
+    sqwidth    total line width in square pixels.
+    vdelay     start of active video in 2 * field lines relative to
+               trailing edge of /VRESET pulse (VDELAY register).
+    sheight    height of active video in 2 * field lines.
+    videostart0        ITU-R frame line number of the line corresponding
+               to vdelay in the first field. */
+ #define CROPCAP(minhdelayx1, hdelayx1, swidth, totalwidth, sqwidth,    \
+               vdelay, sheight, videostart0)                            \
+       .cropcap.bounds.left = minhdelayx1,                              \
+       /* * 2 because vertically we count field lines times two, */     \
+       /* e.g. 23 * 2 to 23 * 2 + 576 in PAL-BGHI defrect. */           \
+       .cropcap.bounds.top = (videostart0) * 2 - (vdelay) + MIN_VDELAY, \
+       /* 4 is a safety margin at the end of the line. */               \
+       .cropcap.bounds.width = (totalwidth) - (minhdelayx1) - 4,        \
+       .cropcap.bounds.height = (sheight) + (vdelay) - MIN_VDELAY,      \
+       .cropcap.defrect.left = hdelayx1,                                \
+       .cropcap.defrect.top = (videostart0) * 2,                        \
+       .cropcap.defrect.width = swidth,                                 \
+       .cropcap.defrect.height = sheight,                               \
+       .cropcap.pixelaspect.numerator = totalwidth,                     \
+       .cropcap.pixelaspect.denominator = sqwidth,
+ const struct bttv_tvnorm bttv_tvnorms[] = {
+       /* PAL-BDGHI */
+       /* max. active video is actually 922, but 924 is divisible by 4 and 3! */
+       /* actually, max active PAL with HSCALE=0 is 948, NTSC is 768 - nil */
+       {
+               .v4l2_id        = V4L2_STD_PAL,
+               .name           = "PAL",
+               .Fsc            = 35468950,
+               .swidth         = 924,
+               .sheight        = 576,
+               .totalwidth     = 1135,
+               .adelay         = 0x7f,
+               .bdelay         = 0x72,
+               .iform          = (BT848_IFORM_PAL_BDGHI|BT848_IFORM_XT1),
+               .scaledtwidth   = 1135,
+               .hdelayx1       = 186,
+               .hactivex1      = 924,
+               .vdelay         = 0x20,
+               .vbipack        = 255, /* min (2048 / 4, 0x1ff) & 0xff */
+               .sram           = 0,
+               /* ITU-R frame line number of the first VBI line
+                  we can capture, of the first and second field.
+                  The last line is determined by cropcap.bounds. */
+               .vbistart       = { 7, 320 },
+               CROPCAP(/* minhdelayx1 */ 68,
+                       /* hdelayx1 */ 186,
+                       /* Should be (768 * 1135 + 944 / 2) / 944.
+                          cropcap.defrect is used for image width
+                          checks, so we keep the old value 924. */
+                       /* swidth */ 924,
+                       /* totalwidth */ 1135,
+                       /* sqwidth */ 944,
+                       /* vdelay */ 0x20,
+                       /* sheight */ 576,
+                       /* videostart0 */ 23)
+               /* bt878 (and bt848?) can capture another
+                  line below active video. */
+               .cropcap.bounds.height = (576 + 2) + 0x20 - 2,
+       },{
+               .v4l2_id        = V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_KR,
+               .name           = "NTSC",
+               .Fsc            = 28636363,
+               .swidth         = 768,
+               .sheight        = 480,
+               .totalwidth     = 910,
+               .adelay         = 0x68,
+               .bdelay         = 0x5d,
+               .iform          = (BT848_IFORM_NTSC|BT848_IFORM_XT0),
+               .scaledtwidth   = 910,
+               .hdelayx1       = 128,
+               .hactivex1      = 910,
+               .vdelay         = 0x1a,
+               .vbipack        = 144, /* min (1600 / 4, 0x1ff) & 0xff */
+               .sram           = 1,
+               .vbistart       = { 10, 273 },
+               CROPCAP(/* minhdelayx1 */ 68,
+                       /* hdelayx1 */ 128,
+                       /* Should be (640 * 910 + 780 / 2) / 780? */
+                       /* swidth */ 768,
+                       /* totalwidth */ 910,
+                       /* sqwidth */ 780,
+                       /* vdelay */ 0x1a,
+                       /* sheight */ 480,
+                       /* videostart0 */ 23)
+       },{
+               .v4l2_id        = V4L2_STD_SECAM,
+               .name           = "SECAM",
+               .Fsc            = 35468950,
+               .swidth         = 924,
+               .sheight        = 576,
+               .totalwidth     = 1135,
+               .adelay         = 0x7f,
+               .bdelay         = 0xb0,
+               .iform          = (BT848_IFORM_SECAM|BT848_IFORM_XT1),
+               .scaledtwidth   = 1135,
+               .hdelayx1       = 186,
+               .hactivex1      = 922,
+               .vdelay         = 0x20,
+               .vbipack        = 255,
+               .sram           = 0, /* like PAL, correct? */
+               .vbistart       = { 7, 320 },
+               CROPCAP(/* minhdelayx1 */ 68,
+                       /* hdelayx1 */ 186,
+                       /* swidth */ 924,
+                       /* totalwidth */ 1135,
+                       /* sqwidth */ 944,
+                       /* vdelay */ 0x20,
+                       /* sheight */ 576,
+                       /* videostart0 */ 23)
+       },{
+               .v4l2_id        = V4L2_STD_PAL_Nc,
+               .name           = "PAL-Nc",
+               .Fsc            = 28636363,
+               .swidth         = 640,
+               .sheight        = 576,
+               .totalwidth     = 910,
+               .adelay         = 0x68,
+               .bdelay         = 0x5d,
+               .iform          = (BT848_IFORM_PAL_NC|BT848_IFORM_XT0),
+               .scaledtwidth   = 780,
+               .hdelayx1       = 130,
+               .hactivex1      = 734,
+               .vdelay         = 0x1a,
+               .vbipack        = 144,
+               .sram           = -1,
+               .vbistart       = { 7, 320 },
+               CROPCAP(/* minhdelayx1 */ 68,
+                       /* hdelayx1 */ 130,
+                       /* swidth */ (640 * 910 + 780 / 2) / 780,
+                       /* totalwidth */ 910,
+                       /* sqwidth */ 780,
+                       /* vdelay */ 0x1a,
+                       /* sheight */ 576,
+                       /* videostart0 */ 23)
+       },{
+               .v4l2_id        = V4L2_STD_PAL_M,
+               .name           = "PAL-M",
+               .Fsc            = 28636363,
+               .swidth         = 640,
+               .sheight        = 480,
+               .totalwidth     = 910,
+               .adelay         = 0x68,
+               .bdelay         = 0x5d,
+               .iform          = (BT848_IFORM_PAL_M|BT848_IFORM_XT0),
+               .scaledtwidth   = 780,
+               .hdelayx1       = 135,
+               .hactivex1      = 754,
+               .vdelay         = 0x1a,
+               .vbipack        = 144,
+               .sram           = -1,
+               .vbistart       = { 10, 273 },
+               CROPCAP(/* minhdelayx1 */ 68,
+                       /* hdelayx1 */ 135,
+                       /* swidth */ (640 * 910 + 780 / 2) / 780,
+                       /* totalwidth */ 910,
+                       /* sqwidth */ 780,
+                       /* vdelay */ 0x1a,
+                       /* sheight */ 480,
+                       /* videostart0 */ 23)
+       },{
+               .v4l2_id        = V4L2_STD_PAL_N,
+               .name           = "PAL-N",
+               .Fsc            = 35468950,
+               .swidth         = 768,
+               .sheight        = 576,
+               .totalwidth     = 1135,
+               .adelay         = 0x7f,
+               .bdelay         = 0x72,
+               .iform          = (BT848_IFORM_PAL_N|BT848_IFORM_XT1),
+               .scaledtwidth   = 944,
+               .hdelayx1       = 186,
+               .hactivex1      = 922,
+               .vdelay         = 0x20,
+               .vbipack        = 144,
+               .sram           = -1,
+               .vbistart       = { 7, 320 },
+               CROPCAP(/* minhdelayx1 */ 68,
+                       /* hdelayx1 */ 186,
+                       /* swidth */ (768 * 1135 + 944 / 2) / 944,
+                       /* totalwidth */ 1135,
+                       /* sqwidth */ 944,
+                       /* vdelay */ 0x20,
+                       /* sheight */ 576,
+                       /* videostart0 */ 23)
+       },{
+               .v4l2_id        = V4L2_STD_NTSC_M_JP,
+               .name           = "NTSC-JP",
+               .Fsc            = 28636363,
+               .swidth         = 640,
+               .sheight        = 480,
+               .totalwidth     = 910,
+               .adelay         = 0x68,
+               .bdelay         = 0x5d,
+               .iform          = (BT848_IFORM_NTSC_J|BT848_IFORM_XT0),
+               .scaledtwidth   = 780,
+               .hdelayx1       = 135,
+               .hactivex1      = 754,
+               .vdelay         = 0x16,
+               .vbipack        = 144,
+               .sram           = -1,
+               .vbistart       = { 10, 273 },
+               CROPCAP(/* minhdelayx1 */ 68,
+                       /* hdelayx1 */ 135,
+                       /* swidth */ (640 * 910 + 780 / 2) / 780,
+                       /* totalwidth */ 910,
+                       /* sqwidth */ 780,
+                       /* vdelay */ 0x16,
+                       /* sheight */ 480,
+                       /* videostart0 */ 23)
+       },{
+               /* that one hopefully works with the strange timing
+                * which video recorders produce when playing a NTSC
+                * tape on a PAL TV ... */
+               .v4l2_id        = V4L2_STD_PAL_60,
+               .name           = "PAL-60",
+               .Fsc            = 35468950,
+               .swidth         = 924,
+               .sheight        = 480,
+               .totalwidth     = 1135,
+               .adelay         = 0x7f,
+               .bdelay         = 0x72,
+               .iform          = (BT848_IFORM_PAL_BDGHI|BT848_IFORM_XT1),
+               .scaledtwidth   = 1135,
+               .hdelayx1       = 186,
+               .hactivex1      = 924,
+               .vdelay         = 0x1a,
+               .vbipack        = 255,
+               .vtotal         = 524,
+               .sram           = -1,
+               .vbistart       = { 10, 273 },
+               CROPCAP(/* minhdelayx1 */ 68,
+                       /* hdelayx1 */ 186,
+                       /* swidth */ 924,
+                       /* totalwidth */ 1135,
+                       /* sqwidth */ 944,
+                       /* vdelay */ 0x1a,
+                       /* sheight */ 480,
+                       /* videostart0 */ 23)
+       }
+ };
+ static const unsigned int BTTV_TVNORMS = ARRAY_SIZE(bttv_tvnorms);
+ /* ----------------------------------------------------------------------- */
+ /* bttv format list
+    packed pixel formats must come first */
+ static const struct bttv_format formats[] = {
+       {
+               .name     = "8 bpp, gray",
+               .fourcc   = V4L2_PIX_FMT_GREY,
+               .btformat = BT848_COLOR_FMT_Y8,
+               .depth    = 8,
+               .flags    = FORMAT_FLAGS_PACKED,
+       },{
+               .name     = "8 bpp, dithered color",
+               .fourcc   = V4L2_PIX_FMT_HI240,
+               .btformat = BT848_COLOR_FMT_RGB8,
+               .depth    = 8,
+               .flags    = FORMAT_FLAGS_PACKED | FORMAT_FLAGS_DITHER,
+       },{
+               .name     = "15 bpp RGB, le",
+               .fourcc   = V4L2_PIX_FMT_RGB555,
+               .btformat = BT848_COLOR_FMT_RGB15,
+               .depth    = 16,
+               .flags    = FORMAT_FLAGS_PACKED,
+       },{
+               .name     = "15 bpp RGB, be",
+               .fourcc   = V4L2_PIX_FMT_RGB555X,
+               .btformat = BT848_COLOR_FMT_RGB15,
+               .btswap   = 0x03, /* byteswap */
+               .depth    = 16,
+               .flags    = FORMAT_FLAGS_PACKED,
+       },{
+               .name     = "16 bpp RGB, le",
+               .fourcc   = V4L2_PIX_FMT_RGB565,
+               .btformat = BT848_COLOR_FMT_RGB16,
+               .depth    = 16,
+               .flags    = FORMAT_FLAGS_PACKED,
+       },{
+               .name     = "16 bpp RGB, be",
+               .fourcc   = V4L2_PIX_FMT_RGB565X,
+               .btformat = BT848_COLOR_FMT_RGB16,
+               .btswap   = 0x03, /* byteswap */
+               .depth    = 16,
+               .flags    = FORMAT_FLAGS_PACKED,
+       },{
+               .name     = "24 bpp RGB, le",
+               .fourcc   = V4L2_PIX_FMT_BGR24,
+               .btformat = BT848_COLOR_FMT_RGB24,
+               .depth    = 24,
+               .flags    = FORMAT_FLAGS_PACKED,
+       },{
+               .name     = "32 bpp RGB, le",
+               .fourcc   = V4L2_PIX_FMT_BGR32,
+               .btformat = BT848_COLOR_FMT_RGB32,
+               .depth    = 32,
+               .flags    = FORMAT_FLAGS_PACKED,
+       },{
+               .name     = "32 bpp RGB, be",
+               .fourcc   = V4L2_PIX_FMT_RGB32,
+               .btformat = BT848_COLOR_FMT_RGB32,
+               .btswap   = 0x0f, /* byte+word swap */
+               .depth    = 32,
+               .flags    = FORMAT_FLAGS_PACKED,
+       },{
+               .name     = "4:2:2, packed, YUYV",
+               .fourcc   = V4L2_PIX_FMT_YUYV,
+               .btformat = BT848_COLOR_FMT_YUY2,
+               .depth    = 16,
+               .flags    = FORMAT_FLAGS_PACKED,
+       },{
+               .name     = "4:2:2, packed, UYVY",
+               .fourcc   = V4L2_PIX_FMT_UYVY,
+               .btformat = BT848_COLOR_FMT_YUY2,
+               .btswap   = 0x03, /* byteswap */
+               .depth    = 16,
+               .flags    = FORMAT_FLAGS_PACKED,
+       },{
+               .name     = "4:2:2, planar, Y-Cb-Cr",
+               .fourcc   = V4L2_PIX_FMT_YUV422P,
+               .btformat = BT848_COLOR_FMT_YCrCb422,
+               .depth    = 16,
+               .flags    = FORMAT_FLAGS_PLANAR,
+               .hshift   = 1,
+               .vshift   = 0,
+       },{
+               .name     = "4:2:0, planar, Y-Cb-Cr",
+               .fourcc   = V4L2_PIX_FMT_YUV420,
+               .btformat = BT848_COLOR_FMT_YCrCb422,
+               .depth    = 12,
+               .flags    = FORMAT_FLAGS_PLANAR,
+               .hshift   = 1,
+               .vshift   = 1,
+       },{
+               .name     = "4:2:0, planar, Y-Cr-Cb",
+               .fourcc   = V4L2_PIX_FMT_YVU420,
+               .btformat = BT848_COLOR_FMT_YCrCb422,
+               .depth    = 12,
+               .flags    = FORMAT_FLAGS_PLANAR | FORMAT_FLAGS_CrCb,
+               .hshift   = 1,
+               .vshift   = 1,
+       },{
+               .name     = "4:1:1, planar, Y-Cb-Cr",
+               .fourcc   = V4L2_PIX_FMT_YUV411P,
+               .btformat = BT848_COLOR_FMT_YCrCb411,
+               .depth    = 12,
+               .flags    = FORMAT_FLAGS_PLANAR,
+               .hshift   = 2,
+               .vshift   = 0,
+       },{
+               .name     = "4:1:0, planar, Y-Cb-Cr",
+               .fourcc   = V4L2_PIX_FMT_YUV410,
+               .btformat = BT848_COLOR_FMT_YCrCb411,
+               .depth    = 9,
+               .flags    = FORMAT_FLAGS_PLANAR,
+               .hshift   = 2,
+               .vshift   = 2,
+       },{
+               .name     = "4:1:0, planar, Y-Cr-Cb",
+               .fourcc   = V4L2_PIX_FMT_YVU410,
+               .btformat = BT848_COLOR_FMT_YCrCb411,
+               .depth    = 9,
+               .flags    = FORMAT_FLAGS_PLANAR | FORMAT_FLAGS_CrCb,
+               .hshift   = 2,
+               .vshift   = 2,
+       },{
+               .name     = "raw scanlines",
+               .fourcc   = -1,
+               .btformat = BT848_COLOR_FMT_RAW,
+               .depth    = 8,
+               .flags    = FORMAT_FLAGS_RAW,
+       }
+ };
+ static const unsigned int FORMATS = ARRAY_SIZE(formats);
+ /* ----------------------------------------------------------------------- */
+ #define V4L2_CID_PRIVATE_CHROMA_AGC  (V4L2_CID_PRIVATE_BASE + 0)
+ #define V4L2_CID_PRIVATE_COMBFILTER  (V4L2_CID_PRIVATE_BASE + 1)
+ #define V4L2_CID_PRIVATE_AUTOMUTE    (V4L2_CID_PRIVATE_BASE + 2)
+ #define V4L2_CID_PRIVATE_LUMAFILTER  (V4L2_CID_PRIVATE_BASE + 3)
+ #define V4L2_CID_PRIVATE_AGC_CRUSH   (V4L2_CID_PRIVATE_BASE + 4)
+ #define V4L2_CID_PRIVATE_VCR_HACK    (V4L2_CID_PRIVATE_BASE + 5)
+ #define V4L2_CID_PRIVATE_WHITECRUSH_UPPER   (V4L2_CID_PRIVATE_BASE + 6)
+ #define V4L2_CID_PRIVATE_WHITECRUSH_LOWER   (V4L2_CID_PRIVATE_BASE + 7)
+ #define V4L2_CID_PRIVATE_UV_RATIO    (V4L2_CID_PRIVATE_BASE + 8)
+ #define V4L2_CID_PRIVATE_FULL_LUMA_RANGE    (V4L2_CID_PRIVATE_BASE + 9)
+ #define V4L2_CID_PRIVATE_CORING      (V4L2_CID_PRIVATE_BASE + 10)
+ #define V4L2_CID_PRIVATE_LASTP1      (V4L2_CID_PRIVATE_BASE + 11)
+ static const struct v4l2_queryctrl no_ctl = {
+       .name  = "42",
+       .flags = V4L2_CTRL_FLAG_DISABLED,
+ };
+ static const struct v4l2_queryctrl bttv_ctls[] = {
+       /* --- video --- */
+       {
+               .id            = V4L2_CID_BRIGHTNESS,
+               .name          = "Brightness",
+               .minimum       = 0,
+               .maximum       = 65535,
+               .step          = 256,
+               .default_value = 32768,
+               .type          = V4L2_CTRL_TYPE_INTEGER,
+       },{
+               .id            = V4L2_CID_CONTRAST,
+               .name          = "Contrast",
+               .minimum       = 0,
+               .maximum       = 65535,
+               .step          = 128,
+               .default_value = 27648,
+               .type          = V4L2_CTRL_TYPE_INTEGER,
+       },{
+               .id            = V4L2_CID_SATURATION,
+               .name          = "Saturation",
+               .minimum       = 0,
+               .maximum       = 65535,
+               .step          = 128,
+               .default_value = 32768,
+               .type          = V4L2_CTRL_TYPE_INTEGER,
+       },{
+               .id            = V4L2_CID_HUE,
+               .name          = "Hue",
+               .minimum       = 0,
+               .maximum       = 65535,
+               .step          = 256,
+               .default_value = 32768,
+               .type          = V4L2_CTRL_TYPE_INTEGER,
+       },
+       /* --- 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       = 0,
+               .maximum       = 65535,
+               .step          = 65535/100,
+               .default_value = 65535,
+               .type          = V4L2_CTRL_TYPE_INTEGER,
+       },{
+               .id            = V4L2_CID_AUDIO_BALANCE,
+               .name          = "Balance",
+               .minimum       = 0,
+               .maximum       = 65535,
+               .step          = 65535/100,
+               .default_value = 32768,
+               .type          = V4L2_CTRL_TYPE_INTEGER,
+       },{
+               .id            = V4L2_CID_AUDIO_BASS,
+               .name          = "Bass",
+               .minimum       = 0,
+               .maximum       = 65535,
+               .step          = 65535/100,
+               .default_value = 32768,
+               .type          = V4L2_CTRL_TYPE_INTEGER,
+       },{
+               .id            = V4L2_CID_AUDIO_TREBLE,
+               .name          = "Treble",
+               .minimum       = 0,
+               .maximum       = 65535,
+               .step          = 65535/100,
+               .default_value = 32768,
+               .type          = V4L2_CTRL_TYPE_INTEGER,
+       },
+       /* --- private --- */
+       {
+               .id            = V4L2_CID_PRIVATE_CHROMA_AGC,
+               .name          = "chroma agc",
+               .minimum       = 0,
+               .maximum       = 1,
+               .type          = V4L2_CTRL_TYPE_BOOLEAN,
+       },{
+               .id            = V4L2_CID_PRIVATE_COMBFILTER,
+               .name          = "combfilter",
+               .minimum       = 0,
+               .maximum       = 1,
+               .type          = V4L2_CTRL_TYPE_BOOLEAN,
+       },{
+               .id            = V4L2_CID_PRIVATE_AUTOMUTE,
+               .name          = "automute",
+               .minimum       = 0,
+               .maximum       = 1,
+               .type          = V4L2_CTRL_TYPE_BOOLEAN,
+       },{
+               .id            = V4L2_CID_PRIVATE_LUMAFILTER,
+               .name          = "luma decimation filter",
+               .minimum       = 0,
+               .maximum       = 1,
+               .type          = V4L2_CTRL_TYPE_BOOLEAN,
+       },{
+               .id            = V4L2_CID_PRIVATE_AGC_CRUSH,
+               .name          = "agc crush",
+               .minimum       = 0,
+               .maximum       = 1,
+               .type          = V4L2_CTRL_TYPE_BOOLEAN,
+       },{
+               .id            = V4L2_CID_PRIVATE_VCR_HACK,
+               .name          = "vcr hack",
+               .minimum       = 0,
+               .maximum       = 1,
+               .type          = V4L2_CTRL_TYPE_BOOLEAN,
+       },{
+               .id            = V4L2_CID_PRIVATE_WHITECRUSH_UPPER,
+               .name          = "whitecrush upper",
+               .minimum       = 0,
+               .maximum       = 255,
+               .step          = 1,
+               .default_value = 0xCF,
+               .type          = V4L2_CTRL_TYPE_INTEGER,
+       },{
+               .id            = V4L2_CID_PRIVATE_WHITECRUSH_LOWER,
+               .name          = "whitecrush lower",
+               .minimum       = 0,
+               .maximum       = 255,
+               .step          = 1,
+               .default_value = 0x7F,
+               .type          = V4L2_CTRL_TYPE_INTEGER,
+       },{
+               .id            = V4L2_CID_PRIVATE_UV_RATIO,
+               .name          = "uv ratio",
+               .minimum       = 0,
+               .maximum       = 100,
+               .step          = 1,
+               .default_value = 50,
+               .type          = V4L2_CTRL_TYPE_INTEGER,
+       },{
+               .id            = V4L2_CID_PRIVATE_FULL_LUMA_RANGE,
+               .name          = "full luma range",
+               .minimum       = 0,
+               .maximum       = 1,
+               .type          = V4L2_CTRL_TYPE_BOOLEAN,
+       },{
+               .id            = V4L2_CID_PRIVATE_CORING,
+               .name          = "coring",
+               .minimum       = 0,
+               .maximum       = 3,
+               .step          = 1,
+               .default_value = 0,
+               .type          = V4L2_CTRL_TYPE_INTEGER,
+       }
+ };
+ static const struct v4l2_queryctrl *ctrl_by_id(int id)
+ {
+       int i;
+       for (i = 0; i < ARRAY_SIZE(bttv_ctls); i++)
+               if (bttv_ctls[i].id == id)
+                       return bttv_ctls+i;
+       return NULL;
+ }
+ /* ----------------------------------------------------------------------- */
+ /* resource management                                                     */
+ /*
+    RESOURCE_    allocated by                freed by
+    VIDEO_READ   bttv_read 1)                bttv_read 2)
+    VIDEO_STREAM VIDIOC_STREAMON             VIDIOC_STREAMOFF
+                VIDIOC_QBUF 1)              bttv_release
+                VIDIOCMCAPTURE 1)
+    OVERLAY     VIDIOCCAPTURE on            VIDIOCCAPTURE off
+                VIDIOC_OVERLAY on           VIDIOC_OVERLAY off
+                3)                          bttv_release
+    VBI                 VIDIOC_STREAMON             VIDIOC_STREAMOFF
+                VIDIOC_QBUF 1)              bttv_release
+                bttv_read, bttv_poll 1) 4)
+    1) The resource must be allocated when we enter buffer prepare functions
+       and remain allocated while buffers are in the DMA queue.
+    2) This is a single frame read.
+    3) VIDIOC_S_FBUF and VIDIOC_S_FMT (OVERLAY) still work when
+       RESOURCE_OVERLAY is allocated.
+    4) This is a continuous read, implies VIDIOC_STREAMON.
+    Note this driver permits video input and standard changes regardless if
+    resources are allocated.
+ */
+ #define VBI_RESOURCES (RESOURCE_VBI)
+ #define VIDEO_RESOURCES (RESOURCE_VIDEO_READ | \
+                        RESOURCE_VIDEO_STREAM | \
+                        RESOURCE_OVERLAY)
+ static
+ int check_alloc_btres_lock(struct bttv *btv, struct bttv_fh *fh, int bit)
+ {
+       int xbits; /* mutual exclusive resources */
+       if (fh->resources & bit)
+               /* have it already allocated */
+               return 1;
+       xbits = bit;
+       if (bit & (RESOURCE_VIDEO_READ | RESOURCE_VIDEO_STREAM))
+               xbits |= RESOURCE_VIDEO_READ | RESOURCE_VIDEO_STREAM;
+       /* is it free? */
+       if (btv->resources & xbits) {
+               /* no, someone else uses it */
+               goto fail;
+       }
+       if ((bit & VIDEO_RESOURCES)
+           && 0 == (btv->resources & VIDEO_RESOURCES)) {
+               /* Do crop - use current, don't - use default parameters. */
+               __s32 top = btv->crop[!!fh->do_crop].rect.top;
+               if (btv->vbi_end > top)
+                       goto fail;
+               /* We cannot capture the same line as video and VBI data.
+                  Claim scan lines crop[].rect.top to bottom. */
+               btv->crop_start = top;
+       } else if (bit & VBI_RESOURCES) {
+               __s32 end = fh->vbi_fmt.end;
+               if (end > btv->crop_start)
+                       goto fail;
+               /* Claim scan lines above fh->vbi_fmt.end. */
+               btv->vbi_end = end;
+       }
+       /* it's free, grab it */
+       fh->resources  |= bit;
+       btv->resources |= bit;
+       return 1;
+  fail:
+       return 0;
+ }
+ static
+ int check_btres(struct bttv_fh *fh, int bit)
+ {
+       return (fh->resources & bit);
+ }
+ static
+ int locked_btres(struct bttv *btv, int bit)
+ {
+       return (btv->resources & bit);
+ }
+ /* Call with btv->lock down. */
+ static void
+ disclaim_vbi_lines(struct bttv *btv)
+ {
+       btv->vbi_end = 0;
+ }
+ /* Call with btv->lock down. */
+ static void
+ disclaim_video_lines(struct bttv *btv)
+ {
+       const struct bttv_tvnorm *tvnorm;
+       u8 crop;
+       tvnorm = &bttv_tvnorms[btv->tvnorm];
+       btv->crop_start = tvnorm->cropcap.bounds.top
+               + tvnorm->cropcap.bounds.height;
+       /* VBI capturing ends at VDELAY, start of video capturing, no
+          matter how many lines the VBI RISC program expects. When video
+          capturing is off, it shall no longer "preempt" VBI capturing,
+          so we set VDELAY to maximum. */
+       crop = btread(BT848_E_CROP) | 0xc0;
+       btwrite(crop, BT848_E_CROP);
+       btwrite(0xfe, BT848_E_VDELAY_LO);
+       btwrite(crop, BT848_O_CROP);
+       btwrite(0xfe, BT848_O_VDELAY_LO);
+ }
+ static
+ void free_btres_lock(struct bttv *btv, struct bttv_fh *fh, int bits)
+ {
+       if ((fh->resources & bits) != bits) {
+               /* trying to free resources not allocated by us ... */
+               pr_err("BUG! (btres)\n");
+       }
+       fh->resources  &= ~bits;
+       btv->resources &= ~bits;
+       bits = btv->resources;
+       if (0 == (bits & VIDEO_RESOURCES))
+               disclaim_video_lines(btv);
+       if (0 == (bits & VBI_RESOURCES))
+               disclaim_vbi_lines(btv);
+ }
+ /* ----------------------------------------------------------------------- */
+ /* If Bt848a or Bt849, use PLL for PAL/SECAM and crystal for NTSC          */
+ /* Frequency = (F_input / PLL_X) * PLL_I.PLL_F/PLL_C
+    PLL_X = Reference pre-divider (0=1, 1=2)
+    PLL_C = Post divider (0=6, 1=4)
+    PLL_I = Integer input
+    PLL_F = Fractional input
+    F_input = 28.636363 MHz:
+    PAL (CLKx2 = 35.46895 MHz): PLL_X = 1, PLL_I = 0x0E, PLL_F = 0xDCF9, PLL_C = 0
+ */
+ static void set_pll_freq(struct bttv *btv, unsigned int fin, unsigned int fout)
+ {
+       unsigned char fl, fh, fi;
+       /* prevent overflows */
+       fin/=4;
+       fout/=4;
+       fout*=12;
+       fi=fout/fin;
+       fout=(fout%fin)*256;
+       fh=fout/fin;
+       fout=(fout%fin)*256;
+       fl=fout/fin;
+       btwrite(fl, BT848_PLL_F_LO);
+       btwrite(fh, BT848_PLL_F_HI);
+       btwrite(fi|BT848_PLL_X, BT848_PLL_XCI);
+ }
+ static void set_pll(struct bttv *btv)
+ {
+       int i;
+       if (!btv->pll.pll_crystal)
+               return;
+       if (btv->pll.pll_ofreq == btv->pll.pll_current) {
+               dprintk("%d: PLL: no change required\n", btv->c.nr);
+               return;
+       }
+       if (btv->pll.pll_ifreq == btv->pll.pll_ofreq) {
+               /* no PLL needed */
+               if (btv->pll.pll_current == 0)
+                       return;
+               if (bttv_verbose)
+                       pr_info("%d: PLL can sleep, using XTAL (%d)\n",
+                               btv->c.nr, btv->pll.pll_ifreq);
+               btwrite(0x00,BT848_TGCTRL);
+               btwrite(0x00,BT848_PLL_XCI);
+               btv->pll.pll_current = 0;
+               return;
+       }
+       if (bttv_verbose)
+               pr_info("%d: Setting PLL: %d => %d (needs up to 100ms)\n",
+                       btv->c.nr,
+                       btv->pll.pll_ifreq, btv->pll.pll_ofreq);
+       set_pll_freq(btv, btv->pll.pll_ifreq, btv->pll.pll_ofreq);
+       for (i=0; i<10; i++) {
+               /*  Let other people run while the PLL stabilizes */
+               msleep(10);
+               if (btread(BT848_DSTATUS) & BT848_DSTATUS_PLOCK) {
+                       btwrite(0,BT848_DSTATUS);
+               } else {
+                       btwrite(0x08,BT848_TGCTRL);
+                       btv->pll.pll_current = btv->pll.pll_ofreq;
+                       if (bttv_verbose)
+                               pr_info("PLL set ok\n");
+                       return;
+               }
+       }
+       btv->pll.pll_current = -1;
+       if (bttv_verbose)
+               pr_info("Setting PLL failed\n");
+       return;
+ }
+ /* used to switch between the bt848's analog/digital video capture modes */
+ static void bt848A_set_timing(struct bttv *btv)
+ {
+       int i, len;
+       int table_idx = bttv_tvnorms[btv->tvnorm].sram;
+       int fsc       = bttv_tvnorms[btv->tvnorm].Fsc;
+       if (btv->input == btv->dig) {
+               dprintk("%d: load digital timing table (table_idx=%d)\n",
+                       btv->c.nr,table_idx);
+               /* timing change...reset timing generator address */
+               btwrite(0x00, BT848_TGCTRL);
+               btwrite(0x02, BT848_TGCTRL);
+               btwrite(0x00, BT848_TGCTRL);
+               len=SRAM_Table[table_idx][0];
+               for(i = 1; i <= len; i++)
+                       btwrite(SRAM_Table[table_idx][i],BT848_TGLB);
+               btv->pll.pll_ofreq = 27000000;
+               set_pll(btv);
+               btwrite(0x11, BT848_TGCTRL);
+               btwrite(0x41, BT848_DVSIF);
+       } else {
+               btv->pll.pll_ofreq = fsc;
+               set_pll(btv);
+               btwrite(0x0, BT848_DVSIF);
+       }
+ }
+ /* ----------------------------------------------------------------------- */
+ static void bt848_bright(struct bttv *btv, int bright)
+ {
+       int value;
+       // printk("set bright: %d\n", bright); // DEBUG
+       btv->bright = bright;
+       /* We want -128 to 127 we get 0-65535 */
+       value = (bright >> 8) - 128;
+       btwrite(value & 0xff, BT848_BRIGHT);
+ }
+ static void bt848_hue(struct bttv *btv, int hue)
+ {
+       int value;
+       btv->hue = hue;
+       /* -128 to 127 */
+       value = (hue >> 8) - 128;
+       btwrite(value & 0xff, BT848_HUE);
+ }
+ static void bt848_contrast(struct bttv *btv, int cont)
+ {
+       int value,hibit;
+       btv->contrast = cont;
+       /* 0-511 */
+       value = (cont  >> 7);
+       hibit = (value >> 6) & 4;
+       btwrite(value & 0xff, BT848_CONTRAST_LO);
+       btaor(hibit, ~4, BT848_E_CONTROL);
+       btaor(hibit, ~4, BT848_O_CONTROL);
+ }
+ static void bt848_sat(struct bttv *btv, int color)
+ {
+       int val_u,val_v,hibits;
+       btv->saturation = color;
+       /* 0-511 for the color */
+       val_u   = ((color * btv->opt_uv_ratio) / 50) >> 7;
+       val_v   = (((color * (100 - btv->opt_uv_ratio) / 50) >>7)*180L)/254;
+       hibits  = (val_u >> 7) & 2;
+       hibits |= (val_v >> 8) & 1;
+       btwrite(val_u & 0xff, BT848_SAT_U_LO);
+       btwrite(val_v & 0xff, BT848_SAT_V_LO);
+       btaor(hibits, ~3, BT848_E_CONTROL);
+       btaor(hibits, ~3, BT848_O_CONTROL);
+ }
+ /* ----------------------------------------------------------------------- */
+ static int
+ video_mux(struct bttv *btv, unsigned int input)
+ {
+       int mux,mask2;
+       if (input >= bttv_tvcards[btv->c.type].video_inputs)
+               return -EINVAL;
+       /* needed by RemoteVideo MX */
+       mask2 = bttv_tvcards[btv->c.type].gpiomask2;
+       if (mask2)
+               gpio_inout(mask2,mask2);
+       if (input == btv->svhs)  {
+               btor(BT848_CONTROL_COMP, BT848_E_CONTROL);
+               btor(BT848_CONTROL_COMP, BT848_O_CONTROL);
+       } else {
+               btand(~BT848_CONTROL_COMP, BT848_E_CONTROL);
+               btand(~BT848_CONTROL_COMP, BT848_O_CONTROL);
+       }
+       mux = bttv_muxsel(btv, input);
+       btaor(mux<<5, ~(3<<5), BT848_IFORM);
+       dprintk("%d: video mux: input=%d mux=%d\n", btv->c.nr, input, mux);
+       /* card specific hook */
+       if(bttv_tvcards[btv->c.type].muxsel_hook)
+               bttv_tvcards[btv->c.type].muxsel_hook (btv, input);
+       return 0;
+ }
+ static char *audio_modes[] = {
+       "audio: tuner", "audio: radio", "audio: extern",
+       "audio: intern", "audio: mute"
+ };
+ static int
+ audio_mux(struct bttv *btv, int input, int mute)
+ {
+       int gpio_val, signal;
+       struct v4l2_control ctrl;
+       gpio_inout(bttv_tvcards[btv->c.type].gpiomask,
+                  bttv_tvcards[btv->c.type].gpiomask);
+       signal = btread(BT848_DSTATUS) & BT848_DSTATUS_HLOC;
+       btv->mute = mute;
+       btv->audio = input;
+       /* automute */
+       mute = mute || (btv->opt_automute && !signal && !btv->radio_user);
+       if (mute)
+               gpio_val = bttv_tvcards[btv->c.type].gpiomute;
+       else
+               gpio_val = bttv_tvcards[btv->c.type].gpiomux[input];
+       switch (btv->c.type) {
+       case BTTV_BOARD_VOODOOTV_FM:
+       case BTTV_BOARD_VOODOOTV_200:
+               gpio_val = bttv_tda9880_setnorm(btv, gpio_val);
+               break;
+       default:
+               gpio_bits(bttv_tvcards[btv->c.type].gpiomask, gpio_val);
+       }
+       if (bttv_gpio)
+               bttv_gpio_tracking(btv, audio_modes[mute ? 4 : input]);
+       if (in_interrupt())
+               return 0;
+       ctrl.id = V4L2_CID_AUDIO_MUTE;
+       ctrl.value = btv->mute;
+       bttv_call_all(btv, core, s_ctrl, &ctrl);
+       if (btv->sd_msp34xx) {
+               u32 in;
+               /* Note: the inputs tuner/radio/extern/intern are translated
+                  to msp routings. This assumes common behavior for all msp3400
+                  based TV cards. When this assumption fails, then the
+                  specific MSP routing must be added to the card table.
+                  For now this is sufficient. */
+               switch (input) {
+               case TVAUDIO_INPUT_RADIO:
+                       /* Some boards need the msp do to the radio demod */
+                       if (btv->radio_uses_msp_demodulator) {
+                               in = MSP_INPUT_DEFAULT;
+                               break;
+                       }
+                       in = MSP_INPUT(MSP_IN_SCART2, MSP_IN_TUNER1,
+                                   MSP_DSP_IN_SCART, MSP_DSP_IN_SCART);
+                       break;
+               case TVAUDIO_INPUT_EXTERN:
+                       in = MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1,
+                                   MSP_DSP_IN_SCART, MSP_DSP_IN_SCART);
+                       break;
+               case TVAUDIO_INPUT_INTERN:
+                       /* Yes, this is the same input as for RADIO. I doubt
+                          if this is ever used. The only board with an INTERN
+                          input is the BTTV_BOARD_AVERMEDIA98. I wonder how
+                          that was tested. My guess is that the whole INTERN
+                          input does not work. */
+                       in = MSP_INPUT(MSP_IN_SCART2, MSP_IN_TUNER1,
+                                   MSP_DSP_IN_SCART, MSP_DSP_IN_SCART);
+                       break;
+               case TVAUDIO_INPUT_TUNER:
+               default:
+                       /* This is the only card that uses TUNER2, and afaik,
+                          is the only difference between the VOODOOTV_FM
+                          and VOODOOTV_200 */
+                       if (btv->c.type == BTTV_BOARD_VOODOOTV_200)
+                               in = MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER2, \
+                                       MSP_DSP_IN_TUNER, MSP_DSP_IN_TUNER);
+                       else
+                               in = MSP_INPUT_DEFAULT;
+                       break;
+               }
+               v4l2_subdev_call(btv->sd_msp34xx, audio, s_routing,
+                              in, MSP_OUTPUT_DEFAULT, 0);
+       }
+       if (btv->sd_tvaudio) {
+               v4l2_subdev_call(btv->sd_tvaudio, audio, s_routing,
+                               input, 0, 0);
+       }
+       return 0;
+ }
+ static inline int
+ audio_mute(struct bttv *btv, int mute)
+ {
+       return audio_mux(btv, btv->audio, mute);
+ }
+ static inline int
+ audio_input(struct bttv *btv, int input)
+ {
+       return audio_mux(btv, input, btv->mute);
+ }
+ static void
+ bttv_crop_calc_limits(struct bttv_crop *c)
+ {
+       /* Scale factor min. 1:1, max. 16:1. Min. image size
+          48 x 32. Scaled width must be a multiple of 4. */
+       if (1) {
+               /* For bug compatibility with VIDIOCGCAP and image
+                  size checks in earlier driver versions. */
+               c->min_scaled_width = 48;
+               c->min_scaled_height = 32;
+       } else {
+               c->min_scaled_width =
+                       (max(48, c->rect.width >> 4) + 3) & ~3;
+               c->min_scaled_height =
+                       max(32, c->rect.height >> 4);
+       }
+       c->max_scaled_width  = c->rect.width & ~3;
+       c->max_scaled_height = c->rect.height;
+ }
+ static void
+ bttv_crop_reset(struct bttv_crop *c, unsigned int norm)
+ {
+       c->rect = bttv_tvnorms[norm].cropcap.defrect;
+       bttv_crop_calc_limits(c);
+ }
+ /* Call with btv->lock down. */
+ static int
+ set_tvnorm(struct bttv *btv, unsigned int norm)
+ {
+       const struct bttv_tvnorm *tvnorm;
+       v4l2_std_id id;
+       BUG_ON(norm >= BTTV_TVNORMS);
+       BUG_ON(btv->tvnorm >= BTTV_TVNORMS);
+       tvnorm = &bttv_tvnorms[norm];
+       if (memcmp(&bttv_tvnorms[btv->tvnorm].cropcap, &tvnorm->cropcap,
+                   sizeof (tvnorm->cropcap))) {
+               bttv_crop_reset(&btv->crop[0], norm);
+               btv->crop[1] = btv->crop[0]; /* current = default */
+               if (0 == (btv->resources & VIDEO_RESOURCES)) {
+                       btv->crop_start = tvnorm->cropcap.bounds.top
+                               + tvnorm->cropcap.bounds.height;
+               }
+       }
+       btv->tvnorm = norm;
+       btwrite(tvnorm->adelay, BT848_ADELAY);
+       btwrite(tvnorm->bdelay, BT848_BDELAY);
+       btaor(tvnorm->iform,~(BT848_IFORM_NORM|BT848_IFORM_XTBOTH),
+             BT848_IFORM);
+       btwrite(tvnorm->vbipack, BT848_VBI_PACK_SIZE);
+       btwrite(1, BT848_VBI_PACK_DEL);
+       bt848A_set_timing(btv);
+       switch (btv->c.type) {
+       case BTTV_BOARD_VOODOOTV_FM:
+       case BTTV_BOARD_VOODOOTV_200:
+               bttv_tda9880_setnorm(btv, gpio_read());
+               break;
+       }
+       id = tvnorm->v4l2_id;
+       bttv_call_all(btv, core, s_std, id);
+       return 0;
+ }
+ /* Call with btv->lock down. */
+ static void
+ set_input(struct bttv *btv, unsigned int input, unsigned int norm)
+ {
+       unsigned long flags;
+       btv->input = input;
+       if (irq_iswitch) {
+               spin_lock_irqsave(&btv->s_lock,flags);
+               if (btv->curr.frame_irq) {
+                       /* active capture -> delayed input switch */
+                       btv->new_input = input;
+               } else {
+                       video_mux(btv,input);
+               }
+               spin_unlock_irqrestore(&btv->s_lock,flags);
+       } else {
+               video_mux(btv,input);
+       }
+       audio_input(btv, (btv->tuner_type != TUNER_ABSENT && input == 0) ?
+                        TVAUDIO_INPUT_TUNER : TVAUDIO_INPUT_EXTERN);
+       set_tvnorm(btv, norm);
+ }
+ static void init_irqreg(struct bttv *btv)
+ {
+       /* clear status */
+       btwrite(0xfffffUL, BT848_INT_STAT);
+       if (bttv_tvcards[btv->c.type].no_video) {
+               /* i2c only */
+               btwrite(BT848_INT_I2CDONE,
+                       BT848_INT_MASK);
+       } else {
+               /* full video */
+               btwrite((btv->triton1)  |
+                       (btv->gpioirq ? BT848_INT_GPINT : 0) |
+                       BT848_INT_SCERR |
+                       (fdsr ? BT848_INT_FDSR : 0) |
+                       BT848_INT_RISCI | BT848_INT_OCERR |
+                       BT848_INT_FMTCHG|BT848_INT_HLOCK|
+                       BT848_INT_I2CDONE,
+                       BT848_INT_MASK);
+       }
+ }
+ static void init_bt848(struct bttv *btv)
+ {
+       int val;
+       if (bttv_tvcards[btv->c.type].no_video) {
+               /* very basic init only */
+               init_irqreg(btv);
+               return;
+       }
+       btwrite(0x00, BT848_CAP_CTL);
+       btwrite(BT848_COLOR_CTL_GAMMA, BT848_COLOR_CTL);
+       btwrite(BT848_IFORM_XTAUTO | BT848_IFORM_AUTO, BT848_IFORM);
+       /* set planar and packed mode trigger points and         */
+       /* set rising edge of inverted GPINTR pin as irq trigger */
+       btwrite(BT848_GPIO_DMA_CTL_PKTP_32|
+               BT848_GPIO_DMA_CTL_PLTP1_16|
+               BT848_GPIO_DMA_CTL_PLTP23_16|
+               BT848_GPIO_DMA_CTL_GPINTC|
+               BT848_GPIO_DMA_CTL_GPINTI,
+               BT848_GPIO_DMA_CTL);
+       val = btv->opt_chroma_agc ? BT848_SCLOOP_CAGC : 0;
+       btwrite(val, BT848_E_SCLOOP);
+       btwrite(val, BT848_O_SCLOOP);
+       btwrite(0x20, BT848_E_VSCALE_HI);
+       btwrite(0x20, BT848_O_VSCALE_HI);
+       btwrite(BT848_ADC_RESERVED | (btv->opt_adc_crush ? BT848_ADC_CRUSH : 0),
+               BT848_ADC);
+       btwrite(whitecrush_upper, BT848_WC_UP);
+       btwrite(whitecrush_lower, BT848_WC_DOWN);
+       if (btv->opt_lumafilter) {
+               btwrite(0, BT848_E_CONTROL);
+               btwrite(0, BT848_O_CONTROL);
+       } else {
+               btwrite(BT848_CONTROL_LDEC, BT848_E_CONTROL);
+               btwrite(BT848_CONTROL_LDEC, BT848_O_CONTROL);
+       }
+       bt848_bright(btv,   btv->bright);
+       bt848_hue(btv,      btv->hue);
+       bt848_contrast(btv, btv->contrast);
+       bt848_sat(btv,      btv->saturation);
+       /* interrupt */
+       init_irqreg(btv);
+ }
+ static void bttv_reinit_bt848(struct bttv *btv)
+ {
+       unsigned long flags;
+       if (bttv_verbose)
+               pr_info("%d: reset, reinitialize\n", btv->c.nr);
+       spin_lock_irqsave(&btv->s_lock,flags);
+       btv->errors=0;
+       bttv_set_dma(btv,0);
+       spin_unlock_irqrestore(&btv->s_lock,flags);
+       init_bt848(btv);
+       btv->pll.pll_current = -1;
+       set_input(btv, btv->input, btv->tvnorm);
+ }
+ static int bttv_g_ctrl(struct file *file, void *priv,
+                                       struct v4l2_control *c)
+ {
+       struct bttv_fh *fh = priv;
+       struct bttv *btv = fh->btv;
+       switch (c->id) {
+       case V4L2_CID_BRIGHTNESS:
+               c->value = btv->bright;
+               break;
+       case V4L2_CID_HUE:
+               c->value = btv->hue;
+               break;
+       case V4L2_CID_CONTRAST:
+               c->value = btv->contrast;
+               break;
+       case V4L2_CID_SATURATION:
+               c->value = btv->saturation;
+               break;
+       case V4L2_CID_AUDIO_MUTE:
+       case V4L2_CID_AUDIO_VOLUME:
+       case V4L2_CID_AUDIO_BALANCE:
+       case V4L2_CID_AUDIO_BASS:
+       case V4L2_CID_AUDIO_TREBLE:
+               bttv_call_all(btv, core, g_ctrl, c);
+               break;
+       case V4L2_CID_PRIVATE_CHROMA_AGC:
+               c->value = btv->opt_chroma_agc;
+               break;
+       case V4L2_CID_PRIVATE_COMBFILTER:
+               c->value = btv->opt_combfilter;
+               break;
+       case V4L2_CID_PRIVATE_LUMAFILTER:
+               c->value = btv->opt_lumafilter;
+               break;
+       case V4L2_CID_PRIVATE_AUTOMUTE:
+               c->value = btv->opt_automute;
+               break;
+       case V4L2_CID_PRIVATE_AGC_CRUSH:
+               c->value = btv->opt_adc_crush;
+               break;
+       case V4L2_CID_PRIVATE_VCR_HACK:
+               c->value = btv->opt_vcr_hack;
+               break;
+       case V4L2_CID_PRIVATE_WHITECRUSH_UPPER:
+               c->value = btv->opt_whitecrush_upper;
+               break;
+       case V4L2_CID_PRIVATE_WHITECRUSH_LOWER:
+               c->value = btv->opt_whitecrush_lower;
+               break;
+       case V4L2_CID_PRIVATE_UV_RATIO:
+               c->value = btv->opt_uv_ratio;
+               break;
+       case V4L2_CID_PRIVATE_FULL_LUMA_RANGE:
+               c->value = btv->opt_full_luma_range;
+               break;
+       case V4L2_CID_PRIVATE_CORING:
+               c->value = btv->opt_coring;
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+ }
+ static int bttv_s_ctrl(struct file *file, void *f,
+                                       struct v4l2_control *c)
+ {
+       int err;
+       int val;
+       struct bttv_fh *fh = f;
+       struct bttv *btv = fh->btv;
+       err = v4l2_prio_check(&btv->prio, fh->prio);
+       if (0 != err)
+               return err;
+       switch (c->id) {
+       case V4L2_CID_BRIGHTNESS:
+               bt848_bright(btv, c->value);
+               break;
+       case V4L2_CID_HUE:
+               bt848_hue(btv, c->value);
+               break;
+       case V4L2_CID_CONTRAST:
+               bt848_contrast(btv, c->value);
+               break;
+       case V4L2_CID_SATURATION:
+               bt848_sat(btv, c->value);
+               break;
+       case V4L2_CID_AUDIO_MUTE:
+               audio_mute(btv, c->value);
+               /* fall through */
+       case V4L2_CID_AUDIO_VOLUME:
+               if (btv->volume_gpio)
+                       btv->volume_gpio(btv, c->value);
+               bttv_call_all(btv, core, s_ctrl, c);
+               break;
+       case V4L2_CID_AUDIO_BALANCE:
+       case V4L2_CID_AUDIO_BASS:
+       case V4L2_CID_AUDIO_TREBLE:
+               bttv_call_all(btv, core, s_ctrl, c);
+               break;
+       case V4L2_CID_PRIVATE_CHROMA_AGC:
+               btv->opt_chroma_agc = c->value;
+               val = btv->opt_chroma_agc ? BT848_SCLOOP_CAGC : 0;
+               btwrite(val, BT848_E_SCLOOP);
+               btwrite(val, BT848_O_SCLOOP);
+               break;
+       case V4L2_CID_PRIVATE_COMBFILTER:
+               btv->opt_combfilter = c->value;
+               break;
+       case V4L2_CID_PRIVATE_LUMAFILTER:
+               btv->opt_lumafilter = c->value;
+               if (btv->opt_lumafilter) {
+                       btand(~BT848_CONTROL_LDEC, BT848_E_CONTROL);
+                       btand(~BT848_CONTROL_LDEC, BT848_O_CONTROL);
+               } else {
+                       btor(BT848_CONTROL_LDEC, BT848_E_CONTROL);
+                       btor(BT848_CONTROL_LDEC, BT848_O_CONTROL);
+               }
+               break;
+       case V4L2_CID_PRIVATE_AUTOMUTE:
+               btv->opt_automute = c->value;
+               break;
+       case V4L2_CID_PRIVATE_AGC_CRUSH:
+               btv->opt_adc_crush = c->value;
+               btwrite(BT848_ADC_RESERVED |
+                               (btv->opt_adc_crush ? BT848_ADC_CRUSH : 0),
+                               BT848_ADC);
+               break;
+       case V4L2_CID_PRIVATE_VCR_HACK:
+               btv->opt_vcr_hack = c->value;
+               break;
+       case V4L2_CID_PRIVATE_WHITECRUSH_UPPER:
+               btv->opt_whitecrush_upper = c->value;
+               btwrite(c->value, BT848_WC_UP);
+               break;
+       case V4L2_CID_PRIVATE_WHITECRUSH_LOWER:
+               btv->opt_whitecrush_lower = c->value;
+               btwrite(c->value, BT848_WC_DOWN);
+               break;
+       case V4L2_CID_PRIVATE_UV_RATIO:
+               btv->opt_uv_ratio = c->value;
+               bt848_sat(btv, btv->saturation);
+               break;
+       case V4L2_CID_PRIVATE_FULL_LUMA_RANGE:
+               btv->opt_full_luma_range = c->value;
+               btaor((c->value<<7), ~BT848_OFORM_RANGE, BT848_OFORM);
+               break;
+       case V4L2_CID_PRIVATE_CORING:
+               btv->opt_coring = c->value;
+               btaor((c->value<<5), ~BT848_OFORM_CORE32, BT848_OFORM);
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+ }
+ /* ----------------------------------------------------------------------- */
+ void bttv_gpio_tracking(struct bttv *btv, char *comment)
+ {
+       unsigned int outbits, data;
+       outbits = btread(BT848_GPIO_OUT_EN);
+       data    = btread(BT848_GPIO_DATA);
+       pr_debug("%d: gpio: en=%08x, out=%08x in=%08x [%s]\n",
+                btv->c.nr, outbits, data & outbits, data & ~outbits, comment);
+ }
+ static void bttv_field_count(struct bttv *btv)
+ {
+       int need_count = 0;
+       if (btv->users)
+               need_count++;
+       if (need_count) {
+               /* start field counter */
+               btor(BT848_INT_VSYNC,BT848_INT_MASK);
+       } else {
+               /* stop field counter */
+               btand(~BT848_INT_VSYNC,BT848_INT_MASK);
+               btv->field_count = 0;
+       }
+ }
+ static const struct bttv_format*
+ format_by_fourcc(int fourcc)
+ {
+       unsigned int i;
+       for (i = 0; i < FORMATS; i++) {
+               if (-1 == formats[i].fourcc)
+                       continue;
+               if (formats[i].fourcc == fourcc)
+                       return formats+i;
+       }
+       return NULL;
+ }
+ /* ----------------------------------------------------------------------- */
+ /* misc helpers                                                            */
+ static int
+ bttv_switch_overlay(struct bttv *btv, struct bttv_fh *fh,
+                   struct bttv_buffer *new)
+ {
+       struct bttv_buffer *old;
+       unsigned long flags;
+       int retval = 0;
+       dprintk("switch_overlay: enter [new=%p]\n", new);
+       if (new)
+               new->vb.state = VIDEOBUF_DONE;
+       spin_lock_irqsave(&btv->s_lock,flags);
+       old = btv->screen;
+       btv->screen = new;
+       btv->loop_irq |= 1;
+       bttv_set_dma(btv, 0x03);
+       spin_unlock_irqrestore(&btv->s_lock,flags);
+       if (NULL != old) {
+               dprintk("switch_overlay: old=%p state is %d\n",
+                       old, old->vb.state);
+               bttv_dma_free(&fh->cap,btv, old);
+               kfree(old);
+       }
+       if (NULL == new)
+               free_btres_lock(btv,fh,RESOURCE_OVERLAY);
+       dprintk("switch_overlay: done\n");
+       return retval;
+ }
+ /* ----------------------------------------------------------------------- */
+ /* video4linux (1) interface                                               */
+ static int bttv_prepare_buffer(struct videobuf_queue *q,struct bttv *btv,
+                              struct bttv_buffer *buf,
+                              const struct bttv_format *fmt,
+                              unsigned int width, unsigned int height,
+                              enum v4l2_field field)
+ {
+       struct bttv_fh *fh = q->priv_data;
+       int redo_dma_risc = 0;
+       struct bttv_crop c;
+       int norm;
+       int rc;
+       /* check settings */
+       if (NULL == fmt)
+               return -EINVAL;
+       if (fmt->btformat == BT848_COLOR_FMT_RAW) {
+               width  = RAW_BPL;
+               height = RAW_LINES*2;
+               if (width*height > buf->vb.bsize)
+                       return -EINVAL;
+               buf->vb.size = buf->vb.bsize;
+               /* Make sure tvnorm and vbi_end remain consistent
+                  until we're done. */
+               norm = btv->tvnorm;
+               /* In this mode capturing always starts at defrect.top
+                  (default VDELAY), ignoring cropping parameters. */
+               if (btv->vbi_end > bttv_tvnorms[norm].cropcap.defrect.top) {
+                       return -EINVAL;
+               }
+               c.rect = bttv_tvnorms[norm].cropcap.defrect;
+       } else {
+               norm = btv->tvnorm;
+               c = btv->crop[!!fh->do_crop];
+               if (width < c.min_scaled_width ||
+                   width > c.max_scaled_width ||
+                   height < c.min_scaled_height)
+                       return -EINVAL;
+               switch (field) {
+               case V4L2_FIELD_TOP:
+               case V4L2_FIELD_BOTTOM:
+               case V4L2_FIELD_ALTERNATE:
+                       /* btv->crop counts frame lines. Max. scale
+                          factor is 16:1 for frames, 8:1 for fields. */
+                       if (height * 2 > c.max_scaled_height)
+                               return -EINVAL;
+                       break;
+               default:
+                       if (height > c.max_scaled_height)
+                               return -EINVAL;
+                       break;
+               }
+               buf->vb.size = (width * height * fmt->depth) >> 3;
+               if (0 != buf->vb.baddr  &&  buf->vb.bsize < buf->vb.size)
+                       return -EINVAL;
+       }
+       /* alloc + fill struct bttv_buffer (if changed) */
+       if (buf->vb.width != width || buf->vb.height != height ||
+           buf->vb.field != field ||
+           buf->tvnorm != norm || buf->fmt != fmt ||
+           buf->crop.top != c.rect.top ||
+           buf->crop.left != c.rect.left ||
+           buf->crop.width != c.rect.width ||
+           buf->crop.height != c.rect.height) {
+               buf->vb.width  = width;
+               buf->vb.height = height;
+               buf->vb.field  = field;
+               buf->tvnorm    = norm;
+               buf->fmt       = fmt;
+               buf->crop      = c.rect;
+               redo_dma_risc = 1;
+       }
+       /* alloc risc memory */
+       if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+               redo_dma_risc = 1;
+               if (0 != (rc = videobuf_iolock(q,&buf->vb,&btv->fbuf)))
+                       goto fail;
+       }
+       if (redo_dma_risc)
+               if (0 != (rc = bttv_buffer_risc(btv,buf)))
+                       goto fail;
+       buf->vb.state = VIDEOBUF_PREPARED;
+       return 0;
+  fail:
+       bttv_dma_free(q,btv,buf);
+       return rc;
+ }
+ static int
+ buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
+ {
+       struct bttv_fh *fh = q->priv_data;
+       *size = fh->fmt->depth*fh->width*fh->height >> 3;
+       if (0 == *count)
+               *count = gbuffers;
+       if (*size * *count > gbuffers * gbufsize)
+               *count = (gbuffers * gbufsize) / *size;
+       return 0;
+ }
+ static int
+ buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
+              enum v4l2_field field)
+ {
+       struct bttv_buffer *buf = container_of(vb,struct bttv_buffer,vb);
+       struct bttv_fh *fh = q->priv_data;
+       return bttv_prepare_buffer(q,fh->btv, buf, fh->fmt,
+                                  fh->width, fh->height, field);
+ }
+ static void
+ buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
+ {
+       struct bttv_buffer *buf = container_of(vb,struct bttv_buffer,vb);
+       struct bttv_fh *fh = q->priv_data;
+       struct bttv    *btv = fh->btv;
+       buf->vb.state = VIDEOBUF_QUEUED;
+       list_add_tail(&buf->vb.queue,&btv->capture);
+       if (!btv->curr.frame_irq) {
+               btv->loop_irq |= 1;
+               bttv_set_dma(btv, 0x03);
+       }
+ }
+ static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+ {
+       struct bttv_buffer *buf = container_of(vb,struct bttv_buffer,vb);
+       struct bttv_fh *fh = q->priv_data;
+       bttv_dma_free(q,fh->btv,buf);
+ }
+ static struct videobuf_queue_ops bttv_video_qops = {
+       .buf_setup    = buffer_setup,
+       .buf_prepare  = buffer_prepare,
+       .buf_queue    = buffer_queue,
+       .buf_release  = buffer_release,
+ };
+ static int bttv_s_std(struct file *file, void *priv, v4l2_std_id *id)
+ {
+       struct bttv_fh *fh  = priv;
+       struct bttv *btv = fh->btv;
+       unsigned int i;
+       int err;
+       err = v4l2_prio_check(&btv->prio, fh->prio);
+       if (err)
+               goto err;
+       for (i = 0; i < BTTV_TVNORMS; i++)
+               if (*id & bttv_tvnorms[i].v4l2_id)
+                       break;
+       if (i == BTTV_TVNORMS) {
+               err = -EINVAL;
+               goto err;
+       }
+       set_tvnorm(btv, i);
+ err:
+       return err;
+ }
+ static int bttv_querystd(struct file *file, void *f, v4l2_std_id *id)
+ {
+       struct bttv_fh *fh = f;
+       struct bttv *btv = fh->btv;
+       if (btread(BT848_DSTATUS) & BT848_DSTATUS_NUML)
+               *id = V4L2_STD_625_50;
+       else
+               *id = V4L2_STD_525_60;
+       return 0;
+ }
+ static int bttv_enum_input(struct file *file, void *priv,
+                                       struct v4l2_input *i)
+ {
+       struct bttv_fh *fh = priv;
+       struct bttv *btv = fh->btv;
+       int rc = 0;
+       if (i->index >= bttv_tvcards[btv->c.type].video_inputs) {
+               rc = -EINVAL;
+               goto err;
+       }
+       i->type     = V4L2_INPUT_TYPE_CAMERA;
+       i->audioset = 1;
+       if (btv->tuner_type != TUNER_ABSENT && i->index == 0) {
+               sprintf(i->name, "Television");
+               i->type  = V4L2_INPUT_TYPE_TUNER;
+               i->tuner = 0;
+       } else if (i->index == btv->svhs) {
+               sprintf(i->name, "S-Video");
+       } else {
+               sprintf(i->name, "Composite%d", i->index);
+       }
+       if (i->index == btv->input) {
+               __u32 dstatus = btread(BT848_DSTATUS);
+               if (0 == (dstatus & BT848_DSTATUS_PRES))
+                       i->status |= V4L2_IN_ST_NO_SIGNAL;
+               if (0 == (dstatus & BT848_DSTATUS_HLOC))
+                       i->status |= V4L2_IN_ST_NO_H_LOCK;
+       }
+       i->std = BTTV_NORMS;
+ err:
+       return rc;
+ }
+ static int bttv_g_input(struct file *file, void *priv, unsigned int *i)
+ {
+       struct bttv_fh *fh = priv;
+       struct bttv *btv = fh->btv;
+       *i = btv->input;
+       return 0;
+ }
+ static int bttv_s_input(struct file *file, void *priv, unsigned int i)
+ {
+       struct bttv_fh *fh  = priv;
+       struct bttv *btv = fh->btv;
+       int err;
+       err = v4l2_prio_check(&btv->prio, fh->prio);
+       if (unlikely(err))
+               goto err;
+       if (i > bttv_tvcards[btv->c.type].video_inputs) {
+               err = -EINVAL;
+               goto err;
+       }
+       set_input(btv, i, btv->tvnorm);
+ err:
+       return 0;
+ }
+ static int bttv_s_tuner(struct file *file, void *priv,
+                                       struct v4l2_tuner *t)
+ {
+       struct bttv_fh *fh  = priv;
+       struct bttv *btv = fh->btv;
+       int err;
+       if (unlikely(0 != t->index))
+               return -EINVAL;
+       if (unlikely(btv->tuner_type == TUNER_ABSENT)) {
+               err = -EINVAL;
+               goto err;
+       }
+       err = v4l2_prio_check(&btv->prio, fh->prio);
+       if (unlikely(err))
+               goto err;
+       bttv_call_all(btv, tuner, s_tuner, t);
+       if (btv->audio_mode_gpio)
+               btv->audio_mode_gpio(btv, t, 1);
+ err:
+       return 0;
+ }
+ static int bttv_g_frequency(struct file *file, void *priv,
+                                       struct v4l2_frequency *f)
+ {
+       struct bttv_fh *fh  = priv;
+       struct bttv *btv = fh->btv;
+       f->type = btv->radio_user ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+       f->frequency = btv->freq;
+       return 0;
+ }
+ static int bttv_s_frequency(struct file *file, void *priv,
+                                       struct v4l2_frequency *f)
+ {
+       struct bttv_fh *fh  = priv;
+       struct bttv *btv = fh->btv;
+       int err;
+       if (unlikely(f->tuner != 0))
+               return -EINVAL;
+       err = v4l2_prio_check(&btv->prio, fh->prio);
+       if (unlikely(err))
+               goto err;
+       if (unlikely(f->type != (btv->radio_user
+               ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV))) {
+               err = -EINVAL;
+               goto err;
+       }
+       btv->freq = f->frequency;
+       bttv_call_all(btv, tuner, s_frequency, f);
+       if (btv->has_matchbox && btv->radio_user)
+               tea5757_set_freq(btv, btv->freq);
+ err:
+       return 0;
+ }
+ static int bttv_log_status(struct file *file, void *f)
+ {
+       struct bttv_fh *fh  = f;
+       struct bttv *btv = fh->btv;
+       bttv_call_all(btv, core, log_status);
+       return 0;
+ }
+ #ifdef CONFIG_VIDEO_ADV_DEBUG
+ static int bttv_g_register(struct file *file, void *f,
+                                       struct v4l2_dbg_register *reg)
+ {
+       struct bttv_fh *fh = f;
+       struct bttv *btv = fh->btv;
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+       if (!v4l2_chip_match_host(&reg->match))
+               return -EINVAL;
+       /* bt848 has a 12-bit register space */
+       reg->reg &= 0xfff;
+       reg->val = btread(reg->reg);
+       reg->size = 1;
+       return 0;
+ }
+ static int bttv_s_register(struct file *file, void *f,
+                                       struct v4l2_dbg_register *reg)
+ {
+       struct bttv_fh *fh = f;
+       struct bttv *btv = fh->btv;
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+       if (!v4l2_chip_match_host(&reg->match))
+               return -EINVAL;
+       /* bt848 has a 12-bit register space */
+       reg->reg &= 0xfff;
+       btwrite(reg->val, reg->reg);
+       return 0;
+ }
+ #endif
+ /* Given cropping boundaries b and the scaled width and height of a
+    single field or frame, which must not exceed hardware limits, this
+    function adjusts the cropping parameters c. */
+ static void
+ bttv_crop_adjust      (struct bttv_crop *             c,
+                        const struct v4l2_rect *       b,
+                        __s32                          width,
+                        __s32                          height,
+                        enum v4l2_field                field)
+ {
+       __s32 frame_height = height << !V4L2_FIELD_HAS_BOTH(field);
+       __s32 max_left;
+       __s32 max_top;
+       if (width < c->min_scaled_width) {
+               /* Max. hor. scale factor 16:1. */
+               c->rect.width = width * 16;
+       } else if (width > c->max_scaled_width) {
+               /* Min. hor. scale factor 1:1. */
+               c->rect.width = width;
+               max_left = b->left + b->width - width;
+               max_left = min(max_left, (__s32) MAX_HDELAY);
+               if (c->rect.left > max_left)
+                       c->rect.left = max_left;
+       }
+       if (height < c->min_scaled_height) {
+               /* Max. vert. scale factor 16:1, single fields 8:1. */
+               c->rect.height = height * 16;
+       } else if (frame_height > c->max_scaled_height) {
+               /* Min. vert. scale factor 1:1.
+                  Top and height count field lines times two. */
+               c->rect.height = (frame_height + 1) & ~1;
+               max_top = b->top + b->height - c->rect.height;
+               if (c->rect.top > max_top)
+                       c->rect.top = max_top;
+       }
+       bttv_crop_calc_limits(c);
+ }
+ /* Returns an error if scaling to a frame or single field with the given
+    width and height is not possible with the current cropping parameters
+    and width aligned according to width_mask. If adjust_size is TRUE the
+    function may adjust the width and/or height instead, rounding width
+    to (width + width_bias) & width_mask. If adjust_crop is TRUE it may
+    also adjust the current cropping parameters to get closer to the
+    desired image size. */
+ static int
+ limit_scaled_size_lock       (struct bttv_fh *               fh,
+                        __s32 *                        width,
+                        __s32 *                        height,
+                        enum v4l2_field                field,
+                        unsigned int                   width_mask,
+                        unsigned int                   width_bias,
+                        int                            adjust_size,
+                        int                            adjust_crop)
+ {
+       struct bttv *btv = fh->btv;
+       const struct v4l2_rect *b;
+       struct bttv_crop *c;
+       __s32 min_width;
+       __s32 min_height;
+       __s32 max_width;
+       __s32 max_height;
+       int rc;
+       BUG_ON((int) width_mask >= 0 ||
+              width_bias >= (unsigned int) -width_mask);
+       /* Make sure tvnorm, vbi_end and the current cropping parameters
+          remain consistent until we're done. */
+       b = &bttv_tvnorms[btv->tvnorm].cropcap.bounds;
+       /* Do crop - use current, don't - use default parameters. */
+       c = &btv->crop[!!fh->do_crop];
+       if (fh->do_crop
+           && adjust_size
+           && adjust_crop
+           && !locked_btres(btv, VIDEO_RESOURCES)) {
+               min_width = 48;
+               min_height = 32;
+               /* We cannot scale up. When the scaled image is larger
+                  than crop.rect we adjust the crop.rect as required
+                  by the V4L2 spec, hence cropcap.bounds are our limit. */
+               max_width = min(b->width, (__s32) MAX_HACTIVE);
+               max_height = b->height;
+               /* We cannot capture the same line as video and VBI data.
+                  Note btv->vbi_end is really a minimum, see
+                  bttv_vbi_try_fmt(). */
+               if (btv->vbi_end > b->top) {
+                       max_height -= btv->vbi_end - b->top;
+                       rc = -EBUSY;
+                       if (min_height > max_height)
+                               goto fail;
+               }
+       } else {
+               rc = -EBUSY;
+               if (btv->vbi_end > c->rect.top)
+                       goto fail;
+               min_width  = c->min_scaled_width;
+               min_height = c->min_scaled_height;
+               max_width  = c->max_scaled_width;
+               max_height = c->max_scaled_height;
+               adjust_crop = 0;
+       }
+       min_width = (min_width - width_mask - 1) & width_mask;
+       max_width = max_width & width_mask;
+       /* Max. scale factor is 16:1 for frames, 8:1 for fields. */
+       min_height = min_height;
+       /* Min. scale factor is 1:1. */
+       max_height >>= !V4L2_FIELD_HAS_BOTH(field);
+       if (adjust_size) {
+               *width = clamp(*width, min_width, max_width);
+               *height = clamp(*height, min_height, max_height);
+               /* Round after clamping to avoid overflow. */
+               *width = (*width + width_bias) & width_mask;
+               if (adjust_crop) {
+                       bttv_crop_adjust(c, b, *width, *height, field);
+                       if (btv->vbi_end > c->rect.top) {
+                               /* Move the crop window out of the way. */
+                               c->rect.top = btv->vbi_end;
+                       }
+               }
+       } else {
+               rc = -EINVAL;
+               if (*width  < min_width ||
+                   *height < min_height ||
+                   *width  > max_width ||
+                   *height > max_height ||
+                   0 != (*width & ~width_mask))
+                       goto fail;
+       }
+       rc = 0; /* success */
+  fail:
+       return rc;
+ }
+ /* Returns an error if the given overlay window dimensions are not
+    possible with the current cropping parameters. If adjust_size is
+    TRUE the function may adjust the window width and/or height
+    instead, however it always rounds the horizontal position and
+    width as btcx_align() does. If adjust_crop is TRUE the function
+    may also adjust the current cropping parameters to get closer
+    to the desired window size. */
+ static int
+ verify_window_lock            (struct bttv_fh *               fh,
+                        struct v4l2_window *           win,
+                        int                            adjust_size,
+                        int                            adjust_crop)
+ {
+       enum v4l2_field field;
+       unsigned int width_mask;
+       int rc;
+       if (win->w.width  < 48 || win->w.height < 32)
+               return -EINVAL;
+       if (win->clipcount > 2048)
+               return -EINVAL;
+       field = win->field;
+       if (V4L2_FIELD_ANY == field) {
+               __s32 height2;
+               height2 = fh->btv->crop[!!fh->do_crop].rect.height >> 1;
+               field = (win->w.height > height2)
+                       ? V4L2_FIELD_INTERLACED
+                       : V4L2_FIELD_TOP;
+       }
+       switch (field) {
+       case V4L2_FIELD_TOP:
+       case V4L2_FIELD_BOTTOM:
+       case V4L2_FIELD_INTERLACED:
+               break;
+       default:
+               return -EINVAL;
+       }
+       /* 4-byte alignment. */
+       if (NULL == fh->ovfmt)
+               return -EINVAL;
+       width_mask = ~0;
+       switch (fh->ovfmt->depth) {
+       case 8:
+       case 24:
+               width_mask = ~3;
+               break;
+       case 16:
+               width_mask = ~1;
+               break;
+       case 32:
+               break;
+       default:
+               BUG();
+       }
+       win->w.width -= win->w.left & ~width_mask;
+       win->w.left = (win->w.left - width_mask - 1) & width_mask;
+       rc = limit_scaled_size_lock(fh, &win->w.width, &win->w.height,
+                              field, width_mask,
+                              /* width_bias: round down */ 0,
+                              adjust_size, adjust_crop);
+       if (0 != rc)
+               return rc;
+       win->field = field;
+       return 0;
+ }
+ static int setup_window_lock(struct bttv_fh *fh, struct bttv *btv,
+                       struct v4l2_window *win, int fixup)
+ {
+       struct v4l2_clip *clips = NULL;
+       int n,size,retval = 0;
+       if (NULL == fh->ovfmt)
+               return -EINVAL;
+       if (!(fh->ovfmt->flags & FORMAT_FLAGS_PACKED))
+               return -EINVAL;
+       retval = verify_window_lock(fh, win,
+                              /* adjust_size */ fixup,
+                              /* adjust_crop */ fixup);
+       if (0 != retval)
+               return retval;
+       /* copy clips  --  luckily v4l1 + v4l2 are binary
+          compatible here ...*/
+       n = win->clipcount;
+       size = sizeof(*clips)*(n+4);
+       clips = kmalloc(size,GFP_KERNEL);
+       if (NULL == clips)
+               return -ENOMEM;
+       if (n > 0) {
+               if (copy_from_user(clips,win->clips,sizeof(struct v4l2_clip)*n)) {
+                       kfree(clips);
+                       return -EFAULT;
+               }
+       }
+       /* clip against screen */
+       if (NULL != btv->fbuf.base)
+               n = btcx_screen_clips(btv->fbuf.fmt.width, btv->fbuf.fmt.height,
+                                     &win->w, clips, n);
+       btcx_sort_clips(clips,n);
+       /* 4-byte alignments */
+       switch (fh->ovfmt->depth) {
+       case 8:
+       case 24:
+               btcx_align(&win->w, clips, n, 3);
+               break;
+       case 16:
+               btcx_align(&win->w, clips, n, 1);
+               break;
+       case 32:
+               /* no alignment fixups needed */
+               break;
+       default:
+               BUG();
+       }
+       kfree(fh->ov.clips);
+       fh->ov.clips    = clips;
+       fh->ov.nclips   = n;
+       fh->ov.w        = win->w;
+       fh->ov.field    = win->field;
+       fh->ov.setup_ok = 1;
+       btv->init.ov.w.width   = win->w.width;
+       btv->init.ov.w.height  = win->w.height;
+       btv->init.ov.field     = win->field;
+       /* update overlay if needed */
+       retval = 0;
+       if (check_btres(fh, RESOURCE_OVERLAY)) {
+               struct bttv_buffer *new;
+               new = videobuf_sg_alloc(sizeof(*new));
+               new->crop = btv->crop[!!fh->do_crop].rect;
+               bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new);
+               retval = bttv_switch_overlay(btv,fh,new);
+       }
+       return retval;
+ }
+ /* ----------------------------------------------------------------------- */
+ static struct videobuf_queue* bttv_queue(struct bttv_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 bttv_resource(struct bttv_fh *fh)
+ {
+       int res = 0;
+       switch (fh->type) {
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+               res = RESOURCE_VIDEO_STREAM;
+               break;
+       case V4L2_BUF_TYPE_VBI_CAPTURE:
+               res = RESOURCE_VBI;
+               break;
+       default:
+               BUG();
+       }
+       return res;
+ }
+ static int bttv_switch_type(struct bttv_fh *fh, enum v4l2_buf_type type)
+ {
+       struct videobuf_queue *q = bttv_queue(fh);
+       int res = bttv_resource(fh);
+       if (check_btres(fh,res))
+               return -EBUSY;
+       if (videobuf_queue_is_busy(q))
+               return -EBUSY;
+       fh->type = type;
+       return 0;
+ }
+ static void
+ pix_format_set_size     (struct v4l2_pix_format *       f,
+                        const struct bttv_format *     fmt,
+                        unsigned int                   width,
+                        unsigned int                   height)
+ {
+       f->width = width;
+       f->height = height;
+       if (fmt->flags & FORMAT_FLAGS_PLANAR) {
+               f->bytesperline = width; /* Y plane */
+               f->sizeimage = (width * height * fmt->depth) >> 3;
+       } else {
+               f->bytesperline = (width * fmt->depth) >> 3;
+               f->sizeimage = height * f->bytesperline;
+       }
+ }
+ static int bttv_g_fmt_vid_cap(struct file *file, void *priv,
+                                       struct v4l2_format *f)
+ {
+       struct bttv_fh *fh  = priv;
+       pix_format_set_size(&f->fmt.pix, fh->fmt,
+                               fh->width, fh->height);
+       f->fmt.pix.field        = fh->cap.field;
+       f->fmt.pix.pixelformat  = fh->fmt->fourcc;
+       return 0;
+ }
+ static int bttv_g_fmt_vid_overlay(struct file *file, void *priv,
+                                       struct v4l2_format *f)
+ {
+       struct bttv_fh *fh  = priv;
+       f->fmt.win.w     = fh->ov.w;
+       f->fmt.win.field = fh->ov.field;
+       return 0;
+ }
+ static int bttv_try_fmt_vid_cap(struct file *file, void *priv,
+                                               struct v4l2_format *f)
+ {
+       const struct bttv_format *fmt;
+       struct bttv_fh *fh = priv;
+       struct bttv *btv = fh->btv;
+       enum v4l2_field field;
+       __s32 width, height;
+       int rc;
+       fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+       if (NULL == fmt)
+               return -EINVAL;
+       field = f->fmt.pix.field;
+       if (V4L2_FIELD_ANY == field) {
+               __s32 height2;
+               height2 = btv->crop[!!fh->do_crop].rect.height >> 1;
+               field = (f->fmt.pix.height > height2)
+                       ? V4L2_FIELD_INTERLACED
+                       : V4L2_FIELD_BOTTOM;
+       }
+       if (V4L2_FIELD_SEQ_BT == field)
+               field = V4L2_FIELD_SEQ_TB;
+       switch (field) {
+       case V4L2_FIELD_TOP:
+       case V4L2_FIELD_BOTTOM:
+       case V4L2_FIELD_ALTERNATE:
+       case V4L2_FIELD_INTERLACED:
+               break;
+       case V4L2_FIELD_SEQ_TB:
+               if (fmt->flags & FORMAT_FLAGS_PLANAR)
+                       return -EINVAL;
+               break;
+       default:
+               return -EINVAL;
+       }
+       width = f->fmt.pix.width;
+       height = f->fmt.pix.height;
+       rc = limit_scaled_size_lock(fh, &width, &height, field,
+                              /* width_mask: 4 pixels */ ~3,
+                              /* width_bias: nearest */ 2,
+                              /* adjust_size */ 1,
+                              /* adjust_crop */ 0);
+       if (0 != rc)
+               return rc;
+       /* update data for the application */
+       f->fmt.pix.field = field;
+       pix_format_set_size(&f->fmt.pix, fmt, width, height);
+       return 0;
+ }
+ static int bttv_try_fmt_vid_overlay(struct file *file, void *priv,
+                                               struct v4l2_format *f)
+ {
+       struct bttv_fh *fh = priv;
+       return verify_window_lock(fh, &f->fmt.win,
+                       /* adjust_size */ 1,
+                       /* adjust_crop */ 0);
+ }
+ static int bttv_s_fmt_vid_cap(struct file *file, void *priv,
+                               struct v4l2_format *f)
+ {
+       int retval;
+       const struct bttv_format *fmt;
+       struct bttv_fh *fh = priv;
+       struct bttv *btv = fh->btv;
+       __s32 width, height;
+       enum v4l2_field field;
+       retval = bttv_switch_type(fh, f->type);
+       if (0 != retval)
+               return retval;
+       retval = bttv_try_fmt_vid_cap(file, priv, f);
+       if (0 != retval)
+               return retval;
+       width = f->fmt.pix.width;
+       height = f->fmt.pix.height;
+       field = f->fmt.pix.field;
+       retval = limit_scaled_size_lock(fh, &width, &height, f->fmt.pix.field,
+                              /* width_mask: 4 pixels */ ~3,
+                              /* width_bias: nearest */ 2,
+                              /* adjust_size */ 1,
+                              /* adjust_crop */ 1);
+       if (0 != retval)
+               return retval;
+       f->fmt.pix.field = field;
+       fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+       /* update our state informations */
+       fh->fmt              = fmt;
+       fh->cap.field        = f->fmt.pix.field;
+       fh->cap.last         = V4L2_FIELD_NONE;
+       fh->width            = f->fmt.pix.width;
+       fh->height           = f->fmt.pix.height;
+       btv->init.fmt        = fmt;
+       btv->init.width      = f->fmt.pix.width;
+       btv->init.height     = f->fmt.pix.height;
+       return 0;
+ }
+ static int bttv_s_fmt_vid_overlay(struct file *file, void *priv,
+                               struct v4l2_format *f)
+ {
+       struct bttv_fh *fh = priv;
+       struct bttv *btv = fh->btv;
+       if (no_overlay > 0) {
+               pr_err("V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
+               return -EINVAL;
+       }
+       return setup_window_lock(fh, btv, &f->fmt.win, 1);
+ }
+ static int bttv_querycap(struct file *file, void  *priv,
+                               struct v4l2_capability *cap)
+ {
+       struct bttv_fh *fh = priv;
+       struct bttv *btv = fh->btv;
+       if (0 == v4l2)
+               return -EINVAL;
+       strlcpy(cap->driver, "bttv", sizeof(cap->driver));
+       strlcpy(cap->card, btv->video_dev->name, sizeof(cap->card));
+       snprintf(cap->bus_info, sizeof(cap->bus_info),
+                "PCI:%s", pci_name(btv->c.pci));
+       cap->capabilities =
+               V4L2_CAP_VIDEO_CAPTURE |
+               V4L2_CAP_VBI_CAPTURE |
+               V4L2_CAP_READWRITE |
+               V4L2_CAP_STREAMING;
+       if (no_overlay <= 0)
+               cap->capabilities |= V4L2_CAP_VIDEO_OVERLAY;
+       /*
+        * No need to lock here: those vars are initialized during board
+        * probe and remains untouched during the rest of the driver lifecycle
+        */
+       if (btv->has_saa6588)
+               cap->capabilities |= V4L2_CAP_RDS_CAPTURE;
+       if (btv->tuner_type != TUNER_ABSENT)
+               cap->capabilities |= V4L2_CAP_TUNER;
+       return 0;
+ }
+ static int bttv_enum_fmt_cap_ovr(struct v4l2_fmtdesc *f)
+ {
+       int index = -1, i;
+       for (i = 0; i < FORMATS; i++) {
+               if (formats[i].fourcc != -1)
+                       index++;
+               if ((unsigned int)index == f->index)
+                       break;
+       }
+       if (FORMATS == i)
+               return -EINVAL;
+       f->pixelformat = formats[i].fourcc;
+       strlcpy(f->description, formats[i].name, sizeof(f->description));
+       return i;
+ }
+ static int bttv_enum_fmt_vid_cap(struct file *file, void  *priv,
+                               struct v4l2_fmtdesc *f)
+ {
+       int rc = bttv_enum_fmt_cap_ovr(f);
+       if (rc < 0)
+               return rc;
+       return 0;
+ }
+ static int bttv_enum_fmt_vid_overlay(struct file *file, void  *priv,
+                                       struct v4l2_fmtdesc *f)
+ {
+       int rc;
+       if (no_overlay > 0) {
+               pr_err("V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
+               return -EINVAL;
+       }
+       rc = bttv_enum_fmt_cap_ovr(f);
+       if (rc < 0)
+               return rc;
+       if (!(formats[rc].flags & FORMAT_FLAGS_PACKED))
+               return -EINVAL;
+       return 0;
+ }
+ static int bttv_g_fbuf(struct file *file, void *f,
+                               struct v4l2_framebuffer *fb)
+ {
+       struct bttv_fh *fh = f;
+       struct bttv *btv = fh->btv;
+       *fb = btv->fbuf;
+       fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING;
+       if (fh->ovfmt)
+               fb->fmt.pixelformat  = fh->ovfmt->fourcc;
+       return 0;
+ }
+ static int bttv_overlay(struct file *file, void *f, unsigned int on)
+ {
+       struct bttv_fh *fh = f;
+       struct bttv *btv = fh->btv;
+       struct bttv_buffer *new;
+       int retval = 0;
+       if (on) {
+               /* verify args */
+               if (unlikely(!btv->fbuf.base)) {
+                       return -EINVAL;
+               }
+               if (unlikely(!fh->ov.setup_ok)) {
+                       dprintk("%d: overlay: !setup_ok\n", btv->c.nr);
+                       retval = -EINVAL;
+               }
+               if (retval)
+                       return retval;
+       }
+       if (!check_alloc_btres_lock(btv, fh, RESOURCE_OVERLAY))
+               return -EBUSY;
+       if (on) {
+               fh->ov.tvnorm = btv->tvnorm;
+               new = videobuf_sg_alloc(sizeof(*new));
+               new->crop = btv->crop[!!fh->do_crop].rect;
+               bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new);
+       } else {
+               new = NULL;
+       }
+       /* switch over */
+       retval = bttv_switch_overlay(btv, fh, new);
+       return retval;
+ }
+ static int bttv_s_fbuf(struct file *file, void *f,
+                               const struct v4l2_framebuffer *fb)
+ {
+       struct bttv_fh *fh = f;
+       struct bttv *btv = fh->btv;
+       const struct bttv_format *fmt;
+       int retval;
+       if (!capable(CAP_SYS_ADMIN) &&
+               !capable(CAP_SYS_RAWIO))
+               return -EPERM;
+       /* check args */
+       fmt = format_by_fourcc(fb->fmt.pixelformat);
+       if (NULL == fmt)
+               return -EINVAL;
+       if (0 == (fmt->flags & FORMAT_FLAGS_PACKED))
+               return -EINVAL;
+       retval = -EINVAL;
+       if (fb->flags & V4L2_FBUF_FLAG_OVERLAY) {
+               __s32 width = fb->fmt.width;
+               __s32 height = fb->fmt.height;
+               retval = limit_scaled_size_lock(fh, &width, &height,
+                                          V4L2_FIELD_INTERLACED,
+                                          /* width_mask */ ~3,
+                                          /* width_bias */ 2,
+                                          /* adjust_size */ 0,
+                                          /* adjust_crop */ 0);
+               if (0 != retval)
+                       return retval;
+       }
+       /* ok, accept it */
+       btv->fbuf.base       = fb->base;
+       btv->fbuf.fmt.width  = fb->fmt.width;
+       btv->fbuf.fmt.height = fb->fmt.height;
+       if (0 != fb->fmt.bytesperline)
+               btv->fbuf.fmt.bytesperline = fb->fmt.bytesperline;
+       else
+               btv->fbuf.fmt.bytesperline = btv->fbuf.fmt.width*fmt->depth/8;
+       retval = 0;
+       fh->ovfmt = fmt;
+       btv->init.ovfmt = fmt;
+       if (fb->flags & V4L2_FBUF_FLAG_OVERLAY) {
+               fh->ov.w.left   = 0;
+               fh->ov.w.top    = 0;
+               fh->ov.w.width  = fb->fmt.width;
+               fh->ov.w.height = fb->fmt.height;
+               btv->init.ov.w.width  = fb->fmt.width;
+               btv->init.ov.w.height = fb->fmt.height;
+                       kfree(fh->ov.clips);
+               fh->ov.clips = NULL;
+               fh->ov.nclips = 0;
+               if (check_btres(fh, RESOURCE_OVERLAY)) {
+                       struct bttv_buffer *new;
+                       new = videobuf_sg_alloc(sizeof(*new));
+                       new->crop = btv->crop[!!fh->do_crop].rect;
+                       bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new);
+                       retval = bttv_switch_overlay(btv, fh, new);
+               }
+       }
+       return retval;
+ }
+ static int bttv_reqbufs(struct file *file, void *priv,
+                               struct v4l2_requestbuffers *p)
+ {
+       struct bttv_fh *fh = priv;
+       return videobuf_reqbufs(bttv_queue(fh), p);
+ }
+ static int bttv_querybuf(struct file *file, void *priv,
+                               struct v4l2_buffer *b)
+ {
+       struct bttv_fh *fh = priv;
+       return videobuf_querybuf(bttv_queue(fh), b);
+ }
+ static int bttv_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+ {
+       struct bttv_fh *fh = priv;
+       struct bttv *btv = fh->btv;
+       int res = bttv_resource(fh);
+       if (!check_alloc_btres_lock(btv, fh, res))
+               return -EBUSY;
+       return videobuf_qbuf(bttv_queue(fh), b);
+ }
+ static int bttv_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+ {
+       struct bttv_fh *fh = priv;
+       return videobuf_dqbuf(bttv_queue(fh), b,
+                       file->f_flags & O_NONBLOCK);
+ }
+ static int bttv_streamon(struct file *file, void *priv,
+                                       enum v4l2_buf_type type)
+ {
+       struct bttv_fh *fh = priv;
+       struct bttv *btv = fh->btv;
+       int res = bttv_resource(fh);
+       if (!check_alloc_btres_lock(btv, fh, res))
+               return -EBUSY;
+       return videobuf_streamon(bttv_queue(fh));
+ }
+ static int bttv_streamoff(struct file *file, void *priv,
+                                       enum v4l2_buf_type type)
+ {
+       struct bttv_fh *fh = priv;
+       struct bttv *btv = fh->btv;
+       int retval;
+       int res = bttv_resource(fh);
+       retval = videobuf_streamoff(bttv_queue(fh));
+       if (retval < 0)
+               return retval;
+       free_btres_lock(btv, fh, res);
+       return 0;
+ }
+ static int bttv_queryctrl(struct file *file, void *priv,
+                                       struct v4l2_queryctrl *c)
+ {
+       struct bttv_fh *fh = priv;
+       struct bttv *btv = fh->btv;
+       const struct v4l2_queryctrl *ctrl;
+       if ((c->id <  V4L2_CID_BASE ||
+            c->id >= V4L2_CID_LASTP1) &&
+           (c->id <  V4L2_CID_PRIVATE_BASE ||
+            c->id >= V4L2_CID_PRIVATE_LASTP1))
+               return -EINVAL;
+       if (!btv->volume_gpio && (c->id == V4L2_CID_AUDIO_VOLUME))
+               *c = no_ctl;
+       else {
+               ctrl = ctrl_by_id(c->id);
+               *c = (NULL != ctrl) ? *ctrl : no_ctl;
+       }
+       return 0;
+ }
+ static int bttv_g_parm(struct file *file, void *f,
+                               struct v4l2_streamparm *parm)
+ {
+       struct bttv_fh *fh = f;
+       struct bttv *btv = fh->btv;
+       v4l2_video_std_frame_period(bttv_tvnorms[btv->tvnorm].v4l2_id,
+                                   &parm->parm.capture.timeperframe);
+       return 0;
+ }
+ static int bttv_g_tuner(struct file *file, void *priv,
+                               struct v4l2_tuner *t)
+ {
+       struct bttv_fh *fh = priv;
+       struct bttv *btv = fh->btv;
+       if (btv->tuner_type == TUNER_ABSENT)
+               return -EINVAL;
+       if (0 != t->index)
+               return -EINVAL;
+       t->rxsubchans = V4L2_TUNER_SUB_MONO;
+       bttv_call_all(btv, tuner, g_tuner, t);
+       strcpy(t->name, "Television");
+       t->capability = V4L2_TUNER_CAP_NORM;
+       t->type       = V4L2_TUNER_ANALOG_TV;
+       if (btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC)
+               t->signal = 0xffff;
+       if (btv->audio_mode_gpio)
+               btv->audio_mode_gpio(btv, t, 0);
+       return 0;
+ }
+ static int bttv_g_priority(struct file *file, void *f, enum v4l2_priority *p)
+ {
+       struct bttv_fh *fh = f;
+       struct bttv *btv = fh->btv;
+       *p = v4l2_prio_max(&btv->prio);
+       return 0;
+ }
+ static int bttv_s_priority(struct file *file, void *f,
+                                       enum v4l2_priority prio)
+ {
+       struct bttv_fh *fh = f;
+       struct bttv *btv = fh->btv;
+       int     rc;
+       rc = v4l2_prio_change(&btv->prio, &fh->prio, prio);
+       return rc;
+ }
+ static int bttv_cropcap(struct file *file, void *priv,
+                               struct v4l2_cropcap *cap)
+ {
+       struct bttv_fh *fh = priv;
+       struct bttv *btv = fh->btv;
+       if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+           cap->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
+               return -EINVAL;
+       *cap = bttv_tvnorms[btv->tvnorm].cropcap;
+       return 0;
+ }
+ static int bttv_g_crop(struct file *file, void *f, struct v4l2_crop *crop)
+ {
+       struct bttv_fh *fh = f;
+       struct bttv *btv = fh->btv;
+       if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+           crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
+               return -EINVAL;
+       /* No fh->do_crop = 1; because btv->crop[1] may be
+          inconsistent with fh->width or fh->height and apps
+          do not expect a change here. */
+       crop->c = btv->crop[!!fh->do_crop].rect;
+       return 0;
+ }
+ static int bttv_s_crop(struct file *file, void *f, const struct v4l2_crop *crop)
+ {
+       struct bttv_fh *fh = f;
+       struct bttv *btv = fh->btv;
+       const struct v4l2_rect *b;
+       int retval;
+       struct bttv_crop c;
+       __s32 b_left;
+       __s32 b_top;
+       __s32 b_right;
+       __s32 b_bottom;
+       if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+           crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
+               return -EINVAL;
+       /* Make sure tvnorm, vbi_end and the current cropping
+          parameters remain consistent until we're done. Note
+          read() may change vbi_end in check_alloc_btres_lock(). */
+       retval = v4l2_prio_check(&btv->prio, fh->prio);
+       if (0 != retval) {
+               return retval;
+       }
+       retval = -EBUSY;
+       if (locked_btres(fh->btv, VIDEO_RESOURCES)) {
+               return retval;
+       }
+       b = &bttv_tvnorms[btv->tvnorm].cropcap.bounds;
+       b_left = b->left;
+       b_right = b_left + b->width;
+       b_bottom = b->top + b->height;
+       b_top = max(b->top, btv->vbi_end);
+       if (b_top + 32 >= b_bottom) {
+               return retval;
+       }
+       /* Min. scaled size 48 x 32. */
+       c.rect.left = clamp_t(s32, crop->c.left, b_left, b_right - 48);
+       c.rect.left = min(c.rect.left, (__s32) MAX_HDELAY);
+       c.rect.width = clamp_t(s32, crop->c.width,
+                            48, b_right - c.rect.left);
+       c.rect.top = clamp_t(s32, crop->c.top, b_top, b_bottom - 32);
+       /* Top and height must be a multiple of two. */
+       c.rect.top = (c.rect.top + 1) & ~1;
+       c.rect.height = clamp_t(s32, crop->c.height,
+                             32, b_bottom - c.rect.top);
+       c.rect.height = (c.rect.height + 1) & ~1;
+       bttv_crop_calc_limits(&c);
+       btv->crop[1] = c;
+       fh->do_crop = 1;
+       if (fh->width < c.min_scaled_width) {
+               fh->width = c.min_scaled_width;
+               btv->init.width = c.min_scaled_width;
+       } else if (fh->width > c.max_scaled_width) {
+               fh->width = c.max_scaled_width;
+               btv->init.width = c.max_scaled_width;
+       }
+       if (fh->height < c.min_scaled_height) {
+               fh->height = c.min_scaled_height;
+               btv->init.height = c.min_scaled_height;
+       } else if (fh->height > c.max_scaled_height) {
+               fh->height = c.max_scaled_height;
+               btv->init.height = c.max_scaled_height;
+       }
+       return 0;
+ }
+ static int bttv_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
+ {
+       if (unlikely(a->index))
+               return -EINVAL;
+       strcpy(a->name, "audio");
+       return 0;
+ }
+ static int bttv_s_audio(struct file *file, void *priv, const struct v4l2_audio *a)
+ {
+       if (unlikely(a->index))
+               return -EINVAL;
+       return 0;
+ }
+ static ssize_t bttv_read(struct file *file, char __user *data,
+                        size_t count, loff_t *ppos)
+ {
+       struct bttv_fh *fh = file->private_data;
+       int retval = 0;
+       if (fh->btv->errors)
+               bttv_reinit_bt848(fh->btv);
+       dprintk("%d: read count=%d type=%s\n",
+               fh->btv->c.nr, (int)count, v4l2_type_names[fh->type]);
+       switch (fh->type) {
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+               if (!check_alloc_btres_lock(fh->btv, fh, RESOURCE_VIDEO_READ)) {
+                       /* VIDEO_READ in use by another fh,
+                          or VIDEO_STREAM by any fh. */
+                       return -EBUSY;
+               }
+               retval = videobuf_read_one(&fh->cap, data, count, ppos,
+                                          file->f_flags & O_NONBLOCK);
+               free_btres_lock(fh->btv, fh, RESOURCE_VIDEO_READ);
+               break;
+       case V4L2_BUF_TYPE_VBI_CAPTURE:
+               if (!check_alloc_btres_lock(fh->btv,fh,RESOURCE_VBI))
+                       return -EBUSY;
+               retval = videobuf_read_stream(&fh->vbi, data, count, ppos, 1,
+                                             file->f_flags & O_NONBLOCK);
+               break;
+       default:
+               BUG();
+       }
+       return retval;
+ }
+ static unsigned int bttv_poll(struct file *file, poll_table *wait)
+ {
+       struct bttv_fh *fh = file->private_data;
+       struct bttv_buffer *buf;
+       enum v4l2_field field;
+       unsigned int rc = POLLERR;
+       if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) {
+               if (!check_alloc_btres_lock(fh->btv,fh,RESOURCE_VBI))
+                       return POLLERR;
+               return videobuf_poll_stream(file, &fh->vbi, wait);
+       }
+       if (check_btres(fh,RESOURCE_VIDEO_STREAM)) {
+               /* streaming capture */
+               if (list_empty(&fh->cap.stream))
+                       goto err;
+               buf = list_entry(fh->cap.stream.next,struct bttv_buffer,vb.stream);
+       } else {
+               /* read() capture */
+               if (NULL == fh->cap.read_buf) {
+                       /* need to capture a new frame */
+                       if (locked_btres(fh->btv,RESOURCE_VIDEO_STREAM))
+                               goto err;
+                       fh->cap.read_buf = videobuf_sg_alloc(fh->cap.msize);
+                       if (NULL == fh->cap.read_buf)
+                               goto err;
+                       fh->cap.read_buf->memory = V4L2_MEMORY_USERPTR;
+                       field = videobuf_next_field(&fh->cap);
+                       if (0 != fh->cap.ops->buf_prepare(&fh->cap,fh->cap.read_buf,field)) {
+                               kfree (fh->cap.read_buf);
+                               fh->cap.read_buf = NULL;
+                               goto err;
+                       }
+                       fh->cap.ops->buf_queue(&fh->cap,fh->cap.read_buf);
+                       fh->cap.read_off = 0;
+               }
+               buf = (struct bttv_buffer*)fh->cap.read_buf;
+       }
+       poll_wait(file, &buf->vb.done, wait);
+       if (buf->vb.state == VIDEOBUF_DONE ||
+           buf->vb.state == VIDEOBUF_ERROR)
+               rc =  POLLIN|POLLRDNORM;
+       else
+               rc = 0;
+ err:
+       return rc;
+ }
+ static int bttv_open(struct file *file)
+ {
+       struct video_device *vdev = video_devdata(file);
+       struct bttv *btv = video_drvdata(file);
+       struct bttv_fh *fh;
+       enum v4l2_buf_type type = 0;
+       dprintk("open dev=%s\n", video_device_node_name(vdev));
+       if (vdev->vfl_type == VFL_TYPE_GRABBER) {
+               type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       } else if (vdev->vfl_type == VFL_TYPE_VBI) {
+               type = V4L2_BUF_TYPE_VBI_CAPTURE;
+       } else {
+               WARN_ON(1);
+               return -ENODEV;
+       }
+       dprintk("%d: open called (type=%s)\n",
+               btv->c.nr, v4l2_type_names[type]);
+       /* allocate per filehandle data */
+       fh = kmalloc(sizeof(*fh), GFP_KERNEL);
+       if (unlikely(!fh))
+               return -ENOMEM;
+       file->private_data = fh;
+       *fh = btv->init;
+       fh->type = type;
+       fh->ov.setup_ok = 0;
+       v4l2_prio_open(&btv->prio, &fh->prio);
+       videobuf_queue_sg_init(&fh->cap, &bttv_video_qops,
+                           &btv->c.pci->dev, &btv->s_lock,
+                           V4L2_BUF_TYPE_VIDEO_CAPTURE,
+                           V4L2_FIELD_INTERLACED,
+                           sizeof(struct bttv_buffer),
+                           fh, &btv->lock);
+       videobuf_queue_sg_init(&fh->vbi, &bttv_vbi_qops,
+                           &btv->c.pci->dev, &btv->s_lock,
+                           V4L2_BUF_TYPE_VBI_CAPTURE,
+                           V4L2_FIELD_SEQ_TB,
+                           sizeof(struct bttv_buffer),
+                           fh, &btv->lock);
+       set_tvnorm(btv,btv->tvnorm);
+       set_input(btv, btv->input, btv->tvnorm);
+       btv->users++;
+       /* The V4L2 spec requires one global set of cropping parameters
+          which only change on request. These are stored in btv->crop[1].
+          However for compatibility with V4L apps and cropping unaware
+          V4L2 apps we now reset the cropping parameters as seen through
+          this fh, which is to say VIDIOC_G_CROP and scaling limit checks
+          will use btv->crop[0], the default cropping parameters for the
+          current video standard, and VIDIOC_S_FMT will not implicitely
+          change the cropping parameters until VIDIOC_S_CROP has been
+          called. */
+       fh->do_crop = !reset_crop; /* module parameter */
+       /* Likewise there should be one global set of VBI capture
+          parameters, but for compatibility with V4L apps and earlier
+          driver versions each fh has its own parameters. */
+       bttv_vbi_fmt_reset(&fh->vbi_fmt, btv->tvnorm);
+       bttv_field_count(btv);
+       return 0;
+ }
+ static int bttv_release(struct file *file)
+ {
+       struct bttv_fh *fh = file->private_data;
+       struct bttv *btv = fh->btv;
+       /* turn off overlay */
+       if (check_btres(fh, RESOURCE_OVERLAY))
+               bttv_switch_overlay(btv,fh,NULL);
+       /* stop video capture */
+       if (check_btres(fh, RESOURCE_VIDEO_STREAM)) {
+               videobuf_streamoff(&fh->cap);
+               free_btres_lock(btv,fh,RESOURCE_VIDEO_STREAM);
+       }
+       if (fh->cap.read_buf) {
+               buffer_release(&fh->cap,fh->cap.read_buf);
+               kfree(fh->cap.read_buf);
+       }
+       if (check_btres(fh, RESOURCE_VIDEO_READ)) {
+               free_btres_lock(btv, fh, RESOURCE_VIDEO_READ);
+       }
+       /* stop vbi capture */
+       if (check_btres(fh, RESOURCE_VBI)) {
+               videobuf_stop(&fh->vbi);
+               free_btres_lock(btv,fh,RESOURCE_VBI);
+       }
+       /* free stuff */
+       videobuf_mmap_free(&fh->cap);
+       videobuf_mmap_free(&fh->vbi);
+       v4l2_prio_close(&btv->prio, fh->prio);
+       file->private_data = NULL;
+       kfree(fh);
+       btv->users--;
+       bttv_field_count(btv);
+       if (!btv->users)
+               audio_mute(btv, 1);
+       return 0;
+ }
+ static int
+ bttv_mmap(struct file *file, struct vm_area_struct *vma)
+ {
+       struct bttv_fh *fh = file->private_data;
+       dprintk("%d: mmap type=%s 0x%lx+%ld\n",
+               fh->btv->c.nr, v4l2_type_names[fh->type],
+               vma->vm_start, vma->vm_end - vma->vm_start);
+       return videobuf_mmap_mapper(bttv_queue(fh),vma);
+ }
+ static const struct v4l2_file_operations bttv_fops =
+ {
+       .owner            = THIS_MODULE,
+       .open             = bttv_open,
+       .release          = bttv_release,
+       .unlocked_ioctl   = video_ioctl2,
+       .read             = bttv_read,
+       .mmap             = bttv_mmap,
+       .poll             = bttv_poll,
+ };
+ static const struct v4l2_ioctl_ops bttv_ioctl_ops = {
+       .vidioc_querycap                = bttv_querycap,
+       .vidioc_enum_fmt_vid_cap        = bttv_enum_fmt_vid_cap,
+       .vidioc_g_fmt_vid_cap           = bttv_g_fmt_vid_cap,
+       .vidioc_try_fmt_vid_cap         = bttv_try_fmt_vid_cap,
+       .vidioc_s_fmt_vid_cap           = bttv_s_fmt_vid_cap,
+       .vidioc_enum_fmt_vid_overlay    = bttv_enum_fmt_vid_overlay,
+       .vidioc_g_fmt_vid_overlay       = bttv_g_fmt_vid_overlay,
+       .vidioc_try_fmt_vid_overlay     = bttv_try_fmt_vid_overlay,
+       .vidioc_s_fmt_vid_overlay       = bttv_s_fmt_vid_overlay,
+       .vidioc_g_fmt_vbi_cap           = bttv_g_fmt_vbi_cap,
+       .vidioc_try_fmt_vbi_cap         = bttv_try_fmt_vbi_cap,
+       .vidioc_s_fmt_vbi_cap           = bttv_s_fmt_vbi_cap,
+       .vidioc_g_audio                 = bttv_g_audio,
+       .vidioc_s_audio                 = bttv_s_audio,
+       .vidioc_cropcap                 = bttv_cropcap,
+       .vidioc_reqbufs                 = bttv_reqbufs,
+       .vidioc_querybuf                = bttv_querybuf,
+       .vidioc_qbuf                    = bttv_qbuf,
+       .vidioc_dqbuf                   = bttv_dqbuf,
+       .vidioc_s_std                   = bttv_s_std,
+       .vidioc_enum_input              = bttv_enum_input,
+       .vidioc_g_input                 = bttv_g_input,
+       .vidioc_s_input                 = bttv_s_input,
+       .vidioc_queryctrl               = bttv_queryctrl,
+       .vidioc_g_ctrl                  = bttv_g_ctrl,
+       .vidioc_s_ctrl                  = bttv_s_ctrl,
+       .vidioc_streamon                = bttv_streamon,
+       .vidioc_streamoff               = bttv_streamoff,
+       .vidioc_g_tuner                 = bttv_g_tuner,
+       .vidioc_s_tuner                 = bttv_s_tuner,
+       .vidioc_g_crop                  = bttv_g_crop,
+       .vidioc_s_crop                  = bttv_s_crop,
+       .vidioc_g_fbuf                  = bttv_g_fbuf,
+       .vidioc_s_fbuf                  = bttv_s_fbuf,
+       .vidioc_overlay                 = bttv_overlay,
+       .vidioc_g_priority              = bttv_g_priority,
+       .vidioc_s_priority              = bttv_s_priority,
+       .vidioc_g_parm                  = bttv_g_parm,
+       .vidioc_g_frequency             = bttv_g_frequency,
+       .vidioc_s_frequency             = bttv_s_frequency,
+       .vidioc_log_status              = bttv_log_status,
+       .vidioc_querystd                = bttv_querystd,
+ #ifdef CONFIG_VIDEO_ADV_DEBUG
+       .vidioc_g_register              = bttv_g_register,
+       .vidioc_s_register              = bttv_s_register,
+ #endif
+ };
+ static struct video_device bttv_video_template = {
+       .fops         = &bttv_fops,
+       .ioctl_ops    = &bttv_ioctl_ops,
+       .tvnorms      = BTTV_NORMS,
+       .current_norm = V4L2_STD_PAL,
+ };
+ /* ----------------------------------------------------------------------- */
+ /* radio interface                                                         */
+ static int radio_open(struct file *file)
+ {
+       struct video_device *vdev = video_devdata(file);
+       struct bttv *btv = video_drvdata(file);
+       struct bttv_fh *fh;
+       dprintk("open dev=%s\n", video_device_node_name(vdev));
+       dprintk("%d: open called (radio)\n", btv->c.nr);
+       /* allocate per filehandle data */
+       fh = kmalloc(sizeof(*fh), GFP_KERNEL);
+       if (unlikely(!fh))
+               return -ENOMEM;
+       file->private_data = fh;
+       *fh = btv->init;
+       v4l2_prio_open(&btv->prio, &fh->prio);
+       btv->radio_user++;
+       bttv_call_all(btv, tuner, s_radio);
+       audio_input(btv,TVAUDIO_INPUT_RADIO);
+       return 0;
+ }
+ static int radio_release(struct file *file)
+ {
+       struct bttv_fh *fh = file->private_data;
+       struct bttv *btv = fh->btv;
+       struct saa6588_command cmd;
+       v4l2_prio_close(&btv->prio, fh->prio);
+       file->private_data = NULL;
+       kfree(fh);
+       btv->radio_user--;
+       bttv_call_all(btv, core, ioctl, SAA6588_CMD_CLOSE, &cmd);
+       return 0;
+ }
+ static int radio_querycap(struct file *file, void *priv,
+                                       struct v4l2_capability *cap)
+ {
+       struct bttv_fh *fh = priv;
+       struct bttv *btv = fh->btv;
+       strcpy(cap->driver, "bttv");
+       strlcpy(cap->card, btv->radio_dev->name, sizeof(cap->card));
+       sprintf(cap->bus_info, "PCI:%s", pci_name(btv->c.pci));
+       cap->capabilities = V4L2_CAP_TUNER;
+       return 0;
+ }
+ static int radio_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
+ {
+       struct bttv_fh *fh = priv;
+       struct bttv *btv = fh->btv;
+       if (btv->tuner_type == TUNER_ABSENT)
+               return -EINVAL;
+       if (0 != t->index)
+               return -EINVAL;
+       strcpy(t->name, "Radio");
+       t->type = V4L2_TUNER_RADIO;
+       bttv_call_all(btv, tuner, g_tuner, t);
+       if (btv->audio_mode_gpio)
+               btv->audio_mode_gpio(btv, t, 0);
+       return 0;
+ }
+ static int radio_enum_input(struct file *file, void *priv,
+                               struct v4l2_input *i)
+ {
+       if (i->index != 0)
+               return -EINVAL;
+       strcpy(i->name, "Radio");
+       i->type = V4L2_INPUT_TYPE_TUNER;
+       return 0;
+ }
+ static int radio_g_audio(struct file *file, void *priv,
+                                       struct v4l2_audio *a)
+ {
+       if (unlikely(a->index))
+               return -EINVAL;
+       strcpy(a->name, "Radio");
+       return 0;
+ }
+ static int radio_s_tuner(struct file *file, void *priv,
+                                       struct v4l2_tuner *t)
+ {
+       struct bttv_fh *fh = priv;
+       struct bttv *btv = fh->btv;
+       if (0 != t->index)
+               return -EINVAL;
+       bttv_call_all(btv, tuner, s_tuner, t);
+       return 0;
+ }
+ static int radio_s_audio(struct file *file, void *priv,
+                                       const struct v4l2_audio *a)
+ {
+       if (unlikely(a->index))
+               return -EINVAL;
+       return 0;
+ }
+ static int radio_s_input(struct file *filp, void *priv, unsigned int i)
+ {
+       if (unlikely(i))
+               return -EINVAL;
+       return 0;
+ }
+ static int radio_s_std(struct file *file, void *fh, v4l2_std_id *norm)
+ {
+       return 0;
+ }
+ static int radio_queryctrl(struct file *file, void *priv,
+                                       struct v4l2_queryctrl *c)
+ {
+       const struct v4l2_queryctrl *ctrl;
+       if (c->id <  V4L2_CID_BASE ||
+                       c->id >= V4L2_CID_LASTP1)
+               return -EINVAL;
+       if (c->id == V4L2_CID_AUDIO_MUTE) {
+               ctrl = ctrl_by_id(c->id);
+               *c = *ctrl;
+       } else
+               *c = no_ctl;
+       return 0;
+ }
+ static int radio_g_input(struct file *filp, void *priv, unsigned int *i)
+ {
+       *i = 0;
+       return 0;
+ }
+ static ssize_t radio_read(struct file *file, char __user *data,
+                        size_t count, loff_t *ppos)
+ {
+       struct bttv_fh *fh = file->private_data;
+       struct bttv *btv = fh->btv;
+       struct saa6588_command cmd;
+       cmd.block_count = count/3;
+       cmd.buffer = data;
+       cmd.instance = file;
+       cmd.result = -ENODEV;
+       bttv_call_all(btv, core, ioctl, SAA6588_CMD_READ, &cmd);
+       return cmd.result;
+ }
+ static unsigned int radio_poll(struct file *file, poll_table *wait)
+ {
+       struct bttv_fh *fh = file->private_data;
+       struct bttv *btv = fh->btv;
+       struct saa6588_command cmd;
+       cmd.instance = file;
+       cmd.event_list = wait;
+       cmd.result = -ENODEV;
+       bttv_call_all(btv, core, ioctl, SAA6588_CMD_POLL, &cmd);
+       return cmd.result;
+ }
+ static const struct v4l2_file_operations radio_fops =
+ {
+       .owner    = THIS_MODULE,
+       .open     = radio_open,
+       .read     = radio_read,
+       .release  = radio_release,
+       .unlocked_ioctl = video_ioctl2,
+       .poll     = radio_poll,
+ };
+ static const struct v4l2_ioctl_ops radio_ioctl_ops = {
+       .vidioc_querycap        = radio_querycap,
+       .vidioc_g_tuner         = radio_g_tuner,
+       .vidioc_enum_input      = radio_enum_input,
+       .vidioc_g_audio         = radio_g_audio,
+       .vidioc_s_tuner         = radio_s_tuner,
+       .vidioc_s_audio         = radio_s_audio,
+       .vidioc_s_input         = radio_s_input,
+       .vidioc_s_std           = radio_s_std,
+       .vidioc_queryctrl       = radio_queryctrl,
+       .vidioc_g_input         = radio_g_input,
+       .vidioc_g_ctrl          = bttv_g_ctrl,
+       .vidioc_s_ctrl          = bttv_s_ctrl,
+       .vidioc_g_frequency     = bttv_g_frequency,
+       .vidioc_s_frequency     = bttv_s_frequency,
+ };
+ static struct video_device radio_template = {
+       .fops      = &radio_fops,
+       .ioctl_ops = &radio_ioctl_ops,
+ };
+ /* ----------------------------------------------------------------------- */
+ /* some debug code                                                         */
+ static int bttv_risc_decode(u32 risc)
+ {
+       static char *instr[16] = {
+               [ BT848_RISC_WRITE     >> 28 ] = "write",
+               [ BT848_RISC_SKIP      >> 28 ] = "skip",
+               [ BT848_RISC_WRITEC    >> 28 ] = "writec",
+               [ BT848_RISC_JUMP      >> 28 ] = "jump",
+               [ BT848_RISC_SYNC      >> 28 ] = "sync",
+               [ BT848_RISC_WRITE123  >> 28 ] = "write123",
+               [ BT848_RISC_SKIP123   >> 28 ] = "skip123",
+               [ BT848_RISC_WRITE1S23 >> 28 ] = "write1s23",
+       };
+       static int incr[16] = {
+               [ BT848_RISC_WRITE     >> 28 ] = 2,
+               [ BT848_RISC_JUMP      >> 28 ] = 2,
+               [ BT848_RISC_SYNC      >> 28 ] = 2,
+               [ BT848_RISC_WRITE123  >> 28 ] = 5,
+               [ BT848_RISC_SKIP123   >> 28 ] = 2,
+               [ BT848_RISC_WRITE1S23 >> 28 ] = 3,
+       };
+       static char *bits[] = {
+               "be0",  "be1",  "be2",  "be3/resync",
+               "set0", "set1", "set2", "set3",
+               "clr0", "clr1", "clr2", "clr3",
+               "irq",  "res",  "eol",  "sol",
+       };
+       int i;
+       pr_cont("0x%08x [ %s", risc,
+              instr[risc >> 28] ? instr[risc >> 28] : "INVALID");
+       for (i = ARRAY_SIZE(bits)-1; i >= 0; i--)
+               if (risc & (1 << (i + 12)))
+                       pr_cont(" %s", bits[i]);
+       pr_cont(" count=%d ]\n", risc & 0xfff);
+       return incr[risc >> 28] ? incr[risc >> 28] : 1;
+ }
+ static void bttv_risc_disasm(struct bttv *btv,
+                            struct btcx_riscmem *risc)
+ {
+       unsigned int i,j,n;
+       pr_info("%s: risc disasm: %p [dma=0x%08lx]\n",
+               btv->c.v4l2_dev.name, risc->cpu, (unsigned long)risc->dma);
+       for (i = 0; i < (risc->size >> 2); i += n) {
+               pr_info("%s:   0x%lx: ",
+                       btv->c.v4l2_dev.name,
+                       (unsigned long)(risc->dma + (i<<2)));
+               n = bttv_risc_decode(le32_to_cpu(risc->cpu[i]));
+               for (j = 1; j < n; j++)
+                       pr_info("%s:   0x%lx: 0x%08x [ arg #%d ]\n",
+                               btv->c.v4l2_dev.name,
+                               (unsigned long)(risc->dma + ((i+j)<<2)),
+                               risc->cpu[i+j], j);
+               if (0 == risc->cpu[i])
+                       break;
+       }
+ }
+ static void bttv_print_riscaddr(struct bttv *btv)
+ {
+       pr_info("  main: %08llx\n", (unsigned long long)btv->main.dma);
+       pr_info("  vbi : o=%08llx e=%08llx\n",
+               btv->cvbi ? (unsigned long long)btv->cvbi->top.dma : 0,
+               btv->cvbi ? (unsigned long long)btv->cvbi->bottom.dma : 0);
+       pr_info("  cap : o=%08llx e=%08llx\n",
+               btv->curr.top
+               ? (unsigned long long)btv->curr.top->top.dma : 0,
+               btv->curr.bottom
+               ? (unsigned long long)btv->curr.bottom->bottom.dma : 0);
+       pr_info("  scr : o=%08llx e=%08llx\n",
+               btv->screen ? (unsigned long long)btv->screen->top.dma : 0,
+               btv->screen ? (unsigned long long)btv->screen->bottom.dma : 0);
+       bttv_risc_disasm(btv, &btv->main);
+ }
+ /* ----------------------------------------------------------------------- */
+ /* irq handler                                                             */
+ static char *irq_name[] = {
+       "FMTCHG",  // format change detected (525 vs. 625)
+       "VSYNC",   // vertical sync (new field)
+       "HSYNC",   // horizontal sync
+       "OFLOW",   // chroma/luma AGC overflow
+       "HLOCK",   // horizontal lock changed
+       "VPRES",   // video presence changed
+       "6", "7",
+       "I2CDONE", // hw irc operation finished
+       "GPINT",   // gpio port triggered irq
+       "10",
+       "RISCI",   // risc instruction triggered irq
+       "FBUS",    // pixel data fifo dropped data (high pci bus latencies)
+       "FTRGT",   // pixel data fifo overrun
+       "FDSR",    // fifo data stream resyncronisation
+       "PPERR",   // parity error (data transfer)
+       "RIPERR",  // parity error (read risc instructions)
+       "PABORT",  // pci abort
+       "OCERR",   // risc instruction error
+       "SCERR",   // syncronisation error
+ };
+ static void bttv_print_irqbits(u32 print, u32 mark)
+ {
+       unsigned int i;
+       pr_cont("bits:");
+       for (i = 0; i < ARRAY_SIZE(irq_name); i++) {
+               if (print & (1 << i))
+                       pr_cont(" %s", irq_name[i]);
+               if (mark & (1 << i))
+                       pr_cont("*");
+       }
+ }
+ static void bttv_irq_debug_low_latency(struct bttv *btv, u32 rc)
+ {
+       pr_warn("%d: irq: skipped frame [main=%lx,o_vbi=%lx,o_field=%lx,rc=%lx]\n",
+               btv->c.nr,
+               (unsigned long)btv->main.dma,
+               (unsigned long)le32_to_cpu(btv->main.cpu[RISC_SLOT_O_VBI+1]),
+               (unsigned long)le32_to_cpu(btv->main.cpu[RISC_SLOT_O_FIELD+1]),
+               (unsigned long)rc);
+       if (0 == (btread(BT848_DSTATUS) & BT848_DSTATUS_HLOC)) {
+               pr_notice("%d: Oh, there (temporarily?) is no input signal. "
+                         "Ok, then this is harmless, don't worry ;)\n",
+                         btv->c.nr);
+               return;
+       }
+       pr_notice("%d: Uhm. Looks like we have unusual high IRQ latencies\n",
+                 btv->c.nr);
+       pr_notice("%d: Lets try to catch the culpit red-handed ...\n",
+                 btv->c.nr);
+       dump_stack();
+ }
+ static int
+ bttv_irq_next_video(struct bttv *btv, struct bttv_buffer_set *set)
+ {
+       struct bttv_buffer *item;
+       memset(set,0,sizeof(*set));
+       /* capture request ? */
+       if (!list_empty(&btv->capture)) {
+               set->frame_irq = 1;
+               item = list_entry(btv->capture.next, struct bttv_buffer, vb.queue);
+               if (V4L2_FIELD_HAS_TOP(item->vb.field))
+                       set->top    = item;
+               if (V4L2_FIELD_HAS_BOTTOM(item->vb.field))
+                       set->bottom = item;
+               /* capture request for other field ? */
+               if (!V4L2_FIELD_HAS_BOTH(item->vb.field) &&
+                   (item->vb.queue.next != &btv->capture)) {
+                       item = list_entry(item->vb.queue.next, struct bttv_buffer, vb.queue);
+                       /* Mike Isely <isely@pobox.com> - Only check
+                        * and set up the bottom field in the logic
+                        * below.  Don't ever do the top field.  This
+                        * of course means that if we set up the
+                        * bottom field in the above code that we'll
+                        * actually skip a field.  But that's OK.
+                        * Having processed only a single buffer this
+                        * time, then the next time around the first
+                        * available buffer should be for a top field.
+                        * That will then cause us here to set up a
+                        * top then a bottom field in the normal way.
+                        * The alternative to this understanding is
+                        * that we set up the second available buffer
+                        * as a top field, but that's out of order
+                        * since this driver always processes the top
+                        * field first - the effect will be the two
+                        * buffers being returned in the wrong order,
+                        * with the second buffer also being delayed
+                        * by one field time (owing to the fifo nature
+                        * of videobuf).  Worse still, we'll be stuck
+                        * doing fields out of order now every time
+                        * until something else causes a field to be
+                        * dropped.  By effectively forcing a field to
+                        * drop this way then we always get back into
+                        * sync within a single frame time.  (Out of
+                        * order fields can screw up deinterlacing
+                        * algorithms.) */
+                       if (!V4L2_FIELD_HAS_BOTH(item->vb.field)) {
+                               if (NULL == set->bottom &&
+                                   V4L2_FIELD_BOTTOM == item->vb.field) {
+                                       set->bottom = item;
+                               }
+                               if (NULL != set->top  &&  NULL != set->bottom)
+                                       set->top_irq = 2;
+                       }
+               }
+       }
+       /* screen overlay ? */
+       if (NULL != btv->screen) {
+               if (V4L2_FIELD_HAS_BOTH(btv->screen->vb.field)) {
+                       if (NULL == set->top && NULL == set->bottom) {
+                               set->top    = btv->screen;
+                               set->bottom = btv->screen;
+                       }
+               } else {
+                       if (V4L2_FIELD_TOP == btv->screen->vb.field &&
+                           NULL == set->top) {
+                               set->top = btv->screen;
+                       }
+                       if (V4L2_FIELD_BOTTOM == btv->screen->vb.field &&
+                           NULL == set->bottom) {
+                               set->bottom = btv->screen;
+                       }
+               }
+       }
+       dprintk("%d: next set: top=%p bottom=%p [screen=%p,irq=%d,%d]\n",
+               btv->c.nr, set->top, set->bottom,
+               btv->screen, set->frame_irq, set->top_irq);
+       return 0;
+ }
+ static void
+ bttv_irq_wakeup_video(struct bttv *btv, struct bttv_buffer_set *wakeup,
+                     struct bttv_buffer_set *curr, unsigned int state)
+ {
+       struct timeval ts;
+       do_gettimeofday(&ts);
+       if (wakeup->top == wakeup->bottom) {
+               if (NULL != wakeup->top && curr->top != wakeup->top) {
+                       if (irq_debug > 1)
+                               pr_debug("%d: wakeup: both=%p\n",
+                                        btv->c.nr, wakeup->top);
+                       wakeup->top->vb.ts = ts;
+                       wakeup->top->vb.field_count = btv->field_count;
+                       wakeup->top->vb.state = state;
+                       wake_up(&wakeup->top->vb.done);
+               }
+       } else {
+               if (NULL != wakeup->top && curr->top != wakeup->top) {
+                       if (irq_debug > 1)
+                               pr_debug("%d: wakeup: top=%p\n",
+                                        btv->c.nr, wakeup->top);
+                       wakeup->top->vb.ts = ts;
+                       wakeup->top->vb.field_count = btv->field_count;
+                       wakeup->top->vb.state = state;
+                       wake_up(&wakeup->top->vb.done);
+               }
+               if (NULL != wakeup->bottom && curr->bottom != wakeup->bottom) {
+                       if (irq_debug > 1)
+                               pr_debug("%d: wakeup: bottom=%p\n",
+                                        btv->c.nr, wakeup->bottom);
+                       wakeup->bottom->vb.ts = ts;
+                       wakeup->bottom->vb.field_count = btv->field_count;
+                       wakeup->bottom->vb.state = state;
+                       wake_up(&wakeup->bottom->vb.done);
+               }
+       }
+ }
+ static void
+ bttv_irq_wakeup_vbi(struct bttv *btv, struct bttv_buffer *wakeup,
+                   unsigned int state)
+ {
+       struct timeval ts;
+       if (NULL == wakeup)
+               return;
+       do_gettimeofday(&ts);
+       wakeup->vb.ts = ts;
+       wakeup->vb.field_count = btv->field_count;
+       wakeup->vb.state = state;
+       wake_up(&wakeup->vb.done);
+ }
+ static void bttv_irq_timeout(unsigned long data)
+ {
+       struct bttv *btv = (struct bttv *)data;
+       struct bttv_buffer_set old,new;
+       struct bttv_buffer *ovbi;
+       struct bttv_buffer *item;
+       unsigned long flags;
+       if (bttv_verbose) {
+               pr_info("%d: timeout: drop=%d irq=%d/%d, risc=%08x, ",
+                       btv->c.nr, btv->framedrop, btv->irq_me, btv->irq_total,
+                       btread(BT848_RISC_COUNT));
+               bttv_print_irqbits(btread(BT848_INT_STAT),0);
+               pr_cont("\n");
+       }
+       spin_lock_irqsave(&btv->s_lock,flags);
+       /* deactivate stuff */
+       memset(&new,0,sizeof(new));
+       old  = btv->curr;
+       ovbi = btv->cvbi;
+       btv->curr = new;
+       btv->cvbi = NULL;
+       btv->loop_irq = 0;
+       bttv_buffer_activate_video(btv, &new);
+       bttv_buffer_activate_vbi(btv,   NULL);
+       bttv_set_dma(btv, 0);
+       /* wake up */
+       bttv_irq_wakeup_video(btv, &old, &new, VIDEOBUF_ERROR);
+       bttv_irq_wakeup_vbi(btv, ovbi, VIDEOBUF_ERROR);
+       /* cancel all outstanding capture / vbi requests */
+       while (!list_empty(&btv->capture)) {
+               item = list_entry(btv->capture.next, struct bttv_buffer, vb.queue);
+               list_del(&item->vb.queue);
+               item->vb.state = VIDEOBUF_ERROR;
+               wake_up(&item->vb.done);
+       }
+       while (!list_empty(&btv->vcapture)) {
+               item = list_entry(btv->vcapture.next, struct bttv_buffer, vb.queue);
+               list_del(&item->vb.queue);
+               item->vb.state = VIDEOBUF_ERROR;
+               wake_up(&item->vb.done);
+       }
+       btv->errors++;
+       spin_unlock_irqrestore(&btv->s_lock,flags);
+ }
+ static void
+ bttv_irq_wakeup_top(struct bttv *btv)
+ {
+       struct bttv_buffer *wakeup = btv->curr.top;
+       if (NULL == wakeup)
+               return;
+       spin_lock(&btv->s_lock);
+       btv->curr.top_irq = 0;
+       btv->curr.top = NULL;
+       bttv_risc_hook(btv, RISC_SLOT_O_FIELD, NULL, 0);
+       do_gettimeofday(&wakeup->vb.ts);
+       wakeup->vb.field_count = btv->field_count;
+       wakeup->vb.state = VIDEOBUF_DONE;
+       wake_up(&wakeup->vb.done);
+       spin_unlock(&btv->s_lock);
+ }
+ static inline int is_active(struct btcx_riscmem *risc, u32 rc)
+ {
+       if (rc < risc->dma)
+               return 0;
+       if (rc > risc->dma + risc->size)
+               return 0;
+       return 1;
+ }
+ static void
+ bttv_irq_switch_video(struct bttv *btv)
+ {
+       struct bttv_buffer_set new;
+       struct bttv_buffer_set old;
+       dma_addr_t rc;
+       spin_lock(&btv->s_lock);
+       /* new buffer set */
+       bttv_irq_next_video(btv, &new);
+       rc = btread(BT848_RISC_COUNT);
+       if ((btv->curr.top    && is_active(&btv->curr.top->top,       rc)) ||
+           (btv->curr.bottom && is_active(&btv->curr.bottom->bottom, rc))) {
+               btv->framedrop++;
+               if (debug_latency)
+                       bttv_irq_debug_low_latency(btv, rc);
+               spin_unlock(&btv->s_lock);
+               return;
+       }
+       /* switch over */
+       old = btv->curr;
+       btv->curr = new;
+       btv->loop_irq &= ~1;
+       bttv_buffer_activate_video(btv, &new);
+       bttv_set_dma(btv, 0);
+       /* switch input */
+       if (UNSET != btv->new_input) {
+               video_mux(btv,btv->new_input);
+               btv->new_input = UNSET;
+       }
+       /* wake up finished buffers */
+       bttv_irq_wakeup_video(btv, &old, &new, VIDEOBUF_DONE);
+       spin_unlock(&btv->s_lock);
+ }
+ static void
+ bttv_irq_switch_vbi(struct bttv *btv)
+ {
+       struct bttv_buffer *new = NULL;
+       struct bttv_buffer *old;
+       u32 rc;
+       spin_lock(&btv->s_lock);
+       if (!list_empty(&btv->vcapture))
+               new = list_entry(btv->vcapture.next, struct bttv_buffer, vb.queue);
+       old = btv->cvbi;
+       rc = btread(BT848_RISC_COUNT);
+       if (NULL != old && (is_active(&old->top,    rc) ||
+                           is_active(&old->bottom, rc))) {
+               btv->framedrop++;
+               if (debug_latency)
+                       bttv_irq_debug_low_latency(btv, rc);
+               spin_unlock(&btv->s_lock);
+               return;
+       }
+       /* switch */
+       btv->cvbi = new;
+       btv->loop_irq &= ~4;
+       bttv_buffer_activate_vbi(btv, new);
+       bttv_set_dma(btv, 0);
+       bttv_irq_wakeup_vbi(btv, old, VIDEOBUF_DONE);
+       spin_unlock(&btv->s_lock);
+ }
+ static irqreturn_t bttv_irq(int irq, void *dev_id)
+ {
+       u32 stat,astat;
+       u32 dstat;
+       int count;
+       struct bttv *btv;
+       int handled = 0;
+       btv=(struct bttv *)dev_id;
+       count=0;
+       while (1) {
+               /* get/clear interrupt status bits */
+               stat=btread(BT848_INT_STAT);
+               astat=stat&btread(BT848_INT_MASK);
+               if (!astat)
+                       break;
+               handled = 1;
+               btwrite(stat,BT848_INT_STAT);
+               /* get device status bits */
+               dstat=btread(BT848_DSTATUS);
+               if (irq_debug) {
+                       pr_debug("%d: irq loop=%d fc=%d riscs=%x, riscc=%08x, ",
+                                btv->c.nr, count, btv->field_count,
+                                stat>>28, btread(BT848_RISC_COUNT));
+                       bttv_print_irqbits(stat,astat);
+                       if (stat & BT848_INT_HLOCK)
+                               pr_cont("   HLOC => %s",
+                                       dstat & BT848_DSTATUS_HLOC
+                                       ? "yes" : "no");
+                       if (stat & BT848_INT_VPRES)
+                               pr_cont("   PRES => %s",
+                                       dstat & BT848_DSTATUS_PRES
+                                       ? "yes" : "no");
+                       if (stat & BT848_INT_FMTCHG)
+                               pr_cont("   NUML => %s",
+                                       dstat & BT848_DSTATUS_NUML
+                                       ? "625" : "525");
+                       pr_cont("\n");
+               }
+               if (astat&BT848_INT_VSYNC)
+                       btv->field_count++;
+               if ((astat & BT848_INT_GPINT) && btv->remote) {
+                       bttv_input_irq(btv);
+               }
+               if (astat & BT848_INT_I2CDONE) {
+                       btv->i2c_done = stat;
+                       wake_up(&btv->i2c_queue);
+               }
+               if ((astat & BT848_INT_RISCI)  &&  (stat & (4<<28)))
+                       bttv_irq_switch_vbi(btv);
+               if ((astat & BT848_INT_RISCI)  &&  (stat & (2<<28)))
+                       bttv_irq_wakeup_top(btv);
+               if ((astat & BT848_INT_RISCI)  &&  (stat & (1<<28)))
+                       bttv_irq_switch_video(btv);
+               if ((astat & BT848_INT_HLOCK)  &&  btv->opt_automute)
+                       audio_mute(btv, btv->mute);  /* trigger automute */
+               if (astat & (BT848_INT_SCERR|BT848_INT_OCERR)) {
+                       pr_info("%d: %s%s @ %08x,",
+                               btv->c.nr,
+                               (astat & BT848_INT_SCERR) ? "SCERR" : "",
+                               (astat & BT848_INT_OCERR) ? "OCERR" : "",
+                               btread(BT848_RISC_COUNT));
+                       bttv_print_irqbits(stat,astat);
+                       pr_cont("\n");
+                       if (bttv_debug)
+                               bttv_print_riscaddr(btv);
+               }
+               if (fdsr && astat & BT848_INT_FDSR) {
+                       pr_info("%d: FDSR @ %08x\n",
+                               btv->c.nr, btread(BT848_RISC_COUNT));
+                       if (bttv_debug)
+                               bttv_print_riscaddr(btv);
+               }
+               count++;
+               if (count > 4) {
+                       if (count > 8 || !(astat & BT848_INT_GPINT)) {
+                               btwrite(0, BT848_INT_MASK);
+                               pr_err("%d: IRQ lockup, cleared int mask [",
+                                      btv->c.nr);
+                       } else {
+                               pr_err("%d: IRQ lockup, clearing GPINT from int mask [",
+                                      btv->c.nr);
+                               btwrite(btread(BT848_INT_MASK) & (-1 ^ BT848_INT_GPINT),
+                                               BT848_INT_MASK);
+                       };
+                       bttv_print_irqbits(stat,astat);
+                       pr_cont("]\n");
+               }
+       }
+       btv->irq_total++;
+       if (handled)
+               btv->irq_me++;
+       return IRQ_RETVAL(handled);
+ }
+ /* ----------------------------------------------------------------------- */
+ /* initialitation                                                          */
+ static struct video_device *vdev_init(struct bttv *btv,
+                                     const struct video_device *template,
+                                     const char *type_name)
+ {
+       struct video_device *vfd;
+       vfd = video_device_alloc();
+       if (NULL == vfd)
+               return NULL;
+       *vfd = *template;
+       vfd->v4l2_dev = &btv->c.v4l2_dev;
+       vfd->release = video_device_release;
+       vfd->debug   = bttv_debug;
+       video_set_drvdata(vfd, btv);
+       snprintf(vfd->name, sizeof(vfd->name), "BT%d%s %s (%s)",
+                btv->id, (btv->id==848 && btv->revision==0x12) ? "A" : "",
+                type_name, bttv_tvcards[btv->c.type].name);
+       return vfd;
+ }
+ static void bttv_unregister_video(struct bttv *btv)
+ {
+       if (btv->video_dev) {
+               if (video_is_registered(btv->video_dev))
+                       video_unregister_device(btv->video_dev);
+               else
+                       video_device_release(btv->video_dev);
+               btv->video_dev = NULL;
+       }
+       if (btv->vbi_dev) {
+               if (video_is_registered(btv->vbi_dev))
+                       video_unregister_device(btv->vbi_dev);
+               else
+                       video_device_release(btv->vbi_dev);
+               btv->vbi_dev = NULL;
+       }
+       if (btv->radio_dev) {
+               if (video_is_registered(btv->radio_dev))
+                       video_unregister_device(btv->radio_dev);
+               else
+                       video_device_release(btv->radio_dev);
+               btv->radio_dev = NULL;
+       }
+ }
+ /* register video4linux devices */
+ static int __devinit bttv_register_video(struct bttv *btv)
+ {
+       if (no_overlay > 0)
+               pr_notice("Overlay support disabled\n");
+       /* video */
+       btv->video_dev = vdev_init(btv, &bttv_video_template, "video");
+       if (NULL == btv->video_dev)
+               goto err;
+       if (video_register_device(btv->video_dev, VFL_TYPE_GRABBER,
+                                 video_nr[btv->c.nr]) < 0)
+               goto err;
+       pr_info("%d: registered device %s\n",
+               btv->c.nr, video_device_node_name(btv->video_dev));
+       if (device_create_file(&btv->video_dev->dev,
+                                    &dev_attr_card)<0) {
+               pr_err("%d: device_create_file 'card' failed\n", btv->c.nr);
+               goto err;
+       }
+       /* vbi */
+       btv->vbi_dev = vdev_init(btv, &bttv_video_template, "vbi");
+       if (NULL == btv->vbi_dev)
+               goto err;
+       if (video_register_device(btv->vbi_dev, VFL_TYPE_VBI,
+                                 vbi_nr[btv->c.nr]) < 0)
+               goto err;
+       pr_info("%d: registered device %s\n",
+               btv->c.nr, video_device_node_name(btv->vbi_dev));
+       if (!btv->has_radio)
+               return 0;
+       /* radio */
+       btv->radio_dev = vdev_init(btv, &radio_template, "radio");
+       if (NULL == btv->radio_dev)
+               goto err;
+       if (video_register_device(btv->radio_dev, VFL_TYPE_RADIO,
+                                 radio_nr[btv->c.nr]) < 0)
+               goto err;
+       pr_info("%d: registered device %s\n",
+               btv->c.nr, video_device_node_name(btv->radio_dev));
+       /* all done */
+       return 0;
+  err:
+       bttv_unregister_video(btv);
+       return -1;
+ }
+ /* on OpenFirmware machines (PowerMac at least), PCI memory cycle */
+ /* response on cards with no firmware is not enabled by OF */
+ static void pci_set_command(struct pci_dev *dev)
+ {
+ #if defined(__powerpc__)
+       unsigned int cmd;
+       pci_read_config_dword(dev, PCI_COMMAND, &cmd);
+       cmd = (cmd | PCI_COMMAND_MEMORY );
+       pci_write_config_dword(dev, PCI_COMMAND, cmd);
+ #endif
+ }
+ static int __devinit bttv_probe(struct pci_dev *dev,
+                               const struct pci_device_id *pci_id)
+ {
+       int result;
+       unsigned char lat;
+       struct bttv *btv;
+       if (bttv_num == BTTV_MAX)
+               return -ENOMEM;
+       pr_info("Bt8xx card found (%d)\n", bttv_num);
+       bttvs[bttv_num] = btv = kzalloc(sizeof(*btv), GFP_KERNEL);
+       if (btv == NULL) {
+               pr_err("out of memory\n");
+               return -ENOMEM;
+       }
+       btv->c.nr  = bttv_num;
+       snprintf(btv->c.v4l2_dev.name, sizeof(btv->c.v4l2_dev.name),
+                       "bttv%d", btv->c.nr);
+       /* initialize structs / fill in defaults */
+       mutex_init(&btv->lock);
+       spin_lock_init(&btv->s_lock);
+       spin_lock_init(&btv->gpio_lock);
+       init_waitqueue_head(&btv->i2c_queue);
+       INIT_LIST_HEAD(&btv->c.subs);
+       INIT_LIST_HEAD(&btv->capture);
+       INIT_LIST_HEAD(&btv->vcapture);
+       v4l2_prio_init(&btv->prio);
+       init_timer(&btv->timeout);
+       btv->timeout.function = bttv_irq_timeout;
+       btv->timeout.data     = (unsigned long)btv;
+       btv->i2c_rc = -1;
+       btv->tuner_type  = UNSET;
+       btv->new_input   = UNSET;
+       btv->has_radio=radio[btv->c.nr];
+       /* pci stuff (init, get irq/mmio, ... */
+       btv->c.pci = dev;
+       btv->id  = dev->device;
+       if (pci_enable_device(dev)) {
+               pr_warn("%d: Can't enable device\n", btv->c.nr);
+               return -EIO;
+       }
+       if (pci_set_dma_mask(dev, DMA_BIT_MASK(32))) {
+               pr_warn("%d: No suitable DMA available\n", btv->c.nr);
+               return -EIO;
+       }
+       if (!request_mem_region(pci_resource_start(dev,0),
+                               pci_resource_len(dev,0),
+                               btv->c.v4l2_dev.name)) {
+               pr_warn("%d: can't request iomem (0x%llx)\n",
+                       btv->c.nr,
+                       (unsigned long long)pci_resource_start(dev, 0));
+               return -EBUSY;
+       }
+       pci_set_master(dev);
+       pci_set_command(dev);
+       result = v4l2_device_register(&dev->dev, &btv->c.v4l2_dev);
+       if (result < 0) {
+               pr_warn("%d: v4l2_device_register() failed\n", btv->c.nr);
+               goto fail0;
+       }
+       btv->revision = dev->revision;
+       pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat);
+       pr_info("%d: Bt%d (rev %d) at %s, irq: %d, latency: %d, mmio: 0x%llx\n",
+               bttv_num, btv->id, btv->revision, pci_name(dev),
+               btv->c.pci->irq, lat,
+               (unsigned long long)pci_resource_start(dev, 0));
+       schedule();
+       btv->bt848_mmio = ioremap(pci_resource_start(dev, 0), 0x1000);
+       if (NULL == btv->bt848_mmio) {
+               pr_err("%d: ioremap() failed\n", btv->c.nr);
+               result = -EIO;
+               goto fail1;
+       }
+       /* identify card */
+       bttv_idcard(btv);
+       /* disable irqs, register irq handler */
+       btwrite(0, BT848_INT_MASK);
+       result = request_irq(btv->c.pci->irq, bttv_irq,
+           IRQF_SHARED | IRQF_DISABLED, btv->c.v4l2_dev.name, (void *)btv);
+       if (result < 0) {
+               pr_err("%d: can't get IRQ %d\n",
+                      bttv_num, btv->c.pci->irq);
+               goto fail1;
+       }
+       if (0 != bttv_handle_chipset(btv)) {
+               result = -EIO;
+               goto fail2;
+       }
+       /* init options from insmod args */
+       btv->opt_combfilter = combfilter;
+       btv->opt_lumafilter = lumafilter;
+       btv->opt_automute   = automute;
+       btv->opt_chroma_agc = chroma_agc;
+       btv->opt_adc_crush  = adc_crush;
+       btv->opt_vcr_hack   = vcr_hack;
+       btv->opt_whitecrush_upper  = whitecrush_upper;
+       btv->opt_whitecrush_lower  = whitecrush_lower;
+       btv->opt_uv_ratio   = uv_ratio;
+       btv->opt_full_luma_range   = full_luma_range;
+       btv->opt_coring     = coring;
+       /* fill struct bttv with some useful defaults */
+       btv->init.btv         = btv;
+       btv->init.ov.w.width  = 320;
+       btv->init.ov.w.height = 240;
+       btv->init.fmt         = format_by_fourcc(V4L2_PIX_FMT_BGR24);
+       btv->init.width       = 320;
+       btv->init.height      = 240;
+       btv->input = 0;
+       /* initialize hardware */
+       if (bttv_gpio)
+               bttv_gpio_tracking(btv,"pre-init");
+       bttv_risc_init_main(btv);
+       init_bt848(btv);
+       /* gpio */
+       btwrite(0x00, BT848_GPIO_REG_INP);
+       btwrite(0x00, BT848_GPIO_OUT_EN);
+       if (bttv_verbose)
+               bttv_gpio_tracking(btv,"init");
+       /* needs to be done before i2c is registered */
+       bttv_init_card1(btv);
+       /* register i2c + gpio */
+       init_bttv_i2c(btv);
+       /* some card-specific stuff (needs working i2c) */
+       bttv_init_card2(btv);
+       bttv_init_tuner(btv);
+       init_irqreg(btv);
+       /* register video4linux + input */
+       if (!bttv_tvcards[btv->c.type].no_video) {
+               bttv_register_video(btv);
+               bt848_bright(btv,32768);
+               bt848_contrast(btv, 27648);
+               bt848_hue(btv,32768);
+               bt848_sat(btv,32768);
+               audio_mute(btv, 1);
+               set_input(btv, 0, btv->tvnorm);
+               bttv_crop_reset(&btv->crop[0], btv->tvnorm);
+               btv->crop[1] = btv->crop[0]; /* current = default */
+               disclaim_vbi_lines(btv);
+               disclaim_video_lines(btv);
+       }
+       /* add subdevices and autoload dvb-bt8xx if needed */
+       if (bttv_tvcards[btv->c.type].has_dvb) {
+               bttv_sub_add_device(&btv->c, "dvb");
+               request_modules(btv);
+       }
+       if (!disable_ir) {
+               init_bttv_i2c_ir(btv);
+               bttv_input_init(btv);
+       }
+       /* everything is fine */
+       bttv_num++;
+       return 0;
+ fail2:
+       free_irq(btv->c.pci->irq,btv);
+ fail1:
+       v4l2_device_unregister(&btv->c.v4l2_dev);
+ fail0:
+       if (btv->bt848_mmio)
+               iounmap(btv->bt848_mmio);
+       release_mem_region(pci_resource_start(btv->c.pci,0),
+                          pci_resource_len(btv->c.pci,0));
+       return result;
+ }
+ static void __devexit bttv_remove(struct pci_dev *pci_dev)
+ {
+       struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
+       struct bttv *btv = to_bttv(v4l2_dev);
+       if (bttv_verbose)
+               pr_info("%d: unloading\n", btv->c.nr);
+       if (bttv_tvcards[btv->c.type].has_dvb)
+               flush_request_modules(btv);
+       /* shutdown everything (DMA+IRQs) */
+       btand(~15, BT848_GPIO_DMA_CTL);
+       btwrite(0, BT848_INT_MASK);
+       btwrite(~0x0, BT848_INT_STAT);
+       btwrite(0x0, BT848_GPIO_OUT_EN);
+       if (bttv_gpio)
+               bttv_gpio_tracking(btv,"cleanup");
+       /* tell gpio modules we are leaving ... */
+       btv->shutdown=1;
+       bttv_input_fini(btv);
+       bttv_sub_del_devices(&btv->c);
+       /* unregister i2c_bus + input */
+       fini_bttv_i2c(btv);
+       /* unregister video4linux */
+       bttv_unregister_video(btv);
+       /* free allocated memory */
+       btcx_riscmem_free(btv->c.pci,&btv->main);
+       /* free ressources */
+       free_irq(btv->c.pci->irq,btv);
+       iounmap(btv->bt848_mmio);
+       release_mem_region(pci_resource_start(btv->c.pci,0),
+                          pci_resource_len(btv->c.pci,0));
+       v4l2_device_unregister(&btv->c.v4l2_dev);
+       bttvs[btv->c.nr] = NULL;
+       kfree(btv);
+       return;
+ }
+ #ifdef CONFIG_PM
+ static int bttv_suspend(struct pci_dev *pci_dev, pm_message_t state)
+ {
+       struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
+       struct bttv *btv = to_bttv(v4l2_dev);
+       struct bttv_buffer_set idle;
+       unsigned long flags;
+       dprintk("%d: suspend %d\n", btv->c.nr, state.event);
+       /* stop dma + irqs */
+       spin_lock_irqsave(&btv->s_lock,flags);
+       memset(&idle, 0, sizeof(idle));
+       btv->state.video = btv->curr;
+       btv->state.vbi   = btv->cvbi;
+       btv->state.loop_irq = btv->loop_irq;
+       btv->curr = idle;
+       btv->loop_irq = 0;
+       bttv_buffer_activate_video(btv, &idle);
+       bttv_buffer_activate_vbi(btv, NULL);
+       bttv_set_dma(btv, 0);
+       btwrite(0, BT848_INT_MASK);
+       spin_unlock_irqrestore(&btv->s_lock,flags);
+       /* save bt878 state */
+       btv->state.gpio_enable = btread(BT848_GPIO_OUT_EN);
+       btv->state.gpio_data   = gpio_read();
+       /* save pci state */
+       pci_save_state(pci_dev);
+       if (0 != pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state))) {
+               pci_disable_device(pci_dev);
+               btv->state.disabled = 1;
+       }
+       return 0;
+ }
+ static int bttv_resume(struct pci_dev *pci_dev)
+ {
+       struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
+       struct bttv *btv = to_bttv(v4l2_dev);
+       unsigned long flags;
+       int err;
+       dprintk("%d: resume\n", btv->c.nr);
+       /* restore pci state */
+       if (btv->state.disabled) {
+               err=pci_enable_device(pci_dev);
+               if (err) {
+                       pr_warn("%d: Can't enable device\n", btv->c.nr);
+                       return err;
+               }
+               btv->state.disabled = 0;
+       }
+       err=pci_set_power_state(pci_dev, PCI_D0);
+       if (err) {
+               pci_disable_device(pci_dev);
+               pr_warn("%d: Can't enable device\n", btv->c.nr);
+               btv->state.disabled = 1;
+               return err;
+       }
+       pci_restore_state(pci_dev);
+       /* restore bt878 state */
+       bttv_reinit_bt848(btv);
+       gpio_inout(0xffffff, btv->state.gpio_enable);
+       gpio_write(btv->state.gpio_data);
+       /* restart dma */
+       spin_lock_irqsave(&btv->s_lock,flags);
+       btv->curr = btv->state.video;
+       btv->cvbi = btv->state.vbi;
+       btv->loop_irq = btv->state.loop_irq;
+       bttv_buffer_activate_video(btv, &btv->curr);
+       bttv_buffer_activate_vbi(btv, btv->cvbi);
+       bttv_set_dma(btv, 0);
+       spin_unlock_irqrestore(&btv->s_lock,flags);
+       return 0;
+ }
+ #endif
+ static struct pci_device_id bttv_pci_tbl[] = {
+       {PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT848), 0},
+       {PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT849), 0},
+       {PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT878), 0},
+       {PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT879), 0},
+       {PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_FUSION879), 0},
+       {0,}
+ };
+ MODULE_DEVICE_TABLE(pci, bttv_pci_tbl);
+ static struct pci_driver bttv_pci_driver = {
+       .name     = "bttv",
+       .id_table = bttv_pci_tbl,
+       .probe    = bttv_probe,
+       .remove   = __devexit_p(bttv_remove),
+ #ifdef CONFIG_PM
+       .suspend  = bttv_suspend,
+       .resume   = bttv_resume,
+ #endif
+ };
+ static int __init bttv_init_module(void)
+ {
+       int ret;
+       bttv_num = 0;
+       pr_info("driver version %s loaded\n", BTTV_VERSION);
+       if (gbuffers < 2 || gbuffers > VIDEO_MAX_FRAME)
+               gbuffers = 2;
+       if (gbufsize > BTTV_MAX_FBUF)
+               gbufsize = BTTV_MAX_FBUF;
+       gbufsize = (gbufsize + PAGE_SIZE - 1) & PAGE_MASK;
+       if (bttv_verbose)
+               pr_info("using %d buffers with %dk (%d pages) each for capture\n",
+                       gbuffers, gbufsize >> 10, gbufsize >> PAGE_SHIFT);
+       bttv_check_chipset();
+       ret = bus_register(&bttv_sub_bus_type);
+       if (ret < 0) {
+               pr_warn("bus_register error: %d\n", ret);
+               return ret;
+       }
+       ret = pci_register_driver(&bttv_pci_driver);
+       if (ret < 0)
+               bus_unregister(&bttv_sub_bus_type);
+       return ret;
+ }
+ static void __exit bttv_cleanup_module(void)
+ {
+       pci_unregister_driver(&bttv_pci_driver);
+       bus_unregister(&bttv_sub_bus_type);
+ }
+ module_init(bttv_init_module);
+ module_exit(bttv_cleanup_module);
+ /*
+  * Local variables:
+  * c-basic-offset: 8
+  * End:
+  */
index 0000000,c67733d..039133d
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,1360 +1,1360 @@@
 -      flush_work_sync(&dev->request_module_wk);
+ /*
+  *  cx18 driver initialization and card probing
+  *
+  *  Derived from ivtv-driver.c
+  *
+  *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
+  *  Copyright (C) 2008  Andy Walls <awalls@md.metrocast.net>
+  *
+  *  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., 59 Temple Place, Suite 330, Boston, MA
+  *  02111-1307  USA
+  */
+ #include "cx18-driver.h"
+ #include "cx18-io.h"
+ #include "cx18-version.h"
+ #include "cx18-cards.h"
+ #include "cx18-i2c.h"
+ #include "cx18-irq.h"
+ #include "cx18-gpio.h"
+ #include "cx18-firmware.h"
+ #include "cx18-queue.h"
+ #include "cx18-streams.h"
+ #include "cx18-av-core.h"
+ #include "cx18-scb.h"
+ #include "cx18-mailbox.h"
+ #include "cx18-ioctl.h"
+ #include "cx18-controls.h"
+ #include "tuner-xc2028.h"
+ #include <linux/dma-mapping.h>
+ #include <media/tveeprom.h>
+ /* If you have already X v4l cards, then set this to X. This way
+    the device numbers stay matched. Example: you have a WinTV card
+    without radio and a Compro H900 with. Normally this would give a
+    video1 device together with a radio0 device for the Compro. By
+    setting this to 1 you ensure that radio0 is now also radio1. */
+ int cx18_first_minor;
+ /* Callback for registering extensions */
+ int (*cx18_ext_init)(struct cx18 *);
+ EXPORT_SYMBOL(cx18_ext_init);
+ /* add your revision and whatnot here */
+ static struct pci_device_id cx18_pci_tbl[] __devinitdata = {
+       {PCI_VENDOR_ID_CX, PCI_DEVICE_ID_CX23418,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {0,}
+ };
+ MODULE_DEVICE_TABLE(pci, cx18_pci_tbl);
+ static atomic_t cx18_instance = ATOMIC_INIT(0);
+ /* Parameter declarations */
+ static int cardtype[CX18_MAX_CARDS];
+ static int tuner[CX18_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1,
+                                    -1, -1, -1, -1, -1, -1, -1, -1,
+                                    -1, -1, -1, -1, -1, -1, -1, -1,
+                                    -1, -1, -1, -1, -1, -1, -1, -1 };
+ static int radio[CX18_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1,
+                                    -1, -1, -1, -1, -1, -1, -1, -1,
+                                    -1, -1, -1, -1, -1, -1, -1, -1,
+                                    -1, -1, -1, -1, -1, -1, -1, -1 };
+ static unsigned cardtype_c = 1;
+ static unsigned tuner_c = 1;
+ static unsigned radio_c = 1;
+ static char pal[] = "--";
+ static char secam[] = "--";
+ static char ntsc[] = "-";
+ /* Buffers */
+ static int enc_ts_buffers = CX18_DEFAULT_ENC_TS_BUFFERS;
+ static int enc_mpg_buffers = CX18_DEFAULT_ENC_MPG_BUFFERS;
+ static int enc_idx_buffers = CX18_DEFAULT_ENC_IDX_BUFFERS;
+ static int enc_yuv_buffers = CX18_DEFAULT_ENC_YUV_BUFFERS;
+ static int enc_vbi_buffers = CX18_DEFAULT_ENC_VBI_BUFFERS;
+ static int enc_pcm_buffers = CX18_DEFAULT_ENC_PCM_BUFFERS;
+ static int enc_ts_bufsize = CX18_DEFAULT_ENC_TS_BUFSIZE;
+ static int enc_mpg_bufsize = CX18_DEFAULT_ENC_MPG_BUFSIZE;
+ static int enc_idx_bufsize = CX18_DEFAULT_ENC_IDX_BUFSIZE;
+ static int enc_yuv_bufsize = CX18_DEFAULT_ENC_YUV_BUFSIZE;
+ static int enc_pcm_bufsize = CX18_DEFAULT_ENC_PCM_BUFSIZE;
+ static int enc_ts_bufs = -1;
+ static int enc_mpg_bufs = -1;
+ static int enc_idx_bufs = CX18_MAX_FW_MDLS_PER_STREAM;
+ static int enc_yuv_bufs = -1;
+ static int enc_vbi_bufs = -1;
+ static int enc_pcm_bufs = -1;
+ static int cx18_pci_latency = 1;
+ static int mmio_ndelay;
+ static int retry_mmio = 1;
+ int cx18_debug;
+ module_param_array(tuner, int, &tuner_c, 0644);
+ module_param_array(radio, int, &radio_c, 0644);
+ module_param_array(cardtype, int, &cardtype_c, 0644);
+ module_param_string(pal, pal, sizeof(pal), 0644);
+ module_param_string(secam, secam, sizeof(secam), 0644);
+ module_param_string(ntsc, ntsc, sizeof(ntsc), 0644);
+ module_param_named(debug, cx18_debug, int, 0644);
+ module_param(mmio_ndelay, int, 0644);
+ module_param(retry_mmio, int, 0644);
+ module_param(cx18_pci_latency, int, 0644);
+ module_param(cx18_first_minor, int, 0644);
+ module_param(enc_ts_buffers, int, 0644);
+ module_param(enc_mpg_buffers, int, 0644);
+ module_param(enc_idx_buffers, int, 0644);
+ module_param(enc_yuv_buffers, int, 0644);
+ module_param(enc_vbi_buffers, int, 0644);
+ module_param(enc_pcm_buffers, int, 0644);
+ module_param(enc_ts_bufsize, int, 0644);
+ module_param(enc_mpg_bufsize, int, 0644);
+ module_param(enc_idx_bufsize, int, 0644);
+ module_param(enc_yuv_bufsize, int, 0644);
+ module_param(enc_pcm_bufsize, int, 0644);
+ module_param(enc_ts_bufs, int, 0644);
+ module_param(enc_mpg_bufs, int, 0644);
+ module_param(enc_idx_bufs, int, 0644);
+ module_param(enc_yuv_bufs, int, 0644);
+ module_param(enc_vbi_bufs, int, 0644);
+ module_param(enc_pcm_bufs, int, 0644);
+ MODULE_PARM_DESC(tuner, "Tuner type selection,\n"
+                       "\t\t\tsee tuner.h for values");
+ MODULE_PARM_DESC(radio,
+                "Enable or disable the radio. Use only if autodetection\n"
+                "\t\t\tfails. 0 = disable, 1 = enable");
+ MODULE_PARM_DESC(cardtype,
+                "Only use this option if your card is not detected properly.\n"
+                "\t\tSpecify card type:\n"
+                "\t\t\t 1 = Hauppauge HVR 1600 (ESMT memory)\n"
+                "\t\t\t 2 = Hauppauge HVR 1600 (Samsung memory)\n"
+                "\t\t\t 3 = Compro VideoMate H900\n"
+                "\t\t\t 4 = Yuan MPC718\n"
+                "\t\t\t 5 = Conexant Raptor PAL/SECAM\n"
+                "\t\t\t 6 = Toshiba Qosmio DVB-T/Analog\n"
+                "\t\t\t 7 = Leadtek WinFast PVR2100\n"
+                "\t\t\t 8 = Leadtek WinFast DVR3100 H\n"
+                "\t\t\t 9 = GoTView PCI DVD3 Hybrid\n"
+                "\t\t\t 10 = Hauppauge HVR 1600 (S5H1411)\n"
+                "\t\t\t 0 = Autodetect (default)\n"
+                "\t\t\t-1 = Ignore this card\n\t\t");
+ MODULE_PARM_DESC(pal, "Set PAL standard: B, G, H, D, K, I, M, N, Nc, 60");
+ MODULE_PARM_DESC(secam, "Set SECAM standard: B, G, H, D, K, L, LC");
+ MODULE_PARM_DESC(ntsc, "Set NTSC standard: M, J, K");
+ MODULE_PARM_DESC(debug,
+                "Debug level (bitmask). Default: 0\n"
+                "\t\t\t  1/0x0001: warning\n"
+                "\t\t\t  2/0x0002: info\n"
+                "\t\t\t  4/0x0004: mailbox\n"
+                "\t\t\t  8/0x0008: dma\n"
+                "\t\t\t 16/0x0010: ioctl\n"
+                "\t\t\t 32/0x0020: file\n"
+                "\t\t\t 64/0x0040: i2c\n"
+                "\t\t\t128/0x0080: irq\n"
+                "\t\t\t256/0x0100: high volume\n");
+ MODULE_PARM_DESC(cx18_pci_latency,
+                "Change the PCI latency to 64 if lower: 0 = No, 1 = Yes,\n"
+                "\t\t\tDefault: Yes");
+ MODULE_PARM_DESC(retry_mmio,
+                "(Deprecated) MMIO writes are now always checked and retried\n"
+                "\t\t\tEffectively: 1 [Yes]");
+ MODULE_PARM_DESC(mmio_ndelay,
+                "(Deprecated) MMIO accesses are now never purposely delayed\n"
+                "\t\t\tEffectively: 0 ns");
+ MODULE_PARM_DESC(enc_ts_buffers,
+                "Encoder TS buffer memory (MB). (enc_ts_bufs can override)\n"
+                "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_TS_BUFFERS));
+ MODULE_PARM_DESC(enc_ts_bufsize,
+                "Size of an encoder TS buffer (kB)\n"
+                "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_TS_BUFSIZE));
+ MODULE_PARM_DESC(enc_ts_bufs,
+                "Number of encoder TS buffers\n"
+                "\t\t\tDefault is computed from other enc_ts_* parameters");
+ MODULE_PARM_DESC(enc_mpg_buffers,
+                "Encoder MPG buffer memory (MB). (enc_mpg_bufs can override)\n"
+                "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_MPG_BUFFERS));
+ MODULE_PARM_DESC(enc_mpg_bufsize,
+                "Size of an encoder MPG buffer (kB)\n"
+                "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_MPG_BUFSIZE));
+ MODULE_PARM_DESC(enc_mpg_bufs,
+                "Number of encoder MPG buffers\n"
+                "\t\t\tDefault is computed from other enc_mpg_* parameters");
+ MODULE_PARM_DESC(enc_idx_buffers,
+                "(Deprecated) Encoder IDX buffer memory (MB)\n"
+                "\t\t\tIgnored, except 0 disables IDX buffer allocations\n"
+                "\t\t\tDefault: 1 [Enabled]");
+ MODULE_PARM_DESC(enc_idx_bufsize,
+                "Size of an encoder IDX buffer (kB)\n"
+                "\t\t\tAllowed values are multiples of 1.5 kB rounded up\n"
+                "\t\t\t(multiples of size required for 64 index entries)\n"
+                "\t\t\tDefault: 2");
+ MODULE_PARM_DESC(enc_idx_bufs,
+                "Number of encoder IDX buffers\n"
+                "\t\t\tDefault: " __stringify(CX18_MAX_FW_MDLS_PER_STREAM));
+ MODULE_PARM_DESC(enc_yuv_buffers,
+                "Encoder YUV buffer memory (MB). (enc_yuv_bufs can override)\n"
+                "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_YUV_BUFFERS));
+ MODULE_PARM_DESC(enc_yuv_bufsize,
+                "Size of an encoder YUV buffer (kB)\n"
+                "\t\t\tAllowed values are multiples of 33.75 kB rounded up\n"
+                "\t\t\t(multiples of size required for 32 screen lines)\n"
+                "\t\t\tDefault: 102");
+ MODULE_PARM_DESC(enc_yuv_bufs,
+                "Number of encoder YUV buffers\n"
+                "\t\t\tDefault is computed from other enc_yuv_* parameters");
+ MODULE_PARM_DESC(enc_vbi_buffers,
+                "Encoder VBI buffer memory (MB). (enc_vbi_bufs can override)\n"
+                "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_VBI_BUFFERS));
+ MODULE_PARM_DESC(enc_vbi_bufs,
+                "Number of encoder VBI buffers\n"
+                "\t\t\tDefault is computed from enc_vbi_buffers");
+ MODULE_PARM_DESC(enc_pcm_buffers,
+                "Encoder PCM buffer memory (MB). (enc_pcm_bufs can override)\n"
+                "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_PCM_BUFFERS));
+ MODULE_PARM_DESC(enc_pcm_bufsize,
+                "Size of an encoder PCM buffer (kB)\n"
+                "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_PCM_BUFSIZE));
+ MODULE_PARM_DESC(enc_pcm_bufs,
+                "Number of encoder PCM buffers\n"
+                "\t\t\tDefault is computed from other enc_pcm_* parameters");
+ MODULE_PARM_DESC(cx18_first_minor,
+                "Set device node number assigned to first card");
+ MODULE_AUTHOR("Hans Verkuil");
+ MODULE_DESCRIPTION("CX23418 driver");
+ MODULE_SUPPORTED_DEVICE("CX23418 MPEG2 encoder");
+ MODULE_LICENSE("GPL");
+ MODULE_VERSION(CX18_VERSION);
+ #if defined(CONFIG_MODULES) && defined(MODULE)
+ static void request_module_async(struct work_struct *work)
+ {
+       struct cx18 *dev = container_of(work, struct cx18, request_module_wk);
+       /* Make sure cx18-alsa module is loaded */
+       request_module("cx18-alsa");
+       /* Initialize cx18-alsa for this instance of the cx18 device */
+       if (cx18_ext_init != NULL)
+               cx18_ext_init(dev);
+ }
+ static void request_modules(struct cx18 *dev)
+ {
+       INIT_WORK(&dev->request_module_wk, request_module_async);
+       schedule_work(&dev->request_module_wk);
+ }
+ static void flush_request_modules(struct cx18 *dev)
+ {
++      flush_work(&dev->request_module_wk);
+ }
+ #else
+ #define request_modules(dev)
+ #define flush_request_modules(dev)
+ #endif /* CONFIG_MODULES */
+ /* Generic utility functions */
+ int cx18_msleep_timeout(unsigned int msecs, int intr)
+ {
+       long int timeout = msecs_to_jiffies(msecs);
+       int sig;
+       do {
+               set_current_state(intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
+               timeout = schedule_timeout(timeout);
+               sig = intr ? signal_pending(current) : 0;
+       } while (!sig && timeout);
+       return sig;
+ }
+ /* Release ioremapped memory */
+ static void cx18_iounmap(struct cx18 *cx)
+ {
+       if (cx == NULL)
+               return;
+       /* Release io memory */
+       if (cx->enc_mem != NULL) {
+               CX18_DEBUG_INFO("releasing enc_mem\n");
+               iounmap(cx->enc_mem);
+               cx->enc_mem = NULL;
+       }
+ }
+ static void cx18_eeprom_dump(struct cx18 *cx, unsigned char *eedata, int len)
+ {
+       int i;
+       CX18_INFO("eeprom dump:\n");
+       for (i = 0; i < len; i++) {
+               if (0 == (i % 16))
+                       CX18_INFO("eeprom %02x:", i);
+               printk(KERN_CONT " %02x", eedata[i]);
+               if (15 == (i % 16))
+                       printk(KERN_CONT "\n");
+       }
+ }
+ /* Hauppauge card? get values from tveeprom */
+ void cx18_read_eeprom(struct cx18 *cx, struct tveeprom *tv)
+ {
+       struct i2c_client c;
+       u8 eedata[256];
+       memset(&c, 0, sizeof(c));
+       strlcpy(c.name, "cx18 tveeprom tmp", sizeof(c.name));
+       c.adapter = &cx->i2c_adap[0];
+       c.addr = 0xA0 >> 1;
+       memset(tv, 0, sizeof(*tv));
+       if (tveeprom_read(&c, eedata, sizeof(eedata)))
+               return;
+       switch (cx->card->type) {
+       case CX18_CARD_HVR_1600_ESMT:
+       case CX18_CARD_HVR_1600_SAMSUNG:
+       case CX18_CARD_HVR_1600_S5H1411:
+               tveeprom_hauppauge_analog(&c, tv, eedata);
+               break;
+       case CX18_CARD_YUAN_MPC718:
+       case CX18_CARD_GOTVIEW_PCI_DVD3:
+               tv->model = 0x718;
+               cx18_eeprom_dump(cx, eedata, sizeof(eedata));
+               CX18_INFO("eeprom PCI ID: %02x%02x:%02x%02x\n",
+                         eedata[2], eedata[1], eedata[4], eedata[3]);
+               break;
+       default:
+               tv->model = 0xffffffff;
+               cx18_eeprom_dump(cx, eedata, sizeof(eedata));
+               break;
+       }
+ }
+ static void cx18_process_eeprom(struct cx18 *cx)
+ {
+       struct tveeprom tv;
+       cx18_read_eeprom(cx, &tv);
+       /* Many thanks to Steven Toth from Hauppauge for providing the
+          model numbers */
+       /* Note: the Samsung memory models cannot be reliably determined
+          from the model number. Use the cardtype module option if you
+          have one of these preproduction models. */
+       switch (tv.model) {
+       case 74301: /* Retail models */
+       case 74321:
+       case 74351: /* OEM models */
+       case 74361:
+               /* Digital side is s5h1411/tda18271 */
+               cx->card = cx18_get_card(CX18_CARD_HVR_1600_S5H1411);
+               break;
+       case 74021: /* Retail models */
+       case 74031:
+       case 74041:
+       case 74141:
+       case 74541: /* OEM models */
+       case 74551:
+       case 74591:
+       case 74651:
+       case 74691:
+       case 74751:
+       case 74891:
+               /* Digital side is s5h1409/mxl5005s */
+               cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT);
+               break;
+       case 0x718:
+               return;
+       case 0xffffffff:
+               CX18_INFO("Unknown EEPROM encoding\n");
+               return;
+       case 0:
+               CX18_ERR("Invalid EEPROM\n");
+               return;
+       default:
+               CX18_ERR("Unknown model %d, defaulting to original HVR-1600 "
+                        "(cardtype=1)\n", tv.model);
+               cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT);
+               break;
+       }
+       cx->v4l2_cap = cx->card->v4l2_capabilities;
+       cx->card_name = cx->card->name;
+       cx->card_i2c = cx->card->i2c;
+       CX18_INFO("Autodetected %s\n", cx->card_name);
+       if (tv.tuner_type == TUNER_ABSENT)
+               CX18_ERR("tveeprom cannot autodetect tuner!\n");
+       if (cx->options.tuner == -1)
+               cx->options.tuner = tv.tuner_type;
+       if (cx->options.radio == -1)
+               cx->options.radio = (tv.has_radio != 0);
+       if (cx->std != 0)
+               /* user specified tuner standard */
+               return;
+       /* autodetect tuner standard */
+ #define TVEEPROM_TUNER_FORMAT_ALL (V4L2_STD_B  | V4L2_STD_GH | \
+                                  V4L2_STD_MN | \
+                                  V4L2_STD_PAL_I | \
+                                  V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC | \
+                                  V4L2_STD_DK)
+       if ((tv.tuner_formats & TVEEPROM_TUNER_FORMAT_ALL)
+                                       == TVEEPROM_TUNER_FORMAT_ALL) {
+               CX18_DEBUG_INFO("Worldwide tuner detected\n");
+               cx->std = V4L2_STD_ALL;
+       } else if (tv.tuner_formats & V4L2_STD_PAL) {
+               CX18_DEBUG_INFO("PAL tuner detected\n");
+               cx->std |= V4L2_STD_PAL_BG | V4L2_STD_PAL_H;
+       } else if (tv.tuner_formats & V4L2_STD_NTSC) {
+               CX18_DEBUG_INFO("NTSC tuner detected\n");
+               cx->std |= V4L2_STD_NTSC_M;
+       } else if (tv.tuner_formats & V4L2_STD_SECAM) {
+               CX18_DEBUG_INFO("SECAM tuner detected\n");
+               cx->std |= V4L2_STD_SECAM_L;
+       } else {
+               CX18_INFO("No tuner detected, default to NTSC-M\n");
+               cx->std |= V4L2_STD_NTSC_M;
+       }
+ }
+ static v4l2_std_id cx18_parse_std(struct cx18 *cx)
+ {
+       switch (pal[0]) {
+       case '6':
+               return V4L2_STD_PAL_60;
+       case 'b':
+       case 'B':
+       case 'g':
+       case 'G':
+               return V4L2_STD_PAL_BG;
+       case 'h':
+       case 'H':
+               return V4L2_STD_PAL_H;
+       case 'n':
+       case 'N':
+               if (pal[1] == 'c' || pal[1] == 'C')
+                       return V4L2_STD_PAL_Nc;
+               return V4L2_STD_PAL_N;
+       case 'i':
+       case 'I':
+               return V4L2_STD_PAL_I;
+       case 'd':
+       case 'D':
+       case 'k':
+       case 'K':
+               return V4L2_STD_PAL_DK;
+       case 'M':
+       case 'm':
+               return V4L2_STD_PAL_M;
+       case '-':
+               break;
+       default:
+               CX18_WARN("pal= argument not recognised\n");
+               return 0;
+       }
+       switch (secam[0]) {
+       case 'b':
+       case 'B':
+       case 'g':
+       case 'G':
+       case 'h':
+       case 'H':
+               return V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H;
+       case 'd':
+       case 'D':
+       case 'k':
+       case 'K':
+               return V4L2_STD_SECAM_DK;
+       case 'l':
+       case 'L':
+               if (secam[1] == 'C' || secam[1] == 'c')
+                       return V4L2_STD_SECAM_LC;
+               return V4L2_STD_SECAM_L;
+       case '-':
+               break;
+       default:
+               CX18_WARN("secam= argument not recognised\n");
+               return 0;
+       }
+       switch (ntsc[0]) {
+       case 'm':
+       case 'M':
+               return V4L2_STD_NTSC_M;
+       case 'j':
+       case 'J':
+               return V4L2_STD_NTSC_M_JP;
+       case 'k':
+       case 'K':
+               return V4L2_STD_NTSC_M_KR;
+       case '-':
+               break;
+       default:
+               CX18_WARN("ntsc= argument not recognised\n");
+               return 0;
+       }
+       /* no match found */
+       return 0;
+ }
+ static void cx18_process_options(struct cx18 *cx)
+ {
+       int i, j;
+       cx->options.megabytes[CX18_ENC_STREAM_TYPE_TS] = enc_ts_buffers;
+       cx->options.megabytes[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_buffers;
+       cx->options.megabytes[CX18_ENC_STREAM_TYPE_IDX] = enc_idx_buffers;
+       cx->options.megabytes[CX18_ENC_STREAM_TYPE_YUV] = enc_yuv_buffers;
+       cx->options.megabytes[CX18_ENC_STREAM_TYPE_VBI] = enc_vbi_buffers;
+       cx->options.megabytes[CX18_ENC_STREAM_TYPE_PCM] = enc_pcm_buffers;
+       cx->options.megabytes[CX18_ENC_STREAM_TYPE_RAD] = 0; /* control only */
+       cx->stream_buffers[CX18_ENC_STREAM_TYPE_TS] = enc_ts_bufs;
+       cx->stream_buffers[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_bufs;
+       cx->stream_buffers[CX18_ENC_STREAM_TYPE_IDX] = enc_idx_bufs;
+       cx->stream_buffers[CX18_ENC_STREAM_TYPE_YUV] = enc_yuv_bufs;
+       cx->stream_buffers[CX18_ENC_STREAM_TYPE_VBI] = enc_vbi_bufs;
+       cx->stream_buffers[CX18_ENC_STREAM_TYPE_PCM] = enc_pcm_bufs;
+       cx->stream_buffers[CX18_ENC_STREAM_TYPE_RAD] = 0; /* control, no data */
+       cx->stream_buf_size[CX18_ENC_STREAM_TYPE_TS] = enc_ts_bufsize;
+       cx->stream_buf_size[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_bufsize;
+       cx->stream_buf_size[CX18_ENC_STREAM_TYPE_IDX] = enc_idx_bufsize;
+       cx->stream_buf_size[CX18_ENC_STREAM_TYPE_YUV] = enc_yuv_bufsize;
+       cx->stream_buf_size[CX18_ENC_STREAM_TYPE_VBI] = vbi_active_samples * 36;
+       cx->stream_buf_size[CX18_ENC_STREAM_TYPE_PCM] = enc_pcm_bufsize;
+       cx->stream_buf_size[CX18_ENC_STREAM_TYPE_RAD] = 0; /* control no data */
+       /* Ensure stream_buffers & stream_buf_size are valid */
+       for (i = 0; i < CX18_MAX_STREAMS; i++) {
+               if (cx->stream_buffers[i] == 0 ||     /* User said 0 buffers */
+                   cx->options.megabytes[i] <= 0 ||  /* User said 0 MB total */
+                   cx->stream_buf_size[i] <= 0) {    /* User said buf size 0 */
+                       cx->options.megabytes[i] = 0;
+                       cx->stream_buffers[i] = 0;
+                       cx->stream_buf_size[i] = 0;
+                       continue;
+               }
+               /*
+                * YUV is a special case where the stream_buf_size needs to be
+                * an integral multiple of 33.75 kB (storage for 32 screens
+                * lines to maintain alignment in case of lost buffers).
+                *
+                * IDX is a special case where the stream_buf_size should be
+                * an integral multiple of 1.5 kB (storage for 64 index entries
+                * to maintain alignment in case of lost buffers).
+                *
+                */
+               if (i == CX18_ENC_STREAM_TYPE_YUV) {
+                       cx->stream_buf_size[i] *= 1024;
+                       cx->stream_buf_size[i] -=
+                          (cx->stream_buf_size[i] % CX18_UNIT_ENC_YUV_BUFSIZE);
+                       if (cx->stream_buf_size[i] < CX18_UNIT_ENC_YUV_BUFSIZE)
+                               cx->stream_buf_size[i] =
+                                               CX18_UNIT_ENC_YUV_BUFSIZE;
+               } else if (i == CX18_ENC_STREAM_TYPE_IDX) {
+                       cx->stream_buf_size[i] *= 1024;
+                       cx->stream_buf_size[i] -=
+                          (cx->stream_buf_size[i] % CX18_UNIT_ENC_IDX_BUFSIZE);
+                       if (cx->stream_buf_size[i] < CX18_UNIT_ENC_IDX_BUFSIZE)
+                               cx->stream_buf_size[i] =
+                                               CX18_UNIT_ENC_IDX_BUFSIZE;
+               }
+               /*
+                * YUV and IDX are special cases where the stream_buf_size is
+                * now in bytes.
+                * VBI is a special case where the stream_buf_size is fixed
+                * and already in bytes
+                */
+               if (i == CX18_ENC_STREAM_TYPE_VBI ||
+                   i == CX18_ENC_STREAM_TYPE_YUV ||
+                   i == CX18_ENC_STREAM_TYPE_IDX) {
+                       if (cx->stream_buffers[i] < 0) {
+                               cx->stream_buffers[i] =
+                                       cx->options.megabytes[i] * 1024 * 1024
+                                       / cx->stream_buf_size[i];
+                       } else {
+                               /* N.B. This might round down to 0 */
+                               cx->options.megabytes[i] =
+                                       cx->stream_buffers[i]
+                                       * cx->stream_buf_size[i]/(1024 * 1024);
+                       }
+               } else {
+                       /* All other streams have stream_buf_size in kB here */
+                       if (cx->stream_buffers[i] < 0) {
+                               cx->stream_buffers[i] =
+                                               cx->options.megabytes[i] * 1024
+                                               / cx->stream_buf_size[i];
+                       } else {
+                               /* N.B. This might round down to 0 */
+                               cx->options.megabytes[i] =
+                                               cx->stream_buffers[i]
+                                               * cx->stream_buf_size[i] / 1024;
+                       }
+                       /* convert from kB to bytes */
+                       cx->stream_buf_size[i] *= 1024;
+               }
+               CX18_DEBUG_INFO("Stream type %d options: %d MB, %d buffers, "
+                               "%d bytes\n", i, cx->options.megabytes[i],
+                               cx->stream_buffers[i], cx->stream_buf_size[i]);
+       }
+       cx->options.cardtype = cardtype[cx->instance];
+       cx->options.tuner = tuner[cx->instance];
+       cx->options.radio = radio[cx->instance];
+       cx->std = cx18_parse_std(cx);
+       if (cx->options.cardtype == -1) {
+               CX18_INFO("Ignore card\n");
+               return;
+       }
+       cx->card = cx18_get_card(cx->options.cardtype - 1);
+       if (cx->card)
+               CX18_INFO("User specified %s card\n", cx->card->name);
+       else if (cx->options.cardtype != 0)
+               CX18_ERR("Unknown user specified type, trying to autodetect card\n");
+       if (cx->card == NULL) {
+               if (cx->pci_dev->subsystem_vendor == CX18_PCI_ID_HAUPPAUGE) {
+                       cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT);
+                       CX18_INFO("Autodetected Hauppauge card\n");
+               }
+       }
+       if (cx->card == NULL) {
+               for (i = 0; (cx->card = cx18_get_card(i)); i++) {
+                       if (cx->card->pci_list == NULL)
+                               continue;
+                       for (j = 0; cx->card->pci_list[j].device; j++) {
+                               if (cx->pci_dev->device !=
+                                   cx->card->pci_list[j].device)
+                                       continue;
+                               if (cx->pci_dev->subsystem_vendor !=
+                                   cx->card->pci_list[j].subsystem_vendor)
+                                       continue;
+                               if (cx->pci_dev->subsystem_device !=
+                                   cx->card->pci_list[j].subsystem_device)
+                                       continue;
+                               CX18_INFO("Autodetected %s card\n", cx->card->name);
+                               goto done;
+                       }
+               }
+       }
+ done:
+       if (cx->card == NULL) {
+               cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT);
+               CX18_ERR("Unknown card: vendor/device: [%04x:%04x]\n",
+                        cx->pci_dev->vendor, cx->pci_dev->device);
+               CX18_ERR("              subsystem vendor/device: [%04x:%04x]\n",
+                        cx->pci_dev->subsystem_vendor,
+                        cx->pci_dev->subsystem_device);
+               CX18_ERR("Defaulting to %s card\n", cx->card->name);
+               CX18_ERR("Please mail the vendor/device and subsystem vendor/device IDs and what kind of\n");
+               CX18_ERR("card you have to the ivtv-devel mailinglist (www.ivtvdriver.org)\n");
+               CX18_ERR("Prefix your subject line with [UNKNOWN CX18 CARD].\n");
+       }
+       cx->v4l2_cap = cx->card->v4l2_capabilities;
+       cx->card_name = cx->card->name;
+       cx->card_i2c = cx->card->i2c;
+ }
+ static int __devinit cx18_create_in_workq(struct cx18 *cx)
+ {
+       snprintf(cx->in_workq_name, sizeof(cx->in_workq_name), "%s-in",
+                cx->v4l2_dev.name);
+       cx->in_work_queue = alloc_ordered_workqueue(cx->in_workq_name, 0);
+       if (cx->in_work_queue == NULL) {
+               CX18_ERR("Unable to create incoming mailbox handler thread\n");
+               return -ENOMEM;
+       }
+       return 0;
+ }
+ static void __devinit cx18_init_in_work_orders(struct cx18 *cx)
+ {
+       int i;
+       for (i = 0; i < CX18_MAX_IN_WORK_ORDERS; i++) {
+               cx->in_work_order[i].cx = cx;
+               cx->in_work_order[i].str = cx->epu_debug_str;
+               INIT_WORK(&cx->in_work_order[i].work, cx18_in_work_handler);
+       }
+ }
+ /* Precondition: the cx18 structure has been memset to 0. Only
+    the dev and instance fields have been filled in.
+    No assumptions on the card type may be made here (see cx18_init_struct2
+    for that).
+  */
+ static int __devinit cx18_init_struct1(struct cx18 *cx)
+ {
+       int ret;
+       cx->base_addr = pci_resource_start(cx->pci_dev, 0);
+       mutex_init(&cx->serialize_lock);
+       mutex_init(&cx->gpio_lock);
+       mutex_init(&cx->epu2apu_mb_lock);
+       mutex_init(&cx->epu2cpu_mb_lock);
+       ret = cx18_create_in_workq(cx);
+       if (ret)
+               return ret;
+       cx18_init_in_work_orders(cx);
+       /* start counting open_id at 1 */
+       cx->open_id = 1;
+       /* Initial settings */
+       cx->cxhdl.port = CX2341X_PORT_MEMORY;
+       cx->cxhdl.capabilities = CX2341X_CAP_HAS_TS | CX2341X_CAP_HAS_SLICED_VBI;
+       cx->cxhdl.ops = &cx18_cxhdl_ops;
+       cx->cxhdl.func = cx18_api_func;
+       cx->cxhdl.priv = &cx->streams[CX18_ENC_STREAM_TYPE_MPG];
+       ret = cx2341x_handler_init(&cx->cxhdl, 50);
+       if (ret)
+               return ret;
+       cx->v4l2_dev.ctrl_handler = &cx->cxhdl.hdl;
+       cx->temporal_strength = cx->cxhdl.video_temporal_filter->cur.val;
+       cx->spatial_strength = cx->cxhdl.video_spatial_filter->cur.val;
+       cx->filter_mode = cx->cxhdl.video_spatial_filter_mode->cur.val |
+               (cx->cxhdl.video_temporal_filter_mode->cur.val << 1) |
+               (cx->cxhdl.video_median_filter_type->cur.val << 2);
+       init_waitqueue_head(&cx->cap_w);
+       init_waitqueue_head(&cx->mb_apu_waitq);
+       init_waitqueue_head(&cx->mb_cpu_waitq);
+       init_waitqueue_head(&cx->dma_waitq);
+       /* VBI */
+       cx->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE;
+       cx->vbi.sliced_in = &cx->vbi.in.fmt.sliced;
+       /* IVTV style VBI insertion into MPEG streams */
+       INIT_LIST_HEAD(&cx->vbi.sliced_mpeg_buf.list);
+       INIT_LIST_HEAD(&cx->vbi.sliced_mpeg_mdl.list);
+       INIT_LIST_HEAD(&cx->vbi.sliced_mpeg_mdl.buf_list);
+       list_add(&cx->vbi.sliced_mpeg_buf.list,
+                &cx->vbi.sliced_mpeg_mdl.buf_list);
+       return 0;
+ }
+ /* Second initialization part. Here the card type has been
+    autodetected. */
+ static void __devinit cx18_init_struct2(struct cx18 *cx)
+ {
+       int i;
+       for (i = 0; i < CX18_CARD_MAX_VIDEO_INPUTS; i++)
+               if (cx->card->video_inputs[i].video_type == 0)
+                       break;
+       cx->nof_inputs = i;
+       for (i = 0; i < CX18_CARD_MAX_AUDIO_INPUTS; i++)
+               if (cx->card->audio_inputs[i].audio_type == 0)
+                       break;
+       cx->nof_audio_inputs = i;
+       /* Find tuner input */
+       for (i = 0; i < cx->nof_inputs; i++) {
+               if (cx->card->video_inputs[i].video_type ==
+                               CX18_CARD_INPUT_VID_TUNER)
+                       break;
+       }
+       if (i == cx->nof_inputs)
+               i = 0;
+       cx->active_input = i;
+       cx->audio_input = cx->card->video_inputs[i].audio_index;
+ }
+ static int cx18_setup_pci(struct cx18 *cx, struct pci_dev *pci_dev,
+                         const struct pci_device_id *pci_id)
+ {
+       u16 cmd;
+       unsigned char pci_latency;
+       CX18_DEBUG_INFO("Enabling pci device\n");
+       if (pci_enable_device(pci_dev)) {
+               CX18_ERR("Can't enable device %d!\n", cx->instance);
+               return -EIO;
+       }
+       if (pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32))) {
+               CX18_ERR("No suitable DMA available, card %d\n", cx->instance);
+               return -EIO;
+       }
+       if (!request_mem_region(cx->base_addr, CX18_MEM_SIZE, "cx18 encoder")) {
+               CX18_ERR("Cannot request encoder memory region, card %d\n",
+                        cx->instance);
+               return -EIO;
+       }
+       /* Enable bus mastering and memory mapped IO for the CX23418 */
+       pci_read_config_word(pci_dev, PCI_COMMAND, &cmd);
+       cmd |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER;
+       pci_write_config_word(pci_dev, PCI_COMMAND, cmd);
+       cx->card_rev = pci_dev->revision;
+       pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &pci_latency);
+       if (pci_latency < 64 && cx18_pci_latency) {
+               CX18_INFO("Unreasonably low latency timer, "
+                              "setting to 64 (was %d)\n", pci_latency);
+               pci_write_config_byte(pci_dev, PCI_LATENCY_TIMER, 64);
+               pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &pci_latency);
+       }
+       CX18_DEBUG_INFO("cx%d (rev %d) at %02x:%02x.%x, "
+                  "irq: %d, latency: %d, memory: 0x%llx\n",
+                  cx->pci_dev->device, cx->card_rev, pci_dev->bus->number,
+                  PCI_SLOT(pci_dev->devfn), PCI_FUNC(pci_dev->devfn),
+                  cx->pci_dev->irq, pci_latency, (u64)cx->base_addr);
+       return 0;
+ }
+ static void cx18_init_subdevs(struct cx18 *cx)
+ {
+       u32 hw = cx->card->hw_all;
+       u32 device;
+       int i;
+       for (i = 0, device = 1; i < 32; i++, device <<= 1) {
+               if (!(device & hw))
+                       continue;
+               switch (device) {
+               case CX18_HW_DVB:
+               case CX18_HW_TVEEPROM:
+                       /* These subordinate devices do not use probing */
+                       cx->hw_flags |= device;
+                       break;
+               case CX18_HW_418_AV:
+                       /* The A/V decoder gets probed earlier to set PLLs */
+                       /* Just note that the card uses it (i.e. has analog) */
+                       cx->hw_flags |= device;
+                       break;
+               case CX18_HW_GPIO_RESET_CTRL:
+                       /*
+                        * The Reset Controller gets probed and added to
+                        * hw_flags earlier for i2c adapter/bus initialization
+                        */
+                       break;
+               case CX18_HW_GPIO_MUX:
+                       if (cx18_gpio_register(cx, device) == 0)
+                               cx->hw_flags |= device;
+                       break;
+               default:
+                       if (cx18_i2c_register(cx, i) == 0)
+                               cx->hw_flags |= device;
+                       break;
+               }
+       }
+       if (cx->hw_flags & CX18_HW_418_AV)
+               cx->sd_av = cx18_find_hw(cx, CX18_HW_418_AV);
+       if (cx->card->hw_muxer != 0)
+               cx->sd_extmux = cx18_find_hw(cx, cx->card->hw_muxer);
+ }
+ static int __devinit cx18_probe(struct pci_dev *pci_dev,
+                               const struct pci_device_id *pci_id)
+ {
+       int retval = 0;
+       int i;
+       u32 devtype;
+       struct cx18 *cx;
+       /* FIXME - module parameter arrays constrain max instances */
+       i = atomic_inc_return(&cx18_instance) - 1;
+       if (i >= CX18_MAX_CARDS) {
+               printk(KERN_ERR "cx18: cannot manage card %d, driver has a "
+                      "limit of 0 - %d\n", i, CX18_MAX_CARDS - 1);
+               return -ENOMEM;
+       }
+       cx = kzalloc(sizeof(struct cx18), GFP_ATOMIC);
+       if (cx == NULL) {
+               printk(KERN_ERR "cx18: cannot manage card %d, out of memory\n",
+                      i);
+               return -ENOMEM;
+       }
+       cx->pci_dev = pci_dev;
+       cx->instance = i;
+       retval = v4l2_device_register(&pci_dev->dev, &cx->v4l2_dev);
+       if (retval) {
+               printk(KERN_ERR "cx18: v4l2_device_register of card %d failed"
+                      "\n", cx->instance);
+               kfree(cx);
+               return retval;
+       }
+       snprintf(cx->v4l2_dev.name, sizeof(cx->v4l2_dev.name), "cx18-%d",
+                cx->instance);
+       CX18_INFO("Initializing card %d\n", cx->instance);
+       cx18_process_options(cx);
+       if (cx->options.cardtype == -1) {
+               retval = -ENODEV;
+               goto err;
+       }
+       retval = cx18_init_struct1(cx);
+       if (retval)
+               goto err;
+       CX18_DEBUG_INFO("base addr: 0x%llx\n", (u64)cx->base_addr);
+       /* PCI Device Setup */
+       retval = cx18_setup_pci(cx, pci_dev, pci_id);
+       if (retval != 0)
+               goto free_workqueues;
+       /* map io memory */
+       CX18_DEBUG_INFO("attempting ioremap at 0x%llx len 0x%08x\n",
+                  (u64)cx->base_addr + CX18_MEM_OFFSET, CX18_MEM_SIZE);
+       cx->enc_mem = ioremap_nocache(cx->base_addr + CX18_MEM_OFFSET,
+                                      CX18_MEM_SIZE);
+       if (!cx->enc_mem) {
+               CX18_ERR("ioremap failed. Can't get a window into CX23418 "
+                        "memory and register space\n");
+               CX18_ERR("Each capture card with a CX23418 needs 64 MB of "
+                        "vmalloc address space for the window\n");
+               CX18_ERR("Check the output of 'grep Vmalloc /proc/meminfo'\n");
+               CX18_ERR("Use the vmalloc= kernel command line option to set "
+                        "VmallocTotal to a larger value\n");
+               retval = -ENOMEM;
+               goto free_mem;
+       }
+       cx->reg_mem = cx->enc_mem + CX18_REG_OFFSET;
+       devtype = cx18_read_reg(cx, 0xC72028);
+       switch (devtype & 0xff000000) {
+       case 0xff000000:
+               CX18_INFO("cx23418 revision %08x (A)\n", devtype);
+               break;
+       case 0x01000000:
+               CX18_INFO("cx23418 revision %08x (B)\n", devtype);
+               break;
+       default:
+               CX18_INFO("cx23418 revision %08x (Unknown)\n", devtype);
+               break;
+       }
+       cx18_init_power(cx, 1);
+       cx18_init_memory(cx);
+       cx->scb = (struct cx18_scb __iomem *)(cx->enc_mem + SCB_OFFSET);
+       cx18_init_scb(cx);
+       cx18_gpio_init(cx);
+       /* Initialize integrated A/V decoder early to set PLLs, just in case */
+       retval = cx18_av_probe(cx);
+       if (retval) {
+               CX18_ERR("Could not register A/V decoder subdevice\n");
+               goto free_map;
+       }
+       /* Initialize GPIO Reset Controller to do chip resets during i2c init */
+       if (cx->card->hw_all & CX18_HW_GPIO_RESET_CTRL) {
+               if (cx18_gpio_register(cx, CX18_HW_GPIO_RESET_CTRL) != 0)
+                       CX18_WARN("Could not register GPIO reset controller"
+                                 "subdevice; proceeding anyway.\n");
+               else
+                       cx->hw_flags |= CX18_HW_GPIO_RESET_CTRL;
+       }
+       /* active i2c  */
+       CX18_DEBUG_INFO("activating i2c...\n");
+       retval = init_cx18_i2c(cx);
+       if (retval) {
+               CX18_ERR("Could not initialize i2c\n");
+               goto free_map;
+       }
+       if (cx->card->hw_all & CX18_HW_TVEEPROM) {
+               /* Based on the model number the cardtype may be changed.
+                  The PCI IDs are not always reliable. */
+               const struct cx18_card *orig_card = cx->card;
+               cx18_process_eeprom(cx);
+               if (cx->card != orig_card) {
+                       /* Changed the cardtype; re-reset the I2C chips */
+                       cx18_gpio_init(cx);
+                       cx18_call_hw(cx, CX18_HW_GPIO_RESET_CTRL,
+                                       core, reset, (u32) CX18_GPIO_RESET_I2C);
+               }
+       }
+       if (cx->card->comment)
+               CX18_INFO("%s", cx->card->comment);
+       if (cx->card->v4l2_capabilities == 0) {
+               retval = -ENODEV;
+               goto free_i2c;
+       }
+       cx18_init_memory(cx);
+       cx18_init_scb(cx);
+       /* Register IRQ */
+       retval = request_irq(cx->pci_dev->irq, cx18_irq_handler,
+                            IRQF_SHARED | IRQF_DISABLED,
+                            cx->v4l2_dev.name, (void *)cx);
+       if (retval) {
+               CX18_ERR("Failed to register irq %d\n", retval);
+               goto free_i2c;
+       }
+       if (cx->std == 0)
+               cx->std = V4L2_STD_NTSC_M;
+       if (cx->options.tuner == -1) {
+               for (i = 0; i < CX18_CARD_MAX_TUNERS; i++) {
+                       if ((cx->std & cx->card->tuners[i].std) == 0)
+                               continue;
+                       cx->options.tuner = cx->card->tuners[i].tuner;
+                       break;
+               }
+       }
+       /* if no tuner was found, then pick the first tuner in the card list */
+       if (cx->options.tuner == -1 && cx->card->tuners[0].std) {
+               cx->std = cx->card->tuners[0].std;
+               if (cx->std & V4L2_STD_PAL)
+                       cx->std = V4L2_STD_PAL_BG | V4L2_STD_PAL_H;
+               else if (cx->std & V4L2_STD_NTSC)
+                       cx->std = V4L2_STD_NTSC_M;
+               else if (cx->std & V4L2_STD_SECAM)
+                       cx->std = V4L2_STD_SECAM_L;
+               cx->options.tuner = cx->card->tuners[0].tuner;
+       }
+       if (cx->options.radio == -1)
+               cx->options.radio = (cx->card->radio_input.audio_type != 0);
+       /* The card is now fully identified, continue with card-specific
+          initialization. */
+       cx18_init_struct2(cx);
+       cx18_init_subdevs(cx);
+       if (cx->std & V4L2_STD_525_60)
+               cx->is_60hz = 1;
+       else
+               cx->is_50hz = 1;
+       cx2341x_handler_set_50hz(&cx->cxhdl, !cx->is_60hz);
+       if (cx->options.radio > 0)
+               cx->v4l2_cap |= V4L2_CAP_RADIO;
+       if (cx->options.tuner > -1) {
+               struct tuner_setup setup;
+               setup.addr = ADDR_UNSET;
+               setup.type = cx->options.tuner;
+               setup.mode_mask = T_ANALOG_TV;  /* matches TV tuners */
+               if (cx->options.radio > 0)
+                       setup.mode_mask |= T_RADIO;
+               setup.tuner_callback = (setup.type == TUNER_XC2028) ?
+                       cx18_reset_tuner_gpio : NULL;
+               cx18_call_all(cx, tuner, s_type_addr, &setup);
+               if (setup.type == TUNER_XC2028) {
+                       static struct xc2028_ctrl ctrl = {
+                               .fname = XC2028_DEFAULT_FIRMWARE,
+                               .max_len = 64,
+                       };
+                       struct v4l2_priv_tun_config cfg = {
+                               .tuner = cx->options.tuner,
+                               .priv = &ctrl,
+                       };
+                       cx18_call_all(cx, tuner, s_config, &cfg);
+               }
+       }
+       /* The tuner is fixed to the standard. The other inputs (e.g. S-Video)
+          are not. */
+       cx->tuner_std = cx->std;
+       if (cx->std == V4L2_STD_ALL)
+               cx->std = V4L2_STD_NTSC_M;
+       retval = cx18_streams_setup(cx);
+       if (retval) {
+               CX18_ERR("Error %d setting up streams\n", retval);
+               goto free_irq;
+       }
+       retval = cx18_streams_register(cx);
+       if (retval) {
+               CX18_ERR("Error %d registering devices\n", retval);
+               goto free_streams;
+       }
+       CX18_INFO("Initialized card: %s\n", cx->card_name);
+       /* Load cx18 submodules (cx18-alsa) */
+       request_modules(cx);
+       return 0;
+ free_streams:
+       cx18_streams_cleanup(cx, 1);
+ free_irq:
+       free_irq(cx->pci_dev->irq, (void *)cx);
+ free_i2c:
+       exit_cx18_i2c(cx);
+ free_map:
+       cx18_iounmap(cx);
+ free_mem:
+       release_mem_region(cx->base_addr, CX18_MEM_SIZE);
+ free_workqueues:
+       destroy_workqueue(cx->in_work_queue);
+ err:
+       if (retval == 0)
+               retval = -ENODEV;
+       CX18_ERR("Error %d on initialization\n", retval);
+       v4l2_device_unregister(&cx->v4l2_dev);
+       kfree(cx);
+       return retval;
+ }
+ int cx18_init_on_first_open(struct cx18 *cx)
+ {
+       int video_input;
+       int fw_retry_count = 3;
+       struct v4l2_frequency vf;
+       struct cx18_open_id fh;
+       v4l2_std_id std;
+       fh.cx = cx;
+       if (test_bit(CX18_F_I_FAILED, &cx->i_flags))
+               return -ENXIO;
+       if (test_and_set_bit(CX18_F_I_INITED, &cx->i_flags))
+               return 0;
+       while (--fw_retry_count > 0) {
+               /* load firmware */
+               if (cx18_firmware_init(cx) == 0)
+                       break;
+               if (fw_retry_count > 1)
+                       CX18_WARN("Retry loading firmware\n");
+       }
+       if (fw_retry_count == 0) {
+               set_bit(CX18_F_I_FAILED, &cx->i_flags);
+               return -ENXIO;
+       }
+       set_bit(CX18_F_I_LOADED_FW, &cx->i_flags);
+       /*
+        * Init the firmware twice to work around a silicon bug
+        * with the digital TS.
+        *
+        * The second firmware load requires us to normalize the APU state,
+        * or the audio for the first analog capture will be badly incorrect.
+        *
+        * I can't seem to call APU_RESETAI and have it succeed without the
+        * APU capturing audio, so we start and stop it here to do the reset
+        */
+       /* MPEG Encoding, 224 kbps, MPEG Layer II, 48 ksps */
+       cx18_vapi(cx, CX18_APU_START, 2, CX18_APU_ENCODING_METHOD_MPEG|0xb9, 0);
+       cx18_vapi(cx, CX18_APU_RESETAI, 0);
+       cx18_vapi(cx, CX18_APU_STOP, 1, CX18_APU_ENCODING_METHOD_MPEG);
+       fw_retry_count = 3;
+       while (--fw_retry_count > 0) {
+               /* load firmware */
+               if (cx18_firmware_init(cx) == 0)
+                       break;
+               if (fw_retry_count > 1)
+                       CX18_WARN("Retry loading firmware\n");
+       }
+       if (fw_retry_count == 0) {
+               set_bit(CX18_F_I_FAILED, &cx->i_flags);
+               return -ENXIO;
+       }
+       /*
+        * The second firmware load requires us to normalize the APU state,
+        * or the audio for the first analog capture will be badly incorrect.
+        *
+        * I can't seem to call APU_RESETAI and have it succeed without the
+        * APU capturing audio, so we start and stop it here to do the reset
+        */
+       /* MPEG Encoding, 224 kbps, MPEG Layer II, 48 ksps */
+       cx18_vapi(cx, CX18_APU_START, 2, CX18_APU_ENCODING_METHOD_MPEG|0xb9, 0);
+       cx18_vapi(cx, CX18_APU_RESETAI, 0);
+       cx18_vapi(cx, CX18_APU_STOP, 1, CX18_APU_ENCODING_METHOD_MPEG);
+       /* Init the A/V decoder, if it hasn't been already */
+       v4l2_subdev_call(cx->sd_av, core, load_fw);
+       vf.tuner = 0;
+       vf.type = V4L2_TUNER_ANALOG_TV;
+       vf.frequency = 6400; /* the tuner 'baseline' frequency */
+       /* Set initial frequency. For PAL/SECAM broadcasts no
+          'default' channel exists AFAIK. */
+       if (cx->std == V4L2_STD_NTSC_M_JP)
+               vf.frequency = 1460;    /* ch. 1 91250*16/1000 */
+       else if (cx->std & V4L2_STD_NTSC_M)
+               vf.frequency = 1076;    /* ch. 4 67250*16/1000 */
+       video_input = cx->active_input;
+       cx->active_input++;     /* Force update of input */
+       cx18_s_input(NULL, &fh, video_input);
+       /* Let the VIDIOC_S_STD ioctl do all the work, keeps the code
+          in one place. */
+       cx->std++;              /* Force full standard initialization */
+       std = (cx->tuner_std == V4L2_STD_ALL) ? V4L2_STD_NTSC_M : cx->tuner_std;
+       cx18_s_std(NULL, &fh, &std);
+       cx18_s_frequency(NULL, &fh, &vf);
+       return 0;
+ }
+ static void cx18_cancel_in_work_orders(struct cx18 *cx)
+ {
+       int i;
+       for (i = 0; i < CX18_MAX_IN_WORK_ORDERS; i++)
+               cancel_work_sync(&cx->in_work_order[i].work);
+ }
+ static void cx18_cancel_out_work_orders(struct cx18 *cx)
+ {
+       int i;
+       for (i = 0; i < CX18_MAX_STREAMS; i++)
+               if (&cx->streams[i].video_dev != NULL)
+                       cancel_work_sync(&cx->streams[i].out_work_order);
+ }
+ static void cx18_remove(struct pci_dev *pci_dev)
+ {
+       struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
+       struct cx18 *cx = to_cx18(v4l2_dev);
+       int i;
+       CX18_DEBUG_INFO("Removing Card\n");
+       flush_request_modules(cx);
+       /* Stop all captures */
+       CX18_DEBUG_INFO("Stopping all streams\n");
+       if (atomic_read(&cx->tot_capturing) > 0)
+               cx18_stop_all_captures(cx);
+       /* Stop interrupts that cause incoming work to be queued */
+       cx18_sw1_irq_disable(cx, IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU);
+       /* Incoming work can cause outgoing work, so clean up incoming first */
+       cx18_cancel_in_work_orders(cx);
+       cx18_cancel_out_work_orders(cx);
+       /* Stop ack interrupts that may have been needed for work to finish */
+       cx18_sw2_irq_disable(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK);
+       cx18_halt_firmware(cx);
+       destroy_workqueue(cx->in_work_queue);
+       cx18_streams_cleanup(cx, 1);
+       exit_cx18_i2c(cx);
+       free_irq(cx->pci_dev->irq, (void *)cx);
+       cx18_iounmap(cx);
+       release_mem_region(cx->base_addr, CX18_MEM_SIZE);
+       pci_disable_device(cx->pci_dev);
+       if (cx->vbi.sliced_mpeg_data[0] != NULL)
+               for (i = 0; i < CX18_VBI_FRAMES; i++)
+                       kfree(cx->vbi.sliced_mpeg_data[i]);
+       v4l2_ctrl_handler_free(&cx->av_state.hdl);
+       CX18_INFO("Removed %s\n", cx->card_name);
+       v4l2_device_unregister(v4l2_dev);
+       kfree(cx);
+ }
+ /* define a pci_driver for card detection */
+ static struct pci_driver cx18_pci_driver = {
+       .name =     "cx18",
+       .id_table = cx18_pci_tbl,
+       .probe =    cx18_probe,
+       .remove =   cx18_remove,
+ };
+ static int __init module_start(void)
+ {
+       printk(KERN_INFO "cx18:  Start initialization, version %s\n",
+              CX18_VERSION);
+       /* Validate parameters */
+       if (cx18_first_minor < 0 || cx18_first_minor >= CX18_MAX_CARDS) {
+               printk(KERN_ERR "cx18:  Exiting, cx18_first_minor must be between 0 and %d\n",
+                    CX18_MAX_CARDS - 1);
+               return -1;
+       }
+       if (cx18_debug < 0 || cx18_debug > 511) {
+               cx18_debug = 0;
+               printk(KERN_INFO "cx18:   Debug value must be >= 0 and <= 511!\n");
+       }
+       if (pci_register_driver(&cx18_pci_driver)) {
+               printk(KERN_ERR "cx18:   Error detecting PCI card\n");
+               return -ENODEV;
+       }
+       printk(KERN_INFO "cx18:  End initialization\n");
+       return 0;
+ }
+ static void __exit module_cleanup(void)
+ {
+       pci_unregister_driver(&cx18_pci_driver);
+ }
+ module_init(module_start);
+ module_exit(module_cleanup);
+ MODULE_FIRMWARE(XC2028_DEFAULT_FIRMWARE);
index 0000000,5606672..2c925f7
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,365 +1,365 @@@
 -      flush_work_sync(&dev->cx25840_work);
 -      flush_work_sync(&dev->ir_rx_work);
 -      flush_work_sync(&dev->ir_tx_work);
+ /*
+  *  Driver for the Conexant CX23885/7/8 PCIe bridge
+  *
+  *  Infrared remote control input device
+  *
+  *  Most of this file is
+  *
+  *  Copyright (C) 2009  Andy Walls <awalls@md.metrocast.net>
+  *
+  *  However, the cx23885_input_{init,fini} functions contained herein are
+  *  derived from Linux kernel files linux/media/video/.../...-input.c marked as:
+  *
+  *  Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
+  *  Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it>
+  *                   Markus Rechberger <mrechberger@gmail.com>
+  *                   Mauro Carvalho Chehab <mchehab@infradead.org>
+  *                   Sascha Sommer <saschasommer@freenet.de>
+  *  Copyright (C) 2004, 2005 Chris Pascoe
+  *  Copyright (C) 2003, 2004 Gerd Knorr
+  *  Copyright (C) 2003 Pavel Machek
+  *
+  *  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/slab.h>
+ #include <media/rc-core.h>
+ #include <media/v4l2-subdev.h>
+ #include "cx23885.h"
+ #define MODULE_NAME "cx23885"
+ static void cx23885_input_process_measurements(struct cx23885_dev *dev,
+                                              bool overrun)
+ {
+       struct cx23885_kernel_ir *kernel_ir = dev->kernel_ir;
+       ssize_t num;
+       int count, i;
+       bool handle = false;
+       struct ir_raw_event ir_core_event[64];
+       do {
+               num = 0;
+               v4l2_subdev_call(dev->sd_ir, ir, rx_read, (u8 *) ir_core_event,
+                                sizeof(ir_core_event), &num);
+               count = num / sizeof(struct ir_raw_event);
+               for (i = 0; i < count; i++) {
+                       ir_raw_event_store(kernel_ir->rc,
+                                          &ir_core_event[i]);
+                       handle = true;
+               }
+       } while (num != 0);
+       if (overrun)
+               ir_raw_event_reset(kernel_ir->rc);
+       else if (handle)
+               ir_raw_event_handle(kernel_ir->rc);
+ }
+ void cx23885_input_rx_work_handler(struct cx23885_dev *dev, u32 events)
+ {
+       struct v4l2_subdev_ir_parameters params;
+       int overrun, data_available;
+       if (dev->sd_ir == NULL || events == 0)
+               return;
+       switch (dev->board) {
+       case CX23885_BOARD_HAUPPAUGE_HVR1270:
+       case CX23885_BOARD_HAUPPAUGE_HVR1850:
+       case CX23885_BOARD_HAUPPAUGE_HVR1290:
+       case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL:
+       case CX23885_BOARD_TEVII_S470:
+       case CX23885_BOARD_HAUPPAUGE_HVR1250:
+               /*
+                * The only boards we handle right now.  However other boards
+                * using the CX2388x integrated IR controller should be similar
+                */
+               break;
+       default:
+               return;
+       }
+       overrun = events & (V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN |
+                           V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN);
+       data_available = events & (V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED |
+                                  V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ);
+       if (overrun) {
+               /* If there was a FIFO overrun, stop the device */
+               v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, &params);
+               params.enable = false;
+               /* Mitigate race with cx23885_input_ir_stop() */
+               params.shutdown = atomic_read(&dev->ir_input_stopping);
+               v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, &params);
+       }
+       if (data_available)
+               cx23885_input_process_measurements(dev, overrun);
+       if (overrun) {
+               /* If there was a FIFO overrun, clear & restart the device */
+               params.enable = true;
+               /* Mitigate race with cx23885_input_ir_stop() */
+               params.shutdown = atomic_read(&dev->ir_input_stopping);
+               v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, &params);
+       }
+ }
+ static int cx23885_input_ir_start(struct cx23885_dev *dev)
+ {
+       struct v4l2_subdev_ir_parameters params;
+       if (dev->sd_ir == NULL)
+               return -ENODEV;
+       atomic_set(&dev->ir_input_stopping, 0);
+       v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, &params);
+       switch (dev->board) {
+       case CX23885_BOARD_HAUPPAUGE_HVR1270:
+       case CX23885_BOARD_HAUPPAUGE_HVR1850:
+       case CX23885_BOARD_HAUPPAUGE_HVR1290:
+       case CX23885_BOARD_HAUPPAUGE_HVR1250:
+               /*
+                * The IR controller on this board only returns pulse widths.
+                * Any other mode setting will fail to set up the device.
+               */
+               params.mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH;
+               params.enable = true;
+               params.interrupt_enable = true;
+               params.shutdown = false;
+               /* Setup for baseband compatible with both RC-5 and RC-6A */
+               params.modulation = false;
+               /* RC-5:  2,222,222 ns = 1/36 kHz * 32 cycles * 2 marks * 1.25*/
+               /* RC-6A: 3,333,333 ns = 1/36 kHz * 16 cycles * 6 marks * 1.25*/
+               params.max_pulse_width = 3333333; /* ns */
+               /* RC-5:    666,667 ns = 1/36 kHz * 32 cycles * 1 mark * 0.75 */
+               /* RC-6A:   333,333 ns = 1/36 kHz * 16 cycles * 1 mark * 0.75 */
+               params.noise_filter_min_width = 333333; /* ns */
+               /*
+                * This board has inverted receive sense:
+                * mark is received as low logic level;
+                * falling edges are detected as rising edges; etc.
+                */
+               params.invert_level = true;
+               break;
+       case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL:
+       case CX23885_BOARD_TEVII_S470:
+               /*
+                * The IR controller on this board only returns pulse widths.
+                * Any other mode setting will fail to set up the device.
+                */
+               params.mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH;
+               params.enable = true;
+               params.interrupt_enable = true;
+               params.shutdown = false;
+               /* Setup for a standard NEC protocol */
+               params.carrier_freq = 37917; /* Hz, 455 kHz/12 for NEC */
+               params.carrier_range_lower = 33000; /* Hz */
+               params.carrier_range_upper = 43000; /* Hz */
+               params.duty_cycle = 33; /* percent, 33 percent for NEC */
+               /*
+                * NEC max pulse width: (64/3)/(455 kHz/12) * 16 nec_units
+                * (64/3)/(455 kHz/12) * 16 nec_units * 1.375 = 12378022 ns
+                */
+               params.max_pulse_width = 12378022; /* ns */
+               /*
+                * NEC noise filter min width: (64/3)/(455 kHz/12) * 1 nec_unit
+                * (64/3)/(455 kHz/12) * 1 nec_units * 0.625 = 351648 ns
+                */
+               params.noise_filter_min_width = 351648; /* ns */
+               params.modulation = false;
+               params.invert_level = true;
+               break;
+       }
+       v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, &params);
+       return 0;
+ }
+ static int cx23885_input_ir_open(struct rc_dev *rc)
+ {
+       struct cx23885_kernel_ir *kernel_ir = rc->priv;
+       if (kernel_ir->cx == NULL)
+               return -ENODEV;
+       return cx23885_input_ir_start(kernel_ir->cx);
+ }
+ static void cx23885_input_ir_stop(struct cx23885_dev *dev)
+ {
+       struct v4l2_subdev_ir_parameters params;
+       if (dev->sd_ir == NULL)
+               return;
+       /*
+        * Stop the sd_ir subdevice from generating notifications and
+        * scheduling work.
+        * It is shutdown this way in order to mitigate a race with
+        * cx23885_input_rx_work_handler() in the overrun case, which could
+        * re-enable the subdevice.
+        */
+       atomic_set(&dev->ir_input_stopping, 1);
+       v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, &params);
+       while (params.shutdown == false) {
+               params.enable = false;
+               params.interrupt_enable = false;
+               params.shutdown = true;
+               v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, &params);
+               v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, &params);
+       }
++      flush_work(&dev->cx25840_work);
++      flush_work(&dev->ir_rx_work);
++      flush_work(&dev->ir_tx_work);
+ }
+ static void cx23885_input_ir_close(struct rc_dev *rc)
+ {
+       struct cx23885_kernel_ir *kernel_ir = rc->priv;
+       if (kernel_ir->cx != NULL)
+               cx23885_input_ir_stop(kernel_ir->cx);
+ }
+ int cx23885_input_init(struct cx23885_dev *dev)
+ {
+       struct cx23885_kernel_ir *kernel_ir;
+       struct rc_dev *rc;
+       char *rc_map;
+       enum rc_driver_type driver_type;
+       unsigned long allowed_protos;
+       int ret;
+       /*
+        * If the IR device (hardware registers, chip, GPIO lines, etc.) isn't
+        * encapsulated in a v4l2_subdev, then I'm not going to deal with it.
+        */
+       if (dev->sd_ir == NULL)
+               return -ENODEV;
+       switch (dev->board) {
+       case CX23885_BOARD_HAUPPAUGE_HVR1270:
+       case CX23885_BOARD_HAUPPAUGE_HVR1850:
+       case CX23885_BOARD_HAUPPAUGE_HVR1290:
+       case CX23885_BOARD_HAUPPAUGE_HVR1250:
+               /* Integrated CX2388[58] IR controller */
+               driver_type = RC_DRIVER_IR_RAW;
+               allowed_protos = RC_TYPE_ALL;
+               /* The grey Hauppauge RC-5 remote */
+               rc_map = RC_MAP_HAUPPAUGE;
+               break;
+       case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL:
+               /* Integrated CX23885 IR controller */
+               driver_type = RC_DRIVER_IR_RAW;
+               allowed_protos = RC_TYPE_NEC;
+               /* The grey Terratec remote with orange buttons */
+               rc_map = RC_MAP_NEC_TERRATEC_CINERGY_XS;
+               break;
+       case CX23885_BOARD_TEVII_S470:
+               /* Integrated CX23885 IR controller */
+               driver_type = RC_DRIVER_IR_RAW;
+               allowed_protos = RC_TYPE_ALL;
+               /* A guess at the remote */
+               rc_map = RC_MAP_TEVII_NEC;
+               break;
+       default:
+               return -ENODEV;
+       }
+       /* cx23885 board instance kernel IR state */
+       kernel_ir = kzalloc(sizeof(struct cx23885_kernel_ir), GFP_KERNEL);
+       if (kernel_ir == NULL)
+               return -ENOMEM;
+       kernel_ir->cx = dev;
+       kernel_ir->name = kasprintf(GFP_KERNEL, "cx23885 IR (%s)",
+                                   cx23885_boards[dev->board].name);
+       kernel_ir->phys = kasprintf(GFP_KERNEL, "pci-%s/ir0",
+                                   pci_name(dev->pci));
+       /* input device */
+       rc = rc_allocate_device();
+       if (!rc) {
+               ret = -ENOMEM;
+               goto err_out_free;
+       }
+       kernel_ir->rc = rc;
+       rc->input_name = kernel_ir->name;
+       rc->input_phys = kernel_ir->phys;
+       rc->input_id.bustype = BUS_PCI;
+       rc->input_id.version = 1;
+       if (dev->pci->subsystem_vendor) {
+               rc->input_id.vendor  = dev->pci->subsystem_vendor;
+               rc->input_id.product = dev->pci->subsystem_device;
+       } else {
+               rc->input_id.vendor  = dev->pci->vendor;
+               rc->input_id.product = dev->pci->device;
+       }
+       rc->dev.parent = &dev->pci->dev;
+       rc->driver_type = driver_type;
+       rc->allowed_protos = allowed_protos;
+       rc->priv = kernel_ir;
+       rc->open = cx23885_input_ir_open;
+       rc->close = cx23885_input_ir_close;
+       rc->map_name = rc_map;
+       rc->driver_name = MODULE_NAME;
+       /* Go */
+       dev->kernel_ir = kernel_ir;
+       ret = rc_register_device(rc);
+       if (ret)
+               goto err_out_stop;
+       return 0;
+ err_out_stop:
+       cx23885_input_ir_stop(dev);
+       dev->kernel_ir = NULL;
+       rc_free_device(rc);
+ err_out_free:
+       kfree(kernel_ir->phys);
+       kfree(kernel_ir->name);
+       kfree(kernel_ir);
+       return ret;
+ }
+ void cx23885_input_fini(struct cx23885_dev *dev)
+ {
+       /* Always stop the IR hardware from generating interrupts */
+       cx23885_input_ir_stop(dev);
+       if (dev->kernel_ir == NULL)
+               return;
+       rc_unregister_device(dev->kernel_ir->rc);
+       kfree(dev->kernel_ir->phys);
+       kfree(dev->kernel_ir->name);
+       kfree(dev->kernel_ir);
+       dev->kernel_ir = NULL;
+ }
Simple merge
Simple merge
index 0000000,a6cd695..96a13ed
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,823 +1,823 @@@
 -static struct pci_error_handlers ngene_errors = {
+ /*
+  * ngene-cards.c: nGene PCIe bridge driver - card specific info
+  *
+  * Copyright (C) 2005-2007 Micronas
+  *
+  * Copyright (C) 2008-2009 Ralph Metzler <rjkm@metzlerbros.de>
+  *                         Modifications for new nGene firmware,
+  *                         support for EEPROM-copying,
+  *                         support for new dual DVB-S2 card prototype
+  *
+  *
+  * This program is free software; you can redistribute it and/or
+  * modify it under the terms of the GNU General Public License
+  * version 2 only, as published by the Free Software Foundation.
+  *
+  *
+  * 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
+  * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+  */
+ #include <linux/module.h>
+ #include <linux/init.h>
+ #include <linux/pci.h>
+ #include <linux/pci_ids.h>
+ #include "ngene.h"
+ /* demods/tuners */
+ #include "stv6110x.h"
+ #include "stv090x.h"
+ #include "lnbh24.h"
+ #include "lgdt330x.h"
+ #include "mt2131.h"
+ #include "tda18271c2dd.h"
+ #include "drxk.h"
+ #include "drxd.h"
+ #include "dvb-pll.h"
+ /****************************************************************************/
+ /* Demod/tuner attachment ***************************************************/
+ /****************************************************************************/
+ static int tuner_attach_stv6110(struct ngene_channel *chan)
+ {
+       struct i2c_adapter *i2c;
+       struct stv090x_config *feconf = (struct stv090x_config *)
+               chan->dev->card_info->fe_config[chan->number];
+       struct stv6110x_config *tunerconf = (struct stv6110x_config *)
+               chan->dev->card_info->tuner_config[chan->number];
+       struct stv6110x_devctl *ctl;
+       /* tuner 1+2: i2c adapter #0, tuner 3+4: i2c adapter #1 */
+       if (chan->number < 2)
+               i2c = &chan->dev->channel[0].i2c_adapter;
+       else
+               i2c = &chan->dev->channel[1].i2c_adapter;
+       ctl = dvb_attach(stv6110x_attach, chan->fe, tunerconf, i2c);
+       if (ctl == NULL) {
+               printk(KERN_ERR DEVICE_NAME ": No STV6110X found!\n");
+               return -ENODEV;
+       }
+       feconf->tuner_init          = ctl->tuner_init;
+       feconf->tuner_sleep         = ctl->tuner_sleep;
+       feconf->tuner_set_mode      = ctl->tuner_set_mode;
+       feconf->tuner_set_frequency = ctl->tuner_set_frequency;
+       feconf->tuner_get_frequency = ctl->tuner_get_frequency;
+       feconf->tuner_set_bandwidth = ctl->tuner_set_bandwidth;
+       feconf->tuner_get_bandwidth = ctl->tuner_get_bandwidth;
+       feconf->tuner_set_bbgain    = ctl->tuner_set_bbgain;
+       feconf->tuner_get_bbgain    = ctl->tuner_get_bbgain;
+       feconf->tuner_set_refclk    = ctl->tuner_set_refclk;
+       feconf->tuner_get_status    = ctl->tuner_get_status;
+       return 0;
+ }
+ static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable)
+ {
+       struct ngene_channel *chan = fe->sec_priv;
+       int status;
+       if (enable) {
+               down(&chan->dev->pll_mutex);
+               status = chan->gate_ctrl(fe, 1);
+       } else {
+               status = chan->gate_ctrl(fe, 0);
+               up(&chan->dev->pll_mutex);
+       }
+       return status;
+ }
+ static int tuner_attach_tda18271(struct ngene_channel *chan)
+ {
+       struct i2c_adapter *i2c;
+       struct dvb_frontend *fe;
+       i2c = &chan->dev->channel[0].i2c_adapter;
+       if (chan->fe->ops.i2c_gate_ctrl)
+               chan->fe->ops.i2c_gate_ctrl(chan->fe, 1);
+       fe = dvb_attach(tda18271c2dd_attach, chan->fe, i2c, 0x60);
+       if (chan->fe->ops.i2c_gate_ctrl)
+               chan->fe->ops.i2c_gate_ctrl(chan->fe, 0);
+       if (!fe) {
+               printk(KERN_ERR "No TDA18271 found!\n");
+               return -ENODEV;
+       }
+       return 0;
+ }
+ static int tuner_attach_probe(struct ngene_channel *chan)
+ {
+       if (chan->demod_type == 0)
+               return tuner_attach_stv6110(chan);
+       if (chan->demod_type == 1)
+               return tuner_attach_tda18271(chan);
+       return -EINVAL;
+ }
+ static int demod_attach_stv0900(struct ngene_channel *chan)
+ {
+       struct i2c_adapter *i2c;
+       struct stv090x_config *feconf = (struct stv090x_config *)
+               chan->dev->card_info->fe_config[chan->number];
+       /* tuner 1+2: i2c adapter #0, tuner 3+4: i2c adapter #1 */
+       /* Note: Both adapters share the same i2c bus, but the demod     */
+       /*       driver requires that each demod has its own i2c adapter */
+       if (chan->number < 2)
+               i2c = &chan->dev->channel[0].i2c_adapter;
+       else
+               i2c = &chan->dev->channel[1].i2c_adapter;
+       chan->fe = dvb_attach(stv090x_attach, feconf, i2c,
+                       (chan->number & 1) == 0 ? STV090x_DEMODULATOR_0
+                                               : STV090x_DEMODULATOR_1);
+       if (chan->fe == NULL) {
+               printk(KERN_ERR DEVICE_NAME ": No STV0900 found!\n");
+               return -ENODEV;
+       }
+       /* store channel info */
+       if (feconf->tuner_i2c_lock)
+               chan->fe->analog_demod_priv = chan;
+       if (!dvb_attach(lnbh24_attach, chan->fe, i2c, 0,
+                       0, chan->dev->card_info->lnb[chan->number])) {
+               printk(KERN_ERR DEVICE_NAME ": No LNBH24 found!\n");
+               dvb_frontend_detach(chan->fe);
+               chan->fe = NULL;
+               return -ENODEV;
+       }
+       return 0;
+ }
+ static void cineS2_tuner_i2c_lock(struct dvb_frontend *fe, int lock)
+ {
+       struct ngene_channel *chan = fe->analog_demod_priv;
+       if (lock)
+               down(&chan->dev->pll_mutex);
+       else
+               up(&chan->dev->pll_mutex);
+ }
+ static int i2c_read(struct i2c_adapter *adapter, u8 adr, u8 *val)
+ {
+       struct i2c_msg msgs[1] = {{.addr = adr,  .flags = I2C_M_RD,
+                                  .buf  = val,  .len   = 1 } };
+       return (i2c_transfer(adapter, msgs, 1) == 1) ? 0 : -1;
+ }
+ static int i2c_read_reg16(struct i2c_adapter *adapter, u8 adr,
+                         u16 reg, u8 *val)
+ {
+       u8 msg[2] = {reg>>8, reg&0xff};
+       struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0,
+                                  .buf  = msg, .len   = 2},
+                                 {.addr = adr, .flags = I2C_M_RD,
+                                  .buf  = val, .len   = 1} };
+       return (i2c_transfer(adapter, msgs, 2) == 2) ? 0 : -1;
+ }
+ static int port_has_stv0900(struct i2c_adapter *i2c, int port)
+ {
+       u8 val;
+       if (i2c_read_reg16(i2c, 0x68+port/2, 0xf100, &val) < 0)
+               return 0;
+       return 1;
+ }
+ static int port_has_drxk(struct i2c_adapter *i2c, int port)
+ {
+       u8 val;
+       if (i2c_read(i2c, 0x29+port, &val) < 0)
+               return 0;
+       return 1;
+ }
+ static int demod_attach_drxk(struct ngene_channel *chan,
+                            struct i2c_adapter *i2c)
+ {
+       struct drxk_config config;
+       memset(&config, 0, sizeof(config));
+       config.microcode_name = "drxk_a3.mc";
+       config.qam_demod_parameter_count = 4;
+       config.adr = 0x29 + (chan->number ^ 2);
+       chan->fe = dvb_attach(drxk_attach, &config, i2c);
+       if (!chan->fe) {
+               printk(KERN_ERR "No DRXK found!\n");
+               return -ENODEV;
+       }
+       chan->fe->sec_priv = chan;
+       chan->gate_ctrl = chan->fe->ops.i2c_gate_ctrl;
+       chan->fe->ops.i2c_gate_ctrl = drxk_gate_ctrl;
+       return 0;
+ }
+ static int cineS2_probe(struct ngene_channel *chan)
+ {
+       struct i2c_adapter *i2c;
+       struct stv090x_config *fe_conf;
+       u8 buf[3];
+       struct i2c_msg i2c_msg = { .flags = 0, .buf = buf };
+       int rc;
+       /* tuner 1+2: i2c adapter #0, tuner 3+4: i2c adapter #1 */
+       if (chan->number < 2)
+               i2c = &chan->dev->channel[0].i2c_adapter;
+       else
+               i2c = &chan->dev->channel[1].i2c_adapter;
+       if (port_has_stv0900(i2c, chan->number)) {
+               chan->demod_type = 0;
+               fe_conf = chan->dev->card_info->fe_config[chan->number];
+               /* demod found, attach it */
+               rc = demod_attach_stv0900(chan);
+               if (rc < 0 || chan->number < 2)
+                       return rc;
+               /* demod #2: reprogram outputs DPN1 & DPN2 */
+               i2c_msg.addr = fe_conf->address;
+               i2c_msg.len = 3;
+               buf[0] = 0xf1;
+               switch (chan->number) {
+               case 2:
+                       buf[1] = 0x5c;
+                       buf[2] = 0xc2;
+                       break;
+               case 3:
+                       buf[1] = 0x61;
+                       buf[2] = 0xcc;
+                       break;
+               default:
+                       return -ENODEV;
+               }
+               rc = i2c_transfer(i2c, &i2c_msg, 1);
+               if (rc != 1) {
+                       printk(KERN_ERR DEVICE_NAME ": could not setup DPNx\n");
+                       return -EIO;
+               }
+       } else if (port_has_drxk(i2c, chan->number^2)) {
+               chan->demod_type = 1;
+               demod_attach_drxk(chan, i2c);
+       } else {
+               printk(KERN_ERR "No demod found on chan %d\n", chan->number);
+               return -ENODEV;
+       }
+       return 0;
+ }
+ static struct lgdt330x_config aver_m780 = {
+       .demod_address = 0xb2 >> 1,
+       .demod_chip    = LGDT3303,
+       .serial_mpeg   = 0x00, /* PARALLEL */
+       .clock_polarity_flip = 1,
+ };
+ static struct mt2131_config m780_tunerconfig = {
+       0xc0 >> 1
+ };
+ /* A single func to attach the demo and tuner, rather than
+  * use two sep funcs like the current design mandates.
+  */
+ static int demod_attach_lg330x(struct ngene_channel *chan)
+ {
+       chan->fe = dvb_attach(lgdt330x_attach, &aver_m780, &chan->i2c_adapter);
+       if (chan->fe == NULL) {
+               printk(KERN_ERR DEVICE_NAME ": No LGDT330x found!\n");
+               return -ENODEV;
+       }
+       dvb_attach(mt2131_attach, chan->fe, &chan->i2c_adapter,
+                  &m780_tunerconfig, 0);
+       return (chan->fe) ? 0 : -ENODEV;
+ }
+ static int demod_attach_drxd(struct ngene_channel *chan)
+ {
+       struct drxd_config *feconf;
+       feconf = chan->dev->card_info->fe_config[chan->number];
+       chan->fe = dvb_attach(drxd_attach, feconf, chan,
+                       &chan->i2c_adapter, &chan->dev->pci_dev->dev);
+       if (!chan->fe) {
+               pr_err("No DRXD found!\n");
+               return -ENODEV;
+       }
+       if (!dvb_attach(dvb_pll_attach, chan->fe, feconf->pll_address,
+                       &chan->i2c_adapter,
+                       feconf->pll_type)) {
+               pr_err("No pll(%d) found!\n", feconf->pll_type);
+               return -ENODEV;
+       }
+       return 0;
+ }
+ /****************************************************************************/
+ /* EEPROM TAGS **************************************************************/
+ /****************************************************************************/
+ #define MICNG_EE_START      0x0100
+ #define MICNG_EE_END        0x0FF0
+ #define MICNG_EETAG_END0    0x0000
+ #define MICNG_EETAG_END1    0xFFFF
+ /* 0x0001 - 0x000F reserved for housekeeping */
+ /* 0xFFFF - 0xFFFE reserved for housekeeping */
+ /* Micronas assigned tags
+    EEProm tags for hardware support */
+ #define MICNG_EETAG_DRXD1_OSCDEVIATION  0x1000  /* 2 Bytes data */
+ #define MICNG_EETAG_DRXD2_OSCDEVIATION  0x1001  /* 2 Bytes data */
+ #define MICNG_EETAG_MT2060_1_1STIF      0x1100  /* 2 Bytes data */
+ #define MICNG_EETAG_MT2060_2_1STIF      0x1101  /* 2 Bytes data */
+ /* Tag range for OEMs */
+ #define MICNG_EETAG_OEM_FIRST  0xC000
+ #define MICNG_EETAG_OEM_LAST   0xFFEF
+ static int i2c_write_eeprom(struct i2c_adapter *adapter,
+                           u8 adr, u16 reg, u8 data)
+ {
+       u8 m[3] = {(reg >> 8), (reg & 0xff), data};
+       struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = m,
+                             .len = sizeof(m)};
+       if (i2c_transfer(adapter, &msg, 1) != 1) {
+               pr_err(DEVICE_NAME ": Error writing EEPROM!\n");
+               return -EIO;
+       }
+       return 0;
+ }
+ static int i2c_read_eeprom(struct i2c_adapter *adapter,
+                          u8 adr, u16 reg, u8 *data, int len)
+ {
+       u8 msg[2] = {(reg >> 8), (reg & 0xff)};
+       struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0,
+                                  .buf = msg, .len = 2 },
+                                 {.addr = adr, .flags = I2C_M_RD,
+                                  .buf = data, .len = len} };
+       if (i2c_transfer(adapter, msgs, 2) != 2) {
+               pr_err(DEVICE_NAME ": Error reading EEPROM\n");
+               return -EIO;
+       }
+       return 0;
+ }
+ static int ReadEEProm(struct i2c_adapter *adapter,
+                     u16 Tag, u32 MaxLen, u8 *data, u32 *pLength)
+ {
+       int status = 0;
+       u16 Addr = MICNG_EE_START, Length, tag = 0;
+       u8  EETag[3];
+       while (Addr + sizeof(u16) + 1 < MICNG_EE_END) {
+               if (i2c_read_eeprom(adapter, 0x50, Addr, EETag, sizeof(EETag)))
+                       return -1;
+               tag = (EETag[0] << 8) | EETag[1];
+               if (tag == MICNG_EETAG_END0 || tag == MICNG_EETAG_END1)
+                       return -1;
+               if (tag == Tag)
+                       break;
+               Addr += sizeof(u16) + 1 + EETag[2];
+       }
+       if (Addr + sizeof(u16) + 1 + EETag[2] > MICNG_EE_END) {
+               pr_err(DEVICE_NAME
+                      ": Reached EOEE @ Tag = %04x Length = %3d\n",
+                      tag, EETag[2]);
+               return -1;
+       }
+       Length = EETag[2];
+       if (Length > MaxLen)
+               Length = (u16) MaxLen;
+       if (Length > 0) {
+               Addr += sizeof(u16) + 1;
+               status = i2c_read_eeprom(adapter, 0x50, Addr, data, Length);
+               if (!status) {
+                       *pLength = EETag[2];
+                       if (Length < EETag[2])
+                               ; /*status=STATUS_BUFFER_OVERFLOW; */
+               }
+       }
+       return status;
+ }
+ static int WriteEEProm(struct i2c_adapter *adapter,
+                      u16 Tag, u32 Length, u8 *data)
+ {
+       int status = 0;
+       u16 Addr = MICNG_EE_START;
+       u8 EETag[3];
+       u16 tag = 0;
+       int retry, i;
+       while (Addr + sizeof(u16) + 1 < MICNG_EE_END) {
+               if (i2c_read_eeprom(adapter, 0x50, Addr, EETag, sizeof(EETag)))
+                       return -1;
+               tag = (EETag[0] << 8) | EETag[1];
+               if (tag == MICNG_EETAG_END0 || tag == MICNG_EETAG_END1)
+                       return -1;
+               if (tag == Tag)
+                       break;
+               Addr += sizeof(u16) + 1 + EETag[2];
+       }
+       if (Addr + sizeof(u16) + 1 + EETag[2] > MICNG_EE_END) {
+               pr_err(DEVICE_NAME
+                      ": Reached EOEE @ Tag = %04x Length = %3d\n",
+                      tag, EETag[2]);
+               return -1;
+       }
+       if (Length > EETag[2])
+               return -EINVAL;
+       /* Note: We write the data one byte at a time to avoid
+          issues with page sizes. (which are different for
+          each manufacture and eeprom size)
+        */
+       Addr += sizeof(u16) + 1;
+       for (i = 0; i < Length; i++, Addr++) {
+               status = i2c_write_eeprom(adapter, 0x50, Addr, data[i]);
+               if (status)
+                       break;
+               /* Poll for finishing write cycle */
+               retry = 10;
+               while (retry) {
+                       u8 Tmp;
+                       msleep(50);
+                       status = i2c_read_eeprom(adapter, 0x50, Addr, &Tmp, 1);
+                       if (status)
+                               break;
+                       if (Tmp != data[i])
+                               pr_err(DEVICE_NAME
+                                      "eeprom write error\n");
+                       retry -= 1;
+               }
+               if (status) {
+                       pr_err(DEVICE_NAME
+                              ": Timeout polling eeprom\n");
+                       break;
+               }
+       }
+       return status;
+ }
+ static int eeprom_read_ushort(struct i2c_adapter *adapter, u16 tag, u16 *data)
+ {
+       int stat;
+       u8 buf[2];
+       u32 len = 0;
+       stat = ReadEEProm(adapter, tag, 2, buf, &len);
+       if (stat)
+               return stat;
+       if (len != 2)
+               return -EINVAL;
+       *data = (buf[0] << 8) | buf[1];
+       return 0;
+ }
+ static int eeprom_write_ushort(struct i2c_adapter *adapter, u16 tag, u16 data)
+ {
+       int stat;
+       u8 buf[2];
+       buf[0] = data >> 8;
+       buf[1] = data & 0xff;
+       stat = WriteEEProm(adapter, tag, 2, buf);
+       if (stat)
+               return stat;
+       return 0;
+ }
+ static s16 osc_deviation(void *priv, s16 deviation, int flag)
+ {
+       struct ngene_channel *chan = priv;
+       struct i2c_adapter *adap = &chan->i2c_adapter;
+       u16 data = 0;
+       if (flag) {
+               data = (u16) deviation;
+               pr_info(DEVICE_NAME ": write deviation %d\n",
+                      deviation);
+               eeprom_write_ushort(adap, 0x1000 + chan->number, data);
+       } else {
+               if (eeprom_read_ushort(adap, 0x1000 + chan->number, &data))
+                       data = 0;
+               pr_info(DEVICE_NAME ": read deviation %d\n",
+                      (s16) data);
+       }
+       return (s16) data;
+ }
+ /****************************************************************************/
+ /* Switch control (I2C gates, etc.) *****************************************/
+ /****************************************************************************/
+ static struct stv090x_config fe_cineS2 = {
+       .device         = STV0900,
+       .demod_mode     = STV090x_DUAL,
+       .clk_mode       = STV090x_CLK_EXT,
+       .xtal           = 27000000,
+       .address        = 0x68,
+       .ts1_mode       = STV090x_TSMODE_SERIAL_PUNCTURED,
+       .ts2_mode       = STV090x_TSMODE_SERIAL_PUNCTURED,
+       .repeater_level = STV090x_RPTLEVEL_16,
+       .adc1_range     = STV090x_ADC_1Vpp,
+       .adc2_range     = STV090x_ADC_1Vpp,
+       .diseqc_envelope_mode = true,
+       .tuner_i2c_lock = cineS2_tuner_i2c_lock,
+ };
+ static struct stv090x_config fe_cineS2_2 = {
+       .device         = STV0900,
+       .demod_mode     = STV090x_DUAL,
+       .clk_mode       = STV090x_CLK_EXT,
+       .xtal           = 27000000,
+       .address        = 0x69,
+       .ts1_mode       = STV090x_TSMODE_SERIAL_PUNCTURED,
+       .ts2_mode       = STV090x_TSMODE_SERIAL_PUNCTURED,
+       .repeater_level = STV090x_RPTLEVEL_16,
+       .adc1_range     = STV090x_ADC_1Vpp,
+       .adc2_range     = STV090x_ADC_1Vpp,
+       .diseqc_envelope_mode = true,
+       .tuner_i2c_lock = cineS2_tuner_i2c_lock,
+ };
+ static struct stv6110x_config tuner_cineS2_0 = {
+       .addr   = 0x60,
+       .refclk = 27000000,
+       .clk_div = 1,
+ };
+ static struct stv6110x_config tuner_cineS2_1 = {
+       .addr   = 0x63,
+       .refclk = 27000000,
+       .clk_div = 1,
+ };
+ static struct ngene_info ngene_info_cineS2 = {
+       .type           = NGENE_SIDEWINDER,
+       .name           = "Linux4Media cineS2 DVB-S2 Twin Tuner",
+       .io_type        = {NGENE_IO_TSIN, NGENE_IO_TSIN},
+       .demod_attach   = {demod_attach_stv0900, demod_attach_stv0900},
+       .tuner_attach   = {tuner_attach_stv6110, tuner_attach_stv6110},
+       .fe_config      = {&fe_cineS2, &fe_cineS2},
+       .tuner_config   = {&tuner_cineS2_0, &tuner_cineS2_1},
+       .lnb            = {0x0b, 0x08},
+       .tsf            = {3, 3},
+       .fw_version     = 18,
+       .msi_supported  = true,
+ };
+ static struct ngene_info ngene_info_satixS2 = {
+       .type           = NGENE_SIDEWINDER,
+       .name           = "Mystique SaTiX-S2 Dual",
+       .io_type        = {NGENE_IO_TSIN, NGENE_IO_TSIN},
+       .demod_attach   = {demod_attach_stv0900, demod_attach_stv0900},
+       .tuner_attach   = {tuner_attach_stv6110, tuner_attach_stv6110},
+       .fe_config      = {&fe_cineS2, &fe_cineS2},
+       .tuner_config   = {&tuner_cineS2_0, &tuner_cineS2_1},
+       .lnb            = {0x0b, 0x08},
+       .tsf            = {3, 3},
+       .fw_version     = 18,
+       .msi_supported  = true,
+ };
+ static struct ngene_info ngene_info_satixS2v2 = {
+       .type           = NGENE_SIDEWINDER,
+       .name           = "Mystique SaTiX-S2 Dual (v2)",
+       .io_type        = {NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN,
+                          NGENE_IO_TSOUT},
+       .demod_attach   = {demod_attach_stv0900, demod_attach_stv0900, cineS2_probe, cineS2_probe},
+       .tuner_attach   = {tuner_attach_stv6110, tuner_attach_stv6110, tuner_attach_probe, tuner_attach_probe},
+       .fe_config      = {&fe_cineS2, &fe_cineS2, &fe_cineS2_2, &fe_cineS2_2},
+       .tuner_config   = {&tuner_cineS2_0, &tuner_cineS2_1, &tuner_cineS2_0, &tuner_cineS2_1},
+       .lnb            = {0x0a, 0x08, 0x0b, 0x09},
+       .tsf            = {3, 3},
+       .fw_version     = 18,
+       .msi_supported  = true,
+ };
+ static struct ngene_info ngene_info_cineS2v5 = {
+       .type           = NGENE_SIDEWINDER,
+       .name           = "Linux4Media cineS2 DVB-S2 Twin Tuner (v5)",
+       .io_type        = {NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN,
+                          NGENE_IO_TSOUT},
+       .demod_attach   = {demod_attach_stv0900, demod_attach_stv0900, cineS2_probe, cineS2_probe},
+       .tuner_attach   = {tuner_attach_stv6110, tuner_attach_stv6110, tuner_attach_probe, tuner_attach_probe},
+       .fe_config      = {&fe_cineS2, &fe_cineS2, &fe_cineS2_2, &fe_cineS2_2},
+       .tuner_config   = {&tuner_cineS2_0, &tuner_cineS2_1, &tuner_cineS2_0, &tuner_cineS2_1},
+       .lnb            = {0x0a, 0x08, 0x0b, 0x09},
+       .tsf            = {3, 3},
+       .fw_version     = 18,
+       .msi_supported  = true,
+ };
+ static struct ngene_info ngene_info_duoFlex = {
+       .type           = NGENE_SIDEWINDER,
+       .name           = "Digital Devices DuoFlex PCIe or miniPCIe",
+       .io_type        = {NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN,
+                          NGENE_IO_TSOUT},
+       .demod_attach   = {cineS2_probe, cineS2_probe, cineS2_probe, cineS2_probe},
+       .tuner_attach   = {tuner_attach_probe, tuner_attach_probe, tuner_attach_probe, tuner_attach_probe},
+       .fe_config      = {&fe_cineS2, &fe_cineS2, &fe_cineS2_2, &fe_cineS2_2},
+       .tuner_config   = {&tuner_cineS2_0, &tuner_cineS2_1, &tuner_cineS2_0, &tuner_cineS2_1},
+       .lnb            = {0x0a, 0x08, 0x0b, 0x09},
+       .tsf            = {3, 3},
+       .fw_version     = 18,
+       .msi_supported  = true,
+ };
+ static struct ngene_info ngene_info_m780 = {
+       .type           = NGENE_APP,
+       .name           = "Aver M780 ATSC/QAM-B",
+       /* Channel 0 is analog, which is currently unsupported */
+       .io_type        = { NGENE_IO_NONE, NGENE_IO_TSIN },
+       .demod_attach   = { NULL, demod_attach_lg330x },
+       /* Ensure these are NULL else the frame will call them (as funcs) */
+       .tuner_attach   = { 0, 0, 0, 0 },
+       .fe_config      = { NULL, &aver_m780 },
+       .avf            = { 0 },
+       /* A custom electrical interface config for the demod to bridge */
+       .tsf            = { 4, 4 },
+       .fw_version     = 15,
+ };
+ static struct drxd_config fe_terratec_dvbt_0 = {
+       .index          = 0,
+       .demod_address  = 0x70,
+       .demod_revision = 0xa2,
+       .demoda_address = 0x00,
+       .pll_address    = 0x60,
+       .pll_type       = DVB_PLL_THOMSON_DTT7520X,
+       .clock          = 20000,
+       .osc_deviation  = osc_deviation,
+ };
+ static struct drxd_config fe_terratec_dvbt_1 = {
+       .index          = 1,
+       .demod_address  = 0x71,
+       .demod_revision = 0xa2,
+       .demoda_address = 0x00,
+       .pll_address    = 0x60,
+       .pll_type       = DVB_PLL_THOMSON_DTT7520X,
+       .clock          = 20000,
+       .osc_deviation  = osc_deviation,
+ };
+ static struct ngene_info ngene_info_terratec = {
+       .type           = NGENE_TERRATEC,
+       .name           = "Terratec Integra/Cinergy2400i Dual DVB-T",
+       .io_type        = {NGENE_IO_TSIN, NGENE_IO_TSIN},
+       .demod_attach   = {demod_attach_drxd, demod_attach_drxd},
+       .fe_config      = {&fe_terratec_dvbt_0, &fe_terratec_dvbt_1},
+       .i2c_access     = 1,
+ };
+ /****************************************************************************/
+ /****************************************************************************/
+ /* PCI Subsystem ID *********************************************************/
+ /****************************************************************************/
+ #define NGENE_ID(_subvend, _subdev, _driverdata) { \
+       .vendor = NGENE_VID, .device = NGENE_PID, \
+       .subvendor = _subvend, .subdevice = _subdev, \
+       .driver_data = (unsigned long) &_driverdata }
+ /****************************************************************************/
+ static const struct pci_device_id ngene_id_tbl[] __devinitdata = {
+       NGENE_ID(0x18c3, 0xabc3, ngene_info_cineS2),
+       NGENE_ID(0x18c3, 0xabc4, ngene_info_cineS2),
+       NGENE_ID(0x18c3, 0xdb01, ngene_info_satixS2),
+       NGENE_ID(0x18c3, 0xdb02, ngene_info_satixS2v2),
+       NGENE_ID(0x18c3, 0xdd00, ngene_info_cineS2v5),
+       NGENE_ID(0x18c3, 0xdd10, ngene_info_duoFlex),
+       NGENE_ID(0x18c3, 0xdd20, ngene_info_duoFlex),
+       NGENE_ID(0x1461, 0x062e, ngene_info_m780),
+       NGENE_ID(0x153b, 0x1167, ngene_info_terratec),
+       {0}
+ };
+ MODULE_DEVICE_TABLE(pci, ngene_id_tbl);
+ /****************************************************************************/
+ /* Init/Exit ****************************************************************/
+ /****************************************************************************/
+ static pci_ers_result_t ngene_error_detected(struct pci_dev *dev,
+                                            enum pci_channel_state state)
+ {
+       printk(KERN_ERR DEVICE_NAME ": PCI error\n");
+       if (state == pci_channel_io_perm_failure)
+               return PCI_ERS_RESULT_DISCONNECT;
+       if (state == pci_channel_io_frozen)
+               return PCI_ERS_RESULT_NEED_RESET;
+       return PCI_ERS_RESULT_CAN_RECOVER;
+ }
+ static pci_ers_result_t ngene_link_reset(struct pci_dev *dev)
+ {
+       printk(KERN_INFO DEVICE_NAME ": link reset\n");
+       return 0;
+ }
+ static pci_ers_result_t ngene_slot_reset(struct pci_dev *dev)
+ {
+       printk(KERN_INFO DEVICE_NAME ": slot reset\n");
+       return 0;
+ }
+ static void ngene_resume(struct pci_dev *dev)
+ {
+       printk(KERN_INFO DEVICE_NAME ": resume\n");
+ }
++static const struct pci_error_handlers ngene_errors = {
+       .error_detected = ngene_error_detected,
+       .link_reset = ngene_link_reset,
+       .slot_reset = ngene_slot_reset,
+       .resume = ngene_resume,
+ };
+ static struct pci_driver ngene_pci_driver = {
+       .name        = "ngene",
+       .id_table    = ngene_id_tbl,
+       .probe       = ngene_probe,
+       .remove      = __devexit_p(ngene_remove),
+       .err_handler = &ngene_errors,
+       .shutdown    = ngene_shutdown,
+ };
+ static __init int module_init_ngene(void)
+ {
+       printk(KERN_INFO
+              "nGene PCIE bridge driver, Copyright (C) 2005-2007 Micronas\n");
+       return pci_register_driver(&ngene_pci_driver);
+ }
+ static __exit void module_exit_ngene(void)
+ {
+       pci_unregister_driver(&ngene_pci_driver);
+ }
+ module_init(module_init_ngene);
+ module_exit(module_exit_ngene);
+ MODULE_DESCRIPTION("nGene");
+ MODULE_AUTHOR("Micronas, Ralph Metzler, Manfred Voelkel");
+ MODULE_LICENSE("GPL");
index 0000000,196e516..66ac21d
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,2290 +1,2291 @@@
+ /*
+  * omap_vout.c
+  *
+  * Copyright (C) 2005-2010 Texas Instruments.
+  *
+  * This file is licensed under the terms of the GNU General Public License
+  * version 2. This program is licensed "as is" without any warranty of any
+  * kind, whether express or implied.
+  *
+  * Leveraged code from the OMAP2 camera driver
+  * Video-for-Linux (Version 2) camera capture driver for
+  * the OMAP24xx camera controller.
+  *
+  * Author: Andy Lowe (source@mvista.com)
+  *
+  * Copyright (C) 2004 MontaVista Software, Inc.
+  * Copyright (C) 2010 Texas Instruments.
+  *
+  * History:
+  * 20-APR-2006 Khasim         Modified VRFB based Rotation,
+  *                            The image data is always read from 0 degree
+  *                            view and written
+  *                            to the virtual space of desired rotation angle
+  * 4-DEC-2006  Jian           Changed to support better memory management
+  *
+  * 17-Nov-2008 Hardik         Changed driver to use video_ioctl2
+  *
+  * 23-Feb-2010 Vaibhav H      Modified to use new DSS2 interface
+  *
+  */
+ #include <linux/init.h>
+ #include <linux/module.h>
+ #include <linux/vmalloc.h>
+ #include <linux/sched.h>
+ #include <linux/types.h>
+ #include <linux/platform_device.h>
+ #include <linux/irq.h>
+ #include <linux/videodev2.h>
+ #include <linux/dma-mapping.h>
+ #include <linux/slab.h>
+ #include <media/videobuf-dma-contig.h>
+ #include <media/v4l2-device.h>
+ #include <media/v4l2-ioctl.h>
++#include <plat/cpu.h>
+ #include <plat/dma.h>
+ #include <plat/vrfb.h>
+ #include <video/omapdss.h>
+ #include "omap_voutlib.h"
+ #include "omap_voutdef.h"
+ #include "omap_vout_vrfb.h"
+ MODULE_AUTHOR("Texas Instruments");
+ MODULE_DESCRIPTION("OMAP Video for Linux Video out driver");
+ MODULE_LICENSE("GPL");
+ /* Driver Configuration macros */
+ #define VOUT_NAME             "omap_vout"
+ enum omap_vout_channels {
+       OMAP_VIDEO1,
+       OMAP_VIDEO2,
+ };
+ static struct videobuf_queue_ops video_vbq_ops;
+ /* Variables configurable through module params*/
+ static u32 video1_numbuffers = 3;
+ static u32 video2_numbuffers = 3;
+ static u32 video1_bufsize = OMAP_VOUT_MAX_BUF_SIZE;
+ static u32 video2_bufsize = OMAP_VOUT_MAX_BUF_SIZE;
+ static bool vid1_static_vrfb_alloc;
+ static bool vid2_static_vrfb_alloc;
+ static bool debug;
+ /* Module parameters */
+ module_param(video1_numbuffers, uint, S_IRUGO);
+ MODULE_PARM_DESC(video1_numbuffers,
+       "Number of buffers to be allocated at init time for Video1 device.");
+ module_param(video2_numbuffers, uint, S_IRUGO);
+ MODULE_PARM_DESC(video2_numbuffers,
+       "Number of buffers to be allocated at init time for Video2 device.");
+ module_param(video1_bufsize, uint, S_IRUGO);
+ MODULE_PARM_DESC(video1_bufsize,
+       "Size of the buffer to be allocated for video1 device");
+ module_param(video2_bufsize, uint, S_IRUGO);
+ MODULE_PARM_DESC(video2_bufsize,
+       "Size of the buffer to be allocated for video2 device");
+ module_param(vid1_static_vrfb_alloc, bool, S_IRUGO);
+ MODULE_PARM_DESC(vid1_static_vrfb_alloc,
+       "Static allocation of the VRFB buffer for video1 device");
+ module_param(vid2_static_vrfb_alloc, bool, S_IRUGO);
+ MODULE_PARM_DESC(vid2_static_vrfb_alloc,
+       "Static allocation of the VRFB buffer for video2 device");
+ module_param(debug, bool, S_IRUGO);
+ MODULE_PARM_DESC(debug, "Debug level (0-1)");
+ /* list of image formats supported by OMAP2 video pipelines */
+ static const struct v4l2_fmtdesc omap_formats[] = {
+       {
+               /* Note:  V4L2 defines RGB565 as:
+                *
+                *      Byte 0                    Byte 1
+                *      g2 g1 g0 r4 r3 r2 r1 r0   b4 b3 b2 b1 b0 g5 g4 g3
+                *
+                * We interpret RGB565 as:
+                *
+                *      Byte 0                    Byte 1
+                *      g2 g1 g0 b4 b3 b2 b1 b0   r4 r3 r2 r1 r0 g5 g4 g3
+                */
+               .description = "RGB565, le",
+               .pixelformat = V4L2_PIX_FMT_RGB565,
+       },
+       {
+               /* Note:  V4L2 defines RGB32 as: RGB-8-8-8-8  we use
+                *  this for RGB24 unpack mode, the last 8 bits are ignored
+                * */
+               .description = "RGB32, le",
+               .pixelformat = V4L2_PIX_FMT_RGB32,
+       },
+       {
+               /* Note:  V4L2 defines RGB24 as: RGB-8-8-8  we use
+                *        this for RGB24 packed mode
+                *
+                */
+               .description = "RGB24, le",
+               .pixelformat = V4L2_PIX_FMT_RGB24,
+       },
+       {
+               .description = "YUYV (YUV 4:2:2), packed",
+               .pixelformat = V4L2_PIX_FMT_YUYV,
+       },
+       {
+               .description = "UYVY, packed",
+               .pixelformat = V4L2_PIX_FMT_UYVY,
+       },
+ };
+ #define NUM_OUTPUT_FORMATS (ARRAY_SIZE(omap_formats))
+ /*
+  * Try format
+  */
+ static int omap_vout_try_format(struct v4l2_pix_format *pix)
+ {
+       int ifmt, bpp = 0;
+       pix->height = clamp(pix->height, (u32)VID_MIN_HEIGHT,
+                                               (u32)VID_MAX_HEIGHT);
+       pix->width = clamp(pix->width, (u32)VID_MIN_WIDTH, (u32)VID_MAX_WIDTH);
+       for (ifmt = 0; ifmt < NUM_OUTPUT_FORMATS; ifmt++) {
+               if (pix->pixelformat == omap_formats[ifmt].pixelformat)
+                       break;
+       }
+       if (ifmt == NUM_OUTPUT_FORMATS)
+               ifmt = 0;
+       pix->pixelformat = omap_formats[ifmt].pixelformat;
+       pix->field = V4L2_FIELD_ANY;
+       pix->priv = 0;
+       switch (pix->pixelformat) {
+       case V4L2_PIX_FMT_YUYV:
+       case V4L2_PIX_FMT_UYVY:
+       default:
+               pix->colorspace = V4L2_COLORSPACE_JPEG;
+               bpp = YUYV_BPP;
+               break;
+       case V4L2_PIX_FMT_RGB565:
+       case V4L2_PIX_FMT_RGB565X:
+               pix->colorspace = V4L2_COLORSPACE_SRGB;
+               bpp = RGB565_BPP;
+               break;
+       case V4L2_PIX_FMT_RGB24:
+               pix->colorspace = V4L2_COLORSPACE_SRGB;
+               bpp = RGB24_BPP;
+               break;
+       case V4L2_PIX_FMT_RGB32:
+       case V4L2_PIX_FMT_BGR32:
+               pix->colorspace = V4L2_COLORSPACE_SRGB;
+               bpp = RGB32_BPP;
+               break;
+       }
+       pix->bytesperline = pix->width * bpp;
+       pix->sizeimage = pix->bytesperline * pix->height;
+       return bpp;
+ }
+ /*
+  * omap_vout_uservirt_to_phys: This inline function is used to convert user
+  * space virtual address to physical address.
+  */
+ static u32 omap_vout_uservirt_to_phys(u32 virtp)
+ {
+       unsigned long physp = 0;
+       struct vm_area_struct *vma;
+       struct mm_struct *mm = current->mm;
+       vma = find_vma(mm, virtp);
+       /* For kernel direct-mapped memory, take the easy way */
+       if (virtp >= PAGE_OFFSET) {
+               physp = virt_to_phys((void *) virtp);
+       } else if (vma && (vma->vm_flags & VM_IO) && vma->vm_pgoff) {
+               /* this will catch, kernel-allocated, mmaped-to-usermode
+                  addresses */
+               physp = (vma->vm_pgoff << PAGE_SHIFT) + (virtp - vma->vm_start);
+       } else {
+               /* otherwise, use get_user_pages() for general userland pages */
+               int res, nr_pages = 1;
+               struct page *pages;
+               down_read(&current->mm->mmap_sem);
+               res = get_user_pages(current, current->mm, virtp, nr_pages, 1,
+                               0, &pages, NULL);
+               up_read(&current->mm->mmap_sem);
+               if (res == nr_pages) {
+                       physp =  __pa(page_address(&pages[0]) +
+                                       (virtp & ~PAGE_MASK));
+               } else {
+                       printk(KERN_WARNING VOUT_NAME
+                                       "get_user_pages failed\n");
+                       return 0;
+               }
+       }
+       return physp;
+ }
+ /*
+  * Free the V4L2 buffers
+  */
+ void omap_vout_free_buffers(struct omap_vout_device *vout)
+ {
+       int i, numbuffers;
+       /* Allocate memory for the buffers */
+       numbuffers = (vout->vid) ?  video2_numbuffers : video1_numbuffers;
+       vout->buffer_size = (vout->vid) ? video2_bufsize : video1_bufsize;
+       for (i = 0; i < numbuffers; i++) {
+               omap_vout_free_buffer(vout->buf_virt_addr[i],
+                               vout->buffer_size);
+               vout->buf_phy_addr[i] = 0;
+               vout->buf_virt_addr[i] = 0;
+       }
+ }
+ /*
+  * Convert V4L2 rotation to DSS rotation
+  *    V4L2 understand 0, 90, 180, 270.
+  *    Convert to 0, 1, 2 and 3 respectively for DSS
+  */
+ static int v4l2_rot_to_dss_rot(int v4l2_rotation,
+                       enum dss_rotation *rotation, bool mirror)
+ {
+       int ret = 0;
+       switch (v4l2_rotation) {
+       case 90:
+               *rotation = dss_rotation_90_degree;
+               break;
+       case 180:
+               *rotation = dss_rotation_180_degree;
+               break;
+       case 270:
+               *rotation = dss_rotation_270_degree;
+               break;
+       case 0:
+               *rotation = dss_rotation_0_degree;
+               break;
+       default:
+               ret = -EINVAL;
+       }
+       return ret;
+ }
+ static int omap_vout_calculate_offset(struct omap_vout_device *vout)
+ {
+       struct omapvideo_info *ovid;
+       struct v4l2_rect *crop = &vout->crop;
+       struct v4l2_pix_format *pix = &vout->pix;
+       int *cropped_offset = &vout->cropped_offset;
+       int ps = 2, line_length = 0;
+       ovid = &vout->vid_info;
+       if (ovid->rotation_type == VOUT_ROT_VRFB) {
+               omap_vout_calculate_vrfb_offset(vout);
+       } else {
+               vout->line_length = line_length = pix->width;
+               if (V4L2_PIX_FMT_YUYV == pix->pixelformat ||
+                       V4L2_PIX_FMT_UYVY == pix->pixelformat)
+                       ps = 2;
+               else if (V4L2_PIX_FMT_RGB32 == pix->pixelformat)
+                       ps = 4;
+               else if (V4L2_PIX_FMT_RGB24 == pix->pixelformat)
+                       ps = 3;
+               vout->ps = ps;
+               *cropped_offset = (line_length * ps) *
+                       crop->top + crop->left * ps;
+       }
+       v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, "%s Offset:%x\n",
+                       __func__, vout->cropped_offset);
+       return 0;
+ }
+ /*
+  * Convert V4L2 pixel format to DSS pixel format
+  */
+ static int video_mode_to_dss_mode(struct omap_vout_device *vout)
+ {
+       struct omap_overlay *ovl;
+       struct omapvideo_info *ovid;
+       struct v4l2_pix_format *pix = &vout->pix;
+       enum omap_color_mode mode;
+       ovid = &vout->vid_info;
+       ovl = ovid->overlays[0];
+       switch (pix->pixelformat) {
+       case 0:
+               break;
+       case V4L2_PIX_FMT_YUYV:
+               mode = OMAP_DSS_COLOR_YUV2;
+               break;
+       case V4L2_PIX_FMT_UYVY:
+               mode = OMAP_DSS_COLOR_UYVY;
+               break;
+       case V4L2_PIX_FMT_RGB565:
+               mode = OMAP_DSS_COLOR_RGB16;
+               break;
+       case V4L2_PIX_FMT_RGB24:
+               mode = OMAP_DSS_COLOR_RGB24P;
+               break;
+       case V4L2_PIX_FMT_RGB32:
+               mode = (ovl->id == OMAP_DSS_VIDEO1) ?
+                       OMAP_DSS_COLOR_RGB24U : OMAP_DSS_COLOR_ARGB32;
+               break;
+       case V4L2_PIX_FMT_BGR32:
+               mode = OMAP_DSS_COLOR_RGBX32;
+               break;
+       default:
+               mode = -EINVAL;
+       }
+       return mode;
+ }
+ /*
+  * Setup the overlay
+  */
+ static int omapvid_setup_overlay(struct omap_vout_device *vout,
+               struct omap_overlay *ovl, int posx, int posy, int outw,
+               int outh, u32 addr)
+ {
+       int ret = 0;
+       struct omap_overlay_info info;
+       int cropheight, cropwidth, pixheight, pixwidth;
+       if ((ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0 &&
+                       (outw != vout->pix.width || outh != vout->pix.height)) {
+               ret = -EINVAL;
+               goto setup_ovl_err;
+       }
+       vout->dss_mode = video_mode_to_dss_mode(vout);
+       if (vout->dss_mode == -EINVAL) {
+               ret = -EINVAL;
+               goto setup_ovl_err;
+       }
+       /* Setup the input plane parameters according to
+        * rotation value selected.
+        */
+       if (is_rotation_90_or_270(vout)) {
+               cropheight = vout->crop.width;
+               cropwidth = vout->crop.height;
+               pixheight = vout->pix.width;
+               pixwidth = vout->pix.height;
+       } else {
+               cropheight = vout->crop.height;
+               cropwidth = vout->crop.width;
+               pixheight = vout->pix.height;
+               pixwidth = vout->pix.width;
+       }
+       ovl->get_overlay_info(ovl, &info);
+       info.paddr = addr;
+       info.width = cropwidth;
+       info.height = cropheight;
+       info.color_mode = vout->dss_mode;
+       info.mirror = vout->mirror;
+       info.pos_x = posx;
+       info.pos_y = posy;
+       info.out_width = outw;
+       info.out_height = outh;
+       info.global_alpha = vout->win.global_alpha;
+       if (!is_rotation_enabled(vout)) {
+               info.rotation = 0;
+               info.rotation_type = OMAP_DSS_ROT_DMA;
+               info.screen_width = pixwidth;
+       } else {
+               info.rotation = vout->rotation;
+               info.rotation_type = OMAP_DSS_ROT_VRFB;
+               info.screen_width = 2048;
+       }
+       v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev,
+               "%s enable=%d addr=%x width=%d\n height=%d color_mode=%d\n"
+               "rotation=%d mirror=%d posx=%d posy=%d out_width = %d \n"
+               "out_height=%d rotation_type=%d screen_width=%d\n",
+               __func__, ovl->is_enabled(ovl), info.paddr, info.width, info.height,
+               info.color_mode, info.rotation, info.mirror, info.pos_x,
+               info.pos_y, info.out_width, info.out_height, info.rotation_type,
+               info.screen_width);
+       ret = ovl->set_overlay_info(ovl, &info);
+       if (ret)
+               goto setup_ovl_err;
+       return 0;
+ setup_ovl_err:
+       v4l2_warn(&vout->vid_dev->v4l2_dev, "setup_overlay failed\n");
+       return ret;
+ }
+ /*
+  * Initialize the overlay structure
+  */
+ static int omapvid_init(struct omap_vout_device *vout, u32 addr)
+ {
+       int ret = 0, i;
+       struct v4l2_window *win;
+       struct omap_overlay *ovl;
+       int posx, posy, outw, outh, temp;
+       struct omap_video_timings *timing;
+       struct omapvideo_info *ovid = &vout->vid_info;
+       win = &vout->win;
+       for (i = 0; i < ovid->num_overlays; i++) {
+               ovl = ovid->overlays[i];
+               if (!ovl->manager || !ovl->manager->device)
+                       return -EINVAL;
+               timing = &ovl->manager->device->panel.timings;
+               outw = win->w.width;
+               outh = win->w.height;
+               switch (vout->rotation) {
+               case dss_rotation_90_degree:
+                       /* Invert the height and width for 90
+                        * and 270 degree rotation
+                        */
+                       temp = outw;
+                       outw = outh;
+                       outh = temp;
+                       posy = (timing->y_res - win->w.width) - win->w.left;
+                       posx = win->w.top;
+                       break;
+               case dss_rotation_180_degree:
+                       posx = (timing->x_res - win->w.width) - win->w.left;
+                       posy = (timing->y_res - win->w.height) - win->w.top;
+                       break;
+               case dss_rotation_270_degree:
+                       temp = outw;
+                       outw = outh;
+                       outh = temp;
+                       posy = win->w.left;
+                       posx = (timing->x_res - win->w.height) - win->w.top;
+                       break;
+               default:
+                       posx = win->w.left;
+                       posy = win->w.top;
+                       break;
+               }
+               ret = omapvid_setup_overlay(vout, ovl, posx, posy,
+                               outw, outh, addr);
+               if (ret)
+                       goto omapvid_init_err;
+       }
+       return 0;
+ omapvid_init_err:
+       v4l2_warn(&vout->vid_dev->v4l2_dev, "apply_changes failed\n");
+       return ret;
+ }
+ /*
+  * Apply the changes set the go bit of DSS
+  */
+ static int omapvid_apply_changes(struct omap_vout_device *vout)
+ {
+       int i;
+       struct omap_overlay *ovl;
+       struct omapvideo_info *ovid = &vout->vid_info;
+       for (i = 0; i < ovid->num_overlays; i++) {
+               ovl = ovid->overlays[i];
+               if (!ovl->manager || !ovl->manager->device)
+                       return -EINVAL;
+               ovl->manager->apply(ovl->manager);
+       }
+       return 0;
+ }
+ static int omapvid_handle_interlace_display(struct omap_vout_device *vout,
+               unsigned int irqstatus, struct timeval timevalue)
+ {
+       u32 fid;
+       if (vout->first_int) {
+               vout->first_int = 0;
+               goto err;
+       }
+       if (irqstatus & DISPC_IRQ_EVSYNC_ODD)
+               fid = 1;
+       else if (irqstatus & DISPC_IRQ_EVSYNC_EVEN)
+               fid = 0;
+       else
+               goto err;
+       vout->field_id ^= 1;
+       if (fid != vout->field_id) {
+               if (fid == 0)
+                       vout->field_id = fid;
+       } else if (0 == fid) {
+               if (vout->cur_frm == vout->next_frm)
+                       goto err;
+               vout->cur_frm->ts = timevalue;
+               vout->cur_frm->state = VIDEOBUF_DONE;
+               wake_up_interruptible(&vout->cur_frm->done);
+               vout->cur_frm = vout->next_frm;
+       } else {
+               if (list_empty(&vout->dma_queue) ||
+                               (vout->cur_frm != vout->next_frm))
+                       goto err;
+       }
+       return vout->field_id;
+ err:
+       return 0;
+ }
+ static void omap_vout_isr(void *arg, unsigned int irqstatus)
+ {
+       int ret, fid, mgr_id;
+       u32 addr, irq;
+       struct omap_overlay *ovl;
+       struct timeval timevalue;
+       struct omapvideo_info *ovid;
+       struct omap_dss_device *cur_display;
+       struct omap_vout_device *vout = (struct omap_vout_device *)arg;
+       if (!vout->streaming)
+               return;
+       ovid = &vout->vid_info;
+       ovl = ovid->overlays[0];
+       /* get the display device attached to the overlay */
+       if (!ovl->manager || !ovl->manager->device)
+               return;
+       mgr_id = ovl->manager->id;
+       cur_display = ovl->manager->device;
+       spin_lock(&vout->vbq_lock);
+       do_gettimeofday(&timevalue);
+       switch (cur_display->type) {
+       case OMAP_DISPLAY_TYPE_DSI:
+       case OMAP_DISPLAY_TYPE_DPI:
+               if (mgr_id == OMAP_DSS_CHANNEL_LCD)
+                       irq = DISPC_IRQ_VSYNC;
+               else if (mgr_id == OMAP_DSS_CHANNEL_LCD2)
+                       irq = DISPC_IRQ_VSYNC2;
+               else
+                       goto vout_isr_err;
+               if (!(irqstatus & irq))
+                       goto vout_isr_err;
+               break;
+       case OMAP_DISPLAY_TYPE_VENC:
+               fid = omapvid_handle_interlace_display(vout, irqstatus,
+                               timevalue);
+               if (!fid)
+                       goto vout_isr_err;
+               break;
+       case OMAP_DISPLAY_TYPE_HDMI:
+               if (!(irqstatus & DISPC_IRQ_EVSYNC_EVEN))
+                       goto vout_isr_err;
+               break;
+       default:
+               goto vout_isr_err;
+       }
+       if (!vout->first_int && (vout->cur_frm != vout->next_frm)) {
+               vout->cur_frm->ts = timevalue;
+               vout->cur_frm->state = VIDEOBUF_DONE;
+               wake_up_interruptible(&vout->cur_frm->done);
+               vout->cur_frm = vout->next_frm;
+       }
+       vout->first_int = 0;
+       if (list_empty(&vout->dma_queue))
+               goto vout_isr_err;
+       vout->next_frm = list_entry(vout->dma_queue.next,
+                       struct videobuf_buffer, queue);
+       list_del(&vout->next_frm->queue);
+       vout->next_frm->state = VIDEOBUF_ACTIVE;
+       addr = (unsigned long) vout->queued_buf_addr[vout->next_frm->i]
+               + vout->cropped_offset;
+       /* First save the configuration in ovelray structure */
+       ret = omapvid_init(vout, addr);
+       if (ret)
+               printk(KERN_ERR VOUT_NAME
+                       "failed to set overlay info\n");
+       /* Enable the pipeline and set the Go bit */
+       ret = omapvid_apply_changes(vout);
+       if (ret)
+               printk(KERN_ERR VOUT_NAME "failed to change mode\n");
+ vout_isr_err:
+       spin_unlock(&vout->vbq_lock);
+ }
+ /* Video buffer call backs */
+ /*
+  * Buffer setup function is called by videobuf layer when REQBUF ioctl is
+  * called. This is used to setup buffers and return size and count of
+  * buffers allocated. After the call to this buffer, videobuf layer will
+  * setup buffer queue depending on the size and count of buffers
+  */
+ static int omap_vout_buffer_setup(struct videobuf_queue *q, unsigned int *count,
+                         unsigned int *size)
+ {
+       int startindex = 0, i, j;
+       u32 phy_addr = 0, virt_addr = 0;
+       struct omap_vout_device *vout = q->priv_data;
+       struct omapvideo_info *ovid = &vout->vid_info;
+       int vid_max_buf_size;
+       if (!vout)
+               return -EINVAL;
+       vid_max_buf_size = vout->vid == OMAP_VIDEO1 ? video1_bufsize :
+               video2_bufsize;
+       if (V4L2_BUF_TYPE_VIDEO_OUTPUT != q->type)
+               return -EINVAL;
+       startindex = (vout->vid == OMAP_VIDEO1) ?
+               video1_numbuffers : video2_numbuffers;
+       if (V4L2_MEMORY_MMAP == vout->memory && *count < startindex)
+               *count = startindex;
+       if (ovid->rotation_type == VOUT_ROT_VRFB) {
+               if (omap_vout_vrfb_buffer_setup(vout, count, startindex))
+                       return -ENOMEM;
+       }
+       if (V4L2_MEMORY_MMAP != vout->memory)
+               return 0;
+       /* Now allocated the V4L2 buffers */
+       *size = PAGE_ALIGN(vout->pix.width * vout->pix.height * vout->bpp);
+       startindex = (vout->vid == OMAP_VIDEO1) ?
+               video1_numbuffers : video2_numbuffers;
+       /* Check the size of the buffer */
+       if (*size > vid_max_buf_size) {
+               v4l2_err(&vout->vid_dev->v4l2_dev,
+                               "buffer allocation mismatch [%u] [%u]\n",
+                               *size, vout->buffer_size);
+               return -ENOMEM;
+       }
+       for (i = startindex; i < *count; i++) {
+               vout->buffer_size = *size;
+               virt_addr = omap_vout_alloc_buffer(vout->buffer_size,
+                               &phy_addr);
+               if (!virt_addr) {
+                       if (ovid->rotation_type == VOUT_ROT_NONE) {
+                               break;
+                       } else {
+                               if (!is_rotation_enabled(vout))
+                                       break;
+                       /* Free the VRFB buffers if no space for V4L2 buffers */
+                       for (j = i; j < *count; j++) {
+                               omap_vout_free_buffer(
+                                               vout->smsshado_virt_addr[j],
+                                               vout->smsshado_size);
+                               vout->smsshado_virt_addr[j] = 0;
+                               vout->smsshado_phy_addr[j] = 0;
+                               }
+                       }
+               }
+               vout->buf_virt_addr[i] = virt_addr;
+               vout->buf_phy_addr[i] = phy_addr;
+       }
+       *count = vout->buffer_allocated = i;
+       return 0;
+ }
+ /*
+  * Free the V4L2 buffers additionally allocated than default
+  * number of buffers
+  */
+ static void omap_vout_free_extra_buffers(struct omap_vout_device *vout)
+ {
+       int num_buffers = 0, i;
+       num_buffers = (vout->vid == OMAP_VIDEO1) ?
+               video1_numbuffers : video2_numbuffers;
+       for (i = num_buffers; i < vout->buffer_allocated; i++) {
+               if (vout->buf_virt_addr[i])
+                       omap_vout_free_buffer(vout->buf_virt_addr[i],
+                                       vout->buffer_size);
+               vout->buf_virt_addr[i] = 0;
+               vout->buf_phy_addr[i] = 0;
+       }
+       vout->buffer_allocated = num_buffers;
+ }
+ /*
+  * This function will be called when VIDIOC_QBUF ioctl is called.
+  * It prepare buffers before give out for the display. This function
+  * converts user space virtual address into physical address if userptr memory
+  * exchange mechanism is used. If rotation is enabled, it copies entire
+  * buffer into VRFB memory space before giving it to the DSS.
+  */
+ static int omap_vout_buffer_prepare(struct videobuf_queue *q,
+                       struct videobuf_buffer *vb,
+                       enum v4l2_field field)
+ {
+       struct omap_vout_device *vout = q->priv_data;
+       struct omapvideo_info *ovid = &vout->vid_info;
+       if (VIDEOBUF_NEEDS_INIT == vb->state) {
+               vb->width = vout->pix.width;
+               vb->height = vout->pix.height;
+               vb->size = vb->width * vb->height * vout->bpp;
+               vb->field = field;
+       }
+       vb->state = VIDEOBUF_PREPARED;
+       /* if user pointer memory mechanism is used, get the physical
+        * address of the buffer
+        */
+       if (V4L2_MEMORY_USERPTR == vb->memory) {
+               if (0 == vb->baddr)
+                       return -EINVAL;
+               /* Physical address */
+               vout->queued_buf_addr[vb->i] = (u8 *)
+                       omap_vout_uservirt_to_phys(vb->baddr);
+       } else {
+               u32 addr, dma_addr;
+               unsigned long size;
+               addr = (unsigned long) vout->buf_virt_addr[vb->i];
+               size = (unsigned long) vb->size;
+               dma_addr = dma_map_single(vout->vid_dev->v4l2_dev.dev, (void *) addr,
+                               size, DMA_TO_DEVICE);
+               if (dma_mapping_error(vout->vid_dev->v4l2_dev.dev, dma_addr))
+                       v4l2_err(&vout->vid_dev->v4l2_dev, "dma_map_single failed\n");
+               vout->queued_buf_addr[vb->i] = (u8 *)vout->buf_phy_addr[vb->i];
+       }
+       if (ovid->rotation_type == VOUT_ROT_VRFB)
+               return omap_vout_prepare_vrfb(vout, vb);
+       else
+               return 0;
+ }
+ /*
+  * Buffer queue function will be called from the videobuf layer when _QBUF
+  * ioctl is called. It is used to enqueue buffer, which is ready to be
+  * displayed.
+  */
+ static void omap_vout_buffer_queue(struct videobuf_queue *q,
+                         struct videobuf_buffer *vb)
+ {
+       struct omap_vout_device *vout = q->priv_data;
+       /* Driver is also maintainig a queue. So enqueue buffer in the driver
+        * queue */
+       list_add_tail(&vb->queue, &vout->dma_queue);
+       vb->state = VIDEOBUF_QUEUED;
+ }
+ /*
+  * Buffer release function is called from videobuf layer to release buffer
+  * which are already allocated
+  */
+ static void omap_vout_buffer_release(struct videobuf_queue *q,
+                           struct videobuf_buffer *vb)
+ {
+       struct omap_vout_device *vout = q->priv_data;
+       vb->state = VIDEOBUF_NEEDS_INIT;
+       if (V4L2_MEMORY_MMAP != vout->memory)
+               return;
+ }
+ /*
+  *  File operations
+  */
+ static unsigned int omap_vout_poll(struct file *file,
+                                  struct poll_table_struct *wait)
+ {
+       struct omap_vout_device *vout = file->private_data;
+       struct videobuf_queue *q = &vout->vbq;
+       return videobuf_poll_stream(file, q, wait);
+ }
+ static void omap_vout_vm_open(struct vm_area_struct *vma)
+ {
+       struct omap_vout_device *vout = vma->vm_private_data;
+       v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev,
+               "vm_open [vma=%08lx-%08lx]\n", vma->vm_start, vma->vm_end);
+       vout->mmap_count++;
+ }
+ static void omap_vout_vm_close(struct vm_area_struct *vma)
+ {
+       struct omap_vout_device *vout = vma->vm_private_data;
+       v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev,
+               "vm_close [vma=%08lx-%08lx]\n", vma->vm_start, vma->vm_end);
+       vout->mmap_count--;
+ }
+ static struct vm_operations_struct omap_vout_vm_ops = {
+       .open   = omap_vout_vm_open,
+       .close  = omap_vout_vm_close,
+ };
+ static int omap_vout_mmap(struct file *file, struct vm_area_struct *vma)
+ {
+       int i;
+       void *pos;
+       unsigned long start = vma->vm_start;
+       unsigned long size = (vma->vm_end - vma->vm_start);
+       struct omap_vout_device *vout = file->private_data;
+       struct videobuf_queue *q = &vout->vbq;
+       v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev,
+                       " %s pgoff=0x%lx, start=0x%lx, end=0x%lx\n", __func__,
+                       vma->vm_pgoff, vma->vm_start, vma->vm_end);
+       /* look for the buffer to map */
+       for (i = 0; i < VIDEO_MAX_FRAME; i++) {
+               if (NULL == q->bufs[i])
+                       continue;
+               if (V4L2_MEMORY_MMAP != q->bufs[i]->memory)
+                       continue;
+               if (q->bufs[i]->boff == (vma->vm_pgoff << PAGE_SHIFT))
+                       break;
+       }
+       if (VIDEO_MAX_FRAME == i) {
+               v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev,
+                               "offset invalid [offset=0x%lx]\n",
+                               (vma->vm_pgoff << PAGE_SHIFT));
+               return -EINVAL;
+       }
+       /* Check the size of the buffer */
+       if (size > vout->buffer_size) {
+               v4l2_err(&vout->vid_dev->v4l2_dev,
+                               "insufficient memory [%lu] [%u]\n",
+                               size, vout->buffer_size);
+               return -ENOMEM;
+       }
+       q->bufs[i]->baddr = vma->vm_start;
+       vma->vm_flags |= VM_RESERVED;
+       vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+       vma->vm_ops = &omap_vout_vm_ops;
+       vma->vm_private_data = (void *) vout;
+       pos = (void *)vout->buf_virt_addr[i];
+       vma->vm_pgoff = virt_to_phys((void *)pos) >> PAGE_SHIFT;
+       while (size > 0) {
+               unsigned long pfn;
+               pfn = virt_to_phys((void *) pos) >> PAGE_SHIFT;
+               if (remap_pfn_range(vma, start, pfn, PAGE_SIZE, PAGE_SHARED))
+                       return -EAGAIN;
+               start += PAGE_SIZE;
+               pos += PAGE_SIZE;
+               size -= PAGE_SIZE;
+       }
+       vout->mmap_count++;
+       v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, "Exiting %s\n", __func__);
+       return 0;
+ }
+ static int omap_vout_release(struct file *file)
+ {
+       unsigned int ret, i;
+       struct videobuf_queue *q;
+       struct omapvideo_info *ovid;
+       struct omap_vout_device *vout = file->private_data;
+       v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, "Entering %s\n", __func__);
+       ovid = &vout->vid_info;
+       if (!vout)
+               return 0;
+       q = &vout->vbq;
+       /* Disable all the overlay managers connected with this interface */
+       for (i = 0; i < ovid->num_overlays; i++) {
+               struct omap_overlay *ovl = ovid->overlays[i];
+               if (ovl->manager && ovl->manager->device)
+                       ovl->disable(ovl);
+       }
+       /* Turn off the pipeline */
+       ret = omapvid_apply_changes(vout);
+       if (ret)
+               v4l2_warn(&vout->vid_dev->v4l2_dev,
+                               "Unable to apply changes\n");
+       /* Free all buffers */
+       omap_vout_free_extra_buffers(vout);
+       /* Free the VRFB buffers only if they are allocated
+        * during reqbufs.  Don't free if init time allocated
+        */
+       if (ovid->rotation_type == VOUT_ROT_VRFB) {
+               if (!vout->vrfb_static_allocation)
+                       omap_vout_free_vrfb_buffers(vout);
+       }
+       videobuf_mmap_free(q);
+       /* Even if apply changes fails we should continue
+          freeing allocated memory */
+       if (vout->streaming) {
+               u32 mask = 0;
+               mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_EVEN |
+                       DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_VSYNC2;
+               omap_dispc_unregister_isr(omap_vout_isr, vout, mask);
+               vout->streaming = 0;
+               videobuf_streamoff(q);
+               videobuf_queue_cancel(q);
+       }
+       if (vout->mmap_count != 0)
+               vout->mmap_count = 0;
+       vout->opened -= 1;
+       file->private_data = NULL;
+       if (vout->buffer_allocated)
+               videobuf_mmap_free(q);
+       v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, "Exiting %s\n", __func__);
+       return ret;
+ }
+ static int omap_vout_open(struct file *file)
+ {
+       struct videobuf_queue *q;
+       struct omap_vout_device *vout = NULL;
+       vout = video_drvdata(file);
+       v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, "Entering %s\n", __func__);
+       if (vout == NULL)
+               return -ENODEV;
+       /* for now, we only support single open */
+       if (vout->opened)
+               return -EBUSY;
+       vout->opened += 1;
+       file->private_data = vout;
+       vout->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+       q = &vout->vbq;
+       video_vbq_ops.buf_setup = omap_vout_buffer_setup;
+       video_vbq_ops.buf_prepare = omap_vout_buffer_prepare;
+       video_vbq_ops.buf_release = omap_vout_buffer_release;
+       video_vbq_ops.buf_queue = omap_vout_buffer_queue;
+       spin_lock_init(&vout->vbq_lock);
+       videobuf_queue_dma_contig_init(q, &video_vbq_ops, q->dev,
+                       &vout->vbq_lock, vout->type, V4L2_FIELD_NONE,
+                       sizeof(struct videobuf_buffer), vout, NULL);
+       v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, "Exiting %s\n", __func__);
+       return 0;
+ }
+ /*
+  * V4L2 ioctls
+  */
+ static int vidioc_querycap(struct file *file, void *fh,
+               struct v4l2_capability *cap)
+ {
+       struct omap_vout_device *vout = fh;
+       strlcpy(cap->driver, VOUT_NAME, sizeof(cap->driver));
+       strlcpy(cap->card, vout->vfd->name, sizeof(cap->card));
+       cap->bus_info[0] = '\0';
+       cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_OUTPUT |
+               V4L2_CAP_VIDEO_OUTPUT_OVERLAY;
+       return 0;
+ }
+ static int vidioc_enum_fmt_vid_out(struct file *file, void *fh,
+                       struct v4l2_fmtdesc *fmt)
+ {
+       int index = fmt->index;
+       if (index >= NUM_OUTPUT_FORMATS)
+               return -EINVAL;
+       fmt->flags = omap_formats[index].flags;
+       strlcpy(fmt->description, omap_formats[index].description,
+                       sizeof(fmt->description));
+       fmt->pixelformat = omap_formats[index].pixelformat;
+       return 0;
+ }
+ static int vidioc_g_fmt_vid_out(struct file *file, void *fh,
+                       struct v4l2_format *f)
+ {
+       struct omap_vout_device *vout = fh;
+       f->fmt.pix = vout->pix;
+       return 0;
+ }
+ static int vidioc_try_fmt_vid_out(struct file *file, void *fh,
+                       struct v4l2_format *f)
+ {
+       struct omap_overlay *ovl;
+       struct omapvideo_info *ovid;
+       struct omap_video_timings *timing;
+       struct omap_vout_device *vout = fh;
+       ovid = &vout->vid_info;
+       ovl = ovid->overlays[0];
+       if (!ovl->manager || !ovl->manager->device)
+               return -EINVAL;
+       /* get the display device attached to the overlay */
+       timing = &ovl->manager->device->panel.timings;
+       vout->fbuf.fmt.height = timing->y_res;
+       vout->fbuf.fmt.width = timing->x_res;
+       omap_vout_try_format(&f->fmt.pix);
+       return 0;
+ }
+ static int vidioc_s_fmt_vid_out(struct file *file, void *fh,
+                       struct v4l2_format *f)
+ {
+       int ret, bpp;
+       struct omap_overlay *ovl;
+       struct omapvideo_info *ovid;
+       struct omap_video_timings *timing;
+       struct omap_vout_device *vout = fh;
+       if (vout->streaming)
+               return -EBUSY;
+       mutex_lock(&vout->lock);
+       ovid = &vout->vid_info;
+       ovl = ovid->overlays[0];
+       /* get the display device attached to the overlay */
+       if (!ovl->manager || !ovl->manager->device) {
+               ret = -EINVAL;
+               goto s_fmt_vid_out_exit;
+       }
+       timing = &ovl->manager->device->panel.timings;
+       /* We dont support RGB24-packed mode if vrfb rotation
+        * is enabled*/
+       if ((is_rotation_enabled(vout)) &&
+                       f->fmt.pix.pixelformat == V4L2_PIX_FMT_RGB24) {
+               ret = -EINVAL;
+               goto s_fmt_vid_out_exit;
+       }
+       /* get the framebuffer parameters */
+       if (is_rotation_90_or_270(vout)) {
+               vout->fbuf.fmt.height = timing->x_res;
+               vout->fbuf.fmt.width = timing->y_res;
+       } else {
+               vout->fbuf.fmt.height = timing->y_res;
+               vout->fbuf.fmt.width = timing->x_res;
+       }
+       /* change to samller size is OK */
+       bpp = omap_vout_try_format(&f->fmt.pix);
+       f->fmt.pix.sizeimage = f->fmt.pix.width * f->fmt.pix.height * bpp;
+       /* try & set the new output format */
+       vout->bpp = bpp;
+       vout->pix = f->fmt.pix;
+       vout->vrfb_bpp = 1;
+       /* If YUYV then vrfb bpp is 2, for  others its 1 */
+       if (V4L2_PIX_FMT_YUYV == vout->pix.pixelformat ||
+                       V4L2_PIX_FMT_UYVY == vout->pix.pixelformat)
+               vout->vrfb_bpp = 2;
+       /* set default crop and win */
+       omap_vout_new_format(&vout->pix, &vout->fbuf, &vout->crop, &vout->win);
+       /* Save the changes in the overlay strcuture */
+       ret = omapvid_init(vout, 0);
+       if (ret) {
+               v4l2_err(&vout->vid_dev->v4l2_dev, "failed to change mode\n");
+               goto s_fmt_vid_out_exit;
+       }
+       ret = 0;
+ s_fmt_vid_out_exit:
+       mutex_unlock(&vout->lock);
+       return ret;
+ }
+ static int vidioc_try_fmt_vid_overlay(struct file *file, void *fh,
+                       struct v4l2_format *f)
+ {
+       int ret = 0;
+       struct omap_vout_device *vout = fh;
+       struct omap_overlay *ovl;
+       struct omapvideo_info *ovid;
+       struct v4l2_window *win = &f->fmt.win;
+       ovid = &vout->vid_info;
+       ovl = ovid->overlays[0];
+       ret = omap_vout_try_window(&vout->fbuf, win);
+       if (!ret) {
+               if ((ovl->caps & OMAP_DSS_OVL_CAP_GLOBAL_ALPHA) == 0)
+                       win->global_alpha = 255;
+               else
+                       win->global_alpha = f->fmt.win.global_alpha;
+       }
+       return ret;
+ }
+ static int vidioc_s_fmt_vid_overlay(struct file *file, void *fh,
+                       struct v4l2_format *f)
+ {
+       int ret = 0;
+       struct omap_overlay *ovl;
+       struct omapvideo_info *ovid;
+       struct omap_vout_device *vout = fh;
+       struct v4l2_window *win = &f->fmt.win;
+       mutex_lock(&vout->lock);
+       ovid = &vout->vid_info;
+       ovl = ovid->overlays[0];
+       ret = omap_vout_new_window(&vout->crop, &vout->win, &vout->fbuf, win);
+       if (!ret) {
+               /* Video1 plane does not support global alpha on OMAP3 */
+               if ((ovl->caps & OMAP_DSS_OVL_CAP_GLOBAL_ALPHA) == 0)
+                       vout->win.global_alpha = 255;
+               else
+                       vout->win.global_alpha = f->fmt.win.global_alpha;
+               vout->win.chromakey = f->fmt.win.chromakey;
+       }
+       mutex_unlock(&vout->lock);
+       return ret;
+ }
+ static int vidioc_enum_fmt_vid_overlay(struct file *file, void *fh,
+                       struct v4l2_fmtdesc *fmt)
+ {
+       int index = fmt->index;
+       if (index >= NUM_OUTPUT_FORMATS)
+               return -EINVAL;
+       fmt->flags = omap_formats[index].flags;
+       strlcpy(fmt->description, omap_formats[index].description,
+                       sizeof(fmt->description));
+       fmt->pixelformat = omap_formats[index].pixelformat;
+       return 0;
+ }
+ static int vidioc_g_fmt_vid_overlay(struct file *file, void *fh,
+                       struct v4l2_format *f)
+ {
+       u32 key_value =  0;
+       struct omap_overlay *ovl;
+       struct omapvideo_info *ovid;
+       struct omap_vout_device *vout = fh;
+       struct omap_overlay_manager_info info;
+       struct v4l2_window *win = &f->fmt.win;
+       ovid = &vout->vid_info;
+       ovl = ovid->overlays[0];
+       win->w = vout->win.w;
+       win->field = vout->win.field;
+       win->global_alpha = vout->win.global_alpha;
+       if (ovl->manager && ovl->manager->get_manager_info) {
+               ovl->manager->get_manager_info(ovl->manager, &info);
+               key_value = info.trans_key;
+       }
+       win->chromakey = key_value;
+       return 0;
+ }
+ static int vidioc_cropcap(struct file *file, void *fh,
+               struct v4l2_cropcap *cropcap)
+ {
+       struct omap_vout_device *vout = fh;
+       struct v4l2_pix_format *pix = &vout->pix;
+       if (cropcap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+               return -EINVAL;
+       /* Width and height are always even */
+       cropcap->bounds.width = pix->width & ~1;
+       cropcap->bounds.height = pix->height & ~1;
+       omap_vout_default_crop(&vout->pix, &vout->fbuf, &cropcap->defrect);
+       cropcap->pixelaspect.numerator = 1;
+       cropcap->pixelaspect.denominator = 1;
+       return 0;
+ }
+ static int vidioc_g_crop(struct file *file, void *fh, struct v4l2_crop *crop)
+ {
+       struct omap_vout_device *vout = fh;
+       if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+               return -EINVAL;
+       crop->c = vout->crop;
+       return 0;
+ }
+ static int vidioc_s_crop(struct file *file, void *fh, const struct v4l2_crop *crop)
+ {
+       int ret = -EINVAL;
+       struct omap_vout_device *vout = fh;
+       struct omapvideo_info *ovid;
+       struct omap_overlay *ovl;
+       struct omap_video_timings *timing;
+       if (vout->streaming)
+               return -EBUSY;
+       mutex_lock(&vout->lock);
+       ovid = &vout->vid_info;
+       ovl = ovid->overlays[0];
+       if (!ovl->manager || !ovl->manager->device) {
+               ret = -EINVAL;
+               goto s_crop_err;
+       }
+       /* get the display device attached to the overlay */
+       timing = &ovl->manager->device->panel.timings;
+       if (is_rotation_90_or_270(vout)) {
+               vout->fbuf.fmt.height = timing->x_res;
+               vout->fbuf.fmt.width = timing->y_res;
+       } else {
+               vout->fbuf.fmt.height = timing->y_res;
+               vout->fbuf.fmt.width = timing->x_res;
+       }
+       if (crop->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+               ret = omap_vout_new_crop(&vout->pix, &vout->crop, &vout->win,
+                               &vout->fbuf, &crop->c);
+ s_crop_err:
+       mutex_unlock(&vout->lock);
+       return ret;
+ }
+ static int vidioc_queryctrl(struct file *file, void *fh,
+               struct v4l2_queryctrl *ctrl)
+ {
+       int ret = 0;
+       switch (ctrl->id) {
+       case V4L2_CID_ROTATE:
+               ret = v4l2_ctrl_query_fill(ctrl, 0, 270, 90, 0);
+               break;
+       case V4L2_CID_BG_COLOR:
+               ret = v4l2_ctrl_query_fill(ctrl, 0, 0xFFFFFF, 1, 0);
+               break;
+       case V4L2_CID_VFLIP:
+               ret = v4l2_ctrl_query_fill(ctrl, 0, 1, 1, 0);
+               break;
+       default:
+               ctrl->name[0] = '\0';
+               ret = -EINVAL;
+       }
+       return ret;
+ }
+ static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *ctrl)
+ {
+       int ret = 0;
+       struct omap_vout_device *vout = fh;
+       switch (ctrl->id) {
+       case V4L2_CID_ROTATE:
+               ctrl->value = vout->control[0].value;
+               break;
+       case V4L2_CID_BG_COLOR:
+       {
+               struct omap_overlay_manager_info info;
+               struct omap_overlay *ovl;
+               ovl = vout->vid_info.overlays[0];
+               if (!ovl->manager || !ovl->manager->get_manager_info) {
+                       ret = -EINVAL;
+                       break;
+               }
+               ovl->manager->get_manager_info(ovl->manager, &info);
+               ctrl->value = info.default_color;
+               break;
+       }
+       case V4L2_CID_VFLIP:
+               ctrl->value = vout->control[2].value;
+               break;
+       default:
+               ret = -EINVAL;
+       }
+       return ret;
+ }
+ static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *a)
+ {
+       int ret = 0;
+       struct omap_vout_device *vout = fh;
+       switch (a->id) {
+       case V4L2_CID_ROTATE:
+       {
+               struct omapvideo_info *ovid;
+               int rotation = a->value;
+               ovid = &vout->vid_info;
+               mutex_lock(&vout->lock);
+               if (rotation && ovid->rotation_type == VOUT_ROT_NONE) {
+                       mutex_unlock(&vout->lock);
+                       ret = -ERANGE;
+                       break;
+               }
+               if (rotation && vout->pix.pixelformat == V4L2_PIX_FMT_RGB24) {
+                       mutex_unlock(&vout->lock);
+                       ret = -EINVAL;
+                       break;
+               }
+               if (v4l2_rot_to_dss_rot(rotation, &vout->rotation,
+                                                       vout->mirror)) {
+                       mutex_unlock(&vout->lock);
+                       ret = -EINVAL;
+                       break;
+               }
+               vout->control[0].value = rotation;
+               mutex_unlock(&vout->lock);
+               break;
+       }
+       case V4L2_CID_BG_COLOR:
+       {
+               struct omap_overlay *ovl;
+               unsigned int  color = a->value;
+               struct omap_overlay_manager_info info;
+               ovl = vout->vid_info.overlays[0];
+               mutex_lock(&vout->lock);
+               if (!ovl->manager || !ovl->manager->get_manager_info) {
+                       mutex_unlock(&vout->lock);
+                       ret = -EINVAL;
+                       break;
+               }
+               ovl->manager->get_manager_info(ovl->manager, &info);
+               info.default_color = color;
+               if (ovl->manager->set_manager_info(ovl->manager, &info)) {
+                       mutex_unlock(&vout->lock);
+                       ret = -EINVAL;
+                       break;
+               }
+               vout->control[1].value = color;
+               mutex_unlock(&vout->lock);
+               break;
+       }
+       case V4L2_CID_VFLIP:
+       {
+               struct omap_overlay *ovl;
+               struct omapvideo_info *ovid;
+               unsigned int  mirror = a->value;
+               ovid = &vout->vid_info;
+               ovl = ovid->overlays[0];
+               mutex_lock(&vout->lock);
+               if (mirror && ovid->rotation_type == VOUT_ROT_NONE) {
+                       mutex_unlock(&vout->lock);
+                       ret = -ERANGE;
+                       break;
+               }
+               if (mirror  && vout->pix.pixelformat == V4L2_PIX_FMT_RGB24) {
+                       mutex_unlock(&vout->lock);
+                       ret = -EINVAL;
+                       break;
+               }
+               vout->mirror = mirror;
+               vout->control[2].value = mirror;
+               mutex_unlock(&vout->lock);
+               break;
+       }
+       default:
+               ret = -EINVAL;
+       }
+       return ret;
+ }
+ static int vidioc_reqbufs(struct file *file, void *fh,
+                       struct v4l2_requestbuffers *req)
+ {
+       int ret = 0;
+       unsigned int i, num_buffers = 0;
+       struct omap_vout_device *vout = fh;
+       struct videobuf_queue *q = &vout->vbq;
+       if ((req->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) || (req->count < 0))
+               return -EINVAL;
+       /* if memory is not mmp or userptr
+          return error */
+       if ((V4L2_MEMORY_MMAP != req->memory) &&
+                       (V4L2_MEMORY_USERPTR != req->memory))
+               return -EINVAL;
+       mutex_lock(&vout->lock);
+       /* Cannot be requested when streaming is on */
+       if (vout->streaming) {
+               ret = -EBUSY;
+               goto reqbuf_err;
+       }
+       /* If buffers are already allocated free them */
+       if (q->bufs[0] && (V4L2_MEMORY_MMAP == q->bufs[0]->memory)) {
+               if (vout->mmap_count) {
+                       ret = -EBUSY;
+                       goto reqbuf_err;
+               }
+               num_buffers = (vout->vid == OMAP_VIDEO1) ?
+                       video1_numbuffers : video2_numbuffers;
+               for (i = num_buffers; i < vout->buffer_allocated; i++) {
+                       omap_vout_free_buffer(vout->buf_virt_addr[i],
+                                       vout->buffer_size);
+                       vout->buf_virt_addr[i] = 0;
+                       vout->buf_phy_addr[i] = 0;
+               }
+               vout->buffer_allocated = num_buffers;
+               videobuf_mmap_free(q);
+       } else if (q->bufs[0] && (V4L2_MEMORY_USERPTR == q->bufs[0]->memory)) {
+               if (vout->buffer_allocated) {
+                       videobuf_mmap_free(q);
+                       for (i = 0; i < vout->buffer_allocated; i++) {
+                               kfree(q->bufs[i]);
+                               q->bufs[i] = NULL;
+                       }
+                       vout->buffer_allocated = 0;
+               }
+       }
+       /*store the memory type in data structure */
+       vout->memory = req->memory;
+       INIT_LIST_HEAD(&vout->dma_queue);
+       /* call videobuf_reqbufs api */
+       ret = videobuf_reqbufs(q, req);
+       if (ret < 0)
+               goto reqbuf_err;
+       vout->buffer_allocated = req->count;
+ reqbuf_err:
+       mutex_unlock(&vout->lock);
+       return ret;
+ }
+ static int vidioc_querybuf(struct file *file, void *fh,
+                       struct v4l2_buffer *b)
+ {
+       struct omap_vout_device *vout = fh;
+       return videobuf_querybuf(&vout->vbq, b);
+ }
+ static int vidioc_qbuf(struct file *file, void *fh,
+                       struct v4l2_buffer *buffer)
+ {
+       struct omap_vout_device *vout = fh;
+       struct videobuf_queue *q = &vout->vbq;
+       if ((V4L2_BUF_TYPE_VIDEO_OUTPUT != buffer->type) ||
+                       (buffer->index >= vout->buffer_allocated) ||
+                       (q->bufs[buffer->index]->memory != buffer->memory)) {
+               return -EINVAL;
+       }
+       if (V4L2_MEMORY_USERPTR == buffer->memory) {
+               if ((buffer->length < vout->pix.sizeimage) ||
+                               (0 == buffer->m.userptr)) {
+                       return -EINVAL;
+               }
+       }
+       if ((is_rotation_enabled(vout)) &&
+                       vout->vrfb_dma_tx.req_status == DMA_CHAN_NOT_ALLOTED) {
+               v4l2_warn(&vout->vid_dev->v4l2_dev,
+                               "DMA Channel not allocated for Rotation\n");
+               return -EINVAL;
+       }
+       return videobuf_qbuf(q, buffer);
+ }
+ static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)
+ {
+       struct omap_vout_device *vout = fh;
+       struct videobuf_queue *q = &vout->vbq;
+       int ret;
+       u32 addr;
+       unsigned long size;
+       struct videobuf_buffer *vb;
+       vb = q->bufs[b->index];
+       if (!vout->streaming)
+               return -EINVAL;
+       if (file->f_flags & O_NONBLOCK)
+               /* Call videobuf_dqbuf for non blocking mode */
+               ret = videobuf_dqbuf(q, (struct v4l2_buffer *)b, 1);
+       else
+               /* Call videobuf_dqbuf for  blocking mode */
+               ret = videobuf_dqbuf(q, (struct v4l2_buffer *)b, 0);
+       addr = (unsigned long) vout->buf_phy_addr[vb->i];
+       size = (unsigned long) vb->size;
+       dma_unmap_single(vout->vid_dev->v4l2_dev.dev,  addr,
+                               size, DMA_TO_DEVICE);
+       return ret;
+ }
+ static int vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type i)
+ {
+       int ret = 0, j;
+       u32 addr = 0, mask = 0;
+       struct omap_vout_device *vout = fh;
+       struct videobuf_queue *q = &vout->vbq;
+       struct omapvideo_info *ovid = &vout->vid_info;
+       mutex_lock(&vout->lock);
+       if (vout->streaming) {
+               ret = -EBUSY;
+               goto streamon_err;
+       }
+       ret = videobuf_streamon(q);
+       if (ret)
+               goto streamon_err;
+       if (list_empty(&vout->dma_queue)) {
+               ret = -EIO;
+               goto streamon_err1;
+       }
+       /* Get the next frame from the buffer queue */
+       vout->next_frm = vout->cur_frm = list_entry(vout->dma_queue.next,
+                       struct videobuf_buffer, queue);
+       /* Remove buffer from the buffer queue */
+       list_del(&vout->cur_frm->queue);
+       /* Mark state of the current frame to active */
+       vout->cur_frm->state = VIDEOBUF_ACTIVE;
+       /* Initialize field_id and started member */
+       vout->field_id = 0;
+       /* set flag here. Next QBUF will start DMA */
+       vout->streaming = 1;
+       vout->first_int = 1;
+       if (omap_vout_calculate_offset(vout)) {
+               ret = -EINVAL;
+               goto streamon_err1;
+       }
+       addr = (unsigned long) vout->queued_buf_addr[vout->cur_frm->i]
+               + vout->cropped_offset;
+       mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD
+               | DISPC_IRQ_VSYNC2;
+       omap_dispc_register_isr(omap_vout_isr, vout, mask);
+       for (j = 0; j < ovid->num_overlays; j++) {
+               struct omap_overlay *ovl = ovid->overlays[j];
+               if (ovl->manager && ovl->manager->device) {
+                       struct omap_overlay_info info;
+                       ovl->get_overlay_info(ovl, &info);
+                       info.paddr = addr;
+                       if (ovl->set_overlay_info(ovl, &info)) {
+                               ret = -EINVAL;
+                               goto streamon_err1;
+                       }
+               }
+       }
+       /* First save the configuration in ovelray structure */
+       ret = omapvid_init(vout, addr);
+       if (ret)
+               v4l2_err(&vout->vid_dev->v4l2_dev,
+                               "failed to set overlay info\n");
+       /* Enable the pipeline and set the Go bit */
+       ret = omapvid_apply_changes(vout);
+       if (ret)
+               v4l2_err(&vout->vid_dev->v4l2_dev, "failed to change mode\n");
+       for (j = 0; j < ovid->num_overlays; j++) {
+               struct omap_overlay *ovl = ovid->overlays[j];
+               if (ovl->manager && ovl->manager->device) {
+                       ret = ovl->enable(ovl);
+                       if (ret)
+                               goto streamon_err1;
+               }
+       }
+       ret = 0;
+ streamon_err1:
+       if (ret)
+               ret = videobuf_streamoff(q);
+ streamon_err:
+       mutex_unlock(&vout->lock);
+       return ret;
+ }
+ static int vidioc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i)
+ {
+       u32 mask = 0;
+       int ret = 0, j;
+       struct omap_vout_device *vout = fh;
+       struct omapvideo_info *ovid = &vout->vid_info;
+       if (!vout->streaming)
+               return -EINVAL;
+       vout->streaming = 0;
+       mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD
+               | DISPC_IRQ_VSYNC2;
+       omap_dispc_unregister_isr(omap_vout_isr, vout, mask);
+       for (j = 0; j < ovid->num_overlays; j++) {
+               struct omap_overlay *ovl = ovid->overlays[j];
+               if (ovl->manager && ovl->manager->device)
+                       ovl->disable(ovl);
+       }
+       /* Turn of the pipeline */
+       ret = omapvid_apply_changes(vout);
+       if (ret)
+               v4l2_err(&vout->vid_dev->v4l2_dev, "failed to change mode in"
+                               " streamoff\n");
+       INIT_LIST_HEAD(&vout->dma_queue);
+       ret = videobuf_streamoff(&vout->vbq);
+       return ret;
+ }
+ static int vidioc_s_fbuf(struct file *file, void *fh,
+                               const struct v4l2_framebuffer *a)
+ {
+       int enable = 0;
+       struct omap_overlay *ovl;
+       struct omapvideo_info *ovid;
+       struct omap_vout_device *vout = fh;
+       struct omap_overlay_manager_info info;
+       enum omap_dss_trans_key_type key_type = OMAP_DSS_COLOR_KEY_GFX_DST;
+       ovid = &vout->vid_info;
+       ovl = ovid->overlays[0];
+       /* OMAP DSS doesn't support Source and Destination color
+          key together */
+       if ((a->flags & V4L2_FBUF_FLAG_SRC_CHROMAKEY) &&
+                       (a->flags & V4L2_FBUF_FLAG_CHROMAKEY))
+               return -EINVAL;
+       /* OMAP DSS Doesn't support the Destination color key
+          and alpha blending together */
+       if ((a->flags & V4L2_FBUF_FLAG_CHROMAKEY) &&
+                       (a->flags & V4L2_FBUF_FLAG_LOCAL_ALPHA))
+               return -EINVAL;
+       if ((a->flags & V4L2_FBUF_FLAG_SRC_CHROMAKEY)) {
+               vout->fbuf.flags |= V4L2_FBUF_FLAG_SRC_CHROMAKEY;
+               key_type =  OMAP_DSS_COLOR_KEY_VID_SRC;
+       } else
+               vout->fbuf.flags &= ~V4L2_FBUF_FLAG_SRC_CHROMAKEY;
+       if ((a->flags & V4L2_FBUF_FLAG_CHROMAKEY)) {
+               vout->fbuf.flags |= V4L2_FBUF_FLAG_CHROMAKEY;
+               key_type =  OMAP_DSS_COLOR_KEY_GFX_DST;
+       } else
+               vout->fbuf.flags &=  ~V4L2_FBUF_FLAG_CHROMAKEY;
+       if (a->flags & (V4L2_FBUF_FLAG_CHROMAKEY |
+                               V4L2_FBUF_FLAG_SRC_CHROMAKEY))
+               enable = 1;
+       else
+               enable = 0;
+       if (ovl->manager && ovl->manager->get_manager_info &&
+                       ovl->manager->set_manager_info) {
+               ovl->manager->get_manager_info(ovl->manager, &info);
+               info.trans_enabled = enable;
+               info.trans_key_type = key_type;
+               info.trans_key = vout->win.chromakey;
+               if (ovl->manager->set_manager_info(ovl->manager, &info))
+                       return -EINVAL;
+       }
+       if (a->flags & V4L2_FBUF_FLAG_LOCAL_ALPHA) {
+               vout->fbuf.flags |= V4L2_FBUF_FLAG_LOCAL_ALPHA;
+               enable = 1;
+       } else {
+               vout->fbuf.flags &= ~V4L2_FBUF_FLAG_LOCAL_ALPHA;
+               enable = 0;
+       }
+       if (ovl->manager && ovl->manager->get_manager_info &&
+                       ovl->manager->set_manager_info) {
+               ovl->manager->get_manager_info(ovl->manager, &info);
+               /* enable this only if there is no zorder cap */
+               if ((ovl->caps & OMAP_DSS_OVL_CAP_ZORDER) == 0)
+                       info.partial_alpha_enabled = enable;
+               if (ovl->manager->set_manager_info(ovl->manager, &info))
+                       return -EINVAL;
+       }
+       return 0;
+ }
+ static int vidioc_g_fbuf(struct file *file, void *fh,
+               struct v4l2_framebuffer *a)
+ {
+       struct omap_overlay *ovl;
+       struct omapvideo_info *ovid;
+       struct omap_vout_device *vout = fh;
+       struct omap_overlay_manager_info info;
+       ovid = &vout->vid_info;
+       ovl = ovid->overlays[0];
+       /* The video overlay must stay within the framebuffer and can't be
+          positioned independently. */
+       a->flags = V4L2_FBUF_FLAG_OVERLAY;
+       a->capability = V4L2_FBUF_CAP_LOCAL_ALPHA | V4L2_FBUF_CAP_CHROMAKEY
+               | V4L2_FBUF_CAP_SRC_CHROMAKEY;
+       if (ovl->manager && ovl->manager->get_manager_info) {
+               ovl->manager->get_manager_info(ovl->manager, &info);
+               if (info.trans_key_type == OMAP_DSS_COLOR_KEY_VID_SRC)
+                       a->flags |= V4L2_FBUF_FLAG_SRC_CHROMAKEY;
+               if (info.trans_key_type == OMAP_DSS_COLOR_KEY_GFX_DST)
+                       a->flags |= V4L2_FBUF_FLAG_CHROMAKEY;
+       }
+       if (ovl->manager && ovl->manager->get_manager_info) {
+               ovl->manager->get_manager_info(ovl->manager, &info);
+               if (info.partial_alpha_enabled)
+                       a->flags |= V4L2_FBUF_FLAG_LOCAL_ALPHA;
+       }
+       return 0;
+ }
+ static const struct v4l2_ioctl_ops vout_ioctl_ops = {
+       .vidioc_querycap                        = vidioc_querycap,
+       .vidioc_enum_fmt_vid_out                = vidioc_enum_fmt_vid_out,
+       .vidioc_g_fmt_vid_out                   = vidioc_g_fmt_vid_out,
+       .vidioc_try_fmt_vid_out                 = vidioc_try_fmt_vid_out,
+       .vidioc_s_fmt_vid_out                   = vidioc_s_fmt_vid_out,
+       .vidioc_queryctrl                       = vidioc_queryctrl,
+       .vidioc_g_ctrl                          = vidioc_g_ctrl,
+       .vidioc_s_fbuf                          = vidioc_s_fbuf,
+       .vidioc_g_fbuf                          = vidioc_g_fbuf,
+       .vidioc_s_ctrl                          = vidioc_s_ctrl,
+       .vidioc_try_fmt_vid_overlay             = vidioc_try_fmt_vid_overlay,
+       .vidioc_s_fmt_vid_overlay               = vidioc_s_fmt_vid_overlay,
+       .vidioc_enum_fmt_vid_overlay            = vidioc_enum_fmt_vid_overlay,
+       .vidioc_g_fmt_vid_overlay               = vidioc_g_fmt_vid_overlay,
+       .vidioc_cropcap                         = vidioc_cropcap,
+       .vidioc_g_crop                          = vidioc_g_crop,
+       .vidioc_s_crop                          = vidioc_s_crop,
+       .vidioc_reqbufs                         = vidioc_reqbufs,
+       .vidioc_querybuf                        = vidioc_querybuf,
+       .vidioc_qbuf                            = vidioc_qbuf,
+       .vidioc_dqbuf                           = vidioc_dqbuf,
+       .vidioc_streamon                        = vidioc_streamon,
+       .vidioc_streamoff                       = vidioc_streamoff,
+ };
+ static const struct v4l2_file_operations omap_vout_fops = {
+       .owner          = THIS_MODULE,
+       .poll           = omap_vout_poll,
+       .unlocked_ioctl = video_ioctl2,
+       .mmap           = omap_vout_mmap,
+       .open           = omap_vout_open,
+       .release        = omap_vout_release,
+ };
+ /* Init functions used during driver initialization */
+ /* Initial setup of video_data */
+ static int __init omap_vout_setup_video_data(struct omap_vout_device *vout)
+ {
+       struct video_device *vfd;
+       struct v4l2_pix_format *pix;
+       struct v4l2_control *control;
+       struct omap_dss_device *display =
+               vout->vid_info.overlays[0]->manager->device;
+       /* set the default pix */
+       pix = &vout->pix;
+       /* Set the default picture of QVGA  */
+       pix->width = QQVGA_WIDTH;
+       pix->height = QQVGA_HEIGHT;
+       /* Default pixel format is RGB 5-6-5 */
+       pix->pixelformat = V4L2_PIX_FMT_RGB565;
+       pix->field = V4L2_FIELD_ANY;
+       pix->bytesperline = pix->width * 2;
+       pix->sizeimage = pix->bytesperline * pix->height;
+       pix->priv = 0;
+       pix->colorspace = V4L2_COLORSPACE_JPEG;
+       vout->bpp = RGB565_BPP;
+       vout->fbuf.fmt.width  =  display->panel.timings.x_res;
+       vout->fbuf.fmt.height =  display->panel.timings.y_res;
+       /* Set the data structures for the overlay parameters*/
+       vout->win.global_alpha = 255;
+       vout->fbuf.flags = 0;
+       vout->fbuf.capability = V4L2_FBUF_CAP_LOCAL_ALPHA |
+               V4L2_FBUF_CAP_SRC_CHROMAKEY | V4L2_FBUF_CAP_CHROMAKEY;
+       vout->win.chromakey = 0;
+       omap_vout_new_format(pix, &vout->fbuf, &vout->crop, &vout->win);
+       /*Initialize the control variables for
+         rotation, flipping and background color. */
+       control = vout->control;
+       control[0].id = V4L2_CID_ROTATE;
+       control[0].value = 0;
+       vout->rotation = 0;
+       vout->mirror = 0;
+       vout->control[2].id = V4L2_CID_HFLIP;
+       vout->control[2].value = 0;
+       if (vout->vid_info.rotation_type == VOUT_ROT_VRFB)
+               vout->vrfb_bpp = 2;
+       control[1].id = V4L2_CID_BG_COLOR;
+       control[1].value = 0;
+       /* initialize the video_device struct */
+       vfd = vout->vfd = video_device_alloc();
+       if (!vfd) {
+               printk(KERN_ERR VOUT_NAME ": could not allocate"
+                               " video device struct\n");
+               return -ENOMEM;
+       }
+       vfd->release = video_device_release;
+       vfd->ioctl_ops = &vout_ioctl_ops;
+       strlcpy(vfd->name, VOUT_NAME, sizeof(vfd->name));
+       vfd->fops = &omap_vout_fops;
+       vfd->v4l2_dev = &vout->vid_dev->v4l2_dev;
+       vfd->vfl_dir = VFL_DIR_TX;
+       mutex_init(&vout->lock);
+       vfd->minor = -1;
+       return 0;
+ }
+ /* Setup video buffers */
+ static int __init omap_vout_setup_video_bufs(struct platform_device *pdev,
+               int vid_num)
+ {
+       u32 numbuffers;
+       int ret = 0, i;
+       struct omapvideo_info *ovid;
+       struct omap_vout_device *vout;
+       struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev);
+       struct omap2video_device *vid_dev =
+               container_of(v4l2_dev, struct omap2video_device, v4l2_dev);
+       vout = vid_dev->vouts[vid_num];
+       ovid = &vout->vid_info;
+       numbuffers = (vid_num == 0) ? video1_numbuffers : video2_numbuffers;
+       vout->buffer_size = (vid_num == 0) ? video1_bufsize : video2_bufsize;
+       dev_info(&pdev->dev, "Buffer Size = %d\n", vout->buffer_size);
+       for (i = 0; i < numbuffers; i++) {
+               vout->buf_virt_addr[i] =
+                       omap_vout_alloc_buffer(vout->buffer_size,
+                                       (u32 *) &vout->buf_phy_addr[i]);
+               if (!vout->buf_virt_addr[i]) {
+                       numbuffers = i;
+                       ret = -ENOMEM;
+                       goto free_buffers;
+               }
+       }
+       vout->cropped_offset = 0;
+       if (ovid->rotation_type == VOUT_ROT_VRFB) {
+               int static_vrfb_allocation = (vid_num == 0) ?
+                       vid1_static_vrfb_alloc : vid2_static_vrfb_alloc;
+               ret = omap_vout_setup_vrfb_bufs(pdev, vid_num,
+                               static_vrfb_allocation);
+       }
+       return ret;
+ free_buffers:
+       for (i = 0; i < numbuffers; i++) {
+               omap_vout_free_buffer(vout->buf_virt_addr[i],
+                                               vout->buffer_size);
+               vout->buf_virt_addr[i] = 0;
+               vout->buf_phy_addr[i] = 0;
+       }
+       return ret;
+ }
+ /* Create video out devices */
+ static int __init omap_vout_create_video_devices(struct platform_device *pdev)
+ {
+       int ret = 0, k;
+       struct omap_vout_device *vout;
+       struct video_device *vfd = NULL;
+       struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev);
+       struct omap2video_device *vid_dev = container_of(v4l2_dev,
+                       struct omap2video_device, v4l2_dev);
+       for (k = 0; k < pdev->num_resources; k++) {
+               vout = kzalloc(sizeof(struct omap_vout_device), GFP_KERNEL);
+               if (!vout) {
+                       dev_err(&pdev->dev, ": could not allocate memory\n");
+                       return -ENOMEM;
+               }
+               vout->vid = k;
+               vid_dev->vouts[k] = vout;
+               vout->vid_dev = vid_dev;
+               /* Select video2 if only 1 overlay is controlled by V4L2 */
+               if (pdev->num_resources == 1)
+                       vout->vid_info.overlays[0] = vid_dev->overlays[k + 2];
+               else
+                       /* Else select video1 and video2 one by one. */
+                       vout->vid_info.overlays[0] = vid_dev->overlays[k + 1];
+               vout->vid_info.num_overlays = 1;
+               vout->vid_info.id = k + 1;
+               /* Set VRFB as rotation_type for omap2 and omap3 */
+               if (cpu_is_omap24xx() || cpu_is_omap34xx())
+                       vout->vid_info.rotation_type = VOUT_ROT_VRFB;
+               /* Setup the default configuration for the video devices
+                */
+               if (omap_vout_setup_video_data(vout) != 0) {
+                       ret = -ENOMEM;
+                       goto error;
+               }
+               /* Allocate default number of buffers for the video streaming
+                * and reserve the VRFB space for rotation
+                */
+               if (omap_vout_setup_video_bufs(pdev, k) != 0) {
+                       ret = -ENOMEM;
+                       goto error1;
+               }
+               /* Register the Video device with V4L2
+                */
+               vfd = vout->vfd;
+               if (video_register_device(vfd, VFL_TYPE_GRABBER, -1) < 0) {
+                       dev_err(&pdev->dev, ": Could not register "
+                                       "Video for Linux device\n");
+                       vfd->minor = -1;
+                       ret = -ENODEV;
+                       goto error2;
+               }
+               video_set_drvdata(vfd, vout);
+               /* Configure the overlay structure */
+               ret = omapvid_init(vid_dev->vouts[k], 0);
+               if (!ret)
+                       goto success;
+ error2:
+               if (vout->vid_info.rotation_type == VOUT_ROT_VRFB)
+                       omap_vout_release_vrfb(vout);
+               omap_vout_free_buffers(vout);
+ error1:
+               video_device_release(vfd);
+ error:
+               kfree(vout);
+               return ret;
+ success:
+               dev_info(&pdev->dev, ": registered and initialized"
+                               " video device %d\n", vfd->minor);
+               if (k == (pdev->num_resources - 1))
+                       return 0;
+       }
+       return -ENODEV;
+ }
+ /* Driver functions */
+ static void omap_vout_cleanup_device(struct omap_vout_device *vout)
+ {
+       struct video_device *vfd;
+       struct omapvideo_info *ovid;
+       if (!vout)
+               return;
+       vfd = vout->vfd;
+       ovid = &vout->vid_info;
+       if (vfd) {
+               if (!video_is_registered(vfd)) {
+                       /*
+                        * The device was never registered, so release the
+                        * video_device struct directly.
+                        */
+                       video_device_release(vfd);
+               } else {
+                       /*
+                        * The unregister function will release the video_device
+                        * struct as well as unregistering it.
+                        */
+                       video_unregister_device(vfd);
+               }
+       }
+       if (ovid->rotation_type == VOUT_ROT_VRFB) {
+               omap_vout_release_vrfb(vout);
+               /* Free the VRFB buffer if allocated
+                * init time
+                */
+               if (vout->vrfb_static_allocation)
+                       omap_vout_free_vrfb_buffers(vout);
+       }
+       omap_vout_free_buffers(vout);
+       kfree(vout);
+ }
+ static int omap_vout_remove(struct platform_device *pdev)
+ {
+       int k;
+       struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev);
+       struct omap2video_device *vid_dev = container_of(v4l2_dev, struct
+                       omap2video_device, v4l2_dev);
+       v4l2_device_unregister(v4l2_dev);
+       for (k = 0; k < pdev->num_resources; k++)
+               omap_vout_cleanup_device(vid_dev->vouts[k]);
+       for (k = 0; k < vid_dev->num_displays; k++) {
+               if (vid_dev->displays[k]->state != OMAP_DSS_DISPLAY_DISABLED)
+                       vid_dev->displays[k]->driver->disable(vid_dev->displays[k]);
+               omap_dss_put_device(vid_dev->displays[k]);
+       }
+       kfree(vid_dev);
+       return 0;
+ }
+ static int __init omap_vout_probe(struct platform_device *pdev)
+ {
+       int ret = 0, i;
+       struct omap_overlay *ovl;
+       struct omap_dss_device *dssdev = NULL;
+       struct omap_dss_device *def_display;
+       struct omap2video_device *vid_dev = NULL;
+       if (pdev->num_resources == 0) {
+               dev_err(&pdev->dev, "probed for an unknown device\n");
+               return -ENODEV;
+       }
+       vid_dev = kzalloc(sizeof(struct omap2video_device), GFP_KERNEL);
+       if (vid_dev == NULL)
+               return -ENOMEM;
+       vid_dev->num_displays = 0;
+       for_each_dss_dev(dssdev) {
+               omap_dss_get_device(dssdev);
+               if (!dssdev->driver) {
+                       dev_warn(&pdev->dev, "no driver for display: %s\n",
+                                       dssdev->name);
+                       omap_dss_put_device(dssdev);
+                       continue;
+               }
+               vid_dev->displays[vid_dev->num_displays++] = dssdev;
+       }
+       if (vid_dev->num_displays == 0) {
+               dev_err(&pdev->dev, "no displays\n");
+               ret = -EINVAL;
+               goto probe_err0;
+       }
+       vid_dev->num_overlays = omap_dss_get_num_overlays();
+       for (i = 0; i < vid_dev->num_overlays; i++)
+               vid_dev->overlays[i] = omap_dss_get_overlay(i);
+       vid_dev->num_managers = omap_dss_get_num_overlay_managers();
+       for (i = 0; i < vid_dev->num_managers; i++)
+               vid_dev->managers[i] = omap_dss_get_overlay_manager(i);
+       /* Get the Video1 overlay and video2 overlay.
+        * Setup the Display attached to that overlays
+        */
+       for (i = 1; i < vid_dev->num_overlays; i++) {
+               ovl = omap_dss_get_overlay(i);
+               if (ovl->manager && ovl->manager->device) {
+                       def_display = ovl->manager->device;
+               } else {
+                       dev_warn(&pdev->dev, "cannot find display\n");
+                       def_display = NULL;
+               }
+               if (def_display) {
+                       struct omap_dss_driver *dssdrv = def_display->driver;
+                       ret = dssdrv->enable(def_display);
+                       if (ret) {
+                               /* Here we are not considering a error
+                                *  as display may be enabled by frame
+                                *  buffer driver
+                                */
+                               dev_warn(&pdev->dev,
+                                       "'%s' Display already enabled\n",
+                                       def_display->name);
+                       }
+               }
+       }
+       if (v4l2_device_register(&pdev->dev, &vid_dev->v4l2_dev) < 0) {
+               dev_err(&pdev->dev, "v4l2_device_register failed\n");
+               ret = -ENODEV;
+               goto probe_err1;
+       }
+       ret = omap_vout_create_video_devices(pdev);
+       if (ret)
+               goto probe_err2;
+       for (i = 0; i < vid_dev->num_displays; i++) {
+               struct omap_dss_device *display = vid_dev->displays[i];
+               if (display->driver->update)
+                       display->driver->update(display, 0, 0,
+                                       display->panel.timings.x_res,
+                                       display->panel.timings.y_res);
+       }
+       return 0;
+ probe_err2:
+       v4l2_device_unregister(&vid_dev->v4l2_dev);
+ probe_err1:
+       for (i = 1; i < vid_dev->num_overlays; i++) {
+               def_display = NULL;
+               ovl = omap_dss_get_overlay(i);
+               if (ovl->manager && ovl->manager->device)
+                       def_display = ovl->manager->device;
+               if (def_display && def_display->driver)
+                       def_display->driver->disable(def_display);
+       }
+ probe_err0:
+       kfree(vid_dev);
+       return ret;
+ }
+ static struct platform_driver omap_vout_driver = {
+       .driver = {
+               .name = VOUT_NAME,
+       },
+       .remove = omap_vout_remove,
+ };
+ static int __init omap_vout_init(void)
+ {
+       if (platform_driver_probe(&omap_vout_driver, omap_vout_probe) != 0) {
+               printk(KERN_ERR VOUT_NAME ":Could not register Video driver\n");
+               return -EINVAL;
+       }
+       return 0;
+ }
+ static void omap_vout_cleanup(void)
+ {
+       platform_driver_unregister(&omap_vout_driver);
+ }
+ late_initcall(omap_vout_init);
+ module_exit(omap_vout_cleanup);
index 0000000,fde2e66..70f45c3
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,1881 +1,1881 @@@
 -      flush_work_sync(&cam->sensor_reset_work);
+ /*
+  * drivers/media/platform/omap24xxcam.c
+  *
+  * OMAP 2 camera block driver.
+  *
+  * Copyright (C) 2004 MontaVista Software, Inc.
+  * Copyright (C) 2004 Texas Instruments.
+  * Copyright (C) 2007-2008 Nokia Corporation.
+  *
+  * Contact: Sakari Ailus <sakari.ailus@nokia.com>
+  *
+  * Based on code from Andy Lowe <source@mvista.com>
+  *
+  * This program is free software; you can redistribute it and/or
+  * modify it under the terms of the GNU General Public License
+  * version 2 as published by the Free Software Foundation.
+  *
+  * 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 St, Fifth Floor, Boston, MA
+  * 02110-1301 USA
+  */
+ #include <linux/delay.h>
+ #include <linux/kernel.h>
+ #include <linux/interrupt.h>
+ #include <linux/videodev2.h>
+ #include <linux/pci.h>                /* needed for videobufs */
+ #include <linux/platform_device.h>
+ #include <linux/clk.h>
+ #include <linux/io.h>
+ #include <linux/slab.h>
+ #include <linux/sched.h>
+ #include <linux/module.h>
+ #include <media/v4l2-common.h>
+ #include <media/v4l2-ioctl.h>
+ #include "omap24xxcam.h"
+ #define OMAP24XXCAM_VERSION "0.0.1"
+ #define RESET_TIMEOUT_NS 10000
+ static void omap24xxcam_reset(struct omap24xxcam_device *cam);
+ static int omap24xxcam_sensor_if_enable(struct omap24xxcam_device *cam);
+ static void omap24xxcam_device_unregister(struct v4l2_int_device *s);
+ static int omap24xxcam_remove(struct platform_device *pdev);
+ /* module parameters */
+ static int video_nr = -1;     /* video device minor (-1 ==> auto assign) */
+ /*
+  * Maximum amount of memory to use for capture buffers.
+  * Default is 4800KB, enough to double-buffer SXGA.
+  */
+ static int capture_mem = 1280 * 960 * 2 * 2;
+ static struct v4l2_int_device omap24xxcam;
+ /*
+  *
+  * Clocks.
+  *
+  */
+ static void omap24xxcam_clock_put(struct omap24xxcam_device *cam)
+ {
+       if (cam->ick != NULL && !IS_ERR(cam->ick))
+               clk_put(cam->ick);
+       if (cam->fck != NULL && !IS_ERR(cam->fck))
+               clk_put(cam->fck);
+       cam->ick = cam->fck = NULL;
+ }
+ static int omap24xxcam_clock_get(struct omap24xxcam_device *cam)
+ {
+       int rval = 0;
+       cam->fck = clk_get(cam->dev, "fck");
+       if (IS_ERR(cam->fck)) {
+               dev_err(cam->dev, "can't get camera fck");
+               rval = PTR_ERR(cam->fck);
+               omap24xxcam_clock_put(cam);
+               return rval;
+       }
+       cam->ick = clk_get(cam->dev, "ick");
+       if (IS_ERR(cam->ick)) {
+               dev_err(cam->dev, "can't get camera ick");
+               rval = PTR_ERR(cam->ick);
+               omap24xxcam_clock_put(cam);
+       }
+       return rval;
+ }
+ static void omap24xxcam_clock_on(struct omap24xxcam_device *cam)
+ {
+       clk_enable(cam->fck);
+       clk_enable(cam->ick);
+ }
+ static void omap24xxcam_clock_off(struct omap24xxcam_device *cam)
+ {
+       clk_disable(cam->fck);
+       clk_disable(cam->ick);
+ }
+ /*
+  *
+  * Camera core
+  *
+  */
+ /*
+  * Set xclk.
+  *
+  * To disable xclk, use value zero.
+  */
+ static void omap24xxcam_core_xclk_set(const struct omap24xxcam_device *cam,
+                                     u32 xclk)
+ {
+       if (xclk) {
+               u32 divisor = CAM_MCLK / xclk;
+               if (divisor == 1)
+                       omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET,
+                                           CC_CTRL_XCLK,
+                                           CC_CTRL_XCLK_DIV_BYPASS);
+               else
+                       omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET,
+                                           CC_CTRL_XCLK, divisor);
+       } else
+               omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET,
+                                   CC_CTRL_XCLK, CC_CTRL_XCLK_DIV_STABLE_LOW);
+ }
+ static void omap24xxcam_core_hwinit(const struct omap24xxcam_device *cam)
+ {
+       /*
+        * Setting the camera core AUTOIDLE bit causes problems with frame
+        * synchronization, so we will clear the AUTOIDLE bit instead.
+        */
+       omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_SYSCONFIG,
+                           CC_SYSCONFIG_AUTOIDLE);
+       /* program the camera interface DMA packet size */
+       omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_CTRL_DMA,
+                           CC_CTRL_DMA_EN | (DMA_THRESHOLD / 4 - 1));
+       /* enable camera core error interrupts */
+       omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_IRQENABLE,
+                           CC_IRQENABLE_FW_ERR_IRQ
+                           | CC_IRQENABLE_FSC_ERR_IRQ
+                           | CC_IRQENABLE_SSC_ERR_IRQ
+                           | CC_IRQENABLE_FIFO_OF_IRQ);
+ }
+ /*
+  * Enable the camera core.
+  *
+  * Data transfer to the camera DMA starts from next starting frame.
+  */
+ static void omap24xxcam_core_enable(const struct omap24xxcam_device *cam)
+ {
+       omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_CTRL,
+                           cam->cc_ctrl);
+ }
+ /*
+  * Disable camera core.
+  *
+  * The data transfer will be stopped immediately (CC_CTRL_CC_RST). The
+  * core internal state machines will be reset. Use
+  * CC_CTRL_CC_FRAME_TRIG instead if you want to transfer the current
+  * frame completely.
+  */
+ static void omap24xxcam_core_disable(const struct omap24xxcam_device *cam)
+ {
+       omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_CTRL,
+                           CC_CTRL_CC_RST);
+ }
+ /* Interrupt service routine for camera core interrupts. */
+ static void omap24xxcam_core_isr(struct omap24xxcam_device *cam)
+ {
+       u32 cc_irqstatus;
+       const u32 cc_irqstatus_err =
+               CC_IRQSTATUS_FW_ERR_IRQ
+               | CC_IRQSTATUS_FSC_ERR_IRQ
+               | CC_IRQSTATUS_SSC_ERR_IRQ
+               | CC_IRQSTATUS_FIFO_UF_IRQ
+               | CC_IRQSTATUS_FIFO_OF_IRQ;
+       cc_irqstatus = omap24xxcam_reg_in(cam->mmio_base + CC_REG_OFFSET,
+                                         CC_IRQSTATUS);
+       omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_IRQSTATUS,
+                           cc_irqstatus);
+       if (cc_irqstatus & cc_irqstatus_err
+           && !atomic_read(&cam->in_reset)) {
+               dev_dbg(cam->dev, "resetting camera, cc_irqstatus 0x%x\n",
+                       cc_irqstatus);
+               omap24xxcam_reset(cam);
+       }
+ }
+ /*
+  *
+  * videobuf_buffer handling.
+  *
+  * Memory for mmapped videobuf_buffers is not allocated
+  * conventionally, but by several kmalloc allocations and then
+  * creating the scatterlist on our own. User-space buffers are handled
+  * normally.
+  *
+  */
+ /*
+  * Free the memory-mapped buffer memory allocated for a
+  * videobuf_buffer and the associated scatterlist.
+  */
+ static void omap24xxcam_vbq_free_mmap_buffer(struct videobuf_buffer *vb)
+ {
+       struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
+       size_t alloc_size;
+       struct page *page;
+       int i;
+       if (dma->sglist == NULL)
+               return;
+       i = dma->sglen;
+       while (i) {
+               i--;
+               alloc_size = sg_dma_len(&dma->sglist[i]);
+               page = sg_page(&dma->sglist[i]);
+               do {
+                       ClearPageReserved(page++);
+               } while (alloc_size -= PAGE_SIZE);
+               __free_pages(sg_page(&dma->sglist[i]),
+                            get_order(sg_dma_len(&dma->sglist[i])));
+       }
+       kfree(dma->sglist);
+       dma->sglist = NULL;
+ }
+ /* Release all memory related to the videobuf_queue. */
+ static void omap24xxcam_vbq_free_mmap_buffers(struct videobuf_queue *vbq)
+ {
+       int i;
+       mutex_lock(&vbq->vb_lock);
+       for (i = 0; i < VIDEO_MAX_FRAME; i++) {
+               if (NULL == vbq->bufs[i])
+                       continue;
+               if (V4L2_MEMORY_MMAP != vbq->bufs[i]->memory)
+                       continue;
+               vbq->ops->buf_release(vbq, vbq->bufs[i]);
+               omap24xxcam_vbq_free_mmap_buffer(vbq->bufs[i]);
+               kfree(vbq->bufs[i]);
+               vbq->bufs[i] = NULL;
+       }
+       mutex_unlock(&vbq->vb_lock);
+       videobuf_mmap_free(vbq);
+ }
+ /*
+  * Allocate physically as contiguous as possible buffer for video
+  * frame and allocate and build DMA scatter-gather list for it.
+  */
+ static int omap24xxcam_vbq_alloc_mmap_buffer(struct videobuf_buffer *vb)
+ {
+       unsigned int order;
+       size_t alloc_size, size = vb->bsize; /* vb->bsize is page aligned */
+       struct page *page;
+       int max_pages, err = 0, i = 0;
+       struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
+       /*
+        * allocate maximum size scatter-gather list. Note this is
+        * overhead. We may not use as many entries as we allocate
+        */
+       max_pages = vb->bsize >> PAGE_SHIFT;
+       dma->sglist = kcalloc(max_pages, sizeof(*dma->sglist), GFP_KERNEL);
+       if (dma->sglist == NULL) {
+               err = -ENOMEM;
+               goto out;
+       }
+       while (size) {
+               order = get_order(size);
+               /*
+                * do not over-allocate even if we would get larger
+                * contiguous chunk that way
+                */
+               if ((PAGE_SIZE << order) > size)
+                       order--;
+               /* try to allocate as many contiguous pages as possible */
+               page = alloc_pages(GFP_KERNEL, order);
+               /* if allocation fails, try to allocate smaller amount */
+               while (page == NULL) {
+                       order--;
+                       page = alloc_pages(GFP_KERNEL, order);
+                       if (page == NULL && !order) {
+                               err = -ENOMEM;
+                               goto out;
+                       }
+               }
+               size -= (PAGE_SIZE << order);
+               /* append allocated chunk of pages into scatter-gather list */
+               sg_set_page(&dma->sglist[i], page, PAGE_SIZE << order, 0);
+               dma->sglen++;
+               i++;
+               alloc_size = (PAGE_SIZE << order);
+               /* clear pages before giving them to user space */
+               memset(page_address(page), 0, alloc_size);
+               /* mark allocated pages reserved */
+               do {
+                       SetPageReserved(page++);
+               } while (alloc_size -= PAGE_SIZE);
+       }
+       /*
+        * REVISIT: not fully correct to assign nr_pages == sglen but
+        * video-buf is passing nr_pages for e.g. unmap_sg calls
+        */
+       dma->nr_pages = dma->sglen;
+       dma->direction = PCI_DMA_FROMDEVICE;
+       return 0;
+ out:
+       omap24xxcam_vbq_free_mmap_buffer(vb);
+       return err;
+ }
+ static int omap24xxcam_vbq_alloc_mmap_buffers(struct videobuf_queue *vbq,
+                                             unsigned int count)
+ {
+       int i, err = 0;
+       struct omap24xxcam_fh *fh =
+               container_of(vbq, struct omap24xxcam_fh, vbq);
+       mutex_lock(&vbq->vb_lock);
+       for (i = 0; i < count; i++) {
+               err = omap24xxcam_vbq_alloc_mmap_buffer(vbq->bufs[i]);
+               if (err)
+                       goto out;
+               dev_dbg(fh->cam->dev, "sglen is %d for buffer %d\n",
+                       videobuf_to_dma(vbq->bufs[i])->sglen, i);
+       }
+       mutex_unlock(&vbq->vb_lock);
+       return 0;
+ out:
+       while (i) {
+               i--;
+               omap24xxcam_vbq_free_mmap_buffer(vbq->bufs[i]);
+       }
+       mutex_unlock(&vbq->vb_lock);
+       return err;
+ }
+ /*
+  * This routine is called from interrupt context when a scatter-gather DMA
+  * transfer of a videobuf_buffer completes.
+  */
+ static void omap24xxcam_vbq_complete(struct omap24xxcam_sgdma *sgdma,
+                                    u32 csr, void *arg)
+ {
+       struct omap24xxcam_device *cam =
+               container_of(sgdma, struct omap24xxcam_device, sgdma);
+       struct omap24xxcam_fh *fh = cam->streaming->private_data;
+       struct videobuf_buffer *vb = (struct videobuf_buffer *)arg;
+       const u32 csr_error = CAMDMA_CSR_MISALIGNED_ERR
+               | CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR
+               | CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP;
+       unsigned long flags;
+       spin_lock_irqsave(&cam->core_enable_disable_lock, flags);
+       if (--cam->sgdma_in_queue == 0)
+               omap24xxcam_core_disable(cam);
+       spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags);
+       do_gettimeofday(&vb->ts);
+       vb->field_count = atomic_add_return(2, &fh->field_count);
+       if (csr & csr_error) {
+               vb->state = VIDEOBUF_ERROR;
+               if (!atomic_read(&fh->cam->in_reset)) {
+                       dev_dbg(cam->dev, "resetting camera, csr 0x%x\n", csr);
+                       omap24xxcam_reset(cam);
+               }
+       } else
+               vb->state = VIDEOBUF_DONE;
+       wake_up(&vb->done);
+ }
+ static void omap24xxcam_vbq_release(struct videobuf_queue *vbq,
+                                   struct videobuf_buffer *vb)
+ {
+       struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
+       /* wait for buffer, especially to get out of the sgdma queue */
+       videobuf_waiton(vbq, vb, 0, 0);
+       if (vb->memory == V4L2_MEMORY_MMAP) {
+               dma_unmap_sg(vbq->dev, dma->sglist, dma->sglen,
+                            dma->direction);
+               dma->direction = DMA_NONE;
+       } else {
+               videobuf_dma_unmap(vbq->dev, videobuf_to_dma(vb));
+               videobuf_dma_free(videobuf_to_dma(vb));
+       }
+       vb->state = VIDEOBUF_NEEDS_INIT;
+ }
+ /*
+  * Limit the number of available kernel image capture buffers based on the
+  * number requested, the currently selected image size, and the maximum
+  * amount of memory permitted for kernel capture buffers.
+  */
+ static int omap24xxcam_vbq_setup(struct videobuf_queue *vbq, unsigned int *cnt,
+                                unsigned int *size)
+ {
+       struct omap24xxcam_fh *fh = vbq->priv_data;
+       if (*cnt <= 0)
+               *cnt = VIDEO_MAX_FRAME; /* supply a default number of buffers */
+       if (*cnt > VIDEO_MAX_FRAME)
+               *cnt = VIDEO_MAX_FRAME;
+       *size = fh->pix.sizeimage;
+       /* accessing fh->cam->capture_mem is ok, it's constant */
+       if (*size * *cnt > fh->cam->capture_mem)
+               *cnt = fh->cam->capture_mem / *size;
+       return 0;
+ }
+ static int omap24xxcam_dma_iolock(struct videobuf_queue *vbq,
+                                 struct videobuf_dmabuf *dma)
+ {
+       int err = 0;
+       dma->direction = PCI_DMA_FROMDEVICE;
+       if (!dma_map_sg(vbq->dev, dma->sglist, dma->sglen, dma->direction)) {
+               kfree(dma->sglist);
+               dma->sglist = NULL;
+               dma->sglen = 0;
+               err = -EIO;
+       }
+       return err;
+ }
+ static int omap24xxcam_vbq_prepare(struct videobuf_queue *vbq,
+                                  struct videobuf_buffer *vb,
+                                  enum v4l2_field field)
+ {
+       struct omap24xxcam_fh *fh = vbq->priv_data;
+       int err = 0;
+       /*
+        * Accessing pix here is okay since it's constant while
+        * streaming is on (and we only get called then).
+        */
+       if (vb->baddr) {
+               /* This is a userspace buffer. */
+               if (fh->pix.sizeimage > vb->bsize) {
+                       /* The buffer isn't big enough. */
+                       err = -EINVAL;
+               } else
+                       vb->size = fh->pix.sizeimage;
+       } else {
+               if (vb->state != VIDEOBUF_NEEDS_INIT) {
+                       /*
+                        * We have a kernel bounce buffer that has
+                        * already been allocated.
+                        */
+                       if (fh->pix.sizeimage > vb->size) {
+                               /*
+                                * The image size has been changed to
+                                * a larger size since this buffer was
+                                * allocated, so we need to free and
+                                * reallocate it.
+                                */
+                               omap24xxcam_vbq_release(vbq, vb);
+                               vb->size = fh->pix.sizeimage;
+                       }
+               } else {
+                       /* We need to allocate a new kernel bounce buffer. */
+                       vb->size = fh->pix.sizeimage;
+               }
+       }
+       if (err)
+               return err;
+       vb->width = fh->pix.width;
+       vb->height = fh->pix.height;
+       vb->field = field;
+       if (vb->state == VIDEOBUF_NEEDS_INIT) {
+               if (vb->memory == V4L2_MEMORY_MMAP)
+                       /*
+                        * we have built the scatter-gather list by ourself so
+                        * do the scatter-gather mapping as well
+                        */
+                       err = omap24xxcam_dma_iolock(vbq, videobuf_to_dma(vb));
+               else
+                       err = videobuf_iolock(vbq, vb, NULL);
+       }
+       if (!err)
+               vb->state = VIDEOBUF_PREPARED;
+       else
+               omap24xxcam_vbq_release(vbq, vb);
+       return err;
+ }
+ static void omap24xxcam_vbq_queue(struct videobuf_queue *vbq,
+                                 struct videobuf_buffer *vb)
+ {
+       struct omap24xxcam_fh *fh = vbq->priv_data;
+       struct omap24xxcam_device *cam = fh->cam;
+       enum videobuf_state state = vb->state;
+       unsigned long flags;
+       int err;
+       /*
+        * FIXME: We're marking the buffer active since we have no
+        * pretty way of marking it active exactly when the
+        * scatter-gather transfer starts.
+        */
+       vb->state = VIDEOBUF_ACTIVE;
+       err = omap24xxcam_sgdma_queue(&fh->cam->sgdma,
+                                     videobuf_to_dma(vb)->sglist,
+                                     videobuf_to_dma(vb)->sglen, vb->size,
+                                     omap24xxcam_vbq_complete, vb);
+       if (!err) {
+               spin_lock_irqsave(&cam->core_enable_disable_lock, flags);
+               if (++cam->sgdma_in_queue == 1
+                   && !atomic_read(&cam->in_reset))
+                       omap24xxcam_core_enable(cam);
+               spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags);
+       } else {
+               /*
+                * Oops. We're not supposed to get any errors here.
+                * The only way we could get an error is if we ran out
+                * of scatter-gather DMA slots, but we are supposed to
+                * have at least as many scatter-gather DMA slots as
+                * video buffers so that can't happen.
+                */
+               dev_err(cam->dev, "failed to queue a video buffer for dma!\n");
+               dev_err(cam->dev, "likely a bug in the driver!\n");
+               vb->state = state;
+       }
+ }
+ static struct videobuf_queue_ops omap24xxcam_vbq_ops = {
+       .buf_setup   = omap24xxcam_vbq_setup,
+       .buf_prepare = omap24xxcam_vbq_prepare,
+       .buf_queue   = omap24xxcam_vbq_queue,
+       .buf_release = omap24xxcam_vbq_release,
+ };
+ /*
+  *
+  * OMAP main camera system
+  *
+  */
+ /*
+  * Reset camera block to power-on state.
+  */
+ static void omap24xxcam_poweron_reset(struct omap24xxcam_device *cam)
+ {
+       int max_loop = RESET_TIMEOUT_NS;
+       /* Reset whole camera subsystem */
+       omap24xxcam_reg_out(cam->mmio_base,
+                           CAM_SYSCONFIG,
+                           CAM_SYSCONFIG_SOFTRESET);
+       /* Wait till it's finished */
+       while (!(omap24xxcam_reg_in(cam->mmio_base, CAM_SYSSTATUS)
+                & CAM_SYSSTATUS_RESETDONE)
+              && --max_loop) {
+               ndelay(1);
+       }
+       if (!(omap24xxcam_reg_in(cam->mmio_base, CAM_SYSSTATUS)
+             & CAM_SYSSTATUS_RESETDONE))
+               dev_err(cam->dev, "camera soft reset timeout\n");
+ }
+ /*
+  * (Re)initialise the camera block.
+  */
+ static void omap24xxcam_hwinit(struct omap24xxcam_device *cam)
+ {
+       omap24xxcam_poweron_reset(cam);
+       /* set the camera subsystem autoidle bit */
+       omap24xxcam_reg_out(cam->mmio_base, CAM_SYSCONFIG,
+                           CAM_SYSCONFIG_AUTOIDLE);
+       /* set the camera MMU autoidle bit */
+       omap24xxcam_reg_out(cam->mmio_base,
+                           CAMMMU_REG_OFFSET + CAMMMU_SYSCONFIG,
+                           CAMMMU_SYSCONFIG_AUTOIDLE);
+       omap24xxcam_core_hwinit(cam);
+       omap24xxcam_dma_hwinit(&cam->sgdma.dma);
+ }
+ /*
+  * Callback for dma transfer stalling.
+  */
+ static void omap24xxcam_stalled_dma_reset(unsigned long data)
+ {
+       struct omap24xxcam_device *cam = (struct omap24xxcam_device *)data;
+       if (!atomic_read(&cam->in_reset)) {
+               dev_dbg(cam->dev, "dma stalled, resetting camera\n");
+               omap24xxcam_reset(cam);
+       }
+ }
+ /*
+  * Stop capture. Mark we're doing a reset, stop DMA transfers and
+  * core. (No new scatter-gather transfers will be queued whilst
+  * in_reset is non-zero.)
+  *
+  * If omap24xxcam_capture_stop is called from several places at
+  * once, only the first call will have an effect. Similarly, the last
+  * call omap24xxcam_streaming_cont will have effect.
+  *
+  * Serialisation is ensured by using cam->core_enable_disable_lock.
+  */
+ static void omap24xxcam_capture_stop(struct omap24xxcam_device *cam)
+ {
+       unsigned long flags;
+       spin_lock_irqsave(&cam->core_enable_disable_lock, flags);
+       if (atomic_inc_return(&cam->in_reset) != 1) {
+               spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags);
+               return;
+       }
+       omap24xxcam_core_disable(cam);
+       spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags);
+       omap24xxcam_sgdma_sync(&cam->sgdma);
+ }
+ /*
+  * Reset and continue streaming.
+  *
+  * Note: Resetting the camera FIFO via the CC_RST bit in the CC_CTRL
+  * register is supposed to be sufficient to recover from a camera
+  * interface error, but it doesn't seem to be enough. If we only do
+  * that then subsequent image captures are out of sync by either one
+  * or two times DMA_THRESHOLD bytes. Resetting and re-initializing the
+  * entire camera subsystem prevents the problem with frame
+  * synchronization.
+  */
+ static void omap24xxcam_capture_cont(struct omap24xxcam_device *cam)
+ {
+       unsigned long flags;
+       spin_lock_irqsave(&cam->core_enable_disable_lock, flags);
+       if (atomic_read(&cam->in_reset) != 1)
+               goto out;
+       omap24xxcam_hwinit(cam);
+       omap24xxcam_sensor_if_enable(cam);
+       omap24xxcam_sgdma_process(&cam->sgdma);
+       if (cam->sgdma_in_queue)
+               omap24xxcam_core_enable(cam);
+ out:
+       atomic_dec(&cam->in_reset);
+       spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags);
+ }
+ static ssize_t
+ omap24xxcam_streaming_show(struct device *dev, struct device_attribute *attr,
+               char *buf)
+ {
+       struct omap24xxcam_device *cam = dev_get_drvdata(dev);
+       return sprintf(buf, "%s\n", cam->streaming ?  "active" : "inactive");
+ }
+ static DEVICE_ATTR(streaming, S_IRUGO, omap24xxcam_streaming_show, NULL);
+ /*
+  * Stop capture and restart it. I.e. reset the camera during use.
+  */
+ static void omap24xxcam_reset(struct omap24xxcam_device *cam)
+ {
+       omap24xxcam_capture_stop(cam);
+       omap24xxcam_capture_cont(cam);
+ }
+ /*
+  * The main interrupt handler.
+  */
+ static irqreturn_t omap24xxcam_isr(int irq, void *arg)
+ {
+       struct omap24xxcam_device *cam = (struct omap24xxcam_device *)arg;
+       u32 irqstatus;
+       unsigned int irqhandled = 0;
+       irqstatus = omap24xxcam_reg_in(cam->mmio_base, CAM_IRQSTATUS);
+       if (irqstatus &
+           (CAM_IRQSTATUS_DMA_IRQ2 | CAM_IRQSTATUS_DMA_IRQ1
+            | CAM_IRQSTATUS_DMA_IRQ0)) {
+               omap24xxcam_dma_isr(&cam->sgdma.dma);
+               irqhandled = 1;
+       }
+       if (irqstatus & CAM_IRQSTATUS_CC_IRQ) {
+               omap24xxcam_core_isr(cam);
+               irqhandled = 1;
+       }
+       if (irqstatus & CAM_IRQSTATUS_MMU_IRQ)
+               dev_err(cam->dev, "unhandled camera MMU interrupt!\n");
+       return IRQ_RETVAL(irqhandled);
+ }
+ /*
+  *
+  * Sensor handling.
+  *
+  */
+ /*
+  * Enable the external sensor interface. Try to negotiate interface
+  * parameters with the sensor and start using the new ones. The calls
+  * to sensor_if_enable and sensor_if_disable need not to be balanced.
+  */
+ static int omap24xxcam_sensor_if_enable(struct omap24xxcam_device *cam)
+ {
+       int rval;
+       struct v4l2_ifparm p;
+       rval = vidioc_int_g_ifparm(cam->sdev, &p);
+       if (rval) {
+               dev_err(cam->dev, "vidioc_int_g_ifparm failed with %d\n", rval);
+               return rval;
+       }
+       cam->if_type = p.if_type;
+       cam->cc_ctrl = CC_CTRL_CC_EN;
+       switch (p.if_type) {
+       case V4L2_IF_TYPE_BT656:
+               if (p.u.bt656.frame_start_on_rising_vs)
+                       cam->cc_ctrl |= CC_CTRL_NOBT_SYNCHRO;
+               if (p.u.bt656.bt_sync_correct)
+                       cam->cc_ctrl |= CC_CTRL_BT_CORRECT;
+               if (p.u.bt656.swap)
+                       cam->cc_ctrl |= CC_CTRL_PAR_ORDERCAM;
+               if (p.u.bt656.latch_clk_inv)
+                       cam->cc_ctrl |= CC_CTRL_PAR_CLK_POL;
+               if (p.u.bt656.nobt_hs_inv)
+                       cam->cc_ctrl |= CC_CTRL_NOBT_HS_POL;
+               if (p.u.bt656.nobt_vs_inv)
+                       cam->cc_ctrl |= CC_CTRL_NOBT_VS_POL;
+               switch (p.u.bt656.mode) {
+               case V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT:
+                       cam->cc_ctrl |= CC_CTRL_PAR_MODE_NOBT8;
+                       break;
+               case V4L2_IF_TYPE_BT656_MODE_NOBT_10BIT:
+                       cam->cc_ctrl |= CC_CTRL_PAR_MODE_NOBT10;
+                       break;
+               case V4L2_IF_TYPE_BT656_MODE_NOBT_12BIT:
+                       cam->cc_ctrl |= CC_CTRL_PAR_MODE_NOBT12;
+                       break;
+               case V4L2_IF_TYPE_BT656_MODE_BT_8BIT:
+                       cam->cc_ctrl |= CC_CTRL_PAR_MODE_BT8;
+                       break;
+               case V4L2_IF_TYPE_BT656_MODE_BT_10BIT:
+                       cam->cc_ctrl |= CC_CTRL_PAR_MODE_BT10;
+                       break;
+               default:
+                       dev_err(cam->dev,
+                               "bt656 interface mode %d not supported\n",
+                               p.u.bt656.mode);
+                       return -EINVAL;
+               }
+               /*
+                * The clock rate that the sensor wants has changed.
+                * We have to adjust the xclk from OMAP 2 side to
+                * match the sensor's wish as closely as possible.
+                */
+               if (p.u.bt656.clock_curr != cam->if_u.bt656.xclk) {
+                       u32 xclk = p.u.bt656.clock_curr;
+                       u32 divisor;
+                       if (xclk == 0)
+                               return -EINVAL;
+                       if (xclk > CAM_MCLK)
+                               xclk = CAM_MCLK;
+                       divisor = CAM_MCLK / xclk;
+                       if (divisor * xclk < CAM_MCLK)
+                               divisor++;
+                       if (CAM_MCLK / divisor < p.u.bt656.clock_min
+                           && divisor > 1)
+                               divisor--;
+                       if (divisor > 30)
+                               divisor = 30;
+                       xclk = CAM_MCLK / divisor;
+                       if (xclk < p.u.bt656.clock_min
+                           || xclk > p.u.bt656.clock_max)
+                               return -EINVAL;
+                       cam->if_u.bt656.xclk = xclk;
+               }
+               omap24xxcam_core_xclk_set(cam, cam->if_u.bt656.xclk);
+               break;
+       default:
+               /* FIXME: how about other interfaces? */
+               dev_err(cam->dev, "interface type %d not supported\n",
+                       p.if_type);
+               return -EINVAL;
+       }
+       return 0;
+ }
+ static void omap24xxcam_sensor_if_disable(const struct omap24xxcam_device *cam)
+ {
+       switch (cam->if_type) {
+       case V4L2_IF_TYPE_BT656:
+               omap24xxcam_core_xclk_set(cam, 0);
+               break;
+       }
+ }
+ /*
+  * Initialise the sensor hardware.
+  */
+ static int omap24xxcam_sensor_init(struct omap24xxcam_device *cam)
+ {
+       int err = 0;
+       struct v4l2_int_device *sdev = cam->sdev;
+       omap24xxcam_clock_on(cam);
+       err = omap24xxcam_sensor_if_enable(cam);
+       if (err) {
+               dev_err(cam->dev, "sensor interface could not be enabled at "
+                       "initialisation, %d\n", err);
+               cam->sdev = NULL;
+               goto out;
+       }
+       /* power up sensor during sensor initialization */
+       vidioc_int_s_power(sdev, 1);
+       err = vidioc_int_dev_init(sdev);
+       if (err) {
+               dev_err(cam->dev, "cannot initialize sensor, error %d\n", err);
+               /* Sensor init failed --- it's nonexistent to us! */
+               cam->sdev = NULL;
+               goto out;
+       }
+       dev_info(cam->dev, "sensor is %s\n", sdev->name);
+ out:
+       omap24xxcam_sensor_if_disable(cam);
+       omap24xxcam_clock_off(cam);
+       vidioc_int_s_power(sdev, 0);
+       return err;
+ }
+ static void omap24xxcam_sensor_exit(struct omap24xxcam_device *cam)
+ {
+       if (cam->sdev)
+               vidioc_int_dev_exit(cam->sdev);
+ }
+ static void omap24xxcam_sensor_disable(struct omap24xxcam_device *cam)
+ {
+       omap24xxcam_sensor_if_disable(cam);
+       omap24xxcam_clock_off(cam);
+       vidioc_int_s_power(cam->sdev, 0);
+ }
+ /*
+  * Power-up and configure camera sensor. It's ready for capturing now.
+  */
+ static int omap24xxcam_sensor_enable(struct omap24xxcam_device *cam)
+ {
+       int rval;
+       omap24xxcam_clock_on(cam);
+       omap24xxcam_sensor_if_enable(cam);
+       rval = vidioc_int_s_power(cam->sdev, 1);
+       if (rval)
+               goto out;
+       rval = vidioc_int_init(cam->sdev);
+       if (rval)
+               goto out;
+       return 0;
+ out:
+       omap24xxcam_sensor_disable(cam);
+       return rval;
+ }
+ static void omap24xxcam_sensor_reset_work(struct work_struct *work)
+ {
+       struct omap24xxcam_device *cam =
+               container_of(work, struct omap24xxcam_device,
+                            sensor_reset_work);
+       if (atomic_read(&cam->reset_disable))
+               return;
+       omap24xxcam_capture_stop(cam);
+       if (vidioc_int_reset(cam->sdev) == 0) {
+               vidioc_int_init(cam->sdev);
+       } else {
+               /* Can't reset it by vidioc_int_reset. */
+               omap24xxcam_sensor_disable(cam);
+               omap24xxcam_sensor_enable(cam);
+       }
+       omap24xxcam_capture_cont(cam);
+ }
+ /*
+  *
+  * IOCTL interface.
+  *
+  */
+ static int vidioc_querycap(struct file *file, void *fh,
+                          struct v4l2_capability *cap)
+ {
+       struct omap24xxcam_fh *ofh = fh;
+       struct omap24xxcam_device *cam = ofh->cam;
+       strlcpy(cap->driver, CAM_NAME, sizeof(cap->driver));
+       strlcpy(cap->card, cam->vfd->name, sizeof(cap->card));
+       cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+       return 0;
+ }
+ static int vidioc_enum_fmt_vid_cap(struct file *file, void *fh,
+                                  struct v4l2_fmtdesc *f)
+ {
+       struct omap24xxcam_fh *ofh = fh;
+       struct omap24xxcam_device *cam = ofh->cam;
+       int rval;
+       rval = vidioc_int_enum_fmt_cap(cam->sdev, f);
+       return rval;
+ }
+ static int vidioc_g_fmt_vid_cap(struct file *file, void *fh,
+                               struct v4l2_format *f)
+ {
+       struct omap24xxcam_fh *ofh = fh;
+       struct omap24xxcam_device *cam = ofh->cam;
+       int rval;
+       mutex_lock(&cam->mutex);
+       rval = vidioc_int_g_fmt_cap(cam->sdev, f);
+       mutex_unlock(&cam->mutex);
+       return rval;
+ }
+ static int vidioc_s_fmt_vid_cap(struct file *file, void *fh,
+                               struct v4l2_format *f)
+ {
+       struct omap24xxcam_fh *ofh = fh;
+       struct omap24xxcam_device *cam = ofh->cam;
+       int rval;
+       mutex_lock(&cam->mutex);
+       if (cam->streaming) {
+               rval = -EBUSY;
+               goto out;
+       }
+       rval = vidioc_int_s_fmt_cap(cam->sdev, f);
+ out:
+       mutex_unlock(&cam->mutex);
+       if (!rval) {
+               mutex_lock(&ofh->vbq.vb_lock);
+               ofh->pix = f->fmt.pix;
+               mutex_unlock(&ofh->vbq.vb_lock);
+       }
+       memset(f, 0, sizeof(*f));
+       vidioc_g_fmt_vid_cap(file, fh, f);
+       return rval;
+ }
+ static int vidioc_try_fmt_vid_cap(struct file *file, void *fh,
+                                 struct v4l2_format *f)
+ {
+       struct omap24xxcam_fh *ofh = fh;
+       struct omap24xxcam_device *cam = ofh->cam;
+       int rval;
+       mutex_lock(&cam->mutex);
+       rval = vidioc_int_try_fmt_cap(cam->sdev, f);
+       mutex_unlock(&cam->mutex);
+       return rval;
+ }
+ static int vidioc_reqbufs(struct file *file, void *fh,
+                         struct v4l2_requestbuffers *b)
+ {
+       struct omap24xxcam_fh *ofh = fh;
+       struct omap24xxcam_device *cam = ofh->cam;
+       int rval;
+       mutex_lock(&cam->mutex);
+       if (cam->streaming) {
+               mutex_unlock(&cam->mutex);
+               return -EBUSY;
+       }
+       omap24xxcam_vbq_free_mmap_buffers(&ofh->vbq);
+       mutex_unlock(&cam->mutex);
+       rval = videobuf_reqbufs(&ofh->vbq, b);
+       /*
+        * Either videobuf_reqbufs failed or the buffers are not
+        * memory-mapped (which would need special attention).
+        */
+       if (rval < 0 || b->memory != V4L2_MEMORY_MMAP)
+               goto out;
+       rval = omap24xxcam_vbq_alloc_mmap_buffers(&ofh->vbq, rval);
+       if (rval)
+               omap24xxcam_vbq_free_mmap_buffers(&ofh->vbq);
+ out:
+       return rval;
+ }
+ static int vidioc_querybuf(struct file *file, void *fh,
+                          struct v4l2_buffer *b)
+ {
+       struct omap24xxcam_fh *ofh = fh;
+       return videobuf_querybuf(&ofh->vbq, b);
+ }
+ static int vidioc_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
+ {
+       struct omap24xxcam_fh *ofh = fh;
+       return videobuf_qbuf(&ofh->vbq, b);
+ }
+ static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)
+ {
+       struct omap24xxcam_fh *ofh = fh;
+       struct omap24xxcam_device *cam = ofh->cam;
+       struct videobuf_buffer *vb;
+       int rval;
+ videobuf_dqbuf_again:
+       rval = videobuf_dqbuf(&ofh->vbq, b, file->f_flags & O_NONBLOCK);
+       if (rval)
+               goto out;
+       vb = ofh->vbq.bufs[b->index];
+       mutex_lock(&cam->mutex);
+       /* _needs_reset returns -EIO if reset is required. */
+       rval = vidioc_int_g_needs_reset(cam->sdev, (void *)vb->baddr);
+       mutex_unlock(&cam->mutex);
+       if (rval == -EIO)
+               schedule_work(&cam->sensor_reset_work);
+       else
+               rval = 0;
+ out:
+       /*
+        * This is a hack. We don't want to show -EIO to the user
+        * space. Requeue the buffer and try again if we're not doing
+        * this in non-blocking mode.
+        */
+       if (rval == -EIO) {
+               videobuf_qbuf(&ofh->vbq, b);
+               if (!(file->f_flags & O_NONBLOCK))
+                       goto videobuf_dqbuf_again;
+               /*
+                * We don't have a videobuf_buffer now --- maybe next
+                * time...
+                */
+               rval = -EAGAIN;
+       }
+       return rval;
+ }
+ static int vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type i)
+ {
+       struct omap24xxcam_fh *ofh = fh;
+       struct omap24xxcam_device *cam = ofh->cam;
+       int rval;
+       mutex_lock(&cam->mutex);
+       if (cam->streaming) {
+               rval = -EBUSY;
+               goto out;
+       }
+       rval = omap24xxcam_sensor_if_enable(cam);
+       if (rval) {
+               dev_dbg(cam->dev, "vidioc_int_g_ifparm failed\n");
+               goto out;
+       }
+       rval = videobuf_streamon(&ofh->vbq);
+       if (!rval) {
+               cam->streaming = file;
+               sysfs_notify(&cam->dev->kobj, NULL, "streaming");
+       }
+ out:
+       mutex_unlock(&cam->mutex);
+       return rval;
+ }
+ static int vidioc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i)
+ {
+       struct omap24xxcam_fh *ofh = fh;
+       struct omap24xxcam_device *cam = ofh->cam;
+       struct videobuf_queue *q = &ofh->vbq;
+       int rval;
+       atomic_inc(&cam->reset_disable);
 -      flush_work_sync(&cam->sensor_reset_work);
++      flush_work(&cam->sensor_reset_work);
+       rval = videobuf_streamoff(q);
+       if (!rval) {
+               mutex_lock(&cam->mutex);
+               cam->streaming = NULL;
+               mutex_unlock(&cam->mutex);
+               sysfs_notify(&cam->dev->kobj, NULL, "streaming");
+       }
+       atomic_dec(&cam->reset_disable);
+       return rval;
+ }
+ static int vidioc_enum_input(struct file *file, void *fh,
+                            struct v4l2_input *inp)
+ {
+       if (inp->index > 0)
+               return -EINVAL;
+       strlcpy(inp->name, "camera", sizeof(inp->name));
+       inp->type = V4L2_INPUT_TYPE_CAMERA;
+       return 0;
+ }
+ static int vidioc_g_input(struct file *file, void *fh, unsigned int *i)
+ {
+       *i = 0;
+       return 0;
+ }
+ static int vidioc_s_input(struct file *file, void *fh, unsigned int i)
+ {
+       if (i > 0)
+               return -EINVAL;
+       return 0;
+ }
+ static int vidioc_queryctrl(struct file *file, void *fh,
+                           struct v4l2_queryctrl *a)
+ {
+       struct omap24xxcam_fh *ofh = fh;
+       struct omap24xxcam_device *cam = ofh->cam;
+       int rval;
+       rval = vidioc_int_queryctrl(cam->sdev, a);
+       return rval;
+ }
+ static int vidioc_g_ctrl(struct file *file, void *fh,
+                        struct v4l2_control *a)
+ {
+       struct omap24xxcam_fh *ofh = fh;
+       struct omap24xxcam_device *cam = ofh->cam;
+       int rval;
+       mutex_lock(&cam->mutex);
+       rval = vidioc_int_g_ctrl(cam->sdev, a);
+       mutex_unlock(&cam->mutex);
+       return rval;
+ }
+ static int vidioc_s_ctrl(struct file *file, void *fh,
+                        struct v4l2_control *a)
+ {
+       struct omap24xxcam_fh *ofh = fh;
+       struct omap24xxcam_device *cam = ofh->cam;
+       int rval;
+       mutex_lock(&cam->mutex);
+       rval = vidioc_int_s_ctrl(cam->sdev, a);
+       mutex_unlock(&cam->mutex);
+       return rval;
+ }
+ static int vidioc_g_parm(struct file *file, void *fh,
+                        struct v4l2_streamparm *a) {
+       struct omap24xxcam_fh *ofh = fh;
+       struct omap24xxcam_device *cam = ofh->cam;
+       int rval;
+       mutex_lock(&cam->mutex);
+       rval = vidioc_int_g_parm(cam->sdev, a);
+       mutex_unlock(&cam->mutex);
+       return rval;
+ }
+ static int vidioc_s_parm(struct file *file, void *fh,
+                        struct v4l2_streamparm *a)
+ {
+       struct omap24xxcam_fh *ofh = fh;
+       struct omap24xxcam_device *cam = ofh->cam;
+       struct v4l2_streamparm old_streamparm;
+       int rval;
+       mutex_lock(&cam->mutex);
+       if (cam->streaming) {
+               rval = -EBUSY;
+               goto out;
+       }
+       old_streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       rval = vidioc_int_g_parm(cam->sdev, &old_streamparm);
+       if (rval)
+               goto out;
+       rval = vidioc_int_s_parm(cam->sdev, a);
+       if (rval)
+               goto out;
+       rval = omap24xxcam_sensor_if_enable(cam);
+       /*
+        * Revert to old streaming parameters if enabling sensor
+        * interface with the new ones failed.
+        */
+       if (rval)
+               vidioc_int_s_parm(cam->sdev, &old_streamparm);
+ out:
+       mutex_unlock(&cam->mutex);
+       return rval;
+ }
+ /*
+  *
+  * File operations.
+  *
+  */
+ static unsigned int omap24xxcam_poll(struct file *file,
+                                    struct poll_table_struct *wait)
+ {
+       struct omap24xxcam_fh *fh = file->private_data;
+       struct omap24xxcam_device *cam = fh->cam;
+       struct videobuf_buffer *vb;
+       mutex_lock(&cam->mutex);
+       if (cam->streaming != file) {
+               mutex_unlock(&cam->mutex);
+               return POLLERR;
+       }
+       mutex_unlock(&cam->mutex);
+       mutex_lock(&fh->vbq.vb_lock);
+       if (list_empty(&fh->vbq.stream)) {
+               mutex_unlock(&fh->vbq.vb_lock);
+               return POLLERR;
+       }
+       vb = list_entry(fh->vbq.stream.next, struct videobuf_buffer, stream);
+       mutex_unlock(&fh->vbq.vb_lock);
+       poll_wait(file, &vb->done, wait);
+       if (vb->state == VIDEOBUF_DONE || vb->state == VIDEOBUF_ERROR)
+               return POLLIN | POLLRDNORM;
+       return 0;
+ }
+ static int omap24xxcam_mmap_buffers(struct file *file,
+                                   struct vm_area_struct *vma)
+ {
+       struct omap24xxcam_fh *fh = file->private_data;
+       struct omap24xxcam_device *cam = fh->cam;
+       struct videobuf_queue *vbq = &fh->vbq;
+       unsigned int first, last, size, i, j;
+       int err = 0;
+       mutex_lock(&cam->mutex);
+       if (cam->streaming) {
+               mutex_unlock(&cam->mutex);
+               return -EBUSY;
+       }
+       mutex_unlock(&cam->mutex);
+       mutex_lock(&vbq->vb_lock);
+       /* look for first buffer to map */
+       for (first = 0; first < VIDEO_MAX_FRAME; first++) {
+               if (NULL == vbq->bufs[first])
+                       continue;
+               if (V4L2_MEMORY_MMAP != vbq->bufs[first]->memory)
+                       continue;
+               if (vbq->bufs[first]->boff == (vma->vm_pgoff << PAGE_SHIFT))
+                       break;
+       }
+       /* look for last buffer to map */
+       for (size = 0, last = first; last < VIDEO_MAX_FRAME; last++) {
+               if (NULL == vbq->bufs[last])
+                       continue;
+               if (V4L2_MEMORY_MMAP != vbq->bufs[last]->memory)
+                       continue;
+               size += vbq->bufs[last]->bsize;
+               if (size == (vma->vm_end - vma->vm_start))
+                       break;
+       }
+       size = 0;
+       for (i = first; i <= last && i < VIDEO_MAX_FRAME; i++) {
+               struct videobuf_dmabuf *dma = videobuf_to_dma(vbq->bufs[i]);
+               for (j = 0; j < dma->sglen; j++) {
+                       err = remap_pfn_range(
+                               vma, vma->vm_start + size,
+                               page_to_pfn(sg_page(&dma->sglist[j])),
+                               sg_dma_len(&dma->sglist[j]), vma->vm_page_prot);
+                       if (err)
+                               goto out;
+                       size += sg_dma_len(&dma->sglist[j]);
+               }
+       }
+ out:
+       mutex_unlock(&vbq->vb_lock);
+       return err;
+ }
+ static int omap24xxcam_mmap(struct file *file, struct vm_area_struct *vma)
+ {
+       struct omap24xxcam_fh *fh = file->private_data;
+       int rval;
+       /* let the video-buf mapper check arguments and set-up structures */
+       rval = videobuf_mmap_mapper(&fh->vbq, vma);
+       if (rval)
+               return rval;
+       vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+       /* do mapping to our allocated buffers */
+       rval = omap24xxcam_mmap_buffers(file, vma);
+       /*
+        * In case of error, free vma->vm_private_data allocated by
+        * videobuf_mmap_mapper.
+        */
+       if (rval)
+               kfree(vma->vm_private_data);
+       return rval;
+ }
+ static int omap24xxcam_open(struct file *file)
+ {
+       struct omap24xxcam_device *cam = omap24xxcam.priv;
+       struct omap24xxcam_fh *fh;
+       struct v4l2_format format;
+       if (!cam || !cam->vfd)
+               return -ENODEV;
+       fh = kzalloc(sizeof(*fh), GFP_KERNEL);
+       if (fh == NULL)
+               return -ENOMEM;
+       mutex_lock(&cam->mutex);
+       if (cam->sdev == NULL || !try_module_get(cam->sdev->module)) {
+               mutex_unlock(&cam->mutex);
+               goto out_try_module_get;
+       }
+       if (atomic_inc_return(&cam->users) == 1) {
+               omap24xxcam_hwinit(cam);
+               if (omap24xxcam_sensor_enable(cam)) {
+                       mutex_unlock(&cam->mutex);
+                       goto out_omap24xxcam_sensor_enable;
+               }
+       }
+       mutex_unlock(&cam->mutex);
+       fh->cam = cam;
+       mutex_lock(&cam->mutex);
+       vidioc_int_g_fmt_cap(cam->sdev, &format);
+       mutex_unlock(&cam->mutex);
+       /* FIXME: how about fh->pix when there are more users? */
+       fh->pix = format.fmt.pix;
+       file->private_data = fh;
+       spin_lock_init(&fh->vbq_lock);
+       videobuf_queue_sg_init(&fh->vbq, &omap24xxcam_vbq_ops, NULL,
+                               &fh->vbq_lock, V4L2_BUF_TYPE_VIDEO_CAPTURE,
+                               V4L2_FIELD_NONE,
+                               sizeof(struct videobuf_buffer), fh, NULL);
+       return 0;
+ out_omap24xxcam_sensor_enable:
+       omap24xxcam_poweron_reset(cam);
+       module_put(cam->sdev->module);
+ out_try_module_get:
+       kfree(fh);
+       return -ENODEV;
+ }
+ static int omap24xxcam_release(struct file *file)
+ {
+       struct omap24xxcam_fh *fh = file->private_data;
+       struct omap24xxcam_device *cam = fh->cam;
+       atomic_inc(&cam->reset_disable);
 -      flush_work_sync(&cam->sensor_reset_work);
++      flush_work(&cam->sensor_reset_work);
+       /* stop streaming capture */
+       videobuf_streamoff(&fh->vbq);
+       mutex_lock(&cam->mutex);
+       if (cam->streaming == file) {
+               cam->streaming = NULL;
+               mutex_unlock(&cam->mutex);
+               sysfs_notify(&cam->dev->kobj, NULL, "streaming");
+       } else {
+               mutex_unlock(&cam->mutex);
+       }
+       atomic_dec(&cam->reset_disable);
+       omap24xxcam_vbq_free_mmap_buffers(&fh->vbq);
+       /*
+        * Make sure the reset work we might have scheduled is not
+        * pending! It may be run *only* if we have users. (And it may
+        * not be scheduled anymore since streaming is already
+        * disabled.)
+        */
++      flush_work(&cam->sensor_reset_work);
+       mutex_lock(&cam->mutex);
+       if (atomic_dec_return(&cam->users) == 0) {
+               omap24xxcam_sensor_disable(cam);
+               omap24xxcam_poweron_reset(cam);
+       }
+       mutex_unlock(&cam->mutex);
+       file->private_data = NULL;
+       module_put(cam->sdev->module);
+       kfree(fh);
+       return 0;
+ }
+ static struct v4l2_file_operations omap24xxcam_fops = {
+       .ioctl   = video_ioctl2,
+       .poll    = omap24xxcam_poll,
+       .mmap    = omap24xxcam_mmap,
+       .open    = omap24xxcam_open,
+       .release = omap24xxcam_release,
+ };
+ /*
+  *
+  * Power management.
+  *
+  */
+ #ifdef CONFIG_PM
+ static int omap24xxcam_suspend(struct platform_device *pdev, pm_message_t state)
+ {
+       struct omap24xxcam_device *cam = platform_get_drvdata(pdev);
+       if (atomic_read(&cam->users) == 0)
+               return 0;
+       if (!atomic_read(&cam->reset_disable))
+               omap24xxcam_capture_stop(cam);
+       omap24xxcam_sensor_disable(cam);
+       omap24xxcam_poweron_reset(cam);
+       return 0;
+ }
+ static int omap24xxcam_resume(struct platform_device *pdev)
+ {
+       struct omap24xxcam_device *cam = platform_get_drvdata(pdev);
+       if (atomic_read(&cam->users) == 0)
+               return 0;
+       omap24xxcam_hwinit(cam);
+       omap24xxcam_sensor_enable(cam);
+       if (!atomic_read(&cam->reset_disable))
+               omap24xxcam_capture_cont(cam);
+       return 0;
+ }
+ #endif /* CONFIG_PM */
+ static const struct v4l2_ioctl_ops omap24xxcam_ioctl_fops = {
+       .vidioc_querycap        = vidioc_querycap,
+       .vidioc_enum_fmt_vid_cap        = vidioc_enum_fmt_vid_cap,
+       .vidioc_g_fmt_vid_cap   = vidioc_g_fmt_vid_cap,
+       .vidioc_s_fmt_vid_cap   = vidioc_s_fmt_vid_cap,
+       .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+       .vidioc_reqbufs         = vidioc_reqbufs,
+       .vidioc_querybuf        = vidioc_querybuf,
+       .vidioc_qbuf            = vidioc_qbuf,
+       .vidioc_dqbuf           = vidioc_dqbuf,
+       .vidioc_streamon        = vidioc_streamon,
+       .vidioc_streamoff       = vidioc_streamoff,
+       .vidioc_enum_input      = vidioc_enum_input,
+       .vidioc_g_input         = vidioc_g_input,
+       .vidioc_s_input         = vidioc_s_input,
+       .vidioc_queryctrl       = vidioc_queryctrl,
+       .vidioc_g_ctrl          = vidioc_g_ctrl,
+       .vidioc_s_ctrl          = vidioc_s_ctrl,
+       .vidioc_g_parm          = vidioc_g_parm,
+       .vidioc_s_parm          = vidioc_s_parm,
+ };
+ /*
+  *
+  * Camera device (i.e. /dev/video).
+  *
+  */
+ static int omap24xxcam_device_register(struct v4l2_int_device *s)
+ {
+       struct omap24xxcam_device *cam = s->u.slave->master->priv;
+       struct video_device *vfd;
+       int rval;
+       /* We already have a slave. */
+       if (cam->sdev)
+               return -EBUSY;
+       cam->sdev = s;
+       if (device_create_file(cam->dev, &dev_attr_streaming) != 0) {
+               dev_err(cam->dev, "could not register sysfs entry\n");
+               rval = -EBUSY;
+               goto err;
+       }
+       /* initialize the video_device struct */
+       vfd = cam->vfd = video_device_alloc();
+       if (!vfd) {
+               dev_err(cam->dev, "could not allocate video device struct\n");
+               rval = -ENOMEM;
+               goto err;
+       }
+       vfd->release = video_device_release;
+       vfd->parent = cam->dev;
+       strlcpy(vfd->name, CAM_NAME, sizeof(vfd->name));
+       vfd->fops                = &omap24xxcam_fops;
+       vfd->ioctl_ops           = &omap24xxcam_ioctl_fops;
+       omap24xxcam_hwinit(cam);
+       rval = omap24xxcam_sensor_init(cam);
+       if (rval)
+               goto err;
+       if (video_register_device(vfd, VFL_TYPE_GRABBER, video_nr) < 0) {
+               dev_err(cam->dev, "could not register V4L device\n");
+               rval = -EBUSY;
+               goto err;
+       }
+       omap24xxcam_poweron_reset(cam);
+       dev_info(cam->dev, "registered device %s\n",
+                video_device_node_name(vfd));
+       return 0;
+ err:
+       omap24xxcam_device_unregister(s);
+       return rval;
+ }
+ static void omap24xxcam_device_unregister(struct v4l2_int_device *s)
+ {
+       struct omap24xxcam_device *cam = s->u.slave->master->priv;
+       omap24xxcam_sensor_exit(cam);
+       if (cam->vfd) {
+               if (!video_is_registered(cam->vfd)) {
+                       /*
+                        * The device was never registered, so release the
+                        * video_device struct directly.
+                        */
+                       video_device_release(cam->vfd);
+               } else {
+                       /*
+                        * The unregister function will release the
+                        * video_device struct as well as
+                        * unregistering it.
+                        */
+                       video_unregister_device(cam->vfd);
+               }
+               cam->vfd = NULL;
+       }
+       device_remove_file(cam->dev, &dev_attr_streaming);
+       cam->sdev = NULL;
+ }
+ static struct v4l2_int_master omap24xxcam_master = {
+       .attach = omap24xxcam_device_register,
+       .detach = omap24xxcam_device_unregister,
+ };
+ static struct v4l2_int_device omap24xxcam = {
+       .module = THIS_MODULE,
+       .name   = CAM_NAME,
+       .type   = v4l2_int_type_master,
+       .u      = {
+               .master = &omap24xxcam_master
+       },
+ };
+ /*
+  *
+  * Driver initialisation and deinitialisation.
+  *
+  */
+ static int __devinit omap24xxcam_probe(struct platform_device *pdev)
+ {
+       struct omap24xxcam_device *cam;
+       struct resource *mem;
+       int irq;
+       cam = kzalloc(sizeof(*cam), GFP_KERNEL);
+       if (!cam) {
+               dev_err(&pdev->dev, "could not allocate memory\n");
+               goto err;
+       }
+       platform_set_drvdata(pdev, cam);
+       cam->dev = &pdev->dev;
+       /*
+        * Impose a lower limit on the amount of memory allocated for
+        * capture. We require at least enough memory to double-buffer
+        * QVGA (300KB).
+        */
+       if (capture_mem < 320 * 240 * 2 * 2)
+               capture_mem = 320 * 240 * 2 * 2;
+       cam->capture_mem = capture_mem;
+       /* request the mem region for the camera registers */
+       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!mem) {
+               dev_err(cam->dev, "no mem resource?\n");
+               goto err;
+       }
+       if (!request_mem_region(mem->start, resource_size(mem), pdev->name)) {
+               dev_err(cam->dev,
+                       "cannot reserve camera register I/O region\n");
+               goto err;
+       }
+       cam->mmio_base_phys = mem->start;
+       cam->mmio_size = resource_size(mem);
+       /* map the region */
+       cam->mmio_base = ioremap_nocache(cam->mmio_base_phys, cam->mmio_size);
+       if (!cam->mmio_base) {
+               dev_err(cam->dev, "cannot map camera register I/O region\n");
+               goto err;
+       }
+       irq = platform_get_irq(pdev, 0);
+       if (irq <= 0) {
+               dev_err(cam->dev, "no irq for camera?\n");
+               goto err;
+       }
+       /* install the interrupt service routine */
+       if (request_irq(irq, omap24xxcam_isr, 0, CAM_NAME, cam)) {
+               dev_err(cam->dev,
+                       "could not install interrupt service routine\n");
+               goto err;
+       }
+       cam->irq = irq;
+       if (omap24xxcam_clock_get(cam))
+               goto err;
+       INIT_WORK(&cam->sensor_reset_work, omap24xxcam_sensor_reset_work);
+       mutex_init(&cam->mutex);
+       spin_lock_init(&cam->core_enable_disable_lock);
+       omap24xxcam_sgdma_init(&cam->sgdma,
+                              cam->mmio_base + CAMDMA_REG_OFFSET,
+                              omap24xxcam_stalled_dma_reset,
+                              (unsigned long)cam);
+       omap24xxcam.priv = cam;
+       if (v4l2_int_device_register(&omap24xxcam))
+               goto err;
+       return 0;
+ err:
+       omap24xxcam_remove(pdev);
+       return -ENODEV;
+ }
+ static int omap24xxcam_remove(struct platform_device *pdev)
+ {
+       struct omap24xxcam_device *cam = platform_get_drvdata(pdev);
+       if (!cam)
+               return 0;
+       if (omap24xxcam.priv != NULL)
+               v4l2_int_device_unregister(&omap24xxcam);
+       omap24xxcam.priv = NULL;
+       omap24xxcam_clock_put(cam);
+       if (cam->irq) {
+               free_irq(cam->irq, cam);
+               cam->irq = 0;
+       }
+       if (cam->mmio_base) {
+               iounmap((void *)cam->mmio_base);
+               cam->mmio_base = 0;
+       }
+       if (cam->mmio_base_phys) {
+               release_mem_region(cam->mmio_base_phys, cam->mmio_size);
+               cam->mmio_base_phys = 0;
+       }
+       kfree(cam);
+       return 0;
+ }
+ static struct platform_driver omap24xxcam_driver = {
+       .probe   = omap24xxcam_probe,
+       .remove  = omap24xxcam_remove,
+ #ifdef CONFIG_PM
+       .suspend = omap24xxcam_suspend,
+       .resume  = omap24xxcam_resume,
+ #endif
+       .driver  = {
+               .name = CAM_NAME,
+               .owner = THIS_MODULE,
+       },
+ };
+ module_platform_driver(omap24xxcam_driver);
+ MODULE_AUTHOR("Sakari Ailus <sakari.ailus@nokia.com>");
+ MODULE_DESCRIPTION("OMAP24xx Video for Linux camera driver");
+ MODULE_LICENSE("GPL");
+ MODULE_VERSION(OMAP24XXCAM_VERSION);
+ module_param(video_nr, int, 0);
+ MODULE_PARM_DESC(video_nr,
+                "Minor number for video device (-1 ==> auto assign)");
+ module_param(capture_mem, int, 0);
+ MODULE_PARM_DESC(capture_mem, "Maximum amount of memory for capture "
+                "buffers (default 4800kiB)");
index 0000000,d7aa513..99640d8
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,2243 +1,2245 @@@
+ /*
+  * isp.c
+  *
+  * TI OMAP3 ISP - Core
+  *
+  * Copyright (C) 2006-2010 Nokia Corporation
+  * Copyright (C) 2007-2009 Texas Instruments, Inc.
+  *
+  * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+  *         Sakari Ailus <sakari.ailus@iki.fi>
+  *
+  * Contributors:
+  *    Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+  *    Sakari Ailus <sakari.ailus@iki.fi>
+  *    David Cohen <dacohen@gmail.com>
+  *    Stanimir Varbanov <svarbanov@mm-sol.com>
+  *    Vimarsh Zutshi <vimarsh.zutshi@gmail.com>
+  *    Tuukka Toivonen <tuukkat76@gmail.com>
+  *    Sergio Aguirre <saaguirre@ti.com>
+  *    Antti Koskipaa <akoskipa@gmail.com>
+  *    Ivan T. Ivanov <iivanov@mm-sol.com>
+  *    RaniSuneela <r-m@ti.com>
+  *    Atanas Filipov <afilipov@mm-sol.com>
+  *    Gjorgji Rosikopulos <grosikopulos@mm-sol.com>
+  *    Hiroshi DOYU <hiroshi.doyu@nokia.com>
+  *    Nayden Kanchev <nkanchev@mm-sol.com>
+  *    Phil Carmody <ext-phil.2.carmody@nokia.com>
+  *    Artem Bityutskiy <artem.bityutskiy@nokia.com>
+  *    Dominic Curran <dcurran@ti.com>
+  *    Ilkka Myllyperkio <ilkka.myllyperkio@sofica.fi>
+  *    Pallavi Kulkarni <p-kulkarni@ti.com>
+  *    Vaibhav Hiremath <hvaibhav@ti.com>
+  *    Mohit Jalori <mjalori@ti.com>
+  *    Sameer Venkatraman <sameerv@ti.com>
+  *    Senthilvadivu Guruswamy <svadivu@ti.com>
+  *    Thara Gopinath <thara@ti.com>
+  *    Toni Leinonen <toni.leinonen@nokia.com>
+  *    Troy Laramy <t-laramy@ti.com>
+  *
+  * This program is free software; you can redistribute it and/or modify
+  * it under the terms of the GNU General Public License version 2 as
+  * published by the Free Software Foundation.
+  *
+  * 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 St, Fifth Floor, Boston, MA
+  * 02110-1301 USA
+  */
+ #include <asm/cacheflush.h>
+ #include <linux/clk.h>
+ #include <linux/delay.h>
+ #include <linux/device.h>
+ #include <linux/dma-mapping.h>
+ #include <linux/i2c.h>
+ #include <linux/interrupt.h>
+ #include <linux/module.h>
+ #include <linux/platform_device.h>
+ #include <linux/regulator/consumer.h>
+ #include <linux/slab.h>
+ #include <linux/sched.h>
+ #include <linux/vmalloc.h>
+ #include <media/v4l2-common.h>
+ #include <media/v4l2-device.h>
++#include <plat/cpu.h>
++
+ #include "isp.h"
+ #include "ispreg.h"
+ #include "ispccdc.h"
+ #include "isppreview.h"
+ #include "ispresizer.h"
+ #include "ispcsi2.h"
+ #include "ispccp2.h"
+ #include "isph3a.h"
+ #include "isphist.h"
+ static unsigned int autoidle;
+ module_param(autoidle, int, 0444);
+ MODULE_PARM_DESC(autoidle, "Enable OMAP3ISP AUTOIDLE support");
+ static void isp_save_ctx(struct isp_device *isp);
+ static void isp_restore_ctx(struct isp_device *isp);
+ static const struct isp_res_mapping isp_res_maps[] = {
+       {
+               .isp_rev = ISP_REVISION_2_0,
+               .map = 1 << OMAP3_ISP_IOMEM_MAIN |
+                      1 << OMAP3_ISP_IOMEM_CCP2 |
+                      1 << OMAP3_ISP_IOMEM_CCDC |
+                      1 << OMAP3_ISP_IOMEM_HIST |
+                      1 << OMAP3_ISP_IOMEM_H3A |
+                      1 << OMAP3_ISP_IOMEM_PREV |
+                      1 << OMAP3_ISP_IOMEM_RESZ |
+                      1 << OMAP3_ISP_IOMEM_SBL |
+                      1 << OMAP3_ISP_IOMEM_CSI2A_REGS1 |
+                      1 << OMAP3_ISP_IOMEM_CSIPHY2,
+       },
+       {
+               .isp_rev = ISP_REVISION_15_0,
+               .map = 1 << OMAP3_ISP_IOMEM_MAIN |
+                      1 << OMAP3_ISP_IOMEM_CCP2 |
+                      1 << OMAP3_ISP_IOMEM_CCDC |
+                      1 << OMAP3_ISP_IOMEM_HIST |
+                      1 << OMAP3_ISP_IOMEM_H3A |
+                      1 << OMAP3_ISP_IOMEM_PREV |
+                      1 << OMAP3_ISP_IOMEM_RESZ |
+                      1 << OMAP3_ISP_IOMEM_SBL |
+                      1 << OMAP3_ISP_IOMEM_CSI2A_REGS1 |
+                      1 << OMAP3_ISP_IOMEM_CSIPHY2 |
+                      1 << OMAP3_ISP_IOMEM_CSI2A_REGS2 |
+                      1 << OMAP3_ISP_IOMEM_CSI2C_REGS1 |
+                      1 << OMAP3_ISP_IOMEM_CSIPHY1 |
+                      1 << OMAP3_ISP_IOMEM_CSI2C_REGS2,
+       },
+ };
+ /* Structure for saving/restoring ISP module registers */
+ static struct isp_reg isp_reg_list[] = {
+       {OMAP3_ISP_IOMEM_MAIN, ISP_SYSCONFIG, 0},
+       {OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, 0},
+       {OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL, 0},
+       {0, ISP_TOK_TERM, 0}
+ };
+ /*
+  * omap3isp_flush - Post pending L3 bus writes by doing a register readback
+  * @isp: OMAP3 ISP device
+  *
+  * In order to force posting of pending writes, we need to write and
+  * readback the same register, in this case the revision register.
+  *
+  * See this link for reference:
+  *   http://www.mail-archive.com/linux-omap@vger.kernel.org/msg08149.html
+  */
+ void omap3isp_flush(struct isp_device *isp)
+ {
+       isp_reg_writel(isp, 0, OMAP3_ISP_IOMEM_MAIN, ISP_REVISION);
+       isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_REVISION);
+ }
+ /*
+  * isp_enable_interrupts - Enable ISP interrupts.
+  * @isp: OMAP3 ISP device
+  */
+ static void isp_enable_interrupts(struct isp_device *isp)
+ {
+       static const u32 irq = IRQ0ENABLE_CSIA_IRQ
+                            | IRQ0ENABLE_CSIB_IRQ
+                            | IRQ0ENABLE_CCDC_LSC_PREF_ERR_IRQ
+                            | IRQ0ENABLE_CCDC_LSC_DONE_IRQ
+                            | IRQ0ENABLE_CCDC_VD0_IRQ
+                            | IRQ0ENABLE_CCDC_VD1_IRQ
+                            | IRQ0ENABLE_HS_VS_IRQ
+                            | IRQ0ENABLE_HIST_DONE_IRQ
+                            | IRQ0ENABLE_H3A_AWB_DONE_IRQ
+                            | IRQ0ENABLE_H3A_AF_DONE_IRQ
+                            | IRQ0ENABLE_PRV_DONE_IRQ
+                            | IRQ0ENABLE_RSZ_DONE_IRQ;
+       isp_reg_writel(isp, irq, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS);
+       isp_reg_writel(isp, irq, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE);
+ }
+ /*
+  * isp_disable_interrupts - Disable ISP interrupts.
+  * @isp: OMAP3 ISP device
+  */
+ static void isp_disable_interrupts(struct isp_device *isp)
+ {
+       isp_reg_writel(isp, 0, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE);
+ }
+ /**
+  * isp_set_xclk - Configures the specified cam_xclk to the desired frequency.
+  * @isp: OMAP3 ISP device
+  * @xclk: Desired frequency of the clock in Hz. 0 = stable low, 1 is stable high
+  * @xclksel: XCLK to configure (0 = A, 1 = B).
+  *
+  * Configures the specified MCLK divisor in the ISP timing control register
+  * (TCTRL_CTRL) to generate the desired xclk clock value.
+  *
+  * Divisor = cam_mclk_hz / xclk
+  *
+  * Returns the final frequency that is actually being generated
+  **/
+ static u32 isp_set_xclk(struct isp_device *isp, u32 xclk, u8 xclksel)
+ {
+       u32 divisor;
+       u32 currentxclk;
+       unsigned long mclk_hz;
+       if (!omap3isp_get(isp))
+               return 0;
+       mclk_hz = clk_get_rate(isp->clock[ISP_CLK_CAM_MCLK]);
+       if (xclk >= mclk_hz) {
+               divisor = ISPTCTRL_CTRL_DIV_BYPASS;
+               currentxclk = mclk_hz;
+       } else if (xclk >= 2) {
+               divisor = mclk_hz / xclk;
+               if (divisor >= ISPTCTRL_CTRL_DIV_BYPASS)
+                       divisor = ISPTCTRL_CTRL_DIV_BYPASS - 1;
+               currentxclk = mclk_hz / divisor;
+       } else {
+               divisor = xclk;
+               currentxclk = 0;
+       }
+       switch (xclksel) {
+       case ISP_XCLK_A:
+               isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL,
+                               ISPTCTRL_CTRL_DIVA_MASK,
+                               divisor << ISPTCTRL_CTRL_DIVA_SHIFT);
+               dev_dbg(isp->dev, "isp_set_xclk(): cam_xclka set to %d Hz\n",
+                       currentxclk);
+               break;
+       case ISP_XCLK_B:
+               isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL,
+                               ISPTCTRL_CTRL_DIVB_MASK,
+                               divisor << ISPTCTRL_CTRL_DIVB_SHIFT);
+               dev_dbg(isp->dev, "isp_set_xclk(): cam_xclkb set to %d Hz\n",
+                       currentxclk);
+               break;
+       case ISP_XCLK_NONE:
+       default:
+               omap3isp_put(isp);
+               dev_dbg(isp->dev, "ISP_ERR: isp_set_xclk(): Invalid requested "
+                       "xclk. Must be 0 (A) or 1 (B).\n");
+               return -EINVAL;
+       }
+       /* Do we go from stable whatever to clock? */
+       if (divisor >= 2 && isp->xclk_divisor[xclksel - 1] < 2)
+               omap3isp_get(isp);
+       /* Stopping the clock. */
+       else if (divisor < 2 && isp->xclk_divisor[xclksel - 1] >= 2)
+               omap3isp_put(isp);
+       isp->xclk_divisor[xclksel - 1] = divisor;
+       omap3isp_put(isp);
+       return currentxclk;
+ }
+ /*
+  * isp_core_init - ISP core settings
+  * @isp: OMAP3 ISP device
+  * @idle: Consider idle state.
+  *
+  * Set the power settings for the ISP and SBL bus and cConfigure the HS/VS
+  * interrupt source.
+  *
+  * We need to configure the HS/VS interrupt source before interrupts get
+  * enabled, as the sensor might be free-running and the ISP default setting
+  * (HS edge) would put an unnecessary burden on the CPU.
+  */
+ static void isp_core_init(struct isp_device *isp, int idle)
+ {
+       isp_reg_writel(isp,
+                      ((idle ? ISP_SYSCONFIG_MIDLEMODE_SMARTSTANDBY :
+                               ISP_SYSCONFIG_MIDLEMODE_FORCESTANDBY) <<
+                       ISP_SYSCONFIG_MIDLEMODE_SHIFT) |
+                       ((isp->revision == ISP_REVISION_15_0) ?
+                         ISP_SYSCONFIG_AUTOIDLE : 0),
+                      OMAP3_ISP_IOMEM_MAIN, ISP_SYSCONFIG);
+       isp_reg_writel(isp,
+                      (isp->autoidle ? ISPCTRL_SBL_AUTOIDLE : 0) |
+                      ISPCTRL_SYNC_DETECT_VSRISE,
+                      OMAP3_ISP_IOMEM_MAIN, ISP_CTRL);
+ }
+ /*
+  * Configure the bridge and lane shifter. Valid inputs are
+  *
+  * CCDC_INPUT_PARALLEL: Parallel interface
+  * CCDC_INPUT_CSI2A: CSI2a receiver
+  * CCDC_INPUT_CCP2B: CCP2b receiver
+  * CCDC_INPUT_CSI2C: CSI2c receiver
+  *
+  * The bridge and lane shifter are configured according to the selected input
+  * and the ISP platform data.
+  */
+ void omap3isp_configure_bridge(struct isp_device *isp,
+                              enum ccdc_input_entity input,
+                              const struct isp_parallel_platform_data *pdata,
+                              unsigned int shift, unsigned int bridge)
+ {
+       u32 ispctrl_val;
+       ispctrl_val  = isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL);
+       ispctrl_val &= ~ISPCTRL_SHIFT_MASK;
+       ispctrl_val &= ~ISPCTRL_PAR_CLK_POL_INV;
+       ispctrl_val &= ~ISPCTRL_PAR_SER_CLK_SEL_MASK;
+       ispctrl_val &= ~ISPCTRL_PAR_BRIDGE_MASK;
+       ispctrl_val |= bridge;
+       switch (input) {
+       case CCDC_INPUT_PARALLEL:
+               ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_PARALLEL;
+               ispctrl_val |= pdata->clk_pol << ISPCTRL_PAR_CLK_POL_SHIFT;
+               shift += pdata->data_lane_shift * 2;
+               break;
+       case CCDC_INPUT_CSI2A:
+               ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_CSIA;
+               break;
+       case CCDC_INPUT_CCP2B:
+               ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_CSIB;
+               break;
+       case CCDC_INPUT_CSI2C:
+               ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_CSIC;
+               break;
+       default:
+               return;
+       }
+       ispctrl_val |= ((shift/2) << ISPCTRL_SHIFT_SHIFT) & ISPCTRL_SHIFT_MASK;
+       isp_reg_writel(isp, ispctrl_val, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL);
+ }
+ void omap3isp_hist_dma_done(struct isp_device *isp)
+ {
+       if (omap3isp_ccdc_busy(&isp->isp_ccdc) ||
+           omap3isp_stat_pcr_busy(&isp->isp_hist)) {
+               /* Histogram cannot be enabled in this frame anymore */
+               atomic_set(&isp->isp_hist.buf_err, 1);
+               dev_dbg(isp->dev, "hist: Out of synchronization with "
+                                 "CCDC. Ignoring next buffer.\n");
+       }
+ }
+ static inline void isp_isr_dbg(struct isp_device *isp, u32 irqstatus)
+ {
+       static const char *name[] = {
+               "CSIA_IRQ",
+               "res1",
+               "res2",
+               "CSIB_LCM_IRQ",
+               "CSIB_IRQ",
+               "res5",
+               "res6",
+               "res7",
+               "CCDC_VD0_IRQ",
+               "CCDC_VD1_IRQ",
+               "CCDC_VD2_IRQ",
+               "CCDC_ERR_IRQ",
+               "H3A_AF_DONE_IRQ",
+               "H3A_AWB_DONE_IRQ",
+               "res14",
+               "res15",
+               "HIST_DONE_IRQ",
+               "CCDC_LSC_DONE",
+               "CCDC_LSC_PREFETCH_COMPLETED",
+               "CCDC_LSC_PREFETCH_ERROR",
+               "PRV_DONE_IRQ",
+               "CBUFF_IRQ",
+               "res22",
+               "res23",
+               "RSZ_DONE_IRQ",
+               "OVF_IRQ",
+               "res26",
+               "res27",
+               "MMU_ERR_IRQ",
+               "OCP_ERR_IRQ",
+               "SEC_ERR_IRQ",
+               "HS_VS_IRQ",
+       };
+       int i;
+       dev_dbg(isp->dev, "ISP IRQ: ");
+       for (i = 0; i < ARRAY_SIZE(name); i++) {
+               if ((1 << i) & irqstatus)
+                       printk(KERN_CONT "%s ", name[i]);
+       }
+       printk(KERN_CONT "\n");
+ }
+ static void isp_isr_sbl(struct isp_device *isp)
+ {
+       struct device *dev = isp->dev;
+       struct isp_pipeline *pipe;
+       u32 sbl_pcr;
+       /*
+        * Handle shared buffer logic overflows for video buffers.
+        * ISPSBL_PCR_CCDCPRV_2_RSZ_OVF can be safely ignored.
+        */
+       sbl_pcr = isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_PCR);
+       isp_reg_writel(isp, sbl_pcr, OMAP3_ISP_IOMEM_SBL, ISPSBL_PCR);
+       sbl_pcr &= ~ISPSBL_PCR_CCDCPRV_2_RSZ_OVF;
+       if (sbl_pcr)
+               dev_dbg(dev, "SBL overflow (PCR = 0x%08x)\n", sbl_pcr);
+       if (sbl_pcr & ISPSBL_PCR_CSIB_WBL_OVF) {
+               pipe = to_isp_pipeline(&isp->isp_ccp2.subdev.entity);
+               if (pipe != NULL)
+                       pipe->error = true;
+       }
+       if (sbl_pcr & ISPSBL_PCR_CSIA_WBL_OVF) {
+               pipe = to_isp_pipeline(&isp->isp_csi2a.subdev.entity);
+               if (pipe != NULL)
+                       pipe->error = true;
+       }
+       if (sbl_pcr & ISPSBL_PCR_CCDC_WBL_OVF) {
+               pipe = to_isp_pipeline(&isp->isp_ccdc.subdev.entity);
+               if (pipe != NULL)
+                       pipe->error = true;
+       }
+       if (sbl_pcr & ISPSBL_PCR_PRV_WBL_OVF) {
+               pipe = to_isp_pipeline(&isp->isp_prev.subdev.entity);
+               if (pipe != NULL)
+                       pipe->error = true;
+       }
+       if (sbl_pcr & (ISPSBL_PCR_RSZ1_WBL_OVF
+                      | ISPSBL_PCR_RSZ2_WBL_OVF
+                      | ISPSBL_PCR_RSZ3_WBL_OVF
+                      | ISPSBL_PCR_RSZ4_WBL_OVF)) {
+               pipe = to_isp_pipeline(&isp->isp_res.subdev.entity);
+               if (pipe != NULL)
+                       pipe->error = true;
+       }
+       if (sbl_pcr & ISPSBL_PCR_H3A_AF_WBL_OVF)
+               omap3isp_stat_sbl_overflow(&isp->isp_af);
+       if (sbl_pcr & ISPSBL_PCR_H3A_AEAWB_WBL_OVF)
+               omap3isp_stat_sbl_overflow(&isp->isp_aewb);
+ }
+ /*
+  * isp_isr - Interrupt Service Routine for Camera ISP module.
+  * @irq: Not used currently.
+  * @_isp: Pointer to the OMAP3 ISP device
+  *
+  * Handles the corresponding callback if plugged in.
+  *
+  * Returns IRQ_HANDLED when IRQ was correctly handled, or IRQ_NONE when the
+  * IRQ wasn't handled.
+  */
+ static irqreturn_t isp_isr(int irq, void *_isp)
+ {
+       static const u32 ccdc_events = IRQ0STATUS_CCDC_LSC_PREF_ERR_IRQ |
+                                      IRQ0STATUS_CCDC_LSC_DONE_IRQ |
+                                      IRQ0STATUS_CCDC_VD0_IRQ |
+                                      IRQ0STATUS_CCDC_VD1_IRQ |
+                                      IRQ0STATUS_HS_VS_IRQ;
+       struct isp_device *isp = _isp;
+       u32 irqstatus;
+       irqstatus = isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS);
+       isp_reg_writel(isp, irqstatus, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS);
+       isp_isr_sbl(isp);
+       if (irqstatus & IRQ0STATUS_CSIA_IRQ)
+               omap3isp_csi2_isr(&isp->isp_csi2a);
+       if (irqstatus & IRQ0STATUS_CSIB_IRQ)
+               omap3isp_ccp2_isr(&isp->isp_ccp2);
+       if (irqstatus & IRQ0STATUS_CCDC_VD0_IRQ) {
+               if (isp->isp_ccdc.output & CCDC_OUTPUT_PREVIEW)
+                       omap3isp_preview_isr_frame_sync(&isp->isp_prev);
+               if (isp->isp_ccdc.output & CCDC_OUTPUT_RESIZER)
+                       omap3isp_resizer_isr_frame_sync(&isp->isp_res);
+               omap3isp_stat_isr_frame_sync(&isp->isp_aewb);
+               omap3isp_stat_isr_frame_sync(&isp->isp_af);
+               omap3isp_stat_isr_frame_sync(&isp->isp_hist);
+       }
+       if (irqstatus & ccdc_events)
+               omap3isp_ccdc_isr(&isp->isp_ccdc, irqstatus & ccdc_events);
+       if (irqstatus & IRQ0STATUS_PRV_DONE_IRQ) {
+               if (isp->isp_prev.output & PREVIEW_OUTPUT_RESIZER)
+                       omap3isp_resizer_isr_frame_sync(&isp->isp_res);
+               omap3isp_preview_isr(&isp->isp_prev);
+       }
+       if (irqstatus & IRQ0STATUS_RSZ_DONE_IRQ)
+               omap3isp_resizer_isr(&isp->isp_res);
+       if (irqstatus & IRQ0STATUS_H3A_AWB_DONE_IRQ)
+               omap3isp_stat_isr(&isp->isp_aewb);
+       if (irqstatus & IRQ0STATUS_H3A_AF_DONE_IRQ)
+               omap3isp_stat_isr(&isp->isp_af);
+       if (irqstatus & IRQ0STATUS_HIST_DONE_IRQ)
+               omap3isp_stat_isr(&isp->isp_hist);
+       omap3isp_flush(isp);
+ #if defined(DEBUG) && defined(ISP_ISR_DEBUG)
+       isp_isr_dbg(isp, irqstatus);
+ #endif
+       return IRQ_HANDLED;
+ }
+ /* -----------------------------------------------------------------------------
+  * Pipeline power management
+  *
+  * Entities must be powered up when part of a pipeline that contains at least
+  * one open video device node.
+  *
+  * To achieve this use the entity use_count field to track the number of users.
+  * For entities corresponding to video device nodes the use_count field stores
+  * the users count of the node. For entities corresponding to subdevs the
+  * use_count field stores the total number of users of all video device nodes
+  * in the pipeline.
+  *
+  * The omap3isp_pipeline_pm_use() function must be called in the open() and
+  * close() handlers of video device nodes. It increments or decrements the use
+  * count of all subdev entities in the pipeline.
+  *
+  * To react to link management on powered pipelines, the link setup notification
+  * callback updates the use count of all entities in the source and sink sides
+  * of the link.
+  */
+ /*
+  * isp_pipeline_pm_use_count - Count the number of users of a pipeline
+  * @entity: The entity
+  *
+  * Return the total number of users of all video device nodes in the pipeline.
+  */
+ static int isp_pipeline_pm_use_count(struct media_entity *entity)
+ {
+       struct media_entity_graph graph;
+       int use = 0;
+       media_entity_graph_walk_start(&graph, entity);
+       while ((entity = media_entity_graph_walk_next(&graph))) {
+               if (media_entity_type(entity) == MEDIA_ENT_T_DEVNODE)
+                       use += entity->use_count;
+       }
+       return use;
+ }
+ /*
+  * isp_pipeline_pm_power_one - Apply power change to an entity
+  * @entity: The entity
+  * @change: Use count change
+  *
+  * Change the entity use count by @change. If the entity is a subdev update its
+  * power state by calling the core::s_power operation when the use count goes
+  * from 0 to != 0 or from != 0 to 0.
+  *
+  * Return 0 on success or a negative error code on failure.
+  */
+ static int isp_pipeline_pm_power_one(struct media_entity *entity, int change)
+ {
+       struct v4l2_subdev *subdev;
+       int ret;
+       subdev = media_entity_type(entity) == MEDIA_ENT_T_V4L2_SUBDEV
+              ? media_entity_to_v4l2_subdev(entity) : NULL;
+       if (entity->use_count == 0 && change > 0 && subdev != NULL) {
+               ret = v4l2_subdev_call(subdev, core, s_power, 1);
+               if (ret < 0 && ret != -ENOIOCTLCMD)
+                       return ret;
+       }
+       entity->use_count += change;
+       WARN_ON(entity->use_count < 0);
+       if (entity->use_count == 0 && change < 0 && subdev != NULL)
+               v4l2_subdev_call(subdev, core, s_power, 0);
+       return 0;
+ }
+ /*
+  * isp_pipeline_pm_power - Apply power change to all entities in a pipeline
+  * @entity: The entity
+  * @change: Use count change
+  *
+  * Walk the pipeline to update the use count and the power state of all non-node
+  * entities.
+  *
+  * Return 0 on success or a negative error code on failure.
+  */
+ static int isp_pipeline_pm_power(struct media_entity *entity, int change)
+ {
+       struct media_entity_graph graph;
+       struct media_entity *first = entity;
+       int ret = 0;
+       if (!change)
+               return 0;
+       media_entity_graph_walk_start(&graph, entity);
+       while (!ret && (entity = media_entity_graph_walk_next(&graph)))
+               if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE)
+                       ret = isp_pipeline_pm_power_one(entity, change);
+       if (!ret)
+               return 0;
+       media_entity_graph_walk_start(&graph, first);
+       while ((first = media_entity_graph_walk_next(&graph))
+              && first != entity)
+               if (media_entity_type(first) != MEDIA_ENT_T_DEVNODE)
+                       isp_pipeline_pm_power_one(first, -change);
+       return ret;
+ }
+ /*
+  * omap3isp_pipeline_pm_use - Update the use count of an entity
+  * @entity: The entity
+  * @use: Use (1) or stop using (0) the entity
+  *
+  * Update the use count of all entities in the pipeline and power entities on or
+  * off accordingly.
+  *
+  * Return 0 on success or a negative error code on failure. Powering entities
+  * off is assumed to never fail. No failure can occur when the use parameter is
+  * set to 0.
+  */
+ int omap3isp_pipeline_pm_use(struct media_entity *entity, int use)
+ {
+       int change = use ? 1 : -1;
+       int ret;
+       mutex_lock(&entity->parent->graph_mutex);
+       /* Apply use count to node. */
+       entity->use_count += change;
+       WARN_ON(entity->use_count < 0);
+       /* Apply power change to connected non-nodes. */
+       ret = isp_pipeline_pm_power(entity, change);
+       if (ret < 0)
+               entity->use_count -= change;
+       mutex_unlock(&entity->parent->graph_mutex);
+       return ret;
+ }
+ /*
+  * isp_pipeline_link_notify - Link management notification callback
+  * @source: Pad at the start of the link
+  * @sink: Pad at the end of the link
+  * @flags: New link flags that will be applied
+  *
+  * React to link management on powered pipelines by updating the use count of
+  * all entities in the source and sink sides of the link. Entities are powered
+  * on or off accordingly.
+  *
+  * Return 0 on success or a negative error code on failure. Powering entities
+  * off is assumed to never fail. This function will not fail for disconnection
+  * events.
+  */
+ static int isp_pipeline_link_notify(struct media_pad *source,
+                                   struct media_pad *sink, u32 flags)
+ {
+       int source_use = isp_pipeline_pm_use_count(source->entity);
+       int sink_use = isp_pipeline_pm_use_count(sink->entity);
+       int ret;
+       if (!(flags & MEDIA_LNK_FL_ENABLED)) {
+               /* Powering off entities is assumed to never fail. */
+               isp_pipeline_pm_power(source->entity, -sink_use);
+               isp_pipeline_pm_power(sink->entity, -source_use);
+               return 0;
+       }
+       ret = isp_pipeline_pm_power(source->entity, sink_use);
+       if (ret < 0)
+               return ret;
+       ret = isp_pipeline_pm_power(sink->entity, source_use);
+       if (ret < 0)
+               isp_pipeline_pm_power(source->entity, -sink_use);
+       return ret;
+ }
+ /* -----------------------------------------------------------------------------
+  * Pipeline stream management
+  */
+ /*
+  * isp_pipeline_enable - Enable streaming on a pipeline
+  * @pipe: ISP pipeline
+  * @mode: Stream mode (single shot or continuous)
+  *
+  * Walk the entities chain starting at the pipeline output video node and start
+  * all modules in the chain in the given mode.
+  *
+  * Return 0 if successful, or the return value of the failed video::s_stream
+  * operation otherwise.
+  */
+ static int isp_pipeline_enable(struct isp_pipeline *pipe,
+                              enum isp_pipeline_stream_state mode)
+ {
+       struct isp_device *isp = pipe->output->isp;
+       struct media_entity *entity;
+       struct media_pad *pad;
+       struct v4l2_subdev *subdev;
+       unsigned long flags;
+       int ret;
+       /* If the preview engine crashed it might not respond to read/write
+        * operations on the L4 bus. This would result in a bus fault and a
+        * kernel oops. Refuse to start streaming in that case. This check must
+        * be performed before the loop below to avoid starting entities if the
+        * pipeline won't start anyway (those entities would then likely fail to
+        * stop, making the problem worse).
+        */
+       if ((pipe->entities & isp->crashed) &
+           (1U << isp->isp_prev.subdev.entity.id))
+               return -EIO;
+       spin_lock_irqsave(&pipe->lock, flags);
+       pipe->state &= ~(ISP_PIPELINE_IDLE_INPUT | ISP_PIPELINE_IDLE_OUTPUT);
+       spin_unlock_irqrestore(&pipe->lock, flags);
+       pipe->do_propagation = false;
+       entity = &pipe->output->video.entity;
+       while (1) {
+               pad = &entity->pads[0];
+               if (!(pad->flags & MEDIA_PAD_FL_SINK))
+                       break;
+               pad = media_entity_remote_source(pad);
+               if (pad == NULL ||
+                   media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+                       break;
+               entity = pad->entity;
+               subdev = media_entity_to_v4l2_subdev(entity);
+               ret = v4l2_subdev_call(subdev, video, s_stream, mode);
+               if (ret < 0 && ret != -ENOIOCTLCMD)
+                       return ret;
+               if (subdev == &isp->isp_ccdc.subdev) {
+                       v4l2_subdev_call(&isp->isp_aewb.subdev, video,
+                                       s_stream, mode);
+                       v4l2_subdev_call(&isp->isp_af.subdev, video,
+                                       s_stream, mode);
+                       v4l2_subdev_call(&isp->isp_hist.subdev, video,
+                                       s_stream, mode);
+                       pipe->do_propagation = true;
+               }
+       }
+       return 0;
+ }
+ static int isp_pipeline_wait_resizer(struct isp_device *isp)
+ {
+       return omap3isp_resizer_busy(&isp->isp_res);
+ }
+ static int isp_pipeline_wait_preview(struct isp_device *isp)
+ {
+       return omap3isp_preview_busy(&isp->isp_prev);
+ }
+ static int isp_pipeline_wait_ccdc(struct isp_device *isp)
+ {
+       return omap3isp_stat_busy(&isp->isp_af)
+           || omap3isp_stat_busy(&isp->isp_aewb)
+           || omap3isp_stat_busy(&isp->isp_hist)
+           || omap3isp_ccdc_busy(&isp->isp_ccdc);
+ }
+ #define ISP_STOP_TIMEOUT      msecs_to_jiffies(1000)
+ static int isp_pipeline_wait(struct isp_device *isp,
+                            int(*busy)(struct isp_device *isp))
+ {
+       unsigned long timeout = jiffies + ISP_STOP_TIMEOUT;
+       while (!time_after(jiffies, timeout)) {
+               if (!busy(isp))
+                       return 0;
+       }
+       return 1;
+ }
+ /*
+  * isp_pipeline_disable - Disable streaming on a pipeline
+  * @pipe: ISP pipeline
+  *
+  * Walk the entities chain starting at the pipeline output video node and stop
+  * all modules in the chain. Wait synchronously for the modules to be stopped if
+  * necessary.
+  *
+  * Return 0 if all modules have been properly stopped, or -ETIMEDOUT if a module
+  * can't be stopped (in which case a software reset of the ISP is probably
+  * necessary).
+  */
+ static int isp_pipeline_disable(struct isp_pipeline *pipe)
+ {
+       struct isp_device *isp = pipe->output->isp;
+       struct media_entity *entity;
+       struct media_pad *pad;
+       struct v4l2_subdev *subdev;
+       int failure = 0;
+       int ret;
+       /*
+        * We need to stop all the modules after CCDC first or they'll
+        * never stop since they may not get a full frame from CCDC.
+        */
+       entity = &pipe->output->video.entity;
+       while (1) {
+               pad = &entity->pads[0];
+               if (!(pad->flags & MEDIA_PAD_FL_SINK))
+                       break;
+               pad = media_entity_remote_source(pad);
+               if (pad == NULL ||
+                   media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+                       break;
+               entity = pad->entity;
+               subdev = media_entity_to_v4l2_subdev(entity);
+               if (subdev == &isp->isp_ccdc.subdev) {
+                       v4l2_subdev_call(&isp->isp_aewb.subdev,
+                                        video, s_stream, 0);
+                       v4l2_subdev_call(&isp->isp_af.subdev,
+                                        video, s_stream, 0);
+                       v4l2_subdev_call(&isp->isp_hist.subdev,
+                                        video, s_stream, 0);
+               }
+               v4l2_subdev_call(subdev, video, s_stream, 0);
+               if (subdev == &isp->isp_res.subdev)
+                       ret = isp_pipeline_wait(isp, isp_pipeline_wait_resizer);
+               else if (subdev == &isp->isp_prev.subdev)
+                       ret = isp_pipeline_wait(isp, isp_pipeline_wait_preview);
+               else if (subdev == &isp->isp_ccdc.subdev)
+                       ret = isp_pipeline_wait(isp, isp_pipeline_wait_ccdc);
+               else
+                       ret = 0;
+               if (ret) {
+                       dev_info(isp->dev, "Unable to stop %s\n", subdev->name);
+                       /* If the entity failed to stopped, assume it has
+                        * crashed. Mark it as such, the ISP will be reset when
+                        * applications will release it.
+                        */
+                       isp->crashed |= 1U << subdev->entity.id;
+                       failure = -ETIMEDOUT;
+               }
+       }
+       return failure;
+ }
+ /*
+  * omap3isp_pipeline_set_stream - Enable/disable streaming on a pipeline
+  * @pipe: ISP pipeline
+  * @state: Stream state (stopped, single shot or continuous)
+  *
+  * Set the pipeline to the given stream state. Pipelines can be started in
+  * single-shot or continuous mode.
+  *
+  * Return 0 if successful, or the return value of the failed video::s_stream
+  * operation otherwise. The pipeline state is not updated when the operation
+  * fails, except when stopping the pipeline.
+  */
+ int omap3isp_pipeline_set_stream(struct isp_pipeline *pipe,
+                                enum isp_pipeline_stream_state state)
+ {
+       int ret;
+       if (state == ISP_PIPELINE_STREAM_STOPPED)
+               ret = isp_pipeline_disable(pipe);
+       else
+               ret = isp_pipeline_enable(pipe, state);
+       if (ret == 0 || state == ISP_PIPELINE_STREAM_STOPPED)
+               pipe->stream_state = state;
+       return ret;
+ }
+ /*
+  * isp_pipeline_resume - Resume streaming on a pipeline
+  * @pipe: ISP pipeline
+  *
+  * Resume video output and input and re-enable pipeline.
+  */
+ static void isp_pipeline_resume(struct isp_pipeline *pipe)
+ {
+       int singleshot = pipe->stream_state == ISP_PIPELINE_STREAM_SINGLESHOT;
+       omap3isp_video_resume(pipe->output, !singleshot);
+       if (singleshot)
+               omap3isp_video_resume(pipe->input, 0);
+       isp_pipeline_enable(pipe, pipe->stream_state);
+ }
+ /*
+  * isp_pipeline_suspend - Suspend streaming on a pipeline
+  * @pipe: ISP pipeline
+  *
+  * Suspend pipeline.
+  */
+ static void isp_pipeline_suspend(struct isp_pipeline *pipe)
+ {
+       isp_pipeline_disable(pipe);
+ }
+ /*
+  * isp_pipeline_is_last - Verify if entity has an enabled link to the output
+  *                      video node
+  * @me: ISP module's media entity
+  *
+  * Returns 1 if the entity has an enabled link to the output video node or 0
+  * otherwise. It's true only while pipeline can have no more than one output
+  * node.
+  */
+ static int isp_pipeline_is_last(struct media_entity *me)
+ {
+       struct isp_pipeline *pipe;
+       struct media_pad *pad;
+       if (!me->pipe)
+               return 0;
+       pipe = to_isp_pipeline(me);
+       if (pipe->stream_state == ISP_PIPELINE_STREAM_STOPPED)
+               return 0;
+       pad = media_entity_remote_source(&pipe->output->pad);
+       return pad->entity == me;
+ }
+ /*
+  * isp_suspend_module_pipeline - Suspend pipeline to which belongs the module
+  * @me: ISP module's media entity
+  *
+  * Suspend the whole pipeline if module's entity has an enabled link to the
+  * output video node. It works only while pipeline can have no more than one
+  * output node.
+  */
+ static void isp_suspend_module_pipeline(struct media_entity *me)
+ {
+       if (isp_pipeline_is_last(me))
+               isp_pipeline_suspend(to_isp_pipeline(me));
+ }
+ /*
+  * isp_resume_module_pipeline - Resume pipeline to which belongs the module
+  * @me: ISP module's media entity
+  *
+  * Resume the whole pipeline if module's entity has an enabled link to the
+  * output video node. It works only while pipeline can have no more than one
+  * output node.
+  */
+ static void isp_resume_module_pipeline(struct media_entity *me)
+ {
+       if (isp_pipeline_is_last(me))
+               isp_pipeline_resume(to_isp_pipeline(me));
+ }
+ /*
+  * isp_suspend_modules - Suspend ISP submodules.
+  * @isp: OMAP3 ISP device
+  *
+  * Returns 0 if suspend left in idle state all the submodules properly,
+  * or returns 1 if a general Reset is required to suspend the submodules.
+  */
+ static int isp_suspend_modules(struct isp_device *isp)
+ {
+       unsigned long timeout;
+       omap3isp_stat_suspend(&isp->isp_aewb);
+       omap3isp_stat_suspend(&isp->isp_af);
+       omap3isp_stat_suspend(&isp->isp_hist);
+       isp_suspend_module_pipeline(&isp->isp_res.subdev.entity);
+       isp_suspend_module_pipeline(&isp->isp_prev.subdev.entity);
+       isp_suspend_module_pipeline(&isp->isp_ccdc.subdev.entity);
+       isp_suspend_module_pipeline(&isp->isp_csi2a.subdev.entity);
+       isp_suspend_module_pipeline(&isp->isp_ccp2.subdev.entity);
+       timeout = jiffies + ISP_STOP_TIMEOUT;
+       while (omap3isp_stat_busy(&isp->isp_af)
+           || omap3isp_stat_busy(&isp->isp_aewb)
+           || omap3isp_stat_busy(&isp->isp_hist)
+           || omap3isp_preview_busy(&isp->isp_prev)
+           || omap3isp_resizer_busy(&isp->isp_res)
+           || omap3isp_ccdc_busy(&isp->isp_ccdc)) {
+               if (time_after(jiffies, timeout)) {
+                       dev_info(isp->dev, "can't stop modules.\n");
+                       return 1;
+               }
+               msleep(1);
+       }
+       return 0;
+ }
+ /*
+  * isp_resume_modules - Resume ISP submodules.
+  * @isp: OMAP3 ISP device
+  */
+ static void isp_resume_modules(struct isp_device *isp)
+ {
+       omap3isp_stat_resume(&isp->isp_aewb);
+       omap3isp_stat_resume(&isp->isp_af);
+       omap3isp_stat_resume(&isp->isp_hist);
+       isp_resume_module_pipeline(&isp->isp_res.subdev.entity);
+       isp_resume_module_pipeline(&isp->isp_prev.subdev.entity);
+       isp_resume_module_pipeline(&isp->isp_ccdc.subdev.entity);
+       isp_resume_module_pipeline(&isp->isp_csi2a.subdev.entity);
+       isp_resume_module_pipeline(&isp->isp_ccp2.subdev.entity);
+ }
+ /*
+  * isp_reset - Reset ISP with a timeout wait for idle.
+  * @isp: OMAP3 ISP device
+  */
+ static int isp_reset(struct isp_device *isp)
+ {
+       unsigned long timeout = 0;
+       isp_reg_writel(isp,
+                      isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_SYSCONFIG)
+                      | ISP_SYSCONFIG_SOFTRESET,
+                      OMAP3_ISP_IOMEM_MAIN, ISP_SYSCONFIG);
+       while (!(isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN,
+                              ISP_SYSSTATUS) & 0x1)) {
+               if (timeout++ > 10000) {
+                       dev_alert(isp->dev, "cannot reset ISP\n");
+                       return -ETIMEDOUT;
+               }
+               udelay(1);
+       }
+       isp->crashed = 0;
+       return 0;
+ }
+ /*
+  * isp_save_context - Saves the values of the ISP module registers.
+  * @isp: OMAP3 ISP device
+  * @reg_list: Structure containing pairs of register address and value to
+  *            modify on OMAP.
+  */
+ static void
+ isp_save_context(struct isp_device *isp, struct isp_reg *reg_list)
+ {
+       struct isp_reg *next = reg_list;
+       for (; next->reg != ISP_TOK_TERM; next++)
+               next->val = isp_reg_readl(isp, next->mmio_range, next->reg);
+ }
+ /*
+  * isp_restore_context - Restores the values of the ISP module registers.
+  * @isp: OMAP3 ISP device
+  * @reg_list: Structure containing pairs of register address and value to
+  *            modify on OMAP.
+  */
+ static void
+ isp_restore_context(struct isp_device *isp, struct isp_reg *reg_list)
+ {
+       struct isp_reg *next = reg_list;
+       for (; next->reg != ISP_TOK_TERM; next++)
+               isp_reg_writel(isp, next->val, next->mmio_range, next->reg);
+ }
+ /*
+  * isp_save_ctx - Saves ISP, CCDC, HIST, H3A, PREV, RESZ & MMU context.
+  * @isp: OMAP3 ISP device
+  *
+  * Routine for saving the context of each module in the ISP.
+  * CCDC, HIST, H3A, PREV, RESZ and MMU.
+  */
+ static void isp_save_ctx(struct isp_device *isp)
+ {
+       isp_save_context(isp, isp_reg_list);
+       omap_iommu_save_ctx(isp->dev);
+ }
+ /*
+  * isp_restore_ctx - Restores ISP, CCDC, HIST, H3A, PREV, RESZ & MMU context.
+  * @isp: OMAP3 ISP device
+  *
+  * Routine for restoring the context of each module in the ISP.
+  * CCDC, HIST, H3A, PREV, RESZ and MMU.
+  */
+ static void isp_restore_ctx(struct isp_device *isp)
+ {
+       isp_restore_context(isp, isp_reg_list);
+       omap_iommu_restore_ctx(isp->dev);
+       omap3isp_ccdc_restore_context(isp);
+       omap3isp_preview_restore_context(isp);
+ }
+ /* -----------------------------------------------------------------------------
+  * SBL resources management
+  */
+ #define OMAP3_ISP_SBL_READ    (OMAP3_ISP_SBL_CSI1_READ | \
+                                OMAP3_ISP_SBL_CCDC_LSC_READ | \
+                                OMAP3_ISP_SBL_PREVIEW_READ | \
+                                OMAP3_ISP_SBL_RESIZER_READ)
+ #define OMAP3_ISP_SBL_WRITE   (OMAP3_ISP_SBL_CSI1_WRITE | \
+                                OMAP3_ISP_SBL_CSI2A_WRITE | \
+                                OMAP3_ISP_SBL_CSI2C_WRITE | \
+                                OMAP3_ISP_SBL_CCDC_WRITE | \
+                                OMAP3_ISP_SBL_PREVIEW_WRITE)
+ void omap3isp_sbl_enable(struct isp_device *isp, enum isp_sbl_resource res)
+ {
+       u32 sbl = 0;
+       isp->sbl_resources |= res;
+       if (isp->sbl_resources & OMAP3_ISP_SBL_CSI1_READ)
+               sbl |= ISPCTRL_SBL_SHARED_RPORTA;
+       if (isp->sbl_resources & OMAP3_ISP_SBL_CCDC_LSC_READ)
+               sbl |= ISPCTRL_SBL_SHARED_RPORTB;
+       if (isp->sbl_resources & OMAP3_ISP_SBL_CSI2C_WRITE)
+               sbl |= ISPCTRL_SBL_SHARED_WPORTC;
+       if (isp->sbl_resources & OMAP3_ISP_SBL_RESIZER_WRITE)
+               sbl |= ISPCTRL_SBL_WR0_RAM_EN;
+       if (isp->sbl_resources & OMAP3_ISP_SBL_WRITE)
+               sbl |= ISPCTRL_SBL_WR1_RAM_EN;
+       if (isp->sbl_resources & OMAP3_ISP_SBL_READ)
+               sbl |= ISPCTRL_SBL_RD_RAM_EN;
+       isp_reg_set(isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, sbl);
+ }
+ void omap3isp_sbl_disable(struct isp_device *isp, enum isp_sbl_resource res)
+ {
+       u32 sbl = 0;
+       isp->sbl_resources &= ~res;
+       if (!(isp->sbl_resources & OMAP3_ISP_SBL_CSI1_READ))
+               sbl |= ISPCTRL_SBL_SHARED_RPORTA;
+       if (!(isp->sbl_resources & OMAP3_ISP_SBL_CCDC_LSC_READ))
+               sbl |= ISPCTRL_SBL_SHARED_RPORTB;
+       if (!(isp->sbl_resources & OMAP3_ISP_SBL_CSI2C_WRITE))
+               sbl |= ISPCTRL_SBL_SHARED_WPORTC;
+       if (!(isp->sbl_resources & OMAP3_ISP_SBL_RESIZER_WRITE))
+               sbl |= ISPCTRL_SBL_WR0_RAM_EN;
+       if (!(isp->sbl_resources & OMAP3_ISP_SBL_WRITE))
+               sbl |= ISPCTRL_SBL_WR1_RAM_EN;
+       if (!(isp->sbl_resources & OMAP3_ISP_SBL_READ))
+               sbl |= ISPCTRL_SBL_RD_RAM_EN;
+       isp_reg_clr(isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, sbl);
+ }
+ /*
+  * isp_module_sync_idle - Helper to sync module with its idle state
+  * @me: ISP submodule's media entity
+  * @wait: ISP submodule's wait queue for streamoff/interrupt synchronization
+  * @stopping: flag which tells module wants to stop
+  *
+  * This function checks if ISP submodule needs to wait for next interrupt. If
+  * yes, makes the caller to sleep while waiting for such event.
+  */
+ int omap3isp_module_sync_idle(struct media_entity *me, wait_queue_head_t *wait,
+                             atomic_t *stopping)
+ {
+       struct isp_pipeline *pipe = to_isp_pipeline(me);
+       if (pipe->stream_state == ISP_PIPELINE_STREAM_STOPPED ||
+           (pipe->stream_state == ISP_PIPELINE_STREAM_SINGLESHOT &&
+            !isp_pipeline_ready(pipe)))
+               return 0;
+       /*
+        * atomic_set() doesn't include memory barrier on ARM platform for SMP
+        * scenario. We'll call it here to avoid race conditions.
+        */
+       atomic_set(stopping, 1);
+       smp_mb();
+       /*
+        * If module is the last one, it's writing to memory. In this case,
+        * it's necessary to check if the module is already paused due to
+        * DMA queue underrun or if it has to wait for next interrupt to be
+        * idle.
+        * If it isn't the last one, the function won't sleep but *stopping
+        * will still be set to warn next submodule caller's interrupt the
+        * module wants to be idle.
+        */
+       if (isp_pipeline_is_last(me)) {
+               struct isp_video *video = pipe->output;
+               unsigned long flags;
+               spin_lock_irqsave(&video->queue->irqlock, flags);
+               if (video->dmaqueue_flags & ISP_VIDEO_DMAQUEUE_UNDERRUN) {
+                       spin_unlock_irqrestore(&video->queue->irqlock, flags);
+                       atomic_set(stopping, 0);
+                       smp_mb();
+                       return 0;
+               }
+               spin_unlock_irqrestore(&video->queue->irqlock, flags);
+               if (!wait_event_timeout(*wait, !atomic_read(stopping),
+                                       msecs_to_jiffies(1000))) {
+                       atomic_set(stopping, 0);
+                       smp_mb();
+                       return -ETIMEDOUT;
+               }
+       }
+       return 0;
+ }
+ /*
+  * omap3isp_module_sync_is_stopped - Helper to verify if module was stopping
+  * @wait: ISP submodule's wait queue for streamoff/interrupt synchronization
+  * @stopping: flag which tells module wants to stop
+  *
+  * This function checks if ISP submodule was stopping. In case of yes, it
+  * notices the caller by setting stopping to 0 and waking up the wait queue.
+  * Returns 1 if it was stopping or 0 otherwise.
+  */
+ int omap3isp_module_sync_is_stopping(wait_queue_head_t *wait,
+                                    atomic_t *stopping)
+ {
+       if (atomic_cmpxchg(stopping, 1, 0)) {
+               wake_up(wait);
+               return 1;
+       }
+       return 0;
+ }
+ /* --------------------------------------------------------------------------
+  * Clock management
+  */
+ #define ISPCTRL_CLKS_MASK     (ISPCTRL_H3A_CLK_EN | \
+                                ISPCTRL_HIST_CLK_EN | \
+                                ISPCTRL_RSZ_CLK_EN | \
+                                (ISPCTRL_CCDC_CLK_EN | ISPCTRL_CCDC_RAM_EN) | \
+                                (ISPCTRL_PREV_CLK_EN | ISPCTRL_PREV_RAM_EN))
+ static void __isp_subclk_update(struct isp_device *isp)
+ {
+       u32 clk = 0;
+       /* AEWB and AF share the same clock. */
+       if (isp->subclk_resources &
+           (OMAP3_ISP_SUBCLK_AEWB | OMAP3_ISP_SUBCLK_AF))
+               clk |= ISPCTRL_H3A_CLK_EN;
+       if (isp->subclk_resources & OMAP3_ISP_SUBCLK_HIST)
+               clk |= ISPCTRL_HIST_CLK_EN;
+       if (isp->subclk_resources & OMAP3_ISP_SUBCLK_RESIZER)
+               clk |= ISPCTRL_RSZ_CLK_EN;
+       /* NOTE: For CCDC & Preview submodules, we need to affect internal
+        *       RAM as well.
+        */
+       if (isp->subclk_resources & OMAP3_ISP_SUBCLK_CCDC)
+               clk |= ISPCTRL_CCDC_CLK_EN | ISPCTRL_CCDC_RAM_EN;
+       if (isp->subclk_resources & OMAP3_ISP_SUBCLK_PREVIEW)
+               clk |= ISPCTRL_PREV_CLK_EN | ISPCTRL_PREV_RAM_EN;
+       isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL,
+                       ISPCTRL_CLKS_MASK, clk);
+ }
+ void omap3isp_subclk_enable(struct isp_device *isp,
+                           enum isp_subclk_resource res)
+ {
+       isp->subclk_resources |= res;
+       __isp_subclk_update(isp);
+ }
+ void omap3isp_subclk_disable(struct isp_device *isp,
+                            enum isp_subclk_resource res)
+ {
+       isp->subclk_resources &= ~res;
+       __isp_subclk_update(isp);
+ }
+ /*
+  * isp_enable_clocks - Enable ISP clocks
+  * @isp: OMAP3 ISP device
+  *
+  * Return 0 if successful, or clk_enable return value if any of tthem fails.
+  */
+ static int isp_enable_clocks(struct isp_device *isp)
+ {
+       int r;
+       unsigned long rate;
+       int divisor;
+       /*
+        * cam_mclk clock chain:
+        *   dpll4 -> dpll4_m5 -> dpll4_m5x2 -> cam_mclk
+        *
+        * In OMAP3630 dpll4_m5x2 != 2 x dpll4_m5 but both are
+        * set to the same value. Hence the rate set for dpll4_m5
+        * has to be twice of what is set on OMAP3430 to get
+        * the required value for cam_mclk
+        */
+       if (cpu_is_omap3630())
+               divisor = 1;
+       else
+               divisor = 2;
+       r = clk_enable(isp->clock[ISP_CLK_CAM_ICK]);
+       if (r) {
+               dev_err(isp->dev, "clk_enable cam_ick failed\n");
+               goto out_clk_enable_ick;
+       }
+       r = clk_set_rate(isp->clock[ISP_CLK_DPLL4_M5_CK],
+                        CM_CAM_MCLK_HZ/divisor);
+       if (r) {
+               dev_err(isp->dev, "clk_set_rate for dpll4_m5_ck failed\n");
+               goto out_clk_enable_mclk;
+       }
+       r = clk_enable(isp->clock[ISP_CLK_CAM_MCLK]);
+       if (r) {
+               dev_err(isp->dev, "clk_enable cam_mclk failed\n");
+               goto out_clk_enable_mclk;
+       }
+       rate = clk_get_rate(isp->clock[ISP_CLK_CAM_MCLK]);
+       if (rate != CM_CAM_MCLK_HZ)
+               dev_warn(isp->dev, "unexpected cam_mclk rate:\n"
+                                  " expected : %d\n"
+                                  " actual   : %ld\n", CM_CAM_MCLK_HZ, rate);
+       r = clk_enable(isp->clock[ISP_CLK_CSI2_FCK]);
+       if (r) {
+               dev_err(isp->dev, "clk_enable csi2_fck failed\n");
+               goto out_clk_enable_csi2_fclk;
+       }
+       return 0;
+ out_clk_enable_csi2_fclk:
+       clk_disable(isp->clock[ISP_CLK_CAM_MCLK]);
+ out_clk_enable_mclk:
+       clk_disable(isp->clock[ISP_CLK_CAM_ICK]);
+ out_clk_enable_ick:
+       return r;
+ }
+ /*
+  * isp_disable_clocks - Disable ISP clocks
+  * @isp: OMAP3 ISP device
+  */
+ static void isp_disable_clocks(struct isp_device *isp)
+ {
+       clk_disable(isp->clock[ISP_CLK_CAM_ICK]);
+       clk_disable(isp->clock[ISP_CLK_CAM_MCLK]);
+       clk_disable(isp->clock[ISP_CLK_CSI2_FCK]);
+ }
+ static const char *isp_clocks[] = {
+       "cam_ick",
+       "cam_mclk",
+       "dpll4_m5_ck",
+       "csi2_96m_fck",
+       "l3_ick",
+ };
+ static void isp_put_clocks(struct isp_device *isp)
+ {
+       unsigned int i;
+       for (i = 0; i < ARRAY_SIZE(isp_clocks); ++i) {
+               if (isp->clock[i]) {
+                       clk_put(isp->clock[i]);
+                       isp->clock[i] = NULL;
+               }
+       }
+ }
+ static int isp_get_clocks(struct isp_device *isp)
+ {
+       struct clk *clk;
+       unsigned int i;
+       for (i = 0; i < ARRAY_SIZE(isp_clocks); ++i) {
+               clk = clk_get(isp->dev, isp_clocks[i]);
+               if (IS_ERR(clk)) {
+                       dev_err(isp->dev, "clk_get %s failed\n", isp_clocks[i]);
+                       isp_put_clocks(isp);
+                       return PTR_ERR(clk);
+               }
+               isp->clock[i] = clk;
+       }
+       return 0;
+ }
+ /*
+  * omap3isp_get - Acquire the ISP resource.
+  *
+  * Initializes the clocks for the first acquire.
+  *
+  * Increment the reference count on the ISP. If the first reference is taken,
+  * enable clocks and power-up all submodules.
+  *
+  * Return a pointer to the ISP device structure, or NULL if an error occurred.
+  */
+ static struct isp_device *__omap3isp_get(struct isp_device *isp, bool irq)
+ {
+       struct isp_device *__isp = isp;
+       if (isp == NULL)
+               return NULL;
+       mutex_lock(&isp->isp_mutex);
+       if (isp->ref_count > 0)
+               goto out;
+       if (isp_enable_clocks(isp) < 0) {
+               __isp = NULL;
+               goto out;
+       }
+       /* We don't want to restore context before saving it! */
+       if (isp->has_context)
+               isp_restore_ctx(isp);
+       if (irq)
+               isp_enable_interrupts(isp);
+ out:
+       if (__isp != NULL)
+               isp->ref_count++;
+       mutex_unlock(&isp->isp_mutex);
+       return __isp;
+ }
+ struct isp_device *omap3isp_get(struct isp_device *isp)
+ {
+       return __omap3isp_get(isp, true);
+ }
+ /*
+  * omap3isp_put - Release the ISP
+  *
+  * Decrement the reference count on the ISP. If the last reference is released,
+  * power-down all submodules, disable clocks and free temporary buffers.
+  */
+ void omap3isp_put(struct isp_device *isp)
+ {
+       if (isp == NULL)
+               return;
+       mutex_lock(&isp->isp_mutex);
+       BUG_ON(isp->ref_count == 0);
+       if (--isp->ref_count == 0) {
+               isp_disable_interrupts(isp);
+               if (isp->domain) {
+                       isp_save_ctx(isp);
+                       isp->has_context = 1;
+               }
+               /* Reset the ISP if an entity has failed to stop. This is the
+                * only way to recover from such conditions.
+                */
+               if (isp->crashed)
+                       isp_reset(isp);
+               isp_disable_clocks(isp);
+       }
+       mutex_unlock(&isp->isp_mutex);
+ }
+ /* --------------------------------------------------------------------------
+  * Platform device driver
+  */
+ /*
+  * omap3isp_print_status - Prints the values of the ISP Control Module registers
+  * @isp: OMAP3 ISP device
+  */
+ #define ISP_PRINT_REGISTER(isp, name)\
+       dev_dbg(isp->dev, "###ISP " #name "=0x%08x\n", \
+               isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_##name))
+ #define SBL_PRINT_REGISTER(isp, name)\
+       dev_dbg(isp->dev, "###SBL " #name "=0x%08x\n", \
+               isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_##name))
+ void omap3isp_print_status(struct isp_device *isp)
+ {
+       dev_dbg(isp->dev, "-------------ISP Register dump--------------\n");
+       ISP_PRINT_REGISTER(isp, SYSCONFIG);
+       ISP_PRINT_REGISTER(isp, SYSSTATUS);
+       ISP_PRINT_REGISTER(isp, IRQ0ENABLE);
+       ISP_PRINT_REGISTER(isp, IRQ0STATUS);
+       ISP_PRINT_REGISTER(isp, TCTRL_GRESET_LENGTH);
+       ISP_PRINT_REGISTER(isp, TCTRL_PSTRB_REPLAY);
+       ISP_PRINT_REGISTER(isp, CTRL);
+       ISP_PRINT_REGISTER(isp, TCTRL_CTRL);
+       ISP_PRINT_REGISTER(isp, TCTRL_FRAME);
+       ISP_PRINT_REGISTER(isp, TCTRL_PSTRB_DELAY);
+       ISP_PRINT_REGISTER(isp, TCTRL_STRB_DELAY);
+       ISP_PRINT_REGISTER(isp, TCTRL_SHUT_DELAY);
+       ISP_PRINT_REGISTER(isp, TCTRL_PSTRB_LENGTH);
+       ISP_PRINT_REGISTER(isp, TCTRL_STRB_LENGTH);
+       ISP_PRINT_REGISTER(isp, TCTRL_SHUT_LENGTH);
+       SBL_PRINT_REGISTER(isp, PCR);
+       SBL_PRINT_REGISTER(isp, SDR_REQ_EXP);
+       dev_dbg(isp->dev, "--------------------------------------------\n");
+ }
+ #ifdef CONFIG_PM
+ /*
+  * Power management support.
+  *
+  * As the ISP can't properly handle an input video stream interruption on a non
+  * frame boundary, the ISP pipelines need to be stopped before sensors get
+  * suspended. However, as suspending the sensors can require a running clock,
+  * which can be provided by the ISP, the ISP can't be completely suspended
+  * before the sensor.
+  *
+  * To solve this problem power management support is split into prepare/complete
+  * and suspend/resume operations. The pipelines are stopped in prepare() and the
+  * ISP clocks get disabled in suspend(). Similarly, the clocks are reenabled in
+  * resume(), and the the pipelines are restarted in complete().
+  *
+  * TODO: PM dependencies between the ISP and sensors are not modeled explicitly
+  * yet.
+  */
+ static int isp_pm_prepare(struct device *dev)
+ {
+       struct isp_device *isp = dev_get_drvdata(dev);
+       int reset;
+       WARN_ON(mutex_is_locked(&isp->isp_mutex));
+       if (isp->ref_count == 0)
+               return 0;
+       reset = isp_suspend_modules(isp);
+       isp_disable_interrupts(isp);
+       isp_save_ctx(isp);
+       if (reset)
+               isp_reset(isp);
+       return 0;
+ }
+ static int isp_pm_suspend(struct device *dev)
+ {
+       struct isp_device *isp = dev_get_drvdata(dev);
+       WARN_ON(mutex_is_locked(&isp->isp_mutex));
+       if (isp->ref_count)
+               isp_disable_clocks(isp);
+       return 0;
+ }
+ static int isp_pm_resume(struct device *dev)
+ {
+       struct isp_device *isp = dev_get_drvdata(dev);
+       if (isp->ref_count == 0)
+               return 0;
+       return isp_enable_clocks(isp);
+ }
+ static void isp_pm_complete(struct device *dev)
+ {
+       struct isp_device *isp = dev_get_drvdata(dev);
+       if (isp->ref_count == 0)
+               return;
+       isp_restore_ctx(isp);
+       isp_enable_interrupts(isp);
+       isp_resume_modules(isp);
+ }
+ #else
+ #define isp_pm_prepare        NULL
+ #define isp_pm_suspend        NULL
+ #define isp_pm_resume NULL
+ #define isp_pm_complete       NULL
+ #endif /* CONFIG_PM */
+ static void isp_unregister_entities(struct isp_device *isp)
+ {
+       omap3isp_csi2_unregister_entities(&isp->isp_csi2a);
+       omap3isp_ccp2_unregister_entities(&isp->isp_ccp2);
+       omap3isp_ccdc_unregister_entities(&isp->isp_ccdc);
+       omap3isp_preview_unregister_entities(&isp->isp_prev);
+       omap3isp_resizer_unregister_entities(&isp->isp_res);
+       omap3isp_stat_unregister_entities(&isp->isp_aewb);
+       omap3isp_stat_unregister_entities(&isp->isp_af);
+       omap3isp_stat_unregister_entities(&isp->isp_hist);
+       v4l2_device_unregister(&isp->v4l2_dev);
+       media_device_unregister(&isp->media_dev);
+ }
+ /*
+  * isp_register_subdev_group - Register a group of subdevices
+  * @isp: OMAP3 ISP device
+  * @board_info: I2C subdevs board information array
+  *
+  * Register all I2C subdevices in the board_info array. The array must be
+  * terminated by a NULL entry, and the first entry must be the sensor.
+  *
+  * Return a pointer to the sensor media entity if it has been successfully
+  * registered, or NULL otherwise.
+  */
+ static struct v4l2_subdev *
+ isp_register_subdev_group(struct isp_device *isp,
+                    struct isp_subdev_i2c_board_info *board_info)
+ {
+       struct v4l2_subdev *sensor = NULL;
+       unsigned int first;
+       if (board_info->board_info == NULL)
+               return NULL;
+       for (first = 1; board_info->board_info; ++board_info, first = 0) {
+               struct v4l2_subdev *subdev;
+               struct i2c_adapter *adapter;
+               adapter = i2c_get_adapter(board_info->i2c_adapter_id);
+               if (adapter == NULL) {
+                       printk(KERN_ERR "%s: Unable to get I2C adapter %d for "
+                               "device %s\n", __func__,
+                               board_info->i2c_adapter_id,
+                               board_info->board_info->type);
+                       continue;
+               }
+               subdev = v4l2_i2c_new_subdev_board(&isp->v4l2_dev, adapter,
+                               board_info->board_info, NULL);
+               if (subdev == NULL) {
+                       printk(KERN_ERR "%s: Unable to register subdev %s\n",
+                               __func__, board_info->board_info->type);
+                       continue;
+               }
+               if (first)
+                       sensor = subdev;
+       }
+       return sensor;
+ }
+ static int isp_register_entities(struct isp_device *isp)
+ {
+       struct isp_platform_data *pdata = isp->pdata;
+       struct isp_v4l2_subdevs_group *subdevs;
+       int ret;
+       isp->media_dev.dev = isp->dev;
+       strlcpy(isp->media_dev.model, "TI OMAP3 ISP",
+               sizeof(isp->media_dev.model));
+       isp->media_dev.hw_revision = isp->revision;
+       isp->media_dev.link_notify = isp_pipeline_link_notify;
+       ret = media_device_register(&isp->media_dev);
+       if (ret < 0) {
+               printk(KERN_ERR "%s: Media device registration failed (%d)\n",
+                       __func__, ret);
+               return ret;
+       }
+       isp->v4l2_dev.mdev = &isp->media_dev;
+       ret = v4l2_device_register(isp->dev, &isp->v4l2_dev);
+       if (ret < 0) {
+               printk(KERN_ERR "%s: V4L2 device registration failed (%d)\n",
+                       __func__, ret);
+               goto done;
+       }
+       /* Register internal entities */
+       ret = omap3isp_ccp2_register_entities(&isp->isp_ccp2, &isp->v4l2_dev);
+       if (ret < 0)
+               goto done;
+       ret = omap3isp_csi2_register_entities(&isp->isp_csi2a, &isp->v4l2_dev);
+       if (ret < 0)
+               goto done;
+       ret = omap3isp_ccdc_register_entities(&isp->isp_ccdc, &isp->v4l2_dev);
+       if (ret < 0)
+               goto done;
+       ret = omap3isp_preview_register_entities(&isp->isp_prev,
+                                                &isp->v4l2_dev);
+       if (ret < 0)
+               goto done;
+       ret = omap3isp_resizer_register_entities(&isp->isp_res, &isp->v4l2_dev);
+       if (ret < 0)
+               goto done;
+       ret = omap3isp_stat_register_entities(&isp->isp_aewb, &isp->v4l2_dev);
+       if (ret < 0)
+               goto done;
+       ret = omap3isp_stat_register_entities(&isp->isp_af, &isp->v4l2_dev);
+       if (ret < 0)
+               goto done;
+       ret = omap3isp_stat_register_entities(&isp->isp_hist, &isp->v4l2_dev);
+       if (ret < 0)
+               goto done;
+       /* Register external entities */
+       for (subdevs = pdata->subdevs; subdevs && subdevs->subdevs; ++subdevs) {
+               struct v4l2_subdev *sensor;
+               struct media_entity *input;
+               unsigned int flags;
+               unsigned int pad;
+               sensor = isp_register_subdev_group(isp, subdevs->subdevs);
+               if (sensor == NULL)
+                       continue;
+               sensor->host_priv = subdevs;
+               /* Connect the sensor to the correct interface module. Parallel
+                * sensors are connected directly to the CCDC, while serial
+                * sensors are connected to the CSI2a, CCP2b or CSI2c receiver
+                * through CSIPHY1 or CSIPHY2.
+                */
+               switch (subdevs->interface) {
+               case ISP_INTERFACE_PARALLEL:
+                       input = &isp->isp_ccdc.subdev.entity;
+                       pad = CCDC_PAD_SINK;
+                       flags = 0;
+                       break;
+               case ISP_INTERFACE_CSI2A_PHY2:
+                       input = &isp->isp_csi2a.subdev.entity;
+                       pad = CSI2_PAD_SINK;
+                       flags = MEDIA_LNK_FL_IMMUTABLE
+                             | MEDIA_LNK_FL_ENABLED;
+                       break;
+               case ISP_INTERFACE_CCP2B_PHY1:
+               case ISP_INTERFACE_CCP2B_PHY2:
+                       input = &isp->isp_ccp2.subdev.entity;
+                       pad = CCP2_PAD_SINK;
+                       flags = 0;
+                       break;
+               case ISP_INTERFACE_CSI2C_PHY1:
+                       input = &isp->isp_csi2c.subdev.entity;
+                       pad = CSI2_PAD_SINK;
+                       flags = MEDIA_LNK_FL_IMMUTABLE
+                             | MEDIA_LNK_FL_ENABLED;
+                       break;
+               default:
+                       printk(KERN_ERR "%s: invalid interface type %u\n",
+                              __func__, subdevs->interface);
+                       ret = -EINVAL;
+                       goto done;
+               }
+               ret = media_entity_create_link(&sensor->entity, 0, input, pad,
+                                              flags);
+               if (ret < 0)
+                       goto done;
+       }
+       ret = v4l2_device_register_subdev_nodes(&isp->v4l2_dev);
+ done:
+       if (ret < 0)
+               isp_unregister_entities(isp);
+       return ret;
+ }
+ static void isp_cleanup_modules(struct isp_device *isp)
+ {
+       omap3isp_h3a_aewb_cleanup(isp);
+       omap3isp_h3a_af_cleanup(isp);
+       omap3isp_hist_cleanup(isp);
+       omap3isp_resizer_cleanup(isp);
+       omap3isp_preview_cleanup(isp);
+       omap3isp_ccdc_cleanup(isp);
+       omap3isp_ccp2_cleanup(isp);
+       omap3isp_csi2_cleanup(isp);
+ }
+ static int isp_initialize_modules(struct isp_device *isp)
+ {
+       int ret;
+       ret = omap3isp_csiphy_init(isp);
+       if (ret < 0) {
+               dev_err(isp->dev, "CSI PHY initialization failed\n");
+               goto error_csiphy;
+       }
+       ret = omap3isp_csi2_init(isp);
+       if (ret < 0) {
+               dev_err(isp->dev, "CSI2 initialization failed\n");
+               goto error_csi2;
+       }
+       ret = omap3isp_ccp2_init(isp);
+       if (ret < 0) {
+               dev_err(isp->dev, "CCP2 initialization failed\n");
+               goto error_ccp2;
+       }
+       ret = omap3isp_ccdc_init(isp);
+       if (ret < 0) {
+               dev_err(isp->dev, "CCDC initialization failed\n");
+               goto error_ccdc;
+       }
+       ret = omap3isp_preview_init(isp);
+       if (ret < 0) {
+               dev_err(isp->dev, "Preview initialization failed\n");
+               goto error_preview;
+       }
+       ret = omap3isp_resizer_init(isp);
+       if (ret < 0) {
+               dev_err(isp->dev, "Resizer initialization failed\n");
+               goto error_resizer;
+       }
+       ret = omap3isp_hist_init(isp);
+       if (ret < 0) {
+               dev_err(isp->dev, "Histogram initialization failed\n");
+               goto error_hist;
+       }
+       ret = omap3isp_h3a_aewb_init(isp);
+       if (ret < 0) {
+               dev_err(isp->dev, "H3A AEWB initialization failed\n");
+               goto error_h3a_aewb;
+       }
+       ret = omap3isp_h3a_af_init(isp);
+       if (ret < 0) {
+               dev_err(isp->dev, "H3A AF initialization failed\n");
+               goto error_h3a_af;
+       }
+       /* Connect the submodules. */
+       ret = media_entity_create_link(
+                       &isp->isp_csi2a.subdev.entity, CSI2_PAD_SOURCE,
+                       &isp->isp_ccdc.subdev.entity, CCDC_PAD_SINK, 0);
+       if (ret < 0)
+               goto error_link;
+       ret = media_entity_create_link(
+                       &isp->isp_ccp2.subdev.entity, CCP2_PAD_SOURCE,
+                       &isp->isp_ccdc.subdev.entity, CCDC_PAD_SINK, 0);
+       if (ret < 0)
+               goto error_link;
+       ret = media_entity_create_link(
+                       &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP,
+                       &isp->isp_prev.subdev.entity, PREV_PAD_SINK, 0);
+       if (ret < 0)
+               goto error_link;
+       ret = media_entity_create_link(
+                       &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_OF,
+                       &isp->isp_res.subdev.entity, RESZ_PAD_SINK, 0);
+       if (ret < 0)
+               goto error_link;
+       ret = media_entity_create_link(
+                       &isp->isp_prev.subdev.entity, PREV_PAD_SOURCE,
+                       &isp->isp_res.subdev.entity, RESZ_PAD_SINK, 0);
+       if (ret < 0)
+               goto error_link;
+       ret = media_entity_create_link(
+                       &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP,
+                       &isp->isp_aewb.subdev.entity, 0,
+                       MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE);
+       if (ret < 0)
+               goto error_link;
+       ret = media_entity_create_link(
+                       &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP,
+                       &isp->isp_af.subdev.entity, 0,
+                       MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE);
+       if (ret < 0)
+               goto error_link;
+       ret = media_entity_create_link(
+                       &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP,
+                       &isp->isp_hist.subdev.entity, 0,
+                       MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE);
+       if (ret < 0)
+               goto error_link;
+       return 0;
+ error_link:
+       omap3isp_h3a_af_cleanup(isp);
+ error_h3a_af:
+       omap3isp_h3a_aewb_cleanup(isp);
+ error_h3a_aewb:
+       omap3isp_hist_cleanup(isp);
+ error_hist:
+       omap3isp_resizer_cleanup(isp);
+ error_resizer:
+       omap3isp_preview_cleanup(isp);
+ error_preview:
+       omap3isp_ccdc_cleanup(isp);
+ error_ccdc:
+       omap3isp_ccp2_cleanup(isp);
+ error_ccp2:
+       omap3isp_csi2_cleanup(isp);
+ error_csi2:
+ error_csiphy:
+       return ret;
+ }
+ /*
+  * isp_remove - Remove ISP platform device
+  * @pdev: Pointer to ISP platform device
+  *
+  * Always returns 0.
+  */
+ static int __devexit isp_remove(struct platform_device *pdev)
+ {
+       struct isp_device *isp = platform_get_drvdata(pdev);
+       int i;
+       isp_unregister_entities(isp);
+       isp_cleanup_modules(isp);
+       __omap3isp_get(isp, false);
+       iommu_detach_device(isp->domain, &pdev->dev);
+       iommu_domain_free(isp->domain);
+       isp->domain = NULL;
+       omap3isp_put(isp);
+       free_irq(isp->irq_num, isp);
+       isp_put_clocks(isp);
+       for (i = 0; i < OMAP3_ISP_IOMEM_LAST; i++) {
+               if (isp->mmio_base[i]) {
+                       iounmap(isp->mmio_base[i]);
+                       isp->mmio_base[i] = NULL;
+               }
+               if (isp->mmio_base_phys[i]) {
+                       release_mem_region(isp->mmio_base_phys[i],
+                                          isp->mmio_size[i]);
+                       isp->mmio_base_phys[i] = 0;
+               }
+       }
+       regulator_put(isp->isp_csiphy1.vdd);
+       regulator_put(isp->isp_csiphy2.vdd);
+       kfree(isp);
+       return 0;
+ }
+ static int isp_map_mem_resource(struct platform_device *pdev,
+                               struct isp_device *isp,
+                               enum isp_mem_resources res)
+ {
+       struct resource *mem;
+       /* request the mem region for the camera registers */
+       mem = platform_get_resource(pdev, IORESOURCE_MEM, res);
+       if (!mem) {
+               dev_err(isp->dev, "no mem resource?\n");
+               return -ENODEV;
+       }
+       if (!request_mem_region(mem->start, resource_size(mem), pdev->name)) {
+               dev_err(isp->dev,
+                       "cannot reserve camera register I/O region\n");
+               return -ENODEV;
+       }
+       isp->mmio_base_phys[res] = mem->start;
+       isp->mmio_size[res] = resource_size(mem);
+       /* map the region */
+       isp->mmio_base[res] = ioremap_nocache(isp->mmio_base_phys[res],
+                                             isp->mmio_size[res]);
+       if (!isp->mmio_base[res]) {
+               dev_err(isp->dev, "cannot map camera register I/O region\n");
+               return -ENODEV;
+       }
+       return 0;
+ }
+ /*
+  * isp_probe - Probe ISP platform device
+  * @pdev: Pointer to ISP platform device
+  *
+  * Returns 0 if successful,
+  *   -ENOMEM if no memory available,
+  *   -ENODEV if no platform device resources found
+  *     or no space for remapping registers,
+  *   -EINVAL if couldn't install ISR,
+  *   or clk_get return error value.
+  */
+ static int __devinit isp_probe(struct platform_device *pdev)
+ {
+       struct isp_platform_data *pdata = pdev->dev.platform_data;
+       struct isp_device *isp;
+       int ret;
+       int i, m;
+       if (pdata == NULL)
+               return -EINVAL;
+       isp = kzalloc(sizeof(*isp), GFP_KERNEL);
+       if (!isp) {
+               dev_err(&pdev->dev, "could not allocate memory\n");
+               return -ENOMEM;
+       }
+       isp->autoidle = autoidle;
+       isp->platform_cb.set_xclk = isp_set_xclk;
+       mutex_init(&isp->isp_mutex);
+       spin_lock_init(&isp->stat_lock);
+       isp->dev = &pdev->dev;
+       isp->pdata = pdata;
+       isp->ref_count = 0;
+       isp->raw_dmamask = DMA_BIT_MASK(32);
+       isp->dev->dma_mask = &isp->raw_dmamask;
+       isp->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+       platform_set_drvdata(pdev, isp);
+       /* Regulators */
+       isp->isp_csiphy1.vdd = regulator_get(&pdev->dev, "VDD_CSIPHY1");
+       isp->isp_csiphy2.vdd = regulator_get(&pdev->dev, "VDD_CSIPHY2");
+       /* Clocks */
+       ret = isp_map_mem_resource(pdev, isp, OMAP3_ISP_IOMEM_MAIN);
+       if (ret < 0)
+               goto error;
+       ret = isp_get_clocks(isp);
+       if (ret < 0)
+               goto error;
+       if (__omap3isp_get(isp, false) == NULL) {
+               ret = -ENODEV;
+               goto error;
+       }
+       ret = isp_reset(isp);
+       if (ret < 0)
+               goto error_isp;
+       /* Memory resources */
+       isp->revision = isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_REVISION);
+       dev_info(isp->dev, "Revision %d.%d found\n",
+                (isp->revision & 0xf0) >> 4, isp->revision & 0x0f);
+       for (m = 0; m < ARRAY_SIZE(isp_res_maps); m++)
+               if (isp->revision == isp_res_maps[m].isp_rev)
+                       break;
+       if (m == ARRAY_SIZE(isp_res_maps)) {
+               dev_err(isp->dev, "No resource map found for ISP rev %d.%d\n",
+                       (isp->revision & 0xf0) >> 4, isp->revision & 0xf);
+               ret = -ENODEV;
+               goto error_isp;
+       }
+       for (i = 1; i < OMAP3_ISP_IOMEM_LAST; i++) {
+               if (isp_res_maps[m].map & 1 << i) {
+                       ret = isp_map_mem_resource(pdev, isp, i);
+                       if (ret)
+                               goto error_isp;
+               }
+       }
+       isp->domain = iommu_domain_alloc(pdev->dev.bus);
+       if (!isp->domain) {
+               dev_err(isp->dev, "can't alloc iommu domain\n");
+               ret = -ENOMEM;
+               goto error_isp;
+       }
+       ret = iommu_attach_device(isp->domain, &pdev->dev);
+       if (ret) {
+               dev_err(&pdev->dev, "can't attach iommu device: %d\n", ret);
+               goto free_domain;
+       }
+       /* Interrupt */
+       isp->irq_num = platform_get_irq(pdev, 0);
+       if (isp->irq_num <= 0) {
+               dev_err(isp->dev, "No IRQ resource\n");
+               ret = -ENODEV;
+               goto detach_dev;
+       }
+       if (request_irq(isp->irq_num, isp_isr, IRQF_SHARED, "OMAP3 ISP", isp)) {
+               dev_err(isp->dev, "Unable to request IRQ\n");
+               ret = -EINVAL;
+               goto detach_dev;
+       }
+       /* Entities */
+       ret = isp_initialize_modules(isp);
+       if (ret < 0)
+               goto error_irq;
+       ret = isp_register_entities(isp);
+       if (ret < 0)
+               goto error_modules;
+       isp_core_init(isp, 1);
+       omap3isp_put(isp);
+       return 0;
+ error_modules:
+       isp_cleanup_modules(isp);
+ error_irq:
+       free_irq(isp->irq_num, isp);
+ detach_dev:
+       iommu_detach_device(isp->domain, &pdev->dev);
+ free_domain:
+       iommu_domain_free(isp->domain);
+ error_isp:
+       omap3isp_put(isp);
+ error:
+       isp_put_clocks(isp);
+       for (i = 0; i < OMAP3_ISP_IOMEM_LAST; i++) {
+               if (isp->mmio_base[i]) {
+                       iounmap(isp->mmio_base[i]);
+                       isp->mmio_base[i] = NULL;
+               }
+               if (isp->mmio_base_phys[i]) {
+                       release_mem_region(isp->mmio_base_phys[i],
+                                          isp->mmio_size[i]);
+                       isp->mmio_base_phys[i] = 0;
+               }
+       }
+       regulator_put(isp->isp_csiphy2.vdd);
+       regulator_put(isp->isp_csiphy1.vdd);
+       platform_set_drvdata(pdev, NULL);
+       mutex_destroy(&isp->isp_mutex);
+       kfree(isp);
+       return ret;
+ }
+ static const struct dev_pm_ops omap3isp_pm_ops = {
+       .prepare = isp_pm_prepare,
+       .suspend = isp_pm_suspend,
+       .resume = isp_pm_resume,
+       .complete = isp_pm_complete,
+ };
+ static struct platform_device_id omap3isp_id_table[] = {
+       { "omap3isp", 0 },
+       { },
+ };
+ MODULE_DEVICE_TABLE(platform, omap3isp_id_table);
+ static struct platform_driver omap3isp_driver = {
+       .probe = isp_probe,
+       .remove = __devexit_p(isp_remove),
+       .id_table = omap3isp_id_table,
+       .driver = {
+               .owner = THIS_MODULE,
+               .name = "omap3isp",
+               .pm     = &omap3isp_pm_ops,
+       },
+ };
+ module_platform_driver(omap3isp_driver);
+ MODULE_AUTHOR("Nokia Corporation");
+ MODULE_DESCRIPTION("TI OMAP3 ISP driver");
+ MODULE_LICENSE("GPL");
+ MODULE_VERSION(ISP_VIDEO_DRIVER_VERSION);
index 0000000,8e4eed8..e92236a
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,842 +1,842 @@@
 -#include <plat/mipi_csis.h>
+ /*
+  * Samsung S5P/EXYNOS4 SoC series MIPI-CSI receiver driver
+  *
+  * Copyright (C) 2011 - 2012 Samsung Electronics Co., Ltd.
+  * Sylwester Nawrocki, <s.nawrocki@samsung.com>
+  *
+  * This program is free software; you can redistribute it and/or modify
+  * it under the terms of the GNU General Public License version 2 as
+  * published by the Free Software Foundation.
+  */
+ #include <linux/clk.h>
+ #include <linux/delay.h>
+ #include <linux/device.h>
+ #include <linux/errno.h>
+ #include <linux/interrupt.h>
+ #include <linux/io.h>
+ #include <linux/irq.h>
+ #include <linux/kernel.h>
+ #include <linux/memory.h>
+ #include <linux/module.h>
+ #include <linux/platform_device.h>
+ #include <linux/pm_runtime.h>
+ #include <linux/regulator/consumer.h>
+ #include <linux/slab.h>
+ #include <linux/spinlock.h>
+ #include <linux/videodev2.h>
+ #include <media/v4l2-subdev.h>
++#include <linux/platform_data/mipi-csis.h>
+ #include "mipi-csis.h"
+ static int debug;
+ module_param(debug, int, 0644);
+ MODULE_PARM_DESC(debug, "Debug level (0-2)");
+ /* Register map definition */
+ /* CSIS global control */
+ #define S5PCSIS_CTRL                  0x00
+ #define S5PCSIS_CTRL_DPDN_DEFAULT     (0 << 31)
+ #define S5PCSIS_CTRL_DPDN_SWAP                (1 << 31)
+ #define S5PCSIS_CTRL_ALIGN_32BIT      (1 << 20)
+ #define S5PCSIS_CTRL_UPDATE_SHADOW    (1 << 16)
+ #define S5PCSIS_CTRL_WCLK_EXTCLK      (1 << 8)
+ #define S5PCSIS_CTRL_RESET            (1 << 4)
+ #define S5PCSIS_CTRL_ENABLE           (1 << 0)
+ /* D-PHY control */
+ #define S5PCSIS_DPHYCTRL              0x04
+ #define S5PCSIS_DPHYCTRL_HSS_MASK     (0x1f << 27)
+ #define S5PCSIS_DPHYCTRL_ENABLE               (0x1f << 0)
+ #define S5PCSIS_CONFIG                        0x08
+ #define S5PCSIS_CFG_FMT_YCBCR422_8BIT (0x1e << 2)
+ #define S5PCSIS_CFG_FMT_RAW8          (0x2a << 2)
+ #define S5PCSIS_CFG_FMT_RAW10         (0x2b << 2)
+ #define S5PCSIS_CFG_FMT_RAW12         (0x2c << 2)
+ /* User defined formats, x = 1...4 */
+ #define S5PCSIS_CFG_FMT_USER(x)               ((0x30 + x - 1) << 2)
+ #define S5PCSIS_CFG_FMT_MASK          (0x3f << 2)
+ #define S5PCSIS_CFG_NR_LANE_MASK      3
+ /* Interrupt mask */
+ #define S5PCSIS_INTMSK                        0x10
+ #define S5PCSIS_INTMSK_EN_ALL         0xf000103f
+ #define S5PCSIS_INTMSK_EVEN_BEFORE    (1 << 31)
+ #define S5PCSIS_INTMSK_EVEN_AFTER     (1 << 30)
+ #define S5PCSIS_INTMSK_ODD_BEFORE     (1 << 29)
+ #define S5PCSIS_INTMSK_ODD_AFTER      (1 << 28)
+ #define S5PCSIS_INTMSK_ERR_SOT_HS     (1 << 12)
+ #define S5PCSIS_INTMSK_ERR_LOST_FS    (1 << 5)
+ #define S5PCSIS_INTMSK_ERR_LOST_FE    (1 << 4)
+ #define S5PCSIS_INTMSK_ERR_OVER               (1 << 3)
+ #define S5PCSIS_INTMSK_ERR_ECC                (1 << 2)
+ #define S5PCSIS_INTMSK_ERR_CRC                (1 << 1)
+ #define S5PCSIS_INTMSK_ERR_UNKNOWN    (1 << 0)
+ /* Interrupt source */
+ #define S5PCSIS_INTSRC                        0x14
+ #define S5PCSIS_INTSRC_EVEN_BEFORE    (1 << 31)
+ #define S5PCSIS_INTSRC_EVEN_AFTER     (1 << 30)
+ #define S5PCSIS_INTSRC_EVEN           (0x3 << 30)
+ #define S5PCSIS_INTSRC_ODD_BEFORE     (1 << 29)
+ #define S5PCSIS_INTSRC_ODD_AFTER      (1 << 28)
+ #define S5PCSIS_INTSRC_ODD            (0x3 << 28)
+ #define S5PCSIS_INTSRC_NON_IMAGE_DATA (0xff << 28)
+ #define S5PCSIS_INTSRC_ERR_SOT_HS     (0xf << 12)
+ #define S5PCSIS_INTSRC_ERR_LOST_FS    (1 << 5)
+ #define S5PCSIS_INTSRC_ERR_LOST_FE    (1 << 4)
+ #define S5PCSIS_INTSRC_ERR_OVER               (1 << 3)
+ #define S5PCSIS_INTSRC_ERR_ECC                (1 << 2)
+ #define S5PCSIS_INTSRC_ERR_CRC                (1 << 1)
+ #define S5PCSIS_INTSRC_ERR_UNKNOWN    (1 << 0)
+ #define S5PCSIS_INTSRC_ERRORS         0xf03f
+ /* Pixel resolution */
+ #define S5PCSIS_RESOL                 0x2c
+ #define CSIS_MAX_PIX_WIDTH            0xffff
+ #define CSIS_MAX_PIX_HEIGHT           0xffff
+ enum {
+       CSIS_CLK_MUX,
+       CSIS_CLK_GATE,
+ };
+ static char *csi_clock_name[] = {
+       [CSIS_CLK_MUX]  = "sclk_csis",
+       [CSIS_CLK_GATE] = "csis",
+ };
+ #define NUM_CSIS_CLOCKS       ARRAY_SIZE(csi_clock_name)
+ static const char * const csis_supply_name[] = {
+       "vdd11", /* 1.1V or 1.2V (s5pc100) MIPI CSI suppply */
+       "vdd18", /* VDD 1.8V and MIPI CSI PLL supply */
+ };
+ #define CSIS_NUM_SUPPLIES ARRAY_SIZE(csis_supply_name)
+ enum {
+       ST_POWERED      = 1,
+       ST_STREAMING    = 2,
+       ST_SUSPENDED    = 4,
+ };
+ struct s5pcsis_event {
+       u32 mask;
+       const char * const name;
+       unsigned int counter;
+ };
+ static const struct s5pcsis_event s5pcsis_events[] = {
+       /* Errors */
+       { S5PCSIS_INTSRC_ERR_SOT_HS,    "SOT Error" },
+       { S5PCSIS_INTSRC_ERR_LOST_FS,   "Lost Frame Start Error" },
+       { S5PCSIS_INTSRC_ERR_LOST_FE,   "Lost Frame End Error" },
+       { S5PCSIS_INTSRC_ERR_OVER,      "FIFO Overflow Error" },
+       { S5PCSIS_INTSRC_ERR_ECC,       "ECC Error" },
+       { S5PCSIS_INTSRC_ERR_CRC,       "CRC Error" },
+       { S5PCSIS_INTSRC_ERR_UNKNOWN,   "Unknown Error" },
+       /* Non-image data receive events */
+       { S5PCSIS_INTSRC_EVEN_BEFORE,   "Non-image data before even frame" },
+       { S5PCSIS_INTSRC_EVEN_AFTER,    "Non-image data after even frame" },
+       { S5PCSIS_INTSRC_ODD_BEFORE,    "Non-image data before odd frame" },
+       { S5PCSIS_INTSRC_ODD_AFTER,     "Non-image data after odd frame" },
+ };
+ #define S5PCSIS_NUM_EVENTS ARRAY_SIZE(s5pcsis_events)
+ /**
+  * struct csis_state - the driver's internal state data structure
+  * @lock: mutex serializing the subdev and power management operations,
+  *        protecting @format and @flags members
+  * @pads: CSIS pads array
+  * @sd: v4l2_subdev associated with CSIS device instance
+  * @pdev: CSIS platform device
+  * @regs: mmaped I/O registers memory
+  * @supplies: CSIS regulator supplies
+  * @clock: CSIS clocks
+  * @irq: requested s5p-mipi-csis irq number
+  * @flags: the state variable for power and streaming control
+  * @csis_fmt: current CSIS pixel format
+  * @format: common media bus format for the source and sink pad
+  * @slock: spinlock protecting structure members below
+  * @events: MIPI-CSIS event (error) counters
+  */
+ struct csis_state {
+       struct mutex lock;
+       struct media_pad pads[CSIS_PADS_NUM];
+       struct v4l2_subdev sd;
+       struct platform_device *pdev;
+       void __iomem *regs;
+       struct regulator_bulk_data supplies[CSIS_NUM_SUPPLIES];
+       struct clk *clock[NUM_CSIS_CLOCKS];
+       int irq;
+       u32 flags;
+       const struct csis_pix_format *csis_fmt;
+       struct v4l2_mbus_framefmt format;
+       struct spinlock slock;
+       struct s5pcsis_event events[S5PCSIS_NUM_EVENTS];
+ };
+ /**
+  * struct csis_pix_format - CSIS pixel format description
+  * @pix_width_alignment: horizontal pixel alignment, width will be
+  *                       multiple of 2^pix_width_alignment
+  * @code: corresponding media bus code
+  * @fmt_reg: S5PCSIS_CONFIG register value
+  * @data_alignment: MIPI-CSI data alignment in bits
+  */
+ struct csis_pix_format {
+       unsigned int pix_width_alignment;
+       enum v4l2_mbus_pixelcode code;
+       u32 fmt_reg;
+       u8 data_alignment;
+ };
+ static const struct csis_pix_format s5pcsis_formats[] = {
+       {
+               .code = V4L2_MBUS_FMT_VYUY8_2X8,
+               .fmt_reg = S5PCSIS_CFG_FMT_YCBCR422_8BIT,
+               .data_alignment = 32,
+       }, {
+               .code = V4L2_MBUS_FMT_JPEG_1X8,
+               .fmt_reg = S5PCSIS_CFG_FMT_USER(1),
+               .data_alignment = 32,
+       },
+ };
+ #define s5pcsis_write(__csis, __r, __v) writel(__v, __csis->regs + __r)
+ #define s5pcsis_read(__csis, __r) readl(__csis->regs + __r)
+ static struct csis_state *sd_to_csis_state(struct v4l2_subdev *sdev)
+ {
+       return container_of(sdev, struct csis_state, sd);
+ }
+ static const struct csis_pix_format *find_csis_format(
+       struct v4l2_mbus_framefmt *mf)
+ {
+       int i;
+       for (i = 0; i < ARRAY_SIZE(s5pcsis_formats); i++)
+               if (mf->code == s5pcsis_formats[i].code)
+                       return &s5pcsis_formats[i];
+       return NULL;
+ }
+ static void s5pcsis_enable_interrupts(struct csis_state *state, bool on)
+ {
+       u32 val = s5pcsis_read(state, S5PCSIS_INTMSK);
+       val = on ? val | S5PCSIS_INTMSK_EN_ALL :
+                  val & ~S5PCSIS_INTMSK_EN_ALL;
+       s5pcsis_write(state, S5PCSIS_INTMSK, val);
+ }
+ static void s5pcsis_reset(struct csis_state *state)
+ {
+       u32 val = s5pcsis_read(state, S5PCSIS_CTRL);
+       s5pcsis_write(state, S5PCSIS_CTRL, val | S5PCSIS_CTRL_RESET);
+       udelay(10);
+ }
+ static void s5pcsis_system_enable(struct csis_state *state, int on)
+ {
+       u32 val;
+       val = s5pcsis_read(state, S5PCSIS_CTRL);
+       if (on)
+               val |= S5PCSIS_CTRL_ENABLE;
+       else
+               val &= ~S5PCSIS_CTRL_ENABLE;
+       s5pcsis_write(state, S5PCSIS_CTRL, val);
+       val = s5pcsis_read(state, S5PCSIS_DPHYCTRL);
+       if (on)
+               val |= S5PCSIS_DPHYCTRL_ENABLE;
+       else
+               val &= ~S5PCSIS_DPHYCTRL_ENABLE;
+       s5pcsis_write(state, S5PCSIS_DPHYCTRL, val);
+ }
+ /* Called with the state.lock mutex held */
+ static void __s5pcsis_set_format(struct csis_state *state)
+ {
+       struct v4l2_mbus_framefmt *mf = &state->format;
+       u32 val;
+       v4l2_dbg(1, debug, &state->sd, "fmt: %d, %d x %d\n",
+                mf->code, mf->width, mf->height);
+       /* Color format */
+       val = s5pcsis_read(state, S5PCSIS_CONFIG);
+       val = (val & ~S5PCSIS_CFG_FMT_MASK) | state->csis_fmt->fmt_reg;
+       s5pcsis_write(state, S5PCSIS_CONFIG, val);
+       /* Pixel resolution */
+       val = (mf->width << 16) | mf->height;
+       s5pcsis_write(state, S5PCSIS_RESOL, val);
+ }
+ static void s5pcsis_set_hsync_settle(struct csis_state *state, int settle)
+ {
+       u32 val = s5pcsis_read(state, S5PCSIS_DPHYCTRL);
+       val = (val & ~S5PCSIS_DPHYCTRL_HSS_MASK) | (settle << 27);
+       s5pcsis_write(state, S5PCSIS_DPHYCTRL, val);
+ }
+ static void s5pcsis_set_params(struct csis_state *state)
+ {
+       struct s5p_platform_mipi_csis *pdata = state->pdev->dev.platform_data;
+       u32 val;
+       val = s5pcsis_read(state, S5PCSIS_CONFIG);
+       val = (val & ~S5PCSIS_CFG_NR_LANE_MASK) | (pdata->lanes - 1);
+       s5pcsis_write(state, S5PCSIS_CONFIG, val);
+       __s5pcsis_set_format(state);
+       s5pcsis_set_hsync_settle(state, pdata->hs_settle);
+       val = s5pcsis_read(state, S5PCSIS_CTRL);
+       if (state->csis_fmt->data_alignment == 32)
+               val |= S5PCSIS_CTRL_ALIGN_32BIT;
+       else /* 24-bits */
+               val &= ~S5PCSIS_CTRL_ALIGN_32BIT;
+       /* Not using external clock. */
+       val &= ~S5PCSIS_CTRL_WCLK_EXTCLK;
+       s5pcsis_write(state, S5PCSIS_CTRL, val);
+       /* Update the shadow register. */
+       val = s5pcsis_read(state, S5PCSIS_CTRL);
+       s5pcsis_write(state, S5PCSIS_CTRL, val | S5PCSIS_CTRL_UPDATE_SHADOW);
+ }
+ static void s5pcsis_clk_put(struct csis_state *state)
+ {
+       int i;
+       for (i = 0; i < NUM_CSIS_CLOCKS; i++) {
+               if (IS_ERR_OR_NULL(state->clock[i]))
+                       continue;
+               clk_unprepare(state->clock[i]);
+               clk_put(state->clock[i]);
+               state->clock[i] = NULL;
+       }
+ }
+ static int s5pcsis_clk_get(struct csis_state *state)
+ {
+       struct device *dev = &state->pdev->dev;
+       int i, ret;
+       for (i = 0; i < NUM_CSIS_CLOCKS; i++) {
+               state->clock[i] = clk_get(dev, csi_clock_name[i]);
+               if (IS_ERR(state->clock[i]))
+                       goto err;
+               ret = clk_prepare(state->clock[i]);
+               if (ret < 0) {
+                       clk_put(state->clock[i]);
+                       state->clock[i] = NULL;
+                       goto err;
+               }
+       }
+       return 0;
+ err:
+       s5pcsis_clk_put(state);
+       dev_err(dev, "failed to get clock: %s\n", csi_clock_name[i]);
+       return -ENXIO;
+ }
+ static void s5pcsis_start_stream(struct csis_state *state)
+ {
+       s5pcsis_reset(state);
+       s5pcsis_set_params(state);
+       s5pcsis_system_enable(state, true);
+       s5pcsis_enable_interrupts(state, true);
+ }
+ static void s5pcsis_stop_stream(struct csis_state *state)
+ {
+       s5pcsis_enable_interrupts(state, false);
+       s5pcsis_system_enable(state, false);
+ }
+ static void s5pcsis_clear_counters(struct csis_state *state)
+ {
+       unsigned long flags;
+       int i;
+       spin_lock_irqsave(&state->slock, flags);
+       for (i = 0; i < S5PCSIS_NUM_EVENTS; i++)
+               state->events[i].counter = 0;
+       spin_unlock_irqrestore(&state->slock, flags);
+ }
+ static void s5pcsis_log_counters(struct csis_state *state, bool non_errors)
+ {
+       int i = non_errors ? S5PCSIS_NUM_EVENTS : S5PCSIS_NUM_EVENTS - 4;
+       unsigned long flags;
+       spin_lock_irqsave(&state->slock, flags);
+       for (i--; i >= 0; i--)
+               if (state->events[i].counter >= 0)
+                       v4l2_info(&state->sd, "%s events: %d\n",
+                                 state->events[i].name,
+                                 state->events[i].counter);
+       spin_unlock_irqrestore(&state->slock, flags);
+ }
+ /*
+  * V4L2 subdev operations
+  */
+ static int s5pcsis_s_power(struct v4l2_subdev *sd, int on)
+ {
+       struct csis_state *state = sd_to_csis_state(sd);
+       struct device *dev = &state->pdev->dev;
+       if (on)
+               return pm_runtime_get_sync(dev);
+       return pm_runtime_put_sync(dev);
+ }
+ static int s5pcsis_s_stream(struct v4l2_subdev *sd, int enable)
+ {
+       struct csis_state *state = sd_to_csis_state(sd);
+       int ret = 0;
+       v4l2_dbg(1, debug, sd, "%s: %d, state: 0x%x\n",
+                __func__, enable, state->flags);
+       if (enable) {
+               s5pcsis_clear_counters(state);
+               ret = pm_runtime_get_sync(&state->pdev->dev);
+               if (ret && ret != 1)
+                       return ret;
+       }
+       mutex_lock(&state->lock);
+       if (enable) {
+               if (state->flags & ST_SUSPENDED) {
+                       ret = -EBUSY;
+                       goto unlock;
+               }
+               s5pcsis_start_stream(state);
+               state->flags |= ST_STREAMING;
+       } else {
+               s5pcsis_stop_stream(state);
+               state->flags &= ~ST_STREAMING;
+               if (debug > 0)
+                       s5pcsis_log_counters(state, true);
+       }
+ unlock:
+       mutex_unlock(&state->lock);
+       if (!enable)
+               pm_runtime_put(&state->pdev->dev);
+       return ret == 1 ? 0 : ret;
+ }
+ static int s5pcsis_enum_mbus_code(struct v4l2_subdev *sd,
+                                 struct v4l2_subdev_fh *fh,
+                                 struct v4l2_subdev_mbus_code_enum *code)
+ {
+       if (code->index >= ARRAY_SIZE(s5pcsis_formats))
+               return -EINVAL;
+       code->code = s5pcsis_formats[code->index].code;
+       return 0;
+ }
+ static struct csis_pix_format const *s5pcsis_try_format(
+       struct v4l2_mbus_framefmt *mf)
+ {
+       struct csis_pix_format const *csis_fmt;
+       csis_fmt = find_csis_format(mf);
+       if (csis_fmt == NULL)
+               csis_fmt = &s5pcsis_formats[0];
+       mf->code = csis_fmt->code;
+       v4l_bound_align_image(&mf->width, 1, CSIS_MAX_PIX_WIDTH,
+                             csis_fmt->pix_width_alignment,
+                             &mf->height, 1, CSIS_MAX_PIX_HEIGHT, 1,
+                             0);
+       return csis_fmt;
+ }
+ static struct v4l2_mbus_framefmt *__s5pcsis_get_format(
+               struct csis_state *state, struct v4l2_subdev_fh *fh,
+               u32 pad, enum v4l2_subdev_format_whence which)
+ {
+       if (which == V4L2_SUBDEV_FORMAT_TRY)
+               return fh ? v4l2_subdev_get_try_format(fh, pad) : NULL;
+       return &state->format;
+ }
+ static int s5pcsis_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+                          struct v4l2_subdev_format *fmt)
+ {
+       struct csis_state *state = sd_to_csis_state(sd);
+       struct csis_pix_format const *csis_fmt;
+       struct v4l2_mbus_framefmt *mf;
+       if (fmt->pad != CSIS_PAD_SOURCE && fmt->pad != CSIS_PAD_SINK)
+               return -EINVAL;
+       mf = __s5pcsis_get_format(state, fh, fmt->pad, fmt->which);
+       if (fmt->pad == CSIS_PAD_SOURCE) {
+               if (mf) {
+                       mutex_lock(&state->lock);
+                       fmt->format = *mf;
+                       mutex_unlock(&state->lock);
+               }
+               return 0;
+       }
+       csis_fmt = s5pcsis_try_format(&fmt->format);
+       if (mf) {
+               mutex_lock(&state->lock);
+               *mf = fmt->format;
+               if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+                       state->csis_fmt = csis_fmt;
+               mutex_unlock(&state->lock);
+       }
+       return 0;
+ }
+ static int s5pcsis_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+                          struct v4l2_subdev_format *fmt)
+ {
+       struct csis_state *state = sd_to_csis_state(sd);
+       struct v4l2_mbus_framefmt *mf;
+       if (fmt->pad != CSIS_PAD_SOURCE && fmt->pad != CSIS_PAD_SINK)
+               return -EINVAL;
+       mf = __s5pcsis_get_format(state, fh, fmt->pad, fmt->which);
+       if (!mf)
+               return -EINVAL;
+       mutex_lock(&state->lock);
+       fmt->format = *mf;
+       mutex_unlock(&state->lock);
+       return 0;
+ }
+ static int s5pcsis_log_status(struct v4l2_subdev *sd)
+ {
+       struct csis_state *state = sd_to_csis_state(sd);
+       s5pcsis_log_counters(state, true);
+       return 0;
+ }
+ static int s5pcsis_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+ {
+       struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(fh, 0);
+       format->colorspace = V4L2_COLORSPACE_JPEG;
+       format->code = s5pcsis_formats[0].code;
+       format->width = S5PCSIS_DEF_PIX_WIDTH;
+       format->height = S5PCSIS_DEF_PIX_HEIGHT;
+       format->field = V4L2_FIELD_NONE;
+       return 0;
+ }
+ static const struct v4l2_subdev_internal_ops s5pcsis_sd_internal_ops = {
+       .open = s5pcsis_open,
+ };
+ static struct v4l2_subdev_core_ops s5pcsis_core_ops = {
+       .s_power = s5pcsis_s_power,
+       .log_status = s5pcsis_log_status,
+ };
+ static struct v4l2_subdev_pad_ops s5pcsis_pad_ops = {
+       .enum_mbus_code = s5pcsis_enum_mbus_code,
+       .get_fmt = s5pcsis_get_fmt,
+       .set_fmt = s5pcsis_set_fmt,
+ };
+ static struct v4l2_subdev_video_ops s5pcsis_video_ops = {
+       .s_stream = s5pcsis_s_stream,
+ };
+ static struct v4l2_subdev_ops s5pcsis_subdev_ops = {
+       .core = &s5pcsis_core_ops,
+       .pad = &s5pcsis_pad_ops,
+       .video = &s5pcsis_video_ops,
+ };
+ static irqreturn_t s5pcsis_irq_handler(int irq, void *dev_id)
+ {
+       struct csis_state *state = dev_id;
+       unsigned long flags;
+       u32 status;
+       status = s5pcsis_read(state, S5PCSIS_INTSRC);
+       spin_lock_irqsave(&state->slock, flags);
+       /* Update the event/error counters */
+       if ((status & S5PCSIS_INTSRC_ERRORS) || debug) {
+               int i;
+               for (i = 0; i < S5PCSIS_NUM_EVENTS; i++) {
+                       if (!(status & state->events[i].mask))
+                               continue;
+                       state->events[i].counter++;
+                       v4l2_dbg(2, debug, &state->sd, "%s: %d\n",
+                                state->events[i].name,
+                                state->events[i].counter);
+               }
+               v4l2_dbg(2, debug, &state->sd, "status: %08x\n", status);
+       }
+       spin_unlock_irqrestore(&state->slock, flags);
+       s5pcsis_write(state, S5PCSIS_INTSRC, status);
+       return IRQ_HANDLED;
+ }
+ static int __devinit s5pcsis_probe(struct platform_device *pdev)
+ {
+       struct s5p_platform_mipi_csis *pdata;
+       struct resource *mem_res;
+       struct csis_state *state;
+       int ret = -ENOMEM;
+       int i;
+       state = devm_kzalloc(&pdev->dev, sizeof(*state), GFP_KERNEL);
+       if (!state)
+               return -ENOMEM;
+       mutex_init(&state->lock);
+       spin_lock_init(&state->slock);
+       state->pdev = pdev;
+       pdata = pdev->dev.platform_data;
+       if (pdata == NULL || pdata->phy_enable == NULL) {
+               dev_err(&pdev->dev, "Platform data not fully specified\n");
+               return -EINVAL;
+       }
+       if ((pdev->id == 1 && pdata->lanes > CSIS1_MAX_LANES) ||
+           pdata->lanes > CSIS0_MAX_LANES) {
+               dev_err(&pdev->dev, "Unsupported number of data lanes: %d\n",
+                       pdata->lanes);
+               return -EINVAL;
+       }
+       mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       state->regs = devm_request_and_ioremap(&pdev->dev, mem_res);
+       if (state->regs == NULL) {
+               dev_err(&pdev->dev, "Failed to request and remap io memory\n");
+               return -ENXIO;
+       }
+       state->irq = platform_get_irq(pdev, 0);
+       if (state->irq < 0) {
+               dev_err(&pdev->dev, "Failed to get irq\n");
+               return state->irq;
+       }
+       for (i = 0; i < CSIS_NUM_SUPPLIES; i++)
+               state->supplies[i].supply = csis_supply_name[i];
+       ret = regulator_bulk_get(&pdev->dev, CSIS_NUM_SUPPLIES,
+                                state->supplies);
+       if (ret)
+               return ret;
+       ret = s5pcsis_clk_get(state);
+       if (ret)
+               goto e_clkput;
+       clk_enable(state->clock[CSIS_CLK_MUX]);
+       if (pdata->clk_rate)
+               clk_set_rate(state->clock[CSIS_CLK_MUX], pdata->clk_rate);
+       else
+               dev_WARN(&pdev->dev, "No clock frequency specified!\n");
+       ret = devm_request_irq(&pdev->dev, state->irq, s5pcsis_irq_handler,
+                              0, dev_name(&pdev->dev), state);
+       if (ret) {
+               dev_err(&pdev->dev, "Interrupt request failed\n");
+               goto e_regput;
+       }
+       v4l2_subdev_init(&state->sd, &s5pcsis_subdev_ops);
+       state->sd.owner = THIS_MODULE;
+       strlcpy(state->sd.name, dev_name(&pdev->dev), sizeof(state->sd.name));
+       state->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+       state->csis_fmt = &s5pcsis_formats[0];
+       state->format.code = s5pcsis_formats[0].code;
+       state->format.width = S5PCSIS_DEF_PIX_WIDTH;
+       state->format.height = S5PCSIS_DEF_PIX_HEIGHT;
+       state->pads[CSIS_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+       state->pads[CSIS_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
+       ret = media_entity_init(&state->sd.entity,
+                               CSIS_PADS_NUM, state->pads, 0);
+       if (ret < 0)
+               goto e_clkput;
+       /* This allows to retrieve the platform device id by the host driver */
+       v4l2_set_subdevdata(&state->sd, pdev);
+       /* .. and a pointer to the subdev. */
+       platform_set_drvdata(pdev, &state->sd);
+       memcpy(state->events, s5pcsis_events, sizeof(state->events));
+       pm_runtime_enable(&pdev->dev);
+       return 0;
+ e_regput:
+       regulator_bulk_free(CSIS_NUM_SUPPLIES, state->supplies);
+ e_clkput:
+       clk_disable(state->clock[CSIS_CLK_MUX]);
+       s5pcsis_clk_put(state);
+       return ret;
+ }
+ static int s5pcsis_pm_suspend(struct device *dev, bool runtime)
+ {
+       struct s5p_platform_mipi_csis *pdata = dev->platform_data;
+       struct platform_device *pdev = to_platform_device(dev);
+       struct v4l2_subdev *sd = platform_get_drvdata(pdev);
+       struct csis_state *state = sd_to_csis_state(sd);
+       int ret = 0;
+       v4l2_dbg(1, debug, sd, "%s: flags: 0x%x\n",
+                __func__, state->flags);
+       mutex_lock(&state->lock);
+       if (state->flags & ST_POWERED) {
+               s5pcsis_stop_stream(state);
+               ret = pdata->phy_enable(state->pdev, false);
+               if (ret)
+                       goto unlock;
+               ret = regulator_bulk_disable(CSIS_NUM_SUPPLIES,
+                                            state->supplies);
+               if (ret)
+                       goto unlock;
+               clk_disable(state->clock[CSIS_CLK_GATE]);
+               state->flags &= ~ST_POWERED;
+               if (!runtime)
+                       state->flags |= ST_SUSPENDED;
+       }
+  unlock:
+       mutex_unlock(&state->lock);
+       return ret ? -EAGAIN : 0;
+ }
+ static int s5pcsis_pm_resume(struct device *dev, bool runtime)
+ {
+       struct s5p_platform_mipi_csis *pdata = dev->platform_data;
+       struct platform_device *pdev = to_platform_device(dev);
+       struct v4l2_subdev *sd = platform_get_drvdata(pdev);
+       struct csis_state *state = sd_to_csis_state(sd);
+       int ret = 0;
+       v4l2_dbg(1, debug, sd, "%s: flags: 0x%x\n",
+                __func__, state->flags);
+       mutex_lock(&state->lock);
+       if (!runtime && !(state->flags & ST_SUSPENDED))
+               goto unlock;
+       if (!(state->flags & ST_POWERED)) {
+               ret = regulator_bulk_enable(CSIS_NUM_SUPPLIES,
+                                           state->supplies);
+               if (ret)
+                       goto unlock;
+               ret = pdata->phy_enable(state->pdev, true);
+               if (!ret) {
+                       state->flags |= ST_POWERED;
+               } else {
+                       regulator_bulk_disable(CSIS_NUM_SUPPLIES,
+                                              state->supplies);
+                       goto unlock;
+               }
+               clk_enable(state->clock[CSIS_CLK_GATE]);
+       }
+       if (state->flags & ST_STREAMING)
+               s5pcsis_start_stream(state);
+       state->flags &= ~ST_SUSPENDED;
+  unlock:
+       mutex_unlock(&state->lock);
+       return ret ? -EAGAIN : 0;
+ }
+ #ifdef CONFIG_PM_SLEEP
+ static int s5pcsis_suspend(struct device *dev)
+ {
+       return s5pcsis_pm_suspend(dev, false);
+ }
+ static int s5pcsis_resume(struct device *dev)
+ {
+       return s5pcsis_pm_resume(dev, false);
+ }
+ #endif
+ #ifdef CONFIG_PM_RUNTIME
+ static int s5pcsis_runtime_suspend(struct device *dev)
+ {
+       return s5pcsis_pm_suspend(dev, true);
+ }
+ static int s5pcsis_runtime_resume(struct device *dev)
+ {
+       return s5pcsis_pm_resume(dev, true);
+ }
+ #endif
+ static int __devexit s5pcsis_remove(struct platform_device *pdev)
+ {
+       struct v4l2_subdev *sd = platform_get_drvdata(pdev);
+       struct csis_state *state = sd_to_csis_state(sd);
+       pm_runtime_disable(&pdev->dev);
+       s5pcsis_pm_suspend(&pdev->dev, false);
+       clk_disable(state->clock[CSIS_CLK_MUX]);
+       pm_runtime_set_suspended(&pdev->dev);
+       s5pcsis_clk_put(state);
+       regulator_bulk_free(CSIS_NUM_SUPPLIES, state->supplies);
+       media_entity_cleanup(&state->sd.entity);
+       return 0;
+ }
+ static const struct dev_pm_ops s5pcsis_pm_ops = {
+       SET_RUNTIME_PM_OPS(s5pcsis_runtime_suspend, s5pcsis_runtime_resume,
+                          NULL)
+       SET_SYSTEM_SLEEP_PM_OPS(s5pcsis_suspend, s5pcsis_resume)
+ };
+ static struct platform_driver s5pcsis_driver = {
+       .probe          = s5pcsis_probe,
+       .remove         = __devexit_p(s5pcsis_remove),
+       .driver         = {
+               .name   = CSIS_DRIVER_NAME,
+               .owner  = THIS_MODULE,
+               .pm     = &s5pcsis_pm_ops,
+       },
+ };
+ module_platform_driver(s5pcsis_driver);
+ MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
+ MODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC MIPI-CSI2 receiver driver");
+ MODULE_LICENSE("GPL");
index 0000000,619df35..403d7f1
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,1863 +1,1863 @@@
 -#include <mach/mx2_cam.h>
+ /*
+  * V4L2 Driver for i.MX27/i.MX25 camera host
+  *
+  * Copyright (C) 2008, Sascha Hauer, Pengutronix
+  * Copyright (C) 2010, Baruch Siach, Orex Computed Radiography
+  * Copyright (C) 2012, Javier Martin, Vista Silicon S.L.
+  *
+  * 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.
+  */
+ #include <linux/init.h>
+ #include <linux/module.h>
+ #include <linux/io.h>
+ #include <linux/delay.h>
+ #include <linux/slab.h>
+ #include <linux/dma-mapping.h>
+ #include <linux/errno.h>
+ #include <linux/fs.h>
+ #include <linux/gcd.h>
+ #include <linux/interrupt.h>
+ #include <linux/kernel.h>
+ #include <linux/math64.h>
+ #include <linux/mm.h>
+ #include <linux/moduleparam.h>
+ #include <linux/time.h>
+ #include <linux/device.h>
+ #include <linux/platform_device.h>
+ #include <linux/mutex.h>
+ #include <linux/clk.h>
+ #include <media/v4l2-common.h>
+ #include <media/v4l2-dev.h>
+ #include <media/videobuf2-core.h>
+ #include <media/videobuf2-dma-contig.h>
+ #include <media/soc_camera.h>
+ #include <media/soc_mediabus.h>
+ #include <linux/videodev2.h>
++#include <linux/platform_data/camera-mx2.h>
+ #include <mach/hardware.h>
+ #include <asm/dma.h>
+ #define MX2_CAM_DRV_NAME "mx2-camera"
+ #define MX2_CAM_VERSION "0.0.6"
+ #define MX2_CAM_DRIVER_DESCRIPTION "i.MX2x_Camera"
+ /* reset values */
+ #define CSICR1_RESET_VAL      0x40000800
+ #define CSICR2_RESET_VAL      0x0
+ #define CSICR3_RESET_VAL      0x0
+ /* csi control reg 1 */
+ #define CSICR1_SWAP16_EN      (1 << 31)
+ #define CSICR1_EXT_VSYNC      (1 << 30)
+ #define CSICR1_EOF_INTEN      (1 << 29)
+ #define CSICR1_PRP_IF_EN      (1 << 28)
+ #define CSICR1_CCIR_MODE      (1 << 27)
+ #define CSICR1_COF_INTEN      (1 << 26)
+ #define CSICR1_SF_OR_INTEN    (1 << 25)
+ #define CSICR1_RF_OR_INTEN    (1 << 24)
+ #define CSICR1_STATFF_LEVEL   (3 << 22)
+ #define CSICR1_STATFF_INTEN   (1 << 21)
+ #define CSICR1_RXFF_LEVEL(l)  (((l) & 3) << 19)       /* MX27 */
+ #define CSICR1_FB2_DMA_INTEN  (1 << 20)               /* MX25 */
+ #define CSICR1_FB1_DMA_INTEN  (1 << 19)               /* MX25 */
+ #define CSICR1_RXFF_INTEN     (1 << 18)
+ #define CSICR1_SOF_POL                (1 << 17)
+ #define CSICR1_SOF_INTEN      (1 << 16)
+ #define CSICR1_MCLKDIV(d)     (((d) & 0xF) << 12)
+ #define CSICR1_HSYNC_POL      (1 << 11)
+ #define CSICR1_CCIR_EN                (1 << 10)
+ #define CSICR1_MCLKEN         (1 << 9)
+ #define CSICR1_FCC            (1 << 8)
+ #define CSICR1_PACK_DIR               (1 << 7)
+ #define CSICR1_CLR_STATFIFO   (1 << 6)
+ #define CSICR1_CLR_RXFIFO     (1 << 5)
+ #define CSICR1_GCLK_MODE      (1 << 4)
+ #define CSICR1_INV_DATA               (1 << 3)
+ #define CSICR1_INV_PCLK               (1 << 2)
+ #define CSICR1_REDGE          (1 << 1)
+ #define CSICR1_FMT_MASK               (CSICR1_PACK_DIR | CSICR1_SWAP16_EN)
+ #define SHIFT_STATFF_LEVEL    22
+ #define SHIFT_RXFF_LEVEL      19
+ #define SHIFT_MCLKDIV         12
+ /* control reg 3 */
+ #define CSICR3_FRMCNT         (0xFFFF << 16)
+ #define CSICR3_FRMCNT_RST     (1 << 15)
+ #define CSICR3_DMA_REFLASH_RFF        (1 << 14)
+ #define CSICR3_DMA_REFLASH_SFF        (1 << 13)
+ #define CSICR3_DMA_REQ_EN_RFF (1 << 12)
+ #define CSICR3_DMA_REQ_EN_SFF (1 << 11)
+ #define CSICR3_RXFF_LEVEL(l)  (((l) & 7) << 4)        /* MX25 */
+ #define CSICR3_CSI_SUP                (1 << 3)
+ #define CSICR3_ZERO_PACK_EN   (1 << 2)
+ #define CSICR3_ECC_INT_EN     (1 << 1)
+ #define CSICR3_ECC_AUTO_EN    (1 << 0)
+ #define SHIFT_FRMCNT          16
+ /* csi status reg */
+ #define CSISR_SFF_OR_INT      (1 << 25)
+ #define CSISR_RFF_OR_INT      (1 << 24)
+ #define CSISR_STATFF_INT      (1 << 21)
+ #define CSISR_DMA_TSF_FB2_INT (1 << 20)       /* MX25 */
+ #define CSISR_DMA_TSF_FB1_INT (1 << 19)       /* MX25 */
+ #define CSISR_RXFF_INT                (1 << 18)
+ #define CSISR_EOF_INT         (1 << 17)
+ #define CSISR_SOF_INT         (1 << 16)
+ #define CSISR_F2_INT          (1 << 15)
+ #define CSISR_F1_INT          (1 << 14)
+ #define CSISR_COF_INT         (1 << 13)
+ #define CSISR_ECC_INT         (1 << 1)
+ #define CSISR_DRDY            (1 << 0)
+ #define CSICR1                        0x00
+ #define CSICR2                        0x04
+ #define CSISR                 (cpu_is_mx27() ? 0x08 : 0x18)
+ #define CSISTATFIFO           0x0c
+ #define CSIRFIFO              0x10
+ #define CSIRXCNT              0x14
+ #define CSICR3                        (cpu_is_mx27() ? 0x1C : 0x08)
+ #define CSIDMASA_STATFIFO     0x20
+ #define CSIDMATA_STATFIFO     0x24
+ #define CSIDMASA_FB1          0x28
+ #define CSIDMASA_FB2          0x2c
+ #define CSIFBUF_PARA          0x30
+ #define CSIIMAG_PARA          0x34
+ /* EMMA PrP */
+ #define PRP_CNTL                      0x00
+ #define PRP_INTR_CNTL                 0x04
+ #define PRP_INTRSTATUS                        0x08
+ #define PRP_SOURCE_Y_PTR              0x0c
+ #define PRP_SOURCE_CB_PTR             0x10
+ #define PRP_SOURCE_CR_PTR             0x14
+ #define PRP_DEST_RGB1_PTR             0x18
+ #define PRP_DEST_RGB2_PTR             0x1c
+ #define PRP_DEST_Y_PTR                        0x20
+ #define PRP_DEST_CB_PTR                       0x24
+ #define PRP_DEST_CR_PTR                       0x28
+ #define PRP_SRC_FRAME_SIZE            0x2c
+ #define PRP_DEST_CH1_LINE_STRIDE      0x30
+ #define PRP_SRC_PIXEL_FORMAT_CNTL     0x34
+ #define PRP_CH1_PIXEL_FORMAT_CNTL     0x38
+ #define PRP_CH1_OUT_IMAGE_SIZE                0x3c
+ #define PRP_CH2_OUT_IMAGE_SIZE                0x40
+ #define PRP_SRC_LINE_STRIDE           0x44
+ #define PRP_CSC_COEF_012              0x48
+ #define PRP_CSC_COEF_345              0x4c
+ #define PRP_CSC_COEF_678              0x50
+ #define PRP_CH1_RZ_HORI_COEF1         0x54
+ #define PRP_CH1_RZ_HORI_COEF2         0x58
+ #define PRP_CH1_RZ_HORI_VALID         0x5c
+ #define PRP_CH1_RZ_VERT_COEF1         0x60
+ #define PRP_CH1_RZ_VERT_COEF2         0x64
+ #define PRP_CH1_RZ_VERT_VALID         0x68
+ #define PRP_CH2_RZ_HORI_COEF1         0x6c
+ #define PRP_CH2_RZ_HORI_COEF2         0x70
+ #define PRP_CH2_RZ_HORI_VALID         0x74
+ #define PRP_CH2_RZ_VERT_COEF1         0x78
+ #define PRP_CH2_RZ_VERT_COEF2         0x7c
+ #define PRP_CH2_RZ_VERT_VALID         0x80
+ #define PRP_CNTL_CH1EN                (1 << 0)
+ #define PRP_CNTL_CH2EN                (1 << 1)
+ #define PRP_CNTL_CSIEN                (1 << 2)
+ #define PRP_CNTL_DATA_IN_YUV420       (0 << 3)
+ #define PRP_CNTL_DATA_IN_YUV422       (1 << 3)
+ #define PRP_CNTL_DATA_IN_RGB16        (2 << 3)
+ #define PRP_CNTL_DATA_IN_RGB32        (3 << 3)
+ #define PRP_CNTL_CH1_OUT_RGB8 (0 << 5)
+ #define PRP_CNTL_CH1_OUT_RGB16        (1 << 5)
+ #define PRP_CNTL_CH1_OUT_RGB32        (2 << 5)
+ #define PRP_CNTL_CH1_OUT_YUV422       (3 << 5)
+ #define PRP_CNTL_CH2_OUT_YUV420       (0 << 7)
+ #define PRP_CNTL_CH2_OUT_YUV422 (1 << 7)
+ #define PRP_CNTL_CH2_OUT_YUV444       (2 << 7)
+ #define PRP_CNTL_CH1_LEN      (1 << 9)
+ #define PRP_CNTL_CH2_LEN      (1 << 10)
+ #define PRP_CNTL_SKIP_FRAME   (1 << 11)
+ #define PRP_CNTL_SWRST                (1 << 12)
+ #define PRP_CNTL_CLKEN                (1 << 13)
+ #define PRP_CNTL_WEN          (1 << 14)
+ #define PRP_CNTL_CH1BYP               (1 << 15)
+ #define PRP_CNTL_IN_TSKIP(x)  ((x) << 16)
+ #define PRP_CNTL_CH1_TSKIP(x) ((x) << 19)
+ #define PRP_CNTL_CH2_TSKIP(x) ((x) << 22)
+ #define PRP_CNTL_INPUT_FIFO_LEVEL(x)  ((x) << 25)
+ #define PRP_CNTL_RZ_FIFO_LEVEL(x)     ((x) << 27)
+ #define PRP_CNTL_CH2B1EN      (1 << 29)
+ #define PRP_CNTL_CH2B2EN      (1 << 30)
+ #define PRP_CNTL_CH2FEN               (1 << 31)
+ /* IRQ Enable and status register */
+ #define PRP_INTR_RDERR                (1 << 0)
+ #define PRP_INTR_CH1WERR      (1 << 1)
+ #define PRP_INTR_CH2WERR      (1 << 2)
+ #define PRP_INTR_CH1FC                (1 << 3)
+ #define PRP_INTR_CH2FC                (1 << 5)
+ #define PRP_INTR_LBOVF                (1 << 7)
+ #define PRP_INTR_CH2OVF               (1 << 8)
+ /* Resizing registers */
+ #define PRP_RZ_VALID_TBL_LEN(x)       ((x) << 24)
+ #define PRP_RZ_VALID_BILINEAR (1 << 31)
+ #define MAX_VIDEO_MEM 16
+ #define RESIZE_NUM_MIN        1
+ #define RESIZE_NUM_MAX        20
+ #define BC_COEF               3
+ #define SZ_COEF               (1 << BC_COEF)
+ #define RESIZE_DIR_H  0
+ #define RESIZE_DIR_V  1
+ #define RESIZE_ALGO_BILINEAR 0
+ #define RESIZE_ALGO_AVERAGING 1
+ struct mx2_prp_cfg {
+       int channel;
+       u32 in_fmt;
+       u32 out_fmt;
+       u32 src_pixel;
+       u32 ch1_pixel;
+       u32 irq_flags;
+       u32 csicr1;
+ };
+ /* prp resizing parameters */
+ struct emma_prp_resize {
+       int             algo; /* type of algorithm used */
+       int             len; /* number of coefficients */
+       unsigned char   s[RESIZE_NUM_MAX]; /* table of coefficients */
+ };
+ /* prp configuration for a client-host fmt pair */
+ struct mx2_fmt_cfg {
+       enum v4l2_mbus_pixelcode        in_fmt;
+       u32                             out_fmt;
+       struct mx2_prp_cfg              cfg;
+ };
+ enum mx2_buffer_state {
+       MX2_STATE_QUEUED,
+       MX2_STATE_ACTIVE,
+       MX2_STATE_DONE,
+ };
+ struct mx2_buf_internal {
+       struct list_head        queue;
+       int                     bufnum;
+       bool                    discard;
+ };
+ /* buffer for one video frame */
+ struct mx2_buffer {
+       /* common v4l buffer stuff -- must be first */
+       struct vb2_buffer               vb;
+       enum mx2_buffer_state           state;
+       struct mx2_buf_internal         internal;
+ };
+ struct mx2_camera_dev {
+       struct device           *dev;
+       struct soc_camera_host  soc_host;
+       struct soc_camera_device *icd;
+       struct clk              *clk_csi, *clk_emma_ahb, *clk_emma_ipg;
+       void __iomem            *base_csi, *base_emma;
+       struct mx2_camera_platform_data *pdata;
+       unsigned long           platform_flags;
+       struct list_head        capture;
+       struct list_head        active_bufs;
+       struct list_head        discard;
+       spinlock_t              lock;
+       int                     dma;
+       struct mx2_buffer       *active;
+       struct mx2_buffer       *fb1_active;
+       struct mx2_buffer       *fb2_active;
+       u32                     csicr1;
+       struct mx2_buf_internal buf_discard[2];
+       void                    *discard_buffer;
+       dma_addr_t              discard_buffer_dma;
+       size_t                  discard_size;
+       struct mx2_fmt_cfg      *emma_prp;
+       struct emma_prp_resize  resizing[2];
+       unsigned int            s_width, s_height;
+       u32                     frame_count;
+       struct vb2_alloc_ctx    *alloc_ctx;
+ };
+ static struct mx2_buffer *mx2_ibuf_to_buf(struct mx2_buf_internal *int_buf)
+ {
+       return container_of(int_buf, struct mx2_buffer, internal);
+ }
+ static struct mx2_fmt_cfg mx27_emma_prp_table[] = {
+       /*
+        * This is a generic configuration which is valid for most
+        * prp input-output format combinations.
+        * We set the incomming and outgoing pixelformat to a
+        * 16 Bit wide format and adjust the bytesperline
+        * accordingly. With this configuration the inputdata
+        * will not be changed by the emma and could be any type
+        * of 16 Bit Pixelformat.
+        */
+       {
+               .in_fmt         = 0,
+               .out_fmt        = 0,
+               .cfg            = {
+                       .channel        = 1,
+                       .in_fmt         = PRP_CNTL_DATA_IN_RGB16,
+                       .out_fmt        = PRP_CNTL_CH1_OUT_RGB16,
+                       .src_pixel      = 0x2ca00565, /* RGB565 */
+                       .ch1_pixel      = 0x2ca00565, /* RGB565 */
+                       .irq_flags      = PRP_INTR_RDERR | PRP_INTR_CH1WERR |
+                                               PRP_INTR_CH1FC | PRP_INTR_LBOVF,
+                       .csicr1         = 0,
+               }
+       },
+       {
+               .in_fmt         = V4L2_MBUS_FMT_UYVY8_2X8,
+               .out_fmt        = V4L2_PIX_FMT_YUYV,
+               .cfg            = {
+                       .channel        = 1,
+                       .in_fmt         = PRP_CNTL_DATA_IN_YUV422,
+                       .out_fmt        = PRP_CNTL_CH1_OUT_YUV422,
+                       .src_pixel      = 0x22000888, /* YUV422 (YUYV) */
+                       .ch1_pixel      = 0x62000888, /* YUV422 (YUYV) */
+                       .irq_flags      = PRP_INTR_RDERR | PRP_INTR_CH1WERR |
+                                               PRP_INTR_CH1FC | PRP_INTR_LBOVF,
+                       .csicr1         = CSICR1_SWAP16_EN,
+               }
+       },
+       {
+               .in_fmt         = V4L2_MBUS_FMT_YUYV8_2X8,
+               .out_fmt        = V4L2_PIX_FMT_YUYV,
+               .cfg            = {
+                       .channel        = 1,
+                       .in_fmt         = PRP_CNTL_DATA_IN_YUV422,
+                       .out_fmt        = PRP_CNTL_CH1_OUT_YUV422,
+                       .src_pixel      = 0x22000888, /* YUV422 (YUYV) */
+                       .ch1_pixel      = 0x62000888, /* YUV422 (YUYV) */
+                       .irq_flags      = PRP_INTR_RDERR | PRP_INTR_CH1WERR |
+                                               PRP_INTR_CH1FC | PRP_INTR_LBOVF,
+                       .csicr1         = CSICR1_PACK_DIR,
+               }
+       },
+       {
+               .in_fmt         = V4L2_MBUS_FMT_YUYV8_2X8,
+               .out_fmt        = V4L2_PIX_FMT_YUV420,
+               .cfg            = {
+                       .channel        = 2,
+                       .in_fmt         = PRP_CNTL_DATA_IN_YUV422,
+                       .out_fmt        = PRP_CNTL_CH2_OUT_YUV420,
+                       .src_pixel      = 0x22000888, /* YUV422 (YUYV) */
+                       .irq_flags      = PRP_INTR_RDERR | PRP_INTR_CH2WERR |
+                                       PRP_INTR_CH2FC | PRP_INTR_LBOVF |
+                                       PRP_INTR_CH2OVF,
+                       .csicr1         = CSICR1_PACK_DIR,
+               }
+       },
+       {
+               .in_fmt         = V4L2_MBUS_FMT_UYVY8_2X8,
+               .out_fmt        = V4L2_PIX_FMT_YUV420,
+               .cfg            = {
+                       .channel        = 2,
+                       .in_fmt         = PRP_CNTL_DATA_IN_YUV422,
+                       .out_fmt        = PRP_CNTL_CH2_OUT_YUV420,
+                       .src_pixel      = 0x22000888, /* YUV422 (YUYV) */
+                       .irq_flags      = PRP_INTR_RDERR | PRP_INTR_CH2WERR |
+                                       PRP_INTR_CH2FC | PRP_INTR_LBOVF |
+                                       PRP_INTR_CH2OVF,
+                       .csicr1         = CSICR1_SWAP16_EN,
+               }
+       },
+ };
+ static struct mx2_fmt_cfg *mx27_emma_prp_get_format(
+                                       enum v4l2_mbus_pixelcode in_fmt,
+                                       u32 out_fmt)
+ {
+       int i;
+       for (i = 1; i < ARRAY_SIZE(mx27_emma_prp_table); i++)
+               if ((mx27_emma_prp_table[i].in_fmt == in_fmt) &&
+                               (mx27_emma_prp_table[i].out_fmt == out_fmt)) {
+                       return &mx27_emma_prp_table[i];
+               }
+       /* If no match return the most generic configuration */
+       return &mx27_emma_prp_table[0];
+ };
+ static void mx27_update_emma_buf(struct mx2_camera_dev *pcdev,
+                                unsigned long phys, int bufnum)
+ {
+       struct mx2_fmt_cfg *prp = pcdev->emma_prp;
+       if (prp->cfg.channel == 1) {
+               writel(phys, pcdev->base_emma +
+                               PRP_DEST_RGB1_PTR + 4 * bufnum);
+       } else {
+               writel(phys, pcdev->base_emma +
+                       PRP_DEST_Y_PTR - 0x14 * bufnum);
+               if (prp->out_fmt == V4L2_PIX_FMT_YUV420) {
+                       u32 imgsize = pcdev->icd->user_height *
+                                       pcdev->icd->user_width;
+                       writel(phys + imgsize, pcdev->base_emma +
+                               PRP_DEST_CB_PTR - 0x14 * bufnum);
+                       writel(phys + ((5 * imgsize) / 4), pcdev->base_emma +
+                               PRP_DEST_CR_PTR - 0x14 * bufnum);
+               }
+       }
+ }
+ static void mx2_camera_deactivate(struct mx2_camera_dev *pcdev)
+ {
+       unsigned long flags;
+       clk_disable_unprepare(pcdev->clk_csi);
+       writel(0, pcdev->base_csi + CSICR1);
+       if (cpu_is_mx27()) {
+               writel(0, pcdev->base_emma + PRP_CNTL);
+       } else if (cpu_is_mx25()) {
+               spin_lock_irqsave(&pcdev->lock, flags);
+               pcdev->fb1_active = NULL;
+               pcdev->fb2_active = NULL;
+               writel(0, pcdev->base_csi + CSIDMASA_FB1);
+               writel(0, pcdev->base_csi + CSIDMASA_FB2);
+               spin_unlock_irqrestore(&pcdev->lock, flags);
+       }
+ }
+ /*
+  * The following two functions absolutely depend on the fact, that
+  * there can be only one camera on mx2 camera sensor interface
+  */
+ static int mx2_camera_add_device(struct soc_camera_device *icd)
+ {
+       struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+       struct mx2_camera_dev *pcdev = ici->priv;
+       int ret;
+       u32 csicr1;
+       if (pcdev->icd)
+               return -EBUSY;
+       ret = clk_prepare_enable(pcdev->clk_csi);
+       if (ret < 0)
+               return ret;
+       csicr1 = CSICR1_MCLKEN;
+       if (cpu_is_mx27())
+               csicr1 |= CSICR1_PRP_IF_EN | CSICR1_FCC |
+                       CSICR1_RXFF_LEVEL(0);
+       pcdev->csicr1 = csicr1;
+       writel(pcdev->csicr1, pcdev->base_csi + CSICR1);
+       pcdev->icd = icd;
+       pcdev->frame_count = 0;
+       dev_info(icd->parent, "Camera driver attached to camera %d\n",
+                icd->devnum);
+       return 0;
+ }
+ static void mx2_camera_remove_device(struct soc_camera_device *icd)
+ {
+       struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+       struct mx2_camera_dev *pcdev = ici->priv;
+       BUG_ON(icd != pcdev->icd);
+       dev_info(icd->parent, "Camera driver detached from camera %d\n",
+                icd->devnum);
+       mx2_camera_deactivate(pcdev);
+       pcdev->icd = NULL;
+ }
+ static void mx25_camera_frame_done(struct mx2_camera_dev *pcdev, int fb,
+               int state)
+ {
+       struct vb2_buffer *vb;
+       struct mx2_buffer *buf;
+       struct mx2_buffer **fb_active = fb == 1 ? &pcdev->fb1_active :
+               &pcdev->fb2_active;
+       u32 fb_reg = fb == 1 ? CSIDMASA_FB1 : CSIDMASA_FB2;
+       unsigned long flags;
+       spin_lock_irqsave(&pcdev->lock, flags);
+       if (*fb_active == NULL)
+               goto out;
+       vb = &(*fb_active)->vb;
+       dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%p %lu\n", __func__,
+               vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
+       do_gettimeofday(&vb->v4l2_buf.timestamp);
+       vb->v4l2_buf.sequence++;
+       vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+       if (list_empty(&pcdev->capture)) {
+               buf = NULL;
+               writel(0, pcdev->base_csi + fb_reg);
+       } else {
+               buf = list_first_entry(&pcdev->capture, struct mx2_buffer,
+                               internal.queue);
+               vb = &buf->vb;
+               list_del(&buf->internal.queue);
+               buf->state = MX2_STATE_ACTIVE;
+               writel(vb2_dma_contig_plane_dma_addr(vb, 0),
+                      pcdev->base_csi + fb_reg);
+       }
+       *fb_active = buf;
+ out:
+       spin_unlock_irqrestore(&pcdev->lock, flags);
+ }
+ static irqreturn_t mx25_camera_irq(int irq_csi, void *data)
+ {
+       struct mx2_camera_dev *pcdev = data;
+       u32 status = readl(pcdev->base_csi + CSISR);
+       if (status & CSISR_DMA_TSF_FB1_INT)
+               mx25_camera_frame_done(pcdev, 1, MX2_STATE_DONE);
+       else if (status & CSISR_DMA_TSF_FB2_INT)
+               mx25_camera_frame_done(pcdev, 2, MX2_STATE_DONE);
+       /* FIXME: handle CSISR_RFF_OR_INT */
+       writel(status, pcdev->base_csi + CSISR);
+       return IRQ_HANDLED;
+ }
+ /*
+  *  Videobuf operations
+  */
+ static int mx2_videobuf_setup(struct vb2_queue *vq,
+                       const struct v4l2_format *fmt,
+                       unsigned int *count, unsigned int *num_planes,
+                       unsigned int sizes[], void *alloc_ctxs[])
+ {
+       struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
+       struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+       struct mx2_camera_dev *pcdev = ici->priv;
+       dev_dbg(icd->parent, "count=%d, size=%d\n", *count, sizes[0]);
+       /* TODO: support for VIDIOC_CREATE_BUFS not ready */
+       if (fmt != NULL)
+               return -ENOTTY;
+       alloc_ctxs[0] = pcdev->alloc_ctx;
+       sizes[0] = icd->sizeimage;
+       if (0 == *count)
+               *count = 32;
+       if (!*num_planes &&
+           sizes[0] * *count > MAX_VIDEO_MEM * 1024 * 1024)
+               *count = (MAX_VIDEO_MEM * 1024 * 1024) / sizes[0];
+       *num_planes = 1;
+       return 0;
+ }
+ static int mx2_videobuf_prepare(struct vb2_buffer *vb)
+ {
+       struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
+       int ret = 0;
+       dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__,
+               vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
+ #ifdef DEBUG
+       /*
+        * This can be useful if you want to see if we actually fill
+        * the buffer with something
+        */
+       memset((void *)vb2_plane_vaddr(vb, 0),
+              0xaa, vb2_get_plane_payload(vb, 0));
+ #endif
+       vb2_set_plane_payload(vb, 0, icd->sizeimage);
+       if (vb2_plane_vaddr(vb, 0) &&
+           vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) {
+               ret = -EINVAL;
+               goto out;
+       }
+       return 0;
+ out:
+       return ret;
+ }
+ static void mx2_videobuf_queue(struct vb2_buffer *vb)
+ {
+       struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
+       struct soc_camera_host *ici =
+               to_soc_camera_host(icd->parent);
+       struct mx2_camera_dev *pcdev = ici->priv;
+       struct mx2_buffer *buf = container_of(vb, struct mx2_buffer, vb);
+       unsigned long flags;
+       dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__,
+               vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
+       spin_lock_irqsave(&pcdev->lock, flags);
+       buf->state = MX2_STATE_QUEUED;
+       list_add_tail(&buf->internal.queue, &pcdev->capture);
+       if (cpu_is_mx25()) {
+               u32 csicr3, dma_inten = 0;
+               if (pcdev->fb1_active == NULL) {
+                       writel(vb2_dma_contig_plane_dma_addr(vb, 0),
+                                       pcdev->base_csi + CSIDMASA_FB1);
+                       pcdev->fb1_active = buf;
+                       dma_inten = CSICR1_FB1_DMA_INTEN;
+               } else if (pcdev->fb2_active == NULL) {
+                       writel(vb2_dma_contig_plane_dma_addr(vb, 0),
+                                       pcdev->base_csi + CSIDMASA_FB2);
+                       pcdev->fb2_active = buf;
+                       dma_inten = CSICR1_FB2_DMA_INTEN;
+               }
+               if (dma_inten) {
+                       list_del(&buf->internal.queue);
+                       buf->state = MX2_STATE_ACTIVE;
+                       csicr3 = readl(pcdev->base_csi + CSICR3);
+                       /* Reflash DMA */
+                       writel(csicr3 | CSICR3_DMA_REFLASH_RFF,
+                                       pcdev->base_csi + CSICR3);
+                       /* clear & enable interrupts */
+                       writel(dma_inten, pcdev->base_csi + CSISR);
+                       pcdev->csicr1 |= dma_inten;
+                       writel(pcdev->csicr1, pcdev->base_csi + CSICR1);
+                       /* enable DMA */
+                       csicr3 |= CSICR3_DMA_REQ_EN_RFF | CSICR3_RXFF_LEVEL(1);
+                       writel(csicr3, pcdev->base_csi + CSICR3);
+               }
+       }
+       spin_unlock_irqrestore(&pcdev->lock, flags);
+ }
+ static void mx2_videobuf_release(struct vb2_buffer *vb)
+ {
+       struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
+       struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+       struct mx2_camera_dev *pcdev = ici->priv;
+       struct mx2_buffer *buf = container_of(vb, struct mx2_buffer, vb);
+       unsigned long flags;
+ #ifdef DEBUG
+       dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__,
+               vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
+       switch (buf->state) {
+       case MX2_STATE_ACTIVE:
+               dev_info(icd->parent, "%s (active)\n", __func__);
+               break;
+       case MX2_STATE_QUEUED:
+               dev_info(icd->parent, "%s (queued)\n", __func__);
+               break;
+       default:
+               dev_info(icd->parent, "%s (unknown) %d\n", __func__,
+                               buf->state);
+               break;
+       }
+ #endif
+       /*
+        * Terminate only queued but inactive buffers. Active buffers are
+        * released when they become inactive after videobuf_waiton().
+        *
+        * FIXME: implement forced termination of active buffers for mx27 and
+        * mx27 eMMA, so that the user won't get stuck in an uninterruptible
+        * state. This requires a specific handling for each of the these DMA
+        * types.
+        */
+       spin_lock_irqsave(&pcdev->lock, flags);
+       if (cpu_is_mx25() && buf->state == MX2_STATE_ACTIVE) {
+               if (pcdev->fb1_active == buf) {
+                       pcdev->csicr1 &= ~CSICR1_FB1_DMA_INTEN;
+                       writel(0, pcdev->base_csi + CSIDMASA_FB1);
+                       pcdev->fb1_active = NULL;
+               } else if (pcdev->fb2_active == buf) {
+                       pcdev->csicr1 &= ~CSICR1_FB2_DMA_INTEN;
+                       writel(0, pcdev->base_csi + CSIDMASA_FB2);
+                       pcdev->fb2_active = NULL;
+               }
+               writel(pcdev->csicr1, pcdev->base_csi + CSICR1);
+       }
+       spin_unlock_irqrestore(&pcdev->lock, flags);
+ }
+ static void mx27_camera_emma_buf_init(struct soc_camera_device *icd,
+               int bytesperline)
+ {
+       struct soc_camera_host *ici =
+               to_soc_camera_host(icd->parent);
+       struct mx2_camera_dev *pcdev = ici->priv;
+       struct mx2_fmt_cfg *prp = pcdev->emma_prp;
+       writel((pcdev->s_width << 16) | pcdev->s_height,
+              pcdev->base_emma + PRP_SRC_FRAME_SIZE);
+       writel(prp->cfg.src_pixel,
+              pcdev->base_emma + PRP_SRC_PIXEL_FORMAT_CNTL);
+       if (prp->cfg.channel == 1) {
+               writel((icd->user_width << 16) | icd->user_height,
+                       pcdev->base_emma + PRP_CH1_OUT_IMAGE_SIZE);
+               writel(bytesperline,
+                       pcdev->base_emma + PRP_DEST_CH1_LINE_STRIDE);
+               writel(prp->cfg.ch1_pixel,
+                       pcdev->base_emma + PRP_CH1_PIXEL_FORMAT_CNTL);
+       } else { /* channel 2 */
+               writel((icd->user_width << 16) | icd->user_height,
+                       pcdev->base_emma + PRP_CH2_OUT_IMAGE_SIZE);
+       }
+       /* Enable interrupts */
+       writel(prp->cfg.irq_flags, pcdev->base_emma + PRP_INTR_CNTL);
+ }
+ static void mx2_prp_resize_commit(struct mx2_camera_dev *pcdev)
+ {
+       int dir;
+       for (dir = RESIZE_DIR_H; dir <= RESIZE_DIR_V; dir++) {
+               unsigned char *s = pcdev->resizing[dir].s;
+               int len = pcdev->resizing[dir].len;
+               unsigned int coeff[2] = {0, 0};
+               unsigned int valid  = 0;
+               int i;
+               if (len == 0)
+                       continue;
+               for (i = RESIZE_NUM_MAX - 1; i >= 0; i--) {
+                       int j;
+                       j = i > 9 ? 1 : 0;
+                       coeff[j] = (coeff[j] << BC_COEF) |
+                                       (s[i] & (SZ_COEF - 1));
+                       if (i == 5 || i == 15)
+                               coeff[j] <<= 1;
+                       valid = (valid << 1) | (s[i] >> BC_COEF);
+               }
+               valid |= PRP_RZ_VALID_TBL_LEN(len);
+               if (pcdev->resizing[dir].algo == RESIZE_ALGO_BILINEAR)
+                       valid |= PRP_RZ_VALID_BILINEAR;
+               if (pcdev->emma_prp->cfg.channel == 1) {
+                       if (dir == RESIZE_DIR_H) {
+                               writel(coeff[0], pcdev->base_emma +
+                                                       PRP_CH1_RZ_HORI_COEF1);
+                               writel(coeff[1], pcdev->base_emma +
+                                                       PRP_CH1_RZ_HORI_COEF2);
+                               writel(valid, pcdev->base_emma +
+                                                       PRP_CH1_RZ_HORI_VALID);
+                       } else {
+                               writel(coeff[0], pcdev->base_emma +
+                                                       PRP_CH1_RZ_VERT_COEF1);
+                               writel(coeff[1], pcdev->base_emma +
+                                                       PRP_CH1_RZ_VERT_COEF2);
+                               writel(valid, pcdev->base_emma +
+                                                       PRP_CH1_RZ_VERT_VALID);
+                       }
+               } else {
+                       if (dir == RESIZE_DIR_H) {
+                               writel(coeff[0], pcdev->base_emma +
+                                                       PRP_CH2_RZ_HORI_COEF1);
+                               writel(coeff[1], pcdev->base_emma +
+                                                       PRP_CH2_RZ_HORI_COEF2);
+                               writel(valid, pcdev->base_emma +
+                                                       PRP_CH2_RZ_HORI_VALID);
+                       } else {
+                               writel(coeff[0], pcdev->base_emma +
+                                                       PRP_CH2_RZ_VERT_COEF1);
+                               writel(coeff[1], pcdev->base_emma +
+                                                       PRP_CH2_RZ_VERT_COEF2);
+                               writel(valid, pcdev->base_emma +
+                                                       PRP_CH2_RZ_VERT_VALID);
+                       }
+               }
+       }
+ }
+ static int mx2_start_streaming(struct vb2_queue *q, unsigned int count)
+ {
+       struct soc_camera_device *icd = soc_camera_from_vb2q(q);
+       struct soc_camera_host *ici =
+               to_soc_camera_host(icd->parent);
+       struct mx2_camera_dev *pcdev = ici->priv;
+       struct mx2_fmt_cfg *prp = pcdev->emma_prp;
+       struct vb2_buffer *vb;
+       struct mx2_buffer *buf;
+       unsigned long phys;
+       int bytesperline;
+       if (cpu_is_mx27()) {
+               unsigned long flags;
+               if (count < 2)
+                       return -EINVAL;
+               spin_lock_irqsave(&pcdev->lock, flags);
+               buf = list_first_entry(&pcdev->capture, struct mx2_buffer,
+                                      internal.queue);
+               buf->internal.bufnum = 0;
+               vb = &buf->vb;
+               buf->state = MX2_STATE_ACTIVE;
+               phys = vb2_dma_contig_plane_dma_addr(vb, 0);
+               mx27_update_emma_buf(pcdev, phys, buf->internal.bufnum);
+               list_move_tail(pcdev->capture.next, &pcdev->active_bufs);
+               buf = list_first_entry(&pcdev->capture, struct mx2_buffer,
+                                      internal.queue);
+               buf->internal.bufnum = 1;
+               vb = &buf->vb;
+               buf->state = MX2_STATE_ACTIVE;
+               phys = vb2_dma_contig_plane_dma_addr(vb, 0);
+               mx27_update_emma_buf(pcdev, phys, buf->internal.bufnum);
+               list_move_tail(pcdev->capture.next, &pcdev->active_bufs);
+               bytesperline = soc_mbus_bytes_per_line(icd->user_width,
+                               icd->current_fmt->host_fmt);
+               if (bytesperline < 0)
+                       return bytesperline;
+               /*
+                * I didn't manage to properly enable/disable the prp
+                * on a per frame basis during running transfers,
+                * thus we allocate a buffer here and use it to
+                * discard frames when no buffer is available.
+                * Feel free to work on this ;)
+                */
+               pcdev->discard_size = icd->user_height * bytesperline;
+               pcdev->discard_buffer = dma_alloc_coherent(ici->v4l2_dev.dev,
+                               pcdev->discard_size, &pcdev->discard_buffer_dma,
+                               GFP_KERNEL);
+               if (!pcdev->discard_buffer)
+                       return -ENOMEM;
+               pcdev->buf_discard[0].discard = true;
+               list_add_tail(&pcdev->buf_discard[0].queue,
+                                     &pcdev->discard);
+               pcdev->buf_discard[1].discard = true;
+               list_add_tail(&pcdev->buf_discard[1].queue,
+                                     &pcdev->discard);
+               mx2_prp_resize_commit(pcdev);
+               mx27_camera_emma_buf_init(icd, bytesperline);
+               if (prp->cfg.channel == 1) {
+                       writel(PRP_CNTL_CH1EN |
+                               PRP_CNTL_CSIEN |
+                               prp->cfg.in_fmt |
+                               prp->cfg.out_fmt |
+                               PRP_CNTL_CH1_LEN |
+                               PRP_CNTL_CH1BYP |
+                               PRP_CNTL_CH1_TSKIP(0) |
+                               PRP_CNTL_IN_TSKIP(0),
+                               pcdev->base_emma + PRP_CNTL);
+               } else {
+                       writel(PRP_CNTL_CH2EN |
+                               PRP_CNTL_CSIEN |
+                               prp->cfg.in_fmt |
+                               prp->cfg.out_fmt |
+                               PRP_CNTL_CH2_LEN |
+                               PRP_CNTL_CH2_TSKIP(0) |
+                               PRP_CNTL_IN_TSKIP(0),
+                               pcdev->base_emma + PRP_CNTL);
+               }
+               spin_unlock_irqrestore(&pcdev->lock, flags);
+       }
+       return 0;
+ }
+ static int mx2_stop_streaming(struct vb2_queue *q)
+ {
+       struct soc_camera_device *icd = soc_camera_from_vb2q(q);
+       struct soc_camera_host *ici =
+               to_soc_camera_host(icd->parent);
+       struct mx2_camera_dev *pcdev = ici->priv;
+       struct mx2_fmt_cfg *prp = pcdev->emma_prp;
+       unsigned long flags;
+       void *b;
+       u32 cntl;
+       if (cpu_is_mx27()) {
+               spin_lock_irqsave(&pcdev->lock, flags);
+               cntl = readl(pcdev->base_emma + PRP_CNTL);
+               if (prp->cfg.channel == 1) {
+                       writel(cntl & ~PRP_CNTL_CH1EN,
+                              pcdev->base_emma + PRP_CNTL);
+               } else {
+                       writel(cntl & ~PRP_CNTL_CH2EN,
+                              pcdev->base_emma + PRP_CNTL);
+               }
+               INIT_LIST_HEAD(&pcdev->capture);
+               INIT_LIST_HEAD(&pcdev->active_bufs);
+               INIT_LIST_HEAD(&pcdev->discard);
+               b = pcdev->discard_buffer;
+               pcdev->discard_buffer = NULL;
+               spin_unlock_irqrestore(&pcdev->lock, flags);
+               dma_free_coherent(ici->v4l2_dev.dev,
+                       pcdev->discard_size, b, pcdev->discard_buffer_dma);
+       }
+       return 0;
+ }
+ static struct vb2_ops mx2_videobuf_ops = {
+       .queue_setup     = mx2_videobuf_setup,
+       .buf_prepare     = mx2_videobuf_prepare,
+       .buf_queue       = mx2_videobuf_queue,
+       .buf_cleanup     = mx2_videobuf_release,
+       .start_streaming = mx2_start_streaming,
+       .stop_streaming  = mx2_stop_streaming,
+ };
+ static int mx2_camera_init_videobuf(struct vb2_queue *q,
+                             struct soc_camera_device *icd)
+ {
+       q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       q->io_modes = VB2_MMAP | VB2_USERPTR;
+       q->drv_priv = icd;
+       q->ops = &mx2_videobuf_ops;
+       q->mem_ops = &vb2_dma_contig_memops;
+       q->buf_struct_size = sizeof(struct mx2_buffer);
+       return vb2_queue_init(q);
+ }
+ #define MX2_BUS_FLAGS (V4L2_MBUS_MASTER | \
+                       V4L2_MBUS_VSYNC_ACTIVE_HIGH | \
+                       V4L2_MBUS_VSYNC_ACTIVE_LOW | \
+                       V4L2_MBUS_HSYNC_ACTIVE_HIGH | \
+                       V4L2_MBUS_HSYNC_ACTIVE_LOW | \
+                       V4L2_MBUS_PCLK_SAMPLE_RISING | \
+                       V4L2_MBUS_PCLK_SAMPLE_FALLING | \
+                       V4L2_MBUS_DATA_ACTIVE_HIGH | \
+                       V4L2_MBUS_DATA_ACTIVE_LOW)
+ static int mx27_camera_emma_prp_reset(struct mx2_camera_dev *pcdev)
+ {
+       u32 cntl;
+       int count = 0;
+       cntl = readl(pcdev->base_emma + PRP_CNTL);
+       writel(PRP_CNTL_SWRST, pcdev->base_emma + PRP_CNTL);
+       while (count++ < 100) {
+               if (!(readl(pcdev->base_emma + PRP_CNTL) & PRP_CNTL_SWRST))
+                       return 0;
+               barrier();
+               udelay(1);
+       }
+       return -ETIMEDOUT;
+ }
+ static int mx2_camera_set_bus_param(struct soc_camera_device *icd)
+ {
+       struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+       struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+       struct mx2_camera_dev *pcdev = ici->priv;
+       struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+       unsigned long common_flags;
+       int ret;
+       int bytesperline;
+       u32 csicr1 = pcdev->csicr1;
+       ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+       if (!ret) {
+               common_flags = soc_mbus_config_compatible(&cfg, MX2_BUS_FLAGS);
+               if (!common_flags) {
+                       dev_warn(icd->parent,
+                                "Flags incompatible: camera 0x%x, host 0x%x\n",
+                                cfg.flags, MX2_BUS_FLAGS);
+                       return -EINVAL;
+               }
+       } else if (ret != -ENOIOCTLCMD) {
+               return ret;
+       } else {
+               common_flags = MX2_BUS_FLAGS;
+       }
+       if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) &&
+           (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) {
+               if (pcdev->platform_flags & MX2_CAMERA_HSYNC_HIGH)
+                       common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW;
+               else
+                       common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH;
+       }
+       if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) &&
+           (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) {
+               if (pcdev->platform_flags & MX2_CAMERA_PCLK_SAMPLE_RISING)
+                       common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING;
+               else
+                       common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING;
+       }
+       cfg.flags = common_flags;
+       ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
+       if (ret < 0 && ret != -ENOIOCTLCMD) {
+               dev_dbg(icd->parent, "camera s_mbus_config(0x%lx) returned %d\n",
+                       common_flags, ret);
+               return ret;
+       }
+       csicr1 = (csicr1 & ~CSICR1_FMT_MASK) | pcdev->emma_prp->cfg.csicr1;
+       if (common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
+               csicr1 |= CSICR1_REDGE;
+       if (common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)
+               csicr1 |= CSICR1_SOF_POL;
+       if (common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH)
+               csicr1 |= CSICR1_HSYNC_POL;
+       if (pcdev->platform_flags & MX2_CAMERA_EXT_VSYNC)
+               csicr1 |= CSICR1_EXT_VSYNC;
+       if (pcdev->platform_flags & MX2_CAMERA_CCIR)
+               csicr1 |= CSICR1_CCIR_EN;
+       if (pcdev->platform_flags & MX2_CAMERA_CCIR_INTERLACE)
+               csicr1 |= CSICR1_CCIR_MODE;
+       if (pcdev->platform_flags & MX2_CAMERA_GATED_CLOCK)
+               csicr1 |= CSICR1_GCLK_MODE;
+       if (pcdev->platform_flags & MX2_CAMERA_INV_DATA)
+               csicr1 |= CSICR1_INV_DATA;
+       pcdev->csicr1 = csicr1;
+       bytesperline = soc_mbus_bytes_per_line(icd->user_width,
+                       icd->current_fmt->host_fmt);
+       if (bytesperline < 0)
+               return bytesperline;
+       if (cpu_is_mx27()) {
+               ret = mx27_camera_emma_prp_reset(pcdev);
+               if (ret)
+                       return ret;
+       } else if (cpu_is_mx25()) {
+               writel((bytesperline * icd->user_height) >> 2,
+                               pcdev->base_csi + CSIRXCNT);
+               writel((bytesperline << 16) | icd->user_height,
+                               pcdev->base_csi + CSIIMAG_PARA);
+       }
+       writel(pcdev->csicr1, pcdev->base_csi + CSICR1);
+       return 0;
+ }
+ static int mx2_camera_set_crop(struct soc_camera_device *icd,
+                               struct v4l2_crop *a)
+ {
+       struct v4l2_rect *rect = &a->c;
+       struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+       struct v4l2_mbus_framefmt mf;
+       int ret;
+       soc_camera_limit_side(&rect->left, &rect->width, 0, 2, 4096);
+       soc_camera_limit_side(&rect->top, &rect->height, 0, 2, 4096);
+       ret = v4l2_subdev_call(sd, video, s_crop, a);
+       if (ret < 0)
+               return ret;
+       /* The capture device might have changed its output  */
+       ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mf);
+       if (ret < 0)
+               return ret;
+       dev_dbg(icd->parent, "Sensor cropped %dx%d\n",
+               mf.width, mf.height);
+       icd->user_width         = mf.width;
+       icd->user_height        = mf.height;
+       return ret;
+ }
+ static int mx2_camera_get_formats(struct soc_camera_device *icd,
+                                 unsigned int idx,
+                                 struct soc_camera_format_xlate *xlate)
+ {
+       struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+       const struct soc_mbus_pixelfmt *fmt;
+       struct device *dev = icd->parent;
+       enum v4l2_mbus_pixelcode code;
+       int ret, formats = 0;
+       ret = v4l2_subdev_call(sd, video, enum_mbus_fmt, idx, &code);
+       if (ret < 0)
+               /* no more formats */
+               return 0;
+       fmt = soc_mbus_get_fmtdesc(code);
+       if (!fmt) {
+               dev_err(dev, "Invalid format code #%u: %d\n", idx, code);
+               return 0;
+       }
+       if (code == V4L2_MBUS_FMT_YUYV8_2X8 ||
+           code == V4L2_MBUS_FMT_UYVY8_2X8) {
+               formats++;
+               if (xlate) {
+                       /*
+                        * CH2 can output YUV420 which is a standard format in
+                        * soc_mediabus.c
+                        */
+                       xlate->host_fmt =
+                               soc_mbus_get_fmtdesc(V4L2_MBUS_FMT_YUYV8_1_5X8);
+                       xlate->code     = code;
+                       dev_dbg(dev, "Providing host format %s for sensor code %d\n",
+                              xlate->host_fmt->name, code);
+                       xlate++;
+               }
+       }
+       if (code == V4L2_MBUS_FMT_UYVY8_2X8) {
+               formats++;
+               if (xlate) {
+                       xlate->host_fmt =
+                               soc_mbus_get_fmtdesc(V4L2_MBUS_FMT_YUYV8_2X8);
+                       xlate->code     = code;
+                       dev_dbg(dev, "Providing host format %s for sensor code %d\n",
+                               xlate->host_fmt->name, code);
+                       xlate++;
+               }
+       }
+       /* Generic pass-trough */
+       formats++;
+       if (xlate) {
+               xlate->host_fmt = fmt;
+               xlate->code     = code;
+               xlate++;
+       }
+       return formats;
+ }
+ static int mx2_emmaprp_resize(struct mx2_camera_dev *pcdev,
+                             struct v4l2_mbus_framefmt *mf_in,
+                             struct v4l2_pix_format *pix_out, bool apply)
+ {
+       int num, den;
+       unsigned long m;
+       int i, dir;
+       for (dir = RESIZE_DIR_H; dir <= RESIZE_DIR_V; dir++) {
+               struct emma_prp_resize tmprsz;
+               unsigned char *s = tmprsz.s;
+               int len = 0;
+               int in, out;
+               if (dir == RESIZE_DIR_H) {
+                       in = mf_in->width;
+                       out = pix_out->width;
+               } else {
+                       in = mf_in->height;
+                       out = pix_out->height;
+               }
+               if (in < out)
+                       return -EINVAL;
+               else if (in == out)
+                       continue;
+               /* Calculate ratio */
+               m = gcd(in, out);
+               num = in / m;
+               den = out / m;
+               if (num > RESIZE_NUM_MAX)
+                       return -EINVAL;
+               if ((num >= 2 * den) && (den == 1) &&
+                   (num < 9) && (!(num & 0x01))) {
+                       int sum = 0;
+                       int j;
+                       /* Average scaling for >= 2:1 ratios */
+                       /* Support can be added for num >=9 and odd values */
+                       tmprsz.algo = RESIZE_ALGO_AVERAGING;
+                       len = num;
+                       for (i = 0; i < (len / 2); i++)
+                               s[i] = 8;
+                       do {
+                               for (i = 0; i < (len / 2); i++) {
+                                       s[i] = s[i] >> 1;
+                                       sum = 0;
+                                       for (j = 0; j < (len / 2); j++)
+                                               sum += s[j];
+                                       if (sum == 4)
+                                               break;
+                               }
+                       } while (sum != 4);
+                       for (i = (len / 2); i < len; i++)
+                               s[i] = s[len - i - 1];
+                       s[len - 1] |= SZ_COEF;
+               } else {
+                       /* bilinear scaling for < 2:1 ratios */
+                       int v; /* overflow counter */
+                       int coeff, nxt; /* table output */
+                       int in_pos_inc = 2 * den;
+                       int out_pos = num;
+                       int out_pos_inc = 2 * num;
+                       int init_carry = num - den;
+                       int carry = init_carry;
+                       tmprsz.algo = RESIZE_ALGO_BILINEAR;
+                       v = den + in_pos_inc;
+                       do {
+                               coeff = v - out_pos;
+                               out_pos += out_pos_inc;
+                               carry += out_pos_inc;
+                               for (nxt = 0; v < out_pos; nxt++) {
+                                       v += in_pos_inc;
+                                       carry -= in_pos_inc;
+                               }
+                               if (len > RESIZE_NUM_MAX)
+                                       return -EINVAL;
+                               coeff = ((coeff << BC_COEF) +
+                                       (in_pos_inc >> 1)) / in_pos_inc;
+                               if (coeff >= (SZ_COEF - 1))
+                                       coeff--;
+                               coeff |= SZ_COEF;
+                               s[len] = (unsigned char)coeff;
+                               len++;
+                               for (i = 1; i < nxt; i++) {
+                                       if (len >= RESIZE_NUM_MAX)
+                                               return -EINVAL;
+                                       s[len] = 0;
+                                       len++;
+                               }
+                       } while (carry != init_carry);
+               }
+               tmprsz.len = len;
+               if (dir == RESIZE_DIR_H)
+                       mf_in->width = pix_out->width;
+               else
+                       mf_in->height = pix_out->height;
+               if (apply)
+                       memcpy(&pcdev->resizing[dir], &tmprsz, sizeof(tmprsz));
+       }
+       return 0;
+ }
+ static int mx2_camera_set_fmt(struct soc_camera_device *icd,
+                              struct v4l2_format *f)
+ {
+       struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+       struct mx2_camera_dev *pcdev = ici->priv;
+       struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+       const struct soc_camera_format_xlate *xlate;
+       struct v4l2_pix_format *pix = &f->fmt.pix;
+       struct v4l2_mbus_framefmt mf;
+       int ret;
+       dev_dbg(icd->parent, "%s: requested params: width = %d, height = %d\n",
+               __func__, pix->width, pix->height);
+       xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
+       if (!xlate) {
+               dev_warn(icd->parent, "Format %x not found\n",
+                               pix->pixelformat);
+               return -EINVAL;
+       }
+       mf.width        = pix->width;
+       mf.height       = pix->height;
+       mf.field        = pix->field;
+       mf.colorspace   = pix->colorspace;
+       mf.code         = xlate->code;
+       ret = v4l2_subdev_call(sd, video, s_mbus_fmt, &mf);
+       if (ret < 0 && ret != -ENOIOCTLCMD)
+               return ret;
+       /* Store width and height returned by the sensor for resizing */
+       pcdev->s_width = mf.width;
+       pcdev->s_height = mf.height;
+       dev_dbg(icd->parent, "%s: sensor params: width = %d, height = %d\n",
+               __func__, pcdev->s_width, pcdev->s_height);
+       pcdev->emma_prp = mx27_emma_prp_get_format(xlate->code,
+                                                  xlate->host_fmt->fourcc);
+       memset(pcdev->resizing, 0, sizeof(pcdev->resizing));
+       if ((mf.width != pix->width || mf.height != pix->height) &&
+               pcdev->emma_prp->cfg.in_fmt == PRP_CNTL_DATA_IN_YUV422) {
+               if (mx2_emmaprp_resize(pcdev, &mf, pix, true) < 0)
+                       dev_dbg(icd->parent, "%s: can't resize\n", __func__);
+       }
+       if (mf.code != xlate->code)
+               return -EINVAL;
+       pix->width              = mf.width;
+       pix->height             = mf.height;
+       pix->field              = mf.field;
+       pix->colorspace         = mf.colorspace;
+       icd->current_fmt        = xlate;
+       dev_dbg(icd->parent, "%s: returned params: width = %d, height = %d\n",
+               __func__, pix->width, pix->height);
+       return 0;
+ }
+ static int mx2_camera_try_fmt(struct soc_camera_device *icd,
+                                 struct v4l2_format *f)
+ {
+       struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+       const struct soc_camera_format_xlate *xlate;
+       struct v4l2_pix_format *pix = &f->fmt.pix;
+       struct v4l2_mbus_framefmt mf;
+       __u32 pixfmt = pix->pixelformat;
+       struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+       struct mx2_camera_dev *pcdev = ici->priv;
+       unsigned int width_limit;
+       int ret;
+       dev_dbg(icd->parent, "%s: requested params: width = %d, height = %d\n",
+               __func__, pix->width, pix->height);
+       xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
+       if (pixfmt && !xlate) {
+               dev_warn(icd->parent, "Format %x not found\n", pixfmt);
+               return -EINVAL;
+       }
+       /* FIXME: implement MX27 limits */
+       /* limit to MX25 hardware capabilities */
+       if (cpu_is_mx25()) {
+               if (xlate->host_fmt->bits_per_sample <= 8)
+                       width_limit = 0xffff * 4;
+               else
+                       width_limit = 0xffff * 2;
+               /* CSIIMAG_PARA limit */
+               if (pix->width > width_limit)
+                       pix->width = width_limit;
+               if (pix->height > 0xffff)
+                       pix->height = 0xffff;
+               pix->bytesperline = soc_mbus_bytes_per_line(pix->width,
+                               xlate->host_fmt);
+               if (pix->bytesperline < 0)
+                       return pix->bytesperline;
+               pix->sizeimage = soc_mbus_image_size(xlate->host_fmt,
+                                               pix->bytesperline, pix->height);
+               /* Check against the CSIRXCNT limit */
+               if (pix->sizeimage > 4 * 0x3ffff) {
+                       /* Adjust geometry, preserve aspect ratio */
+                       unsigned int new_height = int_sqrt(div_u64(0x3ffffULL *
+                                       4 * pix->height, pix->bytesperline));
+                       pix->width = new_height * pix->width / pix->height;
+                       pix->height = new_height;
+                       pix->bytesperline = soc_mbus_bytes_per_line(pix->width,
+                                                       xlate->host_fmt);
+                       BUG_ON(pix->bytesperline < 0);
+                       pix->sizeimage = soc_mbus_image_size(xlate->host_fmt,
+                                               pix->bytesperline, pix->height);
+               }
+       }
+       /* limit to sensor capabilities */
+       mf.width        = pix->width;
+       mf.height       = pix->height;
+       mf.field        = pix->field;
+       mf.colorspace   = pix->colorspace;
+       mf.code         = xlate->code;
+       ret = v4l2_subdev_call(sd, video, try_mbus_fmt, &mf);
+       if (ret < 0)
+               return ret;
+       dev_dbg(icd->parent, "%s: sensor params: width = %d, height = %d\n",
+               __func__, pcdev->s_width, pcdev->s_height);
+       /* If the sensor does not support image size try PrP resizing */
+       pcdev->emma_prp = mx27_emma_prp_get_format(xlate->code,
+                                                  xlate->host_fmt->fourcc);
+       memset(pcdev->resizing, 0, sizeof(pcdev->resizing));
+       if ((mf.width != pix->width || mf.height != pix->height) &&
+               pcdev->emma_prp->cfg.in_fmt == PRP_CNTL_DATA_IN_YUV422) {
+               if (mx2_emmaprp_resize(pcdev, &mf, pix, false) < 0)
+                       dev_dbg(icd->parent, "%s: can't resize\n", __func__);
+       }
+       if (mf.field == V4L2_FIELD_ANY)
+               mf.field = V4L2_FIELD_NONE;
+       /*
+        * Driver supports interlaced images provided they have
+        * both fields so that they can be processed as if they
+        * were progressive.
+        */
+       if (mf.field != V4L2_FIELD_NONE && !V4L2_FIELD_HAS_BOTH(mf.field)) {
+               dev_err(icd->parent, "Field type %d unsupported.\n",
+                               mf.field);
+               return -EINVAL;
+       }
+       pix->width      = mf.width;
+       pix->height     = mf.height;
+       pix->field      = mf.field;
+       pix->colorspace = mf.colorspace;
+       dev_dbg(icd->parent, "%s: returned params: width = %d, height = %d\n",
+               __func__, pix->width, pix->height);
+       return 0;
+ }
+ static int mx2_camera_querycap(struct soc_camera_host *ici,
+                              struct v4l2_capability *cap)
+ {
+       /* cap->name is set by the friendly caller:-> */
+       strlcpy(cap->card, MX2_CAM_DRIVER_DESCRIPTION, sizeof(cap->card));
+       cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+       return 0;
+ }
+ static unsigned int mx2_camera_poll(struct file *file, poll_table *pt)
+ {
+       struct soc_camera_device *icd = file->private_data;
+       return vb2_poll(&icd->vb2_vidq, file, pt);
+ }
+ static struct soc_camera_host_ops mx2_soc_camera_host_ops = {
+       .owner          = THIS_MODULE,
+       .add            = mx2_camera_add_device,
+       .remove         = mx2_camera_remove_device,
+       .set_fmt        = mx2_camera_set_fmt,
+       .set_crop       = mx2_camera_set_crop,
+       .get_formats    = mx2_camera_get_formats,
+       .try_fmt        = mx2_camera_try_fmt,
+       .init_videobuf2 = mx2_camera_init_videobuf,
+       .poll           = mx2_camera_poll,
+       .querycap       = mx2_camera_querycap,
+       .set_bus_param  = mx2_camera_set_bus_param,
+ };
+ static void mx27_camera_frame_done_emma(struct mx2_camera_dev *pcdev,
+               int bufnum, bool err)
+ {
+ #ifdef DEBUG
+       struct mx2_fmt_cfg *prp = pcdev->emma_prp;
+ #endif
+       struct mx2_buf_internal *ibuf;
+       struct mx2_buffer *buf;
+       struct vb2_buffer *vb;
+       unsigned long phys;
+       ibuf = list_first_entry(&pcdev->active_bufs, struct mx2_buf_internal,
+                              queue);
+       BUG_ON(ibuf->bufnum != bufnum);
+       if (ibuf->discard) {
+               /*
+                * Discard buffer must not be returned to user space.
+                * Just return it to the discard queue.
+                */
+               list_move_tail(pcdev->active_bufs.next, &pcdev->discard);
+       } else {
+               buf = mx2_ibuf_to_buf(ibuf);
+               vb = &buf->vb;
+ #ifdef DEBUG
+               phys = vb2_dma_contig_plane_dma_addr(vb, 0);
+               if (prp->cfg.channel == 1) {
+                       if (readl(pcdev->base_emma + PRP_DEST_RGB1_PTR +
+                               4 * bufnum) != phys) {
+                               dev_err(pcdev->dev, "%lx != %x\n", phys,
+                                       readl(pcdev->base_emma +
+                                       PRP_DEST_RGB1_PTR + 4 * bufnum));
+                       }
+               } else {
+                       if (readl(pcdev->base_emma + PRP_DEST_Y_PTR -
+                               0x14 * bufnum) != phys) {
+                               dev_err(pcdev->dev, "%lx != %x\n", phys,
+                                       readl(pcdev->base_emma +
+                                       PRP_DEST_Y_PTR - 0x14 * bufnum));
+                       }
+               }
+ #endif
+               dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%p %lu\n", __func__, vb,
+                               vb2_plane_vaddr(vb, 0),
+                               vb2_get_plane_payload(vb, 0));
+               list_del_init(&buf->internal.queue);
+               do_gettimeofday(&vb->v4l2_buf.timestamp);
+               vb->v4l2_buf.sequence = pcdev->frame_count;
+               if (err)
+                       vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
+               else
+                       vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+       }
+       pcdev->frame_count++;
+       if (list_empty(&pcdev->capture)) {
+               if (list_empty(&pcdev->discard)) {
+                       dev_warn(pcdev->dev, "%s: trying to access empty discard list\n",
+                                __func__);
+                       return;
+               }
+               ibuf = list_first_entry(&pcdev->discard,
+                                       struct mx2_buf_internal, queue);
+               ibuf->bufnum = bufnum;
+               list_move_tail(pcdev->discard.next, &pcdev->active_bufs);
+               mx27_update_emma_buf(pcdev, pcdev->discard_buffer_dma, bufnum);
+               return;
+       }
+       buf = list_first_entry(&pcdev->capture, struct mx2_buffer,
+                              internal.queue);
+       buf->internal.bufnum = bufnum;
+       list_move_tail(pcdev->capture.next, &pcdev->active_bufs);
+       vb = &buf->vb;
+       buf->state = MX2_STATE_ACTIVE;
+       phys = vb2_dma_contig_plane_dma_addr(vb, 0);
+       mx27_update_emma_buf(pcdev, phys, bufnum);
+ }
+ static irqreturn_t mx27_camera_emma_irq(int irq_emma, void *data)
+ {
+       struct mx2_camera_dev *pcdev = data;
+       unsigned int status = readl(pcdev->base_emma + PRP_INTRSTATUS);
+       struct mx2_buf_internal *ibuf;
+       spin_lock(&pcdev->lock);
+       if (list_empty(&pcdev->active_bufs)) {
+               dev_warn(pcdev->dev, "%s: called while active list is empty\n",
+                       __func__);
+               if (!status) {
+                       spin_unlock(&pcdev->lock);
+                       return IRQ_NONE;
+               }
+       }
+       if (status & (1 << 7)) { /* overflow */
+               u32 cntl = readl(pcdev->base_emma + PRP_CNTL);
+               writel(cntl & ~(PRP_CNTL_CH1EN | PRP_CNTL_CH2EN),
+                      pcdev->base_emma + PRP_CNTL);
+               writel(cntl, pcdev->base_emma + PRP_CNTL);
+               ibuf = list_first_entry(&pcdev->active_bufs,
+                                       struct mx2_buf_internal, queue);
+               mx27_camera_frame_done_emma(pcdev,
+                                       ibuf->bufnum, true);
+               status &= ~(1 << 7);
+       } else if (((status & (3 << 5)) == (3 << 5)) ||
+               ((status & (3 << 3)) == (3 << 3))) {
+               /*
+                * Both buffers have triggered, process the one we're expecting
+                * to first
+                */
+               ibuf = list_first_entry(&pcdev->active_bufs,
+                                       struct mx2_buf_internal, queue);
+               mx27_camera_frame_done_emma(pcdev, ibuf->bufnum, false);
+               status &= ~(1 << (6 - ibuf->bufnum)); /* mark processed */
+       } else if ((status & (1 << 6)) || (status & (1 << 4))) {
+               mx27_camera_frame_done_emma(pcdev, 0, false);
+       } else if ((status & (1 << 5)) || (status & (1 << 3))) {
+               mx27_camera_frame_done_emma(pcdev, 1, false);
+       }
+       spin_unlock(&pcdev->lock);
+       writel(status, pcdev->base_emma + PRP_INTRSTATUS);
+       return IRQ_HANDLED;
+ }
+ static int __devinit mx27_camera_emma_init(struct platform_device *pdev)
+ {
+       struct mx2_camera_dev *pcdev = platform_get_drvdata(pdev);
+       struct resource *res_emma;
+       int irq_emma;
+       int err = 0;
+       res_emma = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+       irq_emma = platform_get_irq(pdev, 1);
+       if (!res_emma || !irq_emma) {
+               dev_err(pcdev->dev, "no EMMA resources\n");
+               goto out;
+       }
+       pcdev->base_emma = devm_request_and_ioremap(pcdev->dev, res_emma);
+       if (!pcdev->base_emma) {
+               err = -EADDRNOTAVAIL;
+               goto out;
+       }
+       err = devm_request_irq(pcdev->dev, irq_emma, mx27_camera_emma_irq, 0,
+                              MX2_CAM_DRV_NAME, pcdev);
+       if (err) {
+               dev_err(pcdev->dev, "Camera EMMA interrupt register failed \n");
+               goto out;
+       }
+       pcdev->clk_emma_ipg = devm_clk_get(pcdev->dev, "emma-ipg");
+       if (IS_ERR(pcdev->clk_emma_ipg)) {
+               err = PTR_ERR(pcdev->clk_emma_ipg);
+               goto out;
+       }
+       clk_prepare_enable(pcdev->clk_emma_ipg);
+       pcdev->clk_emma_ahb = devm_clk_get(pcdev->dev, "emma-ahb");
+       if (IS_ERR(pcdev->clk_emma_ahb)) {
+               err = PTR_ERR(pcdev->clk_emma_ahb);
+               goto exit_clk_emma_ipg;
+       }
+       clk_prepare_enable(pcdev->clk_emma_ahb);
+       err = mx27_camera_emma_prp_reset(pcdev);
+       if (err)
+               goto exit_clk_emma_ahb;
+       return err;
+ exit_clk_emma_ahb:
+       clk_disable_unprepare(pcdev->clk_emma_ahb);
+ exit_clk_emma_ipg:
+       clk_disable_unprepare(pcdev->clk_emma_ipg);
+ out:
+       return err;
+ }
+ static int __devinit mx2_camera_probe(struct platform_device *pdev)
+ {
+       struct mx2_camera_dev *pcdev;
+       struct resource *res_csi;
+       int irq_csi;
+       int err = 0;
+       dev_dbg(&pdev->dev, "initialising\n");
+       res_csi = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       irq_csi = platform_get_irq(pdev, 0);
+       if (res_csi == NULL || irq_csi < 0) {
+               dev_err(&pdev->dev, "Missing platform resources data\n");
+               err = -ENODEV;
+               goto exit;
+       }
+       pcdev = devm_kzalloc(&pdev->dev, sizeof(*pcdev), GFP_KERNEL);
+       if (!pcdev) {
+               dev_err(&pdev->dev, "Could not allocate pcdev\n");
+               err = -ENOMEM;
+               goto exit;
+       }
+       pcdev->clk_csi = devm_clk_get(&pdev->dev, "ahb");
+       if (IS_ERR(pcdev->clk_csi)) {
+               dev_err(&pdev->dev, "Could not get csi clock\n");
+               err = PTR_ERR(pcdev->clk_csi);
+               goto exit;
+       }
+       pcdev->pdata = pdev->dev.platform_data;
+       if (pcdev->pdata) {
+               long rate;
+               pcdev->platform_flags = pcdev->pdata->flags;
+               rate = clk_round_rate(pcdev->clk_csi, pcdev->pdata->clk * 2);
+               if (rate <= 0) {
+                       err = -ENODEV;
+                       goto exit;
+               }
+               err = clk_set_rate(pcdev->clk_csi, rate);
+               if (err < 0)
+                       goto exit;
+       }
+       INIT_LIST_HEAD(&pcdev->capture);
+       INIT_LIST_HEAD(&pcdev->active_bufs);
+       INIT_LIST_HEAD(&pcdev->discard);
+       spin_lock_init(&pcdev->lock);
+       pcdev->base_csi = devm_request_and_ioremap(&pdev->dev, res_csi);
+       if (!pcdev->base_csi) {
+               err = -EADDRNOTAVAIL;
+               goto exit;
+       }
+       pcdev->dev = &pdev->dev;
+       platform_set_drvdata(pdev, pcdev);
+       if (cpu_is_mx25()) {
+               err = devm_request_irq(&pdev->dev, irq_csi, mx25_camera_irq, 0,
+                                      MX2_CAM_DRV_NAME, pcdev);
+               if (err) {
+                       dev_err(pcdev->dev, "Camera interrupt register failed \n");
+                       goto exit;
+               }
+       }
+       if (cpu_is_mx27()) {
+               err = mx27_camera_emma_init(pdev);
+               if (err)
+                       goto exit;
+       }
+       /*
+        * We're done with drvdata here.  Clear the pointer so that
+        * v4l2 core can start using drvdata on its purpose.
+        */
+       platform_set_drvdata(pdev, NULL);
+       pcdev->soc_host.drv_name        = MX2_CAM_DRV_NAME,
+       pcdev->soc_host.ops             = &mx2_soc_camera_host_ops,
+       pcdev->soc_host.priv            = pcdev;
+       pcdev->soc_host.v4l2_dev.dev    = &pdev->dev;
+       pcdev->soc_host.nr              = pdev->id;
+       if (cpu_is_mx25())
+               pcdev->soc_host.capabilities = SOCAM_HOST_CAP_STRIDE;
+       pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
+       if (IS_ERR(pcdev->alloc_ctx)) {
+               err = PTR_ERR(pcdev->alloc_ctx);
+               goto eallocctx;
+       }
+       err = soc_camera_host_register(&pcdev->soc_host);
+       if (err)
+               goto exit_free_emma;
+       dev_info(&pdev->dev, "MX2 Camera (CSI) driver probed, clock frequency: %ld\n",
+                       clk_get_rate(pcdev->clk_csi));
+       return 0;
+ exit_free_emma:
+       vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
+ eallocctx:
+       if (cpu_is_mx27()) {
+               clk_disable_unprepare(pcdev->clk_emma_ipg);
+               clk_disable_unprepare(pcdev->clk_emma_ahb);
+       }
+ exit:
+       return err;
+ }
+ static int __devexit mx2_camera_remove(struct platform_device *pdev)
+ {
+       struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev);
+       struct mx2_camera_dev *pcdev = container_of(soc_host,
+                       struct mx2_camera_dev, soc_host);
+       soc_camera_host_unregister(&pcdev->soc_host);
+       vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
+       if (cpu_is_mx27()) {
+               clk_disable_unprepare(pcdev->clk_emma_ipg);
+               clk_disable_unprepare(pcdev->clk_emma_ahb);
+       }
+       dev_info(&pdev->dev, "MX2 Camera driver unloaded\n");
+       return 0;
+ }
+ static struct platform_driver mx2_camera_driver = {
+       .driver         = {
+               .name   = MX2_CAM_DRV_NAME,
+       },
+       .remove         = __devexit_p(mx2_camera_remove),
+ };
+ static int __init mx2_camera_init(void)
+ {
+       return platform_driver_probe(&mx2_camera_driver, &mx2_camera_probe);
+ }
+ static void __exit mx2_camera_exit(void)
+ {
+       return platform_driver_unregister(&mx2_camera_driver);
+ }
+ module_init(mx2_camera_init);
+ module_exit(mx2_camera_exit);
+ MODULE_DESCRIPTION("i.MX27/i.MX25 SoC Camera Host driver");
+ MODULE_AUTHOR("Sascha Hauer <sha@pengutronix.de>");
+ MODULE_LICENSE("GPL");
+ MODULE_VERSION(MX2_CAM_VERSION);
index 0000000,16975c6..3557ac9
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,1290 +1,1290 @@@
 -#include <mach/mx3_camera.h>
 -#include <mach/dma.h>
+ /*
+  * V4L2 Driver for i.MX3x camera host
+  *
+  * Copyright (C) 2008
+  * Guennadi Liakhovetski, DENX Software Engineering, <lg@denx.de>
+  *
+  * This program is free software; you can redistribute it and/or modify
+  * it under the terms of the GNU General Public License version 2 as
+  * published by the Free Software Foundation.
+  */
+ #include <linux/init.h>
+ #include <linux/module.h>
+ #include <linux/videodev2.h>
+ #include <linux/platform_device.h>
+ #include <linux/clk.h>
+ #include <linux/vmalloc.h>
+ #include <linux/interrupt.h>
+ #include <linux/sched.h>
+ #include <media/v4l2-common.h>
+ #include <media/v4l2-dev.h>
+ #include <media/videobuf2-dma-contig.h>
+ #include <media/soc_camera.h>
+ #include <media/soc_mediabus.h>
+ #include <mach/ipu.h>
++#include <linux/platform_data/camera-mx3.h>
++#include <linux/platform_data/dma-imx.h>
+ #define MX3_CAM_DRV_NAME "mx3-camera"
+ /* CMOS Sensor Interface Registers */
+ #define CSI_REG_START         0x60
+ #define CSI_SENS_CONF         (0x60 - CSI_REG_START)
+ #define CSI_SENS_FRM_SIZE     (0x64 - CSI_REG_START)
+ #define CSI_ACT_FRM_SIZE      (0x68 - CSI_REG_START)
+ #define CSI_OUT_FRM_CTRL      (0x6C - CSI_REG_START)
+ #define CSI_TST_CTRL          (0x70 - CSI_REG_START)
+ #define CSI_CCIR_CODE_1               (0x74 - CSI_REG_START)
+ #define CSI_CCIR_CODE_2               (0x78 - CSI_REG_START)
+ #define CSI_CCIR_CODE_3               (0x7C - CSI_REG_START)
+ #define CSI_FLASH_STROBE_1    (0x80 - CSI_REG_START)
+ #define CSI_FLASH_STROBE_2    (0x84 - CSI_REG_START)
+ #define CSI_SENS_CONF_VSYNC_POL_SHIFT         0
+ #define CSI_SENS_CONF_HSYNC_POL_SHIFT         1
+ #define CSI_SENS_CONF_DATA_POL_SHIFT          2
+ #define CSI_SENS_CONF_PIX_CLK_POL_SHIFT               3
+ #define CSI_SENS_CONF_SENS_PRTCL_SHIFT                4
+ #define CSI_SENS_CONF_SENS_CLKSRC_SHIFT               7
+ #define CSI_SENS_CONF_DATA_FMT_SHIFT          8
+ #define CSI_SENS_CONF_DATA_WIDTH_SHIFT                10
+ #define CSI_SENS_CONF_EXT_VSYNC_SHIFT         15
+ #define CSI_SENS_CONF_DIVRATIO_SHIFT          16
+ #define CSI_SENS_CONF_DATA_FMT_RGB_YUV444     (0UL << CSI_SENS_CONF_DATA_FMT_SHIFT)
+ #define CSI_SENS_CONF_DATA_FMT_YUV422         (2UL << CSI_SENS_CONF_DATA_FMT_SHIFT)
+ #define CSI_SENS_CONF_DATA_FMT_BAYER          (3UL << CSI_SENS_CONF_DATA_FMT_SHIFT)
+ #define MAX_VIDEO_MEM 16
+ struct mx3_camera_buffer {
+       /* common v4l buffer stuff -- must be first */
+       struct vb2_buffer                       vb;
+       struct list_head                        queue;
+       /* One descriptot per scatterlist (per frame) */
+       struct dma_async_tx_descriptor          *txd;
+       /* We have to "build" a scatterlist ourselves - one element per frame */
+       struct scatterlist                      sg;
+ };
+ /**
+  * struct mx3_camera_dev - i.MX3x camera (CSI) object
+  * @dev:              camera device, to which the coherent buffer is attached
+  * @icd:              currently attached camera sensor
+  * @clk:              pointer to clock
+  * @base:             remapped register base address
+  * @pdata:            platform data
+  * @platform_flags:   platform flags
+  * @mclk:             master clock frequency in Hz
+  * @capture:          list of capture videobuffers
+  * @lock:             protects video buffer lists
+  * @active:           active video buffer
+  * @idmac_channel:    array of pointers to IPU DMAC DMA channels
+  * @soc_host:         embedded soc_host object
+  */
+ struct mx3_camera_dev {
+       /*
+        * i.MX3x is only supposed to handle one camera on its Camera Sensor
+        * Interface. If anyone ever builds hardware to enable more than one
+        * camera _simultaneously_, they will have to modify this driver too
+        */
+       struct soc_camera_device *icd;
+       struct clk              *clk;
+       void __iomem            *base;
+       struct mx3_camera_pdata *pdata;
+       unsigned long           platform_flags;
+       unsigned long           mclk;
+       u16                     width_flags;    /* max 15 bits */
+       struct list_head        capture;
+       spinlock_t              lock;           /* Protects video buffer lists */
+       struct mx3_camera_buffer *active;
+       size_t                  buf_total;
+       struct vb2_alloc_ctx    *alloc_ctx;
+       enum v4l2_field         field;
+       int                     sequence;
+       /* IDMAC / dmaengine interface */
+       struct idmac_channel    *idmac_channel[1];      /* We need one channel */
+       struct soc_camera_host  soc_host;
+ };
+ struct dma_chan_request {
+       struct mx3_camera_dev   *mx3_cam;
+       enum ipu_channel        id;
+ };
+ static u32 csi_reg_read(struct mx3_camera_dev *mx3, off_t reg)
+ {
+       return __raw_readl(mx3->base + reg);
+ }
+ static void csi_reg_write(struct mx3_camera_dev *mx3, u32 value, off_t reg)
+ {
+       __raw_writel(value, mx3->base + reg);
+ }
+ static struct mx3_camera_buffer *to_mx3_vb(struct vb2_buffer *vb)
+ {
+       return container_of(vb, struct mx3_camera_buffer, vb);
+ }
+ /* Called from the IPU IDMAC ISR */
+ static void mx3_cam_dma_done(void *arg)
+ {
+       struct idmac_tx_desc *desc = to_tx_desc(arg);
+       struct dma_chan *chan = desc->txd.chan;
+       struct idmac_channel *ichannel = to_idmac_chan(chan);
+       struct mx3_camera_dev *mx3_cam = ichannel->client;
+       dev_dbg(chan->device->dev, "callback cookie %d, active DMA 0x%08x\n",
+               desc->txd.cookie, mx3_cam->active ? sg_dma_address(&mx3_cam->active->sg) : 0);
+       spin_lock(&mx3_cam->lock);
+       if (mx3_cam->active) {
+               struct vb2_buffer *vb = &mx3_cam->active->vb;
+               struct mx3_camera_buffer *buf = to_mx3_vb(vb);
+               list_del_init(&buf->queue);
+               do_gettimeofday(&vb->v4l2_buf.timestamp);
+               vb->v4l2_buf.field = mx3_cam->field;
+               vb->v4l2_buf.sequence = mx3_cam->sequence++;
+               vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+       }
+       if (list_empty(&mx3_cam->capture)) {
+               mx3_cam->active = NULL;
+               spin_unlock(&mx3_cam->lock);
+               /*
+                * stop capture - without further buffers IPU_CHA_BUF0_RDY will
+                * not get updated
+                */
+               return;
+       }
+       mx3_cam->active = list_entry(mx3_cam->capture.next,
+                                    struct mx3_camera_buffer, queue);
+       spin_unlock(&mx3_cam->lock);
+ }
+ /*
+  * Videobuf operations
+  */
+ /*
+  * Calculate the __buffer__ (not data) size and number of buffers.
+  */
+ static int mx3_videobuf_setup(struct vb2_queue *vq,
+                       const struct v4l2_format *fmt,
+                       unsigned int *count, unsigned int *num_planes,
+                       unsigned int sizes[], void *alloc_ctxs[])
+ {
+       struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
+       struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+       struct mx3_camera_dev *mx3_cam = ici->priv;
+       if (!mx3_cam->idmac_channel[0])
+               return -EINVAL;
+       if (fmt) {
+               const struct soc_camera_format_xlate *xlate = soc_camera_xlate_by_fourcc(icd,
+                                                               fmt->fmt.pix.pixelformat);
+               unsigned int bytes_per_line;
+               int ret;
+               if (!xlate)
+                       return -EINVAL;
+               ret = soc_mbus_bytes_per_line(fmt->fmt.pix.width,
+                                             xlate->host_fmt);
+               if (ret < 0)
+                       return ret;
+               bytes_per_line = max_t(u32, fmt->fmt.pix.bytesperline, ret);
+               ret = soc_mbus_image_size(xlate->host_fmt, bytes_per_line,
+                                         fmt->fmt.pix.height);
+               if (ret < 0)
+                       return ret;
+               sizes[0] = max_t(u32, fmt->fmt.pix.sizeimage, ret);
+       } else {
+               /* Called from VIDIOC_REQBUFS or in compatibility mode */
+               sizes[0] = icd->sizeimage;
+       }
+       alloc_ctxs[0] = mx3_cam->alloc_ctx;
+       if (!vq->num_buffers)
+               mx3_cam->sequence = 0;
+       if (!*count)
+               *count = 2;
+       /* If *num_planes != 0, we have already verified *count. */
+       if (!*num_planes &&
+           sizes[0] * *count + mx3_cam->buf_total > MAX_VIDEO_MEM * 1024 * 1024)
+               *count = (MAX_VIDEO_MEM * 1024 * 1024 - mx3_cam->buf_total) /
+                       sizes[0];
+       *num_planes = 1;
+       return 0;
+ }
+ static enum pixel_fmt fourcc_to_ipu_pix(__u32 fourcc)
+ {
+       /* Add more formats as need arises and test possibilities appear... */
+       switch (fourcc) {
+       case V4L2_PIX_FMT_RGB24:
+               return IPU_PIX_FMT_RGB24;
+       case V4L2_PIX_FMT_UYVY:
+       case V4L2_PIX_FMT_RGB565:
+       default:
+               return IPU_PIX_FMT_GENERIC;
+       }
+ }
+ static void mx3_videobuf_queue(struct vb2_buffer *vb)
+ {
+       struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
+       struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+       struct mx3_camera_dev *mx3_cam = ici->priv;
+       struct mx3_camera_buffer *buf = to_mx3_vb(vb);
+       struct scatterlist *sg = &buf->sg;
+       struct dma_async_tx_descriptor *txd;
+       struct idmac_channel *ichan = mx3_cam->idmac_channel[0];
+       struct idmac_video_param *video = &ichan->params.video;
+       const struct soc_mbus_pixelfmt *host_fmt = icd->current_fmt->host_fmt;
+       unsigned long flags;
+       dma_cookie_t cookie;
+       size_t new_size;
+       new_size = icd->sizeimage;
+       if (vb2_plane_size(vb, 0) < new_size) {
+               dev_err(icd->parent, "Buffer #%d too small (%lu < %zu)\n",
+                       vb->v4l2_buf.index, vb2_plane_size(vb, 0), new_size);
+               goto error;
+       }
+       if (!buf->txd) {
+               sg_dma_address(sg)      = vb2_dma_contig_plane_dma_addr(vb, 0);
+               sg_dma_len(sg)          = new_size;
+               txd = dmaengine_prep_slave_sg(
+                       &ichan->dma_chan, sg, 1, DMA_DEV_TO_MEM,
+                       DMA_PREP_INTERRUPT);
+               if (!txd)
+                       goto error;
+               txd->callback_param     = txd;
+               txd->callback           = mx3_cam_dma_done;
+               buf->txd                = txd;
+       } else {
+               txd = buf->txd;
+       }
+       vb2_set_plane_payload(vb, 0, new_size);
+       /* This is the configuration of one sg-element */
+       video->out_pixel_fmt = fourcc_to_ipu_pix(host_fmt->fourcc);
+       if (video->out_pixel_fmt == IPU_PIX_FMT_GENERIC) {
+               /*
+                * If the IPU DMA channel is configured to transfer generic
+                * 8-bit data, we have to set up the geometry parameters
+                * correctly, according to the current pixel format. The DMA
+                * horizontal parameters in this case are expressed in bytes,
+                * not in pixels.
+                */
+               video->out_width        = icd->bytesperline;
+               video->out_height       = icd->user_height;
+               video->out_stride       = icd->bytesperline;
+       } else {
+               /*
+                * For IPU known formats the pixel unit will be managed
+                * successfully by the IPU code
+                */
+               video->out_width        = icd->user_width;
+               video->out_height       = icd->user_height;
+               video->out_stride       = icd->user_width;
+       }
+ #ifdef DEBUG
+       /* helps to see what DMA actually has written */
+       if (vb2_plane_vaddr(vb, 0))
+               memset(vb2_plane_vaddr(vb, 0), 0xaa, vb2_get_plane_payload(vb, 0));
+ #endif
+       spin_lock_irqsave(&mx3_cam->lock, flags);
+       list_add_tail(&buf->queue, &mx3_cam->capture);
+       if (!mx3_cam->active)
+               mx3_cam->active = buf;
+       spin_unlock_irq(&mx3_cam->lock);
+       cookie = txd->tx_submit(txd);
+       dev_dbg(icd->parent, "Submitted cookie %d DMA 0x%08x\n",
+               cookie, sg_dma_address(&buf->sg));
+       if (cookie >= 0)
+               return;
+       spin_lock_irq(&mx3_cam->lock);
+       /* Submit error */
+       list_del_init(&buf->queue);
+       if (mx3_cam->active == buf)
+               mx3_cam->active = NULL;
+       spin_unlock_irqrestore(&mx3_cam->lock, flags);
+ error:
+       vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
+ }
+ static void mx3_videobuf_release(struct vb2_buffer *vb)
+ {
+       struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
+       struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+       struct mx3_camera_dev *mx3_cam = ici->priv;
+       struct mx3_camera_buffer *buf = to_mx3_vb(vb);
+       struct dma_async_tx_descriptor *txd = buf->txd;
+       unsigned long flags;
+       dev_dbg(icd->parent,
+               "Release%s DMA 0x%08x, queue %sempty\n",
+               mx3_cam->active == buf ? " active" : "", sg_dma_address(&buf->sg),
+               list_empty(&buf->queue) ? "" : "not ");
+       spin_lock_irqsave(&mx3_cam->lock, flags);
+       if (mx3_cam->active == buf)
+               mx3_cam->active = NULL;
+       /* Doesn't hurt also if the list is empty */
+       list_del_init(&buf->queue);
+       if (txd) {
+               buf->txd = NULL;
+               if (mx3_cam->idmac_channel[0])
+                       async_tx_ack(txd);
+       }
+       spin_unlock_irqrestore(&mx3_cam->lock, flags);
+       mx3_cam->buf_total -= vb2_plane_size(vb, 0);
+ }
+ static int mx3_videobuf_init(struct vb2_buffer *vb)
+ {
+       struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
+       struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+       struct mx3_camera_dev *mx3_cam = ici->priv;
+       struct mx3_camera_buffer *buf = to_mx3_vb(vb);
+       if (!buf->txd) {
+               /* This is for locking debugging only */
+               INIT_LIST_HEAD(&buf->queue);
+               sg_init_table(&buf->sg, 1);
+               mx3_cam->buf_total += vb2_plane_size(vb, 0);
+       }
+       return 0;
+ }
+ static int mx3_stop_streaming(struct vb2_queue *q)
+ {
+       struct soc_camera_device *icd = soc_camera_from_vb2q(q);
+       struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+       struct mx3_camera_dev *mx3_cam = ici->priv;
+       struct idmac_channel *ichan = mx3_cam->idmac_channel[0];
+       struct mx3_camera_buffer *buf, *tmp;
+       unsigned long flags;
+       if (ichan) {
+               struct dma_chan *chan = &ichan->dma_chan;
+               chan->device->device_control(chan, DMA_PAUSE, 0);
+       }
+       spin_lock_irqsave(&mx3_cam->lock, flags);
+       mx3_cam->active = NULL;
+       list_for_each_entry_safe(buf, tmp, &mx3_cam->capture, queue) {
+               list_del_init(&buf->queue);
+               vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+       }
+       spin_unlock_irqrestore(&mx3_cam->lock, flags);
+       return 0;
+ }
+ static struct vb2_ops mx3_videobuf_ops = {
+       .queue_setup    = mx3_videobuf_setup,
+       .buf_queue      = mx3_videobuf_queue,
+       .buf_cleanup    = mx3_videobuf_release,
+       .buf_init       = mx3_videobuf_init,
+       .wait_prepare   = soc_camera_unlock,
+       .wait_finish    = soc_camera_lock,
+       .stop_streaming = mx3_stop_streaming,
+ };
+ static int mx3_camera_init_videobuf(struct vb2_queue *q,
+                                    struct soc_camera_device *icd)
+ {
+       q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       q->io_modes = VB2_MMAP | VB2_USERPTR;
+       q->drv_priv = icd;
+       q->ops = &mx3_videobuf_ops;
+       q->mem_ops = &vb2_dma_contig_memops;
+       q->buf_struct_size = sizeof(struct mx3_camera_buffer);
+       return vb2_queue_init(q);
+ }
+ /* First part of ipu_csi_init_interface() */
+ static void mx3_camera_activate(struct mx3_camera_dev *mx3_cam,
+                               struct soc_camera_device *icd)
+ {
+       u32 conf;
+       long rate;
+       /* Set default size: ipu_csi_set_window_size() */
+       csi_reg_write(mx3_cam, (640 - 1) | ((480 - 1) << 16), CSI_ACT_FRM_SIZE);
+       /* ...and position to 0:0: ipu_csi_set_window_pos() */
+       conf = csi_reg_read(mx3_cam, CSI_OUT_FRM_CTRL) & 0xffff0000;
+       csi_reg_write(mx3_cam, conf, CSI_OUT_FRM_CTRL);
+       /* We use only gated clock synchronisation mode so far */
+       conf = 0 << CSI_SENS_CONF_SENS_PRTCL_SHIFT;
+       /* Set generic data, platform-biggest bus-width */
+       conf |= CSI_SENS_CONF_DATA_FMT_BAYER;
+       if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_15)
+               conf |= 3 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
+       else if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_10)
+               conf |= 2 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
+       else if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_8)
+               conf |= 1 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
+       else/* if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_4)*/
+               conf |= 0 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
+       if (mx3_cam->platform_flags & MX3_CAMERA_CLK_SRC)
+               conf |= 1 << CSI_SENS_CONF_SENS_CLKSRC_SHIFT;
+       if (mx3_cam->platform_flags & MX3_CAMERA_EXT_VSYNC)
+               conf |= 1 << CSI_SENS_CONF_EXT_VSYNC_SHIFT;
+       if (mx3_cam->platform_flags & MX3_CAMERA_DP)
+               conf |= 1 << CSI_SENS_CONF_DATA_POL_SHIFT;
+       if (mx3_cam->platform_flags & MX3_CAMERA_PCP)
+               conf |= 1 << CSI_SENS_CONF_PIX_CLK_POL_SHIFT;
+       if (mx3_cam->platform_flags & MX3_CAMERA_HSP)
+               conf |= 1 << CSI_SENS_CONF_HSYNC_POL_SHIFT;
+       if (mx3_cam->platform_flags & MX3_CAMERA_VSP)
+               conf |= 1 << CSI_SENS_CONF_VSYNC_POL_SHIFT;
+       /* ipu_csi_init_interface() */
+       csi_reg_write(mx3_cam, conf, CSI_SENS_CONF);
+       clk_prepare_enable(mx3_cam->clk);
+       rate = clk_round_rate(mx3_cam->clk, mx3_cam->mclk);
+       dev_dbg(icd->parent, "Set SENS_CONF to %x, rate %ld\n", conf, rate);
+       if (rate)
+               clk_set_rate(mx3_cam->clk, rate);
+ }
+ /* Called with .video_lock held */
+ static int mx3_camera_add_device(struct soc_camera_device *icd)
+ {
+       struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+       struct mx3_camera_dev *mx3_cam = ici->priv;
+       if (mx3_cam->icd)
+               return -EBUSY;
+       mx3_camera_activate(mx3_cam, icd);
+       mx3_cam->buf_total = 0;
+       mx3_cam->icd = icd;
+       dev_info(icd->parent, "MX3 Camera driver attached to camera %d\n",
+                icd->devnum);
+       return 0;
+ }
+ /* Called with .video_lock held */
+ static void mx3_camera_remove_device(struct soc_camera_device *icd)
+ {
+       struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+       struct mx3_camera_dev *mx3_cam = ici->priv;
+       struct idmac_channel **ichan = &mx3_cam->idmac_channel[0];
+       BUG_ON(icd != mx3_cam->icd);
+       if (*ichan) {
+               dma_release_channel(&(*ichan)->dma_chan);
+               *ichan = NULL;
+       }
+       clk_disable_unprepare(mx3_cam->clk);
+       mx3_cam->icd = NULL;
+       dev_info(icd->parent, "MX3 Camera driver detached from camera %d\n",
+                icd->devnum);
+ }
+ static int test_platform_param(struct mx3_camera_dev *mx3_cam,
+                              unsigned char buswidth, unsigned long *flags)
+ {
+       /*
+        * If requested data width is supported by the platform, use it or any
+        * possible lower value - i.MX31 is smart enough to shift bits
+        */
+       if (buswidth > fls(mx3_cam->width_flags))
+               return -EINVAL;
+       /*
+        * Platform specified synchronization and pixel clock polarities are
+        * only a recommendation and are only used during probing. MX3x
+        * camera interface only works in master mode, i.e., uses HSYNC and
+        * VSYNC signals from the sensor
+        */
+       *flags = V4L2_MBUS_MASTER |
+               V4L2_MBUS_HSYNC_ACTIVE_HIGH |
+               V4L2_MBUS_HSYNC_ACTIVE_LOW |
+               V4L2_MBUS_VSYNC_ACTIVE_HIGH |
+               V4L2_MBUS_VSYNC_ACTIVE_LOW |
+               V4L2_MBUS_PCLK_SAMPLE_RISING |
+               V4L2_MBUS_PCLK_SAMPLE_FALLING |
+               V4L2_MBUS_DATA_ACTIVE_HIGH |
+               V4L2_MBUS_DATA_ACTIVE_LOW;
+       return 0;
+ }
+ static int mx3_camera_try_bus_param(struct soc_camera_device *icd,
+                                   const unsigned int depth)
+ {
+       struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+       struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+       struct mx3_camera_dev *mx3_cam = ici->priv;
+       struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+       unsigned long bus_flags, common_flags;
+       int ret = test_platform_param(mx3_cam, depth, &bus_flags);
+       dev_dbg(icd->parent, "request bus width %d bit: %d\n", depth, ret);
+       if (ret < 0)
+               return ret;
+       ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+       if (!ret) {
+               common_flags = soc_mbus_config_compatible(&cfg,
+                                                         bus_flags);
+               if (!common_flags) {
+                       dev_warn(icd->parent,
+                                "Flags incompatible: camera 0x%x, host 0x%lx\n",
+                                cfg.flags, bus_flags);
+                       return -EINVAL;
+               }
+       } else if (ret != -ENOIOCTLCMD) {
+               return ret;
+       }
+       return 0;
+ }
+ static bool chan_filter(struct dma_chan *chan, void *arg)
+ {
+       struct dma_chan_request *rq = arg;
+       struct mx3_camera_pdata *pdata;
+       if (!imx_dma_is_ipu(chan))
+               return false;
+       if (!rq)
+               return false;
+       pdata = rq->mx3_cam->soc_host.v4l2_dev.dev->platform_data;
+       return rq->id == chan->chan_id &&
+               pdata->dma_dev == chan->device->dev;
+ }
+ static const struct soc_mbus_pixelfmt mx3_camera_formats[] = {
+       {
+               .fourcc                 = V4L2_PIX_FMT_SBGGR8,
+               .name                   = "Bayer BGGR (sRGB) 8 bit",
+               .bits_per_sample        = 8,
+               .packing                = SOC_MBUS_PACKING_NONE,
+               .order                  = SOC_MBUS_ORDER_LE,
+               .layout                 = SOC_MBUS_LAYOUT_PACKED,
+       }, {
+               .fourcc                 = V4L2_PIX_FMT_GREY,
+               .name                   = "Monochrome 8 bit",
+               .bits_per_sample        = 8,
+               .packing                = SOC_MBUS_PACKING_NONE,
+               .order                  = SOC_MBUS_ORDER_LE,
+               .layout                 = SOC_MBUS_LAYOUT_PACKED,
+       },
+ };
+ /* This will be corrected as we get more formats */
+ static bool mx3_camera_packing_supported(const struct soc_mbus_pixelfmt *fmt)
+ {
+       return  fmt->packing == SOC_MBUS_PACKING_NONE ||
+               (fmt->bits_per_sample == 8 &&
+                fmt->packing == SOC_MBUS_PACKING_2X8_PADHI) ||
+               (fmt->bits_per_sample > 8 &&
+                fmt->packing == SOC_MBUS_PACKING_EXTEND16);
+ }
+ static int mx3_camera_get_formats(struct soc_camera_device *icd, unsigned int idx,
+                                 struct soc_camera_format_xlate *xlate)
+ {
+       struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+       struct device *dev = icd->parent;
+       int formats = 0, ret;
+       enum v4l2_mbus_pixelcode code;
+       const struct soc_mbus_pixelfmt *fmt;
+       ret = v4l2_subdev_call(sd, video, enum_mbus_fmt, idx, &code);
+       if (ret < 0)
+               /* No more formats */
+               return 0;
+       fmt = soc_mbus_get_fmtdesc(code);
+       if (!fmt) {
+               dev_warn(icd->parent,
+                        "Unsupported format code #%u: %d\n", idx, code);
+               return 0;
+       }
+       /* This also checks support for the requested bits-per-sample */
+       ret = mx3_camera_try_bus_param(icd, fmt->bits_per_sample);
+       if (ret < 0)
+               return 0;
+       switch (code) {
+       case V4L2_MBUS_FMT_SBGGR10_1X10:
+               formats++;
+               if (xlate) {
+                       xlate->host_fmt = &mx3_camera_formats[0];
+                       xlate->code     = code;
+                       xlate++;
+                       dev_dbg(dev, "Providing format %s using code %d\n",
+                               mx3_camera_formats[0].name, code);
+               }
+               break;
+       case V4L2_MBUS_FMT_Y10_1X10:
+               formats++;
+               if (xlate) {
+                       xlate->host_fmt = &mx3_camera_formats[1];
+                       xlate->code     = code;
+                       xlate++;
+                       dev_dbg(dev, "Providing format %s using code %d\n",
+                               mx3_camera_formats[1].name, code);
+               }
+               break;
+       default:
+               if (!mx3_camera_packing_supported(fmt))
+                       return 0;
+       }
+       /* Generic pass-through */
+       formats++;
+       if (xlate) {
+               xlate->host_fmt = fmt;
+               xlate->code     = code;
+               dev_dbg(dev, "Providing format %c%c%c%c in pass-through mode\n",
+                       (fmt->fourcc >> (0*8)) & 0xFF,
+                       (fmt->fourcc >> (1*8)) & 0xFF,
+                       (fmt->fourcc >> (2*8)) & 0xFF,
+                       (fmt->fourcc >> (3*8)) & 0xFF);
+               xlate++;
+       }
+       return formats;
+ }
+ static void configure_geometry(struct mx3_camera_dev *mx3_cam,
+                              unsigned int width, unsigned int height,
+                              const struct soc_mbus_pixelfmt *fmt)
+ {
+       u32 ctrl, width_field, height_field;
+       if (fourcc_to_ipu_pix(fmt->fourcc) == IPU_PIX_FMT_GENERIC) {
+               /*
+                * As the CSI will be configured to output BAYER, here
+                * the width parameter count the number of samples to
+                * capture to complete the whole image width.
+                */
+               unsigned int num, den;
+               int ret = soc_mbus_samples_per_pixel(fmt, &num, &den);
+               BUG_ON(ret < 0);
+               width = width * num / den;
+       }
+       /* Setup frame size - this cannot be changed on-the-fly... */
+       width_field = width - 1;
+       height_field = height - 1;
+       csi_reg_write(mx3_cam, width_field | (height_field << 16), CSI_SENS_FRM_SIZE);
+       csi_reg_write(mx3_cam, width_field << 16, CSI_FLASH_STROBE_1);
+       csi_reg_write(mx3_cam, (height_field << 16) | 0x22, CSI_FLASH_STROBE_2);
+       csi_reg_write(mx3_cam, width_field | (height_field << 16), CSI_ACT_FRM_SIZE);
+       /* ...and position */
+       ctrl = csi_reg_read(mx3_cam, CSI_OUT_FRM_CTRL) & 0xffff0000;
+       /* Sensor does the cropping */
+       csi_reg_write(mx3_cam, ctrl | 0 | (0 << 8), CSI_OUT_FRM_CTRL);
+ }
+ static int acquire_dma_channel(struct mx3_camera_dev *mx3_cam)
+ {
+       dma_cap_mask_t mask;
+       struct dma_chan *chan;
+       struct idmac_channel **ichan = &mx3_cam->idmac_channel[0];
+       /* We have to use IDMAC_IC_7 for Bayer / generic data */
+       struct dma_chan_request rq = {.mx3_cam = mx3_cam,
+                                     .id = IDMAC_IC_7};
+       dma_cap_zero(mask);
+       dma_cap_set(DMA_SLAVE, mask);
+       dma_cap_set(DMA_PRIVATE, mask);
+       chan = dma_request_channel(mask, chan_filter, &rq);
+       if (!chan)
+               return -EBUSY;
+       *ichan = to_idmac_chan(chan);
+       (*ichan)->client = mx3_cam;
+       return 0;
+ }
+ /*
+  * FIXME: learn to use stride != width, then we can keep stride properly aligned
+  * and support arbitrary (even) widths.
+  */
+ static inline void stride_align(__u32 *width)
+ {
+       if (ALIGN(*width, 8) < 4096)
+               *width = ALIGN(*width, 8);
+       else
+               *width = *width &  ~7;
+ }
+ /*
+  * As long as we don't implement host-side cropping and scaling, we can use
+  * default g_crop and cropcap from soc_camera.c
+  */
+ static int mx3_camera_set_crop(struct soc_camera_device *icd,
+                              struct v4l2_crop *a)
+ {
+       struct v4l2_rect *rect = &a->c;
+       struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+       struct mx3_camera_dev *mx3_cam = ici->priv;
+       struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+       struct v4l2_mbus_framefmt mf;
+       int ret;
+       soc_camera_limit_side(&rect->left, &rect->width, 0, 2, 4096);
+       soc_camera_limit_side(&rect->top, &rect->height, 0, 2, 4096);
+       ret = v4l2_subdev_call(sd, video, s_crop, a);
+       if (ret < 0)
+               return ret;
+       /* The capture device might have changed its output sizes */
+       ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mf);
+       if (ret < 0)
+               return ret;
+       if (mf.code != icd->current_fmt->code)
+               return -EINVAL;
+       if (mf.width & 7) {
+               /* Ouch! We can only handle 8-byte aligned width... */
+               stride_align(&mf.width);
+               ret = v4l2_subdev_call(sd, video, s_mbus_fmt, &mf);
+               if (ret < 0)
+                       return ret;
+       }
+       if (mf.width != icd->user_width || mf.height != icd->user_height)
+               configure_geometry(mx3_cam, mf.width, mf.height,
+                                  icd->current_fmt->host_fmt);
+       dev_dbg(icd->parent, "Sensor cropped %dx%d\n",
+               mf.width, mf.height);
+       icd->user_width         = mf.width;
+       icd->user_height        = mf.height;
+       return ret;
+ }
+ static int mx3_camera_set_fmt(struct soc_camera_device *icd,
+                             struct v4l2_format *f)
+ {
+       struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+       struct mx3_camera_dev *mx3_cam = ici->priv;
+       struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+       const struct soc_camera_format_xlate *xlate;
+       struct v4l2_pix_format *pix = &f->fmt.pix;
+       struct v4l2_mbus_framefmt mf;
+       int ret;
+       xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
+       if (!xlate) {
+               dev_warn(icd->parent, "Format %x not found\n",
+                        pix->pixelformat);
+               return -EINVAL;
+       }
+       stride_align(&pix->width);
+       dev_dbg(icd->parent, "Set format %dx%d\n", pix->width, pix->height);
+       /*
+        * Might have to perform a complete interface initialisation like in
+        * ipu_csi_init_interface() in mxc_v4l2_s_param(). Also consider
+        * mxc_v4l2_s_fmt()
+        */
+       configure_geometry(mx3_cam, pix->width, pix->height, xlate->host_fmt);
+       mf.width        = pix->width;
+       mf.height       = pix->height;
+       mf.field        = pix->field;
+       mf.colorspace   = pix->colorspace;
+       mf.code         = xlate->code;
+       ret = v4l2_subdev_call(sd, video, s_mbus_fmt, &mf);
+       if (ret < 0)
+               return ret;
+       if (mf.code != xlate->code)
+               return -EINVAL;
+       if (!mx3_cam->idmac_channel[0]) {
+               ret = acquire_dma_channel(mx3_cam);
+               if (ret < 0)
+                       return ret;
+       }
+       pix->width              = mf.width;
+       pix->height             = mf.height;
+       pix->field              = mf.field;
+       mx3_cam->field          = mf.field;
+       pix->colorspace         = mf.colorspace;
+       icd->current_fmt        = xlate;
+       dev_dbg(icd->parent, "Sensor set %dx%d\n", pix->width, pix->height);
+       return ret;
+ }
+ static int mx3_camera_try_fmt(struct soc_camera_device *icd,
+                             struct v4l2_format *f)
+ {
+       struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+       const struct soc_camera_format_xlate *xlate;
+       struct v4l2_pix_format *pix = &f->fmt.pix;
+       struct v4l2_mbus_framefmt mf;
+       __u32 pixfmt = pix->pixelformat;
+       int ret;
+       xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
+       if (pixfmt && !xlate) {
+               dev_warn(icd->parent, "Format %x not found\n", pixfmt);
+               return -EINVAL;
+       }
+       /* limit to MX3 hardware capabilities */
+       if (pix->height > 4096)
+               pix->height = 4096;
+       if (pix->width > 4096)
+               pix->width = 4096;
+       /* limit to sensor capabilities */
+       mf.width        = pix->width;
+       mf.height       = pix->height;
+       mf.field        = pix->field;
+       mf.colorspace   = pix->colorspace;
+       mf.code         = xlate->code;
+       ret = v4l2_subdev_call(sd, video, try_mbus_fmt, &mf);
+       if (ret < 0)
+               return ret;
+       pix->width      = mf.width;
+       pix->height     = mf.height;
+       pix->colorspace = mf.colorspace;
+       switch (mf.field) {
+       case V4L2_FIELD_ANY:
+               pix->field = V4L2_FIELD_NONE;
+               break;
+       case V4L2_FIELD_NONE:
+               break;
+       default:
+               dev_err(icd->parent, "Field type %d unsupported.\n",
+                       mf.field);
+               ret = -EINVAL;
+       }
+       return ret;
+ }
+ static int mx3_camera_reqbufs(struct soc_camera_device *icd,
+                             struct v4l2_requestbuffers *p)
+ {
+       return 0;
+ }
+ static unsigned int mx3_camera_poll(struct file *file, poll_table *pt)
+ {
+       struct soc_camera_device *icd = file->private_data;
+       return vb2_poll(&icd->vb2_vidq, file, pt);
+ }
+ static int mx3_camera_querycap(struct soc_camera_host *ici,
+                              struct v4l2_capability *cap)
+ {
+       /* cap->name is set by the firendly caller:-> */
+       strlcpy(cap->card, "i.MX3x Camera", sizeof(cap->card));
+       cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+       return 0;
+ }
+ static int mx3_camera_set_bus_param(struct soc_camera_device *icd)
+ {
+       struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+       struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+       struct mx3_camera_dev *mx3_cam = ici->priv;
+       struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+       u32 pixfmt = icd->current_fmt->host_fmt->fourcc;
+       unsigned long bus_flags, common_flags;
+       u32 dw, sens_conf;
+       const struct soc_mbus_pixelfmt *fmt;
+       int buswidth;
+       int ret;
+       const struct soc_camera_format_xlate *xlate;
+       struct device *dev = icd->parent;
+       fmt = soc_mbus_get_fmtdesc(icd->current_fmt->code);
+       if (!fmt)
+               return -EINVAL;
+       xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
+       if (!xlate) {
+               dev_warn(dev, "Format %x not found\n", pixfmt);
+               return -EINVAL;
+       }
+       buswidth = fmt->bits_per_sample;
+       ret = test_platform_param(mx3_cam, buswidth, &bus_flags);
+       dev_dbg(dev, "requested bus width %d bit: %d\n", buswidth, ret);
+       if (ret < 0)
+               return ret;
+       ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+       if (!ret) {
+               common_flags = soc_mbus_config_compatible(&cfg,
+                                                         bus_flags);
+               if (!common_flags) {
+                       dev_warn(icd->parent,
+                                "Flags incompatible: camera 0x%x, host 0x%lx\n",
+                                cfg.flags, bus_flags);
+                       return -EINVAL;
+               }
+       } else if (ret != -ENOIOCTLCMD) {
+               return ret;
+       } else {
+               common_flags = bus_flags;
+       }
+       dev_dbg(dev, "Flags cam: 0x%x host: 0x%lx common: 0x%lx\n",
+               cfg.flags, bus_flags, common_flags);
+       /* Make choices, based on platform preferences */
+       if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) &&
+           (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) {
+               if (mx3_cam->platform_flags & MX3_CAMERA_HSP)
+                       common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH;
+               else
+                       common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW;
+       }
+       if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) &&
+           (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) {
+               if (mx3_cam->platform_flags & MX3_CAMERA_VSP)
+                       common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH;
+               else
+                       common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW;
+       }
+       if ((common_flags & V4L2_MBUS_DATA_ACTIVE_HIGH) &&
+           (common_flags & V4L2_MBUS_DATA_ACTIVE_LOW)) {
+               if (mx3_cam->platform_flags & MX3_CAMERA_DP)
+                       common_flags &= ~V4L2_MBUS_DATA_ACTIVE_HIGH;
+               else
+                       common_flags &= ~V4L2_MBUS_DATA_ACTIVE_LOW;
+       }
+       if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) &&
+           (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) {
+               if (mx3_cam->platform_flags & MX3_CAMERA_PCP)
+                       common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING;
+               else
+                       common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING;
+       }
+       cfg.flags = common_flags;
+       ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
+       if (ret < 0 && ret != -ENOIOCTLCMD) {
+               dev_dbg(dev, "camera s_mbus_config(0x%lx) returned %d\n",
+                       common_flags, ret);
+               return ret;
+       }
+       /*
+        * So far only gated clock mode is supported. Add a line
+        *      (3 << CSI_SENS_CONF_SENS_PRTCL_SHIFT) |
+        * below and select the required mode when supporting other
+        * synchronisation protocols.
+        */
+       sens_conf = csi_reg_read(mx3_cam, CSI_SENS_CONF) &
+               ~((1 << CSI_SENS_CONF_VSYNC_POL_SHIFT) |
+                 (1 << CSI_SENS_CONF_HSYNC_POL_SHIFT) |
+                 (1 << CSI_SENS_CONF_DATA_POL_SHIFT) |
+                 (1 << CSI_SENS_CONF_PIX_CLK_POL_SHIFT) |
+                 (3 << CSI_SENS_CONF_DATA_FMT_SHIFT) |
+                 (3 << CSI_SENS_CONF_DATA_WIDTH_SHIFT));
+       /* TODO: Support RGB and YUV formats */
+       /* This has been set in mx3_camera_activate(), but we clear it above */
+       sens_conf |= CSI_SENS_CONF_DATA_FMT_BAYER;
+       if (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
+               sens_conf |= 1 << CSI_SENS_CONF_PIX_CLK_POL_SHIFT;
+       if (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
+               sens_conf |= 1 << CSI_SENS_CONF_HSYNC_POL_SHIFT;
+       if (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
+               sens_conf |= 1 << CSI_SENS_CONF_VSYNC_POL_SHIFT;
+       if (common_flags & V4L2_MBUS_DATA_ACTIVE_LOW)
+               sens_conf |= 1 << CSI_SENS_CONF_DATA_POL_SHIFT;
+       /* Just do what we're asked to do */
+       switch (xlate->host_fmt->bits_per_sample) {
+       case 4:
+               dw = 0 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
+               break;
+       case 8:
+               dw = 1 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
+               break;
+       case 10:
+               dw = 2 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
+               break;
+       default:
+               /*
+                * Actually it can only be 15 now, default is just to silence
+                * compiler warnings
+                */
+       case 15:
+               dw = 3 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
+       }
+       csi_reg_write(mx3_cam, sens_conf | dw, CSI_SENS_CONF);
+       dev_dbg(dev, "Set SENS_CONF to %x\n", sens_conf | dw);
+       return 0;
+ }
+ static struct soc_camera_host_ops mx3_soc_camera_host_ops = {
+       .owner          = THIS_MODULE,
+       .add            = mx3_camera_add_device,
+       .remove         = mx3_camera_remove_device,
+       .set_crop       = mx3_camera_set_crop,
+       .set_fmt        = mx3_camera_set_fmt,
+       .try_fmt        = mx3_camera_try_fmt,
+       .get_formats    = mx3_camera_get_formats,
+       .init_videobuf2 = mx3_camera_init_videobuf,
+       .reqbufs        = mx3_camera_reqbufs,
+       .poll           = mx3_camera_poll,
+       .querycap       = mx3_camera_querycap,
+       .set_bus_param  = mx3_camera_set_bus_param,
+ };
+ static int __devinit mx3_camera_probe(struct platform_device *pdev)
+ {
+       struct mx3_camera_dev *mx3_cam;
+       struct resource *res;
+       void __iomem *base;
+       int err = 0;
+       struct soc_camera_host *soc_host;
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               err = -ENODEV;
+               goto egetres;
+       }
+       mx3_cam = vzalloc(sizeof(*mx3_cam));
+       if (!mx3_cam) {
+               dev_err(&pdev->dev, "Could not allocate mx3 camera object\n");
+               err = -ENOMEM;
+               goto ealloc;
+       }
+       mx3_cam->clk = clk_get(&pdev->dev, NULL);
+       if (IS_ERR(mx3_cam->clk)) {
+               err = PTR_ERR(mx3_cam->clk);
+               goto eclkget;
+       }
+       mx3_cam->pdata = pdev->dev.platform_data;
+       mx3_cam->platform_flags = mx3_cam->pdata->flags;
+       if (!(mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_MASK)) {
+               /*
+                * Platform hasn't set available data widths. This is bad.
+                * Warn and use a default.
+                */
+               dev_warn(&pdev->dev, "WARNING! Platform hasn't set available "
+                        "data widths, using default 8 bit\n");
+               mx3_cam->platform_flags |= MX3_CAMERA_DATAWIDTH_8;
+       }
+       if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_4)
+               mx3_cam->width_flags = 1 << 3;
+       if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_8)
+               mx3_cam->width_flags |= 1 << 7;
+       if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_10)
+               mx3_cam->width_flags |= 1 << 9;
+       if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_15)
+               mx3_cam->width_flags |= 1 << 14;
+       mx3_cam->mclk = mx3_cam->pdata->mclk_10khz * 10000;
+       if (!mx3_cam->mclk) {
+               dev_warn(&pdev->dev,
+                        "mclk_10khz == 0! Please, fix your platform data. "
+                        "Using default 20MHz\n");
+               mx3_cam->mclk = 20000000;
+       }
+       /* list of video-buffers */
+       INIT_LIST_HEAD(&mx3_cam->capture);
+       spin_lock_init(&mx3_cam->lock);
+       base = ioremap(res->start, resource_size(res));
+       if (!base) {
+               pr_err("Couldn't map %x@%x\n", resource_size(res), res->start);
+               err = -ENOMEM;
+               goto eioremap;
+       }
+       mx3_cam->base   = base;
+       soc_host                = &mx3_cam->soc_host;
+       soc_host->drv_name      = MX3_CAM_DRV_NAME;
+       soc_host->ops           = &mx3_soc_camera_host_ops;
+       soc_host->priv          = mx3_cam;
+       soc_host->v4l2_dev.dev  = &pdev->dev;
+       soc_host->nr            = pdev->id;
+       mx3_cam->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
+       if (IS_ERR(mx3_cam->alloc_ctx)) {
+               err = PTR_ERR(mx3_cam->alloc_ctx);
+               goto eallocctx;
+       }
+       err = soc_camera_host_register(soc_host);
+       if (err)
+               goto ecamhostreg;
+       /* IDMAC interface */
+       dmaengine_get();
+       return 0;
+ ecamhostreg:
+       vb2_dma_contig_cleanup_ctx(mx3_cam->alloc_ctx);
+ eallocctx:
+       iounmap(base);
+ eioremap:
+       clk_put(mx3_cam->clk);
+ eclkget:
+       vfree(mx3_cam);
+ ealloc:
+ egetres:
+       return err;
+ }
+ static int __devexit mx3_camera_remove(struct platform_device *pdev)
+ {
+       struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev);
+       struct mx3_camera_dev *mx3_cam = container_of(soc_host,
+                                       struct mx3_camera_dev, soc_host);
+       clk_put(mx3_cam->clk);
+       soc_camera_host_unregister(soc_host);
+       iounmap(mx3_cam->base);
+       /*
+        * The channel has either not been allocated,
+        * or should have been released
+        */
+       if (WARN_ON(mx3_cam->idmac_channel[0]))
+               dma_release_channel(&mx3_cam->idmac_channel[0]->dma_chan);
+       vb2_dma_contig_cleanup_ctx(mx3_cam->alloc_ctx);
+       vfree(mx3_cam);
+       dmaengine_put();
+       return 0;
+ }
+ static struct platform_driver mx3_camera_driver = {
+       .driver         = {
+               .name   = MX3_CAM_DRV_NAME,
+       },
+       .probe          = mx3_camera_probe,
+       .remove         = __devexit_p(mx3_camera_remove),
+ };
+ module_platform_driver(mx3_camera_driver);
+ MODULE_DESCRIPTION("i.MX3x SoC Camera Host driver");
+ MODULE_AUTHOR("Guennadi Liakhovetski <lg@denx.de>");
+ MODULE_LICENSE("GPL v2");
+ MODULE_VERSION("0.2.3");
+ MODULE_ALIAS("platform:" MX3_CAM_DRV_NAME);
index 0000000,ab98d08..f7297ae
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,3415 +1,3415 @@@
 -      flush_work_sync(&dev->request_module_wk);
+ /*
+    em28xx-cards.c - driver for Empia EM2800/EM2820/2840 USB
+                   video capture devices
+    Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it>
+                     Markus Rechberger <mrechberger@gmail.com>
+                     Mauro Carvalho Chehab <mchehab@infradead.org>
+                     Sascha Sommer <saschasommer@freenet.de>
+    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., 675 Mass Ave, Cambridge, MA 02139, USA.
+  */
+ #include <linux/init.h>
+ #include <linux/module.h>
+ #include <linux/slab.h>
+ #include <linux/delay.h>
+ #include <linux/i2c.h>
+ #include <linux/usb.h>
+ #include <media/tuner.h>
+ #include <media/msp3400.h>
+ #include <media/saa7115.h>
+ #include <media/tvp5150.h>
+ #include <media/tvaudio.h>
+ #include <media/mt9v011.h>
+ #include <media/i2c-addr.h>
+ #include <media/tveeprom.h>
+ #include <media/v4l2-common.h>
+ #include <media/v4l2-chip-ident.h>
+ #include "em28xx.h"
+ #define DRIVER_NAME         "em28xx"
+ static int tuner = -1;
+ module_param(tuner, int, 0444);
+ MODULE_PARM_DESC(tuner, "tuner type");
+ static unsigned int disable_ir;
+ module_param(disable_ir, int, 0444);
+ MODULE_PARM_DESC(disable_ir, "disable infrared remote support");
+ static unsigned int disable_usb_speed_check;
+ module_param(disable_usb_speed_check, int, 0444);
+ MODULE_PARM_DESC(disable_usb_speed_check,
+                "override min bandwidth requirement of 480M bps");
+ static unsigned int card[]     = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
+ module_param_array(card,  int, NULL, 0444);
+ MODULE_PARM_DESC(card,     "card type");
+ /* Bitmask marking allocated devices from 0 to EM28XX_MAXBOARDS - 1 */
+ static unsigned long em28xx_devused;
+ struct em28xx_hash_table {
+       unsigned long hash;
+       unsigned int  model;
+       unsigned int  tuner;
+ };
+ static void em28xx_pre_card_setup(struct em28xx *dev);
+ /*
+  *  Reset sequences for analog/digital modes
+  */
+ /* Reset for the most [analog] boards */
+ static struct em28xx_reg_seq default_analog[] = {
+       {EM28XX_R08_GPIO,       0x6d,   ~EM_GPIO_4,     10},
+       {       -1,             -1,     -1,             -1},
+ };
+ /* Reset for the most [digital] boards */
+ static struct em28xx_reg_seq default_digital[] = {
+       {EM28XX_R08_GPIO,       0x6e,   ~EM_GPIO_4,     10},
+       {       -1,             -1,     -1,             -1},
+ };
+ /* Board Hauppauge WinTV HVR 900 analog */
+ static struct em28xx_reg_seq hauppauge_wintv_hvr_900_analog[] = {
+       {EM28XX_R08_GPIO,       0x2d,   ~EM_GPIO_4,     10},
+       {0x05,                  0xff,   0x10,           10},
+       {  -1,                  -1,     -1,             -1},
+ };
+ /* Board Hauppauge WinTV HVR 900 digital */
+ static struct em28xx_reg_seq hauppauge_wintv_hvr_900_digital[] = {
+       {EM28XX_R08_GPIO,       0x2e,   ~EM_GPIO_4,     10},
+       {EM2880_R04_GPO,        0x04,   0x0f,           10},
+       {EM2880_R04_GPO,        0x0c,   0x0f,           10},
+       { -1,                   -1,     -1,             -1},
+ };
+ /* Board Hauppauge WinTV HVR 900 (R2) digital */
+ static struct em28xx_reg_seq hauppauge_wintv_hvr_900R2_digital[] = {
+       {EM28XX_R08_GPIO,       0x2e,   ~EM_GPIO_4,     10},
+       {EM2880_R04_GPO,        0x0c,   0x0f,           10},
+       { -1,                   -1,     -1,             -1},
+ };
+ /* Boards - EM2880 MSI DIGIVOX AD and EM2880_BOARD_MSI_DIGIVOX_AD_II */
+ static struct em28xx_reg_seq em2880_msi_digivox_ad_analog[] = {
+       {EM28XX_R08_GPIO,       0x69,   ~EM_GPIO_4,      10},
+       {       -1,             -1,     -1,              -1},
+ };
+ /* Boards - EM2880 MSI DIGIVOX AD and EM2880_BOARD_MSI_DIGIVOX_AD_II */
+ /* Board  - EM2870 Kworld 355u
+    Analog - No input analog */
+ /* Board - EM2882 Kworld 315U digital */
+ static struct em28xx_reg_seq em2882_kworld_315u_digital[] = {
+       {EM28XX_R08_GPIO,       0xff,   0xff,           10},
+       {EM28XX_R08_GPIO,       0xfe,   0xff,           10},
+       {EM2880_R04_GPO,        0x04,   0xff,           10},
+       {EM2880_R04_GPO,        0x0c,   0xff,           10},
+       {EM28XX_R08_GPIO,       0x7e,   0xff,           10},
+       {  -1,                  -1,     -1,             -1},
+ };
+ static struct em28xx_reg_seq em2882_kworld_315u_tuner_gpio[] = {
+       {EM2880_R04_GPO,        0x08,   0xff,           10},
+       {EM2880_R04_GPO,        0x0c,   0xff,           10},
+       {EM2880_R04_GPO,        0x08,   0xff,           10},
+       {EM2880_R04_GPO,        0x0c,   0xff,           10},
+       {  -1,                  -1,     -1,             -1},
+ };
+ static struct em28xx_reg_seq kworld_330u_analog[] = {
+       {EM28XX_R08_GPIO,       0x6d,   ~EM_GPIO_4,     10},
+       {EM2880_R04_GPO,        0x00,   0xff,           10},
+       { -1,                   -1,     -1,             -1},
+ };
+ static struct em28xx_reg_seq kworld_330u_digital[] = {
+       {EM28XX_R08_GPIO,       0x6e,   ~EM_GPIO_4,     10},
+       {EM2880_R04_GPO,        0x08,   0xff,           10},
+       { -1,                   -1,     -1,             -1},
+ };
+ /* Evga inDtube
+    GPIO0 - Enable digital power (s5h1409) - low to enable
+    GPIO1 - Enable analog power (tvp5150/emp202) - low to enable
+    GPIO4 - xc3028 reset
+    GOP3  - s5h1409 reset
+  */
+ static struct em28xx_reg_seq evga_indtube_analog[] = {
+       {EM28XX_R08_GPIO,       0x79,   0xff,           60},
+       {       -1,             -1,     -1,             -1},
+ };
+ static struct em28xx_reg_seq evga_indtube_digital[] = {
+       {EM28XX_R08_GPIO,       0x7a,   0xff,            1},
+       {EM2880_R04_GPO,        0x04,   0xff,           10},
+       {EM2880_R04_GPO,        0x0c,   0xff,            1},
+       { -1,                   -1,     -1,             -1},
+ };
+ /*
+  * KWorld PlusTV 340U and UB435-Q (ATSC) GPIOs map:
+  * EM_GPIO_0 - currently unknown
+  * EM_GPIO_1 - LED disable/enable (1 = off, 0 = on)
+  * EM_GPIO_2 - currently unknown
+  * EM_GPIO_3 - currently unknown
+  * EM_GPIO_4 - TDA18271HD/C1 tuner (1 = active, 0 = in reset)
+  * EM_GPIO_5 - LGDT3304 ATSC/QAM demod (1 = active, 0 = in reset)
+  * EM_GPIO_6 - currently unknown
+  * EM_GPIO_7 - currently unknown
+  */
+ static struct em28xx_reg_seq kworld_a340_digital[] = {
+       {EM28XX_R08_GPIO,       0x6d,           ~EM_GPIO_4,     10},
+       { -1,                   -1,             -1,             -1},
+ };
+ /* Pinnacle Hybrid Pro eb1a:2881 */
+ static struct em28xx_reg_seq pinnacle_hybrid_pro_analog[] = {
+       {EM28XX_R08_GPIO,       0xfd,   ~EM_GPIO_4,     10},
+       {       -1,             -1,     -1,             -1},
+ };
+ static struct em28xx_reg_seq pinnacle_hybrid_pro_digital[] = {
+       {EM28XX_R08_GPIO,       0x6e,   ~EM_GPIO_4,     10},
+       {EM2880_R04_GPO,        0x04,   0xff,          100},/* zl10353 reset */
+       {EM2880_R04_GPO,        0x0c,   0xff,            1},
+       {       -1,             -1,     -1,             -1},
+ };
+ static struct em28xx_reg_seq terratec_cinergy_USB_XS_FR_analog[] = {
+       {EM28XX_R08_GPIO,       0x6d,   ~EM_GPIO_4,     10},
+       {EM2880_R04_GPO,        0x00,   0xff,           10},
+       { -1,                   -1,     -1,             -1},
+ };
+ static struct em28xx_reg_seq terratec_cinergy_USB_XS_FR_digital[] = {
+       {EM28XX_R08_GPIO,       0x6e,   ~EM_GPIO_4,     10},
+       {EM2880_R04_GPO,        0x08,   0xff,           10},
+       { -1,                   -1,     -1,             -1},
+ };
+ /* eb1a:2868 Reddo DVB-C USB TV Box
+    GPIO4 - CU1216L NIM
+    Other GPIOs seems to be don't care. */
+ static struct em28xx_reg_seq reddo_dvb_c_usb_box[] = {
+       {EM28XX_R08_GPIO,       0xfe,   0xff,           10},
+       {EM28XX_R08_GPIO,       0xde,   0xff,           10},
+       {EM28XX_R08_GPIO,       0xfe,   0xff,           10},
+       {EM28XX_R08_GPIO,       0xff,   0xff,           10},
+       {EM28XX_R08_GPIO,       0x7f,   0xff,           10},
+       {EM28XX_R08_GPIO,       0x6f,   0xff,           10},
+       {EM28XX_R08_GPIO,       0xff,   0xff,           10},
+       {-1,                    -1,     -1,             -1},
+ };
+ /* Callback for the most boards */
+ static struct em28xx_reg_seq default_tuner_gpio[] = {
+       {EM28XX_R08_GPIO,       EM_GPIO_4,      EM_GPIO_4,      10},
+       {EM28XX_R08_GPIO,       0,              EM_GPIO_4,      10},
+       {EM28XX_R08_GPIO,       EM_GPIO_4,      EM_GPIO_4,      10},
+       {  -1,                  -1,             -1,             -1},
+ };
+ /* Mute/unmute */
+ static struct em28xx_reg_seq compro_unmute_tv_gpio[] = {
+       {EM28XX_R08_GPIO,       5,              7,              10},
+       {  -1,                  -1,             -1,             -1},
+ };
+ static struct em28xx_reg_seq compro_unmute_svid_gpio[] = {
+       {EM28XX_R08_GPIO,       4,              7,              10},
+       {  -1,                  -1,             -1,             -1},
+ };
+ static struct em28xx_reg_seq compro_mute_gpio[] = {
+       {EM28XX_R08_GPIO,       6,              7,              10},
+       {  -1,                  -1,             -1,             -1},
+ };
+ /* Terratec AV350 */
+ static struct em28xx_reg_seq terratec_av350_mute_gpio[] = {
+       {EM28XX_R08_GPIO,       0xff,   0x7f,           10},
+       {       -1,             -1,     -1,             -1},
+ };
+ static struct em28xx_reg_seq terratec_av350_unmute_gpio[] = {
+       {EM28XX_R08_GPIO,       0xff,   0xff,           10},
+       {       -1,             -1,     -1,             -1},
+ };
+ static struct em28xx_reg_seq silvercrest_reg_seq[] = {
+       {EM28XX_R08_GPIO,       0xff,   0xff,           10},
+       {EM28XX_R08_GPIO,       0x01,   0xf7,           10},
+       {       -1,             -1,     -1,             -1},
+ };
+ static struct em28xx_reg_seq vc211a_enable[] = {
+       {EM28XX_R08_GPIO,       0xff,   0x07,           10},
+       {EM28XX_R08_GPIO,       0xff,   0x0f,           10},
+       {EM28XX_R08_GPIO,       0xff,   0x0b,           10},
+       {       -1,             -1,     -1,             -1},
+ };
+ static struct em28xx_reg_seq dikom_dk300_digital[] = {
+       {EM28XX_R08_GPIO,       0x6e,   ~EM_GPIO_4,     10},
+       {EM2880_R04_GPO,        0x08,   0xff,           10},
+       { -1,                   -1,     -1,             -1},
+ };
+ /* Reset for the most [digital] boards */
+ static struct em28xx_reg_seq leadership_digital[] = {
+       {EM2874_R80_GPIO,       0x70,   0xff,   10},
+       {       -1,             -1,     -1,     -1},
+ };
+ static struct em28xx_reg_seq leadership_reset[] = {
+       {EM2874_R80_GPIO,       0xf0,   0xff,   10},
+       {EM2874_R80_GPIO,       0xb0,   0xff,   10},
+       {EM2874_R80_GPIO,       0xf0,   0xff,   10},
+       {       -1,             -1,     -1,     -1},
+ };
+ /* 2013:024f PCTV nanoStick T2 290e
+  * GPIO_6 - demod reset
+  * GPIO_7 - LED
+  */
+ static struct em28xx_reg_seq pctv_290e[] = {
+       {EM2874_R80_GPIO,       0x00,   0xff,           80},
+       {EM2874_R80_GPIO,       0x40,   0xff,           80}, /* GPIO_6 = 1 */
+       {EM2874_R80_GPIO,       0xc0,   0xff,           80}, /* GPIO_7 = 1 */
+       {-1,                    -1,     -1,             -1},
+ };
+ #if 0
+ static struct em28xx_reg_seq terratec_h5_gpio[] = {
+       {EM28XX_R08_GPIO,       0xff,   0xff,   10},
+       {EM2874_R80_GPIO,       0xf6,   0xff,   100},
+       {EM2874_R80_GPIO,       0xf2,   0xff,   50},
+       {EM2874_R80_GPIO,       0xf6,   0xff,   50},
+       { -1,                   -1,     -1,     -1},
+ };
+ static struct em28xx_reg_seq terratec_h5_digital[] = {
+       {EM2874_R80_GPIO,       0xf6,   0xff,   10},
+       {EM2874_R80_GPIO,       0xe6,   0xff,   100},
+       {EM2874_R80_GPIO,       0xa6,   0xff,   10},
+       { -1,                   -1,     -1,     -1},
+ };
+ #endif
+ /* 2013:024f PCTV DVB-S2 Stick 460e
+  * GPIO_0 - POWER_ON
+  * GPIO_1 - BOOST
+  * GPIO_2 - VUV_LNB (red LED)
+  * GPIO_3 - EXT_12V
+  * GPIO_4 - INT_DEM (DEMOD GPIO_0)
+  * GPIO_5 - INT_LNB
+  * GPIO_6 - RESET_DEM
+  * GPIO_7 - LED (green LED)
+  */
+ static struct em28xx_reg_seq pctv_460e[] = {
+       {EM2874_R80_GPIO, 0x01, 0xff,  50},
+       {0x0d,            0xff, 0xff,  50},
+       {EM2874_R80_GPIO, 0x41, 0xff,  50}, /* GPIO_6=1 */
+       {0x0d,            0x42, 0xff,  50},
+       {EM2874_R80_GPIO, 0x61, 0xff,  50}, /* GPIO_5=1 */
+       {             -1,   -1,   -1,  -1},
+ };
+ #if 0
+ static struct em28xx_reg_seq hauppauge_930c_gpio[] = {
+       {EM2874_R80_GPIO,       0x6f,   0xff,   10},
+       {EM2874_R80_GPIO,       0x4f,   0xff,   10}, /* xc5000 reset */
+       {EM2874_R80_GPIO,       0x6f,   0xff,   10},
+       {EM2874_R80_GPIO,       0x4f,   0xff,   10},
+       { -1,                   -1,     -1,     -1},
+ };
+ static struct em28xx_reg_seq hauppauge_930c_digital[] = {
+       {EM2874_R80_GPIO,       0xf6,   0xff,   10},
+       {EM2874_R80_GPIO,       0xe6,   0xff,   100},
+       {EM2874_R80_GPIO,       0xa6,   0xff,   10},
+       { -1,                   -1,     -1,     -1},
+ };
+ #endif
+ /* 1b80:e425 MaxMedia UB425-TC
+  * GPIO_6 - demod reset, 0=active
+  * GPIO_7 - LED, 0=active
+  */
+ static struct em28xx_reg_seq maxmedia_ub425_tc[] = {
+       {EM2874_R80_GPIO,  0x83,  0xff,  100},
+       {EM2874_R80_GPIO,  0xc3,  0xff,  100}, /* GPIO_6 = 1 */
+       {EM2874_R80_GPIO,  0x43,  0xff,  000}, /* GPIO_7 = 0 */
+       {-1,                 -1,    -1,   -1},
+ };
+ /* 2304:0242 PCTV QuatroStick (510e)
+  * GPIO_2: decoder reset, 0=active
+  * GPIO_4: decoder suspend, 0=active
+  * GPIO_6: demod reset, 0=active
+  * GPIO_7: LED, 1=active
+  */
+ static struct em28xx_reg_seq pctv_510e[] = {
+       {EM2874_R80_GPIO, 0x10, 0xff, 100},
+       {EM2874_R80_GPIO, 0x14, 0xff, 100}, /* GPIO_2 = 1 */
+       {EM2874_R80_GPIO, 0x54, 0xff, 050}, /* GPIO_6 = 1 */
+       {             -1,   -1,   -1,  -1},
+ };
+ /* 2013:0251 PCTV QuatroStick nano (520e)
+  * GPIO_2: decoder reset, 0=active
+  * GPIO_4: decoder suspend, 0=active
+  * GPIO_6: demod reset, 0=active
+  * GPIO_7: LED, 1=active
+  */
+ static struct em28xx_reg_seq pctv_520e[] = {
+       {EM2874_R80_GPIO, 0x10, 0xff, 100},
+       {EM2874_R80_GPIO, 0x14, 0xff, 100}, /* GPIO_2 = 1 */
+       {EM2874_R80_GPIO, 0x54, 0xff, 050}, /* GPIO_6 = 1 */
+       {EM2874_R80_GPIO, 0xd4, 0xff, 000}, /* GPIO_7 = 1 */
+       {             -1,   -1,   -1,  -1},
+ };
+ /*
+  *  Board definitions
+  */
+ struct em28xx_board em28xx_boards[] = {
+       [EM2750_BOARD_UNKNOWN] = {
+               .name          = "EM2710/EM2750/EM2751 webcam grabber",
+               .xclk          = EM28XX_XCLK_FREQUENCY_20MHZ,
+               .tuner_type    = TUNER_ABSENT,
+               .is_webcam     = 1,
+               .input         = { {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = 0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+                       .gpio     = silvercrest_reg_seq,
+               } },
+       },
+       [EM2800_BOARD_UNKNOWN] = {
+               .name         = "Unknown EM2800 video grabber",
+               .is_em2800    = 1,
+               .tda9887_conf = TDA9887_PRESENT,
+               .decoder      = EM28XX_SAA711X,
+               .tuner_type   = TUNER_ABSENT,
+               .input        = { {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = SAA7115_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = SAA7115_SVIDEO3,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2820_BOARD_UNKNOWN] = {
+               .name          = "Unknown EM2750/28xx video grabber",
+               .tuner_type    = TUNER_ABSENT,
+               .is_webcam     = 1,     /* To enable sensor probe */
+       },
+       [EM2750_BOARD_DLCW_130] = {
+               /* Beijing Huaqi Information Digital Technology Co., Ltd */
+               .name          = "Huaqi DLCW-130",
+               .valid         = EM28XX_BOARD_NOT_VALIDATED,
+               .xclk          = EM28XX_XCLK_FREQUENCY_48MHZ,
+               .tuner_type    = TUNER_ABSENT,
+               .is_webcam     = 1,
+               .input         = { {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = 0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+               } },
+       },
+       [EM2820_BOARD_KWORLD_PVRTV2800RF] = {
+               .name         = "Kworld PVR TV 2800 RF",
+               .tuner_type   = TUNER_TEMIC_PAL,
+               .tda9887_conf = TDA9887_PRESENT,
+               .decoder      = EM28XX_SAA711X,
+               .input        = { {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = SAA7115_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = SAA7115_SVIDEO3,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2820_BOARD_GADMEI_TVR200] = {
+               .name         = "Gadmei TVR200",
+               .tuner_type   = TUNER_LG_PAL_NEW_TAPC,
+               .tda9887_conf = TDA9887_PRESENT,
+               .decoder      = EM28XX_SAA711X,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = SAA7115_COMPOSITE2,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = SAA7115_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = SAA7115_SVIDEO3,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2820_BOARD_TERRATEC_CINERGY_250] = {
+               .name         = "Terratec Cinergy 250 USB",
+               .tuner_type   = TUNER_LG_PAL_NEW_TAPC,
+               .has_ir_i2c   = 1,
+               .tda9887_conf = TDA9887_PRESENT,
+               .decoder      = EM28XX_SAA711X,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = SAA7115_COMPOSITE2,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = SAA7115_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = SAA7115_SVIDEO3,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2820_BOARD_PINNACLE_USB_2] = {
+               .name         = "Pinnacle PCTV USB 2",
+               .tuner_type   = TUNER_LG_PAL_NEW_TAPC,
+               .has_ir_i2c   = 1,
+               .tda9887_conf = TDA9887_PRESENT,
+               .decoder      = EM28XX_SAA711X,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = SAA7115_COMPOSITE2,
+                       .amux     = EM28XX_AMUX_VIDEO,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = SAA7115_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = SAA7115_SVIDEO3,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2820_BOARD_HAUPPAUGE_WINTV_USB_2] = {
+               .name         = "Hauppauge WinTV USB 2",
+               .tuner_type   = TUNER_PHILIPS_FM1236_MK3,
+               .tda9887_conf = TDA9887_PRESENT |
+                               TDA9887_PORT1_ACTIVE |
+                               TDA9887_PORT2_ACTIVE,
+               .decoder      = EM28XX_TVP5150,
+               .has_msp34xx  = 1,
+               .has_ir_i2c   = 1,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = MSP_INPUT_DEFAULT,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1,
+                                       MSP_DSP_IN_SCART, MSP_DSP_IN_SCART),
+               } },
+       },
+       [EM2820_BOARD_DLINK_USB_TV] = {
+               .name         = "D-Link DUB-T210 TV Tuner",
+               .valid        = EM28XX_BOARD_NOT_VALIDATED,
+               .tuner_type   = TUNER_LG_PAL_NEW_TAPC,
+               .tda9887_conf = TDA9887_PRESENT,
+               .decoder      = EM28XX_SAA711X,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = SAA7115_COMPOSITE2,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = SAA7115_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = SAA7115_SVIDEO3,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2820_BOARD_HERCULES_SMART_TV_USB2] = {
+               .name         = "Hercules Smart TV USB 2.0",
+               .valid        = EM28XX_BOARD_NOT_VALIDATED,
+               .tuner_type   = TUNER_LG_PAL_NEW_TAPC,
+               .tda9887_conf = TDA9887_PRESENT,
+               .decoder      = EM28XX_SAA711X,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = SAA7115_COMPOSITE2,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = SAA7115_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = SAA7115_SVIDEO3,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2820_BOARD_PINNACLE_USB_2_FM1216ME] = {
+               .name         = "Pinnacle PCTV USB 2 (Philips FM1216ME)",
+               .valid        = EM28XX_BOARD_NOT_VALIDATED,
+               .tuner_type   = TUNER_PHILIPS_FM1216ME_MK3,
+               .tda9887_conf = TDA9887_PRESENT,
+               .decoder      = EM28XX_SAA711X,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = SAA7115_COMPOSITE2,
+                       .amux     = EM28XX_AMUX_VIDEO,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = SAA7115_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = SAA7115_SVIDEO3,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2820_BOARD_GADMEI_UTV310] = {
+               .name         = "Gadmei UTV310",
+               .valid        = EM28XX_BOARD_NOT_VALIDATED,
+               .tuner_type   = TUNER_TNF_5335MF,
+               .tda9887_conf = TDA9887_PRESENT,
+               .decoder      = EM28XX_SAA711X,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = SAA7115_COMPOSITE1,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = SAA7115_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = SAA7115_SVIDEO3,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE] = {
+               .name         = "Leadtek Winfast USB II Deluxe",
+               .valid        = EM28XX_BOARD_NOT_VALIDATED,
+               .tuner_type   = TUNER_PHILIPS_FM1216ME_MK3,
+               .has_ir_i2c   = 1,
+               .tvaudio_addr = 0x58,
+               .tda9887_conf = TDA9887_PRESENT |
+                               TDA9887_PORT2_ACTIVE |
+                               TDA9887_QSS,
+               .decoder      = EM28XX_SAA711X,
+               .adecoder     = EM28XX_TVAUDIO,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = SAA7115_COMPOSITE4,
+                       .amux     = EM28XX_AMUX_AUX,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = SAA7115_COMPOSITE5,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = SAA7115_SVIDEO3,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+                       .radio    = {
+                       .type     = EM28XX_RADIO,
+                       .amux     = EM28XX_AMUX_AUX,
+                       }
+       },
+       [EM2820_BOARD_VIDEOLOGY_20K14XUSB] = {
+               .name         = "Videology 20K14XUSB USB2.0",
+               .valid        = EM28XX_BOARD_NOT_VALIDATED,
+               .tuner_type   = TUNER_ABSENT,
+               .is_webcam    = 1,
+               .input        = { {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = 0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+               } },
+       },
+       [EM2820_BOARD_SILVERCREST_WEBCAM] = {
+               .name         = "Silvercrest Webcam 1.3mpix",
+               .tuner_type   = TUNER_ABSENT,
+               .is_webcam    = 1,
+               .input        = { {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = 0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+                       .gpio     = silvercrest_reg_seq,
+               } },
+       },
+       [EM2821_BOARD_SUPERCOMP_USB_2] = {
+               .name         = "Supercomp USB 2.0 TV",
+               .valid        = EM28XX_BOARD_NOT_VALIDATED,
+               .tuner_type   = TUNER_PHILIPS_FM1236_MK3,
+               .tda9887_conf = TDA9887_PRESENT |
+                               TDA9887_PORT1_ACTIVE |
+                               TDA9887_PORT2_ACTIVE,
+               .decoder      = EM28XX_SAA711X,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = SAA7115_COMPOSITE2,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = SAA7115_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = SAA7115_SVIDEO3,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2821_BOARD_USBGEAR_VD204] = {
+               .name         = "Usbgear VD204v9",
+               .valid        = EM28XX_BOARD_NOT_VALIDATED,
+               .tuner_type   = TUNER_ABSENT,   /* Capture only device */
+               .decoder      = EM28XX_SAA711X,
+               .input        = { {
+                       .type  = EM28XX_VMUX_COMPOSITE1,
+                       .vmux  = SAA7115_COMPOSITE0,
+                       .amux  = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type  = EM28XX_VMUX_SVIDEO,
+                       .vmux  = SAA7115_SVIDEO3,
+                       .amux  = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2860_BOARD_NETGMBH_CAM] = {
+               /* Beijing Huaqi Information Digital Technology Co., Ltd */
+               .name         = "NetGMBH Cam",
+               .valid        = EM28XX_BOARD_NOT_VALIDATED,
+               .tuner_type   = TUNER_ABSENT,
+               .is_webcam    = 1,
+               .input        = { {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = 0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+               } },
+       },
+       [EM2860_BOARD_TYPHOON_DVD_MAKER] = {
+               .name         = "Typhoon DVD Maker",
+               .decoder      = EM28XX_SAA711X,
+               .tuner_type   = TUNER_ABSENT,   /* Capture only device */
+               .input        = { {
+                       .type  = EM28XX_VMUX_COMPOSITE1,
+                       .vmux  = SAA7115_COMPOSITE0,
+                       .amux  = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type  = EM28XX_VMUX_SVIDEO,
+                       .vmux  = SAA7115_SVIDEO3,
+                       .amux  = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2860_BOARD_GADMEI_UTV330] = {
+               .name         = "Gadmei UTV330",
+               .valid        = EM28XX_BOARD_NOT_VALIDATED,
+               .tuner_type   = TUNER_TNF_5335MF,
+               .tda9887_conf = TDA9887_PRESENT,
+               .decoder      = EM28XX_SAA711X,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = SAA7115_COMPOSITE2,
+                       .amux     = EM28XX_AMUX_VIDEO,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = SAA7115_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = SAA7115_SVIDEO3,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2861_BOARD_GADMEI_UTV330PLUS] = {
+               .name         = "Gadmei UTV330+",
+               .tuner_type   = TUNER_TNF_5335MF,
+               .tda9887_conf = TDA9887_PRESENT,
+               .ir_codes     = RC_MAP_GADMEI_RM008Z,
+               .decoder      = EM28XX_SAA711X,
+               .xclk         = EM28XX_XCLK_FREQUENCY_12MHZ,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = SAA7115_COMPOSITE2,
+                       .amux     = EM28XX_AMUX_VIDEO,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = SAA7115_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = SAA7115_SVIDEO3,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2860_BOARD_TERRATEC_HYBRID_XS] = {
+               .name         = "Terratec Cinergy A Hybrid XS",
+               .valid        = EM28XX_BOARD_NOT_VALIDATED,
+               .tuner_type   = TUNER_XC2028,
+               .tuner_gpio   = default_tuner_gpio,
+               .decoder      = EM28XX_TVP5150,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               } },
+       },
+       [EM2861_BOARD_KWORLD_PVRTV_300U] = {
+               .name         = "KWorld PVRTV 300U",
+               .valid        = EM28XX_BOARD_NOT_VALIDATED,
+               .tuner_type   = TUNER_XC2028,
+               .tuner_gpio   = default_tuner_gpio,
+               .decoder      = EM28XX_TVP5150,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2861_BOARD_YAKUMO_MOVIE_MIXER] = {
+               .name          = "Yakumo MovieMixer",
+               .tuner_type    = TUNER_ABSENT,  /* Capture only device */
+               .decoder       = EM28XX_TVP5150,
+               .input         = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2860_BOARD_TVP5150_REFERENCE_DESIGN] = {
+               .name          = "EM2860/TVP5150 Reference Design",
+               .tuner_type    = TUNER_ABSENT,  /* Capture only device */
+               .decoder       = EM28XX_TVP5150,
+               .input         = { {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2861_BOARD_PLEXTOR_PX_TV100U] = {
+               .name         = "Plextor ConvertX PX-TV100U",
+               .tuner_type   = TUNER_TNF_5335MF,
+               .xclk         = EM28XX_XCLK_I2S_MSB_TIMING |
+                               EM28XX_XCLK_FREQUENCY_12MHZ,
+               .tda9887_conf = TDA9887_PRESENT,
+               .decoder      = EM28XX_TVP5150,
+               .has_msp34xx  = 1,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = pinnacle_hybrid_pro_analog,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = pinnacle_hybrid_pro_analog,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = pinnacle_hybrid_pro_analog,
+               } },
+       },
+       /* Those boards with em2870 are DVB Only*/
+       [EM2870_BOARD_TERRATEC_XS] = {
+               .name         = "Terratec Cinergy T XS",
+               .valid        = EM28XX_BOARD_NOT_VALIDATED,
+               .tuner_type   = TUNER_XC2028,
+               .tuner_gpio   = default_tuner_gpio,
+       },
+       [EM2870_BOARD_TERRATEC_XS_MT2060] = {
+               .name         = "Terratec Cinergy T XS (MT2060)",
+               .valid        = EM28XX_BOARD_NOT_VALIDATED,
+               .tuner_type   = TUNER_ABSENT, /* MT2060 */
+       },
+       [EM2870_BOARD_KWORLD_350U] = {
+               .name         = "Kworld 350 U DVB-T",
+               .valid        = EM28XX_BOARD_NOT_VALIDATED,
+               .tuner_type   = TUNER_XC2028,
+               .tuner_gpio   = default_tuner_gpio,
+       },
+       [EM2870_BOARD_KWORLD_355U] = {
+               .name         = "Kworld 355 U DVB-T",
+               .valid        = EM28XX_BOARD_NOT_VALIDATED,
+               .tuner_type   = TUNER_ABSENT,
+               .tuner_gpio   = default_tuner_gpio,
+               .has_dvb      = 1,
+               .dvb_gpio     = default_digital,
+       },
+       [EM2870_BOARD_PINNACLE_PCTV_DVB] = {
+               .name         = "Pinnacle PCTV DVB-T",
+               .valid        = EM28XX_BOARD_NOT_VALIDATED,
+               .tuner_type   = TUNER_ABSENT, /* MT2060 */
+               /* djh - I have serious doubts this is right... */
+               .xclk         = EM28XX_XCLK_IR_RC5_MODE |
+                               EM28XX_XCLK_FREQUENCY_10MHZ,
+       },
+       [EM2870_BOARD_COMPRO_VIDEOMATE] = {
+               .name         = "Compro, VideoMate U3",
+               .valid        = EM28XX_BOARD_NOT_VALIDATED,
+               .tuner_type   = TUNER_ABSENT, /* MT2060 */
+       },
+       [EM2880_BOARD_TERRATEC_HYBRID_XS_FR] = {
+               .name         = "Terratec Hybrid XS Secam",
+               .has_msp34xx  = 1,
+               .tuner_type   = TUNER_XC2028,
+               .tuner_gpio   = default_tuner_gpio,
+               .decoder      = EM28XX_TVP5150,
+               .has_dvb      = 1,
+               .dvb_gpio     = terratec_cinergy_USB_XS_FR_digital,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+                       .gpio     = terratec_cinergy_USB_XS_FR_analog,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = terratec_cinergy_USB_XS_FR_analog,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = terratec_cinergy_USB_XS_FR_analog,
+               } },
+       },
+       [EM2884_BOARD_TERRATEC_H5] = {
+               .name         = "Terratec Cinergy H5",
+               .has_dvb      = 1,
+ #if 0
+               .tuner_type   = TUNER_PHILIPS_TDA8290,
+               .tuner_addr   = 0x41,
+               .dvb_gpio     = terratec_h5_digital, /* FIXME: probably wrong */
+               .tuner_gpio   = terratec_h5_gpio,
+ #else
+               .tuner_type   = TUNER_ABSENT,
+ #endif
+               .i2c_speed    = EM2874_I2C_SECONDARY_BUS_SELECT |
+                               EM28XX_I2C_CLK_WAIT_ENABLE |
+                               EM28XX_I2C_FREQ_400_KHZ,
+       },
+       [EM2884_BOARD_HAUPPAUGE_WINTV_HVR_930C] = {
+               .name         = "Hauppauge WinTV HVR 930C",
+               .has_dvb      = 1,
+ #if 0 /* FIXME: Add analog support */
+               .tuner_type   = TUNER_XC5000,
+               .tuner_addr   = 0x41,
+               .dvb_gpio     = hauppauge_930c_digital,
+               .tuner_gpio   = hauppauge_930c_gpio,
+ #else
+               .tuner_type   = TUNER_ABSENT,
+ #endif
+               .ir_codes     = RC_MAP_HAUPPAUGE,
+               .i2c_speed    = EM2874_I2C_SECONDARY_BUS_SELECT |
+                               EM28XX_I2C_CLK_WAIT_ENABLE |
+                               EM28XX_I2C_FREQ_400_KHZ,
+       },
+       [EM2884_BOARD_CINERGY_HTC_STICK] = {
+               .name         = "Terratec Cinergy HTC Stick",
+               .has_dvb      = 1,
+               .ir_codes     = RC_MAP_NEC_TERRATEC_CINERGY_XS,
+               .tuner_type   = TUNER_ABSENT,
+               .i2c_speed    = EM2874_I2C_SECONDARY_BUS_SELECT |
+                               EM28XX_I2C_CLK_WAIT_ENABLE |
+                               EM28XX_I2C_FREQ_400_KHZ,
+       },
+       [EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900] = {
+               .name         = "Hauppauge WinTV HVR 900",
+               .tda9887_conf = TDA9887_PRESENT,
+               .tuner_type   = TUNER_XC2028,
+               .tuner_gpio   = default_tuner_gpio,
+               .mts_firmware = 1,
+               .has_dvb      = 1,
+               .dvb_gpio     = hauppauge_wintv_hvr_900_digital,
+               .ir_codes     = RC_MAP_HAUPPAUGE,
+               .decoder      = EM28XX_TVP5150,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               } },
+       },
+       [EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2] = {
+               .name         = "Hauppauge WinTV HVR 900 (R2)",
+               .tda9887_conf = TDA9887_PRESENT,
+               .tuner_type   = TUNER_XC2028,
+               .tuner_gpio   = default_tuner_gpio,
+               .mts_firmware = 1,
+               .has_dvb      = 1,
+               .dvb_gpio     = hauppauge_wintv_hvr_900R2_digital,
+               .ir_codes     = RC_MAP_HAUPPAUGE,
+               .decoder      = EM28XX_TVP5150,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               } },
+       },
+       [EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850] = {
+               .name           = "Hauppauge WinTV HVR 850",
+               .tuner_type     = TUNER_XC2028,
+               .tuner_gpio     = default_tuner_gpio,
+               .mts_firmware   = 1,
+               .has_dvb        = 1,
+               .dvb_gpio       = hauppauge_wintv_hvr_900_digital,
+               .ir_codes       = RC_MAP_HAUPPAUGE,
+               .decoder        = EM28XX_TVP5150,
+               .input          = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               } },
+       },
+       [EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950] = {
+               .name           = "Hauppauge WinTV HVR 950",
+               .tuner_type     = TUNER_XC2028,
+               .tuner_gpio     = default_tuner_gpio,
+               .mts_firmware   = 1,
+               .has_dvb        = 1,
+               .dvb_gpio       = hauppauge_wintv_hvr_900_digital,
+               .ir_codes       = RC_MAP_HAUPPAUGE,
+               .decoder        = EM28XX_TVP5150,
+               .input          = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               } },
+       },
+       [EM2880_BOARD_PINNACLE_PCTV_HD_PRO] = {
+               .name           = "Pinnacle PCTV HD Pro Stick",
+               .tuner_type     = TUNER_XC2028,
+               .tuner_gpio   = default_tuner_gpio,
+               .mts_firmware   = 1,
+               .has_dvb        = 1,
+               .dvb_gpio       = hauppauge_wintv_hvr_900_digital,
+               .ir_codes       = RC_MAP_PINNACLE_PCTV_HD,
+               .decoder        = EM28XX_TVP5150,
+               .input          = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               } },
+       },
+       [EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600] = {
+               .name           = "AMD ATI TV Wonder HD 600",
+               .tuner_type     = TUNER_XC2028,
+               .tuner_gpio     = default_tuner_gpio,
+               .mts_firmware   = 1,
+               .has_dvb        = 1,
+               .dvb_gpio       = hauppauge_wintv_hvr_900_digital,
+               .ir_codes       = RC_MAP_ATI_TV_WONDER_HD_600,
+               .decoder        = EM28XX_TVP5150,
+               .input          = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               } },
+       },
+       [EM2880_BOARD_TERRATEC_HYBRID_XS] = {
+               .name           = "Terratec Hybrid XS",
+               .tuner_type     = TUNER_XC2028,
+               .tuner_gpio     = default_tuner_gpio,
+               .decoder        = EM28XX_TVP5150,
+               .has_dvb        = 1,
+               .dvb_gpio       = default_digital,
+               .ir_codes       = RC_MAP_TERRATEC_CINERGY_XS,
+               .xclk           = EM28XX_XCLK_FREQUENCY_12MHZ, /* NEC IR */
+               .input          = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+                       .gpio     = default_analog,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = default_analog,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = default_analog,
+               } },
+       },
+       /* maybe there's a reason behind it why Terratec sells the Hybrid XS
+          as Prodigy XS with a different PID, let's keep it separated for now
+          maybe we'll need it lateron */
+       [EM2880_BOARD_TERRATEC_PRODIGY_XS] = {
+               .name         = "Terratec Prodigy XS",
+               .tuner_type   = TUNER_XC2028,
+               .tuner_gpio   = default_tuner_gpio,
+               .decoder      = EM28XX_TVP5150,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               } },
+       },
+       [EM2820_BOARD_MSI_VOX_USB_2] = {
+               .name              = "MSI VOX USB 2.0",
+               .tuner_type        = TUNER_LG_PAL_NEW_TAPC,
+               .tda9887_conf      = TDA9887_PRESENT      |
+                                    TDA9887_PORT1_ACTIVE |
+                                    TDA9887_PORT2_ACTIVE,
+               .max_range_640_480 = 1,
+               .decoder           = EM28XX_SAA711X,
+               .input             = { {
+                       .type      = EM28XX_VMUX_TELEVISION,
+                       .vmux      = SAA7115_COMPOSITE4,
+                       .amux      = EM28XX_AMUX_VIDEO,
+               }, {
+                       .type      = EM28XX_VMUX_COMPOSITE1,
+                       .vmux      = SAA7115_COMPOSITE0,
+                       .amux      = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type      = EM28XX_VMUX_SVIDEO,
+                       .vmux      = SAA7115_SVIDEO3,
+                       .amux      = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2800_BOARD_TERRATEC_CINERGY_200] = {
+               .name         = "Terratec Cinergy 200 USB",
+               .is_em2800    = 1,
+               .has_ir_i2c   = 1,
+               .tuner_type   = TUNER_LG_TALN,
+               .tda9887_conf = TDA9887_PRESENT,
+               .decoder      = EM28XX_SAA711X,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = SAA7115_COMPOSITE2,
+                       .amux     = EM28XX_AMUX_VIDEO,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = SAA7115_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = SAA7115_SVIDEO3,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2800_BOARD_GRABBEEX_USB2800] = {
+               .name       = "eMPIA Technology, Inc. GrabBeeX+ Video Encoder",
+               .is_em2800  = 1,
+               .decoder    = EM28XX_SAA711X,
+               .tuner_type = TUNER_ABSENT, /* capture only board */
+               .input      = { {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = SAA7115_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = SAA7115_SVIDEO3,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2800_BOARD_VC211A] = {
+               .name         = "Actionmaster/LinXcel/Digitus VC211A",
+               .is_em2800    = 1,
+               .tuner_type   = TUNER_ABSENT,   /* Capture-only board */
+               .decoder      = EM28XX_SAA711X,
+               .input        = { {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = SAA7115_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = vc211a_enable,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = SAA7115_SVIDEO3,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = vc211a_enable,
+               } },
+       },
+       [EM2800_BOARD_LEADTEK_WINFAST_USBII] = {
+               .name         = "Leadtek Winfast USB II",
+               .is_em2800    = 1,
+               .tuner_type   = TUNER_LG_PAL_NEW_TAPC,
+               .tda9887_conf = TDA9887_PRESENT,
+               .decoder      = EM28XX_SAA711X,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = SAA7115_COMPOSITE2,
+                       .amux     = EM28XX_AMUX_VIDEO,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = SAA7115_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = SAA7115_SVIDEO3,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2800_BOARD_KWORLD_USB2800] = {
+               .name         = "Kworld USB2800",
+               .is_em2800    = 1,
+               .tuner_type   = TUNER_PHILIPS_FCV1236D,
+               .tda9887_conf = TDA9887_PRESENT,
+               .decoder      = EM28XX_SAA711X,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = SAA7115_COMPOSITE2,
+                       .amux     = EM28XX_AMUX_VIDEO,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = SAA7115_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = SAA7115_SVIDEO3,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2820_BOARD_PINNACLE_DVC_90] = {
+               .name         = "Pinnacle Dazzle DVC 90/100/101/107 / Kaiser Baas Video to DVD maker "
+                              "/ Kworld DVD Maker 2 / Plextor ConvertX PX-AV100U",
+               .tuner_type   = TUNER_ABSENT, /* capture only board */
+               .decoder      = EM28XX_SAA711X,
+               .input        = { {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = SAA7115_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = SAA7115_SVIDEO3,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2800_BOARD_VGEAR_POCKETTV] = {
+               .name         = "V-Gear PocketTV",
+               .is_em2800    = 1,
+               .tuner_type   = TUNER_LG_PAL_NEW_TAPC,
+               .tda9887_conf = TDA9887_PRESENT,
+               .decoder      = EM28XX_SAA711X,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = SAA7115_COMPOSITE2,
+                       .amux     = EM28XX_AMUX_VIDEO,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = SAA7115_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = SAA7115_SVIDEO3,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2820_BOARD_PROLINK_PLAYTV_BOX4_USB2] = {
+               .name         = "Pixelview PlayTV Box 4 USB 2.0",
+               .tda9887_conf = TDA9887_PRESENT,
+               .tuner_type   = TUNER_YMEC_TVF_5533MF,
+               .decoder      = EM28XX_SAA711X,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = SAA7115_COMPOSITE2,
+                       .amux     = EM28XX_AMUX_VIDEO,
+                       .aout     = EM28XX_AOUT_MONO |  /* I2S */
+                                   EM28XX_AOUT_MASTER, /* Line out pin */
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = SAA7115_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = SAA7115_SVIDEO3,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2820_BOARD_PROLINK_PLAYTV_USB2] = {
+               .name         = "SIIG AVTuner-PVR / Pixelview Prolink PlayTV USB 2.0",
+               .has_snapshot_button = 1,
+               .tda9887_conf = TDA9887_PRESENT,
+               .tuner_type   = TUNER_YMEC_TVF_5533MF,
+               .decoder      = EM28XX_SAA711X,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = SAA7115_COMPOSITE2,
+                       .amux     = EM28XX_AMUX_VIDEO,
+                       .aout     = EM28XX_AOUT_MONO |  /* I2S */
+                                   EM28XX_AOUT_MASTER, /* Line out pin */
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = SAA7115_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = SAA7115_SVIDEO3,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2860_BOARD_SAA711X_REFERENCE_DESIGN] = {
+               .name                = "EM2860/SAA711X Reference Design",
+               .has_snapshot_button = 1,
+               .tuner_type          = TUNER_ABSENT,
+               .decoder             = EM28XX_SAA711X,
+               .input               = { {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = SAA7115_SVIDEO3,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = SAA7115_COMPOSITE0,
+               } },
+       },
+       [EM2874_BOARD_LEADERSHIP_ISDBT] = {
+               .i2c_speed      = EM2874_I2C_SECONDARY_BUS_SELECT |
+                                 EM28XX_I2C_CLK_WAIT_ENABLE |
+                                 EM28XX_I2C_FREQ_100_KHZ,
+               .xclk           = EM28XX_XCLK_FREQUENCY_10MHZ,
+               .name           = "EM2874 Leadership ISDBT",
+               .tuner_type     = TUNER_ABSENT,
+               .tuner_gpio     = leadership_reset,
+               .dvb_gpio       = leadership_digital,
+               .has_dvb        = 1,
+       },
+       [EM2880_BOARD_MSI_DIGIVOX_AD] = {
+               .name         = "MSI DigiVox A/D",
+               .valid        = EM28XX_BOARD_NOT_VALIDATED,
+               .tuner_type   = TUNER_XC2028,
+               .tuner_gpio   = default_tuner_gpio,
+               .decoder      = EM28XX_TVP5150,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+                       .gpio     = em2880_msi_digivox_ad_analog,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = em2880_msi_digivox_ad_analog,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = em2880_msi_digivox_ad_analog,
+               } },
+       },
+       [EM2880_BOARD_MSI_DIGIVOX_AD_II] = {
+               .name         = "MSI DigiVox A/D II",
+               .valid        = EM28XX_BOARD_NOT_VALIDATED,
+               .tuner_type   = TUNER_XC2028,
+               .tuner_gpio   = default_tuner_gpio,
+               .decoder      = EM28XX_TVP5150,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+                       .gpio     = em2880_msi_digivox_ad_analog,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = em2880_msi_digivox_ad_analog,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = em2880_msi_digivox_ad_analog,
+               } },
+       },
+       [EM2880_BOARD_KWORLD_DVB_305U] = {
+               .name         = "KWorld DVB-T 305U",
+               .tuner_type   = TUNER_XC2028,
+               .tuner_gpio   = default_tuner_gpio,
+               .decoder      = EM28XX_TVP5150,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2880_BOARD_KWORLD_DVB_310U] = {
+               .name         = "KWorld DVB-T 310U",
+               .tuner_type   = TUNER_XC2028,
+               .tuner_gpio   = default_tuner_gpio,
+               .has_dvb      = 1,
+               .dvb_gpio     = default_digital,
+               .mts_firmware = 1,
+               .decoder      = EM28XX_TVP5150,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+                       .gpio     = default_analog,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = default_analog,
+               }, {    /* S-video has not been tested yet */
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = default_analog,
+               } },
+       },
+       [EM2882_BOARD_KWORLD_ATSC_315U] = {
+               .name           = "KWorld ATSC 315U HDTV TV Box",
+               .valid          = EM28XX_BOARD_NOT_VALIDATED,
+               .tuner_type     = TUNER_THOMSON_DTT761X,
+               .tuner_gpio     = em2882_kworld_315u_tuner_gpio,
+               .tda9887_conf   = TDA9887_PRESENT,
+               .decoder        = EM28XX_SAA711X,
+               .has_dvb        = 1,
+               .dvb_gpio       = em2882_kworld_315u_digital,
+               .ir_codes       = RC_MAP_KWORLD_315U,
+               .xclk           = EM28XX_XCLK_FREQUENCY_12MHZ,
+               .i2c_speed      = EM28XX_I2C_CLK_WAIT_ENABLE,
+               /* Analog mode - still not ready */
+               /*.input        = { {
+                       .type = EM28XX_VMUX_TELEVISION,
+                       .vmux = SAA7115_COMPOSITE2,
+                       .amux = EM28XX_AMUX_VIDEO,
+                       .gpio = em2882_kworld_315u_analog,
+                       .aout = EM28XX_AOUT_PCM_IN | EM28XX_AOUT_PCM_STEREO,
+               }, {
+                       .type = EM28XX_VMUX_COMPOSITE1,
+                       .vmux = SAA7115_COMPOSITE0,
+                       .amux = EM28XX_AMUX_LINE_IN,
+                       .gpio = em2882_kworld_315u_analog1,
+                       .aout = EM28XX_AOUT_PCM_IN | EM28XX_AOUT_PCM_STEREO,
+               }, {
+                       .type = EM28XX_VMUX_SVIDEO,
+                       .vmux = SAA7115_SVIDEO3,
+                       .amux = EM28XX_AMUX_LINE_IN,
+                       .gpio = em2882_kworld_315u_analog1,
+                       .aout = EM28XX_AOUT_PCM_IN | EM28XX_AOUT_PCM_STEREO,
+               } }, */
+       },
+       [EM2880_BOARD_EMPIRE_DUAL_TV] = {
+               .name = "Empire dual TV",
+               .tuner_type = TUNER_XC2028,
+               .tuner_gpio = default_tuner_gpio,
+               .has_dvb = 1,
+               .dvb_gpio = default_digital,
+               .mts_firmware = 1,
+               .decoder = EM28XX_TVP5150,
+               .input = { {
+                       .type = EM28XX_VMUX_TELEVISION,
+                       .vmux = TVP5150_COMPOSITE0,
+                       .amux = EM28XX_AMUX_VIDEO,
+                       .gpio = default_analog,
+               }, {
+                       .type = EM28XX_VMUX_COMPOSITE1,
+                       .vmux = TVP5150_COMPOSITE1,
+                       .amux = EM28XX_AMUX_LINE_IN,
+                       .gpio = default_analog,
+               }, {
+                       .type = EM28XX_VMUX_SVIDEO,
+                       .vmux = TVP5150_SVIDEO,
+                       .amux = EM28XX_AMUX_LINE_IN,
+                       .gpio = default_analog,
+               } },
+       },
+       [EM2881_BOARD_DNT_DA2_HYBRID] = {
+               .name         = "DNT DA2 Hybrid",
+               .valid        = EM28XX_BOARD_NOT_VALIDATED,
+               .tuner_type   = TUNER_XC2028,
+               .tuner_gpio   = default_tuner_gpio,
+               .decoder      = EM28XX_TVP5150,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+                       .gpio     = default_analog,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = default_analog,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = default_analog,
+               } },
+       },
+       [EM2881_BOARD_PINNACLE_HYBRID_PRO] = {
+               .name         = "Pinnacle Hybrid Pro",
+               .tuner_type   = TUNER_XC2028,
+               .tuner_gpio   = default_tuner_gpio,
+               .decoder      = EM28XX_TVP5150,
+               .has_dvb      = 1,
+               .dvb_gpio     = pinnacle_hybrid_pro_digital,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+                       .gpio     = pinnacle_hybrid_pro_analog,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = pinnacle_hybrid_pro_analog,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = pinnacle_hybrid_pro_analog,
+               } },
+       },
+       [EM2882_BOARD_PINNACLE_HYBRID_PRO_330E] = {
+               .name         = "Pinnacle Hybrid Pro (330e)",
+               .tuner_type   = TUNER_XC2028,
+               .tuner_gpio   = default_tuner_gpio,
+               .mts_firmware = 1,
+               .has_dvb      = 1,
+               .dvb_gpio     = hauppauge_wintv_hvr_900R2_digital,
+               .ir_codes     = RC_MAP_PINNACLE_PCTV_HD,
+               .decoder      = EM28XX_TVP5150,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               } },
+       },
+       [EM2882_BOARD_KWORLD_VS_DVBT] = {
+               .name         = "Kworld VS-DVB-T 323UR",
+               .tuner_type   = TUNER_XC2028,
+               .tuner_gpio   = default_tuner_gpio,
+               .decoder      = EM28XX_TVP5150,
+               .mts_firmware = 1,
+               .has_dvb      = 1,
+               .dvb_gpio     = kworld_330u_digital,
+               .xclk         = EM28XX_XCLK_FREQUENCY_12MHZ, /* NEC IR */
+               .ir_codes     = RC_MAP_KWORLD_315U,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2882_BOARD_TERRATEC_HYBRID_XS] = {
+               .name         = "Terratec Cinnergy Hybrid T USB XS (em2882)",
+               .tuner_type   = TUNER_XC2028,
+               .tuner_gpio   = default_tuner_gpio,
+               .mts_firmware = 1,
+               .decoder      = EM28XX_TVP5150,
+               .has_dvb      = 1,
+               .dvb_gpio     = hauppauge_wintv_hvr_900_digital,
+               .ir_codes     = RC_MAP_TERRATEC_CINERGY_XS,
+               .xclk         = EM28XX_XCLK_FREQUENCY_12MHZ,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               } },
+       },
+       [EM2882_BOARD_DIKOM_DK300] = {
+               .name         = "Dikom DK300",
+               .tuner_type   = TUNER_XC2028,
+               .tuner_gpio   = default_tuner_gpio,
+               .decoder      = EM28XX_TVP5150,
+               .mts_firmware = 1,
+               .has_dvb      = 1,
+               .dvb_gpio     = dikom_dk300_digital,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+                       .gpio     = default_analog,
+               } },
+       },
+       [EM2883_BOARD_KWORLD_HYBRID_330U] = {
+               .name         = "Kworld PlusTV HD Hybrid 330",
+               .tuner_type   = TUNER_XC2028,
+               .tuner_gpio   = default_tuner_gpio,
+               .decoder      = EM28XX_TVP5150,
+               .mts_firmware = 1,
+               .has_dvb      = 1,
+               .dvb_gpio     = kworld_330u_digital,
+               .xclk             = EM28XX_XCLK_FREQUENCY_12MHZ,
+               .i2c_speed        = EM28XX_I2C_CLK_WAIT_ENABLE |
+                                   EM28XX_I2C_EEPROM_ON_BOARD |
+                                   EM28XX_I2C_EEPROM_KEY_VALID,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+                       .gpio     = kworld_330u_analog,
+                       .aout     = EM28XX_AOUT_PCM_IN | EM28XX_AOUT_PCM_STEREO,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = kworld_330u_analog,
+                       .aout     = EM28XX_AOUT_PCM_IN | EM28XX_AOUT_PCM_STEREO,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = kworld_330u_analog,
+               } },
+       },
+       [EM2820_BOARD_COMPRO_VIDEOMATE_FORYOU] = {
+               .name         = "Compro VideoMate ForYou/Stereo",
+               .tuner_type   = TUNER_LG_PAL_NEW_TAPC,
+               .tvaudio_addr = 0xb0,
+               .tda9887_conf = TDA9887_PRESENT,
+               .decoder      = EM28XX_TVP5150,
+               .adecoder     = EM28XX_TVAUDIO,
+               .mute_gpio    = compro_mute_gpio,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+                       .gpio     = compro_unmute_tv_gpio,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = compro_unmute_svid_gpio,
+               } },
+       },
+       [EM2860_BOARD_KAIOMY_TVNPC_U2] = {
+               .name         = "Kaiomy TVnPC U2",
+               .vchannels    = 3,
+               .tuner_type   = TUNER_XC2028,
+               .tuner_addr   = 0x61,
+               .mts_firmware = 1,
+               .decoder      = EM28XX_TVP5150,
+               .tuner_gpio   = default_tuner_gpio,
+               .ir_codes     = RC_MAP_KAIOMY,
+               .input          = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+               .radio          = {
+                       .type     = EM28XX_RADIO,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }
+       },
+       [EM2860_BOARD_EASYCAP] = {
+               .name         = "Easy Cap Capture DC-60",
+               .vchannels    = 2,
+               .tuner_type   = TUNER_ABSENT,
+               .decoder      = EM28XX_SAA711X,
+               .input           = { {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = SAA7115_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = SAA7115_SVIDEO3,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2820_BOARD_IODATA_GVMVP_SZ] = {
+               .name       = "IO-DATA GV-MVP/SZ",
+               .tuner_type   = TUNER_PHILIPS_FM1236_MK3,
+               .tuner_gpio   = default_tuner_gpio,
+               .tda9887_conf = TDA9887_PRESENT,
+               .decoder      = EM28XX_TVP5150,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+               }, { /* Composite has not been tested yet */
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = EM28XX_AMUX_VIDEO,
+               }, { /* S-video has not been tested yet */
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AMUX_VIDEO,
+               } },
+       },
+       [EM2860_BOARD_TERRATEC_GRABBY] = {
+               .name            = "Terratec Grabby",
+               .vchannels       = 2,
+               .tuner_type      = TUNER_ABSENT,
+               .decoder         = EM28XX_SAA711X,
+               .xclk            = EM28XX_XCLK_FREQUENCY_12MHZ,
+               .input           = { {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = SAA7115_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = SAA7115_SVIDEO3,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2860_BOARD_TERRATEC_AV350] = {
+               .name            = "Terratec AV350",
+               .vchannels       = 2,
+               .tuner_type      = TUNER_ABSENT,
+               .decoder         = EM28XX_TVP5150,
+               .xclk            = EM28XX_XCLK_FREQUENCY_12MHZ,
+               .mute_gpio       = terratec_av350_mute_gpio,
+               .input           = { {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = EM28XX_AUDIO_SRC_LINE,
+                       .gpio     = terratec_av350_unmute_gpio,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AUDIO_SRC_LINE,
+                       .gpio     = terratec_av350_unmute_gpio,
+               } },
+       },
+       [EM2860_BOARD_ELGATO_VIDEO_CAPTURE] = {
+               .name         = "Elgato Video Capture",
+               .decoder      = EM28XX_SAA711X,
+               .tuner_type   = TUNER_ABSENT,   /* Capture only device */
+               .input        = { {
+                       .type  = EM28XX_VMUX_COMPOSITE1,
+                       .vmux  = SAA7115_COMPOSITE0,
+                       .amux  = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type  = EM28XX_VMUX_SVIDEO,
+                       .vmux  = SAA7115_SVIDEO3,
+                       .amux  = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2882_BOARD_EVGA_INDTUBE] = {
+               .name         = "Evga inDtube",
+               .tuner_type   = TUNER_XC2028,
+               .tuner_gpio   = default_tuner_gpio,
+               .decoder      = EM28XX_TVP5150,
+               .xclk         = EM28XX_XCLK_FREQUENCY_12MHZ, /* NEC IR */
+               .mts_firmware = 1,
+               .has_dvb      = 1,
+               .dvb_gpio     = evga_indtube_digital,
+               .ir_codes     = RC_MAP_EVGA_INDTUBE,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+                       .gpio     = evga_indtube_analog,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = evga_indtube_analog,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = evga_indtube_analog,
+               } },
+       },
+       /* eb1a:2868 Empia EM2870 + Philips CU1216L NIM (Philips TDA10023 +
+          Infineon TUA6034) */
+       [EM2870_BOARD_REDDO_DVB_C_USB_BOX] = {
+               .name          = "Reddo DVB-C USB TV Box",
+               .tuner_type    = TUNER_ABSENT,
+               .tuner_gpio    = reddo_dvb_c_usb_box,
+               .has_dvb       = 1,
+       },
+       /* 1b80:a340 - Empia EM2870, NXP TDA18271HD and LG DT3304, sold
+        * initially as the KWorld PlusTV 340U, then as the UB435-Q.
+        * Early variants have a TDA18271HD/C1, later ones a TDA18271HD/C2 */
+       [EM2870_BOARD_KWORLD_A340] = {
+               .name       = "KWorld PlusTV 340U or UB435-Q (ATSC)",
+               .tuner_type = TUNER_ABSENT,     /* Digital-only TDA18271HD */
+               .has_dvb    = 1,
+               .dvb_gpio   = kworld_a340_digital,
+               .tuner_gpio = default_tuner_gpio,
+       },
+       /* 2013:024f PCTV nanoStick T2 290e.
+        * Empia EM28174, Sony CXD2820R and NXP TDA18271HD/C2 */
+       [EM28174_BOARD_PCTV_290E] = {
+               .name          = "PCTV nanoStick T2 290e",
+               .i2c_speed      = EM2874_I2C_SECONDARY_BUS_SELECT |
+                       EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_100_KHZ,
+               .tuner_type    = TUNER_ABSENT,
+               .tuner_gpio    = pctv_290e,
+               .has_dvb       = 1,
+               .ir_codes      = RC_MAP_PINNACLE_PCTV_HD,
+       },
+       /* 2013:024f PCTV DVB-S2 Stick 460e
+        * Empia EM28174, NXP TDA10071, Conexant CX24118A and Allegro A8293 */
+       [EM28174_BOARD_PCTV_460E] = {
+               .i2c_speed     = EM2874_I2C_SECONDARY_BUS_SELECT |
+                       EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ,
+               .name          = "PCTV DVB-S2 Stick (460e)",
+               .tuner_type    = TUNER_ABSENT,
+               .tuner_gpio    = pctv_460e,
+               .has_dvb       = 1,
+               .ir_codes      = RC_MAP_PINNACLE_PCTV_HD,
+       },
+       /* eb1a:5006 Honestech VIDBOX NW03
+        * Empia EM2860, Philips SAA7113, Empia EMP202, No Tuner */
+       [EM2860_BOARD_HT_VIDBOX_NW03] = {
+               .name                = "Honestech Vidbox NW03",
+               .tuner_type          = TUNER_ABSENT,
+               .decoder             = EM28XX_SAA711X,
+               .input               = { {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = SAA7115_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = SAA7115_SVIDEO3,  /* S-VIDEO needs confirming */
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       /* 1b80:e425 MaxMedia UB425-TC
+        * Empia EM2874B + Micronas DRX 3913KA2 + NXP TDA18271HDC2 */
+       [EM2874_BOARD_MAXMEDIA_UB425_TC] = {
+               .name          = "MaxMedia UB425-TC",
+               .tuner_type    = TUNER_ABSENT,
+               .tuner_gpio    = maxmedia_ub425_tc,
+               .has_dvb       = 1,
+               .i2c_speed     = EM2874_I2C_SECONDARY_BUS_SELECT |
+                               EM28XX_I2C_CLK_WAIT_ENABLE |
+                               EM28XX_I2C_FREQ_400_KHZ,
+       },
+       /* 2304:0242 PCTV QuatroStick (510e)
+        * Empia EM2884 + Micronas DRX 3926K + NXP TDA18271HDC2 */
+       [EM2884_BOARD_PCTV_510E] = {
+               .name          = "PCTV QuatroStick (510e)",
+               .tuner_type    = TUNER_ABSENT,
+               .tuner_gpio    = pctv_510e,
+               .has_dvb       = 1,
+               .ir_codes      = RC_MAP_PINNACLE_PCTV_HD,
+               .i2c_speed     = EM2874_I2C_SECONDARY_BUS_SELECT |
+                               EM28XX_I2C_CLK_WAIT_ENABLE |
+                               EM28XX_I2C_FREQ_400_KHZ,
+       },
+       /* 2013:0251 PCTV QuatroStick nano (520e)
+        * Empia EM2884 + Micronas DRX 3926K + NXP TDA18271HDC2 */
+       [EM2884_BOARD_PCTV_520E] = {
+               .name          = "PCTV QuatroStick nano (520e)",
+               .tuner_type    = TUNER_ABSENT,
+               .tuner_gpio    = pctv_520e,
+               .has_dvb       = 1,
+               .ir_codes      = RC_MAP_PINNACLE_PCTV_HD,
+               .i2c_speed     = EM2874_I2C_SECONDARY_BUS_SELECT |
+                               EM28XX_I2C_CLK_WAIT_ENABLE |
+                               EM28XX_I2C_FREQ_400_KHZ,
+       },
+ };
+ const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards);
+ /* table of devices that work with this driver */
+ struct usb_device_id em28xx_id_table[] = {
+       { USB_DEVICE(0xeb1a, 0x2750),
+                       .driver_info = EM2750_BOARD_UNKNOWN },
+       { USB_DEVICE(0xeb1a, 0x2751),
+                       .driver_info = EM2750_BOARD_UNKNOWN },
+       { USB_DEVICE(0xeb1a, 0x2800),
+                       .driver_info = EM2800_BOARD_UNKNOWN },
+       { USB_DEVICE(0xeb1a, 0x2710),
+                       .driver_info = EM2820_BOARD_UNKNOWN },
+       { USB_DEVICE(0xeb1a, 0x2820),
+                       .driver_info = EM2820_BOARD_UNKNOWN },
+       { USB_DEVICE(0xeb1a, 0x2821),
+                       .driver_info = EM2820_BOARD_UNKNOWN },
+       { USB_DEVICE(0xeb1a, 0x2860),
+                       .driver_info = EM2820_BOARD_UNKNOWN },
+       { USB_DEVICE(0xeb1a, 0x2861),
+                       .driver_info = EM2820_BOARD_UNKNOWN },
+       { USB_DEVICE(0xeb1a, 0x2862),
+                       .driver_info = EM2820_BOARD_UNKNOWN },
+       { USB_DEVICE(0xeb1a, 0x2863),
+                       .driver_info = EM2820_BOARD_UNKNOWN },
+       { USB_DEVICE(0xeb1a, 0x2870),
+                       .driver_info = EM2820_BOARD_UNKNOWN },
+       { USB_DEVICE(0xeb1a, 0x2881),
+                       .driver_info = EM2820_BOARD_UNKNOWN },
+       { USB_DEVICE(0xeb1a, 0x2883),
+                       .driver_info = EM2820_BOARD_UNKNOWN },
+       { USB_DEVICE(0xeb1a, 0x2868),
+                       .driver_info = EM2820_BOARD_UNKNOWN },
+       { USB_DEVICE(0xeb1a, 0x2875),
+                       .driver_info = EM2820_BOARD_UNKNOWN },
+       { USB_DEVICE(0xeb1a, 0xe300),
+                       .driver_info = EM2861_BOARD_KWORLD_PVRTV_300U },
+       { USB_DEVICE(0xeb1a, 0xe303),
+                       .driver_info = EM2860_BOARD_KAIOMY_TVNPC_U2 },
+       { USB_DEVICE(0xeb1a, 0xe305),
+                       .driver_info = EM2880_BOARD_KWORLD_DVB_305U },
+       { USB_DEVICE(0xeb1a, 0xe310),
+                       .driver_info = EM2880_BOARD_MSI_DIGIVOX_AD },
+       { USB_DEVICE(0xeb1a, 0xa313),
+               .driver_info = EM2882_BOARD_KWORLD_ATSC_315U },
+       { USB_DEVICE(0xeb1a, 0xa316),
+                       .driver_info = EM2883_BOARD_KWORLD_HYBRID_330U },
+       { USB_DEVICE(0xeb1a, 0xe320),
+                       .driver_info = EM2880_BOARD_MSI_DIGIVOX_AD_II },
+       { USB_DEVICE(0xeb1a, 0xe323),
+                       .driver_info = EM2882_BOARD_KWORLD_VS_DVBT },
+       { USB_DEVICE(0xeb1a, 0xe350),
+                       .driver_info = EM2870_BOARD_KWORLD_350U },
+       { USB_DEVICE(0xeb1a, 0xe355),
+                       .driver_info = EM2870_BOARD_KWORLD_355U },
+       { USB_DEVICE(0xeb1a, 0x2801),
+                       .driver_info = EM2800_BOARD_GRABBEEX_USB2800 },
+       { USB_DEVICE(0xeb1a, 0xe357),
+                       .driver_info = EM2870_BOARD_KWORLD_355U },
+       { USB_DEVICE(0xeb1a, 0xe359),
+                       .driver_info = EM2870_BOARD_KWORLD_355U },
+       { USB_DEVICE(0x1b80, 0xe302),
+                       .driver_info = EM2820_BOARD_PINNACLE_DVC_90 }, /* Kaiser Baas Video to DVD maker */
+       { USB_DEVICE(0x1b80, 0xe304),
+                       .driver_info = EM2820_BOARD_PINNACLE_DVC_90 }, /* Kworld DVD Maker 2 */
+       { USB_DEVICE(0x0ccd, 0x0036),
+                       .driver_info = EM2820_BOARD_TERRATEC_CINERGY_250 },
+       { USB_DEVICE(0x0ccd, 0x004c),
+                       .driver_info = EM2880_BOARD_TERRATEC_HYBRID_XS_FR },
+       { USB_DEVICE(0x0ccd, 0x004f),
+                       .driver_info = EM2860_BOARD_TERRATEC_HYBRID_XS },
+       { USB_DEVICE(0x0ccd, 0x005e),
+                       .driver_info = EM2882_BOARD_TERRATEC_HYBRID_XS },
+       { USB_DEVICE(0x0ccd, 0x0042),
+                       .driver_info = EM2882_BOARD_TERRATEC_HYBRID_XS },
+       { USB_DEVICE(0x0ccd, 0x0043),
+                       .driver_info = EM2870_BOARD_TERRATEC_XS },
+       { USB_DEVICE(0x0ccd, 0x008e),   /* Cinergy HTC USB XS Rev. 1 */
+                       .driver_info = EM2884_BOARD_TERRATEC_H5 },
+       { USB_DEVICE(0x0ccd, 0x00ac),   /* Cinergy HTC USB XS Rev. 2 */
+                       .driver_info = EM2884_BOARD_TERRATEC_H5 },
+       { USB_DEVICE(0x0ccd, 0x10a2),   /* H5 Rev. 1 */
+                       .driver_info = EM2884_BOARD_TERRATEC_H5 },
+       { USB_DEVICE(0x0ccd, 0x10ad),   /* H5 Rev. 2 */
+                       .driver_info = EM2884_BOARD_TERRATEC_H5 },
+       { USB_DEVICE(0x0ccd, 0x0084),
+                       .driver_info = EM2860_BOARD_TERRATEC_AV350 },
+       { USB_DEVICE(0x0ccd, 0x0096),
+                       .driver_info = EM2860_BOARD_TERRATEC_GRABBY },
+       { USB_DEVICE(0x0ccd, 0x10AF),
+                       .driver_info = EM2860_BOARD_TERRATEC_GRABBY },
+       { USB_DEVICE(0x0ccd, 0x00b2),
+                       .driver_info = EM2884_BOARD_CINERGY_HTC_STICK },
+       { USB_DEVICE(0x0fd9, 0x0033),
+                       .driver_info = EM2860_BOARD_ELGATO_VIDEO_CAPTURE},
+       { USB_DEVICE(0x185b, 0x2870),
+                       .driver_info = EM2870_BOARD_COMPRO_VIDEOMATE },
+       { USB_DEVICE(0x185b, 0x2041),
+                       .driver_info = EM2820_BOARD_COMPRO_VIDEOMATE_FORYOU },
+       { USB_DEVICE(0x2040, 0x4200),
+                       .driver_info = EM2820_BOARD_HAUPPAUGE_WINTV_USB_2 },
+       { USB_DEVICE(0x2040, 0x4201),
+                       .driver_info = EM2820_BOARD_HAUPPAUGE_WINTV_USB_2 },
+       { USB_DEVICE(0x2040, 0x6500),
+                       .driver_info = EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900 },
+       { USB_DEVICE(0x2040, 0x6502),
+                       .driver_info = EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2 },
+       { USB_DEVICE(0x2040, 0x6513), /* HCW HVR-980 */
+                       .driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950 },
+       { USB_DEVICE(0x2040, 0x6517), /* HP  HVR-950 */
+                       .driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950 },
+       { USB_DEVICE(0x2040, 0x651b), /* RP  HVR-950 */
+                       .driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950 },
+       { USB_DEVICE(0x2040, 0x651f),
+                       .driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850 },
+       { USB_DEVICE(0x0438, 0xb002),
+                       .driver_info = EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600 },
+       { USB_DEVICE(0x2001, 0xf112),
+                       .driver_info = EM2820_BOARD_DLINK_USB_TV },
+       { USB_DEVICE(0x2304, 0x0207),
+                       .driver_info = EM2820_BOARD_PINNACLE_DVC_90 },
+       { USB_DEVICE(0x2304, 0x0208),
+                       .driver_info = EM2820_BOARD_PINNACLE_USB_2 },
+       { USB_DEVICE(0x2304, 0x021a),
+                       .driver_info = EM2820_BOARD_PINNACLE_DVC_90 },
+       { USB_DEVICE(0x2304, 0x0226),
+                       .driver_info = EM2882_BOARD_PINNACLE_HYBRID_PRO_330E },
+       { USB_DEVICE(0x2304, 0x0227),
+                       .driver_info = EM2880_BOARD_PINNACLE_PCTV_HD_PRO },
+       { USB_DEVICE(0x0413, 0x6023),
+                       .driver_info = EM2800_BOARD_LEADTEK_WINFAST_USBII },
+       { USB_DEVICE(0x093b, 0xa003),
+                      .driver_info = EM2820_BOARD_PINNACLE_DVC_90 },
+       { USB_DEVICE(0x093b, 0xa005),
+                       .driver_info = EM2861_BOARD_PLEXTOR_PX_TV100U },
+       { USB_DEVICE(0x04bb, 0x0515),
+                       .driver_info = EM2820_BOARD_IODATA_GVMVP_SZ },
+       { USB_DEVICE(0xeb1a, 0x50a6),
+                       .driver_info = EM2860_BOARD_GADMEI_UTV330 },
+       { USB_DEVICE(0x1b80, 0xa340),
+                       .driver_info = EM2870_BOARD_KWORLD_A340 },
+       { USB_DEVICE(0x2013, 0x024f),
+                       .driver_info = EM28174_BOARD_PCTV_290E },
+       { USB_DEVICE(0x2013, 0x024c),
+                       .driver_info = EM28174_BOARD_PCTV_460E },
+       { USB_DEVICE(0x2040, 0x1605),
+                       .driver_info = EM2884_BOARD_HAUPPAUGE_WINTV_HVR_930C },
+       { USB_DEVICE(0xeb1a, 0x5006),
+                       .driver_info = EM2860_BOARD_HT_VIDBOX_NW03 },
+       { USB_DEVICE(0x1b80, 0xe309), /* Sveon STV40 */
+                       .driver_info = EM2860_BOARD_EASYCAP },
+       { USB_DEVICE(0x1b80, 0xe425),
+                       .driver_info = EM2874_BOARD_MAXMEDIA_UB425_TC },
+       { USB_DEVICE(0x2304, 0x0242),
+                       .driver_info = EM2884_BOARD_PCTV_510E },
+       { USB_DEVICE(0x2013, 0x0251),
+                       .driver_info = EM2884_BOARD_PCTV_520E },
+       { },
+ };
+ MODULE_DEVICE_TABLE(usb, em28xx_id_table);
+ /*
+  * EEPROM hash table for devices with generic USB IDs
+  */
+ static struct em28xx_hash_table em28xx_eeprom_hash[] = {
+       /* P/N: SA 60002070465 Tuner: TVF7533-MF */
+       {0x6ce05a8f, EM2820_BOARD_PROLINK_PLAYTV_USB2, TUNER_YMEC_TVF_5533MF},
+       {0x72cc5a8b, EM2820_BOARD_PROLINK_PLAYTV_BOX4_USB2, TUNER_YMEC_TVF_5533MF},
+       {0x966a0441, EM2880_BOARD_KWORLD_DVB_310U, TUNER_XC2028},
+       {0x166a0441, EM2880_BOARD_EMPIRE_DUAL_TV, TUNER_XC2028},
+       {0xcee44a99, EM2882_BOARD_EVGA_INDTUBE, TUNER_XC2028},
+       {0xb8846b20, EM2881_BOARD_PINNACLE_HYBRID_PRO, TUNER_XC2028},
+       {0x63f653bd, EM2870_BOARD_REDDO_DVB_C_USB_BOX, TUNER_ABSENT},
+       {0x4e913442, EM2882_BOARD_DIKOM_DK300, TUNER_XC2028},
+ };
+ /* I2C devicelist hash table for devices with generic USB IDs */
+ static struct em28xx_hash_table em28xx_i2c_hash[] = {
+       {0xb06a32c3, EM2800_BOARD_TERRATEC_CINERGY_200, TUNER_LG_PAL_NEW_TAPC},
+       {0xf51200e3, EM2800_BOARD_VGEAR_POCKETTV, TUNER_LG_PAL_NEW_TAPC},
+       {0x1ba50080, EM2860_BOARD_SAA711X_REFERENCE_DESIGN, TUNER_ABSENT},
+       {0x77800080, EM2860_BOARD_TVP5150_REFERENCE_DESIGN, TUNER_ABSENT},
+       {0xc51200e3, EM2820_BOARD_GADMEI_TVR200, TUNER_LG_PAL_NEW_TAPC},
+       {0x4ba50080, EM2861_BOARD_GADMEI_UTV330PLUS, TUNER_TNF_5335MF},
+       {0x6b800080, EM2874_BOARD_LEADERSHIP_ISDBT, TUNER_ABSENT},
+ };
+ /* I2C possible address to saa7115, tvp5150, msp3400, tvaudio */
+ static unsigned short saa711x_addrs[] = {
+       0x4a >> 1, 0x48 >> 1,   /* SAA7111, SAA7111A and SAA7113 */
+       0x42 >> 1, 0x40 >> 1,   /* SAA7114, SAA7115 and SAA7118 */
+       I2C_CLIENT_END };
+ static unsigned short tvp5150_addrs[] = {
+       0xb8 >> 1,
+       0xba >> 1,
+       I2C_CLIENT_END
+ };
+ static unsigned short msp3400_addrs[] = {
+       0x80 >> 1,
+       0x88 >> 1,
+       I2C_CLIENT_END
+ };
+ int em28xx_tuner_callback(void *ptr, int component, int command, int arg)
+ {
+       int rc = 0;
+       struct em28xx *dev = ptr;
+       if (dev->tuner_type != TUNER_XC2028 && dev->tuner_type != TUNER_XC5000)
+               return 0;
+       if (command != XC2028_TUNER_RESET && command != XC5000_TUNER_RESET)
+               return 0;
+       rc = em28xx_gpio_set(dev, dev->board.tuner_gpio);
+       return rc;
+ }
+ EXPORT_SYMBOL_GPL(em28xx_tuner_callback);
+ static inline void em28xx_set_model(struct em28xx *dev)
+ {
+       memcpy(&dev->board, &em28xx_boards[dev->model], sizeof(dev->board));
+       /* Those are the default values for the majority of boards
+          Use those values if not specified otherwise at boards entry
+        */
+       if (!dev->board.xclk)
+               dev->board.xclk = EM28XX_XCLK_IR_RC5_MODE |
+                                 EM28XX_XCLK_FREQUENCY_12MHZ;
+       if (!dev->board.i2c_speed)
+               dev->board.i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE |
+                                      EM28XX_I2C_FREQ_100_KHZ;
+ }
+ /* FIXME: Should be replaced by a proper mt9m111 driver */
+ static int em28xx_initialize_mt9m111(struct em28xx *dev)
+ {
+       int i;
+       unsigned char regs[][3] = {
+               { 0x0d, 0x00, 0x01, },  /* reset and use defaults */
+               { 0x0d, 0x00, 0x00, },
+               { 0x0a, 0x00, 0x21, },
+               { 0x21, 0x04, 0x00, },  /* full readout speed, no row/col skipping */
+       };
+       for (i = 0; i < ARRAY_SIZE(regs); i++)
+               i2c_master_send(&dev->i2c_client, &regs[i][0], 3);
+       return 0;
+ }
+ /* FIXME: Should be replaced by a proper mt9m001 driver */
+ static int em28xx_initialize_mt9m001(struct em28xx *dev)
+ {
+       int i;
+       unsigned char regs[][3] = {
+               { 0x0d, 0x00, 0x01, },
+               { 0x0d, 0x00, 0x00, },
+               { 0x04, 0x05, 0x00, },  /* hres = 1280 */
+               { 0x03, 0x04, 0x00, },  /* vres = 1024 */
+               { 0x20, 0x11, 0x00, },
+               { 0x06, 0x00, 0x10, },
+               { 0x2b, 0x00, 0x24, },
+               { 0x2e, 0x00, 0x24, },
+               { 0x35, 0x00, 0x24, },
+               { 0x2d, 0x00, 0x20, },
+               { 0x2c, 0x00, 0x20, },
+               { 0x09, 0x0a, 0xd4, },
+               { 0x35, 0x00, 0x57, },
+       };
+       for (i = 0; i < ARRAY_SIZE(regs); i++)
+               i2c_master_send(&dev->i2c_client, &regs[i][0], 3);
+       return 0;
+ }
+ /* HINT method: webcam I2C chips
+  *
+  * This method works for webcams with Micron sensors
+  */
+ static int em28xx_hint_sensor(struct em28xx *dev)
+ {
+       int rc;
+       char *sensor_name;
+       unsigned char cmd;
+       __be16 version_be;
+       u16 version;
+       /* Micron sensor detection */
+       dev->i2c_client.addr = 0xba >> 1;
+       cmd = 0;
+       i2c_master_send(&dev->i2c_client, &cmd, 1);
+       rc = i2c_master_recv(&dev->i2c_client, (char *)&version_be, 2);
+       if (rc != 2)
+               return -EINVAL;
+       version = be16_to_cpu(version_be);
+       switch (version) {
+       case 0x8232:            /* mt9v011 640x480 1.3 Mpix sensor */
+       case 0x8243:            /* mt9v011 rev B 640x480 1.3 Mpix sensor */
+               dev->model = EM2820_BOARD_SILVERCREST_WEBCAM;
+               em28xx_set_model(dev);
+               sensor_name = "mt9v011";
+               dev->em28xx_sensor = EM28XX_MT9V011;
+               dev->sensor_xres = 640;
+               dev->sensor_yres = 480;
+               /*
+                * FIXME: mt9v011 uses I2S speed as xtal clk - at least with
+                * the Silvercrest cam I have here for testing - for higher
+                * resolutions, a high clock cause horizontal artifacts, so we
+                * need to use a lower xclk frequency.
+                * Yet, it would be possible to adjust xclk depending on the
+                * desired resolution, since this affects directly the
+                * frame rate.
+                */
+               dev->board.xclk = EM28XX_XCLK_FREQUENCY_4_3MHZ;
+               dev->sensor_xtal = 4300000;
+               /* probably means GRGB 16 bit bayer */
+               dev->vinmode = 0x0d;
+               dev->vinctl = 0x00;
+               break;
+       case 0x143a:    /* MT9M111 as found in the ECS G200 */
+               dev->model = EM2750_BOARD_UNKNOWN;
+               em28xx_set_model(dev);
+               sensor_name = "mt9m111";
+               dev->board.xclk = EM28XX_XCLK_FREQUENCY_48MHZ;
+               dev->em28xx_sensor = EM28XX_MT9M111;
+               em28xx_initialize_mt9m111(dev);
+               dev->sensor_xres = 640;
+               dev->sensor_yres = 512;
+               dev->vinmode = 0x0a;
+               dev->vinctl = 0x00;
+               break;
+       case 0x8431:
+               dev->model = EM2750_BOARD_UNKNOWN;
+               em28xx_set_model(dev);
+               sensor_name = "mt9m001";
+               dev->em28xx_sensor = EM28XX_MT9M001;
+               em28xx_initialize_mt9m001(dev);
+               dev->sensor_xres = 1280;
+               dev->sensor_yres = 1024;
+               /* probably means BGGR 16 bit bayer */
+               dev->vinmode = 0x0c;
+               dev->vinctl = 0x00;
+               break;
+       default:
+               printk("Unknown Micron Sensor 0x%04x\n", version);
+               return -EINVAL;
+       }
+       /* Setup webcam defaults */
+       em28xx_pre_card_setup(dev);
+       em28xx_errdev("Sensor is %s, using model %s entry.\n",
+                     sensor_name, em28xx_boards[dev->model].name);
+       return 0;
+ }
+ /* Since em28xx_pre_card_setup() requires a proper dev->model,
+  * this won't work for boards with generic PCI IDs
+  */
+ static void em28xx_pre_card_setup(struct em28xx *dev)
+ {
+       /* Set the initial XCLK and I2C clock values based on the board
+          definition */
+       em28xx_write_reg(dev, EM28XX_R0F_XCLK, dev->board.xclk & 0x7f);
+       if (!dev->board.is_em2800)
+               em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, dev->board.i2c_speed);
+       msleep(50);
+       /* request some modules */
+       switch (dev->model) {
+       case EM2861_BOARD_PLEXTOR_PX_TV100U:
+               /* Sets the msp34xx I2S speed */
+               dev->i2s_speed = 2048000;
+               break;
+       case EM2861_BOARD_KWORLD_PVRTV_300U:
+       case EM2880_BOARD_KWORLD_DVB_305U:
+               em28xx_write_reg(dev, EM28XX_R08_GPIO, 0x6d);
+               msleep(10);
+               em28xx_write_reg(dev, EM28XX_R08_GPIO, 0x7d);
+               msleep(10);
+               break;
+       case EM2870_BOARD_COMPRO_VIDEOMATE:
+               /* TODO: someone can do some cleanup here...
+                        not everything's needed */
+               em28xx_write_reg(dev, EM2880_R04_GPO, 0x00);
+               msleep(10);
+               em28xx_write_reg(dev, EM2880_R04_GPO, 0x01);
+               msleep(10);
+               em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd);
+               mdelay(70);
+               em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfc);
+               mdelay(70);
+               em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xdc);
+               mdelay(70);
+               em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfc);
+               mdelay(70);
+               break;
+       case EM2870_BOARD_TERRATEC_XS_MT2060:
+               /* this device needs some gpio writes to get the DVB-T
+                  demod work */
+               em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe);
+               mdelay(70);
+               em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xde);
+               mdelay(70);
+               em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe);
+               mdelay(70);
+               break;
+       case EM2870_BOARD_PINNACLE_PCTV_DVB:
+               /* this device needs some gpio writes to get the
+                  DVB-T demod work */
+               em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe);
+               mdelay(70);
+               em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xde);
+               mdelay(70);
+               em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe);
+               mdelay(70);
+               break;
+       case EM2820_BOARD_GADMEI_UTV310:
+       case EM2820_BOARD_MSI_VOX_USB_2:
+               /* enables audio for that devices */
+               em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd);
+               break;
+       case EM2882_BOARD_KWORLD_ATSC_315U:
+               em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xff);
+               msleep(10);
+               em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe);
+               msleep(10);
+               em28xx_write_reg(dev, EM2880_R04_GPO, 0x00);
+               msleep(10);
+               em28xx_write_reg(dev, EM2880_R04_GPO, 0x08);
+               msleep(10);
+               break;
+       case EM2860_BOARD_KAIOMY_TVNPC_U2:
+               em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x07", 1);
+               em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1);
+               em28xx_write_regs(dev, 0x0d, "\x42", 1);
+               em28xx_write_regs(dev, 0x08, "\xfd", 1);
+               msleep(10);
+               em28xx_write_regs(dev, 0x08, "\xff", 1);
+               msleep(10);
+               em28xx_write_regs(dev, 0x08, "\x7f", 1);
+               msleep(10);
+               em28xx_write_regs(dev, 0x08, "\x6b", 1);
+               break;
+       case EM2860_BOARD_EASYCAP:
+               em28xx_write_regs(dev, 0x08, "\xf8", 1);
+               break;
+       case EM2820_BOARD_IODATA_GVMVP_SZ:
+               em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xff);
+               msleep(70);
+               em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xf7);
+               msleep(10);
+               em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe);
+               msleep(70);
+               em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd);
+               msleep(70);
+               break;
+       }
+       em28xx_gpio_set(dev, dev->board.tuner_gpio);
+       em28xx_set_mode(dev, EM28XX_ANALOG_MODE);
+       /* Unlock device */
+       em28xx_set_mode(dev, EM28XX_SUSPEND);
+ }
+ static void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl)
+ {
+       memset(ctl, 0, sizeof(*ctl));
+       ctl->fname   = XC2028_DEFAULT_FIRMWARE;
+       ctl->max_len = 64;
+       ctl->mts = em28xx_boards[dev->model].mts_firmware;
+       switch (dev->model) {
+       case EM2880_BOARD_EMPIRE_DUAL_TV:
+       case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900:
+       case EM2882_BOARD_TERRATEC_HYBRID_XS:
+               ctl->demod = XC3028_FE_ZARLINK456;
+               break;
+       case EM2880_BOARD_TERRATEC_HYBRID_XS:
+       case EM2880_BOARD_TERRATEC_HYBRID_XS_FR:
+       case EM2881_BOARD_PINNACLE_HYBRID_PRO:
+               ctl->demod = XC3028_FE_ZARLINK456;
+               break;
+       case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2:
+       case EM2882_BOARD_PINNACLE_HYBRID_PRO_330E:
+               ctl->demod = XC3028_FE_DEFAULT;
+               break;
+       case EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600:
+               ctl->demod = XC3028_FE_DEFAULT;
+               ctl->fname = XC3028L_DEFAULT_FIRMWARE;
+               break;
+       case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850:
+       case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950:
+       case EM2880_BOARD_PINNACLE_PCTV_HD_PRO:
+               /* FIXME: Better to specify the needed IF */
+               ctl->demod = XC3028_FE_DEFAULT;
+               break;
+       case EM2883_BOARD_KWORLD_HYBRID_330U:
+       case EM2882_BOARD_DIKOM_DK300:
+       case EM2882_BOARD_KWORLD_VS_DVBT:
+               ctl->demod = XC3028_FE_CHINA;
+               ctl->fname = XC2028_DEFAULT_FIRMWARE;
+               break;
+       case EM2882_BOARD_EVGA_INDTUBE:
+               ctl->demod = XC3028_FE_CHINA;
+               ctl->fname = XC3028L_DEFAULT_FIRMWARE;
+               break;
+       default:
+               ctl->demod = XC3028_FE_OREN538;
+       }
+ }
+ static void em28xx_tuner_setup(struct em28xx *dev)
+ {
+       struct tuner_setup           tun_setup;
+       struct v4l2_frequency        f;
+       if (dev->tuner_type == TUNER_ABSENT)
+               return;
+       memset(&tun_setup, 0, sizeof(tun_setup));
+       tun_setup.mode_mask = T_ANALOG_TV | T_RADIO;
+       tun_setup.tuner_callback = em28xx_tuner_callback;
+       if (dev->board.radio.type) {
+               tun_setup.type = dev->board.radio.type;
+               tun_setup.addr = dev->board.radio_addr;
+               v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr, &tun_setup);
+       }
+       if ((dev->tuner_type != TUNER_ABSENT) && (dev->tuner_type)) {
+               tun_setup.type   = dev->tuner_type;
+               tun_setup.addr   = dev->tuner_addr;
+               v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr, &tun_setup);
+       }
+       if (dev->tda9887_conf) {
+               struct v4l2_priv_tun_config tda9887_cfg;
+               tda9887_cfg.tuner = TUNER_TDA9887;
+               tda9887_cfg.priv = &dev->tda9887_conf;
+               v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config, &tda9887_cfg);
+       }
+       if (dev->tuner_type == TUNER_XC2028) {
+               struct v4l2_priv_tun_config  xc2028_cfg;
+               struct xc2028_ctrl           ctl;
+               memset(&xc2028_cfg, 0, sizeof(xc2028_cfg));
+               memset(&ctl, 0, sizeof(ctl));
+               em28xx_setup_xc3028(dev, &ctl);
+               xc2028_cfg.tuner = TUNER_XC2028;
+               xc2028_cfg.priv  = &ctl;
+               v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config, &xc2028_cfg);
+       }
+       /* configure tuner */
+       f.tuner = 0;
+       f.type = V4L2_TUNER_ANALOG_TV;
+       f.frequency = 9076;     /* just a magic number */
+       dev->ctl_freq = f.frequency;
+       v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f);
+ }
+ static int em28xx_hint_board(struct em28xx *dev)
+ {
+       int i;
+       /* HINT method: EEPROM
+        *
+        * This method works only for boards with eeprom.
+        * Uses a hash of all eeprom bytes. The hash should be
+        * unique for a vendor/tuner pair.
+        * There are a high chance that tuners for different
+        * video standards produce different hashes.
+        */
+       for (i = 0; i < ARRAY_SIZE(em28xx_eeprom_hash); i++) {
+               if (dev->hash == em28xx_eeprom_hash[i].hash) {
+                       dev->model = em28xx_eeprom_hash[i].model;
+                       dev->tuner_type = em28xx_eeprom_hash[i].tuner;
+                       em28xx_errdev("Your board has no unique USB ID.\n");
+                       em28xx_errdev("A hint were successfully done, "
+                                     "based on eeprom hash.\n");
+                       em28xx_errdev("This method is not 100%% failproof.\n");
+                       em28xx_errdev("If the board were missdetected, "
+                                     "please email this log to:\n");
+                       em28xx_errdev("\tV4L Mailing List "
+                                     " <linux-media@vger.kernel.org>\n");
+                       em28xx_errdev("Board detected as %s\n",
+                                     em28xx_boards[dev->model].name);
+                       return 0;
+               }
+       }
+       /* HINT method: I2C attached devices
+        *
+        * This method works for all boards.
+        * Uses a hash of i2c scanned devices.
+        * Devices with the same i2c attached chips will
+        * be considered equal.
+        * This method is less precise than the eeprom one.
+        */
+       /* user did not request i2c scanning => do it now */
+       if (!dev->i2c_hash)
+               em28xx_do_i2c_scan(dev);
+       for (i = 0; i < ARRAY_SIZE(em28xx_i2c_hash); i++) {
+               if (dev->i2c_hash == em28xx_i2c_hash[i].hash) {
+                       dev->model = em28xx_i2c_hash[i].model;
+                       dev->tuner_type = em28xx_i2c_hash[i].tuner;
+                       em28xx_errdev("Your board has no unique USB ID.\n");
+                       em28xx_errdev("A hint were successfully done, "
+                                     "based on i2c devicelist hash.\n");
+                       em28xx_errdev("This method is not 100%% failproof.\n");
+                       em28xx_errdev("If the board were missdetected, "
+                                     "please email this log to:\n");
+                       em28xx_errdev("\tV4L Mailing List "
+                                     " <linux-media@vger.kernel.org>\n");
+                       em28xx_errdev("Board detected as %s\n",
+                                     em28xx_boards[dev->model].name);
+                       return 0;
+               }
+       }
+       em28xx_errdev("Your board has no unique USB ID and thus need a "
+                     "hint to be detected.\n");
+       em28xx_errdev("You may try to use card=<n> insmod option to "
+                     "workaround that.\n");
+       em28xx_errdev("Please send an email with this log to:\n");
+       em28xx_errdev("\tV4L Mailing List <linux-media@vger.kernel.org>\n");
+       em28xx_errdev("Board eeprom hash is 0x%08lx\n", dev->hash);
+       em28xx_errdev("Board i2c devicelist hash is 0x%08lx\n", dev->i2c_hash);
+       em28xx_errdev("Here is a list of valid choices for the card=<n>"
+                     " insmod option:\n");
+       for (i = 0; i < em28xx_bcount; i++) {
+               em28xx_errdev("    card=%d -> %s\n",
+                               i, em28xx_boards[i].name);
+       }
+       return -1;
+ }
+ static void em28xx_card_setup(struct em28xx *dev)
+ {
+       /*
+        * If the device can be a webcam, seek for a sensor.
+        * If sensor is not found, then it isn't a webcam.
+        */
+       if (dev->board.is_webcam) {
+               if (em28xx_hint_sensor(dev) < 0)
+                       dev->board.is_webcam = 0;
+               else
+                       dev->progressive = 1;
+       }
+       if (!dev->board.is_webcam) {
+               switch (dev->model) {
+               case EM2820_BOARD_UNKNOWN:
+               case EM2800_BOARD_UNKNOWN:
+               /*
+                * The K-WORLD DVB-T 310U is detected as an MSI Digivox AD.
+                *
+                * This occurs because they share identical USB vendor and
+                * product IDs.
+                *
+                * What we do here is look up the EEPROM hash of the K-WORLD
+                * and if it is found then we decide that we do not have
+                * a DIGIVOX and reset the device to the K-WORLD instead.
+                *
+                * This solution is only valid if they do not share eeprom
+                * hash identities which has not been determined as yet.
+                */
+               if (em28xx_hint_board(dev) < 0)
+                       em28xx_errdev("Board not discovered\n");
+               else {
+                       em28xx_set_model(dev);
+                       em28xx_pre_card_setup(dev);
+               }
+               break;
+               default:
+                       em28xx_set_model(dev);
+               }
+       }
+       em28xx_info("Identified as %s (card=%d)\n",
+                   dev->board.name, dev->model);
+       dev->tuner_type = em28xx_boards[dev->model].tuner_type;
+       if (em28xx_boards[dev->model].tuner_addr)
+               dev->tuner_addr = em28xx_boards[dev->model].tuner_addr;
+       if (em28xx_boards[dev->model].tda9887_conf)
+               dev->tda9887_conf = em28xx_boards[dev->model].tda9887_conf;
+       /* request some modules */
+       switch (dev->model) {
+       case EM2820_BOARD_HAUPPAUGE_WINTV_USB_2:
+       case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900:
+       case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2:
+       case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850:
+       case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950:
+       {
+               struct tveeprom tv;
+ #if defined(CONFIG_MODULES) && defined(MODULE)
+               request_module("tveeprom");
+ #endif
+               /* Call first TVeeprom */
+               dev->i2c_client.addr = 0xa0 >> 1;
+               tveeprom_hauppauge_analog(&dev->i2c_client, &tv, dev->eedata);
+               dev->tuner_type = tv.tuner_type;
+               if (tv.audio_processor == V4L2_IDENT_MSPX4XX) {
+                       dev->i2s_speed = 2048000;
+                       dev->board.has_msp34xx = 1;
+               }
+               break;
+       }
+       case EM2882_BOARD_KWORLD_ATSC_315U:
+               em28xx_write_reg(dev, 0x0d, 0x42);
+               msleep(10);
+               em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd);
+               msleep(10);
+               break;
+       case EM2820_BOARD_KWORLD_PVRTV2800RF:
+               /* GPIO enables sound on KWORLD PVR TV 2800RF */
+               em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xf9);
+               break;
+       case EM2820_BOARD_UNKNOWN:
+       case EM2800_BOARD_UNKNOWN:
+               /*
+                * The K-WORLD DVB-T 310U is detected as an MSI Digivox AD.
+                *
+                * This occurs because they share identical USB vendor and
+                * product IDs.
+                *
+                * What we do here is look up the EEPROM hash of the K-WORLD
+                * and if it is found then we decide that we do not have
+                * a DIGIVOX and reset the device to the K-WORLD instead.
+                *
+                * This solution is only valid if they do not share eeprom
+                * hash identities which has not been determined as yet.
+                */
+       case EM2880_BOARD_MSI_DIGIVOX_AD:
+               if (!em28xx_hint_board(dev))
+                       em28xx_set_model(dev);
+               /* In cases where we had to use a board hint, the call to
+                  em28xx_set_mode() in em28xx_pre_card_setup() was a no-op,
+                  so make the call now so the analog GPIOs are set properly
+                  before probing the i2c bus. */
+               em28xx_gpio_set(dev, dev->board.tuner_gpio);
+               em28xx_set_mode(dev, EM28XX_ANALOG_MODE);
+               break;
+ /*
+                * The Dikom DK300 is detected as an Kworld VS-DVB-T 323UR.
+                *
+                * This occurs because they share identical USB vendor and
+                * product IDs.
+                *
+                * What we do here is look up the EEPROM hash of the Dikom
+                * and if it is found then we decide that we do not have
+                * a Kworld and reset the device to the Dikom instead.
+                *
+                * This solution is only valid if they do not share eeprom
+                * hash identities which has not been determined as yet.
+                */
+       case EM2882_BOARD_KWORLD_VS_DVBT:
+               if (!em28xx_hint_board(dev))
+                       em28xx_set_model(dev);
+               /* In cases where we had to use a board hint, the call to
+                  em28xx_set_mode() in em28xx_pre_card_setup() was a no-op,
+                  so make the call now so the analog GPIOs are set properly
+                  before probing the i2c bus. */
+               em28xx_gpio_set(dev, dev->board.tuner_gpio);
+               em28xx_set_mode(dev, EM28XX_ANALOG_MODE);
+               break;
+       }
+       if (dev->board.valid == EM28XX_BOARD_NOT_VALIDATED) {
+               em28xx_errdev("\n\n");
+               em28xx_errdev("The support for this board weren't "
+                             "valid yet.\n");
+               em28xx_errdev("Please send a report of having this working\n");
+               em28xx_errdev("not to V4L mailing list (and/or to other "
+                               "addresses)\n\n");
+       }
+       /* Allow override tuner type by a module parameter */
+       if (tuner >= 0)
+               dev->tuner_type = tuner;
+       /* request some modules */
+       if (dev->board.has_msp34xx)
+               v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
+                       "msp3400", 0, msp3400_addrs);
+       if (dev->board.decoder == EM28XX_SAA711X)
+               v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
+                       "saa7115_auto", 0, saa711x_addrs);
+       if (dev->board.decoder == EM28XX_TVP5150)
+               v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
+                       "tvp5150", 0, tvp5150_addrs);
+       if (dev->em28xx_sensor == EM28XX_MT9V011) {
+               struct mt9v011_platform_data pdata;
+               struct i2c_board_info mt9v011_info = {
+                       .type = "mt9v011",
+                       .addr = 0xba >> 1,
+                       .platform_data = &pdata,
+               };
+               pdata.xtal = dev->sensor_xtal;
+               v4l2_i2c_new_subdev_board(&dev->v4l2_dev, &dev->i2c_adap,
+                               &mt9v011_info, NULL);
+       }
+       if (dev->board.adecoder == EM28XX_TVAUDIO)
+               v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
+                       "tvaudio", dev->board.tvaudio_addr, NULL);
+       if (dev->board.tuner_type != TUNER_ABSENT) {
+               int has_demod = (dev->tda9887_conf & TDA9887_PRESENT);
+               if (dev->board.radio.type)
+                       v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
+                               "tuner", dev->board.radio_addr, NULL);
+               if (has_demod)
+                       v4l2_i2c_new_subdev(&dev->v4l2_dev,
+                               &dev->i2c_adap, "tuner",
+                               0, v4l2_i2c_tuner_addrs(ADDRS_DEMOD));
+               if (dev->tuner_addr == 0) {
+                       enum v4l2_i2c_tuner_type type =
+                               has_demod ? ADDRS_TV_WITH_DEMOD : ADDRS_TV;
+                       struct v4l2_subdev *sd;
+                       sd = v4l2_i2c_new_subdev(&dev->v4l2_dev,
+                               &dev->i2c_adap, "tuner",
+                               0, v4l2_i2c_tuner_addrs(type));
+                       if (sd)
+                               dev->tuner_addr = v4l2_i2c_subdev_addr(sd);
+               } else {
+                       v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
+                               "tuner", dev->tuner_addr, NULL);
+               }
+       }
+       em28xx_tuner_setup(dev);
+ }
+ static void request_module_async(struct work_struct *work)
+ {
+       struct em28xx *dev = container_of(work,
+                            struct em28xx, request_module_wk);
+       /*
+        * The em28xx extensions can be modules or builtin. If the
+        * modules are already loaded or are built in, those extensions
+        * can be initialised right now. Otherwise, the module init
+        * code will do it.
+        */
+       em28xx_init_extension(dev);
+ #if defined(CONFIG_MODULES) && defined(MODULE)
+       if (dev->has_audio_class)
+               request_module("snd-usb-audio");
+       else if (dev->has_alsa_audio)
+               request_module("em28xx-alsa");
+       if (dev->board.has_dvb)
+               request_module("em28xx-dvb");
+       if (dev->board.ir_codes && !disable_ir)
+               request_module("em28xx-rc");
+ #endif /* CONFIG_MODULES */
+ }
+ static void request_modules(struct em28xx *dev)
+ {
+       INIT_WORK(&dev->request_module_wk, request_module_async);
+       schedule_work(&dev->request_module_wk);
+ }
+ static void flush_request_modules(struct em28xx *dev)
+ {
++      flush_work(&dev->request_module_wk);
+ }
+ /*
+  * em28xx_release_resources()
+  * unregisters the v4l2,i2c and usb devices
+  * called when the device gets disconnected or at module unload
+ */
+ void em28xx_release_resources(struct em28xx *dev)
+ {
+       /*FIXME: I2C IR should be disconnected */
+       em28xx_release_analog_resources(dev);
+       em28xx_i2c_unregister(dev);
+       v4l2_device_unregister(&dev->v4l2_dev);
+       usb_put_dev(dev->udev);
+       /* Mark device as unused */
+       clear_bit(dev->devno, &em28xx_devused);
+ };
+ /*
+  * em28xx_init_dev()
+  * allocates and inits the device structs, registers i2c bus and v4l device
+  */
+ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev,
+                          struct usb_interface *interface,
+                          int minor)
+ {
+       int retval;
+       dev->udev = udev;
+       mutex_init(&dev->ctrl_urb_lock);
+       spin_lock_init(&dev->slock);
+       dev->em28xx_write_regs = em28xx_write_regs;
+       dev->em28xx_read_reg = em28xx_read_reg;
+       dev->em28xx_read_reg_req_len = em28xx_read_reg_req_len;
+       dev->em28xx_write_regs_req = em28xx_write_regs_req;
+       dev->em28xx_read_reg_req = em28xx_read_reg_req;
+       dev->board.is_em2800 = em28xx_boards[dev->model].is_em2800;
+       em28xx_set_model(dev);
+       /* Set the default GPO/GPIO for legacy devices */
+       dev->reg_gpo_num = EM2880_R04_GPO;
+       dev->reg_gpio_num = EM28XX_R08_GPIO;
+       dev->wait_after_write = 5;
+       /* Based on the Chip ID, set the device configuration */
+       retval = em28xx_read_reg(dev, EM28XX_R0A_CHIPID);
+       if (retval > 0) {
+               dev->chip_id = retval;
+               switch (dev->chip_id) {
+               case CHIP_ID_EM2800:
+                       em28xx_info("chip ID is em2800\n");
+                       break;
+               case CHIP_ID_EM2710:
+                       em28xx_info("chip ID is em2710\n");
+                       break;
+               case CHIP_ID_EM2750:
+                       em28xx_info("chip ID is em2750\n");
+                       break;
+               case CHIP_ID_EM2820:
+                       em28xx_info("chip ID is em2820 (or em2710)\n");
+                       break;
+               case CHIP_ID_EM2840:
+                       em28xx_info("chip ID is em2840\n");
+                       break;
+               case CHIP_ID_EM2860:
+                       em28xx_info("chip ID is em2860\n");
+                       break;
+               case CHIP_ID_EM2870:
+                       em28xx_info("chip ID is em2870\n");
+                       dev->wait_after_write = 0;
+                       break;
+               case CHIP_ID_EM2874:
+                       em28xx_info("chip ID is em2874\n");
+                       dev->reg_gpio_num = EM2874_R80_GPIO;
+                       dev->wait_after_write = 0;
+                       break;
+               case CHIP_ID_EM28174:
+                       em28xx_info("chip ID is em28174\n");
+                       dev->reg_gpio_num = EM2874_R80_GPIO;
+                       dev->wait_after_write = 0;
+                       break;
+               case CHIP_ID_EM2883:
+                       em28xx_info("chip ID is em2882/em2883\n");
+                       dev->wait_after_write = 0;
+                       break;
+               case CHIP_ID_EM2884:
+                       em28xx_info("chip ID is em2884\n");
+                       dev->reg_gpio_num = EM2874_R80_GPIO;
+                       dev->wait_after_write = 0;
+                       break;
+               default:
+                       em28xx_info("em28xx chip ID = %d\n", dev->chip_id);
+               }
+       }
+       if (dev->is_audio_only) {
+               retval = em28xx_audio_setup(dev);
+               if (retval)
+                       return -ENODEV;
+               em28xx_init_extension(dev);
+               return 0;
+       }
+       /* Prepopulate cached GPO register content */
+       retval = em28xx_read_reg(dev, dev->reg_gpo_num);
+       if (retval >= 0)
+               dev->reg_gpo = retval;
+       em28xx_pre_card_setup(dev);
+       if (!dev->board.is_em2800) {
+               /* Resets I2C speed */
+               retval = em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, dev->board.i2c_speed);
+               if (retval < 0) {
+                       em28xx_errdev("%s: em28xx_write_reg failed!"
+                                     " retval [%d]\n",
+                                     __func__, retval);
+                       return retval;
+               }
+       }
+       retval = v4l2_device_register(&interface->dev, &dev->v4l2_dev);
+       if (retval < 0) {
+               em28xx_errdev("Call to v4l2_device_register() failed!\n");
+               return retval;
+       }
+       /* register i2c bus */
+       retval = em28xx_i2c_register(dev);
+       if (retval < 0) {
+               em28xx_errdev("%s: em28xx_i2c_register - error [%d]!\n",
+                       __func__, retval);
+               goto unregister_dev;
+       }
+       /*
+        * Default format, used for tvp5150 or saa711x output formats
+        */
+       dev->vinmode = 0x10;
+       dev->vinctl  = EM28XX_VINCTRL_INTERLACED |
+                      EM28XX_VINCTRL_CCIR656_ENABLE;
+       /* Do board specific init and eeprom reading */
+       em28xx_card_setup(dev);
+       /* Configure audio */
+       retval = em28xx_audio_setup(dev);
+       if (retval < 0) {
+               em28xx_errdev("%s: Error while setting audio - error [%d]!\n",
+                       __func__, retval);
+               goto fail;
+       }
+       /* wake i2c devices */
+       em28xx_wake_i2c(dev);
+       /* init video dma queues */
+       INIT_LIST_HEAD(&dev->vidq.active);
+       INIT_LIST_HEAD(&dev->vbiq.active);
+       if (dev->board.has_msp34xx) {
+               /* Send a reset to other chips via gpio */
+               retval = em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xf7);
+               if (retval < 0) {
+                       em28xx_errdev("%s: em28xx_write_reg - "
+                                     "msp34xx(1) failed! error [%d]\n",
+                                     __func__, retval);
+                       goto fail;
+               }
+               msleep(3);
+               retval = em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xff);
+               if (retval < 0) {
+                       em28xx_errdev("%s: em28xx_write_reg - "
+                                     "msp34xx(2) failed! error [%d]\n",
+                                     __func__, retval);
+                       goto fail;
+               }
+               msleep(3);
+       }
+       retval = em28xx_register_analog_devices(dev);
+       if (retval < 0) {
+               goto fail;
+       }
+       /* Save some power by putting tuner to sleep */
+       v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0);
+       return 0;
+ fail:
+       em28xx_i2c_unregister(dev);
+ unregister_dev:
+       v4l2_device_unregister(&dev->v4l2_dev);
+       return retval;
+ }
+ /* high bandwidth multiplier, as encoded in highspeed endpoint descriptors */
+ #define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03))
+ /*
+  * em28xx_usb_probe()
+  * checks for supported devices
+  */
+ static int em28xx_usb_probe(struct usb_interface *interface,
+                           const struct usb_device_id *id)
+ {
+       struct usb_device *udev;
+       struct em28xx *dev = NULL;
+       int retval;
+       bool has_audio = false, has_video = false, has_dvb = false;
+       int i, nr;
+       const int ifnum = interface->altsetting[0].desc.bInterfaceNumber;
+       char *speed;
+       udev = usb_get_dev(interface_to_usbdev(interface));
+       /* Check to see next free device and mark as used */
+       do {
+               nr = find_first_zero_bit(&em28xx_devused, EM28XX_MAXBOARDS);
+               if (nr >= EM28XX_MAXBOARDS) {
+                       /* No free device slots */
+                       printk(DRIVER_NAME ": Supports only %i em28xx boards.\n",
+                                       EM28XX_MAXBOARDS);
+                       retval = -ENOMEM;
+                       goto err_no_slot;
+               }
+       } while (test_and_set_bit(nr, &em28xx_devused));
+       /* Don't register audio interfaces */
+       if (interface->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) {
+               em28xx_err(DRIVER_NAME " audio device (%04x:%04x): "
+                       "interface %i, class %i\n",
+                       le16_to_cpu(udev->descriptor.idVendor),
+                       le16_to_cpu(udev->descriptor.idProduct),
+                       ifnum,
+                       interface->altsetting[0].desc.bInterfaceClass);
+               retval = -ENODEV;
+               goto err;
+       }
+       /* allocate memory for our device state and initialize it */
+       dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+       if (dev == NULL) {
+               em28xx_err(DRIVER_NAME ": out of memory!\n");
+               retval = -ENOMEM;
+               goto err;
+       }
+       /* compute alternate max packet sizes */
+       dev->alt_max_pkt_size = kmalloc(sizeof(dev->alt_max_pkt_size[0]) *
+                                       interface->num_altsetting, GFP_KERNEL);
+       if (dev->alt_max_pkt_size == NULL) {
+               em28xx_errdev("out of memory!\n");
+               kfree(dev);
+               retval = -ENOMEM;
+               goto err;
+       }
+       /* Get endpoints */
+       for (i = 0; i < interface->num_altsetting; i++) {
+               int ep;
+               for (ep = 0; ep < interface->altsetting[i].desc.bNumEndpoints; ep++) {
+                       const struct usb_endpoint_descriptor *e;
+                       int sizedescr, size;
+                       e = &interface->altsetting[i].endpoint[ep].desc;
+                       sizedescr = le16_to_cpu(e->wMaxPacketSize);
+                       size = sizedescr & 0x7ff;
+                       if (udev->speed == USB_SPEED_HIGH)
+                               size = size * hb_mult(sizedescr);
+                       if (usb_endpoint_xfer_isoc(e) &&
+                           usb_endpoint_dir_in(e)) {
+                               switch (e->bEndpointAddress) {
+                               case EM28XX_EP_AUDIO:
+                                       has_audio = true;
+                                       break;
+                               case EM28XX_EP_ANALOG:
+                                       has_video = true;
+                                       dev->alt_max_pkt_size[i] = size;
+                                       break;
+                               case EM28XX_EP_DIGITAL:
+                                       has_dvb = true;
+                                       if (size > dev->dvb_max_pkt_size) {
+                                               dev->dvb_max_pkt_size = size;
+                                               dev->dvb_alt = i;
+                                       }
+                                       break;
+                               }
+                       }
+               }
+       }
+       if (!(has_audio || has_video || has_dvb)) {
+               retval = -ENODEV;
+               goto err_free;
+       }
+       switch (udev->speed) {
+       case USB_SPEED_LOW:
+               speed = "1.5";
+               break;
+       case USB_SPEED_UNKNOWN:
+       case USB_SPEED_FULL:
+               speed = "12";
+               break;
+       case USB_SPEED_HIGH:
+               speed = "480";
+               break;
+       default:
+               speed = "unknown";
+       }
+       printk(KERN_INFO DRIVER_NAME
+               ": New device %s %s @ %s Mbps "
+               "(%04x:%04x, interface %d, class %d)\n",
+               udev->manufacturer ? udev->manufacturer : "",
+               udev->product ? udev->product : "",
+               speed,
+               le16_to_cpu(udev->descriptor.idVendor),
+               le16_to_cpu(udev->descriptor.idProduct),
+               ifnum,
+               interface->altsetting->desc.bInterfaceNumber);
+       if (has_audio)
+               printk(KERN_INFO DRIVER_NAME
+                      ": Audio Vendor Class interface %i found\n",
+                      ifnum);
+       if (has_video)
+               printk(KERN_INFO DRIVER_NAME
+                      ": Video interface %i found\n",
+                      ifnum);
+       if (has_dvb)
+               printk(KERN_INFO DRIVER_NAME
+                      ": DVB interface %i found\n",
+                      ifnum);
+       /*
+        * Make sure we have 480 Mbps of bandwidth, otherwise things like
+        * video stream wouldn't likely work, since 12 Mbps is generally
+        * not enough even for most Digital TV streams.
+        */
+       if (udev->speed != USB_SPEED_HIGH && disable_usb_speed_check == 0) {
+               printk(DRIVER_NAME ": Device initialization failed.\n");
+               printk(DRIVER_NAME ": Device must be connected to a high-speed"
+                      " USB 2.0 port.\n");
+               retval = -ENODEV;
+               goto err_free;
+       }
+       snprintf(dev->name, sizeof(dev->name), "em28xx #%d", nr);
+       dev->devno = nr;
+       dev->model = id->driver_info;
+       dev->alt   = -1;
+       dev->is_audio_only = has_audio && !(has_video || has_dvb);
+       dev->has_alsa_audio = has_audio;
+       dev->audio_ifnum = ifnum;
+       /* Checks if audio is provided by some interface */
+       for (i = 0; i < udev->config->desc.bNumInterfaces; i++) {
+               struct usb_interface *uif = udev->config->interface[i];
+               if (uif->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) {
+                       dev->has_audio_class = 1;
+                       break;
+               }
+       }
+       dev->num_alt = interface->num_altsetting;
+       if ((card[nr] >= 0) && (card[nr] < em28xx_bcount))
+               dev->model = card[nr];
+       /* save our data pointer in this interface device */
+       usb_set_intfdata(interface, dev);
+       /* allocate device struct */
+       mutex_init(&dev->lock);
+       mutex_lock(&dev->lock);
+       retval = em28xx_init_dev(dev, udev, interface, nr);
+       if (retval) {
+               goto unlock_and_free;
+       }
+       if (has_dvb) {
+               /* pre-allocate DVB isoc transfer buffers */
+               retval = em28xx_alloc_isoc(dev, EM28XX_DIGITAL_MODE,
+                                          EM28XX_DVB_MAX_PACKETS,
+                                          EM28XX_DVB_NUM_BUFS,
+                                          dev->dvb_max_pkt_size);
+               if (retval) {
+                       goto unlock_and_free;
+               }
+       }
+       request_modules(dev);
+       /* Should be the last thing to do, to avoid newer udev's to
+          open the device before fully initializing it
+        */
+       mutex_unlock(&dev->lock);
+       return 0;
+ unlock_and_free:
+       mutex_unlock(&dev->lock);
+ err_free:
+       kfree(dev->alt_max_pkt_size);
+       kfree(dev);
+ err:
+       clear_bit(nr, &em28xx_devused);
+ err_no_slot:
+       usb_put_dev(udev);
+       return retval;
+ }
+ /*
+  * em28xx_usb_disconnect()
+  * called when the device gets disconnected
+  * video device will be unregistered on v4l2_close in case it is still open
+  */
+ static void em28xx_usb_disconnect(struct usb_interface *interface)
+ {
+       struct em28xx *dev;
+       dev = usb_get_intfdata(interface);
+       usb_set_intfdata(interface, NULL);
+       if (!dev)
+               return;
+       if (dev->is_audio_only) {
+               mutex_lock(&dev->lock);
+               em28xx_close_extension(dev);
+               mutex_unlock(&dev->lock);
+               return;
+       }
+       em28xx_info("disconnecting %s\n", dev->vdev->name);
+       flush_request_modules(dev);
+       /* wait until all current v4l2 io is finished then deallocate
+          resources */
+       mutex_lock(&dev->lock);
+       v4l2_device_disconnect(&dev->v4l2_dev);
+       if (dev->users) {
+               em28xx_warn
+                   ("device %s is open! Deregistration and memory "
+                    "deallocation are deferred on close.\n",
+                    video_device_node_name(dev->vdev));
+               dev->state |= DEV_MISCONFIGURED;
+               em28xx_uninit_isoc(dev, dev->mode);
+               dev->state |= DEV_DISCONNECTED;
+       } else {
+               dev->state |= DEV_DISCONNECTED;
+               em28xx_release_resources(dev);
+       }
+       /* free DVB isoc buffers */
+       em28xx_uninit_isoc(dev, EM28XX_DIGITAL_MODE);
+       mutex_unlock(&dev->lock);
+       em28xx_close_extension(dev);
+       if (!dev->users) {
+               kfree(dev->alt_max_pkt_size);
+               kfree(dev);
+       }
+ }
+ static struct usb_driver em28xx_usb_driver = {
+       .name = "em28xx",
+       .probe = em28xx_usb_probe,
+       .disconnect = em28xx_usb_disconnect,
+       .id_table = em28xx_id_table,
+ };
+ module_usb_driver(em28xx_usb_driver);
Simple merge