]> git.karo-electronics.de Git - mv-sheeva.git/commitdiff
Merge git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/v4l-dvb
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 21 Jul 2008 04:14:42 +0000 (21:14 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 21 Jul 2008 04:14:42 +0000 (21:14 -0700)
* git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/v4l-dvb: (277 commits)
  V4L/DVB (8415): gspca: Infinite loop in i2c_w() of etoms.
  V4L/DVB (8414): videodev/cx18: fix get_index bug and error-handling lock-ups
  V4L/DVB (8411): videobuf-dma-contig.c: fix 64-bit build for pre-2.6.24 kernels
  V4L/DVB (8410): sh_mobile_ceu_camera: fix 64-bit compiler warnings
  V4L/DVB (8397): video: convert select VIDEO_ZORAN_ZR36060 into depends on
  V4L/DVB (8396): video: Fix Kbuild dependency for VIDEO_IR_I2C
  V4L/DVB (8395): saa7134: Fix Kbuild dependency of ir-kbd-i2c
  V4L/DVB (8394): ir-common: CodingStyle fix: move EXPORT_SYMBOL_GPL to their proper places
  V4L/DVB (8393): media/video: Fix depencencies for VIDEOBUF
  V4L/DVB (8392): media/Kconfig: Convert V4L1_COMPAT select into "depends on"
  V4L/DVB (8390): videodev: add comment and remove magic number.
  V4L/DVB (8389): videodev: simplify get_index()
  V4L/DVB (8387): Some cosmetic changes
  V4L/DVB (8381): ov7670: fix compile warnings
  V4L/DVB (8380): saa7115: use saa7115_auto instead of saa711x as the autodetect driver name.
  V4L/DVB (8379): saa7127: Make device detection optional
  V4L/DVB (8378): cx18: move cx18_av_vbi_setup to av-core.c and rename to cx18_av_std_setup
  V4L/DVB (8377): ivtv/cx18: ensure the default control values are correct
  V4L/DVB (8376): cx25840: move cx25840_vbi_setup to core.c and rename to cx25840_std_setup
  V4L/DVB (8374): gspca: No conflict of 0c45:6011 with the sn9c102 driver.
  ...

290 files changed:
Documentation/video4linux/CARDLIST.cx23885
Documentation/video4linux/CARDLIST.em28xx
Documentation/video4linux/CARDLIST.saa7134
Documentation/video4linux/cx18.txt
Documentation/video4linux/gspca.txt [new file with mode: 0644]
drivers/media/Kconfig
drivers/media/common/ir-functions.c
drivers/media/common/saa7146_core.c
drivers/media/common/saa7146_hlp.c
drivers/media/common/saa7146_i2c.c
drivers/media/common/saa7146_video.c
drivers/media/common/tuners/Kconfig
drivers/media/common/tuners/tda18271-maps.c
drivers/media/common/tuners/tuner-xc2028.c
drivers/media/common/tuners/xc5000.c
drivers/media/dvb/Kconfig
drivers/media/dvb/Makefile
drivers/media/dvb/bt8xx/bt878.h
drivers/media/dvb/dvb-core/demux.h
drivers/media/dvb/dvb-core/dmxdev.c
drivers/media/dvb/dvb-core/dvb_ca_en50221.c
drivers/media/dvb/dvb-core/dvb_demux.c
drivers/media/dvb/dvb-core/dvb_net.c
drivers/media/dvb/dvb-core/dvb_ringbuffer.c
drivers/media/dvb/dvb-core/dvb_ringbuffer.h
drivers/media/dvb/dvb-usb/Kconfig
drivers/media/dvb/dvb-usb/Makefile
drivers/media/dvb/dvb-usb/anysee.c [new file with mode: 0644]
drivers/media/dvb/dvb-usb/anysee.h [new file with mode: 0644]
drivers/media/dvb/dvb-usb/au6610.c
drivers/media/dvb/dvb-usb/au6610.h
drivers/media/dvb/dvb-usb/cxusb.c
drivers/media/dvb/dvb-usb/cxusb.h
drivers/media/dvb/dvb-usb/dib0700_devices.c
drivers/media/dvb/dvb-usb/dvb-usb-i2c.c
drivers/media/dvb/dvb-usb/dvb-usb-ids.h
drivers/media/dvb/dvb-usb/gl861.c
drivers/media/dvb/dvb-usb/gl861.h
drivers/media/dvb/frontends/au8522.c
drivers/media/dvb/frontends/dvb-pll.c
drivers/media/dvb/frontends/dvb-pll.h
drivers/media/dvb/frontends/lgdt330x.c
drivers/media/dvb/frontends/s5h1409.c
drivers/media/dvb/frontends/s5h1411.c
drivers/media/dvb/frontends/tda10023.c
drivers/media/dvb/frontends/tda1002x.h
drivers/media/dvb/pluto2/pluto2.c
drivers/media/dvb/siano/Kconfig [new file with mode: 0644]
drivers/media/dvb/siano/Makefile [new file with mode: 0644]
drivers/media/dvb/siano/sms-cards.c [new file with mode: 0644]
drivers/media/dvb/siano/sms-cards.h [new file with mode: 0644]
drivers/media/dvb/siano/smscoreapi.c [new file with mode: 0644]
drivers/media/dvb/siano/smscoreapi.h [new file with mode: 0644]
drivers/media/dvb/siano/smsdvb.c [new file with mode: 0644]
drivers/media/dvb/siano/smsusb.c [new file with mode: 0644]
drivers/media/dvb/ttpci/Kconfig
drivers/media/dvb/ttpci/Makefile
drivers/media/dvb/ttpci/av7110.c
drivers/media/dvb/ttpci/av7110.h
drivers/media/dvb/ttpci/av7110_av.c
drivers/media/dvb/ttpci/av7110_ca.c
drivers/media/dvb/ttpci/av7110_hw.h
drivers/media/dvb/ttpci/budget-av.c
drivers/media/dvb/ttpci/budget-ci.c
drivers/media/dvb/ttpci/budget-core.c
drivers/media/dvb/ttpci/budget-patch.c
drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c
drivers/media/radio/radio-si470x.c
drivers/media/video/Kconfig
drivers/media/video/Makefile
drivers/media/video/bt819.c
drivers/media/video/bt8xx/bt832.c
drivers/media/video/bt8xx/bttv-driver.c
drivers/media/video/bt8xx/bttv-i2c.c
drivers/media/video/bt8xx/bttv-vbi.c
drivers/media/video/bt8xx/bttv.h
drivers/media/video/bt8xx/bttvp.h
drivers/media/video/cafe_ccic.c
drivers/media/video/compat_ioctl32.c
drivers/media/video/cs5345.c
drivers/media/video/cs53l32a.c
drivers/media/video/cx18/cx18-audio.c
drivers/media/video/cx18/cx18-av-audio.c
drivers/media/video/cx18/cx18-av-core.c
drivers/media/video/cx18/cx18-av-core.h
drivers/media/video/cx18/cx18-av-firmware.c
drivers/media/video/cx18/cx18-av-vbi.c
drivers/media/video/cx18/cx18-cards.c
drivers/media/video/cx18/cx18-cards.h
drivers/media/video/cx18/cx18-controls.c
drivers/media/video/cx18/cx18-controls.h
drivers/media/video/cx18/cx18-driver.c
drivers/media/video/cx18/cx18-driver.h
drivers/media/video/cx18/cx18-firmware.c
drivers/media/video/cx18/cx18-gpio.c
drivers/media/video/cx18/cx18-gpio.h
drivers/media/video/cx18/cx18-i2c.c
drivers/media/video/cx18/cx18-ioctl.c
drivers/media/video/cx18/cx18-ioctl.h
drivers/media/video/cx18/cx18-mailbox.c
drivers/media/video/cx18/cx18-streams.c
drivers/media/video/cx18/cx23418.h
drivers/media/video/cx2341x.c
drivers/media/video/cx23885/Kconfig
drivers/media/video/cx23885/cx23885-417.c
drivers/media/video/cx23885/cx23885-cards.c
drivers/media/video/cx23885/cx23885-core.c
drivers/media/video/cx23885/cx23885-dvb.c
drivers/media/video/cx23885/cx23885-video.c
drivers/media/video/cx23885/cx23885.h
drivers/media/video/cx25840/cx25840-core.c
drivers/media/video/cx25840/cx25840-core.h
drivers/media/video/cx25840/cx25840-vbi.c
drivers/media/video/cx88/cx88-alsa.c
drivers/media/video/cx88/cx88-blackbird.c
drivers/media/video/cx88/cx88-i2c.c
drivers/media/video/cx88/cx88-video.c
drivers/media/video/cx88/cx88-vp3054-i2c.c
drivers/media/video/em28xx/em28xx-cards.c
drivers/media/video/em28xx/em28xx-dvb.c
drivers/media/video/em28xx/em28xx-i2c.c
drivers/media/video/em28xx/em28xx-input.c
drivers/media/video/em28xx/em28xx-video.c
drivers/media/video/em28xx/em28xx.h
drivers/media/video/gspca/Kconfig [new file with mode: 0644]
drivers/media/video/gspca/Makefile [new file with mode: 0644]
drivers/media/video/gspca/conex.c [new file with mode: 0644]
drivers/media/video/gspca/etoms.c [new file with mode: 0644]
drivers/media/video/gspca/gspca.c [new file with mode: 0644]
drivers/media/video/gspca/gspca.h [new file with mode: 0644]
drivers/media/video/gspca/jpeg.h [new file with mode: 0644]
drivers/media/video/gspca/mars.c [new file with mode: 0644]
drivers/media/video/gspca/ov519.c [new file with mode: 0644]
drivers/media/video/gspca/pac207.c [new file with mode: 0644]
drivers/media/video/gspca/pac7311.c [new file with mode: 0644]
drivers/media/video/gspca/sonixb.c [new file with mode: 0644]
drivers/media/video/gspca/sonixj.c [new file with mode: 0644]
drivers/media/video/gspca/spca500.c [new file with mode: 0644]
drivers/media/video/gspca/spca501.c [new file with mode: 0644]
drivers/media/video/gspca/spca505.c [new file with mode: 0644]
drivers/media/video/gspca/spca506.c [new file with mode: 0644]
drivers/media/video/gspca/spca508.c [new file with mode: 0644]
drivers/media/video/gspca/spca561.c [new file with mode: 0644]
drivers/media/video/gspca/stk014.c [new file with mode: 0644]
drivers/media/video/gspca/sunplus.c [new file with mode: 0644]
drivers/media/video/gspca/t613.c [new file with mode: 0644]
drivers/media/video/gspca/tv8532.c [new file with mode: 0644]
drivers/media/video/gspca/vc032x.c [new file with mode: 0644]
drivers/media/video/gspca/zc3xx-reg.h [new file with mode: 0644]
drivers/media/video/gspca/zc3xx.c [new file with mode: 0644]
drivers/media/video/ir-kbd-i2c.c
drivers/media/video/ivtv/ivtv-cards.c
drivers/media/video/ivtv/ivtv-cards.h
drivers/media/video/ivtv/ivtv-controls.c
drivers/media/video/ivtv/ivtv-controls.h
drivers/media/video/ivtv/ivtv-driver.c
drivers/media/video/ivtv/ivtv-driver.h
drivers/media/video/ivtv/ivtv-fileops.c
drivers/media/video/ivtv/ivtv-gpio.c
drivers/media/video/ivtv/ivtv-i2c.c
drivers/media/video/ivtv/ivtv-ioctl.c
drivers/media/video/ivtv/ivtv-ioctl.h
drivers/media/video/ivtv/ivtv-streams.c
drivers/media/video/ivtv/ivtvfb.c
drivers/media/video/m52790.c
drivers/media/video/meye.c
drivers/media/video/msp3400-driver.c
drivers/media/video/msp3400-kthreads.c
drivers/media/video/mt9v022.c
drivers/media/video/ov7670.c
drivers/media/video/ovcamchip/ovcamchip_core.c
drivers/media/video/pvrusb2/pvrusb2-audio.c
drivers/media/video/pvrusb2/pvrusb2-audio.h
drivers/media/video/pvrusb2/pvrusb2-context.c
drivers/media/video/pvrusb2/pvrusb2-context.h
drivers/media/video/pvrusb2/pvrusb2-ctrl.c
drivers/media/video/pvrusb2/pvrusb2-ctrl.h
drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c
drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h
drivers/media/video/pvrusb2/pvrusb2-debug.h
drivers/media/video/pvrusb2/pvrusb2-debugifc.c
drivers/media/video/pvrusb2/pvrusb2-debugifc.h
drivers/media/video/pvrusb2/pvrusb2-devattr.c
drivers/media/video/pvrusb2/pvrusb2-devattr.h
drivers/media/video/pvrusb2/pvrusb2-eeprom.c
drivers/media/video/pvrusb2/pvrusb2-eeprom.h
drivers/media/video/pvrusb2/pvrusb2-encoder.c
drivers/media/video/pvrusb2/pvrusb2-encoder.h
drivers/media/video/pvrusb2/pvrusb2-fx2-cmd.h
drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h
drivers/media/video/pvrusb2/pvrusb2-hdw.c
drivers/media/video/pvrusb2/pvrusb2-hdw.h
drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c
drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c
drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h
drivers/media/video/pvrusb2/pvrusb2-i2c-core.c
drivers/media/video/pvrusb2/pvrusb2-i2c-core.h
drivers/media/video/pvrusb2/pvrusb2-io.c
drivers/media/video/pvrusb2/pvrusb2-io.h
drivers/media/video/pvrusb2/pvrusb2-ioread.c
drivers/media/video/pvrusb2/pvrusb2-ioread.h
drivers/media/video/pvrusb2/pvrusb2-main.c
drivers/media/video/pvrusb2/pvrusb2-std.c
drivers/media/video/pvrusb2/pvrusb2-std.h
drivers/media/video/pvrusb2/pvrusb2-sysfs.c
drivers/media/video/pvrusb2/pvrusb2-sysfs.h
drivers/media/video/pvrusb2/pvrusb2-tuner.c
drivers/media/video/pvrusb2/pvrusb2-tuner.h
drivers/media/video/pvrusb2/pvrusb2-util.h
drivers/media/video/pvrusb2/pvrusb2-v4l2.c
drivers/media/video/pvrusb2/pvrusb2-v4l2.h
drivers/media/video/pvrusb2/pvrusb2-video-v4l.c
drivers/media/video/pvrusb2/pvrusb2-video-v4l.h
drivers/media/video/pvrusb2/pvrusb2-wm8775.c
drivers/media/video/pvrusb2/pvrusb2-wm8775.h
drivers/media/video/pvrusb2/pvrusb2.h
drivers/media/video/pwc/pwc-ctrl.c
drivers/media/video/pwc/pwc-ioctl.h
drivers/media/video/pxa_camera.c
drivers/media/video/s2255drv.c [new file with mode: 0644]
drivers/media/video/saa5246a.c
drivers/media/video/saa5249.c
drivers/media/video/saa6588.c
drivers/media/video/saa7115.c
drivers/media/video/saa711x.c [deleted file]
drivers/media/video/saa7127.c
drivers/media/video/saa7134/saa6752hs.c
drivers/media/video/saa7134/saa7134-alsa.c
drivers/media/video/saa7134/saa7134-cards.c
drivers/media/video/saa7134/saa7134-core.c
drivers/media/video/saa7134/saa7134-dvb.c
drivers/media/video/saa7134/saa7134-empress.c
drivers/media/video/saa7134/saa7134-i2c.c
drivers/media/video/saa7134/saa7134-input.c
drivers/media/video/saa7134/saa7134-reg.h
drivers/media/video/saa7134/saa7134-tvaudio.c
drivers/media/video/saa7134/saa7134-video.c
drivers/media/video/saa7134/saa7134.h
drivers/media/video/sh_mobile_ceu_camera.c [new file with mode: 0644]
drivers/media/video/sn9c102/sn9c102_devtable.h
drivers/media/video/soc_camera.c
drivers/media/video/soc_camera_platform.c [new file with mode: 0644]
drivers/media/video/stk-webcam.c
drivers/media/video/tcm825x.c
drivers/media/video/tcm825x.h
drivers/media/video/tda7432.c
drivers/media/video/tda9840.c
drivers/media/video/tda9875.c
drivers/media/video/tea6415c.c
drivers/media/video/tea6420.c
drivers/media/video/tlv320aic23b.c
drivers/media/video/tuner-core.c
drivers/media/video/tvaudio.c
drivers/media/video/usbvision/usbvision-core.c
drivers/media/video/usbvision/usbvision-i2c.c
drivers/media/video/usbvision/usbvision-video.c
drivers/media/video/uvc/Kconfig [new file with mode: 0644]
drivers/media/video/uvc/uvc_ctrl.c
drivers/media/video/uvc/uvc_driver.c
drivers/media/video/uvc/uvc_queue.c
drivers/media/video/uvc/uvc_status.c
drivers/media/video/uvc/uvc_v4l2.c
drivers/media/video/uvc/uvc_video.c
drivers/media/video/uvc/uvcvideo.h
drivers/media/video/videobuf-dma-contig.c [new file with mode: 0644]
drivers/media/video/videobuf-dma-sg.c
drivers/media/video/videobuf-dvb.c
drivers/media/video/videobuf-vmalloc.c
drivers/media/video/videodev.c
drivers/media/video/vivi.c
drivers/media/video/vp27smpx.c
drivers/media/video/wm8739.c
drivers/media/video/wm8775.c
drivers/media/video/zoran_card.c
drivers/media/video/zoran_driver.c
drivers/media/video/zr364xx.c
include/linux/i2c-id.h
include/linux/videodev2.h
include/media/cx2341x.h
include/media/ir-kbd-i2c.h
include/media/pwc-ioctl.h
include/media/saa7146.h
include/media/sh_mobile_ceu.h [new file with mode: 0644]
include/media/soc_camera.h
include/media/soc_camera_platform.h [new file with mode: 0644]
include/media/v4l2-dev.h
include/media/v4l2-i2c-drv-legacy.h
include/media/videobuf-dma-contig.h [new file with mode: 0644]
include/media/videobuf-dma-sg.h
include/media/videobuf-vmalloc.h

index 191194ea1e25ce694217259eb64fe0f583a33462..f0e613ba55b8bd2f1ed0f39b7e93699ec3b9d5bc 100644 (file)
@@ -8,3 +8,4 @@
   7 -> Hauppauge WinTV-HVR1200                             [0070:71d1,0070:71d3]
   8 -> Hauppauge WinTV-HVR1700                             [0070:8101]
   9 -> Hauppauge WinTV-HVR1400                             [0070:8010]
+ 10 -> DViCO FusionHDTV7 Dual Express                      [18ac:d618]
index 1d6a245c828f45f7b9c9c6ea4de824feb1990520..10591467ef166aef609c2b527de16c9d17ca1ff1 100644 (file)
@@ -8,10 +8,13 @@
   7 -> Leadtek Winfast USB II                   (em2800)
   8 -> Kworld USB2800                           (em2800)
   9 -> Pinnacle Dazzle DVC 90/DVC 100           (em2820/em2840) [2304:0207,2304:021a]
- 10 -> Hauppauge WinTV HVR 900                  (em2880)        [2040:6500,2040:6502]
+ 10 -> Hauppauge WinTV HVR 900                  (em2880)        [2040:6500]
  11 -> Terratec Hybrid XS                       (em2880)        [0ccd:0042]
  12 -> Kworld PVR TV 2800 RF                    (em2820/em2840)
  13 -> Terratec Prodigy XS                      (em2880)        [0ccd:0047]
  14 -> Pixelview Prolink PlayTV USB 2.0         (em2820/em2840)
  15 -> V-Gear PocketTV                          (em2800)
  16 -> Hauppauge WinTV HVR 950                  (em2880)        [2040:6513,2040:6517,2040:651b,2040:651f]
+ 17 -> Pinnacle PCTV HD Pro Stick               (em2880)        [2304:0227]
+ 18 -> Hauppauge WinTV HVR 900 (R2)             (em2880)        [2040:6502]
+ 19 -> PointNix Intra-Oral Camera               (em2860)
index 67937df1e974b0e787199c04d996ab084786844d..39868af9cf9fe665a6bbd991a4927e55061232d3 100644 (file)
@@ -37,7 +37,7 @@
  36 -> UPMOST PURPLE TV                         [12ab:0800]
  37 -> Items MuchTV Plus / IT-005
  38 -> Terratec Cinergy 200 TV                  [153b:1152]
- 39 -> LifeView FlyTV Platinum Mini             [5168:0212,4e42:0212]
+ 39 -> LifeView FlyTV Platinum Mini             [5168:0212,4e42:0212,5169:1502]
  40 -> Compro VideoMate TV PVR/FM               [185b:c100]
  41 -> Compro VideoMate TV Gold+                [185b:c100]
  42 -> Sabrent SBT-TVFM (saa7130)
 127 -> Beholder BeholdTV 507 FM/RDS / BeholdTV 509 FM [0000:5071,0000:507B,5ace:5070,5ace:5090]
 128 -> Beholder BeholdTV Columbus TVFM          [0000:5201]
 129 -> Beholder BeholdTV 607 / BeholdTV 609     [5ace:6070,5ace:6071,5ace:6072,5ace:6073,5ace:6090,5ace:6091,5ace:6092,5ace:6093]
-130 -> Beholder BeholdTV M6 / BeholdTV M6 Extra [5ace:6190,5ace:6193,5ace:6191]
+130 -> Beholder BeholdTV M6                     [5ace:6190]
 131 -> Twinhan Hybrid DTV-DVB 3056 PCI          [1822:0022]
 132 -> Genius TVGO AM11MCE
 133 -> NXP Snake DVB-S reference design
 140 -> Avermedia DVB-S Pro A700                 [1461:a7a1]
 141 -> Avermedia DVB-S Hybrid+FM A700           [1461:a7a2]
 142 -> Beholder BeholdTV H6                     [5ace:6290]
+143 -> Beholder BeholdTV M63                    [5ace:6191]
+144 -> Beholder BeholdTV M6 Extra               [5ace:6193]
+145 -> AVerMedia MiniPCI DVB-T Hybrid M103      [1461:f636]
+146 -> ASUSTeK P7131 Analog
index 6842c262890f898561f537888ddf53b8bc39bda5..914cb7e734a20b773709cb067804e07576881097 100644 (file)
@@ -1,36 +1,30 @@
 Some notes regarding the cx18 driver for the Conexant CX23418 MPEG
 encoder chip:
 
-1) The only hardware currently supported is the Hauppauge HVR-1600
-   card and the Compro VideoMate H900 (note that this card only
-   supports analog input, it has no digital tuner!).
+1) Currently supported are:
 
-2) Some people have problems getting the i2c bus to work. Cause unknown.
-   The symptom is that the eeprom cannot be read and the card is
-   unusable.
+       - Hauppauge HVR-1600
+       - Compro VideoMate H900
+       - Yuan MPC718
+       - Conexant Raptor PAL/SECAM devkit
 
-3) The audio from the analog tuner is mono only. Probably caused by
-   incorrect audio register information in the datasheet. We are
-   waiting for updated information from Conexant.
+2) Some people have problems getting the i2c bus to work.
+   The symptom is that the eeprom cannot be read and the card is
+   unusable. This is probably fixed, but if you have problems
+   then post to the video4linux or ivtv-users mailinglist.
 
-4) VBI (raw or sliced) has not yet been implemented.
+3) VBI (raw or sliced) has not yet been implemented.
 
-5) MPEG indexing is not yet implemented.
+4) MPEG indexing is not yet implemented.
 
-6) The driver is still a bit rough around the edges, this should
+5) The driver is still a bit rough around the edges, this should
    improve over time.
 
 
 Firmware:
 
-The firmware needs to be extracted from the Windows Hauppauge HVR-1600
-driver, available here:
-
-http://hauppauge.lightpath.net/software/install_cd/hauppauge_cd_3.4d1.zip
+You can obtain the firmware files here:
 
-Unzip, then copy the following files to the firmware directory
-and rename them as follows:
+http://dl.ivtvdriver.org/ivtv/firmware/cx18-firmware.tar.gz
 
-Drivers/Driver18/hcw18apu.rom -> v4l-cx23418-apu.fw
-Drivers/Driver18/hcw18enc.rom -> v4l-cx23418-cpu.fw
-Drivers/Driver18/hcw18mlC.rom -> v4l-cx23418-dig.fw
+Untar and copy the .fw files to your firmware directory.
diff --git a/Documentation/video4linux/gspca.txt b/Documentation/video4linux/gspca.txt
new file mode 100644 (file)
index 0000000..0c4880a
--- /dev/null
@@ -0,0 +1,243 @@
+List of the webcams know by gspca.
+
+The modules are:
+       gspca_main      main driver
+       gspca_xxxx      subdriver module with xxxx as follows
+
+xxxx           vend:prod
+----
+spca501                0000:0000       MystFromOri Unknow Camera
+spca501                040a:0002       Kodak DVC-325
+spca500                040a:0300       Kodak EZ200
+zc3xx          041e:041e       Creative WebCam Live!
+spca500                041e:400a       Creative PC-CAM 300
+sunplus                041e:400b       Creative PC-CAM 600
+sunplus                041e:4012       PC-Cam350
+sunplus                041e:4013       Creative Pccam750
+zc3xx          041e:4017       Creative Webcam Mobile PD1090
+spca508                041e:4018       Creative Webcam Vista (PD1100)
+spca561                041e:401a       Creative Webcam Vista (PD1100)
+zc3xx          041e:401c       Creative NX
+spca505                041e:401d       Creative Webcam NX ULTRA
+zc3xx          041e:401e       Creative Nx Pro
+zc3xx          041e:401f       Creative Webcam Notebook PD1171
+pac207         041e:4028       Creative Webcam Vista Plus
+zc3xx          041e:4029       Creative WebCam Vista Pro
+zc3xx          041e:4034       Creative Instant P0620
+zc3xx          041e:4035       Creative Instant P0620D
+zc3xx          041e:4036       Creative Live !
+zc3xx          041e:403a       Creative Nx Pro 2
+spca561                041e:403b       Creative Webcam Vista (VF0010)
+zc3xx          041e:4051       Creative Live!Cam Notebook Pro (VF0250)
+ov519          041e:4052       Creative Live! VISTA IM
+zc3xx          041e:4053       Creative Live!Cam Video IM
+ov519          041e:405f       Creative Live! VISTA VF0330
+ov519          041e:4060       Creative Live! VISTA VF0350
+ov519          041e:4061       Creative Live! VISTA VF0400
+ov519          041e:4064       Creative Live! VISTA VF0420
+ov519          041e:4068       Creative Live! VISTA VF0470
+spca561                0458:7004       Genius VideoCAM Express V2
+sunplus                0458:7006       Genius Dsc 1.3 Smart
+zc3xx          0458:7007       Genius VideoCam V2
+zc3xx          0458:700c       Genius VideoCam V3
+zc3xx          0458:700f       Genius VideoCam Web V2
+sonixj         0458:7025       Genius Eye 311Q
+sonixj         045e:00f5       MicroSoft VX3000
+sonixj         045e:00f7       MicroSoft VX1000
+ov519          045e:028c       Micro$oft xbox cam
+spca508                0461:0815       Micro Innovation IC200
+sunplus                0461:0821       Fujifilm MV-1
+zc3xx          0461:0a00       MicroInnovation WebCam320
+spca500                046d:0890       Logitech QuickCam traveler
+vc032x         046d:0892       Logitech Orbicam
+vc032x         046d:0896       Logitech Orbicam
+zc3xx          046d:08a0       Logitech QC IM
+zc3xx          046d:08a1       Logitech QC IM 0x08A1 +sound
+zc3xx          046d:08a2       Labtec Webcam Pro
+zc3xx          046d:08a3       Logitech QC Chat
+zc3xx          046d:08a6       Logitech QCim
+zc3xx          046d:08a7       Logitech QuickCam Image
+zc3xx          046d:08a9       Logitech Notebook Deluxe
+zc3xx          046d:08aa       Labtec Webcam  Notebook
+zc3xx          046d:08ac       Logitech QuickCam Cool
+zc3xx          046d:08ad       Logitech QCCommunicate STX
+zc3xx          046d:08ae       Logitech QuickCam for Notebooks
+zc3xx          046d:08af       Logitech QuickCam Cool
+zc3xx          046d:08b9       Logitech QC IM ???
+zc3xx          046d:08d7       Logitech QCam STX
+zc3xx          046d:08d9       Logitech QuickCam IM/Connect
+zc3xx          046d:08d8       Logitech Notebook Deluxe
+zc3xx          046d:08da       Logitech QuickCam Messenger
+zc3xx          046d:08dd       Logitech QuickCam for Notebooks
+spca500                046d:0900       Logitech Inc. ClickSmart 310
+spca500                046d:0901       Logitech Inc. ClickSmart 510
+sunplus                046d:0905       Logitech ClickSmart 820
+tv8532         046d:0920       QC Express
+tv8532         046d:0921       Labtec Webcam
+spca561                046d:0928       Logitech QC Express Etch2
+spca561                046d:0929       Labtec Webcam Elch2
+spca561                046d:092a       Logitech QC for Notebook
+spca561                046d:092b       Labtec Webcam Plus
+spca561                046d:092c       Logitech QC chat Elch2
+spca561                046d:092d       Logitech QC Elch2
+spca561                046d:092e       Logitech QC Elch2
+spca561                046d:092f       Logitech QC Elch2
+sunplus                046d:0960       Logitech ClickSmart 420
+sunplus                0471:0322       Philips DMVC1300K
+zc3xx          0471:0325       Philips SPC 200 NC
+zc3xx          0471:0326       Philips SPC 300 NC
+sonixj         0471:0327       Philips SPC 600 NC
+sonixj         0471:0328       Philips SPC 700 NC
+zc3xx          0471:032d       Philips spc210nc
+zc3xx          0471:032e       Philips spc315nc
+sonixj         0471:0330       Philips SPC 710NC
+spca501                0497:c001       Smile International
+sunplus                04a5:3003       Benq DC 1300
+sunplus                04a5:3008       Benq DC 1500
+sunplus                04a5:300a       Benq DC3410
+spca500                04a5:300c       Benq DC1016
+sunplus                04f1:1001       JVC GC A50
+spca561                04fc:0561       Flexcam 100
+sunplus                04fc:500c       Sunplus CA500C
+sunplus                04fc:504a       Aiptek Mini PenCam 1.3
+sunplus                04fc:504b       Maxell MaxPocket LE 1.3
+sunplus                04fc:5330       Digitrex 2110
+sunplus                04fc:5360       Sunplus Generic
+spca500                04fc:7333       PalmPixDC85
+sunplus                04fc:ffff       Pure DigitalDakota
+spca501                0506:00df       3Com HomeConnect Lite
+sunplus                052b:1513       Megapix V4
+tv8532         0545:808b       Veo Stingray
+tv8532         0545:8333       Veo Stingray
+sunplus                0546:3155       Polaroid PDC3070
+sunplus                0546:3191       Polaroid Ion 80
+sunplus                0546:3273       Polaroid PDC2030
+ov519          054c:0154       Sonny toy4
+ov519          054c:0155       Sonny toy5
+zc3xx          055f:c005       Mustek Wcam300A
+spca500                055f:c200       Mustek Gsmart 300
+sunplus                055f:c211       Kowa Bs888e Microcamera
+spca500                055f:c220       Gsmart Mini
+sunplus                055f:c230       Mustek Digicam 330K
+sunplus                055f:c232       Mustek MDC3500
+sunplus                055f:c360       Mustek DV4000 Mpeg4
+sunplus                055f:c420       Mustek gSmart Mini 2
+sunplus                055f:c430       Mustek Gsmart LCD 2
+sunplus                055f:c440       Mustek DV 3000
+sunplus                055f:c520       Mustek gSmart Mini 3
+sunplus                055f:c530       Mustek Gsmart LCD 3
+sunplus                055f:c540       Gsmart D30
+sunplus                055f:c630       Mustek MDC4000
+sunplus                055f:c650       Mustek MDC5500Z
+zc3xx          055f:d003       Mustek WCam300A
+zc3xx          055f:d004       Mustek WCam300 AN
+conex          0572:0041       Creative Notebook cx11646
+ov519          05a9:0519       OmniVision
+ov519          05a9:0530       OmniVision
+ov519          05a9:4519       OmniVision
+ov519          05a9:8519       OmniVision
+sunplus                05da:1018       Digital Dream Enigma 1.3
+stk014         05e1:0893       Syntek DV4000
+spca561                060b:a001       Maxell Compact Pc PM3
+zc3xx          0698:2003       CTX M730V built in
+spca500                06bd:0404       Agfa CL20
+spca500                06be:0800       Optimedia
+sunplus                06d6:0031       Trust 610 LCD PowerC@m Zoom
+spca506                06e1:a190       ADS Instant VCD
+spca508                0733:0110       ViewQuest VQ110
+spca508                0130:0130       Clone Digital Webcam 11043
+spca501                0733:0401       Intel Create and Share
+spca501                0733:0402       ViewQuest M318B
+spca505                0733:0430       Intel PC Camera Pro
+sunplus                0733:1311       Digital Dream Epsilon 1.3
+sunplus                0733:1314       Mercury 2.1MEG Deluxe Classic Cam
+sunplus                0733:2211       Jenoptik jdc 21 LCD
+sunplus                0733:2221       Mercury Digital Pro 3.1p
+sunplus                0733:3261       Concord 3045 spca536a
+sunplus                0733:3281       Cyberpix S550V
+spca506                0734:043b       3DeMon USB Capture aka
+spca500                084d:0003       D-Link DSC-350
+spca500                08ca:0103       Aiptek PocketDV
+sunplus                08ca:0104       Aiptek PocketDVII 1.3
+sunplus                08ca:0106       Aiptek Pocket DV3100+
+sunplus                08ca:2008       Aiptek Mini PenCam 2 M
+sunplus                08ca:2010       Aiptek PocketCam 3M
+sunplus                08ca:2016       Aiptek PocketCam 2 Mega
+sunplus                08ca:2018       Aiptek Pencam SD 2M
+sunplus                08ca:2020       Aiptek Slim 3000F
+sunplus                08ca:2022       Aiptek Slim 3200
+sunplus                08ca:2024       Aiptek DV3500 Mpeg4
+sunplus                08ca:2028       Aiptek PocketCam4M
+sunplus                08ca:2040       Aiptek PocketDV4100M
+sunplus                08ca:2042       Aiptek PocketDV5100
+sunplus                08ca:2050       Medion MD 41437
+sunplus                08ca:2060       Aiptek PocketDV5300
+tv8532         0923:010f       ICM532 cams
+mars           093a:050f       Mars-Semi Pc-Camera
+pac207         093a:2460       PAC207 Qtec Webcam 100
+pac207         093a:2463       Philips spc200nc pac207
+pac207         093a:2464       Labtec Webcam 1200
+pac207         093a:2468       PAC207
+pac207         093a:2470       Genius GF112
+pac207         093a:2471       PAC207 Genius VideoCam ge111
+pac207         093a:2472       PAC207 Genius VideoCam ge110
+pac7311                093a:2600       PAC7311 Typhoon
+pac7311                093a:2601       PAC7311 Phillips SPC610NC
+pac7311                093a:2603       PAC7312
+pac7311                093a:2608       PAC7311 Trust WB-3300p
+pac7311                093a:260e       PAC7311 Gigaware VGA PC Camera, Trust WB-3350p, SIGMA cam 2350
+pac7311                093a:260f       PAC7311 SnakeCam
+pac7311                093a:2621       PAC731x
+zc3xx          0ac8:0302       Z-star Vimicro zc0302
+vc032x         0ac8:0321       Vimicro generic vc0321
+vc032x         0ac8:0323       Vimicro Vc0323
+vc032x         0ac8:0328       A4Tech PK-130MG
+zc3xx          0ac8:301b       Z-Star zc301b
+zc3xx          0ac8:303b       Vimicro 0x303b
+zc3xx          0ac8:305b       Z-star Vimicro zc0305b
+zc3xx          0ac8:307b       Ldlc VC302+Ov7620
+vc032x         0ac8:c001       Sony embedded vimicro
+vc032x         0ac8:c002       Sony embedded vimicro
+spca508                0af9:0010       Hama USB Sightcam 100
+spca508                0af9:0011       Hama USB Sightcam 100
+sonixb         0c45:6001       Genius VideoCAM NB
+sonixb         0c45:6005       Microdia Sweex Mini Webcam
+sonixb         0c45:6007       Sonix sn9c101 + Tas5110D
+sonixb         0c45:6009       spcaCam@120
+sonixb         0c45:600d       spcaCam@120
+sonixb         0c45:6011       Microdia PC Camera (SN9C102)
+sonixb         0c45:6019       Generic Sonix OV7630
+sonixb         0c45:6024       Generic Sonix Tas5130c
+sonixb         0c45:6025       Xcam Shanga
+sonixb         0c45:6028       Sonix Btc Pc380
+sonixb         0c45:6029       spcaCam@150
+sonixb         0c45:602c       Generic Sonix OV7630
+sonixb         0c45:602d       LIC-200 LG
+sonixb         0c45:602e       Genius VideoCam Messenger
+sonixj         0c45:6040       Speed NVC 350K
+sonixj         0c45:607c       Sonix sn9c102p Hv7131R
+sonixj         0c45:60c0       Sangha Sn535
+sonixj         0c45:60ec       SN9C105+MO4000
+sonixj         0c45:60fb       Surfer NoName
+sonixj         0c45:60fc       LG-LIC300
+sonixj         0c45:612a       Avant Camera
+sonixj         0c45:612c       Typhoon Rasy Cam 1.3MPix
+sonixj         0c45:6130       Sonix Pccam
+sonixj         0c45:6138       Sn9c120 Mo4000
+sonixj         0c45:613b       Surfer SN-206
+sonixj         0c45:613c       Sonix Pccam168
+sunplus                0d64:0303       Sunplus FashionCam DXG
+etoms          102c:6151       Qcam Sangha CIF
+etoms          102c:6251       Qcam xxxxxx VGA
+zc3xx          10fd:0128       Typhoon Webshot II USB 300k 0x0128
+spca561                10fd:7e50       FlyCam Usb 100
+zc3xx          10fd:8050       Typhoon Webshot II USB 300k
+spca501                1776:501c       Arowana 300K CMOS Camera
+t613           17a1:0128       T613/TAS5130A
+vc032x         17ef:4802       Lenovo Vc0323+MI1310_SOC
+pac207         2001:f115       D-Link DSB-C120
+spca500                2899:012c       Toptro Industrial
+spca508                8086:0110       Intel Easy PC Camera
+spca500                8086:0630       Intel Pocket PC Camera
+spca506                99fa:8988       Grandtec V.cap
+spca561                abcd:cdee       Petcam
index 7a7803b5d4976353b8a475a9eebf98a9e94da52a..93ea201f426cad53997ee815c69c1679bbe70b39 100644 (file)
@@ -38,7 +38,6 @@ config VIDEO_ALLOW_V4L1
        bool "Enable Video For Linux API 1 (DEPRECATED)"
        depends on VIDEO_DEV && VIDEO_V4L2_COMMON
        default VIDEO_DEV && VIDEO_V4L2_COMMON
-       select VIDEO_V4L1_COMPAT
        ---help---
          Enables drivers based on the legacy V4L1 API.
 
@@ -49,9 +48,9 @@ config VIDEO_ALLOW_V4L1
          If you are unsure as to whether this is required, answer Y.
 
 config VIDEO_V4L1_COMPAT
-       bool "Enable Video For Linux API 1 compatible Layer"
+       bool "Enable Video For Linux API 1 compatible Layer" if !VIDEO_ALLOW_V4L1
        depends on VIDEO_DEV
-       default VIDEO_DEV
+       default y
        ---help---
          Enables a compatibility API used by most V4L2 devices to allow
          its usage with legacy applications that supports only V4L1 api.
index 2665052079258c6d015cc42f7109bfd1d11f6b8c..16792a68a44951e1bc1c2cbb1c9da27a2b6b2722 100644 (file)
@@ -66,7 +66,6 @@ void ir_input_init(struct input_dev *dev, struct ir_input_state *ir,
        if (ir_codes)
                memcpy(ir->ir_codes, ir_codes, sizeof(ir->ir_codes));
 
-
        dev->keycode     = ir->ir_codes;
        dev->keycodesize = sizeof(IR_KEYTAB_TYPE);
        dev->keycodemax  = IR_KEYTAB_SIZE;
@@ -78,6 +77,7 @@ void ir_input_init(struct input_dev *dev, struct ir_input_state *ir,
        if (repeat)
                set_bit(EV_REP, dev->evbit);
 }
+EXPORT_SYMBOL_GPL(ir_input_init);
 
 void ir_input_nokey(struct input_dev *dev, struct ir_input_state *ir)
 {
@@ -86,6 +86,7 @@ void ir_input_nokey(struct input_dev *dev, struct ir_input_state *ir)
                ir_input_key_event(dev,ir);
        }
 }
+EXPORT_SYMBOL_GPL(ir_input_nokey);
 
 void ir_input_keydown(struct input_dev *dev, struct ir_input_state *ir,
                      u32 ir_key, u32 ir_raw)
@@ -104,6 +105,7 @@ void ir_input_keydown(struct input_dev *dev, struct ir_input_state *ir,
                ir_input_key_event(dev,ir);
        }
 }
+EXPORT_SYMBOL_GPL(ir_input_keydown);
 
 /* -------------------------------------------------------------------------- */
 /* extract mask bits out of data and pack them into the result */
@@ -122,6 +124,7 @@ u32 ir_extract_bits(u32 data, u32 mask)
 
        return value;
 }
+EXPORT_SYMBOL_GPL(ir_extract_bits);
 
 static int inline getbit(u32 *samples, int bit)
 {
@@ -146,6 +149,7 @@ int ir_dump_samples(u32 *samples, int count)
        printk("\n");
        return 0;
 }
+EXPORT_SYMBOL_GPL(ir_dump_samples);
 
 /* decode raw samples, pulse distance coding used by NEC remotes */
 int ir_decode_pulsedistance(u32 *samples, int count, int low, int high)
@@ -212,6 +216,7 @@ int ir_decode_pulsedistance(u32 *samples, int count, int low, int high)
 
        return value;
 }
+EXPORT_SYMBOL_GPL(ir_decode_pulsedistance);
 
 /* decode raw samples, biphase coding, used by rc5 for example */
 int ir_decode_biphase(u32 *samples, int count, int low, int high)
@@ -253,6 +258,7 @@ int ir_decode_biphase(u32 *samples, int count, int low, int high)
        }
        return value;
 }
+EXPORT_SYMBOL_GPL(ir_decode_biphase);
 
 /* RC5 decoding stuff, moved from bttv-input.c to share it with
  * saa7134 */
@@ -353,6 +359,7 @@ void ir_rc5_timer_end(unsigned long data)
                }
        }
 }
+EXPORT_SYMBOL_GPL(ir_rc5_timer_end);
 
 void ir_rc5_timer_keyup(unsigned long data)
 {
@@ -361,21 +368,4 @@ void ir_rc5_timer_keyup(unsigned long data)
        dprintk(1, "ir-common: key released\n");
        ir_input_nokey(ir->dev, &ir->ir);
 }
-
-EXPORT_SYMBOL_GPL(ir_input_init);
-EXPORT_SYMBOL_GPL(ir_input_nokey);
-EXPORT_SYMBOL_GPL(ir_input_keydown);
-
-EXPORT_SYMBOL_GPL(ir_extract_bits);
-EXPORT_SYMBOL_GPL(ir_dump_samples);
-EXPORT_SYMBOL_GPL(ir_decode_biphase);
-EXPORT_SYMBOL_GPL(ir_decode_pulsedistance);
-
-EXPORT_SYMBOL_GPL(ir_rc5_timer_end);
 EXPORT_SYMBOL_GPL(ir_rc5_timer_keyup);
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
index 89c7660b85d66cf4dc84c6da4baa9ba14461bd0e..d01965e96927b2477530ed666d86001eb68be946 100644 (file)
@@ -233,7 +233,7 @@ void saa7146_pgtable_free(struct pci_dev *pci, struct saa7146_pgtable *pt)
 
 int saa7146_pgtable_alloc(struct pci_dev *pci, struct saa7146_pgtable *pt)
 {
-       u32          *cpu;
+       __le32       *cpu;
        dma_addr_t   dma_addr;
 
        cpu = pci_alloc_consistent(pci, PAGE_SIZE, &dma_addr);
@@ -250,7 +250,7 @@ int saa7146_pgtable_alloc(struct pci_dev *pci, struct saa7146_pgtable *pt)
 int saa7146_pgtable_build_single(struct pci_dev *pci, struct saa7146_pgtable *pt,
        struct scatterlist *list, int sglen  )
 {
-       u32 *ptr, fill;
+       __le32 *ptr, fill;
        int nr_pages = 0;
        int i,p;
 
index 9c905399a233028ba9872cc503efa1f6ef8b699f..05bde9ccb770b377eb327409a707ef38b5e25a17 100644 (file)
@@ -338,7 +338,7 @@ static void calculate_clipping_registers_rect(struct saa7146_dev *dev, struct sa
        struct saa7146_video_dma *vdma2, u32* clip_format, u32* arbtr_ctrl, enum v4l2_field field)
 {
        struct saa7146_vv *vv = dev->vv_data;
-       u32 *clipping = vv->d_clipping.cpu_addr;
+       __le32 *clipping = vv->d_clipping.cpu_addr;
 
        int width = fh->ov.win.w.width;
        int height =  fh->ov.win.w.height;
index 35b01ec40a5139d6e48b675338bd057360a0a79c..c11da4d09cd0971d17487371fd6d1b7967894ac4 100644 (file)
@@ -24,7 +24,7 @@ static inline u32 saa7146_i2c_status(struct saa7146_dev *dev)
    sent through the saa7146. have a look at the specifications p. 122 ff
    to understand this. it returns the number of u32s to send, or -1
    in case of an error. */
-static int saa7146_i2c_msg_prepare(const struct i2c_msg *m, int num, u32 *op)
+static int saa7146_i2c_msg_prepare(const struct i2c_msg *m, int num, __le32 *op)
 {
        int h1, h2;
        int i, j, addr;
@@ -47,7 +47,7 @@ static int saa7146_i2c_msg_prepare(const struct i2c_msg *m, int num, u32 *op)
        }
 
        /* be careful: clear out the i2c-mem first */
-       memset(op,0,sizeof(u32)*mem);
+       memset(op,0,sizeof(__le32)*mem);
 
        /* loop through all messages */
        for(i = 0; i < num; i++) {
@@ -57,16 +57,16 @@ static int saa7146_i2c_msg_prepare(const struct i2c_msg *m, int num, u32 *op)
                   so we have to perform a translation */
                addr = (m[i].addr*2) + ( (0 != (m[i].flags & I2C_M_RD)) ? 1 : 0);
                h1 = op_count/3; h2 = op_count%3;
-               op[h1] |= (         (u8)addr << ((3-h2)*8));
-               op[h1] |= (SAA7146_I2C_START << ((3-h2)*2));
+               op[h1] |= cpu_to_le32(      (u8)addr << ((3-h2)*8));
+               op[h1] |= cpu_to_le32(SAA7146_I2C_START << ((3-h2)*2));
                op_count++;
 
                /* loop through all bytes of message i */
                for(j = 0; j < m[i].len; j++) {
                        /* insert the data bytes */
                        h1 = op_count/3; h2 = op_count%3;
-                       op[h1] |= ( (u32)((u8)m[i].buf[j]) << ((3-h2)*8));
-                       op[h1] |= (       SAA7146_I2C_CONT << ((3-h2)*2));
+                       op[h1] |= cpu_to_le32( (u32)((u8)m[i].buf[j]) << ((3-h2)*8));
+                       op[h1] |= cpu_to_le32(       SAA7146_I2C_CONT << ((3-h2)*2));
                        op_count++;
                }
 
@@ -75,9 +75,9 @@ static int saa7146_i2c_msg_prepare(const struct i2c_msg *m, int num, u32 *op)
        /* have a look at the last byte inserted:
          if it was: ...CONT change it to ...STOP */
        h1 = (op_count-1)/3; h2 = (op_count-1)%3;
-       if ( SAA7146_I2C_CONT == (0x3 & (op[h1] >> ((3-h2)*2))) ) {
-               op[h1] &= ~(0x2 << ((3-h2)*2));
-               op[h1] |= (SAA7146_I2C_STOP << ((3-h2)*2));
+       if ( SAA7146_I2C_CONT == (0x3 & (le32_to_cpu(op[h1]) >> ((3-h2)*2))) ) {
+               op[h1] &= ~cpu_to_le32(0x2 << ((3-h2)*2));
+               op[h1] |= cpu_to_le32(SAA7146_I2C_STOP << ((3-h2)*2));
        }
 
        /* return the number of u32s to send */
@@ -88,7 +88,7 @@ static int saa7146_i2c_msg_prepare(const struct i2c_msg *m, int num, u32 *op)
    which bytes were read through the adapter and write them back to the corresponding
    i2c-message. but instead, we simply write back all bytes.
    fixme: this could be improved. */
-static int saa7146_i2c_msg_cleanup(const struct i2c_msg *m, int num, u32 *op)
+static int saa7146_i2c_msg_cleanup(const struct i2c_msg *m, int num, __le32 *op)
 {
        int i, j;
        int op_count = 0;
@@ -101,7 +101,7 @@ static int saa7146_i2c_msg_cleanup(const struct i2c_msg *m, int num, u32 *op)
                /* loop throgh all bytes of message i */
                for(j = 0; j < m[i].len; j++) {
                        /* write back all bytes that could have been read */
-                       m[i].buf[j] = (op[op_count/3] >> ((3-(op_count%3))*8));
+                       m[i].buf[j] = (le32_to_cpu(op[op_count/3]) >> ((3-(op_count%3))*8));
                        op_count++;
                }
        }
@@ -174,7 +174,7 @@ static int saa7146_i2c_reset(struct saa7146_dev *dev)
 /* this functions writes out the data-byte 'dword' to the i2c-device.
    it returns 0 if ok, -1 if the transfer failed, -2 if the transfer
    failed badly (e.g. address error) */
-static int saa7146_i2c_writeout(struct saa7146_dev *dev, u32* dword, int short_delay)
+static int saa7146_i2c_writeout(struct saa7146_dev *dev, __le32 *dword, int short_delay)
 {
        u32 status = 0, mc2 = 0;
        int trial = 0;
@@ -186,7 +186,7 @@ static int saa7146_i2c_writeout(struct saa7146_dev *dev, u32* dword, int short_d
        if( 0 != (SAA7146_USE_I2C_IRQ & dev->ext->flags)) {
 
                saa7146_write(dev, I2C_STATUS,   dev->i2c_bitrate);
-               saa7146_write(dev, I2C_TRANSFER, *dword);
+               saa7146_write(dev, I2C_TRANSFER, le32_to_cpu(*dword));
 
                dev->i2c_op = 1;
                SAA7146_ISR_CLEAR(dev, MASK_16|MASK_17);
@@ -209,7 +209,7 @@ static int saa7146_i2c_writeout(struct saa7146_dev *dev, u32* dword, int short_d
                status = saa7146_read(dev, I2C_STATUS);
        } else {
                saa7146_write(dev, I2C_STATUS,   dev->i2c_bitrate);
-               saa7146_write(dev, I2C_TRANSFER, *dword);
+               saa7146_write(dev, I2C_TRANSFER, le32_to_cpu(*dword));
                saa7146_write(dev, MC2, (MASK_00 | MASK_16));
 
                /* do not poll for i2c-status before upload is complete */
@@ -282,7 +282,7 @@ static int saa7146_i2c_writeout(struct saa7146_dev *dev, u32* dword, int short_d
        }
 
        /* read back data, just in case we were reading ... */
-       *dword = saa7146_read(dev, I2C_TRANSFER);
+       *dword = cpu_to_le32(saa7146_read(dev, I2C_TRANSFER));
 
        DEB_I2C(("after: 0x%08x\n",*dword));
        return 0;
@@ -291,7 +291,7 @@ static int saa7146_i2c_writeout(struct saa7146_dev *dev, u32* dword, int short_d
 static int saa7146_i2c_transfer(struct saa7146_dev *dev, const struct i2c_msg *msgs, int num, int retries)
 {
        int i = 0, count = 0;
-       u32* buffer = dev->d_i2c.cpu_addr;
+       __le32 *buffer = dev->d_i2c.cpu_addr;
        int err = 0;
        int address_err = 0;
        int short_delay = 0;
@@ -376,7 +376,7 @@ out:
        /* another bug in revision 0: the i2c-registers get uploaded randomly by other
           uploads, so we better clear them out before continueing */
        if( 0 == dev->revision ) {
-               u32 zero = 0;
+               __le32 zero = 0;
                saa7146_i2c_reset(dev);
                if( 0 != saa7146_i2c_writeout(dev, &zero, short_delay)) {
                        INFO(("revision 0 error. this should never happen.\n"));
index 3cbc6ebbe64939b5d7239dd40caa4b6ab38c58f0..a5e62750eea3cb59b976aec917e6b88ab2a1ed87 100644 (file)
@@ -605,8 +605,8 @@ static int saa7146_pgtable_build(struct saa7146_dev *dev, struct saa7146_buf *bu
                struct saa7146_pgtable *pt1 = &buf->pt[0];
                struct saa7146_pgtable *pt2 = &buf->pt[1];
                struct saa7146_pgtable *pt3 = &buf->pt[2];
-               u32  *ptr1, *ptr2, *ptr3;
-               u32 fill;
+               __le32  *ptr1, *ptr2, *ptr3;
+               __le32 fill;
 
                int size = buf->fmt->width*buf->fmt->height;
                int i,p,m1,m2,m3,o1,o2;
index 85482960d0127a66308afec8d331d36d6258112e..850d5689b14dcded97ca5ac74adcb3db913da214 100644 (file)
@@ -34,6 +34,7 @@ config MEDIA_TUNER
 menuconfig MEDIA_TUNER_CUSTOMIZE
        bool "Customize analog and hybrid tuner modules to build"
        depends on MEDIA_TUNER
+       default n
        help
          This allows the user to deselect tuner drivers unnecessary
          for their hardware from the build. Use this option with care
index 83e7561960c185aa7e92a43739bd22ae169677fd..ab14ceb9e0ce0c2f6c592f724fed6ebe7c95012e 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    tda18271-tables.c - driver for the Philips / NXP TDA18271 silicon tuner
+    tda18271-maps.c - driver for the Philips / NXP TDA18271 silicon tuner
 
     Copyright (C) 2007, 2008 Michael Krufky <mkrufky@linuxtv.org>
 
index 30eb07b7f9efb13b2c0a071c7b1be84c3f450d0c..4dd1d2421cc529fde892a6204b02dd725d25f277 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/delay.h>
 #include <media/tuner.h>
 #include <linux/mutex.h>
+#include <asm/unaligned.h>
 #include "tuner-i2c.h"
 #include "tuner-xc2028.h"
 #include "tuner-xc2028-types.h"
@@ -292,10 +293,10 @@ static int load_all_firmwares(struct dvb_frontend *fe)
        name[sizeof(name) - 1] = 0;
        p += sizeof(name) - 1;
 
-       priv->firm_version = le16_to_cpu(*(__u16 *) p);
+       priv->firm_version = get_unaligned_le16(p);
        p += 2;
 
-       n_array = le16_to_cpu(*(__u16 *) p);
+       n_array = get_unaligned_le16(p);
        p += 2;
 
        tuner_info("Loading %d firmware images from %s, type: %s, ver %d.%d\n",
@@ -324,26 +325,26 @@ static int load_all_firmwares(struct dvb_frontend *fe)
                }
 
                /* Checks if there's enough bytes to read */
-               if (p + sizeof(type) + sizeof(id) + sizeof(size) > endp) {
-                       tuner_err("Firmware header is incomplete!\n");
-                       goto corrupt;
-               }
+               if (endp - p < sizeof(type) + sizeof(id) + sizeof(size))
+                       goto header;
 
-               type = le32_to_cpu(*(__u32 *) p);
+               type = get_unaligned_le32(p);
                p += sizeof(type);
 
-               id = le64_to_cpu(*(v4l2_std_id *) p);
+               id = get_unaligned_le64(p);
                p += sizeof(id);
 
                if (type & HAS_IF) {
-                       int_freq = le16_to_cpu(*(__u16 *) p);
+                       int_freq = get_unaligned_le16(p);
                        p += sizeof(int_freq);
+                       if (endp - p < sizeof(size))
+                               goto header;
                }
 
-               size = le32_to_cpu(*(__u32 *) p);
+               size = get_unaligned_le32(p);
                p += sizeof(size);
 
-               if ((!size) || (size + p > endp)) {
+               if (!size || size > endp - p) {
                        tuner_err("Firmware type ");
                        dump_firm_type(type);
                        printk("(%x), id %llx is corrupted "
@@ -382,6 +383,8 @@ static int load_all_firmwares(struct dvb_frontend *fe)
 
        goto done;
 
+header:
+       tuner_err("Firmware header is incomplete!\n");
 corrupt:
        rc = -EINVAL;
        tuner_err("Error: firmware file is corrupted!\n");
index 4878d6477a8c58ad731d0a2498a50e47b9b0ab8b..5f99de0ad61252545567b5bfa01a38ff9ff92ede 100644 (file)
@@ -36,6 +36,10 @@ static int debug;
 module_param(debug, int, 0644);
 MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
 
+static int xc5000_load_fw_on_attach;
+module_param_named(init_fw, xc5000_load_fw_on_attach, int, 0644);
+MODULE_PARM_DESC(init_fw, "Load firmware during driver initialization.");
+
 #define dprintk(level,fmt, arg...) if (debug >= level) \
        printk(KERN_INFO "%s: " fmt, "xc5000", ## arg)
 
@@ -972,6 +976,9 @@ struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe,
 
        fe->tuner_priv = priv;
 
+       if (xc5000_load_fw_on_attach)
+               xc5000_init(fe);
+
        return fe;
 }
 EXPORT_SYMBOL(xc5000_attach);
index 7b21b49f1945ad56430f6bf16ac99bdaed89f52b..8bc1445bd33bda02d7c4d1bc6e5413aa375b51a7 100644 (file)
@@ -21,6 +21,7 @@ source "drivers/media/dvb/dvb-usb/Kconfig"
 source "drivers/media/dvb/ttusb-budget/Kconfig"
 source "drivers/media/dvb/ttusb-dec/Kconfig"
 source "drivers/media/dvb/cinergyT2/Kconfig"
+source "drivers/media/dvb/siano/Kconfig"
 
 comment "Supported FlexCopII (B2C2) Adapters"
        depends on DVB_CORE && (PCI || USB) && I2C
index a7ad0841e6fc049a662965718373c7bd468ef97f..d6ba4d19520189b1606d7bedc595c26283e75518 100644 (file)
@@ -2,4 +2,4 @@
 # Makefile for the kernel multimedia device drivers.
 #
 
-obj-y        := dvb-core/ frontends/ ttpci/ ttusb-dec/ ttusb-budget/ b2c2/ bt8xx/ cinergyT2/ dvb-usb/ pluto2/
+obj-y        := dvb-core/ frontends/ ttpci/ ttusb-dec/ ttusb-budget/ b2c2/ bt8xx/ cinergyT2/ dvb-usb/ pluto2/ siano/
index 375fd2892a11098bd01ec2253436e4a53affabfc..d19b59299d78adca911089a28cebdeff9661250a 100644 (file)
@@ -128,7 +128,7 @@ struct bt878 {
        dma_addr_t buf_dma;
 
        u32 risc_size;
-       u32 *risc_cpu;
+       __le32 *risc_cpu;
        dma_addr_t risc_dma;
        u32 risc_pos;
 
index b0d347daae47a889ba54f89b938ecd8dd3ee46a5..eb91fd808c16307a9d7259f960ff89be70efbd96 100644 (file)
@@ -247,7 +247,7 @@ struct dmx_demux {
        void* priv;                  /* Pointer to private data of the API client */
        int (*open) (struct dmx_demux* demux);
        int (*close) (struct dmx_demux* demux);
-       int (*write) (struct dmx_demux* demux, const charbuf, size_t count);
+       int (*write) (struct dmx_demux* demux, const char __user *buf, size_t count);
        int (*allocate_ts_feed) (struct dmx_demux* demux,
                                 struct dmx_ts_feed** feed,
                                 dmx_ts_cb callback);
index df5bef6a2517821c013c1aedbcae843ff7b278fa..1cf9fcb6f5149ff882af75cc54b0a5225656ac74 100644 (file)
@@ -96,7 +96,7 @@ static ssize_t dvb_dmxdev_buffer_read(struct dvb_ringbuffer *src,
                if (avail > todo)
                        avail = todo;
 
-               ret = dvb_ringbuffer_read(src, (u8 *)buf, avail, 1);
+               ret = dvb_ringbuffer_read_user(src, buf, avail);
                if (ret < 0)
                        break;
 
index 588fbe105c27ccc298bf01d9fc75f0450cffae7f..8e5dd7b1f034b052b8401ad1f6c646707a7127b9 100644 (file)
@@ -1357,7 +1357,7 @@ static int dvb_ca_en50221_io_read_condition(struct dvb_ca_private *ca,
 
                idx = dvb_ringbuffer_pkt_next(&ca->slot_info[slot].rx_buffer, -1, &fraglen);
                while (idx != -1) {
-                       dvb_ringbuffer_pkt_read(&ca->slot_info[slot].rx_buffer, idx, 0, hdr, 2, 0);
+                       dvb_ringbuffer_pkt_read(&ca->slot_info[slot].rx_buffer, idx, 0, hdr, 2);
                        if (connection_id == -1)
                                connection_id = hdr[0];
                        if ((hdr[0] == connection_id) && ((hdr[1] & 0x80) == 0)) {
@@ -1438,7 +1438,7 @@ static ssize_t dvb_ca_en50221_io_read(struct file *file, char __user * buf,
                        goto exit;
                }
 
-               dvb_ringbuffer_pkt_read(&ca->slot_info[slot].rx_buffer, idx, 0, hdr, 2, 0);
+               dvb_ringbuffer_pkt_read(&ca->slot_info[slot].rx_buffer, idx, 0, hdr, 2);
                if (connection_id == -1)
                        connection_id = hdr[0];
                if (hdr[0] == connection_id) {
@@ -1449,8 +1449,8 @@ static ssize_t dvb_ca_en50221_io_read(struct file *file, char __user * buf,
                                        fraglen -= 2;
                                }
 
-                               if ((status = dvb_ringbuffer_pkt_read(&ca->slot_info[slot].rx_buffer, idx, 2,
-                                                                     (u8 *)buf + pktlen, fraglen, 1)) < 0) {
+                               if ((status = dvb_ringbuffer_pkt_read_user(&ca->slot_info[slot].rx_buffer, idx, 2,
+                                                                     buf + pktlen, fraglen)) < 0) {
                                        goto exit;
                                }
                                pktlen += fraglen;
index 934e15fffc565f4660dfc5954a49d1346748d315..e2eca0b1fe7cfad6dc1215aa6d2bd1247497b93e 100644 (file)
@@ -1056,16 +1056,27 @@ static int dvbdmx_close(struct dmx_demux *demux)
        return 0;
 }
 
-static int dvbdmx_write(struct dmx_demux *demux, const char *buf, size_t count)
+static int dvbdmx_write(struct dmx_demux *demux, const char __user *buf, size_t count)
 {
        struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
+       void *p;
 
        if ((!demux->frontend) || (demux->frontend->source != DMX_MEMORY_FE))
                return -EINVAL;
 
-       if (mutex_lock_interruptible(&dvbdemux->mutex))
+       p = kmalloc(count, GFP_USER);
+       if (!p)
+               return -ENOMEM;
+       if (copy_from_user(p, buf, count)) {
+               kfree(p);
+               return -EFAULT;
+       }
+       if (mutex_lock_interruptible(&dvbdemux->mutex)) {
+               kfree(p);
                return -ERESTARTSYS;
-       dvb_dmx_swfilter(dvbdemux, (u8 *)buf, count);
+       }
+       dvb_dmx_swfilter(dvbdemux, p, count);
+       kfree(p);
        mutex_unlock(&dvbdemux->mutex);
 
        if (signal_pending(current))
index c2c033722a93beb8509bad1cc952a284f729391e..c93019ca519e677e8a979e5feab9e22f93a5dc0d 100644 (file)
@@ -606,7 +606,7 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len )
                        if (priv->ule_dbit) {
                                /* Set D-bit for CRC32 verification,
                                 * if it was set originally. */
-                               ulen |= 0x0080;
+                               ulen |= htons(0x8000);
                        }
 
                        ule_crc = iov_crc32(ule_crc, iov, 3);
index 872985b7912d3db12036abd78380a012012033e1..584bbd194dc8a18ee65b327949bcc7a67dee8c48 100644 (file)
@@ -107,35 +107,43 @@ void dvb_ringbuffer_flush_spinlock_wakeup(struct dvb_ringbuffer *rbuf)
        wake_up(&rbuf->queue);
 }
 
-
-
-ssize_t dvb_ringbuffer_read(struct dvb_ringbuffer *rbuf, u8 *buf, size_t len, int usermem)
+ssize_t dvb_ringbuffer_read_user(struct dvb_ringbuffer *rbuf, u8 __user *buf, size_t len)
 {
        size_t todo = len;
        size_t split;
 
        split = (rbuf->pread + len > rbuf->size) ? rbuf->size - rbuf->pread : 0;
        if (split > 0) {
-               if (!usermem)
-                       memcpy(buf, rbuf->data+rbuf->pread, split);
-               else
-                       if (copy_to_user(buf, rbuf->data+rbuf->pread, split))
-                               return -EFAULT;
+               if (copy_to_user(buf, rbuf->data+rbuf->pread, split))
+                       return -EFAULT;
                buf += split;
                todo -= split;
                rbuf->pread = 0;
        }
-       if (!usermem)
-               memcpy(buf, rbuf->data+rbuf->pread, todo);
-       else
-               if (copy_to_user(buf, rbuf->data+rbuf->pread, todo))
-                       return -EFAULT;
+       if (copy_to_user(buf, rbuf->data+rbuf->pread, todo))
+               return -EFAULT;
 
        rbuf->pread = (rbuf->pread + todo) % rbuf->size;
 
        return len;
 }
 
+void dvb_ringbuffer_read(struct dvb_ringbuffer *rbuf, u8 *buf, size_t len)
+{
+       size_t todo = len;
+       size_t split;
+
+       split = (rbuf->pread + len > rbuf->size) ? rbuf->size - rbuf->pread : 0;
+       if (split > 0) {
+               memcpy(buf, rbuf->data+rbuf->pread, split);
+               buf += split;
+               todo -= split;
+               rbuf->pread = 0;
+       }
+       memcpy(buf, rbuf->data+rbuf->pread, todo);
+
+       rbuf->pread = (rbuf->pread + todo) % rbuf->size;
+}
 
 
 ssize_t dvb_ringbuffer_write(struct dvb_ringbuffer *rbuf, const u8 *buf, size_t len)
@@ -171,8 +179,8 @@ ssize_t dvb_ringbuffer_pkt_write(struct dvb_ringbuffer *rbuf, u8* buf, size_t le
        return status;
 }
 
-ssize_t dvb_ringbuffer_pkt_read(struct dvb_ringbuffer *rbuf, size_t idx,
-                               int offset, u8* buf, size_t len, int usermem)
+ssize_t dvb_ringbuffer_pkt_read_user(struct dvb_ringbuffer *rbuf, size_t idx,
+                               int offset, u8 __user *buf, size_t len)
 {
        size_t todo;
        size_t split;
@@ -187,21 +195,40 @@ ssize_t dvb_ringbuffer_pkt_read(struct dvb_ringbuffer *rbuf, size_t idx,
        todo = len;
        split = ((idx + len) > rbuf->size) ? rbuf->size - idx : 0;
        if (split > 0) {
-               if (!usermem)
-                       memcpy(buf, rbuf->data+idx, split);
-               else
-                       if (copy_to_user(buf, rbuf->data+idx, split))
-                               return -EFAULT;
+               if (copy_to_user(buf, rbuf->data+idx, split))
+                       return -EFAULT;
                buf += split;
                todo -= split;
                idx = 0;
        }
-       if (!usermem)
-               memcpy(buf, rbuf->data+idx, todo);
-       else
-               if (copy_to_user(buf, rbuf->data+idx, todo))
-                       return -EFAULT;
+       if (copy_to_user(buf, rbuf->data+idx, todo))
+               return -EFAULT;
+
+       return len;
+}
 
+ssize_t dvb_ringbuffer_pkt_read(struct dvb_ringbuffer *rbuf, size_t idx,
+                               int offset, u8* buf, size_t len)
+{
+       size_t todo;
+       size_t split;
+       size_t pktlen;
+
+       pktlen = rbuf->data[idx] << 8;
+       pktlen |= rbuf->data[(idx + 1) % rbuf->size];
+       if (offset > pktlen) return -EINVAL;
+       if ((offset + len) > pktlen) len = pktlen - offset;
+
+       idx = (idx + DVB_RINGBUFFER_PKTHDRSIZE + offset) % rbuf->size;
+       todo = len;
+       split = ((idx + len) > rbuf->size) ? rbuf->size - idx : 0;
+       if (split > 0) {
+               memcpy(buf, rbuf->data+idx, split);
+               buf += split;
+               todo -= split;
+               idx = 0;
+       }
+       memcpy(buf, rbuf->data+idx, todo);
        return len;
 }
 
@@ -266,5 +293,6 @@ EXPORT_SYMBOL(dvb_ringbuffer_empty);
 EXPORT_SYMBOL(dvb_ringbuffer_free);
 EXPORT_SYMBOL(dvb_ringbuffer_avail);
 EXPORT_SYMBOL(dvb_ringbuffer_flush_spinlock_wakeup);
+EXPORT_SYMBOL(dvb_ringbuffer_read_user);
 EXPORT_SYMBOL(dvb_ringbuffer_read);
 EXPORT_SYMBOL(dvb_ringbuffer_write);
index 890826262966c16c709815d83e1e08ac68c08058..41f04dae69b618badd91e783a1ad463b6bd0a393 100644 (file)
@@ -61,7 +61,7 @@ struct dvb_ringbuffer {
 **     *** read min. 1000, max. <bufsize> bytes ***
 **     avail = dvb_ringbuffer_avail(rbuf);
 **     if (avail >= 1000)
-**         count = dvb_ringbuffer_read(rbuf, buffer, min(avail, bufsize), 0);
+**         count = dvb_ringbuffer_read(rbuf, buffer, min(avail, bufsize));
 **     else
 **         ...
 **
@@ -114,8 +114,10 @@ extern void dvb_ringbuffer_flush_spinlock_wakeup(struct dvb_ringbuffer *rbuf);
 ** <usermem> specifies whether <buf> resides in user space
 ** returns number of bytes transferred or -EFAULT
 */
-extern ssize_t dvb_ringbuffer_read(struct dvb_ringbuffer *rbuf, u8 *buf,
-                                  size_t len, int usermem);
+extern ssize_t dvb_ringbuffer_read_user(struct dvb_ringbuffer *rbuf,
+                                  u8 __user *buf, size_t len);
+extern void dvb_ringbuffer_read(struct dvb_ringbuffer *rbuf,
+                                  u8 *buf, size_t len);
 
 
 /* write routines & macros */
@@ -157,8 +159,10 @@ extern ssize_t dvb_ringbuffer_pkt_write(struct dvb_ringbuffer *rbuf, u8* buf,
  * <usermem> Set to 1 if <buf> is in userspace.
  * returns Number of bytes read, or -EFAULT.
  */
+extern ssize_t dvb_ringbuffer_pkt_read_user(struct dvb_ringbuffer *rbuf, size_t idx,
+                                      int offset, u8 __user *buf, size_t len);
 extern ssize_t dvb_ringbuffer_pkt_read(struct dvb_ringbuffer *rbuf, size_t idx,
-                                      int offset, u8* buf, size_t len, int usermem);
+                                      int offset, u8 *buf, size_t len);
 
 /**
  * Dispose of a packet in the ring buffer.
index f00a0eb40420942284e53e2c78c26b402d4991b1..a577c0f89f6745c421426a9b49f2af6888ba285e 100644 (file)
@@ -76,6 +76,7 @@ config DVB_USB_DIB0700
        select DVB_DIB3000MC
        select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE
        select MEDIA_TUNER_MT2266 if !DVB_FE_CUSTOMISE
+       select MEDIA_TUNER_XC2028 if !DVB_FE_CUSTOMISE
        select DVB_TUNER_DIB0070
        help
          Support for USB2.0/1.1 DVB receivers based on the DiB0700 USB bridge. The
@@ -107,6 +108,8 @@ config DVB_USB_CXUSB
        select DVB_MT352 if !DVB_FE_CUSTOMISE
        select DVB_ZL10353 if !DVB_FE_CUSTOMISE
        select MEDIA_TUNER_SIMPLE if !DVB_FE_CUSTOMISE
+       select MEDIA_TUNER_XC2028 if !DVB_FE_CUSTOMISE
+       select MEDIA_TUNER_MXL5005S if !DVB_FE_CUSTOMISE
        help
          Say Y here to support the Conexant USB2.0 hybrid reference design.
          Currently, only DVB and ATSC modes are supported, analog mode
@@ -120,6 +123,8 @@ config DVB_USB_M920X
        depends on DVB_USB
        select DVB_MT352 if !DVB_FE_CUSTOMISE
        select MEDIA_TUNER_QT1010 if !DVB_FE_CUSTOMISE
+       select MEDIA_TUNER_TDA827X if !DVB_FE_CUSTOMISE
+       select DVB_TDA1004X if !DVB_FE_CUSTOMISE
        help
          Say Y here to support the MSI Mega Sky 580 USB2.0 DVB-T receiver.
          Currently, only devices with a product id of
@@ -241,3 +246,13 @@ config DVB_USB_AF9005_REMOTE
          Say Y here to support the default remote control decoding for the
          Afatech AF9005 based receiver.
 
+config DVB_USB_ANYSEE
+       tristate "Anysee DVB-T/C USB2.0 support"
+       depends on DVB_USB
+       select DVB_PLL if !DVB_FE_CUSTOMISE
+       select DVB_MT352 if !DVB_FE_CUSTOMISE
+       select DVB_ZL10353 if !DVB_FE_CUSTOMISE
+       select DVB_TDA10023 if !DVB_FE_CUSTOMISE
+       help
+         Say Y here to support the Anysee E30, Anysee E30 Plus or
+         Anysee E30 C Plus DVB USB2.0 receiver.
index c6511a6c0ab82dbd9d429d31991c72657ff74a18..44c11e45e564022ba889a5c9a7437a12a0f5ad87 100644 (file)
@@ -61,6 +61,9 @@ obj-$(CONFIG_DVB_USB_AF9005) += dvb-usb-af9005.o
 dvb-usb-af9005-remote-objs = af9005-remote.o
 obj-$(CONFIG_DVB_USB_AF9005_REMOTE) += dvb-usb-af9005-remote.o
 
+dvb-usb-anysee-objs = anysee.o
+obj-$(CONFIG_DVB_USB_ANYSEE) += dvb-usb-anysee.o
+
 EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
 # due to tuner-xc3028
 EXTRA_CFLAGS += -Idrivers/media/common/tuners
diff --git a/drivers/media/dvb/dvb-usb/anysee.c b/drivers/media/dvb/dvb-usb/anysee.c
new file mode 100644 (file)
index 0000000..adfd4fc
--- /dev/null
@@ -0,0 +1,553 @@
+/*
+ * DVB USB Linux driver for Anysee E30 DVB-C & DVB-T USB2.0 receiver
+ *
+ * Copyright (C) 2007 Antti Palosaari <crope@iki.fi>
+ *
+ *    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.
+ *
+ * TODO:
+ * - add smart card reader support for Conditional Access (CA)
+ *
+ * Card reader in Anysee is nothing more than ISO 7816 card reader.
+ * There is no hardware CAM in any Anysee device sold.
+ * In my understanding it should be implemented by making own module
+ * for ISO 7816 card reader, like dvb_ca_en50221 is implemented. This
+ * module registers serial interface that can be used to communicate
+ * with any ISO 7816 smart card.
+ *
+ * Any help according to implement serial smart card reader support
+ * is highly welcome!
+ */
+
+#include "anysee.h"
+#include "tda1002x.h"
+#include "mt352.h"
+#include "mt352_priv.h"
+#include "zl10353.h"
+
+/* debug */
+static int dvb_usb_anysee_debug;
+module_param_named(debug, dvb_usb_anysee_debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS);
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+struct mutex anysee_usb_mutex;
+
+static int anysee_ctrl_msg(struct dvb_usb_device *d, u8 *sbuf, u8 slen,
+       u8 *rbuf, u8 rlen)
+{
+       struct anysee_state *state = d->priv;
+       int act_len, ret;
+       u8 buf[64];
+
+       if (slen > sizeof(buf))
+               slen = sizeof(buf);
+       memcpy(&buf[0], sbuf, slen);
+       buf[60] = state->seq++;
+
+       if (mutex_lock_interruptible(&anysee_usb_mutex) < 0)
+               return -EAGAIN;
+
+       /* We need receive one message more after dvb_usb_generic_rw due
+          to weird transaction flow, which is 1 x send + 2 x receive. */
+       ret = dvb_usb_generic_rw(d, buf, sizeof(buf), buf, sizeof(buf), 0);
+
+       if (!ret) {
+               /* receive 2nd answer */
+               ret = usb_bulk_msg(d->udev, usb_rcvbulkpipe(d->udev,
+                       d->props.generic_bulk_ctrl_endpoint), buf, sizeof(buf),
+                       &act_len, 2000);
+               if (ret)
+                       err("%s: recv bulk message failed: %d", __func__, ret);
+               else {
+                       deb_xfer("<<< ");
+                       debug_dump(buf, act_len, deb_xfer);
+               }
+       }
+
+       /* read request, copy returned data to return buf */
+       if (!ret && rbuf && rlen)
+               memcpy(rbuf, buf, rlen);
+
+       mutex_unlock(&anysee_usb_mutex);
+
+       return ret;
+}
+
+static int anysee_read_reg(struct dvb_usb_device *d, u16 reg, u8 *val)
+{
+       u8 buf[] = {CMD_REG_READ, reg >> 8, reg & 0xff, 0x01};
+       int ret;
+       ret = anysee_ctrl_msg(d, buf, sizeof(buf), val, 1);
+       deb_info("%s: reg:%04x val:%02x\n", __func__, reg, *val);
+       return ret;
+}
+
+static int anysee_write_reg(struct dvb_usb_device *d, u16 reg, u8 val)
+{
+       u8 buf[] = {CMD_REG_WRITE, reg >> 8, reg & 0xff, 0x01, val};
+       deb_info("%s: reg:%04x val:%02x\n", __func__, reg, val);
+       return anysee_ctrl_msg(d, buf, sizeof(buf), NULL, 0);
+}
+
+static int anysee_get_hw_info(struct dvb_usb_device *d, u8 *id)
+{
+       u8 buf[] = {CMD_GET_HW_INFO};
+       return anysee_ctrl_msg(d, buf, sizeof(buf), id, 3);
+}
+
+static int anysee_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
+{
+       u8 buf[] = {CMD_STREAMING_CTRL, (u8)onoff, 0x00};
+       deb_info("%s: onoff:%02x\n", __func__, onoff);
+       return anysee_ctrl_msg(adap->dev, buf, sizeof(buf), NULL, 0);
+}
+
+static int anysee_led_ctrl(struct dvb_usb_device *d, u8 mode, u8 interval)
+{
+       u8 buf[] = {CMD_LED_AND_IR_CTRL, 0x01, mode, interval};
+       deb_info("%s: state:%02x interval:%02x\n", __func__, mode, interval);
+       return anysee_ctrl_msg(d, buf, sizeof(buf), NULL, 0);
+}
+
+static int anysee_ir_ctrl(struct dvb_usb_device *d, u8 onoff)
+{
+       u8 buf[] = {CMD_LED_AND_IR_CTRL, 0x02, onoff};
+       deb_info("%s: onoff:%02x\n", __func__, onoff);
+       return anysee_ctrl_msg(d, buf, sizeof(buf), NULL, 0);
+}
+
+static int anysee_init(struct dvb_usb_device *d)
+{
+       int ret;
+       /* LED light */
+       ret = anysee_led_ctrl(d, 0x01, 0x03);
+       if (ret)
+               return ret;
+
+       /* enable IR */
+       ret = anysee_ir_ctrl(d, 1);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+/* I2C */
+static int anysee_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msg,
+       int num)
+{
+       struct dvb_usb_device *d = i2c_get_adapdata(adap);
+       int ret, inc, i = 0;
+
+       if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+               return -EAGAIN;
+
+       while (i < num) {
+               if (num > i + 1 && (msg[i+1].flags & I2C_M_RD)) {
+                       u8 buf[6];
+                       buf[0] = CMD_I2C_READ;
+                       buf[1] = msg[i].addr + 1;
+                       buf[2] = msg[i].buf[0];
+                       buf[3] = 0x00;
+                       buf[4] = 0x00;
+                       buf[5] = 0x01;
+                       ret = anysee_ctrl_msg(d, buf, sizeof(buf), msg[i+1].buf,
+                               msg[i+1].len);
+                       inc = 2;
+               } else {
+                       u8 buf[4+msg[i].len];
+                       buf[0] = CMD_I2C_WRITE;
+                       buf[1] = msg[i].addr;
+                       buf[2] = msg[i].len;
+                       buf[3] = 0x01;
+                       memcpy(&buf[4], msg[i].buf, msg[i].len);
+                       ret = anysee_ctrl_msg(d, buf, sizeof(buf), NULL, 0);
+                       inc = 1;
+               }
+               if (ret)
+                       return ret;
+
+               i += inc;
+       }
+
+       mutex_unlock(&d->i2c_mutex);
+
+       return i;
+}
+
+static u32 anysee_i2c_func(struct i2c_adapter *adapter)
+{
+       return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm anysee_i2c_algo = {
+       .master_xfer   = anysee_master_xfer,
+       .functionality = anysee_i2c_func,
+};
+
+static int anysee_mt352_demod_init(struct dvb_frontend *fe)
+{
+       static u8 clock_config []  = { CLOCK_CTL,  0x38, 0x28 };
+       static u8 reset []         = { RESET,      0x80 };
+       static u8 adc_ctl_1_cfg [] = { ADC_CTL_1,  0x40 };
+       static u8 agc_cfg []       = { AGC_TARGET, 0x28, 0x20 };
+       static u8 gpp_ctl_cfg []   = { GPP_CTL,    0x33 };
+       static u8 capt_range_cfg[] = { CAPT_RANGE, 0x32 };
+
+       mt352_write(fe, clock_config,   sizeof(clock_config));
+       udelay(200);
+       mt352_write(fe, reset,          sizeof(reset));
+       mt352_write(fe, adc_ctl_1_cfg,  sizeof(adc_ctl_1_cfg));
+
+       mt352_write(fe, agc_cfg,        sizeof(agc_cfg));
+       mt352_write(fe, gpp_ctl_cfg,    sizeof(gpp_ctl_cfg));
+       mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg));
+
+       return 0;
+}
+
+/* Callbacks for DVB USB */
+static struct tda10023_config anysee_tda10023_config = {
+       .demod_address = 0x1a,
+       .invert = 0,
+       .xtal   = 16000000,
+       .pll_m  = 11,
+       .pll_p  = 3,
+       .pll_n  = 1,
+       .output_mode = TDA10023_OUTPUT_MODE_PARALLEL_C,
+       .deltaf = 0xfeeb,
+};
+
+static struct mt352_config anysee_mt352_config = {
+       .demod_address = 0x1e,
+       .demod_init    = anysee_mt352_demod_init,
+};
+
+static struct zl10353_config anysee_zl10353_config = {
+       .demod_address = 0x1e,
+       .parallel_ts = 1,
+};
+
+static int anysee_frontend_attach(struct dvb_usb_adapter *adap)
+{
+       int ret;
+       struct anysee_state *state = adap->dev->priv;
+       u8 hw_info[3];
+       u8 io_d; /* IO port D */
+
+       /* check which hardware we have
+          We must do this call two times to get reliable values (hw bug). */
+       ret = anysee_get_hw_info(adap->dev, hw_info);
+       if (ret)
+               return ret;
+       ret = anysee_get_hw_info(adap->dev, hw_info);
+       if (ret)
+               return ret;
+
+       /* Meaning of these info bytes are guessed. */
+       info("firmware version:%d.%d.%d hardware id:%d",
+               0, hw_info[1], hw_info[2], hw_info[0]);
+
+       ret = anysee_read_reg(adap->dev, 0xb0, &io_d); /* IO port D */
+       if (ret)
+               return ret;
+       deb_info("%s: IO port D:%02x\n", __func__, io_d);
+
+       /* Select demod using trial and error method. */
+
+       /* Try to attach demodulator in following order:
+             model      demod     hw  firmware
+          1. E30        MT352     02  0.2.1
+          2. E30        ZL10353   02  0.2.1
+          3. E30 Plus   ZL10353   06  0.1.0
+          4. E30C Plus  TDA10023  0a  0.1.0    rev 0.2
+          4. E30C Plus  TDA10023  0f  0.1.2    rev 0.4
+       */
+
+       /* Zarlink MT352 DVB-T demod inside of Samsung DNOS404ZH102A NIM */
+       adap->fe = dvb_attach(mt352_attach, &anysee_mt352_config,
+                             &adap->dev->i2c_adap);
+       if (adap->fe != NULL) {
+               state->tuner = DVB_PLL_THOMSON_DTT7579;
+               return 0;
+       }
+
+       /* Zarlink ZL10353 DVB-T demod inside of Samsung DNOS404ZH103A NIM */
+       adap->fe = dvb_attach(zl10353_attach, &anysee_zl10353_config,
+                             &adap->dev->i2c_adap);
+       if (adap->fe != NULL) {
+               state->tuner = DVB_PLL_THOMSON_DTT7579;
+               return 0;
+       }
+
+       /* connect demod on IO port D for TDA10023 & ZL10353 */
+       ret = anysee_write_reg(adap->dev, 0xb0, 0x25);
+       if (ret)
+               return ret;
+
+       /* Zarlink ZL10353 DVB-T demod inside of Samsung DNOS404ZH103A NIM */
+       adap->fe = dvb_attach(zl10353_attach, &anysee_zl10353_config,
+                             &adap->dev->i2c_adap);
+       if (adap->fe != NULL) {
+               state->tuner = DVB_PLL_THOMSON_DTT7579;
+               return 0;
+       }
+
+       /* IO port E - E30C rev 0.4 board requires this */
+       ret = anysee_write_reg(adap->dev, 0xb1, 0xa7);
+       if (ret)
+               return ret;
+
+       /* Philips TDA10023 DVB-C demod */
+       adap->fe = dvb_attach(tda10023_attach, &anysee_tda10023_config,
+                             &adap->dev->i2c_adap, 0x48);
+       if (adap->fe != NULL) {
+               state->tuner = DVB_PLL_SAMSUNG_DTOS403IH102A;
+               return 0;
+       }
+
+       /* return IO port D to init value for safe */
+       ret = anysee_write_reg(adap->dev, 0xb0, io_d);
+       if (ret)
+               return ret;
+
+       err("Unkown Anysee version: %02x %02x %02x. "\
+           "Please report the <linux-dvb@linuxtv.org>.",
+           hw_info[0], hw_info[1], hw_info[2]);
+
+       return -ENODEV;
+}
+
+static int anysee_tuner_attach(struct dvb_usb_adapter *adap)
+{
+       struct anysee_state *state = adap->dev->priv;
+       deb_info("%s: \n", __func__);
+
+       switch (state->tuner) {
+       case DVB_PLL_THOMSON_DTT7579:
+               /* Thomson dtt7579 (not sure) PLL inside of:
+                  Samsung DNOS404ZH102A NIM
+                  Samsung DNOS404ZH103A NIM */
+               dvb_attach(dvb_pll_attach, adap->fe, 0x61,
+                          NULL, DVB_PLL_THOMSON_DTT7579);
+               break;
+       case DVB_PLL_SAMSUNG_DTOS403IH102A:
+               /* Unknown PLL inside of Samsung DTOS403IH102A tuner module */
+               dvb_attach(dvb_pll_attach, adap->fe, 0xc0,
+                          &adap->dev->i2c_adap, DVB_PLL_SAMSUNG_DTOS403IH102A);
+               break;
+       }
+
+       return 0;
+}
+
+static int anysee_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
+{
+       u8 buf[] = {CMD_GET_IR_CODE};
+       struct dvb_usb_rc_key *keymap = d->props.rc_key_map;
+       u8 ircode[2];
+       int i, ret;
+
+       ret = anysee_ctrl_msg(d, buf, sizeof(buf), &ircode[0], 2);
+       if (ret)
+               return ret;
+
+       *event = 0;
+       *state = REMOTE_NO_KEY_PRESSED;
+
+       for (i = 0; i < d->props.rc_key_map_size; i++) {
+               if (keymap[i].custom == ircode[0] &&
+                   keymap[i].data == ircode[1]) {
+                       *event = keymap[i].event;
+                       *state = REMOTE_KEY_PRESSED;
+                       return 0;
+               }
+       }
+       return 0;
+}
+
+static struct dvb_usb_rc_key anysee_rc_keys[] = {
+       { 0x01, 0x00, KEY_0 },
+       { 0x01, 0x01, KEY_1 },
+       { 0x01, 0x02, KEY_2 },
+       { 0x01, 0x03, KEY_3 },
+       { 0x01, 0x04, KEY_4 },
+       { 0x01, 0x05, KEY_5 },
+       { 0x01, 0x06, KEY_6 },
+       { 0x01, 0x07, KEY_7 },
+       { 0x01, 0x08, KEY_8 },
+       { 0x01, 0x09, KEY_9 },
+       { 0x01, 0x0a, KEY_POWER },
+       { 0x01, 0x0b, KEY_DOCUMENTS },    /* * */
+       { 0x01, 0x19, KEY_FAVORITES },
+       { 0x01, 0x20, KEY_SLEEP },
+       { 0x01, 0x21, KEY_MODE },         /* 4:3 / 16:9 select */
+       { 0x01, 0x22, KEY_ZOOM },
+       { 0x01, 0x47, KEY_TEXT },
+       { 0x01, 0x16, KEY_TV },           /* TV / radio select */
+       { 0x01, 0x1e, KEY_LANGUAGE },     /* Second Audio Program */
+       { 0x01, 0x1a, KEY_SUBTITLE },
+       { 0x01, 0x1b, KEY_CAMERA },       /* screenshot */
+       { 0x01, 0x42, KEY_MUTE },
+       { 0x01, 0x0e, KEY_MENU },
+       { 0x01, 0x0f, KEY_EPG },
+       { 0x01, 0x17, KEY_INFO },
+       { 0x01, 0x10, KEY_EXIT },
+       { 0x01, 0x13, KEY_VOLUMEUP },
+       { 0x01, 0x12, KEY_VOLUMEDOWN },
+       { 0x01, 0x11, KEY_CHANNELUP },
+       { 0x01, 0x14, KEY_CHANNELDOWN },
+       { 0x01, 0x15, KEY_OK },
+       { 0x01, 0x1d, KEY_RED },
+       { 0x01, 0x1f, KEY_GREEN },
+       { 0x01, 0x1c, KEY_YELLOW },
+       { 0x01, 0x44, KEY_BLUE },
+       { 0x01, 0x0c, KEY_SHUFFLE },      /* snapshot */
+       { 0x01, 0x48, KEY_STOP },
+       { 0x01, 0x50, KEY_PLAY },
+       { 0x01, 0x51, KEY_PAUSE },
+       { 0x01, 0x49, KEY_RECORD },
+       { 0x01, 0x18, KEY_PREVIOUS },     /* |<< */
+       { 0x01, 0x0d, KEY_NEXT },         /* >>| */
+       { 0x01, 0x24, KEY_PROG1 },        /* F1 */
+       { 0x01, 0x25, KEY_PROG2 },        /* F2 */
+};
+
+/* DVB USB Driver stuff */
+static struct dvb_usb_device_properties anysee_properties;
+
+static int anysee_probe(struct usb_interface *intf,
+                       const struct usb_device_id *id)
+{
+       struct dvb_usb_device *d;
+       struct usb_host_interface *alt;
+       int ret;
+
+       mutex_init(&anysee_usb_mutex);
+
+       /* There is one interface with two alternate settings.
+          Alternate setting 0 is for bulk transfer.
+          Alternate setting 1 is for isochronous transfer.
+          We use bulk transfer (alternate setting 0). */
+       if (intf->num_altsetting < 1)
+               return -ENODEV;
+
+       ret = dvb_usb_device_init(intf, &anysee_properties, THIS_MODULE, &d,
+               adapter_nr);
+       if (ret)
+               return ret;
+
+       alt = usb_altnum_to_altsetting(intf, 0);
+       if (alt == NULL) {
+               deb_info("%s: no alt found!\n", __func__);
+               return -ENODEV;
+       }
+
+       ret = usb_set_interface(d->udev, alt->desc.bInterfaceNumber,
+               alt->desc.bAlternateSetting);
+       if (ret)
+               return ret;
+
+       if (d)
+               ret = anysee_init(d);
+
+       return ret;
+}
+
+static struct usb_device_id anysee_table [] = {
+       { USB_DEVICE(USB_VID_CYPRESS, USB_PID_ANYSEE) },
+       { USB_DEVICE(USB_VID_AMT,     USB_PID_ANYSEE) },
+       { }             /* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, anysee_table);
+
+static struct dvb_usb_device_properties anysee_properties = {
+       .caps             = DVB_USB_IS_AN_I2C_ADAPTER,
+
+       .usb_ctrl         = DEVICE_SPECIFIC,
+
+       .size_of_priv     = sizeof(struct anysee_state),
+
+       .num_adapters = 1,
+       .adapter = {
+               {
+                       .streaming_ctrl   = anysee_streaming_ctrl,
+                       .frontend_attach  = anysee_frontend_attach,
+                       .tuner_attach     = anysee_tuner_attach,
+                       .stream = {
+                               .type = USB_BULK,
+                               .count = 8,
+                               .endpoint = 0x82,
+                               .u = {
+                                       .bulk = {
+                                               .buffersize = 512,
+                                       }
+                               }
+                       },
+               }
+       },
+
+       .rc_key_map       = anysee_rc_keys,
+       .rc_key_map_size  = ARRAY_SIZE(anysee_rc_keys),
+       .rc_query         = anysee_rc_query,
+       .rc_interval      = 200,  /* windows driver uses 500ms */
+
+       .i2c_algo         = &anysee_i2c_algo,
+
+       .generic_bulk_ctrl_endpoint = 1,
+
+       .num_device_descs = 1,
+       .devices = {
+               {
+                       .name = "Anysee DVB USB2.0",
+                       .cold_ids = {NULL},
+                       .warm_ids = {&anysee_table[0],
+                                    &anysee_table[1], NULL},
+               },
+       }
+};
+
+static struct usb_driver anysee_driver = {
+       .name       = "dvb_usb_anysee",
+       .probe      = anysee_probe,
+       .disconnect = dvb_usb_device_exit,
+       .id_table   = anysee_table,
+};
+
+/* module stuff */
+static int __init anysee_module_init(void)
+{
+       int ret;
+
+       ret = usb_register(&anysee_driver);
+       if (ret)
+               err("%s: usb_register failed. Error number %d", __func__, ret);
+
+       return ret;
+}
+
+static void __exit anysee_module_exit(void)
+{
+       /* deregister this driver from the USB subsystem */
+       usb_deregister(&anysee_driver);
+}
+
+module_init(anysee_module_init);
+module_exit(anysee_module_exit);
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("Driver Anysee E30 DVB-C & DVB-T USB2.0");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-usb/anysee.h b/drivers/media/dvb/dvb-usb/anysee.h
new file mode 100644 (file)
index 0000000..7ca01ff
--- /dev/null
@@ -0,0 +1,304 @@
+/*
+ * DVB USB Linux driver for Anysee E30 DVB-C & DVB-T USB2.0 receiver
+ *
+ * Copyright (C) 2007 Antti Palosaari <crope@iki.fi>
+ *
+ *    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.
+ *
+ * TODO:
+ * - add smart card reader support for Conditional Access (CA)
+ *
+ * Card reader in Anysee is nothing more than ISO 7816 card reader.
+ * There is no hardware CAM in any Anysee device sold.
+ * In my understanding it should be implemented by making own module
+ * for ISO 7816 card reader, like dvb_ca_en50221 is implemented. This
+ * module registers serial interface that can be used to communicate
+ * with any ISO 7816 smart card.
+ *
+ * Any help according to implement serial smart card reader support
+ * is highly welcome!
+ */
+
+#ifndef _DVB_USB_ANYSEE_H_
+#define _DVB_USB_ANYSEE_H_
+
+#define DVB_USB_LOG_PREFIX "anysee"
+#include "dvb-usb.h"
+
+#define deb_info(args...) dprintk(dvb_usb_anysee_debug, 0x01, args)
+#define deb_xfer(args...) dprintk(dvb_usb_anysee_debug, 0x02, args)
+#define deb_rc(args...)   dprintk(dvb_usb_anysee_debug, 0x04, args)
+#define deb_reg(args...)  dprintk(dvb_usb_anysee_debug, 0x08, args)
+#define deb_i2c(args...)  dprintk(dvb_usb_anysee_debug, 0x10, args)
+#define deb_fw(args...)   dprintk(dvb_usb_anysee_debug, 0x20, args)
+
+enum cmd {
+       CMD_I2C_READ            = 0x33,
+       CMD_I2C_WRITE           = 0x31,
+       CMD_REG_READ            = 0xb0,
+       CMD_REG_WRITE           = 0xb1,
+       CMD_STREAMING_CTRL      = 0x12,
+       CMD_LED_AND_IR_CTRL     = 0x16,
+       CMD_GET_IR_CODE         = 0x41,
+       CMD_GET_HW_INFO         = 0x19,
+       CMD_SMARTCARD           = 0x34,
+};
+
+struct anysee_state {
+       u8 tuner;
+       u8 seq;
+};
+
+#endif
+
+/***************************************************************************
+ * USB API description (reverse engineered)
+ ***************************************************************************
+
+Transaction flow:
+=================
+BULK[00001] >>> REQUEST PACKET 64 bytes
+BULK[00081] <<< REPLY PACKET #1 64 bytes (PREVIOUS TRANSACTION REPLY)
+BULK[00081] <<< REPLY PACKET #2 64 bytes (CURRENT TRANSACTION REPLY)
+
+General reply packet(s) are always used if not own reply defined.
+
+============================================================================
+| 00-63 | GENERAL REPLY PACKET #1 (PREVIOUS REPLY)
+============================================================================
+|    00 | reply data (if any) from previous transaction
+|       | Just same reply packet as returned during previous transaction.
+|       | Needed only if reply is missed in previous transaction.
+|       | Just skip normally.
+----------------------------------------------------------------------------
+| 01-59 | don't care
+----------------------------------------------------------------------------
+|    60 | packet sequence number
+----------------------------------------------------------------------------
+| 61-63 | don't care
+----------------------------------------------------------------------------
+
+============================================================================
+| 00-63 | GENERAL REPLY PACKET #2 (CURRENT REPLY)
+============================================================================
+|    00 | reply data (if any)
+----------------------------------------------------------------------------
+| 01-59 | don't care
+----------------------------------------------------------------------------
+|    60 | packet sequence number
+----------------------------------------------------------------------------
+| 61-63 | don't care
+----------------------------------------------------------------------------
+
+============================================================================
+| 00-63 | I2C WRITE REQUEST PACKET
+============================================================================
+|    00 | 0x31 I2C write command
+----------------------------------------------------------------------------
+|    01 | i2c address
+----------------------------------------------------------------------------
+|    02 | data length
+|       | 0x02 (for typical I2C reg / val pair)
+----------------------------------------------------------------------------
+|    03 | 0x01
+----------------------------------------------------------------------------
+| 04-   | data
+----------------------------------------------------------------------------
+|   -59 | don't care
+----------------------------------------------------------------------------
+|    60 | packet sequence number
+----------------------------------------------------------------------------
+| 61-63 | don't care
+----------------------------------------------------------------------------
+
+============================================================================
+| 00-63 | I2C READ REQUEST PACKET
+============================================================================
+|    00 | 0x33 I2C read command
+----------------------------------------------------------------------------
+|    01 | i2c address + 1
+----------------------------------------------------------------------------
+|    02 | register
+----------------------------------------------------------------------------
+|    03 | 0x00
+----------------------------------------------------------------------------
+|    04 | 0x00
+----------------------------------------------------------------------------
+|    05 | 0x01
+----------------------------------------------------------------------------
+| 06-59 | don't care
+----------------------------------------------------------------------------
+|    60 | packet sequence number
+----------------------------------------------------------------------------
+| 61-63 | don't care
+----------------------------------------------------------------------------
+
+============================================================================
+| 00-63 | USB CONTROLLER REGISTER WRITE REQUEST PACKET
+============================================================================
+|    00 | 0xb1 register write command
+----------------------------------------------------------------------------
+| 01-02 | register
+----------------------------------------------------------------------------
+|    03 | 0x01
+----------------------------------------------------------------------------
+|    04 | value
+----------------------------------------------------------------------------
+| 05-59 | don't care
+----------------------------------------------------------------------------
+|    60 | packet sequence number
+----------------------------------------------------------------------------
+| 61-63 | don't care
+----------------------------------------------------------------------------
+
+============================================================================
+| 00-63 | USB CONTROLLER REGISTER READ REQUEST PACKET
+============================================================================
+|    00 | 0xb0 register read command
+----------------------------------------------------------------------------
+| 01-02 | register
+----------------------------------------------------------------------------
+|    03 | 0x01
+----------------------------------------------------------------------------
+| 04-59 | don't care
+----------------------------------------------------------------------------
+|    60 | packet sequence number
+----------------------------------------------------------------------------
+| 61-63 | don't care
+----------------------------------------------------------------------------
+
+============================================================================
+| 00-63 | LED CONTROL REQUEST PACKET
+============================================================================
+|    00 | 0x16 LED and IR control command
+----------------------------------------------------------------------------
+|    01 | 0x01 (LED)
+----------------------------------------------------------------------------
+|    03 | 0x00 blink
+|       | 0x01 lights continuously
+----------------------------------------------------------------------------
+|    04 | blink interval
+|       | 0x00 fastest (looks like LED lights continuously)
+|       | 0xff slowest
+----------------------------------------------------------------------------
+| 05-59 | don't care
+----------------------------------------------------------------------------
+|    60 | packet sequence number
+----------------------------------------------------------------------------
+| 61-63 | don't care
+----------------------------------------------------------------------------
+
+============================================================================
+| 00-63 | IR CONTROL REQUEST PACKET
+============================================================================
+|    00 | 0x16 LED and IR control command
+----------------------------------------------------------------------------
+|    01 | 0x02 (IR)
+----------------------------------------------------------------------------
+|    03 | 0x00 IR disabled
+|       | 0x01 IR enabled
+----------------------------------------------------------------------------
+| 04-59 | don't care
+----------------------------------------------------------------------------
+|    60 | packet sequence number
+----------------------------------------------------------------------------
+| 61-63 | don't care
+----------------------------------------------------------------------------
+
+============================================================================
+| 00-63 | STREAMING CONTROL REQUEST PACKET
+============================================================================
+|    00 | 0x12 streaming control command
+----------------------------------------------------------------------------
+|    01 | 0x00 streaming disabled
+|       | 0x01 streaming enabled
+----------------------------------------------------------------------------
+|    02 | 0x00
+----------------------------------------------------------------------------
+| 03-59 | don't care
+----------------------------------------------------------------------------
+|    60 | packet sequence number
+----------------------------------------------------------------------------
+| 61-63 | don't care
+----------------------------------------------------------------------------
+
+============================================================================
+| 00-63 | REMOTE CONTROL REQUEST PACKET
+============================================================================
+|    00 | 0x41 remote control command
+----------------------------------------------------------------------------
+| 01-59 | don't care
+----------------------------------------------------------------------------
+|    60 | packet sequence number
+----------------------------------------------------------------------------
+| 61-63 | don't care
+----------------------------------------------------------------------------
+
+============================================================================
+| 00-63 | REMOTE CONTROL REPLY PACKET
+============================================================================
+|    00 | 0x00 code not received
+|       | 0x01 code received
+----------------------------------------------------------------------------
+|    01 | remote control code
+----------------------------------------------------------------------------
+| 02-59 | don't care
+----------------------------------------------------------------------------
+|    60 | packet sequence number
+----------------------------------------------------------------------------
+| 61-63 | don't care
+----------------------------------------------------------------------------
+
+============================================================================
+| 00-63 | GET HARDWARE INFO REQUEST PACKET
+============================================================================
+|    00 | 0x19 get hardware info command
+----------------------------------------------------------------------------
+| 01-59 | don't care
+----------------------------------------------------------------------------
+|    60 | packet sequence number
+----------------------------------------------------------------------------
+| 61-63 | don't care
+----------------------------------------------------------------------------
+
+============================================================================
+| 00-63 | GET HARDWARE INFO REPLY PACKET
+============================================================================
+|    00 | hardware id
+----------------------------------------------------------------------------
+| 01-02 | firmware version
+----------------------------------------------------------------------------
+| 03-59 | don't care
+----------------------------------------------------------------------------
+|    60 | packet sequence number
+----------------------------------------------------------------------------
+| 61-63 | don't care
+----------------------------------------------------------------------------
+
+============================================================================
+| 00-63 | SMART CARD READER PACKET
+============================================================================
+|    00 | 0x34 smart card reader command
+----------------------------------------------------------------------------
+|    xx |
+----------------------------------------------------------------------------
+| xx-59 | don't care
+----------------------------------------------------------------------------
+|    60 | packet sequence number
+----------------------------------------------------------------------------
+| 61-63 | don't care
+----------------------------------------------------------------------------
+
+*/
index 2ccb90fa60c85917cd9604443cb13e5169e61f5f..eb34cc3894e070e90929b33effe8d900e87796c8 100644 (file)
@@ -1,24 +1,31 @@
-/* DVB USB compliant linux driver for Sigmatek DVB-110 DVB-T USB2.0 receiver
+/*
+ * DVB USB Linux driver for Alcor Micro AU6610 DVB-T USB2.0.
  *
  * Copyright (C) 2006 Antti Palosaari <crope@iki.fi>
  *
- *     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, version 2.
+ *    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.
  *
- * see Documentation/dvb/README.dvb-usb for more information
+ *    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 "au6610.h"
-
 #include "zl10353.h"
 #include "qt1010.h"
 
 /* debug */
 static int dvb_usb_au6610_debug;
 module_param_named(debug, dvb_usb_au6610_debug, int, 0644);
-MODULE_PARM_DESC(debug, "set debugging level (1=rc (or-able))." DVB_USB_DEBUG_STATUS);
-
+MODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS);
 DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
 
 static int au6610_usb_msg(struct dvb_usb_device *d, u8 operation, u8 addr,
@@ -42,9 +49,8 @@ static int au6610_usb_msg(struct dvb_usb_device *d, u8 operation, u8 addr,
        }
 
        ret = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), operation,
-                             USB_TYPE_VENDOR|USB_DIR_IN, addr << 1, index, usb_buf,
-                             sizeof(usb_buf), AU6610_USB_TIMEOUT);
-
+                             USB_TYPE_VENDOR|USB_DIR_IN, addr << 1, index,
+                             usb_buf, sizeof(usb_buf), AU6610_USB_TIMEOUT);
        if (ret < 0)
                return ret;
 
@@ -116,15 +122,6 @@ static struct i2c_algorithm au6610_i2c_algo = {
 };
 
 /* Callbacks for DVB USB */
-static int au6610_identify_state(struct usb_device *udev,
-                                struct dvb_usb_device_properties *props,
-                                struct dvb_usb_device_description **desc,
-                                int *cold)
-{
-       *cold = 0;
-       return 0;
-}
-
 static struct zl10353_config au6610_zl10353_config = {
        .demod_address = 0x0f,
        .no_tuner = 1,
@@ -133,12 +130,12 @@ static struct zl10353_config au6610_zl10353_config = {
 
 static int au6610_zl10353_frontend_attach(struct dvb_usb_adapter *adap)
 {
-       if ((adap->fe = dvb_attach(zl10353_attach, &au6610_zl10353_config,
-                                  &adap->dev->i2c_adap)) != NULL) {
-               return 0;
-       }
+       adap->fe = dvb_attach(zl10353_attach, &au6610_zl10353_config,
+               &adap->dev->i2c_adap);
+       if (adap->fe == NULL)
+               return -ENODEV;
 
-       return -EIO;
+       return 0;
 }
 
 static struct qt1010_config au6610_qt1010_config = {
@@ -171,7 +168,7 @@ static int au6610_probe(struct usb_interface *intf,
                alt = usb_altnum_to_altsetting(intf, AU6610_ALTSETTING);
 
                if (alt == NULL) {
-                       deb_rc("no alt found!\n");
+                       deb_info("%s: no alt found!\n", __func__);
                        return -ENODEV;
                }
                ret = usb_set_interface(d->udev, alt->desc.bInterfaceNumber,
@@ -181,18 +178,19 @@ static int au6610_probe(struct usb_interface *intf,
        return ret;
 }
 
-
 static struct usb_device_id au6610_table [] = {
        { USB_DEVICE(USB_VID_ALCOR_MICRO, USB_PID_SIGMATEK_DVB_110) },
        { }             /* Terminating entry */
 };
-MODULE_DEVICE_TABLE (usb, au6610_table);
+MODULE_DEVICE_TABLE(usb, au6610_table);
 
 static struct dvb_usb_device_properties au6610_properties = {
        .caps = DVB_USB_IS_AN_I2C_ADAPTER,
+
        .usb_ctrl = DEVICE_SPECIFIC,
-       .size_of_priv     = 0,
-       .identify_state   = au6610_identify_state,
+
+       .size_of_priv = 0,
+
        .num_adapters = 1,
        .adapter = {
                {
@@ -206,20 +204,22 @@ static struct dvb_usb_device_properties au6610_properties = {
                                .u = {
                                        .isoc = {
                                                .framesperurb = 40,
-                                               .framesize = 942,   /* maximum packet size */
-                                               .interval = 1.25,   /* 125 us */
+                                               .framesize = 942,
+                                               .interval = 1,
                                        }
                                }
                        },
                }
        },
+
        .i2c_algo = &au6610_i2c_algo,
+
        .num_device_descs = 1,
        .devices = {
                {
-                       "Sigmatek DVB-110 DVB-T USB2.0",
-                       { &au6610_table[0], NULL },
-                       { NULL },
+                       .name = "Sigmatek DVB-110 DVB-T USB2.0",
+                       .cold_ids = {NULL},
+                       .warm_ids = {&au6610_table[0], NULL},
                },
        }
 };
@@ -236,12 +236,11 @@ static int __init au6610_module_init(void)
 {
        int ret;
 
-       if ((ret = usb_register(&au6610_driver))) {
+       ret = usb_register(&au6610_driver);
+       if (ret)
                err("usb_register failed. Error number %d", ret);
-               return ret;
-       }
 
-       return 0;
+       return ret;
 }
 
 static void __exit au6610_module_exit(void)
@@ -250,10 +249,10 @@ static void __exit au6610_module_exit(void)
        usb_deregister(&au6610_driver);
 }
 
-module_init (au6610_module_init);
-module_exit (au6610_module_exit);
+module_init(au6610_module_init);
+module_exit(au6610_module_exit);
 
 MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
-MODULE_DESCRIPTION("Driver Sigmatek DVB-110 DVB-T USB2.0 / AU6610");
+MODULE_DESCRIPTION("Driver for Alcor Micro AU6610 DVB-T USB2.0");
 MODULE_VERSION("0.1");
 MODULE_LICENSE("GPL");
index 4161b054c71344263688f197706c5ad4527d73bf..7849abe2c61433c685c58dd231f27c79575b7c9b 100644 (file)
@@ -1,10 +1,30 @@
+/*
+ * DVB USB Linux driver for Alcor Micro AU6610 DVB-T USB2.0.
+ *
+ * Copyright (C) 2006 Antti Palosaari <crope@iki.fi>
+ *
+ *    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.
+ */
+
 #ifndef _DVB_USB_AU6610_H_
 #define _DVB_USB_AU6610_H_
 
 #define DVB_USB_LOG_PREFIX "au6610"
 #include "dvb-usb.h"
 
-#define deb_rc(args...)   dprintk(dvb_usb_au6610_debug,0x01,args)
+#define deb_info(args...)   dprintk(dvb_usb_au6610_debug, 0x01, args)
 
 #define AU6610_REQ_I2C_WRITE   0x14
 #define AU6610_REQ_I2C_READ    0x13
index 0286156704f2552e8a6ff429e30b34370040f4a2..578afce6884c554701f77c4a15af93faabb712c1 100644 (file)
@@ -35,6 +35,7 @@
 #include "zl10353.h"
 #include "tuner-xc2028.h"
 #include "tuner-simple.h"
+#include "mxl5005s.h"
 
 /* debug */
 static int dvb_usb_cxusb_debug;
@@ -43,9 +44,8 @@ MODULE_PARM_DESC(debug, "set debugging level (1=rc (or-able))." DVB_USB_DEBUG_ST
 
 DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
 
-#define deb_info(args...)   dprintk(dvb_usb_cxusb_debug,0x01,args)
-#define deb_i2c(args...)    if (d->udev->descriptor.idVendor == USB_VID_MEDION) \
-                               dprintk(dvb_usb_cxusb_debug,0x01,args)
+#define deb_info(args...)   dprintk(dvb_usb_cxusb_debug, 0x03, args)
+#define deb_i2c(args...)    dprintk(dvb_usb_cxusb_debug, 0x02, args)
 
 static int cxusb_ctrl_msg(struct dvb_usb_device *d,
                          u8 cmd, u8 *wbuf, int wlen, u8 *rbuf, int rlen)
@@ -202,6 +202,46 @@ static int cxusb_power_ctrl(struct dvb_usb_device *d, int onoff)
                return cxusb_ctrl_msg(d, CMD_POWER_OFF, &b, 1, NULL, 0);
 }
 
+static int cxusb_aver_power_ctrl(struct dvb_usb_device *d, int onoff)
+{
+       int ret;
+       if (!onoff)
+               return cxusb_ctrl_msg(d, CMD_POWER_OFF, NULL, 0, NULL, 0);
+       if (d->state == DVB_USB_STATE_INIT &&
+           usb_set_interface(d->udev, 0, 0) < 0)
+               err("set interface failed");
+       do; while (!(ret = cxusb_ctrl_msg(d, CMD_POWER_ON, NULL, 0, NULL, 0)) &&
+                  !(ret = cxusb_ctrl_msg(d, 0x15, NULL, 0, NULL, 0)) &&
+                  !(ret = cxusb_ctrl_msg(d, 0x17, NULL, 0, NULL, 0)) && 0);
+       if (!ret) {
+               /* FIXME: We don't know why, but we need to configure the
+                * lgdt3303 with the register settings below on resume */
+               int i;
+               u8 buf, bufs[] = {
+                       0x0e, 0x2, 0x00, 0x7f,
+                       0x0e, 0x2, 0x02, 0xfe,
+                       0x0e, 0x2, 0x02, 0x01,
+                       0x0e, 0x2, 0x00, 0x03,
+                       0x0e, 0x2, 0x0d, 0x40,
+                       0x0e, 0x2, 0x0e, 0x87,
+                       0x0e, 0x2, 0x0f, 0x8e,
+                       0x0e, 0x2, 0x10, 0x01,
+                       0x0e, 0x2, 0x14, 0xd7,
+                       0x0e, 0x2, 0x47, 0x88,
+               };
+               msleep(20);
+               for (i = 0; i < sizeof(bufs)/sizeof(u8); i += 4/sizeof(u8)) {
+                       ret = cxusb_ctrl_msg(d, CMD_I2C_WRITE,
+                                            bufs+i, 4, &buf, 1);
+                       if (ret)
+                               break;
+                       if (buf != 0x8)
+                               return -EREMOTEIO;
+               }
+       }
+       return ret;
+}
+
 static int cxusb_bluebird_power_ctrl(struct dvb_usb_device *d, int onoff)
 {
        u8 b = 0;
@@ -233,6 +273,16 @@ static int cxusb_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
        return 0;
 }
 
+static int cxusb_aver_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
+{
+       if (onoff)
+               cxusb_ctrl_msg(adap->dev, CMD_AVER_STREAM_ON, NULL, 0, NULL, 0);
+       else
+               cxusb_ctrl_msg(adap->dev, CMD_AVER_STREAM_OFF,
+                              NULL, 0, NULL, 0);
+       return 0;
+}
+
 static int cxusb_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
 {
        struct dvb_usb_rc_key *keymap = d->props.rc_key_map;
@@ -423,6 +473,12 @@ static struct lgdt330x_config cxusb_lgdt3303_config = {
        .demod_chip    = LGDT3303,
 };
 
+static struct lgdt330x_config cxusb_aver_lgdt3303_config = {
+       .demod_address       = 0x0e,
+       .demod_chip          = LGDT3303,
+       .clock_polarity_flip = 2,
+};
+
 static struct mt352_config cxusb_dee1601_config = {
        .demod_address = 0x0f,
        .demod_init    = cxusb_dee1601_demod_init,
@@ -453,6 +509,24 @@ static struct mt352_config cxusb_mt352_xc3028_config = {
        .demod_init = cxusb_mt352_demod_init,
 };
 
+/* FIXME: needs tweaking */
+static struct mxl5005s_config aver_a868r_tuner = {
+       .i2c_address     = 0x63,
+       .if_freq         = 6000000UL,
+       .xtal_freq       = CRYSTAL_FREQ_16000000HZ,
+       .agc_mode        = MXL_SINGLE_AGC,
+       .tracking_filter = MXL_TF_C,
+       .rssi_enable     = MXL_RSSI_ENABLE,
+       .cap_select      = MXL_CAP_SEL_ENABLE,
+       .div_out         = MXL_DIV_OUT_4,
+       .clock_out       = MXL_CLOCK_OUT_DISABLE,
+       .output_load     = MXL5005S_IF_OUTPUT_LOAD_200_OHM,
+       .top             = MXL5005S_TOP_25P2,
+       .mod_mode        = MXL_DIGITAL_MODE,
+       .if_mode         = MXL_ZERO_IF,
+       .AgcMasterByte   = 0x00,
+};
+
 /* Callbacks for DVB USB */
 static int cxusb_fmd1216me_tuner_attach(struct dvb_usb_adapter *adap)
 {
@@ -533,6 +607,13 @@ static int cxusb_dvico_xc3028_tuner_attach(struct dvb_usb_adapter *adap)
        return 0;
 }
 
+static int cxusb_mxl5003s_tuner_attach(struct dvb_usb_adapter *adap)
+{
+       dvb_attach(mxl5005s_attach, adap->fe,
+                  &adap->dev->i2c_adap, &aver_a868r_tuner);
+       return 0;
+}
+
 static int cxusb_cx22702_frontend_attach(struct dvb_usb_adapter *adap)
 {
        u8 b;
@@ -562,6 +643,16 @@ static int cxusb_lgdt3303_frontend_attach(struct dvb_usb_adapter *adap)
        return -EIO;
 }
 
+static int cxusb_aver_lgdt3303_frontend_attach(struct dvb_usb_adapter *adap)
+{
+       adap->fe = dvb_attach(lgdt330x_attach, &cxusb_aver_lgdt3303_config,
+                             &adap->dev->i2c_adap);
+       if (adap->fe != NULL)
+               return 0;
+
+       return -EIO;
+}
+
 static int cxusb_mt352_frontend_attach(struct dvb_usb_adapter *adap)
 {
        /* used in both lgz201 and th7579 */
@@ -736,6 +827,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_dtt7579_properties;
 static struct dvb_usb_device_properties cxusb_bluebird_dualdig4_properties;
 static struct dvb_usb_device_properties cxusb_bluebird_nano2_properties;
 static struct dvb_usb_device_properties cxusb_bluebird_nano2_needsfirmware_properties;
+static struct dvb_usb_device_properties cxusb_aver_a868r_properties;
 
 static int cxusb_probe(struct usb_interface *intf,
                       const struct usb_device_id *id)
@@ -756,7 +848,10 @@ static int cxusb_probe(struct usb_interface *intf,
                                     THIS_MODULE, NULL, adapter_nr) ||
            0 == dvb_usb_device_init(intf,
                                &cxusb_bluebird_nano2_needsfirmware_properties,
-                                    THIS_MODULE, NULL, adapter_nr))
+                                    THIS_MODULE, NULL, adapter_nr) ||
+           0 == dvb_usb_device_init(intf, &cxusb_aver_a868r_properties,
+                                    THIS_MODULE, NULL, adapter_nr) ||
+           0)
                return 0;
 
        return -EINVAL;
@@ -779,6 +874,7 @@ static struct usb_device_id cxusb_table [] = {
        { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_4) },
        { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DVB_T_NANO_2) },
        { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DVB_T_NANO_2_NFW_WARM) },
+       { USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_A868R) },
        {}              /* Terminating entry */
 };
 MODULE_DEVICE_TABLE (usb, cxusb_table);
@@ -1182,6 +1278,48 @@ static struct dvb_usb_device_properties cxusb_bluebird_nano2_needsfirmware_prope
        }
 };
 
+static struct dvb_usb_device_properties cxusb_aver_a868r_properties = {
+       .caps = DVB_USB_IS_AN_I2C_ADAPTER,
+
+       .usb_ctrl         = CYPRESS_FX2,
+
+       .size_of_priv     = sizeof(struct cxusb_state),
+
+       .num_adapters = 1,
+       .adapter = {
+               {
+                       .streaming_ctrl   = cxusb_aver_streaming_ctrl,
+                       .frontend_attach  = cxusb_aver_lgdt3303_frontend_attach,
+                       .tuner_attach     = cxusb_mxl5003s_tuner_attach,
+                       /* parameter for the MPEG2-data transfer */
+                       .stream = {
+                               .type = USB_BULK,
+                               .count = 5,
+                               .endpoint = 0x04,
+                               .u = {
+                                       .bulk = {
+                                               .buffersize = 8192,
+                                       }
+                               }
+                       },
+
+               },
+       },
+       .power_ctrl       = cxusb_aver_power_ctrl,
+
+       .i2c_algo         = &cxusb_i2c_algo,
+
+       .generic_bulk_ctrl_endpoint = 0x01,
+
+       .num_device_descs = 1,
+       .devices = {
+               {   "AVerMedia AVerTVHD Volar (A868R)",
+                       { NULL },
+                       { &cxusb_table[16], NULL },
+               },
+       }
+};
+
 static struct usb_driver cxusb_driver = {
        .name           = "dvb_usb_cxusb",
        .probe          = cxusb_probe,
index 4768a2c355172b9be95a7e07dd0b2f0dfa6861c7..1a51eafd31b94e02f563ba5100aaa4d4809fe569 100644 (file)
@@ -20,6 +20,9 @@
 #define CMD_STREAMING_ON  0x36
 #define CMD_STREAMING_OFF 0x37
 
+#define CMD_AVER_STREAM_ON  0x18
+#define CMD_AVER_STREAM_OFF 0x19
+
 #define CMD_GET_IR_CODE   0x47
 
 #define CMD_ANALOG        0x50
index c4d40fe01d579f09d57489a86b90a206538f5ed0..3dd20bfbed326f7d573f6861a0c1757adf9e6b47 100644 (file)
@@ -1117,6 +1117,7 @@ struct usb_device_id dib0700_usb_id_table[] = {
        { USB_DEVICE(USB_VID_TERRATEC,  USB_PID_TERRATEC_CINERGY_HT_EXPRESS) },
        { USB_DEVICE(USB_VID_TERRATEC,  USB_PID_TERRATEC_CINERGY_T_XXS) },
        { USB_DEVICE(USB_VID_LEADTEK,   USB_PID_WINFAST_DTV_DONGLE_STK7700P_2) },
+       { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_NOVA_TD_STICK_52009) },
        { 0 }           /* Terminating entry */
 };
 MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table);
@@ -1372,7 +1373,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
                        }
                },
 
-               .num_device_descs = 2,
+               .num_device_descs = 3,
                .devices = {
                        {   "DiBcom STK7070PD reference design",
                                { &dib0700_usb_id_table[17], NULL },
@@ -1381,6 +1382,10 @@ struct dvb_usb_device_properties dib0700_devices[] = {
                        {   "Pinnacle PCTV Dual DVB-T Diversity Stick",
                                { &dib0700_usb_id_table[18], NULL },
                                { NULL },
+                       },
+                       {   "Hauppauge Nova-TD Stick (52009)",
+                               { &dib0700_usb_id_table[35], NULL },
+                               { NULL },
                        }
                }
        }, { DIB0700_DEFAULT_DEVICE_PROPERTIES,
index 23428cd307569d8ebdf5d252b0ceed4958356b9c..326f7608954b12e36ef72c1d45a152793a89c5be 100644 (file)
@@ -20,11 +20,7 @@ int dvb_usb_i2c_init(struct dvb_usb_device *d)
        }
 
        strncpy(d->i2c_adap.name, d->desc->name, sizeof(d->i2c_adap.name));
-#ifdef I2C_ADAP_CLASS_TV_DIGITAL
-       d->i2c_adap.class = I2C_ADAP_CLASS_TV_DIGITAL,
-#else
        d->i2c_adap.class = I2C_CLASS_TV_DIGITAL,
-#endif
        d->i2c_adap.algo      = d->props.i2c_algo;
        d->i2c_adap.algo_data = NULL;
        d->i2c_adap.dev.parent = &d->udev->dev;
index 34245d1b7dd92a151233de0e266466e1df4b06ae..e5238b31e946e1b26c426d5ea5a76cbabedb5520 100644 (file)
@@ -14,6 +14,7 @@
 #define USB_VID_AFATECH                                0x15a4
 #define USB_VID_ALCOR_MICRO                    0x058f
 #define USB_VID_ALINK                          0x05e3
+#define USB_VID_AMT                            0x1c73
 #define USB_VID_ANCHOR                         0x0547
 #define USB_VID_ANSONIC                                0x10b9
 #define USB_VID_ANUBIS_ELECTRONIC              0x10fd
@@ -57,6 +58,7 @@
 #define USB_PID_AFATECH_AF9005                         0x9020
 #define USB_VID_ALINK_DTU                              0xf170
 #define USB_PID_ANSONIC_DVBT_USB                       0x6000
+#define USB_PID_ANYSEE                                 0x861f
 #define USB_PID_AVERMEDIA_DVBT_USB_COLD                        0x0001
 #define USB_PID_AVERMEDIA_DVBT_USB_WARM                        0x0002
 #define USB_PID_AVERMEDIA_DVBT_USB2_COLD               0xa800
 #define USB_PID_HAUPPAUGE_NOVA_T_STICK_3               0x7070
 #define USB_PID_HAUPPAUGE_MYTV_T                       0x7080
 #define USB_PID_HAUPPAUGE_NOVA_TD_STICK                        0x9580
+#define USB_PID_HAUPPAUGE_NOVA_TD_STICK_52009          0x5200
 #define USB_PID_AVERMEDIA_EXPRESS                      0xb568
 #define USB_PID_AVERMEDIA_VOLAR                                0xa807
 #define USB_PID_AVERMEDIA_VOLAR_2                      0xb808
+#define USB_PID_AVERMEDIA_VOLAR_A868R                  0xa868
+#define USB_PID_AVERMEDIA_MCE_USB_M038                 0x1228
+#define USB_PID_AVERMEDIA_HYBRID_ULTRA_USB_M039R       0x0039
+#define USB_PID_AVERMEDIA_HYBRID_ULTRA_USB_M039R_ATSC  0x1039
+#define USB_PID_AVERMEDIA_HYBRID_ULTRA_USB_M039R_DVBT  0x2039
 #define USB_PID_TECHNOTREND_CONNECT_S2400               0x3006
 #define USB_PID_TERRATEC_CINERGY_DT_XS_DIVERSITY       0x005a
 #define USB_PID_TERRATEC_CINERGY_HT_USB_XE             0x0058
index 037f7ffb47b297e6696fd167b8849143cac654f6..6f596ed41761ce927c6b2c6ba51e4aa40192f343 100644 (file)
@@ -1,8 +1,8 @@
 /* DVB USB compliant linux driver for GL861 USB2.0 devices.
  *
  *     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, version 2.
+ *     under the terms of the GNU General Public License as published by the
+ *     Free Software Foundation, version 2.
  *
  * see Documentation/dvb/README.dvb-usb for more information
  */
@@ -13,9 +13,9 @@
 
 /* debug */
 static int dvb_usb_gl861_debug;
-module_param_named(debug,dvb_usb_gl861_debug, int, 0644);
-MODULE_PARM_DESC(debug, "set debugging level (1=rc (or-able))." DVB_USB_DEBUG_STATUS);
-
+module_param_named(debug, dvb_usb_gl861_debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level (1=rc (or-able))."
+       DVB_USB_DEBUG_STATUS);
 DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
 
 static int gl861_i2c_msg(struct dvb_usb_device *d, u8 addr,
@@ -70,7 +70,7 @@ static int gl861_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
                /* write/read request */
                if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) {
                        if (gl861_i2c_msg(d, msg[i].addr, msg[i].buf,
-                                         msg[i].len, msg[i+1].buf, msg[i+1].len) < 0)
+                               msg[i].len, msg[i+1].buf, msg[i+1].len) < 0)
                                break;
                        i++;
                } else
@@ -102,12 +102,13 @@ static struct zl10353_config gl861_zl10353_config = {
 
 static int gl861_frontend_attach(struct dvb_usb_adapter *adap)
 {
-       if ((adap->fe = dvb_attach(zl10353_attach, &gl861_zl10353_config,
-                                  &adap->dev->i2c_adap)) != NULL) {
-               return 0;
-       }
 
-       return -EIO;
+       adap->fe = dvb_attach(zl10353_attach, &gl861_zl10353_config,
+               &adap->dev->i2c_adap);
+       if (adap->fe == NULL)
+               return -EIO;
+
+       return 0;
 }
 
 static struct qt1010_config gl861_qt1010_config = {
@@ -156,7 +157,7 @@ static struct usb_device_id gl861_table [] = {
                { USB_DEVICE(USB_VID_ALINK, USB_VID_ALINK_DTU) },
                { }             /* Terminating entry */
 };
-MODULE_DEVICE_TABLE (usb, gl861_table);
+MODULE_DEVICE_TABLE(usb, gl861_table);
 
 static struct dvb_usb_device_properties gl861_properties = {
        .caps = DVB_USB_IS_AN_I2C_ADAPTER,
@@ -180,7 +181,7 @@ static struct dvb_usb_device_properties gl861_properties = {
                                }
                        }
                },
-       }},
+       } },
        .i2c_algo         = &gl861_i2c_algo,
 
        .num_device_descs = 2,
@@ -210,12 +211,11 @@ static int __init gl861_module_init(void)
 {
        int ret;
 
-       if ((ret = usb_register(&gl861_driver))) {
+       ret = usb_register(&gl861_driver);
+       if (ret)
                err("usb_register failed. Error number %d", ret);
-               return ret;
-       }
 
-       return 0;
+       return ret;
 }
 
 static void __exit gl861_module_exit(void)
@@ -224,8 +224,8 @@ static void __exit gl861_module_exit(void)
        usb_deregister(&gl861_driver);
 }
 
-module_init (gl861_module_init);
-module_exit (gl861_module_exit);
+module_init(gl861_module_init);
+module_exit(gl861_module_exit);
 
 MODULE_AUTHOR("Carl Lundqvist <comabug@gmail.com>");
 MODULE_DESCRIPTION("Driver MSI Mega Sky 580 DVB-T USB2.0 / GL861");
index 72a51afd5ee3abbf77b3e70e5009ca1b84916b72..c54855e2c233f8ae007e30a31f2f57ecf5c6e32c 100644 (file)
@@ -4,7 +4,7 @@
 #define DVB_USB_LOG_PREFIX "gl861"
 #include "dvb-usb.h"
 
-#define deb_rc(args...)   dprintk(dvb_usb_gl861_debug,0x01,args)
+#define deb_rc(args...)   dprintk(dvb_usb_gl861_debug, 0x01, args)
 
 #define GL861_WRITE            0x40
 #define GL861_READ             0xc0
index 03900d241a76da067545aaba61916d4c88385291..f7b71657f0f6324f57891d470faf1d2a4f30275f 100644 (file)
@@ -26,7 +26,6 @@
 #include <linux/slab.h>
 #include <linux/delay.h>
 #include "dvb_frontend.h"
-#include "dvb-pll.h"
 #include "au8522.h"
 
 struct au8522_state {
index a054894ff481867842ca344632b3feba1393e729..ea058153ebfa3dfd8e88660662fd7e4fa3f9ee50 100644 (file)
@@ -343,6 +343,52 @@ static struct dvb_pll_desc dvb_pll_opera1 = {
        }
 };
 
+static void samsung_dtos403ih102a_set(struct dvb_frontend *fe, u8 *buf,
+                      const struct dvb_frontend_parameters *params)
+{
+       struct dvb_pll_priv *priv = fe->tuner_priv;
+       struct i2c_msg msg = {
+               .addr = priv->pll_i2c_address,
+               .flags = 0,
+               .buf = buf,
+               .len = 4
+       };
+       int result;
+
+       if (fe->ops.i2c_gate_ctrl)
+               fe->ops.i2c_gate_ctrl(fe, 1);
+
+       result = i2c_transfer(priv->i2c, &msg, 1);
+       if (result != 1)
+               printk(KERN_ERR "%s: i2c_transfer failed:%d",
+                       __func__, result);
+
+       buf[2] = 0x9e;
+       buf[3] = 0x90;
+
+       return;
+}
+
+/* unknown pll used in Samsung DTOS403IH102A DVB-C tuner */
+static struct dvb_pll_desc dvb_pll_samsung_dtos403ih102a = {
+       .name   = "Samsung DTOS403IH102A",
+       .min    =  44250000,
+       .max    = 858000000,
+       .iffreq =  36125000,
+       .count  = 8,
+       .set    = samsung_dtos403ih102a_set,
+       .entries = {
+               { 135000000, 62500, 0xbe, 0x01 },
+               { 177000000, 62500, 0xf6, 0x01 },
+               { 370000000, 62500, 0xbe, 0x02 },
+               { 450000000, 62500, 0xf6, 0x02 },
+               { 466000000, 62500, 0xfe, 0x02 },
+               { 538000000, 62500, 0xbe, 0x08 },
+               { 826000000, 62500, 0xf6, 0x08 },
+               { 999999999, 62500, 0xfe, 0x08 },
+       }
+};
+
 /* ----------------------------------------------------------- */
 
 static struct dvb_pll_desc *pll_list[] = {
@@ -360,6 +406,7 @@ static struct dvb_pll_desc *pll_list[] = {
        [DVB_PLL_SAMSUNG_TBMV]           = &dvb_pll_samsung_tbmv,
        [DVB_PLL_PHILIPS_SD1878_TDA8261] = &dvb_pll_philips_sd1878_tda8261,
        [DVB_PLL_OPERA1]                 = &dvb_pll_opera1,
+       [DVB_PLL_SAMSUNG_DTOS403IH102A]  = &dvb_pll_samsung_dtos403ih102a,
 };
 
 /* ----------------------------------------------------------- */
index 872ca29e7cf37db7af523619637b24f2951c7408..05239f579ccfc007e10ac95fdcf53203a61dc7bc 100644 (file)
@@ -22,6 +22,7 @@
 #define DVB_PLL_SAMSUNG_TBMV           11
 #define DVB_PLL_PHILIPS_SD1878_TDA8261 12
 #define DVB_PLL_OPERA1                 13
+#define DVB_PLL_SAMSUNG_DTOS403IH102A  14
 
 /**
  * Attach a dvb-pll to the supplied frontend structure.
index f0195c8272f4486c4c67d402025f50e0bfe64d75..056387b41a8fc516e36e8b02d0835562a77eb8ec 100644 (file)
@@ -226,11 +226,16 @@ static int lgdt330x_init(struct dvb_frontend* fe)
                0x4c, 0x14
        };
 
-       static u8 flip_lgdt3303_init_data[] = {
+       static u8 flip_1_lgdt3303_init_data[] = {
                0x4c, 0x14,
                0x87, 0xf3
        };
 
+       static u8 flip_2_lgdt3303_init_data[] = {
+               0x4c, 0x14,
+               0x87, 0xda
+       };
+
        struct lgdt330x_state* state = fe->demodulator_priv;
        char  *chip_name;
        int    err;
@@ -243,10 +248,19 @@ static int lgdt330x_init(struct dvb_frontend* fe)
                break;
        case LGDT3303:
                chip_name = "LGDT3303";
-               if (state->config->clock_polarity_flip) {
-                       err = i2c_write_demod_bytes(state, flip_lgdt3303_init_data,
-                                                   sizeof(flip_lgdt3303_init_data));
-               } else {
+               switch (state->config->clock_polarity_flip) {
+               case 2:
+                       err = i2c_write_demod_bytes(state,
+                                       flip_2_lgdt3303_init_data,
+                                       sizeof(flip_2_lgdt3303_init_data));
+                       break;
+               case 1:
+                       err = i2c_write_demod_bytes(state,
+                                       flip_1_lgdt3303_init_data,
+                                       sizeof(flip_1_lgdt3303_init_data));
+                       break;
+               case 0:
+               default:
                        err = i2c_write_demod_bytes(state, lgdt3303_init_data,
                                                    sizeof(lgdt3303_init_data));
                }
index b999ec424ff7782041bc2c8fcb993a7355ec04dc..5ddb2dca305c9970e5a4ba315fbaed844b8f2754 100644 (file)
@@ -26,7 +26,6 @@
 #include <linux/slab.h>
 #include <linux/delay.h>
 #include "dvb_frontend.h"
-#include "dvb-pll.h"
 #include "s5h1409.h"
 
 struct s5h1409_state {
index eb5bfc99d4e95a942b370dc078389f1b276e274b..cff360ce1ba3f22bddbd4e051c51ae997a8bb595 100644 (file)
@@ -26,7 +26,6 @@
 #include <linux/slab.h>
 #include <linux/delay.h>
 #include "dvb_frontend.h"
-#include "dvb-pll.h"
 #include "s5h1411.h"
 
 struct s5h1411_state {
index c6ff5b82ff80d481e58151cbe47ee336fe76868d..a3c34eecdee9a81315bf68b44c5d6f23923c5cda 100644 (file)
 #include "dvb_frontend.h"
 #include "tda1002x.h"
 
+#define REG0_INIT_VAL 0x23
 
 struct tda10023_state {
        struct i2c_adapter* i2c;
        /* configuration settings */
-       const struct tda1002x_config* config;
+       const struct tda10023_config *config;
        struct dvb_frontend frontend;
 
        u8 pwm;
        u8 reg0;
-};
 
+       /* clock settings */
+       u32 xtal;
+       u8 pll_m;
+       u8 pll_p;
+       u8 pll_n;
+       u32 sysclk;
+};
 
 #define dprintk(x...)
 
 static int verbose;
 
-#define XTAL   28920000UL
-#define PLL_M  8UL
-#define PLL_P  4UL
-#define PLL_N  1UL
-#define SYSCLK (XTAL*PLL_M/(PLL_N*PLL_P))  // -> 57840000
-
-static u8 tda10023_inittab[]={
-       // reg mask val
-       0x2a,0xff,0x02,  // PLL3, Bypass, Power Down
-       0xff,0x64,0x00,  // Sleep 100ms
-       0x2a,0xff,0x03,  // PLL3, Bypass, Power Down
-       0xff,0x64,0x00,  // Sleep 100ms
-       0x28,0xff,PLL_M-1,  // PLL1 M=8
-       0x29,0xff,((PLL_P-1)<<6)|(PLL_N-1),  // PLL2
-       0x00,0xff,0x23,  // GPR FSAMPLING=1
-       0x2a,0xff,0x08,  // PLL3 PSACLK=1
-       0xff,0x64,0x00,  // Sleep 100ms
-       0x1f,0xff,0x00,  // RESET
-       0xff,0x64,0x00,  // Sleep 100ms
-       0xe6,0x0c,0x04,  // RSCFG_IND
-       0x10,0xc0,0x80,  // DECDVBCFG1 PBER=1
-
-       0x0e,0xff,0x82,  // GAIN1
-       0x03,0x08,0x08,  // CLKCONF DYN=1
-       0x2e,0xbf,0x30,  // AGCCONF2 TRIAGC=0,POSAGC=ENAGCIF=1 PPWMTUN=0 PPWMIF=0
-       0x01,0xff,0x30,  // AGCREF
-       0x1e,0x84,0x84,  // CONTROL SACLK_ON=1
-       0x1b,0xff,0xc8,  // ADC TWOS=1
-       0x3b,0xff,0xff,  // IFMAX
-       0x3c,0xff,0x00,  // IFMIN
-       0x34,0xff,0x00,  // PWMREF
-       0x35,0xff,0xff,  // TUNMAX
-       0x36,0xff,0x00,  // TUNMIN
-       0x06,0xff,0x7f,  // EQCONF1 POSI=7 ENADAPT=ENEQUAL=DFE=1    // 0x77
-       0x1c,0x30,0x30,  // EQCONF2 STEPALGO=SGNALGO=1
-       0x37,0xff,0xf6,  // DELTAF_LSB
-       0x38,0xff,0xff,  // DELTAF_MSB
-       0x02,0xff,0x93,  // AGCCONF1  IFS=1 KAGCIF=2 KAGCTUN=3
-       0x2d,0xff,0xf6,  // SWEEP SWPOS=1 SWDYN=7 SWSTEP=1 SWLEN=2
-       0x04,0x10,0x00,   // SWRAMP=1
-       0x12,0xff,0xa1,  // INTP1 POCLKP=1 FEL=1 MFS=0
-       0x2b,0x01,0xa1,  // INTS1
-       0x20,0xff,0x04,  // INTP2 SWAPP=? MSBFIRSTP=? INTPSEL=?
-       0x2c,0xff,0x0d,  // INTP/S TRIP=0 TRIS=0
-       0xc4,0xff,0x00,
-       0xc3,0x30,0x00,
-       0xb5,0xff,0x19,  // ERAGC_THD
-       0x00,0x03,0x01,  // GPR, CLBS soft reset
-       0x00,0x03,0x03,  // GPR, CLBS soft reset
-       0xff,0x64,0x00,  // Sleep 100ms
-       0xff,0xff,0xff
-};
-
 static u8 tda10023_readreg (struct tda10023_state* state, u8 reg)
 {
        u8 b0 [] = { reg };
@@ -219,30 +173,34 @@ static int tda10023_set_symbolrate (struct tda10023_state* state, u32 sr)
        s16 SFIL=0;
        u16 NDEC = 0;
 
-       if (sr < (u32)(SYSCLK/98.40)) {
+       /* avoid floating point operations multiplying syscloc and divider
+          by 10 */
+       u32 sysclk_x_10 = state->sysclk * 10;
+
+       if (sr < (u32)(sysclk_x_10/984)) {
                NDEC=3;
                SFIL=1;
-       } else if (sr<(u32)(SYSCLK/64.0)) {
+       } else if (sr < (u32)(sysclk_x_10/640)) {
                NDEC=3;
                SFIL=0;
-       } else if (sr<(u32)(SYSCLK/49.2)) {
+       } else if (sr < (u32)(sysclk_x_10/492)) {
                NDEC=2;
                SFIL=1;
-       } else if (sr<(u32)(SYSCLK/32.0)) {
+       } else if (sr < (u32)(sysclk_x_10/320)) {
                NDEC=2;
                SFIL=0;
-       } else if (sr<(u32)(SYSCLK/24.6)) {
+       } else if (sr < (u32)(sysclk_x_10/246)) {
                NDEC=1;
                SFIL=1;
-       } else if (sr<(u32)(SYSCLK/16.0)) {
+       } else if (sr < (u32)(sysclk_x_10/160)) {
                NDEC=1;
                SFIL=0;
-       } else if (sr<(u32)(SYSCLK/12.3)) {
+       } else if (sr < (u32)(sysclk_x_10/123)) {
                NDEC=0;
                SFIL=1;
        }
 
-       BDRI=SYSCLK*16;
+       BDRI = (state->sysclk)*16;
        BDRI>>=NDEC;
        BDRI +=sr/2;
        BDRI /=sr;
@@ -255,11 +213,12 @@ static int tda10023_set_symbolrate (struct tda10023_state* state, u32 sr)
 
                BDRX=1<<(24+NDEC);
                BDRX*=sr;
-               do_div(BDRX,SYSCLK);    // BDRX/=SYSCLK;
+               do_div(BDRX, state->sysclk);    /* BDRX/=SYSCLK; */
 
                BDR=(s32)BDRX;
        }
-//     printk("Symbolrate %i, BDR %i BDRI %i, NDEC %i\n",sr,BDR,BDRI,NDEC);
+       dprintk("Symbolrate %i, BDR %i BDRI %i, NDEC %i\n",
+               sr, BDR, BDRI, NDEC);
        tda10023_writebit (state, 0x03, 0xc0, NDEC<<6);
        tda10023_writereg (state, 0x0a, BDR&255);
        tda10023_writereg (state, 0x0b, (BDR>>8)&255);
@@ -272,8 +231,67 @@ static int tda10023_set_symbolrate (struct tda10023_state* state, u32 sr)
 static int tda10023_init (struct dvb_frontend *fe)
 {
        struct tda10023_state* state = fe->demodulator_priv;
+       u8 tda10023_inittab[] = {
+/*        reg  mask val */
+/* 000 */ 0x2a, 0xff, 0x02,  /* PLL3, Bypass, Power Down */
+/* 003 */ 0xff, 0x64, 0x00,  /* Sleep 100ms */
+/* 006 */ 0x2a, 0xff, 0x03,  /* PLL3, Bypass, Power Down */
+/* 009 */ 0xff, 0x64, 0x00,  /* Sleep 100ms */
+                          /* PLL1 */
+/* 012 */ 0x28, 0xff, (state->pll_m-1),
+                          /* PLL2 */
+/* 015 */ 0x29, 0xff, ((state->pll_p-1)<<6)|(state->pll_n-1),
+                          /* GPR FSAMPLING=1 */
+/* 018 */ 0x00, 0xff, REG0_INIT_VAL,
+/* 021 */ 0x2a, 0xff, 0x08,  /* PLL3 PSACLK=1 */
+/* 024 */ 0xff, 0x64, 0x00,  /* Sleep 100ms */
+/* 027 */ 0x1f, 0xff, 0x00,  /* RESET */
+/* 030 */ 0xff, 0x64, 0x00,  /* Sleep 100ms */
+/* 033 */ 0xe6, 0x0c, 0x04,  /* RSCFG_IND */
+/* 036 */ 0x10, 0xc0, 0x80,  /* DECDVBCFG1 PBER=1 */
+
+/* 039 */ 0x0e, 0xff, 0x82,  /* GAIN1 */
+/* 042 */ 0x03, 0x08, 0x08,  /* CLKCONF DYN=1 */
+/* 045 */ 0x2e, 0xbf, 0x30,  /* AGCCONF2 TRIAGC=0,POSAGC=ENAGCIF=1
+                                      PPWMTUN=0 PPWMIF=0 */
+/* 048 */ 0x01, 0xff, 0x30,  /* AGCREF */
+/* 051 */ 0x1e, 0x84, 0x84,  /* CONTROL SACLK_ON=1 */
+/* 054 */ 0x1b, 0xff, 0xc8,  /* ADC TWOS=1 */
+/* 057 */ 0x3b, 0xff, 0xff,  /* IFMAX */
+/* 060 */ 0x3c, 0xff, 0x00,  /* IFMIN */
+/* 063 */ 0x34, 0xff, 0x00,  /* PWMREF */
+/* 066 */ 0x35, 0xff, 0xff,  /* TUNMAX */
+/* 069 */ 0x36, 0xff, 0x00,  /* TUNMIN */
+/* 072 */ 0x06, 0xff, 0x7f,  /* EQCONF1 POSI=7 ENADAPT=ENEQUAL=DFE=1 */
+/* 075 */ 0x1c, 0x30, 0x30,  /* EQCONF2 STEPALGO=SGNALGO=1 */
+/* 078 */ 0x37, 0xff, 0xf6,  /* DELTAF_LSB */
+/* 081 */ 0x38, 0xff, 0xff,  /* DELTAF_MSB */
+/* 084 */ 0x02, 0xff, 0x93,  /* AGCCONF1  IFS=1 KAGCIF=2 KAGCTUN=3 */
+/* 087 */ 0x2d, 0xff, 0xf6,  /* SWEEP SWPOS=1 SWDYN=7 SWSTEP=1 SWLEN=2 */
+/* 090 */ 0x04, 0x10, 0x00,  /* SWRAMP=1 */
+/* 093 */ 0x12, 0xff, TDA10023_OUTPUT_MODE_PARALLEL_B, /*
+                               INTP1 POCLKP=1 FEL=1 MFS=0 */
+/* 096 */ 0x2b, 0x01, 0xa1,  /* INTS1 */
+/* 099 */ 0x20, 0xff, 0x04,  /* INTP2 SWAPP=? MSBFIRSTP=? INTPSEL=? */
+/* 102 */ 0x2c, 0xff, 0x0d,  /* INTP/S TRIP=0 TRIS=0 */
+/* 105 */ 0xc4, 0xff, 0x00,
+/* 108 */ 0xc3, 0x30, 0x00,
+/* 111 */ 0xb5, 0xff, 0x19,  /* ERAGC_THD */
+/* 114 */ 0x00, 0x03, 0x01,  /* GPR, CLBS soft reset */
+/* 117 */ 0x00, 0x03, 0x03,  /* GPR, CLBS soft reset */
+/* 120 */ 0xff, 0x64, 0x00,  /* Sleep 100ms */
+/* 123 */ 0xff, 0xff, 0xff
+};
+       dprintk("DVB: TDA10023(%d): init chip\n", fe->dvb->num);
+
+       /* override default values if set in config */
+       if (state->config->deltaf) {
+               tda10023_inittab[80] = (state->config->deltaf & 0xff);
+               tda10023_inittab[83] = (state->config->deltaf >> 8);
+       }
 
-       dprintk("DVB: TDA10023(%d): init chip\n", fe->adapter->num);
+       if (state->config->output_mode)
+               tda10023_inittab[95] = state->config->output_mode;
 
        tda10023_writetab(state, tda10023_inittab);
 
@@ -460,12 +478,11 @@ static void tda10023_release(struct dvb_frontend* fe)
 
 static struct dvb_frontend_ops tda10023_ops;
 
-struct dvb_frontend* tda10023_attach(const struct tda1002x_config* config,
-                                    struct i2c_adapteri2c,
+struct dvb_frontend *tda10023_attach(const struct tda10023_config *config,
+                                    struct i2c_adapter *i2c,
                                     u8 pwm)
 {
        struct tda10023_state* state = NULL;
-       int i;
 
        /* allocate memory for the internal state */
        state = kzalloc(sizeof(struct tda10023_state), GFP_KERNEL);
@@ -474,22 +491,40 @@ struct dvb_frontend* tda10023_attach(const struct tda1002x_config* config,
        /* setup the state */
        state->config = config;
        state->i2c = i2c;
-       memcpy(&state->frontend.ops, &tda10023_ops, sizeof(struct dvb_frontend_ops));
-       state->pwm = pwm;
-       for (i=0; i < ARRAY_SIZE(tda10023_inittab);i+=3) {
-               if (tda10023_inittab[i] == 0x00) {
-                       state->reg0 = tda10023_inittab[i+2];
-                       break;
-               }
-       }
 
-       // Wakeup if in standby
+       /* wakeup if in standby */
        tda10023_writereg (state, 0x00, 0x33);
        /* check if the demod is there */
        if ((tda10023_readreg(state, 0x1a) & 0xf0) != 0x70) goto error;
 
        /* create dvb_frontend */
        memcpy(&state->frontend.ops, &tda10023_ops, sizeof(struct dvb_frontend_ops));
+       state->pwm = pwm;
+       state->reg0 = REG0_INIT_VAL;
+       if (state->config->xtal) {
+               state->xtal  = state->config->xtal;
+               state->pll_m = state->config->pll_m;
+               state->pll_p = state->config->pll_p;
+               state->pll_n = state->config->pll_n;
+       } else {
+               /* set default values if not defined in config */
+               state->xtal  = 28920000;
+               state->pll_m = 8;
+               state->pll_p = 4;
+               state->pll_n = 1;
+       }
+
+       /* calc sysclk */
+       state->sysclk = (state->xtal * state->pll_m / \
+                       (state->pll_n * state->pll_p));
+
+       state->frontend.ops.info.symbol_rate_min = (state->sysclk/2)/64;
+       state->frontend.ops.info.symbol_rate_max = (state->sysclk/2)/4;
+
+       dprintk("DVB: TDA10023 %s: xtal:%d pll_m:%d pll_p:%d pll_n:%d\n",
+               __func__, state->xtal, state->pll_m, state->pll_p,
+               state->pll_n);
+
        state->frontend.demodulator_priv = state;
        return &state->frontend;
 
@@ -504,10 +539,10 @@ static struct dvb_frontend_ops tda10023_ops = {
                .name = "Philips TDA10023 DVB-C",
                .type = FE_QAM,
                .frequency_stepsize = 62500,
-               .frequency_min = 47000000,
+               .frequency_min =  47000000,
                .frequency_max = 862000000,
-               .symbol_rate_min = (SYSCLK/2)/64,     /* SACLK/64 == (SYSCLK/2)/64 */
-               .symbol_rate_max = (SYSCLK/2)/4,      /* SACLK/4 */
+               .symbol_rate_min = 0,  /* set in tda10023_attach */
+               .symbol_rate_max = 0,  /* set in tda10023_attach */
                .caps = 0x400 | //FE_CAN_QAM_4
                        FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 |
                        FE_CAN_QAM_128 | FE_CAN_QAM_256 |
index 1bcc0d44b90bf789d33b73d5bb93b96014f6ec02..04d19418bf2086254bb6dda50e8480bb176a9e72 100644 (file)
 
 #include <linux/dvb/frontend.h>
 
-struct tda1002x_config
-{
+struct tda1002x_config {
        /* the demodulator's i2c address */
        u8 demod_address;
        u8 invert;
 };
 
+enum tda10023_output_mode {
+       TDA10023_OUTPUT_MODE_PARALLEL_A = 0xe0,
+       TDA10023_OUTPUT_MODE_PARALLEL_B = 0xa1,
+       TDA10023_OUTPUT_MODE_PARALLEL_C = 0xa0,
+       TDA10023_OUTPUT_MODE_SERIAL, /* TODO: not implemented */
+};
+
+struct tda10023_config {
+       /* the demodulator's i2c address */
+       u8 demod_address;
+       u8 invert;
+
+       /* clock settings */
+       u32 xtal; /* defaults: 28920000 */
+       u8 pll_m; /* defaults: 8 */
+       u8 pll_p; /* defaults: 4 */
+       u8 pll_n; /* defaults: 1 */
+
+       /* MPEG2 TS output mode */
+       u8 output_mode;
+
+       /* input freq offset + baseband conversion type */
+       u16 deltaf;
+};
+
 #if defined(CONFIG_DVB_TDA10021) || (defined(CONFIG_DVB_TDA10021_MODULE) && defined(MODULE))
 extern struct dvb_frontend* tda10021_attach(const struct tda1002x_config* config,
                                            struct i2c_adapter* i2c, u8 pwm);
@@ -45,12 +69,15 @@ static inline struct dvb_frontend* tda10021_attach(const struct tda1002x_config*
 }
 #endif // CONFIG_DVB_TDA10021
 
-#if defined(CONFIG_DVB_TDA10023) || (defined(CONFIG_DVB_TDA10023_MODULE) && defined(MODULE))
-extern struct dvb_frontend* tda10023_attach(const struct tda1002x_config* config,
-                                           struct i2c_adapter* i2c, u8 pwm);
+#if defined(CONFIG_DVB_TDA10023) || \
+       (defined(CONFIG_DVB_TDA10023_MODULE) && defined(MODULE))
+extern struct dvb_frontend *tda10023_attach(
+       const struct tda10023_config *config,
+       struct i2c_adapter *i2c, u8 pwm);
 #else
-static inline struct dvb_frontend* tda10023_attach(const struct tda1002x_config* config,
-                                           struct i2c_adapter* i2c, u8 pwm)
+static inline struct dvb_frontend *tda10023_attach(
+       const struct tda10023_config *config,
+       struct i2c_adapter *i2c, u8 pwm)
 {
        printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
        return NULL;
index 960ed5763ae1300f929538210bdbb509d36c4be0..1360403b88b6953d778e300717e5e8c646bf2da0 100644 (file)
@@ -234,7 +234,7 @@ static void pluto_reset_ts(struct pluto *pluto, int reenable)
 
 static void pluto_set_dma_addr(struct pluto *pluto)
 {
-       pluto_writereg(pluto, REG_PCAR, cpu_to_le32(pluto->dma_addr));
+       pluto_writereg(pluto, REG_PCAR, pluto->dma_addr);
 }
 
 static int __devinit pluto_dma_map(struct pluto *pluto)
diff --git a/drivers/media/dvb/siano/Kconfig b/drivers/media/dvb/siano/Kconfig
new file mode 100644 (file)
index 0000000..dd863f2
--- /dev/null
@@ -0,0 +1,26 @@
+#
+# Siano Mobile Silicon Digital TV device configuration
+#
+
+config DVB_SIANO_SMS1XXX
+       tristate "Siano SMS1XXX USB dongle support"
+       depends on DVB_CORE && USB
+       ---help---
+         Choose Y here if you have a USB dongle with a SMS1XXX chipset.
+
+         To compile this driver as a module, choose M here: the
+         module will be called sms1xxx.
+
+config DVB_SIANO_SMS1XXX_SMS_IDS
+       bool "Enable support for Siano Mobile Silicon default USB IDs"
+       depends on DVB_SIANO_SMS1XXX
+       default y
+       ---help---
+         Choose Y here if you have a USB dongle with a SMS1XXX chipset
+         that uses Siano Mobile Silicon's default usb vid:pid.
+
+         Choose N here if you would prefer to use Siano's external driver.
+
+         Further documentation on this driver can be found on the WWW at
+         <http://www.siano-ms.com/>.
+
diff --git a/drivers/media/dvb/siano/Makefile b/drivers/media/dvb/siano/Makefile
new file mode 100644 (file)
index 0000000..ee0737a
--- /dev/null
@@ -0,0 +1,8 @@
+sms1xxx-objs := smscoreapi.o smsusb.o smsdvb.o sms-cards.o
+
+obj-$(CONFIG_DVB_SIANO_SMS1XXX) += sms1xxx.o
+
+EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
+
+EXTRA_CFLAGS += $(extra-cflags-y) $(extra-cflags-m)
+
diff --git a/drivers/media/dvb/siano/sms-cards.c b/drivers/media/dvb/siano/sms-cards.c
new file mode 100644 (file)
index 0000000..e7a8ac0
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ *  Card-specific functions for the Siano SMS1xxx USB dongle
+ *
+ *  Copyright (c) 2008 Michael Krufky <mkrufky@linuxtv.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 3 as
+ *  published by the Free Software Foundation;
+ *
+ *  Software distributed under the License is distributed on an "AS IS"
+ *  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+ *
+ *  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 "sms-cards.h"
+
+struct usb_device_id smsusb_id_table[] = {
+#ifdef CONFIG_DVB_SIANO_SMS1XXX_SMS_IDS
+       { USB_DEVICE(0x187f, 0x0010),
+               .driver_info = SMS1XXX_BOARD_SIANO_STELLAR },
+       { USB_DEVICE(0x187f, 0x0100),
+               .driver_info = SMS1XXX_BOARD_SIANO_STELLAR },
+       { USB_DEVICE(0x187f, 0x0200),
+               .driver_info = SMS1XXX_BOARD_SIANO_NOVA_A },
+       { USB_DEVICE(0x187f, 0x0201),
+               .driver_info = SMS1XXX_BOARD_SIANO_NOVA_B },
+       { USB_DEVICE(0x187f, 0x0300),
+               .driver_info = SMS1XXX_BOARD_SIANO_VEGA },
+#endif
+       { USB_DEVICE(0x2040, 0x1700),
+               .driver_info = SMS1XXX_BOARD_HAUPPAUGE_CATAMOUNT },
+       { USB_DEVICE(0x2040, 0x1800),
+               .driver_info = SMS1XXX_BOARD_HAUPPAUGE_OKEMO_A },
+       { USB_DEVICE(0x2040, 0x1801),
+               .driver_info = SMS1XXX_BOARD_HAUPPAUGE_OKEMO_B },
+       { USB_DEVICE(0x2040, 0x5500),
+               .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
+       { USB_DEVICE(0x2040, 0x5580),
+               .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
+       { USB_DEVICE(0x2040, 0x5590),
+               .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
+       { }             /* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, smsusb_id_table);
+
+static struct sms_board sms_boards[] = {
+       [SMS_BOARD_UNKNOWN] = {
+               .name   = "Unknown board",
+       },
+       [SMS1XXX_BOARD_SIANO_STELLAR] = {
+               .name   = "Siano Stellar Digital Receiver",
+               .type   = SMS_STELLAR,
+               .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-stellar-dvbt-01.fw",
+       },
+       [SMS1XXX_BOARD_SIANO_NOVA_A] = {
+               .name   = "Siano Nova A Digital Receiver",
+               .type   = SMS_NOVA_A0,
+               .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-nova-a-dvbt-01.fw",
+       },
+       [SMS1XXX_BOARD_SIANO_NOVA_B] = {
+               .name   = "Siano Nova B Digital Receiver",
+               .type   = SMS_NOVA_B0,
+               .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-nova-b-dvbt-01.fw",
+       },
+       [SMS1XXX_BOARD_SIANO_VEGA] = {
+               .name   = "Siano Vega Digital Receiver",
+               .type   = SMS_VEGA,
+       },
+       [SMS1XXX_BOARD_HAUPPAUGE_CATAMOUNT] = {
+               .name   = "Hauppauge Catamount",
+               .type   = SMS_STELLAR,
+               .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-stellar-dvbt-01.fw",
+       },
+       [SMS1XXX_BOARD_HAUPPAUGE_OKEMO_A] = {
+               .name   = "Hauppauge Okemo-A",
+               .type   = SMS_NOVA_A0,
+               .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-nova-a-dvbt-01.fw",
+       },
+       [SMS1XXX_BOARD_HAUPPAUGE_OKEMO_B] = {
+               .name   = "Hauppauge Okemo-B",
+               .type   = SMS_NOVA_B0,
+               .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-nova-b-dvbt-01.fw",
+       },
+       [SMS1XXX_BOARD_HAUPPAUGE_WINDHAM] = {
+               .name   = "Hauppauge WinTV-Nova-T-MiniStick",
+               .type   = SMS_NOVA_B0,
+               .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-hcw-55xxx-dvbt-01.fw",
+       },
+};
+
+struct sms_board *sms_get_board(int id)
+{
+       BUG_ON(id >= ARRAY_SIZE(sms_boards));
+
+       return &sms_boards[id];
+}
+
diff --git a/drivers/media/dvb/siano/sms-cards.h b/drivers/media/dvb/siano/sms-cards.h
new file mode 100644 (file)
index 0000000..83b39bc
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ *  Card-specific functions for the Siano SMS1xxx USB dongle
+ *
+ *  Copyright (c) 2008 Michael Krufky <mkrufky@linuxtv.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 3 as
+ *  published by the Free Software Foundation;
+ *
+ *  Software distributed under the License is distributed on an "AS IS"
+ *  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+ *
+ *  See the GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __SMS_CARDS_H__
+#define __SMS_CARDS_H__
+
+#include <linux/usb.h>
+#include "smscoreapi.h"
+
+#define SMS_BOARD_UNKNOWN 0
+#define SMS1XXX_BOARD_SIANO_STELLAR 1
+#define SMS1XXX_BOARD_SIANO_NOVA_A  2
+#define SMS1XXX_BOARD_SIANO_NOVA_B  3
+#define SMS1XXX_BOARD_SIANO_VEGA    4
+#define SMS1XXX_BOARD_HAUPPAUGE_CATAMOUNT 5
+#define SMS1XXX_BOARD_HAUPPAUGE_OKEMO_A 6
+#define SMS1XXX_BOARD_HAUPPAUGE_OKEMO_B 7
+#define SMS1XXX_BOARD_HAUPPAUGE_WINDHAM 8
+
+struct sms_board {
+       enum sms_device_type_st type;
+       char *name, *fw[DEVICE_MODE_MAX];
+};
+
+struct sms_board *sms_get_board(int id);
+
+extern struct usb_device_id smsusb_id_table[];
+
+#endif /* __SMS_CARDS_H__ */
diff --git a/drivers/media/dvb/siano/smscoreapi.c b/drivers/media/dvb/siano/smscoreapi.c
new file mode 100644 (file)
index 0000000..b4b8ed7
--- /dev/null
@@ -0,0 +1,1251 @@
+/*
+ *  Siano core API module
+ *
+ *  This file contains implementation for the interface to sms core component
+ *
+ *  author: Anatoly Greenblat
+ *
+ *  Copyright (c), 2005-2008 Siano Mobile Silicon, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 3 as
+ *  published by the Free Software Foundation;
+ *
+ *  Software distributed under the License is distributed on an "AS IS"
+ *  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+ *
+ *  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/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+
+#include <linux/firmware.h>
+
+#include "smscoreapi.h"
+#include "sms-cards.h"
+
+int sms_debug;
+module_param_named(debug, sms_debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debug level (info=1, adv=2 (or-able))");
+
+struct smscore_device_notifyee_t {
+       struct list_head entry;
+       hotplug_t hotplug;
+};
+
+struct smscore_idlist_t {
+       struct list_head entry;
+       int             id;
+       int             data_type;
+};
+
+struct smscore_client_t {
+       struct list_head entry;
+       struct smscore_device_t *coredev;
+       void                    *context;
+       struct list_head        idlist;
+       onresponse_t    onresponse_handler;
+       onremove_t              onremove_handler;
+};
+
+struct smscore_device_t {
+       struct list_head entry;
+
+       struct list_head clients;
+       struct list_head subclients;
+       spinlock_t              clientslock;
+
+       struct list_head buffers;
+       spinlock_t              bufferslock;
+       int                             num_buffers;
+
+       void                    *common_buffer;
+       int                             common_buffer_size;
+       dma_addr_t              common_buffer_phys;
+
+       void                    *context;
+       struct device   *device;
+
+       char                    devpath[32];
+       unsigned long   device_flags;
+
+       setmode_t               setmode_handler;
+       detectmode_t    detectmode_handler;
+       sendrequest_t   sendrequest_handler;
+       preload_t               preload_handler;
+       postload_t              postload_handler;
+
+       int                             mode, modes_supported;
+
+       struct completion version_ex_done, data_download_done, trigger_done;
+       struct completion init_device_done, reload_start_done, resume_done;
+
+       int board_id;
+};
+
+void smscore_set_board_id(struct smscore_device_t *core, int id)
+{
+       core->board_id = id;
+}
+
+int smscore_get_board_id(struct smscore_device_t *core)
+{
+       return core->board_id;
+}
+
+struct smscore_registry_entry_t {
+       struct list_head entry;
+       char                    devpath[32];
+       int                             mode;
+       enum sms_device_type_st type;
+};
+
+struct list_head g_smscore_notifyees;
+struct list_head g_smscore_devices;
+struct mutex g_smscore_deviceslock;
+
+struct list_head g_smscore_registry;
+struct mutex g_smscore_registrylock;
+
+static int default_mode = 4;
+
+module_param(default_mode, int, 0644);
+MODULE_PARM_DESC(default_mode, "default firmware id (device mode)");
+
+static struct smscore_registry_entry_t *smscore_find_registry(char *devpath)
+{
+       struct smscore_registry_entry_t *entry;
+       struct list_head *next;
+
+       kmutex_lock(&g_smscore_registrylock);
+       for (next = g_smscore_registry.next;
+            next != &g_smscore_registry;
+            next = next->next) {
+               entry = (struct smscore_registry_entry_t *) next;
+               if (!strcmp(entry->devpath, devpath)) {
+                       kmutex_unlock(&g_smscore_registrylock);
+                       return entry;
+               }
+       }
+       entry = (struct smscore_registry_entry_t *)
+                       kmalloc(sizeof(struct smscore_registry_entry_t),
+                               GFP_KERNEL);
+       if (entry) {
+               entry->mode = default_mode;
+               strcpy(entry->devpath, devpath);
+               list_add(&entry->entry, &g_smscore_registry);
+       } else
+               sms_err("failed to create smscore_registry.");
+       kmutex_unlock(&g_smscore_registrylock);
+       return entry;
+}
+
+int smscore_registry_getmode(char *devpath)
+{
+       struct smscore_registry_entry_t *entry;
+
+       entry = smscore_find_registry(devpath);
+       if (entry)
+               return entry->mode;
+       else
+               sms_err("No registry found.");
+
+       return default_mode;
+}
+
+static enum sms_device_type_st smscore_registry_gettype(char *devpath)
+{
+       struct smscore_registry_entry_t *entry;
+
+       entry = smscore_find_registry(devpath);
+       if (entry)
+               return entry->type;
+       else
+               sms_err("No registry found.");
+
+       return -1;
+}
+
+void smscore_registry_setmode(char *devpath, int mode)
+{
+       struct smscore_registry_entry_t *entry;
+
+       entry = smscore_find_registry(devpath);
+       if (entry)
+               entry->mode = mode;
+       else
+               sms_err("No registry found.");
+}
+
+static void smscore_registry_settype(char *devpath,
+                                    enum sms_device_type_st type)
+{
+       struct smscore_registry_entry_t *entry;
+
+       entry = smscore_find_registry(devpath);
+       if (entry)
+               entry->type = type;
+       else
+               sms_err("No registry found.");
+}
+
+
+static void list_add_locked(struct list_head *new, struct list_head *head,
+                           spinlock_t *lock)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(lock, flags);
+
+       list_add(new, head);
+
+       spin_unlock_irqrestore(lock, flags);
+}
+
+/**
+ * register a client callback that called when device plugged in/unplugged
+ * NOTE: if devices exist callback is called immediately for each device
+ *
+ * @param hotplug callback
+ *
+ * @return 0 on success, <0 on error.
+ */
+int smscore_register_hotplug(hotplug_t hotplug)
+{
+       struct smscore_device_notifyee_t *notifyee;
+       struct list_head *next, *first;
+       int rc = 0;
+
+       kmutex_lock(&g_smscore_deviceslock);
+
+       notifyee = kmalloc(sizeof(struct smscore_device_notifyee_t),
+                          GFP_KERNEL);
+       if (notifyee) {
+               /* now notify callback about existing devices */
+               first = &g_smscore_devices;
+               for (next = first->next;
+                    next != first && !rc;
+                    next = next->next) {
+                       struct smscore_device_t *coredev =
+                               (struct smscore_device_t *) next;
+                       rc = hotplug(coredev, coredev->device, 1);
+               }
+
+               if (rc >= 0) {
+                       notifyee->hotplug = hotplug;
+                       list_add(&notifyee->entry, &g_smscore_notifyees);
+               } else
+                       kfree(notifyee);
+       } else
+               rc = -ENOMEM;
+
+       kmutex_unlock(&g_smscore_deviceslock);
+
+       return rc;
+}
+
+/**
+ * unregister a client callback that called when device plugged in/unplugged
+ *
+ * @param hotplug callback
+ *
+ */
+void smscore_unregister_hotplug(hotplug_t hotplug)
+{
+       struct list_head *next, *first;
+
+       kmutex_lock(&g_smscore_deviceslock);
+
+       first = &g_smscore_notifyees;
+
+       for (next = first->next; next != first;) {
+               struct smscore_device_notifyee_t *notifyee =
+                       (struct smscore_device_notifyee_t *) next;
+               next = next->next;
+
+               if (notifyee->hotplug == hotplug) {
+                       list_del(&notifyee->entry);
+                       kfree(notifyee);
+               }
+       }
+
+       kmutex_unlock(&g_smscore_deviceslock);
+}
+
+static void smscore_notify_clients(struct smscore_device_t *coredev)
+{
+       struct smscore_client_t *client;
+
+       /* the client must call smscore_unregister_client from remove handler */
+       while (!list_empty(&coredev->clients)) {
+               client = (struct smscore_client_t *) coredev->clients.next;
+               client->onremove_handler(client->context);
+       }
+}
+
+static int smscore_notify_callbacks(struct smscore_device_t *coredev,
+                                   struct device *device, int arrival)
+{
+       struct list_head *next, *first;
+       int rc = 0;
+
+       /* note: must be called under g_deviceslock */
+
+       first = &g_smscore_notifyees;
+
+       for (next = first->next; next != first; next = next->next) {
+               rc = ((struct smscore_device_notifyee_t *) next)->
+                               hotplug(coredev, device, arrival);
+               if (rc < 0)
+                       break;
+       }
+
+       return rc;
+}
+
+static struct
+smscore_buffer_t *smscore_createbuffer(u8 *buffer, void *common_buffer,
+                                      dma_addr_t common_buffer_phys)
+{
+       struct smscore_buffer_t *cb =
+               kmalloc(sizeof(struct smscore_buffer_t), GFP_KERNEL);
+       if (!cb) {
+               sms_info("kmalloc(...) failed");
+               return NULL;
+       }
+
+       cb->p = buffer;
+       cb->offset_in_common = buffer - (u8 *) common_buffer;
+       cb->phys = common_buffer_phys + cb->offset_in_common;
+
+       return cb;
+}
+
+/**
+ * creates coredev object for a device, prepares buffers,
+ * creates buffer mappings, notifies registered hotplugs about new device.
+ *
+ * @param params device pointer to struct with device specific parameters
+ *               and handlers
+ * @param coredev pointer to a value that receives created coredev object
+ *
+ * @return 0 on success, <0 on error.
+ */
+int smscore_register_device(struct smsdevice_params_t *params,
+                           struct smscore_device_t **coredev)
+{
+       struct smscore_device_t *dev;
+       u8 *buffer;
+
+       dev = kzalloc(sizeof(struct smscore_device_t), GFP_KERNEL);
+       if (!dev) {
+               sms_info("kzalloc(...) failed");
+               return -ENOMEM;
+       }
+
+       /* init list entry so it could be safe in smscore_unregister_device */
+       INIT_LIST_HEAD(&dev->entry);
+
+       /* init queues */
+       INIT_LIST_HEAD(&dev->clients);
+       INIT_LIST_HEAD(&dev->buffers);
+
+       /* init locks */
+       spin_lock_init(&dev->clientslock);
+       spin_lock_init(&dev->bufferslock);
+
+       /* init completion events */
+       init_completion(&dev->version_ex_done);
+       init_completion(&dev->data_download_done);
+       init_completion(&dev->trigger_done);
+       init_completion(&dev->init_device_done);
+       init_completion(&dev->reload_start_done);
+       init_completion(&dev->resume_done);
+
+       /* alloc common buffer */
+       dev->common_buffer_size = params->buffer_size * params->num_buffers;
+       dev->common_buffer = dma_alloc_coherent(NULL, dev->common_buffer_size,
+                                               &dev->common_buffer_phys,
+                                               GFP_KERNEL | GFP_DMA);
+       if (!dev->common_buffer) {
+               smscore_unregister_device(dev);
+               return -ENOMEM;
+       }
+
+       /* prepare dma buffers */
+       for (buffer = dev->common_buffer;
+            dev->num_buffers < params->num_buffers;
+            dev->num_buffers++, buffer += params->buffer_size) {
+               struct smscore_buffer_t *cb =
+                       smscore_createbuffer(buffer, dev->common_buffer,
+                                            dev->common_buffer_phys);
+               if (!cb) {
+                       smscore_unregister_device(dev);
+                       return -ENOMEM;
+               }
+
+               smscore_putbuffer(dev, cb);
+       }
+
+       sms_info("allocated %d buffers", dev->num_buffers);
+
+       dev->mode = DEVICE_MODE_NONE;
+       dev->context = params->context;
+       dev->device = params->device;
+       dev->setmode_handler = params->setmode_handler;
+       dev->detectmode_handler = params->detectmode_handler;
+       dev->sendrequest_handler = params->sendrequest_handler;
+       dev->preload_handler = params->preload_handler;
+       dev->postload_handler = params->postload_handler;
+
+       dev->device_flags = params->flags;
+       strcpy(dev->devpath, params->devpath);
+
+       smscore_registry_settype(dev->devpath, params->device_type);
+
+       /* add device to devices list */
+       kmutex_lock(&g_smscore_deviceslock);
+       list_add(&dev->entry, &g_smscore_devices);
+       kmutex_unlock(&g_smscore_deviceslock);
+
+       *coredev = dev;
+
+       sms_info("device %p created", dev);
+
+       return 0;
+}
+
+/**
+ * sets initial device mode and notifies client hotplugs that device is ready
+ *
+ * @param coredev pointer to a coredev object returned by
+ *               smscore_register_device
+ *
+ * @return 0 on success, <0 on error.
+ */
+int smscore_start_device(struct smscore_device_t *coredev)
+{
+       int rc = smscore_set_device_mode(
+                       coredev, smscore_registry_getmode(coredev->devpath));
+       if (rc < 0) {
+               sms_info("set device mode faile , rc %d", rc);
+               return rc;
+       }
+
+       kmutex_lock(&g_smscore_deviceslock);
+
+       rc = smscore_notify_callbacks(coredev, coredev->device, 1);
+
+       sms_info("device %p started, rc %d", coredev, rc);
+
+       kmutex_unlock(&g_smscore_deviceslock);
+
+       return rc;
+}
+
+static int smscore_sendrequest_and_wait(struct smscore_device_t *coredev,
+                                       void *buffer, size_t size,
+                                       struct completion *completion)
+{
+       int rc = coredev->sendrequest_handler(coredev->context, buffer, size);
+       if (rc < 0) {
+               sms_info("sendrequest returned error %d", rc);
+               return rc;
+       }
+
+       return wait_for_completion_timeout(completion,
+                                          msecs_to_jiffies(10000)) ?
+                                               0 : -ETIME;
+}
+
+static int smscore_load_firmware_family2(struct smscore_device_t *coredev,
+                                        void *buffer, size_t size)
+{
+       struct SmsFirmware_ST *firmware = (struct SmsFirmware_ST *) buffer;
+       struct SmsMsgHdr_ST *msg;
+       u32 mem_address = firmware->StartAddress;
+       u8 *payload = firmware->Payload;
+       int rc = 0;
+
+       sms_info("loading FW to addr 0x%x size %d",
+                mem_address, firmware->Length);
+       if (coredev->preload_handler) {
+               rc = coredev->preload_handler(coredev->context);
+               if (rc < 0)
+                       return rc;
+       }
+
+       /* PAGE_SIZE buffer shall be enough and dma aligned */
+       msg = kmalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
+       if (!msg)
+               return -ENOMEM;
+
+       if (coredev->mode != DEVICE_MODE_NONE) {
+               sms_debug("sending reload command.");
+               SMS_INIT_MSG(msg, MSG_SW_RELOAD_START_REQ,
+                            sizeof(struct SmsMsgHdr_ST));
+               rc = smscore_sendrequest_and_wait(coredev, msg,
+                                                 msg->msgLength,
+                                                 &coredev->reload_start_done);
+               mem_address = *(u32 *) &payload[20];
+       }
+
+       while (size && rc >= 0) {
+               struct SmsDataDownload_ST *DataMsg =
+                       (struct SmsDataDownload_ST *) msg;
+               int payload_size = min((int) size, SMS_MAX_PAYLOAD_SIZE);
+
+               SMS_INIT_MSG(msg, MSG_SMS_DATA_DOWNLOAD_REQ,
+                            (u16)(sizeof(struct SmsMsgHdr_ST) +
+                                     sizeof(u32) + payload_size));
+
+               DataMsg->MemAddr = mem_address;
+               memcpy(DataMsg->Payload, payload, payload_size);
+
+               if ((coredev->device_flags & SMS_ROM_NO_RESPONSE) &&
+                   (coredev->mode == DEVICE_MODE_NONE))
+                       rc = coredev->sendrequest_handler(
+                               coredev->context, DataMsg,
+                               DataMsg->xMsgHeader.msgLength);
+               else
+                       rc = smscore_sendrequest_and_wait(
+                               coredev, DataMsg,
+                               DataMsg->xMsgHeader.msgLength,
+                               &coredev->data_download_done);
+
+               payload += payload_size;
+               size -= payload_size;
+               mem_address += payload_size;
+       }
+
+       if (rc >= 0) {
+               if (coredev->mode == DEVICE_MODE_NONE) {
+                       struct SmsMsgData_ST *TriggerMsg =
+                               (struct SmsMsgData_ST *) msg;
+
+                       SMS_INIT_MSG(msg, MSG_SMS_SWDOWNLOAD_TRIGGER_REQ,
+                                    sizeof(struct SmsMsgHdr_ST) +
+                                    sizeof(u32) * 5);
+
+                       TriggerMsg->msgData[0] = firmware->StartAddress;
+                                               /* Entry point */
+                       TriggerMsg->msgData[1] = 5; /* Priority */
+                       TriggerMsg->msgData[2] = 0x200; /* Stack size */
+                       TriggerMsg->msgData[3] = 0; /* Parameter */
+                       TriggerMsg->msgData[4] = 4; /* Task ID */
+
+                       if (coredev->device_flags & SMS_ROM_NO_RESPONSE) {
+                               rc = coredev->sendrequest_handler(
+                                       coredev->context, TriggerMsg,
+                                       TriggerMsg->xMsgHeader.msgLength);
+                               msleep(100);
+                       } else
+                               rc = smscore_sendrequest_and_wait(
+                                       coredev, TriggerMsg,
+                                       TriggerMsg->xMsgHeader.msgLength,
+                                       &coredev->trigger_done);
+               } else {
+                       SMS_INIT_MSG(msg, MSG_SW_RELOAD_EXEC_REQ,
+                                    sizeof(struct SmsMsgHdr_ST));
+
+                       rc = coredev->sendrequest_handler(coredev->context,
+                                                         msg, msg->msgLength);
+               }
+               msleep(500);
+       }
+
+       sms_debug("rc=%d, postload=%p ", rc,
+                 coredev->postload_handler);
+
+       kfree(msg);
+
+       return ((rc >= 0) && coredev->postload_handler) ?
+               coredev->postload_handler(coredev->context) :
+               rc;
+}
+
+/**
+ * loads specified firmware into a buffer and calls device loadfirmware_handler
+ *
+ * @param coredev pointer to a coredev object returned by
+ *                smscore_register_device
+ * @param filename null-terminated string specifies firmware file name
+ * @param loadfirmware_handler device handler that loads firmware
+ *
+ * @return 0 on success, <0 on error.
+ */
+static int smscore_load_firmware_from_file(struct smscore_device_t *coredev,
+                                          char *filename,
+                                          loadfirmware_t loadfirmware_handler)
+{
+       int rc = -ENOENT;
+       const struct firmware *fw;
+       u8 *fw_buffer;
+
+       if (loadfirmware_handler == NULL && !(coredev->device_flags &
+                                             SMS_DEVICE_FAMILY2))
+               return -EINVAL;
+
+       rc = request_firmware(&fw, filename, coredev->device);
+       if (rc < 0) {
+               sms_info("failed to open \"%s\"", filename);
+               return rc;
+       }
+       sms_info("read FW %s, size=%zd", filename, fw->size);
+       fw_buffer = kmalloc(ALIGN(fw->size, SMS_ALLOC_ALIGNMENT),
+                           GFP_KERNEL | GFP_DMA);
+       if (fw_buffer) {
+               memcpy(fw_buffer, fw->data, fw->size);
+
+               rc = (coredev->device_flags & SMS_DEVICE_FAMILY2) ?
+                     smscore_load_firmware_family2(coredev,
+                                                   fw_buffer,
+                                                   fw->size) :
+                     loadfirmware_handler(coredev->context,
+                                          fw_buffer, fw->size);
+
+               kfree(fw_buffer);
+       } else {
+               sms_info("failed to allocate firmware buffer");
+               rc = -ENOMEM;
+       }
+
+       release_firmware(fw);
+
+       return rc;
+}
+
+/**
+ * notifies all clients registered with the device, notifies hotplugs,
+ * frees all buffers and coredev object
+ *
+ * @param coredev pointer to a coredev object returned by
+ *                smscore_register_device
+ *
+ * @return 0 on success, <0 on error.
+ */
+void smscore_unregister_device(struct smscore_device_t *coredev)
+{
+       struct smscore_buffer_t *cb;
+       int num_buffers = 0;
+       int retry = 0;
+
+       kmutex_lock(&g_smscore_deviceslock);
+
+       smscore_notify_clients(coredev);
+       smscore_notify_callbacks(coredev, NULL, 0);
+
+       /* at this point all buffers should be back
+        * onresponse must no longer be called */
+
+       while (1) {
+               while ((cb = smscore_getbuffer(coredev))) {
+                       kfree(cb);
+                       num_buffers++;
+               }
+               if (num_buffers == coredev->num_buffers)
+                       break;
+               if (++retry > 10) {
+                       sms_info("exiting although "
+                                "not all buffers released.");
+                       break;
+               }
+
+               sms_info("waiting for %d buffer(s)",
+                        coredev->num_buffers - num_buffers);
+               msleep(100);
+       }
+
+       sms_info("freed %d buffers", num_buffers);
+
+       if (coredev->common_buffer)
+               dma_free_coherent(NULL, coredev->common_buffer_size,
+                                 coredev->common_buffer,
+                                 coredev->common_buffer_phys);
+
+       list_del(&coredev->entry);
+       kfree(coredev);
+
+       kmutex_unlock(&g_smscore_deviceslock);
+
+       sms_info("device %p destroyed", coredev);
+}
+
+static int smscore_detect_mode(struct smscore_device_t *coredev)
+{
+       void *buffer = kmalloc(sizeof(struct SmsMsgHdr_ST) + SMS_DMA_ALIGNMENT,
+                              GFP_KERNEL | GFP_DMA);
+       struct SmsMsgHdr_ST *msg =
+               (struct SmsMsgHdr_ST *) SMS_ALIGN_ADDRESS(buffer);
+       int rc;
+
+       if (!buffer)
+               return -ENOMEM;
+
+       SMS_INIT_MSG(msg, MSG_SMS_GET_VERSION_EX_REQ,
+                    sizeof(struct SmsMsgHdr_ST));
+
+       rc = smscore_sendrequest_and_wait(coredev, msg, msg->msgLength,
+                                         &coredev->version_ex_done);
+       if (rc == -ETIME) {
+               sms_err("MSG_SMS_GET_VERSION_EX_REQ failed first try");
+
+               if (wait_for_completion_timeout(&coredev->resume_done,
+                                               msecs_to_jiffies(5000))) {
+                       rc = smscore_sendrequest_and_wait(
+                               coredev, msg, msg->msgLength,
+                               &coredev->version_ex_done);
+                       if (rc < 0)
+                               sms_err("MSG_SMS_GET_VERSION_EX_REQ failed "
+                                       "second try, rc %d", rc);
+               } else
+                       rc = -ETIME;
+       }
+
+       kfree(buffer);
+
+       return rc;
+}
+
+static char *smscore_fw_lkup[][SMS_NUM_OF_DEVICE_TYPES] = {
+       /*Stellar               NOVA A0         Nova B0         VEGA*/
+       /*DVBT*/
+       {"none", "dvb_nova_12mhz.inp", "dvb_nova_12mhz_b0.inp", "none"},
+       /*DVBH*/
+       {"none", "dvb_nova_12mhz.inp", "dvb_nova_12mhz_b0.inp", "none"},
+       /*TDMB*/
+       {"none", "tdmb_nova_12mhz.inp", "none", "none"},
+       /*DABIP*/
+       {"none", "none", "none", "none"},
+       /*BDA*/
+       {"none", "dvb_nova_12mhz.inp", "dvb_nova_12mhz_b0.inp", "none"},
+       /*ISDBT*/
+       {"none", "isdbt_nova_12mhz.inp", "dvb_nova_12mhz.inp", "none"},
+       /*ISDBTBDA*/
+       {"none", "isdbt_nova_12mhz.inp", "isdbt_nova_12mhz_b0.inp", "none"},
+       /*CMMB*/
+       {"none", "none", "none", "cmmb_vega_12mhz.inp"}
+};
+
+static inline char *sms_get_fw_name(struct smscore_device_t *coredev,
+                                   int mode, enum sms_device_type_st type)
+{
+       char **fw = sms_get_board(smscore_get_board_id(coredev))->fw;
+       return (fw && fw[mode]) ? fw[mode] : smscore_fw_lkup[mode][type];
+}
+
+/**
+ * calls device handler to change mode of operation
+ * NOTE: stellar/usb may disconnect when changing mode
+ *
+ * @param coredev pointer to a coredev object returned by
+ *                smscore_register_device
+ * @param mode requested mode of operation
+ *
+ * @return 0 on success, <0 on error.
+ */
+int smscore_set_device_mode(struct smscore_device_t *coredev, int mode)
+{
+       void *buffer;
+       int rc = 0;
+       enum sms_device_type_st type;
+
+       sms_debug("set device mode to %d", mode);
+       if (coredev->device_flags & SMS_DEVICE_FAMILY2) {
+               if (mode < DEVICE_MODE_DVBT || mode > DEVICE_MODE_RAW_TUNER) {
+                       sms_err("invalid mode specified %d", mode);
+                       return -EINVAL;
+               }
+
+               smscore_registry_setmode(coredev->devpath, mode);
+
+               if (!(coredev->device_flags & SMS_DEVICE_NOT_READY)) {
+                       rc = smscore_detect_mode(coredev);
+                       if (rc < 0) {
+                               sms_err("mode detect failed %d", rc);
+                               return rc;
+                       }
+               }
+
+               if (coredev->mode == mode) {
+                       sms_info("device mode %d already set", mode);
+                       return 0;
+               }
+
+               if (!(coredev->modes_supported & (1 << mode))) {
+                       char *fw_filename;
+
+                       type = smscore_registry_gettype(coredev->devpath);
+                       fw_filename = sms_get_fw_name(coredev, mode, type);
+
+                       rc = smscore_load_firmware_from_file(coredev,
+                                                            fw_filename, NULL);
+                       if (rc < 0) {
+                               sms_warn("error %d loading firmware: %s, "
+                                        "trying again with default firmware",
+                                        rc, fw_filename);
+
+                               /* try again with the default firmware */
+                               fw_filename = smscore_fw_lkup[mode][type];
+                               rc = smscore_load_firmware_from_file(coredev,
+                                                            fw_filename, NULL);
+
+                               if (rc < 0) {
+                                       sms_warn("error %d loading "
+                                                "firmware: %s", rc,
+                                                fw_filename);
+                                       return rc;
+                               }
+                       }
+                       sms_log("firmware download success: %s", fw_filename);
+               } else
+                       sms_info("mode %d supported by running "
+                                "firmware", mode);
+
+               buffer = kmalloc(sizeof(struct SmsMsgData_ST) +
+                                SMS_DMA_ALIGNMENT, GFP_KERNEL | GFP_DMA);
+               if (buffer) {
+                       struct SmsMsgData_ST *msg =
+                               (struct SmsMsgData_ST *)
+                                       SMS_ALIGN_ADDRESS(buffer);
+
+                       SMS_INIT_MSG(&msg->xMsgHeader, MSG_SMS_INIT_DEVICE_REQ,
+                                    sizeof(struct SmsMsgData_ST));
+                       msg->msgData[0] = mode;
+
+                       rc = smscore_sendrequest_and_wait(
+                               coredev, msg, msg->xMsgHeader.msgLength,
+                               &coredev->init_device_done);
+
+                       kfree(buffer);
+               } else {
+                       sms_err("Could not allocate buffer for "
+                               "init device message.");
+                       rc = -ENOMEM;
+               }
+       } else {
+               if (mode < DEVICE_MODE_DVBT || mode > DEVICE_MODE_DVBT_BDA) {
+                       sms_err("invalid mode specified %d", mode);
+                       return -EINVAL;
+               }
+
+               smscore_registry_setmode(coredev->devpath, mode);
+
+               if (coredev->detectmode_handler)
+                       coredev->detectmode_handler(coredev->context,
+                                                   &coredev->mode);
+
+               if (coredev->mode != mode && coredev->setmode_handler)
+                       rc = coredev->setmode_handler(coredev->context, mode);
+       }
+
+       if (rc >= 0) {
+               coredev->mode = mode;
+               coredev->device_flags &= ~SMS_DEVICE_NOT_READY;
+       }
+
+       if (rc != 0)
+               sms_err("return error code %d.", rc);
+       return rc;
+}
+
+/**
+ * calls device handler to get current mode of operation
+ *
+ * @param coredev pointer to a coredev object returned by
+ *                smscore_register_device
+ *
+ * @return current mode
+ */
+int smscore_get_device_mode(struct smscore_device_t *coredev)
+{
+       return coredev->mode;
+}
+
+/**
+ * find client by response id & type within the clients list.
+ * return client handle or NULL.
+ *
+ * @param coredev pointer to a coredev object returned by
+ *                smscore_register_device
+ * @param data_type client data type (SMS_DONT_CARE for all types)
+ * @param id client id (SMS_DONT_CARE for all id)
+ *
+ */
+static struct
+smscore_client_t *smscore_find_client(struct smscore_device_t *coredev,
+                                     int data_type, int id)
+{
+       struct smscore_client_t *client = NULL;
+       struct list_head *next, *first;
+       unsigned long flags;
+       struct list_head *firstid, *nextid;
+
+
+       spin_lock_irqsave(&coredev->clientslock, flags);
+       first = &coredev->clients;
+       for (next = first->next;
+            (next != first) && !client;
+            next = next->next) {
+               firstid = &((struct smscore_client_t *)next)->idlist;
+               for (nextid = firstid->next;
+                    nextid != firstid;
+                    nextid = nextid->next) {
+                       if ((((struct smscore_idlist_t *)nextid)->id == id) &&
+                           (((struct smscore_idlist_t *)nextid)->data_type == data_type ||
+                           (((struct smscore_idlist_t *)nextid)->data_type == 0))) {
+                               client = (struct smscore_client_t *) next;
+                               break;
+                       }
+               }
+       }
+       spin_unlock_irqrestore(&coredev->clientslock, flags);
+       return client;
+}
+
+/**
+ * find client by response id/type, call clients onresponse handler
+ * return buffer to pool on error
+ *
+ * @param coredev pointer to a coredev object returned by
+ *                smscore_register_device
+ * @param cb pointer to response buffer descriptor
+ *
+ */
+void smscore_onresponse(struct smscore_device_t *coredev,
+                       struct smscore_buffer_t *cb)
+{
+       struct SmsMsgHdr_ST *phdr =
+               (struct SmsMsgHdr_ST *)((u8 *) cb->p + cb->offset);
+       struct smscore_client_t *client =
+               smscore_find_client(coredev, phdr->msgType, phdr->msgDstId);
+       int rc = -EBUSY;
+
+       static unsigned long last_sample_time; /* = 0; */
+       static int data_total; /* = 0; */
+       unsigned long time_now = jiffies_to_msecs(jiffies);
+
+       if (!last_sample_time)
+               last_sample_time = time_now;
+
+       if (time_now - last_sample_time > 10000) {
+               sms_debug("\ndata rate %d bytes/secs",
+                         (int)((data_total * 1000) /
+                               (time_now - last_sample_time)));
+
+               last_sample_time = time_now;
+               data_total = 0;
+       }
+
+       data_total += cb->size;
+       /* If no client registered for type & id,
+        * check for control client where type is not registered */
+       if (client)
+               rc = client->onresponse_handler(client->context, cb);
+
+       if (rc < 0) {
+               switch (phdr->msgType) {
+               case MSG_SMS_GET_VERSION_EX_RES:
+               {
+                       struct SmsVersionRes_ST *ver =
+                               (struct SmsVersionRes_ST *) phdr;
+                       sms_debug("MSG_SMS_GET_VERSION_EX_RES "
+                                 "id %d prots 0x%x ver %d.%d",
+                                 ver->FirmwareId, ver->SupportedProtocols,
+                                 ver->RomVersionMajor, ver->RomVersionMinor);
+
+                       coredev->mode = ver->FirmwareId == 255 ?
+                               DEVICE_MODE_NONE : ver->FirmwareId;
+                       coredev->modes_supported = ver->SupportedProtocols;
+
+                       complete(&coredev->version_ex_done);
+                       break;
+               }
+               case MSG_SMS_INIT_DEVICE_RES:
+                       sms_debug("MSG_SMS_INIT_DEVICE_RES");
+                       complete(&coredev->init_device_done);
+                       break;
+               case MSG_SW_RELOAD_START_RES:
+                       sms_debug("MSG_SW_RELOAD_START_RES");
+                       complete(&coredev->reload_start_done);
+                       break;
+               case MSG_SMS_DATA_DOWNLOAD_RES:
+                       complete(&coredev->data_download_done);
+                       break;
+               case MSG_SW_RELOAD_EXEC_RES:
+                       sms_debug("MSG_SW_RELOAD_EXEC_RES");
+                       break;
+               case MSG_SMS_SWDOWNLOAD_TRIGGER_RES:
+                       sms_debug("MSG_SMS_SWDOWNLOAD_TRIGGER_RES");
+                       complete(&coredev->trigger_done);
+                       break;
+               case MSG_SMS_SLEEP_RESUME_COMP_IND:
+                       complete(&coredev->resume_done);
+                       break;
+               default:
+                       break;
+               }
+               smscore_putbuffer(coredev, cb);
+       }
+}
+
+/**
+ * return pointer to next free buffer descriptor from core pool
+ *
+ * @param coredev pointer to a coredev object returned by
+ *                smscore_register_device
+ *
+ * @return pointer to descriptor on success, NULL on error.
+ */
+struct smscore_buffer_t *smscore_getbuffer(struct smscore_device_t *coredev)
+{
+       struct smscore_buffer_t *cb = NULL;
+       unsigned long flags;
+
+       spin_lock_irqsave(&coredev->bufferslock, flags);
+
+       if (!list_empty(&coredev->buffers)) {
+               cb = (struct smscore_buffer_t *) coredev->buffers.next;
+               list_del(&cb->entry);
+       }
+
+       spin_unlock_irqrestore(&coredev->bufferslock, flags);
+
+       return cb;
+}
+
+/**
+ * return buffer descriptor to a pool
+ *
+ * @param coredev pointer to a coredev object returned by
+ *                smscore_register_device
+ * @param cb pointer buffer descriptor
+ *
+ */
+void smscore_putbuffer(struct smscore_device_t *coredev,
+                      struct smscore_buffer_t *cb)
+{
+       list_add_locked(&cb->entry, &coredev->buffers, &coredev->bufferslock);
+}
+
+static int smscore_validate_client(struct smscore_device_t *coredev,
+                                  struct smscore_client_t *client,
+                                  int data_type, int id)
+{
+       struct smscore_idlist_t *listentry;
+       struct smscore_client_t *registered_client;
+
+       if (!client) {
+               sms_err("bad parameter.");
+               return -EFAULT;
+       }
+       registered_client = smscore_find_client(coredev, data_type, id);
+       if (registered_client == client)
+               return 0;
+
+       if (registered_client) {
+               sms_err("The msg ID already registered to another client.");
+               return -EEXIST;
+       }
+       listentry = kzalloc(sizeof(struct smscore_idlist_t), GFP_KERNEL);
+       if (!listentry) {
+               sms_err("Can't allocate memory for client id.");
+               return -ENOMEM;
+       }
+       listentry->id = id;
+       listentry->data_type = data_type;
+       list_add_locked(&listentry->entry, &client->idlist,
+                       &coredev->clientslock);
+       return 0;
+}
+
+/**
+ * creates smsclient object, check that id is taken by another client
+ *
+ * @param coredev pointer to a coredev object from clients hotplug
+ * @param initial_id all messages with this id would be sent to this client
+ * @param data_type all messages of this type would be sent to this client
+ * @param onresponse_handler client handler that is called to
+ *                           process incoming messages
+ * @param onremove_handler client handler that is called when device is removed
+ * @param context client-specific context
+ * @param client pointer to a value that receives created smsclient object
+ *
+ * @return 0 on success, <0 on error.
+ */
+int smscore_register_client(struct smscore_device_t *coredev,
+                           struct smsclient_params_t *params,
+                           struct smscore_client_t **client)
+{
+       struct smscore_client_t *newclient;
+       /* check that no other channel with same parameters exists */
+       if (smscore_find_client(coredev, params->data_type,
+                               params->initial_id)) {
+               sms_err("Client already exist.");
+               return -EEXIST;
+       }
+
+       newclient = kzalloc(sizeof(struct smscore_client_t), GFP_KERNEL);
+       if (!newclient) {
+               sms_err("Failed to allocate memory for client.");
+               return -ENOMEM;
+       }
+
+       INIT_LIST_HEAD(&newclient->idlist);
+       newclient->coredev = coredev;
+       newclient->onresponse_handler = params->onresponse_handler;
+       newclient->onremove_handler = params->onremove_handler;
+       newclient->context = params->context;
+       list_add_locked(&newclient->entry, &coredev->clients,
+                       &coredev->clientslock);
+       smscore_validate_client(coredev, newclient, params->data_type,
+                               params->initial_id);
+       *client = newclient;
+       sms_debug("%p %d %d", params->context, params->data_type,
+                 params->initial_id);
+
+       return 0;
+}
+
+/**
+ * frees smsclient object and all subclients associated with it
+ *
+ * @param client pointer to smsclient object returned by
+ *               smscore_register_client
+ *
+ */
+void smscore_unregister_client(struct smscore_client_t *client)
+{
+       struct smscore_device_t *coredev = client->coredev;
+       unsigned long flags;
+
+       spin_lock_irqsave(&coredev->clientslock, flags);
+
+
+       while (!list_empty(&client->idlist)) {
+               struct smscore_idlist_t *identry =
+                       (struct smscore_idlist_t *) client->idlist.next;
+               list_del(&identry->entry);
+               kfree(identry);
+       }
+
+       sms_info("%p", client->context);
+
+       list_del(&client->entry);
+       kfree(client);
+
+       spin_unlock_irqrestore(&coredev->clientslock, flags);
+}
+
+/**
+ * verifies that source id is not taken by another client,
+ * calls device handler to send requests to the device
+ *
+ * @param client pointer to smsclient object returned by
+ *               smscore_register_client
+ * @param buffer pointer to a request buffer
+ * @param size size (in bytes) of request buffer
+ *
+ * @return 0 on success, <0 on error.
+ */
+int smsclient_sendrequest(struct smscore_client_t *client,
+                         void *buffer, size_t size)
+{
+       struct smscore_device_t *coredev;
+       struct SmsMsgHdr_ST *phdr = (struct SmsMsgHdr_ST *) buffer;
+       int rc;
+
+       if (client == NULL) {
+               sms_err("Got NULL client");
+               return -EINVAL;
+       }
+
+       coredev = client->coredev;
+
+       /* check that no other channel with same id exists */
+       if (coredev == NULL) {
+               sms_err("Got NULL coredev");
+               return -EINVAL;
+       }
+
+       rc = smscore_validate_client(client->coredev, client, 0,
+                                    phdr->msgSrcId);
+       if (rc < 0)
+               return rc;
+
+       return coredev->sendrequest_handler(coredev->context, buffer, size);
+}
+
+
+int smscore_module_init(void)
+{
+       int rc = 0;
+
+       INIT_LIST_HEAD(&g_smscore_notifyees);
+       INIT_LIST_HEAD(&g_smscore_devices);
+       kmutex_init(&g_smscore_deviceslock);
+
+       INIT_LIST_HEAD(&g_smscore_registry);
+       kmutex_init(&g_smscore_registrylock);
+
+       /* USB Register */
+       rc = smsusb_register();
+
+       /* DVB Register */
+       rc = smsdvb_register();
+
+       sms_debug("rc %d", rc);
+
+       return rc;
+}
+
+void smscore_module_exit(void)
+{
+
+       kmutex_lock(&g_smscore_deviceslock);
+       while (!list_empty(&g_smscore_notifyees)) {
+               struct smscore_device_notifyee_t *notifyee =
+                       (struct smscore_device_notifyee_t *)
+                               g_smscore_notifyees.next;
+
+               list_del(&notifyee->entry);
+               kfree(notifyee);
+       }
+       kmutex_unlock(&g_smscore_deviceslock);
+
+       kmutex_lock(&g_smscore_registrylock);
+       while (!list_empty(&g_smscore_registry)) {
+               struct smscore_registry_entry_t *entry =
+                       (struct smscore_registry_entry_t *)
+                               g_smscore_registry.next;
+
+               list_del(&entry->entry);
+               kfree(entry);
+       }
+       kmutex_unlock(&g_smscore_registrylock);
+
+       /* DVB UnRegister */
+       smsdvb_unregister();
+
+       /* Unregister USB */
+       smsusb_unregister();
+
+       sms_debug("");
+}
+
+module_init(smscore_module_init);
+module_exit(smscore_module_exit);
+
+MODULE_DESCRIPTION("Driver for the Siano SMS1XXX USB dongle");
+MODULE_AUTHOR("Siano Mobile Silicon,,, (doronc@siano-ms.com)");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/siano/smscoreapi.h b/drivers/media/dvb/siano/smscoreapi.h
new file mode 100644 (file)
index 0000000..c1f8f1d
--- /dev/null
@@ -0,0 +1,434 @@
+/*
+ *  Driver for the Siano SMS1xxx USB dongle
+ *
+ *  author: Anatoly Greenblat
+ *
+ *  Copyright (c), 2005-2008 Siano Mobile Silicon, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 3 as
+ *  published by the Free Software Foundation;
+ *
+ *  Software distributed under the License is distributed on an "AS IS"
+ *  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+ *
+ *  See the GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __smscoreapi_h__
+#define __smscoreapi_h__
+
+#include <linux/version.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/mm.h>
+#include <linux/scatterlist.h>
+#include <linux/types.h>
+#include <asm/page.h>
+
+#include "dmxdev.h"
+#include "dvbdev.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+
+#include <linux/mutex.h>
+
+#define kmutex_init(_p_) mutex_init(_p_)
+#define kmutex_lock(_p_) mutex_lock(_p_)
+#define kmutex_trylock(_p_) mutex_trylock(_p_)
+#define kmutex_unlock(_p_) mutex_unlock(_p_)
+
+#ifndef min
+#define min(a, b) (((a) < (b)) ? (a) : (b))
+#endif
+
+#define SMS_ALLOC_ALIGNMENT                                    128
+#define SMS_DMA_ALIGNMENT                                      16
+#define SMS_ALIGN_ADDRESS(addr) \
+       ((((uintptr_t)(addr)) + (SMS_DMA_ALIGNMENT-1)) & ~(SMS_DMA_ALIGNMENT-1))
+
+#define SMS_DEVICE_FAMILY2                                     1
+#define SMS_ROM_NO_RESPONSE                                    2
+#define SMS_DEVICE_NOT_READY                           0x8000000
+
+enum sms_device_type_st {
+       SMS_STELLAR = 0,
+       SMS_NOVA_A0,
+       SMS_NOVA_B0,
+       SMS_VEGA,
+       SMS_NUM_OF_DEVICE_TYPES
+};
+
+struct smscore_device_t;
+struct smscore_client_t;
+struct smscore_buffer_t;
+
+typedef int (*hotplug_t)(struct smscore_device_t *coredev,
+                        struct device *device, int arrival);
+
+typedef int (*setmode_t)(void *context, int mode);
+typedef void (*detectmode_t)(void *context, int *mode);
+typedef int (*sendrequest_t)(void *context, void *buffer, size_t size);
+typedef int (*loadfirmware_t)(void *context, void *buffer, size_t size);
+typedef int (*preload_t)(void *context);
+typedef int (*postload_t)(void *context);
+
+typedef int (*onresponse_t)(void *context, struct smscore_buffer_t *cb);
+typedef void (*onremove_t)(void *context);
+
+struct smscore_buffer_t {
+       /* public members, once passed to clients can be changed freely */
+       struct list_head entry;
+       int                             size;
+       int                             offset;
+
+       /* private members, read-only for clients */
+       void                    *p;
+       dma_addr_t              phys;
+       unsigned long   offset_in_common;
+};
+
+struct smsdevice_params_t {
+       struct device   *device;
+
+       int                             buffer_size;
+       int                             num_buffers;
+
+       char                    devpath[32];
+       unsigned long   flags;
+
+       setmode_t               setmode_handler;
+       detectmode_t    detectmode_handler;
+       sendrequest_t   sendrequest_handler;
+       preload_t               preload_handler;
+       postload_t              postload_handler;
+
+       void                    *context;
+       enum sms_device_type_st device_type;
+};
+
+struct smsclient_params_t {
+       int                             initial_id;
+       int                             data_type;
+       onresponse_t    onresponse_handler;
+       onremove_t              onremove_handler;
+
+       void                    *context;
+};
+
+/* GPIO definitions for antenna frequency domain control (SMS8021) */
+#define SMS_ANTENNA_GPIO_0                                     1
+#define SMS_ANTENNA_GPIO_1                                     0
+
+#define BW_8_MHZ                                                       0
+#define BW_7_MHZ                                                       1
+#define BW_6_MHZ                                                       2
+#define BW_5_MHZ                                                       3
+#define BW_ISDBT_1SEG                                          4
+#define BW_ISDBT_3SEG                                          5
+
+#define MSG_HDR_FLAG_SPLIT_MSG                         4
+
+#define MAX_GPIO_PIN_NUMBER                                    31
+
+#define HIF_TASK                                                       11
+#define SMS_HOST_LIB                                           150
+#define DVBT_BDA_CONTROL_MSG_ID                                201
+
+#define SMS_MAX_PAYLOAD_SIZE                           240
+#define SMS_TUNE_TIMEOUT                                       500
+
+#define MSG_SMS_GPIO_CONFIG_REQ                                507
+#define MSG_SMS_GPIO_CONFIG_RES                                508
+#define MSG_SMS_GPIO_SET_LEVEL_REQ                     509
+#define MSG_SMS_GPIO_SET_LEVEL_RES                     510
+#define MSG_SMS_GPIO_GET_LEVEL_REQ                     511
+#define MSG_SMS_GPIO_GET_LEVEL_RES                     512
+#define MSG_SMS_RF_TUNE_REQ                                    561
+#define MSG_SMS_RF_TUNE_RES                                    562
+#define MSG_SMS_INIT_DEVICE_REQ                                578
+#define MSG_SMS_INIT_DEVICE_RES                                579
+#define MSG_SMS_ADD_PID_FILTER_REQ                     601
+#define MSG_SMS_ADD_PID_FILTER_RES                     602
+#define MSG_SMS_REMOVE_PID_FILTER_REQ          603
+#define MSG_SMS_REMOVE_PID_FILTER_RES          604
+#define MSG_SMS_DAB_CHANNEL                                    607
+#define MSG_SMS_GET_PID_FILTER_LIST_REQ                608
+#define MSG_SMS_GET_PID_FILTER_LIST_RES                609
+#define MSG_SMS_GET_STATISTICS_REQ                     615
+#define MSG_SMS_GET_STATISTICS_RES                     616
+#define MSG_SMS_SET_ANTENNA_CONFIG_REQ         651
+#define MSG_SMS_SET_ANTENNA_CONFIG_RES         652
+#define MSG_SMS_GET_STATISTICS_EX_REQ          653
+#define MSG_SMS_GET_STATISTICS_EX_RES          654
+#define MSG_SMS_SLEEP_RESUME_COMP_IND          655
+#define MSG_SMS_DATA_DOWNLOAD_REQ                      660
+#define MSG_SMS_DATA_DOWNLOAD_RES                      661
+#define MSG_SMS_SWDOWNLOAD_TRIGGER_REQ         664
+#define MSG_SMS_SWDOWNLOAD_TRIGGER_RES         665
+#define MSG_SMS_SWDOWNLOAD_BACKDOOR_REQ                666
+#define MSG_SMS_SWDOWNLOAD_BACKDOOR_RES                667
+#define MSG_SMS_GET_VERSION_EX_REQ                     668
+#define MSG_SMS_GET_VERSION_EX_RES                     669
+#define MSG_SMS_SET_CLOCK_OUTPUT_REQ           670
+#define MSG_SMS_I2C_SET_FREQ_REQ                       685
+#define MSG_SMS_GENERIC_I2C_REQ                                687
+#define MSG_SMS_GENERIC_I2C_RES                                688
+#define MSG_SMS_DVBT_BDA_DATA                          693
+#define MSG_SW_RELOAD_REQ                                      697
+#define MSG_SMS_DATA_MSG                                       699
+#define MSG_SW_RELOAD_START_REQ                                702
+#define MSG_SW_RELOAD_START_RES                                703
+#define MSG_SW_RELOAD_EXEC_REQ                         704
+#define MSG_SW_RELOAD_EXEC_RES                         705
+#define MSG_SMS_SPI_INT_LINE_SET_REQ           710
+#define MSG_SMS_ISDBT_TUNE_REQ                         776
+#define MSG_SMS_ISDBT_TUNE_RES                         777
+
+#define SMS_INIT_MSG_EX(ptr, type, src, dst, len) do { \
+       (ptr)->msgType = type; (ptr)->msgSrcId = src; (ptr)->msgDstId = dst; \
+       (ptr)->msgLength = len; (ptr)->msgFlags = 0; \
+} while (0)
+#define SMS_INIT_MSG(ptr, type, len) \
+       SMS_INIT_MSG_EX(ptr, type, 0, HIF_TASK, len)
+
+enum SMS_DEVICE_MODE {
+       DEVICE_MODE_NONE = -1,
+       DEVICE_MODE_DVBT = 0,
+       DEVICE_MODE_DVBH,
+       DEVICE_MODE_DAB_TDMB,
+       DEVICE_MODE_DAB_TDMB_DABIP,
+       DEVICE_MODE_DVBT_BDA,
+       DEVICE_MODE_ISDBT,
+       DEVICE_MODE_ISDBT_BDA,
+       DEVICE_MODE_CMMB,
+       DEVICE_MODE_RAW_TUNER,
+       DEVICE_MODE_MAX,
+};
+
+struct SmsMsgHdr_ST {
+       u16     msgType;
+       u8      msgSrcId;
+       u8      msgDstId;
+       u16     msgLength; /* Length of entire message, including header */
+       u16     msgFlags;
+};
+
+struct SmsMsgData_ST {
+       struct SmsMsgHdr_ST     xMsgHeader;
+       u32                     msgData[1];
+};
+
+struct SmsDataDownload_ST {
+       struct SmsMsgHdr_ST     xMsgHeader;
+       u32                     MemAddr;
+       u8                      Payload[SMS_MAX_PAYLOAD_SIZE];
+};
+
+struct SmsVersionRes_ST {
+       struct SmsMsgHdr_ST     xMsgHeader;
+
+       u16             ChipModel; /* e.g. 0x1102 for SMS-1102 "Nova" */
+       u8              Step; /* 0 - Step A */
+       u8              MetalFix; /* 0 - Metal 0 */
+
+       u8              FirmwareId; /* 0xFF ï¿½ ROM, otherwise the
+                                    * value indicated by
+                                    * SMSHOSTLIB_DEVICE_MODES_E */
+       u8              SupportedProtocols; /* Bitwise OR combination of
+                                            * supported protocols */
+
+       u8              VersionMajor;
+       u8              VersionMinor;
+       u8              VersionPatch;
+       u8              VersionFieldPatch;
+
+       u8              RomVersionMajor;
+       u8              RomVersionMinor;
+       u8              RomVersionPatch;
+       u8              RomVersionFieldPatch;
+
+       u8              TextLabel[34];
+};
+
+struct SmsFirmware_ST {
+       u32                     CheckSum;
+       u32                     Length;
+       u32                     StartAddress;
+       u8                      Payload[1];
+};
+
+struct SMSHOSTLIB_STATISTICS_ST {
+       u32 Reserved; /* Reserved */
+
+       /* Common parameters */
+       u32 IsRfLocked; /* 0 - not locked, 1 - locked */
+       u32 IsDemodLocked; /* 0 - not locked, 1 - locked */
+       u32 IsExternalLNAOn; /* 0 - external LNA off, 1 - external LNA on */
+
+       /* Reception quality */
+       s32  SNR; /* dB */
+       u32 BER; /* Post Viterbi BER [1E-5] */
+       u32 FIB_CRC;    /* CRC errors percentage, valid only for DAB */
+       u32 TS_PER; /* Transport stream PER, 0xFFFFFFFF indicate N/A,
+                    * valid only for DVB-T/H */
+       u32 MFER; /* DVB-H frame error rate in percentage,
+                  * 0xFFFFFFFF indicate N/A, valid only for DVB-H */
+       s32  RSSI; /* dBm */
+       s32  InBandPwr; /* In band power in dBM */
+       s32  CarrierOffset; /* Carrier Offset in bin/1024 */
+
+       /* Transmission parameters, valid only for DVB-T/H */
+       u32 Frequency; /* Frequency in Hz */
+       u32 Bandwidth; /* Bandwidth in MHz */
+       u32 TransmissionMode; /* Transmission Mode, for DAB modes 1-4,
+                              * for DVB-T/H FFT mode carriers in Kilos */
+       u32 ModemState; /* from SMS_DvbModemState_ET */
+       u32 GuardInterval; /* Guard Interval, 1 divided by value */
+       u32 CodeRate; /* Code Rate from SMS_DvbModemState_ET */
+       u32 LPCodeRate; /* Low Priority Code Rate from SMS_DvbModemState_ET */
+       u32 Hierarchy; /* Hierarchy from SMS_Hierarchy_ET */
+       u32 Constellation; /* Constellation from SMS_Constellation_ET */
+
+       /* Burst parameters, valid only for DVB-H */
+       u32 BurstSize; /* Current burst size in bytes */
+       u32 BurstDuration; /* Current burst duration in mSec */
+       u32 BurstCycleTime; /* Current burst cycle time in mSec */
+       u32 CalculatedBurstCycleTime; /* Current burst cycle time in mSec,
+                                      * as calculated by demodulator */
+       u32 NumOfRows; /* Number of rows in MPE table */
+       u32 NumOfPaddCols; /* Number of padding columns in MPE table */
+       u32 NumOfPunctCols; /* Number of puncturing columns in MPE table */
+       /* Burst parameters */
+       u32 ErrorTSPackets; /* Number of erroneous transport-stream packets */
+       u32 TotalTSPackets; /* Total number of transport-stream packets */
+       u32 NumOfValidMpeTlbs; /* Number of MPE tables which do not include
+                               * errors after MPE RS decoding */
+       u32 NumOfInvalidMpeTlbs; /* Number of MPE tables which include errors
+                                 * after MPE RS decoding */
+       u32 NumOfCorrectedMpeTlbs; /* Number of MPE tables which were corrected
+                                   * by MPE RS decoding */
+
+       /* Common params */
+       u32 BERErrorCount; /* Number of errornous SYNC bits. */
+       u32 BERBitCount; /* Total number of SYNC bits. */
+
+       /* Interface information */
+       u32 SmsToHostTxErrors; /* Total number of transmission errors. */
+
+       /* DAB/T-DMB */
+       u32 PreBER; /* DAB/T-DMB only: Pre Viterbi BER [1E-5] */
+
+       /* DVB-H TPS parameters */
+       u32 CellId; /* TPS Cell ID in bits 15..0, bits 31..16 zero;
+                    * if set to 0xFFFFFFFF cell_id not yet recovered */
+
+};
+
+struct SmsMsgStatisticsInfo_ST {
+       u32 RequestResult;
+
+       struct SMSHOSTLIB_STATISTICS_ST Stat;
+
+       /* Split the calc of the SNR in DAB */
+       u32 Signal; /* dB */
+       u32 Noise; /* dB */
+
+};
+
+
+struct smsdvb_client_t {
+       struct list_head entry;
+
+       struct smscore_device_t *coredev;
+       struct smscore_client_t *smsclient;
+
+       struct dvb_adapter      adapter;
+       struct dvb_demux        demux;
+       struct dmxdev           dmxdev;
+       struct dvb_frontend     frontend;
+
+       fe_status_t             fe_status;
+       int                     fe_ber, fe_snr, fe_signal_strength;
+
+       struct completion       tune_done, stat_done;
+
+       /* todo: save freq/band instead whole struct */
+       struct dvb_frontend_parameters fe_params;
+
+};
+
+extern void smscore_registry_setmode(char *devpath, int mode);
+extern int smscore_registry_getmode(char *devpath);
+
+extern int smscore_register_hotplug(hotplug_t hotplug);
+extern void smscore_unregister_hotplug(hotplug_t hotplug);
+
+extern int smscore_register_device(struct smsdevice_params_t *params,
+                                  struct smscore_device_t **coredev);
+extern void smscore_unregister_device(struct smscore_device_t *coredev);
+
+extern int smscore_start_device(struct smscore_device_t *coredev);
+extern int smscore_load_firmware(struct smscore_device_t *coredev,
+                                char *filename,
+                                loadfirmware_t loadfirmware_handler);
+
+extern int smscore_set_device_mode(struct smscore_device_t *coredev, int mode);
+extern int smscore_get_device_mode(struct smscore_device_t *coredev);
+
+extern int smscore_register_client(struct smscore_device_t *coredev,
+                                   struct smsclient_params_t *params,
+                                   struct smscore_client_t **client);
+extern void smscore_unregister_client(struct smscore_client_t *client);
+
+extern int smsclient_sendrequest(struct smscore_client_t *client,
+                                void *buffer, size_t size);
+extern void smscore_onresponse(struct smscore_device_t *coredev,
+                              struct smscore_buffer_t *cb);
+
+
+extern
+struct smscore_buffer_t *smscore_getbuffer(struct smscore_device_t *coredev);
+extern void smscore_putbuffer(struct smscore_device_t *coredev,
+                             struct smscore_buffer_t *cb);
+
+void smscore_set_board_id(struct smscore_device_t *core, int id);
+int smscore_get_board_id(struct smscore_device_t *core);
+
+/* smsdvb.c */
+int smsdvb_register(void);
+void smsdvb_unregister(void);
+
+/* smsusb.c */
+int smsusb_register(void);
+void smsusb_unregister(void);
+
+/* ------------------------------------------------------------------------ */
+
+extern int sms_debug;
+
+#define DBG_INFO 1
+#define DBG_ADV  2
+
+#define sms_printk(kern, fmt, arg...) \
+       printk(kern "%s: " fmt "\n", __func__, ##arg)
+
+#define dprintk(kern, lvl, fmt, arg...) do {\
+       if (sms_debug & lvl) \
+               sms_printk(kern, fmt, ##arg); } while (0)
+
+#define sms_log(fmt, arg...) sms_printk(KERN_INFO, fmt, ##arg)
+#define sms_err(fmt, arg...) \
+       sms_printk(KERN_ERR, "line: %d: " fmt, __LINE__, ##arg)
+#define sms_warn(fmt, arg...)  sms_printk(KERN_WARNING, fmt, ##arg)
+#define sms_info(fmt, arg...) \
+       dprintk(KERN_INFO, DBG_INFO, fmt, ##arg)
+#define sms_debug(fmt, arg...) \
+       dprintk(KERN_DEBUG, DBG_ADV, fmt, ##arg)
+
+
+#endif /* __smscoreapi_h__ */
diff --git a/drivers/media/dvb/siano/smsdvb.c b/drivers/media/dvb/siano/smsdvb.c
new file mode 100644 (file)
index 0000000..6f9c185
--- /dev/null
@@ -0,0 +1,449 @@
+/*
+ *  Driver for the Siano SMS1xxx USB dongle
+ *
+ *  author: Anatoly Greenblat
+ *
+ *  Copyright (c), 2005-2008 Siano Mobile Silicon, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 3 as
+ *  published by the Free Software Foundation;
+ *
+ *  Software distributed under the License is distributed on an "AS IS"
+ *  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+ *
+ *  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/module.h>
+#include <linux/init.h>
+
+#include "smscoreapi.h"
+#include "sms-cards.h"
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+struct list_head g_smsdvb_clients;
+struct mutex g_smsdvb_clientslock;
+
+static int smsdvb_onresponse(void *context, struct smscore_buffer_t *cb)
+{
+       struct smsdvb_client_t *client = (struct smsdvb_client_t *) context;
+       struct SmsMsgHdr_ST *phdr =
+               (struct SmsMsgHdr_ST *)(((u8 *) cb->p) + cb->offset);
+
+       switch (phdr->msgType) {
+       case MSG_SMS_DVBT_BDA_DATA:
+               dvb_dmx_swfilter(&client->demux, (u8 *)(phdr + 1),
+                                cb->size - sizeof(struct SmsMsgHdr_ST));
+               break;
+
+       case MSG_SMS_RF_TUNE_RES:
+               complete(&client->tune_done);
+               break;
+
+       case MSG_SMS_GET_STATISTICS_RES:
+       {
+               struct SmsMsgStatisticsInfo_ST *p =
+                       (struct SmsMsgStatisticsInfo_ST *)(phdr + 1);
+
+               if (p->Stat.IsDemodLocked) {
+                       client->fe_status = FE_HAS_SIGNAL |
+                                           FE_HAS_CARRIER |
+                                           FE_HAS_VITERBI |
+                                           FE_HAS_SYNC |
+                                           FE_HAS_LOCK;
+
+                       client->fe_snr = p->Stat.SNR;
+                       client->fe_ber = p->Stat.BER;
+
+                       if (p->Stat.InBandPwr < -95)
+                               client->fe_signal_strength = 0;
+                       else if (p->Stat.InBandPwr > -29)
+                               client->fe_signal_strength = 100;
+                       else
+                               client->fe_signal_strength =
+                                       (p->Stat.InBandPwr + 95) * 3 / 2;
+               } else {
+                       client->fe_status = 0;
+                       client->fe_snr =
+                       client->fe_ber =
+                       client->fe_signal_strength = 0;
+               }
+
+               complete(&client->stat_done);
+               break;
+       } }
+
+       smscore_putbuffer(client->coredev, cb);
+
+       return 0;
+}
+
+static void smsdvb_unregister_client(struct smsdvb_client_t *client)
+{
+       /* must be called under clientslock */
+
+       list_del(&client->entry);
+
+       smscore_unregister_client(client->smsclient);
+       dvb_unregister_frontend(&client->frontend);
+       dvb_dmxdev_release(&client->dmxdev);
+       dvb_dmx_release(&client->demux);
+       dvb_unregister_adapter(&client->adapter);
+       kfree(client);
+}
+
+static void smsdvb_onremove(void *context)
+{
+       kmutex_lock(&g_smsdvb_clientslock);
+
+       smsdvb_unregister_client((struct smsdvb_client_t *) context);
+
+       kmutex_unlock(&g_smsdvb_clientslock);
+}
+
+static int smsdvb_start_feed(struct dvb_demux_feed *feed)
+{
+       struct smsdvb_client_t *client =
+               container_of(feed->demux, struct smsdvb_client_t, demux);
+       struct SmsMsgData_ST PidMsg;
+
+       sms_debug("add pid %d(%x)",
+                 feed->pid, feed->pid);
+
+       PidMsg.xMsgHeader.msgSrcId = DVBT_BDA_CONTROL_MSG_ID;
+       PidMsg.xMsgHeader.msgDstId = HIF_TASK;
+       PidMsg.xMsgHeader.msgFlags = 0;
+       PidMsg.xMsgHeader.msgType  = MSG_SMS_ADD_PID_FILTER_REQ;
+       PidMsg.xMsgHeader.msgLength = sizeof(PidMsg);
+       PidMsg.msgData[0] = feed->pid;
+
+       return smsclient_sendrequest(client->smsclient,
+                                    &PidMsg, sizeof(PidMsg));
+}
+
+static int smsdvb_stop_feed(struct dvb_demux_feed *feed)
+{
+       struct smsdvb_client_t *client =
+               container_of(feed->demux, struct smsdvb_client_t, demux);
+       struct SmsMsgData_ST PidMsg;
+
+       sms_debug("remove pid %d(%x)",
+                 feed->pid, feed->pid);
+
+       PidMsg.xMsgHeader.msgSrcId = DVBT_BDA_CONTROL_MSG_ID;
+       PidMsg.xMsgHeader.msgDstId = HIF_TASK;
+       PidMsg.xMsgHeader.msgFlags = 0;
+       PidMsg.xMsgHeader.msgType  = MSG_SMS_REMOVE_PID_FILTER_REQ;
+       PidMsg.xMsgHeader.msgLength = sizeof(PidMsg);
+       PidMsg.msgData[0] = feed->pid;
+
+       return smsclient_sendrequest(client->smsclient,
+                                    &PidMsg, sizeof(PidMsg));
+}
+
+static int smsdvb_sendrequest_and_wait(struct smsdvb_client_t *client,
+                                       void *buffer, size_t size,
+                                       struct completion *completion)
+{
+       int rc = smsclient_sendrequest(client->smsclient, buffer, size);
+       if (rc < 0)
+               return rc;
+
+       return wait_for_completion_timeout(completion,
+                                          msecs_to_jiffies(2000)) ?
+                                               0 : -ETIME;
+}
+
+static int smsdvb_send_statistics_request(struct smsdvb_client_t *client)
+{
+       struct SmsMsgHdr_ST Msg = { MSG_SMS_GET_STATISTICS_REQ,
+                            DVBT_BDA_CONTROL_MSG_ID,
+                            HIF_TASK, sizeof(struct SmsMsgHdr_ST), 0 };
+       return smsdvb_sendrequest_and_wait(client, &Msg, sizeof(Msg),
+                                          &client->stat_done);
+}
+
+static int smsdvb_read_status(struct dvb_frontend *fe, fe_status_t *stat)
+{
+       struct smsdvb_client_t *client =
+               container_of(fe, struct smsdvb_client_t, frontend);
+       int rc = smsdvb_send_statistics_request(client);
+
+       if (!rc)
+               *stat = client->fe_status;
+
+       return rc;
+}
+
+static int smsdvb_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+       struct smsdvb_client_t *client =
+               container_of(fe, struct smsdvb_client_t, frontend);
+       int rc = smsdvb_send_statistics_request(client);
+
+       if (!rc)
+               *ber = client->fe_ber;
+
+       return rc;
+}
+
+static int smsdvb_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+{
+       struct smsdvb_client_t *client =
+               container_of(fe, struct smsdvb_client_t, frontend);
+       int rc = smsdvb_send_statistics_request(client);
+
+       if (!rc)
+               *strength = client->fe_signal_strength;
+
+       return rc;
+}
+
+static int smsdvb_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+       struct smsdvb_client_t *client =
+               container_of(fe, struct smsdvb_client_t, frontend);
+       int rc = smsdvb_send_statistics_request(client);
+
+       if (!rc)
+               *snr = client->fe_snr;
+
+       return rc;
+}
+
+static int smsdvb_get_tune_settings(struct dvb_frontend *fe,
+                                   struct dvb_frontend_tune_settings *tune)
+{
+       sms_debug("");
+
+       tune->min_delay_ms = 400;
+       tune->step_size = 250000;
+       tune->max_drift = 0;
+       return 0;
+}
+
+static int smsdvb_set_frontend(struct dvb_frontend *fe,
+                              struct dvb_frontend_parameters *fep)
+{
+       struct smsdvb_client_t *client =
+               container_of(fe, struct smsdvb_client_t, frontend);
+
+       struct {
+               struct SmsMsgHdr_ST     Msg;
+               u32             Data[3];
+       } Msg;
+
+       Msg.Msg.msgSrcId  = DVBT_BDA_CONTROL_MSG_ID;
+       Msg.Msg.msgDstId  = HIF_TASK;
+       Msg.Msg.msgFlags  = 0;
+       Msg.Msg.msgType   = MSG_SMS_RF_TUNE_REQ;
+       Msg.Msg.msgLength = sizeof(Msg);
+       Msg.Data[0] = fep->frequency;
+       Msg.Data[2] = 12000000;
+
+       sms_debug("freq %d band %d",
+                 fep->frequency, fep->u.ofdm.bandwidth);
+
+       switch (fep->u.ofdm.bandwidth) {
+       case BANDWIDTH_8_MHZ: Msg.Data[1] = BW_8_MHZ; break;
+       case BANDWIDTH_7_MHZ: Msg.Data[1] = BW_7_MHZ; break;
+       case BANDWIDTH_6_MHZ: Msg.Data[1] = BW_6_MHZ; break;
+       case BANDWIDTH_AUTO: return -EOPNOTSUPP;
+       default: return -EINVAL;
+       }
+
+       return smsdvb_sendrequest_and_wait(client, &Msg, sizeof(Msg),
+                                          &client->tune_done);
+}
+
+static int smsdvb_get_frontend(struct dvb_frontend *fe,
+                              struct dvb_frontend_parameters *fep)
+{
+       struct smsdvb_client_t *client =
+               container_of(fe, struct smsdvb_client_t, frontend);
+
+       sms_debug("");
+
+       /* todo: */
+       memcpy(fep, &client->fe_params,
+              sizeof(struct dvb_frontend_parameters));
+       return 0;
+}
+
+static void smsdvb_release(struct dvb_frontend *fe)
+{
+       /* do nothing */
+}
+
+static struct dvb_frontend_ops smsdvb_fe_ops = {
+       .info = {
+               .name                   = "Siano Mobile Digital SMS1xxx",
+               .type                   = FE_OFDM,
+               .frequency_min          = 44250000,
+               .frequency_max          = 867250000,
+               .frequency_stepsize     = 250000,
+               .caps = FE_CAN_INVERSION_AUTO |
+                       FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+                       FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+                       FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 |
+                       FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO |
+                       FE_CAN_GUARD_INTERVAL_AUTO |
+                       FE_CAN_RECOVER |
+                       FE_CAN_HIERARCHY_AUTO,
+       },
+
+       .release = smsdvb_release,
+
+       .set_frontend = smsdvb_set_frontend,
+       .get_frontend = smsdvb_get_frontend,
+       .get_tune_settings = smsdvb_get_tune_settings,
+
+       .read_status = smsdvb_read_status,
+       .read_ber = smsdvb_read_ber,
+       .read_signal_strength = smsdvb_read_signal_strength,
+       .read_snr = smsdvb_read_snr,
+};
+
+static int smsdvb_hotplug(struct smscore_device_t *coredev,
+                         struct device *device, int arrival)
+{
+       struct smsclient_params_t params;
+       struct smsdvb_client_t *client;
+       int rc;
+
+       /* device removal handled by onremove callback */
+       if (!arrival)
+               return 0;
+
+       if (smscore_get_device_mode(coredev) != 4) {
+               sms_err("SMS Device mode is not set for "
+                       "DVB operation.");
+               return 0;
+       }
+
+       client = kzalloc(sizeof(struct smsdvb_client_t), GFP_KERNEL);
+       if (!client) {
+               sms_err("kmalloc() failed");
+               return -ENOMEM;
+       }
+
+       /* register dvb adapter */
+       rc = dvb_register_adapter(&client->adapter,
+                                 sms_get_board(
+                                       smscore_get_board_id(coredev))->name,
+                                 THIS_MODULE, device, adapter_nr);
+       if (rc < 0) {
+               sms_err("dvb_register_adapter() failed %d", rc);
+               goto adapter_error;
+       }
+
+       /* init dvb demux */
+       client->demux.dmx.capabilities = DMX_TS_FILTERING;
+       client->demux.filternum = 32; /* todo: nova ??? */
+       client->demux.feednum = 32;
+       client->demux.start_feed = smsdvb_start_feed;
+       client->demux.stop_feed = smsdvb_stop_feed;
+
+       rc = dvb_dmx_init(&client->demux);
+       if (rc < 0) {
+               sms_err("dvb_dmx_init failed %d", rc);
+               goto dvbdmx_error;
+       }
+
+       /* init dmxdev */
+       client->dmxdev.filternum = 32;
+       client->dmxdev.demux = &client->demux.dmx;
+       client->dmxdev.capabilities = 0;
+
+       rc = dvb_dmxdev_init(&client->dmxdev, &client->adapter);
+       if (rc < 0) {
+               sms_err("dvb_dmxdev_init failed %d", rc);
+               goto dmxdev_error;
+       }
+
+       /* init and register frontend */
+       memcpy(&client->frontend.ops, &smsdvb_fe_ops,
+              sizeof(struct dvb_frontend_ops));
+
+       rc = dvb_register_frontend(&client->adapter, &client->frontend);
+       if (rc < 0) {
+               sms_err("frontend registration failed %d", rc);
+               goto frontend_error;
+       }
+
+       params.initial_id = 1;
+       params.data_type = MSG_SMS_DVBT_BDA_DATA;
+       params.onresponse_handler = smsdvb_onresponse;
+       params.onremove_handler = smsdvb_onremove;
+       params.context = client;
+
+       rc = smscore_register_client(coredev, &params, &client->smsclient);
+       if (rc < 0) {
+               sms_err("smscore_register_client() failed %d", rc);
+               goto client_error;
+       }
+
+       client->coredev = coredev;
+
+       init_completion(&client->tune_done);
+       init_completion(&client->stat_done);
+
+       kmutex_lock(&g_smsdvb_clientslock);
+
+       list_add(&client->entry, &g_smsdvb_clients);
+
+       kmutex_unlock(&g_smsdvb_clientslock);
+
+       sms_info("success");
+
+       return 0;
+
+client_error:
+       dvb_unregister_frontend(&client->frontend);
+
+frontend_error:
+       dvb_dmxdev_release(&client->dmxdev);
+
+dmxdev_error:
+       dvb_dmx_release(&client->demux);
+
+dvbdmx_error:
+       dvb_unregister_adapter(&client->adapter);
+
+adapter_error:
+       kfree(client);
+       return rc;
+}
+
+int smsdvb_register(void)
+{
+       int rc;
+
+       INIT_LIST_HEAD(&g_smsdvb_clients);
+       kmutex_init(&g_smsdvb_clientslock);
+
+       rc = smscore_register_hotplug(smsdvb_hotplug);
+
+       sms_debug("");
+
+       return rc;
+}
+
+void smsdvb_unregister(void)
+{
+       smscore_unregister_hotplug(smsdvb_hotplug);
+
+       kmutex_lock(&g_smsdvb_clientslock);
+
+       while (!list_empty(&g_smsdvb_clients))
+              smsdvb_unregister_client(
+                       (struct smsdvb_client_t *) g_smsdvb_clients.next);
+
+       kmutex_unlock(&g_smsdvb_clientslock);
+}
diff --git a/drivers/media/dvb/siano/smsusb.c b/drivers/media/dvb/siano/smsusb.c
new file mode 100644 (file)
index 0000000..c10b184
--- /dev/null
@@ -0,0 +1,459 @@
+/*
+ *  Driver for the Siano SMS1xxx USB dongle
+ *
+ *  author: Anatoly Greenblat
+ *
+ *  Copyright (c), 2005-2008 Siano Mobile Silicon, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 3 as
+ *  published by the Free Software Foundation;
+ *
+ *  Software distributed under the License is distributed on an "AS IS"
+ *  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+ *
+ *  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/kernel.h>
+#include <linux/init.h>
+#include <linux/usb.h>
+#include <linux/firmware.h>
+
+#include "smscoreapi.h"
+#include "sms-cards.h"
+
+#define USB1_BUFFER_SIZE               0x1000
+#define USB2_BUFFER_SIZE               0x4000
+
+#define MAX_BUFFERS            50
+#define MAX_URBS               10
+
+struct smsusb_device_t;
+
+struct smsusb_urb_t {
+       struct smscore_buffer_t *cb;
+       struct smsusb_device_t  *dev;
+
+       struct urb urb;
+};
+
+struct smsusb_device_t {
+       struct usb_device *udev;
+       struct smscore_device_t *coredev;
+
+       struct smsusb_urb_t     surbs[MAX_URBS];
+
+       int             response_alignment;
+       int             buffer_size;
+};
+
+static int smsusb_submit_urb(struct smsusb_device_t *dev,
+                            struct smsusb_urb_t *surb);
+
+static void smsusb_onresponse(struct urb *urb)
+{
+       struct smsusb_urb_t *surb = (struct smsusb_urb_t *) urb->context;
+       struct smsusb_device_t *dev = surb->dev;
+
+       if (urb->status < 0) {
+               sms_err("error, urb status %d, %d bytes",
+                       urb->status, urb->actual_length);
+               return;
+       }
+
+       if (urb->actual_length > 0) {
+               struct SmsMsgHdr_ST *phdr = (struct SmsMsgHdr_ST *) surb->cb->p;
+
+               if (urb->actual_length >= phdr->msgLength) {
+                       surb->cb->size = phdr->msgLength;
+
+                       if (dev->response_alignment &&
+                           (phdr->msgFlags & MSG_HDR_FLAG_SPLIT_MSG)) {
+
+                               surb->cb->offset =
+                                       dev->response_alignment +
+                                       ((phdr->msgFlags >> 8) & 3);
+
+                               /* sanity check */
+                               if (((int) phdr->msgLength +
+                                    surb->cb->offset) > urb->actual_length) {
+                                       sms_err("invalid response "
+                                               "msglen %d offset %d "
+                                               "size %d",
+                                               phdr->msgLength,
+                                               surb->cb->offset,
+                                               urb->actual_length);
+                                       goto exit_and_resubmit;
+                               }
+
+                               /* move buffer pointer and
+                                * copy header to its new location */
+                               memcpy((char *) phdr + surb->cb->offset,
+                                      phdr, sizeof(struct SmsMsgHdr_ST));
+                       } else
+                               surb->cb->offset = 0;
+
+                       smscore_onresponse(dev->coredev, surb->cb);
+                       surb->cb = NULL;
+               } else {
+                       sms_err("invalid response "
+                               "msglen %d actual %d",
+                               phdr->msgLength, urb->actual_length);
+               }
+       }
+
+exit_and_resubmit:
+       smsusb_submit_urb(dev, surb);
+}
+
+static int smsusb_submit_urb(struct smsusb_device_t *dev,
+                            struct smsusb_urb_t *surb)
+{
+       if (!surb->cb) {
+               surb->cb = smscore_getbuffer(dev->coredev);
+               if (!surb->cb) {
+                       sms_err("smscore_getbuffer(...) returned NULL");
+                       return -ENOMEM;
+               }
+       }
+
+       usb_fill_bulk_urb(
+               &surb->urb,
+               dev->udev,
+               usb_rcvbulkpipe(dev->udev, 0x81),
+               surb->cb->p,
+               dev->buffer_size,
+               smsusb_onresponse,
+               surb
+       );
+       surb->urb.transfer_dma = surb->cb->phys;
+       surb->urb.transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+       return usb_submit_urb(&surb->urb, GFP_ATOMIC);
+}
+
+static void smsusb_stop_streaming(struct smsusb_device_t *dev)
+{
+       int i;
+
+       for (i = 0; i < MAX_URBS; i++) {
+               usb_kill_urb(&dev->surbs[i].urb);
+
+               if (dev->surbs[i].cb) {
+                       smscore_putbuffer(dev->coredev, dev->surbs[i].cb);
+                       dev->surbs[i].cb = NULL;
+               }
+       }
+}
+
+static int smsusb_start_streaming(struct smsusb_device_t *dev)
+{
+       int i, rc;
+
+       for (i = 0; i < MAX_URBS; i++) {
+               rc = smsusb_submit_urb(dev, &dev->surbs[i]);
+               if (rc < 0) {
+                       sms_err("smsusb_submit_urb(...) failed");
+                       smsusb_stop_streaming(dev);
+                       break;
+               }
+       }
+
+       return rc;
+}
+
+static int smsusb_sendrequest(void *context, void *buffer, size_t size)
+{
+       struct smsusb_device_t *dev = (struct smsusb_device_t *) context;
+       int dummy;
+
+       return usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, 2),
+                           buffer, size, &dummy, 1000);
+}
+
+static char *smsusb1_fw_lkup[] = {
+       "dvbt_stellar_usb.inp",
+       "dvbh_stellar_usb.inp",
+       "tdmb_stellar_usb.inp",
+       "none",
+       "dvbt_bda_stellar_usb.inp",
+};
+
+static inline char *sms_get_fw_name(int mode, int board_id)
+{
+       char **fw = sms_get_board(board_id)->fw;
+       return (fw && fw[mode]) ? fw[mode] : smsusb1_fw_lkup[mode];
+}
+
+static int smsusb1_load_firmware(struct usb_device *udev, int id, int board_id)
+{
+       const struct firmware *fw;
+       u8 *fw_buffer;
+       int rc, dummy;
+       char *fw_filename;
+
+       if (id < DEVICE_MODE_DVBT || id > DEVICE_MODE_DVBT_BDA) {
+               sms_err("invalid firmware id specified %d", id);
+               return -EINVAL;
+       }
+
+       fw_filename = sms_get_fw_name(id, board_id);
+
+       rc = request_firmware(&fw, fw_filename, &udev->dev);
+       if (rc < 0) {
+               sms_warn("failed to open \"%s\" mode %d, "
+                        "trying again with default firmware", fw_filename, id);
+
+               fw_filename = smsusb1_fw_lkup[id];
+               rc = request_firmware(&fw, fw_filename, &udev->dev);
+               if (rc < 0) {
+                       sms_warn("failed to open \"%s\" mode %d",
+                                fw_filename, id);
+
+                       return rc;
+               }
+       }
+
+       fw_buffer = kmalloc(fw->size, GFP_KERNEL);
+       if (fw_buffer) {
+               memcpy(fw_buffer, fw->data, fw->size);
+
+               rc = usb_bulk_msg(udev, usb_sndbulkpipe(udev, 2),
+                                 fw_buffer, fw->size, &dummy, 1000);
+
+               sms_info("sent %zd(%d) bytes, rc %d", fw->size, dummy, rc);
+
+               kfree(fw_buffer);
+       } else {
+               sms_err("failed to allocate firmware buffer");
+               rc = -ENOMEM;
+       }
+       sms_info("read FW %s, size=%zd", fw_filename, fw->size);
+
+       release_firmware(fw);
+
+       return rc;
+}
+
+static void smsusb1_detectmode(void *context, int *mode)
+{
+       char *product_string =
+               ((struct smsusb_device_t *) context)->udev->product;
+
+       *mode = DEVICE_MODE_NONE;
+
+       if (!product_string) {
+               product_string = "none";
+               sms_err("product string not found");
+       } else if (strstr(product_string, "DVBH"))
+               *mode = 1;
+       else if (strstr(product_string, "BDA"))
+               *mode = 4;
+       else if (strstr(product_string, "DVBT"))
+               *mode = 0;
+       else if (strstr(product_string, "TDMB"))
+               *mode = 2;
+
+       sms_info("%d \"%s\"", *mode, product_string);
+}
+
+static int smsusb1_setmode(void *context, int mode)
+{
+       struct SmsMsgHdr_ST Msg = { MSG_SW_RELOAD_REQ, 0, HIF_TASK,
+                            sizeof(struct SmsMsgHdr_ST), 0 };
+
+       if (mode < DEVICE_MODE_DVBT || mode > DEVICE_MODE_DVBT_BDA) {
+               sms_err("invalid firmware id specified %d", mode);
+               return -EINVAL;
+       }
+
+       return smsusb_sendrequest(context, &Msg, sizeof(Msg));
+}
+
+static void smsusb_term_device(struct usb_interface *intf)
+{
+       struct smsusb_device_t *dev =
+               (struct smsusb_device_t *) usb_get_intfdata(intf);
+
+       if (dev) {
+               smsusb_stop_streaming(dev);
+
+               /* unregister from smscore */
+               if (dev->coredev)
+                       smscore_unregister_device(dev->coredev);
+
+               kfree(dev);
+
+               sms_info("device %p destroyed", dev);
+       }
+
+       usb_set_intfdata(intf, NULL);
+}
+
+static int smsusb_init_device(struct usb_interface *intf, int board_id)
+{
+       struct smsdevice_params_t params;
+       struct smsusb_device_t *dev;
+       int i, rc;
+
+       /* create device object */
+       dev = kzalloc(sizeof(struct smsusb_device_t), GFP_KERNEL);
+       if (!dev) {
+               sms_err("kzalloc(sizeof(struct smsusb_device_t) failed");
+               return -ENOMEM;
+       }
+
+       memset(&params, 0, sizeof(params));
+       usb_set_intfdata(intf, dev);
+       dev->udev = interface_to_usbdev(intf);
+
+       params.device_type = sms_get_board(board_id)->type;
+
+       switch (params.device_type) {
+       case SMS_STELLAR:
+               dev->buffer_size = USB1_BUFFER_SIZE;
+
+               params.setmode_handler = smsusb1_setmode;
+               params.detectmode_handler = smsusb1_detectmode;
+               break;
+       default:
+               sms_err("Unspecified sms device type!");
+               /* fall-thru */
+       case SMS_NOVA_A0:
+       case SMS_NOVA_B0:
+       case SMS_VEGA:
+               dev->buffer_size = USB2_BUFFER_SIZE;
+               dev->response_alignment =
+                       dev->udev->ep_in[1]->desc.wMaxPacketSize -
+                       sizeof(struct SmsMsgHdr_ST);
+
+               params.flags |= SMS_DEVICE_FAMILY2;
+               break;
+       }
+
+       params.device = &dev->udev->dev;
+       params.buffer_size = dev->buffer_size;
+       params.num_buffers = MAX_BUFFERS;
+       params.sendrequest_handler = smsusb_sendrequest;
+       params.context = dev;
+       snprintf(params.devpath, sizeof(params.devpath),
+                "usb\\%d-%s", dev->udev->bus->busnum, dev->udev->devpath);
+
+       /* register in smscore */
+       rc = smscore_register_device(&params, &dev->coredev);
+       if (rc < 0) {
+               sms_err("smscore_register_device(...) failed, rc %d", rc);
+               smsusb_term_device(intf);
+               return rc;
+       }
+
+       smscore_set_board_id(dev->coredev, board_id);
+
+       /* initialize urbs */
+       for (i = 0; i < MAX_URBS; i++) {
+               dev->surbs[i].dev = dev;
+               usb_init_urb(&dev->surbs[i].urb);
+       }
+
+       sms_info("smsusb_start_streaming(...).");
+       rc = smsusb_start_streaming(dev);
+       if (rc < 0) {
+               sms_err("smsusb_start_streaming(...) failed");
+               smsusb_term_device(intf);
+               return rc;
+       }
+
+       rc = smscore_start_device(dev->coredev);
+       if (rc < 0) {
+               sms_err("smscore_start_device(...) failed");
+               smsusb_term_device(intf);
+               return rc;
+       }
+
+       sms_info("device %p created", dev);
+
+       return rc;
+}
+
+static int smsusb_probe(struct usb_interface *intf,
+                       const struct usb_device_id *id)
+{
+       struct usb_device *udev = interface_to_usbdev(intf);
+       char devpath[32];
+       int i, rc;
+
+       rc = usb_clear_halt(udev, usb_rcvbulkpipe(udev, 0x81));
+       rc = usb_clear_halt(udev, usb_rcvbulkpipe(udev, 0x02));
+
+       if (intf->num_altsetting > 0) {
+               rc = usb_set_interface(
+                       udev, intf->cur_altsetting->desc.bInterfaceNumber, 0);
+               if (rc < 0) {
+                       sms_err("usb_set_interface failed, rc %d", rc);
+                       return rc;
+               }
+       }
+
+       sms_info("smsusb_probe %d",
+              intf->cur_altsetting->desc.bInterfaceNumber);
+       for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++)
+               sms_info("endpoint %d %02x %02x %d", i,
+                      intf->cur_altsetting->endpoint[i].desc.bEndpointAddress,
+                      intf->cur_altsetting->endpoint[i].desc.bmAttributes,
+                      intf->cur_altsetting->endpoint[i].desc.wMaxPacketSize);
+
+       if ((udev->actconfig->desc.bNumInterfaces == 2) &&
+           (intf->cur_altsetting->desc.bInterfaceNumber == 0)) {
+               sms_err("rom interface 0 is not used");
+               return -ENODEV;
+       }
+
+       if (intf->cur_altsetting->desc.bInterfaceNumber == 1) {
+               snprintf(devpath, sizeof(devpath), "usb\\%d-%s",
+                        udev->bus->busnum, udev->devpath);
+               sms_info("stellar device was found.");
+               return smsusb1_load_firmware(
+                               udev, smscore_registry_getmode(devpath),
+                               id->driver_info);
+       }
+
+       rc = smsusb_init_device(intf, id->driver_info);
+       sms_info("rc %d", rc);
+       return rc;
+}
+
+static void smsusb_disconnect(struct usb_interface *intf)
+{
+       smsusb_term_device(intf);
+}
+
+static struct usb_driver smsusb_driver = {
+       .name                   = "sms1xxx",
+       .probe                  = smsusb_probe,
+       .disconnect             = smsusb_disconnect,
+       .id_table               = smsusb_id_table,
+};
+
+int smsusb_register(void)
+{
+       int rc = usb_register(&smsusb_driver);
+       if (rc)
+               sms_err("usb_register failed. Error number %d", rc);
+
+       sms_debug("");
+
+       return rc;
+}
+
+void smsusb_unregister(void)
+{
+       sms_debug("");
+       /* Regular USB Cleanup */
+       usb_deregister(&smsusb_driver);
+}
+
index 07643e01009340cada617b9979a2837ad7747965..87c973ac668bde2aad93d1493a9b870175d4999c 100644 (file)
@@ -106,6 +106,8 @@ config DVB_BUDGET_CI
        select DVB_STV0299 if !DVB_FE_CUSTOMISE
        select DVB_TDA1004X if !DVB_FE_CUSTOMISE
        select DVB_LNBP21 if !DVB_FE_CUSTOMISE
+       select DVB_TDA10023 if !DVB_FE_CUSTOMISE
+       select MEDIA_TUNER_TDA827X if !DVB_FE_CUSTOMISE
        select VIDEO_IR
        help
          Support for simple SAA7146 based DVB cards
index d7483f1a9b3f9fa4f311fd266045a0feb000ea9f..71451237294c2c205e396a0c10d23ddb2138f073 100644 (file)
@@ -3,7 +3,11 @@
 # and the AV7110 DVB device driver
 #
 
-dvb-ttpci-objs := av7110_hw.o av7110_v4l.o av7110_av.o av7110_ca.o av7110.o av7110_ipack.o av7110_ir.o
+dvb-ttpci-objs := av7110_hw.o av7110_v4l.o av7110_av.o av7110_ca.o av7110.o av7110_ipack.o
+
+ifdef CONFIG_INPUT_EVDEV
+dvb-ttpci-objs += av7110_ir.o
+endif
 
 obj-$(CONFIG_TTPCI_EEPROM) += ttpci-eeprom.o
 obj-$(CONFIG_DVB_BUDGET_CORE) += budget-core.o
@@ -14,6 +18,7 @@ obj-$(CONFIG_DVB_BUDGET_PATCH) += budget-patch.o
 obj-$(CONFIG_DVB_AV7110) += dvb-ttpci.o
 
 EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
+EXTRA_CFLAGS += -Idrivers/media/common/tuners
 
 hostprogs-y    := fdump
 
index f05d43d8b5cf25cfef2bdc0e866c97080ffca257..0777e8f9544ba66d63402a7f7bc5facd2a73c08c 100644 (file)
@@ -587,7 +587,7 @@ static void gpioirq(unsigned long data)
                }
                DVB_RINGBUFFER_SKIP(cibuf, 2);
 
-               dvb_ringbuffer_read(cibuf, av7110->debi_virt, len, 0);
+               dvb_ringbuffer_read(cibuf, av7110->debi_virt, len);
 
                iwdebi(av7110, DEBINOSWAP, TX_LEN, len, 2);
                iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, len, 2);
@@ -1198,7 +1198,6 @@ static int start_ts_capture(struct av7110 *budget)
        if (budget->feeding1)
                return ++budget->feeding1;
        memset(budget->grabbing, 0x00, TS_HEIGHT * TS_WIDTH);
-       budget->tsf = 0xff;
        budget->ttbp = 0;
        SAA7146_IER_ENABLE(budget->dev, MASK_10); /* VPE */
        saa7146_write(budget->dev, MC1, (MASK_04 | MASK_20)); /* DMA3 on */
@@ -2403,18 +2402,18 @@ static int __devinit av7110_attach(struct saa7146_dev* dev,
                saa7146_write(dev, MC1, MASK_29);
                /* RPS1 timeout disable */
                saa7146_write(dev, RPS_TOV1, 0);
-               WRITE_RPS1(cpu_to_le32(CMD_PAUSE | EVT_VBI_B));
-               WRITE_RPS1(cpu_to_le32(CMD_WR_REG_MASK | (GPIO_CTRL>>2)));
-               WRITE_RPS1(cpu_to_le32(GPIO3_MSK));
-               WRITE_RPS1(cpu_to_le32(SAA7146_GPIO_OUTLO<<24));
+               WRITE_RPS1(CMD_PAUSE | EVT_VBI_B);
+               WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2));
+               WRITE_RPS1(GPIO3_MSK);
+               WRITE_RPS1(SAA7146_GPIO_OUTLO<<24);
 #if RPS_IRQ
                /* issue RPS1 interrupt to increment counter */
-               WRITE_RPS1(cpu_to_le32(CMD_INTERRUPT));
+               WRITE_RPS1(CMD_INTERRUPT);
 #endif
-               WRITE_RPS1(cpu_to_le32(CMD_STOP));
+               WRITE_RPS1(CMD_STOP);
                /* Jump to begin of RPS program as safety measure               (p37) */
-               WRITE_RPS1(cpu_to_le32(CMD_JUMP));
-               WRITE_RPS1(cpu_to_le32(dev->d_rps1.dma_handle));
+               WRITE_RPS1(CMD_JUMP);
+               WRITE_RPS1(dev->d_rps1.dma_handle);
 
 #if RPS_IRQ
                /* set event counter 1 source as RPS1 interrupt (0x03)          (rE4 p53)
@@ -2472,11 +2471,7 @@ static int __devinit av7110_attach(struct saa7146_dev* dev,
           get recognized before the main driver is fully loaded */
        saa7146_write(dev, GPIO_CTRL, 0x500000);
 
-#ifdef I2C_ADAP_CLASS_TV_DIGITAL
-       av7110->i2c_adap.class = I2C_ADAP_CLASS_TV_DIGITAL;
-#else
        av7110->i2c_adap.class = I2C_CLASS_TV_DIGITAL;
-#endif
        strlcpy(av7110->i2c_adap.name, pci_ext->ext_priv, sizeof(av7110->i2c_adap.name));
 
        saa7146_i2c_adapter_prepare(dev, &av7110->i2c_adap, SAA7146_I2C_BUS_BIT_RATE_120); /* 275 kHz */
@@ -2527,28 +2522,28 @@ static int __devinit av7110_attach(struct saa7146_dev* dev,
                count = 0;
 
                /* Wait Source Line Counter Threshold                           (p36) */
-               WRITE_RPS1(cpu_to_le32(CMD_PAUSE | EVT_HS));
+               WRITE_RPS1(CMD_PAUSE | EVT_HS);
                /* Set GPIO3=1                                                  (p42) */
-               WRITE_RPS1(cpu_to_le32(CMD_WR_REG_MASK | (GPIO_CTRL>>2)));
-               WRITE_RPS1(cpu_to_le32(GPIO3_MSK));
-               WRITE_RPS1(cpu_to_le32(SAA7146_GPIO_OUTHI<<24));
+               WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2));
+               WRITE_RPS1(GPIO3_MSK);
+               WRITE_RPS1(SAA7146_GPIO_OUTHI<<24);
 #if RPS_IRQ
                /* issue RPS1 interrupt */
-               WRITE_RPS1(cpu_to_le32(CMD_INTERRUPT));
+               WRITE_RPS1(CMD_INTERRUPT);
 #endif
                /* Wait reset Source Line Counter Threshold                     (p36) */
-               WRITE_RPS1(cpu_to_le32(CMD_PAUSE | RPS_INV | EVT_HS));
+               WRITE_RPS1(CMD_PAUSE | RPS_INV | EVT_HS);
                /* Set GPIO3=0                                                  (p42) */
-               WRITE_RPS1(cpu_to_le32(CMD_WR_REG_MASK | (GPIO_CTRL>>2)));
-               WRITE_RPS1(cpu_to_le32(GPIO3_MSK));
-               WRITE_RPS1(cpu_to_le32(SAA7146_GPIO_OUTLO<<24));
+               WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2));
+               WRITE_RPS1(GPIO3_MSK);
+               WRITE_RPS1(SAA7146_GPIO_OUTLO<<24);
 #if RPS_IRQ
                /* issue RPS1 interrupt */
-               WRITE_RPS1(cpu_to_le32(CMD_INTERRUPT));
+               WRITE_RPS1(CMD_INTERRUPT);
 #endif
                /* Jump to begin of RPS program                                 (p37) */
-               WRITE_RPS1(cpu_to_le32(CMD_JUMP));
-               WRITE_RPS1(cpu_to_le32(dev->d_rps1.dma_handle));
+               WRITE_RPS1(CMD_JUMP);
+               WRITE_RPS1(dev->d_rps1.dma_handle);
 
                /* Fix VSYNC level */
                saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO);
index e494e04eeee8fb7b8ac2922cbfb8e90d4e0c211c..55f23ddcb994e259fa56fe45f646019bf7e916da 100644 (file)
@@ -188,7 +188,6 @@ struct av7110 {
        struct dvb_net          dvb_net1;
        spinlock_t              feedlock1;
        int                     feeding1;
-       u8                      tsf;
        u32                     ttbp;
        unsigned char           *grabbing;
        struct saa7146_pgtable  pt;
index ec55a968f204cf1195dbb219883055a149b46267..184647ad1c7c041fb5aa890c4b9cadbb6c979571 100644 (file)
@@ -269,7 +269,7 @@ int av7110_pes_play(void *dest, struct dvb_ringbuffer *buf, int dlen)
                return -1;
        }
 
-       dvb_ringbuffer_read(buf, dest, (size_t) blen, 0);
+       dvb_ringbuffer_read(buf, dest, (size_t) blen);
 
        dprintk(2, "pread=0x%08lx, pwrite=0x%08lx\n",
               (unsigned long) buf->pread, (unsigned long) buf->pwrite);
index c58e3fc509ed314f8c039d75870a94e1275f4679..261135ded4815ecf909875773ddf19c9ceb50836 100644 (file)
@@ -208,7 +208,7 @@ static ssize_t ci_ll_read(struct dvb_ringbuffer *cibuf, struct file *file,
                return -EINVAL;
        DVB_RINGBUFFER_SKIP(cibuf, 2);
 
-       return dvb_ringbuffer_read(cibuf, (u8 *)buf, len, 1);
+       return dvb_ringbuffer_read_user(cibuf, buf, len);
 }
 
 static int dvb_ca_open(struct inode *inode, struct file *file)
index 74d940f75da606517e9ed46098b63db8cbc59d59..ca99e5c1fc8abe142e23a7d93552f26d0a57f1f6 100644 (file)
@@ -305,7 +305,6 @@ enum av7110_command_type {
 #define IRQ_STATE      (DPRAM_BASE + 0x0F4)
 #define IRQ_STATE_EXT  (DPRAM_BASE + 0x0F6)
 #define MSGSTATE       (DPRAM_BASE + 0x0F8)
-#define FILT_STATE     (DPRAM_BASE + 0x0FA)
 #define COMMAND                (DPRAM_BASE + 0x0FC)
 #define COM_BUFF       (DPRAM_BASE + 0x100)
 #define COM_BUFF_SIZE  0x20
@@ -332,8 +331,6 @@ enum av7110_command_type {
 
 /* firmware status area */
 #define STATUS_BASE    (DPRAM_BASE + 0x1FC0)
-#define STATUS_SCR     (STATUS_BASE + 0x00)
-#define STATUS_MODES   (STATUS_BASE + 0x04)
 #define STATUS_LOOPS   (STATUS_BASE + 0x08)
 
 #define STATUS_MPEG_WIDTH     (STATUS_BASE + 0x0C)
index b30a5288e484fb837e2985c41180be506de01a8b..b7d1f2f18d3a45e1939b61e69ea1dd9ff6448cc9 100644 (file)
@@ -667,6 +667,11 @@ static struct tda1002x_config philips_cu1216_config_altaddress = {
        .invert = 0,
 };
 
+static struct tda10023_config philips_cu1216_tda10023_config = {
+       .demod_address = 0x0c,
+       .invert = 1,
+};
+
 static int philips_tu1216_tuner_init(struct dvb_frontend *fe)
 {
        struct budget *budget = (struct budget *) fe->dvb->priv;
@@ -1019,9 +1024,10 @@ static void frontend_init(struct budget_av *budget_av)
        case SUBID_DVBC_KNC1_PLUS_MK3:
                budget_av->reinitialise_demod = 1;
                budget_av->budget.dev->i2c_bitrate = SAA7146_I2C_BUS_BIT_RATE_240;
-               fe = dvb_attach(tda10023_attach, &philips_cu1216_config,
-                                    &budget_av->budget.i2c_adap,
-                                    read_pwm(budget_av));
+               fe = dvb_attach(tda10023_attach,
+                       &philips_cu1216_tda10023_config,
+                       &budget_av->budget.i2c_adap,
+                       read_pwm(budget_av));
                if (fe) {
                        fe->ops.tuner_ops.set_params = philips_cu1216_tuner_set_params;
                }
index 6530323d540617259db6c46f4bc7150d3b9858d1..060e7c7853266f9a2ae6fd514748a6e10b67182c 100644 (file)
@@ -46,6 +46,8 @@
 #include "lnbp21.h"
 #include "bsbe1.h"
 #include "bsru6.h"
+#include "tda1002x.h"
+#include "tda827x.h"
 
 /*
  * Regarding DEBIADDR_IR:
@@ -225,6 +227,7 @@ static int msp430_ir_init(struct budget_ci *budget_ci)
                break;
        case 0x1010:
        case 0x1017:
+       case 0x101a:
                /* for the Technotrend 1500 bundled remote */
                ir_input_init(input_dev, &budget_ci->ir.state,
                              IR_TYPE_RC5, ir_codes_tt_1500);
@@ -1056,6 +1059,15 @@ static struct stv0297_config dvbc_philips_tdm1316l_config = {
        .stop_during_read = 1,
 };
 
+static struct tda10023_config tda10023_config = {
+       .demod_address = 0xc,
+       .invert = 0,
+       .xtal = 16000000,
+       .pll_m = 11,
+       .pll_p = 3,
+       .pll_n = 1,
+       .deltaf = 0xa511,
+};
 
 
 
@@ -1126,7 +1138,17 @@ static void frontend_init(struct budget_ci *budget_ci)
                                budget_ci->budget.dvb_frontend = NULL;
                        }
                }
+               break;
 
+       case 0x101a: /* TT Budget-C-1501 (philips tda10023/philips tda8274A) */
+               budget_ci->budget.dvb_frontend = dvb_attach(tda10023_attach, &tda10023_config, &budget_ci->budget.i2c_adap, 0x48);
+               if (budget_ci->budget.dvb_frontend) {
+                       if (dvb_attach(tda827x_attach, budget_ci->budget.dvb_frontend, 0x61, &budget_ci->budget.i2c_adap, NULL) == NULL) {
+                               printk(KERN_ERR "%s: No tda827x found!\n", __func__);
+                               dvb_frontend_detach(budget_ci->budget.dvb_frontend);
+                               budget_ci->budget.dvb_frontend = NULL;
+                       }
+               }
                break;
        }
 
@@ -1216,6 +1238,7 @@ MAKE_BUDGET_INFO(ttbci, "TT-Budget/WinTV-NOVA-CI PCI", BUDGET_TT_HW_DISEQC);
 MAKE_BUDGET_INFO(ttbt2, "TT-Budget/WinTV-NOVA-T         PCI", BUDGET_TT);
 MAKE_BUDGET_INFO(ttbtci, "TT-Budget-T-CI PCI", BUDGET_TT);
 MAKE_BUDGET_INFO(ttbcci, "TT-Budget-C-CI PCI", BUDGET_TT);
+MAKE_BUDGET_INFO(ttc1501, "TT-Budget C-1501 PCI", BUDGET_TT);
 
 static struct pci_device_id pci_tbl[] = {
        MAKE_EXTENSION_PCI(ttbci, 0x13c2, 0x100c),
@@ -1224,6 +1247,7 @@ static struct pci_device_id pci_tbl[] = {
        MAKE_EXTENSION_PCI(ttbt2, 0x13c2, 0x1011),
        MAKE_EXTENSION_PCI(ttbtci, 0x13c2, 0x1012),
        MAKE_EXTENSION_PCI(ttbs2, 0x13c2, 0x1017),
+       MAKE_EXTENSION_PCI(ttc1501, 0x13c2, 0x101a),
        {
         .vendor = 0,
         }
index 18cac4b12ab28f73fb97a25f219788438b703bee..6f4ddb643fee12482ae84ab0f76939a4fd91999c 100644 (file)
@@ -497,11 +497,7 @@ int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev,
        if (bi->type != BUDGET_FS_ACTIVY)
                saa7146_write(dev, GPIO_CTRL, 0x500000);        /* GPIO 3 = 1 */
 
-#ifdef I2C_ADAP_CLASS_TV_DIGITAL
-       budget->i2c_adap.class = I2C_ADAP_CLASS_TV_DIGITAL;
-#else
        budget->i2c_adap.class = I2C_CLASS_TV_DIGITAL;
-#endif
 
        strlcpy(budget->i2c_adap.name, budget->card->name, sizeof(budget->i2c_adap.name));
 
index 9a155396d6acd1648d45f255d5faf04ef8be9720..39bd0a20f53a41f4d8cf51ecdc41644abb039994 100644 (file)
@@ -431,22 +431,22 @@ static int budget_patch_attach (struct saa7146_dev* dev, struct saa7146_pci_exte
        // in budget patch GPIO3 is connected to VSYNC_B
        count = 0;
 #if 0
-       WRITE_RPS1(cpu_to_le32(CMD_UPLOAD |
-         MASK_10 | MASK_09 | MASK_08 | MASK_06 | MASK_05 | MASK_04 | MASK_03 | MASK_02 ));
+       WRITE_RPS1(CMD_UPLOAD |
+         MASK_10 | MASK_09 | MASK_08 | MASK_06 | MASK_05 | MASK_04 | MASK_03 | MASK_02 );
 #endif
-       WRITE_RPS1(cpu_to_le32(CMD_PAUSE | EVT_VBI_B));
-       WRITE_RPS1(cpu_to_le32(CMD_WR_REG_MASK | (GPIO_CTRL>>2)));
-       WRITE_RPS1(cpu_to_le32(GPIO3_MSK));
-       WRITE_RPS1(cpu_to_le32(SAA7146_GPIO_OUTLO<<24));
+       WRITE_RPS1(CMD_PAUSE | EVT_VBI_B);
+       WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2));
+       WRITE_RPS1(GPIO3_MSK);
+       WRITE_RPS1(SAA7146_GPIO_OUTLO<<24);
 #if RPS_IRQ
        // issue RPS1 interrupt to increment counter
-       WRITE_RPS1(cpu_to_le32(CMD_INTERRUPT));
+       WRITE_RPS1(CMD_INTERRUPT);
        // at least a NOP is neede between two interrupts
-       WRITE_RPS1(cpu_to_le32(CMD_NOP));
+       WRITE_RPS1(CMD_NOP);
        // interrupt again
-       WRITE_RPS1(cpu_to_le32(CMD_INTERRUPT));
+       WRITE_RPS1(CMD_INTERRUPT);
 #endif
-       WRITE_RPS1(cpu_to_le32(CMD_STOP));
+       WRITE_RPS1(CMD_STOP);
 
 #if RPS_IRQ
        // set event counter 1 source as RPS1 interrupt (0x03)          (rE4 p53)
@@ -558,28 +558,28 @@ static int budget_patch_attach (struct saa7146_dev* dev, struct saa7146_pci_exte
 
 
        // Wait Source Line Counter Threshold                           (p36)
-       WRITE_RPS1(cpu_to_le32(CMD_PAUSE | EVT_HS));
+       WRITE_RPS1(CMD_PAUSE | EVT_HS);
        // Set GPIO3=1                                                  (p42)
-       WRITE_RPS1(cpu_to_le32(CMD_WR_REG_MASK | (GPIO_CTRL>>2)));
-       WRITE_RPS1(cpu_to_le32(GPIO3_MSK));
-       WRITE_RPS1(cpu_to_le32(SAA7146_GPIO_OUTHI<<24));
+       WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2));
+       WRITE_RPS1(GPIO3_MSK);
+       WRITE_RPS1(SAA7146_GPIO_OUTHI<<24);
 #if RPS_IRQ
        // issue RPS1 interrupt
-       WRITE_RPS1(cpu_to_le32(CMD_INTERRUPT));
+       WRITE_RPS1(CMD_INTERRUPT);
 #endif
        // Wait reset Source Line Counter Threshold                     (p36)
-       WRITE_RPS1(cpu_to_le32(CMD_PAUSE | RPS_INV | EVT_HS));
+       WRITE_RPS1(CMD_PAUSE | RPS_INV | EVT_HS);
        // Set GPIO3=0                                                  (p42)
-       WRITE_RPS1(cpu_to_le32(CMD_WR_REG_MASK | (GPIO_CTRL>>2)));
-       WRITE_RPS1(cpu_to_le32(GPIO3_MSK));
-       WRITE_RPS1(cpu_to_le32(SAA7146_GPIO_OUTLO<<24));
+       WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2));
+       WRITE_RPS1(GPIO3_MSK);
+       WRITE_RPS1(SAA7146_GPIO_OUTLO<<24);
 #if RPS_IRQ
        // issue RPS1 interrupt
-       WRITE_RPS1(cpu_to_le32(CMD_INTERRUPT));
+       WRITE_RPS1(CMD_INTERRUPT);
 #endif
        // Jump to begin of RPS program                                 (p37)
-       WRITE_RPS1(cpu_to_le32(CMD_JUMP));
-       WRITE_RPS1(cpu_to_le32(dev->d_rps1.dma_handle));
+       WRITE_RPS1(CMD_JUMP);
+       WRITE_RPS1(dev->d_rps1.dma_handle);
 
        // Fix VSYNC level
        saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO);
index bc2043e44ebdef143d1615dbb9e0b4aab2976753..e6c9cd2e3b94514784a4ee423553e2f94987ca35 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/wait.h>
+#include <linux/fs.h>
 #include <linux/module.h>
 #include <linux/usb.h>
 #include <linux/delay.h>
@@ -990,22 +991,9 @@ static int stc_open(struct inode *inode, struct file *file)
 }
 
 static ssize_t stc_read(struct file *file, char *buf, size_t count,
-                loff_t * offset)
+                loff_t *offset)
 {
-       int tc = count;
-
-       if ((tc + *offset) > 8192)
-               tc = 8192 - *offset;
-
-       if (tc < 0)
-               return 0;
-
-       if (copy_to_user(buf, stc_firmware + *offset, tc))
-               return -EFAULT;
-
-       *offset += tc;
-
-       return tc;
+       return simple_read_from_buffer(buf, count, offset, stc_firmware, 8192);
 }
 
 static int stc_release(struct inode *inode, struct file *file)
@@ -1693,11 +1681,7 @@ static int ttusb_probe(struct usb_interface *intf, const struct usb_device_id *i
 
        i2c_set_adapdata(&ttusb->i2c_adap, ttusb);
 
-#ifdef I2C_ADAP_CLASS_TV_DIGITAL
-       ttusb->i2c_adap.class             = I2C_ADAP_CLASS_TV_DIGITAL;
-#else
        ttusb->i2c_adap.class             = I2C_CLASS_TV_DIGITAL;
-#endif
        ttusb->i2c_adap.algo              = &ttusb_dec_algo;
        ttusb->i2c_adap.algo_data         = NULL;
        ttusb->i2c_adap.dev.parent        = &udev->dev;
index 77354ca6e8e928005cfcf631ed97d698bfd9b1ab..dc93a882b38595114169bb575820cf023155a460 100644 (file)
  */
 
 
+/*
+ * User Notes:
+ * - USB Audio is provided by the alsa snd_usb_audio module.
+ *   For listing you have to redirect the sound, for example using:
+ *   arecord -D hw:1,0 -r96000 -c2 -f S16_LE | artsdsp aplay -B -
+ * - regarding module parameters in /sys/module/radio_si470x/parameters:
+ *   the contents of read-only files (0444) are not updated, even if
+ *   space, band and de are changed using private video controls
+ * - increase tune_timeout, if you often get -EIO errors
+ * - hw_freq_seek returns -EAGAIN, when timed out or band limit is reached
+ */
+
+
 /*
  * History:
  * 2008-01-12  Tobias Lorenz <tobias.lorenz@gmx.net>
  *             Oliver Neukum <oliver@neukum.org>
  *             Version 1.0.7
  *             - usb autosuspend support
- *             - unplugging fixed
+ *             - unplugging fixed
+ * 2008-05-07  Tobias Lorenz <tobias.lorenz@gmx.net>
+ *             Version 1.0.8
+ *             - hardware frequency seek support
+ *             - afc indication
+ *             - more safety checks, let si470x_get_freq return errno
  *
  * ToDo:
- * - add seeking support
  * - add firmware download/update support
  * - RDS support: interrupt mode, instead of polling
  * - add LED status output (check if that's not already done in firmware)
 /* driver definitions */
 #define DRIVER_AUTHOR "Tobias Lorenz <tobias.lorenz@gmx.net>"
 #define DRIVER_NAME "radio-si470x"
-#define DRIVER_KERNEL_VERSION KERNEL_VERSION(1, 0, 7)
+#define DRIVER_KERNEL_VERSION KERNEL_VERSION(1, 0, 8)
 #define DRIVER_CARD "Silicon Labs Si470x FM Radio Receiver"
 #define DRIVER_DESC "USB radio driver for Si470x FM Radio Receivers"
-#define DRIVER_VERSION "1.0.7"
+#define DRIVER_VERSION "1.0.8"
 
 
 /* kernel includes */
@@ -175,6 +192,11 @@ static unsigned int tune_timeout = 3000;
 module_param(tune_timeout, uint, 0);
 MODULE_PARM_DESC(tune_timeout, "Tune timeout: *3000*");
 
+/* Seek timeout */
+static unsigned int seek_timeout = 5000;
+module_param(seek_timeout, uint, 0);
+MODULE_PARM_DESC(seek_timeout, "Seek timeout: *5000*");
+
 /* RDS buffer blocks */
 static unsigned int rds_buf = 100;
 module_param(rds_buf, uint, 0);
@@ -425,7 +447,8 @@ struct si470x_device {
 
        /* driver management */
        unsigned int users;
-       unsigned char disconnected;
+       unsigned char disconnected;
+       struct mutex disconnect_lock;
 
        /* Silabs internal registers (0..15) */
        unsigned short registers[RADIO_REGISTER_NUM];
@@ -441,12 +464,6 @@ struct si470x_device {
 };
 
 
-/*
- * Lock to prevent kfree of data before all users have releases the device.
- */
-static DEFINE_MUTEX(open_close_lock);
-
-
 /*
  * The frequency is set in units of 62.5 Hz when using V4L2_TUNER_CAP_LOW,
  * 62.5 kHz otherwise.
@@ -476,11 +493,11 @@ static int si470x_get_report(struct si470x_device *radio, void *buf, int size)
                USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
                report[0], 2,
                buf, size, usb_timeout);
+
        if (retval < 0)
                printk(KERN_WARNING DRIVER_NAME
                        ": si470x_get_report: usb_control_msg returned %d\n",
                        retval);
-
        return retval;
 }
 
@@ -499,11 +516,11 @@ static int si470x_set_report(struct si470x_device *radio, void *buf, int size)
                USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
                report[0], 2,
                buf, size, usb_timeout);
+
        if (retval < 0)
                printk(KERN_WARNING DRIVER_NAME
                        ": si470x_set_report: usb_control_msg returned %d\n",
                        retval);
-
        return retval;
 }
 
@@ -521,8 +538,7 @@ static int si470x_get_register(struct si470x_device *radio, int regnr)
        retval = si470x_get_report(radio, (void *) &buf, sizeof(buf));
 
        if (retval >= 0)
-               radio->registers[regnr] = be16_to_cpu(get_unaligned(
-                       (unsigned short *) &buf[1]));
+               radio->registers[regnr] = get_unaligned_be16(&buf[1]);
 
        return (retval < 0) ? -EINVAL : 0;
 }
@@ -537,8 +553,7 @@ static int si470x_set_register(struct si470x_device *radio, int regnr)
        int retval;
 
        buf[0] = REGISTER_REPORT(regnr);
-       put_unaligned(cpu_to_be16(radio->registers[regnr]),
-               (unsigned short *) &buf[1]);
+       put_unaligned_be16(radio->registers[regnr], &buf[1]);
 
        retval = si470x_set_report(radio, (void *) &buf, sizeof(buf));
 
@@ -561,9 +576,8 @@ static int si470x_get_all_registers(struct si470x_device *radio)
 
        if (retval >= 0)
                for (regnr = 0; regnr < RADIO_REGISTER_NUM; regnr++)
-                       radio->registers[regnr] = be16_to_cpu(get_unaligned(
-                               (unsigned short *)
-                               &buf[regnr * RADIO_REGISTER_SIZE + 1]));
+                       radio->registers[regnr] = get_unaligned_be16(
+                               &buf[regnr * RADIO_REGISTER_SIZE + 1]);
 
        return (retval < 0) ? -EINVAL : 0;
 }
@@ -585,7 +599,7 @@ static int si470x_get_rds_registers(struct si470x_device *radio)
                usb_rcvintpipe(radio->usbdev, 1),
                (void *) &buf, sizeof(buf), &size, usb_timeout);
        if (size != sizeof(buf))
-              printk(KERN_WARNING DRIVER_NAME ": si470x_get_rds_registers: "
+               printk(KERN_WARNING DRIVER_NAME ": si470x_get_rds_registers: "
                        "return size differs: %d != %zu\n", size, sizeof(buf));
        if (retval < 0)
                printk(KERN_WARNING DRIVER_NAME ": si470x_get_rds_registers: "
@@ -594,8 +608,8 @@ static int si470x_get_rds_registers(struct si470x_device *radio)
        if (retval >= 0)
                for (regnr = 0; regnr < RDS_REGISTER_NUM; regnr++)
                        radio->registers[STATUSRSSI + regnr] =
-                               be16_to_cpu(get_unaligned((unsigned short *)
-                               &buf[regnr * RADIO_REGISTER_SIZE + 1]));
+                               get_unaligned_be16(
+                               &buf[regnr * RADIO_REGISTER_SIZE + 1]);
 
        return (retval < 0) ? -EINVAL : 0;
 }
@@ -615,33 +629,39 @@ static int si470x_set_chan(struct si470x_device *radio, unsigned short chan)
        radio->registers[CHANNEL] |= CHANNEL_TUNE | chan;
        retval = si470x_set_register(radio, CHANNEL);
        if (retval < 0)
-               return retval;
+               goto done;
 
-       /* wait till seek operation has completed */
+       /* wait till tune operation has completed */
        timeout = jiffies + msecs_to_jiffies(tune_timeout);
        do {
                retval = si470x_get_register(radio, STATUSRSSI);
                if (retval < 0)
-                       return retval;
+                       goto stop;
                timed_out = time_after(jiffies, timeout);
        } while (((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) &&
                (!timed_out));
+       if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0)
+               printk(KERN_WARNING DRIVER_NAME ": tune does not complete\n");
        if (timed_out)
                printk(KERN_WARNING DRIVER_NAME
-                       ": seek does not finish after %u ms\n", tune_timeout);
+                       ": tune timed out after %u ms\n", tune_timeout);
 
+stop:
        /* stop tuning */
        radio->registers[CHANNEL] &= ~CHANNEL_TUNE;
-       return si470x_set_register(radio, CHANNEL);
+       retval = si470x_set_register(radio, CHANNEL);
+
+done:
+       return retval;
 }
 
 
 /*
  * si470x_get_freq - get the frequency
  */
-static unsigned int si470x_get_freq(struct si470x_device *radio)
+static int si470x_get_freq(struct si470x_device *radio, unsigned int *freq)
 {
-       unsigned int spacing, band_bottom, freq;
+       unsigned int spacing, band_bottom;
        unsigned short chan;
        int retval;
 
@@ -667,14 +687,12 @@ static unsigned int si470x_get_freq(struct si470x_device *radio)
 
        /* read channel */
        retval = si470x_get_register(radio, READCHAN);
-       if (retval < 0)
-               return retval;
        chan = radio->registers[READCHAN] & READCHAN_READCHAN;
 
        /* Frequency (MHz) = Spacing (kHz) x Channel + Bottom of Band (MHz) */
-       freq = chan * spacing + band_bottom;
+       *freq = chan * spacing + band_bottom;
 
-       return freq;
+       return retval;
 }
 
 
@@ -713,6 +731,62 @@ static int si470x_set_freq(struct si470x_device *radio, unsigned int freq)
 }
 
 
+/*
+ * si470x_set_seek - set seek
+ */
+static int si470x_set_seek(struct si470x_device *radio,
+               unsigned int wrap_around, unsigned int seek_upward)
+{
+       int retval = 0;
+       unsigned long timeout;
+       bool timed_out = 0;
+
+       /* start seeking */
+       radio->registers[POWERCFG] |= POWERCFG_SEEK;
+       if (wrap_around == 1)
+               radio->registers[POWERCFG] &= ~POWERCFG_SKMODE;
+       else
+               radio->registers[POWERCFG] |= POWERCFG_SKMODE;
+       if (seek_upward == 1)
+               radio->registers[POWERCFG] |= POWERCFG_SEEKUP;
+       else
+               radio->registers[POWERCFG] &= ~POWERCFG_SEEKUP;
+       retval = si470x_set_register(radio, POWERCFG);
+       if (retval < 0)
+               goto done;
+
+       /* wait till seek operation has completed */
+       timeout = jiffies + msecs_to_jiffies(seek_timeout);
+       do {
+               retval = si470x_get_register(radio, STATUSRSSI);
+               if (retval < 0)
+                       goto stop;
+               timed_out = time_after(jiffies, timeout);
+       } while (((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) &&
+               (!timed_out));
+       if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0)
+               printk(KERN_WARNING DRIVER_NAME ": seek does not complete\n");
+       if (radio->registers[STATUSRSSI] & STATUSRSSI_SF)
+               printk(KERN_WARNING DRIVER_NAME
+                       ": seek failed / band limit reached\n");
+       if (timed_out)
+               printk(KERN_WARNING DRIVER_NAME
+                       ": seek timed out after %u ms\n", seek_timeout);
+
+stop:
+       /* stop seeking */
+       radio->registers[POWERCFG] &= ~POWERCFG_SEEK;
+       retval = si470x_set_register(radio, POWERCFG);
+
+done:
+       /* try again, if timed out */
+       if ((retval == 0) && timed_out)
+               retval = -EAGAIN;
+
+       return retval;
+}
+
+
 /*
  * si470x_start - switch on radio
  */
@@ -725,27 +799,30 @@ static int si470x_start(struct si470x_device *radio)
                POWERCFG_DMUTE | POWERCFG_ENABLE | POWERCFG_RDSM;
        retval = si470x_set_register(radio, POWERCFG);
        if (retval < 0)
-               return retval;
+               goto done;
 
        /* sysconfig 1 */
        radio->registers[SYSCONFIG1] = SYSCONFIG1_DE;
        retval = si470x_set_register(radio, SYSCONFIG1);
        if (retval < 0)
-               return retval;
+               goto done;
 
        /* sysconfig 2 */
        radio->registers[SYSCONFIG2] =
-               (0x3f  << 8) |  /* SEEKTH */
-               (band  << 6) |  /* BAND */
-               (space << 4) |  /* SPACE */
-               15;             /* VOLUME (max) */
+               (0x3f  << 8) |                          /* SEEKTH */
+               ((band  << 6) & SYSCONFIG2_BAND)  |     /* BAND */
+               ((space << 4) & SYSCONFIG2_SPACE) |     /* SPACE */
+               15;                                     /* VOLUME (max) */
        retval = si470x_set_register(radio, SYSCONFIG2);
        if (retval < 0)
-               return retval;
+               goto done;
 
        /* reset last channel */
-       return si470x_set_chan(radio,
+       retval = si470x_set_chan(radio,
                radio->registers[CHANNEL] & CHANNEL_CHAN);
+
+done:
+       return retval;
 }
 
 
@@ -760,13 +837,16 @@ static int si470x_stop(struct si470x_device *radio)
        radio->registers[SYSCONFIG1] &= ~SYSCONFIG1_RDS;
        retval = si470x_set_register(radio, SYSCONFIG1);
        if (retval < 0)
-               return retval;
+               goto done;
 
        /* powercfg */
        radio->registers[POWERCFG] &= ~POWERCFG_DMUTE;
        /* POWERCFG_ENABLE has to automatically go low */
        radio->registers[POWERCFG] |= POWERCFG_ENABLE | POWERCFG_DISABLE;
-       return si470x_set_register(radio, POWERCFG);
+       retval = si470x_set_register(radio, POWERCFG);
+
+done:
+       return retval;
 }
 
 
@@ -843,7 +923,7 @@ static void si470x_rds(struct si470x_device *radio)
                };
 
                /* Fill the V4L2 RDS buffer */
-               put_unaligned(cpu_to_le16(rds), (unsigned short *) &tmpbuf);
+               put_unaligned_le16(rds, &tmpbuf);
                tmpbuf[2] = blocknum;           /* offset name */
                tmpbuf[2] |= blocknum << 3;     /* received offset */
                if (bler > max_rds_errors)
@@ -883,8 +963,9 @@ static void si470x_work(struct work_struct *work)
        struct si470x_device *radio = container_of(work, struct si470x_device,
                work.work);
 
-       if (radio->disconnected)
-              return;
+       /* safety checks */
+       if (radio->disconnected)
+               return;
        if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0)
                return;
 
@@ -917,11 +998,15 @@ static ssize_t si470x_fops_read(struct file *file, char __user *buf,
 
        /* block if no new data available */
        while (radio->wr_index == radio->rd_index) {
-               if (file->f_flags & O_NONBLOCK)
-                       return -EWOULDBLOCK;
+               if (file->f_flags & O_NONBLOCK) {
+                       retval = -EWOULDBLOCK;
+                       goto done;
+               }
                if (wait_event_interruptible(radio->read_queue,
-                       radio->wr_index != radio->rd_index) < 0)
-                       return -EINTR;
+                       radio->wr_index != radio->rd_index) < 0) {
+                       retval = -EINTR;
+                       goto done;
+               }
        }
 
        /* calculate block count from byte count */
@@ -950,6 +1035,7 @@ static ssize_t si470x_fops_read(struct file *file, char __user *buf,
        }
        mutex_unlock(&radio->lock);
 
+done:
        return retval;
 }
 
@@ -961,6 +1047,7 @@ static unsigned int si470x_fops_poll(struct file *file,
                struct poll_table_struct *pts)
 {
        struct si470x_device *radio = video_get_drvdata(video_devdata(file));
+       int retval = 0;
 
        /* switch on rds reception */
        if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) {
@@ -972,9 +1059,9 @@ static unsigned int si470x_fops_poll(struct file *file,
        poll_wait(file, &radio->read_queue, pts);
 
        if (radio->rd_index != radio->wr_index)
-               return POLLIN | POLLRDNORM;
+               retval = POLLIN | POLLRDNORM;
 
-       return 0;
+       return retval;
 }
 
 
@@ -991,17 +1078,18 @@ static int si470x_fops_open(struct inode *inode, struct file *file)
        retval = usb_autopm_get_interface(radio->intf);
        if (retval < 0) {
                radio->users--;
-               return -EIO;
+               retval = -EIO;
+               goto done;
        }
 
        if (radio->users == 1) {
                retval = si470x_start(radio);
                if (retval < 0)
                        usb_autopm_put_interface(radio->intf);
-               return retval;
        }
 
-       return 0;
+done:
+       return retval;
 }
 
 
@@ -1011,20 +1099,23 @@ static int si470x_fops_open(struct inode *inode, struct file *file)
 static int si470x_fops_release(struct inode *inode, struct file *file)
 {
        struct si470x_device *radio = video_get_drvdata(video_devdata(file));
-       int retval = 0;
+       int retval = 0;
 
-       if (!radio)
-               return -ENODEV;
+       /* safety check */
+       if (!radio) {
+               retval = -ENODEV;
+               goto done;
+       }
 
-       mutex_lock(&open_close_lock);
+       mutex_lock(&radio->disconnect_lock);
        radio->users--;
        if (radio->users == 0) {
-              if (radio->disconnected) {
-                      video_unregister_device(radio->videodev);
-                      kfree(radio->buffer);
-                      kfree(radio);
-                      goto done;
-              }
+               if (radio->disconnected) {
+                       video_unregister_device(radio->videodev);
+                       kfree(radio->buffer);
+                       kfree(radio);
+                       goto unlock;
+               }
 
                /* stop rds reception */
                cancel_delayed_work_sync(&radio->work);
@@ -1036,9 +1127,11 @@ static int si470x_fops_release(struct inode *inode, struct file *file)
                usb_autopm_put_interface(radio->intf);
        }
 
+unlock:
+       mutex_unlock(&radio->disconnect_lock);
+
 done:
-       mutex_unlock(&open_close_lock);
-       return retval;
+       return retval;
 }
 
 
@@ -1116,7 +1209,8 @@ static int si470x_vidioc_querycap(struct file *file, void *priv,
        strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card));
        sprintf(capability->bus_info, "USB");
        capability->version = DRIVER_KERNEL_VERSION;
-       capability->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
+       capability->capabilities = V4L2_CAP_HW_FREQ_SEEK |
+               V4L2_CAP_TUNER | V4L2_CAP_RADIO;
 
        return 0;
 }
@@ -1125,7 +1219,7 @@ static int si470x_vidioc_querycap(struct file *file, void *priv,
 /*
  * si470x_vidioc_g_input - get input
  */
-static int si470x_vidioc_g_input(struct file *filp, void *priv,
+static int si470x_vidioc_g_input(struct file *file, void *priv,
                unsigned int *i)
 {
        *i = 0;
@@ -1137,12 +1231,18 @@ static int si470x_vidioc_g_input(struct file *filp, void *priv,
 /*
  * si470x_vidioc_s_input - set input
  */
-static int si470x_vidioc_s_input(struct file *filp, void *priv, unsigned int i)
+static int si470x_vidioc_s_input(struct file *file, void *priv, unsigned int i)
 {
+       int retval = 0;
+
+       /* safety checks */
        if (i != 0)
-               return -EINVAL;
+               retval = -EINVAL;
 
-       return 0;
+       if (retval < 0)
+               printk(KERN_WARNING DRIVER_NAME
+                       ": set input failed with %d\n", retval);
+       return retval;
 }
 
 
@@ -1155,17 +1255,22 @@ static int si470x_vidioc_queryctrl(struct file *file, void *priv,
        unsigned char i;
        int retval = -EINVAL;
 
+       /* safety checks */
+       if (!qc->id)
+               goto done;
+
        for (i = 0; i < ARRAY_SIZE(si470x_v4l2_queryctrl); i++) {
-               if (qc->id && qc->id == si470x_v4l2_queryctrl[i].id) {
+               if (qc->id == si470x_v4l2_queryctrl[i].id) {
                        memcpy(qc, &(si470x_v4l2_queryctrl[i]), sizeof(*qc));
                        retval = 0;
                        break;
                }
        }
+
+done:
        if (retval < 0)
                printk(KERN_WARNING DRIVER_NAME
-                       ": query control failed with %d\n", retval);
-
+                       ": query controls failed with %d\n", retval);
        return retval;
 }
 
@@ -1177,9 +1282,13 @@ static int si470x_vidioc_g_ctrl(struct file *file, void *priv,
                struct v4l2_control *ctrl)
 {
        struct si470x_device *radio = video_get_drvdata(video_devdata(file));
+       int retval = 0;
 
-       if (radio->disconnected)
-              return -EIO;
+       /* safety checks */
+       if (radio->disconnected) {
+               retval = -EIO;
+               goto done;
+       }
 
        switch (ctrl->id) {
        case V4L2_CID_AUDIO_VOLUME:
@@ -1190,9 +1299,15 @@ static int si470x_vidioc_g_ctrl(struct file *file, void *priv,
                ctrl->value = ((radio->registers[POWERCFG] &
                                POWERCFG_DMUTE) == 0) ? 1 : 0;
                break;
+       default:
+               retval = -EINVAL;
        }
 
-       return 0;
+done:
+       if (retval < 0)
+               printk(KERN_WARNING DRIVER_NAME
+                       ": get control failed with %d\n", retval);
+       return retval;
 }
 
 
@@ -1203,10 +1318,13 @@ static int si470x_vidioc_s_ctrl(struct file *file, void *priv,
                struct v4l2_control *ctrl)
 {
        struct si470x_device *radio = video_get_drvdata(video_devdata(file));
-       int retval;
+       int retval = 0;
 
-       if (radio->disconnected)
-              return -EIO;
+       /* safety checks */
+       if (radio->disconnected) {
+               retval = -EIO;
+               goto done;
+       }
 
        switch (ctrl->id) {
        case V4L2_CID_AUDIO_VOLUME:
@@ -1224,10 +1342,11 @@ static int si470x_vidioc_s_ctrl(struct file *file, void *priv,
        default:
                retval = -EINVAL;
        }
+
+done:
        if (retval < 0)
                printk(KERN_WARNING DRIVER_NAME
                        ": set control failed with %d\n", retval);
-
        return retval;
 }
 
@@ -1238,13 +1357,22 @@ static int si470x_vidioc_s_ctrl(struct file *file, void *priv,
 static int si470x_vidioc_g_audio(struct file *file, void *priv,
                struct v4l2_audio *audio)
 {
-       if (audio->index > 1)
-               return -EINVAL;
+       int retval = 0;
+
+       /* safety checks */
+       if (audio->index != 0) {
+               retval = -EINVAL;
+               goto done;
+       }
 
        strcpy(audio->name, "Radio");
        audio->capability = V4L2_AUDCAP_STEREO;
 
-       return 0;
+done:
+       if (retval < 0)
+               printk(KERN_WARNING DRIVER_NAME
+                       ": get audio failed with %d\n", retval);
+       return retval;
 }
 
 
@@ -1254,10 +1382,19 @@ static int si470x_vidioc_g_audio(struct file *file, void *priv,
 static int si470x_vidioc_s_audio(struct file *file, void *priv,
                struct v4l2_audio *audio)
 {
-       if (audio->index != 0)
-               return -EINVAL;
+       int retval = 0;
 
-       return 0;
+       /* safety checks */
+       if (audio->index != 0) {
+               retval = -EINVAL;
+               goto done;
+       }
+
+done:
+       if (retval < 0)
+               printk(KERN_WARNING DRIVER_NAME
+                       ": set audio failed with %d\n", retval);
+       return retval;
 }
 
 
@@ -1268,20 +1405,23 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv,
                struct v4l2_tuner *tuner)
 {
        struct si470x_device *radio = video_get_drvdata(video_devdata(file));
-       int retval;
+       int retval = 0;
 
-       if (radio->disconnected)
-              return -EIO;
-       if (tuner->index > 0)
-               return -EINVAL;
+       /* safety checks */
+       if (radio->disconnected) {
+               retval = -EIO;
+               goto done;
+       }
+       if ((tuner->index != 0) && (tuner->type != V4L2_TUNER_RADIO)) {
+               retval = -EINVAL;
+               goto done;
+       }
 
-       /* read status rssi */
        retval = si470x_get_register(radio, STATUSRSSI);
        if (retval < 0)
-               return retval;
+               goto done;
 
        strcpy(tuner->name, "FM");
-       tuner->type = V4L2_TUNER_RADIO;
        switch (band) {
        /* 0: 87.5 - 108 MHz (USA, Europe, default) */
        default:
@@ -1313,9 +1453,14 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv,
                                * 0x0101;
 
        /* automatic frequency control: -1: freq to low, 1 freq to high */
-       tuner->afc = 0;
+       /* AFCRL does only indicate that freq. differs, not if too low/high */
+       tuner->afc = (radio->registers[STATUSRSSI] & STATUSRSSI_AFCRL) ? 1 : 0;
 
-       return 0;
+done:
+       if (retval < 0)
+               printk(KERN_WARNING DRIVER_NAME
+                       ": get tuner failed with %d\n", retval);
+       return retval;
 }
 
 
@@ -1326,12 +1471,17 @@ static int si470x_vidioc_s_tuner(struct file *file, void *priv,
                struct v4l2_tuner *tuner)
 {
        struct si470x_device *radio = video_get_drvdata(video_devdata(file));
-       int retval;
+       int retval = 0;
 
-       if (radio->disconnected)
-              return -EIO;
-       if (tuner->index > 0)
-               return -EINVAL;
+       /* safety checks */
+       if (radio->disconnected) {
+               retval = -EIO;
+               goto done;
+       }
+       if ((tuner->index != 0) && (tuner->type != V4L2_TUNER_RADIO)) {
+               retval = -EINVAL;
+               goto done;
+       }
 
        if (tuner->audmode == V4L2_TUNER_MODE_MONO)
                radio->registers[POWERCFG] |= POWERCFG_MONO;  /* force mono */
@@ -1339,10 +1489,11 @@ static int si470x_vidioc_s_tuner(struct file *file, void *priv,
                radio->registers[POWERCFG] &= ~POWERCFG_MONO; /* try stereo */
 
        retval = si470x_set_register(radio, POWERCFG);
+
+done:
        if (retval < 0)
                printk(KERN_WARNING DRIVER_NAME
                        ": set tuner failed with %d\n", retval);
-
        return retval;
 }
 
@@ -1354,14 +1505,25 @@ static int si470x_vidioc_g_frequency(struct file *file, void *priv,
                struct v4l2_frequency *freq)
 {
        struct si470x_device *radio = video_get_drvdata(video_devdata(file));
+       int retval = 0;
 
-       if (radio->disconnected)
-              return -EIO;
+       /* safety checks */
+       if (radio->disconnected) {
+               retval = -EIO;
+               goto done;
+       }
+       if ((freq->tuner != 0) && (freq->type != V4L2_TUNER_RADIO)) {
+               retval = -EINVAL;
+               goto done;
+       }
 
-       freq->type = V4L2_TUNER_RADIO;
-       freq->frequency = si470x_get_freq(radio);
+       retval = si470x_get_freq(radio, &freq->frequency);
 
-       return 0;
+done:
+       if (retval < 0)
+               printk(KERN_WARNING DRIVER_NAME
+                       ": get frequency failed with %d\n", retval);
+       return retval;
 }
 
 
@@ -1372,19 +1534,55 @@ static int si470x_vidioc_s_frequency(struct file *file, void *priv,
                struct v4l2_frequency *freq)
 {
        struct si470x_device *radio = video_get_drvdata(video_devdata(file));
-       int retval;
+       int retval = 0;
 
-       if (radio->disconnected)
-              return -EIO;
-       if (freq->type != V4L2_TUNER_RADIO)
-               return -EINVAL;
+       /* safety checks */
+       if (radio->disconnected) {
+               retval = -EIO;
+               goto done;
+       }
+       if ((freq->tuner != 0) && (freq->type != V4L2_TUNER_RADIO)) {
+               retval = -EINVAL;
+               goto done;
+       }
 
        retval = si470x_set_freq(radio, freq->frequency);
+
+done:
        if (retval < 0)
                printk(KERN_WARNING DRIVER_NAME
                        ": set frequency failed with %d\n", retval);
+       return retval;
+}
 
-       return 0;
+
+/*
+ * si470x_vidioc_s_hw_freq_seek - set hardware frequency seek
+ */
+static int si470x_vidioc_s_hw_freq_seek(struct file *file, void *priv,
+               struct v4l2_hw_freq_seek *seek)
+{
+       struct si470x_device *radio = video_get_drvdata(video_devdata(file));
+       int retval = 0;
+
+       /* safety checks */
+       if (radio->disconnected) {
+               retval = -EIO;
+               goto done;
+       }
+       if ((seek->tuner != 0) && (seek->type != V4L2_TUNER_RADIO)) {
+               retval = -EINVAL;
+               goto done;
+       }
+
+       retval = si470x_set_seek(radio, seek->wrap_around, seek->seek_upward);
+
+done:
+       if (retval < 0)
+               printk(KERN_WARNING DRIVER_NAME
+                       ": set hardware frequency seek failed with %d\n",
+                       retval);
+       return retval;
 }
 
 
@@ -1408,6 +1606,7 @@ static struct video_device si470x_viddev_template = {
        .vidioc_s_tuner         = si470x_vidioc_s_tuner,
        .vidioc_g_frequency     = si470x_vidioc_g_frequency,
        .vidioc_s_frequency     = si470x_vidioc_s_frequency,
+       .vidioc_s_hw_freq_seek  = si470x_vidioc_s_hw_freq_seek,
        .owner                  = THIS_MODULE,
 };
 
@@ -1424,31 +1623,36 @@ static int si470x_usb_driver_probe(struct usb_interface *intf,
                const struct usb_device_id *id)
 {
        struct si470x_device *radio;
-       int retval = -ENOMEM;
+       int retval = 0;
 
-       /* private data allocation */
+       /* private data allocation and initialization */
        radio = kzalloc(sizeof(struct si470x_device), GFP_KERNEL);
-       if (!radio)
+       if (!radio) {
+               retval = -ENOMEM;
                goto err_initial;
+       }
+       radio->users = 0;
+       radio->disconnected = 0;
+       radio->usbdev = interface_to_usbdev(intf);
+       radio->intf = intf;
+       mutex_init(&radio->disconnect_lock);
+       mutex_init(&radio->lock);
 
-       /* video device allocation */
+       /* video device allocation and initialization */
        radio->videodev = video_device_alloc();
-       if (!radio->videodev)
+       if (!radio->videodev) {
+               retval = -ENOMEM;
                goto err_radio;
-
-       /* initial configuration */
+       }
        memcpy(radio->videodev, &si470x_viddev_template,
                        sizeof(si470x_viddev_template));
-       radio->users = 0;
-       radio->usbdev = interface_to_usbdev(intf);
-       radio->intf = intf;
-       mutex_init(&radio->lock);
        video_set_drvdata(radio->videodev, radio);
 
        /* show some infos about the specific device */
-       retval = -EIO;
-       if (si470x_get_all_registers(radio) < 0)
+       if (si470x_get_all_registers(radio) < 0) {
+               retval = -EIO;
                goto err_all;
+       }
        printk(KERN_INFO DRIVER_NAME ": DeviceID=0x%4.4hx ChipID=0x%4.4hx\n",
                        radio->registers[DEVICEID], radio->registers[CHIPID]);
 
@@ -1474,8 +1678,10 @@ static int si470x_usb_driver_probe(struct usb_interface *intf,
        /* rds buffer allocation */
        radio->buf_size = rds_buf * 3;
        radio->buffer = kmalloc(radio->buf_size, GFP_KERNEL);
-       if (!radio->buffer)
+       if (!radio->buffer) {
+               retval = -EIO;
                goto err_all;
+       }
 
        /* rds buffer configuration */
        radio->wr_index = 0;
@@ -1487,6 +1693,7 @@ static int si470x_usb_driver_probe(struct usb_interface *intf,
 
        /* register video device */
        if (video_register_device(radio->videodev, VFL_TYPE_RADIO, radio_nr)) {
+               retval = -EIO;
                printk(KERN_WARNING DRIVER_NAME
                                ": Could not register video device\n");
                goto err_all;
@@ -1546,16 +1753,16 @@ static void si470x_usb_driver_disconnect(struct usb_interface *intf)
 {
        struct si470x_device *radio = usb_get_intfdata(intf);
 
-       mutex_lock(&open_close_lock);
-       radio->disconnected = 1;
+       mutex_lock(&radio->disconnect_lock);
+       radio->disconnected = 1;
        cancel_delayed_work_sync(&radio->work);
        usb_set_intfdata(intf, NULL);
-       if (radio->users == 0) {
-              video_unregister_device(radio->videodev);
-              kfree(radio->buffer);
-              kfree(radio);
-       }
-       mutex_unlock(&open_close_lock);
+       if (radio->users == 0) {
+               video_unregister_device(radio->videodev);
+               kfree(radio->buffer);
+               kfree(radio);
+       }
+       mutex_unlock(&radio->disconnect_lock);
 }
 
 
index 5ccb0aeca8ccde50c4a605a42c3574c46358a045..f606d2951fde5a44fb650df92187ff71cd5ea3ad 100644 (file)
@@ -24,21 +24,21 @@ config VIDEOBUF_VMALLOC
        select VIDEOBUF_GEN
        tristate
 
+config VIDEOBUF_DMA_CONTIG
+       depends on HAS_DMA
+       select VIDEOBUF_GEN
+       tristate
+
 config VIDEOBUF_DVB
        tristate
        select VIDEOBUF_GEN
-       select VIDEOBUF_DMA_SG
 
 config VIDEO_BTCX
        tristate
 
-config VIDEO_IR_I2C
-       tristate
-
 config VIDEO_IR
        tristate
        depends on INPUT
-       select VIDEO_IR_I2C if I2C
 
 config VIDEO_TVEEPROM
        tristate
@@ -84,6 +84,19 @@ config VIDEO_HELPER_CHIPS_AUTO
 
          In doubt, say Y.
 
+config VIDEO_IR_I2C
+       tristate "I2C module for IR" if !VIDEO_HELPER_CHIPS_AUTO
+       depends on I2C && VIDEO_IR
+       default y
+       ---help---
+         Most boards have an IR chip directly connected via GPIO. However,
+         some video boards have the IR connected via I2C bus.
+
+         If your board doesn't have an I2C IR chip, you may disable this
+         option.
+
+         In doubt, say Y.
+
 #
 # Encoder / Decoder module configuration
 #
@@ -600,9 +613,6 @@ config VIDEO_STRADIS
          driver for PCI.  There is a product page at
          <http://www.stradis.com/>.
 
-config VIDEO_ZORAN_ZR36060
-       tristate
-
 config VIDEO_ZORAN
        tristate "Zoran ZR36057/36067 Video For Linux"
        depends on PCI && I2C_ALGOBIT && VIDEO_V4L1 && VIRT_TO_BUS
@@ -616,61 +626,64 @@ config VIDEO_ZORAN
          To compile this driver as a module, choose M here: the
          module will be called zr36067.
 
+config VIDEO_ZORAN_DC30
+       tristate "Pinnacle/Miro DC30(+) support"
+       depends on VIDEO_ZORAN
+       select VIDEO_ADV7175 if VIDEO_HELPER_CHIPS_AUTO
+       select VIDEO_VPX3220 if VIDEO_HELPER_CHIPS_AUTO
+       help
+         Support for the Pinnacle/Miro DC30(+) MJPEG capture/playback
+         card. This also supports really old DC10 cards based on the
+         zr36050 MJPEG codec and zr36016 VFE.
+
+config VIDEO_ZORAN_ZR36060
+       tristate "Zoran ZR36060"
+       depends on VIDEO_ZORAN
+       help
+         Say Y to support Zoran boards based on 36060 chips.
+         This includes Iomega Bus, Pinnacle DC10, Linux media Labs 33
+         and 33 R10 and AverMedia 6 boards.
+
 config VIDEO_ZORAN_BUZ
        tristate "Iomega Buz support"
-       depends on VIDEO_ZORAN
+       depends on VIDEO_ZORAN_ZR36060
        select VIDEO_SAA7111 if VIDEO_HELPER_CHIPS_AUTO
        select VIDEO_SAA7185 if VIDEO_HELPER_CHIPS_AUTO
-       select VIDEO_ZORAN_ZR36060
        help
          Support for the Iomega Buz MJPEG capture/playback card.
 
 config VIDEO_ZORAN_DC10
        tristate "Pinnacle/Miro DC10(+) support"
-       depends on VIDEO_ZORAN
-       select VIDEO_SAA7110
+       depends on VIDEO_ZORAN_ZR36060
+       select VIDEO_SAA7110 if VIDEO_HELPER_CHIPS_AUTO
        select VIDEO_ADV7175 if VIDEO_HELPER_CHIPS_AUTO
-       select VIDEO_ZORAN_ZR36060
        help
          Support for the Pinnacle/Miro DC10(+) MJPEG capture/playback
          card.
 
-config VIDEO_ZORAN_DC30
-       tristate "Pinnacle/Miro DC30(+) support"
-       depends on VIDEO_ZORAN
-       select VIDEO_ADV7175 if VIDEO_HELPER_CHIPS_AUTO
-       select VIDEO_VPX3220 if VIDEO_HELPER_CHIPS_AUTO
-       help
-         Support for the Pinnacle/Miro DC30(+) MJPEG capture/playback
-         card. This also supports really old DC10 cards based on the
-         zr36050 MJPEG codec and zr36016 VFE.
-
 config VIDEO_ZORAN_LML33
        tristate "Linux Media Labs LML33 support"
-       depends on VIDEO_ZORAN
+       depends on VIDEO_ZORAN_ZR36060
        select VIDEO_BT819 if VIDEO_HELPER_CHIPS_AUTO
        select VIDEO_BT856 if VIDEO_HELPER_CHIPS_AUTO
-       select VIDEO_ZORAN_ZR36060
        help
          Support for the Linux Media Labs LML33 MJPEG capture/playback
          card.
 
 config VIDEO_ZORAN_LML33R10
        tristate "Linux Media Labs LML33R10 support"
-       depends on VIDEO_ZORAN
+       depends on VIDEO_ZORAN_ZR36060
        select VIDEO_SAA7114 if VIDEO_HELPER_CHIPS_AUTO
        select VIDEO_ADV7170 if VIDEO_HELPER_CHIPS_AUTO
-       select VIDEO_ZORAN_ZR36060
        help
          support for the Linux Media Labs LML33R10 MJPEG capture/playback
          card.
 
 config VIDEO_ZORAN_AVS6EYES
        tristate "AverMedia 6 Eyes support (EXPERIMENTAL)"
-       depends on VIDEO_ZORAN && EXPERIMENTAL && VIDEO_V4L1
+       depends on VIDEO_ZORAN_ZR36060 && EXPERIMENTAL && VIDEO_V4L1
        select VIDEO_BT856 if VIDEO_HELPER_CHIPS_AUTO
        select VIDEO_KS0127 if VIDEO_HELPER_CHIPS_AUTO
-       select VIDEO_ZORAN_ZR36060
        help
          Support for the AverMedia 6 Eyes video surveillance card.
 
@@ -801,6 +814,8 @@ config USB_VIDEO_CLASS
 
          For more information see: <http://linux-uvc.berlios.de/>
 
+source "drivers/media/video/gspca/Kconfig"
+
 source "drivers/media/video/pvrusb2/Kconfig"
 
 source "drivers/media/video/em28xx/Kconfig"
@@ -905,12 +920,21 @@ config USB_STKWEBCAM
          To compile this driver as a module, choose M here: the
          module will be called stkwebcam.
 
+config USB_S2255
+       tristate "USB Sensoray 2255 video capture device"
+       depends on VIDEO_V4L2
+       select VIDEOBUF_VMALLOC
+       default n
+       help
+         Say Y here if you want support for the Sensoray 2255 USB device.
+         This driver can be compiled as a module, called s2255drv.
+
 endif # V4L_USB_DRIVERS
 
 config SOC_CAMERA
        tristate "SoC camera support"
        depends on VIDEO_V4L2 && HAS_DMA
-       select VIDEOBUF_DMA_SG
+       select VIDEOBUF_GEN
        help
          SoC Camera is a common API to several cameras, not connecting
          over a bus like PCI or USB. For example some i2c camera connected
@@ -945,11 +969,26 @@ config MT9V022_PCA9536_SWITCH
          Select this if your MT9V022 camera uses a PCA9536 I2C GPIO
          extender to switch between 8 and 10 bit datawidth modes
 
+config SOC_CAMERA_PLATFORM
+       tristate "platform camera support"
+       depends on SOC_CAMERA
+       help
+         This is a generic SoC camera platform driver, useful for testing
+
 config VIDEO_PXA27x
        tristate "PXA27x Quick Capture Interface driver"
        depends on VIDEO_DEV && PXA27x
        select SOC_CAMERA
+       select VIDEOBUF_DMA_SG
        ---help---
          This is a v4l2 driver for the PXA27x Quick Capture Interface
 
+config VIDEO_SH_MOBILE_CEU
+       tristate "SuperH Mobile CEU Interface driver"
+       depends on VIDEO_DEV
+       select SOC_CAMERA
+       select VIDEOBUF_DMA_CONTIG
+       ---help---
+         This is a v4l2 driver for the SuperH Mobile CEU Interface
+
 endif # VIDEO_CAPTURE_DRIVERS
index ecbbfaab24d506daf49920a1755ef549277f4334..45d5db5abb1eabe445c8af4cf8182e0f1728c6f9 100644 (file)
@@ -88,6 +88,7 @@ obj-$(CONFIG_VIDEO_TUNER) += tuner.o
 
 obj-$(CONFIG_VIDEOBUF_GEN) += videobuf-core.o
 obj-$(CONFIG_VIDEOBUF_DMA_SG) += videobuf-dma-sg.o
+obj-$(CONFIG_VIDEOBUF_DMA_CONTIG) += videobuf-dma-contig.o
 obj-$(CONFIG_VIDEOBUF_VMALLOC) += videobuf-vmalloc.o
 obj-$(CONFIG_VIDEOBUF_DVB) += videobuf-dvb.o
 obj-$(CONFIG_VIDEO_BTCX)  += btcx-risc.o
@@ -117,11 +118,13 @@ obj-$(CONFIG_USB_SN9C102)       += sn9c102/
 obj-$(CONFIG_USB_ET61X251)      += et61x251/
 obj-$(CONFIG_USB_PWC)           += pwc/
 obj-$(CONFIG_USB_ZC0301)        += zc0301/
+obj-$(CONFIG_USB_GSPCA)         += gspca/
 
 obj-$(CONFIG_USB_IBMCAM)        += usbvideo/
 obj-$(CONFIG_USB_KONICAWC)      += usbvideo/
 obj-$(CONFIG_USB_VICAM)         += usbvideo/
 obj-$(CONFIG_USB_QUICKCAM_MESSENGER)   += usbvideo/
+obj-$(CONFIG_USB_S2255)                += s2255drv.o
 
 obj-$(CONFIG_VIDEO_IVTV) += ivtv/
 obj-$(CONFIG_VIDEO_CX18) += cx18/
@@ -130,9 +133,11 @@ obj-$(CONFIG_VIDEO_VIVI) += vivi.o
 obj-$(CONFIG_VIDEO_CX23885) += cx23885/
 
 obj-$(CONFIG_VIDEO_PXA27x)     += pxa_camera.o
+obj-$(CONFIG_VIDEO_SH_MOBILE_CEU)      += sh_mobile_ceu_camera.o
 obj-$(CONFIG_SOC_CAMERA)       += soc_camera.o
 obj-$(CONFIG_SOC_CAMERA_MT9M001)       += mt9m001.o
 obj-$(CONFIG_SOC_CAMERA_MT9V022)       += mt9v022.o
+obj-$(CONFIG_SOC_CAMERA_PLATFORM)      += soc_camera_platform.o
 
 obj-$(CONFIG_VIDEO_AU0828) += au0828/
 
index 8bfd5c75cb3a736b04218cb697a870c8d5ab936d..ddd2a7964dec25f179fdf7633ffca9ccf690a3cf 100644 (file)
@@ -516,7 +516,7 @@ bt819_detect_client (struct i2c_adapter *adapter,
 
        dprintk(1,
                KERN_INFO
-               "saa7111.c: detecting bt819 client on address 0x%x\n",
+               "bt819: detecting bt819 client on address 0x%x\n",
                address << 1);
 
        /* Check if the adapter supports the needed features */
index f92f06dec0d0a2fce3c09965e5e1b8570ca34acd..216fc9680e80c01ebd9762680403ab5b7831cbfd 100644 (file)
@@ -179,7 +179,6 @@ static int bt832_attach(struct i2c_adapter *adap, int addr, int kind)
 
        v4l_info(&t->client,"chip found @ 0x%x\n", addr<<1);
 
-
        if(! bt832_init(&t->client)) {
                bt832_detach(&t->client);
                return -1;
index 0165aac533bf1d98eb7d7c9f2a0c4cf71f61d11e..0ea559a7fe59ea3dba50fc435dfd57900477592f 100644 (file)
@@ -2448,7 +2448,7 @@ pix_format_set_size     (struct v4l2_pix_format *       f,
        }
 }
 
-static int bttv_g_fmt_cap(struct file *file, void *priv,
+static int bttv_g_fmt_vid_cap(struct file *file, void *priv,
                                        struct v4l2_format *f)
 {
        struct bttv_fh *fh  = priv;
@@ -2461,7 +2461,7 @@ static int bttv_g_fmt_cap(struct file *file, void *priv,
        return 0;
 }
 
-static int bttv_g_fmt_overlay(struct file *file, void *priv,
+static int bttv_g_fmt_vid_overlay(struct file *file, void *priv,
                                        struct v4l2_format *f)
 {
        struct bttv_fh *fh  = priv;
@@ -2472,7 +2472,7 @@ static int bttv_g_fmt_overlay(struct file *file, void *priv,
        return 0;
 }
 
-static int bttv_try_fmt_cap(struct file *file, void *priv,
+static int bttv_try_fmt_vid_cap(struct file *file, void *priv,
                                                struct v4l2_format *f)
 {
        const struct bttv_format *fmt;
@@ -2532,7 +2532,7 @@ static int bttv_try_fmt_cap(struct file *file, void *priv,
        return 0;
 }
 
-static int bttv_try_fmt_overlay(struct file *file, void *priv,
+static int bttv_try_fmt_vid_overlay(struct file *file, void *priv,
                                                struct v4l2_format *f)
 {
        struct bttv_fh *fh = priv;
@@ -2542,7 +2542,7 @@ static int bttv_try_fmt_overlay(struct file *file, void *priv,
                        /* adjust_crop */ 0);
 }
 
-static int bttv_s_fmt_cap(struct file *file, void *priv,
+static int bttv_s_fmt_vid_cap(struct file *file, void *priv,
                                struct v4l2_format *f)
 {
        int retval;
@@ -2556,7 +2556,7 @@ static int bttv_s_fmt_cap(struct file *file, void *priv,
        if (0 != retval)
                return retval;
 
-       retval = bttv_try_fmt_cap(file, priv, f);
+       retval = bttv_try_fmt_vid_cap(file, priv, f);
        if (0 != retval)
                return retval;
 
@@ -2591,7 +2591,7 @@ static int bttv_s_fmt_cap(struct file *file, void *priv,
        return 0;
 }
 
-static int bttv_s_fmt_overlay(struct file *file, void *priv,
+static int bttv_s_fmt_vid_overlay(struct file *file, void *priv,
                                struct v4l2_format *f)
 {
        struct bttv_fh *fh = priv;
@@ -2661,7 +2661,7 @@ static int bttv_querycap(struct file *file, void  *priv,
        return 0;
 }
 
-static int bttv_enum_fmt_vbi(struct file *file, void  *priv,
+static int bttv_enum_fmt_vbi_cap(struct file *file, void  *priv,
                                struct v4l2_fmtdesc *f)
 {
        if (0 != f->index)
@@ -2692,7 +2692,7 @@ static int bttv_enum_fmt_cap_ovr(struct v4l2_fmtdesc *f)
        return i;
 }
 
-static int bttv_enum_fmt_cap(struct file *file, void  *priv,
+static int bttv_enum_fmt_vid_cap(struct file *file, void  *priv,
                                struct v4l2_fmtdesc *f)
 {
        int rc = bttv_enum_fmt_cap_ovr(f);
@@ -2703,7 +2703,7 @@ static int bttv_enum_fmt_cap(struct file *file, void  *priv,
        return 0;
 }
 
-static int bttv_enum_fmt_overlay(struct file *file, void  *priv,
+static int bttv_enum_fmt_vid_overlay(struct file *file, void  *priv,
                                        struct v4l2_fmtdesc *f)
 {
        int rc;
@@ -3362,18 +3362,18 @@ static struct video_device bttv_video_template =
        .fops     = &bttv_fops,
        .minor    = -1,
        .vidioc_querycap                = bttv_querycap,
-       .vidioc_enum_fmt_cap            = bttv_enum_fmt_cap,
-       .vidioc_g_fmt_cap               = bttv_g_fmt_cap,
-       .vidioc_try_fmt_cap             = bttv_try_fmt_cap,
-       .vidioc_s_fmt_cap               = bttv_s_fmt_cap,
-       .vidioc_enum_fmt_overlay        = bttv_enum_fmt_overlay,
-       .vidioc_g_fmt_overlay           = bttv_g_fmt_overlay,
-       .vidioc_try_fmt_overlay         = bttv_try_fmt_overlay,
-       .vidioc_s_fmt_overlay           = bttv_s_fmt_overlay,
-       .vidioc_enum_fmt_vbi            = bttv_enum_fmt_vbi,
-       .vidioc_g_fmt_vbi               = bttv_g_fmt_vbi,
-       .vidioc_try_fmt_vbi             = bttv_try_fmt_vbi,
-       .vidioc_s_fmt_vbi               = bttv_s_fmt_vbi,
+       .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_enum_fmt_vbi_cap        = bttv_enum_fmt_vbi_cap,
+       .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,
@@ -3705,7 +3705,7 @@ static void bttv_risc_disasm(struct bttv *btv,
        for (i = 0; i < (risc->size >> 2); i += n) {
                printk("%s:   0x%lx: ", btv->c.name,
                       (unsigned long)(risc->dma + (i<<2)));
-               n = bttv_risc_decode(risc->cpu[i]);
+               n = bttv_risc_decode(le32_to_cpu(risc->cpu[i]));
                for (j = 1; j < n; j++)
                        printk("%s:   0x%lx: 0x%08x [ arg #%d ]\n",
                               btv->c.name, (unsigned long)(risc->dma + ((i+j)<<2)),
@@ -3774,8 +3774,8 @@ static void bttv_irq_debug_low_latency(struct bttv *btv, u32 rc)
        printk("bttv%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)btv->main.cpu[RISC_SLOT_O_VBI+1],
-              (unsigned long)btv->main.cpu[RISC_SLOT_O_FIELD+1],
+              (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)) {
@@ -4188,6 +4188,7 @@ static struct video_device *vdev_init(struct bttv *btv,
        vfd->dev     = &btv->c.pci->dev;
        vfd->release = video_device_release;
        vfd->type    = type;
+       vfd->debug   = bttv_debug;
        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);
index 4d5b8035e46634d31ade156cbee392130299be09..bcd2cd240a165d96f189c5e66e93028cc00735e1 100644 (file)
 #include <linux/jiffies.h>
 #include <asm/io.h>
 
-static struct i2c_algo_bit_data bttv_i2c_algo_bit_template;
-static struct i2c_adapter bttv_i2c_adap_sw_template;
-static struct i2c_adapter bttv_i2c_adap_hw_template;
-static struct i2c_client bttv_i2c_client_template;
-
 static int attach_inform(struct i2c_client *client);
 
 static int i2c_debug;
@@ -104,7 +99,7 @@ static int bttv_bit_getsda(void *data)
        return state;
 }
 
-static struct i2c_algo_bit_data bttv_i2c_algo_bit_template = {
+static struct i2c_algo_bit_data __devinitdata bttv_i2c_algo_bit_template = {
        .setsda  = bttv_bit_setsda,
        .setscl  = bttv_bit_setscl,
        .getsda  = bttv_bit_getsda,
@@ -113,14 +108,6 @@ static struct i2c_algo_bit_data bttv_i2c_algo_bit_template = {
        .timeout = 200,
 };
 
-static struct i2c_adapter bttv_i2c_adap_sw_template = {
-       .owner             = THIS_MODULE,
-       .class             = I2C_CLASS_TV_ANALOG,
-       .name              = "bttv",
-       .id                = I2C_HW_B_BT848,
-       .client_register   = attach_inform,
-};
-
 /* ----------------------------------------------------------------------- */
 /* I2C functions - hardware i2c                                            */
 
@@ -270,20 +257,11 @@ static int bttv_i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int
        return retval;
 }
 
-static struct i2c_algorithm bttv_algo = {
+static const struct i2c_algorithm bttv_algo = {
        .master_xfer   = bttv_i2c_xfer,
        .functionality = functionality,
 };
 
-static struct i2c_adapter bttv_i2c_adap_hw_template = {
-       .owner             = THIS_MODULE,
-       .class         = I2C_CLASS_TV_ANALOG,
-       .name          = "bt878",
-       .id            = I2C_HW_B_BT848 /* FIXME */,
-       .algo          = &bttv_algo,
-       .client_register = attach_inform,
-};
-
 /* ----------------------------------------------------------------------- */
 /* I2C functions - common stuff                                            */
 
@@ -332,10 +310,6 @@ void bttv_call_i2c_clients(struct bttv *btv, unsigned int cmd, void *arg)
        i2c_clients_command(&btv->c.i2c_adap, cmd, arg);
 }
 
-static struct i2c_client bttv_i2c_client_template = {
-       .name   = "bttv internal",
-};
-
 
 /* read I2C */
 int bttv_I2CRead(struct bttv *btv, unsigned char addr, char *probe_for)
@@ -417,29 +391,34 @@ static void do_i2c_scan(char *name, struct i2c_client *c)
 /* init + register i2c algo-bit adapter */
 int __devinit init_bttv_i2c(struct bttv *btv)
 {
-       memcpy(&btv->i2c_client, &bttv_i2c_client_template,
-              sizeof(bttv_i2c_client_template));
+       strlcpy(btv->i2c_client.name, "bttv internal", I2C_NAME_SIZE);
 
        if (i2c_hw)
                btv->use_i2c_hw = 1;
        if (btv->use_i2c_hw) {
                /* bt878 */
-               memcpy(&btv->c.i2c_adap, &bttv_i2c_adap_hw_template,
-                      sizeof(bttv_i2c_adap_hw_template));
+               strlcpy(btv->c.i2c_adap.name, "bt878",
+                       sizeof(btv->c.i2c_adap.name));
+               btv->c.i2c_adap.id = I2C_HW_B_BT848;    /* FIXME */
+               btv->c.i2c_adap.algo = &bttv_algo;
        } else {
                /* bt848 */
        /* Prevents usage of invalid delay values */
                if (i2c_udelay<5)
                        i2c_udelay=5;
-               bttv_i2c_algo_bit_template.udelay=i2c_udelay;
 
-               memcpy(&btv->c.i2c_adap, &bttv_i2c_adap_sw_template,
-                      sizeof(bttv_i2c_adap_sw_template));
+               strlcpy(btv->c.i2c_adap.name, "bttv",
+                       sizeof(btv->c.i2c_adap.name));
+               btv->c.i2c_adap.id = I2C_HW_B_BT848;
                memcpy(&btv->i2c_algo, &bttv_i2c_algo_bit_template,
                       sizeof(bttv_i2c_algo_bit_template));
+               btv->i2c_algo.udelay = i2c_udelay;
                btv->i2c_algo.data = btv;
                btv->c.i2c_adap.algo_data = &btv->i2c_algo;
        }
+       btv->c.i2c_adap.owner = THIS_MODULE;
+       btv->c.i2c_adap.class = I2C_CLASS_TV_ANALOG;
+       btv->c.i2c_adap.client_register = attach_inform;
 
        btv->c.i2c_adap.dev.parent = &btv->c.pci->dev;
        snprintf(btv->c.i2c_adap.name, sizeof(btv->c.i2c_adap.name),
index bfdbc469e30f379e6e75bdf4a2abaf63fcc28c4d..68f28e5fa0409307c460546e511dbbb68b7a04e0 100644 (file)
@@ -303,7 +303,7 @@ static int try_fmt(struct v4l2_vbi_format *f, const struct bttv_tvnorm *tvnorm,
        return 0;
 }
 
-int bttv_try_fmt_vbi(struct file *file, void *f, struct v4l2_format *frt)
+int bttv_try_fmt_vbi_cap(struct file *file, void *f, struct v4l2_format *frt)
 {
        struct bttv_fh *fh = f;
        struct bttv *btv = fh->btv;
@@ -321,7 +321,7 @@ int bttv_try_fmt_vbi(struct file *file, void *f, struct v4l2_format *frt)
 }
 
 
-int bttv_s_fmt_vbi(struct file *file, void *f, struct v4l2_format *frt)
+int bttv_s_fmt_vbi_cap(struct file *file, void *f, struct v4l2_format *frt)
 {
        struct bttv_fh *fh = f;
        struct bttv *btv = fh->btv;
@@ -369,7 +369,7 @@ int bttv_s_fmt_vbi(struct file *file, void *f, struct v4l2_format *frt)
 }
 
 
-int bttv_g_fmt_vbi(struct file *file, void *f, struct v4l2_format *frt)
+int bttv_g_fmt_vbi_cap(struct file *file, void *f, struct v4l2_format *frt)
 {
        struct bttv_fh *fh = f;
        const struct bttv_tvnorm *tvnorm;
index f2393202904b795a6528fab8745ea821b7627c6a..6d93d16c96e4609cb899da64f4fc12364705cb52 100644 (file)
@@ -299,7 +299,6 @@ extern int bttv_write_gpio(unsigned int card,
 /* ---------------------------------------------------------- */
 /* sysfs/driver-moded based gpio access interface             */
 
-
 struct bttv_sub_device {
        struct device    dev;
        struct bttv_core *core;
index 27da7b4232754745c0127e61fc39014fe30fa887..08ef54a22c9e7e843be5b8d8faa5a9789aa4235e 100644 (file)
@@ -39,7 +39,6 @@
 #include <linux/scatterlist.h>
 #include <asm/io.h>
 #include <media/v4l2-common.h>
-
 #include <linux/device.h>
 #include <media/videobuf-dma-sg.h>
 #include <media/tveeprom.h>
@@ -254,21 +253,19 @@ int bttv_overlay_risc(struct bttv *btv, struct bttv_overlay *ov,
 /* ---------------------------------------------------------- */
 /* bttv-vbi.c                                                 */
 
-int bttv_try_fmt_vbi(struct file *file, void *fh, struct v4l2_format *f);
-int bttv_g_fmt_vbi(struct file *file, void *fh, struct v4l2_format *f);
-int bttv_s_fmt_vbi(struct file *file, void *fh, struct v4l2_format *f);
+int bttv_try_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *f);
+int bttv_g_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *f);
+int bttv_s_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *f);
 
 extern struct videobuf_queue_ops bttv_vbi_qops;
 
 /* ---------------------------------------------------------- */
 /* bttv-gpio.c */
 
-
 extern struct bus_type bttv_sub_bus_type;
 int bttv_sub_add_device(struct bttv_core *core, char *name);
 int bttv_sub_del_devices(struct bttv_core *core);
 
-
 /* ---------------------------------------------------------- */
 /* bttv-driver.c                                              */
 
index 5195b1f3378a627d60dd66452033ae1f2e56f353..d99453faaab79715d3723492e99a9ea9d9a92116 100644 (file)
@@ -1593,7 +1593,7 @@ static struct v4l2_pix_format cafe_def_pix_format = {
        .sizeimage      = VGA_WIDTH*VGA_HEIGHT*2,
 };
 
-static int cafe_vidioc_enum_fmt_cap(struct file *filp,
+static int cafe_vidioc_enum_fmt_vid_cap(struct file *filp,
                void *priv, struct v4l2_fmtdesc *fmt)
 {
        struct cafe_camera *cam = priv;
@@ -1608,7 +1608,7 @@ static int cafe_vidioc_enum_fmt_cap(struct file *filp,
 }
 
 
-static int cafe_vidioc_try_fmt_cap (struct file *filp, void *priv,
+static int cafe_vidioc_try_fmt_vid_cap(struct file *filp, void *priv,
                struct v4l2_format *fmt)
 {
        struct cafe_camera *cam = priv;
@@ -1620,7 +1620,7 @@ static int cafe_vidioc_try_fmt_cap (struct file *filp, void *priv,
        return ret;
 }
 
-static int cafe_vidioc_s_fmt_cap(struct file *filp, void *priv,
+static int cafe_vidioc_s_fmt_vid_cap(struct file *filp, void *priv,
                struct v4l2_format *fmt)
 {
        struct cafe_camera *cam = priv;
@@ -1635,7 +1635,7 @@ static int cafe_vidioc_s_fmt_cap(struct file *filp, void *priv,
        /*
         * See if the formatting works in principle.
         */
-       ret = cafe_vidioc_try_fmt_cap(filp, priv, fmt);
+       ret = cafe_vidioc_try_fmt_vid_cap(filp, priv, fmt);
        if (ret)
                return ret;
        /*
@@ -1670,7 +1670,7 @@ static int cafe_vidioc_s_fmt_cap(struct file *filp, void *priv,
  * The V4l2 spec wants us to be smarter, and actually get this from
  * the camera (and not mess with it at open time).  Someday.
  */
-static int cafe_vidioc_g_fmt_cap(struct file *filp, void *priv,
+static int cafe_vidioc_g_fmt_vid_cap(struct file *filp, void *priv,
                struct v4l2_format *f)
 {
        struct cafe_camera *cam = priv;
@@ -1780,10 +1780,10 @@ static struct video_device cafe_v4l_template = {
        .release = cafe_v4l_dev_release,
 
        .vidioc_querycap        = cafe_vidioc_querycap,
-       .vidioc_enum_fmt_cap    = cafe_vidioc_enum_fmt_cap,
-       .vidioc_try_fmt_cap     = cafe_vidioc_try_fmt_cap,
-       .vidioc_s_fmt_cap       = cafe_vidioc_s_fmt_cap,
-       .vidioc_g_fmt_cap       = cafe_vidioc_g_fmt_cap,
+       .vidioc_enum_fmt_vid_cap = cafe_vidioc_enum_fmt_vid_cap,
+       .vidioc_try_fmt_vid_cap = cafe_vidioc_try_fmt_vid_cap,
+       .vidioc_s_fmt_vid_cap   = cafe_vidioc_s_fmt_vid_cap,
+       .vidioc_g_fmt_vid_cap   = cafe_vidioc_g_fmt_vid_cap,
        .vidioc_enum_input      = cafe_vidioc_enum_input,
        .vidioc_g_input         = cafe_vidioc_g_input,
        .vidioc_s_input         = cafe_vidioc_s_input,
index cefd1381e8de89eba2e75bb7056b27720e5afda0..54de0cd482e926c1f31f230ea823b90e3e77d078 100644 (file)
@@ -884,6 +884,7 @@ long v4l_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg)
        case VIDIOC_G_INPUT32:
        case VIDIOC_S_INPUT32:
        case VIDIOC_TRY_FMT32:
+       case VIDIOC_S_HW_FREQ_SEEK:
                ret = do_video_ioctl(file, cmd, arg);
                break;
 
index 03411503457ed5ca01fc12148aa31049a40a533c..1c3fa3a7470a3f9b7dd3f316aa14fe810d51631a 100644 (file)
@@ -173,4 +173,3 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = {
        .probe = cs5345_probe,
        .id_table = cs5345_id,
 };
-
index d965af860ab2636dfdc5f6b63da7f8a580237d42..645b339152d34aced2623d59974286736ce9e089 100644 (file)
@@ -43,7 +43,6 @@ MODULE_PARM_DESC(debug, "Debugging messages\n\t\t\t0=Off (default), 1=On");
 
 static unsigned short normal_i2c[] = { 0x22 >> 1, I2C_CLIENT_END };
 
-
 I2C_CLIENT_INSMOD;
 
 /* ----------------------------------------------------------------------- */
@@ -189,4 +188,3 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = {
        .probe = cs53l32a_probe,
        .id_table = cs53l32a_id,
 };
-
index 1adc404d955e48d00592b3b69615b2fdfdcfb0c6..6d5b94fc70879eec06835a8c958481019a3440bf 100644 (file)
 #include "cx18-cards.h"
 #include "cx18-audio.h"
 
+#define CX18_AUDIO_ENABLE 0xc72014
+
 /* Selects the audio input and output according to the current
    settings. */
 int cx18_audio_set_io(struct cx18 *cx)
 {
        struct v4l2_routing route;
        u32 audio_input;
+       u32 val;
        int mux_input;
+       int err;
 
        /* Determine which input to use */
        if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) {
@@ -51,8 +55,17 @@ int cx18_audio_set_io(struct cx18 *cx)
        cx18_i2c_hw(cx, cx->card->hw_muxer, VIDIOC_INT_S_AUDIO_ROUTING, &route);
 
        route.input = audio_input;
-       return cx18_i2c_hw(cx, cx->card->hw_audio_ctrl,
+       err = cx18_i2c_hw(cx, cx->card->hw_audio_ctrl,
                        VIDIOC_INT_S_AUDIO_ROUTING, &route);
+       if (err)
+               return err;
+
+       val = read_reg(CX18_AUDIO_ENABLE) & ~0x30;
+       val |= (audio_input > CX18_AV_AUDIO_SERIAL2) ? 0x20 :
+                                       (audio_input << 4);
+       write_reg(val | 0xb00, CX18_AUDIO_ENABLE);
+       cx18_vapi(cx, CX18_APU_RESETAI, 1, 0);
+       return 0;
 }
 
 void cx18_audio_set_route(struct cx18 *cx, struct v4l2_routing *route)
index 2dc3a5dd170e5d91b94921ea15cecb58e7b18183..c40a286de1b97a261542662fc114606a598f8f2d 100644 (file)
@@ -34,7 +34,7 @@ static int set_audclk_freq(struct cx18 *cx, u32 freq)
        /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x10 */
        cx18_av_write(cx, 0x127, 0x50);
 
-       if (state->aud_input != CX18_AV_AUDIO_SERIAL) {
+       if (state->aud_input > CX18_AV_AUDIO_SERIAL2) {
                switch (freq) {
                case 32000:
                        /* VID_PLL and AUX_PLL */
@@ -148,7 +148,7 @@ void cx18_av_audio_set_path(struct cx18 *cx)
        /* Mute everything to prevent the PFFT! */
        cx18_av_write(cx, 0x8d3, 0x1f);
 
-       if (state->aud_input == CX18_AV_AUDIO_SERIAL) {
+       if (state->aud_input <= CX18_AV_AUDIO_SERIAL2) {
                /* Set Path1 to Serial Audio Input */
                cx18_av_write4(cx, 0x8d0, 0x01011012);
 
@@ -165,7 +165,7 @@ void cx18_av_audio_set_path(struct cx18 *cx)
        /* deassert soft reset */
        cx18_av_and_or(cx, 0x810, ~0x1, 0x00);
 
-       if (state->aud_input != CX18_AV_AUDIO_SERIAL) {
+       if (state->aud_input > CX18_AV_AUDIO_SERIAL2) {
                /* When the microcontroller detects the
                 * audio format, it will unmute the lines */
                cx18_av_and_or(cx, 0x803, ~0x10, 0x10);
@@ -271,7 +271,7 @@ static void set_mute(struct cx18 *cx, int mute)
 {
        struct cx18_av_state *state = &cx->av_state;
 
-       if (state->aud_input != CX18_AV_AUDIO_SERIAL) {
+       if (state->aud_input > CX18_AV_AUDIO_SERIAL2) {
                /* Must turn off microcontroller in order to mute sound.
                 * Not sure if this is the best method, but it does work.
                 * If the microcontroller is running, then it will undo any
@@ -298,14 +298,14 @@ int cx18_av_audio(struct cx18 *cx, unsigned int cmd, void *arg)
 
        switch (cmd) {
        case VIDIOC_INT_AUDIO_CLOCK_FREQ:
-               if (state->aud_input != CX18_AV_AUDIO_SERIAL) {
+               if (state->aud_input > CX18_AV_AUDIO_SERIAL2) {
                        cx18_av_and_or(cx, 0x803, ~0x10, 0);
                        cx18_av_write(cx, 0x8d3, 0x1f);
                }
                cx18_av_and_or(cx, 0x810, ~0x1, 1);
                retval = set_audclk_freq(cx, *(u32 *)arg);
                cx18_av_and_or(cx, 0x810, ~0x1, 0);
-               if (state->aud_input != CX18_AV_AUDIO_SERIAL)
+               if (state->aud_input > CX18_AV_AUDIO_SERIAL2)
                        cx18_av_and_or(cx, 0x803, ~0x10, 0x10);
                return retval;
 
index faca43eb940f7aa17bfbb552c038e0ad2ecd07f3..3b0a2c450605e84415289a1cc21e667f0ba7b531 100644 (file)
@@ -69,58 +69,6 @@ int cx18_av_and_or4(struct cx18 *cx, u16 addr, u32 and_mask,
                             or_value);
 }
 
-int cx18_av_write_no_acfg(struct cx18 *cx, u16 addr, u8 value, int no_acfg_mask)
-{
-       int retval;
-       u32 saved_reg[8] = {0};
-
-       if (no_acfg_mask & CXADEC_NO_ACFG_AFE) {
-               saved_reg[0] = cx18_av_read4(cx, CXADEC_CHIP_CTRL);
-               saved_reg[1] = cx18_av_read4(cx, CXADEC_AFE_CTRL);
-       }
-
-       if (no_acfg_mask & CXADEC_NO_ACFG_PLL) {
-               saved_reg[2] = cx18_av_read4(cx, CXADEC_PLL_CTRL1);
-               saved_reg[3] = cx18_av_read4(cx, CXADEC_VID_PLL_FRAC);
-       }
-
-       if (no_acfg_mask & CXADEC_NO_ACFG_VID) {
-               saved_reg[4] = cx18_av_read4(cx, CXADEC_HORIZ_TIM_CTRL);
-               saved_reg[5] = cx18_av_read4(cx, CXADEC_VERT_TIM_CTRL);
-               saved_reg[6] = cx18_av_read4(cx, CXADEC_SRC_COMB_CFG);
-               saved_reg[7] = cx18_av_read4(cx, CXADEC_CHROMA_VBIOFF_CFG);
-       }
-
-       retval = cx18_av_write(cx, addr, value);
-
-       if (no_acfg_mask & CXADEC_NO_ACFG_AFE) {
-               cx18_av_write4(cx, CXADEC_CHIP_CTRL, saved_reg[0]);
-               cx18_av_write4(cx, CXADEC_AFE_CTRL,  saved_reg[1]);
-       }
-
-       if (no_acfg_mask & CXADEC_NO_ACFG_PLL) {
-               cx18_av_write4(cx, CXADEC_PLL_CTRL1,    saved_reg[2]);
-               cx18_av_write4(cx, CXADEC_VID_PLL_FRAC, saved_reg[3]);
-       }
-
-       if (no_acfg_mask & CXADEC_NO_ACFG_VID) {
-               cx18_av_write4(cx, CXADEC_HORIZ_TIM_CTRL,    saved_reg[4]);
-               cx18_av_write4(cx, CXADEC_VERT_TIM_CTRL,     saved_reg[5]);
-               cx18_av_write4(cx, CXADEC_SRC_COMB_CFG,      saved_reg[6]);
-               cx18_av_write4(cx, CXADEC_CHROMA_VBIOFF_CFG, saved_reg[7]);
-       }
-
-       return retval;
-}
-
-int cx18_av_and_or_no_acfg(struct cx18 *cx, u16 addr, unsigned and_mask,
-                          u8 or_value, int no_acfg_mask)
-{
-       return cx18_av_write_no_acfg(cx, addr,
-                                    (cx18_av_read(cx, addr) & and_mask) |
-                                    or_value, no_acfg_mask);
-}
-
 /* ----------------------------------------------------------------------- */
 
 static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input,
@@ -132,6 +80,7 @@ static void log_video_status(struct cx18 *cx);
 
 static void cx18_av_initialize(struct cx18 *cx)
 {
+       struct cx18_av_state *state = &cx->av_state;
        u32 v;
 
        cx18_av_loadfw(cx);
@@ -211,6 +160,149 @@ static void cx18_av_initialize(struct cx18 *cx)
 /*             CxDevWrReg(CXADEC_SRC_COMB_CFG, 0x6628021F); */
 /*    } */
        cx18_av_write4(cx, CXADEC_SRC_COMB_CFG, 0x6628021F);
+       state->default_volume = 228 - cx18_av_read(cx, 0x8d4);
+       state->default_volume = ((state->default_volume / 2) + 23) << 9;
+}
+
+/* ----------------------------------------------------------------------- */
+
+void cx18_av_std_setup(struct cx18 *cx)
+{
+       struct cx18_av_state *state = &cx->av_state;
+       v4l2_std_id std = state->std;
+       int hblank, hactive, burst, vblank, vactive, sc;
+       int vblank656, src_decimation;
+       int luma_lpf, uv_lpf, comb;
+       u32 pll_int, pll_frac, pll_post;
+
+       /* datasheet startup, step 8d */
+       if (std & ~V4L2_STD_NTSC)
+               cx18_av_write(cx, 0x49f, 0x11);
+       else
+               cx18_av_write(cx, 0x49f, 0x14);
+
+       if (std & V4L2_STD_625_50) {
+               hblank = 132;
+               hactive = 720;
+               burst = 93;
+               vblank = 36;
+               vactive = 580;
+               vblank656 = 40;
+               src_decimation = 0x21f;
+
+               luma_lpf = 2;
+               if (std & V4L2_STD_PAL) {
+                       uv_lpf = 1;
+                       comb = 0x20;
+                       sc = 688739;
+               } else if (std == V4L2_STD_PAL_Nc) {
+                       uv_lpf = 1;
+                       comb = 0x20;
+                       sc = 556453;
+               } else { /* SECAM */
+                       uv_lpf = 0;
+                       comb = 0;
+                       sc = 672351;
+               }
+       } else {
+               hactive = 720;
+               hblank = 122;
+               vactive = 487;
+               luma_lpf = 1;
+               uv_lpf = 1;
+               vblank = 26;
+               vblank656 = 26;
+
+               src_decimation = 0x21f;
+               if (std == V4L2_STD_PAL_60) {
+                       burst = 0x5b;
+                       luma_lpf = 2;
+                       comb = 0x20;
+                       sc = 688739;
+               } else if (std == V4L2_STD_PAL_M) {
+                       burst = 0x61;
+                       comb = 0x20;
+                       sc = 555452;
+               } else {
+                       burst = 0x5b;
+                       comb = 0x66;
+                       sc = 556063;
+               }
+       }
+
+       /* DEBUG: Displays configured PLL frequency */
+       pll_int = cx18_av_read(cx, 0x108);
+       pll_frac = cx18_av_read4(cx, 0x10c) & 0x1ffffff;
+       pll_post = cx18_av_read(cx, 0x109);
+       CX18_DEBUG_INFO("PLL regs = int: %u, frac: %u, post: %u\n",
+                       pll_int, pll_frac, pll_post);
+
+       if (pll_post) {
+               int fin, fsc;
+               int pll = 28636363L * ((((u64)pll_int) << 25) + pll_frac);
+
+               pll >>= 25;
+               pll /= pll_post;
+               CX18_DEBUG_INFO("PLL = %d.%06d MHz\n",
+                                       pll / 1000000, pll % 1000000);
+               CX18_DEBUG_INFO("PLL/8 = %d.%06d MHz\n",
+                                       pll / 8000000, (pll / 8) % 1000000);
+
+               fin = ((u64)src_decimation * pll) >> 12;
+               CX18_DEBUG_INFO("ADC Sampling freq = %d.%06d MHz\n",
+                                       fin / 1000000, fin % 1000000);
+
+               fsc = (((u64)sc) * pll) >> 24L;
+               CX18_DEBUG_INFO("Chroma sub-carrier freq = %d.%06d MHz\n",
+                                       fsc / 1000000, fsc % 1000000);
+
+               CX18_DEBUG_INFO("hblank %i, hactive %i, "
+                       "vblank %i , vactive %i, vblank656 %i, src_dec %i,"
+                       "burst 0x%02x, luma_lpf %i, uv_lpf %i, comb 0x%02x,"
+                       " sc 0x%06x\n",
+                       hblank, hactive, vblank, vactive, vblank656,
+                       src_decimation, burst, luma_lpf, uv_lpf, comb, sc);
+       }
+
+       /* Sets horizontal blanking delay and active lines */
+       cx18_av_write(cx, 0x470, hblank);
+       cx18_av_write(cx, 0x471, 0xff & (((hblank >> 8) & 0x3) |
+                                               (hactive << 4)));
+       cx18_av_write(cx, 0x472, hactive >> 4);
+
+       /* Sets burst gate delay */
+       cx18_av_write(cx, 0x473, burst);
+
+       /* Sets vertical blanking delay and active duration */
+       cx18_av_write(cx, 0x474, vblank);
+       cx18_av_write(cx, 0x475, 0xff & (((vblank >> 8) & 0x3) |
+                                               (vactive << 4)));
+       cx18_av_write(cx, 0x476, vactive >> 4);
+       cx18_av_write(cx, 0x477, vblank656);
+
+       /* Sets src decimation rate */
+       cx18_av_write(cx, 0x478, 0xff & src_decimation);
+       cx18_av_write(cx, 0x479, 0xff & (src_decimation >> 8));
+
+       /* Sets Luma and UV Low pass filters */
+       cx18_av_write(cx, 0x47a, luma_lpf << 6 | ((uv_lpf << 4) & 0x30));
+
+       /* Enables comb filters */
+       cx18_av_write(cx, 0x47b, comb);
+
+       /* Sets SC Step*/
+       cx18_av_write(cx, 0x47c, sc);
+       cx18_av_write(cx, 0x47d, 0xff & sc >> 8);
+       cx18_av_write(cx, 0x47e, 0xff & sc >> 16);
+
+       /* Sets VBI parameters */
+       if (std & V4L2_STD_625_50) {
+               cx18_av_write(cx, 0x47f, 0x01);
+               state->vbi_line_offset = 5;
+       } else {
+               cx18_av_write(cx, 0x47f, 0x00);
+               state->vbi_line_offset = 8;
+       }
 }
 
 /* ----------------------------------------------------------------------- */
@@ -221,16 +313,9 @@ static void input_change(struct cx18 *cx)
        v4l2_std_id std = state->std;
 
        /* Follow step 8c and 8d of section 3.16 in the cx18_av datasheet */
-       if (std & V4L2_STD_SECAM)
-               cx18_av_write_no_acfg(cx, 0x402, 0, CXADEC_NO_ACFG_ALL);
-       else {
-               cx18_av_write_no_acfg(cx, 0x402, 0x04, CXADEC_NO_ACFG_ALL);
-               cx18_av_write(cx, 0x49f, (std & V4L2_STD_NTSC) ? 0x14 : 0x11);
-       }
-       cx18_av_and_or_no_acfg(cx, 0x401, ~0x60, 0,
-                               CXADEC_NO_ACFG_PLL | CXADEC_NO_ACFG_VID);
-       cx18_av_and_or_no_acfg(cx, 0x401, ~0x60, 0x60,
-                               CXADEC_NO_ACFG_PLL | CXADEC_NO_ACFG_VID);
+       cx18_av_write(cx, 0x49f, (std & V4L2_STD_NTSC) ? 0x14 : 0x11);
+       cx18_av_and_or(cx, 0x401, ~0x60, 0);
+       cx18_av_and_or(cx, 0x401, ~0x60, 0x60);
 
        if (std & V4L2_STD_525_60) {
                if (std == V4L2_STD_NTSC_M_JP) {
@@ -300,7 +385,8 @@ static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input,
        }
 
        switch (aud_input) {
-       case CX18_AV_AUDIO_SERIAL:
+       case CX18_AV_AUDIO_SERIAL1:
+       case CX18_AV_AUDIO_SERIAL2:
                /* do nothing, use serial audio input */
                break;
        case CX18_AV_AUDIO4: reg &= ~0x30; break;
@@ -316,8 +402,7 @@ static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input,
 
        cx18_av_write(cx, 0x103, reg);
        /* Set INPUT_MODE to Composite (0) or S-Video (1) */
-       cx18_av_and_or_no_acfg(cx, 0x401, ~0x6, is_composite ? 0 : 0x02,
-                               CXADEC_NO_ACFG_PLL | CXADEC_NO_ACFG_VID);
+       cx18_av_and_or(cx, 0x401, ~0x6, is_composite ? 0 : 0x02);
        /* Set CH_SEL_ADC2 to 1 if input comes from CH3 */
        cx18_av_and_or(cx, 0x102, ~0x2, (reg & 0x80) == 0 ? 2 : 0);
        /* Set DUAL_MODE_ADC2 to 1 if input comes from both CH2 and CH3 */
@@ -373,13 +458,13 @@ static int set_v4lstd(struct cx18 *cx)
           This happens for example with the Yuan MPC622. */
        if (fmt >= 4 && fmt < 8) {
                /* Set format to NTSC-M */
-               cx18_av_and_or_no_acfg(cx, 0x400, ~0xf, 1, CXADEC_NO_ACFG_AFE);
+               cx18_av_and_or(cx, 0x400, ~0xf, 1);
                /* Turn off LCOMB */
                cx18_av_and_or(cx, 0x47b, ~6, 0);
        }
-       cx18_av_and_or_no_acfg(cx, 0x400, ~0xf, fmt, CXADEC_NO_ACFG_AFE);
-       cx18_av_and_or_no_acfg(cx, 0x403, ~0x3, pal_m, CXADEC_NO_ACFG_ALL);
-       cx18_av_vbi_setup(cx);
+       cx18_av_and_or(cx, 0x400, ~0x2f, fmt | 0x20);
+       cx18_av_and_or(cx, 0x403, ~0x3, pal_m);
+       cx18_av_std_setup(cx);
        input_change(cx);
        return 0;
 }
@@ -618,6 +703,8 @@ int cx18_av_cmd(struct cx18 *cx, unsigned int cmd, void *arg)
 
                switch (qc->id) {
                case V4L2_CID_AUDIO_VOLUME:
+                       return v4l2_ctrl_query_fill(qc, 0, 65535,
+                               65535 / 100, state->default_volume);
                case V4L2_CID_AUDIO_MUTE:
                case V4L2_CID_AUDIO_BALANCE:
                case V4L2_CID_AUDIO_BASS:
index c172823ce1d8801a93f384fbd5063057a1b6d2a3..eb61fa1e0965bbbcebccbcc0ee86ec4b9939ed55 100644 (file)
@@ -62,7 +62,8 @@ enum cx18_av_video_input {
 
 enum cx18_av_audio_input {
        /* Audio inputs: serial or In4-In8 */
-       CX18_AV_AUDIO_SERIAL,
+       CX18_AV_AUDIO_SERIAL1,
+       CX18_AV_AUDIO_SERIAL2,
        CX18_AV_AUDIO4 = 4,
        CX18_AV_AUDIO5,
        CX18_AV_AUDIO6,
@@ -78,6 +79,7 @@ struct cx18_av_state {
        u32 audclk_freq;
        int audmode;
        int vbi_line_offset;
+       int default_volume;
        u32 id;
        u32 rev;
        int is_initialized;
@@ -295,25 +297,16 @@ struct cx18_av_state {
 #define CXADEC_SELECT_AUDIO_STANDARD_FM    0xF9  /* FM radio */
 #define CXADEC_SELECT_AUDIO_STANDARD_AUTO  0xFF  /* Auto detect */
 
-/* Flags on what to preserve on write to 0x400-0x403 with cx18_av_.*_no_acfg()*/
-#define CXADEC_NO_ACFG_AFE     0x01 /* Preserve 0x100-0x107 */
-#define CXADEC_NO_ACFG_PLL     0x02 /* Preserve 0x108-0x10f */
-#define CXADEC_NO_ACFG_VID     0x04 /* Preserve 0x470-0x47f */
-#define CXADEC_NO_ACFG_ALL     0x07
-
 /* ----------------------------------------------------------------------- */
 /* cx18_av-core.c                                                         */
 int cx18_av_write(struct cx18 *cx, u16 addr, u8 value);
 int cx18_av_write4(struct cx18 *cx, u16 addr, u32 value);
-int cx18_av_write_no_acfg(struct cx18 *cx, u16 addr, u8 value,
-                               int no_acfg_mask);
 u8 cx18_av_read(struct cx18 *cx, u16 addr);
 u32 cx18_av_read4(struct cx18 *cx, u16 addr);
 int cx18_av_and_or(struct cx18 *cx, u16 addr, unsigned mask, u8 value);
 int cx18_av_and_or4(struct cx18 *cx, u16 addr, u32 mask, u32 value);
-int cx18_av_and_or_no_acfg(struct cx18 *cx, u16 addr, unsigned mask, u8 value,
-                               int no_acfg_mask);
 int cx18_av_cmd(struct cx18 *cx, unsigned int cmd, void *arg);
+void cx18_av_std_setup(struct cx18 *cx);
 
 /* ----------------------------------------------------------------------- */
 /* cx18_av-firmware.c                                                      */
@@ -326,7 +319,6 @@ void cx18_av_audio_set_path(struct cx18 *cx);
 
 /* ----------------------------------------------------------------------- */
 /* cx18_av-vbi.c                                                           */
-void cx18_av_vbi_setup(struct cx18 *cx);
 int cx18_av_vbi(struct cx18 *cx, unsigned int cmd, void *arg);
 
 #endif
index a1a6af6c1c8f5888a83432b35bbf4d5b69bac2fe..834b9248242ecc51e3526c526ccc3f153a45aca8 100644 (file)
@@ -22,6 +22,7 @@
 #include "cx18-driver.h"
 #include <linux/firmware.h>
 
+#define CX18_AUDIO_ENABLE 0xc72014
 #define FWFILE "v4l-cx23418-dig.fw"
 
 int cx18_av_loadfw(struct cx18 *cx)
@@ -31,40 +32,58 @@ int cx18_av_loadfw(struct cx18 *cx)
        u32 v;
        const u8 *ptr;
        int i;
+       int retries = 0;
 
        if (request_firmware(&fw, FWFILE, &cx->dev->dev) != 0) {
                CX18_ERR("unable to open firmware %s\n", FWFILE);
                return -EINVAL;
        }
 
-       cx18_av_write4(cx, CXADEC_CHIP_CTRL, 0x00010000);
-       cx18_av_write(cx, CXADEC_STD_DET_CTL, 0xf6); /* Byte 0 */
-
-       /* Reset the Mako core (Register is undocumented.) */
-       cx18_av_write4(cx, 0x8100, 0x00010000);
-
-       /* Put the 8051 in reset and enable firmware upload */
-       cx18_av_write4(cx, CXADEC_DL_CTL, 0x0F000000);
-
-       ptr = fw->data;
-       size = fw->size;
-
-       for (i = 0; i < size; i++) {
-               u32 dl_control = 0x0F000000 | ((u32)ptr[i] << 16);
-               u32 value = 0;
-               int retries;
-
-               for (retries = 0; retries < 5; retries++) {
-                       cx18_av_write4(cx, CXADEC_DL_CTL, dl_control);
-                       value = cx18_av_read4(cx, CXADEC_DL_CTL);
-                       if ((value & 0x3F00) == (dl_control & 0x3F00))
+       /* The firmware load often has byte errors, so allow for several
+          retries, both at byte level and at the firmware load level. */
+       while (retries < 5) {
+               cx18_av_write4(cx, CXADEC_CHIP_CTRL, 0x00010000);
+               cx18_av_write(cx, CXADEC_STD_DET_CTL, 0xf6);
+
+               /* Reset the Mako core (Register is undocumented.) */
+               cx18_av_write4(cx, 0x8100, 0x00010000);
+
+               /* Put the 8051 in reset and enable firmware upload */
+               cx18_av_write4(cx, CXADEC_DL_CTL, 0x0F000000);
+
+               ptr = fw->data;
+               size = fw->size;
+
+               for (i = 0; i < size; i++) {
+                       u32 dl_control = 0x0F000000 | i | ((u32)ptr[i] << 16);
+                       u32 value = 0;
+                       int retries;
+
+                       for (retries = 0; retries < 5; retries++) {
+                               cx18_av_write4(cx, CXADEC_DL_CTL, dl_control);
+                               udelay(10);
+                               value = cx18_av_read4(cx, CXADEC_DL_CTL);
+                               if (value == dl_control)
+                                       break;
+                               /* Check if we can correct the byte by changing
+                                  the address.  We can only write the lower
+                                  address byte of the address. */
+                               if ((value & 0x3F00) != (dl_control & 0x3F00)) {
+                                       retries = 5;
+                                       break;
+                               }
+                       }
+                       if (retries >= 5)
                                break;
                }
-               if (retries >= 5) {
-                       CX18_ERR("unable to load firmware %s\n", FWFILE);
-                       release_firmware(fw);
-                       return -EIO;
-               }
+               if (i == size)
+                       break;
+               retries++;
+       }
+       if (retries >= 5) {
+               CX18_ERR("unable to load firmware %s\n", FWFILE);
+               release_firmware(fw);
+               return -EIO;
        }
 
        cx18_av_write4(cx, CXADEC_DL_CTL, 0x13000000 | fw->size);
@@ -100,7 +119,6 @@ int cx18_av_loadfw(struct cx18 *cx)
           have a name in the spec. */
        cx18_av_write4(cx, 0x09CC, 1);
 
-#define CX18_AUDIO_ENABLE              0xc72014
        v = read_reg(CX18_AUDIO_ENABLE);
        /* If bit 11 is 1 */
        if (v & 0x800)
index d09f1daf4ebff978c3cb5401d912aa584542556e..02fdf57bb678f1faf5ed0f1671ee435bd1db252b 100644 (file)
@@ -83,150 +83,6 @@ static int decode_vps(u8 *dst, u8 *p)
        return err & 0xf0;
 }
 
-void cx18_av_vbi_setup(struct cx18 *cx)
-{
-       struct cx18_av_state *state = &cx->av_state;
-       v4l2_std_id std = state->std;
-       int hblank, hactive, burst, vblank, vactive, sc;
-       int vblank656, src_decimation;
-       int luma_lpf, uv_lpf, comb;
-       u32 pll_int, pll_frac, pll_post;
-
-       /* datasheet startup, step 8d */
-       if (std & ~V4L2_STD_NTSC)
-               cx18_av_write(cx, 0x49f, 0x11);
-       else
-               cx18_av_write(cx, 0x49f, 0x14);
-
-       if (std & V4L2_STD_625_50) {
-               hblank = 0x084;
-               hactive = 0x2d0;
-               burst = 0x5d;
-               vblank = 0x024;
-               vactive = 0x244;
-               vblank656 = 0x28;
-               src_decimation = 0x21f;
-
-               luma_lpf = 2;
-               if (std & V4L2_STD_SECAM) {
-                       uv_lpf = 0;
-                       comb = 0;
-                       sc = 0x0a425f;
-               } else if (std == V4L2_STD_PAL_Nc) {
-                       uv_lpf = 1;
-                       comb = 0x20;
-                       sc = 556453;
-               } else {
-                       uv_lpf = 1;
-                       comb = 0x20;
-                       sc = 0x0a8263;
-               }
-       } else {
-               hactive = 720;
-               hblank = 122;
-               vactive = 487;
-               luma_lpf = 1;
-               uv_lpf = 1;
-
-               src_decimation = 0x21f;
-               if (std == V4L2_STD_PAL_60) {
-                       vblank = 26;
-                       vblank656 = 26;
-                       burst = 0x5b;
-                       luma_lpf = 2;
-                       comb = 0x20;
-                       sc = 0x0a8263;
-               } else if (std == V4L2_STD_PAL_M) {
-                       vblank = 20;
-                       vblank656 = 24;
-                       burst = 0x61;
-                       comb = 0x20;
-
-                       sc = 555452;
-               } else {
-                       vblank = 26;
-                       vblank656 = 26;
-                       burst = 0x5b;
-                       comb = 0x66;
-                       sc = 556063;
-               }
-       }
-
-       /* DEBUG: Displays configured PLL frequency */
-       pll_int = cx18_av_read(cx, 0x108);
-       pll_frac = cx18_av_read4(cx, 0x10c) & 0x1ffffff;
-       pll_post = cx18_av_read(cx, 0x109);
-       CX18_DEBUG_INFO("PLL regs = int: %u, frac: %u, post: %u\n",
-                       pll_int, pll_frac, pll_post);
-
-       if (pll_post) {
-               int fin, fsc;
-               int pll = 28636363L * ((((u64)pll_int) << 25) + pll_frac);
-
-               pll >>= 25;
-               pll /= pll_post;
-               CX18_DEBUG_INFO("PLL = %d.%06d MHz\n",
-                                       pll / 1000000, pll % 1000000);
-               CX18_DEBUG_INFO("PLL/8 = %d.%06d MHz\n",
-                                       pll / 8000000, (pll / 8) % 1000000);
-
-               fin = ((u64)src_decimation * pll) >> 12;
-               CX18_DEBUG_INFO("ADC Sampling freq = %d.%06d MHz\n",
-                                       fin / 1000000, fin % 1000000);
-
-               fsc = (((u64)sc) * pll) >> 24L;
-               CX18_DEBUG_INFO("Chroma sub-carrier freq = %d.%06d MHz\n",
-                                       fsc / 1000000, fsc % 1000000);
-
-               CX18_DEBUG_INFO("hblank %i, hactive %i, "
-                       "vblank %i , vactive %i, vblank656 %i, src_dec %i,"
-                       "burst 0x%02x, luma_lpf %i, uv_lpf %i, comb 0x%02x,"
-                       " sc 0x%06x\n",
-                       hblank, hactive, vblank, vactive, vblank656,
-                       src_decimation, burst, luma_lpf, uv_lpf, comb, sc);
-       }
-
-       /* Sets horizontal blanking delay and active lines */
-       cx18_av_write(cx, 0x470, hblank);
-       cx18_av_write(cx, 0x471, 0xff & (((hblank >> 8) & 0x3) |
-                                               (hactive << 4)));
-       cx18_av_write(cx, 0x472, hactive >> 4);
-
-       /* Sets burst gate delay */
-       cx18_av_write(cx, 0x473, burst);
-
-       /* Sets vertical blanking delay and active duration */
-       cx18_av_write(cx, 0x474, vblank);
-       cx18_av_write(cx, 0x475, 0xff & (((vblank >> 8) & 0x3) |
-                                               (vactive << 4)));
-       cx18_av_write(cx, 0x476, vactive >> 4);
-       cx18_av_write(cx, 0x477, vblank656);
-
-       /* Sets src decimation rate */
-       cx18_av_write(cx, 0x478, 0xff & src_decimation);
-       cx18_av_write(cx, 0x479, 0xff & (src_decimation >> 8));
-
-       /* Sets Luma and UV Low pass filters */
-       cx18_av_write(cx, 0x47a, luma_lpf << 6 | ((uv_lpf << 4) & 0x30));
-
-       /* Enables comb filters */
-       cx18_av_write(cx, 0x47b, comb);
-
-       /* Sets SC Step*/
-       cx18_av_write(cx, 0x47c, sc);
-       cx18_av_write(cx, 0x47d, 0xff & sc >> 8);
-       cx18_av_write(cx, 0x47e, 0xff & sc >> 16);
-
-       /* Sets VBI parameters */
-       if (std & V4L2_STD_625_50) {
-               cx18_av_write(cx, 0x47f, 0x01);
-               state->vbi_line_offset = 5;
-       } else {
-               cx18_av_write(cx, 0x47f, 0x00);
-               state->vbi_line_offset = 8;
-       }
-}
-
 int cx18_av_vbi(struct cx18 *cx, unsigned int cmd, void *arg)
 {
        struct cx18_av_state *state = &cx->av_state;
@@ -292,8 +148,8 @@ int cx18_av_vbi(struct cx18 *cx, unsigned int cmd, void *arg)
                        /* raw VBI */
                        memset(svbi, 0, sizeof(*svbi));
 
-                       /* Setup VBI */
-                       cx18_av_vbi_setup(cx);
+                       /* Setup standard */
+                       cx18_av_std_setup(cx);
 
                        /* VBI Offset */
                        cx18_av_write(cx, 0x47f, vbi_offset);
@@ -304,8 +160,8 @@ int cx18_av_vbi(struct cx18 *cx, unsigned int cmd, void *arg)
                for (x = 0; x <= 23; x++)
                        lcr[x] = 0x00;
 
-               /* Setup VBI */
-               cx18_av_vbi_setup(cx);
+               /* Setup standard */
+               cx18_av_std_setup(cx);
 
                /* Sliced VBI */
                cx18_av_write(cx, 0x404, 0x32); /* Ancillary data */
index c26e0ef5b07583927c5d5ddd80fee3c304557741..8fe5f38c4d7cf694a6a2c9a7738bfffd79324430 100644 (file)
@@ -27,6 +27,8 @@
 #include "cx18-i2c.h"
 #include <media/cs5345.h>
 
+#define V4L2_STD_PAL_SECAM (V4L2_STD_PAL|V4L2_STD_SECAM)
+
 /********************** card configuration *******************************/
 
 /* usual i2c tuner addresses to probe */
@@ -65,12 +67,12 @@ static const struct cx18_card cx18_card_hvr1600_esmt = {
                { CX18_CARD_INPUT_AUD_TUNER,
                  CX18_AV_AUDIO8, CS5345_IN_1 | CS5345_MCLK_1_5 },
                { CX18_CARD_INPUT_LINE_IN1,
-                 CX18_AV_AUDIO_SERIAL, CS5345_IN_2 },
+                 CX18_AV_AUDIO_SERIAL1, CS5345_IN_2 },
                { CX18_CARD_INPUT_LINE_IN2,
-                 CX18_AV_AUDIO_SERIAL, CS5345_IN_3 },
+                 CX18_AV_AUDIO_SERIAL1, CS5345_IN_3 },
        },
        .radio_input = { CX18_CARD_INPUT_AUD_TUNER,
-                        CX18_AV_AUDIO_SERIAL, CS5345_IN_4 },
+                        CX18_AV_AUDIO_SERIAL1, CS5345_IN_4 },
        .ddr = {
                /* ESMT M13S128324A-5B memory */
                .chip_config = 0x003,
@@ -86,6 +88,7 @@ static const struct cx18_card cx18_card_hvr1600_esmt = {
                .active_lo_mask = 0x3001,
                .msecs_asserted = 10,
                .msecs_recovery = 40,
+               .ir_reset_mask  = 0x0001,
        },
        .i2c = &cx18_i2c_std,
 };
@@ -110,12 +113,12 @@ static const struct cx18_card cx18_card_hvr1600_samsung = {
                { CX18_CARD_INPUT_AUD_TUNER,
                  CX18_AV_AUDIO8, CS5345_IN_1 | CS5345_MCLK_1_5 },
                { CX18_CARD_INPUT_LINE_IN1,
-                 CX18_AV_AUDIO_SERIAL, CS5345_IN_2 },
+                 CX18_AV_AUDIO_SERIAL1, CS5345_IN_2 },
                { CX18_CARD_INPUT_LINE_IN2,
-                 CX18_AV_AUDIO_SERIAL, CS5345_IN_3 },
+                 CX18_AV_AUDIO_SERIAL1, CS5345_IN_3 },
        },
        .radio_input = { CX18_CARD_INPUT_AUD_TUNER,
-                        CX18_AV_AUDIO_SERIAL, CS5345_IN_4 },
+                        CX18_AV_AUDIO_SERIAL1, CS5345_IN_4 },
        .ddr = {
                /* Samsung K4D263238G-VC33 memory */
                .chip_config = 0x003,
@@ -131,6 +134,7 @@ static const struct cx18_card cx18_card_hvr1600_samsung = {
                .active_lo_mask = 0x3001,
                .msecs_asserted = 10,
                .msecs_recovery = 40,
+               .ir_reset_mask  = 0x0001,
        },
        .i2c = &cx18_i2c_std,
 };
@@ -161,10 +165,10 @@ static const struct cx18_card cx18_card_h900 = {
                { CX18_CARD_INPUT_AUD_TUNER,
                  CX18_AV_AUDIO8, 0 },
                { CX18_CARD_INPUT_LINE_IN1,
-                 CX18_AV_AUDIO_SERIAL, 0 },
+                 CX18_AV_AUDIO_SERIAL1, 0 },
        },
        .radio_input = { CX18_CARD_INPUT_AUD_TUNER,
-                        CX18_AV_AUDIO_SERIAL, 0 },
+                        CX18_AV_AUDIO_SERIAL1, 0 },
        .tuners = {
                { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 },
        },
@@ -194,7 +198,7 @@ static const struct cx18_card_pci_info cx18_pci_mpc718[] = {
 static const struct cx18_card cx18_card_mpc718 = {
        .type = CX18_CARD_YUAN_MPC718,
        .name = "Yuan MPC718",
-       .comment = "Some Composite and S-Video inputs are currently working.\n",
+       .comment = "Analog video capture works; some audio line in may not.\n",
        .v4l2_capabilities = CX18_CAP_ENCODER,
        .hw_audio_ctrl = CX18_HW_CX23418,
        .hw_all = CX18_HW_TUNER,
@@ -209,11 +213,11 @@ static const struct cx18_card cx18_card_mpc718 = {
                { CX18_CARD_INPUT_COMPOSITE3, 2, CX18_AV_COMPOSITE3 },
        },
        .audio_inputs = {
-               { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5,       0 },
-               { CX18_CARD_INPUT_LINE_IN1,  CX18_AV_AUDIO_SERIAL, 0 },
-               { CX18_CARD_INPUT_LINE_IN2,  CX18_AV_AUDIO_SERIAL, 0 },
+               { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5,        0 },
+               { CX18_CARD_INPUT_LINE_IN1,  CX18_AV_AUDIO_SERIAL1, 0 },
+               { CX18_CARD_INPUT_LINE_IN2,  CX18_AV_AUDIO_SERIAL1, 0 },
        },
-       .radio_input = { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO_SERIAL, 0 },
+       .radio_input = { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO_SERIAL1, 0 },
        .tuners = {
                /* XC3028 tuner */
                { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 },
@@ -227,16 +231,73 @@ static const struct cx18_card cx18_card_mpc718 = {
                .tune_lane = 0,
                .initial_emrs = 2,
        },
-       .xceive_pin = 15,
+       .xceive_pin = 0,
        .pci_list = cx18_pci_mpc718,
        .i2c = &cx18_i2c_std,
 };
 
+/* ------------------------------------------------------------------------- */
+
+/* Conexant Raptor PAL/SECAM: note that this card is analog only! */
+
+static const struct cx18_card_pci_info cx18_pci_cnxt_raptor_pal[] = {
+       { PCI_DEVICE_ID_CX23418, CX18_PCI_ID_CONEXANT, 0x0009 },
+       { 0, 0, 0 }
+};
+
+static const struct cx18_card cx18_card_cnxt_raptor_pal = {
+       .type = CX18_CARD_CNXT_RAPTOR_PAL,
+       .name = "Conexant Raptor PAL/SECAM",
+       .comment = "VBI is not yet supported\n",
+       .v4l2_capabilities = CX18_CAP_ENCODER,
+       .hw_audio_ctrl = CX18_HW_CX23418,
+       .hw_muxer = CX18_HW_GPIO,
+       .hw_all = CX18_HW_TUNER | CX18_HW_GPIO,
+       .video_inputs = {
+               { CX18_CARD_INPUT_VID_TUNER,  0, CX18_AV_COMPOSITE2 },
+               { CX18_CARD_INPUT_SVIDEO1,    1,
+                       CX18_AV_SVIDEO_LUMA3 | CX18_AV_SVIDEO_CHROMA4 },
+               { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE1 },
+               { CX18_CARD_INPUT_SVIDEO2,    2,
+                       CX18_AV_SVIDEO_LUMA7 | CX18_AV_SVIDEO_CHROMA8 },
+               { CX18_CARD_INPUT_COMPOSITE2, 2, CX18_AV_COMPOSITE6 },
+       },
+       .audio_inputs = {
+               { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5,        0 },
+               { CX18_CARD_INPUT_LINE_IN1,  CX18_AV_AUDIO_SERIAL1, 1 },
+               { CX18_CARD_INPUT_LINE_IN2,  CX18_AV_AUDIO_SERIAL2, 1 },
+       },
+       .tuners = {
+               { .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
+       },
+       .radio_input = { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO_SERIAL1, 2 },
+       .ddr = {
+               /* MT 46V16M16 memory */
+               .chip_config = 0x50306,
+               .refresh = 0x753,
+               .timing1 = 0x33220953,
+               .timing2 = 0x09,
+               .tune_lane = 0,
+               .initial_emrs = 0,
+       },
+       .gpio_init.initial_value = 0x1002,
+       .gpio_init.direction = 0xf002,
+       .gpio_audio_input = { .mask   = 0xf002,
+                             .tuner  = 0x1002,   /* LED D1  Tuner AF  */
+                             .linein = 0x2000,   /* LED D2  Line In 1 */
+                             .radio  = 0x4002 }, /* LED D3  Tuner AF  */
+       .pci_list = cx18_pci_cnxt_raptor_pal,
+       .i2c = &cx18_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
 static const struct cx18_card *cx18_card_list[] = {
        &cx18_card_hvr1600_esmt,
        &cx18_card_hvr1600_samsung,
        &cx18_card_h900,
        &cx18_card_mpc718,
+       &cx18_card_cnxt_raptor_pal,
 };
 
 const struct cx18_card *cx18_get_card(u16 index)
index dc2dd945d4c3be4aefd2b324a6d4f368b8825c8c..32155f6e6fe41b35700d69870aff057010f3dcdb 100644 (file)
@@ -83,6 +83,14 @@ struct cx18_gpio_i2c_slave_reset {
        u32 active_hi_mask; /* GPIO outputs that reset i2c chips when high */
        int msecs_asserted; /* time period reset must remain asserted */
        int msecs_recovery; /* time after deassert for chips to be ready */
+       u32 ir_reset_mask;  /* GPIO to reset the Zilog Z8F0811 IR contoller */
+};
+
+struct cx18_gpio_audio_input {         /* select tuner/line in input */
+       u32 mask;               /* leave to 0 if not supported */
+       u32 tuner;
+       u32 linein;
+       u32 radio;
 };
 
 struct cx18_card_tuner {
@@ -123,6 +131,7 @@ struct cx18_card {
        u8 xceive_pin;          /* XCeive tuner GPIO reset pin */
        struct cx18_gpio_init            gpio_init;
        struct cx18_gpio_i2c_slave_reset gpio_i2c_slave_reset;
+       struct cx18_gpio_audio_input    gpio_audio_input;
 
        struct cx18_card_tuner tuners[CX18_CARD_MAX_TUNERS];
        struct cx18_card_tuner_i2c *i2c;
index 87cf41021665fdf2d4a862b79ce8853becdf0d7d..f46c7e5ed747c61c7f7b50ce58fb4aa09c01f58f 100644 (file)
@@ -51,12 +51,11 @@ static const u32 *ctrl_classes[] = {
        NULL
 };
 
-static int cx18_queryctrl(struct cx18 *cx, struct v4l2_queryctrl *qctrl)
+int cx18_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qctrl)
 {
+       struct cx18 *cx = ((struct cx18_open_id *)fh)->cx;
        const char *name;
 
-       CX18_DEBUG_IOCTL("VIDIOC_QUERYCTRL(%08x)\n", qctrl->id);
-
        qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id);
        if (qctrl->id == 0)
                return -EINVAL;
@@ -91,21 +90,35 @@ static int cx18_queryctrl(struct cx18 *cx, struct v4l2_queryctrl *qctrl)
        return 0;
 }
 
-static int cx18_querymenu(struct cx18 *cx, struct v4l2_querymenu *qmenu)
+int cx18_querymenu(struct file *file, void *fh, struct v4l2_querymenu *qmenu)
 {
+       struct cx18 *cx = ((struct cx18_open_id *)fh)->cx;
        struct v4l2_queryctrl qctrl;
 
        qctrl.id = qmenu->id;
-       cx18_queryctrl(cx, &qctrl);
-       return v4l2_ctrl_query_menu(qmenu, &qctrl, cx2341x_ctrl_get_menu(qmenu->id));
+       cx18_queryctrl(file, fh, &qctrl);
+       return v4l2_ctrl_query_menu(qmenu, &qctrl,
+                       cx2341x_ctrl_get_menu(&cx->params, qmenu->id));
 }
 
-static int cx18_s_ctrl(struct cx18 *cx, struct v4l2_control *vctrl)
+static int cx18_try_ctrl(struct file *file, void *fh,
+                                       struct v4l2_ext_control *vctrl)
 {
-       s32 v = vctrl->value;
-
-       CX18_DEBUG_IOCTL("VIDIOC_S_CTRL(%08x, %x)\n", vctrl->id, v);
+       struct v4l2_queryctrl qctrl;
+       const char **menu_items = NULL;
+       int err;
+
+       qctrl.id = vctrl->id;
+       err = cx18_queryctrl(file, fh, &qctrl);
+       if (err)
+               return err;
+       if (qctrl.type == V4L2_CTRL_TYPE_MENU)
+               menu_items = v4l2_ctrl_get_menu(qctrl.id);
+       return v4l2_ctrl_check(vctrl, &qctrl, menu_items);
+}
 
+static int cx18_s_ctrl(struct cx18 *cx, struct v4l2_control *vctrl)
+{
        switch (vctrl->id) {
                /* Standard V4L2 controls */
        case V4L2_CID_BRIGHTNESS:
@@ -123,7 +136,7 @@ static int cx18_s_ctrl(struct cx18 *cx, struct v4l2_control *vctrl)
                return cx18_i2c_hw(cx, cx->card->hw_audio_ctrl, VIDIOC_S_CTRL, vctrl);
 
        default:
-               CX18_DEBUG_IOCTL("invalid control %x\n", vctrl->id);
+               CX18_DEBUG_IOCTL("invalid control 0x%x\n", vctrl->id);
                return -EINVAL;
        }
        return 0;
@@ -131,8 +144,6 @@ static int cx18_s_ctrl(struct cx18 *cx, struct v4l2_control *vctrl)
 
 static int cx18_g_ctrl(struct cx18 *cx, struct v4l2_control *vctrl)
 {
-       CX18_DEBUG_IOCTL("VIDIOC_G_CTRL(%08x)\n", vctrl->id);
-
        switch (vctrl->id) {
                /* Standard V4L2 controls */
        case V4L2_CID_BRIGHTNESS:
@@ -149,7 +160,7 @@ static int cx18_g_ctrl(struct cx18 *cx, struct v4l2_control *vctrl)
        case V4L2_CID_AUDIO_LOUDNESS:
                return cx18_i2c_hw(cx, cx->card->hw_audio_ctrl, VIDIOC_G_CTRL, vctrl);
        default:
-               CX18_DEBUG_IOCTL("invalid control %x\n", vctrl->id);
+               CX18_DEBUG_IOCTL("invalid control 0x%x\n", vctrl->id);
                return -EINVAL;
        }
        return 0;
@@ -194,113 +205,110 @@ static int cx18_setup_vbi_fmt(struct cx18 *cx, enum v4l2_mpeg_stream_vbi_fmt fmt
        return 0;
 }
 
-int cx18_control_ioctls(struct cx18 *cx, unsigned int cmd, void *arg)
+int cx18_g_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c)
 {
+       struct cx18 *cx = ((struct cx18_open_id *)fh)->cx;
        struct v4l2_control ctrl;
 
-       switch (cmd) {
-       case VIDIOC_QUERYMENU:
-               CX18_DEBUG_IOCTL("VIDIOC_QUERYMENU\n");
-               return cx18_querymenu(cx, arg);
-
-       case VIDIOC_QUERYCTRL:
-               return cx18_queryctrl(cx, arg);
-
-       case VIDIOC_S_CTRL:
-               return cx18_s_ctrl(cx, arg);
-
-       case VIDIOC_G_CTRL:
-               return cx18_g_ctrl(cx, arg);
-
-       case VIDIOC_S_EXT_CTRLS:
-       {
-               struct v4l2_ext_controls *c = arg;
-
-               if (c->ctrl_class == V4L2_CTRL_CLASS_USER) {
-                       int i;
-                       int err = 0;
-
-                       for (i = 0; i < c->count; i++) {
-                               ctrl.id = c->controls[i].id;
-                               ctrl.value = c->controls[i].value;
-                               err = cx18_s_ctrl(cx, &ctrl);
-                               c->controls[i].value = ctrl.value;
-                               if (err) {
-                                       c->error_idx = i;
-                                       break;
-                               }
+       if (c->ctrl_class == V4L2_CTRL_CLASS_USER) {
+               int i;
+               int err = 0;
+
+               for (i = 0; i < c->count; i++) {
+                       ctrl.id = c->controls[i].id;
+                       ctrl.value = c->controls[i].value;
+                       err = cx18_g_ctrl(cx, &ctrl);
+                       c->controls[i].value = ctrl.value;
+                       if (err) {
+                               c->error_idx = i;
+                               break;
                        }
-                       return err;
                }
-               CX18_DEBUG_IOCTL("VIDIOC_S_EXT_CTRLS\n");
-               if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
-                       struct cx2341x_mpeg_params p = cx->params;
-                       int err = cx2341x_ext_ctrls(&p, atomic_read(&cx->ana_capturing), arg, cmd);
+               return err;
+       }
+       if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG)
+               return cx2341x_ext_ctrls(&cx->params, 0, c, VIDIOC_G_EXT_CTRLS);
+       return -EINVAL;
+}
 
-                       if (err)
-                               return err;
+int cx18_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c)
+{
+       struct cx18_open_id *id = fh;
+       struct cx18 *cx = id->cx;
+       int ret;
+       struct v4l2_control ctrl;
 
-                       if (p.video_encoding != cx->params.video_encoding) {
-                               int is_mpeg1 = p.video_encoding ==
-                                               V4L2_MPEG_VIDEO_ENCODING_MPEG_1;
-                               struct v4l2_format fmt;
+       ret = v4l2_prio_check(&cx->prio, &id->prio);
+       if (ret)
+               return ret;
 
-                               /* fix videodecoder resolution */
-                               fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-                               fmt.fmt.pix.width = cx->params.width / (is_mpeg1 ? 2 : 1);
-                               fmt.fmt.pix.height = cx->params.height;
-                               cx18_av_cmd(cx, VIDIOC_S_FMT, &fmt);
+       if (c->ctrl_class == V4L2_CTRL_CLASS_USER) {
+               int i;
+               int err = 0;
+
+               for (i = 0; i < c->count; i++) {
+                       ctrl.id = c->controls[i].id;
+                       ctrl.value = c->controls[i].value;
+                       err = cx18_s_ctrl(cx, &ctrl);
+                       c->controls[i].value = ctrl.value;
+                       if (err) {
+                               c->error_idx = i;
+                               break;
                        }
-                       err = cx2341x_update(cx, cx18_api_func, &cx->params, &p);
-                       if (!err && cx->params.stream_vbi_fmt != p.stream_vbi_fmt)
-                               err = cx18_setup_vbi_fmt(cx, p.stream_vbi_fmt);
-                       cx->params = p;
-                       cx->dualwatch_stereo_mode = p.audio_properties & 0x0300;
-                       cx18_audio_set_audio_clock_freq(cx, p.audio_properties & 0x03);
-                       return err;
                }
-               return -EINVAL;
+               return err;
        }
+       if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
+               struct cx2341x_mpeg_params p = cx->params;
+               int err = cx2341x_ext_ctrls(&p, atomic_read(&cx->ana_capturing),
+                                               c, VIDIOC_S_EXT_CTRLS);
 
-       case VIDIOC_G_EXT_CTRLS:
-       {
-               struct v4l2_ext_controls *c = arg;
-
-               if (c->ctrl_class == V4L2_CTRL_CLASS_USER) {
-                       int i;
-                       int err = 0;
-
-                       for (i = 0; i < c->count; i++) {
-                               ctrl.id = c->controls[i].id;
-                               ctrl.value = c->controls[i].value;
-                               err = cx18_g_ctrl(cx, &ctrl);
-                               c->controls[i].value = ctrl.value;
-                               if (err) {
-                                       c->error_idx = i;
-                                       break;
-                               }
-                       }
+               if (err)
                        return err;
+
+               if (p.video_encoding != cx->params.video_encoding) {
+                       int is_mpeg1 = p.video_encoding ==
+                                               V4L2_MPEG_VIDEO_ENCODING_MPEG_1;
+                       struct v4l2_format fmt;
+
+                       /* fix videodecoder resolution */
+                       fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+                       fmt.fmt.pix.width = cx->params.width
+                                               / (is_mpeg1 ? 2 : 1);
+                       fmt.fmt.pix.height = cx->params.height;
+                       cx18_av_cmd(cx, VIDIOC_S_FMT, &fmt);
                }
-               CX18_DEBUG_IOCTL("VIDIOC_G_EXT_CTRLS\n");
-               if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG)
-                       return cx2341x_ext_ctrls(&cx->params, 0, arg, cmd);
-               return -EINVAL;
+               err = cx2341x_update(cx, cx18_api_func, &cx->params, &p);
+               if (!err && cx->params.stream_vbi_fmt != p.stream_vbi_fmt)
+                       err = cx18_setup_vbi_fmt(cx, p.stream_vbi_fmt);
+               cx->params = p;
+               cx->dualwatch_stereo_mode = p.audio_properties & 0x0300;
+               cx18_audio_set_audio_clock_freq(cx, p.audio_properties & 0x03);
+               return err;
        }
+       return -EINVAL;
+}
 
-       case VIDIOC_TRY_EXT_CTRLS:
-       {
-               struct v4l2_ext_controls *c = arg;
+int cx18_try_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c)
+{
+       struct cx18 *cx = ((struct cx18_open_id *)fh)->cx;
 
-               CX18_DEBUG_IOCTL("VIDIOC_TRY_EXT_CTRLS\n");
-               if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG)
-                       return cx2341x_ext_ctrls(&cx->params,
-                                       atomic_read(&cx->ana_capturing), arg, cmd);
-               return -EINVAL;
-       }
+       if (c->ctrl_class == V4L2_CTRL_CLASS_USER) {
+               int i;
+               int err = 0;
 
-       default:
-               return -EINVAL;
+               for (i = 0; i < c->count; i++) {
+                       err = cx18_try_ctrl(file, fh, &c->controls[i]);
+                       if (err) {
+                               c->error_idx = i;
+                               break;
+                       }
+               }
+               return err;
        }
-       return 0;
+       if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG)
+               return cx2341x_ext_ctrls(&cx->params,
+                                               atomic_read(&cx->ana_capturing),
+                                               c, VIDIOC_TRY_EXT_CTRLS);
+       return -EINVAL;
 }
index 6e985cf422a09cf7181d2ee36544ba58edcb607c..e46323700b81ca358d10aafec1df1275d23346ba 100644 (file)
@@ -21,4 +21,9 @@
  *  02111-1307  USA
  */
 
-int cx18_control_ioctls(struct cx18 *cx, unsigned int cmd, void *arg);
+int cx18_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *a);
+int cx18_g_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *a);
+int cx18_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *a);
+int cx18_try_ext_ctrls(struct file *file, void *fh,
+                       struct v4l2_ext_controls *a);
+int cx18_querymenu(struct file *file, void *fh, struct v4l2_querymenu *a);
index 2b810bb2a4c70f8d37fc40b6ed5646d1ce5e308f..22434aadde316183afcf0165d659361befda60eb 100644 (file)
@@ -120,6 +120,7 @@ MODULE_PARM_DESC(cardtype,
                 "\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 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");
@@ -420,6 +421,7 @@ static int __devinit cx18_init_struct1(struct cx18 *cx)
        mutex_init(&cx->serialize_lock);
        mutex_init(&cx->i2c_bus_lock[0]);
        mutex_init(&cx->i2c_bus_lock[1]);
+       mutex_init(&cx->gpio_lock);
 
        spin_lock_init(&cx->lock);
        spin_lock_init(&cx->dma_reg_lock);
@@ -435,7 +437,7 @@ static int __devinit cx18_init_struct1(struct cx18 *cx)
                (cx->params.video_temporal_filter_mode << 1) |
                (cx->params.video_median_filter_type << 2);
        cx->params.port = CX2341X_PORT_MEMORY;
-       cx->params.capabilities = CX2341X_CAP_HAS_SLICED_VBI;
+       cx->params.capabilities = CX2341X_CAP_HAS_TS;
        init_waitqueue_head(&cx->cap_w);
        init_waitqueue_head(&cx->mb_apu_waitq);
        init_waitqueue_head(&cx->mb_cpu_waitq);
@@ -614,7 +616,7 @@ static int __devinit cx18_probe(struct pci_dev *dev,
        cx18_cards[cx18_cards_active] = cx;
        cx->dev = dev;
        cx->num = cx18_cards_active++;
-       snprintf(cx->name, sizeof(cx->name) - 1, "cx18-%d", cx->num);
+       snprintf(cx->name, sizeof(cx->name), "cx18-%d", cx->num);
        CX18_INFO("Initializing card #%d\n", cx->num);
 
        spin_unlock(&cx18_cards_lock);
@@ -721,6 +723,12 @@ static int __devinit cx18_probe(struct pci_dev *dev,
        /* 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)
@@ -818,6 +826,9 @@ 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;
+
+       fh.cx = cx;
 
        if (test_bit(CX18_F_I_FAILED, &cx->i_flags))
                return -ENXIO;
@@ -869,13 +880,13 @@ int cx18_init_on_first_open(struct cx18 *cx)
 
        video_input = cx->active_input;
        cx->active_input++;     /* Force update of input */
-       cx18_v4l2_ioctls(cx, NULL, VIDIOC_S_INPUT, &video_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 */
-       cx18_v4l2_ioctls(cx, NULL, VIDIOC_S_STD, &cx->tuner_std);
-       cx18_v4l2_ioctls(cx, NULL, VIDIOC_S_FREQUENCY, &vf);
+       cx18_s_std(NULL, &fh, &cx->tuner_std);
+       cx18_s_frequency(NULL, &fh, &vf);
        return 0;
 }
 
index de14ab59a206b6c84e88f07e53b0124a5b5a7281..45e31b04730e2c2780c9253d938cac4891a95e7b 100644 (file)
@@ -75,7 +75,8 @@
 #define CX18_CARD_HVR_1600_SAMSUNG    1        /* Hauppauge HVR 1600 (Samsung memory) */
 #define CX18_CARD_COMPRO_H900        2 /* Compro VideoMate H900 */
 #define CX18_CARD_YUAN_MPC718        3 /* Yuan MPC718 */
-#define CX18_CARD_LAST                       3
+#define CX18_CARD_CNXT_RAPTOR_PAL     4        /* Conexant Raptor PAL */
+#define CX18_CARD_LAST                       4
 
 #define CX18_ENC_STREAM_TYPE_MPG  0
 #define CX18_ENC_STREAM_TYPE_TS   1
@@ -94,6 +95,7 @@
 #define CX18_PCI_ID_HAUPPAUGE          0x0070
 #define CX18_PCI_ID_COMPRO             0x185b
 #define CX18_PCI_ID_YUAN               0x12ab
+#define CX18_PCI_ID_CONEXANT           0x14f1
 
 /* ======================================================================== */
 /* ========================== START USER SETTABLE DMA VARIABLES =========== */
@@ -228,9 +230,7 @@ struct cx18_dvb {
        struct dvb_net dvbnet;
        int enabled;
        int feeding;
-
        struct mutex feedlock;
-
 };
 
 struct cx18;    /* forward reference */
@@ -427,6 +427,7 @@ struct cx18 {
        /* gpio */
        u32 gpio_dir;
        u32 gpio_val;
+       struct mutex gpio_lock;
 
        /* v4l2 and User settings */
 
index 2694ce35063140a61d1f598bb7462c1adfb55b60..2d630d9f7496b8c2e6b8989f11f64d44903437ff 100644 (file)
@@ -41,9 +41,6 @@
 
 #define CX18_REG_BUS_TIMEOUT_EN        0xc72024
 
-#define CX18_AUDIO_ENABLE              0xc72014
-#define CX18_REG_BUS_TIMEOUT_EN        0xc72024
-
 #define CX18_FAST_CLOCK_PLL_INT        0xc78000
 #define CX18_FAST_CLOCK_PLL_FRAC       0xc78004
 #define CX18_FAST_CLOCK_PLL_POST       0xc78008
@@ -90,7 +87,7 @@
 #define CX18_DSP0_INTERRUPT_MASK       0xd0004C
 
 /* Encoder/decoder firmware sizes */
-#define CX18_FW_CPU_SIZE               (174716)
+#define CX18_FW_CPU_SIZE               (158332)
 #define CX18_FW_APU_SIZE               (141200)
 
 #define APU_ROM_SYNC1 0x6D676553 /* "mgeS" */
@@ -345,6 +342,11 @@ int cx18_firmware_init(struct cx18 *cx)
                int sz = load_apu_fw_direct("v4l-cx23418-apu.fw",
                               cx->enc_mem, cx, CX18_FW_APU_SIZE);
 
+               write_enc(0xE51FF004, 0);
+               write_enc(0xa00000, 4);  /* todo: not hardcoded */
+               write_reg(0x00010000, CX18_PROC_SOFT_RESET); /* Start APU */
+               cx18_msleep_timeout(500, 0);
+
                sz = sz <= 0 ? sz : load_cpu_fw_direct("v4l-cx23418-cpu.fw",
                                        cx->enc_mem, cx, CX18_FW_CPU_SIZE);
 
index b302833f6f9dd8d8250f4e716ed5fa9084d515d6..3d495dba4983d1662b2c2973ca97a1bb87b0ec54 100644 (file)
@@ -69,6 +69,7 @@ void cx18_reset_i2c_slaves_gpio(struct cx18 *cx)
        /* Assuming that the masks are a subset of the bits in gpio_dir */
 
        /* Assert */
+       mutex_lock(&cx->gpio_lock);
        cx->gpio_val =
                (cx->gpio_val | p->active_hi_mask) & ~(p->active_lo_mask);
        gpio_write(cx);
@@ -79,10 +80,53 @@ void cx18_reset_i2c_slaves_gpio(struct cx18 *cx)
                (cx->gpio_val | p->active_lo_mask) & ~(p->active_hi_mask);
        gpio_write(cx);
        schedule_timeout_uninterruptible(msecs_to_jiffies(p->msecs_recovery));
+       mutex_unlock(&cx->gpio_lock);
 }
 
+void cx18_reset_ir_gpio(void *data)
+{
+       struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx;
+       const struct cx18_gpio_i2c_slave_reset *p;
+
+       p = &cx->card->gpio_i2c_slave_reset;
+
+       if (p->ir_reset_mask == 0)
+               return;
+
+       CX18_DEBUG_INFO("Resetting IR microcontroller\n");
+
+       /*
+          Assert timing for the Z8F0811 on HVR-1600 boards:
+          1. Assert RESET for min of 4 clock cycles at 18.432 MHz to initiate
+          2. Reset then takes 66 WDT cycles at 10 kHz + 16 xtal clock cycles
+               (6,601,085 nanoseconds ~= 7 milliseconds)
+          3. DBG pin must be high before chip exits reset for normal operation.
+               DBG is open drain and hopefully pulled high since we don't
+               normally drive it (GPIO 1?) for the HVR-1600
+          4. Z8F0811 won't exit reset until RESET is deasserted
+       */
+       mutex_lock(&cx->gpio_lock);
+       cx->gpio_val = cx->gpio_val & ~p->ir_reset_mask;
+       gpio_write(cx);
+       mutex_unlock(&cx->gpio_lock);
+       schedule_timeout_uninterruptible(msecs_to_jiffies(p->msecs_asserted));
+
+       /*
+          Zilog comes out of reset, loads reset vector address and executes
+          from there. Required recovery delay unknown.
+       */
+       mutex_lock(&cx->gpio_lock);
+       cx->gpio_val = cx->gpio_val | p->ir_reset_mask;
+       gpio_write(cx);
+       mutex_unlock(&cx->gpio_lock);
+       schedule_timeout_uninterruptible(msecs_to_jiffies(p->msecs_recovery));
+}
+EXPORT_SYMBOL(cx18_reset_ir_gpio);
+/* This symbol is exported for use by an infrared module for the IR-blaster */
+
 void cx18_gpio_init(struct cx18 *cx)
 {
+       mutex_lock(&cx->gpio_lock);
        cx->gpio_dir = cx->card->gpio_init.direction;
        cx->gpio_val = cx->card->gpio_init.initial_value;
 
@@ -91,14 +135,17 @@ void cx18_gpio_init(struct cx18 *cx)
                cx->gpio_val |= 1 << cx->card->xceive_pin;
        }
 
-       if (cx->gpio_dir == 0)
+       if (cx->gpio_dir == 0) {
+               mutex_unlock(&cx->gpio_lock);
                return;
+       }
 
        CX18_DEBUG_INFO("GPIO initial dir: %08x/%08x out: %08x/%08x\n",
                   read_reg(CX18_REG_GPIO_DIR1), read_reg(CX18_REG_GPIO_DIR2),
                   read_reg(CX18_REG_GPIO_OUT1), read_reg(CX18_REG_GPIO_OUT2));
 
        gpio_write(cx);
+       mutex_unlock(&cx->gpio_lock);
 }
 
 /* Xceive tuner reset function */
@@ -112,13 +159,52 @@ int cx18_reset_tuner_gpio(void *dev, int cmd, int value)
                return 0;
        CX18_DEBUG_INFO("Resetting tuner\n");
 
+       mutex_lock(&cx->gpio_lock);
        cx->gpio_val &= ~(1 << cx->card->xceive_pin);
-
        gpio_write(cx);
+       mutex_unlock(&cx->gpio_lock);
        schedule_timeout_interruptible(msecs_to_jiffies(1));
 
+       mutex_lock(&cx->gpio_lock);
        cx->gpio_val |= 1 << cx->card->xceive_pin;
        gpio_write(cx);
+       mutex_unlock(&cx->gpio_lock);
        schedule_timeout_interruptible(msecs_to_jiffies(1));
        return 0;
 }
+
+int cx18_gpio(struct cx18 *cx, unsigned int command, void *arg)
+{
+       struct v4l2_routing *route = arg;
+       u32 mask, data;
+
+       switch (command) {
+       case VIDIOC_INT_S_AUDIO_ROUTING:
+               if (route->input > 2)
+                       return -EINVAL;
+               mask = cx->card->gpio_audio_input.mask;
+               switch (route->input) {
+               case 0:
+                       data = cx->card->gpio_audio_input.tuner;
+                       break;
+               case 1:
+                       data = cx->card->gpio_audio_input.linein;
+                       break;
+               case 2:
+               default:
+                       data = cx->card->gpio_audio_input.radio;
+                       break;
+               }
+               break;
+
+       default:
+               return -EINVAL;
+       }
+       if (mask) {
+               mutex_lock(&cx->gpio_lock);
+               cx->gpio_val = (cx->gpio_val & ~mask) | (data & mask);
+               gpio_write(cx);
+               mutex_unlock(&cx->gpio_lock);
+       }
+       return 0;
+}
index 525c328f748a3d0f677863bdc4fdde9ae4bdc0fb..22cd7ddf8554ba36dc6c8f1e97772ec17bb679d6 100644 (file)
@@ -22,4 +22,6 @@
 
 void cx18_gpio_init(struct cx18 *cx);
 void cx18_reset_i2c_slaves_gpio(struct cx18 *cx);
+void cx18_reset_ir_gpio(void *data);
 int cx18_reset_tuner_gpio(void *dev, int cmd, int value);
+int cx18_gpio(struct cx18 *cx, unsigned int command, void *arg);
index 680bc4e35b79a3c67d38e072af2471fb30e35e2d..6023ba3bd3a63d79d967fdbb86a2fa67611a391c 100644 (file)
 #define GETSCL_BIT      0x0004
 #define GETSDL_BIT      0x0008
 
-#ifndef I2C_ADAP_CLASS_TV_ANALOG
-#define I2C_ADAP_CLASS_TV_ANALOG I2C_CLASS_TV_ANALOG
-#endif
-
 #define CX18_CS5345_I2C_ADDR           0x4c
 
 /* This array should match the CX18_HW_ defines */
@@ -311,8 +307,12 @@ int cx18_i2c_hw(struct cx18 *cx, u32 hw, unsigned int cmd, void *arg)
 {
        int addr;
 
-       if (hw == CX18_HW_GPIO || hw == 0)
+       if (hw == 0)
                return 0;
+
+       if (hw == CX18_HW_GPIO)
+               return cx18_gpio(cx, cmd, arg);
+
        if (hw == CX18_HW_CX23418)
                return cx18_av_cmd(cx, cmd, arg);
 
@@ -350,6 +350,8 @@ void cx18_call_i2c_clients(struct cx18 *cx, unsigned int cmd, void *arg)
        cx18_av_cmd(cx, cmd, arg);
        i2c_clients_command(&cx->i2c_adap[0], cmd, arg);
        i2c_clients_command(&cx->i2c_adap[1], cmd, arg);
+       if (cx->hw_flags & CX18_HW_GPIO)
+               cx18_gpio(cx, cmd, arg);
 }
 
 /* init + register i2c algo-bit adapter */
@@ -358,6 +360,18 @@ int init_cx18_i2c(struct cx18 *cx)
        int i;
        CX18_DEBUG_I2C("i2c init\n");
 
+       /* Sanity checks for the I2C hardware arrays. They must be the
+        * same size and GPIO/CX23418 must be the last entries.
+        */
+       if (ARRAY_SIZE(hw_driverids) != ARRAY_SIZE(hw_addrs) ||
+           ARRAY_SIZE(hw_devicenames) != ARRAY_SIZE(hw_addrs) ||
+           CX18_HW_GPIO != (1 << (ARRAY_SIZE(hw_addrs) - 2)) ||
+           CX18_HW_CX23418 != (1 << (ARRAY_SIZE(hw_addrs) - 1)) ||
+           hw_driverids[ARRAY_SIZE(hw_addrs) - 1]) {
+               CX18_ERR("Mismatched I2C hardware arrays\n");
+               return -ENODEV;
+       }
+
        for (i = 0; i < 2; i++) {
                memcpy(&cx->i2c_adap[i], &cx18_i2c_adap_template,
                        sizeof(struct i2c_adapter));
@@ -391,6 +405,7 @@ int init_cx18_i2c(struct cx18 *cx)
        write_reg_sync(0x00c000c0, 0xc7001c);
        mdelay(10);
        write_reg_sync(0x00c00000, 0xc7001c);
+       mdelay(10);
 
        write_reg_sync(0x00c00000, 0xc730c8); /* Set to edge-triggered intrs. */
        write_reg_sync(0x00c00000, 0xc730c4); /* Clear any stale intrs */
index 4151f1e5493febdf9825440be9e0650cb0d4c09f..0d74e59e503eb58a47b7fcf53596f33bfa76a696 100644 (file)
@@ -100,19 +100,6 @@ void cx18_expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal)
        }
 }
 
-static int check_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal)
-{
-       int f, l;
-       u16 set = 0;
-
-       for (f = 0; f < 2; f++) {
-               for (l = 0; l < 24; l++) {
-                       fmt->service_lines[f][l] = select_service_from_set(f, l, fmt->service_lines[f][l], is_pal);
-                       set |= fmt->service_lines[f][l];
-               }
-       }
-       return set != 0;
-}
 
 u16 cx18_get_service_set(struct v4l2_sliced_vbi_format *fmt)
 {
@@ -126,35 +113,167 @@ u16 cx18_get_service_set(struct v4l2_sliced_vbi_format *fmt)
        return set;
 }
 
-static const struct {
-       v4l2_std_id  std;
-       char        *name;
-} enum_stds[] = {
-       { V4L2_STD_PAL_BG | V4L2_STD_PAL_H, "PAL-BGH" },
-       { V4L2_STD_PAL_DK,    "PAL-DK"    },
-       { V4L2_STD_PAL_I,     "PAL-I"     },
-       { V4L2_STD_PAL_M,     "PAL-M"     },
-       { V4L2_STD_PAL_N,     "PAL-N"     },
-       { V4L2_STD_PAL_Nc,    "PAL-Nc"    },
-       { V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H, "SECAM-BGH" },
-       { V4L2_STD_SECAM_DK,  "SECAM-DK"  },
-       { V4L2_STD_SECAM_L,   "SECAM-L"   },
-       { V4L2_STD_SECAM_LC,  "SECAM-L'"  },
-       { V4L2_STD_NTSC_M,    "NTSC-M"    },
-       { V4L2_STD_NTSC_M_JP, "NTSC-J"    },
-       { V4L2_STD_NTSC_M_KR, "NTSC-K"    },
-};
-
-static const struct v4l2_standard cx18_std_60hz = {
-       .frameperiod = {.numerator = 1001, .denominator = 30000},
-       .framelines = 525,
-};
-
-static const struct v4l2_standard cx18_std_50hz = {
-       .frameperiod = { .numerator = 1, .denominator = 25 },
-       .framelines = 625,
-};
+static int cx18_g_fmt_vid_cap(struct file *file, void *fh,
+                               struct v4l2_format *fmt)
+{
+       struct cx18_open_id *id = fh;
+       struct cx18 *cx = id->cx;
+       struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
+
+       pixfmt->width = cx->params.width;
+       pixfmt->height = cx->params.height;
+       pixfmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
+       pixfmt->field = V4L2_FIELD_INTERLACED;
+       pixfmt->priv = 0;
+       if (id->type == CX18_ENC_STREAM_TYPE_YUV) {
+               pixfmt->pixelformat = V4L2_PIX_FMT_HM12;
+               /* YUV size is (Y=(h*w) + UV=(h*(w/2))) */
+               pixfmt->sizeimage =
+                       pixfmt->height * pixfmt->width +
+                       pixfmt->height * (pixfmt->width / 2);
+               pixfmt->bytesperline = 720;
+       } else {
+               pixfmt->pixelformat = V4L2_PIX_FMT_MPEG;
+               pixfmt->sizeimage = 128 * 1024;
+               pixfmt->bytesperline = 0;
+       }
+       return 0;
+}
+
+static int cx18_g_fmt_vbi_cap(struct file *file, void *fh,
+                               struct v4l2_format *fmt)
+{
+       struct cx18 *cx = ((struct cx18_open_id *)fh)->cx;
+       struct v4l2_vbi_format *vbifmt = &fmt->fmt.vbi;
+
+       vbifmt->sampling_rate = 27000000;
+       vbifmt->offset = 248;
+       vbifmt->samples_per_line = cx->vbi.raw_decoder_line_size - 4;
+       vbifmt->sample_format = V4L2_PIX_FMT_GREY;
+       vbifmt->start[0] = cx->vbi.start[0];
+       vbifmt->start[1] = cx->vbi.start[1];
+       vbifmt->count[0] = vbifmt->count[1] = cx->vbi.count;
+       vbifmt->flags = 0;
+       vbifmt->reserved[0] = 0;
+       vbifmt->reserved[1] = 0;
+       return 0;
+}
+
+static int cx18_g_fmt_sliced_vbi_cap(struct file *file, void *fh,
+                                       struct v4l2_format *fmt)
+{
+       return -EINVAL;
+}
+
+static int cx18_try_fmt_vid_cap(struct file *file, void *fh,
+                               struct v4l2_format *fmt)
+{
+       struct cx18_open_id *id = fh;
+       struct cx18 *cx = id->cx;
+
+       int w = fmt->fmt.pix.width;
+       int h = fmt->fmt.pix.height;
+
+       w = min(w, 720);
+       w = max(w, 1);
+       h = min(h, cx->is_50hz ? 576 : 480);
+       h = max(h, 2);
+       cx18_g_fmt_vid_cap(file, fh, fmt);
+       fmt->fmt.pix.width = w;
+       fmt->fmt.pix.height = h;
+       return 0;
+}
+
+static int cx18_try_fmt_vbi_cap(struct file *file, void *fh,
+                               struct v4l2_format *fmt)
+{
+       return cx18_g_fmt_vbi_cap(file, fh, fmt);
+}
+
+static int cx18_try_fmt_sliced_vbi_cap(struct file *file, void *fh,
+                                       struct v4l2_format *fmt)
+{
+       return -EINVAL;
+}
+
+static int cx18_s_fmt_vid_cap(struct file *file, void *fh,
+                               struct v4l2_format *fmt)
+{
+       struct cx18_open_id *id = fh;
+       struct cx18 *cx = id->cx;
+       int ret;
+       int w = fmt->fmt.pix.width;
+       int h = fmt->fmt.pix.height;
+
+       ret = v4l2_prio_check(&cx->prio, &id->prio);
+       if (ret)
+               return ret;
+
+       ret = cx18_try_fmt_vid_cap(file, fh, fmt);
+       if (ret)
+               return ret;
+
+       if (cx->params.width == w && cx->params.height == h)
+               return 0;
+
+       if (atomic_read(&cx->ana_capturing) > 0)
+               return -EBUSY;
+
+       cx->params.width = w;
+       cx->params.height = h;
+       cx18_av_cmd(cx, VIDIOC_S_FMT, fmt);
+       return cx18_g_fmt_vid_cap(file, fh, fmt);
+}
+
+static int cx18_s_fmt_vbi_cap(struct file *file, void *fh,
+                               struct v4l2_format *fmt)
+{
+       struct cx18_open_id *id = fh;
+       struct cx18 *cx = id->cx;
+       int ret;
+
+       ret = v4l2_prio_check(&cx->prio, &id->prio);
+       if (ret)
+               return ret;
+
+       if (id->type == CX18_ENC_STREAM_TYPE_VBI &&
+                       cx->vbi.sliced_in->service_set &&
+                       atomic_read(&cx->ana_capturing) > 0)
+               return -EBUSY;
+
+       cx->vbi.sliced_in->service_set = 0;
+       cx18_av_cmd(cx, VIDIOC_S_FMT, &cx->vbi.in);
+       return cx18_g_fmt_vbi_cap(file, fh, fmt);
+}
+
+static int cx18_s_fmt_sliced_vbi_cap(struct file *file, void *fh,
+                                       struct v4l2_format *fmt)
+{
+       return -EINVAL;
+}
 
+static int cx18_g_chip_ident(struct file *file, void *fh,
+                               struct v4l2_chip_ident *chip)
+{
+       struct cx18 *cx = ((struct cx18_open_id *)fh)->cx;
+
+       chip->ident = V4L2_IDENT_NONE;
+       chip->revision = 0;
+       if (chip->match_type == V4L2_CHIP_MATCH_HOST) {
+               if (v4l2_chip_match_host(chip->match_type, chip->match_chip))
+                       chip->ident = V4L2_IDENT_CX23418;
+               return 0;
+       }
+       if (chip->match_type == V4L2_CHIP_MATCH_I2C_DRIVER)
+               return cx18_i2c_id(cx, chip->match_chip, VIDIOC_G_CHIP_IDENT,
+                                       chip);
+       if (chip->match_type == V4L2_CHIP_MATCH_I2C_ADDR)
+               return cx18_call_i2c_client(cx, chip->match_chip,
+                                               VIDIOC_G_CHIP_IDENT, chip);
+       return -EINVAL;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
 static int cx18_cxc(struct cx18 *cx, unsigned int cmd, void *arg)
 {
        struct v4l2_register *regs = arg;
@@ -174,665 +293,478 @@ static int cx18_cxc(struct cx18 *cx, unsigned int cmd, void *arg)
        return 0;
 }
 
-static int cx18_get_fmt(struct cx18 *cx, int streamtype, struct v4l2_format *fmt)
-{
-       switch (fmt->type) {
-       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-               fmt->fmt.pix.width = cx->params.width;
-               fmt->fmt.pix.height = cx->params.height;
-               fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
-               fmt->fmt.pix.field = V4L2_FIELD_INTERLACED;
-               if (streamtype == CX18_ENC_STREAM_TYPE_YUV) {
-                       fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_HM12;
-                       /* YUV size is (Y=(h*w) + UV=(h*(w/2))) */
-                       fmt->fmt.pix.sizeimage =
-                               fmt->fmt.pix.height * fmt->fmt.pix.width +
-                               fmt->fmt.pix.height * (fmt->fmt.pix.width / 2);
-               } else {
-                       fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
-                       fmt->fmt.pix.sizeimage = 128 * 1024;
-               }
-               break;
-
-       case V4L2_BUF_TYPE_VBI_CAPTURE:
-               fmt->fmt.vbi.sampling_rate = 27000000;
-               fmt->fmt.vbi.offset = 248;
-               fmt->fmt.vbi.samples_per_line = cx->vbi.raw_decoder_line_size - 4;
-               fmt->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
-               fmt->fmt.vbi.start[0] = cx->vbi.start[0];
-               fmt->fmt.vbi.start[1] = cx->vbi.start[1];
-               fmt->fmt.vbi.count[0] = fmt->fmt.vbi.count[1] = cx->vbi.count;
-               break;
+static int cx18_g_register(struct file *file, void *fh,
+                               struct v4l2_register *reg)
+{
+       struct cx18 *cx = ((struct cx18_open_id *)fh)->cx;
+
+       if (v4l2_chip_match_host(reg->match_type, reg->match_chip))
+               return cx18_cxc(cx, VIDIOC_DBG_G_REGISTER, reg);
+       if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER)
+               return cx18_i2c_id(cx, reg->match_chip, VIDIOC_DBG_G_REGISTER,
+                                       reg);
+       return cx18_call_i2c_client(cx, reg->match_chip, VIDIOC_DBG_G_REGISTER,
+                                       reg);
+}
 
-       case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
-       {
-               struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
+static int cx18_s_register(struct file *file, void *fh,
+                               struct v4l2_register *reg)
+{
+       struct cx18 *cx = ((struct cx18_open_id *)fh)->cx;
+
+       if (v4l2_chip_match_host(reg->match_type, reg->match_chip))
+               return cx18_cxc(cx, VIDIOC_DBG_S_REGISTER, reg);
+       if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER)
+               return cx18_i2c_id(cx, reg->match_chip, VIDIOC_DBG_S_REGISTER,
+                                       reg);
+       return cx18_call_i2c_client(cx, reg->match_chip, VIDIOC_DBG_S_REGISTER,
+                                       reg);
+}
+#endif
 
-               vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
-               memset(vbifmt->reserved, 0, sizeof(vbifmt->reserved));
-               memset(vbifmt->service_lines, 0, sizeof(vbifmt->service_lines));
+static int cx18_g_priority(struct file *file, void *fh, enum v4l2_priority *p)
+{
+       struct cx18 *cx = ((struct cx18_open_id *)fh)->cx;
 
-               cx18_av_cmd(cx, VIDIOC_G_FMT, fmt);
-               vbifmt->service_set = cx18_get_service_set(vbifmt);
-               break;
-       }
-       default:
-               return -EINVAL;
-       }
+       *p = v4l2_prio_max(&cx->prio);
        return 0;
 }
 
-static int cx18_try_or_set_fmt(struct cx18 *cx, int streamtype,
-               struct v4l2_format *fmt, int set_fmt)
+static int cx18_s_priority(struct file *file, void *fh, enum v4l2_priority prio)
 {
-       struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
-       u16 set;
+       struct cx18_open_id *id = fh;
+       struct cx18 *cx = id->cx;
 
-       /* set window size */
-       if (fmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
-               int w = fmt->fmt.pix.width;
-               int h = fmt->fmt.pix.height;
+       return v4l2_prio_change(&cx->prio, &id->prio, prio);
+}
 
-               if (w > 720)
-                       w = 720;
-               else if (w < 1)
-                       w = 1;
-               if (h > (cx->is_50hz ? 576 : 480))
-                       h = (cx->is_50hz ? 576 : 480);
-               else if (h < 2)
-                       h = 2;
-               cx18_get_fmt(cx, streamtype, fmt);
-               fmt->fmt.pix.width = w;
-               fmt->fmt.pix.height = h;
+static int cx18_querycap(struct file *file, void *fh,
+                               struct v4l2_capability *vcap)
+{
+       struct cx18 *cx = ((struct cx18_open_id *)fh)->cx;
 
-               if (!set_fmt || (cx->params.width == w && cx->params.height == h))
-                       return 0;
-               if (atomic_read(&cx->ana_capturing) > 0)
-                       return -EBUSY;
-
-               cx->params.width = w;
-               cx->params.height = h;
-               if (w != 720 || h != (cx->is_50hz ? 576 : 480))
-                       cx->params.video_temporal_filter = 0;
-               else
-                       cx->params.video_temporal_filter = 8;
-               cx18_av_cmd(cx, VIDIOC_S_FMT, fmt);
-               return cx18_get_fmt(cx, streamtype, fmt);
-       }
+       strlcpy(vcap->driver, CX18_DRIVER_NAME, sizeof(vcap->driver));
+       strlcpy(vcap->card, cx->card_name, sizeof(vcap->card));
+       strlcpy(vcap->bus_info, pci_name(cx->dev), sizeof(vcap->bus_info));
+       vcap->version = CX18_DRIVER_VERSION;        /* version */
+       vcap->capabilities = cx->v4l2_cap;          /* capabilities */
+       return 0;
+}
 
-       /* set raw VBI format */
-       if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
-               if (set_fmt && streamtype == CX18_ENC_STREAM_TYPE_VBI &&
-                   cx->vbi.sliced_in->service_set &&
-                   atomic_read(&cx->ana_capturing) > 0)
-                       return -EBUSY;
-               if (set_fmt) {
-                       cx->vbi.sliced_in->service_set = 0;
-                       cx18_av_cmd(cx, VIDIOC_S_FMT, &cx->vbi.in);
-               }
-               return cx18_get_fmt(cx, streamtype, fmt);
-       }
+static int cx18_enumaudio(struct file *file, void *fh, struct v4l2_audio *vin)
+{
+       struct cx18 *cx = ((struct cx18_open_id *)fh)->cx;
 
-       /* any else but sliced VBI capture is an error */
-       if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
-               return -EINVAL;
+       return cx18_get_audio_input(cx, vin->index, vin);
+}
 
-       /* TODO: implement sliced VBI, for now silently return 0 */
-       return 0;
+static int cx18_g_audio(struct file *file, void *fh, struct v4l2_audio *vin)
+{
+       struct cx18 *cx = ((struct cx18_open_id *)fh)->cx;
 
-       /* set sliced VBI capture format */
-       vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
-       memset(vbifmt->reserved, 0, sizeof(vbifmt->reserved));
+       vin->index = cx->audio_input;
+       return cx18_get_audio_input(cx, vin->index, vin);
+}
 
-       if (vbifmt->service_set)
-               cx18_expand_service_set(vbifmt, cx->is_50hz);
-       set = check_service_set(vbifmt, cx->is_50hz);
-       vbifmt->service_set = cx18_get_service_set(vbifmt);
+static int cx18_s_audio(struct file *file, void *fh, struct v4l2_audio *vout)
+{
+       struct cx18 *cx = ((struct cx18_open_id *)fh)->cx;
 
-       if (!set_fmt)
-               return 0;
-       if (set == 0)
+       if (vout->index >= cx->nof_audio_inputs)
                return -EINVAL;
-       if (atomic_read(&cx->ana_capturing) > 0 && cx->vbi.sliced_in->service_set == 0)
-               return -EBUSY;
-       cx18_av_cmd(cx, VIDIOC_S_FMT, fmt);
-       memcpy(cx->vbi.sliced_in, vbifmt, sizeof(*cx->vbi.sliced_in));
+       cx->audio_input = vout->index;
+       cx18_audio_set_io(cx);
        return 0;
 }
 
-static int cx18_debug_ioctls(struct file *filp, unsigned int cmd, void *arg)
+static int cx18_enum_input(struct file *file, void *fh, struct v4l2_input *vin)
 {
-       struct cx18_open_id *id = (struct cx18_open_id *)filp->private_data;
-       struct cx18 *cx = id->cx;
-       struct v4l2_register *reg = arg;
+       struct cx18 *cx = ((struct cx18_open_id *)fh)->cx;
 
-       switch (cmd) {
-       /* ioctls to allow direct access to the encoder registers for testing */
-       case VIDIOC_DBG_G_REGISTER:
-               if (v4l2_chip_match_host(reg->match_type, reg->match_chip))
-                       return cx18_cxc(cx, cmd, arg);
-               if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER)
-                       return cx18_i2c_id(cx, reg->match_chip, cmd, arg);
-               return cx18_call_i2c_client(cx, reg->match_chip, cmd, arg);
-
-       case VIDIOC_DBG_S_REGISTER:
-               if (v4l2_chip_match_host(reg->match_type, reg->match_chip))
-                       return cx18_cxc(cx, cmd, arg);
-               if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER)
-                       return cx18_i2c_id(cx, reg->match_chip, cmd, arg);
-               return cx18_call_i2c_client(cx, reg->match_chip, cmd, arg);
-
-       case VIDIOC_G_CHIP_IDENT: {
-               struct v4l2_chip_ident *chip = arg;
-
-               chip->ident = V4L2_IDENT_NONE;
-               chip->revision = 0;
-               if (reg->match_type == V4L2_CHIP_MATCH_HOST) {
-                       if (v4l2_chip_match_host(reg->match_type, reg->match_chip)) {
-                               struct v4l2_chip_ident *chip = arg;
-
-                               chip->ident = V4L2_IDENT_CX23418;
-                       }
-                       return 0;
-               }
-               if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER)
-                       return cx18_i2c_id(cx, reg->match_chip, cmd, arg);
-               if (reg->match_type == V4L2_CHIP_MATCH_I2C_ADDR)
-                       return cx18_call_i2c_client(cx, reg->match_chip, cmd, arg);
-               return -EINVAL;
-       }
-
-       case VIDIOC_INT_S_AUDIO_ROUTING: {
-               struct v4l2_routing *route = arg;
+       /* set it to defaults from our table */
+       return cx18_get_input(cx, vin->index, vin);
+}
 
-               cx18_audio_set_route(cx, route);
-               break;
-       }
+static int cx18_cropcap(struct file *file, void *fh,
+                       struct v4l2_cropcap *cropcap)
+{
+       struct cx18 *cx = ((struct cx18_open_id *)fh)->cx;
 
-       default:
+       if (cropcap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
                return -EINVAL;
-       }
+       cropcap->bounds.top = cropcap->bounds.left = 0;
+       cropcap->bounds.width = 720;
+       cropcap->bounds.height = cx->is_50hz ? 576 : 480;
+       cropcap->pixelaspect.numerator = cx->is_50hz ? 59 : 10;
+       cropcap->pixelaspect.denominator = cx->is_50hz ? 54 : 11;
+       cropcap->defrect = cropcap->bounds;
        return 0;
 }
 
-int cx18_v4l2_ioctls(struct cx18 *cx, struct file *filp, unsigned cmd, void *arg)
+static int cx18_s_crop(struct file *file, void *fh, struct v4l2_crop *crop)
 {
-       struct cx18_open_id *id = NULL;
-
-       if (filp)
-               id = (struct cx18_open_id *)filp->private_data;
-
-       switch (cmd) {
-       case VIDIOC_G_PRIORITY:
-       {
-               enum v4l2_priority *p = arg;
+       struct cx18_open_id *id = fh;
+       struct cx18 *cx = id->cx;
+       int ret;
 
-               *p = v4l2_prio_max(&cx->prio);
-               break;
-       }
+       ret = v4l2_prio_check(&cx->prio, &id->prio);
+       if (ret)
+               return ret;
 
-       case VIDIOC_S_PRIORITY:
-       {
-               enum v4l2_priority *prio = arg;
+       if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+               return -EINVAL;
+       return cx18_av_cmd(cx, VIDIOC_S_CROP, crop);
+}
 
-               return v4l2_prio_change(&cx->prio, &id->prio, *prio);
-       }
+static int cx18_g_crop(struct file *file, void *fh, struct v4l2_crop *crop)
+{
+       struct cx18 *cx = ((struct cx18_open_id *)fh)->cx;
 
-       case VIDIOC_QUERYCAP:{
-               struct v4l2_capability *vcap = arg;
+       if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+               return -EINVAL;
+       return cx18_av_cmd(cx, VIDIOC_G_CROP, crop);
+}
 
-               memset(vcap, 0, sizeof(*vcap));
-               strlcpy(vcap->driver, CX18_DRIVER_NAME, sizeof(vcap->driver));
-               strlcpy(vcap->card, cx->card_name, sizeof(vcap->card));
-               strlcpy(vcap->bus_info, pci_name(cx->dev), sizeof(vcap->bus_info));
-               vcap->version = CX18_DRIVER_VERSION;        /* version */
-               vcap->capabilities = cx->v4l2_cap;          /* capabilities */
+static int cx18_enum_fmt_vid_cap(struct file *file, void *fh,
+                                       struct v4l2_fmtdesc *fmt)
+{
+       static struct v4l2_fmtdesc formats[] = {
+               { 0, V4L2_BUF_TYPE_VIDEO_CAPTURE, 0,
+                 "HM12 (YUV 4:1:1)", V4L2_PIX_FMT_HM12, { 0, 0, 0, 0 }
+               },
+               { 1, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FMT_FLAG_COMPRESSED,
+                 "MPEG", V4L2_PIX_FMT_MPEG, { 0, 0, 0, 0 }
+               }
+       };
 
-               /* reserved.. must set to 0! */
-               vcap->reserved[0] = vcap->reserved[1] =
-                       vcap->reserved[2] = vcap->reserved[3] = 0;
-               break;
-       }
+       if (fmt->index > 1)
+               return -EINVAL;
+       *fmt = formats[fmt->index];
+       return 0;
+}
 
-       case VIDIOC_ENUMAUDIO:{
-               struct v4l2_audio *vin = arg;
+static int cx18_g_input(struct file *file, void *fh, unsigned int *i)
+{
+       struct cx18 *cx = ((struct cx18_open_id *)fh)->cx;
 
-               return cx18_get_audio_input(cx, vin->index, vin);
-       }
+       *i = cx->active_input;
+       return 0;
+}
 
-       case VIDIOC_G_AUDIO:{
-               struct v4l2_audio *vin = arg;
+int cx18_s_input(struct file *file, void *fh, unsigned int inp)
+{
+       struct cx18_open_id *id = fh;
+       struct cx18 *cx = id->cx;
+       int ret;
 
-               vin->index = cx->audio_input;
-               return cx18_get_audio_input(cx, vin->index, vin);
-       }
+       ret = v4l2_prio_check(&cx->prio, &id->prio);
+       if (ret)
+               return ret;
 
-       case VIDIOC_S_AUDIO:{
-               struct v4l2_audio *vout = arg;
+       if (inp < 0 || inp >= cx->nof_inputs)
+               return -EINVAL;
 
-               if (vout->index >= cx->nof_audio_inputs)
-                       return -EINVAL;
-               cx->audio_input = vout->index;
-               cx18_audio_set_io(cx);
-               break;
+       if (inp == cx->active_input) {
+               CX18_DEBUG_INFO("Input unchanged\n");
+               return 0;
        }
 
-       case VIDIOC_ENUMINPUT:{
-               struct v4l2_input *vin = arg;
-
-               /* set it to defaults from our table */
-               return cx18_get_input(cx, vin->index, vin);
-       }
+       CX18_DEBUG_INFO("Changing input from %d to %d\n",
+                       cx->active_input, inp);
 
-       case VIDIOC_TRY_FMT:
-       case VIDIOC_S_FMT: {
-               struct v4l2_format *fmt = arg;
+       cx->active_input = inp;
+       /* Set the audio input to whatever is appropriate for the input type. */
+       cx->audio_input = cx->card->video_inputs[inp].audio_index;
 
-               return cx18_try_or_set_fmt(cx, id->type, fmt, cmd == VIDIOC_S_FMT);
-       }
+       /* prevent others from messing with the streams until
+          we're finished changing inputs. */
+       cx18_mute(cx);
+       cx18_video_set_io(cx);
+       cx18_audio_set_io(cx);
+       cx18_unmute(cx);
+       return 0;
+}
 
-       case VIDIOC_G_FMT: {
-               struct v4l2_format *fmt = arg;
-               int type = fmt->type;
+static int cx18_g_frequency(struct file *file, void *fh,
+                               struct v4l2_frequency *vf)
+{
+       struct cx18 *cx = ((struct cx18_open_id *)fh)->cx;
 
-               memset(fmt, 0, sizeof(*fmt));
-               fmt->type = type;
-               return cx18_get_fmt(cx, id->type, fmt);
-       }
+       if (vf->tuner != 0)
+               return -EINVAL;
 
-       case VIDIOC_CROPCAP: {
-               struct v4l2_cropcap *cropcap = arg;
-
-               if (cropcap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
-                       return -EINVAL;
-               cropcap->bounds.top = cropcap->bounds.left = 0;
-               cropcap->bounds.width = 720;
-               cropcap->bounds.height = cx->is_50hz ? 576 : 480;
-               cropcap->pixelaspect.numerator = cx->is_50hz ? 59 : 10;
-               cropcap->pixelaspect.denominator = cx->is_50hz ? 54 : 11;
-               cropcap->defrect = cropcap->bounds;
-               return 0;
-       }
+       cx18_call_i2c_clients(cx, VIDIOC_G_FREQUENCY, vf);
+       return 0;
+}
 
-       case VIDIOC_S_CROP: {
-               struct v4l2_crop *crop = arg;
+int cx18_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf)
+{
+       struct cx18_open_id *id = fh;
+       struct cx18 *cx = id->cx;
+       int ret;
 
-               if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
-                       return -EINVAL;
-               return cx18_av_cmd(cx, VIDIOC_S_CROP, arg);
-       }
+       ret = v4l2_prio_check(&cx->prio, &id->prio);
+       if (ret)
+               return ret;
 
-       case VIDIOC_G_CROP: {
-               struct v4l2_crop *crop = arg;
+       if (vf->tuner != 0)
+               return -EINVAL;
 
-               if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
-                       return -EINVAL;
-               return cx18_av_cmd(cx, VIDIOC_G_CROP, arg);
-       }
+       cx18_mute(cx);
+       CX18_DEBUG_INFO("v4l2 ioctl: set frequency %d\n", vf->frequency);
+       cx18_call_i2c_clients(cx, VIDIOC_S_FREQUENCY, vf);
+       cx18_unmute(cx);
+       return 0;
+}
 
-       case VIDIOC_ENUM_FMT: {
-               static struct v4l2_fmtdesc formats[] = {
-                       { 0, 0, 0,
-                         "HM12 (YUV 4:1:1)", V4L2_PIX_FMT_HM12,
-                         { 0, 0, 0, 0 }
-                       },
-                       { 1, 0, V4L2_FMT_FLAG_COMPRESSED,
-                         "MPEG", V4L2_PIX_FMT_MPEG,
-                         { 0, 0, 0, 0 }
-                       }
-               };
-               struct v4l2_fmtdesc *fmt = arg;
-               enum v4l2_buf_type type = fmt->type;
-
-               switch (type) {
-               case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-                       break;
-               default:
-                       return -EINVAL;
-               }
-               if (fmt->index > 1)
-                       return -EINVAL;
-               *fmt = formats[fmt->index];
-               fmt->type = type;
-               return 0;
-       }
+static int cx18_g_std(struct file *file, void *fh, v4l2_std_id *std)
+{
+       struct cx18 *cx = ((struct cx18_open_id *)fh)->cx;
 
-       case VIDIOC_G_INPUT:{
-               *(int *)arg = cx->active_input;
-               break;
-       }
+       *std = cx->std;
+       return 0;
+}
 
-       case VIDIOC_S_INPUT:{
-               int inp = *(int *)arg;
+int cx18_s_std(struct file *file, void *fh, v4l2_std_id *std)
+{
+       struct cx18_open_id *id = fh;
+       struct cx18 *cx = id->cx;
+       int ret;
 
-               if (inp < 0 || inp >= cx->nof_inputs)
-                       return -EINVAL;
+       ret = v4l2_prio_check(&cx->prio, &id->prio);
+       if (ret)
+               return ret;
 
-               if (inp == cx->active_input) {
-                       CX18_DEBUG_INFO("Input unchanged\n");
-                       break;
-               }
-               CX18_DEBUG_INFO("Changing input from %d to %d\n",
-                               cx->active_input, inp);
+       if ((*std & V4L2_STD_ALL) == 0)
+               return -EINVAL;
 
-               cx->active_input = inp;
-               /* Set the audio input to whatever is appropriate for the
-                  input type. */
-               cx->audio_input = cx->card->video_inputs[inp].audio_index;
+       if (*std == cx->std)
+               return 0;
 
-               /* prevent others from messing with the streams until
-                  we're finished changing inputs. */
-               cx18_mute(cx);
-               cx18_video_set_io(cx);
-               cx18_audio_set_io(cx);
-               cx18_unmute(cx);
-               break;
+       if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags) ||
+           atomic_read(&cx->ana_capturing) > 0) {
+               /* Switching standard would turn off the radio or mess
+                  with already running streams, prevent that by
+                  returning EBUSY. */
+               return -EBUSY;
        }
 
-       case VIDIOC_G_FREQUENCY:{
-               struct v4l2_frequency *vf = arg;
+       cx->std = *std;
+       cx->is_60hz = (*std & V4L2_STD_525_60) ? 1 : 0;
+       cx->params.is_50hz = cx->is_50hz = !cx->is_60hz;
+       cx->params.width = 720;
+       cx->params.height = cx->is_50hz ? 576 : 480;
+       cx->vbi.count = cx->is_50hz ? 18 : 12;
+       cx->vbi.start[0] = cx->is_50hz ? 6 : 10;
+       cx->vbi.start[1] = cx->is_50hz ? 318 : 273;
+       cx->vbi.sliced_decoder_line_size = cx->is_60hz ? 272 : 284;
+       CX18_DEBUG_INFO("Switching standard to %llx.\n",
+                       (unsigned long long) cx->std);
+
+       /* Tuner */
+       cx18_call_i2c_clients(cx, VIDIOC_S_STD, &cx->std);
+       return 0;
+}
 
-               if (vf->tuner != 0)
-                       return -EINVAL;
-               cx18_call_i2c_clients(cx, cmd, arg);
-               break;
-       }
+static int cx18_s_tuner(struct file *file, void *fh, struct v4l2_tuner *vt)
+{
+       struct cx18_open_id *id = fh;
+       struct cx18 *cx = id->cx;
+       int ret;
 
-       case VIDIOC_S_FREQUENCY:{
-               struct v4l2_frequency vf = *(struct v4l2_frequency *)arg;
+       ret = v4l2_prio_check(&cx->prio, &id->prio);
+       if (ret)
+               return ret;
 
-               if (vf.tuner != 0)
-                       return -EINVAL;
+       if (vt->index != 0)
+               return -EINVAL;
 
-               cx18_mute(cx);
-               CX18_DEBUG_INFO("v4l2 ioctl: set frequency %d\n", vf.frequency);
-               cx18_call_i2c_clients(cx, cmd, &vf);
-               cx18_unmute(cx);
-               break;
-       }
+       /* Setting tuner can only set audio mode */
+       cx18_call_i2c_clients(cx, VIDIOC_S_TUNER, vt);
 
-       case VIDIOC_ENUMSTD:{
-               struct v4l2_standard *vs = arg;
-               int idx = vs->index;
+       return 0;
+}
 
-               if (idx < 0 || idx >= ARRAY_SIZE(enum_stds))
-                       return -EINVAL;
+static int cx18_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt)
+{
+       struct cx18 *cx = ((struct cx18_open_id *)fh)->cx;
 
-               *vs = (enum_stds[idx].std & V4L2_STD_525_60) ?
-                               cx18_std_60hz : cx18_std_50hz;
-               vs->index = idx;
-               vs->id = enum_stds[idx].std;
-               strlcpy(vs->name, enum_stds[idx].name, sizeof(vs->name));
-               break;
-       }
+       if (vt->index != 0)
+               return -EINVAL;
 
-       case VIDIOC_G_STD:{
-               *(v4l2_std_id *) arg = cx->std;
-               break;
+       cx18_call_i2c_clients(cx, VIDIOC_G_TUNER, vt);
+
+       if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) {
+               strlcpy(vt->name, "cx18 Radio Tuner", sizeof(vt->name));
+               vt->type = V4L2_TUNER_RADIO;
+       } else {
+               strlcpy(vt->name, "cx18 TV Tuner", sizeof(vt->name));
+               vt->type = V4L2_TUNER_ANALOG_TV;
        }
 
-       case VIDIOC_S_STD: {
-               v4l2_std_id std = *(v4l2_std_id *) arg;
+       return 0;
+}
 
-               if ((std & V4L2_STD_ALL) == 0)
-                       return -EINVAL;
+static int cx18_g_sliced_vbi_cap(struct file *file, void *fh,
+                                       struct v4l2_sliced_vbi_cap *cap)
+{
+       return -EINVAL;
+}
 
-               if (std == cx->std)
-                       break;
+static int cx18_g_enc_index(struct file *file, void *fh,
+                               struct v4l2_enc_idx *idx)
+{
+       return -EINVAL;
+}
 
-               if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags) ||
-                   atomic_read(&cx->ana_capturing) > 0) {
-                       /* Switching standard would turn off the radio or mess
-                          with already running streams, prevent that by
-                          returning EBUSY. */
-                       return -EBUSY;
-               }
+static int cx18_encoder_cmd(struct file *file, void *fh,
+                               struct v4l2_encoder_cmd *enc)
+{
+       struct cx18_open_id *id = fh;
+       struct cx18 *cx = id->cx;
 
-               cx->std = std;
-               cx->is_60hz = (std & V4L2_STD_525_60) ? 1 : 0;
-               cx->params.is_50hz = cx->is_50hz = !cx->is_60hz;
-               cx->params.width = 720;
-               cx->params.height = cx->is_50hz ? 576 : 480;
-               cx->vbi.count = cx->is_50hz ? 18 : 12;
-               cx->vbi.start[0] = cx->is_50hz ? 6 : 10;
-               cx->vbi.start[1] = cx->is_50hz ? 318 : 273;
-               cx->vbi.sliced_decoder_line_size = cx->is_60hz ? 272 : 284;
-               CX18_DEBUG_INFO("Switching standard to %llx.\n", (unsigned long long)cx->std);
-
-               /* Tuner */
-               cx18_call_i2c_clients(cx, VIDIOC_S_STD, &cx->std);
+       switch (enc->cmd) {
+       case V4L2_ENC_CMD_START:
+               CX18_DEBUG_IOCTL("V4L2_ENC_CMD_START\n");
+               enc->flags = 0;
+               return cx18_start_capture(id);
+
+       case V4L2_ENC_CMD_STOP:
+               CX18_DEBUG_IOCTL("V4L2_ENC_CMD_STOP\n");
+               enc->flags &= V4L2_ENC_CMD_STOP_AT_GOP_END;
+               cx18_stop_capture(id,
+                                 enc->flags & V4L2_ENC_CMD_STOP_AT_GOP_END);
                break;
-       }
-
-       case VIDIOC_S_TUNER: {  /* Setting tuner can only set audio mode */
-               struct v4l2_tuner *vt = arg;
 
-               if (vt->index != 0)
-                       return -EINVAL;
-
-               cx18_call_i2c_clients(cx, VIDIOC_S_TUNER, vt);
+       case V4L2_ENC_CMD_PAUSE:
+               CX18_DEBUG_IOCTL("V4L2_ENC_CMD_PAUSE\n");
+               enc->flags = 0;
+               if (!atomic_read(&cx->ana_capturing))
+                       return -EPERM;
+               if (test_and_set_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags))
+                       return 0;
+               cx18_mute(cx);
+               cx18_vapi(cx, CX18_CPU_CAPTURE_PAUSE, 1, cx18_find_handle(cx));
                break;
-       }
-
-       case VIDIOC_G_TUNER: {
-               struct v4l2_tuner *vt = arg;
-
-               if (vt->index != 0)
-                       return -EINVAL;
-
-               memset(vt, 0, sizeof(*vt));
-               cx18_call_i2c_clients(cx, VIDIOC_G_TUNER, vt);
 
-               if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) {
-                       strlcpy(vt->name, "cx18 Radio Tuner", sizeof(vt->name));
-                       vt->type = V4L2_TUNER_RADIO;
-               } else {
-                       strlcpy(vt->name, "cx18 TV Tuner", sizeof(vt->name));
-                       vt->type = V4L2_TUNER_ANALOG_TV;
-               }
+       case V4L2_ENC_CMD_RESUME:
+               CX18_DEBUG_IOCTL("V4L2_ENC_CMD_RESUME\n");
+               enc->flags = 0;
+               if (!atomic_read(&cx->ana_capturing))
+                       return -EPERM;
+               if (!test_and_clear_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags))
+                       return 0;
+               cx18_vapi(cx, CX18_CPU_CAPTURE_RESUME, 1, cx18_find_handle(cx));
+               cx18_unmute(cx);
                break;
-       }
 
-       case VIDIOC_G_SLICED_VBI_CAP: {
-               struct v4l2_sliced_vbi_cap *cap = arg;
-               int set = cx->is_50hz ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525;
-               int f, l;
-               enum v4l2_buf_type type = cap->type;
-
-               memset(cap, 0, sizeof(*cap));
-               cap->type = type;
-               if (type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) {
-                       for (f = 0; f < 2; f++) {
-                               for (l = 0; l < 24; l++) {
-                                       if (valid_service_line(f, l, cx->is_50hz))
-                                               cap->service_lines[f][l] = set;
-                               }
-                       }
-                       return 0;
-               }
+       default:
+               CX18_DEBUG_IOCTL("Unknown cmd %d\n", enc->cmd);
                return -EINVAL;
        }
+       return 0;
+}
 
-       case VIDIOC_ENCODER_CMD:
-       case VIDIOC_TRY_ENCODER_CMD: {
-               struct v4l2_encoder_cmd *enc = arg;
-               int try = cmd == VIDIOC_TRY_ENCODER_CMD;
-
-               memset(&enc->raw, 0, sizeof(enc->raw));
-               switch (enc->cmd) {
-               case V4L2_ENC_CMD_START:
-                       enc->flags = 0;
-                       if (try)
-                               return 0;
-                       return cx18_start_capture(id);
-
-               case V4L2_ENC_CMD_STOP:
-                       enc->flags &= V4L2_ENC_CMD_STOP_AT_GOP_END;
-                       if (try)
-                               return 0;
-                       cx18_stop_capture(id, enc->flags & V4L2_ENC_CMD_STOP_AT_GOP_END);
-                       return 0;
+static int cx18_try_encoder_cmd(struct file *file, void *fh,
+                               struct v4l2_encoder_cmd *enc)
+{
+       struct cx18 *cx = ((struct cx18_open_id *)fh)->cx;
 
-               case V4L2_ENC_CMD_PAUSE:
-                       enc->flags = 0;
-                       if (try)
-                               return 0;
-                       if (!atomic_read(&cx->ana_capturing))
-                               return -EPERM;
-                       if (test_and_set_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags))
-                               return 0;
-                       cx18_mute(cx);
-                       cx18_vapi(cx, CX18_CPU_CAPTURE_PAUSE, 1, cx18_find_handle(cx));
-                       break;
-
-               case V4L2_ENC_CMD_RESUME:
-                       enc->flags = 0;
-                       if (try)
-                               return 0;
-                       if (!atomic_read(&cx->ana_capturing))
-                               return -EPERM;
-                       if (!test_and_clear_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags))
-                               return 0;
-                       cx18_vapi(cx, CX18_CPU_CAPTURE_RESUME, 1, cx18_find_handle(cx));
-                       cx18_unmute(cx);
-                       break;
-               default:
-                       return -EINVAL;
-               }
+       switch (enc->cmd) {
+       case V4L2_ENC_CMD_START:
+               CX18_DEBUG_IOCTL("V4L2_ENC_CMD_START\n");
+               enc->flags = 0;
                break;
-       }
 
-       case VIDIOC_LOG_STATUS:
-       {
-               struct v4l2_input vidin;
-               struct v4l2_audio audin;
-               int i;
+       case V4L2_ENC_CMD_STOP:
+               CX18_DEBUG_IOCTL("V4L2_ENC_CMD_STOP\n");
+               enc->flags &= V4L2_ENC_CMD_STOP_AT_GOP_END;
+               break;
 
-               CX18_INFO("=================  START STATUS CARD #%d  =================\n", cx->num);
-               if (cx->hw_flags & CX18_HW_TVEEPROM) {
-                       struct tveeprom tv;
+       case V4L2_ENC_CMD_PAUSE:
+               CX18_DEBUG_IOCTL("V4L2_ENC_CMD_PAUSE\n");
+               enc->flags = 0;
+               break;
 
-                       cx18_read_eeprom(cx, &tv);
-               }
-               cx18_call_i2c_clients(cx, VIDIOC_LOG_STATUS, NULL);
-               cx18_get_input(cx, cx->active_input, &vidin);
-               cx18_get_audio_input(cx, cx->audio_input, &audin);
-               CX18_INFO("Video Input: %s\n", vidin.name);
-               CX18_INFO("Audio Input: %s\n", audin.name);
-               CX18_INFO("Tuner: %s\n",
-                       test_bit(CX18_F_I_RADIO_USER, &cx->i_flags) ?
-                       "Radio" : "TV");
-               cx2341x_log_status(&cx->params, cx->name);
-               CX18_INFO("Status flags: 0x%08lx\n", cx->i_flags);
-               for (i = 0; i < CX18_MAX_STREAMS; i++) {
-                       struct cx18_stream *s = &cx->streams[i];
-
-                       if (s->v4l2dev == NULL || s->buffers == 0)
-                               continue;
-                       CX18_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n",
-                               s->name, s->s_flags,
-                               (s->buffers - s->q_free.buffers) * 100 / s->buffers,
-                               (s->buffers * s->buf_size) / 1024, s->buffers);
-               }
-               CX18_INFO("Read MPEG/VBI: %lld/%lld bytes\n",
-                               (long long)cx->mpg_data_received,
-                               (long long)cx->vbi_data_inserted);
-               CX18_INFO("==================  END STATUS CARD #%d  ==================\n", cx->num);
+       case V4L2_ENC_CMD_RESUME:
+               CX18_DEBUG_IOCTL("V4L2_ENC_CMD_RESUME\n");
+               enc->flags = 0;
                break;
-       }
 
        default:
+               CX18_DEBUG_IOCTL("Unknown cmd %d\n", enc->cmd);
                return -EINVAL;
        }
        return 0;
 }
 
-static int cx18_v4l2_do_ioctl(struct inode *inode, struct file *filp,
-                             unsigned int cmd, void *arg)
+static int cx18_log_status(struct file *file, void *fh)
 {
-       struct cx18_open_id *id = (struct cx18_open_id *)filp->private_data;
-       struct cx18 *cx = id->cx;
-       int ret;
+       struct cx18 *cx = ((struct cx18_open_id *)fh)->cx;
+       struct v4l2_input vidin;
+       struct v4l2_audio audin;
+       int i;
+
+       CX18_INFO("=================  START STATUS CARD #%d  =================\n", cx->num);
+       if (cx->hw_flags & CX18_HW_TVEEPROM) {
+               struct tveeprom tv;
+
+               cx18_read_eeprom(cx, &tv);
+       }
+       cx18_call_i2c_clients(cx, VIDIOC_LOG_STATUS, NULL);
+       cx18_get_input(cx, cx->active_input, &vidin);
+       cx18_get_audio_input(cx, cx->audio_input, &audin);
+       CX18_INFO("Video Input: %s\n", vidin.name);
+       CX18_INFO("Audio Input: %s\n", audin.name);
+       mutex_lock(&cx->gpio_lock);
+       CX18_INFO("GPIO:  direction 0x%08x, value 0x%08x\n",
+               cx->gpio_dir, cx->gpio_val);
+       mutex_unlock(&cx->gpio_lock);
+       CX18_INFO("Tuner: %s\n",
+               test_bit(CX18_F_I_RADIO_USER, &cx->i_flags) ?  "Radio" : "TV");
+       cx2341x_log_status(&cx->params, cx->name);
+       CX18_INFO("Status flags: 0x%08lx\n", cx->i_flags);
+       for (i = 0; i < CX18_MAX_STREAMS; i++) {
+               struct cx18_stream *s = &cx->streams[i];
+
+               if (s->v4l2dev == NULL || s->buffers == 0)
+                       continue;
+               CX18_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n",
+                         s->name, s->s_flags,
+                         (s->buffers - s->q_free.buffers) * 100 / s->buffers,
+                         (s->buffers * s->buf_size) / 1024, s->buffers);
+       }
+       CX18_INFO("Read MPEG/VBI: %lld/%lld bytes\n",
+                       (long long)cx->mpg_data_received,
+                       (long long)cx->vbi_data_inserted);
+       CX18_INFO("==================  END STATUS CARD #%d  ==================\n", cx->num);
+       return 0;
+}
+
+static int cx18_default(struct file *file, void *fh, int cmd, void *arg)
+{
+       struct cx18 *cx = ((struct cx18_open_id *)fh)->cx;
 
-       /* check priority */
        switch (cmd) {
-       case VIDIOC_S_CTRL:
-       case VIDIOC_S_STD:
-       case VIDIOC_S_INPUT:
-       case VIDIOC_S_TUNER:
-       case VIDIOC_S_FREQUENCY:
-       case VIDIOC_S_FMT:
-       case VIDIOC_S_CROP:
-       case VIDIOC_S_EXT_CTRLS:
-               ret = v4l2_prio_check(&cx->prio, &id->prio);
-               if (ret)
-                       return ret;
+       case VIDIOC_INT_S_AUDIO_ROUTING: {
+               struct v4l2_routing *route = arg;
+
+               CX18_DEBUG_IOCTL("VIDIOC_INT_S_AUDIO_ROUTING(%d, %d)\n",
+                       route->input, route->output);
+               cx18_audio_set_route(cx, route);
+               break;
        }
 
-       switch (cmd) {
-       case VIDIOC_DBG_G_REGISTER:
-       case VIDIOC_DBG_S_REGISTER:
-       case VIDIOC_G_CHIP_IDENT:
-       case VIDIOC_INT_S_AUDIO_ROUTING:
-       case VIDIOC_INT_RESET:
-               if (cx18_debug & CX18_DBGFLG_IOCTL) {
-                       printk(KERN_INFO "cx18%d ioctl: ", cx->num);
-                       v4l_printk_ioctl(cmd);
-               }
-               return cx18_debug_ioctls(filp, cmd, arg);
-
-       case VIDIOC_G_PRIORITY:
-       case VIDIOC_S_PRIORITY:
-       case VIDIOC_QUERYCAP:
-       case VIDIOC_ENUMINPUT:
-       case VIDIOC_G_INPUT:
-       case VIDIOC_S_INPUT:
-       case VIDIOC_G_FMT:
-       case VIDIOC_S_FMT:
-       case VIDIOC_TRY_FMT:
-       case VIDIOC_ENUM_FMT:
-       case VIDIOC_CROPCAP:
-       case VIDIOC_G_CROP:
-       case VIDIOC_S_CROP:
-       case VIDIOC_G_FREQUENCY:
-       case VIDIOC_S_FREQUENCY:
-       case VIDIOC_ENUMSTD:
-       case VIDIOC_G_STD:
-       case VIDIOC_S_STD:
-       case VIDIOC_S_TUNER:
-       case VIDIOC_G_TUNER:
-       case VIDIOC_ENUMAUDIO:
-       case VIDIOC_S_AUDIO:
-       case VIDIOC_G_AUDIO:
-       case VIDIOC_G_SLICED_VBI_CAP:
-       case VIDIOC_LOG_STATUS:
-       case VIDIOC_G_ENC_INDEX:
-       case VIDIOC_ENCODER_CMD:
-       case VIDIOC_TRY_ENCODER_CMD:
-               if (cx18_debug & CX18_DBGFLG_IOCTL) {
-                       printk(KERN_INFO "cx18%d ioctl: ", cx->num);
-                       v4l_printk_ioctl(cmd);
-               }
-               return cx18_v4l2_ioctls(cx, filp, cmd, arg);
-
-       case VIDIOC_QUERYMENU:
-       case VIDIOC_QUERYCTRL:
-       case VIDIOC_S_CTRL:
-       case VIDIOC_G_CTRL:
-       case VIDIOC_S_EXT_CTRLS:
-       case VIDIOC_G_EXT_CTRLS:
-       case VIDIOC_TRY_EXT_CTRLS:
-               if (cx18_debug & CX18_DBGFLG_IOCTL) {
-                       printk(KERN_INFO "cx18%d ioctl: ", cx->num);
-                       v4l_printk_ioctl(cmd);
-               }
-               return cx18_control_ioctls(cx, cmd, arg);
+       case VIDIOC_INT_RESET: {
+               u32 val = *(u32 *)arg;
+
+               if ((val == 0) || (val & 0x01))
+                       cx18_reset_ir_gpio(&cx->i2c_algo_cb_data[0]);
+               break;
+       }
 
-       case 0x00005401:        /* Handle isatty() calls */
-               return -EINVAL;
        default:
-               return v4l_compat_translate_ioctl(inode, filp, cmd, arg,
-                                                  cx18_v4l2_do_ioctl);
+               return -EINVAL;
        }
        return 0;
 }
@@ -840,12 +772,65 @@ static int cx18_v4l2_do_ioctl(struct inode *inode, struct file *filp,
 int cx18_v4l2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
                    unsigned long arg)
 {
+       struct video_device *vfd = video_devdata(filp);
        struct cx18_open_id *id = (struct cx18_open_id *)filp->private_data;
        struct cx18 *cx = id->cx;
        int res;
 
        mutex_lock(&cx->serialize_lock);
-       res = video_usercopy(inode, filp, cmd, arg, cx18_v4l2_do_ioctl);
+
+       if (cx18_debug & CX18_DBGFLG_IOCTL)
+               vfd->debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG;
+       res = video_ioctl2(inode, filp, cmd, arg);
+       vfd->debug = 0;
        mutex_unlock(&cx->serialize_lock);
        return res;
 }
+
+void cx18_set_funcs(struct video_device *vdev)
+{
+       vdev->vidioc_querycap                = cx18_querycap;
+       vdev->vidioc_g_priority              = cx18_g_priority;
+       vdev->vidioc_s_priority              = cx18_s_priority;
+       vdev->vidioc_s_audio                 = cx18_s_audio;
+       vdev->vidioc_g_audio                 = cx18_g_audio;
+       vdev->vidioc_enumaudio               = cx18_enumaudio;
+       vdev->vidioc_enum_input              = cx18_enum_input;
+       vdev->vidioc_cropcap                 = cx18_cropcap;
+       vdev->vidioc_s_crop                  = cx18_s_crop;
+       vdev->vidioc_g_crop                  = cx18_g_crop;
+       vdev->vidioc_g_input                 = cx18_g_input;
+       vdev->vidioc_s_input                 = cx18_s_input;
+       vdev->vidioc_g_frequency             = cx18_g_frequency;
+       vdev->vidioc_s_frequency             = cx18_s_frequency;
+       vdev->vidioc_s_tuner                 = cx18_s_tuner;
+       vdev->vidioc_g_tuner                 = cx18_g_tuner;
+       vdev->vidioc_g_enc_index             = cx18_g_enc_index;
+       vdev->vidioc_g_std                   = cx18_g_std;
+       vdev->vidioc_s_std                   = cx18_s_std;
+       vdev->vidioc_log_status              = cx18_log_status;
+       vdev->vidioc_enum_fmt_vid_cap        = cx18_enum_fmt_vid_cap;
+       vdev->vidioc_encoder_cmd             = cx18_encoder_cmd;
+       vdev->vidioc_try_encoder_cmd         = cx18_try_encoder_cmd;
+       vdev->vidioc_g_fmt_vid_cap           = cx18_g_fmt_vid_cap;
+       vdev->vidioc_g_fmt_vbi_cap           = cx18_g_fmt_vbi_cap;
+       vdev->vidioc_g_fmt_sliced_vbi_cap    = cx18_g_fmt_sliced_vbi_cap;
+       vdev->vidioc_s_fmt_vid_cap           = cx18_s_fmt_vid_cap;
+       vdev->vidioc_s_fmt_vbi_cap           = cx18_s_fmt_vbi_cap;
+       vdev->vidioc_s_fmt_sliced_vbi_cap    = cx18_s_fmt_sliced_vbi_cap;
+       vdev->vidioc_try_fmt_vid_cap         = cx18_try_fmt_vid_cap;
+       vdev->vidioc_try_fmt_vbi_cap         = cx18_try_fmt_vbi_cap;
+       vdev->vidioc_try_fmt_sliced_vbi_cap  = cx18_try_fmt_sliced_vbi_cap;
+       vdev->vidioc_g_sliced_vbi_cap        = cx18_g_sliced_vbi_cap;
+       vdev->vidioc_g_chip_ident            = cx18_g_chip_ident;
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+       vdev->vidioc_g_register              = cx18_g_register;
+       vdev->vidioc_s_register              = cx18_s_register;
+#endif
+       vdev->vidioc_default                 = cx18_default;
+       vdev->vidioc_queryctrl               = cx18_queryctrl;
+       vdev->vidioc_querymenu               = cx18_querymenu;
+       vdev->vidioc_g_ext_ctrls             = cx18_g_ext_ctrls;
+       vdev->vidioc_s_ext_ctrls             = cx18_s_ext_ctrls;
+       vdev->vidioc_try_ext_ctrls           = cx18_try_ext_ctrls;
+}
index 9f4c7eb2897f91ebee7ffb9fad87faa18e2928f3..2222f679d86d5f2f68b7c5add6695a018242daca 100644 (file)
@@ -24,7 +24,9 @@
 u16 cx18_service2vbi(int type);
 void cx18_expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal);
 u16 cx18_get_service_set(struct v4l2_sliced_vbi_format *fmt);
+void cx18_set_funcs(struct video_device *vdev);
+int cx18_s_std(struct file *file, void *fh, v4l2_std_id *std);
+int cx18_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf);
+int cx18_s_input(struct file *file, void *fh, unsigned int inp);
 int cx18_v4l2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
                    unsigned long arg);
-int cx18_v4l2_ioctls(struct cx18 *cx, struct file *filp, unsigned cmd,
-                    void *arg);
index 2a5ccef9185b4d04a87576afd6005e44db967c20..93177514e846e8c5ab73ce485976ac8b424e773f 100644 (file)
@@ -81,6 +81,7 @@ static const struct cx18_api_info api_info[] = {
        API_ENTRY(CPU, CX18_CPU_GET_ENC_PTS,                    0),
        API_ENTRY(CPU, CX18_CPU_DE_SET_MDL_ACK,                 0),
        API_ENTRY(CPU, CX18_CPU_DE_SET_MDL,                     API_FAST),
+       API_ENTRY(CPU, CX18_APU_RESETAI,                        API_FAST),
        API_ENTRY(0, 0,                                         0),
 };
 
index 1b921a33609226afb08a569caefc230d8cfe5df9..1728b1d832a9ca2eef5f147553da2e1d97a9e4f3 100644 (file)
@@ -39,6 +39,7 @@ static struct file_operations cx18_v4l2_enc_fops = {
        .owner = THIS_MODULE,
        .read = cx18_v4l2_read,
        .open = cx18_v4l2_open,
+       /* FIXME change to video_ioctl2 if serialization lock can be removed */
        .ioctl = cx18_v4l2_ioctl,
        .compat_ioctl = v4l_compat_ioctl32,
        .release = cx18_v4l2_close,
@@ -189,14 +190,15 @@ static int cx18_prep_dev(struct cx18 *cx, int type)
        s->v4l2dev->type =
                VID_TYPE_CAPTURE | VID_TYPE_TUNER | VID_TYPE_TELETEXT |
                VID_TYPE_CLIPPING | VID_TYPE_SCALES | VID_TYPE_MPEG_ENCODER;
-       snprintf(s->v4l2dev->name, sizeof(s->v4l2dev->name), "cx18%d %s",
-                       cx->num, s->name);
+       snprintf(s->v4l2dev->name, sizeof(s->v4l2dev->name), "cx18-%d",
+                       cx->num);
 
        s->v4l2dev->minor = minor;
        s->v4l2dev->dev = &cx->dev->dev;
        s->v4l2dev->fops = cx18_stream_info[type].fops;
        s->v4l2dev->release = video_device_release;
-
+       s->v4l2dev->tvnorms = V4L2_STD_ALL;
+       cx18_set_funcs(s->v4l2dev);
        return 0;
 }
 
@@ -309,8 +311,10 @@ void cx18_streams_cleanup(struct cx18 *cx, int unregister)
 
        /* Teardown all streams */
        for (type = 0; type < CX18_MAX_STREAMS; type++) {
-               if (cx->streams[type].dvb.enabled)
+               if (cx->streams[type].dvb.enabled) {
                        cx18_dvb_unregister(&cx->streams[type]);
+                       cx->streams[type].dvb.enabled = false;
+               }
 
                vdev = cx->streams[type].v4l2dev;
 
index 33f78da9dba84a2e14c8776b29ddbe34e9e62e0e..e7ed053059a8fdb4dc28a7cf5a7057b86e89971e 100644 (file)
 #define EPU_CMD_MASK_DEBUG                     (EPU_CMD_MASK | 0x000000)
 #define EPU_CMD_MASK_DE                        (EPU_CMD_MASK | 0x040000)
 
+#define APU_CMD_MASK                           0x10000000
+#define APU_CMD_MASK_ACK                       (APU_CMD_MASK | 0x80000000)
+
+#define CX18_APU_RESETAI                       (APU_CMD_MASK | 0x05)
+
 /* Description: This command indicates that a Memory Descriptor List has been
    filled with the requested channel type
    IN[0] - Task handle. Handle of the task
index c592899a23175f8a75febad62b3e39b8e930f9cb..22847a0444f59d051acd3e3d68bcbc2ad6852c50 100644 (file)
@@ -77,10 +77,65 @@ const u32 cx2341x_mpeg_ctrls[] = {
 };
 EXPORT_SYMBOL(cx2341x_mpeg_ctrls);
 
+static const struct cx2341x_mpeg_params default_params = {
+       /* misc */
+       .capabilities = 0,
+       .port = CX2341X_PORT_MEMORY,
+       .width = 720,
+       .height = 480,
+       .is_50hz = 0,
+
+       /* stream */
+       .stream_type = V4L2_MPEG_STREAM_TYPE_MPEG2_PS,
+       .stream_vbi_fmt = V4L2_MPEG_STREAM_VBI_FMT_NONE,
+       .stream_insert_nav_packets = 0,
+
+       /* audio */
+       .audio_sampling_freq = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000,
+       .audio_encoding = V4L2_MPEG_AUDIO_ENCODING_LAYER_2,
+       .audio_l2_bitrate = V4L2_MPEG_AUDIO_L2_BITRATE_224K,
+       .audio_mode = V4L2_MPEG_AUDIO_MODE_STEREO,
+       .audio_mode_extension = V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4,
+       .audio_emphasis = V4L2_MPEG_AUDIO_EMPHASIS_NONE,
+       .audio_crc = V4L2_MPEG_AUDIO_CRC_NONE,
+       .audio_mute = 0,
+
+       /* video */
+       .video_encoding = V4L2_MPEG_VIDEO_ENCODING_MPEG_2,
+       .video_aspect = V4L2_MPEG_VIDEO_ASPECT_4x3,
+       .video_b_frames = 2,
+       .video_gop_size = 12,
+       .video_gop_closure = 1,
+       .video_bitrate_mode = V4L2_MPEG_VIDEO_BITRATE_MODE_VBR,
+       .video_bitrate = 6000000,
+       .video_bitrate_peak = 8000000,
+       .video_temporal_decimation = 0,
+       .video_mute = 0,
+       .video_mute_yuv = 0x008080,  /* YCbCr value for black */
+
+       /* encoding filters */
+       .video_spatial_filter_mode =
+               V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL,
+       .video_spatial_filter = 0,
+       .video_luma_spatial_filter_type =
+               V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_HOR,
+       .video_chroma_spatial_filter_type =
+               V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR,
+       .video_temporal_filter_mode =
+               V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL,
+       .video_temporal_filter = 8,
+       .video_median_filter_type =
+               V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF,
+       .video_luma_median_filter_top = 255,
+       .video_luma_median_filter_bottom = 0,
+       .video_chroma_median_filter_top = 255,
+       .video_chroma_median_filter_bottom = 0,
+};
+
 
 /* Map the control ID to the correct field in the cx2341x_mpeg_params
    struct. Return -EINVAL if the ID is unknown, else return 0. */
-static int cx2341x_get_ctrl(struct cx2341x_mpeg_params *params,
+static int cx2341x_get_ctrl(const struct cx2341x_mpeg_params *params,
                struct v4l2_ext_control *ctrl)
 {
        switch (ctrl->id) {
@@ -420,7 +475,7 @@ static int cx2341x_ctrl_query_fill(struct v4l2_queryctrl *qctrl,
        return 0;
 }
 
-int cx2341x_ctrl_query(struct cx2341x_mpeg_params *params,
+int cx2341x_ctrl_query(const struct cx2341x_mpeg_params *params,
                       struct v4l2_queryctrl *qctrl)
 {
        int err;
@@ -430,13 +485,13 @@ int cx2341x_ctrl_query(struct cx2341x_mpeg_params *params,
                return v4l2_ctrl_query_fill(qctrl,
                                V4L2_MPEG_AUDIO_ENCODING_LAYER_2,
                                V4L2_MPEG_AUDIO_ENCODING_LAYER_2, 1,
-                               V4L2_MPEG_AUDIO_ENCODING_LAYER_2);
+                               default_params.audio_encoding);
 
        case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
                return v4l2_ctrl_query_fill(qctrl,
                                V4L2_MPEG_AUDIO_L2_BITRATE_192K,
                                V4L2_MPEG_AUDIO_L2_BITRATE_384K, 1,
-                               V4L2_MPEG_AUDIO_L2_BITRATE_224K);
+                               default_params.audio_l2_bitrate);
 
        case V4L2_CID_MPEG_AUDIO_L1_BITRATE:
        case V4L2_CID_MPEG_AUDIO_L3_BITRATE:
@@ -479,17 +534,22 @@ int cx2341x_ctrl_query(struct cx2341x_mpeg_params *params,
                return cx2341x_ctrl_query_fill(qctrl,
                                V4L2_MPEG_STREAM_VBI_FMT_NONE,
                                V4L2_MPEG_STREAM_VBI_FMT_NONE, 1,
-                               V4L2_MPEG_STREAM_VBI_FMT_NONE);
+                               default_params.stream_vbi_fmt);
+
+       case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
+               return v4l2_ctrl_query_fill(qctrl, 1, 34, 1,
+                               params->is_50hz ? 12 : 15);
 
        /* CX23415/6 specific */
        case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
                return cx2341x_ctrl_query_fill(qctrl,
                        V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL,
                        V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO, 1,
-                       V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL);
+                       default_params.video_spatial_filter_mode);
 
        case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER:
-               cx2341x_ctrl_query_fill(qctrl, 0, 15, 1, 0);
+               cx2341x_ctrl_query_fill(qctrl, 0, 15, 1,
+                               default_params.video_spatial_filter);
                qctrl->flags |= V4L2_CTRL_FLAG_SLIDER;
                if (params->video_spatial_filter_mode ==
                            V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO)
@@ -501,7 +561,7 @@ int cx2341x_ctrl_query(struct cx2341x_mpeg_params *params,
                        V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_OFF,
                        V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_SYM_NON_SEPARABLE,
                        1,
-                       V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_OFF);
+                       default_params.video_luma_spatial_filter_type);
                if (params->video_spatial_filter_mode ==
                            V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO)
                        qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
@@ -512,7 +572,7 @@ int cx2341x_ctrl_query(struct cx2341x_mpeg_params *params,
                    V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_OFF,
                    V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR,
                    1,
-                   V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_OFF);
+                   default_params.video_chroma_spatial_filter_type);
                if (params->video_spatial_filter_mode ==
                        V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO)
                        qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
@@ -522,10 +582,11 @@ int cx2341x_ctrl_query(struct cx2341x_mpeg_params *params,
                return cx2341x_ctrl_query_fill(qctrl,
                        V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL,
                        V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO, 1,
-                       V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL);
+                       default_params.video_temporal_filter_mode);
 
        case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER:
-               cx2341x_ctrl_query_fill(qctrl, 0, 31, 1, 0);
+               cx2341x_ctrl_query_fill(qctrl, 0, 31, 1,
+                               default_params.video_temporal_filter);
                qctrl->flags |= V4L2_CTRL_FLAG_SLIDER;
                if (params->video_temporal_filter_mode ==
                        V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO)
@@ -536,10 +597,11 @@ int cx2341x_ctrl_query(struct cx2341x_mpeg_params *params,
                return cx2341x_ctrl_query_fill(qctrl,
                        V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF,
                        V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_DIAG, 1,
-                       V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF);
+                       default_params.video_median_filter_type);
 
        case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP:
-               cx2341x_ctrl_query_fill(qctrl, 0, 255, 1, 255);
+               cx2341x_ctrl_query_fill(qctrl, 0, 255, 1,
+                               default_params.video_luma_median_filter_top);
                qctrl->flags |= V4L2_CTRL_FLAG_SLIDER;
                if (params->video_median_filter_type ==
                                V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF)
@@ -547,7 +609,8 @@ int cx2341x_ctrl_query(struct cx2341x_mpeg_params *params,
                return 0;
 
        case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM:
-               cx2341x_ctrl_query_fill(qctrl, 0, 255, 1, 0);
+               cx2341x_ctrl_query_fill(qctrl, 0, 255, 1,
+                               default_params.video_luma_median_filter_bottom);
                qctrl->flags |= V4L2_CTRL_FLAG_SLIDER;
                if (params->video_median_filter_type ==
                                V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF)
@@ -555,7 +618,8 @@ int cx2341x_ctrl_query(struct cx2341x_mpeg_params *params,
                return 0;
 
        case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP:
-               cx2341x_ctrl_query_fill(qctrl, 0, 255, 1, 255);
+               cx2341x_ctrl_query_fill(qctrl, 0, 255, 1,
+                               default_params.video_chroma_median_filter_top);
                qctrl->flags |= V4L2_CTRL_FLAG_SLIDER;
                if (params->video_median_filter_type ==
                                V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF)
@@ -563,7 +627,8 @@ int cx2341x_ctrl_query(struct cx2341x_mpeg_params *params,
                return 0;
 
        case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM:
-               cx2341x_ctrl_query_fill(qctrl, 0, 255, 1, 0);
+               cx2341x_ctrl_query_fill(qctrl, 0, 255, 1,
+                       default_params.video_chroma_median_filter_bottom);
                qctrl->flags |= V4L2_CTRL_FLAG_SLIDER;
                if (params->video_median_filter_type ==
                                V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF)
@@ -571,7 +636,8 @@ int cx2341x_ctrl_query(struct cx2341x_mpeg_params *params,
                return 0;
 
        case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS:
-               return cx2341x_ctrl_query_fill(qctrl, 0, 1, 1, 0);
+               return cx2341x_ctrl_query_fill(qctrl, 0, 1, 1,
+                               default_params.stream_insert_nav_packets);
 
        default:
                return v4l2_ctrl_query_fill_std(qctrl);
@@ -580,9 +646,9 @@ int cx2341x_ctrl_query(struct cx2341x_mpeg_params *params,
 }
 EXPORT_SYMBOL(cx2341x_ctrl_query);
 
-const char **cx2341x_ctrl_get_menu(u32 id)
+const char **cx2341x_ctrl_get_menu(const struct cx2341x_mpeg_params *p, u32 id)
 {
-       static const char *mpeg_stream_type[] = {
+       static const char *mpeg_stream_type_without_ts[] = {
                "MPEG-2 Program Stream",
                "",
                "MPEG-1 System Stream",
@@ -592,6 +658,16 @@ const char **cx2341x_ctrl_get_menu(u32 id)
                NULL
        };
 
+       static const char *mpeg_stream_type_with_ts[] = {
+               "MPEG-2 Program Stream",
+               "MPEG-2 Transport Stream",
+               "MPEG-1 System Stream",
+               "MPEG-2 DVD-compatible Stream",
+               "MPEG-1 VCD-compatible Stream",
+               "MPEG-2 SVCD-compatible Stream",
+               NULL
+       };
+
        static const char *cx2341x_video_spatial_filter_mode_menu[] = {
                "Manual",
                "Auto",
@@ -630,7 +706,8 @@ const char **cx2341x_ctrl_get_menu(u32 id)
 
        switch (id) {
        case V4L2_CID_MPEG_STREAM_TYPE:
-               return mpeg_stream_type;
+               return (p->capabilities & CX2341X_CAP_HAS_TS) ?
+                       mpeg_stream_type_with_ts : mpeg_stream_type_without_ts;
        case V4L2_CID_MPEG_AUDIO_L1_BITRATE:
        case V4L2_CID_MPEG_AUDIO_L3_BITRATE:
                return NULL;
@@ -690,7 +767,7 @@ int cx2341x_ext_ctrls(struct cx2341x_mpeg_params *params, int busy,
                if (err)
                        break;
                if (qctrl.type == V4L2_CTRL_TYPE_MENU)
-                       menu_items = cx2341x_ctrl_get_menu(qctrl.id);
+                       menu_items = cx2341x_ctrl_get_menu(params, qctrl.id);
                err = v4l2_ctrl_check(ctrl, &qctrl, menu_items);
                if (err)
                        break;
@@ -714,61 +791,6 @@ EXPORT_SYMBOL(cx2341x_ext_ctrls);
 
 void cx2341x_fill_defaults(struct cx2341x_mpeg_params *p)
 {
-       static struct cx2341x_mpeg_params default_params = {
-       /* misc */
-       .capabilities = 0,
-       .port = CX2341X_PORT_MEMORY,
-       .width = 720,
-       .height = 480,
-       .is_50hz = 0,
-
-       /* stream */
-       .stream_type = V4L2_MPEG_STREAM_TYPE_MPEG2_PS,
-       .stream_vbi_fmt = V4L2_MPEG_STREAM_VBI_FMT_NONE,
-       .stream_insert_nav_packets = 0,
-
-       /* audio */
-       .audio_sampling_freq = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000,
-       .audio_encoding = V4L2_MPEG_AUDIO_ENCODING_LAYER_2,
-       .audio_l2_bitrate = V4L2_MPEG_AUDIO_L2_BITRATE_224K,
-       .audio_mode = V4L2_MPEG_AUDIO_MODE_STEREO,
-       .audio_mode_extension = V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4,
-       .audio_emphasis = V4L2_MPEG_AUDIO_EMPHASIS_NONE,
-       .audio_crc = V4L2_MPEG_AUDIO_CRC_NONE,
-       .audio_mute = 0,
-
-       /* video */
-       .video_encoding = V4L2_MPEG_VIDEO_ENCODING_MPEG_2,
-       .video_aspect = V4L2_MPEG_VIDEO_ASPECT_4x3,
-       .video_b_frames = 2,
-       .video_gop_size = 12,
-       .video_gop_closure = 1,
-       .video_bitrate_mode = V4L2_MPEG_VIDEO_BITRATE_MODE_VBR,
-       .video_bitrate = 6000000,
-       .video_bitrate_peak = 8000000,
-       .video_temporal_decimation = 0,
-       .video_mute = 0,
-       .video_mute_yuv = 0x008080,  /* YCbCr value for black */
-
-       /* encoding filters */
-       .video_spatial_filter_mode =
-               V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL,
-       .video_spatial_filter = 0,
-       .video_luma_spatial_filter_type =
-               V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_HOR,
-       .video_chroma_spatial_filter_type =
-               V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR,
-       .video_temporal_filter_mode =
-               V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL,
-       .video_temporal_filter = 8,
-       .video_median_filter_type =
-               V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF,
-       .video_luma_median_filter_top = 255,
-       .video_luma_median_filter_bottom = 0,
-       .video_chroma_median_filter_top = 255,
-       .video_chroma_median_filter_bottom = 0,
-       };
-
        *p = default_params;
        cx2341x_calc_audio_properties(p);
 }
@@ -933,9 +955,9 @@ int cx2341x_update(void *priv, cx2341x_mbox_func func,
 }
 EXPORT_SYMBOL(cx2341x_update);
 
-static const char *cx2341x_menu_item(struct cx2341x_mpeg_params *p, u32 id)
+static const char *cx2341x_menu_item(const struct cx2341x_mpeg_params *p, u32 id)
 {
-       const char **menu = cx2341x_ctrl_get_menu(id);
+       const char **menu = cx2341x_ctrl_get_menu(p, id);
        struct v4l2_ext_control ctrl;
 
        if (menu == NULL)
@@ -952,7 +974,7 @@ invalid:
        return "<invalid>";
 }
 
-void cx2341x_log_status(struct cx2341x_mpeg_params *p, const char *prefix)
+void cx2341x_log_status(const struct cx2341x_mpeg_params *p, const char *prefix)
 {
        int is_mpeg1 = p->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1;
        int temporal = p->video_temporal_filter;
index 7bf14c9a15c71f37456f1af1f600ca2f42cb5add..5cfb46bbdaa91a3a28613ef3f1ad5d2d4e48111e 100644 (file)
@@ -9,11 +9,13 @@ config VIDEO_CX23885
        select VIDEO_TVEEPROM
        select VIDEO_IR
        select VIDEOBUF_DVB
+       select VIDEOBUF_DMA_SG
        select VIDEO_CX25840
        select VIDEO_CX2341X
        select DVB_DIB7000P if !DVB_FE_CUSTOMISE
        select MEDIA_TUNER_MT2131 if !DVB_FE_CUSTOMISE
        select DVB_S5H1409 if !DVB_FE_CUSTOMISE
+       select DVB_S5H1411 if !DVB_FE_CUSTOMISE
        select DVB_LGDT330X if !DVB_FE_CUSTOMISE
        select MEDIA_TUNER_XC2028 if !DVB_FE_CUSTOMIZE
        select MEDIA_TUNER_TDA8290 if !DVB_FE_CUSTOMIZE
index acdd3b6b3e7c254c8dfa28f2b102112eaf2c70a0..e7ef093265af0d2275ca149a98355f0423d7949c 100644 (file)
@@ -1173,379 +1173,404 @@ static int cx23885_querymenu(struct cx23885_dev *dev,
        qctrl.id = qmenu->id;
        cx23885_queryctrl(dev, &qctrl);
        return v4l2_ctrl_query_menu(qmenu, &qctrl,
-               cx2341x_ctrl_get_menu(qmenu->id));
+               cx2341x_ctrl_get_menu(&dev->mpeg_params, qmenu->id));
 }
 
-int cx23885_do_ioctl(struct inode *inode, struct file *file, int radio,
-       struct cx23885_dev *dev, unsigned int cmd, void *arg,
-       v4l2_kioctl driver_ioctl)
+static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *id)
 {
-       int err;
+       struct cx23885_fh  *fh  = file->private_data;
+       struct cx23885_dev *dev = fh->dev;
+       unsigned int i;
 
-       switch (cmd) {
-       /* ---------- tv norms ---------- */
-       case VIDIOC_ENUMSTD:
-       {
-               struct v4l2_standard *e = arg;
-               unsigned int i;
-
-               i = e->index;
-               if (i >= ARRAY_SIZE(cx23885_tvnorms))
-                       return -EINVAL;
-               err = v4l2_video_std_construct(e,
-                       cx23885_tvnorms[e->index].id,
-                       cx23885_tvnorms[e->index].name);
-               e->index = i;
-               if (err < 0)
-                       return err;
-               return 0;
-       }
-       case VIDIOC_G_STD:
-       {
-               v4l2_std_id *id = arg;
+       for (i = 0; i < ARRAY_SIZE(cx23885_tvnorms); i++)
+               if (*id & cx23885_tvnorms[i].id)
+                       break;
+       if (i == ARRAY_SIZE(cx23885_tvnorms))
+               return -EINVAL;
+       dev->encodernorm = cx23885_tvnorms[i];
+       return 0;
+}
 
-               *id = dev->encodernorm.id;
-               return 0;
-       }
-       case VIDIOC_S_STD:
-       {
-               v4l2_std_id *id = arg;
-               unsigned int i;
+static int vidioc_enum_input(struct file *file, void *priv,
+                               struct v4l2_input *i)
+{
+       struct cx23885_fh  *fh  = file->private_data;
+       struct cx23885_dev *dev = fh->dev;
+       struct cx23885_input *input;
+       unsigned int n;
 
-               for (i = 0; i < ARRAY_SIZE(cx23885_tvnorms); i++)
-                       if (*id & cx23885_tvnorms[i].id)
-                               break;
-               if (i == ARRAY_SIZE(cx23885_tvnorms))
-                       return -EINVAL;
-               dev->encodernorm = cx23885_tvnorms[i];
+       n = i->index;
 
-               return 0;
-       }
+       if (n >= 4)
+               return -EINVAL;
 
-       /* ------ input switching ---------- */
-       case VIDIOC_ENUMINPUT:
-       {
-               struct cx23885_input *input;
-               struct v4l2_input *i = arg;
-               unsigned int n;
-
-               n = i->index;
-               if (n >= 4)
-                       return -EINVAL;
-               input = &cx23885_boards[dev->board].input[n];
-               if (input->type == 0)
-                       return -EINVAL;
-               memset(i, 0, sizeof(*i));
-               i->index = n;
-               /* FIXME
-                * strcpy(i->name, input->name); */
-               strcpy(i->name, "unset");
-               if (input->type == CX23885_VMUX_TELEVISION ||
-                   input->type == CX23885_VMUX_CABLE)
-                       i->type = V4L2_INPUT_TYPE_TUNER;
-               else
-                       i->type  = V4L2_INPUT_TYPE_CAMERA;
+       input = &cx23885_boards[dev->board].input[n];
 
-               for (n = 0; n < ARRAY_SIZE(cx23885_tvnorms); n++)
-                       i->std |= cx23885_tvnorms[n].id;
-               return 0;
-       }
-       case VIDIOC_G_INPUT:
-       {
-               unsigned int *i = arg;
+       if (input->type == 0)
+               return -EINVAL;
 
-               *i = dev->input;
-               return 0;
-       }
-       case VIDIOC_S_INPUT:
-       {
-               unsigned int *i = arg;
+       memset(i, 0, sizeof(*i));
+       i->index = n;
 
-               if (*i >= 4)
-                       return -EINVAL;
+       /* FIXME
+        * strcpy(i->name, input->name); */
+       strcpy(i->name, "unset");
 
-               return 0;
-       }
+       if (input->type == CX23885_VMUX_TELEVISION ||
+           input->type == CX23885_VMUX_CABLE)
+               i->type = V4L2_INPUT_TYPE_TUNER;
+       else
+               i->type  = V4L2_INPUT_TYPE_CAMERA;
 
-       /* --- tuner ioctls ------------------------------------------ */
-       case VIDIOC_G_TUNER:
-       {
-               struct v4l2_tuner *t = arg;
+       for (n = 0; n < ARRAY_SIZE(cx23885_tvnorms); n++)
+               i->std |= cx23885_tvnorms[n].id;
+       return 0;
+}
 
-               if (UNSET == dev->tuner_type)
-                       return -EINVAL;
-               if (0 != t->index)
-                       return -EINVAL;
-               memset(t, 0, sizeof(*t));
-               strcpy(t->name, "Television");
-               cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_G_TUNER, t);
-               cx23885_call_i2c_clients(&dev->i2c_bus[1], VIDIOC_G_TUNER, t);
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+       struct cx23885_fh  *fh  = file->private_data;
+       struct cx23885_dev *dev = fh->dev;
 
-               dprintk(1, "VIDIOC_G_TUNER: tuner type %d\n", t->type);
+       *i = dev->input;
+       return 0;
+}
 
-               return 0;
-       }
-       case VIDIOC_S_TUNER:
-       {
-               struct v4l2_tuner *t = arg;
+static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+       if (i >= 4)
+               return -EINVAL;
 
-               if (UNSET == dev->tuner_type)
-                       return -EINVAL;
+       return 0;
+}
 
-               /* Update the A/V core */
-               cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_S_TUNER, t);
+static int vidioc_g_tuner(struct file *file, void *priv,
+                               struct v4l2_tuner *t)
+{
+       struct cx23885_fh  *fh  = file->private_data;
+       struct cx23885_dev *dev = fh->dev;
 
-               return 0;
-       }
-       case VIDIOC_G_FREQUENCY:
-       {
-               struct v4l2_frequency *f = arg;
+       if (UNSET == dev->tuner_type)
+               return -EINVAL;
+       if (0 != t->index)
+               return -EINVAL;
+       memset(t, 0, sizeof(*t));
+       strcpy(t->name, "Television");
+       cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_G_TUNER, t);
+       cx23885_call_i2c_clients(&dev->i2c_bus[1], VIDIOC_G_TUNER, t);
 
-               memset(f, 0, sizeof(*f));
-               if (UNSET == dev->tuner_type)
-                       return -EINVAL;
-               f->type = V4L2_TUNER_ANALOG_TV;
-               f->frequency = dev->freq;
+       dprintk(1, "VIDIOC_G_TUNER: tuner type %d\n", t->type);
 
-               /* Assumption that tuner is always on bus 1 */
-               cx23885_call_i2c_clients(&dev->i2c_bus[1],
-                       VIDIOC_G_FREQUENCY, f);
+       return 0;
+}
 
-               return 0;
-       }
-       case VIDIOC_S_FREQUENCY:
-       {
-               struct v4l2_frequency *f = arg;
-
-               dprintk(1, "VIDIOC_S_FREQUENCY: dev type %d, f\n",
-                       dev->tuner_type);
-               dprintk(1, "VIDIOC_S_FREQUENCY: f tuner %d, f type %d\n",
-                       f->tuner, f->type);
-               if (UNSET == dev->tuner_type)
-                       return -EINVAL;
-               if (f->tuner != 0)
-                       return -EINVAL;
-               if (f->type != V4L2_TUNER_ANALOG_TV)
-                       return -EINVAL;
-               dev->freq = f->frequency;
-
-               /* Assumption that tuner is always on bus 1 */
-               cx23885_call_i2c_clients(&dev->i2c_bus[1],
-                       VIDIOC_S_FREQUENCY, f);
-               return 0;
-       }
-       case VIDIOC_S_CTRL:
-       {
-               /* Update the A/V core */
-               cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_S_CTRL, arg);
-               return 0;
-       }
-       default:
-               /* Convert V4L ioctl to V4L2 and call mpeg_do_ioctl
-                * (driver_ioctl) */
-               return v4l_compat_translate_ioctl(inode, file, cmd, arg,
-                                                 driver_ioctl);
-       }
+static int vidioc_s_tuner(struct file *file, void *priv,
+                               struct v4l2_tuner *t)
+{
+       struct cx23885_fh  *fh  = file->private_data;
+       struct cx23885_dev *dev = fh->dev;
+
+       if (UNSET == dev->tuner_type)
+               return -EINVAL;
+
+       /* Update the A/V core */
+       cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_S_TUNER, t);
+
+       return 0;
+}
+
+static int vidioc_g_frequency(struct file *file, void *priv,
+                               struct v4l2_frequency *f)
+{
+       struct cx23885_fh  *fh  = file->private_data;
+       struct cx23885_dev *dev = fh->dev;
+
+       memset(f, 0, sizeof(*f));
+       if (UNSET == dev->tuner_type)
+               return -EINVAL;
+       f->type = V4L2_TUNER_ANALOG_TV;
+       f->frequency = dev->freq;
+
+       /* Assumption that tuner is always on bus 1 */
+       cx23885_call_i2c_clients(&dev->i2c_bus[1], VIDIOC_G_FREQUENCY, f);
+
+       return 0;
+}
+
+static int vidioc_s_frequency(struct file *file, void *priv,
+                               struct v4l2_frequency *f)
+{
+       struct cx23885_fh  *fh  = file->private_data;
+       struct cx23885_dev *dev = fh->dev;
+
+       cx23885_api_cmd(fh->dev, CX2341X_ENC_STOP_CAPTURE, 3, 0,
+               CX23885_END_NOW, CX23885_MPEG_CAPTURE,
+               CX23885_RAW_BITS_NONE);
+
+       dprintk(1, "VIDIOC_S_FREQUENCY: dev type %d, f\n",
+               dev->tuner_type);
+       dprintk(1, "VIDIOC_S_FREQUENCY: f tuner %d, f type %d\n",
+               f->tuner, f->type);
+       if (UNSET == dev->tuner_type)
+               return -EINVAL;
+       if (f->tuner != 0)
+               return -EINVAL;
+       if (f->type != V4L2_TUNER_ANALOG_TV)
+               return -EINVAL;
+       dev->freq = f->frequency;
+
+       /* Assumption that tuner is always on bus 1 */
+       cx23885_call_i2c_clients(&dev->i2c_bus[1], VIDIOC_S_FREQUENCY, f);
+
+       cx23885_initialize_codec(dev);
 
        return 0;
 }
 
-static int mpeg_do_ioctl(struct inode *inode, struct file *file,
-                        unsigned int cmd, void *arg)
+static int vidioc_s_ctrl(struct file *file, void *priv,
+                               struct v4l2_control *ctl)
+{
+       struct cx23885_fh  *fh  = file->private_data;
+       struct cx23885_dev *dev = fh->dev;
+
+       /* Update the A/V core */
+       cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_S_CTRL, ctl);
+       return 0;
+}
+
+static int vidioc_querycap(struct file *file, void  *priv,
+                               struct v4l2_capability *cap)
 {
        struct cx23885_fh  *fh  = file->private_data;
        struct cx23885_dev *dev = fh->dev;
        struct cx23885_tsport  *tsport = &dev->ts1;
 
-       if (v4l_debug > 1)
-               v4l_print_ioctl(dev->name, cmd);
+       memset(cap, 0, sizeof(*cap));
+       strcpy(cap->driver, dev->name);
+       strlcpy(cap->card, cx23885_boards[tsport->dev->board].name,
+               sizeof(cap->card));
+       sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
+       cap->version = CX23885_VERSION_CODE;
+       cap->capabilities =
+               V4L2_CAP_VIDEO_CAPTURE |
+               V4L2_CAP_READWRITE     |
+               V4L2_CAP_STREAMING     |
+               0;
+       if (UNSET != dev->tuner_type)
+               cap->capabilities |= V4L2_CAP_TUNER;
 
-       switch (cmd) {
+       return 0;
+}
 
-       /* --- capabilities ------------------------------------------ */
-       case VIDIOC_QUERYCAP:
-       {
-               struct v4l2_capability *cap = arg;
-
-               memset(cap, 0, sizeof(*cap));
-               strcpy(cap->driver, dev->name);
-               strlcpy(cap->card, cx23885_boards[tsport->dev->board].name,
-                       sizeof(cap->card));
-               sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
-               cap->version = CX23885_VERSION_CODE;
-               cap->capabilities =
-                       V4L2_CAP_VIDEO_CAPTURE |
-                       V4L2_CAP_READWRITE     |
-                       V4L2_CAP_STREAMING     |
-                       0;
-               if (UNSET != dev->tuner_type)
-                       cap->capabilities |= V4L2_CAP_TUNER;
-
-               return 0;
-       }
+static int vidioc_enum_fmt_vid_cap(struct file *file, void  *priv,
+                                       struct v4l2_fmtdesc *f)
+{
+       int index;
 
-       /* --- capture ioctls ---------------------------------------- */
-       case VIDIOC_ENUM_FMT:
-       {
-               struct v4l2_fmtdesc *f = arg;
-               int index;
-
-               index = f->index;
-               if (index != 0)
-                       return -EINVAL;
-
-               memset(f, 0, sizeof(*f));
-               f->index = index;
-               strlcpy(f->description, "MPEG", sizeof(f->description));
-               f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-               f->pixelformat = V4L2_PIX_FMT_MPEG;
-               return 0;
-       }
-       case VIDIOC_G_FMT:
-       {
-               struct v4l2_format *f = arg;
-
-               memset(f, 0, sizeof(*f));
-               f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-               f->fmt.pix.pixelformat  = V4L2_PIX_FMT_MPEG;
-               f->fmt.pix.bytesperline = 0;
-               f->fmt.pix.sizeimage    =
-                       dev->ts1.ts_packet_size * dev->ts1.ts_packet_count;
-               f->fmt.pix.colorspace   = 0;
-               f->fmt.pix.width        = dev->ts1.width;
-               f->fmt.pix.height       = dev->ts1.height;
-               f->fmt.pix.field        = fh->mpegq.field;
-               dprintk(1, "VIDIOC_G_FMT: w: %d, h: %d, f: %d\n",
-                       dev->ts1.width, dev->ts1.height, fh->mpegq.field);
-               return 0;
-       }
-       case VIDIOC_TRY_FMT:
-       {
-               struct v4l2_format *f = arg;
-
-               f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-               f->fmt.pix.pixelformat  = V4L2_PIX_FMT_MPEG;
-               f->fmt.pix.bytesperline = 0;
-               f->fmt.pix.sizeimage    =
-                       dev->ts1.ts_packet_size * dev->ts1.ts_packet_count;
-               f->fmt.pix.sizeimage    =
-               f->fmt.pix.colorspace   = 0;
-               dprintk(1, "VIDIOC_TRY_FMT: w: %d, h: %d, f: %d\n",
-                       dev->ts1.width, dev->ts1.height, fh->mpegq.field);
-               return 0;
-       }
-       case VIDIOC_S_FMT:
-       {
-               struct v4l2_format *f = arg;
-
-               f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-               f->fmt.pix.pixelformat  = V4L2_PIX_FMT_MPEG;
-               f->fmt.pix.bytesperline = 0;
-               f->fmt.pix.sizeimage    =
-                       dev->ts1.ts_packet_size * dev->ts1.ts_packet_count;
-               f->fmt.pix.colorspace   = 0;
-               dprintk(1, "VIDIOC_S_FMT: w: %d, h: %d, f: %d\n",
-                       f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field);
-               return 0;
-       }
+       index = f->index;
+       if (index != 0)
+               return -EINVAL;
 
-       /* --- streaming capture ------------------------------------- */
-       case VIDIOC_REQBUFS:
-               return videobuf_reqbufs(&fh->mpegq, arg);
+       memset(f, 0, sizeof(*f));
+       f->index = index;
+       strlcpy(f->description, "MPEG", sizeof(f->description));
+       f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       f->pixelformat = V4L2_PIX_FMT_MPEG;
 
-       case VIDIOC_QUERYBUF:
-               return videobuf_querybuf(&fh->mpegq, arg);
+       return 0;
+}
 
-       case VIDIOC_QBUF:
-               return videobuf_qbuf(&fh->mpegq, arg);
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+                               struct v4l2_format *f)
+{
+       struct cx23885_fh  *fh  = file->private_data;
+       struct cx23885_dev *dev = fh->dev;
 
-       case VIDIOC_DQBUF:
-               return videobuf_dqbuf(&fh->mpegq, arg,
-                                     file->f_flags & O_NONBLOCK);
+       memset(f, 0, sizeof(*f));
+       f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       f->fmt.pix.pixelformat  = V4L2_PIX_FMT_MPEG;
+       f->fmt.pix.bytesperline = 0;
+       f->fmt.pix.sizeimage    =
+               dev->ts1.ts_packet_size * dev->ts1.ts_packet_count;
+       f->fmt.pix.colorspace   = 0;
+       f->fmt.pix.width        = dev->ts1.width;
+       f->fmt.pix.height       = dev->ts1.height;
+       f->fmt.pix.field        = fh->mpegq.field;
+       dprintk(1, "VIDIOC_G_FMT: w: %d, h: %d, f: %d\n",
+               dev->ts1.width, dev->ts1.height, fh->mpegq.field);
+       return 0;
+}
 
-       case VIDIOC_STREAMON:
-               return videobuf_streamon(&fh->mpegq);
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+                               struct v4l2_format *f)
+{
+       struct cx23885_fh  *fh  = file->private_data;
+       struct cx23885_dev *dev = fh->dev;
 
-       case VIDIOC_STREAMOFF:
-               return videobuf_streamoff(&fh->mpegq);
+       f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       f->fmt.pix.pixelformat  = V4L2_PIX_FMT_MPEG;
+       f->fmt.pix.bytesperline = 0;
+       f->fmt.pix.sizeimage    =
+               dev->ts1.ts_packet_size * dev->ts1.ts_packet_count;
+       f->fmt.pix.sizeimage    =
+       f->fmt.pix.colorspace   = 0;
+       dprintk(1, "VIDIOC_TRY_FMT: w: %d, h: %d, f: %d\n",
+               dev->ts1.width, dev->ts1.height, fh->mpegq.field);
+       return 0;
+}
 
-       case VIDIOC_G_EXT_CTRLS:
-       {
-               struct v4l2_ext_controls *f = arg;
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+                               struct v4l2_format *f)
+{
+       struct cx23885_fh  *fh  = file->private_data;
+       struct cx23885_dev *dev = fh->dev;
 
-               if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG)
-                       return -EINVAL;
-               return cx2341x_ext_ctrls(&dev->mpeg_params, 0, f, cmd);
-       }
-       case VIDIOC_S_EXT_CTRLS:
-       case VIDIOC_TRY_EXT_CTRLS:
-       {
-               struct v4l2_ext_controls *f = arg;
-               struct cx2341x_mpeg_params p;
-               int err;
-
-               if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG)
-                       return -EINVAL;
-               p = dev->mpeg_params;
-               err = cx2341x_ext_ctrls(&p, 0, f, cmd);
-               if (err == 0 && cmd == VIDIOC_S_EXT_CTRLS) {
-                       err = cx2341x_update(dev, cx23885_mbox_func,
-                               &dev->mpeg_params, &p);
-                       dev->mpeg_params = p;
-               }
-               return err;
-       }
-       case VIDIOC_S_FREQUENCY:
-       {
-               cx23885_api_cmd(fh->dev, CX2341X_ENC_STOP_CAPTURE, 3, 0,
-                       CX23885_END_NOW, CX23885_MPEG_CAPTURE,
-                       CX23885_RAW_BITS_NONE);
-               cx23885_do_ioctl(inode, file, 0, dev, cmd, arg,
-                       mpeg_do_ioctl);
-               cx23885_initialize_codec(dev);
-
-               return 0;
-       }
-       case VIDIOC_LOG_STATUS:
-       {
-               char name[32 + 2];
-
-               snprintf(name, sizeof(name), "%s/2", dev->name);
-               printk(KERN_INFO
-                       "%s/2: ============  START LOG STATUS  ============\n",
-                      dev->name);
-               cx23885_call_i2c_clients(&dev->i2c_bus[0], VIDIOC_LOG_STATUS,
-                       NULL);
-               cx23885_call_i2c_clients(&dev->i2c_bus[1], VIDIOC_LOG_STATUS,
-                       NULL);
-               cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_LOG_STATUS,
-                       NULL);
-               cx2341x_log_status(&dev->mpeg_params, name);
-               printk(KERN_INFO
-                       "%s/2: =============  END LOG STATUS  =============\n",
-                      dev->name);
-               return 0;
-       }
-       case VIDIOC_QUERYMENU:
-               return cx23885_querymenu(dev, arg);
-       case VIDIOC_QUERYCTRL:
-       {
-               struct v4l2_queryctrl *c = arg;
+       f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       f->fmt.pix.pixelformat  = V4L2_PIX_FMT_MPEG;
+       f->fmt.pix.bytesperline = 0;
+       f->fmt.pix.sizeimage    =
+               dev->ts1.ts_packet_size * dev->ts1.ts_packet_count;
+       f->fmt.pix.colorspace   = 0;
+       dprintk(1, "VIDIOC_S_FMT: w: %d, h: %d, f: %d\n",
+               f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field);
+       return 0;
+}
 
-               return cx23885_queryctrl(dev, c);
-       }
+static int vidioc_reqbufs(struct file *file, void *priv,
+                               struct v4l2_requestbuffers *p)
+{
+       struct cx23885_fh  *fh  = file->private_data;
 
-       default:
-               return cx23885_do_ioctl(inode, file, 0, dev, cmd, arg,
-                               mpeg_do_ioctl);
+       return videobuf_reqbufs(&fh->mpegq, p);
+}
+
+static int vidioc_querybuf(struct file *file, void *priv,
+                               struct v4l2_buffer *p)
+{
+       struct cx23885_fh  *fh  = file->private_data;
+
+       return videobuf_querybuf(&fh->mpegq, p);
+}
+
+static int vidioc_qbuf(struct file *file, void *priv,
+                               struct v4l2_buffer *p)
+{
+       struct cx23885_fh  *fh  = file->private_data;
+
+       return videobuf_qbuf(&fh->mpegq, p);
+}
+
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+       struct cx23885_fh  *fh  = priv;
+
+       return videobuf_dqbuf(&fh->mpegq, b, file->f_flags & O_NONBLOCK);
+}
+
+
+static int vidioc_streamon(struct file *file, void *priv,
+                               enum v4l2_buf_type i)
+{
+       struct cx23885_fh  *fh  = file->private_data;
+
+       return videobuf_streamon(&fh->mpegq);
+}
+
+static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+       struct cx23885_fh  *fh  = file->private_data;
+
+       return videobuf_streamoff(&fh->mpegq);
+}
+
+static int vidioc_g_ext_ctrls(struct file *file, void *priv,
+                               struct v4l2_ext_controls *f)
+{
+       struct cx23885_fh  *fh  = priv;
+       struct cx23885_dev *dev = fh->dev;
+
+       if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG)
+               return -EINVAL;
+       return cx2341x_ext_ctrls(&dev->mpeg_params, 0, f, VIDIOC_G_EXT_CTRLS);
+}
+
+static int vidioc_s_ext_ctrls(struct file *file, void *priv,
+                               struct v4l2_ext_controls *f)
+{
+       struct cx23885_fh  *fh  = priv;
+       struct cx23885_dev *dev = fh->dev;
+       struct cx2341x_mpeg_params p;
+       int err;
+
+       if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG)
+               return -EINVAL;
+
+       p = dev->mpeg_params;
+       err = cx2341x_ext_ctrls(&p, 0, f, VIDIOC_S_EXT_CTRLS);
+
+       if (err == 0) {
+               err = cx2341x_update(dev, cx23885_mbox_func,
+                       &dev->mpeg_params, &p);
+               dev->mpeg_params = p;
        }
+       return err;
+}
+
+static int vidioc_try_ext_ctrls(struct file *file, void *priv,
+                               struct v4l2_ext_controls *f)
+{
+       struct cx23885_fh  *fh  = priv;
+       struct cx23885_dev *dev = fh->dev;
+       struct cx2341x_mpeg_params p;
+       int err;
+
+       if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG)
+               return -EINVAL;
+
+       p = dev->mpeg_params;
+       err = cx2341x_ext_ctrls(&p, 0, f, VIDIOC_TRY_EXT_CTRLS);
+       return err;
+}
+
+static int vidioc_log_status(struct file *file, void *priv)
+{
+       struct cx23885_fh  *fh  = priv;
+       struct cx23885_dev *dev = fh->dev;
+       char name[32 + 2];
+
+       snprintf(name, sizeof(name), "%s/2", dev->name);
+       printk(KERN_INFO
+               "%s/2: ============  START LOG STATUS  ============\n",
+              dev->name);
+       cx23885_call_i2c_clients(&dev->i2c_bus[0], VIDIOC_LOG_STATUS,
+               NULL);
+       cx23885_call_i2c_clients(&dev->i2c_bus[1], VIDIOC_LOG_STATUS,
+               NULL);
+       cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_LOG_STATUS,
+               NULL);
+       cx2341x_log_status(&dev->mpeg_params, name);
+       printk(KERN_INFO
+               "%s/2: =============  END LOG STATUS  =============\n",
+              dev->name);
        return 0;
 }
 
-static int mpeg_ioctl(struct inode *inode, struct file *file,
-                       unsigned int cmd, unsigned long arg)
+static int vidioc_querymenu(struct file *file, void *priv,
+                               struct v4l2_querymenu *a)
+{
+       struct cx23885_fh  *fh  = priv;
+       struct cx23885_dev *dev = fh->dev;
+
+       return cx23885_querymenu(dev, a);
+}
+
+static int vidioc_queryctrl(struct file *file, void *priv,
+                               struct v4l2_queryctrl *c)
 {
-       return video_usercopy(inode, file, cmd, arg, mpeg_do_ioctl);
+       struct cx23885_fh  *fh  = priv;
+       struct cx23885_dev *dev = fh->dev;
+
+       return cx23885_queryctrl(dev, c);
 }
 
 static int mpeg_open(struct inode *inode, struct file *file)
@@ -1670,7 +1695,7 @@ static struct file_operations mpeg_fops = {
        .read          = mpeg_read,
        .poll          = mpeg_poll,
        .mmap          = mpeg_mmap,
-       .ioctl         = mpeg_ioctl,
+       .ioctl         = video_ioctl2,
        .llseek        = no_llseek,
 };
 
@@ -1682,6 +1707,32 @@ static struct video_device cx23885_mpeg_template = {
                                VID_TYPE_MPEG_ENCODER,
        .fops          = &mpeg_fops,
        .minor         = -1,
+       .vidioc_s_std            = vidioc_s_std,
+       .vidioc_enum_input       = vidioc_enum_input,
+       .vidioc_g_input          = vidioc_g_input,
+       .vidioc_s_input          = vidioc_s_input,
+       .vidioc_g_tuner          = vidioc_g_tuner,
+       .vidioc_s_tuner          = vidioc_s_tuner,
+       .vidioc_g_frequency      = vidioc_g_frequency,
+       .vidioc_s_frequency      = vidioc_s_frequency,
+       .vidioc_s_ctrl           = vidioc_s_ctrl,
+       .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_try_fmt_vid_cap  = vidioc_try_fmt_vid_cap,
+       .vidioc_s_fmt_vid_cap    = vidioc_s_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_g_ext_ctrls      = vidioc_g_ext_ctrls,
+       .vidioc_s_ext_ctrls      = vidioc_s_ext_ctrls,
+       .vidioc_try_ext_ctrls    = vidioc_try_ext_ctrls,
+       .vidioc_log_status       = vidioc_log_status,
+       .vidioc_querymenu        = vidioc_querymenu,
+       .vidioc_queryctrl        = vidioc_queryctrl,
 };
 
 void cx23885_417_unregister(struct cx23885_dev *dev)
index 20e05f230546b66979f65fde727f5f6d30c43203..fd7112c11d35bfc8658628e7ca15d796d7d6c3a9 100644 (file)
@@ -143,6 +143,10 @@ struct cx23885_board cx23885_boards[] = {
                .name           = "Hauppauge WinTV-HVR1400",
                .portc          = CX23885_MPEG_DVB,
        },
+       [CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP] = {
+               .name           = "DViCO FusionHDTV7 Dual Express",
+               .portc          = CX23885_MPEG_DVB,
+       },
 };
 const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards);
 
@@ -210,6 +214,10 @@ struct cx23885_subid cx23885_subids[] = {
                .subvendor = 0x0070,
                .subdevice = 0x8010,
                .card      = CX23885_BOARD_HAUPPAUGE_HVR1400,
+       },{
+               .subvendor = 0x18ac,
+               .subdevice = 0xd618,
+               .card      = CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP,
        },
 };
 const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids);
@@ -477,6 +485,11 @@ void cx23885_card_setup(struct cx23885_dev *dev)
        }
 
        switch (dev->board) {
+       case CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP:
+               ts2->gen_ctrl_val  = 0xc; /* Serial bus + punctured clock */
+               ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+               ts2->src_sel_val   = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+               /* break omitted intentionally */
        case CX23885_BOARD_DVICO_FUSIONHDTV_5_EXP:
                ts1->gen_ctrl_val  = 0xc; /* Serial bus + punctured clock */
                ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
index c4cc2f3b887643dc5facfc9ce3e1d9f5d7210c38..d17343ea0d33bc097ef9f144bb2c45b83c7f7113 100644 (file)
@@ -291,9 +291,9 @@ int cx23885_sram_channel_setup(struct cx23885_dev *dev,
                lines = 6;
        BUG_ON(lines < 2);
 
-       cx_write(8 + 0, cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC) );
-       cx_write(8 + 4, cpu_to_le32(8) );
-       cx_write(8 + 8, cpu_to_le32(0) );
+       cx_write(8 + 0, RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC);
+       cx_write(8 + 4, 8);
+       cx_write(8 + 8, 0);
 
        /* write CDT */
        for (i = 0; i < lines; i++) {
@@ -408,11 +408,11 @@ static void cx23885_risc_disasm(struct cx23885_tsport *port,
               dev->name, risc->cpu, (unsigned long)risc->dma);
        for (i = 0; i < (risc->size >> 2); i += n) {
                printk("%s:   %04d: ", dev->name, i);
-               n = cx23885_risc_decode(risc->cpu[i]);
+               n = cx23885_risc_decode(le32_to_cpu(risc->cpu[i]));
                for (j = 1; j < n; j++)
                        printk("%s:   %04d: 0x%08x [ arg #%d ]\n",
                               dev->name, i + j, risc->cpu[i + j], j);
-               if (risc->cpu[i] == RISC_JUMP)
+               if (risc->cpu[i] == cpu_to_le32(RISC_JUMP))
                        break;
        }
 }
index 022aa391937a673b7780d16dae2e22cd25e14925..0a2e6558cd669aee9377cd68567fe246eb53cdff 100644 (file)
@@ -31,6 +31,7 @@
 #include <media/v4l2-common.h>
 
 #include "s5h1409.h"
+#include "s5h1411.h"
 #include "mt2131.h"
 #include "tda8290.h"
 #include "tda18271.h"
@@ -164,12 +165,38 @@ static struct s5h1409_config hauppauge_hvr1500q_config = {
        .mpeg_timing   = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
 };
 
+static struct s5h1409_config dvico_s5h1409_config = {
+       .demod_address = 0x32 >> 1,
+       .output_mode   = S5H1409_SERIAL_OUTPUT,
+       .gpio          = S5H1409_GPIO_ON,
+       .qam_if        = 44000,
+       .inversion     = S5H1409_INVERSION_OFF,
+       .status_mode   = S5H1409_DEMODLOCKING,
+       .mpeg_timing   = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+};
+
+static struct s5h1411_config dvico_s5h1411_config = {
+       .output_mode   = S5H1411_SERIAL_OUTPUT,
+       .gpio          = S5H1411_GPIO_ON,
+       .qam_if        = S5H1411_IF_44000,
+       .vsb_if        = S5H1411_IF_44000,
+       .inversion     = S5H1411_INVERSION_OFF,
+       .status_mode   = S5H1411_DEMODLOCKING,
+       .mpeg_timing   = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+};
+
 static struct xc5000_config hauppauge_hvr1500q_tunerconfig = {
        .i2c_address      = 0x61,
        .if_khz           = 5380,
        .tuner_callback   = cx23885_tuner_callback
 };
 
+static struct xc5000_config dvico_xc5000_tunerconfig = {
+       .i2c_address      = 0x64,
+       .if_khz           = 5380,
+       .tuner_callback   = cx23885_tuner_callback
+};
+
 static struct tda829x_config tda829x_no_probe = {
        .probe_tuner = TDA829X_DONT_PROBE,
 };
@@ -453,6 +480,21 @@ static int dvb_register(struct cx23885_tsport *port)
                                fe->ops.tuner_ops.set_config(fe, &ctl);
                }
                break;
+       case CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP:
+               i2c_bus = &dev->i2c_bus[port->nr - 1];
+
+               port->dvb.frontend = dvb_attach(s5h1409_attach,
+                                               &dvico_s5h1409_config,
+                                               &i2c_bus->i2c_adap);
+               if (port->dvb.frontend == NULL)
+                       port->dvb.frontend = dvb_attach(s5h1411_attach,
+                                                       &dvico_s5h1411_config,
+                                                       &i2c_bus->i2c_adap);
+               if (port->dvb.frontend != NULL)
+                       dvb_attach(xc5000_attach, port->dvb.frontend,
+                               &i2c_bus->i2c_adap,
+                               &dvico_xc5000_tunerconfig, i2c_bus);
+               break;
        default:
                printk("%s: The frontend of your DVB/ATSC card isn't supported yet\n",
                       dev->name);
index 84652210a28c6fee3d24fbd626de2c4a56d2db97..043fc4e5c586c82ec547d595a48e8a6ed0580edf 100644 (file)
@@ -915,7 +915,7 @@ static void init_controls(struct cx23885_dev *dev)
 /* ------------------------------------------------------------------ */
 /* VIDEO IOCTLS                                                       */
 
-static int vidioc_g_fmt_cap(struct file *file, void *priv,
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
        struct v4l2_format *f)
 {
        struct cx23885_fh *fh   = priv;
@@ -932,7 +932,7 @@ static int vidioc_g_fmt_cap(struct file *file, void *priv,
        return 0;
 }
 
-static int vidioc_try_fmt_cap(struct file *file, void *priv,
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
        struct v4l2_format *f)
 {
        struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
@@ -983,7 +983,7 @@ static int vidioc_try_fmt_cap(struct file *file, void *priv,
        return 0;
 }
 
-static int vidioc_s_fmt_cap(struct file *file, void *priv,
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
        struct v4l2_format *f)
 {
        struct cx23885_fh *fh = priv;
@@ -991,7 +991,7 @@ static int vidioc_s_fmt_cap(struct file *file, void *priv,
        int err;
 
        dprintk(2, "%s()\n", __func__);
-       err = vidioc_try_fmt_cap(file, priv, f);
+       err = vidioc_try_fmt_vid_cap(file, priv, f);
 
        if (0 != err)
                return err;
@@ -1025,7 +1025,7 @@ static int vidioc_querycap(struct file *file, void  *priv,
        return 0;
 }
 
-static int vidioc_enum_fmt_cap(struct file *file, void  *priv,
+static int vidioc_enum_fmt_vid_cap(struct file *file, void  *priv,
        struct v4l2_fmtdesc *f)
 {
        if (unlikely(f->index >= ARRAY_SIZE(formats)))
@@ -1440,13 +1440,13 @@ static struct video_device cx23885_video_template = {
        .fops                 = &video_fops,
        .minor                = -1,
        .vidioc_querycap      = vidioc_querycap,
-       .vidioc_enum_fmt_cap  = vidioc_enum_fmt_cap,
-       .vidioc_g_fmt_cap     = vidioc_g_fmt_cap,
-       .vidioc_try_fmt_cap   = vidioc_try_fmt_cap,
-       .vidioc_s_fmt_cap     = vidioc_s_fmt_cap,
-       .vidioc_g_fmt_vbi     = cx23885_vbi_fmt,
-       .vidioc_try_fmt_vbi   = cx23885_vbi_fmt,
-       .vidioc_s_fmt_vbi     = cx23885_vbi_fmt,
+       .vidioc_enum_fmt_vid_cap  = vidioc_enum_fmt_vid_cap,
+       .vidioc_g_fmt_vid_cap     = vidioc_g_fmt_vid_cap,
+       .vidioc_try_fmt_vid_cap   = vidioc_try_fmt_vid_cap,
+       .vidioc_s_fmt_vid_cap     = vidioc_s_fmt_vid_cap,
+       .vidioc_g_fmt_vbi_cap     = cx23885_vbi_fmt,
+       .vidioc_try_fmt_vbi_cap   = cx23885_vbi_fmt,
+       .vidioc_s_fmt_vbi_cap     = cx23885_vbi_fmt,
        .vidioc_reqbufs       = vidioc_reqbufs,
        .vidioc_querybuf      = vidioc_querybuf,
        .vidioc_qbuf          = vidioc_qbuf,
index 32af87f25e7bbb769a711d1bd6cf125e1bfd3a39..00dfdc89d64128f26b21f79999743a614c2141b5 100644 (file)
@@ -63,6 +63,7 @@
 #define CX23885_BOARD_HAUPPAUGE_HVR1200        7
 #define CX23885_BOARD_HAUPPAUGE_HVR1700        8
 #define CX23885_BOARD_HAUPPAUGE_HVR1400        9
+#define CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP 10
 
 /* Currently unsupported by the driver: PAL/H, NTSC/Kr, SECAM B/G/H/LC */
 #define CX23885_NORMS (\
index 1da6f134888d6d524ee375d345c75574981706bc..e7bf4f4c1319a220f448f4b352aaace26e92c5c4 100644 (file)
@@ -50,7 +50,6 @@ MODULE_LICENSE("GPL");
 
 static unsigned short normal_i2c[] = { 0x88 >> 1, I2C_CLIENT_END };
 
-
 int cx25840_debug;
 
 module_param_named(debug,cx25840_debug, int, 0644);
@@ -238,7 +237,7 @@ static void cx25840_initialize(struct i2c_client *client)
        cx25840_write(client, 0x8d3, 0x1f);
        cx25840_write(client, 0x8e3, 0x03);
 
-       cx25840_vbi_setup(client);
+       cx25840_std_setup(client);
 
        /* trial and error says these are needed to get audio */
        cx25840_write(client, 0x914, 0xa0);
@@ -338,7 +337,7 @@ static void cx23885_initialize(struct i2c_client *client)
        finish_wait(&state->fw_wait, &wait);
        destroy_workqueue(q);
 
-       cx25840_vbi_setup(client);
+       cx25840_std_setup(client);
 
        /* (re)set input */
        set_input(client, state->vid_input, state->aud_input);
@@ -349,6 +348,153 @@ static void cx23885_initialize(struct i2c_client *client)
 
 /* ----------------------------------------------------------------------- */
 
+void cx25840_std_setup(struct i2c_client *client)
+{
+       struct cx25840_state *state = i2c_get_clientdata(client);
+       v4l2_std_id std = state->std;
+       int hblank, hactive, burst, vblank, vactive, sc;
+       int vblank656, src_decimation;
+       int luma_lpf, uv_lpf, comb;
+       u32 pll_int, pll_frac, pll_post;
+
+       /* datasheet startup, step 8d */
+       if (std & ~V4L2_STD_NTSC)
+               cx25840_write(client, 0x49f, 0x11);
+       else
+               cx25840_write(client, 0x49f, 0x14);
+
+       if (std & V4L2_STD_625_50) {
+               hblank = 132;
+               hactive = 720;
+               burst = 93;
+               vblank = 36;
+               vactive = 580;
+               vblank656 = 40;
+               src_decimation = 0x21f;
+               luma_lpf = 2;
+
+               if (std & V4L2_STD_SECAM) {
+                       uv_lpf = 0;
+                       comb = 0;
+                       sc = 0x0a425f;
+               } else if (std == V4L2_STD_PAL_Nc) {
+                       uv_lpf = 1;
+                       comb = 0x20;
+                       sc = 556453;
+               } else {
+                       uv_lpf = 1;
+                       comb = 0x20;
+                       sc = 688739;
+               }
+       } else {
+               hactive = 720;
+               hblank = 122;
+               vactive = 487;
+               luma_lpf = 1;
+               uv_lpf = 1;
+
+               src_decimation = 0x21f;
+               if (std == V4L2_STD_PAL_60) {
+                       vblank = 26;
+                       vblank656 = 26;
+                       burst = 0x5b;
+                       luma_lpf = 2;
+                       comb = 0x20;
+                       sc = 688739;
+               } else if (std == V4L2_STD_PAL_M) {
+                       vblank = 20;
+                       vblank656 = 24;
+                       burst = 0x61;
+                       comb = 0x20;
+                       sc = 555452;
+               } else {
+                       vblank = 26;
+                       vblank656 = 26;
+                       burst = 0x5b;
+                       comb = 0x66;
+                       sc = 556063;
+               }
+       }
+
+       /* DEBUG: Displays configured PLL frequency */
+       pll_int = cx25840_read(client, 0x108);
+       pll_frac = cx25840_read4(client, 0x10c) & 0x1ffffff;
+       pll_post = cx25840_read(client, 0x109);
+       v4l_dbg(1, cx25840_debug, client,
+                               "PLL regs = int: %u, frac: %u, post: %u\n",
+                               pll_int, pll_frac, pll_post);
+
+       if (pll_post) {
+               int fin, fsc;
+               int pll = (28636363L * ((((u64)pll_int) << 25L) + pll_frac)) >> 25L;
+
+               pll /= pll_post;
+               v4l_dbg(1, cx25840_debug, client, "PLL = %d.%06d MHz\n",
+                               pll / 1000000, pll % 1000000);
+               v4l_dbg(1, cx25840_debug, client, "PLL/8 = %d.%06d MHz\n",
+                               pll / 8000000, (pll / 8) % 1000000);
+
+               fin = ((u64)src_decimation * pll) >> 12;
+               v4l_dbg(1, cx25840_debug, client,
+                               "ADC Sampling freq = %d.%06d MHz\n",
+                               fin / 1000000, fin % 1000000);
+
+               fsc = (((u64)sc) * pll) >> 24L;
+               v4l_dbg(1, cx25840_debug, client,
+                               "Chroma sub-carrier freq = %d.%06d MHz\n",
+                               fsc / 1000000, fsc % 1000000);
+
+               v4l_dbg(1, cx25840_debug, client, "hblank %i, hactive %i, "
+                       "vblank %i, vactive %i, vblank656 %i, src_dec %i, "
+                       "burst 0x%02x, luma_lpf %i, uv_lpf %i, comb 0x%02x, "
+                       "sc 0x%06x\n",
+                       hblank, hactive, vblank, vactive, vblank656,
+                       src_decimation, burst, luma_lpf, uv_lpf, comb, sc);
+       }
+
+       /* Sets horizontal blanking delay and active lines */
+       cx25840_write(client, 0x470, hblank);
+       cx25840_write(client, 0x471,
+                       0xff & (((hblank >> 8) & 0x3) | (hactive << 4)));
+       cx25840_write(client, 0x472, hactive >> 4);
+
+       /* Sets burst gate delay */
+       cx25840_write(client, 0x473, burst);
+
+       /* Sets vertical blanking delay and active duration */
+       cx25840_write(client, 0x474, vblank);
+       cx25840_write(client, 0x475,
+                       0xff & (((vblank >> 8) & 0x3) | (vactive << 4)));
+       cx25840_write(client, 0x476, vactive >> 4);
+       cx25840_write(client, 0x477, vblank656);
+
+       /* Sets src decimation rate */
+       cx25840_write(client, 0x478, 0xff & src_decimation);
+       cx25840_write(client, 0x479, 0xff & (src_decimation >> 8));
+
+       /* Sets Luma and UV Low pass filters */
+       cx25840_write(client, 0x47a, luma_lpf << 6 | ((uv_lpf << 4) & 0x30));
+
+       /* Enables comb filters */
+       cx25840_write(client, 0x47b, comb);
+
+       /* Sets SC Step*/
+       cx25840_write(client, 0x47c, sc);
+       cx25840_write(client, 0x47d, 0xff & sc >> 8);
+       cx25840_write(client, 0x47e, 0xff & sc >> 16);
+
+       /* Sets VBI parameters */
+       if (std & V4L2_STD_625_50) {
+               cx25840_write(client, 0x47f, 0x01);
+               state->vbi_line_offset = 5;
+       } else {
+               cx25840_write(client, 0x47f, 0x00);
+               state->vbi_line_offset = 8;
+       }
+}
+
+/* ----------------------------------------------------------------------- */
+
 static void input_change(struct i2c_client *client)
 {
        struct cx25840_state *state = i2c_get_clientdata(client);
@@ -566,7 +712,7 @@ static int set_v4lstd(struct i2c_client *client)
        }
        cx25840_and_or(client, 0x400, ~0xf, fmt);
        cx25840_and_or(client, 0x403, ~0x3, pal_m);
-       cx25840_vbi_setup(client);
+       cx25840_std_setup(client);
        if (!state->is_cx25836)
                input_change(client);
        return 0;
@@ -1058,6 +1204,8 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd,
 
                switch (qc->id) {
                        case V4L2_CID_AUDIO_VOLUME:
+                               return v4l2_ctrl_query_fill(qc, 0, 65535,
+                                       65535 / 100, state->default_volume);
                        case V4L2_CID_AUDIO_MUTE:
                        case V4L2_CID_AUDIO_BALANCE:
                        case V4L2_CID_AUDIO_BASS:
@@ -1265,6 +1413,8 @@ static int cx25840_probe(struct i2c_client *client,
        state->pvr150_workaround = 0;
        state->audmode = V4L2_TUNER_MODE_LANG1;
        state->unmute_volume = -1;
+       state->default_volume = 228 - cx25840_read(client, 0x8d4);
+       state->default_volume = ((state->default_volume / 2) + 23) << 9;
        state->vbi_line_offset = 8;
        state->id = id;
        state->rev = device_id;
index 8bf797f48b09d0779d7f7a6423dc0a29954bbf7b..72916ba975a8a24108773a2f9487703114c414d4 100644 (file)
@@ -44,6 +44,7 @@ struct cx25840_state {
        u32 audclk_freq;
        int audmode;
        int unmute_volume; /* -1 if not muted */
+       int default_volume;
        int vbi_line_offset;
        u32 id;
        u32 rev;
@@ -61,6 +62,7 @@ int cx25840_write4(struct i2c_client *client, u16 addr, u32 value);
 u8 cx25840_read(struct i2c_client *client, u16 addr);
 u32 cx25840_read4(struct i2c_client *client, u16 addr);
 int cx25840_and_or(struct i2c_client *client, u16 addr, unsigned mask, u8 value);
+void cx25840_std_setup(struct i2c_client *client);
 
 /* ----------------------------------------------------------------------- */
 /* cx25850-firmware.c                                                      */
@@ -73,7 +75,6 @@ void cx25840_audio_set_path(struct i2c_client *client);
 
 /* ----------------------------------------------------------------------- */
 /* cx25850-vbi.c                                                           */
-void cx25840_vbi_setup(struct i2c_client *client);
 int cx25840_vbi(struct i2c_client *client, unsigned int cmd, void *arg);
 
 #endif
index c754b9d13369f580e1b6a02fd8da07dcdc414e42..69f2bbdbb929b1a9c41dad93cdfa97caca0d9122 100644 (file)
@@ -82,150 +82,6 @@ static int decode_vps(u8 * dst, u8 * p)
        return err & 0xf0;
 }
 
-void cx25840_vbi_setup(struct i2c_client *client)
-{
-       struct cx25840_state *state = i2c_get_clientdata(client);
-       v4l2_std_id std = state->std;
-       int hblank,hactive,burst,vblank,vactive,sc,vblank656,src_decimation;
-       int luma_lpf,uv_lpf, comb;
-       u32 pll_int,pll_frac,pll_post;
-
-       /* datasheet startup, step 8d */
-       if (std & ~V4L2_STD_NTSC) {
-               cx25840_write(client, 0x49f, 0x11);
-       } else {
-               cx25840_write(client, 0x49f, 0x14);
-       }
-
-       if (std & V4L2_STD_625_50) {
-               hblank=0x084;
-               hactive=0x2d0;
-               burst=0x5d;
-               vblank=0x024;
-               vactive=0x244;
-               vblank656=0x28;
-               src_decimation=0x21f;
-
-               luma_lpf=2;
-               if (std & V4L2_STD_SECAM) {
-                       uv_lpf=0;
-                       comb=0;
-                       sc=0x0a425f;
-               } else if (std == V4L2_STD_PAL_Nc) {
-                       uv_lpf=1;
-                       comb=0x20;
-                       sc=556453;
-               } else {
-                       uv_lpf=1;
-                       comb=0x20;
-                       sc=0x0a8263;
-               }
-       } else {
-               hactive=720;
-               hblank=122;
-               vactive=487;
-               luma_lpf=1;
-               uv_lpf=1;
-
-               src_decimation=0x21f;
-               if (std == V4L2_STD_PAL_60) {
-                       vblank=26;
-                       vblank656=26;
-                       burst=0x5b;
-                       luma_lpf=2;
-                       comb=0x20;
-                       sc=0x0a8263;
-               } else if (std == V4L2_STD_PAL_M) {
-                       vblank=20;
-                       vblank656=24;
-                       burst=0x61;
-                       comb=0x20;
-
-                       sc=555452;
-               } else {
-                       vblank=26;
-                       vblank656=26;
-                       burst=0x5b;
-                       comb=0x66;
-                       sc=556063;
-               }
-       }
-
-       /* DEBUG: Displays configured PLL frequency */
-       pll_int=cx25840_read(client, 0x108);
-       pll_frac=cx25840_read4(client, 0x10c)&0x1ffffff;
-       pll_post=cx25840_read(client, 0x109);
-       v4l_dbg(1, cx25840_debug, client,
-                               "PLL regs = int: %u, frac: %u, post: %u\n",
-                               pll_int,pll_frac,pll_post);
-
-       if (pll_post) {
-               int fin, fsc;
-               int pll= (28636363L*((((u64)pll_int)<<25L)+pll_frac)) >>25L;
-
-               pll/=pll_post;
-               v4l_dbg(1, cx25840_debug, client, "PLL = %d.%06d MHz\n",
-                                               pll/1000000, pll%1000000);
-               v4l_dbg(1, cx25840_debug, client, "PLL/8 = %d.%06d MHz\n",
-                                               pll/8000000, (pll/8)%1000000);
-
-               fin=((u64)src_decimation*pll)>>12;
-               v4l_dbg(1, cx25840_debug, client, "ADC Sampling freq = "
-                                               "%d.%06d MHz\n",
-                                               fin/1000000,fin%1000000);
-
-               fsc= (((u64)sc)*pll) >> 24L;
-               v4l_dbg(1, cx25840_debug, client, "Chroma sub-carrier freq = "
-                                               "%d.%06d MHz\n",
-                                               fsc/1000000,fsc%1000000);
-
-               v4l_dbg(1, cx25840_debug, client, "hblank %i, hactive %i, "
-                       "vblank %i, vactive %i, vblank656 %i, src_dec %i, "
-                       "burst 0x%02x, luma_lpf %i, uv_lpf %i, comb 0x%02x,"
-                       " sc 0x%06x\n",
-                       hblank, hactive, vblank, vactive, vblank656,
-                       src_decimation, burst, luma_lpf, uv_lpf, comb, sc);
-       }
-
-       /* Sets horizontal blanking delay and active lines */
-       cx25840_write(client, 0x470, hblank);
-       cx25840_write(client, 0x471, 0xff&(((hblank>>8)&0x3)|(hactive <<4)));
-       cx25840_write(client, 0x472, hactive>>4);
-
-       /* Sets burst gate delay */
-       cx25840_write(client, 0x473, burst);
-
-       /* Sets vertical blanking delay and active duration */
-       cx25840_write(client, 0x474, vblank);
-       cx25840_write(client, 0x475, 0xff&(((vblank>>8)&0x3)|(vactive <<4)));
-       cx25840_write(client, 0x476, vactive>>4);
-       cx25840_write(client, 0x477, vblank656);
-
-       /* Sets src decimation rate */
-       cx25840_write(client, 0x478, 0xff&src_decimation);
-       cx25840_write(client, 0x479, 0xff&(src_decimation>>8));
-
-       /* Sets Luma and UV Low pass filters */
-       cx25840_write(client, 0x47a, luma_lpf<<6|((uv_lpf<<4)&0x30));
-
-       /* Enables comb filters */
-       cx25840_write(client, 0x47b, comb);
-
-       /* Sets SC Step*/
-       cx25840_write(client, 0x47c, sc);
-       cx25840_write(client, 0x47d, 0xff&sc>>8);
-       cx25840_write(client, 0x47e, 0xff&sc>>16);
-
-       /* Sets VBI parameters */
-       if (std & V4L2_STD_625_50) {
-               cx25840_write(client, 0x47f, 0x01);
-               state->vbi_line_offset = 5;
-       } else {
-               cx25840_write(client, 0x47f, 0x00);
-               state->vbi_line_offset = 8;
-       }
-}
-
 int cx25840_vbi(struct i2c_client *client, unsigned int cmd, void *arg)
 {
        struct cx25840_state *state = i2c_get_clientdata(client);
@@ -292,8 +148,8 @@ int cx25840_vbi(struct i2c_client *client, unsigned int cmd, void *arg)
                        /* raw VBI */
                        memset(svbi, 0, sizeof(*svbi));
 
-                       /* Setup VBI */
-                       cx25840_vbi_setup(client);
+                       /* Setup standard */
+                       cx25840_std_setup(client);
 
                        /* VBI Offset */
                        cx25840_write(client, 0x47f, vbi_offset);
@@ -304,8 +160,8 @@ int cx25840_vbi(struct i2c_client *client, unsigned int cmd, void *arg)
                for (x = 0; x <= 23; x++)
                        lcr[x] = 0x00;
 
-               /* Setup VBI */
-               cx25840_vbi_setup(client);
+               /* Setup standard */
+               cx25840_std_setup(client);
 
                /* Sliced VBI */
                cx25840_write(client, 0x404, 0x32);     /* Ancillary data */
index 80c8883e54b5c6ef853d5e8560da3424044376e3..06f171ab61495b4180befde4da18ea8c5a5e14c1 100644 (file)
@@ -82,7 +82,6 @@ typedef struct cx88_audio_dev snd_cx88_card_t;
 
 
 
-
 /****************************************************************************
                        Module global static vars
  ****************************************************************************/
index 6c0c94c5ef91113e8b875b52f823c1e2cccb50c5..bfdca5847764fde66852c48a5ab85712d8340a63 100644 (file)
@@ -715,7 +715,8 @@ static int vidioc_querymenu (struct file *file, void *priv,
 
        qctrl.id = qmenu->id;
        blackbird_queryctrl(dev, &qctrl);
-       return v4l2_ctrl_query_menu(qmenu, &qctrl, cx2341x_ctrl_get_menu(qmenu->id));
+       return v4l2_ctrl_query_menu(qmenu, &qctrl,
+                       cx2341x_ctrl_get_menu(&dev->params, qmenu->id));
 }
 
 static int vidioc_querycap (struct file *file, void  *priv,
@@ -737,7 +738,7 @@ static int vidioc_querycap (struct file *file, void  *priv,
        return 0;
 }
 
-static int vidioc_enum_fmt_cap (struct file *file, void  *priv,
+static int vidioc_enum_fmt_vid_cap (struct file *file, void  *priv,
                                        struct v4l2_fmtdesc *f)
 {
        if (f->index != 0)
@@ -749,7 +750,7 @@ static int vidioc_enum_fmt_cap (struct file *file, void  *priv,
        return 0;
 }
 
-static int vidioc_g_fmt_cap (struct file *file, void *priv,
+static int vidioc_g_fmt_vid_cap (struct file *file, void *priv,
                                        struct v4l2_format *f)
 {
        struct cx8802_fh  *fh   = priv;
@@ -768,7 +769,7 @@ static int vidioc_g_fmt_cap (struct file *file, void *priv,
        return 0;
 }
 
-static int vidioc_try_fmt_cap (struct file *file, void *priv,
+static int vidioc_try_fmt_vid_cap (struct file *file, void *priv,
                        struct v4l2_format *f)
 {
        struct cx8802_fh  *fh   = priv;
@@ -784,7 +785,7 @@ static int vidioc_try_fmt_cap (struct file *file, void *priv,
        return 0;
 }
 
-static int vidioc_s_fmt_cap (struct file *file, void *priv,
+static int vidioc_s_fmt_vid_cap (struct file *file, void *priv,
                                        struct v4l2_format *f)
 {
        struct cx8802_fh  *fh   = priv;
@@ -1181,10 +1182,10 @@ static struct video_device cx8802_mpeg_template =
        .minor                = -1,
        .vidioc_querymenu     = vidioc_querymenu,
        .vidioc_querycap      = vidioc_querycap,
-       .vidioc_enum_fmt_cap  = vidioc_enum_fmt_cap,
-       .vidioc_g_fmt_cap     = vidioc_g_fmt_cap,
-       .vidioc_try_fmt_cap   = vidioc_try_fmt_cap,
-       .vidioc_s_fmt_cap     = vidioc_s_fmt_cap,
+       .vidioc_enum_fmt_vid_cap  = vidioc_enum_fmt_vid_cap,
+       .vidioc_g_fmt_vid_cap     = vidioc_g_fmt_vid_cap,
+       .vidioc_try_fmt_vid_cap   = vidioc_try_fmt_vid_cap,
+       .vidioc_s_fmt_vid_cap     = vidioc_s_fmt_vid_cap,
        .vidioc_reqbufs       = vidioc_reqbufs,
        .vidioc_querybuf      = vidioc_querybuf,
        .vidioc_qbuf          = vidioc_qbuf,
index cb6a096069c7b0fc7a0fcc093dc225d018948ba5..d7406a994f094942924b3726e3b41ba31ac1f19b 100644 (file)
@@ -103,7 +103,6 @@ static int attach_inform(struct i2c_client *client)
 
        dprintk(1, "%s i2c attach [addr=0x%x,client=%s]\n",
                client->driver->driver.name, client->addr, client->name);
-
        return 0;
 }
 
index eea23f95edb751d42f9dce1588f817038e0ba5bf..0fed5cd2cceaa7caa808afba923e70e47c765b8f 100644 (file)
@@ -1045,7 +1045,7 @@ static void init_controls(struct cx88_core *core)
 /* ------------------------------------------------------------------ */
 /* VIDEO IOCTLS                                                       */
 
-static int vidioc_g_fmt_cap (struct file *file, void *priv,
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
                                        struct v4l2_format *f)
 {
        struct cx8800_fh  *fh   = priv;
@@ -1061,7 +1061,7 @@ static int vidioc_g_fmt_cap (struct file *file, void *priv,
        return 0;
 }
 
-static int vidioc_try_fmt_cap (struct file *file, void *priv,
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
                        struct v4l2_format *f)
 {
        struct cx88_core  *core = ((struct cx8800_fh *)priv)->dev->core;
@@ -1112,11 +1112,11 @@ static int vidioc_try_fmt_cap (struct file *file, void *priv,
        return 0;
 }
 
-static int vidioc_s_fmt_cap (struct file *file, void *priv,
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
                                        struct v4l2_format *f)
 {
        struct cx8800_fh  *fh   = priv;
-       int err = vidioc_try_fmt_cap (file,priv,f);
+       int err = vidioc_try_fmt_vid_cap (file,priv,f);
 
        if (0 != err)
                return err;
@@ -1147,7 +1147,7 @@ static int vidioc_querycap (struct file *file, void  *priv,
        return 0;
 }
 
-static int vidioc_enum_fmt_cap (struct file *file, void  *priv,
+static int vidioc_enum_fmt_vid_cap (struct file *file, void  *priv,
                                        struct v4l2_fmtdesc *f)
 {
        if (unlikely(f->index >= ARRAY_SIZE(formats)))
@@ -1690,13 +1690,13 @@ static struct video_device cx8800_video_template =
        .fops                 = &video_fops,
        .minor                = -1,
        .vidioc_querycap      = vidioc_querycap,
-       .vidioc_enum_fmt_cap  = vidioc_enum_fmt_cap,
-       .vidioc_g_fmt_cap     = vidioc_g_fmt_cap,
-       .vidioc_try_fmt_cap   = vidioc_try_fmt_cap,
-       .vidioc_s_fmt_cap     = vidioc_s_fmt_cap,
-       .vidioc_g_fmt_vbi     = cx8800_vbi_fmt,
-       .vidioc_try_fmt_vbi   = cx8800_vbi_fmt,
-       .vidioc_s_fmt_vbi     = cx8800_vbi_fmt,
+       .vidioc_enum_fmt_vid_cap  = vidioc_enum_fmt_vid_cap,
+       .vidioc_g_fmt_vid_cap     = vidioc_g_fmt_vid_cap,
+       .vidioc_try_fmt_vid_cap   = vidioc_try_fmt_vid_cap,
+       .vidioc_s_fmt_vid_cap     = vidioc_s_fmt_vid_cap,
+       .vidioc_g_fmt_vbi_cap     = cx8800_vbi_fmt,
+       .vidioc_try_fmt_vbi_cap   = cx8800_vbi_fmt,
+       .vidioc_s_fmt_vbi_cap     = cx8800_vbi_fmt,
        .vidioc_reqbufs       = vidioc_reqbufs,
        .vidioc_querybuf      = vidioc_querybuf,
        .vidioc_qbuf          = vidioc_qbuf,
index 6ce5af48847170a12316df9474bbc01f23fd6984..20800425c51ed5d1f5c4e08f82ecaf183d75deec 100644 (file)
@@ -30,7 +30,6 @@
 #include "cx88.h"
 #include "cx88-vp3054-i2c.h"
 
-
 MODULE_DESCRIPTION("driver for cx2388x VP3054 design");
 MODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>");
 MODULE_LICENSE("GPL");
index 8cbda43727c3de59ba73a9427706b0961f7d80b1..05f0d5a1505843edcdad70beec452456a52d17dd 100644 (file)
@@ -173,6 +173,27 @@ struct em28xx_board em28xx_boards[] = {
                        .amux     = 1,
                } },
        },
+       [EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2] = {
+               .name         = "Hauppauge WinTV HVR 900 (R2)",
+               .vchannels    = 3,
+               .tda9887_conf = TDA9887_PRESENT,
+               .tuner_type   = TUNER_XC2028,
+               .mts_firmware = 1,
+               .decoder      = EM28XX_TVP5150,
+               .input          = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = 0,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = 1,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = 1,
+               } },
+       },
        [EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950] = {
                .name           = "Hauppauge WinTV HVR 950",
                .vchannels      = 3,
@@ -196,6 +217,29 @@ struct em28xx_board em28xx_boards[] = {
                        .amux     = 1,
                } },
        },
+       [EM2880_BOARD_PINNACLE_PCTV_HD_PRO] = {
+               .name           = "Pinnacle PCTV HD Pro Stick",
+               .vchannels      = 3,
+               .tda9887_conf   = TDA9887_PRESENT,
+               .tuner_type     = TUNER_XC2028,
+               .mts_firmware   = 1,
+               .has_12mhz_i2s  = 1,
+               .has_dvb        = 1,
+               .decoder        = EM28XX_TVP5150,
+               .input          = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = 0,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = 1,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = 1,
+               } },
+       },
        [EM2880_BOARD_TERRATEC_HYBRID_XS] = {
                .name         = "Terratec Hybrid XS",
                .vchannels    = 3,
@@ -382,6 +426,19 @@ struct em28xx_board em28xx_boards[] = {
                        .amux     = EM28XX_AMUX_LINE_IN,
                } },
        },
+       [EM2860_BOARD_POINTNIX_INTRAORAL_CAMERA] = {
+               .name         = "PointNix Intra-Oral Camera",
+               .has_snapshot_button = 1,
+               .vchannels    = 1,
+               .tda9887_conf = TDA9887_PRESENT,
+               .tuner_type   = TUNER_ABSENT,
+               .decoder      = EM28XX_SAA7113,
+               .input          = { {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = SAA7115_SVIDEO3,
+                       .amux     = 0,
+               } },
+       },
 };
 const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards);
 
@@ -417,10 +474,12 @@ struct usb_device_id em28xx_id_table [] = {
                        .driver_info = EM2820_BOARD_PINNACLE_DVC_90 },
        { USB_DEVICE(0x2304, 0x021a),
                        .driver_info = EM2820_BOARD_PINNACLE_DVC_90 },
+       { USB_DEVICE(0x2304, 0x0227),
+                       .driver_info = EM2880_BOARD_PINNACLE_PCTV_HD_PRO },
        { USB_DEVICE(0x2040, 0x6500),
                        .driver_info = EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900 },
        { USB_DEVICE(0x2040, 0x6502),
-                       .driver_info = EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900 },
+                       .driver_info = EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2 },
        { USB_DEVICE(0x2040, 0x6513), /* HCW HVR-980 */
                        .driver_info = EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950 },
        { USB_DEVICE(0x2040, 0x6517), /* HP  HVR-950 */
@@ -476,6 +535,7 @@ static struct em28xx_hash_table em28xx_eeprom_hash [] = {
 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_POINTNIX_INTRAORAL_CAMERA, TUNER_ABSENT},
 };
 
 int em28xx_tuner_callback(void *ptr, int command, int arg)
@@ -508,6 +568,7 @@ static void em28xx_set_model(struct em28xx *dev)
        dev->has_12mhz_i2s = em28xx_boards[dev->model].has_12mhz_i2s;
        dev->max_range_640_480 = em28xx_boards[dev->model].max_range_640_480;
        dev->has_dvb = em28xx_boards[dev->model].has_dvb;
+       dev->has_snapshot_button = em28xx_boards[dev->model].has_snapshot_button;
 }
 
 /* Since em28xx_pre_card_setup() requires a proper dev->model,
@@ -542,8 +603,10 @@ void em28xx_pre_card_setup(struct em28xx *dev)
        switch (dev->model) {
        case EM2880_BOARD_TERRATEC_PRODIGY_XS:
        case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900:
+       case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2:
        case EM2880_BOARD_TERRATEC_HYBRID_XS:
        case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950:
+       case EM2880_BOARD_PINNACLE_PCTV_HD_PRO:
                em28xx_write_regs(dev, EM28XX_R0F_XCLK,    "\x27", 1);
                em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1);
                msleep(50);
@@ -576,7 +639,12 @@ static void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl)
        case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900:
                ctl->demod = XC3028_FE_ZARLINK456;
                break;
+       case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2:
+               /* djh - Not sure which demod we need here */
+               ctl->demod = XC3028_FE_DEFAULT;
+               break;
        case EM2880_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;
@@ -754,6 +822,7 @@ void em28xx_card_setup(struct em28xx *dev)
        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 EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950:
        {
                struct tveeprom tv;
@@ -787,6 +856,9 @@ void em28xx_card_setup(struct em28xx *dev)
                        em28xx_set_model(dev);
        }
 
+       if (dev->has_snapshot_button)
+               em28xx_register_snapshot_button(dev);
+
        /* Allow override tuner type by a module parameter */
        if (tuner >= 0)
                dev->tuner_type = tuner;
index 0b2333ee07f8ea6c3bbf7200cd70f47b89c3a61f..cc61cfb23a4a6213e77a6e8e703491cbbe4bb1c8 100644 (file)
@@ -5,6 +5,7 @@
 
  (c) 2008 Devin Heitmueller <devin.heitmueller@gmail.com>
        - Fixes for the driver to properly work with HVR-950
+       - Fixes for the driver to properly work with Pinnacle PCTV HD Pro Stick
 
  (c) 2008 Aidan Thornton <makosoft@googlemail.com>
 
@@ -26,6 +27,9 @@
 
 #include "lgdt330x.h"
 #include "zl10353.h"
+#ifdef EM28XX_DRX397XD_SUPPORT
+#include "drx397xD.h"
+#endif
 
 MODULE_DESCRIPTION("driver for em28xx based DVB cards");
 MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
@@ -227,6 +231,13 @@ static struct zl10353_config em28xx_zl10353_with_xc3028 = {
        .if2 = 45600,
 };
 
+#ifdef EM28XX_DRX397XD_SUPPORT
+/* [TODO] djh - not sure yet what the device config needs to contain */
+static struct drx397xD_config em28xx_drx397xD_with_xc3028 = {
+       .demod_address = (0xe0 >> 1),
+};
+#endif
+
 /* ------------------------------------------------------------------ */
 
 static int attach_xc3028(u8 addr, struct em28xx *dev)
@@ -399,6 +410,7 @@ static int dvb_init(struct em28xx *dev)
        /* init frontend */
        switch (dev->model) {
        case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950:
+       case EM2880_BOARD_PINNACLE_PCTV_HD_PRO:
                dvb->frontend = dvb_attach(lgdt330x_attach,
                                           &em2880_lgdt3303_dev,
                                           &dev->i2c_adap);
@@ -416,6 +428,19 @@ static int dvb_init(struct em28xx *dev)
                        goto out_free;
                }
                break;
+       case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2:
+#ifdef EM28XX_DRX397XD_SUPPORT
+               /* We don't have the config structure properly populated, so
+                  this is commented out for now */
+               dvb->frontend = dvb_attach(drx397xD_attach,
+                                          &em28xx_drx397xD_with_xc3028,
+                                          &dev->i2c_adap);
+               if (attach_xc3028(0x61, dev) < 0) {
+                       result = -EINVAL;
+                       goto out_free;
+               }
+               break;
+#endif
        default:
                printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card"
                                " isn't supported yet\n",
index 6a78fd294cabfaef3343ce7fb9b94e85ea031bc0..97853384c9436f0d198d668bb3cdca789125e5ae 100644 (file)
@@ -432,7 +432,6 @@ static u32 functionality(struct i2c_adapter *adap)
        return I2C_FUNC_SMBUS_EMUL;
 }
 
-
 /*
  * attach_inform()
  * gets called when a device attaches to the i2c bus
index bb5807159b8d28638e46c01d7f0621861aedeed4..eab3d9511af3397c8aaa42be2cd116264d1ca892 100644 (file)
 
 #include "em28xx.h"
 
+#define EM28XX_SNAPSHOT_KEY KEY_CAMERA
+#define EM28XX_SBUTTON_QUERY_INTERVAL 500
+#define EM28XX_R0C_USBSUSP_SNAPSHOT 0x20
+
 static unsigned int ir_debug;
 module_param(ir_debug, int, 0644);
 MODULE_PARM_DESC(ir_debug, "enable debug messages [IR]");
@@ -124,6 +128,89 @@ int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key,
        return 1;
 }
 
+static void em28xx_query_sbutton(struct work_struct *work)
+{
+       /* Poll the register and see if the button is depressed */
+       struct em28xx *dev =
+               container_of(work, struct em28xx, sbutton_query_work.work);
+       int ret;
+
+       ret = em28xx_read_reg(dev, EM28XX_R0C_USBSUSP);
+
+       if (ret & EM28XX_R0C_USBSUSP_SNAPSHOT) {
+               u8 cleared;
+               /* Button is depressed, clear the register */
+               cleared = ((u8) ret) & ~EM28XX_R0C_USBSUSP_SNAPSHOT;
+               em28xx_write_regs(dev, EM28XX_R0C_USBSUSP, &cleared, 1);
+
+               /* Not emulate the keypress */
+               input_report_key(dev->sbutton_input_dev, EM28XX_SNAPSHOT_KEY,
+                                1);
+               /* Now unpress the key */
+               input_report_key(dev->sbutton_input_dev, EM28XX_SNAPSHOT_KEY,
+                                0);
+       }
+
+       /* Schedule next poll */
+       schedule_delayed_work(&dev->sbutton_query_work,
+                             msecs_to_jiffies(EM28XX_SBUTTON_QUERY_INTERVAL));
+}
+
+void em28xx_register_snapshot_button(struct em28xx *dev)
+{
+       struct input_dev *input_dev;
+       int err;
+
+       em28xx_info("Registering snapshot button...\n");
+       input_dev = input_allocate_device();
+       if (!input_dev) {
+               em28xx_errdev("input_allocate_device failed\n");
+               return;
+       }
+
+       usb_make_path(dev->udev, dev->snapshot_button_path,
+                     sizeof(dev->snapshot_button_path));
+       strlcat(dev->snapshot_button_path, "/sbutton",
+               sizeof(dev->snapshot_button_path));
+       INIT_DELAYED_WORK(&dev->sbutton_query_work, em28xx_query_sbutton);
+
+       input_dev->name = "em28xx snapshot button";
+       input_dev->phys = dev->snapshot_button_path;
+       input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
+       set_bit(EM28XX_SNAPSHOT_KEY, input_dev->keybit);
+       input_dev->keycodesize = 0;
+       input_dev->keycodemax = 0;
+       input_dev->id.bustype = BUS_USB;
+       input_dev->id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor);
+       input_dev->id.product = le16_to_cpu(dev->udev->descriptor.idProduct);
+       input_dev->id.version = 1;
+       input_dev->dev.parent = &dev->udev->dev;
+
+       err = input_register_device(input_dev);
+       if (err) {
+               em28xx_errdev("input_register_device failed\n");
+               input_free_device(input_dev);
+               return;
+       }
+
+       dev->sbutton_input_dev = input_dev;
+       schedule_delayed_work(&dev->sbutton_query_work,
+                             msecs_to_jiffies(EM28XX_SBUTTON_QUERY_INTERVAL));
+       return;
+
+}
+
+void em28xx_deregister_snapshot_button(struct em28xx *dev)
+{
+       if (dev->sbutton_input_dev != NULL) {
+               em28xx_info("Deregistering snapshot button\n");
+               cancel_rearming_delayed_work(&dev->sbutton_query_work);
+               input_unregister_device(dev->sbutton_input_dev);
+               dev->sbutton_input_dev = NULL;
+       }
+       return;
+}
+
 /* ----------------------------------------------------------------------
  * Local variables:
  * c-basic-offset: 8
index 285bc62bbe4670f203cde0e08c2c14e1e00743bc..2d9f14d2a00b817119abeb1d5f29a2886014232d 100644 (file)
@@ -683,7 +683,7 @@ static void get_scale(struct em28xx *dev,
        IOCTL vidioc handling
    ------------------------------------------------------------------*/
 
-static int vidioc_g_fmt_cap(struct file *file, void *priv,
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
                                        struct v4l2_format *f)
 {
        struct em28xx_fh      *fh  = priv;
@@ -706,7 +706,7 @@ static int vidioc_g_fmt_cap(struct file *file, void *priv,
        return 0;
 }
 
-static int vidioc_try_fmt_cap(struct file *file, void *priv,
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
                        struct v4l2_format *f)
 {
        struct em28xx_fh      *fh    = priv;
@@ -766,7 +766,7 @@ static int vidioc_try_fmt_cap(struct file *file, void *priv,
        return 0;
 }
 
-static int vidioc_s_fmt_cap(struct file *file, void *priv,
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
                        struct v4l2_format *f)
 {
        struct em28xx_fh      *fh  = priv;
@@ -777,7 +777,7 @@ static int vidioc_s_fmt_cap(struct file *file, void *priv,
        if (rc < 0)
                return rc;
 
-       vidioc_try_fmt_cap(file, priv, f);
+       vidioc_try_fmt_vid_cap(file, priv, f);
 
        mutex_lock(&dev->lock);
 
@@ -826,7 +826,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id * norm)
        /* Adjusts width/height, if needed */
        f.fmt.pix.width = dev->width;
        f.fmt.pix.height = dev->height;
-       vidioc_try_fmt_cap(file, priv, &f);
+       vidioc_try_fmt_vid_cap(file, priv, &f);
 
        mutex_lock(&dev->lock);
 
@@ -1277,7 +1277,7 @@ static int vidioc_querycap(struct file *file, void  *priv,
        return 0;
 }
 
-static int vidioc_enum_fmt_cap(struct file *file, void  *priv,
+static int vidioc_enum_fmt_vid_cap(struct file *file, void  *priv,
                                        struct v4l2_fmtdesc *fmtd)
 {
        if (fmtd->index != 0)
@@ -1292,7 +1292,7 @@ static int vidioc_enum_fmt_cap(struct file *file, void  *priv,
 }
 
 /* Sliced VBI ioctls */
-static int vidioc_g_fmt_vbi_capture(struct file *file, void *priv,
+static int vidioc_g_fmt_sliced_vbi_cap(struct file *file, void *priv,
                                        struct v4l2_format *f)
 {
        struct em28xx_fh      *fh  = priv;
@@ -1316,7 +1316,7 @@ static int vidioc_g_fmt_vbi_capture(struct file *file, void *priv,
        return rc;
 }
 
-static int vidioc_try_set_vbi_capture(struct file *file, void *priv,
+static int vidioc_try_set_sliced_vbi_cap(struct file *file, void *priv,
                        struct v4l2_format *f)
 {
        struct em28xx_fh      *fh  = priv;
@@ -1590,6 +1590,8 @@ static void em28xx_release_resources(struct em28xx *dev)
                                dev->vdev->minor-MINOR_VFL_TYPE_GRABBER_MIN,
                                dev->vbi_dev->minor-MINOR_VFL_TYPE_VBI_MIN);
        list_del(&dev->devlist);
+       if (dev->sbutton_input_dev)
+               em28xx_deregister_snapshot_button(dev);
        if (dev->radio_dev) {
                if (-1 != dev->radio_dev->minor)
                        video_unregister_device(dev->radio_dev);
@@ -1776,17 +1778,17 @@ static const struct video_device em28xx_video_template = {
 
        .minor                      = -1,
        .vidioc_querycap            = vidioc_querycap,
-       .vidioc_enum_fmt_cap        = vidioc_enum_fmt_cap,
-       .vidioc_g_fmt_cap           = vidioc_g_fmt_cap,
-       .vidioc_try_fmt_cap         = vidioc_try_fmt_cap,
-       .vidioc_s_fmt_cap           = vidioc_s_fmt_cap,
+       .vidioc_enum_fmt_vid_cap    = vidioc_enum_fmt_vid_cap,
+       .vidioc_g_fmt_vid_cap       = vidioc_g_fmt_vid_cap,
+       .vidioc_try_fmt_vid_cap     = vidioc_try_fmt_vid_cap,
+       .vidioc_s_fmt_vid_cap       = vidioc_s_fmt_vid_cap,
        .vidioc_g_audio             = vidioc_g_audio,
        .vidioc_s_audio             = vidioc_s_audio,
        .vidioc_cropcap             = vidioc_cropcap,
 
-       .vidioc_g_fmt_vbi_capture   = vidioc_g_fmt_vbi_capture,
-       .vidioc_try_fmt_vbi_capture = vidioc_try_set_vbi_capture,
-       .vidioc_s_fmt_vbi_capture   = vidioc_try_set_vbi_capture,
+       .vidioc_g_fmt_sliced_vbi_cap   = vidioc_g_fmt_sliced_vbi_cap,
+       .vidioc_try_fmt_sliced_vbi_cap = vidioc_try_set_sliced_vbi_cap,
+       .vidioc_s_fmt_sliced_vbi_cap   = vidioc_try_set_sliced_vbi_cap,
 
        .vidioc_reqbufs             = vidioc_reqbufs,
        .vidioc_querybuf            = vidioc_querybuf,
index 002f170b211a34416cdf9e414b8d7d5a7023f7f3..89842c5d64a13da3e1204b41dace3fdc07eba8c0 100644 (file)
@@ -55,6 +55,9 @@
 #define EM2820_BOARD_PROLINK_PLAYTV_USB2       14
 #define EM2800_BOARD_VGEAR_POCKETTV             15
 #define EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950   16
+#define EM2880_BOARD_PINNACLE_PCTV_HD_PRO      17
+#define EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2        18
+#define EM2860_BOARD_POINTNIX_INTRAORAL_CAMERA  19
 
 /* Limits minimum and default number of buffers */
 #define EM28XX_MIN_BUF 4
@@ -247,6 +250,7 @@ struct em28xx_board {
        unsigned int has_12mhz_i2s:1;
        unsigned int max_range_640_480:1;
        unsigned int has_dvb:1;
+       unsigned int has_snapshot_button:1;
 
        enum em28xx_decoder decoder;
 
@@ -326,6 +330,7 @@ struct em28xx {
        unsigned int has_12mhz_i2s:1;
        unsigned int max_range_640_480:1;
        unsigned int has_dvb:1;
+       unsigned int has_snapshot_button:1;
 
        /* Some older em28xx chips needs a waiting time after writing */
        unsigned int wait_after_write;
@@ -416,6 +421,11 @@ struct em28xx {
        /* Caches GPO and GPIO registers */
        unsigned char   reg_gpo, reg_gpio;
 
+       /* Snapshot button */
+       char snapshot_button_path[30];  /* path of the input dev */
+       struct input_dev *sbutton_input_dev;
+       struct delayed_work sbutton_query_work;
+
        struct em28xx_dvb *dvb;
 };
 
@@ -481,6 +491,8 @@ int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw);
 int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw);
 int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key,
                                     u32 *ir_raw);
+void em28xx_register_snapshot_button(struct em28xx *dev);
+void em28xx_deregister_snapshot_button(struct em28xx *dev);
 
 /* printk macros */
 
diff --git a/drivers/media/video/gspca/Kconfig b/drivers/media/video/gspca/Kconfig
new file mode 100644 (file)
index 0000000..42b9074
--- /dev/null
@@ -0,0 +1,13 @@
+config USB_GSPCA
+       tristate "USB GSPCA driver"
+       depends on VIDEO_V4L2
+       ---help---
+         Say Y here if you want support for various USB webcams.
+
+         See <file:Documentation/video4linux/gspca.txt> for more info.
+
+         This driver uses the Video For Linux API. You must say Y or M to
+         "Video For Linux" to use this driver.
+
+         To compile this driver as modules, choose M here: the
+         modules will be called gspca_xxxx.
diff --git a/drivers/media/video/gspca/Makefile b/drivers/media/video/gspca/Makefile
new file mode 100644 (file)
index 0000000..e68a896
--- /dev/null
@@ -0,0 +1,29 @@
+obj-$(CONFIG_USB_GSPCA)        += gspca_main.o \
+       gspca_conex.o gspca_etoms.o gspca_mars.o \
+       gspca_ov519.o gspca_pac207.o gspca_pac7311.o \
+       gspca_sonixb.o gspca_sonixj.o gspca_spca500.o gspca_spca501.o \
+       gspca_spca505.o gspca_spca506.o gspca_spca508.o gspca_spca561.o \
+       gspca_sunplus.o gspca_stk014.o gspca_t613.o gspca_tv8532.o \
+       gspca_vc032x.o gspca_zc3xx.o
+
+gspca_main-objs := gspca.o
+gspca_conex-objs := conex.o
+gspca_etoms-objs := etoms.o
+gspca_mars-objs := mars.o
+gspca_ov519-objs := ov519.o
+gspca_pac207-objs := pac207.o
+gspca_pac7311-objs := pac7311.o
+gspca_sonixb-objs := sonixb.o
+gspca_sonixj-objs := sonixj.o
+gspca_spca500-objs := spca500.o
+gspca_spca501-objs := spca501.o
+gspca_spca505-objs := spca505.o
+gspca_spca506-objs := spca506.o
+gspca_spca508-objs := spca508.o
+gspca_spca561-objs := spca561.o
+gspca_stk014-objs := stk014.o
+gspca_sunplus-objs := sunplus.o
+gspca_t613-objs := t613.o
+gspca_tv8532-objs := tv8532.o
+gspca_vc032x-objs := vc032x.o
+gspca_zc3xx-objs := zc3xx.o
diff --git a/drivers/media/video/gspca/conex.c b/drivers/media/video/gspca/conex.c
new file mode 100644 (file)
index 0000000..013d593
--- /dev/null
@@ -0,0 +1,1051 @@
+/*
+ *             Connexant Cx11646 library
+ *             Copyright (C) 2004 Michel Xhaard mxhaard@magic.fr
+ *
+ * V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
+ *
+ * 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
+ * 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
+ */
+
+#define MODULE_NAME "conex"
+
+#include "gspca.h"
+#define CONEX_CAM 1            /* special JPEG header */
+#include "jpeg.h"
+
+#define DRIVER_VERSION_NUMBER  KERNEL_VERSION(2, 1, 7)
+static const char version[] = "2.1.7";
+
+MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
+MODULE_DESCRIPTION("GSPCA USB Conexant Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* specific webcam descriptor */
+struct sd {
+       struct gspca_dev gspca_dev;     /* !! must be the first item */
+
+       unsigned char brightness;
+       unsigned char contrast;
+       unsigned char colors;
+
+       unsigned char qindex;
+};
+
+/* V4L2 controls supported by the driver */
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
+
+static struct ctrl sd_ctrls[] = {
+       {
+           {
+               .id      = V4L2_CID_BRIGHTNESS,
+               .type    = V4L2_CTRL_TYPE_INTEGER,
+               .name    = "Brightness",
+               .minimum = 0,
+               .maximum = 255,
+               .step    = 1,
+#define BRIGHTNESS_DEF 0xd4
+               .default_value = BRIGHTNESS_DEF,
+           },
+           .set = sd_setbrightness,
+           .get = sd_getbrightness,
+       },
+       {
+           {
+               .id      = V4L2_CID_CONTRAST,
+               .type    = V4L2_CTRL_TYPE_INTEGER,
+               .name    = "Contrast",
+               .minimum = 0x0a,
+               .maximum = 0x1f,
+               .step    = 1,
+#define CONTRAST_DEF 0x0c
+               .default_value = CONTRAST_DEF,
+           },
+           .set = sd_setcontrast,
+           .get = sd_getcontrast,
+       },
+       {
+           {
+               .id      = V4L2_CID_SATURATION,
+               .type    = V4L2_CTRL_TYPE_INTEGER,
+               .name    = "Color",
+               .minimum = 0,
+               .maximum = 7,
+               .step    = 1,
+#define COLOR_DEF 3
+               .default_value = COLOR_DEF,
+           },
+           .set = sd_setcolors,
+           .get = sd_getcolors,
+       },
+};
+
+static struct v4l2_pix_format vga_mode[] = {
+       {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+               .bytesperline = 176,
+               .sizeimage = 176 * 144 * 3 / 8 + 590,
+               .colorspace = V4L2_COLORSPACE_JPEG,
+               .priv = 3},
+       {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+               .bytesperline = 320,
+               .sizeimage = 320 * 240 * 3 / 8 + 590,
+               .colorspace = V4L2_COLORSPACE_JPEG,
+               .priv = 2},
+       {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+               .bytesperline = 352,
+               .sizeimage = 352 * 288 * 3 / 8 + 590,
+               .colorspace = V4L2_COLORSPACE_JPEG,
+               .priv = 1},
+       {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+               .bytesperline = 640,
+               .sizeimage = 640 * 480 * 3 / 8 + 590,
+               .colorspace = V4L2_COLORSPACE_JPEG,
+               .priv = 0},
+};
+
+/* the read bytes are found in gspca_dev->usb_buf */
+static void reg_r(struct gspca_dev *gspca_dev,
+                 __u16 index,
+                 __u16 len)
+{
+       struct usb_device *dev = gspca_dev->dev;
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+       if (len > sizeof gspca_dev->usb_buf) {
+               err("reg_r: buffer overflow");
+               return;
+       }
+#endif
+       usb_control_msg(dev,
+                       usb_rcvctrlpipe(dev, 0),
+                       0,
+                       USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                       0,
+                       index, gspca_dev->usb_buf, len,
+                       500);
+       PDEBUG(D_USBI, "reg read [%02x] -> %02x ..",
+                       index, gspca_dev->usb_buf[0]);
+}
+
+/* the bytes to write are in gspca_dev->usb_buf */
+static void reg_w_val(struct gspca_dev *gspca_dev,
+                       __u16 index,
+                       __u8 val)
+{
+       struct usb_device *dev = gspca_dev->dev;
+
+       gspca_dev->usb_buf[0] = val;
+       usb_control_msg(dev,
+                       usb_sndctrlpipe(dev, 0),
+                       0,
+                       USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                       0,
+                       index, gspca_dev->usb_buf, 1, 500);
+}
+
+static void reg_w(struct gspca_dev *gspca_dev,
+                 __u16 index,
+                 const __u8 *buffer,
+                 __u16 len)
+{
+       struct usb_device *dev = gspca_dev->dev;
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+       if (len > sizeof gspca_dev->usb_buf) {
+               err("reg_w: buffer overflow");
+               return;
+       }
+       PDEBUG(D_USBO, "reg write [%02x] = %02x..", index, *buffer);
+#endif
+       memcpy(gspca_dev->usb_buf, buffer, len);
+       usb_control_msg(dev,
+                       usb_sndctrlpipe(dev, 0),
+                       0,
+                       USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                       0,
+                       index, gspca_dev->usb_buf, len, 500);
+}
+
+static const __u8 cx_sensor_init[][4] = {
+       {0x88, 0x11, 0x01, 0x01},
+       {0x88, 0x12, 0x70, 0x01},
+       {0x88, 0x0f, 0x00, 0x01},
+       {0x88, 0x05, 0x01, 0x01},
+       {}
+};
+
+static const __u8 cx11646_fw1[][3] = {
+       {0x00, 0x02, 0x00},
+       {0x01, 0x43, 0x00},
+       {0x02, 0xA7, 0x00},
+       {0x03, 0x8B, 0x01},
+       {0x04, 0xE9, 0x02},
+       {0x05, 0x08, 0x04},
+       {0x06, 0x08, 0x05},
+       {0x07, 0x07, 0x06},
+       {0x08, 0xE7, 0x06},
+       {0x09, 0xC6, 0x07},
+       {0x0A, 0x86, 0x08},
+       {0x0B, 0x46, 0x09},
+       {0x0C, 0x05, 0x0A},
+       {0x0D, 0xA5, 0x0A},
+       {0x0E, 0x45, 0x0B},
+       {0x0F, 0xE5, 0x0B},
+       {0x10, 0x85, 0x0C},
+       {0x11, 0x25, 0x0D},
+       {0x12, 0xC4, 0x0D},
+       {0x13, 0x45, 0x0E},
+       {0x14, 0xE4, 0x0E},
+       {0x15, 0x64, 0x0F},
+       {0x16, 0xE4, 0x0F},
+       {0x17, 0x64, 0x10},
+       {0x18, 0xE4, 0x10},
+       {0x19, 0x64, 0x11},
+       {0x1A, 0xE4, 0x11},
+       {0x1B, 0x64, 0x12},
+       {0x1C, 0xE3, 0x12},
+       {0x1D, 0x44, 0x13},
+       {0x1E, 0xC3, 0x13},
+       {0x1F, 0x24, 0x14},
+       {0x20, 0xA3, 0x14},
+       {0x21, 0x04, 0x15},
+       {0x22, 0x83, 0x15},
+       {0x23, 0xE3, 0x15},
+       {0x24, 0x43, 0x16},
+       {0x25, 0xA4, 0x16},
+       {0x26, 0x23, 0x17},
+       {0x27, 0x83, 0x17},
+       {0x28, 0xE3, 0x17},
+       {0x29, 0x43, 0x18},
+       {0x2A, 0xA3, 0x18},
+       {0x2B, 0x03, 0x19},
+       {0x2C, 0x63, 0x19},
+       {0x2D, 0xC3, 0x19},
+       {0x2E, 0x22, 0x1A},
+       {0x2F, 0x63, 0x1A},
+       {0x30, 0xC3, 0x1A},
+       {0x31, 0x23, 0x1B},
+       {0x32, 0x83, 0x1B},
+       {0x33, 0xE2, 0x1B},
+       {0x34, 0x23, 0x1C},
+       {0x35, 0x83, 0x1C},
+       {0x36, 0xE2, 0x1C},
+       {0x37, 0x23, 0x1D},
+       {0x38, 0x83, 0x1D},
+       {0x39, 0xE2, 0x1D},
+       {0x3A, 0x23, 0x1E},
+       {0x3B, 0x82, 0x1E},
+       {0x3C, 0xC3, 0x1E},
+       {0x3D, 0x22, 0x1F},
+       {0x3E, 0x63, 0x1F},
+       {0x3F, 0xC1, 0x1F},
+       {}
+};
+static void cx11646_fw(struct gspca_dev*gspca_dev)
+{
+       int i = 0;
+
+       reg_w_val(gspca_dev, 0x006a, 0x02);
+       while (cx11646_fw1[i][1]) {
+               reg_w(gspca_dev, 0x006b, cx11646_fw1[i], 3);
+               i++;
+       }
+       reg_w_val(gspca_dev, 0x006a, 0x00);
+}
+
+static const __u8 cxsensor[] = {
+       0x88, 0x12, 0x70, 0x01,
+       0x88, 0x0d, 0x02, 0x01,
+       0x88, 0x0f, 0x00, 0x01,
+       0x88, 0x03, 0x71, 0x01, 0x88, 0x04, 0x00, 0x01, /* 3 */
+       0x88, 0x02, 0x10, 0x01,
+       0x88, 0x00, 0xD4, 0x01, 0x88, 0x01, 0x01, 0x01, /* 5 */
+       0x88, 0x0B, 0x00, 0x01,
+       0x88, 0x0A, 0x0A, 0x01,
+       0x88, 0x00, 0x08, 0x01, 0x88, 0x01, 0x00, 0x01, /* 8 */
+       0x88, 0x05, 0x01, 0x01,
+       0xA1, 0x18, 0x00, 0x01,
+       0x00
+};
+
+static const __u8 reg20[] = { 0x10, 0x42, 0x81, 0x19, 0xd3, 0xff, 0xa7, 0xff };
+static const __u8 reg28[] = { 0x87, 0x00, 0x87, 0x00, 0x8f, 0xff, 0xea, 0xff };
+static const __u8 reg10[] = { 0xb1, 0xb1 };
+static const __u8 reg71a[] = { 0x08, 0x18, 0x0a, 0x1e };       /* 640 */
+static const __u8 reg71b[] = { 0x04, 0x0c, 0x05, 0x0f };
+       /* 352{0x04,0x0a,0x06,0x12}; //352{0x05,0x0e,0x06,0x11}; //352 */
+static const __u8 reg71c[] = { 0x02, 0x07, 0x03, 0x09 };
+                                       /* 320{0x04,0x0c,0x05,0x0f}; //320 */
+static const __u8 reg71d[] = { 0x02, 0x07, 0x03, 0x09 };       /* 176 */
+static const __u8 reg7b[] = { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff };
+
+static void cx_sensor(struct gspca_dev*gspca_dev)
+{
+       int i = 0;
+       int length;
+       const __u8 *ptsensor = cxsensor;
+
+       reg_w(gspca_dev, 0x0020, reg20, 8);
+       reg_w(gspca_dev, 0x0028, reg28, 8);
+       reg_w(gspca_dev, 0x0010, reg10, 8);
+       reg_w_val(gspca_dev, 0x0092, 0x03);
+
+       switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) {
+       case 0:
+               reg_w(gspca_dev, 0x0071, reg71a, 4);
+               break;
+       case 1:
+               reg_w(gspca_dev, 0x0071, reg71b, 4);
+               break;
+       default:
+/*     case 2: */
+               reg_w(gspca_dev, 0x0071, reg71c, 4);
+               break;
+       case 3:
+               reg_w(gspca_dev, 0x0071, reg71d, 4);
+               break;
+       }
+       reg_w(gspca_dev, 0x007b, reg7b, 6);
+       reg_w_val(gspca_dev, 0x00f8, 0x00);
+       reg_w(gspca_dev, 0x0010, reg10, 8);
+       reg_w_val(gspca_dev, 0x0098, 0x41);
+       for (i = 0; i < 11; i++) {
+               if (i == 3 || i == 5 || i == 8)
+                       length = 8;
+               else
+                       length = 4;
+               reg_w(gspca_dev, 0x00e5, ptsensor, length);
+               if (length == 4)
+                       reg_r(gspca_dev, 0x00e8, 1);
+               else
+                       reg_r(gspca_dev, 0x00e8, length);
+               ptsensor += length;
+       }
+       reg_r(gspca_dev, 0x00e7, 8);
+}
+
+static const __u8 cx_inits_176[] = {
+       0x33, 0x81, 0xB0, 0x00, 0x90, 0x00, 0x0A, 0x03, /* 176x144 */
+       0x00, 0x03, 0x03, 0x03, 0x1B, 0x05, 0x30, 0x03,
+       0x65, 0x15, 0x18, 0x25, 0x03, 0x25, 0x08, 0x30,
+       0x3B, 0x25, 0x10, 0x00, 0x04, 0x00, 0x00, 0x00,
+       0xDC, 0xFF, 0xEE, 0xFF, 0xC5, 0xFF, 0xBF, 0xFF,
+       0xF7, 0xFF, 0x88, 0xFF, 0x66, 0x02, 0x28, 0x02,
+       0x1E, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+static const __u8 cx_inits_320[] = {
+       0x7f, 0x7f, 0x40, 0x01, 0xf0, 0x00, 0x02, 0x01,
+       0x00, 0x01, 0x01, 0x01, 0x10, 0x00, 0x02, 0x01,
+       0x65, 0x45, 0xfa, 0x4c, 0x2c, 0xdf, 0xb9, 0x81,
+       0x30, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+       0xe2, 0xff, 0xf1, 0xff, 0xc2, 0xff, 0xbc, 0xff,
+       0xf5, 0xff, 0x6d, 0xff, 0xf6, 0x01, 0x43, 0x02,
+       0xd3, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+static const __u8 cx_inits_352[] = {
+       0x2e, 0x7c, 0x60, 0x01, 0x20, 0x01, 0x05, 0x03,
+       0x00, 0x06, 0x03, 0x06, 0x1b, 0x10, 0x05, 0x3b,
+       0x30, 0x25, 0x18, 0x25, 0x08, 0x30, 0x03, 0x25,
+       0x3b, 0x30, 0x25, 0x1b, 0x10, 0x05, 0x00, 0x00,
+       0xe3, 0xff, 0xf1, 0xff, 0xc2, 0xff, 0xbc, 0xff,
+       0xf5, 0xff, 0x6b, 0xff, 0xee, 0x01, 0x43, 0x02,
+       0xe4, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+static const __u8 cx_inits_640[] = {
+       0x7e, 0x7e, 0x80, 0x02, 0xe0, 0x01, 0x01, 0x01,
+       0x00, 0x02, 0x01, 0x02, 0x10, 0x30, 0x01, 0x01,
+       0x65, 0x45, 0xf7, 0x52, 0x2c, 0xdf, 0xb9, 0x81,
+       0x30, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+       0xe2, 0xff, 0xf1, 0xff, 0xc2, 0xff, 0xbc, 0xff,
+       0xf6, 0xff, 0x7b, 0xff, 0x01, 0x02, 0x43, 0x02,
+       0x77, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static void cx11646_initsize(struct gspca_dev *gspca_dev)
+{
+       const __u8 *cxinit;
+       static const __u8 reg12[] = { 0x08, 0x05, 0x07, 0x04, 0x24 };
+       static const __u8 reg17[] =
+                       { 0x0a, 0x00, 0xf2, 0x01, 0x0f, 0x00, 0x97, 0x02 };
+
+       switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) {
+       case 0:
+               cxinit = cx_inits_640;
+               break;
+       case 1:
+               cxinit = cx_inits_352;
+               break;
+       default:
+/*     case 2: */
+               cxinit = cx_inits_320;
+               break;
+       case 3:
+               cxinit = cx_inits_176;
+               break;
+       }
+       reg_w_val(gspca_dev, 0x009a, 0x01);
+       reg_w_val(gspca_dev, 0x0010, 0x10);
+       reg_w(gspca_dev, 0x0012, reg12, 5);
+       reg_w(gspca_dev, 0x0017, reg17, 8);
+       reg_w_val(gspca_dev, 0x00c0, 0x00);
+       reg_w_val(gspca_dev, 0x00c1, 0x04);
+       reg_w_val(gspca_dev, 0x00c2, 0x04);
+
+       reg_w(gspca_dev, 0x0061, cxinit, 8);
+       cxinit += 8;
+       reg_w(gspca_dev, 0x00ca, cxinit, 8);
+       cxinit += 8;
+       reg_w(gspca_dev, 0x00d2, cxinit, 8);
+       cxinit += 8;
+       reg_w(gspca_dev, 0x00da, cxinit, 6);
+       cxinit += 8;
+       reg_w(gspca_dev, 0x0041, cxinit, 8);
+       cxinit += 8;
+       reg_w(gspca_dev, 0x0049, cxinit, 8);
+       cxinit += 8;
+       reg_w(gspca_dev, 0x0051, cxinit, 2);
+
+       reg_r(gspca_dev, 0x0010, 1);
+}
+
+static const __u8 cx_jpeg_init[][8] = {
+       {0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x15},       /* 1 */
+       {0x0f, 0x10, 0x12, 0x10, 0x0d, 0x15, 0x12, 0x11},
+       {0x12, 0x18, 0x16, 0x15, 0x19, 0x20, 0x35, 0x22},
+       {0x20, 0x1d, 0x1d, 0x20, 0x41, 0x2e, 0x31, 0x26},
+       {0x35, 0x4d, 0x43, 0x51, 0x4f, 0x4b, 0x43, 0x4a},
+       {0x49, 0x55, 0x5F, 0x79, 0x67, 0x55, 0x5A, 0x73},
+       {0x5B, 0x49, 0x4A, 0x6A, 0x90, 0x6B, 0x73, 0x7D},
+       {0x81, 0x88, 0x89, 0x88, 0x52, 0x66, 0x95, 0xA0},
+       {0x94, 0x84, 0x9E, 0x79, 0x85, 0x88, 0x83, 0x01},
+       {0x15, 0x0F, 0x10, 0x12, 0x10, 0x0D, 0x15, 0x12},
+       {0x11, 0x12, 0x18, 0x16, 0x15, 0x19, 0x20, 0x35},
+       {0x22, 0x20, 0x1D, 0x1D, 0x20, 0x41, 0x2E, 0x31},
+       {0x26, 0x35, 0x4D, 0x43, 0x51, 0x4F, 0x4B, 0x43},
+       {0x4A, 0x49, 0x55, 0x5F, 0x79, 0x67, 0x55, 0x5A},
+       {0x73, 0x5B, 0x49, 0x4A, 0x6A, 0x90, 0x6B, 0x73},
+       {0x7D, 0x81, 0x88, 0x89, 0x88, 0x52, 0x66, 0x95},
+       {0xA0, 0x94, 0x84, 0x9E, 0x79, 0x85, 0x88, 0x83},
+       {0xFF, 0xC4, 0x01, 0xA2, 0x00, 0x00, 0x01, 0x05},
+       {0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00},
+       {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02},
+       {0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A},
+       {0x0B, 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01},
+       {0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00},
+       {0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05},
+       {0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x10, 0x00},
+       {0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05},
+       {0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7D, 0x01},
+       {0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21},
+       {0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22},
+       {0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, 0x08, 0x23},
+       {0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24},
+       {0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17},
+       {0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29},
+       {0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A},
+       {0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A},
+       {0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A},
+       {0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A},
+       {0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A},
+       {0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A},
+       {0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99},
+       {0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8},
+       {0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7},
+       {0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6},
+       {0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5},
+       {0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2, 0xE3},
+       {0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1},
+       {0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9},
+       {0xFA, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04},
+       {0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01},
+       {0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04},
+       {0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07},
+       {0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14},
+       {0x42, 0x91, 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33},
+       {0x52, 0xF0, 0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16},
+       {0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, 0x19},
+       {0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x35, 0x36},
+       {0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46},
+       {0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56},
+       {0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66},
+       {0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76},
+       {0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84, 0x85},
+       {0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94},
+       {0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3},
+       {0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2},
+       {0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA},
+       {0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9},
+       {0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8},
+       {0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7},
+       {0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6},
+       {0xF7, 0xF8, 0xF9, 0xFA, 0xFF, 0x20, 0x00, 0x1F},
+       {0x02, 0x0C, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00},
+       {0x00, 0x00, 0x11, 0x00, 0x11, 0x22, 0x00, 0x22},
+       {0x22, 0x11, 0x22, 0x22, 0x11, 0x33, 0x33, 0x11},
+       {0x44, 0x66, 0x22, 0x55, 0x66, 0xFF, 0xDD, 0x00},
+       {0x04, 0x00, 0x14, 0xFF, 0xC0, 0x00, 0x11, 0x08},
+       {0x00, 0xF0, 0x01, 0x40, 0x03, 0x00, 0x21, 0x00},
+       {0x01, 0x11, 0x01, 0x02, 0x11, 0x01, 0xFF, 0xDA},
+       {0x00, 0x0C, 0x03, 0x00, 0x00, 0x01, 0x11, 0x02},
+       {0x11, 0x00, 0x3F, 0x00, 0xFF, 0xD9, 0x00, 0x00}        /* 79 */
+};
+
+
+static const __u8 cxjpeg_640[][8] = {
+       {0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x10},       /* 1 */
+       {0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e, 0x0d},
+       {0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28, 0x1a},
+       {0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25, 0x1d},
+       {0x28, 0x3a, 0x33, 0x3D, 0x3C, 0x39, 0x33, 0x38},
+       {0x37, 0x40, 0x48, 0x5C, 0x4E, 0x40, 0x44, 0x57},
+       {0x45, 0x37, 0x38, 0x50, 0x6D, 0x51, 0x57, 0x5F},
+       {0x62, 0x67, 0x68, 0x67, 0x3E, 0x4D, 0x71, 0x79},
+       {0x70, 0x64, 0x78, 0x5C, 0x65, 0x67, 0x63, 0x01},
+       {0x10, 0x0B, 0x0C, 0x0E, 0x0C, 0x0A, 0x10, 0x0E},
+       {0x0D, 0x0E, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28},
+       {0x1A, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25},
+       {0x1D, 0x28, 0x3A, 0x33, 0x3D, 0x3C, 0x39, 0x33},
+       {0x38, 0x37, 0x40, 0x48, 0x5C, 0x4E, 0x40, 0x44},
+       {0x57, 0x45, 0x37, 0x38, 0x50, 0x6D, 0x51, 0x57},
+       {0x5F, 0x62, 0x67, 0x68, 0x67, 0x3E, 0x4D, 0x71},
+       {0x79, 0x70, 0x64, 0x78, 0x5C, 0x65, 0x67, 0x63},
+       {0xFF, 0x20, 0x00, 0x1F, 0x00, 0x83, 0x00, 0x00},
+       {0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00},
+       {0x11, 0x22, 0x00, 0x22, 0x22, 0x11, 0x22, 0x22},
+       {0x11, 0x33, 0x33, 0x11, 0x44, 0x66, 0x22, 0x55},
+       {0x66, 0xFF, 0xDD, 0x00, 0x04, 0x00, 0x28, 0xFF},
+       {0xC0, 0x00, 0x11, 0x08, 0x01, 0xE0, 0x02, 0x80},
+       {0x03, 0x00, 0x21, 0x00, 0x01, 0x11, 0x01, 0x02},
+       {0x11, 0x01, 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x00},
+       {0x00, 0x01, 0x11, 0x02, 0x11, 0x00, 0x3F, 0x00},
+       {0xFF, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}        /* 27 */
+};
+static const __u8 cxjpeg_352[][8] = {
+       {0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x0d},
+       {0x09, 0x09, 0x0b, 0x09, 0x08, 0x0D, 0x0b, 0x0a},
+       {0x0b, 0x0e, 0x0d, 0x0d, 0x0f, 0x13, 0x1f, 0x14},
+       {0x13, 0x11, 0x11, 0x13, 0x26, 0x1b, 0x1d, 0x17},
+       {0x1F, 0x2D, 0x28, 0x30, 0x2F, 0x2D, 0x28, 0x2C},
+       {0x2B, 0x32, 0x38, 0x48, 0x3D, 0x32, 0x35, 0x44},
+       {0x36, 0x2B, 0x2C, 0x3F, 0x55, 0x3F, 0x44, 0x4A},
+       {0x4D, 0x50, 0x51, 0x50, 0x30, 0x3C, 0x58, 0x5F},
+       {0x58, 0x4E, 0x5E, 0x48, 0x4F, 0x50, 0x4D, 0x01},
+       {0x0D, 0x09, 0x09, 0x0B, 0x09, 0x08, 0x0D, 0x0B},
+       {0x0A, 0x0B, 0x0E, 0x0D, 0x0D, 0x0F, 0x13, 0x1F},
+       {0x14, 0x13, 0x11, 0x11, 0x13, 0x26, 0x1B, 0x1D},
+       {0x17, 0x1F, 0x2D, 0x28, 0x30, 0x2F, 0x2D, 0x28},
+       {0x2C, 0x2B, 0x32, 0x38, 0x48, 0x3D, 0x32, 0x35},
+       {0x44, 0x36, 0x2B, 0x2C, 0x3F, 0x55, 0x3F, 0x44},
+       {0x4A, 0x4D, 0x50, 0x51, 0x50, 0x30, 0x3C, 0x58},
+       {0x5F, 0x58, 0x4E, 0x5E, 0x48, 0x4F, 0x50, 0x4D},
+       {0xFF, 0x20, 0x00, 0x1F, 0x01, 0x83, 0x00, 0x00},
+       {0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00},
+       {0x11, 0x22, 0x00, 0x22, 0x22, 0x11, 0x22, 0x22},
+       {0x11, 0x33, 0x33, 0x11, 0x44, 0x66, 0x22, 0x55},
+       {0x66, 0xFF, 0xDD, 0x00, 0x04, 0x00, 0x16, 0xFF},
+       {0xC0, 0x00, 0x11, 0x08, 0x01, 0x20, 0x01, 0x60},
+       {0x03, 0x00, 0x21, 0x00, 0x01, 0x11, 0x01, 0x02},
+       {0x11, 0x01, 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x00},
+       {0x00, 0x01, 0x11, 0x02, 0x11, 0x00, 0x3F, 0x00},
+       {0xFF, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
+};
+static const __u8 cxjpeg_320[][8] = {
+       {0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x05},
+       {0x03, 0x04, 0x04, 0x04, 0x03, 0x05, 0x04, 0x04},
+       {0x04, 0x05, 0x05, 0x05, 0x06, 0x07, 0x0c, 0x08},
+       {0x07, 0x07, 0x07, 0x07, 0x0f, 0x0b, 0x0b, 0x09},
+       {0x0C, 0x11, 0x0F, 0x12, 0x12, 0x11, 0x0f, 0x11},
+       {0x11, 0x13, 0x16, 0x1C, 0x17, 0x13, 0x14, 0x1A},
+       {0x15, 0x11, 0x11, 0x18, 0x21, 0x18, 0x1A, 0x1D},
+       {0x1D, 0x1F, 0x1F, 0x1F, 0x13, 0x17, 0x22, 0x24},
+       {0x22, 0x1E, 0x24, 0x1C, 0x1E, 0x1F, 0x1E, 0x01},
+       {0x05, 0x03, 0x04, 0x04, 0x04, 0x03, 0x05, 0x04},
+       {0x04, 0x04, 0x05, 0x05, 0x05, 0x06, 0x07, 0x0C},
+       {0x08, 0x07, 0x07, 0x07, 0x07, 0x0F, 0x0B, 0x0B},
+       {0x09, 0x0C, 0x11, 0x0F, 0x12, 0x12, 0x11, 0x0F},
+       {0x11, 0x11, 0x13, 0x16, 0x1C, 0x17, 0x13, 0x14},
+       {0x1A, 0x15, 0x11, 0x11, 0x18, 0x21, 0x18, 0x1A},
+       {0x1D, 0x1D, 0x1F, 0x1F, 0x1F, 0x13, 0x17, 0x22},
+       {0x24, 0x22, 0x1E, 0x24, 0x1C, 0x1E, 0x1F, 0x1E},
+       {0xFF, 0x20, 0x00, 0x1F, 0x02, 0x0C, 0x00, 0x00},
+       {0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00},
+       {0x11, 0x22, 0x00, 0x22, 0x22, 0x11, 0x22, 0x22},
+       {0x11, 0x33, 0x33, 0x11, 0x44, 0x66, 0x22, 0x55},
+       {0x66, 0xFF, 0xDD, 0x00, 0x04, 0x00, 0x14, 0xFF},
+       {0xC0, 0x00, 0x11, 0x08, 0x00, 0xF0, 0x01, 0x40},
+       {0x03, 0x00, 0x21, 0x00, 0x01, 0x11, 0x01, 0x02},
+       {0x11, 0x01, 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x00},
+       {0x00, 0x01, 0x11, 0x02, 0x11, 0x00, 0x3F, 0x00},
+       {0xFF, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}        /* 27 */
+};
+static const __u8 cxjpeg_176[][8] = {
+       {0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x0d},
+       {0x09, 0x09, 0x0B, 0x09, 0x08, 0x0D, 0x0B, 0x0A},
+       {0x0B, 0x0E, 0x0D, 0x0D, 0x0F, 0x13, 0x1F, 0x14},
+       {0x13, 0x11, 0x11, 0x13, 0x26, 0x1B, 0x1D, 0x17},
+       {0x1F, 0x2D, 0x28, 0x30, 0x2F, 0x2D, 0x28, 0x2C},
+       {0x2B, 0x32, 0x38, 0x48, 0x3D, 0x32, 0x35, 0x44},
+       {0x36, 0x2B, 0x2C, 0x3F, 0x55, 0x3F, 0x44, 0x4A},
+       {0x4D, 0x50, 0x51, 0x50, 0x30, 0x3C, 0x58, 0x5F},
+       {0x58, 0x4E, 0x5E, 0x48, 0x4F, 0x50, 0x4D, 0x01},
+       {0x0D, 0x09, 0x09, 0x0B, 0x09, 0x08, 0x0D, 0x0B},
+       {0x0A, 0x0B, 0x0E, 0x0D, 0x0D, 0x0F, 0x13, 0x1F},
+       {0x14, 0x13, 0x11, 0x11, 0x13, 0x26, 0x1B, 0x1D},
+       {0x17, 0x1F, 0x2D, 0x28, 0x30, 0x2F, 0x2D, 0x28},
+       {0x2C, 0x2B, 0x32, 0x38, 0x48, 0x3D, 0x32, 0x35},
+       {0x44, 0x36, 0x2B, 0x2C, 0x3F, 0x55, 0x3F, 0x44},
+       {0x4A, 0x4D, 0x50, 0x51, 0x50, 0x30, 0x3C, 0x58},
+       {0x5F, 0x58, 0x4E, 0x5E, 0x48, 0x4F, 0x50, 0x4D},
+       {0xFF, 0x20, 0x00, 0x1F, 0x03, 0xA1, 0x00, 0x00},
+       {0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00},
+       {0x11, 0x22, 0x00, 0x22, 0x22, 0x11, 0x22, 0x22},
+       {0x11, 0x33, 0x33, 0x11, 0x44, 0x66, 0x22, 0x55},
+       {0x66, 0xFF, 0xDD, 0x00, 0x04, 0x00, 0x0B, 0xFF},
+       {0xC0, 0x00, 0x11, 0x08, 0x00, 0x90, 0x00, 0xB0},
+       {0x03, 0x00, 0x21, 0x00, 0x01, 0x11, 0x01, 0x02},
+       {0x11, 0x01, 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x00},
+       {0x00, 0x01, 0x11, 0x02, 0x11, 0x00, 0x3F, 0x00},
+       {0xFF, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
+};
+/* 640 take with the zcx30x part */
+static const __u8 cxjpeg_qtable[][8] = {
+       {0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x08},
+       {0x06, 0x06, 0x07, 0x06, 0x05, 0x08, 0x07, 0x07},
+       {0x07, 0x09, 0x09, 0x08, 0x0a, 0x0c, 0x14, 0x0a},
+       {0x0c, 0x0b, 0x0b, 0x0c, 0x19, 0x12, 0x13, 0x0f},
+       {0x14, 0x1d, 0x1a, 0x1f, 0x1e, 0x1d, 0x1a, 0x1c},
+       {0x1c, 0x20, 0x24, 0x2e, 0x27, 0x20, 0x22, 0x2c},
+       {0x23, 0x1c, 0x1c, 0x28, 0x37, 0x29, 0x2c, 0x30},
+       {0x31, 0x34, 0x34, 0x34, 0x1f, 0x27, 0x39, 0x3d},
+       {0x38, 0x32, 0x3c, 0x2e, 0x33, 0x34, 0x32, 0x01},
+       {0x09, 0x09, 0x09, 0x0c, 0x0b, 0x0c, 0x18, 0x0a},
+       {0x0a, 0x18, 0x32, 0x21, 0x1c, 0x21, 0x32, 0x32},
+       {0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32},
+       {0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32},
+       {0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32},
+       {0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32},
+       {0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32},
+       {0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32},
+       {0xFF, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}        /* 18 */
+};
+
+
+static void cx11646_jpegInit(struct gspca_dev*gspca_dev)
+{
+       int i;
+       int length;
+
+       reg_w_val(gspca_dev, 0x00c0, 0x01);
+       reg_w_val(gspca_dev, 0x00c3, 0x00);
+       reg_w_val(gspca_dev, 0x00c0, 0x00);
+       reg_r(gspca_dev, 0x0001, 1);
+       length = 8;
+       for (i = 0; i < 79; i++) {
+               if (i == 78)
+                       length = 6;
+               reg_w(gspca_dev, 0x0008, cx_jpeg_init[i], length);
+       }
+       reg_r(gspca_dev, 0x0002, 1);
+       reg_w_val(gspca_dev, 0x0055, 0x14);
+}
+
+static const __u8 reg12[] = { 0x0a, 0x05, 0x07, 0x04, 0x19 };
+static const __u8 regE5_8[] =
+               { 0x88, 0x00, 0xd4, 0x01, 0x88, 0x01, 0x01, 0x01 };
+static const __u8 regE5a[] = { 0x88, 0x0a, 0x0c, 0x01 };
+static const __u8 regE5b[] = { 0x88, 0x0b, 0x12, 0x01 };
+static const __u8 regE5c[] = { 0x88, 0x05, 0x01, 0x01 };
+static const __u8 reg51[] = { 0x77, 0x03 };
+#define reg70 0x03
+
+static void cx11646_jpeg(struct gspca_dev*gspca_dev)
+{
+       int i;
+       int length;
+       __u8 Reg55;
+       int retry;
+
+       reg_w_val(gspca_dev, 0x00c0, 0x01);
+       reg_w_val(gspca_dev, 0x00c3, 0x00);
+       reg_w_val(gspca_dev, 0x00c0, 0x00);
+       reg_r(gspca_dev, 0x0001, 1);
+       length = 8;
+       switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) {
+       case 0:
+               for (i = 0; i < 27; i++) {
+                       if (i == 26)
+                               length = 2;
+                       reg_w(gspca_dev, 0x0008, cxjpeg_640[i], length);
+               }
+               Reg55 = 0x28;
+               break;
+       case 1:
+               for (i = 0; i < 27; i++) {
+                       if (i == 26)
+                               length = 2;
+                       reg_w(gspca_dev, 0x0008, cxjpeg_352[i], length);
+               }
+               Reg55 = 0x16;
+               break;
+       default:
+/*     case 2: */
+               for (i = 0; i < 27; i++) {
+                       if (i == 26)
+                               length = 2;
+                       reg_w(gspca_dev, 0x0008, cxjpeg_320[i], length);
+               }
+               Reg55 = 0x14;
+               break;
+       case 3:
+               for (i = 0; i < 27; i++) {
+                       if (i == 26)
+                               length = 2;
+                       reg_w(gspca_dev, 0x0008, cxjpeg_176[i], length);
+               }
+               Reg55 = 0x0B;
+               break;
+       }
+
+       reg_r(gspca_dev, 0x0002, 1);
+       reg_w_val(gspca_dev, 0x0055, Reg55);
+       reg_r(gspca_dev, 0x0002, 1);
+       reg_w(gspca_dev, 0x0010, reg10, 2);
+       reg_w_val(gspca_dev, 0x0054, 0x02);
+       reg_w_val(gspca_dev, 0x0054, 0x01);
+       reg_w_val(gspca_dev, 0x0000, 0x94);
+       reg_w_val(gspca_dev, 0x0053, 0xc0);
+       reg_w_val(gspca_dev, 0x00fc, 0xe1);
+       reg_w_val(gspca_dev, 0x0000, 0x00);
+       /* wait for completion */
+       retry = 50;
+       while (retry--) {
+               reg_r(gspca_dev, 0x0002, 1);
+                                                       /* 0x07 until 0x00 */
+               if (gspca_dev->usb_buf[0] == 0x00)
+                       break;
+               reg_w_val(gspca_dev, 0x0053, 0x00);
+       }
+       if (retry == 0)
+               PDEBUG(D_ERR, "Damned Errors sending jpeg Table");
+       /* send the qtable now */
+       reg_r(gspca_dev, 0x0001, 1);            /* -> 0x18 */
+       length = 8;
+       for (i = 0; i < 18; i++) {
+               if (i == 17)
+                       length = 2;
+               reg_w(gspca_dev, 0x0008, cxjpeg_qtable[i], length);
+
+       }
+       reg_r(gspca_dev, 0x0002, 1);    /* 0x00 */
+       reg_r(gspca_dev, 0x0053, 1);    /* 0x00 */
+       reg_w_val(gspca_dev, 0x0054, 0x02);
+       reg_w_val(gspca_dev, 0x0054, 0x01);
+       reg_w_val(gspca_dev, 0x0000, 0x94);
+       reg_w_val(gspca_dev, 0x0053, 0xc0);
+
+       reg_r(gspca_dev, 0x0038, 1);            /* 0x40 */
+       reg_r(gspca_dev, 0x0038, 1);            /* 0x40 */
+       reg_r(gspca_dev, 0x001f, 1);            /* 0x38 */
+       reg_w(gspca_dev, 0x0012, reg12, 5);
+       reg_w(gspca_dev, 0x00e5, regE5_8, 8);
+       reg_r(gspca_dev, 0x00e8, 8);
+       reg_w(gspca_dev, 0x00e5, regE5a, 4);
+       reg_r(gspca_dev, 0x00e8, 1);            /* 0x00 */
+       reg_w_val(gspca_dev, 0x009a, 0x01);
+       reg_w(gspca_dev, 0x00e5, regE5b, 4);
+       reg_r(gspca_dev, 0x00e8, 1);            /* 0x00 */
+       reg_w(gspca_dev, 0x00e5, regE5c, 4);
+       reg_r(gspca_dev, 0x00e8, 1);            /* 0x00 */
+
+       reg_w(gspca_dev, 0x0051, reg51, 2);
+       reg_w(gspca_dev, 0x0010, reg10, 2);
+       reg_w_val(gspca_dev, 0x0070, reg70);
+}
+
+static void cx11646_init1(struct gspca_dev *gspca_dev)
+{
+       int i = 0;
+
+       reg_w_val(gspca_dev, 0x0010, 0x00);
+       reg_w_val(gspca_dev, 0x0053, 0x00);
+       reg_w_val(gspca_dev, 0x0052, 0x00);
+       reg_w_val(gspca_dev, 0x009b, 0x2f);
+       reg_w_val(gspca_dev, 0x009c, 0x10);
+       reg_r(gspca_dev, 0x0098, 1);
+       reg_w_val(gspca_dev, 0x0098, 0x40);
+       reg_r(gspca_dev, 0x0099, 1);
+       reg_w_val(gspca_dev, 0x0099, 0x07);
+       reg_w_val(gspca_dev, 0x0039, 0x40);
+       reg_w_val(gspca_dev, 0x003c, 0xff);
+       reg_w_val(gspca_dev, 0x003f, 0x1f);
+       reg_w_val(gspca_dev, 0x003d, 0x40);
+/*     reg_w_val(gspca_dev, 0x003d, 0x60); */
+       reg_r(gspca_dev, 0x0099, 1);                    /* ->0x07 */
+
+       while (cx_sensor_init[i][0]) {
+               reg_w_val(gspca_dev, 0x00e5, cx_sensor_init[i][0]);
+               reg_r(gspca_dev, 0x00e8, 1);            /* -> 0x00 */
+               if (i == 1) {
+                       reg_w_val(gspca_dev, 0x00ed, 0x01);
+                       reg_r(gspca_dev, 0x00ed, 1);    /* -> 0x01 */
+               }
+               i++;
+       }
+       reg_w_val(gspca_dev, 0x00c3, 0x00);
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+                       const struct usb_device_id *id)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       struct cam *cam;
+
+       cam = &gspca_dev->cam;
+       cam->dev_name = (char *) id->driver_info;
+       cam->epaddr = 0x01;
+       cam->cam_mode = vga_mode;
+       cam->nmodes = sizeof vga_mode / sizeof vga_mode[0];
+
+       sd->qindex = 0;                 /* set the quantization */
+       sd->brightness = BRIGHTNESS_DEF;
+       sd->contrast = CONTRAST_DEF;
+       sd->colors = COLOR_DEF;
+       return 0;
+}
+
+/* this function is called at open time */
+static int sd_open(struct gspca_dev *gspca_dev)
+{
+       cx11646_init1(gspca_dev);
+       cx11646_initsize(gspca_dev);
+       cx11646_fw(gspca_dev);
+       cx_sensor(gspca_dev);
+       cx11646_jpegInit(gspca_dev);
+       return 0;
+}
+
+static void sd_start(struct gspca_dev *gspca_dev)
+{
+       cx11646_initsize(gspca_dev);
+       cx11646_fw(gspca_dev);
+       cx_sensor(gspca_dev);
+       cx11646_jpeg(gspca_dev);
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+}
+
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+       int retry = 50;
+
+       reg_w_val(gspca_dev, 0x0000, 0x00);
+       reg_r(gspca_dev, 0x0002, 1);
+       reg_w_val(gspca_dev, 0x0053, 0x00);
+
+       while (retry--) {
+/*             reg_r(gspca_dev, 0x0002, 1);*/
+               reg_r(gspca_dev, 0x0053, 1);
+               if (gspca_dev->usb_buf[0] == 0)
+                       break;
+       }
+       reg_w_val(gspca_dev, 0x0000, 0x00);
+       reg_r(gspca_dev, 0x0002, 1);
+
+       reg_w_val(gspca_dev, 0x0010, 0x00);
+       reg_r(gspca_dev, 0x0033, 1);
+       reg_w_val(gspca_dev, 0x00fc, 0xe0);
+}
+
+static void sd_close(struct gspca_dev *gspca_dev)
+{
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+                       struct gspca_frame *frame,      /* target */
+                       __u8 *data,                     /* isoc packet */
+                       int len)                        /* iso packet length */
+{
+       if (data[0] == 0xff && data[1] == 0xd8) {
+
+               /* start of frame */
+               frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
+                                       data, 0);
+
+               /* put the JPEG header in the new frame */
+               jpeg_put_header(gspca_dev, frame,
+                               ((struct sd *) gspca_dev)->qindex,
+                               0x22);
+               data += 2;
+               len -= 2;
+       }
+       gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
+}
+
+static void setbrightness(struct gspca_dev*gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       __u8 regE5cbx[] = { 0x88, 0x00, 0xd4, 0x01, 0x88, 0x01, 0x01, 0x01 };
+       __u8 reg51c[2];
+       __u8 bright;
+       __u8 colors;
+
+       bright = sd->brightness;
+       regE5cbx[2] = bright;
+       reg_w(gspca_dev, 0x00e5, regE5cbx, 8);
+       reg_r(gspca_dev, 0x00e8, 8);
+       reg_w(gspca_dev, 0x00e5, regE5c, 4);
+       reg_r(gspca_dev, 0x00e8, 1);            /* 0x00 */
+
+       colors = sd->colors;
+       reg51c[0] = 0x77;
+       reg51c[1] = colors;
+       reg_w(gspca_dev, 0x0051, reg51c, 2);
+       reg_w(gspca_dev, 0x0010, reg10, 2);
+       reg_w_val(gspca_dev, 0x0070, reg70);
+}
+
+static void setcontrast(struct gspca_dev*gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       __u8 regE5acx[] = { 0x88, 0x0a, 0x0c, 0x01 };   /* seem MSB */
+/*     __u8 regE5bcx[] = { 0x88, 0x0b, 0x12, 0x01};     * LSB */
+       __u8 reg51c[2];
+
+       regE5acx[2] = sd->contrast;
+       reg_w(gspca_dev, 0x00e5, regE5acx, 4);
+       reg_r(gspca_dev, 0x00e8, 1);            /* 0x00 */
+       reg51c[0] = 0x77;
+       reg51c[1] = sd->colors;
+       reg_w(gspca_dev, 0x0051, reg51c, 2);
+       reg_w(gspca_dev, 0x0010, reg10, 2);
+       reg_w_val(gspca_dev, 0x0070, reg70);
+}
+
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->brightness = val;
+       if (gspca_dev->streaming)
+               setbrightness(gspca_dev);
+       return 0;
+}
+
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->brightness;
+       return 0;
+}
+
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->contrast = val;
+       if (gspca_dev->streaming)
+               setcontrast(gspca_dev);
+       return 0;
+}
+
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->contrast;
+       return 0;
+}
+
+static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->colors = val;
+       if (gspca_dev->streaming) {
+               setbrightness(gspca_dev);
+               setcontrast(gspca_dev);
+       }
+       return 0;
+}
+
+static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->colors;
+       return 0;
+}
+
+/* sub-driver description */
+static struct sd_desc sd_desc = {
+       .name = MODULE_NAME,
+       .ctrls = sd_ctrls,
+       .nctrls = ARRAY_SIZE(sd_ctrls),
+       .config = sd_config,
+       .open = sd_open,
+       .start = sd_start,
+       .stopN = sd_stopN,
+       .stop0 = sd_stop0,
+       .close = sd_close,
+       .pkt_scan = sd_pkt_scan,
+};
+
+/* -- module initialisation -- */
+#define DVNM(name) .driver_info = (kernel_ulong_t) name
+static __devinitdata struct usb_device_id device_table[] = {
+       {USB_DEVICE(0x0572, 0x0041), DVNM("Creative Notebook cx11646")},
+       {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+                       const struct usb_device_id *id)
+{
+       return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+                               THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+       .name = MODULE_NAME,
+       .id_table = device_table,
+       .probe = sd_probe,
+       .disconnect = gspca_disconnect,
+};
+
+/* -- module insert / remove -- */
+static int __init sd_mod_init(void)
+{
+       if (usb_register(&sd_driver) < 0)
+               return -1;
+       PDEBUG(D_PROBE, "v%s registered", version);
+       return 0;
+}
+static void __exit sd_mod_exit(void)
+{
+       usb_deregister(&sd_driver);
+       PDEBUG(D_PROBE, "deregistered");
+}
+
+module_init(sd_mod_init);
+module_exit(sd_mod_exit);
diff --git a/drivers/media/video/gspca/etoms.c b/drivers/media/video/gspca/etoms.c
new file mode 100644 (file)
index 0000000..8ab4ea7
--- /dev/null
@@ -0,0 +1,956 @@
+/*
+ * Etoms Et61x151 GPL Linux driver by Michel Xhaard (09/09/2004)
+ *
+ * V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
+ *
+ * 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
+ * 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
+ */
+
+#define MODULE_NAME "etoms"
+
+#include "gspca.h"
+
+#define DRIVER_VERSION_NUMBER  KERNEL_VERSION(2, 1, 7)
+static const char version[] = "2.1.7";
+
+MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
+MODULE_DESCRIPTION("Etoms USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* specific webcam descriptor */
+struct sd {
+       struct gspca_dev gspca_dev;     /* !! must be the first item */
+
+       unsigned char brightness;
+       unsigned char contrast;
+       unsigned char colors;
+       unsigned char autogain;
+
+       char sensor;
+#define SENSOR_PAS106 0
+#define SENSOR_TAS5130CXX 1
+       signed char ag_cnt;
+#define AG_CNT_START 13
+};
+
+/* V4L2 controls supported by the driver */
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
+
+static struct ctrl sd_ctrls[] = {
+       {
+        {
+         .id = V4L2_CID_BRIGHTNESS,
+         .type = V4L2_CTRL_TYPE_INTEGER,
+         .name = "Brightness",
+         .minimum = 1,
+         .maximum = 127,
+         .step = 1,
+#define BRIGHTNESS_DEF 63
+         .default_value = BRIGHTNESS_DEF,
+         },
+        .set = sd_setbrightness,
+        .get = sd_getbrightness,
+        },
+       {
+        {
+         .id = V4L2_CID_CONTRAST,
+         .type = V4L2_CTRL_TYPE_INTEGER,
+         .name = "Contrast",
+         .minimum = 0,
+         .maximum = 255,
+         .step = 1,
+#define CONTRAST_DEF 127
+         .default_value = CONTRAST_DEF,
+         },
+        .set = sd_setcontrast,
+        .get = sd_getcontrast,
+        },
+       {
+        {
+         .id = V4L2_CID_SATURATION,
+         .type = V4L2_CTRL_TYPE_INTEGER,
+         .name = "Color",
+         .minimum = 0,
+         .maximum = 15,
+         .step = 1,
+#define COLOR_DEF 7
+         .default_value = COLOR_DEF,
+         },
+        .set = sd_setcolors,
+        .get = sd_getcolors,
+        },
+       {
+        {
+         .id = V4L2_CID_AUTOGAIN,
+         .type = V4L2_CTRL_TYPE_BOOLEAN,
+         .name = "Auto Gain",
+         .minimum = 0,
+         .maximum = 1,
+         .step = 1,
+#define AUTOGAIN_DEF 1
+         .default_value = AUTOGAIN_DEF,
+         },
+        .set = sd_setautogain,
+        .get = sd_getautogain,
+        },
+};
+
+static struct v4l2_pix_format vga_mode[] = {
+       {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+               .bytesperline = 320,
+               .sizeimage = 320 * 240,
+               .colorspace = V4L2_COLORSPACE_SRGB,
+               .priv = 1},
+/*     {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+               .bytesperline = 640,
+               .sizeimage = 640 * 480,
+               .colorspace = V4L2_COLORSPACE_SRGB,
+               .priv = 0}, */
+};
+
+static struct v4l2_pix_format sif_mode[] = {
+       {176, 144, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+               .bytesperline = 176,
+               .sizeimage = 176 * 144,
+               .colorspace = V4L2_COLORSPACE_SRGB,
+               .priv = 1},
+       {352, 288, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+               .bytesperline = 352,
+               .sizeimage = 352 * 288,
+               .colorspace = V4L2_COLORSPACE_SRGB,
+               .priv = 0},
+};
+
+#define ETOMS_ALT_SIZE_1000   12
+
+#define ET_GPIO_DIR_CTRL 0x04  /* Control IO bit[0..5] (0 in  1 out) */
+#define ET_GPIO_OUT 0x05       /* Only IO data */
+#define ET_GPIO_IN 0x06                /* Read Only IO data */
+#define ET_RESET_ALL 0x03
+#define ET_ClCK 0x01
+#define ET_CTRL 0x02           /* enable i2c OutClck Powerdown mode */
+
+#define ET_COMP 0x12           /* Compression register */
+#define ET_MAXQt 0x13
+#define ET_MINQt 0x14
+#define ET_COMP_VAL0 0x02
+#define ET_COMP_VAL1 0x03
+
+#define ET_REG1d 0x1d
+#define ET_REG1e 0x1e
+#define ET_REG1f 0x1f
+#define ET_REG20 0x20
+#define ET_REG21 0x21
+#define ET_REG22 0x22
+#define ET_REG23 0x23
+#define ET_REG24 0x24
+#define ET_REG25 0x25
+/* base registers for luma calculation */
+#define ET_LUMA_CENTER 0x39
+
+#define ET_G_RED 0x4d
+#define ET_G_GREEN1 0x4e
+#define ET_G_BLUE 0x4f
+#define ET_G_GREEN2 0x50
+#define ET_G_GR_H 0x51
+#define ET_G_GB_H 0x52
+
+#define ET_O_RED 0x34
+#define ET_O_GREEN1 0x35
+#define ET_O_BLUE 0x36
+#define ET_O_GREEN2 0x37
+
+#define ET_SYNCHRO 0x68
+#define ET_STARTX 0x69
+#define ET_STARTY 0x6a
+#define ET_WIDTH_LOW 0x6b
+#define ET_HEIGTH_LOW 0x6c
+#define ET_W_H_HEIGTH 0x6d
+
+#define ET_REG6e 0x6e          /* OBW */
+#define ET_REG6f 0x6f          /* OBW */
+#define ET_REG70 0x70          /* OBW_AWB */
+#define ET_REG71 0x71          /* OBW_AWB */
+#define ET_REG72 0x72          /* OBW_AWB */
+#define ET_REG73 0x73          /* Clkdelay ns */
+#define ET_REG74 0x74          /* test pattern */
+#define ET_REG75 0x75          /* test pattern */
+
+#define ET_I2C_CLK 0x8c
+#define ET_PXL_CLK 0x60
+
+#define ET_I2C_BASE 0x89
+#define ET_I2C_COUNT 0x8a
+#define ET_I2C_PREFETCH 0x8b
+#define ET_I2C_REG 0x88
+#define ET_I2C_DATA7 0x87
+#define ET_I2C_DATA6 0x86
+#define ET_I2C_DATA5 0x85
+#define ET_I2C_DATA4 0x84
+#define ET_I2C_DATA3 0x83
+#define ET_I2C_DATA2 0x82
+#define ET_I2C_DATA1 0x81
+#define ET_I2C_DATA0 0x80
+
+#define PAS106_REG2 0x02       /* pxlClk = systemClk/(reg2) */
+#define PAS106_REG3 0x03       /* line/frame H [11..4] */
+#define PAS106_REG4 0x04       /* line/frame L [3..0] */
+#define PAS106_REG5 0x05       /* exposure time line offset(default 5) */
+#define PAS106_REG6 0x06       /* exposure time pixel offset(default 6) */
+#define PAS106_REG7 0x07       /* signbit Dac (default 0) */
+#define PAS106_REG9 0x09
+#define PAS106_REG0e 0x0e      /* global gain [4..0](default 0x0e) */
+#define PAS106_REG13 0x13      /* end i2c write */
+
+static const __u8 GainRGBG[] = { 0x80, 0x80, 0x80, 0x80, 0x00, 0x00 };
+
+static const __u8 I2c2[] = { 0x08, 0x08, 0x08, 0x08, 0x0d };
+
+static const __u8 I2c3[] = { 0x12, 0x05 };
+
+static const __u8 I2c4[] = { 0x41, 0x08 };
+
+/* read 'len' bytes to gspca_dev->usb_buf */
+static void reg_r(struct gspca_dev *gspca_dev,
+                 __u16 index,
+                 __u16 len)
+{
+       struct usb_device *dev = gspca_dev->dev;
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+       if (len > sizeof gspca_dev->usb_buf) {
+               err("reg_r: buffer overflow");
+               return;
+       }
+#endif
+       usb_control_msg(dev,
+                       usb_rcvctrlpipe(dev, 0),
+                       0,
+                       USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+                       0,
+                       index, gspca_dev->usb_buf, len, 500);
+       PDEBUG(D_USBI, "reg read [%02x] -> %02x ..",
+                       index, gspca_dev->usb_buf[0]);
+}
+
+static void reg_w_val(struct gspca_dev *gspca_dev,
+                       __u16 index,
+                       __u8 val)
+{
+       struct usb_device *dev = gspca_dev->dev;
+
+       gspca_dev->usb_buf[0] = val;
+       usb_control_msg(dev,
+                       usb_sndctrlpipe(dev, 0),
+                       0,
+                       USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+                       0,
+                       index, gspca_dev->usb_buf, 1, 500);
+}
+
+static void reg_w(struct gspca_dev *gspca_dev,
+                 __u16 index,
+                 const __u8 *buffer,
+                 __u16 len)
+{
+       struct usb_device *dev = gspca_dev->dev;
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+       if (len > sizeof gspca_dev->usb_buf) {
+               err("reg_w: buffer overflow");
+               return;
+       }
+       PDEBUG(D_USBO, "reg write [%02x] = %02x..", index, *buffer);
+#endif
+       memcpy(gspca_dev->usb_buf, buffer, len);
+       usb_control_msg(dev,
+                       usb_sndctrlpipe(dev, 0),
+                       0,
+                       USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+                       0, index, gspca_dev->usb_buf, len, 500);
+}
+
+static int i2c_w(struct gspca_dev *gspca_dev,
+                __u8 reg,
+                const __u8 *buffer,
+                int len, __u8 mode)
+{
+       /* buffer should be [D0..D7] */
+       __u8 ptchcount;
+
+       /* set the base address */
+       reg_w_val(gspca_dev, ET_I2C_BASE, 0x40);
+                                        /* sensor base for the pas106 */
+       /* set count and prefetch */
+       ptchcount = ((len & 0x07) << 4) | (mode & 0x03);
+       reg_w_val(gspca_dev, ET_I2C_COUNT, ptchcount);
+       /* set the register base */
+       reg_w_val(gspca_dev, ET_I2C_REG, reg);
+       while (--len >= 0)
+               reg_w_val(gspca_dev, ET_I2C_DATA0 + len, buffer[len]);
+       return 0;
+}
+
+static int i2c_r(struct gspca_dev *gspca_dev,
+                       __u8 reg)
+{
+       /* set the base address */
+       reg_w_val(gspca_dev, ET_I2C_BASE, 0x40);
+                                       /* sensor base for the pas106 */
+       /* set count and prefetch (cnd: 4 bits - mode: 4 bits) */
+       reg_w_val(gspca_dev, ET_I2C_COUNT, 0x11);
+       reg_w_val(gspca_dev, ET_I2C_REG, reg);  /* set the register base */
+       reg_w_val(gspca_dev, ET_I2C_PREFETCH, 0x02);    /* prefetch */
+       reg_w_val(gspca_dev, ET_I2C_PREFETCH, 0x00);
+       reg_r(gspca_dev, ET_I2C_DATA0, 1);      /* read one byte */
+       return 0;
+}
+
+static int Et_WaitStatus(struct gspca_dev *gspca_dev)
+{
+       int retry = 10;
+
+       while (retry--) {
+               reg_r(gspca_dev, ET_ClCK, 1);
+               if (gspca_dev->usb_buf[0] != 0)
+                       return 1;
+       }
+       return 0;
+}
+
+static int et_video(struct gspca_dev *gspca_dev,
+                   int on)
+{
+       int ret;
+
+       reg_w_val(gspca_dev, ET_GPIO_OUT,
+                 on ? 0x10             /* startvideo - set Bit5 */
+                    : 0);              /* stopvideo */
+       ret = Et_WaitStatus(gspca_dev);
+       if (ret != 0)
+               PDEBUG(D_ERR, "timeout video on/off");
+       return ret;
+}
+
+static void Et_init2(struct gspca_dev *gspca_dev)
+{
+       __u8 value;
+       static const __u8 FormLine[] = { 0x84, 0x03, 0x14, 0xf4, 0x01, 0x05 };
+
+       PDEBUG(D_STREAM, "Open Init2 ET");
+       reg_w_val(gspca_dev, ET_GPIO_DIR_CTRL, 0x2f);
+       reg_w_val(gspca_dev, ET_GPIO_OUT, 0x10);
+       reg_r(gspca_dev, ET_GPIO_IN, 1);
+       reg_w_val(gspca_dev, ET_ClCK, 0x14); /* 0x14 // 0x16 enabled pattern */
+       reg_w_val(gspca_dev, ET_CTRL, 0x1b);
+
+       /*  compression et subsampling */
+       if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv)
+               value = ET_COMP_VAL1;   /* 320 */
+       else
+               value = ET_COMP_VAL0;   /* 640 */
+       reg_w_val(gspca_dev, ET_COMP, value);
+       reg_w_val(gspca_dev, ET_MAXQt, 0x1f);
+       reg_w_val(gspca_dev, ET_MINQt, 0x04);
+       /* undocumented registers */
+       reg_w_val(gspca_dev, ET_REG1d, 0xff);
+       reg_w_val(gspca_dev, ET_REG1e, 0xff);
+       reg_w_val(gspca_dev, ET_REG1f, 0xff);
+       reg_w_val(gspca_dev, ET_REG20, 0x35);
+       reg_w_val(gspca_dev, ET_REG21, 0x01);
+       reg_w_val(gspca_dev, ET_REG22, 0x00);
+       reg_w_val(gspca_dev, ET_REG23, 0xff);
+       reg_w_val(gspca_dev, ET_REG24, 0xff);
+       reg_w_val(gspca_dev, ET_REG25, 0x0f);
+       /* colors setting */
+       reg_w_val(gspca_dev, 0x30, 0x11);               /* 0x30 */
+       reg_w_val(gspca_dev, 0x31, 0x40);
+       reg_w_val(gspca_dev, 0x32, 0x00);
+       reg_w_val(gspca_dev, ET_O_RED, 0x00);           /* 0x34 */
+       reg_w_val(gspca_dev, ET_O_GREEN1, 0x00);
+       reg_w_val(gspca_dev, ET_O_BLUE, 0x00);
+       reg_w_val(gspca_dev, ET_O_GREEN2, 0x00);
+       /*************/
+       reg_w_val(gspca_dev, ET_G_RED, 0x80);           /* 0x4d */
+       reg_w_val(gspca_dev, ET_G_GREEN1, 0x80);
+       reg_w_val(gspca_dev, ET_G_BLUE, 0x80);
+       reg_w_val(gspca_dev, ET_G_GREEN2, 0x80);
+       reg_w_val(gspca_dev, ET_G_GR_H, 0x00);
+       reg_w_val(gspca_dev, ET_G_GB_H, 0x00);          /* 0x52 */
+       /* Window control registers */
+       reg_w_val(gspca_dev, 0x61, 0x80);               /* use cmc_out */
+       reg_w_val(gspca_dev, 0x62, 0x02);
+       reg_w_val(gspca_dev, 0x63, 0x03);
+       reg_w_val(gspca_dev, 0x64, 0x14);
+       reg_w_val(gspca_dev, 0x65, 0x0e);
+       reg_w_val(gspca_dev, 0x66, 0x02);
+       reg_w_val(gspca_dev, 0x67, 0x02);
+
+       /**************************************/
+       reg_w_val(gspca_dev, ET_SYNCHRO, 0x8f);         /* 0x68 */
+       reg_w_val(gspca_dev, ET_STARTX, 0x69);          /* 0x6a //0x69 */
+       reg_w_val(gspca_dev, ET_STARTY, 0x0d);          /* 0x0d //0x0c */
+       reg_w_val(gspca_dev, ET_WIDTH_LOW, 0x80);
+       reg_w_val(gspca_dev, ET_HEIGTH_LOW, 0xe0);
+       reg_w_val(gspca_dev, ET_W_H_HEIGTH, 0x60);      /* 6d */
+       reg_w_val(gspca_dev, ET_REG6e, 0x86);
+       reg_w_val(gspca_dev, ET_REG6f, 0x01);
+       reg_w_val(gspca_dev, ET_REG70, 0x26);
+       reg_w_val(gspca_dev, ET_REG71, 0x7a);
+       reg_w_val(gspca_dev, ET_REG72, 0x01);
+       /* Clock Pattern registers ***************** */
+       reg_w_val(gspca_dev, ET_REG73, 0x00);
+       reg_w_val(gspca_dev, ET_REG74, 0x18);           /* 0x28 */
+       reg_w_val(gspca_dev, ET_REG75, 0x0f);           /* 0x01 */
+       /**********************************************/
+       reg_w_val(gspca_dev, 0x8a, 0x20);
+       reg_w_val(gspca_dev, 0x8d, 0x0f);
+       reg_w_val(gspca_dev, 0x8e, 0x08);
+       /**************************************/
+       reg_w_val(gspca_dev, 0x03, 0x08);
+       reg_w_val(gspca_dev, ET_PXL_CLK, 0x03);
+       reg_w_val(gspca_dev, 0x81, 0xff);
+       reg_w_val(gspca_dev, 0x80, 0x00);
+       reg_w_val(gspca_dev, 0x81, 0xff);
+       reg_w_val(gspca_dev, 0x80, 0x20);
+       reg_w_val(gspca_dev, 0x03, 0x01);
+       reg_w_val(gspca_dev, 0x03, 0x00);
+       reg_w_val(gspca_dev, 0x03, 0x08);
+       /********************************************/
+
+/*     reg_r(gspca_dev, ET_I2C_BASE, 1);
+                                        always 0x40 as the pas106 ??? */
+       /* set the sensor */
+       if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv)
+               value = 0x04;           /* 320 */
+       else                            /* 640 */
+               value = 0x1e;   /* 0x17  * setting PixelClock
+                                        * 0x03 mean 24/(3+1) = 6 Mhz
+                                        * 0x05 -> 24/(5+1) = 4 Mhz
+                                        * 0x0b -> 24/(11+1) = 2 Mhz
+                                        * 0x17 -> 24/(23+1) = 1 Mhz
+                                        */
+       reg_w_val(gspca_dev, ET_PXL_CLK, value);
+       /* now set by fifo the FormatLine setting */
+       reg_w(gspca_dev, 0x62, FormLine, 6);
+
+       /* set exposure times [ 0..0x78] 0->longvalue 0x78->shortvalue */
+       reg_w_val(gspca_dev, 0x81, 0x47);       /* 0x47; */
+       reg_w_val(gspca_dev, 0x80, 0x40);       /* 0x40; */
+       /* Pedro change */
+       /* Brightness change Brith+ decrease value */
+       /* Brigth- increase value */
+       /* original value = 0x70; */
+       reg_w_val(gspca_dev, 0x81, 0x30);       /* 0x20; - set brightness */
+       reg_w_val(gspca_dev, 0x80, 0x20);       /* 0x20; */
+}
+
+static void setcolors(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       __u8 I2cc[] = { 0x05, 0x02, 0x02, 0x05, 0x0d };
+       __u8 i2cflags = 0x01;
+       /* __u8 green = 0; */
+       __u8 colors = sd->colors;
+
+       I2cc[3] = colors;       /* red */
+       I2cc[0] = 15 - colors;  /* blue */
+       /* green = 15 - ((((7*I2cc[0]) >> 2 ) + I2cc[3]) >> 1); */
+       /* I2cc[1] = I2cc[2] = green; */
+       if (sd->sensor == SENSOR_PAS106) {
+               i2c_w(gspca_dev, PAS106_REG13, &i2cflags, 1, 3);
+               i2c_w(gspca_dev, PAS106_REG9, I2cc, sizeof I2cc, 1);
+       }
+/*     PDEBUG(D_CONF , "Etoms red %d blue %d green %d",
+               I2cc[3], I2cc[0], green); */
+}
+
+static void getcolors(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       if (sd->sensor == SENSOR_PAS106) {
+/*             i2c_r(gspca_dev, PAS106_REG9);           * blue */
+               i2c_r(gspca_dev, PAS106_REG9 + 3);      /* red */
+               sd->colors = gspca_dev->usb_buf[0] & 0x0f;
+       }
+}
+
+static void Et_init1(struct gspca_dev *gspca_dev)
+{
+       __u8 value;
+/*     __u8 I2c0 [] = {0x0a, 0x12, 0x05, 0x22, 0xac, 0x00, 0x01, 0x00}; */
+       __u8 I2c0[] = { 0x0a, 0x12, 0x05, 0x6d, 0xcd, 0x00, 0x01, 0x00 };
+                                               /* try 1/120 0x6d 0xcd 0x40 */
+/*     __u8 I2c0 [] = {0x0a, 0x12, 0x05, 0xfe, 0xfe, 0xc0, 0x01, 0x00};
+                                                * 1/60000 hmm ?? */
+
+       PDEBUG(D_STREAM, "Open Init1 ET");
+       reg_w_val(gspca_dev, ET_GPIO_DIR_CTRL, 7);
+       reg_r(gspca_dev, ET_GPIO_IN, 1);
+       reg_w_val(gspca_dev, ET_RESET_ALL, 1);
+       reg_w_val(gspca_dev, ET_RESET_ALL, 0);
+       reg_w_val(gspca_dev, ET_ClCK, 0x10);
+       reg_w_val(gspca_dev, ET_CTRL, 0x19);
+       /*   compression et subsampling */
+       if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv)
+               value = ET_COMP_VAL1;
+       else
+               value = ET_COMP_VAL0;
+       PDEBUG(D_STREAM, "Open mode %d Compression %d",
+              gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv,
+              value);
+       reg_w_val(gspca_dev, ET_COMP, value);
+       reg_w_val(gspca_dev, ET_MAXQt, 0x1d);
+       reg_w_val(gspca_dev, ET_MINQt, 0x02);
+       /* undocumented registers */
+       reg_w_val(gspca_dev, ET_REG1d, 0xff);
+       reg_w_val(gspca_dev, ET_REG1e, 0xff);
+       reg_w_val(gspca_dev, ET_REG1f, 0xff);
+       reg_w_val(gspca_dev, ET_REG20, 0x35);
+       reg_w_val(gspca_dev, ET_REG21, 0x01);
+       reg_w_val(gspca_dev, ET_REG22, 0x00);
+       reg_w_val(gspca_dev, ET_REG23, 0xf7);
+       reg_w_val(gspca_dev, ET_REG24, 0xff);
+       reg_w_val(gspca_dev, ET_REG25, 0x07);
+       /* colors setting */
+       reg_w_val(gspca_dev, ET_G_RED, 0x80);
+       reg_w_val(gspca_dev, ET_G_GREEN1, 0x80);
+       reg_w_val(gspca_dev, ET_G_BLUE, 0x80);
+       reg_w_val(gspca_dev, ET_G_GREEN2, 0x80);
+       reg_w_val(gspca_dev, ET_G_GR_H, 0x00);
+       reg_w_val(gspca_dev, ET_G_GB_H, 0x00);
+       /* Window control registers */
+       reg_w_val(gspca_dev, ET_SYNCHRO, 0xf0);
+       reg_w_val(gspca_dev, ET_STARTX, 0x56);          /* 0x56 */
+       reg_w_val(gspca_dev, ET_STARTY, 0x05);          /* 0x04 */
+       reg_w_val(gspca_dev, ET_WIDTH_LOW, 0x60);
+       reg_w_val(gspca_dev, ET_HEIGTH_LOW, 0x20);
+       reg_w_val(gspca_dev, ET_W_H_HEIGTH, 0x50);
+       reg_w_val(gspca_dev, ET_REG6e, 0x86);
+       reg_w_val(gspca_dev, ET_REG6f, 0x01);
+       reg_w_val(gspca_dev, ET_REG70, 0x86);
+       reg_w_val(gspca_dev, ET_REG71, 0x14);
+       reg_w_val(gspca_dev, ET_REG72, 0x00);
+       /* Clock Pattern registers */
+       reg_w_val(gspca_dev, ET_REG73, 0x00);
+       reg_w_val(gspca_dev, ET_REG74, 0x00);
+       reg_w_val(gspca_dev, ET_REG75, 0x0a);
+       reg_w_val(gspca_dev, ET_I2C_CLK, 0x04);
+       reg_w_val(gspca_dev, ET_PXL_CLK, 0x01);
+       /* set the sensor */
+       if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) {
+               I2c0[0] = 0x06;
+               i2c_w(gspca_dev, PAS106_REG2, I2c0, sizeof I2c0, 1);
+               i2c_w(gspca_dev, PAS106_REG9, I2c2, sizeof I2c2, 1);
+               value = 0x06;
+               i2c_w(gspca_dev, PAS106_REG2, &value, 1, 1);
+               i2c_w(gspca_dev, PAS106_REG3, I2c3, sizeof I2c3, 1);
+               /* value = 0x1f; */
+               value = 0x04;
+               i2c_w(gspca_dev, PAS106_REG0e, &value, 1, 1);
+       } else {
+               I2c0[0] = 0x0a;
+
+               i2c_w(gspca_dev, PAS106_REG2, I2c0, sizeof I2c0, 1);
+               i2c_w(gspca_dev, PAS106_REG9, I2c2, sizeof I2c2, 1);
+               value = 0x0a;
+               i2c_w(gspca_dev, PAS106_REG2, &value, 1, 1);
+               i2c_w(gspca_dev, PAS106_REG3, I2c3, sizeof I2c3, 1);
+               value = 0x04;
+               /* value = 0x10; */
+               i2c_w(gspca_dev, PAS106_REG0e, &value, 1, 1);
+               /* bit 2 enable bit 1:2 select 0 1 2 3
+                  value = 0x07;                                * curve 0 *
+                  i2c_w(gspca_dev, PAS106_REG0f, &value, 1, 1);
+                */
+       }
+
+/*     value = 0x01; */
+/*     value = 0x22; */
+/*     i2c_w(gspca_dev, PAS106_REG5, &value, 1, 1); */
+       /* magnetude and sign bit for DAC */
+       i2c_w(gspca_dev, PAS106_REG7, I2c4, sizeof I2c4, 1);
+       /* now set by fifo the whole colors setting */
+       reg_w(gspca_dev, ET_G_RED, GainRGBG, 6);
+       getcolors(gspca_dev);
+       setcolors(gspca_dev);
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+                    const struct usb_device_id *id)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       struct cam *cam;
+       __u16 vendor;
+       __u16 product;
+
+       vendor = id->idVendor;
+       product = id->idProduct;
+/*     switch (vendor) { */
+/*     case 0x102c:            * Etoms */
+               switch (product) {
+               case 0x6151:
+                       sd->sensor = SENSOR_PAS106;     /* Etoms61x151 */
+                       break;
+               case 0x6251:
+                       sd->sensor = SENSOR_TAS5130CXX; /* Etoms61x251 */
+                       break;
+/*             } */
+/*             break; */
+       }
+       cam = &gspca_dev->cam;
+       cam->dev_name = (char *) id->driver_info;
+       cam->epaddr = 1;
+       if (sd->sensor == SENSOR_PAS106) {
+               cam->cam_mode = sif_mode;
+               cam->nmodes = sizeof sif_mode / sizeof sif_mode[0];
+       } else {
+               cam->cam_mode = vga_mode;
+               cam->nmodes = sizeof vga_mode / sizeof vga_mode[0];
+       }
+       sd->brightness = BRIGHTNESS_DEF;
+       sd->contrast = CONTRAST_DEF;
+       sd->colors = COLOR_DEF;
+       sd->autogain = AUTOGAIN_DEF;
+       return 0;
+}
+
+/* this function is called at open time */
+static int sd_open(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       if (sd->sensor == SENSOR_PAS106)
+               Et_init1(gspca_dev);
+       else
+               Et_init2(gspca_dev);
+       reg_w_val(gspca_dev, ET_RESET_ALL, 0x08);
+       et_video(gspca_dev, 0);         /* video off */
+       return 0;
+}
+
+/* -- start the camera -- */
+static void sd_start(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       if (sd->sensor == SENSOR_PAS106)
+               Et_init1(gspca_dev);
+       else
+               Et_init2(gspca_dev);
+
+       reg_w_val(gspca_dev, ET_RESET_ALL, 0x08);
+       et_video(gspca_dev, 1);         /* video on */
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+       et_video(gspca_dev, 0);         /* video off */
+}
+
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+}
+
+static void sd_close(struct gspca_dev *gspca_dev)
+{
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       int i;
+       __u8 brightness = sd->brightness;
+
+       for (i = 0; i < 4; i++)
+               reg_w_val(gspca_dev, ET_O_RED + i, brightness);
+}
+
+static void getbrightness(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       int i;
+       int brightness = 0;
+
+       for (i = 0; i < 4; i++) {
+               reg_r(gspca_dev, ET_O_RED + i, 1);
+               brightness += gspca_dev->usb_buf[0];
+       }
+       sd->brightness = brightness >> 3;
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       __u8 RGBG[] = { 0x80, 0x80, 0x80, 0x80, 0x00, 0x00 };
+       __u8 contrast = sd->contrast;
+
+       memset(RGBG, contrast, sizeof(RGBG) - 2);
+       reg_w(gspca_dev, ET_G_RED, RGBG, 6);
+}
+
+static void getcontrast(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       int i;
+       int contrast = 0;
+
+       for (i = 0; i < 4; i++) {
+               reg_r(gspca_dev, ET_G_RED + i, 1);
+               contrast += gspca_dev->usb_buf[0];
+       }
+       sd->contrast = contrast >> 2;
+}
+
+static __u8 Et_getgainG(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       if (sd->sensor == SENSOR_PAS106) {
+               i2c_r(gspca_dev, PAS106_REG0e);
+               PDEBUG(D_CONF, "Etoms gain G %d", gspca_dev->usb_buf[0]);
+               return gspca_dev->usb_buf[0];
+       }
+       return 0x1f;
+}
+
+static void Et_setgainG(struct gspca_dev *gspca_dev, __u8 gain)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       if (sd->sensor == SENSOR_PAS106) {
+               __u8 i2cflags = 0x01;
+
+               i2c_w(gspca_dev, PAS106_REG13, &i2cflags, 1, 3);
+               i2c_w(gspca_dev, PAS106_REG0e, &gain, 1, 1);
+       }
+}
+
+#define BLIMIT(bright) \
+       (__u8)((bright > 0x1f)?0x1f:((bright < 4)?3:bright))
+#define LIMIT(color) \
+       (unsigned char)((color > 0xff)?0xff:((color < 0)?0:color))
+
+static void setautogain(struct gspca_dev *gspca_dev)
+{
+       __u8 luma = 0;
+       __u8 luma_mean = 128;
+       __u8 luma_delta = 20;
+       __u8 spring = 4;
+       int Gbright = 0;
+       __u8 r, g, b;
+
+       Gbright = Et_getgainG(gspca_dev);
+       reg_r(gspca_dev, ET_LUMA_CENTER, 4);
+       g = (gspca_dev->usb_buf[0] + gspca_dev->usb_buf[3]) >> 1;
+       r = gspca_dev->usb_buf[1];
+       b = gspca_dev->usb_buf[2];
+       r = ((r << 8) - (r << 4) - (r << 3)) >> 10;
+       b = ((b << 7) >> 10);
+       g = ((g << 9) + (g << 7) + (g << 5)) >> 10;
+       luma = LIMIT(r + g + b);
+       PDEBUG(D_FRAM, "Etoms luma G %d", luma);
+       if (luma < luma_mean - luma_delta || luma > luma_mean + luma_delta) {
+               Gbright += (luma_mean - luma) >> spring;
+               Gbright = BLIMIT(Gbright);
+               PDEBUG(D_FRAM, "Etoms Gbright %d", Gbright);
+               Et_setgainG(gspca_dev, (__u8) Gbright);
+       }
+}
+
+#undef BLIMIT
+#undef LIMIT
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+                       struct gspca_frame *frame,      /* target */
+                       __u8 *data,                     /* isoc packet */
+                       int len)                        /* iso packet length */
+{
+       struct sd *sd;
+       int seqframe;
+
+       seqframe = data[0] & 0x3f;
+       len = (int) (((data[0] & 0xc0) << 2) | data[1]);
+       if (seqframe == 0x3f) {
+               PDEBUG(D_FRAM,
+                      "header packet found datalength %d !!", len);
+               PDEBUG(D_FRAM, "G %d R %d G %d B %d",
+                      data[2], data[3], data[4], data[5]);
+               data += 30;
+               /* don't change datalength as the chips provided it */
+               frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
+                                       data, 0);
+               gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, len);
+               sd = (struct sd *) gspca_dev;
+               if (sd->ag_cnt >= 0) {
+                       if (--sd->ag_cnt < 0) {
+                               sd->ag_cnt = AG_CNT_START;
+                               setautogain(gspca_dev);
+                       }
+               }
+               return;
+       }
+       if (len) {
+               data += 8;
+               gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
+       } else {                        /* Drop Packet */
+               gspca_dev->last_packet_type = DISCARD_PACKET;
+       }
+}
+
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->brightness = val;
+       if (gspca_dev->streaming)
+               setbrightness(gspca_dev);
+       return 0;
+}
+
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       getbrightness(gspca_dev);
+       *val = sd->brightness;
+       return 0;
+}
+
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->contrast = val;
+       if (gspca_dev->streaming)
+               setcontrast(gspca_dev);
+       return 0;
+}
+
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       getcontrast(gspca_dev);
+       *val = sd->contrast;
+       return 0;
+}
+
+static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->colors = val;
+       if (gspca_dev->streaming)
+               setcolors(gspca_dev);
+       return 0;
+}
+
+static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       getcolors(gspca_dev);
+       *val = sd->colors;
+       return 0;
+}
+
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->autogain = val;
+       if (val)
+               sd->ag_cnt = AG_CNT_START;
+       else
+               sd->ag_cnt = -1;
+       return 0;
+}
+
+static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->autogain;
+       return 0;
+}
+
+/* sub-driver description */
+static struct sd_desc sd_desc = {
+       .name = MODULE_NAME,
+       .ctrls = sd_ctrls,
+       .nctrls = ARRAY_SIZE(sd_ctrls),
+       .config = sd_config,
+       .open = sd_open,
+       .start = sd_start,
+       .stopN = sd_stopN,
+       .stop0 = sd_stop0,
+       .close = sd_close,
+       .pkt_scan = sd_pkt_scan,
+};
+
+/* -- module initialisation -- */
+#define DVNM(name) .driver_info = (kernel_ulong_t) name
+static __devinitdata struct usb_device_id device_table[] = {
+#ifndef CONFIG_USB_ET61X251
+       {USB_DEVICE(0x102c, 0x6151), DVNM("Qcam Sangha CIF")},
+#endif
+       {USB_DEVICE(0x102c, 0x6251), DVNM("Qcam xxxxxx VGA")},
+       {}
+};
+
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+                   const struct usb_device_id *id)
+{
+       return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+                              THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+       .name = MODULE_NAME,
+       .id_table = device_table,
+       .probe = sd_probe,
+       .disconnect = gspca_disconnect,
+};
+
+/* -- module insert / remove -- */
+static int __init sd_mod_init(void)
+{
+       if (usb_register(&sd_driver) < 0)
+               return -1;
+       PDEBUG(D_PROBE, "v%s registered", version);
+       return 0;
+}
+
+static void __exit sd_mod_exit(void)
+{
+       usb_deregister(&sd_driver);
+       PDEBUG(D_PROBE, "deregistered");
+}
+
+module_init(sd_mod_init);
+module_exit(sd_mod_exit);
diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c
new file mode 100644 (file)
index 0000000..16e367c
--- /dev/null
@@ -0,0 +1,1905 @@
+/*
+ * Main USB camera driver
+ *
+ * V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
+ *
+ * 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 MODULE_NAME "gspca"
+
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/vmalloc.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/pagemap.h>
+#include <linux/io.h>
+#include <asm/page.h>
+#include <linux/uaccess.h>
+#include <linux/jiffies.h>
+
+#include "gspca.h"
+
+/* global values */
+#define DEF_NURBS 2            /* default number of URBs */
+
+MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>");
+MODULE_DESCRIPTION("GSPCA USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+#define DRIVER_VERSION_NUMBER  KERNEL_VERSION(2, 1, 7)
+static const char version[] = "2.1.7";
+
+static int video_nr = -1;
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+int gspca_debug = D_ERR | D_PROBE;
+EXPORT_SYMBOL(gspca_debug);
+
+static void PDEBUG_MODE(char *txt, __u32 pixfmt, int w, int h)
+{
+       if ((pixfmt >> 24) >= '0' && (pixfmt >> 24) <= 'z') {
+               PDEBUG(D_CONF|D_STREAM, "%s %c%c%c%c %dx%d",
+                       txt,
+                       pixfmt & 0xff,
+                       (pixfmt >> 8) & 0xff,
+                       (pixfmt >> 16) & 0xff,
+                       pixfmt >> 24,
+                       w, h);
+       } else {
+               PDEBUG(D_CONF|D_STREAM, "%s 0x%08x %dx%d",
+                       txt,
+                       pixfmt,
+                       w, h);
+       }
+}
+#else
+#define PDEBUG_MODE(txt, pixfmt, w, h)
+#endif
+
+/* specific memory types - !! should different from V4L2_MEMORY_xxx */
+#define GSPCA_MEMORY_NO 0      /* V4L2_MEMORY_xxx starts from 1 */
+#define GSPCA_MEMORY_READ 7
+
+#define BUF_ALL_FLAGS (V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE)
+
+/*
+ * VMA operations.
+ */
+static void gspca_vm_open(struct vm_area_struct *vma)
+{
+       struct gspca_frame *frame = vma->vm_private_data;
+
+       frame->vma_use_count++;
+       frame->v4l2_buf.flags |= V4L2_BUF_FLAG_MAPPED;
+}
+
+static void gspca_vm_close(struct vm_area_struct *vma)
+{
+       struct gspca_frame *frame = vma->vm_private_data;
+
+       if (--frame->vma_use_count <= 0)
+               frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_MAPPED;
+}
+
+static struct vm_operations_struct gspca_vm_ops = {
+       .open           = gspca_vm_open,
+       .close          = gspca_vm_close,
+};
+
+/*
+ * fill a video frame from an URB and resubmit
+ */
+static void fill_frame(struct gspca_dev *gspca_dev,
+                       struct urb *urb)
+{
+       struct gspca_frame *frame;
+       __u8 *data;             /* address of data in the iso message */
+       int i, j, len, st;
+       cam_pkt_op pkt_scan;
+
+       if (urb->status != 0) {
+               PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status);
+               return;         /* disconnection ? */
+       }
+       pkt_scan = gspca_dev->sd_desc->pkt_scan;
+       for (i = 0; i < urb->number_of_packets; i++) {
+
+               /* check the availability of the frame buffer */
+               j = gspca_dev->fr_i;
+               j = gspca_dev->fr_queue[j];
+               frame = &gspca_dev->frame[j];
+               if ((frame->v4l2_buf.flags & BUF_ALL_FLAGS)
+                                       != V4L2_BUF_FLAG_QUEUED) {
+                       gspca_dev->last_packet_type = DISCARD_PACKET;
+                       break;
+               }
+
+               /* check the packet status and length */
+               len = urb->iso_frame_desc[i].actual_length;
+               if (len == 0)
+                       continue;
+               st = urb->iso_frame_desc[i].status;
+               if (st) {
+                       PDEBUG(D_ERR,
+                               "ISOC data error: [%d] len=%d, status=%d",
+                               i, len, st);
+                       gspca_dev->last_packet_type = DISCARD_PACKET;
+                       continue;
+               }
+
+               /* let the packet be analyzed by the subdriver */
+               PDEBUG(D_PACK, "packet [%d] o:%d l:%d",
+                       i, urb->iso_frame_desc[i].offset, len);
+               data = (__u8 *) urb->transfer_buffer
+                                       + urb->iso_frame_desc[i].offset;
+               pkt_scan(gspca_dev, frame, data, len);
+       }
+
+       /* resubmit the URB */
+       urb->status = 0;
+       st = usb_submit_urb(urb, GFP_ATOMIC);
+       if (st < 0)
+               PDEBUG(D_ERR|D_PACK, "usb_submit_urb() ret %d", st);
+}
+
+/*
+ * ISOC message interrupt from the USB device
+ *
+ * Analyse each packet and call the subdriver for copy to the frame buffer.
+ */
+static void isoc_irq(struct urb *urb
+)
+{
+       struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context;
+
+       PDEBUG(D_PACK, "isoc irq");
+       if (!gspca_dev->streaming)
+               return;
+       fill_frame(gspca_dev, urb);
+}
+
+/*
+ * add data to the current frame
+ *
+ * This function is called by the subdrivers at interrupt level.
+ *
+ * To build a frame, these ones must add
+ *     - one FIRST_PACKET
+ *     - 0 or many INTER_PACKETs
+ *     - one LAST_PACKET
+ * DISCARD_PACKET invalidates the whole frame.
+ * On LAST_PACKET, a new frame is returned.
+ */
+struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev,
+                                   int packet_type,
+                                   struct gspca_frame *frame,
+                                   const __u8 *data,
+                                   int len)
+{
+       int i, j;
+
+       PDEBUG(D_PACK, "add t:%d l:%d", packet_type, len);
+
+       /* when start of a new frame, if the current frame buffer
+        * is not queued, discard the whole frame */
+       if (packet_type == FIRST_PACKET) {
+               if ((frame->v4l2_buf.flags & BUF_ALL_FLAGS)
+                                               != V4L2_BUF_FLAG_QUEUED) {
+                       gspca_dev->last_packet_type = DISCARD_PACKET;
+                       return frame;
+               }
+               frame->data_end = frame->data;
+               jiffies_to_timeval(get_jiffies_64(),
+                                  &frame->v4l2_buf.timestamp);
+               frame->v4l2_buf.sequence = ++gspca_dev->sequence;
+       } else if (gspca_dev->last_packet_type == DISCARD_PACKET) {
+               return frame;
+       }
+
+       /* append the packet to the frame buffer */
+       if (len > 0) {
+               if (frame->data_end - frame->data + len
+                                                > frame->v4l2_buf.length) {
+                       PDEBUG(D_ERR|D_PACK, "frame overflow %zd > %d",
+                               frame->data_end - frame->data + len,
+                               frame->v4l2_buf.length);
+                       packet_type = DISCARD_PACKET;
+               } else {
+                       memcpy(frame->data_end, data, len);
+                       frame->data_end += len;
+               }
+       }
+       gspca_dev->last_packet_type = packet_type;
+
+       /* if last packet, wake the application and advance in the queue */
+       if (packet_type == LAST_PACKET) {
+               frame->v4l2_buf.bytesused = frame->data_end - frame->data;
+               frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_QUEUED;
+               frame->v4l2_buf.flags |= V4L2_BUF_FLAG_DONE;
+               atomic_inc(&gspca_dev->nevent);
+               wake_up_interruptible(&gspca_dev->wq);  /* event = new frame */
+               i = (gspca_dev->fr_i + 1) % gspca_dev->nframes;
+               gspca_dev->fr_i = i;
+               PDEBUG(D_FRAM, "frame complete len:%d q:%d i:%d o:%d",
+                       frame->v4l2_buf.bytesused,
+                       gspca_dev->fr_q,
+                       i,
+                       gspca_dev->fr_o);
+               j = gspca_dev->fr_queue[i];
+               frame = &gspca_dev->frame[j];
+       }
+       return frame;
+}
+EXPORT_SYMBOL(gspca_frame_add);
+
+static int gspca_is_compressed(__u32 format)
+{
+       switch (format) {
+       case V4L2_PIX_FMT_MJPEG:
+       case V4L2_PIX_FMT_JPEG:
+       case V4L2_PIX_FMT_SPCA561:
+       case V4L2_PIX_FMT_PAC207:
+               return 1;
+       }
+       return 0;
+}
+
+static void *rvmalloc(unsigned long size)
+{
+       void *mem;
+       unsigned long adr;
+
+/*     size = PAGE_ALIGN(size);        (already done) */
+       mem = vmalloc_32(size);
+       if (mem != NULL) {
+               adr = (unsigned long) mem;
+               while ((long) size > 0) {
+                       SetPageReserved(vmalloc_to_page((void *) adr));
+                       adr += PAGE_SIZE;
+                       size -= PAGE_SIZE;
+               }
+       }
+       return mem;
+}
+
+static void rvfree(void *mem, long size)
+{
+       unsigned long adr;
+
+       adr = (unsigned long) mem;
+       while (size > 0) {
+               ClearPageReserved(vmalloc_to_page((void *) adr));
+               adr += PAGE_SIZE;
+               size -= PAGE_SIZE;
+       }
+       vfree(mem);
+}
+
+static int frame_alloc(struct gspca_dev *gspca_dev,
+                       unsigned int count)
+{
+       struct gspca_frame *frame;
+       unsigned int frsz;
+       int i;
+
+       i = gspca_dev->curr_mode;
+       frsz = gspca_dev->cam.cam_mode[i].sizeimage;
+       PDEBUG(D_STREAM, "frame alloc frsz: %d", frsz);
+       frsz = PAGE_ALIGN(frsz);
+       gspca_dev->frsz = frsz;
+       if (count > GSPCA_MAX_FRAMES)
+               count = GSPCA_MAX_FRAMES;
+       gspca_dev->frbuf = rvmalloc(frsz * count);
+       if (!gspca_dev->frbuf) {
+               err("frame alloc failed");
+               return -ENOMEM;
+       }
+       gspca_dev->nframes = count;
+       for (i = 0; i < count; i++) {
+               frame = &gspca_dev->frame[i];
+               frame->v4l2_buf.index = i;
+               frame->v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+               frame->v4l2_buf.flags = 0;
+               frame->v4l2_buf.field = V4L2_FIELD_NONE;
+               frame->v4l2_buf.length = frsz;
+               frame->v4l2_buf.memory = gspca_dev->memory;
+               frame->v4l2_buf.sequence = 0;
+               frame->data = frame->data_end =
+                                       gspca_dev->frbuf + i * frsz;
+               frame->v4l2_buf.m.offset = i * frsz;
+       }
+       gspca_dev->fr_i = gspca_dev->fr_o = gspca_dev->fr_q = 0;
+       gspca_dev->last_packet_type = DISCARD_PACKET;
+       gspca_dev->sequence = 0;
+       atomic_set(&gspca_dev->nevent, 0);
+       return 0;
+}
+
+static void frame_free(struct gspca_dev *gspca_dev)
+{
+       int i;
+
+       PDEBUG(D_STREAM, "frame free");
+       if (gspca_dev->frbuf != NULL) {
+               rvfree(gspca_dev->frbuf,
+                       gspca_dev->nframes * gspca_dev->frsz);
+               gspca_dev->frbuf = NULL;
+               for (i = 0; i < gspca_dev->nframes; i++)
+                       gspca_dev->frame[i].data = NULL;
+       }
+       gspca_dev->nframes = 0;
+}
+
+static void destroy_urbs(struct gspca_dev *gspca_dev)
+{
+       struct urb *urb;
+       unsigned int i;
+
+       PDEBUG(D_STREAM, "kill transfer");
+       for (i = 0; i < MAX_NURBS; ++i) {
+               urb = gspca_dev->urb[i];
+               if (urb == NULL)
+                       break;
+
+               gspca_dev->urb[i] = NULL;
+               usb_kill_urb(urb);
+               if (urb->transfer_buffer != NULL)
+                       usb_buffer_free(gspca_dev->dev,
+                                       urb->transfer_buffer_length,
+                                       urb->transfer_buffer,
+                                       urb->transfer_dma);
+               usb_free_urb(urb);
+       }
+}
+
+/*
+ * search an input isochronous endpoint in an alternate setting
+ */
+static struct usb_host_endpoint *alt_isoc(struct usb_host_interface *alt,
+                                         __u8 epaddr)
+{
+       struct usb_host_endpoint *ep;
+       int i, attr;
+
+       epaddr |= USB_DIR_IN;
+       for (i = 0; i < alt->desc.bNumEndpoints; i++) {
+               ep = &alt->endpoint[i];
+               if (ep->desc.bEndpointAddress == epaddr) {
+                       attr = ep->desc.bmAttributes
+                                               & USB_ENDPOINT_XFERTYPE_MASK;
+                       if (attr == USB_ENDPOINT_XFER_ISOC)
+                               return ep;
+                       break;
+               }
+       }
+       return NULL;
+}
+
+/*
+ * search an input isochronous endpoint
+ *
+ * The endpoint is defined by the subdriver.
+ * Use only the first isoc (some Zoran - 0x0572:0x0001 - have two such ep).
+ * This routine may be called many times when the bandwidth is too small
+ * (the bandwidth is checked on urb submit).
+ */
+struct usb_host_endpoint *get_isoc_ep(struct gspca_dev *gspca_dev)
+{
+       struct usb_interface *intf;
+       struct usb_host_endpoint *ep;
+       int i, ret;
+
+       intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface);
+       ep = NULL;
+       i = gspca_dev->alt;                     /* previous alt setting */
+       while (--i > 0) {                       /* alt 0 is unusable */
+               ep = alt_isoc(&intf->altsetting[i], gspca_dev->cam.epaddr);
+               if (ep)
+                       break;
+       }
+       if (ep == NULL) {
+               err("no ISOC endpoint found");
+               return NULL;
+       }
+       PDEBUG(D_STREAM, "use ISOC alt %d ep 0x%02x",
+                       i, ep->desc.bEndpointAddress);
+       ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, i);
+       if (ret < 0) {
+               err("set interface err %d", ret);
+               return NULL;
+       }
+       gspca_dev->alt = i;             /* memorize the current alt setting */
+       return ep;
+}
+
+/*
+ * create the isochronous URBs
+ */
+static int create_urbs(struct gspca_dev *gspca_dev,
+                       struct usb_host_endpoint *ep)
+{
+       struct urb *urb;
+       int n, nurbs, i, psize, npkt, bsize;
+
+       /* calculate the packet size and the number of packets */
+       psize = le16_to_cpu(ep->desc.wMaxPacketSize);
+
+       /* See paragraph 5.9 / table 5-11 of the usb 2.0 spec. */
+       psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3));
+       npkt = ISO_MAX_SIZE / psize;
+       if (npkt > ISO_MAX_PKT)
+               npkt = ISO_MAX_PKT;
+       bsize = psize * npkt;
+       PDEBUG(D_STREAM,
+               "isoc %d pkts size %d (bsize:%d)", npkt, psize, bsize);
+       nurbs = DEF_NURBS;
+       gspca_dev->nurbs = nurbs;
+       for (n = 0; n < nurbs; n++) {
+               urb = usb_alloc_urb(npkt, GFP_KERNEL);
+               if (!urb) {
+                       err("usb_alloc_urb failed");
+                       return -ENOMEM;
+               }
+               urb->transfer_buffer = usb_buffer_alloc(gspca_dev->dev,
+                                               bsize,
+                                               GFP_KERNEL,
+                                               &urb->transfer_dma);
+
+               if (urb->transfer_buffer == NULL) {
+                       usb_free_urb(urb);
+                       destroy_urbs(gspca_dev);
+                       err("usb_buffer_urb failed");
+                       return -ENOMEM;
+               }
+               gspca_dev->urb[n] = urb;
+               urb->dev = gspca_dev->dev;
+               urb->context = gspca_dev;
+               urb->pipe = usb_rcvisocpipe(gspca_dev->dev,
+                                           ep->desc.bEndpointAddress);
+               urb->transfer_flags = URB_ISO_ASAP
+                                       | URB_NO_TRANSFER_DMA_MAP;
+               urb->interval = ep->desc.bInterval;
+               urb->complete = isoc_irq;
+               urb->number_of_packets = npkt;
+               urb->transfer_buffer_length = bsize;
+               for (i = 0; i < npkt; i++) {
+                       urb->iso_frame_desc[i].length = psize;
+                       urb->iso_frame_desc[i].offset = psize * i;
+               }
+       }
+       return 0;
+}
+
+/*
+ * start the USB transfer
+ */
+static int gspca_init_transfer(struct gspca_dev *gspca_dev)
+{
+       struct usb_host_endpoint *ep;
+       int n, ret;
+
+       if (mutex_lock_interruptible(&gspca_dev->usb_lock))
+               return -ERESTARTSYS;
+
+       /* set the higher alternate setting and
+        * loop until urb submit succeeds */
+       gspca_dev->alt = gspca_dev->nbalt;
+       for (;;) {
+               PDEBUG(D_STREAM, "init transfer alt %d", gspca_dev->alt);
+               ep = get_isoc_ep(gspca_dev);
+               if (ep == NULL) {
+                       ret = -EIO;
+                       goto out;
+               }
+               ret = create_urbs(gspca_dev, ep);
+               if (ret < 0)
+                       goto out;
+
+               /* start the cam */
+               gspca_dev->sd_desc->start(gspca_dev);
+               gspca_dev->streaming = 1;
+               atomic_set(&gspca_dev->nevent, 0);
+
+               /* submit the URBs */
+               for (n = 0; n < gspca_dev->nurbs; n++) {
+                       ret = usb_submit_urb(gspca_dev->urb[n], GFP_KERNEL);
+                       if (ret < 0) {
+                               PDEBUG(D_ERR|D_STREAM,
+                                       "usb_submit_urb [%d] err %d", n, ret);
+                               gspca_dev->streaming = 0;
+                               destroy_urbs(gspca_dev);
+                               if (ret == -ENOSPC)
+                                       break;  /* try the previous alt */
+                               goto out;
+                       }
+               }
+               if (ret >= 0)
+                       break;
+       }
+out:
+       mutex_unlock(&gspca_dev->usb_lock);
+       return ret;
+}
+
+static int gspca_set_alt0(struct gspca_dev *gspca_dev)
+{
+       int ret;
+
+       ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, 0);
+       if (ret < 0)
+               PDEBUG(D_ERR|D_STREAM, "set interface 0 err %d", ret);
+       return ret;
+}
+
+/* Note both the queue and the usb lock should be hold when calling this */
+static void gspca_stream_off(struct gspca_dev *gspca_dev)
+{
+       gspca_dev->streaming = 0;
+       atomic_set(&gspca_dev->nevent, 0);
+       if (gspca_dev->present) {
+               gspca_dev->sd_desc->stopN(gspca_dev);
+               destroy_urbs(gspca_dev);
+               gspca_set_alt0(gspca_dev);
+               gspca_dev->sd_desc->stop0(gspca_dev);
+               PDEBUG(D_STREAM, "stream off OK");
+       }
+}
+
+static void gspca_set_default_mode(struct gspca_dev *gspca_dev)
+{
+       int i;
+
+       i = gspca_dev->cam.nmodes - 1;  /* take the highest mode */
+       gspca_dev->curr_mode = i;
+       gspca_dev->width = gspca_dev->cam.cam_mode[i].width;
+       gspca_dev->height = gspca_dev->cam.cam_mode[i].height;
+       gspca_dev->pixfmt = gspca_dev->cam.cam_mode[i].pixelformat;
+}
+
+static int wxh_to_mode(struct gspca_dev *gspca_dev,
+                       int width, int height)
+{
+       int i;
+
+       for (i = gspca_dev->cam.nmodes; --i > 0; ) {
+               if (width >= gspca_dev->cam.cam_mode[i].width
+                   && height >= gspca_dev->cam.cam_mode[i].height)
+                       break;
+       }
+       return i;
+}
+
+/*
+ * search a mode with the right pixel format
+ */
+static int gspca_get_mode(struct gspca_dev *gspca_dev,
+                       int mode,
+                       int pixfmt)
+{
+       int modeU, modeD;
+
+       modeU = modeD = mode;
+       while ((modeU < gspca_dev->cam.nmodes) || modeD >= 0) {
+               if (--modeD >= 0) {
+                       if (gspca_dev->cam.cam_mode[modeD].pixelformat
+                                                               == pixfmt)
+                               return modeD;
+               }
+               if (++modeU < gspca_dev->cam.nmodes) {
+                       if (gspca_dev->cam.cam_mode[modeU].pixelformat
+                                                               == pixfmt)
+                               return modeU;
+               }
+       }
+       return -EINVAL;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void  *priv,
+                               struct v4l2_fmtdesc *fmtdesc)
+{
+       struct gspca_dev *gspca_dev = priv;
+       int i, j, index;
+       __u32 fmt_tb[8];
+
+       /* give an index to each format */
+       index = 0;
+       j = 0;
+       for (i = gspca_dev->cam.nmodes; --i >= 0; ) {
+               fmt_tb[index] = gspca_dev->cam.cam_mode[i].pixelformat;
+               j = 0;
+               for (;;) {
+                       if (fmt_tb[j] == fmt_tb[index])
+                               break;
+                       j++;
+               }
+               if (j == index) {
+                       if (fmtdesc->index == index)
+                               break;          /* new format */
+                       index++;
+                       if (index >= sizeof fmt_tb / sizeof fmt_tb[0])
+                               return -EINVAL;
+               }
+       }
+       if (i < 0)
+               return -EINVAL;         /* no more format */
+
+       fmtdesc->pixelformat = fmt_tb[index];
+       if (gspca_is_compressed(fmt_tb[index]))
+               fmtdesc->flags = V4L2_FMT_FLAG_COMPRESSED;
+       fmtdesc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       fmtdesc->description[0] = fmtdesc->pixelformat & 0xff;
+       fmtdesc->description[1] = (fmtdesc->pixelformat >> 8) & 0xff;
+       fmtdesc->description[2] = (fmtdesc->pixelformat >> 16) & 0xff;
+       fmtdesc->description[3] = fmtdesc->pixelformat >> 24;
+       fmtdesc->description[4] = '\0';
+       return 0;
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+                           struct v4l2_format *fmt)
+{
+       struct gspca_dev *gspca_dev = priv;
+       int mode;
+
+       if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+               return -EINVAL;
+       mode = gspca_dev->curr_mode;
+       memcpy(&fmt->fmt.pix, &gspca_dev->cam.cam_mode[mode],
+               sizeof fmt->fmt.pix);
+       return 0;
+}
+
+static int try_fmt_vid_cap(struct gspca_dev *gspca_dev,
+                       struct v4l2_format *fmt)
+{
+       int w, h, mode, mode2;
+
+       if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+               return -EINVAL;
+       w = fmt->fmt.pix.width;
+       h = fmt->fmt.pix.height;
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+       if (gspca_debug & D_CONF)
+               PDEBUG_MODE("try fmt cap", fmt->fmt.pix.pixelformat, w, h);
+#endif
+       /* search the closest mode for width and height */
+       mode = wxh_to_mode(gspca_dev, w, h);
+
+       /* OK if right palette */
+       if (gspca_dev->cam.cam_mode[mode].pixelformat
+                                               != fmt->fmt.pix.pixelformat) {
+
+               /* else, search the closest mode with the same pixel format */
+               mode2 = gspca_get_mode(gspca_dev, mode,
+                                       fmt->fmt.pix.pixelformat);
+               if (mode2 >= 0)
+                       mode = mode2;
+/*             else
+                       ;                * no chance, return this mode */
+       }
+       memcpy(&fmt->fmt.pix, &gspca_dev->cam.cam_mode[mode],
+               sizeof fmt->fmt.pix);
+       return mode;                    /* used when s_fmt */
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file,
+                             void *priv,
+                             struct v4l2_format *fmt)
+{
+       struct gspca_dev *gspca_dev = priv;
+       int ret;
+
+       ret = try_fmt_vid_cap(gspca_dev, fmt);
+       if (ret < 0)
+               return ret;
+       return 0;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+                           struct v4l2_format *fmt)
+{
+       struct gspca_dev *gspca_dev = priv;
+       int ret;
+
+       if (mutex_lock_interruptible(&gspca_dev->queue_lock))
+               return -ERESTARTSYS;
+
+       ret = try_fmt_vid_cap(gspca_dev, fmt);
+       if (ret < 0)
+               goto out;
+
+       if (gspca_dev->nframes != 0
+           && fmt->fmt.pix.sizeimage > gspca_dev->frsz) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       if (ret == gspca_dev->curr_mode) {
+               ret = 0;
+               goto out;                       /* same mode */
+       }
+
+       if (gspca_dev->streaming) {
+               ret = -EBUSY;
+               goto out;
+       }
+       gspca_dev->width = fmt->fmt.pix.width;
+       gspca_dev->height = fmt->fmt.pix.height;
+       gspca_dev->pixfmt = fmt->fmt.pix.pixelformat;
+       gspca_dev->curr_mode = ret;
+
+       ret = 0;
+out:
+       mutex_unlock(&gspca_dev->queue_lock);
+       return ret;
+}
+
+static int dev_open(struct inode *inode, struct file *file)
+{
+       struct gspca_dev *gspca_dev;
+       int ret;
+
+       PDEBUG(D_STREAM, "%s open", current->comm);
+       gspca_dev = (struct gspca_dev *) video_devdata(file);
+       if (mutex_lock_interruptible(&gspca_dev->queue_lock))
+               return -ERESTARTSYS;
+       if (!gspca_dev->present) {
+               ret = -ENODEV;
+               goto out;
+       }
+
+       /* if not done yet, initialize the sensor */
+       if (gspca_dev->users == 0) {
+               if (mutex_lock_interruptible(&gspca_dev->usb_lock)) {
+                       ret = -ERESTARTSYS;
+                       goto out;
+               }
+               ret = gspca_dev->sd_desc->open(gspca_dev);
+               mutex_unlock(&gspca_dev->usb_lock);
+               if (ret != 0) {
+                       PDEBUG(D_ERR|D_CONF, "init device failed %d", ret);
+                       goto out;
+               }
+       } else if (gspca_dev->users > 4) {      /* (arbitrary value) */
+               ret = -EBUSY;
+               goto out;
+       }
+       gspca_dev->users++;
+       file->private_data = gspca_dev;
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+       /* activate the v4l2 debug */
+       if (gspca_debug & D_V4L2)
+               gspca_dev->vdev.debug |= 3;
+       else
+               gspca_dev->vdev.debug &= ~3;
+#endif
+out:
+       mutex_unlock(&gspca_dev->queue_lock);
+       if (ret != 0)
+               PDEBUG(D_ERR|D_STREAM, "open failed err %d", ret);
+       else
+               PDEBUG(D_STREAM, "open done");
+       return ret;
+}
+
+static int dev_close(struct inode *inode, struct file *file)
+{
+       struct gspca_dev *gspca_dev = file->private_data;
+
+       PDEBUG(D_STREAM, "%s close", current->comm);
+       if (mutex_lock_interruptible(&gspca_dev->queue_lock))
+               return -ERESTARTSYS;
+       gspca_dev->users--;
+
+       /* if the file did the capture, free the streaming resources */
+       if (gspca_dev->capt_file == file) {
+               mutex_lock(&gspca_dev->usb_lock);
+               if (gspca_dev->streaming)
+                       gspca_stream_off(gspca_dev);
+               gspca_dev->sd_desc->close(gspca_dev);
+               mutex_unlock(&gspca_dev->usb_lock);
+               frame_free(gspca_dev);
+               gspca_dev->capt_file = NULL;
+               gspca_dev->memory = GSPCA_MEMORY_NO;
+       }
+       file->private_data = NULL;
+       mutex_unlock(&gspca_dev->queue_lock);
+       PDEBUG(D_STREAM, "close done");
+       return 0;
+}
+
+static int vidioc_querycap(struct file *file, void  *priv,
+                          struct v4l2_capability *cap)
+{
+       struct gspca_dev *gspca_dev = priv;
+
+       memset(cap, 0, sizeof *cap);
+       strncpy(cap->driver, gspca_dev->sd_desc->name, sizeof cap->driver);
+       strncpy(cap->card, gspca_dev->cam.dev_name, sizeof cap->card);
+       strncpy(cap->bus_info, gspca_dev->dev->bus->bus_name,
+               sizeof cap->bus_info);
+       cap->version = DRIVER_VERSION_NUMBER;
+       cap->capabilities = V4L2_CAP_VIDEO_CAPTURE
+                         | V4L2_CAP_STREAMING
+                         | V4L2_CAP_READWRITE;
+       return 0;
+}
+
+/* the use of V4L2_CTRL_FLAG_NEXT_CTRL asks for the controls to be sorted */
+static int vidioc_queryctrl(struct file *file, void *priv,
+                          struct v4l2_queryctrl *q_ctrl)
+{
+       struct gspca_dev *gspca_dev = priv;
+       int i;
+       u32 id;
+
+       id = q_ctrl->id;
+       if (id & V4L2_CTRL_FLAG_NEXT_CTRL) {
+               id &= V4L2_CTRL_ID_MASK;
+               id++;
+               for (i = 0; i < gspca_dev->sd_desc->nctrls; i++) {
+                       if (id >= gspca_dev->sd_desc->ctrls[i].qctrl.id) {
+                               memcpy(q_ctrl,
+                                       &gspca_dev->sd_desc->ctrls[i].qctrl,
+                                       sizeof *q_ctrl);
+                               return 0;
+                       }
+               }
+               return -EINVAL;
+       }
+       for (i = 0; i < gspca_dev->sd_desc->nctrls; i++) {
+               if (id == gspca_dev->sd_desc->ctrls[i].qctrl.id) {
+                       memcpy(q_ctrl,
+                               &gspca_dev->sd_desc->ctrls[i].qctrl,
+                               sizeof *q_ctrl);
+                       return 0;
+               }
+       }
+       if (id >= V4L2_CID_BASE
+           && id <= V4L2_CID_LASTP1) {
+               q_ctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
+               return 0;
+       }
+       return -EINVAL;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+                        struct v4l2_control *ctrl)
+{
+       struct gspca_dev *gspca_dev = priv;
+       const struct ctrl *ctrls;
+       int i, ret;
+
+       for (i = 0, ctrls = gspca_dev->sd_desc->ctrls;
+            i < gspca_dev->sd_desc->nctrls;
+            i++, ctrls++) {
+               if (ctrl->id != ctrls->qctrl.id)
+                       continue;
+               if (ctrl->value < ctrls->qctrl.minimum
+                   && ctrl->value > ctrls->qctrl.maximum)
+                       return -ERANGE;
+               PDEBUG(D_CONF, "set ctrl [%08x] = %d", ctrl->id, ctrl->value);
+               if (mutex_lock_interruptible(&gspca_dev->usb_lock))
+                       return -ERESTARTSYS;
+               ret = ctrls->set(gspca_dev, ctrl->value);
+               mutex_unlock(&gspca_dev->usb_lock);
+               return ret;
+       }
+       return -EINVAL;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *priv,
+                        struct v4l2_control *ctrl)
+{
+       struct gspca_dev *gspca_dev = priv;
+
+       const struct ctrl *ctrls;
+       int i, ret;
+
+       for (i = 0, ctrls = gspca_dev->sd_desc->ctrls;
+            i < gspca_dev->sd_desc->nctrls;
+            i++, ctrls++) {
+               if (ctrl->id != ctrls->qctrl.id)
+                       continue;
+               if (mutex_lock_interruptible(&gspca_dev->usb_lock))
+                       return -ERESTARTSYS;
+               ret = ctrls->get(gspca_dev, &ctrl->value);
+               mutex_unlock(&gspca_dev->usb_lock);
+               return ret;
+       }
+       return -EINVAL;
+}
+
+static int vidioc_querymenu(struct file *file, void *priv,
+                           struct v4l2_querymenu *qmenu)
+{
+       struct gspca_dev *gspca_dev = priv;
+
+       if (!gspca_dev->sd_desc->querymenu)
+               return -EINVAL;
+       return gspca_dev->sd_desc->querymenu(gspca_dev, qmenu);
+}
+
+static int vidioc_enum_input(struct file *file, void *priv,
+                               struct v4l2_input *input)
+{
+       struct gspca_dev *gspca_dev = priv;
+
+       if (input->index != 0)
+               return -EINVAL;
+       memset(input, 0, sizeof *input);
+       input->type = V4L2_INPUT_TYPE_CAMERA;
+       strncpy(input->name, gspca_dev->sd_desc->name,
+               sizeof input->name);
+       return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+       *i = 0;
+       return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+       if (i > 0)
+               return -EINVAL;
+       return (0);
+}
+
+static int vidioc_reqbufs(struct file *file, void *priv,
+                         struct v4l2_requestbuffers *rb)
+{
+       struct gspca_dev *gspca_dev = priv;
+       int i, ret = 0;
+
+       if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+               return -EINVAL;
+       switch (rb->memory) {
+       case GSPCA_MEMORY_READ:                 /* (internal call) */
+       case V4L2_MEMORY_MMAP:
+       case V4L2_MEMORY_USERPTR:
+               break;
+       default:
+               return -EINVAL;
+       }
+       if (mutex_lock_interruptible(&gspca_dev->queue_lock))
+               return -ERESTARTSYS;
+
+       if (gspca_dev->memory != GSPCA_MEMORY_NO
+           && gspca_dev->memory != rb->memory) {
+               ret = -EBUSY;
+               goto out;
+       }
+
+       /* only one file may do the capture */
+       if (gspca_dev->capt_file != NULL
+           && gspca_dev->capt_file != file) {
+               ret = -EBUSY;
+               goto out;
+       }
+
+       /* if allocated, the buffers must not be mapped */
+       for (i = 0; i < gspca_dev->nframes; i++) {
+               if (gspca_dev->frame[i].vma_use_count) {
+                       ret = -EBUSY;
+                       goto out;
+               }
+       }
+
+       /* stop streaming */
+       if (gspca_dev->streaming) {
+               mutex_lock(&gspca_dev->usb_lock);
+               gspca_stream_off(gspca_dev);
+               mutex_unlock(&gspca_dev->usb_lock);
+       }
+
+       /* free the previous allocated buffers, if any */
+       if (gspca_dev->nframes != 0) {
+               frame_free(gspca_dev);
+               gspca_dev->capt_file = NULL;
+       }
+       if (rb->count == 0)                     /* unrequest */
+               goto out;
+       gspca_dev->memory = rb->memory;
+       ret = frame_alloc(gspca_dev, rb->count);
+       if (ret == 0) {
+               rb->count = gspca_dev->nframes;
+               gspca_dev->capt_file = file;
+       }
+out:
+       mutex_unlock(&gspca_dev->queue_lock);
+       PDEBUG(D_STREAM, "reqbufs st:%d c:%d", ret, rb->count);
+       return ret;
+}
+
+static int vidioc_querybuf(struct file *file, void *priv,
+                          struct v4l2_buffer *v4l2_buf)
+{
+       struct gspca_dev *gspca_dev = priv;
+       struct gspca_frame *frame;
+
+       if (v4l2_buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE
+           || v4l2_buf->index < 0
+           || v4l2_buf->index >= gspca_dev->nframes)
+               return -EINVAL;
+
+       frame = &gspca_dev->frame[v4l2_buf->index];
+       memcpy(v4l2_buf, &frame->v4l2_buf, sizeof *v4l2_buf);
+       return 0;
+}
+
+static int vidioc_streamon(struct file *file, void *priv,
+                          enum v4l2_buf_type buf_type)
+{
+       struct gspca_dev *gspca_dev = priv;
+       int ret;
+
+       if (buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+               return -EINVAL;
+       if (mutex_lock_interruptible(&gspca_dev->queue_lock))
+               return -ERESTARTSYS;
+       if (!gspca_dev->present) {
+               ret = -ENODEV;
+               goto out;
+       }
+       if (gspca_dev->nframes == 0) {
+               ret = -EINVAL;
+               goto out;
+       }
+       if (!gspca_dev->streaming) {
+               ret = gspca_init_transfer(gspca_dev);
+               if (ret < 0)
+                       goto out;
+       }
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+       if (gspca_debug & D_STREAM) {
+               PDEBUG_MODE("stream on OK",
+                       gspca_dev->pixfmt,
+                       gspca_dev->width,
+                       gspca_dev->height);
+       }
+#endif
+       ret = 0;
+out:
+       mutex_unlock(&gspca_dev->queue_lock);
+       return ret;
+}
+
+static int vidioc_streamoff(struct file *file, void *priv,
+                               enum v4l2_buf_type buf_type)
+{
+       struct gspca_dev *gspca_dev = priv;
+       int i, ret;
+
+       if (buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+               return -EINVAL;
+       if (!gspca_dev->streaming)
+               return 0;
+       if (mutex_lock_interruptible(&gspca_dev->queue_lock))
+               return -ERESTARTSYS;
+
+       /* stop streaming */
+       if (mutex_lock_interruptible(&gspca_dev->usb_lock)) {
+               ret = -ERESTARTSYS;
+               goto out;
+       }
+       gspca_stream_off(gspca_dev);
+       mutex_unlock(&gspca_dev->usb_lock);
+
+       /* empty the application queues */
+       for (i = 0; i < gspca_dev->nframes; i++)
+               gspca_dev->frame[i].v4l2_buf.flags &= ~BUF_ALL_FLAGS;
+       gspca_dev->fr_i = gspca_dev->fr_o = gspca_dev->fr_q = 0;
+       gspca_dev->last_packet_type = DISCARD_PACKET;
+       gspca_dev->sequence = 0;
+       atomic_set(&gspca_dev->nevent, 0);
+       ret = 0;
+out:
+       mutex_unlock(&gspca_dev->queue_lock);
+       return ret;
+}
+
+static int vidioc_g_jpegcomp(struct file *file, void *priv,
+                       struct v4l2_jpegcompression *jpegcomp)
+{
+       struct gspca_dev *gspca_dev = priv;
+       int ret;
+
+       if (!gspca_dev->sd_desc->get_jcomp)
+               return -EINVAL;
+       if (mutex_lock_interruptible(&gspca_dev->usb_lock))
+               return -ERESTARTSYS;
+       ret = gspca_dev->sd_desc->get_jcomp(gspca_dev, jpegcomp);
+       mutex_unlock(&gspca_dev->usb_lock);
+       return ret;
+}
+
+static int vidioc_s_jpegcomp(struct file *file, void *priv,
+                       struct v4l2_jpegcompression *jpegcomp)
+{
+       struct gspca_dev *gspca_dev = priv;
+       int ret;
+
+       if (mutex_lock_interruptible(&gspca_dev->usb_lock))
+               return -ERESTARTSYS;
+       if (!gspca_dev->sd_desc->set_jcomp)
+               return -EINVAL;
+       ret = gspca_dev->sd_desc->set_jcomp(gspca_dev, jpegcomp);
+       mutex_unlock(&gspca_dev->usb_lock);
+       return ret;
+}
+
+static int vidioc_g_parm(struct file *filp, void *priv,
+                       struct v4l2_streamparm *parm)
+{
+       struct gspca_dev *gspca_dev = priv;
+
+       memset(parm, 0, sizeof *parm);
+       parm->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       parm->parm.capture.readbuffers = gspca_dev->nbufread;
+       return 0;
+}
+
+static int vidioc_s_parm(struct file *filp, void *priv,
+                       struct v4l2_streamparm *parm)
+{
+       struct gspca_dev *gspca_dev = priv;
+       int n;
+
+       n = parm->parm.capture.readbuffers;
+       if (n == 0 || n > GSPCA_MAX_FRAMES)
+               parm->parm.capture.readbuffers = gspca_dev->nbufread;
+       else
+               gspca_dev->nbufread = n;
+       return 0;
+}
+
+static int vidioc_s_std(struct file *filp, void *priv,
+                       v4l2_std_id *parm)
+{
+       return 0;
+}
+
+#ifdef CONFIG_VIDEO_V4L1_COMPAT
+static int vidiocgmbuf(struct file *file, void *priv,
+                       struct video_mbuf *mbuf)
+{
+       struct gspca_dev *gspca_dev = file->private_data;
+       int i;
+
+       PDEBUG(D_STREAM, "cgmbuf");
+       if (gspca_dev->nframes == 0) {
+               int ret;
+
+               {
+                       struct v4l2_format fmt;
+
+                       memset(&fmt, 0, sizeof fmt);
+                       fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+                       i = gspca_dev->cam.nmodes - 1;  /* highest mode */
+                       fmt.fmt.pix.width = gspca_dev->cam.cam_mode[i].width;
+                       fmt.fmt.pix.height = gspca_dev->cam.cam_mode[i].height;
+                       fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_BGR24;
+                       ret = vidioc_s_fmt_vid_cap(file, priv, &fmt);
+                       if (ret != 0)
+                               return ret;
+               }
+               {
+                       struct v4l2_requestbuffers rb;
+
+                       memset(&rb, 0, sizeof rb);
+                       rb.count = 4;
+                       rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+                       rb.memory = V4L2_MEMORY_MMAP;
+                       ret = vidioc_reqbufs(file, priv, &rb);
+                       if (ret != 0)
+                               return ret;
+               }
+       }
+       mbuf->frames = gspca_dev->nframes;
+       mbuf->size = gspca_dev->frsz * gspca_dev->nframes;
+       for (i = 0; i < mbuf->frames; i++)
+               mbuf->offsets[i] = gspca_dev->frame[i].v4l2_buf.m.offset;
+       return 0;
+}
+#endif
+
+static int dev_mmap(struct file *file, struct vm_area_struct *vma)
+{
+       struct gspca_dev *gspca_dev = file->private_data;
+       struct gspca_frame *frame;
+       struct page *page;
+       unsigned long addr, start, size;
+       int i, ret;
+
+       start = vma->vm_start;
+       size = vma->vm_end - vma->vm_start;
+       PDEBUG(D_STREAM, "mmap start:%08x size:%d", (int) start, (int) size);
+
+       if (mutex_lock_interruptible(&gspca_dev->queue_lock))
+               return -ERESTARTSYS;
+       if (!gspca_dev->present) {
+               ret = -ENODEV;
+               goto out;
+       }
+       if (gspca_dev->capt_file != file) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       frame = NULL;
+       for (i = 0; i < gspca_dev->nframes; ++i) {
+               if (gspca_dev->frame[i].v4l2_buf.memory != V4L2_MEMORY_MMAP) {
+                       PDEBUG(D_STREAM, "mmap bad memory type");
+                       break;
+               }
+               if ((gspca_dev->frame[i].v4l2_buf.m.offset >> PAGE_SHIFT)
+                                               == vma->vm_pgoff) {
+                       frame = &gspca_dev->frame[i];
+                       break;
+               }
+       }
+       if (frame == NULL) {
+               PDEBUG(D_STREAM, "mmap no frame buffer found");
+               ret = -EINVAL;
+               goto out;
+       }
+#ifdef CONFIG_VIDEO_V4L1_COMPAT
+       /* v4l1 maps all the buffers */
+       if (i != 0
+           || size != frame->v4l2_buf.length * gspca_dev->nframes)
+#endif
+           if (size != frame->v4l2_buf.length) {
+               PDEBUG(D_STREAM, "mmap bad size");
+               ret = -EINVAL;
+               goto out;
+       }
+
+       /*
+        * - VM_IO marks the area as being a mmaped region for I/O to a
+        *   device. It also prevents the region from being core dumped.
+        */
+       vma->vm_flags |= VM_IO;
+
+       addr = (unsigned long) frame->data;
+       while (size > 0) {
+               page = vmalloc_to_page((void *) addr);
+               ret = vm_insert_page(vma, start, page);
+               if (ret < 0)
+                       goto out;
+               start += PAGE_SIZE;
+               addr += PAGE_SIZE;
+               size -= PAGE_SIZE;
+       }
+
+       vma->vm_ops = &gspca_vm_ops;
+       vma->vm_private_data = frame;
+       gspca_vm_open(vma);
+       ret = 0;
+out:
+       mutex_unlock(&gspca_dev->queue_lock);
+       return ret;
+}
+
+/*
+ * wait for a video frame
+ *
+ * If a frame is ready, its index is returned.
+ */
+static int frame_wait(struct gspca_dev *gspca_dev,
+                       int nonblock_ing)
+{
+       struct gspca_frame *frame;
+       int i, j, ret;
+
+       /* check if a frame is ready */
+       i = gspca_dev->fr_o;
+       j = gspca_dev->fr_queue[i];
+       frame = &gspca_dev->frame[j];
+       if (frame->v4l2_buf.flags & V4L2_BUF_FLAG_DONE) {
+               atomic_dec(&gspca_dev->nevent);
+               goto ok;
+       }
+       if (nonblock_ing)                       /* no frame yet */
+               return -EAGAIN;
+
+       /* wait till a frame is ready */
+       for (;;) {
+               ret = wait_event_interruptible_timeout(gspca_dev->wq,
+                                       atomic_read(&gspca_dev->nevent) > 0,
+                                       msecs_to_jiffies(3000));
+               if (ret <= 0) {
+                       if (ret < 0)
+                               return ret;     /* interrupt */
+                       return -EIO;            /* timeout */
+               }
+               atomic_dec(&gspca_dev->nevent);
+               if (!gspca_dev->streaming || !gspca_dev->present)
+                       return -EIO;
+               i = gspca_dev->fr_o;
+               j = gspca_dev->fr_queue[i];
+               frame = &gspca_dev->frame[j];
+               if (frame->v4l2_buf.flags & V4L2_BUF_FLAG_DONE)
+                       break;
+       }
+ok:
+       gspca_dev->fr_o = (i + 1) % gspca_dev->nframes;
+       PDEBUG(D_FRAM, "frame wait q:%d i:%d o:%d",
+               gspca_dev->fr_q,
+               gspca_dev->fr_i,
+               gspca_dev->fr_o);
+
+       if (gspca_dev->sd_desc->dq_callback) {
+               mutex_lock(&gspca_dev->usb_lock);
+               gspca_dev->sd_desc->dq_callback(gspca_dev);
+               mutex_unlock(&gspca_dev->usb_lock);
+       }
+       return j;
+}
+
+/*
+ * dequeue a video buffer
+ *
+ * If nonblock_ing is false, block until a buffer is available.
+ */
+static int vidioc_dqbuf(struct file *file, void *priv,
+                       struct v4l2_buffer *v4l2_buf)
+{
+       struct gspca_dev *gspca_dev = priv;
+       struct gspca_frame *frame;
+       int i, ret;
+
+       PDEBUG(D_FRAM, "dqbuf");
+       if (v4l2_buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+               return -EINVAL;
+       if (v4l2_buf->memory != gspca_dev->memory)
+               return -EINVAL;
+
+       /* if not streaming, be sure the application will not loop forever */
+       if (!(file->f_flags & O_NONBLOCK)
+           && !gspca_dev->streaming && gspca_dev->users == 1)
+               return -EINVAL;
+
+       /* only the capturing file may dequeue */
+       if (gspca_dev->capt_file != file)
+               return -EINVAL;
+
+       /* only one dequeue / read at a time */
+       if (mutex_lock_interruptible(&gspca_dev->read_lock))
+               return -ERESTARTSYS;
+
+       ret = frame_wait(gspca_dev, file->f_flags & O_NONBLOCK);
+       if (ret < 0)
+               goto out;
+       i = ret;                                /* frame index */
+       frame = &gspca_dev->frame[i];
+       if (gspca_dev->memory == V4L2_MEMORY_USERPTR) {
+               if (copy_to_user((__u8 *) frame->v4l2_buf.m.userptr,
+                                frame->data,
+                                frame->v4l2_buf.bytesused)) {
+                       PDEBUG(D_ERR|D_STREAM,
+                               "dqbuf cp to user failed");
+                       ret = -EFAULT;
+                       goto out;
+               }
+       }
+       frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_DONE;
+       memcpy(v4l2_buf, &frame->v4l2_buf, sizeof *v4l2_buf);
+       PDEBUG(D_FRAM, "dqbuf %d", i);
+       ret = 0;
+out:
+       mutex_unlock(&gspca_dev->read_lock);
+       return ret;
+}
+
+/*
+ * queue a video buffer
+ *
+ * Attempting to queue a buffer that has already been
+ * queued will return -EINVAL.
+ */
+static int vidioc_qbuf(struct file *file, void *priv,
+                       struct v4l2_buffer *v4l2_buf)
+{
+       struct gspca_dev *gspca_dev = priv;
+       struct gspca_frame *frame;
+       int i, index, ret;
+
+       PDEBUG(D_FRAM, "qbuf %d", v4l2_buf->index);
+       if (v4l2_buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+               return -EINVAL;
+
+       if (mutex_lock_interruptible(&gspca_dev->queue_lock))
+               return -ERESTARTSYS;
+
+       index = v4l2_buf->index;
+       if ((unsigned) index >= gspca_dev->nframes) {
+               PDEBUG(D_FRAM,
+                       "qbuf idx %d >= %d", index, gspca_dev->nframes);
+               ret = -EINVAL;
+               goto out;
+       }
+       if (v4l2_buf->memory != gspca_dev->memory) {
+               PDEBUG(D_FRAM, "qbuf bad memory type");
+               ret = -EINVAL;
+               goto out;
+       }
+
+       frame = &gspca_dev->frame[index];
+       if (frame->v4l2_buf.flags & BUF_ALL_FLAGS) {
+               PDEBUG(D_FRAM, "qbuf bad state");
+               ret = -EINVAL;
+               goto out;
+       }
+
+       frame->v4l2_buf.flags |= V4L2_BUF_FLAG_QUEUED;
+/*     frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_DONE; */
+
+       if (frame->v4l2_buf.memory == V4L2_MEMORY_USERPTR) {
+               frame->v4l2_buf.m.userptr = v4l2_buf->m.userptr;
+               frame->v4l2_buf.length = v4l2_buf->length;
+       }
+
+       /* put the buffer in the 'queued' queue */
+       i = gspca_dev->fr_q;
+       gspca_dev->fr_queue[i] = index;
+       gspca_dev->fr_q = (i + 1) % gspca_dev->nframes;
+       PDEBUG(D_FRAM, "qbuf q:%d i:%d o:%d",
+               gspca_dev->fr_q,
+               gspca_dev->fr_i,
+               gspca_dev->fr_o);
+
+       v4l2_buf->flags |= V4L2_BUF_FLAG_QUEUED;
+       v4l2_buf->flags &= ~V4L2_BUF_FLAG_DONE;
+       ret = 0;
+out:
+       mutex_unlock(&gspca_dev->queue_lock);
+       return ret;
+}
+
+/*
+ * allocate the resources for read()
+ */
+static int read_alloc(struct gspca_dev *gspca_dev,
+                       struct file *file)
+{
+       struct v4l2_buffer v4l2_buf;
+       int i, ret;
+
+       PDEBUG(D_STREAM, "read alloc");
+       if (gspca_dev->nframes == 0) {
+               struct v4l2_requestbuffers rb;
+
+               memset(&rb, 0, sizeof rb);
+               rb.count = gspca_dev->nbufread;
+               rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+               rb.memory = GSPCA_MEMORY_READ;
+               ret = vidioc_reqbufs(file, gspca_dev, &rb);
+               if (ret != 0) {
+                       PDEBUG(D_STREAM, "read reqbuf err %d", ret);
+                       return ret;
+               }
+               memset(&v4l2_buf, 0, sizeof v4l2_buf);
+               v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+               v4l2_buf.memory = GSPCA_MEMORY_READ;
+               for (i = 0; i < gspca_dev->nbufread; i++) {
+                       v4l2_buf.index = i;
+                       ret = vidioc_qbuf(file, gspca_dev, &v4l2_buf);
+                       if (ret != 0) {
+                               PDEBUG(D_STREAM, "read qbuf err: %d", ret);
+                               return ret;
+                       }
+               }
+               gspca_dev->memory = GSPCA_MEMORY_READ;
+       }
+
+       /* start streaming */
+       ret = vidioc_streamon(file, gspca_dev, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+       if (ret != 0)
+               PDEBUG(D_STREAM, "read streamon err %d", ret);
+       return ret;
+}
+
+static unsigned int dev_poll(struct file *file, poll_table *wait)
+{
+       struct gspca_dev *gspca_dev = file->private_data;
+       int i, ret;
+
+       PDEBUG(D_FRAM, "poll");
+
+       poll_wait(file, &gspca_dev->wq, wait);
+       if (!gspca_dev->present)
+               return POLLERR;
+
+       /* if reqbufs is not done, the user would use read() */
+       if (gspca_dev->nframes == 0) {
+               if (gspca_dev->memory != GSPCA_MEMORY_NO)
+                       return POLLERR;         /* not the 1st time */
+               ret = read_alloc(gspca_dev, file);
+               if (ret != 0)
+                       return POLLERR;
+       }
+
+       if (mutex_lock_interruptible(&gspca_dev->queue_lock) != 0)
+               return POLLERR;
+       if (!gspca_dev->present) {
+               ret = POLLERR;
+               goto out;
+       }
+
+       /* check the next incoming buffer */
+       i = gspca_dev->fr_o;
+       i = gspca_dev->fr_queue[i];
+       if (gspca_dev->frame[i].v4l2_buf.flags & V4L2_BUF_FLAG_DONE)
+               ret = POLLIN | POLLRDNORM;      /* something to read */
+       else
+               ret = 0;
+out:
+       mutex_unlock(&gspca_dev->queue_lock);
+       return ret;
+}
+
+static ssize_t dev_read(struct file *file, char __user *data,
+                   size_t count, loff_t *ppos)
+{
+       struct gspca_dev *gspca_dev = file->private_data;
+       struct gspca_frame *frame;
+       struct v4l2_buffer v4l2_buf;
+       struct timeval timestamp;
+       int n, ret, ret2;
+
+       PDEBUG(D_FRAM, "read (%zd)", count);
+       if (!gspca_dev->present)
+               return -ENODEV;
+       switch (gspca_dev->memory) {
+       case GSPCA_MEMORY_NO:                   /* first time */
+               ret = read_alloc(gspca_dev, file);
+               if (ret != 0)
+                       return ret;
+               break;
+       case GSPCA_MEMORY_READ:
+               if (gspca_dev->capt_file == file)
+                       break;
+               /* fall thru */
+       default:
+               return -EINVAL;
+       }
+
+       /* get a frame */
+       jiffies_to_timeval(get_jiffies_64(), &timestamp);
+       timestamp.tv_sec--;
+       n = 2;
+       for (;;) {
+               memset(&v4l2_buf, 0, sizeof v4l2_buf);
+               v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+               v4l2_buf.memory = GSPCA_MEMORY_READ;
+               ret = vidioc_dqbuf(file, gspca_dev, &v4l2_buf);
+               if (ret != 0) {
+                       PDEBUG(D_STREAM, "read dqbuf err %d", ret);
+                       return ret;
+               }
+
+               /* if the process slept for more than 1 second,
+                * get anewer frame */
+               frame = &gspca_dev->frame[v4l2_buf.index];
+               if (--n < 0)
+                       break;                  /* avoid infinite loop */
+               if (frame->v4l2_buf.timestamp.tv_sec >= timestamp.tv_sec)
+                       break;
+               ret = vidioc_qbuf(file, gspca_dev, &v4l2_buf);
+               if (ret != 0) {
+                       PDEBUG(D_STREAM, "read qbuf err %d", ret);
+                       return ret;
+               }
+       }
+
+       /* copy the frame */
+       if (count > frame->v4l2_buf.bytesused)
+               count = frame->v4l2_buf.bytesused;
+       ret = copy_to_user(data, frame->data, count);
+       if (ret != 0) {
+               PDEBUG(D_ERR|D_STREAM,
+                       "read cp to user lack %d / %zd", ret, count);
+               ret = -EFAULT;
+               goto out;
+       }
+       ret = count;
+out:
+       /* in each case, requeue the buffer */
+       ret2 = vidioc_qbuf(file, gspca_dev, &v4l2_buf);
+       if (ret2 != 0)
+               return ret2;
+       return ret;
+}
+
+static void dev_release(struct video_device *vfd)
+{
+       /* nothing */
+}
+
+static struct file_operations dev_fops = {
+       .owner = THIS_MODULE,
+       .open = dev_open,
+       .release = dev_close,
+       .read = dev_read,
+       .mmap = dev_mmap,
+       .ioctl = video_ioctl2,
+#ifdef CONFIG_COMPAT
+       .compat_ioctl = v4l_compat_ioctl32,
+#endif
+       .llseek = no_llseek,
+       .poll   = dev_poll,
+};
+
+static struct video_device gspca_template = {
+       .name = "gspca main driver",
+       .type = VID_TYPE_CAPTURE,
+       .fops = &dev_fops,
+       .release = dev_release,         /* mandatory */
+       .minor = -1,
+       .vidioc_querycap        = vidioc_querycap,
+       .vidioc_dqbuf           = vidioc_dqbuf,
+       .vidioc_qbuf            = vidioc_qbuf,
+       .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+       .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+       .vidioc_g_fmt_vid_cap   = vidioc_g_fmt_vid_cap,
+       .vidioc_s_fmt_vid_cap   = vidioc_s_fmt_vid_cap,
+       .vidioc_streamon        = vidioc_streamon,
+       .vidioc_queryctrl       = vidioc_queryctrl,
+       .vidioc_g_ctrl          = vidioc_g_ctrl,
+       .vidioc_s_ctrl          = vidioc_s_ctrl,
+       .vidioc_querymenu       = vidioc_querymenu,
+       .vidioc_enum_input      = vidioc_enum_input,
+       .vidioc_g_input         = vidioc_g_input,
+       .vidioc_s_input         = vidioc_s_input,
+       .vidioc_reqbufs         = vidioc_reqbufs,
+       .vidioc_querybuf        = vidioc_querybuf,
+       .vidioc_streamoff       = vidioc_streamoff,
+       .vidioc_g_jpegcomp      = vidioc_g_jpegcomp,
+       .vidioc_s_jpegcomp      = vidioc_s_jpegcomp,
+       .vidioc_g_parm          = vidioc_g_parm,
+       .vidioc_s_parm          = vidioc_s_parm,
+       .vidioc_s_std           = vidioc_s_std,
+#ifdef CONFIG_VIDEO_V4L1_COMPAT
+       .vidiocgmbuf          = vidiocgmbuf,
+#endif
+};
+
+/*
+ * probe and create a new gspca device
+ *
+ * This function must be called by the sub-driver when it is
+ * called for probing a new device.
+ */
+int gspca_dev_probe(struct usb_interface *intf,
+               const struct usb_device_id *id,
+               const struct sd_desc *sd_desc,
+               int dev_size,
+               struct module *module)
+{
+       struct usb_interface_descriptor *interface;
+       struct gspca_dev *gspca_dev;
+       struct usb_device *dev = interface_to_usbdev(intf);
+       int ret;
+
+       PDEBUG(D_PROBE, "probing %04x:%04x", id->idVendor, id->idProduct);
+
+       /* we don't handle multi-config cameras */
+       if (dev->descriptor.bNumConfigurations != 1)
+               return -ENODEV;
+       interface = &intf->cur_altsetting->desc;
+       if (interface->bInterfaceNumber > 0)
+               return -ENODEV;
+
+       /* create the device */
+       if (dev_size < sizeof *gspca_dev)
+               dev_size = sizeof *gspca_dev;
+       gspca_dev = kzalloc(dev_size, GFP_KERNEL);
+       if (gspca_dev == NULL) {
+               err("couldn't kzalloc gspca struct");
+               return -EIO;
+       }
+       gspca_dev->dev = dev;
+       gspca_dev->iface = interface->bInterfaceNumber;
+       gspca_dev->nbalt = intf->num_altsetting;
+       gspca_dev->sd_desc = sd_desc;
+/*     gspca_dev->users = 0;                   (done by kzalloc) */
+       gspca_dev->nbufread = 2;
+
+       /* configure the subdriver */
+       ret = gspca_dev->sd_desc->config(gspca_dev, id);
+       if (ret < 0)
+               goto out;
+       ret = gspca_set_alt0(gspca_dev);
+       if (ret < 0)
+               goto out;
+       gspca_set_default_mode(gspca_dev);
+
+       mutex_init(&gspca_dev->usb_lock);
+       mutex_init(&gspca_dev->read_lock);
+       mutex_init(&gspca_dev->queue_lock);
+       init_waitqueue_head(&gspca_dev->wq);
+
+       /* init video stuff */
+       memcpy(&gspca_dev->vdev, &gspca_template, sizeof gspca_template);
+       gspca_dev->vdev.dev = &dev->dev;
+       memcpy(&gspca_dev->fops, &dev_fops, sizeof gspca_dev->fops);
+       gspca_dev->vdev.fops = &gspca_dev->fops;
+       gspca_dev->fops.owner = module;         /* module protection */
+       ret = video_register_device(&gspca_dev->vdev,
+                                 VFL_TYPE_GRABBER,
+                                 video_nr);
+       if (ret < 0) {
+               err("video_register_device err %d", ret);
+               goto out;
+       }
+
+       gspca_dev->present = 1;
+       usb_set_intfdata(intf, gspca_dev);
+       PDEBUG(D_PROBE, "probe ok");
+       return 0;
+out:
+       kfree(gspca_dev);
+       return ret;
+}
+EXPORT_SYMBOL(gspca_dev_probe);
+
+/*
+ * USB disconnection
+ *
+ * This function must be called by the sub-driver
+ * when the device disconnects, after the specific resources are freed.
+ */
+void gspca_disconnect(struct usb_interface *intf)
+{
+       struct gspca_dev *gspca_dev = usb_get_intfdata(intf);
+
+       if (!gspca_dev)
+               return;
+       gspca_dev->present = 0;
+       mutex_lock(&gspca_dev->queue_lock);
+       mutex_lock(&gspca_dev->usb_lock);
+       gspca_dev->streaming = 0;
+       destroy_urbs(gspca_dev);
+       mutex_unlock(&gspca_dev->usb_lock);
+       mutex_unlock(&gspca_dev->queue_lock);
+       while (gspca_dev->users != 0) {         /* wait until fully closed */
+               atomic_inc(&gspca_dev->nevent);
+               wake_up_interruptible(&gspca_dev->wq);  /* wake processes */
+               schedule();
+       }
+/* We don't want people trying to open up the device */
+       video_unregister_device(&gspca_dev->vdev);
+/* Free the memory */
+       kfree(gspca_dev);
+       PDEBUG(D_PROBE, "disconnect complete");
+}
+EXPORT_SYMBOL(gspca_disconnect);
+
+/* -- cam driver utility functions -- */
+
+/* auto gain and exposure algorithm based on the knee algorithm described here:
+   http://ytse.tricolour.net/docs/LowLightOptimization.html
+
+   Returns 0 if no changes were made, 1 if the gain and or exposure settings
+   where changed. */
+int gspca_auto_gain_n_exposure(struct gspca_dev *gspca_dev, int avg_lum,
+       int desired_avg_lum, int deadzone, int gain_knee, int exposure_knee)
+{
+       int i, steps, gain, orig_gain, exposure, orig_exposure, autogain;
+       const struct ctrl *gain_ctrl = NULL;
+       const struct ctrl *exposure_ctrl = NULL;
+       const struct ctrl *autogain_ctrl = NULL;
+       int retval = 0;
+
+       for (i = 0; i < gspca_dev->sd_desc->nctrls; i++) {
+               if (gspca_dev->sd_desc->ctrls[i].qctrl.id == V4L2_CID_GAIN)
+                       gain_ctrl = &gspca_dev->sd_desc->ctrls[i];
+               if (gspca_dev->sd_desc->ctrls[i].qctrl.id == V4L2_CID_EXPOSURE)
+                       exposure_ctrl = &gspca_dev->sd_desc->ctrls[i];
+               if (gspca_dev->sd_desc->ctrls[i].qctrl.id == V4L2_CID_AUTOGAIN)
+                       autogain_ctrl = &gspca_dev->sd_desc->ctrls[i];
+       }
+       if (!gain_ctrl || !exposure_ctrl || !autogain_ctrl) {
+               PDEBUG(D_ERR, "Error: gspca_auto_gain_n_exposure called "
+                       "on cam without (auto)gain/exposure");
+               return 0;
+       }
+
+       if (gain_ctrl->get(gspca_dev, &gain) ||
+                       exposure_ctrl->get(gspca_dev, &exposure) ||
+                       autogain_ctrl->get(gspca_dev, &autogain) || !autogain)
+               return 0;
+
+       orig_gain = gain;
+       orig_exposure = exposure;
+
+       /* If we are of a multiple of deadzone, do multiple steps to reach the
+          desired lumination fast (with the risc of a slight overshoot) */
+       steps = abs(desired_avg_lum - avg_lum) / deadzone;
+
+       PDEBUG(D_FRAM, "autogain: lum: %d, desired: %d, steps: %d\n",
+               avg_lum, desired_avg_lum, steps);
+
+       for (i = 0; i < steps; i++) {
+               if (avg_lum > desired_avg_lum) {
+                       if (gain > gain_knee)
+                               gain--;
+                       else if (exposure > exposure_knee)
+                               exposure--;
+                       else if (gain > gain_ctrl->qctrl.default_value)
+                               gain--;
+                       else if (exposure > exposure_ctrl->qctrl.minimum)
+                               exposure--;
+                       else if (gain > gain_ctrl->qctrl.minimum)
+                               gain--;
+                       else
+                               break;
+               } else {
+                       if (gain < gain_ctrl->qctrl.default_value)
+                               gain++;
+                       else if (exposure < exposure_knee)
+                               exposure++;
+                       else if (gain < gain_knee)
+                               gain++;
+                       else if (exposure < exposure_ctrl->qctrl.maximum)
+                               exposure++;
+                       else if (gain < gain_ctrl->qctrl.maximum)
+                               gain++;
+                       else
+                               break;
+               }
+       }
+
+       if (gain != orig_gain) {
+               gain_ctrl->set(gspca_dev, gain);
+               retval = 1;
+       }
+       if (exposure != orig_exposure) {
+               exposure_ctrl->set(gspca_dev, exposure);
+               retval = 1;
+       }
+
+       return retval;
+}
+EXPORT_SYMBOL(gspca_auto_gain_n_exposure);
+
+/* -- module insert / remove -- */
+static int __init gspca_init(void)
+{
+       info("main v%s registered", version);
+       return 0;
+}
+static void __exit gspca_exit(void)
+{
+       info("main deregistered");
+}
+
+module_init(gspca_init);
+module_exit(gspca_exit);
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+module_param_named(debug, gspca_debug, int, 0644);
+MODULE_PARM_DESC(debug,
+               "Debug (bit) 0x01:error 0x02:probe 0x04:config"
+               " 0x08:stream 0x10:frame 0x20:packet 0x40:USBin 0x80:USBout"
+               " 0x0100: v4l2");
+#endif
diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h
new file mode 100644 (file)
index 0000000..3fd2c4e
--- /dev/null
@@ -0,0 +1,176 @@
+#ifndef GSPCAV2_H
+#define GSPCAV2_H
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/usb.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <linux/mutex.h>
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+/* GSPCA our debug messages */
+extern int gspca_debug;
+#define PDEBUG(level, fmt, args...) \
+       do {\
+               if (gspca_debug & (level)) \
+                       printk(KERN_INFO MODULE_NAME ": " fmt "\n", ## args); \
+       } while (0)
+#define D_ERR  0x01
+#define D_PROBE 0x02
+#define D_CONF 0x04
+#define D_STREAM 0x08
+#define D_FRAM 0x10
+#define D_PACK 0x20
+#define D_USBI 0x40
+#define D_USBO 0x80
+#define D_V4L2 0x0100
+#else
+#define PDEBUG(level, fmt, args...)
+#endif
+#undef err
+#define err(fmt, args...) \
+       do {\
+               printk(KERN_ERR MODULE_NAME ": " fmt "\n", ## args); \
+       } while (0)
+#undef info
+#define info(fmt, args...) \
+       do {\
+               printk(KERN_INFO MODULE_NAME ": " fmt "\n", ## args); \
+       } while (0)
+#undef warn
+#define warn(fmt, args...) \
+       do {\
+               printk(KERN_WARNING MODULE_NAME ": " fmt "\n", ## args); \
+       } while (0)
+
+#define GSPCA_MAX_FRAMES 16    /* maximum number of video frame buffers */
+/* ISOC transfers */
+#define MAX_NURBS 16           /* max number of URBs */
+#define ISO_MAX_PKT 32         /* max number of packets in an ISOC transfer */
+#define ISO_MAX_SIZE 0x8000    /* max size of one URB buffer (32 Kb) */
+
+/* device information - set at probe time */
+struct cam {
+       char *dev_name;
+       struct v4l2_pix_format *cam_mode;       /* size nmodes */
+       char nmodes;
+       __u8 epaddr;
+};
+
+struct gspca_dev;
+struct gspca_frame;
+
+/* subdriver operations */
+typedef int (*cam_op) (struct gspca_dev *);
+typedef void (*cam_v_op) (struct gspca_dev *);
+typedef int (*cam_cf_op) (struct gspca_dev *, const struct usb_device_id *);
+typedef int (*cam_jpg_op) (struct gspca_dev *,
+                               struct v4l2_jpegcompression *);
+typedef int (*cam_qmnu_op) (struct gspca_dev *,
+                       struct v4l2_querymenu *);
+typedef void (*cam_pkt_op) (struct gspca_dev *gspca_dev,
+                               struct gspca_frame *frame,
+                               __u8 *data,
+                               int len);
+
+struct ctrl {
+       struct v4l2_queryctrl qctrl;
+       int (*set)(struct gspca_dev *, __s32);
+       int (*get)(struct gspca_dev *, __s32 *);
+};
+
+/* subdriver description */
+struct sd_desc {
+/* information */
+       const char *name;       /* sub-driver name */
+/* controls */
+       const struct ctrl *ctrls;
+       int nctrls;
+/* operations */
+       cam_cf_op config;       /* called on probe */
+       cam_op open;            /* called on open */
+       cam_v_op start;         /* called on stream on */
+       cam_v_op stopN;         /* called on stream off - main alt */
+       cam_v_op stop0;         /* called on stream off - alt 0 */
+       cam_v_op close;         /* called on close */
+       cam_pkt_op pkt_scan;
+/* optional operations */
+       cam_v_op dq_callback;   /* called when a frame has been dequeued */
+       cam_jpg_op get_jcomp;
+       cam_jpg_op set_jcomp;
+       cam_qmnu_op querymenu;
+};
+
+/* packet types when moving from iso buf to frame buf */
+#define DISCARD_PACKET 0
+#define FIRST_PACKET   1
+#define INTER_PACKET   2
+#define LAST_PACKET    3
+
+struct gspca_frame {
+       __u8 *data;                     /* frame buffer */
+       __u8 *data_end;                 /* end of frame while filling */
+       int vma_use_count;
+       struct v4l2_buffer v4l2_buf;
+};
+
+struct gspca_dev {
+       struct video_device vdev;       /* !! must be the first item */
+       struct file_operations fops;
+       struct usb_device *dev;
+       struct file *capt_file;         /* file doing video capture */
+
+       struct cam cam;                         /* device information */
+       const struct sd_desc *sd_desc;          /* subdriver description */
+
+       __u8 usb_buf[8];                        /* buffer for USB exchanges */
+       struct urb *urb[MAX_NURBS];
+
+       __u8 *frbuf;                            /* buffer for nframes */
+       struct gspca_frame frame[GSPCA_MAX_FRAMES];
+       __u32 frsz;                             /* frame size */
+       char nframes;                           /* number of frames */
+       char fr_i;                              /* frame being filled */
+       char fr_q;                              /* next frame to queue */
+       char fr_o;                              /* next frame to dequeue */
+       signed char fr_queue[GSPCA_MAX_FRAMES]; /* frame queue */
+       char last_packet_type;
+
+       __u8 iface;                     /* USB interface number */
+       __u8 alt;                       /* USB alternate setting */
+       __u8 curr_mode;                 /* current camera mode */
+       __u32 pixfmt;                   /* current mode parameters */
+       __u16 width;
+       __u16 height;
+
+       atomic_t nevent;                /* number of frames done */
+       wait_queue_head_t wq;           /* wait queue */
+       struct mutex usb_lock;          /* usb exchange protection */
+       struct mutex read_lock;         /* read protection */
+       struct mutex queue_lock;        /* ISOC queue protection */
+       __u32 sequence;                 /* frame sequence number */
+       char streaming;
+       char users;                     /* number of opens */
+       char present;                   /* device connected */
+       char nbufread;                  /* number of buffers for read() */
+       char nurbs;                     /* number of allocated URBs */
+       char memory;                    /* memory type (V4L2_MEMORY_xxx) */
+       __u8 nbalt;                     /* number of USB alternate settings */
+};
+
+int gspca_dev_probe(struct usb_interface *intf,
+               const struct usb_device_id *id,
+               const struct sd_desc *sd_desc,
+               int dev_size,
+               struct module *module);
+void gspca_disconnect(struct usb_interface *intf);
+struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev,
+                                   int packet_type,
+                                   struct gspca_frame *frame,
+                                   const __u8 *data,
+                                   int len);
+int gspca_auto_gain_n_exposure(struct gspca_dev *gspca_dev, int avg_lum,
+       int desired_avg_lum, int deadzone, int gain_knee, int exposure_knee);
+#endif /* GSPCAV2_H */
diff --git a/drivers/media/video/gspca/jpeg.h b/drivers/media/video/gspca/jpeg.h
new file mode 100644 (file)
index 0000000..d823b47
--- /dev/null
@@ -0,0 +1,301 @@
+#ifndef JPEG_H
+#define JPEG_H 1
+/*
+ * Insert a JPEG header at start of frame
+ *
+ * This module is used by the gspca subdrivers.
+ * A special case is done for Conexant webcams.
+ *
+ * Copyright (C) Jean-Francois Moine (http://moinejf.free.fr)
+ *
+ * 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
+ * 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
+ *
+ */
+
+/* start of jpeg frame + quantization table */
+static const unsigned char quant[][0x88] = {
+/* index 0 - Q40*/
+    {
+       0xff, 0xd8,                     /* jpeg */
+       0xff, 0xdb, 0x00, 0x84,         /* DQT */
+0,                                     /* quantization table part 1 */
+     20, 14, 15, 18, 15, 13, 20, 18, 16, 18, 23, 21, 20, 24, 30, 50,
+     33, 30, 28, 28, 30, 61, 44, 46, 36, 50, 73, 64, 76, 75, 71, 64,
+     70, 69, 80, 90, 115, 98, 80, 85, 109, 86, 69, 70, 100, 136, 101,
+     109,
+     119, 123, 129, 130, 129, 78, 96, 141, 151, 140, 125, 150, 115,
+     126, 129, 124,
+1,                                     /* quantization table part 2 */
+     21, 23, 23, 30, 26, 30, 59, 33, 33, 59, 124, 83, 70, 83, 124, 124,
+     124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124,
+     124, 124, 124,
+     124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124,
+     124, 124, 124,
+     124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124,
+     124, 124, 124},
+/* index 1 - Q50 */
+    {
+       0xff, 0xd8,
+       0xff, 0xdb, 0x00, 0x84,         /* DQT */
+0,
+     16, 11, 12, 14, 12, 10, 16, 14, 13, 14, 18, 17, 16, 19, 24, 40,
+     26, 24, 22, 22, 24, 49, 35, 37, 29, 40, 58, 51, 61, 60, 57, 51,
+     56, 55, 64, 72, 92, 78, 64, 68, 87, 69, 55, 56, 80, 109, 81, 87,
+     95, 98, 103, 104, 103, 62, 77, 113, 121, 112, 100, 120, 92, 101,
+     103, 99,
+1,
+    17, 18, 18, 24, 21, 24, 47, 26, 26, 47, 99, 66, 56, 66, 99, 99,
+     99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
+     99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
+     99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99},
+/* index 2 Q60 */
+    {
+       0xff, 0xd8,
+       0xff, 0xdb, 0x00, 0x84,         /* DQT */
+0,
+     13, 9, 10, 11, 10, 8, 13, 11, 10, 11, 14, 14, 13, 15, 19, 32,
+     21, 19, 18, 18, 19, 39, 28, 30, 23, 32, 46, 41, 49, 48, 46, 41,
+     45, 44, 51, 58, 74, 62, 51, 54, 70, 55, 44, 45, 64, 87, 65, 70,
+     76, 78, 82, 83, 82, 50, 62, 90, 97, 90, 80, 96, 74, 81, 82, 79,
+1,
+     14, 14, 14, 19, 17, 19, 38, 21, 21, 38, 79, 53, 45, 53, 79, 79,
+     79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79,
+     79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79,
+     79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79},
+/* index 3 - Q70 */
+    {
+       0xff, 0xd8,
+       0xff, 0xdb, 0x00, 0x84,         /* DQT */
+0,
+     10, 7, 7, 8, 7, 6, 10, 8, 8, 8, 11, 10, 10, 11, 14, 24,
+     16, 14, 13, 13, 14, 29, 21, 22, 17, 24, 35, 31, 37, 36, 34, 31,
+     34, 33, 38, 43, 55, 47, 38, 41, 52, 41, 33, 34, 48, 65, 49, 52,
+     57, 59, 62, 62, 62, 37, 46, 68, 73, 67, 60, 72, 55, 61, 62, 59,
+1,
+     10, 11, 11, 14, 13, 14, 28, 16, 16, 28, 59, 40, 34, 40, 59, 59,
+     59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+     59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+     59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59},
+/* index 4 - Q80 */
+    {
+       0xff, 0xd8,
+       0xff, 0xdb, 0x00, 0x84,         /* DQT */
+0,
+      6, 4, 5, 6, 5, 4, 6, 6, 5, 6, 7, 7, 6, 8, 10, 16,
+     10, 10, 9, 9, 10, 20, 14, 15, 12, 16, 23, 20, 24, 24, 23, 20,
+     22, 22, 26, 29, 37, 31, 26, 27, 35, 28, 22, 22, 32, 44, 32, 35,
+     38, 39, 41, 42, 41, 25, 31, 45, 48, 45, 40, 48, 37, 40, 41, 40,
+1,
+      7, 7, 7, 10, 8, 10, 19, 10, 10, 19, 40, 26, 22, 26, 40, 40,
+     40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40,
+     40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40,
+     40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40},
+/* index 5 - Q85 */
+    {
+       0xff, 0xd8,
+       0xff, 0xdb, 0x00, 0x84,         /* DQT */
+0,
+     5, 3, 4, 4, 4, 3, 5, 4, 4, 4, 5, 5, 5, 6, 7, 12,
+     8, 7, 7, 7, 7, 15, 11, 11, 9, 12, 17, 15, 18, 18, 17, 15,
+     17, 17, 19, 22, 28, 23, 19, 20, 26, 21, 17, 17, 24, 33, 24, 26,
+     29, 29, 31, 31, 31, 19, 23, 34, 36, 34, 30, 36, 28, 30, 31, 30,
+1,
+     5, 5, 5, 7, 6, 7, 14, 8, 8, 14, 30, 20, 17, 20, 30, 30,
+     30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
+     30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
+     30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30},
+/* index 6 - 86 */
+{
+       0xff, 0xd8,
+       0xff, 0xdb, 0x00, 0x84,         /* DQT */
+0,
+       0x04, 0x03, 0x03, 0x04, 0x03, 0x03, 0x04, 0x04,
+       0x04, 0x04, 0x05, 0x05, 0x04, 0x05, 0x07, 0x0B,
+       0x07, 0x07, 0x06, 0x06, 0x07, 0x0E, 0x0A, 0x0A,
+       0x08, 0x0B, 0x10, 0x0E, 0x11, 0x11, 0x10, 0x0E,
+       0x10, 0x0F, 0x12, 0x14, 0x1A, 0x16, 0x12, 0x13,
+       0x18, 0x13, 0x0F, 0x10, 0x16, 0x1F, 0x17, 0x18,
+       0x1B, 0x1B, 0x1D, 0x1D, 0x1D, 0x11, 0x16, 0x20,
+       0x22, 0x1F, 0x1C, 0x22, 0x1A, 0x1C, 0x1D, 0x1C,
+1,
+       0x05, 0x05, 0x05, 0x07, 0x06, 0x07, 0x0D, 0x07,
+       0x07, 0x0D, 0x1C, 0x12, 0x10, 0x12, 0x1C, 0x1C,
+       0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C,
+       0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C,
+       0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C,
+       0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C,
+       0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C,
+       0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C,
+ },
+/* index 7 - 88 */
+{
+       0xff, 0xd8,
+       0xff, 0xdb, 0x00, 0x84,         /* DQT */
+0,
+       0x04, 0x03, 0x03, 0x03, 0x03, 0x02, 0x04, 0x03,
+       0x03, 0x03, 0x04, 0x04, 0x04, 0x05, 0x06, 0x0A,
+       0x06, 0x06, 0x05, 0x05, 0x06, 0x0C, 0x08, 0x09,
+       0x07, 0x0A, 0x0E, 0x0C, 0x0F, 0x0E, 0x0E, 0x0C,
+       0x0D, 0x0D, 0x0F, 0x11, 0x16, 0x13, 0x0F, 0x10,
+       0x15, 0x11, 0x0D, 0x0D, 0x13, 0x1A, 0x13, 0x15,
+       0x17, 0x18, 0x19, 0x19, 0x19, 0x0F, 0x12, 0x1B,
+       0x1D, 0x1B, 0x18, 0x1D, 0x16, 0x18, 0x19, 0x18,
+1,
+       0x04, 0x04, 0x04, 0x06, 0x05, 0x06, 0x0B, 0x06,
+       0x06, 0x0B, 0x18, 0x10, 0x0D, 0x10, 0x18, 0x18,
+       0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+       0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+       0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+       0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+       0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+       0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+},
+/* index 8 - ?? */
+{
+       0xff, 0xd8,
+       0xff, 0xdb, 0x00, 0x84,         /* DQT */
+0,
+       0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+       0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x05,
+       0x03, 0x03, 0x03, 0x03, 0x03, 0x06, 0x04, 0x05,
+       0x04, 0x05, 0x07, 0x06, 0x08, 0x08, 0x07, 0x06,
+       0x07, 0x07, 0x08, 0x09, 0x0C, 0x0A, 0x08, 0x09,
+       0x0B, 0x09, 0x07, 0x07, 0x0A, 0x0E, 0x0A, 0x0B,
+       0x0C, 0x0C, 0x0D, 0x0D, 0x0D, 0x08, 0x0A, 0x0E,
+       0x0F, 0x0E, 0x0D, 0x0F, 0x0C, 0x0D, 0x0D, 0x0C,
+1,
+       0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x06, 0x03,
+       0x03, 0x06, 0x0C, 0x08, 0x07, 0x08, 0x0C, 0x0C,
+       0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
+       0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
+       0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
+       0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
+       0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
+       0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C
+}
+};
+
+/* huffman table + start of SOF0 */
+static unsigned char huffman[] = {
+       0xff, 0xc4, 0x01, 0xa2,
+       0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01,
+       0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+       0x07, 0x08, 0x09, 0x0a, 0x0b, 0x01, 0x00, 0x03,
+       0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+       0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+       0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
+       0x0a, 0x0b, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03,
+       0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00,
+       0x00, 0x01, 0x7d, 0x01, 0x02, 0x03, 0x00, 0x04,
+       0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13,
+       0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81,
+       0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15,
+       0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72, 0x82,
+       0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25,
+       0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36,
+       0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46,
+       0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56,
+       0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66,
+       0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76,
+       0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86,
+       0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95,
+       0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4,
+       0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3,
+       0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2,
+       0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca,
+       0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9,
+       0xda, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
+       0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5,
+       0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0x11, 0x00, 0x02,
+       0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05,
+       0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01,
+       0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06,
+       0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22,
+       0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1,
+       0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62,
+       0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25,
+       0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, 0x27, 0x28,
+       0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a,
+       0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a,
+       0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a,
+       0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a,
+       0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a,
+       0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
+       0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
+       0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
+       0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
+       0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
+       0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
+       0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2, 0xe3,
+       0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2,
+       0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa,
+#ifdef CONEX_CAM
+/* the Conexant frames start with SOF0 */
+#else
+       0xff, 0xc0, 0x00, 0x11,         /* SOF0 (start of frame 0 */
+       0x08,                           /* data precision */
+#endif
+};
+
+#ifndef CONEX_CAM
+/* variable part:
+ *     0x01, 0xe0,                      height
+ *     0x02, 0x80,                      width
+ *     0x03,                            component number
+ *             0x01,
+ *                     0x21,                   samples Y
+ */
+
+/* end of header */
+static unsigned char eoh[] = {
+                       0x00,           /* quant Y */
+               0x02, 0x11, 0x01,       /* samples CbCr - quant CbCr */
+               0x03, 0x11, 0x01,
+
+       0xff, 0xda, 0x00, 0x0c,         /* SOS (start of scan) */
+       0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00
+};
+#endif
+
+/* -- output the JPEG header -- */
+static void jpeg_put_header(struct gspca_dev *gspca_dev,
+                           struct gspca_frame *frame,
+                           int qindex,
+                           int samplesY)
+{
+#ifndef CONEX_CAM
+       unsigned char tmpbuf[8];
+#endif
+
+       gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
+                       (unsigned char *) quant[qindex], sizeof quant[0]);
+       gspca_frame_add(gspca_dev, INTER_PACKET, frame,
+                       (unsigned char *) huffman, sizeof huffman);
+#ifndef CONEX_CAM
+       tmpbuf[0] = gspca_dev->height >> 8;
+       tmpbuf[1] = gspca_dev->height & 0xff;
+       tmpbuf[2] = gspca_dev->width >> 8;
+       tmpbuf[3] = gspca_dev->width & 0xff;
+       tmpbuf[4] = 0x03;               /* component number */
+       tmpbuf[5] = 0x01;               /* first component */
+       tmpbuf[6] = samplesY;
+       gspca_frame_add(gspca_dev, INTER_PACKET, frame,
+                       tmpbuf, 7);
+       gspca_frame_add(gspca_dev, INTER_PACKET, frame,
+                       eoh, sizeof eoh);
+#endif
+}
+#endif
diff --git a/drivers/media/video/gspca/mars.c b/drivers/media/video/gspca/mars.c
new file mode 100644 (file)
index 0000000..88c2b02
--- /dev/null
@@ -0,0 +1,464 @@
+/*
+ *             Mars-Semi MR97311A library
+ *             Copyright (C) 2005 <bradlch@hotmail.com>
+ *
+ * V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
+ *
+ * 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
+ * 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
+ */
+
+#define MODULE_NAME "mars"
+
+#include "gspca.h"
+#include "jpeg.h"
+
+#define DRIVER_VERSION_NUMBER  KERNEL_VERSION(2, 1, 7)
+static const char version[] = "2.1.7";
+
+MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
+MODULE_DESCRIPTION("GSPCA/Mars USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* specific webcam descriptor */
+struct sd {
+       struct gspca_dev gspca_dev;     /* !! must be the first item */
+
+       char qindex;
+};
+
+/* V4L2 controls supported by the driver */
+static struct ctrl sd_ctrls[] = {
+};
+
+static struct v4l2_pix_format vga_mode[] = {
+       {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+               .bytesperline = 320,
+               .sizeimage = 320 * 240 * 3 / 8 + 589,
+               .colorspace = V4L2_COLORSPACE_JPEG,
+               .priv = 2},
+       {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+               .bytesperline = 640,
+               .sizeimage = 640 * 480 * 3 / 8 + 590,
+               .colorspace = V4L2_COLORSPACE_JPEG,
+               .priv = 1},
+};
+
+/* MI Register table //elvis */
+enum {
+       REG_HW_MI_0,
+       REG_HW_MI_1,
+       REG_HW_MI_2,
+       REG_HW_MI_3,
+       REG_HW_MI_4,
+       REG_HW_MI_5,
+       REG_HW_MI_6,
+       REG_HW_MI_7,
+       REG_HW_MI_9 = 0x09,
+       REG_HW_MI_B = 0x0B,
+       REG_HW_MI_C,
+       REG_HW_MI_D,
+       REG_HW_MI_1E = 0x1E,
+       REG_HW_MI_20 = 0x20,
+       REG_HW_MI_2B = 0x2B,
+       REG_HW_MI_2C,
+       REG_HW_MI_2D,
+       REG_HW_MI_2E,
+       REG_HW_MI_35 = 0x35,
+       REG_HW_MI_5F = 0x5f,
+       REG_HW_MI_60,
+       REG_HW_MI_61,
+       REG_HW_MI_62,
+       REG_HW_MI_63,
+       REG_HW_MI_64,
+       REG_HW_MI_F1 = 0xf1,
+       ATTR_TOTAL_MI_REG = 0xf2
+};
+
+/* the bytes to write are in gspca_dev->usb_buf */
+static int reg_w(struct gspca_dev *gspca_dev,
+                __u16 index, int len)
+{
+       int rc;
+
+       rc = usb_control_msg(gspca_dev->dev,
+                        usb_sndbulkpipe(gspca_dev->dev, 4),
+                        0x12,
+                        0xc8,          /* ?? */
+                        0,             /* value */
+                        index, gspca_dev->usb_buf, len, 500);
+       if (rc < 0)
+               PDEBUG(D_ERR, "reg write [%02x] error %d", index, rc);
+       return rc;
+}
+
+static int reg_w_buf(struct gspca_dev *gspca_dev,
+                       __u16 index, __u8 *buf, int len)
+{
+       int rc;
+
+       rc = usb_control_msg(gspca_dev->dev,
+                        usb_sndbulkpipe(gspca_dev->dev, 4),
+                        0x12,
+                        0xc8,          /* ?? */
+                        0,             /* value */
+                        index, buf, len, 500);
+       if (rc < 0)
+               PDEBUG(D_ERR, "reg write [%02x] error %d", index, rc);
+       return rc;
+}
+
+static void bulk_w(struct gspca_dev *gspca_dev,
+                  __u16 *pch,
+                  __u16 Address)
+{
+       gspca_dev->usb_buf[0] = 0x1f;
+       gspca_dev->usb_buf[1] = 0;                      /* control byte */
+       gspca_dev->usb_buf[2] = Address;
+       gspca_dev->usb_buf[3] = *pch >> 8;              /* high byte */
+       gspca_dev->usb_buf[4] = *pch;                   /* low byte */
+
+       reg_w(gspca_dev, Address, 5);
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+                       const struct usb_device_id *id)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       struct cam *cam;
+
+       cam = &gspca_dev->cam;
+       cam->dev_name = (char *) id->driver_info;
+       cam->epaddr = 0x01;
+       cam->cam_mode = vga_mode;
+       cam->nmodes = sizeof vga_mode / sizeof vga_mode[0];
+       sd->qindex = 1;                 /* set the quantization table */
+       return 0;
+}
+
+/* this function is called at open time */
+static int sd_open(struct gspca_dev *gspca_dev)
+{
+       return 0;
+}
+
+static void sd_start(struct gspca_dev *gspca_dev)
+{
+       int err_code;
+       __u8 *data;
+       __u16 *MI_buf;
+       int h_size, v_size;
+       int intpipe;
+
+       PDEBUG(D_STREAM, "camera start, iface %d, alt 8", gspca_dev->iface);
+       if (usb_set_interface(gspca_dev->dev, gspca_dev->iface, 8) < 0) {
+               PDEBUG(D_ERR|D_STREAM, "Set packet size: set interface error");
+               return;
+       }
+
+       data = gspca_dev->usb_buf;
+       data[0] = 0x01;         /* address */
+       data[1] = 0x01;
+
+       err_code = reg_w(gspca_dev, data[0], 2);
+       if (err_code < 0)
+               return;
+
+       /*
+          Initialize the MR97113 chip register
+        */
+       data = kmalloc(16, GFP_KERNEL);
+       data[0] = 0x00;         /* address */
+       data[1] = 0x0c | 0x01;  /* reg 0 */
+       data[2] = 0x01;         /* reg 1 */
+       h_size = gspca_dev->width;
+       v_size = gspca_dev->height;
+       data[3] = h_size / 8;   /* h_size , reg 2 */
+       data[4] = v_size / 8;   /* v_size , reg 3 */
+       data[5] = 0x30;         /* reg 4, MI, PAS5101 :
+                                *      0x30 for 24mhz , 0x28 for 12mhz */
+       data[6] = 4;            /* reg 5, H start */
+       data[7] = 0xc0;         /* reg 6, gamma 1.5 */
+       data[8] = 3;            /* reg 7, V start */
+/*     if (h_size == 320 ) */
+/*             data[9]= 0x56;   * reg 8, 24MHz, 2:1 scale down */
+/*     else */
+       data[9] = 0x52;         /* reg 8, 24MHz, no scale down */
+       data[10] = 0x5d;        /* reg 9, I2C device address
+                                *      [for PAS5101 (0x40)] [for MI (0x5d)] */
+
+       err_code = reg_w_buf(gspca_dev, data[0], data, 11);
+       kfree(data);
+       if (err_code < 0)
+               return;
+
+       data = gspca_dev->usb_buf;
+       data[0] = 0x23;         /* address */
+       data[1] = 0x09;         /* reg 35, append frame header */
+
+       err_code = reg_w(gspca_dev, data[0], 2);
+       if (err_code < 0)
+               return;
+
+       data[0] = 0x3c;         /* address */
+/*     if (gspca_dev->width == 1280) */
+/*             data[1] = 200;   * reg 60, pc-cam frame size
+                                *      (unit: 4KB) 800KB */
+/*     else */
+       data[1] = 50;           /* 50 reg 60, pc-cam frame size
+                                *      (unit: 4KB) 200KB */
+       err_code = reg_w(gspca_dev, data[0], 2);
+       if (err_code < 0)
+               return;
+
+       if (0) {                        /* fixed dark-gain */
+               data[1] = 0;            /* reg 94, Y Gain (1.75) */
+               data[2] = 0;            /* reg 95, UV Gain (1.75) */
+               data[3] = 0x3f;         /* reg 96, Y Gain/UV Gain/disable
+                                        *      auto dark-gain */
+               data[4] = 0;            /* reg 97, set fixed dark level */
+               data[5] = 0;            /* reg 98, don't care */
+       } else {                        /* auto dark-gain */
+               data[1] = 0;            /* reg 94, Y Gain (auto) */
+               data[2] = 0;            /* reg 95, UV Gain (1.75) */
+               data[3] = 0x78;         /* reg 96, Y Gain/UV Gain/disable
+                                        *      auto dark-gain */
+               switch (gspca_dev->width) {
+/*             case 1280: */
+/*                     data[4] = 154;
+                                * reg 97, %3 shadow point (unit: 256 pixel) */
+/*                     data[5] = 51;
+                                * reg 98, %1 highlight point
+                                *      (uint: 256 pixel) */
+/*                     break; */
+               default:
+/*             case 640: */
+                       data[4] = 36;   /* reg 97, %3 shadow point
+                                        *      (unit: 256 pixel) */
+                       data[5] = 12;   /* reg 98, %1 highlight point
+                                        *      (uint: 256 pixel) */
+                       break;
+               case 320:
+                       data[4] = 9;    /* reg 97, %3 shadow point
+                                        *      (unit: 256 pixel) */
+                       data[5] = 3;    /* reg 98, %1 highlight point
+                                        *      (uint: 256 pixel) */
+                       break;
+               }
+       }
+       /* auto dark-gain */
+       data[0] = 0x5e;         /* address */
+
+       err_code = reg_w(gspca_dev, data[0], 6);
+       if (err_code < 0)
+               return;
+
+       data[0] = 0x67;
+       data[1] = 0x13;         /* reg 103, first pixel B, disable sharpness */
+       err_code = reg_w(gspca_dev, data[0], 2);
+       if (err_code < 0)
+               return;
+
+       /*
+        * initialize the value of MI sensor...
+        */
+       MI_buf = kzalloc(ATTR_TOTAL_MI_REG * sizeof *MI_buf, GFP_KERNEL);
+       MI_buf[REG_HW_MI_1] = 0x000a;
+       MI_buf[REG_HW_MI_2] = 0x000c;
+       MI_buf[REG_HW_MI_3] = 0x0405;
+       MI_buf[REG_HW_MI_4] = 0x0507;
+       /* mi_Attr_Reg_[REG_HW_MI_5]     = 0x01ff;//13 */
+       MI_buf[REG_HW_MI_5] = 0x0013;   /* 13 */
+       MI_buf[REG_HW_MI_6] = 0x001f;   /* vertical blanking */
+       /* mi_Attr_Reg_[REG_HW_MI_6]     = 0x0400;  // vertical blanking */
+       MI_buf[REG_HW_MI_7] = 0x0002;
+       /* mi_Attr_Reg_[REG_HW_MI_9]     = 0x015f; */
+       /* mi_Attr_Reg_[REG_HW_MI_9]     = 0x030f; */
+       MI_buf[REG_HW_MI_9] = 0x0374;
+       MI_buf[REG_HW_MI_B] = 0x0000;
+       MI_buf[REG_HW_MI_C] = 0x0000;
+       MI_buf[REG_HW_MI_D] = 0x0000;
+       MI_buf[REG_HW_MI_1E] = 0x8000;
+/* mi_Attr_Reg_[REG_HW_MI_20]    = 0x1104; */
+       MI_buf[REG_HW_MI_20] = 0x1104;  /* 0x111c; */
+       MI_buf[REG_HW_MI_2B] = 0x0008;
+/* mi_Attr_Reg_[REG_HW_MI_2C]    = 0x000f; */
+       MI_buf[REG_HW_MI_2C] = 0x001f;  /* lita suggest */
+       MI_buf[REG_HW_MI_2D] = 0x0008;
+       MI_buf[REG_HW_MI_2E] = 0x0008;
+       MI_buf[REG_HW_MI_35] = 0x0051;
+       MI_buf[REG_HW_MI_5F] = 0x0904;  /* fail to write */
+       MI_buf[REG_HW_MI_60] = 0x0000;
+       MI_buf[REG_HW_MI_61] = 0x0000;
+       MI_buf[REG_HW_MI_62] = 0x0498;
+       MI_buf[REG_HW_MI_63] = 0x0000;
+       MI_buf[REG_HW_MI_64] = 0x0000;
+       MI_buf[REG_HW_MI_F1] = 0x0001;
+       /* changing while setting up the different value of dx/dy */
+
+       if (gspca_dev->width != 1280) {
+               MI_buf[0x01] = 0x010a;
+               MI_buf[0x02] = 0x014c;
+               MI_buf[0x03] = 0x01e5;
+               MI_buf[0x04] = 0x0287;
+       }
+       MI_buf[0x20] = 0x1104;
+
+       bulk_w(gspca_dev, MI_buf + 1, 1);
+       bulk_w(gspca_dev, MI_buf + 2, 2);
+       bulk_w(gspca_dev, MI_buf + 3, 3);
+       bulk_w(gspca_dev, MI_buf + 4, 4);
+       bulk_w(gspca_dev, MI_buf + 5, 5);
+       bulk_w(gspca_dev, MI_buf + 6, 6);
+       bulk_w(gspca_dev, MI_buf + 7, 7);
+       bulk_w(gspca_dev, MI_buf + 9, 9);
+       bulk_w(gspca_dev, MI_buf + 0x0b, 0x0b);
+       bulk_w(gspca_dev, MI_buf + 0x0c, 0x0c);
+       bulk_w(gspca_dev, MI_buf + 0x0d, 0x0d);
+       bulk_w(gspca_dev, MI_buf + 0x1e, 0x1e);
+       bulk_w(gspca_dev, MI_buf + 0x20, 0x20);
+       bulk_w(gspca_dev, MI_buf + 0x2b, 0x2b);
+       bulk_w(gspca_dev, MI_buf + 0x2c, 0x2c);
+       bulk_w(gspca_dev, MI_buf + 0x2d, 0x2d);
+       bulk_w(gspca_dev, MI_buf + 0x2e, 0x2e);
+       bulk_w(gspca_dev, MI_buf + 0x35, 0x35);
+       bulk_w(gspca_dev, MI_buf + 0x5f, 0x5f);
+       bulk_w(gspca_dev, MI_buf + 0x60, 0x60);
+       bulk_w(gspca_dev, MI_buf + 0x61, 0x61);
+       bulk_w(gspca_dev, MI_buf + 0x62, 0x62);
+       bulk_w(gspca_dev, MI_buf + 0x63, 0x63);
+       bulk_w(gspca_dev, MI_buf + 0x64, 0x64);
+       bulk_w(gspca_dev, MI_buf + 0xf1, 0xf1);
+       kfree(MI_buf);
+
+       intpipe = usb_sndintpipe(gspca_dev->dev, 0);
+       err_code = usb_clear_halt(gspca_dev->dev, intpipe);
+
+       data[0] = 0x00;
+       data[1] = 0x4d;         /* ISOC transfering enable... */
+       reg_w(gspca_dev, data[0], 2);
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+       int result;
+
+       gspca_dev->usb_buf[0] = 1;
+       gspca_dev->usb_buf[1] = 0;
+       result = reg_w(gspca_dev, gspca_dev->usb_buf[0], 2);
+       if (result < 0)
+               PDEBUG(D_ERR, "Camera Stop failed");
+}
+
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+}
+
+static void sd_close(struct gspca_dev *gspca_dev)
+{
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+                       struct gspca_frame *frame,      /* target */
+                       __u8 *data,                     /* isoc packet */
+                       int len)                        /* iso packet length */
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       int p;
+
+       if (len < 6) {
+/*             gspca_dev->last_packet_type = DISCARD_PACKET; */
+               return;
+       }
+       for (p = 0; p < len - 6; p++) {
+               if (data[0 + p] == 0xff
+                   && data[1 + p] == 0xff
+                   && data[2 + p] == 0x00
+                   && data[3 + p] == 0xff
+                   && data[4 + p] == 0x96) {
+                       if (data[5 + p] == 0x64
+                           || data[5 + p] == 0x65
+                           || data[5 + p] == 0x66
+                           || data[5 + p] == 0x67) {
+                               PDEBUG(D_PACK, "sof offset: %d leng: %d",
+                                       p, len);
+                               frame = gspca_frame_add(gspca_dev, LAST_PACKET,
+                                                       frame, data, 0);
+
+                               /* put the JPEG header */
+                               jpeg_put_header(gspca_dev, frame,
+                                               sd->qindex, 0x21);
+                               data += 16;
+                               len -= 16;
+                               break;
+                       }
+               }
+       }
+       gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+       .name = MODULE_NAME,
+       .ctrls = sd_ctrls,
+       .nctrls = ARRAY_SIZE(sd_ctrls),
+       .config = sd_config,
+       .open = sd_open,
+       .start = sd_start,
+       .stopN = sd_stopN,
+       .stop0 = sd_stop0,
+       .close = sd_close,
+       .pkt_scan = sd_pkt_scan,
+};
+
+/* -- module initialisation -- */
+#define DVNM(name) .driver_info = (kernel_ulong_t) name
+static const __devinitdata struct usb_device_id device_table[] = {
+       {USB_DEVICE(0x093a, 0x050f), DVNM("Mars-Semi Pc-Camera")},
+       {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+                       const struct usb_device_id *id)
+{
+       return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+                               THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+       .name = MODULE_NAME,
+       .id_table = device_table,
+       .probe = sd_probe,
+       .disconnect = gspca_disconnect,
+};
+
+/* -- module insert / remove -- */
+static int __init sd_mod_init(void)
+{
+       if (usb_register(&sd_driver) < 0)
+               return -1;
+       PDEBUG(D_PROBE, "v%s registered", version);
+       return 0;
+}
+static void __exit sd_mod_exit(void)
+{
+       usb_deregister(&sd_driver);
+       PDEBUG(D_PROBE, "deregistered");
+}
+
+module_init(sd_mod_init);
+module_exit(sd_mod_exit);
diff --git a/drivers/media/video/gspca/ov519.c b/drivers/media/video/gspca/ov519.c
new file mode 100644 (file)
index 0000000..08d99c3
--- /dev/null
@@ -0,0 +1,2186 @@
+/**
+ * OV519 driver
+ *
+ * Copyright (C) 2008 Jean-Francois Moine (http://moinejf.free.fr)
+ *
+ * (This module is adapted from the ov51x-jpeg package)
+ *
+ * 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
+ * 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
+ *
+ */
+#define MODULE_NAME "ov519"
+
+#include "gspca.h"
+
+#define DRIVER_VERSION_NUMBER  KERNEL_VERSION(2, 1, 7)
+static const char version[] = "2.1.7";
+
+MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>");
+MODULE_DESCRIPTION("OV519 USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* global parameters */
+static int frame_rate;
+
+/* Number of times to retry a failed I2C transaction. Increase this if you
+ * are getting "Failed to read sensor ID..." */
+static int i2c_detect_tries = 10;
+
+/* ov519 device descriptor */
+struct sd {
+       struct gspca_dev gspca_dev;             /* !! must be the first item */
+
+       /* Determined by sensor type */
+       short maxwidth;
+       short maxheight;
+
+       unsigned char primary_i2c_slave;        /* I2C write id of sensor */
+
+       unsigned char brightness;
+       unsigned char contrast;
+       unsigned char colors;
+
+       char compress;          /* Should the next frame be compressed? */
+       char compress_inited;   /* Are compression params uploaded? */
+       char stopped;           /* Streaming is temporarily paused */
+
+       char frame_rate;        /* current Framerate (OV519 only) */
+       char clockdiv;          /* clockdiv override for OV519 only */
+
+       char sensor;            /* Type of image sensor chip (SEN_*) */
+#define SEN_UNKNOWN 0
+#define SEN_OV6620 1
+#define SEN_OV6630 2
+#define SEN_OV7610 3
+#define SEN_OV7620 4
+#define SEN_OV7630 5
+#define SEN_OV7640 6
+#define SEN_OV7670 7
+#define SEN_OV76BE 8
+#define SEN_OV8610 9
+
+};
+
+/* V4L2 controls supported by the driver */
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
+
+static struct ctrl sd_ctrls[] = {
+#define SD_BRIGHTNESS 0
+       {
+           {
+               .id      = V4L2_CID_BRIGHTNESS,
+               .type    = V4L2_CTRL_TYPE_INTEGER,
+               .name    = "Brightness",
+               .minimum = 0,
+               .maximum = 255,
+               .step    = 1,
+               .default_value = 127,
+           },
+           .set = sd_setbrightness,
+           .get = sd_getbrightness,
+       },
+#define SD_CONTRAST 1
+       {
+           {
+               .id      = V4L2_CID_CONTRAST,
+               .type    = V4L2_CTRL_TYPE_INTEGER,
+               .name    = "Contrast",
+               .minimum = 0,
+               .maximum = 255,
+               .step    = 1,
+               .default_value = 127,
+           },
+           .set = sd_setcontrast,
+           .get = sd_getcontrast,
+       },
+#define SD_COLOR 2
+       {
+           {
+               .id      = V4L2_CID_SATURATION,
+               .type    = V4L2_CTRL_TYPE_INTEGER,
+               .name    = "Saturation",
+               .minimum = 0,
+               .maximum = 255,
+               .step    = 1,
+               .default_value = 127,
+           },
+           .set = sd_setcolors,
+           .get = sd_getcolors,
+       },
+};
+
+static struct v4l2_pix_format vga_mode[] = {
+       {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+               .bytesperline = 320,
+               .sizeimage = 320 * 240 * 3 / 8 + 589,
+               .colorspace = V4L2_COLORSPACE_JPEG,
+               .priv = 1},
+       {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+               .bytesperline = 640,
+               .sizeimage = 640 * 480 * 3 / 8 + 590,
+               .colorspace = V4L2_COLORSPACE_JPEG,
+               .priv = 0},
+};
+static struct v4l2_pix_format sif_mode[] = {
+       {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+               .bytesperline = 176,
+               .sizeimage = 176 * 144 * 3 / 8 + 589,
+               .colorspace = V4L2_COLORSPACE_JPEG,
+               .priv = 1},
+       {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+               .bytesperline = 352,
+               .sizeimage = 352 * 288 * 3 / 8 + 589,
+               .colorspace = V4L2_COLORSPACE_JPEG,
+               .priv = 0},
+};
+
+/* OV519 Camera interface register numbers */
+#define OV519_CAM_H_SIZE               0x10
+#define OV519_CAM_V_SIZE               0x11
+#define OV519_CAM_X_OFFSETL            0x12
+#define OV519_CAM_X_OFFSETH            0x13
+#define OV519_CAM_Y_OFFSETL            0x14
+#define OV519_CAM_Y_OFFSETH            0x15
+#define OV519_CAM_DIVIDER              0x16
+#define OV519_CAM_DFR                  0x20
+#define OV519_CAM_FORMAT               0x25
+
+/* OV519 System Controller register numbers */
+#define OV519_SYS_RESET1 0x51
+#define OV519_SYS_EN_CLK1 0x54
+
+#define OV519_GPIO_DATA_OUT0           0x71
+#define OV519_GPIO_IO_CTRL0            0x72
+
+#define OV511_ENDPOINT_ADDRESS  1      /* Isoc endpoint number */
+
+/* I2C registers */
+#define R51x_I2C_W_SID         0x41
+#define R51x_I2C_SADDR_3       0x42
+#define R51x_I2C_SADDR_2       0x43
+#define R51x_I2C_R_SID         0x44
+#define R51x_I2C_DATA          0x45
+#define R518_I2C_CTL           0x47    /* OV518(+) only */
+
+/* I2C ADDRESSES */
+#define OV7xx0_SID   0x42
+#define OV8xx0_SID   0xa0
+#define OV6xx0_SID   0xc0
+
+/* OV7610 registers */
+#define OV7610_REG_GAIN                0x00    /* gain setting (5:0) */
+#define OV7610_REG_SAT         0x03    /* saturation */
+#define OV8610_REG_HUE         0x04    /* 04 reserved */
+#define OV7610_REG_CNT         0x05    /* Y contrast */
+#define OV7610_REG_BRT         0x06    /* Y brightness */
+#define OV7610_REG_COM_C       0x14    /* misc common regs */
+#define OV7610_REG_ID_HIGH     0x1c    /* manufacturer ID MSB */
+#define OV7610_REG_ID_LOW      0x1d    /* manufacturer ID LSB */
+#define OV7610_REG_COM_I       0x29    /* misc settings */
+
+/* OV7670 registers */
+#define OV7670_REG_GAIN        0x00    /* Gain lower 8 bits (rest in vref) */
+#define OV7670_REG_BLUE        0x01    /* blue gain */
+#define OV7670_REG_RED         0x02    /* red gain */
+#define OV7670_REG_VREF        0x03    /* Pieces of GAIN, VSTART, VSTOP */
+#define OV7670_REG_COM1        0x04    /* Control 1 */
+#define OV7670_REG_AECHH       0x07    /* AEC MS 5 bits */
+#define OV7670_REG_COM3        0x0c    /* Control 3 */
+#define OV7670_REG_COM4        0x0d    /* Control 4 */
+#define OV7670_REG_COM5        0x0e    /* All "reserved" */
+#define OV7670_REG_COM6        0x0f    /* Control 6 */
+#define OV7670_REG_AECH        0x10    /* More bits of AEC value */
+#define OV7670_REG_CLKRC       0x11    /* Clock control */
+#define OV7670_REG_COM7        0x12    /* Control 7 */
+#define   OV7670_COM7_FMT_VGA    0x00
+#define   OV7670_COM7_YUV        0x00    /* YUV */
+#define   OV7670_COM7_FMT_QVGA   0x10    /* QVGA format */
+#define   OV7670_COM7_FMT_MASK   0x38
+#define   OV7670_COM7_RESET      0x80    /* Register reset */
+#define OV7670_REG_COM8        0x13    /* Control 8 */
+#define   OV7670_COM8_AEC        0x01    /* Auto exposure enable */
+#define   OV7670_COM8_AWB        0x02    /* White balance enable */
+#define   OV7670_COM8_AGC        0x04    /* Auto gain enable */
+#define   OV7670_COM8_BFILT      0x20    /* Band filter enable */
+#define   OV7670_COM8_AECSTEP    0x40    /* Unlimited AEC step size */
+#define   OV7670_COM8_FASTAEC    0x80    /* Enable fast AGC/AEC */
+#define OV7670_REG_COM9        0x14    /* Control 9  - gain ceiling */
+#define OV7670_REG_COM10       0x15    /* Control 10 */
+#define OV7670_REG_HSTART      0x17    /* Horiz start high bits */
+#define OV7670_REG_HSTOP       0x18    /* Horiz stop high bits */
+#define OV7670_REG_VSTART      0x19    /* Vert start high bits */
+#define OV7670_REG_VSTOP       0x1a    /* Vert stop high bits */
+#define OV7670_REG_MVFP        0x1e    /* Mirror / vflip */
+#define   OV7670_MVFP_MIRROR     0x20    /* Mirror image */
+#define OV7670_REG_AEW         0x24    /* AGC upper limit */
+#define OV7670_REG_AEB         0x25    /* AGC lower limit */
+#define OV7670_REG_VPT         0x26    /* AGC/AEC fast mode op region */
+#define OV7670_REG_HREF        0x32    /* HREF pieces */
+#define OV7670_REG_TSLB        0x3a    /* lots of stuff */
+#define OV7670_REG_COM11       0x3b    /* Control 11 */
+#define   OV7670_COM11_EXP       0x02
+#define   OV7670_COM11_HZAUTO    0x10    /* Auto detect 50/60 Hz */
+#define OV7670_REG_COM12       0x3c    /* Control 12 */
+#define OV7670_REG_COM13       0x3d    /* Control 13 */
+#define   OV7670_COM13_GAMMA     0x80    /* Gamma enable */
+#define   OV7670_COM13_UVSAT     0x40    /* UV saturation auto adjustment */
+#define OV7670_REG_COM14       0x3e    /* Control 14 */
+#define OV7670_REG_EDGE        0x3f    /* Edge enhancement factor */
+#define OV7670_REG_COM15       0x40    /* Control 15 */
+#define   OV7670_COM15_R00FF     0xc0    /*            00 to FF */
+#define OV7670_REG_COM16       0x41    /* Control 16 */
+#define   OV7670_COM16_AWBGAIN   0x08    /* AWB gain enable */
+#define OV7670_REG_BRIGHT      0x55    /* Brightness */
+#define OV7670_REG_CONTRAS     0x56    /* Contrast control */
+#define OV7670_REG_GFIX        0x69    /* Fix gain control */
+#define OV7670_REG_RGB444      0x8c    /* RGB 444 control */
+#define OV7670_REG_HAECC1      0x9f    /* Hist AEC/AGC control 1 */
+#define OV7670_REG_HAECC2      0xa0    /* Hist AEC/AGC control 2 */
+#define OV7670_REG_BD50MAX     0xa5    /* 50hz banding step limit */
+#define OV7670_REG_HAECC3      0xa6    /* Hist AEC/AGC control 3 */
+#define OV7670_REG_HAECC4      0xa7    /* Hist AEC/AGC control 4 */
+#define OV7670_REG_HAECC5      0xa8    /* Hist AEC/AGC control 5 */
+#define OV7670_REG_HAECC6      0xa9    /* Hist AEC/AGC control 6 */
+#define OV7670_REG_HAECC7      0xaa    /* Hist AEC/AGC control 7 */
+#define OV7670_REG_BD60MAX     0xab    /* 60hz banding step limit */
+
+struct ovsensor_window {
+       short x;
+       short y;
+       short width;
+       short height;
+/*     int format; */
+       short quarter;          /* Scale width and height down 2x */
+       short clockdiv;         /* Clock divisor setting */
+};
+
+static unsigned char ov7670_abs_to_sm(unsigned char v)
+{
+       if (v > 127)
+               return v & 0x7f;
+       return (128 - v) | 0x80;
+}
+
+/* Write a OV519 register */
+static int reg_w(struct sd *sd, __u16 index, __u8 value)
+{
+       int ret;
+
+       sd->gspca_dev.usb_buf[0] = value;
+       ret = usb_control_msg(sd->gspca_dev.dev,
+                       usb_sndctrlpipe(sd->gspca_dev.dev, 0),
+                       1,                      /* REQ_IO (ov518/519) */
+                       USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                       0, index,
+                       sd->gspca_dev.usb_buf, 1, 500);
+       if (ret < 0)
+               PDEBUG(D_ERR, "Write reg [%02x] %02x failed", index, value);
+       return ret;
+}
+
+/* Read from a OV519 register */
+/* returns: negative is error, pos or zero is data */
+static int reg_r(struct sd *sd, __u16 index)
+{
+       int ret;
+
+       ret = usb_control_msg(sd->gspca_dev.dev,
+                       usb_rcvctrlpipe(sd->gspca_dev.dev, 0),
+                       1,                      /* REQ_IO */
+                       USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                       0, index, sd->gspca_dev.usb_buf, 1, 500);
+
+       if (ret >= 0)
+               ret = sd->gspca_dev.usb_buf[0];
+       else
+               PDEBUG(D_ERR, "Read reg [0x%02x] failed", index);
+       return ret;
+}
+
+/* Read 8 values from a OV519 register */
+static int reg_r8(struct sd *sd,
+                 __u16 index)
+{
+       int ret;
+
+       ret = usb_control_msg(sd->gspca_dev.dev,
+                       usb_rcvctrlpipe(sd->gspca_dev.dev, 0),
+                       1,                      /* REQ_IO */
+                       USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                       0, index, sd->gspca_dev.usb_buf, 8, 500);
+
+       if (ret >= 0)
+               ret = sd->gspca_dev.usb_buf[0];
+       else
+               PDEBUG(D_ERR, "Read reg 8 [0x%02x] failed", index);
+       return ret;
+}
+
+/*
+ * Writes bits at positions specified by mask to an OV51x reg. Bits that are in
+ * the same position as 1's in "mask" are cleared and set to "value". Bits
+ * that are in the same position as 0's in "mask" are preserved, regardless
+ * of their respective state in "value".
+ */
+static int reg_w_mask(struct sd *sd,
+                       __u16 index,
+                       __u8 value,
+                       __u8 mask)
+{
+       int ret;
+       __u8 oldval;
+
+       if (mask != 0xff) {
+               value &= mask;                  /* Enforce mask on value */
+               ret = reg_r(sd, index);
+               if (ret < 0)
+                       return ret;
+
+               oldval = ret & ~mask;           /* Clear the masked bits */
+               value |= oldval;                /* Set the desired bits */
+       }
+       return reg_w(sd, index, value);
+}
+
+/*
+ * The OV518 I2C I/O procedure is different, hence, this function.
+ * This is normally only called from i2c_w(). Note that this function
+ * always succeeds regardless of whether the sensor is present and working.
+ */
+static int i2c_w(struct sd *sd,
+               __u8 reg,
+               __u8 value)
+{
+       int rc;
+
+       PDEBUG(D_USBO, "i2c 0x%02x -> [0x%02x]", value, reg);
+
+       /* Select camera register */
+       rc = reg_w(sd, R51x_I2C_SADDR_3, reg);
+       if (rc < 0)
+               return rc;
+
+       /* Write "value" to I2C data port of OV511 */
+       rc = reg_w(sd, R51x_I2C_DATA, value);
+       if (rc < 0)
+               return rc;
+
+       /* Initiate 3-byte write cycle */
+       rc = reg_w(sd, R518_I2C_CTL, 0x01);
+
+       /* wait for write complete */
+       msleep(4);
+       if (rc < 0)
+               return rc;
+       return reg_r8(sd, R518_I2C_CTL);
+}
+
+/*
+ * returns: negative is error, pos or zero is data
+ *
+ * The OV518 I2C I/O procedure is different, hence, this function.
+ * This is normally only called from i2c_r(). Note that this function
+ * always succeeds regardless of whether the sensor is present and working.
+ */
+static int i2c_r(struct sd *sd, __u8 reg)
+{
+       int rc, value;
+
+       /* Select camera register */
+       rc = reg_w(sd, R51x_I2C_SADDR_2, reg);
+       if (rc < 0)
+               return rc;
+
+       /* Initiate 2-byte write cycle */
+       rc = reg_w(sd, R518_I2C_CTL, 0x03);
+       if (rc < 0)
+               return rc;
+
+       /* Initiate 2-byte read cycle */
+       rc = reg_w(sd, R518_I2C_CTL, 0x05);
+       if (rc < 0)
+               return rc;
+       value = reg_r(sd, R51x_I2C_DATA);
+       PDEBUG(D_USBI, "i2c [0x%02X] -> 0x%02X", reg, value);
+       return value;
+}
+
+/* Writes bits at positions specified by mask to an I2C reg. Bits that are in
+ * the same position as 1's in "mask" are cleared and set to "value". Bits
+ * that are in the same position as 0's in "mask" are preserved, regardless
+ * of their respective state in "value".
+ */
+static int i2c_w_mask(struct sd *sd,
+                  __u8 reg,
+                  __u8 value,
+                  __u8 mask)
+{
+       int rc;
+       __u8 oldval;
+
+       value &= mask;                  /* Enforce mask on value */
+       rc = i2c_r(sd, reg);
+       if (rc < 0)
+               return rc;
+       oldval = rc & ~mask;            /* Clear the masked bits */
+       value |= oldval;                /* Set the desired bits */
+       return i2c_w(sd, reg, value);
+}
+
+/* Temporarily stops OV511 from functioning. Must do this before changing
+ * registers while the camera is streaming */
+static inline int ov51x_stop(struct sd *sd)
+{
+       PDEBUG(D_STREAM, "stopping");
+       sd->stopped = 1;
+       return reg_w(sd, OV519_SYS_RESET1, 0x0f);
+}
+
+/* Restarts OV511 after ov511_stop() is called. Has no effect if it is not
+ * actually stopped (for performance). */
+static inline int ov51x_restart(struct sd *sd)
+{
+       PDEBUG(D_STREAM, "restarting");
+       if (!sd->stopped)
+               return 0;
+       sd->stopped = 0;
+
+       /* Reinitialize the stream */
+       return reg_w(sd, OV519_SYS_RESET1, 0x00);
+}
+
+/* This does an initial reset of an OmniVision sensor and ensures that I2C
+ * is synchronized. Returns <0 on failure.
+ */
+static int init_ov_sensor(struct sd *sd)
+{
+       int i, success;
+
+       /* Reset the sensor */
+       if (i2c_w(sd, 0x12, 0x80) < 0)
+               return -EIO;
+
+       /* Wait for it to initialize */
+       msleep(150);
+
+       for (i = 0, success = 0; i < i2c_detect_tries && !success; i++) {
+               if (i2c_r(sd, OV7610_REG_ID_HIGH) == 0x7f &&
+                   i2c_r(sd, OV7610_REG_ID_LOW) == 0xa2) {
+                       success = 1;
+                       continue;
+               }
+
+               /* Reset the sensor */
+               if (i2c_w(sd, 0x12, 0x80) < 0)
+                       return -EIO;
+               /* Wait for it to initialize */
+               msleep(150);
+               /* Dummy read to sync I2C */
+               if (i2c_r(sd, 0x00) < 0)
+                       return -EIO;
+       }
+       if (!success)
+               return -EIO;
+       PDEBUG(D_PROBE, "I2C synced in %d attempt(s)", i);
+       return 0;
+}
+
+/* Switch on standard JPEG compression. Returns 0 for success. */
+static int ov519_init_compression(struct sd *sd)
+{
+       if (!sd->compress_inited) {
+               if (reg_w_mask(sd, OV519_SYS_EN_CLK1, 1 << 2, 1 << 2) < 0) {
+                       PDEBUG(D_ERR, "Error switching to compressed mode");
+                       return -EIO;
+               }
+               sd->compress_inited = 1;
+       }
+       return 0;
+}
+
+/* Set the read and write slave IDs. The "slave" argument is the write slave,
+ * and the read slave will be set to (slave + 1).
+ * This should not be called from outside the i2c I/O functions.
+ * Sets I2C read and write slave IDs. Returns <0 for error
+ */
+static int ov51x_set_slave_ids(struct sd *sd,
+                               __u8 slave)
+{
+       int rc;
+
+       rc = reg_w(sd, R51x_I2C_W_SID, slave);
+       if (rc < 0)
+               return rc;
+       return reg_w(sd, R51x_I2C_R_SID, slave + 1);
+}
+
+struct ov_regvals {
+       __u8 reg;
+       __u8 val;
+};
+struct ov_i2c_regvals {
+       __u8 reg;
+       __u8 val;
+};
+
+static int write_regvals(struct sd *sd,
+                        const struct ov_regvals *regvals,
+                        int n)
+{
+       int rc;
+
+       while (--n >= 0) {
+               rc = reg_w(sd, regvals->reg, regvals->val);
+               if (rc < 0)
+                       return rc;
+               regvals++;
+       }
+       return 0;
+}
+
+static int write_i2c_regvals(struct sd *sd,
+                            const struct ov_i2c_regvals *regvals,
+                            int n)
+{
+       int rc;
+
+       while (--n >= 0) {
+               rc = i2c_w(sd, regvals->reg, regvals->val);
+               if (rc < 0)
+                       return rc;
+               regvals++;
+       }
+       return 0;
+}
+
+/****************************************************************************
+ *
+ * OV511 and sensor configuration
+ *
+ ***************************************************************************/
+
+/* This initializes the OV8110, OV8610 sensor. The OV8110 uses
+ * the same register settings as the OV8610, since they are very similar.
+ */
+static int ov8xx0_configure(struct sd *sd)
+{
+       int rc;
+       static const struct ov_i2c_regvals norm_8610[] = {
+               { 0x12, 0x80 },
+               { 0x00, 0x00 },
+               { 0x01, 0x80 },
+               { 0x02, 0x80 },
+               { 0x03, 0xc0 },
+               { 0x04, 0x30 },
+               { 0x05, 0x30 }, /* was 0x10, new from windrv 090403 */
+               { 0x06, 0x70 }, /* was 0x80, new from windrv 090403 */
+               { 0x0a, 0x86 },
+               { 0x0b, 0xb0 },
+               { 0x0c, 0x20 },
+               { 0x0d, 0x20 },
+               { 0x11, 0x01 },
+               { 0x12, 0x25 },
+               { 0x13, 0x01 },
+               { 0x14, 0x04 },
+               { 0x15, 0x01 }, /* Lin and Win think different about UV order */
+               { 0x16, 0x03 },
+               { 0x17, 0x38 }, /* was 0x2f, new from windrv 090403 */
+               { 0x18, 0xea }, /* was 0xcf, new from windrv 090403 */
+               { 0x19, 0x02 }, /* was 0x06, new from windrv 090403 */
+               { 0x1a, 0xf5 },
+               { 0x1b, 0x00 },
+               { 0x20, 0xd0 }, /* was 0x90, new from windrv 090403 */
+               { 0x23, 0xc0 }, /* was 0x00, new from windrv 090403 */
+               { 0x24, 0x30 }, /* was 0x1d, new from windrv 090403 */
+               { 0x25, 0x50 }, /* was 0x57, new from windrv 090403 */
+               { 0x26, 0xa2 },
+               { 0x27, 0xea },
+               { 0x28, 0x00 },
+               { 0x29, 0x00 },
+               { 0x2a, 0x80 },
+               { 0x2b, 0xc8 }, /* was 0xcc, new from windrv 090403 */
+               { 0x2c, 0xac },
+               { 0x2d, 0x45 }, /* was 0xd5, new from windrv 090403 */
+               { 0x2e, 0x80 },
+               { 0x2f, 0x14 }, /* was 0x01, new from windrv 090403 */
+               { 0x4c, 0x00 },
+               { 0x4d, 0x30 }, /* was 0x10, new from windrv 090403 */
+               { 0x60, 0x02 }, /* was 0x01, new from windrv 090403 */
+               { 0x61, 0x00 }, /* was 0x09, new from windrv 090403 */
+               { 0x62, 0x5f }, /* was 0xd7, new from windrv 090403 */
+               { 0x63, 0xff },
+               { 0x64, 0x53 }, /* new windrv 090403 says 0x57,
+                                * maybe thats wrong */
+               { 0x65, 0x00 },
+               { 0x66, 0x55 },
+               { 0x67, 0xb0 },
+               { 0x68, 0xc0 }, /* was 0xaf, new from windrv 090403 */
+               { 0x69, 0x02 },
+               { 0x6a, 0x22 },
+               { 0x6b, 0x00 },
+               { 0x6c, 0x99 }, /* was 0x80, old windrv says 0x00, but
+                                  deleting bit7 colors the first images red */
+               { 0x6d, 0x11 }, /* was 0x00, new from windrv 090403 */
+               { 0x6e, 0x11 }, /* was 0x00, new from windrv 090403 */
+               { 0x6f, 0x01 },
+               { 0x70, 0x8b },
+               { 0x71, 0x00 },
+               { 0x72, 0x14 },
+               { 0x73, 0x54 },
+               { 0x74, 0x00 },/* 0x60? - was 0x00, new from windrv 090403 */
+               { 0x75, 0x0e },
+               { 0x76, 0x02 }, /* was 0x02, new from windrv 090403 */
+               { 0x77, 0xff },
+               { 0x78, 0x80 },
+               { 0x79, 0x80 },
+               { 0x7a, 0x80 },
+               { 0x7b, 0x10 }, /* was 0x13, new from windrv 090403 */
+               { 0x7c, 0x00 },
+               { 0x7d, 0x08 }, /* was 0x09, new from windrv 090403 */
+               { 0x7e, 0x08 }, /* was 0xc0, new from windrv 090403 */
+               { 0x7f, 0xfb },
+               { 0x80, 0x28 },
+               { 0x81, 0x00 },
+               { 0x82, 0x23 },
+               { 0x83, 0x0b },
+               { 0x84, 0x00 },
+               { 0x85, 0x62 }, /* was 0x61, new from windrv 090403 */
+               { 0x86, 0xc9 },
+               { 0x87, 0x00 },
+               { 0x88, 0x00 },
+               { 0x89, 0x01 },
+               { 0x12, 0x20 },
+               { 0x12, 0x25 }, /* was 0x24, new from windrv 090403 */
+       };
+
+       PDEBUG(D_PROBE, "starting ov8xx0 configuration");
+
+       if (init_ov_sensor(sd) < 0)
+               PDEBUG(D_ERR|D_PROBE, "Failed to read sensor ID");
+       else
+               PDEBUG(D_PROBE, "OV86x0 initialized");
+
+       /* Detect sensor (sub)type */
+       rc = i2c_r(sd, OV7610_REG_COM_I);
+       if (rc < 0) {
+               PDEBUG(D_ERR, "Error detecting sensor type");
+               return -1;
+       }
+       if ((rc & 3) == 1) {
+               PDEBUG(D_PROBE, "Sensor is an OV8610");
+               sd->sensor = SEN_OV8610;
+       } else {
+               PDEBUG(D_ERR, "Unknown image sensor version: %d", rc & 3);
+               return -1;
+       }
+       PDEBUG(D_PROBE, "Writing 8610 registers");
+       if (write_i2c_regvals(sd,
+                       norm_8610,
+                       sizeof norm_8610 / sizeof norm_8610[0]))
+               return -1;
+
+       /* Set sensor-specific vars */
+       sd->maxwidth = 640;
+       sd->maxheight = 480;
+       return 0;
+}
+
+/* This initializes the OV7610, OV7620, or OV76BE sensor. The OV76BE uses
+ * the same register settings as the OV7610, since they are very similar.
+ */
+static int ov7xx0_configure(struct sd *sd)
+{
+       int rc, high, low;
+
+       /* Lawrence Glaister <lg@jfm.bc.ca> reports:
+        *
+        * Register 0x0f in the 7610 has the following effects:
+        *
+        * 0x85 (AEC method 1): Best overall, good contrast range
+        * 0x45 (AEC method 2): Very overexposed
+        * 0xa5 (spec sheet default): Ok, but the black level is
+        *      shifted resulting in loss of contrast
+        * 0x05 (old driver setting): very overexposed, too much
+        *      contrast
+        */
+       static const struct ov_i2c_regvals norm_7610[] = {
+               { 0x10, 0xff },
+               { 0x16, 0x06 },
+               { 0x28, 0x24 },
+               { 0x2b, 0xac },
+               { 0x12, 0x00 },
+               { 0x38, 0x81 },
+               { 0x28, 0x24 }, /* 0c */
+               { 0x0f, 0x85 }, /* lg's setting */
+               { 0x15, 0x01 },
+               { 0x20, 0x1c },
+               { 0x23, 0x2a },
+               { 0x24, 0x10 },
+               { 0x25, 0x8a },
+               { 0x26, 0xa2 },
+               { 0x27, 0xc2 },
+               { 0x2a, 0x04 },
+               { 0x2c, 0xfe },
+               { 0x2d, 0x93 },
+               { 0x30, 0x71 },
+               { 0x31, 0x60 },
+               { 0x32, 0x26 },
+               { 0x33, 0x20 },
+               { 0x34, 0x48 },
+               { 0x12, 0x24 },
+               { 0x11, 0x01 },
+               { 0x0c, 0x24 },
+               { 0x0d, 0x24 },
+       };
+
+       static const struct ov_i2c_regvals norm_7620[] = {
+               { 0x00, 0x00 },         /* gain */
+               { 0x01, 0x80 },         /* blue gain */
+               { 0x02, 0x80 },         /* red gain */
+               { 0x03, 0xc0 },         /* OV7670_REG_VREF */
+               { 0x06, 0x60 },
+               { 0x07, 0x00 },
+               { 0x0c, 0x24 },
+               { 0x0c, 0x24 },
+               { 0x0d, 0x24 },
+               { 0x11, 0x01 },
+               { 0x12, 0x24 },
+               { 0x13, 0x01 },
+               { 0x14, 0x84 },
+               { 0x15, 0x01 },
+               { 0x16, 0x03 },
+               { 0x17, 0x2f },
+               { 0x18, 0xcf },
+               { 0x19, 0x06 },
+               { 0x1a, 0xf5 },
+               { 0x1b, 0x00 },
+               { 0x20, 0x18 },
+               { 0x21, 0x80 },
+               { 0x22, 0x80 },
+               { 0x23, 0x00 },
+               { 0x26, 0xa2 },
+               { 0x27, 0xea },
+               { 0x28, 0x20 },
+               { 0x29, 0x00 },
+               { 0x2a, 0x10 },
+               { 0x2b, 0x00 },
+               { 0x2c, 0x88 },
+               { 0x2d, 0x91 },
+               { 0x2e, 0x80 },
+               { 0x2f, 0x44 },
+               { 0x60, 0x27 },
+               { 0x61, 0x02 },
+               { 0x62, 0x5f },
+               { 0x63, 0xd5 },
+               { 0x64, 0x57 },
+               { 0x65, 0x83 },
+               { 0x66, 0x55 },
+               { 0x67, 0x92 },
+               { 0x68, 0xcf },
+               { 0x69, 0x76 },
+               { 0x6a, 0x22 },
+               { 0x6b, 0x00 },
+               { 0x6c, 0x02 },
+               { 0x6d, 0x44 },
+               { 0x6e, 0x80 },
+               { 0x6f, 0x1d },
+               { 0x70, 0x8b },
+               { 0x71, 0x00 },
+               { 0x72, 0x14 },
+               { 0x73, 0x54 },
+               { 0x74, 0x00 },
+               { 0x75, 0x8e },
+               { 0x76, 0x00 },
+               { 0x77, 0xff },
+               { 0x78, 0x80 },
+               { 0x79, 0x80 },
+               { 0x7a, 0x80 },
+               { 0x7b, 0xe2 },
+               { 0x7c, 0x00 },
+       };
+
+       /* 7640 and 7648. The defaults should be OK for most registers. */
+       static const struct ov_i2c_regvals norm_7640[] = {
+               { 0x12, 0x80 },
+               { 0x12, 0x14 },
+       };
+
+       /* 7670. Defaults taken from OmniVision provided data,
+       *  as provided by Jonathan Corbet of OLPC               */
+       static const struct ov_i2c_regvals norm_7670[] = {
+               { OV7670_REG_COM7, OV7670_COM7_RESET },
+               { OV7670_REG_TSLB, 0x04 },              /* OV */
+               { OV7670_REG_COM7, OV7670_COM7_FMT_VGA }, /* VGA */
+               { OV7670_REG_CLKRC, 0x1 },
+       /*
+        * Set the hardware window.  These values from OV don't entirely
+        * make sense - hstop is less than hstart.  But they work...
+        */
+               { OV7670_REG_HSTART, 0x13 },    { OV7670_REG_HSTOP, 0x01 },
+               { OV7670_REG_HREF, 0xb6 },      { OV7670_REG_VSTART, 0x02 },
+               { OV7670_REG_VSTOP, 0x7a },     { OV7670_REG_VREF, 0x0a },
+
+               { OV7670_REG_COM3, 0 }, { OV7670_REG_COM14, 0 },
+       /* Mystery scaling numbers */
+               { 0x70, 0x3a },         { 0x71, 0x35 },
+               { 0x72, 0x11 },         { 0x73, 0xf0 },
+               { 0xa2, 0x02 },
+/* jfm */
+/* { OV7670_REG_COM10, 0x0 }, */
+
+       /* Gamma curve values */
+               { 0x7a, 0x20 },
+/* jfm:win 7b=1c */
+               { 0x7b, 0x10 },
+/* jfm:win 7c=28 */
+               { 0x7c, 0x1e },
+/* jfm:win 7d=3c */
+               { 0x7d, 0x35 },
+               { 0x7e, 0x5a },         { 0x7f, 0x69 },
+               { 0x80, 0x76 },         { 0x81, 0x80 },
+               { 0x82, 0x88 },         { 0x83, 0x8f },
+               { 0x84, 0x96 },         { 0x85, 0xa3 },
+               { 0x86, 0xaf },         { 0x87, 0xc4 },
+               { 0x88, 0xd7 },         { 0x89, 0xe8 },
+
+       /* AGC and AEC parameters.  Note we start by disabling those features,
+          then turn them only after tweaking the values. */
+               { OV7670_REG_COM8, OV7670_COM8_FASTAEC
+                                | OV7670_COM8_AECSTEP
+                                | OV7670_COM8_BFILT },
+               { OV7670_REG_GAIN, 0 }, { OV7670_REG_AECH, 0 },
+               { OV7670_REG_COM4, 0x40 }, /* magic reserved bit */
+/* jfm:win 14=38 */
+               { OV7670_REG_COM9, 0x18 }, /* 4x gain + magic rsvd bit */
+               { OV7670_REG_BD50MAX, 0x05 },   { OV7670_REG_BD60MAX, 0x07 },
+               { OV7670_REG_AEW, 0x95 },       { OV7670_REG_AEB, 0x33 },
+               { OV7670_REG_VPT, 0xe3 },       { OV7670_REG_HAECC1, 0x78 },
+               { OV7670_REG_HAECC2, 0x68 },
+/* jfm:win a1=0b */
+               { 0xa1, 0x03 }, /* magic */
+               { OV7670_REG_HAECC3, 0xd8 },    { OV7670_REG_HAECC4, 0xd8 },
+               { OV7670_REG_HAECC5, 0xf0 },    { OV7670_REG_HAECC6, 0x90 },
+               { OV7670_REG_HAECC7, 0x94 },
+               { OV7670_REG_COM8, OV7670_COM8_FASTAEC
+                               | OV7670_COM8_AECSTEP
+                               | OV7670_COM8_BFILT
+                               | OV7670_COM8_AGC
+                               | OV7670_COM8_AEC },
+
+       /* Almost all of these are magic "reserved" values.  */
+               { OV7670_REG_COM5, 0x61 },      { OV7670_REG_COM6, 0x4b },
+               { 0x16, 0x02 },
+/* jfm */
+/*             { OV7670_REG_MVFP, 0x07|OV7670_MVFP_MIRROR }, */
+               { OV7670_REG_MVFP, 0x07 },
+               { 0x21, 0x02 },         { 0x22, 0x91 },
+               { 0x29, 0x07 },         { 0x33, 0x0b },
+               { 0x35, 0x0b },         { 0x37, 0x1d },
+               { 0x38, 0x71 },         { 0x39, 0x2a },
+               { OV7670_REG_COM12, 0x78 },     { 0x4d, 0x40 },
+               { 0x4e, 0x20 },         { OV7670_REG_GFIX, 0 },
+               { 0x6b, 0x4a },         { 0x74, 0x10 },
+               { 0x8d, 0x4f },         { 0x8e, 0 },
+               { 0x8f, 0 },            { 0x90, 0 },
+               { 0x91, 0 },            { 0x96, 0 },
+               { 0x9a, 0 },            { 0xb0, 0x84 },
+               { 0xb1, 0x0c },         { 0xb2, 0x0e },
+               { 0xb3, 0x82 },         { 0xb8, 0x0a },
+
+       /* More reserved magic, some of which tweaks white balance */
+               { 0x43, 0x0a },         { 0x44, 0xf0 },
+               { 0x45, 0x34 },         { 0x46, 0x58 },
+               { 0x47, 0x28 },         { 0x48, 0x3a },
+               { 0x59, 0x88 },         { 0x5a, 0x88 },
+               { 0x5b, 0x44 },         { 0x5c, 0x67 },
+               { 0x5d, 0x49 },         { 0x5e, 0x0e },
+               { 0x6c, 0x0a },         { 0x6d, 0x55 },
+               { 0x6e, 0x11 },         { 0x6f, 0x9f },
+                                               /* "9e for advance AWB" */
+               { 0x6a, 0x40 },         { OV7670_REG_BLUE, 0x40 },
+               { OV7670_REG_RED, 0x60 },
+               { OV7670_REG_COM8, OV7670_COM8_FASTAEC
+                               | OV7670_COM8_AECSTEP
+                               | OV7670_COM8_BFILT
+                               | OV7670_COM8_AGC
+                               | OV7670_COM8_AEC
+                               | OV7670_COM8_AWB },
+
+       /* Matrix coefficients */
+               { 0x4f, 0x80 },         { 0x50, 0x80 },
+               { 0x51, 0 },            { 0x52, 0x22 },
+               { 0x53, 0x5e },         { 0x54, 0x80 },
+               { 0x58, 0x9e },
+
+               { OV7670_REG_COM16, OV7670_COM16_AWBGAIN },
+               { OV7670_REG_EDGE, 0 },
+               { 0x75, 0x05 },         { 0x76, 0xe1 },
+               { 0x4c, 0 },            { 0x77, 0x01 },
+               { OV7670_REG_COM13, 0xc3 },     { 0x4b, 0x09 },
+               { 0xc9, 0x60 },         { OV7670_REG_COM16, 0x38 },
+               { 0x56, 0x40 },
+
+               { 0x34, 0x11 },
+               { OV7670_REG_COM11, OV7670_COM11_EXP|OV7670_COM11_HZAUTO },
+               { 0xa4, 0x88 },         { 0x96, 0 },
+               { 0x97, 0x30 },         { 0x98, 0x20 },
+               { 0x99, 0x30 },         { 0x9a, 0x84 },
+               { 0x9b, 0x29 },         { 0x9c, 0x03 },
+               { 0x9d, 0x4c },         { 0x9e, 0x3f },
+               { 0x78, 0x04 },
+
+       /* Extra-weird stuff.  Some sort of multiplexor register */
+               { 0x79, 0x01 },         { 0xc8, 0xf0 },
+               { 0x79, 0x0f },         { 0xc8, 0x00 },
+               { 0x79, 0x10 },         { 0xc8, 0x7e },
+               { 0x79, 0x0a },         { 0xc8, 0x80 },
+               { 0x79, 0x0b },         { 0xc8, 0x01 },
+               { 0x79, 0x0c },         { 0xc8, 0x0f },
+               { 0x79, 0x0d },         { 0xc8, 0x20 },
+               { 0x79, 0x09 },         { 0xc8, 0x80 },
+               { 0x79, 0x02 },         { 0xc8, 0xc0 },
+               { 0x79, 0x03 },         { 0xc8, 0x40 },
+               { 0x79, 0x05 },         { 0xc8, 0x30 },
+               { 0x79, 0x26 },
+
+       /* Format YUV422 */
+               { OV7670_REG_COM7, OV7670_COM7_YUV },  /* Selects YUV mode */
+               { OV7670_REG_RGB444, 0 },       /* No RGB444 please */
+               { OV7670_REG_COM1, 0 },
+               { OV7670_REG_COM15, OV7670_COM15_R00FF },
+               { OV7670_REG_COM9, 0x18 },
+                               /* 4x gain ceiling; 0x8 is reserved bit */
+               { 0x4f, 0x80 },         /* "matrix coefficient 1" */
+               { 0x50, 0x80 },         /* "matrix coefficient 2" */
+               { 0x52, 0x22 },         /* "matrix coefficient 4" */
+               { 0x53, 0x5e },         /* "matrix coefficient 5" */
+               { 0x54, 0x80 },         /* "matrix coefficient 6" */
+               { OV7670_REG_COM13, OV7670_COM13_GAMMA|OV7670_COM13_UVSAT },
+};
+
+       PDEBUG(D_PROBE, "starting OV7xx0 configuration");
+
+/* jfm:already done? */
+       if (init_ov_sensor(sd) < 0)
+               PDEBUG(D_ERR, "Failed to read sensor ID");
+       else
+               PDEBUG(D_PROBE, "OV7xx0 initialized");
+
+       /* Detect sensor (sub)type */
+       rc = i2c_r(sd, OV7610_REG_COM_I);
+
+       /* add OV7670 here
+        * it appears to be wrongly detected as a 7610 by default */
+       if (rc < 0) {
+               PDEBUG(D_ERR, "Error detecting sensor type");
+               return -1;
+       }
+       if ((rc & 3) == 3) {
+               /* quick hack to make OV7670s work */
+               high = i2c_r(sd, 0x0a);
+               low = i2c_r(sd, 0x0b);
+               /* info("%x, %x", high, low); */
+               if (high == 0x76 && low == 0x73) {
+                       PDEBUG(D_PROBE, "Sensor is an OV7670");
+                       sd->sensor = SEN_OV7670;
+               } else {
+                       PDEBUG(D_PROBE, "Sensor is an OV7610");
+                       sd->sensor = SEN_OV7610;
+               }
+       } else if ((rc & 3) == 1) {
+               /* I don't know what's different about the 76BE yet. */
+               if (i2c_r(sd, 0x15) & 1)
+                       PDEBUG(D_PROBE, "Sensor is an OV7620AE");
+               else
+                       PDEBUG(D_PROBE, "Sensor is an OV76BE");
+
+               /* OV511+ will return all zero isoc data unless we
+                * configure the sensor as a 7620. Someone needs to
+                * find the exact reg. setting that causes this. */
+               sd->sensor = SEN_OV76BE;
+       } else if ((rc & 3) == 0) {
+               /* try to read product id registers */
+               high = i2c_r(sd, 0x0a);
+               if (high < 0) {
+                       PDEBUG(D_ERR, "Error detecting camera chip PID");
+                       return high;
+               }
+               low = i2c_r(sd, 0x0b);
+               if (low < 0) {
+                       PDEBUG(D_ERR, "Error detecting camera chip VER");
+                       return low;
+               }
+               if (high == 0x76) {
+                       if (low == 0x30) {
+                               PDEBUG(D_PROBE, "Sensor is an OV7630/OV7635");
+                               sd->sensor = SEN_OV7630;
+                       } else if (low == 0x40) {
+                               PDEBUG(D_PROBE, "Sensor is an OV7645");
+                               sd->sensor = SEN_OV7640; /* FIXME */
+                       } else if (low == 0x45) {
+                               PDEBUG(D_PROBE, "Sensor is an OV7645B");
+                               sd->sensor = SEN_OV7640; /* FIXME */
+                       } else if (low == 0x48) {
+                               PDEBUG(D_PROBE, "Sensor is an OV7648");
+                               sd->sensor = SEN_OV7640; /* FIXME */
+                       } else {
+                               PDEBUG(D_PROBE, "Unknown sensor: 0x76%X", low);
+                               return -1;
+                       }
+               } else {
+                       PDEBUG(D_PROBE, "Sensor is an OV7620");
+                       sd->sensor = SEN_OV7620;
+               }
+       } else {
+               PDEBUG(D_ERR, "Unknown image sensor version: %d", rc & 3);
+               return -1;
+       }
+
+       if (sd->sensor == SEN_OV7620) {
+               PDEBUG(D_PROBE, "Writing 7620 registers");
+               if (write_i2c_regvals(sd, norm_7620,
+                               sizeof norm_7620 / sizeof norm_7620[0]))
+                       return -1;
+       } else if (sd->sensor == SEN_OV7630) {
+               PDEBUG(D_ERR, "7630 is not supported by this driver version");
+               return -1;
+       } else if (sd->sensor == SEN_OV7640) {
+               PDEBUG(D_PROBE, "Writing 7640 registers");
+               if (write_i2c_regvals(sd, norm_7640,
+                               sizeof norm_7640 / sizeof norm_7640[0]))
+                       return -1;
+       } else if (sd->sensor == SEN_OV7670) {
+               PDEBUG(D_PROBE, "Writing 7670 registers");
+               if (write_i2c_regvals(sd, norm_7670,
+                               sizeof norm_7670 / sizeof norm_7670[0]))
+                       return -1;
+       } else {
+               PDEBUG(D_PROBE, "Writing 7610 registers");
+               if (write_i2c_regvals(sd, norm_7610,
+                               sizeof norm_7610 / sizeof norm_7610[0]))
+                       return -1;
+       }
+
+       /* Set sensor-specific vars */
+       sd->maxwidth = 640;
+       sd->maxheight = 480;
+       return 0;
+}
+
+/* This initializes the OV6620, OV6630, OV6630AE, or OV6630AF sensor. */
+static int ov6xx0_configure(struct sd *sd)
+{
+       int rc;
+       static const struct ov_i2c_regvals norm_6x20[] = {
+               { 0x12, 0x80 }, /* reset */
+               { 0x11, 0x01 },
+               { 0x03, 0x60 },
+               { 0x05, 0x7f }, /* For when autoadjust is off */
+               { 0x07, 0xa8 },
+               /* The ratio of 0x0c and 0x0d  controls the white point */
+               { 0x0c, 0x24 },
+               { 0x0d, 0x24 },
+               { 0x0f, 0x15 }, /* COMS */
+               { 0x10, 0x75 }, /* AEC Exposure time */
+               { 0x12, 0x24 }, /* Enable AGC */
+               { 0x14, 0x04 },
+               /* 0x16: 0x06 helps frame stability with moving objects */
+               { 0x16, 0x06 },
+/*             { 0x20, 0x30 },  * Aperture correction enable */
+               { 0x26, 0xb2 }, /* BLC enable */
+               /* 0x28: 0x05 Selects RGB format if RGB on */
+               { 0x28, 0x05 },
+               { 0x2a, 0x04 }, /* Disable framerate adjust */
+/*             { 0x2b, 0xac },  * Framerate; Set 2a[7] first */
+               { 0x2d, 0x99 },
+               { 0x33, 0xa0 }, /* Color Processing Parameter */
+               { 0x34, 0xd2 }, /* Max A/D range */
+               { 0x38, 0x8b },
+               { 0x39, 0x40 },
+
+               { 0x3c, 0x39 }, /* Enable AEC mode changing */
+               { 0x3c, 0x3c }, /* Change AEC mode */
+               { 0x3c, 0x24 }, /* Disable AEC mode changing */
+
+               { 0x3d, 0x80 },
+               /* These next two registers (0x4a, 0x4b) are undocumented.
+                * They control the color balance */
+               { 0x4a, 0x80 },
+               { 0x4b, 0x80 },
+               { 0x4d, 0xd2 }, /* This reduces noise a bit */
+               { 0x4e, 0xc1 },
+               { 0x4f, 0x04 },
+/* Do 50-53 have any effect? */
+/* Toggle 0x12[2] off and on here? */
+       };
+
+       static const struct ov_i2c_regvals norm_6x30[] = {
+               { 0x12, 0x80 }, /* Reset */
+               { 0x00, 0x1f }, /* Gain */
+               { 0x01, 0x99 }, /* Blue gain */
+               { 0x02, 0x7c }, /* Red gain */
+               { 0x03, 0xc0 }, /* Saturation */
+               { 0x05, 0x0a }, /* Contrast */
+               { 0x06, 0x95 }, /* Brightness */
+               { 0x07, 0x2d }, /* Sharpness */
+               { 0x0c, 0x20 },
+               { 0x0d, 0x20 },
+               { 0x0e, 0x20 },
+               { 0x0f, 0x05 },
+               { 0x10, 0x9a },
+               { 0x11, 0x00 }, /* Pixel clock = fastest */
+               { 0x12, 0x24 }, /* Enable AGC and AWB */
+               { 0x13, 0x21 },
+               { 0x14, 0x80 },
+               { 0x15, 0x01 },
+               { 0x16, 0x03 },
+               { 0x17, 0x38 },
+               { 0x18, 0xea },
+               { 0x19, 0x04 },
+               { 0x1a, 0x93 },
+               { 0x1b, 0x00 },
+               { 0x1e, 0xc4 },
+               { 0x1f, 0x04 },
+               { 0x20, 0x20 },
+               { 0x21, 0x10 },
+               { 0x22, 0x88 },
+               { 0x23, 0xc0 }, /* Crystal circuit power level */
+               { 0x25, 0x9a }, /* Increase AEC black ratio */
+               { 0x26, 0xb2 }, /* BLC enable */
+               { 0x27, 0xa2 },
+               { 0x28, 0x00 },
+               { 0x29, 0x00 },
+               { 0x2a, 0x84 }, /* 60 Hz power */
+               { 0x2b, 0xa8 }, /* 60 Hz power */
+               { 0x2c, 0xa0 },
+               { 0x2d, 0x95 }, /* Enable auto-brightness */
+               { 0x2e, 0x88 },
+               { 0x33, 0x26 },
+               { 0x34, 0x03 },
+               { 0x36, 0x8f },
+               { 0x37, 0x80 },
+               { 0x38, 0x83 },
+               { 0x39, 0x80 },
+               { 0x3a, 0x0f },
+               { 0x3b, 0x3c },
+               { 0x3c, 0x1a },
+               { 0x3d, 0x80 },
+               { 0x3e, 0x80 },
+               { 0x3f, 0x0e },
+               { 0x40, 0x00 }, /* White bal */
+               { 0x41, 0x00 }, /* White bal */
+               { 0x42, 0x80 },
+               { 0x43, 0x3f }, /* White bal */
+               { 0x44, 0x80 },
+               { 0x45, 0x20 },
+               { 0x46, 0x20 },
+               { 0x47, 0x80 },
+               { 0x48, 0x7f },
+               { 0x49, 0x00 },
+               { 0x4a, 0x00 },
+               { 0x4b, 0x80 },
+               { 0x4c, 0xd0 },
+               { 0x4d, 0x10 }, /* U = 0.563u, V = 0.714v */
+               { 0x4e, 0x40 },
+               { 0x4f, 0x07 }, /* UV avg., col. killer: max */
+               { 0x50, 0xff },
+               { 0x54, 0x23 }, /* Max AGC gain: 18dB */
+               { 0x55, 0xff },
+               { 0x56, 0x12 },
+               { 0x57, 0x81 },
+               { 0x58, 0x75 },
+               { 0x59, 0x01 }, /* AGC dark current comp.: +1 */
+               { 0x5a, 0x2c },
+               { 0x5b, 0x0f }, /* AWB chrominance levels */
+               { 0x5c, 0x10 },
+               { 0x3d, 0x80 },
+               { 0x27, 0xa6 },
+               { 0x12, 0x20 }, /* Toggle AWB */
+               { 0x12, 0x24 },
+       };
+
+       PDEBUG(D_PROBE, "starting sensor configuration");
+
+       if (init_ov_sensor(sd) < 0) {
+               PDEBUG(D_ERR, "Failed to read sensor ID.");
+               return -1;
+       }
+       PDEBUG(D_PROBE, "OV6xx0 sensor detected");
+
+       /* Detect sensor (sub)type */
+       rc = i2c_r(sd, OV7610_REG_COM_I);
+       if (rc < 0) {
+               PDEBUG(D_ERR, "Error detecting sensor type");
+               return -1;
+       }
+
+       /* Ugh. The first two bits are the version bits, but
+        * the entire register value must be used. I guess OVT
+        * underestimated how many variants they would make. */
+       if (rc == 0x00) {
+               sd->sensor = SEN_OV6630;
+               PDEBUG(D_ERR,
+                       "WARNING: Sensor is an OV66308. Your camera may have");
+               PDEBUG(D_ERR, "been misdetected in previous driver versions.");
+       } else if (rc == 0x01) {
+               sd->sensor = SEN_OV6620;
+               PDEBUG(D_PROBE, "Sensor is an OV6620");
+       } else if (rc == 0x02) {
+               sd->sensor = SEN_OV6630;
+               PDEBUG(D_PROBE, "Sensor is an OV66308AE");
+       } else if (rc == 0x03) {
+               sd->sensor = SEN_OV6630;
+               PDEBUG(D_PROBE, "Sensor is an OV66308AF");
+       } else if (rc == 0x90) {
+               sd->sensor = SEN_OV6630;
+               PDEBUG(D_ERR,
+                       "WARNING: Sensor is an OV66307. Your camera may have");
+               PDEBUG(D_ERR, "been misdetected in previous driver versions.");
+       } else {
+               PDEBUG(D_ERR, "FATAL: Unknown sensor version: 0x%02x", rc);
+               return -1;
+       }
+
+       /* Set sensor-specific vars */
+       sd->maxwidth = 352;
+       sd->maxheight = 288;
+
+       if (sd->sensor == SEN_OV6620) {
+               PDEBUG(D_PROBE, "Writing 6x20 registers");
+               if (write_i2c_regvals(sd, norm_6x20,
+                               sizeof norm_6x20 / sizeof norm_6x20[0]))
+                       return -1;
+       } else {
+               PDEBUG(D_PROBE, "Writing 6x30 registers");
+               if (write_i2c_regvals(sd, norm_6x30,
+                               sizeof norm_6x30 / sizeof norm_6x30[0]))
+                       return -1;
+       }
+       return 0;
+}
+
+/* Turns on or off the LED. Only has an effect with OV511+/OV518(+)/OV519 */
+static void ov51x_led_control(struct sd *sd, int on)
+{
+       PDEBUG(D_STREAM, "LED (%s)", on ? "on" : "off");
+
+/*     if (sd->bridge == BRG_OV511PLUS) */
+/*             reg_w(sd, R511_SYS_LED_CTL, on ? 1 : 0); */
+/*     else if (sd->bridge == BRG_OV519) */
+               reg_w_mask(sd, OV519_GPIO_DATA_OUT0, !on, 1);   /* 0 / 1 */
+/*     else if (sd->bclass == BCL_OV518) */
+/*             reg_w_mask(sd, R518_GPIO_OUT, on ? 0x02 : 0x00, 0x02); */
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+                       const struct usb_device_id *id)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       struct cam *cam;
+
+/* (from ov519_configure) */
+       static const struct ov_regvals init_519[] = {
+               { 0x5a,  0x6d }, /* EnableSystem */
+/* jfm trace usbsnoop3-1.txt */
+/* jfm 53 = fb */
+               { 0x53,  0x9b },
+               { 0x54,  0xff }, /* set bit2 to enable jpeg */
+               { 0x5d,  0x03 },
+               { 0x49,  0x01 },
+               { 0x48,  0x00 },
+               /* Set LED pin to output mode. Bit 4 must be cleared or sensor
+                * detection will fail. This deserves further investigation. */
+               { OV519_GPIO_IO_CTRL0,   0xee },
+               { 0x51,  0x0f }, /* SetUsbInit */
+               { 0x51,  0x00 },
+               { 0x22,  0x00 },
+               /* windows reads 0x55 at this point*/
+       };
+
+       if (write_regvals(sd, init_519, ARRAY_SIZE(init_519)))
+               goto error;
+/* jfm: not seen in windows trace */
+       if (ov519_init_compression(sd))
+               goto error;
+       ov51x_led_control(sd, 0);       /* turn LED off */
+
+       /* Test for 76xx */
+       sd->primary_i2c_slave = OV7xx0_SID;
+       if (ov51x_set_slave_ids(sd, OV7xx0_SID) < 0)
+               goto error;
+
+       /* The OV519 must be more aggressive about sensor detection since
+        * I2C write will never fail if the sensor is not present. We have
+        * to try to initialize the sensor to detect its presence */
+       if (init_ov_sensor(sd) < 0) {
+               /* Test for 6xx0 */
+               sd->primary_i2c_slave = OV6xx0_SID;
+               if (ov51x_set_slave_ids(sd, OV6xx0_SID) < 0)
+                       goto error;
+
+               if (init_ov_sensor(sd) < 0) {
+                       /* Test for 8xx0 */
+                       sd->primary_i2c_slave = OV8xx0_SID;
+                       if (ov51x_set_slave_ids(sd, OV8xx0_SID) < 0)
+                               goto error;
+
+                       if (init_ov_sensor(sd) < 0) {
+                               PDEBUG(D_ERR,
+                                       "Can't determine sensor slave IDs");
+                               goto error;
+                       } else {
+                               if (ov8xx0_configure(sd) < 0) {
+                                       PDEBUG(D_ERR,
+                                          "Failed to configure OV8xx0 sensor");
+                                       goto error;
+                               }
+                       }
+               } else {
+                       if (ov6xx0_configure(sd) < 0) {
+                               PDEBUG(D_ERR, "Failed to configure OV6xx0");
+                               goto error;
+                       }
+               }
+       } else {
+               if (ov7xx0_configure(sd) < 0) {
+                       PDEBUG(D_ERR, "Failed to configure OV7xx0");
+                       goto error;
+               }
+       }
+
+       cam = &gspca_dev->cam;
+       cam->epaddr = OV511_ENDPOINT_ADDRESS;
+       if (sd->maxwidth == 640) {
+               cam->cam_mode = vga_mode;
+               cam->nmodes = sizeof vga_mode / sizeof vga_mode[0];
+       } else {
+               cam->cam_mode = sif_mode;
+               cam->nmodes = sizeof sif_mode / sizeof sif_mode[0];
+       }
+       cam->dev_name = (char *) id->driver_info;
+       sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value;
+       sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value;
+       sd->colors = sd_ctrls[SD_COLOR].qctrl.default_value;
+       return 0;
+error:
+       PDEBUG(D_ERR, "OV519 Config failed");
+       return -EBUSY;
+}
+
+/* this function is called at open time */
+static int sd_open(struct gspca_dev *gspca_dev)
+{
+       return 0;
+}
+
+/* Sets up the OV519 with the given image parameters
+ *
+ * OV519 needs a completely different approach, until we can figure out what
+ * the individual registers do.
+ *
+ * Do not put any sensor-specific code in here (including I2C I/O functions)
+ */
+static int ov519_mode_init_regs(struct sd *sd,
+                               int width, int height)
+{
+       static const struct ov_regvals mode_init_519_ov7670[] = {
+               { 0x5d, 0x03 }, /* Turn off suspend mode */
+               { 0x53, 0x9f }, /* was 9b in 1.65-1.08 */
+               { 0x54, 0x0f }, /* bit2 (jpeg enable) */
+               { 0xa2, 0x20 }, /* a2-a5 are undocumented */
+               { 0xa3, 0x18 },
+               { 0xa4, 0x04 },
+               { 0xa5, 0x28 },
+               { 0x37, 0x00 }, /* SetUsbInit */
+               { 0x55, 0x02 }, /* 4.096 Mhz audio clock */
+               /* Enable both fields, YUV Input, disable defect comp (why?) */
+               { 0x20, 0x0c },
+               { 0x21, 0x38 },
+               { 0x22, 0x1d },
+               { 0x17, 0x50 }, /* undocumented */
+               { 0x37, 0x00 }, /* undocumented */
+               { 0x40, 0xff }, /* I2C timeout counter */
+               { 0x46, 0x00 }, /* I2C clock prescaler */
+               { 0x59, 0x04 }, /* new from windrv 090403 */
+               { 0xff, 0x00 }, /* undocumented */
+               /* windows reads 0x55 at this point, why? */
+       };
+
+       static const struct ov_regvals mode_init_519[] = {
+               { 0x5d, 0x03 }, /* Turn off suspend mode */
+               { 0x53, 0x9f }, /* was 9b in 1.65-1.08 */
+               { 0x54, 0x0f }, /* bit2 (jpeg enable) */
+               { 0xa2, 0x20 }, /* a2-a5 are undocumented */
+               { 0xa3, 0x18 },
+               { 0xa4, 0x04 },
+               { 0xa5, 0x28 },
+               { 0x37, 0x00 }, /* SetUsbInit */
+               { 0x55, 0x02 }, /* 4.096 Mhz audio clock */
+               /* Enable both fields, YUV Input, disable defect comp (why?) */
+               { 0x22, 0x1d },
+               { 0x17, 0x50 }, /* undocumented */
+               { 0x37, 0x00 }, /* undocumented */
+               { 0x40, 0xff }, /* I2C timeout counter */
+               { 0x46, 0x00 }, /* I2C clock prescaler */
+               { 0x59, 0x04 }, /* new from windrv 090403 */
+               { 0xff, 0x00 }, /* undocumented */
+               /* windows reads 0x55 at this point, why? */
+       };
+
+/* int hi_res; */
+
+       PDEBUG(D_CONF, "mode init %dx%d", width, height);
+
+/*     if (width >= 800 && height >= 600)
+               hi_res = 1;
+       else
+               hi_res = 0; */
+
+/*     if (ov51x_stop(sd) < 0)
+               return -EIO; */
+
+       /******** Set the mode ********/
+       if (sd->sensor != SEN_OV7670) {
+               if (write_regvals(sd, mode_init_519,
+                                 ARRAY_SIZE(mode_init_519)))
+                       return -EIO;
+       } else {
+               if (write_regvals(sd, mode_init_519_ov7670,
+                                 ARRAY_SIZE(mode_init_519_ov7670)))
+                       return -EIO;
+       }
+
+       if (sd->sensor == SEN_OV7640) {
+               /* Select 8-bit input mode */
+               reg_w_mask(sd, OV519_CAM_DFR, 0x10, 0x10);
+       }
+
+       reg_w(sd, OV519_CAM_H_SIZE,     width >> 4);
+       reg_w(sd, OV519_CAM_V_SIZE,     height >> 3);
+       reg_w(sd, OV519_CAM_X_OFFSETL,  0x00);
+       reg_w(sd, OV519_CAM_X_OFFSETH,  0x00);
+       reg_w(sd, OV519_CAM_Y_OFFSETL,  0x00);
+       reg_w(sd, OV519_CAM_Y_OFFSETH,  0x00);
+       reg_w(sd, OV519_CAM_DIVIDER,    0x00);
+       reg_w(sd, OV519_CAM_FORMAT,     0x03); /* YUV422 */
+       reg_w(sd, 0x26,                 0x00); /* Undocumented */
+
+       /******** Set the framerate ********/
+       if (frame_rate > 0)
+               sd->frame_rate = frame_rate;
+
+/* FIXME: These are only valid at the max resolution. */
+       sd->clockdiv = 0;
+       if (sd->sensor == SEN_OV7640) {
+               switch (sd->frame_rate) {
+/*jfm: default was 30 fps */
+               case 30:
+                       reg_w(sd, 0xa4, 0x0c);
+                       reg_w(sd, 0x23, 0xff);
+                       break;
+               case 25:
+                       reg_w(sd, 0xa4, 0x0c);
+                       reg_w(sd, 0x23, 0x1f);
+                       break;
+               case 20:
+                       reg_w(sd, 0xa4, 0x0c);
+                       reg_w(sd, 0x23, 0x1b);
+                       break;
+               default:
+/*             case 15: */
+                       reg_w(sd, 0xa4, 0x04);
+                       reg_w(sd, 0x23, 0xff);
+                       sd->clockdiv = 1;
+                       break;
+               case 10:
+                       reg_w(sd, 0xa4, 0x04);
+                       reg_w(sd, 0x23, 0x1f);
+                       sd->clockdiv = 1;
+                       break;
+               case 5:
+                       reg_w(sd, 0xa4, 0x04);
+                       reg_w(sd, 0x23, 0x1b);
+                       sd->clockdiv = 1;
+                       break;
+               }
+       } else if (sd->sensor == SEN_OV8610) {
+               switch (sd->frame_rate) {
+               default:        /* 15 fps */
+/*             case 15: */
+                       reg_w(sd, 0xa4, 0x06);
+                       reg_w(sd, 0x23, 0xff);
+                       break;
+               case 10:
+                       reg_w(sd, 0xa4, 0x06);
+                       reg_w(sd, 0x23, 0x1f);
+                       break;
+               case 5:
+                       reg_w(sd, 0xa4, 0x06);
+                       reg_w(sd, 0x23, 0x1b);
+                       break;
+               }
+               sd->clockdiv = 0;
+       } else if (sd->sensor == SEN_OV7670) { /* guesses, based on 7640 */
+               PDEBUG(D_STREAM, "Setting framerate to %d fps",
+                                (sd->frame_rate == 0) ? 15 : sd->frame_rate);
+               switch (sd->frame_rate) {
+               case 30:
+                       reg_w(sd, 0xa4, 0x10);
+                       reg_w(sd, 0x23, 0xff);
+                       break;
+               case 20:
+                       reg_w(sd, 0xa4, 0x10);
+                       reg_w(sd, 0x23, 0x1b);
+                       break;
+               default: /* 15 fps */
+/*                     case 15: */
+                       reg_w(sd, 0xa4, 0x10);
+                       reg_w(sd, 0x23, 0xff);
+                       sd->clockdiv = 1;
+                       break;
+               }
+       }
+
+/*     if (ov51x_restart(sd) < 0)
+               return -EIO; */
+
+       /* Reset it just for good measure */
+/*     if (ov51x_reset(sd, OV511_RESET_NOREGS) < 0)
+               return -EIO; */
+       return 0;
+}
+
+static int mode_init_ov_sensor_regs(struct sd *sd,
+                               struct ovsensor_window *win)
+{
+       int qvga = win->quarter;
+
+       /******** Mode (VGA/QVGA) and sensor specific regs ********/
+       switch (sd->sensor) {
+       case SEN_OV8610:
+               /* For OV8610 qvga means qsvga */
+               i2c_w_mask(sd, OV7610_REG_COM_C, qvga ? (1 << 5) : 0, 1 << 5);
+               break;
+       case SEN_OV7610:
+               i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20);
+               break;
+       case SEN_OV7620:
+/*             i2c_w(sd, 0x2b, 0x00); */
+               i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20);
+               i2c_w_mask(sd, 0x28, qvga ? 0x00 : 0x20, 0x20);
+               i2c_w(sd, 0x24, qvga ? 0x20 : 0x3a);
+               i2c_w(sd, 0x25, qvga ? 0x30 : 0x60);
+               i2c_w_mask(sd, 0x2d, qvga ? 0x40 : 0x00, 0x40);
+               i2c_w_mask(sd, 0x67, qvga ? 0xf0 : 0x90, 0xf0);
+               i2c_w_mask(sd, 0x74, qvga ? 0x20 : 0x00, 0x20);
+               break;
+       case SEN_OV76BE:
+/*             i2c_w(sd, 0x2b, 0x00); */
+               i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20);
+               break;
+       case SEN_OV7640:
+/*             i2c_w(sd, 0x2b, 0x00); */
+               i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20);
+               i2c_w_mask(sd, 0x28, qvga ? 0x00 : 0x20, 0x20);
+/*             i2c_w(sd, 0x24, qvga ? 0x20 : 0x3a); */
+/*             i2c_w(sd, 0x25, qvga ? 0x30 : 0x60); */
+/*             i2c_w_mask(sd, 0x2d, qvga ? 0x40 : 0x00, 0x40); */
+/*             i2c_w_mask(sd, 0x67, qvga ? 0xf0 : 0x90, 0xf0); */
+/*             i2c_w_mask(sd, 0x74, qvga ? 0x20 : 0x00, 0x20); */
+               break;
+       case SEN_OV7670:
+               /* set COM7_FMT_VGA or COM7_FMT_QVGA
+                * do we need to set anything else?
+                *      HSTART etc are set in set_ov_sensor_window itself */
+               i2c_w_mask(sd, OV7670_REG_COM7,
+                        qvga ? OV7670_COM7_FMT_QVGA : OV7670_COM7_FMT_VGA,
+                        OV7670_COM7_FMT_MASK);
+               break;
+       case SEN_OV6620:
+               i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20);
+               break;
+       case SEN_OV6630:
+               i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /******** Palette-specific regs ********/
+/* Need to do work here for the OV7670 */
+
+               if (sd->sensor == SEN_OV7610 || sd->sensor == SEN_OV76BE) {
+                       /* not valid on the OV6620/OV7620/6630? */
+                       i2c_w_mask(sd, 0x0e, 0x00, 0x40);
+               }
+
+               /* The OV518 needs special treatment. Although both the OV518
+                * and the OV6630 support a 16-bit video bus, only the 8 bit Y
+                * bus is actually used. The UV bus is tied to ground.
+                * Therefore, the OV6630 needs to be in 8-bit multiplexed
+                * output mode */
+
+               /* OV7640 is 8-bit only */
+
+               if (sd->sensor != SEN_OV6630 && sd->sensor != SEN_OV7640)
+                       i2c_w_mask(sd, 0x13, 0x00, 0x20);
+/*     } */
+
+       /******** Clock programming ********/
+       /* The OV6620 needs special handling. This prevents the
+        * severe banding that normally occurs */
+       if (sd->sensor == SEN_OV6620) {
+
+               /* Clock down */
+               i2c_w(sd, 0x2a, 0x04);
+               i2c_w(sd, 0x11, win->clockdiv);
+               i2c_w(sd, 0x2a, 0x84);
+               /* This next setting is critical. It seems to improve
+                * the gain or the contrast. The "reserved" bits seem
+                * to have some effect in this case. */
+               i2c_w(sd, 0x2d, 0x85);
+       } else if (win->clockdiv >= 0) {
+               i2c_w(sd, 0x11, win->clockdiv);
+       }
+
+       /******** Special Features ********/
+/* no evidence this is possible with OV7670, either */
+       /* Test Pattern */
+       if (sd->sensor != SEN_OV7640 && sd->sensor != SEN_OV7670)
+               i2c_w_mask(sd, 0x12, 0x00, 0x02);
+
+       /* Enable auto white balance */
+       if (sd->sensor == SEN_OV7670)
+               i2c_w_mask(sd, OV7670_REG_COM8, OV7670_COM8_AWB,
+                               OV7670_COM8_AWB);
+       else
+               i2c_w_mask(sd, 0x12, 0x04, 0x04);
+
+       /* This will go away as soon as ov51x_mode_init_sensor_regs() */
+       /* is fully tested. */
+       /* 7620/6620/6630? don't have register 0x35, so play it safe */
+       if (sd->sensor == SEN_OV7610 || sd->sensor == SEN_OV76BE) {
+               if (win->width == 640 /*&& win->height == 480*/)
+                       i2c_w(sd, 0x35, 0x9e);
+               else
+                       i2c_w(sd, 0x35, 0x1e);
+       }
+       return 0;
+}
+
+static int set_ov_sensor_window(struct sd *sd,
+                               struct ovsensor_window *win)
+{
+       int hwsbase, hwebase, vwsbase, vwebase, hwscale, vwscale;
+       int ret, hstart, hstop, vstop, vstart;
+       __u8 v;
+
+       /* The different sensor ICs handle setting up of window differently.
+        * IF YOU SET IT WRONG, YOU WILL GET ALL ZERO ISOC DATA FROM OV51x!! */
+       switch (sd->sensor) {
+       case SEN_OV8610:
+               hwsbase = 0x1e;
+               hwebase = 0x1e;
+               vwsbase = 0x02;
+               vwebase = 0x02;
+               break;
+       case SEN_OV7610:
+       case SEN_OV76BE:
+               hwsbase = 0x38;
+               hwebase = 0x3a;
+               vwsbase = vwebase = 0x05;
+               break;
+       case SEN_OV6620:
+       case SEN_OV6630:
+               hwsbase = 0x38;
+               hwebase = 0x3a;
+               vwsbase = 0x05;
+               vwebase = 0x06;
+               break;
+       case SEN_OV7620:
+               hwsbase = 0x2f;         /* From 7620.SET (spec is wrong) */
+               hwebase = 0x2f;
+               vwsbase = vwebase = 0x05;
+               break;
+       case SEN_OV7640:
+               hwsbase = 0x1a;
+               hwebase = 0x1a;
+               vwsbase = vwebase = 0x03;
+               break;
+       case SEN_OV7670:
+               /*handling of OV7670 hardware sensor start and stop values
+                * is very odd, compared to the other OV sensors */
+               vwsbase = vwebase = hwebase = hwsbase = 0x00;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (sd->sensor) {
+       case SEN_OV6620:
+       case SEN_OV6630:
+               if (win->quarter) {     /* QCIF */
+                       hwscale = 0;
+                       vwscale = 0;
+               } else {                /* CIF */
+                       hwscale = 1;
+                       vwscale = 1;    /* The datasheet says 0;
+                                        * it's wrong */
+               }
+               break;
+       case SEN_OV8610:
+               if (win->quarter) {     /* QSVGA */
+                       hwscale = 1;
+                       vwscale = 1;
+               } else {                /* SVGA */
+                       hwscale = 2;
+                       vwscale = 2;
+               }
+               break;
+       default:                        /* SEN_OV7xx0 */
+               if (win->quarter) {     /* QVGA */
+                       hwscale = 1;
+                       vwscale = 0;
+               } else {                /* VGA */
+                       hwscale = 2;
+                       vwscale = 1;
+               }
+       }
+
+       ret = mode_init_ov_sensor_regs(sd, win);
+       if (ret < 0)
+               return ret;
+
+       if (sd->sensor == SEN_OV8610) {
+               i2c_w_mask(sd, 0x2d, 0x05, 0x40);
+                               /* old 0x95, new 0x05 from windrv 090403 */
+                                               /* bits 5-7: reserved */
+               i2c_w_mask(sd, 0x28, 0x20, 0x20);
+                                       /* bit 5: progressive mode on */
+       }
+
+       /* The below is wrong for OV7670s because their window registers
+        * only store the high bits in 0x17 to 0x1a */
+
+       /* SRH Use sd->max values instead of requested win values */
+       /* SCS Since we're sticking with only the max hardware widths
+        * for a given mode */
+       /* I can hard code this for OV7670s */
+       /* Yes, these numbers do look odd, but they're tested and work! */
+       if (sd->sensor == SEN_OV7670) {
+               if (win->quarter) {     /* QVGA from ov7670.c by
+                                        * Jonathan Corbet */
+                       hstart = 164;
+                       hstop = 20;
+                       vstart = 14;
+                       vstop = 494;
+               } else {                /* VGA */
+                       hstart = 158;
+                       hstop = 14;
+                       vstart = 10;
+                       vstop = 490;
+               }
+               /* OV7670 hardware window registers are split across
+                * multiple locations */
+               i2c_w(sd, OV7670_REG_HSTART, (hstart >> 3) & 0xff);
+               i2c_w(sd, OV7670_REG_HSTOP, (hstop >> 3) & 0xff);
+               v = i2c_r(sd, OV7670_REG_HREF);
+               v = (v & 0xc0) | ((hstop & 0x7) << 3) | (hstart & 0x07);
+               msleep(10);     /* need to sleep between read and write to
+                                * same reg! */
+               i2c_w(sd, OV7670_REG_HREF, v);
+
+               i2c_w(sd, OV7670_REG_VSTART, (vstart >> 2) & 0xff);
+               i2c_w(sd, OV7670_REG_VSTOP, (vstop >> 2) & 0xff);
+               v = i2c_r(sd, OV7670_REG_VREF);
+               v = (v & 0xc0) | ((vstop & 0x3) << 2) | (vstart & 0x03);
+               msleep(10);     /* need to sleep between read and write to
+                                * same reg! */
+               i2c_w(sd, OV7670_REG_VREF, v);
+
+       } else {
+               i2c_w(sd, 0x17, hwsbase + (win->x >> hwscale));
+               i2c_w(sd, 0x18, hwebase + ((win->x + win->width) >> hwscale));
+               i2c_w(sd, 0x19, vwsbase + (win->y >> vwscale));
+               i2c_w(sd, 0x1a, vwebase + ((win->y + win->height) >> vwscale));
+       }
+       return 0;
+}
+
+static int ov_sensor_mode_setup(struct sd *sd,
+                               int width, int height)
+{
+       struct ovsensor_window win;
+
+/*     win.format = mode; */
+
+       /* Unless subcapture is enabled,
+        * center the image window and downsample
+        * if possible to increase the field of view */
+       /* NOTE: OV518(+) and OV519 does downsampling on its own */
+       win.width = width;
+       win.height = height;
+       if (width == sd->maxwidth)
+               win.quarter = 0;
+       else
+               win.quarter = 1;
+
+       /* Center it */
+       win.x = (win.width - width) / 2;
+       win.y = (win.height - height) / 2;
+
+       /* Clock is determined by OV519 frame rate code */
+       win.clockdiv = sd->clockdiv;
+
+       PDEBUG(D_CONF, "Setting clock divider to %d", win.clockdiv);
+       return set_ov_sensor_window(sd, &win);
+}
+
+/* -- start the camera -- */
+static void sd_start(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       int ret;
+
+
+       ret = ov519_mode_init_regs(sd, gspca_dev->width, gspca_dev->height);
+       if (ret < 0)
+               goto out;
+       ret = ov_sensor_mode_setup(sd, gspca_dev->width, gspca_dev->height);
+       if (ret < 0)
+               goto out;
+
+       ret = ov51x_restart((struct sd *) gspca_dev);
+       if (ret < 0)
+               goto out;
+       PDEBUG(D_STREAM, "camera started alt: 0x%02x", gspca_dev->alt);
+       ov51x_led_control(sd, 1);
+       return;
+out:
+       PDEBUG(D_ERR, "camera start error:%d", ret);
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+       ov51x_stop((struct sd *) gspca_dev);
+       ov51x_led_control((struct sd *) gspca_dev, 0);
+}
+
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+}
+
+static void sd_close(struct gspca_dev *gspca_dev)
+{
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+                       struct gspca_frame *frame,      /* target */
+                       __u8 *data,                     /* isoc packet */
+                       int len)                        /* iso packet length */
+{
+       /* Header of ov519 is 16 bytes:
+        *     Byte     Value      Description
+        *      0       0xff    magic
+        *      1       0xff    magic
+        *      2       0xff    magic
+        *      3       0xXX    0x50 = SOF, 0x51 = EOF
+        *      9       0xXX    0x01 initial frame without data,
+        *                      0x00 standard frame with image
+        *      14      Lo      in EOF: length of image data / 8
+        *      15      Hi
+        */
+
+       if (data[0] == 0xff && data[1] == 0xff && data[2] == 0xff) {
+               switch (data[3]) {
+               case 0x50:              /* start of frame */
+#define HDRSZ 16
+                       data += HDRSZ;
+                       len -= HDRSZ;
+#undef HDRSZ
+                       if (data[0] == 0xff || data[1] == 0xd8)
+                               gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
+                                               data, len);
+                       else
+                               gspca_dev->last_packet_type = DISCARD_PACKET;
+                       return;
+               case 0x51:              /* end of frame */
+                       if (data[9] != 0)
+                               gspca_dev->last_packet_type = DISCARD_PACKET;
+                       gspca_frame_add(gspca_dev, LAST_PACKET, frame,
+                                       data, 0);
+                       return;
+               }
+       }
+
+       /* intermediate packet */
+       gspca_frame_add(gspca_dev, INTER_PACKET, frame,
+                       data, len);
+}
+
+/* -- management routines -- */
+
+static void setbrightness(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       int val;
+/*     int was_streaming; */
+
+       val = sd->brightness;
+       PDEBUG(D_CONF, "brightness:%d", val);
+/*     was_streaming = gspca_dev->streaming;
+ *     if (was_streaming)
+ *             ov51x_stop(sd); */
+       switch (sd->sensor) {
+       case SEN_OV8610:
+       case SEN_OV7610:
+       case SEN_OV76BE:
+       case SEN_OV6620:
+       case SEN_OV6630:
+       case SEN_OV7640:
+               i2c_w(sd, OV7610_REG_BRT, val);
+               break;
+       case SEN_OV7620:
+               /* 7620 doesn't like manual changes when in auto mode */
+/*fixme
+ *             if (!sd->auto_brt) */
+                       i2c_w(sd, OV7610_REG_BRT, val);
+               break;
+       case SEN_OV7670:
+/*jfm - from windblows
+ *             i2c_w_mask(sd, OV7670_REG_COM8, 0, OV7670_COM8_AEC); */
+               i2c_w(sd, OV7670_REG_BRIGHT, ov7670_abs_to_sm(val));
+               break;
+       }
+/*     if (was_streaming)
+ *             ov51x_restart(sd); */
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       int val;
+/*     int was_streaming; */
+
+       val = sd->contrast;
+       PDEBUG(D_CONF, "contrast:%d", val);
+/*     was_streaming = gspca_dev->streaming;
+       if (was_streaming)
+               ov51x_stop(sd); */
+       switch (sd->sensor) {
+       case SEN_OV7610:
+       case SEN_OV6620:
+               i2c_w(sd, OV7610_REG_CNT, val);
+               break;
+       case SEN_OV6630:
+               i2c_w_mask(sd, OV7610_REG_CNT, val >> 4, 0x0f);
+       case SEN_OV8610: {
+               static const __u8 ctab[] = {
+                       0x03, 0x09, 0x0b, 0x0f, 0x53, 0x6f, 0x35, 0x7f
+               };
+
+               /* Use Y gamma control instead. Bit 0 enables it. */
+               i2c_w(sd, 0x64, ctab[val >> 5]);
+               break;
+           }
+       case SEN_OV7620: {
+               static const __u8 ctab[] = {
+                       0x01, 0x05, 0x09, 0x11, 0x15, 0x35, 0x37, 0x57,
+                       0x5b, 0xa5, 0xa7, 0xc7, 0xc9, 0xcf, 0xef, 0xff
+               };
+
+               /* Use Y gamma control instead. Bit 0 enables it. */
+               i2c_w(sd, 0x64, ctab[val >> 4]);
+               break;
+           }
+       case SEN_OV7640:
+               /* Use gain control instead. */
+               i2c_w(sd, OV7610_REG_GAIN, val >> 2);
+               break;
+       case SEN_OV7670:
+               /* check that this isn't just the same as ov7610 */
+               i2c_w(sd, OV7670_REG_CONTRAS, val >> 1);
+               break;
+       }
+/*     if (was_streaming)
+               ov51x_restart(sd); */
+}
+
+static void setcolors(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       int val;
+/*     int was_streaming; */
+
+       val = sd->colors;
+       PDEBUG(D_CONF, "saturation:%d", val);
+/*     was_streaming = gspca_dev->streaming;
+       if (was_streaming)
+               ov51x_stop(sd); */
+       switch (sd->sensor) {
+       case SEN_OV8610:
+       case SEN_OV7610:
+       case SEN_OV76BE:
+       case SEN_OV6620:
+       case SEN_OV6630:
+               i2c_w(sd, OV7610_REG_SAT, val);
+               break;
+       case SEN_OV7620:
+               /* Use UV gamma control instead. Bits 0 & 7 are reserved. */
+/*             rc = ov_i2c_write(sd->dev, 0x62, (val >> 9) & 0x7e);
+               if (rc < 0)
+                       goto out; */
+               i2c_w(sd, OV7610_REG_SAT, val);
+               break;
+       case SEN_OV7640:
+               i2c_w(sd, OV7610_REG_SAT, val & 0xf0);
+               break;
+       case SEN_OV7670:
+               /* supported later once I work out how to do it
+                * transparently fail now! */
+               /* set REG_COM13 values for UV sat auto mode */
+               break;
+       }
+/*     if (was_streaming)
+               ov51x_restart(sd); */
+}
+
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->brightness = val;
+       setbrightness(gspca_dev);
+       return 0;
+}
+
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->brightness;
+       return 0;
+}
+
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->contrast = val;
+       setcontrast(gspca_dev);
+       return 0;
+}
+
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->contrast;
+       return 0;
+}
+
+static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->colors = val;
+       setcolors(gspca_dev);
+       return 0;
+}
+
+static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->colors;
+       return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+       .name = MODULE_NAME,
+       .ctrls = sd_ctrls,
+       .nctrls = ARRAY_SIZE(sd_ctrls),
+       .config = sd_config,
+       .open = sd_open,
+       .start = sd_start,
+       .stopN = sd_stopN,
+       .stop0 = sd_stop0,
+       .close = sd_close,
+       .pkt_scan = sd_pkt_scan,
+};
+
+/* -- module initialisation -- */
+#define DVNM(name) .driver_info = (kernel_ulong_t) name
+static const __devinitdata struct usb_device_id device_table[] = {
+       {USB_DEVICE(0x041e, 0x4052), DVNM("Creative Live! VISTA IM")},
+       {USB_DEVICE(0x041e, 0x405f), DVNM("Creative Live! VISTA VF0330")},
+       {USB_DEVICE(0x041e, 0x4060), DVNM("Creative Live! VISTA VF0350")},
+       {USB_DEVICE(0x041e, 0x4061), DVNM("Creative Live! VISTA VF0400")},
+       {USB_DEVICE(0x041e, 0x4064), DVNM("Creative Live! VISTA VF0420")},
+       {USB_DEVICE(0x041e, 0x4068), DVNM("Creative Live! VISTA VF0470")},
+       {USB_DEVICE(0x045e, 0x028c), DVNM("Microsoft xbox cam")},
+       {USB_DEVICE(0x054c, 0x0154), DVNM("Sonny toy4")},
+       {USB_DEVICE(0x054c, 0x0155), DVNM("Sonny toy5")},
+       {USB_DEVICE(0x05a9, 0x0519), DVNM("OmniVision")},
+       {USB_DEVICE(0x05a9, 0x0530), DVNM("OmniVision")},
+       {USB_DEVICE(0x05a9, 0x4519), DVNM("OmniVision")},
+       {USB_DEVICE(0x05a9, 0x8519), DVNM("OmniVision")},
+       {}
+};
+#undef DVNAME
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+                       const struct usb_device_id *id)
+{
+       return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+                               THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+       .name = MODULE_NAME,
+       .id_table = device_table,
+       .probe = sd_probe,
+       .disconnect = gspca_disconnect,
+};
+
+/* -- module insert / remove -- */
+static int __init sd_mod_init(void)
+{
+       if (usb_register(&sd_driver) < 0)
+               return -1;
+       PDEBUG(D_PROBE, "v%s registered", version);
+       return 0;
+}
+static void __exit sd_mod_exit(void)
+{
+       usb_deregister(&sd_driver);
+       PDEBUG(D_PROBE, "deregistered");
+}
+
+module_init(sd_mod_init);
+module_exit(sd_mod_exit);
+
+module_param(frame_rate, int, 0644);
+MODULE_PARM_DESC(frame_rate, "Frame rate (5, 10, 15, 20 or 30 fps)");
+
diff --git a/drivers/media/video/gspca/pac207.c b/drivers/media/video/gspca/pac207.c
new file mode 100644 (file)
index 0000000..fa7abc4
--- /dev/null
@@ -0,0 +1,622 @@
+/*
+ * Pixart PAC207BCA library
+ *
+ * Copyright (C) 2008 Hans de Goede <j.w.r.degoede@hhs.nl>
+ * Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li
+ * Copyleft (C) 2005 Michel Xhaard mxhaard@magic.fr
+ *
+ * V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
+ *
+ * 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
+ *
+ */
+
+#define MODULE_NAME "pac207"
+
+#include "gspca.h"
+
+#define DRIVER_VERSION_NUMBER  KERNEL_VERSION(2, 1, 7)
+static const char version[] = "2.1.7";
+
+MODULE_AUTHOR("Hans de Goede <j.w.r.degoede@hhs.nl>");
+MODULE_DESCRIPTION("Pixart PAC207");
+MODULE_LICENSE("GPL");
+
+#define PAC207_CTRL_TIMEOUT            100  /* ms */
+
+#define PAC207_BRIGHTNESS_MIN          0
+#define PAC207_BRIGHTNESS_MAX          255
+#define PAC207_BRIGHTNESS_DEFAULT      4 /* power on default: 4 */
+
+/* An exposure value of 4 also works (3 does not) but then we need to lower
+   the compression balance setting when in 352x288 mode, otherwise the usb
+   bandwidth is not enough and packets get dropped resulting in corrupt
+   frames. The problem with this is that when the compression balance gets
+   lowered below 0x80, the pac207 starts using a different compression
+   algorithm for some lines, these lines get prefixed with a 0x2dd2 prefix
+   and currently we do not know how to decompress these lines, so for now
+   we use a minimum exposure value of 5 */
+#define PAC207_EXPOSURE_MIN            5
+#define PAC207_EXPOSURE_MAX            26
+#define PAC207_EXPOSURE_DEFAULT                5 /* power on default: 3 ?? */
+#define PAC207_EXPOSURE_KNEE           11 /* 4 = 30 fps, 11 = 8, 15 = 6 */
+
+#define PAC207_GAIN_MIN                        0
+#define PAC207_GAIN_MAX                        31
+#define PAC207_GAIN_DEFAULT            9 /* power on default: 9 */
+#define PAC207_GAIN_KNEE               20
+
+#define PAC207_AUTOGAIN_DEADZONE       30
+/* We calculating the autogain at the end of the transfer of a frame, at this
+   moment a frame with the old settings is being transmitted, and a frame is
+   being captured with the old settings. So if we adjust the autogain we must
+   ignore atleast the 2 next frames for the new settings to come into effect
+   before doing any other adjustments */
+#define PAC207_AUTOGAIN_IGNORE_FRAMES  3
+
+/* specific webcam descriptor */
+struct sd {
+       struct gspca_dev gspca_dev;             /* !! must be the first item */
+
+       u8 mode;
+
+       u8 brightness;
+       u8 exposure;
+       u8 autogain;
+       u8 gain;
+
+       u8 sof_read;
+       u8 header_read;
+       u8 autogain_ignore_frames;
+
+       atomic_t avg_lum;
+};
+
+/* V4L2 controls supported by the driver */
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val);
+
+static struct ctrl sd_ctrls[] = {
+#define SD_BRIGHTNESS 0
+       {
+           {
+               .id      = V4L2_CID_BRIGHTNESS,
+               .type    = V4L2_CTRL_TYPE_INTEGER,
+               .name    = "Brightness",
+               .minimum = PAC207_BRIGHTNESS_MIN,
+               .maximum = PAC207_BRIGHTNESS_MAX,
+               .step = 1,
+               .default_value = PAC207_BRIGHTNESS_DEFAULT,
+               .flags = 0,
+           },
+           .set = sd_setbrightness,
+           .get = sd_getbrightness,
+       },
+#define SD_EXPOSURE 1
+       {
+           {
+               .id = V4L2_CID_EXPOSURE,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "exposure",
+               .minimum = PAC207_EXPOSURE_MIN,
+               .maximum = PAC207_EXPOSURE_MAX,
+               .step = 1,
+               .default_value = PAC207_EXPOSURE_DEFAULT,
+               .flags = 0,
+           },
+           .set = sd_setexposure,
+           .get = sd_getexposure,
+       },
+#define SD_AUTOGAIN 2
+       {
+           {
+               .id       = V4L2_CID_AUTOGAIN,
+               .type   = V4L2_CTRL_TYPE_BOOLEAN,
+               .name   = "Auto Gain",
+               .minimum = 0,
+               .maximum = 1,
+               .step   = 1,
+               .default_value = 1,
+               .flags = 0,
+           },
+           .set = sd_setautogain,
+           .get = sd_getautogain,
+       },
+#define SD_GAIN 3
+       {
+           {
+               .id = V4L2_CID_GAIN,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "gain",
+               .minimum = PAC207_GAIN_MIN,
+               .maximum = PAC207_GAIN_MAX,
+               .step = 1,
+               .default_value = PAC207_GAIN_DEFAULT,
+               .flags = 0,
+           },
+           .set = sd_setgain,
+           .get = sd_getgain,
+       },
+};
+
+static struct v4l2_pix_format sif_mode[] = {
+       {176, 144, V4L2_PIX_FMT_PAC207, V4L2_FIELD_NONE,
+               .bytesperline = 176,
+               .sizeimage = (176 + 2) * 144,
+                       /* uncompressed, add 2 bytes / line for line header */
+               .colorspace = V4L2_COLORSPACE_SRGB,
+               .priv = 1},
+       {352, 288, V4L2_PIX_FMT_PAC207, V4L2_FIELD_NONE,
+               .bytesperline = 352,
+                       /* compressed, but only when needed (not compressed
+                          when the framerate is low) */
+               .sizeimage = (352 + 2) * 288,
+               .colorspace = V4L2_COLORSPACE_SRGB,
+               .priv = 0},
+};
+
+static const __u8 pac207_sensor_init[][8] = {
+       {0x10, 0x12, 0x0d, 0x12, 0x0c, 0x01, 0x29, 0xf0},
+       {0x00, 0x64, 0x64, 0x64, 0x04, 0x10, 0xf0, 0x30},
+       {0x00, 0x00, 0x00, 0x70, 0xa0, 0xf8, 0x00, 0x00},
+       {0x00, 0x00, 0x32, 0x00, 0x96, 0x00, 0xa2, 0x02},
+       {0x32, 0x00, 0x96, 0x00, 0xA2, 0x02, 0xaf, 0x00},
+};
+
+                       /* 48 reg_72 Rate Control end BalSize_4a =0x36 */
+static const __u8 PacReg72[] = { 0x00, 0x00, 0x36, 0x00 };
+
+static const unsigned char pac207_sof_marker[5] =
+               { 0xff, 0xff, 0x00, 0xff, 0x96 };
+
+static int pac207_write_regs(struct gspca_dev *gspca_dev, u16 index,
+       const u8 *buffer, u16 length)
+{
+       struct usb_device *udev = gspca_dev->dev;
+       int err;
+
+       memcpy(gspca_dev->usb_buf, buffer, length);
+
+       err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x01,
+                       USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+                       0x00, index,
+                       gspca_dev->usb_buf, length, PAC207_CTRL_TIMEOUT);
+       if (err < 0)
+               PDEBUG(D_ERR,
+                       "Failed to write registers to index 0x%04X, error %d)",
+                       index, err);
+
+       return err;
+}
+
+
+int pac207_write_reg(struct gspca_dev *gspca_dev, u16 index, u16 value)
+{
+       struct usb_device *udev = gspca_dev->dev;
+       int err;
+
+       err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00,
+                       USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+                       value, index, NULL, 0, PAC207_CTRL_TIMEOUT);
+       if (err)
+               PDEBUG(D_ERR, "Failed to write a register (index 0x%04X,"
+                       " value 0x%02X, error %d)", index, value, err);
+
+       return err;
+}
+
+
+int pac207_read_reg(struct gspca_dev *gspca_dev, u16 index)
+{
+       struct usb_device *udev = gspca_dev->dev;
+       int res;
+
+       res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00,
+                       USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+                       0x00, index,
+                       gspca_dev->usb_buf, 1, PAC207_CTRL_TIMEOUT);
+       if (res < 0) {
+               PDEBUG(D_ERR,
+                       "Failed to read a register (index 0x%04X, error %d)",
+                       index, res);
+               return res;
+       }
+
+       return gspca_dev->usb_buf[0];
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+                       const struct usb_device_id *id)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       struct cam *cam;
+       u8 idreg[2];
+
+       idreg[0] = pac207_read_reg(gspca_dev, 0x0000);
+       idreg[1] = pac207_read_reg(gspca_dev, 0x0001);
+       idreg[0] = ((idreg[0] & 0x0F) << 4) | ((idreg[1] & 0xf0) >> 4);
+       idreg[1] = idreg[1] & 0x0f;
+       PDEBUG(D_PROBE, "Pixart Sensor ID 0x%02X Chips ID 0x%02X",
+               idreg[0], idreg[1]);
+
+       if (idreg[0] != 0x27) {
+               PDEBUG(D_PROBE, "Error invalid sensor ID!");
+               return -ENODEV;
+       }
+
+       pac207_write_reg(gspca_dev, 0x41, 0x00);
+                               /* Bit_0=Image Format,
+                                * Bit_1=LED,
+                                * Bit_2=Compression test mode enable */
+       pac207_write_reg(gspca_dev, 0x0f, 0x00); /* Power Control */
+       pac207_write_reg(gspca_dev, 0x11, 0x30); /* Analog Bias */
+
+       PDEBUG(D_PROBE,
+               "Pixart PAC207BCA Image Processor and Control Chip detected"
+               " (vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct);
+
+       cam = &gspca_dev->cam;
+       cam->dev_name = (char *) id->driver_info;
+       cam->epaddr = 0x05;
+       cam->cam_mode = sif_mode;
+       cam->nmodes = ARRAY_SIZE(sif_mode);
+       sd->brightness = PAC207_BRIGHTNESS_DEFAULT;
+       sd->exposure = PAC207_EXPOSURE_DEFAULT;
+       sd->gain = PAC207_GAIN_DEFAULT;
+
+       return 0;
+}
+
+/* this function is called at open time */
+static int sd_open(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->autogain = 1;
+       return 0;
+}
+
+/* -- start the camera -- */
+static void sd_start(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       __u8 mode;
+
+       pac207_write_reg(gspca_dev, 0x0f, 0x10); /* Power control (Bit 6-0) */
+       pac207_write_regs(gspca_dev, 0x0002, pac207_sensor_init[0], 8);
+       pac207_write_regs(gspca_dev, 0x000a, pac207_sensor_init[1], 8);
+       pac207_write_regs(gspca_dev, 0x0012, pac207_sensor_init[2], 8);
+       pac207_write_regs(gspca_dev, 0x0040, pac207_sensor_init[3], 8);
+       pac207_write_regs(gspca_dev, 0x0042, pac207_sensor_init[4], 8);
+       pac207_write_regs(gspca_dev, 0x0048, PacReg72, 4);
+
+       /* Compression Balance */
+       if (gspca_dev->width == 176)
+               pac207_write_reg(gspca_dev, 0x4a, 0xff);
+       else
+               pac207_write_reg(gspca_dev, 0x4a, 0x88);
+       pac207_write_reg(gspca_dev, 0x4b, 0x00); /* Sram test value */
+       pac207_write_reg(gspca_dev, 0x08, sd->brightness);
+
+       /* PGA global gain (Bit 4-0) */
+       pac207_write_reg(gspca_dev, 0x0e, sd->gain);
+       pac207_write_reg(gspca_dev, 0x02, sd->exposure); /* PXCK = 12MHz /n */
+
+       mode = 0x02; /* Image Format (Bit 0), LED (1), Compr. test mode (2) */
+       if (gspca_dev->width == 176) {  /* 176x144 */
+               mode |= 0x01;
+               PDEBUG(D_STREAM, "pac207_start mode 176x144");
+       } else {                                /* 352x288 */
+               PDEBUG(D_STREAM, "pac207_start mode 352x288");
+       }
+       pac207_write_reg(gspca_dev, 0x41, mode);
+
+       pac207_write_reg(gspca_dev, 0x13, 0x01); /* Bit 0, auto clear */
+       pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */
+       msleep(10);
+       pac207_write_reg(gspca_dev, 0x40, 0x01); /* Start ISO pipe */
+
+       sd->sof_read = 0;
+       sd->autogain_ignore_frames = 0;
+       atomic_set(&sd->avg_lum, -1);
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+       pac207_write_reg(gspca_dev, 0x40, 0x00); /* Stop ISO pipe */
+       pac207_write_reg(gspca_dev, 0x41, 0x00); /* Turn of LED */
+       pac207_write_reg(gspca_dev, 0x0f, 0x00); /* Power Control */
+}
+
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+}
+
+/* this function is called at close time */
+static void sd_close(struct gspca_dev *gspca_dev)
+{
+}
+
+static void pac207_do_auto_gain(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       int avg_lum = atomic_read(&sd->avg_lum);
+
+       if (avg_lum == -1)
+               return;
+
+       if (sd->autogain_ignore_frames > 0)
+               sd->autogain_ignore_frames--;
+       else if (gspca_auto_gain_n_exposure(gspca_dev, avg_lum,
+                       100 + sd->brightness / 2, PAC207_AUTOGAIN_DEADZONE,
+                       PAC207_GAIN_KNEE, PAC207_EXPOSURE_KNEE))
+               sd->autogain_ignore_frames = PAC207_AUTOGAIN_IGNORE_FRAMES;
+}
+
+static unsigned char *pac207_find_sof(struct gspca_dev *gspca_dev,
+                                       unsigned char *m, int len)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       int i;
+
+       /* Search for the SOF marker (fixed part) in the header */
+       for (i = 0; i < len; i++) {
+               if (m[i] == pac207_sof_marker[sd->sof_read]) {
+                       sd->sof_read++;
+                       if (sd->sof_read == sizeof(pac207_sof_marker)) {
+                               PDEBUG(D_STREAM,
+                                       "SOF found, bytes to analyze: %u."
+                                       " Frame starts at byte #%u",
+                                       len, i + 1);
+                               sd->sof_read = 0;
+                               return m + i + 1;
+                       }
+               } else {
+                       sd->sof_read = 0;
+               }
+       }
+
+       return NULL;
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+                       struct gspca_frame *frame,
+                       __u8 *data,
+                       int len)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       unsigned char *sof;
+
+       sof = pac207_find_sof(gspca_dev, data, len);
+       if (sof) {
+               int n;
+
+               /* finish decoding current frame */
+               n = sof - data;
+               if (n > sizeof pac207_sof_marker)
+                       n -= sizeof pac207_sof_marker;
+               else
+                       n = 0;
+               frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
+                                       data, n);
+               sd->header_read = 0;
+               gspca_frame_add(gspca_dev, FIRST_PACKET, frame, NULL, 0);
+               len -= sof - data;
+               data = sof;
+       }
+       if (sd->header_read < 11) {
+               int needed;
+
+               /* get average lumination from frame header (byte 5) */
+               if (sd->header_read < 5) {
+                       needed = 5 - sd->header_read;
+                       if (len >= needed)
+                               atomic_set(&sd->avg_lum, data[needed - 1]);
+               }
+               /* skip the rest of the header */
+               needed = 11 - sd->header_read;
+               if (len <= needed) {
+                       sd->header_read += len;
+                       return;
+               }
+               data += needed;
+               len -= needed;
+               sd->header_read = 11;
+       }
+
+       gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       pac207_write_reg(gspca_dev, 0x08, sd->brightness);
+       pac207_write_reg(gspca_dev, 0x13, 0x01);        /* Bit 0, auto clear */
+       pac207_write_reg(gspca_dev, 0x1c, 0x01);        /* not documented */
+}
+
+static void setexposure(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       pac207_write_reg(gspca_dev, 0x02, sd->exposure);
+       pac207_write_reg(gspca_dev, 0x13, 0x01);        /* Bit 0, auto clear */
+       pac207_write_reg(gspca_dev, 0x1c, 0x01);        /* not documented */
+}
+
+static void setgain(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       pac207_write_reg(gspca_dev, 0x0e, sd->gain);
+       pac207_write_reg(gspca_dev, 0x13, 0x01);        /* Bit 0, auto clear */
+       pac207_write_reg(gspca_dev, 0x1c, 0x01);        /* not documented */
+}
+
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->brightness = val;
+       if (gspca_dev->streaming)
+               setbrightness(gspca_dev);
+       return 0;
+}
+
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->brightness;
+       return 0;
+}
+
+static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->exposure = val;
+       if (gspca_dev->streaming)
+               setexposure(gspca_dev);
+       return 0;
+}
+
+static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->exposure;
+       return 0;
+}
+
+static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->gain = val;
+       if (gspca_dev->streaming)
+               setgain(gspca_dev);
+       return 0;
+}
+
+static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->gain;
+       return 0;
+}
+
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->autogain = val;
+       /* when switching to autogain set defaults to make sure
+          we are on a valid point of the autogain gain /
+          exposure knee graph, and give this change time to
+          take effect before doing autogain. */
+       if (sd->autogain) {
+               sd->exposure = PAC207_EXPOSURE_DEFAULT;
+               sd->gain = PAC207_GAIN_DEFAULT;
+               if (gspca_dev->streaming) {
+                       sd->autogain_ignore_frames =
+                               PAC207_AUTOGAIN_IGNORE_FRAMES;
+                       setexposure(gspca_dev);
+                       setgain(gspca_dev);
+               }
+       }
+
+       return 0;
+}
+
+static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->autogain;
+       return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+       .name = MODULE_NAME,
+       .ctrls = sd_ctrls,
+       .nctrls = ARRAY_SIZE(sd_ctrls),
+       .config = sd_config,
+       .open = sd_open,
+       .start = sd_start,
+       .stopN = sd_stopN,
+       .stop0 = sd_stop0,
+       .close = sd_close,
+       .dq_callback = pac207_do_auto_gain,
+       .pkt_scan = sd_pkt_scan,
+};
+
+/* -- module initialisation -- */
+#define DVNM(name) .driver_info = (kernel_ulong_t) name
+static const __devinitdata struct usb_device_id device_table[] = {
+       {USB_DEVICE(0x041e, 0x4028), DVNM("Creative Webcam Vista Plus")},
+       {USB_DEVICE(0x093a, 0x2460), DVNM("Q-Tec Webcam 100")},
+       {USB_DEVICE(0x093a, 0x2463), DVNM("Philips spc200nc pac207")},
+       {USB_DEVICE(0x093a, 0x2464), DVNM("Labtec Webcam 1200")},
+       {USB_DEVICE(0x093a, 0x2468), DVNM("PAC207")},
+       {USB_DEVICE(0x093a, 0x2470), DVNM("Genius GF112")},
+       {USB_DEVICE(0x093a, 0x2471), DVNM("Genius VideoCam GE111")},
+       {USB_DEVICE(0x093a, 0x2472), DVNM("Genius VideoCam GE110")},
+       {USB_DEVICE(0x2001, 0xf115), DVNM("D-Link DSB-C120")},
+       {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+                       const struct usb_device_id *id)
+{
+       return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+                               THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+       .name = MODULE_NAME,
+       .id_table = device_table,
+       .probe = sd_probe,
+       .disconnect = gspca_disconnect,
+};
+
+/* -- module insert / remove -- */
+static int __init sd_mod_init(void)
+{
+       if (usb_register(&sd_driver) < 0)
+               return -1;
+       PDEBUG(D_PROBE, "v%s registered", version);
+       return 0;
+}
+static void __exit sd_mod_exit(void)
+{
+       usb_deregister(&sd_driver);
+       PDEBUG(D_PROBE, "deregistered");
+}
+
+module_init(sd_mod_init);
+module_exit(sd_mod_exit);
diff --git a/drivers/media/video/gspca/pac7311.c b/drivers/media/video/gspca/pac7311.c
new file mode 100644 (file)
index 0000000..5c052e3
--- /dev/null
@@ -0,0 +1,760 @@
+/*
+ *             Pixart PAC7311 library
+ *             Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li
+ *
+ * V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
+ *
+ * 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
+ * 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
+ */
+
+#define MODULE_NAME "pac7311"
+
+#include "gspca.h"
+
+#define DRIVER_VERSION_NUMBER  KERNEL_VERSION(2, 1, 7)
+static const char version[] = "2.1.7";
+
+MODULE_AUTHOR("Thomas Kaiser thomas@kaiser-linux.li");
+MODULE_DESCRIPTION("Pixart PAC7311");
+MODULE_LICENSE("GPL");
+
+/* specific webcam descriptor */
+struct sd {
+       struct gspca_dev gspca_dev;             /* !! must be the first item */
+
+       int avg_lum;
+
+       unsigned char brightness;
+       unsigned char contrast;
+       unsigned char colors;
+       unsigned char autogain;
+
+       char ffseq;
+       signed char ag_cnt;
+#define AG_CNT_START 13
+};
+
+/* V4L2 controls supported by the driver */
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
+
+static struct ctrl sd_ctrls[] = {
+       {
+           {
+               .id      = V4L2_CID_BRIGHTNESS,
+               .type    = V4L2_CTRL_TYPE_INTEGER,
+               .name    = "Brightness",
+               .minimum = 0,
+#define BRIGHTNESS_MAX 0x20
+               .maximum = BRIGHTNESS_MAX,
+               .step    = 1,
+#define BRIGHTNESS_DEF 0x10
+               .default_value = BRIGHTNESS_DEF,
+           },
+           .set = sd_setbrightness,
+           .get = sd_getbrightness,
+       },
+       {
+           {
+               .id      = V4L2_CID_CONTRAST,
+               .type    = V4L2_CTRL_TYPE_INTEGER,
+               .name    = "Contrast",
+               .minimum = 0,
+               .maximum = 255,
+               .step    = 1,
+#define CONTRAST_DEF 127
+               .default_value = CONTRAST_DEF,
+           },
+           .set = sd_setcontrast,
+           .get = sd_getcontrast,
+       },
+       {
+           {
+               .id      = V4L2_CID_SATURATION,
+               .type    = V4L2_CTRL_TYPE_INTEGER,
+               .name    = "Color",
+               .minimum = 0,
+               .maximum = 255,
+               .step    = 1,
+#define COLOR_DEF 127
+               .default_value = COLOR_DEF,
+           },
+           .set = sd_setcolors,
+           .get = sd_getcolors,
+       },
+       {
+           {
+               .id      = V4L2_CID_AUTOGAIN,
+               .type    = V4L2_CTRL_TYPE_BOOLEAN,
+               .name    = "Auto Gain",
+               .minimum = 0,
+               .maximum = 1,
+               .step    = 1,
+#define AUTOGAIN_DEF 1
+               .default_value = AUTOGAIN_DEF,
+           },
+           .set = sd_setautogain,
+           .get = sd_getautogain,
+       },
+};
+
+static struct v4l2_pix_format vga_mode[] = {
+       {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+               .bytesperline = 160,
+               .sizeimage = 160 * 120 * 3 / 8 + 590,
+               .colorspace = V4L2_COLORSPACE_JPEG,
+               .priv = 2},
+       {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+               .bytesperline = 320,
+               .sizeimage = 320 * 240 * 3 / 8 + 590,
+               .colorspace = V4L2_COLORSPACE_JPEG,
+               .priv = 1},
+       {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+               .bytesperline = 640,
+               .sizeimage = 640 * 480 * 3 / 8 + 590,
+               .colorspace = V4L2_COLORSPACE_JPEG,
+               .priv = 0},
+};
+
+#define PAC7311_JPEG_HEADER_SIZE (sizeof pac7311_jpeg_header)  /* (594) */
+
+static const __u8 pac7311_jpeg_header[] = {
+       0xff, 0xd8,
+       0xff, 0xe0, 0x00, 0x03, 0x20,
+       0xff, 0xc0, 0x00, 0x11, 0x08,
+               0x01, 0xe0,                     /* 12: height */
+               0x02, 0x80,                     /* 14: width */
+               0x03,                           /* 16 */
+                       0x01, 0x21, 0x00,
+                       0x02, 0x11, 0x01,
+                       0x03, 0x11, 0x01,
+       0xff, 0xdb, 0x00, 0x84,
+       0x00, 0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e, 0x0d,
+       0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28, 0x1a, 0x18, 0x16,
+       0x16, 0x18, 0x31, 0x23, 0x25, 0x1d, 0x28, 0x3a, 0x33, 0x3d,
+       0x3c, 0x39, 0x33, 0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40,
+       0x44, 0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57, 0x5f,
+       0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71, 0x79, 0x70, 0x64,
+       0x78, 0x5c, 0x65, 0x67, 0x63, 0x01, 0x11, 0x12, 0x12, 0x18,
+       0x15, 0x18, 0x2f, 0x1a, 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42,
+       0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+       0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+       0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+       0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+       0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+       0xff, 0xc4, 0x01, 0xa2, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01,
+       0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+       0x09, 0x0a, 0x0b, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02,
+       0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d,
+       0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31,
+       0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32,
+       0x81, 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52,
+       0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,
+       0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a,
+       0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45,
+       0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57,
+       0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
+       0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83,
+       0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94,
+       0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5,
+       0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
+       0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
+       0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8,
+       0xd9, 0xda, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8,
+       0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
+       0xf9, 0xfa, 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01,
+       0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
+       0x0b, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04,
+       0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01,
+       0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41,
+       0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14,
+       0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0,
+       0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25,
+       0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a,
+       0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46,
+       0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
+       0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a,
+       0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83,
+       0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94,
+       0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5,
+       0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
+       0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
+       0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8,
+       0xd9, 0xda, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
+       0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa,
+       0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03,
+       0x11, 0x00, 0x3f, 0x00
+};
+
+static void reg_w_buf(struct gspca_dev *gspca_dev,
+                 __u16 index,
+                 const char *buffer, __u16 len)
+{
+       memcpy(gspca_dev->usb_buf, buffer, len);
+       usb_control_msg(gspca_dev->dev,
+                       usb_sndctrlpipe(gspca_dev->dev, 0),
+                       1,              /* request */
+                       USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                       0,              /* value */
+                       index, gspca_dev->usb_buf, len,
+                       500);
+}
+
+static __u8 reg_r(struct gspca_dev *gspca_dev,
+                            __u16 index)
+{
+       usb_control_msg(gspca_dev->dev,
+                       usb_rcvctrlpipe(gspca_dev->dev, 0),
+                       0,                      /* request */
+                       USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                       0,                      /* value */
+                       index, gspca_dev->usb_buf, 1,
+                       500);
+       return gspca_dev->usb_buf[0];
+}
+
+static void reg_w(struct gspca_dev *gspca_dev,
+                 __u16 index,
+                 __u8 value)
+{
+       gspca_dev->usb_buf[0] = value;
+       usb_control_msg(gspca_dev->dev,
+                       usb_sndctrlpipe(gspca_dev->dev, 0),
+                       0,                      /* request */
+                       USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                       value, index, gspca_dev->usb_buf, 1,
+                       500);
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+                       const struct usb_device_id *id)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       struct cam *cam;
+
+       PDEBUG(D_CONF, "Find Sensor PAC7311");
+       reg_w(gspca_dev, 0x78, 0x40); /* Bit_0=start stream, Bit_7=LED */
+       reg_w(gspca_dev, 0x78, 0x40); /* Bit_0=start stream, Bit_7=LED */
+       reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_7=LED */
+       reg_w(gspca_dev, 0xff, 0x04);
+       reg_w(gspca_dev, 0x27, 0x80);
+       reg_w(gspca_dev, 0x28, 0xca);
+       reg_w(gspca_dev, 0x29, 0x53);
+       reg_w(gspca_dev, 0x2a, 0x0e);
+       reg_w(gspca_dev, 0xff, 0x01);
+       reg_w(gspca_dev, 0x3e, 0x20);
+
+       cam = &gspca_dev->cam;
+       cam->dev_name = (char *) id->driver_info;
+       cam->epaddr = 0x05;
+       cam->cam_mode = vga_mode;
+       cam->nmodes = ARRAY_SIZE(vga_mode);
+
+       sd->brightness = BRIGHTNESS_DEF;
+       sd->contrast = CONTRAST_DEF;
+       sd->colors = COLOR_DEF;
+       sd->autogain = AUTOGAIN_DEF;
+       return 0;
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       int brightness;
+
+/*jfm: inverted?*/
+       brightness = BRIGHTNESS_MAX - sd->brightness;
+       reg_w(gspca_dev, 0xff, 0x04);
+/*     reg_w(gspca_dev, 0x0e, 0x00); */
+       reg_w(gspca_dev, 0x0f, brightness);
+       /* load registers to sensor (Bit 0, auto clear) */
+       reg_w(gspca_dev, 0x11, 0x01);
+       PDEBUG(D_CONF|D_STREAM, "brightness: %i", brightness);
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       reg_w(gspca_dev, 0xff, 0x01);
+       reg_w(gspca_dev, 0x80, sd->contrast);
+       /* load registers to sensor (Bit 0, auto clear) */
+       reg_w(gspca_dev, 0x11, 0x01);
+       PDEBUG(D_CONF|D_STREAM, "contrast: %i", sd->contrast);
+}
+
+static void setcolors(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       reg_w(gspca_dev, 0xff, 0x01);
+       reg_w(gspca_dev, 0x10, sd->colors);
+       /* load registers to sensor (Bit 0, auto clear) */
+       reg_w(gspca_dev, 0x11, 0x01);
+       PDEBUG(D_CONF|D_STREAM, "color: %i", sd->colors);
+}
+
+/* this function is called at open time */
+static int sd_open(struct gspca_dev *gspca_dev)
+{
+       reg_w(gspca_dev, 0x78, 0x00);   /* Turn on LED */
+       return 0;
+}
+
+static void sd_start(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       reg_w(gspca_dev, 0xff, 0x01);
+       reg_w_buf(gspca_dev, 0x0002, "\x48\x0a\x40\x08\x00\x00\x08\x00", 8);
+       reg_w_buf(gspca_dev, 0x000a, "\x06\xff\x11\xff\x5a\x30\x90\x4c", 8);
+       reg_w_buf(gspca_dev, 0x0012, "\x00\x07\x00\x0a\x10\x00\xa0\x10", 8);
+       reg_w_buf(gspca_dev, 0x001a, "\x02\x00\x00\x00\x00\x0b\x01\x00", 8);
+       reg_w_buf(gspca_dev, 0x0022, "\x00\x00\x00\x00\x00\x00\x00\x00", 8);
+       reg_w_buf(gspca_dev, 0x002a, "\x00\x00\x00", 3);
+       reg_w_buf(gspca_dev, 0x003e, "\x00\x00\x78\x52\x4a\x52\x78\x6e", 8);
+       reg_w_buf(gspca_dev, 0x0046, "\x48\x46\x48\x6e\x5f\x49\x42\x49", 8);
+       reg_w_buf(gspca_dev, 0x004e, "\x5f\x5f\x49\x42\x49\x5f\x6e\x48", 8);
+       reg_w_buf(gspca_dev, 0x0056, "\x46\x48\x6e\x78\x52\x4a\x52\x78", 8);
+       reg_w_buf(gspca_dev, 0x005e, "\x00\x00\x09\x1b\x34\x49\x5c\x9b", 8);
+       reg_w_buf(gspca_dev, 0x0066, "\xd0\xff", 2);
+       reg_w_buf(gspca_dev, 0x0078, "\x44\x00\xf2\x01\x01\x80", 6);
+       reg_w_buf(gspca_dev, 0x007f, "\x2a\x1c\x00\xc8\x02\x58\x03\x84", 8);
+       reg_w_buf(gspca_dev, 0x0087, "\x12\x00\x1a\x04\x08\x0c\x10\x14", 8);
+       reg_w_buf(gspca_dev, 0x008f, "\x18\x20", 2);
+       reg_w_buf(gspca_dev, 0x0096, "\x01\x08\x04", 3);
+       reg_w_buf(gspca_dev, 0x00a0, "\x44\x44\x44\x04", 4);
+       reg_w_buf(gspca_dev, 0x00f0, "\x01\x00\x00\x00\x22\x00\x20\x00", 8);
+       reg_w_buf(gspca_dev, 0x00f8, "\x3f\x00\x0a\x01\x00", 5);
+
+       reg_w(gspca_dev, 0xff, 0x04);
+       reg_w(gspca_dev, 0x02, 0x04);
+       reg_w(gspca_dev, 0x03, 0x54);
+       reg_w(gspca_dev, 0x04, 0x07);
+       reg_w(gspca_dev, 0x05, 0x2b);
+       reg_w(gspca_dev, 0x06, 0x09);
+       reg_w(gspca_dev, 0x07, 0x0f);
+       reg_w(gspca_dev, 0x08, 0x09);
+       reg_w(gspca_dev, 0x09, 0x00);
+       reg_w(gspca_dev, 0x0c, 0x07);
+       reg_w(gspca_dev, 0x0d, 0x00);
+       reg_w(gspca_dev, 0x0e, 0x00);
+       reg_w(gspca_dev, 0x0f, 0x62);
+       reg_w(gspca_dev, 0x10, 0x08);
+       reg_w(gspca_dev, 0x12, 0x07);
+       reg_w(gspca_dev, 0x13, 0x00);
+       reg_w(gspca_dev, 0x14, 0x00);
+       reg_w(gspca_dev, 0x15, 0x00);
+       reg_w(gspca_dev, 0x16, 0x00);
+       reg_w(gspca_dev, 0x17, 0x00);
+       reg_w(gspca_dev, 0x18, 0x00);
+       reg_w(gspca_dev, 0x19, 0x00);
+       reg_w(gspca_dev, 0x1a, 0x00);
+       reg_w(gspca_dev, 0x1b, 0x03);
+       reg_w(gspca_dev, 0x1c, 0xa0);
+       reg_w(gspca_dev, 0x1d, 0x01);
+       reg_w(gspca_dev, 0x1e, 0xf4);
+       reg_w(gspca_dev, 0x21, 0x00);
+       reg_w(gspca_dev, 0x22, 0x08);
+       reg_w(gspca_dev, 0x24, 0x03);
+       reg_w(gspca_dev, 0x26, 0x00);
+       reg_w(gspca_dev, 0x27, 0x01);
+       reg_w(gspca_dev, 0x28, 0xca);
+       reg_w(gspca_dev, 0x29, 0x10);
+       reg_w(gspca_dev, 0x2a, 0x06);
+       reg_w(gspca_dev, 0x2b, 0x78);
+       reg_w(gspca_dev, 0x2c, 0x00);
+       reg_w(gspca_dev, 0x2d, 0x00);
+       reg_w(gspca_dev, 0x2e, 0x00);
+       reg_w(gspca_dev, 0x2f, 0x00);
+       reg_w(gspca_dev, 0x30, 0x23);
+       reg_w(gspca_dev, 0x31, 0x28);
+       reg_w(gspca_dev, 0x32, 0x04);
+       reg_w(gspca_dev, 0x33, 0x11);
+       reg_w(gspca_dev, 0x34, 0x00);
+       reg_w(gspca_dev, 0x35, 0x00);
+       reg_w(gspca_dev, 0x11, 0x01);
+       setcontrast(gspca_dev);
+       setbrightness(gspca_dev);
+       setcolors(gspca_dev);
+
+       /* set correct resolution */
+       switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) {
+       case 2:                                 /* 160x120 */
+               reg_w(gspca_dev, 0xff, 0x04);
+               reg_w(gspca_dev, 0x02, 0x03);
+               reg_w(gspca_dev, 0xff, 0x01);
+               reg_w(gspca_dev, 0x08, 0x09);
+               reg_w(gspca_dev, 0x17, 0x20);
+               reg_w(gspca_dev, 0x1b, 0x00);
+/*             reg_w(gspca_dev, 0x80, 0x69); */
+               reg_w(gspca_dev, 0x87, 0x10);
+               break;
+       case 1:                                 /* 320x240 */
+               reg_w(gspca_dev, 0xff, 0x04);
+               reg_w(gspca_dev, 0x02, 0x03);
+               reg_w(gspca_dev, 0xff, 0x01);
+               reg_w(gspca_dev, 0x08, 0x09);
+               reg_w(gspca_dev, 0x17, 0x30);
+/*             reg_w(gspca_dev, 0x80, 0x3f); */
+               reg_w(gspca_dev, 0x87, 0x11);
+               break;
+       case 0:                                 /* 640x480 */
+               reg_w(gspca_dev, 0xff, 0x04);
+               reg_w(gspca_dev, 0x02, 0x03);
+               reg_w(gspca_dev, 0xff, 0x01);
+               reg_w(gspca_dev, 0x08, 0x08);
+               reg_w(gspca_dev, 0x17, 0x00);
+/*             reg_w(gspca_dev, 0x80, 0x1c); */
+               reg_w(gspca_dev, 0x87, 0x12);
+               break;
+       }
+
+       /* start stream */
+       reg_w(gspca_dev, 0xff, 0x01);
+       reg_w(gspca_dev, 0x78, 0x04);
+       reg_w(gspca_dev, 0x78, 0x05);
+
+       if (sd->autogain) {
+               sd->ag_cnt = AG_CNT_START;
+               sd->avg_lum = 0;
+       } else {
+               sd->ag_cnt = -1;
+       }
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+       reg_w(gspca_dev, 0xff, 0x04);
+       reg_w(gspca_dev, 0x27, 0x80);
+       reg_w(gspca_dev, 0x28, 0xca);
+       reg_w(gspca_dev, 0x29, 0x53);
+       reg_w(gspca_dev, 0x2a, 0x0e);
+       reg_w(gspca_dev, 0xff, 0x01);
+       reg_w(gspca_dev, 0x3e, 0x20);
+       reg_w(gspca_dev, 0x78, 0x04); /* Bit_0=start stream, Bit_7=LED */
+       reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_7=LED */
+       reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_7=LED */
+}
+
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+}
+
+/* this function is called at close time */
+static void sd_close(struct gspca_dev *gspca_dev)
+{
+       reg_w(gspca_dev, 0xff, 0x04);
+       reg_w(gspca_dev, 0x27, 0x80);
+       reg_w(gspca_dev, 0x28, 0xca);
+       reg_w(gspca_dev, 0x29, 0x53);
+       reg_w(gspca_dev, 0x2a, 0x0e);
+       reg_w(gspca_dev, 0xff, 0x01);
+       reg_w(gspca_dev, 0x3e, 0x20);
+       reg_w(gspca_dev, 0x78, 0x04); /* Bit_0=start stream, Bit_7=LED */
+       reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_7=LED */
+       reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_7=LED */
+}
+
+static void setautogain(struct gspca_dev *gspca_dev, int luma)
+{
+       int luma_mean = 128;
+       int luma_delta = 20;
+       __u8 spring = 5;
+       int Gbright;
+
+       Gbright = reg_r(gspca_dev, 0x02);
+       PDEBUG(D_FRAM, "luma mean %d", luma);
+       if (luma < luma_mean - luma_delta ||
+           luma > luma_mean + luma_delta) {
+               Gbright += (luma_mean - luma) >> spring;
+               if (Gbright > 0x1a)
+                       Gbright = 0x1a;
+               else if (Gbright < 4)
+                       Gbright = 4;
+               PDEBUG(D_FRAM, "gbright %d", Gbright);
+               reg_w(gspca_dev, 0xff, 0x04);
+               reg_w(gspca_dev, 0x0f, Gbright);
+               /* load registers to sensor (Bit 0, auto clear) */
+               reg_w(gspca_dev, 0x11, 0x01);
+       }
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+                       struct gspca_frame *frame,      /* target */
+                       __u8 *data,                     /* isoc packet */
+                       int len)                        /* iso packet length */
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       unsigned char tmpbuf[4];
+       int i, p, ffseq;
+
+/*     if (len < 5) { */
+       if (len < 6) {
+/*             gspca_dev->last_packet_type = DISCARD_PACKET; */
+               return;
+       }
+
+       ffseq = sd->ffseq;
+
+       for (p = 0; p < len - 6; p++) {
+               if ((data[0 + p] == 0xff)
+                   && (data[1 + p] == 0xff)
+                   && (data[2 + p] == 0x00)
+                   && (data[3 + p] == 0xff)
+                   && (data[4 + p] == 0x96)) {
+
+                       /* start of frame */
+                       if (sd->ag_cnt >= 0 && p > 28) {
+                               sd->avg_lum += data[p - 23];
+                               if (--sd->ag_cnt < 0) {
+                                       sd->ag_cnt = AG_CNT_START;
+                                       setautogain(gspca_dev,
+                                               sd->avg_lum / AG_CNT_START);
+                                       sd->avg_lum = 0;
+                               }
+                       }
+
+                       /* copy the end of data to the current frame */
+                       frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
+                                               data, p);
+
+                       /* put the JPEG header in the new frame */
+                       gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
+                                       (unsigned char *) pac7311_jpeg_header,
+                                       12);
+                       tmpbuf[0] = gspca_dev->height >> 8;
+                       tmpbuf[1] = gspca_dev->height & 0xff;
+                       tmpbuf[2] = gspca_dev->width >> 8;
+                       tmpbuf[3] = gspca_dev->width & 0xff;
+                       gspca_frame_add(gspca_dev, INTER_PACKET, frame,
+                                       tmpbuf, 4);
+                       gspca_frame_add(gspca_dev, INTER_PACKET, frame,
+                               (unsigned char *) &pac7311_jpeg_header[16],
+                               PAC7311_JPEG_HEADER_SIZE - 16);
+
+                       data += p + 7;
+                       len -= p + 7;
+                       ffseq = 0;
+                       break;
+               }
+       }
+
+       /* remove the 'ff ff ff xx' sequences */
+       switch (ffseq) {
+       case 3:
+               data += 1;
+               len -= 1;
+               break;
+       case 2:
+               if (data[0] == 0xff) {
+                       data += 2;
+                       len -= 2;
+                       frame->data_end -= 2;
+               }
+               break;
+       case 1:
+               if (data[0] == 0xff
+                   && data[1] == 0xff) {
+                       data += 3;
+                       len -= 3;
+                       frame->data_end -= 1;
+               }
+               break;
+       }
+       for (i = 0; i < len - 4; i++) {
+               if (data[i] == 0xff
+                   && data[i + 1] == 0xff
+                   && data[i + 2] == 0xff) {
+                       memmove(&data[i], &data[i + 4], len - i - 4);
+                       len -= 4;
+               }
+       }
+       ffseq = 0;
+       if (data[len - 4] == 0xff) {
+               if (data[len - 3] == 0xff
+                   && data[len - 2] == 0xff) {
+                       len -= 4;
+               }
+       } else if (data[len - 3] == 0xff) {
+               if (data[len - 2] == 0xff
+                   && data[len - 1] == 0xff)
+                       ffseq = 3;
+       } else if (data[len - 2] == 0xff) {
+               if (data[len - 1] == 0xff)
+                       ffseq = 2;
+       } else if (data[len - 1] == 0xff)
+               ffseq = 1;
+       sd->ffseq = ffseq;
+       gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
+}
+
+static void getbrightness(struct gspca_dev *gspca_dev)
+{
+/*     sd->brightness = reg_r(gspca_dev, 0x08);
+       return sd->brightness;  */
+/*     PDEBUG(D_CONF, "Called pac7311_getbrightness: Not implemented yet"); */
+}
+
+
+
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->brightness = val;
+       if (gspca_dev->streaming)
+               setbrightness(gspca_dev);
+       return 0;
+}
+
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       getbrightness(gspca_dev);
+       *val = sd->brightness;
+       return 0;
+}
+
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->contrast = val;
+       if (gspca_dev->streaming)
+               setcontrast(gspca_dev);
+       return 0;
+}
+
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+/*     getcontrast(gspca_dev); */
+       *val = sd->contrast;
+       return 0;
+}
+
+static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->colors = val;
+       if (gspca_dev->streaming)
+               setcolors(gspca_dev);
+       return 0;
+}
+
+static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+/*     getcolors(gspca_dev); */
+       *val = sd->colors;
+       return 0;
+}
+
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->autogain = val;
+       if (val) {
+               sd->ag_cnt = AG_CNT_START;
+               sd->avg_lum = 0;
+       } else {
+               sd->ag_cnt = -1;
+       }
+       return 0;
+}
+
+static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->autogain;
+       return 0;
+}
+
+/* sub-driver description */
+static struct sd_desc sd_desc = {
+       .name = MODULE_NAME,
+       .ctrls = sd_ctrls,
+       .nctrls = ARRAY_SIZE(sd_ctrls),
+       .config = sd_config,
+       .open = sd_open,
+       .start = sd_start,
+       .stopN = sd_stopN,
+       .stop0 = sd_stop0,
+       .close = sd_close,
+       .pkt_scan = sd_pkt_scan,
+};
+
+/* -- module initialisation -- */
+#define DVNM(name) .driver_info = (kernel_ulong_t) name
+static __devinitdata struct usb_device_id device_table[] = {
+       {USB_DEVICE(0x093a, 0x2600), DVNM("Typhoon")},
+       {USB_DEVICE(0x093a, 0x2601), DVNM("Philips SPC610NC")},
+       {USB_DEVICE(0x093a, 0x2603), DVNM("PAC7312")},
+       {USB_DEVICE(0x093a, 0x2608), DVNM("Trust WB-3300p")},
+       {USB_DEVICE(0x093a, 0x260e), DVNM("Gigaware VGA PC Camera")},
+                       /* and also ', Trust WB-3350p, SIGMA cam 2350' */
+       {USB_DEVICE(0x093a, 0x260f), DVNM("SnakeCam")},
+       {USB_DEVICE(0x093a, 0x2621), DVNM("PAC731x")},
+       {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+                       const struct usb_device_id *id)
+{
+       return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+                               THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+       .name = MODULE_NAME,
+       .id_table = device_table,
+       .probe = sd_probe,
+       .disconnect = gspca_disconnect,
+};
+
+/* -- module insert / remove -- */
+static int __init sd_mod_init(void)
+{
+       if (usb_register(&sd_driver) < 0)
+               return -1;
+       PDEBUG(D_PROBE, "v%s registered", version);
+       return 0;
+}
+static void __exit sd_mod_exit(void)
+{
+       usb_deregister(&sd_driver);
+       PDEBUG(D_PROBE, "deregistered");
+}
+
+module_init(sd_mod_init);
+module_exit(sd_mod_exit);
diff --git a/drivers/media/video/gspca/sonixb.c b/drivers/media/video/gspca/sonixb.c
new file mode 100644 (file)
index 0000000..dbeebe8
--- /dev/null
@@ -0,0 +1,1477 @@
+/*
+ *             sonix sn9c102 (bayer) library
+ *             Copyright (C) 2003 2004 Michel Xhaard mxhaard@magic.fr
+ * Add Pas106 Stefano Mozzi (C) 2004
+ *
+ * V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
+ *
+ * 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
+ * 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
+ */
+
+#define MODULE_NAME "sonixb"
+
+#include "gspca.h"
+
+#define DRIVER_VERSION_NUMBER  KERNEL_VERSION(2, 1, 8)
+static const char version[] = "2.1.8";
+
+MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
+MODULE_DESCRIPTION("GSPCA/SN9C102 USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* specific webcam descriptor */
+struct sd {
+       struct gspca_dev gspca_dev;     /* !! must be the first item */
+
+       struct sd_desc sd_desc;         /* our nctrls differ dependend upon the
+                                          sensor, so we use a per cam copy */
+       atomic_t avg_lum;
+
+       unsigned char gain;
+       unsigned char exposure;
+       unsigned char brightness;
+       unsigned char autogain;
+       unsigned char autogain_ignore_frames;
+       unsigned char freq;             /* light freq filter setting */
+       unsigned char saturation;
+       unsigned char hue;
+       unsigned char contrast;
+
+       unsigned char fr_h_sz;          /* size of frame header */
+       char sensor;                    /* Type of image sensor chip */
+#define SENSOR_HV7131R 0
+#define SENSOR_OV6650 1
+#define SENSOR_OV7630 2
+#define SENSOR_OV7630_3 3
+#define SENSOR_PAS106 4
+#define SENSOR_PAS202 5
+#define SENSOR_TAS5110 6
+#define SENSOR_TAS5130CXX 7
+       char sensor_has_gain;
+       __u8 sensor_addr;
+};
+
+#define COMP2 0x8f
+#define COMP 0xc7              /* 0x87 //0x07 */
+#define COMP1 0xc9             /* 0x89 //0x09 */
+
+#define MCK_INIT 0x63
+#define MCK_INIT1 0x20         /*fixme: Bayer - 0x50 for JPEG ??*/
+
+#define SYS_CLK 0x04
+
+/* We calculate the autogain at the end of the transfer of a frame, at this
+   moment a frame with the old settings is being transmitted, and a frame is
+   being captured with the old settings. So if we adjust the autogain we must
+   ignore atleast the 2 next frames for the new settings to come into effect
+   before doing any other adjustments */
+#define AUTOGAIN_IGNORE_FRAMES 3
+#define AUTOGAIN_DEADZONE 1000
+#define DESIRED_AVG_LUM 7000
+
+/* V4L2 controls supported by the driver */
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setsaturation(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getsaturation(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_sethue(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
+
+static struct ctrl sd_ctrls[] = {
+       {
+           {
+               .id      = V4L2_CID_BRIGHTNESS,
+               .type    = V4L2_CTRL_TYPE_INTEGER,
+               .name    = "Brightness",
+               .minimum = 0,
+               .maximum = 255,
+               .step    = 1,
+#define BRIGHTNESS_DEF 127
+               .default_value = BRIGHTNESS_DEF,
+           },
+           .set = sd_setbrightness,
+           .get = sd_getbrightness,
+       },
+       {
+           {
+               .id      = V4L2_CID_GAIN,
+               .type    = V4L2_CTRL_TYPE_INTEGER,
+               .name    = "Gain",
+               .minimum = 0,
+               .maximum = 255,
+               .step    = 1,
+#define GAIN_DEF 127
+#define GAIN_KNEE 200
+               .default_value = GAIN_DEF,
+           },
+           .set = sd_setgain,
+           .get = sd_getgain,
+       },
+       {
+               {
+                       .id = V4L2_CID_EXPOSURE,
+                       .type = V4L2_CTRL_TYPE_INTEGER,
+                       .name = "Exposure",
+#define EXPOSURE_DEF  16 /*  32 ms / 30 fps */
+#define EXPOSURE_KNEE 50 /* 100 ms / 10 fps */
+                       .minimum = 0,
+                       .maximum = 255,
+                       .step = 1,
+                       .default_value = EXPOSURE_DEF,
+                       .flags = 0,
+               },
+               .set = sd_setexposure,
+               .get = sd_getexposure,
+       },
+       {
+               {
+                       .id = V4L2_CID_AUTOGAIN,
+                       .type = V4L2_CTRL_TYPE_BOOLEAN,
+                       .name = "Automatic Gain (and Exposure)",
+                       .minimum = 0,
+                       .maximum = 1,
+                       .step = 1,
+#define AUTOGAIN_DEF 1
+                       .default_value = AUTOGAIN_DEF,
+                       .flags = 0,
+               },
+               .set = sd_setautogain,
+               .get = sd_getautogain,
+       },
+       {
+               {
+                       .id      = V4L2_CID_POWER_LINE_FREQUENCY,
+                       .type    = V4L2_CTRL_TYPE_MENU,
+                       .name    = "Light frequency filter",
+                       .minimum = 0,
+                       .maximum = 2,   /* 0: 0, 1: 50Hz, 2:60Hz */
+                       .step    = 1,
+#define FREQ_DEF 1
+                       .default_value = FREQ_DEF,
+               },
+               .set = sd_setfreq,
+               .get = sd_getfreq,
+       },
+       {
+               {
+                       .id      = V4L2_CID_SATURATION,
+                       .type    = V4L2_CTRL_TYPE_INTEGER,
+                       .name    = "Saturation",
+                       .minimum = 0,
+                       .maximum = 255,
+                       .step    = 1,
+#define SATURATION_DEF 127
+                       .default_value = SATURATION_DEF,
+               },
+               .set = sd_setsaturation,
+               .get = sd_getsaturation,
+       },
+       {
+               {
+                       .id      = V4L2_CID_HUE,
+                       .type    = V4L2_CTRL_TYPE_INTEGER,
+                       .name    = "Hue",
+                       .minimum = 0,
+                       .maximum = 255,
+                       .step    = 1,
+#define HUE_DEF 127
+                       .default_value = HUE_DEF,
+               },
+               .set = sd_sethue,
+               .get = sd_gethue,
+       },
+       {
+               {
+                       .id      = V4L2_CID_CONTRAST,
+                       .type    = V4L2_CTRL_TYPE_INTEGER,
+                       .name    = "Contrast",
+                       .minimum = 0,
+                       .maximum = 255,
+                       .step    = 1,
+#define CONTRAST_DEF 127
+                       .default_value = CONTRAST_DEF,
+               },
+               .set = sd_setcontrast,
+               .get = sd_getcontrast,
+       },
+};
+
+static struct v4l2_pix_format vga_mode[] = {
+       {160, 120, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE,
+               .bytesperline = 160,
+               .sizeimage = 160 * 120,
+               .colorspace = V4L2_COLORSPACE_SRGB,
+               .priv = 2},
+       {320, 240, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE,
+               .bytesperline = 320,
+               .sizeimage = 320 * 240,
+               .colorspace = V4L2_COLORSPACE_SRGB,
+               .priv = 1},
+       {640, 480, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE,
+               .bytesperline = 640,
+               .sizeimage = 640 * 480,
+               .colorspace = V4L2_COLORSPACE_SRGB,
+               .priv = 0},
+};
+static struct v4l2_pix_format sif_mode[] = {
+       {176, 144, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE,
+               .bytesperline = 176,
+               .sizeimage = 176 * 144,
+               .colorspace = V4L2_COLORSPACE_SRGB,
+               .priv = 1},
+       {352, 288, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE,
+               .bytesperline = 352,
+               .sizeimage = 352 * 288,
+               .colorspace = V4L2_COLORSPACE_SRGB,
+               .priv = 0},
+};
+
+static const __u8 probe_ov7630[] = {0x08, 0x44};
+
+static const __u8 initHv7131[] = {
+       0x46, 0x77, 0x00, 0x04, 0x00, 0x00, 0x00, 0x80, 0x11, 0x00, 0x00, 0x00,
+       0x00, 0x00,
+       0x00, 0x00, 0x00, 0x03, 0x01, 0x00,     /* shift from 0x02 0x01 0x00 */
+       0x28, 0x1e, 0x60, 0x8a, 0x20,
+       0x1d, 0x10, 0x02, 0x03, 0x0f, 0x0c
+};
+static const __u8 hv7131_sensor_init[][8] = {
+       {0xc0, 0x11, 0x31, 0x38, 0x2a, 0x2e, 0x00, 0x10},
+       {0xa0, 0x11, 0x01, 0x08, 0x2a, 0x2e, 0x00, 0x10},
+       {0xb0, 0x11, 0x20, 0x00, 0xd0, 0x2e, 0x00, 0x10},
+       {0xc0, 0x11, 0x25, 0x03, 0x0e, 0x28, 0x00, 0x16},
+       {0xa0, 0x11, 0x30, 0x10, 0x0e, 0x28, 0x00, 0x15},
+};
+static const __u8 initOv6650[] = {
+       0x44, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+       0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x02, 0x01, 0x0a, 0x16, 0x12, 0x68, 0x0b,
+       0x10, 0x1d, 0x10, 0x00, 0x06, 0x1f, 0x00
+};
+static const __u8 ov6650_sensor_init[][8] =
+{
+       /* Bright, contrast, etc are set througth SCBB interface.
+        * AVCAP on win2 do not send any data on this   controls. */
+       /* Anyway, some registers appears to alter bright and constrat */
+
+       /* Reset sensor */
+       {0xa0, 0x60, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10},
+       /* Set clock register 0x11 low nibble is clock divider */
+       {0xd0, 0x60, 0x11, 0xc0, 0x1b, 0x18, 0xc1, 0x10},
+       /* Next some unknown stuff */
+       {0xb0, 0x60, 0x15, 0x00, 0x02, 0x18, 0xc1, 0x10},
+/*     {0xa0, 0x60, 0x1b, 0x01, 0x02, 0x18, 0xc1, 0x10},
+                * THIS SET GREEN SCREEN
+                * (pixels could be innverted in decode kind of "brg",
+                * but blue wont be there. Avoid this data ... */
+       {0xd0, 0x60, 0x26, 0x01, 0x14, 0xd8, 0xa4, 0x10}, /* format out? */
+       {0xd0, 0x60, 0x26, 0x01, 0x14, 0xd8, 0xa4, 0x10},
+       {0xa0, 0x60, 0x30, 0x3d, 0x0A, 0xd8, 0xa4, 0x10},
+       /* Enable rgb brightness control */
+       {0xa0, 0x60, 0x61, 0x08, 0x00, 0x00, 0x00, 0x10},
+       /* HDG: Note windows uses the line below, which sets both register 0x60
+          and 0x61 I believe these registers of the ov6650 are identical as
+          those of the ov7630, because if this is true the windows settings
+          add a bit additional red gain and a lot additional blue gain, which
+          matches my findings that the windows settings make blue much too
+          blue and red a little too red.
+       {0xb0, 0x60, 0x60, 0x66, 0x68, 0xd8, 0xa4, 0x10}, */
+       /* Some more unknown stuff */
+       {0xa0, 0x60, 0x68, 0x04, 0x68, 0xd8, 0xa4, 0x10},
+       {0xd0, 0x60, 0x17, 0x24, 0xd6, 0x04, 0x94, 0x10}, /* Clipreg */
+};
+
+static const __u8 initOv7630[] = {
+       0x04, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, /* r01 .. r08 */
+       0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* r09 .. r10 */
+       0x00, 0x02, 0x01, 0x0a,                         /* r11 .. r14 */
+       0x28, 0x1e,                     /* H & V sizes     r15 .. r16 */
+       0x68, COMP1, MCK_INIT1,                         /* r17 .. r19 */
+       0x1d, 0x10, 0x02, 0x03, 0x0f, 0x0c              /* r1a .. r1f */
+};
+static const __u8 initOv7630_3[] = {
+       0x44, 0x44, 0x00, 0x1a, 0x20, 0x20, 0x20, 0x80, /* r01 .. r08 */
+       0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, /* r09 .. r10 */
+       0x00, 0x01, 0x01, 0x0a,                         /* r11 .. r14 */
+       0x28, 0x1e,                     /* H & V sizes     r15 .. r16 */
+       0x68, 0x8f, MCK_INIT1,                          /* r17 .. r19 */
+       0x1d, 0x10, 0x02, 0x03, 0x0f, 0x0c, 0x00,       /* r1a .. r20 */
+       0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, /* r21 .. r28 */
+       0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0, 0xff  /* r29 .. r30 */
+};
+static const __u8 ov7630_sensor_init_com[][8] = {
+       {0xa0, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10},
+       {0xb0, 0x21, 0x01, 0x77, 0x3a, 0x00, 0x00, 0x10},
+/*     {0xd0, 0x21, 0x12, 0x7c, 0x01, 0x80, 0x34, 0x10},          jfm */
+       {0xd0, 0x21, 0x12, 0x1c, 0x00, 0x80, 0x34, 0x10},       /* jfm */
+       {0xa0, 0x21, 0x1b, 0x04, 0x00, 0x80, 0x34, 0x10},
+       {0xa0, 0x21, 0x20, 0x44, 0x00, 0x80, 0x34, 0x10},
+       {0xa0, 0x21, 0x23, 0xee, 0x00, 0x80, 0x34, 0x10},
+       {0xd0, 0x21, 0x26, 0xa0, 0x9a, 0xa0, 0x30, 0x10},
+       {0xb0, 0x21, 0x2a, 0x80, 0x00, 0xa0, 0x30, 0x10},
+       {0xb0, 0x21, 0x2f, 0x3d, 0x24, 0xa0, 0x30, 0x10},
+       {0xa0, 0x21, 0x32, 0x86, 0x24, 0xa0, 0x30, 0x10},
+       {0xb0, 0x21, 0x60, 0xa9, 0x4a, 0xa0, 0x30, 0x10},
+/*     {0xb0, 0x21, 0x60, 0xa9, 0x42, 0xa0, 0x30, 0x10},        * jfm */
+       {0xa0, 0x21, 0x65, 0x00, 0x42, 0xa0, 0x30, 0x10},
+       {0xa0, 0x21, 0x69, 0x38, 0x42, 0xa0, 0x30, 0x10},
+       {0xc0, 0x21, 0x6f, 0x88, 0x0b, 0x00, 0x30, 0x10},
+       {0xc0, 0x21, 0x74, 0x21, 0x8e, 0x00, 0x30, 0x10},
+       {0xa0, 0x21, 0x7d, 0xf7, 0x8e, 0x00, 0x30, 0x10},
+       {0xd0, 0x21, 0x17, 0x1c, 0xbd, 0x06, 0xf6, 0x10},
+};
+static const __u8 ov7630_sensor_init[][8] = {
+       {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 200ms */
+       {0xa0, 0x21, 0x11, 0x01, 0xbd, 0x06, 0xf6, 0x10},       /* jfm */
+       {0xa0, 0x21, 0x10, 0x57, 0xbd, 0x06, 0xf6, 0x16},
+       {0xa0, 0x21, 0x76, 0x02, 0xbd, 0x06, 0xf6, 0x16},
+       {0xa0, 0x21, 0x00, 0x10, 0xbd, 0x06, 0xf6, 0x15},       /* gain */
+};
+static const __u8 ov7630_sensor_init_3[][8] = {
+       {0xa0, 0x21, 0x2a, 0xa0, 0x00, 0x00, 0x00, 0x10},
+       {0xa0, 0x21, 0x2a, 0x80, 0x00, 0x00, 0x00, 0x10},
+};
+
+static const __u8 initPas106[] = {
+       0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x40, 0x00, 0x00, 0x00,
+       0x00, 0x00,
+       0x00, 0x00, 0x00, 0x05, 0x01, 0x00,
+       0x16, 0x12, 0x28, COMP1, MCK_INIT1,
+       0x18, 0x10, 0x04, 0x03, 0x11, 0x0c
+};
+/* compression 0x86 mckinit1 0x2b */
+static const __u8 pas106_data[][2] = {
+       {0x02, 0x04},           /* Pixel Clock Divider 6 */
+       {0x03, 0x13},           /* Frame Time MSB */
+/*     {0x03, 0x12},            * Frame Time MSB */
+       {0x04, 0x06},           /* Frame Time LSB */
+/*     {0x04, 0x05},            * Frame Time LSB */
+       {0x05, 0x65},           /* Shutter Time Line Offset */
+/*     {0x05, 0x6d},            * Shutter Time Line Offset */
+/*     {0x06, 0xb1},            * Shutter Time Pixel Offset */
+       {0x06, 0xcd},           /* Shutter Time Pixel Offset */
+       {0x07, 0xc1},           /* Black Level Subtract Sign */
+/*     {0x07, 0x00},            * Black Level Subtract Sign */
+       {0x08, 0x06},           /* Black Level Subtract Level */
+       {0x08, 0x06},           /* Black Level Subtract Level */
+/*     {0x08, 0x01},            * Black Level Subtract Level */
+       {0x09, 0x05},           /* Color Gain B Pixel 5 a */
+       {0x0a, 0x04},           /* Color Gain G1 Pixel 1 5 */
+       {0x0b, 0x04},           /* Color Gain G2 Pixel 1 0 5 */
+       {0x0c, 0x05},           /* Color Gain R Pixel 3 1 */
+       {0x0d, 0x00},           /* Color GainH  Pixel */
+       {0x0e, 0x0e},           /* Global Gain */
+       {0x0f, 0x00},           /* Contrast */
+       {0x10, 0x06},           /* H&V synchro polarity */
+       {0x11, 0x06},           /* ?default */
+       {0x12, 0x06},           /* DAC scale */
+       {0x14, 0x02},           /* ?default */
+       {0x13, 0x01},           /* Validate Settings */
+};
+static const __u8 initPas202[] = {
+       0x44, 0x44, 0x21, 0x30, 0x00, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, 0x00,
+       0x00, 0x00,
+       0x00, 0x00, 0x00, 0x07, 0x03, 0x0a,     /* 6 */
+       0x28, 0x1e, 0x28, 0x89, 0x30,
+       0x00, 0x00, 0x02, 0x03, 0x0f, 0x0c
+};
+static const __u8 pas202_sensor_init[][8] = {
+       {0xa0, 0x40, 0x02, 0x03, 0x00, 0x00, 0x00, 0x10},
+       {0xd0, 0x40, 0x04, 0x07, 0x34, 0x00, 0x09, 0x10},
+       {0xd0, 0x40, 0x08, 0x01, 0x00, 0x00, 0x01, 0x10},
+       {0xd0, 0x40, 0x0C, 0x00, 0x0C, 0x00, 0x32, 0x10},
+       {0xd0, 0x40, 0x10, 0x00, 0x01, 0x00, 0x63, 0x10},
+       {0xa0, 0x40, 0x15, 0x70, 0x01, 0x00, 0x63, 0x10},
+       {0xa0, 0x40, 0x18, 0x00, 0x01, 0x00, 0x63, 0x10},
+       {0xa0, 0x40, 0x11, 0x01, 0x01, 0x00, 0x63, 0x10},
+       {0xa0, 0x40, 0x03, 0x56, 0x01, 0x00, 0x63, 0x10},
+       {0xa0, 0x40, 0x11, 0x01, 0x01, 0x00, 0x63, 0x10},
+       {0xb0, 0x40, 0x04, 0x07, 0x2a, 0x00, 0x63, 0x10},
+       {0xb0, 0x40, 0x0e, 0x00, 0x3d, 0x00, 0x63, 0x10},
+
+       {0xa0, 0x40, 0x11, 0x01, 0x3d, 0x00, 0x63, 0x16},
+       {0xa0, 0x40, 0x10, 0x08, 0x3d, 0x00, 0x63, 0x15},
+       {0xa0, 0x40, 0x02, 0x04, 0x3d, 0x00, 0x63, 0x16},
+       {0xa0, 0x40, 0x11, 0x01, 0x3d, 0x00, 0x63, 0x16},
+       {0xb0, 0x40, 0x0e, 0x00, 0x31, 0x00, 0x63, 0x16},
+       {0xa0, 0x40, 0x11, 0x01, 0x31, 0x00, 0x63, 0x16},
+       {0xa0, 0x40, 0x10, 0x0e, 0x31, 0x00, 0x63, 0x15},
+       {0xa0, 0x40, 0x11, 0x01, 0x31, 0x00, 0x63, 0x16},
+};
+
+static const __u8 initTas5110[] = {
+       0x44, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x11, 0x00, 0x00, 0x00,
+       0x00, 0x00,
+       0x00, 0x01, 0x00, 0x46, 0x09, 0x0a,     /* shift from 0x45 0x09 0x0a */
+       0x16, 0x12, 0x60, 0x86, 0x2b,
+       0x14, 0x0a, 0x02, 0x02, 0x09, 0x07
+};
+static const __u8 tas5110_sensor_init[][8] = {
+       {0x30, 0x11, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x10},
+       {0x30, 0x11, 0x02, 0x20, 0xa9, 0x00, 0x00, 0x10},
+       {0xa0, 0x61, 0x9a, 0xca, 0x00, 0x00, 0x00, 0x17},
+};
+
+static const __u8 initTas5130[] = {
+       0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x11, 0x00, 0x00, 0x00,
+       0x00, 0x00,
+       0x00, 0x01, 0x00, 0x69, 0x0c, 0x0a,
+       0x28, 0x1e, 0x60, COMP, MCK_INIT,
+       0x18, 0x10, 0x04, 0x03, 0x11, 0x0c
+};
+static const __u8 tas5130_sensor_init[][8] = {
+/*     {0x30, 0x11, 0x00, 0x40, 0x47, 0x00, 0x00, 0x10},
+                                       * shutter 0x47 short exposure? */
+       {0x30, 0x11, 0x00, 0x40, 0x01, 0x00, 0x00, 0x10},
+                                       /* shutter 0x01 long exposure */
+       {0x30, 0x11, 0x02, 0x20, 0x70, 0x00, 0x00, 0x10},
+};
+
+/* get one byte in gspca_dev->usb_buf */
+static void reg_r(struct gspca_dev *gspca_dev,
+                 __u16 value)
+{
+       usb_control_msg(gspca_dev->dev,
+                       usb_rcvctrlpipe(gspca_dev->dev, 0),
+                       0,                      /* request */
+                       USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+                       value,
+                       0,                      /* index */
+                       gspca_dev->usb_buf, 1,
+                       500);
+}
+
+static void reg_w(struct gspca_dev *gspca_dev,
+                 __u16 value,
+                 const __u8 *buffer,
+                 int len)
+{
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+       if (len > sizeof gspca_dev->usb_buf) {
+               PDEBUG(D_ERR|D_PACK, "reg_w: buffer overflow");
+               return;
+       }
+#endif
+       memcpy(gspca_dev->usb_buf, buffer, len);
+       usb_control_msg(gspca_dev->dev,
+                       usb_sndctrlpipe(gspca_dev->dev, 0),
+                       0x08,                   /* request */
+                       USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+                       value,
+                       0,                      /* index */
+                       gspca_dev->usb_buf, len,
+                       500);
+}
+
+static void reg_w_big(struct gspca_dev *gspca_dev,
+                 __u16 value,
+                 const __u8 *buffer,
+                 int len)
+{
+       __u8 *tmpbuf;
+
+       tmpbuf = kmalloc(len, GFP_KERNEL);
+       memcpy(tmpbuf, buffer, len);
+       usb_control_msg(gspca_dev->dev,
+                       usb_sndctrlpipe(gspca_dev->dev, 0),
+                       0x08,                   /* request */
+                       USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+                       value,
+                       0,                      /* index */
+                       tmpbuf, len,
+                       500);
+       kfree(tmpbuf);
+}
+
+static int i2c_w(struct gspca_dev *gspca_dev, const __u8 *buffer)
+{
+       int retry = 60;
+
+       /* is i2c ready */
+       reg_w(gspca_dev, 0x08, buffer, 8);
+       while (retry--) {
+               msleep(10);
+               reg_r(gspca_dev, 0x08);
+               if (gspca_dev->usb_buf[0] & 0x04) {
+                       if (gspca_dev->usb_buf[0] & 0x08)
+                               return -1;
+                       return 0;
+               }
+       }
+       return -1;
+}
+
+static void i2c_w_vector(struct gspca_dev *gspca_dev,
+                       const __u8 buffer[][8], int len)
+{
+       for (;;) {
+               reg_w(gspca_dev, 0x08, *buffer, 8);
+               len -= 8;
+               if (len <= 0)
+                       break;
+               buffer++;
+       }
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       __u8 value;
+
+       switch (sd->sensor) {
+       case  SENSOR_OV6650:
+       case  SENSOR_OV7630_3:
+       case  SENSOR_OV7630: {
+               __u8 i2cOV[] =
+                       {0xa0, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x10};
+
+               /* change reg 0x06 */
+               i2cOV[1] = sd->sensor_addr;
+               i2cOV[3] = sd->brightness;
+               if (i2c_w(gspca_dev, i2cOV) < 0)
+                       goto err;
+               break;
+           }
+       case SENSOR_PAS106: {
+               __u8 i2c1[] =
+                       {0xa1, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14};
+
+               i2c1[3] = sd->brightness >> 3;
+               i2c1[2] = 0x0e;
+               if (i2c_w(gspca_dev, i2c1) < 0)
+                       goto err;
+               i2c1[3] = 0x01;
+               i2c1[2] = 0x13;
+               if (i2c_w(gspca_dev, i2c1) < 0)
+                       goto err;
+               break;
+           }
+       case SENSOR_PAS202: {
+               /* __u8 i2cpexpo1[] =
+                       {0xb0, 0x40, 0x04, 0x07, 0x2a, 0x00, 0x63, 0x16}; */
+               __u8 i2cpexpo[] =
+                       {0xb0, 0x40, 0x0e, 0x01, 0xab, 0x00, 0x63, 0x16};
+               __u8 i2cp202[] =
+                       {0xa0, 0x40, 0x10, 0x0e, 0x31, 0x00, 0x63, 0x15};
+               static __u8 i2cpdoit[] =
+                       {0xa0, 0x40, 0x11, 0x01, 0x31, 0x00, 0x63, 0x16};
+
+               /* change reg 0x10 */
+               i2cpexpo[4] = 0xff - sd->brightness;
+/*             if(i2c_w(gspca_dev,i2cpexpo1) < 0)
+                       goto err; */
+/*             if(i2c_w(gspca_dev,i2cpdoit) < 0)
+                       goto err; */
+               if (i2c_w(gspca_dev, i2cpexpo) < 0)
+                       goto err;
+               if (i2c_w(gspca_dev, i2cpdoit) < 0)
+                       goto err;
+               i2cp202[3] = sd->brightness >> 3;
+               if (i2c_w(gspca_dev, i2cp202) < 0)
+                       goto err;
+               if (i2c_w(gspca_dev, i2cpdoit) < 0)
+                       goto err;
+               break;
+           }
+       case SENSOR_TAS5130CXX: {
+               __u8 i2c[] =
+                       {0x30, 0x11, 0x02, 0x20, 0x70, 0x00, 0x00, 0x10};
+
+               value = 0xff - sd->brightness;
+               i2c[4] = value;
+               PDEBUG(D_CONF, "brightness %d : %d", value, i2c[4]);
+               if (i2c_w(gspca_dev, i2c) < 0)
+                       goto err;
+               break;
+           }
+       case SENSOR_TAS5110:
+               /* FIXME figure out howto control brightness on TAS5110 */
+               break;
+       }
+       return;
+err:
+       PDEBUG(D_ERR, "i2c error brightness");
+}
+
+static void setsensorgain(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       unsigned char gain = sd->gain;
+
+       switch (sd->sensor) {
+
+       case SENSOR_TAS5110: {
+               __u8 i2c[] =
+                       {0x30, 0x11, 0x02, 0x20, 0x70, 0x00, 0x00, 0x10};
+
+               i2c[4] = 255 - gain;
+               if (i2c_w(gspca_dev, i2c) < 0)
+                       goto err;
+               break;
+           }
+
+       case SENSOR_OV6650:
+               gain >>= 1;
+               /* fall thru */
+       case SENSOR_OV7630_3: {
+               __u8 i2c[] = {0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10};
+
+               i2c[1] = sd->sensor_addr;
+               i2c[3] = gain >> 2;
+               if (i2c_w(gspca_dev, i2c) < 0)
+                       goto err;
+               break;
+           }
+       }
+       return;
+err:
+       PDEBUG(D_ERR, "i2c error gain");
+}
+
+static void setgain(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       __u8 gain;
+       __u8 rgb_value;
+
+       gain = sd->gain >> 4;
+
+       /* red and blue gain */
+       rgb_value = gain << 4 | gain;
+       reg_w(gspca_dev, 0x10, &rgb_value, 1);
+       /* green gain */
+       rgb_value = gain;
+       reg_w(gspca_dev, 0x11, &rgb_value, 1);
+
+       if (sd->sensor_has_gain)
+               setsensorgain(gspca_dev);
+}
+
+static void setexposure(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       switch (sd->sensor) {
+       case SENSOR_TAS5110: {
+               __u8 reg;
+
+               /* register 19's high nibble contains the sn9c10x clock divider
+                  The high nibble configures the no fps according to the
+                  formula: 60 / high_nibble. With a maximum of 30 fps */
+               reg = 120 * sd->exposure / 1000;
+               if (reg < 2)
+                       reg = 2;
+               else if (reg > 15)
+                       reg = 15;
+               reg = (reg << 4) | 0x0b;
+               reg_w(gspca_dev, 0x19, &reg, 1);
+               break;
+           }
+       case SENSOR_OV6650:
+       case SENSOR_OV7630_3: {
+               /* The ov6650 / ov7630 have 2 registers which both influence
+                  exposure, register 11, whose low nibble sets the nr off fps
+                  according to: fps = 30 / (low_nibble + 1)
+
+                  The fps configures the maximum exposure setting, but it is
+                  possible to use less exposure then what the fps maximum
+                  allows by setting register 10. register 10 configures the
+                  actual exposure as quotient of the full exposure, with 0
+                  being no exposure at all (not very usefull) and reg10_max
+                  being max exposure possible at that framerate.
+
+                  The code maps our 0 - 510 ms exposure ctrl to these 2
+                  registers, trying to keep fps as high as possible.
+               */
+               __u8 i2c[] = {0xb0, 0x00, 0x10, 0x00, 0xc0, 0x00, 0x00, 0x10};
+               int reg10, reg11;
+               /* ov6645 datasheet says reg10_max is 9a, but that uses
+                  tline * 2 * reg10 as formula for calculating texpo, the
+                  ov6650 probably uses the same formula as the 7730 which uses
+                  tline * 4 * reg10, which explains why the reg10max we've
+                  found experimentally for the ov6650 is exactly half that of
+                  the ov6645. The ov7630 datasheet says the max is 0x41. */
+               const int reg10_max = (sd->sensor == SENSOR_OV6650)
+                               ? 0x4d : 0x41;
+
+               reg11 = (60 * sd->exposure + 999) / 1000;
+               if (reg11 < 1)
+                       reg11 = 1;
+               else if (reg11 > 16)
+                       reg11 = 16;
+
+               /* frame exposure time in ms = 1000 * reg11 / 30    ->
+               reg10 = sd->exposure * 2 * reg10_max / (1000 * reg11 / 30) */
+               reg10 = (sd->exposure * 60 * reg10_max) / (1000 * reg11);
+
+               /* Don't allow this to get below 10 when using autogain, the
+                  steps become very large (relatively) when below 10 causing
+                  the image to oscilate from much too dark, to much too bright
+                  and back again. */
+               if (sd->autogain && reg10 < 10)
+                       reg10 = 10;
+               else if (reg10 > reg10_max)
+                       reg10 = reg10_max;
+
+               /* Write reg 10 and reg11 low nibble */
+               i2c[1] = sd->sensor_addr;
+               i2c[3] = reg10;
+               i2c[4] |= reg11 - 1;
+               if (sd->sensor == SENSOR_OV7630_3) {
+                       __u8 reg76 = reg10 & 0x03;
+                       __u8 i2c_reg76[] = {0xa0, 0x21, 0x76, 0x00,
+                                           0x00, 0x00, 0x00, 0x10};
+                       reg10 >>= 2;
+                       i2c_reg76[3] = reg76;
+                       if (i2c_w(gspca_dev, i2c_reg76) < 0)
+                               PDEBUG(D_ERR, "i2c error exposure");
+               }
+               if (i2c_w(gspca_dev, i2c) < 0)
+                       PDEBUG(D_ERR, "i2c error exposure");
+               break;
+           }
+       }
+}
+
+static void setfreq(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       switch (sd->sensor) {
+       case SENSOR_OV6650:
+       case SENSOR_OV7630_3: {
+               /* Framerate adjust register for artificial light 50 hz flicker
+                  compensation, identical to ov6630 0x2b register, see ov6630
+                  datasheet.
+                  0x4f -> (30 fps -> 25 fps), 0x00 -> no adjustment */
+               __u8 i2c[] = {0xa0, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x10};
+               switch (sd->freq) {
+               default:
+/*             case 0:                  * no filter*/
+/*             case 2:                  * 60 hz */
+                       i2c[3] = 0;
+                       break;
+               case 1:                 /* 50 hz */
+                       i2c[3] = (sd->sensor == SENSOR_OV6650)
+                                       ? 0x4f : 0x8a;
+                       break;
+               }
+               i2c[1] = sd->sensor_addr;
+               if (i2c_w(gspca_dev, i2c) < 0)
+                       PDEBUG(D_ERR, "i2c error setfreq");
+               break;
+           }
+       }
+}
+
+static void setsaturation(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       switch (sd->sensor) {
+/*     case SENSOR_OV6650: */
+       case SENSOR_OV7630_3:
+       case SENSOR_OV7630: {
+               __u8 i2c[] = {0xa0, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10};
+               i2c[1] = sd->sensor_addr;
+               i2c[3] = sd->saturation & 0xf0;
+               if (i2c_w(gspca_dev, i2c) < 0)
+                       PDEBUG(D_ERR, "i2c error setsaturation");
+               else
+                       PDEBUG(D_CONF, "saturation set to: %d",
+                               (int)sd->saturation);
+               break;
+           }
+       }
+}
+
+static void sethue(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       switch (sd->sensor) {
+/*     case SENSOR_OV6650: */
+       case SENSOR_OV7630_3:
+       case SENSOR_OV7630: {
+               __u8 i2c[] = {0xa0, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x10};
+               i2c[1] = sd->sensor_addr;
+               i2c[3] = 0x20 | (sd->hue >> 3);
+               if (i2c_w(gspca_dev, i2c) < 0)
+                       PDEBUG(D_ERR, "i2c error setsaturation");
+               else
+                       PDEBUG(D_CONF, "hue set to: %d", (int)sd->hue);
+               break;
+           }
+       }
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       switch (sd->sensor) {
+/*     case SENSOR_OV6650: */
+       case SENSOR_OV7630_3:
+       case SENSOR_OV7630: {
+               __u8 i2c[] = {0xa0, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x10};
+               i2c[1] = sd->sensor_addr;
+               i2c[3] = 0x20 | (sd->contrast >> 3);
+               if (i2c_w(gspca_dev, i2c) < 0)
+                       PDEBUG(D_ERR, "i2c error setcontrast");
+               else
+                       PDEBUG(D_CONF, "contrast set to: %d",
+                               (int)sd->contrast);
+               break;
+           }
+       }
+}
+
+
+static void do_autogain(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       int avg_lum = atomic_read(&sd->avg_lum);
+
+       if (avg_lum == -1)
+               return;
+
+       if (sd->autogain_ignore_frames > 0)
+               sd->autogain_ignore_frames--;
+       else if (gspca_auto_gain_n_exposure(gspca_dev, avg_lum,
+                       sd->brightness * DESIRED_AVG_LUM / 127,
+                       AUTOGAIN_DEADZONE, GAIN_KNEE, EXPOSURE_KNEE)) {
+               PDEBUG(D_FRAM, "autogain: gain changed: gain: %d expo: %d\n",
+                       (int)sd->gain, (int)sd->exposure);
+               sd->autogain_ignore_frames = AUTOGAIN_IGNORE_FRAMES;
+       }
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+                       const struct usb_device_id *id)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       struct cam *cam;
+       __u16 product;
+       int sif = 0;
+
+       /* nctrls depends upon the sensor, so we use a per cam copy */
+       memcpy(&sd->sd_desc, gspca_dev->sd_desc, sizeof(struct sd_desc));
+       gspca_dev->sd_desc = &sd->sd_desc;
+
+       sd->fr_h_sz = 12;               /* default size of the frame header */
+       sd->sd_desc.nctrls = 2;         /* default nb of ctrls */
+       sd->autogain = AUTOGAIN_DEF;    /* default is autogain active */
+
+       product = id->idProduct;
+/*     switch (id->idVendor) { */
+/*     case 0x0c45:                             * Sonix */
+               switch (product) {
+               case 0x6001:                    /* SN9C102 */
+               case 0x6005:                    /* SN9C101 */
+               case 0x6007:                    /* SN9C101 */
+                       sd->sensor = SENSOR_TAS5110;
+                       sd->sensor_has_gain = 1;
+                       sd->sd_desc.nctrls = 4;
+                       sd->sd_desc.dq_callback = do_autogain;
+                       sif = 1;
+                       break;
+               case 0x6009:                    /* SN9C101 */
+               case 0x600d:                    /* SN9C101 */
+               case 0x6029:                    /* SN9C101 */
+                       sd->sensor = SENSOR_PAS106;
+                       sif = 1;
+                       break;
+               case 0x6011:                    /* SN9C101 - SN9C101G */
+                       sd->sensor = SENSOR_OV6650;
+                       sd->sensor_has_gain = 1;
+                       sd->sensor_addr = 0x60;
+                       sd->sd_desc.nctrls = 5;
+                       sd->sd_desc.dq_callback = do_autogain;
+                       sif = 1;
+                       break;
+               case 0x6019:                    /* SN9C101 */
+               case 0x602c:                    /* SN9C102 */
+               case 0x602e:                    /* SN9C102 */
+                       sd->sensor = SENSOR_OV7630;
+                       sd->sensor_addr = 0x21;
+                       break;
+               case 0x60b0:                    /* SN9C103 */
+                       sd->sensor = SENSOR_OV7630_3;
+                       sd->sensor_addr = 0x21;
+                       sd->fr_h_sz = 18;       /* size of frame header */
+                       sd->sensor_has_gain = 1;
+                       sd->sd_desc.nctrls = 8;
+                       sd->sd_desc.dq_callback = do_autogain;
+                       sd->autogain = 0;
+                       break;
+               case 0x6024:                    /* SN9C102 */
+               case 0x6025:                    /* SN9C102 */
+                       sd->sensor = SENSOR_TAS5130CXX;
+                       break;
+               case 0x6028:                    /* SN9C102 */
+                       sd->sensor = SENSOR_PAS202;
+                       break;
+               case 0x602d:                    /* SN9C102 */
+                       sd->sensor = SENSOR_HV7131R;
+                       break;
+               case 0x60af:                    /* SN9C103 */
+                       sd->sensor = SENSOR_PAS202;
+                       sd->fr_h_sz = 18;       /* size of frame header (?) */
+                       break;
+               }
+/*             break; */
+/*     } */
+
+       cam = &gspca_dev->cam;
+       cam->dev_name = (char *) id->driver_info;
+       cam->epaddr = 0x01;
+       if (!sif) {
+               cam->cam_mode = vga_mode;
+               cam->nmodes = ARRAY_SIZE(vga_mode);
+               if (sd->sensor == SENSOR_OV7630_3) {
+                       /* We only have 320x240 & 640x480 */
+                       cam->cam_mode++;
+                       cam->nmodes--;
+               }
+       } else {
+               cam->cam_mode = sif_mode;
+               cam->nmodes = ARRAY_SIZE(sif_mode);
+       }
+       sd->brightness = BRIGHTNESS_DEF;
+       sd->gain = GAIN_DEF;
+       sd->exposure = EXPOSURE_DEF;
+       sd->freq = FREQ_DEF;
+       sd->contrast = CONTRAST_DEF;
+       sd->saturation = SATURATION_DEF;
+       sd->hue = HUE_DEF;
+       if (sd->sensor == SENSOR_OV7630_3)      /* jfm: from win trace */
+               reg_w(gspca_dev, 0x01, probe_ov7630, sizeof probe_ov7630);
+       return 0;
+}
+
+/* this function is called at open time */
+static int sd_open(struct gspca_dev *gspca_dev)
+{
+       reg_r(gspca_dev, 0x00);
+       if (gspca_dev->usb_buf[0] != 0x10)
+               return -ENODEV;
+       return 0;
+}
+
+static void pas106_i2cinit(struct gspca_dev *gspca_dev)
+{
+       int i;
+       const __u8 *data;
+       __u8 i2c1[] = { 0xa1, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14 };
+
+       i = ARRAY_SIZE(pas106_data);
+       data = pas106_data[0];
+       while (--i >= 0) {
+               memcpy(&i2c1[2], data, 2);
+                                       /* copy 2 bytes from the template */
+               if (i2c_w(gspca_dev, i2c1) < 0)
+                       PDEBUG(D_ERR, "i2c error pas106");
+               data += 2;
+       }
+}
+
+/* -- start the camera -- */
+static void sd_start(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       int mode, l;
+       const __u8 *sn9c10x;
+       __u8 reg01, reg17;
+       __u8 reg17_19[3];
+
+       mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;
+       switch (sd->sensor) {
+       case SENSOR_HV7131R:
+               sn9c10x = initHv7131;
+               reg17_19[0] = 0x60;
+               reg17_19[1] = (mode << 4) | 0x8a;
+               reg17_19[2] = 0x20;
+               break;
+       case SENSOR_OV6650:
+               sn9c10x = initOv6650;
+               reg17_19[0] = 0x68;
+               reg17_19[1] = (mode << 4) | 0x8b;
+               reg17_19[2] = 0x20;
+               break;
+       case SENSOR_OV7630:
+               sn9c10x = initOv7630;
+               reg17_19[0] = 0x68;
+               reg17_19[1] = (mode << 4) | COMP2;
+               reg17_19[2] = MCK_INIT1;
+               break;
+       case SENSOR_OV7630_3:
+               sn9c10x = initOv7630_3;
+               reg17_19[0] = 0x68;
+               reg17_19[1] = (mode << 4) | COMP2;
+               reg17_19[2] = MCK_INIT1;
+               break;
+       case SENSOR_PAS106:
+               sn9c10x = initPas106;
+               reg17_19[0] = 0x24;             /* 0x28 */
+               reg17_19[1] = (mode << 4) | COMP1;
+               reg17_19[2] = MCK_INIT1;
+               break;
+       case SENSOR_PAS202:
+               sn9c10x = initPas202;
+               reg17_19[0] = mode ? 0x24 : 0x20;
+               reg17_19[1] = (mode << 4) | 0x89;
+               reg17_19[2] = 0x20;
+               break;
+       case SENSOR_TAS5110:
+               sn9c10x = initTas5110;
+               reg17_19[0] = 0x60;
+               reg17_19[1] = (mode << 4) | 0x86;
+               reg17_19[2] = 0x2b;             /* 0xf3; */
+               break;
+       default:
+/*     case SENSOR_TAS5130CXX: */
+               sn9c10x = initTas5130;
+               reg17_19[0] = 0x60;
+               reg17_19[1] = (mode << 4) | COMP;
+               reg17_19[2] = mode ? 0x23 : 0x43;
+               break;
+       }
+       switch (sd->sensor) {
+       case SENSOR_OV7630:
+               reg01 = 0x06;
+               reg17 = 0x29;
+               l = sizeof initOv7630;
+               break;
+       case SENSOR_OV7630_3:
+               reg01 = 0x44;
+               reg17 = 0x68;
+               l = sizeof initOv7630_3;
+               break;
+       default:
+               reg01 = sn9c10x[0];
+               reg17 = sn9c10x[0x17 - 1];
+               l = 0x1f;
+               break;
+       }
+
+       /* reg 0x01 bit 2 video transfert on */
+       reg_w(gspca_dev, 0x01, &reg01, 1);
+       /* reg 0x17 SensorClk enable inv Clk 0x60 */
+       reg_w(gspca_dev, 0x17, &reg17, 1);
+/*fixme: for ov7630 102
+       reg_w(gspca_dev, 0x01, {0x06, sn9c10x[1]}, 2); */
+       /* Set the registers from the template */
+       reg_w_big(gspca_dev, 0x01, sn9c10x, l);
+       switch (sd->sensor) {
+       case SENSOR_HV7131R:
+               i2c_w_vector(gspca_dev, hv7131_sensor_init,
+                               sizeof hv7131_sensor_init);
+               break;
+       case SENSOR_OV6650:
+               i2c_w_vector(gspca_dev, ov6650_sensor_init,
+                               sizeof ov6650_sensor_init);
+               break;
+       case SENSOR_OV7630:
+               i2c_w_vector(gspca_dev, ov7630_sensor_init_com,
+                               sizeof ov7630_sensor_init_com);
+               msleep(200);
+               i2c_w_vector(gspca_dev, ov7630_sensor_init,
+                               sizeof ov7630_sensor_init);
+               break;
+       case SENSOR_OV7630_3:
+               i2c_w_vector(gspca_dev, ov7630_sensor_init_com,
+                               sizeof ov7630_sensor_init_com);
+               msleep(200);
+               i2c_w(gspca_dev, ov7630_sensor_init_3[mode]);
+               break;
+       case SENSOR_PAS106:
+               pas106_i2cinit(gspca_dev);
+               break;
+       case SENSOR_PAS202:
+               i2c_w_vector(gspca_dev, pas202_sensor_init,
+                               sizeof pas202_sensor_init);
+               break;
+       case SENSOR_TAS5110:
+               i2c_w_vector(gspca_dev, tas5110_sensor_init,
+                               sizeof tas5110_sensor_init);
+               break;
+       default:
+/*     case SENSOR_TAS5130CXX: */
+               i2c_w_vector(gspca_dev, tas5130_sensor_init,
+                               sizeof tas5130_sensor_init);
+               break;
+       }
+       /* H_size V_size 0x28, 0x1e -> 640x480. 0x16, 0x12 -> 352x288 */
+       reg_w(gspca_dev, 0x15, &sn9c10x[0x15 - 1], 2);
+       /* compression register */
+       reg_w(gspca_dev, 0x18, &reg17_19[1], 1);
+       /* H_start */
+       reg_w(gspca_dev, 0x12, &sn9c10x[0x12 - 1], 1);
+       /* V_START */
+       reg_w(gspca_dev, 0x13, &sn9c10x[0x13 - 1], 1);
+       /* reset 0x17 SensorClk enable inv Clk 0x60 */
+                               /*fixme: ov7630 [17]=68 8f (+20 if 102)*/
+       reg_w(gspca_dev, 0x17, &reg17_19[0], 1);
+       /*MCKSIZE ->3 */        /*fixme: not ov7630*/
+       reg_w(gspca_dev, 0x19, &reg17_19[2], 1);
+       /* AE_STRX AE_STRY AE_ENDX AE_ENDY */
+       reg_w(gspca_dev, 0x1c, &sn9c10x[0x1c - 1], 4);
+       /* Enable video transfert */
+       reg_w(gspca_dev, 0x01, &sn9c10x[0], 1);
+       /* Compression */
+       reg_w(gspca_dev, 0x18, &reg17_19[1], 2);
+       msleep(20);
+
+       setgain(gspca_dev);
+       setbrightness(gspca_dev);
+       setexposure(gspca_dev);
+       setfreq(gspca_dev);
+       setsaturation(gspca_dev);
+       sethue(gspca_dev);
+       setcontrast(gspca_dev);
+
+       sd->autogain_ignore_frames = 0;
+       atomic_set(&sd->avg_lum, -1);
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+       __u8 ByteSend;
+
+       ByteSend = 0x09;        /* 0X00 */
+       reg_w(gspca_dev, 0x01, &ByteSend, 1);
+}
+
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+}
+
+static void sd_close(struct gspca_dev *gspca_dev)
+{
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+                       struct gspca_frame *frame,      /* target */
+                       unsigned char *data,            /* isoc packet */
+                       int len)                        /* iso packet length */
+{
+       int i;
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       /* frames start with:
+        *      ff ff 00 c4 c4 96       synchro
+        *      00              (unknown)
+        *      xx              (frame sequence / size / compression)
+        *      (xx)            (idem - extra byte for sn9c103)
+        *      ll mm           brightness sum inside auto exposure
+        *      ll mm           brightness sum outside auto exposure
+        *      (xx xx xx xx xx)        audio values for snc103
+        */
+       if (len > 6 && len < 24) {
+               for (i = 0; i < len - 6; i++) {
+                       if (data[0 + i] == 0xff
+                           && data[1 + i] == 0xff
+                           && data[2 + i] == 0x00
+                           && data[3 + i] == 0xc4
+                           && data[4 + i] == 0xc4
+                           && data[5 + i] == 0x96) {   /* start of frame */
+                               frame = gspca_frame_add(gspca_dev, LAST_PACKET,
+                                                       frame, data, 0);
+                               if (len - i < sd->fr_h_sz) {
+                                       atomic_set(&sd->avg_lum, -1);
+                                       PDEBUG(D_STREAM, "packet too short to"
+                                               " get avg brightness");
+                               } else if (sd->fr_h_sz == 12) {
+                                       atomic_set(&sd->avg_lum,
+                                               data[i + 8] +
+                                                       (data[i + 9] << 8));
+                               } else {
+                                       atomic_set(&sd->avg_lum,
+                                               data[i + 9] +
+                                                       (data[i + 10] << 8));
+                               }
+                               data += i + sd->fr_h_sz;
+                               len -= i + sd->fr_h_sz;
+                               gspca_frame_add(gspca_dev, FIRST_PACKET,
+                                               frame, data, len);
+                               return;
+                       }
+               }
+       }
+       gspca_frame_add(gspca_dev, INTER_PACKET,
+                       frame, data, len);
+}
+
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->brightness = val;
+       if (gspca_dev->streaming)
+               setbrightness(gspca_dev);
+       return 0;
+}
+
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->brightness;
+       return 0;
+}
+
+static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->gain = val;
+       if (gspca_dev->streaming)
+               setgain(gspca_dev);
+       return 0;
+}
+
+static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->gain;
+       return 0;
+}
+
+static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->exposure = val;
+       if (gspca_dev->streaming)
+               setexposure(gspca_dev);
+       return 0;
+}
+
+static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->exposure;
+       return 0;
+}
+
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->autogain = val;
+       /* when switching to autogain set defaults to make sure
+          we are on a valid point of the autogain gain /
+          exposure knee graph, and give this change time to
+          take effect before doing autogain. */
+       if (sd->autogain) {
+               sd->exposure = EXPOSURE_DEF;
+               sd->gain = GAIN_DEF;
+               if (gspca_dev->streaming) {
+                       sd->autogain_ignore_frames = AUTOGAIN_IGNORE_FRAMES;
+                       setexposure(gspca_dev);
+                       setgain(gspca_dev);
+               }
+       }
+
+       return 0;
+}
+
+static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->autogain;
+       return 0;
+}
+
+static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->freq = val;
+       if (gspca_dev->streaming)
+               setfreq(gspca_dev);
+       return 0;
+}
+
+static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->freq;
+       return 0;
+}
+
+static int sd_setsaturation(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->saturation = val;
+       if (gspca_dev->streaming)
+               setsaturation(gspca_dev);
+       return 0;
+}
+
+static int sd_getsaturation(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->saturation;
+       return 0;
+}
+
+static int sd_sethue(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->hue = val;
+       if (gspca_dev->streaming)
+               sethue(gspca_dev);
+       return 0;
+}
+
+static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->hue;
+       return 0;
+}
+
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->contrast = val;
+       if (gspca_dev->streaming)
+               setcontrast(gspca_dev);
+       return 0;
+}
+
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->contrast;
+       return 0;
+}
+
+static int sd_querymenu(struct gspca_dev *gspca_dev,
+                       struct v4l2_querymenu *menu)
+{
+       switch (menu->id) {
+       case V4L2_CID_POWER_LINE_FREQUENCY:
+               switch (menu->index) {
+               case 0:         /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */
+                       strcpy((char *) menu->name, "NoFliker");
+                       return 0;
+               case 1:         /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */
+                       strcpy((char *) menu->name, "50 Hz");
+                       return 0;
+               case 2:         /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */
+                       strcpy((char *) menu->name, "60 Hz");
+                       return 0;
+               }
+               break;
+       }
+       return -EINVAL;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+       .name = MODULE_NAME,
+       .ctrls = sd_ctrls,
+       .nctrls = ARRAY_SIZE(sd_ctrls),
+       .config = sd_config,
+       .open = sd_open,
+       .start = sd_start,
+       .stopN = sd_stopN,
+       .stop0 = sd_stop0,
+       .close = sd_close,
+       .pkt_scan = sd_pkt_scan,
+       .querymenu = sd_querymenu,
+};
+
+/* -- module initialisation -- */
+#define DVNM(name) .driver_info = (kernel_ulong_t) name
+static __devinitdata struct usb_device_id device_table[] = {
+#ifndef CONFIG_USB_SN9C102
+       {USB_DEVICE(0x0c45, 0x6001), DVNM("Genius VideoCAM NB")},
+       {USB_DEVICE(0x0c45, 0x6005), DVNM("Sweex Tas5110")},
+       {USB_DEVICE(0x0c45, 0x6007), DVNM("Sonix sn9c101 + Tas5110D")},
+       {USB_DEVICE(0x0c45, 0x6009), DVNM("spcaCam@120")},
+       {USB_DEVICE(0x0c45, 0x600d), DVNM("spcaCam@120")},
+#endif
+       {USB_DEVICE(0x0c45, 0x6011), DVNM("MAX Webcam Microdia")},
+#ifndef CONFIG_USB_SN9C102
+       {USB_DEVICE(0x0c45, 0x6019), DVNM("Generic Sonix OV7630")},
+       {USB_DEVICE(0x0c45, 0x6024), DVNM("Generic Sonix Tas5130c")},
+       {USB_DEVICE(0x0c45, 0x6025), DVNM("Xcam Shanga")},
+       {USB_DEVICE(0x0c45, 0x6028), DVNM("Sonix Btc Pc380")},
+       {USB_DEVICE(0x0c45, 0x6029), DVNM("spcaCam@150")},
+       {USB_DEVICE(0x0c45, 0x602c), DVNM("Generic Sonix OV7630")},
+       {USB_DEVICE(0x0c45, 0x602d), DVNM("LIC-200 LG")},
+       {USB_DEVICE(0x0c45, 0x602e), DVNM("Genius VideoCam Messenger")},
+       {USB_DEVICE(0x0c45, 0x60af), DVNM("Trust WB3100P")},
+       {USB_DEVICE(0x0c45, 0x60b0), DVNM("Genius VideoCam Look")},
+#endif
+       {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+                       const struct usb_device_id *id)
+{
+       return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+                               THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+       .name = MODULE_NAME,
+       .id_table = device_table,
+       .probe = sd_probe,
+       .disconnect = gspca_disconnect,
+};
+
+/* -- module insert / remove -- */
+static int __init sd_mod_init(void)
+{
+       if (usb_register(&sd_driver) < 0)
+               return -1;
+       PDEBUG(D_PROBE, "v%s registered", version);
+       return 0;
+}
+static void __exit sd_mod_exit(void)
+{
+       usb_deregister(&sd_driver);
+       PDEBUG(D_PROBE, "deregistered");
+}
+
+module_init(sd_mod_init);
+module_exit(sd_mod_exit);
diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c
new file mode 100644 (file)
index 0000000..3e68b99
--- /dev/null
@@ -0,0 +1,1671 @@
+/*
+ *             Sonix sn9c102p sn9c105 sn9c120 (jpeg) library
+ *             Copyright (C) 2005 Michel Xhaard mxhaard@magic.fr
+ *
+ * V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
+ *
+ * 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
+ * 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
+ */
+
+#define MODULE_NAME "sonixj"
+
+#include "gspca.h"
+#include "jpeg.h"
+
+#define DRIVER_VERSION_NUMBER  KERNEL_VERSION(2, 1, 7)
+static const char version[] = "2.1.7";
+
+MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
+MODULE_DESCRIPTION("GSPCA/SONIX JPEG USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* specific webcam descriptor */
+struct sd {
+       struct gspca_dev gspca_dev;     /* !! must be the first item */
+
+       int avg_lum;
+       unsigned int exposure;
+
+       unsigned short brightness;
+       unsigned char contrast;
+       unsigned char colors;
+       unsigned char autogain;
+
+       signed char ag_cnt;
+#define AG_CNT_START 13
+
+       char qindex;
+       unsigned char bridge;
+#define BRIDGE_SN9C102P 0
+#define BRIDGE_SN9C105 1
+#define BRIDGE_SN9C110 2
+#define BRIDGE_SN9C120 3
+#define BRIDGE_SN9C325 4
+       char sensor;                    /* Type of image sensor chip */
+#define SENSOR_HV7131R 0
+#define SENSOR_MI0360 1
+#define SENSOR_MO4000 2
+#define SENSOR_OV7648 3
+#define SENSOR_OV7660 4
+       unsigned char i2c_base;
+};
+
+/* V4L2 controls supported by the driver */
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
+
+static struct ctrl sd_ctrls[] = {
+       {
+           {
+               .id      = V4L2_CID_BRIGHTNESS,
+               .type    = V4L2_CTRL_TYPE_INTEGER,
+               .name    = "Brightness",
+               .minimum = 0,
+               .maximum = 0xffff,
+               .step    = 1,
+#define BRIGHTNESS_DEF 0x7fff
+               .default_value = BRIGHTNESS_DEF,
+           },
+           .set = sd_setbrightness,
+           .get = sd_getbrightness,
+       },
+       {
+           {
+               .id      = V4L2_CID_CONTRAST,
+               .type    = V4L2_CTRL_TYPE_INTEGER,
+               .name    = "Contrast",
+               .minimum = 0,
+               .maximum = 127,
+               .step    = 1,
+#define CONTRAST_DEF 63
+               .default_value = CONTRAST_DEF,
+           },
+           .set = sd_setcontrast,
+           .get = sd_getcontrast,
+       },
+       {
+           {
+               .id      = V4L2_CID_SATURATION,
+               .type    = V4L2_CTRL_TYPE_INTEGER,
+               .name    = "Color",
+               .minimum = 0,
+               .maximum = 255,
+               .step    = 1,
+#define COLOR_DEF 127
+               .default_value = COLOR_DEF,
+           },
+           .set = sd_setcolors,
+           .get = sd_getcolors,
+       },
+       {
+           {
+               .id      = V4L2_CID_AUTOGAIN,
+               .type    = V4L2_CTRL_TYPE_BOOLEAN,
+               .name    = "Auto Gain",
+               .minimum = 0,
+               .maximum = 1,
+               .step    = 1,
+#define AUTOGAIN_DEF 1
+               .default_value = AUTOGAIN_DEF,
+           },
+           .set = sd_setautogain,
+           .get = sd_getautogain,
+       },
+};
+
+static struct v4l2_pix_format vga_mode[] = {
+       {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+               .bytesperline = 160,
+               .sizeimage = 160 * 120 * 3 / 8 + 590,
+               .colorspace = V4L2_COLORSPACE_JPEG,
+               .priv = 2},
+       {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+               .bytesperline = 320,
+               .sizeimage = 320 * 240 * 3 / 8 + 590,
+               .colorspace = V4L2_COLORSPACE_JPEG,
+               .priv = 1},
+       {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+               .bytesperline = 640,
+               .sizeimage = 640 * 480 * 3 / 8 + 590,
+               .colorspace = V4L2_COLORSPACE_JPEG,
+               .priv = 0},
+};
+
+/*Data from sn9c102p+hv71331r */
+static const __u8 sn_hv7131[] = {
+/*     reg0  reg1  reg2  reg3  reg4  reg5  reg6  reg7  reg8  reg9 */
+       0x00, 0x03, 0x64, 0x00, 0x1A, 0x20, 0x20, 0x20, 0xA1, 0x11,
+/*     rega  regb  regc  regd  rege  regf  reg10 reg11 */
+       0x02, 0x09, 0x00, 0x00, 0x00, 0x10, 0x03, 0x00,         /* 00 */
+/*     reg12 reg13 reg14 reg15 reg16 reg17 reg18 reg19 reg1a reg1b */
+       0x00, 0x01, 0x03, 0x28, 0x1e, 0x41, 0x0a, 0x00, 0x00, 0x00,
+/*     reg1c reg1d reg1e reg1f reg20 reg21 reg22 reg23 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static const __u8 sn_mi0360[] = {
+/*     reg0  reg1  reg2  reg3  reg4  reg5  reg6  reg7  reg8  reg9 */
+       0x00, 0x61, 0x44, 0x00, 0x1a, 0x20, 0x20, 0x20, 0xb1, 0x5d,
+/*     rega  regb  regc  regd  rege  regf  reg10 reg11 */
+       0x07, 0x00, 0x00, 0x00, 0x00, 0x10, 0x03, 0x00,
+/*     reg12 reg13 reg14 reg15 reg16 reg17 reg18 reg19 reg1a reg1b */
+       0x00, 0x02, 0x0a, 0x28, 0x1e, 0x61, 0x06, 0x00, 0x00, 0x00,
+/*     reg1c reg1d reg1e reg1f reg20 reg21 reg22 reg23 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static const __u8 sn_mo4000[] = {
+/*     reg0    reg1    reg2    reg3    reg4    reg5    reg6    reg7    reg8 */
+       0x12,   0x23,   0x60,   0x00,   0x1A,   0x00,   0x20,   0x18,   0x81,
+/*     reg9    rega    regb    regc    regd    rege    regf    reg10   reg11*/
+       0x21,   0x00,   0x00,   0x00,   0x00,   0x00,   0x00,   0x03,   0x00,
+/*     reg12   reg13   reg14   reg15   reg16   reg17   reg18   reg19   reg1a*/
+       0x0b,   0x0f,   0x14,   0x28,   0x1e,   0x40,   0x08,   0x00,   0x00,
+/*     reg1b   reg1c   reg1d   reg1e   reg1f   reg20   reg21   reg22   reg23*/
+       0x00,   0x00,   0x00,   0x00,   0x00,   0x08,   0x25,   0x39,   0x4b,
+       0x5c,   0x6b,   0x79,   0x87,   0x95,   0xa2,   0xaf,   0xbb,   0xc7,
+       0xd3,   0xdf,   0xea,   0xf5
+};
+
+static const __u8 sn_ov7648[] = {
+       0x00, 0x21, 0x62, 0x00, 0x1a, 0x20, 0x20, 0x20, 0xA1, 0x6E, 0x18, 0x65,
+       0x00, 0x00, 0x00, 0x10, 0x03, 0x00, 0x00, 0x06, 0x06, 0x28, 0x1E, 0x82,
+       0x07, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static const __u8 sn_ov7660[]  = {
+/*     reg0    reg1    reg2    reg3    reg4    reg5    reg6    reg7    reg8 */
+       0x00,   0x61,   0x40,   0x00,   0x1a,   0x00,   0x00,   0x00,   0x81,
+/*     reg9    rega    regb    regc    regd    rege    regf    reg10   reg11*/
+       0x21,   0x00,   0x00,   0x00,   0x00,   0x00,   0x00,   0x03,   0x00,
+/*     reg12   reg13   reg14   reg15   reg16   reg17   reg18   reg19   reg1a*/
+       0x01,   0x01,   0x14,   0x28,   0x1e,   0x00,   0x07,   0x00,   0x00,
+/*     reg1b   reg1c   reg1d   reg1e   reg1f   reg20   reg21   reg22   reg23*/
+       0x00,   0x00,   0x00,   0x00,   0x00,   0x00,   0x00,   0x00,   0x00
+};
+
+/* sequence specific to the sensors - !! index = SENSOR_xxx */
+static const __u8 *sn_tb[] = {
+       sn_hv7131,
+       sn_mi0360,
+       sn_mo4000,
+       sn_ov7648,
+       sn_ov7660
+};
+
+static const __u8 regsn20[] = {
+       0x00, 0x2d, 0x46, 0x5a, 0x6c, 0x7c, 0x8b, 0x99,
+       0xa6, 0xb2, 0xbf, 0xca, 0xd5, 0xe0, 0xeb, 0xf5, 0xff
+};
+static const __u8 regsn20_sn9c120[] = {
+       0x00, 0x25, 0x3c, 0x50, 0x62, 0x72, 0x81, 0x90,
+       0x9e, 0xab, 0xb8, 0xc5, 0xd1, 0xdd, 0xe9, 0xf4, 0xff
+};
+static const __u8 regsn20_sn9c325[] = {
+       0x0a, 0x3a, 0x56, 0x6c, 0x7e, 0x8d, 0x9a, 0xa4,
+       0xaf, 0xbb, 0xc5, 0xcd, 0xd5, 0xde, 0xe8, 0xed, 0xf5
+};
+
+static const __u8 reg84[] = {
+       0x14, 0x00, 0x27, 0x00, 0x07, 0x00, 0xe5, 0x0f,
+       0xe4, 0x0f, 0x38, 0x00, 0x3e, 0x00, 0xc3, 0x0f,
+/*     0x00, 0x00, 0x00, 0x00, 0x00 */
+       0xf7, 0x0f, 0x0a, 0x00, 0x00
+};
+static const __u8 reg84_sn9c120_1[] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x0c, 0x00, 0x00
+};
+static const __u8 reg84_sn9c120_2[] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x0c, 0x02, 0x3b
+};
+static const __u8 reg84_sn9c120_3[] = {
+       0x14, 0x00, 0x27, 0x00, 0x08, 0x00, 0xeb, 0x0f,
+       0xd5, 0x0f, 0x42, 0x00, 0x41, 0x00, 0xca, 0x0f,
+       0xf5, 0x0f, 0x0c, 0x02, 0x3b
+};
+static const __u8 reg84_sn9c325[] = {
+       0x14, 0x00, 0x27, 0x00, 0x07, 0x00, 0xe4, 0x0f,
+       0xd3, 0x0f, 0x4b, 0x00, 0x48, 0x00, 0xc0, 0x0f,
+       0xf8, 0x0f, 0x00, 0x00, 0x00
+};
+
+static const __u8 hv7131r_sensor_init[][8] = {
+       {0xC1, 0x11, 0x01, 0x08, 0x01, 0x00, 0x00, 0x10},
+       {0xB1, 0x11, 0x34, 0x17, 0x7F, 0x00, 0x00, 0x10},
+       {0xD1, 0x11, 0x40, 0xFF, 0x7F, 0x7F, 0x7F, 0x10},
+       {0x91, 0x11, 0x44, 0x00, 0x00, 0x00, 0x00, 0x10},
+       {0xD1, 0x11, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10},
+       {0xD1, 0x11, 0x14, 0x01, 0xE2, 0x02, 0x82, 0x10},
+       {0x91, 0x11, 0x18, 0x00, 0x00, 0x00, 0x00, 0x10},
+
+       {0xA1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10},
+       {0xA1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10},
+       {0xC1, 0x11, 0x25, 0x00, 0x61, 0xA8, 0x00, 0x10},
+       {0xA1, 0x11, 0x30, 0x22, 0x00, 0x00, 0x00, 0x10},
+       {0xC1, 0x11, 0x31, 0x20, 0x2E, 0x20, 0x00, 0x10},
+       {0xC1, 0x11, 0x25, 0x00, 0xC3, 0x50, 0x00, 0x10},
+       {0xA1, 0x11, 0x30, 0x07, 0x00, 0x00, 0x00, 0x10}, /* gain14 */
+       {0xC1, 0x11, 0x31, 0x10, 0x10, 0x10, 0x00, 0x10}, /* r g b 101a10 */
+
+       {0xA1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10},
+       {0xA1, 0x11, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10},
+       {0xA1, 0x11, 0x21, 0xD0, 0x00, 0x00, 0x00, 0x10},
+       {0xA1, 0x11, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10},
+       {0xA1, 0x11, 0x23, 0x09, 0x00, 0x00, 0x00, 0x10},
+
+       {0xA1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10},
+       {0xA1, 0x11, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10},
+       {0xA1, 0x11, 0x21, 0xD0, 0x00, 0x00, 0x00, 0x10},
+       {0xA1, 0x11, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10},
+       {0xA1, 0x11, 0x23, 0x10, 0x00, 0x00, 0x00, 0x10},
+       {}
+};
+static const __u8 mi0360_sensor_init[][8] = {
+       {0xB1, 0x5D, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10},
+       {0xB1, 0x5D, 0x0D, 0x00, 0x01, 0x00, 0x00, 0x10},
+       {0xB1, 0x5D, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x10},
+       {0xD1, 0x5D, 0x01, 0x00, 0x08, 0x00, 0x16, 0x10},
+       {0xD1, 0x5D, 0x03, 0x01, 0xE2, 0x02, 0x82, 0x10},
+       {0xD1, 0x5D, 0x05, 0x00, 0x09, 0x00, 0x53, 0x10},
+       {0xB1, 0x5D, 0x0D, 0x00, 0x02, 0x00, 0x00, 0x10},
+       {0xD1, 0x5D, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x10},
+       {0xD1, 0x5D, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x10},
+       {0xD1, 0x5D, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x10},
+       {0xD1, 0x5D, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10},
+       {0xD1, 0x5D, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10},
+       {0xD1, 0x5D, 0x14, 0x00, 0x00, 0x00, 0x00, 0x10},
+       {0xD1, 0x5D, 0x16, 0x00, 0x00, 0x00, 0x00, 0x10},
+       {0xD1, 0x5D, 0x18, 0x00, 0x00, 0x00, 0x00, 0x10},
+       {0xD1, 0x5D, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x10},
+       {0xD1, 0x5D, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x10},
+       {0xB1, 0x5D, 0x32, 0x00, 0x00, 0x00, 0x00, 0x10},
+       {0xD1, 0x5D, 0x20, 0x91, 0x01, 0x00, 0x00, 0x10},
+       {0xD1, 0x5D, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10},
+       {0xD1, 0x5D, 0x24, 0x00, 0x00, 0x00, 0x00, 0x10},
+       {0xD1, 0x5D, 0x26, 0x00, 0x00, 0x00, 0x24, 0x10},
+       {0xD1, 0x5D, 0x2F, 0xF7, 0xB0, 0x00, 0x04, 0x10},
+       {0xD1, 0x5D, 0x31, 0x00, 0x00, 0x00, 0x00, 0x10},
+       {0xD1, 0x5D, 0x33, 0x00, 0x00, 0x01, 0x00, 0x10},
+       {0xB1, 0x5D, 0x3D, 0x06, 0x8F, 0x00, 0x00, 0x10},
+       {0xD1, 0x5D, 0x40, 0x01, 0xE0, 0x00, 0xD1, 0x10},
+       {0xB1, 0x5D, 0x44, 0x00, 0x82, 0x00, 0x00, 0x10},
+       {0xD1, 0x5D, 0x58, 0x00, 0x78, 0x00, 0x43, 0x10},
+       {0xD1, 0x5D, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x10},
+       {0xD1, 0x5D, 0x5C, 0x00, 0x00, 0x00, 0x00, 0x10},
+       {0xD1, 0x5D, 0x5E, 0x00, 0x00, 0xA3, 0x1D, 0x10},
+       {0xB1, 0x5D, 0x62, 0x04, 0x11, 0x00, 0x00, 0x10},
+
+       {0xB1, 0x5D, 0x20, 0x91, 0x01, 0x00, 0x00, 0x10},
+       {0xB1, 0x5D, 0x20, 0x11, 0x01, 0x00, 0x00, 0x10},
+       {0xB1, 0x5D, 0x09, 0x00, 0x64, 0x00, 0x00, 0x10},
+       {0xD1, 0x5D, 0x2B, 0x00, 0xA0, 0x00, 0xB0, 0x10},
+       {0xD1, 0x5D, 0x2D, 0x00, 0xA0, 0x00, 0xA0, 0x10},
+
+       {0xB1, 0x5D, 0x0A, 0x00, 0x02, 0x00, 0x00, 0x10}, /* sensor clck ?2 */
+       {0xB1, 0x5D, 0x06, 0x00, 0x30, 0x00, 0x00, 0x10},
+       {0xB1, 0x5D, 0x05, 0x00, 0x0A, 0x00, 0x00, 0x10},
+       {0xB1, 0x5D, 0x09, 0x02, 0x35, 0x00, 0x00, 0x10}, /* exposure 2 */
+
+       {0xD1, 0x5D, 0x2B, 0x00, 0xB9, 0x00, 0xE3, 0x10},
+       {0xD1, 0x5D, 0x2D, 0x00, 0x5f, 0x00, 0xB9, 0x10}, /* 42 */
+/*     {0xB1, 0x5D, 0x35, 0x00, 0x67, 0x00, 0x00, 0x10}, * gain orig */
+/*     {0xB1, 0x5D, 0x35, 0x00, 0x20, 0x00, 0x00, 0x10}, * gain */
+       {0xB1, 0x5D, 0x07, 0x00, 0x03, 0x00, 0x00, 0x10}, /* update */
+       {0xB1, 0x5D, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10}, /* sensor on */
+       {}
+};
+static const __u8 mo4000_sensor_init[][8] = {
+       {0xa1, 0x21, 0x01, 0x02, 0x00, 0x00, 0x00, 0x10},
+       {0xa1, 0x21, 0x02, 0x00, 0x00, 0x00, 0x00, 0x10},
+       {0xa1, 0x21, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10},
+       {0xa1, 0x21, 0x04, 0x00, 0x00, 0x00, 0x00, 0x10},
+       {0xa1, 0x21, 0x05, 0x00, 0x00, 0x00, 0x00, 0x10},
+       {0xa1, 0x21, 0x05, 0x04, 0x00, 0x00, 0x00, 0x10},
+       {0xa1, 0x21, 0x06, 0x80, 0x00, 0x00, 0x00, 0x10},
+       {0xa1, 0x21, 0x06, 0x81, 0x00, 0x00, 0x00, 0x10},
+       {0xa1, 0x21, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x10},
+       {0xa1, 0x21, 0x11, 0x00, 0x00, 0x00, 0x00, 0x10},
+       {0xa1, 0x21, 0x11, 0x20, 0x00, 0x00, 0x00, 0x10},
+       {0xa1, 0x21, 0x11, 0x30, 0x00, 0x00, 0x00, 0x10},
+       {0xa1, 0x21, 0x11, 0x38, 0x00, 0x00, 0x00, 0x10},
+       {0xa1, 0x21, 0x11, 0x38, 0x00, 0x00, 0x00, 0x10},
+       {0xa1, 0x21, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10},
+       {0xa1, 0x21, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10},
+       {0xa1, 0x21, 0x0f, 0x20, 0x00, 0x00, 0x00, 0x10},
+       {0xa1, 0x21, 0x10, 0x20, 0x00, 0x00, 0x00, 0x10},
+       {0xa1, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10},
+       {0xa1, 0x21, 0x11, 0x38, 0x00, 0x00, 0x00, 0x10},
+       {}
+};
+static const __u8 ov7660_sensor_init[][8] = {
+       {0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, /* reset SCCB */
+       {0xa1, 0x21, 0x12, 0x05, 0x00, 0x00, 0x00, 0x10},
+                                               /* Outformat ?? rawRGB */
+       {0xa1, 0x21, 0x13, 0xb8, 0x00, 0x00, 0x00, 0x10}, /* init COM8 */
+       {0xd1, 0x21, 0x00, 0x01, 0x74, 0x92, 0x00, 0x10},
+/*     {0xd1, 0x21, 0x00, 0x01, 0x74, 0x74, 0x00, 0x10}, */
+                                               /* GAIN BLUE RED VREF */
+       {0xd1, 0x21, 0x04, 0x00, 0x7d, 0x62, 0x00, 0x10},
+                                               /* COM 1 BAVE GEAVE AECHH */
+       {0xb1, 0x21, 0x08, 0x83, 0x01, 0x00, 0x00, 0x10}, /* RAVE COM2 */
+       {0xd1, 0x21, 0x0c, 0x00, 0x08, 0x04, 0x4f, 0x10}, /* COM 3 4 5 6 */
+       {0xd1, 0x21, 0x10, 0x7f, 0x40, 0x05, 0xf8, 0x10},
+/*     {0xd1, 0x21, 0x10, 0x7f, 0x40, 0x05, 0xff, 0x10}, */
+                                               /* AECH CLKRC COM7 COM8 */
+       {0xc1, 0x21, 0x14, 0x2c, 0x00, 0x02, 0x00, 0x10}, /* COM9 COM10 */
+       {0xd1, 0x21, 0x17, 0x10, 0x60, 0x02, 0x7b, 0x10},
+                                               /* HSTART HSTOP VSTRT VSTOP */
+       {0xa1, 0x21, 0x1b, 0x02, 0x00, 0x00, 0x00, 0x10}, /* PSHFT */
+       {0xb1, 0x21, 0x1e, 0x01, 0x0e, 0x00, 0x00, 0x10}, /* MVFP LAEC */
+       {0xd1, 0x21, 0x20, 0x07, 0x07, 0x07, 0x07, 0x10},
+                                       /* BOS GBOS GROS ROS (BGGR offset) */
+       {0xd1, 0x21, 0x24, 0x68, 0x58, 0xd4, 0x80, 0x10},
+/*     {0xd1, 0x21, 0x24, 0x78, 0x68, 0xd4, 0x80, 0x10}, */
+                                               /* AEW AEB VPT BBIAS */
+       {0xd1, 0x21, 0x28, 0x80, 0x30, 0x00, 0x00, 0x10},
+                                               /* GbBIAS RSVD EXHCH EXHCL */
+       {0xd1, 0x21, 0x2c, 0x80, 0x00, 0x00, 0x62, 0x10},
+                                               /* RBIAS ADVFL ASDVFH YAVE */
+       {0xc1, 0x21, 0x30, 0x08, 0x30, 0xb4, 0x00, 0x10},
+                                               /* HSYST HSYEN HREF */
+       {0xd1, 0x21, 0x33, 0x00, 0x07, 0x84, 0x00, 0x10}, /* reserved */
+       {0xd1, 0x21, 0x37, 0x0c, 0x02, 0x43, 0x00, 0x10},
+                                               /* ADC ACOM OFON TSLB */
+       {0xd1, 0x21, 0x3b, 0x02, 0x6c, 0x19, 0x0e, 0x10},
+                                               /* COM11 COM12 COM13 COM14 */
+       {0xd1, 0x21, 0x3f, 0x41, 0xc1, 0x22, 0x08, 0x10},
+                                               /* EDGE COM15 COM16 COM17 */
+       {0xd1, 0x21, 0x43, 0xf0, 0x10, 0x78, 0xa8, 0x10}, /* reserved */
+       {0xd1, 0x21, 0x47, 0x60, 0x80, 0x00, 0x00, 0x10}, /* reserved */
+       {0xd1, 0x21, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x10}, /* reserved */
+       {0xd1, 0x21, 0x4f, 0x46, 0x36, 0x0f, 0x17, 0x10}, /* MTX 1 2 3 4 */
+       {0xd1, 0x21, 0x53, 0x7f, 0x96, 0x40, 0x40, 0x10}, /* MTX 5 6 7 8 */
+       {0xb1, 0x21, 0x57, 0x40, 0x0f, 0x00, 0x00, 0x10}, /* MTX9 MTXS */
+       {0xd1, 0x21, 0x59, 0xba, 0x9a, 0x22, 0xb9, 0x10}, /* reserved */
+       {0xd1, 0x21, 0x5d, 0x9b, 0x10, 0xf0, 0x05, 0x10}, /* reserved */
+       {0xa1, 0x21, 0x61, 0x60, 0x00, 0x00, 0x00, 0x10}, /* reserved */
+       {0xd1, 0x21, 0x62, 0x00, 0x00, 0x50, 0x30, 0x10},
+                                               /* LCC1 LCC2 LCC3 LCC4 */
+       {0xa1, 0x21, 0x66, 0x00, 0x00, 0x00, 0x00, 0x10}, /* LCC5 */
+       {0xd1, 0x21, 0x67, 0x80, 0x7a, 0x90, 0x80, 0x10},
+       {0xa1, 0x21, 0x6b, 0x0a, 0x00, 0x00, 0x00, 0x10},
+                                       /* band gap reference [0..3] DBLV */
+       {0xd1, 0x21, 0x6c, 0x30, 0x48, 0x80, 0x74, 0x10}, /* gamma curve */
+       {0xd1, 0x21, 0x70, 0x64, 0x60, 0x5c, 0x58, 0x10}, /* gamma curve */
+       {0xd1, 0x21, 0x74, 0x54, 0x4c, 0x40, 0x38, 0x10}, /* gamma curve */
+       {0xd1, 0x21, 0x78, 0x34, 0x30, 0x2f, 0x2b, 0x10}, /* gamma curve */
+       {0xd1, 0x21, 0x7c, 0x03, 0x07, 0x17, 0x34, 0x10}, /* gamma curve */
+       {0xd1, 0x21, 0x80, 0x41, 0x4d, 0x58, 0x63, 0x10}, /* gamma curve */
+       {0xd1, 0x21, 0x84, 0x6e, 0x77, 0x87, 0x95, 0x10}, /* gamma curve */
+       {0xc1, 0x21, 0x88, 0xaf, 0xc7, 0xdf, 0x00, 0x10}, /* gamma curve */
+       {0xc1, 0x21, 0x8b, 0x99, 0x99, 0xcf, 0x00, 0x10}, /* reserved */
+       {0xb1, 0x21, 0x92, 0x00, 0x00, 0x00, 0x00, 0x10},
+/****** (some exchanges in the win trace) ******/
+       {0xa1, 0x21, 0x1e, 0x01, 0x00, 0x00, 0x00, 0x10},
+                                               /* bits[3..0]reserved */
+       {0xa1, 0x21, 0x1e, 0x01, 0x00, 0x00, 0x00, 0x10},
+       {0xa1, 0x21, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10},
+                                               /* VREF vertical frame ctrl */
+       {0xa1, 0x21, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10},
+       {0xa1, 0x21, 0x10, 0x20, 0x00, 0x00, 0x00, 0x10}, /* 0x20 */
+       {0xa1, 0x21, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x10},
+       {0xa1, 0x21, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x10},
+/*     {0xa1, 0x21, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x10}, */
+       {0xa1, 0x21, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x10},
+       {0xb1, 0x21, 0x01, 0x78, 0x78, 0x00, 0x00, 0x10},
+/****** (some exchanges in the win trace) ******/
+       {0xa1, 0x21, 0x93, 0x00, 0x00, 0x00, 0x00, 0x10},/* dummy line hight */
+       {0xa1, 0x21, 0x92, 0x25, 0x00, 0x00, 0x00, 0x10},/* dummy line low */
+       {0xa1, 0x21, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x10},
+       {0xa1, 0x21, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x10},
+       {0xa1, 0x21, 0x02, 0x90, 0x00, 0x00, 0x00, 0x10},
+/****** (some exchanges in the win trace) ******/
+/**********startsensor KO if changed !!****/
+       {0xa1, 0x21, 0x93, 0x01, 0x00, 0x00, 0x00, 0x10},
+       {0xa1, 0x21, 0x92, 0xff, 0x00, 0x00, 0x00, 0x10},
+       {0xa1, 0x21, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x10},
+       {0xa1, 0x21, 0x2b, 0xc3, 0x00, 0x00, 0x00, 0x10},
+/* here may start the isoc exchanges */
+       {}
+};
+/* reg0x04             reg0x07         reg 0x10 */
+/* expo  = (COM1 & 0x02) | (AECHH & 0x2f <<10) [ (AECh << 2) */
+
+static const __u8 ov7648_sensor_init[][8] = {
+       {0xC1, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00},
+       {0xC1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00},
+       {0xC1, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00},
+       {0xA1, 0x6E, 0x3F, 0x20, 0x00, 0x00, 0x00, 0x10},
+       {0xA1, 0x6E, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x10},
+       {0xA1, 0x6E, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x10},
+       {0xD1, 0x6E, 0x04, 0x02, 0xB1, 0x02, 0x39, 0x10},
+       {0xD1, 0x6E, 0x08, 0x00, 0x01, 0x00, 0x00, 0x10},
+       {0xD1, 0x6E, 0x0C, 0x02, 0x7F, 0x01, 0xE0, 0x10},
+       {0xD1, 0x6E, 0x12, 0x03, 0x02, 0x00, 0x03, 0x10},
+       {0xD1, 0x6E, 0x16, 0x85, 0x40, 0x4A, 0x40, 0x10},
+       {0xC1, 0x6E, 0x1A, 0x00, 0x80, 0x00, 0x00, 0x10},
+       {0xD1, 0x6E, 0x1D, 0x08, 0x03, 0x00, 0x00, 0x10},
+       {0xD1, 0x6E, 0x23, 0x00, 0xB0, 0x00, 0x94, 0x10},
+       {0xD1, 0x6E, 0x27, 0x58, 0x00, 0x00, 0x00, 0x10},
+       {0xD1, 0x6E, 0x2D, 0x14, 0x35, 0x61, 0x84, 0x10},
+       {0xD1, 0x6E, 0x31, 0xA2, 0xBD, 0xD8, 0xFF, 0x10},
+       {0xD1, 0x6E, 0x35, 0x06, 0x1E, 0x12, 0x02, 0x10},
+       {0xD1, 0x6E, 0x39, 0xAA, 0x53, 0x37, 0xD5, 0x10},
+       {0xA1, 0x6E, 0x3D, 0xF2, 0x00, 0x00, 0x00, 0x10},
+       {0xD1, 0x6E, 0x3E, 0x00, 0x00, 0x80, 0x03, 0x10},
+       {0xD1, 0x6E, 0x42, 0x03, 0x00, 0x00, 0x00, 0x10},
+       {0xC1, 0x6E, 0x46, 0x00, 0x80, 0x80, 0x00, 0x10},
+       {0xD1, 0x6E, 0x4B, 0x02, 0xEF, 0x08, 0xCD, 0x10},
+       {0xD1, 0x6E, 0x4F, 0x00, 0xD0, 0x00, 0xA0, 0x10},
+       {0xD1, 0x6E, 0x53, 0x01, 0xAA, 0x01, 0x40, 0x10},
+       {0xD1, 0x6E, 0x5A, 0x50, 0x04, 0x30, 0x03, 0x10},
+       {0xA1, 0x6E, 0x5E, 0x00, 0x00, 0x00, 0x00, 0x10},
+       {0xD1, 0x6E, 0x5F, 0x10, 0x40, 0xFF, 0x00, 0x10},
+  /*   {0xD1, 0x6E, 0x63, 0x40, 0x40, 0x00, 0x00, 0x10},
+       {0xD1, 0x6E, 0x67, 0x00, 0x00, 0x00, 0x00, 0x10},
+ * This is currently setting a
+ * blue tint, and some things more , i leave it here for future test if
+ * somene is having problems with color on this sensor
+       {0xD1, 0x6E, 0x6B, 0x00, 0x00, 0x00, 0x00, 0x10},
+       {0xD1, 0x6E, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x10},
+       {0xC1, 0x6E, 0x73, 0x10, 0x80, 0xEB, 0x00, 0x10},
+       {0xA1, 0x6E, 0x1E, 0x03, 0x00, 0x00, 0x00, 0x10},
+       {0xA1, 0x6E, 0x15, 0x01, 0x00, 0x00, 0x00, 0x10},
+       {0xC1, 0x6E, 0x16, 0x40, 0x40, 0x40, 0x00, 0x10},
+       {0xA1, 0x6E, 0x1D, 0x08, 0x00, 0x00, 0x00, 0x10},
+       {0xA1, 0x6E, 0x06, 0x02, 0x00, 0x00, 0x00, 0x10},
+       {0xA1, 0x6E, 0x07, 0xB5, 0x00, 0x00, 0x00, 0x10},
+       {0xA1, 0x6E, 0x18, 0x6B, 0x00, 0x00, 0x00, 0x10},
+       {0xA1, 0x6E, 0x1D, 0x08, 0x00, 0x00, 0x00, 0x10},
+       {0xA1, 0x6E, 0x06, 0x02, 0x00, 0x00, 0x00, 0x10},
+       {0xA1, 0x6E, 0x07, 0xB8, 0x00, 0x00, 0x00, 0x10},  */
+       {0xC1, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00},
+       {0xA1, 0x6E, 0x06, 0x03, 0x00, 0x00, 0x00, 0x10}, /* Bright... */
+       {0xA1, 0x6E, 0x07, 0x66, 0x00, 0x00, 0x00, 0x10}, /* B.. */
+       {0xC1, 0x6E, 0x1A, 0x03, 0x65, 0x90, 0x00, 0x10}, /* Bright/Witen....*/
+/*     {0xC1, 0x6E, 0x16, 0x45, 0x40, 0x60, 0x00, 0x10},  * Bright/Witene */
+       {}
+};
+
+static const __u8 qtable4[] = {
+       0x06, 0x04, 0x04, 0x06, 0x04, 0x04, 0x06, 0x06, 0x06, 0x06, 0x08, 0x06,
+       0x06, 0x08, 0x0A, 0x11,
+       0x0A, 0x0A, 0x08, 0x08, 0x0A, 0x15, 0x0F, 0x0F, 0x0C, 0x11, 0x19, 0x15,
+       0x19, 0x19, 0x17, 0x15,
+       0x17, 0x17, 0x1B, 0x1D, 0x25, 0x21, 0x1B, 0x1D, 0x23, 0x1D, 0x17, 0x17,
+       0x21, 0x2E, 0x21, 0x23,
+       0x27, 0x29, 0x2C, 0x2C, 0x2C, 0x19, 0x1F, 0x30, 0x32, 0x2E, 0x29, 0x32,
+       0x25, 0x29, 0x2C, 0x29,
+       0x06, 0x08, 0x08, 0x0A, 0x08, 0x0A, 0x13, 0x0A, 0x0A, 0x13, 0x29, 0x1B,
+       0x17, 0x1B, 0x29, 0x29,
+       0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+       0x29, 0x29, 0x29, 0x29,
+       0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+       0x29, 0x29, 0x29, 0x29,
+       0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+       0x29, 0x29, 0x29, 0x29
+};
+
+/* read <len> bytes (len < sizeof gspca_dev->usb_buf) to gspca_dev->usb_buf */
+static void reg_r(struct gspca_dev *gspca_dev,
+                 __u16 value, int len)
+{
+       usb_control_msg(gspca_dev->dev,
+                       usb_rcvctrlpipe(gspca_dev->dev, 0),
+                       0,
+                       USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+                       value, 0,
+                       gspca_dev->usb_buf, len,
+                       500);
+}
+
+static void reg_w(struct gspca_dev *gspca_dev,
+                         __u16 value,
+                         const __u8 *buffer,
+                         int len)
+{
+       if (len <= sizeof gspca_dev->usb_buf) {
+               memcpy(gspca_dev->usb_buf, buffer, len);
+               usb_control_msg(gspca_dev->dev,
+                               usb_sndctrlpipe(gspca_dev->dev, 0),
+                               0x08,
+                          USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+                               value, 0,
+                               gspca_dev->usb_buf, len,
+                               500);
+       } else {
+               __u8 *tmpbuf;
+
+               tmpbuf = kmalloc(len, GFP_KERNEL);
+               memcpy(tmpbuf, buffer, len);
+               usb_control_msg(gspca_dev->dev,
+                               usb_sndctrlpipe(gspca_dev->dev, 0),
+                               0x08,
+                          USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+                               value, 0,
+                               tmpbuf, len,
+                               500);
+               kfree(tmpbuf);
+       }
+}
+
+/* I2C write 2 bytes */
+static void i2c_w2(struct gspca_dev *gspca_dev,
+                  const __u8 *buffer)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       __u8 mode[8];
+
+       /* is i2c ready */
+       mode[0] = 0x81 | (2 << 4);
+       mode[1] = sd->i2c_base;
+       mode[2] = buffer[0];
+       mode[3] = buffer[1];
+       mode[4] = 0;
+       mode[5] = 0;
+       mode[6] = 0;
+       mode[7] = 0x10;
+       reg_w(gspca_dev, 0x08, mode, 8);
+}
+
+/* I2C write 8 bytes */
+static void i2c_w8(struct gspca_dev *gspca_dev,
+                  const __u8 *buffer)
+{
+       reg_w(gspca_dev, 0x08, buffer, 8);
+       msleep(1);
+}
+
+/* read 5 bytes in gspca_dev->usb_buf */
+static void i2c_r5(struct gspca_dev *gspca_dev, __u8 reg)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       __u8 mode[8];
+
+       mode[0] = 0x81 | 0x10;
+       mode[1] = sd->i2c_base;
+       mode[2] = reg;
+       mode[3] = 0;
+       mode[4] = 0;
+       mode[5] = 0;
+       mode[6] = 0;
+       mode[7] = 0x10;
+       i2c_w8(gspca_dev, mode);
+       mode[0] = 0x81 | (5 << 4) | 0x02;
+       mode[2] = 0;
+       i2c_w8(gspca_dev, mode);
+       reg_r(gspca_dev, 0x0a, 5);
+}
+
+static int probesensor(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       __u8 reg02;
+       static const __u8 datasend[] = { 2, 0 };
+       /* reg val1 val2 val3 val4 */
+
+       i2c_w2(gspca_dev, datasend);
+/* should write 0xa1 0x11 0x02 0x00 0x00 0x00 0x00 the 0x10 is add by i2cw */
+       msleep(10);
+       reg02 = 0x66;
+       reg_w(gspca_dev, 0x02, &reg02, 1);              /* Gpio on */
+       msleep(10);
+       i2c_r5(gspca_dev, 0);                           /* read sensor id */
+       if (gspca_dev->usb_buf[0] == 0x02
+           && gspca_dev->usb_buf[1] == 0x09
+           && gspca_dev->usb_buf[2] == 0x01
+           && gspca_dev->usb_buf[3] == 0x00
+           && gspca_dev->usb_buf[4] == 0x00) {
+               PDEBUG(D_PROBE, "Find Sensor sn9c102P HV7131R");
+               sd->sensor = SENSOR_HV7131R;
+               return SENSOR_HV7131R;
+       }
+       PDEBUG(D_PROBE, "Find Sensor %d %d %d",
+               gspca_dev->usb_buf[0], gspca_dev->usb_buf[1],
+               gspca_dev->usb_buf[2]);
+       PDEBUG(D_PROBE, "Sensor sn9c102P Not found");
+       return -ENODEV;
+}
+
+static int configure_gpio(struct gspca_dev *gspca_dev,
+                         const __u8 *sn9c1xx)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       __u8 data;
+       __u8 regF1;
+       const __u8 *reg9a;
+       static const __u8 reg9a_def[] =
+               {0x08, 0x40, 0x20, 0x10, 0x00, 0x04};
+       static const __u8 reg9a_sn9c120[] =             /* from win trace */
+               {0x00, 0x40, 0x38, 0x30, 0x00, 0x20};
+       static const __u8 reg9a_sn9c325[] =
+               {0x0a, 0x40, 0x38, 0x30, 0x00, 0x20};
+
+
+       regF1 = 0x00;
+       reg_w(gspca_dev, 0xf1, &regF1, 1);
+       reg_w(gspca_dev, 0x01, &sn9c1xx[0], 1); /*fixme:jfm was [1] en v1*/
+
+       /* configure gpio */
+       reg_w(gspca_dev, 0x01, &sn9c1xx[1], 2);
+       reg_w(gspca_dev, 0x08, &sn9c1xx[8], 2);
+       reg_w(gspca_dev, 0x17, &sn9c1xx[0x17], 5);      /* jfm was 3 */
+       switch (sd->bridge) {
+       case BRIDGE_SN9C325:
+               reg9a = reg9a_sn9c325;
+               break;
+       case BRIDGE_SN9C120:
+               reg9a = reg9a_sn9c120;
+               break;
+       default:
+               reg9a = reg9a_def;
+               break;
+       }
+       reg_w(gspca_dev, 0x9a, reg9a, 6);
+
+       data = 0x60;                            /*fixme:jfm 60 00 00 (3) */
+       reg_w(gspca_dev, 0xd4, &data, 1);
+
+       reg_w(gspca_dev, 0x03, &sn9c1xx[3], 0x0f);
+
+       switch (sd->bridge) {
+       case BRIDGE_SN9C120:                    /* from win trace */
+               data = 0x61;
+               reg_w(gspca_dev, 0x01, &data, 1);
+               data = 0x20;
+               reg_w(gspca_dev, 0x17, &data, 1);
+               data = 0x60;
+               reg_w(gspca_dev, 0x01, &data, 1);
+               break;
+       case BRIDGE_SN9C325:
+               data = 0x43;
+               reg_w(gspca_dev, 0x01, &data, 1);
+               data = 0xae;
+               reg_w(gspca_dev, 0x17, &data, 1);
+               data = 0x42;
+               reg_w(gspca_dev, 0x01, &data, 1);
+               break;
+       default:
+               data = 0x43;
+               reg_w(gspca_dev, 0x01, &data, 1);
+               data = 0x61;
+               reg_w(gspca_dev, 0x17, &data, 1);
+               data = 0x42;
+               reg_w(gspca_dev, 0x01, &data, 1);
+       }
+
+       if (sd->sensor == SENSOR_HV7131R) {
+               if (probesensor(gspca_dev) < 0)
+                       return -ENODEV;
+       }
+       return 0;
+}
+
+static void hv7131R_InitSensor(struct gspca_dev *gspca_dev)
+{
+       int i = 0;
+       static const __u8 SetSensorClk[] =      /* 0x08 Mclk */
+               { 0xa1, 0x11, 0x01, 0x18, 0x00, 0x00, 0x00, 0x10 };
+
+       while (hv7131r_sensor_init[i][0]) {
+               i2c_w8(gspca_dev, hv7131r_sensor_init[i]);
+               i++;
+       }
+       i2c_w8(gspca_dev, SetSensorClk);
+}
+
+static void mi0360_InitSensor(struct gspca_dev *gspca_dev)
+{
+       int i = 0;
+
+       while (mi0360_sensor_init[i][0]) {
+               i2c_w8(gspca_dev, mi0360_sensor_init[i]);
+               i++;
+       }
+}
+
+static void mo4000_InitSensor(struct gspca_dev *gspca_dev)
+{
+       int i = 0;
+
+       while (mo4000_sensor_init[i][0]) {
+               i2c_w8(gspca_dev, mo4000_sensor_init[i]);
+               i++;
+       }
+}
+
+static void ov7648_InitSensor(struct gspca_dev *gspca_dev)
+{
+       int i = 0;
+
+       while (ov7648_sensor_init[i][0]) {
+               i2c_w8(gspca_dev, ov7648_sensor_init[i]);
+               i++;
+       }
+}
+
+static void ov7660_InitSensor(struct gspca_dev *gspca_dev)
+{
+       int i = 0;
+
+       while (ov7660_sensor_init[i][0]) {
+               i2c_w8(gspca_dev, ov7660_sensor_init[i]);
+               i++;
+       }
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+                       const struct usb_device_id *id)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       struct cam *cam;
+       __u16 vendor;
+       __u16 product;
+
+       vendor = id->idVendor;
+       product = id->idProduct;
+       sd->sensor = -1;
+       switch (vendor) {
+       case 0x0458:                            /* Genius */
+/*             switch (product) {
+               case 0x7025: */
+                       sd->bridge = BRIDGE_SN9C120;
+                       sd->sensor = SENSOR_MI0360;
+                       sd->i2c_base = 0x5d;
+/*                     break;
+               } */
+               break;
+       case 0x045e:
+/*             switch (product) {
+               case 0x00f5:
+               case 0x00f7: */
+                       sd->bridge = BRIDGE_SN9C105;
+                       sd->sensor = SENSOR_OV7660;
+                       sd->i2c_base = 0x21;
+/*                     break;
+               } */
+               break;
+       case 0x0471:                            /* Philips */
+/*             switch (product) {
+               case 0x0327:
+               case 0x0328:
+               case 0x0330: */
+                       sd->bridge = BRIDGE_SN9C105;
+                       sd->sensor = SENSOR_MI0360;
+                       sd->i2c_base = 0x5d;
+/*                     break;
+               } */
+               break;
+       case 0x0c45:                            /* Sonix */
+               switch (product) {
+               case 0x6040:
+                       sd->bridge = BRIDGE_SN9C102P;
+/*                     sd->sensor = SENSOR_MI0360;      * from BW600.inf */
+/*fixme: MI0360 base=5d ? */
+                       sd->sensor = SENSOR_HV7131R;    /* gspcav1 value */
+                       sd->i2c_base = 0x11;
+                       break;
+/*             case 0x607a:                            * from BW600.inf
+                       sd->bridge = BRIDGE_SN9C102P;
+                       sd->sensor = SENSOR_OV7648;
+                       sd->i2c_base = 0x??;
+                       break; */
+               case 0x607c:
+                       sd->bridge = BRIDGE_SN9C102P;
+                       sd->sensor = SENSOR_HV7131R;
+                       sd->i2c_base = 0x11;
+                       break;
+/*             case 0x607e:                            * from BW600.inf
+                       sd->bridge = BRIDGE_SN9C102P;
+                       sd->sensor = SENSOR_OV7630;
+                       sd->i2c_base = 0x??;
+                       break; */
+               case 0x60c0:
+                       sd->bridge = BRIDGE_SN9C105;
+                       sd->sensor = SENSOR_MI0360;
+                       sd->i2c_base = 0x5d;
+                       break;
+/*             case 0x60c8:                            * from BW600.inf
+                       sd->bridge = BRIDGE_SN9C105;
+                       sd->sensor = SENSOR_OM6801;
+                       sd->i2c_base = 0x??;
+                       break; */
+/*             case 0x60cc:                            * from BW600.inf
+                       sd->bridge = BRIDGE_SN9C105;
+                       sd->sensor = SENSOR_HV7131GP;
+                       sd->i2c_base = 0x??;
+                       break; */
+               case 0x60ec:
+                       sd->bridge = BRIDGE_SN9C105;
+                       sd->sensor = SENSOR_MO4000;
+                       sd->i2c_base = 0x21;
+                       break;
+/*             case 0x60ef:                            * from BW600.inf
+                       sd->bridge = BRIDGE_SN9C105;
+                       sd->sensor = SENSOR_ICM105C;
+                       sd->i2c_base = 0x??;
+                       break; */
+/*             case 0x60fa:                            * from BW600.inf
+                       sd->bridge = BRIDGE_SN9C105;
+                       sd->sensor = SENSOR_OV7648;
+                       sd->i2c_base = 0x??;
+                       break; */
+               case 0x60fb:
+                       sd->bridge = BRIDGE_SN9C105;
+                       sd->sensor = SENSOR_OV7660;
+                       sd->i2c_base = 0x21;
+                       break;
+               case 0x60fc:
+                       sd->bridge = BRIDGE_SN9C105;
+                       sd->sensor = SENSOR_HV7131R;
+                       sd->i2c_base = 0x11;
+                       break;
+/*             case 0x60fe:                            * from BW600.inf
+                       sd->bridge = BRIDGE_SN9C105;
+                       sd->sensor = SENSOR_OV7630;
+                       sd->i2c_base = 0x??;
+                       break; */
+/*             case 0x6108:                            * from BW600.inf
+                       sd->bridge = BRIDGE_SN9C120;
+                       sd->sensor = SENSOR_OM6801;
+                       sd->i2c_base = 0x??;
+                       break; */
+/*             case 0x6122:                            * from BW600.inf
+                       sd->bridge = BRIDGE_SN9C110;
+                       sd->sensor = SENSOR_ICM105C;
+                       sd->i2c_base = 0x??;
+                       break; */
+               case 0x612a:
+/*                     sd->bridge = BRIDGE_SN9C110;     * in BW600.inf */
+                       sd->bridge = BRIDGE_SN9C325;
+                       sd->sensor = SENSOR_OV7648;
+                       sd->i2c_base = 0x21;
+/*fixme: sensor_init has base = 00 et 6e!*/
+                       break;
+/*             case 0x6123:                            * from BW600.inf
+                       sd->bridge = BRIDGE_SN9C110;
+                       sd->sensor = SENSOR_SanyoCCD;
+                       sd->i2c_base = 0x??;
+                       break; */
+               case 0x612c:
+                       sd->bridge = BRIDGE_SN9C110;
+                       sd->sensor = SENSOR_MO4000;
+                       sd->i2c_base = 0x21;
+                       break;
+/*             case 0x612e:                            * from BW600.inf
+                       sd->bridge = BRIDGE_SN9C110;
+                       sd->sensor = SENSOR_OV7630;
+                       sd->i2c_base = 0x??;
+                       break; */
+/*             case 0x612f:                            * from BW600.inf
+                       sd->bridge = BRIDGE_SN9C110;
+                       sd->sensor = SENSOR_ICM105C;
+                       sd->i2c_base = 0x??;
+                       break; */
+               case 0x6130:
+                       sd->bridge = BRIDGE_SN9C120;
+                       sd->sensor = SENSOR_MI0360;
+                       sd->i2c_base = 0x5d;
+                       break;
+               case 0x6138:
+                       sd->bridge = BRIDGE_SN9C120;
+                       sd->sensor = SENSOR_MO4000;
+                       sd->i2c_base = 0x21;
+                       break;
+/*             case 0x613a:                            * from BW600.inf
+                       sd->bridge = BRIDGE_SN9C120;
+                       sd->sensor = SENSOR_OV7648;
+                       sd->i2c_base = 0x??;
+                       break; */
+               case 0x613b:
+                       sd->bridge = BRIDGE_SN9C120;
+                       sd->sensor = SENSOR_OV7660;
+                       sd->i2c_base = 0x21;
+                       break;
+               case 0x613c:
+                       sd->bridge = BRIDGE_SN9C120;
+                       sd->sensor = SENSOR_HV7131R;
+                       sd->i2c_base = 0x11;
+                       break;
+/*             case 0x613e:                            * from BW600.inf
+                       sd->bridge = BRIDGE_SN9C120;
+                       sd->sensor = SENSOR_OV7630;
+                       sd->i2c_base = 0x??;
+                       break; */
+               }
+               break;
+       }
+       if (sd->sensor < 0) {
+               PDEBUG(D_ERR, "Invalid vendor/product %04x:%04x",
+                       vendor, product);
+               return -EINVAL;
+       }
+
+       cam = &gspca_dev->cam;
+       cam->dev_name = (char *) id->driver_info;
+       cam->epaddr = 0x01;
+       cam->cam_mode = vga_mode;
+       cam->nmodes = ARRAY_SIZE(vga_mode);
+
+       sd->qindex = 4;                 /* set the quantization table */
+       sd->brightness = BRIGHTNESS_DEF;
+       sd->contrast = CONTRAST_DEF;
+       sd->colors = COLOR_DEF;
+       sd->autogain = AUTOGAIN_DEF;
+       return 0;
+}
+
+/* this function is called at open time */
+static int sd_open(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+/*     const __u8 *sn9c1xx; */
+       __u8 regF1;
+       __u8 regGpio[] = { 0x29, 0x74 };
+
+       /* setup a selector by bridge */
+       regF1 = 0x01;
+       reg_w(gspca_dev, 0xf1, &regF1, 1);
+       reg_r(gspca_dev, 0x00, 1);              /* -> regF1 = 0x00 */
+       regF1 = gspca_dev->usb_buf[0];
+       reg_w(gspca_dev, 0xf1, &regF1, 1);
+       reg_r(gspca_dev, 0x00, 1);
+       regF1 = gspca_dev->usb_buf[0];
+       switch (sd->bridge) {
+       case BRIDGE_SN9C102P:
+               if (regF1 != 0x11)
+                       return -ENODEV;
+               reg_w(gspca_dev, 0x02, &regGpio[1], 1);
+               break;
+       case BRIDGE_SN9C105:
+               if (regF1 != 0x11)
+                       return -ENODEV;
+               reg_w(gspca_dev, 0x02, regGpio, 2);
+               break;
+       case BRIDGE_SN9C110:
+               if (regF1 != 0x12)
+                       return -ENODEV;
+               regGpio[1] = 0x62;
+               reg_w(gspca_dev, 0x02, &regGpio[1], 1);
+               break;
+       case BRIDGE_SN9C120:
+               if (regF1 != 0x12)
+                       return -ENODEV;
+               regGpio[1] = 0x70;
+               reg_w(gspca_dev, 0x02, regGpio, 2);
+               break;
+       default:
+/*     case BRIDGE_SN9C325: */
+               if (regF1 != 0x12)
+                       return -ENODEV;
+               regGpio[1] = 0x62;
+               reg_w(gspca_dev, 0x02, &regGpio[1], 1);
+               break;
+       }
+
+       regF1 = 0x01;
+       reg_w(gspca_dev, 0xf1, &regF1, 1);
+
+       return 0;
+}
+
+static unsigned int setexposure(struct gspca_dev *gspca_dev,
+                               unsigned int expo)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       static const __u8 doit[] =              /* update sensor */
+               { 0xb1, 0x5d, 0x07, 0x00, 0x03, 0x00, 0x00, 0x10 };
+       static const __u8 sensorgo[] =          /* sensor on */
+               { 0xb1, 0x5d, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10 };
+       static const __u8 gainMo[] =
+               { 0xa1, 0x21, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1d };
+
+       switch (sd->sensor) {
+       case SENSOR_HV7131R: {
+               __u8 Expodoit[] =
+                       { 0xc1, 0x11, 0x25, 0x07, 0x27, 0xc0, 0x00, 0x16 };
+
+               Expodoit[3] = expo >> 16;
+               Expodoit[4] = expo >> 8;
+               Expodoit[5] = expo;
+               i2c_w8(gspca_dev, Expodoit);
+               break;
+           }
+       case SENSOR_MI0360: {
+               __u8 expoMi[] =  /* exposure 0x0635 -> 4 fp/s 0x10 */
+                       { 0xb1, 0x5d, 0x09, 0x06, 0x35, 0x00, 0x00, 0x16 };
+
+               if (expo > 0x0635)
+                       expo = 0x0635;
+               else if (expo < 0x0001)
+                       expo = 0x0001;
+               expoMi[3] = expo >> 8;
+               expoMi[4] = expo;
+               i2c_w8(gspca_dev, expoMi);
+               i2c_w8(gspca_dev, doit);
+               i2c_w8(gspca_dev, sensorgo);
+               break;
+           }
+       case SENSOR_MO4000: {
+               __u8 expoMof[] =
+                       { 0xa1, 0x21, 0x0f, 0x20, 0x00, 0x00, 0x00, 0x10 };
+               __u8 expoMo10[] =
+                       { 0xa1, 0x21, 0x10, 0x20, 0x00, 0x00, 0x00, 0x10 };
+
+               if (expo > 0x1fff)
+                       expo = 0x1fff;
+               else if (expo < 0x0001)
+                       expo = 0x0001;
+               expoMof[3] = (expo & 0x03fc) >> 2;
+               i2c_w8(gspca_dev, expoMof);
+               expoMo10[3] = ((expo & 0x1c00) >> 10)
+                               | ((expo & 0x0003) << 4);
+               i2c_w8(gspca_dev, expoMo10);
+               i2c_w8(gspca_dev, gainMo);
+               PDEBUG(D_CONF, "set exposure %d",
+                       ((expoMo10[3] & 0x07) << 10)
+                       | (expoMof[3] << 2)
+                       | ((expoMo10[3] & 0x30) >> 4));
+               break;
+           }
+       }
+       return expo;
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       unsigned int expo;
+       __u8 k2;
+
+       switch (sd->sensor) {
+       case SENSOR_HV7131R:
+               expo = sd->brightness << 4;
+               if (expo > 0x002dc6c0)
+                       expo = 0x002dc6c0;
+               else if (expo < 0x02a0)
+                       expo = 0x02a0;
+               sd->exposure = setexposure(gspca_dev, expo);
+               break;
+       case SENSOR_MI0360:
+               expo = sd->brightness >> 4;
+               sd->exposure = setexposure(gspca_dev, expo);
+               break;
+       case SENSOR_MO4000:
+               expo = sd->brightness >> 4;
+               sd->exposure = setexposure(gspca_dev, expo);
+               break;
+       case SENSOR_OV7660:
+               return;                         /*jfm??*/
+       }
+
+       k2 = sd->brightness >> 10;
+       reg_w(gspca_dev, 0x96, &k2, 1);
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       __u8 k2;
+       __u8 contrast[] = { 0x00, 0x00, 0x28, 0x00, 0x07, 0x00 };
+
+       if (sd->sensor == SENSOR_OV7660)
+               return;                         /*jfm??*/
+       k2 = sd->contrast;
+       contrast[2] = k2;
+       contrast[0] = (k2 + 1) >> 1;
+       contrast[4] = (k2 + 1) / 5;
+       reg_w(gspca_dev, 0x84, contrast, 6);
+}
+
+static void setcolors(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       __u8 data;
+       int colour;
+
+       colour = sd->colors - 128;
+       if (colour > 0)
+               data = (colour + 32) & 0x7f;    /* blue */
+       else
+               data = (-colour + 32) & 0x7f;   /* red */
+       reg_w(gspca_dev, 0x05, &data, 1);
+}
+
+/* -- start the camera -- */
+static void sd_start(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       int i;
+       __u8 data;
+       __u8 reg1;
+       __u8 reg17;
+       const __u8 *sn9c1xx;
+       int mode;
+       static const __u8 DC29[] = { 0x6a, 0x50, 0x00, 0x00, 0x50, 0x3c };
+       static const __u8 C0[] = { 0x2d, 0x2d, 0x3a, 0x05, 0x04, 0x3f };
+       static const __u8 CA[] = { 0x28, 0xd8, 0x14, 0xec };
+       static const __u8 CA_sn9c120[] =
+                                { 0x14, 0xec, 0x0a, 0xf6 };    /* SN9C120 */
+       static const __u8 CE[] = { 0x32, 0xdd, 0x2d, 0xdd };    /* MI0360 */
+       static const __u8 CE_sn9c325[] =
+                       { 0x32, 0xdd, 0x32, 0xdd };     /* OV7648 - SN9C325 */
+
+       sn9c1xx = sn_tb[(int) sd->sensor];
+       configure_gpio(gspca_dev, sn9c1xx);
+
+/*fixme:jfm this sequence should appear at end of sd_start */
+/* with
+       data = 0x44;
+       reg_w(gspca_dev, 0x01, &data, 1); */
+       reg_w(gspca_dev, 0x15, &sn9c1xx[0x15], 1);
+       reg_w(gspca_dev, 0x16, &sn9c1xx[0x16], 1);
+       reg_w(gspca_dev, 0x12, &sn9c1xx[0x12], 1);
+       reg_w(gspca_dev, 0x13, &sn9c1xx[0x13], 1);
+       reg_w(gspca_dev, 0x18, &sn9c1xx[0x18], 1);
+       reg_w(gspca_dev, 0xd2, &DC29[0], 1);
+       reg_w(gspca_dev, 0xd3, &DC29[1], 1);
+       reg_w(gspca_dev, 0xc6, &DC29[2], 1);
+       reg_w(gspca_dev, 0xc7, &DC29[3], 1);
+       reg_w(gspca_dev, 0xc8, &DC29[4], 1);
+       reg_w(gspca_dev, 0xc9, &DC29[5], 1);
+/*fixme:jfm end of ending sequence */
+       reg_w(gspca_dev, 0x18, &sn9c1xx[0x18], 1);
+       switch (sd->bridge) {
+       case BRIDGE_SN9C325:
+               data = 0xae;
+               break;
+       case BRIDGE_SN9C120:
+               data = 0xa0;
+               break;
+       default:
+               data = 0x60;
+               break;
+       }
+       reg_w(gspca_dev, 0x17, &data, 1);
+       reg_w(gspca_dev, 0x05, &sn9c1xx[5], 1);
+       reg_w(gspca_dev, 0x07, &sn9c1xx[7], 1);
+       reg_w(gspca_dev, 0x06, &sn9c1xx[6], 1);
+       reg_w(gspca_dev, 0x14, &sn9c1xx[0x14], 1);
+       switch (sd->bridge) {
+       case BRIDGE_SN9C325:
+               reg_w(gspca_dev, 0x20, regsn20_sn9c325,
+                               sizeof regsn20_sn9c325);
+               for (i = 0; i < 8; i++)
+                       reg_w(gspca_dev, 0x84, reg84_sn9c325,
+                                       sizeof reg84_sn9c325);
+               data = 0x0a;
+               reg_w(gspca_dev, 0x9a, &data, 1);
+               data = 0x60;
+               reg_w(gspca_dev, 0x99, &data, 1);
+               break;
+       case BRIDGE_SN9C120:
+               reg_w(gspca_dev, 0x20, regsn20_sn9c120,
+                               sizeof regsn20_sn9c120);
+               for (i = 0; i < 2; i++)
+                       reg_w(gspca_dev, 0x84, reg84_sn9c120_1,
+                                       sizeof reg84_sn9c120_1);
+               for (i = 0; i < 6; i++)
+                       reg_w(gspca_dev, 0x84, reg84_sn9c120_2,
+                                       sizeof reg84_sn9c120_2);
+               reg_w(gspca_dev, 0x84, reg84_sn9c120_3,
+                               sizeof reg84_sn9c120_3);
+               data = 0x05;
+               reg_w(gspca_dev, 0x9a, &data, 1);
+               data = 0x5b;
+               reg_w(gspca_dev, 0x99, &data, 1);
+               break;
+       default:
+               reg_w(gspca_dev, 0x20, regsn20, sizeof regsn20);
+               for (i = 0; i < 8; i++)
+                       reg_w(gspca_dev, 0x84, reg84, sizeof reg84);
+               data = 0x08;
+               reg_w(gspca_dev, 0x9a, &data, 1);
+               data = 0x59;
+               reg_w(gspca_dev, 0x99, &data, 1);
+               break;
+       }
+
+       mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;
+       reg1 = 0x02;
+       reg17 = 0x61;
+       switch (sd->sensor) {
+       case SENSOR_HV7131R:
+               hv7131R_InitSensor(gspca_dev);
+               if (mode)
+                       reg1 = 0x46;    /* 320 clk 48Mhz */
+               else
+                       reg1 = 0x06;    /* 640 clk 24Mz */
+               break;
+       case SENSOR_MI0360:
+               mi0360_InitSensor(gspca_dev);
+               if (mode)
+                       reg1 = 0x46;    /* 320 clk 48Mhz */
+               else
+                       reg1 = 0x06;    /* 640 clk 24Mz */
+               break;
+       case SENSOR_MO4000:
+               mo4000_InitSensor(gspca_dev);
+               if (mode) {
+/*                     reg1 = 0x46;     * 320 clk 48Mhz 60fp/s */
+                       reg1 = 0x06;    /* clk 24Mz */
+               } else {
+                       reg17 = 0x22;   /* 640 MCKSIZE */
+                       reg1 = 0x06;    /* 640 clk 24Mz */
+               }
+               break;
+       case SENSOR_OV7648:
+               reg17 = 0xa2;
+               reg1 = 0x44;
+               ov7648_InitSensor(gspca_dev);
+/*             if (mode)
+                       ;                * 320x2...
+               else
+                       ;                * 640x... */
+               break;
+       default:
+/*     case SENSOR_OV7660: */
+               ov7660_InitSensor(gspca_dev);
+               if (mode) {
+/*                     reg17 = 0x21;    * 320 */
+/*                     reg1 = 0x44; */
+                       reg1 = 0x46;
+               } else {
+                       reg17 = 0xa2;   /* 640 */
+                       reg1 = 0x40;
+               }
+               break;
+       }
+       reg_w(gspca_dev, 0xc0, C0, 6);
+       switch (sd->bridge) {
+       case BRIDGE_SN9C120:                    /*jfm ?? */
+               reg_w(gspca_dev, 0xca, CA_sn9c120, 4);
+               break;
+       default:
+               reg_w(gspca_dev, 0xca, CA, 4);
+               break;
+       }
+       switch (sd->bridge) {
+       case BRIDGE_SN9C120:                    /*jfm ?? */
+       case BRIDGE_SN9C325:
+               reg_w(gspca_dev, 0xce, CE_sn9c325, 4);
+               break;
+       default:
+               reg_w(gspca_dev, 0xce, CE, 4);
+                                       /* ?? {0x1e, 0xdd, 0x2d, 0xe7} */
+               break;
+       }
+
+       /* here change size mode 0 -> VGA; 1 -> CIF */
+       data = 0x40 | sn9c1xx[0x18] | (mode << 4);
+       reg_w(gspca_dev, 0x18, &data, 1);
+
+       reg_w(gspca_dev, 0x100, qtable4, 0x40);
+       reg_w(gspca_dev, 0x140, qtable4 + 0x40, 0x40);
+
+       data = sn9c1xx[0x18] | (mode << 4);
+       reg_w(gspca_dev, 0x18, &data, 1);
+
+       reg_w(gspca_dev, 0x17, &reg17, 1);
+       reg_w(gspca_dev, 0x01, &reg1, 1);
+       setbrightness(gspca_dev);
+       setcontrast(gspca_dev);
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       static const __u8 stophv7131[] =
+               { 0xa1, 0x11, 0x02, 0x09, 0x00, 0x00, 0x00, 0x10 };
+       static const __u8 stopmi0360[] =
+               { 0xb1, 0x5d, 0x07, 0x00, 0x00, 0x00, 0x00, 0x10 };
+       __u8 regF1;
+       __u8 data;
+       const __u8 *sn9c1xx;
+
+       data = 0x0b;
+       switch (sd->sensor) {
+       case SENSOR_HV7131R:
+               i2c_w8(gspca_dev, stophv7131);
+               data = 0x2b;
+               break;
+       case SENSOR_MI0360:
+               i2c_w8(gspca_dev, stopmi0360);
+               data = 0x29;
+               break;
+       case SENSOR_MO4000:
+               break;
+       case SENSOR_OV7648:
+               data = 0x29;
+               break;
+       default:
+/*     case SENSOR_OV7660: */
+               break;
+       }
+       sn9c1xx = sn_tb[(int) sd->sensor];
+       reg_w(gspca_dev, 0x01, &sn9c1xx[1], 1);
+       reg_w(gspca_dev, 0x17, &sn9c1xx[0x17], 1);
+       reg_w(gspca_dev, 0x01, &sn9c1xx[1], 1);
+       reg_w(gspca_dev, 0x01, &data, 1);
+       regF1 = 0x01;
+       reg_w(gspca_dev, 0xf1, &regF1, 1);
+}
+
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+}
+
+static void sd_close(struct gspca_dev *gspca_dev)
+{
+}
+
+static void setautogain(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       /* Thanks S., without your advice, autobright should not work :) */
+       int delta;
+       int expotimes = 0;
+       __u8 luma_mean = 130;
+       __u8 luma_delta = 20;
+
+       delta = sd->avg_lum;
+       if (delta < luma_mean - luma_delta ||
+           delta > luma_mean + luma_delta) {
+               switch (sd->sensor) {
+               case SENSOR_HV7131R:
+                       expotimes = sd->exposure >> 8;
+                       expotimes += (luma_mean - delta) >> 4;
+                       if (expotimes < 0)
+                               expotimes = 0;
+                       sd->exposure = setexposure(gspca_dev,
+                                       (unsigned int) (expotimes << 8));
+                       break;
+               case SENSOR_MO4000:
+               case SENSOR_MI0360:
+                       expotimes = sd->exposure;
+                       expotimes += (luma_mean - delta) >> 6;
+                       if (expotimes < 0)
+                               expotimes = 0;
+                       sd->exposure = setexposure(gspca_dev,
+                                                  (unsigned int) expotimes);
+                       setcolors(gspca_dev);
+                       break;
+               }
+       }
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+                       struct gspca_frame *frame,      /* target */
+                       __u8 *data,                     /* isoc packet */
+                       int len)                        /* iso packet length */
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       int sof, avg_lum;
+
+       sof = len - 64;
+       if (sof >= 0 && data[sof] == 0xff && data[sof + 1] == 0xd9) {
+
+               /* end of frame */
+               gspca_frame_add(gspca_dev, LAST_PACKET,
+                               frame, data, sof + 2);
+               if (sd->ag_cnt < 0)
+                       return;
+               if (--sd->ag_cnt >= 0)
+                       return;
+               sd->ag_cnt = AG_CNT_START;
+/* w1 w2 w3 */
+/* w4 w5 w6 */
+/* w7 w8 */
+/* w4 */
+               avg_lum = ((data[sof + 29] << 8) | data[sof + 30]) >> 6;
+/* w6 */
+               avg_lum += ((data[sof + 33] << 8) | data[sof + 34]) >> 6;
+/* w2 */
+               avg_lum += ((data[sof + 25] << 8) | data[sof + 26]) >> 6;
+/* w8 */
+               avg_lum += ((data[sof + 37] << 8) | data[sof + 38]) >> 6;
+/* w5 */
+               avg_lum += ((data[sof + 31] << 8) | data[sof + 32]) >> 4;
+               avg_lum >>= 4;
+               sd->avg_lum = avg_lum;
+               PDEBUG(D_PACK, "mean lum %d", avg_lum);
+               setautogain(gspca_dev);
+               return;
+       }
+       if (gspca_dev->last_packet_type == LAST_PACKET) {
+
+               /* put the JPEG 422 header */
+               jpeg_put_header(gspca_dev, frame, sd->qindex, 0x21);
+       }
+       gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
+}
+
+static unsigned int getexposure(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       __u8 hexpo, mexpo, lexpo;
+
+       switch (sd->sensor) {
+       case SENSOR_HV7131R:
+               /* read sensor exposure */
+               i2c_r5(gspca_dev, 0x25);
+               return (gspca_dev->usb_buf[0] << 16)
+                       | (gspca_dev->usb_buf[1] << 8)
+                       | gspca_dev->usb_buf[2];
+       case SENSOR_MI0360:
+               /* read sensor exposure */
+               i2c_r5(gspca_dev, 0x09);
+               return (gspca_dev->usb_buf[0] << 8)
+                       | gspca_dev->usb_buf[1];
+       case SENSOR_MO4000:
+               i2c_r5(gspca_dev, 0x0e);
+               hexpo = 0;              /* gspca_dev->usb_buf[1] & 0x07; */
+               mexpo = 0x40;           /* gspca_dev->usb_buf[2] & 0xff; */
+               lexpo = (gspca_dev->usb_buf[1] & 0x30) >> 4;
+               PDEBUG(D_CONF, "exposure %d",
+                       (hexpo << 10) | (mexpo << 2) | lexpo);
+               return (hexpo << 10) | (mexpo << 2) | lexpo;
+       default:
+/*     case SENSOR_OV7660: */
+               /* read sensor exposure */
+               i2c_r5(gspca_dev, 0x04);
+               hexpo = gspca_dev->usb_buf[3] & 0x2f;
+               lexpo = gspca_dev->usb_buf[0] & 0x02;
+               i2c_r5(gspca_dev, 0x08);
+               mexpo = gspca_dev->usb_buf[2];
+               return (hexpo << 10) | (mexpo << 2) | lexpo;
+       }
+}
+
+static void getbrightness(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       /* hardcoded registers seem not readable */
+       switch (sd->sensor) {
+       case SENSOR_HV7131R:
+/*             sd->brightness = 0x7fff; */
+               sd->brightness = getexposure(gspca_dev) >> 4;
+               break;
+       case SENSOR_MI0360:
+               sd->brightness = getexposure(gspca_dev) << 4;
+               break;
+       case SENSOR_MO4000:
+/*             sd->brightness = 0x1fff; */
+               sd->brightness = getexposure(gspca_dev) << 4;
+               break;
+       }
+}
+
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->brightness = val;
+       if (gspca_dev->streaming)
+               setbrightness(gspca_dev);
+       return 0;
+}
+
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       getbrightness(gspca_dev);
+       *val = sd->brightness;
+       return 0;
+}
+
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->contrast = val;
+       if (gspca_dev->streaming)
+               setcontrast(gspca_dev);
+       return 0;
+}
+
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->contrast;
+       return 0;
+}
+
+static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->colors = val;
+       if (gspca_dev->streaming)
+               setcolors(gspca_dev);
+       return 0;
+}
+
+static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->colors;
+       return 0;
+}
+
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->autogain = val;
+       if (val)
+               sd->ag_cnt = AG_CNT_START;
+       else
+               sd->ag_cnt = -1;
+       return 0;
+}
+
+static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->autogain;
+       return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+       .name = MODULE_NAME,
+       .ctrls = sd_ctrls,
+       .nctrls = ARRAY_SIZE(sd_ctrls),
+       .config = sd_config,
+       .open = sd_open,
+       .start = sd_start,
+       .stopN = sd_stopN,
+       .stop0 = sd_stop0,
+       .close = sd_close,
+       .pkt_scan = sd_pkt_scan,
+};
+
+/* -- module initialisation -- */
+#define DVNM(name) .driver_info = (kernel_ulong_t) name
+static const __devinitdata struct usb_device_id device_table[] = {
+#ifndef CONFIG_USB_SN9C102
+       {USB_DEVICE(0x0458, 0x7025), DVNM("Genius Eye 311Q")},
+       {USB_DEVICE(0x045e, 0x00f5), DVNM("MicroSoft VX3000")},
+       {USB_DEVICE(0x045e, 0x00f7), DVNM("MicroSoft VX1000")},
+       {USB_DEVICE(0x0471, 0x0327), DVNM("Philips SPC 600 NC")},
+       {USB_DEVICE(0x0471, 0x0328), DVNM("Philips SPC 700 NC")},
+#endif
+       {USB_DEVICE(0x0471, 0x0330), DVNM("Philips SPC 710NC")},
+       {USB_DEVICE(0x0c45, 0x6040), DVNM("Speed NVC 350K")},
+       {USB_DEVICE(0x0c45, 0x607c), DVNM("Sonix sn9c102p Hv7131R")},
+       {USB_DEVICE(0x0c45, 0x60c0), DVNM("Sangha Sn535")},
+       {USB_DEVICE(0x0c45, 0x60ec), DVNM("SN9C105+MO4000")},
+       {USB_DEVICE(0x0c45, 0x60fb), DVNM("Surfer NoName")},
+       {USB_DEVICE(0x0c45, 0x60fc), DVNM("LG-LIC300")},
+       {USB_DEVICE(0x0c45, 0x612a), DVNM("Avant Camera")},
+       {USB_DEVICE(0x0c45, 0x612c), DVNM("Typhoon Rasy Cam 1.3MPix")},
+#ifndef CONFIG_USB_SN9C102
+       {USB_DEVICE(0x0c45, 0x6130), DVNM("Sonix Pccam")},
+       {USB_DEVICE(0x0c45, 0x6138), DVNM("Sn9c120 Mo4000")},
+       {USB_DEVICE(0x0c45, 0x613b), DVNM("Surfer SN-206")},
+       {USB_DEVICE(0x0c45, 0x613c), DVNM("Sonix Pccam168")},
+#endif
+       {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+                   const struct usb_device_id *id)
+{
+       return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+                               THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+       .name = MODULE_NAME,
+       .id_table = device_table,
+       .probe = sd_probe,
+       .disconnect = gspca_disconnect,
+};
+
+/* -- module insert / remove -- */
+static int __init sd_mod_init(void)
+{
+       if (usb_register(&sd_driver) < 0)
+               return -1;
+       info("v%s registered", version);
+       return 0;
+}
+static void __exit sd_mod_exit(void)
+{
+       usb_deregister(&sd_driver);
+       info("deregistered");
+}
+
+module_init(sd_mod_init);
+module_exit(sd_mod_exit);
diff --git a/drivers/media/video/gspca/spca500.c b/drivers/media/video/gspca/spca500.c
new file mode 100644 (file)
index 0000000..1562061
--- /dev/null
@@ -0,0 +1,1216 @@
+/*
+ * SPCA500 chip based cameras initialization data
+ *
+ * V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
+ *
+ * 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
+ * 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
+ *
+ */
+
+#define MODULE_NAME "spca500"
+
+#include "gspca.h"
+#include "jpeg.h"
+
+#define DRIVER_VERSION_NUMBER  KERNEL_VERSION(2, 1, 7)
+static const char version[] = "2.1.7";
+
+MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
+MODULE_DESCRIPTION("GSPCA/SPCA500 USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* specific webcam descriptor */
+struct sd {
+       struct gspca_dev gspca_dev;             /* !! must be the first item */
+
+       __u8 packet[ISO_MAX_SIZE + 128];
+                                /* !! no more than 128 ff in an ISO packet */
+
+       unsigned char brightness;
+       unsigned char contrast;
+       unsigned char colors;
+
+       char qindex;
+       char subtype;
+#define AgfaCl20 0
+#define AiptekPocketDV 1
+#define BenqDC1016 2
+#define CreativePCCam300 3
+#define DLinkDSC350 4
+#define Gsmartmini 5
+#define IntelPocketPCCamera 6
+#define KodakEZ200 7
+#define LogitechClickSmart310 8
+#define LogitechClickSmart510 9
+#define LogitechTraveler 10
+#define MustekGsmart300 11
+#define Optimedia 12
+#define PalmPixDC85 13
+#define ToptroIndus 14
+};
+
+/* V4L2 controls supported by the driver */
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
+
+static struct ctrl sd_ctrls[] = {
+       {
+           {
+               .id      = V4L2_CID_BRIGHTNESS,
+               .type    = V4L2_CTRL_TYPE_INTEGER,
+               .name    = "Brightness",
+               .minimum = 0,
+               .maximum = 255,
+               .step    = 1,
+#define BRIGHTNESS_DEF 127
+               .default_value = BRIGHTNESS_DEF,
+           },
+           .set = sd_setbrightness,
+           .get = sd_getbrightness,
+       },
+       {
+           {
+               .id      = V4L2_CID_CONTRAST,
+               .type    = V4L2_CTRL_TYPE_INTEGER,
+               .name    = "Contrast",
+               .minimum = 0,
+               .maximum = 63,
+               .step    = 1,
+#define CONTRAST_DEF 31
+               .default_value = CONTRAST_DEF,
+           },
+           .set = sd_setcontrast,
+           .get = sd_getcontrast,
+       },
+       {
+           {
+               .id      = V4L2_CID_SATURATION,
+               .type    = V4L2_CTRL_TYPE_INTEGER,
+               .name    = "Color",
+               .minimum = 0,
+               .maximum = 63,
+               .step    = 1,
+#define COLOR_DEF 31
+               .default_value = COLOR_DEF,
+           },
+           .set = sd_setcolors,
+           .get = sd_getcolors,
+       },
+};
+
+static struct v4l2_pix_format vga_mode[] = {
+       {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+               .bytesperline = 320,
+               .sizeimage = 320 * 240 * 3 / 8 + 590,
+               .colorspace = V4L2_COLORSPACE_JPEG,
+               .priv = 1},
+       {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+               .bytesperline = 640,
+               .sizeimage = 640 * 480 * 3 / 8 + 590,
+               .colorspace = V4L2_COLORSPACE_JPEG,
+               .priv = 0},
+};
+
+static struct v4l2_pix_format sif_mode[] = {
+       {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+               .bytesperline = 176,
+               .sizeimage = 176 * 144 * 3 / 8 + 590,
+               .colorspace = V4L2_COLORSPACE_JPEG,
+               .priv = 1},
+       {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+               .bytesperline = 352,
+               .sizeimage = 352 * 288 * 3 / 8 + 590,
+               .colorspace = V4L2_COLORSPACE_JPEG,
+               .priv = 0},
+};
+
+/* Frame packet header offsets for the spca500 */
+#define SPCA500_OFFSET_PADDINGLB 2
+#define SPCA500_OFFSET_PADDINGHB 3
+#define SPCA500_OFFSET_MODE      4
+#define SPCA500_OFFSET_IMGWIDTH  5
+#define SPCA500_OFFSET_IMGHEIGHT 6
+#define SPCA500_OFFSET_IMGMODE   7
+#define SPCA500_OFFSET_QTBLINDEX 8
+#define SPCA500_OFFSET_FRAMSEQ   9
+#define SPCA500_OFFSET_CDSPINFO  10
+#define SPCA500_OFFSET_GPIO      11
+#define SPCA500_OFFSET_AUGPIO    12
+#define SPCA500_OFFSET_DATA      16
+
+
+static const __u16 spca500_visual_defaults[][3] = {
+       {0x00, 0x0003, 0x816b}, /* SSI not active sync with vsync,
+                                * hue (H byte) = 0,
+                                * saturation/hue enable,
+                                * brightness/contrast enable.
+                                */
+       {0x00, 0x0000, 0x8167}, /* brightness = 0 */
+       {0x00, 0x0020, 0x8168}, /* contrast = 0 */
+       {0x00, 0x0003, 0x816b}, /* SSI not active sync with vsync,
+                                * hue (H byte) = 0, saturation/hue enable,
+                                * brightness/contrast enable.
+                                * was 0x0003, now 0x0000.
+                                */
+       {0x00, 0x0000, 0x816a}, /* hue (L byte) = 0 */
+       {0x00, 0x0020, 0x8169}, /* saturation = 0x20 */
+       {0x00, 0x0050, 0x8157}, /* edge gain high threshold */
+       {0x00, 0x0030, 0x8158}, /* edge gain low threshold */
+       {0x00, 0x0028, 0x8159}, /* edge bandwidth high threshold */
+       {0x00, 0x000a, 0x815a}, /* edge bandwidth low threshold */
+       {0x00, 0x0001, 0x8202}, /* clock rate compensation = 1/25 sec/frame */
+       {0x0c, 0x0004, 0x0000},
+       /* set interface */
+       {}
+};
+static const __u16 Clicksmart510_defaults[][3] = {
+       {0x00, 0x00, 0x8211},
+       {0x00, 0x01, 0x82c0},
+       {0x00, 0x10, 0x82cb},
+       {0x00, 0x0f, 0x800d},
+       {0x00, 0x82, 0x8225},
+       {0x00, 0x21, 0x8228},
+       {0x00, 0x00, 0x8203},
+       {0x00, 0x00, 0x8204},
+       {0x00, 0x08, 0x8205},
+       {0x00, 0xf8, 0x8206},
+       {0x00, 0x28, 0x8207},
+       {0x00, 0xa0, 0x8208},
+       {0x00, 0x08, 0x824a},
+       {0x00, 0x08, 0x8214},
+       {0x00, 0x80, 0x82c1},
+       {0x00, 0x00, 0x82c2},
+       {0x00, 0x00, 0x82ca},
+       {0x00, 0x80, 0x82c1},
+       {0x00, 0x04, 0x82c2},
+       {0x00, 0x00, 0x82ca},
+       {0x00, 0xfc, 0x8100},
+       {0x00, 0xfc, 0x8105},
+       {0x00, 0x30, 0x8101},
+       {0x00, 0x00, 0x8102},
+       {0x00, 0x00, 0x8103},
+       {0x00, 0x66, 0x8107},
+       {0x00, 0x00, 0x816b},
+       {0x00, 0x00, 0x8155},
+       {0x00, 0x01, 0x8156},
+       {0x00, 0x60, 0x8157},
+       {0x00, 0x40, 0x8158},
+       {0x00, 0x0a, 0x8159},
+       {0x00, 0x06, 0x815a},
+       {0x00, 0x00, 0x813f},
+       {0x00, 0x00, 0x8200},
+       {0x00, 0x19, 0x8201},
+       {0x00, 0x00, 0x82c1},
+       {0x00, 0xa0, 0x82c2},
+       {0x00, 0x00, 0x82ca},
+       {0x00, 0x00, 0x8117},
+       {0x00, 0x00, 0x8118},
+       {0x00, 0x65, 0x8119},
+       {0x00, 0x00, 0x811a},
+       {0x00, 0x00, 0x811b},
+       {0x00, 0x55, 0x811c},
+       {0x00, 0x65, 0x811d},
+       {0x00, 0x55, 0x811e},
+       {0x00, 0x16, 0x811f},
+       {0x00, 0x19, 0x8120},
+       {0x00, 0x80, 0x8103},
+       {0x00, 0x83, 0x816b},
+       {0x00, 0x25, 0x8168},
+       {0x00, 0x01, 0x820f},
+       {0x00, 0xff, 0x8115},
+       {0x00, 0x48, 0x8116},
+       {0x00, 0x50, 0x8151},
+       {0x00, 0x40, 0x8152},
+       {0x00, 0x78, 0x8153},
+       {0x00, 0x40, 0x8154},
+       {0x00, 0x00, 0x8167},
+       {0x00, 0x20, 0x8168},
+       {0x00, 0x00, 0x816a},
+       {0x00, 0x03, 0x816b},
+       {0x00, 0x20, 0x8169},
+       {0x00, 0x60, 0x8157},
+       {0x00, 0x00, 0x8190},
+       {0x00, 0x00, 0x81a1},
+       {0x00, 0x00, 0x81b2},
+       {0x00, 0x27, 0x8191},
+       {0x00, 0x27, 0x81a2},
+       {0x00, 0x27, 0x81b3},
+       {0x00, 0x4b, 0x8192},
+       {0x00, 0x4b, 0x81a3},
+       {0x00, 0x4b, 0x81b4},
+       {0x00, 0x66, 0x8193},
+       {0x00, 0x66, 0x81a4},
+       {0x00, 0x66, 0x81b5},
+       {0x00, 0x79, 0x8194},
+       {0x00, 0x79, 0x81a5},
+       {0x00, 0x79, 0x81b6},
+       {0x00, 0x8a, 0x8195},
+       {0x00, 0x8a, 0x81a6},
+       {0x00, 0x8a, 0x81b7},
+       {0x00, 0x9b, 0x8196},
+       {0x00, 0x9b, 0x81a7},
+       {0x00, 0x9b, 0x81b8},
+       {0x00, 0xa6, 0x8197},
+       {0x00, 0xa6, 0x81a8},
+       {0x00, 0xa6, 0x81b9},
+       {0x00, 0xb2, 0x8198},
+       {0x00, 0xb2, 0x81a9},
+       {0x00, 0xb2, 0x81ba},
+       {0x00, 0xbe, 0x8199},
+       {0x00, 0xbe, 0x81aa},
+       {0x00, 0xbe, 0x81bb},
+       {0x00, 0xc8, 0x819a},
+       {0x00, 0xc8, 0x81ab},
+       {0x00, 0xc8, 0x81bc},
+       {0x00, 0xd2, 0x819b},
+       {0x00, 0xd2, 0x81ac},
+       {0x00, 0xd2, 0x81bd},
+       {0x00, 0xdb, 0x819c},
+       {0x00, 0xdb, 0x81ad},
+       {0x00, 0xdb, 0x81be},
+       {0x00, 0xe4, 0x819d},
+       {0x00, 0xe4, 0x81ae},
+       {0x00, 0xe4, 0x81bf},
+       {0x00, 0xed, 0x819e},
+       {0x00, 0xed, 0x81af},
+       {0x00, 0xed, 0x81c0},
+       {0x00, 0xf7, 0x819f},
+       {0x00, 0xf7, 0x81b0},
+       {0x00, 0xf7, 0x81c1},
+       {0x00, 0xff, 0x81a0},
+       {0x00, 0xff, 0x81b1},
+       {0x00, 0xff, 0x81c2},
+       {0x00, 0x03, 0x8156},
+       {0x00, 0x00, 0x8211},
+       {0x00, 0x20, 0x8168},
+       {0x00, 0x01, 0x8202},
+       {0x00, 0x30, 0x8101},
+       {0x00, 0x00, 0x8111},
+       {0x00, 0x00, 0x8112},
+       {0x00, 0x00, 0x8113},
+       {0x00, 0x00, 0x8114},
+       {}
+};
+
+static const __u8 qtable_creative_pccam[2][64] = {
+       {                               /* Q-table Y-components */
+        0x05, 0x03, 0x03, 0x05, 0x07, 0x0c, 0x0f, 0x12,
+        0x04, 0x04, 0x04, 0x06, 0x08, 0x11, 0x12, 0x11,
+        0x04, 0x04, 0x05, 0x07, 0x0c, 0x11, 0x15, 0x11,
+        0x04, 0x05, 0x07, 0x09, 0x0f, 0x1a, 0x18, 0x13,
+        0x05, 0x07, 0x0b, 0x11, 0x14, 0x21, 0x1f, 0x17,
+        0x07, 0x0b, 0x11, 0x13, 0x18, 0x1f, 0x22, 0x1c,
+        0x0f, 0x13, 0x17, 0x1a, 0x1f, 0x24, 0x24, 0x1e,
+        0x16, 0x1c, 0x1d, 0x1d, 0x22, 0x1e, 0x1f, 0x1e},
+       {                               /* Q-table C-components */
+        0x05, 0x05, 0x07, 0x0e, 0x1e, 0x1e, 0x1e, 0x1e,
+        0x05, 0x06, 0x08, 0x14, 0x1e, 0x1e, 0x1e, 0x1e,
+        0x07, 0x08, 0x11, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+        0x0e, 0x14, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+        0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+        0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+        0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+        0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e}
+};
+
+static const __u8 qtable_kodak_ez200[2][64] = {
+       {                               /* Q-table Y-components */
+        0x02, 0x01, 0x01, 0x02, 0x02, 0x04, 0x05, 0x06,
+        0x01, 0x01, 0x01, 0x02, 0x03, 0x06, 0x06, 0x06,
+        0x01, 0x01, 0x02, 0x02, 0x04, 0x06, 0x07, 0x06,
+        0x01, 0x02, 0x02, 0x03, 0x05, 0x09, 0x08, 0x06,
+        0x02, 0x02, 0x04, 0x06, 0x07, 0x0b, 0x0a, 0x08,
+        0x02, 0x04, 0x06, 0x06, 0x08, 0x0a, 0x0b, 0x09,
+        0x05, 0x06, 0x08, 0x09, 0x0a, 0x0c, 0x0c, 0x0a,
+        0x07, 0x09, 0x0a, 0x0a, 0x0b, 0x0a, 0x0a, 0x0a},
+       {                               /* Q-table C-components */
+        0x02, 0x02, 0x02, 0x05, 0x0a, 0x0a, 0x0a, 0x0a,
+        0x02, 0x02, 0x03, 0x07, 0x0a, 0x0a, 0x0a, 0x0a,
+        0x02, 0x03, 0x06, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+        0x05, 0x07, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+        0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+        0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+        0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+        0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a}
+};
+
+static const __u8 qtable_pocketdv[2][64] = {
+       {               /* Q-table Y-components start registers 0x8800 */
+        0x06, 0x04, 0x04, 0x06, 0x0a, 0x10, 0x14, 0x18,
+        0x05, 0x05, 0x06, 0x08, 0x0a, 0x17, 0x18, 0x16,
+        0x06, 0x05, 0x06, 0x0a, 0x10, 0x17, 0x1c, 0x16,
+        0x06, 0x07, 0x09, 0x0c, 0x14, 0x23, 0x20, 0x19,
+        0x07, 0x09, 0x0f, 0x16, 0x1b, 0x2c, 0x29, 0x1f,
+        0x0a, 0x0e, 0x16, 0x1a, 0x20, 0x2a, 0x2d, 0x25,
+        0x14, 0x1a, 0x1f, 0x23, 0x29, 0x30, 0x30, 0x28,
+        0x1d, 0x25, 0x26, 0x27, 0x2d, 0x28, 0x29, 0x28,
+        },
+       {               /* Q-table C-components start registers 0x8840 */
+        0x07, 0x07, 0x0a, 0x13, 0x28, 0x28, 0x28, 0x28,
+        0x07, 0x08, 0x0a, 0x1a, 0x28, 0x28, 0x28, 0x28,
+        0x0a, 0x0a, 0x16, 0x28, 0x28, 0x28, 0x28, 0x28,
+        0x13, 0x1a, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+        0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+        0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+        0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+        0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28}
+};
+
+/* read 'len' bytes to gspca_dev->usb_buf */
+static void reg_r(struct gspca_dev *gspca_dev,
+                 __u16 index,
+                 __u16 length)
+{
+       usb_control_msg(gspca_dev->dev,
+                       usb_rcvctrlpipe(gspca_dev->dev, 0),
+                       0,
+                       USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                       0,              /* value */
+                       index, gspca_dev->usb_buf, length, 500);
+}
+
+static int reg_w(struct gspca_dev *gspca_dev,
+                    __u16 req, __u16 index, __u16 value)
+{
+       int ret;
+
+       PDEBUG(D_USBO, "reg write: [0x%02x] = 0x%02x", index, value);
+       ret = usb_control_msg(gspca_dev->dev,
+                       usb_sndctrlpipe(gspca_dev->dev, 0),
+                       req,
+                       USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                       value, index, NULL, 0, 500);
+       if (ret < 0)
+               PDEBUG(D_ERR, "reg write: error %d", ret);
+       return ret;
+}
+
+/* returns: negative is error, pos or zero is data */
+static int reg_r_12(struct gspca_dev *gspca_dev,
+                       __u16 req,      /* bRequest */
+                       __u16 index,    /* wIndex */
+                       __u16 length)   /* wLength (1 or 2 only) */
+{
+       int ret;
+
+       gspca_dev->usb_buf[1] = 0;
+       ret = usb_control_msg(gspca_dev->dev,
+                       usb_rcvctrlpipe(gspca_dev->dev, 0),
+                       req,
+                       USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                       0,              /* value */
+                       index,
+                       gspca_dev->usb_buf, length,
+                       500);           /* timeout */
+       if (ret < 0) {
+               PDEBUG(D_ERR, "reg_r_12 err %d", ret);
+               return -1;
+       }
+       return (gspca_dev->usb_buf[1] << 8) + gspca_dev->usb_buf[0];
+}
+
+/*
+ * Simple function to wait for a given 8-bit value to be returned from
+ * a reg_read call.
+ * Returns: negative is error or timeout, zero is success.
+ */
+static int reg_r_wait(struct gspca_dev *gspca_dev,
+                       __u16 reg, __u16 index, __u16 value)
+{
+       int ret, cnt = 20;
+
+       while (--cnt > 0) {
+               ret = reg_r_12(gspca_dev, reg, index, 1);
+               if (ret == value)
+                       return 0;
+               msleep(50);
+       }
+       return -EIO;
+}
+
+static int write_vector(struct gspca_dev *gspca_dev,
+                       const __u16 data[][3])
+{
+       int ret, i = 0;
+
+       while (data[i][0] != 0 || data[i][1] != 0 || data[i][2] != 0) {
+               ret = reg_w(gspca_dev, data[i][0], data[i][2], data[i][1]);
+               if (ret < 0)
+                       return ret;
+               i++;
+       }
+       return 0;
+}
+
+static int spca50x_setup_qtable(struct gspca_dev *gspca_dev,
+                               unsigned int request,
+                               unsigned int ybase,
+                               unsigned int cbase,
+                               const __u8 qtable[2][64])
+{
+       int i, err;
+
+       /* loop over y components */
+       for (i = 0; i < 64; i++) {
+               err = reg_w(gspca_dev, request, ybase + i, qtable[0][i]);
+               if (err < 0)
+                       return err;
+       }
+
+       /* loop over c components */
+       for (i = 0; i < 64; i++) {
+               err = reg_w(gspca_dev, request, cbase + i, qtable[1][i]);
+               if (err < 0)
+                       return err;
+       }
+       return 0;
+}
+
+static void spca500_ping310(struct gspca_dev *gspca_dev)
+{
+       reg_r(gspca_dev, 0x0d04, 2);
+       PDEBUG(D_STREAM, "ClickSmart310 ping 0x0d04 0x%02x 0x%02x",
+               gspca_dev->usb_buf[0], gspca_dev->usb_buf[1]);
+}
+
+static void spca500_clksmart310_init(struct gspca_dev *gspca_dev)
+{
+       reg_r(gspca_dev, 0x0d05, 2);
+       PDEBUG(D_STREAM, "ClickSmart310 init 0x0d05 0x%02x 0x%02x",
+               gspca_dev->usb_buf[0], gspca_dev->usb_buf[1]);
+       reg_w(gspca_dev, 0x00, 0x8167, 0x5a);
+       spca500_ping310(gspca_dev);
+
+       reg_w(gspca_dev, 0x00, 0x8168, 0x22);
+       reg_w(gspca_dev, 0x00, 0x816a, 0xc0);
+       reg_w(gspca_dev, 0x00, 0x816b, 0x0b);
+       reg_w(gspca_dev, 0x00, 0x8169, 0x25);
+       reg_w(gspca_dev, 0x00, 0x8157, 0x5b);
+       reg_w(gspca_dev, 0x00, 0x8158, 0x5b);
+       reg_w(gspca_dev, 0x00, 0x813f, 0x03);
+       reg_w(gspca_dev, 0x00, 0x8151, 0x4a);
+       reg_w(gspca_dev, 0x00, 0x8153, 0x78);
+       reg_w(gspca_dev, 0x00, 0x0d01, 0x04);
+                                               /* 00 for adjust shutter */
+       reg_w(gspca_dev, 0x00, 0x0d02, 0x01);
+       reg_w(gspca_dev, 0x00, 0x8169, 0x25);
+       reg_w(gspca_dev, 0x00, 0x0d01, 0x02);
+}
+
+static void spca500_setmode(struct gspca_dev *gspca_dev,
+                       __u8 xmult, __u8 ymult)
+{
+       int mode;
+
+       /* set x multiplier */
+       reg_w(gspca_dev, 0, 0x8001, xmult);
+
+       /* set y multiplier */
+       reg_w(gspca_dev, 0, 0x8002, ymult);
+
+       /* use compressed mode, VGA, with mode specific subsample */
+       mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;
+       reg_w(gspca_dev, 0, 0x8003, mode << 4);
+}
+
+static int spca500_full_reset(struct gspca_dev *gspca_dev)
+{
+       int err;
+
+       /* send the reset command */
+       err = reg_w(gspca_dev, 0xe0, 0x0001, 0x0000);
+       if (err < 0)
+               return err;
+
+       /* wait for the reset to complete */
+       err = reg_r_wait(gspca_dev, 0x06, 0x0000, 0x0000);
+       if (err < 0)
+               return err;
+       err = reg_w(gspca_dev, 0xe0, 0x0000, 0x0000);
+       if (err < 0)
+               return err;
+       err = reg_r_wait(gspca_dev, 0x06, 0, 0);
+       if (err < 0) {
+               PDEBUG(D_ERR, "reg_r_wait() failed");
+               return err;
+       }
+       /* all ok */
+       return 0;
+}
+
+/* Synchro the Bridge with sensor */
+/* Maybe that will work on all spca500 chip */
+/* because i only own a clicksmart310 try for that chip */
+/* using spca50x_set_packet_size() cause an Ooops here */
+/* usb_set_interface from kernel 2.6.x clear all the urb stuff */
+/* up-port the same feature as in 2.4.x kernel */
+static int spca500_synch310(struct gspca_dev *gspca_dev)
+{
+       if (usb_set_interface(gspca_dev->dev, gspca_dev->iface, 0) < 0) {
+               PDEBUG(D_ERR, "Set packet size: set interface error");
+               goto error;
+       }
+       spca500_ping310(gspca_dev);
+
+       reg_r(gspca_dev, 0x0d00, 1);
+
+       /* need alt setting here */
+       PDEBUG(D_PACK, "ClickSmart310 sync alt: %d", gspca_dev->alt);
+
+       /* Windoze use pipe with altsetting 6 why 7 here */
+       if (usb_set_interface(gspca_dev->dev,
+                               gspca_dev->iface,
+                               gspca_dev->alt) < 0) {
+               PDEBUG(D_ERR, "Set packet size: set interface error");
+               goto error;
+       }
+       return 0;
+error:
+       return -EBUSY;
+}
+
+static void spca500_reinit(struct gspca_dev *gspca_dev)
+{
+       int err;
+       __u8 Data;
+
+       /* some unknow command from Aiptek pocket dv and family300 */
+
+       reg_w(gspca_dev, 0x00, 0x0d01, 0x01);
+       reg_w(gspca_dev, 0x00, 0x0d03, 0x00);
+       reg_w(gspca_dev, 0x00, 0x0d02, 0x01);
+
+       /* enable drop packet */
+       reg_w(gspca_dev, 0x00, 0x850a, 0x0001);
+
+       err = spca50x_setup_qtable(gspca_dev, 0x00, 0x8800, 0x8840,
+                                qtable_pocketdv);
+       if (err < 0)
+               PDEBUG(D_ERR|D_STREAM, "spca50x_setup_qtable failed on init");
+
+       /* set qtable index */
+       reg_w(gspca_dev, 0x00, 0x8880, 2);
+       /* family cam Quicksmart stuff */
+       reg_w(gspca_dev, 0x00, 0x800a, 0x00);
+       /* Set agc transfer: synced inbetween frames */
+       reg_w(gspca_dev, 0x00, 0x820f, 0x01);
+       /* Init SDRAM - needed for SDRAM access */
+       reg_w(gspca_dev, 0x00, 0x870a, 0x04);
+       /*Start init sequence or stream */
+       reg_w(gspca_dev, 0, 0x8003, 0x00);
+       /* switch to video camera mode */
+       reg_w(gspca_dev, 0x00, 0x8000, 0x0004);
+       msleep(2000);
+       if (reg_r_wait(gspca_dev, 0, 0x8000, 0x44) != 0) {
+               reg_r(gspca_dev, 0x816b, 1);
+               Data = gspca_dev->usb_buf[0];
+               reg_w(gspca_dev, 0x00, 0x816b, Data);
+       }
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+                       const struct usb_device_id *id)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       struct cam *cam;
+       __u16 vendor;
+       __u16 product;
+
+       vendor = id->idVendor;
+       product = id->idProduct;
+       switch (vendor) {
+       case 0x040a:            /* Kodak cameras */
+/*             switch (product) { */
+/*             case 0x0300: */
+                       sd->subtype = KodakEZ200;
+/*                     break; */
+/*             } */
+               break;
+       case 0x041e:            /* Creative cameras */
+/*             switch (product) { */
+/*             case 0x400a: */
+                       sd->subtype = CreativePCCam300;
+/*                     break; */
+/*             } */
+               break;
+       case 0x046d:            /* Logitech Labtec */
+               switch (product) {
+               case 0x0890:
+                       sd->subtype = LogitechTraveler;
+                       break;
+               case 0x0900:
+                       sd->subtype = LogitechClickSmart310;
+                       break;
+               case 0x0901:
+                       sd->subtype = LogitechClickSmart510;
+                       break;
+               }
+               break;
+       case 0x04a5:            /* Benq */
+/*             switch (product) { */
+/*             case 0x300c: */
+                       sd->subtype = BenqDC1016;
+/*                     break; */
+/*             } */
+               break;
+       case 0x04fc:            /* SunPlus */
+/*             switch (product) { */
+/*             case 0x7333: */
+                       sd->subtype = PalmPixDC85;
+/*                     break; */
+/*             } */
+               break;
+       case 0x055f:            /* Mustek cameras */
+               switch (product) {
+               case 0xc200:
+                       sd->subtype = MustekGsmart300;
+                       break;
+               case 0xc220:
+                       sd->subtype = Gsmartmini;
+                       break;
+               }
+               break;
+       case 0x06bd:            /* Agfa Cl20 */
+/*             switch (product) { */
+/*             case 0x0404: */
+                       sd->subtype = AgfaCl20;
+/*                     break; */
+/*             } */
+               break;
+       case 0x06be:            /* Optimedia */
+/*             switch (product) { */
+/*             case 0x0800: */
+                       sd->subtype = Optimedia;
+/*                     break; */
+/*             } */
+               break;
+       case 0x084d:            /* D-Link / Minton */
+/*             switch (product) { */
+/*             case 0x0003:     * DSC-350 / S-Cam F5 */
+                       sd->subtype = DLinkDSC350;
+/*                     break; */
+/*             } */
+               break;
+       case 0x08ca:            /* Aiptek */
+/*             switch (product) { */
+/*             case 0x0103: */
+                       sd->subtype = AiptekPocketDV;
+/*                     break; */
+/*             } */
+               break;
+       case 0x2899:            /* ToptroIndustrial */
+/*             switch (product) { */
+/*             case 0x012c: */
+                       sd->subtype = ToptroIndus;
+/*                     break; */
+/*             } */
+               break;
+       case 0x8086:            /* Intel */
+/*             switch (product) { */
+/*             case 0x0630:     * Pocket PC Camera */
+                       sd->subtype = IntelPocketPCCamera;
+/*                     break; */
+/*             } */
+               break;
+       }
+       cam = &gspca_dev->cam;
+       cam->dev_name = (char *) id->driver_info;
+       cam->epaddr = 0x01;
+       if (sd->subtype != LogitechClickSmart310) {
+               cam->cam_mode = vga_mode;
+               cam->nmodes = sizeof vga_mode / sizeof vga_mode[0];
+       } else {
+               cam->cam_mode = sif_mode;
+               cam->nmodes = sizeof sif_mode / sizeof sif_mode[0];
+       }
+       sd->qindex = 5;
+       sd->brightness = BRIGHTNESS_DEF;
+       sd->contrast = CONTRAST_DEF;
+       sd->colors = COLOR_DEF;
+       return 0;
+}
+
+/* this function is called at open time */
+static int sd_open(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       /* initialisation of spca500 based cameras is deferred */
+       PDEBUG(D_STREAM, "SPCA500 init");
+       if (sd->subtype == LogitechClickSmart310)
+               spca500_clksmart310_init(gspca_dev);
+/*     else
+               spca500_initialise(gspca_dev); */
+       PDEBUG(D_STREAM, "SPCA500 init done");
+       return 0;
+}
+
+static void sd_start(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       int err;
+       __u8 Data;
+       __u8 xmult, ymult;
+
+       if (sd->subtype == LogitechClickSmart310) {
+               xmult = 0x16;
+               ymult = 0x12;
+       } else {
+               xmult = 0x28;
+               ymult = 0x1e;
+       }
+
+       /* is there a sensor here ? */
+       reg_r(gspca_dev, 0x8a04, 1);
+       PDEBUG(D_STREAM, "Spca500 Sensor Address 0x%02x",
+               gspca_dev->usb_buf[0]);
+       PDEBUG(D_STREAM, "Spca500 curr_mode: %d Xmult: 0x%02x, Ymult: 0x%02x",
+               gspca_dev->curr_mode, xmult, ymult);
+
+       /* setup qtable */
+       switch (sd->subtype) {
+       case LogitechClickSmart310:
+                spca500_setmode(gspca_dev, xmult, ymult);
+
+               /* enable drop packet */
+               reg_w(gspca_dev, 0x00, 0x850a, 0x0001);
+               reg_w(gspca_dev, 0x00, 0x8880, 3);
+               err = spca50x_setup_qtable(gspca_dev,
+                                          0x00, 0x8800, 0x8840,
+                                          qtable_creative_pccam);
+               if (err < 0)
+                       PDEBUG(D_ERR, "spca50x_setup_qtable failed");
+               /* Init SDRAM - needed for SDRAM access */
+               reg_w(gspca_dev, 0x00, 0x870a, 0x04);
+
+               /* switch to video camera mode */
+               reg_w(gspca_dev, 0x00, 0x8000, 0x0004);
+               msleep(500);
+               if (reg_r_wait(gspca_dev, 0, 0x8000, 0x44) != 0)
+                       PDEBUG(D_ERR, "reg_r_wait() failed");
+
+               reg_r(gspca_dev, 0x816b, 1);
+               Data = gspca_dev->usb_buf[0];
+               reg_w(gspca_dev, 0x00, 0x816b, Data);
+
+               spca500_synch310(gspca_dev);
+
+               write_vector(gspca_dev, spca500_visual_defaults);
+               spca500_setmode(gspca_dev, xmult, ymult);
+               /* enable drop packet */
+               reg_w(gspca_dev, 0x00, 0x850a, 0x0001);
+                       PDEBUG(D_ERR, "failed to enable drop packet");
+               reg_w(gspca_dev, 0x00, 0x8880, 3);
+               err = spca50x_setup_qtable(gspca_dev,
+                                          0x00, 0x8800, 0x8840,
+                                          qtable_creative_pccam);
+               if (err < 0)
+                       PDEBUG(D_ERR, "spca50x_setup_qtable failed");
+
+               /* Init SDRAM - needed for SDRAM access */
+               reg_w(gspca_dev, 0x00, 0x870a, 0x04);
+
+               /* switch to video camera mode */
+               reg_w(gspca_dev, 0x00, 0x8000, 0x0004);
+
+               if (reg_r_wait(gspca_dev, 0, 0x8000, 0x44) != 0)
+                       PDEBUG(D_ERR, "reg_r_wait() failed");
+
+               reg_r(gspca_dev, 0x816b, 1);
+               Data = gspca_dev->usb_buf[0];
+               reg_w(gspca_dev, 0x00, 0x816b, Data);
+               break;
+       case CreativePCCam300:          /* Creative PC-CAM 300 640x480 CCD */
+       case IntelPocketPCCamera:       /* FIXME: Temporary fix for
+                                        *      Intel Pocket PC Camera
+                                        *      - NWG (Sat 29th March 2003) */
+
+               /* do a full reset */
+               err = spca500_full_reset(gspca_dev);
+               if (err < 0)
+                       PDEBUG(D_ERR, "spca500_full_reset failed");
+
+               /* enable drop packet */
+               err = reg_w(gspca_dev, 0x00, 0x850a, 0x0001);
+               if (err < 0)
+                       PDEBUG(D_ERR, "failed to enable drop packet");
+               reg_w(gspca_dev, 0x00, 0x8880, 3);
+               err = spca50x_setup_qtable(gspca_dev,
+                                          0x00, 0x8800, 0x8840,
+                                          qtable_creative_pccam);
+               if (err < 0)
+                       PDEBUG(D_ERR, "spca50x_setup_qtable failed");
+
+               spca500_setmode(gspca_dev, xmult, ymult);
+               reg_w(gspca_dev, 0x20, 0x0001, 0x0004);
+
+               /* switch to video camera mode */
+               reg_w(gspca_dev, 0x00, 0x8000, 0x0004);
+
+               if (reg_r_wait(gspca_dev, 0, 0x8000, 0x44) != 0)
+                       PDEBUG(D_ERR, "reg_r_wait() failed");
+
+               reg_r(gspca_dev, 0x816b, 1);
+               Data = gspca_dev->usb_buf[0];
+               reg_w(gspca_dev, 0x00, 0x816b, Data);
+
+/*             write_vector(gspca_dev, spca500_visual_defaults); */
+               break;
+       case KodakEZ200:                /* Kodak EZ200 */
+
+               /* do a full reset */
+               err = spca500_full_reset(gspca_dev);
+               if (err < 0)
+                       PDEBUG(D_ERR, "spca500_full_reset failed");
+               /* enable drop packet */
+               reg_w(gspca_dev, 0x00, 0x850a, 0x0001);
+               reg_w(gspca_dev, 0x00, 0x8880, 0);
+               err = spca50x_setup_qtable(gspca_dev,
+                                          0x00, 0x8800, 0x8840,
+                                          qtable_kodak_ez200);
+               if (err < 0)
+                       PDEBUG(D_ERR, "spca50x_setup_qtable failed");
+               spca500_setmode(gspca_dev, xmult, ymult);
+
+               reg_w(gspca_dev, 0x20, 0x0001, 0x0004);
+
+               /* switch to video camera mode */
+               reg_w(gspca_dev, 0x00, 0x8000, 0x0004);
+
+               if (reg_r_wait(gspca_dev, 0, 0x8000, 0x44) != 0)
+                       PDEBUG(D_ERR, "reg_r_wait() failed");
+
+               reg_r(gspca_dev, 0x816b, 1);
+               Data = gspca_dev->usb_buf[0];
+               reg_w(gspca_dev, 0x00, 0x816b, Data);
+
+/*             write_vector(gspca_dev, spca500_visual_defaults); */
+               break;
+
+       case BenqDC1016:
+       case DLinkDSC350:               /* FamilyCam 300 */
+       case AiptekPocketDV:            /* Aiptek PocketDV */
+       case Gsmartmini:                /*Mustek Gsmart Mini */
+       case MustekGsmart300:           /* Mustek Gsmart 300 */
+       case PalmPixDC85:
+       case Optimedia:
+       case ToptroIndus:
+       case AgfaCl20:
+               spca500_reinit(gspca_dev);
+               reg_w(gspca_dev, 0x00, 0x0d01, 0x01);
+               /* enable drop packet */
+               reg_w(gspca_dev, 0x00, 0x850a, 0x0001);
+
+               err = spca50x_setup_qtable(gspca_dev,
+                                  0x00, 0x8800, 0x8840, qtable_pocketdv);
+               if (err < 0)
+                       PDEBUG(D_ERR, "spca50x_setup_qtable failed");
+               reg_w(gspca_dev, 0x00, 0x8880, 2);
+
+               /* familycam Quicksmart pocketDV stuff */
+               reg_w(gspca_dev, 0x00, 0x800a, 0x00);
+               /* Set agc transfer: synced inbetween frames */
+               reg_w(gspca_dev, 0x00, 0x820f, 0x01);
+               /* Init SDRAM - needed for SDRAM access */
+               reg_w(gspca_dev, 0x00, 0x870a, 0x04);
+
+               spca500_setmode(gspca_dev, xmult, ymult);
+               /* switch to video camera mode */
+               reg_w(gspca_dev, 0x00, 0x8000, 0x0004);
+
+               reg_r_wait(gspca_dev, 0, 0x8000, 0x44);
+
+               reg_r(gspca_dev, 0x816b, 1);
+               Data = gspca_dev->usb_buf[0];
+               reg_w(gspca_dev, 0x00, 0x816b, Data);
+               break;
+       case LogitechTraveler:
+       case LogitechClickSmart510:
+               reg_w(gspca_dev, 0x02, 0x00, 0x00);
+               /* enable drop packet */
+               reg_w(gspca_dev, 0x00, 0x850a, 0x0001);
+
+               err = spca50x_setup_qtable(gspca_dev,
+                                       0x00, 0x8800,
+                                       0x8840, qtable_creative_pccam);
+               if (err < 0)
+                       PDEBUG(D_ERR, "spca50x_setup_qtable failed");
+               reg_w(gspca_dev, 0x00, 0x8880, 3);
+               reg_w(gspca_dev, 0x00, 0x800a, 0x00);
+               /* Init SDRAM - needed for SDRAM access */
+               reg_w(gspca_dev, 0x00, 0x870a, 0x04);
+
+               spca500_setmode(gspca_dev, xmult, ymult);
+
+               /* switch to video camera mode */
+               reg_w(gspca_dev, 0x00, 0x8000, 0x0004);
+               reg_r_wait(gspca_dev, 0, 0x8000, 0x44);
+
+               reg_r(gspca_dev, 0x816b, 1);
+               Data = gspca_dev->usb_buf[0];
+               reg_w(gspca_dev, 0x00, 0x816b, Data);
+               write_vector(gspca_dev, Clicksmart510_defaults);
+               break;
+       }
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+       reg_w(gspca_dev, 0, 0x8003, 0x00);
+
+       /* switch to video camera mode */
+       reg_w(gspca_dev, 0x00, 0x8000, 0x0004);
+       reg_r(gspca_dev, 0x8000, 1);
+       PDEBUG(D_STREAM, "stop SPCA500 done reg8000: 0x%2x",
+               gspca_dev->usb_buf[0]);
+}
+
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+}
+
+static void sd_close(struct gspca_dev *gspca_dev)
+{
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+                       struct gspca_frame *frame,      /* target */
+                       __u8 *data,                     /* isoc packet */
+                       int len)                        /* iso packet length */
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       int i;
+       __u8 *s, *d;
+       static __u8 ffd9[] = {0xff, 0xd9};
+
+/* frames are jpeg 4.1.1 without 0xff escape */
+       if (data[0] == 0xff) {
+               if (data[1] != 0x01) {  /* drop packet */
+/*                     gspca_dev->last_packet_type = DISCARD_PACKET; */
+                       return;
+               }
+               frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
+                                       ffd9, 2);
+
+               /* put the JPEG header in the new frame */
+               jpeg_put_header(gspca_dev, frame, sd->qindex, 0x22);
+
+               data += SPCA500_OFFSET_DATA;
+               len -= SPCA500_OFFSET_DATA;
+       } else {
+               data += 1;
+               len -= 1;
+       }
+
+       /* add 0x00 after 0xff */
+       for (i = len; --i >= 0; )
+               if (data[i] == 0xff)
+                       break;
+       if (i < 0) {                    /* no 0xff */
+               gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
+               return;
+       }
+       s = data;
+       d = sd->packet;
+       for (i = 0; i < len; i++) {
+               *d++ = *s++;
+               if (s[-1] == 0xff)
+                       *d++ = 0x00;
+       }
+       gspca_frame_add(gspca_dev, INTER_PACKET, frame,
+                       sd->packet, d - sd->packet);
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       reg_w(gspca_dev, 0x00, 0x8167,
+                       (__u8) (sd->brightness - 128));
+}
+
+static void getbrightness(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       int ret;
+
+       ret = reg_r_12(gspca_dev, 0x00, 0x8167, 1);
+       if (ret >= 0)
+               sd->brightness = ret + 128;
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       reg_w(gspca_dev, 0x00, 0x8168, sd->contrast);
+}
+
+static void getcontrast(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       int ret;
+
+       ret = reg_r_12(gspca_dev, 0x0, 0x8168, 1);
+       if (ret >= 0)
+               sd->contrast = ret;
+}
+
+static void setcolors(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       reg_w(gspca_dev, 0x00, 0x8169, sd->colors);
+}
+
+static void getcolors(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       int ret;
+
+       ret = reg_r_12(gspca_dev, 0x0, 0x8169, 1);
+       if (ret >= 0)
+               sd->colors = ret;
+}
+
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->brightness = val;
+       if (gspca_dev->streaming)
+               setbrightness(gspca_dev);
+       return 0;
+}
+
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       getbrightness(gspca_dev);
+       *val = sd->brightness;
+       return 0;
+}
+
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->contrast = val;
+       if (gspca_dev->streaming)
+               setcontrast(gspca_dev);
+       return 0;
+}
+
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       getcontrast(gspca_dev);
+       *val = sd->contrast;
+       return 0;
+}
+
+static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->colors = val;
+       if (gspca_dev->streaming)
+               setcolors(gspca_dev);
+       return 0;
+}
+
+static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       getcolors(gspca_dev);
+       *val = sd->colors;
+       return 0;
+}
+
+/* sub-driver description */
+static struct sd_desc sd_desc = {
+       .name = MODULE_NAME,
+       .ctrls = sd_ctrls,
+       .nctrls = ARRAY_SIZE(sd_ctrls),
+       .config = sd_config,
+       .open = sd_open,
+       .start = sd_start,
+       .stopN = sd_stopN,
+       .stop0 = sd_stop0,
+       .close = sd_close,
+       .pkt_scan = sd_pkt_scan,
+};
+
+/* -- module initialisation -- */
+#define DVNM(name) .driver_info = (kernel_ulong_t) name
+static const __devinitdata struct usb_device_id device_table[] = {
+       {USB_DEVICE(0x040a, 0x0300), DVNM("Kodak EZ200")},
+       {USB_DEVICE(0x041e, 0x400a), DVNM("Creative PC-CAM 300")},
+       {USB_DEVICE(0x046d, 0x0890), DVNM("Logitech QuickCam traveler")},
+       {USB_DEVICE(0x046d, 0x0900), DVNM("Logitech Inc. ClickSmart 310")},
+       {USB_DEVICE(0x046d, 0x0901), DVNM("Logitech Inc. ClickSmart 510")},
+       {USB_DEVICE(0x04a5, 0x300c), DVNM("Benq DC1016")},
+       {USB_DEVICE(0x04fc, 0x7333), DVNM("PalmPixDC85")},
+       {USB_DEVICE(0x055f, 0xc200), DVNM("Mustek Gsmart 300")},
+       {USB_DEVICE(0x055f, 0xc220), DVNM("Gsmart Mini")},
+       {USB_DEVICE(0x06bd, 0x0404), DVNM("Agfa CL20")},
+       {USB_DEVICE(0x06be, 0x0800), DVNM("Optimedia")},
+       {USB_DEVICE(0x084d, 0x0003), DVNM("D-Link DSC-350")},
+       {USB_DEVICE(0x08ca, 0x0103), DVNM("Aiptek PocketDV")},
+       {USB_DEVICE(0x2899, 0x012c), DVNM("Toptro Industrial")},
+       {USB_DEVICE(0x8086, 0x0630), DVNM("Intel Pocket PC Camera")},
+       {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+                       const struct usb_device_id *id)
+{
+       return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+                               THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+       .name = MODULE_NAME,
+       .id_table = device_table,
+       .probe = sd_probe,
+       .disconnect = gspca_disconnect,
+};
+
+/* -- module insert / remove -- */
+static int __init sd_mod_init(void)
+{
+       if (usb_register(&sd_driver) < 0)
+               return -1;
+       PDEBUG(D_PROBE, "v%s registered", version);
+       return 0;
+}
+static void __exit sd_mod_exit(void)
+{
+       usb_deregister(&sd_driver);
+       PDEBUG(D_PROBE, "deregistered");
+}
+
+module_init(sd_mod_init);
+module_exit(sd_mod_exit);
diff --git a/drivers/media/video/gspca/spca501.c b/drivers/media/video/gspca/spca501.c
new file mode 100644 (file)
index 0000000..50e929d
--- /dev/null
@@ -0,0 +1,2229 @@
+/*
+ * SPCA501 chip based cameras initialization data
+ *
+ * V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
+ *
+ * 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
+ * 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
+ *
+ */
+
+#define MODULE_NAME "spca501"
+
+#include "gspca.h"
+
+#define DRIVER_VERSION_NUMBER  KERNEL_VERSION(2, 1, 7)
+static const char version[] = "2.1.7";
+
+MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
+MODULE_DESCRIPTION("GSPCA/SPCA501 USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* specific webcam descriptor */
+struct sd {
+       struct gspca_dev gspca_dev;     /* !! must be the first item */
+
+       unsigned short contrast;
+       __u8 brightness;
+       __u8 colors;
+
+       char subtype;
+#define Arowana300KCMOSCamera 0
+#define IntelCreateAndShare 1
+#define KodakDVC325 2
+#define MystFromOriUnknownCamera 3
+#define SmileIntlCamera 4
+#define ThreeComHomeConnectLite 5
+#define ViewQuestM318B 6
+};
+
+/* V4L2 controls supported by the driver */
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
+
+static struct ctrl sd_ctrls[] = {
+#define MY_BRIGHTNESS 0
+       {
+           {
+               .id      = V4L2_CID_BRIGHTNESS,
+               .type    = V4L2_CTRL_TYPE_INTEGER,
+               .name    = "Brightness",
+               .minimum = 0,
+               .maximum = 127,
+               .step    = 1,
+               .default_value = 63,
+           },
+           .set = sd_setbrightness,
+           .get = sd_getbrightness,
+       },
+#define MY_CONTRAST 1
+       {
+           {
+               .id      = V4L2_CID_CONTRAST,
+               .type    = V4L2_CTRL_TYPE_INTEGER,
+               .name    = "Contrast",
+               .minimum = 0,
+               .maximum = 0xffff,
+               .step    = 1,
+               .default_value = 0xaa00,
+           },
+           .set = sd_setcontrast,
+           .get = sd_getcontrast,
+       },
+#define MY_COLOR 2
+       {
+           {
+               .id      = V4L2_CID_SATURATION,
+               .type    = V4L2_CTRL_TYPE_INTEGER,
+               .name    = "Color",
+               .minimum = 0,
+               .maximum = 63,
+               .step    = 1,
+               .default_value = 31,
+           },
+           .set = sd_setcolors,
+           .get = sd_getcolors,
+       },
+};
+
+static struct v4l2_pix_format vga_mode[] = {
+       {160, 120, V4L2_PIX_FMT_SPCA501, V4L2_FIELD_NONE,
+               .bytesperline = 160,
+               .sizeimage = 160 * 120 * 3 / 2,
+               .colorspace = V4L2_COLORSPACE_SRGB,
+               .priv = 2},
+       {320, 240, V4L2_PIX_FMT_SPCA501, V4L2_FIELD_NONE,
+               .bytesperline = 320,
+               .sizeimage = 320 * 240 * 3 / 2,
+               .colorspace = V4L2_COLORSPACE_SRGB,
+               .priv = 1},
+       {640, 480, V4L2_PIX_FMT_SPCA501, V4L2_FIELD_NONE,
+               .bytesperline = 640,
+               .sizeimage = 640 * 480 * 3 / 2,
+               .colorspace = V4L2_COLORSPACE_SRGB,
+               .priv = 0},
+};
+
+#define SPCA50X_REG_USB 0x2    /* spca505 501 */
+/*
+ * Data to initialize a SPCA501. From a capture file provided by Bill Roehl
+ * With SPCA501 chip description
+ */
+#define CCDSP_SET              /* set CCDSP parameters */
+#define TG_SET                 /* set time generator set */
+#undef DSPWIN_SET              /* set DSP windows parameters */
+#undef ALTER_GAMA      /* Set alternate set to YUV transform coeffs. */
+#define SPCA501_SNAPBIT 0x80
+#define SPCA501_SNAPCTRL 0x10
+/* Frame packet header offsets for the spca501 */
+#define SPCA501_OFFSET_GPIO   1
+#define SPCA501_OFFSET_TYPE   2
+#define SPCA501_OFFSET_TURN3A 3
+#define SPCA501_OFFSET_FRAMSEQ 4
+#define SPCA501_OFFSET_COMPRESS 5
+#define SPCA501_OFFSET_QUANT 6
+#define SPCA501_OFFSET_QUANT2 7
+#define SPCA501_OFFSET_DATA 8
+
+#define SPCA501_PROP_COMP_ENABLE(d) ((d) & 1)
+#define SPCA501_PROP_SNAP(d) ((d) & 0x40)
+#define SPCA501_PROP_SNAP_CTRL(d) ((d) & 0x10)
+#define SPCA501_PROP_COMP_THRESH(d) (((d) & 0x0e) >> 1)
+#define SPCA501_PROP_COMP_QUANT(d) (((d) & 0x70) >> 4)
+
+/* SPCA501 CCDSP control */
+#define SPCA501_REG_CCDSP 0x01
+/* SPCA501 control/status registers */
+#define SPCA501_REG_CTLRL 0x02
+
+/* registers for color correction and YUV transformation */
+#define SPCA501_A11 0x08
+#define SPCA501_A12 0x09
+#define SPCA501_A13 0x0A
+#define SPCA501_A21 0x0B
+#define SPCA501_A22 0x0C
+#define SPCA501_A23 0x0D
+#define SPCA501_A31 0x0E
+#define SPCA501_A32 0x0F
+#define SPCA501_A33 0x10
+
+/* Data for video camera initialization before capturing */
+static const __u16 spca501_open_data[][3] = {
+       /* bmRequest,value,index */
+
+       {0x2, 0x50, 0x00},      /* C/S enable soft reset */
+       {0x2, 0x40, 0x00},      /* C/S disable soft reset */
+       {0x2, 0x02, 0x05},      /* C/S general purpose I/O data */
+       {0x2, 0x03, 0x05},      /* C/S general purpose I/O data */
+
+#ifdef CCDSP_SET
+       {0x1, 0x38, 0x01},      /* CCDSP options */
+       {0x1, 0x05, 0x02}, /* CCDSP Optical black level for user settings */
+       {0x1, 0xC0, 0x03},      /* CCDSP Optical black settings */
+
+       {0x1, 0x67, 0x07},
+       {0x1, 0x63, 0x3f},      /* CCDSP CCD gamma enable */
+       {0x1, 0x03, 0x56},      /* Add gamma correction */
+
+       {0x1, 0xFF, 0x15},      /* CCDSP High luminance for white balance */
+       {0x1, 0x01, 0x16},      /* CCDSP Low luminance for white balance */
+
+/* Color correction and RGB-to-YUV transformation coefficients changing */
+#ifdef ALTER_GAMA
+       {0x0, 0x00, 0x08},      /* A11 */
+       {0x0, 0x00, 0x09},      /* A12 */
+       {0x0, 0x90, 0x0A},      /* A13 */
+       {0x0, 0x12, 0x0B},      /* A21 */
+       {0x0, 0x00, 0x0C},      /* A22 */
+       {0x0, 0x00, 0x0D},      /* A23 */
+       {0x0, 0x00, 0x0E},      /* A31 */
+       {0x0, 0x02, 0x0F},      /* A32 */
+       {0x0, 0x00, 0x10},      /* A33 */
+#else
+       {0x1, 0x2a, 0x08},      /* A11 0x31 */
+       {0x1, 0xf8, 0x09},      /* A12 f8 */
+       {0x1, 0xf8, 0x0A},      /* A13 f8 */
+       {0x1, 0xf8, 0x0B},      /* A21 f8 */
+       {0x1, 0x14, 0x0C},      /* A22 0x14 */
+       {0x1, 0xf8, 0x0D},      /* A23 f8 */
+       {0x1, 0xf8, 0x0E},      /* A31 f8 */
+       {0x1, 0xf8, 0x0F},      /* A32 f8 */
+       {0x1, 0x20, 0x10},      /* A33 0x20 */
+#endif
+       {0x1, 0x00, 0x11},      /* R offset */
+       {0x1, 0x00, 0x12},      /* G offset */
+       {0x1, 0x00, 0x13},      /* B offset */
+       {0x1, 0x00, 0x14},      /* GB offset */
+
+#endif
+
+#ifdef TG_SET
+       /* Time generator manipulations */
+       {0x0, 0xfc, 0x0},       /* Set up high bits of shutter speed */
+       {0x0, 0x01, 0x1},       /* Set up low bits of shutter speed */
+
+       {0x0, 0xe4, 0x04},      /* DCLK*2 clock phase adjustment */
+       {0x0, 0x08, 0x05},      /* ADCK phase adjustment, inv. ext. VB */
+       {0x0, 0x03, 0x06},      /* FR phase adjustment */
+       {0x0, 0x01, 0x07},      /* FCDS phase adjustment */
+       {0x0, 0x39, 0x08},      /* FS phase adjustment */
+       {0x0, 0x88, 0x0a},      /* FH1 phase and delay adjustment */
+       {0x0, 0x03, 0x0f},      /* pixel identification */
+       {0x0, 0x00, 0x11},      /* clock source selection (default) */
+
+       /*VERY strange manipulations with
+        * select DMCLP or OBPX to be ADCLP output (0x0C)
+        * OPB always toggle or not (0x0D) but they allow
+        * us to set up brightness
+        */
+       {0x0, 0x01, 0x0c},
+       {0x0, 0xe0, 0x0d},
+       /* Done */
+#endif
+
+#ifdef DSPWIN_SET
+       {0x1, 0xa0, 0x01},      /* Setting image processing parameters */
+       {0x1, 0x1c, 0x17},      /* Changing Windows positions X1 */
+       {0x1, 0xe2, 0x19},      /* X2 */
+       {0x1, 0x1c, 0x1b},      /* X3 */
+       {0x1, 0xe2, 0x1d},      /* X4 */
+       {0x1, 0x5f, 0x1f},      /* X5 */
+       {0x1, 0x32, 0x20},      /* Y5 */
+       {0x1, 0x01, 0x10},      /* Changing A33 */
+#endif
+
+       {0x2, 0x204a, 0x07},/* Setting video compression & resolution 160x120 */
+       {0x2, 0x94, 0x06},      /* Setting video no compression */
+       {}
+};
+
+/*
+   The SPCAxxx docs from Sunplus document these values
+   in tables, one table per register number.  In the data
+   below, dmRequest is the register number, index is the Addr,
+   and value is a combination of Bit values.
+   Bit  Value (hex)
+   0    01
+   1    02
+   2    04
+   3    08
+   4    10
+   5    20
+   6    40
+   7    80
+ */
+
+/* Data for chip initialization (set default values) */
+static const __u16 spca501_init_data[][3] = {
+       /* Set all the values to powerup defaults */
+       /* bmRequest,value,index */
+       {0x0, 0xAA, 0x00},
+       {0x0, 0x02, 0x01},
+       {0x0, 0x01, 0x02},
+       {0x0, 0x02, 0x03},
+       {0x0, 0xCE, 0x04},
+       {0x0, 0x00, 0x05},
+       {0x0, 0x00, 0x06},
+       {0x0, 0x00, 0x07},
+       {0x0, 0x00, 0x08},
+       {0x0, 0x00, 0x09},
+       {0x0, 0x90, 0x0A},
+       {0x0, 0x12, 0x0B},
+       {0x0, 0x00, 0x0C},
+       {0x0, 0x00, 0x0D},
+       {0x0, 0x00, 0x0E},
+       {0x0, 0x02, 0x0F},
+       {0x0, 0x00, 0x10},
+       {0x0, 0x00, 0x11},
+       {0x0, 0x00, 0x12},
+       {0x0, 0x00, 0x13},
+       {0x0, 0x00, 0x14},
+       {0x0, 0x00, 0x15},
+       {0x0, 0x00, 0x16},
+       {0x0, 0x00, 0x17},
+       {0x0, 0x00, 0x18},
+       {0x0, 0x00, 0x19},
+       {0x0, 0x00, 0x1A},
+       {0x0, 0x00, 0x1B},
+       {0x0, 0x00, 0x1C},
+       {0x0, 0x00, 0x1D},
+       {0x0, 0x00, 0x1E},
+       {0x0, 0x00, 0x1F},
+       {0x0, 0x00, 0x20},
+       {0x0, 0x00, 0x21},
+       {0x0, 0x00, 0x22},
+       {0x0, 0x00, 0x23},
+       {0x0, 0x00, 0x24},
+       {0x0, 0x00, 0x25},
+       {0x0, 0x00, 0x26},
+       {0x0, 0x00, 0x27},
+       {0x0, 0x00, 0x28},
+       {0x0, 0x00, 0x29},
+       {0x0, 0x00, 0x2A},
+       {0x0, 0x00, 0x2B},
+       {0x0, 0x00, 0x2C},
+       {0x0, 0x00, 0x2D},
+       {0x0, 0x00, 0x2E},
+       {0x0, 0x00, 0x2F},
+       {0x0, 0x00, 0x30},
+       {0x0, 0x00, 0x31},
+       {0x0, 0x00, 0x32},
+       {0x0, 0x00, 0x33},
+       {0x0, 0x00, 0x34},
+       {0x0, 0x00, 0x35},
+       {0x0, 0x00, 0x36},
+       {0x0, 0x00, 0x37},
+       {0x0, 0x00, 0x38},
+       {0x0, 0x00, 0x39},
+       {0x0, 0x00, 0x3A},
+       {0x0, 0x00, 0x3B},
+       {0x0, 0x00, 0x3C},
+       {0x0, 0x00, 0x3D},
+       {0x0, 0x00, 0x3E},
+       {0x0, 0x00, 0x3F},
+       {0x0, 0x00, 0x40},
+       {0x0, 0x00, 0x41},
+       {0x0, 0x00, 0x42},
+       {0x0, 0x00, 0x43},
+       {0x0, 0x00, 0x44},
+       {0x0, 0x00, 0x45},
+       {0x0, 0x00, 0x46},
+       {0x0, 0x00, 0x47},
+       {0x0, 0x00, 0x48},
+       {0x0, 0x00, 0x49},
+       {0x0, 0x00, 0x4A},
+       {0x0, 0x00, 0x4B},
+       {0x0, 0x00, 0x4C},
+       {0x0, 0x00, 0x4D},
+       {0x0, 0x00, 0x4E},
+       {0x0, 0x00, 0x4F},
+       {0x0, 0x00, 0x50},
+       {0x0, 0x00, 0x51},
+       {0x0, 0x00, 0x52},
+       {0x0, 0x00, 0x53},
+       {0x0, 0x00, 0x54},
+       {0x0, 0x00, 0x55},
+       {0x0, 0x00, 0x56},
+       {0x0, 0x00, 0x57},
+       {0x0, 0x00, 0x58},
+       {0x0, 0x00, 0x59},
+       {0x0, 0x00, 0x5A},
+       {0x0, 0x00, 0x5B},
+       {0x0, 0x00, 0x5C},
+       {0x0, 0x00, 0x5D},
+       {0x0, 0x00, 0x5E},
+       {0x0, 0x00, 0x5F},
+       {0x0, 0x00, 0x60},
+       {0x0, 0x00, 0x61},
+       {0x0, 0x00, 0x62},
+       {0x0, 0x00, 0x63},
+       {0x0, 0x00, 0x64},
+       {0x0, 0x00, 0x65},
+       {0x0, 0x00, 0x66},
+       {0x0, 0x00, 0x67},
+       {0x0, 0x00, 0x68},
+       {0x0, 0x00, 0x69},
+       {0x0, 0x00, 0x6A},
+       {0x0, 0x00, 0x6B},
+       {0x0, 0x00, 0x6C},
+       {0x0, 0x00, 0x6D},
+       {0x0, 0x00, 0x6E},
+       {0x0, 0x00, 0x6F},
+       {0x0, 0x00, 0x70},
+       {0x0, 0x00, 0x71},
+       {0x0, 0x00, 0x72},
+       {0x0, 0x00, 0x73},
+       {0x0, 0x00, 0x74},
+       {0x0, 0x00, 0x75},
+       {0x0, 0x00, 0x76},
+       {0x0, 0x00, 0x77},
+       {0x0, 0x00, 0x78},
+       {0x0, 0x00, 0x79},
+       {0x0, 0x00, 0x7A},
+       {0x0, 0x00, 0x7B},
+       {0x0, 0x00, 0x7C},
+       {0x0, 0x00, 0x7D},
+       {0x0, 0x00, 0x7E},
+       {0x0, 0x00, 0x7F},
+       {0x0, 0x00, 0x80},
+       {0x0, 0x00, 0x81},
+       {0x0, 0x00, 0x82},
+       {0x0, 0x00, 0x83},
+       {0x0, 0x00, 0x84},
+       {0x0, 0x00, 0x85},
+       {0x0, 0x00, 0x86},
+       {0x0, 0x00, 0x87},
+       {0x0, 0x00, 0x88},
+       {0x0, 0x00, 0x89},
+       {0x0, 0x00, 0x8A},
+       {0x0, 0x00, 0x8B},
+       {0x0, 0x00, 0x8C},
+       {0x0, 0x00, 0x8D},
+       {0x0, 0x00, 0x8E},
+       {0x0, 0x00, 0x8F},
+       {0x0, 0x00, 0x90},
+       {0x0, 0x00, 0x91},
+       {0x0, 0x00, 0x92},
+       {0x0, 0x00, 0x93},
+       {0x0, 0x00, 0x94},
+       {0x0, 0x00, 0x95},
+       {0x0, 0x00, 0x96},
+       {0x0, 0x00, 0x97},
+       {0x0, 0x00, 0x98},
+       {0x0, 0x00, 0x99},
+       {0x0, 0x00, 0x9A},
+       {0x0, 0x00, 0x9B},
+       {0x0, 0x00, 0x9C},
+       {0x0, 0x00, 0x9D},
+       {0x0, 0x00, 0x9E},
+       {0x0, 0x00, 0x9F},
+       {0x0, 0x00, 0xA0},
+       {0x0, 0x00, 0xA1},
+       {0x0, 0x00, 0xA2},
+       {0x0, 0x00, 0xA3},
+       {0x0, 0x00, 0xA4},
+       {0x0, 0x00, 0xA5},
+       {0x0, 0x00, 0xA6},
+       {0x0, 0x00, 0xA7},
+       {0x0, 0x00, 0xA8},
+       {0x0, 0x00, 0xA9},
+       {0x0, 0x00, 0xAA},
+       {0x0, 0x00, 0xAB},
+       {0x0, 0x00, 0xAC},
+       {0x0, 0x00, 0xAD},
+       {0x0, 0x00, 0xAE},
+       {0x0, 0x00, 0xAF},
+       {0x0, 0x00, 0xB0},
+       {0x0, 0x00, 0xB1},
+       {0x0, 0x00, 0xB2},
+       {0x0, 0x00, 0xB3},
+       {0x0, 0x00, 0xB4},
+       {0x0, 0x00, 0xB5},
+       {0x0, 0x00, 0xB6},
+       {0x0, 0x00, 0xB7},
+       {0x0, 0x00, 0xB8},
+       {0x0, 0x00, 0xB9},
+       {0x0, 0x00, 0xBA},
+       {0x0, 0x00, 0xBB},
+       {0x0, 0x00, 0xBC},
+       {0x0, 0x00, 0xBD},
+       {0x0, 0x00, 0xBE},
+       {0x0, 0x00, 0xBF},
+       {0x0, 0x00, 0xC0},
+       {0x0, 0x00, 0xC1},
+       {0x0, 0x00, 0xC2},
+       {0x0, 0x00, 0xC3},
+       {0x0, 0x00, 0xC4},
+       {0x0, 0x00, 0xC5},
+       {0x0, 0x00, 0xC6},
+       {0x0, 0x00, 0xC7},
+       {0x0, 0x00, 0xC8},
+       {0x0, 0x00, 0xC9},
+       {0x0, 0x00, 0xCA},
+       {0x0, 0x00, 0xCB},
+       {0x0, 0x00, 0xCC},
+       {0x1, 0xF4, 0x00},
+       {0x1, 0x38, 0x01},
+       {0x1, 0x40, 0x02},
+       {0x1, 0x0A, 0x03},
+       {0x1, 0x40, 0x04},
+       {0x1, 0x40, 0x05},
+       {0x1, 0x40, 0x06},
+       {0x1, 0x67, 0x07},
+       {0x1, 0x31, 0x08},
+       {0x1, 0x00, 0x09},
+       {0x1, 0x00, 0x0A},
+       {0x1, 0x00, 0x0B},
+       {0x1, 0x14, 0x0C},
+       {0x1, 0x00, 0x0D},
+       {0x1, 0x00, 0x0E},
+       {0x1, 0x00, 0x0F},
+       {0x1, 0x1E, 0x10},
+       {0x1, 0x00, 0x11},
+       {0x1, 0x00, 0x12},
+       {0x1, 0x00, 0x13},
+       {0x1, 0x00, 0x14},
+       {0x1, 0xFF, 0x15},
+       {0x1, 0x01, 0x16},
+       {0x1, 0x32, 0x17},
+       {0x1, 0x23, 0x18},
+       {0x1, 0xCE, 0x19},
+       {0x1, 0x23, 0x1A},
+       {0x1, 0x32, 0x1B},
+       {0x1, 0x8D, 0x1C},
+       {0x1, 0xCE, 0x1D},
+       {0x1, 0x8D, 0x1E},
+       {0x1, 0x00, 0x1F},
+       {0x1, 0x00, 0x20},
+       {0x1, 0xFF, 0x3E},
+       {0x1, 0x02, 0x3F},
+       {0x1, 0x00, 0x40},
+       {0x1, 0x00, 0x41},
+       {0x1, 0x00, 0x42},
+       {0x1, 0x00, 0x43},
+       {0x1, 0x00, 0x44},
+       {0x1, 0x00, 0x45},
+       {0x1, 0x00, 0x46},
+       {0x1, 0x00, 0x47},
+       {0x1, 0x00, 0x48},
+       {0x1, 0x00, 0x49},
+       {0x1, 0x00, 0x4A},
+       {0x1, 0x00, 0x4B},
+       {0x1, 0x00, 0x4C},
+       {0x1, 0x00, 0x4D},
+       {0x1, 0x00, 0x4E},
+       {0x1, 0x00, 0x4F},
+       {0x1, 0x00, 0x50},
+       {0x1, 0x00, 0x51},
+       {0x1, 0x00, 0x52},
+       {0x1, 0x00, 0x53},
+       {0x1, 0x00, 0x54},
+       {0x1, 0x00, 0x55},
+       {0x1, 0x00, 0x56},
+       {0x1, 0x00, 0x57},
+       {0x1, 0x00, 0x58},
+       {0x1, 0x00, 0x59},
+       {0x1, 0x00, 0x5A},
+       {0x2, 0x03, 0x00},
+       {0x2, 0x00, 0x01},
+       {0x2, 0x00, 0x05},
+       {0x2, 0x00, 0x06},
+       {0x2, 0x00, 0x07},
+       {0x2, 0x00, 0x10},
+       {0x2, 0x00, 0x11},
+       /* Strange - looks like the 501 driver doesn't do anything
+        * at insert time except read the EEPROM
+        */
+       {}
+};
+
+/* Data for video camera init before capture.
+ * Capture and decoding by Colin Peart.
+ * This is is for the 3com HomeConnect Lite which is spca501a based.
+ */
+static const __u16 spca501_3com_open_data[][3] = {
+       /* bmRequest,value,index */
+       {0x2, 0x0050, 0x0000},  /* C/S Enable TG soft reset, timing mode=010 */
+       {0x2, 0x0043, 0x0000},  /* C/S Disable TG soft reset, timing mode=010 */
+       {0x2, 0x0002, 0x0005},  /* C/S GPIO */
+       {0x2, 0x0003, 0x0005},  /* C/S GPIO */
+
+#ifdef CCDSP_SET
+       {0x1, 0x0020, 0x0001},  /* CCDSP Options */
+
+       {0x1, 0x0020, 0x0002},  /* CCDSP Black Level */
+       {0x1, 0x006e, 0x0007},  /* CCDSP Gamma options */
+       {0x1, 0x0090, 0x0015},  /* CCDSP Luminance Low */
+       {0x1, 0x00ff, 0x0016},  /* CCDSP Luminance High */
+       {0x1, 0x0003, 0x003F},  /* CCDSP Gamma correction toggle */
+
+#ifdef ALTER_GAMMA
+       {0x1, 0x0010, 0x0008},  /* CCDSP YUV A11 */
+       {0x1, 0x0000, 0x0009},  /* CCDSP YUV A12 */
+       {0x1, 0x0000, 0x000a},  /* CCDSP YUV A13 */
+       {0x1, 0x0000, 0x000b},  /* CCDSP YUV A21 */
+       {0x1, 0x0010, 0x000c},  /* CCDSP YUV A22 */
+       {0x1, 0x0000, 0x000d},  /* CCDSP YUV A23 */
+       {0x1, 0x0000, 0x000e},  /* CCDSP YUV A31 */
+       {0x1, 0x0000, 0x000f},  /* CCDSP YUV A32 */
+       {0x1, 0x0010, 0x0010},  /* CCDSP YUV A33 */
+       {0x1, 0x0000, 0x0011},  /* CCDSP R Offset */
+       {0x1, 0x0000, 0x0012},  /* CCDSP G Offset */
+       {0x1, 0x0001, 0x0013},  /* CCDSP B Offset */
+       {0x1, 0x0001, 0x0014},  /* CCDSP BG Offset */
+       {0x1, 0x003f, 0x00C1},  /* CCDSP Gamma Correction Enable */
+#endif
+#endif
+
+#ifdef TG_SET
+       {0x0, 0x00fc, 0x0000},  /* TG Shutter Speed High Bits */
+       {0x0, 0x0000, 0x0001},  /* TG Shutter Speed Low Bits */
+       {0x0, 0x00e4, 0x0004},  /* TG DCLK*2 Adjust */
+       {0x0, 0x0008, 0x0005},  /* TG ADCK Adjust */
+       {0x0, 0x0003, 0x0006},  /* TG FR Phase Adjust */
+       {0x0, 0x0001, 0x0007},  /* TG FCDS Phase Adjust */
+       {0x0, 0x0039, 0x0008},  /* TG FS Phase Adjust */
+       {0x0, 0x0088, 0x000a},  /* TG MH1 */
+       {0x0, 0x0003, 0x000f},  /* TG Pixel ID */
+
+       /* Like below, unexplained toglleing */
+       {0x0, 0x0080, 0x000c},
+       {0x0, 0x0000, 0x000d},
+       {0x0, 0x0080, 0x000c},
+       {0x0, 0x0004, 0x000d},
+       {0x0, 0x0000, 0x000c},
+       {0x0, 0x0000, 0x000d},
+       {0x0, 0x0040, 0x000c},
+       {0x0, 0x0017, 0x000d},
+       {0x0, 0x00c0, 0x000c},
+       {0x0, 0x0000, 0x000d},
+       {0x0, 0x0080, 0x000c},
+       {0x0, 0x0006, 0x000d},
+       {0x0, 0x0080, 0x000c},
+       {0x0, 0x0004, 0x000d},
+       {0x0, 0x0002, 0x0003},
+#endif
+
+#ifdef DSPWIN_SET
+       {0x1, 0x001c, 0x0017},  /* CCDSP W1 Start X */
+       {0x1, 0x00e2, 0x0019},  /* CCDSP W2 Start X */
+       {0x1, 0x001c, 0x001b},  /* CCDSP W3 Start X */
+       {0x1, 0x00e2, 0x001d},  /* CCDSP W4 Start X */
+       {0x1, 0x00aa, 0x001f},  /* CCDSP W5 Start X */
+       {0x1, 0x0070, 0x0020},  /* CCDSP W5 Start Y */
+#endif
+       {0x0, 0x0001, 0x0010},  /* TG Start Clock */
+
+/*     {0x2, 0x006a, 0x0001},   * C/S Enable ISOSYNCH Packet Engine */
+       {0x2, 0x0068, 0x0001},  /* C/S Diable ISOSYNCH Packet Engine */
+       {0x2, 0x0000, 0x0005},
+       {0x2, 0x0043, 0x0000},  /* C/S Set Timing Mode, Disable TG soft reset */
+       {0x2, 0x0043, 0x0000},  /* C/S Set Timing Mode, Disable TG soft reset */
+       {0x2, 0x0002, 0x0005},  /* C/S GPIO */
+       {0x2, 0x0003, 0x0005},  /* C/S GPIO */
+
+       {0x2, 0x006a, 0x0001},  /* C/S Enable ISOSYNCH Packet Engine */
+       {}
+};
+
+/*
+ * Data used to initialize a SPCA501C with HV7131B sensor.
+ * From a capture file taken with USBSnoop v 1.5
+ * I have a "SPCA501C pc camera chipset" manual by sunplus, but some
+ * of the value meanings are obscure or simply "reserved".
+ * to do list:
+ * 1) Understand what every value means
+ * 2) Understand why some values seem to appear more than once
+ * 3) Write a small comment for each line of the following arrays.
+ */
+static const __u16 spca501c_arowana_open_data[][3] = {
+       /* bmRequest,value,index */
+       {0x02, 0x0007, 0x0005},
+       {0x02, 0xa048, 0x0000},
+       {0x05, 0x0022, 0x0004},
+       {0x01, 0x0006, 0x0011},
+       {0x01, 0x00ff, 0x0012},
+       {0x01, 0x0014, 0x0013},
+       {0x01, 0x0000, 0x0014},
+       {0x01, 0x0042, 0x0051},
+       {0x01, 0x0040, 0x0052},
+       {0x01, 0x0051, 0x0053},
+       {0x01, 0x0040, 0x0054},
+       {0x01, 0x0000, 0x0055},
+       {0x00, 0x0025, 0x0000},
+       {0x00, 0x0026, 0x0000},
+       {0x00, 0x0001, 0x0000},
+       {0x00, 0x0027, 0x0000},
+       {0x00, 0x008a, 0x0000},
+       {}
+};
+
+static const __u16 spca501c_arowana_init_data[][3] = {
+       /* bmRequest,value,index */
+       {0x02, 0x0007, 0x0005},
+       {0x02, 0xa048, 0x0000},
+       {0x05, 0x0022, 0x0004},
+       {0x01, 0x0006, 0x0011},
+       {0x01, 0x00ff, 0x0012},
+       {0x01, 0x0014, 0x0013},
+       {0x01, 0x0000, 0x0014},
+       {0x01, 0x0042, 0x0051},
+       {0x01, 0x0040, 0x0052},
+       {0x01, 0x0051, 0x0053},
+       {0x01, 0x0040, 0x0054},
+       {0x01, 0x0000, 0x0055},
+       {0x00, 0x0025, 0x0000},
+       {0x00, 0x0026, 0x0000},
+       {0x00, 0x0001, 0x0000},
+       {0x00, 0x0027, 0x0000},
+       {0x00, 0x008a, 0x0000},
+       {0x02, 0x0000, 0x0005},
+       {0x02, 0x0007, 0x0005},
+       {0x02, 0x2000, 0x0000},
+       {0x05, 0x0022, 0x0004},
+       {0x05, 0x0015, 0x0001},
+       {0x05, 0x00ea, 0x0000},
+       {0x05, 0x0021, 0x0001},
+       {0x05, 0x00d2, 0x0000},
+       {0x05, 0x0023, 0x0001},
+       {0x05, 0x0003, 0x0000},
+       {0x05, 0x0030, 0x0001},
+       {0x05, 0x002b, 0x0000},
+       {0x05, 0x0031, 0x0001},
+       {0x05, 0x0023, 0x0000},
+       {0x05, 0x0032, 0x0001},
+       {0x05, 0x0023, 0x0000},
+       {0x05, 0x0033, 0x0001},
+       {0x05, 0x0023, 0x0000},
+       {0x05, 0x0034, 0x0001},
+       {0x05, 0x0002, 0x0000},
+       {0x05, 0x0050, 0x0001},
+       {0x05, 0x0000, 0x0000},
+       {0x05, 0x0051, 0x0001},
+       {0x05, 0x0000, 0x0000},
+       {0x05, 0x0052, 0x0001},
+       {0x05, 0x0000, 0x0000},
+       {0x05, 0x0054, 0x0001},
+       {0x05, 0x0001, 0x0000},
+       {0x00, 0x0000, 0x0001},
+       {0x00, 0x0000, 0x0002},
+       {0x00, 0x000c, 0x0003},
+       {0x00, 0x0000, 0x0004},
+       {0x00, 0x0090, 0x0005},
+       {0x00, 0x0000, 0x0006},
+       {0x00, 0x0040, 0x0007},
+       {0x00, 0x00c0, 0x0008},
+       {0x00, 0x004a, 0x0009},
+       {0x00, 0x0000, 0x000a},
+       {0x00, 0x0000, 0x000b},
+       {0x00, 0x0001, 0x000c},
+       {0x00, 0x0001, 0x000d},
+       {0x00, 0x0000, 0x000e},
+       {0x00, 0x0002, 0x000f},
+       {0x00, 0x0001, 0x0010},
+       {0x00, 0x0000, 0x0011},
+       {0x00, 0x0000, 0x0012},
+       {0x00, 0x0002, 0x0020},
+       {0x00, 0x0080, 0x0021},
+       {0x00, 0x0001, 0x0022},
+       {0x00, 0x00e0, 0x0023},
+       {0x00, 0x0000, 0x0024},
+       {0x00, 0x00d5, 0x0025},
+       {0x00, 0x0000, 0x0026},
+       {0x00, 0x000b, 0x0027},
+       {0x00, 0x0000, 0x0046},
+       {0x00, 0x0000, 0x0047},
+       {0x00, 0x0000, 0x0048},
+       {0x00, 0x0000, 0x0049},
+       {0x00, 0x0008, 0x004a},
+       {0xff, 0x0000, 0x00d0},
+       {0xff, 0x00d8, 0x00d1},
+       {0xff, 0x0000, 0x00d4},
+       {0xff, 0x0000, 0x00d5},
+       {0x01, 0x00a6, 0x0000},
+       {0x01, 0x0028, 0x0001},
+       {0x01, 0x0000, 0x0002},
+       {0x01, 0x000a, 0x0003},
+       {0x01, 0x0040, 0x0004},
+       {0x01, 0x0066, 0x0007},
+       {0x01, 0x0011, 0x0008},
+       {0x01, 0x0032, 0x0009},
+       {0x01, 0x00fd, 0x000a},
+       {0x01, 0x0038, 0x000b},
+       {0x01, 0x00d1, 0x000c},
+       {0x01, 0x00f7, 0x000d},
+       {0x01, 0x00ed, 0x000e},
+       {0x01, 0x00d8, 0x000f},
+       {0x01, 0x0038, 0x0010},
+       {0x01, 0x00ff, 0x0015},
+       {0x01, 0x0001, 0x0016},
+       {0x01, 0x0032, 0x0017},
+       {0x01, 0x0023, 0x0018},
+       {0x01, 0x00ce, 0x0019},
+       {0x01, 0x0023, 0x001a},
+       {0x01, 0x0032, 0x001b},
+       {0x01, 0x008d, 0x001c},
+       {0x01, 0x00ce, 0x001d},
+       {0x01, 0x008d, 0x001e},
+       {0x01, 0x0000, 0x001f},
+       {0x01, 0x0000, 0x0020},
+       {0x01, 0x00ff, 0x003e},
+       {0x01, 0x0003, 0x003f},
+       {0x01, 0x0000, 0x0040},
+       {0x01, 0x0035, 0x0041},
+       {0x01, 0x0053, 0x0042},
+       {0x01, 0x0069, 0x0043},
+       {0x01, 0x007c, 0x0044},
+       {0x01, 0x008c, 0x0045},
+       {0x01, 0x009a, 0x0046},
+       {0x01, 0x00a8, 0x0047},
+       {0x01, 0x00b4, 0x0048},
+       {0x01, 0x00bf, 0x0049},
+       {0x01, 0x00ca, 0x004a},
+       {0x01, 0x00d4, 0x004b},
+       {0x01, 0x00dd, 0x004c},
+       {0x01, 0x00e7, 0x004d},
+       {0x01, 0x00ef, 0x004e},
+       {0x01, 0x00f8, 0x004f},
+       {0x01, 0x00ff, 0x0050},
+       {0x01, 0x0001, 0x0056},
+       {0x01, 0x0060, 0x0057},
+       {0x01, 0x0040, 0x0058},
+       {0x01, 0x0011, 0x0059},
+       {0x01, 0x0001, 0x005a},
+       {0x02, 0x0007, 0x0005},
+       {0x02, 0xa048, 0x0000},
+       {0x02, 0x0007, 0x0005},
+       {0x02, 0x0015, 0x0006},
+       {0x02, 0x100a, 0x0007},
+       {0x02, 0xa048, 0x0000},
+       {0x02, 0xc002, 0x0001},
+       {0x02, 0x000f, 0x0005},
+       {0x02, 0xa048, 0x0000},
+       {0x05, 0x0022, 0x0004},
+       {0x05, 0x0025, 0x0001},
+       {0x05, 0x0000, 0x0000},
+       {0x05, 0x0026, 0x0001},
+       {0x05, 0x0001, 0x0000},
+       {0x05, 0x0027, 0x0001},
+       {0x05, 0x0000, 0x0000},
+       {0x05, 0x0001, 0x0001},
+       {0x05, 0x0000, 0x0000},
+       {0x05, 0x0021, 0x0001},
+       {0x05, 0x00d2, 0x0000},
+       {0x05, 0x0020, 0x0001},
+       {0x05, 0x0000, 0x0000},
+       {0x00, 0x0090, 0x0005},
+       {0x01, 0x00a6, 0x0000},
+       {0x02, 0x0007, 0x0005},
+       {0x02, 0x2000, 0x0000},
+       {0x05, 0x0022, 0x0004},
+       {0x05, 0x0015, 0x0001},
+       {0x05, 0x00ea, 0x0000},
+       {0x05, 0x0021, 0x0001},
+       {0x05, 0x00d2, 0x0000},
+       {0x05, 0x0023, 0x0001},
+       {0x05, 0x0003, 0x0000},
+       {0x05, 0x0030, 0x0001},
+       {0x05, 0x002b, 0x0000},
+       {0x05, 0x0031, 0x0001},
+       {0x05, 0x0023, 0x0000},
+       {0x05, 0x0032, 0x0001},
+       {0x05, 0x0023, 0x0000},
+       {0x05, 0x0033, 0x0001},
+       {0x05, 0x0023, 0x0000},
+       {0x05, 0x0034, 0x0001},
+       {0x05, 0x0002, 0x0000},
+       {0x05, 0x0050, 0x0001},
+       {0x05, 0x0000, 0x0000},
+       {0x05, 0x0051, 0x0001},
+       {0x05, 0x0000, 0x0000},
+       {0x05, 0x0052, 0x0001},
+       {0x05, 0x0000, 0x0000},
+       {0x05, 0x0054, 0x0001},
+       {0x05, 0x0001, 0x0000},
+       {0x00, 0x0000, 0x0001},
+       {0x00, 0x0000, 0x0002},
+       {0x00, 0x000c, 0x0003},
+       {0x00, 0x0000, 0x0004},
+       {0x00, 0x0090, 0x0005},
+       {0x00, 0x0000, 0x0006},
+       {0x00, 0x0040, 0x0007},
+       {0x00, 0x00c0, 0x0008},
+       {0x00, 0x004a, 0x0009},
+       {0x00, 0x0000, 0x000a},
+       {0x00, 0x0000, 0x000b},
+       {0x00, 0x0001, 0x000c},
+       {0x00, 0x0001, 0x000d},
+       {0x00, 0x0000, 0x000e},
+       {0x00, 0x0002, 0x000f},
+       {0x00, 0x0001, 0x0010},
+       {0x00, 0x0000, 0x0011},
+       {0x00, 0x0000, 0x0012},
+       {0x00, 0x0002, 0x0020},
+       {0x00, 0x0080, 0x0021},
+       {0x00, 0x0001, 0x0022},
+       {0x00, 0x00e0, 0x0023},
+       {0x00, 0x0000, 0x0024},
+       {0x00, 0x00d5, 0x0025},
+       {0x00, 0x0000, 0x0026},
+       {0x00, 0x000b, 0x0027},
+       {0x00, 0x0000, 0x0046},
+       {0x00, 0x0000, 0x0047},
+       {0x00, 0x0000, 0x0048},
+       {0x00, 0x0000, 0x0049},
+       {0x00, 0x0008, 0x004a},
+       {0xff, 0x0000, 0x00d0},
+       {0xff, 0x00d8, 0x00d1},
+       {0xff, 0x0000, 0x00d4},
+       {0xff, 0x0000, 0x00d5},
+       {0x01, 0x00a6, 0x0000},
+       {0x01, 0x0028, 0x0001},
+       {0x01, 0x0000, 0x0002},
+       {0x01, 0x000a, 0x0003},
+       {0x01, 0x0040, 0x0004},
+       {0x01, 0x0066, 0x0007},
+       {0x01, 0x0011, 0x0008},
+       {0x01, 0x0032, 0x0009},
+       {0x01, 0x00fd, 0x000a},
+       {0x01, 0x0038, 0x000b},
+       {0x01, 0x00d1, 0x000c},
+       {0x01, 0x00f7, 0x000d},
+       {0x01, 0x00ed, 0x000e},
+       {0x01, 0x00d8, 0x000f},
+       {0x01, 0x0038, 0x0010},
+       {0x01, 0x00ff, 0x0015},
+       {0x01, 0x0001, 0x0016},
+       {0x01, 0x0032, 0x0017},
+       {0x01, 0x0023, 0x0018},
+       {0x01, 0x00ce, 0x0019},
+       {0x01, 0x0023, 0x001a},
+       {0x01, 0x0032, 0x001b},
+       {0x01, 0x008d, 0x001c},
+       {0x01, 0x00ce, 0x001d},
+       {0x01, 0x008d, 0x001e},
+       {0x01, 0x0000, 0x001f},
+       {0x01, 0x0000, 0x0020},
+       {0x01, 0x00ff, 0x003e},
+       {0x01, 0x0003, 0x003f},
+       {0x01, 0x0000, 0x0040},
+       {0x01, 0x0035, 0x0041},
+       {0x01, 0x0053, 0x0042},
+       {0x01, 0x0069, 0x0043},
+       {0x01, 0x007c, 0x0044},
+       {0x01, 0x008c, 0x0045},
+       {0x01, 0x009a, 0x0046},
+       {0x01, 0x00a8, 0x0047},
+       {0x01, 0x00b4, 0x0048},
+       {0x01, 0x00bf, 0x0049},
+       {0x01, 0x00ca, 0x004a},
+       {0x01, 0x00d4, 0x004b},
+       {0x01, 0x00dd, 0x004c},
+       {0x01, 0x00e7, 0x004d},
+       {0x01, 0x00ef, 0x004e},
+       {0x01, 0x00f8, 0x004f},
+       {0x01, 0x00ff, 0x0050},
+       {0x01, 0x0001, 0x0056},
+       {0x01, 0x0060, 0x0057},
+       {0x01, 0x0040, 0x0058},
+       {0x01, 0x0011, 0x0059},
+       {0x01, 0x0001, 0x005a},
+       {0x02, 0x0007, 0x0005},
+       {0x02, 0xa048, 0x0000},
+       {0x02, 0x0007, 0x0005},
+       {0x02, 0x0015, 0x0006},
+       {0x02, 0x100a, 0x0007},
+       {0x02, 0xa048, 0x0000},
+       {0x02, 0xc002, 0x0001},
+       {0x02, 0x000f, 0x0005},
+       {0x02, 0xa048, 0x0000},
+       {0x05, 0x0022, 0x0004},
+       {0x05, 0x0025, 0x0001},
+       {0x05, 0x0000, 0x0000},
+       {0x05, 0x0026, 0x0001},
+       {0x05, 0x0001, 0x0000},
+       {0x05, 0x0027, 0x0001},
+       {0x05, 0x0000, 0x0000},
+       {0x05, 0x0001, 0x0001},
+       {0x05, 0x0000, 0x0000},
+       {0x05, 0x0021, 0x0001},
+       {0x05, 0x00d2, 0x0000},
+       {0x05, 0x0020, 0x0001},
+       {0x05, 0x0000, 0x0000},
+       {0x00, 0x0090, 0x0005},
+       {0x01, 0x00a6, 0x0000},
+       {0x01, 0x0003, 0x003f},
+       {0x01, 0x0001, 0x0056},
+       {0x01, 0x0011, 0x0008},
+       {0x01, 0x0032, 0x0009},
+       {0x01, 0xfffd, 0x000a},
+       {0x01, 0x0023, 0x000b},
+       {0x01, 0xffea, 0x000c},
+       {0x01, 0xfff4, 0x000d},
+       {0x01, 0xfffc, 0x000e},
+       {0x01, 0xffe3, 0x000f},
+       {0x01, 0x001f, 0x0010},
+       {0x01, 0x00a8, 0x0001},
+       {0x01, 0x0067, 0x0007},
+       {0x01, 0x0032, 0x0017},
+       {0x01, 0x0023, 0x0018},
+       {0x01, 0x00ce, 0x0019},
+       {0x01, 0x0023, 0x001a},
+       {0x01, 0x0032, 0x001b},
+       {0x01, 0x008d, 0x001c},
+       {0x01, 0x00ce, 0x001d},
+       {0x01, 0x008d, 0x001e},
+       {0x01, 0x00c8, 0x0015},
+       {0x01, 0x0032, 0x0016},
+       {0x01, 0x0000, 0x0011},
+       {0x01, 0x0000, 0x0012},
+       {0x01, 0x0000, 0x0013},
+       {0x01, 0x000a, 0x0003},
+       {0x02, 0xc002, 0x0001},
+       {0x02, 0x0007, 0x0005},
+       {0x02, 0xc000, 0x0001},
+       {0x02, 0x0000, 0x0005},
+       {0x02, 0x0007, 0x0005},
+       {0x02, 0x2000, 0x0000},
+       {0x05, 0x0022, 0x0004},
+       {0x05, 0x0015, 0x0001},
+       {0x05, 0x00ea, 0x0000},
+       {0x05, 0x0021, 0x0001},
+       {0x05, 0x00d2, 0x0000},
+       {0x05, 0x0023, 0x0001},
+       {0x05, 0x0003, 0x0000},
+       {0x05, 0x0030, 0x0001},
+       {0x05, 0x002b, 0x0000},
+       {0x05, 0x0031, 0x0001},
+       {0x05, 0x0023, 0x0000},
+       {0x05, 0x0032, 0x0001},
+       {0x05, 0x0023, 0x0000},
+       {0x05, 0x0033, 0x0001},
+       {0x05, 0x0023, 0x0000},
+       {0x05, 0x0034, 0x0001},
+       {0x05, 0x0002, 0x0000},
+       {0x05, 0x0050, 0x0001},
+       {0x05, 0x0000, 0x0000},
+       {0x05, 0x0051, 0x0001},
+       {0x05, 0x0000, 0x0000},
+       {0x05, 0x0052, 0x0001},
+       {0x05, 0x0000, 0x0000},
+       {0x05, 0x0054, 0x0001},
+       {0x05, 0x0001, 0x0000},
+       {0x00, 0x0000, 0x0001},
+       {0x00, 0x0000, 0x0002},
+       {0x00, 0x000c, 0x0003},
+       {0x00, 0x0000, 0x0004},
+       {0x00, 0x0090, 0x0005},
+       {0x00, 0x0000, 0x0006},
+       {0x00, 0x0040, 0x0007},
+       {0x00, 0x00c0, 0x0008},
+       {0x00, 0x004a, 0x0009},
+       {0x00, 0x0000, 0x000a},
+       {0x00, 0x0000, 0x000b},
+       {0x00, 0x0001, 0x000c},
+       {0x00, 0x0001, 0x000d},
+       {0x00, 0x0000, 0x000e},
+       {0x00, 0x0002, 0x000f},
+       {0x00, 0x0001, 0x0010},
+       {0x00, 0x0000, 0x0011},
+       {0x00, 0x0000, 0x0012},
+       {0x00, 0x0002, 0x0020},
+       {0x00, 0x0080, 0x0021},
+       {0x00, 0x0001, 0x0022},
+       {0x00, 0x00e0, 0x0023},
+       {0x00, 0x0000, 0x0024},
+       {0x00, 0x00d5, 0x0025},
+       {0x00, 0x0000, 0x0026},
+       {0x00, 0x000b, 0x0027},
+       {0x00, 0x0000, 0x0046},
+       {0x00, 0x0000, 0x0047},
+       {0x00, 0x0000, 0x0048},
+       {0x00, 0x0000, 0x0049},
+       {0x00, 0x0008, 0x004a},
+       {0xff, 0x0000, 0x00d0},
+       {0xff, 0x00d8, 0x00d1},
+       {0xff, 0x0000, 0x00d4},
+       {0xff, 0x0000, 0x00d5},
+       {0x01, 0x00a6, 0x0000},
+       {0x01, 0x0028, 0x0001},
+       {0x01, 0x0000, 0x0002},
+       {0x01, 0x000a, 0x0003},
+       {0x01, 0x0040, 0x0004},
+       {0x01, 0x0066, 0x0007},
+       {0x01, 0x0011, 0x0008},
+       {0x01, 0x0032, 0x0009},
+       {0x01, 0x00fd, 0x000a},
+       {0x01, 0x0038, 0x000b},
+       {0x01, 0x00d1, 0x000c},
+       {0x01, 0x00f7, 0x000d},
+       {0x01, 0x00ed, 0x000e},
+       {0x01, 0x00d8, 0x000f},
+       {0x01, 0x0038, 0x0010},
+       {0x01, 0x00ff, 0x0015},
+       {0x01, 0x0001, 0x0016},
+       {0x01, 0x0032, 0x0017},
+       {0x01, 0x0023, 0x0018},
+       {0x01, 0x00ce, 0x0019},
+       {0x01, 0x0023, 0x001a},
+       {0x01, 0x0032, 0x001b},
+       {0x01, 0x008d, 0x001c},
+       {0x01, 0x00ce, 0x001d},
+       {0x01, 0x008d, 0x001e},
+       {0x01, 0x0000, 0x001f},
+       {0x01, 0x0000, 0x0020},
+       {0x01, 0x00ff, 0x003e},
+       {0x01, 0x0003, 0x003f},
+       {0x01, 0x0000, 0x0040},
+       {0x01, 0x0035, 0x0041},
+       {0x01, 0x0053, 0x0042},
+       {0x01, 0x0069, 0x0043},
+       {0x01, 0x007c, 0x0044},
+       {0x01, 0x008c, 0x0045},
+       {0x01, 0x009a, 0x0046},
+       {0x01, 0x00a8, 0x0047},
+       {0x01, 0x00b4, 0x0048},
+       {0x01, 0x00bf, 0x0049},
+       {0x01, 0x00ca, 0x004a},
+       {0x01, 0x00d4, 0x004b},
+       {0x01, 0x00dd, 0x004c},
+       {0x01, 0x00e7, 0x004d},
+       {0x01, 0x00ef, 0x004e},
+       {0x01, 0x00f8, 0x004f},
+       {0x01, 0x00ff, 0x0050},
+       {0x01, 0x0001, 0x0056},
+       {0x01, 0x0060, 0x0057},
+       {0x01, 0x0040, 0x0058},
+       {0x01, 0x0011, 0x0059},
+       {0x01, 0x0001, 0x005a},
+       {0x02, 0x0007, 0x0005},
+       {0x02, 0xa048, 0x0000},
+       {0x02, 0x0007, 0x0005},
+       {0x02, 0x0015, 0x0006},
+       {0x02, 0x100a, 0x0007},
+       {0x02, 0xa048, 0x0000},
+       {0x02, 0xc002, 0x0001},
+       {0x02, 0x000f, 0x0005},
+       {0x02, 0xa048, 0x0000},
+       {0x05, 0x0022, 0x0004},
+       {0x05, 0x0025, 0x0001},
+       {0x05, 0x0000, 0x0000},
+       {0x05, 0x0026, 0x0001},
+       {0x05, 0x0001, 0x0000},
+       {0x05, 0x0027, 0x0001},
+       {0x05, 0x0000, 0x0000},
+       {0x05, 0x0001, 0x0001},
+       {0x05, 0x0000, 0x0000},
+       {0x05, 0x0021, 0x0001},
+       {0x05, 0x00d2, 0x0000},
+       {0x05, 0x0020, 0x0001},
+       {0x05, 0x0000, 0x0000},
+       {0x00, 0x0090, 0x0005},
+       {0x01, 0x00a6, 0x0000},
+       {0x02, 0x0007, 0x0005},
+       {0x02, 0x2000, 0x0000},
+       {0x05, 0x0022, 0x0004},
+       {0x05, 0x0015, 0x0001},
+       {0x05, 0x00ea, 0x0000},
+       {0x05, 0x0021, 0x0001},
+       {0x05, 0x00d2, 0x0000},
+       {0x05, 0x0023, 0x0001},
+       {0x05, 0x0003, 0x0000},
+       {0x05, 0x0030, 0x0001},
+       {0x05, 0x002b, 0x0000},
+       {0x05, 0x0031, 0x0001},
+       {0x05, 0x0023, 0x0000},
+       {0x05, 0x0032, 0x0001},
+       {0x05, 0x0023, 0x0000},
+       {0x05, 0x0033, 0x0001},
+       {0x05, 0x0023, 0x0000},
+       {0x05, 0x0034, 0x0001},
+       {0x05, 0x0002, 0x0000},
+       {0x05, 0x0050, 0x0001},
+       {0x05, 0x0000, 0x0000},
+       {0x05, 0x0051, 0x0001},
+       {0x05, 0x0000, 0x0000},
+       {0x05, 0x0052, 0x0001},
+       {0x05, 0x0000, 0x0000},
+       {0x05, 0x0054, 0x0001},
+       {0x05, 0x0001, 0x0000},
+       {0x00, 0x0000, 0x0001},
+       {0x00, 0x0000, 0x0002},
+       {0x00, 0x000c, 0x0003},
+       {0x00, 0x0000, 0x0004},
+       {0x00, 0x0090, 0x0005},
+       {0x00, 0x0000, 0x0006},
+       {0x00, 0x0040, 0x0007},
+       {0x00, 0x00c0, 0x0008},
+       {0x00, 0x004a, 0x0009},
+       {0x00, 0x0000, 0x000a},
+       {0x00, 0x0000, 0x000b},
+       {0x00, 0x0001, 0x000c},
+       {0x00, 0x0001, 0x000d},
+       {0x00, 0x0000, 0x000e},
+       {0x00, 0x0002, 0x000f},
+       {0x00, 0x0001, 0x0010},
+       {0x00, 0x0000, 0x0011},
+       {0x00, 0x0000, 0x0012},
+       {0x00, 0x0002, 0x0020},
+       {0x00, 0x0080, 0x0021},
+       {0x00, 0x0001, 0x0022},
+       {0x00, 0x00e0, 0x0023},
+       {0x00, 0x0000, 0x0024},
+       {0x00, 0x00d5, 0x0025},
+       {0x00, 0x0000, 0x0026},
+       {0x00, 0x000b, 0x0027},
+       {0x00, 0x0000, 0x0046},
+       {0x00, 0x0000, 0x0047},
+       {0x00, 0x0000, 0x0048},
+       {0x00, 0x0000, 0x0049},
+       {0x00, 0x0008, 0x004a},
+       {0xff, 0x0000, 0x00d0},
+       {0xff, 0x00d8, 0x00d1},
+       {0xff, 0x0000, 0x00d4},
+       {0xff, 0x0000, 0x00d5},
+       {0x01, 0x00a6, 0x0000},
+       {0x01, 0x0028, 0x0001},
+       {0x01, 0x0000, 0x0002},
+       {0x01, 0x000a, 0x0003},
+       {0x01, 0x0040, 0x0004},
+       {0x01, 0x0066, 0x0007},
+       {0x01, 0x0011, 0x0008},
+       {0x01, 0x0032, 0x0009},
+       {0x01, 0x00fd, 0x000a},
+       {0x01, 0x0038, 0x000b},
+       {0x01, 0x00d1, 0x000c},
+       {0x01, 0x00f7, 0x000d},
+       {0x01, 0x00ed, 0x000e},
+       {0x01, 0x00d8, 0x000f},
+       {0x01, 0x0038, 0x0010},
+       {0x01, 0x00ff, 0x0015},
+       {0x01, 0x0001, 0x0016},
+       {0x01, 0x0032, 0x0017},
+       {0x01, 0x0023, 0x0018},
+       {0x01, 0x00ce, 0x0019},
+       {0x01, 0x0023, 0x001a},
+       {0x01, 0x0032, 0x001b},
+       {0x01, 0x008d, 0x001c},
+       {0x01, 0x00ce, 0x001d},
+       {0x01, 0x008d, 0x001e},
+       {0x01, 0x0000, 0x001f},
+       {0x01, 0x0000, 0x0020},
+       {0x01, 0x00ff, 0x003e},
+       {0x01, 0x0003, 0x003f},
+       {0x01, 0x0000, 0x0040},
+       {0x01, 0x0035, 0x0041},
+       {0x01, 0x0053, 0x0042},
+       {0x01, 0x0069, 0x0043},
+       {0x01, 0x007c, 0x0044},
+       {0x01, 0x008c, 0x0045},
+       {0x01, 0x009a, 0x0046},
+       {0x01, 0x00a8, 0x0047},
+       {0x01, 0x00b4, 0x0048},
+       {0x01, 0x00bf, 0x0049},
+       {0x01, 0x00ca, 0x004a},
+       {0x01, 0x00d4, 0x004b},
+       {0x01, 0x00dd, 0x004c},
+       {0x01, 0x00e7, 0x004d},
+       {0x01, 0x00ef, 0x004e},
+       {0x01, 0x00f8, 0x004f},
+       {0x01, 0x00ff, 0x0050},
+       {0x01, 0x0001, 0x0056},
+       {0x01, 0x0060, 0x0057},
+       {0x01, 0x0040, 0x0058},
+       {0x01, 0x0011, 0x0059},
+       {0x01, 0x0001, 0x005a},
+       {0x02, 0x0007, 0x0005},
+       {0x02, 0xa048, 0x0000},
+       {0x02, 0x0007, 0x0005},
+       {0x02, 0x0015, 0x0006},
+       {0x02, 0x100a, 0x0007},
+       {0x02, 0xa048, 0x0000},
+       {0x02, 0xc002, 0x0001},
+       {0x02, 0x000f, 0x0005},
+       {0x02, 0xa048, 0x0000},
+       {0x05, 0x0022, 0x0004},
+       {0x05, 0x0025, 0x0001},
+       {0x05, 0x0000, 0x0000},
+       {0x05, 0x0026, 0x0001},
+       {0x05, 0x0001, 0x0000},
+       {0x05, 0x0027, 0x0001},
+       {0x05, 0x0000, 0x0000},
+       {0x05, 0x0001, 0x0001},
+       {0x05, 0x0000, 0x0000},
+       {0x05, 0x0021, 0x0001},
+       {0x05, 0x00d2, 0x0000},
+       {0x05, 0x0020, 0x0001},
+       {0x05, 0x0000, 0x0000},
+       {0x00, 0x0090, 0x0005},
+       {0x01, 0x00a6, 0x0000},
+       {0x05, 0x0026, 0x0001},
+       {0x05, 0x0001, 0x0000},
+       {0x05, 0x0027, 0x0001},
+       {0x05, 0x000f, 0x0000},
+       {0x01, 0x0003, 0x003f},
+       {0x01, 0x0001, 0x0056},
+       {0x01, 0x0011, 0x0008},
+       {0x01, 0x0032, 0x0009},
+       {0x01, 0xfffd, 0x000a},
+       {0x01, 0x0023, 0x000b},
+       {0x01, 0xffea, 0x000c},
+       {0x01, 0xfff4, 0x000d},
+       {0x01, 0xfffc, 0x000e},
+       {0x01, 0xffe3, 0x000f},
+       {0x01, 0x001f, 0x0010},
+       {0x01, 0x00a8, 0x0001},
+       {0x01, 0x0067, 0x0007},
+       {0x01, 0x0042, 0x0051},
+       {0x01, 0x0051, 0x0053},
+       {0x01, 0x000a, 0x0003},
+       {0x02, 0xc002, 0x0001},
+       {0x02, 0x0007, 0x0005},
+       {0x02, 0xc000, 0x0001},
+       {0x02, 0x0000, 0x0005},
+       {0x02, 0x0007, 0x0005},
+       {0x02, 0x2000, 0x0000},
+       {0x05, 0x0022, 0x0004},
+       {0x05, 0x0015, 0x0001},
+       {0x05, 0x00ea, 0x0000},
+       {0x05, 0x0021, 0x0001},
+       {0x05, 0x00d2, 0x0000},
+       {0x05, 0x0023, 0x0001},
+       {0x05, 0x0003, 0x0000},
+       {0x05, 0x0030, 0x0001},
+       {0x05, 0x002b, 0x0000},
+       {0x05, 0x0031, 0x0001},
+       {0x05, 0x0023, 0x0000},
+       {0x05, 0x0032, 0x0001},
+       {0x05, 0x0023, 0x0000},
+       {0x05, 0x0033, 0x0001},
+       {0x05, 0x0023, 0x0000},
+       {0x05, 0x0034, 0x0001},
+       {0x05, 0x0002, 0x0000},
+       {0x05, 0x0050, 0x0001},
+       {0x05, 0x0000, 0x0000},
+       {0x05, 0x0051, 0x0001},
+       {0x05, 0x0000, 0x0000},
+       {0x05, 0x0052, 0x0001},
+       {0x05, 0x0000, 0x0000},
+       {0x05, 0x0054, 0x0001},
+       {0x05, 0x0001, 0x0000},
+       {0x00, 0x0000, 0x0001},
+       {0x00, 0x0000, 0x0002},
+       {0x00, 0x000c, 0x0003},
+       {0x00, 0x0000, 0x0004},
+       {0x00, 0x0090, 0x0005},
+       {0x00, 0x0000, 0x0006},
+       {0x00, 0x0040, 0x0007},
+       {0x00, 0x00c0, 0x0008},
+       {0x00, 0x004a, 0x0009},
+       {0x00, 0x0000, 0x000a},
+       {0x00, 0x0000, 0x000b},
+       {0x00, 0x0001, 0x000c},
+       {0x00, 0x0001, 0x000d},
+       {0x00, 0x0000, 0x000e},
+       {0x00, 0x0002, 0x000f},
+       {0x00, 0x0001, 0x0010},
+       {0x00, 0x0000, 0x0011},
+       {0x00, 0x0000, 0x0012},
+       {0x00, 0x0002, 0x0020},
+       {0x00, 0x0080, 0x0021},
+       {0x00, 0x0001, 0x0022},
+       {0x00, 0x00e0, 0x0023},
+       {0x00, 0x0000, 0x0024},
+       {0x00, 0x00d5, 0x0025},
+       {0x00, 0x0000, 0x0026},
+       {0x00, 0x000b, 0x0027},
+       {0x00, 0x0000, 0x0046},
+       {0x00, 0x0000, 0x0047},
+       {0x00, 0x0000, 0x0048},
+       {0x00, 0x0000, 0x0049},
+       {0x00, 0x0008, 0x004a},
+       {0xff, 0x0000, 0x00d0},
+       {0xff, 0x00d8, 0x00d1},
+       {0xff, 0x0000, 0x00d4},
+       {0xff, 0x0000, 0x00d5},
+       {0x01, 0x00a6, 0x0000},
+       {0x01, 0x0028, 0x0001},
+       {0x01, 0x0000, 0x0002},
+       {0x01, 0x000a, 0x0003},
+       {0x01, 0x0040, 0x0004},
+       {0x01, 0x0066, 0x0007},
+       {0x01, 0x0011, 0x0008},
+       {0x01, 0x0032, 0x0009},
+       {0x01, 0x00fd, 0x000a},
+       {0x01, 0x0038, 0x000b},
+       {0x01, 0x00d1, 0x000c},
+       {0x01, 0x00f7, 0x000d},
+       {0x01, 0x00ed, 0x000e},
+       {0x01, 0x00d8, 0x000f},
+       {0x01, 0x0038, 0x0010},
+       {0x01, 0x00ff, 0x0015},
+       {0x01, 0x0001, 0x0016},
+       {0x01, 0x0032, 0x0017},
+       {0x01, 0x0023, 0x0018},
+       {0x01, 0x00ce, 0x0019},
+       {0x01, 0x0023, 0x001a},
+       {0x01, 0x0032, 0x001b},
+       {0x01, 0x008d, 0x001c},
+       {0x01, 0x00ce, 0x001d},
+       {0x01, 0x008d, 0x001e},
+       {0x01, 0x0000, 0x001f},
+       {0x01, 0x0000, 0x0020},
+       {0x01, 0x00ff, 0x003e},
+       {0x01, 0x0003, 0x003f},
+       {0x01, 0x0000, 0x0040},
+       {0x01, 0x0035, 0x0041},
+       {0x01, 0x0053, 0x0042},
+       {0x01, 0x0069, 0x0043},
+       {0x01, 0x007c, 0x0044},
+       {0x01, 0x008c, 0x0045},
+       {0x01, 0x009a, 0x0046},
+       {0x01, 0x00a8, 0x0047},
+       {0x01, 0x00b4, 0x0048},
+       {0x01, 0x00bf, 0x0049},
+       {0x01, 0x00ca, 0x004a},
+       {0x01, 0x00d4, 0x004b},
+       {0x01, 0x00dd, 0x004c},
+       {0x01, 0x00e7, 0x004d},
+       {0x01, 0x00ef, 0x004e},
+       {0x01, 0x00f8, 0x004f},
+       {0x01, 0x00ff, 0x0050},
+       {0x01, 0x0001, 0x0056},
+       {0x01, 0x0060, 0x0057},
+       {0x01, 0x0040, 0x0058},
+       {0x01, 0x0011, 0x0059},
+       {0x01, 0x0001, 0x005a},
+       {0x02, 0x0007, 0x0005},
+       {0x02, 0xa048, 0x0000},
+       {0x02, 0x0007, 0x0005},
+       {0x02, 0x0015, 0x0006},
+       {0x02, 0x100a, 0x0007},
+       {0x02, 0xa048, 0x0000},
+       {0x02, 0xc002, 0x0001},
+       {0x02, 0x000f, 0x0005},
+       {0x02, 0xa048, 0x0000},
+       {0x05, 0x0022, 0x0004},
+       {0x05, 0x0025, 0x0001},
+       {0x05, 0x0000, 0x0000},
+       {0x05, 0x0026, 0x0001},
+       {0x05, 0x0001, 0x0000},
+       {0x05, 0x0027, 0x0001},
+       {0x05, 0x0000, 0x0000},
+       {0x05, 0x0001, 0x0001},
+       {0x05, 0x0000, 0x0000},
+       {0x05, 0x0021, 0x0001},
+       {0x05, 0x00d2, 0x0000},
+       {0x05, 0x0020, 0x0001},
+       {0x05, 0x0000, 0x0000},
+       {0x00, 0x0090, 0x0005},
+       {0x01, 0x00a6, 0x0000},
+       {0x02, 0x0007, 0x0005},
+       {0x02, 0x2000, 0x0000},
+       {0x05, 0x0022, 0x0004},
+       {0x05, 0x0015, 0x0001},
+       {0x05, 0x00ea, 0x0000},
+       {0x05, 0x0021, 0x0001},
+       {0x05, 0x00d2, 0x0000},
+       {0x05, 0x0023, 0x0001},
+       {0x05, 0x0003, 0x0000},
+       {0x05, 0x0030, 0x0001},
+       {0x05, 0x002b, 0x0000},
+       {0x05, 0x0031, 0x0001},
+       {0x05, 0x0023, 0x0000},
+       {0x05, 0x0032, 0x0001},
+       {0x05, 0x0023, 0x0000},
+       {0x05, 0x0033, 0x0001},
+       {0x05, 0x0023, 0x0000},
+       {0x05, 0x0034, 0x0001},
+       {0x05, 0x0002, 0x0000},
+       {0x05, 0x0050, 0x0001},
+       {0x05, 0x0000, 0x0000},
+       {0x05, 0x0051, 0x0001},
+       {0x05, 0x0000, 0x0000},
+       {0x05, 0x0052, 0x0001},
+       {0x05, 0x0000, 0x0000},
+       {0x05, 0x0054, 0x0001},
+       {0x05, 0x0001, 0x0000},
+       {0x00, 0x0000, 0x0001},
+       {0x00, 0x0000, 0x0002},
+       {0x00, 0x000c, 0x0003},
+       {0x00, 0x0000, 0x0004},
+       {0x00, 0x0090, 0x0005},
+       {0x00, 0x0000, 0x0006},
+       {0x00, 0x0040, 0x0007},
+       {0x00, 0x00c0, 0x0008},
+       {0x00, 0x004a, 0x0009},
+       {0x00, 0x0000, 0x000a},
+       {0x00, 0x0000, 0x000b},
+       {0x00, 0x0001, 0x000c},
+       {0x00, 0x0001, 0x000d},
+       {0x00, 0x0000, 0x000e},
+       {0x00, 0x0002, 0x000f},
+       {0x00, 0x0001, 0x0010},
+       {0x00, 0x0000, 0x0011},
+       {0x00, 0x0000, 0x0012},
+       {0x00, 0x0002, 0x0020},
+       {0x00, 0x0080, 0x0021},
+       {0x00, 0x0001, 0x0022},
+       {0x00, 0x00e0, 0x0023},
+       {0x00, 0x0000, 0x0024},
+       {0x00, 0x00d5, 0x0025},
+       {0x00, 0x0000, 0x0026},
+       {0x00, 0x000b, 0x0027},
+       {0x00, 0x0000, 0x0046},
+       {0x00, 0x0000, 0x0047},
+       {0x00, 0x0000, 0x0048},
+       {0x00, 0x0000, 0x0049},
+       {0x00, 0x0008, 0x004a},
+       {0xff, 0x0000, 0x00d0},
+       {0xff, 0x00d8, 0x00d1},
+       {0xff, 0x0000, 0x00d4},
+       {0xff, 0x0000, 0x00d5},
+       {0x01, 0x00a6, 0x0000},
+       {0x01, 0x0028, 0x0001},
+       {0x01, 0x0000, 0x0002},
+       {0x01, 0x000a, 0x0003},
+       {0x01, 0x0040, 0x0004},
+       {0x01, 0x0066, 0x0007},
+       {0x01, 0x0011, 0x0008},
+       {0x01, 0x0032, 0x0009},
+       {0x01, 0x00fd, 0x000a},
+       {0x01, 0x0038, 0x000b},
+       {0x01, 0x00d1, 0x000c},
+       {0x01, 0x00f7, 0x000d},
+       {0x01, 0x00ed, 0x000e},
+       {0x01, 0x00d8, 0x000f},
+       {0x01, 0x0038, 0x0010},
+       {0x01, 0x00ff, 0x0015},
+       {0x01, 0x0001, 0x0016},
+       {0x01, 0x0032, 0x0017},
+       {0x01, 0x0023, 0x0018},
+       {0x01, 0x00ce, 0x0019},
+       {0x01, 0x0023, 0x001a},
+       {0x01, 0x0032, 0x001b},
+       {0x01, 0x008d, 0x001c},
+       {0x01, 0x00ce, 0x001d},
+       {0x01, 0x008d, 0x001e},
+       {0x01, 0x0000, 0x001f},
+       {0x01, 0x0000, 0x0020},
+       {0x01, 0x00ff, 0x003e},
+       {0x01, 0x0003, 0x003f},
+       {0x01, 0x0000, 0x0040},
+       {0x01, 0x0035, 0x0041},
+       {0x01, 0x0053, 0x0042},
+       {0x01, 0x0069, 0x0043},
+       {0x01, 0x007c, 0x0044},
+       {0x01, 0x008c, 0x0045},
+       {0x01, 0x009a, 0x0046},
+       {0x01, 0x00a8, 0x0047},
+       {0x01, 0x00b4, 0x0048},
+       {0x01, 0x00bf, 0x0049},
+       {0x01, 0x00ca, 0x004a},
+       {0x01, 0x00d4, 0x004b},
+       {0x01, 0x00dd, 0x004c},
+       {0x01, 0x00e7, 0x004d},
+       {0x01, 0x00ef, 0x004e},
+       {0x01, 0x00f8, 0x004f},
+       {0x01, 0x00ff, 0x0050},
+       {0x01, 0x0001, 0x0056},
+       {0x01, 0x0060, 0x0057},
+       {0x01, 0x0040, 0x0058},
+       {0x01, 0x0011, 0x0059},
+       {0x01, 0x0001, 0x005a},
+       {0x02, 0x0007, 0x0005},
+       {0x02, 0xa048, 0x0000},
+       {0x02, 0x0007, 0x0005},
+       {0x02, 0x0015, 0x0006},
+       {0x02, 0x100a, 0x0007},
+       {0x02, 0xa048, 0x0000},
+       {0x02, 0xc002, 0x0001},
+       {0x02, 0x000f, 0x0005},
+       {0x02, 0xa048, 0x0000},
+       {0x05, 0x0022, 0x0004},
+       {0x05, 0x0025, 0x0001},
+       {0x05, 0x0000, 0x0000},
+       {0x05, 0x0026, 0x0001},
+       {0x05, 0x0001, 0x0000},
+       {0x05, 0x0027, 0x0001},
+       {0x05, 0x0000, 0x0000},
+       {0x05, 0x0001, 0x0001},
+       {0x05, 0x0000, 0x0000},
+       {0x05, 0x0021, 0x0001},
+       {0x05, 0x00d2, 0x0000},
+       {0x05, 0x0020, 0x0001},
+       {0x05, 0x0000, 0x0000},
+       {0x00, 0x0090, 0x0005},
+       {0x01, 0x00a6, 0x0000},
+       {0x05, 0x0026, 0x0001},
+       {0x05, 0x0001, 0x0000},
+       {0x05, 0x0027, 0x0001},
+       {0x05, 0x001e, 0x0000},
+       {0x01, 0x0003, 0x003f},
+       {0x01, 0x0001, 0x0056},
+       {0x01, 0x0011, 0x0008},
+       {0x01, 0x0032, 0x0009},
+       {0x01, 0xfffd, 0x000a},
+       {0x01, 0x0023, 0x000b},
+       {0x01, 0xffea, 0x000c},
+       {0x01, 0xfff4, 0x000d},
+       {0x01, 0xfffc, 0x000e},
+       {0x01, 0xffe3, 0x000f},
+       {0x01, 0x001f, 0x0010},
+       {0x01, 0x00a8, 0x0001},
+       {0x01, 0x0067, 0x0007},
+       {0x01, 0x0042, 0x0051},
+       {0x01, 0x0051, 0x0053},
+       {0x01, 0x000a, 0x0003},
+       {0x02, 0xc002, 0x0001},
+       {0x02, 0x0007, 0x0005},
+       {0x01, 0x0042, 0x0051},
+       {0x01, 0x0051, 0x0053},
+       {0x05, 0x0026, 0x0001},
+       {0x05, 0x0001, 0x0000},
+       {0x05, 0x0027, 0x0001},
+       {0x05, 0x002d, 0x0000},
+       {0x01, 0x0003, 0x003f},
+       {0x01, 0x0001, 0x0056},
+       {0x02, 0xc000, 0x0001},
+       {0x02, 0x0000, 0x0005},
+       {}
+};
+
+/* Unknow camera from Ori Usbid 0x0000:0x0000 */
+/* Based on snoops from Ori Cohen */
+static const __u16 spca501c_mysterious_open_data[][3] = {
+       {0x02, 0x000f, 0x0005},
+       {0x02, 0xa048, 0x0000},
+       {0x05, 0x0022, 0x0004},
+/* DSP Registers */
+       {0x01, 0x0016, 0x0011}, /* RGB offset */
+       {0x01, 0x0000, 0x0012},
+       {0x01, 0x0006, 0x0013},
+       {0x01, 0x0078, 0x0051},
+       {0x01, 0x0040, 0x0052},
+       {0x01, 0x0046, 0x0053},
+       {0x01, 0x0040, 0x0054},
+       {0x00, 0x0025, 0x0000},
+/*     {0x00, 0x0000, 0x0000 }, */
+/* Part 2 */
+/* TG Registers */
+       {0x00, 0x0026, 0x0000},
+       {0x00, 0x0001, 0x0000},
+       {0x00, 0x0027, 0x0000},
+       {0x00, 0x008a, 0x0000},
+       {0x02, 0x0007, 0x0005},
+       {0x02, 0x2000, 0x0000},
+       {0x05, 0x0022, 0x0004},
+       {0x05, 0x0015, 0x0001},
+       {0x05, 0x00ea, 0x0000},
+       {0x05, 0x0021, 0x0001},
+       {0x05, 0x00d2, 0x0000},
+       {0x05, 0x0023, 0x0001},
+       {0x05, 0x0003, 0x0000},
+       {0x05, 0x0030, 0x0001},
+       {0x05, 0x002b, 0x0000},
+       {0x05, 0x0031, 0x0001},
+       {0x05, 0x0023, 0x0000},
+       {0x05, 0x0032, 0x0001},
+       {0x05, 0x0023, 0x0000},
+       {0x05, 0x0033, 0x0001},
+       {0x05, 0x0023, 0x0000},
+       {0x05, 0x0034, 0x0001},
+       {0x05, 0x0002, 0x0000},
+       {0x05, 0x0050, 0x0001},
+       {0x05, 0x0000, 0x0000},
+       {0x05, 0x0051, 0x0001},
+       {0x05, 0x0000, 0x0000},
+       {0x05, 0x0052, 0x0001},
+       {0x05, 0x0000, 0x0000},
+       {0x05, 0x0054, 0x0001},
+       {0x05, 0x0001, 0x0000},
+       {}
+};
+
+/* Based on snoops from Ori Cohen */
+static const __u16 spca501c_mysterious_init_data[][3] = {
+/* Part 3 */
+/* TG registers */
+/*     {0x00, 0x0000, 0x0000}, */
+       {0x00, 0x0000, 0x0001},
+       {0x00, 0x0000, 0x0002},
+       {0x00, 0x0006, 0x0003},
+       {0x00, 0x0000, 0x0004},
+       {0x00, 0x0090, 0x0005},
+       {0x00, 0x0000, 0x0006},
+       {0x00, 0x0040, 0x0007},
+       {0x00, 0x00c0, 0x0008},
+       {0x00, 0x004a, 0x0009},
+       {0x00, 0x0000, 0x000a},
+       {0x00, 0x0000, 0x000b},
+       {0x00, 0x0001, 0x000c},
+       {0x00, 0x0001, 0x000d},
+       {0x00, 0x0000, 0x000e},
+       {0x00, 0x0002, 0x000f},
+       {0x00, 0x0001, 0x0010},
+       {0x00, 0x0000, 0x0011},
+       {0x00, 0x0001, 0x0012},
+       {0x00, 0x0002, 0x0020},
+       {0x00, 0x0080, 0x0021}, /* 640 */
+       {0x00, 0x0001, 0x0022},
+       {0x00, 0x00e0, 0x0023}, /* 480 */
+       {0x00, 0x0000, 0x0024}, /* Offset H hight */
+       {0x00, 0x00d3, 0x0025}, /* low */
+       {0x00, 0x0000, 0x0026}, /* Offset V */
+       {0x00, 0x000d, 0x0027}, /* low */
+       {0x00, 0x0000, 0x0046},
+       {0x00, 0x0000, 0x0047},
+       {0x00, 0x0000, 0x0048},
+       {0x00, 0x0000, 0x0049},
+       {0x00, 0x0008, 0x004a},
+/* DSP Registers        */
+       {0x01, 0x00a6, 0x0000},
+       {0x01, 0x0028, 0x0001},
+       {0x01, 0x0000, 0x0002},
+       {0x01, 0x000a, 0x0003}, /* Level Calc bit7 ->1 Auto */
+       {0x01, 0x0040, 0x0004},
+       {0x01, 0x0066, 0x0007},
+       {0x01, 0x000f, 0x0008}, /* A11 Color correction coeff */
+       {0x01, 0x002d, 0x0009}, /* A12 */
+       {0x01, 0x0005, 0x000a}, /* A13 */
+       {0x01, 0x0023, 0x000b}, /* A21 */
+       {0x01, 0x00e0, 0x000c}, /* A22 */
+       {0x01, 0x00fd, 0x000d}, /* A23 */
+       {0x01, 0x00f4, 0x000e}, /* A31 */
+       {0x01, 0x00e4, 0x000f}, /* A32 */
+       {0x01, 0x0028, 0x0010}, /* A33 */
+       {0x01, 0x00ff, 0x0015}, /* Reserved */
+       {0x01, 0x0001, 0x0016}, /* Reserved */
+       {0x01, 0x0032, 0x0017}, /* Win1 Start begin */
+       {0x01, 0x0023, 0x0018},
+       {0x01, 0x00ce, 0x0019},
+       {0x01, 0x0023, 0x001a},
+       {0x01, 0x0032, 0x001b},
+       {0x01, 0x008d, 0x001c},
+       {0x01, 0x00ce, 0x001d},
+       {0x01, 0x008d, 0x001e},
+       {0x01, 0x0000, 0x001f},
+       {0x01, 0x0000, 0x0020}, /* Win1 Start end */
+       {0x01, 0x00ff, 0x003e}, /* Reserved begin */
+       {0x01, 0x0002, 0x003f},
+       {0x01, 0x0000, 0x0040},
+       {0x01, 0x0035, 0x0041},
+       {0x01, 0x0053, 0x0042},
+       {0x01, 0x0069, 0x0043},
+       {0x01, 0x007c, 0x0044},
+       {0x01, 0x008c, 0x0045},
+       {0x01, 0x009a, 0x0046},
+       {0x01, 0x00a8, 0x0047},
+       {0x01, 0x00b4, 0x0048},
+       {0x01, 0x00bf, 0x0049},
+       {0x01, 0x00ca, 0x004a},
+       {0x01, 0x00d4, 0x004b},
+       {0x01, 0x00dd, 0x004c},
+       {0x01, 0x00e7, 0x004d},
+       {0x01, 0x00ef, 0x004e},
+       {0x01, 0x00f8, 0x004f},
+       {0x01, 0x00ff, 0x0050},
+       {0x01, 0x0003, 0x0056}, /* Reserved end */
+       {0x01, 0x0060, 0x0057}, /* Edge Gain */
+       {0x01, 0x0040, 0x0058},
+       {0x01, 0x0011, 0x0059}, /* Edge Bandwidth */
+       {0x01, 0x0001, 0x005a},
+       {0x02, 0x0007, 0x0005},
+       {0x02, 0xa048, 0x0000},
+       {0x02, 0x0007, 0x0005},
+       {0x02, 0x0015, 0x0006},
+       {0x02, 0x200a, 0x0007},
+       {0x02, 0xa048, 0x0000},
+       {0x02, 0xc000, 0x0001},
+       {0x02, 0x000f, 0x0005},
+       {0x02, 0xa048, 0x0000},
+       {0x05, 0x0022, 0x0004},
+       {0x05, 0x0025, 0x0001},
+       {0x05, 0x0000, 0x0000},
+/* Part 4             */
+       {0x05, 0x0026, 0x0001},
+       {0x05, 0x0001, 0x0000},
+       {0x05, 0x0027, 0x0001},
+       {0x05, 0x0000, 0x0000},
+       {0x05, 0x0001, 0x0001},
+       {0x05, 0x0000, 0x0000},
+       {0x05, 0x0021, 0x0001},
+       {0x05, 0x00d2, 0x0000},
+       {0x05, 0x0020, 0x0001},
+       {0x05, 0x0000, 0x0000},
+       {0x00, 0x0090, 0x0005},
+       {0x01, 0x00a6, 0x0000},
+       {0x02, 0x0000, 0x0005},
+       {0x05, 0x0026, 0x0001},
+       {0x05, 0x0001, 0x0000},
+       {0x05, 0x0027, 0x0001},
+       {0x05, 0x004e, 0x0000},
+/* Part 5               */
+       {0x01, 0x0003, 0x003f},
+       {0x01, 0x0001, 0x0056},
+       {0x01, 0x000f, 0x0008},
+       {0x01, 0x002d, 0x0009},
+       {0x01, 0x0005, 0x000a},
+       {0x01, 0x0023, 0x000b},
+       {0x01, 0xffe0, 0x000c},
+       {0x01, 0xfffd, 0x000d},
+       {0x01, 0xfff4, 0x000e},
+       {0x01, 0xffe4, 0x000f},
+       {0x01, 0x0028, 0x0010},
+       {0x01, 0x00a8, 0x0001},
+       {0x01, 0x0066, 0x0007},
+       {0x01, 0x0032, 0x0017},
+       {0x01, 0x0023, 0x0018},
+       {0x01, 0x00ce, 0x0019},
+       {0x01, 0x0023, 0x001a},
+       {0x01, 0x0032, 0x001b},
+       {0x01, 0x008d, 0x001c},
+       {0x01, 0x00ce, 0x001d},
+       {0x01, 0x008d, 0x001e},
+       {0x01, 0x00c8, 0x0015}, /* c8 Poids fort Luma */
+       {0x01, 0x0032, 0x0016}, /* 32 */
+       {0x01, 0x0016, 0x0011}, /* R 00 */
+       {0x01, 0x0016, 0x0012}, /* G 00 */
+       {0x01, 0x0016, 0x0013}, /* B 00 */
+       {0x01, 0x000a, 0x0003},
+       {0x02, 0xc002, 0x0001},
+       {0x02, 0x0007, 0x0005},
+       {}
+};
+
+static int reg_write(struct usb_device *dev,
+                    __u16 req, __u16 index, __u16 value)
+{
+       int ret;
+
+       ret = usb_control_msg(dev,
+                       usb_sndctrlpipe(dev, 0),
+                       req,
+                       USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                       value, index, NULL, 0, 500);
+       PDEBUG(D_USBO, "reg write: 0x%02x 0x%02x 0x%02x",
+               req, index, value);
+       if (ret < 0)
+               PDEBUG(D_ERR, "reg write: error %d", ret);
+       return ret;
+}
+
+/* returns: negative is error, pos or zero is data */
+static int reg_read(struct gspca_dev *gspca_dev,
+                       __u16 req,      /* bRequest */
+                       __u16 index,    /* wIndex */
+                       __u16 length)   /* wLength (1 or 2 only) */
+{
+       int ret;
+
+       gspca_dev->usb_buf[1] = 0;
+       ret = usb_control_msg(gspca_dev->dev,
+                       usb_rcvctrlpipe(gspca_dev->dev, 0),
+                       req,
+                       USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                       0,              /* value */
+                       index,
+                       gspca_dev->usb_buf, length,
+                       500);                   /* timeout */
+       if (ret < 0) {
+               PDEBUG(D_ERR, "reg_read err %d", ret);
+               return -1;
+       }
+       return (gspca_dev->usb_buf[1] << 8) + gspca_dev->usb_buf[0];
+}
+
+static int write_vector(struct gspca_dev *gspca_dev,
+                       const __u16 data[][3])
+{
+       struct usb_device *dev = gspca_dev->dev;
+       int ret, i = 0;
+
+       while (data[i][0] != 0 || data[i][1] != 0 || data[i][2] != 0) {
+               ret = reg_write(dev, data[i][0], data[i][2], data[i][1]);
+               if (ret < 0) {
+                       PDEBUG(D_ERR,
+                               "Reg write failed for 0x%02x,0x%02x,0x%02x",
+                               data[i][0], data[i][1], data[i][2]);
+                       return ret;
+               }
+               i++;
+       }
+       return 0;
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x11, sd->brightness);
+       reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x12, sd->brightness);
+       reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x13, sd->brightness);
+}
+
+static void getbrightness(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       __u16 brightness;
+
+       brightness = reg_read(gspca_dev, SPCA501_REG_CCDSP, 0x11, 2);
+       sd->brightness = brightness << 1;
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       reg_write(gspca_dev->dev, 0x00, 0x00,
+                                 (sd->contrast >> 8) & 0xff);
+       reg_write(gspca_dev->dev, 0x00, 0x01,
+                                 sd->contrast & 0xff);
+}
+
+static void getcontrast(struct gspca_dev *gspca_dev)
+{
+/*     spca50x->contrast = 0xaa01; */
+}
+
+static void setcolors(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x0c, sd->colors);
+}
+
+static void getcolors(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->colors = reg_read(gspca_dev, SPCA501_REG_CCDSP, 0x0c, 2);
+/*     sd->hue = (reg_read(gspca_dev, SPCA501_REG_CCDSP, 0x13, */
+/*                     2) & 0xFF) << 8; */
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+                       const struct usb_device_id *id)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       struct cam *cam;
+       __u16 vendor;
+       __u16 product;
+
+       vendor = id->idVendor;
+       product = id->idProduct;
+       switch (vendor) {
+       case 0x0000:            /* Unknow Camera */
+/*             switch (product) { */
+/*             case 0x0000: */
+                       sd->subtype = MystFromOriUnknownCamera;
+/*                     break; */
+/*             } */
+               break;
+       case 0x040a:            /* Kodak cameras */
+/*             switch (product) { */
+/*             case 0x0002: */
+                       sd->subtype = KodakDVC325;
+/*                     break; */
+/*             } */
+               break;
+       case 0x0497:            /* Smile International */
+/*             switch (product) { */
+/*             case 0xc001: */
+                       sd->subtype = SmileIntlCamera;
+/*                     break; */
+/*             } */
+               break;
+       case 0x0506:            /* 3COM cameras */
+/*             switch (product) { */
+/*             case 0x00df: */
+                       sd->subtype = ThreeComHomeConnectLite;
+/*                     break; */
+/*             } */
+               break;
+       case 0x0733:    /* Rebadged ViewQuest (Intel) and ViewQuest cameras */
+               switch (product) {
+               case 0x0401:
+                       sd->subtype = IntelCreateAndShare;
+                       break;
+               case 0x0402:
+                       sd->subtype = ViewQuestM318B;
+                       break;
+               }
+               break;
+       case 0x1776:            /* Arowana */
+/*             switch (product) { */
+/*             case 0x501c: */
+                       sd->subtype = Arowana300KCMOSCamera;
+/*                     break; */
+/*             } */
+               break;
+       }
+       cam = &gspca_dev->cam;
+       cam->dev_name = (char *) id->driver_info;
+       cam->epaddr = 0x01;
+       cam->cam_mode = vga_mode;
+       cam->nmodes = sizeof vga_mode / sizeof vga_mode[0];
+       sd->brightness = sd_ctrls[MY_BRIGHTNESS].qctrl.default_value;
+       sd->contrast = sd_ctrls[MY_CONTRAST].qctrl.default_value;
+       sd->colors = sd_ctrls[MY_COLOR].qctrl.default_value;
+
+       switch (sd->subtype) {
+       case Arowana300KCMOSCamera:
+       case SmileIntlCamera:
+               /* Arowana 300k CMOS Camera data */
+               if (write_vector(gspca_dev, spca501c_arowana_init_data))
+                       goto error;
+               break;
+       case MystFromOriUnknownCamera:
+               /* UnKnow Ori CMOS Camera data */
+               if (write_vector(gspca_dev, spca501c_mysterious_open_data))
+                       goto error;
+               break;
+       default:
+               /* generic spca501 init data */
+               if (write_vector(gspca_dev, spca501_init_data))
+                       goto error;
+               break;
+       }
+       return 0;
+error:
+       return -EINVAL;
+}
+
+/* this function is called at open time */
+static int sd_open(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       switch (sd->subtype) {
+       case ThreeComHomeConnectLite:
+               /* Special handling for 3com data */
+               write_vector(gspca_dev, spca501_3com_open_data);
+               break;
+       case Arowana300KCMOSCamera:
+       case SmileIntlCamera:
+               /* Arowana 300k CMOS Camera data */
+               write_vector(gspca_dev, spca501c_arowana_open_data);
+               break;
+       case MystFromOriUnknownCamera:
+               /* UnKnow  CMOS Camera data */
+               write_vector(gspca_dev, spca501c_mysterious_init_data);
+               break;
+       default:
+               /* Generic 501 open data */
+               write_vector(gspca_dev, spca501_open_data);
+       }
+       PDEBUG(D_STREAM, "Initializing SPCA501 finished");
+       return 0;
+}
+
+static void sd_start(struct gspca_dev *gspca_dev)
+{
+       struct usb_device *dev = gspca_dev->dev;
+       int mode;
+
+       /* memorize the wanted pixel format */
+       mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;
+
+       /* Enable ISO packet machine CTRL reg=2,
+        * index=1 bitmask=0x2 (bit ordinal 1) */
+       reg_write(dev, SPCA50X_REG_USB, 0x6, 0x94);
+       switch (mode) {
+       case 0: /* 640x480 */
+               reg_write(dev, SPCA50X_REG_USB, 0x07, 0x004a);
+               break;
+       case 1: /* 320x240 */
+               reg_write(dev, SPCA50X_REG_USB, 0x07, 0x104a);
+               break;
+       default:
+/*     case 2:  * 160x120 */
+               reg_write(dev, SPCA50X_REG_USB, 0x07, 0x204a);
+               break;
+       }
+       reg_write(dev, SPCA501_REG_CTLRL, 0x01, 0x02);
+
+       /* HDG atleast the Intel CreateAndShare needs to have one of its
+        * brightness / contrast / color set otherwise it assumes what seems
+        * max contrast. Note that strange enough setting any of these is
+        * enough to fix the max contrast problem, to be sure we set all 3 */
+       setbrightness(gspca_dev);
+       setcontrast(gspca_dev);
+       setcolors(gspca_dev);
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+       /* Disable ISO packet
+        * machine CTRL reg=2, index=1 bitmask=0x0 (bit ordinal 1) */
+       reg_write(gspca_dev->dev, SPCA501_REG_CTLRL, 0x01, 0x00);
+}
+
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+}
+
+/* this function is called at close time */
+static void sd_close(struct gspca_dev *gspca_dev)
+{
+       reg_write(gspca_dev->dev, SPCA501_REG_CTLRL, 0x05, 0x00);
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+                       struct gspca_frame *frame,      /* target */
+                       __u8 *data,                     /* isoc packet */
+                       int len)                        /* iso packet length */
+{
+       switch (data[0]) {
+       case 0:                         /* start of frame */
+               frame = gspca_frame_add(gspca_dev,
+                                       LAST_PACKET,
+                                       frame,
+                                       data, 0);
+               data += SPCA501_OFFSET_DATA;
+               len -= SPCA501_OFFSET_DATA;
+               gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
+                               data, len);
+               return;
+       case 0xff:                      /* drop */
+/*             gspca_dev->last_packet_type = DISCARD_PACKET; */
+               return;
+       }
+       data++;
+       len--;
+       gspca_frame_add(gspca_dev, INTER_PACKET, frame,
+                       data, len);
+}
+
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->brightness = val;
+       if (gspca_dev->streaming)
+               setbrightness(gspca_dev);
+       return 0;
+}
+
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       getbrightness(gspca_dev);
+       *val = sd->brightness;
+       return 0;
+}
+
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->contrast = val;
+       if (gspca_dev->streaming)
+               setcontrast(gspca_dev);
+       return 0;
+}
+
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       getcontrast(gspca_dev);
+       *val = sd->contrast;
+       return 0;
+}
+
+static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->colors = val;
+       if (gspca_dev->streaming)
+               setcolors(gspca_dev);
+       return 0;
+}
+
+static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       getcolors(gspca_dev);
+       *val = sd->colors;
+       return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+       .name = MODULE_NAME,
+       .ctrls = sd_ctrls,
+       .nctrls = ARRAY_SIZE(sd_ctrls),
+       .config = sd_config,
+       .open = sd_open,
+       .start = sd_start,
+       .stopN = sd_stopN,
+       .stop0 = sd_stop0,
+       .close = sd_close,
+       .pkt_scan = sd_pkt_scan,
+};
+
+/* -- module initialisation -- */
+#define DVNM(name) .driver_info = (kernel_ulong_t) name
+static const __devinitdata struct usb_device_id device_table[] = {
+       {USB_DEVICE(0x040a, 0x0002), DVNM("Kodak DVC-325")},
+       {USB_DEVICE(0x0497, 0xc001), DVNM("Smile International")},
+       {USB_DEVICE(0x0506, 0x00df), DVNM("3Com HomeConnect Lite")},
+       {USB_DEVICE(0x0733, 0x0401), DVNM("Intel Create and Share")},
+       {USB_DEVICE(0x0733, 0x0402), DVNM("ViewQuest M318B")},
+       {USB_DEVICE(0x1776, 0x501c), DVNM("Arowana 300K CMOS Camera")},
+       {USB_DEVICE(0x0000, 0x0000), DVNM("MystFromOri Unknow Camera")},
+       {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+                       const struct usb_device_id *id)
+{
+       return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+                               THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+       .name = MODULE_NAME,
+       .id_table = device_table,
+       .probe = sd_probe,
+       .disconnect = gspca_disconnect,
+};
+
+/* -- module insert / remove -- */
+static int __init sd_mod_init(void)
+{
+       if (usb_register(&sd_driver) < 0)
+               return -1;
+       PDEBUG(D_PROBE, "v%s registered", version);
+       return 0;
+}
+static void __exit sd_mod_exit(void)
+{
+       usb_deregister(&sd_driver);
+       PDEBUG(D_PROBE, "deregistered");
+}
+
+module_init(sd_mod_init);
+module_exit(sd_mod_exit);
diff --git a/drivers/media/video/gspca/spca505.c b/drivers/media/video/gspca/spca505.c
new file mode 100644 (file)
index 0000000..ddea6e1
--- /dev/null
@@ -0,0 +1,951 @@
+/*
+ * SPCA505 chip based cameras initialization data
+ *
+ * V4L2 by Jean-Francis Moine <http://moinejf.free.fr>
+ *
+ * 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
+ * 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
+ *
+ */
+
+#define MODULE_NAME "spca505"
+
+#include "gspca.h"
+
+#define DRIVER_VERSION_NUMBER  KERNEL_VERSION(2, 1, 7)
+static const char version[] = "2.1.7";
+
+MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
+MODULE_DESCRIPTION("GSPCA/SPCA505 USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* specific webcam descriptor */
+struct sd {
+       struct gspca_dev gspca_dev;             /* !! must be the first item */
+
+       int buflen;
+       unsigned char tmpbuf[640 * 480 * 3 / 2]; /* YYUV per line */
+       unsigned char tmpbuf2[640 * 480 * 2];   /* YUYV */
+
+       unsigned char brightness;
+
+       char subtype;
+#define IntelPCCameraPro 0
+#define Nxultra 1
+};
+
+/* V4L2 controls supported by the driver */
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
+
+static struct ctrl sd_ctrls[] = {
+#define SD_BRIGHTNESS 0
+       {
+           {
+               .id      = V4L2_CID_BRIGHTNESS,
+               .type    = V4L2_CTRL_TYPE_INTEGER,
+               .name    = "Brightness",
+               .minimum = 0,
+               .maximum = 255,
+               .step    = 1,
+               .default_value = 127,
+           },
+           .set = sd_setbrightness,
+           .get = sd_getbrightness,
+       },
+};
+
+static struct v4l2_pix_format vga_mode[] = {
+       {160, 120, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE,
+               .bytesperline = 160 * 2,
+               .sizeimage = 160 * 120 * 2,
+               .colorspace = V4L2_COLORSPACE_SRGB,
+               .priv = 5},
+       {176, 144, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE,
+               .bytesperline = 176 * 2,
+               .sizeimage = 176 * 144 * 2,
+               .colorspace = V4L2_COLORSPACE_SRGB,
+               .priv = 4},
+       {320, 240, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE,
+               .bytesperline = 320 * 2,
+               .sizeimage = 320 * 240 * 2,
+               .colorspace = V4L2_COLORSPACE_SRGB,
+               .priv = 2},
+       {352, 288, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE,
+               .bytesperline = 352 * 2,
+               .sizeimage = 352 * 288 * 2,
+               .colorspace = V4L2_COLORSPACE_SRGB,
+               .priv = 1},
+       {640, 480, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE,
+               .bytesperline = 640 * 2,
+               .sizeimage = 640 * 480 * 2,
+               .colorspace = V4L2_COLORSPACE_SRGB,
+               .priv = 0},
+};
+
+#define SPCA50X_OFFSET_DATA 10
+
+#define SPCA50X_REG_USB 0x02   /* spca505 501 */
+
+#define SPCA50X_USB_CTRL 0x00  /* spca505 */
+#define SPCA50X_CUSB_ENABLE 0x01 /* spca505 */
+#define SPCA50X_REG_GLOBAL 0x03        /* spca505 */
+#define SPCA50X_GMISC0_IDSEL 0x01 /* Global control device ID select spca505 */
+#define SPCA50X_GLOBAL_MISC0 0x00 /* Global control miscellaneous 0 spca505 */
+
+#define SPCA50X_GLOBAL_MISC1 0x01 /* 505 */
+#define SPCA50X_GLOBAL_MISC3 0x03 /* 505 */
+#define SPCA50X_GMISC3_SAA7113RST 0x20 /* Not sure about this one spca505 */
+
+/*
+ * Data to initialize a SPCA505. Common to the CCD and external modes
+ */
+static const __u16 spca505_init_data[][3] = {
+       /* line    bmRequest,value,index */
+       /* 1819 */
+       {SPCA50X_REG_GLOBAL, SPCA50X_GMISC3_SAA7113RST, SPCA50X_GLOBAL_MISC3},
+       /* Sensor reset */
+       /* 1822 */ {SPCA50X_REG_GLOBAL, 0x00, SPCA50X_GLOBAL_MISC3},
+       /* 1825 */ {SPCA50X_REG_GLOBAL, 0x00, SPCA50X_GLOBAL_MISC1},
+       /* Block USB reset */
+       /* 1828 */ {SPCA50X_REG_GLOBAL, SPCA50X_GMISC0_IDSEL,
+               SPCA50X_GLOBAL_MISC0},
+
+       /* 1831 */ {0x5, 0x01, 0x10},
+                                       /* Maybe power down some stuff */
+       /* 1834 */ {0x5, 0x0f, 0x11},
+
+       /* Setup internal CCD  ? */
+       /* 1837 */ {0x6, 0x10, 0x08},
+       /* 1840 */ {0x6, 0x00, 0x09},
+       /* 1843 */ {0x6, 0x00, 0x0a},
+       /* 1846 */ {0x6, 0x00, 0x0b},
+       /* 1849 */ {0x6, 0x10, 0x0c},
+       /* 1852 */ {0x6, 0x00, 0x0d},
+       /* 1855 */ {0x6, 0x00, 0x0e},
+       /* 1858 */ {0x6, 0x00, 0x0f},
+       /* 1861 */ {0x6, 0x10, 0x10},
+       /* 1864 */ {0x6, 0x02, 0x11},
+       /* 1867 */ {0x6, 0x00, 0x12},
+       /* 1870 */ {0x6, 0x04, 0x13},
+       /* 1873 */ {0x6, 0x02, 0x14},
+       /* 1876 */ {0x6, 0x8a, 0x51},
+       /* 1879 */ {0x6, 0x40, 0x52},
+       /* 1882 */ {0x6, 0xb6, 0x53},
+       /* 1885 */ {0x6, 0x3d, 0x54},
+       {}
+};
+
+/*
+ * Data to initialize the camera using the internal CCD
+ */
+static const __u16 spca505_open_data_ccd[][3] = {
+       /* line    bmRequest,value,index */
+       /* Internal CCD data set */
+       /* 1891 */ {0x3, 0x04, 0x01},
+       /* This could be a reset */
+       /* 1894 */ {0x3, 0x00, 0x01},
+
+       /* Setup compression and image registers. 0x6 and 0x7 seem to be
+          related to H&V hold, and are resolution mode specific */
+               /* 1897 */ {0x4, 0x10, 0x01},
+               /* DIFF(0x50), was (0x10) */
+       /* 1900 */ {0x4, 0x00, 0x04},
+       /* 1903 */ {0x4, 0x00, 0x05},
+       /* 1906 */ {0x4, 0x20, 0x06},
+       /* 1909 */ {0x4, 0x20, 0x07},
+
+       /* 1912 */ {0x8, 0x0a, 0x00},
+       /* DIFF (0x4a), was (0xa) */
+
+       /* 1915 */ {0x5, 0x00, 0x10},
+       /* 1918 */ {0x5, 0x00, 0x11},
+       /* 1921 */ {0x5, 0x00, 0x00},
+       /* DIFF not written */
+       /* 1924 */ {0x5, 0x00, 0x01},
+       /* DIFF not written */
+       /* 1927 */ {0x5, 0x00, 0x02},
+       /* DIFF not written */
+       /* 1930 */ {0x5, 0x00, 0x03},
+       /* DIFF not written */
+       /* 1933 */ {0x5, 0x00, 0x04},
+       /* DIFF not written */
+               /* 1936 */ {0x5, 0x80, 0x05},
+               /* DIFF not written */
+               /* 1939 */ {0x5, 0xe0, 0x06},
+               /* DIFF not written */
+               /* 1942 */ {0x5, 0x20, 0x07},
+               /* DIFF not written */
+               /* 1945 */ {0x5, 0xa0, 0x08},
+               /* DIFF not written */
+               /* 1948 */ {0x5, 0x0, 0x12},
+               /* DIFF not written */
+       /* 1951 */ {0x5, 0x02, 0x0f},
+       /* DIFF not written */
+               /* 1954 */ {0x5, 0x10, 0x46},
+               /* DIFF not written */
+               /* 1957 */ {0x5, 0x8, 0x4a},
+               /* DIFF not written */
+
+       /* 1960 */ {0x3, 0x08, 0x03},
+       /* DIFF (0x3,0x28,0x3) */
+       /* 1963 */ {0x3, 0x08, 0x01},
+       /* 1966 */ {0x3, 0x0c, 0x03},
+       /* DIFF not written */
+               /* 1969 */ {0x3, 0x21, 0x00},
+               /* DIFF (0x39) */
+
+/* Extra block copied from init to hopefully ensure CCD is in a sane state */
+       /* 1837 */ {0x6, 0x10, 0x08},
+       /* 1840 */ {0x6, 0x00, 0x09},
+       /* 1843 */ {0x6, 0x00, 0x0a},
+       /* 1846 */ {0x6, 0x00, 0x0b},
+       /* 1849 */ {0x6, 0x10, 0x0c},
+       /* 1852 */ {0x6, 0x00, 0x0d},
+       /* 1855 */ {0x6, 0x00, 0x0e},
+       /* 1858 */ {0x6, 0x00, 0x0f},
+       /* 1861 */ {0x6, 0x10, 0x10},
+       /* 1864 */ {0x6, 0x02, 0x11},
+       /* 1867 */ {0x6, 0x00, 0x12},
+       /* 1870 */ {0x6, 0x04, 0x13},
+       /* 1873 */ {0x6, 0x02, 0x14},
+       /* 1876 */ {0x6, 0x8a, 0x51},
+       /* 1879 */ {0x6, 0x40, 0x52},
+       /* 1882 */ {0x6, 0xb6, 0x53},
+       /* 1885 */ {0x6, 0x3d, 0x54},
+       /* End of extra block */
+
+               /* 1972 */ {0x6, 0x3f, 0x1},
+               /* Block skipped */
+       /* 1975 */ {0x6, 0x10, 0x02},
+       /* 1978 */ {0x6, 0x64, 0x07},
+       /* 1981 */ {0x6, 0x10, 0x08},
+       /* 1984 */ {0x6, 0x00, 0x09},
+       /* 1987 */ {0x6, 0x00, 0x0a},
+       /* 1990 */ {0x6, 0x00, 0x0b},
+       /* 1993 */ {0x6, 0x10, 0x0c},
+       /* 1996 */ {0x6, 0x00, 0x0d},
+       /* 1999 */ {0x6, 0x00, 0x0e},
+       /* 2002 */ {0x6, 0x00, 0x0f},
+       /* 2005 */ {0x6, 0x10, 0x10},
+       /* 2008 */ {0x6, 0x02, 0x11},
+       /* 2011 */ {0x6, 0x00, 0x12},
+       /* 2014 */ {0x6, 0x04, 0x13},
+       /* 2017 */ {0x6, 0x02, 0x14},
+       /* 2020 */ {0x6, 0x8a, 0x51},
+       /* 2023 */ {0x6, 0x40, 0x52},
+       /* 2026 */ {0x6, 0xb6, 0x53},
+       /* 2029 */ {0x6, 0x3d, 0x54},
+       /* 2032 */ {0x6, 0x60, 0x57},
+       /* 2035 */ {0x6, 0x20, 0x58},
+       /* 2038 */ {0x6, 0x15, 0x59},
+       /* 2041 */ {0x6, 0x05, 0x5a},
+
+       /* 2044 */ {0x5, 0x01, 0xc0},
+       /* 2047 */ {0x5, 0x10, 0xcb},
+               /* 2050 */ {0x5, 0x80, 0xc1},
+               /* */
+               /* 2053 */ {0x5, 0x0, 0xc2},
+               /* 4 was 0 */
+       /* 2056 */ {0x5, 0x00, 0xca},
+               /* 2059 */ {0x5, 0x80, 0xc1},
+               /*  */
+       /* 2062 */ {0x5, 0x04, 0xc2},
+       /* 2065 */ {0x5, 0x00, 0xca},
+               /* 2068 */ {0x5, 0x0, 0xc1},
+               /*  */
+       /* 2071 */ {0x5, 0x00, 0xc2},
+       /* 2074 */ {0x5, 0x00, 0xca},
+               /* 2077 */ {0x5, 0x40, 0xc1},
+               /* */
+       /* 2080 */ {0x5, 0x17, 0xc2},
+       /* 2083 */ {0x5, 0x00, 0xca},
+               /* 2086 */ {0x5, 0x80, 0xc1},
+               /* */
+       /* 2089 */ {0x5, 0x06, 0xc2},
+       /* 2092 */ {0x5, 0x00, 0xca},
+               /* 2095 */ {0x5, 0x80, 0xc1},
+               /* */
+       /* 2098 */ {0x5, 0x04, 0xc2},
+       /* 2101 */ {0x5, 0x00, 0xca},
+
+       /* 2104 */ {0x3, 0x4c, 0x3},
+       /* 2107 */ {0x3, 0x18, 0x1},
+
+       /* 2110 */ {0x6, 0x70, 0x51},
+       /* 2113 */ {0x6, 0xbe, 0x53},
+       /* 2116 */ {0x6, 0x71, 0x57},
+       /* 2119 */ {0x6, 0x20, 0x58},
+       /* 2122 */ {0x6, 0x05, 0x59},
+       /* 2125 */ {0x6, 0x15, 0x5a},
+
+       /* 2128 */ {0x4, 0x00, 0x08},
+       /* Compress = OFF (0x1 to turn on) */
+       /* 2131 */ {0x4, 0x12, 0x09},
+       /* 2134 */ {0x4, 0x21, 0x0a},
+       /* 2137 */ {0x4, 0x10, 0x0b},
+       /* 2140 */ {0x4, 0x21, 0x0c},
+       /* 2143 */ {0x4, 0x05, 0x00},
+       /* was 5 (Image Type ? ) */
+       /* 2146 */ {0x4, 0x00, 0x01},
+
+       /* 2149 */ {0x6, 0x3f, 0x01},
+
+       /* 2152 */ {0x4, 0x00, 0x04},
+       /* 2155 */ {0x4, 0x00, 0x05},
+       /* 2158 */ {0x4, 0x40, 0x06},
+       /* 2161 */ {0x4, 0x40, 0x07},
+
+       /* 2164 */ {0x6, 0x1c, 0x17},
+       /* 2167 */ {0x6, 0xe2, 0x19},
+       /* 2170 */ {0x6, 0x1c, 0x1b},
+       /* 2173 */ {0x6, 0xe2, 0x1d},
+       /* 2176 */ {0x6, 0xaa, 0x1f},
+       /* 2179 */ {0x6, 0x70, 0x20},
+
+       /* 2182 */ {0x5, 0x01, 0x10},
+       /* 2185 */ {0x5, 0x00, 0x11},
+       /* 2188 */ {0x5, 0x01, 0x00},
+       /* 2191 */ {0x5, 0x05, 0x01},
+               /* 2194 */ {0x5, 0x00, 0xc1},
+               /* */
+       /* 2197 */ {0x5, 0x00, 0xc2},
+       /* 2200 */ {0x5, 0x00, 0xca},
+
+       /* 2203 */ {0x6, 0x70, 0x51},
+       /* 2206 */ {0x6, 0xbe, 0x53},
+       {}
+};
+
+/*
+   Made by Tomasz Zablocki (skalamandra@poczta.onet.pl)
+ * SPCA505b chip based cameras initialization data
+ *
+ */
+/* jfm */
+#define initial_brightness 0x7f        /* 0x0(white)-0xff(black) */
+/* #define initial_brightness 0x0      //0x0(white)-0xff(black) */
+/*
+ * Data to initialize a SPCA505. Common to the CCD and external modes
+ */
+static const __u16 spca505b_init_data[][3] = {
+/* start */
+       {0x02, 0x00, 0x00},             /* init */
+       {0x02, 0x00, 0x01},
+       {0x02, 0x00, 0x02},
+       {0x02, 0x00, 0x03},
+       {0x02, 0x00, 0x04},
+       {0x02, 0x00, 0x05},
+       {0x02, 0x00, 0x06},
+       {0x02, 0x00, 0x07},
+       {0x02, 0x00, 0x08},
+       {0x02, 0x00, 0x09},
+       {0x03, 0x00, 0x00},
+       {0x03, 0x00, 0x01},
+       {0x03, 0x00, 0x02},
+       {0x03, 0x00, 0x03},
+       {0x03, 0x00, 0x04},
+       {0x03, 0x00, 0x05},
+       {0x03, 0x00, 0x06},
+       {0x04, 0x00, 0x00},
+       {0x04, 0x00, 0x02},
+       {0x04, 0x00, 0x04},
+       {0x04, 0x00, 0x05},
+       {0x04, 0x00, 0x06},
+       {0x04, 0x00, 0x07},
+       {0x04, 0x00, 0x08},
+       {0x04, 0x00, 0x09},
+       {0x04, 0x00, 0x0a},
+       {0x04, 0x00, 0x0b},
+       {0x04, 0x00, 0x0c},
+       {0x07, 0x00, 0x00},
+       {0x07, 0x00, 0x03},
+       {0x08, 0x00, 0x00},
+       {0x08, 0x00, 0x01},
+       {0x08, 0x00, 0x02},
+       {0x00, 0x01, 0x00},
+       {0x00, 0x01, 0x01},
+       {0x00, 0x01, 0x34},
+       {0x00, 0x01, 0x35},
+       {0x06, 0x18, 0x08},
+       {0x06, 0xfc, 0x09},
+       {0x06, 0xfc, 0x0a},
+       {0x06, 0xfc, 0x0b},
+       {0x06, 0x18, 0x0c},
+       {0x06, 0xfc, 0x0d},
+       {0x06, 0xfc, 0x0e},
+       {0x06, 0xfc, 0x0f},
+       {0x06, 0x18, 0x10},
+       {0x06, 0xfe, 0x12},
+       {0x06, 0x00, 0x11},
+       {0x06, 0x00, 0x14},
+       {0x06, 0x00, 0x13},
+       {0x06, 0x28, 0x51},
+       {0x06, 0xff, 0x53},
+       {0x02, 0x00, 0x08},
+
+       {0x03, 0x00, 0x03},
+       {0x03, 0x10, 0x03},
+       {}
+};
+
+/*
+ * Data to initialize the camera using the internal CCD
+ */
+static const __u16 spca505b_open_data_ccd[][3] = {
+
+/* {0x02,0x00,0x00}, */
+       {0x03, 0x04, 0x01},             /* rst */
+       {0x03, 0x00, 0x01},
+       {0x03, 0x00, 0x00},
+       {0x03, 0x21, 0x00},
+       {0x03, 0x00, 0x04},
+       {0x03, 0x00, 0x03},
+       {0x03, 0x18, 0x03},
+       {0x03, 0x08, 0x01},
+       {0x03, 0x1c, 0x03},
+       {0x03, 0x5c, 0x03},
+       {0x03, 0x5c, 0x03},
+       {0x03, 0x18, 0x01},
+
+/* same as 505 */
+       {0x04, 0x10, 0x01},
+       {0x04, 0x00, 0x04},
+       {0x04, 0x00, 0x05},
+       {0x04, 0x20, 0x06},
+       {0x04, 0x20, 0x07},
+
+       {0x08, 0x0a, 0x00},
+
+       {0x05, 0x00, 0x10},
+       {0x05, 0x00, 0x11},
+       {0x05, 0x00, 0x12},
+       {0x05, 0x6f, 0x00},
+       {0x05, initial_brightness >> 6, 0x00},
+       {0x05, initial_brightness << 2, 0x01},
+       {0x05, 0x00, 0x02},
+       {0x05, 0x01, 0x03},
+       {0x05, 0x00, 0x04},
+       {0x05, 0x03, 0x05},
+       {0x05, 0xe0, 0x06},
+       {0x05, 0x20, 0x07},
+       {0x05, 0xa0, 0x08},
+       {0x05, 0x00, 0x12},
+       {0x05, 0x02, 0x0f},
+       {0x05, 128, 0x14},              /* max exposure off (0=on) */
+       {0x05, 0x01, 0xb0},
+       {0x05, 0x01, 0xbf},
+       {0x03, 0x02, 0x06},
+       {0x05, 0x10, 0x46},
+       {0x05, 0x08, 0x4a},
+
+       {0x06, 0x00, 0x01},
+       {0x06, 0x10, 0x02},
+       {0x06, 0x64, 0x07},
+       {0x06, 0x18, 0x08},
+       {0x06, 0xfc, 0x09},
+       {0x06, 0xfc, 0x0a},
+       {0x06, 0xfc, 0x0b},
+       {0x04, 0x00, 0x01},
+       {0x06, 0x18, 0x0c},
+       {0x06, 0xfc, 0x0d},
+       {0x06, 0xfc, 0x0e},
+       {0x06, 0xfc, 0x0f},
+       {0x06, 0x11, 0x10},             /* contrast */
+       {0x06, 0x00, 0x11},
+       {0x06, 0xfe, 0x12},
+       {0x06, 0x00, 0x13},
+       {0x06, 0x00, 0x14},
+       {0x06, 0x9d, 0x51},
+       {0x06, 0x40, 0x52},
+       {0x06, 0x7c, 0x53},
+       {0x06, 0x40, 0x54},
+       {0x06, 0x02, 0x57},
+       {0x06, 0x03, 0x58},
+       {0x06, 0x15, 0x59},
+       {0x06, 0x05, 0x5a},
+       {0x06, 0x03, 0x56},
+       {0x06, 0x02, 0x3f},
+       {0x06, 0x00, 0x40},
+       {0x06, 0x39, 0x41},
+       {0x06, 0x69, 0x42},
+       {0x06, 0x87, 0x43},
+       {0x06, 0x9e, 0x44},
+       {0x06, 0xb1, 0x45},
+       {0x06, 0xbf, 0x46},
+       {0x06, 0xcc, 0x47},
+       {0x06, 0xd5, 0x48},
+       {0x06, 0xdd, 0x49},
+       {0x06, 0xe3, 0x4a},
+       {0x06, 0xe8, 0x4b},
+       {0x06, 0xed, 0x4c},
+       {0x06, 0xf2, 0x4d},
+       {0x06, 0xf7, 0x4e},
+       {0x06, 0xfc, 0x4f},
+       {0x06, 0xff, 0x50},
+
+       {0x05, 0x01, 0xc0},
+       {0x05, 0x10, 0xcb},
+       {0x05, 0x40, 0xc1},
+       {0x05, 0x04, 0xc2},
+       {0x05, 0x00, 0xca},
+       {0x05, 0x40, 0xc1},
+       {0x05, 0x09, 0xc2},
+       {0x05, 0x00, 0xca},
+       {0x05, 0xc0, 0xc1},
+       {0x05, 0x09, 0xc2},
+       {0x05, 0x00, 0xca},
+       {0x05, 0x40, 0xc1},
+       {0x05, 0x59, 0xc2},
+       {0x05, 0x00, 0xca},
+       {0x04, 0x00, 0x01},
+       {0x05, 0x80, 0xc1},
+       {0x05, 0xec, 0xc2},
+       {0x05, 0x0, 0xca},
+
+       {0x06, 0x02, 0x57},
+       {0x06, 0x01, 0x58},
+       {0x06, 0x15, 0x59},
+       {0x06, 0x0a, 0x5a},
+       {0x06, 0x01, 0x57},
+       {0x06, 0x8a, 0x03},
+       {0x06, 0x0a, 0x6c},
+       {0x06, 0x30, 0x01},
+       {0x06, 0x20, 0x02},
+       {0x06, 0x00, 0x03},
+
+       {0x05, 0x8c, 0x25},
+
+       {0x06, 0x4d, 0x51},             /* maybe saturation (4d) */
+       {0x06, 0x84, 0x53},             /* making green (84) */
+       {0x06, 0x00, 0x57},             /* sharpness (1) */
+       {0x06, 0x18, 0x08},
+       {0x06, 0xfc, 0x09},
+       {0x06, 0xfc, 0x0a},
+       {0x06, 0xfc, 0x0b},
+       {0x06, 0x18, 0x0c},             /* maybe hue (18) */
+       {0x06, 0xfc, 0x0d},
+       {0x06, 0xfc, 0x0e},
+       {0x06, 0xfc, 0x0f},
+       {0x06, 0x18, 0x10},             /* maybe contrast (18) */
+
+       {0x05, 0x01, 0x02},
+
+       {0x04, 0x00, 0x08},             /* compression */
+       {0x04, 0x12, 0x09},
+       {0x04, 0x21, 0x0a},
+       {0x04, 0x10, 0x0b},
+       {0x04, 0x21, 0x0c},
+       {0x04, 0x1d, 0x00},             /* imagetype (1d) */
+       {0x04, 0x41, 0x01},             /* hardware snapcontrol */
+
+       {0x04, 0x00, 0x04},
+       {0x04, 0x00, 0x05},
+       {0x04, 0x10, 0x06},
+       {0x04, 0x10, 0x07},
+       {0x04, 0x40, 0x06},
+       {0x04, 0x40, 0x07},
+       {0x04, 0x00, 0x04},
+       {0x04, 0x00, 0x05},
+
+       {0x06, 0x1c, 0x17},
+       {0x06, 0xe2, 0x19},
+       {0x06, 0x1c, 0x1b},
+       {0x06, 0xe2, 0x1d},
+       {0x06, 0x5f, 0x1f},
+       {0x06, 0x32, 0x20},
+
+       {0x05, initial_brightness >> 6, 0x00},
+       {0x05, initial_brightness << 2, 0x01},
+       {0x05, 0x06, 0xc1},
+       {0x05, 0x58, 0xc2},
+       {0x05, 0x0, 0xca},
+       {0x05, 0x0, 0x11},
+       {}
+};
+
+static int reg_write(struct usb_device *dev,
+                    __u16 reg, __u16 index, __u16 value)
+{
+       int ret;
+
+       ret = usb_control_msg(dev,
+                       usb_sndctrlpipe(dev, 0),
+                       reg,
+                       USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                       value, index, NULL, 0, 500);
+       PDEBUG(D_PACK, "reg write: 0x%02x,0x%02x:0x%02x, 0x%x",
+               reg, index, value, ret);
+       if (ret < 0)
+               PDEBUG(D_ERR, "reg write: error %d", ret);
+       return ret;
+}
+
+/* returns: negative is error, pos or zero is data */
+static int reg_read(struct gspca_dev *gspca_dev,
+                       __u16 reg,      /* bRequest */
+                       __u16 index,    /* wIndex */
+                       __u16 length)   /* wLength (1 or 2 only) */
+{
+       int ret;
+
+       gspca_dev->usb_buf[1] = 0;
+       ret = usb_control_msg(gspca_dev->dev,
+                       usb_rcvctrlpipe(gspca_dev->dev, 0),
+                       reg,
+                       USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                       (__u16) 0,              /* value */
+                       (__u16) index,
+                       gspca_dev->usb_buf, length,
+                       500);                   /* timeout */
+       if (ret < 0) {
+               PDEBUG(D_ERR, "reg_read err %d", ret);
+               return -1;
+       }
+       return (gspca_dev->usb_buf[1] << 8) + gspca_dev->usb_buf[0];
+}
+
+static int write_vector(struct gspca_dev *gspca_dev,
+                       const __u16 data[][3])
+{
+       struct usb_device *dev = gspca_dev->dev;
+       int ret, i = 0;
+
+       while (data[i][0] != 0 || data[i][1] != 0 || data[i][2] != 0) {
+               ret = reg_write(dev, data[i][0], data[i][2], data[i][1]);
+               if (ret < 0) {
+                       PDEBUG(D_ERR,
+                               "Register write failed for 0x%x,0x%x,0x%x",
+                               data[i][0], data[i][1], data[i][2]);
+                       return ret;
+               }
+               i++;
+       }
+       return 0;
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+                       const struct usb_device_id *id)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       struct cam *cam;
+       __u16 vendor;
+       __u16 product;
+
+       vendor = id->idVendor;
+       product = id->idProduct;
+       switch (vendor) {
+       case 0x041e:            /* Creative cameras */
+/*             switch (product) { */
+/*             case 0x401d:     * here505b */
+                       sd->subtype = Nxultra;
+/*                     break; */
+/*             } */
+               break;
+       case 0x0733:    /* Rebadged ViewQuest (Intel) and ViewQuest cameras */
+/*             switch (product) { */
+/*             case 0x0430: */
+/*             fixme: may be UsbGrabberPV321 BRIDGE_SPCA506 SENSOR_SAA7113 */
+                       sd->subtype = IntelPCCameraPro;
+/*                     break; */
+/*             } */
+               break;
+       }
+
+       cam = &gspca_dev->cam;
+       cam->dev_name = (char *) id->driver_info;
+       cam->epaddr = 0x01;
+       cam->cam_mode = vga_mode;
+       if (sd->subtype != IntelPCCameraPro)
+               cam->nmodes = sizeof vga_mode / sizeof vga_mode[0];
+       else                    /* no 640x480 for IntelPCCameraPro */
+               cam->nmodes = sizeof vga_mode / sizeof vga_mode[0] - 1;
+       sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value;
+
+       if (sd->subtype == Nxultra) {
+               if (write_vector(gspca_dev, spca505b_init_data))
+                       return -EIO;
+       } else {
+               if (write_vector(gspca_dev, spca505_init_data))
+                       return -EIO;
+       }
+       return 0;
+}
+
+/* this function is called at open time */
+static int sd_open(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       int ret;
+
+       PDEBUG(D_STREAM, "Initializing SPCA505");
+       if (sd->subtype == Nxultra)
+               write_vector(gspca_dev, spca505b_open_data_ccd);
+       else
+               write_vector(gspca_dev, spca505_open_data_ccd);
+       ret = reg_read(gspca_dev, 6, 0x16, 2);
+
+       if (ret < 0) {
+               PDEBUG(D_ERR|D_STREAM,
+                      "register read failed for after vector read err = %d",
+                      ret);
+               return -EIO;
+       }
+       PDEBUG(D_STREAM,
+               "After vector read returns : 0x%x should be 0x0101",
+               ret & 0xffff);
+
+       ret = reg_write(gspca_dev->dev, 6, 0x16, 0x0a);
+       if (ret < 0) {
+               PDEBUG(D_ERR, "register write failed for (6,0xa,0x16) err=%d",
+                      ret);
+               return -EIO;
+       }
+       reg_write(gspca_dev->dev, 5, 0xc2, 18);
+       return 0;
+}
+
+static void sd_start(struct gspca_dev *gspca_dev)
+{
+       struct usb_device *dev = gspca_dev->dev;
+       int ret;
+
+       /* necessary because without it we can see stream
+        * only once after loading module */
+       /* stopping usb registers Tomasz change */
+       reg_write(dev, 0x02, 0x0, 0x0);
+       switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) {
+       case 0:
+               reg_write(dev, 0x04, 0x00, 0x00);
+               reg_write(dev, 0x04, 0x06, 0x10);
+               reg_write(dev, 0x04, 0x07, 0x10);
+               break;
+       case 1:
+               reg_write(dev, 0x04, 0x00, 0x01);
+               reg_write(dev, 0x04, 0x06, 0x1a);
+               reg_write(dev, 0x04, 0x07, 0x1a);
+               break;
+       case 2:
+               reg_write(dev, 0x04, 0x00, 0x02);
+               reg_write(dev, 0x04, 0x06, 0x1c);
+               reg_write(dev, 0x04, 0x07, 0x1d);
+               break;
+       case 4:
+               reg_write(dev, 0x04, 0x00, 0x04);
+               reg_write(dev, 0x04, 0x06, 0x34);
+               reg_write(dev, 0x04, 0x07, 0x34);
+               break;
+       default:
+/*     case 5: */
+               reg_write(dev, 0x04, 0x00, 0x05);
+               reg_write(dev, 0x04, 0x06, 0x40);
+               reg_write(dev, 0x04, 0x07, 0x40);
+               break;
+       }
+/* Enable ISO packet machine - should we do this here or in ISOC init ? */
+       ret = reg_write(dev, SPCA50X_REG_USB,
+                        SPCA50X_USB_CTRL,
+                        SPCA50X_CUSB_ENABLE);
+
+/*     reg_write(dev, 0x5, 0x0, 0x0); */
+/*     reg_write(dev, 0x5, 0x0, 0x1); */
+/*     reg_write(dev, 0x5, 0x11, 0x2); */
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+       /* Disable ISO packet machine */
+       reg_write(gspca_dev->dev, 0x02, 0x00, 0x00);
+}
+
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+}
+
+/* this function is called at close time */
+static void sd_close(struct gspca_dev *gspca_dev)
+{
+       /* This maybe reset or power control */
+       reg_write(gspca_dev->dev, 0x03, 0x03, 0x20);
+       reg_write(gspca_dev->dev, 0x03, 0x01, 0x0);
+       reg_write(gspca_dev->dev, 0x03, 0x00, 0x1);
+       reg_write(gspca_dev->dev, 0x05, 0x10, 0x1);
+       reg_write(gspca_dev->dev, 0x05, 0x11, 0xf);
+}
+
+/* convert YYUV per line to YUYV (YUV 4:2:2) */
+static void yyuv_decode(unsigned char *out,
+                       unsigned char *in,
+                       int width,
+                       int height)
+{
+       unsigned char *Ui, *Vi, *yi, *yi1;
+       unsigned char *out1;
+       int i, j;
+
+       yi = in;
+       for (i = height / 2; --i >= 0; ) {
+               out1 = out + width * 2;         /* next line */
+               yi1 = yi + width;
+               Ui = yi1 + width;
+               Vi = Ui + width / 2;
+               for (j = width / 2; --j >= 0; ) {
+                       *out++ = 128 + *yi++;
+                       *out++ = 128 + *Ui;
+                       *out++ = 128 + *yi++;
+                       *out++ = 128 + *Vi;
+
+                       *out1++ = 128 + *yi1++;
+                       *out1++ = 128 + *Ui++;
+                       *out1++ = 128 + *yi1++;
+                       *out1++ = 128 + *Vi++;
+               }
+               yi += width * 2;
+               out = out1;
+       }
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+                       struct gspca_frame *frame,      /* target */
+                       __u8 *data,                     /* isoc packet */
+                       int len)                        /* iso packet length */
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       switch (data[0]) {
+       case 0:                         /* start of frame */
+               if (gspca_dev->last_packet_type == FIRST_PACKET) {
+                       yyuv_decode(sd->tmpbuf2, sd->tmpbuf,
+                                       gspca_dev->width,
+                                       gspca_dev->height);
+                       frame = gspca_frame_add(gspca_dev,
+                                               LAST_PACKET,
+                                               frame,
+                                               sd->tmpbuf2,
+                                               gspca_dev->width
+                                                       * gspca_dev->height
+                                                       * 2);
+               }
+               gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
+                               data, 0);
+               data += SPCA50X_OFFSET_DATA;
+               len -= SPCA50X_OFFSET_DATA;
+               if (len > 0)
+                       memcpy(sd->tmpbuf, data, len);
+               else
+                       len = 0;
+               sd->buflen = len;
+               return;
+       case 0xff:                      /* drop */
+/*             gspca_dev->last_packet_type = DISCARD_PACKET; */
+               return;
+       }
+       data += 1;
+       len -= 1;
+       memcpy(&sd->tmpbuf[sd->buflen], data, len);
+       sd->buflen += len;
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       __u8 brightness = sd->brightness;
+       reg_write(gspca_dev->dev, 5, 0x00, (255 - brightness) >> 6);
+       reg_write(gspca_dev->dev, 5, 0x01, (255 - brightness) << 2);
+
+}
+static void getbrightness(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->brightness = 255
+               - ((reg_read(gspca_dev, 5, 0x01, 1) >> 2)
+                       + (reg_read(gspca_dev, 5, 0x0, 1) << 6));
+}
+
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->brightness = val;
+       if (gspca_dev->streaming)
+               setbrightness(gspca_dev);
+       return 0;
+}
+
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       getbrightness(gspca_dev);
+       *val = sd->brightness;
+       return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+       .name = MODULE_NAME,
+       .ctrls = sd_ctrls,
+       .nctrls = ARRAY_SIZE(sd_ctrls),
+       .config = sd_config,
+       .open = sd_open,
+       .start = sd_start,
+       .stopN = sd_stopN,
+       .stop0 = sd_stop0,
+       .close = sd_close,
+       .pkt_scan = sd_pkt_scan,
+};
+
+/* -- module initialisation -- */
+#define DVNM(name) .driver_info = (kernel_ulong_t) name
+static const __devinitdata struct usb_device_id device_table[] = {
+       {USB_DEVICE(0x041e, 0x401d), DVNM("Creative Webcam NX ULTRA")},
+       {USB_DEVICE(0x0733, 0x0430), DVNM("Intel PC Camera Pro")},
+       {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+                       const struct usb_device_id *id)
+{
+       return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+                               THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+       .name = MODULE_NAME,
+       .id_table = device_table,
+       .probe = sd_probe,
+       .disconnect = gspca_disconnect,
+};
+
+/* -- module insert / remove -- */
+static int __init sd_mod_init(void)
+{
+       if (usb_register(&sd_driver) < 0)
+               return -1;
+       PDEBUG(D_PROBE, "v%s registered", version);
+       return 0;
+}
+static void __exit sd_mod_exit(void)
+{
+       usb_deregister(&sd_driver);
+       PDEBUG(D_PROBE, "deregistered");
+}
+
+module_init(sd_mod_init);
+module_exit(sd_mod_exit);
diff --git a/drivers/media/video/gspca/spca506.c b/drivers/media/video/gspca/spca506.c
new file mode 100644 (file)
index 0000000..143203c
--- /dev/null
@@ -0,0 +1,847 @@
+/*
+ * SPCA506 chip based cameras function
+ * M Xhaard 15/04/2004 based on different work Mark Taylor and others
+ * and my own snoopy file on a pv-321c donate by a german compagny
+ *                "Firma Frank Gmbh" from  Saarbruecken
+ *
+ * V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
+ *
+ * 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
+ * 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
+ */
+
+#define MODULE_NAME "spca506"
+
+#include "gspca.h"
+
+#define DRIVER_VERSION_NUMBER  KERNEL_VERSION(2, 1, 7)
+static const char version[] = "2.1.7";
+
+MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
+MODULE_DESCRIPTION("GSPCA/SPCA506 USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* specific webcam descriptor */
+struct sd {
+       struct gspca_dev gspca_dev;     /* !! must be the first item */
+
+       int buflen;
+       __u8 tmpbuf[640 * 480 * 3];     /* YYUV per line */
+       __u8 tmpbuf2[640 * 480 * 2];    /* YUYV */
+
+       unsigned char brightness;
+       unsigned char contrast;
+       unsigned char colors;
+       unsigned char hue;
+       char norme;
+       char channel;
+};
+
+/* V4L2 controls supported by the driver */
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_sethue(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val);
+
+static struct ctrl sd_ctrls[] = {
+#define SD_BRIGHTNESS 0
+       {
+           {
+               .id      = V4L2_CID_BRIGHTNESS,
+               .type    = V4L2_CTRL_TYPE_INTEGER,
+               .name    = "Brightness",
+               .minimum = 0,
+               .maximum = 0xff,
+               .step    = 1,
+               .default_value = 0x80,
+           },
+           .set = sd_setbrightness,
+           .get = sd_getbrightness,
+       },
+#define SD_CONTRAST 1
+       {
+           {
+               .id      = V4L2_CID_CONTRAST,
+               .type    = V4L2_CTRL_TYPE_INTEGER,
+               .name    = "Contrast",
+               .minimum = 0,
+               .maximum = 0xff,
+               .step    = 1,
+               .default_value = 0x47,
+           },
+           .set = sd_setcontrast,
+           .get = sd_getcontrast,
+       },
+#define SD_COLOR 2
+       {
+           {
+               .id      = V4L2_CID_SATURATION,
+               .type    = V4L2_CTRL_TYPE_INTEGER,
+               .name    = "Saturation",
+               .minimum = 0,
+               .maximum = 0xff,
+               .step    = 1,
+               .default_value = 0x40,
+           },
+           .set = sd_setcolors,
+           .get = sd_getcolors,
+       },
+#define SD_HUE 3
+       {
+           {
+               .id      = V4L2_CID_HUE,
+               .type    = V4L2_CTRL_TYPE_INTEGER,
+               .name    = "Hue",
+               .minimum = 0,
+               .maximum = 0xff,
+               .step    = 1,
+               .default_value = 0,
+           },
+           .set = sd_sethue,
+           .get = sd_gethue,
+       },
+};
+
+static struct v4l2_pix_format vga_mode[] = {
+       {160, 120, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE,
+               .bytesperline = 160 * 2,
+               .sizeimage = 160 * 120 * 2,
+               .colorspace = V4L2_COLORSPACE_SRGB,
+               .priv = 5},
+       {176, 144, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE,
+               .bytesperline = 176 * 2,
+               .sizeimage = 176 * 144 * 2,
+               .colorspace = V4L2_COLORSPACE_SRGB,
+               .priv = 4},
+       {320, 240, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE,
+               .bytesperline = 320 * 2,
+               .sizeimage = 320 * 240 * 2,
+               .colorspace = V4L2_COLORSPACE_SRGB,
+               .priv = 2},
+       {352, 288, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE,
+               .bytesperline = 352 * 2,
+               .sizeimage = 352 * 288 * 2,
+               .colorspace = V4L2_COLORSPACE_SRGB,
+               .priv = 1},
+       {640, 480, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE,
+               .bytesperline = 640 * 2,
+               .sizeimage = 640 * 480 * 2,
+               .colorspace = V4L2_COLORSPACE_SRGB,
+               .priv = 0},
+};
+
+#define SPCA50X_OFFSET_DATA 10
+
+#define SAA7113_bright 0x0a    /* defaults 0x80 */
+#define SAA7113_contrast 0x0b  /* defaults 0x47 */
+#define SAA7113_saturation 0x0c        /* defaults 0x40 */
+#define SAA7113_hue 0x0d       /* defaults 0x00 */
+#define SAA7113_I2C_BASE_WRITE 0x4a
+
+/* read 'len' bytes to gspca_dev->usb_buf */
+static void reg_r(struct gspca_dev *gspca_dev,
+                 __u16 req,
+                 __u16 index,
+                 __u16 length)
+{
+       usb_control_msg(gspca_dev->dev,
+                       usb_rcvctrlpipe(gspca_dev->dev, 0),
+                       req,
+                       USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                       0,              /* value */
+                       index, gspca_dev->usb_buf, length,
+                       500);
+}
+
+static void reg_w(struct usb_device *dev,
+                 __u16 req,
+                 __u16 value,
+                 __u16 index)
+{
+       usb_control_msg(dev,
+                       usb_sndctrlpipe(dev, 0),
+                       req,
+                       USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                       value, index,
+                       NULL, 0, 500);
+}
+
+static void spca506_Initi2c(struct gspca_dev *gspca_dev)
+{
+       reg_w(gspca_dev->dev, 0x07, SAA7113_I2C_BASE_WRITE, 0x0004);
+}
+
+static void spca506_WriteI2c(struct gspca_dev *gspca_dev, __u16 valeur,
+                            __u16 reg)
+{
+       int retry = 60;
+
+       reg_w(gspca_dev->dev, 0x07, reg, 0x0001);
+       reg_w(gspca_dev->dev, 0x07, valeur, 0x0000);
+       while (retry--) {
+               reg_r(gspca_dev, 0x07, 0x0003, 2);
+               if ((gspca_dev->usb_buf[0] | gspca_dev->usb_buf[1]) == 0x00)
+                       break;
+       }
+}
+
+static int spca506_ReadI2c(struct gspca_dev *gspca_dev, __u16 reg)
+{
+       int retry = 60;
+
+       reg_w(gspca_dev->dev, 0x07, SAA7113_I2C_BASE_WRITE, 0x0004);
+       reg_w(gspca_dev->dev, 0x07, reg, 0x0001);
+       reg_w(gspca_dev->dev, 0x07, 0x01, 0x0002);
+       while (--retry) {
+               reg_r(gspca_dev, 0x07, 0x0003, 2);
+               if ((gspca_dev->usb_buf[0] | gspca_dev->usb_buf[1]) == 0x00)
+                       break;
+       }
+       if (retry == 0)
+               return -1;
+       reg_r(gspca_dev, 0x07, 0x0000, 1);
+       return gspca_dev->usb_buf[0];
+}
+
+static void spca506_SetNormeInput(struct gspca_dev *gspca_dev,
+                                __u16 norme,
+                                __u16 channel)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+/* fixme: check if channel == 0..3 and 6..9 (8 values) */
+       __u8 setbit0 = 0x00;
+       __u8 setbit1 = 0x00;
+       __u8 videomask = 0x00;
+
+       PDEBUG(D_STREAM, "** Open Set Norme **");
+       spca506_Initi2c(gspca_dev);
+       /* NTSC bit0 -> 1(525 l) PAL SECAM bit0 -> 0 (625 l) */
+       /* Composite channel bit1 -> 1 S-video bit 1 -> 0 */
+       /* and exclude SAA7113 reserved channel set default 0 otherwise */
+       if (norme & V4L2_STD_NTSC)
+               setbit0 = 0x01;
+       if (channel == 4 || channel == 5 || channel > 9)
+               channel = 0;
+       if (channel < 4)
+               setbit1 = 0x02;
+       videomask = (0x48 | setbit0 | setbit1);
+       reg_w(gspca_dev->dev, 0x08, videomask, 0x0000);
+       spca506_WriteI2c(gspca_dev, (0xc0 | (channel & 0x0F)), 0x02);
+
+       if (norme & V4L2_STD_NTSC)
+               spca506_WriteI2c(gspca_dev, 0x33, 0x0e);
+                                       /* Chrominance Control NTSC N */
+       else if (norme & V4L2_STD_SECAM)
+               spca506_WriteI2c(gspca_dev, 0x53, 0x0e);
+                                       /* Chrominance Control SECAM */
+       else
+               spca506_WriteI2c(gspca_dev, 0x03, 0x0e);
+                                       /* Chrominance Control PAL BGHIV */
+
+       sd->norme = norme;
+       sd->channel = channel;
+       PDEBUG(D_STREAM, "Set Video Byte to 0x%2x", videomask);
+       PDEBUG(D_STREAM, "Set Norme: %08x Channel %d", norme, channel);
+}
+
+static void spca506_GetNormeInput(struct gspca_dev *gspca_dev,
+                                 __u16 *norme, __u16 *channel)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       /* Read the register is not so good value change so
+          we use your own copy in spca50x struct */
+       *norme = sd->norme;
+       *channel = sd->channel;
+       PDEBUG(D_STREAM, "Get Norme: %d Channel %d", *norme, *channel);
+}
+
+static void spca506_Setsize(struct gspca_dev *gspca_dev, __u16 code,
+                           __u16 xmult, __u16 ymult)
+{
+       struct usb_device *dev = gspca_dev->dev;
+
+       PDEBUG(D_STREAM, "** SetSize **");
+       reg_w(dev, 0x04, (0x18 | (code & 0x07)), 0x0000);
+       /* Soft snap 0x40 Hard 0x41 */
+       reg_w(dev, 0x04, 0x41, 0x0001);
+       reg_w(dev, 0x04, 0x00, 0x0002);
+       /* reserved */
+       reg_w(dev, 0x04, 0x00, 0x0003);
+
+       /* reserved */
+       reg_w(dev, 0x04, 0x00, 0x0004);
+       /* reserved */
+       reg_w(dev, 0x04, 0x01, 0x0005);
+       /* reserced */
+       reg_w(dev, 0x04, xmult, 0x0006);
+       /* reserved */
+       reg_w(dev, 0x04, ymult, 0x0007);
+       /* compression 1 */
+       reg_w(dev, 0x04, 0x00, 0x0008);
+       /* T=64 -> 2 */
+       reg_w(dev, 0x04, 0x00, 0x0009);
+       /* threshold2D */
+       reg_w(dev, 0x04, 0x21, 0x000a);
+       /* quantization */
+       reg_w(dev, 0x04, 0x00, 0x000b);
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+                       const struct usb_device_id *id)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       struct cam *cam;
+
+       cam = &gspca_dev->cam;
+       cam->dev_name = (char *) id->driver_info;
+       cam->epaddr = 0x01;
+       cam->cam_mode = vga_mode;
+       cam->nmodes = sizeof vga_mode / sizeof vga_mode[0];
+       sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value;
+       sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value;
+       sd->colors = sd_ctrls[SD_COLOR].qctrl.default_value;
+       sd->hue = sd_ctrls[SD_HUE].qctrl.default_value;
+       return 0;
+}
+
+/* this function is called at open time */
+static int sd_open(struct gspca_dev *gspca_dev)
+{
+       struct usb_device *dev = gspca_dev->dev;
+
+       reg_w(dev, 0x03, 0x00, 0x0004);
+       reg_w(dev, 0x03, 0xFF, 0x0003);
+       reg_w(dev, 0x03, 0x00, 0x0000);
+       reg_w(dev, 0x03, 0x1c, 0x0001);
+       reg_w(dev, 0x03, 0x18, 0x0001);
+       /* Init on PAL and composite input0 */
+       spca506_SetNormeInput(gspca_dev, 0, 0);
+       reg_w(dev, 0x03, 0x1c, 0x0001);
+       reg_w(dev, 0x03, 0x18, 0x0001);
+       reg_w(dev, 0x05, 0x00, 0x0000);
+       reg_w(dev, 0x05, 0xef, 0x0001);
+       reg_w(dev, 0x05, 0x00, 0x00c1);
+       reg_w(dev, 0x05, 0x00, 0x00c2);
+       reg_w(dev, 0x06, 0x18, 0x0002);
+       reg_w(dev, 0x06, 0xf5, 0x0011);
+       reg_w(dev, 0x06, 0x02, 0x0012);
+       reg_w(dev, 0x06, 0xfb, 0x0013);
+       reg_w(dev, 0x06, 0x00, 0x0014);
+       reg_w(dev, 0x06, 0xa4, 0x0051);
+       reg_w(dev, 0x06, 0x40, 0x0052);
+       reg_w(dev, 0x06, 0x71, 0x0053);
+       reg_w(dev, 0x06, 0x40, 0x0054);
+       /************************************************/
+       reg_w(dev, 0x03, 0x00, 0x0004);
+       reg_w(dev, 0x03, 0x00, 0x0003);
+       reg_w(dev, 0x03, 0x00, 0x0004);
+       reg_w(dev, 0x03, 0xFF, 0x0003);
+       reg_w(dev, 0x02, 0x00, 0x0000);
+       reg_w(dev, 0x03, 0x60, 0x0000);
+       reg_w(dev, 0x03, 0x18, 0x0001);
+       /* for a better reading mx :)     */
+       /*sdca506_WriteI2c(value,register) */
+       spca506_Initi2c(gspca_dev);
+       spca506_WriteI2c(gspca_dev, 0x08, 0x01);
+       spca506_WriteI2c(gspca_dev, 0xc0, 0x02);
+                                               /* input composite video */
+       spca506_WriteI2c(gspca_dev, 0x33, 0x03);
+       spca506_WriteI2c(gspca_dev, 0x00, 0x04);
+       spca506_WriteI2c(gspca_dev, 0x00, 0x05);
+       spca506_WriteI2c(gspca_dev, 0x0d, 0x06);
+       spca506_WriteI2c(gspca_dev, 0xf0, 0x07);
+       spca506_WriteI2c(gspca_dev, 0x98, 0x08);
+       spca506_WriteI2c(gspca_dev, 0x03, 0x09);
+       spca506_WriteI2c(gspca_dev, 0x80, 0x0a);
+       spca506_WriteI2c(gspca_dev, 0x47, 0x0b);
+       spca506_WriteI2c(gspca_dev, 0x48, 0x0c);
+       spca506_WriteI2c(gspca_dev, 0x00, 0x0d);
+       spca506_WriteI2c(gspca_dev, 0x03, 0x0e);        /* Chroma Pal adjust */
+       spca506_WriteI2c(gspca_dev, 0x2a, 0x0f);
+       spca506_WriteI2c(gspca_dev, 0x00, 0x10);
+       spca506_WriteI2c(gspca_dev, 0x0c, 0x11);
+       spca506_WriteI2c(gspca_dev, 0xb8, 0x12);
+       spca506_WriteI2c(gspca_dev, 0x01, 0x13);
+       spca506_WriteI2c(gspca_dev, 0x00, 0x14);
+       spca506_WriteI2c(gspca_dev, 0x00, 0x15);
+       spca506_WriteI2c(gspca_dev, 0x00, 0x16);
+       spca506_WriteI2c(gspca_dev, 0x00, 0x17);
+       spca506_WriteI2c(gspca_dev, 0x00, 0x18);
+       spca506_WriteI2c(gspca_dev, 0x00, 0x19);
+       spca506_WriteI2c(gspca_dev, 0x00, 0x1a);
+       spca506_WriteI2c(gspca_dev, 0x00, 0x1b);
+       spca506_WriteI2c(gspca_dev, 0x00, 0x1c);
+       spca506_WriteI2c(gspca_dev, 0x00, 0x1d);
+       spca506_WriteI2c(gspca_dev, 0x00, 0x1e);
+       spca506_WriteI2c(gspca_dev, 0xa1, 0x1f);
+       spca506_WriteI2c(gspca_dev, 0x02, 0x40);
+       spca506_WriteI2c(gspca_dev, 0xff, 0x41);
+       spca506_WriteI2c(gspca_dev, 0xff, 0x42);
+       spca506_WriteI2c(gspca_dev, 0xff, 0x43);
+       spca506_WriteI2c(gspca_dev, 0xff, 0x44);
+       spca506_WriteI2c(gspca_dev, 0xff, 0x45);
+       spca506_WriteI2c(gspca_dev, 0xff, 0x46);
+       spca506_WriteI2c(gspca_dev, 0xff, 0x47);
+       spca506_WriteI2c(gspca_dev, 0xff, 0x48);
+       spca506_WriteI2c(gspca_dev, 0xff, 0x49);
+       spca506_WriteI2c(gspca_dev, 0xff, 0x4a);
+       spca506_WriteI2c(gspca_dev, 0xff, 0x4b);
+       spca506_WriteI2c(gspca_dev, 0xff, 0x4c);
+       spca506_WriteI2c(gspca_dev, 0xff, 0x4d);
+       spca506_WriteI2c(gspca_dev, 0xff, 0x4e);
+       spca506_WriteI2c(gspca_dev, 0xff, 0x4f);
+       spca506_WriteI2c(gspca_dev, 0xff, 0x50);
+       spca506_WriteI2c(gspca_dev, 0xff, 0x51);
+       spca506_WriteI2c(gspca_dev, 0xff, 0x52);
+       spca506_WriteI2c(gspca_dev, 0xff, 0x53);
+       spca506_WriteI2c(gspca_dev, 0xff, 0x54);
+       spca506_WriteI2c(gspca_dev, 0xff, 0x55);
+       spca506_WriteI2c(gspca_dev, 0xff, 0x56);
+       spca506_WriteI2c(gspca_dev, 0xff, 0x57);
+       spca506_WriteI2c(gspca_dev, 0x00, 0x58);
+       spca506_WriteI2c(gspca_dev, 0x54, 0x59);
+       spca506_WriteI2c(gspca_dev, 0x07, 0x5a);
+       spca506_WriteI2c(gspca_dev, 0x83, 0x5b);
+       spca506_WriteI2c(gspca_dev, 0x00, 0x5c);
+       spca506_WriteI2c(gspca_dev, 0x00, 0x5d);
+       spca506_WriteI2c(gspca_dev, 0x00, 0x5e);
+       spca506_WriteI2c(gspca_dev, 0x00, 0x5f);
+       spca506_WriteI2c(gspca_dev, 0x00, 0x60);
+       spca506_WriteI2c(gspca_dev, 0x05, 0x61);
+       spca506_WriteI2c(gspca_dev, 0x9f, 0x62);
+       PDEBUG(D_STREAM, "** Close Init *");
+       return 0;
+}
+
+static void sd_start(struct gspca_dev *gspca_dev)
+{
+       struct usb_device *dev = gspca_dev->dev;
+       __u16 norme;
+       __u16 channel;
+
+       /**************************************/
+       reg_w(dev, 0x03, 0x00, 0x0004);
+       reg_w(dev, 0x03, 0x00, 0x0003);
+       reg_w(dev, 0x03, 0x00, 0x0004);
+       reg_w(dev, 0x03, 0xFF, 0x0003);
+       reg_w(dev, 0x02, 0x00, 0x0000);
+       reg_w(dev, 0x03, 0x60, 0x0000);
+       reg_w(dev, 0x03, 0x18, 0x0001);
+
+       /*sdca506_WriteI2c(value,register) */
+       spca506_Initi2c(gspca_dev);
+       spca506_WriteI2c(gspca_dev, 0x08, 0x01);        /* Increment Delay */
+/*     spca506_WriteI2c(gspca_dev, 0xc0, 0x02); * Analog Input Control 1 */
+       spca506_WriteI2c(gspca_dev, 0x33, 0x03);
+                                               /* Analog Input Control 2 */
+       spca506_WriteI2c(gspca_dev, 0x00, 0x04);
+                                               /* Analog Input Control 3 */
+       spca506_WriteI2c(gspca_dev, 0x00, 0x05);
+                                               /* Analog Input Control 4 */
+       spca506_WriteI2c(gspca_dev, 0x0d, 0x06);
+                                       /* Horizontal Sync Start 0xe9-0x0d */
+       spca506_WriteI2c(gspca_dev, 0xf0, 0x07);
+                                       /* Horizontal Sync Stop  0x0d-0xf0 */
+
+       spca506_WriteI2c(gspca_dev, 0x98, 0x08);        /* Sync Control */
+/*             Defaults value                  */
+       spca506_WriteI2c(gspca_dev, 0x03, 0x09);        /* Luminance Control */
+       spca506_WriteI2c(gspca_dev, 0x80, 0x0a);
+                                               /* Luminance Brightness */
+       spca506_WriteI2c(gspca_dev, 0x47, 0x0b);        /* Luminance Contrast */
+       spca506_WriteI2c(gspca_dev, 0x48, 0x0c);
+                                               /* Chrominance Saturation */
+       spca506_WriteI2c(gspca_dev, 0x00, 0x0d);
+                                               /* Chrominance Hue Control */
+       spca506_WriteI2c(gspca_dev, 0x2a, 0x0f);
+                                               /* Chrominance Gain Control */
+       /**************************************/
+       spca506_WriteI2c(gspca_dev, 0x00, 0x10);
+                                               /* Format/Delay Control */
+       spca506_WriteI2c(gspca_dev, 0x0c, 0x11);        /* Output Control 1 */
+       spca506_WriteI2c(gspca_dev, 0xb8, 0x12);        /* Output Control 2 */
+       spca506_WriteI2c(gspca_dev, 0x01, 0x13);        /* Output Control 3 */
+       spca506_WriteI2c(gspca_dev, 0x00, 0x14);        /* reserved */
+       spca506_WriteI2c(gspca_dev, 0x00, 0x15);        /* VGATE START */
+       spca506_WriteI2c(gspca_dev, 0x00, 0x16);        /* VGATE STOP */
+       spca506_WriteI2c(gspca_dev, 0x00, 0x17);    /* VGATE Control (MSB) */
+       spca506_WriteI2c(gspca_dev, 0x00, 0x18);
+       spca506_WriteI2c(gspca_dev, 0x00, 0x19);
+       spca506_WriteI2c(gspca_dev, 0x00, 0x1a);
+       spca506_WriteI2c(gspca_dev, 0x00, 0x1b);
+       spca506_WriteI2c(gspca_dev, 0x00, 0x1c);
+       spca506_WriteI2c(gspca_dev, 0x00, 0x1d);
+       spca506_WriteI2c(gspca_dev, 0x00, 0x1e);
+       spca506_WriteI2c(gspca_dev, 0xa1, 0x1f);
+       spca506_WriteI2c(gspca_dev, 0x02, 0x40);
+       spca506_WriteI2c(gspca_dev, 0xff, 0x41);
+       spca506_WriteI2c(gspca_dev, 0xff, 0x42);
+       spca506_WriteI2c(gspca_dev, 0xff, 0x43);
+       spca506_WriteI2c(gspca_dev, 0xff, 0x44);
+       spca506_WriteI2c(gspca_dev, 0xff, 0x45);
+       spca506_WriteI2c(gspca_dev, 0xff, 0x46);
+       spca506_WriteI2c(gspca_dev, 0xff, 0x47);
+       spca506_WriteI2c(gspca_dev, 0xff, 0x48);
+       spca506_WriteI2c(gspca_dev, 0xff, 0x49);
+       spca506_WriteI2c(gspca_dev, 0xff, 0x4a);
+       spca506_WriteI2c(gspca_dev, 0xff, 0x4b);
+       spca506_WriteI2c(gspca_dev, 0xff, 0x4c);
+       spca506_WriteI2c(gspca_dev, 0xff, 0x4d);
+       spca506_WriteI2c(gspca_dev, 0xff, 0x4e);
+       spca506_WriteI2c(gspca_dev, 0xff, 0x4f);
+       spca506_WriteI2c(gspca_dev, 0xff, 0x50);
+       spca506_WriteI2c(gspca_dev, 0xff, 0x51);
+       spca506_WriteI2c(gspca_dev, 0xff, 0x52);
+       spca506_WriteI2c(gspca_dev, 0xff, 0x53);
+       spca506_WriteI2c(gspca_dev, 0xff, 0x54);
+       spca506_WriteI2c(gspca_dev, 0xff, 0x55);
+       spca506_WriteI2c(gspca_dev, 0xff, 0x56);
+       spca506_WriteI2c(gspca_dev, 0xff, 0x57);
+       spca506_WriteI2c(gspca_dev, 0x00, 0x58);
+       spca506_WriteI2c(gspca_dev, 0x54, 0x59);
+       spca506_WriteI2c(gspca_dev, 0x07, 0x5a);
+       spca506_WriteI2c(gspca_dev, 0x83, 0x5b);
+       spca506_WriteI2c(gspca_dev, 0x00, 0x5c);
+       spca506_WriteI2c(gspca_dev, 0x00, 0x5d);
+       spca506_WriteI2c(gspca_dev, 0x00, 0x5e);
+       spca506_WriteI2c(gspca_dev, 0x00, 0x5f);
+       spca506_WriteI2c(gspca_dev, 0x00, 0x60);
+       spca506_WriteI2c(gspca_dev, 0x05, 0x61);
+       spca506_WriteI2c(gspca_dev, 0x9f, 0x62);
+       /**************************************/
+       reg_w(dev, 0x05, 0x00, 0x0003);
+       reg_w(dev, 0x05, 0x00, 0x0004);
+       reg_w(dev, 0x03, 0x10, 0x0001);
+       reg_w(dev, 0x03, 0x78, 0x0000);
+       switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) {
+       case 0:
+               spca506_Setsize(gspca_dev, 0, 0x10, 0x10);
+               break;
+       case 1:
+               spca506_Setsize(gspca_dev, 1, 0x1a, 0x1a);
+               break;
+       case 2:
+               spca506_Setsize(gspca_dev, 2, 0x1c, 0x1c);
+               break;
+       case 4:
+               spca506_Setsize(gspca_dev, 4, 0x34, 0x34);
+               break;
+       default:
+/*     case 5: */
+               spca506_Setsize(gspca_dev, 5, 0x40, 0x40);
+               break;
+       }
+
+       /* compress setting and size */
+       /* set i2c luma */
+       reg_w(dev, 0x02, 0x01, 0x0000);
+       reg_w(dev, 0x03, 0x12, 0x0000);
+       reg_r(gspca_dev, 0x04, 0x0001, 2);
+       PDEBUG(D_STREAM, "webcam started");
+       spca506_GetNormeInput(gspca_dev, &norme, &channel);
+       spca506_SetNormeInput(gspca_dev, norme, channel);
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+       struct usb_device *dev = gspca_dev->dev;
+
+       reg_w(dev, 0x02, 0x00, 0x0000);
+       reg_w(dev, 0x03, 0x00, 0x0004);
+       reg_w(dev, 0x03, 0x00, 0x0003);
+}
+
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+}
+
+static void sd_close(struct gspca_dev *gspca_dev)
+{
+}
+
+/* convert YYUV per line to YUYV (YUV 4:2:2) */
+static void yyuv_decode(unsigned char *out,
+                       unsigned char *in,
+                       int width,
+                       int height)
+{
+       unsigned char *Ui, *Vi, *yi, *yi1;
+       unsigned char *out1;
+       int i, j;
+
+       yi = in;
+       for (i = height / 2; --i >= 0; ) {
+               out1 = out + width * 2;         /* next line */
+               yi1 = yi + width;
+               Ui = yi1 + width;
+               Vi = Ui + width / 2;
+               for (j = width / 2; --j >= 0; ) {
+                       *out++ = 128 + *yi++;
+                       *out++ = 128 + *Ui;
+                       *out++ = 128 + *yi++;
+                       *out++ = 128 + *Vi;
+
+                       *out1++ = 128 + *yi1++;
+                       *out1++ = 128 + *Ui++;
+                       *out1++ = 128 + *yi1++;
+                       *out1++ = 128 + *Vi++;
+               }
+               yi += width * 2;
+               out = out1;
+       }
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+                       struct gspca_frame *frame,      /* target */
+                       __u8 *data,                     /* isoc packet */
+                       int len)                        /* iso packet length */
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       switch (data[0]) {
+       case 0:                         /* start of frame */
+               if (gspca_dev->last_packet_type == FIRST_PACKET) {
+                       yyuv_decode(sd->tmpbuf2, sd->tmpbuf,
+                                       gspca_dev->width,
+                                       gspca_dev->height);
+                       frame = gspca_frame_add(gspca_dev,
+                                               LAST_PACKET,
+                                               frame,
+                                               sd->tmpbuf2,
+                                               gspca_dev->width
+                                                       * gspca_dev->height
+                                                       * 2);
+               }
+               gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
+                               data, 0);
+               data += SPCA50X_OFFSET_DATA;
+               len -= SPCA50X_OFFSET_DATA;
+               if (len > 0)
+                       memcpy(sd->tmpbuf, data, len);
+               else
+                       len = 0;
+               sd->buflen = len;
+               return;
+       case 0xff:                      /* drop */
+/*             gspca_dev->last_packet_type = DISCARD_PACKET; */
+               return;
+       }
+       data += 1;
+       len -= 1;
+       memcpy(&sd->tmpbuf[sd->buflen], data, len);
+       sd->buflen += len;
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       spca506_Initi2c(gspca_dev);
+       spca506_WriteI2c(gspca_dev, sd->brightness, SAA7113_bright);
+       spca506_WriteI2c(gspca_dev, 0x01, 0x09);
+}
+
+static void getbrightness(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->brightness = spca506_ReadI2c(gspca_dev, SAA7113_bright);
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       spca506_Initi2c(gspca_dev);
+       spca506_WriteI2c(gspca_dev, sd->contrast, SAA7113_contrast);
+       spca506_WriteI2c(gspca_dev, 0x01, 0x09);
+}
+
+static void getcontrast(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->contrast = spca506_ReadI2c(gspca_dev, SAA7113_contrast);
+}
+
+static void setcolors(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       spca506_Initi2c(gspca_dev);
+       spca506_WriteI2c(gspca_dev, sd->colors, SAA7113_saturation);
+       spca506_WriteI2c(gspca_dev, 0x01, 0x09);
+}
+
+static void getcolors(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->colors = spca506_ReadI2c(gspca_dev, SAA7113_saturation);
+}
+
+static void sethue(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       spca506_Initi2c(gspca_dev);
+       spca506_WriteI2c(gspca_dev, sd->hue, SAA7113_hue);
+       spca506_WriteI2c(gspca_dev, 0x01, 0x09);
+}
+
+static void gethue(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->hue = spca506_ReadI2c(gspca_dev, SAA7113_hue);
+}
+
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->brightness = val;
+       if (gspca_dev->streaming)
+               setbrightness(gspca_dev);
+       return 0;
+}
+
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       getbrightness(gspca_dev);
+       *val = sd->brightness;
+       return 0;
+}
+
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->contrast = val;
+       if (gspca_dev->streaming)
+               setcontrast(gspca_dev);
+       return 0;
+}
+
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       getcontrast(gspca_dev);
+       *val = sd->contrast;
+       return 0;
+}
+
+static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->colors = val;
+       if (gspca_dev->streaming)
+               setcolors(gspca_dev);
+       return 0;
+}
+
+static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       getcolors(gspca_dev);
+       *val = sd->colors;
+       return 0;
+}
+
+static int sd_sethue(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->hue = val;
+       if (gspca_dev->streaming)
+               sethue(gspca_dev);
+       return 0;
+}
+
+static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       gethue(gspca_dev);
+       *val = sd->hue;
+       return 0;
+}
+
+/* sub-driver description */
+static struct sd_desc sd_desc = {
+       .name = MODULE_NAME,
+       .ctrls = sd_ctrls,
+       .nctrls = ARRAY_SIZE(sd_ctrls),
+       .config = sd_config,
+       .open = sd_open,
+       .start = sd_start,
+       .stopN = sd_stopN,
+       .stop0 = sd_stop0,
+       .close = sd_close,
+       .pkt_scan = sd_pkt_scan,
+};
+
+/* -- module initialisation -- */
+#define DVNM(name) .driver_info = (kernel_ulong_t) name
+static __devinitdata struct usb_device_id device_table[] = {
+       {USB_DEVICE(0x06e1, 0xa190), DVNM("ADS Instant VCD")},
+/*     {USB_DEVICE(0x0733, 0x0430), DVNM("UsbGrabber PV321c")}, */
+       {USB_DEVICE(0x0734, 0x043b), DVNM("3DeMon USB Capture aka")},
+       {USB_DEVICE(0x99fa, 0x8988), DVNM("Grandtec V.cap")},
+       {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+                       const struct usb_device_id *id)
+{
+       return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+                               THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+       .name = MODULE_NAME,
+       .id_table = device_table,
+       .probe = sd_probe,
+       .disconnect = gspca_disconnect,
+};
+
+/* -- module insert / remove -- */
+static int __init sd_mod_init(void)
+{
+       if (usb_register(&sd_driver) < 0)
+               return -1;
+       PDEBUG(D_PROBE, "v%s registered", version);
+       return 0;
+}
+static void __exit sd_mod_exit(void)
+{
+       usb_deregister(&sd_driver);
+       PDEBUG(D_PROBE, "deregistered");
+}
+
+module_init(sd_mod_init);
+module_exit(sd_mod_exit);
diff --git a/drivers/media/video/gspca/spca508.c b/drivers/media/video/gspca/spca508.c
new file mode 100644 (file)
index 0000000..d8cd938
--- /dev/null
@@ -0,0 +1,1791 @@
+/*
+ * SPCA508 chip based cameras subdriver
+ *
+ * V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
+ *
+ * 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
+ * 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
+ */
+
+#define MODULE_NAME "spca508"
+
+#include "gspca.h"
+
+#define DRIVER_VERSION_NUMBER  KERNEL_VERSION(2, 1, 7)
+static const char version[] = "2.1.7";
+
+MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
+MODULE_DESCRIPTION("GSPCA/SPCA508 USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* specific webcam descriptor */
+struct sd {
+       struct gspca_dev gspca_dev;             /* !! must be the first item */
+
+       int buflen;
+       unsigned char tmpbuf[352 * 288 * 3 / 2]; /* YUVY per line */
+       unsigned char tmpbuf2[352 * 288 * 2];   /* YUYV */
+
+       unsigned char brightness;
+
+       char subtype;
+#define CreativeVista 0
+#define HamaUSBSightcam 1
+#define HamaUSBSightcam2 2
+#define IntelEasyPCCamera 3
+#define MicroInnovationIC200 4
+#define ViewQuestVQ110 5
+};
+
+/* V4L2 controls supported by the driver */
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
+
+static struct ctrl sd_ctrls[] = {
+       {
+           {
+               .id      = V4L2_CID_BRIGHTNESS,
+               .type    = V4L2_CTRL_TYPE_INTEGER,
+               .name    = "Brightness",
+               .minimum = 0,
+               .maximum = 255,
+               .step    = 1,
+#define BRIGHTNESS_DEF 128
+               .default_value = BRIGHTNESS_DEF,
+           },
+           .set = sd_setbrightness,
+           .get = sd_getbrightness,
+       },
+};
+
+static struct v4l2_pix_format sif_mode[] = {
+       {160, 120, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE,
+               .bytesperline = 160 * 2,
+               .sizeimage = 160 * 120 * 2,
+               .colorspace = V4L2_COLORSPACE_SRGB,
+               .priv = 3},
+       {176, 144, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE,
+               .bytesperline = 176 * 2,
+               .sizeimage = 176 * 144 * 2,
+               .colorspace = V4L2_COLORSPACE_SRGB,
+               .priv = 2},
+       {320, 240, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE,
+               .bytesperline = 320 * 2,
+               .sizeimage = 320 * 240 * 2,
+               .colorspace = V4L2_COLORSPACE_SRGB,
+               .priv = 1},
+       {352, 288, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE,
+               .bytesperline = 352 * 2,
+               .sizeimage = 352 * 288 * 2,
+               .colorspace = V4L2_COLORSPACE_SRGB,
+               .priv = 0},
+};
+
+/* Frame packet header offsets for the spca508 */
+#define SPCA508_OFFSET_TYPE 1
+#define SPCA508_OFFSET_COMPRESS 2
+#define SPCA508_OFFSET_FRAMSEQ 8
+#define SPCA508_OFFSET_WIN1LUM 11
+#define SPCA508_OFFSET_DATA 37
+
+#define SPCA508_SNAPBIT 0x20
+#define SPCA508_SNAPCTRL 0x40
+/*************** I2c ****************/
+#define SPCA508_INDEX_I2C_BASE 0x8800
+
+/*
+ * Initialization data: this is the first set-up data written to the
+ * device (before the open data).
+ */
+static const __u16 spca508_init_data[][3] =
+#define IGN(x)                 /* nothing */
+{
+       /*  line   URB      value, index */
+       /* 44274  1804 */ {0x0000, 0x870b},
+
+       /* 44299  1805 */ {0x0020, 0x8112},
+       /* Video drop enable, ISO streaming disable */
+       /* 44324  1806 */ {0x0003, 0x8111},
+       /* Reset compression & memory */
+       /* 44349  1807 */ {0x0000, 0x8110},
+       /* Disable all outputs */
+       /* 44372  1808 */ /* READ {0x0000, 0x8114} -> 0000: 00  */
+       /* 44398  1809 */ {0x0000, 0x8114},
+       /* SW GPIO data */
+       /* 44423  1810 */ {0x0008, 0x8110},
+       /* Enable charge pump output */
+       /* 44527  1811 */ {0x0002, 0x8116},
+       /* 200 kHz pump clock */
+       /* 44555  1812 */
+               /* UNKNOWN DIRECTION (URB_FUNCTION_SELECT_INTERFACE:) */
+       /* 44590  1813 */ {0x0003, 0x8111},
+       /* Reset compression & memory */
+       /* 44615  1814 */ {0x0000, 0x8111},
+       /* Normal mode (not reset) */
+       /* 44640  1815 */ {0x0098, 0x8110},
+       /* Enable charge pump output, sync.serial,external 2x clock */
+       /* 44665  1816 */ {0x000d, 0x8114},
+       /* SW GPIO data */
+       /* 44690  1817 */ {0x0002, 0x8116},
+       /* 200 kHz pump clock */
+       /* 44715  1818 */ {0x0020, 0x8112},
+       /* Video drop enable, ISO streaming disable */
+/* --------------------------------------- */
+       /* 44740  1819 */ {0x000f, 0x8402},
+       /* memory bank */
+       /* 44765  1820 */ {0x0000, 0x8403},
+       /* ... address */
+/* --------------------------------------- */
+/* 0x88__ is Synchronous Serial Interface. */
+/* TBD: This table could be expressed more compactly */
+/* using spca508_write_i2c_vector(). */
+/* TBD: Should see if the values in spca50x_i2c_data */
+/* would work with the VQ110 instead of the values */
+/* below. */
+       /* 44790  1821 */ {0x00c0, 0x8804},
+       /* SSI slave addr */
+       /* 44815  1822 */ {0x0008, 0x8802},
+       /* 375 Khz SSI clock */
+       /* 44838  1823 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 44862  1824 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08  */
+       /* 44888  1825 */ {0x0008, 0x8802},
+       /* 375 Khz SSI clock */
+       /* 44913  1826 */ {0x0012, 0x8801},
+       /* SSI reg addr */
+       /* 44938  1827 */ {0x0080, 0x8800},
+       /* SSI data to write */
+       /* 44961  1828 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 44985  1829 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 45009  1830 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08  */
+       /* 45035  1831 */ {0x0008, 0x8802},
+       /* 375 Khz SSI clock */
+       /* 45060  1832 */ {0x0012, 0x8801},
+       /* SSI reg addr */
+       /* 45085  1833 */ {0x0000, 0x8800},
+       /* SSI data to write */
+       /* 45108  1834 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 45132  1835 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 45156  1836 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08  */
+       /* 45182  1837 */ {0x0008, 0x8802},
+       /* 375 Khz SSI clock */
+       /* 45207  1838 */ {0x0011, 0x8801},
+       /* SSI reg addr */
+       /* 45232  1839 */ {0x0040, 0x8800},
+       /* SSI data to write */
+       /* 45255  1840 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 45279  1841 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 45303  1842 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08  */
+       /* 45329  1843 */ {0x0008, 0x8802},
+       /* 45354  1844 */ {0x0013, 0x8801},
+       /* 45379  1845 */ {0x0000, 0x8800},
+       /* 45402  1846 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 45426  1847 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 45450  1848 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08  */
+       /* 45476  1849 */ {0x0008, 0x8802},
+       /* 45501  1850 */ {0x0014, 0x8801},
+       /* 45526  1851 */ {0x0000, 0x8800},
+       /* 45549  1852 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 45573  1853 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 45597  1854 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08  */
+       /* 45623  1855 */ {0x0008, 0x8802},
+       /* 45648  1856 */ {0x0015, 0x8801},
+       /* 45673  1857 */ {0x0001, 0x8800},
+       /* 45696  1858 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 45720  1859 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 45744  1860 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08  */
+       /* 45770  1861 */ {0x0008, 0x8802},
+       /* 45795  1862 */ {0x0016, 0x8801},
+       /* 45820  1863 */ {0x0003, 0x8800},
+       /* 45843  1864 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 45867  1865 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 45891  1866 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08  */
+       /* 45917  1867 */ {0x0008, 0x8802},
+       /* 45942  1868 */ {0x0017, 0x8801},
+       /* 45967  1869 */ {0x0036, 0x8800},
+       /* 45990  1870 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 46014  1871 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 46038  1872 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08  */
+       /* 46064  1873 */ {0x0008, 0x8802},
+       /* 46089  1874 */ {0x0018, 0x8801},
+       /* 46114  1875 */ {0x00ec, 0x8800},
+       /* 46137  1876 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 46161  1877 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 46185  1878 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08  */
+       /* 46211  1879 */ {0x0008, 0x8802},
+       /* 46236  1880 */ {0x001a, 0x8801},
+       /* 46261  1881 */ {0x0094, 0x8800},
+       /* 46284  1882 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 46308  1883 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 46332  1884 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08  */
+       /* 46358  1885 */ {0x0008, 0x8802},
+       /* 46383  1886 */ {0x001b, 0x8801},
+       /* 46408  1887 */ {0x0000, 0x8800},
+       /* 46431  1888 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 46455  1889 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 46479  1890 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08  */
+       /* 46505  1891 */ {0x0008, 0x8802},
+       /* 46530  1892 */ {0x0027, 0x8801},
+       /* 46555  1893 */ {0x00a2, 0x8800},
+       /* 46578  1894 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 46602  1895 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 46626  1896 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08  */
+       /* 46652  1897 */ {0x0008, 0x8802},
+       /* 46677  1898 */ {0x0028, 0x8801},
+       /* 46702  1899 */ {0x0040, 0x8800},
+       /* 46725  1900 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 46749  1901 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 46773  1902 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08  */
+       /* 46799  1903 */ {0x0008, 0x8802},
+       /* 46824  1904 */ {0x002a, 0x8801},
+       /* 46849  1905 */ {0x0084, 0x8800},
+       /* 46872  1906 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 46896  1907 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */
+       /* 46920  1908 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08  */
+       /* 46946  1909 */ {0x0008, 0x8802},
+       /* 46971  1910 */ {0x002b, 0x8801},
+       /* 46996  1911 */ {0x00a8, 0x8800},
+       /* 47019  1912 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 47043  1913 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 47067  1914 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08  */
+       /* 47093  1915 */ {0x0008, 0x8802},
+       /* 47118  1916 */ {0x002c, 0x8801},
+       /* 47143  1917 */ {0x00fe, 0x8800},
+       /* 47166  1918 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 47190  1919 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 47214  1920 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08  */
+       /* 47240  1921 */ {0x0008, 0x8802},
+       /* 47265  1922 */ {0x002d, 0x8801},
+       /* 47290  1923 */ {0x0003, 0x8800},
+       /* 47313  1924 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 47337  1925 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 47361  1926 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08  */
+       /* 47387  1927 */ {0x0008, 0x8802},
+       /* 47412  1928 */ {0x0038, 0x8801},
+       /* 47437  1929 */ {0x0083, 0x8800},
+       /* 47460  1930 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 47484  1931 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 47508  1932 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08  */
+       /* 47534  1933 */ {0x0008, 0x8802},
+       /* 47559  1934 */ {0x0033, 0x8801},
+       /* 47584  1935 */ {0x0081, 0x8800},
+       /* 47607  1936 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 47631  1937 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 47655  1938 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08  */
+       /* 47681  1939 */ {0x0008, 0x8802},
+       /* 47706  1940 */ {0x0034, 0x8801},
+       /* 47731  1941 */ {0x004a, 0x8800},
+       /* 47754  1942 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 47778  1943 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 47802  1944 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08  */
+       /* 47828  1945 */ {0x0008, 0x8802},
+       /* 47853  1946 */ {0x0039, 0x8801},
+       /* 47878  1947 */ {0x0000, 0x8800},
+       /* 47901  1948 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 47925  1949 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 47949  1950 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08  */
+       /* 47975  1951 */ {0x0008, 0x8802},
+       /* 48000  1952 */ {0x0010, 0x8801},
+       /* 48025  1953 */ {0x00a8, 0x8800},
+       /* 48048  1954 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 48072  1955 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 48096  1956 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08  */
+       /* 48122  1957 */ {0x0008, 0x8802},
+       /* 48147  1958 */ {0x0006, 0x8801},
+       /* 48172  1959 */ {0x0058, 0x8800},
+       /* 48195  1960 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 48219  1961 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */
+       /* 48243  1962 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08  */
+       /* 48269  1963 */ {0x0008, 0x8802},
+       /* 48294  1964 */ {0x0000, 0x8801},
+       /* 48319  1965 */ {0x0004, 0x8800},
+       /* 48342  1966 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 48366  1967 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 48390  1968 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08  */
+       /* 48416  1969 */ {0x0008, 0x8802},
+       /* 48441  1970 */ {0x0040, 0x8801},
+       /* 48466  1971 */ {0x0080, 0x8800},
+       /* 48489  1972 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 48513  1973 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 48537  1974 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08  */
+       /* 48563  1975 */ {0x0008, 0x8802},
+       /* 48588  1976 */ {0x0041, 0x8801},
+       /* 48613  1977 */ {0x000c, 0x8800},
+       /* 48636  1978 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 48660  1979 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 48684  1980 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08  */
+       /* 48710  1981 */ {0x0008, 0x8802},
+       /* 48735  1982 */ {0x0042, 0x8801},
+       /* 48760  1983 */ {0x000c, 0x8800},
+       /* 48783  1984 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 48807  1985 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 48831  1986 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08  */
+       /* 48857  1987 */ {0x0008, 0x8802},
+       /* 48882  1988 */ {0x0043, 0x8801},
+       /* 48907  1989 */ {0x0028, 0x8800},
+       /* 48930  1990 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 48954  1991 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 48978  1992 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08  */
+       /* 49004  1993 */ {0x0008, 0x8802},
+       /* 49029  1994 */ {0x0044, 0x8801},
+       /* 49054  1995 */ {0x0080, 0x8800},
+       /* 49077  1996 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 49101  1997 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 49125  1998 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08  */
+       /* 49151  1999 */ {0x0008, 0x8802},
+       /* 49176  2000 */ {0x0045, 0x8801},
+       /* 49201  2001 */ {0x0020, 0x8800},
+       /* 49224  2002 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 49248  2003 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 49272  2004 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08  */
+       /* 49298  2005 */ {0x0008, 0x8802},
+       /* 49323  2006 */ {0x0046, 0x8801},
+       /* 49348  2007 */ {0x0020, 0x8800},
+       /* 49371  2008 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 49395  2009 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 49419  2010 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08  */
+       /* 49445  2011 */ {0x0008, 0x8802},
+       /* 49470  2012 */ {0x0047, 0x8801},
+       /* 49495  2013 */ {0x0080, 0x8800},
+       /* 49518  2014 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 49542  2015 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 49566  2016 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08  */
+       /* 49592  2017 */ {0x0008, 0x8802},
+       /* 49617  2018 */ {0x0048, 0x8801},
+       /* 49642  2019 */ {0x004c, 0x8800},
+       /* 49665  2020 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 49689  2021 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 49713  2022 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08  */
+       /* 49739  2023 */ {0x0008, 0x8802},
+       /* 49764  2024 */ {0x0049, 0x8801},
+       /* 49789  2025 */ {0x0084, 0x8800},
+       /* 49812  2026 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 49836  2027 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 49860  2028 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08  */
+       /* 49886  2029 */ {0x0008, 0x8802},
+       /* 49911  2030 */ {0x004a, 0x8801},
+       /* 49936  2031 */ {0x0084, 0x8800},
+       /* 49959  2032 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 49983  2033 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 50007  2034 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08  */
+       /* 50033  2035 */ {0x0008, 0x8802},
+       /* 50058  2036 */ {0x004b, 0x8801},
+       /* 50083  2037 */ {0x0084, 0x8800},
+       /* 50106  2038 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* --------------------------------------- */
+       /* 50132  2039 */ {0x0012, 0x8700},
+       /* Clock speed 48Mhz/(2+2)/2= 6 Mhz */
+       /* 50157  2040 */ {0x0000, 0x8701},
+       /* CKx1 clock delay adj */
+       /* 50182  2041 */ {0x0000, 0x8701},
+       /* CKx1 clock delay adj */
+       /* 50207  2042 */ {0x0001, 0x870c},
+       /* CKOx2 output */
+       /* --------------------------------------- */
+       /* 50232  2043 */ {0x0080, 0x8600},
+       /* Line memory read counter (L) */
+       /* 50257  2044 */ {0x0001, 0x8606},
+       /* reserved */
+       /* 50282  2045 */ {0x0064, 0x8607},
+       /* Line memory read counter (H) 0x6480=25,728 */
+       /* 50307  2046 */ {0x002a, 0x8601},
+       /* CDSP sharp interpolation mode,
+        *                      line sel for color sep, edge enhance enab */
+       /* 50332  2047 */ {0x0000, 0x8602},
+       /* optical black level for user settng = 0 */
+       /* 50357  2048 */ {0x0080, 0x8600},
+       /* Line memory read counter (L) */
+       /* 50382  2049 */ {0x000a, 0x8603},
+       /* optical black level calc mode: auto; optical black offset = 10 */
+       /* 50407  2050 */ {0x00df, 0x865b},
+       /* Horiz offset for valid pixels (L)=0xdf */
+       /* 50432  2051 */ {0x0012, 0x865c},
+       /* Vert offset for valid lines (L)=0x12 */
+
+/* The following two lines seem to be the "wrong" resolution. */
+/* But perhaps these indicate the actual size of the sensor */
+/* rather than the size of the current video mode. */
+       /* 50457  2052 */ {0x0058, 0x865d},
+       /* Horiz valid pixels (*4) (L) = 352 */
+       /* 50482  2053 */ {0x0048, 0x865e},
+       /* Vert valid lines (*4) (L) = 288 */
+
+       /* 50507  2054 */ {0x0015, 0x8608},
+       /* A11 Coef ... */
+       /* 50532  2055 */ {0x0030, 0x8609},
+       /* 50557  2056 */ {0x00fb, 0x860a},
+       /* 50582  2057 */ {0x003e, 0x860b},
+       /* 50607  2058 */ {0x00ce, 0x860c},
+       /* 50632  2059 */ {0x00f4, 0x860d},
+       /* 50657  2060 */ {0x00eb, 0x860e},
+       /* 50682  2061 */ {0x00dc, 0x860f},
+       /* 50707  2062 */ {0x0039, 0x8610},
+       /* 50732  2063 */ {0x0001, 0x8611},
+       /* R offset for white balance ... */
+       /* 50757  2064 */ {0x0000, 0x8612},
+       /* 50782  2065 */ {0x0001, 0x8613},
+       /* 50807  2066 */ {0x0000, 0x8614},
+       /* 50832  2067 */ {0x005b, 0x8651},
+       /* R gain for white balance ... */
+       /* 50857  2068 */ {0x0040, 0x8652},
+       /* 50882  2069 */ {0x0060, 0x8653},
+       /* 50907  2070 */ {0x0040, 0x8654},
+       /* 50932  2071 */ {0x0000, 0x8655},
+       /* 50957  2072 */ {0x0001, 0x863f},
+       /* Fixed gamma correction enable, USB control,
+        *                       lum filter disable, lum noise clip disable */
+       /* 50982  2073 */ {0x00a1, 0x8656},
+       /* Window1 size 256x256, Windows2 size 64x64,
+        *               gamma look-up disable, new edge enhancement enable */
+       /* 51007  2074 */ {0x0018, 0x8657},
+       /* Edge gain high thresh */
+       /* 51032  2075 */ {0x0020, 0x8658},
+       /* Edge gain low thresh */
+       /* 51057  2076 */ {0x000a, 0x8659},
+       /* Edge bandwidth high threshold */
+       /* 51082  2077 */ {0x0005, 0x865a},
+       /* Edge bandwidth low threshold */
+       /* -------------------------------- */
+       /* 51107  2078 */ {0x0030, 0x8112},
+       /* Video drop enable, ISO streaming enable */
+       /* 51130  2079 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 51154  2080 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08  */
+       /* 51180  2081 */ {0xa908, 0x8802},
+       /* 51205  2082 */ {0x0034, 0x8801},
+       /* SSI reg addr */
+       /* 51230  2083 */ {0x00ca, 0x8800},
+       /* SSI data to write */
+       /* 51253  2084 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 51277  2085 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 51301  2086 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08  */
+       /* 51327  2087 */ {0x1f08, 0x8802},
+       /* 51352  2088 */ {0x0006, 0x8801},
+       /* 51377  2089 */ {0x0080, 0x8800},
+       /* 51400  2090 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+
+/* ----- Read back coefs we wrote earlier. */
+       /* 51424  2091 */ /* READ { 0, 0x0000, 0x8608 } -> 0000: 15  */
+       /* 51448  2092 */ /* READ { 0, 0x0000, 0x8609 } -> 0000: 30  */
+       /* 51472  2093 */ /* READ { 0, 0x0000, 0x860a } -> 0000: fb  */
+       /* 51496  2094 */ /* READ { 0, 0x0000, 0x860b } -> 0000: 3e  */
+       /* 51520  2095 */ /* READ { 0, 0x0000, 0x860c } -> 0000: ce  */
+       /* 51544  2096 */ /* READ { 0, 0x0000, 0x860d } -> 0000: f4  */
+       /* 51568  2097 */ /* READ { 0, 0x0000, 0x860e } -> 0000: eb  */
+       /* 51592  2098 */ /* READ { 0, 0x0000, 0x860f } -> 0000: dc  */
+       /* 51616  2099 */ /* READ { 0, 0x0000, 0x8610 } -> 0000: 39  */
+       /* 51640  2100 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 51664  2101 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08  */
+       /* 51690  2102 */ {0xb008, 0x8802},
+       /* 51715  2103 */ {0x0006, 0x8801},
+       /* 51740  2104 */ {0x007d, 0x8800},
+       /* 51763  2105 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+
+
+       /* This chunk is seemingly redundant with */
+       /* earlier commands (A11 Coef...), but if I disable it, */
+       /* the image appears too dark.  Maybe there was some kind of */
+       /* reset since the earlier commands, so this is necessary again. */
+       /* 51789  2106 */ {0x0015, 0x8608},
+       /* 51814  2107 */ {0x0030, 0x8609},
+       /* 51839  2108 */ {0xfffb, 0x860a},
+       /* 51864  2109 */ {0x003e, 0x860b},
+       /* 51889  2110 */ {0xffce, 0x860c},
+       /* 51914  2111 */ {0xfff4, 0x860d},
+       /* 51939  2112 */ {0xffeb, 0x860e},
+       /* 51964  2113 */ {0xffdc, 0x860f},
+       /* 51989  2114 */ {0x0039, 0x8610},
+       /* 52014  2115 */ {0x0018, 0x8657},
+
+       /* 52039  2116 */ {0x0000, 0x8508},
+       /* Disable compression. */
+       /* Previous line was:
+        * 52039  2116 *  { 0, 0x0021, 0x8508 },  * Enable compression. */
+       /* 52064  2117 */ {0x0032, 0x850b},
+       /* compression stuff */
+       /* 52089  2118 */ {0x0003, 0x8509},
+       /* compression stuff */
+       /* 52114  2119 */ {0x0011, 0x850a},
+       /* compression stuff */
+       /* 52139  2120 */ {0x0021, 0x850d},
+       /* compression stuff */
+       /* 52164  2121 */ {0x0010, 0x850c},
+       /* compression stuff */
+       /* 52189  2122 */ {0x0003, 0x8500},
+       /* *** Video mode: 160x120 */
+       /* 52214  2123 */ {0x0001, 0x8501},
+       /* Hardware-dominated snap control */
+       /* 52239  2124 */ {0x0061, 0x8656},
+       /* Window1 size 128x128, Windows2 size 128x128,
+        *              gamma look-up disable, new edge enhancement enable */
+       /* 52264  2125 */ {0x0018, 0x8617},
+       /* Window1 start X (*2) */
+       /* 52289  2126 */ {0x0008, 0x8618},
+       /* Window1 start Y (*2) */
+       /* 52314  2127 */ {0x0061, 0x8656},
+       /* Window1 size 128x128, Windows2 size 128x128,
+        *              gamma look-up disable, new edge enhancement enable */
+       /* 52339  2128 */ {0x0058, 0x8619},
+       /* Window2 start X (*2) */
+       /* 52364  2129 */ {0x0008, 0x861a},
+       /* Window2 start Y (*2) */
+       /* 52389  2130 */ {0x00ff, 0x8615},
+       /* High lum thresh for white balance */
+       /* 52414  2131 */ {0x0000, 0x8616},
+       /* Low lum thresh for white balance */
+       /* 52439  2132 */ {0x0012, 0x8700},
+       /* Clock speed 48Mhz/(2+2)/2= 6 Mhz */
+       /* 52464  2133 */ {0x0012, 0x8700},
+       /* Clock speed 48Mhz/(2+2)/2= 6 Mhz */
+       /* 52487  2134 */ /* READ { 0, 0x0000, 0x8656 } -> 0000: 61  */
+       /* 52513  2135 */ {0x0028, 0x8802},
+       /* 375 Khz SSI clock, SSI r/w sync with VSYNC */
+       /* 52536  2136 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 52560  2137 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 28  */
+       /* 52586  2138 */ {0x1f28, 0x8802},
+       /* 375 Khz SSI clock, SSI r/w sync with VSYNC */
+       /* 52611  2139 */ {0x0010, 0x8801},
+       /* SSI reg addr */
+       /* 52636  2140 */ {0x003e, 0x8800},
+       /* SSI data to write */
+       /* 52659  2141 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 52685  2142 */ {0x0028, 0x8802},
+       /* 52708  2143 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 52732  2144 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 28  */
+       /* 52758  2145 */ {0x1f28, 0x8802},
+       /* 52783  2146 */ {0x0000, 0x8801},
+       /* 52808  2147 */ {0x001f, 0x8800},
+       /* 52831  2148 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 52857  2149 */ {0x0001, 0x8602},
+       /* optical black level for user settning = 1 */
+
+       /* Original: */
+       /* 52882  2150 */ {0x0023, 0x8700},
+       /* Clock speed 48Mhz/(3+2)/4= 2.4 Mhz */
+       /* 52907  2151 */ {0x000f, 0x8602},
+       /* optical black level for user settning = 15 */
+
+       /* 52932  2152 */ {0x0028, 0x8802},
+       /* 52955  2153 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 52979  2154 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 28  */
+       /* 53005  2155 */ {0x1f28, 0x8802},
+       /* 53030  2156 */ {0x0010, 0x8801},
+       /* 53055  2157 */ {0x007b, 0x8800},
+       /* 53078  2158 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00  */
+       /* 53104  2159 */ {0x002f, 0x8651},
+       /* R gain for white balance ... */
+       /* 53129  2160 */ {0x0080, 0x8653},
+       /* 53152  2161 */ /* READ { 0, 0x0000, 0x8655 } -> 0000: 00  */
+       /* 53178  2162 */ {0x0000, 0x8655},
+
+       /* 53203  2163 */ {0x0030, 0x8112},
+       /* Video drop enable, ISO streaming enable */
+       /* 53228  2164 */ {0x0020, 0x8112},
+       /* Video drop enable, ISO streaming disable */
+       /* 53252  2165 */
+            /* UNKNOWN DIRECTION (URB_FUNCTION_SELECT_INTERFACE: (ALT=0) ) */
+       {}
+};
+
+
+/*
+ * Initialization data for Intel EasyPC Camera CS110
+ */
+static const __u16 spca508cs110_init_data[][3] = {
+       {0x0000, 0x870b}, /* Reset CTL3 */
+       {0x0003, 0x8111}, /* Soft Reset compression, memory, TG & CDSP */
+       {0x0000, 0x8111}, /* Normal operation on reset */
+       {0x0090, 0x8110},
+                /* External Clock 2x & Synchronous Serial Interface Output */
+       {0x0020, 0x8112}, /* Video Drop packet enable */
+       {0x0000, 0x8114}, /* Software GPIO output data */
+       {0x0001, 0x8114},
+       {0x0001, 0x8114},
+       {0x0001, 0x8114},
+       {0x0003, 0x8114},
+
+       /* Initial sequence Synchronous Serial Interface */
+       {0x000f, 0x8402}, /* Memory bank Address */
+       {0x0000, 0x8403}, /* Memory bank Address */
+       {0x00ba, 0x8804}, /* SSI Slave address */
+       {0x0010, 0x8802}, /* 93.75kHz SSI Clock Two DataByte */
+       {0x0010, 0x8802}, /* 93.75kHz SSI Clock two DataByte */
+
+       {0x0001, 0x8801},
+       {0x000a, 0x8805},/* a - NWG: Dunno what this is about */
+       {0x0000, 0x8800},
+       {0x0010, 0x8802},
+
+       {0x0002, 0x8801},
+       {0x0000, 0x8805},
+       {0x0000, 0x8800},
+       {0x0010, 0x8802},
+
+       {0x0003, 0x8801},
+       {0x0027, 0x8805},
+       {0x0001, 0x8800},
+       {0x0010, 0x8802},
+
+       {0x0004, 0x8801},
+       {0x0065, 0x8805},
+       {0x0001, 0x8800},
+       {0x0010, 0x8802},
+
+       {0x0005, 0x8801},
+       {0x0003, 0x8805},
+       {0x0000, 0x8800},
+       {0x0010, 0x8802},
+
+       {0x0006, 0x8801},
+       {0x001c, 0x8805},
+       {0x0000, 0x8800},
+       {0x0010, 0x8802},
+
+       {0x0007, 0x8801},
+       {0x002a, 0x8805},
+       {0x0000, 0x8800},
+       {0x0010, 0x8802},
+
+       {0x0002, 0x8704}, /* External input CKIx1 */
+       {0x0001, 0x8606}, /* 1 Line memory Read Counter (H) Result: (d)410 */
+       {0x009a, 0x8600}, /* Line memory Read Counter (L) */
+       {0x0001, 0x865b}, /* 1 Horizontal Offset for Valid Pixel(L) */
+       {0x0003, 0x865c}, /* 3 Vertical Offset for Valid Lines(L) */
+       {0x0058, 0x865d}, /* 58 Horizontal Valid Pixel Window(L) */
+
+       {0x0006, 0x8660}, /* Nibble data + input order */
+
+       {0x000a, 0x8602}, /* Optical black level set to 0x0a */
+/* 1945 */ {0x0000, 0x8603}, /* Optical black level Offset */
+
+/* 1962 *  {0, 0x0000, 0x8611},  * 0 R  Offset for white Balance */
+/* 1963 *  {0, 0x0000, 0x8612},  * 1 Gr Offset for white Balance */
+/* 1964 *  {0, 0x0000, 0x8613},  * 1f B  Offset for white Balance */
+/* 1965 *  {0, 0x0000, 0x8614},  * f0 Gb Offset for white Balance */
+
+       {0x0040, 0x8651}, /* 2b BLUE gain for white balance  good at all 60 */
+       {0x0030, 0x8652}, /* 41 Gr Gain for white Balance (L) */
+       {0x0035, 0x8653}, /* 26 RED gain for white balance */
+       {0x0035, 0x8654}, /* 40Gb Gain for white Balance (L) */
+       {0x0041, 0x863f},
+             /* Fixed Gamma correction enabled (makes colours look better) */
+
+/* 2422 */ {0x0000, 0x8655},
+       /* High bits for white balance*****brightness control*** */
+       {}
+};
+
+static const __u16 spca508_sightcam_init_data[][3] = {
+/* This line seems to setup the frame/canvas */
+       /*368  */ {0x000f, 0x8402},
+
+/* Theese 6 lines are needed to startup the webcam */
+       /*398  */ {0x0090, 0x8110},
+       /*399  */ {0x0001, 0x8114},
+       /*400  */ {0x0001, 0x8114},
+       /*401  */ {0x0001, 0x8114},
+       /*402  */ {0x0003, 0x8114},
+       /*403  */ {0x0080, 0x8804},
+
+/* This part seems to make the pictures darker? (autobrightness?) */
+       /*436  */ {0x0001, 0x8801},
+       /*437  */ {0x0004, 0x8800},
+       /*439  */ {0x0003, 0x8801},
+       /*440  */ {0x00e0, 0x8800},
+       /*442  */ {0x0004, 0x8801},
+       /*443  */ {0x00b4, 0x8800},
+       /*445  */ {0x0005, 0x8801},
+       /*446  */ {0x0000, 0x8800},
+
+       /*448  */ {0x0006, 0x8801},
+       /*449  */ {0x00e0, 0x8800},
+       /*451  */ {0x0007, 0x8801},
+       /*452  */ {0x000c, 0x8800},
+
+/* This section is just needed, it probably
+ * does something like the previous section,
+ * but the cam won't start if it's not included.
+ */
+       /*484  */ {0x0014, 0x8801},
+       /*485  */ {0x0008, 0x8800},
+       /*487  */ {0x0015, 0x8801},
+       /*488  */ {0x0067, 0x8800},
+       /*490  */ {0x0016, 0x8801},
+       /*491  */ {0x0000, 0x8800},
+       /*493  */ {0x0017, 0x8801},
+       /*494  */ {0x0020, 0x8800},
+       /*496  */ {0x0018, 0x8801},
+       /*497  */ {0x0044, 0x8800},
+
+/* Makes the picture darker - and the
+ * cam won't start if not included
+ */
+       /*505  */ {0x001e, 0x8801},
+       /*506  */ {0x00ea, 0x8800},
+       /*508  */ {0x001f, 0x8801},
+       /*509  */ {0x0001, 0x8800},
+       /*511  */ {0x0003, 0x8801},
+       /*512  */ {0x00e0, 0x8800},
+
+/* seems to place the colors ontop of each other #1 */
+       /*517  */ {0x0006, 0x8704},
+       /*518  */ {0x0001, 0x870c},
+       /*519  */ {0x0016, 0x8600},
+       /*520  */ {0x0002, 0x8606},
+
+/* if not included the pictures becomes _very_ dark */
+       /*521  */ {0x0064, 0x8607},
+       /*522  */ {0x003a, 0x8601},
+       /*523  */ {0x0000, 0x8602},
+
+/* seems to place the colors ontop of each other #2 */
+       /*524  */ {0x0016, 0x8600},
+       /*525  */ {0x0018, 0x8617},
+       /*526  */ {0x0008, 0x8618},
+       /*527  */ {0x00a1, 0x8656},
+
+/* webcam won't start if not included */
+       /*528  */ {0x0007, 0x865b},
+       /*529  */ {0x0001, 0x865c},
+       /*530  */ {0x0058, 0x865d},
+       /*531  */ {0x0048, 0x865e},
+
+/* adjusts the colors */
+       /*541  */ {0x0049, 0x8651},
+       /*542  */ {0x0040, 0x8652},
+       /*543  */ {0x004c, 0x8653},
+       /*544  */ {0x0040, 0x8654},
+       {}
+};
+
+static const __u16 spca508_sightcam2_init_data[][3] = {
+/* 35 */ {0x0020, 0x8112},
+
+/* 36 */ {0x000f, 0x8402},
+/* 37 */ {0x0000, 0x8403},
+
+/* 38 */ {0x0008, 0x8201},
+/* 39 */ {0x0008, 0x8200},
+/* 40 */ {0x0001, 0x8200},
+/* 43 */ {0x0009, 0x8201},
+/* 44 */ {0x0008, 0x8200},
+/* 45 */ {0x0001, 0x8200},
+/* 48 */ {0x000a, 0x8201},
+/* 49 */ {0x0008, 0x8200},
+/* 50 */ {0x0001, 0x8200},
+/* 53 */ {0x000b, 0x8201},
+/* 54 */ {0x0008, 0x8200},
+/* 55 */ {0x0001, 0x8200},
+/* 58 */ {0x000c, 0x8201},
+/* 59 */ {0x0008, 0x8200},
+/* 60 */ {0x0001, 0x8200},
+/* 63 */ {0x000d, 0x8201},
+/* 64 */ {0x0008, 0x8200},
+/* 65 */ {0x0001, 0x8200},
+/* 68 */ {0x000e, 0x8201},
+/* 69 */ {0x0008, 0x8200},
+/* 70 */ {0x0001, 0x8200},
+/* 73 */ {0x0007, 0x8201},
+/* 74 */ {0x0008, 0x8200},
+/* 75 */ {0x0001, 0x8200},
+/* 78 */ {0x000f, 0x8201},
+/* 79 */ {0x0008, 0x8200},
+/* 80 */ {0x0001, 0x8200},
+
+/* 84 */ {0x0018, 0x8660},
+/* 85 */ {0x0010, 0x8201},
+
+/* 86 */ {0x0008, 0x8200},
+/* 87 */ {0x0001, 0x8200},
+/* 90 */ {0x0011, 0x8201},
+/* 91 */ {0x0008, 0x8200},
+/* 92 */ {0x0001, 0x8200},
+
+/* 95 */ {0x0000, 0x86b0},
+/* 96 */ {0x0034, 0x86b1},
+/* 97 */ {0x0000, 0x86b2},
+/* 98 */ {0x0049, 0x86b3},
+/* 99 */ {0x0000, 0x86b4},
+/* 100 */ {0x0000, 0x86b4},
+
+/* 101 */ {0x0012, 0x8201},
+/* 102 */ {0x0008, 0x8200},
+/* 103 */ {0x0001, 0x8200},
+/* 106 */ {0x0013, 0x8201},
+/* 107 */ {0x0008, 0x8200},
+/* 108 */ {0x0001, 0x8200},
+
+/* 111 */ {0x0001, 0x86b0},
+/* 112 */ {0x00aa, 0x86b1},
+/* 113 */ {0x0000, 0x86b2},
+/* 114 */ {0x00e4, 0x86b3},
+/* 115 */ {0x0000, 0x86b4},
+/* 116 */ {0x0000, 0x86b4},
+
+/* 118 */ {0x0018, 0x8660},
+
+/* 119 */ {0x0090, 0x8110},
+/* 120 */ {0x0001, 0x8114},
+/* 121 */ {0x0001, 0x8114},
+/* 122 */ {0x0001, 0x8114},
+/* 123 */ {0x0003, 0x8114},
+
+/* 124 */ {0x0080, 0x8804},
+/* 157 */ {0x0003, 0x8801},
+/* 158 */ {0x0012, 0x8800},
+/* 160 */ {0x0004, 0x8801},
+/* 161 */ {0x0005, 0x8800},
+/* 163 */ {0x0005, 0x8801},
+/* 164 */ {0x0000, 0x8800},
+/* 166 */ {0x0006, 0x8801},
+/* 167 */ {0x0000, 0x8800},
+/* 169 */ {0x0007, 0x8801},
+/* 170 */ {0x0000, 0x8800},
+/* 172 */ {0x0008, 0x8801},
+/* 173 */ {0x0005, 0x8800},
+/* 175 */ {0x000a, 0x8700},
+/* 176 */ {0x000e, 0x8801},
+/* 177 */ {0x0004, 0x8800},
+/* 179 */ {0x0005, 0x8801},
+/* 180 */ {0x0047, 0x8800},
+/* 182 */ {0x0006, 0x8801},
+/* 183 */ {0x0000, 0x8800},
+/* 185 */ {0x0007, 0x8801},
+/* 186 */ {0x00c0, 0x8800},
+/* 188 */ {0x0008, 0x8801},
+/* 189 */ {0x0003, 0x8800},
+/* 191 */ {0x0013, 0x8801},
+/* 192 */ {0x0001, 0x8800},
+/* 194 */ {0x0009, 0x8801},
+/* 195 */ {0x0000, 0x8800},
+/* 197 */ {0x000a, 0x8801},
+/* 198 */ {0x0000, 0x8800},
+/* 200 */ {0x000b, 0x8801},
+/* 201 */ {0x0000, 0x8800},
+/* 203 */ {0x000c, 0x8801},
+/* 204 */ {0x0000, 0x8800},
+/* 206 */ {0x000e, 0x8801},
+/* 207 */ {0x0004, 0x8800},
+/* 209 */ {0x000f, 0x8801},
+/* 210 */ {0x0000, 0x8800},
+/* 212 */ {0x0010, 0x8801},
+/* 213 */ {0x0006, 0x8800},
+/* 215 */ {0x0011, 0x8801},
+/* 216 */ {0x0006, 0x8800},
+/* 218 */ {0x0012, 0x8801},
+/* 219 */ {0x0000, 0x8800},
+/* 221 */ {0x0013, 0x8801},
+/* 222 */ {0x0001, 0x8800},
+
+/* 224 */ {0x000a, 0x8700},
+/* 225 */ {0x0000, 0x8702},
+/* 226 */ {0x0000, 0x8703},
+/* 227 */ {0x00c2, 0x8704},
+/* 228 */ {0x0001, 0x870c},
+
+/* 229 */ {0x0044, 0x8600},
+/* 230 */ {0x0002, 0x8606},
+/* 231 */ {0x0064, 0x8607},
+/* 232 */ {0x003a, 0x8601},
+/* 233 */ {0x0008, 0x8602},
+/* 234 */ {0x0044, 0x8600},
+/* 235 */ {0x0018, 0x8617},
+/* 236 */ {0x0008, 0x8618},
+/* 237 */ {0x00a1, 0x8656},
+/* 238 */ {0x0004, 0x865b},
+/* 239 */ {0x0002, 0x865c},
+/* 240 */ {0x0058, 0x865d},
+/* 241 */ {0x0048, 0x865e},
+/* 242 */ {0x0012, 0x8608},
+/* 243 */ {0x002c, 0x8609},
+/* 244 */ {0x0002, 0x860a},
+/* 245 */ {0x002c, 0x860b},
+/* 246 */ {0x00db, 0x860c},
+/* 247 */ {0x00f9, 0x860d},
+/* 248 */ {0x00f1, 0x860e},
+/* 249 */ {0x00e3, 0x860f},
+/* 250 */ {0x002c, 0x8610},
+/* 251 */ {0x006c, 0x8651},
+/* 252 */ {0x0041, 0x8652},
+/* 253 */ {0x0059, 0x8653},
+/* 254 */ {0x0040, 0x8654},
+/* 255 */ {0x00fa, 0x8611},
+/* 256 */ {0x00ff, 0x8612},
+/* 257 */ {0x00f8, 0x8613},
+/* 258 */ {0x0000, 0x8614},
+/* 259 */ {0x0001, 0x863f},
+/* 260 */ {0x0000, 0x8640},
+/* 261 */ {0x0026, 0x8641},
+/* 262 */ {0x0045, 0x8642},
+/* 263 */ {0x0060, 0x8643},
+/* 264 */ {0x0075, 0x8644},
+/* 265 */ {0x0088, 0x8645},
+/* 266 */ {0x009b, 0x8646},
+/* 267 */ {0x00b0, 0x8647},
+/* 268 */ {0x00c5, 0x8648},
+/* 269 */ {0x00d2, 0x8649},
+/* 270 */ {0x00dc, 0x864a},
+/* 271 */ {0x00e5, 0x864b},
+/* 272 */ {0x00eb, 0x864c},
+/* 273 */ {0x00f0, 0x864d},
+/* 274 */ {0x00f6, 0x864e},
+/* 275 */ {0x00fa, 0x864f},
+/* 276 */ {0x00ff, 0x8650},
+/* 277 */ {0x0060, 0x8657},
+/* 278 */ {0x0010, 0x8658},
+/* 279 */ {0x0018, 0x8659},
+/* 280 */ {0x0005, 0x865a},
+/* 281 */ {0x0018, 0x8660},
+/* 282 */ {0x0003, 0x8509},
+/* 283 */ {0x0011, 0x850a},
+/* 284 */ {0x0032, 0x850b},
+/* 285 */ {0x0010, 0x850c},
+/* 286 */ {0x0021, 0x850d},
+/* 287 */ {0x0001, 0x8500},
+/* 288 */ {0x0000, 0x8508},
+/* 289 */ {0x0012, 0x8608},
+/* 290 */ {0x002c, 0x8609},
+/* 291 */ {0x0002, 0x860a},
+/* 292 */ {0x0039, 0x860b},
+/* 293 */ {0x00d0, 0x860c},
+/* 294 */ {0x00f7, 0x860d},
+/* 295 */ {0x00ed, 0x860e},
+/* 296 */ {0x00db, 0x860f},
+/* 297 */ {0x0039, 0x8610},
+/* 298 */ {0x0012, 0x8657},
+/* 299 */ {0x000c, 0x8619},
+/* 300 */ {0x0004, 0x861a},
+/* 301 */ {0x00a1, 0x8656},
+/* 302 */ {0x00c8, 0x8615},
+/* 303 */ {0x0032, 0x8616},
+
+/* 306 */ {0x0030, 0x8112},
+/* 313 */ {0x0020, 0x8112},
+/* 314 */ {0x0020, 0x8112},
+/* 315 */ {0x000f, 0x8402},
+/* 316 */ {0x0000, 0x8403},
+
+/* 317 */ {0x0090, 0x8110},
+/* 318 */ {0x0001, 0x8114},
+/* 319 */ {0x0001, 0x8114},
+/* 320 */ {0x0001, 0x8114},
+/* 321 */ {0x0003, 0x8114},
+/* 322 */ {0x0080, 0x8804},
+
+/* 355 */ {0x0003, 0x8801},
+/* 356 */ {0x0012, 0x8800},
+/* 358 */ {0x0004, 0x8801},
+/* 359 */ {0x0005, 0x8800},
+/* 361 */ {0x0005, 0x8801},
+/* 362 */ {0x0047, 0x8800},
+/* 364 */ {0x0006, 0x8801},
+/* 365 */ {0x0000, 0x8800},
+/* 367 */ {0x0007, 0x8801},
+/* 368 */ {0x00c0, 0x8800},
+/* 370 */ {0x0008, 0x8801},
+/* 371 */ {0x0003, 0x8800},
+/* 373 */ {0x000a, 0x8700},
+/* 374 */ {0x000e, 0x8801},
+/* 375 */ {0x0004, 0x8800},
+/* 377 */ {0x0005, 0x8801},
+/* 378 */ {0x0047, 0x8800},
+/* 380 */ {0x0006, 0x8801},
+/* 381 */ {0x0000, 0x8800},
+/* 383 */ {0x0007, 0x8801},
+/* 384 */ {0x00c0, 0x8800},
+/* 386 */ {0x0008, 0x8801},
+/* 387 */ {0x0003, 0x8800},
+/* 389 */ {0x0013, 0x8801},
+/* 390 */ {0x0001, 0x8800},
+/* 392 */ {0x0009, 0x8801},
+/* 393 */ {0x0000, 0x8800},
+/* 395 */ {0x000a, 0x8801},
+/* 396 */ {0x0000, 0x8800},
+/* 398 */ {0x000b, 0x8801},
+/* 399 */ {0x0000, 0x8800},
+/* 401 */ {0x000c, 0x8801},
+/* 402 */ {0x0000, 0x8800},
+/* 404 */ {0x000e, 0x8801},
+/* 405 */ {0x0004, 0x8800},
+/* 407 */ {0x000f, 0x8801},
+/* 408 */ {0x0000, 0x8800},
+/* 410 */ {0x0010, 0x8801},
+/* 411 */ {0x0006, 0x8800},
+/* 413 */ {0x0011, 0x8801},
+/* 414 */ {0x0006, 0x8800},
+/* 416 */ {0x0012, 0x8801},
+/* 417 */ {0x0000, 0x8800},
+/* 419 */ {0x0013, 0x8801},
+/* 420 */ {0x0001, 0x8800},
+/* 422 */ {0x000a, 0x8700},
+/* 423 */ {0x0000, 0x8702},
+/* 424 */ {0x0000, 0x8703},
+/* 425 */ {0x00c2, 0x8704},
+/* 426 */ {0x0001, 0x870c},
+/* 427 */ {0x0044, 0x8600},
+/* 428 */ {0x0002, 0x8606},
+/* 429 */ {0x0064, 0x8607},
+/* 430 */ {0x003a, 0x8601},
+/* 431 */ {0x0008, 0x8602},
+/* 432 */ {0x0044, 0x8600},
+/* 433 */ {0x0018, 0x8617},
+/* 434 */ {0x0008, 0x8618},
+/* 435 */ {0x00a1, 0x8656},
+/* 436 */ {0x0004, 0x865b},
+/* 437 */ {0x0002, 0x865c},
+/* 438 */ {0x0058, 0x865d},
+/* 439 */ {0x0048, 0x865e},
+/* 440 */ {0x0012, 0x8608},
+/* 441 */ {0x002c, 0x8609},
+/* 442 */ {0x0002, 0x860a},
+/* 443 */ {0x002c, 0x860b},
+/* 444 */ {0x00db, 0x860c},
+/* 445 */ {0x00f9, 0x860d},
+/* 446 */ {0x00f1, 0x860e},
+/* 447 */ {0x00e3, 0x860f},
+/* 448 */ {0x002c, 0x8610},
+/* 449 */ {0x006c, 0x8651},
+/* 450 */ {0x0041, 0x8652},
+/* 451 */ {0x0059, 0x8653},
+/* 452 */ {0x0040, 0x8654},
+/* 453 */ {0x00fa, 0x8611},
+/* 454 */ {0x00ff, 0x8612},
+/* 455 */ {0x00f8, 0x8613},
+/* 456 */ {0x0000, 0x8614},
+/* 457 */ {0x0001, 0x863f},
+/* 458 */ {0x0000, 0x8640},
+/* 459 */ {0x0026, 0x8641},
+/* 460 */ {0x0045, 0x8642},
+/* 461 */ {0x0060, 0x8643},
+/* 462 */ {0x0075, 0x8644},
+/* 463 */ {0x0088, 0x8645},
+/* 464 */ {0x009b, 0x8646},
+/* 465 */ {0x00b0, 0x8647},
+/* 466 */ {0x00c5, 0x8648},
+/* 467 */ {0x00d2, 0x8649},
+/* 468 */ {0x00dc, 0x864a},
+/* 469 */ {0x00e5, 0x864b},
+/* 470 */ {0x00eb, 0x864c},
+/* 471 */ {0x00f0, 0x864d},
+/* 472 */ {0x00f6, 0x864e},
+/* 473 */ {0x00fa, 0x864f},
+/* 474 */ {0x00ff, 0x8650},
+/* 475 */ {0x0060, 0x8657},
+/* 476 */ {0x0010, 0x8658},
+/* 477 */ {0x0018, 0x8659},
+/* 478 */ {0x0005, 0x865a},
+/* 479 */ {0x0018, 0x8660},
+/* 480 */ {0x0003, 0x8509},
+/* 481 */ {0x0011, 0x850a},
+/* 482 */ {0x0032, 0x850b},
+/* 483 */ {0x0010, 0x850c},
+/* 484 */ {0x0021, 0x850d},
+/* 485 */ {0x0001, 0x8500},
+/* 486 */ {0x0000, 0x8508},
+
+/* 487 */ {0x0012, 0x8608},
+/* 488 */ {0x002c, 0x8609},
+/* 489 */ {0x0002, 0x860a},
+/* 490 */ {0x0039, 0x860b},
+/* 491 */ {0x00d0, 0x860c},
+/* 492 */ {0x00f7, 0x860d},
+/* 493 */ {0x00ed, 0x860e},
+/* 494 */ {0x00db, 0x860f},
+/* 495 */ {0x0039, 0x8610},
+/* 496 */ {0x0012, 0x8657},
+/* 497 */ {0x0064, 0x8619},
+
+/* This line starts it all, it is not needed here */
+/* since it has been build into the driver */
+/* jfm: don't start now */
+/* 590  *  {0x0030, 0x8112}, */
+       {}
+};
+
+/*
+ * Initialization data for Creative Webcam Vista
+ */
+static const __u16 spca508_vista_init_data[][3] = {
+       {0x0008, 0x8200},       /* Clear register */
+       {0x0000, 0x870b},       /* Reset CTL3 */
+       {0x0020, 0x8112},       /* Video Drop packet enable */
+       {0x0003, 0x8111},  /* Soft Reset compression, memory, TG & CDSP */
+       {0x0000, 0x8110},       /* Disable everything */
+       {0x0000, 0x8114},       /* Software GPIO output data */
+       {0x0000, 0x8114},
+
+       {0x0003, 0x8111},
+       {0x0000, 0x8111},
+       {0x0090, 0x8110},  /* Enable: SSI output, External 2X clock output */
+       {0x0020, 0x8112},
+       {0x0000, 0x8114},
+       {0x0001, 0x8114},
+       {0x0001, 0x8114},
+       {0x0001, 0x8114},
+       {0x0003, 0x8114},
+
+       {0x000f, 0x8402},       /* Memory bank Address */
+       {0x0000, 0x8403},       /* Memory bank Address */
+       {0x00ba, 0x8804},       /* SSI Slave address */
+       {0x0010, 0x8802},       /* 93.75kHz SSI Clock Two DataByte */
+
+       /* READ { 0, 0x0001, 0x8803 } ->
+               0000: 00  */
+       /* READ { 0, 0x0001, 0x8802 } ->
+               0000: 10  */
+       {0x0010, 0x8802},       /* Will write 2 bytes (DATA1+DATA2) */
+       {0x0020, 0x8801},       /* Register address for SSI read/write */
+       {0x0044, 0x8805},       /* DATA2 */
+       {0x0004, 0x8800},       /* DATA1 -> write triggered */
+       /* READ { 0, 0x0001, 0x8803 } ->
+               0000: 00  */
+
+       /* READ { 0, 0x0001, 0x8803 } ->
+               0000: 00  */
+       /* READ { 0, 0x0001, 0x8802 } ->
+               0000: 10  */
+       {0x0010, 0x8802},
+       {0x0009, 0x8801},
+       {0x0042, 0x8805},
+       {0x0001, 0x8800},
+       /* READ { 0, 0x0001, 0x8803 } ->
+               0000: 00  */
+
+       /* READ { 0, 0x0001, 0x8803 } ->
+               0000: 00  */
+       /* READ { 0, 0x0001, 0x8802 } ->
+               0000: 10  */
+       {0x0010, 0x8802},
+       {0x003c, 0x8801},
+       {0x0001, 0x8805},
+       {0x0000, 0x8800},
+       /* READ { 0, 0x0001, 0x8803 } ->
+               0000: 00  */
+
+       /* READ { 0, 0x0001, 0x8803 } ->
+               0000: 00  */
+       /* READ { 0, 0x0001, 0x8802 } ->
+               0000: 10  */
+       {0x0010, 0x8802},
+       {0x0001, 0x8801},
+       {0x000a, 0x8805},
+       {0x0000, 0x8800},
+       /* READ { 0, 0x0001, 0x8803 } ->
+               0000: 00  */
+
+       /* READ { 0, 0x0001, 0x8803 } ->
+               0000: 00  */
+       /* READ { 0, 0x0001, 0x8802 } ->
+               0000: 10  */
+       {0x0010, 0x8802},
+       {0x0002, 0x8801},
+       {0x0000, 0x8805},
+       {0x0000, 0x8800},
+       /* READ { 0, 0x0001, 0x8803 } ->
+               0000: 00  */
+
+       /* READ { 0, 0x0001, 0x8803 } ->
+               0000: 00  */
+       /* READ { 0, 0x0001, 0x8802 } ->
+               0000: 10  */
+       {0x0010, 0x8802},
+       {0x0003, 0x8801},
+       {0x0027, 0x8805},
+       {0x0001, 0x8800},
+       /* READ { 0, 0x0001, 0x8803 } ->
+               0000: 00  */
+
+       /* READ { 0, 0x0001, 0x8803 } ->
+               0000: 00  */
+       /* READ { 0, 0x0001, 0x8802 } ->
+               0000: 10  */
+       {0x0010, 0x8802},
+       {0x0004, 0x8801},
+       {0x0065, 0x8805},
+       {0x0001, 0x8800},
+       /* READ { 0, 0x0001, 0x8803 } ->
+               0000: 00  */
+
+       /* READ { 0, 0x0001, 0x8803 } ->
+               0000: 00  */
+       /* READ { 0, 0x0001, 0x8802 } ->
+               0000: 10  */
+       {0x0010, 0x8802},
+       {0x0005, 0x8801},
+       {0x0003, 0x8805},
+       {0x0000, 0x8800},
+       /* READ { 0, 0x0001, 0x8803 } ->
+               0000: 00  */
+
+       /* READ { 0, 0x0001, 0x8803 } ->
+               0000: 00  */
+       /* READ { 0, 0x0001, 0x8802 } ->
+               0000: 10  */
+       {0x0010, 0x8802},
+       {0x0006, 0x8801},
+       {0x001c, 0x8805},
+       {0x0000, 0x8800},
+       /* READ { 0, 0x0001, 0x8803 } ->
+               0000: 00  */
+
+       /* READ { 0, 0x0001, 0x8803 } ->
+               0000: 00  */
+       /* READ { 0, 0x0001, 0x8802 } ->
+               0000: 10  */
+       {0x0010, 0x8802},
+       {0x0007, 0x8801},
+       {0x002a, 0x8805},
+       {0x0000, 0x8800},
+       /* READ { 0, 0x0001, 0x8803 } ->
+               0000: 00  */
+
+       /* READ { 0, 0x0001, 0x8803 } ->
+               0000: 00  */
+       /* READ { 0, 0x0001, 0x8802 } ->
+               0000: 10  */
+       {0x0010, 0x8802},
+       {0x000e, 0x8801},
+       {0x0000, 0x8805},
+       {0x0000, 0x8800},
+       /* READ { 0, 0x0001, 0x8803 } ->
+               0000: 00  */
+
+       /* READ { 0, 0x0001, 0x8803 } ->
+               0000: 00  */
+       /* READ { 0, 0x0001, 0x8802 } ->
+               0000: 10  */
+       {0x0010, 0x8802},
+       {0x0028, 0x8801},
+       {0x002e, 0x8805},
+       {0x0000, 0x8800},
+       /* READ { 0, 0x0001, 0x8803 } ->
+               0000: 00  */
+
+       /* READ { 0, 0x0001, 0x8803 } ->
+               0000: 00  */
+       /* READ { 0, 0x0001, 0x8802 } ->
+               0000: 10  */
+       {0x0010, 0x8802},
+       {0x0039, 0x8801},
+       {0x0013, 0x8805},
+       {0x0000, 0x8800},
+       /* READ { 0, 0x0001, 0x8803 } ->
+               0000: 00  */
+
+       /* READ { 0, 0x0001, 0x8803 } ->
+               0000: 00  */
+       /* READ { 0, 0x0001, 0x8802 } ->
+               0000: 10  */
+       {0x0010, 0x8802},
+       {0x003b, 0x8801},
+       {0x000c, 0x8805},
+       {0x0000, 0x8800},
+       /* READ { 0, 0x0001, 0x8803 } ->
+               0000: 00  */
+
+       /* READ { 0, 0x0001, 0x8803 } ->
+               0000: 00  */
+       /* READ { 0, 0x0001, 0x8802 } ->
+               0000: 10  */
+       {0x0010, 0x8802},
+       {0x0035, 0x8801},
+       {0x0028, 0x8805},
+       {0x0000, 0x8800},
+       /* READ { 0, 0x0001, 0x8803 } ->
+               0000: 00  */
+
+       /* READ { 0, 0x0001, 0x8803 } ->
+               0000: 00  */
+       /* READ { 0, 0x0001, 0x8802 } ->
+               0000: 10  */
+       {0x0010, 0x8802},
+       {0x0009, 0x8801},
+       {0x0042, 0x8805},
+       {0x0001, 0x8800},
+       /* READ { 0, 0x0001, 0x8803 } ->
+               0000: 00  */
+
+       {0x0050, 0x8703},
+       {0x0002, 0x8704},       /* External input CKIx1 */
+       {0x0001, 0x870C},       /* Select CKOx2 output */
+       {0x009A, 0x8600},       /* Line memory Read Counter (L) */
+       {0x0001, 0x8606},  /* 1 Line memory Read Counter (H) Result: (d)410 */
+       {0x0023, 0x8601},
+       {0x0010, 0x8602},
+       {0x000A, 0x8603},
+       {0x009A, 0x8600},
+       {0x0001, 0x865B},       /* 1 Horizontal Offset for Valid Pixel(L) */
+       {0x0003, 0x865C},       /* Vertical offset for valid lines (L) */
+       {0x0058, 0x865D},       /* Horizontal valid pixels window (L) */
+       {0x0048, 0x865E},       /* Vertical valid lines window (L) */
+       {0x0000, 0x865F},
+
+       {0x0006, 0x8660},
+                   /* Enable nibble data input, select nibble input order */
+
+       {0x0013, 0x8608},       /* A11 Coeficients for color correction */
+       {0x0028, 0x8609},
+                   /* Note: these values are confirmed at the end of array */
+       {0x0005, 0x860A},       /* ... */
+       {0x0025, 0x860B},
+       {0x00E1, 0x860C},
+       {0x00FA, 0x860D},
+       {0x00F4, 0x860E},
+       {0x00E8, 0x860F},
+       {0x0025, 0x8610},       /* A33 Coef. */
+       {0x00FC, 0x8611},       /* White balance offset: R */
+       {0x0001, 0x8612},       /* White balance offset: Gr */
+       {0x00FE, 0x8613},       /* White balance offset: B */
+       {0x0000, 0x8614},       /* White balance offset: Gb */
+
+       {0x0064, 0x8651},       /* R gain for white balance (L) */
+       {0x0040, 0x8652},       /* Gr gain for white balance (L) */
+       {0x0066, 0x8653},       /* B gain for white balance (L) */
+       {0x0040, 0x8654},       /* Gb gain for white balance (L) */
+       {0x0001, 0x863F},       /* Enable fixed gamma correction */
+
+       {0x00A1, 0x8656},       /* Size - Window1: 256x256, Window2: 128x128 */
+       /* UV division: UV no change, Enable New edge enhancement */
+       {0x0018, 0x8657},       /* Edge gain high threshold */
+       {0x0020, 0x8658},       /* Edge gain low threshold */
+       {0x000A, 0x8659},       /* Edge bandwidth high threshold */
+       {0x0005, 0x865A},       /* Edge bandwidth low threshold */
+       {0x0064, 0x8607},       /* UV filter enable */
+
+       {0x0016, 0x8660},
+       {0x0000, 0x86B0},       /* Bad pixels compensation address */
+       {0x00DC, 0x86B1},       /* X coord for bad pixels compensation (L) */
+       {0x0000, 0x86B2},
+       {0x0009, 0x86B3},       /* Y coord for bad pixels compensation (L) */
+       {0x0000, 0x86B4},
+
+       {0x0001, 0x86B0},
+       {0x00F5, 0x86B1},
+       {0x0000, 0x86B2},
+       {0x00C6, 0x86B3},
+       {0x0000, 0x86B4},
+
+       {0x0002, 0x86B0},
+       {0x001C, 0x86B1},
+       {0x0001, 0x86B2},
+       {0x00D7, 0x86B3},
+       {0x0000, 0x86B4},
+
+       {0x0003, 0x86B0},
+       {0x001C, 0x86B1},
+       {0x0001, 0x86B2},
+       {0x00D8, 0x86B3},
+       {0x0000, 0x86B4},
+
+       {0x0004, 0x86B0},
+       {0x001D, 0x86B1},
+       {0x0001, 0x86B2},
+       {0x00D8, 0x86B3},
+       {0x0000, 0x86B4},
+       {0x001E, 0x8660},
+
+       /* READ { 0, 0x0000, 0x8608 } ->
+               0000: 13  */
+       /* READ { 0, 0x0000, 0x8609 } ->
+               0000: 28  */
+       /* READ { 0, 0x0000, 0x8610 } ->
+               0000: 05  */
+       /* READ { 0, 0x0000, 0x8611 } ->
+               0000: 25  */
+       /* READ { 0, 0x0000, 0x8612 } ->
+               0000: e1  */
+       /* READ { 0, 0x0000, 0x8613 } ->
+               0000: fa  */
+       /* READ { 0, 0x0000, 0x8614 } ->
+               0000: f4  */
+       /* READ { 0, 0x0000, 0x8615 } ->
+               0000: e8  */
+       /* READ { 0, 0x0000, 0x8616 } ->
+               0000: 25  */
+       {}
+};
+
+static int reg_write(struct usb_device *dev,
+                       __u16 index, __u16 value)
+{
+       int ret;
+
+       ret = usb_control_msg(dev,
+                       usb_sndctrlpipe(dev, 0),
+                       0,              /* request */
+                       USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                       value, index, NULL, 0, 500);
+       PDEBUG(D_USBO, "reg write i:0x%04x = 0x%02x",
+               index, value);
+       if (ret < 0)
+               PDEBUG(D_ERR|D_USBO, "reg write: error %d", ret);
+       return ret;
+}
+
+/* read 1 byte */
+/* returns: negative is error, pos or zero is data */
+static int reg_read(struct gspca_dev *gspca_dev,
+                       __u16 index)    /* wIndex */
+{
+       int ret;
+
+       ret = usb_control_msg(gspca_dev->dev,
+                       usb_rcvctrlpipe(gspca_dev->dev, 0),
+                       0,                      /* register */
+                       USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                       0,              /* value */
+                       index,
+                       gspca_dev->usb_buf, 1,
+                       500);                   /* timeout */
+       PDEBUG(D_USBI, "reg read i:%04x --> %02x",
+               index, gspca_dev->usb_buf[0]);
+       if (ret < 0) {
+               PDEBUG(D_ERR|D_USBI, "reg_read err %d", ret);
+               return ret;
+       }
+       return gspca_dev->usb_buf[0];
+}
+
+static int write_vector(struct gspca_dev *gspca_dev,
+                       const __u16 data[][3])
+{
+       struct usb_device *dev = gspca_dev->dev;
+       int ret, i = 0;
+
+       while (data[i][1] != 0) {
+               ret = reg_write(dev, data[i][1], data[i][0]);
+               if (ret < 0)
+                       return ret;
+               i++;
+       }
+       return 0;
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+                       const struct usb_device_id *id)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       struct cam *cam;
+       __u16 product;
+       int data1, data2;
+
+       product = id->idProduct;
+       switch (id->idVendor) {
+       case 0x0130:            /* Clone webcam */
+/*             switch (product) { */
+/*             case 0x0130: */
+                       sd->subtype = HamaUSBSightcam;  /* same as Hama 0010 */
+/*                     break; */
+/*             } */
+               break;
+       case 0x041e:            /* Creative cameras */
+/*             switch (product) { */
+/*             case 0x4018: */
+                       sd->subtype = CreativeVista;
+/*                     break; */
+/*             } */
+               break;
+       case 0x0461:            /* MicroInnovation */
+/*             switch (product) { */
+/*             case 0x0815: */
+                       sd->subtype = MicroInnovationIC200;
+/*                     break; */
+/*             } */
+               break;
+       case 0x0733:    /* Rebadged ViewQuest (Intel) and ViewQuest cameras */
+/*             switch (product) { */
+/*             case 0x110: */
+                       sd->subtype = ViewQuestVQ110;
+/*                     break; */
+/*             } */
+               break;
+       case 0x0af9:            /* Hama cameras */
+               switch (product) {
+               case 0x0010:
+                       sd->subtype = HamaUSBSightcam;
+                       break;
+               case 0x0011:
+                       sd->subtype = HamaUSBSightcam2;
+                       break;
+               }
+               break;
+       case 0x8086:            /* Intel */
+/*             switch (product) { */
+/*             case 0x0110: */
+                       sd->subtype = IntelEasyPCCamera;
+/*                     break; */
+/*             } */
+               break;
+       }
+
+       /* Read from global register the USB product and vendor IDs, just to
+        * prove that we can communicate with the device.  This works, which
+        * confirms at we are communicating properly and that the device
+        * is a 508. */
+       data1 = reg_read(gspca_dev, 0x8104);
+       data2 = reg_read(gspca_dev, 0x8105);
+       PDEBUG(D_PROBE, "Webcam Vendor ID: 0x%02x%02x", data2, data1);
+
+       data1 = reg_read(gspca_dev, 0x8106);
+       data2 = reg_read(gspca_dev, 0x8107);
+       PDEBUG(D_PROBE, "Webcam Product ID: 0x%02x%02x", data2, data1);
+
+       data1 = reg_read(gspca_dev, 0x8621);
+       PDEBUG(D_PROBE, "Window 1 average luminance: %d", data1);
+
+       cam = &gspca_dev->cam;
+       cam->dev_name = (char *) id->driver_info;
+       cam->epaddr = 0x01;
+       cam->cam_mode = sif_mode;
+       cam->nmodes = ARRAY_SIZE(sif_mode);
+       sd->brightness = BRIGHTNESS_DEF;
+
+       switch (sd->subtype) {
+       case ViewQuestVQ110:
+               if (write_vector(gspca_dev, spca508_init_data))
+                       return -1;
+               break;
+       default:
+/*     case MicroInnovationIC200: */
+/*     case IntelEasyPCCamera: */
+               if (write_vector(gspca_dev, spca508cs110_init_data))
+                       return -1;
+               break;
+       case HamaUSBSightcam:
+               if (write_vector(gspca_dev, spca508_sightcam_init_data))
+                       return -1;
+               break;
+       case HamaUSBSightcam2:
+               if (write_vector(gspca_dev, spca508_sightcam2_init_data))
+                       return -1;
+               break;
+       case CreativeVista:
+               if (write_vector(gspca_dev, spca508_vista_init_data))
+                       return -1;
+               break;
+       }
+       return 0;                       /* success */
+}
+
+/* this function is called at open time */
+static int sd_open(struct gspca_dev *gspca_dev)
+{
+/*     write_vector(gspca_dev, spca508_open_data); */
+       return 0;
+}
+
+static void sd_start(struct gspca_dev *gspca_dev)
+{
+       int mode;
+
+       mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;
+       reg_write(gspca_dev->dev, 0x8500, mode);
+       switch (mode) {
+       case 0:
+       case 1:
+               reg_write(gspca_dev->dev, 0x8700, 0x28);        /* clock */
+               break;
+       default:
+/*     case 2: */
+/*     case 3: */
+               reg_write(gspca_dev->dev, 0x8700, 0x23);        /* clock */
+               break;
+       }
+       reg_write(gspca_dev->dev, 0x8112, 0x10 | 0x20);
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+       /* Video ISO disable, Video Drop Packet enable: */
+       reg_write(gspca_dev->dev, 0x8112, 0x20);
+}
+
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+}
+
+/* this function is called at close time */
+static void sd_close(struct gspca_dev *gspca_dev)
+{
+}
+
+/* convert YUVY per line to YUYV (YUV 4:2:2) */
+static void yuvy_decode(unsigned char *out,
+                       unsigned char *in,
+                       int width,
+                       int height)
+{
+       unsigned char *Ui, *Vi, *yi, *yi1;
+       unsigned char *out1;
+       int i, j;
+
+       yi = in;
+       for (i = height / 2; --i >= 0; ) {
+               out1 = out + width * 2;         /* next line */
+               Ui = yi + width;
+               Vi = Ui + width / 2;
+               yi1 = Vi + width / 2;
+               for (j = width / 2; --j >= 0; ) {
+                       *out++ = 128 + *yi++;
+                       *out++ = 128 + *Ui;
+                       *out++ = 128 + *yi++;
+                       *out++ = 128 + *Vi;
+
+                       *out1++ = 128 + *yi1++;
+                       *out1++ = 128 + *Ui++;
+                       *out1++ = 128 + *yi1++;
+                       *out1++ = 128 + *Vi++;
+               }
+               yi += width * 2;
+               out = out1;
+       }
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+                       struct gspca_frame *frame,      /* target */
+                       __u8 *data,                     /* isoc packet */
+                       int len)                        /* iso packet length */
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       switch (data[0]) {
+       case 0:                         /* start of frame */
+               if (gspca_dev->last_packet_type == FIRST_PACKET) {
+                       yuvy_decode(sd->tmpbuf2, sd->tmpbuf,
+                                       gspca_dev->width,
+                                       gspca_dev->height);
+                       frame = gspca_frame_add(gspca_dev,
+                                               LAST_PACKET,
+                                               frame,
+                                               sd->tmpbuf2,
+                                               gspca_dev->width
+                                                       * gspca_dev->height
+                                                       * 2);
+               }
+               gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
+                               data, 0);
+               data += SPCA508_OFFSET_DATA;
+               len -= SPCA508_OFFSET_DATA;
+               if (len > 0)
+                       memcpy(sd->tmpbuf, data, len);
+               else
+                       len = 0;
+               sd->buflen = len;
+               return;
+       case 0xff:                      /* drop */
+/*             gspca_dev->last_packet_type = DISCARD_PACKET; */
+               return;
+       }
+       data += 1;
+       len -= 1;
+       memcpy(&sd->tmpbuf[sd->buflen], data, len);
+       sd->buflen += len;
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       __u8 brightness = sd->brightness;
+
+       /* MX seem contrast */
+       reg_write(gspca_dev->dev, 0x8651, brightness);
+       reg_write(gspca_dev->dev, 0x8652, brightness);
+       reg_write(gspca_dev->dev, 0x8653, brightness);
+       reg_write(gspca_dev->dev, 0x8654, brightness);
+}
+
+static void getbrightness(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->brightness = reg_read(gspca_dev, 0x8651);
+}
+
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->brightness = val;
+       if (gspca_dev->streaming)
+               setbrightness(gspca_dev);
+       return 0;
+}
+
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       getbrightness(gspca_dev);
+       *val = sd->brightness;
+       return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+       .name = MODULE_NAME,
+       .ctrls = sd_ctrls,
+       .nctrls = ARRAY_SIZE(sd_ctrls),
+       .config = sd_config,
+       .open = sd_open,
+       .start = sd_start,
+       .stopN = sd_stopN,
+       .stop0 = sd_stop0,
+       .close = sd_close,
+       .pkt_scan = sd_pkt_scan,
+};
+
+/* -- module initialisation -- */
+#define DVNM(name) .driver_info = (kernel_ulong_t) name
+static const __devinitdata struct usb_device_id device_table[] = {
+       {USB_DEVICE(0x0130, 0x0130), DVNM("Clone Digital Webcam 11043")},
+       {USB_DEVICE(0x041e, 0x4018), DVNM("Creative Webcam Vista (PD1100)")},
+       {USB_DEVICE(0x0461, 0x0815), DVNM("Micro Innovation IC200")},
+       {USB_DEVICE(0x0733, 0x0110), DVNM("ViewQuest VQ110")},
+       {USB_DEVICE(0x0af9, 0x0010), DVNM("Hama USB Sightcam 100")},
+       {USB_DEVICE(0x0af9, 0x0011), DVNM("Hama USB Sightcam 100")},
+       {USB_DEVICE(0x8086, 0x0110), DVNM("Intel Easy PC Camera")},
+       {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+                       const struct usb_device_id *id)
+{
+       return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+                               THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+       .name = MODULE_NAME,
+       .id_table = device_table,
+       .probe = sd_probe,
+       .disconnect = gspca_disconnect,
+};
+
+/* -- module insert / remove -- */
+static int __init sd_mod_init(void)
+{
+       if (usb_register(&sd_driver) < 0)
+               return -1;
+       PDEBUG(D_PROBE, "v%s registered", version);
+       return 0;
+}
+static void __exit sd_mod_exit(void)
+{
+       usb_deregister(&sd_driver);
+       PDEBUG(D_PROBE, "deregistered");
+}
+
+module_init(sd_mod_init);
+module_exit(sd_mod_exit);
diff --git a/drivers/media/video/gspca/spca561.c b/drivers/media/video/gspca/spca561.c
new file mode 100644 (file)
index 0000000..b659bd0
--- /dev/null
@@ -0,0 +1,1052 @@
+/*
+ * Sunplus spca561 subdriver
+ *
+ * Copyright (C) 2004 Michel Xhaard mxhaard@magic.fr
+ *
+ * V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
+ *
+ * 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
+ * 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
+ */
+
+#define MODULE_NAME "spca561"
+
+#include "gspca.h"
+
+#define DRIVER_VERSION_NUMBER  KERNEL_VERSION(2, 1, 7)
+static const char version[] = "2.1.7";
+
+MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
+MODULE_DESCRIPTION("GSPCA/SPCA561 USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* specific webcam descriptor */
+struct sd {
+       struct gspca_dev gspca_dev;     /* !! must be the first item */
+
+       unsigned short contrast;
+       __u8 brightness;
+       __u8 autogain;
+
+       __u8 chip_revision;
+       signed char ag_cnt;
+#define AG_CNT_START 13
+};
+
+/* V4L2 controls supported by the driver */
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
+
+static struct ctrl sd_ctrls[] = {
+#define SD_BRIGHTNESS 0
+       {
+        {
+         .id = V4L2_CID_BRIGHTNESS,
+         .type = V4L2_CTRL_TYPE_INTEGER,
+         .name = "Brightness",
+         .minimum = 0,
+         .maximum = 63,
+         .step = 1,
+         .default_value = 32,
+         },
+        .set = sd_setbrightness,
+        .get = sd_getbrightness,
+        },
+#define SD_CONTRAST 1
+       {
+        {
+         .id = V4L2_CID_CONTRAST,
+         .type = V4L2_CTRL_TYPE_INTEGER,
+         .name = "Contrast",
+         .minimum = 0,
+         .maximum = 0x3fff,
+         .step = 1,
+         .default_value = 0x2000,
+         },
+        .set = sd_setcontrast,
+        .get = sd_getcontrast,
+        },
+#define SD_AUTOGAIN 2
+       {
+        {
+         .id = V4L2_CID_AUTOGAIN,
+         .type = V4L2_CTRL_TYPE_BOOLEAN,
+         .name = "Auto Gain",
+         .minimum = 0,
+         .maximum = 1,
+         .step = 1,
+         .default_value = 1,
+         },
+        .set = sd_setautogain,
+        .get = sd_getautogain,
+        },
+};
+
+static struct v4l2_pix_format sif_mode[] = {
+       {160, 120, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
+               .bytesperline = 160,
+               .sizeimage = 160 * 120,
+               .colorspace = V4L2_COLORSPACE_SRGB,
+               .priv = 3},
+       {176, 144, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
+               .bytesperline = 176,
+               .sizeimage = 176 * 144,
+               .colorspace = V4L2_COLORSPACE_SRGB,
+               .priv = 2},
+       {320, 240, V4L2_PIX_FMT_SPCA561, V4L2_FIELD_NONE,
+               .bytesperline = 320,
+               .sizeimage = 320 * 240 * 4 / 8,
+               .colorspace = V4L2_COLORSPACE_SRGB,
+               .priv = 1},
+       {352, 288, V4L2_PIX_FMT_SPCA561, V4L2_FIELD_NONE,
+               .bytesperline = 352,
+               .sizeimage = 352 * 288 * 4 / 8,
+               .colorspace = V4L2_COLORSPACE_SRGB,
+               .priv = 0},
+};
+
+/*
+ * Initialization data
+ * I'm not very sure how to split initialization from open data
+ * chunks. For now, we'll consider everything as initialization
+ */
+/* Frame packet header offsets for the spca561 */
+#define SPCA561_OFFSET_SNAP 1
+#define SPCA561_OFFSET_TYPE 2
+#define SPCA561_OFFSET_COMPRESS 3
+#define SPCA561_OFFSET_FRAMSEQ   4
+#define SPCA561_OFFSET_GPIO 5
+#define SPCA561_OFFSET_USBBUFF 6
+#define SPCA561_OFFSET_WIN2GRAVE 7
+#define SPCA561_OFFSET_WIN2RAVE 8
+#define SPCA561_OFFSET_WIN2BAVE 9
+#define SPCA561_OFFSET_WIN2GBAVE 10
+#define SPCA561_OFFSET_WIN1GRAVE 11
+#define SPCA561_OFFSET_WIN1RAVE 12
+#define SPCA561_OFFSET_WIN1BAVE 13
+#define SPCA561_OFFSET_WIN1GBAVE 14
+#define SPCA561_OFFSET_FREQ 15
+#define SPCA561_OFFSET_VSYNC 16
+#define SPCA561_OFFSET_DATA 1
+#define SPCA561_INDEX_I2C_BASE 0x8800
+#define SPCA561_SNAPBIT 0x20
+#define SPCA561_SNAPCTRL 0x40
+enum {
+       Rev072A = 0,
+       Rev012A,
+};
+
+static void reg_w_val(struct usb_device *dev, __u16 index, __u16 value)
+{
+       int ret;
+
+       ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+                             0,                /* request */
+                             USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                             value, index, NULL, 0, 500);
+       PDEBUG(D_USBO, "reg write: 0x%02x:0x%02x", index, value);
+       if (ret < 0)
+               PDEBUG(D_ERR, "reg write: error %d", ret);
+}
+
+static void write_vector(struct gspca_dev *gspca_dev,
+                       const __u16 data[][2])
+{
+       struct usb_device *dev = gspca_dev->dev;
+       int i;
+
+       i = 0;
+       while (data[i][1] != 0) {
+               reg_w_val(dev, data[i][1], data[i][0]);
+               i++;
+       }
+}
+
+/* read 'len' bytes to gspca_dev->usb_buf */
+static void reg_r(struct gspca_dev *gspca_dev,
+                 __u16 index, __u16 length)
+{
+       usb_control_msg(gspca_dev->dev,
+                       usb_rcvctrlpipe(gspca_dev->dev, 0),
+                       0,                      /* request */
+                       USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                       0,                      /* value */
+                       index, gspca_dev->usb_buf, length, 500);
+}
+
+static void reg_w_buf(struct gspca_dev *gspca_dev,
+                     __u16 index, const __u8 *buffer, __u16 len)
+{
+       memcpy(gspca_dev->usb_buf, buffer, len);
+       usb_control_msg(gspca_dev->dev,
+                       usb_sndctrlpipe(gspca_dev->dev, 0),
+                       0,                      /* request */
+                       USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                       0,                      /* value */
+                       index, gspca_dev->usb_buf, len, 500);
+}
+
+static void i2c_init(struct gspca_dev *gspca_dev, __u8 mode)
+{
+       reg_w_val(gspca_dev->dev, 0x92, 0x8804);
+       reg_w_val(gspca_dev->dev, mode, 0x8802);
+}
+
+static void i2c_write(struct gspca_dev *gspca_dev, __u16 valeur, __u16 reg)
+{
+       int retry = 60;
+       __u8 DataLow;
+       __u8 DataHight;
+
+       DataLow = valeur;
+       DataHight = valeur >> 8;
+       reg_w_val(gspca_dev->dev, reg, 0x8801);
+       reg_w_val(gspca_dev->dev, DataLow, 0x8805);
+       reg_w_val(gspca_dev->dev, DataHight, 0x8800);
+       while (retry--) {
+               reg_r(gspca_dev, 0x8803, 1);
+               if (!gspca_dev->usb_buf[0])
+                       break;
+       }
+}
+
+static int i2c_read(struct gspca_dev *gspca_dev, __u16 reg, __u8 mode)
+{
+       int retry = 60;
+       __u8 value;
+       __u8 vallsb;
+
+       reg_w_val(gspca_dev->dev, 0x92, 0x8804);
+       reg_w_val(gspca_dev->dev, reg, 0x8801);
+       reg_w_val(gspca_dev->dev, (mode | 0x01), 0x8802);
+       while (retry--) {
+               reg_r(gspca_dev, 0x8803, 1);
+               if (!gspca_dev->usb_buf)
+                       break;
+       }
+       if (retry == 0)
+               return -1;
+       reg_r(gspca_dev, 0x8800, 1);
+       value = gspca_dev->usb_buf[0];
+       reg_r(gspca_dev, 0x8805, 1);
+       vallsb = gspca_dev->usb_buf[0];
+       return ((int) value << 8) | vallsb;
+}
+
+static const __u16 spca561_init_data[][2] = {
+       {0x0000, 0x8114},       /* Software GPIO output data */
+       {0x0001, 0x8114},       /* Software GPIO output data */
+       {0x0000, 0x8112},       /* Some kind of reset */
+       {0x0003, 0x8701},       /* PCLK clock delay adjustment */
+       {0x0001, 0x8703},       /* HSYNC from cmos inverted */
+       {0x0011, 0x8118},       /* Enable and conf sensor */
+       {0x0001, 0x8118},       /* Conf sensor */
+       {0x0092, 0x8804},       /* I know nothing about these */
+       {0x0010, 0x8802},       /* 0x88xx registers, so I won't */
+       /***************/
+       {0x000d, 0x8805},       /* sensor default setting */
+       {0x0001, 0x8801},       /* 1 <- 0x0d */
+       {0x0000, 0x8800},
+       {0x0018, 0x8805},
+       {0x0002, 0x8801},       /* 2 <- 0x18 */
+       {0x0000, 0x8800},
+       {0x0065, 0x8805},
+       {0x0004, 0x8801},       /* 4 <- 0x01 0x65 */
+       {0x0001, 0x8800},
+       {0x0021, 0x8805},
+       {0x0005, 0x8801},       /* 5 <- 0x21 */
+       {0x0000, 0x8800},
+       {0x00aa, 0x8805},
+       {0x0007, 0x8801},       /* 7 <- 0xaa */
+       {0x0000, 0x8800},
+       {0x0004, 0x8805},
+       {0x0020, 0x8801},       /* 0x20 <- 0x15 0x04 */
+       {0x0015, 0x8800},
+       {0x0002, 0x8805},
+       {0x0039, 0x8801},       /* 0x39 <- 0x02 */
+       {0x0000, 0x8800},
+       {0x0010, 0x8805},
+       {0x0035, 0x8801},       /* 0x35 <- 0x10 */
+       {0x0000, 0x8800},
+       {0x0049, 0x8805},
+       {0x0009, 0x8801},       /* 0x09 <- 0x10 0x49 */
+       {0x0010, 0x8800},
+       {0x000b, 0x8805},
+       {0x0028, 0x8801},       /* 0x28 <- 0x0b */
+       {0x0000, 0x8800},
+       {0x000f, 0x8805},
+       {0x003b, 0x8801},       /* 0x3b <- 0x0f */
+       {0x0000, 0x8800},
+       {0x0000, 0x8805},
+       {0x003c, 0x8801},       /* 0x3c <- 0x00 */
+       {0x0000, 0x8800},
+       /***************/
+       {0x0018, 0x8601},       /* Pixel/line selection for color separation */
+       {0x0000, 0x8602},       /* Optical black level for user setting */
+       {0x0060, 0x8604},       /* Optical black horizontal offset */
+       {0x0002, 0x8605},       /* Optical black vertical offset */
+       {0x0000, 0x8603},       /* Non-automatic optical black level */
+       {0x0002, 0x865b},       /* Horizontal offset for valid pixels */
+       {0x0000, 0x865f},       /* Vertical valid pixels window (x2) */
+       {0x00b0, 0x865d},       /* Horizontal valid pixels window (x2) */
+       {0x0090, 0x865e},       /* Vertical valid lines window (x2) */
+       {0x00e0, 0x8406},       /* Memory buffer threshold */
+       {0x0000, 0x8660},       /* Compensation memory stuff */
+       {0x0002, 0x8201},       /* Output address for r/w serial EEPROM */
+       {0x0008, 0x8200},       /* Clear valid bit for serial EEPROM */
+       {0x0001, 0x8200},       /* OprMode to be executed by hardware */
+       {0x0007, 0x8201},       /* Output address for r/w serial EEPROM */
+       {0x0008, 0x8200},       /* Clear valid bit for serial EEPROM */
+       {0x0001, 0x8200},       /* OprMode to be executed by hardware */
+       {0x0010, 0x8660},       /* Compensation memory stuff */
+       {0x0018, 0x8660},       /* Compensation memory stuff */
+
+       {0x0004, 0x8611},       /* R offset for white balance */
+       {0x0004, 0x8612},       /* Gr offset for white balance */
+       {0x0007, 0x8613},       /* B offset for white balance */
+       {0x0000, 0x8614},       /* Gb offset for white balance */
+       {0x008c, 0x8651},       /* R gain for white balance */
+       {0x008c, 0x8652},       /* Gr gain for white balance */
+       {0x00b5, 0x8653},       /* B gain for white balance */
+       {0x008c, 0x8654},       /* Gb gain for white balance */
+       {0x0002, 0x8502},       /* Maximum average bit rate stuff */
+
+       {0x0011, 0x8802},
+       {0x0087, 0x8700},       /* Set master clock (96Mhz????) */
+       {0x0081, 0x8702},       /* Master clock output enable */
+
+       {0x0000, 0x8500},       /* Set image type (352x288 no compression) */
+       /* Originally was 0x0010 (352x288 compression) */
+
+       {0x0002, 0x865b},       /* Horizontal offset for valid pixels */
+       {0x0003, 0x865c},       /* Vertical offset for valid lines */
+       /***************//* sensor active */
+       {0x0003, 0x8801},       /* 0x03 <- 0x01 0x21 //289 */
+       {0x0021, 0x8805},
+       {0x0001, 0x8800},
+       {0x0004, 0x8801},       /* 0x04 <- 0x01 0x65 //357 */
+       {0x0065, 0x8805},
+       {0x0001, 0x8800},
+       {0x0005, 0x8801},       /* 0x05 <- 0x2f */
+       {0x002f, 0x8805},
+       {0x0000, 0x8800},
+       {0x0006, 0x8801},       /* 0x06 <- 0 */
+       {0x0000, 0x8805},
+       {0x0000, 0x8800},
+       {0x000a, 0x8801},       /* 0x0a <- 2 */
+       {0x0002, 0x8805},
+       {0x0000, 0x8800},
+       {0x0009, 0x8801},       /* 0x09 <- 0x1061 */
+       {0x0061, 0x8805},
+       {0x0010, 0x8800},
+       {0x0035, 0x8801},       /* 0x35 <-0x14 */
+       {0x0014, 0x8805},
+       {0x0000, 0x8800},
+       {0x0030, 0x8112},       /* ISO and drop packet enable */
+       {0x0000, 0x8112},       /* Some kind of reset ???? */
+       {0x0009, 0x8118},       /* Enable sensor and set standby */
+       {0x0000, 0x8114},       /* Software GPIO output data */
+       {0x0000, 0x8114},       /* Software GPIO output data */
+       {0x0001, 0x8114},       /* Software GPIO output data */
+       {0x0000, 0x8112},       /* Some kind of reset ??? */
+       {0x0003, 0x8701},
+       {0x0001, 0x8703},
+       {0x0011, 0x8118},
+       {0x0001, 0x8118},
+       /***************/
+       {0x0092, 0x8804},
+       {0x0010, 0x8802},
+       {0x000d, 0x8805},
+       {0x0001, 0x8801},
+       {0x0000, 0x8800},
+       {0x0018, 0x8805},
+       {0x0002, 0x8801},
+       {0x0000, 0x8800},
+       {0x0065, 0x8805},
+       {0x0004, 0x8801},
+       {0x0001, 0x8800},
+       {0x0021, 0x8805},
+       {0x0005, 0x8801},
+       {0x0000, 0x8800},
+       {0x00aa, 0x8805},
+       {0x0007, 0x8801},       /* mode 0xaa */
+       {0x0000, 0x8800},
+       {0x0004, 0x8805},
+       {0x0020, 0x8801},
+       {0x0015, 0x8800},       /* mode 0x0415 */
+       {0x0002, 0x8805},
+       {0x0039, 0x8801},
+       {0x0000, 0x8800},
+       {0x0010, 0x8805},
+       {0x0035, 0x8801},
+       {0x0000, 0x8800},
+       {0x0049, 0x8805},
+       {0x0009, 0x8801},
+       {0x0010, 0x8800},
+       {0x000b, 0x8805},
+       {0x0028, 0x8801},
+       {0x0000, 0x8800},
+       {0x000f, 0x8805},
+       {0x003b, 0x8801},
+       {0x0000, 0x8800},
+       {0x0000, 0x8805},
+       {0x003c, 0x8801},
+       {0x0000, 0x8800},
+       {0x0002, 0x8502},
+       {0x0039, 0x8801},
+       {0x0000, 0x8805},
+       {0x0000, 0x8800},
+
+       {0x0087, 0x8700},       /* overwrite by start */
+       {0x0081, 0x8702},
+       {0x0000, 0x8500},
+/*     {0x0010, 0x8500},  -- Previous line was this */
+       {0x0002, 0x865b},
+       {0x0003, 0x865c},
+       /***************/
+       {0x0003, 0x8801},       /* 0x121-> 289 */
+       {0x0021, 0x8805},
+       {0x0001, 0x8800},
+       {0x0004, 0x8801},       /* 0x165 -> 357 */
+       {0x0065, 0x8805},
+       {0x0001, 0x8800},
+       {0x0005, 0x8801},       /* 0x2f //blanking control colonne */
+       {0x002f, 0x8805},
+       {0x0000, 0x8800},
+       {0x0006, 0x8801},       /* 0x00 //blanking mode row */
+       {0x0000, 0x8805},
+       {0x0000, 0x8800},
+       {0x000a, 0x8801},       /* 0x01 //0x02 */
+       {0x0001, 0x8805},
+       {0x0000, 0x8800},
+       {0x0009, 0x8801},       /* 0x1061 - setexposure times && pixel clock
+                                * 0001 0 | 000 0110 0001 */
+       {0x0061, 0x8805},       /* 61 31 */
+       {0x0008, 0x8800},       /* 08 */
+       {0x0035, 0x8801},       /* 0x14 - set gain general */
+       {0x001f, 0x8805},       /* 0x14 */
+       {0x0000, 0x8800},
+       {0x0030, 0x8112},
+       {}
+};
+
+static void sensor_reset(struct gspca_dev *gspca_dev)
+{
+       reg_w_val(gspca_dev->dev, 0x8631, 0xc8);
+       reg_w_val(gspca_dev->dev, 0x8634, 0xc8);
+       reg_w_val(gspca_dev->dev, 0x8112, 0x00);
+       reg_w_val(gspca_dev->dev, 0x8114, 0x00);
+       reg_w_val(gspca_dev->dev, 0x8118, 0x21);
+       i2c_init(gspca_dev, 0x14);
+       i2c_write(gspca_dev, 1, 0x0d);
+       i2c_write(gspca_dev, 0, 0x0d);
+}
+
+/******************** QC Express etch2 stuff ********************/
+static const __u16 Pb100_1map8300[][2] = {
+       /* reg, value */
+       {0x8320, 0x3304},
+
+       {0x8303, 0x0125},       /* image area */
+       {0x8304, 0x0169},
+       {0x8328, 0x000b},
+       {0x833c, 0x0001},
+
+       {0x832f, 0x0419},
+       {0x8307, 0x00aa},
+       {0x8301, 0x0003},
+       {0x8302, 0x000e},
+       {}
+};
+static const __u16 Pb100_2map8300[][2] = {
+       /* reg, value */
+       {0x8339, 0x0000},
+       {0x8307, 0x00aa},
+       {}
+};
+
+static const __u16 spca561_161rev12A_data1[][2] = {
+       {0x21, 0x8118},
+       {0x01, 0x8114},
+       {0x00, 0x8112},
+       {0x92, 0x8804},
+       {0x04, 0x8802},         /* windows uses 08 */
+       {}
+};
+static const __u16 spca561_161rev12A_data2[][2] = {
+       {0x21, 0x8118},
+       {0x10, 0x8500},
+       {0x07, 0x8601},
+       {0x07, 0x8602},
+       {0x04, 0x8501},
+       {0x21, 0x8118},
+
+       {0x07, 0x8201},         /* windows uses 02 */
+       {0x08, 0x8200},
+       {0x01, 0x8200},
+
+       {0x00, 0x8114},
+       {0x01, 0x8114},         /* windows uses 00 */
+
+       {0x90, 0x8604},
+       {0x00, 0x8605},
+       {0xb0, 0x8603},
+
+       /* sensor gains */
+       {0x00, 0x8610},         /* *red */
+       {0x00, 0x8611},         /* 3f   *green */
+       {0x00, 0x8612},         /* green *blue */
+       {0x00, 0x8613},         /* blue *green */
+       {0x35, 0x8614},         /* green *red */
+       {0x35, 0x8615},         /* 40   *green */
+       {0x35, 0x8616},         /* 7a   *blue */
+       {0x35, 0x8617},         /* 40   *green */
+
+       {0x0c, 0x8620},         /* 0c */
+       {0xc8, 0x8631},         /* c8 */
+       {0xc8, 0x8634},         /* c8 */
+       {0x23, 0x8635},         /* 23 */
+       {0x1f, 0x8636},         /* 1f */
+       {0xdd, 0x8637},         /* dd */
+       {0xe1, 0x8638},         /* e1 */
+       {0x1d, 0x8639},         /* 1d */
+       {0x21, 0x863a},         /* 21 */
+       {0xe3, 0x863b},         /* e3 */
+       {0xdf, 0x863c},         /* df */
+       {0xf0, 0x8505},
+       {0x32, 0x850a},
+       {}
+};
+
+static void sensor_mapwrite(struct gspca_dev *gspca_dev,
+                           const __u16 sensormap[][2])
+{
+       int i = 0;
+       __u8 usbval[2];
+
+       while (sensormap[i][0]) {
+               usbval[0] = sensormap[i][1];
+               usbval[1] = sensormap[i][1] >> 8;
+               reg_w_buf(gspca_dev, sensormap[i][0], usbval, 2);
+               i++;
+       }
+}
+static void init_161rev12A(struct gspca_dev *gspca_dev)
+{
+       sensor_reset(gspca_dev);
+       write_vector(gspca_dev, spca561_161rev12A_data1);
+       sensor_mapwrite(gspca_dev, Pb100_1map8300);
+       write_vector(gspca_dev, spca561_161rev12A_data2);
+       sensor_mapwrite(gspca_dev, Pb100_2map8300);
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+                    const struct usb_device_id *id)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       struct cam *cam;
+       __u16 vendor, product;
+       __u8 data1, data2;
+
+       /* Read frm global register the USB product and vendor IDs, just to
+        * prove that we can communicate with the device.  This works, which
+        * confirms at we are communicating properly and that the device
+        * is a 561. */
+       reg_r(gspca_dev, 0x8104, 1);
+       data1 = gspca_dev->usb_buf[0];
+       reg_r(gspca_dev, 0x8105, 1);
+       data2 = gspca_dev->usb_buf[0];
+       vendor = (data2 << 8) | data1;
+       reg_r(gspca_dev, 0x8106, 1);
+       data1 = gspca_dev->usb_buf[0];
+       reg_r(gspca_dev, 0x8107, 1);
+       data2 = gspca_dev->usb_buf[0];
+       product = (data2 << 8) | data1;
+       if (vendor != id->idVendor || product != id->idProduct) {
+               PDEBUG(D_PROBE, "Bad vendor / product from device");
+               return -EINVAL;
+       }
+       switch (product) {
+       case 0x0928:
+       case 0x0929:
+       case 0x092a:
+       case 0x092b:
+       case 0x092c:
+       case 0x092d:
+       case 0x092e:
+       case 0x092f:
+       case 0x403b:
+               sd->chip_revision = Rev012A;
+               break;
+       default:
+/*     case 0x0561:
+       case 0x0815:                    * ?? in spca508.c
+       case 0x401a:
+       case 0x7004:
+       case 0x7e50:
+       case 0xa001:
+       case 0xcdee: */
+               sd->chip_revision = Rev072A;
+               break;
+       }
+       cam = &gspca_dev->cam;
+       cam->dev_name = (char *) id->driver_info;
+       cam->epaddr = 0x01;
+       gspca_dev->nbalt = 7 + 1;       /* choose alternate 7 first */
+       cam->cam_mode = sif_mode;
+       cam->nmodes = sizeof sif_mode / sizeof sif_mode[0];
+       sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value;
+       sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value;
+       sd->autogain = sd_ctrls[SD_AUTOGAIN].qctrl.default_value;
+       return 0;
+}
+
+/* this function is called at open time */
+static int sd_open(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       switch (sd->chip_revision) {
+       case Rev072A:
+               PDEBUG(D_STREAM, "Chip revision id: 072a");
+               write_vector(gspca_dev, spca561_init_data);
+               break;
+       default:
+/*     case Rev012A: */
+               PDEBUG(D_STREAM, "Chip revision id: 012a");
+               init_161rev12A(gspca_dev);
+               break;
+       }
+       return 0;
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       struct usb_device *dev = gspca_dev->dev;
+       __u8 lowb;
+       int expotimes;
+
+       switch (sd->chip_revision) {
+       case Rev072A:
+               lowb = sd->contrast >> 8;
+               reg_w_val(dev, lowb, 0x8651);
+               reg_w_val(dev, lowb, 0x8652);
+               reg_w_val(dev, lowb, 0x8653);
+               reg_w_val(dev, lowb, 0x8654);
+               break;
+       case Rev012A: {
+               __u8 Reg8391[] =
+                       { 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00 };
+
+               /* Write camera sensor settings */
+               expotimes = (sd->contrast >> 5) & 0x07ff;
+               Reg8391[0] = expotimes & 0xff;  /* exposure */
+               Reg8391[1] = 0x18 | (expotimes >> 8);
+               Reg8391[2] = sd->brightness;    /* gain */
+               reg_w_buf(gspca_dev, 0x8391, Reg8391, 8);
+               reg_w_buf(gspca_dev, 0x8390, Reg8391, 8);
+               break;
+           }
+       }
+}
+
+static void sd_start(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       struct usb_device *dev = gspca_dev->dev;
+       int Clck;
+       __u8 Reg8307[] = { 0xaa, 0x00 };
+       int mode;
+
+       mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;
+       switch (sd->chip_revision) {
+       case Rev072A:
+               switch (mode) {
+               default:
+/*             case 0:
+               case 1: */
+                       Clck = 0x25;
+                       break;
+               case 2:
+                       Clck = 0x22;
+                       break;
+               case 3:
+                       Clck = 0x21;
+                       break;
+               }
+               reg_w_val(dev, 0x8500, mode);   /* mode */
+               reg_w_val(dev, 0x8700, Clck);   /* 0x27 clock */
+               reg_w_val(dev, 0x8112, 0x10 | 0x20);
+               break;
+       default:
+/*     case Rev012A: */
+               switch (mode) {
+               case 0:
+               case 1:
+                       Clck = 0x8a;
+                       break;
+               case 2:
+                       Clck = 0x85;
+                       break;
+               default:
+                       Clck = 0x83;
+                       break;
+               }
+               if (mode <= 1) {
+                       /* Use compression on 320x240 and above */
+                       reg_w_val(dev, 0x8500, 0x10 | mode);
+               } else {
+                       /* I couldn't get the compression to work below 320x240
+                        * Fortunately at these resolutions the bandwidth
+                        * is sufficient to push raw frames at ~20fps */
+                       reg_w_val(dev, 0x8500, mode);
+               }               /* -- qq@kuku.eu.org */
+               reg_w_buf(gspca_dev, 0x8307, Reg8307, 2);
+               reg_w_val(gspca_dev->dev, 0x8700, Clck);
+                                               /* 0x8f 0x85 0x27 clock */
+               reg_w_val(gspca_dev->dev, 0x8112, 0x1e | 0x20);
+               reg_w_val(gspca_dev->dev, 0x850b, 0x03);
+               setcontrast(gspca_dev);
+               break;
+       }
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+       reg_w_val(gspca_dev->dev, 0x8112, 0x20);
+}
+
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+}
+
+/* this function is called at close time */
+static void sd_close(struct gspca_dev *gspca_dev)
+{
+       reg_w_val(gspca_dev->dev, 0x8114, 0);
+}
+
+static void setautogain(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       int expotimes = 0;
+       int pixelclk = 0;
+       int gainG = 0;
+       __u8 R, Gr, Gb, B;
+       int y;
+       __u8 luma_mean = 110;
+       __u8 luma_delta = 20;
+       __u8 spring = 4;
+
+       switch (sd->chip_revision) {
+       case Rev072A:
+               reg_r(gspca_dev, 0x8621, 1);
+               Gr = gspca_dev->usb_buf[0];
+               reg_r(gspca_dev, 0x8622, 1);
+               R = gspca_dev->usb_buf[0];
+               reg_r(gspca_dev, 0x8623, 1);
+               B = gspca_dev->usb_buf[0];
+               reg_r(gspca_dev, 0x8624, 1);
+               Gb = gspca_dev->usb_buf[0];
+               y = (77 * R + 75 * (Gr + Gb) + 29 * B) >> 8;
+               /* u= (128*B-(43*(Gr+Gb+R))) >> 8; */
+               /* v= (128*R-(53*(Gr+Gb))-21*B) >> 8; */
+               /* PDEBUG(D_CONF,"reading Y %d U %d V %d ",y,u,v); */
+
+               if (y < luma_mean - luma_delta ||
+                   y > luma_mean + luma_delta) {
+                       expotimes = i2c_read(gspca_dev, 0x09, 0x10);
+                       pixelclk = 0x0800;
+                       expotimes = expotimes & 0x07ff;
+                       /* PDEBUG(D_PACK,
+                               "Exposition Times 0x%03X Clock 0x%04X ",
+                               expotimes,pixelclk); */
+                       gainG = i2c_read(gspca_dev, 0x35, 0x10);
+                       /* PDEBUG(D_PACK,
+                               "reading Gain register %d", gainG); */
+
+                       expotimes += (luma_mean - y) >> spring;
+                       gainG += (luma_mean - y) / 50;
+                       /* PDEBUG(D_PACK,
+                               "compute expotimes %d gain %d",
+                               expotimes,gainG); */
+
+                       if (gainG > 0x3f)
+                               gainG = 0x3f;
+                       else if (gainG < 4)
+                               gainG = 3;
+                       i2c_write(gspca_dev, gainG, 0x35);
+
+                       if (expotimes >= 0x0256)
+                               expotimes = 0x0256;
+                       else if (expotimes < 4)
+                               expotimes = 3;
+                       i2c_write(gspca_dev, expotimes | pixelclk, 0x09);
+               }
+               break;
+       case Rev012A:
+               /* sensor registers is access and memory mapped to 0x8300 */
+               /* readind all 0x83xx block the sensor */
+               /*
+                * The data from the header seem wrong where is the luma
+                * and chroma mean value
+                * at the moment set exposure in contrast set
+                */
+               break;
+       }
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+                       struct gspca_frame *frame, /* target */
+                       __u8 *data,             /* isoc packet */
+                       int len)                /* iso packet length */
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       switch (data[0]) {
+       case 0:         /* start of frame */
+               frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
+                                       data, 0);
+               if (sd->ag_cnt >= 0) {
+                       if (--sd->ag_cnt < 0) {
+                               sd->ag_cnt = AG_CNT_START;
+                               setautogain(gspca_dev);
+                       }
+               }
+               data += SPCA561_OFFSET_DATA;
+               len -= SPCA561_OFFSET_DATA;
+               if (data[1] & 0x10) {
+                       /* compressed bayer */
+                       gspca_frame_add(gspca_dev, FIRST_PACKET,
+                                       frame, data, len);
+               } else {
+                       /* raw bayer (with a header, which we skip) */
+                       data += 20;
+                       len -= 20;
+                       gspca_frame_add(gspca_dev, FIRST_PACKET,
+                                               frame, data, len);
+               }
+               return;
+       case 0xff:              /* drop */
+/*             gspca_dev->last_packet_type = DISCARD_PACKET; */
+               return;
+       }
+       data++;
+       len--;
+       gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       __u8 value;
+
+       switch (sd->chip_revision) {
+       case Rev072A:
+               value = sd->brightness;
+               reg_w_val(gspca_dev->dev, value, 0x8611);
+               reg_w_val(gspca_dev->dev, value, 0x8612);
+               reg_w_val(gspca_dev->dev, value, 0x8613);
+               reg_w_val(gspca_dev->dev, value, 0x8614);
+               break;
+       default:
+/*     case Rev012A: */
+               setcontrast(gspca_dev);
+               break;
+       }
+}
+
+static void getbrightness(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       __u16 tot;
+
+       switch (sd->chip_revision) {
+       case Rev072A:
+               tot = 0;
+               reg_r(gspca_dev, 0x8611, 1);
+               tot += gspca_dev->usb_buf[0];
+               reg_r(gspca_dev, 0x8612, 1);
+               tot += gspca_dev->usb_buf[0];
+               reg_r(gspca_dev, 0x8613, 1);
+               tot += gspca_dev->usb_buf[0];
+               reg_r(gspca_dev, 0x8614, 1);
+               tot += gspca_dev->usb_buf[0];
+               sd->brightness = tot >> 2;
+               break;
+       default:
+/*     case Rev012A: */
+               /* no way to read sensor settings */
+               break;
+       }
+}
+
+static void getcontrast(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       __u16 tot;
+
+       switch (sd->chip_revision) {
+       case Rev072A:
+               tot = 0;
+               reg_r(gspca_dev, 0x8651, 1);
+               tot += gspca_dev->usb_buf[0];
+               reg_r(gspca_dev, 0x8652, 1);
+               tot += gspca_dev->usb_buf[0];
+               reg_r(gspca_dev, 0x8653, 1);
+               tot += gspca_dev->usb_buf[0];
+               reg_r(gspca_dev, 0x8654, 1);
+               tot += gspca_dev->usb_buf[0];
+               sd->contrast = tot << 6;
+               break;
+       default:
+/*     case Rev012A: */
+               /* no way to read sensor settings */
+               break;
+       }
+       PDEBUG(D_CONF, "get contrast %d", sd->contrast);
+}
+
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->brightness = val;
+       if (gspca_dev->streaming)
+               setbrightness(gspca_dev);
+       return 0;
+}
+
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       getbrightness(gspca_dev);
+       *val = sd->brightness;
+       return 0;
+}
+
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->contrast = val;
+       if (gspca_dev->streaming)
+               setcontrast(gspca_dev);
+       return 0;
+}
+
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       getcontrast(gspca_dev);
+       *val = sd->contrast;
+       return 0;
+}
+
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->autogain = val;
+       if (val)
+               sd->ag_cnt = AG_CNT_START;
+       else
+               sd->ag_cnt = -1;
+       return 0;
+}
+
+static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->autogain;
+       return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+       .name = MODULE_NAME,
+       .ctrls = sd_ctrls,
+       .nctrls = ARRAY_SIZE(sd_ctrls),
+       .config = sd_config,
+       .open = sd_open,
+       .start = sd_start,
+       .stopN = sd_stopN,
+       .stop0 = sd_stop0,
+       .close = sd_close,
+       .pkt_scan = sd_pkt_scan,
+};
+
+/* -- module initialisation -- */
+#define DVNM(name) .driver_info = (kernel_ulong_t) name
+static const __devinitdata struct usb_device_id device_table[] = {
+       {USB_DEVICE(0x041e, 0x401a), DVNM("Creative Webcam Vista (PD1100)")},
+       {USB_DEVICE(0x041e, 0x403b),  DVNM("Creative Webcam Vista (VF0010)")},
+       {USB_DEVICE(0x0458, 0x7004), DVNM("Genius VideoCAM Express V2")},
+       {USB_DEVICE(0x046d, 0x0928), DVNM("Logitech QC Express Etch2")},
+       {USB_DEVICE(0x046d, 0x0929), DVNM("Labtec Webcam Elch2")},
+       {USB_DEVICE(0x046d, 0x092a), DVNM("Logitech QC for Notebook")},
+       {USB_DEVICE(0x046d, 0x092b), DVNM("Labtec Webcam Plus")},
+       {USB_DEVICE(0x046d, 0x092c), DVNM("Logitech QC chat Elch2")},
+       {USB_DEVICE(0x046d, 0x092d), DVNM("Logitech QC Elch2")},
+       {USB_DEVICE(0x046d, 0x092e), DVNM("Logitech QC Elch2")},
+       {USB_DEVICE(0x046d, 0x092f), DVNM("Logitech QC Elch2")},
+       {USB_DEVICE(0x04fc, 0x0561), DVNM("Flexcam 100")},
+       {USB_DEVICE(0x060b, 0xa001), DVNM("Maxell Compact Pc PM3")},
+       {USB_DEVICE(0x10fd, 0x7e50), DVNM("FlyCam Usb 100")},
+       {USB_DEVICE(0xabcd, 0xcdee), DVNM("Petcam")},
+       {}
+};
+
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+                   const struct usb_device_id *id)
+{
+       return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+                              THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+       .name = MODULE_NAME,
+       .id_table = device_table,
+       .probe = sd_probe,
+       .disconnect = gspca_disconnect,
+};
+
+/* -- module insert / remove -- */
+static int __init sd_mod_init(void)
+{
+       if (usb_register(&sd_driver) < 0)
+               return -1;
+       PDEBUG(D_PROBE, "v%s registered", version);
+       return 0;
+}
+static void __exit sd_mod_exit(void)
+{
+       usb_deregister(&sd_driver);
+       PDEBUG(D_PROBE, "deregistered");
+}
+
+module_init(sd_mod_init);
+module_exit(sd_mod_exit);
diff --git a/drivers/media/video/gspca/stk014.c b/drivers/media/video/gspca/stk014.c
new file mode 100644 (file)
index 0000000..c78ee0d
--- /dev/null
@@ -0,0 +1,592 @@
+/*
+ * Syntek DV4000 (STK014) subdriver
+ *
+ * Copyright (C) 2008 Jean-Francois Moine (http://moinejf.free.fr)
+ *
+ * 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
+ * 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
+ */
+
+#define MODULE_NAME "stk014"
+
+#include "gspca.h"
+#include "jpeg.h"
+
+#define DRIVER_VERSION_NUMBER  KERNEL_VERSION(2, 1, 7)
+static const char version[] = "2.1.7";
+
+MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>");
+MODULE_DESCRIPTION("Syntek DV4000 (STK014) USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* specific webcam descriptor */
+struct sd {
+       struct gspca_dev gspca_dev;     /* !! must be the first item */
+
+       unsigned char brightness;
+       unsigned char contrast;
+       unsigned char colors;
+       unsigned char lightfreq;
+};
+
+/* global parameters */
+static int sd_quant = 7;               /* <= 4 KO - 7: good (enough!) */
+
+/* V4L2 controls supported by the driver */
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val);
+
+static struct ctrl sd_ctrls[] = {
+       {
+           {
+               .id      = V4L2_CID_BRIGHTNESS,
+               .type    = V4L2_CTRL_TYPE_INTEGER,
+               .name    = "Brightness",
+               .minimum = 0,
+               .maximum = 255,
+               .step    = 1,
+#define BRIGHTNESS_DEF 127
+               .default_value = BRIGHTNESS_DEF,
+           },
+           .set = sd_setbrightness,
+           .get = sd_getbrightness,
+       },
+       {
+           {
+               .id      = V4L2_CID_CONTRAST,
+               .type    = V4L2_CTRL_TYPE_INTEGER,
+               .name    = "Contrast",
+               .minimum = 0,
+               .maximum = 255,
+               .step    = 1,
+#define CONTRAST_DEF 127
+               .default_value = CONTRAST_DEF,
+           },
+           .set = sd_setcontrast,
+           .get = sd_getcontrast,
+       },
+       {
+           {
+               .id      = V4L2_CID_SATURATION,
+               .type    = V4L2_CTRL_TYPE_INTEGER,
+               .name    = "Color",
+               .minimum = 0,
+               .maximum = 255,
+               .step    = 1,
+#define COLOR_DEF 127
+               .default_value = COLOR_DEF,
+           },
+           .set = sd_setcolors,
+           .get = sd_getcolors,
+       },
+       {
+           {
+               .id      = V4L2_CID_POWER_LINE_FREQUENCY,
+               .type    = V4L2_CTRL_TYPE_MENU,
+               .name    = "Light frequency filter",
+               .minimum = 1,
+               .maximum = 2,   /* 0: 0, 1: 50Hz, 2:60Hz */
+               .step    = 1,
+#define FREQ_DEF 1
+               .default_value = FREQ_DEF,
+           },
+           .set = sd_setfreq,
+           .get = sd_getfreq,
+       },
+};
+
+static struct v4l2_pix_format vga_mode[] = {
+       {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+               .bytesperline = 320,
+               .sizeimage = 320 * 240 * 3 / 8 + 590,
+               .colorspace = V4L2_COLORSPACE_JPEG,
+               .priv = 1},
+       {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+               .bytesperline = 640,
+               .sizeimage = 640 * 480 * 3 / 8 + 590,
+               .colorspace = V4L2_COLORSPACE_JPEG,
+               .priv = 0},
+};
+
+/* -- read a register -- */
+static int reg_r(struct gspca_dev *gspca_dev,
+                       __u16 index)
+{
+       struct usb_device *dev = gspca_dev->dev;
+       int ret;
+
+       ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+                       0x00,
+                       USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                       0x00,
+                       index,
+                       gspca_dev->usb_buf, 1,
+                       500);
+       if (ret < 0) {
+               PDEBUG(D_ERR, "reg_r err %d", ret);
+               return ret;
+       }
+       return gspca_dev->usb_buf[0];
+}
+
+/* -- write a register -- */
+static int reg_w(struct gspca_dev *gspca_dev,
+                       __u16 index, __u16 value)
+{
+       struct usb_device *dev = gspca_dev->dev;
+       int ret;
+
+       ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+                       0x01,
+                       USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                       value,
+                       index,
+                       NULL,
+                       0,
+                       500);
+       if (ret < 0)
+               PDEBUG(D_ERR, "reg_w err %d", ret);
+       return ret;
+}
+
+/* -- get a bulk value (4 bytes) -- */
+static int rcv_val(struct gspca_dev *gspca_dev,
+                       int ads)
+{
+       struct usb_device *dev = gspca_dev->dev;
+       int alen, ret;
+
+       reg_w(gspca_dev, 0x634, (ads >> 16) & 0xff);
+       reg_w(gspca_dev, 0x635, (ads >> 8) & 0xff);
+       reg_w(gspca_dev, 0x636, ads & 0xff);
+       reg_w(gspca_dev, 0x637, 0);
+       reg_w(gspca_dev, 0x638, 4);     /* len & 0xff */
+       reg_w(gspca_dev, 0x639, 0);     /* len >> 8 */
+       reg_w(gspca_dev, 0x63a, 0);
+       reg_w(gspca_dev, 0x63b, 0);
+       reg_w(gspca_dev, 0x630, 5);
+       ret = usb_bulk_msg(dev,
+                       usb_rcvbulkpipe(dev, 5),
+                       gspca_dev->usb_buf,
+                       4,              /* length */
+                       &alen,
+                       500);           /* timeout in milliseconds */
+       return ret;
+}
+
+/* -- send a bulk value -- */
+static int snd_val(struct gspca_dev *gspca_dev,
+                       int ads,
+                       unsigned int val)
+{
+       struct usb_device *dev = gspca_dev->dev;
+       int alen, ret;
+       __u8 seq = 0;
+
+       if (ads == 0x003f08) {
+               ret = reg_r(gspca_dev, 0x0704);
+               if (ret < 0)
+                       goto ko;
+               ret = reg_r(gspca_dev, 0x0705);
+               if (ret < 0)
+                       goto ko;
+               seq = ret;              /* keep the sequence number */
+               ret = reg_r(gspca_dev, 0x0650);
+               if (ret < 0)
+                       goto ko;
+               reg_w(gspca_dev, 0x654, seq);
+       } else {
+               reg_w(gspca_dev, 0x654, (ads >> 16) & 0xff);
+       }
+       reg_w(gspca_dev, 0x655, (ads >> 8) & 0xff);
+       reg_w(gspca_dev, 0x656, ads & 0xff);
+       reg_w(gspca_dev, 0x657, 0);
+       reg_w(gspca_dev, 0x658, 0x04);  /* size */
+       reg_w(gspca_dev, 0x659, 0);
+       reg_w(gspca_dev, 0x65a, 0);
+       reg_w(gspca_dev, 0x65b, 0);
+       reg_w(gspca_dev, 0x650, 5);
+       gspca_dev->usb_buf[0] = val >> 24;
+       gspca_dev->usb_buf[1] = val >> 16;
+       gspca_dev->usb_buf[2] = val >> 8;
+       gspca_dev->usb_buf[3] = val;
+       ret = usb_bulk_msg(dev,
+                       usb_sndbulkpipe(dev, 6),
+                       gspca_dev->usb_buf,
+                       4,
+                       &alen,
+                       500);   /* timeout in milliseconds */
+       if (ret < 0)
+               goto ko;
+       if (ads == 0x003f08) {
+               seq += 4;
+               seq &= 0x3f;
+               reg_w(gspca_dev, 0x705, seq);
+       }
+       return ret;
+ko:
+       PDEBUG(D_ERR, "snd_val err %d", ret);
+       return ret;
+}
+
+/* set a camera parameter */
+static int set_par(struct gspca_dev *gspca_dev,
+                  int parval)
+{
+       return snd_val(gspca_dev, 0x003f08, parval);
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       int parval;
+
+       parval = 0x06000000             /* whiteness */
+               + (sd->brightness << 16);
+       set_par(gspca_dev, parval);
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       int parval;
+
+       parval = 0x07000000             /* contrast */
+               + (sd->contrast << 16);
+       set_par(gspca_dev, parval);
+}
+
+static void setcolors(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       int parval;
+
+       parval = 0x08000000             /* saturation */
+               + (sd->colors << 16);
+       set_par(gspca_dev, parval);
+}
+
+static void setfreq(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       set_par(gspca_dev, sd->lightfreq == 1
+                       ? 0x33640000            /* 50 Hz */
+                       : 0x33780000);          /* 60 Hz */
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+                       const struct usb_device_id *id)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       struct cam *cam = &gspca_dev->cam;
+
+       cam->dev_name = (char *) id->driver_info;
+       cam->epaddr = 0x02;
+       gspca_dev->cam.cam_mode = vga_mode;
+       gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode);
+       sd->brightness = BRIGHTNESS_DEF;
+       sd->contrast = CONTRAST_DEF;
+       sd->colors = COLOR_DEF;
+       sd->lightfreq = FREQ_DEF;
+       return 0;
+}
+
+/* this function is called at open time */
+static int sd_open(struct gspca_dev *gspca_dev)
+{
+       int ret;
+
+       /* check if the device responds */
+       usb_set_interface(gspca_dev->dev, gspca_dev->iface, 1);
+       ret = reg_r(gspca_dev, 0x0740);
+       if (ret < 0)
+               return ret;
+       if (ret != 0xff) {
+               PDEBUG(D_ERR|D_STREAM, "init reg: 0x%02x", ret);
+               return -1;
+       }
+       return 0;
+}
+
+/* -- start the camera -- */
+static void sd_start(struct gspca_dev *gspca_dev)
+{
+       int ret, value;
+
+       /* work on alternate 1 */
+       usb_set_interface(gspca_dev->dev, gspca_dev->iface, 1);
+
+       set_par(gspca_dev, 0x10000000);
+       set_par(gspca_dev, 0x00000000);
+       set_par(gspca_dev, 0x8002e001);
+       set_par(gspca_dev, 0x14000000);
+       if (gspca_dev->width > 320)
+               value = 0x8002e001;             /* 640x480 */
+       else
+               value = 0x4001f000;             /* 320x240 */
+       set_par(gspca_dev, value);
+       ret = usb_set_interface(gspca_dev->dev,
+                                       gspca_dev->iface,
+                                       gspca_dev->alt);
+       if (ret < 0) {
+               PDEBUG(D_ERR|D_STREAM, "set intf %d %d failed",
+                       gspca_dev->iface, gspca_dev->alt);
+               goto out;
+       }
+       ret = reg_r(gspca_dev, 0x0630);
+       if (ret < 0)
+               goto out;
+       rcv_val(gspca_dev, 0x000020);   /* << (value ff ff ff ff) */
+       ret = reg_r(gspca_dev, 0x0650);
+       if (ret < 0)
+               goto out;
+       snd_val(gspca_dev, 0x000020, 0xffffffff);
+       reg_w(gspca_dev, 0x0620, 0);
+       reg_w(gspca_dev, 0x0630, 0);
+       reg_w(gspca_dev, 0x0640, 0);
+       reg_w(gspca_dev, 0x0650, 0);
+       reg_w(gspca_dev, 0x0660, 0);
+       setbrightness(gspca_dev);               /* whiteness */
+       setcontrast(gspca_dev);                 /* contrast */
+       setcolors(gspca_dev);                   /* saturation */
+       set_par(gspca_dev, 0x09800000);         /* Red ? */
+       set_par(gspca_dev, 0x0a800000);         /* Green ? */
+       set_par(gspca_dev, 0x0b800000);         /* Blue ? */
+       set_par(gspca_dev, 0x0d030000);         /* Gamma ? */
+       setfreq(gspca_dev);                     /* light frequency */
+
+       /* start the video flow */
+       set_par(gspca_dev, 0x01000000);
+       set_par(gspca_dev, 0x01000000);
+       PDEBUG(D_STREAM, "camera started alt: 0x%02x", gspca_dev->alt);
+       return;
+out:
+       PDEBUG(D_ERR|D_STREAM, "camera start err %d", ret);
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+       struct usb_device *dev = gspca_dev->dev;
+
+       set_par(gspca_dev, 0x02000000);
+       set_par(gspca_dev, 0x02000000);
+       usb_set_interface(dev, gspca_dev->iface, 1);
+       reg_r(gspca_dev, 0x0630);
+       rcv_val(gspca_dev, 0x000020);   /* << (value ff ff ff ff) */
+       reg_r(gspca_dev, 0x0650);
+       snd_val(gspca_dev, 0x000020, 0xffffffff);
+       reg_w(gspca_dev, 0x0620, 0);
+       reg_w(gspca_dev, 0x0630, 0);
+       reg_w(gspca_dev, 0x0640, 0);
+       reg_w(gspca_dev, 0x0650, 0);
+       reg_w(gspca_dev, 0x0660, 0);
+       PDEBUG(D_STREAM, "camera stopped");
+}
+
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+}
+
+static void sd_close(struct gspca_dev *gspca_dev)
+{
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+                       struct gspca_frame *frame,      /* target */
+                       __u8 *data,                     /* isoc packet */
+                       int len)                        /* iso packet length */
+{
+       static unsigned char ffd9[] = {0xff, 0xd9};
+
+       /* a frame starts with:
+        *      - 0xff 0xfe
+        *      - 0x08 0x00     - length (little endian ?!)
+        *      - 4 bytes = size of whole frame (BE - including header)
+        *      - 0x00 0x0c
+        *      - 0xff 0xd8
+        *      - ..    JPEG image with escape sequences (ff 00)
+        *              (without ending - ff d9)
+        */
+       if (data[0] == 0xff && data[1] == 0xfe) {
+               frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
+                                       ffd9, 2);
+
+               /* put the JPEG 411 header */
+               jpeg_put_header(gspca_dev, frame, sd_quant, 0x22);
+
+               /* beginning of the frame */
+#define STKHDRSZ 12
+               gspca_frame_add(gspca_dev, INTER_PACKET, frame,
+                               data + STKHDRSZ, len - STKHDRSZ);
+#undef STKHDRSZ
+               return;
+       }
+       gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
+}
+
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->brightness = val;
+       if (gspca_dev->streaming)
+               setbrightness(gspca_dev);
+       return 0;
+}
+
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->brightness;
+       return 0;
+}
+
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->contrast = val;
+       if (gspca_dev->streaming)
+               setcontrast(gspca_dev);
+       return 0;
+}
+
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->contrast;
+       return 0;
+}
+
+static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->colors = val;
+       if (gspca_dev->streaming)
+               setcolors(gspca_dev);
+       return 0;
+}
+
+static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->colors;
+       return 0;
+}
+
+static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->lightfreq = val;
+       if (gspca_dev->streaming)
+               setfreq(gspca_dev);
+       return 0;
+}
+
+static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->lightfreq;
+       return 0;
+}
+
+static int sd_querymenu(struct gspca_dev *gspca_dev,
+                       struct v4l2_querymenu *menu)
+{
+       switch (menu->id) {
+       case V4L2_CID_POWER_LINE_FREQUENCY:
+               switch (menu->index) {
+               case 1:         /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */
+                       strcpy((char *) menu->name, "50 Hz");
+                       return 0;
+               case 2:         /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */
+                       strcpy((char *) menu->name, "60 Hz");
+                       return 0;
+               }
+               break;
+       }
+       return -EINVAL;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+       .name = MODULE_NAME,
+       .ctrls = sd_ctrls,
+       .nctrls = ARRAY_SIZE(sd_ctrls),
+       .config = sd_config,
+       .open = sd_open,
+       .start = sd_start,
+       .stopN = sd_stopN,
+       .stop0 = sd_stop0,
+       .close = sd_close,
+       .pkt_scan = sd_pkt_scan,
+       .querymenu = sd_querymenu,
+};
+
+/* -- module initialisation -- */
+#define DVNM(name) .driver_info = (kernel_ulong_t) name
+static const __devinitdata struct usb_device_id device_table[] = {
+       {USB_DEVICE(0x05e1, 0x0893), DVNM("Syntek DV4000")},
+       {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+                       const struct usb_device_id *id)
+{
+       return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+                               THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+       .name = MODULE_NAME,
+       .id_table = device_table,
+       .probe = sd_probe,
+       .disconnect = gspca_disconnect,
+};
+
+/* -- module insert / remove -- */
+static int __init sd_mod_init(void)
+{
+       if (usb_register(&sd_driver) < 0)
+               return -1;
+       info("v%s registered", version);
+       return 0;
+}
+static void __exit sd_mod_exit(void)
+{
+       usb_deregister(&sd_driver);
+       info("deregistered");
+}
+
+module_init(sd_mod_init);
+module_exit(sd_mod_exit);
+
+module_param_named(quant, sd_quant, int, 0644);
+MODULE_PARM_DESC(quant, "Quantization index (0..8)");
diff --git a/drivers/media/video/gspca/sunplus.c b/drivers/media/video/gspca/sunplus.c
new file mode 100644 (file)
index 0000000..abd7bef
--- /dev/null
@@ -0,0 +1,1677 @@
+/*
+ *             Sunplus spca504(abc) spca533 spca536 library
+ *             Copyright (C) 2005 Michel Xhaard mxhaard@magic.fr
+ *
+ * V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
+ *
+ * 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
+ * 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
+ */
+
+#define MODULE_NAME "sunplus"
+
+#include "gspca.h"
+#include "jpeg.h"
+
+#define DRIVER_VERSION_NUMBER  KERNEL_VERSION(2, 1, 8)
+static const char version[] = "2.1.8";
+
+MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
+MODULE_DESCRIPTION("GSPCA/SPCA5xx USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* specific webcam descriptor */
+struct sd {
+       struct gspca_dev gspca_dev;     /* !! must be the first item */
+
+       __u8 packet[ISO_MAX_SIZE + 128];
+                               /* !! no more than 128 ff in an ISO packet */
+
+       unsigned char brightness;
+       unsigned char contrast;
+       unsigned char colors;
+       unsigned char autogain;
+
+       char qindex;
+       char bridge;
+#define BRIDGE_SPCA504 0
+#define BRIDGE_SPCA504B 1
+#define BRIDGE_SPCA504C 2
+#define BRIDGE_SPCA533 3
+#define BRIDGE_SPCA536 4
+       char subtype;
+#define AiptekMiniPenCam13 1
+#define LogitechClickSmart420 2
+#define LogitechClickSmart820 3
+#define MegapixV4 4
+};
+
+/* V4L2 controls supported by the driver */
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
+
+static struct ctrl sd_ctrls[] = {
+#define SD_BRIGHTNESS 0
+       {
+           {
+               .id      = V4L2_CID_BRIGHTNESS,
+               .type    = V4L2_CTRL_TYPE_INTEGER,
+               .name    = "Brightness",
+               .minimum = 0,
+               .maximum = 0xff,
+               .step    = 1,
+               .default_value = 0,
+           },
+           .set = sd_setbrightness,
+           .get = sd_getbrightness,
+       },
+#define SD_CONTRAST 1
+       {
+           {
+               .id      = V4L2_CID_CONTRAST,
+               .type    = V4L2_CTRL_TYPE_INTEGER,
+               .name    = "Contrast",
+               .minimum = 0,
+               .maximum = 0xff,
+               .step    = 1,
+               .default_value = 0x20,
+           },
+           .set = sd_setcontrast,
+           .get = sd_getcontrast,
+       },
+#define SD_COLOR 2
+       {
+           {
+               .id      = V4L2_CID_SATURATION,
+               .type    = V4L2_CTRL_TYPE_INTEGER,
+               .name    = "Color",
+               .minimum = 0,
+               .maximum = 0xff,
+               .step    = 1,
+               .default_value = 0x1a,
+           },
+           .set = sd_setcolors,
+           .get = sd_getcolors,
+       },
+#define SD_AUTOGAIN 3
+       {
+           {
+               .id      = V4L2_CID_AUTOGAIN,
+               .type    = V4L2_CTRL_TYPE_BOOLEAN,
+               .name    = "Auto Gain",
+               .minimum = 0,
+               .maximum = 1,
+               .step    = 1,
+               .default_value = 1,
+           },
+           .set = sd_setautogain,
+           .get = sd_getautogain,
+       },
+};
+
+static struct v4l2_pix_format vga_mode[] = {
+       {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+               .bytesperline = 320,
+               .sizeimage = 320 * 240 * 3 / 8 + 590,
+               .colorspace = V4L2_COLORSPACE_JPEG,
+               .priv = 2},
+       {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+               .bytesperline = 640,
+               .sizeimage = 640 * 480 * 3 / 8 + 590,
+               .colorspace = V4L2_COLORSPACE_JPEG,
+               .priv = 1},
+};
+
+static struct v4l2_pix_format custom_mode[] = {
+       {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+               .bytesperline = 320,
+               .sizeimage = 320 * 240 * 3 / 8 + 590,
+               .colorspace = V4L2_COLORSPACE_JPEG,
+               .priv = 2},
+       {464, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+               .bytesperline = 464,
+               .sizeimage = 464 * 480 * 3 / 8 + 590,
+               .colorspace = V4L2_COLORSPACE_JPEG,
+               .priv = 1},
+};
+
+static struct v4l2_pix_format vga_mode2[] = {
+       {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+               .bytesperline = 176,
+               .sizeimage = 176 * 144 * 3 / 8 + 590,
+               .colorspace = V4L2_COLORSPACE_JPEG,
+               .priv = 4},
+       {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+               .bytesperline = 320,
+               .sizeimage = 320 * 240 * 3 / 8 + 590,
+               .colorspace = V4L2_COLORSPACE_JPEG,
+               .priv = 3},
+       {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+               .bytesperline = 352,
+               .sizeimage = 352 * 288 * 3 / 8 + 590,
+               .colorspace = V4L2_COLORSPACE_JPEG,
+               .priv = 2},
+       {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+               .bytesperline = 640,
+               .sizeimage = 640 * 480 * 3 / 8 + 590,
+               .colorspace = V4L2_COLORSPACE_JPEG,
+               .priv = 1},
+};
+
+#define SPCA50X_OFFSET_DATA 10
+#define SPCA504_PCCAM600_OFFSET_SNAPSHOT 3
+#define SPCA504_PCCAM600_OFFSET_COMPRESS 4
+#define SPCA504_PCCAM600_OFFSET_MODE    5
+#define SPCA504_PCCAM600_OFFSET_DATA    14
+ /* Frame packet header offsets for the spca533 */
+#define SPCA533_OFFSET_DATA      16
+#define SPCA533_OFFSET_FRAMSEQ 15
+/* Frame packet header offsets for the spca536 */
+#define SPCA536_OFFSET_DATA      4
+#define SPCA536_OFFSET_FRAMSEQ  1
+
+/* Initialisation data for the Creative PC-CAM 600 */
+static const __u16 spca504_pccam600_init_data[][3] = {
+/*     {0xa0, 0x0000, 0x0503},  * capture mode */
+       {0x00, 0x0000, 0x2000},
+       {0x00, 0x0013, 0x2301},
+       {0x00, 0x0003, 0x2000},
+       {0x00, 0x0001, 0x21ac},
+       {0x00, 0x0001, 0x21a6},
+       {0x00, 0x0000, 0x21a7}, /* brightness */
+       {0x00, 0x0020, 0x21a8}, /* contrast */
+       {0x00, 0x0001, 0x21ac}, /* sat/hue */
+       {0x00, 0x0000, 0x21ad}, /* hue */
+       {0x00, 0x001a, 0x21ae}, /* saturation */
+       {0x00, 0x0002, 0x21a3}, /* gamma */
+       {0x30, 0x0154, 0x0008},
+       {0x30, 0x0004, 0x0006},
+       {0x30, 0x0258, 0x0009},
+       {0x30, 0x0004, 0x0000},
+       {0x30, 0x0093, 0x0004},
+       {0x30, 0x0066, 0x0005},
+       {0x00, 0x0000, 0x2000},
+       {0x00, 0x0013, 0x2301},
+       {0x00, 0x0003, 0x2000},
+       {0x00, 0x0013, 0x2301},
+       {0x00, 0x0003, 0x2000},
+       {}
+};
+
+/* Creative PC-CAM 600 specific open data, sent before using the
+ * generic initialisation data from spca504_open_data.
+ */
+static const __u16 spca504_pccam600_open_data[][3] = {
+       {0x00, 0x0001, 0x2501},
+       {0x20, 0x0500, 0x0001}, /* snapshot mode */
+       {0x00, 0x0003, 0x2880},
+       {0x00, 0x0001, 0x2881},
+       {}
+};
+
+/* Initialisation data for the logitech clicksmart 420 */
+static const __u16 spca504A_clicksmart420_init_data[][3] = {
+/*     {0xa0, 0x0000, 0x0503},  * capture mode */
+       {0x00, 0x0000, 0x2000},
+       {0x00, 0x0013, 0x2301},
+       {0x00, 0x0003, 0x2000},
+       {0x00, 0x0001, 0x21ac},
+       {0x00, 0x0001, 0x21a6},
+       {0x00, 0x0000, 0x21a7}, /* brightness */
+       {0x00, 0x0020, 0x21a8}, /* contrast */
+       {0x00, 0x0001, 0x21ac}, /* sat/hue */
+       {0x00, 0x0000, 0x21ad}, /* hue */
+       {0x00, 0x001a, 0x21ae}, /* saturation */
+       {0x00, 0x0002, 0x21a3}, /* gamma */
+       {0x30, 0x0004, 0x000a},
+       {0xb0, 0x0001, 0x0000},
+
+
+       {0x0a1, 0x0080, 0x0001},
+       {0x30, 0x0049, 0x0000},
+       {0x30, 0x0060, 0x0005},
+       {0x0c, 0x0004, 0x0000},
+       {0x00, 0x0000, 0x0000},
+       {0x00, 0x0000, 0x2000},
+       {0x00, 0x0013, 0x2301},
+       {0x00, 0x0003, 0x2000},
+       {0x00, 0x0000, 0x2000},
+
+       {}
+};
+
+/* clicksmart 420 open data ? */
+static const __u16 spca504A_clicksmart420_open_data[][3] = {
+       {0x00, 0x0001, 0x2501},
+       {0x20, 0x0502, 0x0000},
+       {0x06, 0x0000, 0x0000},
+       {0x00, 0x0004, 0x2880},
+       {0x00, 0x0001, 0x2881},
+/* look like setting a qTable */
+       {0x00, 0x0006, 0x2800},
+       {0x00, 0x0004, 0x2801},
+       {0x00, 0x0004, 0x2802},
+       {0x00, 0x0006, 0x2803},
+       {0x00, 0x000a, 0x2804},
+       {0x00, 0x0010, 0x2805},
+       {0x00, 0x0014, 0x2806},
+       {0x00, 0x0018, 0x2807},
+       {0x00, 0x0005, 0x2808},
+       {0x00, 0x0005, 0x2809},
+       {0x00, 0x0006, 0x280a},
+       {0x00, 0x0008, 0x280b},
+       {0x00, 0x000a, 0x280c},
+       {0x00, 0x0017, 0x280d},
+       {0x00, 0x0018, 0x280e},
+       {0x00, 0x0016, 0x280f},
+
+       {0x00, 0x0006, 0x2810},
+       {0x00, 0x0005, 0x2811},
+       {0x00, 0x0006, 0x2812},
+       {0x00, 0x000a, 0x2813},
+       {0x00, 0x0010, 0x2814},
+       {0x00, 0x0017, 0x2815},
+       {0x00, 0x001c, 0x2816},
+       {0x00, 0x0016, 0x2817},
+       {0x00, 0x0006, 0x2818},
+       {0x00, 0x0007, 0x2819},
+       {0x00, 0x0009, 0x281a},
+       {0x00, 0x000c, 0x281b},
+       {0x00, 0x0014, 0x281c},
+       {0x00, 0x0023, 0x281d},
+       {0x00, 0x0020, 0x281e},
+       {0x00, 0x0019, 0x281f},
+
+       {0x00, 0x0007, 0x2820},
+       {0x00, 0x0009, 0x2821},
+       {0x00, 0x000f, 0x2822},
+       {0x00, 0x0016, 0x2823},
+       {0x00, 0x001b, 0x2824},
+       {0x00, 0x002c, 0x2825},
+       {0x00, 0x0029, 0x2826},
+       {0x00, 0x001f, 0x2827},
+       {0x00, 0x000a, 0x2828},
+       {0x00, 0x000e, 0x2829},
+       {0x00, 0x0016, 0x282a},
+       {0x00, 0x001a, 0x282b},
+       {0x00, 0x0020, 0x282c},
+       {0x00, 0x002a, 0x282d},
+       {0x00, 0x002d, 0x282e},
+       {0x00, 0x0025, 0x282f},
+
+       {0x00, 0x0014, 0x2830},
+       {0x00, 0x001a, 0x2831},
+       {0x00, 0x001f, 0x2832},
+       {0x00, 0x0023, 0x2833},
+       {0x00, 0x0029, 0x2834},
+       {0x00, 0x0030, 0x2835},
+       {0x00, 0x0030, 0x2836},
+       {0x00, 0x0028, 0x2837},
+       {0x00, 0x001d, 0x2838},
+       {0x00, 0x0025, 0x2839},
+       {0x00, 0x0026, 0x283a},
+       {0x00, 0x0027, 0x283b},
+       {0x00, 0x002d, 0x283c},
+       {0x00, 0x0028, 0x283d},
+       {0x00, 0x0029, 0x283e},
+       {0x00, 0x0028, 0x283f},
+
+       {0x00, 0x0007, 0x2840},
+       {0x00, 0x0007, 0x2841},
+       {0x00, 0x000a, 0x2842},
+       {0x00, 0x0013, 0x2843},
+       {0x00, 0x0028, 0x2844},
+       {0x00, 0x0028, 0x2845},
+       {0x00, 0x0028, 0x2846},
+       {0x00, 0x0028, 0x2847},
+       {0x00, 0x0007, 0x2848},
+       {0x00, 0x0008, 0x2849},
+       {0x00, 0x000a, 0x284a},
+       {0x00, 0x001a, 0x284b},
+       {0x00, 0x0028, 0x284c},
+       {0x00, 0x0028, 0x284d},
+       {0x00, 0x0028, 0x284e},
+       {0x00, 0x0028, 0x284f},
+
+       {0x00, 0x000a, 0x2850},
+       {0x00, 0x000a, 0x2851},
+       {0x00, 0x0016, 0x2852},
+       {0x00, 0x0028, 0x2853},
+       {0x00, 0x0028, 0x2854},
+       {0x00, 0x0028, 0x2855},
+       {0x00, 0x0028, 0x2856},
+       {0x00, 0x0028, 0x2857},
+       {0x00, 0x0013, 0x2858},
+       {0x00, 0x001a, 0x2859},
+       {0x00, 0x0028, 0x285a},
+       {0x00, 0x0028, 0x285b},
+       {0x00, 0x0028, 0x285c},
+       {0x00, 0x0028, 0x285d},
+       {0x00, 0x0028, 0x285e},
+       {0x00, 0x0028, 0x285f},
+
+       {0x00, 0x0028, 0x2860},
+       {0x00, 0x0028, 0x2861},
+       {0x00, 0x0028, 0x2862},
+       {0x00, 0x0028, 0x2863},
+       {0x00, 0x0028, 0x2864},
+       {0x00, 0x0028, 0x2865},
+       {0x00, 0x0028, 0x2866},
+       {0x00, 0x0028, 0x2867},
+       {0x00, 0x0028, 0x2868},
+       {0x00, 0x0028, 0x2869},
+       {0x00, 0x0028, 0x286a},
+       {0x00, 0x0028, 0x286b},
+       {0x00, 0x0028, 0x286c},
+       {0x00, 0x0028, 0x286d},
+       {0x00, 0x0028, 0x286e},
+       {0x00, 0x0028, 0x286f},
+
+       {0x00, 0x0028, 0x2870},
+       {0x00, 0x0028, 0x2871},
+       {0x00, 0x0028, 0x2872},
+       {0x00, 0x0028, 0x2873},
+       {0x00, 0x0028, 0x2874},
+       {0x00, 0x0028, 0x2875},
+       {0x00, 0x0028, 0x2876},
+       {0x00, 0x0028, 0x2877},
+       {0x00, 0x0028, 0x2878},
+       {0x00, 0x0028, 0x2879},
+       {0x00, 0x0028, 0x287a},
+       {0x00, 0x0028, 0x287b},
+       {0x00, 0x0028, 0x287c},
+       {0x00, 0x0028, 0x287d},
+       {0x00, 0x0028, 0x287e},
+       {0x00, 0x0028, 0x287f},
+
+       {0xa0, 0x0000, 0x0503},
+       {}
+};
+
+static const __u8 qtable_creative_pccam[2][64] = {
+       {                               /* Q-table Y-components */
+        0x05, 0x03, 0x03, 0x05, 0x07, 0x0c, 0x0f, 0x12,
+        0x04, 0x04, 0x04, 0x06, 0x08, 0x11, 0x12, 0x11,
+        0x04, 0x04, 0x05, 0x07, 0x0c, 0x11, 0x15, 0x11,
+        0x04, 0x05, 0x07, 0x09, 0x0f, 0x1a, 0x18, 0x13,
+        0x05, 0x07, 0x0b, 0x11, 0x14, 0x21, 0x1f, 0x17,
+        0x07, 0x0b, 0x11, 0x13, 0x18, 0x1f, 0x22, 0x1c,
+        0x0f, 0x13, 0x17, 0x1a, 0x1f, 0x24, 0x24, 0x1e,
+        0x16, 0x1c, 0x1d, 0x1d, 0x22, 0x1e, 0x1f, 0x1e},
+       {                               /* Q-table C-components */
+        0x05, 0x05, 0x07, 0x0e, 0x1e, 0x1e, 0x1e, 0x1e,
+        0x05, 0x06, 0x08, 0x14, 0x1e, 0x1e, 0x1e, 0x1e,
+        0x07, 0x08, 0x11, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+        0x0e, 0x14, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+        0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+        0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+        0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+        0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e}
+};
+
+/* FIXME: This Q-table is identical to the Creative PC-CAM one,
+ *             except for one byte. Possibly a typo?
+ *             NWG: 18/05/2003.
+ */
+static const __u8 qtable_spca504_default[2][64] = {
+       {                               /* Q-table Y-components */
+        0x05, 0x03, 0x03, 0x05, 0x07, 0x0c, 0x0f, 0x12,
+        0x04, 0x04, 0x04, 0x06, 0x08, 0x11, 0x12, 0x11,
+        0x04, 0x04, 0x05, 0x07, 0x0c, 0x11, 0x15, 0x11,
+        0x04, 0x05, 0x07, 0x09, 0x0f, 0x1a, 0x18, 0x13,
+        0x05, 0x07, 0x0b, 0x11, 0x14, 0x21, 0x1f, 0x17,
+        0x07, 0x0b, 0x11, 0x13, 0x18, 0x1f, 0x22, 0x1c,
+        0x0f, 0x13, 0x17, 0x1a, 0x1f, 0x24, 0x24, 0x1e,
+        0x16, 0x1c, 0x1d, 0x1d, 0x1d /* 0x22 */ , 0x1e, 0x1f, 0x1e,
+        },
+       {                               /* Q-table C-components */
+        0x05, 0x05, 0x07, 0x0e, 0x1e, 0x1e, 0x1e, 0x1e,
+        0x05, 0x06, 0x08, 0x14, 0x1e, 0x1e, 0x1e, 0x1e,
+        0x07, 0x08, 0x11, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+        0x0e, 0x14, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+        0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+        0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+        0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+        0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e}
+};
+
+static void reg_r(struct usb_device *dev,
+                          __u16 req,
+                          __u16 index,
+                          __u8 *buffer, __u16 length)
+{
+       usb_control_msg(dev,
+                       usb_rcvctrlpipe(dev, 0),
+                       req,
+                       USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                       0,              /* value */
+                       index, buffer, length,
+                       500);
+}
+
+static void reg_w(struct usb_device *dev,
+                           __u16 req,
+                           __u16 value,
+                           __u16 index,
+                           __u8 *buffer, __u16 length)
+{
+       usb_control_msg(dev,
+                       usb_sndctrlpipe(dev, 0),
+                       req,
+                       USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                       value, index, buffer, length,
+                       500);
+}
+
+/* write req / index / value */
+static int reg_w_riv(struct usb_device *dev,
+                    __u16 req, __u16 index, __u16 value)
+{
+       int ret;
+
+       ret = usb_control_msg(dev,
+                       usb_sndctrlpipe(dev, 0),
+                       req,
+                       USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                       value, index, NULL, 0, 500);
+       PDEBUG(D_USBO, "reg write: 0x%02x,0x%02x:0x%02x, %d",
+               req, index, value, ret);
+       if (ret < 0)
+               PDEBUG(D_ERR, "reg write: error %d", ret);
+       return ret;
+}
+
+/* read 1 byte */
+static int reg_r_1(struct gspca_dev *gspca_dev,
+                       __u16 value)    /* wValue */
+{
+       int ret;
+
+       ret = usb_control_msg(gspca_dev->dev,
+                       usb_rcvctrlpipe(gspca_dev->dev, 0),
+                       0x20,                   /* request */
+                       USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                       value,
+                       0,                      /* index */
+                       gspca_dev->usb_buf, 1,
+                       500);                   /* timeout */
+       if (ret < 0) {
+               PDEBUG(D_ERR, "reg_r_1 err %d", ret);
+               return 0;
+       }
+       return gspca_dev->usb_buf[0];
+}
+
+/* read 1 or 2 bytes - returns < 0 if error */
+static int reg_r_12(struct gspca_dev *gspca_dev,
+                       __u16 req,      /* bRequest */
+                       __u16 index,    /* wIndex */
+                       __u16 length)   /* wLength (1 or 2 only) */
+{
+       int ret;
+
+       gspca_dev->usb_buf[1] = 0;
+       ret = usb_control_msg(gspca_dev->dev,
+                       usb_rcvctrlpipe(gspca_dev->dev, 0),
+                       req,
+                       USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                       0,              /* value */
+                       index,
+                       gspca_dev->usb_buf, length,
+                       500);
+       if (ret < 0) {
+               PDEBUG(D_ERR, "reg_read err %d", ret);
+               return -1;
+       }
+       return (gspca_dev->usb_buf[1] << 8) + gspca_dev->usb_buf[0];
+}
+
+static int write_vector(struct gspca_dev *gspca_dev,
+                       const __u16 data[][3])
+{
+       struct usb_device *dev = gspca_dev->dev;
+       int ret, i = 0;
+
+       while (data[i][0] != 0 || data[i][1] != 0 || data[i][2] != 0) {
+               ret = reg_w_riv(dev, data[i][0], data[i][2], data[i][1]);
+               if (ret < 0) {
+                       PDEBUG(D_ERR,
+                               "Register write failed for 0x%x,0x%x,0x%x",
+                               data[i][0], data[i][1], data[i][2]);
+                       return ret;
+               }
+               i++;
+       }
+       return 0;
+}
+
+static int spca50x_setup_qtable(struct gspca_dev *gspca_dev,
+                               unsigned int request,
+                               unsigned int ybase,
+                               unsigned int cbase,
+                               const __u8 qtable[2][64])
+{
+       struct usb_device *dev = gspca_dev->dev;
+       int i, err;
+
+       /* loop over y components */
+       for (i = 0; i < 64; i++) {
+               err = reg_w_riv(dev, request, ybase + i, qtable[0][i]);
+               if (err < 0)
+                       return err;
+       }
+
+       /* loop over c components */
+       for (i = 0; i < 64; i++) {
+               err = reg_w_riv(dev, request, cbase + i, qtable[1][i]);
+               if (err < 0)
+                       return err;
+       }
+       return 0;
+}
+
+static void spca504_acknowledged_command(struct gspca_dev *gspca_dev,
+                            __u16 req, __u16 idx, __u16 val)
+{
+       struct usb_device *dev = gspca_dev->dev;
+       __u8 notdone;
+
+       reg_w_riv(dev, req, idx, val);
+       notdone = reg_r_12(gspca_dev, 0x01, 0x0001, 1);
+       reg_w_riv(dev, req, idx, val);
+
+       PDEBUG(D_FRAM, "before wait 0x%x", notdone);
+
+       msleep(200);
+       notdone = reg_r_12(gspca_dev, 0x01, 0x0001, 1);
+       PDEBUG(D_FRAM, "after wait 0x%x", notdone);
+}
+
+static void spca504A_acknowledged_command(struct gspca_dev *gspca_dev,
+                       __u16 req,
+                       __u16 idx, __u16 val, __u8 stat, __u8 count)
+{
+       struct usb_device *dev = gspca_dev->dev;
+       __u8 status;
+       __u8 endcode;
+
+       reg_w_riv(dev, req, idx, val);
+       status = reg_r_12(gspca_dev, 0x01, 0x0001, 1);
+       endcode = stat;
+       PDEBUG(D_FRAM, "Status 0x%x Need 0x%x", status, stat);
+       if (!count)
+               return;
+       count = 200;
+       while (--count > 0) {
+               msleep(10);
+               /* gsmart mini2 write a each wait setting 1 ms is enought */
+/*             reg_w_riv(dev, req, idx, val); */
+               status = reg_r_12(gspca_dev, 0x01, 0x0001, 1);
+               if (status == endcode) {
+                       PDEBUG(D_FRAM, "status 0x%x after wait 0x%x",
+                               status, 200 - count);
+                               break;
+               }
+       }
+}
+
+static int spca504B_PollingDataReady(struct gspca_dev *gspca_dev)
+{
+       int count = 10;
+
+       while (--count > 0) {
+               reg_r(gspca_dev->dev, 0x21, 0, gspca_dev->usb_buf, 1);
+               if ((gspca_dev->usb_buf[0] & 0x01) == 0)
+                       break;
+               msleep(10);
+       }
+       return gspca_dev->usb_buf[0];
+}
+
+static void spca504B_WaitCmdStatus(struct gspca_dev *gspca_dev)
+{
+       struct usb_device *dev = gspca_dev->dev;
+       int count = 50;
+
+       while (--count > 0) {
+               reg_r(dev, 0x21, 1, gspca_dev->usb_buf, 1);
+               if (gspca_dev->usb_buf[0] != 0) {
+                       gspca_dev->usb_buf[0] = 0;
+                       reg_w(dev, 0x21, 0, 1, gspca_dev->usb_buf, 1);
+                       reg_r(dev, 0x21, 1, gspca_dev->usb_buf, 1);
+                       spca504B_PollingDataReady(gspca_dev);
+                       break;
+               }
+               msleep(10);
+       }
+}
+
+static void spca50x_GetFirmware(struct gspca_dev *gspca_dev)
+{
+       struct usb_device *dev = gspca_dev->dev;
+       __u8 *data;
+
+       data = kmalloc(64, GFP_KERNEL);
+       reg_r(dev, 0x20, 0, data, 5);
+       PDEBUG(D_STREAM, "FirmWare : %d %d %d %d %d ",
+               data[0], data[1], data[2], data[3], data[4]);
+       reg_r(dev, 0x23, 0, data, 64);
+       reg_r(dev, 0x23, 1, data, 64);
+       kfree(data);
+}
+
+static void spca504B_SetSizeType(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       struct usb_device *dev = gspca_dev->dev;
+       __u8 Size;
+       __u8 Type;
+       int rc;
+
+       Size = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;
+       Type = 0;
+       switch (sd->bridge) {
+       case BRIDGE_SPCA533:
+               reg_w(dev, 0x31, 0, 0, NULL, 0);
+               spca504B_WaitCmdStatus(gspca_dev);
+               rc = spca504B_PollingDataReady(gspca_dev);
+               spca50x_GetFirmware(gspca_dev);
+               gspca_dev->usb_buf[0] = 2;                      /* type */
+               reg_w(dev, 0x24, 0, 8, gspca_dev->usb_buf, 1);
+               reg_r(dev, 0x24, 8, gspca_dev->usb_buf, 1);
+
+               gspca_dev->usb_buf[0] = Size;
+               reg_w(dev, 0x25, 0, 4, gspca_dev->usb_buf, 1);
+               reg_r(dev, 0x25, 4, gspca_dev->usb_buf, 1);     /* size */
+               rc = spca504B_PollingDataReady(gspca_dev);
+
+               /* Init the cam width height with some values get on init ? */
+               reg_w(dev, 0x31, 0, 4, NULL, 0);
+               spca504B_WaitCmdStatus(gspca_dev);
+               rc = spca504B_PollingDataReady(gspca_dev);
+               break;
+       default:
+/* case BRIDGE_SPCA504B: */
+/* case BRIDGE_SPCA536: */
+               gspca_dev->usb_buf[0] = Size;
+               reg_w(dev, 0x25, 0, 4, gspca_dev->usb_buf, 1);
+               reg_r(dev, 0x25, 4, gspca_dev->usb_buf, 1);     /* size */
+               Type = 6;
+               gspca_dev->usb_buf[0] = Type;
+               reg_w(dev, 0x27, 0, 0, gspca_dev->usb_buf, 1);
+               reg_r(dev, 0x27, 0, gspca_dev->usb_buf, 1);     /* type */
+               rc = spca504B_PollingDataReady(gspca_dev);
+               break;
+       case BRIDGE_SPCA504:
+               Size += 3;
+               if (sd->subtype == AiptekMiniPenCam13) {
+                       /* spca504a aiptek */
+                       spca504A_acknowledged_command(gspca_dev,
+                                               0x08, Size, 0,
+                                               0x80 | (Size & 0x0f), 1);
+                       spca504A_acknowledged_command(gspca_dev,
+                                                       1, 3, 0, 0x9f, 0);
+               } else {
+                       spca504_acknowledged_command(gspca_dev, 0x08, Size, 0);
+               }
+               break;
+       case BRIDGE_SPCA504C:
+               /* capture mode */
+               reg_w_riv(dev, 0xa0, (0x0500 | (Size & 0x0f)), 0x00);
+               reg_w_riv(dev, 0x20, 0x01, 0x0500 | (Size & 0x0f));
+               break;
+       }
+}
+
+static void spca504_wait_status(struct gspca_dev *gspca_dev)
+{
+       int cnt;
+
+       cnt = 256;
+       while (--cnt > 0) {
+               /* With this we get the status, when return 0 it's all ok */
+               if (reg_r_12(gspca_dev, 0x06, 0x00, 1) == 0)
+                       return;
+               msleep(10);
+       }
+}
+
+static void spca504B_setQtable(struct gspca_dev *gspca_dev)
+{
+       struct usb_device *dev = gspca_dev->dev;
+
+       gspca_dev->usb_buf[0] = 3;
+       reg_w(dev, 0x26, 0, 0, gspca_dev->usb_buf, 1);
+       reg_r(dev, 0x26, 0, gspca_dev->usb_buf, 1);
+       spca504B_PollingDataReady(gspca_dev);
+}
+
+static void sp5xx_initContBrigHueRegisters(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       struct usb_device *dev = gspca_dev->dev;
+       int pollreg = 1;
+
+       switch (sd->bridge) {
+       case BRIDGE_SPCA504:
+       case BRIDGE_SPCA504C:
+               pollreg = 0;
+               /* fall thru */
+       default:
+/*     case BRIDGE_SPCA533: */
+/*     case BRIDGE_SPCA504B: */
+               reg_w(dev, 0, 0, 0x21a7, NULL, 0);      /* brightness */
+               reg_w(dev, 0, 0x20, 0x21a8, NULL, 0);   /* contrast */
+               reg_w(dev, 0, 0, 0x21ad, NULL, 0);      /* hue */
+               reg_w(dev, 0, 1, 0x21ac, NULL, 0);      /* sat/hue */
+               reg_w(dev, 0, 0x20, 0x21ae, NULL, 0);   /* saturation */
+               reg_w(dev, 0, 0, 0x21a3, NULL, 0);      /* gamma */
+               break;
+       case BRIDGE_SPCA536:
+               reg_w(dev, 0, 0, 0x20f0, NULL, 0);
+               reg_w(dev, 0, 0x21, 0x20f1, NULL, 0);
+               reg_w(dev, 0, 0x40, 0x20f5, NULL, 0);
+               reg_w(dev, 0, 1, 0x20f4, NULL, 0);
+               reg_w(dev, 0, 0x40, 0x20f6, NULL, 0);
+               reg_w(dev, 0, 0, 0x2089, NULL, 0);
+               break;
+       }
+       if (pollreg)
+               spca504B_PollingDataReady(gspca_dev);
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+                       const struct usb_device_id *id)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       struct usb_device *dev = gspca_dev->dev;
+       struct cam *cam;
+       __u16 vendor;
+       __u16 product;
+       __u8 fw;
+
+       vendor = id->idVendor;
+       product = id->idProduct;
+       switch (vendor) {
+       case 0x041e:            /* Creative cameras */
+/*             switch (product) { */
+/*             case 0x400b: */
+/*             case 0x4012: */
+/*             case 0x4013: */
+/*                     sd->bridge = BRIDGE_SPCA504C; */
+/*                     break; */
+/*             } */
+               break;
+       case 0x0458:            /* Genius KYE cameras */
+/*             switch (product) { */
+/*             case 0x7006: */
+                       sd->bridge = BRIDGE_SPCA504B;
+/*                     break; */
+/*             } */
+               break;
+       case 0x0461:            /* MicroInnovation */
+/*             switch (product) { */
+/*             case 0x0821: */
+                       sd->bridge = BRIDGE_SPCA533;
+/*                     break; */
+/*             } */
+               break;
+       case 0x046d:            /* Logitech Labtec */
+               switch (product) {
+               case 0x0905:
+                       sd->subtype = LogitechClickSmart820;
+                       sd->bridge = BRIDGE_SPCA533;
+                       break;
+               case 0x0960:
+                       sd->subtype = LogitechClickSmart420;
+                       sd->bridge = BRIDGE_SPCA504C;
+                       break;
+               }
+               break;
+       case 0x0471:                            /* Philips */
+/*             switch (product) { */
+/*             case 0x0322: */
+                       sd->bridge = BRIDGE_SPCA504B;
+/*                     break; */
+/*             } */
+               break;
+       case 0x04a5:            /* Benq */
+               switch (product) {
+               case 0x3003:
+                       sd->bridge = BRIDGE_SPCA504B;
+                       break;
+               case 0x3008:
+               case 0x300a:
+                       sd->bridge = BRIDGE_SPCA533;
+                       break;
+               }
+               break;
+       case 0x04f1:            /* JVC */
+/*             switch (product) { */
+/*             case 0x1001: */
+                       sd->bridge = BRIDGE_SPCA504B;
+/*                     break; */
+/*             } */
+               break;
+       case 0x04fc:            /* SunPlus */
+               switch (product) {
+               case 0x500c:
+                       sd->bridge = BRIDGE_SPCA504B;
+                       break;
+               case 0x504a:
+/* try to get the firmware as some cam answer 2.0.1.2.2
+ * and should be a spca504b then overwrite that setting */
+                       reg_r(dev, 0x20, 0, gspca_dev->usb_buf, 1);
+                       fw = gspca_dev->usb_buf[0];
+                       if (fw == 1) {
+                               sd->subtype = AiptekMiniPenCam13;
+                               sd->bridge = BRIDGE_SPCA504;
+                       } else if (fw == 2) {
+                               sd->bridge = BRIDGE_SPCA504B;
+                       } else
+                               return -ENODEV;
+                       break;
+               case 0x504b:
+                       sd->bridge = BRIDGE_SPCA504B;
+                       break;
+               case 0x5330:
+                       sd->bridge = BRIDGE_SPCA533;
+                       break;
+               case 0x5360:
+                       sd->bridge = BRIDGE_SPCA536;
+                       break;
+               case 0xffff:
+                       sd->bridge = BRIDGE_SPCA504B;
+                       break;
+               }
+               break;
+       case 0x052b:            /* ?? Megapix */
+/*             switch (product) { */
+/*             case 0x1513: */
+                       sd->subtype = MegapixV4;
+                       sd->bridge = BRIDGE_SPCA533;
+/*                     break; */
+/*             } */
+               break;
+       case 0x0546:            /* Polaroid */
+               switch (product) {
+               case 0x3155:
+                       sd->bridge = BRIDGE_SPCA533;
+                       break;
+               case 0x3191:
+               case 0x3273:
+                       sd->bridge = BRIDGE_SPCA504B;
+                       break;
+               }
+               break;
+       case 0x055f:            /* Mustek cameras */
+               switch (product) {
+               case 0xc211:
+                       sd->bridge = BRIDGE_SPCA536;
+                       break;
+               case 0xc230:
+               case 0xc232:
+                       sd->bridge = BRIDGE_SPCA533;
+                       break;
+               case 0xc360:
+                       sd->bridge = BRIDGE_SPCA536;
+                       break;
+               case 0xc420:
+                       sd->bridge = BRIDGE_SPCA504;
+                       break;
+               case 0xc430:
+               case 0xc440:
+                       sd->bridge = BRIDGE_SPCA533;
+                       break;
+               case 0xc520:
+                       sd->bridge = BRIDGE_SPCA504;
+                       break;
+               case 0xc530:
+               case 0xc540:
+               case 0xc630:
+               case 0xc650:
+                       sd->bridge = BRIDGE_SPCA533;
+                       break;
+               }
+               break;
+       case 0x05da:            /* Digital Dream cameras */
+/*             switch (product) { */
+/*             case 0x1018: */
+                       sd->bridge = BRIDGE_SPCA504B;
+/*                     break; */
+/*             } */
+               break;
+       case 0x06d6:            /* Trust */
+/*             switch (product) { */
+/*             case 0x0031: */
+                       sd->bridge = BRIDGE_SPCA533;    /* SPCA533A */
+/*                     break; */
+/*             } */
+               break;
+       case 0x0733:    /* Rebadged ViewQuest (Intel) and ViewQuest cameras */
+               switch (product) {
+               case 0x1311:
+               case 0x1314:
+               case 0x2211:
+               case 0x2221:
+                       sd->bridge = BRIDGE_SPCA533;
+                       break;
+               case 0x3261:
+               case 0x3281:
+                       sd->bridge = BRIDGE_SPCA536;
+                       break;
+               }
+               break;
+       case 0x08ca:            /* Aiptek */
+               switch (product) {
+               case 0x0104:
+               case 0x0106:
+                       sd->bridge = BRIDGE_SPCA533;
+                       break;
+               case 0x2008:
+                       sd->bridge = BRIDGE_SPCA504B;
+                       break;
+               case 0x2010:
+                       sd->bridge = BRIDGE_SPCA533;
+                       break;
+               case 0x2016:
+               case 0x2018:
+                       sd->bridge = BRIDGE_SPCA504B;
+                       break;
+               case 0x2020:
+               case 0x2022:
+                       sd->bridge = BRIDGE_SPCA533;
+                       break;
+               case 0x2024:
+                       sd->bridge = BRIDGE_SPCA536;
+                       break;
+               case 0x2028:
+                       sd->bridge = BRIDGE_SPCA533;
+                       break;
+               case 0x2040:
+               case 0x2042:
+               case 0x2050:
+               case 0x2060:
+                       sd->bridge = BRIDGE_SPCA536;
+                       break;
+               }
+               break;
+       case 0x0d64:            /* SunPlus */
+/*             switch (product) { */
+/*             case 0x0303: */
+                       sd->bridge = BRIDGE_SPCA536;
+/*                     break; */
+/*             } */
+               break;
+       }
+
+       cam = &gspca_dev->cam;
+       cam->dev_name = (char *) id->driver_info;
+       cam->epaddr = 0x01;
+
+       switch (sd->bridge) {
+       default:
+/*     case BRIDGE_SPCA504B: */
+/*     case BRIDGE_SPCA504: */
+/*     case BRIDGE_SPCA536: */
+               cam->cam_mode = vga_mode;
+               cam->nmodes = sizeof vga_mode / sizeof vga_mode[0];
+               break;
+       case BRIDGE_SPCA533:
+               cam->cam_mode = custom_mode;
+               cam->nmodes = sizeof custom_mode / sizeof custom_mode[0];
+               break;
+       case BRIDGE_SPCA504C:
+               cam->cam_mode = vga_mode2;
+               cam->nmodes = sizeof vga_mode2 / sizeof vga_mode2[0];
+               break;
+       }
+       sd->qindex = 5;                 /* set the quantization table */
+       sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value;
+       sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value;
+       sd->colors = sd_ctrls[SD_COLOR].qctrl.default_value;
+       return 0;
+}
+
+/* this function is called at open time */
+static int sd_open(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       struct usb_device *dev = gspca_dev->dev;
+       int rc;
+       __u8 i;
+       __u8 info[6];
+       int err_code;
+
+       switch (sd->bridge) {
+       case BRIDGE_SPCA504B:
+               reg_w(dev, 0x1d, 0, 0, NULL, 0);
+               reg_w(dev, 0, 1, 0x2306, NULL, 0);
+               reg_w(dev, 0, 0, 0x0d04, NULL, 0);
+               reg_w(dev, 0, 0, 0x2000, NULL, 0);
+               reg_w(dev, 0, 0x13, 0x2301, NULL, 0);
+               reg_w(dev, 0, 0, 0x2306, NULL, 0);
+               /* fall thru */
+       case BRIDGE_SPCA533:
+               rc = spca504B_PollingDataReady(gspca_dev);
+               spca50x_GetFirmware(gspca_dev);
+               break;
+       case BRIDGE_SPCA536:
+               spca50x_GetFirmware(gspca_dev);
+               reg_r(dev, 0x00, 0x5002, gspca_dev->usb_buf, 1);
+               gspca_dev->usb_buf[0] = 0;
+               reg_w(dev, 0x24, 0, 0, gspca_dev->usb_buf, 1);
+               reg_r(dev, 0x24, 0, gspca_dev->usb_buf, 1);
+               rc = spca504B_PollingDataReady(gspca_dev);
+               reg_w(dev, 0x34, 0, 0, NULL, 0);
+               spca504B_WaitCmdStatus(gspca_dev);
+               break;
+       case BRIDGE_SPCA504C:   /* pccam600 */
+               PDEBUG(D_STREAM, "Opening SPCA504 (PC-CAM 600)");
+               reg_w_riv(dev, 0xe0, 0x0000, 0x0000);
+               reg_w_riv(dev, 0xe0, 0x0000, 0x0001);   /* reset */
+               spca504_wait_status(gspca_dev);
+               if (sd->subtype == LogitechClickSmart420)
+                       write_vector(gspca_dev,
+                                       spca504A_clicksmart420_open_data);
+               else
+                       write_vector(gspca_dev, spca504_pccam600_open_data);
+               err_code = spca50x_setup_qtable(gspca_dev,
+                                               0x00, 0x2800,
+                                               0x2840, qtable_creative_pccam);
+               if (err_code < 0) {
+                       PDEBUG(D_ERR|D_STREAM, "spca50x_setup_qtable failed");
+                       return err_code;
+               }
+               break;
+       default:
+/*     case BRIDGE_SPCA504: */
+               PDEBUG(D_STREAM, "Opening SPCA504");
+               if (sd->subtype == AiptekMiniPenCam13) {
+                       /*****************************/
+                       for (i = 0; i < 6; i++)
+                               info[i] = reg_r_1(gspca_dev, i);
+                       PDEBUG(D_STREAM,
+                               "Read info: %d %d %d %d %d %d."
+                               " Should be 1,0,2,2,0,0",
+                               info[0], info[1], info[2],
+                               info[3], info[4], info[5]);
+                       /* spca504a aiptek */
+                       /* Set AE AWB Banding Type 3-> 50Hz 2-> 60Hz */
+                       spca504A_acknowledged_command(gspca_dev, 0x24,
+                                                       8, 3, 0x9e, 1);
+                       /* Twice sequencial need status 0xff->0x9e->0x9d */
+                       spca504A_acknowledged_command(gspca_dev, 0x24,
+                                                       8, 3, 0x9e, 0);
+
+                       spca504A_acknowledged_command(gspca_dev, 0x24,
+                                                       0, 0, 0x9d, 1);
+                       /******************************/
+                       /* spca504a aiptek */
+                       spca504A_acknowledged_command(gspca_dev, 0x08,
+                                                       6, 0, 0x86, 1);
+/*                     reg_write (dev, 0, 0x2000, 0); */
+/*                     reg_write (dev, 0, 0x2883, 1); */
+/*                     spca504A_acknowledged_command (gspca_dev, 0x08,
+                                                       6, 0, 0x86, 1); */
+/*                     spca504A_acknowledged_command (gspca_dev, 0x24,
+                                                       0, 0, 0x9D, 1); */
+                       reg_w_riv(dev, 0x0, 0x270c, 0x05); /* L92 sno1t.txt */
+                       reg_w_riv(dev, 0x0, 0x2310, 0x05);
+                       spca504A_acknowledged_command(gspca_dev, 0x01,
+                                                       0x0f, 0, 0xff, 0);
+               }
+               /* setup qtable */
+               reg_w_riv(dev, 0, 0x2000, 0);
+               reg_w_riv(dev, 0, 0x2883, 1);
+               err_code = spca50x_setup_qtable(gspca_dev,
+                                               0x00, 0x2800,
+                                               0x2840,
+                                               qtable_spca504_default);
+               if (err_code < 0) {
+                       PDEBUG(D_ERR, "spca50x_setup_qtable failed");
+                       return err_code;
+               }
+               break;
+       }
+       return 0;
+}
+
+static void sd_start(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       struct usb_device *dev = gspca_dev->dev;
+       int rc;
+       int enable;
+       __u8 i;
+       __u8 info[6];
+
+       if (sd->bridge == BRIDGE_SPCA504B)
+               spca504B_setQtable(gspca_dev);
+       spca504B_SetSizeType(gspca_dev);
+       switch (sd->bridge) {
+       default:
+/*     case BRIDGE_SPCA504B: */
+/*     case BRIDGE_SPCA533: */
+/*     case BRIDGE_SPCA536: */
+               if (sd->subtype == MegapixV4 ||
+                   sd->subtype == LogitechClickSmart820) {
+                       reg_w(dev, 0xf0, 0, 0, NULL, 0);
+                       spca504B_WaitCmdStatus(gspca_dev);
+                       reg_r(dev, 0xf0, 4, NULL, 0);
+                       spca504B_WaitCmdStatus(gspca_dev);
+               } else {
+                       reg_w(dev, 0x31, 0, 4, NULL, 0);
+                       spca504B_WaitCmdStatus(gspca_dev);
+                       rc = spca504B_PollingDataReady(gspca_dev);
+               }
+               break;
+       case BRIDGE_SPCA504:
+               if (sd->subtype == AiptekMiniPenCam13) {
+                       for (i = 0; i < 6; i++)
+                               info[i] = reg_r_1(gspca_dev, i);
+                       PDEBUG(D_STREAM,
+                               "Read info: %d %d %d %d %d %d."
+                               " Should be 1,0,2,2,0,0",
+                               info[0], info[1], info[2],
+                               info[3], info[4], info[5]);
+                       /* spca504a aiptek */
+                       /* Set AE AWB Banding Type 3-> 50Hz 2-> 60Hz */
+                       spca504A_acknowledged_command(gspca_dev, 0x24,
+                                                       8, 3, 0x9e, 1);
+                       /* Twice sequencial need status 0xff->0x9e->0x9d */
+                       spca504A_acknowledged_command(gspca_dev, 0x24,
+                                                       8, 3, 0x9e, 0);
+                       spca504A_acknowledged_command(gspca_dev, 0x24,
+                                                       0, 0, 0x9d, 1);
+               } else {
+                       spca504_acknowledged_command(gspca_dev, 0x24, 8, 3);
+                       for (i = 0; i < 6; i++)
+                               info[i] = reg_r_1(gspca_dev, i);
+                       PDEBUG(D_STREAM,
+                               "Read info: %d %d %d %d %d %d."
+                               " Should be 1,0,2,2,0,0",
+                               info[0], info[1], info[2],
+                               info[3], info[4], info[5]);
+                       spca504_acknowledged_command(gspca_dev, 0x24, 8, 3);
+                       spca504_acknowledged_command(gspca_dev, 0x24, 0, 0);
+               }
+               spca504B_SetSizeType(gspca_dev);
+               reg_w_riv(dev, 0x0, 0x270c, 0x05);      /* L92 sno1t.txt */
+               reg_w_riv(dev, 0x0, 0x2310, 0x05);
+               break;
+       case BRIDGE_SPCA504C:
+               if (sd->subtype == LogitechClickSmart420) {
+                       write_vector(gspca_dev,
+                                       spca504A_clicksmart420_init_data);
+               } else {
+                       write_vector(gspca_dev, spca504_pccam600_init_data);
+               }
+               enable = (sd->autogain ? 0x04 : 0x01);
+               reg_w_riv(dev, 0x0c, 0x0000, enable);   /* auto exposure */
+               reg_w_riv(dev, 0xb0, 0x0000, enable);   /* auto whiteness */
+
+               /* set default exposure compensation and whiteness balance */
+               reg_w_riv(dev, 0x30, 0x0001, 800);      /* ~ 20 fps */
+               reg_w_riv(dev, 0x30, 0x0002, 1600);
+               spca504B_SetSizeType(gspca_dev);
+               break;
+       }
+       sp5xx_initContBrigHueRegisters(gspca_dev);
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       struct usb_device *dev = gspca_dev->dev;
+
+       switch (sd->bridge) {
+       default:
+/*     case BRIDGE_SPCA533: */
+/*     case BRIDGE_SPCA536: */
+/*     case BRIDGE_SPCA504B: */
+               reg_w(dev, 0x31, 0, 0, NULL, 0);
+               spca504B_WaitCmdStatus(gspca_dev);
+               spca504B_PollingDataReady(gspca_dev);
+               break;
+       case BRIDGE_SPCA504:
+       case BRIDGE_SPCA504C:
+               reg_w_riv(dev, 0x00, 0x2000, 0x0000);
+
+               if (sd->subtype == AiptekMiniPenCam13) {
+                       /* spca504a aiptek */
+/*                     spca504A_acknowledged_command(gspca_dev, 0x08,
+                                                        6, 0, 0x86, 1); */
+                       spca504A_acknowledged_command(gspca_dev, 0x24,
+                                                       0x00, 0x00, 0x9d, 1);
+                       spca504A_acknowledged_command(gspca_dev, 0x01,
+                                                       0x0f, 0x00, 0xff, 1);
+               } else {
+                       spca504_acknowledged_command(gspca_dev, 0x24, 0, 0);
+                       reg_w_riv(dev, 0x01, 0x000f, 0x00);
+               }
+               break;
+       }
+}
+
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+}
+
+static void sd_close(struct gspca_dev *gspca_dev)
+{
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+                       struct gspca_frame *frame,      /* target */
+                       __u8 *data,                     /* isoc packet */
+                       int len)                        /* iso packet length */
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       int i, sof = 0;
+       unsigned char *s, *d;
+       static unsigned char ffd9[] = {0xff, 0xd9};
+
+/* frames are jpeg 4.1.1 without 0xff escape */
+       switch (sd->bridge) {
+       case BRIDGE_SPCA533:
+               if (data[0] == 0xff) {
+                       if (data[1] != 0x01) {  /* drop packet */
+/*                             gspca_dev->last_packet_type = DISCARD_PACKET; */
+                               return;
+                       }
+                       sof = 1;
+                       data += SPCA533_OFFSET_DATA;
+                       len -= SPCA533_OFFSET_DATA;
+               } else {
+                       data += 1;
+                       len -= 1;
+               }
+               break;
+       case BRIDGE_SPCA536:
+               if (data[0] == 0xff) {
+                       sof = 1;
+                       data += SPCA536_OFFSET_DATA;
+                       len -= SPCA536_OFFSET_DATA;
+               } else {
+                       data += 2;
+                       len -= 2;
+               }
+               break;
+       default:
+/*     case BRIDGE_SPCA504: */
+/*     case BRIDGE_SPCA504B: */
+               switch (data[0]) {
+               case 0xfe:                      /* start of frame */
+                       sof = 1;
+                       data += SPCA50X_OFFSET_DATA;
+                       len -= SPCA50X_OFFSET_DATA;
+                       break;
+               case 0xff:                      /* drop packet */
+/*                     gspca_dev->last_packet_type = DISCARD_PACKET; */
+                       return;
+               default:
+                       data += 1;
+                       len -= 1;
+                       break;
+               }
+               break;
+       case BRIDGE_SPCA504C:
+               switch (data[0]) {
+               case 0xfe:                      /* start of frame */
+                       sof = 1;
+                       data += SPCA504_PCCAM600_OFFSET_DATA;
+                       len -= SPCA504_PCCAM600_OFFSET_DATA;
+                       break;
+               case 0xff:                      /* drop packet */
+/*                     gspca_dev->last_packet_type = DISCARD_PACKET; */
+                       return;
+               default:
+                       data += 1;
+                       len -= 1;
+                       break;
+               }
+               break;
+       }
+       if (sof) {              /* start of frame */
+               frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
+                                       ffd9, 2);
+
+               /* put the JPEG header in the new frame */
+               jpeg_put_header(gspca_dev, frame,
+                               ((struct sd *) gspca_dev)->qindex,
+                               0x22);
+       }
+
+       /* add 0x00 after 0xff */
+       for (i = len; --i >= 0; )
+               if (data[i] == 0xff)
+                       break;
+       if (i < 0) {                    /* no 0xff */
+               gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
+               return;
+       }
+       s = data;
+       d = sd->packet;
+       for (i = 0; i < len; i++) {
+               *d++ = *s++;
+               if (s[-1] == 0xff)
+                       *d++ = 0x00;
+       }
+       gspca_frame_add(gspca_dev, INTER_PACKET, frame,
+                       sd->packet, d - sd->packet);
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       struct usb_device *dev = gspca_dev->dev;
+
+       switch (sd->bridge) {
+       default:
+/*     case BRIDGE_SPCA533: */
+/*     case BRIDGE_SPCA504B: */
+/*     case BRIDGE_SPCA504: */
+/*     case BRIDGE_SPCA504C: */
+               reg_w_riv(dev, 0x0, 0x21a7, sd->brightness);
+               break;
+       case BRIDGE_SPCA536:
+               reg_w_riv(dev, 0x0, 0x20f0, sd->brightness);
+               break;
+       }
+}
+
+static void getbrightness(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       __u16 brightness = 0;
+
+       switch (sd->bridge) {
+       default:
+/*     case BRIDGE_SPCA533: */
+/*     case BRIDGE_SPCA504B: */
+/*     case BRIDGE_SPCA504: */
+/*     case BRIDGE_SPCA504C: */
+               brightness = reg_r_12(gspca_dev, 0x00, 0x21a7, 2);
+               break;
+       case BRIDGE_SPCA536:
+               brightness = reg_r_12(gspca_dev, 0x00, 0x20f0, 2);
+               break;
+       }
+       sd->brightness = ((brightness & 0xff) - 128) % 255;
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       struct usb_device *dev = gspca_dev->dev;
+
+       switch (sd->bridge) {
+       default:
+/*     case BRIDGE_SPCA533: */
+/*     case BRIDGE_SPCA504B: */
+/*     case BRIDGE_SPCA504: */
+/*     case BRIDGE_SPCA504C: */
+               reg_w_riv(dev, 0x0, 0x21a8, sd->contrast);
+               break;
+       case BRIDGE_SPCA536:
+               reg_w_riv(dev, 0x0, 0x20f1, sd->contrast);
+               break;
+       }
+}
+
+static void getcontrast(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       switch (sd->bridge) {
+       default:
+/*     case BRIDGE_SPCA533: */
+/*     case BRIDGE_SPCA504B: */
+/*     case BRIDGE_SPCA504: */
+/*     case BRIDGE_SPCA504C: */
+               sd->contrast = reg_r_12(gspca_dev, 0x00, 0x21a8, 2);
+               break;
+       case BRIDGE_SPCA536:
+               sd->contrast = reg_r_12(gspca_dev, 0x00, 0x20f1, 2);
+               break;
+       }
+}
+
+static void setcolors(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       struct usb_device *dev = gspca_dev->dev;
+
+       switch (sd->bridge) {
+       default:
+/*     case BRIDGE_SPCA533: */
+/*     case BRIDGE_SPCA504B: */
+/*     case BRIDGE_SPCA504: */
+/*     case BRIDGE_SPCA504C: */
+               reg_w_riv(dev, 0x0, 0x21ae, sd->colors);
+               break;
+       case BRIDGE_SPCA536:
+               reg_w_riv(dev, 0x0, 0x20f6, sd->colors);
+               break;
+       }
+}
+
+static void getcolors(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       switch (sd->bridge) {
+       default:
+/*     case BRIDGE_SPCA533: */
+/*     case BRIDGE_SPCA504B: */
+/*     case BRIDGE_SPCA504: */
+/*     case BRIDGE_SPCA504C: */
+               sd->colors = reg_r_12(gspca_dev, 0x00, 0x21ae, 2) >> 1;
+               break;
+       case BRIDGE_SPCA536:
+               sd->colors = reg_r_12(gspca_dev, 0x00, 0x20f6, 2) >> 1;
+               break;
+       }
+}
+
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->brightness = val;
+       if (gspca_dev->streaming)
+               setbrightness(gspca_dev);
+       return 0;
+}
+
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       getbrightness(gspca_dev);
+       *val = sd->brightness;
+       return 0;
+}
+
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->contrast = val;
+       if (gspca_dev->streaming)
+               setcontrast(gspca_dev);
+       return 0;
+}
+
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       getcontrast(gspca_dev);
+       *val = sd->contrast;
+       return 0;
+}
+
+static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->colors = val;
+       if (gspca_dev->streaming)
+               setcolors(gspca_dev);
+       return 0;
+}
+
+static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       getcolors(gspca_dev);
+       *val = sd->colors;
+       return 0;
+}
+
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->autogain = val;
+       return 0;
+}
+
+static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->autogain;
+       return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+       .name = MODULE_NAME,
+       .ctrls = sd_ctrls,
+       .nctrls = ARRAY_SIZE(sd_ctrls),
+       .config = sd_config,
+       .open = sd_open,
+       .start = sd_start,
+       .stopN = sd_stopN,
+       .stop0 = sd_stop0,
+       .close = sd_close,
+       .pkt_scan = sd_pkt_scan,
+};
+
+/* -- module initialisation -- */
+#define DVNM(name) .driver_info = (kernel_ulong_t) name
+static const __devinitdata struct usb_device_id device_table[] = {
+       {USB_DEVICE(0x041e, 0x400b), DVNM("Creative PC-CAM 600")},
+       {USB_DEVICE(0x041e, 0x4012), DVNM("PC-Cam350")},
+       {USB_DEVICE(0x041e, 0x4013), DVNM("Creative Pccam750")},
+       {USB_DEVICE(0x0458, 0x7006), DVNM("Genius Dsc 1.3 Smart")},
+       {USB_DEVICE(0x0461, 0x0821), DVNM("Fujifilm MV-1")},
+       {USB_DEVICE(0x046d, 0x0905), DVNM("Logitech ClickSmart 820")},
+       {USB_DEVICE(0x046d, 0x0960), DVNM("Logitech ClickSmart 420")},
+       {USB_DEVICE(0x0471, 0x0322), DVNM("Philips DMVC1300K")},
+       {USB_DEVICE(0x04a5, 0x3003), DVNM("Benq DC 1300")},
+       {USB_DEVICE(0x04a5, 0x3008), DVNM("Benq DC 1500")},
+       {USB_DEVICE(0x04a5, 0x300a), DVNM("Benq DC3410")},
+       {USB_DEVICE(0x04f1, 0x1001), DVNM("JVC GC A50")},
+       {USB_DEVICE(0x04fc, 0x500c), DVNM("Sunplus CA500C")},
+       {USB_DEVICE(0x04fc, 0x504a), DVNM("Aiptek Mini PenCam 1.3")},
+       {USB_DEVICE(0x04fc, 0x504b), DVNM("Maxell MaxPocket LE 1.3")},
+       {USB_DEVICE(0x04fc, 0x5330), DVNM("Digitrex 2110")},
+       {USB_DEVICE(0x04fc, 0x5360), DVNM("Sunplus Generic")},
+       {USB_DEVICE(0x04fc, 0xffff), DVNM("Pure DigitalDakota")},
+       {USB_DEVICE(0x052b, 0x1513), DVNM("Megapix V4")},
+       {USB_DEVICE(0x0546, 0x3155), DVNM("Polaroid PDC3070")},
+       {USB_DEVICE(0x0546, 0x3191), DVNM("Polaroid Ion 80")},
+       {USB_DEVICE(0x0546, 0x3273), DVNM("Polaroid PDC2030")},
+       {USB_DEVICE(0x055f, 0xc211), DVNM("Kowa Bs888e Microcamera")},
+       {USB_DEVICE(0x055f, 0xc230), DVNM("Mustek Digicam 330K")},
+       {USB_DEVICE(0x055f, 0xc232), DVNM("Mustek MDC3500")},
+       {USB_DEVICE(0x055f, 0xc360), DVNM("Mustek DV4000 Mpeg4 ")},
+       {USB_DEVICE(0x055f, 0xc420), DVNM("Mustek gSmart Mini 2")},
+       {USB_DEVICE(0x055f, 0xc430), DVNM("Mustek Gsmart LCD 2")},
+       {USB_DEVICE(0x055f, 0xc440), DVNM("Mustek DV 3000")},
+       {USB_DEVICE(0x055f, 0xc520), DVNM("Mustek gSmart Mini 3")},
+       {USB_DEVICE(0x055f, 0xc530), DVNM("Mustek Gsmart LCD 3")},
+       {USB_DEVICE(0x055f, 0xc540), DVNM("Gsmart D30")},
+       {USB_DEVICE(0x055f, 0xc630), DVNM("Mustek MDC4000")},
+       {USB_DEVICE(0x055f, 0xc650), DVNM("Mustek MDC5500Z")},
+       {USB_DEVICE(0x05da, 0x1018), DVNM("Digital Dream Enigma 1.3")},
+       {USB_DEVICE(0x06d6, 0x0031), DVNM("Trust 610 LCD PowerC@m Zoom")},
+       {USB_DEVICE(0x0733, 0x1311), DVNM("Digital Dream Epsilon 1.3")},
+       {USB_DEVICE(0x0733, 0x1314), DVNM("Mercury 2.1MEG Deluxe Classic Cam")},
+       {USB_DEVICE(0x0733, 0x2211), DVNM("Jenoptik jdc 21 LCD")},
+       {USB_DEVICE(0x0733, 0x2221), DVNM("Mercury Digital Pro 3.1p")},
+       {USB_DEVICE(0x0733, 0x3261), DVNM("Concord 3045 spca536a")},
+       {USB_DEVICE(0x0733, 0x3281), DVNM("Cyberpix S550V")},
+       {USB_DEVICE(0x08ca, 0x0104), DVNM("Aiptek PocketDVII 1.3")},
+       {USB_DEVICE(0x08ca, 0x0106), DVNM("Aiptek Pocket DV3100+")},
+       {USB_DEVICE(0x08ca, 0x2008), DVNM("Aiptek Mini PenCam 2 M")},
+       {USB_DEVICE(0x08ca, 0x2010), DVNM("Aiptek PocketCam 3M")},
+       {USB_DEVICE(0x08ca, 0x2016), DVNM("Aiptek PocketCam 2 Mega")},
+       {USB_DEVICE(0x08ca, 0x2018), DVNM("Aiptek Pencam SD 2M")},
+       {USB_DEVICE(0x08ca, 0x2020), DVNM("Aiptek Slim 3000F")},
+       {USB_DEVICE(0x08ca, 0x2022), DVNM("Aiptek Slim 3200")},
+       {USB_DEVICE(0x08ca, 0x2024), DVNM("Aiptek DV3500 Mpeg4 ")},
+       {USB_DEVICE(0x08ca, 0x2028), DVNM("Aiptek PocketCam4M")},
+       {USB_DEVICE(0x08ca, 0x2040), DVNM("Aiptek PocketDV4100M")},
+       {USB_DEVICE(0x08ca, 0x2042), DVNM("Aiptek PocketDV5100")},
+       {USB_DEVICE(0x08ca, 0x2050), DVNM("Medion MD 41437")},
+       {USB_DEVICE(0x08ca, 0x2060), DVNM("Aiptek PocketDV5300")},
+       {USB_DEVICE(0x0d64, 0x0303), DVNM("Sunplus FashionCam DXG")},
+       {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+                       const struct usb_device_id *id)
+{
+       return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+                               THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+       .name = MODULE_NAME,
+       .id_table = device_table,
+       .probe = sd_probe,
+       .disconnect = gspca_disconnect,
+};
+
+/* -- module insert / remove -- */
+static int __init sd_mod_init(void)
+{
+       if (usb_register(&sd_driver) < 0)
+               return -1;
+       PDEBUG(D_PROBE, "v%s registered", version);
+       return 0;
+}
+static void __exit sd_mod_exit(void)
+{
+       usb_deregister(&sd_driver);
+       PDEBUG(D_PROBE, "deregistered");
+}
+
+module_init(sd_mod_init);
+module_exit(sd_mod_exit);
diff --git a/drivers/media/video/gspca/t613.c b/drivers/media/video/gspca/t613.c
new file mode 100644 (file)
index 0000000..00f47e4
--- /dev/null
@@ -0,0 +1,1038 @@
+/*
+ *Notes: * t613  + tas5130A
+ *     * Focus to light do not balance well as in win.
+ *       Quality in win is not good, but its kinda better.
+ *      * Fix some "extraneous bytes", most of apps will show the image anyway
+ *      * Gamma table, is there, but its really doing something?
+ *      * 7~8 Fps, its ok, max on win its 10.
+ *                     Costantino Leandro
+ *
+ * V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
+ *
+ * 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
+ * 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
+ */
+
+#define MODULE_NAME "t613"
+#include "gspca.h"
+#define DRIVER_VERSION_NUMBER  KERNEL_VERSION(2, 1, 7)
+static const char version[] = "2.1.7";
+
+#define MAX_GAMMA 0x10         /* 0 to 15 */
+
+/* From LUVCVIEW */
+#define V4L2_CID_EFFECTS (V4L2_CID_PRIVATE_BASE + 3)
+
+MODULE_AUTHOR("Leandro Costantino <le_costantino@pixartargentina.com.ar>");
+MODULE_DESCRIPTION("GSPCA/T613 (JPEG Compliance) USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+struct sd {
+       struct gspca_dev gspca_dev;     /* !! must be the first item */
+
+       unsigned char brightness;
+       unsigned char contrast;
+       unsigned char colors;
+       unsigned char autogain;
+       unsigned char gamma;
+       unsigned char sharpness;
+       unsigned char freq;
+       unsigned char whitebalance;
+       unsigned char mirror;
+       unsigned char effect;
+};
+
+/* V4L2 controls supported by the driver */
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setlowlight(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getlowlight(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setgamma(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getgamma(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setwhitebalance(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getwhitebalance(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setflip(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getflip(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_seteffect(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_geteffect(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_querymenu(struct gspca_dev *gspca_dev,
+                       struct v4l2_querymenu *menu);
+
+static struct ctrl sd_ctrls[] = {
+#define SD_BRIGHTNESS 0
+       {
+        {
+         .id = V4L2_CID_BRIGHTNESS,
+         .type = V4L2_CTRL_TYPE_INTEGER,
+         .name = "Brightness",
+         .minimum = 0,
+         .maximum = 0x0f,
+         .step = 1,
+         .default_value = 0x09,
+         },
+        .set = sd_setbrightness,
+        .get = sd_getbrightness,
+        },
+#define SD_CONTRAST 1
+       {
+        {
+         .id = V4L2_CID_CONTRAST,
+         .type = V4L2_CTRL_TYPE_INTEGER,
+         .name = "Contrast",
+         .minimum = 0,
+         .maximum = 0x0d,
+         .step = 1,
+         .default_value = 0x07,
+         },
+        .set = sd_setcontrast,
+        .get = sd_getcontrast,
+        },
+#define SD_COLOR 2
+       {
+        {
+         .id = V4L2_CID_SATURATION,
+         .type = V4L2_CTRL_TYPE_INTEGER,
+         .name = "Color",
+         .minimum = 0,
+         .maximum = 0x0f,
+         .step = 1,
+         .default_value = 0x05,
+         },
+        .set = sd_setcolors,
+        .get = sd_getcolors,
+        },
+#define SD_GAMMA 3
+       {
+        {
+         .id = V4L2_CID_GAMMA, /* (gamma on win) */
+         .type = V4L2_CTRL_TYPE_INTEGER,
+         .name = "Gamma (Untested)",
+         .minimum = 0,
+         .maximum = MAX_GAMMA,
+         .step = 1,
+         .default_value = 0x09,
+         },
+        .set = sd_setgamma,
+        .get = sd_getgamma,
+        },
+#define SD_AUTOGAIN 4
+       {
+        {
+         .id = V4L2_CID_GAIN,  /* here, i activate only the lowlight,
+                                * some apps dont bring up the
+                                * backligth_compensation control) */
+         .type = V4L2_CTRL_TYPE_INTEGER,
+         .name = "Low Light",
+         .minimum = 0,
+         .maximum = 1,
+         .step = 1,
+         .default_value = 0x01,
+         },
+        .set = sd_setlowlight,
+        .get = sd_getlowlight,
+        },
+#define SD_MIRROR 5
+       {
+        {
+         .id = V4L2_CID_HFLIP,
+         .type = V4L2_CTRL_TYPE_BOOLEAN,
+         .name = "Mirror Image",
+         .minimum = 0,
+         .maximum = 1,
+         .step = 1,
+         .default_value = 0,
+         },
+        .set = sd_setflip,
+        .get = sd_getflip
+       },
+#define SD_LIGHTFREQ 6
+       {
+        {
+         .id = V4L2_CID_POWER_LINE_FREQUENCY,
+         .type = V4L2_CTRL_TYPE_MENU,
+         .name = "Light Frequency Filter",
+         .minimum = 1,         /* 1 -> 0x50, 2->0x60 */
+         .maximum = 2,
+         .step = 1,
+         .default_value = 1,
+         },
+        .set = sd_setfreq,
+        .get = sd_getfreq},
+
+#define SD_WHITE_BALANCE 7
+       {
+        {
+         .id = V4L2_CID_WHITE_BALANCE_TEMPERATURE,
+         .type = V4L2_CTRL_TYPE_INTEGER,
+         .name = "White Balance",
+         .minimum = 0,
+         .maximum = 1,
+         .step = 1,
+         .default_value = 1,
+         },
+        .set = sd_setwhitebalance,
+        .get = sd_getwhitebalance
+       },
+#define SD_SHARPNESS 8         /* (aka definition on win) */
+       {
+        {
+         .id = V4L2_CID_SHARPNESS,
+         .type = V4L2_CTRL_TYPE_INTEGER,
+         .name = "Sharpness",
+         .minimum = 0,
+         .maximum = MAX_GAMMA, /* 0 to 16 */
+         .step = 1,
+         .default_value = 0x06,
+         },
+        .set = sd_setsharpness,
+        .get = sd_getsharpness,
+        },
+#define SD_EFFECTS 9
+       {
+        {
+         .id = V4L2_CID_EFFECTS,
+         .type = V4L2_CTRL_TYPE_MENU,
+         .name = "Webcam Effects",
+         .minimum = 0,
+         .maximum = 4,
+         .step = 1,
+         .default_value = 0,
+         },
+        .set = sd_seteffect,
+        .get = sd_geteffect
+       },
+};
+
+static char *effects_control[] = {
+       "Normal",
+       "Emboss",               /* disabled */
+       "Monochrome",
+       "Sepia",
+       "Sketch",
+       "Sun Effect",           /* disabled */
+       "Negative",
+};
+
+static struct v4l2_pix_format vga_mode_t16[] = {
+       {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+               .bytesperline = 160,
+               .sizeimage = 160 * 120 * 3 / 8 + 590,
+               .colorspace = V4L2_COLORSPACE_JPEG,
+               .priv = 4},
+       {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+               .bytesperline = 176,
+               .sizeimage = 176 * 144 * 3 / 8 + 590,
+               .colorspace = V4L2_COLORSPACE_JPEG,
+               .priv = 3},
+       {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+               .bytesperline = 320,
+               .sizeimage = 320 * 240 * 3 / 8 + 590,
+               .colorspace = V4L2_COLORSPACE_JPEG,
+               .priv = 2},
+       {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+               .bytesperline = 352,
+               .sizeimage = 352 * 288 * 3 / 8 + 590,
+               .colorspace = V4L2_COLORSPACE_JPEG,
+               .priv = 1},
+       {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+               .bytesperline = 640,
+               .sizeimage = 640 * 480 * 3 / 8 + 590,
+               .colorspace = V4L2_COLORSPACE_JPEG,
+               .priv = 0},
+};
+
+#define T16_OFFSET_DATA 631
+#define MAX_EFFECTS 7
+/* easily done by soft, this table could be removed,
+ * i keep it here just in case */
+static const __u8 effects_table[MAX_EFFECTS][6] = {
+       {0xa8, 0xe8, 0xc6, 0xd2, 0xc0, 0x00},   /* Normal */
+       {0xa8, 0xc8, 0xc6, 0x52, 0xc0, 0x04},   /* Repujar */
+       {0xa8, 0xe8, 0xc6, 0xd2, 0xc0, 0x20},   /* Monochrome */
+       {0xa8, 0xe8, 0xc6, 0xd2, 0xc0, 0x80},   /* Sepia */
+       {0xa8, 0xc8, 0xc6, 0x52, 0xc0, 0x02},   /* Croquis */
+       {0xa8, 0xc8, 0xc6, 0xd2, 0xc0, 0x10},   /* Sun Effect */
+       {0xa8, 0xc8, 0xc6, 0xd2, 0xc0, 0x40},   /* Negative */
+};
+
+static const __u8 gamma_table[MAX_GAMMA][34] = {
+       {0x90, 0x00, 0x91, 0x3e, 0x92, 0x69, 0x93, 0x85,
+        0x94, 0x95, 0x95, 0xa1, 0x96, 0xae, 0x97, 0xb9,
+        0x98, 0xc2, 0x99, 0xcb, 0x9a, 0xd4, 0x9b, 0xdb,
+        0x9c, 0xe3, 0x9d, 0xea, 0x9e, 0xf1, 0x9f, 0xf8,
+        0xa0, 0xff},
+       {0x90, 0x00, 0x91, 0x33, 0x92, 0x5A, 0x93, 0x75,
+        0x94, 0x85, 0x95, 0x93, 0x96, 0xA1, 0x97, 0xAD,
+        0x98, 0xB7, 0x99, 0xC2, 0x9A, 0xCB, 0x9B, 0xD4,
+        0x9C, 0xDE, 0x9D, 0xE7, 0x9E, 0xF0, 0x9F, 0xF7,
+        0xa0, 0xff},
+       {0x90, 0x00, 0x91, 0x2F, 0x92, 0x51, 0x93, 0x6B,
+        0x94, 0x7C, 0x95, 0x8A, 0x96, 0x99, 0x97, 0xA6,
+        0x98, 0xB1, 0x99, 0xBC, 0x9A, 0xC6, 0x9B, 0xD0,
+        0x9C, 0xDB, 0x9D, 0xE4, 0x9E, 0xED, 0x9F, 0xF6,
+        0xa0, 0xff},
+       {0x90, 0x00, 0x91, 0x29, 0x92, 0x48, 0x93, 0x60,
+        0x94, 0x72, 0x95, 0x81, 0x96, 0x90, 0x97, 0x9E,
+        0x98, 0xAA, 0x99, 0xB5, 0x9A, 0xBF, 0x9B, 0xCB,
+        0x9C, 0xD6, 0x9D, 0xE1, 0x9E, 0xEB, 0x9F, 0xF5,
+        0xa0, 0xff},
+       {0x90, 0x00, 0x91, 0x23, 0x92, 0x3F, 0x93, 0x55,
+        0x94, 0x68, 0x95, 0x77, 0x96, 0x86, 0x97, 0x95,
+        0x98, 0xA2, 0x99, 0xAD, 0x9A, 0xB9, 0x9B, 0xC6,
+        0x9C, 0xD2, 0x9D, 0xDE, 0x9E, 0xE9, 0x9F, 0xF4,
+        0xa0, 0xff},
+       {0x90, 0x00, 0x91, 0x1B, 0x92, 0x33, 0x93, 0x48,
+        0x94, 0x59, 0x95, 0x69, 0x96, 0x79, 0x97, 0x87,
+        0x98, 0x96, 0x99, 0xA3, 0x9A, 0xB1, 0x9B, 0xBE,
+        0x9C, 0xCC, 0x9D, 0xDA, 0x9E, 0xE7, 0x9F, 0xF3,
+        0xa0, 0xff},
+       {0x90, 0x00, 0x91, 0x02, 0x92, 0x10, 0x93, 0x20,
+        0x94, 0x32, 0x95, 0x40, 0x96, 0x57, 0x97, 0x67,
+        0x98, 0x77, 0x99, 0x88, 0x9a, 0x99, 0x9b, 0xaa,
+        0x9c, 0xbb, 0x9d, 0xcc, 0x9e, 0xdd, 0x9f, 0xee,
+        0xa0, 0xff},
+       {0x90, 0x00, 0x91, 0x02, 0x92, 0x14, 0x93, 0x26,
+        0x94, 0x38, 0x95, 0x4A, 0x96, 0x60, 0x97, 0x70,
+        0x98, 0x80, 0x99, 0x90, 0x9A, 0xA0, 0x9B, 0xB0,
+        0x9C, 0xC0, 0x9D, 0xD0, 0x9E, 0xE0, 0x9F, 0xF0,
+        0xa0, 0xff},
+       {0x90, 0x00, 0x91, 0x10, 0x92, 0x22, 0x93, 0x35,
+        0x94, 0x47, 0x95, 0x5A, 0x96, 0x69, 0x97, 0x79,
+        0x98, 0x88, 0x99, 0x97, 0x9A, 0xA7, 0x9B, 0xB6,
+        0x9C, 0xC4, 0x9D, 0xD3, 0x9E, 0xE0, 0x9F, 0xF0,
+        0xa0, 0xff},
+       {0x90, 0x00, 0x91, 0x10, 0x92, 0x26, 0x93, 0x40,
+        0x94, 0x54, 0x95, 0x65, 0x96, 0x75, 0x97, 0x84,
+        0x98, 0x93, 0x99, 0xa1, 0x9a, 0xb0, 0x9b, 0xbd,
+        0x9c, 0xca, 0x9d, 0xd6, 0x9e, 0xe0, 0x9f, 0xf0,
+        0xa0, 0xff},
+       {0x90, 0x00, 0x91, 0x18, 0x92, 0x2B, 0x93, 0x44,
+        0x94, 0x60, 0x95, 0x70, 0x96, 0x80, 0x97, 0x8E,
+        0x98, 0x9C, 0x99, 0xAA, 0x9A, 0xB7, 0x9B, 0xC4,
+        0x9C, 0xD0, 0x9D, 0xD8, 0x9E, 0xE2, 0x9F, 0xF0,
+        0xa0, 0xff},
+       {0x90, 0x00, 0x91, 0x1A, 0x92, 0x34, 0x93, 0x52,
+        0x94, 0x66, 0x95, 0x7E, 0x96, 0x8D, 0x97, 0x9B,
+        0x98, 0xA8, 0x99, 0xB4, 0x9A, 0xC0, 0x9B, 0xCB,
+        0x9C, 0xD6, 0x9D, 0xE1, 0x9E, 0xEB, 0x9F, 0xF5,
+        0xa0, 0xff},
+       {0x90, 0x00, 0x91, 0x3F, 0x92, 0x5A, 0x93, 0x6E,
+        0x94, 0x7F, 0x95, 0x8E, 0x96, 0x9C, 0x97, 0xA8,
+        0x98, 0xB4, 0x99, 0xBF, 0x9A, 0xC9, 0x9B, 0xD3,
+        0x9C, 0xDC, 0x9D, 0xE5, 0x9E, 0xEE, 0x9F, 0xF6,
+        0xA0, 0xFF},
+       {0x90, 0x00, 0x91, 0x54, 0x92, 0x6F, 0x93, 0x83,
+        0x94, 0x93, 0x95, 0xA0, 0x96, 0xAD, 0x97, 0xB7,
+        0x98, 0xC2, 0x99, 0xCB, 0x9A, 0xD4, 0x9B, 0xDC,
+        0x9C, 0xE4, 0x9D, 0xEB, 0x9E, 0xF2, 0x9F, 0xF9,
+        0xa0, 0xff},
+       {0x90, 0x00, 0x91, 0x6E, 0x92, 0x88, 0x93, 0x9A,
+        0x94, 0xA8, 0x95, 0xB3, 0x96, 0xBD, 0x97, 0xC6,
+        0x98, 0xCF, 0x99, 0xD6, 0x9A, 0xDD, 0x9B, 0xE3,
+        0x9C, 0xE9, 0x9D, 0xEF, 0x9E, 0xF4, 0x9F, 0xFA,
+        0xa0, 0xff},
+       {0x90, 0x00, 0x91, 0x93, 0x92, 0xA8, 0x93, 0xB7,
+        0x94, 0xC1, 0x95, 0xCA, 0x96, 0xD2, 0x97, 0xD8,
+        0x98, 0xDE, 0x99, 0xE3, 0x9A, 0xE8, 0x9B, 0xED,
+        0x9C, 0xF1, 0x9D, 0xF5, 0x9E, 0xF8, 0x9F, 0xFC,
+        0xA0, 0xFF}
+};
+
+static const __u8 tas5130a_sensor_init[][8] = {
+       {0x62, 0x08, 0x63, 0x70, 0x64, 0x1d, 0x60, 0x09},
+       {0x62, 0x20, 0x63, 0x01, 0x64, 0x02, 0x60, 0x09},
+       {0x62, 0x07, 0x63, 0x03, 0x64, 0x00, 0x60, 0x09},
+       {0x62, 0x07, 0x63, 0x03, 0x64, 0x00, 0x60, 0x09},
+       {},
+};
+
+/* read 1 byte */
+static int reg_r_1(struct gspca_dev *gspca_dev,
+                  __u16 index)
+{
+       usb_control_msg(gspca_dev->dev,
+                       usb_rcvctrlpipe(gspca_dev->dev, 0),
+                       0,              /* request */
+                       USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                       0,              /* value */
+                       index,
+                       gspca_dev->usb_buf, 1, 500);
+       return gspca_dev->usb_buf[0];
+}
+
+static void reg_w(struct gspca_dev *gspca_dev,
+                       __u16 value,
+                       __u16 index,
+                       const __u8 *buffer, __u16 len)
+{
+       if (buffer == NULL) {
+               usb_control_msg(gspca_dev->dev,
+                               usb_sndctrlpipe(gspca_dev->dev, 0),
+                               0,
+                          USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+                               value, index,
+                               NULL, 0, 500);
+               return;
+       }
+       if (len <= sizeof gspca_dev->usb_buf) {
+               memcpy(gspca_dev->usb_buf, buffer, len);
+               usb_control_msg(gspca_dev->dev,
+                               usb_sndctrlpipe(gspca_dev->dev, 0),
+                               0,
+                          USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+                               value, index,
+                               gspca_dev->usb_buf, len, 500);
+       } else {
+               __u8 *tmpbuf;
+
+               tmpbuf = kmalloc(len, GFP_KERNEL);
+               memcpy(tmpbuf, buffer, len);
+               usb_control_msg(gspca_dev->dev,
+                               usb_sndctrlpipe(gspca_dev->dev, 0),
+                               0,
+                          USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+                               value, index,
+                               tmpbuf, len, 500);
+               kfree(tmpbuf);
+       }
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+                    const struct usb_device_id *id)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       struct cam *cam;
+
+       cam = &gspca_dev->cam;
+       cam->dev_name = (char *) id->driver_info;
+       cam->epaddr = 0x01;
+
+       cam->cam_mode = vga_mode_t16;
+       cam->nmodes = ARRAY_SIZE(vga_mode_t16);
+
+       sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value;
+       sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value;
+       sd->colors = sd_ctrls[SD_COLOR].qctrl.default_value;
+       sd->gamma = sd_ctrls[SD_GAMMA].qctrl.default_value;
+       sd->mirror = sd_ctrls[SD_MIRROR].qctrl.default_value;
+       sd->freq = sd_ctrls[SD_LIGHTFREQ].qctrl.default_value;
+       sd->whitebalance = sd_ctrls[SD_WHITE_BALANCE].qctrl.default_value;
+       sd->sharpness = sd_ctrls[SD_SHARPNESS].qctrl.default_value;
+       sd->effect = sd_ctrls[SD_EFFECTS].qctrl.default_value;
+       return 0;
+}
+
+static int init_default_parameters(struct gspca_dev *gspca_dev)
+{
+       /* some of this registers are not really neded, because
+        * they are overriden by setbrigthness, setcontrast, etc,
+        * but wont hurt anyway, and can help someone with similar webcam
+        * to see the initial parameters.*/
+       int i = 0;
+       __u8 test_byte;
+
+       static const __u8 read_indexs[] =
+               { 0x06, 0x07, 0x0a, 0x0b, 0x66, 0x80, 0x81, 0x8e, 0x8f, 0xa5,
+                 0xa6, 0xa8, 0xbb, 0xbc, 0xc6, 0x00, 0x00 };
+       static const __u8 n1[6] =
+                       {0x08, 0x03, 0x09, 0x03, 0x12, 0x04};
+       static const __u8 n2[2] =
+                       {0x08, 0x00};
+       static const __u8 nset[6] =
+               { 0x61, 0x68, 0x62, 0xff, 0x60, 0x07 };
+       static const __u8 n3[6] =
+                       {0x61, 0x68, 0x65, 0x0a, 0x60, 0x04};
+       static const __u8 n4[0x46] =
+               {0x09, 0x01, 0x12, 0x04, 0x66, 0x8a, 0x80, 0x3c,
+                0x81, 0x22, 0x84, 0x50, 0x8a, 0x78, 0x8b, 0x68,
+                0x8c, 0x88, 0x8e, 0x33, 0x8f, 0x24, 0xaa, 0xb1,
+                0xa2, 0x60, 0xa5, 0x30, 0xa6, 0x3a, 0xa8, 0xe8,
+                0xae, 0x05, 0xb1, 0x00, 0xbb, 0x04, 0xbc, 0x48,
+                0xbe, 0x36, 0xc6, 0x88, 0xe9, 0x00, 0xc5, 0xc0,
+                0x65, 0x0a, 0xbb, 0x86, 0xaf, 0x58, 0xb0, 0x68,
+                0x87, 0x40, 0x89, 0x2b, 0x8d, 0xff, 0x83, 0x40,
+                0xac, 0x84, 0xad, 0x86, 0xaf, 0x46};
+       static const __u8 nset4[18] = {
+               0xe0, 0x60, 0xe1, 0xa8, 0xe2, 0xe0, 0xe3, 0x60, 0xe4, 0xa8,
+               0xe5, 0xe0, 0xe6, 0x60, 0xe7, 0xa8,
+               0xe8, 0xe0
+       };
+       /* ojo puede ser 0xe6 en vez de 0xe9 */
+       static const __u8 nset2[20] = {
+               0xd0, 0xbb, 0xd1, 0x28, 0xd2, 0x10, 0xd3, 0x10, 0xd4, 0xbb,
+               0xd5, 0x28, 0xd6, 0x1e, 0xd7, 0x27,
+               0xd8, 0xc8, 0xd9, 0xfc
+       };
+       static const __u8 missing[8] =
+               { 0x87, 0x20, 0x88, 0x20, 0x89, 0x20, 0x80, 0x38 };
+       static const __u8 nset3[18] = {
+               0xc7, 0x60, 0xc8, 0xa8, 0xc9, 0xe0, 0xca, 0x60, 0xcb, 0xa8,
+               0xcc, 0xe0, 0xcd, 0x60, 0xce, 0xa8,
+               0xcf, 0xe0
+       };
+       static const __u8 nset5[4] =
+               { 0x8f, 0x24, 0xc3, 0x00 };     /* bright */
+       static const __u8 nset6[34] = {
+               0x90, 0x00, 0x91, 0x1c, 0x92, 0x30, 0x93, 0x43, 0x94, 0x54,
+               0x95, 0x65, 0x96, 0x75, 0x97, 0x84,
+               0x98, 0x93, 0x99, 0xa1, 0x9a, 0xb0, 0x9b, 0xbd, 0x9c, 0xca,
+               0x9d, 0xd8, 0x9e, 0xe5, 0x9f, 0xf2,
+               0xa0, 0xff
+       };                      /* Gamma */
+       static const __u8 nset7[4] =
+                       { 0x66, 0xca, 0xa8, 0xf8 };     /* 50/60 Hz */
+       static const __u8 nset9[4] =
+                       { 0x0b, 0x04, 0x0a, 0x78 };
+       static const __u8 nset8[6] =
+                       { 0xa8, 0xf0, 0xc6, 0x88, 0xc0, 0x00 };
+       static const __u8 nset10[6] =
+                       { 0x0c, 0x03, 0xab, 0x10, 0x81, 0x20 };
+
+       reg_w(gspca_dev, 0x01, 0x0000, n1, 0x06);
+       reg_w(gspca_dev, 0x01, 0x0000, nset, 0x06);
+       reg_r_1(gspca_dev, 0x0063);
+       reg_w(gspca_dev, 0x01, 0x0000, n2, 0x02);
+
+       while (read_indexs[i] != 0x00) {
+               test_byte = reg_r_1(gspca_dev, read_indexs[i]);
+               PDEBUG(D_CONF, "Reg 0x%02x => 0x%02x", read_indexs[i],
+                      test_byte);
+               i++;
+       }
+
+       reg_w(gspca_dev, 0x01, 0x0000, n3, 0x06);
+       reg_w(gspca_dev, 0x01, 0x0000, n4, 0x46);
+       reg_r_1(gspca_dev, 0x0080);
+       reg_w(gspca_dev, 0x00, 0x2c80, NULL, 0);
+       reg_w(gspca_dev, 0x01, 0x0000, nset2, 0x14);
+       reg_w(gspca_dev, 0x01, 0x0000, nset3, 0x12);
+       reg_w(gspca_dev, 0x01, 0x0000, nset4, 0x12);
+       reg_w(gspca_dev, 0x00, 0x3880, NULL, 0);
+       reg_w(gspca_dev, 0x00, 0x3880, NULL, 0);
+       reg_w(gspca_dev, 0x00, 0x338e, NULL, 0);
+       reg_w(gspca_dev, 0x01, 0x0000, nset5, 0x04);
+       reg_w(gspca_dev, 0x00, 0x00a9, NULL, 0);
+       reg_w(gspca_dev, 0x01, 0x0000, nset6, 0x22);
+       reg_w(gspca_dev, 0x00, 0x86bb, NULL, 0);
+       reg_w(gspca_dev, 0x00, 0x4aa6, NULL, 0);
+
+       reg_w(gspca_dev, 0x01, 0x0000, missing, 0x08);
+
+       reg_w(gspca_dev, 0x00, 0x2087, NULL, 0);
+       reg_w(gspca_dev, 0x00, 0x2088, NULL, 0);
+       reg_w(gspca_dev, 0x00, 0x2089, NULL, 0);
+
+       reg_w(gspca_dev, 0x01, 0x0000, nset7, 0x04);
+       reg_w(gspca_dev, 0x01, 0x0000, nset10, 0x06);
+       reg_w(gspca_dev, 0x01, 0x0000, nset8, 0x06);
+       reg_w(gspca_dev, 0x01, 0x0000, nset9, 0x04);
+
+       reg_w(gspca_dev, 0x00, 0x2880, NULL, 0);
+       reg_w(gspca_dev, 0x01, 0x0000, nset2, 0x14);
+       reg_w(gspca_dev, 0x01, 0x0000, nset3, 0x12);
+       reg_w(gspca_dev, 0x01, 0x0000, nset4, 0x12);
+
+       return 0;
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       unsigned int brightness;
+       __u8 set6[4] = { 0x8f, 0x26, 0xc3, 0x80 };
+       brightness = sd->brightness;
+
+       if (brightness < 7) {
+               set6[3] = 0x70 - (brightness * 0xa);
+       } else {
+               set6[1] = 0x24;
+               set6[3] = 0x00 + ((brightness - 7) * 0xa);
+       }
+
+       reg_w(gspca_dev, 0x01, 0x0000, set6, 4);
+}
+
+static void setflip(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       __u8 flipcmd[8] =
+           { 0x62, 0x07, 0x63, 0x03, 0x64, 0x00, 0x60, 0x09 };
+
+       if (sd->mirror == 1)
+               flipcmd[3] = 0x01;
+
+       reg_w(gspca_dev, 0x01, 0x0000, flipcmd, 8);
+}
+
+static void seteffect(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       reg_w(gspca_dev, 0x01, 0x0000, effects_table[sd->effect], 0x06);
+       if (sd->effect == 1 || sd->effect == 5) {
+               PDEBUG(D_CONF,
+                      "This effect have been disabled for webcam \"safety\"");
+               return;
+       }
+
+       if (sd->effect == 1 || sd->effect == 4)
+               reg_w(gspca_dev, 0x00, 0x4aa6, NULL, 0);
+       else
+               reg_w(gspca_dev, 0x00, 0xfaa6, NULL, 0);
+}
+
+static void setwhitebalance(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       __u8 white_balance[8] =
+           { 0x87, 0x20, 0x88, 0x20, 0x89, 0x20, 0x80, 0x38 };
+
+       if (sd->whitebalance == 1)
+               white_balance[7] = 0x3c;
+
+       reg_w(gspca_dev, 0x01, 0x0000, white_balance, 8);
+}
+
+static void setlightfreq(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       __u8 freq[4] = { 0x66, 0x40, 0xa8, 0xe8 };
+
+       if (sd->freq == 2)      /* 60hz */
+               freq[1] = 0x00;
+
+       reg_w(gspca_dev, 0x1, 0x0000, freq, 0x4);
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       unsigned int contrast = sd->contrast;
+       __u16 reg_to_write = 0x00;
+
+       if (contrast < 7)
+               reg_to_write = 0x8ea9 - (0x200 * contrast);
+       else
+               reg_to_write = (0x00a9 + ((contrast - 7) * 0x200));
+
+       reg_w(gspca_dev, 0x00, reg_to_write, NULL, 0);
+}
+
+static void setcolors(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       __u16 reg_to_write;
+
+       reg_to_write = 0xc0bb + sd->colors * 0x100;
+       reg_w(gspca_dev, 0x00, reg_to_write, NULL, 0);
+}
+
+static void setgamma(struct gspca_dev *gspca_dev)
+{
+}
+
+static void setsharpness(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       __u16 reg_to_write;
+
+       reg_to_write = 0x0aa6 + 0x1000 * sd->sharpness;
+
+       reg_w(gspca_dev, 0x00, reg_to_write, NULL, 0);
+}
+
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->brightness = val;
+       if (gspca_dev->streaming)
+               setbrightness(gspca_dev);
+       return 0;
+}
+
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->brightness;
+       return *val;
+}
+
+static int sd_setwhitebalance(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->whitebalance = val;
+       if (gspca_dev->streaming)
+               setwhitebalance(gspca_dev);
+       return 0;
+}
+
+static int sd_getwhitebalance(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->whitebalance;
+       return *val;
+}
+
+static int sd_setflip(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->mirror = val;
+       if (gspca_dev->streaming)
+               setflip(gspca_dev);
+       return 0;
+}
+
+static int sd_getflip(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->mirror;
+       return *val;
+}
+
+static int sd_seteffect(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->effect = val;
+       if (gspca_dev->streaming)
+               seteffect(gspca_dev);
+       return 0;
+}
+
+static int sd_geteffect(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->effect;
+       return *val;
+}
+
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->contrast = val;
+       if (gspca_dev->streaming)
+               setcontrast(gspca_dev);
+       return 0;
+}
+
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->contrast;
+       return *val;
+}
+
+static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->colors = val;
+       if (gspca_dev->streaming)
+               setcolors(gspca_dev);
+       return 0;
+}
+
+static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->colors;
+       return 0;
+}
+
+static int sd_setgamma(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->gamma = val;
+       if (gspca_dev->streaming)
+               setgamma(gspca_dev);
+       return 0;
+}
+
+static int sd_getgamma(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       *val = sd->gamma;
+       return 0;
+}
+
+static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->freq = val;
+       if (gspca_dev->streaming)
+               setlightfreq(gspca_dev);
+       return 0;
+}
+
+static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->freq;
+       return 0;
+}
+
+static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->sharpness = val;
+       if (gspca_dev->streaming)
+               setsharpness(gspca_dev);
+       return 0;
+}
+
+static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->sharpness;
+       return 0;
+}
+
+/* Low Light set  here......*/
+static int sd_setlowlight(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->autogain = val;
+       if (val != 0)
+               reg_w(gspca_dev, 0x00, 0xf48e, NULL, 0);
+       else
+               reg_w(gspca_dev, 0x00, 0xb48e, NULL, 0);
+       return 0;
+}
+
+static int sd_getlowlight(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->autogain;
+       return 0;
+}
+
+static void sd_start(struct gspca_dev *gspca_dev)
+{
+       int mode;
+
+       static const __u8 t1[] = { 0x66, 0x00, 0xa8, 0xe8 };
+       __u8 t2[] = { 0x07, 0x00, 0x0d, 0x60, 0x0e, 0x80 };
+       static const __u8 t3[] =
+               { 0xb3, 0x07, 0xb4, 0x00, 0xb5, 0x88, 0xb6, 0x02, 0xb7, 0x06,
+                 0xb8, 0x00, 0xb9, 0xe7, 0xba, 0x01 };
+       static const __u8 t4[] = { 0x0b, 0x04, 0x0a, 0x40 };
+
+       mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode]. priv;
+       switch (mode) {
+       case 1:         /* 352x288 */
+               t2[1] = 0x40;
+               break;
+       case 2:         /* 320x240 */
+               t2[1] = 0x10;
+               break;
+       case 3:         /* 176x144 */
+               t2[1] = 0x50;
+               break;
+       case 4:         /* 160x120 */
+               t2[1] = 0x20;
+               break;
+       default:        /* 640x480 (0x00) */
+               break;
+       }
+
+       reg_w(gspca_dev, 0x01, 0x0000, tas5130a_sensor_init[0], 0x8);
+       reg_w(gspca_dev, 0x01, 0x0000, tas5130a_sensor_init[1], 0x8);
+       reg_w(gspca_dev, 0x01, 0x0000, tas5130a_sensor_init[2], 0x8);
+       reg_w(gspca_dev, 0x01, 0x0000, tas5130a_sensor_init[3], 0x8);
+       reg_w(gspca_dev, 0x00, 0x3c80, NULL, 0);
+               /* just in case and to keep sync with logs  (for mine) */
+       reg_w(gspca_dev, 0x01, 0x0000, tas5130a_sensor_init[3], 0x8);
+       reg_w(gspca_dev, 0x00, 0x3c80, NULL, 0);
+               /* just in case and to keep sync with logs  (for mine) */
+       reg_w(gspca_dev, 0x01, 0x0000, t1, 4);
+       reg_w(gspca_dev, 0x01, 0x0000, t2, 6);
+       reg_r_1(gspca_dev, 0x0012);
+       reg_w(gspca_dev, 0x01, 0x0000, t3, 0x10);
+       reg_w(gspca_dev, 0x00, 0x0013, NULL, 0);
+       reg_w(gspca_dev, 0x01, 0x0000, t4, 0x4);
+       /* restart on each start, just in case, sometimes regs goes wrong
+        * when using controls from app */
+       setbrightness(gspca_dev);
+       setcontrast(gspca_dev);
+       setcolors(gspca_dev);
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+}
+
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+}
+
+static void sd_close(struct gspca_dev *gspca_dev)
+{
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+                       struct gspca_frame *frame,      /* target */
+                       __u8 *data,                     /* isoc packet */
+                       int len)                        /* iso packet length */
+{
+       int sof = 0;
+       static __u8 ffd9[] = { 0xff, 0xd9 };
+
+       if (data[0] == 0x5a) {
+               /* Control Packet, after this came the header again,
+                * but extra bytes came in the packet before this,
+                * sometimes an EOF arrives, sometimes not... */
+               return;
+       }
+
+       if (data[len - 1] == 0xff && data[len] == 0xd9) {
+               /* Just in case, i have seen packets with the marker,
+                * other's do not include it... */
+               data += 2;
+               len -= 4;
+       } else if (data[2] == 0xff && data[3] == 0xd8) {
+               sof = 1;
+               data += 2;
+               len -= 2;
+       } else {
+               data += 2;
+               len -= 2;
+       }
+
+       if (sof) {
+               /* extra bytes....., could be processed too but would be
+                * a waste of time, right now leave the application and
+                * libjpeg do it for ourserlves.. */
+               frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
+                                       ffd9, 2);
+               gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, len);
+               return;
+       }
+
+       gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
+}
+
+static int sd_querymenu(struct gspca_dev *gspca_dev,
+                       struct v4l2_querymenu *menu)
+{
+       switch (menu->id) {
+       case V4L2_CID_POWER_LINE_FREQUENCY:
+               switch (menu->index) {
+               case 1:         /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */
+                       strcpy((char *) menu->name, "50 Hz");
+                       return 0;
+               case 2:         /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */
+                       strcpy((char *) menu->name, "60 Hz");
+                       return 0;
+               }
+               break;
+       case V4L2_CID_EFFECTS:
+               if ((unsigned) menu->index < ARRAY_SIZE(effects_control)) {
+                       strncpy((char *) menu->name,
+                               effects_control[menu->index], 32);
+                       return 0;
+               }
+               break;
+       }
+       return -EINVAL;
+}
+
+/* this function is called at open time */
+static int sd_open(struct gspca_dev *gspca_dev)
+{
+       init_default_parameters(gspca_dev);
+       return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+       .name = MODULE_NAME,
+       .ctrls = sd_ctrls,
+       .nctrls = ARRAY_SIZE(sd_ctrls),
+       .config = sd_config,
+       .open = sd_open,
+       .start = sd_start,
+       .stopN = sd_stopN,
+       .stop0 = sd_stop0,
+       .close = sd_close,
+       .pkt_scan = sd_pkt_scan,
+       .querymenu = sd_querymenu,
+};
+
+/* -- module initialisation -- */
+#define DVNM(name) .driver_info = (kernel_ulong_t) name
+static const __devinitdata struct usb_device_id device_table[] = {
+       {USB_DEVICE(0x17a1, 0x0128), DVNM("XPX Webcam")},
+       {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+                   const struct usb_device_id *id)
+{
+       return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+                              THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+       .name = MODULE_NAME,
+       .id_table = device_table,
+       .probe = sd_probe,
+       .disconnect = gspca_disconnect,
+};
+
+/* -- module insert / remove -- */
+static int __init sd_mod_init(void)
+{
+       if (usb_register(&sd_driver) < 0)
+               return -1;
+       PDEBUG(D_PROBE, "v%s registered", version);
+       return 0;
+}
+static void __exit sd_mod_exit(void)
+{
+       usb_deregister(&sd_driver);
+       PDEBUG(D_PROBE, "deregistered");
+}
+
+module_init(sd_mod_init);
+module_exit(sd_mod_exit);
diff --git a/drivers/media/video/gspca/tv8532.c b/drivers/media/video/gspca/tv8532.c
new file mode 100644 (file)
index 0000000..0b79389
--- /dev/null
@@ -0,0 +1,670 @@
+/*
+ * Quickcam cameras initialization data
+ *
+ * V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
+ *
+ * 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
+ * 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
+ *
+ */
+#define MODULE_NAME "tv8532"
+
+#include "gspca.h"
+
+#define DRIVER_VERSION_NUMBER  KERNEL_VERSION(2, 1, 7)
+static const char version[] = "2.1.7";
+
+MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
+MODULE_DESCRIPTION("TV8532 USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* specific webcam descriptor */
+struct sd {
+       struct gspca_dev gspca_dev;     /* !! must be the first item */
+
+       int buflen;                     /* current length of tmpbuf */
+       __u8 tmpbuf[352 * 288 + 10 * 288];      /* no protection... */
+       __u8 tmpbuf2[352 * 288];                /* no protection... */
+
+       unsigned short brightness;
+       unsigned short contrast;
+
+       char packet;
+       char synchro;
+};
+
+/* V4L2 controls supported by the driver */
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
+
+static struct ctrl sd_ctrls[] = {
+#define SD_BRIGHTNESS 0
+       {
+        {
+         .id = V4L2_CID_BRIGHTNESS,
+         .type = V4L2_CTRL_TYPE_INTEGER,
+         .name = "Brightness",
+         .minimum = 1,
+         .maximum = 0x2ff,
+         .step = 1,
+         .default_value = 0x18f,
+         },
+        .set = sd_setbrightness,
+        .get = sd_getbrightness,
+        },
+#define SD_CONTRAST 1
+       {
+        {
+         .id = V4L2_CID_CONTRAST,
+         .type = V4L2_CTRL_TYPE_INTEGER,
+         .name = "Contrast",
+         .minimum = 0,
+         .maximum = 0xffff,
+         .step = 1,
+         .default_value = 0x7fff,
+         },
+        .set = sd_setcontrast,
+        .get = sd_getcontrast,
+        },
+};
+
+static struct v4l2_pix_format sif_mode[] = {
+       {176, 144, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+               .bytesperline = 176,
+               .sizeimage = 176 * 144,
+               .colorspace = V4L2_COLORSPACE_SRGB,
+               .priv = 1},
+       {352, 288, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+               .bytesperline = 352,
+               .sizeimage = 352 * 288,
+               .colorspace = V4L2_COLORSPACE_SRGB,
+               .priv = 0},
+};
+
+/*
+ * Initialization data: this is the first set-up data written to the
+ * device (before the open data).
+ */
+#define TESTCLK 0x10           /* reg 0x2c -> 0x12 //10 */
+#define TESTCOMP 0x90          /* reg 0x28 -> 0x80 */
+#define TESTLINE 0x81          /* reg 0x29 -> 0x81 */
+#define QCIFLINE 0x41          /* reg 0x29 -> 0x81 */
+#define TESTPTL 0x14           /* reg 0x2D -> 0x14 */
+#define TESTPTH 0x01           /* reg 0x2E -> 0x01 */
+#define TESTPTBL 0x12          /* reg 0x2F -> 0x0a */
+#define TESTPTBH 0x01          /* reg 0x30 -> 0x01 */
+#define ADWIDTHL 0xe8          /* reg 0x0c -> 0xe8 */
+#define ADWIDTHH 0x03          /* reg 0x0d -> 0x03 */
+#define ADHEIGHL 0x90          /* reg 0x0e -> 0x91 //93 */
+#define ADHEIGHH 0x01          /* reg 0x0f -> 0x01 */
+#define EXPOL 0x8f             /* reg 0x1c -> 0x8f */
+#define EXPOH 0x01             /* reg 0x1d -> 0x01 */
+#define ADCBEGINL 0x44         /* reg 0x10 -> 0x46 //47 */
+#define ADCBEGINH 0x00         /* reg 0x11 -> 0x00 */
+#define ADRBEGINL 0x0a         /* reg 0x14 -> 0x0b //0x0c */
+#define ADRBEGINH 0x00         /* reg 0x15 -> 0x00 */
+#define TV8532_CMD_UPDATE 0x84
+
+#define TV8532_EEprom_Add 0x03
+#define TV8532_EEprom_DataL 0x04
+#define TV8532_EEprom_DataM 0x05
+#define TV8532_EEprom_DataH 0x06
+#define TV8532_EEprom_TableLength 0x07
+#define TV8532_EEprom_Write 0x08
+#define TV8532_PART_CTRL 0x00
+#define TV8532_CTRL 0x01
+#define TV8532_CMD_EEprom_Open 0x30
+#define TV8532_CMD_EEprom_Close 0x29
+#define TV8532_UDP_UPDATE 0x31
+#define TV8532_GPIO 0x39
+#define TV8532_GPIO_OE 0x3B
+#define TV8532_REQ_RegWrite 0x02
+#define TV8532_REQ_RegRead 0x03
+
+#define TV8532_ADWIDTH_L 0x0C
+#define TV8532_ADWIDTH_H 0x0D
+#define TV8532_ADHEIGHT_L 0x0E
+#define TV8532_ADHEIGHT_H 0x0F
+#define TV8532_EXPOSURE 0x1C
+#define TV8532_QUANT_COMP 0x28
+#define TV8532_MODE_PACKET 0x29
+#define TV8532_SETCLK 0x2C
+#define TV8532_POINT_L 0x2D
+#define TV8532_POINT_H 0x2E
+#define TV8532_POINTB_L 0x2F
+#define TV8532_POINTB_H 0x30
+#define TV8532_BUDGET_L 0x2A
+#define TV8532_BUDGET_H 0x2B
+#define TV8532_VID_L 0x34
+#define TV8532_VID_H 0x35
+#define TV8532_PID_L 0x36
+#define TV8532_PID_H 0x37
+#define TV8532_DeviceID 0x83
+#define TV8532_AD_SLOPE 0x91
+#define TV8532_AD_BITCTRL 0x94
+#define TV8532_AD_COLBEGIN_L 0x10
+#define TV8532_AD_COLBEGIN_H 0x11
+#define TV8532_AD_ROWBEGIN_L 0x14
+#define TV8532_AD_ROWBEGIN_H 0x15
+
+static const __u32 tv_8532_eeprom_data[] = {
+/*     add             dataL      dataM        dataH */
+       0x00010001, 0x01018011, 0x02050014, 0x0305001c,
+       0x040d001e, 0x0505001f, 0x06050519, 0x0705011b,
+       0x0805091e, 0x090d892e, 0x0a05892f, 0x0b050dd9,
+       0x0c0509f1, 0
+};
+
+static int reg_r(struct gspca_dev *gspca_dev,
+                __u16 index)
+{
+       usb_control_msg(gspca_dev->dev,
+                       usb_rcvctrlpipe(gspca_dev->dev, 0),
+                       TV8532_REQ_RegRead,
+                       USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                       0,      /* value */
+                       index, gspca_dev->usb_buf, 1,
+                       500);
+       return gspca_dev->usb_buf[0];
+}
+
+/* write 1 byte */
+static void reg_w_1(struct gspca_dev *gspca_dev,
+                 __u16 index, __u8 value)
+{
+       gspca_dev->usb_buf[0] = value;
+       usb_control_msg(gspca_dev->dev,
+                       usb_sndctrlpipe(gspca_dev->dev, 0),
+                       TV8532_REQ_RegWrite,
+                       USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                       0,      /* value */
+                       index, gspca_dev->usb_buf, 1, 500);
+}
+
+/* write 2 bytes */
+static void reg_w_2(struct gspca_dev *gspca_dev,
+                 __u16 index, __u8 val1, __u8 val2)
+{
+       gspca_dev->usb_buf[0] = val1;
+       gspca_dev->usb_buf[1] = val2;
+       usb_control_msg(gspca_dev->dev,
+                       usb_sndctrlpipe(gspca_dev->dev, 0),
+                       TV8532_REQ_RegWrite,
+                       USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                       0,      /* value */
+                       index, gspca_dev->usb_buf, 2, 500);
+}
+
+static void tv_8532WriteEEprom(struct gspca_dev *gspca_dev)
+{
+       int i = 0;
+       __u8 reg, data0, data1, data2;
+
+       reg_w_1(gspca_dev, TV8532_GPIO, 0xb0);
+       reg_w_1(gspca_dev, TV8532_CTRL, TV8532_CMD_EEprom_Open);
+/*     msleep(1); */
+       while (tv_8532_eeprom_data[i]) {
+               reg = (tv_8532_eeprom_data[i] & 0xff000000) >> 24;
+               reg_w_1(gspca_dev, TV8532_EEprom_Add, reg);
+               /* msleep(1); */
+               data0 = (tv_8532_eeprom_data[i] & 0x000000ff);
+               reg_w_1(gspca_dev, TV8532_EEprom_DataL, data0);
+               /* msleep(1); */
+               data1 = (tv_8532_eeprom_data[i] & 0x0000ff00) >> 8;
+               reg_w_1(gspca_dev, TV8532_EEprom_DataM, data1);
+               /* msleep(1); */
+               data2 = (tv_8532_eeprom_data[i] & 0x00ff0000) >> 16;
+               reg_w_1(gspca_dev, TV8532_EEprom_DataH, data2);
+               /* msleep(1); */
+               reg_w_1(gspca_dev, TV8532_EEprom_Write, 0);
+               /* msleep(10); */
+               i++;
+       }
+       reg_w_1(gspca_dev, TV8532_EEprom_TableLength, i);
+/*     msleep(1); */
+       reg_w_1(gspca_dev, TV8532_CTRL, TV8532_CMD_EEprom_Close);
+       msleep(10);
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+                    const struct usb_device_id *id)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       struct cam *cam;
+
+       tv_8532WriteEEprom(gspca_dev);
+
+       cam = &gspca_dev->cam;
+       cam->dev_name = (char *) id->driver_info;
+       cam->epaddr = 1;
+       cam->cam_mode = sif_mode;
+       cam->nmodes = sizeof sif_mode / sizeof sif_mode[0];
+
+       sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value;
+       sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value;
+       return 0;
+}
+
+static void tv_8532ReadRegisters(struct gspca_dev *gspca_dev)
+{
+       __u8 data;
+
+       data = reg_r(gspca_dev, 0x0001);
+       PDEBUG(D_USBI, "register 0x01-> %x", data);
+       data = reg_r(gspca_dev, 0x0002);
+       PDEBUG(D_USBI, "register 0x02-> %x", data);
+       reg_r(gspca_dev, TV8532_ADWIDTH_L);
+       reg_r(gspca_dev, TV8532_ADWIDTH_H);
+       reg_r(gspca_dev, TV8532_QUANT_COMP);
+       reg_r(gspca_dev, TV8532_MODE_PACKET);
+       reg_r(gspca_dev, TV8532_SETCLK);
+       reg_r(gspca_dev, TV8532_POINT_L);
+       reg_r(gspca_dev, TV8532_POINT_H);
+       reg_r(gspca_dev, TV8532_POINTB_L);
+       reg_r(gspca_dev, TV8532_POINTB_H);
+       reg_r(gspca_dev, TV8532_BUDGET_L);
+       reg_r(gspca_dev, TV8532_BUDGET_H);
+       reg_r(gspca_dev, TV8532_VID_L);
+       reg_r(gspca_dev, TV8532_VID_H);
+       reg_r(gspca_dev, TV8532_PID_L);
+       reg_r(gspca_dev, TV8532_PID_H);
+       reg_r(gspca_dev, TV8532_DeviceID);
+       reg_r(gspca_dev, TV8532_AD_COLBEGIN_L);
+       reg_r(gspca_dev, TV8532_AD_COLBEGIN_H);
+       reg_r(gspca_dev, TV8532_AD_ROWBEGIN_L);
+       reg_r(gspca_dev, TV8532_AD_ROWBEGIN_H);
+}
+
+static void tv_8532_setReg(struct gspca_dev *gspca_dev)
+{
+       reg_w_1(gspca_dev, TV8532_AD_COLBEGIN_L,
+                       ADCBEGINL);                     /* 0x10 */
+       reg_w_1(gspca_dev, TV8532_AD_COLBEGIN_H,
+                       ADCBEGINH);                     /* also digital gain */
+       reg_w_1(gspca_dev, TV8532_PART_CTRL,
+                       TV8532_CMD_UPDATE);             /* 0x00<-0x84 */
+
+       reg_w_1(gspca_dev, TV8532_GPIO_OE, 0x0a);
+       /******************************************************/
+       reg_w_1(gspca_dev, TV8532_ADHEIGHT_L, ADHEIGHL); /* 0e */
+       reg_w_1(gspca_dev, TV8532_ADHEIGHT_H, ADHEIGHH); /* 0f */
+       reg_w_2(gspca_dev, TV8532_EXPOSURE,
+                       EXPOL, EXPOH);                  /* 350d 0x014c; 1c */
+       reg_w_1(gspca_dev, TV8532_AD_COLBEGIN_L,
+                       ADCBEGINL);                     /* 0x10 */
+       reg_w_1(gspca_dev, TV8532_AD_COLBEGIN_H,
+                       ADCBEGINH);                     /* also digital gain */
+       reg_w_1(gspca_dev, TV8532_AD_ROWBEGIN_L,
+                       ADRBEGINL);                     /* 0x14 */
+
+       reg_w_1(gspca_dev, TV8532_AD_SLOPE, 0x00);      /* 0x91 */
+       reg_w_1(gspca_dev, TV8532_AD_BITCTRL, 0x02);    /* 0x94 */
+
+       reg_w_1(gspca_dev, TV8532_CTRL,
+                       TV8532_CMD_EEprom_Close);       /* 0x01 */
+
+       reg_w_1(gspca_dev, TV8532_AD_SLOPE, 0x00);      /* 0x91 */
+       reg_w_1(gspca_dev, TV8532_PART_CTRL,
+                       TV8532_CMD_UPDATE);             /* 0x00<-0x84 */
+}
+
+static void tv_8532_PollReg(struct gspca_dev *gspca_dev)
+{
+       int i;
+
+       /* strange polling from tgc */
+       for (i = 0; i < 10; i++) {
+               reg_w_1(gspca_dev, TV8532_SETCLK,
+                       TESTCLK);               /* 0x48; //0x08; 0x2c */
+               reg_w_1(gspca_dev, TV8532_PART_CTRL, TV8532_CMD_UPDATE);
+               reg_w_1(gspca_dev, TV8532_UDP_UPDATE, 0x01);    /* 0x31 */
+       }
+}
+
+/* this function is called at open time */
+static int sd_open(struct gspca_dev *gspca_dev)
+{
+       reg_w_1(gspca_dev, TV8532_AD_SLOPE, 0x32);
+       reg_w_1(gspca_dev, TV8532_AD_BITCTRL, 0x00);
+       tv_8532ReadRegisters(gspca_dev);
+       reg_w_1(gspca_dev, TV8532_GPIO_OE, 0x0b);
+       reg_w_2(gspca_dev, TV8532_ADHEIGHT_L, ADHEIGHL,
+                               ADHEIGHH);      /* 401d 0x0169; 0e */
+       reg_w_2(gspca_dev, TV8532_EXPOSURE, EXPOL,
+                               EXPOH);         /* 350d 0x014c; 1c */
+       reg_w_1(gspca_dev, TV8532_ADWIDTH_L, ADWIDTHL); /* 0x20; 0x0c */
+       reg_w_1(gspca_dev, TV8532_ADWIDTH_H, ADWIDTHH); /* 0x0d */
+
+       /*******************************************************************/
+       reg_w_1(gspca_dev, TV8532_QUANT_COMP,
+                       TESTCOMP);      /* 0x72 compressed mode 0x28 */
+       reg_w_1(gspca_dev, TV8532_MODE_PACKET,
+                       TESTLINE);      /* 0x84; // CIF | 4 packet 0x29 */
+
+       /************************************************/
+       reg_w_1(gspca_dev, TV8532_SETCLK,
+                       TESTCLK);               /* 0x48; //0x08; 0x2c */
+       reg_w_1(gspca_dev, TV8532_POINT_L,
+                       TESTPTL);               /* 0x38; 0x2d */
+       reg_w_1(gspca_dev, TV8532_POINT_H,
+                       TESTPTH);               /* 0x04; 0x2e */
+       reg_w_1(gspca_dev, TV8532_POINTB_L,
+                       TESTPTBL);              /* 0x04; 0x2f */
+       reg_w_1(gspca_dev, TV8532_POINTB_H,
+                       TESTPTBH);              /* 0x04; 0x30 */
+       reg_w_1(gspca_dev, TV8532_PART_CTRL,
+                       TV8532_CMD_UPDATE);     /* 0x00<-0x84 */
+       /*************************************************/
+       reg_w_1(gspca_dev, TV8532_UDP_UPDATE, 0x01);    /* 0x31 */
+       msleep(200);
+       reg_w_1(gspca_dev, TV8532_UDP_UPDATE, 0x00);    /* 0x31 */
+       /*************************************************/
+       tv_8532_setReg(gspca_dev);
+       /*************************************************/
+       reg_w_1(gspca_dev, TV8532_GPIO_OE, 0x0b);
+       /*************************************************/
+       tv_8532_setReg(gspca_dev);
+       /*************************************************/
+       tv_8532_PollReg(gspca_dev);
+       return 0;
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       int brightness = sd->brightness;
+
+       reg_w_2(gspca_dev, TV8532_EXPOSURE,
+               brightness >> 8, brightness);           /* 1c */
+       reg_w_1(gspca_dev, TV8532_PART_CTRL, TV8532_CMD_UPDATE);
+}
+
+/* -- start the camera -- */
+static void sd_start(struct gspca_dev *gspca_dev)
+{
+       reg_w_1(gspca_dev, TV8532_AD_SLOPE, 0x32);
+       reg_w_1(gspca_dev, TV8532_AD_BITCTRL, 0x00);
+       tv_8532ReadRegisters(gspca_dev);
+       reg_w_1(gspca_dev, TV8532_GPIO_OE, 0x0b);
+       reg_w_2(gspca_dev, TV8532_ADHEIGHT_L,
+               ADHEIGHL, ADHEIGHH);    /* 401d 0x0169; 0e */
+/*     reg_w_2(gspca_dev, TV8532_EXPOSURE,
+               EXPOL, EXPOH);           * 350d 0x014c; 1c */
+       setbrightness(gspca_dev);
+
+       reg_w_1(gspca_dev, TV8532_ADWIDTH_L, ADWIDTHL); /* 0x20; 0x0c */
+       reg_w_1(gspca_dev, TV8532_ADWIDTH_H, ADWIDTHH); /* 0x0d */
+
+       /************************************************/
+       reg_w_1(gspca_dev, TV8532_QUANT_COMP,
+                       TESTCOMP);      /* 0x72 compressed mode 0x28 */
+       if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) {
+               /* 176x144 */
+               reg_w_1(gspca_dev, TV8532_MODE_PACKET,
+                       QCIFLINE);      /* 0x84; // CIF | 4 packet 0x29 */
+       } else {
+               /* 352x288 */
+               reg_w_1(gspca_dev, TV8532_MODE_PACKET,
+                       TESTLINE);      /* 0x84; // CIF | 4 packet 0x29 */
+       }
+       /************************************************/
+       reg_w_1(gspca_dev, TV8532_SETCLK,
+                       TESTCLK);               /* 0x48; //0x08; 0x2c */
+       reg_w_1(gspca_dev, TV8532_POINT_L,
+                       TESTPTL);               /* 0x38; 0x2d */
+       reg_w_1(gspca_dev, TV8532_POINT_H,
+                       TESTPTH);               /* 0x04; 0x2e */
+       reg_w_1(gspca_dev, TV8532_POINTB_L,
+                       TESTPTBL);              /* 0x04; 0x2f */
+       reg_w_1(gspca_dev, TV8532_POINTB_H,
+                       TESTPTBH);              /* 0x04; 0x30 */
+       reg_w_1(gspca_dev, TV8532_PART_CTRL,
+                       TV8532_CMD_UPDATE);     /* 0x00<-0x84 */
+       /************************************************/
+       reg_w_1(gspca_dev, TV8532_UDP_UPDATE, 0x01);    /* 0x31 */
+       msleep(200);
+       reg_w_1(gspca_dev, TV8532_UDP_UPDATE, 0x00);    /* 0x31 */
+       /************************************************/
+       tv_8532_setReg(gspca_dev);
+       /************************************************/
+       reg_w_1(gspca_dev, TV8532_GPIO_OE, 0x0b);
+       /************************************************/
+       tv_8532_setReg(gspca_dev);
+       /************************************************/
+       tv_8532_PollReg(gspca_dev);
+       reg_w_1(gspca_dev, TV8532_UDP_UPDATE, 0x00);    /* 0x31 */
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+       reg_w_1(gspca_dev, TV8532_GPIO_OE, 0x0b);
+}
+
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+}
+
+static void sd_close(struct gspca_dev *gspca_dev)
+{
+}
+
+static void tv8532_preprocess(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+/* we should received a whole frame with header and EOL marker
+ * in gspca_dev->tmpbuf and return a GBRG pattern in gspca_dev->tmpbuf2
+ * sequence 2bytes header the Alternate pixels bayer GB 4 bytes
+ * Alternate pixels bayer RG 4 bytes EOL */
+       int width = gspca_dev->width;
+       int height = gspca_dev->height;
+       unsigned char *dst = sd->tmpbuf2;
+       unsigned char *data = sd->tmpbuf;
+       int i;
+
+       /* precompute where is the good bayer line */
+       if (((data[3] + data[width + 7]) >> 1)
+           + (data[4] >> 2)
+           + (data[width + 6] >> 1) >= ((data[2] + data[width + 6]) >> 1)
+           + (data[3] >> 2)
+           + (data[width + 5] >> 1))
+               data += 3;
+       else
+               data += 2;
+       for (i = 0; i < height / 2; i++) {
+               memcpy(dst, data, width);
+               data += width + 3;
+               dst += width;
+               memcpy(dst, data, width);
+               data += width + 7;
+               dst += width;
+       }
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+                       struct gspca_frame *frame,      /* target */
+                       __u8 *data,                     /* isoc packet */
+                       int len)                        /* iso packet length */
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       if (data[0] != 0x80) {
+               sd->packet++;
+               if (sd->buflen + len > sizeof sd->tmpbuf) {
+                       if (gspca_dev->last_packet_type != DISCARD_PACKET) {
+                               PDEBUG(D_PACK, "buffer overflow");
+                               gspca_dev->last_packet_type = DISCARD_PACKET;
+                       }
+                       return;
+               }
+               memcpy(&sd->tmpbuf[sd->buflen], data, len);
+               sd->buflen += len;
+               return;
+       }
+
+       /* here we detect 0x80 */
+       /* counter is limited so we need few header for a frame :) */
+
+       /* header 0x80 0x80 0x80 0x80 0x80 */
+       /* packet  00   63  127  145  00   */
+       /* sof     0     1   1    0    0   */
+
+       /* update sequence */
+       if (sd->packet == 63 || sd->packet == 127)
+               sd->synchro = 1;
+
+       /* is there a frame start ? */
+       if (sd->packet >= (gspca_dev->height >> 1) - 1) {
+               PDEBUG(D_PACK, "SOF > %d packet %d", sd->synchro,
+                      sd->packet);
+               if (!sd->synchro) {     /* start of frame */
+                       if (gspca_dev->last_packet_type == FIRST_PACKET) {
+                               tv8532_preprocess(gspca_dev);
+                               frame = gspca_frame_add(gspca_dev,
+                                                       LAST_PACKET,
+                                                       frame, sd->tmpbuf2,
+                                                       gspca_dev->width *
+                                                           gspca_dev->width);
+                       }
+                       gspca_frame_add(gspca_dev, FIRST_PACKET,
+                                       frame, data, 0);
+                       memcpy(sd->tmpbuf, data, len);
+                       sd->buflen = len;
+                       sd->packet = 0;
+                       return;
+               }
+               if (gspca_dev->last_packet_type != DISCARD_PACKET) {
+                       PDEBUG(D_PACK,
+                              "Warning wrong TV8532 frame detection %d",
+                              sd->packet);
+                       gspca_dev->last_packet_type = DISCARD_PACKET;
+               }
+               return;
+       }
+
+       if (!sd->synchro) {
+               /* Drop packet frame corrupt */
+               PDEBUG(D_PACK, "DROP SOF %d packet %d",
+                      sd->synchro, sd->packet);
+               sd->packet = 0;
+               gspca_dev->last_packet_type = DISCARD_PACKET;
+               return;
+       }
+       sd->synchro = 1;
+       sd->packet++;
+       memcpy(&sd->tmpbuf[sd->buflen], data, len);
+       sd->buflen += len;
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev)
+{
+}
+
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->brightness = val;
+       if (gspca_dev->streaming)
+               setbrightness(gspca_dev);
+       return 0;
+}
+
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->brightness;
+       return 0;
+}
+
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->contrast = val;
+       if (gspca_dev->streaming)
+               setcontrast(gspca_dev);
+       return 0;
+}
+
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->contrast;
+       return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+       .name = MODULE_NAME,
+       .ctrls = sd_ctrls,
+       .nctrls = ARRAY_SIZE(sd_ctrls),
+       .config = sd_config,
+       .open = sd_open,
+       .start = sd_start,
+       .stopN = sd_stopN,
+       .stop0 = sd_stop0,
+       .close = sd_close,
+       .pkt_scan = sd_pkt_scan,
+};
+
+/* -- module initialisation -- */
+#define DVNM(name) .driver_info = (kernel_ulong_t) name
+static const __devinitdata struct usb_device_id device_table[] = {
+       {USB_DEVICE(0x046d, 0x0920), DVNM("QC Express")},
+       {USB_DEVICE(0x046d, 0x0921), DVNM("Labtec Webcam")},
+       {USB_DEVICE(0x0545, 0x808b), DVNM("Veo Stingray")},
+       {USB_DEVICE(0x0545, 0x8333), DVNM("Veo Stingray")},
+       {USB_DEVICE(0x0923, 0x010f), DVNM("ICM532 cams")},
+       {}
+};
+
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+                   const struct usb_device_id *id)
+{
+       return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+                              THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+       .name = MODULE_NAME,
+       .id_table = device_table,
+       .probe = sd_probe,
+       .disconnect = gspca_disconnect,
+};
+
+/* -- module insert / remove -- */
+static int __init sd_mod_init(void)
+{
+       if (usb_register(&sd_driver) < 0)
+               return -1;
+       PDEBUG(D_PROBE, "v%s registered", version);
+       return 0;
+}
+
+static void __exit sd_mod_exit(void)
+{
+       usb_deregister(&sd_driver);
+       PDEBUG(D_PROBE, "deregistered");
+}
+
+module_init(sd_mod_init);
+module_exit(sd_mod_exit);
diff --git a/drivers/media/video/gspca/vc032x.c b/drivers/media/video/gspca/vc032x.c
new file mode 100644 (file)
index 0000000..fcf2c9e
--- /dev/null
@@ -0,0 +1,1818 @@
+/*
+ *             Z-star vc0321 library
+ *             Copyright (C) 2006 Koninski Artur takeshi87@o2.pl
+ *             Copyright (C) 2006 Michel Xhaard
+ *
+ * V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
+ *
+ * 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
+ * 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
+ */
+
+#define MODULE_NAME "vc032x"
+
+#include "gspca.h"
+
+#define DRIVER_VERSION_NUMBER  KERNEL_VERSION(2, 1, 7)
+static const char version[] = "2.1.7";
+
+MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
+MODULE_DESCRIPTION("GSPCA/VC032X USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* specific webcam descriptor */
+struct sd {
+       struct gspca_dev gspca_dev;     /* !! must be the first item */
+
+       unsigned char autogain;
+       unsigned char lightfreq;
+
+       char qindex;
+       char bridge;
+#define BRIDGE_VC0321 0
+#define BRIDGE_VC0323 1
+       char sensor;
+#define SENSOR_HV7131R 0
+#define SENSOR_MI1320 1
+#define SENSOR_MI1310_SOC 2
+#define SENSOR_OV7660 3
+#define SENSOR_OV7670 4
+#define SENSOR_PO3130NC 5
+};
+
+/* V4L2 controls supported by the driver */
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val);
+
+static struct ctrl sd_ctrls[] = {
+       {
+           {
+               .id      = V4L2_CID_AUTOGAIN,
+               .type    = V4L2_CTRL_TYPE_BOOLEAN,
+               .name    = "Auto Gain",
+               .minimum = 0,
+               .maximum = 1,
+               .step    = 1,
+#define AUTOGAIN_DEF 1
+               .default_value = AUTOGAIN_DEF,
+           },
+           .set = sd_setautogain,
+           .get = sd_getautogain,
+       },
+       {
+           {
+               .id      = V4L2_CID_POWER_LINE_FREQUENCY,
+               .type    = V4L2_CTRL_TYPE_MENU,
+               .name    = "Light frequency filter",
+               .minimum = 0,
+               .maximum = 2,   /* 0: No, 1: 50Hz, 2:60Hz */
+               .step    = 1,
+#define FREQ_DEF 1
+               .default_value = FREQ_DEF,
+               .default_value = 1,
+           },
+           .set = sd_setfreq,
+           .get = sd_getfreq,
+       },
+};
+
+static struct v4l2_pix_format vc0321_mode[] = {
+       {320, 240, V4L2_PIX_FMT_YUV420, V4L2_FIELD_NONE,
+               .bytesperline = 320 * 2,
+               .sizeimage = 320 * 240 * 2,
+               .colorspace = V4L2_COLORSPACE_SRGB,
+               .priv = 1},
+       {640, 480, V4L2_PIX_FMT_YUV420, V4L2_FIELD_NONE,
+               .bytesperline = 640 * 2,
+               .sizeimage = 640 * 480 * 2,
+               .colorspace = V4L2_COLORSPACE_SRGB,
+               .priv = 0},
+};
+static struct v4l2_pix_format vc0323_mode[] = {
+       {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+               .bytesperline = 320,
+               .sizeimage = 320 * 240 * 3 / 8 + 590,
+               .colorspace = V4L2_COLORSPACE_JPEG,
+               .priv = 1},
+       {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+               .bytesperline = 640,
+               .sizeimage = 640 * 480 * 3 / 8 + 590,
+               .colorspace = V4L2_COLORSPACE_JPEG,
+               .priv = 0},
+};
+
+static const __u8 mi1310_socinitVGA_JPG[][4] = {
+       {0xb0, 0x03, 0x19, 0xcc},
+       {0xb0, 0x04, 0x02, 0xcc},
+       {0xb3, 0x00, 0x64, 0xcc},
+       {0xb3, 0x00, 0x65, 0xcc},
+       {0xb3, 0x05, 0x00, 0xcc},
+       {0xb3, 0x06, 0x00, 0xcc},
+       {0xb3, 0x08, 0x01, 0xcc},
+       {0xb3, 0x09, 0x0c, 0xcc},
+       {0xb3, 0x34, 0x02, 0xcc},
+       {0xb3, 0x35, 0xdd, 0xcc},
+       {0xb3, 0x02, 0x00, 0xcc},
+       {0xb3, 0x03, 0x0a, 0xcc},
+       {0xb3, 0x04, 0x05, 0xcc},
+       {0xb3, 0x20, 0x00, 0xcc},
+       {0xb3, 0x21, 0x00, 0xcc},
+       {0xb3, 0x22, 0x03, 0xcc},
+       {0xb3, 0x23, 0xc0, 0xcc},
+       {0xb3, 0x14, 0x00, 0xcc},
+       {0xb3, 0x15, 0x00, 0xcc},
+       {0xb3, 0x16, 0x04, 0xcc},
+       {0xb3, 0x17, 0xff, 0xcc},
+       {0xb3, 0x00, 0x65, 0xcc},
+       {0xb8, 0x00, 0x00, 0xcc},
+       {0xbc, 0x00, 0xd0, 0xcc},
+       {0xbc, 0x01, 0x01, 0xcc},
+       {0xf0, 0x00, 0x02, 0xbb},
+       {0xc8, 0x9f, 0x0b, 0xbb},
+       {0x5b, 0x00, 0x01, 0xbb},
+       {0x2f, 0xde, 0x20, 0xbb},
+       {0xf0, 0x00, 0x00, 0xbb},
+       {0x20, 0x03, 0x02, 0xbb},
+       {0xf0, 0x00, 0x01, 0xbb},
+       {0x05, 0x00, 0x07, 0xbb},
+       {0x34, 0x00, 0x00, 0xbb},
+       {0x35, 0xff, 0x00, 0xbb},
+       {0xdc, 0x07, 0x02, 0xbb},
+       {0xdd, 0x3c, 0x18, 0xbb},
+       {0xde, 0x92, 0x6d, 0xbb},
+       {0xdf, 0xcd, 0xb1, 0xbb},
+       {0xe0, 0xff, 0xe7, 0xbb},
+       {0x06, 0xf0, 0x0d, 0xbb},
+       {0x06, 0x70, 0x0e, 0xbb},
+       {0x4c, 0x00, 0x01, 0xbb},
+       {0x4d, 0x00, 0x01, 0xbb},
+       {0xf0, 0x00, 0x02, 0xbb},
+       {0x2e, 0x0c, 0x55, 0xbb},
+       {0x21, 0xb6, 0x6e, 0xbb},
+       {0x36, 0x30, 0x10, 0xbb},
+       {0x37, 0x00, 0xc1, 0xbb},
+       {0xf0, 0x00, 0x00, 0xbb},
+       {0x07, 0x00, 0x84, 0xbb},
+       {0x08, 0x02, 0x4a, 0xbb},
+       {0x05, 0x01, 0x10, 0xbb},
+       {0x06, 0x00, 0x39, 0xbb},
+       {0xf0, 0x00, 0x02, 0xbb},
+       {0x58, 0x02, 0x67, 0xbb},
+       {0x57, 0x02, 0x00, 0xbb},
+       {0x5a, 0x02, 0x67, 0xbb},
+       {0x59, 0x02, 0x00, 0xbb},
+       {0x5c, 0x12, 0x0d, 0xbb},
+       {0x5d, 0x16, 0x11, 0xbb},
+       {0x39, 0x06, 0x18, 0xbb},
+       {0x3a, 0x06, 0x18, 0xbb},
+       {0x3b, 0x06, 0x18, 0xbb},
+       {0x3c, 0x06, 0x18, 0xbb},
+       {0x64, 0x7b, 0x5b, 0xbb},
+       {0xf0, 0x00, 0x02, 0xbb},
+       {0x36, 0x30, 0x10, 0xbb},
+       {0x37, 0x00, 0xc0, 0xbb},
+       {0xbc, 0x0e, 0x00, 0xcc},
+       {0xbc, 0x0f, 0x05, 0xcc},
+       {0xbc, 0x10, 0xc0, 0xcc},
+       {0xbc, 0x11, 0x03, 0xcc},
+       {0xb6, 0x00, 0x00, 0xcc},
+       {0xb6, 0x03, 0x02, 0xcc},
+       {0xb6, 0x02, 0x80, 0xcc},
+       {0xb6, 0x05, 0x01, 0xcc},
+       {0xb6, 0x04, 0xe0, 0xcc},
+       {0xb6, 0x12, 0xf8, 0xcc},
+       {0xb6, 0x13, 0x25, 0xcc},
+       {0xb6, 0x18, 0x02, 0xcc},
+       {0xb6, 0x17, 0x58, 0xcc},
+       {0xb6, 0x16, 0x00, 0xcc},
+       {0xb6, 0x22, 0x12, 0xcc},
+       {0xb6, 0x23, 0x0b, 0xcc},
+       {0xbf, 0xc0, 0x39, 0xcc},
+       {0xbf, 0xc1, 0x04, 0xcc},
+       {0xbf, 0xcc, 0x00, 0xcc},
+       {0xbc, 0x02, 0x18, 0xcc},
+       {0xbc, 0x03, 0x50, 0xcc},
+       {0xbc, 0x04, 0x18, 0xcc},
+       {0xbc, 0x05, 0x00, 0xcc},
+       {0xbc, 0x06, 0x00, 0xcc},
+       {0xbc, 0x08, 0x30, 0xcc},
+       {0xbc, 0x09, 0x40, 0xcc},
+       {0xbc, 0x0a, 0x10, 0xcc},
+       {0xbc, 0x0b, 0x00, 0xcc},
+       {0xbc, 0x0c, 0x00, 0xcc},
+       {0xb3, 0x5c, 0x01, 0xcc},
+       {0xf0, 0x00, 0x01, 0xbb},
+       {0x80, 0x00, 0x03, 0xbb},
+       {0x81, 0xc7, 0x14, 0xbb},
+       {0x82, 0xeb, 0xe8, 0xbb},
+       {0x83, 0xfe, 0xf4, 0xbb},
+       {0x84, 0xcd, 0x10, 0xbb},
+       {0x85, 0xf3, 0xee, 0xbb},
+       {0x86, 0xff, 0xf1, 0xbb},
+       {0x87, 0xcd, 0x10, 0xbb},
+       {0x88, 0xf3, 0xee, 0xbb},
+       {0x89, 0x01, 0xf1, 0xbb},
+       {0x8a, 0xe5, 0x17, 0xbb},
+       {0x8b, 0xe8, 0xe2, 0xbb},
+       {0x8c, 0xf7, 0xed, 0xbb},
+       {0x8d, 0x00, 0xff, 0xbb},
+       {0x8e, 0xec, 0x10, 0xbb},
+       {0x8f, 0xf0, 0xed, 0xbb},
+       {0x90, 0xf9, 0xf2, 0xbb},
+       {0x91, 0x00, 0x00, 0xbb},
+       {0x92, 0xe9, 0x0d, 0xbb},
+       {0x93, 0xf4, 0xf2, 0xbb},
+       {0x94, 0xfb, 0xf5, 0xbb},
+       {0x95, 0x00, 0xff, 0xbb},
+       {0xb6, 0x0f, 0x08, 0xbb},
+       {0xb7, 0x3d, 0x16, 0xbb},
+       {0xb8, 0x0c, 0x04, 0xbb},
+       {0xb9, 0x1c, 0x07, 0xbb},
+       {0xba, 0x0a, 0x03, 0xbb},
+       {0xbb, 0x1b, 0x09, 0xbb},
+       {0xbc, 0x17, 0x0d, 0xbb},
+       {0xbd, 0x23, 0x1d, 0xbb},
+       {0xbe, 0x00, 0x28, 0xbb},
+       {0xbf, 0x11, 0x09, 0xbb},
+       {0xc0, 0x16, 0x15, 0xbb},
+       {0xc1, 0x00, 0x1b, 0xbb},
+       {0xc2, 0x0e, 0x07, 0xbb},
+       {0xc3, 0x14, 0x10, 0xbb},
+       {0xc4, 0x00, 0x17, 0xbb},
+       {0x06, 0x74, 0x8e, 0xbb},
+       {0xf0, 0x00, 0x01, 0xbb},
+       {0x06, 0xf4, 0x8e, 0xbb},
+       {0x00, 0x00, 0x50, 0xdd},
+       {0x06, 0x74, 0x8e, 0xbb},
+       {0xf0, 0x00, 0x02, 0xbb},
+       {0x24, 0x50, 0x20, 0xbb},
+       {0xf0, 0x00, 0x02, 0xbb},
+       {0x34, 0x0c, 0x50, 0xbb},
+       {0xb3, 0x01, 0x41, 0xcc},
+       {0xf0, 0x00, 0x00, 0xbb},
+       {0x03, 0x03, 0xc0, 0xbb},
+       {},
+};
+static const __u8 mi1310_socinitQVGA_JPG[][4] = {
+       {0xb0, 0x03, 0x19, 0xcc},       {0xb0, 0x04, 0x02, 0xcc},
+       {0xb3, 0x00, 0x64, 0xcc},       {0xb3, 0x00, 0x65, 0xcc},
+       {0xb3, 0x05, 0x00, 0xcc},       {0xb3, 0x06, 0x00, 0xcc},
+       {0xb3, 0x08, 0x01, 0xcc},       {0xb3, 0x09, 0x0c, 0xcc},
+       {0xb3, 0x34, 0x02, 0xcc},       {0xb3, 0x35, 0xdd, 0xcc},
+       {0xb3, 0x02, 0x00, 0xcc},       {0xb3, 0x03, 0x0a, 0xcc},
+       {0xb3, 0x04, 0x05, 0xcc},       {0xb3, 0x20, 0x00, 0xcc},
+       {0xb3, 0x21, 0x00, 0xcc},       {0xb3, 0x22, 0x03, 0xcc},
+       {0xb3, 0x23, 0xc0, 0xcc},       {0xb3, 0x14, 0x00, 0xcc},
+       {0xb3, 0x15, 0x00, 0xcc},       {0xb3, 0x16, 0x04, 0xcc},
+       {0xb3, 0x17, 0xff, 0xcc},       {0xb3, 0x00, 0x65, 0xcc},
+       {0xb8, 0x00, 0x00, 0xcc},       {0xbc, 0x00, 0xf0, 0xcc},
+       {0xbc, 0x01, 0x01, 0xcc},       {0xf0, 0x00, 0x02, 0xbb},
+       {0xc8, 0x9f, 0x0b, 0xbb},       {0x5b, 0x00, 0x01, 0xbb},
+       {0x2f, 0xde, 0x20, 0xbb},       {0xf0, 0x00, 0x00, 0xbb},
+       {0x20, 0x03, 0x02, 0xbb},       {0xf0, 0x00, 0x01, 0xbb},
+       {0x05, 0x00, 0x07, 0xbb},       {0x34, 0x00, 0x00, 0xbb},
+       {0x35, 0xff, 0x00, 0xbb},       {0xdc, 0x07, 0x02, 0xbb},
+       {0xdd, 0x3c, 0x18, 0xbb},       {0xde, 0x92, 0x6d, 0xbb},
+       {0xdf, 0xcd, 0xb1, 0xbb},       {0xe0, 0xff, 0xe7, 0xbb},
+       {0x06, 0xf0, 0x0d, 0xbb},       {0x06, 0x70, 0x0e, 0xbb},
+       {0x4c, 0x00, 0x01, 0xbb},       {0x4d, 0x00, 0x01, 0xbb},
+       {0xf0, 0x00, 0x02, 0xbb},       {0x2e, 0x0c, 0x55, 0xbb},
+       {0x21, 0xb6, 0x6e, 0xbb},       {0x36, 0x30, 0x10, 0xbb},
+       {0x37, 0x00, 0xc1, 0xbb},       {0xf0, 0x00, 0x00, 0xbb},
+       {0x07, 0x00, 0x84, 0xbb},       {0x08, 0x02, 0x4a, 0xbb},
+       {0x05, 0x01, 0x10, 0xbb},       {0x06, 0x00, 0x39, 0xbb},
+       {0xf0, 0x00, 0x02, 0xbb},       {0x58, 0x02, 0x67, 0xbb},
+       {0x57, 0x02, 0x00, 0xbb},       {0x5a, 0x02, 0x67, 0xbb},
+       {0x59, 0x02, 0x00, 0xbb},       {0x5c, 0x12, 0x0d, 0xbb},
+       {0x5d, 0x16, 0x11, 0xbb},       {0x39, 0x06, 0x18, 0xbb},
+       {0x3a, 0x06, 0x18, 0xbb},       {0x3b, 0x06, 0x18, 0xbb},
+       {0x3c, 0x06, 0x18, 0xbb},       {0x64, 0x7b, 0x5b, 0xbb},
+       {0xf0, 0x00, 0x02, 0xbb},       {0x36, 0x30, 0x10, 0xbb},
+       {0x37, 0x00, 0xc0, 0xbb},       {0xbc, 0x0e, 0x00, 0xcc},
+       {0xbc, 0x0f, 0x05, 0xcc},       {0xbc, 0x10, 0xc0, 0xcc},
+       {0xbc, 0x11, 0x03, 0xcc},       {0xb6, 0x00, 0x00, 0xcc},
+       {0xb6, 0x03, 0x01, 0xcc},       {0xb6, 0x02, 0x40, 0xcc},
+       {0xb6, 0x05, 0x00, 0xcc},       {0xb6, 0x04, 0xf0, 0xcc},
+       {0xb6, 0x12, 0xf8, 0xcc},       {0xb6, 0x13, 0x25, 0xcc},
+       {0xb6, 0x18, 0x00, 0xcc},       {0xb6, 0x17, 0x96, 0xcc},
+       {0xb6, 0x16, 0x00, 0xcc},       {0xb6, 0x22, 0x12, 0xcc},
+       {0xb6, 0x23, 0x0b, 0xcc},       {0xbf, 0xc0, 0x39, 0xcc},
+       {0xbf, 0xc1, 0x04, 0xcc},       {0xbf, 0xcc, 0x00, 0xcc},
+       {0xb3, 0x5c, 0x01, 0xcc},       {0xf0, 0x00, 0x01, 0xbb},
+       {0x80, 0x00, 0x03, 0xbb},       {0x81, 0xc7, 0x14, 0xbb},
+       {0x82, 0xeb, 0xe8, 0xbb},       {0x83, 0xfe, 0xf4, 0xbb},
+       {0x84, 0xcd, 0x10, 0xbb},       {0x85, 0xf3, 0xee, 0xbb},
+       {0x86, 0xff, 0xf1, 0xbb},       {0x87, 0xcd, 0x10, 0xbb},
+       {0x88, 0xf3, 0xee, 0xbb},       {0x89, 0x01, 0xf1, 0xbb},
+       {0x8a, 0xe5, 0x17, 0xbb},       {0x8b, 0xe8, 0xe2, 0xbb},
+       {0x8c, 0xf7, 0xed, 0xbb},       {0x8d, 0x00, 0xff, 0xbb},
+       {0x8e, 0xec, 0x10, 0xbb},       {0x8f, 0xf0, 0xed, 0xbb},
+       {0x90, 0xf9, 0xf2, 0xbb},       {0x91, 0x00, 0x00, 0xbb},
+       {0x92, 0xe9, 0x0d, 0xbb},       {0x93, 0xf4, 0xf2, 0xbb},
+       {0x94, 0xfb, 0xf5, 0xbb},       {0x95, 0x00, 0xff, 0xbb},
+       {0xb6, 0x0f, 0x08, 0xbb},       {0xb7, 0x3d, 0x16, 0xbb},
+       {0xb8, 0x0c, 0x04, 0xbb},       {0xb9, 0x1c, 0x07, 0xbb},
+       {0xba, 0x0a, 0x03, 0xbb},       {0xbb, 0x1b, 0x09, 0xbb},
+       {0xbc, 0x17, 0x0d, 0xbb},       {0xbd, 0x23, 0x1d, 0xbb},
+       {0xbe, 0x00, 0x28, 0xbb},       {0xbf, 0x11, 0x09, 0xbb},
+       {0xc0, 0x16, 0x15, 0xbb},       {0xc1, 0x00, 0x1b, 0xbb},
+       {0xc2, 0x0e, 0x07, 0xbb},       {0xc3, 0x14, 0x10, 0xbb},
+       {0xc4, 0x00, 0x17, 0xbb},       {0x06, 0x74, 0x8e, 0xbb},
+       {0xf0, 0x00, 0x01, 0xbb},       {0x06, 0xf4, 0x8e, 0xbb},
+       {0x00, 0x00, 0x50, 0xdd},       {0x06, 0x74, 0x8e, 0xbb},
+       {0xf0, 0x00, 0x02, 0xbb},       {0x24, 0x50, 0x20, 0xbb},
+       {0xf0, 0x00, 0x02, 0xbb},       {0x34, 0x0c, 0x50, 0xbb},
+       {0xb3, 0x01, 0x41, 0xcc},       {0xf0, 0x00, 0x00, 0xbb},
+       {0x03, 0x03, 0xc0, 0xbb},
+       {},
+};
+
+static const __u8 mi1320_gamma[17] = {
+       0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8,
+       0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff
+};
+static const __u8 mi1320_matrix[9] = {
+       0x54, 0xda, 0x06, 0xf1, 0x50, 0xf4, 0xf7, 0xea, 0x52
+};
+static const __u8 mi1320_initVGA_data[][4] = {
+       {0xb3, 0x01, 0x01, 0xcc},       {0x00, 0x00, 0x33, 0xdd},
+       {0xb0, 0x03, 0x19, 0xcc},       {0x00, 0x00, 0x33, 0xdd},
+       {0xb0, 0x04, 0x02, 0xcc},       {0x00, 0x00, 0x33, 0xdd},
+       {0xb3, 0x00, 0x64, 0xcc},       {0xb3, 0x00, 0x65, 0xcc},
+       {0xb0, 0x16, 0x03, 0xcc},       {0xb3, 0x05, 0x00, 0xcc},
+       {0xb3, 0x06, 0x00, 0xcc},       {0xb3, 0x08, 0x01, 0xcc},
+       {0xb3, 0x09, 0x0c, 0xcc},       {0xb3, 0x34, 0x02, 0xcc},
+       {0xb3, 0x35, 0xc8, 0xcc},       {0xb3, 0x02, 0x00, 0xcc},
+       {0xb3, 0x03, 0x0a, 0xcc},       {0xb3, 0x04, 0x05, 0xcc},
+       {0xb3, 0x20, 0x00, 0xcc},       {0xb3, 0x21, 0x00, 0xcc},
+       {0xb3, 0x22, 0x03, 0xcc},       {0xb3, 0x23, 0xc0, 0xcc},
+       {0xb3, 0x14, 0x00, 0xcc},       {0xb3, 0x15, 0x00, 0xcc},
+       {0xb3, 0x16, 0x04, 0xcc},       {0xb3, 0x17, 0xff, 0xcc},
+       {0xb3, 0x00, 0x67, 0xcc},       {0xbc, 0x00, 0xd0, 0xcc},
+       {0xbc, 0x01, 0x01, 0xcc},       {0xf0, 0x00, 0x00, 0xbb},
+       {0x0d, 0x00, 0x09, 0xbb},       {0x00, 0x01, 0x00, 0xdd},
+       {0x0d, 0x00, 0x08, 0xbb},       {0xf0, 0x00, 0x01, 0xbb},
+       {0xa1, 0x05, 0x00, 0xbb},       {0xa4, 0x03, 0xc0, 0xbb},
+       {0xf0, 0x00, 0x02, 0xbb},       {0x00, 0x00, 0x10, 0xdd},
+       {0xc8, 0x9f, 0x0b, 0xbb},       {0x00, 0x00, 0x10, 0xdd},
+       {0xf0, 0x00, 0x00, 0xbb},       {0x00, 0x00, 0x10, 0xdd},
+       {0x20, 0x01, 0x00, 0xbb},       {0x00, 0x00, 0x10, 0xdd},
+       {0xf0, 0x00, 0x01, 0xbb},       {0x9d, 0x3c, 0xa0, 0xbb},
+       {0x47, 0x30, 0x30, 0xbb},       {0xf0, 0x00, 0x00, 0xbb},
+       {0x0a, 0x80, 0x11, 0xbb},       {0x35, 0x00, 0x22, 0xbb},
+       {0xf0, 0x00, 0x02, 0xbb},       {0x9d, 0xc5, 0x05, 0xbb},
+       {0xdc, 0x0f, 0xfc, 0xbb},       {0xf0, 0x00, 0x01, 0xbb},
+       {0x06, 0x74, 0x0e, 0xbb},       {0x80, 0x00, 0x06, 0xbb},
+       {0x81, 0x04, 0x00, 0xbb},       {0x82, 0x01, 0x02, 0xbb},
+       {0x83, 0x03, 0x02, 0xbb},       {0x84, 0x05, 0x00, 0xbb},
+       {0x85, 0x01, 0x00, 0xbb},       {0x86, 0x03, 0x02, 0xbb},
+       {0x87, 0x05, 0x00, 0xbb},       {0x88, 0x01, 0x00, 0xbb},
+       {0x89, 0x02, 0x02, 0xbb},       {0x8a, 0xfd, 0x04, 0xbb},
+       {0x8b, 0xfc, 0xfd, 0xbb},       {0x8c, 0xff, 0xfd, 0xbb},
+       {0x8d, 0x00, 0x00, 0xbb},       {0x8e, 0xfe, 0x05, 0xbb},
+       {0x8f, 0xfc, 0xfd, 0xbb},       {0x90, 0xfe, 0xfd, 0xbb},
+       {0x91, 0x00, 0x00, 0xbb},       {0x92, 0xfe, 0x03, 0xbb},
+       {0x93, 0xfd, 0xfe, 0xbb},       {0x94, 0xff, 0xfd, 0xbb},
+       {0x95, 0x00, 0x00, 0xbb},       {0xb6, 0x07, 0x05, 0xbb},
+       {0xb7, 0x13, 0x06, 0xbb},       {0xb8, 0x08, 0x06, 0xbb},
+       {0xb9, 0x14, 0x08, 0xbb},       {0xba, 0x06, 0x05, 0xbb},
+       {0xbb, 0x13, 0x06, 0xbb},       {0xbc, 0x03, 0x01, 0xbb},
+       {0xbd, 0x03, 0x04, 0xbb},       {0xbe, 0x00, 0x02, 0xbb},
+       {0xbf, 0x03, 0x01, 0xbb},       {0xc0, 0x02, 0x04, 0xbb},
+       {0xc1, 0x00, 0x04, 0xbb},       {0xc2, 0x02, 0x01, 0xbb},
+       {0xc3, 0x01, 0x03, 0xbb},       {0xc4, 0x00, 0x04, 0xbb},
+       {0xf0, 0x00, 0x00, 0xbb},       {0x05, 0x01, 0x13, 0xbb},
+       {0x06, 0x00, 0x11, 0xbb},       {0x07, 0x00, 0x85, 0xbb},
+       {0x08, 0x00, 0x27, 0xbb},       {0x20, 0x01, 0x03, 0xbb},
+       {0x21, 0x80, 0x00, 0xbb},       {0x22, 0x0d, 0x0f, 0xbb},
+       {0x24, 0x80, 0x00, 0xbb},       {0x59, 0x00, 0xff, 0xbb},
+       {0xf0, 0x00, 0x02, 0xbb},       {0x39, 0x03, 0x0d, 0xbb},
+       {0x3a, 0x06, 0x1b, 0xbb},       {0x3b, 0x00, 0x95, 0xbb},
+       {0x3c, 0x04, 0xdb, 0xbb},       {0x57, 0x02, 0x00, 0xbb},
+       {0x58, 0x02, 0x66, 0xbb},       {0x59, 0x00, 0xff, 0xbb},
+       {0x5a, 0x01, 0x33, 0xbb},       {0x5c, 0x12, 0x0d, 0xbb},
+       {0x5d, 0x16, 0x11, 0xbb},       {0x64, 0x5e, 0x1c, 0xbb},
+       {0xf0, 0x00, 0x02, 0xbb},       {0x2f, 0xd1, 0x00, 0xbb},
+       {0x5b, 0x00, 0x01, 0xbb},       {0xf0, 0x00, 0x02, 0xbb},
+       {0x36, 0x68, 0x10, 0xbb},       {0x00, 0x00, 0x30, 0xdd},
+       {0x37, 0x82, 0x00, 0xbb},       {0xbc, 0x0e, 0x00, 0xcc},
+       {0xbc, 0x0f, 0x05, 0xcc},       {0xbc, 0x10, 0xc0, 0xcc},
+       {0xbc, 0x11, 0x03, 0xcc},       {0xb6, 0x00, 0x00, 0xcc},
+       {0xb6, 0x03, 0x05, 0xcc},       {0xb6, 0x02, 0x00, 0xcc},
+       {0xb6, 0x05, 0x04, 0xcc},       {0xb6, 0x04, 0x00, 0xcc},
+       {0xb6, 0x12, 0xf8, 0xcc},       {0xb6, 0x13, 0x29, 0xcc},
+       {0xb6, 0x18, 0x0a, 0xcc},       {0xb6, 0x17, 0x00, 0xcc},
+       {0xb6, 0x16, 0x00, 0xcc},       {0xb6, 0x22, 0x12, 0xcc},
+       {0xb6, 0x23, 0x0b, 0xcc},       {0xbf, 0xc0, 0x26, 0xcc},
+       {0xbf, 0xc1, 0x02, 0xcc},       {0xbf, 0xcc, 0x04, 0xcc},
+       {0xbc, 0x02, 0x18, 0xcc},       {0xbc, 0x03, 0x50, 0xcc},
+       {0xbc, 0x04, 0x18, 0xcc},       {0xbc, 0x05, 0x00, 0xcc},
+       {0xbc, 0x06, 0x00, 0xcc},       {0xbc, 0x08, 0x30, 0xcc},
+       {0xbc, 0x09, 0x40, 0xcc},       {0xbc, 0x0a, 0x10, 0xcc},
+       {0xbc, 0x0b, 0x00, 0xcc},       {0xbc, 0x0c, 0x00, 0xcc},
+       {0xb3, 0x5c, 0x01, 0xcc},       {0xb3, 0x01, 0x41, 0xcc},
+       {}
+};
+static const __u8 mi1320_initQVGA_data[][4] = {
+       {0xb3, 0x01, 0x01, 0xcc},       {0x00, 0x00, 0x33, 0xdd},
+       {0xb0, 0x03, 0x19, 0xcc},       {0x00, 0x00, 0x33, 0xdd},
+       {0xb0, 0x04, 0x02, 0xcc},       {0x00, 0x00, 0x33, 0xdd},
+       {0xb3, 0x00, 0x64, 0xcc},       {0xb3, 0x00, 0x65, 0xcc},
+       {0xb0, 0x16, 0x03, 0xcc},       {0xb3, 0x05, 0x01, 0xcc},
+       {0xb3, 0x06, 0x01, 0xcc},       {0xb3, 0x08, 0x01, 0xcc},
+       {0xb3, 0x09, 0x0c, 0xcc},       {0xb3, 0x34, 0x02, 0xcc},
+       {0xb3, 0x35, 0xc8, 0xcc},       {0xb3, 0x02, 0x00, 0xcc},
+       {0xb3, 0x03, 0x0a, 0xcc},       {0xb3, 0x04, 0x05, 0xcc},
+       {0xb3, 0x20, 0x00, 0xcc},       {0xb3, 0x21, 0x00, 0xcc},
+       {0xb3, 0x22, 0x01, 0xcc},       {0xb3, 0x23, 0xe0, 0xcc},
+       {0xb3, 0x14, 0x00, 0xcc},       {0xb3, 0x15, 0x00, 0xcc},
+       {0xb3, 0x16, 0x02, 0xcc},       {0xb3, 0x17, 0x7f, 0xcc},
+       {0xb3, 0x00, 0x65, 0xcc},       {0xb8, 0x00, 0x00, 0xcc},
+       {0xbc, 0x00, 0xd0, 0xcc},       {0xbc, 0x01, 0x01, 0xcc},
+       {0xf0, 0x00, 0x00, 0xbb},       {0x0d, 0x00, 0x09, 0xbb},
+       {0x00, 0x01, 0x00, 0xdd},       {0x0d, 0x00, 0x08, 0xbb},
+       {0xf0, 0x00, 0x00, 0xbb},       {0x02, 0x00, 0x64, 0xbb},
+       {0x05, 0x01, 0x78, 0xbb},       {0x06, 0x00, 0x11, 0xbb},
+       {0x07, 0x01, 0x42, 0xbb},       {0x08, 0x00, 0x11, 0xbb},
+       {0x20, 0x01, 0x00, 0xbb},       {0x21, 0x80, 0x00, 0xbb},
+       {0x22, 0x0d, 0x0f, 0xbb},       {0x24, 0x80, 0x00, 0xbb},
+       {0x59, 0x00, 0xff, 0xbb},       {0xf0, 0x00, 0x01, 0xbb},
+       {0x9d, 0x3c, 0xa0, 0xbb},       {0x47, 0x30, 0x30, 0xbb},
+       {0xf0, 0x00, 0x00, 0xbb},       {0x0a, 0x80, 0x11, 0xbb},
+       {0x35, 0x00, 0x22, 0xbb},       {0xf0, 0x00, 0x02, 0xbb},
+       {0x9d, 0xc5, 0x05, 0xbb},       {0xdc, 0x0f, 0xfc, 0xbb},
+       {0xf0, 0x00, 0x01, 0xbb},       {0x06, 0x74, 0x0e, 0xbb},
+       {0x80, 0x00, 0x06, 0xbb},       {0x81, 0x04, 0x00, 0xbb},
+       {0x82, 0x01, 0x02, 0xbb},       {0x83, 0x03, 0x02, 0xbb},
+       {0x84, 0x05, 0x00, 0xbb},       {0x85, 0x01, 0x00, 0xbb},
+       {0x86, 0x03, 0x02, 0xbb},       {0x87, 0x05, 0x00, 0xbb},
+       {0x88, 0x01, 0x00, 0xbb},       {0x89, 0x02, 0x02, 0xbb},
+       {0x8a, 0xfd, 0x04, 0xbb},       {0x8b, 0xfc, 0xfd, 0xbb},
+       {0x8c, 0xff, 0xfd, 0xbb},       {0x8d, 0x00, 0x00, 0xbb},
+       {0x8e, 0xfe, 0x05, 0xbb},       {0x8f, 0xfc, 0xfd, 0xbb},
+       {0x90, 0xfe, 0xfd, 0xbb},       {0x91, 0x00, 0x00, 0xbb},
+       {0x92, 0xfe, 0x03, 0xbb},       {0x93, 0xfd, 0xfe, 0xbb},
+       {0x94, 0xff, 0xfd, 0xbb},       {0x95, 0x00, 0x00, 0xbb},
+       {0xb6, 0x07, 0x05, 0xbb},       {0xb7, 0x13, 0x06, 0xbb},
+       {0xb8, 0x08, 0x06, 0xbb},       {0xb9, 0x14, 0x08, 0xbb},
+       {0xba, 0x06, 0x05, 0xbb},       {0xbb, 0x13, 0x06, 0xbb},
+       {0xbc, 0x03, 0x01, 0xbb},       {0xbd, 0x03, 0x04, 0xbb},
+       {0xbe, 0x00, 0x02, 0xbb},       {0xbf, 0x03, 0x01, 0xbb},
+       {0xc0, 0x02, 0x04, 0xbb},       {0xc1, 0x00, 0x04, 0xbb},
+       {0xc2, 0x02, 0x01, 0xbb},       {0xc3, 0x01, 0x03, 0xbb},
+       {0xc4, 0x00, 0x04, 0xbb},       {0xf0, 0x00, 0x02, 0xbb},
+       {0xc8, 0x00, 0x00, 0xbb},       {0x2e, 0x00, 0x00, 0xbb},
+       {0x2e, 0x0c, 0x5b, 0xbb},       {0x2f, 0xd1, 0x00, 0xbb},
+       {0x39, 0x03, 0xca, 0xbb},       {0x3a, 0x06, 0x80, 0xbb},
+       {0x3b, 0x01, 0x52, 0xbb},       {0x3c, 0x05, 0x40, 0xbb},
+       {0x57, 0x01, 0x9c, 0xbb},       {0x58, 0x01, 0xee, 0xbb},
+       {0x59, 0x00, 0xf0, 0xbb},       {0x5a, 0x01, 0x20, 0xbb},
+       {0x5c, 0x1d, 0x17, 0xbb},       {0x5d, 0x22, 0x1c, 0xbb},
+       {0x64, 0x1e, 0x1c, 0xbb},       {0x5b, 0x00, 0x01, 0xbb},
+       {0xf0, 0x00, 0x02, 0xbb},       {0x36, 0x68, 0x10, 0xbb},
+       {0x00, 0x00, 0x30, 0xdd},       {0x37, 0x81, 0x00, 0xbb},
+       {0xbc, 0x02, 0x18, 0xcc},       {0xbc, 0x03, 0x50, 0xcc},
+       {0xbc, 0x04, 0x18, 0xcc},       {0xbc, 0x05, 0x00, 0xcc},
+       {0xbc, 0x06, 0x00, 0xcc},       {0xbc, 0x08, 0x30, 0xcc},
+       {0xbc, 0x09, 0x40, 0xcc},       {0xbc, 0x0a, 0x10, 0xcc},
+       {0xbc, 0x0b, 0x00, 0xcc},       {0xbc, 0x0c, 0x00, 0xcc},
+       {0xbf, 0xc0, 0x26, 0xcc},       {0xbf, 0xc1, 0x02, 0xcc},
+       {0xbf, 0xcc, 0x04, 0xcc},       {0xb3, 0x5c, 0x01, 0xcc},
+       {0xb3, 0x01, 0x41, 0xcc},
+       {}
+};
+
+static const __u8 po3130_gamma[17] = {
+       0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8,
+       0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff
+};
+static const __u8 po3130_matrix[9] = {
+       0x5f, 0xec, 0xf5, 0xf1, 0x5a, 0xf5, 0xf1, 0xec, 0x63
+};
+
+static const __u8 po3130_initVGA_data[][4] = {
+       {0xb0, 0x4d, 0x00, 0xcc},       {0xb3, 0x01, 0x01, 0xcc},
+       {0x00, 0x00, 0x50, 0xdd},       {0xb0, 0x03, 0x01, 0xcc},
+       {0xb3, 0x00, 0x04, 0xcc},       {0xb3, 0x00, 0x24, 0xcc},
+       {0xb3, 0x00, 0x25, 0xcc},       {0xb3, 0x08, 0x01, 0xcc},
+       {0xb3, 0x09, 0x0c, 0xcc},       {0xb3, 0x05, 0x00, 0xcc},
+       {0xb3, 0x06, 0x01, 0xcc},       {0xb3, 0x03, 0x1a, 0xcc},
+       {0xb3, 0x04, 0x15, 0xcc},       {0xb3, 0x20, 0x00, 0xcc},
+       {0xb3, 0x21, 0x00, 0xcc},       {0xb3, 0x22, 0x01, 0xcc},
+       {0xb3, 0x23, 0xe8, 0xcc},       {0xb8, 0x08, 0xe8, 0xcc},
+       {0xb3, 0x14, 0x00, 0xcc},       {0xb3, 0x15, 0x00, 0xcc},
+       {0xb3, 0x16, 0x02, 0xcc},       {0xb3, 0x17, 0x7f, 0xcc},
+       {0xb3, 0x34, 0x01, 0xcc},       {0xb3, 0x35, 0xf6, 0xcc},
+       {0xb3, 0x00, 0x27, 0xcc},       {0xbc, 0x00, 0x71, 0xcc},
+       {0xb8, 0x00, 0x21, 0xcc},       {0xb8, 0x27, 0x20, 0xcc},
+       {0xb8, 0x01, 0x79, 0xcc},       {0xb8, 0x81, 0x09, 0xcc},
+       {0xb8, 0x2c, 0x50, 0xcc},       {0xb8, 0x2d, 0xf8, 0xcc},
+       {0xb8, 0x2e, 0xf8, 0xcc},       {0xb8, 0x2f, 0xf8, 0xcc},
+       {0xb8, 0x30, 0x50, 0xcc},       {0xb8, 0x31, 0xf8, 0xcc},
+       {0xb8, 0x32, 0xf8, 0xcc},       {0xb8, 0x33, 0xf8, 0xcc},
+       {0xb8, 0x34, 0x50, 0xcc},       {0xb8, 0x35, 0x00, 0xcc},
+       {0xb8, 0x36, 0x00, 0xcc},       {0xb8, 0x37, 0x00, 0xcc},
+       {0x00, 0x1e, 0xc6, 0xaa},       {0x00, 0x20, 0x44, 0xaa},
+       {0x00, 0xad, 0x02, 0xaa},       {0x00, 0xae, 0x2c, 0xaa},
+       {0x00, 0x12, 0x08, 0xaa},       {0x00, 0x17, 0x41, 0xaa},
+       {0x00, 0x19, 0x41, 0xaa},       {0x00, 0x1e, 0x06, 0xaa},
+       {0x00, 0x21, 0x00, 0xaa},       {0x00, 0x36, 0xc0, 0xaa},
+       {0x00, 0x37, 0xc8, 0xaa},       {0x00, 0x3b, 0x36, 0xaa},
+       {0x00, 0x4b, 0xfe, 0xaa},       {0x00, 0x51, 0x1c, 0xaa},
+       {0x00, 0x52, 0x01, 0xaa},       {0x00, 0x55, 0x0a, 0xaa},
+       {0x00, 0x59, 0x02, 0xaa},       {0x00, 0x5a, 0x04, 0xaa},
+       {0x00, 0x5c, 0x10, 0xaa},       {0x00, 0x5d, 0x10, 0xaa},
+       {0x00, 0x5e, 0x10, 0xaa},       {0x00, 0x5f, 0x10, 0xaa},
+       {0x00, 0x61, 0x00, 0xaa},       {0x00, 0x62, 0x18, 0xaa},
+       {0x00, 0x63, 0x30, 0xaa},       {0x00, 0x70, 0x68, 0xaa},
+       {0x00, 0x80, 0x71, 0xaa},       {0x00, 0x81, 0x08, 0xaa},
+       {0x00, 0x82, 0x00, 0xaa},       {0x00, 0x83, 0x55, 0xaa},
+       {0x00, 0x84, 0x06, 0xaa},       {0x00, 0x85, 0x06, 0xaa},
+       {0x00, 0x86, 0x13, 0xaa},       {0x00, 0x87, 0x18, 0xaa},
+       {0x00, 0xaa, 0x3f, 0xaa},       {0x00, 0xab, 0x44, 0xaa},
+       {0x00, 0xb0, 0x68, 0xaa},       {0x00, 0xb5, 0x10, 0xaa},
+       {0x00, 0xb8, 0x20, 0xaa},       {0x00, 0xb9, 0xa0, 0xaa},
+       {0x00, 0xbc, 0x04, 0xaa},       {0x00, 0x8b, 0x40, 0xaa},
+       {0x00, 0x8c, 0x91, 0xaa},       {0x00, 0x8d, 0x8f, 0xaa},
+       {0x00, 0x8e, 0x91, 0xaa},       {0x00, 0x8f, 0x43, 0xaa},
+       {0x00, 0x90, 0x92, 0xaa},       {0x00, 0x91, 0x89, 0xaa},
+       {0x00, 0x92, 0x9d, 0xaa},       {0x00, 0x93, 0x46, 0xaa},
+       {0x00, 0xd6, 0x22, 0xaa},       {0x00, 0x73, 0x00, 0xaa},
+       {0x00, 0x74, 0x10, 0xaa},       {0x00, 0x75, 0x20, 0xaa},
+       {0x00, 0x76, 0x2b, 0xaa},       {0x00, 0x77, 0x36, 0xaa},
+       {0x00, 0x78, 0x49, 0xaa},       {0x00, 0x79, 0x5a, 0xaa},
+       {0x00, 0x7a, 0x7f, 0xaa},       {0x00, 0x7b, 0x9b, 0xaa},
+       {0x00, 0x7c, 0xba, 0xaa},       {0x00, 0x7d, 0xd4, 0xaa},
+       {0x00, 0x7e, 0xea, 0xaa},       {0x00, 0xd6, 0x62, 0xaa},
+       {0x00, 0x73, 0x00, 0xaa},       {0x00, 0x74, 0x10, 0xaa},
+       {0x00, 0x75, 0x20, 0xaa},       {0x00, 0x76, 0x2b, 0xaa},
+       {0x00, 0x77, 0x36, 0xaa},       {0x00, 0x78, 0x49, 0xaa},
+       {0x00, 0x79, 0x5a, 0xaa},       {0x00, 0x7a, 0x7f, 0xaa},
+       {0x00, 0x7b, 0x9b, 0xaa},       {0x00, 0x7c, 0xba, 0xaa},
+       {0x00, 0x7d, 0xd4, 0xaa},       {0x00, 0x7e, 0xea, 0xaa},
+       {0x00, 0xd6, 0xa2, 0xaa},       {0x00, 0x73, 0x00, 0xaa},
+       {0x00, 0x74, 0x10, 0xaa},       {0x00, 0x75, 0x20, 0xaa},
+       {0x00, 0x76, 0x2b, 0xaa},       {0x00, 0x77, 0x36, 0xaa},
+       {0x00, 0x78, 0x49, 0xaa},       {0x00, 0x79, 0x5a, 0xaa},
+       {0x00, 0x7a, 0x7f, 0xaa},       {0x00, 0x7b, 0x9b, 0xaa},
+       {0x00, 0x7c, 0xba, 0xaa},       {0x00, 0x7d, 0xd4, 0xaa},
+       {0x00, 0x7e, 0xea, 0xaa},
+       {0x00, 0x4c, 0x07, 0xaa},
+       {0x00, 0x4b, 0xe0, 0xaa},       {0x00, 0x4e, 0x77, 0xaa},
+       {0x00, 0x59, 0x02, 0xaa},       {0x00, 0x4d, 0x0a, 0xaa},
+/*     {0x00, 0xd1, 0x00, 0xaa},       {0x00, 0x20, 0xc4, 0xaa},
+       {0xb8, 0x8e, 0x00, 0xcc},       {0xb8, 0x8f, 0xff, 0xcc}, */
+       {0x00, 0xd1, 0x3c, 0xaa},       {0x00, 0x20, 0xc4, 0xaa},
+       {0xb8, 0x8e, 0x00, 0xcc},       {0xb8, 0x8f, 0xff, 0xcc},
+       {0xb8, 0xfe, 0x00, 0xcc},       {0xb8, 0xff, 0x28, 0xcc},
+       {0xb9, 0x00, 0x28, 0xcc},       {0xb9, 0x01, 0x28, 0xcc},
+       {0xb9, 0x02, 0x28, 0xcc},       {0xb9, 0x03, 0x00, 0xcc},
+       {0xb9, 0x04, 0x00, 0xcc},       {0xb9, 0x05, 0x3c, 0xcc},
+       {0xb9, 0x06, 0x3c, 0xcc},       {0xb9, 0x07, 0x3c, 0xcc},
+       {0xb9, 0x08, 0x3c, 0xcc},       {0x00, 0x05, 0x00, 0xaa},
+       {0xb3, 0x5c, 0x00, 0xcc},       {0xb3, 0x01, 0x41, 0xcc},
+       {}
+};
+static const __u8 po3130_rundata[][4] = {
+       {0x00, 0x47, 0x45, 0xaa},       {0x00, 0x48, 0x9b, 0xaa},
+       {0x00, 0x49, 0x3a, 0xaa},       {0x00, 0x4a, 0x01, 0xaa},
+       {0x00, 0x44, 0x40, 0xaa},
+/*     {0x00, 0xd5, 0x7c, 0xaa}, */
+       {0x00, 0xad, 0x04, 0xaa},       {0x00, 0xae, 0x00, 0xaa},
+       {0x00, 0xb0, 0x78, 0xaa},       {0x00, 0x98, 0x02, 0xaa},
+       {0x00, 0x94, 0x25, 0xaa},       {0x00, 0x95, 0x25, 0xaa},
+       {0x00, 0x59, 0x68, 0xaa},       {0x00, 0x44, 0x20, 0xaa},
+       {0x00, 0x17, 0x50, 0xaa},       {0x00, 0x19, 0x50, 0xaa},
+       {0x00, 0xd1, 0x3c, 0xaa},       {0x00, 0xd1, 0x3c, 0xaa},
+       {0x00, 0x1e, 0x06, 0xaa},       {0x00, 0x1e, 0x06, 0xaa},
+       {}
+};
+
+static const __u8 po3130_initQVGA_data[][4] = {
+       {0xb0, 0x4d, 0x00, 0xcc},       {0xb3, 0x01, 0x01, 0xcc},
+       {0x00, 0x00, 0x50, 0xdd},       {0xb0, 0x03, 0x09, 0xcc},
+       {0xb3, 0x00, 0x04, 0xcc},       {0xb3, 0x00, 0x24, 0xcc},
+       {0xb3, 0x00, 0x25, 0xcc},       {0xb3, 0x08, 0x01, 0xcc},
+       {0xb3, 0x09, 0x0c, 0xcc},       {0xb3, 0x05, 0x00, 0xcc},
+       {0xb3, 0x06, 0x01, 0xcc},       {0xb3, 0x03, 0x1a, 0xcc},
+       {0xb3, 0x04, 0x15, 0xcc},       {0xb3, 0x20, 0x00, 0xcc},
+       {0xb3, 0x21, 0x00, 0xcc},       {0xb3, 0x22, 0x01, 0xcc},
+       {0xb3, 0x23, 0xe0, 0xcc},       {0xb8, 0x08, 0xe0, 0xcc},
+       {0xb3, 0x14, 0x00, 0xcc},       {0xb3, 0x15, 0x00, 0xcc},
+       {0xb3, 0x16, 0x02, 0xcc},       {0xb3, 0x17, 0x7f, 0xcc},
+       {0xb3, 0x34, 0x01, 0xcc},       {0xb3, 0x35, 0xf6, 0xcc},
+       {0xb3, 0x00, 0x27, 0xcc},       {0xbc, 0x00, 0xd1, 0xcc},
+       {0xb8, 0x00, 0x21, 0xcc},       {0xb8, 0x27, 0x20, 0xcc},
+       {0xb8, 0x01, 0x79, 0xcc},       {0xb8, 0x81, 0x09, 0xcc},
+       {0xb8, 0x2c, 0x50, 0xcc},       {0xb8, 0x2d, 0xf8, 0xcc},
+       {0xb8, 0x2e, 0xf8, 0xcc},       {0xb8, 0x2f, 0xf8, 0xcc},
+       {0xb8, 0x30, 0x50, 0xcc},       {0xb8, 0x31, 0xf8, 0xcc},
+       {0xb8, 0x32, 0xf8, 0xcc},       {0xb8, 0x33, 0xf8, 0xcc},
+       {0xb8, 0x34, 0x50, 0xcc},       {0xb8, 0x35, 0x00, 0xcc},
+       {0xb8, 0x36, 0x00, 0xcc},       {0xb8, 0x37, 0x00, 0xcc},
+       {0x00, 0x1e, 0xc6, 0xaa},       {0x00, 0x20, 0x44, 0xaa},
+       {0x00, 0xad, 0x02, 0xaa},       {0x00, 0xae, 0x2c, 0xaa},
+       {0x00, 0x12, 0x08, 0xaa},       {0x00, 0x17, 0x41, 0xaa},
+       {0x00, 0x19, 0x41, 0xaa},       {0x00, 0x1e, 0x06, 0xaa},
+       {0x00, 0x21, 0x00, 0xaa},       {0x00, 0x36, 0xc0, 0xaa},
+       {0x00, 0x37, 0xc8, 0xaa},       {0x00, 0x3b, 0x36, 0xaa},
+       {0x00, 0x4b, 0xfe, 0xaa},       {0x00, 0x51, 0x1c, 0xaa},
+       {0x00, 0x52, 0x01, 0xaa},       {0x00, 0x55, 0x0a, 0xaa},
+       {0x00, 0x59, 0x6f, 0xaa},       {0x00, 0x5a, 0x04, 0xaa},
+       {0x00, 0x5c, 0x10, 0xaa},       {0x00, 0x5d, 0x10, 0xaa},
+       {0x00, 0x5e, 0x10, 0xaa},       {0x00, 0x5f, 0x10, 0xaa},
+       {0x00, 0x61, 0x00, 0xaa},       {0x00, 0x62, 0x18, 0xaa},
+       {0x00, 0x63, 0x30, 0xaa},       {0x00, 0x70, 0x68, 0xaa},
+       {0x00, 0x80, 0x71, 0xaa},       {0x00, 0x81, 0x08, 0xaa},
+       {0x00, 0x82, 0x00, 0xaa},       {0x00, 0x83, 0x55, 0xaa},
+       {0x00, 0x84, 0x06, 0xaa},       {0x00, 0x85, 0x06, 0xaa},
+       {0x00, 0x86, 0x13, 0xaa},       {0x00, 0x87, 0x18, 0xaa},
+       {0x00, 0xaa, 0x3f, 0xaa},       {0x00, 0xab, 0x44, 0xaa},
+       {0x00, 0xb0, 0x68, 0xaa},       {0x00, 0xb5, 0x10, 0xaa},
+       {0x00, 0xb8, 0x20, 0xaa},       {0x00, 0xb9, 0xa0, 0xaa},
+       {0x00, 0xbc, 0x04, 0xaa},       {0x00, 0x8b, 0x40, 0xaa},
+       {0x00, 0x8c, 0x91, 0xaa},       {0x00, 0x8d, 0x8f, 0xaa},
+       {0x00, 0x8e, 0x91, 0xaa},       {0x00, 0x8f, 0x43, 0xaa},
+       {0x00, 0x90, 0x92, 0xaa},       {0x00, 0x91, 0x89, 0xaa},
+       {0x00, 0x92, 0x9d, 0xaa},       {0x00, 0x93, 0x46, 0xaa},
+       {0x00, 0xd6, 0x22, 0xaa},       {0x00, 0x73, 0x00, 0xaa},
+       {0x00, 0x74, 0x10, 0xaa},       {0x00, 0x75, 0x20, 0xaa},
+       {0x00, 0x76, 0x2b, 0xaa},       {0x00, 0x77, 0x36, 0xaa},
+       {0x00, 0x78, 0x49, 0xaa},       {0x00, 0x79, 0x5a, 0xaa},
+       {0x00, 0x7a, 0x7f, 0xaa},       {0x00, 0x7b, 0x9b, 0xaa},
+       {0x00, 0x7c, 0xba, 0xaa},       {0x00, 0x7d, 0xd4, 0xaa},
+       {0x00, 0x7e, 0xea, 0xaa},       {0x00, 0xd6, 0x62, 0xaa},
+       {0x00, 0x73, 0x00, 0xaa},       {0x00, 0x74, 0x10, 0xaa},
+       {0x00, 0x75, 0x20, 0xaa},       {0x00, 0x76, 0x2b, 0xaa},
+       {0x00, 0x77, 0x36, 0xaa},       {0x00, 0x78, 0x49, 0xaa},
+       {0x00, 0x79, 0x5a, 0xaa},       {0x00, 0x7a, 0x7f, 0xaa},
+       {0x00, 0x7b, 0x9b, 0xaa},       {0x00, 0x7c, 0xba, 0xaa},
+       {0x00, 0x7d, 0xd4, 0xaa},       {0x00, 0x7e, 0xea, 0xaa},
+       {0x00, 0xd6, 0xa2, 0xaa},       {0x00, 0x73, 0x00, 0xaa},
+       {0x00, 0x74, 0x10, 0xaa},       {0x00, 0x75, 0x20, 0xaa},
+       {0x00, 0x76, 0x2b, 0xaa},       {0x00, 0x77, 0x36, 0xaa},
+       {0x00, 0x78, 0x49, 0xaa},       {0x00, 0x79, 0x5a, 0xaa},
+       {0x00, 0x7a, 0x7f, 0xaa},       {0x00, 0x7b, 0x9b, 0xaa},
+       {0x00, 0x7c, 0xba, 0xaa},       {0x00, 0x7d, 0xd4, 0xaa},
+       {0x00, 0x7e, 0xea, 0xaa},       {0x00, 0x4c, 0x07, 0xaa},
+       {0x00, 0x4b, 0xe0, 0xaa},       {0x00, 0x4e, 0x77, 0xaa},
+       {0x00, 0x59, 0x66, 0xaa},       {0x00, 0x4d, 0x0a, 0xaa},
+       {0x00, 0xd1, 0x00, 0xaa},       {0x00, 0x20, 0xc4, 0xaa},
+       {0xb8, 0x8e, 0x00, 0xcc},       {0xb8, 0x8f, 0xff, 0xcc},
+       {0xb8, 0xfe, 0x00, 0xcc},       {0xb8, 0xff, 0x28, 0xcc},
+       {0xb9, 0x00, 0x28, 0xcc},       {0xb9, 0x01, 0x28, 0xcc},
+       {0xb9, 0x02, 0x28, 0xcc},       {0xb9, 0x03, 0x00, 0xcc},
+       {0xb9, 0x04, 0x00, 0xcc},       {0xb9, 0x05, 0x3c, 0xcc},
+       {0xb9, 0x06, 0x3c, 0xcc},       {0xb9, 0x07, 0x3c, 0xcc},
+       {0xb9, 0x08, 0x3c, 0xcc},       {0xbc, 0x02, 0x18, 0xcc},
+       {0xbc, 0x03, 0x50, 0xcc},       {0xbc, 0x04, 0x18, 0xcc},
+       {0xbc, 0x05, 0x00, 0xcc},       {0xbc, 0x06, 0x00, 0xcc},
+       {0xbc, 0x08, 0x30, 0xcc},       {0xbc, 0x09, 0x40, 0xcc},
+       {0xbc, 0x0a, 0x10, 0xcc},       {0xbc, 0x0b, 0x00, 0xcc},
+       {0xbc, 0x0c, 0x00, 0xcc},       {0x00, 0x05, 0x00, 0xaa},
+       {0xb3, 0x5c, 0x00, 0xcc},       {0xb3, 0x01, 0x41, 0xcc},
+       {}
+};
+
+static const __u8 hv7131r_gamma[17] = {
+/*     0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8,
+ *     0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff */
+       0x04, 0x1a, 0x36, 0x55, 0x6f, 0x87, 0x9d, 0xb0, 0xc1,
+       0xcf, 0xda, 0xe4, 0xec, 0xf3, 0xf8, 0xfd, 0xff
+};
+static const __u8 hv7131r_matrix[9] = {
+       0x5f, 0xec, 0xf5, 0xf1, 0x5a, 0xf5, 0xf1, 0xec, 0x63
+};
+static const __u8 hv7131r_initVGA_data[][4] = {
+       {0xb0, 0x4d, 0x00, 0xcc},       {0xb3, 0x01, 0x01, 0xcc},
+       {0x00, 0x00, 0x50, 0xdd},       {0xb0, 0x03, 0x01, 0xcc},
+       {0xb3, 0x00, 0x24, 0xcc},
+       {0xb3, 0x00, 0x25, 0xcc},       {0xb3, 0x08, 0x01, 0xcc},
+       {0xb3, 0x09, 0x0c, 0xcc},       {0xb3, 0x05, 0x00, 0xcc},
+       {0xb3, 0x06, 0x01, 0xcc},
+       {0xb3, 0x01, 0x45, 0xcc},       {0xb3, 0x03, 0x0b, 0xcc},
+       {0xb3, 0x04, 0x05, 0xcc},       {0xb3, 0x20, 0x00, 0xcc},
+       {0xb3, 0x21, 0x00, 0xcc},
+       {0xb3, 0x22, 0x01, 0xcc},       {0xb3, 0x23, 0xe0, 0xcc},
+       {0xb3, 0x14, 0x00, 0xcc},       {0xb3, 0x15, 0x00, 0xcc},
+       {0xb3, 0x16, 0x02, 0xcc},
+       {0xb3, 0x17, 0x7f, 0xcc},       {0xb3, 0x34, 0x01, 0xcc},
+       {0xb3, 0x35, 0x91, 0xcc},       {0xb3, 0x00, 0x27, 0xcc},
+       {0xbc, 0x00, 0x73, 0xcc},
+       {0xb8, 0x00, 0x23, 0xcc},       {0x00, 0x01, 0x0c, 0xaa},
+       {0x00, 0x14, 0x01, 0xaa},       {0x00, 0x15, 0xe6, 0xaa},
+       {0x00, 0x16, 0x02, 0xaa},
+       {0x00, 0x17, 0x86, 0xaa},       {0x00, 0x23, 0x00, 0xaa},
+       {0x00, 0x25, 0x09, 0xaa},       {0x00, 0x26, 0x27, 0xaa},
+       {0x00, 0x27, 0xc0, 0xaa},
+       {0xb8, 0x2c, 0x60, 0xcc},       {0xb8, 0x2d, 0xf8, 0xcc},
+       {0xb8, 0x2e, 0xf8, 0xcc},       {0xb8, 0x2f, 0xf8, 0xcc},
+       {0xb8, 0x30, 0x50, 0xcc},
+       {0xb8, 0x31, 0xf8, 0xcc},       {0xb8, 0x32, 0xf8, 0xcc},
+       {0xb8, 0x33, 0xf8, 0xcc},       {0xb8, 0x34, 0x65, 0xcc},
+       {0xb8, 0x35, 0x00, 0xcc},
+       {0xb8, 0x36, 0x00, 0xcc},       {0xb8, 0x37, 0x00, 0xcc},
+       {0xb8, 0x27, 0x20, 0xcc},       {0xb8, 0x01, 0x7d, 0xcc},
+       {0xb8, 0x81, 0x09, 0xcc},
+       {0xb3, 0x01, 0x41, 0xcc},       {0xb8, 0xfe, 0x00, 0xcc},
+       {0xb8, 0xff, 0x28, 0xcc},       {0xb9, 0x00, 0x28, 0xcc},
+       {0xb9, 0x01, 0x28, 0xcc},
+       {0xb9, 0x02, 0x28, 0xcc},       {0xb9, 0x03, 0x00, 0xcc},
+       {0xb9, 0x04, 0x00, 0xcc},       {0xb9, 0x05, 0x3c, 0xcc},
+       {0xb9, 0x06, 0x3c, 0xcc},
+       {0xb9, 0x07, 0x3c, 0xcc},       {0xb9, 0x08, 0x3c, 0xcc},
+       {0xb8, 0x8e, 0x00, 0xcc},       {0xb8, 0x8f, 0xff, 0xcc},
+       {0x00, 0x30, 0x18, 0xaa},
+       {}
+};
+
+static const __u8 hv7131r_initQVGA_data[][4] = {
+       {0xb0, 0x4d, 0x00, 0xcc},       {0xb3, 0x01, 0x01, 0xcc},
+       {0x00, 0x00, 0x50, 0xdd},       {0xb0, 0x03, 0x01, 0xcc},
+       {0xb3, 0x00, 0x24, 0xcc},
+       {0xb3, 0x00, 0x25, 0xcc},       {0xb3, 0x08, 0x01, 0xcc},
+       {0xb3, 0x09, 0x0c, 0xcc},       {0xb3, 0x05, 0x00, 0xcc},
+       {0xb3, 0x06, 0x01, 0xcc},
+       {0xb3, 0x03, 0x0b, 0xcc},       {0xb3, 0x04, 0x05, 0xcc},
+       {0xb3, 0x20, 0x00, 0xcc},       {0xb3, 0x21, 0x00, 0xcc},
+       {0xb3, 0x22, 0x01, 0xcc},
+       {0xb3, 0x23, 0xe0, 0xcc},       {0xb3, 0x14, 0x00, 0xcc},
+       {0xb3, 0x15, 0x00, 0xcc},       {0xb3, 0x16, 0x02, 0xcc},
+       {0xb3, 0x17, 0x7f, 0xcc},
+       {0xb3, 0x34, 0x01, 0xcc},       {0xb3, 0x35, 0x91, 0xcc},
+       {0xb3, 0x00, 0x27, 0xcc},       {0xbc, 0x00, 0xd1, 0xcc},
+       {0xb8, 0x00, 0x21, 0xcc},
+       {0x00, 0x01, 0x0c, 0xaa},       {0x00, 0x14, 0x01, 0xaa},
+       {0x00, 0x15, 0xe6, 0xaa},       {0x00, 0x16, 0x02, 0xaa},
+       {0x00, 0x17, 0x86, 0xaa},
+       {0x00, 0x23, 0x00, 0xaa},       {0x00, 0x25, 0x01, 0xaa},
+       {0x00, 0x26, 0xd4, 0xaa},       {0x00, 0x27, 0xc0, 0xaa},
+       {0xbc, 0x02, 0x08, 0xcc},
+       {0xbc, 0x03, 0x70, 0xcc},       {0xbc, 0x04, 0x08, 0xcc},
+       {0xbc, 0x05, 0x00, 0xcc},       {0xbc, 0x06, 0x00, 0xcc},
+       {0xbc, 0x08, 0x3c, 0xcc},
+       {0xbc, 0x09, 0x40, 0xcc},       {0xbc, 0x0a, 0x04, 0xcc},
+       {0xbc, 0x0b, 0x00, 0xcc},       {0xbc, 0x0c, 0x00, 0xcc},
+       {0xb8, 0xfe, 0x02, 0xcc},
+       {0xb8, 0xff, 0x07, 0xcc},       {0xb9, 0x00, 0x14, 0xcc},
+       {0xb9, 0x01, 0x14, 0xcc},       {0xb9, 0x02, 0x14, 0xcc},
+       {0xb9, 0x03, 0x00, 0xcc},
+       {0xb9, 0x04, 0x02, 0xcc},       {0xb9, 0x05, 0x05, 0xcc},
+       {0xb9, 0x06, 0x0f, 0xcc},       {0xb9, 0x07, 0x0f, 0xcc},
+       {0xb9, 0x08, 0x0f, 0xcc},
+       {0xb8, 0x2c, 0x60, 0xcc},       {0xb8, 0x2d, 0xf8, 0xcc},
+       {0xb8, 0x2e, 0xf8, 0xcc},       {0xb8, 0x2f, 0xf8, 0xcc},
+       {0xb8, 0x30, 0x50, 0xcc},
+       {0xb8, 0x31, 0xf8, 0xcc},       {0xb8, 0x32, 0xf8, 0xcc},
+       {0xb8, 0x33, 0xf8, 0xcc},
+       {0xb8, 0x34, 0x65, 0xcc},       {0xb8, 0x35, 0x00, 0xcc},
+       {0xb8, 0x36, 0x00, 0xcc},       {0xb8, 0x37, 0x00, 0xcc},
+       {0xb8, 0x27, 0x20, 0xcc},
+       {0xb8, 0x01, 0x7d, 0xcc},       {0xb8, 0x81, 0x09, 0xcc},
+       {0xb3, 0x01, 0x41, 0xcc},       {0xb8, 0xfe, 0x00, 0xcc},
+       {0xb8, 0xff, 0x28, 0xcc},
+       {0xb9, 0x00, 0x28, 0xcc},       {0xb9, 0x01, 0x28, 0xcc},
+       {0xb9, 0x02, 0x28, 0xcc},       {0xb9, 0x03, 0x00, 0xcc},
+       {0xb9, 0x04, 0x00, 0xcc},
+       {0xb9, 0x05, 0x3c, 0xcc},       {0xb9, 0x06, 0x3c, 0xcc},
+       {0xb9, 0x07, 0x3c, 0xcc},       {0xb9, 0x08, 0x3c, 0xcc},
+       {0xb8, 0x8e, 0x00, 0xcc},
+       {0xb8, 0x8f, 0xff, 0xcc},       {0x00, 0x30, 0x18, 0xaa},
+       {}
+};
+
+static const __u8 ov7660_gamma[17] = {
+       0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8,
+       0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff
+};
+static const __u8 ov7660_matrix[9] = {
+       0x5a, 0xf0, 0xf6, 0xf3, 0x57, 0xf6, 0xf3, 0xef, 0x62
+};
+static const __u8 ov7660_initVGA_data[][4] = {
+       {0xb0, 0x4d, 0x00, 0xcc},       {0xb3, 0x01, 0x01, 0xcc},
+       {0x00, 0x00, 0x50, 0xdd},
+       {0xb0, 0x03, 0x01, 0xcc},
+       {0xb3, 0x00, 0x21, 0xcc},       {0xb3, 0x00, 0x26, 0xcc},
+       {0xb3, 0x05, 0x01, 0xcc},
+       {0xb3, 0x06, 0x03, 0xcc},
+       {0xb3, 0x03, 0x1f, 0xcc},       {0xb3, 0x04, 0x05, 0xcc},
+       {0xb3, 0x05, 0x00, 0xcc},
+       {0xb3, 0x06, 0x01, 0xcc},
+       {0xb3, 0x15, 0x00, 0xcc},/* 0xb315  <-0 href startl */
+       {0xb3, 0x16, 0x02, 0xcc},       {0xb3, 0x17, 0x7f, 0xcc},
+       {0xb3, 0x21, 0x00, 0xcc},
+       {0xb3, 0x23, 0xe0, 0xcc},       {0xb3, 0x1d, 0x01, 0xcc},
+       {0xb3, 0x1f, 0x02, 0xcc},
+       {0xb3, 0x34, 0x01, 0xcc},
+       {0xb3, 0x35, 0xa1, 0xcc},       {0xb3, 0x00, 0x26, 0xcc},
+       {0xb8, 0x00, 0x33, 0xcc}, /* 13 */
+       {0xb8, 0x01, 0x7d, 0xcc},
+       {0xbc, 0x00, 0x73, 0xcc},       {0xb8, 0x81, 0x09, 0xcc},
+       {0xb8, 0x27, 0x20, 0xcc},
+       {0xb8, 0x8f, 0x50, 0xcc},
+       {0x00, 0x01, 0x80, 0xaa},       {0x00, 0x02, 0x80, 0xaa},
+       {0x00, 0x12, 0x80, 0xaa},
+       {0x00, 0x12, 0x05, 0xaa},
+       {0x00, 0x1e, 0x01, 0xaa},
+       {0x00, 0x3d, 0x40, 0xaa}, /* 0x3d <-40 gamma 01 */
+       {0x00, 0x41, 0x00, 0xaa}, /* edge 00 */
+       {0x00, 0x0d, 0x48, 0xaa},       {0x00, 0x0e, 0x04, 0xaa},
+       {0x00, 0x13, 0xa7, 0xaa},
+       {0x00, 0x40, 0xc1, 0xaa},       {0x00, 0x35, 0x00, 0xaa},
+       {0x00, 0x36, 0x00, 0xaa},
+       {0x00, 0x3c, 0x68, 0xaa},       {0x00, 0x1b, 0x05, 0xaa},
+       {0x00, 0x39, 0x43, 0xaa},
+       {0x00, 0x8d, 0xcf, 0xaa},
+       {0x00, 0x8b, 0xcc, 0xaa},       {0x00, 0x8c, 0xcc, 0xaa},
+       {0x00, 0x0f, 0x62, 0xaa},
+       {0x00, 0x35, 0x84, 0xaa},
+       {0x00, 0x3b, 0x08, 0xaa}, /* 0 * Nightframe 1/4 + 50Hz -> 0xC8 */
+       {0x00, 0x3a, 0x00, 0xaa}, /* mx change yuyv format 00, 04, 01; 08, 0c*/
+       {0x00, 0x14, 0x2a, 0xaa}, /* agc ampli */
+       {0x00, 0x9e, 0x40, 0xaa},       {0xb8, 0x8f, 0x50, 0xcc},
+       {0x00, 0x01, 0x80, 0xaa},
+       {0x00, 0x02, 0x80, 0xaa},
+       {0xb8, 0xfe, 0x00, 0xcc},       {0xb8, 0xff, 0x28, 0xcc},
+       {0xb9, 0x00, 0x28, 0xcc},
+       {0xb9, 0x01, 0x28, 0xcc},       {0xb9, 0x02, 0x28, 0xcc},
+       {0xb9, 0x03, 0x00, 0xcc},
+       {0xb9, 0x04, 0x00, 0xcc},
+       {0xb9, 0x05, 0x3c, 0xcc},       {0xb9, 0x06, 0x3c, 0xcc},
+       {0xb9, 0x07, 0x3c, 0xcc},
+       {0xb9, 0x08, 0x3c, 0xcc},
+
+       {0xb8, 0x8e, 0x00, 0xcc},       {0xb8, 0x8f, 0xff, 0xcc},
+
+       {0x00, 0x29, 0x3c, 0xaa},       {0xb3, 0x01, 0x45, 0xcc},
+       {}
+};
+static const __u8 ov7660_initQVGA_data[][4] = {
+       {0xb0, 0x4d, 0x00, 0xcc},       {0xb3, 0x01, 0x01, 0xcc},
+       {0x00, 0x00, 0x50, 0xdd},       {0xb0, 0x03, 0x01, 0xcc},
+       {0xb3, 0x00, 0x21, 0xcc},       {0xb3, 0x00, 0x26, 0xcc},
+       {0xb3, 0x05, 0x01, 0xcc},       {0xb3, 0x06, 0x03, 0xcc},
+       {0xb3, 0x03, 0x1f, 0xcc},       {0xb3, 0x04, 0x05, 0xcc},
+       {0xb3, 0x05, 0x00, 0xcc},       {0xb3, 0x06, 0x01, 0xcc},
+       {0xb3, 0x15, 0x00, 0xcc},/* 0xb315  <-0 href startl */
+       {0xb3, 0x16, 0x02, 0xcc},       {0xb3, 0x17, 0x7f, 0xcc},
+       {0xb3, 0x21, 0x00, 0xcc},
+       {0xb3, 0x23, 0xe0, 0xcc},       {0xb3, 0x1d, 0x01, 0xcc},
+       {0xb3, 0x1f, 0x02, 0xcc},       {0xb3, 0x34, 0x01, 0xcc},
+       {0xb3, 0x35, 0xa1, 0xcc},       {0xb3, 0x00, 0x26, 0xcc},
+       {0xb8, 0x00, 0x33, 0xcc}, /* 13 */
+       {0xb8, 0x01, 0x7d, 0xcc},
+/* sizer */
+       {0xbc, 0x00, 0xd3, 0xcc},
+       {0xb8, 0x81, 0x09, 0xcc},       {0xb8, 0x81, 0x09, 0xcc},
+       {0xb8, 0x27, 0x20, 0xcc},       {0xb8, 0x8f, 0x50, 0xcc},
+       {0x00, 0x01, 0x80, 0xaa},       {0x00, 0x02, 0x80, 0xaa},
+       {0x00, 0x12, 0x80, 0xaa},       {0x00, 0x12, 0x05, 0xaa},
+       {0x00, 0x1e, 0x01, 0xaa},
+       {0x00, 0x3d, 0x40, 0xaa}, /* 0x3d <-40 gamma 01 */
+       {0x00, 0x41, 0x00, 0xaa}, /* edge 00 */
+       {0x00, 0x0d, 0x48, 0xaa},       {0x00, 0x0e, 0x04, 0xaa},
+       {0x00, 0x13, 0xa7, 0xaa},
+       {0x00, 0x40, 0xc1, 0xaa},       {0x00, 0x35, 0x00, 0xaa},
+       {0x00, 0x36, 0x00, 0xaa},
+       {0x00, 0x3c, 0x68, 0xaa},       {0x00, 0x1b, 0x05, 0xaa},
+       {0x00, 0x39, 0x43, 0xaa},       {0x00, 0x8d, 0xcf, 0xaa},
+       {0x00, 0x8b, 0xcc, 0xaa},       {0x00, 0x8c, 0xcc, 0xaa},
+       {0x00, 0x0f, 0x62, 0xaa},       {0x00, 0x35, 0x84, 0xaa},
+       {0x00, 0x3b, 0x08, 0xaa}, /* 0  * Nightframe 1/4 + 50Hz -> 0xC8 */
+       {0x00, 0x3a, 0x00, 0xaa}, /* mx change yuyv format 00, 04, 01; 08, 0c*/
+       {0x00, 0x14, 0x2a, 0xaa}, /* agc ampli */
+       {0x00, 0x9e, 0x40, 0xaa},       {0xb8, 0x8f, 0x50, 0xcc},
+       {0x00, 0x01, 0x80, 0xaa},
+       {0x00, 0x02, 0x80, 0xaa},
+/* sizer filters */
+       {0xbc, 0x02, 0x08, 0xcc},
+       {0xbc, 0x03, 0x70, 0xcc},
+       {0xb8, 0x35, 0x00, 0xcc},
+       {0xb8, 0x36, 0x00, 0xcc},
+       {0xb8, 0x37, 0x00, 0xcc},
+       {0xbc, 0x04, 0x08, 0xcc},
+       {0xbc, 0x05, 0x00, 0xcc},
+       {0xbc, 0x06, 0x00, 0xcc},
+       {0xbc, 0x08, 0x3c, 0xcc},
+       {0xbc, 0x09, 0x40, 0xcc},
+       {0xbc, 0x0a, 0x04, 0xcc},
+       {0xbc, 0x0b, 0x00, 0xcc},
+       {0xbc, 0x0c, 0x00, 0xcc},
+/* */
+       {0xb8, 0xfe, 0x00, 0xcc},
+       {0xb8, 0xff, 0x28, 0xcc},
+/* */
+       {0xb9, 0x00, 0x28, 0xcc},       {0xb9, 0x01, 0x28, 0xcc},
+       {0xb9, 0x02, 0x28, 0xcc},       {0xb9, 0x03, 0x00, 0xcc},
+       {0xb9, 0x04, 0x00, 0xcc},       {0xb9, 0x05, 0x3c, 0xcc},
+       {0xb9, 0x06, 0x3c, 0xcc},       {0xb9, 0x07, 0x3c, 0xcc},
+       {0xb9, 0x08, 0x3c, 0xcc},
+/* */
+       {0xb8, 0x8e, 0x00, 0xcc},
+       {0xb8, 0x8f, 0xff, 0xcc}, /* ff */
+       {0x00, 0x29, 0x3c, 0xaa},
+       {0xb3, 0x01, 0x45, 0xcc}, /* 45 */
+       {}
+};
+
+static const __u8 ov7660_50HZ[][4] = {
+       {0x00, 0x3b, 0x08, 0xaa},
+       {0x00, 0x9d, 0x40, 0xaa},
+       {0x00, 0x13, 0xa7, 0xaa},
+       {}
+};
+
+static const __u8 ov7660_60HZ[][4] = {
+       {0x00, 0x3b, 0x00, 0xaa},
+       {0x00, 0x9e, 0x40, 0xaa},
+       {0x00, 0x13, 0xa7, 0xaa},
+       {}
+};
+
+static const __u8 ov7660_NoFliker[][4] = {
+       {0x00, 0x13, 0x87, 0xaa},
+       {}
+};
+
+static const __u8 ov7670_initVGA_JPG[][4] = {
+       {0xb3, 0x01, 0x05, 0xcc},
+       {0x00, 0x00, 0x30, 0xdd},       {0xb0, 0x03, 0x19, 0xcc},
+       {0x00, 0x00, 0x10, 0xdd},
+       {0xb0, 0x04, 0x02, 0xcc},       {0x00, 0x00, 0x10, 0xdd},
+       {0xb3, 0x00, 0x66, 0xcc},       {0xb3, 0x00, 0x67, 0xcc},
+       {0xb3, 0x35, 0xa1, 0xcc},       {0xb3, 0x34, 0x01, 0xcc},
+       {0xb3, 0x05, 0x01, 0xcc},       {0xb3, 0x06, 0x01, 0xcc},
+       {0xb3, 0x08, 0x01, 0xcc},       {0xb3, 0x09, 0x0c, 0xcc},
+       {0xb3, 0x02, 0x02, 0xcc},       {0xb3, 0x03, 0x1f, 0xcc},
+       {0xb3, 0x14, 0x00, 0xcc},       {0xb3, 0x15, 0x00, 0xcc},
+       {0xb3, 0x16, 0x02, 0xcc},       {0xb3, 0x17, 0x7f, 0xcc},
+       {0xb3, 0x04, 0x05, 0xcc},       {0xb3, 0x20, 0x00, 0xcc},
+       {0xb3, 0x21, 0x00, 0xcc},       {0xb3, 0x22, 0x01, 0xcc},
+       {0xb3, 0x23, 0xe0, 0xcc},       {0xbc, 0x00, 0x41, 0xcc},
+       {0xbc, 0x01, 0x01, 0xcc},       {0x00, 0x12, 0x80, 0xaa},
+       {0x00, 0x00, 0x20, 0xdd},       {0x00, 0x12, 0x00, 0xaa},
+       {0x00, 0x11, 0x40, 0xaa},       {0x00, 0x6b, 0x0a, 0xaa},
+       {0x00, 0x3a, 0x04, 0xaa},       {0x00, 0x40, 0xc0, 0xaa},
+       {0x00, 0x8c, 0x00, 0xaa},       {0x00, 0x7a, 0x29, 0xaa},
+       {0x00, 0x7b, 0x0e, 0xaa},       {0x00, 0x7c, 0x1a, 0xaa},
+       {0x00, 0x7d, 0x31, 0xaa},       {0x00, 0x7e, 0x53, 0xaa},
+       {0x00, 0x7f, 0x60, 0xaa},       {0x00, 0x80, 0x6b, 0xaa},
+       {0x00, 0x81, 0x73, 0xaa},       {0x00, 0x82, 0x7b, 0xaa},
+       {0x00, 0x83, 0x82, 0xaa},       {0x00, 0x84, 0x89, 0xaa},
+       {0x00, 0x85, 0x96, 0xaa},       {0x00, 0x86, 0xa1, 0xaa},
+       {0x00, 0x87, 0xb7, 0xaa},       {0x00, 0x88, 0xcc, 0xaa},
+       {0x00, 0x89, 0xe1, 0xaa},       {0x00, 0x13, 0xe0, 0xaa},
+       {0x00, 0x00, 0x00, 0xaa},       {0x00, 0x10, 0x00, 0xaa},
+       {0x00, 0x0d, 0x40, 0xaa},       {0x00, 0x14, 0x28, 0xaa},
+       {0x00, 0xa5, 0x05, 0xaa},       {0x00, 0xab, 0x07, 0xaa},
+       {0x00, 0x24, 0x95, 0xaa},       {0x00, 0x25, 0x33, 0xaa},
+       {0x00, 0x26, 0xe3, 0xaa},       {0x00, 0x9f, 0x88, 0xaa},
+       {0x00, 0xa0, 0x78, 0xaa},       {0x00, 0x55, 0x90, 0xaa},
+       {0x00, 0xa1, 0x03, 0xaa},       {0x00, 0xa6, 0xe0, 0xaa},
+       {0x00, 0xa7, 0xd8, 0xaa},       {0x00, 0xa8, 0xf0, 0xaa},
+       {0x00, 0xa9, 0x90, 0xaa},       {0x00, 0xaa, 0x14, 0xaa},
+       {0x00, 0x13, 0xe5, 0xaa},       {0x00, 0x0e, 0x61, 0xaa},
+       {0x00, 0x0f, 0x4b, 0xaa},       {0x00, 0x16, 0x02, 0xaa},
+       {0x00, 0x1e, 0x07, 0xaa},       {0x00, 0x21, 0x02, 0xaa},
+       {0x00, 0x22, 0x91, 0xaa},       {0x00, 0x29, 0x07, 0xaa},
+       {0x00, 0x33, 0x0b, 0xaa},       {0x00, 0x35, 0x0b, 0xaa},
+       {0x00, 0x37, 0x1d, 0xaa},       {0x00, 0x38, 0x71, 0xaa},
+       {0x00, 0x39, 0x2a, 0xaa},       {0x00, 0x3c, 0x78, 0xaa},
+       {0x00, 0x4d, 0x40, 0xaa},       {0x00, 0x4e, 0x20, 0xaa},
+       {0x00, 0x74, 0x19, 0xaa},       {0x00, 0x8d, 0x4f, 0xaa},
+       {0x00, 0x8e, 0x00, 0xaa},       {0x00, 0x8f, 0x00, 0xaa},
+       {0x00, 0x90, 0x00, 0xaa},       {0x00, 0x91, 0x00, 0xaa},
+       {0x00, 0x96, 0x00, 0xaa},       {0x00, 0x9a, 0x80, 0xaa},
+       {0x00, 0xb0, 0x84, 0xaa},       {0x00, 0xb1, 0x0c, 0xaa},
+       {0x00, 0xb2, 0x0e, 0xaa},       {0x00, 0xb3, 0x82, 0xaa},
+       {0x00, 0xb8, 0x0a, 0xaa},       {0x00, 0x43, 0x14, 0xaa},
+       {0x00, 0x44, 0xf0, 0xaa},       {0x00, 0x45, 0x45, 0xaa},
+       {0x00, 0x46, 0x63, 0xaa},       {0x00, 0x47, 0x2d, 0xaa},
+       {0x00, 0x48, 0x46, 0xaa},       {0x00, 0x59, 0x88, 0xaa},
+       {0x00, 0x5a, 0xa0, 0xaa},       {0x00, 0x5b, 0xc6, 0xaa},
+       {0x00, 0x5c, 0x7d, 0xaa},       {0x00, 0x5d, 0x5f, 0xaa},
+       {0x00, 0x5e, 0x19, 0xaa},       {0x00, 0x6c, 0x0a, 0xaa},
+       {0x00, 0x6d, 0x55, 0xaa},       {0x00, 0x6e, 0x11, 0xaa},
+       {0x00, 0x6f, 0x9e, 0xaa},       {0x00, 0x69, 0x00, 0xaa},
+       {0x00, 0x6a, 0x40, 0xaa},       {0x00, 0x01, 0x40, 0xaa},
+       {0x00, 0x02, 0x40, 0xaa},       {0x00, 0x13, 0xe7, 0xaa},
+       {0x00, 0x5f, 0xf0, 0xaa},       {0x00, 0x60, 0xf0, 0xaa},
+       {0x00, 0x61, 0xf0, 0xaa},       {0x00, 0x27, 0xa0, 0xaa},
+       {0x00, 0x28, 0x80, 0xaa},       {0x00, 0x2c, 0x90, 0xaa},
+       {0x00, 0x4f, 0x66, 0xaa},       {0x00, 0x50, 0x66, 0xaa},
+       {0x00, 0x51, 0x00, 0xaa},       {0x00, 0x52, 0x22, 0xaa},
+       {0x00, 0x53, 0x5e, 0xaa},       {0x00, 0x54, 0x80, 0xaa},
+       {0x00, 0x58, 0x9e, 0xaa},       {0x00, 0x41, 0x08, 0xaa},
+       {0x00, 0x3f, 0x00, 0xaa},       {0x00, 0x75, 0x85, 0xaa},
+       {0x00, 0x76, 0xe1, 0xaa},       {0x00, 0x4c, 0x00, 0xaa},
+       {0x00, 0x77, 0x0a, 0xaa},       {0x00, 0x3d, 0x88, 0xaa},
+       {0x00, 0x4b, 0x09, 0xaa},       {0x00, 0xc9, 0x60, 0xaa},
+       {0x00, 0x41, 0x38, 0xaa},       {0x00, 0x62, 0x30, 0xaa},
+       {0x00, 0x63, 0x30, 0xaa},       {0x00, 0x64, 0x08, 0xaa},
+       {0x00, 0x94, 0x07, 0xaa},       {0x00, 0x95, 0x0b, 0xaa},
+       {0x00, 0x65, 0x00, 0xaa},       {0x00, 0x66, 0x05, 0xaa},
+       {0x00, 0x56, 0x50, 0xaa},       {0x00, 0x34, 0x11, 0xaa},
+       {0x00, 0xa4, 0x88, 0xaa},       {0x00, 0x96, 0x00, 0xaa},
+       {0x00, 0x97, 0x30, 0xaa},       {0x00, 0x98, 0x20, 0xaa},
+       {0x00, 0x99, 0x30, 0xaa},       {0x00, 0x9a, 0x84, 0xaa},
+       {0x00, 0x9b, 0x29, 0xaa},       {0x00, 0x9c, 0x03, 0xaa},
+       {0x00, 0x78, 0x04, 0xaa},       {0x00, 0x79, 0x01, 0xaa},
+       {0x00, 0xc8, 0xf0, 0xaa},       {0x00, 0x79, 0x0f, 0xaa},
+       {0x00, 0xc8, 0x00, 0xaa},       {0x00, 0x79, 0x10, 0xaa},
+       {0x00, 0xc8, 0x7e, 0xaa},       {0x00, 0x79, 0x0a, 0xaa},
+       {0x00, 0xc8, 0x80, 0xaa},       {0x00, 0x79, 0x0b, 0xaa},
+       {0x00, 0xc8, 0x01, 0xaa},       {0x00, 0x79, 0x0c, 0xaa},
+       {0x00, 0xc8, 0x0f, 0xaa},       {0x00, 0x79, 0x0d, 0xaa},
+       {0x00, 0xc8, 0x20, 0xaa},       {0x00, 0x79, 0x09, 0xaa},
+       {0x00, 0xc8, 0x80, 0xaa},       {0x00, 0x79, 0x02, 0xaa},
+       {0x00, 0xc8, 0xc0, 0xaa},       {0x00, 0x79, 0x03, 0xaa},
+       {0x00, 0xc8, 0x40, 0xaa},       {0x00, 0x79, 0x05, 0xaa},
+       {0x00, 0xc8, 0x30, 0xaa},       {0x00, 0x79, 0x26, 0xaa},
+       {0x00, 0x11, 0x40, 0xaa},       {0x00, 0x3a, 0x04, 0xaa},
+       {0x00, 0x12, 0x00, 0xaa},       {0x00, 0x40, 0xc0, 0xaa},
+       {0x00, 0x8c, 0x00, 0xaa},       {0x00, 0x17, 0x14, 0xaa},
+       {0x00, 0x18, 0x02, 0xaa},       {0x00, 0x32, 0x92, 0xaa},
+       {0x00, 0x19, 0x02, 0xaa},       {0x00, 0x1a, 0x7a, 0xaa},
+       {0x00, 0x03, 0x0a, 0xaa},       {0x00, 0x0c, 0x00, 0xaa},
+       {0x00, 0x3e, 0x00, 0xaa},       {0x00, 0x70, 0x3a, 0xaa},
+       {0x00, 0x71, 0x35, 0xaa},       {0x00, 0x72, 0x11, 0xaa},
+       {0x00, 0x73, 0xf0, 0xaa},       {0x00, 0xa2, 0x02, 0xaa},
+       {0x00, 0xb1, 0x00, 0xaa},       {0x00, 0xb1, 0x0c, 0xaa},
+       {0x00, 0x1e, 0x37, 0xaa},       {0x00, 0xaa, 0x14, 0xaa},
+       {0x00, 0x24, 0x80, 0xaa},       {0x00, 0x25, 0x74, 0xaa},
+       {0x00, 0x26, 0xd3, 0xaa},       {0x00, 0x0d, 0x00, 0xaa},
+       {0x00, 0x14, 0x18, 0xaa},       {0x00, 0x9d, 0x99, 0xaa},
+       {0x00, 0x9e, 0x7f, 0xaa},       {0x00, 0x64, 0x08, 0xaa},
+       {0x00, 0x94, 0x07, 0xaa},       {0x00, 0x95, 0x06, 0xaa},
+       {0x00, 0x66, 0x05, 0xaa},       {0x00, 0x41, 0x08, 0xaa},
+       {0x00, 0x3f, 0x00, 0xaa},       {0x00, 0x75, 0x07, 0xaa},
+       {0x00, 0x76, 0xe1, 0xaa},       {0x00, 0x4c, 0x00, 0xaa},
+       {0x00, 0x77, 0x00, 0xaa},       {0x00, 0x3d, 0xc2, 0xaa},
+       {0x00, 0x4b, 0x09, 0xaa},       {0x00, 0xc9, 0x60, 0xaa},
+       {0x00, 0x41, 0x38, 0xaa},       {0xb6, 0x00, 0x00, 0xcc},
+       {0xb6, 0x03, 0x02, 0xcc},       {0xb6, 0x02, 0x80, 0xcc},
+       {0xb6, 0x05, 0x01, 0xcc},       {0xb6, 0x04, 0xe0, 0xcc},
+       {0xb6, 0x12, 0xf8, 0xcc},       {0xb6, 0x13, 0x13, 0xcc},
+       {0xb6, 0x18, 0x02, 0xcc},       {0xb6, 0x17, 0x58, 0xcc},
+       {0xb6, 0x16, 0x00, 0xcc},       {0xb6, 0x22, 0x12, 0xcc},
+       {0xb6, 0x23, 0x0b, 0xcc},       {0xbf, 0xc0, 0x39, 0xcc},
+       {0xbf, 0xc1, 0x04, 0xcc},       {0xbf, 0xcc, 0x00, 0xcc},
+       {0xb3, 0x5c, 0x01, 0xcc},       {0xb3, 0x01, 0x45, 0xcc},
+       {0x00, 0x77, 0x05, 0xaa},
+       {},
+};
+
+static const __u8 ov7670_initQVGA_JPG[][4] = {
+       {0xb3, 0x01, 0x05, 0xcc},       {0x00, 0x00, 0x30, 0xdd},
+       {0xb0, 0x03, 0x19, 0xcc},       {0x00, 0x00, 0x10, 0xdd},
+       {0xb0, 0x04, 0x02, 0xcc},       {0x00, 0x00, 0x10, 0xdd},
+       {0xb3, 0x00, 0x66, 0xcc},       {0xb3, 0x00, 0x67, 0xcc},
+       {0xb3, 0x35, 0xa1, 0xcc},       {0xb3, 0x34, 0x01, 0xcc},
+       {0xb3, 0x05, 0x01, 0xcc},       {0xb3, 0x06, 0x01, 0xcc},
+       {0xb3, 0x08, 0x01, 0xcc},       {0xb3, 0x09, 0x0c, 0xcc},
+       {0xb3, 0x02, 0x02, 0xcc},       {0xb3, 0x03, 0x1f, 0xcc},
+       {0xb3, 0x14, 0x00, 0xcc},       {0xb3, 0x15, 0x00, 0xcc},
+       {0xb3, 0x16, 0x02, 0xcc},       {0xb3, 0x17, 0x7f, 0xcc},
+       {0xb3, 0x04, 0x05, 0xcc},       {0xb3, 0x20, 0x00, 0xcc},
+       {0xb3, 0x21, 0x00, 0xcc},       {0xb3, 0x22, 0x01, 0xcc},
+       {0xb3, 0x23, 0xe0, 0xcc},       {0xbc, 0x00, 0xd1, 0xcc},
+       {0xbc, 0x01, 0x01, 0xcc},       {0x00, 0x12, 0x80, 0xaa},
+       {0x00, 0x00, 0x20, 0xdd},       {0x00, 0x12, 0x00, 0xaa},
+       {0x00, 0x11, 0x40, 0xaa},       {0x00, 0x6b, 0x0a, 0xaa},
+       {0x00, 0x3a, 0x04, 0xaa},       {0x00, 0x40, 0xc0, 0xaa},
+       {0x00, 0x8c, 0x00, 0xaa},       {0x00, 0x7a, 0x29, 0xaa},
+       {0x00, 0x7b, 0x0e, 0xaa},       {0x00, 0x7c, 0x1a, 0xaa},
+       {0x00, 0x7d, 0x31, 0xaa},       {0x00, 0x7e, 0x53, 0xaa},
+       {0x00, 0x7f, 0x60, 0xaa},       {0x00, 0x80, 0x6b, 0xaa},
+       {0x00, 0x81, 0x73, 0xaa},       {0x00, 0x82, 0x7b, 0xaa},
+       {0x00, 0x83, 0x82, 0xaa},       {0x00, 0x84, 0x89, 0xaa},
+       {0x00, 0x85, 0x96, 0xaa},       {0x00, 0x86, 0xa1, 0xaa},
+       {0x00, 0x87, 0xb7, 0xaa},       {0x00, 0x88, 0xcc, 0xaa},
+       {0x00, 0x89, 0xe1, 0xaa},       {0x00, 0x13, 0xe0, 0xaa},
+       {0x00, 0x00, 0x00, 0xaa},       {0x00, 0x10, 0x00, 0xaa},
+       {0x00, 0x0d, 0x40, 0xaa},       {0x00, 0x14, 0x28, 0xaa},
+       {0x00, 0xa5, 0x05, 0xaa},       {0x00, 0xab, 0x07, 0xaa},
+       {0x00, 0x24, 0x95, 0xaa},       {0x00, 0x25, 0x33, 0xaa},
+       {0x00, 0x26, 0xe3, 0xaa},       {0x00, 0x9f, 0x88, 0xaa},
+       {0x00, 0xa0, 0x78, 0xaa},       {0x00, 0x55, 0x90, 0xaa},
+       {0x00, 0xa1, 0x03, 0xaa},       {0x00, 0xa6, 0xe0, 0xaa},
+       {0x00, 0xa7, 0xd8, 0xaa},       {0x00, 0xa8, 0xf0, 0xaa},
+       {0x00, 0xa9, 0x90, 0xaa},       {0x00, 0xaa, 0x14, 0xaa},
+       {0x00, 0x13, 0xe5, 0xaa},       {0x00, 0x0e, 0x61, 0xaa},
+       {0x00, 0x0f, 0x4b, 0xaa},       {0x00, 0x16, 0x02, 0xaa},
+       {0x00, 0x1e, 0x07, 0xaa},       {0x00, 0x21, 0x02, 0xaa},
+       {0x00, 0x22, 0x91, 0xaa},       {0x00, 0x29, 0x07, 0xaa},
+       {0x00, 0x33, 0x0b, 0xaa},       {0x00, 0x35, 0x0b, 0xaa},
+       {0x00, 0x37, 0x1d, 0xaa},       {0x00, 0x38, 0x71, 0xaa},
+       {0x00, 0x39, 0x2a, 0xaa},       {0x00, 0x3c, 0x78, 0xaa},
+       {0x00, 0x4d, 0x40, 0xaa},       {0x00, 0x4e, 0x20, 0xaa},
+       {0x00, 0x74, 0x19, 0xaa},       {0x00, 0x8d, 0x4f, 0xaa},
+       {0x00, 0x8e, 0x00, 0xaa},       {0x00, 0x8f, 0x00, 0xaa},
+       {0x00, 0x90, 0x00, 0xaa},       {0x00, 0x91, 0x00, 0xaa},
+       {0x00, 0x96, 0x00, 0xaa},       {0x00, 0x9a, 0x80, 0xaa},
+       {0x00, 0xb0, 0x84, 0xaa},       {0x00, 0xb1, 0x0c, 0xaa},
+       {0x00, 0xb2, 0x0e, 0xaa},       {0x00, 0xb3, 0x82, 0xaa},
+       {0x00, 0xb8, 0x0a, 0xaa},       {0x00, 0x43, 0x14, 0xaa},
+       {0x00, 0x44, 0xf0, 0xaa},       {0x00, 0x45, 0x45, 0xaa},
+       {0x00, 0x46, 0x63, 0xaa},       {0x00, 0x47, 0x2d, 0xaa},
+       {0x00, 0x48, 0x46, 0xaa},       {0x00, 0x59, 0x88, 0xaa},
+       {0x00, 0x5a, 0xa0, 0xaa},       {0x00, 0x5b, 0xc6, 0xaa},
+       {0x00, 0x5c, 0x7d, 0xaa},       {0x00, 0x5d, 0x5f, 0xaa},
+       {0x00, 0x5e, 0x19, 0xaa},       {0x00, 0x6c, 0x0a, 0xaa},
+       {0x00, 0x6d, 0x55, 0xaa},       {0x00, 0x6e, 0x11, 0xaa},
+       {0x00, 0x6f, 0x9e, 0xaa},       {0x00, 0x69, 0x00, 0xaa},
+       {0x00, 0x6a, 0x40, 0xaa},       {0x00, 0x01, 0x40, 0xaa},
+       {0x00, 0x02, 0x40, 0xaa},       {0x00, 0x13, 0xe7, 0xaa},
+       {0x00, 0x5f, 0xf0, 0xaa},       {0x00, 0x60, 0xf0, 0xaa},
+       {0x00, 0x61, 0xf0, 0xaa},       {0x00, 0x27, 0xa0, 0xaa},
+       {0x00, 0x28, 0x80, 0xaa},       {0x00, 0x2c, 0x90, 0xaa},
+       {0x00, 0x4f, 0x66, 0xaa},       {0x00, 0x50, 0x66, 0xaa},
+       {0x00, 0x51, 0x00, 0xaa},       {0x00, 0x52, 0x22, 0xaa},
+       {0x00, 0x53, 0x5e, 0xaa},       {0x00, 0x54, 0x80, 0xaa},
+       {0x00, 0x58, 0x9e, 0xaa},       {0x00, 0x41, 0x08, 0xaa},
+       {0x00, 0x3f, 0x00, 0xaa},       {0x00, 0x75, 0x85, 0xaa},
+       {0x00, 0x76, 0xe1, 0xaa},       {0x00, 0x4c, 0x00, 0xaa},
+       {0x00, 0x77, 0x0a, 0xaa},       {0x00, 0x3d, 0x88, 0xaa},
+       {0x00, 0x4b, 0x09, 0xaa},       {0x00, 0xc9, 0x60, 0xaa},
+       {0x00, 0x41, 0x38, 0xaa},       {0x00, 0x62, 0x30, 0xaa},
+       {0x00, 0x63, 0x30, 0xaa},       {0x00, 0x64, 0x08, 0xaa},
+       {0x00, 0x94, 0x07, 0xaa},       {0x00, 0x95, 0x0b, 0xaa},
+       {0x00, 0x65, 0x00, 0xaa},       {0x00, 0x66, 0x05, 0xaa},
+       {0x00, 0x56, 0x50, 0xaa},       {0x00, 0x34, 0x11, 0xaa},
+       {0x00, 0xa4, 0x88, 0xaa},       {0x00, 0x96, 0x00, 0xaa},
+       {0x00, 0x97, 0x30, 0xaa},       {0x00, 0x98, 0x20, 0xaa},
+       {0x00, 0x99, 0x30, 0xaa},       {0x00, 0x9a, 0x84, 0xaa},
+       {0x00, 0x9b, 0x29, 0xaa},       {0x00, 0x9c, 0x03, 0xaa},
+       {0x00, 0x78, 0x04, 0xaa},       {0x00, 0x79, 0x01, 0xaa},
+       {0x00, 0xc8, 0xf0, 0xaa},       {0x00, 0x79, 0x0f, 0xaa},
+       {0x00, 0xc8, 0x00, 0xaa},       {0x00, 0x79, 0x10, 0xaa},
+       {0x00, 0xc8, 0x7e, 0xaa},       {0x00, 0x79, 0x0a, 0xaa},
+       {0x00, 0xc8, 0x80, 0xaa},       {0x00, 0x79, 0x0b, 0xaa},
+       {0x00, 0xc8, 0x01, 0xaa},       {0x00, 0x79, 0x0c, 0xaa},
+       {0x00, 0xc8, 0x0f, 0xaa},       {0x00, 0x79, 0x0d, 0xaa},
+       {0x00, 0xc8, 0x20, 0xaa},       {0x00, 0x79, 0x09, 0xaa},
+       {0x00, 0xc8, 0x80, 0xaa},       {0x00, 0x79, 0x02, 0xaa},
+       {0x00, 0xc8, 0xc0, 0xaa},       {0x00, 0x79, 0x03, 0xaa},
+       {0x00, 0xc8, 0x40, 0xaa},       {0x00, 0x79, 0x05, 0xaa},
+       {0x00, 0xc8, 0x30, 0xaa},       {0x00, 0x79, 0x26, 0xaa},
+       {0x00, 0x11, 0x40, 0xaa},       {0x00, 0x3a, 0x04, 0xaa},
+       {0x00, 0x12, 0x00, 0xaa},       {0x00, 0x40, 0xc0, 0xaa},
+       {0x00, 0x8c, 0x00, 0xaa},       {0x00, 0x17, 0x14, 0xaa},
+       {0x00, 0x18, 0x02, 0xaa},       {0x00, 0x32, 0x92, 0xaa},
+       {0x00, 0x19, 0x02, 0xaa},       {0x00, 0x1a, 0x7a, 0xaa},
+       {0x00, 0x03, 0x0a, 0xaa},       {0x00, 0x0c, 0x00, 0xaa},
+       {0x00, 0x3e, 0x00, 0xaa},       {0x00, 0x70, 0x3a, 0xaa},
+       {0x00, 0x71, 0x35, 0xaa},       {0x00, 0x72, 0x11, 0xaa},
+       {0x00, 0x73, 0xf0, 0xaa},       {0x00, 0xa2, 0x02, 0xaa},
+       {0x00, 0xb1, 0x00, 0xaa},       {0x00, 0xb1, 0x0c, 0xaa},
+       {0x00, 0x1e, 0x37, 0xaa},       {0x00, 0xaa, 0x14, 0xaa},
+       {0x00, 0x24, 0x80, 0xaa},       {0x00, 0x25, 0x74, 0xaa},
+       {0x00, 0x26, 0xd3, 0xaa},       {0x00, 0x0d, 0x00, 0xaa},
+       {0x00, 0x14, 0x18, 0xaa},       {0x00, 0x9d, 0x99, 0xaa},
+       {0x00, 0x9e, 0x7f, 0xaa},       {0x00, 0x64, 0x08, 0xaa},
+       {0x00, 0x94, 0x07, 0xaa},       {0x00, 0x95, 0x06, 0xaa},
+       {0x00, 0x66, 0x05, 0xaa},       {0x00, 0x41, 0x08, 0xaa},
+       {0x00, 0x3f, 0x00, 0xaa},       {0x00, 0x75, 0x07, 0xaa},
+       {0x00, 0x76, 0xe1, 0xaa},       {0x00, 0x4c, 0x00, 0xaa},
+       {0x00, 0x77, 0x00, 0xaa},       {0x00, 0x3d, 0xc2, 0xaa},
+       {0x00, 0x4b, 0x09, 0xaa},       {0x00, 0xc9, 0x60, 0xaa},
+       {0x00, 0x41, 0x38, 0xaa},       {0xb6, 0x00, 0x00, 0xcc},
+       {0xb6, 0x03, 0x01, 0xcc},       {0xb6, 0x02, 0x40, 0xcc},
+       {0xb6, 0x05, 0x00, 0xcc},       {0xb6, 0x04, 0xf0, 0xcc},
+       {0xb6, 0x12, 0xf8, 0xcc},       {0xb6, 0x13, 0x21, 0xcc},
+       {0xb6, 0x18, 0x00, 0xcc},       {0xb6, 0x17, 0x96, 0xcc},
+       {0xb6, 0x16, 0x00, 0xcc},       {0xb6, 0x22, 0x12, 0xcc},
+       {0xb6, 0x23, 0x0b, 0xcc},       {0xbf, 0xc0, 0x39, 0xcc},
+       {0xbf, 0xc1, 0x04, 0xcc},       {0xbf, 0xcc, 0x00, 0xcc},
+       {0xbc, 0x02, 0x18, 0xcc},       {0xbc, 0x03, 0x50, 0xcc},
+       {0xbc, 0x04, 0x18, 0xcc},       {0xbc, 0x05, 0x00, 0xcc},
+       {0xbc, 0x06, 0x00, 0xcc},       {0xbc, 0x08, 0x30, 0xcc},
+       {0xbc, 0x09, 0x40, 0xcc},       {0xbc, 0x0a, 0x10, 0xcc},
+       {0xbc, 0x0b, 0x00, 0xcc},       {0xbc, 0x0c, 0x00, 0xcc},
+       {0xb3, 0x5c, 0x01, 0xcc},       {0xb3, 0x01, 0x45, 0xcc},
+       {0x00, 0x77, 0x05, 0xaa },
+       {},
+};
+
+struct sensor_info {
+       int sensorId;
+       __u8 I2cAdd;
+       __u8 IdAdd;
+       __u16 VpId;
+       __u8 m1;
+       __u8 m2;
+       __u8 op;
+       };
+
+static const struct sensor_info sensor_info_data[] = {
+/*      sensorId,         I2cAdd,      IdAdd,  VpId,  m1,    m2,  op */
+       {SENSOR_HV7131R,    0x80 | 0x11, 0x00, 0x0209, 0x24, 0x25, 0x01},
+       {SENSOR_OV7660,     0x80 | 0x21, 0x0a, 0x7660, 0x26, 0x26, 0x05},
+       {SENSOR_PO3130NC,   0x80 | 0x76, 0x00, 0x3130, 0x24, 0x25, 0x01},
+       {SENSOR_MI1320,     0x80 | 0xc8, 0x00, 0x148c, 0x64, 0x65, 0x01},
+       {SENSOR_OV7670,     0x80 | 0x21, 0x0a, 0x7673, 0x66, 0x67, 0x05},
+       {SENSOR_MI1310_SOC, 0x80 | 0x5d, 0x00, 0x143a, 0x24, 0x25, 0x01},
+};
+
+/* read 'len' bytes in gspca_dev->usb_buf */
+static void reg_r(struct gspca_dev *gspca_dev,
+                 __u16 req,
+                 __u16 index,
+                 __u16 len)
+{
+       usb_control_msg(gspca_dev->dev,
+                       usb_rcvctrlpipe(gspca_dev->dev, 0),
+                       req,
+                       USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                       1,                      /* value */
+                       index, gspca_dev->usb_buf, len,
+                       500);
+}
+
+static void reg_w(struct usb_device *dev,
+                           __u16 req,
+                           __u16 value,
+                           __u16 index)
+{
+       usb_control_msg(dev,
+                       usb_sndctrlpipe(dev, 0),
+                       req,
+                       USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                       value, index, NULL, 0,
+                       500);
+}
+
+static void read_sensor_register(struct gspca_dev *gspca_dev,
+                               __u16 address, __u16 *value)
+{
+       struct usb_device *dev = gspca_dev->dev;
+       __u8 ldata, mdata, hdata;
+       int retry = 50;
+
+       *value = 0;
+
+       reg_r(gspca_dev, 0xa1, 0xb33f, 1);
+       /*PDEBUG(D_PROBE, " I2c Bus Busy Wait  0x%02X ", tmpvalue); */
+       if (!(gspca_dev->usb_buf[0] & 0x02)) {
+               PDEBUG(D_ERR, "I2c Bus Busy Wait %d",
+                       gspca_dev->usb_buf[0] & 0x02);
+               return;
+       }
+       reg_w(dev, 0xa0, address, 0xb33a);
+       reg_w(dev, 0xa0, 0x02, 0xb339);
+
+       reg_r(gspca_dev, 0xa1, 0xb33b, 1);
+       while (retry-- && gspca_dev->usb_buf[0]) {
+               reg_r(gspca_dev, 0xa1, 0xb33b, 1);
+/*             PDEBUG(D_PROBE, "Read again 0xb33b %d", tmpvalue); */
+               msleep(1);
+       }
+       reg_r(gspca_dev, 0xa1, 0xb33e, 1);
+       hdata = gspca_dev->usb_buf[0];
+       reg_r(gspca_dev, 0xa1, 0xb33d, 1);
+       mdata = gspca_dev->usb_buf[0];
+       reg_r(gspca_dev, 0xa1, 0xb33c, 1);
+       ldata = gspca_dev->usb_buf[0];
+       PDEBUG(D_PROBE, "Read Sensor h (0x%02X) m (0x%02X) l (0x%02X)",
+               hdata, mdata, ldata);
+       reg_r(gspca_dev, 0xa1, 0xb334, 1);
+       if (gspca_dev->usb_buf[0] == 0x02)
+               *value = (ldata << 8) + mdata;
+       else
+               *value = ldata;
+}
+
+static int vc032x_probe_sensor(struct gspca_dev *gspca_dev)
+{
+       struct usb_device *dev = gspca_dev->dev;
+       int i;
+       __u16 value;
+       const struct sensor_info *ptsensor_info;
+
+       reg_r(gspca_dev, 0xa1, 0xbfcf, 1);
+       PDEBUG(D_PROBE, "check sensor header %d", gspca_dev->usb_buf[0]);
+       for (i = 0; i < ARRAY_SIZE(sensor_info_data); i++) {
+               ptsensor_info = &sensor_info_data[i];
+               reg_w(dev, 0xa0, 0x02, 0xb334);
+               reg_w(dev, 0xa0, ptsensor_info->m1, 0xb300);
+               reg_w(dev, 0xa0, ptsensor_info->m2, 0xb300);
+               reg_w(dev, 0xa0, 0x01, 0xb308);
+               reg_w(dev, 0xa0, 0x0c, 0xb309);
+               reg_w(dev, 0xa0, ptsensor_info->I2cAdd, 0xb335);
+/*             PDEBUG(D_PROBE,
+                       "check sensor VC032X -> %d Add -> ox%02X!",
+                       i, ptsensor_info->I2cAdd); */
+               reg_w(dev, 0xa0, ptsensor_info->op, 0xb301);
+               read_sensor_register(gspca_dev, ptsensor_info->IdAdd, &value);
+               if (value == ptsensor_info->VpId) {
+/*                     PDEBUG(D_PROBE, "find sensor VC032X -> ox%04X!",
+                               ptsensor_info->VpId); */
+                       return ptsensor_info->sensorId;
+               }
+       }
+       return -1;
+}
+
+static __u8 i2c_write(struct gspca_dev *gspca_dev,
+                       __u8 reg, const __u8 *val, __u8 size)
+{
+       struct usb_device *dev = gspca_dev->dev;
+
+       if (size > 3 || size < 1)
+               return -EINVAL;
+       reg_r(gspca_dev, 0xa1, 0xb33f, 1);
+       reg_w(dev, 0xa0, size, 0xb334);
+       reg_w(dev, 0xa0, reg, 0xb33a);
+       switch (size) {
+       case 1:
+               reg_w(dev, 0xa0, val[0], 0xb336);
+               break;
+       case 2:
+               reg_w(dev, 0xa0, val[0], 0xb336);
+               reg_w(dev, 0xa0, val[1], 0xb337);
+               break;
+       case 3:
+               reg_w(dev, 0xa0, val[0], 0xb336);
+               reg_w(dev, 0xa0, val[1], 0xb337);
+               reg_w(dev, 0xa0, val[2], 0xb338);
+               break;
+       default:
+               reg_w(dev, 0xa0, 0x01, 0xb334);
+               return -EINVAL;
+       }
+       reg_w(dev, 0xa0, 0x01, 0xb339);
+       reg_r(gspca_dev, 0xa1, 0xb33b, 1);
+       return gspca_dev->usb_buf[0] == 0;
+}
+
+static void put_tab_to_reg(struct gspca_dev *gspca_dev,
+                       const __u8 *tab, __u8 tabsize, __u16 addr)
+{
+       int j;
+       __u16 ad = addr;
+
+       for (j = 0; j < tabsize; j++)
+               reg_w(gspca_dev->dev, 0xa0, tab[j], ad++);
+}
+
+static void usb_exchange(struct gspca_dev *gspca_dev,
+                       const __u8 data[][4])
+{
+       struct usb_device *dev = gspca_dev->dev;
+       int i = 0;
+
+       for (;;) {
+               switch (data[i][3]) {
+               default:
+                       return;
+               case 0xcc:                      /* normal write */
+                       reg_w(dev, 0xa0, data[i][2],
+                                       ((data[i][0])<<8) | data[i][1]);
+                       break;
+               case 0xaa:                      /* i2c op */
+                       i2c_write(gspca_dev, data[i][1], &data[i][2], 1);
+                       break;
+               case 0xbb:                      /* i2c op */
+                       i2c_write(gspca_dev, data[i][0], &data[i][1], 2);
+                       break;
+               case 0xdd:
+                       msleep(data[i][2] + 10);
+                       break;
+               }
+               i++;
+       }
+       /*not reached*/
+}
+
+/*
+ "GammaT"=hex:04,17,31,4f,6a,83,99,ad,bf,ce,da,e5,ee,f5,fb,ff,ff
+ "MatrixT"=hex:60,f9,e5,e7,50,05,f3,e6,66
+ */
+
+static void vc0321_reset(struct gspca_dev *gspca_dev)
+{
+       reg_w(gspca_dev->dev, 0xa0, 0x00, 0xb04d);
+       reg_w(gspca_dev->dev, 0xa0, 0x01, 0xb301);
+       msleep(100);
+       reg_w(gspca_dev->dev, 0xa0, 0x01, 0xb003);
+       msleep(100);
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+                       const struct usb_device_id *id)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       struct usb_device *dev = gspca_dev->dev;
+       struct cam *cam;
+       int sensor;
+       __u16 product;
+
+       product = id->idProduct;
+       sd->bridge = BRIDGE_VC0321;
+       switch (id->idVendor) {
+       case 0x0ac8:            /* Vimicro z-star */
+               switch (product) {
+               case 0x0323:
+                       sd->bridge = BRIDGE_VC0323;
+                       break;
+               }
+               break;
+       case 0x17ef:            /* Lenovo */
+/*             switch (product) { */
+/*             case 0x4802:     * Lenovo MI1310_SOC */
+                       sd->bridge = BRIDGE_VC0323;
+/*                     break; */
+/*             } */
+               break;
+       }
+
+       cam = &gspca_dev->cam;
+       cam->dev_name = (char *) id->driver_info;
+       cam->epaddr = 0x02;
+       if (sd->bridge == BRIDGE_VC0321) {
+               cam->cam_mode = vc0321_mode;
+               cam->nmodes = ARRAY_SIZE(vc0321_mode);
+       } else {
+               cam->cam_mode = vc0323_mode;
+               cam->nmodes = ARRAY_SIZE(vc0323_mode);
+       }
+
+       vc0321_reset(gspca_dev);
+       sensor = vc032x_probe_sensor(gspca_dev);
+       switch (sensor) {
+       case -1:
+               PDEBUG(D_PROBE, "Unknown sensor...");
+               return -EINVAL;
+       case SENSOR_HV7131R:
+               PDEBUG(D_PROBE, "Find Sensor HV7131R");
+               sd->sensor = SENSOR_HV7131R;
+               break;
+       case SENSOR_MI1310_SOC:
+               PDEBUG(D_PROBE, "Find Sensor MI1310_SOC");
+               sd->sensor = SENSOR_MI1310_SOC;
+               break;
+       case SENSOR_MI1320:
+               PDEBUG(D_PROBE, "Find Sensor MI1320");
+               sd->sensor = SENSOR_MI1320;
+               break;
+       case SENSOR_OV7660:
+               PDEBUG(D_PROBE, "Find Sensor OV7660");
+               sd->sensor = SENSOR_OV7660;
+               break;
+       case SENSOR_OV7670:
+               PDEBUG(D_PROBE, "Find Sensor OV7670");
+               sd->sensor = SENSOR_OV7670;
+               break;
+       case SENSOR_PO3130NC:
+               PDEBUG(D_PROBE, "Find Sensor PO3130NC");
+               sd->sensor = SENSOR_PO3130NC;
+               break;
+       }
+
+       sd->qindex = 7;
+       sd->autogain = AUTOGAIN_DEF;
+       sd->lightfreq = FREQ_DEF;
+
+       if (sd->bridge == BRIDGE_VC0321) {
+               reg_r(gspca_dev, 0x8a, 0, 3);
+               reg_w(dev, 0x87, 0x00, 0x0f0f);
+
+               reg_r(gspca_dev, 0x8b, 0, 3);
+               reg_w(dev, 0x88, 0x00, 0x0202);
+       }
+       return 0;
+}
+
+/* this function is called at open time */
+static int sd_open(struct gspca_dev *gspca_dev)
+{
+       return 0;
+}
+
+static void setquality(struct gspca_dev *gspca_dev)
+{
+}
+
+static void setautogain(struct gspca_dev *gspca_dev)
+{
+}
+
+static void setlightfreq(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       static const __u8 (*ov7660_freq_tb[3])[4] =
+               {ov7660_NoFliker, ov7660_50HZ, ov7660_60HZ};
+
+       if (sd->sensor != SENSOR_OV7660)
+               return;
+       usb_exchange(gspca_dev, ov7660_freq_tb[sd->lightfreq]);
+}
+
+static void sd_start(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       const __u8 *GammaT = NULL;
+       const __u8 *MatrixT = NULL;
+       int mode;
+
+       /* Assume start use the good resolution from gspca_dev->mode */
+       if (sd->bridge == BRIDGE_VC0321) {
+               reg_w(gspca_dev->dev, 0xa0, 0xff, 0xbfec);
+               reg_w(gspca_dev->dev, 0xa0, 0xff, 0xbfed);
+               reg_w(gspca_dev->dev, 0xa0, 0xff, 0xbfee);
+               reg_w(gspca_dev->dev, 0xa0, 0xff, 0xbfef);
+       }
+
+       mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;
+       switch (sd->sensor) {
+       case SENSOR_HV7131R:
+               GammaT = hv7131r_gamma;
+               MatrixT = hv7131r_matrix;
+               if (mode) {
+                       /* 320x240 */
+                       usb_exchange(gspca_dev, hv7131r_initQVGA_data);
+               } else {
+                       /* 640x480 */
+                       usb_exchange(gspca_dev, hv7131r_initVGA_data);
+               }
+               break;
+       case SENSOR_OV7660:
+               GammaT = ov7660_gamma;
+               MatrixT = ov7660_matrix;
+               if (mode) {
+                       /* 320x240 */
+                       usb_exchange(gspca_dev, ov7660_initQVGA_data);
+               } else {
+                       /* 640x480 */
+                       usb_exchange(gspca_dev, ov7660_initVGA_data);
+               }
+               break;
+       case SENSOR_OV7670:
+               /*GammaT = ov7660_gamma; */
+               /*MatrixT = ov7660_matrix; */
+               if (mode) {
+                       /* 320x240 */
+                       usb_exchange(gspca_dev, ov7670_initQVGA_JPG);
+               } else {
+                       /* 640x480 */
+                       usb_exchange(gspca_dev, ov7670_initVGA_JPG);
+               }
+               break;
+       case SENSOR_MI1310_SOC:
+               if (mode) {
+                       /* 320x240 */
+                       usb_exchange(gspca_dev, mi1310_socinitQVGA_JPG);
+               } else {
+                       /* 640x480 */
+                       usb_exchange(gspca_dev, mi1310_socinitVGA_JPG);
+               }
+               break;
+       case SENSOR_MI1320:
+               GammaT = mi1320_gamma;
+               MatrixT = mi1320_matrix;
+               if (mode) {
+                       /* 320x240 */
+                       usb_exchange(gspca_dev, mi1320_initQVGA_data);
+               } else {
+                       /* 640x480 */
+                       usb_exchange(gspca_dev, mi1320_initVGA_data);
+               }
+               break;
+       case SENSOR_PO3130NC:
+               GammaT = po3130_gamma;
+               MatrixT = po3130_matrix;
+               if (mode) {
+                       /* 320x240 */
+                       usb_exchange(gspca_dev, po3130_initQVGA_data);
+               } else {
+                       /* 640x480 */
+                       usb_exchange(gspca_dev, po3130_initVGA_data);
+               }
+               usb_exchange(gspca_dev, po3130_rundata);
+               break;
+       default:
+               PDEBUG(D_PROBE, "Damned !! no sensor found Bye");
+               return;
+       }
+       if (GammaT && MatrixT) {
+               put_tab_to_reg(gspca_dev, GammaT, 17, 0xb84a);
+               put_tab_to_reg(gspca_dev, GammaT, 17, 0xb85b);
+               put_tab_to_reg(gspca_dev, GammaT, 17, 0xb86c);
+               put_tab_to_reg(gspca_dev, MatrixT, 9, 0xb82c);
+
+               /* Seem SHARPNESS */
+               /*
+               reg_w(gspca_dev->dev, 0xa0, 0x80, 0xb80a);
+               reg_w(gspca_dev->dev, 0xa0, 0xff, 0xb80b);
+               reg_w(gspca_dev->dev, 0xa0, 0xff, 0xb80e);
+               */
+               /* all 0x40 ??? do nothing
+               reg_w(gspca_dev->dev, 0xa0, 0x40, 0xb822);
+               reg_w(gspca_dev->dev, 0xa0, 0x40, 0xb823);
+               reg_w(gspca_dev->dev, 0xa0, 0x40, 0xb824);
+               */
+               /* Only works for HV7131R ??
+               reg_r (gspca_dev, 0xa1, 0xb881, 1);
+               reg_w(gspca_dev->dev, 0xa0, 0xfe01, 0xb881);
+               reg_w(gspca_dev->dev, 0xa0, 0x79, 0xb801);
+               */
+               /* only hv7131r et ov7660
+               reg_w(gspca_dev->dev, 0xa0, 0x20, 0xb827);
+               reg_w(gspca_dev->dev, 0xa0, 0xff, 0xb826); * ISP_GAIN 80
+               reg_w(gspca_dev->dev, 0xa0, 0x23, 0xb800); * ISP CTRL_BAS
+               */
+               /* set the led on 0x0892 0x0896 */
+               reg_w(gspca_dev->dev, 0x89, 0xffff, 0xfdff);
+               msleep(100);
+               setquality(gspca_dev);
+               setautogain(gspca_dev);
+               setlightfreq(gspca_dev);
+       }
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+       struct usb_device *dev = gspca_dev->dev;
+
+       reg_w(dev, 0x89, 0xffff, 0xffff);
+       reg_w(dev, 0xa0, 0x01, 0xb301);
+       reg_w(dev, 0xa0, 0x09, 0xb003);
+}
+
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+       struct usb_device *dev = gspca_dev->dev;
+
+       reg_w(dev, 0x89, 0xffff, 0xffff);
+}
+
+/* this function is called at close time */
+static void sd_close(struct gspca_dev *gspca_dev)
+{
+/*     struct usb_device *dev = gspca_dev->dev;
+       __u8 buffread;
+
+       reg_w(dev, 0x89, 0xffff, 0xffff);
+       reg_w(dev, 0xa0, 0x01, 0xb301);
+       reg_w(dev, 0xa0, 0x09, 0xb303);
+       reg_w(dev, 0x89, 0xffff, 0xffff);
+*/
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+                       struct gspca_frame *frame,      /* target */
+                       __u8 *data,                     /* isoc packet */
+                       int len)                        /* iso pkt length */
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       if (data[0] == 0xff && data[1] == 0xd8) {
+               PDEBUG(D_PACK,
+                       "vc032x header packet found len %d", len);
+               frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
+                                               data, 0);
+               if (sd->bridge == BRIDGE_VC0321) {
+#define VCHDRSZ 46
+                       data += VCHDRSZ;
+                       len -= VCHDRSZ;
+#undef VCHDRSZ
+               }
+               gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
+                               data, len);
+               return;
+       }
+       gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
+}
+
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->autogain = val;
+       if (gspca_dev->streaming)
+               setautogain(gspca_dev);
+       return 0;
+}
+
+static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->autogain;
+       return 0;
+}
+
+static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->lightfreq = val;
+       if (gspca_dev->streaming)
+               setlightfreq(gspca_dev);
+       return 0;
+}
+
+static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->lightfreq;
+       return 0;
+}
+
+static int sd_querymenu(struct gspca_dev *gspca_dev,
+                       struct v4l2_querymenu *menu)
+{
+       switch (menu->id) {
+       case V4L2_CID_POWER_LINE_FREQUENCY:
+               switch (menu->index) {
+               case 0:         /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */
+                       strcpy((char *) menu->name, "NoFliker");
+                       return 0;
+               case 1:         /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */
+                       strcpy((char *) menu->name, "50 Hz");
+                       return 0;
+               case 2:         /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */
+                       strcpy((char *) menu->name, "60 Hz");
+                       return 0;
+               }
+               break;
+       }
+       return -EINVAL;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+       .name = MODULE_NAME,
+       .ctrls = sd_ctrls,
+       .nctrls = ARRAY_SIZE(sd_ctrls),
+       .config = sd_config,
+       .open = sd_open,
+       .start = sd_start,
+       .stopN = sd_stopN,
+       .stop0 = sd_stop0,
+       .close = sd_close,
+       .pkt_scan = sd_pkt_scan,
+       .querymenu = sd_querymenu,
+};
+
+/* -- module initialisation -- */
+#define DVNM(name) .driver_info = (kernel_ulong_t) name
+static const __devinitdata struct usb_device_id device_table[] = {
+       {USB_DEVICE(0x046d, 0x0892), DVNM("Logitech Orbicam")},
+       {USB_DEVICE(0x046d, 0x0896), DVNM("Logitech Orbicam")},
+       {USB_DEVICE(0x0ac8, 0x0321), DVNM("Vimicro generic vc0321")},
+       {USB_DEVICE(0x0ac8, 0x0323), DVNM("Vimicro Vc0323")},
+       {USB_DEVICE(0x0ac8, 0x0328), DVNM("A4Tech PK-130MG")},
+       {USB_DEVICE(0x0ac8, 0xc001), DVNM("Sony embedded vimicro")},
+       {USB_DEVICE(0x0ac8, 0xc002), DVNM("Sony embedded vimicro")},
+       {USB_DEVICE(0x17ef, 0x4802), DVNM("Lenovo Vc0323+MI1310_SOC")},
+       {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+                       const struct usb_device_id *id)
+{
+       return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+                               THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+       .name = MODULE_NAME,
+       .id_table = device_table,
+       .probe = sd_probe,
+       .disconnect = gspca_disconnect,
+};
+
+/* -- module insert / remove -- */
+static int __init sd_mod_init(void)
+{
+       if (usb_register(&sd_driver) < 0)
+               return -1;
+       PDEBUG(D_PROBE, "v%s registered", version);
+       return 0;
+}
+static void __exit sd_mod_exit(void)
+{
+       usb_deregister(&sd_driver);
+       PDEBUG(D_PROBE, "deregistered");
+}
+
+module_init(sd_mod_init);
+module_exit(sd_mod_exit);
diff --git a/drivers/media/video/gspca/zc3xx-reg.h b/drivers/media/video/gspca/zc3xx-reg.h
new file mode 100644 (file)
index 0000000..f52e09c
--- /dev/null
@@ -0,0 +1,261 @@
+/*
+ * zc030x registers
+ *
+ * Copyright (c) 2008 Mauro Carvalho Chehab <mchehab@infradead.org>
+ *
+ * The register aliases used here came from this driver:
+ *     http://zc0302.sourceforge.net/zc0302.php
+ *
+ * This code is placed under the terms of the GNU General Public License v2
+ */
+
+/* Define the register map */
+#define ZC3XX_R000_SYSTEMCONTROL       0x0000
+#define ZC3XX_R001_SYSTEMOPERATING     0x0001
+
+/* Picture size */
+#define ZC3XX_R002_CLOCKSELECT         0x0002
+#define ZC3XX_R003_FRAMEWIDTHHIGH      0x0003
+#define ZC3XX_R004_FRAMEWIDTHLOW       0x0004
+#define ZC3XX_R005_FRAMEHEIGHTHIGH     0x0005
+#define ZC3XX_R006_FRAMEHEIGHTLOW      0x0006
+
+/* JPEG control */
+#define ZC3XX_R008_CLOCKSETTING        0x0008
+
+/* Test mode */
+#define ZC3XX_R00B_TESTMODECONTROL     0x000b
+
+/* Frame retreiving */
+#define ZC3XX_R00C_LASTACQTIME         0x000c
+#define ZC3XX_R00D_MONITORRES          0x000d
+#define ZC3XX_R00E_TIMESTAMPHIGH       0x000e
+#define ZC3XX_R00F_TIMESTAMPLOW        0x000f
+#define ZC3XX_R018_FRAMELOST           0x0018
+#define ZC3XX_R019_AUTOADJUSTFPS       0x0019
+#define ZC3XX_R01A_LASTFRAMESTATE      0x001a
+#define ZC3XX_R025_DATACOUNTER         0x0025
+
+/* Stream and sensor specific */
+#define ZC3XX_R010_CMOSSENSORSELECT    0x0010
+#define ZC3XX_R011_VIDEOSTATUS         0x0011
+#define ZC3XX_R012_VIDEOCONTROLFUNC    0x0012
+
+/* Horizontal and vertical synchros */
+#define ZC3XX_R01D_HSYNC_0             0x001d
+#define ZC3XX_R01E_HSYNC_1             0x001e
+#define ZC3XX_R01F_HSYNC_2             0x001f
+#define ZC3XX_R020_HSYNC_3             0x0020
+
+/* Target picture size in byte */
+#define ZC3XX_R022_TARGETPICTSIZE_0    0x0022
+#define ZC3XX_R023_TARGETPICTSIZE_1    0x0023
+#define ZC3XX_R024_TARGETPICTSIZE_2    0x0024
+
+/* Audio registers */
+#define ZC3XX_R030_AUDIOADC            0x0030
+#define ZC3XX_R031_AUDIOSTREAMSTATUS   0x0031
+#define ZC3XX_R032_AUDIOSTATUS         0x0032
+
+/* Sensor interface */
+#define ZC3XX_R080_HBLANKHIGH          0x0080
+#define ZC3XX_R081_HBLANKLOW           0x0081
+#define ZC3XX_R082_RESETLEVELADDR      0x0082
+#define ZC3XX_R083_RGAINADDR           0x0083
+#define ZC3XX_R084_GGAINADDR           0x0084
+#define ZC3XX_R085_BGAINADDR           0x0085
+#define ZC3XX_R086_EXPTIMEHIGH         0x0086
+#define ZC3XX_R087_EXPTIMEMID          0x0087
+#define ZC3XX_R088_EXPTIMELOW          0x0088
+#define ZC3XX_R089_RESETBLACKHIGH      0x0089
+#define ZC3XX_R08A_RESETWHITEHIGH      0x008a
+#define ZC3XX_R08B_I2CDEVICEADDR       0x008b
+#define ZC3XX_R08C_I2CIDLEANDNACK      0x008c
+#define ZC3XX_R08D_COMPABILITYMODE     0x008d
+#define ZC3XX_R08E_COMPABILITYMODE2    0x008e
+
+/* I2C control */
+#define ZC3XX_R090_I2CCOMMAND          0x0090
+#define ZC3XX_R091_I2CSTATUS           0x0091
+#define ZC3XX_R092_I2CADDRESSSELECT    0x0092
+#define ZC3XX_R093_I2CSETVALUE         0x0093
+#define ZC3XX_R094_I2CWRITEACK         0x0094
+#define ZC3XX_R095_I2CREAD             0x0095
+#define ZC3XX_R096_I2CREADACK          0x0096
+
+/* Window inside the sensor array */
+#define ZC3XX_R097_WINYSTARTHIGH       0x0097
+#define ZC3XX_R098_WINYSTARTLOW        0x0098
+#define ZC3XX_R099_WINXSTARTHIGH       0x0099
+#define ZC3XX_R09A_WINXSTARTLOW        0x009a
+#define ZC3XX_R09B_WINHEIGHTHIGH       0x009b
+#define ZC3XX_R09C_WINHEIGHTLOW        0x009c
+#define ZC3XX_R09D_WINWIDTHHIGH        0x009d
+#define ZC3XX_R09E_WINWIDTHLOW         0x009e
+#define ZC3XX_R119_FIRSTYHIGH          0x0119
+#define ZC3XX_R11A_FIRSTYLOW           0x011a
+#define ZC3XX_R11B_FIRSTXHIGH          0x011b
+#define ZC3XX_R11C_FIRSTXLOW           0x011c
+
+/* Max sensor array size */
+#define ZC3XX_R09F_MAXXHIGH            0x009f
+#define ZC3XX_R0A0_MAXXLOW             0x00a0
+#define ZC3XX_R0A1_MAXYHIGH            0x00a1
+#define ZC3XX_R0A2_MAXYLOW             0x00a2
+#define ZC3XX_R0A3_EXPOSURETIMEHIGH    0x00a3
+#define ZC3XX_R0A4_EXPOSURETIMELOW     0x00a4
+#define ZC3XX_R0A5_EXPOSUREGAIN        0x00a5
+#define ZC3XX_R0A6_EXPOSUREBLACKLVL    0x00a6
+
+/* Other registers */
+#define ZC3XX_R100_OPERATIONMODE       0x0100
+#define ZC3XX_R101_SENSORCORRECTION    0x0101
+
+/* Gains */
+#define ZC3XX_R116_RGAIN               0x0116
+#define ZC3XX_R117_GGAIN               0x0117
+#define ZC3XX_R118_BGAIN               0x0118
+#define ZC3XX_R11D_GLOBALGAIN          0x011d
+#define ZC3XX_R1A8_DIGITALGAIN         0x01a8
+#define ZC3XX_R1A9_DIGITALLIMITDIFF    0x01a9
+#define ZC3XX_R1AA_DIGITALGAINSTEP     0x01aa
+
+/* Auto correction */
+#define ZC3XX_R180_AUTOCORRECTENABLE   0x0180
+#define ZC3XX_R181_WINXSTART           0x0181
+#define ZC3XX_R182_WINXWIDTH           0x0182
+#define ZC3XX_R183_WINXCENTER          0x0183
+#define ZC3XX_R184_WINYSTART           0x0184
+#define ZC3XX_R185_WINYWIDTH           0x0185
+#define ZC3XX_R186_WINYCENTER          0x0186
+
+/* Gain range */
+#define ZC3XX_R187_MAXGAIN             0x0187
+#define ZC3XX_R188_MINGAIN             0x0188
+
+/* Auto exposure and white balance */
+#define ZC3XX_R189_AWBSTATUS           0x0189
+#define ZC3XX_R18A_AWBFREEZE           0x018a
+#define ZC3XX_R18B_AESTATUS            0x018b
+#define ZC3XX_R18C_AEFREEZE            0x018c
+#define ZC3XX_R18F_AEUNFREEZE          0x018f
+#define ZC3XX_R190_EXPOSURELIMITHIGH   0x0190
+#define ZC3XX_R191_EXPOSURELIMITMID    0x0191
+#define ZC3XX_R192_EXPOSURELIMITLOW    0x0192
+#define ZC3XX_R195_ANTIFLICKERHIGH     0x0195
+#define ZC3XX_R196_ANTIFLICKERMID      0x0196
+#define ZC3XX_R197_ANTIFLICKERLOW      0x0197
+
+/* What is this ? */
+#define ZC3XX_R18D_YTARGET             0x018d
+#define ZC3XX_R18E_RESETLVL            0x018e
+
+/* Color */
+#define ZC3XX_R1A0_REDMEANAFTERAGC     0x01a0
+#define ZC3XX_R1A1_GREENMEANAFTERAGC   0x01a1
+#define ZC3XX_R1A2_BLUEMEANAFTERAGC    0x01a2
+#define ZC3XX_R1A3_REDMEANAFTERAWB     0x01a3
+#define ZC3XX_R1A4_GREENMEANAFTERAWB   0x01a4
+#define ZC3XX_R1A5_BLUEMEANAFTERAWB    0x01a5
+#define ZC3XX_R1A6_YMEANAFTERAE        0x01a6
+#define ZC3XX_R1A7_CALCGLOBALMEAN      0x01a7
+
+#define ZC3XX_R1A2_BLUEMEANAFTERAGC    0x01a2
+
+/* Matrixes */
+
+/* Color matrix is like :
+   R' = R * RGB00 + G * RGB01 + B * RGB02 + RGB03
+   G' = R * RGB10 + G * RGB11 + B * RGB22 + RGB13
+   B' = R * RGB20 + G * RGB21 + B * RGB12 + RGB23
+ */
+#define ZC3XX_R10A_RGB00               0x010a
+#define ZC3XX_R10B_RGB01               0x010b
+#define ZC3XX_R10C_RGB02               0x010c
+#define ZC3XX_R113_RGB03               0x0113
+#define ZC3XX_R10D_RGB10               0x010d
+#define ZC3XX_R10E_RGB11               0x010e
+#define ZC3XX_R10F_RGB12               0x010f
+#define ZC3XX_R114_RGB13               0x0114
+#define ZC3XX_R110_RGB20               0x0110
+#define ZC3XX_R111_RGB21               0x0111
+#define ZC3XX_R112_RGB22               0x0112
+#define ZC3XX_R115_RGB23               0x0115
+
+/* Gamma matrix */
+#define ZC3XX_R120_GAMMA00             0x0120
+#define ZC3XX_R121_GAMMA01             0x0121
+#define ZC3XX_R122_GAMMA02             0x0122
+#define ZC3XX_R123_GAMMA03             0x0123
+#define ZC3XX_R124_GAMMA04             0x0124
+#define ZC3XX_R125_GAMMA05             0x0125
+#define ZC3XX_R126_GAMMA06             0x0126
+#define ZC3XX_R127_GAMMA07             0x0127
+#define ZC3XX_R128_GAMMA08             0x0128
+#define ZC3XX_R129_GAMMA09             0x0129
+#define ZC3XX_R12A_GAMMA0A             0x012a
+#define ZC3XX_R12B_GAMMA0B             0x012b
+#define ZC3XX_R12C_GAMMA0C             0x012c
+#define ZC3XX_R12D_GAMMA0D             0x012d
+#define ZC3XX_R12E_GAMMA0E             0x012e
+#define ZC3XX_R12F_GAMMA0F             0x012f
+#define ZC3XX_R130_GAMMA10             0x0130
+#define ZC3XX_R131_GAMMA11             0x0131
+#define ZC3XX_R132_GAMMA12             0x0132
+#define ZC3XX_R133_GAMMA13             0x0133
+#define ZC3XX_R134_GAMMA14             0x0134
+#define ZC3XX_R135_GAMMA15             0x0135
+#define ZC3XX_R136_GAMMA16             0x0136
+#define ZC3XX_R137_GAMMA17             0x0137
+#define ZC3XX_R138_GAMMA18             0x0138
+#define ZC3XX_R139_GAMMA19             0x0139
+#define ZC3XX_R13A_GAMMA1A             0x013a
+#define ZC3XX_R13B_GAMMA1B             0x013b
+#define ZC3XX_R13C_GAMMA1C             0x013c
+#define ZC3XX_R13D_GAMMA1D             0x013d
+#define ZC3XX_R13E_GAMMA1E             0x013e
+#define ZC3XX_R13F_GAMMA1F             0x013f
+
+/* Luminance gamma */
+#define ZC3XX_R140_YGAMMA00            0x0140
+#define ZC3XX_R141_YGAMMA01            0x0141
+#define ZC3XX_R142_YGAMMA02            0x0142
+#define ZC3XX_R143_YGAMMA03            0x0143
+#define ZC3XX_R144_YGAMMA04            0x0144
+#define ZC3XX_R145_YGAMMA05            0x0145
+#define ZC3XX_R146_YGAMMA06            0x0146
+#define ZC3XX_R147_YGAMMA07            0x0147
+#define ZC3XX_R148_YGAMMA08            0x0148
+#define ZC3XX_R149_YGAMMA09            0x0149
+#define ZC3XX_R14A_YGAMMA0A            0x014a
+#define ZC3XX_R14B_YGAMMA0B            0x014b
+#define ZC3XX_R14C_YGAMMA0C            0x014c
+#define ZC3XX_R14D_YGAMMA0D            0x014d
+#define ZC3XX_R14E_YGAMMA0E            0x014e
+#define ZC3XX_R14F_YGAMMA0F            0x014f
+#define ZC3XX_R150_YGAMMA10            0x0150
+#define ZC3XX_R151_YGAMMA11            0x0151
+
+#define ZC3XX_R1C5_SHARPNESSMODE       0x01c5
+#define ZC3XX_R1C6_SHARPNESS00         0x01c6
+#define ZC3XX_R1C7_SHARPNESS01         0x01c7
+#define ZC3XX_R1C8_SHARPNESS02         0x01c8
+#define ZC3XX_R1C9_SHARPNESS03         0x01c9
+#define ZC3XX_R1CA_SHARPNESS04         0x01ca
+#define ZC3XX_R1CB_SHARPNESS05         0x01cb
+
+/* Synchronization */
+#define ZC3XX_R190_SYNC00LOW           0x0190
+#define ZC3XX_R191_SYNC00MID           0x0191
+#define ZC3XX_R192_SYNC00HIGH          0x0192
+#define ZC3XX_R195_SYNC01LOW           0x0195
+#define ZC3XX_R196_SYNC01MID           0x0196
+#define ZC3XX_R197_SYNC01HIGH          0x0197
+
+/* Dead pixels */
+#define ZC3XX_R250_DEADPIXELSMODE      0x0250
+
+/* EEPROM */
+#define ZC3XX_R300_EEPROMCONFIG        0x0300
+#define ZC3XX_R301_EEPROMACCESS        0x0301
+#define ZC3XX_R302_EEPROMSTATUS        0x0302
diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c
new file mode 100644 (file)
index 0000000..b761b11
--- /dev/null
@@ -0,0 +1,7623 @@
+/*
+ *     Z-Star/Vimicro zc301/zc302p/vc30x library
+ *     Copyright (C) 2004 2005 2006 Michel Xhaard
+ *             mxhaard@magic.fr
+ *
+ * V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
+ *
+ * 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 MODULE_NAME "zc3xx"
+
+#include "gspca.h"
+
+#define DRIVER_VERSION_NUMBER  KERNEL_VERSION(2, 1, 7)
+static const char version[] = "2.1.7";
+
+MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>, "
+               "Serge A. Suchkov <Serge.A.S@tochka.ru>");
+MODULE_DESCRIPTION("GSPCA ZC03xx/VC3xx USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+static int force_sensor = -1;
+
+#include "jpeg.h"
+#include "zc3xx-reg.h"
+
+/* specific webcam descriptor */
+struct sd {
+       struct gspca_dev gspca_dev;     /* !! must be the first item */
+
+       __u8 brightness;
+       __u8 contrast;
+       __u8 gamma;
+       __u8 autogain;
+       __u8 lightfreq;
+       __u8 sharpness;
+
+       char qindex;
+       char sensor;                    /* Type of image sensor chip */
+/* !! values used in different tables */
+#define SENSOR_CS2102 0
+#define SENSOR_CS2102K 1
+#define SENSOR_GC0305 2
+#define SENSOR_HDCS2020 3
+#define SENSOR_HDCS2020b 4
+#define SENSOR_HV7131B 5
+#define SENSOR_HV7131C 6
+#define SENSOR_ICM105A 7
+#define SENSOR_MC501CB 8
+#define SENSOR_OV7620 9
+/*#define SENSOR_OV7648 9 - same values */
+#define SENSOR_OV7630C 10
+#define SENSOR_PAS106 11
+#define SENSOR_PB0330 12
+#define SENSOR_PO2030 13
+#define SENSOR_TAS5130CK 14
+#define SENSOR_TAS5130CXX 15
+#define SENSOR_TAS5130C_VF0250 16
+#define SENSOR_MAX 17
+       unsigned short chip_revision;
+};
+
+/* V4L2 controls supported by the driver */
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setgamma(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getgamma(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val);
+
+static struct ctrl sd_ctrls[] = {
+#define SD_BRIGHTNESS 0
+       {
+           {
+               .id      = V4L2_CID_BRIGHTNESS,
+               .type    = V4L2_CTRL_TYPE_INTEGER,
+               .name    = "Brightness",
+               .minimum = 0,
+               .maximum = 255,
+               .step    = 1,
+               .default_value = 128,
+           },
+           .set = sd_setbrightness,
+           .get = sd_getbrightness,
+       },
+#define SD_CONTRAST 1
+       {
+           {
+               .id      = V4L2_CID_CONTRAST,
+               .type    = V4L2_CTRL_TYPE_INTEGER,
+               .name    = "Contrast",
+               .minimum = 0,
+               .maximum = 256,
+               .step    = 1,
+               .default_value = 128,
+           },
+           .set = sd_setcontrast,
+           .get = sd_getcontrast,
+       },
+#define SD_GAMMA 2
+       {
+           {
+               .id      = V4L2_CID_GAMMA,
+               .type    = V4L2_CTRL_TYPE_INTEGER,
+               .name    = "Gamma",
+               .minimum = 1,
+               .maximum = 6,
+               .step    = 1,
+               .default_value = 4,
+           },
+           .set = sd_setgamma,
+           .get = sd_getgamma,
+       },
+#define SD_AUTOGAIN 3
+       {
+           {
+               .id      = V4L2_CID_AUTOGAIN,
+               .type    = V4L2_CTRL_TYPE_BOOLEAN,
+               .name    = "Auto Gain",
+               .minimum = 0,
+               .maximum = 1,
+               .step    = 1,
+               .default_value = 1,
+           },
+           .set = sd_setautogain,
+           .get = sd_getautogain,
+       },
+#define SD_FREQ 4
+       {
+           {
+               .id      = V4L2_CID_POWER_LINE_FREQUENCY,
+               .type    = V4L2_CTRL_TYPE_MENU,
+               .name    = "Light frequency filter",
+               .minimum = 0,
+               .maximum = 2,   /* 0: 0, 1: 50Hz, 2:60Hz */
+               .step    = 1,
+               .default_value = 1,
+           },
+           .set = sd_setfreq,
+           .get = sd_getfreq,
+       },
+#define SD_SHARPNESS 5
+       {
+           {
+               .id      = V4L2_CID_SHARPNESS,
+               .type    = V4L2_CTRL_TYPE_INTEGER,
+               .name    = "Sharpness",
+               .minimum = 0,
+               .maximum = 3,
+               .step    = 1,
+               .default_value = 2,
+           },
+           .set = sd_setsharpness,
+           .get = sd_getsharpness,
+       },
+};
+
+static struct v4l2_pix_format vga_mode[] = {
+       {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+               .bytesperline = 320,
+               .sizeimage = 320 * 240 * 3 / 8 + 590,
+               .colorspace = V4L2_COLORSPACE_JPEG,
+               .priv = 1},
+       {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+               .bytesperline = 640,
+               .sizeimage = 640 * 480 * 3 / 8 + 590,
+               .colorspace = V4L2_COLORSPACE_JPEG,
+               .priv = 0},
+};
+
+static struct v4l2_pix_format sif_mode[] = {
+       {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+               .bytesperline = 176,
+               .sizeimage = 176 * 144 * 3 / 8 + 590,
+               .colorspace = V4L2_COLORSPACE_JPEG,
+               .priv = 1},
+       {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+               .bytesperline = 352,
+               .sizeimage = 352 * 288 * 3 / 8 + 590,
+               .colorspace = V4L2_COLORSPACE_JPEG,
+               .priv = 0},
+};
+
+/* usb exchanges */
+struct usb_action {
+       __u8    req;
+       __u8    val;
+       __u16   idx;
+};
+
+static const struct usb_action cs2102_Initial[] = {
+       {0xa1, 0x01, 0x0008},
+       {0xa1, 0x01, 0x0008},
+       {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+       {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
+       {0xa0, 0x00, ZC3XX_R010_CMOSSENSORSELECT},
+       {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+       {0xa0, 0x20, ZC3XX_R080_HBLANKHIGH},
+       {0xa0, 0x21, ZC3XX_R081_HBLANKLOW},
+       {0xa0, 0x30, ZC3XX_R083_RGAINADDR},
+       {0xa0, 0x31, ZC3XX_R084_GGAINADDR},
+       {0xa0, 0x32, ZC3XX_R085_BGAINADDR},
+       {0xa0, 0x23, ZC3XX_R086_EXPTIMEHIGH},
+       {0xa0, 0x24, ZC3XX_R087_EXPTIMEMID},
+       {0xa0, 0x25, ZC3XX_R088_EXPTIMELOW},
+       {0xa0, 0xb3, ZC3XX_R08B_I2CDEVICEADDR},
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},  /* 00 */
+       {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+       {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+       {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+       {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+       {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+       {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+       {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+       {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+       {0xaa, 0x02, 0x0008},
+       {0xaa, 0x03, 0x0000},
+       {0xaa, 0x11, 0x0000},
+       {0xaa, 0x12, 0x0089},
+       {0xaa, 0x13, 0x0000},
+       {0xaa, 0x14, 0x00e9},
+       {0xaa, 0x20, 0x0000},
+       {0xaa, 0x22, 0x0000},
+       {0xaa, 0x0b, 0x0004},
+       {0xaa, 0x30, 0x0030},
+       {0xaa, 0x31, 0x0030},
+       {0xaa, 0x32, 0x0030},
+       {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+       {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+       {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+       {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+       {0xa0, 0x10, 0x01ae},
+       {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+       {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+       {0xa0, 0x68, ZC3XX_R18D_YTARGET},
+       {0xa0, 0x00, 0x01ad},
+       {0xa1, 0x01, 0x0002},
+       {0xa1, 0x01, 0x0008},
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},  /* 00 */
+       {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},   /* sharpness+ */
+       {0xa1, 0x01, 0x01c8},
+       {0xa1, 0x01, 0x01c9},
+       {0xa1, 0x01, 0x01ca},
+       {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05},   /* sharpness- */
+       {0xa0, 0x24, ZC3XX_R120_GAMMA00},       /* gamma 5 */
+       {0xa0, 0x44, ZC3XX_R121_GAMMA01},
+       {0xa0, 0x64, ZC3XX_R122_GAMMA02},
+       {0xa0, 0x84, ZC3XX_R123_GAMMA03},
+       {0xa0, 0x9d, ZC3XX_R124_GAMMA04},
+       {0xa0, 0xb2, ZC3XX_R125_GAMMA05},
+       {0xa0, 0xc4, ZC3XX_R126_GAMMA06},
+       {0xa0, 0xd3, ZC3XX_R127_GAMMA07},
+       {0xa0, 0xe0, ZC3XX_R128_GAMMA08},
+       {0xa0, 0xeb, ZC3XX_R129_GAMMA09},
+       {0xa0, 0xf4, ZC3XX_R12A_GAMMA0A},
+       {0xa0, 0xfb, ZC3XX_R12B_GAMMA0B},
+       {0xa0, 0xff, ZC3XX_R12C_GAMMA0C},
+       {0xa0, 0xff, ZC3XX_R12D_GAMMA0D},
+       {0xa0, 0xff, ZC3XX_R12E_GAMMA0E},
+       {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
+       {0xa0, 0x18, ZC3XX_R130_GAMMA10},
+       {0xa0, 0x20, ZC3XX_R131_GAMMA11},
+       {0xa0, 0x20, ZC3XX_R132_GAMMA12},
+       {0xa0, 0x1c, ZC3XX_R133_GAMMA13},
+       {0xa0, 0x16, ZC3XX_R134_GAMMA14},
+       {0xa0, 0x13, ZC3XX_R135_GAMMA15},
+       {0xa0, 0x10, ZC3XX_R136_GAMMA16},
+       {0xa0, 0x0e, ZC3XX_R137_GAMMA17},
+       {0xa0, 0x0b, ZC3XX_R138_GAMMA18},
+       {0xa0, 0x09, ZC3XX_R139_GAMMA19},
+       {0xa0, 0x07, ZC3XX_R13A_GAMMA1A},
+       {0xa0, 0x06, ZC3XX_R13B_GAMMA1B},
+       {0xa0, 0x00, ZC3XX_R13C_GAMMA1C},
+       {0xa0, 0x00, ZC3XX_R13D_GAMMA1D},
+       {0xa0, 0x00, ZC3XX_R13E_GAMMA1E},
+       {0xa0, 0x01, ZC3XX_R13F_GAMMA1F},
+       {0xa0, 0x58, ZC3XX_R10A_RGB00}, /* matrix */
+       {0xa0, 0xf4, ZC3XX_R10B_RGB01},
+       {0xa0, 0xf4, ZC3XX_R10C_RGB02},
+       {0xa0, 0xf4, ZC3XX_R10D_RGB10},
+       {0xa0, 0x58, ZC3XX_R10E_RGB11},
+       {0xa0, 0xf4, ZC3XX_R10F_RGB12},
+       {0xa0, 0xf4, ZC3XX_R110_RGB20},
+       {0xa0, 0xf4, ZC3XX_R111_RGB21},
+       {0xa0, 0x58, ZC3XX_R112_RGB22},
+       {0xa1, 0x01, 0x0180},
+       {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+       {0xaa, 0x23, 0x0001},
+       {0xaa, 0x24, 0x0055},
+       {0xaa, 0x25, 0x00cc},
+       {0xaa, 0x21, 0x003f},
+       {0xa0, 0x02, ZC3XX_R190_EXPOSURELIMITHIGH},
+       {0xa0, 0xab, ZC3XX_R191_EXPOSURELIMITMID},
+       {0xa0, 0x98, ZC3XX_R192_EXPOSURELIMITLOW},
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+       {0xa0, 0x30, ZC3XX_R196_ANTIFLICKERMID},
+       {0xa0, 0xd4, ZC3XX_R197_ANTIFLICKERLOW},
+       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
+       {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+       {0xa0, 0x39, ZC3XX_R01D_HSYNC_0},
+       {0xa0, 0x70, ZC3XX_R01E_HSYNC_1},
+       {0xa0, 0xb0, ZC3XX_R01F_HSYNC_2},
+       {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+       {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa1, 0x01, 0x0180},
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x40, ZC3XX_R116_RGAIN},
+       {0xa0, 0x40, ZC3XX_R117_GGAIN},
+       {0xa0, 0x40, ZC3XX_R118_BGAIN},
+       {}
+};
+
+static const struct usb_action cs2102_InitialScale[] = {
+       {0xa1, 0x01, 0x0008},
+       {0xa1, 0x01, 0x0008},
+       {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+       {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
+       {0xa0, 0x00, ZC3XX_R010_CMOSSENSORSELECT},
+       {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+       {0xa0, 0x20, ZC3XX_R080_HBLANKHIGH},
+       {0xa0, 0x21, ZC3XX_R081_HBLANKLOW},
+       {0xa0, 0x30, ZC3XX_R083_RGAINADDR},
+       {0xa0, 0x31, ZC3XX_R084_GGAINADDR},
+       {0xa0, 0x32, ZC3XX_R085_BGAINADDR},
+       {0xa0, 0x23, ZC3XX_R086_EXPTIMEHIGH},
+       {0xa0, 0x24, ZC3XX_R087_EXPTIMEMID},
+       {0xa0, 0x25, ZC3XX_R088_EXPTIMELOW},
+       {0xa0, 0xb3, ZC3XX_R08B_I2CDEVICEADDR},
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},  /* 00 */
+       {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+       {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+       {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+       {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+       {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+       {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+       {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+       {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+       {0xaa, 0x02, 0x0008},
+       {0xaa, 0x03, 0x0000},
+       {0xaa, 0x11, 0x0001},
+       {0xaa, 0x12, 0x0087},
+       {0xaa, 0x13, 0x0001},
+       {0xaa, 0x14, 0x00e7},
+       {0xaa, 0x20, 0x0000},
+       {0xaa, 0x22, 0x0000},
+       {0xaa, 0x0b, 0x0004},
+       {0xaa, 0x30, 0x0030},
+       {0xaa, 0x31, 0x0030},
+       {0xaa, 0x32, 0x0030},
+       {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION},
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+       {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+       {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+       {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+       {0xa0, 0x15, 0x01ae},
+       {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+       {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+       {0xa0, 0x68, ZC3XX_R18D_YTARGET},
+       {0xa0, 0x00, 0x01ad},
+       {0xa1, 0x01, 0x0002},
+       {0xa1, 0x01, 0x0008},
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},  /* 00 */
+       {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},   /* sharpness+ */
+       {0xa1, 0x01, 0x01c8},
+       {0xa1, 0x01, 0x01c9},
+       {0xa1, 0x01, 0x01ca},
+       {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05},   /* sharpness- */
+       {0xa0, 0x24, ZC3XX_R120_GAMMA00},       /* gamma 5 */
+       {0xa0, 0x44, ZC3XX_R121_GAMMA01},
+       {0xa0, 0x64, ZC3XX_R122_GAMMA02},
+       {0xa0, 0x84, ZC3XX_R123_GAMMA03},
+       {0xa0, 0x9d, ZC3XX_R124_GAMMA04},
+       {0xa0, 0xb2, ZC3XX_R125_GAMMA05},
+       {0xa0, 0xc4, ZC3XX_R126_GAMMA06},
+       {0xa0, 0xd3, ZC3XX_R127_GAMMA07},
+       {0xa0, 0xe0, ZC3XX_R128_GAMMA08},
+       {0xa0, 0xeb, ZC3XX_R129_GAMMA09},
+       {0xa0, 0xf4, ZC3XX_R12A_GAMMA0A},
+       {0xa0, 0xfb, ZC3XX_R12B_GAMMA0B},
+       {0xa0, 0xff, ZC3XX_R12C_GAMMA0C},
+       {0xa0, 0xff, ZC3XX_R12D_GAMMA0D},
+       {0xa0, 0xff, ZC3XX_R12E_GAMMA0E},
+       {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
+       {0xa0, 0x18, ZC3XX_R130_GAMMA10},
+       {0xa0, 0x20, ZC3XX_R131_GAMMA11},
+       {0xa0, 0x20, ZC3XX_R132_GAMMA12},
+       {0xa0, 0x1c, ZC3XX_R133_GAMMA13},
+       {0xa0, 0x16, ZC3XX_R134_GAMMA14},
+       {0xa0, 0x13, ZC3XX_R135_GAMMA15},
+       {0xa0, 0x10, ZC3XX_R136_GAMMA16},
+       {0xa0, 0x0e, ZC3XX_R137_GAMMA17},
+       {0xa0, 0x0b, ZC3XX_R138_GAMMA18},
+       {0xa0, 0x09, ZC3XX_R139_GAMMA19},
+       {0xa0, 0x07, ZC3XX_R13A_GAMMA1A},
+       {0xa0, 0x06, ZC3XX_R13B_GAMMA1B},
+       {0xa0, 0x00, ZC3XX_R13C_GAMMA1C},
+       {0xa0, 0x00, ZC3XX_R13D_GAMMA1D},
+       {0xa0, 0x00, ZC3XX_R13E_GAMMA1E},
+       {0xa0, 0x01, ZC3XX_R13F_GAMMA1F},
+       {0xa0, 0x58, ZC3XX_R10A_RGB00}, /* matrix */
+       {0xa0, 0xf4, ZC3XX_R10B_RGB01},
+       {0xa0, 0xf4, ZC3XX_R10C_RGB02},
+       {0xa0, 0xf4, ZC3XX_R10D_RGB10},
+       {0xa0, 0x58, ZC3XX_R10E_RGB11},
+       {0xa0, 0xf4, ZC3XX_R10F_RGB12},
+       {0xa0, 0xf4, ZC3XX_R110_RGB20},
+       {0xa0, 0xf4, ZC3XX_R111_RGB21},
+       {0xa0, 0x58, ZC3XX_R112_RGB22},
+       {0xa1, 0x01, 0x0180},
+       {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+       {0xaa, 0x23, 0x0000},
+       {0xaa, 0x24, 0x00aa},
+       {0xaa, 0x25, 0x00e6},
+       {0xaa, 0x21, 0x003f},
+       {0xa0, 0x01, ZC3XX_R190_EXPOSURELIMITHIGH},
+       {0xa0, 0x55, ZC3XX_R191_EXPOSURELIMITMID},
+       {0xa0, 0xcc, ZC3XX_R192_EXPOSURELIMITLOW},
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+       {0xa0, 0x18, ZC3XX_R196_ANTIFLICKERMID},
+       {0xa0, 0x6a, ZC3XX_R197_ANTIFLICKERLOW},
+       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
+       {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+       {0xa0, 0x3f, ZC3XX_R01D_HSYNC_0},
+       {0xa0, 0xa5, ZC3XX_R01E_HSYNC_1},
+       {0xa0, 0xf0, ZC3XX_R01F_HSYNC_2},
+       {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+       {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa1, 0x01, 0x0180},
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x40, ZC3XX_R116_RGAIN},
+       {0xa0, 0x40, ZC3XX_R117_GGAIN},
+       {0xa0, 0x40, ZC3XX_R118_BGAIN},
+       {}
+};
+static const struct usb_action cs2102_50HZ[] = {
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+       {0xaa, 0x0f, 0x008c}, /* 00,0f,8c,aa */
+       {0xaa, 0x03, 0x0005}, /* 00,03,05,aa */
+       {0xaa, 0x04, 0x00ac}, /* 00,04,ac,aa */
+       {0xaa, 0x10, 0x0005}, /* 00,10,05,aa */
+       {0xaa, 0x11, 0x00ac}, /* 00,11,ac,aa */
+       {0xaa, 0x1b, 0x0000}, /* 00,1b,00,aa */
+       {0xaa, 0x1c, 0x0005}, /* 00,1c,05,aa */
+       {0xaa, 0x1d, 0x00ac}, /* 00,1d,ac,aa */
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+       {0xa0, 0x3f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,3f,cc */
+       {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+       {0xa0, 0x42, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,42,cc */
+       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+       {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */
+       {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
+       {0xa0, 0x8c, ZC3XX_R01D_HSYNC_0}, /* 00,1d,8c,cc */
+       {0xa0, 0xb0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,b0,cc */
+       {0xa0, 0xd0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,d0,cc */
+       {}
+};
+static const struct usb_action cs2102_50HZScale[] = {
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+       {0xaa, 0x0f, 0x0093}, /* 00,0f,93,aa */
+       {0xaa, 0x03, 0x0005}, /* 00,03,05,aa */
+       {0xaa, 0x04, 0x00a1}, /* 00,04,a1,aa */
+       {0xaa, 0x10, 0x0005}, /* 00,10,05,aa */
+       {0xaa, 0x11, 0x00a1}, /* 00,11,a1,aa */
+       {0xaa, 0x1b, 0x0000}, /* 00,1b,00,aa */
+       {0xaa, 0x1c, 0x0005}, /* 00,1c,05,aa */
+       {0xaa, 0x1d, 0x00a1}, /* 00,1d,a1,aa */
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+       {0xa0, 0x3f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,3f,cc */
+       {0xa0, 0xf7, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f7,cc */
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+       {0xa0, 0x83, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,83,cc */
+       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+       {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */
+       {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
+       {0xa0, 0x93, ZC3XX_R01D_HSYNC_0}, /* 00,1d,93,cc */
+       {0xa0, 0xb0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,b0,cc */
+       {0xa0, 0xd0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,d0,cc */
+       {}
+};
+static const struct usb_action cs2102_60HZ[] = {
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+       {0xaa, 0x0f, 0x005d}, /* 00,0f,5d,aa */
+       {0xaa, 0x03, 0x0005}, /* 00,03,05,aa */
+       {0xaa, 0x04, 0x00aa}, /* 00,04,aa,aa */
+       {0xaa, 0x10, 0x0005}, /* 00,10,05,aa */
+       {0xaa, 0x11, 0x00aa}, /* 00,11,aa,aa */
+       {0xaa, 0x1b, 0x0000}, /* 00,1b,00,aa */
+       {0xaa, 0x1c, 0x0005}, /* 00,1c,05,aa */
+       {0xaa, 0x1d, 0x00aa}, /* 00,1d,aa,aa */
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+       {0xa0, 0x3f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,3f,cc */
+       {0xa0, 0xe4, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,e4,cc */
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+       {0xa0, 0x3a, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,3a,cc */
+       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+       {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */
+       {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
+       {0xa0, 0x5d, ZC3XX_R01D_HSYNC_0}, /* 00,1d,5d,cc */
+       {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc */
+       {0xa0, 0xd0, 0x00c8}, /* 00,c8,d0,cc */
+       {}
+};
+static const struct usb_action cs2102_60HZScale[] = {
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+       {0xaa, 0x0f, 0x00b7}, /* 00,0f,b7,aa */
+       {0xaa, 0x03, 0x0005}, /* 00,03,05,aa */
+       {0xaa, 0x04, 0x00be}, /* 00,04,be,aa */
+       {0xaa, 0x10, 0x0005}, /* 00,10,05,aa */
+       {0xaa, 0x11, 0x00be}, /* 00,11,be,aa */
+       {0xaa, 0x1b, 0x0000}, /* 00,1b,00,aa */
+       {0xaa, 0x1c, 0x0005}, /* 00,1c,05,aa */
+       {0xaa, 0x1d, 0x00be}, /* 00,1d,be,aa */
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+       {0xa0, 0x3f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,3f,cc */
+       {0xa0, 0xfc, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,fc,cc */
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+       {0xa0, 0x69, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,69,cc */
+       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+       {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */
+       {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
+       {0xa0, 0xb7, ZC3XX_R01D_HSYNC_0}, /* 00,1d,b7,cc */
+       {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d0,cc */
+       {0xa0, 0xe8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e8,cc */
+       {}
+};
+static const struct usb_action cs2102_NoFliker[] = {
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+       {0xaa, 0x0f, 0x0059}, /* 00,0f,59,aa */
+       {0xaa, 0x03, 0x0005}, /* 00,03,05,aa */
+       {0xaa, 0x04, 0x0080}, /* 00,04,80,aa */
+       {0xaa, 0x10, 0x0005}, /* 00,10,05,aa */
+       {0xaa, 0x11, 0x0080}, /* 00,11,80,aa */
+       {0xaa, 0x1b, 0x0000}, /* 00,1b,00,aa */
+       {0xaa, 0x1c, 0x0005}, /* 00,1c,05,aa */
+       {0xaa, 0x1d, 0x0080}, /* 00,1d,80,aa */
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+       {0xa0, 0x3f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,3f,cc */
+       {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+       {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */
+       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+       {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
+       {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
+       {0xa0, 0x59, ZC3XX_R01D_HSYNC_0}, /* 00,1d,59,cc */
+       {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc */
+       {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc */
+       {}
+};
+static const struct usb_action cs2102_NoFlikerScale[] = {
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+       {0xaa, 0x0f, 0x0059}, /* 00,0f,59,aa */
+       {0xaa, 0x03, 0x0005}, /* 00,03,05,aa */
+       {0xaa, 0x04, 0x0080}, /* 00,04,80,aa */
+       {0xaa, 0x10, 0x0005}, /* 00,10,05,aa */
+       {0xaa, 0x11, 0x0080}, /* 00,11,80,aa */
+       {0xaa, 0x1b, 0x0000}, /* 00,1b,00,aa */
+       {0xaa, 0x1c, 0x0005}, /* 00,1c,05,aa */
+       {0xaa, 0x1d, 0x0080}, /* 00,1d,80,aa */
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+       {0xa0, 0x3f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,3f,cc */
+       {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+       {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */
+       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+       {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
+       {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
+       {0xa0, 0x59, ZC3XX_R01D_HSYNC_0}, /* 00,1d,59,cc */
+       {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc */
+       {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc */
+       {}
+};
+
+/* CS2102_KOCOM */
+static const struct usb_action cs2102K_Initial[] = {
+       {0xa0, 0x11, ZC3XX_R002_CLOCKSELECT},
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+       {0xa0, 0x08, ZC3XX_R010_CMOSSENSORSELECT},
+       {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+       {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+       {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+       {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+       {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+       {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+       {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+       {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+       {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+       {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW},
+       {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW},
+       {0xa0, 0x55, ZC3XX_R08B_I2CDEVICEADDR},
+       {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x0a, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x0b, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x0c, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x7c, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x0d, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0xa3, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x03, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0xfb, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x05, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x06, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x03, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x09, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x08, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x0e, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x0f, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x10, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x11, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x12, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x15, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x16, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x0c, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x17, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x0c, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION},
+       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x78, ZC3XX_R18D_YTARGET},
+       {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+       {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+       {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+       {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+       {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID},
+       {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW},
+       {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+       {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+       {0xa0, 0x00, 0x01ad},
+       {0xa0, 0x01, 0x01b1},
+       {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x60, ZC3XX_R116_RGAIN},
+       {0xa0, 0x40, ZC3XX_R117_GGAIN},
+       {0xa0, 0x4c, ZC3XX_R118_BGAIN},
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},  /* clock ? */
+       {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},   /* sharpness+ */
+       {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05},   /* sharpness- */
+       {0xa0, 0x13, ZC3XX_R120_GAMMA00},       /* gamma 4 */
+       {0xa0, 0x38, ZC3XX_R121_GAMMA01},
+       {0xa0, 0x59, ZC3XX_R122_GAMMA02},
+       {0xa0, 0x79, ZC3XX_R123_GAMMA03},
+       {0xa0, 0x92, ZC3XX_R124_GAMMA04},
+       {0xa0, 0xa7, ZC3XX_R125_GAMMA05},
+       {0xa0, 0xb9, ZC3XX_R126_GAMMA06},
+       {0xa0, 0xc8, ZC3XX_R127_GAMMA07},
+       {0xa0, 0xd4, ZC3XX_R128_GAMMA08},
+       {0xa0, 0xdf, ZC3XX_R129_GAMMA09},
+       {0xa0, 0xe7, ZC3XX_R12A_GAMMA0A},
+       {0xa0, 0xee, ZC3XX_R12B_GAMMA0B},
+       {0xa0, 0xf4, ZC3XX_R12C_GAMMA0C},
+       {0xa0, 0xf9, ZC3XX_R12D_GAMMA0D},
+       {0xa0, 0xfc, ZC3XX_R12E_GAMMA0E},
+       {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
+       {0xa0, 0x26, ZC3XX_R130_GAMMA10},
+       {0xa0, 0x22, ZC3XX_R131_GAMMA11},
+       {0xa0, 0x20, ZC3XX_R132_GAMMA12},
+       {0xa0, 0x1c, ZC3XX_R133_GAMMA13},
+       {0xa0, 0x16, ZC3XX_R134_GAMMA14},
+       {0xa0, 0x13, ZC3XX_R135_GAMMA15},
+       {0xa0, 0x10, ZC3XX_R136_GAMMA16},
+       {0xa0, 0x0d, ZC3XX_R137_GAMMA17},
+       {0xa0, 0x0b, ZC3XX_R138_GAMMA18},
+       {0xa0, 0x09, ZC3XX_R139_GAMMA19},
+       {0xa0, 0x07, ZC3XX_R13A_GAMMA1A},
+       {0xa0, 0x06, ZC3XX_R13B_GAMMA1B},
+       {0xa0, 0x05, ZC3XX_R13C_GAMMA1C},
+       {0xa0, 0x04, ZC3XX_R13D_GAMMA1D},
+       {0xa0, 0x03, ZC3XX_R13E_GAMMA1E},
+       {0xa0, 0x02, ZC3XX_R13F_GAMMA1F},
+       {0xa0, 0x58, ZC3XX_R10A_RGB00}, /* matrix */
+       {0xa0, 0xf4, ZC3XX_R10B_RGB01},
+       {0xa0, 0xf4, ZC3XX_R10C_RGB02},
+       {0xa0, 0xf4, ZC3XX_R10D_RGB10},
+       {0xa0, 0x58, ZC3XX_R10E_RGB11},
+       {0xa0, 0xf4, ZC3XX_R10F_RGB12},
+       {0xa0, 0xf4, ZC3XX_R110_RGB20},
+       {0xa0, 0xf4, ZC3XX_R111_RGB21},
+       {0xa0, 0x58, ZC3XX_R112_RGB22},
+       {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+       {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x22, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x22, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH},
+       {0xa0, 0x22, ZC3XX_R0A4_EXPOSURETIMELOW},
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+       {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+       {0xa0, 0xee, ZC3XX_R192_EXPOSURELIMITLOW},
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+       {0xa0, 0x3a, ZC3XX_R197_ANTIFLICKERLOW},
+       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF},
+       {0xa0, 0x28, ZC3XX_R1AA_DIGITALGAINSTEP},
+       {0xa0, 0x04, ZC3XX_R01D_HSYNC_0},
+       {0xa0, 0x0f, ZC3XX_R01E_HSYNC_1},
+       {0xa0, 0x19, ZC3XX_R01F_HSYNC_2},
+       {0xa0, 0x1f, ZC3XX_R020_HSYNC_3},
+       {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+       {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x60, ZC3XX_R116_RGAIN},
+       {0xa0, 0x40, ZC3XX_R117_GGAIN},
+       {0xa0, 0x4c, ZC3XX_R118_BGAIN},
+       {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+       {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x5c, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x5c, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+       {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+       {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+       {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+       {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x96, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x96, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+       {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+       {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+       {}
+};
+
+static const struct usb_action cs2102K_InitialScale[] = {
+       {0xa0, 0x11, ZC3XX_R002_CLOCKSELECT},
+       {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+       {0xa0, 0x08, ZC3XX_R010_CMOSSENSORSELECT},
+       {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+       {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+       {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+       {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+       {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+       {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+       {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+       {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+       {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+       {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW},
+       {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW},
+       {0xa0, 0x55, ZC3XX_R08B_I2CDEVICEADDR},
+       {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x0a, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x0b, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x0c, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x7b, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x0d, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0xa3, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x03, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0xfb, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x05, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x06, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x03, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x09, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x08, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x0e, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x0f, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x10, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x11, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x12, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x15, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x16, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x0c, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x17, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x0c, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0xf7, ZC3XX_R101_SENSORCORRECTION},
+       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x78, ZC3XX_R18D_YTARGET},
+       {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+       {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+       {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+       {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+       {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID},
+       {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW},
+       {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+       {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+       {0xa0, 0x00, 0x01ad},
+       {0xa0, 0x01, 0x01b1},
+       {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x60, ZC3XX_R116_RGAIN},
+       {0xa0, 0x40, ZC3XX_R117_GGAIN},
+       {0xa0, 0x4c, ZC3XX_R118_BGAIN},
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},  /* clock ? */
+       {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},   /* sharpness+ */
+       {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05},   /* sharpness- */
+       {0xa0, 0x13, ZC3XX_R120_GAMMA00},       /* gamma 4 */
+       {0xa0, 0x38, ZC3XX_R121_GAMMA01},
+       {0xa0, 0x59, ZC3XX_R122_GAMMA02},
+       {0xa0, 0x79, ZC3XX_R123_GAMMA03},
+       {0xa0, 0x92, ZC3XX_R124_GAMMA04},
+       {0xa0, 0xa7, ZC3XX_R125_GAMMA05},
+       {0xa0, 0xb9, ZC3XX_R126_GAMMA06},
+       {0xa0, 0xc8, ZC3XX_R127_GAMMA07},
+       {0xa0, 0xd4, ZC3XX_R128_GAMMA08},
+       {0xa0, 0xdf, ZC3XX_R129_GAMMA09},
+       {0xa0, 0xe7, ZC3XX_R12A_GAMMA0A},
+       {0xa0, 0xee, ZC3XX_R12B_GAMMA0B},
+       {0xa0, 0xf4, ZC3XX_R12C_GAMMA0C},
+       {0xa0, 0xf9, ZC3XX_R12D_GAMMA0D},
+       {0xa0, 0xfc, ZC3XX_R12E_GAMMA0E},
+       {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
+       {0xa0, 0x26, ZC3XX_R130_GAMMA10},
+       {0xa0, 0x22, ZC3XX_R131_GAMMA11},
+       {0xa0, 0x20, ZC3XX_R132_GAMMA12},
+       {0xa0, 0x1c, ZC3XX_R133_GAMMA13},
+       {0xa0, 0x16, ZC3XX_R134_GAMMA14},
+       {0xa0, 0x13, ZC3XX_R135_GAMMA15},
+       {0xa0, 0x10, ZC3XX_R136_GAMMA16},
+       {0xa0, 0x0d, ZC3XX_R137_GAMMA17},
+       {0xa0, 0x0b, ZC3XX_R138_GAMMA18},
+       {0xa0, 0x09, ZC3XX_R139_GAMMA19},
+       {0xa0, 0x07, ZC3XX_R13A_GAMMA1A},
+       {0xa0, 0x06, ZC3XX_R13B_GAMMA1B},
+       {0xa0, 0x05, ZC3XX_R13C_GAMMA1C},
+       {0xa0, 0x04, ZC3XX_R13D_GAMMA1D},
+       {0xa0, 0x03, ZC3XX_R13E_GAMMA1E},
+       {0xa0, 0x02, ZC3XX_R13F_GAMMA1F},
+       {0xa0, 0x58, ZC3XX_R10A_RGB00}, /* matrix */
+       {0xa0, 0xf4, ZC3XX_R10B_RGB01},
+       {0xa0, 0xf4, ZC3XX_R10C_RGB02},
+       {0xa0, 0xf4, ZC3XX_R10D_RGB10},
+       {0xa0, 0x58, ZC3XX_R10E_RGB11},
+       {0xa0, 0xf4, ZC3XX_R10F_RGB12},
+       {0xa0, 0xf4, ZC3XX_R110_RGB20},
+       {0xa0, 0xf4, ZC3XX_R111_RGB21},
+       {0xa0, 0x58, ZC3XX_R112_RGB22},
+       {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+       {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x22, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x22, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH},
+       {0xa0, 0x22, ZC3XX_R0A4_EXPOSURETIMELOW},
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+       {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+       {0xa0, 0xee, ZC3XX_R192_EXPOSURELIMITLOW},
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+       {0xa0, 0x3a, ZC3XX_R197_ANTIFLICKERLOW},
+       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF},
+       {0xa0, 0x28, ZC3XX_R1AA_DIGITALGAINSTEP},
+       {0xa0, 0x04, ZC3XX_R01D_HSYNC_0},
+       {0xa0, 0x0f, ZC3XX_R01E_HSYNC_1},
+       {0xa0, 0x19, ZC3XX_R01F_HSYNC_2},
+       {0xa0, 0x1f, ZC3XX_R020_HSYNC_3},
+       {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+       {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x60, ZC3XX_R116_RGAIN},
+       {0xa0, 0x40, ZC3XX_R117_GGAIN},
+       {0xa0, 0x4c, ZC3XX_R118_BGAIN},
+       {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+       {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+       {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+       {0xa0, 0x08, ZC3XX_R010_CMOSSENSORSELECT},
+       {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+       {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+       {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+       {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+       {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+       {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+       {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+       {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+       {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+       {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW},
+       {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW},
+       {0xa0, 0x55, ZC3XX_R08B_I2CDEVICEADDR},
+       {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x0A, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x0B, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x0C, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x7b, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x0D, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0xA3, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x03, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0xfb, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x05, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x06, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x03, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x09, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x08, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x0E, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x0f, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x10, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x11, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x12, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x15, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x16, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x0c, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x17, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x0C, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0xf7, ZC3XX_R101_SENSORCORRECTION},
+       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x78, ZC3XX_R18D_YTARGET},
+       {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+       {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+       {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+       {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+       {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID},
+       {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW},
+       {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+       {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+       {0xa0, 0x00, 0x01ad},
+       {0xa0, 0x01, 0x01b1},
+       {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x60, ZC3XX_R116_RGAIN},
+       {0xa0, 0x40, ZC3XX_R117_GGAIN},
+       {0xa0, 0x4c, ZC3XX_R118_BGAIN},
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},  /* clock ? */
+       {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},   /* sharpness+ */
+       {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05},   /* sharpness- */
+       {0xa0, 0x13, ZC3XX_R120_GAMMA00},       /* gamma 4 */
+       {0xa0, 0x38, ZC3XX_R121_GAMMA01},
+       {0xa0, 0x59, ZC3XX_R122_GAMMA02},
+       {0xa0, 0x79, ZC3XX_R123_GAMMA03},
+       {0xa0, 0x92, ZC3XX_R124_GAMMA04},
+       {0xa0, 0xa7, ZC3XX_R125_GAMMA05},
+       {0xa0, 0xb9, ZC3XX_R126_GAMMA06},
+       {0xa0, 0xc8, ZC3XX_R127_GAMMA07},
+       {0xa0, 0xd4, ZC3XX_R128_GAMMA08},
+       {0xa0, 0xdf, ZC3XX_R129_GAMMA09},
+       {0xa0, 0xe7, ZC3XX_R12A_GAMMA0A},
+       {0xa0, 0xee, ZC3XX_R12B_GAMMA0B},
+       {0xa0, 0xf4, ZC3XX_R12C_GAMMA0C},
+       {0xa0, 0xf9, ZC3XX_R12D_GAMMA0D},
+       {0xa0, 0xfc, ZC3XX_R12E_GAMMA0E},
+       {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
+       {0xa0, 0x26, ZC3XX_R130_GAMMA10},
+       {0xa0, 0x22, ZC3XX_R131_GAMMA11},
+       {0xa0, 0x20, ZC3XX_R132_GAMMA12},
+       {0xa0, 0x1c, ZC3XX_R133_GAMMA13},
+       {0xa0, 0x16, ZC3XX_R134_GAMMA14},
+       {0xa0, 0x13, ZC3XX_R135_GAMMA15},
+       {0xa0, 0x10, ZC3XX_R136_GAMMA16},
+       {0xa0, 0x0d, ZC3XX_R137_GAMMA17},
+       {0xa0, 0x0b, ZC3XX_R138_GAMMA18},
+       {0xa0, 0x09, ZC3XX_R139_GAMMA19},
+       {0xa0, 0x07, ZC3XX_R13A_GAMMA1A},
+       {0xa0, 0x06, ZC3XX_R13B_GAMMA1B},
+       {0xa0, 0x05, ZC3XX_R13C_GAMMA1C},
+       {0xa0, 0x04, ZC3XX_R13D_GAMMA1D},
+       {0xa0, 0x03, ZC3XX_R13E_GAMMA1E},
+       {0xa0, 0x02, ZC3XX_R13F_GAMMA1F},
+       {0xa0, 0x58, ZC3XX_R10A_RGB00}, /* matrix */
+       {0xa0, 0xf4, ZC3XX_R10B_RGB01},
+       {0xa0, 0xf4, ZC3XX_R10C_RGB02},
+       {0xa0, 0xf4, ZC3XX_R10D_RGB10},
+       {0xa0, 0x58, ZC3XX_R10E_RGB11},
+       {0xa0, 0xf4, ZC3XX_R10F_RGB12},
+       {0xa0, 0xf4, ZC3XX_R110_RGB20},
+       {0xa0, 0xf4, ZC3XX_R111_RGB21},
+       {0xa0, 0x58, ZC3XX_R112_RGB22},
+       {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+       {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x22, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x22, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH},
+       {0xa0, 0x22, ZC3XX_R0A4_EXPOSURETIMELOW},
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+       {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+       {0xa0, 0xee, ZC3XX_R192_EXPOSURELIMITLOW},
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+       {0xa0, 0x3a, ZC3XX_R197_ANTIFLICKERLOW},
+       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF},
+       {0xa0, 0x28, ZC3XX_R1AA_DIGITALGAINSTEP},
+       {0xa0, 0x04, ZC3XX_R01D_HSYNC_0},
+       {0xa0, 0x0f, ZC3XX_R01E_HSYNC_1},
+       {0xa0, 0x19, ZC3XX_R01F_HSYNC_2},
+       {0xa0, 0x1f, ZC3XX_R020_HSYNC_3},
+       {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+       {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x60, ZC3XX_R116_RGAIN},
+       {0xa0, 0x40, ZC3XX_R117_GGAIN},
+       {0xa0, 0x4c, ZC3XX_R118_BGAIN},
+       {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+       {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x5c, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x5c, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+       {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+       {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+       {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+       {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x96, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x96, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+       {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+       {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+       {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+       {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+       {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+       {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0xd0, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0xd0, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+       {0xa0, 0x02, ZC3XX_R008_CLOCKSETTING},
+       {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+       {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+       {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+       {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x0a, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x0a, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+       {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+       {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+       {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+       {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x44, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x44, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+       {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+       {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+       {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+       {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x7e, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x7e, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+       {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+       {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+       {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+       {}
+};
+
+static const struct usb_action gc0305_Initial[] = {    /* 640x480 */
+       {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},  /* 00,08,03,cc */
+       {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT},      /* 00,10,01,cc */
+       {0xa0, 0x04, ZC3XX_R002_CLOCKSELECT},   /* 00,02,04,cc */
+       {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},        /* 00,03,02,cc */
+       {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */
+       {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},       /* 00,05,01,cc */
+       {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},        /* 00,06,e0,cc */
+       {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},       /* 00,01,01,cc */
+       {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},      /* 00,12,03,cc */
+       {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},      /* 00,12,01,cc */
+       {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},  /* 00,98,00,cc */
+       {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},  /* 00,9a,00,cc */
+       {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},     /* 01,1a,00,cc */
+       {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},     /* 01,1c,00,cc */
+       {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW},  /* 00,9c,e6,cc */
+       {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW},   /* 00,9e,86,cc */
+       {0xa0, 0x98, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,98,cc */
+       {0xaa, 0x13, 0x0002},   /* 00,13,02,aa */
+       {0xaa, 0x15, 0x0003},   /* 00,15,03,aa */
+       {0xaa, 0x01, 0x0000},   /* 00,01,00,aa */
+       {0xaa, 0x02, 0x0000},   /* 00,02,00,aa */
+       {0xaa, 0x1a, 0x0000},   /* 00,1a,00,aa */
+       {0xaa, 0x1c, 0x0017},   /* 00,1c,17,aa */
+       {0xaa, 0x1d, 0x0080},   /* 00,1d,80,aa */
+       {0xaa, 0x1f, 0x0008},   /* 00,1f,08,aa */
+       {0xaa, 0x21, 0x0012},   /* 00,21,12,aa */
+       {0xa0, 0x82, ZC3XX_R086_EXPTIMEHIGH},   /* 00,86,82,cc */
+       {0xa0, 0x83, ZC3XX_R087_EXPTIMEMID},    /* 00,87,83,cc */
+       {0xa0, 0x84, ZC3XX_R088_EXPTIMELOW},    /* 00,88,84,cc */
+       {0xaa, 0x05, 0x0000},   /* 00,05,00,aa */
+       {0xaa, 0x0a, 0x0000},   /* 00,0a,00,aa */
+       {0xaa, 0x0b, 0x00b0},   /* 00,0b,b0,aa */
+       {0xaa, 0x0c, 0x0000},   /* 00,0c,00,aa */
+       {0xaa, 0x0d, 0x00b0},   /* 00,0d,b0,aa */
+       {0xaa, 0x0e, 0x0000},   /* 00,0e,00,aa */
+       {0xaa, 0x0f, 0x00b0},   /* 00,0f,b0,aa */
+       {0xaa, 0x10, 0x0000},   /* 00,10,00,aa */
+       {0xaa, 0x11, 0x00b0},   /* 00,11,b0,aa */
+       {0xaa, 0x16, 0x0001},   /* 00,16,01,aa */
+       {0xaa, 0x17, 0x00e6},   /* 00,17,e6,aa */
+       {0xaa, 0x18, 0x0002},   /* 00,18,02,aa */
+       {0xaa, 0x19, 0x0086},   /* 00,19,86,aa */
+       {0xaa, 0x20, 0x0000},   /* 00,20,00,aa */
+       {0xaa, 0x1b, 0x0020},   /* 00,1b,20,aa */
+       {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION},      /* 01,01,b7,cc */
+       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},      /* 00,12,05,cc */
+       {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */
+       {0xa0, 0x76, ZC3XX_R189_AWBSTATUS},     /* 01,89,76,cc */
+       {0xa0, 0x09, 0x01ad},   /* 01,ad,09,cc */
+       {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */
+       {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},   /* 01,cb,13,cc */
+       {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},        /* 02,50,08,cc */
+       {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},  /* 03,01,08,cc */
+       {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},   /* 01,a8,60,cc */
+       {0xa0, 0x85, ZC3XX_R18D_YTARGET},       /* 01,8d,85,cc */
+       {0xa0, 0x00, 0x011e},   /* 01,1e,00,cc */
+       {0xa0, 0x52, ZC3XX_R116_RGAIN}, /* 01,16,52,cc */
+       {0xa0, 0x40, ZC3XX_R117_GGAIN}, /* 01,17,40,cc */
+       {0xa0, 0x52, ZC3XX_R118_BGAIN}, /* 01,18,52,cc */
+       {0xa0, 0x03, ZC3XX_R113_RGB03}, /* 01,13,03,cc */
+       {}
+};
+static const struct usb_action gc0305_InitialScale[] = { /* 320x240 */
+       {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},  /* 00,08,03,cc */
+       {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT},      /* 00,10,01,cc */
+       {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},   /* 00,02,10,cc */
+       {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},        /* 00,03,02,cc */
+       {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */
+       {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},       /* 00,05,01,cc */
+       {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},        /* 00,06,e0,cc */
+       {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},       /* 00,01,01,cc */
+       {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},      /* 00,12,03,cc */
+       {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},      /* 00,12,01,cc */
+       {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},  /* 00,98,00,cc */
+       {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},  /* 00,9a,00,cc */
+       {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},     /* 01,1a,00,cc */
+       {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},     /* 01,1c,00,cc */
+       {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW},  /* 00,9c,e8,cc */
+       {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW},   /* 00,9e,88,cc */
+       {0xa0, 0x98, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,98,cc */
+       {0xaa, 0x13, 0x0000},   /* 00,13,00,aa */
+       {0xaa, 0x15, 0x0001},   /* 00,15,01,aa */
+       {0xaa, 0x01, 0x0000},   /* 00,01,00,aa */
+       {0xaa, 0x02, 0x0000},   /* 00,02,00,aa */
+       {0xaa, 0x1a, 0x0000},   /* 00,1a,00,aa */
+       {0xaa, 0x1c, 0x0017},   /* 00,1c,17,aa */
+       {0xaa, 0x1d, 0x0080},   /* 00,1d,80,aa */
+       {0xaa, 0x1f, 0x0008},   /* 00,1f,08,aa */
+       {0xaa, 0x21, 0x0012},   /* 00,21,12,aa */
+       {0xa0, 0x82, ZC3XX_R086_EXPTIMEHIGH},   /* 00,86,82,cc */
+       {0xa0, 0x83, ZC3XX_R087_EXPTIMEMID},    /* 00,87,83,cc */
+       {0xa0, 0x84, ZC3XX_R088_EXPTIMELOW},    /* 00,88,84,cc */
+       {0xaa, 0x05, 0x0000},   /* 00,05,00,aa */
+       {0xaa, 0x0a, 0x0000},   /* 00,0a,00,aa */
+       {0xaa, 0x0b, 0x00b0},   /* 00,0b,b0,aa */
+       {0xaa, 0x0c, 0x0000},   /* 00,0c,00,aa */
+       {0xaa, 0x0d, 0x00b0},   /* 00,0d,b0,aa */
+       {0xaa, 0x0e, 0x0000},   /* 00,0e,00,aa */
+       {0xaa, 0x0f, 0x00b0},   /* 00,0f,b0,aa */
+       {0xaa, 0x10, 0x0000},   /* 00,10,00,aa */
+       {0xaa, 0x11, 0x00b0},   /* 00,11,b0,aa */
+       {0xaa, 0x16, 0x0001},   /* 00,16,01,aa */
+       {0xaa, 0x17, 0x00e8},   /* 00,17,e8,aa */
+       {0xaa, 0x18, 0x0002},   /* 00,18,02,aa */
+       {0xaa, 0x19, 0x0088},   /* 00,19,88,aa */
+       {0xaa, 0x20, 0x0000},   /* 00,20,00,aa */
+       {0xaa, 0x1b, 0x0020},   /* 00,1b,20,aa */
+       {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION},      /* 01,01,b7,cc */
+       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},      /* 00,12,05,cc */
+       {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */
+       {0xa0, 0x76, ZC3XX_R189_AWBSTATUS},     /* 01,89,76,cc */
+       {0xa0, 0x09, 0x01ad},   /* 01,ad,09,cc */
+       {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */
+       {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},   /* 01,cb,13,cc */
+       {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},        /* 02,50,08,cc */
+       {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},  /* 03,01,08,cc */
+       {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},   /* 01,a8,60,cc */
+       {0xa0, 0x00, 0x011e},   /* 01,1e,00,cc */
+       {0xa0, 0x52, ZC3XX_R116_RGAIN}, /* 01,16,52,cc */
+       {0xa0, 0x40, ZC3XX_R117_GGAIN}, /* 01,17,40,cc */
+       {0xa0, 0x52, ZC3XX_R118_BGAIN}, /* 01,18,52,cc */
+       {0xa0, 0x03, ZC3XX_R113_RGB03}, /* 01,13,03,cc */
+       {}
+};
+static const struct usb_action gc0305_50HZ[] = {
+       {0xaa, 0x82, 0x0000},   /* 00,82,00,aa */
+       {0xaa, 0x83, 0x0002},   /* 00,83,02,aa */
+       {0xaa, 0x84, 0x0038},   /* 00,84,38,aa */       /* win: 00,84,ec */
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},     /* 01,90,00,cc */
+       {0xa0, 0x0b, ZC3XX_R191_EXPOSURELIMITMID},      /* 01,91,0b,cc */
+       {0xa0, 0x18, ZC3XX_R192_EXPOSURELIMITLOW},      /* 01,92,18,cc */
+                                                       /* win: 01,92,10 */
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},       /* 01,95,00,cc */
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},        /* 01,96,00,cc */
+       {0xa0, 0x8e, ZC3XX_R197_ANTIFLICKERLOW},        /* 01,97,8e,cc */
+                                                       /* win: 01,97,ec */
+       {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},      /* 01,8c,0e,cc */
+       {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE},    /* 01,8f,15,cc */
+       {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},      /* 01,a9,10,cc */
+       {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},       /* 01,aa,24,cc */
+       {0xa0, 0x62, ZC3XX_R01D_HSYNC_0},       /* 00,1d,62,cc */
+       {0xa0, 0x90, ZC3XX_R01E_HSYNC_1},       /* 00,1e,90,cc */
+       {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2},       /* 00,1f,c8,cc */
+       {0xa0, 0xff, ZC3XX_R020_HSYNC_3},       /* 00,20,ff,cc */
+       {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},    /* 01,1d,60,cc */
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},     /* 01,80,42,cc */
+/*     {0xa0, 0x85, ZC3XX_R18D_YTARGET},        * 01,8d,85,cc *
+                                                * if 640x480 */
+       {}
+};
+static const struct usb_action gc0305_60HZ[] = {
+       {0xaa, 0x82, 0x0000},   /* 00,82,00,aa */
+       {0xaa, 0x83, 0x0000},   /* 00,83,00,aa */
+       {0xaa, 0x84, 0x00ec},   /* 00,84,ec,aa */
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},     /* 01,90,00,cc */
+       {0xa0, 0x0b, ZC3XX_R191_EXPOSURELIMITMID},      /* 01,91,0b,cc */
+       {0xa0, 0x10, ZC3XX_R192_EXPOSURELIMITLOW},      /* 01,92,10,cc */
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},       /* 01,95,00,cc */
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},        /* 01,96,00,cc */
+       {0xa0, 0xec, ZC3XX_R197_ANTIFLICKERLOW},        /* 01,97,ec,cc */
+       {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},      /* 01,8c,0e,cc */
+       {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE},    /* 01,8f,15,cc */
+       {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},      /* 01,a9,10,cc */
+       {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},       /* 01,aa,24,cc */
+       {0xa0, 0x62, ZC3XX_R01D_HSYNC_0},       /* 00,1d,62,cc */
+       {0xa0, 0x90, ZC3XX_R01E_HSYNC_1},       /* 00,1e,90,cc */
+       {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2},       /* 00,1f,c8,cc */
+       {0xa0, 0xff, ZC3XX_R020_HSYNC_3},       /* 00,20,ff,cc */
+       {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},    /* 01,1d,60,cc */
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},     /* 01,80,42,cc */
+       {0xa0, 0x80, ZC3XX_R18D_YTARGET},       /* 01,8d,80,cc */
+       {}
+};
+
+static const struct usb_action gc0305_NoFliker[] = {
+       {0xa0, 0x0c, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0c,cc */
+       {0xaa, 0x82, 0x0000},   /* 00,82,00,aa */
+       {0xaa, 0x83, 0x0000},   /* 00,83,00,aa */
+       {0xaa, 0x84, 0x0020},   /* 00,84,20,aa */
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},     /* 01,90,00,cc */
+       {0xa0, 0x00, ZC3XX_R191_EXPOSURELIMITMID},      /* 01,91,00,cc */
+       {0xa0, 0x48, ZC3XX_R192_EXPOSURELIMITLOW},      /* 01,92,48,cc */
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},       /* 01,95,00,cc */
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},        /* 01,96,00,cc */
+       {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW},        /* 01,97,10,cc */
+       {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},      /* 01,8c,0e,cc */
+       {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE},    /* 01,8f,15,cc */
+       {0xa0, 0x62, ZC3XX_R01D_HSYNC_0},       /* 00,1d,62,cc */
+       {0xa0, 0x90, ZC3XX_R01E_HSYNC_1},       /* 00,1e,90,cc */
+       {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2},       /* 00,1f,c8,cc */
+       {0xa0, 0xff, ZC3XX_R020_HSYNC_3},       /* 00,20,ff,cc */
+       {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},    /* 01,1d,60,cc */
+       {0xa0, 0x03, ZC3XX_R180_AUTOCORRECTENABLE},     /* 01,80,03,cc */
+       {0xa0, 0x80, ZC3XX_R18D_YTARGET},       /* 01,8d,80,cc */
+       {}
+};
+
+/* play poker with registers at your own risk !! */
+static const struct usb_action hdcs2020xx_Initial[] = {
+       {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+       {0xa0, 0x0e, ZC3XX_R010_CMOSSENSORSELECT},
+       {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
+       {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+       {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+       {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+       {0xa0, 0xd0, ZC3XX_R006_FRAMEHEIGHTLOW},
+                                               /* D0 ?? E0 did not start */
+       {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+       {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE},
+       {0xa0, 0x08, ZC3XX_R098_WINYSTARTLOW},
+       {0xa0, 0x02, ZC3XX_R09A_WINXSTARTLOW},
+       {0xa0, 0x08, ZC3XX_R11A_FIRSTYLOW},
+       {0xa0, 0x02, ZC3XX_R11C_FIRSTXLOW},
+       {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH},
+       {0xa0, 0xd8, ZC3XX_R09C_WINHEIGHTLOW},
+       {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH},
+       {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW},
+       {0xaa, 0x02, 0x0002},
+       {0xaa, 0x07, 0x0006},
+       {0xaa, 0x08, 0x0002},
+       {0xaa, 0x09, 0x0006},
+       {0xaa, 0x0a, 0x0001},
+       {0xaa, 0x0b, 0x0001},
+       {0xaa, 0x0c, 0x0008},
+       {0xaa, 0x0d, 0x0000},
+       {0xaa, 0x10, 0x0000},
+       {0xaa, 0x12, 0x0005},
+       {0xaa, 0x13, 0x0063},
+       {0xaa, 0x15, 0x0070},
+       {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
+       {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+       {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+       {0xa0, 0x00, 0x01ad},
+       {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+       {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+       {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+       {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+       {0xa0, 0x70, ZC3XX_R18D_YTARGET},
+       {0xa1, 0x01, 0x0002},
+       {0xa1, 0x01, 0x0008},
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},  /* clock ? */
+       {0xa0, 0x04, ZC3XX_R1C6_SHARPNESS00},   /* sharpness+ */
+       {0xa1, 0x01, 0x01c8},
+       {0xa1, 0x01, 0x01c9},
+       {0xa1, 0x01, 0x01ca},
+       {0xa0, 0x07, ZC3XX_R1CB_SHARPNESS05},   /* sharpness- */
+       {0xa0, 0x11, ZC3XX_R120_GAMMA00},       /* gamma ~4 */
+       {0xa0, 0x37, ZC3XX_R121_GAMMA01},
+       {0xa0, 0x58, ZC3XX_R122_GAMMA02},
+       {0xa0, 0x79, ZC3XX_R123_GAMMA03},
+       {0xa0, 0x91, ZC3XX_R124_GAMMA04},
+       {0xa0, 0xa6, ZC3XX_R125_GAMMA05},
+       {0xa0, 0xb8, ZC3XX_R126_GAMMA06},
+       {0xa0, 0xc7, ZC3XX_R127_GAMMA07},
+       {0xa0, 0xd3, ZC3XX_R128_GAMMA08},
+       {0xa0, 0xde, ZC3XX_R129_GAMMA09},
+       {0xa0, 0xe6, ZC3XX_R12A_GAMMA0A},
+       {0xa0, 0xed, ZC3XX_R12B_GAMMA0B},
+       {0xa0, 0xf3, ZC3XX_R12C_GAMMA0C},
+       {0xa0, 0xf8, ZC3XX_R12D_GAMMA0D},
+       {0xa0, 0xfb, ZC3XX_R12E_GAMMA0E},
+       {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
+       {0xa0, 0x26, ZC3XX_R130_GAMMA10},
+       {0xa0, 0x23, ZC3XX_R131_GAMMA11},
+       {0xa0, 0x20, ZC3XX_R132_GAMMA12},
+       {0xa0, 0x1c, ZC3XX_R133_GAMMA13},
+       {0xa0, 0x16, ZC3XX_R134_GAMMA14},
+       {0xa0, 0x13, ZC3XX_R135_GAMMA15},
+       {0xa0, 0x10, ZC3XX_R136_GAMMA16},
+       {0xa0, 0x0d, ZC3XX_R137_GAMMA17},
+       {0xa0, 0x0b, ZC3XX_R138_GAMMA18},
+       {0xa0, 0x09, ZC3XX_R139_GAMMA19},
+       {0xa0, 0x07, ZC3XX_R13A_GAMMA1A},
+       {0xa0, 0x06, ZC3XX_R13B_GAMMA1B},
+       {0xa0, 0x05, ZC3XX_R13C_GAMMA1C},
+       {0xa0, 0x04, ZC3XX_R13D_GAMMA1D},
+       {0xa0, 0x03, ZC3XX_R13E_GAMMA1E},
+       {0xa0, 0x02, ZC3XX_R13F_GAMMA1F},
+
+       {0xa0, 0x4c, ZC3XX_R10A_RGB00}, /* matrix */
+       {0xa0, 0xf5, ZC3XX_R10B_RGB01},
+       {0xa0, 0xff, ZC3XX_R10C_RGB02},
+       {0xa0, 0xf9, ZC3XX_R10D_RGB10},
+       {0xa0, 0x51, ZC3XX_R10E_RGB11},
+       {0xa0, 0xf5, ZC3XX_R10F_RGB12},
+       {0xa0, 0xfb, ZC3XX_R110_RGB20},
+       {0xa0, 0xed, ZC3XX_R111_RGB21},
+       {0xa0, 0x5f, ZC3XX_R112_RGB22},
+
+       {0xa1, 0x01, 0x0180},
+       {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+       {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID},
+       {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW},
+       {0xaa, 0x20, 0x0004},
+       {0xaa, 0x21, 0x003d},
+       {0xaa, 0x03, 0x0041},
+       {0xaa, 0x04, 0x0010},
+       {0xaa, 0x05, 0x003d},
+       {0xaa, 0x0e, 0x0001},
+       {0xaa, 0x0f, 0x0000},
+       {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+       {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+       {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID},
+       {0xa0, 0x3d, ZC3XX_R192_EXPOSURELIMITLOW},
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+       {0xa0, 0x9b, ZC3XX_R197_ANTIFLICKERLOW},
+       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x41, ZC3XX_R01D_HSYNC_0},
+       {0xa0, 0x6f, ZC3XX_R01E_HSYNC_1},
+       {0xa0, 0xad, ZC3XX_R01F_HSYNC_2},
+       {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+       {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID},
+       {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW},
+       {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa1, 0x01, 0x0195},
+       {0xa1, 0x01, 0x0196},
+       {0xa1, 0x01, 0x0197},
+       {0xa0, 0x3d, ZC3XX_R192_EXPOSURELIMITLOW},
+       {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID},
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+       {0xa0, 0x1d, ZC3XX_R116_RGAIN},
+       {0xa0, 0x40, ZC3XX_R117_GGAIN},
+       {0xa0, 0x85, ZC3XX_R118_BGAIN},
+       {0xa1, 0x01, 0x0116},
+       {0xa1, 0x01, 0x0118},
+       {0xa1, 0x01, 0x0180},
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x1d, ZC3XX_R116_RGAIN},
+       {0xa0, 0x40, ZC3XX_R117_GGAIN},
+       {0xa0, 0x85, ZC3XX_R118_BGAIN},
+       {0xa1, 0x01, 0x0116},
+       {0xa1, 0x01, 0x0118},
+/*     {0xa0, 0x02, ZC3XX_R008_CLOCKSETTING}, */
+       {0xa0, 0x00, 0x0007},
+       {}
+};
+
+static const struct usb_action hdcs2020xx_InitialScale[] = {
+       {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+       {0xa0, 0x0e, ZC3XX_R010_CMOSSENSORSELECT},
+       {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
+       {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+       {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+       {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+       {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+       {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+       {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE},
+       {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+       {0xa0, 0x03, ZC3XX_R09A_WINXSTARTLOW},
+       {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+       {0xa0, 0x03, ZC3XX_R11C_FIRSTXLOW},
+       {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH},
+       {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW},
+       {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH},
+       {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW},
+       {0xaa, 0x02, 0x0002},
+       {0xaa, 0x07, 0x0006},
+       {0xaa, 0x08, 0x0002},
+       {0xaa, 0x09, 0x0006},
+       {0xaa, 0x0a, 0x0001},
+       {0xaa, 0x0b, 0x0001},
+       {0xaa, 0x0c, 0x0008},
+       {0xaa, 0x0d, 0x0000},
+       {0xaa, 0x10, 0x0000},
+       {0xaa, 0x12, 0x0005},
+       {0xaa, 0x13, 0x0063},
+       {0xaa, 0x15, 0x0070},
+       {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION},
+       {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+       {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+       {0xa0, 0x00, 0x01ad},
+       {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+       {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+       {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+       {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+       {0xa0, 0x70, ZC3XX_R18D_YTARGET},
+       {0xa1, 0x01, 0x0002},
+       {0xa1, 0x01, 0x0008},
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},  /* clock ? */
+       {0xa0, 0x04, ZC3XX_R1C6_SHARPNESS00},   /* sharpness+ */
+       {0xa1, 0x01, 0x01c8},
+       {0xa1, 0x01, 0x01c9},
+       {0xa1, 0x01, 0x01ca},
+       {0xa0, 0x07, ZC3XX_R1CB_SHARPNESS05},   /* sharpness- */
+       {0xa0, 0x11, ZC3XX_R120_GAMMA00},       /* gamma ~4*/
+       {0xa0, 0x37, ZC3XX_R121_GAMMA01},
+       {0xa0, 0x58, ZC3XX_R122_GAMMA02},
+       {0xa0, 0x79, ZC3XX_R123_GAMMA03},
+       {0xa0, 0x91, ZC3XX_R124_GAMMA04},
+       {0xa0, 0xa6, ZC3XX_R125_GAMMA05},
+       {0xa0, 0xb8, ZC3XX_R126_GAMMA06},
+       {0xa0, 0xc7, ZC3XX_R127_GAMMA07},
+       {0xa0, 0xd3, ZC3XX_R128_GAMMA08},
+       {0xa0, 0xde, ZC3XX_R129_GAMMA09},
+       {0xa0, 0xe6, ZC3XX_R12A_GAMMA0A},
+       {0xa0, 0xed, ZC3XX_R12B_GAMMA0B},
+       {0xa0, 0xf3, ZC3XX_R12C_GAMMA0C},
+       {0xa0, 0xf8, ZC3XX_R12D_GAMMA0D},
+       {0xa0, 0xfb, ZC3XX_R12E_GAMMA0E},
+       {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
+       {0xa0, 0x26, ZC3XX_R130_GAMMA10},
+       {0xa0, 0x23, ZC3XX_R131_GAMMA11},
+       {0xa0, 0x20, ZC3XX_R132_GAMMA12},
+       {0xa0, 0x1c, ZC3XX_R133_GAMMA13},
+       {0xa0, 0x16, ZC3XX_R134_GAMMA14},
+       {0xa0, 0x13, ZC3XX_R135_GAMMA15},
+       {0xa0, 0x10, ZC3XX_R136_GAMMA16},
+       {0xa0, 0x0d, ZC3XX_R137_GAMMA17},
+       {0xa0, 0x0b, ZC3XX_R138_GAMMA18},
+       {0xa0, 0x09, ZC3XX_R139_GAMMA19},
+       {0xa0, 0x07, ZC3XX_R13A_GAMMA1A},
+       {0xa0, 0x06, ZC3XX_R13B_GAMMA1B},
+       {0xa0, 0x05, ZC3XX_R13C_GAMMA1C},
+       {0xa0, 0x04, ZC3XX_R13D_GAMMA1D},
+       {0xa0, 0x03, ZC3XX_R13E_GAMMA1E},
+       {0xa0, 0x02, ZC3XX_R13F_GAMMA1F},
+       {0xa0, 0x60, ZC3XX_R10A_RGB00}, /* matrix */
+       {0xa0, 0xff, ZC3XX_R10B_RGB01},
+       {0xa0, 0xff, ZC3XX_R10C_RGB02},
+       {0xa0, 0xff, ZC3XX_R10D_RGB10},
+       {0xa0, 0x60, ZC3XX_R10E_RGB11},
+       {0xa0, 0xff, ZC3XX_R10F_RGB12},
+       {0xa0, 0xff, ZC3XX_R110_RGB20},
+       {0xa0, 0xff, ZC3XX_R111_RGB21},
+       {0xa0, 0x60, ZC3XX_R112_RGB22},
+
+       {0xa1, 0x01, 0x0180},
+       {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+       {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID},
+       {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW},
+       {0xaa, 0x20, 0x0002},
+       {0xaa, 0x21, 0x001b},
+       {0xaa, 0x03, 0x0044},
+       {0xaa, 0x04, 0x0008},
+       {0xaa, 0x05, 0x001b},
+       {0xaa, 0x0e, 0x0001},
+       {0xaa, 0x0f, 0x0000},
+       {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+       {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+       {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID},
+       {0xa0, 0x1b, ZC3XX_R192_EXPOSURELIMITLOW},
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+       {0xa0, 0x4d, ZC3XX_R197_ANTIFLICKERLOW},
+       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x44, ZC3XX_R01D_HSYNC_0},
+       {0xa0, 0x6f, ZC3XX_R01E_HSYNC_1},
+       {0xa0, 0xad, ZC3XX_R01F_HSYNC_2},
+       {0xa0, 0xeb, ZC3XX_R020_HSYNC_3},
+       {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID},
+       {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW},
+       {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa1, 0x01, 0x0195},
+       {0xa1, 0x01, 0x0196},
+       {0xa1, 0x01, 0x0197},
+       {0xa0, 0x1b, ZC3XX_R192_EXPOSURELIMITLOW},
+       {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID},
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+       {0xa0, 0x1d, ZC3XX_R116_RGAIN},
+       {0xa0, 0x40, ZC3XX_R117_GGAIN},
+       {0xa0, 0x99, ZC3XX_R118_BGAIN},
+       {0xa1, 0x01, 0x0116},
+       {0xa1, 0x01, 0x0118},
+       {0xa1, 0x01, 0x0180},
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x1d, ZC3XX_R116_RGAIN},
+       {0xa0, 0x40, ZC3XX_R117_GGAIN},
+       {0xa0, 0x99, ZC3XX_R118_BGAIN},
+/*     {0xa0, 0x02, ZC3XX_R008_CLOCKSETTING}, */
+       {0xa0, 0x00, 0x0007},
+/*     {0xa0, 0x18, 0x00fe}, */
+       {}
+};
+static const struct usb_action hdcs2020xb_Initial[] = {
+       {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+       {0xa0, 0x11, ZC3XX_R002_CLOCKSELECT},
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},  /* qtable 0x05 */
+       {0xa0, 0x08, ZC3XX_R010_CMOSSENSORSELECT},
+       {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+       {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+       {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+       {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+       {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+       {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+       {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+       {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+       {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+       {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW},
+       {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW},
+       {0xaa, 0x1c, 0x0000},
+       {0xaa, 0x0a, 0x0001},
+       {0xaa, 0x0b, 0x0006},
+       {0xaa, 0x0c, 0x007b},
+       {0xaa, 0x0d, 0x00a7},
+       {0xaa, 0x03, 0x00fb},
+       {0xaa, 0x05, 0x0000},
+       {0xaa, 0x06, 0x0003},
+       {0xaa, 0x09, 0x0008},
+
+       {0xaa, 0x0f, 0x0018},   /* set sensor gain */
+       {0xaa, 0x10, 0x0018},
+       {0xaa, 0x11, 0x0018},
+       {0xaa, 0x12, 0x0018},
+
+       {0xaa, 0x15, 0x004e},
+       {0xaa, 0x1c, 0x0004},
+       {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION},
+       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x70, ZC3XX_R18D_YTARGET},
+       {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+       {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+       {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+       {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+       {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+       {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+       {0xa1, 0x01, 0x0002},
+       {0xa1, 0x01, 0x0008},
+       {0xa1, 0x01, 0x0180},
+       {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x40, ZC3XX_R116_RGAIN},
+       {0xa0, 0x40, ZC3XX_R117_GGAIN},
+       {0xa0, 0x40, ZC3XX_R118_BGAIN},
+       {0xa1, 0x01, 0x0008},
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},  /* clock ? */
+       {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},   /* sharpness+ */
+       {0xa1, 0x01, 0x01c8},
+       {0xa1, 0x01, 0x01c9},
+       {0xa1, 0x01, 0x01ca},
+       {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05},   /* sharpness- */
+       {0xa0, 0x13, ZC3XX_R120_GAMMA00},       /* gamma 4 */
+       {0xa0, 0x38, ZC3XX_R121_GAMMA01},
+       {0xa0, 0x59, ZC3XX_R122_GAMMA02},
+       {0xa0, 0x79, ZC3XX_R123_GAMMA03},
+       {0xa0, 0x92, ZC3XX_R124_GAMMA04},
+       {0xa0, 0xa7, ZC3XX_R125_GAMMA05},
+       {0xa0, 0xb9, ZC3XX_R126_GAMMA06},
+       {0xa0, 0xc8, ZC3XX_R127_GAMMA07},
+       {0xa0, 0xd4, ZC3XX_R128_GAMMA08},
+       {0xa0, 0xdf, ZC3XX_R129_GAMMA09},
+       {0xa0, 0xe7, ZC3XX_R12A_GAMMA0A},
+       {0xa0, 0xee, ZC3XX_R12B_GAMMA0B},
+       {0xa0, 0xf4, ZC3XX_R12C_GAMMA0C},
+       {0xa0, 0xf9, ZC3XX_R12D_GAMMA0D},
+       {0xa0, 0xfc, ZC3XX_R12E_GAMMA0E},
+       {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
+       {0xa0, 0x26, ZC3XX_R130_GAMMA10},
+       {0xa0, 0x22, ZC3XX_R131_GAMMA11},
+       {0xa0, 0x20, ZC3XX_R132_GAMMA12},
+       {0xa0, 0x1c, ZC3XX_R133_GAMMA13},
+       {0xa0, 0x16, ZC3XX_R134_GAMMA14},
+       {0xa0, 0x13, ZC3XX_R135_GAMMA15},
+       {0xa0, 0x10, ZC3XX_R136_GAMMA16},
+       {0xa0, 0x0d, ZC3XX_R137_GAMMA17},
+       {0xa0, 0x0b, ZC3XX_R138_GAMMA18},
+       {0xa0, 0x09, ZC3XX_R139_GAMMA19},
+       {0xa0, 0x07, ZC3XX_R13A_GAMMA1A},
+       {0xa0, 0x06, ZC3XX_R13B_GAMMA1B},
+       {0xa0, 0x05, ZC3XX_R13C_GAMMA1C},
+       {0xa0, 0x04, ZC3XX_R13D_GAMMA1D},
+       {0xa0, 0x03, ZC3XX_R13E_GAMMA1E},
+       {0xa0, 0x02, ZC3XX_R13F_GAMMA1F},
+
+       {0xa0, 0x66, ZC3XX_R10A_RGB00}, /* matrix */
+       {0xa0, 0xed, ZC3XX_R10B_RGB01},
+       {0xa0, 0xed, ZC3XX_R10C_RGB02},
+       {0xa0, 0xed, ZC3XX_R10D_RGB10},
+       {0xa0, 0x66, ZC3XX_R10E_RGB11},
+       {0xa0, 0xed, ZC3XX_R10F_RGB12},
+       {0xa0, 0xed, ZC3XX_R110_RGB20},
+       {0xa0, 0xed, ZC3XX_R111_RGB21},
+       {0xa0, 0x66, ZC3XX_R112_RGB22},
+
+       {0xa1, 0x01, 0x0180},
+       {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+       {0xaa, 0x13, 0x0031},
+       {0xaa, 0x14, 0x0001},
+       {0xaa, 0x0e, 0x0004},
+       {0xaa, 0x19, 0x00cd},
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+       {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID},
+       {0xa0, 0x62, ZC3XX_R192_EXPOSURELIMITLOW},
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+       {0xa0, 0x3d, ZC3XX_R197_ANTIFLICKERLOW},
+       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+
+       {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF},      /* 0x14 */
+       {0xa0, 0x28, ZC3XX_R1AA_DIGITALGAINSTEP},
+       {0xa0, 0x04, ZC3XX_R01D_HSYNC_0},
+       {0xa0, 0x18, ZC3XX_R01E_HSYNC_1},
+       {0xa0, 0x2c, ZC3XX_R01F_HSYNC_2},
+       {0xa0, 0x41, ZC3XX_R020_HSYNC_3},
+       {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa1, 0x01, 0x0180},
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x40, ZC3XX_R116_RGAIN},
+       {0xa0, 0x40, ZC3XX_R117_GGAIN},
+       {0xa0, 0x40, ZC3XX_R118_BGAIN},
+       {}
+};
+static const struct usb_action hdcs2020xb_InitialScale[] = {
+       {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+       {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+       {0xa0, 0x08, ZC3XX_R010_CMOSSENSORSELECT},
+       {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+       {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+       {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+       {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+       {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+       {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+       {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+       {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+       {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+       {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW},
+       {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW},
+       {0xaa, 0x1c, 0x0000},
+       {0xaa, 0x0a, 0x0001},
+       {0xaa, 0x0b, 0x0006},
+       {0xaa, 0x0c, 0x007a},
+       {0xaa, 0x0d, 0x00a7},
+       {0xaa, 0x03, 0x00fb},
+       {0xaa, 0x05, 0x0000},
+       {0xaa, 0x06, 0x0003},
+       {0xaa, 0x09, 0x0008},
+       {0xaa, 0x0f, 0x0018},   /* original setting */
+       {0xaa, 0x10, 0x0018},
+       {0xaa, 0x11, 0x0018},
+       {0xaa, 0x12, 0x0018},
+       {0xaa, 0x15, 0x004e},
+       {0xaa, 0x1c, 0x0004},
+       {0xa0, 0xf7, ZC3XX_R101_SENSORCORRECTION},
+       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x70, ZC3XX_R18D_YTARGET},
+       {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+       {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+       {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+       {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+       {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+       {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+       {0xa1, 0x01, 0x0002},
+       {0xa1, 0x01, 0x0008},
+       {0xa1, 0x01, 0x0180},
+       {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x40, ZC3XX_R116_RGAIN},
+       {0xa0, 0x40, ZC3XX_R117_GGAIN},
+       {0xa0, 0x40, ZC3XX_R118_BGAIN},
+       {0xa1, 0x01, 0x0008},
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},  /* clock ? */
+       {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},   /* sharpness+ */
+       {0xa1, 0x01, 0x01c8},
+       {0xa1, 0x01, 0x01c9},
+       {0xa1, 0x01, 0x01ca},
+       {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05},   /* sharpness- */
+       {0xa0, 0x13, ZC3XX_R120_GAMMA00},       /* gamma 4 */
+       {0xa0, 0x38, ZC3XX_R121_GAMMA01},
+       {0xa0, 0x59, ZC3XX_R122_GAMMA02},
+       {0xa0, 0x79, ZC3XX_R123_GAMMA03},
+       {0xa0, 0x92, ZC3XX_R124_GAMMA04},
+       {0xa0, 0xa7, ZC3XX_R125_GAMMA05},
+       {0xa0, 0xb9, ZC3XX_R126_GAMMA06},
+       {0xa0, 0xc8, ZC3XX_R127_GAMMA07},
+       {0xa0, 0xd4, ZC3XX_R128_GAMMA08},
+       {0xa0, 0xdf, ZC3XX_R129_GAMMA09},
+       {0xa0, 0xe7, ZC3XX_R12A_GAMMA0A},
+       {0xa0, 0xee, ZC3XX_R12B_GAMMA0B},
+       {0xa0, 0xf4, ZC3XX_R12C_GAMMA0C},
+       {0xa0, 0xf9, ZC3XX_R12D_GAMMA0D},
+       {0xa0, 0xfc, ZC3XX_R12E_GAMMA0E},
+       {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
+       {0xa0, 0x26, ZC3XX_R130_GAMMA10},
+       {0xa0, 0x22, ZC3XX_R131_GAMMA11},
+       {0xa0, 0x20, ZC3XX_R132_GAMMA12},
+       {0xa0, 0x1c, ZC3XX_R133_GAMMA13},
+       {0xa0, 0x16, ZC3XX_R134_GAMMA14},
+       {0xa0, 0x13, ZC3XX_R135_GAMMA15},
+       {0xa0, 0x10, ZC3XX_R136_GAMMA16},
+       {0xa0, 0x0d, ZC3XX_R137_GAMMA17},
+       {0xa0, 0x0b, ZC3XX_R138_GAMMA18},
+       {0xa0, 0x09, ZC3XX_R139_GAMMA19},
+       {0xa0, 0x07, ZC3XX_R13A_GAMMA1A},
+       {0xa0, 0x06, ZC3XX_R13B_GAMMA1B},
+       {0xa0, 0x05, ZC3XX_R13C_GAMMA1C},
+       {0xa0, 0x04, ZC3XX_R13D_GAMMA1D},
+       {0xa0, 0x03, ZC3XX_R13E_GAMMA1E},
+       {0xa0, 0x02, ZC3XX_R13F_GAMMA1F},
+       {0xa0, 0x66, ZC3XX_R10A_RGB00}, /* matrix */
+       {0xa0, 0xed, ZC3XX_R10B_RGB01},
+       {0xa0, 0xed, ZC3XX_R10C_RGB02},
+       {0xa0, 0xed, ZC3XX_R10D_RGB10},
+       {0xa0, 0x66, ZC3XX_R10E_RGB11},
+       {0xa0, 0xed, ZC3XX_R10F_RGB12},
+       {0xa0, 0xed, ZC3XX_R110_RGB20},
+       {0xa0, 0xed, ZC3XX_R111_RGB21},
+       {0xa0, 0x66, ZC3XX_R112_RGB22},
+       {0xa1, 0x01, 0x0180},
+       {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ /**** set exposure ***/
+       {0xaa, 0x13, 0x0031},
+       {0xaa, 0x14, 0x0001},
+       {0xaa, 0x0e, 0x0004},
+       {0xaa, 0x19, 0x00cd},
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+       {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID},
+       {0xa0, 0x62, ZC3XX_R192_EXPOSURELIMITLOW},
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+       {0xa0, 0x3d, ZC3XX_R197_ANTIFLICKERLOW},
+       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF},
+       {0xa0, 0x28, ZC3XX_R1AA_DIGITALGAINSTEP},
+       {0xa0, 0x04, ZC3XX_R01D_HSYNC_0},
+       {0xa0, 0x18, ZC3XX_R01E_HSYNC_1},
+       {0xa0, 0x2c, ZC3XX_R01F_HSYNC_2},
+       {0xa0, 0x41, ZC3XX_R020_HSYNC_3},
+       {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa1, 0x01, 0x0180},
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x40, ZC3XX_R116_RGAIN},
+       {0xa0, 0x40, ZC3XX_R117_GGAIN},
+       {0xa0, 0x40, ZC3XX_R118_BGAIN},
+       {}
+};
+static const struct usb_action hdcs2020b_50HZ[] = {
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+       {0xaa, 0x13, 0x0018}, /* 00,13,18,aa */
+       {0xaa, 0x14, 0x0001}, /* 00,14,01,aa */
+       {0xaa, 0x0e, 0x0005}, /* 00,0e,05,aa */
+       {0xaa, 0x19, 0x001f}, /* 00,19,1f,aa */
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+       {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,02,cc */
+       {0xa0, 0x76, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,76,cc */
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+       {0xa0, 0x46, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,46,cc */
+       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+       {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */
+       {0xa0, 0x28, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,28,cc */
+       {0xa0, 0x05, ZC3XX_R01D_HSYNC_0}, /* 00,1d,05,cc */
+       {0xa0, 0x1a, ZC3XX_R01E_HSYNC_1}, /* 00,1e,1a,cc */
+       {0xa0, 0x2f, ZC3XX_R01F_HSYNC_2}, /* 00,1f,2f,cc */
+       {}
+};
+static const struct usb_action hdcs2020b_60HZ[] = {
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+       {0xaa, 0x13, 0x0031}, /* 00,13,31,aa */
+       {0xaa, 0x14, 0x0001}, /* 00,14,01,aa */
+       {0xaa, 0x0e, 0x0004}, /* 00,0e,04,aa */
+       {0xaa, 0x19, 0x00cd}, /* 00,19,cd,aa */
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+       {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,02,cc */
+       {0xa0, 0x62, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,62,cc */
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+       {0xa0, 0x3d, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,3d,cc */
+       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+       {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */
+       {0xa0, 0x28, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,28,cc */
+       {0xa0, 0x04, ZC3XX_R01D_HSYNC_0}, /* 00,1d,04,cc */
+       {0xa0, 0x18, ZC3XX_R01E_HSYNC_1}, /* 00,1e,18,cc */
+       {0xa0, 0x2c, ZC3XX_R01F_HSYNC_2}, /* 00,1f,2c,cc */
+       {}
+};
+static const struct usb_action hdcs2020b_NoFliker[] = {
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+       {0xaa, 0x13, 0x0010}, /* 00,13,10,aa */
+       {0xaa, 0x14, 0x0001}, /* 00,14,01,aa */
+       {0xaa, 0x0e, 0x0004}, /* 00,0e,04,aa */
+       {0xaa, 0x19, 0x0000}, /* 00,19,00,aa */
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+       {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,02,cc */
+       {0xa0, 0x70, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,70,cc */
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+       {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */
+       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+       {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
+       {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
+       {0xa0, 0x04, ZC3XX_R01D_HSYNC_0}, /* 00,1d,04,cc */
+       {0xa0, 0x17, ZC3XX_R01E_HSYNC_1}, /* 00,1e,17,cc */
+       {0xa0, 0x2a, ZC3XX_R01F_HSYNC_2}, /* 00,1f,2a,cc */
+       {}
+};
+
+static const struct usb_action hv7131bxx_Initial[] = {
+       {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+       {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
+       {0xa0, 0x00, ZC3XX_R010_CMOSSENSORSELECT},
+       {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+       {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION},
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},  /* 00 */
+       {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+       {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+       {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+       {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+       {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+       {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+       {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+       {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+       {0xaa, 0x30, 0x002d},
+       {0xaa, 0x01, 0x0005},
+       {0xaa, 0x11, 0x0000},
+       {0xaa, 0x13, 0x0001},   /* {0xaa, 0x13, 0x0000}, */
+       {0xaa, 0x14, 0x0001},
+       {0xaa, 0x15, 0x00e8},
+       {0xaa, 0x16, 0x0002},
+       {0xaa, 0x17, 0x0086},
+       {0xaa, 0x31, 0x0038},
+       {0xaa, 0x32, 0x0038},
+       {0xaa, 0x33, 0x0038},
+       {0xaa, 0x5b, 0x0001},
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+       {0xa0, 0x68, ZC3XX_R18D_YTARGET},
+       {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
+       {0xa0, 0x00, 0x01ad},
+       {0xa0, 0xc0, 0x019b},
+       {0xa0, 0xa0, 0x019c},
+       {0xa0, 0x02, ZC3XX_R188_MINGAIN},
+       {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+       {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+       {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+       {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+       {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+       {0xaa, 0x02, 0x0080},   /* {0xaa, 0x02, 0x0090}; */
+       {0xa1, 0x01, 0x0002},
+       {0xa0, 0x00, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x02, ZC3XX_R090_I2CCOMMAND},
+       {0xa1, 0x01, 0x0091},
+       {0xa1, 0x01, 0x0095},
+       {0xa1, 0x01, 0x0096},
+
+       {0xa1, 0x01, 0x0008},
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},  /* clock ? */
+       {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},   /* sharpness+ */
+       {0xa1, 0x01, 0x01c8},
+       {0xa1, 0x01, 0x01c9},
+       {0xa1, 0x01, 0x01ca},
+       {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05},   /* sharpness- */
+
+       {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */
+       {0xa0, 0xf8, ZC3XX_R10B_RGB01},
+       {0xa0, 0xf8, ZC3XX_R10C_RGB02},
+       {0xa0, 0xf8, ZC3XX_R10D_RGB10},
+       {0xa0, 0x50, ZC3XX_R10E_RGB11},
+       {0xa0, 0xf8, ZC3XX_R10F_RGB12},
+       {0xa0, 0xf8, ZC3XX_R110_RGB20},
+       {0xa0, 0xf8, ZC3XX_R111_RGB21},
+       {0xa0, 0x50, ZC3XX_R112_RGB22},
+       {0xa1, 0x01, 0x0180},
+       {0xa0, 0x10, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+       {0xaa, 0x25, 0x0007},
+       {0xaa, 0x26, 0x00a1},
+       {0xaa, 0x27, 0x0020},
+       {0xaa, 0x20, 0x0000},
+       {0xaa, 0x21, 0x00a0},
+       {0xaa, 0x22, 0x0016},
+       {0xaa, 0x23, 0x0040},
+
+       {0xa0, 0x10, ZC3XX_R190_EXPOSURELIMITHIGH},     /* 2F */
+       {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID},      /* 4d */
+       {0xa0, 0x60, ZC3XX_R192_EXPOSURELIMITLOW},
+       {0xa0, 0x01, ZC3XX_R195_ANTIFLICKERHIGH},
+       {0xa0, 0x86, ZC3XX_R196_ANTIFLICKERMID},
+       {0xa0, 0xa0, ZC3XX_R197_ANTIFLICKERLOW},
+       {0xa0, 0x07, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x0f, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x18, ZC3XX_R1A9_DIGITALLIMITDIFF},
+       {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+       {0xa0, 0x00, ZC3XX_R01D_HSYNC_0},
+       {0xa0, 0xa0, ZC3XX_R01E_HSYNC_1},
+       {0xa0, 0x16, ZC3XX_R01F_HSYNC_2},
+       {0xa0, 0x40, ZC3XX_R020_HSYNC_3},
+       {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+       {0xa1, 0x01, 0x001d},
+       {0xa1, 0x01, 0x001e},
+       {0xa1, 0x01, 0x001f},
+       {0xa1, 0x01, 0x0020},
+       {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa1, 0x01, 0x0180},
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x40, ZC3XX_R116_RGAIN},
+       {0xa0, 0x40, ZC3XX_R117_GGAIN},
+       {0xa0, 0x40, ZC3XX_R118_BGAIN},
+/*     {0xa0, 0x02, ZC3XX_R008_CLOCKSETTING}, */
+       {}
+};
+
+static const struct usb_action hv7131bxx_InitialScale[] = {
+       {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+       {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
+       {0xa0, 0x00, ZC3XX_R010_CMOSSENSORSELECT},
+       {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+       {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},  /* 00 */
+       {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+       {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+       {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+       {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+       {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+       {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+       {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+       {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+       {0xaa, 0x30, 0x002d},
+       {0xaa, 0x01, 0x0005},
+       {0xaa, 0x11, 0x0001},
+       {0xaa, 0x13, 0x0000},   /* {0xaa, 0x13, 0x0001}; */
+       {0xaa, 0x14, 0x0001},
+       {0xaa, 0x15, 0x00e6},
+       {0xaa, 0x16, 0x0002},
+       {0xaa, 0x17, 0x0086},
+       {0xaa, 0x31, 0x0038},
+       {0xaa, 0x32, 0x0038},
+       {0xaa, 0x33, 0x0038},
+       {0xaa, 0x5b, 0x0001},
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+       {0xa0, 0x70, ZC3XX_R18D_YTARGET},
+       {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
+       {0xa0, 0x00, 0x01ad},
+       {0xa0, 0xc0, 0x019b},
+       {0xa0, 0xa0, 0x019c},
+       {0xa0, 0x02, ZC3XX_R188_MINGAIN},
+       {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+       {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+       {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+       {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+       {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+       {0xaa, 0x02, 0x0090},   /* {0xaa, 0x02, 0x0080}, */
+       {0xa1, 0x01, 0x0002},
+       {0xa0, 0x00, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x02, ZC3XX_R090_I2CCOMMAND},
+       {0xa1, 0x01, 0x0091},
+       {0xa1, 0x01, 0x0095},
+       {0xa1, 0x01, 0x0096},
+       {0xa1, 0x01, 0x0008},
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},  /* clock ? */
+       {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},   /* sharpness+ */
+       {0xa1, 0x01, 0x01c8},
+       {0xa1, 0x01, 0x01c9},
+       {0xa1, 0x01, 0x01ca},
+       {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05},   /* sharpness- */
+
+       {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */
+       {0xa0, 0xf8, ZC3XX_R10B_RGB01},
+       {0xa0, 0xf8, ZC3XX_R10C_RGB02},
+       {0xa0, 0xf8, ZC3XX_R10D_RGB10},
+       {0xa0, 0x50, ZC3XX_R10E_RGB11},
+       {0xa0, 0xf8, ZC3XX_R10F_RGB12},
+       {0xa0, 0xf8, ZC3XX_R110_RGB20},
+       {0xa0, 0xf8, ZC3XX_R111_RGB21},
+       {0xa0, 0x50, ZC3XX_R112_RGB22},
+       {0xa1, 0x01, 0x0180},
+       {0xa0, 0x10, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+       {0xaa, 0x25, 0x0007},
+       {0xaa, 0x26, 0x00a1},
+       {0xaa, 0x27, 0x0020},
+       {0xaa, 0x20, 0x0000},
+       {0xaa, 0x21, 0x0040},
+       {0xaa, 0x22, 0x0013},
+       {0xaa, 0x23, 0x004c},
+       {0xa0, 0x10, ZC3XX_R190_EXPOSURELIMITHIGH},     /* 2f */
+       {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID},      /* 4d */
+       {0xa0, 0x60, ZC3XX_R192_EXPOSURELIMITLOW},      /* 60 */
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+       {0xa0, 0xc3, ZC3XX_R196_ANTIFLICKERMID},
+       {0xa0, 0x50, ZC3XX_R197_ANTIFLICKERLOW},
+       {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x18, ZC3XX_R1A9_DIGITALLIMITDIFF},
+       {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+       {0xa0, 0x00, ZC3XX_R01D_HSYNC_0},
+       {0xa0, 0x40, ZC3XX_R01E_HSYNC_1},
+       {0xa0, 0x13, ZC3XX_R01F_HSYNC_2},
+       {0xa0, 0x4c, ZC3XX_R020_HSYNC_3},
+       {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+       {0xa1, 0x01, 0x001d},
+       {0xa1, 0x01, 0x001e},
+       {0xa1, 0x01, 0x001f},
+       {0xa1, 0x01, 0x0020},
+       {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa1, 0x01, 0x0180},
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x40, ZC3XX_R116_RGAIN},
+       {0xa0, 0x40, ZC3XX_R117_GGAIN},
+       {0xa0, 0x40, ZC3XX_R118_BGAIN},
+/*     {0xa0, 0x02, ZC3XX_R008_CLOCKSETTING}, */
+       {}
+};
+
+static const struct usb_action hv7131cxx_Initial[] = {
+       {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+       {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
+       {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT},
+       {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+       {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION},
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+       {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+       {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+       {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+       {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+       {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+       {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH},
+       {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW},
+       {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH},
+       {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW},
+       {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+       {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xaa, 0x01, 0x000c},
+       {0xaa, 0x11, 0x0000},
+       {0xaa, 0x13, 0x0000},
+       {0xaa, 0x14, 0x0001},
+       {0xaa, 0x15, 0x00e8},
+       {0xaa, 0x16, 0x0002},
+       {0xaa, 0x17, 0x0088},
+
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+       {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+       {0xa0, 0x89, ZC3XX_R18D_YTARGET},
+       {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN},
+       {0xa0, 0x00, 0x01ad},
+       {0xa0, 0xc0, 0x019b},
+       {0xa0, 0xa0, 0x019c},
+       {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+       {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+       {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+       {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+       {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+       {0xa1, 0x01, 0x0002},
+       {0xa0, 0x00, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x02, ZC3XX_R090_I2CCOMMAND},
+       {0xa1, 0x01, 0x0091},
+       {0xa1, 0x01, 0x0095},
+       {0xa1, 0x01, 0x0096},
+
+       {0xa1, 0x01, 0x0008},
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},  /* clock ? */
+       {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},   /* sharpness+ */
+       {0xa1, 0x01, 0x01c8},
+       {0xa1, 0x01, 0x01c9},
+       {0xa1, 0x01, 0x01ca},
+       {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05},   /* sharpness- */
+
+       {0xa0, 0x60, ZC3XX_R10A_RGB00}, /* matrix */
+       {0xa0, 0xf0, ZC3XX_R10B_RGB01},
+       {0xa0, 0xf0, ZC3XX_R10C_RGB02},
+       {0xa0, 0xf0, ZC3XX_R10D_RGB10},
+       {0xa0, 0x60, ZC3XX_R10E_RGB11},
+       {0xa0, 0xf0, ZC3XX_R10F_RGB12},
+       {0xa0, 0xf0, ZC3XX_R110_RGB20},
+       {0xa0, 0xf0, ZC3XX_R111_RGB21},
+       {0xa0, 0x60, ZC3XX_R112_RGB22},
+       {0xa1, 0x01, 0x0180},
+       {0xa0, 0x10, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+       {0xaa, 0x25, 0x0007},
+       {0xaa, 0x26, 0x0053},
+       {0xaa, 0x27, 0x0000},
+
+       {0xa0, 0x10, ZC3XX_R190_EXPOSURELIMITHIGH},     /* 2f */
+       {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID},      /* 9b */
+       {0xa0, 0x60, ZC3XX_R192_EXPOSURELIMITLOW},      /* 80 */
+       {0xa0, 0x01, ZC3XX_R195_ANTIFLICKERHIGH},
+       {0xa0, 0xd4, ZC3XX_R196_ANTIFLICKERMID},
+       {0xa0, 0xc0, ZC3XX_R197_ANTIFLICKERLOW},
+       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
+       {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
+       {0xa0, 0x13, ZC3XX_R1AA_DIGITALGAINSTEP},
+       {0xa1, 0x01, 0x001d},
+       {0xa1, 0x01, 0x001e},
+       {0xa1, 0x01, 0x001f},
+       {0xa1, 0x01, 0x0020},
+       {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa1, 0x01, 0x0180},
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+       {}
+};
+
+static const struct usb_action hv7131cxx_InitialScale[] = {
+       {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+
+       {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},   /* diff */
+       {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT},
+       {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+       {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION},
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+
+       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
+
+       {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+       {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+       {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+       {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},        /* 1e0 */
+
+       {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+       {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+       {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH},
+       {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW},
+       {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH},
+       {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW},
+       {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+       {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xaa, 0x01, 0x000c},
+       {0xaa, 0x11, 0x0000},
+       {0xaa, 0x13, 0x0000},
+       {0xaa, 0x14, 0x0001},
+       {0xaa, 0x15, 0x00e8},
+       {0xaa, 0x16, 0x0002},
+       {0xaa, 0x17, 0x0088},
+
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00 */
+
+       {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+       {0xa0, 0x89, ZC3XX_R18D_YTARGET},
+       {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN},
+       {0xa0, 0x00, 0x01ad},
+       {0xa0, 0xc0, 0x019b},
+       {0xa0, 0xa0, 0x019c},
+       {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+       {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+       {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+       {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+       {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+       {0xa1, 0x01, 0x0002},
+       {0xa0, 0x00, ZC3XX_R092_I2CADDRESSSELECT},
+                                               /* read the i2c chips ident */
+       {0xa0, 0x02, ZC3XX_R090_I2CCOMMAND},
+       {0xa1, 0x01, 0x0091},
+       {0xa1, 0x01, 0x0095},
+       {0xa1, 0x01, 0x0096},
+
+       {0xa1, 0x01, 0x0008},
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},  /* clock ? */
+       {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},   /* sharpness+ */
+       {0xa1, 0x01, 0x01c8},
+       {0xa1, 0x01, 0x01c9},
+       {0xa1, 0x01, 0x01ca},
+       {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05},   /* sharpness- */
+
+       {0xa0, 0x60, ZC3XX_R10A_RGB00}, /* matrix */
+       {0xa0, 0xf0, ZC3XX_R10B_RGB01},
+       {0xa0, 0xf0, ZC3XX_R10C_RGB02},
+       {0xa0, 0xf0, ZC3XX_R10D_RGB10},
+       {0xa0, 0x60, ZC3XX_R10E_RGB11},
+       {0xa0, 0xf0, ZC3XX_R10F_RGB12},
+       {0xa0, 0xf0, ZC3XX_R110_RGB20},
+       {0xa0, 0xf0, ZC3XX_R111_RGB21},
+       {0xa0, 0x60, ZC3XX_R112_RGB22},
+       {0xa1, 0x01, 0x0180},
+       {0xa0, 0x10, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+       {0xaa, 0x25, 0x0007},
+       {0xaa, 0x26, 0x0053},
+       {0xaa, 0x27, 0x0000},
+
+       {0xa0, 0x10, ZC3XX_R190_EXPOSURELIMITHIGH},     /* 2f */
+       {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID},      /* 9b */
+       {0xa0, 0x60, ZC3XX_R192_EXPOSURELIMITLOW},      /* 80 */
+
+       {0xa0, 0x01, ZC3XX_R195_ANTIFLICKERHIGH},
+       {0xa0, 0xd4, ZC3XX_R196_ANTIFLICKERMID},
+       {0xa0, 0xc0, ZC3XX_R197_ANTIFLICKERLOW},
+
+       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
+       {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
+       {0xa0, 0x13, ZC3XX_R1AA_DIGITALGAINSTEP},
+       {0xa1, 0x01, 0x001d},
+       {0xa1, 0x01, 0x001e},
+       {0xa1, 0x01, 0x001f},
+       {0xa1, 0x01, 0x0020},
+       {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa1, 0x01, 0x0180},
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+       {}
+};
+
+static const struct usb_action icm105axx_Initial[] = {
+       {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+       {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+       {0xa0, 0x0c, ZC3XX_R010_CMOSSENSORSELECT},
+       {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+       {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+       {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+       {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+       {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+       {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0xa1, ZC3XX_R08B_I2CDEVICEADDR},
+       {0xa0, 0x00, ZC3XX_R097_WINYSTARTHIGH},
+       {0xa0, 0x01, ZC3XX_R098_WINYSTARTLOW},
+       {0xa0, 0x00, ZC3XX_R099_WINXSTARTHIGH},
+       {0xa0, 0x01, ZC3XX_R09A_WINXSTARTLOW},
+       {0xa0, 0x01, ZC3XX_R11A_FIRSTYLOW},
+       {0xa0, 0x01, ZC3XX_R11C_FIRSTXLOW},
+       {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH},
+       {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW},
+       {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH},
+       {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW},
+       {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
+       {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+       {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+       {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+       {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+       {0xaa, 0x01, 0x0010},
+       {0xaa, 0x03, 0x0000},
+       {0xaa, 0x04, 0x0001},
+       {0xaa, 0x05, 0x0020},
+       {0xaa, 0x06, 0x0001},
+       {0xaa, 0x08, 0x0000},
+       {0xaa, 0x03, 0x0001},
+       {0xaa, 0x04, 0x0011},
+       {0xaa, 0x05, 0x00a0},
+       {0xaa, 0x06, 0x0001},
+       {0xaa, 0x08, 0x0000},
+       {0xaa, 0x03, 0x0002},
+       {0xaa, 0x04, 0x0013},
+       {0xaa, 0x05, 0x0020},
+       {0xaa, 0x06, 0x0001},
+       {0xaa, 0x08, 0x0000},
+       {0xaa, 0x03, 0x0003},
+       {0xaa, 0x04, 0x0015},
+       {0xaa, 0x05, 0x0020},
+       {0xaa, 0x06, 0x0005},
+       {0xaa, 0x08, 0x0000},
+       {0xaa, 0x03, 0x0004},
+       {0xaa, 0x04, 0x0017},
+       {0xaa, 0x05, 0x0020},
+       {0xaa, 0x06, 0x000d},
+       {0xaa, 0x08, 0x0000},
+       {0xaa, 0x03, 0x0005},
+       {0xaa, 0x04, 0x0019},
+       {0xaa, 0x05, 0x0020},
+       {0xaa, 0x06, 0x0005},
+       {0xaa, 0x08, 0x0000},
+       {0xaa, 0x03, 0x0006},
+       {0xaa, 0x04, 0x0017},
+       {0xaa, 0x05, 0x0026},
+       {0xaa, 0x06, 0x0005},
+       {0xaa, 0x08, 0x0000},
+       {0xaa, 0x03, 0x0007},
+       {0xaa, 0x04, 0x0019},
+       {0xaa, 0x05, 0x0022},
+       {0xaa, 0x06, 0x0005},
+       {0xaa, 0x08, 0x0000},
+       {0xaa, 0x03, 0x0008},
+       {0xaa, 0x04, 0x0021},
+       {0xaa, 0x05, 0x00aa},
+       {0xaa, 0x06, 0x0005},
+       {0xaa, 0x08, 0x0000},
+       {0xaa, 0x03, 0x0009},
+       {0xaa, 0x04, 0x0023},
+       {0xaa, 0x05, 0x00aa},
+       {0xaa, 0x06, 0x000d},
+       {0xaa, 0x08, 0x0000},
+       {0xaa, 0x03, 0x000a},
+       {0xaa, 0x04, 0x0025},
+       {0xaa, 0x05, 0x00aa},
+       {0xaa, 0x06, 0x0005},
+       {0xaa, 0x08, 0x0000},
+       {0xaa, 0x03, 0x000b},
+       {0xaa, 0x04, 0x00ec},
+       {0xaa, 0x05, 0x002e},
+       {0xaa, 0x06, 0x0005},
+       {0xaa, 0x08, 0x0000},
+       {0xaa, 0x03, 0x000c},
+       {0xaa, 0x04, 0x00fa},
+       {0xaa, 0x05, 0x002a},
+       {0xaa, 0x06, 0x0005},
+       {0xaa, 0x08, 0x0000},
+       {0xaa, 0x07, 0x000d},
+       {0xaa, 0x01, 0x0005},
+       {0xaa, 0x94, 0x0002},
+       {0xaa, 0x90, 0x0000},
+       {0xaa, 0x91, 0x001f},
+       {0xaa, 0x10, 0x0064},
+       {0xaa, 0x9b, 0x00f0},
+       {0xaa, 0x9c, 0x0002},
+       {0xaa, 0x14, 0x001a},
+       {0xaa, 0x20, 0x0080},
+       {0xaa, 0x22, 0x0080},
+       {0xaa, 0x24, 0x0080},
+       {0xaa, 0x26, 0x0080},
+       {0xaa, 0x00, 0x0084},
+       {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+       {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+       {0xaa, 0xa8, 0x00c0},
+       {0xa1, 0x01, 0x0002},
+       {0xa1, 0x01, 0x0008},
+       {0xa1, 0x01, 0x0180},
+       {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x40, ZC3XX_R116_RGAIN},
+       {0xa0, 0x40, ZC3XX_R117_GGAIN},
+       {0xa0, 0x40, ZC3XX_R118_BGAIN},
+       {0xa1, 0x01, 0x0008},
+
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},  /* clock ? */
+       {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},   /* sharpness+ */
+       {0xa1, 0x01, 0x01c8},
+       {0xa1, 0x01, 0x01c9},
+       {0xa1, 0x01, 0x01ca},
+       {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05},   /* sharpness- */
+       {0xa0, 0x52, ZC3XX_R10A_RGB00}, /* matrix */
+       {0xa0, 0xf7, ZC3XX_R10B_RGB01},
+       {0xa0, 0xf7, ZC3XX_R10C_RGB02},
+       {0xa0, 0xf7, ZC3XX_R10D_RGB10},
+       {0xa0, 0x52, ZC3XX_R10E_RGB11},
+       {0xa0, 0xf7, ZC3XX_R10F_RGB12},
+       {0xa0, 0xf7, ZC3XX_R110_RGB20},
+       {0xa0, 0xf7, ZC3XX_R111_RGB21},
+       {0xa0, 0x52, ZC3XX_R112_RGB22},
+       {0xa1, 0x01, 0x0180},
+       {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+       {0xaa, 0x0d, 0x0003},
+       {0xaa, 0x0c, 0x008c},
+       {0xaa, 0x0e, 0x0095},
+       {0xaa, 0x0f, 0x0002},
+       {0xaa, 0x1c, 0x0094},
+       {0xaa, 0x1d, 0x0002},
+       {0xaa, 0x20, 0x0080},
+       {0xaa, 0x22, 0x0080},
+       {0xaa, 0x24, 0x0080},
+       {0xaa, 0x26, 0x0080},
+       {0xaa, 0x00, 0x0084},
+       {0xa0, 0x02, ZC3XX_R0A3_EXPOSURETIMEHIGH},
+       {0xa0, 0x94, ZC3XX_R0A4_EXPOSURETIMELOW},
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+       {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID},
+       {0xa0, 0x20, ZC3XX_R192_EXPOSURELIMITLOW},
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+       {0xa0, 0x84, ZC3XX_R197_ANTIFLICKERLOW},
+       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
+       {0xa0, 0x12, ZC3XX_R1AA_DIGITALGAINSTEP},
+       {0xa0, 0xe3, ZC3XX_R01D_HSYNC_0},
+       {0xa0, 0xec, ZC3XX_R01E_HSYNC_1},
+       {0xa0, 0xf5, ZC3XX_R01F_HSYNC_2},
+       {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+       {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+       {0xa0, 0xc0, ZC3XX_R1A8_DIGITALGAIN},
+       {0xa0, 0xc0, ZC3XX_R11D_GLOBALGAIN},
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa1, 0x01, 0x0180},
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x40, ZC3XX_R116_RGAIN},
+       {0xa0, 0x40, ZC3XX_R117_GGAIN},
+       {0xa0, 0x40, ZC3XX_R118_BGAIN},
+       {}
+};
+
+static const struct usb_action icm105axx_InitialScale[] = {
+       {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+       {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+       {0xa0, 0x0c, ZC3XX_R010_CMOSSENSORSELECT},
+       {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+       {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+       {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+       {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+       {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+       {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0xa1, ZC3XX_R08B_I2CDEVICEADDR},
+       {0xa0, 0x00, ZC3XX_R097_WINYSTARTHIGH},
+       {0xa0, 0x02, ZC3XX_R098_WINYSTARTLOW},
+       {0xa0, 0x00, ZC3XX_R099_WINXSTARTHIGH},
+       {0xa0, 0x02, ZC3XX_R09A_WINXSTARTLOW},
+       {0xa0, 0x02, ZC3XX_R11A_FIRSTYLOW},
+       {0xa0, 0x02, ZC3XX_R11C_FIRSTXLOW},
+       {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH},
+       {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW},
+       {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH},
+       {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW},
+       {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION},
+       {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+       {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+       {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+       {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+       {0xaa, 0x01, 0x0010},
+       {0xaa, 0x03, 0x0000},
+       {0xaa, 0x04, 0x0001},
+       {0xaa, 0x05, 0x0020},
+       {0xaa, 0x06, 0x0001},
+       {0xaa, 0x08, 0x0000},
+       {0xaa, 0x03, 0x0001},
+       {0xaa, 0x04, 0x0011},
+       {0xaa, 0x05, 0x00a0},
+       {0xaa, 0x06, 0x0001},
+       {0xaa, 0x08, 0x0000},
+       {0xaa, 0x03, 0x0002},
+       {0xaa, 0x04, 0x0013},
+       {0xaa, 0x05, 0x0020},
+       {0xaa, 0x06, 0x0001},
+       {0xaa, 0x08, 0x0000},
+       {0xaa, 0x03, 0x0003},
+       {0xaa, 0x04, 0x0015},
+       {0xaa, 0x05, 0x0020},
+       {0xaa, 0x06, 0x0005},
+       {0xaa, 0x08, 0x0000},
+       {0xaa, 0x03, 0x0004},
+       {0xaa, 0x04, 0x0017},
+       {0xaa, 0x05, 0x0020},
+       {0xaa, 0x06, 0x000d},
+       {0xaa, 0x08, 0x0000},
+       {0xaa, 0x03, 0x0005},
+       {0xa0, 0x04, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x19, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa1, 0x01, 0x0091},
+       {0xaa, 0x05, 0x0020},
+       {0xaa, 0x06, 0x0005},
+       {0xaa, 0x08, 0x0000},
+       {0xaa, 0x03, 0x0006},
+       {0xaa, 0x04, 0x0017},
+       {0xaa, 0x05, 0x0026},
+       {0xaa, 0x06, 0x0005},
+       {0xaa, 0x08, 0x0000},
+       {0xaa, 0x03, 0x0007},
+       {0xaa, 0x04, 0x0019},
+       {0xaa, 0x05, 0x0022},
+       {0xaa, 0x06, 0x0005},
+       {0xaa, 0x08, 0x0000},
+       {0xaa, 0x03, 0x0008},
+       {0xaa, 0x04, 0x0021},
+       {0xaa, 0x05, 0x00aa},
+       {0xaa, 0x06, 0x0005},
+       {0xaa, 0x08, 0x0000},
+       {0xaa, 0x03, 0x0009},
+       {0xaa, 0x04, 0x0023},
+       {0xaa, 0x05, 0x00aa},
+       {0xaa, 0x06, 0x000d},
+       {0xaa, 0x08, 0x0000},
+       {0xaa, 0x03, 0x000a},
+       {0xaa, 0x04, 0x0025},
+       {0xaa, 0x05, 0x00aa},
+       {0xaa, 0x06, 0x0005},
+       {0xaa, 0x08, 0x0000},
+       {0xaa, 0x03, 0x000b},
+       {0xaa, 0x04, 0x00ec},
+       {0xaa, 0x05, 0x002e},
+       {0xaa, 0x06, 0x0005},
+       {0xaa, 0x08, 0x0000},
+       {0xaa, 0x03, 0x000c},
+       {0xaa, 0x04, 0x00fa},
+       {0xaa, 0x05, 0x002a},
+       {0xaa, 0x06, 0x0005},
+       {0xaa, 0x08, 0x0000},
+       {0xaa, 0x07, 0x000d},
+       {0xaa, 0x01, 0x0005},
+       {0xaa, 0x94, 0x0002},
+       {0xaa, 0x90, 0x0000},
+       {0xaa, 0x91, 0x0010},
+       {0xaa, 0x10, 0x0064},
+       {0xaa, 0x9b, 0x00f0},
+       {0xaa, 0x9c, 0x0002},
+       {0xaa, 0x14, 0x001a},
+       {0xaa, 0x20, 0x0080},
+       {0xaa, 0x22, 0x0080},
+       {0xaa, 0x24, 0x0080},
+       {0xaa, 0x26, 0x0080},
+       {0xaa, 0x00, 0x0084},
+       {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+       {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+       {0xaa, 0xa8, 0x0080},
+       {0xa0, 0x78, ZC3XX_R18D_YTARGET},
+       {0xa1, 0x01, 0x0002},
+       {0xa1, 0x01, 0x0008},
+       {0xa1, 0x01, 0x0180},
+       {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x40, ZC3XX_R116_RGAIN},
+       {0xa0, 0x40, ZC3XX_R117_GGAIN},
+       {0xa0, 0x40, ZC3XX_R118_BGAIN},
+       {0xa1, 0x01, 0x0008},
+
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},  /* clock ? */
+       {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},   /* sharpness+ */
+       {0xa1, 0x01, 0x01c8},
+       {0xa1, 0x01, 0x01c9},
+       {0xa1, 0x01, 0x01ca},
+       {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05},   /* sharpness- */
+
+       {0xa0, 0x52, ZC3XX_R10A_RGB00}, /* matrix */
+       {0xa0, 0xf7, ZC3XX_R10B_RGB01},
+       {0xa0, 0xf7, ZC3XX_R10C_RGB02},
+       {0xa0, 0xf7, ZC3XX_R10D_RGB10},
+       {0xa0, 0x52, ZC3XX_R10E_RGB11},
+       {0xa0, 0xf7, ZC3XX_R10F_RGB12},
+       {0xa0, 0xf7, ZC3XX_R110_RGB20},
+       {0xa0, 0xf7, ZC3XX_R111_RGB21},
+       {0xa0, 0x52, ZC3XX_R112_RGB22},
+       {0xa1, 0x01, 0x0180},
+       {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+       {0xaa, 0x0d, 0x0003},
+       {0xaa, 0x0c, 0x0020},
+       {0xaa, 0x0e, 0x000e},
+       {0xaa, 0x0f, 0x0002},
+       {0xaa, 0x1c, 0x000d},
+       {0xaa, 0x1d, 0x0002},
+       {0xaa, 0x20, 0x0080},
+       {0xaa, 0x22, 0x0080},
+       {0xaa, 0x24, 0x0080},
+       {0xaa, 0x26, 0x0080},
+       {0xaa, 0x00, 0x0084},
+       {0xa0, 0x02, ZC3XX_R0A3_EXPOSURETIMEHIGH},
+       {0xa0, 0x0d, ZC3XX_R0A4_EXPOSURETIMELOW},
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+       {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID},
+       {0xa0, 0x1a, ZC3XX_R192_EXPOSURELIMITLOW},
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+       {0xa0, 0x4b, ZC3XX_R197_ANTIFLICKERLOW},
+       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
+       {0xa0, 0x12, ZC3XX_R1AA_DIGITALGAINSTEP},
+       {0xa0, 0xc8, ZC3XX_R01D_HSYNC_0},
+       {0xa0, 0xd8, ZC3XX_R01E_HSYNC_1},
+       {0xa0, 0xea, ZC3XX_R01F_HSYNC_2},
+       {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+       {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa1, 0x01, 0x0180},
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x40, ZC3XX_R116_RGAIN},
+       {0xa0, 0x40, ZC3XX_R117_GGAIN},
+       {0xa0, 0x40, ZC3XX_R118_BGAIN},
+       {}
+};
+static const struct usb_action icm105a_50HZ[] = {
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+       {0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */
+       {0xaa, 0x0c, 0x0020}, /* 00,0c,20,aa */
+       {0xaa, 0x0e, 0x000e}, /* 00,0e,0e,aa */
+       {0xaa, 0x0f, 0x0002}, /* 00,0f,02,aa */
+       {0xaa, 0x1c, 0x000d}, /* 00,1c,0d,aa */
+       {0xaa, 0x1d, 0x0002}, /* 00,1d,02,aa */
+       {0xaa, 0x20, 0x0080}, /* 00,20,80,aa */
+       {0xaa, 0x22, 0x0080}, /* 00,22,80,aa */
+       {0xaa, 0x24, 0x0080}, /* 00,24,80,aa */
+       {0xaa, 0x26, 0x0080}, /* 00,26,80,aa */
+       {0xaa, 0x00, 0x0084}, /* 00,00,84,aa */
+       {0xa0, 0x02, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,02,cc */
+       {0xa0, 0x0d, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,0d,cc */
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+       {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */
+       {0xa0, 0x1a, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,1a,cc */
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+       {0xa0, 0x4b, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,4b,cc */
+       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+       {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */
+       {0xa0, 0x12, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,12,cc */
+       {0xa0, 0xc8, ZC3XX_R01D_HSYNC_0}, /* 00,1d,c8,cc */
+       {0xa0, 0xd8, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d8,cc */
+       {0xa0, 0xea, ZC3XX_R01F_HSYNC_2}, /* 00,1f,ea,cc */
+       {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+       {}
+};
+static const struct usb_action icm105a_50HZScale[] = {
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+       {0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */
+       {0xaa, 0x0c, 0x008c}, /* 00,0c,8c,aa */
+       {0xaa, 0x0e, 0x0095}, /* 00,0e,95,aa */
+       {0xaa, 0x0f, 0x0002}, /* 00,0f,02,aa */
+       {0xaa, 0x1c, 0x0094}, /* 00,1c,94,aa */
+       {0xaa, 0x1d, 0x0002}, /* 00,1d,02,aa */
+       {0xaa, 0x20, 0x0080}, /* 00,20,80,aa */
+       {0xaa, 0x22, 0x0080}, /* 00,22,80,aa */
+       {0xaa, 0x24, 0x0080}, /* 00,24,80,aa */
+       {0xaa, 0x26, 0x0080}, /* 00,26,80,aa */
+       {0xaa, 0x00, 0x0084}, /* 00,00,84,aa */
+       {0xa0, 0x02, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,02,cc */
+       {0xa0, 0x94, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,94,cc */
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+       {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */
+       {0xa0, 0x20, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,20,cc */
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+       {0xa0, 0x84, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,84,cc */
+       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+       {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */
+       {0xa0, 0x12, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,12,cc */
+       {0xa0, 0xe3, ZC3XX_R01D_HSYNC_0}, /* 00,1d,e3,cc */
+       {0xa0, 0xec, ZC3XX_R01E_HSYNC_1}, /* 00,1e,ec,cc */
+       {0xa0, 0xf5, ZC3XX_R01F_HSYNC_2}, /* 00,1f,f5,cc */
+       {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+       {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, /* 01,a7,00,cc */
+       {0xa0, 0xc0, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,c0,cc */
+       {}
+};
+static const struct usb_action icm105a_60HZ[] = {
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+       {0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */
+       {0xaa, 0x0c, 0x0004}, /* 00,0c,04,aa */
+       {0xaa, 0x0e, 0x000d}, /* 00,0e,0d,aa */
+       {0xaa, 0x0f, 0x0002}, /* 00,0f,02,aa */
+       {0xaa, 0x1c, 0x0008}, /* 00,1c,08,aa */
+       {0xaa, 0x1d, 0x0002}, /* 00,1d,02,aa */
+       {0xaa, 0x20, 0x0080}, /* 00,20,80,aa */
+       {0xaa, 0x22, 0x0080}, /* 00,22,80,aa */
+       {0xaa, 0x24, 0x0080}, /* 00,24,80,aa */
+       {0xaa, 0x26, 0x0080}, /* 00,26,80,aa */
+       {0xaa, 0x00, 0x0084}, /* 00,00,84,aa */
+       {0xa0, 0x02, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,02,cc */
+       {0xa0, 0x08, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,08,cc */
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+       {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */
+       {0xa0, 0x10, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,10,cc */
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+       {0xa0, 0x41, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,41,cc */
+       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+       {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */
+       {0xa0, 0x12, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,12,cc */
+       {0xa0, 0xc1, ZC3XX_R01D_HSYNC_0}, /* 00,1d,c1,cc */
+       {0xa0, 0xd4, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d4,cc */
+       {0xa0, 0xe8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e8,cc */
+       {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+       {}
+};
+static const struct usb_action icm105a_60HZScale[] = {
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+       {0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */
+       {0xaa, 0x0c, 0x0008}, /* 00,0c,08,aa */
+       {0xaa, 0x0e, 0x0086}, /* 00,0e,86,aa */
+       {0xaa, 0x0f, 0x0002}, /* 00,0f,02,aa */
+       {0xaa, 0x1c, 0x0085}, /* 00,1c,85,aa */
+       {0xaa, 0x1d, 0x0002}, /* 00,1d,02,aa */
+       {0xaa, 0x20, 0x0080}, /* 00,20,80,aa */
+       {0xaa, 0x22, 0x0080}, /* 00,22,80,aa */
+       {0xaa, 0x24, 0x0080}, /* 00,24,80,aa */
+       {0xaa, 0x26, 0x0080}, /* 00,26,80,aa */
+       {0xaa, 0x00, 0x0084}, /* 00,00,84,aa */
+       {0xa0, 0x02, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,02,cc */
+       {0xa0, 0x85, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,85,cc */
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+       {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */
+       {0xa0, 0x08, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,08,cc */
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+       {0xa0, 0x81, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,81,cc */
+       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+       {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */
+       {0xa0, 0x12, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,12,cc */
+       {0xa0, 0xc2, ZC3XX_R01D_HSYNC_0}, /* 00,1d,c2,cc */
+       {0xa0, 0xd6, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d6,cc */
+       {0xa0, 0xea, ZC3XX_R01F_HSYNC_2}, /* 00,1f,ea,cc */
+       {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+       {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, /* 01,a7,00,cc */
+       {0xa0, 0xc0, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,c0,cc */
+       {}
+};
+static const struct usb_action icm105a_NoFliker[] = {
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+       {0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */
+       {0xaa, 0x0c, 0x0004}, /* 00,0c,04,aa */
+       {0xaa, 0x0e, 0x000d}, /* 00,0e,0d,aa */
+       {0xaa, 0x0f, 0x0002}, /* 00,0f,02,aa */
+       {0xaa, 0x1c, 0x0000}, /* 00,1c,00,aa */
+       {0xaa, 0x1d, 0x0002}, /* 00,1d,02,aa */
+       {0xaa, 0x20, 0x0080}, /* 00,20,80,aa */
+       {0xaa, 0x22, 0x0080}, /* 00,22,80,aa */
+       {0xaa, 0x24, 0x0080}, /* 00,24,80,aa */
+       {0xaa, 0x26, 0x0080}, /* 00,26,80,aa */
+       {0xaa, 0x00, 0x0084}, /* 00,00,84,aa */
+       {0xa0, 0x02, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,02,cc */
+       {0xa0, 0x00, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,00,cc */
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+       {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */
+       {0xa0, 0x20, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,20,cc */
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+       {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */
+       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+       {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
+       {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
+       {0xa0, 0xc1, ZC3XX_R01D_HSYNC_0}, /* 00,1d,c1,cc */
+       {0xa0, 0xd4, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d4,cc */
+       {0xa0, 0xe8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e8,cc */
+       {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+       {}
+};
+static const struct usb_action icm105a_NoFlikerScale[] = {
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+       {0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */
+       {0xaa, 0x0c, 0x0004}, /* 00,0c,04,aa */
+       {0xaa, 0x0e, 0x0081}, /* 00,0e,81,aa */
+       {0xaa, 0x0f, 0x0002}, /* 00,0f,02,aa */
+       {0xaa, 0x1c, 0x0080}, /* 00,1c,80,aa */
+       {0xaa, 0x1d, 0x0002}, /* 00,1d,02,aa */
+       {0xaa, 0x20, 0x0080}, /* 00,20,80,aa */
+       {0xaa, 0x22, 0x0080}, /* 00,22,80,aa */
+       {0xaa, 0x24, 0x0080}, /* 00,24,80,aa */
+       {0xaa, 0x26, 0x0080}, /* 00,26,80,aa */
+       {0xaa, 0x00, 0x0084}, /* 00,00,84,aa */
+       {0xa0, 0x02, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,02,cc */
+       {0xa0, 0x80, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,80,cc */
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+       {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */
+       {0xa0, 0x20, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,20,cc */
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+       {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */
+       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+       {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
+       {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
+       {0xa0, 0xc1, ZC3XX_R01D_HSYNC_0}, /* 00,1d,c1,cc */
+       {0xa0, 0xd4, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d4,cc */
+       {0xa0, 0xe8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e8,cc */
+       {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+       {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, /* 01,a7,00,cc */
+       {0xa0, 0xc0, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,c0,cc */
+       {}
+};
+
+static const struct usb_action MC501CB_InitialScale[] = {
+       {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
+       {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, /* 00,02,00,cc */
+       {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
+       {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00,08,03,cc */
+       {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */
+       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */
+       {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */
+       {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */
+       {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */
+       {0xa0, 0xd8, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,d8,cc */
+       {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */
+       {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */
+       {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */
+       {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */
+       {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH}, /* 00,9b,01,cc */
+       {0xa0, 0xde, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,de,cc */
+       {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, /* 00,9d,02,cc */
+       {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,86,cc */
+       {0xa0, 0x33, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,33,cc */
+       {0xa0, 0x34, ZC3XX_R087_EXPTIMEMID}, /* 00,87,34,cc */
+       {0xa0, 0x35, ZC3XX_R088_EXPTIMELOW}, /* 00,88,35,cc */
+       {0xa0, 0xb0, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,b0,cc */
+       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */
+       {0xaa, 0x01, 0x0001}, /* 00,01,01,aa */
+       {0xaa, 0x01, 0x0003}, /* 00,01,03,aa */
+       {0xaa, 0x01, 0x0001}, /* 00,01,01,aa */
+       {0xaa, 0x03, 0x0000}, /* 00,03,00,aa */
+       {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */
+       {0xaa, 0x11, 0x0080}, /* 00,11,80,aa */
+       {0xaa, 0x12, 0x0000}, /* 00,12,00,aa */
+       {0xaa, 0x13, 0x0000}, /* 00,13,00,aa */
+       {0xaa, 0x14, 0x0000}, /* 00,14,00,aa */
+       {0xaa, 0x15, 0x0000}, /* 00,15,00,aa */
+       {0xaa, 0x16, 0x0000}, /* 00,16,00,aa */
+       {0xaa, 0x17, 0x0001}, /* 00,17,01,aa */
+       {0xaa, 0x18, 0x00de}, /* 00,18,de,aa */
+       {0xaa, 0x19, 0x0002}, /* 00,19,02,aa */
+       {0xaa, 0x1a, 0x0086}, /* 00,1a,86,aa */
+       {0xaa, 0x20, 0x00a8}, /* 00,20,a8,aa */
+       {0xaa, 0x22, 0x0000}, /* 00,22,00,aa */
+       {0xaa, 0x23, 0x0000}, /* 00,23,00,aa */
+       {0xaa, 0x24, 0x0000}, /* 00,24,00,aa */
+       {0xaa, 0x40, 0x0033}, /* 00,40,33,aa */
+       {0xaa, 0x41, 0x0077}, /* 00,41,77,aa */
+       {0xaa, 0x42, 0x0053}, /* 00,42,53,aa */
+       {0xaa, 0x43, 0x00b0}, /* 00,43,b0,aa */
+       {0xaa, 0x4b, 0x0001}, /* 00,4b,01,aa */
+       {0xaa, 0x72, 0x0020}, /* 00,72,20,aa */
+       {0xaa, 0x73, 0x0000}, /* 00,73,00,aa */
+       {0xaa, 0x80, 0x0000}, /* 00,80,00,aa */
+       {0xaa, 0x85, 0x0050}, /* 00,85,50,aa */
+       {0xaa, 0x91, 0x0070}, /* 00,91,70,aa */
+       {0xaa, 0x92, 0x0072}, /* 00,92,72,aa */
+       {0xaa, 0x03, 0x0001}, /* 00,03,01,aa */
+       {0xaa, 0x10, 0x00a0}, /* 00,10,a0,aa */
+       {0xaa, 0x11, 0x0001}, /* 00,11,01,aa */
+       {0xaa, 0x30, 0x0000}, /* 00,30,00,aa */
+       {0xaa, 0x60, 0x0000}, /* 00,60,00,aa */
+       {0xaa, 0xa0, ZC3XX_R01A_LASTFRAMESTATE}, /* 00,a0,1a,aa */
+       {0xaa, 0xa1, 0x0000}, /* 00,a1,00,aa */
+       {0xaa, 0xa2, 0x003f}, /* 00,a2,3f,aa */
+       {0xaa, 0xa3, 0x0028}, /* 00,a3,28,aa */
+       {0xaa, 0xa4, 0x0010}, /* 00,a4,10,aa */
+       {0xaa, 0xa5, 0x0020}, /* 00,a5,20,aa */
+       {0xaa, 0xb1, 0x0044}, /* 00,b1,44,aa */
+       {0xaa, 0xd0, 0x0001}, /* 00,d0,01,aa */
+       {0xaa, 0xd1, 0x0085}, /* 00,d1,85,aa */
+       {0xaa, 0xd2, 0x0080}, /* 00,d2,80,aa */
+       {0xaa, 0xd3, 0x0080}, /* 00,d3,80,aa */
+       {0xaa, 0xd4, 0x0080}, /* 00,d4,80,aa */
+       {0xaa, 0xd5, 0x0080}, /* 00,d5,80,aa */
+       {0xaa, 0xc0, 0x00c3}, /* 00,c0,c3,aa */
+       {0xaa, 0xc2, 0x0044}, /* 00,c2,44,aa */
+       {0xaa, 0xc4, 0x0040}, /* 00,c4,40,aa */
+       {0xaa, 0xc5, 0x0020}, /* 00,c5,20,aa */
+       {0xaa, 0xc6, 0x0008}, /* 00,c6,08,aa */
+       {0xaa, 0x03, 0x0004}, /* 00,03,04,aa */
+       {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */
+       {0xaa, 0x40, 0x0030}, /* 00,40,30,aa */
+       {0xaa, 0x41, 0x0020}, /* 00,41,20,aa */
+       {0xaa, 0x42, 0x002d}, /* 00,42,2d,aa */
+       {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
+       {0xaa, 0x1c, 0x0050}, /* 00,1C,50,aa */
+       {0xaa, 0x11, 0x0081}, /* 00,11,81,aa */
+       {0xaa, 0x3b, 0x001d}, /* 00,3b,1D,aa */
+       {0xaa, 0x3c, 0x004c}, /* 00,3c,4C,aa */
+       {0xaa, 0x3d, 0x0018}, /* 00,3d,18,aa */
+       {0xaa, 0x3e, 0x006a}, /* 00,3e,6A,aa */
+       {0xaa, 0x01, 0x0000}, /* 00,01,00,aa */
+       {0xaa, 0x52, 0x00ff}, /* 00,52,FF,aa */
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+       {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */
+       {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,37,cc */
+       {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */
+       {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */
+       {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */
+       {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */
+       {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */
+       {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */
+       {0xaa, 0x03, 0x0002}, /* 00,03,02,aa */
+       {0xaa, 0x51, 0x0027}, /* 00,51,27,aa */
+       {0xaa, 0x52, 0x0020}, /* 00,52,20,aa */
+       {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
+       {0xaa, 0x50, 0x0010}, /* 00,50,10,aa */
+       {0xaa, 0x51, 0x0010}, /* 00,51,10,aa */
+       {0xaa, 0x54, 0x0010}, /* 00,54,10,aa */
+       {0xaa, 0x55, 0x0010}, /* 00,55,10,aa */
+       {0xa0, 0xf0, 0x0199}, /* 01,99,F0,cc */
+       {0xa0, 0x80, 0x019a}, /* 01,9A,80,cc */
+
+       {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
+       {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */
+       {0xaa, 0x36, 0x001d}, /* 00,36,1D,aa */
+       {0xaa, 0x37, 0x004c}, /* 00,37,4C,aa */
+       {0xaa, 0x3b, 0x001d}, /* 00,3B,1D,aa */
+       {}
+};
+
+static const struct usb_action MC501CB_Initial[] = {    /* 320x240 */
+       {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
+       {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, /* 00,02,10,cc */
+       {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
+       {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00,08,03,cc */
+       {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */
+       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */
+       {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */
+       {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */
+       {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */
+       {0xa0, 0xd0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,d0,cc */
+       {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */
+       {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */
+       {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */
+       {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */
+       {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH}, /* 00,9b,01,cc */
+       {0xa0, 0xd8, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,d8,cc */
+       {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, /* 00,9d,02,cc */
+       {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,88,cc */
+       {0xa0, 0x33, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,33,cc */
+       {0xa0, 0x34, ZC3XX_R087_EXPTIMEMID}, /* 00,87,34,cc */
+       {0xa0, 0x35, ZC3XX_R088_EXPTIMELOW}, /* 00,88,35,cc */
+       {0xa0, 0xb0, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,b0,cc */
+       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */
+       {0xaa, 0x01, 0x0001}, /* 00,01,01,aa */
+       {0xaa, 0x01, 0x0003}, /* 00,01,03,aa */
+       {0xaa, 0x01, 0x0001}, /* 00,01,01,aa */
+       {0xaa, 0x03, 0x0000}, /* 00,03,00,aa */
+       {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */
+       {0xaa, 0x11, 0x0080}, /* 00,11,80,aa */
+       {0xaa, 0x12, 0x0000}, /* 00,12,00,aa */
+       {0xaa, 0x13, 0x0000}, /* 00,13,00,aa */
+       {0xaa, 0x14, 0x0000}, /* 00,14,00,aa */
+       {0xaa, 0x15, 0x0000}, /* 00,15,00,aa */
+       {0xaa, 0x16, 0x0000}, /* 00,16,00,aa */
+       {0xaa, 0x17, 0x0001}, /* 00,17,01,aa */
+       {0xaa, 0x18, 0x00d8}, /* 00,18,d8,aa */
+       {0xaa, 0x19, 0x0002}, /* 00,19,02,aa */
+       {0xaa, 0x1a, 0x0088}, /* 00,1a,88,aa */
+       {0xaa, 0x20, 0x00a8}, /* 00,20,a8,aa */
+       {0xaa, 0x22, 0x0000}, /* 00,22,00,aa */
+       {0xaa, 0x23, 0x0000}, /* 00,23,00,aa */
+       {0xaa, 0x24, 0x0000}, /* 00,24,00,aa */
+       {0xaa, 0x40, 0x0033}, /* 00,40,33,aa */
+       {0xaa, 0x41, 0x0077}, /* 00,41,77,aa */
+       {0xaa, 0x42, 0x0053}, /* 00,42,53,aa */
+       {0xaa, 0x43, 0x00b0}, /* 00,43,b0,aa */
+       {0xaa, 0x4b, 0x0001}, /* 00,4b,01,aa */
+       {0xaa, 0x72, 0x0020}, /* 00,72,20,aa */
+       {0xaa, 0x73, 0x0000}, /* 00,73,00,aa */
+       {0xaa, 0x80, 0x0000}, /* 00,80,00,aa */
+       {0xaa, 0x85, 0x0050}, /* 00,85,50,aa */
+       {0xaa, 0x91, 0x0070}, /* 00,91,70,aa */
+       {0xaa, 0x92, 0x0072}, /* 00,92,72,aa */
+       {0xaa, 0x03, 0x0001}, /* 00,03,01,aa */
+       {0xaa, 0x10, 0x00a0}, /* 00,10,a0,aa */
+       {0xaa, 0x11, 0x0001}, /* 00,11,01,aa */
+       {0xaa, 0x30, 0x0000}, /* 00,30,00,aa */
+       {0xaa, 0x60, 0x0000}, /* 00,60,00,aa */
+       {0xaa, 0xa0, ZC3XX_R01A_LASTFRAMESTATE}, /* 00,a0,1a,aa */
+       {0xaa, 0xa1, 0x0000}, /* 00,a1,00,aa */
+       {0xaa, 0xa2, 0x003f}, /* 00,a2,3f,aa */
+       {0xaa, 0xa3, 0x0028}, /* 00,a3,28,aa */
+       {0xaa, 0xa4, 0x0010}, /* 00,a4,10,aa */
+       {0xaa, 0xa5, 0x0020}, /* 00,a5,20,aa */
+       {0xaa, 0xb1, 0x0044}, /* 00,b1,44,aa */
+       {0xaa, 0xd0, 0x0001}, /* 00,d0,01,aa */
+       {0xaa, 0xd1, 0x0085}, /* 00,d1,85,aa */
+       {0xaa, 0xd2, 0x0080}, /* 00,d2,80,aa */
+       {0xaa, 0xd3, 0x0080}, /* 00,d3,80,aa */
+       {0xaa, 0xd4, 0x0080}, /* 00,d4,80,aa */
+       {0xaa, 0xd5, 0x0080}, /* 00,d5,80,aa */
+       {0xaa, 0xc0, 0x00c3}, /* 00,c0,c3,aa */
+       {0xaa, 0xc2, 0x0044}, /* 00,c2,44,aa */
+       {0xaa, 0xc4, 0x0040}, /* 00,c4,40,aa */
+       {0xaa, 0xc5, 0x0020}, /* 00,c5,20,aa */
+       {0xaa, 0xc6, 0x0008}, /* 00,c6,08,aa */
+       {0xaa, 0x03, 0x0004}, /* 00,03,04,aa */
+       {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */
+       {0xaa, 0x40, 0x0030}, /* 00,40,30,aa */
+       {0xaa, 0x41, 0x0020}, /* 00,41,20,aa */
+       {0xaa, 0x42, 0x002d}, /* 00,42,2d,aa */
+       {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
+       {0xaa, 0x1c, 0x0050}, /* 00,1c,50,aa */
+       {0xaa, 0x11, 0x0081}, /* 00,11,81,aa */
+       {0xaa, 0x3b, 0x003a}, /* 00,3b,3A,aa */
+       {0xaa, 0x3c, 0x0098}, /* 00,3c,98,aa */
+       {0xaa, 0x3d, 0x0030}, /* 00,3d,30,aa */
+       {0xaa, 0x3e, 0x00d4}, /* 00,3E,D4,aa */
+       {0xaa, 0x01, 0x0000}, /* 00,01,00,aa */
+       {0xaa, 0x52, 0x00ff}, /* 00,52,FF,aa */
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+       {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */
+       {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,37,cc */
+       {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */
+       {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */
+       {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */
+       {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */
+       {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */
+       {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */
+       {0xaa, 0x03, 0x0002}, /* 00,03,02,aa */
+       {0xaa, 0x51, 0x004e}, /* 00,51,4E,aa */
+       {0xaa, 0x52, 0x0041}, /* 00,52,41,aa */
+       {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
+       {0xaa, 0x50, 0x0010}, /* 00,50,10,aa */
+       {0xaa, 0x51, 0x0010}, /* 00,51,10,aa */
+       {0xaa, 0x54, 0x0010}, /* 00,54,10,aa */
+       {0xaa, 0x55, 0x0010}, /* 00,55,10,aa */
+       {0xa0, 0xf0, 0x0199}, /* 01,99,F0,cc */
+       {0xa0, 0x80, 0x019a}, /* 01,9A,80,cc */
+       {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
+       {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */
+       {0xaa, 0x36, 0x001d}, /* 00,36,1D,aa */
+       {0xaa, 0x37, 0x004c}, /* 00,37,4C,aa */
+       {0xaa, 0x3b, 0x001d}, /* 00,3B,1D,aa */
+       {}
+};
+
+static const struct usb_action MC501CB_50HZ[] = {
+       {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
+       {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */
+       {0xaa, 0x36, 0x001d}, /* 00,36,1D,aa */
+       {0xaa, 0x37, 0x004c}, /* 00,37,4C,aa */
+       {0xaa, 0x3b, 0x001d}, /* 00,3B,1D,aa */
+       {0xaa, 0x3c, 0x004c}, /* 00,3C,4C,aa */
+       {0xaa, 0x3d, 0x001d}, /* 00,3D,1D,aa */
+       {0xaa, 0x3e, 0x004c}, /* 00,3E,4C,aa */
+       {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
+       {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */
+       {0xaa, 0x36, 0x003a}, /* 00,36,3A,aa */
+       {0xaa, 0x37, 0x0098}, /* 00,37,98,aa */
+       {0xaa, 0x3b, 0x003a}, /* 00,3B,3A,aa */
+       {}
+};
+
+static const struct usb_action MC501CB_50HZScale[] = {
+       {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
+       {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */
+       {0xaa, 0x36, 0x003a}, /* 00,36,3A,aa */
+       {0xaa, 0x37, 0x0098}, /* 00,37,98,aa */
+       {0xaa, 0x3b, 0x003a}, /* 00,3B,3A,aa */
+       {0xaa, 0x3c, 0x0098}, /* 00,3C,98,aa */
+       {0xaa, 0x3d, 0x003a}, /* 00,3D,3A,aa */
+       {0xaa, 0x3e, 0x0098}, /* 00,3E,98,aa */
+       {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
+       {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */
+       {0xaa, 0x36, 0x0018}, /* 00,36,18,aa */
+       {0xaa, 0x37, 0x006a}, /* 00,37,6A,aa */
+       {0xaa, 0x3d, 0x0018}, /* 00,3D,18,aa */
+       {}
+};
+
+static const struct usb_action MC501CB_60HZ[] = {
+       {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
+       {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */
+       {0xaa, 0x36, 0x0018}, /* 00,36,18,aa */
+       {0xaa, 0x37, 0x006a}, /* 00,37,6A,aa */
+       {0xaa, 0x3d, 0x0018}, /* 00,3D,18,aa */
+       {0xaa, 0x3e, 0x006a}, /* 00,3E,6A,aa */
+       {0xaa, 0x3b, 0x0018}, /* 00,3B,18,aa */
+       {0xaa, 0x3c, 0x006a}, /* 00,3C,6A,aa */
+       {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
+       {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */
+       {0xaa, 0x36, 0x0030}, /* 00,36,30,aa */
+       {0xaa, 0x37, 0x00d4}, /* 00,37,D4,aa */
+       {0xaa, 0x3d, 0x0030}, /* 00,3D,30,aa */
+       {}
+};
+
+static const struct usb_action MC501CB_60HZScale[] = {
+       {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
+       {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */
+       {0xaa, 0x36, 0x0030}, /* 00,36,30,aa */
+       {0xaa, 0x37, 0x00d4}, /* 00,37,D4,aa */
+       {0xaa, 0x3d, 0x0030}, /* 00,3D,30,aa */
+       {0xaa, 0x3e, 0x00d4}, /* 00,3E,D4,aa */
+       {0xaa, 0x3b, 0x0030}, /* 00,3B,30,aa */
+       {0xaa, 0x3c, 0x00d4}, /* 00,3C,D4,aa */
+       {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
+       {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */
+       {0xaa, 0x36, 0x0018}, /* 00,36,18,aa */
+       {0xaa, 0x37, 0x006a}, /* 00,37,6A,aa */
+       {0xaa, 0x3d, 0x0018}, /* 00,3D,18,aa */
+       {}
+};
+
+static const struct usb_action MC501CB_NoFliker[] = {
+       {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
+       {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */
+       {0xaa, 0x36, 0x0018}, /* 00,36,18,aa */
+       {0xaa, 0x37, 0x006a}, /* 00,37,6A,aa */
+       {0xaa, 0x3d, 0x0018}, /* 00,3D,18,aa */
+       {0xaa, 0x3e, 0x006a}, /* 00,3E,6A,aa */
+       {0xaa, 0x3b, 0x0018}, /* 00,3B,18,aa */
+       {0xaa, 0x3c, 0x006a}, /* 00,3C,6A,aa */
+       {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
+       {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */
+       {0xaa, 0x36, 0x0030}, /* 00,36,30,aa */
+       {0xaa, 0x37, 0x00d4}, /* 00,37,D4,aa */
+       {0xaa, 0x3d, 0x0030}, /* 00,3D,30,aa */
+       {}
+};
+
+static const struct usb_action MC501CB_NoFlikerScale[] = {
+       {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
+       {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */
+       {0xaa, 0x36, 0x0030}, /* 00,36,30,aa */
+       {0xaa, 0x37, 0x00d4}, /* 00,37,D4,aa */
+       {0xaa, 0x3d, 0x0030}, /* 00,3D,30,aa */
+       {0xaa, 0x3e, 0x00d4}, /* 00,3E,D4,aa */
+       {0xaa, 0x3b, 0x0030}, /* 00,3B,30,aa */
+       {0xaa, 0x3c, 0x00d4}, /* 00,3C,D4,aa */
+       {}
+};
+
+/* from zs211.inf - HKR,%OV7620%,Initial - 640x480 */
+static const struct usb_action OV7620_mode0[] = {
+       {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
+       {0xa0, 0x40, ZC3XX_R002_CLOCKSELECT}, /* 00,02,40,cc */
+       {0xa0, 0x00, ZC3XX_R008_CLOCKSETTING}, /* 00,08,00,cc */
+       {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */
+       {0xa0, 0x06, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,06,cc */
+       {0xa0, 0x02, ZC3XX_R083_RGAINADDR}, /* 00,83,02,cc */
+       {0xa0, 0x01, ZC3XX_R085_BGAINADDR}, /* 00,85,01,cc */
+       {0xa0, 0x80, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,80,cc */
+       {0xa0, 0x81, ZC3XX_R087_EXPTIMEMID}, /* 00,87,81,cc */
+       {0xa0, 0x10, ZC3XX_R088_EXPTIMELOW}, /* 00,88,10,cc */
+       {0xa0, 0xa1, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,a1,cc */
+       {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE}, /* 00,8d,08,cc */
+       {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */
+       {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */
+       {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */
+       {0xa0, 0xd8, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,d8,cc */
+       {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */
+       {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */
+       {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */
+       {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */
+       {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */
+       {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */
+       {0xa0, 0xde, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,de,cc */
+       {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,86,cc */
+       {0xaa, 0x12, 0x0088}, /* 00,12,88,aa */
+       {0xaa, 0x12, 0x0048}, /* 00,12,48,aa */
+       {0xaa, 0x75, 0x008a}, /* 00,75,8a,aa */
+       {0xaa, 0x13, 0x00a3}, /* 00,13,a3,aa */
+       {0xaa, 0x04, 0x0000}, /* 00,04,00,aa */
+       {0xaa, 0x05, 0x0000}, /* 00,05,00,aa */
+       {0xaa, 0x14, 0x0000}, /* 00,14,00,aa */
+       {0xaa, 0x15, 0x0004}, /* 00,15,04,aa */
+       {0xaa, 0x17, 0x0018}, /* 00,17,18,aa */
+       {0xaa, 0x18, 0x00ba}, /* 00,18,ba,aa */
+       {0xaa, 0x19, 0x0002}, /* 00,19,02,aa */
+       {0xaa, 0x1a, 0x00f1}, /* 00,1a,f1,aa */
+       {0xaa, 0x20, 0x0040}, /* 00,20,40,aa */
+       {0xaa, 0x24, 0x0088}, /* 00,24,88,aa */
+       {0xaa, 0x25, 0x0078}, /* 00,25,78,aa */
+       {0xaa, 0x27, 0x00f6}, /* 00,27,f6,aa */
+       {0xaa, 0x28, 0x00a0}, /* 00,28,a0,aa */
+       {0xaa, 0x21, 0x0000}, /* 00,21,00,aa */
+       {0xaa, 0x2a, 0x0083}, /* 00,2a,83,aa */
+       {0xaa, 0x2b, 0x0096}, /* 00,2b,96,aa */
+       {0xaa, 0x2d, 0x0005}, /* 00,2d,05,aa */
+       {0xaa, 0x74, 0x0020}, /* 00,74,20,aa */
+       {0xaa, 0x61, 0x0068}, /* 00,61,68,aa */
+       {0xaa, 0x64, 0x0088}, /* 00,64,88,aa */
+       {0xaa, 0x00, 0x0000}, /* 00,00,00,aa */
+       {0xaa, 0x06, 0x0080}, /* 00,06,80,aa */
+       {0xaa, 0x01, 0x0090}, /* 00,01,90,aa */
+       {0xaa, 0x02, 0x0030}, /* 00,02,30,aa */
+       {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,77,cc */
+       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */
+       {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */
+       {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */
+       {0xa0, 0x00, 0x01ad}, /* 01,ad,00,cc */
+       {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */
+       {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */
+       {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */
+       {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */
+       {0xa0, 0x68, ZC3XX_R116_RGAIN}, /* 01,16,68,cc */
+       {0xa0, 0x52, ZC3XX_R118_BGAIN}, /* 01,18,52,cc */
+       {0xa0, 0x40, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,40,cc */
+       {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */
+       {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,50,cc */
+       {}
+};
+
+/* from zs211.inf - HKR,%OV7620%,InitialScale - 320x240 */
+static const struct usb_action OV7620_mode1[] = {
+       {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
+       {0xa0, 0x50, ZC3XX_R002_CLOCKSELECT},   /* 00,02,50,cc */
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},  /* 00,08,00,cc */
+                                               /* mx change? */
+       {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */
+       {0xa0, 0x06, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,06,cc */
+       {0xa0, 0x02, ZC3XX_R083_RGAINADDR},     /* 00,83,02,cc */
+       {0xa0, 0x01, ZC3XX_R085_BGAINADDR},     /* 00,85,01,cc */
+       {0xa0, 0x80, ZC3XX_R086_EXPTIMEHIGH},   /* 00,86,80,cc */
+       {0xa0, 0x81, ZC3XX_R087_EXPTIMEMID},    /* 00,87,81,cc */
+       {0xa0, 0x10, ZC3XX_R088_EXPTIMELOW},    /* 00,88,10,cc */
+       {0xa0, 0xa1, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,a1,cc */
+       {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE}, /* 00,8d,08,cc */
+       {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */
+       {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */
+       {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */
+       {0xa0, 0xd0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,d0,cc */
+       {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */
+       {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */
+       {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},  /* 00,98,00,cc */
+       {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},  /* 00,9a,00,cc */
+       {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},     /* 01,1a,00,cc */
+       {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},     /* 01,1c,00,cc */
+       {0xa0, 0xd6, ZC3XX_R09C_WINHEIGHTLOW},  /* 00,9c,d6,cc */
+                                               /* OV7648 00,9c,d8,cc */
+       {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW},   /* 00,9e,88,cc */
+       {0xaa, 0x12, 0x0088}, /* 00,12,88,aa */
+       {0xaa, 0x12, 0x0048}, /* 00,12,48,aa */
+       {0xaa, 0x75, 0x008a}, /* 00,75,8a,aa */
+       {0xaa, 0x13, 0x00a3}, /* 00,13,a3,aa */
+       {0xaa, 0x04, 0x0000}, /* 00,04,00,aa */
+       {0xaa, 0x05, 0x0000}, /* 00,05,00,aa */
+       {0xaa, 0x14, 0x0000}, /* 00,14,00,aa */
+       {0xaa, 0x15, 0x0004}, /* 00,15,04,aa */
+       {0xaa, 0x24, 0x0088}, /* 00,24,88,aa */
+       {0xaa, 0x25, 0x0078}, /* 00,25,78,aa */
+       {0xaa, 0x17, 0x0018}, /* 00,17,18,aa */
+       {0xaa, 0x18, 0x00ba}, /* 00,18,ba,aa */
+       {0xaa, 0x19, 0x0002}, /* 00,19,02,aa */
+       {0xaa, 0x1a, 0x00f2}, /* 00,1a,f2,aa */
+       {0xaa, 0x20, 0x0040}, /* 00,20,40,aa */
+       {0xaa, 0x27, 0x00f6}, /* 00,27,f6,aa */
+       {0xaa, 0x28, 0x00a0}, /* 00,28,a0,aa */
+       {0xaa, 0x21, 0x0000}, /* 00,21,00,aa */
+       {0xaa, 0x2a, 0x0083}, /* 00,2a,83,aa */
+       {0xaa, 0x2b, 0x0096}, /* 00,2b,96,aa */
+       {0xaa, 0x2d, 0x0005}, /* 00,2d,05,aa */
+       {0xaa, 0x74, 0x0020}, /* 00,74,20,aa */
+       {0xaa, 0x61, 0x0068}, /* 00,61,68,aa */
+       {0xaa, 0x64, 0x0088}, /* 00,64,88,aa */
+       {0xaa, 0x00, 0x0000}, /* 00,00,00,aa */
+       {0xaa, 0x06, 0x0080}, /* 00,06,80,aa */
+       {0xaa, 0x01, 0x0090}, /* 00,01,90,aa */
+       {0xaa, 0x02, 0x0030}, /* 00,02,30,aa */
+       {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,77,cc */
+       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */
+       {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */
+       {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},     /* 01,89,06,cc */
+       {0xa0, 0x00, 0x01ad},                   /* 01,ad,00,cc */
+       {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */
+       {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},   /* 01,cb,13,cc */
+       {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */
+       {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},  /* 03,01,08,cc */
+       {0xa0, 0x68, ZC3XX_R116_RGAIN},         /* 01,16,68,cc */
+       {0xa0, 0x52, ZC3XX_R118_BGAIN},         /* 01,18,52,cc */
+       {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},    /* 01,1d,50,cc */
+       {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */
+       {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN},   /* 01,a8,50,cc */
+       {}
+};
+
+/* from zs211.inf - HKR,%OV7620%\AE,50HZ */
+static const struct usb_action OV7620_50HZ[] = {
+       {0xaa, 0x13, 0x00a3},   /* 00,13,a3,aa */
+       {0xdd, 0x00, 0x0100},   /* 00,01,00,dd */
+       {0xaa, 0x2b, 0x0096},   /* 00,2b,96,aa */
+       {0xaa, 0x75, 0x008a},   /* 00,75,8a,aa */
+       {0xaa, 0x2d, 0x0005},   /* 00,2d,05,aa */
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},     /* 01,90,00,cc */
+       {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID},      /* 01,91,04,cc */
+       {0xa0, 0x18, ZC3XX_R192_EXPOSURELIMITLOW},      /* 01,92,18,cc */
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},       /* 01,95,00,cc */
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},        /* 01,96,00,cc */
+       {0xa0, 0x83, ZC3XX_R197_ANTIFLICKERLOW},        /* 01,97,83,cc */
+       {0xaa, 0x10, 0x0082},                           /* 00,10,82,aa */
+       {0xaa, 0x76, 0x0003},                           /* 00,76,03,aa */
+/*     {0xa0, 0x40, ZC3XX_R002_CLOCKSELECT},            * 00,02,40,cc
+                                                        if mode0 (640x480) */
+       {}
+};
+
+/* from zs211.inf - HKR,%OV7620%\AE,60HZ */
+static const struct usb_action OV7620_60HZ[] = {
+       {0xaa, 0x13, 0x00a3},                   /* 00,13,a3,aa */
+                                               /* (bug in zs211.inf) */
+       {0xdd, 0x00, 0x0100},                   /* 00,01,00,dd */
+       {0xaa, 0x2b, 0x0000},                   /* 00,2b,00,aa */
+       {0xaa, 0x75, 0x008a},                   /* 00,75,8a,aa */
+       {0xaa, 0x2d, 0x0005},                   /* 00,2d,05,aa */
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+       {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */
+       {0xa0, 0x18, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,18,cc */
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+       {0xa0, 0x83, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,83,cc */
+       {0xaa, 0x10, 0x0020},                   /* 00,10,20,aa */
+       {0xaa, 0x76, 0x0003},                   /* 00,76,03,aa */
+/*     {0xa0, 0x40, ZC3XX_R002_CLOCKSELECT},    * 00,02,40,cc
+                                                * if mode0 (640x480) */
+/* ?? in gspca v1, it was
+       {0xa0, 0x00, 0x0039},  * 00,00,00,dd *
+       {0xa1, 0x01, 0x0037},           */
+       {}
+};
+
+/* from zs211.inf - HKR,%OV7620%\AE,NoFliker */
+static const struct usb_action OV7620_NoFliker[] = {
+       {0xaa, 0x13, 0x00a3},                   /* 00,13,a3,aa */
+                                               /* (bug in zs211.inf) */
+       {0xdd, 0x00, 0x0100},                   /* 00,01,00,dd */
+       {0xaa, 0x2b, 0x0000},                   /* 00,2b,00,aa */
+       {0xaa, 0x75, 0x008e},                   /* 00,75,8e,aa */
+       {0xaa, 0x2d, 0x0001},                   /* 00,2d,01,aa */
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+       {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */
+       {0xa0, 0x18, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,18,cc */
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+       {0xa0, 0x01, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,01,cc */
+/*     {0xa0, 0x44, ZC3XX_R002_CLOCKSELECT},    * 00,02,44,cc
+                                                - if mode1 (320x240) */
+/* ?? was
+       {0xa0, 0x00, 0x0039},  * 00,00,00,dd *
+       {0xa1, 0x01, 0x0037},           */
+       {}
+};
+
+static const struct usb_action ov7630c_Initial[] = {
+       {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+       {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
+       {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+       {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+       {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+       {0xa0, 0x06, ZC3XX_R010_CMOSSENSORSELECT},
+       {0xa0, 0xa1, ZC3XX_R08B_I2CDEVICEADDR},
+       {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE},
+       {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+       {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+       {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+       {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+       {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xaa, 0x12, 0x0080},
+       {0xa0, 0x02, ZC3XX_R083_RGAINADDR},
+       {0xa0, 0x01, ZC3XX_R085_BGAINADDR},
+       {0xa0, 0x90, ZC3XX_R086_EXPTIMEHIGH},
+       {0xa0, 0x91, ZC3XX_R087_EXPTIMEMID},
+       {0xa0, 0x10, ZC3XX_R088_EXPTIMELOW},
+       {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+       {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+       {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+       {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+       {0xa0, 0xd8, ZC3XX_R09C_WINHEIGHTLOW},
+       {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW},
+       {0xaa, 0x12, 0x0069},
+       {0xaa, 0x04, 0x0020},
+       {0xaa, 0x06, 0x0050},
+       {0xaa, 0x13, 0x0083},
+       {0xaa, 0x14, 0x0000},
+       {0xaa, 0x15, 0x0024},
+       {0xaa, 0x17, 0x0018},
+       {0xaa, 0x18, 0x00ba},
+       {0xaa, 0x19, 0x0002},
+       {0xaa, 0x1a, 0x00f6},
+       {0xaa, 0x1b, 0x0002},
+       {0xaa, 0x20, 0x00c2},
+       {0xaa, 0x24, 0x0060},
+       {0xaa, 0x25, 0x0040},
+       {0xaa, 0x26, 0x0030},
+       {0xaa, 0x27, 0x00ea},
+       {0xaa, 0x28, 0x00a0},
+       {0xaa, 0x21, 0x0000},
+       {0xaa, 0x2a, 0x0081},
+       {0xaa, 0x2b, 0x0096},
+       {0xaa, 0x2d, 0x0094},
+       {0xaa, 0x2f, 0x003d},
+       {0xaa, 0x30, 0x0024},
+       {0xaa, 0x60, 0x0000},
+       {0xaa, 0x61, 0x0040},
+       {0xaa, 0x68, 0x007c},
+       {0xaa, 0x6f, 0x0015},
+       {0xaa, 0x75, 0x0088},
+       {0xaa, 0x77, 0x00b5},
+       {0xaa, 0x01, 0x0060},
+       {0xaa, 0x02, 0x0060},
+       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION},
+       {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+       {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+       {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+       {0xa0, 0x00, 0x01ad},
+       {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+       {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+       {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+       {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+       {0xa0, 0x60, ZC3XX_R116_RGAIN},
+       {0xa0, 0x46, ZC3XX_R118_BGAIN},
+       {0xa0, 0x04, ZC3XX_R113_RGB03},
+/* 0x10, */
+       {0xa1, 0x01, 0x0002},
+       {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */
+       {0xa0, 0xf8, ZC3XX_R10B_RGB01},
+       {0xa0, 0xf8, ZC3XX_R10C_RGB02},
+       {0xa0, 0xf8, ZC3XX_R10D_RGB10},
+       {0xa0, 0x50, ZC3XX_R10E_RGB11},
+       {0xa0, 0xf8, ZC3XX_R10F_RGB12},
+       {0xa0, 0xf8, ZC3XX_R110_RGB20},
+       {0xa0, 0xf8, ZC3XX_R111_RGB21},
+       {0xa0, 0x50, ZC3XX_R112_RGB22},
+/* 0x03, */
+       {0xa1, 0x01, 0x0008},
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},  /* clock ? */
+       {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},   /* sharpness+ */
+       {0xa1, 0x01, 0x01c8},
+       {0xa1, 0x01, 0x01c9},
+       {0xa1, 0x01, 0x01ca},
+       {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05},   /* sharpness- */
+       {0xa0, 0x01, ZC3XX_R120_GAMMA00},       /* gamma 2 ?*/
+       {0xa0, 0x0c, ZC3XX_R121_GAMMA01},
+       {0xa0, 0x1f, ZC3XX_R122_GAMMA02},
+       {0xa0, 0x3a, ZC3XX_R123_GAMMA03},
+       {0xa0, 0x53, ZC3XX_R124_GAMMA04},
+       {0xa0, 0x6d, ZC3XX_R125_GAMMA05},
+       {0xa0, 0x85, ZC3XX_R126_GAMMA06},
+       {0xa0, 0x9c, ZC3XX_R127_GAMMA07},
+       {0xa0, 0xb0, ZC3XX_R128_GAMMA08},
+       {0xa0, 0xc2, ZC3XX_R129_GAMMA09},
+       {0xa0, 0xd1, ZC3XX_R12A_GAMMA0A},
+       {0xa0, 0xde, ZC3XX_R12B_GAMMA0B},
+       {0xa0, 0xe9, ZC3XX_R12C_GAMMA0C},
+       {0xa0, 0xf2, ZC3XX_R12D_GAMMA0D},
+       {0xa0, 0xf9, ZC3XX_R12E_GAMMA0E},
+       {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
+       {0xa0, 0x05, ZC3XX_R130_GAMMA10},
+       {0xa0, 0x0f, ZC3XX_R131_GAMMA11},
+       {0xa0, 0x16, ZC3XX_R132_GAMMA12},
+       {0xa0, 0x1a, ZC3XX_R133_GAMMA13},
+       {0xa0, 0x19, ZC3XX_R134_GAMMA14},
+       {0xa0, 0x19, ZC3XX_R135_GAMMA15},
+       {0xa0, 0x17, ZC3XX_R136_GAMMA16},
+       {0xa0, 0x15, ZC3XX_R137_GAMMA17},
+       {0xa0, 0x12, ZC3XX_R138_GAMMA18},
+       {0xa0, 0x10, ZC3XX_R139_GAMMA19},
+       {0xa0, 0x0e, ZC3XX_R13A_GAMMA1A},
+       {0xa0, 0x0b, ZC3XX_R13B_GAMMA1B},
+       {0xa0, 0x09, ZC3XX_R13C_GAMMA1C},
+       {0xa0, 0x08, ZC3XX_R13D_GAMMA1D},
+       {0xa0, 0x06, ZC3XX_R13E_GAMMA1E},
+       {0xa0, 0x03, ZC3XX_R13F_GAMMA1F},
+       {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */
+       {0xa0, 0xf8, ZC3XX_R10B_RGB01},
+       {0xa0, 0xf8, ZC3XX_R10C_RGB02},
+       {0xa0, 0xf8, ZC3XX_R10D_RGB10},
+       {0xa0, 0x50, ZC3XX_R10E_RGB11},
+       {0xa0, 0xf8, ZC3XX_R10F_RGB12},
+       {0xa0, 0xf8, ZC3XX_R110_RGB20},
+       {0xa0, 0xf8, ZC3XX_R111_RGB21},
+       {0xa0, 0x50, ZC3XX_R112_RGB22},
+
+       {0xa1, 0x01, 0x0180},
+       {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xaa, 0x10, 0x001b},
+       {0xaa, 0x76, 0x0002},
+       {0xaa, 0x2a, 0x0081},
+       {0xaa, 0x2b, 0x0000},
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+       {0xa0, 0x01, ZC3XX_R191_EXPOSURELIMITMID},
+       {0xa0, 0xb8, ZC3XX_R192_EXPOSURELIMITLOW},
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+       {0xa0, 0x37, ZC3XX_R197_ANTIFLICKERLOW},
+       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
+       {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP},
+       {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
+       {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xaa, 0x13, 0x0083},   /* 40 */
+       {0xa1, 0x01, 0x0180},
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+       {}
+};
+
+static const struct usb_action ov7630c_InitialScale[] = {
+       {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+       {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+       {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+       {0xa0, 0x06, ZC3XX_R010_CMOSSENSORSELECT},
+       {0xa0, 0xa1, ZC3XX_R08B_I2CDEVICEADDR},
+       {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE},
+       {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+       {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+       {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+       {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+       {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+
+       {0xaa, 0x12, 0x0080},
+       {0xa0, 0x02, ZC3XX_R083_RGAINADDR},
+       {0xa0, 0x01, ZC3XX_R085_BGAINADDR},
+       {0xa0, 0x90, ZC3XX_R086_EXPTIMEHIGH},
+       {0xa0, 0x91, ZC3XX_R087_EXPTIMEMID},
+       {0xa0, 0x10, ZC3XX_R088_EXPTIMELOW},
+       {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+       {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+       {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+       {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+       {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW},
+       {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW},
+       {0xaa, 0x12, 0x0069},   /* i2c */
+       {0xaa, 0x04, 0x0020},
+       {0xaa, 0x06, 0x0050},
+       {0xaa, 0x13, 0x00c3},
+       {0xaa, 0x14, 0x0000},
+       {0xaa, 0x15, 0x0024},
+       {0xaa, 0x19, 0x0003},
+       {0xaa, 0x1a, 0x00f6},
+       {0xaa, 0x1b, 0x0002},
+       {0xaa, 0x20, 0x00c2},
+       {0xaa, 0x24, 0x0060},
+       {0xaa, 0x25, 0x0040},
+       {0xaa, 0x26, 0x0030},
+       {0xaa, 0x27, 0x00ea},
+       {0xaa, 0x28, 0x00a0},
+       {0xaa, 0x21, 0x0000},
+       {0xaa, 0x2a, 0x0081},
+       {0xaa, 0x2b, 0x0096},
+       {0xaa, 0x2d, 0x0084},
+       {0xaa, 0x2f, 0x003d},
+       {0xaa, 0x30, 0x0024},
+       {0xaa, 0x60, 0x0000},
+       {0xaa, 0x61, 0x0040},
+       {0xaa, 0x68, 0x007c},
+       {0xaa, 0x6f, 0x0015},
+       {0xaa, 0x75, 0x0088},
+       {0xaa, 0x77, 0x00b5},
+       {0xaa, 0x01, 0x0060},
+       {0xaa, 0x02, 0x0060},
+       {0xaa, 0x17, 0x0018},
+       {0xaa, 0x18, 0x00ba},
+       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION},
+       {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+       {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+       {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+       {0xa0, 0x00, 0x01ad},
+       {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+       {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+       {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+       {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+       {0xa0, 0x60, ZC3XX_R116_RGAIN},
+       {0xa0, 0x46, ZC3XX_R118_BGAIN},
+       {0xa0, 0x04, ZC3XX_R113_RGB03},
+
+       {0xa1, 0x01, 0x0002},
+       {0xa0, 0x4e, ZC3XX_R10A_RGB00}, /* matrix */
+       {0xa0, 0xfe, ZC3XX_R10B_RGB01},
+       {0xa0, 0xf4, ZC3XX_R10C_RGB02},
+       {0xa0, 0xf7, ZC3XX_R10D_RGB10},
+       {0xa0, 0x4d, ZC3XX_R10E_RGB11},
+       {0xa0, 0xfc, ZC3XX_R10F_RGB12},
+       {0xa0, 0x00, ZC3XX_R110_RGB20},
+       {0xa0, 0xf6, ZC3XX_R111_RGB21},
+       {0xa0, 0x4a, ZC3XX_R112_RGB22},
+
+       {0xa1, 0x01, 0x0008},
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},  /* clock ? */
+       {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},   /* sharpness+ */
+       {0xa1, 0x01, 0x01c8},
+       {0xa1, 0x01, 0x01c9},
+       {0xa1, 0x01, 0x01ca},
+       {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05},   /* sharpness- */
+       {0xa0, 0x16, ZC3XX_R120_GAMMA00},       /* gamma ~4 */
+       {0xa0, 0x3a, ZC3XX_R121_GAMMA01},
+       {0xa0, 0x5b, ZC3XX_R122_GAMMA02},
+       {0xa0, 0x7c, ZC3XX_R123_GAMMA03},
+       {0xa0, 0x94, ZC3XX_R124_GAMMA04},
+       {0xa0, 0xa9, ZC3XX_R125_GAMMA05},
+       {0xa0, 0xbb, ZC3XX_R126_GAMMA06},
+       {0xa0, 0xca, ZC3XX_R127_GAMMA07},
+       {0xa0, 0xd7, ZC3XX_R128_GAMMA08},
+       {0xa0, 0xe1, ZC3XX_R129_GAMMA09},
+       {0xa0, 0xea, ZC3XX_R12A_GAMMA0A},
+       {0xa0, 0xf1, ZC3XX_R12B_GAMMA0B},
+       {0xa0, 0xf7, ZC3XX_R12C_GAMMA0C},
+       {0xa0, 0xfc, ZC3XX_R12D_GAMMA0D},
+       {0xa0, 0xff, ZC3XX_R12E_GAMMA0E},
+       {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
+       {0xa0, 0x20, ZC3XX_R130_GAMMA10},
+       {0xa0, 0x22, ZC3XX_R131_GAMMA11},
+       {0xa0, 0x20, ZC3XX_R132_GAMMA12},
+       {0xa0, 0x1c, ZC3XX_R133_GAMMA13},
+       {0xa0, 0x16, ZC3XX_R134_GAMMA14},
+       {0xa0, 0x13, ZC3XX_R135_GAMMA15},
+       {0xa0, 0x10, ZC3XX_R136_GAMMA16},
+       {0xa0, 0x0d, ZC3XX_R137_GAMMA17},
+       {0xa0, 0x0b, ZC3XX_R138_GAMMA18},
+       {0xa0, 0x09, ZC3XX_R139_GAMMA19},
+       {0xa0, 0x07, ZC3XX_R13A_GAMMA1A},
+       {0xa0, 0x06, ZC3XX_R13B_GAMMA1B},
+       {0xa0, 0x05, ZC3XX_R13C_GAMMA1C},
+       {0xa0, 0x04, ZC3XX_R13D_GAMMA1D},
+       {0xa0, 0x00, ZC3XX_R13E_GAMMA1E},
+       {0xa0, 0x01, ZC3XX_R13F_GAMMA1F},
+       {0xa0, 0x4e, ZC3XX_R10A_RGB00}, /* matrix */
+       {0xa0, 0xfe, ZC3XX_R10B_RGB01},
+       {0xa0, 0xf4, ZC3XX_R10C_RGB02},
+       {0xa0, 0xf7, ZC3XX_R10D_RGB10},
+       {0xa0, 0x4d, ZC3XX_R10E_RGB11},
+       {0xa0, 0xfc, ZC3XX_R10F_RGB12},
+       {0xa0, 0x00, ZC3XX_R110_RGB20},
+       {0xa0, 0xf6, ZC3XX_R111_RGB21},
+       {0xa0, 0x4a, ZC3XX_R112_RGB22},
+
+       {0xa1, 0x01, 0x0180},
+       {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xaa, 0x10, 0x000d},
+       {0xaa, 0x76, 0x0002},
+       {0xaa, 0x2a, 0x0081},
+       {0xaa, 0x2b, 0x0000},
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+       {0xa0, 0x00, ZC3XX_R191_EXPOSURELIMITMID},
+       {0xa0, 0xd8, ZC3XX_R192_EXPOSURELIMITLOW},
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+       {0xa0, 0x1b, ZC3XX_R197_ANTIFLICKERLOW},
+       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
+       {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP},
+       {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
+       {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xaa, 0x13, 0x00c3},
+
+       {0xa1, 0x01, 0x0180},
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+       {}
+};
+
+static const struct usb_action pas106b_Initial_com[] = {
+/* Sream and Sensor specific */
+       {0xa1, 0x01, 0x0010},   /* CMOSSensorSelect */
+/* System */
+       {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* SystemControl */
+       {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* SystemControl */
+/* Picture size */
+       {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},   /* ClockSelect */
+       {0xa0, 0x03, 0x003a},
+       {0xa0, 0x0c, 0x003b},
+       {0xa0, 0x04, 0x0038},
+       {}
+};
+
+static const struct usb_action pas106b_Initial[] = {   /* 176x144 */
+/* JPEG control */
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},          /* ClockSetting */
+/* Sream and Sensor specific */
+       {0xa0, 0x0f, ZC3XX_R010_CMOSSENSORSELECT},      /* CMOSSensorSelect */
+/* Picture size */
+       {0xa0, 0x00, ZC3XX_R003_FRAMEWIDTHHIGH},  /* FrameWidthHigh 00 */
+       {0xa0, 0xb0, ZC3XX_R004_FRAMEWIDTHLOW},   /* FrameWidthLow B0 */
+       {0xa0, 0x00, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* FrameHeightHigh 00 */
+       {0xa0, 0x90, ZC3XX_R006_FRAMEHEIGHTLOW},  /* FrameHightLow 90 */
+/* System */
+       {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* SystemOperating */
+/* Sream and Sensor specific */
+       {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* VideoControlFunction */
+       {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* VideoControlFunction */
+/* Sensor Interface */
+       {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE},  /* Compatibily Mode */
+/* Window inside sensor array */
+       {0xa0, 0x03, ZC3XX_R09A_WINXSTARTLOW},  /* WinXStartLow */
+       {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},     /* FirstYLow */
+       {0xa0, 0x03, ZC3XX_R11C_FIRSTXLOW},     /* FirstxLow */
+       {0xa0, 0x28, ZC3XX_R09C_WINHEIGHTLOW},  /* WinHeightLow */
+       {0xa0, 0x68, ZC3XX_R09E_WINWIDTHLOW},   /* WinWidthLow */
+/* Init the sensor */
+       {0xaa, 0x02, 0x0004},
+       {0xaa, 0x08, 0x0000},
+       {0xaa, 0x09, 0x0005},
+       {0xaa, 0x0a, 0x0002},
+       {0xaa, 0x0b, 0x0002},
+       {0xaa, 0x0c, 0x0005},
+       {0xaa, 0x0d, 0x0000},
+       {0xaa, 0x0e, 0x0002},
+       {0xaa, 0x14, 0x0081},
+
+/* Other registors */
+       {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, /* SensorCorrection */
+/* Frame retreiving */
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* AutoAdjustFPS */
+/* Gains */
+       {0xa0, 0xa0, ZC3XX_R1A8_DIGITALGAIN},   /* DigitalGain */
+/* Unknown */
+       {0xa0, 0x00, 0x01ad},
+/* Sharpness */
+       {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* SharpnessMode */
+       {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},   /* Sharpness05 */
+/* Other registors */
+       {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* OperationMode */
+/* Auto exposure and white balance */
+       {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},     /* AWBStatus */
+/*Dead pixels */
+       {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* DeadPixelsMode */
+/* EEPROM */
+       {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},  /* EEPROMAccess */
+/* JPEG control */
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},  /* ClockSetting */
+       {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},   /* sharpness+ */
+       {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05},   /* sharpness- */
+/* Other registers */
+       {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* OperationMode */
+/* Auto exposure and white balance */
+       {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},     /* AWBStatus */
+/*Dead pixels */
+       {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* DeadPixelsMode */
+/* EEPROM */
+       {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},  /* EEPROMAccess */
+/* JPEG control */
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},  /* ClockSetting */
+       {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},   /* sharpness+ */
+       {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05},   /* sharpness- */
+
+       {0xa0, 0x58, ZC3XX_R10A_RGB00}, /* matrix */
+       {0xa0, 0xf4, ZC3XX_R10B_RGB01},
+       {0xa0, 0xf4, ZC3XX_R10C_RGB02},
+       {0xa0, 0xf4, ZC3XX_R10D_RGB10},
+       {0xa0, 0x58, ZC3XX_R10E_RGB11},
+       {0xa0, 0xf4, ZC3XX_R10F_RGB12},
+       {0xa0, 0xf4, ZC3XX_R110_RGB20},
+       {0xa0, 0xf4, ZC3XX_R111_RGB21},
+       {0xa0, 0x58, ZC3XX_R112_RGB22},
+/* Auto correction */
+       {0xa0, 0x03, ZC3XX_R181_WINXSTART},     /* WinXstart */
+       {0xa0, 0x08, ZC3XX_R182_WINXWIDTH},     /* WinXWidth */
+       {0xa0, 0x16, ZC3XX_R183_WINXCENTER},    /* WinXCenter */
+       {0xa0, 0x03, ZC3XX_R184_WINYSTART},     /* WinYStart */
+       {0xa0, 0x05, ZC3XX_R185_WINYWIDTH},     /* WinYWidth */
+       {0xa0, 0x14, ZC3XX_R186_WINYCENTER},    /* WinYCenter */
+       {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, /* AutoCorrectEnable */
+
+/* Auto exposure and white balance */
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},     /* ExposureLimitHigh */
+       {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID},      /* ExposureLimitMid */
+       {0xa0, 0xb1, ZC3XX_R192_EXPOSURELIMITLOW},      /* ExposureLimitLow */
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},       /* AntiFlickerHigh */
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},        /* AntiFlickerLow */
+       {0xa0, 0x87, ZC3XX_R197_ANTIFLICKERLOW},        /* AntiFlickerLow */
+       {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},              /* AEBFreeze */
+       {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},            /* AEBUnfreeze */
+/* sensor on */
+       {0xaa, 0x07, 0x00b1},
+       {0xaa, 0x05, 0x0003},
+       {0xaa, 0x04, 0x0001},
+       {0xaa, 0x03, 0x003b},
+/* Gains */
+       {0xa0, 0x20, ZC3XX_R1A9_DIGITALLIMITDIFF},      /* DigitalLimitDiff */
+       {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP},       /* DigitalGainStep */
+       {0xa0, 0xa0, ZC3XX_R11D_GLOBALGAIN},            /* GlobalGain */
+       {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},            /* GlobalGain */
+/* Auto correction */
+       {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},     /* AutoCorrectEnable */
+       {0xa1, 0x01, 0x0180},                           /* AutoCorrectEnable */
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},     /* AutoCorrectEnable */
+/* Gains */
+       {0xa0, 0x40, ZC3XX_R116_RGAIN},                 /* RGain */
+       {0xa0, 0x40, ZC3XX_R117_GGAIN},                 /* GGain */
+       {0xa0, 0x40, ZC3XX_R118_BGAIN},                 /* BGain */
+       {}
+};
+
+static const struct usb_action pas106b_InitialScale[] = {      /* 352x288 */
+/* JPEG control */
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},          /* ClockSetting */
+/* Sream and Sensor specific */
+       {0xa0, 0x0f, ZC3XX_R010_CMOSSENSORSELECT},      /* CMOSSensorSelect */
+/* Picture size */
+       {0xa0, 0x01, ZC3XX_R003_FRAMEWIDTHHIGH},        /* FrameWidthHigh */
+       {0xa0, 0x60, ZC3XX_R004_FRAMEWIDTHLOW},         /* FrameWidthLow */
+       {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},       /* FrameHeightHigh */
+       {0xa0, 0x20, ZC3XX_R006_FRAMEHEIGHTLOW},        /* FrameHightLow */
+/* System */
+       {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},       /* SystemOperating */
+/* Sream and Sensor specific */
+       {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* VideoControlFunction */
+       {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* VideoControlFunction */
+/* Sensor Interface */
+       {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE},       /* Compatibily Mode */
+/* Window inside sensor array */
+       {0xa0, 0x03, ZC3XX_R09A_WINXSTARTLOW},  /* WinXStartLow */
+       {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},     /* FirstYLow */
+       {0xa0, 0x03, ZC3XX_R11C_FIRSTXLOW},     /* FirstxLow */
+       {0xa0, 0x28, ZC3XX_R09C_WINHEIGHTLOW},  /* WinHeightLow */
+       {0xa0, 0x68, ZC3XX_R09E_WINWIDTHLOW},   /* WinWidthLow */
+/* Init the sensor */
+       {0xaa, 0x02, 0x0004},
+       {0xaa, 0x08, 0x0000},
+       {0xaa, 0x09, 0x0005},
+       {0xaa, 0x0a, 0x0002},
+       {0xaa, 0x0b, 0x0002},
+       {0xaa, 0x0c, 0x0005},
+       {0xaa, 0x0d, 0x0000},
+       {0xaa, 0x0e, 0x0002},
+       {0xaa, 0x14, 0x0081},
+
+/* Other registors */
+       {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, /* SensorCorrection */
+/* Frame retreiving */
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* AutoAdjustFPS */
+/* Gains */
+       {0xa0, 0xa0, ZC3XX_R1A8_DIGITALGAIN},   /* DigitalGain */
+/* Unknown */
+       {0xa0, 0x00, 0x01ad},
+/* Sharpness */
+       {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* SharpnessMode */
+       {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},   /* Sharpness05 */
+/* Other registors */
+       {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* OperationMode */
+/* Auto exposure and white balance */
+       {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},     /* AWBStatus */
+       {0xa0, 0x80, ZC3XX_R18D_YTARGET},       /* ????????? */
+/*Dead pixels */
+       {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* DeadPixelsMode */
+/* EEPROM */
+       {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},  /* EEPROMAccess */
+/* JPEG control */
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},  /* ClockSetting */
+       {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},   /* sharpness+ */
+       {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05},   /* sharpness- */
+/* Other registers */
+       {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* OperationMode */
+/* Auto exposure and white balance */
+       {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},     /* AWBStatus */
+/*Dead pixels */
+       {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* DeadPixelsMode */
+/* EEPROM */
+       {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},  /* EEPROMAccess */
+/* JPEG control */
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},  /* ClockSetting */
+       {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},   /* sharpness+ */
+       {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05},   /* sharpness- */
+
+       {0xa0, 0x58, ZC3XX_R10A_RGB00}, /* matrix */
+       {0xa0, 0xf4, ZC3XX_R10B_RGB01},
+       {0xa0, 0xf4, ZC3XX_R10C_RGB02},
+       {0xa0, 0xf4, ZC3XX_R10D_RGB10},
+       {0xa0, 0x58, ZC3XX_R10E_RGB11},
+       {0xa0, 0xf4, ZC3XX_R10F_RGB12},
+       {0xa0, 0xf4, ZC3XX_R110_RGB20},
+       {0xa0, 0xf4, ZC3XX_R111_RGB21},
+       {0xa0, 0x58, ZC3XX_R112_RGB22},
+/* Auto correction */
+       {0xa0, 0x03, ZC3XX_R181_WINXSTART},     /* WinXstart */
+       {0xa0, 0x08, ZC3XX_R182_WINXWIDTH},     /* WinXWidth */
+       {0xa0, 0x16, ZC3XX_R183_WINXCENTER},    /* WinXCenter */
+       {0xa0, 0x03, ZC3XX_R184_WINYSTART},     /* WinYStart */
+       {0xa0, 0x05, ZC3XX_R185_WINYWIDTH},     /* WinYWidth */
+       {0xa0, 0x14, ZC3XX_R186_WINYCENTER},    /* WinYCenter */
+       {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, /* AutoCorrectEnable */
+
+/* Auto exposure and white balance */
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* ExposureLimitHigh 0 */
+       {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID},  /* ExposureLimitMid */
+       {0xa0, 0xb1, ZC3XX_R192_EXPOSURELIMITLOW},  /* ExposureLimitLow 0xb1 */
+
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},   /* AntiFlickerHigh 0x00 */
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},    /* AntiFlickerLow 0x00 */
+       {0xa0, 0x87, ZC3XX_R197_ANTIFLICKERLOW},    /* AntiFlickerLow 0x87 */
+
+       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},      /* AEBFreeze 0x10 0x0c */
+       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},    /* AEBUnfreeze 0x30 0x18 */
+/* sensor on */
+       {0xaa, 0x07, 0x00b1},
+       {0xaa, 0x05, 0x0003},
+       {0xaa, 0x04, 0x0001},
+       {0xaa, 0x03, 0x003b},
+/* Gains */
+       {0xa0, 0x20, ZC3XX_R1A9_DIGITALLIMITDIFF},      /* DigitalLimitDiff */
+       {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP},       /* DigitalGainStep */
+       {0xa0, 0xa0, ZC3XX_R11D_GLOBALGAIN},    /* GlobalGain */
+       {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},    /* GlobalGain */
+/* Auto correction */
+       {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},     /* AutoCorrectEnable */
+       {0xa1, 0x01, 0x0180},                           /* AutoCorrectEnable */
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},     /* AutoCorrectEnable */
+/* Gains */
+       {0xa0, 0x40, ZC3XX_R116_RGAIN},         /* RGain */
+       {0xa0, 0x40, ZC3XX_R117_GGAIN},         /* GGain */
+       {0xa0, 0x40, ZC3XX_R118_BGAIN},         /* BGain */
+
+       {0xa0, 0x00, 0x0007},                   /* AutoCorrectEnable */
+       {0xa0, 0xff, ZC3XX_R018_FRAMELOST},     /* Frame adjust */
+       {}
+};
+static const struct usb_action pas106b_50HZ[] = {
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+       {0xa0, 0x06, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,06,cc */
+       {0xa0, 0x54, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,54,cc */
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+       {0xa0, 0x87, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,87,cc */
+       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},      /* 01,8c,10,cc */
+       {0xa0, 0x30, ZC3XX_R18F_AEUNFREEZE},    /* 01,8f,30,cc */
+       {0xaa, 0x03, 0x0021},                   /* 00,03,21,aa */
+       {0xaa, 0x04, 0x000c},                   /* 00,04,0c,aa */
+       {0xaa, 0x05, 0x0002},                   /* 00,05,02,aa */
+       {0xaa, 0x07, 0x001c},                   /* 00,07,1c,aa */
+       {0xa0, 0x04, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,04,cc */
+       {}
+};
+static const struct usb_action pas106b_60HZ[] = {
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+       {0xa0, 0x06, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,06,cc */
+       {0xa0, 0x2e, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,2e,cc */
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+       {0xa0, 0x71, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,71,cc */
+       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},      /* 01,8c,10,cc */
+       {0xa0, 0x30, ZC3XX_R18F_AEUNFREEZE},    /* 01,8f,30,cc */
+       {0xaa, 0x03, 0x001c},                   /* 00,03,1c,aa */
+       {0xaa, 0x04, 0x0004},                   /* 00,04,04,aa */
+       {0xaa, 0x05, 0x0001},                   /* 00,05,01,aa */
+       {0xaa, 0x07, 0x00c4},                   /* 00,07,c4,aa */
+       {0xa0, 0x04, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,04,cc */
+       {}
+};
+static const struct usb_action pas106b_NoFliker[] = {
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+       {0xa0, 0x06, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,06,cc */
+       {0xa0, 0x50, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,50,cc */
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+       {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */
+       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},      /* 01,8c,10,cc */
+       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},    /* 01,8f,20,cc */
+       {0xaa, 0x03, 0x0013},                   /* 00,03,13,aa */
+       {0xaa, 0x04, 0x0000},                   /* 00,04,00,aa */
+       {0xaa, 0x05, 0x0001},                   /* 00,05,01,aa */
+       {0xaa, 0x07, 0x0030},                   /* 00,07,30,aa */
+       {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
+       {}
+};
+
+static const struct usb_action pb03303x_Initial[] = {
+       {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+       {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
+       {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
+       {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+       {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+       {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+       {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+       {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, /* 8b -> dc */
+       {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+       {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+       {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+       {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+       {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+       {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
+       {0xaa, 0x01, 0x0001},
+       {0xaa, 0x06, 0x0000},
+       {0xaa, 0x08, 0x0483},
+       {0xaa, 0x01, 0x0004},
+       {0xaa, 0x08, 0x0006},
+       {0xaa, 0x02, 0x0011},
+       {0xaa, 0x03, 0x01e7},
+       {0xaa, 0x04, 0x0287},
+       {0xaa, 0x07, 0x3002},
+       {0xaa, 0x20, 0x1100},
+       {0xaa, 0x35, 0x0050},
+       {0xaa, 0x30, 0x0005},
+       {0xaa, 0x31, 0x0000},
+       {0xaa, 0x58, 0x0078},
+       {0xaa, 0x62, 0x0411},
+       {0xaa, 0x2b, 0x0028},
+       {0xaa, 0x2c, 0x0030},
+       {0xaa, 0x2d, 0x0030},
+       {0xaa, 0x2e, 0x0028},
+       {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID},
+       {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
+       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+       {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+       {0xa0, 0x00, 0x01ad},
+       {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+       {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+       {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+       {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+       {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
+       {0xa0, 0x78, ZC3XX_R18D_YTARGET},
+       {0xa0, 0x61, ZC3XX_R116_RGAIN},
+       {0xa0, 0x65, ZC3XX_R118_BGAIN},
+
+       {0xa1, 0x01, 0x0002},
+       {0xa0, 0x09, 0x01ad},
+       {0xa0, 0x15, 0x01ae},
+       {0xa0, 0x0d, 0x003a},
+       {0xa0, 0x02, 0x003b},
+       {0xa0, 0x00, 0x0038},
+       {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */
+       {0xa0, 0xf8, ZC3XX_R10B_RGB01},
+       {0xa0, 0xf8, ZC3XX_R10C_RGB02},
+       {0xa0, 0xf8, ZC3XX_R10D_RGB10},
+       {0xa0, 0x50, ZC3XX_R10E_RGB11},
+       {0xa0, 0xf8, ZC3XX_R10F_RGB12},
+       {0xa0, 0xf8, ZC3XX_R110_RGB20},
+       {0xa0, 0xf8, ZC3XX_R111_RGB21},
+       {0xa0, 0x50, ZC3XX_R112_RGB22},
+
+       {0xa1, 0x01, 0x0008},
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},  /* clock ? */
+       {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},   /* sharpness+ */
+       {0xa1, 0x01, 0x01c8},
+       {0xa1, 0x01, 0x01c9},
+       {0xa1, 0x01, 0x01ca},
+       {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05},   /* sharpness- */
+       {0xa0, 0x13, ZC3XX_R120_GAMMA00},       /* gamma 4 */
+       {0xa0, 0x38, ZC3XX_R121_GAMMA01},
+       {0xa0, 0x59, ZC3XX_R122_GAMMA02},
+       {0xa0, 0x79, ZC3XX_R123_GAMMA03},
+       {0xa0, 0x92, ZC3XX_R124_GAMMA04},
+       {0xa0, 0xa7, ZC3XX_R125_GAMMA05},
+       {0xa0, 0xb9, ZC3XX_R126_GAMMA06},
+       {0xa0, 0xc8, ZC3XX_R127_GAMMA07},
+       {0xa0, 0xd4, ZC3XX_R128_GAMMA08},
+       {0xa0, 0xdf, ZC3XX_R129_GAMMA09},
+       {0xa0, 0xe7, ZC3XX_R12A_GAMMA0A},
+       {0xa0, 0xee, ZC3XX_R12B_GAMMA0B},
+       {0xa0, 0xf4, ZC3XX_R12C_GAMMA0C},
+       {0xa0, 0xf9, ZC3XX_R12D_GAMMA0D},
+       {0xa0, 0xfc, ZC3XX_R12E_GAMMA0E},
+       {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
+       {0xa0, 0x26, ZC3XX_R130_GAMMA10},
+       {0xa0, 0x22, ZC3XX_R131_GAMMA11},
+       {0xa0, 0x20, ZC3XX_R132_GAMMA12},
+       {0xa0, 0x1c, ZC3XX_R133_GAMMA13},
+       {0xa0, 0x16, ZC3XX_R134_GAMMA14},
+       {0xa0, 0x13, ZC3XX_R135_GAMMA15},
+       {0xa0, 0x10, ZC3XX_R136_GAMMA16},
+       {0xa0, 0x0d, ZC3XX_R137_GAMMA17},
+       {0xa0, 0x0b, ZC3XX_R138_GAMMA18},
+       {0xa0, 0x09, ZC3XX_R139_GAMMA19},
+       {0xa0, 0x07, ZC3XX_R13A_GAMMA1A},
+       {0xa0, 0x06, ZC3XX_R13B_GAMMA1B},
+       {0xa0, 0x05, ZC3XX_R13C_GAMMA1C},
+       {0xa0, 0x04, ZC3XX_R13D_GAMMA1D},
+       {0xa0, 0x03, ZC3XX_R13E_GAMMA1E},
+       {0xa0, 0x02, ZC3XX_R13F_GAMMA1F},
+       {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */
+       {0xa0, 0xf8, ZC3XX_R10B_RGB01},
+       {0xa0, 0xf8, ZC3XX_R10C_RGB02},
+       {0xa0, 0xf8, ZC3XX_R10D_RGB10},
+       {0xa0, 0x50, ZC3XX_R10E_RGB11},
+       {0xa0, 0xf8, ZC3XX_R10F_RGB12},
+       {0xa0, 0xf8, ZC3XX_R110_RGB20},
+       {0xa0, 0xf8, ZC3XX_R111_RGB21},
+       {0xa0, 0x50, ZC3XX_R112_RGB22},
+
+       {0xa1, 0x01, 0x0180},
+       {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+       {0xaa, 0x05, 0x0009},
+       {0xaa, 0x09, 0x0134},
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+       {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+       {0xa0, 0xec, ZC3XX_R192_EXPOSURELIMITLOW},
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+       {0xa0, 0x9c, ZC3XX_R197_ANTIFLICKERLOW},
+       {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+       {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+       {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0},
+       {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1},
+       {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2},
+       {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x09, 0x01ad},
+       {0xa0, 0x15, 0x01ae},
+       {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa1, 0x01, 0x0180},
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+       {}
+};
+
+static const struct usb_action pb03303x_InitialScale[] = {
+       {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+       {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
+       {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
+       {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+       {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+       {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+       {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+       {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, /* 8b -> dc */
+       {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+       {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+       {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+       {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+       {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+       {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
+       {0xaa, 0x01, 0x0001},
+       {0xaa, 0x06, 0x0000},
+       {0xaa, 0x08, 0x0483},
+       {0xaa, 0x01, 0x0004},
+       {0xaa, 0x08, 0x0006},
+       {0xaa, 0x02, 0x0011},
+       {0xaa, 0x03, 0x01e7},
+       {0xaa, 0x04, 0x0287},
+       {0xaa, 0x07, 0x3002},
+       {0xaa, 0x20, 0x1100},
+       {0xaa, 0x35, 0x0050},
+       {0xaa, 0x30, 0x0005},
+       {0xaa, 0x31, 0x0000},
+       {0xaa, 0x58, 0x0078},
+       {0xaa, 0x62, 0x0411},
+       {0xaa, 0x2b, 0x0028},
+       {0xaa, 0x2c, 0x0030},
+       {0xaa, 0x2d, 0x0030},
+       {0xaa, 0x2e, 0x0028},
+       {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID},
+       {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
+       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+       {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+       {0xa0, 0x00, 0x01ad},
+       {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+       {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+       {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+       {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+       {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
+       {0xa0, 0x78, ZC3XX_R18D_YTARGET},
+       {0xa0, 0x61, ZC3XX_R116_RGAIN},
+       {0xa0, 0x65, ZC3XX_R118_BGAIN},
+
+       {0xa1, 0x01, 0x0002},
+
+       {0xa0, 0x09, 0x01ad},
+       {0xa0, 0x15, 0x01ae},
+
+       {0xa0, 0x0d, 0x003a},
+       {0xa0, 0x02, 0x003b},
+       {0xa0, 0x00, 0x0038},
+       {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */
+       {0xa0, 0xf8, ZC3XX_R10B_RGB01},
+       {0xa0, 0xf8, ZC3XX_R10C_RGB02},
+       {0xa0, 0xf8, ZC3XX_R10D_RGB10},
+       {0xa0, 0x50, ZC3XX_R10E_RGB11},
+       {0xa0, 0xf8, ZC3XX_R10F_RGB12},
+       {0xa0, 0xf8, ZC3XX_R110_RGB20},
+       {0xa0, 0xf8, ZC3XX_R111_RGB21},
+       {0xa0, 0x50, ZC3XX_R112_RGB22},
+
+       {0xa1, 0x01, 0x0008},
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},  /* clock ? */
+       {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},   /* sharpness+ */
+       {0xa1, 0x01, 0x01c8},
+       {0xa1, 0x01, 0x01c9},
+       {0xa1, 0x01, 0x01ca},
+       {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05},   /* sharpness- */
+
+       {0xa0, 0x13, ZC3XX_R120_GAMMA00},       /* gamma 4 */
+       {0xa0, 0x38, ZC3XX_R121_GAMMA01},
+       {0xa0, 0x59, ZC3XX_R122_GAMMA02},
+       {0xa0, 0x79, ZC3XX_R123_GAMMA03},
+       {0xa0, 0x92, ZC3XX_R124_GAMMA04},
+       {0xa0, 0xa7, ZC3XX_R125_GAMMA05},
+       {0xa0, 0xb9, ZC3XX_R126_GAMMA06},
+       {0xa0, 0xc8, ZC3XX_R127_GAMMA07},
+       {0xa0, 0xd4, ZC3XX_R128_GAMMA08},
+       {0xa0, 0xdf, ZC3XX_R129_GAMMA09},
+       {0xa0, 0xe7, ZC3XX_R12A_GAMMA0A},
+       {0xa0, 0xee, ZC3XX_R12B_GAMMA0B},
+       {0xa0, 0xf4, ZC3XX_R12C_GAMMA0C},
+       {0xa0, 0xf9, ZC3XX_R12D_GAMMA0D},
+       {0xa0, 0xfc, ZC3XX_R12E_GAMMA0E},
+       {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
+       {0xa0, 0x26, ZC3XX_R130_GAMMA10},
+       {0xa0, 0x22, ZC3XX_R131_GAMMA11},
+       {0xa0, 0x20, ZC3XX_R132_GAMMA12},
+       {0xa0, 0x1c, ZC3XX_R133_GAMMA13},
+       {0xa0, 0x16, ZC3XX_R134_GAMMA14},
+       {0xa0, 0x13, ZC3XX_R135_GAMMA15},
+       {0xa0, 0x10, ZC3XX_R136_GAMMA16},
+       {0xa0, 0x0d, ZC3XX_R137_GAMMA17},
+       {0xa0, 0x0b, ZC3XX_R138_GAMMA18},
+       {0xa0, 0x09, ZC3XX_R139_GAMMA19},
+       {0xa0, 0x07, ZC3XX_R13A_GAMMA1A},
+       {0xa0, 0x06, ZC3XX_R13B_GAMMA1B},
+       {0xa0, 0x05, ZC3XX_R13C_GAMMA1C},
+       {0xa0, 0x04, ZC3XX_R13D_GAMMA1D},
+       {0xa0, 0x03, ZC3XX_R13E_GAMMA1E},
+       {0xa0, 0x02, ZC3XX_R13F_GAMMA1F},
+       {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */
+       {0xa0, 0xf8, ZC3XX_R10B_RGB01},
+       {0xa0, 0xf8, ZC3XX_R10C_RGB02},
+       {0xa0, 0xf8, ZC3XX_R10D_RGB10},
+       {0xa0, 0x50, ZC3XX_R10E_RGB11},
+       {0xa0, 0xf8, ZC3XX_R10F_RGB12},
+       {0xa0, 0xf8, ZC3XX_R110_RGB20},
+       {0xa0, 0xf8, ZC3XX_R111_RGB21},
+       {0xa0, 0x50, ZC3XX_R112_RGB22},
+
+       {0xa1, 0x01, 0x0180},
+       {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+       {0xaa, 0x05, 0x0009},
+       {0xaa, 0x09, 0x0134},
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+       {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+       {0xa0, 0xec, ZC3XX_R192_EXPOSURELIMITLOW},
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+       {0xa0, 0x9c, ZC3XX_R197_ANTIFLICKERLOW},
+       {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+       {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+       {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0},
+       {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1},
+       {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2},
+       {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x09, 0x01ad},
+       {0xa0, 0x15, 0x01ae},
+       {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa1, 0x01, 0x0180},
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+       {}
+};
+static const struct usb_action pb0330xx_Initial[] = {
+       {0xa1, 0x01, 0x0008},
+       {0xa1, 0x01, 0x0008},
+       {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},  /* 00 */
+       {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
+       {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
+       {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+       {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+       {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+       {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+       {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+       {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+       {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+       {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xaa, 0x01, 0x0006},
+       {0xaa, 0x02, 0x0011},
+       {0xaa, 0x03, 0x01e7},
+       {0xaa, 0x04, 0x0287},
+       {0xaa, 0x06, 0x0003},
+       {0xaa, 0x07, 0x3002},
+       {0xaa, 0x20, 0x1100},
+       {0xaa, 0x2f, 0xf7b0},
+       {0xaa, 0x30, 0x0005},
+       {0xaa, 0x31, 0x0000},
+       {0xaa, 0x34, 0x0100},
+       {0xaa, 0x35, 0x0060},
+       {0xaa, 0x3d, 0x068f},
+       {0xaa, 0x40, 0x01e0},
+       {0xaa, 0x58, 0x0078},
+       {0xaa, 0x62, 0x0411},
+       {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID},
+       {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
+       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+       {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+       {0xa0, 0x00, 0x01ad},
+       {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+       {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+       {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+       {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+       {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
+       {0xa0, 0x6c, ZC3XX_R18D_YTARGET},
+       {0xa1, 0x01, 0x0002},
+       {0xa0, 0x09, 0x01ad},
+       {0xa0, 0x15, 0x01ae},
+       {0xa0, 0x00, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x02, ZC3XX_R090_I2CCOMMAND},
+       {0xa1, 0x01, 0x0091},
+       {0xa1, 0x01, 0x0095},
+       {0xa1, 0x01, 0x0096},
+       {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */
+       {0xa0, 0xf8, ZC3XX_R10B_RGB01},
+       {0xa0, 0xf8, ZC3XX_R10C_RGB02},
+       {0xa0, 0xf8, ZC3XX_R10D_RGB10},
+       {0xa0, 0x50, ZC3XX_R10E_RGB11},
+       {0xa0, 0xf8, ZC3XX_R10F_RGB12},
+       {0xa0, 0xf8, ZC3XX_R110_RGB20},
+       {0xa0, 0xf8, ZC3XX_R111_RGB21},
+       {0xa0, 0x50, ZC3XX_R112_RGB22},
+       {0xa1, 0x01, 0x0008},
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},  /* clock ? */
+       {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},   /* sharpness+ */
+       {0xa1, 0x01, 0x01c8},
+       {0xa1, 0x01, 0x01c9},
+       {0xa1, 0x01, 0x01ca},
+       {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05},   /* sharpness- */
+
+       {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */
+       {0xa0, 0xf8, ZC3XX_R10B_RGB01},
+       {0xa0, 0xf8, ZC3XX_R10C_RGB02},
+       {0xa0, 0xf8, ZC3XX_R10D_RGB10},
+       {0xa0, 0x50, ZC3XX_R10E_RGB11},
+       {0xa0, 0xf8, ZC3XX_R10F_RGB12},
+       {0xa0, 0xf8, ZC3XX_R110_RGB20},
+       {0xa0, 0xf8, ZC3XX_R111_RGB21},
+       {0xa0, 0x50, ZC3XX_R112_RGB22},
+       {0xa1, 0x01, 0x0180},
+       {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+       {0xaa, 0x05, 0x0066},
+       {0xaa, 0x09, 0x02b2},
+       {0xaa, 0x10, 0x0002},
+
+       {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+       {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+       {0xa0, 0x8c, ZC3XX_R192_EXPOSURELIMITLOW},
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+       {0xa0, 0x8a, ZC3XX_R197_ANTIFLICKERLOW},
+       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+       {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+       {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0},
+       {0xa0, 0xf0, ZC3XX_R01E_HSYNC_1},
+       {0xa0, 0xf8, ZC3XX_R01F_HSYNC_2},
+       {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+       {0xa0, 0x09, 0x01ad},
+       {0xa0, 0x15, 0x01ae},
+       {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa1, 0x01, 0x0180},
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa1, 0x01, 0x0008},
+       {0xa1, 0x01, 0x0007},
+/*     {0xa0, 0x30, 0x0007}, */
+/*     {0xa0, 0x00, 0x0007}, */
+       {}
+};
+
+static const struct usb_action pb0330xx_InitialScale[] = {
+       {0xa1, 0x01, 0x0008},
+       {0xa1, 0x01, 0x0008},
+       {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},  /* 00 */
+       {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
+       {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},   /* 10 */
+       {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+       {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+       {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+       {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+       {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+       {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+       {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+       {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xaa, 0x01, 0x0006},
+       {0xaa, 0x02, 0x0011},
+       {0xaa, 0x03, 0x01e7},
+       {0xaa, 0x04, 0x0287},
+       {0xaa, 0x06, 0x0003},
+       {0xaa, 0x07, 0x3002},
+       {0xaa, 0x20, 0x1100},
+       {0xaa, 0x2f, 0xf7b0},
+       {0xaa, 0x30, 0x0005},
+       {0xaa, 0x31, 0x0000},
+       {0xaa, 0x34, 0x0100},
+       {0xaa, 0x35, 0x0060},
+       {0xaa, 0x3d, 0x068f},
+       {0xaa, 0x40, 0x01e0},
+       {0xaa, 0x58, 0x0078},
+       {0xaa, 0x62, 0x0411},
+       {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID},
+       {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
+       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+       {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+       {0xa0, 0x00, 0x01ad},
+       {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+       {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+       {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+       {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+       {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
+       {0xa0, 0x6c, ZC3XX_R18D_YTARGET},
+       {0xa1, 0x01, 0x0002},
+       {0xa0, 0x09, 0x01ad},
+       {0xa0, 0x15, 0x01ae},
+       {0xa0, 0x00, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x02, ZC3XX_R090_I2CCOMMAND},
+       {0xa1, 0x01, 0x0091},
+       {0xa1, 0x01, 0x0095},
+       {0xa1, 0x01, 0x0096},
+       {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */
+       {0xa0, 0xf8, ZC3XX_R10B_RGB01},
+       {0xa0, 0xf8, ZC3XX_R10C_RGB02},
+       {0xa0, 0xf8, ZC3XX_R10D_RGB10},
+       {0xa0, 0x50, ZC3XX_R10E_RGB11},
+       {0xa0, 0xf8, ZC3XX_R10F_RGB12},
+       {0xa0, 0xf8, ZC3XX_R110_RGB20},
+       {0xa0, 0xf8, ZC3XX_R111_RGB21},
+       {0xa0, 0x50, ZC3XX_R112_RGB22},
+       {0xa1, 0x01, 0x0008},
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},  /* clock ? */
+       {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},   /* sharpness+ */
+       {0xa1, 0x01, 0x01c8},
+       {0xa1, 0x01, 0x01c9},
+       {0xa1, 0x01, 0x01ca},
+       {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05},   /* sharpness- */
+
+       {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */
+       {0xa0, 0xf8, ZC3XX_R10B_RGB01},
+       {0xa0, 0xf8, ZC3XX_R10C_RGB02},
+       {0xa0, 0xf8, ZC3XX_R10D_RGB10},
+       {0xa0, 0x50, ZC3XX_R10E_RGB11},
+       {0xa0, 0xf8, ZC3XX_R10F_RGB12},
+       {0xa0, 0xf8, ZC3XX_R110_RGB20},
+       {0xa0, 0xf8, ZC3XX_R111_RGB21},
+       {0xa0, 0x50, ZC3XX_R112_RGB22},
+       {0xa1, 0x01, 0x0180},
+       {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+       {0xaa, 0x05, 0x0066},
+       {0xaa, 0x09, 0x02b2},
+       {0xaa, 0x10, 0x0002},
+       {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+       {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+       {0xa0, 0x8c, ZC3XX_R192_EXPOSURELIMITLOW},
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+       {0xa0, 0x8a, ZC3XX_R197_ANTIFLICKERLOW},
+       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+       {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+       {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0},
+       {0xa0, 0xf0, ZC3XX_R01E_HSYNC_1},
+       {0xa0, 0xf8, ZC3XX_R01F_HSYNC_2},
+       {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+       {0xa0, 0x09, 0x01ad},
+       {0xa0, 0x15, 0x01ae},
+       {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa1, 0x01, 0x0180},
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa1, 0x01, 0x0008},
+       {0xa1, 0x01, 0x0007},
+/*     {0xa0, 0x30, 0x0007}, */
+/*     {0xa0, 0x00, 0x0007}, */
+       {}
+};
+static const struct usb_action pb0330_50HZ[] = {
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+       {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */
+       {0xa0, 0xee, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,ee,cc */
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+       {0xa0, 0x46, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,46,cc */
+       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+       {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */
+       {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */
+       {0xa0, 0x68, ZC3XX_R01D_HSYNC_0}, /* 00,1d,68,cc */
+       {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc */
+       {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc */
+       {}
+};
+static const struct usb_action pb0330_50HZScale[] = {
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+       {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */
+       {0xa0, 0xa0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,a0,cc */
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+       {0xa0, 0x7a, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,7a,cc */
+       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+       {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */
+       {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */
+       {0xa0, 0xe5, ZC3XX_R01D_HSYNC_0}, /* 00,1d,e5,cc */
+       {0xa0, 0xf0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,f0,cc */
+       {0xa0, 0xf8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,f8,cc */
+       {}
+};
+static const struct usb_action pb0330_60HZ[] = {
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+       {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */
+       {0xa0, 0xdd, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,dd,cc */
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+       {0xa0, 0x3d, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,3d,cc */
+       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+       {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */
+       {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */
+       {0xa0, 0x43, ZC3XX_R01D_HSYNC_0}, /* 00,1d,43,cc */
+       {0xa0, 0x50, ZC3XX_R01E_HSYNC_1}, /* 00,1e,50,cc */
+       {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, /* 00,1f,90,cc */
+       {}
+};
+static const struct usb_action pb0330_60HZScale[] = {
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+       {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */
+       {0xa0, 0xa0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,a0,cc */
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+       {0xa0, 0x7a, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,7a,cc */
+       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+       {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */
+       {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */
+       {0xa0, 0x41, ZC3XX_R01D_HSYNC_0}, /* 00,1d,41,cc */
+       {0xa0, 0x50, ZC3XX_R01E_HSYNC_1}, /* 00,1e,50,cc */
+       {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, /* 00,1f,90,cc */
+       {}
+};
+static const struct usb_action pb0330_NoFliker[] = {
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+       {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */
+       {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+       {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */
+       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+       {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
+       {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
+       {0xa0, 0x09, ZC3XX_R01D_HSYNC_0}, /* 00,1d,09,cc */
+       {0xa0, 0x40, ZC3XX_R01E_HSYNC_1}, /* 00,1e,40,cc */
+       {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, /* 00,1f,90,cc */
+       {}
+};
+static const struct usb_action pb0330_NoFlikerScale[] = {
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+       {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */
+       {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+       {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */
+       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},      /* 01,8c,10,cc */
+       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},    /* 01,8f,20,cc */
+       {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
+       {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
+       {0xa0, 0x09, ZC3XX_R01D_HSYNC_0},       /* 00,1d,09,cc */
+       {0xa0, 0x40, ZC3XX_R01E_HSYNC_1},       /* 00,1e,40,cc */
+       {0xa0, 0x90, ZC3XX_R01F_HSYNC_2},       /* 00,1f,90,cc */
+       {}
+};
+
+/* from oem9.inf - HKR,%PO2030%,Initial - 640x480 - (close to CS2102) */
+static const struct usb_action PO2030_mode0[] = {
+       {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
+       {0xa0, 0x04, ZC3XX_R002_CLOCKSELECT},   /* 00,02,04,cc */
+       {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
+       {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */
+       {0xa0, 0x04, ZC3XX_R080_HBLANKHIGH}, /* 00,80,04,cc */
+       {0xa0, 0x05, ZC3XX_R081_HBLANKLOW}, /* 00,81,05,cc */
+       {0xa0, 0x16, ZC3XX_R083_RGAINADDR}, /* 00,83,16,cc */
+       {0xa0, 0x18, ZC3XX_R085_BGAINADDR}, /* 00,85,18,cc */
+       {0xa0, 0x1a, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,1a,cc */
+       {0xa0, 0x1b, ZC3XX_R087_EXPTIMEMID}, /* 00,87,1b,cc */
+       {0xa0, 0x1c, ZC3XX_R088_EXPTIMELOW}, /* 00,88,1c,cc */
+       {0xa0, 0xee, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,ee,cc */
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00,08,03,cc */
+       {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */
+       {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */
+       {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */
+       {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */
+       {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */
+       {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,e0,cc */
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc */
+       {0xaa, 0x8d, 0x0008},                   /* 00,8d,08,aa */
+       {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},  /* 00,98,00,cc */
+       {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},  /* 00,9a,00,cc */
+       {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},     /* 01,1a,00,cc */
+       {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},     /* 01,1c,00,cc */
+       {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW},  /* 00,9c,e6,cc */
+       {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW},   /* 00,9e,86,cc */
+       {0xaa, 0x09, 0x00ce}, /* 00,09,ce,aa */
+       {0xaa, 0x0b, 0x0005}, /* 00,0b,05,aa */
+       {0xaa, 0x0d, 0x0054}, /* 00,0d,54,aa */
+       {0xaa, 0x0f, 0x00eb}, /* 00,0f,eb,aa */
+       {0xaa, 0x87, 0x0000}, /* 00,87,00,aa */
+       {0xaa, 0x88, 0x0004}, /* 00,88,04,aa */
+       {0xaa, 0x89, 0x0000}, /* 00,89,00,aa */
+       {0xaa, 0x8a, 0x0005}, /* 00,8a,05,aa */
+       {0xaa, 0x13, 0x0003}, /* 00,13,03,aa */
+       {0xaa, 0x16, 0x0040}, /* 00,16,40,aa */
+       {0xaa, 0x18, 0x0040}, /* 00,18,40,aa */
+       {0xaa, 0x1d, 0x0002}, /* 00,1d,02,aa */
+       {0xaa, 0x29, 0x00e8}, /* 00,29,e8,aa */
+       {0xaa, 0x45, 0x0045}, /* 00,45,45,aa */
+       {0xaa, 0x50, 0x00ed}, /* 00,50,ed,aa */
+       {0xaa, 0x51, 0x0025}, /* 00,51,25,aa */
+       {0xaa, 0x52, 0x0042}, /* 00,52,42,aa */
+       {0xaa, 0x53, 0x002f}, /* 00,53,2f,aa */
+       {0xaa, 0x79, 0x0025}, /* 00,79,25,aa */
+       {0xaa, 0x7b, 0x0000}, /* 00,7b,00,aa */
+       {0xaa, 0x7e, 0x0025}, /* 00,7e,25,aa */
+       {0xaa, 0x7f, 0x0025}, /* 00,7f,25,aa */
+       {0xaa, 0x21, 0x0000}, /* 00,21,00,aa */
+       {0xaa, 0x33, 0x0036}, /* 00,33,36,aa */
+       {0xaa, 0x36, 0x0060}, /* 00,36,60,aa */
+       {0xaa, 0x37, 0x0008}, /* 00,37,08,aa */
+       {0xaa, 0x3b, 0x0031}, /* 00,3b,31,aa */
+       {0xaa, 0x44, 0x000f}, /* 00,44,0f,aa */
+       {0xaa, 0x58, 0x0002}, /* 00,58,02,aa */
+       {0xaa, 0x66, 0x00c0}, /* 00,66,c0,aa */
+       {0xaa, 0x67, 0x0044}, /* 00,67,44,aa */
+       {0xaa, 0x6b, 0x00a0}, /* 00,6b,a0,aa */
+       {0xaa, 0x6c, 0x0054}, /* 00,6c,54,aa */
+       {0xaa, 0xd6, 0x0007}, /* 00,d6,07,aa */
+       {0xa0, 0xf7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,f7,cc */
+       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */
+       {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */
+       {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */
+       {0xa0, 0x00, 0x01ad}, /* 01,ad,00,cc */
+       {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */
+       {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */
+       {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */
+       {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */
+       {0xa0, 0x7a, ZC3XX_R116_RGAIN}, /* 01,16,7a,cc */
+       {0xa0, 0x4a, ZC3XX_R118_BGAIN}, /* 01,18,4a,cc */
+       {}
+};
+
+/* from oem9.inf - HKR,%PO2030%,InitialScale - 320x240 */
+static const struct usb_action PO2030_mode1[] = {
+       {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
+       {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, /* 00,02,10,cc */
+       {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
+       {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */
+       {0xa0, 0x04, ZC3XX_R080_HBLANKHIGH}, /* 00,80,04,cc */
+       {0xa0, 0x05, ZC3XX_R081_HBLANKLOW}, /* 00,81,05,cc */
+       {0xa0, 0x16, ZC3XX_R083_RGAINADDR}, /* 00,83,16,cc */
+       {0xa0, 0x18, ZC3XX_R085_BGAINADDR}, /* 00,85,18,cc */
+       {0xa0, 0x1a, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,1a,cc */
+       {0xa0, 0x1b, ZC3XX_R087_EXPTIMEMID}, /* 00,87,1b,cc */
+       {0xa0, 0x1c, ZC3XX_R088_EXPTIMELOW}, /* 00,88,1c,cc */
+       {0xa0, 0xee, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,ee,cc */
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00,08,03,cc */
+       {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */
+       {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */
+       {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */
+       {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */
+       {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */
+       {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,e0,cc */
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc */
+       {0xaa, 0x8d, 0x0008},                   /* 00,8d,08,aa */
+       {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */
+       {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */
+       {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */
+       {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */
+       {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,e8,cc */
+       {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,88,cc */
+       {0xaa, 0x09, 0x00cc}, /* 00,09,cc,aa */
+       {0xaa, 0x0b, 0x0005}, /* 00,0b,05,aa */
+       {0xaa, 0x0d, 0x0058}, /* 00,0d,58,aa */
+       {0xaa, 0x0f, 0x00ed}, /* 00,0f,ed,aa */
+       {0xaa, 0x87, 0x0000}, /* 00,87,00,aa */
+       {0xaa, 0x88, 0x0004}, /* 00,88,04,aa */
+       {0xaa, 0x89, 0x0000}, /* 00,89,00,aa */
+       {0xaa, 0x8a, 0x0005}, /* 00,8a,05,aa */
+       {0xaa, 0x13, 0x0003}, /* 00,13,03,aa */
+       {0xaa, 0x16, 0x0040}, /* 00,16,40,aa */
+       {0xaa, 0x18, 0x0040}, /* 00,18,40,aa */
+       {0xaa, 0x1d, 0x0002}, /* 00,1d,02,aa */
+       {0xaa, 0x29, 0x00e8}, /* 00,29,e8,aa */
+       {0xaa, 0x45, 0x0045}, /* 00,45,45,aa */
+       {0xaa, 0x50, 0x00ed}, /* 00,50,ed,aa */
+       {0xaa, 0x51, 0x0025}, /* 00,51,25,aa */
+       {0xaa, 0x52, 0x0042}, /* 00,52,42,aa */
+       {0xaa, 0x53, 0x002f}, /* 00,53,2f,aa */
+       {0xaa, 0x79, 0x0025}, /* 00,79,25,aa */
+       {0xaa, 0x7b, 0x0000}, /* 00,7b,00,aa */
+       {0xaa, 0x7e, 0x0025}, /* 00,7e,25,aa */
+       {0xaa, 0x7f, 0x0025}, /* 00,7f,25,aa */
+       {0xaa, 0x21, 0x0000}, /* 00,21,00,aa */
+       {0xaa, 0x33, 0x0036}, /* 00,33,36,aa */
+       {0xaa, 0x36, 0x0060}, /* 00,36,60,aa */
+       {0xaa, 0x37, 0x0008}, /* 00,37,08,aa */
+       {0xaa, 0x3b, 0x0031}, /* 00,3b,31,aa */
+       {0xaa, 0x44, 0x000f}, /* 00,44,0f,aa */
+       {0xaa, 0x58, 0x0002}, /* 00,58,02,aa */
+       {0xaa, 0x66, 0x00c0}, /* 00,66,c0,aa */
+       {0xaa, 0x67, 0x0044}, /* 00,67,44,aa */
+       {0xaa, 0x6b, 0x00a0}, /* 00,6b,a0,aa */
+       {0xaa, 0x6c, 0x0054}, /* 00,6c,54,aa */
+       {0xaa, 0xd6, 0x0007}, /* 00,d6,07,aa */
+       {0xa0, 0xf7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,f7,cc */
+       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */
+       {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */
+       {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */
+       {0xa0, 0x00, 0x01ad}, /* 01,ad,00,cc */
+       {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */
+       {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */
+       {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */
+       {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */
+       {0xa0, 0x7a, ZC3XX_R116_RGAIN}, /* 01,16,7a,cc */
+       {0xa0, 0x4a, ZC3XX_R118_BGAIN}, /* 01,18,4a,cc */
+       {}
+};
+
+static const struct usb_action PO2030_50HZ[] = {
+       {0xaa, 0x8d, 0x0008}, /* 00,8d,08,aa */
+       {0xaa, 0x1a, 0x0001}, /* 00,1a,01,aa */
+       {0xaa, 0x1b, 0x000a}, /* 00,1b,0a,aa */
+       {0xaa, 0x1c, 0x00b0}, /* 00,1c,b0,aa */
+       {0xa0, 0x05, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,05,cc */
+       {0xa0, 0x35, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,35,cc */
+       {0xa0, 0x70, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,70,cc */
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+       {0xa0, 0x85, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,85,cc */
+       {0xa0, 0x58, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,58,cc */
+       {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0c,cc */
+       {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,18,cc */
+       {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,60,cc */
+       {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */
+       {0xa0, 0x22, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,22,cc */
+       {0xa0, 0x88, ZC3XX_R18D_YTARGET}, /* 01,8d,88,cc */
+       {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,58,cc */
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc */
+       {}
+};
+
+static const struct usb_action PO2030_60HZ[] = {
+       {0xaa, 0x8d, 0x0008}, /* 00,8d,08,aa */
+       {0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa */
+       {0xaa, 0x1b, 0x00de}, /* 00,1b,de,aa */
+       {0xaa, 0x1c, 0x0040}, /* 00,1c,40,aa */
+       {0xa0, 0x08, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,08,cc */
+       {0xa0, 0xae, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,ae,cc */
+       {0xa0, 0x80, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,80,cc */
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+       {0xa0, 0x6f, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,6f,cc */
+       {0xa0, 0x20, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,20,cc */
+       {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0c,cc */
+       {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,18,cc */
+       {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,60,cc */
+       {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */
+       {0xa0, 0x22, ZC3XX_R1AA_DIGITALGAINSTEP},       /* 01,aa,22,cc */
+       {0xa0, 0x88, ZC3XX_R18D_YTARGET},               /* 01,8d,88,cc */
+                                                       /* win: 01,8d,80 */
+       {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN},            /* 01,1d,58,cc */
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},     /* 01,80,42,cc */
+       {}
+};
+
+static const struct usb_action PO2030_NoFliker[] = {
+       {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */
+       {0xaa, 0x8d, 0x000d}, /* 00,8d,0d,aa */
+       {0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa */
+       {0xaa, 0x1b, 0x0002}, /* 00,1b,02,aa */
+       {0xaa, 0x1c, 0x0078}, /* 00,1c,78,aa */
+       {0xaa, 0x46, 0x0000}, /* 00,46,00,aa */
+       {0xaa, 0x15, 0x0000}, /* 00,15,00,aa */
+       {}
+};
+
+/* TEST */
+static const struct usb_action tas5130CK_Initial[] = {
+       {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+       {0xa0, 0x01, 0x003b},
+       {0xa0, 0x0e, 0x003a},
+       {0xa0, 0x01, 0x0038},
+       {0xa0, 0x0b, 0x0039},
+       {0xa0, 0x00, 0x0038},
+       {0xa0, 0x0b, 0x0039},
+       {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+       {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
+       {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
+       {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+       {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+       {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+       {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+       {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
+       {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+       {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+       {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+       {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+       {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+       {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
+       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x01, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x06, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x08, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x83, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x04, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x01, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x08, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x06, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x02, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x11, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x03, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0xE7, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x01, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x04, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x87, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x02, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x07, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x30, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x51, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x35, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x7F, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x30, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x05, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x31, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x58, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x78, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x62, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x11, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x04, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x2B, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x7f, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x2c, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x7f, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x2D, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x7f, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x2e, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x7f, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID},
+       {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION},
+       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+       {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+       {0xa0, 0x09, 0x01ad},
+       {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+       {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+       {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+       {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+       {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
+       {0xa0, 0x6c, ZC3XX_R18D_YTARGET},
+       {0xa0, 0x61, ZC3XX_R116_RGAIN},
+       {0xa0, 0x65, ZC3XX_R118_BGAIN},
+       {0xa0, 0x09, 0x01ad},
+       {0xa0, 0x15, 0x01ae},
+       {0xa0, 0x4c, ZC3XX_R10A_RGB00}, /* matrix */
+       {0xa0, 0xf1, ZC3XX_R10B_RGB01},
+       {0xa0, 0x03, ZC3XX_R10C_RGB02},
+       {0xa0, 0xfe, ZC3XX_R10D_RGB10},
+       {0xa0, 0x51, ZC3XX_R10E_RGB11},
+       {0xa0, 0xf1, ZC3XX_R10F_RGB12},
+       {0xa0, 0xec, ZC3XX_R110_RGB20},
+       {0xa0, 0x03, ZC3XX_R111_RGB21},
+       {0xa0, 0x51, ZC3XX_R112_RGB22},
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+       {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},   /* sharpness+ */
+       {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05},   /* sharpness- */
+       {0xa0, 0x38, ZC3XX_R120_GAMMA00},       /* gamma > 5 */
+       {0xa0, 0x51, ZC3XX_R121_GAMMA01},
+       {0xa0, 0x6e, ZC3XX_R122_GAMMA02},
+       {0xa0, 0x8c, ZC3XX_R123_GAMMA03},
+       {0xa0, 0xa2, ZC3XX_R124_GAMMA04},
+       {0xa0, 0xb6, ZC3XX_R125_GAMMA05},
+       {0xa0, 0xc8, ZC3XX_R126_GAMMA06},
+       {0xa0, 0xd6, ZC3XX_R127_GAMMA07},
+       {0xa0, 0xe2, ZC3XX_R128_GAMMA08},
+       {0xa0, 0xed, ZC3XX_R129_GAMMA09},
+       {0xa0, 0xf5, ZC3XX_R12A_GAMMA0A},
+       {0xa0, 0xfc, ZC3XX_R12B_GAMMA0B},
+       {0xa0, 0xff, ZC3XX_R12C_GAMMA0C},
+       {0xa0, 0xff, ZC3XX_R12D_GAMMA0D},
+       {0xa0, 0xff, ZC3XX_R12E_GAMMA0E},
+       {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
+       {0xa0, 0x12, ZC3XX_R130_GAMMA10},
+       {0xa0, 0x1b, ZC3XX_R131_GAMMA11},
+       {0xa0, 0x1d, ZC3XX_R132_GAMMA12},
+       {0xa0, 0x1a, ZC3XX_R133_GAMMA13},
+       {0xa0, 0x15, ZC3XX_R134_GAMMA14},
+       {0xa0, 0x12, ZC3XX_R135_GAMMA15},
+       {0xa0, 0x0f, ZC3XX_R136_GAMMA16},
+       {0xa0, 0x0d, ZC3XX_R137_GAMMA17},
+       {0xa0, 0x0b, ZC3XX_R138_GAMMA18},
+       {0xa0, 0x09, ZC3XX_R139_GAMMA19},
+       {0xa0, 0x07, ZC3XX_R13A_GAMMA1A},
+       {0xa0, 0x05, ZC3XX_R13B_GAMMA1B},
+       {0xa0, 0x00, ZC3XX_R13C_GAMMA1C},
+       {0xa0, 0x00, ZC3XX_R13D_GAMMA1D},
+       {0xa0, 0x00, ZC3XX_R13E_GAMMA1E},
+       {0xa0, 0x01, ZC3XX_R13F_GAMMA1F},
+       {0xa0, 0x4c, ZC3XX_R10A_RGB00}, /* matrix */
+       {0xa0, 0xf1, ZC3XX_R10B_RGB01},
+       {0xa0, 0x03, ZC3XX_R10C_RGB02},
+       {0xa0, 0xfe, ZC3XX_R10D_RGB10},
+       {0xa0, 0x51, ZC3XX_R10E_RGB11},
+       {0xa0, 0xf1, ZC3XX_R10F_RGB12},
+       {0xa0, 0xec, ZC3XX_R110_RGB20},
+       {0xa0, 0x03, ZC3XX_R111_RGB21},
+       {0xa0, 0x51, ZC3XX_R112_RGB22},
+       {0xa0, 0x10, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+       {0xa0, 0x05, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x09, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x09, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x34, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x01, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+       {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+       {0xa0, 0xd2, ZC3XX_R192_EXPOSURELIMITLOW},
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+       {0xa0, 0x9a, ZC3XX_R197_ANTIFLICKERLOW},
+       {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+       {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+       {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0},
+       {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1},
+       {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2},
+       {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x09, 0x01ad},
+       {0xa0, 0x15, 0x01ae},
+       {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+       {}
+};
+
+static const struct usb_action tas5130CK_InitialScale[] = {
+       {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+       {0xa0, 0x01, 0x003b},
+       {0xa0, 0x0e, 0x003a},
+       {0xa0, 0x01, 0x0038},
+       {0xa0, 0x0b, 0x0039},
+       {0xa0, 0x00, 0x0038},
+       {0xa0, 0x0b, 0x0039},
+       {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+       {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
+       {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
+       {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+       {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+       {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+       {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+       {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
+       {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+       {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+       {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+       {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+       {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+       {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
+       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x01, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x06, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x08, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x83, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x04, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x01, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x08, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x06, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x02, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x11, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x03, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0xe5, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x01, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x04, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x85, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x02, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x07, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x30, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x51, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x35, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x7F, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x50, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x30, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x05, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x31, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x58, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x78, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x62, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x11, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x04, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x2B, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x7f, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x2C, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x7F, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x2D, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x7f, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x2e, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x7f, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID},
+       {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION},
+       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+       {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+       {0xa0, 0x09, 0x01ad},
+       {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+       {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+       {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+       {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+       {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
+       {0xa0, 0x6c, ZC3XX_R18D_YTARGET},
+       {0xa0, 0x61, ZC3XX_R116_RGAIN},
+       {0xa0, 0x65, ZC3XX_R118_BGAIN},
+       {0xa0, 0x09, 0x01ad},
+       {0xa0, 0x15, 0x01ae},
+       {0xa0, 0x4c, ZC3XX_R10A_RGB00}, /* matrix */
+       {0xa0, 0xf1, ZC3XX_R10B_RGB01},
+       {0xa0, 0x03, ZC3XX_R10C_RGB02},
+       {0xa0, 0xfe, ZC3XX_R10D_RGB10},
+       {0xa0, 0x51, ZC3XX_R10E_RGB11},
+       {0xa0, 0xf1, ZC3XX_R10F_RGB12},
+       {0xa0, 0xec, ZC3XX_R110_RGB20},
+       {0xa0, 0x03, ZC3XX_R111_RGB21},
+       {0xa0, 0x51, ZC3XX_R112_RGB22},
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+       {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},   /* sharpness+ */
+       {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05},   /* sharpness- */
+       {0xa0, 0x38, ZC3XX_R120_GAMMA00},       /* gamma > 5 */
+       {0xa0, 0x51, ZC3XX_R121_GAMMA01},
+       {0xa0, 0x6e, ZC3XX_R122_GAMMA02},
+       {0xa0, 0x8c, ZC3XX_R123_GAMMA03},
+       {0xa0, 0xa2, ZC3XX_R124_GAMMA04},
+       {0xa0, 0xb6, ZC3XX_R125_GAMMA05},
+       {0xa0, 0xc8, ZC3XX_R126_GAMMA06},
+       {0xa0, 0xd6, ZC3XX_R127_GAMMA07},
+       {0xa0, 0xe2, ZC3XX_R128_GAMMA08},
+       {0xa0, 0xed, ZC3XX_R129_GAMMA09},
+       {0xa0, 0xf5, ZC3XX_R12A_GAMMA0A},
+       {0xa0, 0xfc, ZC3XX_R12B_GAMMA0B},
+       {0xa0, 0xff, ZC3XX_R12C_GAMMA0C},
+       {0xa0, 0xff, ZC3XX_R12D_GAMMA0D},
+       {0xa0, 0xff, ZC3XX_R12E_GAMMA0E},
+       {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
+       {0xa0, 0x12, ZC3XX_R130_GAMMA10},
+       {0xa0, 0x1b, ZC3XX_R131_GAMMA11},
+       {0xa0, 0x1d, ZC3XX_R132_GAMMA12},
+       {0xa0, 0x1a, ZC3XX_R133_GAMMA13},
+       {0xa0, 0x15, ZC3XX_R134_GAMMA14},
+       {0xa0, 0x12, ZC3XX_R135_GAMMA15},
+       {0xa0, 0x0f, ZC3XX_R136_GAMMA16},
+       {0xa0, 0x0d, ZC3XX_R137_GAMMA17},
+       {0xa0, 0x0b, ZC3XX_R138_GAMMA18},
+       {0xa0, 0x09, ZC3XX_R139_GAMMA19},
+       {0xa0, 0x07, ZC3XX_R13A_GAMMA1A},
+       {0xa0, 0x05, ZC3XX_R13B_GAMMA1B},
+       {0xa0, 0x00, ZC3XX_R13C_GAMMA1C},
+       {0xa0, 0x00, ZC3XX_R13D_GAMMA1D},
+       {0xa0, 0x00, ZC3XX_R13E_GAMMA1E},
+       {0xa0, 0x01, ZC3XX_R13F_GAMMA1F},
+       {0xa0, 0x4c, ZC3XX_R10A_RGB00}, /* matrix */
+       {0xa0, 0xf1, ZC3XX_R10B_RGB01},
+       {0xa0, 0x03, ZC3XX_R10C_RGB02},
+       {0xa0, 0xfe, ZC3XX_R10D_RGB10},
+       {0xa0, 0x51, ZC3XX_R10E_RGB11},
+       {0xa0, 0xf1, ZC3XX_R10F_RGB12},
+       {0xa0, 0xec, ZC3XX_R110_RGB20},
+       {0xa0, 0x03, ZC3XX_R111_RGB21},
+       {0xa0, 0x51, ZC3XX_R112_RGB22},
+       {0xa0, 0x10, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+       {0xa0, 0x05, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0x62, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x09, ZC3XX_R092_I2CADDRESSSELECT},
+       {0xa0, 0xaa, ZC3XX_R093_I2CSETVALUE},
+       {0xa0, 0x01, ZC3XX_R094_I2CWRITEACK},
+       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+       {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID},
+       {0xa0, 0x9b, ZC3XX_R192_EXPOSURELIMITLOW},
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+       {0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW},
+       {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+       {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+       {0xa0, 0x62, ZC3XX_R01D_HSYNC_0},
+       {0xa0, 0x90, ZC3XX_R01E_HSYNC_1},
+       {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2},
+       {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+       {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x09, 0x01ad},
+       {0xa0, 0x15, 0x01ae},
+       {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x30, 0x0007},
+       {0xa0, 0x02, ZC3XX_R008_CLOCKSETTING},
+       {0xa0, 0x00, 0x0007},
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+       {}
+};
+
+static const struct usb_action tas5130cxx_Initial[] = {
+       {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+       {0xa0, 0x50, ZC3XX_R002_CLOCKSELECT},
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+       {0xa0, 0x02, ZC3XX_R010_CMOSSENSORSELECT},
+       {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+       {0xa0, 0x00, ZC3XX_R001_SYSTEMOPERATING},
+       {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x07, ZC3XX_R0A5_EXPOSUREGAIN},
+       {0xa0, 0x02, ZC3XX_R0A6_EXPOSUREBLACKLVL},
+
+       {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+       {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+       {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+       {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+
+       {0xa0, 0x04, ZC3XX_R098_WINYSTARTLOW},
+       {0xa0, 0x0f, ZC3XX_R09A_WINXSTARTLOW},
+       {0xa0, 0x04, ZC3XX_R11A_FIRSTYLOW},
+       {0xa0, 0x0f, ZC3XX_R11C_FIRSTXLOW},
+       {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW},
+       {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH},
+       {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW},
+       {0xa0, 0x06, ZC3XX_R08D_COMPABILITYMODE},
+       {0xa0, 0xf7, ZC3XX_R101_SENSORCORRECTION},
+       {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+       {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+       {0xa0, 0x68, ZC3XX_R18D_YTARGET},
+       {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
+       {0xa0, 0x00, 0x01ad},
+       {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+       {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+       {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+       {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+       {0xa1, 0x01, 0x0002},
+       {0xa1, 0x01, 0x0008},
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},  /* clock ? */
+       {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},   /* sharpness+ */
+       {0xa1, 0x01, 0x01c8},
+       {0xa1, 0x01, 0x01c9},
+       {0xa1, 0x01, 0x01ca},
+       {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05},   /* sharpness- */
+
+       {0xa0, 0x68, ZC3XX_R10A_RGB00}, /* matrix */
+       {0xa0, 0xec, ZC3XX_R10B_RGB01},
+       {0xa0, 0xec, ZC3XX_R10C_RGB02},
+       {0xa0, 0xec, ZC3XX_R10D_RGB10},
+       {0xa0, 0x68, ZC3XX_R10E_RGB11},
+       {0xa0, 0xec, ZC3XX_R10F_RGB12},
+       {0xa0, 0xec, ZC3XX_R110_RGB20},
+       {0xa0, 0xec, ZC3XX_R111_RGB21},
+       {0xa0, 0x68, ZC3XX_R112_RGB22},
+
+       {0xa1, 0x01, 0x018d},
+       {0xa0, 0x90, ZC3XX_R18D_YTARGET},       /* 90 */
+       {0xa1, 0x01, 0x0180},
+       {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+
+       {0xaa, 0xa3, 0x0001},
+       {0xaa, 0xa4, 0x0077},
+       {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH},
+       {0xa0, 0x77, ZC3XX_R0A4_EXPOSURETIMELOW},
+
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},     /* 00 */
+       {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID},      /* 03 */
+       {0xa0, 0xe8, ZC3XX_R192_EXPOSURELIMITLOW},      /* e8 */
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},       /* 0 */
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},        /* 0 */
+       {0xa0, 0x7d, ZC3XX_R197_ANTIFLICKERLOW},        /* 7d */
+
+       {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF},      /* 08 */
+       {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},       /* 24 */
+       {0xa0, 0xf0, ZC3XX_R01D_HSYNC_0},
+       {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1},
+       {0xa0, 0xf8, ZC3XX_R01F_HSYNC_2},
+       {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+       {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH},
+       {0xa0, 0xc0, ZC3XX_R0A0_MAXXLOW},
+       {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},    /* 50 */
+       {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa1, 0x01, 0x0180},
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+       {}
+};
+static const struct usb_action tas5130cxx_InitialScale[] = {
+       {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+       {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+       {0xa0, 0x40, ZC3XX_R002_CLOCKSELECT},
+
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+       {0xa1, 0x01, 0x0008},
+
+       {0xa0, 0x02, ZC3XX_R010_CMOSSENSORSELECT},
+       {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+       {0xa0, 0x00, ZC3XX_R001_SYSTEMOPERATING},
+       {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x07, ZC3XX_R0A5_EXPOSUREGAIN},
+       {0xa0, 0x02, ZC3XX_R0A6_EXPOSUREBLACKLVL},
+       {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+       {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+       {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+       {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+       {0xa0, 0x05, ZC3XX_R098_WINYSTARTLOW},
+       {0xa0, 0x0f, ZC3XX_R09A_WINXSTARTLOW},
+       {0xa0, 0x05, ZC3XX_R11A_FIRSTYLOW},
+       {0xa0, 0x0f, ZC3XX_R11C_FIRSTXLOW},
+       {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW},
+       {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH},
+       {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW},
+       {0xa0, 0x06, ZC3XX_R08D_COMPABILITYMODE},
+       {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
+       {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+       {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+       {0xa0, 0x68, ZC3XX_R18D_YTARGET},
+       {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
+       {0xa0, 0x00, 0x01ad},
+       {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+       {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+       {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+       {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+       {0xa1, 0x01, 0x0002},
+       {0xa1, 0x01, 0x0008},
+
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+       {0xa1, 0x01, 0x0008},   /* clock ? */
+       {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},   /* sharpness+ */
+       {0xa1, 0x01, 0x01c8},
+       {0xa1, 0x01, 0x01c9},
+       {0xa1, 0x01, 0x01ca},
+       {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05},   /* sharpness- */
+
+       {0xa0, 0x68, ZC3XX_R10A_RGB00}, /* matrix */
+       {0xa0, 0xec, ZC3XX_R10B_RGB01},
+       {0xa0, 0xec, ZC3XX_R10C_RGB02},
+       {0xa0, 0xec, ZC3XX_R10D_RGB10},
+       {0xa0, 0x68, ZC3XX_R10E_RGB11},
+       {0xa0, 0xec, ZC3XX_R10F_RGB12},
+       {0xa0, 0xec, ZC3XX_R110_RGB20},
+       {0xa0, 0xec, ZC3XX_R111_RGB21},
+       {0xa0, 0x68, ZC3XX_R112_RGB22},
+
+       {0xa1, 0x01, 0x018d},
+       {0xa0, 0x90, ZC3XX_R18D_YTARGET},
+       {0xa1, 0x01, 0x0180},
+       {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+       {0xaa, 0xa3, 0x0001},
+       {0xaa, 0xa4, 0x0063},
+       {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH},
+       {0xa0, 0x63, ZC3XX_R0A4_EXPOSURETIMELOW},
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+       {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID},
+       {0xa0, 0x38, ZC3XX_R192_EXPOSURELIMITLOW},
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+       {0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW},
+       {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF},
+       {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+       {0xa0, 0xd3, ZC3XX_R01D_HSYNC_0},
+       {0xa0, 0xda, ZC3XX_R01E_HSYNC_1},
+       {0xa0, 0xea, ZC3XX_R01F_HSYNC_2},
+       {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+       {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH},
+       {0xa0, 0x4c, ZC3XX_R0A0_MAXXLOW},
+       {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
+       {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa1, 0x01, 0x0180},
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+       {}
+};
+static const struct usb_action tas5130cxx_50HZ[] = {
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+       {0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */
+       {0xaa, 0xa4, 0x0063}, /* 00,a4,63,aa */
+       {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */
+       {0xa0, 0x63, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,63,cc */
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+       {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,02,cc */
+       {0xa0, 0x38, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,38,cc */
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+       {0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,47,cc */
+       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+       {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */
+       {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */
+       {0xa0, 0xd3, ZC3XX_R01D_HSYNC_0}, /* 00,1d,d3,cc */
+       {0xa0, 0xda, ZC3XX_R01E_HSYNC_1}, /* 00,1e,da,cc */
+       {0xa0, 0xea, ZC3XX_R01F_HSYNC_2}, /* 00,1f,ea,cc */
+       {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+       {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,03,cc */
+       {}
+};
+static const struct usb_action tas5130cxx_50HZScale[] = {
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+       {0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */
+       {0xaa, 0xa4, 0x0077}, /* 00,a4,77,aa */
+       {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */
+       {0xa0, 0x77, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,77,cc */
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+       {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,03,cc */
+       {0xa0, 0xe8, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,e8,cc */
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+       {0xa0, 0x7d, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,7d,cc */
+       {0xa0, 0x14, ZC3XX_R18C_AEFREEZE}, /* 01,8c,14,cc */
+       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+       {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */
+       {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */
+       {0xa0, 0xf0, ZC3XX_R01D_HSYNC_0}, /* 00,1d,f0,cc */
+       {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1}, /* 00,1e,f4,cc */
+       {0xa0, 0xf8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,f8,cc */
+       {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+       {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,03,cc */
+       {}
+};
+static const struct usb_action tas5130cxx_60HZ[] = {
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+       {0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */
+       {0xaa, 0xa4, 0x0036}, /* 00,a4,36,aa */
+       {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */
+       {0xa0, 0x36, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,36,cc */
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+       {0xa0, 0x01, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,01,cc */
+       {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+       {0xa0, 0x3e, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,3e,cc */
+       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+       {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */
+       {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */
+       {0xa0, 0xca, ZC3XX_R01D_HSYNC_0}, /* 00,1d,ca,cc */
+       {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d0,cc */
+       {0xa0, 0xe0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e0,cc */
+       {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+       {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,03,cc */
+       {}
+};
+static const struct usb_action tas5130cxx_60HZScale[] = {
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+       {0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */
+       {0xaa, 0xa4, 0x0077}, /* 00,a4,77,aa */
+       {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */
+       {0xa0, 0x77, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,77,cc */
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+       {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,03,cc */
+       {0xa0, 0xe8, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,e8,cc */
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+       {0xa0, 0x7d, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,7d,cc */
+       {0xa0, 0x14, ZC3XX_R18C_AEFREEZE}, /* 01,8c,14,cc */
+       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+       {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */
+       {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */
+       {0xa0, 0xc8, ZC3XX_R01D_HSYNC_0}, /* 00,1d,c8,cc */
+       {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d0,cc */
+       {0xa0, 0xe0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e0,cc */
+       {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+       {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,03,cc */
+       {}
+};
+static const struct usb_action tas5130cxx_NoFliker[] = {
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+       {0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */
+       {0xaa, 0xa4, 0x0040}, /* 00,a4,40,aa */
+       {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */
+       {0xa0, 0x40, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,40,cc */
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+       {0xa0, 0x01, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,01,cc */
+       {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+       {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */
+       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+       {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
+       {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
+       {0xa0, 0xbc, ZC3XX_R01D_HSYNC_0}, /* 00,1d,bc,cc */
+       {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d0,cc */
+       {0xa0, 0xe0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e0,cc */
+       {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+       {0xa0, 0x02, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,02,cc */
+       {}
+};
+
+static const struct usb_action tas5130cxx_NoFlikerScale[] = {
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+       {0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */
+       {0xaa, 0xa4, 0x0090}, /* 00,a4,90,aa */
+       {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */
+       {0xa0, 0x90, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,90,cc */
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+       {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,03,cc */
+       {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+       {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */
+       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+       {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
+       {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
+       {0xa0, 0xbc, ZC3XX_R01D_HSYNC_0}, /* 00,1d,bc,cc */
+       {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d0,cc */
+       {0xa0, 0xe0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e0,cc */
+       {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+       {0xa0, 0x02, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,02,cc */
+       {}
+};
+
+static const struct usb_action tas5130c_vf0250_Initial[] = {
+       {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},         /* 00,00,01,cc, */
+       {0xa0, 0x02, ZC3XX_R008_CLOCKSETTING},          /* 00,08,02,cc, */
+       {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT},      /* 00,10,01,cc, */
+       {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},           /* 00,02,00,cc,
+                                                        * 0<->10 */
+       {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},        /* 00,03,02,cc, */
+       {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},         /* 00,04,80,cc, */
+       {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},       /* 00,05,01,cc, */
+       {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},        /* 00,06,e0,cc, */
+       {0xa0, 0x98, ZC3XX_R08B_I2CDEVICEADDR},         /* 00,8b,98,cc, */
+       {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},       /* 00,01,01,cc, */
+       {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},      /* 00,12,03,cc, */
+       {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},      /* 00,12,01,cc, */
+       {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},          /* 00,98,00,cc, */
+       {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},          /* 00,9a,00,cc, */
+       {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},             /* 01,1a,00,cc, */
+       {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},             /* 01,1c,00,cc, */
+       {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW},          /* 00,9c,e6,cc,
+                                                        * 6<->8 */
+       {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW},           /* 00,9e,86,cc,
+                                                        * 6<->8 */
+       {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID},            /* 00,87,10,cc, */
+       {0xa0, 0x98, ZC3XX_R08B_I2CDEVICEADDR},         /* 00,8b,98,cc, */
+       {0xaa, 0x1b, 0x0024},           /* 00,1b,24,aa, */
+       {0xdd, 0x00, 0x0080},           /* 00,00,80,dd, */
+       {0xaa, 0x1b, 0x0000},           /* 00,1b,00,aa, */
+       {0xaa, 0x13, 0x0002},           /* 00,13,02,aa, */
+       {0xaa, 0x15, 0x0004},           /* 00,15,04,aa */
+       {0xaa, 0x01, 0x0000},
+       {0xaa, 0x01, 0x0000},
+       {0xaa, 0x1a, 0x0000},           /* 00,1a,00,aa, */
+       {0xaa, 0x1c, 0x0017},           /* 00,1c,17,aa, */
+       {0xa0, 0x82, ZC3XX_R086_EXPTIMEHIGH},           /* 00,86,82,cc, */
+       {0xa0, 0x83, ZC3XX_R087_EXPTIMEMID},            /* 00,87,83,cc, */
+       {0xa0, 0x84, ZC3XX_R088_EXPTIMELOW},            /* 00,88,84,cc, */
+       {0xaa, 0x05, 0x0010},           /* 00,05,10,aa, */
+       {0xaa, 0x0a, 0x0000},           /* 00,0a,00,aa, */
+       {0xaa, 0x0b, 0x00a0},           /* 00,0b,a0,aa, */
+       {0xaa, 0x0c, 0x0000},           /* 00,0c,00,aa, */
+       {0xaa, 0x0d, 0x00a0},           /* 00,0d,a0,aa, */
+       {0xaa, 0x0e, 0x0000},           /* 00,0e,00,aa, */
+       {0xaa, 0x0f, 0x00a0},           /* 00,0f,a0,aa, */
+       {0xaa, 0x10, 0x0000},           /* 00,10,00,aa, */
+       {0xaa, 0x11, 0x00a0},           /* 00,11,a0,aa, */
+       {0xa0, 0x00, 0x0039},
+       {0xa1, 0x01, 0x0037},
+       {0xaa, 0x16, 0x0001},           /* 00,16,01,aa, */
+       {0xaa, 0x17, 0x00e8},           /* 00,17,e6,aa, (e6 -> e8) */
+       {0xaa, 0x18, 0x0002},           /* 00,18,02,aa, */
+       {0xaa, 0x19, 0x0088},           /* 00,19,86,aa, */
+       {0xaa, 0x20, 0x0020},           /* 00,20,20,aa, */
+       {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION},      /* 01,01,b7,cc, */
+       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},      /* 00,12,05,cc, */
+       {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},         /* 01,00,0d,cc, */
+       {0xa0, 0x76, ZC3XX_R189_AWBSTATUS},             /* 01,89,76,cc, */
+       {0xa0, 0x09, 0x01ad},                           /* 01,ad,09,cc, */
+       {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},         /* 01,c5,03,cc, */
+       {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},           /* 01,cb,13,cc, */
+       {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},        /* 02,50,08,cc, */
+       {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},          /* 03,01,08,cc, */
+       {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},           /* 01,a8,60,cc, */
+       {0xa0, 0x61, ZC3XX_R116_RGAIN},                 /* 01,16,61,cc, */
+       {0xa0, 0x65, ZC3XX_R118_BGAIN},                 /* 01,18,65,cc */
+       {}
+};
+
+static const struct usb_action tas5130c_vf0250_InitialScale[] = {
+       {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},         /* 00,00,01,cc, */
+       {0xa0, 0x02, ZC3XX_R008_CLOCKSETTING},          /* 00,08,02,cc, */
+       {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT},      /* 00,10,01,cc, */
+       {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},           /* 00,02,10,cc, */
+       {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},        /* 00,03,02,cc, */
+       {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},         /* 00,04,80,cc, */
+       {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},       /* 00,05,01,cc, */
+       {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},        /* 00,06,e0,cc, */
+       {0xa0, 0x98, ZC3XX_R08B_I2CDEVICEADDR},         /* 00,8b,98,cc, */
+       {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},       /* 00,01,01,cc, */
+       {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},      /* 00,12,03,cc, */
+       {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},      /* 00,12,01,cc, */
+       {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},          /* 00,98,00,cc, */
+       {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},          /* 00,9a,00,cc, */
+       {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},             /* 01,1a,00,cc, */
+       {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},             /* 01,1c,00,cc, */
+       {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW},          /* 00,9c,e8,cc,
+                                                        * 8<->6 */
+       {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW},           /* 00,9e,88,cc,
+                                                        * 8<->6 */
+       {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID},            /* 00,87,10,cc, */
+       {0xa0, 0x98, ZC3XX_R08B_I2CDEVICEADDR},         /* 00,8b,98,cc, */
+       {0xaa, 0x1b, 0x0024},           /* 00,1b,24,aa, */
+       {0xdd, 0x00, 0x0080},           /* 00,00,80,dd, */
+       {0xaa, 0x1b, 0x0000},           /* 00,1b,00,aa, */
+       {0xaa, 0x13, 0x0002},           /* 00,13,02,aa, */
+       {0xaa, 0x15, 0x0004},           /* 00,15,04,aa */
+       {0xaa, 0x01, 0x0000},
+       {0xaa, 0x01, 0x0000},
+       {0xaa, 0x1a, 0x0000},           /* 00,1a,00,aa, */
+       {0xaa, 0x1c, 0x0017},           /* 00,1c,17,aa, */
+       {0xa0, 0x82, ZC3XX_R086_EXPTIMEHIGH},   /* 00,86,82,cc, */
+       {0xa0, 0x83, ZC3XX_R087_EXPTIMEMID},    /* 00,87,83,cc, */
+       {0xa0, 0x84, ZC3XX_R088_EXPTIMELOW},    /* 00,88,84,cc, */
+       {0xaa, 0x05, 0x0010},           /* 00,05,10,aa, */
+       {0xaa, 0x0a, 0x0000},           /* 00,0a,00,aa, */
+       {0xaa, 0x0b, 0x00a0},           /* 00,0b,a0,aa, */
+       {0xaa, 0x0c, 0x0000},           /* 00,0c,00,aa, */
+       {0xaa, 0x0d, 0x00a0},           /* 00,0d,a0,aa, */
+       {0xaa, 0x0e, 0x0000},           /* 00,0e,00,aa, */
+       {0xaa, 0x0f, 0x00a0},           /* 00,0f,a0,aa, */
+       {0xaa, 0x10, 0x0000},           /* 00,10,00,aa, */
+       {0xaa, 0x11, 0x00a0},           /* 00,11,a0,aa, */
+       {0xa0, 0x00, 0x0039},
+       {0xa1, 0x01, 0x0037},
+       {0xaa, 0x16, 0x0001},           /* 00,16,01,aa, */
+       {0xaa, 0x17, 0x00e8},           /* 00,17,e6,aa (e6 -> e8) */
+       {0xaa, 0x18, 0x0002},           /* 00,18,02,aa, */
+       {0xaa, 0x19, 0x0088},           /* 00,19,88,aa, */
+       {0xaa, 0x20, 0x0020},           /* 00,20,20,aa, */
+       {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION},      /* 01,01,b7,cc, */
+       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},      /* 00,12,05,cc, */
+       {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},         /* 01,00,0d,cc, */
+       {0xa0, 0x76, ZC3XX_R189_AWBSTATUS},             /* 01,89,76,cc, */
+       {0xa0, 0x09, 0x01ad},                           /* 01,ad,09,cc, */
+       {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},         /* 01,c5,03,cc, */
+       {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},           /* 01,cb,13,cc, */
+       {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},        /* 02,50,08,cc, */
+       {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},          /* 03,01,08,cc, */
+       {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},           /* 01,a8,60,cc, */
+       {0xa0, 0x61, ZC3XX_R116_RGAIN},         /* 01,16,61,cc, */
+       {0xa0, 0x65, ZC3XX_R118_BGAIN},         /* 01,18,65,cc */
+       {}
+};
+/* "50HZ" light frequency banding filter */
+static const struct usb_action tas5130c_vf0250_50HZ[] = {
+       {0xaa, 0x82, 0x0000},           /* 00,82,00,aa */
+       {0xaa, 0x83, 0x0001},           /* 00,83,01,aa */
+       {0xaa, 0x84, 0x00aa},           /* 00,84,aa,aa */
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},     /* 01,90,00,cc, */
+       {0xa0, 0x06, ZC3XX_R191_EXPOSURELIMITMID},      /* 01,91,0d,cc, */
+       {0xa0, 0xa8, ZC3XX_R192_EXPOSURELIMITLOW},      /* 01,92,50,cc, */
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},       /* 01,95,00,cc, */
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},        /* 01,96,00,cc, */
+       {0xa0, 0x8e, ZC3XX_R197_ANTIFLICKERLOW},        /* 01,97,47,cc, */
+       {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},              /* 01,8c,0e,cc, */
+       {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE},            /* 01,8f,15,cc, */
+       {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},      /* 01,a9,10,cc, */
+       {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},       /* 01,aa,24,cc, */
+       {0xa0, 0x62, ZC3XX_R01D_HSYNC_0},               /* 00,1d,62,cc, */
+       {0xa0, 0x90, ZC3XX_R01E_HSYNC_1},               /* 00,1e,90,cc, */
+       {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2},               /* 00,1f,c8,cc, */
+       {0xa0, 0xff, ZC3XX_R020_HSYNC_3},               /* 00,20,ff,cc, */
+       {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN},            /* 01,1d,58,cc, */
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},     /* 01,80,42,cc, */
+       {0xa0, 0x78, ZC3XX_R18D_YTARGET},               /* 01,8d,78,cc */
+       {}
+};
+
+/* "50HZScale" light frequency banding filter */
+static const struct usb_action tas5130c_vf0250_50HZScale[] = {
+       {0xaa, 0x82, 0x0000},           /* 00,82,00,aa */
+       {0xaa, 0x83, 0x0003},           /* 00,83,03,aa */
+       {0xaa, 0x84, 0x0054},           /* 00,84,54,aa */
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},     /* 01,90,00,cc, */
+       {0xa0, 0x0d, ZC3XX_R191_EXPOSURELIMITMID},      /* 01,91,0d,cc, */
+       {0xa0, 0x50, ZC3XX_R192_EXPOSURELIMITLOW},      /* 01,92,50,cc, */
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},       /* 01,95,00,cc, */
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},        /* 01,96,00,cc, */
+       {0xa0, 0x8e, ZC3XX_R197_ANTIFLICKERLOW},        /* 01,97,8e,cc, */
+       {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},              /* 01,8c,0e,cc, */
+       {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE},            /* 01,8f,15,cc, */
+       {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},      /* 01,a9,10,cc, */
+       {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},       /* 01,aa,24,cc, */
+       {0xa0, 0x62, ZC3XX_R01D_HSYNC_0},               /* 00,1d,62,cc, */
+       {0xa0, 0x90, ZC3XX_R01E_HSYNC_1},               /* 00,1e,90,cc, */
+       {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2},               /* 00,1f,c8,cc, */
+       {0xa0, 0xff, ZC3XX_R020_HSYNC_3},               /* 00,20,ff,cc, */
+       {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN},            /* 01,1d,58,cc, */
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},     /* 01,80,42,cc, */
+       {0xa0, 0x78, ZC3XX_R18D_YTARGET},               /* 01,8d,78,cc */
+       {}
+};
+
+/* "60HZ" light frequency banding filter */
+static const struct usb_action tas5130c_vf0250_60HZ[] = {
+       {0xaa, 0x82, 0x0000},           /* 00,82,00,aa */
+       {0xaa, 0x83, 0x0001},           /* 00,83,01,aa */
+       {0xaa, 0x84, 0x0062},           /* 00,84,62,aa */
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},     /* 01,90,00,cc, */
+       {0xa0, 0x05, ZC3XX_R191_EXPOSURELIMITMID},      /* 01,91,05,cc, */
+       {0xa0, 0x88, ZC3XX_R192_EXPOSURELIMITLOW},      /* 01,92,88,cc, */
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},       /* 01,95,00,cc, */
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},        /* 01,96,00,cc, */
+       {0xa0, 0x3b, ZC3XX_R197_ANTIFLICKERLOW},        /* 01,97,3b,cc, */
+       {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},              /* 01,8c,0e,cc, */
+       {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE},            /* 01,8f,15,cc, */
+       {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},      /* 01,a9,10,cc, */
+       {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},       /* 01,aa,24,cc, */
+       {0xa0, 0x62, ZC3XX_R01D_HSYNC_0},               /* 00,1d,62,cc, */
+       {0xa0, 0x90, ZC3XX_R01E_HSYNC_1},               /* 00,1e,90,cc, */
+       {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2},               /* 00,1f,c8,cc, */
+       {0xa0, 0xff, ZC3XX_R020_HSYNC_3},               /* 00,20,ff,cc, */
+       {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN},            /* 01,1d,58,cc, */
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},     /* 01,80,42,cc, */
+       {0xa0, 0x78, ZC3XX_R18D_YTARGET},               /* 01,8d,78,cc */
+       {}
+};
+
+/* "60HZScale" light frequency banding ilter */
+static const struct usb_action tas5130c_vf0250_60HZScale[] = {
+       {0xaa, 0x82, 0x0000},           /* 00,82,00,aa */
+       {0xaa, 0x83, 0x0002},           /* 00,83,02,aa */
+       {0xaa, 0x84, 0x00c4},           /* 00,84,c4,aa */
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},     /* 01,90,00,cc, */
+       {0xa0, 0x0b, ZC3XX_R191_EXPOSURELIMITMID},      /* 01,1,0b,cc, */
+       {0xa0, 0x10, ZC3XX_R192_EXPOSURELIMITLOW},      /* 01,2,10,cc, */
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},       /* 01,5,00,cc, */
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},        /* 01,6,00,cc, */
+       {0xa0, 0x76, ZC3XX_R197_ANTIFLICKERLOW},        /* 01,7,76,cc, */
+       {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},              /* 01,c,0e,cc, */
+       {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE},            /* 01,f,15,cc, */
+       {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},      /* 01,9,10,cc, */
+       {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},       /* 01,a,24,cc, */
+       {0xa0, 0x62, ZC3XX_R01D_HSYNC_0},               /* 00,d,62,cc, */
+       {0xa0, 0x90, ZC3XX_R01E_HSYNC_1},               /* 00,e,90,cc, */
+       {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2},               /* 00,f,c8,cc, */
+       {0xa0, 0xff, ZC3XX_R020_HSYNC_3},               /* 00,0,ff,cc, */
+       {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN},            /* 01,d,58,cc, */
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},     /* 01,80,42,cc, */
+       {0xa0, 0x78, ZC3XX_R18D_YTARGET},               /* 01,d,78,cc */
+       {}
+};
+
+/* "NoFliker" light frequency banding flter */
+static const struct usb_action tas5130c_vf0250_NoFliker[] = {
+       {0xa0, 0x0c, ZC3XX_R100_OPERATIONMODE},         /* 01,00,0c,cc, */
+       {0xaa, 0x82, 0x0000},           /* 00,82,00,aa */
+       {0xaa, 0x83, 0x0000},           /* 00,83,00,aa */
+       {0xaa, 0x84, 0x0020},           /* 00,84,20,aa */
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},     /* 01,0,00,cc, */
+       {0xa0, 0x05, ZC3XX_R191_EXPOSURELIMITMID},      /* 01,91,05,cc, */
+       {0xa0, 0x88, ZC3XX_R192_EXPOSURELIMITLOW},      /* 01,92,88,cc, */
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},       /* 01,95,00,cc, */
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},        /* 01,96,00,cc, */
+       {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW},        /* 01,97,10,cc, */
+       {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},              /* 01,8c,0e,cc, */
+       {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE},            /* 01,8f,15,cc, */
+       {0xa0, 0x62, ZC3XX_R01D_HSYNC_0},               /* 00,1d,62,cc, */
+       {0xa0, 0x90, ZC3XX_R01E_HSYNC_1},               /* 00,1e,90,cc, */
+       {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2},               /* 00,1f,c8,cc, */
+       {0xa0, 0xff, ZC3XX_R020_HSYNC_3},               /* 00,20,ff,cc, */
+       {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN},            /* 01,1d,58,cc, */
+       {0xa0, 0x03, ZC3XX_R180_AUTOCORRECTENABLE},     /* 01,80,03,cc */
+       {}
+};
+
+/* "NoFlikerScale" light frequency banding filter */
+static const struct usb_action tas5130c_vf0250_NoFlikerScale[] = {
+       {0xa0, 0x0c, ZC3XX_R100_OPERATIONMODE},         /* 01,00,0c,cc, */
+       {0xaa, 0x82, 0x0000},           /* 00,82,00,aa */
+       {0xaa, 0x83, 0x0000},           /* 00,83,00,aa */
+       {0xaa, 0x84, 0x0020},           /* 00,84,20,aa */
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},     /* 01,90,00,cc, */
+       {0xa0, 0x0b, ZC3XX_R191_EXPOSURELIMITMID},      /* 01,91,0b,cc, */
+       {0xa0, 0x10, ZC3XX_R192_EXPOSURELIMITLOW},      /* 01,92,10,cc, */
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},       /* 01,95,00,cc, */
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},        /* 01,96,00,cc, */
+       {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW},        /* 01,97,10,cc, */
+       {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},              /* 01,8c,0e,cc, */
+       {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE},            /* 01,8f,15,cc, */
+       {0xa0, 0x62, ZC3XX_R01D_HSYNC_0},               /* 00,1d,62,cc, */
+       {0xa0, 0x90, ZC3XX_R01E_HSYNC_1},               /* 00,1e,90,cc, */
+       {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2},               /* 00,1f,c8,cc, */
+       {0xa0, 0xff, ZC3XX_R020_HSYNC_3},               /* 00,20,ff,cc, */
+       {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN},            /* 01,1d,58,cc, */
+       {0xa0, 0x03, ZC3XX_R180_AUTOCORRECTENABLE},     /* 01,80,03,cc */
+       {}
+};
+
+static int reg_r_i(struct gspca_dev *gspca_dev,
+               __u16 index)
+{
+       usb_control_msg(gspca_dev->dev,
+                       usb_rcvctrlpipe(gspca_dev->dev, 0),
+                       0xa1,
+                       USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                       0x01,                   /* value */
+                       index, gspca_dev->usb_buf, 1,
+                       500);
+       return gspca_dev->usb_buf[0];
+}
+
+static int reg_r(struct gspca_dev *gspca_dev,
+               __u16 index)
+{
+       int ret;
+
+       ret = reg_r_i(gspca_dev, index);
+       PDEBUG(D_USBI, "reg r [%04x] -> %02x", index, ret);
+       return ret;
+}
+
+static void reg_w_i(struct usb_device *dev,
+                       __u8 value,
+                       __u16 index)
+{
+       usb_control_msg(dev,
+                       usb_sndctrlpipe(dev, 0),
+                       0xa0,
+                       USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                       value, index, NULL, 0,
+                       500);
+}
+
+static void reg_w(struct usb_device *dev,
+                       __u8 value,
+                       __u16 index)
+{
+       PDEBUG(D_USBO, "reg w %02x -> [%04x]", value, index);
+       reg_w_i(dev, value, index);
+}
+
+static __u16 i2c_read(struct gspca_dev *gspca_dev,
+                       __u8 reg)
+{
+       __u8 retbyte;
+       __u8 retval[2];
+
+       reg_w_i(gspca_dev->dev, reg, 0x92);
+       reg_w_i(gspca_dev->dev, 0x02, 0x90);            /* <- read command */
+       msleep(25);
+       retbyte = reg_r_i(gspca_dev, 0x0091);           /* read status */
+       retval[0] = reg_r_i(gspca_dev, 0x0095);         /* read Lowbyte */
+       retval[1] = reg_r_i(gspca_dev, 0x0096);         /* read Hightbyte */
+       PDEBUG(D_USBO, "i2c r [%02x] -> (%02x) %02x%02x",
+                       reg, retbyte, retval[1], retval[0]);
+       return (retval[1] << 8) | retval[0];
+}
+
+static __u8 i2c_write(struct gspca_dev *gspca_dev,
+                       __u8 reg,
+                       __u8 valL,
+                       __u8 valH)
+{
+       __u8 retbyte;
+
+       reg_w_i(gspca_dev->dev, reg, 0x92);
+       reg_w_i(gspca_dev->dev, valL, 0x93);
+       reg_w_i(gspca_dev->dev, valH, 0x94);
+       reg_w_i(gspca_dev->dev, 0x01, 0x90);            /* <- write command */
+       msleep(5);
+       retbyte = reg_r_i(gspca_dev, 0x0091);           /* read status */
+       PDEBUG(D_USBO, "i2c w [%02x] %02x%02x (%02x)",
+                       reg, valH, valL, retbyte);
+       return retbyte;
+}
+
+static void usb_exchange(struct gspca_dev *gspca_dev,
+                       const struct usb_action *action)
+{
+       while (action->req) {
+               switch (action->req) {
+               case 0xa0:      /* write register */
+                       reg_w(gspca_dev->dev, action->val, action->idx);
+                       break;
+               case 0xa1:      /* read status */
+                       reg_r(gspca_dev, action->idx);
+                       break;
+               case 0xaa:
+                       i2c_write(gspca_dev,
+                                 action->val,                  /* reg */
+                                 action->idx & 0xff,           /* valL */
+                                 action->idx >> 8);            /* valH */
+                       break;
+               default:
+/*             case 0xdd:       * delay */
+                       msleep(action->val / 64 + 10);
+                       break;
+               }
+               action++;
+/*             msleep(1); */
+       }
+}
+
+static void setmatrix(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       int i;
+       const __u8 *matrix;
+       static const __u8 gc0305_matrix[9] =
+               {0x50, 0xf8, 0xf8, 0xf8, 0x50, 0xf8, 0xf8, 0xf8, 0x50};
+       static const __u8 ov7620_matrix[9] =
+               {0x58, 0xf4, 0xf4, 0xf4, 0x58, 0xf4, 0xf4, 0xf4, 0x58};
+       static const __u8 po2030_matrix[9] =
+               {0x60, 0xf0, 0xf0, 0xf0, 0x60, 0xf0, 0xf0, 0xf0, 0x60};
+
+       switch (sd->sensor) {
+       case SENSOR_GC0305:
+               matrix = gc0305_matrix;
+               break;
+       case SENSOR_MC501CB:
+               return;         /* no matrix? */
+       case SENSOR_OV7620:
+/*     case SENSOR_OV7648: */
+               matrix = ov7620_matrix;
+               break;
+       case SENSOR_PO2030:
+               matrix = po2030_matrix;
+               break;
+       case SENSOR_TAS5130C_VF0250:    /* no matrix? */
+               return;
+       default:                /* matrix already loaded */
+               return;
+       }
+       for (i = 0; i < ARRAY_SIZE(ov7620_matrix); i++)
+               reg_w(gspca_dev->dev, matrix[i], 0x010a + i);
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       __u8 brightness;
+
+       switch (sd->sensor) {
+       case SENSOR_GC0305:
+       case SENSOR_OV7620:
+       case SENSOR_PO2030:
+               return;
+       }
+/*fixme: is it really write to 011d and 018d for all other sensors? */
+       brightness = sd->brightness;
+       reg_w(gspca_dev->dev, brightness, 0x011d);
+       if (brightness < 0x70)
+               brightness += 0x10;
+       else
+               brightness = 0x80;
+       reg_w(gspca_dev->dev, brightness, 0x018d);
+}
+
+static void setsharpness(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       struct usb_device *dev = gspca_dev->dev;
+       int sharpness;
+       static const __u8 sharpness_tb[][2] = {
+               {0x02, 0x03},
+               {0x04, 0x07},
+               {0x08, 0x0f},
+               {0x10, 0x1e}
+       };
+
+       sharpness = sd->sharpness;
+       reg_w(dev, sharpness_tb[sharpness][0], 0x01c6);
+       reg_r(gspca_dev, 0x01c8);
+       reg_r(gspca_dev, 0x01c9);
+       reg_r(gspca_dev, 0x01ca);
+       reg_w(dev, sharpness_tb[sharpness][1], 0x01cb);
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       struct usb_device *dev = gspca_dev->dev;
+       const __u8 *Tgamma, *Tgradient;
+       int g, i, k;
+       static const __u8 kgamma_tb[16] =       /* delta for contrast */
+               {0x15, 0x0d, 0x0a, 0x09, 0x08, 0x08, 0x08, 0x08,
+                0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08};
+       static const __u8 kgrad_tb[16] =
+               {0x1b, 0x06, 0x03, 0x02, 0x00, 0x00, 0x00, 0x00,
+                0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x04};
+       static const __u8 Tgamma_1[16] =
+               {0x00, 0x00, 0x03, 0x0d, 0x1b, 0x2e, 0x45, 0x5f,
+                0x79, 0x93, 0xab, 0xc1, 0xd4, 0xe5, 0xf3, 0xff};
+       static const __u8 Tgradient_1[16] =
+               {0x00, 0x01, 0x05, 0x0b, 0x10, 0x15, 0x18, 0x1a,
+                0x1a, 0x18, 0x16, 0x14, 0x12, 0x0f, 0x0d, 0x06};
+       static const __u8 Tgamma_2[16] =
+               {0x01, 0x0c, 0x1f, 0x3a, 0x53, 0x6d, 0x85, 0x9c,
+                0xb0, 0xc2, 0xd1, 0xde, 0xe9, 0xf2, 0xf9, 0xff};
+       static const __u8 Tgradient_2[16] =
+               {0x05, 0x0f, 0x16, 0x1a, 0x19, 0x19, 0x17, 0x15,
+                0x12, 0x10, 0x0e, 0x0b, 0x09, 0x08, 0x06, 0x03};
+       static const __u8 Tgamma_3[16] =
+               {0x04, 0x16, 0x30, 0x4e, 0x68, 0x81, 0x98, 0xac,
+                0xbe, 0xcd, 0xda, 0xe4, 0xed, 0xf5, 0xfb, 0xff};
+       static const __u8 Tgradient_3[16] =
+               {0x0c, 0x16, 0x1b, 0x1c, 0x19, 0x18, 0x15, 0x12,
+                0x10, 0x0d, 0x0b, 0x09, 0x08, 0x06, 0x05, 0x03};
+       static const __u8 Tgamma_4[16] =
+               {0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8,
+                0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff};
+       static const __u8 Tgradient_4[16] =
+               {0x26, 0x22, 0x20, 0x1c, 0x16, 0x13, 0x10, 0x0d,
+                0x0b, 0x09, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02};
+       static const __u8 Tgamma_5[16] =
+               {0x20, 0x4b, 0x6e, 0x8d, 0xa3, 0xb5, 0xc5, 0xd2,
+                0xdc, 0xe5, 0xec, 0xf2, 0xf6, 0xfa, 0xfd, 0xff};
+       static const __u8 Tgradient_5[16] =
+               {0x37, 0x26, 0x20, 0x1a, 0x14, 0x10, 0x0e, 0x0b,
+                0x09, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x02};
+       static const __u8 Tgamma_6[16] =                /* ?? was gamma 5 */
+               {0x24, 0x44, 0x64, 0x84, 0x9d, 0xb2, 0xc4, 0xd3,
+                0xe0, 0xeb, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff};
+       static const __u8 Tgradient_6[16] =
+               {0x18, 0x20, 0x20, 0x1c, 0x16, 0x13, 0x10, 0x0e,
+                0x0b, 0x09, 0x07, 0x00, 0x00, 0x00, 0x00, 0x01};
+       static const __u8 *gamma_tb[] = {
+               NULL, Tgamma_1, Tgamma_2,
+               Tgamma_3, Tgamma_4, Tgamma_5, Tgamma_6
+       };
+       static const __u8 *gradient_tb[] = {
+               NULL, Tgradient_1, Tgradient_2,
+               Tgradient_3, Tgradient_4, Tgradient_5, Tgradient_6
+       };
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+       __u8 v[16];
+#endif
+
+       Tgamma = gamma_tb[sd->gamma];
+       Tgradient = gradient_tb[sd->gamma];
+
+       k = (sd->contrast - 128)                /* -128 / 128 */
+                       * Tgamma[0];
+       PDEBUG(D_CONF, "gamma:%d contrast:%d gamma coeff: %d/128",
+               sd->gamma, sd->contrast, k);
+       for (i = 0; i < 16; i++) {
+               g = Tgamma[i] + kgamma_tb[i] * k / 128;
+               if (g > 0xff)
+                       g = 0xff;
+               else if (g <= 0)
+                       g = 1;
+               reg_w(dev, g, 0x0120 + i);      /* gamma */
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+               if (gspca_debug & D_CONF)
+                       v[i] = g;
+#endif
+       }
+       PDEBUG(D_CONF, "tb: %02x %02x %02x %02x %02x %02x %02x %02x",
+               v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]);
+       PDEBUG(D_CONF, "    %02x %02x %02x %02x %02x %02x %02x %02x",
+               v[8], v[9], v[10], v[11], v[12], v[13], v[14], v[15]);
+       for (i = 0; i < 16; i++) {
+               g = Tgradient[i] - kgrad_tb[i] * k / 128;
+               if (g > 0xff)
+                       g = 0xff;
+               else if (g <= 0) {
+                       if (i != 15)
+                               g = 0;
+                       else
+                               g = 1;
+               }
+               reg_w(dev, g, 0x0130 + i);      /* gradient */
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+               if (gspca_debug & D_CONF)
+                       v[i] = g;
+#endif
+       }
+       PDEBUG(D_CONF, "    %02x %02x %02x %02x %02x %02x %02x %02x",
+               v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]);
+       PDEBUG(D_CONF, "    %02x %02x %02x %02x %02x %02x %02x %02x",
+               v[8], v[9], v[10], v[11], v[12], v[13], v[14], v[15]);
+}
+
+static void setquality(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       struct usb_device *dev = gspca_dev->dev;
+       __u8 quality;
+       __u8 frxt;
+
+       switch (sd->sensor) {
+       case SENSOR_GC0305:
+       case SENSOR_OV7620:
+       case SENSOR_PO2030:
+               return;
+       }
+/*fixme: is it really 0008 0007 0018 for all other sensors? */
+       quality = sd->qindex;
+       reg_w(dev, quality, 0x0008);
+       frxt = 0x30;
+       reg_w(dev, frxt, 0x0007);
+       switch (quality) {
+       case 0:
+       case 1:
+       case 2:
+               frxt = 0xff;
+               break;
+       case 3:
+               frxt = 0xf0;
+               break;
+       case 4:
+               frxt = 0xe0;
+               break;
+       case 5:
+               frxt = 0x20;
+               break;
+       }
+       reg_w(dev, frxt, 0x0018);
+}
+
+/* Matches the sensor's internal frame rate to the lighting frequency.
+ * Valid frequencies are:
+ *     50Hz, for European and Asian lighting (default)
+ *     60Hz, for American lighting
+ *     0 = No Fliker (for outdoore usage)
+ * Returns: 0 for success
+ */
+static int setlightfreq(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       int i, mode;
+       const struct usb_action *zc3_freq;
+       static const struct usb_action *freq_tb[SENSOR_MAX][6] = {
+/* SENSOR_CS2102 0 */
+               {cs2102_NoFliker, cs2102_NoFlikerScale,
+                cs2102_50HZ, cs2102_50HZScale,
+                cs2102_60HZ, cs2102_60HZScale},
+/* SENSOR_CS2102K 1 */
+               {cs2102_NoFliker, cs2102_NoFlikerScale,
+                cs2102_50HZ, cs2102_50HZScale,
+                cs2102_60HZ, cs2102_60HZScale},
+/* SENSOR_GC0305 2 */
+               {gc0305_NoFliker, gc0305_NoFliker,
+                gc0305_50HZ, gc0305_50HZ,
+                gc0305_60HZ, gc0305_60HZ},
+/* SENSOR_HDCS2020 3 */
+               {NULL, NULL,
+                NULL, NULL,
+                NULL, NULL},
+/* SENSOR_HDCS2020b 4 */
+               {hdcs2020b_NoFliker, hdcs2020b_NoFliker,
+                hdcs2020b_50HZ, hdcs2020b_50HZ,
+                hdcs2020b_60HZ, hdcs2020b_60HZ},
+/* SENSOR_HV7131B 5 */
+               {NULL, NULL,
+                NULL, NULL,
+                NULL, NULL},
+/* SENSOR_HV7131C 6 */
+               {NULL, NULL,
+                NULL, NULL,
+                NULL, NULL},
+/* SENSOR_ICM105A 7 */
+               {icm105a_NoFliker, icm105a_NoFlikerScale,
+                icm105a_50HZ, icm105a_50HZScale,
+                icm105a_60HZ, icm105a_60HZScale},
+/* SENSOR_MC501CB 8 */
+               {MC501CB_NoFliker, MC501CB_NoFlikerScale,
+                MC501CB_50HZ, MC501CB_50HZScale,
+                MC501CB_60HZ, MC501CB_60HZScale},
+/* SENSOR_OV7620 9 */
+               {OV7620_NoFliker, OV7620_NoFliker,
+                OV7620_50HZ, OV7620_50HZ,
+                OV7620_60HZ, OV7620_60HZ},
+/* SENSOR_OV7630C 10 */
+               {NULL, NULL,
+                NULL, NULL,
+                NULL, NULL},
+/* SENSOR_PAS106 11 */
+               {pas106b_NoFliker, pas106b_NoFliker,
+                pas106b_50HZ, pas106b_50HZ,
+                pas106b_60HZ, pas106b_60HZ},
+/* SENSOR_PB0330 12 */
+               {pb0330_NoFliker, pb0330_NoFlikerScale,
+                pb0330_50HZ, pb0330_50HZScale,
+                pb0330_60HZ, pb0330_60HZScale},
+/* SENSOR_PO2030 13 */
+               {PO2030_NoFliker, PO2030_NoFliker,
+                PO2030_50HZ, PO2030_50HZ,
+                PO2030_60HZ, PO2030_60HZ},
+/* SENSOR_TAS5130CK 14 */
+               {tas5130cxx_NoFliker, tas5130cxx_NoFlikerScale,
+                tas5130cxx_50HZ, tas5130cxx_50HZScale,
+                tas5130cxx_60HZ, tas5130cxx_60HZScale},
+/* SENSOR_TAS5130CXX 15 */
+               {tas5130cxx_NoFliker, tas5130cxx_NoFlikerScale,
+                tas5130cxx_50HZ, tas5130cxx_50HZScale,
+                tas5130cxx_60HZ, tas5130cxx_60HZScale},
+/* SENSOR_TAS5130C_VF0250 16 */
+               {tas5130c_vf0250_NoFliker, tas5130c_vf0250_NoFlikerScale,
+                tas5130c_vf0250_50HZ, tas5130c_vf0250_50HZScale,
+                tas5130c_vf0250_60HZ, tas5130c_vf0250_60HZScale},
+       };
+
+       i = sd->lightfreq * 2;
+       mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;
+       if (!mode)
+               i++;                    /* 640x480 */
+       zc3_freq = freq_tb[(int) sd->sensor][i];
+       if (zc3_freq != NULL) {
+               usb_exchange(gspca_dev, zc3_freq);
+               switch (sd->sensor) {
+               case SENSOR_GC0305:
+                       if (mode                        /* if 320x240 */
+                           && sd->lightfreq == 1)      /* and 50Hz */
+                               reg_w(gspca_dev->dev, 0x85, 0x018d);
+                                               /* win: 0x80, 0x018d */
+                       break;
+               case SENSOR_OV7620:
+                       if (!mode) {                    /* if 640x480 */
+                               if (sd->lightfreq != 0) /* and 50 or 60 Hz */
+                                       reg_w(gspca_dev->dev, 0x40, 0x0002);
+                               else
+                                       reg_w(gspca_dev->dev, 0x44, 0x0002);
+                       }
+                       break;
+               }
+       }
+       return 0;
+}
+
+static void setautogain(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       __u8 autoval;
+
+       if (sd->autogain)
+               autoval = 0x42;
+       else
+               autoval = 0x02;
+       reg_w(gspca_dev->dev, autoval, 0x0180);
+}
+
+static void send_unknown(struct usb_device *dev, int sensor)
+{
+       reg_w(dev, 0x01, 0x0000);               /* led off */
+       switch (sensor) {
+       case SENSOR_PAS106:
+               reg_w(dev, 0x03, 0x003a);
+               reg_w(dev, 0x0c, 0x003b);
+               reg_w(dev, 0x08, 0x0038);
+               break;
+       case SENSOR_GC0305:
+       case SENSOR_OV7620:
+       case SENSOR_PB0330:
+       case SENSOR_PO2030:
+               reg_w(dev, 0x0d, 0x003a);
+               reg_w(dev, 0x02, 0x003b);
+               reg_w(dev, 0x00, 0x0038);
+               break;
+       }
+}
+
+/* start probe 2 wires */
+static void start_2wr_probe(struct usb_device *dev, int sensor)
+{
+       reg_w(dev, 0x01, 0x0000);
+       reg_w(dev, sensor, 0x0010);
+       reg_w(dev, 0x01, 0x0001);
+       reg_w(dev, 0x03, 0x0012);
+       reg_w(dev, 0x01, 0x0012);
+/*     msleep(2); */
+}
+
+static int sif_probe(struct gspca_dev *gspca_dev)
+{
+       __u16 checkword;
+
+       start_2wr_probe(gspca_dev->dev, 0x0f);          /* PAS106 */
+       reg_w(gspca_dev->dev, 0x08, 0x008d);
+       msleep(150);
+       checkword = ((i2c_read(gspca_dev, 0x00) & 0x0f) << 4)
+                       | ((i2c_read(gspca_dev, 0x01) & 0xf0) >> 4);
+       PDEBUG(D_PROBE, "probe sif 0x%04x", checkword);
+       if (checkword == 0x0007) {
+               send_unknown(gspca_dev->dev, SENSOR_PAS106);
+               return 0x0f;                    /* PAS106 */
+       }
+       return -1;
+}
+
+static int vga_2wr_probe(struct gspca_dev *gspca_dev)
+{
+       struct usb_device *dev = gspca_dev->dev;
+       __u8 retbyte;
+       __u16 checkword;
+
+       start_2wr_probe(dev, 0x00);             /* HV7131B */
+       i2c_write(gspca_dev, 0x01, 0xaa, 0x00);
+       retbyte = i2c_read(gspca_dev, 0x01);
+       if (retbyte != 0)
+               return 0x00;                    /* HV7131B */
+
+       start_2wr_probe(dev, 0x04);             /* CS2102 */
+       i2c_write(gspca_dev, 0x01, 0xaa, 0x00);
+       retbyte = i2c_read(gspca_dev, 0x01);
+       if (retbyte != 0)
+               return 0x04;                    /* CS2102 */
+
+       start_2wr_probe(dev, 0x06);             /* OmniVision */
+       reg_w(dev, 0x08, 0x8d);
+       i2c_write(gspca_dev, 0x11, 0xaa, 0x00);
+       retbyte = i2c_read(gspca_dev, 0x11);
+       if (retbyte != 0) {
+               /* (should have returned 0xaa) --> Omnivision? */
+               /* reg_r 0x10 -> 0x06 -->  */
+               goto ov_check;
+       }
+
+       start_2wr_probe(dev, 0x08);             /* HDCS2020 */
+       i2c_write(gspca_dev, 0x15, 0xaa, 0x00);
+       retbyte = i2c_read(gspca_dev, 0x15);
+       if (retbyte != 0)
+               return 0x08;                    /* HDCS2020 */
+
+       start_2wr_probe(dev, 0x0a);             /* PB0330 */
+       i2c_write(gspca_dev, 0x07, 0xaa, 0xaa);
+       retbyte = i2c_read(gspca_dev, 0x07);
+       if (retbyte != 0)
+               return 0x0a;                    /* PB0330 */
+       retbyte = i2c_read(gspca_dev, 0x03);
+       if (retbyte != 0)
+               return 0x0a;                    /* PB0330 ?? */
+       retbyte = i2c_read(gspca_dev, 0x04);
+       if (retbyte != 0)
+               return 0x0a;                    /* PB0330 ?? */
+
+       start_2wr_probe(dev, 0x0c);             /* ICM105A */
+       i2c_write(gspca_dev, 0x01, 0x11, 0x00);
+       retbyte = i2c_read(gspca_dev, 0x01);
+       if (retbyte != 0)
+               return 0x0c;                    /* ICM105A */
+
+       start_2wr_probe(dev, 0x0e);             /* PAS202BCB */
+       reg_w(dev, 0x08, 0x8d);
+       i2c_write(gspca_dev, 0x03, 0xaa, 0x00);
+       msleep(500);
+       retbyte = i2c_read(gspca_dev, 0x03);
+       if (retbyte != 0)
+               return 0x0e;                    /* PAS202BCB */
+
+       start_2wr_probe(dev, 0x02);             /* ?? */
+       i2c_write(gspca_dev, 0x01, 0xaa, 0x00);
+       retbyte = i2c_read(gspca_dev, 0x01);
+       if (retbyte != 0)
+               return 0x02;                    /* ?? */
+ov_check:
+       reg_r(gspca_dev, 0x0010);               /* ?? */
+       reg_r(gspca_dev, 0x0010);
+
+       reg_w(dev, 0x01, 0x0000);
+       reg_w(dev, 0x01, 0x0001);
+       reg_w(dev, 0x06, 0x0010);               /* OmniVision */
+       reg_w(dev, 0xa1, 0x008b);
+       reg_w(dev, 0x08, 0x008d);
+       msleep(500);
+       reg_w(dev, 0x01, 0x0012);
+       i2c_write(gspca_dev, 0x12, 0x80, 0x00); /* sensor reset */
+       retbyte = i2c_read(gspca_dev, 0x0a);
+       checkword = retbyte << 8;
+       retbyte = i2c_read(gspca_dev, 0x0b);
+       checkword |= retbyte;
+       PDEBUG(D_PROBE, "probe 2wr ov vga 0x%04x", checkword);
+       switch (checkword) {
+       case 0x7631:                            /* OV7630C */
+               reg_w(dev, 0x06, 0x0010);
+               break;
+       case 0x7620:                            /* OV7620 */
+       case 0x7648:                            /* OV7648 */
+               break;
+       default:
+               return -1;                      /* not OmniVision */
+       }
+       return checkword;
+}
+
+struct sensor_by_chipset_revision {
+       __u16 revision;
+       __u8 internal_sensor_id;
+};
+static const struct sensor_by_chipset_revision chipset_revision_sensor[] = {
+       {0xc001, 0x13},         /* MI0360 */
+       {0xe001, 0x13},
+       {0x8001, 0x13},
+       {0x8000, 0x14},         /* CS2102K */
+       {0x8400, 0x15},         /* TAS5130K */
+       {0, 0}
+};
+
+static int vga_3wr_probe(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       struct usb_device *dev = gspca_dev->dev;
+       int i;
+       __u8 retbyte;
+       __u16 checkword;
+
+/*fixme: lack of 8b=b3 (11,12)-> 10, 8b=e0 (14,15,16)-> 12 found in gspcav1*/
+       reg_w(dev, 0x02, 0x0010);
+       reg_r(gspca_dev, 0x10);
+       reg_w(dev, 0x01, 0x0000);
+       reg_w(dev, 0x00, 0x0010);
+       reg_w(dev, 0x01, 0x0001);
+       reg_w(dev, 0x91, 0x008b);
+       reg_w(dev, 0x03, 0x0012);
+       reg_w(dev, 0x01, 0x0012);
+       reg_w(dev, 0x05, 0x0012);
+       retbyte = i2c_read(gspca_dev, 0x14);
+       if (retbyte != 0)
+               return 0x11;                    /* HV7131R */
+       retbyte = i2c_read(gspca_dev, 0x15);
+       if (retbyte != 0)
+               return 0x11;                    /* HV7131R */
+       retbyte = i2c_read(gspca_dev, 0x16);
+       if (retbyte != 0)
+               return 0x11;                    /* HV7131R */
+
+       reg_w(dev, 0x02, 0x0010);
+       retbyte = reg_r(gspca_dev, 0x000b);
+       checkword = retbyte << 8;
+       retbyte = reg_r(gspca_dev, 0x000a);
+       checkword |= retbyte;
+       PDEBUG(D_PROBE, "probe 3wr vga 1 0x%04x", checkword);
+       reg_r(gspca_dev, 0x0010);
+       /* this is tested only once anyway */
+       i = 0;
+       while (chipset_revision_sensor[i].revision) {
+               if (chipset_revision_sensor[i].revision == checkword) {
+                       sd->chip_revision = checkword;
+                       send_unknown(dev, SENSOR_PB0330);
+                       return chipset_revision_sensor[i].internal_sensor_id;
+               }
+               i++;
+       }
+
+       reg_w(dev, 0x01, 0x0000);
+       reg_w(dev, 0x01, 0x0001);
+       reg_w(dev, 0xdd, 0x008b);
+       reg_w(dev, 0x0a, 0x0010);
+       reg_w(dev, 0x03, 0x0012);
+       reg_w(dev, 0x01, 0x0012);
+       retbyte = i2c_read(gspca_dev, 0x00);
+       if (retbyte != 0) {
+               PDEBUG(D_PROBE, "probe 3wr vga type 0a ?");
+               return 0x0a;                    /* ?? */
+       }
+
+       reg_w(dev, 0x01, 0x0000);
+       reg_w(dev, 0x01, 0x0001);
+       reg_w(dev, 0x98, 0x008b);
+       reg_w(dev, 0x01, 0x0010);
+       reg_w(dev, 0x03, 0x0012);
+       msleep(2);
+       reg_w(dev, 0x01, 0x0012);
+       retbyte = i2c_read(gspca_dev, 0x00);
+       if (retbyte != 0) {
+               PDEBUG(D_PROBE, "probe 3wr vga type %02x", retbyte);
+               send_unknown(dev, SENSOR_GC0305);
+               return retbyte;         /* 0x29 = gc0305 - should continue? */
+       }
+
+       reg_w(dev, 0x01, 0x0000);       /* check OmniVision */
+       reg_w(dev, 0x01, 0x0001);
+       reg_w(dev, 0xa1, 0x008b);
+       reg_w(dev, 0x08, 0x008d);
+       reg_w(dev, 0x06, 0x0010);
+       reg_w(dev, 0x01, 0x0012);
+       reg_w(dev, 0x05, 0x0012);
+       if (i2c_read(gspca_dev, 0x1c) == 0x7f   /* OV7610 - manufacturer ID */
+           && i2c_read(gspca_dev, 0x1d) == 0xa2) {
+               send_unknown(dev, SENSOR_OV7620);
+               return 0x06;            /* OmniVision confirm ? */
+       }
+
+       reg_w(dev, 0x01, 0x00);
+       reg_w(dev, 0x00, 0x02);
+       reg_w(dev, 0x01, 0x10);
+       reg_w(dev, 0x01, 0x01);
+       reg_w(dev, 0xee, 0x8b);
+       reg_w(dev, 0x03, 0x12);
+/*     msleep(150); */
+       reg_w(dev, 0x01, 0x12);
+       reg_w(dev, 0x05, 0x12);
+       retbyte = i2c_read(gspca_dev, 0x00);            /* ID 0 */
+       checkword = retbyte << 8;
+       retbyte = i2c_read(gspca_dev, 0x01);            /* ID 1 */
+       checkword |= retbyte;
+       PDEBUG(D_PROBE, "probe 3wr vga 2 0x%04x", checkword);
+       if (checkword == 0x2030) {
+               retbyte = i2c_read(gspca_dev, 0x02);    /* revision number */
+               PDEBUG(D_PROBE, "sensor PO2030 rev 0x%02x", retbyte);
+               send_unknown(dev, SENSOR_PO2030);
+               return checkword;
+       }
+
+       reg_w(dev, 0x01, 0x00);
+       reg_w(dev, 0x0a, 0x10);
+       reg_w(dev, 0xd3, 0x8b);
+       reg_w(dev, 0x01, 0x01);
+       reg_w(dev, 0x03, 0x12);
+       reg_w(dev, 0x01, 0x12);
+       reg_w(dev, 0x05, 0x01);
+       reg_w(dev, 0xd3, 0x8b);
+       retbyte = i2c_read(gspca_dev, 0x01);
+       if (retbyte != 0) {
+               PDEBUG(D_PROBE, "probe 3wr vga type 0a ?");
+               return 0x0a;                    /* ?? */
+       }
+       return -1;
+}
+
+static int zcxx_probeSensor(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       int sensor, sensor2;
+
+       switch (sd->sensor) {
+       case SENSOR_MC501CB:
+       case SENSOR_TAS5130C_VF0250:
+               return -1;              /* don't probe */
+       }
+       sensor = vga_2wr_probe(gspca_dev);
+       if (sensor >= 0) {
+               if (sensor < 0x7600)
+                       return sensor;
+               /* next probe is needed for OmniVision ? */
+       }
+       sensor2 = vga_3wr_probe(gspca_dev);
+       if (sensor2 >= 0) {
+               if (sensor >= 0)
+                       return sensor;
+               return sensor2;
+       }
+       return sif_probe(gspca_dev);
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+                       const struct usb_device_id *id)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       struct cam *cam;
+       int sensor;
+       int vga = 1;            /* 1: vga, 0: sif */
+       static const __u8 gamma[SENSOR_MAX] = {
+               5,      /* SENSOR_CS2102 0 */
+               5,      /* SENSOR_CS2102K 1 */
+               4,      /* SENSOR_GC0305 2 */
+               4,      /* SENSOR_HDCS2020 3 */
+               4,      /* SENSOR_HDCS2020b 4 */
+               4,      /* SENSOR_HV7131B 5 */
+               4,      /* SENSOR_HV7131C 6 */
+               4,      /* SENSOR_ICM105A 7 */
+               4,      /* SENSOR_MC501CB 8 */
+               3,      /* SENSOR_OV7620 9 */
+               4,      /* SENSOR_OV7630C 10 */
+               4,      /* SENSOR_PAS106 11 */
+               4,      /* SENSOR_PB0330 12 */
+               4,      /* SENSOR_PO2030 13 */
+               4,      /* SENSOR_TAS5130CK 14 */
+               4,      /* SENSOR_TAS5130CXX 15 */
+               3,      /* SENSOR_TAS5130C_VF0250 16 */
+       };
+
+       /* define some sensors from the vendor/product */
+       sd->sharpness = 2;
+       switch (id->idVendor) {
+       case 0x041e:                            /* Creative */
+               switch (id->idProduct) {
+               case 0x4051:                    /* zc301 chips */
+               case 0x4053:
+                       sd->sensor = SENSOR_TAS5130C_VF0250;
+                       break;
+               }
+               break;
+       case 0x046d:                            /* Logitech Labtec */
+               switch (id->idProduct) {
+               case 0x08dd:
+                       sd->sensor = SENSOR_MC501CB;
+                       break;
+               }
+               break;
+       case 0x0ac8:                            /* Vimicro z-star */
+               switch (id->idProduct) {
+               case 0x305b:
+                       sd->sensor = SENSOR_TAS5130C_VF0250;
+                       break;
+               }
+               break;
+       }
+       sensor = zcxx_probeSensor(gspca_dev);
+       if (sensor >= 0)
+               PDEBUG(D_PROBE, "probe sensor -> %02x", sensor);
+       if ((unsigned) force_sensor < SENSOR_MAX) {
+               sd->sensor = force_sensor;
+               PDEBUG(D_PROBE, "sensor forced to %d", force_sensor);
+       } else {
+               switch (sensor) {
+               case -1:
+                       switch (sd->sensor) {
+                       case SENSOR_MC501CB:
+                               PDEBUG(D_PROBE, "Sensor MC501CB");
+                               break;
+                       case SENSOR_TAS5130C_VF0250:
+                               PDEBUG(D_PROBE, "Sensor Tas5130 (VF0250)");
+                               break;
+                       default:
+                               PDEBUG(D_PROBE,
+                                       "Sensor UNKNOW_0 force Tas5130");
+                               sd->sensor = SENSOR_TAS5130CXX;
+                       }
+                       break;
+               case 0:
+                       PDEBUG(D_PROBE, "Find Sensor HV7131B");
+                       sd->sensor = SENSOR_HV7131B;
+                       break;
+               case 0x04:
+                       PDEBUG(D_PROBE, "Find Sensor CS2102");
+                       sd->sensor = SENSOR_CS2102;
+                       break;
+               case 0x08:
+                       PDEBUG(D_PROBE, "Find Sensor HDCS2020(b)");
+                       sd->sensor = SENSOR_HDCS2020b;
+                       break;
+               case 0x0a:
+                       PDEBUG(D_PROBE,
+                               "Find Sensor PB0330. Chip revision %x",
+                               sd->chip_revision);
+                       sd->sensor = SENSOR_PB0330;
+                       break;
+               case 0x0c:
+                       PDEBUG(D_PROBE, "Find Sensor ICM105A");
+                       sd->sensor = SENSOR_ICM105A;
+                       break;
+               case 0x0e:
+                       PDEBUG(D_PROBE, "Find Sensor HDCS2020");
+                       sd->sensor = SENSOR_HDCS2020;
+                       sd->sharpness = 1;
+                       break;
+               case 0x0f:
+                       PDEBUG(D_PROBE, "Find Sensor PAS106");
+                       sd->sensor = SENSOR_PAS106;
+                       vga = 0;                /* SIF */
+                       break;
+               case 0x10:
+               case 0x12:
+                       PDEBUG(D_PROBE, "Find Sensor TAS5130");
+                       sd->sensor = SENSOR_TAS5130CXX;
+                       break;
+               case 0x11:
+                       PDEBUG(D_PROBE, "Find Sensor HV7131R(c)");
+                       sd->sensor = SENSOR_HV7131C;
+                       break;
+               case 0x13:
+                       PDEBUG(D_PROBE,
+                               "Find Sensor MI0360. Chip revision %x",
+                               sd->chip_revision);
+                       sd->sensor = SENSOR_PB0330;
+                       break;
+               case 0x14:
+                       PDEBUG(D_PROBE,
+                               "Find Sensor CS2102K?. Chip revision %x",
+                               sd->chip_revision);
+                       sd->sensor = SENSOR_CS2102K;
+                       break;
+               case 0x15:
+                       PDEBUG(D_PROBE,
+                               "Find Sensor TAS5130CK?. Chip revision %x",
+                               sd->chip_revision);
+                       sd->sensor = SENSOR_TAS5130CK;
+                       break;
+               case 0x29:
+                       PDEBUG(D_PROBE, "Find Sensor GC0305");
+                       sd->sensor = SENSOR_GC0305;
+                       break;
+               case 0x2030:
+                       PDEBUG(D_PROBE, "Find Sensor PO2030");
+                       sd->sensor = SENSOR_PO2030;
+                       sd->sharpness = 0;              /* from win traces */
+                       break;
+               case 0x7620:
+                       PDEBUG(D_PROBE, "Find Sensor OV7620");
+                       sd->sensor = SENSOR_OV7620;
+                       break;
+               case 0x7648:
+                       PDEBUG(D_PROBE, "Find Sensor OV7648");
+                       sd->sensor = SENSOR_OV7620;     /* same sensor (?) */
+                       break;
+               default:
+                       PDEBUG(D_ERR|D_PROBE, "Unknown sensor %02x", sensor);
+                       return -EINVAL;
+               }
+       }
+       if (sensor < 0x20) {
+               if (sensor == -1 || sensor == 0x10 || sensor == 0x12)
+                       reg_w(gspca_dev->dev, 0x02, 0x0010);
+               else
+                       reg_w(gspca_dev->dev, sensor & 0x0f, 0x0010);
+               reg_r(gspca_dev, 0x0010);
+       }
+
+       cam = &gspca_dev->cam;
+       cam->dev_name = (char *) id->driver_info;
+       cam->epaddr = 0x01;
+/*fixme:test*/
+       gspca_dev->nbalt--;
+       if (vga) {
+               cam->cam_mode = vga_mode;
+               cam->nmodes = ARRAY_SIZE(vga_mode);
+       } else {
+               cam->cam_mode = sif_mode;
+               cam->nmodes = ARRAY_SIZE(sif_mode);
+       }
+       sd->qindex = 1;
+       sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value;
+       sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value;
+       sd->gamma = gamma[(int) sd->sensor];
+       sd->autogain = sd_ctrls[SD_AUTOGAIN].qctrl.default_value;
+       sd->lightfreq = sd_ctrls[SD_FREQ].qctrl.default_value;
+       sd->sharpness = sd_ctrls[SD_SHARPNESS].qctrl.default_value;
+
+       /* switch the led off */
+       reg_w(gspca_dev->dev, 0x01, 0x0000);
+       return 0;
+}
+
+/* this function is called at open time */
+static int sd_open(struct gspca_dev *gspca_dev)
+{
+       reg_w(gspca_dev->dev, 0x01, 0x0000);
+       return 0;
+}
+
+static void sd_start(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       struct usb_device *dev = gspca_dev->dev;
+       const struct usb_action *zc3_init;
+       int mode;
+       static const struct usb_action *init_tb[SENSOR_MAX][2] = {
+               {cs2102_InitialScale, cs2102_Initial},          /* 0 */
+               {cs2102K_InitialScale, cs2102K_Initial},        /* 1 */
+               {gc0305_Initial, gc0305_InitialScale},          /* 2 */
+               {hdcs2020xx_InitialScale, hdcs2020xx_Initial},  /* 3 */
+               {hdcs2020xb_InitialScale, hdcs2020xb_Initial},  /* 4 */
+               {hv7131bxx_InitialScale, hv7131bxx_Initial},    /* 5 */
+               {hv7131cxx_InitialScale, hv7131cxx_Initial},    /* 6 */
+               {icm105axx_InitialScale, icm105axx_Initial},    /* 7 */
+               {MC501CB_InitialScale, MC501CB_Initial},        /* 9 */
+               {OV7620_mode0, OV7620_mode1},                   /* 9 */
+               {ov7630c_InitialScale, ov7630c_Initial},        /* 10 */
+               {pas106b_InitialScale, pas106b_Initial},        /* 11 */
+               {pb0330xx_InitialScale, pb0330xx_Initial},      /* 12 */
+/* or          {pb03303x_InitialScale, pb03303x_Initial}, */
+               {PO2030_mode0, PO2030_mode1},                   /* 13 */
+               {tas5130CK_InitialScale, tas5130CK_Initial},    /* 14 */
+               {tas5130cxx_InitialScale, tas5130cxx_Initial},  /* 15 */
+               {tas5130c_vf0250_InitialScale, tas5130c_vf0250_Initial},
+                                                               /* 16 */
+       };
+
+       mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;
+       zc3_init = init_tb[(int) sd->sensor][mode];
+       switch (sd->sensor) {
+       case SENSOR_HV7131B:
+       case SENSOR_HV7131C:
+               zcxx_probeSensor(gspca_dev);
+               break;
+       case SENSOR_PAS106:
+               usb_exchange(gspca_dev, pas106b_Initial_com);
+               break;
+       case SENSOR_PB0330:
+               if (mode) {
+                       if (sd->chip_revision == 0xc001
+                           || sd->chip_revision == 0xe001
+                           || sd->chip_revision == 0x8001)
+                               zc3_init = pb03303x_Initial;
+               } else {
+                       if (sd->chip_revision == 0xc001
+                           || sd->chip_revision == 0xe001
+                           || sd->chip_revision == 0x8001)
+                               zc3_init = pb03303x_InitialScale;
+               }
+               break;
+       }
+       usb_exchange(gspca_dev, zc3_init);
+
+       switch (sd->sensor) {
+       case SENSOR_GC0305:
+       case SENSOR_OV7620:
+       case SENSOR_PO2030:
+               msleep(100);                    /* ?? */
+               reg_r(gspca_dev, 0x0002);       /* --> 0x40 */
+               reg_w(dev, 0x09, 0x01ad);       /* (from win traces) */
+               reg_w(dev, 0x15, 0x01ae);
+               reg_w(dev, 0x0d, 0x003a);
+               reg_w(dev, 0x02, 0x003b);
+               reg_w(dev, 0x00, 0x0038);
+               break;
+       }
+
+       setmatrix(gspca_dev);
+       setbrightness(gspca_dev);
+       switch (sd->sensor) {
+       case SENSOR_OV7620:
+               reg_r(gspca_dev, 0x0008);
+               reg_w(dev, 0x00, 0x0008);
+               break;
+       case SENSOR_GC0305:
+               reg_r(gspca_dev, 0x0008);
+               /* fall thru */
+       case SENSOR_PO2030:
+               reg_w(dev, 0x03, 0x0008);
+               break;
+       }
+       setsharpness(gspca_dev);
+
+       /* set the gamma tables when not set */
+       switch (sd->sensor) {
+       case SENSOR_CS2102:             /* gamma set in xxx_Initial */
+       case SENSOR_CS2102K:
+       case SENSOR_HDCS2020:
+       case SENSOR_HDCS2020b:
+       case SENSOR_PB0330:             /* pb with chip_revision - see above */
+       case SENSOR_OV7630C:
+       case SENSOR_TAS5130CK:
+               break;
+       default:
+               setcontrast(gspca_dev);
+               break;
+       }
+       setmatrix(gspca_dev);                   /* one more time? */
+       switch (sd->sensor) {
+       case SENSOR_OV7620:
+               reg_r(gspca_dev, 0x0180);       /* from win */
+               reg_w(dev, 0x00, 0x0180);
+               break;
+       default:
+               setquality(gspca_dev);
+               break;
+       }
+       setlightfreq(gspca_dev);
+
+       switch (sd->sensor) {
+       case SENSOR_GC0305:
+       case SENSOR_OV7620:
+               reg_w(dev, 0x09, 0x01ad);       /* (from win traces) */
+               reg_w(dev, 0x15, 0x01ae);
+               sd->autogain = 0;
+               break;
+       case SENSOR_PO2030:
+               reg_w(dev, 0x40, 0x0117);       /* (from win traces) */
+               reg_r(gspca_dev, 0x0180);
+               break;
+       }
+
+       setautogain(gspca_dev);
+       switch (sd->sensor) {
+       case SENSOR_GC0305:
+/*             setlightfreq(gspca_dev);        ?? (end: 80 -> [18d]) */
+               reg_w(dev, 0x09, 0x01ad);       /* (from win traces) */
+               reg_w(dev, 0x15, 0x01ae);
+               reg_w(dev, 0x40, 0x0180);
+               reg_w(dev, 0x40, 0x0117);
+               reg_r(gspca_dev, 0x0180);
+               sd->autogain = 1;
+               setautogain(gspca_dev);
+               break;
+       case SENSOR_OV7620:
+               i2c_read(gspca_dev, 0x13);      /*fixme: returns 0xa3 */
+               i2c_write(gspca_dev, 0x13, 0xa3, 0x00);
+                                        /*fixme: returned value to send? */
+               reg_w(dev, 0x40, 0x0117);       /* (from win traces) */
+               reg_r(gspca_dev, 0x0180);
+               setautogain(gspca_dev);
+               msleep(500);
+               break;
+       case SENSOR_PO2030:
+               msleep(500);
+               reg_r(gspca_dev, 0x0008);
+               reg_r(gspca_dev, 0x0007);
+               reg_w(dev, 0x00, 0x0007);       /* (from win traces) */
+               reg_w(dev, 0x02, 0x0008);
+               break;
+       }
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+}
+
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       send_unknown(gspca_dev->dev, sd->sensor);
+}
+
+/* this function is called at close time */
+static void sd_close(struct gspca_dev *gspca_dev)
+{
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+                       struct gspca_frame *frame,
+                       __u8 *data,
+                       int len)
+{
+
+       if (data[0] == 0xff && data[1] == 0xd8) {       /* start of frame */
+               frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
+                                       data, 0);
+               /* put the JPEG header in the new frame */
+               jpeg_put_header(gspca_dev, frame,
+                               ((struct sd *) gspca_dev)->qindex,
+                               0x21);
+               /* remove the webcam's header:
+                * ff d8 ff fe 00 0e 00 00 ss ss 00 01 ww ww hh hh pp pp
+                *      - 'ss ss' is the frame sequence number (BE)
+                *      - 'ww ww' and 'hh hh' are the window dimensions (BE)
+                *      - 'pp pp' is the packet sequence number (BE)
+                */
+               data += 18;
+               len -= 18;
+       }
+       gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
+}
+
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->brightness = val;
+       if (gspca_dev->streaming)
+               setbrightness(gspca_dev);
+       return 0;
+}
+
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->brightness;
+       return 0;
+}
+
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->contrast = val;
+       if (gspca_dev->streaming)
+               setcontrast(gspca_dev);
+       return 0;
+}
+
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->contrast;
+       return 0;
+}
+
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->autogain = val;
+       if (gspca_dev->streaming)
+               setautogain(gspca_dev);
+       return 0;
+}
+
+static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->autogain;
+       return 0;
+}
+
+static int sd_setgamma(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->gamma = val;
+       if (gspca_dev->streaming)
+               setcontrast(gspca_dev);
+       return 0;
+}
+
+static int sd_getgamma(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->gamma;
+       return 0;
+}
+
+static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->lightfreq = val;
+       if (gspca_dev->streaming)
+               setlightfreq(gspca_dev);
+       return 0;
+}
+
+static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->lightfreq;
+       return 0;
+}
+
+static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->sharpness = val;
+       if (gspca_dev->streaming)
+               setsharpness(gspca_dev);
+       return 0;
+}
+
+static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->sharpness;
+       return 0;
+}
+
+static int sd_querymenu(struct gspca_dev *gspca_dev,
+                       struct v4l2_querymenu *menu)
+{
+       switch (menu->id) {
+       case V4L2_CID_POWER_LINE_FREQUENCY:
+               switch (menu->index) {
+               case 0:         /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */
+                       strcpy((char *) menu->name, "NoFliker");
+                       return 0;
+               case 1:         /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */
+                       strcpy((char *) menu->name, "50 Hz");
+                       return 0;
+               case 2:         /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */
+                       strcpy((char *) menu->name, "60 Hz");
+                       return 0;
+               }
+               break;
+       }
+       return -EINVAL;
+}
+
+static const struct sd_desc sd_desc = {
+       .name = MODULE_NAME,
+       .ctrls = sd_ctrls,
+       .nctrls = sizeof sd_ctrls / sizeof sd_ctrls[0],
+       .config = sd_config,
+       .open = sd_open,
+       .start = sd_start,
+       .stopN = sd_stopN,
+       .stop0 = sd_stop0,
+       .close = sd_close,
+       .pkt_scan = sd_pkt_scan,
+       .querymenu = sd_querymenu,
+};
+
+#define DVNM(name) .driver_info = (kernel_ulong_t) name
+static const __devinitdata struct usb_device_id device_table[] = {
+       {USB_DEVICE(0x041e, 0x041e), DVNM("Creative WebCam Live!")},
+#ifndef CONFIG_USB_ZC0301
+       {USB_DEVICE(0x041e, 0x4017), DVNM("Creative Webcam Mobile PD1090")},
+       {USB_DEVICE(0x041e, 0x401c), DVNM("Creative NX")},
+       {USB_DEVICE(0x041e, 0x401e), DVNM("Creative Nx Pro")},
+       {USB_DEVICE(0x041e, 0x401f), DVNM("Creative Webcam Notebook PD1171")},
+#endif
+       {USB_DEVICE(0x041e, 0x4029), DVNM("Creative WebCam Vista Pro")},
+#ifndef CONFIG_USB_ZC0301
+       {USB_DEVICE(0x041e, 0x4034), DVNM("Creative Instant P0620")},
+       {USB_DEVICE(0x041e, 0x4035), DVNM("Creative Instant P0620D")},
+       {USB_DEVICE(0x041e, 0x4036), DVNM("Creative Live !")},
+       {USB_DEVICE(0x041e, 0x403a), DVNM("Creative Nx Pro 2")},
+#endif
+       {USB_DEVICE(0x041e, 0x4051), DVNM("Creative Notebook Pro (VF0250)")},
+       {USB_DEVICE(0x041e, 0x4053), DVNM("Creative Live!Cam Video IM")},
+#ifndef CONFIG_USB_ZC0301
+       {USB_DEVICE(0x0458, 0x7007), DVNM("Genius VideoCam V2")},
+       {USB_DEVICE(0x0458, 0x700c), DVNM("Genius VideoCam V3")},
+       {USB_DEVICE(0x0458, 0x700f), DVNM("Genius VideoCam Web V2")},
+#endif
+       {USB_DEVICE(0x0461, 0x0a00), DVNM("MicroInnovation WebCam320")},
+       {USB_DEVICE(0x046d, 0x08a0), DVNM("Logitech QC IM")},
+       {USB_DEVICE(0x046d, 0x08a1), DVNM("Logitech QC IM 0x08A1 +sound")},
+       {USB_DEVICE(0x046d, 0x08a2), DVNM("Labtec Webcam Pro")},
+       {USB_DEVICE(0x046d, 0x08a3), DVNM("Logitech QC Chat")},
+       {USB_DEVICE(0x046d, 0x08a6), DVNM("Logitech QCim")},
+       {USB_DEVICE(0x046d, 0x08a7), DVNM("Logitech QuickCam Image")},
+       {USB_DEVICE(0x046d, 0x08a9), DVNM("Logitech Notebook Deluxe")},
+       {USB_DEVICE(0x046d, 0x08aa), DVNM("Labtec Webcam Notebook")},
+       {USB_DEVICE(0x046d, 0x08ac), DVNM("Logitech QuickCam Cool")},
+       {USB_DEVICE(0x046d, 0x08ad), DVNM("Logitech QCCommunicate STX")},
+#ifndef CONFIG_USB_ZC0301
+       {USB_DEVICE(0x046d, 0x08ae), DVNM("Logitech QuickCam for Notebooks")},
+#endif
+       {USB_DEVICE(0x046d, 0x08af), DVNM("Logitech QuickCam Cool")},
+       {USB_DEVICE(0x046d, 0x08b9), DVNM("Logitech QC IM ???")},
+       {USB_DEVICE(0x046d, 0x08d7), DVNM("Logitech QCam STX")},
+       {USB_DEVICE(0x046d, 0x08d9), DVNM("Logitech QuickCam IM/Connect")},
+       {USB_DEVICE(0x046d, 0x08d8), DVNM("Logitech Notebook Deluxe")},
+       {USB_DEVICE(0x046d, 0x08da), DVNM("Logitech QuickCam Messenger")},
+       {USB_DEVICE(0x046d, 0x08dd), DVNM("Logitech QuickCam for Notebooks")},
+       {USB_DEVICE(0x0471, 0x0325), DVNM("Philips SPC 200 NC")},
+       {USB_DEVICE(0x0471, 0x0326), DVNM("Philips SPC 300 NC")},
+       {USB_DEVICE(0x0471, 0x032d), DVNM("Philips spc210nc")},
+       {USB_DEVICE(0x0471, 0x032e), DVNM("Philips spc315nc")},
+       {USB_DEVICE(0x055f, 0xc005), DVNM("Mustek Wcam300A")},
+#ifndef CONFIG_USB_ZC0301
+       {USB_DEVICE(0x055f, 0xd003), DVNM("Mustek WCam300A")},
+       {USB_DEVICE(0x055f, 0xd004), DVNM("Mustek WCam300 AN")},
+#endif
+       {USB_DEVICE(0x0698, 0x2003), DVNM("CTX M730V built in")},
+       {USB_DEVICE(0x0ac8, 0x0302), DVNM("Z-star Vimicro zc0302")},
+#ifndef CONFIG_USB_ZC0301
+       {USB_DEVICE(0x0ac8, 0x301b), DVNM("Z-Star zc301b")},
+       {USB_DEVICE(0x0ac8, 0x303b), DVNM("Vimicro 0x303b")},
+#endif
+       {USB_DEVICE(0x0ac8, 0x305b), DVNM("Z-star Vimicro zc0305b")},
+#ifndef CONFIG_USB_ZC0301
+       {USB_DEVICE(0x0ac8, 0x307b), DVNM("Z-Star 307b")},
+       {USB_DEVICE(0x10fd, 0x0128), DVNM("Typhoon Webshot II 300k 0x0128")},
+       {USB_DEVICE(0x10fd, 0x8050), DVNM("Typhoon Webshot II USB 300k")},
+#endif
+       {}                      /* end of entry */
+};
+#undef DVNAME
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+                       const struct usb_device_id *id)
+{
+       return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+                               THIS_MODULE);
+}
+
+/* USB driver */
+static struct usb_driver sd_driver = {
+       .name = MODULE_NAME,
+       .id_table = device_table,
+       .probe = sd_probe,
+       .disconnect = gspca_disconnect,
+};
+
+static int __init sd_mod_init(void)
+{
+       if (usb_register(&sd_driver) < 0)
+               return -1;
+       PDEBUG(D_PROBE, "v%s registered", version);
+       return 0;
+}
+
+static void __exit sd_mod_exit(void)
+{
+       usb_deregister(&sd_driver);
+       PDEBUG(D_PROBE, "deregistered");
+}
+
+module_init(sd_mod_init);
+module_exit(sd_mod_exit);
+
+module_param(force_sensor, int, 0644);
+MODULE_PARM_DESC(force_sensor,
+       "Force sensor. Only for experts!!!");
index 7b65f5e537f803355593a85cf73b984843d851c3..a30254bed3119ed8483270d7f6e8cdb79624da6f 100644 (file)
@@ -194,88 +194,6 @@ static int get_key_knc1(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
        return 1;
 }
 
-/* Common (grey or coloured) pinnacle PCTV remote handling
- *
- */
-static int get_key_pinnacle(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw,
-                           int parity_offset, int marker, int code_modulo)
-{
-       unsigned char b[4];
-       unsigned int start = 0,parity = 0,code = 0;
-
-       /* poll IR chip */
-       if (4 != i2c_master_recv(&ir->c,b,4)) {
-               dprintk(2,"read error\n");
-               return -EIO;
-       }
-
-       for (start = 0; start < ARRAY_SIZE(b); start++) {
-               if (b[start] == marker) {
-                       code=b[(start+parity_offset+1)%4];
-                       parity=b[(start+parity_offset)%4];
-               }
-       }
-
-       /* Empty Request */
-       if (parity==0)
-               return 0;
-
-       /* Repeating... */
-       if (ir->old == parity)
-               return 0;
-
-       ir->old = parity;
-
-       /* drop special codes when a key is held down a long time for the grey controller
-          In this case, the second bit of the code is asserted */
-       if (marker == 0xfe && (code & 0x40))
-               return 0;
-
-       code %= code_modulo;
-
-       *ir_raw = code;
-       *ir_key = code;
-
-       dprintk(1,"Pinnacle PCTV key %02x\n", code);
-
-       return 1;
-}
-
-/* The grey pinnacle PCTV remote
- *
- *  There are one issue with this remote:
- *   - I2c packet does not change when the same key is pressed quickly. The workaround
- *     is to hold down each key for about half a second, so that another code is generated
- *     in the i2c packet, and the function can distinguish key presses.
- *
- * Sylvain Pasche <sylvain.pasche@gmail.com>
- */
-int get_key_pinnacle_grey(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
-{
-
-       return get_key_pinnacle(ir, ir_key, ir_raw, 1, 0xfe, 0xff);
-}
-
-EXPORT_SYMBOL_GPL(get_key_pinnacle_grey);
-
-
-/* The new pinnacle PCTV remote (with the colored buttons)
- *
- * Ricardo Cerqueira <v4l@cerqueira.org>
- */
-int get_key_pinnacle_color(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
-{
-       /* code_modulo parameter (0x88) is used to reduce code value to fit inside IR_KEYTAB_SIZE
-        *
-        * this is the only value that results in 42 unique
-        * codes < 128
-        */
-
-       return get_key_pinnacle(ir, ir_key, ir_raw, 2, 0x80, 0x88);
-}
-
-EXPORT_SYMBOL_GPL(get_key_pinnacle_color);
-
 /* ----------------------------------------------------------------------- */
 
 static void ir_key_poll(struct IR_i2c *ir)
index 4fb8faefe2ced9f54d6c08bca9d8cdff42e10000..4e05f91a9100522a0bc8801fed81aa00be41e631 100644 (file)
@@ -40,7 +40,7 @@
 #define MSP_MONO   MSP_INPUT(MSP_IN_MONO, MSP_IN_TUNER1, \
                                MSP_DSP_IN_SCART, MSP_DSP_IN_SCART)
 
-#define V4L2_STD_NOT_MN (V4L2_STD_PAL|V4L2_STD_SECAM)
+#define V4L2_STD_PAL_SECAM (V4L2_STD_PAL|V4L2_STD_SECAM)
 
 /* usual i2c tuner addresses to probe */
 static struct ivtv_card_tuner_i2c ivtv_i2c_std = {
@@ -300,7 +300,7 @@ static const struct ivtv_card ivtv_card_mpg600 = {
        .gpio_audio_detect = { .mask = 0x0900, .stereo = 0x0100 },
        .tuners = {
                /* The PAL tuner is confirmed */
-               { .std = V4L2_STD_NOT_MN, .tuner = TUNER_PHILIPS_FQ1216ME },
+               { .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FQ1216ME },
                { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FQ1286 },
        },
        .pci_list = ivtv_pci_mpg600,
@@ -341,7 +341,7 @@ static const struct ivtv_card ivtv_card_mpg160 = {
                              .lang1 = 0x0004, .lang2  = 0x0000, .both   = 0x0008 },
        .gpio_audio_detect = { .mask = 0x0900, .stereo = 0x0100 },
        .tuners = {
-               { .std = V4L2_STD_NOT_MN, .tuner = TUNER_PHILIPS_FQ1216ME },
+               { .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FQ1216ME },
                { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FQ1286 },
        },
        .pci_list = ivtv_pci_mpg160,
@@ -377,7 +377,7 @@ static const struct ivtv_card ivtv_card_pg600 = {
                { IVTV_CARD_INPUT_LINE_IN1,   CX25840_AUDIO_SERIAL },
        },
        .tuners = {
-               { .std = V4L2_STD_NOT_MN, .tuner = TUNER_PHILIPS_FQ1216ME },
+               { .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FQ1216ME },
                { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FQ1286 },
        },
        .pci_list = ivtv_pci_pg600,
@@ -418,7 +418,7 @@ static const struct ivtv_card ivtv_card_avc2410 = {
           on the country/region setting of the user to decide which tuner
           is available. */
        .tuners = {
-               { .std = V4L2_STD_NOT_MN, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
+               { .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
                { .std = V4L2_STD_ALL - V4L2_STD_NTSC_M_JP,
                        .tuner = TUNER_PHILIPS_FM1236_MK3 },
                { .std = V4L2_STD_NTSC_M_JP, .tuner = TUNER_PHILIPS_FQ1286 },
@@ -492,7 +492,7 @@ static const struct ivtv_card ivtv_card_tg5000tv = {
        .gpio_video_input  = { .mask = 0x0030, .tuner  = 0x0000,
                          .composite = 0x0010, .svideo = 0x0020 },
        .tuners = {
-               { .std = V4L2_STD_525_60|V4L2_STD_MN, .tuner = TUNER_PHILIPS_FQ1286 },
+               { .std = V4L2_STD_MN, .tuner = TUNER_PHILIPS_FQ1286 },
        },
        .pci_list = ivtv_pci_tg5000tv,
        .i2c = &ivtv_i2c_std,
@@ -523,7 +523,7 @@ static const struct ivtv_card ivtv_card_va2000 = {
                { IVTV_CARD_INPUT_AUD_TUNER, MSP_TUNER },
        },
        .tuners = {
-               { .std = V4L2_STD_525_60|V4L2_STD_MN, .tuner = TUNER_PHILIPS_FQ1286 },
+               { .std = V4L2_STD_MN, .tuner = TUNER_PHILIPS_FQ1286 },
        },
        .pci_list = ivtv_pci_va2000,
        .i2c = &ivtv_i2c_std,
@@ -567,7 +567,7 @@ static const struct ivtv_card ivtv_card_cx23416gyc = {
        .gpio_audio_freq   = { .mask = 0xc000, .f32000 = 0x0000,
                             .f44100 = 0x4000, .f48000 = 0x8000 },
        .tuners = {
-               { .std = V4L2_STD_NOT_MN, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
+               { .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
                { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FM1236_MK3 },
        },
        .pci_list = ivtv_pci_cx23416gyc,
@@ -599,7 +599,7 @@ static const struct ivtv_card ivtv_card_cx23416gyc_nogr = {
        .gpio_audio_freq   = { .mask = 0xc000, .f32000 = 0x0000,
                             .f44100 = 0x4000, .f48000 = 0x8000 },
        .tuners = {
-               { .std = V4L2_STD_NOT_MN, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
+               { .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
                { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FM1236_MK3 },
        },
        .i2c = &ivtv_i2c_std,
@@ -629,7 +629,7 @@ static const struct ivtv_card ivtv_card_cx23416gyc_nogrycs = {
        .gpio_audio_freq   = { .mask = 0xc000, .f32000 = 0x0000,
                             .f44100 = 0x4000, .f48000 = 0x8000 },
        .tuners = {
-               { .std = V4L2_STD_NOT_MN, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
+               { .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
                { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FM1236_MK3 },
        },
        .i2c = &ivtv_i2c_std,
@@ -669,7 +669,7 @@ static const struct ivtv_card ivtv_card_gv_mvprx = {
        .gpio_audio_input  = { .mask = 0xffff, .tuner  = 0x0200, .linein = 0x0300 },
        .tuners = {
                /* This card has the Panasonic VP27 tuner */
-               { .std = V4L2_STD_525_60|V4L2_STD_MN, .tuner = TUNER_PANASONIC_VP27 },
+               { .std = V4L2_STD_MN, .tuner = TUNER_PANASONIC_VP27 },
        },
        .pci_list = ivtv_pci_gv_mvprx,
        .i2c = &ivtv_i2c_std,
@@ -706,7 +706,7 @@ static const struct ivtv_card ivtv_card_gv_mvprx2e = {
        .gpio_audio_input  = { .mask = 0xffff, .tuner  = 0x0200, .linein = 0x0300 },
        .tuners = {
                /* This card has the Panasonic VP27 tuner */
-               { .std = V4L2_STD_525_60|V4L2_STD_MN, .tuner = TUNER_PANASONIC_VP27 },
+               { .std = V4L2_STD_MN, .tuner = TUNER_PANASONIC_VP27 },
        },
        .pci_list = ivtv_pci_gv_mvprx2e,
        .i2c = &ivtv_i2c_std,
@@ -741,7 +741,7 @@ static const struct ivtv_card ivtv_card_gotview_pci_dvd = {
        .gpio_init = { .direction = 0xf000, .initial_value = 0xA000 },
        .tuners = {
                /* This card has a Philips FQ1216ME MK3 tuner */
-               { .std = V4L2_STD_NOT_MN, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
+               { .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
        },
        .pci_list = ivtv_pci_gotview_pci_dvd,
        .i2c = &ivtv_i2c_std,
@@ -780,7 +780,7 @@ static const struct ivtv_card ivtv_card_gotview_pci_dvd2 = {
        .gpio_audio_input  = { .mask = 0x0800, .tuner = 0, .linein = 0, .radio = 0x0800 },
        .tuners = {
                /* This card has a Philips FQ1216ME MK5 tuner */
-               { .std = V4L2_STD_NOT_MN, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
+               { .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
        },
        .pci_list = ivtv_pci_gotview_pci_dvd2,
        .i2c = &ivtv_i2c_std,
@@ -858,7 +858,7 @@ static const struct ivtv_card ivtv_card_dctmvtvp1 = {
        .gpio_video_input  = { .mask = 0x0030, .tuner  = 0x0000,
                               .composite = 0x0010, .svideo = 0x0020},
        .tuners = {
-               { .std = V4L2_STD_525_60|V4L2_STD_MN, .tuner = TUNER_PHILIPS_FQ1286 },
+               { .std = V4L2_STD_MN, .tuner = TUNER_PHILIPS_FQ1286 },
        },
        .pci_list = ivtv_pci_dctmvtvp1,
        .i2c = &ivtv_i2c_std,
@@ -923,7 +923,6 @@ static const struct ivtv_card ivtv_card_club3d = {
                { IVTV_CARD_INPUT_LINE_IN1,   CX25840_AUDIO_SERIAL },
        },
        .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 },
-       .gpio_init = { .direction = 0x1000, .initial_value = 0x1000 }, /* tuner reset */
        .xceive_pin = 12,
        .tuners = {
                { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 },
@@ -959,7 +958,7 @@ static const struct ivtv_card ivtv_card_avertv_mce116 = {
                { IVTV_CARD_INPUT_LINE_IN1,   CX25840_AUDIO_SERIAL, 1 },
        },
        /* enable line-in */
-       .gpio_init = { .direction = 0xe400, .initial_value = 0x4400 },
+       .gpio_init = { .direction = 0xe000, .initial_value = 0x4000 },
        .xceive_pin = 10,
        .tuners = {
                { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 },
@@ -1001,7 +1000,7 @@ static const struct ivtv_card ivtv_card_aver_pvr150 = {
        .gpio_audio_input  = { .mask = 0x0800, .tuner = 0, .linein = 0, .radio = 0x0800 },
        .tuners = {
                /* This card has a Partsnic PTI-5NF05 tuner */
-               { .std = V4L2_STD_525_60|V4L2_STD_MN, .tuner = TUNER_TCL_2002N },
+               { .std = V4L2_STD_MN, .tuner = TUNER_TCL_2002N },
        },
        .pci_list = ivtv_pci_aver_pvr150,
        .i2c = &ivtv_i2c_radio,
@@ -1069,7 +1068,7 @@ static const struct ivtv_card ivtv_card_asus_falcon2 = {
        },
        .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO_SERIAL, M52790_IN_TUNER },
        .tuners = {
-               { .std = V4L2_STD_525_60|V4L2_STD_MN, .tuner = TUNER_PHILIPS_FM1236_MK3 },
+               { .std = V4L2_STD_MN, .tuner = TUNER_PHILIPS_FM1236_MK3 },
        },
        .pci_list = ivtv_pci_asus_falcon2,
        .i2c = &ivtv_i2c_std,
@@ -1102,7 +1101,7 @@ static const struct ivtv_card ivtv_card_aver_m104 = {
        },
        .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO_SERIAL, 2 },
        /* enable line-in + reset tuner */
-       .gpio_init = { .direction = 0xe400, .initial_value = 0x4000 },
+       .gpio_init = { .direction = 0xe000, .initial_value = 0x4000 },
        .xceive_pin = 10,
        .tuners = {
                { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 },
@@ -1111,6 +1110,41 @@ static const struct ivtv_card ivtv_card_aver_m104 = {
        .i2c = &ivtv_i2c_std,
 };
 
+/* ------------------------------------------------------------------------- */
+
+/* Buffalo PC-MV5L/PCI cards */
+
+static const struct ivtv_card_pci_info ivtv_pci_buffalo[] = {
+       { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_MELCO, 0x052b },
+       { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_buffalo = {
+       .type = IVTV_CARD_BUFFALO_MV5L,
+       .name = "Buffalo PC-MV5L/PCI",
+       .v4l2_capabilities = IVTV_CAP_ENCODER,
+       .hw_video = IVTV_HW_CX25840,
+       .hw_audio = IVTV_HW_CX25840,
+       .hw_audio_ctrl = IVTV_HW_CX25840,
+       .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER,
+       .video_inputs = {
+               { IVTV_CARD_INPUT_VID_TUNER,  0, CX25840_COMPOSITE2 },
+               { IVTV_CARD_INPUT_SVIDEO1,    1,
+                       CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 },
+               { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 },
+       },
+       .audio_inputs = {
+               { IVTV_CARD_INPUT_AUD_TUNER,  CX25840_AUDIO5       },
+               { IVTV_CARD_INPUT_LINE_IN1,   CX25840_AUDIO_SERIAL },
+       },
+       .xceive_pin = 12,
+       .tuners = {
+               { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 },
+       },
+       .pci_list = ivtv_pci_buffalo,
+       .i2c = &ivtv_i2c_std,
+};
+
 static const struct ivtv_card *ivtv_card_list[] = {
        &ivtv_card_pvr250,
        &ivtv_card_pvr350,
@@ -1137,6 +1171,7 @@ static const struct ivtv_card *ivtv_card_list[] = {
        &ivtv_card_aver_pvr150,
        &ivtv_card_aver_ezmaker,
        &ivtv_card_aver_m104,
+       &ivtv_card_buffalo,
 
        /* Variations of standard cards but with the same PCI IDs.
           These cards must come last in this list. */
index 748485dcebbdab220009e3ac05c307b9adbf2d71..381af1bceef84c9924ee69b0c33f31a64a78820e 100644 (file)
@@ -49,7 +49,8 @@
 #define IVTV_CARD_AVER_PVR150PLUS    22 /* AVerMedia PVR-150 Plus */
 #define IVTV_CARD_AVER_EZMAKER       23 /* AVerMedia EZMaker PCI Deluxe */
 #define IVTV_CARD_AVER_M104          24 /* AverMedia M104 miniPCI card */
-#define IVTV_CARD_LAST                      24
+#define IVTV_CARD_BUFFALO_MV5L       25 /* Buffalo PC-MV5L/PCI card */
+#define IVTV_CARD_LAST                      25
 
 /* Variants of existing cards but with the same PCI IDs. The driver
    detects these based on other device information.
index c7e449f6397bb9b3d6f91311ee82fbfa0289bdc9..48e103be718340fe0db30394d5b2b989912c8697 100644 (file)
@@ -47,12 +47,12 @@ static const u32 *ctrl_classes[] = {
        NULL
 };
 
-static int ivtv_queryctrl(struct ivtv *itv, struct v4l2_queryctrl *qctrl)
+
+int ivtv_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qctrl)
 {
+       struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
        const char *name;
 
-       IVTV_DEBUG_IOCTL("VIDIOC_QUERYCTRL(%08x)\n", qctrl->id);
-
        qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id);
        if (qctrl->id == 0)
                return -EINVAL;
@@ -87,21 +87,35 @@ static int ivtv_queryctrl(struct ivtv *itv, struct v4l2_queryctrl *qctrl)
        return 0;
 }
 
-static int ivtv_querymenu(struct ivtv *itv, struct v4l2_querymenu *qmenu)
+int ivtv_querymenu(struct file *file, void *fh, struct v4l2_querymenu *qmenu)
 {
+       struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
        struct v4l2_queryctrl qctrl;
 
        qctrl.id = qmenu->id;
-       ivtv_queryctrl(itv, &qctrl);
-       return v4l2_ctrl_query_menu(qmenu, &qctrl, cx2341x_ctrl_get_menu(qmenu->id));
+       ivtv_queryctrl(file, fh, &qctrl);
+       return v4l2_ctrl_query_menu(qmenu, &qctrl,
+                       cx2341x_ctrl_get_menu(&itv->params, qmenu->id));
 }
 
-static int ivtv_s_ctrl(struct ivtv *itv, struct v4l2_control *vctrl)
+static int ivtv_try_ctrl(struct file *file, void *fh,
+                                       struct v4l2_ext_control *vctrl)
 {
-       s32 v = vctrl->value;
-
-       IVTV_DEBUG_IOCTL("VIDIOC_S_CTRL(%08x, %x)\n", vctrl->id, v);
+       struct v4l2_queryctrl qctrl;
+       const char **menu_items = NULL;
+       int err;
+
+       qctrl.id = vctrl->id;
+       err = ivtv_queryctrl(file, fh, &qctrl);
+       if (err)
+               return err;
+       if (qctrl.type == V4L2_CTRL_TYPE_MENU)
+               menu_items = v4l2_ctrl_get_menu(qctrl.id);
+       return v4l2_ctrl_check(vctrl, &qctrl, menu_items);
+}
 
+static int ivtv_s_ctrl(struct ivtv *itv, struct v4l2_control *vctrl)
+{
        switch (vctrl->id) {
                /* Standard V4L2 controls */
        case V4L2_CID_BRIGHTNESS:
@@ -119,7 +133,7 @@ static int ivtv_s_ctrl(struct ivtv *itv, struct v4l2_control *vctrl)
                return ivtv_i2c_hw(itv, itv->card->hw_audio_ctrl, VIDIOC_S_CTRL, vctrl);
 
        default:
-               IVTV_DEBUG_IOCTL("invalid control %x\n", vctrl->id);
+               IVTV_DEBUG_IOCTL("invalid control 0x%x\n", vctrl->id);
                return -EINVAL;
        }
        return 0;
@@ -127,8 +141,6 @@ static int ivtv_s_ctrl(struct ivtv *itv, struct v4l2_control *vctrl)
 
 static int ivtv_g_ctrl(struct ivtv *itv, struct v4l2_control *vctrl)
 {
-       IVTV_DEBUG_IOCTL("VIDIOC_G_CTRL(%08x)\n", vctrl->id);
-
        switch (vctrl->id) {
                /* Standard V4L2 controls */
        case V4L2_CID_BRIGHTNESS:
@@ -145,7 +157,7 @@ static int ivtv_g_ctrl(struct ivtv *itv, struct v4l2_control *vctrl)
        case V4L2_CID_AUDIO_LOUDNESS:
                return ivtv_i2c_hw(itv, itv->card->hw_audio_ctrl, VIDIOC_G_CTRL, vctrl);
        default:
-               IVTV_DEBUG_IOCTL("invalid control %x\n", vctrl->id);
+               IVTV_DEBUG_IOCTL("invalid control 0x%x\n", vctrl->id);
                return -EINVAL;
        }
        return 0;
@@ -191,119 +203,106 @@ static int ivtv_setup_vbi_fmt(struct ivtv *itv, enum v4l2_mpeg_stream_vbi_fmt fm
        return 0;
 }
 
-int ivtv_control_ioctls(struct ivtv *itv, unsigned int cmd, void *arg)
+int ivtv_g_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c)
 {
+       struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
        struct v4l2_control ctrl;
 
-       switch (cmd) {
-       case VIDIOC_QUERYMENU:
-               IVTV_DEBUG_IOCTL("VIDIOC_QUERYMENU\n");
-               return ivtv_querymenu(itv, arg);
-
-       case VIDIOC_QUERYCTRL:
-               return ivtv_queryctrl(itv, arg);
-
-       case VIDIOC_S_CTRL:
-               return ivtv_s_ctrl(itv, arg);
-
-       case VIDIOC_G_CTRL:
-               return ivtv_g_ctrl(itv, arg);
-
-       case VIDIOC_S_EXT_CTRLS:
-       {
-               struct v4l2_ext_controls *c = arg;
-
-               if (c->ctrl_class == V4L2_CTRL_CLASS_USER) {
-                       int i;
-                       int err = 0;
-
-                       for (i = 0; i < c->count; i++) {
-                               ctrl.id = c->controls[i].id;
-                               ctrl.value = c->controls[i].value;
-                               err = ivtv_s_ctrl(itv, &ctrl);
-                               c->controls[i].value = ctrl.value;
-                               if (err) {
-                                       c->error_idx = i;
-                                       break;
-                               }
+       if (c->ctrl_class == V4L2_CTRL_CLASS_USER) {
+               int i;
+               int err = 0;
+
+               for (i = 0; i < c->count; i++) {
+                       ctrl.id = c->controls[i].id;
+                       ctrl.value = c->controls[i].value;
+                       err = ivtv_g_ctrl(itv, &ctrl);
+                       c->controls[i].value = ctrl.value;
+                       if (err) {
+                               c->error_idx = i;
+                               break;
                        }
-                       return err;
                }
-               IVTV_DEBUG_IOCTL("VIDIOC_S_EXT_CTRLS\n");
-               if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
-                       static u32 freqs[3] = { 44100, 48000, 32000 };
-                       struct cx2341x_mpeg_params p = itv->params;
-                       int err = cx2341x_ext_ctrls(&p, atomic_read(&itv->capturing), arg, cmd);
-                       unsigned idx;
-
-                       if (err)
-                               return err;
-
-                       if (p.video_encoding != itv->params.video_encoding) {
-                               int is_mpeg1 = p.video_encoding ==
-                                               V4L2_MPEG_VIDEO_ENCODING_MPEG_1;
-                               struct v4l2_format fmt;
-
-                               /* fix videodecoder resolution */
-                               fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-                               fmt.fmt.pix.width = itv->params.width / (is_mpeg1 ? 2 : 1);
-                               fmt.fmt.pix.height = itv->params.height;
-                               itv->video_dec_func(itv, VIDIOC_S_FMT, &fmt);
-                       }
-                       err = cx2341x_update(itv, ivtv_api_func, &itv->params, &p);
-                       if (!err && itv->params.stream_vbi_fmt != p.stream_vbi_fmt) {
-                               err = ivtv_setup_vbi_fmt(itv, p.stream_vbi_fmt);
+               return err;
+       }
+       if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG)
+               return cx2341x_ext_ctrls(&itv->params, 0, c, VIDIOC_G_EXT_CTRLS);
+       return -EINVAL;
+}
+
+int ivtv_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c)
+{
+       struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
+       struct v4l2_control ctrl;
+
+       if (c->ctrl_class == V4L2_CTRL_CLASS_USER) {
+               int i;
+               int err = 0;
+
+               for (i = 0; i < c->count; i++) {
+                       ctrl.id = c->controls[i].id;
+                       ctrl.value = c->controls[i].value;
+                       err = ivtv_s_ctrl(itv, &ctrl);
+                       c->controls[i].value = ctrl.value;
+                       if (err) {
+                               c->error_idx = i;
+                               break;
                        }
-                       itv->params = p;
-                       itv->dualwatch_stereo_mode = p.audio_properties & 0x0300;
-                       idx = p.audio_properties & 0x03;
-                       /* The audio clock of the digitizer must match the codec sample
-                          rate otherwise you get some very strange effects. */
-                       if (idx < sizeof(freqs))
-                           ivtv_call_i2c_clients(itv, VIDIOC_INT_AUDIO_CLOCK_FREQ, &freqs[idx]);
-                       return err;
                }
-               return -EINVAL;
+               return err;
        }
+       if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
+               static u32 freqs[3] = { 44100, 48000, 32000 };
+               struct cx2341x_mpeg_params p = itv->params;
+               int err = cx2341x_ext_ctrls(&p, atomic_read(&itv->capturing), c, VIDIOC_S_EXT_CTRLS);
+               unsigned idx;
 
-       case VIDIOC_G_EXT_CTRLS:
-       {
-               struct v4l2_ext_controls *c = arg;
-
-               if (c->ctrl_class == V4L2_CTRL_CLASS_USER) {
-                       int i;
-                       int err = 0;
-
-                       for (i = 0; i < c->count; i++) {
-                               ctrl.id = c->controls[i].id;
-                               ctrl.value = c->controls[i].value;
-                               err = ivtv_g_ctrl(itv, &ctrl);
-                               c->controls[i].value = ctrl.value;
-                               if (err) {
-                                       c->error_idx = i;
-                                       break;
-                               }
-                       }
+               if (err)
                        return err;
+
+               if (p.video_encoding != itv->params.video_encoding) {
+                       int is_mpeg1 = p.video_encoding ==
+                               V4L2_MPEG_VIDEO_ENCODING_MPEG_1;
+                       struct v4l2_format fmt;
+
+                       /* fix videodecoder resolution */
+                       fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+                       fmt.fmt.pix.width = itv->params.width / (is_mpeg1 ? 2 : 1);
+                       fmt.fmt.pix.height = itv->params.height;
+                       itv->video_dec_func(itv, VIDIOC_S_FMT, &fmt);
                }
-               IVTV_DEBUG_IOCTL("VIDIOC_G_EXT_CTRLS\n");
-               if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG)
-                       return cx2341x_ext_ctrls(&itv->params, 0, arg, cmd);
-               return -EINVAL;
+               err = cx2341x_update(itv, ivtv_api_func, &itv->params, &p);
+               if (!err && itv->params.stream_vbi_fmt != p.stream_vbi_fmt)
+                       err = ivtv_setup_vbi_fmt(itv, p.stream_vbi_fmt);
+               itv->params = p;
+               itv->dualwatch_stereo_mode = p.audio_properties & 0x0300;
+               idx = p.audio_properties & 0x03;
+               /* The audio clock of the digitizer must match the codec sample
+                  rate otherwise you get some very strange effects. */
+               if (idx < sizeof(freqs))
+                       ivtv_call_i2c_clients(itv, VIDIOC_INT_AUDIO_CLOCK_FREQ, &freqs[idx]);
+               return err;
        }
+       return -EINVAL;
+}
 
-       case VIDIOC_TRY_EXT_CTRLS:
-       {
-               struct v4l2_ext_controls *c = arg;
+int ivtv_try_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c)
+{
+       struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
 
-               IVTV_DEBUG_IOCTL("VIDIOC_TRY_EXT_CTRLS\n");
-               if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG)
-                       return cx2341x_ext_ctrls(&itv->params, atomic_read(&itv->capturing), arg, cmd);
-               return -EINVAL;
-       }
+       if (c->ctrl_class == V4L2_CTRL_CLASS_USER) {
+               int i;
+               int err = 0;
 
-       default:
-               return -EINVAL;
+               for (i = 0; i < c->count; i++) {
+                       err = ivtv_try_ctrl(file, fh, &c->controls[i]);
+                       if (err) {
+                               c->error_idx = i;
+                               break;
+                       }
+               }
+               return err;
        }
-       return 0;
+       if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG)
+               return cx2341x_ext_ctrls(&itv->params, atomic_read(&itv->capturing), c, VIDIOC_TRY_EXT_CTRLS);
+       return -EINVAL;
 }
index bb8a6a5ed2bc48d2ff503c756e06666b043223fe..1c7721e23c9ba003936ab9cb336363612f6ed7d3 100644 (file)
 #ifndef IVTV_CONTROLS_H
 #define IVTV_CONTROLS_H
 
-int ivtv_control_ioctls(struct ivtv *itv, unsigned int cmd, void *arg);
+int ivtv_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *a);
+int ivtv_g_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *a);
+int ivtv_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *a);
+int ivtv_try_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *a);
+int ivtv_querymenu(struct file *file, void *fh, struct v4l2_querymenu *a);
 
 #endif
index 797e636771da07a38bc7025130a681371a09312f..41fd79279bb53eaab172621d3a0ef2c82f94c047 100644 (file)
@@ -191,6 +191,7 @@ MODULE_PARM_DESC(cardtype,
                 "\t\t\t23 = AverMedia PVR-150 Plus\n"
                 "\t\t\t24 = AverMedia EZMaker PCI Deluxe\n"
                 "\t\t\t25 = AverMedia M104 (not yet working)\n"
+                "\t\t\t26 = Buffalo PC-MV5L/PCI\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: BGH, DK, I, M, N, Nc, 60");
@@ -1019,7 +1020,7 @@ static int __devinit ivtv_probe(struct pci_dev *dev,
        ivtv_cards[ivtv_cards_active] = itv;
        itv->dev = dev;
        itv->num = ivtv_cards_active++;
-       snprintf(itv->name, sizeof(itv->name) - 1, "ivtv%d", itv->num);
+       snprintf(itv->name, sizeof(itv->name), "ivtv%d", itv->num);
        IVTV_INFO("Initializing card #%d\n", itv->num);
 
        spin_unlock(&ivtv_cards_lock);
@@ -1127,6 +1128,12 @@ static int __devinit ivtv_probe(struct pci_dev *dev,
        /* if no tuner was found, then pick the first tuner in the card list */
        if (itv->options.tuner == -1 && itv->card->tuners[0].std) {
                itv->std = itv->card->tuners[0].std;
+               if (itv->std & V4L2_STD_PAL)
+                       itv->std = V4L2_STD_PAL_BG | V4L2_STD_PAL_H;
+               else if (itv->std & V4L2_STD_NTSC)
+                       itv->std = V4L2_STD_NTSC_M;
+               else if (itv->std & V4L2_STD_SECAM)
+                       itv->std = V4L2_STD_SECAM_L;
                itv->options.tuner = itv->card->tuners[0].tuner;
        }
        if (itv->options.radio == -1)
@@ -1261,9 +1268,13 @@ err:
 int ivtv_init_on_first_open(struct ivtv *itv)
 {
        struct v4l2_frequency vf;
+       /* Needed to call ioctls later */
+       struct ivtv_open_id fh;
        int fw_retry_count = 3;
        int video_input;
 
+       fh.itv = itv;
+
        if (test_bit(IVTV_F_I_FAILED, &itv->i_flags))
                return -ENXIO;
 
@@ -1311,18 +1322,18 @@ int ivtv_init_on_first_open(struct ivtv *itv)
 
        video_input = itv->active_input;
        itv->active_input++;    /* Force update of input */
-       ivtv_v4l2_ioctls(itv, NULL, VIDIOC_S_INPUT, &video_input);
+       ivtv_s_input(NULL, &fh, video_input);
 
        /* Let the VIDIOC_S_STD ioctl do all the work, keeps the code
           in one place. */
        itv->std++;             /* Force full standard initialization */
        itv->std_out = itv->std;
-       ivtv_v4l2_ioctls(itv, NULL, VIDIOC_S_FREQUENCY, &vf);
+       ivtv_s_frequency(NULL, &fh, &vf);
 
        if (itv->card->v4l2_capabilities & V4L2_CAP_VIDEO_OUTPUT) {
                ivtv_init_mpeg_decoder(itv);
        }
-       ivtv_v4l2_ioctls(itv, NULL, VIDIOC_S_STD, &itv->tuner_std);
+       ivtv_s_std(NULL, &fh, &itv->tuner_std);
 
        /* On a cx23416 this seems to be able to enable DMA to the chip? */
        if (!itv->has_cx23415)
index 9d23b1efd36d9c93a1e43db7f378147cddfa136f..a08bb3331cfbd05f7926912dd3fab6bd8daac569 100644 (file)
@@ -635,7 +635,6 @@ struct ivtv {
        spinlock_t lock;                /* lock access to this struct */
        struct mutex serialize_lock;    /* mutex used to serialize open/close/start/stop/ioctl operations */
 
-
        /* Streams */
        int stream_buf_size[IVTV_MAX_STREAMS];          /* stream buffer size */
        struct ivtv_stream streams[IVTV_MAX_STREAMS];   /* stream data */
index db813e071ce682b36a4dc390c874fbb07ba3989e..7ec5c99f9ad1211c73d6004b3bbe12a4f822c2f4 100644 (file)
@@ -582,6 +582,19 @@ ssize_t ivtv_v4l2_write(struct file *filp, const char __user *user_buf, size_t c
        ivtv_queue_init(&q);
        set_bit(IVTV_F_S_APPL_IO, &s->s_flags);
 
+       /* Start decoder (returns 0 if already started) */
+       mutex_lock(&itv->serialize_lock);
+       rc = ivtv_start_decoding(id, itv->speed);
+       mutex_unlock(&itv->serialize_lock);
+       if (rc) {
+               IVTV_DEBUG_WARN("Failed start decode stream %s\n", s->name);
+
+               /* failure, clean up */
+               clear_bit(IVTV_F_S_STREAMING, &s->s_flags);
+               clear_bit(IVTV_F_S_APPL_IO, &s->s_flags);
+               return rc;
+       }
+
 retry:
        /* If possible, just DMA the entire frame - Check the data transfer size
        since we may get here before the stream has been fully set-up */
@@ -664,18 +677,6 @@ retry:
                ivtv_enqueue(s, buf, &s->q_full);
        }
 
-       /* Start decoder (returns 0 if already started) */
-       mutex_lock(&itv->serialize_lock);
-       rc = ivtv_start_decoding(id, itv->speed);
-       mutex_unlock(&itv->serialize_lock);
-       if (rc) {
-               IVTV_DEBUG_WARN("Failed start decode stream %s\n", s->name);
-
-               /* failure, clean up */
-               clear_bit(IVTV_F_S_STREAMING, &s->s_flags);
-               clear_bit(IVTV_F_S_APPL_IO, &s->s_flags);
-               return rc;
-       }
        if (test_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags)) {
                if (s->q_full.length >= itv->dma_data_req_size) {
                        int got_sig;
index d8ac09f3cce6ffc17506c6bc7d7d4021e8bacf07..bc22905ea20fa58c5f890a58a418361d495a46ef 100644 (file)
@@ -146,15 +146,20 @@ int ivtv_reset_tuner_gpio(void *dev, int cmd, int value)
 
 void ivtv_gpio_init(struct ivtv *itv)
 {
-       if (itv->card->gpio_init.direction == 0)
+       u16 pin = 0;
+
+       if (itv->card->xceive_pin)
+               pin = 1 << itv->card->xceive_pin;
+
+       if ((itv->card->gpio_init.direction | pin) == 0)
                return;
 
        IVTV_DEBUG_INFO("GPIO initial dir: %08x out: %08x\n",
                   read_reg(IVTV_REG_GPIO_DIR), read_reg(IVTV_REG_GPIO_OUT));
 
        /* init output data then direction */
-       write_reg(itv->card->gpio_init.initial_value, IVTV_REG_GPIO_OUT);
-       write_reg(itv->card->gpio_init.direction, IVTV_REG_GPIO_DIR);
+       write_reg(itv->card->gpio_init.initial_value | pin, IVTV_REG_GPIO_OUT);
+       write_reg(itv->card->gpio_init.direction | pin, IVTV_REG_GPIO_DIR);
 }
 
 static struct v4l2_queryctrl gpio_ctrl_mute = {
index 32129f3ea836630e3388ba1ade85b92ee0257b68..af154238fb9afdc7817bc8e685923aa18e607950 100644 (file)
 #define IVTV_REG_I2C_GETSCL_OFFSET 0x7008
 #define IVTV_REG_I2C_GETSDA_OFFSET 0x700c
 
-#ifndef I2C_ADAP_CLASS_TV_ANALOG
-#define I2C_ADAP_CLASS_TV_ANALOG I2C_CLASS_TV_ANALOG
-#endif /* I2C_ADAP_CLASS_TV_ANALOG */
-
 #define IVTV_CS53L32A_I2C_ADDR         0x11
 #define IVTV_M52790_I2C_ADDR           0x48
 #define IVTV_CX25840_I2C_ADDR          0x44
@@ -139,7 +135,7 @@ static const u8 hw_addrs[] = {
 static const char * const hw_devicenames[] = {
        "cx25840",
        "saa7115",
-       "saa7127",
+       "saa7127_auto", /* saa7127 or saa7129 */
        "msp3400",
        "tuner",
        "wm8775",
index 26cc0f6699fdc9d44bfcdce386d2533f3cc06227..52e00a7f3110e7835ac2dfea5d674eacc14486b0 100644 (file)
@@ -128,37 +128,6 @@ u16 ivtv_get_service_set(struct v4l2_sliced_vbi_format *fmt)
        return set;
 }
 
-static const struct {
-       v4l2_std_id  std;
-       char        *name;
-} enum_stds[] = {
-       { V4L2_STD_PAL_BG | V4L2_STD_PAL_H, "PAL-BGH" },
-       { V4L2_STD_PAL_DK,    "PAL-DK"    },
-       { V4L2_STD_PAL_I,     "PAL-I"     },
-       { V4L2_STD_PAL_M,     "PAL-M"     },
-       { V4L2_STD_PAL_N,     "PAL-N"     },
-       { V4L2_STD_PAL_Nc,    "PAL-Nc"    },
-       { V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H, "SECAM-BGH" },
-       { V4L2_STD_SECAM_DK,  "SECAM-DK"  },
-       { V4L2_STD_SECAM_L,   "SECAM-L"   },
-       { V4L2_STD_SECAM_LC,  "SECAM-L'"  },
-       { V4L2_STD_NTSC_M,    "NTSC-M"    },
-       { V4L2_STD_NTSC_M_JP, "NTSC-J"    },
-       { V4L2_STD_NTSC_M_KR, "NTSC-K"    },
-};
-
-static const struct v4l2_standard ivtv_std_60hz =
-{
-       .frameperiod = {.numerator = 1001, .denominator = 30000},
-       .framelines = 525,
-};
-
-static const struct v4l2_standard ivtv_std_50hz =
-{
-       .frameperiod = {.numerator = 1, .denominator = 25},
-       .framelines = 625,
-};
-
 void ivtv_set_osd_alpha(struct ivtv *itv)
 {
        ivtv_vapi(itv, CX2341X_OSD_SET_GLOBAL_ALPHA, 3,
@@ -345,6 +314,380 @@ static int ivtv_video_command(struct ivtv *itv, struct ivtv_open_id *id,
        return 0;
 }
 
+static int ivtv_g_fmt_sliced_vbi_out(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+       struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
+       struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
+
+       vbifmt->reserved[0] = 0;
+       vbifmt->reserved[1] = 0;
+       if (!(itv->v4l2_cap & V4L2_CAP_SLICED_VBI_OUTPUT))
+               return -EINVAL;
+       vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
+       if (itv->is_60hz) {
+               vbifmt->service_lines[0][21] = V4L2_SLICED_CAPTION_525;
+               vbifmt->service_lines[1][21] = V4L2_SLICED_CAPTION_525;
+       } else {
+               vbifmt->service_lines[0][23] = V4L2_SLICED_WSS_625;
+               vbifmt->service_lines[0][16] = V4L2_SLICED_VPS;
+       }
+       vbifmt->service_set = ivtv_get_service_set(vbifmt);
+       return 0;
+}
+
+static int ivtv_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+       struct ivtv_open_id *id = fh;
+       struct ivtv *itv = id->itv;
+       struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
+
+       pixfmt->width = itv->params.width;
+       pixfmt->height = itv->params.height;
+       pixfmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
+       pixfmt->field = V4L2_FIELD_INTERLACED;
+       pixfmt->priv = 0;
+       if (id->type == IVTV_ENC_STREAM_TYPE_YUV) {
+               pixfmt->pixelformat = V4L2_PIX_FMT_HM12;
+               /* YUV size is (Y=(h*w) + UV=(h*(w/2))) */
+               pixfmt->sizeimage =
+                       pixfmt->height * pixfmt->width +
+                       pixfmt->height * (pixfmt->width / 2);
+               pixfmt->bytesperline = 720;
+       } else {
+               pixfmt->pixelformat = V4L2_PIX_FMT_MPEG;
+               pixfmt->sizeimage = 128 * 1024;
+               pixfmt->bytesperline = 0;
+       }
+       return 0;
+}
+
+static int ivtv_g_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+       struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
+       struct v4l2_vbi_format *vbifmt = &fmt->fmt.vbi;
+
+       vbifmt->sampling_rate = 27000000;
+       vbifmt->offset = 248;
+       vbifmt->samples_per_line = itv->vbi.raw_decoder_line_size - 4;
+       vbifmt->sample_format = V4L2_PIX_FMT_GREY;
+       vbifmt->start[0] = itv->vbi.start[0];
+       vbifmt->start[1] = itv->vbi.start[1];
+       vbifmt->count[0] = vbifmt->count[1] = itv->vbi.count;
+       vbifmt->flags = 0;
+       vbifmt->reserved[0] = 0;
+       vbifmt->reserved[1] = 0;
+       return 0;
+}
+
+static int ivtv_g_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+       struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
+       struct ivtv_open_id *id = fh;
+       struct ivtv *itv = id->itv;
+
+       vbifmt->reserved[0] = 0;
+       vbifmt->reserved[1] = 0;
+       vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
+
+       if (id->type == IVTV_DEC_STREAM_TYPE_VBI) {
+               vbifmt->service_set = itv->is_50hz ? V4L2_SLICED_VBI_625 :
+                       V4L2_SLICED_VBI_525;
+               ivtv_expand_service_set(vbifmt, itv->is_50hz);
+               return 0;
+       }
+
+       itv->video_dec_func(itv, VIDIOC_G_FMT, fmt);
+       vbifmt->service_set = ivtv_get_service_set(vbifmt);
+       return 0;
+}
+
+static int ivtv_g_fmt_vid_out(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+       struct ivtv_open_id *id = fh;
+       struct ivtv *itv = id->itv;
+       struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
+
+       if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+               return -EINVAL;
+       pixfmt->width = itv->main_rect.width;
+       pixfmt->height = itv->main_rect.height;
+       pixfmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
+       pixfmt->field = V4L2_FIELD_INTERLACED;
+       pixfmt->priv = 0;
+       if (id->type == IVTV_DEC_STREAM_TYPE_YUV) {
+               switch (itv->yuv_info.lace_mode & IVTV_YUV_MODE_MASK) {
+               case IVTV_YUV_MODE_INTERLACED:
+                       pixfmt->field = (itv->yuv_info.lace_mode & IVTV_YUV_SYNC_MASK) ?
+                               V4L2_FIELD_INTERLACED_BT : V4L2_FIELD_INTERLACED_TB;
+                       break;
+               case IVTV_YUV_MODE_PROGRESSIVE:
+                       pixfmt->field = V4L2_FIELD_NONE;
+                       break;
+               default:
+                       pixfmt->field = V4L2_FIELD_ANY;
+                       break;
+               }
+               pixfmt->pixelformat = V4L2_PIX_FMT_HM12;
+               pixfmt->bytesperline = 720;
+               pixfmt->width = itv->yuv_info.v4l2_src_w;
+               pixfmt->height = itv->yuv_info.v4l2_src_h;
+               /* YUV size is (Y=(h*w) + UV=(h*(w/2))) */
+               pixfmt->sizeimage =
+                       1080 * ((pixfmt->height + 31) & ~31);
+       } else {
+               pixfmt->pixelformat = V4L2_PIX_FMT_MPEG;
+               pixfmt->sizeimage = 128 * 1024;
+               pixfmt->bytesperline = 0;
+       }
+       return 0;
+}
+
+static int ivtv_g_fmt_vid_out_overlay(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+       struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
+       struct v4l2_window *winfmt = &fmt->fmt.win;
+
+       if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+               return -EINVAL;
+       winfmt->chromakey = itv->osd_chroma_key;
+       winfmt->global_alpha = itv->osd_global_alpha;
+       winfmt->field = V4L2_FIELD_INTERLACED;
+       winfmt->clips = NULL;
+       winfmt->clipcount = 0;
+       winfmt->bitmap = NULL;
+       winfmt->w.top = winfmt->w.left = 0;
+       winfmt->w.width = itv->osd_rect.width;
+       winfmt->w.height = itv->osd_rect.height;
+       return 0;
+}
+
+static int ivtv_try_fmt_sliced_vbi_out(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+       return ivtv_g_fmt_sliced_vbi_out(file, fh, fmt);
+}
+
+static int ivtv_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+       struct ivtv_open_id *id = fh;
+       struct ivtv *itv = id->itv;
+       int w = fmt->fmt.pix.width;
+       int h = fmt->fmt.pix.height;
+
+       w = min(w, 720);
+       w = max(w, 1);
+       h = min(h, itv->is_50hz ? 576 : 480);
+       h = max(h, 2);
+       ivtv_g_fmt_vid_cap(file, fh, fmt);
+       fmt->fmt.pix.width = w;
+       fmt->fmt.pix.height = h;
+       return 0;
+}
+
+static int ivtv_try_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+       return ivtv_g_fmt_vbi_cap(file, fh, fmt);
+}
+
+static int ivtv_try_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+       struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
+       struct ivtv_open_id *id = fh;
+       struct ivtv *itv = id->itv;
+
+       if (id->type == IVTV_DEC_STREAM_TYPE_VBI)
+               return ivtv_g_fmt_sliced_vbi_cap(file, fh, fmt);
+
+       /* set sliced VBI capture format */
+       vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
+       vbifmt->reserved[0] = 0;
+       vbifmt->reserved[1] = 0;
+
+       if (vbifmt->service_set)
+               ivtv_expand_service_set(vbifmt, itv->is_50hz);
+       check_service_set(vbifmt, itv->is_50hz);
+       vbifmt->service_set = ivtv_get_service_set(vbifmt);
+       return 0;
+}
+
+static int ivtv_try_fmt_vid_out(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+       struct ivtv_open_id *id = fh;
+       s32 w, h;
+       int field;
+       int ret;
+
+       w = fmt->fmt.pix.width;
+       h = fmt->fmt.pix.height;
+       field = fmt->fmt.pix.field;
+       ret = ivtv_g_fmt_vid_out(file, fh, fmt);
+       fmt->fmt.pix.width = w;
+       fmt->fmt.pix.height = h;
+       if (!ret && id->type == IVTV_DEC_STREAM_TYPE_YUV) {
+               fmt->fmt.pix.field = field;
+               if (fmt->fmt.pix.width < 2)
+                       fmt->fmt.pix.width = 2;
+               if (fmt->fmt.pix.width > 720)
+                       fmt->fmt.pix.width = 720;
+               if (fmt->fmt.pix.height < 2)
+                       fmt->fmt.pix.height = 2;
+               if (fmt->fmt.pix.height > 576)
+                       fmt->fmt.pix.height = 576;
+       }
+       return ret;
+}
+
+static int ivtv_try_fmt_vid_out_overlay(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+       struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
+       u32 chromakey = fmt->fmt.win.chromakey;
+       u8 global_alpha = fmt->fmt.win.global_alpha;
+
+       if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+               return -EINVAL;
+       ivtv_g_fmt_vid_out_overlay(file, fh, fmt);
+       fmt->fmt.win.chromakey = chromakey;
+       fmt->fmt.win.global_alpha = global_alpha;
+       return 0;
+}
+
+static int ivtv_s_fmt_sliced_vbi_out(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+       return ivtv_g_fmt_sliced_vbi_out(file, fh, fmt);
+}
+
+static int ivtv_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+       struct ivtv_open_id *id = fh;
+       struct ivtv *itv = id->itv;
+       struct cx2341x_mpeg_params *p = &itv->params;
+       int w = fmt->fmt.pix.width;
+       int h = fmt->fmt.pix.height;
+       int ret = ivtv_try_fmt_vid_cap(file, fh, fmt);
+
+       if (ret)
+               return ret;
+
+       if (p->width == w && p->height == h)
+               return 0;
+
+       if (atomic_read(&itv->capturing) > 0)
+               return -EBUSY;
+
+       p->width = w;
+       p->height = h;
+       if (p->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1)
+               fmt->fmt.pix.width /= 2;
+       itv->video_dec_func(itv, VIDIOC_S_FMT, fmt);
+       return ivtv_g_fmt_vid_cap(file, fh, fmt);
+}
+
+static int ivtv_s_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+       struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
+
+       itv->vbi.sliced_in->service_set = 0;
+       itv->video_dec_func(itv, VIDIOC_S_FMT, &itv->vbi.in);
+       return ivtv_g_fmt_vbi_cap(file, fh, fmt);
+}
+
+static int ivtv_s_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+       struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
+       struct ivtv_open_id *id = fh;
+       struct ivtv *itv = id->itv;
+       int ret = ivtv_try_fmt_sliced_vbi_cap(file, fh, fmt);
+
+       if (ret || id->type == IVTV_DEC_STREAM_TYPE_VBI)
+               return ret;
+
+       if (check_service_set(vbifmt, itv->is_50hz) == 0)
+               return -EINVAL;
+       if (atomic_read(&itv->capturing) > 0)
+               return -EBUSY;
+       itv->video_dec_func(itv, VIDIOC_S_FMT, fmt);
+       memcpy(itv->vbi.sliced_in, vbifmt, sizeof(*itv->vbi.sliced_in));
+       return 0;
+}
+
+static int ivtv_s_fmt_vid_out(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+       struct ivtv_open_id *id = fh;
+       struct ivtv *itv = id->itv;
+       struct yuv_playback_info *yi = &itv->yuv_info;
+       int ret = ivtv_try_fmt_vid_out(file, fh, fmt);
+
+       if (ret)
+               return ret;
+
+       if (id->type != IVTV_DEC_STREAM_TYPE_YUV)
+               return 0;
+
+       /* Return now if we already have some frame data */
+       if (yi->stream_size)
+               return -EBUSY;
+
+       yi->v4l2_src_w = fmt->fmt.pix.width;
+       yi->v4l2_src_h = fmt->fmt.pix.height;
+
+       switch (fmt->fmt.pix.field) {
+       case V4L2_FIELD_NONE:
+               yi->lace_mode = IVTV_YUV_MODE_PROGRESSIVE;
+               break;
+       case V4L2_FIELD_ANY:
+               yi->lace_mode = IVTV_YUV_MODE_AUTO;
+               break;
+       case V4L2_FIELD_INTERLACED_BT:
+               yi->lace_mode =
+                       IVTV_YUV_MODE_INTERLACED|IVTV_YUV_SYNC_ODD;
+               break;
+       case V4L2_FIELD_INTERLACED_TB:
+       default:
+               yi->lace_mode = IVTV_YUV_MODE_INTERLACED;
+               break;
+       }
+       yi->lace_sync_field = (yi->lace_mode & IVTV_YUV_SYNC_MASK) == IVTV_YUV_SYNC_EVEN ? 0 : 1;
+
+       if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags))
+               itv->dma_data_req_size =
+                       1080 * ((yi->v4l2_src_h + 31) & ~31);
+
+       /* Force update of yuv registers */
+       yi->yuv_forced_update = 1;
+       return 0;
+}
+
+static int ivtv_s_fmt_vid_out_overlay(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+       struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
+       int ret = ivtv_try_fmt_vid_out_overlay(file, fh, fmt);
+
+       if (ret == 0) {
+               itv->osd_chroma_key = fmt->fmt.win.chromakey;
+               itv->osd_global_alpha = fmt->fmt.win.global_alpha;
+               ivtv_set_osd_alpha(itv);
+       }
+       return ret;
+}
+
+static int ivtv_g_chip_ident(struct file *file, void *fh, struct v4l2_chip_ident *chip)
+{
+       struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
+
+       chip->ident = V4L2_IDENT_NONE;
+       chip->revision = 0;
+       if (chip->match_type == V4L2_CHIP_MATCH_HOST) {
+               if (v4l2_chip_match_host(chip->match_type, chip->match_chip))
+                       chip->ident = itv->has_cx23415 ? V4L2_IDENT_CX23415 : V4L2_IDENT_CX23416;
+               return 0;
+       }
+       if (chip->match_type == V4L2_CHIP_MATCH_I2C_DRIVER)
+               return ivtv_i2c_id(itv, chip->match_chip, VIDIOC_G_CHIP_IDENT, chip);
+       if (chip->match_type == V4L2_CHIP_MATCH_I2C_ADDR)
+               return ivtv_call_i2c_client(itv, chip->match_chip, VIDIOC_G_CHIP_IDENT, chip);
+       return -EINVAL;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
 static int ivtv_itvc(struct ivtv *itv, unsigned int cmd, void *arg)
 {
        struct v4l2_register *regs = arg;
@@ -364,1054 +707,826 @@ static int ivtv_itvc(struct ivtv *itv, unsigned int cmd, void *arg)
                return -EINVAL;
 
        spin_lock_irqsave(&ivtv_cards_lock, flags);
-       if (cmd == VIDIOC_DBG_G_REGISTER) {
+       if (cmd == VIDIOC_DBG_G_REGISTER)
                regs->val = readl(regs->reg + reg_start);
-       } else {
+       else
                writel(regs->val, regs->reg + reg_start);
-       }
        spin_unlock_irqrestore(&ivtv_cards_lock, flags);
        return 0;
 }
 
-static int ivtv_get_fmt(struct ivtv *itv, int streamtype, struct v4l2_format *fmt)
+static int ivtv_g_register(struct file *file, void *fh, struct v4l2_register *reg)
 {
-       switch (fmt->type) {
-       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
-               if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
-                       return -EINVAL;
-               fmt->fmt.pix.width = itv->main_rect.width;
-               fmt->fmt.pix.height = itv->main_rect.height;
-               fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
-               fmt->fmt.pix.field = V4L2_FIELD_INTERLACED;
-               if (streamtype == IVTV_DEC_STREAM_TYPE_YUV) {
-                       switch (itv->yuv_info.lace_mode & IVTV_YUV_MODE_MASK) {
-                       case IVTV_YUV_MODE_INTERLACED:
-                               fmt->fmt.pix.field = (itv->yuv_info.lace_mode & IVTV_YUV_SYNC_MASK) ?
-                                       V4L2_FIELD_INTERLACED_BT : V4L2_FIELD_INTERLACED_TB;
-                               break;
-                       case IVTV_YUV_MODE_PROGRESSIVE:
-                               fmt->fmt.pix.field = V4L2_FIELD_NONE;
-                               break;
-                       default:
-                               fmt->fmt.pix.field = V4L2_FIELD_ANY;
-                               break;
-                       }
-                       fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_HM12;
-                       fmt->fmt.pix.bytesperline = 720;
-                       fmt->fmt.pix.width = itv->yuv_info.v4l2_src_w;
-                       fmt->fmt.pix.height = itv->yuv_info.v4l2_src_h;
-                       /* YUV size is (Y=(h*w) + UV=(h*(w/2))) */
-                       fmt->fmt.pix.sizeimage =
-                               1080 * ((fmt->fmt.pix.height + 31) & ~31);
-               } else if (streamtype == IVTV_ENC_STREAM_TYPE_YUV) {
-                       fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_HM12;
-                       /* YUV size is (Y=(h*w) + UV=(h*(w/2))) */
-                       fmt->fmt.pix.sizeimage =
-                               fmt->fmt.pix.height * fmt->fmt.pix.width +
-                               fmt->fmt.pix.height * (fmt->fmt.pix.width / 2);
-               } else {
-                       fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
-                       fmt->fmt.pix.sizeimage = 128 * 1024;
-               }
-               break;
+       struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
 
-       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-               fmt->fmt.pix.width = itv->params.width;
-               fmt->fmt.pix.height = itv->params.height;
-               fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
-               fmt->fmt.pix.field = V4L2_FIELD_INTERLACED;
-               if (streamtype == IVTV_ENC_STREAM_TYPE_YUV ||
-                               streamtype == IVTV_DEC_STREAM_TYPE_YUV) {
-                       fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_HM12;
-                       /* YUV size is (Y=(h*w) + UV=(h*(w/2))) */
-                       fmt->fmt.pix.sizeimage =
-                               fmt->fmt.pix.height * fmt->fmt.pix.width +
-                               fmt->fmt.pix.height * (fmt->fmt.pix.width / 2);
-               } else {
-                       fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
-                       fmt->fmt.pix.sizeimage = 128 * 1024;
-               }
-               break;
+       if (v4l2_chip_match_host(reg->match_type, reg->match_chip))
+               return ivtv_itvc(itv, VIDIOC_DBG_G_REGISTER, reg);
+       if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER)
+               return ivtv_i2c_id(itv, reg->match_chip, VIDIOC_DBG_G_REGISTER, reg);
+       return ivtv_call_i2c_client(itv, reg->match_chip, VIDIOC_DBG_G_REGISTER, reg);
+}
 
-       case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
-               if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
-                       return -EINVAL;
-               fmt->fmt.win.chromakey = itv->osd_chroma_key;
-               fmt->fmt.win.global_alpha = itv->osd_global_alpha;
-               break;
+static int ivtv_s_register(struct file *file, void *fh, struct v4l2_register *reg)
+{
+       struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
 
-       case V4L2_BUF_TYPE_VBI_CAPTURE:
-               fmt->fmt.vbi.sampling_rate = 27000000;
-               fmt->fmt.vbi.offset = 248;
-               fmt->fmt.vbi.samples_per_line = itv->vbi.raw_decoder_line_size - 4;
-               fmt->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
-               fmt->fmt.vbi.start[0] = itv->vbi.start[0];
-               fmt->fmt.vbi.start[1] = itv->vbi.start[1];
-               fmt->fmt.vbi.count[0] = fmt->fmt.vbi.count[1] = itv->vbi.count;
-               break;
+       if (v4l2_chip_match_host(reg->match_type, reg->match_chip))
+               return ivtv_itvc(itv, VIDIOC_DBG_S_REGISTER, reg);
+       if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER)
+               return ivtv_i2c_id(itv, reg->match_chip, VIDIOC_DBG_S_REGISTER, reg);
+       return ivtv_call_i2c_client(itv, reg->match_chip, VIDIOC_DBG_S_REGISTER, reg);
+}
+#endif
 
-       case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
-       {
-               struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
+static int ivtv_g_priority(struct file *file, void *fh, enum v4l2_priority *p)
+{
+       struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
 
-               if (!(itv->v4l2_cap & V4L2_CAP_SLICED_VBI_OUTPUT))
-                       return -EINVAL;
-               vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
-               memset(vbifmt->reserved, 0, sizeof(vbifmt->reserved));
-               memset(vbifmt->service_lines, 0, sizeof(vbifmt->service_lines));
-               if (itv->is_60hz) {
-                       vbifmt->service_lines[0][21] = V4L2_SLICED_CAPTION_525;
-                       vbifmt->service_lines[1][21] = V4L2_SLICED_CAPTION_525;
-               } else {
-                       vbifmt->service_lines[0][23] = V4L2_SLICED_WSS_625;
-                       vbifmt->service_lines[0][16] = V4L2_SLICED_VPS;
-               }
-               vbifmt->service_set = ivtv_get_service_set(vbifmt);
-               break;
-       }
+       *p = v4l2_prio_max(&itv->prio);
 
-       case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
-       {
-               struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
+       return 0;
+}
 
-               vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
-               memset(vbifmt->reserved, 0, sizeof(vbifmt->reserved));
-               memset(vbifmt->service_lines, 0, sizeof(vbifmt->service_lines));
+static int ivtv_s_priority(struct file *file, void *fh, enum v4l2_priority prio)
+{
+       struct ivtv_open_id *id = fh;
+       struct ivtv *itv = id->itv;
 
-               if (streamtype == IVTV_DEC_STREAM_TYPE_VBI) {
-                       vbifmt->service_set = itv->is_50hz ? V4L2_SLICED_VBI_625 :
-                                                V4L2_SLICED_VBI_525;
-                       ivtv_expand_service_set(vbifmt, itv->is_50hz);
-                       break;
-               }
+       return v4l2_prio_change(&itv->prio, &id->prio, prio);
+}
 
-               itv->video_dec_func(itv, VIDIOC_G_FMT, fmt);
-               vbifmt->service_set = ivtv_get_service_set(vbifmt);
-               break;
-       }
-       case V4L2_BUF_TYPE_VBI_OUTPUT:
-       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
-       default:
-               return -EINVAL;
-       }
+static int ivtv_querycap(struct file *file, void *fh, struct v4l2_capability *vcap)
+{
+       struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
+
+       strlcpy(vcap->driver, IVTV_DRIVER_NAME, sizeof(vcap->driver));
+       strlcpy(vcap->card, itv->card_name, sizeof(vcap->card));
+       strlcpy(vcap->bus_info, pci_name(itv->dev), sizeof(vcap->bus_info));
+       vcap->version = IVTV_DRIVER_VERSION;        /* version */
+       vcap->capabilities = itv->v4l2_cap;         /* capabilities */
        return 0;
 }
 
-static int ivtv_try_or_set_fmt(struct ivtv *itv, int streamtype,
-               struct v4l2_format *fmt, int set_fmt)
+static int ivtv_enumaudio(struct file *file, void *fh, struct v4l2_audio *vin)
 {
-       struct yuv_playback_info *yi = &itv->yuv_info;
-       struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
-       u16 set;
+       struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
 
-       if (fmt->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
-               struct v4l2_rect r;
-               int field;
+       return ivtv_get_audio_input(itv, vin->index, vin);
+}
 
-               if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
-                       return -EINVAL;
-               field = fmt->fmt.pix.field;
-               r.top = 0;
-               r.left = 0;
-               r.width = fmt->fmt.pix.width;
-               r.height = fmt->fmt.pix.height;
-               ivtv_get_fmt(itv, streamtype, fmt);
-               fmt->fmt.pix.width = r.width;
-               fmt->fmt.pix.height = r.height;
-               if (streamtype == IVTV_DEC_STREAM_TYPE_YUV) {
-                       fmt->fmt.pix.field = field;
-                       if (fmt->fmt.pix.width < 2)
-                               fmt->fmt.pix.width = 2;
-                       if (fmt->fmt.pix.width > 720)
-                               fmt->fmt.pix.width = 720;
-                       if (fmt->fmt.pix.height < 2)
-                               fmt->fmt.pix.height = 2;
-                       if (fmt->fmt.pix.height > 576)
-                               fmt->fmt.pix.height = 576;
-               }
-               if (set_fmt && streamtype == IVTV_DEC_STREAM_TYPE_YUV) {
-                       /* Return now if we already have some frame data */
-                       if (yi->stream_size)
-                               return -EBUSY;
+static int ivtv_g_audio(struct file *file, void *fh, struct v4l2_audio *vin)
+{
+       struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
 
-                       yi->v4l2_src_w = r.width;
-                       yi->v4l2_src_h = r.height;
+       vin->index = itv->audio_input;
+       return ivtv_get_audio_input(itv, vin->index, vin);
+}
 
-                       switch (field) {
-                       case V4L2_FIELD_NONE:
-                               yi->lace_mode = IVTV_YUV_MODE_PROGRESSIVE;
-                               break;
-                       case V4L2_FIELD_ANY:
-                               yi->lace_mode = IVTV_YUV_MODE_AUTO;
-                               break;
-                       case V4L2_FIELD_INTERLACED_BT:
-                               yi->lace_mode =
-                                    IVTV_YUV_MODE_INTERLACED|IVTV_YUV_SYNC_ODD;
-                               break;
-                       case V4L2_FIELD_INTERLACED_TB:
-                       default:
-                               yi->lace_mode = IVTV_YUV_MODE_INTERLACED;
-                               break;
-                       }
-                       yi->lace_sync_field = (yi->lace_mode & IVTV_YUV_SYNC_MASK) == IVTV_YUV_SYNC_EVEN ? 0 : 1;
+static int ivtv_s_audio(struct file *file, void *fh, struct v4l2_audio *vout)
+{
+       struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
 
-                       if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags))
-                               itv->dma_data_req_size =
-                                          1080 * ((yi->v4l2_src_h + 31) & ~31);
+       if (vout->index >= itv->nof_audio_inputs)
+               return -EINVAL;
 
-                       /* Force update of yuv registers */
-                       yi->yuv_forced_update = 1;
-                       return 0;
-               }
-               return 0;
-       }
+       itv->audio_input = vout->index;
+       ivtv_audio_set_io(itv);
 
-       if (fmt->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY) {
-               if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
-                       return -EINVAL;
-               if (set_fmt) {
-                       itv->osd_chroma_key = fmt->fmt.win.chromakey;
-                       itv->osd_global_alpha = fmt->fmt.win.global_alpha;
-                       ivtv_set_osd_alpha(itv);
-               }
-               return 0;
-       }
+       return 0;
+}
 
-       /* set window size */
-       if (fmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
-               struct cx2341x_mpeg_params *p = &itv->params;
-               int w = fmt->fmt.pix.width;
-               int h = fmt->fmt.pix.height;
-
-               if (w > 720) w = 720;
-               else if (w < 1) w = 1;
-               if (h > (itv->is_50hz ? 576 : 480)) h = (itv->is_50hz ? 576 : 480);
-               else if (h < 2) h = 2;
-               ivtv_get_fmt(itv, streamtype, fmt);
-               fmt->fmt.pix.width = w;
-               fmt->fmt.pix.height = h;
-
-               if (!set_fmt || (p->width == w && p->height == h))
-                       return 0;
-               if (atomic_read(&itv->capturing) > 0)
-                       return -EBUSY;
+static int ivtv_enumaudout(struct file *file, void *fh, struct v4l2_audioout *vin)
+{
+       struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
 
-               p->width = w;
-               p->height = h;
-               if (w != 720 || h != (itv->is_50hz ? 576 : 480))
-                       p->video_temporal_filter = 0;
-               else
-                       p->video_temporal_filter = 8;
-               if (p->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1)
-                       fmt->fmt.pix.width /= 2;
-               itv->video_dec_func(itv, VIDIOC_S_FMT, fmt);
-               return ivtv_get_fmt(itv, streamtype, fmt);
-       }
+       /* set it to defaults from our table */
+       return ivtv_get_audio_output(itv, vin->index, vin);
+}
 
-       /* set raw VBI format */
-       if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
-               if (set_fmt && atomic_read(&itv->capturing) > 0) {
-                       return -EBUSY;
-               }
-               if (set_fmt) {
-                       itv->vbi.sliced_in->service_set = 0;
-                       itv->video_dec_func(itv, VIDIOC_S_FMT, &itv->vbi.in);
-               }
-               return ivtv_get_fmt(itv, streamtype, fmt);
-       }
+static int ivtv_g_audout(struct file *file, void *fh, struct v4l2_audioout *vin)
+{
+       struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
+
+       vin->index = 0;
+       return ivtv_get_audio_output(itv, vin->index, vin);
+}
 
-       /* set sliced VBI output
-          In principle the user could request that only certain
-          VBI types are output and that the others are ignored.
-          I.e., suppress CC in the even fields or only output
-          WSS and no VPS. Currently though there is no choice. */
-       if (fmt->type == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT)
-               return ivtv_get_fmt(itv, streamtype, fmt);
+static int ivtv_s_audout(struct file *file, void *fh, struct v4l2_audioout *vout)
+{
+       struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
 
-       /* any else but sliced VBI capture is an error */
-       if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
-               return -EINVAL;
+       return ivtv_get_audio_output(itv, vout->index, vout);
+}
 
-       if (streamtype == IVTV_DEC_STREAM_TYPE_VBI)
-               return ivtv_get_fmt(itv, streamtype, fmt);
+static int ivtv_enum_input(struct file *file, void *fh, struct v4l2_input *vin)
+{
+       struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
 
-       /* set sliced VBI capture format */
-       vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
-       memset(vbifmt->reserved, 0, sizeof(vbifmt->reserved));
+       /* set it to defaults from our table */
+       return ivtv_get_input(itv, vin->index, vin);
+}
 
-       if (vbifmt->service_set)
-               ivtv_expand_service_set(vbifmt, itv->is_50hz);
-       set = check_service_set(vbifmt, itv->is_50hz);
-       vbifmt->service_set = ivtv_get_service_set(vbifmt);
+static int ivtv_enum_output(struct file *file, void *fh, struct v4l2_output *vout)
+{
+       struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
 
-       if (!set_fmt)
-               return 0;
-       if (set == 0)
+       return ivtv_get_output(itv, vout->index, vout);
+}
+
+static int ivtv_cropcap(struct file *file, void *fh, struct v4l2_cropcap *cropcap)
+{
+       struct ivtv_open_id *id = fh;
+       struct ivtv *itv = id->itv;
+       struct yuv_playback_info *yi = &itv->yuv_info;
+       int streamtype;
+
+       streamtype = id->type;
+
+       if (cropcap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
                return -EINVAL;
-       if (atomic_read(&itv->capturing) > 0) {
-               return -EBUSY;
+       cropcap->bounds.top = cropcap->bounds.left = 0;
+       cropcap->bounds.width = 720;
+       if (cropcap->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+               cropcap->bounds.height = itv->is_50hz ? 576 : 480;
+               cropcap->pixelaspect.numerator = itv->is_50hz ? 59 : 10;
+               cropcap->pixelaspect.denominator = itv->is_50hz ? 54 : 11;
+       } else if (streamtype == IVTV_DEC_STREAM_TYPE_YUV) {
+               if (yi->track_osd) {
+                       cropcap->bounds.width = yi->osd_full_w;
+                       cropcap->bounds.height = yi->osd_full_h;
+               } else {
+                       cropcap->bounds.width = 720;
+                       cropcap->bounds.height =
+                                       itv->is_out_50hz ? 576 : 480;
+               }
+               cropcap->pixelaspect.numerator = itv->is_out_50hz ? 59 : 10;
+               cropcap->pixelaspect.denominator = itv->is_out_50hz ? 54 : 11;
+       } else {
+               cropcap->bounds.height = itv->is_out_50hz ? 576 : 480;
+               cropcap->pixelaspect.numerator = itv->is_out_50hz ? 59 : 10;
+               cropcap->pixelaspect.denominator = itv->is_out_50hz ? 54 : 11;
        }
-       itv->video_dec_func(itv, VIDIOC_S_FMT, fmt);
-       memcpy(itv->vbi.sliced_in, vbifmt, sizeof(*itv->vbi.sliced_in));
+       cropcap->defrect = cropcap->bounds;
        return 0;
 }
 
-static int ivtv_debug_ioctls(struct file *filp, unsigned int cmd, void *arg)
+static int ivtv_s_crop(struct file *file, void *fh, struct v4l2_crop *crop)
 {
-       struct ivtv_open_id *id = (struct ivtv_open_id *)filp->private_data;
+       struct ivtv_open_id *id = fh;
        struct ivtv *itv = id->itv;
-       struct v4l2_register *reg = arg;
+       struct yuv_playback_info *yi = &itv->yuv_info;
+       int streamtype;
 
-       switch (cmd) {
-       /* ioctls to allow direct access to the encoder registers for testing */
-       case VIDIOC_DBG_G_REGISTER:
-               if (v4l2_chip_match_host(reg->match_type, reg->match_chip))
-                       return ivtv_itvc(itv, cmd, arg);
-               if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER)
-                       return ivtv_i2c_id(itv, reg->match_chip, cmd, arg);
-               return ivtv_call_i2c_client(itv, reg->match_chip, cmd, arg);
-
-       case VIDIOC_DBG_S_REGISTER:
-               if (v4l2_chip_match_host(reg->match_type, reg->match_chip))
-                       return ivtv_itvc(itv, cmd, arg);
-               if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER)
-                       return ivtv_i2c_id(itv, reg->match_chip, cmd, arg);
-               return ivtv_call_i2c_client(itv, reg->match_chip, cmd, arg);
-
-       case VIDIOC_G_CHIP_IDENT: {
-               struct v4l2_chip_ident *chip = arg;
-
-               chip->ident = V4L2_IDENT_NONE;
-               chip->revision = 0;
-               if (reg->match_type == V4L2_CHIP_MATCH_HOST) {
-                       if (v4l2_chip_match_host(reg->match_type, reg->match_chip))
-                               chip->ident = itv->has_cx23415 ? V4L2_IDENT_CX23415 : V4L2_IDENT_CX23416;
+       streamtype = id->type;
+
+       if (ivtv_debug & IVTV_DBGFLG_IOCTL) {
+               printk(KERN_INFO "ivtv%d ioctl: ", itv->num);
+               /* Should be replaced */
+               /* v4l_printk_ioctl(VIDIOC_S_CROP); */
+       }
+
+       if (crop->type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
+           (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) {
+               if (streamtype == IVTV_DEC_STREAM_TYPE_YUV) {
+                       yi->main_rect = crop->c;
                        return 0;
+               } else {
+                       if (!ivtv_vapi(itv, CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, 4,
+                               crop->c.width, crop->c.height, crop->c.left, crop->c.top)) {
+                               itv->main_rect = crop->c;
+                               return 0;
+                       }
                }
-               if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER)
-                       return ivtv_i2c_id(itv, reg->match_chip, cmd, arg);
-               if (reg->match_type == V4L2_CHIP_MATCH_I2C_ADDR)
-                       return ivtv_call_i2c_client(itv, reg->match_chip, cmd, arg);
                return -EINVAL;
        }
+       return -EINVAL;
+}
 
-       case VIDIOC_INT_S_AUDIO_ROUTING: {
-               struct v4l2_routing *route = arg;
+static int ivtv_g_crop(struct file *file, void *fh, struct v4l2_crop *crop)
+{
+       struct ivtv_open_id *id = fh;
+       struct ivtv *itv = id->itv;
+       struct yuv_playback_info *yi = &itv->yuv_info;
+       int streamtype;
 
-               ivtv_i2c_hw(itv, itv->card->hw_audio, VIDIOC_INT_S_AUDIO_ROUTING, route);
-               break;
-       }
+       streamtype = id->type;
 
-       case VIDIOC_INT_RESET: {
-               u32 val = *(u32 *)arg;
+       if (crop->type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
+           (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) {
+               if (streamtype == IVTV_DEC_STREAM_TYPE_YUV)
+                       crop->c = yi->main_rect;
+               else
+                       crop->c = itv->main_rect;
+               return 0;
+       }
+       return -EINVAL;
+}
 
-               if ((val == 0 && itv->options.newi2c) || (val & 0x01)) {
-                       ivtv_reset_ir_gpio(itv);
+static int ivtv_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt)
+{
+       static struct v4l2_fmtdesc formats[] = {
+               { 0, 0, 0,
+                 "HM12 (YUV 4:2:0)", V4L2_PIX_FMT_HM12,
+                 { 0, 0, 0, 0 }
+               },
+               { 1, 0, V4L2_FMT_FLAG_COMPRESSED,
+                 "MPEG", V4L2_PIX_FMT_MPEG,
+                 { 0, 0, 0, 0 }
                }
-               if (val & 0x02) {
-                       itv->video_dec_func(itv, cmd, NULL);
+       };
+       enum v4l2_buf_type type = fmt->type;
+
+       if (fmt->index > 1)
+               return -EINVAL;
+
+       *fmt = formats[fmt->index];
+       fmt->type = type;
+       return 0;
+}
+
+static int ivtv_enum_fmt_vid_out(struct file *file, void *fh, struct v4l2_fmtdesc *fmt)
+{
+       struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
+
+       static struct v4l2_fmtdesc formats[] = {
+               { 0, 0, 0,
+                 "HM12 (YUV 4:2:0)", V4L2_PIX_FMT_HM12,
+                 { 0, 0, 0, 0 }
+               },
+               { 1, 0, V4L2_FMT_FLAG_COMPRESSED,
+                 "MPEG", V4L2_PIX_FMT_MPEG,
+                 { 0, 0, 0, 0 }
                }
-               break;
-       }
+       };
+       enum v4l2_buf_type type = fmt->type;
 
-       default:
+       if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
                return -EINVAL;
-       }
+
+       if (fmt->index > 1)
+               return -EINVAL;
+
+       *fmt = formats[fmt->index];
+       fmt->type = type;
+
        return 0;
 }
 
-int ivtv_v4l2_ioctls(struct ivtv *itv, struct file *filp, unsigned int cmd, void *arg)
+static int ivtv_g_input(struct file *file, void *fh, unsigned int *i)
 {
-       struct ivtv_open_id *id = NULL;
-       struct yuv_playback_info *yi = &itv->yuv_info;
-       u32 data[CX2341X_MBOX_MAX_DATA];
-       int streamtype = 0;
+       struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
 
-       if (filp) {
-               id = (struct ivtv_open_id *)filp->private_data;
-               streamtype = id->type;
-       }
+       *i = itv->active_input;
 
-       switch (cmd) {
-       case VIDIOC_G_PRIORITY:
-       {
-               enum v4l2_priority *p = arg;
+       return 0;
+}
 
-               *p = v4l2_prio_max(&itv->prio);
-               break;
-       }
+int ivtv_s_input(struct file *file, void *fh, unsigned int inp)
+{
+       struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
 
-       case VIDIOC_S_PRIORITY:
-       {
-               enum v4l2_priority *prio = arg;
+       if (inp < 0 || inp >= itv->nof_inputs)
+               return -EINVAL;
 
-               return v4l2_prio_change(&itv->prio, &id->prio, *prio);
+       if (inp == itv->active_input) {
+               IVTV_DEBUG_INFO("Input unchanged\n");
+               return 0;
        }
 
-       case VIDIOC_QUERYCAP:{
-               struct v4l2_capability *vcap = arg;
+       if (atomic_read(&itv->capturing) > 0) {
+               return -EBUSY;
+       }
 
-               memset(vcap, 0, sizeof(*vcap));
-               strlcpy(vcap->driver, IVTV_DRIVER_NAME, sizeof(vcap->driver));
-               strlcpy(vcap->card, itv->card_name, sizeof(vcap->card));
-               strlcpy(vcap->bus_info, pci_name(itv->dev), sizeof(vcap->bus_info));
-               vcap->version = IVTV_DRIVER_VERSION;        /* version */
-               vcap->capabilities = itv->v4l2_cap;         /* capabilities */
+       IVTV_DEBUG_INFO("Changing input from %d to %d\n",
+                       itv->active_input, inp);
 
-               /* reserved.. must set to 0! */
-               vcap->reserved[0] = vcap->reserved[1] =
-                       vcap->reserved[2] = vcap->reserved[3] = 0;
-               break;
-       }
+       itv->active_input = inp;
+       /* Set the audio input to whatever is appropriate for the
+          input type. */
+       itv->audio_input = itv->card->video_inputs[inp].audio_index;
 
-       case VIDIOC_ENUMAUDIO:{
-               struct v4l2_audio *vin = arg;
+       /* prevent others from messing with the streams until
+          we're finished changing inputs. */
+       ivtv_mute(itv);
+       ivtv_video_set_io(itv);
+       ivtv_audio_set_io(itv);
+       ivtv_unmute(itv);
 
-               return ivtv_get_audio_input(itv, vin->index, vin);
-       }
+       return 0;
+}
 
-       case VIDIOC_G_AUDIO:{
-               struct v4l2_audio *vin = arg;
+static int ivtv_g_output(struct file *file, void *fh, unsigned int *i)
+{
+       struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
 
-               vin->index = itv->audio_input;
-               return ivtv_get_audio_input(itv, vin->index, vin);
-       }
+       if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+               return -EINVAL;
 
-       case VIDIOC_S_AUDIO:{
-               struct v4l2_audio *vout = arg;
+       *i = itv->active_output;
 
-               if (vout->index >= itv->nof_audio_inputs)
-                       return -EINVAL;
-               itv->audio_input = vout->index;
-               ivtv_audio_set_io(itv);
-               break;
-       }
+       return 0;
+}
 
-       case VIDIOC_ENUMAUDOUT:{
-               struct v4l2_audioout *vin = arg;
+static int ivtv_s_output(struct file *file, void *fh, unsigned int outp)
+{
+       struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
+       struct v4l2_routing route;
 
-               /* set it to defaults from our table */
-               return ivtv_get_audio_output(itv, vin->index, vin);
+       if (outp >= itv->card->nof_outputs)
+               return -EINVAL;
+
+       if (outp == itv->active_output) {
+               IVTV_DEBUG_INFO("Output unchanged\n");
+               return 0;
        }
+       IVTV_DEBUG_INFO("Changing output from %d to %d\n",
+                  itv->active_output, outp);
 
-       case VIDIOC_G_AUDOUT:{
-               struct v4l2_audioout *vin = arg;
+       itv->active_output = outp;
+       route.input = SAA7127_INPUT_TYPE_NORMAL;
+       route.output = itv->card->video_outputs[outp].video_output;
+       ivtv_saa7127(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route);
 
-               vin->index = 0;
-               return ivtv_get_audio_output(itv, vin->index, vin);
-       }
+       return 0;
+}
 
-       case VIDIOC_S_AUDOUT:{
-               struct v4l2_audioout *vout = arg;
+static int ivtv_g_frequency(struct file *file, void *fh, struct v4l2_frequency *vf)
+{
+       struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
 
-               return ivtv_get_audio_output(itv, vout->index, vout);
-       }
+       if (vf->tuner != 0)
+               return -EINVAL;
 
-       case VIDIOC_ENUMINPUT:{
-               struct v4l2_input *vin = arg;
+       ivtv_call_i2c_clients(itv, VIDIOC_G_FREQUENCY, vf);
+       return 0;
+}
 
-               /* set it to defaults from our table */
-               return ivtv_get_input(itv, vin->index, vin);
-       }
+int ivtv_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf)
+{
+       struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
 
-       case VIDIOC_ENUMOUTPUT:{
-               struct v4l2_output *vout = arg;
+       if (vf->tuner != 0)
+               return -EINVAL;
 
-               return ivtv_get_output(itv, vout->index, vout);
-       }
+       ivtv_mute(itv);
+       IVTV_DEBUG_INFO("v4l2 ioctl: set frequency %d\n", vf->frequency);
+       ivtv_call_i2c_clients(itv, VIDIOC_S_FREQUENCY, vf);
+       ivtv_unmute(itv);
+       return 0;
+}
 
-       case VIDIOC_TRY_FMT:
-       case VIDIOC_S_FMT: {
-               struct v4l2_format *fmt = arg;
+static int ivtv_g_std(struct file *file, void *fh, v4l2_std_id *std)
+{
+       struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
 
-               return ivtv_try_or_set_fmt(itv, id->type, fmt, cmd == VIDIOC_S_FMT);
-       }
+       *std = itv->std;
+       return 0;
+}
 
-       case VIDIOC_G_FMT: {
-               struct v4l2_format *fmt = arg;
-               int type = fmt->type;
+int ivtv_s_std(struct file *file, void *fh, v4l2_std_id *std)
+{
+       struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
+       struct yuv_playback_info *yi = &itv->yuv_info;
 
-               memset(fmt, 0, sizeof(*fmt));
-               fmt->type = type;
-               return ivtv_get_fmt(itv, id->type, fmt);
-       }
+       if ((*std & V4L2_STD_ALL) == 0)
+               return -EINVAL;
 
-       case VIDIOC_CROPCAP: {
-               struct v4l2_cropcap *cropcap = arg;
+       if (*std == itv->std)
+               return 0;
 
-               if (cropcap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
-                       return -EINVAL;
-               cropcap->bounds.top = cropcap->bounds.left = 0;
-               cropcap->bounds.width = 720;
-               if (cropcap->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
-                       cropcap->bounds.height = itv->is_50hz ? 576 : 480;
-                       cropcap->pixelaspect.numerator = itv->is_50hz ? 59 : 10;
-                       cropcap->pixelaspect.denominator = itv->is_50hz ? 54 : 11;
-               } else if (streamtype == IVTV_DEC_STREAM_TYPE_YUV) {
-                       if (yi->track_osd) {
-                               cropcap->bounds.width = yi->osd_full_w;
-                               cropcap->bounds.height = yi->osd_full_h;
-                       } else {
-                               cropcap->bounds.width = 720;
-                               cropcap->bounds.height =
-                                               itv->is_out_50hz ? 576 : 480;
-                       }
-                       cropcap->pixelaspect.numerator = itv->is_out_50hz ? 59 : 10;
-                       cropcap->pixelaspect.denominator = itv->is_out_50hz ? 54 : 11;
-               } else {
-                       cropcap->bounds.height = itv->is_out_50hz ? 576 : 480;
-                       cropcap->pixelaspect.numerator = itv->is_out_50hz ? 59 : 10;
-                       cropcap->pixelaspect.denominator = itv->is_out_50hz ? 54 : 11;
+       if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags) ||
+           atomic_read(&itv->capturing) > 0 ||
+           atomic_read(&itv->decoding) > 0) {
+               /* Switching standard would turn off the radio or mess
+                  with already running streams, prevent that by
+                  returning EBUSY. */
+               return -EBUSY;
+       }
+
+       itv->std = *std;
+       itv->is_60hz = (*std & V4L2_STD_525_60) ? 1 : 0;
+       itv->params.is_50hz = itv->is_50hz = !itv->is_60hz;
+       itv->params.width = 720;
+       itv->params.height = itv->is_50hz ? 576 : 480;
+       itv->vbi.count = itv->is_50hz ? 18 : 12;
+       itv->vbi.start[0] = itv->is_50hz ? 6 : 10;
+       itv->vbi.start[1] = itv->is_50hz ? 318 : 273;
+
+       if (itv->hw_flags & IVTV_HW_CX25840)
+               itv->vbi.sliced_decoder_line_size = itv->is_60hz ? 272 : 284;
+
+       IVTV_DEBUG_INFO("Switching standard to %llx.\n", (unsigned long long)itv->std);
+
+       /* Tuner */
+       ivtv_call_i2c_clients(itv, VIDIOC_S_STD, &itv->std);
+
+       if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) {
+               /* set display standard */
+               itv->std_out = *std;
+               itv->is_out_60hz = itv->is_60hz;
+               itv->is_out_50hz = itv->is_50hz;
+               ivtv_call_i2c_clients(itv, VIDIOC_INT_S_STD_OUTPUT, &itv->std_out);
+               ivtv_vapi(itv, CX2341X_DEC_SET_STANDARD, 1, itv->is_out_50hz);
+               itv->main_rect.left = itv->main_rect.top = 0;
+               itv->main_rect.width = 720;
+               itv->main_rect.height = itv->params.height;
+               ivtv_vapi(itv, CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, 4,
+                       720, itv->main_rect.height, 0, 0);
+               yi->main_rect = itv->main_rect;
+               if (!itv->osd_info) {
+                       yi->osd_full_w = 720;
+                       yi->osd_full_h = itv->is_out_50hz ? 576 : 480;
                }
-               cropcap->defrect = cropcap->bounds;
-               return 0;
        }
+       return 0;
+}
 
-       case VIDIOC_S_CROP: {
-               struct v4l2_crop *crop = arg;
+static int ivtv_s_tuner(struct file *file, void *fh, struct v4l2_tuner *vt)
+{
+       struct ivtv_open_id *id = fh;
+       struct ivtv *itv = id->itv;
 
-               if (crop->type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
-                   (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) {
-                       if (streamtype == IVTV_DEC_STREAM_TYPE_YUV) {
-                               yi->main_rect = crop->c;
-                               return 0;
-                       } else {
-                               if (!ivtv_vapi(itv, CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, 4,
-                                       crop->c.width, crop->c.height, crop->c.left, crop->c.top)) {
-                                       itv->main_rect = crop->c;
-                                       return 0;
-                               }
-                       }
-                       return -EINVAL;
-               }
+       if (vt->index != 0)
                return -EINVAL;
-       }
 
-       case VIDIOC_G_CROP: {
-               struct v4l2_crop *crop = arg;
+       ivtv_call_i2c_clients(itv, VIDIOC_S_TUNER, vt);
 
-               if (crop->type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
-                   (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) {
-                       if (streamtype == IVTV_DEC_STREAM_TYPE_YUV)
-                               crop->c = yi->main_rect;
-                       else
-                               crop->c = itv->main_rect;
-                       return 0;
-               }
+       return 0;
+}
+
+static int ivtv_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt)
+{
+       struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
+
+       if (vt->index != 0)
                return -EINVAL;
+
+       ivtv_call_i2c_clients(itv, VIDIOC_G_TUNER, vt);
+
+       if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags)) {
+               strlcpy(vt->name, "ivtv Radio Tuner", sizeof(vt->name));
+               vt->type = V4L2_TUNER_RADIO;
+       } else {
+               strlcpy(vt->name, "ivtv TV Tuner", sizeof(vt->name));
+               vt->type = V4L2_TUNER_ANALOG_TV;
        }
 
-       case VIDIOC_ENUM_FMT: {
-               static struct v4l2_fmtdesc formats[] = {
-                       { 0, 0, 0,
-                         "HM12 (YUV 4:2:0)", V4L2_PIX_FMT_HM12,
-                         { 0, 0, 0, 0 }
-                       },
-                       { 1, 0, V4L2_FMT_FLAG_COMPRESSED,
-                         "MPEG", V4L2_PIX_FMT_MPEG,
-                         { 0, 0, 0, 0 }
-                       }
-               };
-               struct v4l2_fmtdesc *fmt = arg;
-               enum v4l2_buf_type type = fmt->type;
+       return 0;
+}
 
-               switch (type) {
-               case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-                       break;
-               case V4L2_BUF_TYPE_VIDEO_OUTPUT:
-                       if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
-                               return -EINVAL;
-                       break;
-               default:
+static int ivtv_g_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_sliced_vbi_cap *cap)
+{
+       struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
+       int set = itv->is_50hz ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525;
+       int f, l;
+
+       if (cap->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) {
+               for (f = 0; f < 2; f++) {
+                       for (l = 0; l < 24; l++) {
+                               if (valid_service_line(f, l, itv->is_50hz))
+                                       cap->service_lines[f][l] = set;
+                       }
+               }
+               return 0;
+       }
+       if (cap->type == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) {
+               if (!(itv->v4l2_cap & V4L2_CAP_SLICED_VBI_OUTPUT))
                        return -EINVAL;
+               if (itv->is_60hz) {
+                       cap->service_lines[0][21] = V4L2_SLICED_CAPTION_525;
+                       cap->service_lines[1][21] = V4L2_SLICED_CAPTION_525;
+               } else {
+                       cap->service_lines[0][23] = V4L2_SLICED_WSS_625;
+                       cap->service_lines[0][16] = V4L2_SLICED_VPS;
                }
-               if (fmt->index > 1)
-                       return -EINVAL;
-               *fmt = formats[fmt->index];
-               fmt->type = type;
                return 0;
        }
+       return -EINVAL;
+}
 
-       case VIDIOC_G_INPUT:{
-               *(int *)arg = itv->active_input;
-               break;
-       }
-
-       case VIDIOC_S_INPUT:{
-               int inp = *(int *)arg;
-
-               if (inp < 0 || inp >= itv->nof_inputs)
-                       return -EINVAL;
+static int ivtv_g_enc_index(struct file *file, void *fh, struct v4l2_enc_idx *idx)
+{
+       struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
+       struct v4l2_enc_idx_entry *e = idx->entry;
+       int entries;
+       int i;
 
-               if (inp == itv->active_input) {
-                       IVTV_DEBUG_INFO("Input unchanged\n");
-                       break;
-               }
-               if (atomic_read(&itv->capturing) > 0) {
-                       return -EBUSY;
+       entries = (itv->pgm_info_write_idx + IVTV_MAX_PGM_INDEX - itv->pgm_info_read_idx) %
+                               IVTV_MAX_PGM_INDEX;
+       if (entries > V4L2_ENC_IDX_ENTRIES)
+               entries = V4L2_ENC_IDX_ENTRIES;
+       idx->entries = 0;
+       for (i = 0; i < entries; i++) {
+               *e = itv->pgm_info[(itv->pgm_info_read_idx + i) % IVTV_MAX_PGM_INDEX];
+               if ((e->flags & V4L2_ENC_IDX_FRAME_MASK) <= V4L2_ENC_IDX_FRAME_B) {
+                       idx->entries++;
+                       e++;
                }
-               IVTV_DEBUG_INFO("Changing input from %d to %d\n",
-                               itv->active_input, inp);
-
-               itv->active_input = inp;
-               /* Set the audio input to whatever is appropriate for the
-                  input type. */
-               itv->audio_input = itv->card->video_inputs[inp].audio_index;
-
-               /* prevent others from messing with the streams until
-                  we're finished changing inputs. */
-               ivtv_mute(itv);
-               ivtv_video_set_io(itv);
-               ivtv_audio_set_io(itv);
-               ivtv_unmute(itv);
-               break;
        }
+       itv->pgm_info_read_idx = (itv->pgm_info_read_idx + idx->entries) % IVTV_MAX_PGM_INDEX;
+       return 0;
+}
 
-       case VIDIOC_G_OUTPUT:{
-               if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
-                       return -EINVAL;
-               *(int *)arg = itv->active_output;
-               break;
-       }
+static int ivtv_encoder_cmd(struct file *file, void *fh, struct v4l2_encoder_cmd *enc)
+{
+       struct ivtv_open_id *id = fh;
+       struct ivtv *itv = id->itv;
 
-       case VIDIOC_S_OUTPUT:{
-               int outp = *(int *)arg;
-               struct v4l2_routing route;
 
-               if (outp >= itv->card->nof_outputs)
-                       return -EINVAL;
+       switch (enc->cmd) {
+       case V4L2_ENC_CMD_START:
+               IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_START\n");
+               enc->flags = 0;
+               return ivtv_start_capture(id);
 
-               if (outp == itv->active_output) {
-                       IVTV_DEBUG_INFO("Output unchanged\n");
-                       break;
-               }
-               IVTV_DEBUG_INFO("Changing output from %d to %d\n",
-                          itv->active_output, outp);
+       case V4L2_ENC_CMD_STOP:
+               IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_STOP\n");
+               enc->flags &= V4L2_ENC_CMD_STOP_AT_GOP_END;
+               ivtv_stop_capture(id, enc->flags & V4L2_ENC_CMD_STOP_AT_GOP_END);
+               return 0;
 
-               itv->active_output = outp;
-               route.input = SAA7127_INPUT_TYPE_NORMAL;
-               route.output = itv->card->video_outputs[outp].video_output;
-               ivtv_saa7127(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route);
-               break;
-       }
+       case V4L2_ENC_CMD_PAUSE:
+               IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_PAUSE\n");
+               enc->flags = 0;
 
-       case VIDIOC_G_FREQUENCY:{
-               struct v4l2_frequency *vf = arg;
+               if (!atomic_read(&itv->capturing))
+                       return -EPERM;
+               if (test_and_set_bit(IVTV_F_I_ENC_PAUSED, &itv->i_flags))
+                       return 0;
 
-               if (vf->tuner != 0)
-                       return -EINVAL;
-               ivtv_call_i2c_clients(itv, cmd, arg);
+               ivtv_mute(itv);
+               ivtv_vapi(itv, CX2341X_ENC_PAUSE_ENCODER, 1, 0);
                break;
-       }
 
-       case VIDIOC_S_FREQUENCY:{
-               struct v4l2_frequency vf = *(struct v4l2_frequency *)arg;
+       case V4L2_ENC_CMD_RESUME:
+               IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_RESUME\n");
+               enc->flags = 0;
 
-               if (vf.tuner != 0)
-                       return -EINVAL;
+               if (!atomic_read(&itv->capturing))
+                       return -EPERM;
 
-               ivtv_mute(itv);
-               IVTV_DEBUG_INFO("v4l2 ioctl: set frequency %d\n", vf.frequency);
-               ivtv_call_i2c_clients(itv, cmd, &vf);
+               if (!test_and_clear_bit(IVTV_F_I_ENC_PAUSED, &itv->i_flags))
+                       return 0;
+
+               ivtv_vapi(itv, CX2341X_ENC_PAUSE_ENCODER, 1, 1);
                ivtv_unmute(itv);
                break;
+       default:
+               IVTV_DEBUG_IOCTL("Unknown cmd %d\n", enc->cmd);
+               return -EINVAL;
        }
 
-       case VIDIOC_ENUMSTD:{
-               struct v4l2_standard *vs = arg;
-               int idx = vs->index;
-
-               if (idx < 0 || idx >= ARRAY_SIZE(enum_stds))
-                       return -EINVAL;
+       return 0;
+}
 
-               *vs = (enum_stds[idx].std & V4L2_STD_525_60) ?
-                               ivtv_std_60hz : ivtv_std_50hz;
-               vs->index = idx;
-               vs->id = enum_stds[idx].std;
-               strlcpy(vs->name, enum_stds[idx].name, sizeof(vs->name));
-               break;
-       }
+static int ivtv_try_encoder_cmd(struct file *file, void *fh, struct v4l2_encoder_cmd *enc)
+{
+       struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
 
-       case VIDIOC_G_STD:{
-               *(v4l2_std_id *) arg = itv->std;
-               break;
-       }
+       switch (enc->cmd) {
+       case V4L2_ENC_CMD_START:
+               IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_START\n");
+               enc->flags = 0;
+               return 0;
 
-       case VIDIOC_S_STD: {
-               v4l2_std_id std = *(v4l2_std_id *) arg;
+       case V4L2_ENC_CMD_STOP:
+               IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_STOP\n");
+               enc->flags &= V4L2_ENC_CMD_STOP_AT_GOP_END;
+               return 0;
 
-               if ((std & V4L2_STD_ALL) == 0)
-                       return -EINVAL;
+       case V4L2_ENC_CMD_PAUSE:
+               IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_PAUSE\n");
+               enc->flags = 0;
+               return 0;
 
-               if (std == itv->std)
-                       break;
+       case V4L2_ENC_CMD_RESUME:
+               IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_RESUME\n");
+               enc->flags = 0;
+               return 0;
+       default:
+               IVTV_DEBUG_IOCTL("Unknown cmd %d\n", enc->cmd);
+               return -EINVAL;
+       }
+}
 
-               if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags) ||
-                   atomic_read(&itv->capturing) > 0 ||
-                   atomic_read(&itv->decoding) > 0) {
-                       /* Switching standard would turn off the radio or mess
-                          with already running streams, prevent that by
-                          returning EBUSY. */
-                       return -EBUSY;
-               }
+static int ivtv_g_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *fb)
+{
+       struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
+       u32 data[CX2341X_MBOX_MAX_DATA];
+       struct yuv_playback_info *yi = &itv->yuv_info;
 
-               itv->std = std;
-               itv->is_60hz = (std & V4L2_STD_525_60) ? 1 : 0;
-               itv->params.is_50hz = itv->is_50hz = !itv->is_60hz;
-               itv->params.width = 720;
-               itv->params.height = itv->is_50hz ? 576 : 480;
-               itv->vbi.count = itv->is_50hz ? 18 : 12;
-               itv->vbi.start[0] = itv->is_50hz ? 6 : 10;
-               itv->vbi.start[1] = itv->is_50hz ? 318 : 273;
-               if (itv->hw_flags & IVTV_HW_CX25840) {
-                       itv->vbi.sliced_decoder_line_size = itv->is_60hz ? 272 : 284;
-               }
-               IVTV_DEBUG_INFO("Switching standard to %llx.\n", (unsigned long long)itv->std);
-
-               /* Tuner */
-               ivtv_call_i2c_clients(itv, VIDIOC_S_STD, &itv->std);
-
-               if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) {
-                       /* set display standard */
-                       itv->std_out = std;
-                       itv->is_out_60hz = itv->is_60hz;
-                       itv->is_out_50hz = itv->is_50hz;
-                       ivtv_call_i2c_clients(itv, VIDIOC_INT_S_STD_OUTPUT, &itv->std_out);
-                       ivtv_vapi(itv, CX2341X_DEC_SET_STANDARD, 1, itv->is_out_50hz);
-                       itv->main_rect.left = itv->main_rect.top = 0;
-                       itv->main_rect.width = 720;
-                       itv->main_rect.height = itv->params.height;
-                       ivtv_vapi(itv, CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, 4,
-                               720, itv->main_rect.height, 0, 0);
-                       yi->main_rect = itv->main_rect;
-                       if (!itv->osd_info) {
-                               yi->osd_full_w = 720;
-                               yi->osd_full_h = itv->is_out_50hz ? 576 : 480;
-                       }
-               }
-               break;
-       }
+       int pixfmt;
+       static u32 pixel_format[16] = {
+               V4L2_PIX_FMT_PAL8, /* Uses a 256-entry RGB colormap */
+               V4L2_PIX_FMT_RGB565,
+               V4L2_PIX_FMT_RGB555,
+               V4L2_PIX_FMT_RGB444,
+               V4L2_PIX_FMT_RGB32,
+               0,
+               0,
+               0,
+               V4L2_PIX_FMT_PAL8, /* Uses a 256-entry YUV colormap */
+               V4L2_PIX_FMT_YUV565,
+               V4L2_PIX_FMT_YUV555,
+               V4L2_PIX_FMT_YUV444,
+               V4L2_PIX_FMT_YUV32,
+               0,
+               0,
+               0,
+       };
+
+       if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT_OVERLAY))
+               return -EINVAL;
+       if (!itv->osd_video_pbase)
+               return -EINVAL;
 
-       case VIDIOC_S_TUNER: {  /* Setting tuner can only set audio mode */
-               struct v4l2_tuner *vt = arg;
+       fb->capability = V4L2_FBUF_CAP_EXTERNOVERLAY | V4L2_FBUF_CAP_CHROMAKEY |
+               V4L2_FBUF_CAP_GLOBAL_ALPHA;
+
+       ivtv_vapi_result(itv, data, CX2341X_OSD_GET_STATE, 0);
+       data[0] |= (read_reg(0x2a00) >> 7) & 0x40;
+       pixfmt = (data[0] >> 3) & 0xf;
+
+       fb->fmt.pixelformat = pixel_format[pixfmt];
+       fb->fmt.width = itv->osd_rect.width;
+       fb->fmt.height = itv->osd_rect.height;
+       fb->fmt.field = V4L2_FIELD_INTERLACED;
+       fb->fmt.bytesperline = fb->fmt.width;
+       fb->fmt.colorspace = V4L2_COLORSPACE_SMPTE170M;
+       fb->fmt.field = V4L2_FIELD_INTERLACED;
+       fb->fmt.priv = 0;
+       if (fb->fmt.pixelformat != V4L2_PIX_FMT_PAL8)
+               fb->fmt.bytesperline *= 2;
+       if (fb->fmt.pixelformat == V4L2_PIX_FMT_RGB32 ||
+           fb->fmt.pixelformat == V4L2_PIX_FMT_YUV32)
+               fb->fmt.bytesperline *= 2;
+       fb->fmt.sizeimage = fb->fmt.bytesperline * fb->fmt.height;
+       fb->base = (void *)itv->osd_video_pbase;
+       fb->flags = 0;
+
+       if (itv->osd_chroma_key_state)
+               fb->flags |= V4L2_FBUF_FLAG_CHROMAKEY;
+
+       if (itv->osd_global_alpha_state)
+               fb->flags |= V4L2_FBUF_FLAG_GLOBAL_ALPHA;
+
+       pixfmt &= 7;
+
+       /* no local alpha for RGB565 or unknown formats */
+       if (pixfmt == 1 || pixfmt > 4)
+               return 0;
 
-               if (vt->index != 0)
-                       return -EINVAL;
+       /* 16-bit formats have inverted local alpha */
+       if (pixfmt == 2 || pixfmt == 3)
+               fb->capability |= V4L2_FBUF_CAP_LOCAL_INV_ALPHA;
+       else
+               fb->capability |= V4L2_FBUF_CAP_LOCAL_ALPHA;
 
-               ivtv_call_i2c_clients(itv, VIDIOC_S_TUNER, vt);
-               break;
+       if (itv->osd_local_alpha_state) {
+               /* 16-bit formats have inverted local alpha */
+               if (pixfmt == 2 || pixfmt == 3)
+                       fb->flags |= V4L2_FBUF_FLAG_LOCAL_INV_ALPHA;
+               else
+                       fb->flags |= V4L2_FBUF_FLAG_LOCAL_ALPHA;
        }
+       if (yi->track_osd)
+               fb->flags |= V4L2_FBUF_FLAG_OVERLAY;
 
-       case VIDIOC_G_TUNER: {
-               struct v4l2_tuner *vt = arg;
+       return 0;
+}
 
-               if (vt->index != 0)
-                       return -EINVAL;
+static int ivtv_s_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *fb)
+{
+       struct ivtv_open_id *id = fh;
+       struct ivtv *itv = id->itv;
+       struct yuv_playback_info *yi = &itv->yuv_info;
 
-               memset(vt, 0, sizeof(*vt));
-               ivtv_call_i2c_clients(itv, VIDIOC_G_TUNER, vt);
+       if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT_OVERLAY))
+               return -EINVAL;
+       if (!itv->osd_video_pbase)
+               return -EINVAL;
 
-               if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags)) {
-                       strlcpy(vt->name, "ivtv Radio Tuner", sizeof(vt->name));
-                       vt->type = V4L2_TUNER_RADIO;
-               } else {
-                       strlcpy(vt->name, "ivtv TV Tuner", sizeof(vt->name));
-                       vt->type = V4L2_TUNER_ANALOG_TV;
-               }
-               break;
-       }
+       itv->osd_global_alpha_state = (fb->flags & V4L2_FBUF_FLAG_GLOBAL_ALPHA) != 0;
+       itv->osd_local_alpha_state =
+               (fb->flags & (V4L2_FBUF_FLAG_LOCAL_ALPHA|V4L2_FBUF_FLAG_LOCAL_INV_ALPHA)) != 0;
+       itv->osd_chroma_key_state = (fb->flags & V4L2_FBUF_FLAG_CHROMAKEY) != 0;
+       ivtv_set_osd_alpha(itv);
+       yi->track_osd = (fb->flags & V4L2_FBUF_FLAG_OVERLAY) != 0;
+       return ivtv_g_fbuf(file, fh, fb);
+}
 
-       case VIDIOC_G_SLICED_VBI_CAP: {
-               struct v4l2_sliced_vbi_cap *cap = arg;
-               int set = itv->is_50hz ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525;
-               int f, l;
-               enum v4l2_buf_type type = cap->type;
-
-               memset(cap, 0, sizeof(*cap));
-               cap->type = type;
-               if (type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) {
-                       for (f = 0; f < 2; f++) {
-                               for (l = 0; l < 24; l++) {
-                                       if (valid_service_line(f, l, itv->is_50hz)) {
-                                               cap->service_lines[f][l] = set;
-                                       }
-                               }
-                       }
-                       return 0;
-               }
-               if (type == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) {
-                       if (!(itv->v4l2_cap & V4L2_CAP_SLICED_VBI_OUTPUT))
-                               return -EINVAL;
-                       if (itv->is_60hz) {
-                               cap->service_lines[0][21] = V4L2_SLICED_CAPTION_525;
-                               cap->service_lines[1][21] = V4L2_SLICED_CAPTION_525;
-                       } else {
-                               cap->service_lines[0][23] = V4L2_SLICED_WSS_625;
-                               cap->service_lines[0][16] = V4L2_SLICED_VPS;
-                       }
-                       return 0;
-               }
-               return -EINVAL;
-       }
+static int ivtv_overlay(struct file *file, void *fh, unsigned int on)
+{
+       struct ivtv_open_id *id = fh;
+       struct ivtv *itv = id->itv;
 
-       case VIDIOC_G_ENC_INDEX: {
-               struct v4l2_enc_idx *idx = arg;
-               struct v4l2_enc_idx_entry *e = idx->entry;
-               int entries;
-               int i;
-
-               entries = (itv->pgm_info_write_idx + IVTV_MAX_PGM_INDEX - itv->pgm_info_read_idx) %
-                                       IVTV_MAX_PGM_INDEX;
-               if (entries > V4L2_ENC_IDX_ENTRIES)
-                       entries = V4L2_ENC_IDX_ENTRIES;
-               idx->entries = 0;
-               for (i = 0; i < entries; i++) {
-                       *e = itv->pgm_info[(itv->pgm_info_read_idx + i) % IVTV_MAX_PGM_INDEX];
-                       if ((e->flags & V4L2_ENC_IDX_FRAME_MASK) <= V4L2_ENC_IDX_FRAME_B) {
-                               idx->entries++;
-                               e++;
-                       }
-               }
-               itv->pgm_info_read_idx = (itv->pgm_info_read_idx + idx->entries) % IVTV_MAX_PGM_INDEX;
-               break;
-       }
+       if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT_OVERLAY))
+               return -EINVAL;
 
-       case VIDIOC_ENCODER_CMD:
-       case VIDIOC_TRY_ENCODER_CMD: {
-               struct v4l2_encoder_cmd *enc = arg;
-               int try = cmd == VIDIOC_TRY_ENCODER_CMD;
-
-               memset(&enc->raw, 0, sizeof(enc->raw));
-               switch (enc->cmd) {
-               case V4L2_ENC_CMD_START:
-                       IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_START\n");
-                       enc->flags = 0;
-                       if (try)
-                               return 0;
-                       return ivtv_start_capture(id);
+       ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, on != 0);
 
-               case V4L2_ENC_CMD_STOP:
-                       IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_STOP\n");
-                       enc->flags &= V4L2_ENC_CMD_STOP_AT_GOP_END;
-                       if (try)
-                               return 0;
-                       ivtv_stop_capture(id, enc->flags & V4L2_ENC_CMD_STOP_AT_GOP_END);
-                       return 0;
+       return 0;
+}
 
-               case V4L2_ENC_CMD_PAUSE:
-                       IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_PAUSE\n");
-                       enc->flags = 0;
-                       if (try)
-                               return 0;
-                       if (!atomic_read(&itv->capturing))
-                               return -EPERM;
-                       if (test_and_set_bit(IVTV_F_I_ENC_PAUSED, &itv->i_flags))
-                               return 0;
-                       ivtv_mute(itv);
-                       ivtv_vapi(itv, CX2341X_ENC_PAUSE_ENCODER, 1, 0);
-                       break;
+static int ivtv_log_status(struct file *file, void *fh)
+{
+       struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
+       u32 data[CX2341X_MBOX_MAX_DATA];
 
-               case V4L2_ENC_CMD_RESUME:
-                       IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_RESUME\n");
-                       enc->flags = 0;
-                       if (try)
-                               return 0;
-                       if (!atomic_read(&itv->capturing))
-                               return -EPERM;
-                       if (!test_and_clear_bit(IVTV_F_I_ENC_PAUSED, &itv->i_flags))
-                               return 0;
-                       ivtv_vapi(itv, CX2341X_ENC_PAUSE_ENCODER, 1, 1);
-                       ivtv_unmute(itv);
-                       break;
-               default:
-                       IVTV_DEBUG_IOCTL("Unknown cmd %d\n", enc->cmd);
-                       return -EINVAL;
-               }
-               break;
-       }
+       int has_output = itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT;
+       struct v4l2_input vidin;
+       struct v4l2_audio audin;
+       int i;
 
-       case VIDIOC_G_FBUF: {
-               struct v4l2_framebuffer *fb = arg;
-               int pixfmt;
-               static u32 pixel_format[16] = {
-                       V4L2_PIX_FMT_PAL8, /* Uses a 256-entry RGB colormap */
-                       V4L2_PIX_FMT_RGB565,
-                       V4L2_PIX_FMT_RGB555,
-                       V4L2_PIX_FMT_RGB444,
-                       V4L2_PIX_FMT_RGB32,
-                       0,
-                       0,
-                       0,
-                       V4L2_PIX_FMT_PAL8, /* Uses a 256-entry YUV colormap */
-                       V4L2_PIX_FMT_YUV565,
-                       V4L2_PIX_FMT_YUV555,
-                       V4L2_PIX_FMT_YUV444,
-                       V4L2_PIX_FMT_YUV32,
-                       0,
-                       0,
-                       0,
+       IVTV_INFO("=================  START STATUS CARD #%d  =================\n", itv->num);
+       IVTV_INFO("Version: %s Card: %s\n", IVTV_VERSION, itv->card_name);
+       if (itv->hw_flags & IVTV_HW_TVEEPROM) {
+               struct tveeprom tv;
+
+               ivtv_read_eeprom(itv, &tv);
+       }
+       ivtv_call_i2c_clients(itv, VIDIOC_LOG_STATUS, NULL);
+       ivtv_get_input(itv, itv->active_input, &vidin);
+       ivtv_get_audio_input(itv, itv->audio_input, &audin);
+       IVTV_INFO("Video Input:  %s\n", vidin.name);
+       IVTV_INFO("Audio Input:  %s%s\n", audin.name,
+               (itv->dualwatch_stereo_mode & ~0x300) == 0x200 ? " (Bilingual)" : "");
+       if (has_output) {
+               struct v4l2_output vidout;
+               struct v4l2_audioout audout;
+               int mode = itv->output_mode;
+               static const char * const output_modes[5] = {
+                       "None",
+                       "MPEG Streaming",
+                       "YUV Streaming",
+                       "YUV Frames",
+                       "Passthrough",
+               };
+               static const char * const audio_modes[5] = {
+                       "Stereo",
+                       "Left",
+                       "Right",
+                       "Mono",
+                       "Swapped"
+               };
+               static const char * const alpha_mode[4] = {
+                       "None",
+                       "Global",
+                       "Local",
+                       "Global and Local"
+               };
+               static const char * const pixel_format[16] = {
+                       "ARGB Indexed",
+                       "RGB 5:6:5",
+                       "ARGB 1:5:5:5",
+                       "ARGB 1:4:4:4",
+                       "ARGB 8:8:8:8",
+                       "5",
+                       "6",
+                       "7",
+                       "AYUV Indexed",
+                       "YUV 5:6:5",
+                       "AYUV 1:5:5:5",
+                       "AYUV 1:4:4:4",
+                       "AYUV 8:8:8:8",
+                       "13",
+                       "14",
+                       "15",
                };
 
-               memset(fb, 0, sizeof(*fb));
-               if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT_OVERLAY))
-                       return -EINVAL;
-               fb->capability = V4L2_FBUF_CAP_EXTERNOVERLAY | V4L2_FBUF_CAP_CHROMAKEY |
-                       V4L2_FBUF_CAP_GLOBAL_ALPHA;
+               ivtv_get_output(itv, itv->active_output, &vidout);
+               ivtv_get_audio_output(itv, 0, &audout);
+               IVTV_INFO("Video Output: %s\n", vidout.name);
+               IVTV_INFO("Audio Output: %s (Stereo/Bilingual: %s/%s)\n", audout.name,
+                       audio_modes[itv->audio_stereo_mode],
+                       audio_modes[itv->audio_bilingual_mode]);
+               if (mode < 0 || mode > OUT_PASSTHROUGH)
+                       mode = OUT_NONE;
+               IVTV_INFO("Output Mode:  %s\n", output_modes[mode]);
                ivtv_vapi_result(itv, data, CX2341X_OSD_GET_STATE, 0);
                data[0] |= (read_reg(0x2a00) >> 7) & 0x40;
-               pixfmt = (data[0] >> 3) & 0xf;
-               fb->fmt.pixelformat = pixel_format[pixfmt];
-               fb->fmt.width = itv->osd_rect.width;
-               fb->fmt.height = itv->osd_rect.height;
-               fb->base = (void *)itv->osd_video_pbase;
-               if (itv->osd_chroma_key_state)
-                       fb->flags |= V4L2_FBUF_FLAG_CHROMAKEY;
-               if (itv->osd_global_alpha_state)
-                       fb->flags |= V4L2_FBUF_FLAG_GLOBAL_ALPHA;
-               pixfmt &= 7;
-               /* no local alpha for RGB565 or unknown formats */
-               if (pixfmt == 1 || pixfmt > 4)
-                       break;
-               /* 16-bit formats have inverted local alpha */
-               if (pixfmt == 2 || pixfmt == 3)
-                       fb->capability |= V4L2_FBUF_CAP_LOCAL_INV_ALPHA;
-               else
-                       fb->capability |= V4L2_FBUF_CAP_LOCAL_ALPHA;
-               if (itv->osd_local_alpha_state) {
-                       /* 16-bit formats have inverted local alpha */
-                       if (pixfmt == 2 || pixfmt == 3)
-                               fb->flags |= V4L2_FBUF_FLAG_LOCAL_INV_ALPHA;
-                       else
-                               fb->flags |= V4L2_FBUF_FLAG_LOCAL_ALPHA;
-               }
-               if (yi->track_osd)
-                       fb->flags |= V4L2_FBUF_FLAG_OVERLAY;
-               break;
-       }
-
-       case VIDIOC_S_FBUF: {
-               struct v4l2_framebuffer *fb = arg;
-
-               if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT_OVERLAY))
-                       return -EINVAL;
-               itv->osd_global_alpha_state = (fb->flags & V4L2_FBUF_FLAG_GLOBAL_ALPHA) != 0;
-               itv->osd_local_alpha_state =
-                       (fb->flags & (V4L2_FBUF_FLAG_LOCAL_ALPHA|V4L2_FBUF_FLAG_LOCAL_INV_ALPHA)) != 0;
-               itv->osd_chroma_key_state = (fb->flags & V4L2_FBUF_FLAG_CHROMAKEY) != 0;
-               ivtv_set_osd_alpha(itv);
-               yi->track_osd = (fb->flags & V4L2_FBUF_FLAG_OVERLAY) != 0;
-               break;
+               IVTV_INFO("Overlay:      %s, Alpha: %s, Pixel Format: %s\n",
+                       data[0] & 1 ? "On" : "Off",
+                       alpha_mode[(data[0] >> 1) & 0x3],
+                       pixel_format[(data[0] >> 3) & 0xf]);
        }
+       IVTV_INFO("Tuner:  %s\n",
+               test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags) ? "Radio" : "TV");
+       cx2341x_log_status(&itv->params, itv->name);
+       IVTV_INFO("Status flags:    0x%08lx\n", itv->i_flags);
+       for (i = 0; i < IVTV_MAX_STREAMS; i++) {
+               struct ivtv_stream *s = &itv->streams[i];
 
-       case VIDIOC_OVERLAY: {
-               int *on = arg;
-
-               if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT_OVERLAY))
-                       return -EINVAL;
-               ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, *on != 0);
-               break;
+               if (s->v4l2dev == NULL || s->buffers == 0)
+                       continue;
+               IVTV_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n", s->name, s->s_flags,
+                               (s->buffers - s->q_free.buffers) * 100 / s->buffers,
+                               (s->buffers * s->buf_size) / 1024, s->buffers);
        }
 
-       case VIDIOC_LOG_STATUS:
-       {
-               int has_output = itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT;
-               struct v4l2_input vidin;
-               struct v4l2_audio audin;
-               int i;
-
-               IVTV_INFO("=================  START STATUS CARD #%d  =================\n", itv->num);
-               IVTV_INFO("Version: %s Card: %s\n", IVTV_VERSION, itv->card_name);
-               if (itv->hw_flags & IVTV_HW_TVEEPROM) {
-                       struct tveeprom tv;
-
-                       ivtv_read_eeprom(itv, &tv);
-               }
-               ivtv_call_i2c_clients(itv, VIDIOC_LOG_STATUS, NULL);
-               ivtv_get_input(itv, itv->active_input, &vidin);
-               ivtv_get_audio_input(itv, itv->audio_input, &audin);
-               IVTV_INFO("Video Input:  %s\n", vidin.name);
-               IVTV_INFO("Audio Input:  %s%s\n", audin.name,
-                       (itv->dualwatch_stereo_mode & ~0x300) == 0x200 ? " (Bilingual)" : "");
-               if (has_output) {
-                       struct v4l2_output vidout;
-                       struct v4l2_audioout audout;
-                       int mode = itv->output_mode;
-                       static const char * const output_modes[5] = {
-                               "None",
-                               "MPEG Streaming",
-                               "YUV Streaming",
-                               "YUV Frames",
-                               "Passthrough",
-                       };
-                       static const char * const audio_modes[5] = {
-                               "Stereo",
-                               "Left",
-                               "Right",
-                               "Mono",
-                               "Swapped"
-                       };
-                       static const char * const alpha_mode[4] = {
-                               "None",
-                               "Global",
-                               "Local",
-                               "Global and Local"
-                       };
-                       static const char * const pixel_format[16] = {
-                               "ARGB Indexed",
-                               "RGB 5:6:5",
-                               "ARGB 1:5:5:5",
-                               "ARGB 1:4:4:4",
-                               "ARGB 8:8:8:8",
-                               "5",
-                               "6",
-                               "7",
-                               "AYUV Indexed",
-                               "YUV 5:6:5",
-                               "AYUV 1:5:5:5",
-                               "AYUV 1:4:4:4",
-                               "AYUV 8:8:8:8",
-                               "13",
-                               "14",
-                               "15",
-                       };
-
-                       ivtv_get_output(itv, itv->active_output, &vidout);
-                       ivtv_get_audio_output(itv, 0, &audout);
-                       IVTV_INFO("Video Output: %s\n", vidout.name);
-                       IVTV_INFO("Audio Output: %s (Stereo/Bilingual: %s/%s)\n", audout.name,
-                               audio_modes[itv->audio_stereo_mode],
-                               audio_modes[itv->audio_bilingual_mode]);
-                       if (mode < 0 || mode > OUT_PASSTHROUGH)
-                               mode = OUT_NONE;
-                       IVTV_INFO("Output Mode:  %s\n", output_modes[mode]);
-                       ivtv_vapi_result(itv, data, CX2341X_OSD_GET_STATE, 0);
-                       data[0] |= (read_reg(0x2a00) >> 7) & 0x40;
-                       IVTV_INFO("Overlay:      %s, Alpha: %s, Pixel Format: %s\n",
-                               data[0] & 1 ? "On" : "Off",
-                               alpha_mode[(data[0] >> 1) & 0x3],
-                               pixel_format[(data[0] >> 3) & 0xf]);
-               }
-               IVTV_INFO("Tuner:  %s\n",
-                       test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags) ? "Radio" : "TV");
-               cx2341x_log_status(&itv->params, itv->name);
-               IVTV_INFO("Status flags:    0x%08lx\n", itv->i_flags);
-               for (i = 0; i < IVTV_MAX_STREAMS; i++) {
-                       struct ivtv_stream *s = &itv->streams[i];
-
-                       if (s->v4l2dev == NULL || s->buffers == 0)
-                               continue;
-                       IVTV_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n", s->name, s->s_flags,
-                                       (s->buffers - s->q_free.buffers) * 100 / s->buffers,
-                                       (s->buffers * s->buf_size) / 1024, s->buffers);
-               }
-               IVTV_INFO("Read MPG/VBI: %lld/%lld bytes\n", (long long)itv->mpg_data_received, (long long)itv->vbi_data_inserted);
-               IVTV_INFO("==================  END STATUS CARD #%d  ==================\n", itv->num);
-               break;
-       }
+       IVTV_INFO("Read MPG/VBI: %lld/%lld bytes\n", (long long)itv->mpg_data_received, (long long)itv->vbi_data_inserted);
+       IVTV_INFO("==================  END STATUS CARD #%d  ==================\n", itv->num);
 
-       default:
-               return -EINVAL;
-       }
        return 0;
 }
 
@@ -1433,7 +1548,7 @@ static int ivtv_decoder_ioctls(struct file *filp, unsigned int cmd, void *arg)
                        return -EINVAL;
                if (itv->output_mode == OUT_UDMA_YUV && args->y_source == NULL)
                        return 0;
-               if (ivtv_claim_stream(id, id->type)) {
+               if (ivtv_start_decoding(id, id->type)) {
                        return -EBUSY;
                }
                if (ivtv_set_output_mode(itv, OUT_UDMA_YUV) != OUT_UDMA_YUV) {
@@ -1607,121 +1722,30 @@ static int ivtv_decoder_ioctls(struct file *filp, unsigned int cmd, void *arg)
        return 0;
 }
 
-static int ivtv_v4l2_do_ioctl(struct inode *inode, struct file *filp,
-                             unsigned int cmd, void *arg)
+static int ivtv_default(struct file *file, void *fh, int cmd, void *arg)
 {
-       struct ivtv_open_id *id = (struct ivtv_open_id *)filp->private_data;
-       struct ivtv *itv = id->itv;
-       int ret;
-
-       /* check priority */
-       switch (cmd) {
-       case VIDIOC_S_CTRL:
-       case VIDIOC_S_STD:
-       case VIDIOC_S_INPUT:
-       case VIDIOC_S_OUTPUT:
-       case VIDIOC_S_TUNER:
-       case VIDIOC_S_FREQUENCY:
-       case VIDIOC_S_FMT:
-       case VIDIOC_S_CROP:
-       case VIDIOC_S_AUDIO:
-       case VIDIOC_S_AUDOUT:
-       case VIDIOC_S_EXT_CTRLS:
-       case VIDIOC_S_FBUF:
-       case VIDIOC_OVERLAY:
-               ret = v4l2_prio_check(&itv->prio, &id->prio);
-               if (ret)
-                       return ret;
-       }
+       struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
 
        switch (cmd) {
-       case VIDIOC_DBG_G_REGISTER:
-       case VIDIOC_DBG_S_REGISTER:
-       case VIDIOC_G_CHIP_IDENT:
-       case VIDIOC_INT_S_AUDIO_ROUTING:
-       case VIDIOC_INT_RESET:
-               if (ivtv_debug & IVTV_DBGFLG_IOCTL) {
-                       printk(KERN_INFO "ivtv%d ioctl: ", itv->num);
-                       v4l_printk_ioctl(cmd);
-                       printk("\n");
-               }
-               return ivtv_debug_ioctls(filp, cmd, arg);
+       case VIDIOC_INT_S_AUDIO_ROUTING: {
+               struct v4l2_routing *route = arg;
 
-       case VIDIOC_G_PRIORITY:
-       case VIDIOC_S_PRIORITY:
-       case VIDIOC_QUERYCAP:
-       case VIDIOC_ENUMINPUT:
-       case VIDIOC_G_INPUT:
-       case VIDIOC_S_INPUT:
-       case VIDIOC_ENUMOUTPUT:
-       case VIDIOC_G_OUTPUT:
-       case VIDIOC_S_OUTPUT:
-       case VIDIOC_G_FMT:
-       case VIDIOC_S_FMT:
-       case VIDIOC_TRY_FMT:
-       case VIDIOC_ENUM_FMT:
-       case VIDIOC_CROPCAP:
-       case VIDIOC_G_CROP:
-       case VIDIOC_S_CROP:
-       case VIDIOC_G_FREQUENCY:
-       case VIDIOC_S_FREQUENCY:
-       case VIDIOC_ENUMSTD:
-       case VIDIOC_G_STD:
-       case VIDIOC_S_STD:
-       case VIDIOC_S_TUNER:
-       case VIDIOC_G_TUNER:
-       case VIDIOC_ENUMAUDIO:
-       case VIDIOC_S_AUDIO:
-       case VIDIOC_G_AUDIO:
-       case VIDIOC_ENUMAUDOUT:
-       case VIDIOC_S_AUDOUT:
-       case VIDIOC_G_AUDOUT:
-       case VIDIOC_G_SLICED_VBI_CAP:
-       case VIDIOC_LOG_STATUS:
-       case VIDIOC_G_ENC_INDEX:
-       case VIDIOC_ENCODER_CMD:
-       case VIDIOC_TRY_ENCODER_CMD:
-       case VIDIOC_G_FBUF:
-       case VIDIOC_S_FBUF:
-       case VIDIOC_OVERLAY:
-               if (ivtv_debug & IVTV_DBGFLG_IOCTL) {
-                       printk(KERN_INFO "ivtv%d ioctl: ", itv->num);
-                       v4l_printk_ioctl(cmd);
-                       printk("\n");
-               }
-               return ivtv_v4l2_ioctls(itv, filp, cmd, arg);
+               ivtv_i2c_hw(itv, itv->card->hw_audio, VIDIOC_INT_S_AUDIO_ROUTING, route);
+               break;
+       }
 
-       case VIDIOC_QUERYMENU:
-       case VIDIOC_QUERYCTRL:
-       case VIDIOC_S_CTRL:
-       case VIDIOC_G_CTRL:
-       case VIDIOC_S_EXT_CTRLS:
-       case VIDIOC_G_EXT_CTRLS:
-       case VIDIOC_TRY_EXT_CTRLS:
-               if (ivtv_debug & IVTV_DBGFLG_IOCTL) {
-                       printk(KERN_INFO "ivtv%d ioctl: ", itv->num);
-                       v4l_printk_ioctl(cmd);
-                       printk("\n");
-               }
-               return ivtv_control_ioctls(itv, cmd, arg);
+       case VIDIOC_INT_RESET: {
+               u32 val = *(u32 *)arg;
 
-       case IVTV_IOC_DMA_FRAME:
-       case VIDEO_GET_PTS:
-       case VIDEO_GET_FRAME_COUNT:
-       case VIDEO_GET_EVENT:
-       case VIDEO_PLAY:
-       case VIDEO_STOP:
-       case VIDEO_FREEZE:
-       case VIDEO_CONTINUE:
-       case VIDEO_COMMAND:
-       case VIDEO_TRY_COMMAND:
-               return ivtv_decoder_ioctls(filp, cmd, arg);
+               if ((val == 0 && itv->options.newi2c) || (val & 0x01))
+                       ivtv_reset_ir_gpio(itv);
+               if (val & 0x02)
+                       itv->video_dec_func(itv, cmd, NULL);
+               break;
+       }
 
-       case 0x00005401:        /* Handle isatty() calls */
-               return -EINVAL;
        default:
-               return v4l_compat_translate_ioctl(inode, filp, cmd, arg,
-                                                  ivtv_v4l2_do_ioctl);
+               return -EINVAL;
        }
        return 0;
 }
@@ -1729,7 +1753,11 @@ static int ivtv_v4l2_do_ioctl(struct inode *inode, struct file *filp,
 static int ivtv_serialized_ioctl(struct ivtv *itv, struct inode *inode, struct file *filp,
                unsigned int cmd, unsigned long arg)
 {
-       /* Filter dvb ioctls that cannot be handled by video_usercopy */
+       struct video_device *vfd = video_devdata(filp);
+       struct ivtv_open_id *id = (struct ivtv_open_id *)filp->private_data;
+       int ret;
+
+       /* Filter dvb ioctls that cannot be handled by the v4l ioctl framework */
        switch (cmd) {
        case VIDEO_SELECT_SOURCE:
                IVTV_DEBUG_IOCTL("VIDEO_SELECT_SOURCE\n");
@@ -1758,10 +1786,47 @@ static int ivtv_serialized_ioctl(struct ivtv *itv, struct inode *inode, struct f
                ivtv_vapi(itv, CX2341X_DEC_SET_AUDIO_MODE, 2, itv->audio_bilingual_mode, itv->audio_stereo_mode);
                return 0;
 
+       case IVTV_IOC_DMA_FRAME:
+       case VIDEO_GET_PTS:
+       case VIDEO_GET_FRAME_COUNT:
+       case VIDEO_GET_EVENT:
+       case VIDEO_PLAY:
+       case VIDEO_STOP:
+       case VIDEO_FREEZE:
+       case VIDEO_CONTINUE:
+       case VIDEO_COMMAND:
+       case VIDEO_TRY_COMMAND:
+               return ivtv_decoder_ioctls(filp, cmd, (void *)arg);
+
        default:
                break;
        }
-       return video_usercopy(inode, filp, cmd, arg, ivtv_v4l2_do_ioctl);
+
+       /* check priority */
+       switch (cmd) {
+       case VIDIOC_S_CTRL:
+       case VIDIOC_S_STD:
+       case VIDIOC_S_INPUT:
+       case VIDIOC_S_OUTPUT:
+       case VIDIOC_S_TUNER:
+       case VIDIOC_S_FREQUENCY:
+       case VIDIOC_S_FMT:
+       case VIDIOC_S_CROP:
+       case VIDIOC_S_AUDIO:
+       case VIDIOC_S_AUDOUT:
+       case VIDIOC_S_EXT_CTRLS:
+       case VIDIOC_S_FBUF:
+       case VIDIOC_OVERLAY:
+               ret = v4l2_prio_check(&itv->prio, &id->prio);
+               if (ret)
+                       return ret;
+       }
+
+       if (ivtv_debug & IVTV_DBGFLG_IOCTL)
+               vfd->debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG;
+       ret = video_ioctl2(inode, filp, cmd, arg);
+       vfd->debug = 0;
+       return ret;
 }
 
 int ivtv_v4l2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
@@ -1776,3 +1841,70 @@ int ivtv_v4l2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
        mutex_unlock(&itv->serialize_lock);
        return res;
 }
+
+void ivtv_set_funcs(struct video_device *vdev)
+{
+       vdev->vidioc_querycap               = ivtv_querycap;
+       vdev->vidioc_g_priority             = ivtv_g_priority;
+       vdev->vidioc_s_priority             = ivtv_s_priority;
+       vdev->vidioc_s_audio                = ivtv_s_audio;
+       vdev->vidioc_g_audio                = ivtv_g_audio;
+       vdev->vidioc_enumaudio              = ivtv_enumaudio;
+       vdev->vidioc_s_audout               = ivtv_s_audout;
+       vdev->vidioc_g_audout               = ivtv_g_audout;
+       vdev->vidioc_enum_input             = ivtv_enum_input;
+       vdev->vidioc_enum_output            = ivtv_enum_output;
+       vdev->vidioc_enumaudout             = ivtv_enumaudout;
+       vdev->vidioc_cropcap                = ivtv_cropcap;
+       vdev->vidioc_s_crop                 = ivtv_s_crop;
+       vdev->vidioc_g_crop                 = ivtv_g_crop;
+       vdev->vidioc_g_input                = ivtv_g_input;
+       vdev->vidioc_s_input                = ivtv_s_input;
+       vdev->vidioc_g_output               = ivtv_g_output;
+       vdev->vidioc_s_output               = ivtv_s_output;
+       vdev->vidioc_g_frequency            = ivtv_g_frequency;
+       vdev->vidioc_s_frequency            = ivtv_s_frequency;
+       vdev->vidioc_s_tuner                = ivtv_s_tuner;
+       vdev->vidioc_g_tuner                = ivtv_g_tuner;
+       vdev->vidioc_g_enc_index            = ivtv_g_enc_index;
+       vdev->vidioc_g_fbuf                 = ivtv_g_fbuf;
+       vdev->vidioc_s_fbuf                 = ivtv_s_fbuf;
+       vdev->vidioc_g_std                  = ivtv_g_std;
+       vdev->vidioc_s_std                  = ivtv_s_std;
+       vdev->vidioc_overlay                = ivtv_overlay;
+       vdev->vidioc_log_status             = ivtv_log_status;
+       vdev->vidioc_enum_fmt_vid_cap       = ivtv_enum_fmt_vid_cap;
+       vdev->vidioc_encoder_cmd            = ivtv_encoder_cmd;
+       vdev->vidioc_try_encoder_cmd        = ivtv_try_encoder_cmd;
+       vdev->vidioc_enum_fmt_vid_out       = ivtv_enum_fmt_vid_out;
+       vdev->vidioc_g_fmt_vid_cap          = ivtv_g_fmt_vid_cap;
+       vdev->vidioc_g_fmt_vbi_cap          = ivtv_g_fmt_vbi_cap;
+       vdev->vidioc_g_fmt_sliced_vbi_cap   = ivtv_g_fmt_sliced_vbi_cap;
+       vdev->vidioc_g_fmt_vid_out          = ivtv_g_fmt_vid_out;
+       vdev->vidioc_g_fmt_vid_out_overlay  = ivtv_g_fmt_vid_out_overlay;
+       vdev->vidioc_g_fmt_sliced_vbi_out   = ivtv_g_fmt_sliced_vbi_out;
+       vdev->vidioc_s_fmt_vid_cap          = ivtv_s_fmt_vid_cap;
+       vdev->vidioc_s_fmt_vbi_cap          = ivtv_s_fmt_vbi_cap;
+       vdev->vidioc_s_fmt_sliced_vbi_cap   = ivtv_s_fmt_sliced_vbi_cap;
+       vdev->vidioc_s_fmt_vid_out          = ivtv_s_fmt_vid_out;
+       vdev->vidioc_s_fmt_vid_out_overlay  = ivtv_s_fmt_vid_out_overlay;
+       vdev->vidioc_s_fmt_sliced_vbi_out   = ivtv_s_fmt_sliced_vbi_out;
+       vdev->vidioc_try_fmt_vid_cap        = ivtv_try_fmt_vid_cap;
+       vdev->vidioc_try_fmt_vbi_cap        = ivtv_try_fmt_vbi_cap;
+       vdev->vidioc_try_fmt_sliced_vbi_cap = ivtv_try_fmt_sliced_vbi_cap;
+       vdev->vidioc_try_fmt_vid_out        = ivtv_try_fmt_vid_out;
+       vdev->vidioc_try_fmt_vid_out_overlay = ivtv_try_fmt_vid_out_overlay;
+       vdev->vidioc_try_fmt_sliced_vbi_out = ivtv_try_fmt_sliced_vbi_out;
+       vdev->vidioc_g_sliced_vbi_cap       = ivtv_g_sliced_vbi_cap;
+       vdev->vidioc_g_chip_ident           = ivtv_g_chip_ident;
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+       vdev->vidioc_g_register             = ivtv_g_register;
+       vdev->vidioc_s_register             = ivtv_s_register;
+#endif
+       vdev->vidioc_default                = ivtv_default;
+       vdev->vidioc_queryctrl              = ivtv_queryctrl;
+       vdev->vidioc_querymenu              = ivtv_querymenu;
+       vdev->vidioc_g_ext_ctrls            = ivtv_g_ext_ctrls;
+       vdev->vidioc_s_ext_ctrls            = ivtv_s_ext_ctrls;
+       vdev->vidioc_try_ext_ctrls          = ivtv_try_ext_ctrls;
+}
index 4e67f0ed1fc0458d1020fb2826835296de092167..70188588b4f4f1e07c5916371ef13d5fd3da6da6 100644 (file)
 u16 ivtv_service2vbi(int type);
 void ivtv_expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal);
 u16 ivtv_get_service_set(struct v4l2_sliced_vbi_format *fmt);
-int ivtv_v4l2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
-                   unsigned long arg);
-int ivtv_v4l2_ioctls(struct ivtv *itv, struct file *filp, unsigned int cmd, void *arg);
 void ivtv_set_osd_alpha(struct ivtv *itv);
 int ivtv_set_speed(struct ivtv *itv, int speed);
+void ivtv_set_funcs(struct video_device *vdev);
+int ivtv_s_std(struct file *file, void *fh, v4l2_std_id *std);
+int ivtv_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf);
+int ivtv_s_input(struct file *file, void *fh, unsigned int inp);
+int ivtv_v4l2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
+                   unsigned long arg);
 
 #endif
index c854285a4371c91510e71ba39df8449762001ece..f8883b487f4a9dbbee6cda1d1edd0c0fb64fd2c6 100644 (file)
@@ -220,7 +220,8 @@ static int ivtv_prep_dev(struct ivtv *itv, int type)
        s->v4l2dev->dev = &itv->dev->dev;
        s->v4l2dev->fops = ivtv_stream_info[type].fops;
        s->v4l2dev->release = video_device_release;
-
+       s->v4l2dev->tvnorms = V4L2_STD_ALL;
+       ivtv_set_funcs(s->v4l2dev);
        return 0;
 }
 
index 73be154f7f0520666c977140bf8a3047b72b7061..bdfda48e56bf55e0b5ea3af83404348053fd26b8 100644 (file)
@@ -367,6 +367,88 @@ static int ivtvfb_prep_frame(struct ivtv *itv, int cmd, void __user *source,
        return ivtvfb_prep_dec_dma_to_device(itv, dest_offset, source, count);
 }
 
+static ssize_t ivtvfb_write(struct fb_info *info, const char __user *buf,
+                    size_t count, loff_t *ppos)
+{
+       unsigned long p = *ppos;
+       void *dst;
+       int err = 0;
+       unsigned long total_size;
+       struct ivtv *itv = (struct ivtv *) info->par;
+       unsigned long dma_offset =
+                       IVTV_DECODER_OFFSET + itv->osd_info->video_rbase;
+       unsigned long dma_size;
+       u16 lead = 0, tail = 0;
+
+       if (info->state != FBINFO_STATE_RUNNING)
+               return -EPERM;
+
+       total_size = info->screen_size;
+
+       if (total_size == 0)
+               total_size = info->fix.smem_len;
+
+       if (p > total_size)
+               return -EFBIG;
+
+       if (count > total_size) {
+               err = -EFBIG;
+               count = total_size;
+       }
+
+       if (count + p > total_size) {
+               if (!err)
+                       err = -ENOSPC;
+
+               count = total_size - p;
+       }
+
+       dst = (void __force *) (info->screen_base + p);
+
+       if (info->fbops->fb_sync)
+               info->fbops->fb_sync(info);
+
+       if (!access_ok(VERIFY_READ, buf, count)) {
+               IVTVFB_WARN("Invalid userspace pointer 0x%08lx\n",
+                       (unsigned long)buf);
+               err = -EFAULT;
+       }
+
+       if (!err) {
+               /* If transfer size > threshold and both src/dst
+               addresses are aligned, use DMA */
+               if (count >= 4096 &&
+                   ((unsigned long)buf & 3) == ((unsigned long)dst & 3)) {
+                       /* Odd address = can't DMA. Align */
+                       if ((unsigned long)dst & 3) {
+                               lead = 4 - ((unsigned long)dst & 3);
+                               memcpy(dst, buf, lead);
+                               buf += lead;
+                               dst += lead;
+                       }
+                       /* DMA resolution is 32 bits */
+                       if ((count - lead) & 3)
+                               tail = (count - lead) & 3;
+                       /* DMA the data */
+                       dma_size = count - lead - tail;
+                       err = ivtvfb_prep_dec_dma_to_device(itv,
+                              p + lead + dma_offset, (void *)buf, dma_size);
+                       dst += dma_size;
+                       buf += dma_size;
+                       /* Copy any leftover data */
+                       if (tail)
+                               memcpy(dst, buf, tail);
+               } else {
+                       memcpy(dst, buf, count);
+               }
+       }
+
+       if  (!err)
+               *ppos += count;
+
+       return (err) ? err : count;
+}
+
 static int ivtvfb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
 {
        DEFINE_WAIT(wait);
@@ -708,6 +790,9 @@ static int _ivtvfb_check_var(struct fb_var_screeninfo *var, struct ivtv *itv)
        else
                var->pixclock = pixclock;
 
+       itv->osd_rect.width = var->xres;
+       itv->osd_rect.height = var->yres;
+
        IVTVFB_DEBUG_INFO("Display size: %dx%d (virtual %dx%d) @ %dbpp\n",
                      var->xres, var->yres,
                      var->xres_virtual, var->yres_virtual,
@@ -824,6 +909,7 @@ static int ivtvfb_blank(int blank_mode, struct fb_info *info)
 
 static struct fb_ops ivtvfb_ops = {
        .owner = THIS_MODULE,
+       .fb_write       = ivtvfb_write,
        .fb_check_var   = ivtvfb_check_var,
        .fb_set_par     = ivtvfb_set_par,
        .fb_setcolreg   = ivtvfb_setcolreg,
index 8e0160d275ca53aaf3ac27874bfa62dcfd3a7799..39bf6b114d50ac348bfdb2b77115e8dfbddec377 100644 (file)
@@ -171,4 +171,3 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = {
        .remove = m52790_remove,
        .id_table = m52790_id,
 };
-
index e7ccbc895d7abe4fc37b84ae805d6d504d37dbde..2fb5854cf6f039d58967b9ad07735c0a73e5f9b3 100644 (file)
@@ -1253,7 +1253,7 @@ static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *c)
        return 0;
 }
 
-static int vidioc_enum_fmt_cap(struct file *file, void *fh,
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *fh,
                                struct v4l2_fmtdesc *f)
 {
        if (f->index > 1)
@@ -1283,7 +1283,7 @@ static int vidioc_enum_fmt_cap(struct file *file, void *fh,
        return 0;
 }
 
-static int vidioc_try_fmt_cap(struct file *file, void *fh,
+static int vidioc_try_fmt_vid_cap(struct file *file, void *fh,
                                struct v4l2_format *f)
 {
        if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
@@ -1316,7 +1316,8 @@ static int vidioc_try_fmt_cap(struct file *file, void *fh,
        return 0;
 }
 
-static int vidioc_g_fmt_cap(struct file *file, void *fh, struct v4l2_format *f)
+static int vidioc_g_fmt_vid_cap(struct file *file, void *fh,
+                                   struct v4l2_format *f)
 {
        if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
                return -EINVAL;
@@ -1346,7 +1347,8 @@ static int vidioc_g_fmt_cap(struct file *file, void *fh, struct v4l2_format *f)
        return 0;
 }
 
-static int vidioc_s_fmt_cap(struct file *file, void *fh, struct v4l2_format *f)
+static int vidioc_s_fmt_vid_cap(struct file *file, void *fh,
+                                   struct v4l2_format *f)
 {
        if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
                return -EINVAL;
@@ -1709,10 +1711,10 @@ static struct video_device meye_template = {
        .vidioc_queryctrl       = vidioc_queryctrl,
        .vidioc_s_ctrl          = vidioc_s_ctrl,
        .vidioc_g_ctrl          = vidioc_g_ctrl,
-       .vidioc_enum_fmt_cap    = vidioc_enum_fmt_cap,
-       .vidioc_try_fmt_cap     = vidioc_try_fmt_cap,
-       .vidioc_g_fmt_cap       = vidioc_g_fmt_cap,
-       .vidioc_s_fmt_cap       = vidioc_s_fmt_cap,
+       .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+       .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+       .vidioc_g_fmt_vid_cap   = vidioc_g_fmt_vid_cap,
+       .vidioc_s_fmt_vid_cap   = vidioc_s_fmt_vid_cap,
        .vidioc_reqbufs         = vidioc_reqbufs,
        .vidioc_querybuf        = vidioc_querybuf,
        .vidioc_qbuf            = vidioc_qbuf,
index 310dbaba55ff325264675a1f56d79f463e2144cd..5691e019d19567d56ba8f9ba1f82a354f55a6ee5 100644 (file)
@@ -110,6 +110,7 @@ MODULE_PARM_DESC(dolby, "Activates Dolby processsing");
 
 /* Addresses to scan */
 static unsigned short normal_i2c[] = { 0x80 >> 1, 0x88 >> 1, I2C_CLIENT_END };
+
 I2C_CLIENT_INSMOD;
 
 /* ----------------------------------------------------------------------- */
@@ -333,7 +334,6 @@ void msp_set_audio(struct i2c_client *client)
 
 /* ------------------------------------------------------------------------ */
 
-
 static void msp_wake_thread(struct i2c_client *client)
 {
        struct msp_state *state = i2c_get_clientdata(client);
@@ -1004,7 +1004,6 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = {
        .id_table = msp_id,
 };
 
-
 /*
  * Overrides for Emacs so that we follow Linus's tabbing style.
  * ---------------------------------------------------------------------------
index 7f5568592793c836f8a71d78181f83c6dcf2c648..1622f70e4dd0b4244f7f16c098182a4df171a10b 100644 (file)
@@ -480,7 +480,6 @@ int msp3400c_thread(void *data)
        struct msp3400c_carrier_detect *cd;
        int count, max1, max2, val1, val2, val, i;
 
-
        v4l_dbg(1, msp_debug, client, "msp3400 daemon started\n");
        set_freezable();
        for (;;) {
index 1658fe5903928d076612e888ceb0be2d15641f0a..b31ba4e093270e6ab3fc3238637638cf85e1ec97 100644 (file)
@@ -815,7 +815,6 @@ static int mt9v022_remove(struct i2c_client *client)
 
        return 0;
 }
-
 static const struct i2c_device_id mt9v022_id[] = {
        { "mt9v022", 0 },
        { }
index d7bfd30f74a9bed8455d8c69859f83becc4f2471..ea032f5f2f412ff2fae9eb6fa1535ad045e94ede 100644 (file)
@@ -682,17 +682,17 @@ static int ov7670_try_fmt(struct i2c_client *c, struct v4l2_format *fmt,
        for (index = 0; index < N_OV7670_FMTS; index++)
                if (ov7670_formats[index].pixelformat == pix->pixelformat)
                        break;
-       if (index >= N_OV7670_FMTS)
-               return -EINVAL;
+       if (index >= N_OV7670_FMTS) {
+               /* default to first format */
+               index = 0;
+               pix->pixelformat = ov7670_formats[0].pixelformat;
+       }
        if (ret_fmt != NULL)
                *ret_fmt = ov7670_formats + index;
        /*
         * Fields: the OV devices claim to be progressive.
         */
-       if (pix->field == V4L2_FIELD_ANY)
-               pix->field = V4L2_FIELD_NONE;
-       else if (pix->field != V4L2_FIELD_NONE)
-               return -EINVAL;
+       pix->field = V4L2_FIELD_NONE;
        /*
         * Round requested image size down to the nearest
         * we support, but not below the smallest.
@@ -833,7 +833,7 @@ static int ov7670_store_cmatrix(struct i2c_client *client,
                int matrix[CMATRIX_LEN])
 {
        int i, ret;
-       unsigned char signbits;
+       unsigned char signbits = 0;
 
        /*
         * Weird crap seems to exist in the upper part of
@@ -1009,7 +1009,7 @@ static unsigned char ov7670_abs_to_sm(unsigned char v)
 
 static int ov7670_t_brightness(struct i2c_client *client, int value)
 {
-       unsigned char com8, v;
+       unsigned char com8 = 0, v;
        int ret;
 
        ov7670_read(client, REG_COM8, &com8);
@@ -1022,7 +1022,7 @@ static int ov7670_t_brightness(struct i2c_client *client, int value)
 
 static int ov7670_q_brightness(struct i2c_client *client, __s32 *value)
 {
-       unsigned char v;
+       unsigned char v = 0;
        int ret = ov7670_read(client, REG_BRIGHT, &v);
 
        *value = ov7670_sm_to_abs(v);
@@ -1036,7 +1036,7 @@ static int ov7670_t_contrast(struct i2c_client *client, int value)
 
 static int ov7670_q_contrast(struct i2c_client *client, __s32 *value)
 {
-       unsigned char v;
+       unsigned char v = 0;
        int ret = ov7670_read(client, REG_CONTRAS, &v);
 
        *value = v;
@@ -1046,7 +1046,7 @@ static int ov7670_q_contrast(struct i2c_client *client, __s32 *value)
 static int ov7670_q_hflip(struct i2c_client *client, __s32 *value)
 {
        int ret;
-       unsigned char v;
+       unsigned char v = 0;
 
        ret = ov7670_read(client, REG_MVFP, &v);
        *value = (v & MVFP_MIRROR) == MVFP_MIRROR;
@@ -1056,7 +1056,7 @@ static int ov7670_q_hflip(struct i2c_client *client, __s32 *value)
 
 static int ov7670_t_hflip(struct i2c_client *client, int value)
 {
-       unsigned char v;
+       unsigned char v = 0;
        int ret;
 
        ret = ov7670_read(client, REG_MVFP, &v);
@@ -1074,7 +1074,7 @@ static int ov7670_t_hflip(struct i2c_client *client, int value)
 static int ov7670_q_vflip(struct i2c_client *client, __s32 *value)
 {
        int ret;
-       unsigned char v;
+       unsigned char v = 0;
 
        ret = ov7670_read(client, REG_MVFP, &v);
        *value = (v & MVFP_FLIP) == MVFP_FLIP;
@@ -1084,7 +1084,7 @@ static int ov7670_q_vflip(struct i2c_client *client, __s32 *value)
 
 static int ov7670_t_vflip(struct i2c_client *client, int value)
 {
-       unsigned char v;
+       unsigned char v = 0;
        int ret;
 
        ret = ov7670_read(client, REG_MVFP, &v);
index 8063e33f1c85ebc45e2b49780fe0908d581d36cd..065c2454113e717ddb2cb6fd276b5e16e3382c0f 100644 (file)
@@ -297,7 +297,6 @@ static int ovcamchip_attach(struct i2c_adapter *adap)
        switch (adap->id) {
        case I2C_HW_SMBUS_OV511:
        case I2C_HW_SMBUS_OV518:
-       case I2C_HW_SMBUS_OVFX2:
        case I2C_HW_SMBUS_W9968CF:
                PDEBUG(1, "Adapter ID 0x%06x accepted", adap->id);
                break;
index 8d859ccd48ecc0b0da9a59d030a988f37528e629..cdedaa55f152de97549d455ff2edf01bb3d3219c 100644 (file)
@@ -1,6 +1,5 @@
 /*
  *
- *  $Id$
  *
  *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
  *  Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
index 536339b688432e37e86c1de55d2e3d0fd891f66c..ac54eed3721b7a5804aaaa1002d8459b5f88fa20 100644 (file)
@@ -1,6 +1,5 @@
 /*
  *
- *  $Id$
  *
  *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
  *  Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
index 73dcb1c57ae66c8b520558cfc0fb3efe60ded5e0..7c19ff72e6b3255b2dbacbc32e40b028e4f76140 100644 (file)
@@ -1,5 +1,4 @@
 /*
- *  $Id$
  *
  *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
  *
index 745e270233c26572f2c8f647ae64b43b24908268..61801291c2afea954062ca55ce9d18f012b7616f 100644 (file)
@@ -1,5 +1,4 @@
 /*
- *  $Id$
  *
  *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
  *
index 91a42f2473a7ff7718d163233c38e39b3efd3b5f..0764fbfffb73b036c0687b34c94afd1a9d3b58a3 100644 (file)
@@ -1,6 +1,5 @@
 /*
  *
- *  $Id$
  *
  *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
  *
index c1680053cd644f10c7b2c1c0540640583e8c9302..0371ae6e6e4eb61b1e42e8799f92598d5a86e1ff 100644 (file)
@@ -1,6 +1,5 @@
 /*
  *
- *  $Id$
  *
  *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
  *
index 29d50597c88a5aeb2cd9ce14daac7fa5c238afa1..895859ec495a7737b85ebd2ca47fd4924ed7e5da 100644 (file)
@@ -1,6 +1,5 @@
 /*
  *
- *  $Id$
  *
  *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
  *  Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
index 54b2844e7a716ff79c4f50be76224555b8f7529a..66abf77f51fdb91f90c26ae556f81dbfeec77adf 100644 (file)
@@ -1,6 +1,5 @@
 /*
  *
- *  $Id$
  *
  *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
  *  Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
index 707d2d9635d7a9e2f68f8ecd5078f0b2e184f257..be79249f8628be3dfe3b9c7aaf2e1653e76e8c53 100644 (file)
@@ -1,5 +1,4 @@
 /*
- *  $Id$
  *
  *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
  *
index b53121c78ff992e9b249e3a1c3afeb8526823ee6..ca892fb78a5b8055a6da521e767f5a637130f70c 100644 (file)
@@ -1,6 +1,5 @@
 /*
  *
- *  $Id$
  *
  *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
  *
index 990b02d35d36903090b5e1b34725e47f036f1605..e24ff59f8605d50501ca8e5e5a5976e9840ab675 100644 (file)
@@ -1,6 +1,5 @@
 /*
  *
- *  $Id$
  *
  *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
  *
index 5bf6d8fda1f9f284667093337b1ad851f09f9412..5d036e7e3f07707eeecd20b089021ef51d4051e2 100644 (file)
@@ -1,6 +1,5 @@
 /*
  *
- *  $Id$
  *
  *  Copyright (C) 2007 Mike Isely <isely@pobox.com>
  *
@@ -182,7 +181,7 @@ static int pvr2_lgh06xf_attach(struct pvr2_dvb_adapter *adap)
        return 0;
 }
 
-struct pvr2_dvb_props pvr2_onair_creator_fe_props = {
+static struct pvr2_dvb_props pvr2_onair_creator_fe_props = {
        .frontend_attach = pvr2_lgdt3303_attach,
        .tuner_attach    = pvr2_lgh06xf_attach,
 };
@@ -242,7 +241,7 @@ static int pvr2_fcv1236d_attach(struct pvr2_dvb_adapter *adap)
        return 0;
 }
 
-struct pvr2_dvb_props pvr2_onair_usb2_fe_props = {
+static struct pvr2_dvb_props pvr2_onair_usb2_fe_props = {
        .frontend_attach = pvr2_lgdt3302_attach,
        .tuner_attach    = pvr2_fcv1236d_attach,
 };
@@ -315,7 +314,7 @@ static int pvr2_73xxx_tda18271_8295_attach(struct pvr2_dvb_adapter *adap)
        return 0;
 }
 
-struct pvr2_dvb_props pvr2_73xxx_dvb_props = {
+static struct pvr2_dvb_props pvr2_73xxx_dvb_props = {
        .frontend_attach = pvr2_tda10048_attach,
        .tuner_attach    = pvr2_73xxx_tda18271_8295_attach,
 };
@@ -418,12 +417,12 @@ static int pvr2_tda18271_8295_attach(struct pvr2_dvb_adapter *adap)
        return 0;
 }
 
-struct pvr2_dvb_props pvr2_750xx_dvb_props = {
+static struct pvr2_dvb_props pvr2_750xx_dvb_props = {
        .frontend_attach = pvr2_s5h1409_attach,
        .tuner_attach    = pvr2_tda18271_8295_attach,
 };
 
-struct pvr2_dvb_props pvr2_751xx_dvb_props = {
+static struct pvr2_dvb_props pvr2_751xx_dvb_props = {
        .frontend_attach = pvr2_s5h1411_attach,
        .tuner_attach    = pvr2_tda18271_8295_attach,
 };
index d016f8b6c70b478153fb726718d08ca57adf6d34..e23ce1d2edd772e9996652e18df182ce70c3744c 100644 (file)
@@ -1,6 +1,5 @@
 /*
  *
- *  $Id$
  *
  *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
  *
index 5ef005947b04f2bd2bbeed76562b13056872b84c..299afa4fa96953ac804ef778a50cd3329dc6741d 100644 (file)
@@ -1,6 +1,5 @@
 /*
  *
- *  $Id$
  *
  *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
  *  Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
index 84242975dea7f73e861e46df086b7e97cfab6fd9..cca3216f94cc5ac78fd2cb022ded5084bce45b81 100644 (file)
@@ -1,6 +1,5 @@
 /*
  *
- *  $Id$
  *
  *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
  *  Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
index c46d367f7472599ca8b57aeeddd52e2956c372a6..a1252d673b411331e36811dac8cdea5d65e052f4 100644 (file)
@@ -1,6 +1,5 @@
 /*
  *
- *  $Id$
  *
  *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
  *  Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
index 54caf2e3c428adadc7ff16b76261ffeac60a95c1..232fefbcd1ac024ba38042b9ecfeea0284391568 100644 (file)
@@ -1,6 +1,5 @@
 /*
  *
- *  $Id$
  *
  *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
  *  Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
index abaada31e66ebe06c26bb9ba34a2df0006c58ab2..b58369e7f30bcc800d8207784463ff6bc3e7c4c4 100644 (file)
@@ -1,6 +1,5 @@
 /*
  *
- *  $Id$
  *
  *  Copyright (C) 2007 Michael Krufky <mkrufky@linuxtv.org>
  *
index a3fe251d6fd912eeaefc9f07157b702074d67abd..657f861593b352516c324fa7da471462c7250a67 100644 (file)
@@ -1,6 +1,5 @@
 /*
  *
- *  $Id$
  *
  *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
  *
index 0a868888f389d2e74004051558511b90a8dd2b88..a5217a2cf4c0fa5b96451237d136ee0d6b7907eb 100644 (file)
@@ -1,6 +1,5 @@
 /*
  *
- *  $Id$
  *
  *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
  *
 #define TV_MIN_FREQ     55250000L
 #define TV_MAX_FREQ    850000000L
 
+/* This defines a minimum interval that the decoder must remain quiet
+   before we are allowed to start it running. */
+#define TIME_MSEC_DECODER_WAIT 50
+
+/* This defines a minimum interval that the encoder must remain quiet
+   before we are allowed to configure it.  I had this originally set to
+   50msec, but Martin Dauskardt <martin.dauskardt@gmx.de> reports that
+   things work better when it's set to 100msec. */
+#define TIME_MSEC_ENCODER_WAIT 100
+
+/* This defines the minimum interval that the encoder must successfully run
+   before we consider that the encoder has run at least once since its
+   firmware has been loaded.  This measurement is in important for cases
+   where we can't do something until we know that the encoder has been run
+   at least once. */
+#define TIME_MSEC_ENCODER_OK 250
+
 static struct pvr2_hdw *unit_pointers[PVR_NUM] = {[ 0 ... PVR_NUM-1 ] = NULL};
 static DEFINE_MUTEX(pvr2_unit_mtx);
 
@@ -67,6 +83,16 @@ MODULE_PARM_DESC(video_std,"specify initial video standard");
 module_param_array(tolerance,    int, NULL, 0444);
 MODULE_PARM_DESC(tolerance,"specify stream error tolerance");
 
+/* US Broadcast channel 7 (175.25 MHz) */
+static int default_tv_freq    = 175250000L;
+/* 104.3 MHz, a usable FM station for my area */
+static int default_radio_freq = 104300000L;
+
+module_param_named(tv_freq, default_tv_freq, int, 0444);
+MODULE_PARM_DESC(tv_freq, "specify initial television frequency");
+module_param_named(radio_freq, default_radio_freq, int, 0444);
+MODULE_PARM_DESC(radio_freq, "specify initial radio frequency");
+
 #define PVR2_CTL_WRITE_ENDPOINT  0x01
 #define PVR2_CTL_READ_ENDPOINT   0x81
 
@@ -1701,10 +1727,8 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw)
           are, but I set them to something usable in the Chicago area just
           to make driver testing a little easier. */
 
-       /* US Broadcast channel 7 (175.25 MHz) */
-       hdw->freqValTelevision = 175250000L;
-       /* 104.3 MHz, a usable FM station for my area */
-       hdw->freqValRadio = 104300000L;
+       hdw->freqValTelevision = default_tv_freq;
+       hdw->freqValRadio = default_radio_freq;
 
        // Do not use pvr2_reset_ctl_endpoints() here.  It is not
        // thread-safe against the normal pvr2_send_request() mechanism.
@@ -1989,7 +2013,8 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
                case V4L2_CTRL_TYPE_MENU:
                        ciptr->type = pvr2_ctl_enum;
                        ciptr->def.type_enum.value_names =
-                               cx2341x_ctrl_get_menu(ciptr->v4l_id);
+                               cx2341x_ctrl_get_menu(&hdw->enc_ctl_state,
+                                                               ciptr->v4l_id);
                        for (cnt1 = 0;
                             ciptr->def.type_enum.value_names[cnt1] != NULL;
                             cnt1++) { }
@@ -2428,22 +2453,38 @@ static int pvr2_hdw_commit_execute(struct pvr2_hdw *hdw)
        struct pvr2_ctrl *cptr;
        int disruptive_change;
 
-       /* When video standard changes, reset the hres and vres values -
-          but if the user has pending changes there, then let the changes
-          take priority. */
+       /* Handle some required side effects when the video standard is
+          changed.... */
        if (hdw->std_dirty) {
-               /* Rewrite the vertical resolution to be appropriate to the
-                  video standard that has been selected. */
                int nvres;
+               int gop_size;
                if (hdw->std_mask_cur & V4L2_STD_525_60) {
                        nvres = 480;
+                       gop_size = 15;
                } else {
                        nvres = 576;
+                       gop_size = 12;
                }
+               /* Rewrite the vertical resolution to be appropriate to the
+                  video standard that has been selected. */
                if (nvres != hdw->res_ver_val) {
                        hdw->res_ver_val = nvres;
                        hdw->res_ver_dirty = !0;
                }
+               /* Rewrite the GOP size to be appropriate to the video
+                  standard that has been selected. */
+               if (gop_size != hdw->enc_ctl_state.video_gop_size) {
+                       struct v4l2_ext_controls cs;
+                       struct v4l2_ext_control c1;
+                       memset(&cs, 0, sizeof(cs));
+                       memset(&c1, 0, sizeof(c1));
+                       cs.controls = &c1;
+                       cs.count = 1;
+                       c1.id = V4L2_CID_MPEG_VIDEO_GOP_SIZE;
+                       c1.value = gop_size;
+                       cx2341x_ext_ctrls(&hdw->enc_ctl_state, 0, &cs,
+                                         VIDIOC_S_EXT_CTRLS);
+               }
        }
 
        if (hdw->input_dirty && hdw->state_pathway_ok &&
@@ -3421,7 +3462,7 @@ static void pvr2_hdw_cmd_modeswitch(struct pvr2_hdw *hdw,int digitalFl)
 }
 
 
-void pvr2_led_ctrl_hauppauge(struct pvr2_hdw *hdw, int onoff)
+static void pvr2_led_ctrl_hauppauge(struct pvr2_hdw *hdw, int onoff)
 {
        /* change some GPIO data
         *
@@ -3601,7 +3642,9 @@ static int state_eval_encoder_config(struct pvr2_hdw *hdw)
                                   the encoder. */
                                if (!hdw->state_encoder_waitok) {
                                        hdw->encoder_wait_timer.expires =
-                                               jiffies + (HZ*50/1000);
+                                               jiffies +
+                                               (HZ * TIME_MSEC_ENCODER_WAIT
+                                                / 1000);
                                        add_timer(&hdw->encoder_wait_timer);
                                }
                        }
@@ -3725,7 +3768,7 @@ static int state_eval_encoder_run(struct pvr2_hdw *hdw)
                hdw->state_encoder_run = !0;
                if (!hdw->state_encoder_runok) {
                        hdw->encoder_run_timer.expires =
-                               jiffies + (HZ*250/1000);
+                               jiffies + (HZ * TIME_MSEC_ENCODER_OK / 1000);
                        add_timer(&hdw->encoder_run_timer);
                }
        }
@@ -3800,7 +3843,9 @@ static int state_eval_decoder_run(struct pvr2_hdw *hdw)
                                   but before we did the pending check. */
                                if (!hdw->state_decoder_quiescent) {
                                        hdw->quiescent_timer.expires =
-                                               jiffies + (HZ*50/1000);
+                                               jiffies +
+                                               (HZ * TIME_MSEC_DECODER_WAIT
+                                                / 1000);
                                        add_timer(&hdw->quiescent_timer);
                                }
                        }
index 20295e0c19954efe6e3784a781aa0c6c445551ac..c04956d304a7da07814030a29543fcf1c0d55651 100644 (file)
@@ -1,6 +1,5 @@
 /*
  *
- *  $Id$
  *
  *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
  *
index 49773764383b73eac164ba6e768f34405aa0d20f..ccdb429fc7afc8f5b91fa1574aeb417a0a194b41 100644 (file)
@@ -1,6 +1,5 @@
 /*
  *
- *  $Id$
  *
  *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
  *
index c650e02ccd005d13fe4f8c9debc73d07fbc374f3..55f04a0b2047eca152455e7a87c3db1d22e86f4f 100644 (file)
@@ -1,6 +1,5 @@
 /*
  *
- *  $Id$
  *
  *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
  *  Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
index c838df6167f96ad4f715d95fd2e8018a9d4d5447..7fa38683b3b1f309bc2f23835cf63d7d05a308c4 100644 (file)
@@ -1,6 +1,5 @@
 /*
  *
- *  $Id$
  *
  *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
  *  Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
index 793c89a8d672c513c2ea72fb54732008e9c7dd48..9d3c18b24744f6cb1e3d3db8c29c6050a4b65c21 100644 (file)
@@ -1,6 +1,5 @@
 /*
  *
- *  $Id$
  *
  *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
  *
index bd0807b905bbdb9ce4d4039ab7be099c59d1ea84..6ef7a1c0e93595630a23ff3b9eabd6c61a99d29e 100644 (file)
@@ -1,6 +1,5 @@
 /*
  *
- *  $Id$
  *
  *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
  *
index 7aff8b720064c864226ed0502d4ba60365c95f1e..20b6ae0bb40d613c89614c655ca9b7b110a5aa5f 100644 (file)
@@ -1,6 +1,5 @@
 /*
  *
- *  $Id$
  *
  *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
  *
index 42fcf8281a879c10624157043bb7681bcac29ddc..afb7e87c0394dd90fae97ed0bbe06c698f546318 100644 (file)
@@ -1,6 +1,5 @@
 /*
  *
- *  $Id$
  *
  *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
  *
index c572212c9f158feddba21f5757ad8018016a73a5..05a1376405e794d85ddfbcf9d569314519e8c925 100644 (file)
@@ -1,6 +1,5 @@
 /*
  *
- *  $Id$
  *
  *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
  *
index 1d362f83358866aafc8fb57b5934201b4fa6223a..100e0780e1aac4f11e276133ab69e85c116cb539 100644 (file)
@@ -1,6 +1,5 @@
 /*
  *
- *  $Id$
  *
  *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
  *
index 332aced8a5a1f04d182c19acbe600f474900ff4c..ad0d98c2ebb4165ab34ef638f7786bd89463a30e 100644 (file)
@@ -1,6 +1,5 @@
 /*
  *
- *  $Id$
  *
  *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
  *  Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
index fdc5a2b49ca8568cf73adb1b3f5fe6d69859a814..ca9f83a85ca50c0f3f04e721f8e71d6a7739e669 100644 (file)
@@ -1,6 +1,5 @@
 /*
  *
- *  $Id$
  *
  *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
  *
index 07c3993753411522ffd48cc8c4725de399ddec82..a35c53d0b320b5937b6ef96cfe0d3785ad1a7318 100644 (file)
@@ -1,6 +1,5 @@
 /*
  *
- *  $Id$
  *
  *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
  *
index 0ff7a836a8a2493e926ad102664a7814333d026a..46a8c39ba03028fc07ecd7392d2fd44522779869 100644 (file)
@@ -1,6 +1,5 @@
 /*
  *
- *  $Id$
  *
  *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
  *
@@ -71,6 +70,7 @@ struct pvr2_sysfs_ctl_item {
        struct device_attribute attr_val;
        struct device_attribute attr_custom;
        struct pvr2_ctrl *cptr;
+       int ctl_id;
        struct pvr2_sysfs *chptr;
        struct pvr2_sysfs_ctl_item *item_next;
        struct attribute *attr_gen[7];
@@ -83,38 +83,29 @@ struct pvr2_sysfs_class {
        struct class class;
 };
 
-static ssize_t show_name(int id,struct device *class_dev,char *buf)
+static ssize_t show_name(struct device *class_dev,
+                        struct device_attribute *attr,
+                        char *buf)
 {
-       struct pvr2_ctrl *cptr;
-       struct pvr2_sysfs *sfp;
+       struct pvr2_sysfs_ctl_item *cip;
        const char *name;
-
-       sfp = (struct pvr2_sysfs *)class_dev->driver_data;
-       if (!sfp) return -EINVAL;
-       cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id);
-       if (!cptr) return -EINVAL;
-
-       name = pvr2_ctrl_get_desc(cptr);
-       pvr2_sysfs_trace("pvr2_sysfs(%p) show_name(cid=%d) is %s",sfp,id,name);
-
+       cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_name);
+       name = pvr2_ctrl_get_desc(cip->cptr);
+       pvr2_sysfs_trace("pvr2_sysfs(%p) show_name(cid=%d) is %s",
+                        cip->chptr, cip->ctl_id, name);
        if (!name) return -EINVAL;
-
-       return scnprintf(buf,PAGE_SIZE,"%s\n",name);
+       return scnprintf(buf, PAGE_SIZE, "%s\n", name);
 }
 
-static ssize_t show_type(int id,struct device *class_dev,char *buf)
+static ssize_t show_type(struct device *class_dev,
+                        struct device_attribute *attr,
+                        char *buf)
 {
-       struct pvr2_ctrl *cptr;
-       struct pvr2_sysfs *sfp;
+       struct pvr2_sysfs_ctl_item *cip;
        const char *name;
        enum pvr2_ctl_type tp;
-
-       sfp = (struct pvr2_sysfs *)class_dev->driver_data;
-       if (!sfp) return -EINVAL;
-       cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id);
-       if (!cptr) return -EINVAL;
-
-       tp = pvr2_ctrl_get_type(cptr);
+       cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_type);
+       tp = pvr2_ctrl_get_type(cip->cptr);
        switch (tp) {
        case pvr2_ctl_int: name = "integer"; break;
        case pvr2_ctl_enum: name = "enum"; break;
@@ -122,403 +113,178 @@ static ssize_t show_type(int id,struct device *class_dev,char *buf)
        case pvr2_ctl_bool: name = "boolean"; break;
        default: name = "?"; break;
        }
-       pvr2_sysfs_trace("pvr2_sysfs(%p) show_type(cid=%d) is %s",sfp,id,name);
-
+       pvr2_sysfs_trace("pvr2_sysfs(%p) show_type(cid=%d) is %s",
+                        cip->chptr, cip->ctl_id, name);
        if (!name) return -EINVAL;
-
-       return scnprintf(buf,PAGE_SIZE,"%s\n",name);
+       return scnprintf(buf, PAGE_SIZE, "%s\n", name);
 }
 
-static ssize_t show_min(int id,struct device *class_dev,char *buf)
+static ssize_t show_min(struct device *class_dev,
+                       struct device_attribute *attr,
+                       char *buf)
 {
-       struct pvr2_ctrl *cptr;
-       struct pvr2_sysfs *sfp;
+       struct pvr2_sysfs_ctl_item *cip;
        long val;
-
-       sfp = (struct pvr2_sysfs *)class_dev->driver_data;
-       if (!sfp) return -EINVAL;
-       cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id);
-       if (!cptr) return -EINVAL;
-       val = pvr2_ctrl_get_min(cptr);
-
-       pvr2_sysfs_trace("pvr2_sysfs(%p) show_min(cid=%d) is %ld",sfp,id,val);
-
-       return scnprintf(buf,PAGE_SIZE,"%ld\n",val);
+       cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_min);
+       val = pvr2_ctrl_get_min(cip->cptr);
+       pvr2_sysfs_trace("pvr2_sysfs(%p) show_min(cid=%d) is %ld",
+                        cip->chptr, cip->ctl_id, val);
+       return scnprintf(buf, PAGE_SIZE, "%ld\n", val);
 }
 
-static ssize_t show_max(int id,struct device *class_dev,char *buf)
+static ssize_t show_max(struct device *class_dev,
+                       struct device_attribute *attr,
+                       char *buf)
 {
-       struct pvr2_ctrl *cptr;
-       struct pvr2_sysfs *sfp;
+       struct pvr2_sysfs_ctl_item *cip;
        long val;
-
-       sfp = (struct pvr2_sysfs *)class_dev->driver_data;
-       if (!sfp) return -EINVAL;
-       cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id);
-       if (!cptr) return -EINVAL;
-       val = pvr2_ctrl_get_max(cptr);
-
-       pvr2_sysfs_trace("pvr2_sysfs(%p) show_max(cid=%d) is %ld",sfp,id,val);
-
-       return scnprintf(buf,PAGE_SIZE,"%ld\n",val);
+       cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_max);
+       val = pvr2_ctrl_get_max(cip->cptr);
+       pvr2_sysfs_trace("pvr2_sysfs(%p) show_max(cid=%d) is %ld",
+                        cip->chptr, cip->ctl_id, val);
+       return scnprintf(buf, PAGE_SIZE, "%ld\n", val);
 }
 
-static ssize_t show_val_norm(int id,struct device *class_dev,char *buf)
+static ssize_t show_val_norm(struct device *class_dev,
+                            struct device_attribute *attr,
+                            char *buf)
 {
-       struct pvr2_ctrl *cptr;
-       struct pvr2_sysfs *sfp;
-       int val,ret;
+       struct pvr2_sysfs_ctl_item *cip;
+       int val;
+       int ret;
        unsigned int cnt = 0;
-
-       sfp = (struct pvr2_sysfs *)class_dev->driver_data;
-       if (!sfp) return -EINVAL;
-       cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id);
-       if (!cptr) return -EINVAL;
-
-       ret = pvr2_ctrl_get_value(cptr,&val);
+       cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_val);
+       ret = pvr2_ctrl_get_value(cip->cptr, &val);
        if (ret < 0) return ret;
-
-       ret = pvr2_ctrl_value_to_sym(cptr,~0,val,
-                                    buf,PAGE_SIZE-1,&cnt);
-
+       ret = pvr2_ctrl_value_to_sym(cip->cptr, ~0, val,
+                                    buf, PAGE_SIZE - 1, &cnt);
        pvr2_sysfs_trace("pvr2_sysfs(%p) show_val_norm(cid=%d) is %.*s (%d)",
-                        sfp,id,cnt,buf,val);
+                        cip->chptr, cip->ctl_id, cnt, buf, val);
        buf[cnt] = '\n';
        return cnt+1;
 }
 
-static ssize_t show_val_custom(int id,struct device *class_dev,char *buf)
+static ssize_t show_val_custom(struct device *class_dev,
+                              struct device_attribute *attr,
+                              char *buf)
 {
-       struct pvr2_ctrl *cptr;
-       struct pvr2_sysfs *sfp;
-       int val,ret;
+       struct pvr2_sysfs_ctl_item *cip;
+       int val;
+       int ret;
        unsigned int cnt = 0;
-
-       sfp = (struct pvr2_sysfs *)class_dev->driver_data;
-       if (!sfp) return -EINVAL;
-       cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id);
-       if (!cptr) return -EINVAL;
-
-       ret = pvr2_ctrl_get_value(cptr,&val);
+       cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_custom);
+       ret = pvr2_ctrl_get_value(cip->cptr, &val);
        if (ret < 0) return ret;
-
-       ret = pvr2_ctrl_custom_value_to_sym(cptr,~0,val,
-                                           buf,PAGE_SIZE-1,&cnt);
-
+       ret = pvr2_ctrl_custom_value_to_sym(cip->cptr, ~0, val,
+                                           buf, PAGE_SIZE - 1, &cnt);
        pvr2_sysfs_trace("pvr2_sysfs(%p) show_val_custom(cid=%d) is %.*s (%d)",
-                        sfp,id,cnt,buf,val);
+                        cip->chptr, cip->ctl_id, cnt, buf, val);
        buf[cnt] = '\n';
        return cnt+1;
 }
 
-static ssize_t show_enum(int id,struct device *class_dev,char *buf)
+static ssize_t show_enum(struct device *class_dev,
+                        struct device_attribute *attr,
+                        char *buf)
 {
-       struct pvr2_ctrl *cptr;
-       struct pvr2_sysfs *sfp;
+       struct pvr2_sysfs_ctl_item *cip;
        long val;
-       unsigned int bcnt,ccnt,ecnt;
-
-       sfp = (struct pvr2_sysfs *)class_dev->driver_data;
-       if (!sfp) return -EINVAL;
-       cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id);
-       if (!cptr) return -EINVAL;
-       ecnt = pvr2_ctrl_get_cnt(cptr);
+       unsigned int bcnt, ccnt, ecnt;
+       cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_enum);
+       ecnt = pvr2_ctrl_get_cnt(cip->cptr);
        bcnt = 0;
        for (val = 0; val < ecnt; val++) {
-               pvr2_ctrl_get_valname(cptr,val,buf+bcnt,PAGE_SIZE-bcnt,&ccnt);
+               pvr2_ctrl_get_valname(cip->cptr, val, buf + bcnt,
+                                     PAGE_SIZE - bcnt, &ccnt);
                if (!ccnt) continue;
                bcnt += ccnt;
                if (bcnt >= PAGE_SIZE) break;
                buf[bcnt] = '\n';
                bcnt++;
        }
-       pvr2_sysfs_trace("pvr2_sysfs(%p) show_enum(cid=%d)",sfp,id);
+       pvr2_sysfs_trace("pvr2_sysfs(%p) show_enum(cid=%d)",
+                        cip->chptr, cip->ctl_id);
        return bcnt;
 }
 
-static ssize_t show_bits(int id,struct device *class_dev,char *buf)
+static ssize_t show_bits(struct device *class_dev,
+                        struct device_attribute *attr,
+                        char *buf)
 {
-       struct pvr2_ctrl *cptr;
-       struct pvr2_sysfs *sfp;
-       int valid_bits,msk;
-       unsigned int bcnt,ccnt;
-
-       sfp = (struct pvr2_sysfs *)class_dev->driver_data;
-       if (!sfp) return -EINVAL;
-       cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id);
-       if (!cptr) return -EINVAL;
-       valid_bits = pvr2_ctrl_get_mask(cptr);
+       struct pvr2_sysfs_ctl_item *cip;
+       int valid_bits, msk;
+       unsigned int bcnt, ccnt;
+       cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_bits);
+       valid_bits = pvr2_ctrl_get_mask(cip->cptr);
        bcnt = 0;
        for (msk = 1; valid_bits; msk <<= 1) {
                if (!(msk & valid_bits)) continue;
                valid_bits &= ~msk;
-               pvr2_ctrl_get_valname(cptr,msk,buf+bcnt,PAGE_SIZE-bcnt,&ccnt);
+               pvr2_ctrl_get_valname(cip->cptr, msk, buf + bcnt,
+                                     PAGE_SIZE - bcnt, &ccnt);
                bcnt += ccnt;
                if (bcnt >= PAGE_SIZE) break;
                buf[bcnt] = '\n';
                bcnt++;
        }
-       pvr2_sysfs_trace("pvr2_sysfs(%p) show_bits(cid=%d)",sfp,id);
+       pvr2_sysfs_trace("pvr2_sysfs(%p) show_bits(cid=%d)",
+                        cip->chptr, cip->ctl_id);
        return bcnt;
 }
 
-static int store_val_any(int id,int customfl,struct pvr2_sysfs *sfp,
+static int store_val_any(struct pvr2_sysfs_ctl_item *cip, int customfl,
                         const char *buf,unsigned int count)
 {
-       struct pvr2_ctrl *cptr;
        int ret;
        int mask,val;
-
-       cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id);
        if (customfl) {
-               ret = pvr2_ctrl_custom_sym_to_value(cptr,buf,count,&mask,&val);
+               ret = pvr2_ctrl_custom_sym_to_value(cip->cptr, buf, count,
+                                                   &mask, &val);
        } else {
-               ret = pvr2_ctrl_sym_to_value(cptr,buf,count,&mask,&val);
+               ret = pvr2_ctrl_sym_to_value(cip->cptr, buf, count,
+                                            &mask, &val);
        }
        if (ret < 0) return ret;
-       ret = pvr2_ctrl_set_mask_value(cptr,mask,val);
-       pvr2_hdw_commit_ctl(sfp->channel.hdw);
+       ret = pvr2_ctrl_set_mask_value(cip->cptr, mask, val);
+       pvr2_hdw_commit_ctl(cip->chptr->channel.hdw);
        return ret;
 }
 
-static ssize_t store_val_norm(int id,struct device *class_dev,
-                            const char *buf,size_t count)
+static ssize_t store_val_norm(struct device *class_dev,
+                             struct device_attribute *attr,
+                             const char *buf, size_t count)
 {
-       struct pvr2_sysfs *sfp;
+       struct pvr2_sysfs_ctl_item *cip;
        int ret;
-       sfp = (struct pvr2_sysfs *)class_dev->driver_data;
+       cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_val);
        pvr2_sysfs_trace("pvr2_sysfs(%p) store_val_norm(cid=%d) \"%.*s\"",
-                        sfp,id,(int)count,buf);
-       ret = store_val_any(id,0,sfp,buf,count);
+                        cip->chptr, cip->ctl_id, (int)count, buf);
+       ret = store_val_any(cip, 0, buf, count);
        if (!ret) ret = count;
        return ret;
 }
 
-static ssize_t store_val_custom(int id,struct device *class_dev,
-                               const char *buf,size_t count)
+static ssize_t store_val_custom(struct device *class_dev,
+                               struct device_attribute *attr,
+                               const char *buf, size_t count)
 {
-       struct pvr2_sysfs *sfp;
+       struct pvr2_sysfs_ctl_item *cip;
        int ret;
-       sfp = (struct pvr2_sysfs *)class_dev->driver_data;
+       cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_custom);
        pvr2_sysfs_trace("pvr2_sysfs(%p) store_val_custom(cid=%d) \"%.*s\"",
-                        sfp,id,(int)count,buf);
-       ret = store_val_any(id,1,sfp,buf,count);
+                        cip->chptr, cip->ctl_id, (int)count, buf);
+       ret = store_val_any(cip, 1, buf, count);
        if (!ret) ret = count;
        return ret;
 }
 
-/*
-  Mike Isely <isely@pobox.com> 30-April-2005
-
-  This next batch of horrible preprocessor hackery is needed because the
-  kernel's device_attribute mechanism fails to pass the actual
-  attribute through to the show / store functions, which means we have no
-  way to package up any attribute-specific parameters, like for example the
-  control id.  So we work around this brain-damage by encoding the control
-  id into the show / store functions themselves and pick the function based
-  on the control id we're setting up.  These macros try to ease the pain.
-  Yuck.
-*/
-
-#define CREATE_SHOW_INSTANCE(sf_name,ctl_id) \
-static ssize_t sf_name##_##ctl_id(struct device *class_dev, \
-struct device_attribute *attr, char *buf) \
-{ return sf_name(ctl_id,class_dev,buf); }
-
-#define CREATE_STORE_INSTANCE(sf_name,ctl_id) \
-static ssize_t sf_name##_##ctl_id(struct device *class_dev, \
-struct device_attribute *attr, const char *buf, size_t count) \
-{ return sf_name(ctl_id,class_dev,buf,count); }
-
-#define CREATE_BATCH(ctl_id) \
-CREATE_SHOW_INSTANCE(show_name,ctl_id) \
-CREATE_SHOW_INSTANCE(show_type,ctl_id) \
-CREATE_SHOW_INSTANCE(show_min,ctl_id) \
-CREATE_SHOW_INSTANCE(show_max,ctl_id) \
-CREATE_SHOW_INSTANCE(show_val_norm,ctl_id) \
-CREATE_SHOW_INSTANCE(show_val_custom,ctl_id) \
-CREATE_SHOW_INSTANCE(show_enum,ctl_id) \
-CREATE_SHOW_INSTANCE(show_bits,ctl_id) \
-CREATE_STORE_INSTANCE(store_val_norm,ctl_id) \
-CREATE_STORE_INSTANCE(store_val_custom,ctl_id) \
-
-CREATE_BATCH(0)
-CREATE_BATCH(1)
-CREATE_BATCH(2)
-CREATE_BATCH(3)
-CREATE_BATCH(4)
-CREATE_BATCH(5)
-CREATE_BATCH(6)
-CREATE_BATCH(7)
-CREATE_BATCH(8)
-CREATE_BATCH(9)
-CREATE_BATCH(10)
-CREATE_BATCH(11)
-CREATE_BATCH(12)
-CREATE_BATCH(13)
-CREATE_BATCH(14)
-CREATE_BATCH(15)
-CREATE_BATCH(16)
-CREATE_BATCH(17)
-CREATE_BATCH(18)
-CREATE_BATCH(19)
-CREATE_BATCH(20)
-CREATE_BATCH(21)
-CREATE_BATCH(22)
-CREATE_BATCH(23)
-CREATE_BATCH(24)
-CREATE_BATCH(25)
-CREATE_BATCH(26)
-CREATE_BATCH(27)
-CREATE_BATCH(28)
-CREATE_BATCH(29)
-CREATE_BATCH(30)
-CREATE_BATCH(31)
-CREATE_BATCH(32)
-CREATE_BATCH(33)
-CREATE_BATCH(34)
-CREATE_BATCH(35)
-CREATE_BATCH(36)
-CREATE_BATCH(37)
-CREATE_BATCH(38)
-CREATE_BATCH(39)
-CREATE_BATCH(40)
-CREATE_BATCH(41)
-CREATE_BATCH(42)
-CREATE_BATCH(43)
-CREATE_BATCH(44)
-CREATE_BATCH(45)
-CREATE_BATCH(46)
-CREATE_BATCH(47)
-CREATE_BATCH(48)
-CREATE_BATCH(49)
-CREATE_BATCH(50)
-CREATE_BATCH(51)
-CREATE_BATCH(52)
-CREATE_BATCH(53)
-CREATE_BATCH(54)
-CREATE_BATCH(55)
-CREATE_BATCH(56)
-CREATE_BATCH(57)
-CREATE_BATCH(58)
-CREATE_BATCH(59)
-
-struct pvr2_sysfs_func_set {
-       ssize_t (*show_name)(struct device *,
-                            struct device_attribute *attr, char *);
-       ssize_t (*show_type)(struct device *,
-                            struct device_attribute *attr, char *);
-       ssize_t (*show_min)(struct device *,
-                           struct device_attribute *attr, char *);
-       ssize_t (*show_max)(struct device *,
-                           struct device_attribute *attr, char *);
-       ssize_t (*show_enum)(struct device *,
-                            struct device_attribute *attr, char *);
-       ssize_t (*show_bits)(struct device *,
-                            struct device_attribute *attr, char *);
-       ssize_t (*show_val_norm)(struct device *,
-                                struct device_attribute *attr, char *);
-       ssize_t (*store_val_norm)(struct device *,
-                                 struct device_attribute *attr,
-                                 const char *,size_t);
-       ssize_t (*show_val_custom)(struct device *,
-                                  struct device_attribute *attr, char *);
-       ssize_t (*store_val_custom)(struct device *,
-                                   struct device_attribute *attr,
-                                   const char *,size_t);
-};
-
-#define INIT_BATCH(ctl_id) \
-[ctl_id] = { \
-    .show_name = show_name_##ctl_id, \
-    .show_type = show_type_##ctl_id, \
-    .show_min = show_min_##ctl_id, \
-    .show_max = show_max_##ctl_id, \
-    .show_enum = show_enum_##ctl_id, \
-    .show_bits = show_bits_##ctl_id, \
-    .show_val_norm = show_val_norm_##ctl_id, \
-    .store_val_norm = store_val_norm_##ctl_id, \
-    .show_val_custom = show_val_custom_##ctl_id, \
-    .store_val_custom = store_val_custom_##ctl_id, \
-} \
-
-static struct pvr2_sysfs_func_set funcs[] = {
-       INIT_BATCH(0),
-       INIT_BATCH(1),
-       INIT_BATCH(2),
-       INIT_BATCH(3),
-       INIT_BATCH(4),
-       INIT_BATCH(5),
-       INIT_BATCH(6),
-       INIT_BATCH(7),
-       INIT_BATCH(8),
-       INIT_BATCH(9),
-       INIT_BATCH(10),
-       INIT_BATCH(11),
-       INIT_BATCH(12),
-       INIT_BATCH(13),
-       INIT_BATCH(14),
-       INIT_BATCH(15),
-       INIT_BATCH(16),
-       INIT_BATCH(17),
-       INIT_BATCH(18),
-       INIT_BATCH(19),
-       INIT_BATCH(20),
-       INIT_BATCH(21),
-       INIT_BATCH(22),
-       INIT_BATCH(23),
-       INIT_BATCH(24),
-       INIT_BATCH(25),
-       INIT_BATCH(26),
-       INIT_BATCH(27),
-       INIT_BATCH(28),
-       INIT_BATCH(29),
-       INIT_BATCH(30),
-       INIT_BATCH(31),
-       INIT_BATCH(32),
-       INIT_BATCH(33),
-       INIT_BATCH(34),
-       INIT_BATCH(35),
-       INIT_BATCH(36),
-       INIT_BATCH(37),
-       INIT_BATCH(38),
-       INIT_BATCH(39),
-       INIT_BATCH(40),
-       INIT_BATCH(41),
-       INIT_BATCH(42),
-       INIT_BATCH(43),
-       INIT_BATCH(44),
-       INIT_BATCH(45),
-       INIT_BATCH(46),
-       INIT_BATCH(47),
-       INIT_BATCH(48),
-       INIT_BATCH(49),
-       INIT_BATCH(50),
-       INIT_BATCH(51),
-       INIT_BATCH(52),
-       INIT_BATCH(53),
-       INIT_BATCH(54),
-       INIT_BATCH(55),
-       INIT_BATCH(56),
-       INIT_BATCH(57),
-       INIT_BATCH(58),
-       INIT_BATCH(59),
-};
-
-
 static void pvr2_sysfs_add_control(struct pvr2_sysfs *sfp,int ctl_id)
 {
        struct pvr2_sysfs_ctl_item *cip;
-       struct pvr2_sysfs_func_set *fp;
        struct pvr2_ctrl *cptr;
        unsigned int cnt,acnt;
        int ret;
 
-       if ((ctl_id < 0) || (ctl_id >= ARRAY_SIZE(funcs))) {
-               return;
-       }
-
-       fp = funcs + ctl_id;
        cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,ctl_id);
        if (!cptr) return;
 
@@ -527,6 +293,7 @@ static void pvr2_sysfs_add_control(struct pvr2_sysfs *sfp,int ctl_id)
        pvr2_sysfs_trace("Creating pvr2_sysfs_ctl_item id=%p",cip);
 
        cip->cptr = cptr;
+       cip->ctl_id = ctl_id;
 
        cip->chptr = sfp;
        cip->item_next = NULL;
@@ -539,19 +306,19 @@ static void pvr2_sysfs_add_control(struct pvr2_sysfs *sfp,int ctl_id)
 
        cip->attr_name.attr.name = "name";
        cip->attr_name.attr.mode = S_IRUGO;
-       cip->attr_name.show = fp->show_name;
+       cip->attr_name.show = show_name;
 
        cip->attr_type.attr.name = "type";
        cip->attr_type.attr.mode = S_IRUGO;
-       cip->attr_type.show = fp->show_type;
+       cip->attr_type.show = show_type;
 
        cip->attr_min.attr.name = "min_val";
        cip->attr_min.attr.mode = S_IRUGO;
-       cip->attr_min.show = fp->show_min;
+       cip->attr_min.show = show_min;
 
        cip->attr_max.attr.name = "max_val";
        cip->attr_max.attr.mode = S_IRUGO;
-       cip->attr_max.show = fp->show_max;
+       cip->attr_max.show = show_max;
 
        cip->attr_val.attr.name = "cur_val";
        cip->attr_val.attr.mode = S_IRUGO;
@@ -561,11 +328,11 @@ static void pvr2_sysfs_add_control(struct pvr2_sysfs *sfp,int ctl_id)
 
        cip->attr_enum.attr.name = "enum_val";
        cip->attr_enum.attr.mode = S_IRUGO;
-       cip->attr_enum.show = fp->show_enum;
+       cip->attr_enum.show = show_enum;
 
        cip->attr_bits.attr.name = "bit_val";
        cip->attr_bits.attr.mode = S_IRUGO;
-       cip->attr_bits.show = fp->show_bits;
+       cip->attr_bits.show = show_bits;
 
        if (pvr2_ctrl_is_writable(cptr)) {
                cip->attr_val.attr.mode |= S_IWUSR|S_IWGRP;
@@ -576,12 +343,12 @@ static void pvr2_sysfs_add_control(struct pvr2_sysfs *sfp,int ctl_id)
        cip->attr_gen[acnt++] = &cip->attr_name.attr;
        cip->attr_gen[acnt++] = &cip->attr_type.attr;
        cip->attr_gen[acnt++] = &cip->attr_val.attr;
-       cip->attr_val.show = fp->show_val_norm;
-       cip->attr_val.store = fp->store_val_norm;
+       cip->attr_val.show = show_val_norm;
+       cip->attr_val.store = store_val_norm;
        if (pvr2_ctrl_has_custom_symbols(cptr)) {
                cip->attr_gen[acnt++] = &cip->attr_custom.attr;
-               cip->attr_custom.show = fp->show_val_custom;
-               cip->attr_custom.store = fp->store_val_custom;
+               cip->attr_custom.show = show_val_custom;
+               cip->attr_custom.store = store_val_custom;
        }
        switch (pvr2_ctrl_get_type(cptr)) {
        case pvr2_ctl_enum:
index ff9373b47f8fb4734b7d48c1951ef948deb41636..6d875bfe7991e182b7e5161525a4cc8f57aee429 100644 (file)
@@ -1,6 +1,5 @@
 /*
  *
- *  $Id$
  *
  *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
  *
index 05e65ce2e3a96109684695c841b2cb06675cbc58..07775d1aad4efb0e8661bef9b74800551128258f 100644 (file)
@@ -1,6 +1,5 @@
 /*
  *
- *  $Id$
  *
  *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
  *  Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
index 556f12aa916068163ea56f2493b604b97dd33547..ef4afaf37b0afde085574e01b1fd9d7c8cd7dfc1 100644 (file)
@@ -1,6 +1,5 @@
 /*
  *
- *  $Id$
  *
  *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
  *
index e53aee416f5659eab3f53ed8b13e92b63a62e80c..92b75544ee2e5901122f18b30276a46d1e9c4a05 100644 (file)
@@ -1,6 +1,5 @@
 /*
  *
- *  $Id$
  *
  *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
  *
index e9b5d4e913277ebc25348c8f2c71edd231df5fb3..0d72dc470fef871a16b24d35cb339b92badcf313 100644 (file)
@@ -1,6 +1,5 @@
 /*
  *
- *  $Id$
  *
  *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
  *  Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
index 9a995e2d2256d28898882eedfa24df026a827ecd..34c011a7b107cc92161e37108d9bc63f9e4c0753 100644 (file)
@@ -1,6 +1,5 @@
 /*
  *
- *  $Id$
  *
  *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
  *
index 2433a31600413cbf40f6276acd5d83dd50b8094b..4059648c70563341844c90697a79f03782b37ec2 100644 (file)
@@ -1,6 +1,5 @@
 /*
  *
- *  $Id$
  *
  *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
  *  Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
index 2b917fda02e4029b17d77c11aef4c47bdb34c2ba..4ff5b892b30338f66ddbedc940b570ed95d00e66 100644 (file)
@@ -1,6 +1,5 @@
 /*
  *
- *  $Id$
  *
  *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
  *  Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
index 66b4d36ef76585f23af0c7e44cc7334e15490c17..f6fcf0ac61189ef33e0d9c3da977e12e8ce9c2a4 100644 (file)
@@ -1,6 +1,5 @@
 /*
  *
- *  $Id$
  *
  *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
  *  Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
index 8aaeff4e1e20689d1eeb3e27dc5a171ebccba980..807090961255dd101698f4989e305d4605a0037f 100644 (file)
@@ -1,6 +1,5 @@
 /*
  *
- *  $Id$
  *
  *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
  *  Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
index 1a9a4baf12b84664821c42b862789f68a4fcf20d..240de9b356614ff5ae12176297029dc0634affe3 100644 (file)
@@ -1,6 +1,5 @@
 /*
  *
- *  $Id$
  *
  *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
  *  Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
index ea53316d2111fdb2141fc8840755a6b57ce2fe94..1cccd5c77048955502d00fe4732e3999c559914f 100644 (file)
@@ -1255,7 +1255,6 @@ int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor)
    exactly the same otherwise.
  */
 
-
 /* define local variable for arg */
 #define ARG_DEF(ARG_type, ARG_name)\
        ARG_type *ARG_name = arg;
@@ -1268,7 +1267,6 @@ int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor)
 /* copy local variable to arg */
 #define ARG_OUT(ARG_name) /* nothing */
 
-
 int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg)
 {
        int ret = 0;
index cec6602997682fd3aea04be30ee57eb546c5c303..8c0cae7b3dafa3d3014e9de0a4843de03ac99d5e 100644 (file)
@@ -54,7 +54,6 @@
 #include <linux/types.h>
 #include <linux/version.h>
 
-
  /* Enumeration of image sizes */
 #define PSZ_SQCIF      0x00
 #define PSZ_QSIF       0x01
index 5ec5bb9a94d2e848095d8e737eb2936e6803b14d..b15f82c497662550e4404611e9f1b0aa9e3811cc 100644 (file)
@@ -30,6 +30,7 @@
 
 #include <media/v4l2-common.h>
 #include <media/v4l2-dev.h>
+#include <media/videobuf-dma-sg.h>
 #include <media/soc_camera.h>
 
 #include <linux/videodev2.h>
@@ -582,6 +583,19 @@ static struct videobuf_queue_ops pxa_videobuf_ops = {
        .buf_release    = pxa_videobuf_release,
 };
 
+static void pxa_camera_init_videobuf(struct videobuf_queue *q,
+                             struct soc_camera_device *icd)
+{
+       struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+       struct pxa_camera_dev *pcdev = ici->priv;
+
+       /* We must pass NULL as dev pointer, then all pci_* dma operations
+        * transform to normal dma_* ones. */
+       videobuf_queue_sg_init(q, &pxa_videobuf_ops, NULL, &pcdev->lock,
+                               V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE,
+                               sizeof(struct pxa_buffer), icd);
+}
+
 static int mclk_get_divisor(struct pxa_camera_dev *pcdev)
 {
        unsigned int mclk_10khz = pcdev->platform_mclk_10khz;
@@ -983,34 +997,23 @@ static int pxa_camera_querycap(struct soc_camera_host *ici,
        return 0;
 }
 
-static spinlock_t *pxa_camera_spinlock_alloc(struct soc_camera_file *icf)
-{
-       struct soc_camera_host *ici =
-               to_soc_camera_host(icf->icd->dev.parent);
-       struct pxa_camera_dev *pcdev = ici->priv;
-
-       return &pcdev->lock;
-}
-
 static struct soc_camera_host_ops pxa_soc_camera_host_ops = {
        .owner          = THIS_MODULE,
        .add            = pxa_camera_add_device,
        .remove         = pxa_camera_remove_device,
        .set_fmt_cap    = pxa_camera_set_fmt_cap,
        .try_fmt_cap    = pxa_camera_try_fmt_cap,
+       .init_videobuf  = pxa_camera_init_videobuf,
        .reqbufs        = pxa_camera_reqbufs,
        .poll           = pxa_camera_poll,
        .querycap       = pxa_camera_querycap,
        .try_bus_param  = pxa_camera_try_bus_param,
        .set_bus_param  = pxa_camera_set_bus_param,
-       .spinlock_alloc = pxa_camera_spinlock_alloc,
 };
 
 /* Should be allocated dynamically too, but we have only one. */
 static struct soc_camera_host pxa_soc_camera_host = {
        .drv_name               = PXA_CAM_DRV_NAME,
-       .vbq_ops                = &pxa_videobuf_ops,
-       .msize                  = sizeof(struct pxa_buffer),
        .ops                    = &pxa_soc_camera_host_ops,
 };
 
diff --git a/drivers/media/video/s2255drv.c b/drivers/media/video/s2255drv.c
new file mode 100644 (file)
index 0000000..04eb2c3
--- /dev/null
@@ -0,0 +1,2495 @@
+/*
+ *  s2255drv.c - a driver for the Sensoray 2255 USB video capture device
+ *
+ *   Copyright (C) 2007-2008 by Sensoray Company Inc.
+ *                              Dean Anderson
+ *
+ * Some video buffer code based on vivi driver:
+ *
+ * Sensoray 2255 device supports 4 simultaneous channels.
+ * The channels are not "crossbar" inputs, they are physically
+ * attached to separate video decoders.
+ *
+ * Because of USB2.0 bandwidth limitations. There is only a
+ * certain amount of data which may be transferred at one time.
+ *
+ * Example maximum bandwidth utilization:
+ *
+ * -full size, color mode YUYV or YUV422P: 2 channels at once
+ *
+ * -full or half size Grey scale: all 4 channels at once
+ *
+ * -half size, color mode YUYV or YUV422P: all 4 channels at once
+ *
+ * -full size, color mode YUYV or YUV422P 1/2 frame rate: all 4 channels
+ *  at once.
+ *  (TODO: Incorporate videodev2 frame rate(FR) enumeration,
+ *  which is currently experimental.)
+ *
+ * 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/module.h>
+#include <linux/firmware.h>
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/videodev2.h>
+#include <linux/version.h>
+#include <media/videobuf-vmalloc.h>
+#include <media/v4l2-common.h>
+#include <linux/vmalloc.h>
+#include <linux/usb.h>
+
+#define FIRMWARE_FILE_NAME "f2255usb.bin"
+
+
+
+/* vendor request in */
+#define S2255_VR_IN            0
+/* vendor request out */
+#define S2255_VR_OUT           1
+/* firmware query */
+#define S2255_VR_FW            0x30
+/* USB endpoint number for configuring the device */
+#define S2255_CONFIG_EP         2
+/* maximum time for DSP to start responding after last FW word loaded(ms) */
+#define S2255_DSP_BOOTTIME      400
+/* maximum time to wait for firmware to load (ms) */
+#define S2255_LOAD_TIMEOUT      (5000 + S2255_DSP_BOOTTIME)
+#define S2255_DEF_BUFS          16
+#define MAX_CHANNELS           4
+#define FRAME_MARKER           0x2255DA4AL
+#define MAX_PIPE_USBBLOCK      (40 * 1024)
+#define DEFAULT_PIPE_USBBLOCK  (16 * 1024)
+#define MAX_CHANNELS           4
+#define MAX_PIPE_BUFFERS       1
+#define SYS_FRAMES             4
+/* maximum size is PAL full size plus room for the marker header(s) */
+#define SYS_FRAMES_MAXSIZE     (720 * 288 * 2 * 2 + 4096)
+#define DEF_USB_BLOCK          (4096)
+#define LINE_SZ_4CIFS_NTSC     640
+#define LINE_SZ_2CIFS_NTSC     640
+#define LINE_SZ_1CIFS_NTSC     320
+#define LINE_SZ_4CIFS_PAL      704
+#define LINE_SZ_2CIFS_PAL      704
+#define LINE_SZ_1CIFS_PAL      352
+#define NUM_LINES_4CIFS_NTSC   240
+#define NUM_LINES_2CIFS_NTSC   240
+#define NUM_LINES_1CIFS_NTSC   240
+#define NUM_LINES_4CIFS_PAL    288
+#define NUM_LINES_2CIFS_PAL    288
+#define NUM_LINES_1CIFS_PAL    288
+#define LINE_SZ_DEF            640
+#define NUM_LINES_DEF          240
+
+
+/* predefined settings */
+#define FORMAT_NTSC    1
+#define FORMAT_PAL     2
+
+#define SCALE_4CIFS    1       /* 640x480(NTSC) or 704x576(PAL) */
+#define SCALE_2CIFS    2       /* 640x240(NTSC) or 704x288(PAL) */
+#define SCALE_1CIFS    3       /* 320x240(NTSC) or 352x288(PAL) */
+
+#define COLOR_YUVPL    1       /* YUV planar */
+#define COLOR_YUVPK    2       /* YUV packed */
+#define COLOR_Y8       4       /* monochrome */
+
+/* frame decimation. Not implemented by V4L yet(experimental in V4L) */
+#define FDEC_1         1       /* capture every frame. default */
+#define FDEC_2         2       /* capture every 2nd frame */
+#define FDEC_3         3       /* capture every 3rd frame */
+#define FDEC_5         5       /* capture every 5th frame */
+
+/*-------------------------------------------------------
+ * Default mode parameters.
+ *-------------------------------------------------------*/
+#define DEF_SCALE      SCALE_4CIFS
+#define DEF_COLOR      COLOR_YUVPL
+#define DEF_FDEC       FDEC_1
+#define DEF_BRIGHT     0
+#define DEF_CONTRAST   0x5c
+#define DEF_SATURATION 0x80
+#define DEF_HUE                0
+
+/* usb config commands */
+#define IN_DATA_TOKEN  0x2255c0de
+#define CMD_2255       0xc2255000
+#define CMD_SET_MODE   (CMD_2255 | 0x10)
+#define CMD_START      (CMD_2255 | 0x20)
+#define CMD_STOP       (CMD_2255 | 0x30)
+#define CMD_STATUS     (CMD_2255 | 0x40)
+
+struct s2255_mode {
+       u32 format;     /* input video format (NTSC, PAL) */
+       u32 scale;      /* output video scale */
+       u32 color;      /* output video color format */
+       u32 fdec;       /* frame decimation */
+       u32 bright;     /* brightness */
+       u32 contrast;   /* contrast */
+       u32 saturation; /* saturation */
+       u32 hue;        /* hue (NTSC only)*/
+       u32 single;     /* capture 1 frame at a time (!=0), continuously (==0)*/
+       u32 usb_block;  /* block size. should be 4096 of DEF_USB_BLOCK */
+       u32 restart;    /* if DSP requires restart */
+};
+
+/* frame structure */
+#define FRAME_STATE_UNUSED     0
+#define FRAME_STATE_FILLING    1
+#define FRAME_STATE_FULL       2
+
+
+struct s2255_framei {
+       unsigned long size;
+
+       unsigned long ulState;  /* ulState ==0 unused, 1 being filled, 2 full */
+       void *lpvbits;          /* image data */
+       unsigned long cur_size; /* current data copied to it */
+};
+
+/* image buffer structure */
+struct s2255_bufferi {
+       unsigned long dwFrames;                 /* number of frames in buffer */
+       struct s2255_framei frame[SYS_FRAMES];  /* array of FRAME structures */
+};
+
+#define DEF_MODEI_NTSC_CONT    {FORMAT_NTSC, DEF_SCALE, DEF_COLOR,     \
+                       DEF_FDEC, DEF_BRIGHT, DEF_CONTRAST, DEF_SATURATION, \
+                       DEF_HUE, 0, DEF_USB_BLOCK, 0}
+
+struct s2255_dmaqueue {
+       struct list_head        active;
+       /* thread for acquisition */
+       struct task_struct      *kthread;
+       int                     frame;
+       struct s2255_dev        *dev;
+       int                     channel;
+};
+
+/* for firmware loading, fw_state */
+#define S2255_FW_NOTLOADED     0
+#define S2255_FW_LOADED_DSPWAIT        1
+#define S2255_FW_SUCCESS       2
+#define S2255_FW_FAILED                3
+
+struct s2255_fw {
+       int                   fw_loaded;
+       int                   fw_size;
+       struct urb            *fw_urb;
+       atomic_t              fw_state;
+       void                  *pfw_data;
+       wait_queue_head_t     wait_fw;
+       struct timer_list     dsp_wait;
+       const struct firmware *fw;
+};
+
+struct s2255_pipeinfo {
+       u32 max_transfer_size;
+       u32 cur_transfer_size;
+       u8 *transfer_buffer;
+       u32 transfer_flags;;
+       u32 state;
+       u32 prev_state;
+       u32 urb_size;
+       void *stream_urb;
+       void *dev;      /* back pointer to s2255_dev struct*/
+       u32 err_count;
+       u32 buf_index;
+       u32 idx;
+       u32 priority_set;
+};
+
+struct s2255_fmt; /*forward declaration */
+
+struct s2255_dev {
+       int                     frames;
+       int                     users[MAX_CHANNELS];
+       struct mutex            lock;
+       struct mutex            open_lock;
+       int                     resources[MAX_CHANNELS];
+       struct usb_device       *udev;
+       struct usb_interface    *interface;
+       u8                      read_endpoint;
+
+       struct s2255_dmaqueue   vidq[MAX_CHANNELS];
+       struct video_device     *vdev[MAX_CHANNELS];
+       struct list_head        s2255_devlist;
+       struct timer_list       timer;
+       struct s2255_fw *fw_data;
+       int                     board_num;
+       int                     is_open;
+       struct s2255_pipeinfo   pipes[MAX_PIPE_BUFFERS];
+       struct s2255_bufferi            buffer[MAX_CHANNELS];
+       struct s2255_mode       mode[MAX_CHANNELS];
+       const struct s2255_fmt  *cur_fmt[MAX_CHANNELS];
+       int                     cur_frame[MAX_CHANNELS];
+       int                     last_frame[MAX_CHANNELS];
+       u32                     cc;     /* current channel */
+       int                     b_acquire[MAX_CHANNELS];
+       unsigned long           req_image_size[MAX_CHANNELS];
+       int                     bad_payload[MAX_CHANNELS];
+       unsigned long           frame_count[MAX_CHANNELS];
+       int                     frame_ready;
+       struct kref             kref;
+       spinlock_t              slock;
+};
+#define to_s2255_dev(d) container_of(d, struct s2255_dev, kref)
+
+struct s2255_fmt {
+       char *name;
+       u32 fourcc;
+       int depth;
+};
+
+/* buffer for one video frame */
+struct s2255_buffer {
+       /* common v4l buffer stuff -- must be first */
+       struct videobuf_buffer vb;
+       const struct s2255_fmt *fmt;
+};
+
+struct s2255_fh {
+       struct s2255_dev        *dev;
+       unsigned int            resources;
+       const struct s2255_fmt  *fmt;
+       unsigned int            width;
+       unsigned int            height;
+       struct videobuf_queue   vb_vidq;
+       enum v4l2_buf_type      type;
+       int                     channel;
+       /* mode below is the desired mode.
+          mode in s2255_dev is the current mode that was last set */
+       struct s2255_mode       mode;
+};
+
+/*
+ * TODO: fixme S2255_MAX_USERS. Do not limit open driver handles.
+ * Limit V4L to one stream at a time.
+ */
+#define S2255_MAX_USERS         1
+
+#define CUR_USB_FWVER  774     /* current cypress EEPROM firmware version */
+#define S2255_MAJOR_VERSION    1
+#define S2255_MINOR_VERSION    13
+#define S2255_RELEASE          0
+#define S2255_VERSION          KERNEL_VERSION(S2255_MAJOR_VERSION, \
+                                              S2255_MINOR_VERSION, \
+                                              S2255_RELEASE)
+
+/* vendor ids */
+#define USB_S2255_VENDOR_ID    0x1943
+#define USB_S2255_PRODUCT_ID   0x2255
+#define S2255_NORMS            (V4L2_STD_PAL | V4L2_STD_NTSC)
+/* frame prefix size (sent once every frame) */
+#define PREFIX_SIZE            512
+
+/* Channels on box are in reverse order */
+static unsigned long G_chnmap[MAX_CHANNELS] = {3, 2, 1, 0};
+
+static LIST_HEAD(s2255_devlist);
+
+static int debug;
+static int *s2255_debug = &debug;
+
+static int s2255_start_readpipe(struct s2255_dev *dev);
+static void s2255_stop_readpipe(struct s2255_dev *dev);
+static int s2255_start_acquire(struct s2255_dev *dev, unsigned long chn);
+static int s2255_stop_acquire(struct s2255_dev *dev, unsigned long chn);
+static void s2255_fillbuff(struct s2255_dev *dev, struct s2255_buffer *buf,
+                          int chn);
+static int s2255_set_mode(struct s2255_dev *dev, unsigned long chn,
+                         struct s2255_mode *mode);
+static int s2255_board_shutdown(struct s2255_dev *dev);
+static void s2255_exit_v4l(struct s2255_dev *dev);
+static void s2255_fwload_start(struct s2255_dev *dev);
+
+#define dprintk(level, fmt, arg...)                                    \
+       do {                                                            \
+               if (*s2255_debug >= (level)) {                          \
+                       printk(KERN_DEBUG "s2255: " fmt, ##arg);        \
+               }                                                       \
+       } while (0)
+
+
+static struct usb_driver s2255_driver;
+
+
+/* Declare static vars that will be used as parameters */
+static unsigned int vid_limit = 16;    /* Video memory limit, in Mb */
+
+/* start video number */
+static int video_nr = -1;      /* /dev/videoN, -1 for autodetect */
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Debug level(0-100) default 0");
+module_param(vid_limit, int, 0644);
+MODULE_PARM_DESC(vid_limit, "video memory limit(Mb)");
+module_param(video_nr, int, 0644);
+MODULE_PARM_DESC(video_nr, "start video minor(-1 default autodetect)");
+
+/* USB device table */
+static struct usb_device_id s2255_table[] = {
+       {USB_DEVICE(USB_S2255_VENDOR_ID, USB_S2255_PRODUCT_ID)},
+       { }                     /* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, s2255_table);
+
+
+#define BUFFER_TIMEOUT msecs_to_jiffies(400)
+
+/* supported controls */
+static struct v4l2_queryctrl s2255_qctrl[] = {
+       {
+       .id = V4L2_CID_BRIGHTNESS,
+       .type = V4L2_CTRL_TYPE_INTEGER,
+       .name = "Brightness",
+       .minimum = -127,
+       .maximum = 128,
+       .step = 1,
+       .default_value = 0,
+       .flags = 0,
+       }, {
+       .id = V4L2_CID_CONTRAST,
+       .type = V4L2_CTRL_TYPE_INTEGER,
+       .name = "Contrast",
+       .minimum = 0,
+       .maximum = 255,
+       .step = 0x1,
+       .default_value = DEF_CONTRAST,
+       .flags = 0,
+       }, {
+       .id = V4L2_CID_SATURATION,
+       .type = V4L2_CTRL_TYPE_INTEGER,
+       .name = "Saturation",
+       .minimum = 0,
+       .maximum = 255,
+       .step = 0x1,
+       .default_value = DEF_SATURATION,
+       .flags = 0,
+       }, {
+       .id = V4L2_CID_HUE,
+       .type = V4L2_CTRL_TYPE_INTEGER,
+       .name = "Hue",
+       .minimum = 0,
+       .maximum = 255,
+       .step = 0x1,
+       .default_value = DEF_HUE,
+       .flags = 0,
+       }
+};
+
+static int qctl_regs[ARRAY_SIZE(s2255_qctrl)];
+
+/* image formats.  */
+static const struct s2255_fmt formats[] = {
+       {
+               .name = "4:2:2, planar, YUV422P",
+               .fourcc = V4L2_PIX_FMT_YUV422P,
+               .depth = 16
+
+       }, {
+               .name = "4:2:2, packed, YUYV",
+               .fourcc = V4L2_PIX_FMT_YUYV,
+               .depth = 16
+
+       }, {
+               .name = "4:2:2, packed, UYVY",
+               .fourcc = V4L2_PIX_FMT_UYVY,
+               .depth = 16
+       }, {
+               .name = "8bpp GREY",
+               .fourcc = V4L2_PIX_FMT_GREY,
+               .depth = 8
+       }
+};
+
+static int norm_maxw(struct video_device *vdev)
+{
+       return (vdev->current_norm & V4L2_STD_NTSC) ?
+           LINE_SZ_4CIFS_NTSC : LINE_SZ_4CIFS_PAL;
+}
+
+static int norm_maxh(struct video_device *vdev)
+{
+       return (vdev->current_norm & V4L2_STD_NTSC) ?
+           (NUM_LINES_1CIFS_NTSC * 2) : (NUM_LINES_1CIFS_PAL * 2);
+}
+
+static int norm_minw(struct video_device *vdev)
+{
+       return (vdev->current_norm & V4L2_STD_NTSC) ?
+           LINE_SZ_1CIFS_NTSC : LINE_SZ_1CIFS_PAL;
+}
+
+static int norm_minh(struct video_device *vdev)
+{
+       return (vdev->current_norm & V4L2_STD_NTSC) ?
+           (NUM_LINES_1CIFS_NTSC) : (NUM_LINES_1CIFS_PAL);
+}
+
+
+/*
+ * TODO: fixme: move YUV reordering to hardware
+ * converts 2255 planar format to yuyv or uyvy
+ */
+static void planar422p_to_yuv_packed(const unsigned char *in,
+                                    unsigned char *out,
+                                    int width, int height,
+                                    int fmt)
+{
+       unsigned char *pY;
+       unsigned char *pCb;
+       unsigned char *pCr;
+       unsigned long size = height * width;
+       unsigned int i;
+       pY = (unsigned char *)in;
+       pCr = (unsigned char *)in + height * width;
+       pCb = (unsigned char *)in + height * width + (height * width / 2);
+       for (i = 0; i < size * 2; i += 4) {
+               out[i] = (fmt == V4L2_PIX_FMT_YUYV) ? *pY++ : *pCr++;
+               out[i + 1] = (fmt == V4L2_PIX_FMT_YUYV) ? *pCr++ : *pY++;
+               out[i + 2] = (fmt == V4L2_PIX_FMT_YUYV) ? *pY++ : *pCb++;
+               out[i + 3] = (fmt == V4L2_PIX_FMT_YUYV) ? *pCb++ : *pY++;
+       }
+       return;
+}
+
+
+/* kickstarts the firmware loading. from probe
+ */
+static void s2255_timer(unsigned long user_data)
+{
+       struct s2255_fw *data = (struct s2255_fw *)user_data;
+       dprintk(100, "s2255 timer\n");
+       if (usb_submit_urb(data->fw_urb, GFP_ATOMIC) < 0) {
+               printk(KERN_ERR "s2255: can't submit urb\n");
+               if (data->fw) {
+                       release_firmware(data->fw);
+                       data->fw = NULL;
+               }
+               return;
+       }
+}
+
+/* called when DSP is up and running.  DSP is guaranteed to
+   be running after S2255_DSP_BOOTTIME */
+static void s2255_dsp_running(unsigned long user_data)
+{
+       struct s2255_fw *data = (struct s2255_fw *)user_data;
+       dprintk(1, "dsp running\n");
+       atomic_set(&data->fw_state, S2255_FW_SUCCESS);
+       wake_up(&data->wait_fw);
+       printk(KERN_INFO "s2255: firmware loaded successfully\n");
+       return;
+}
+
+
+/* this loads the firmware asynchronously.
+   Originally this was done synchroously in probe.
+   But it is better to load it asynchronously here than block
+   inside the probe function. Blocking inside probe affects boot time.
+   FW loading is triggered by the timer in the probe function
+*/
+static void s2255_fwchunk_complete(struct urb *urb)
+{
+       struct s2255_fw *data = urb->context;
+       struct usb_device *udev = urb->dev;
+       int len;
+       dprintk(100, "udev %p urb %p", udev, urb);
+       /* TODO: fixme.  reflect change in status */
+       if (urb->status) {
+               dev_err(&udev->dev, "URB failed with status %d", urb->status);
+               return;
+       }
+       if (data->fw_urb == NULL) {
+               dev_err(&udev->dev, "early disconncect\n");
+               return;
+       }
+#define CHUNK_SIZE 512
+       /* all USB transfers must be done with continuous kernel memory.
+          can't allocate more than 128k in current linux kernel, so
+          upload the firmware in chunks
+        */
+       if (data->fw_loaded < data->fw_size) {
+               len = (data->fw_loaded + CHUNK_SIZE) > data->fw_size ?
+                   data->fw_size % CHUNK_SIZE : CHUNK_SIZE;
+
+               if (len < CHUNK_SIZE)
+                       memset(data->pfw_data, 0, CHUNK_SIZE);
+
+               dprintk(100, "completed len %d, loaded %d \n", len,
+                       data->fw_loaded);
+
+               memcpy(data->pfw_data,
+                      (char *) data->fw->data + data->fw_loaded, len);
+
+               usb_fill_bulk_urb(data->fw_urb, udev, usb_sndbulkpipe(udev, 2),
+                                 data->pfw_data, CHUNK_SIZE,
+                                 s2255_fwchunk_complete, data);
+               if (usb_submit_urb(data->fw_urb, GFP_ATOMIC) < 0) {
+                       dev_err(&udev->dev, "failed submit URB\n");
+                       atomic_set(&data->fw_state, S2255_FW_FAILED);
+                       /* wake up anything waiting for the firmware */
+                       wake_up(&data->wait_fw);
+                       return;
+               }
+               data->fw_loaded += len;
+       } else {
+               init_timer(&data->dsp_wait);
+               data->dsp_wait.function = s2255_dsp_running;
+               data->dsp_wait.data = (unsigned long)data;
+               atomic_set(&data->fw_state, S2255_FW_LOADED_DSPWAIT);
+               mod_timer(&data->dsp_wait, msecs_to_jiffies(S2255_DSP_BOOTTIME)
+                         + jiffies);
+       }
+       dprintk(100, "2255 complete done\n");
+       return;
+
+}
+
+static int s2255_got_frame(struct s2255_dev *dev, int chn)
+{
+       struct s2255_dmaqueue *dma_q = &dev->vidq[chn];
+       struct s2255_buffer *buf;
+       unsigned long flags = 0;
+       int rc = 0;
+       dprintk(2, "wakeup: %p channel: %d\n", &dma_q, chn);
+       spin_lock_irqsave(&dev->slock, flags);
+
+       if (list_empty(&dma_q->active)) {
+               dprintk(1, "No active queue to serve\n");
+               rc = -1;
+               goto unlock;
+       }
+       buf = list_entry(dma_q->active.next,
+                        struct s2255_buffer, vb.queue);
+
+       if (!waitqueue_active(&buf->vb.done)) {
+               /* no one active */
+               rc = -1;
+               goto unlock;
+       }
+       list_del(&buf->vb.queue);
+       do_gettimeofday(&buf->vb.ts);
+       dprintk(100, "[%p/%d] wakeup\n", buf, buf->vb.i);
+
+       s2255_fillbuff(dev, buf, dma_q->channel);
+       wake_up(&buf->vb.done);
+       dprintk(2, "wakeup [buf/i] [%p/%d]\n", buf, buf->vb.i);
+unlock:
+       spin_unlock_irqrestore(&dev->slock, flags);
+       return 0;
+}
+
+
+static const struct s2255_fmt *format_by_fourcc(int fourcc)
+{
+       unsigned int i;
+
+       for (i = 0; i < ARRAY_SIZE(formats); i++) {
+               if (-1 == formats[i].fourcc)
+                       continue;
+               if (formats[i].fourcc == fourcc)
+                       return formats + i;
+       }
+       return NULL;
+}
+
+
+
+
+/* video buffer vmalloc implementation based partly on VIVI driver which is
+ *          Copyright (c) 2006 by
+ *                  Mauro Carvalho Chehab <mchehab--a.t--infradead.org>
+ *                  Ted Walther <ted--a.t--enumera.com>
+ *                  John Sokol <sokol--a.t--videotechnology.com>
+ *                  http://v4l.videotechnology.com/
+ *
+ */
+static void s2255_fillbuff(struct s2255_dev *dev, struct s2255_buffer *buf,
+                          int chn)
+{
+       int pos = 0;
+       struct timeval ts;
+       const char *tmpbuf;
+       char *vbuf = videobuf_to_vmalloc(&buf->vb);
+       unsigned long last_frame;
+       struct s2255_framei *frm;
+
+       if (!vbuf)
+               return;
+
+       last_frame = dev->last_frame[chn];
+       if (last_frame != -1) {
+               frm = &dev->buffer[chn].frame[last_frame];
+               tmpbuf =
+                   (const char *)dev->buffer[chn].frame[last_frame].lpvbits;
+               switch (buf->fmt->fourcc) {
+               case V4L2_PIX_FMT_YUYV:
+               case V4L2_PIX_FMT_UYVY:
+                       planar422p_to_yuv_packed((const unsigned char *)tmpbuf,
+                                                vbuf, buf->vb.width,
+                                                buf->vb.height,
+                                                buf->fmt->fourcc);
+                       break;
+               case V4L2_PIX_FMT_GREY:
+                       memcpy(vbuf, tmpbuf, buf->vb.width * buf->vb.height);
+                       break;
+               case V4L2_PIX_FMT_YUV422P:
+                       memcpy(vbuf, tmpbuf,
+                              buf->vb.width * buf->vb.height * 2);
+                       break;
+               default:
+                       printk(KERN_DEBUG "s2255: unknown format?\n");
+               }
+               dev->last_frame[chn] = -1;
+               /* done with the frame, free it */
+               frm->ulState = 0;
+               dprintk(4, "freeing buffer\n");
+       } else {
+               printk(KERN_ERR "s2255: =======no frame\n");
+               return;
+
+       }
+       dprintk(2, "s2255fill at : Buffer 0x%08lx size= %d\n",
+               (unsigned long)vbuf, pos);
+       /* tell v4l buffer was filled */
+
+       buf->vb.field_count++;
+       do_gettimeofday(&ts);
+       buf->vb.ts = ts;
+       buf->vb.state = VIDEOBUF_DONE;
+}
+
+
+/* ------------------------------------------------------------------
+   Videobuf operations
+   ------------------------------------------------------------------*/
+
+static int buffer_setup(struct videobuf_queue *vq, unsigned int *count,
+                       unsigned int *size)
+{
+       struct s2255_fh *fh = vq->priv_data;
+
+       *size = fh->width * fh->height * (fh->fmt->depth >> 3);
+
+       if (0 == *count)
+               *count = S2255_DEF_BUFS;
+
+       while (*size * (*count) > vid_limit * 1024 * 1024)
+               (*count)--;
+
+       return 0;
+}
+
+static void free_buffer(struct videobuf_queue *vq, struct s2255_buffer *buf)
+{
+       dprintk(4, "%s\n", __func__);
+
+       videobuf_waiton(&buf->vb, 0, 0);
+       videobuf_vmalloc_free(&buf->vb);
+       buf->vb.state = VIDEOBUF_NEEDS_INIT;
+}
+
+static int buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
+                         enum v4l2_field field)
+{
+       struct s2255_fh *fh = vq->priv_data;
+       struct s2255_buffer *buf = container_of(vb, struct s2255_buffer, vb);
+       int rc;
+       dprintk(4, "%s, field=%d\n", __func__, field);
+       if (fh->fmt == NULL)
+               return -EINVAL;
+
+       if ((fh->width < norm_minw(fh->dev->vdev[fh->channel])) ||
+           (fh->width > norm_maxw(fh->dev->vdev[fh->channel])) ||
+           (fh->height < norm_minh(fh->dev->vdev[fh->channel])) ||
+           (fh->height > norm_maxh(fh->dev->vdev[fh->channel]))) {
+               dprintk(4, "invalid buffer prepare\n");
+               return -EINVAL;
+       }
+
+       buf->vb.size = fh->width * fh->height * (fh->fmt->depth >> 3);
+
+       if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) {
+               dprintk(4, "invalid buffer prepare\n");
+               return -EINVAL;
+       }
+
+       buf->fmt = fh->fmt;
+       buf->vb.width = fh->width;
+       buf->vb.height = fh->height;
+       buf->vb.field = field;
+
+
+       if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+               rc = videobuf_iolock(vq, &buf->vb, NULL);
+               if (rc < 0)
+                       goto fail;
+       }
+
+       buf->vb.state = VIDEOBUF_PREPARED;
+       return 0;
+fail:
+       free_buffer(vq, buf);
+       return rc;
+}
+
+static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+{
+       struct s2255_buffer *buf = container_of(vb, struct s2255_buffer, vb);
+       struct s2255_fh *fh = vq->priv_data;
+       struct s2255_dev *dev = fh->dev;
+       struct s2255_dmaqueue *vidq = &dev->vidq[fh->channel];
+
+       dprintk(1, "%s\n", __func__);
+
+       buf->vb.state = VIDEOBUF_QUEUED;
+       list_add_tail(&buf->vb.queue, &vidq->active);
+}
+
+static void buffer_release(struct videobuf_queue *vq,
+                          struct videobuf_buffer *vb)
+{
+       struct s2255_buffer *buf = container_of(vb, struct s2255_buffer, vb);
+       struct s2255_fh *fh = vq->priv_data;
+       dprintk(4, "%s %d\n", __func__, fh->channel);
+       free_buffer(vq, buf);
+}
+
+static struct videobuf_queue_ops s2255_video_qops = {
+       .buf_setup = buffer_setup,
+       .buf_prepare = buffer_prepare,
+       .buf_queue = buffer_queue,
+       .buf_release = buffer_release,
+};
+
+
+static int res_get(struct s2255_dev *dev, struct s2255_fh *fh)
+{
+       /* is it free? */
+       mutex_lock(&dev->lock);
+       if (dev->resources[fh->channel]) {
+               /* no, someone else uses it */
+               mutex_unlock(&dev->lock);
+               return 0;
+       }
+       /* it's free, grab it */
+       dev->resources[fh->channel] = 1;
+       dprintk(1, "res: get\n");
+       mutex_unlock(&dev->lock);
+       return 1;
+}
+
+static int res_locked(struct s2255_dev *dev, struct s2255_fh *fh)
+{
+       return dev->resources[fh->channel];
+}
+
+static void res_free(struct s2255_dev *dev, struct s2255_fh *fh)
+{
+       dev->resources[fh->channel] = 0;
+       dprintk(1, "res: put\n");
+}
+
+
+static int vidioc_querycap(struct file *file, void *priv,
+                          struct v4l2_capability *cap)
+{
+       struct s2255_fh *fh = file->private_data;
+       struct s2255_dev *dev = fh->dev;
+       strlcpy(cap->driver, "s2255", sizeof(cap->driver));
+       strlcpy(cap->card, "s2255", sizeof(cap->card));
+       strlcpy(cap->bus_info, dev_name(&dev->udev->dev),
+               sizeof(cap->bus_info));
+       cap->version = S2255_VERSION;
+       cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+       return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
+                              struct v4l2_fmtdesc *f)
+{
+       int index = 0;
+       if (f)
+               index = f->index;
+
+       if (index >= ARRAY_SIZE(formats))
+               return -EINVAL;
+
+       dprintk(4, "name %s\n", formats[index].name);
+       strlcpy(f->description, formats[index].name, sizeof(f->description));
+       f->pixelformat = formats[index].fourcc;
+       return 0;
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+                           struct v4l2_format *f)
+{
+       struct s2255_fh *fh = priv;
+
+       f->fmt.pix.width = fh->width;
+       f->fmt.pix.height = fh->height;
+       f->fmt.pix.field = fh->vb_vidq.field;
+       f->fmt.pix.pixelformat = fh->fmt->fourcc;
+       f->fmt.pix.bytesperline = f->fmt.pix.width * (fh->fmt->depth >> 3);
+       f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
+       return 0;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+                             struct v4l2_format *f)
+{
+       const struct s2255_fmt *fmt;
+       enum v4l2_field field;
+       int  b_any_field = 0;
+       struct s2255_fh *fh = priv;
+       struct s2255_dev *dev = fh->dev;
+       int is_ntsc;
+
+       is_ntsc =
+           (dev->vdev[fh->channel]->current_norm & V4L2_STD_NTSC) ? 1 : 0;
+
+       fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+
+       if (fmt == NULL)
+               return -EINVAL;
+
+       field = f->fmt.pix.field;
+       if (field == V4L2_FIELD_ANY)
+               b_any_field = 1;
+
+       dprintk(4, "try format %d \n", is_ntsc);
+       /* supports 3 sizes. see s2255drv.h */
+       dprintk(50, "width test %d, height %d\n",
+               f->fmt.pix.width, f->fmt.pix.height);
+       if (is_ntsc) {
+               /* NTSC */
+               if (f->fmt.pix.height >= NUM_LINES_1CIFS_NTSC * 2) {
+                       f->fmt.pix.height = NUM_LINES_1CIFS_NTSC * 2;
+                       if (b_any_field) {
+                               field = V4L2_FIELD_SEQ_TB;
+                       } else if (!((field == V4L2_FIELD_INTERLACED) ||
+                                     (field == V4L2_FIELD_SEQ_TB) ||
+                                     (field == V4L2_FIELD_INTERLACED_TB))) {
+                               dprintk(1, "unsupported field setting\n");
+                               return -EINVAL;
+                       }
+               } else {
+                       f->fmt.pix.height = NUM_LINES_1CIFS_NTSC;
+                       if (b_any_field) {
+                               field = V4L2_FIELD_TOP;
+                       } else if (!((field == V4L2_FIELD_TOP) ||
+                                     (field == V4L2_FIELD_BOTTOM))) {
+                               dprintk(1, "unsupported field setting\n");
+                               return -EINVAL;
+                       }
+
+               }
+               if (f->fmt.pix.width >= LINE_SZ_4CIFS_NTSC)
+                       f->fmt.pix.width = LINE_SZ_4CIFS_NTSC;
+               else if (f->fmt.pix.width >= LINE_SZ_2CIFS_NTSC)
+                       f->fmt.pix.width = LINE_SZ_2CIFS_NTSC;
+               else if (f->fmt.pix.width >= LINE_SZ_1CIFS_NTSC)
+                       f->fmt.pix.width = LINE_SZ_1CIFS_NTSC;
+               else
+                       f->fmt.pix.width = LINE_SZ_1CIFS_NTSC;
+       } else {
+               /* PAL */
+               if (f->fmt.pix.height >= NUM_LINES_1CIFS_PAL * 2) {
+                       f->fmt.pix.height = NUM_LINES_1CIFS_PAL * 2;
+                       if (b_any_field) {
+                               field = V4L2_FIELD_SEQ_TB;
+                       } else if (!((field == V4L2_FIELD_INTERLACED) ||
+                                     (field == V4L2_FIELD_SEQ_TB) ||
+                                     (field == V4L2_FIELD_INTERLACED_TB))) {
+                               dprintk(1, "unsupported field setting\n");
+                               return -EINVAL;
+                       }
+               } else {
+                       f->fmt.pix.height = NUM_LINES_1CIFS_PAL;
+                       if (b_any_field) {
+                               field = V4L2_FIELD_TOP;
+                       } else if (!((field == V4L2_FIELD_TOP) ||
+                                    (field == V4L2_FIELD_BOTTOM))) {
+                               dprintk(1, "unsupported field setting\n");
+                               return -EINVAL;
+                       }
+               }
+               if (f->fmt.pix.width >= LINE_SZ_4CIFS_PAL) {
+                       dprintk(50, "pal 704\n");
+                       f->fmt.pix.width = LINE_SZ_4CIFS_PAL;
+                       field = V4L2_FIELD_SEQ_TB;
+               } else if (f->fmt.pix.width >= LINE_SZ_2CIFS_PAL) {
+                       dprintk(50, "pal 352A\n");
+                       f->fmt.pix.width = LINE_SZ_2CIFS_PAL;
+                       field = V4L2_FIELD_TOP;
+               } else if (f->fmt.pix.width >= LINE_SZ_1CIFS_PAL) {
+                       dprintk(50, "pal 352B\n");
+                       f->fmt.pix.width = LINE_SZ_1CIFS_PAL;
+                       field = V4L2_FIELD_TOP;
+               } else {
+                       dprintk(50, "pal 352C\n");
+                       f->fmt.pix.width = LINE_SZ_1CIFS_PAL;
+                       field = V4L2_FIELD_TOP;
+               }
+       }
+
+       dprintk(50, "width %d height %d field %d \n", f->fmt.pix.width,
+               f->fmt.pix.height, f->fmt.pix.field);
+       f->fmt.pix.field = field;
+       f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3;
+       f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
+       return 0;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+                           struct v4l2_format *f)
+{
+       struct s2255_fh *fh = priv;
+       const struct s2255_fmt *fmt;
+       struct videobuf_queue *q = &fh->vb_vidq;
+       int ret;
+       int norm;
+
+       ret = vidioc_try_fmt_vid_cap(file, fh, f);
+
+       if (ret < 0)
+               return ret;
+
+       fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+
+       if (fmt == NULL)
+               return -EINVAL;
+
+       mutex_lock(&q->vb_lock);
+
+       if (videobuf_queue_is_busy(&fh->vb_vidq)) {
+               dprintk(1, "queue busy\n");
+               ret = -EBUSY;
+               goto out_s_fmt;
+       }
+
+       if (res_locked(fh->dev, fh)) {
+               dprintk(1, "can't change format after started\n");
+               ret = -EBUSY;
+               goto out_s_fmt;
+       }
+
+       fh->fmt = fmt;
+       fh->width = f->fmt.pix.width;
+       fh->height = f->fmt.pix.height;
+       fh->vb_vidq.field = f->fmt.pix.field;
+       fh->type = f->type;
+       norm = norm_minw(fh->dev->vdev[fh->channel]);
+       if (fh->width > norm_minw(fh->dev->vdev[fh->channel])) {
+               if (fh->height > norm_minh(fh->dev->vdev[fh->channel]))
+                       fh->mode.scale = SCALE_4CIFS;
+               else
+                       fh->mode.scale = SCALE_2CIFS;
+
+       } else {
+               fh->mode.scale = SCALE_1CIFS;
+       }
+
+       /* color mode */
+       switch (fh->fmt->fourcc) {
+       case V4L2_PIX_FMT_GREY:
+               fh->mode.color = COLOR_Y8;
+               break;
+       case V4L2_PIX_FMT_YUV422P:
+               fh->mode.color = COLOR_YUVPL;
+               break;
+       case V4L2_PIX_FMT_YUYV:
+       case V4L2_PIX_FMT_UYVY:
+       default:
+               fh->mode.color = COLOR_YUVPK;
+               break;
+       }
+       ret = 0;
+out_s_fmt:
+       mutex_unlock(&q->vb_lock);
+       return ret;
+}
+
+static int vidioc_reqbufs(struct file *file, void *priv,
+                         struct v4l2_requestbuffers *p)
+{
+       int rc;
+       struct s2255_fh *fh = priv;
+       rc = videobuf_reqbufs(&fh->vb_vidq, p);
+       return rc;
+}
+
+static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+       int rc;
+       struct s2255_fh *fh = priv;
+       rc = videobuf_querybuf(&fh->vb_vidq, p);
+       return rc;
+}
+
+static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+       int rc;
+       struct s2255_fh *fh = priv;
+       rc = videobuf_qbuf(&fh->vb_vidq, p);
+       return rc;
+}
+
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+       int rc;
+       struct s2255_fh *fh = priv;
+       rc = videobuf_dqbuf(&fh->vb_vidq, p, file->f_flags & O_NONBLOCK);
+       return rc;
+}
+
+#ifdef CONFIG_VIDEO_V4L1_COMPAT
+static int vidioc_cgmbuf(struct file *file, void *priv, struct video_mbuf *mbuf)
+{
+       struct s2255_fh *fh = priv;
+
+       return videobuf_cgmbuf(&fh->vb_vidq, mbuf, 8);
+}
+#endif
+
+/* write to the configuration pipe, synchronously */
+static int s2255_write_config(struct usb_device *udev, unsigned char *pbuf,
+                             int size)
+{
+       int pipe;
+       int done;
+       long retval = -1;
+       if (udev) {
+               pipe = usb_sndbulkpipe(udev, S2255_CONFIG_EP);
+               retval = usb_bulk_msg(udev, pipe, pbuf, size, &done, 500);
+       }
+       return retval;
+}
+
+static u32 get_transfer_size(struct s2255_mode *mode)
+{
+       int linesPerFrame = LINE_SZ_DEF;
+       int pixelsPerLine = NUM_LINES_DEF;
+       u32 outImageSize;
+       u32 usbInSize;
+       unsigned int mask_mult;
+
+       if (mode == NULL)
+               return 0;
+
+       if (mode->format == FORMAT_NTSC) {
+               switch (mode->scale) {
+               case SCALE_4CIFS:
+                       linesPerFrame = NUM_LINES_4CIFS_NTSC * 2;
+                       pixelsPerLine = LINE_SZ_4CIFS_NTSC;
+                       break;
+               case SCALE_2CIFS:
+                       linesPerFrame = NUM_LINES_2CIFS_NTSC;
+                       pixelsPerLine = LINE_SZ_2CIFS_NTSC;
+                       break;
+               case SCALE_1CIFS:
+                       linesPerFrame = NUM_LINES_1CIFS_NTSC;
+                       pixelsPerLine = LINE_SZ_1CIFS_NTSC;
+                       break;
+               default:
+                       break;
+               }
+       } else if (mode->format == FORMAT_PAL) {
+               switch (mode->scale) {
+               case SCALE_4CIFS:
+                       linesPerFrame = NUM_LINES_4CIFS_PAL * 2;
+                       pixelsPerLine = LINE_SZ_4CIFS_PAL;
+                       break;
+               case SCALE_2CIFS:
+                       linesPerFrame = NUM_LINES_2CIFS_PAL;
+                       pixelsPerLine = LINE_SZ_2CIFS_PAL;
+                       break;
+               case SCALE_1CIFS:
+                       linesPerFrame = NUM_LINES_1CIFS_PAL;
+                       pixelsPerLine = LINE_SZ_1CIFS_PAL;
+                       break;
+               default:
+                       break;
+               }
+       }
+       outImageSize = linesPerFrame * pixelsPerLine;
+       if (mode->color != COLOR_Y8) {
+               /* 2 bytes/pixel if not monochrome */
+               outImageSize *= 2;
+       }
+
+       /* total bytes to send including prefix and 4K padding;
+          must be a multiple of USB_READ_SIZE */
+       usbInSize = outImageSize + PREFIX_SIZE; /* always send prefix */
+       mask_mult = 0xffffffffUL - DEF_USB_BLOCK + 1;
+       /* if size not a multiple of USB_READ_SIZE */
+       if (usbInSize & ~mask_mult)
+               usbInSize = (usbInSize & mask_mult) + (DEF_USB_BLOCK);
+       return usbInSize;
+}
+
+static void dump_verify_mode(struct s2255_dev *sdev, struct s2255_mode *mode)
+{
+       struct device *dev = &sdev->udev->dev;
+       dev_info(dev, "------------------------------------------------\n");
+       dev_info(dev, "verify mode\n");
+       dev_info(dev, "format: %d\n", mode->format);
+       dev_info(dev, "scale: %d\n", mode->scale);
+       dev_info(dev, "fdec: %d\n", mode->fdec);
+       dev_info(dev, "color: %d\n", mode->color);
+       dev_info(dev, "bright: 0x%x\n", mode->bright);
+       dev_info(dev, "restart: 0x%x\n", mode->restart);
+       dev_info(dev, "usb_block: 0x%x\n", mode->usb_block);
+       dev_info(dev, "single: 0x%x\n", mode->single);
+       dev_info(dev, "------------------------------------------------\n");
+}
+
+/*
+ * set mode is the function which controls the DSP.
+ * the restart parameter in struct s2255_mode should be set whenever
+ * the image size could change via color format, video system or image
+ * size.
+ * When the restart parameter is set, we sleep for ONE frame to allow the
+ * DSP time to get the new frame
+ */
+static int s2255_set_mode(struct s2255_dev *dev, unsigned long chn,
+                         struct s2255_mode *mode)
+{
+       int res;
+       u32 *buffer;
+       unsigned long chn_rev;
+
+       chn_rev = G_chnmap[chn];
+       dprintk(3, "mode scale [%ld] %p %d\n", chn, mode, mode->scale);
+       dprintk(3, "mode scale [%ld] %p %d\n", chn, &dev->mode[chn],
+               dev->mode[chn].scale);
+       dprintk(2, "mode contrast %x\n", mode->contrast);
+
+       /* save the mode */
+       dev->mode[chn] = *mode;
+       dev->req_image_size[chn] = get_transfer_size(mode);
+       dprintk(1, "transfer size %ld\n", dev->req_image_size[chn]);
+
+       buffer = kzalloc(512, GFP_KERNEL);
+       if (buffer == NULL) {
+               dev_err(&dev->udev->dev, "out of mem\n");
+               return -ENOMEM;
+       }
+
+       /* set the mode */
+       buffer[0] = IN_DATA_TOKEN;
+       buffer[1] = (u32) chn_rev;
+       buffer[2] = CMD_SET_MODE;
+       memcpy(&buffer[3], &dev->mode[chn], sizeof(struct s2255_mode));
+       res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512);
+       if (debug)
+               dump_verify_mode(dev, mode);
+       kfree(buffer);
+       dprintk(1, "set mode done chn %lu, %d\n", chn, res);
+
+       /* wait at least 3 frames before continuing */
+       if (mode->restart)
+               msleep(125);
+
+       /* clear the restart flag */
+       dev->mode[chn].restart = 0;
+
+       return res;
+}
+
+static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+       int res;
+       struct s2255_fh *fh = priv;
+       struct s2255_dev *dev = fh->dev;
+       struct s2255_mode *new_mode;
+       struct s2255_mode *old_mode;
+       int chn;
+       int j;
+       dprintk(4, "%s\n", __func__);
+       if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+               dev_err(&dev->udev->dev, "invalid fh type0\n");
+               return -EINVAL;
+       }
+       if (i != fh->type) {
+               dev_err(&dev->udev->dev, "invalid fh type1\n");
+               return -EINVAL;
+       }
+
+       if (!res_get(dev, fh)) {
+               dev_err(&dev->udev->dev, "res get busy\n");
+               return -EBUSY;
+       }
+
+       /* send a set mode command everytime with restart.
+          in case we switch resolutions or other parameters */
+       chn = fh->channel;
+       new_mode = &fh->mode;
+       old_mode = &fh->dev->mode[chn];
+
+       if (new_mode->color != old_mode->color)
+               new_mode->restart = 1;
+       else if (new_mode->scale != old_mode->scale)
+               new_mode->restart = 1;
+       else if (new_mode->format != old_mode->format)
+               new_mode->restart = 1;
+
+       s2255_set_mode(dev, chn, new_mode);
+       new_mode->restart = 0;
+       *old_mode = *new_mode;
+       dev->cur_fmt[chn] = fh->fmt;
+       dprintk(1, "%s[%d]\n", __func__, chn);
+       dev->last_frame[chn] = -1;
+       dev->bad_payload[chn] = 0;
+       dev->cur_frame[chn] = 0;
+       for (j = 0; j < SYS_FRAMES; j++) {
+               dev->buffer[chn].frame[j].ulState = 0;
+               dev->buffer[chn].frame[j].cur_size = 0;
+       }
+       res = videobuf_streamon(&fh->vb_vidq);
+       if (res == 0) {
+               s2255_start_acquire(dev, chn);
+               dev->b_acquire[chn] = 1;
+       } else {
+               res_free(dev, fh);
+       }
+       return res;
+}
+
+static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+       int res;
+       struct s2255_fh *fh = priv;
+       struct s2255_dev *dev = fh->dev;
+
+       dprintk(4, "%s\n, channel: %d", __func__, fh->channel);
+       if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+               printk(KERN_ERR "invalid fh type0\n");
+               return -EINVAL;
+       }
+       if (i != fh->type) {
+               printk(KERN_ERR "invalid type i\n");
+               return -EINVAL;
+       }
+       s2255_stop_acquire(dev, fh->channel);
+       res = videobuf_streamoff(&fh->vb_vidq);
+       res_free(dev, fh);
+       return res;
+}
+
+static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *i)
+{
+       struct s2255_fh *fh = priv;
+       struct s2255_mode *mode;
+       struct videobuf_queue *q = &fh->vb_vidq;
+       int ret = 0;
+
+       mutex_lock(&q->vb_lock);
+       if (videobuf_queue_is_busy(q)) {
+               dprintk(1, "queue busy\n");
+               ret = -EBUSY;
+               goto out_s_std;
+       }
+
+       if (res_locked(fh->dev, fh)) {
+               dprintk(1, "can't change standard after started\n");
+               ret = -EBUSY;
+               goto out_s_std;
+       }
+       mode = &fh->mode;
+
+       if (*i & V4L2_STD_NTSC) {
+               dprintk(4, "vidioc_s_std NTSC\n");
+               mode->format = FORMAT_NTSC;
+       } else if (*i & V4L2_STD_PAL) {
+               dprintk(4, "vidioc_s_std PAL\n");
+               mode->format = FORMAT_PAL;
+       } else {
+               ret = -EINVAL;
+       }
+out_s_std:
+       mutex_unlock(&q->vb_lock);
+       return ret;
+}
+
+/* Sensoray 2255 is a multiple channel capture device.
+   It does not have a "crossbar" of inputs.
+   We use one V4L device per channel. The user must
+   be aware that certain combinations are not allowed.
+   For instance, you cannot do full FPS on more than 2 channels(2 videodevs)
+   at once in color(you can do full fps on 4 channels with greyscale.
+*/
+static int vidioc_enum_input(struct file *file, void *priv,
+                            struct v4l2_input *inp)
+{
+       if (inp->index != 0)
+               return -EINVAL;
+
+       inp->type = V4L2_INPUT_TYPE_CAMERA;
+       inp->std = S2255_NORMS;
+       strlcpy(inp->name, "Camera", sizeof(inp->name));
+       return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+       *i = 0;
+       return 0;
+}
+static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+       if (i > 0)
+               return -EINVAL;
+       return 0;
+}
+
+/* --- controls ---------------------------------------------- */
+static int vidioc_queryctrl(struct file *file, void *priv,
+                           struct v4l2_queryctrl *qc)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(s2255_qctrl); i++)
+               if (qc->id && qc->id == s2255_qctrl[i].id) {
+                       memcpy(qc, &(s2255_qctrl[i]), sizeof(*qc));
+                       return 0;
+               }
+
+       dprintk(4, "query_ctrl -EINVAL %d\n", qc->id);
+       return -EINVAL;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *priv,
+                        struct v4l2_control *ctrl)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(s2255_qctrl); i++)
+               if (ctrl->id == s2255_qctrl[i].id) {
+                       ctrl->value = qctl_regs[i];
+                       return 0;
+               }
+       dprintk(4, "g_ctrl -EINVAL\n");
+
+       return -EINVAL;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+                        struct v4l2_control *ctrl)
+{
+       int i;
+       struct s2255_fh *fh = priv;
+       struct s2255_dev *dev = fh->dev;
+       struct s2255_mode *mode;
+       mode = &fh->mode;
+       dprintk(4, "vidioc_s_ctrl\n");
+       for (i = 0; i < ARRAY_SIZE(s2255_qctrl); i++) {
+               if (ctrl->id == s2255_qctrl[i].id) {
+                       if (ctrl->value < s2255_qctrl[i].minimum ||
+                           ctrl->value > s2255_qctrl[i].maximum)
+                               return -ERANGE;
+
+                       qctl_regs[i] = ctrl->value;
+                       /* update the mode to the corresponding value */
+                       switch (ctrl->id) {
+                       case V4L2_CID_BRIGHTNESS:
+                               mode->bright = ctrl->value;
+                               break;
+                       case V4L2_CID_CONTRAST:
+                               mode->contrast = ctrl->value;
+                               break;
+                       case V4L2_CID_HUE:
+                               mode->hue = ctrl->value;
+                               break;
+                       case V4L2_CID_SATURATION:
+                               mode->saturation = ctrl->value;
+                               break;
+                       }
+                       mode->restart = 0;
+                       /* set mode here.  Note: stream does not need restarted.
+                          some V4L programs restart stream unnecessarily
+                          after a s_crtl.
+                        */
+                       s2255_set_mode(dev, fh->channel, mode);
+                       return 0;
+               }
+       }
+       return -EINVAL;
+}
+
+static int s2255_open(struct inode *inode, struct file *file)
+{
+       int minor = iminor(inode);
+       struct s2255_dev *h, *dev = NULL;
+       struct s2255_fh *fh;
+       struct list_head *list;
+       enum v4l2_buf_type type = 0;
+       int i = 0;
+       int cur_channel = -1;
+       dprintk(1, "s2255: open called (minor=%d)\n", minor);
+
+       list_for_each(list, &s2255_devlist) {
+               h = list_entry(list, struct s2255_dev, s2255_devlist);
+               for (i = 0; i < MAX_CHANNELS; i++) {
+                       if (h->vdev[i]->minor == minor) {
+                               cur_channel = i;
+                               dev = h;
+                               type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+                       }
+               }
+       }
+
+       if ((NULL == dev) || (cur_channel == -1)) {
+               dprintk(1, "s2255: openv4l no dev\n");
+               return -ENODEV;
+       }
+
+       mutex_lock(&dev->open_lock);
+
+       dev->users[cur_channel]++;
+       if (dev->users[cur_channel] > S2255_MAX_USERS) {
+               dev->users[cur_channel]--;
+               mutex_unlock(&dev->open_lock);
+               printk(KERN_INFO "s2255drv: too many open handles!\n");
+               return -EBUSY;
+       }
+
+       if (atomic_read(&dev->fw_data->fw_state) == S2255_FW_FAILED) {
+               err("2255 firmware load failed. retrying.\n");
+               s2255_fwload_start(dev);
+               wait_event_timeout(dev->fw_data->wait_fw,
+                                  (atomic_read(&dev->fw_data->fw_state)
+                                   != S2255_FW_NOTLOADED),
+                                  msecs_to_jiffies(S2255_LOAD_TIMEOUT));
+               if (atomic_read(&dev->fw_data->fw_state)
+                   != S2255_FW_SUCCESS) {
+                       printk(KERN_INFO "2255 FW load failed after 2 tries\n");
+                       mutex_unlock(&dev->open_lock);
+                       return -EFAULT;
+               }
+       } else if (atomic_read(&dev->fw_data->fw_state) == S2255_FW_NOTLOADED) {
+               /* give S2255_LOAD_TIMEOUT time for firmware to load in case
+                  driver loaded and then device immediately opened */
+               printk(KERN_INFO "%s waiting for firmware load\n", __func__);
+               wait_event_timeout(dev->fw_data->wait_fw,
+                                  (atomic_read(&dev->fw_data->fw_state)
+                                  != S2255_FW_NOTLOADED),
+                                  msecs_to_jiffies(S2255_LOAD_TIMEOUT));
+               if (atomic_read(&dev->fw_data->fw_state)
+                   != S2255_FW_SUCCESS) {
+                       printk(KERN_INFO "2255 firmware not loaded"
+                              "try again\n");
+                       mutex_unlock(&dev->open_lock);
+                       return -EBUSY;
+               }
+       }
+
+       /* allocate + initialize per filehandle data */
+       fh = kzalloc(sizeof(*fh), GFP_KERNEL);
+       if (NULL == fh) {
+               mutex_unlock(&dev->open_lock);
+               return -ENOMEM;
+       }
+
+       file->private_data = fh;
+       fh->dev = dev;
+       fh->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       fh->mode = dev->mode[cur_channel];
+       fh->fmt = dev->cur_fmt[cur_channel];
+       /* default 4CIF NTSC */
+       fh->width = LINE_SZ_4CIFS_NTSC;
+       fh->height = NUM_LINES_4CIFS_NTSC * 2;
+       fh->channel = cur_channel;
+
+       /* Put all controls at a sane state */
+       for (i = 0; i < ARRAY_SIZE(s2255_qctrl); i++)
+               qctl_regs[i] = s2255_qctrl[i].default_value;
+
+       dprintk(1, "s2255drv: open minor=%d type=%s users=%d\n",
+               minor, v4l2_type_names[type], dev->users[cur_channel]);
+       dprintk(2, "s2255drv: open: fh=0x%08lx, dev=0x%08lx, vidq=0x%08lx\n",
+               (unsigned long)fh, (unsigned long)dev,
+               (unsigned long)&dev->vidq[cur_channel]);
+       dprintk(4, "s2255drv: open: list_empty active=%d\n",
+               list_empty(&dev->vidq[cur_channel].active));
+
+       videobuf_queue_vmalloc_init(&fh->vb_vidq, &s2255_video_qops,
+                                   NULL, &dev->slock,
+                                   fh->type,
+                                   V4L2_FIELD_INTERLACED,
+                                   sizeof(struct s2255_buffer), fh);
+
+       kref_get(&dev->kref);
+       mutex_unlock(&dev->open_lock);
+       return 0;
+}
+
+
+static unsigned int s2255_poll(struct file *file,
+                              struct poll_table_struct *wait)
+{
+       struct s2255_fh *fh = file->private_data;
+       int rc;
+       dprintk(100, "%s\n", __func__);
+
+       if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type)
+               return POLLERR;
+
+       rc = videobuf_poll_stream(file, &fh->vb_vidq, wait);
+       return rc;
+}
+
+static void s2255_destroy(struct kref *kref)
+{
+       struct s2255_dev *dev = to_s2255_dev(kref);
+       if (!dev) {
+               printk(KERN_ERR "s2255drv: kref problem\n");
+               return;
+       }
+       /* prevent s2255_disconnect from racing s2255_open */
+       mutex_lock(&dev->open_lock);
+       s2255_exit_v4l(dev);
+       /* device unregistered so no longer possible to open. open_mutex
+          can be unlocked */
+       mutex_unlock(&dev->open_lock);
+
+       /* board shutdown stops the read pipe if it is running */
+       s2255_board_shutdown(dev);
+
+       /* make sure firmware still not trying to load */
+       if (dev->fw_data->fw_urb) {
+               dprintk(2, "kill fw_urb\n");
+               usb_kill_urb(dev->fw_data->fw_urb);
+               usb_free_urb(dev->fw_data->fw_urb);
+               dev->fw_data->fw_urb = NULL;
+       }
+       /*
+        * TODO: fixme(above, below): potentially leaving timers alive.
+        *                            do not ignore timeout below if
+        *                            it occurs.
+        */
+
+       /* make sure we aren't waiting for the DSP */
+       if (atomic_read(&dev->fw_data->fw_state) == S2255_FW_LOADED_DSPWAIT) {
+               /* if we are, wait for the wakeup for fw_success or timeout */
+               wait_event_timeout(dev->fw_data->wait_fw,
+                                  (atomic_read(&dev->fw_data->fw_state)
+                                  == S2255_FW_SUCCESS),
+                                  msecs_to_jiffies(S2255_LOAD_TIMEOUT));
+       }
+
+       if (dev->fw_data) {
+               if (dev->fw_data->fw)
+                       release_firmware(dev->fw_data->fw);
+               kfree(dev->fw_data->pfw_data);
+               kfree(dev->fw_data);
+       }
+
+       usb_put_dev(dev->udev);
+       dprintk(1, "%s", __func__);
+       kfree(dev);
+}
+
+static int s2255_close(struct inode *inode, struct file *file)
+{
+       struct s2255_fh *fh = file->private_data;
+       struct s2255_dev *dev = fh->dev;
+       int minor = iminor(inode);
+       if (!dev)
+               return -ENODEV;
+
+       mutex_lock(&dev->open_lock);
+
+       if (dev->b_acquire[fh->channel])
+               s2255_stop_acquire(dev, fh->channel);
+       res_free(dev, fh);
+       videobuf_mmap_free(&fh->vb_vidq);
+       kfree(fh);
+       dev->users[fh->channel]--;
+       mutex_unlock(&dev->open_lock);
+
+       kref_put(&dev->kref, s2255_destroy);
+       dprintk(1, "s2255: close called (minor=%d, users=%d)\n",
+               minor, dev->users[fh->channel]);
+       return 0;
+}
+
+static int s2255_mmap_v4l(struct file *file, struct vm_area_struct *vma)
+{
+       struct s2255_fh *fh = file->private_data;
+       int ret;
+
+       if (!fh)
+               return -ENODEV;
+       dprintk(4, "mmap called, vma=0x%08lx\n", (unsigned long)vma);
+
+       ret = videobuf_mmap_mapper(&fh->vb_vidq, vma);
+
+       dprintk(4, "vma start=0x%08lx, size=%ld, ret=%d\n",
+               (unsigned long)vma->vm_start,
+               (unsigned long)vma->vm_end - (unsigned long)vma->vm_start, ret);
+
+       return ret;
+}
+
+static const struct file_operations s2255_fops_v4l = {
+       .owner = THIS_MODULE,
+       .open = s2255_open,
+       .release = s2255_close,
+       .poll = s2255_poll,
+       .ioctl = video_ioctl2,  /* V4L2 ioctl handler */
+       .compat_ioctl = v4l_compat_ioctl32,
+       .mmap = s2255_mmap_v4l,
+       .llseek = no_llseek,
+};
+
+static struct video_device template = {
+       .name = "s2255v",
+       .type = VID_TYPE_CAPTURE,
+       .fops = &s2255_fops_v4l,
+       .minor = -1,
+       .release = video_device_release,
+       .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_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+       .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+       .vidioc_reqbufs = vidioc_reqbufs,
+       .vidioc_querybuf = vidioc_querybuf,
+       .vidioc_qbuf = vidioc_qbuf,
+       .vidioc_dqbuf = vidioc_dqbuf,
+       .vidioc_s_std = vidioc_s_std,
+       .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_streamon = vidioc_streamon,
+       .vidioc_streamoff = vidioc_streamoff,
+#ifdef CONFIG_VIDEO_V4L1_COMPAT
+       .vidiocgmbuf = vidioc_cgmbuf,
+#endif
+       .tvnorms = S2255_NORMS,
+       .current_norm = V4L2_STD_NTSC_M,
+};
+
+static int s2255_probe_v4l(struct s2255_dev *dev)
+{
+       int ret;
+       int i;
+       int cur_nr = video_nr;
+
+       /* initialize all video 4 linux */
+       list_add_tail(&dev->s2255_devlist, &s2255_devlist);
+       /* register 4 video devices */
+       for (i = 0; i < MAX_CHANNELS; i++) {
+               INIT_LIST_HEAD(&dev->vidq[i].active);
+               dev->vidq[i].dev = dev;
+               dev->vidq[i].channel = i;
+               dev->vidq[i].kthread = NULL;
+               /* register 4 video devices */
+               dev->vdev[i] = video_device_alloc();
+               memcpy(dev->vdev[i], &template, sizeof(struct video_device));
+               dev->vdev[i]->dev = &dev->interface->dev;
+               if (video_nr == -1)
+                       ret = video_register_device(dev->vdev[i],
+                                                   VFL_TYPE_GRABBER,
+                                                   video_nr);
+               else
+                       ret = video_register_device(dev->vdev[i],
+                                                   VFL_TYPE_GRABBER,
+                                                   cur_nr + i);
+               dev->vdev[i]->priv = dev;
+
+               if (ret != 0) {
+                       dev_err(&dev->udev->dev,
+                               "failed to register video device!\n");
+                       return ret;
+               }
+       }
+       printk(KERN_INFO "Sensoray 2255 V4L driver\n");
+       return ret;
+}
+
+static void s2255_exit_v4l(struct s2255_dev *dev)
+{
+       struct list_head *list;
+       int i;
+       /* unregister the video devices */
+       while (!list_empty(&s2255_devlist)) {
+               list = s2255_devlist.next;
+               list_del(list);
+       }
+       for (i = 0; i < MAX_CHANNELS; i++) {
+               if (-1 != dev->vdev[i]->minor)
+                       video_unregister_device(dev->vdev[i]);
+               else
+                       video_device_release(dev->vdev[i]);
+       }
+}
+
+/* this function moves the usb stream read pipe data
+ * into the system buffers.
+ * returns 0 on success, EAGAIN if more data to process( call this
+ * function again).
+ *
+ * Received frame structure:
+ * bytes 0-3:  marker : 0x2255DA4AL (FRAME_MARKER)
+ * bytes 4-7:  channel: 0-3
+ * bytes 8-11: payload size:  size of the frame
+ * bytes 12-payloadsize+12:  frame data
+ */
+static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info)
+{
+       static int dbgsync; /* = 0; */
+       char *pdest;
+       u32 offset = 0;
+       int bsync = 0;
+       int btrunc = 0;
+       char *psrc;
+       unsigned long copy_size;
+       unsigned long size;
+       s32 idx = -1;
+       struct s2255_framei *frm;
+       unsigned char *pdata;
+       unsigned long cur_size;
+       int bsearch = 0;
+       struct s2255_bufferi *buf;
+       dprintk(100, "buffer to user\n");
+
+       idx = dev->cur_frame[dev->cc];
+       buf = &dev->buffer[dev->cc];
+       frm = &buf->frame[idx];
+
+       if (frm->ulState == 0) {
+               frm->ulState = 1;
+               frm->cur_size = 0;
+               bsearch = 1;
+       } else if (frm->ulState == 2) {
+               /* system frame was not freed */
+               dprintk(2, "sys frame not free.  overrun ringbuf\n");
+               bsearch = 1;
+               frm->ulState = 1;
+               frm->cur_size = 0;
+       }
+
+       if (bsearch) {
+               if (*(s32 *) pipe_info->transfer_buffer != FRAME_MARKER) {
+                       u32 jj;
+                       if (dbgsync == 0) {
+                               dprintk(3, "not synched, discarding all packets"
+                                       "until marker\n");
+
+                               dbgsync++;
+                       }
+                       pdata = (unsigned char *)pipe_info->transfer_buffer;
+                       for (jj = 0; jj < (pipe_info->cur_transfer_size - 12);
+                            jj++) {
+                               if (*(s32 *) pdata == FRAME_MARKER) {
+                                       int cc;
+                                       dprintk(3,
+                                               "found frame marker at offset:"
+                                               " %d [%x %x]\n", jj, pdata[0],
+                                               pdata[1]);
+                                       offset = jj;
+                                       bsync = 1;
+                                       cc = *(u32 *) (pdata + sizeof(u32));
+                                       if (cc >= MAX_CHANNELS) {
+                                               printk(KERN_ERR
+                                                      "bad channel\n");
+                                               return -EINVAL;
+                                       }
+                                       /* reverse it */
+                                       dev->cc = G_chnmap[cc];
+                                       break;
+                               }
+                               pdata++;
+                       }
+                       if (bsync == 0)
+                               return -EINVAL;
+               } else {
+                       u32 *pword;
+                       u32 payload;
+                       int cc;
+                       dbgsync = 0;
+                       bsync = 1;
+                       pword = (u32 *) pipe_info->transfer_buffer;
+                       cc = pword[1];
+
+                       if (cc >= MAX_CHANNELS) {
+                               printk("invalid channel found. "
+                                       "throwing out data!\n");
+                               return -EINVAL;
+                       }
+                       dev->cc = G_chnmap[cc];
+                       payload = pword[2];
+                       if (payload != dev->req_image_size[dev->cc]) {
+                               dprintk(1, "[%d][%d]unexpected payload: %d"
+                                       "required: %lu \n", cc, dev->cc,
+                                       payload, dev->req_image_size[dev->cc]);
+                               dev->bad_payload[dev->cc]++;
+                               /* discard the bad frame */
+                               return -EINVAL;
+                       }
+
+               }
+       }
+       /* search done.  now find out if should be acquiring
+          on this channel */
+       if (!dev->b_acquire[dev->cc]) {
+               frm->ulState = 0;
+               return -EINVAL;
+       }
+
+       idx = dev->cur_frame[dev->cc];
+       frm = &dev->buffer[dev->cc].frame[idx];
+
+       if (frm->ulState == 0) {
+               frm->ulState = 1;
+               frm->cur_size = 0;
+       } else if (frm->ulState == 2) {
+               /* system frame ring buffer overrun */
+               dprintk(2, "sys frame overrun.  overwriting frame %d %d\n",
+                       dev->cc, idx);
+               frm->ulState = 1;
+               frm->cur_size = 0;
+       }
+
+       if (bsync) {
+               /* skip the marker 512 bytes (and offset if out of sync) */
+               psrc = (u8 *)pipe_info->transfer_buffer + offset + PREFIX_SIZE;
+       } else {
+               psrc = (u8 *)pipe_info->transfer_buffer;
+       }
+
+       if (frm->lpvbits == NULL) {
+               dprintk(1, "s2255 frame buffer == NULL.%p %p %d %d",
+                       frm, dev, dev->cc, idx);
+               return -ENOMEM;
+       }
+
+       pdest = frm->lpvbits + frm->cur_size;
+
+       if (bsync) {
+               copy_size =
+                   (pipe_info->cur_transfer_size - offset) - PREFIX_SIZE;
+               if (copy_size > pipe_info->cur_transfer_size) {
+                       printk("invalid copy size, overflow!\n");
+                       return -ENOMEM;
+               }
+       } else {
+               copy_size = pipe_info->cur_transfer_size;
+       }
+
+       cur_size = frm->cur_size;
+       size = dev->req_image_size[dev->cc];
+
+       if ((copy_size + cur_size) > size) {
+               copy_size = size - cur_size;
+               btrunc = 1;
+       }
+
+       memcpy(pdest, psrc, copy_size);
+       cur_size += copy_size;
+       frm->cur_size += copy_size;
+       dprintk(50, "cur_size size %lu size %lu \n", cur_size, size);
+
+       if (cur_size >= (size - PREFIX_SIZE)) {
+               u32 cc = dev->cc;
+               frm->ulState = 2;
+               dprintk(2, "****************[%d]Buffer[%d]full*************\n",
+                       cc, idx);
+               dev->last_frame[cc] = dev->cur_frame[cc];
+               dev->cur_frame[cc]++;
+               /* end of system frame ring buffer, start at zero */
+               if ((dev->cur_frame[cc] == SYS_FRAMES) ||
+                   (dev->cur_frame[cc] == dev->buffer[cc].dwFrames))
+                       dev->cur_frame[cc] = 0;
+
+               /* signal the semaphore for this channel */
+               if (dev->b_acquire[cc])
+                       s2255_got_frame(dev, cc);
+               dev->frame_count[cc]++;
+       }
+       /* frame was truncated */
+       if (btrunc) {
+               /* return more data to process */
+               return EAGAIN;
+       }
+       /* done successfully */
+       return 0;
+}
+
+static void s2255_read_video_callback(struct s2255_dev *dev,
+                                     struct s2255_pipeinfo *pipe_info)
+{
+       int res;
+       dprintk(50, "callback read video \n");
+
+       if (dev->cc >= MAX_CHANNELS) {
+               dev->cc = 0;
+               dev_err(&dev->udev->dev, "invalid channel\n");
+               return;
+       }
+       /* otherwise copy to the system buffers */
+       res = save_frame(dev, pipe_info);
+       if (res == EAGAIN)
+               save_frame(dev, pipe_info);
+
+       dprintk(50, "callback read video done\n");
+       return;
+}
+
+static long s2255_vendor_req(struct s2255_dev *dev, unsigned char Request,
+                            u16 Index, u16 Value, void *TransferBuffer,
+                            s32 TransferBufferLength, int bOut)
+{
+       int r;
+       if (!bOut) {
+               r = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0),
+                                   Request,
+                                   USB_TYPE_VENDOR | USB_RECIP_DEVICE |
+                                   USB_DIR_IN,
+                                   Value, Index, TransferBuffer,
+                                   TransferBufferLength, HZ * 5);
+       } else {
+               r = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
+                                   Request, USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                                   Value, Index, TransferBuffer,
+                                   TransferBufferLength, HZ * 5);
+       }
+       return r;
+}
+
+/*
+ * retrieve FX2 firmware version. future use.
+ * @param dev pointer to device extension
+ * @return -1 for fail, else returns firmware version as an int(16 bits)
+ */
+static int s2255_get_fx2fw(struct s2255_dev *dev)
+{
+       int fw;
+       int ret;
+       unsigned char transBuffer[64];
+       ret = s2255_vendor_req(dev, S2255_VR_FW, 0, 0, transBuffer, 2,
+                              S2255_VR_IN);
+       if (ret < 0)
+               dprintk(2, "get fw error: %x\n", ret);
+       fw = transBuffer[0] + (transBuffer[1] << 8);
+       dprintk(2, "Get FW %x %x\n", transBuffer[0], transBuffer[1]);
+       return fw;
+}
+
+/*
+ * Create the system ring buffer to copy frames into from the
+ * usb read pipe.
+ */
+static int s2255_create_sys_buffers(struct s2255_dev *dev, unsigned long chn)
+{
+       unsigned long i;
+       unsigned long reqsize;
+       dprintk(1, "create sys buffers\n");
+       if (chn >= MAX_CHANNELS)
+               return -1;
+
+       dev->buffer[chn].dwFrames = SYS_FRAMES;
+
+       /* always allocate maximum size(PAL) for system buffers */
+       reqsize = SYS_FRAMES_MAXSIZE;
+
+       if (reqsize > SYS_FRAMES_MAXSIZE)
+               reqsize = SYS_FRAMES_MAXSIZE;
+
+       for (i = 0; i < SYS_FRAMES; i++) {
+               /* allocate the frames */
+               dev->buffer[chn].frame[i].lpvbits = vmalloc(reqsize);
+
+               dprintk(1, "valloc %p chan %lu, idx %lu, pdata %p\n",
+                       &dev->buffer[chn].frame[i], chn, i,
+                       dev->buffer[chn].frame[i].lpvbits);
+               dev->buffer[chn].frame[i].size = reqsize;
+               if (dev->buffer[chn].frame[i].lpvbits == NULL) {
+                       printk(KERN_INFO "out of memory.  using less frames\n");
+                       dev->buffer[chn].dwFrames = i;
+                       break;
+               }
+       }
+
+       /* make sure internal states are set */
+       for (i = 0; i < SYS_FRAMES; i++) {
+               dev->buffer[chn].frame[i].ulState = 0;
+               dev->buffer[chn].frame[i].cur_size = 0;
+       }
+
+       dev->cur_frame[chn] = 0;
+       dev->last_frame[chn] = -1;
+       return 0;
+}
+
+static int s2255_release_sys_buffers(struct s2255_dev *dev,
+                                    unsigned long channel)
+{
+       unsigned long i;
+       dprintk(1, "release sys buffers\n");
+       for (i = 0; i < SYS_FRAMES; i++) {
+               if (dev->buffer[channel].frame[i].lpvbits) {
+                       dprintk(1, "vfree %p\n",
+                               dev->buffer[channel].frame[i].lpvbits);
+                       vfree(dev->buffer[channel].frame[i].lpvbits);
+               }
+               dev->buffer[channel].frame[i].lpvbits = NULL;
+       }
+       return 0;
+}
+
+static int s2255_board_init(struct s2255_dev *dev)
+{
+       int j;
+       struct s2255_mode mode_def = DEF_MODEI_NTSC_CONT;
+       int fw_ver;
+       dprintk(4, "board init: %p", dev);
+
+       for (j = 0; j < MAX_PIPE_BUFFERS; j++) {
+               struct s2255_pipeinfo *pipe = &dev->pipes[j];
+
+               memset(pipe, 0, sizeof(*pipe));
+               pipe->dev = dev;
+               pipe->cur_transfer_size = DEFAULT_PIPE_USBBLOCK;
+               pipe->max_transfer_size = MAX_PIPE_USBBLOCK;
+
+               if (pipe->cur_transfer_size > pipe->max_transfer_size)
+                       pipe->cur_transfer_size = pipe->max_transfer_size;
+               pipe->transfer_buffer = kzalloc(pipe->max_transfer_size,
+                                               GFP_KERNEL);
+               if (pipe->transfer_buffer == NULL) {
+                       dprintk(1, "out of memory!\n");
+                       return -ENOMEM;
+               }
+
+       }
+
+       /* query the firmware */
+       fw_ver = s2255_get_fx2fw(dev);
+
+       printk(KERN_INFO "2255 usb firmware version %d \n", fw_ver);
+       if (fw_ver < CUR_USB_FWVER)
+               err("usb firmware not up to date %d\n", fw_ver);
+
+       for (j = 0; j < MAX_CHANNELS; j++) {
+               dev->b_acquire[j] = 0;
+               dev->mode[j] = mode_def;
+               dev->cur_fmt[j] = &formats[0];
+               dev->mode[j].restart = 1;
+               dev->req_image_size[j] = get_transfer_size(&mode_def);
+               dev->frame_count[j] = 0;
+               /* create the system buffers */
+               s2255_create_sys_buffers(dev, j);
+       }
+       /* start read pipe */
+       s2255_start_readpipe(dev);
+
+       dprintk(1, "S2255: board initialized\n");
+       return 0;
+}
+
+static int s2255_board_shutdown(struct s2255_dev *dev)
+{
+       u32 i;
+
+       dprintk(1, "S2255: board shutdown: %p", dev);
+
+       for (i = 0; i < MAX_CHANNELS; i++) {
+               if (dev->b_acquire[i])
+                       s2255_stop_acquire(dev, i);
+       }
+
+       s2255_stop_readpipe(dev);
+
+       for (i = 0; i < MAX_CHANNELS; i++)
+               s2255_release_sys_buffers(dev, i);
+
+       /* release transfer buffers */
+       for (i = 0; i < MAX_PIPE_BUFFERS; i++) {
+               struct s2255_pipeinfo *pipe = &dev->pipes[i];
+               kfree(pipe->transfer_buffer);
+       }
+       return 0;
+}
+
+static void read_pipe_completion(struct urb *purb)
+{
+       struct s2255_pipeinfo *pipe_info;
+       struct s2255_dev *dev;
+       int status;
+       int pipe;
+
+       pipe_info = purb->context;
+       dprintk(100, "read pipe completion %p, status %d\n", purb,
+               purb->status);
+       if (pipe_info == NULL) {
+               err("no context !");
+               return;
+       }
+
+       dev = pipe_info->dev;
+       if (dev == NULL) {
+               err("no context !");
+               return;
+       }
+       status = purb->status;
+       if (status != 0) {
+               dprintk(2, "read_pipe_completion: err\n");
+               return;
+       }
+
+       if (pipe_info->state == 0) {
+               dprintk(2, "exiting USB pipe");
+               return;
+       }
+
+       s2255_read_video_callback(dev, pipe_info);
+
+       pipe_info->err_count = 0;
+       pipe = usb_rcvbulkpipe(dev->udev, dev->read_endpoint);
+       /* reuse urb */
+       usb_fill_bulk_urb(pipe_info->stream_urb, dev->udev,
+                         pipe,
+                         pipe_info->transfer_buffer,
+                         pipe_info->cur_transfer_size,
+                         read_pipe_completion, pipe_info);
+
+       if (pipe_info->state != 0) {
+               if (usb_submit_urb(pipe_info->stream_urb, GFP_KERNEL)) {
+                       dev_err(&dev->udev->dev, "error submitting urb\n");
+                       usb_free_urb(pipe_info->stream_urb);
+               }
+       } else {
+               dprintk(2, "read pipe complete state 0\n");
+       }
+       return;
+}
+
+static int s2255_start_readpipe(struct s2255_dev *dev)
+{
+       int pipe;
+       int retval;
+       int i;
+       struct s2255_pipeinfo *pipe_info = dev->pipes;
+       pipe = usb_rcvbulkpipe(dev->udev, dev->read_endpoint);
+       dprintk(2, "start pipe IN %d\n", dev->read_endpoint);
+
+       for (i = 0; i < MAX_PIPE_BUFFERS; i++) {
+               pipe_info->state = 1;
+               pipe_info->buf_index = (u32) i;
+               pipe_info->priority_set = 0;
+               pipe_info->stream_urb = usb_alloc_urb(0, GFP_KERNEL);
+               if (!pipe_info->stream_urb) {
+                       dev_err(&dev->udev->dev,
+                               "ReadStream: Unable to alloc URB");
+                       return -ENOMEM;
+               }
+               /* transfer buffer allocated in board_init */
+               usb_fill_bulk_urb(pipe_info->stream_urb, dev->udev,
+                                 pipe,
+                                 pipe_info->transfer_buffer,
+                                 pipe_info->cur_transfer_size,
+                                 read_pipe_completion, pipe_info);
+
+               pipe_info->urb_size = sizeof(pipe_info->stream_urb);
+               dprintk(4, "submitting URB %p\n", pipe_info->stream_urb);
+               retval = usb_submit_urb(pipe_info->stream_urb, GFP_KERNEL);
+               if (retval) {
+                       printk(KERN_ERR "s2255: start read pipe failed\n");
+                       return retval;
+               }
+       }
+
+       return 0;
+}
+
+/* starts acquisition process */
+static int s2255_start_acquire(struct s2255_dev *dev, unsigned long chn)
+{
+       unsigned char *buffer;
+       int res;
+       unsigned long chn_rev;
+       int j;
+       if (chn >= MAX_CHANNELS) {
+               dprintk(2, "start acquire failed, bad channel %lu\n", chn);
+               return -1;
+       }
+
+       chn_rev = G_chnmap[chn];
+       dprintk(1, "S2255: start acquire %lu \n", chn);
+
+       buffer = kzalloc(512, GFP_KERNEL);
+       if (buffer == NULL) {
+               dev_err(&dev->udev->dev, "out of mem\n");
+               return -ENOMEM;
+       }
+
+       dev->last_frame[chn] = -1;
+       dev->bad_payload[chn] = 0;
+       dev->cur_frame[chn] = 0;
+       for (j = 0; j < SYS_FRAMES; j++) {
+               dev->buffer[chn].frame[j].ulState = 0;
+               dev->buffer[chn].frame[j].cur_size = 0;
+       }
+
+       /* send the start command */
+       *(u32 *) buffer = IN_DATA_TOKEN;
+       *((u32 *) buffer + 1) = (u32) chn_rev;
+       *((u32 *) buffer + 2) = (u32) CMD_START;
+       res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512);
+       if (res != 0)
+               dev_err(&dev->udev->dev, "CMD_START error\n");
+
+       dprintk(2, "start acquire exit[%lu] %d \n", chn, res);
+       kfree(buffer);
+       return 0;
+}
+
+static int s2255_stop_acquire(struct s2255_dev *dev, unsigned long chn)
+{
+       unsigned char *buffer;
+       int res;
+       unsigned long chn_rev;
+
+       if (chn >= MAX_CHANNELS) {
+               dprintk(2, "stop acquire failed, bad channel %lu\n", chn);
+               return -1;
+       }
+       chn_rev = G_chnmap[chn];
+
+       buffer = kzalloc(512, GFP_KERNEL);
+       if (buffer == NULL) {
+               dev_err(&dev->udev->dev, "out of mem\n");
+               return -ENOMEM;
+       }
+
+       /* send the stop command */
+       dprintk(4, "stop acquire %lu\n", chn);
+       *(u32 *) buffer = IN_DATA_TOKEN;
+       *((u32 *) buffer + 1) = (u32) chn_rev;
+       *((u32 *) buffer + 2) = CMD_STOP;
+       res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512);
+
+       if (res != 0)
+               dev_err(&dev->udev->dev, "CMD_STOP error\n");
+
+       dprintk(4, "stop acquire: releasing states \n");
+
+       kfree(buffer);
+       dev->b_acquire[chn] = 0;
+
+       return 0;
+}
+
+static void s2255_stop_readpipe(struct s2255_dev *dev)
+{
+       int j;
+
+       if (dev == NULL) {
+               err("s2255: invalid device");
+               return;
+       }
+       dprintk(4, "stop read pipe\n");
+       for (j = 0; j < MAX_PIPE_BUFFERS; j++) {
+               struct s2255_pipeinfo *pipe_info = &dev->pipes[j];
+               if (pipe_info) {
+                       if (pipe_info->state == 0)
+                               continue;
+                       pipe_info->state = 0;
+                       pipe_info->prev_state = 1;
+
+               }
+       }
+
+       for (j = 0; j < MAX_PIPE_BUFFERS; j++) {
+               struct s2255_pipeinfo *pipe_info = &dev->pipes[j];
+               if (pipe_info->stream_urb) {
+                       /* cancel urb */
+                       usb_kill_urb(pipe_info->stream_urb);
+                       usb_free_urb(pipe_info->stream_urb);
+                       pipe_info->stream_urb = NULL;
+               }
+       }
+       dprintk(2, "s2255 stop read pipe: %d\n", j);
+       return;
+}
+
+static void s2255_fwload_start(struct s2255_dev *dev)
+{
+       dev->fw_data->fw_size = dev->fw_data->fw->size;
+       atomic_set(&dev->fw_data->fw_state, S2255_FW_NOTLOADED);
+       memcpy(dev->fw_data->pfw_data,
+              dev->fw_data->fw->data, CHUNK_SIZE);
+       dev->fw_data->fw_loaded = CHUNK_SIZE;
+       usb_fill_bulk_urb(dev->fw_data->fw_urb, dev->udev,
+                         usb_sndbulkpipe(dev->udev, 2),
+                         dev->fw_data->pfw_data,
+                         CHUNK_SIZE, s2255_fwchunk_complete,
+                         dev->fw_data);
+       mod_timer(&dev->timer, jiffies + HZ);
+}
+
+/* standard usb probe function */
+static int s2255_probe(struct usb_interface *interface,
+                      const struct usb_device_id *id)
+{
+       struct s2255_dev *dev = NULL;
+       struct usb_host_interface *iface_desc;
+       struct usb_endpoint_descriptor *endpoint;
+       int i;
+       int retval = -ENOMEM;
+
+       dprintk(2, "s2255: probe\n");
+
+       /* allocate memory for our device state and initialize it to zero */
+       dev = kzalloc(sizeof(struct s2255_dev), GFP_KERNEL);
+       if (dev == NULL) {
+               err("s2255: out of memory");
+               goto error;
+       }
+
+       dev->fw_data = kzalloc(sizeof(struct s2255_fw), GFP_KERNEL);
+       if (!dev->fw_data)
+               goto error;
+
+       mutex_init(&dev->lock);
+       mutex_init(&dev->open_lock);
+
+       /* grab usb_device and save it */
+       dev->udev = usb_get_dev(interface_to_usbdev(interface));
+       if (dev->udev == NULL) {
+               dev_err(&interface->dev, "null usb device\n");
+               retval = -ENODEV;
+               goto error;
+       }
+       kref_init(&dev->kref);
+       dprintk(1, "dev: %p, kref: %p udev %p interface %p\n", dev, &dev->kref,
+               dev->udev, interface);
+       dev->interface = interface;
+       /* set up the endpoint information  */
+       iface_desc = interface->cur_altsetting;
+       dprintk(1, "num endpoints %d\n", iface_desc->desc.bNumEndpoints);
+       for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+               endpoint = &iface_desc->endpoint[i].desc;
+               if (!dev->read_endpoint && usb_endpoint_is_bulk_in(endpoint)) {
+                       /* we found the bulk in endpoint */
+                       dev->read_endpoint = endpoint->bEndpointAddress;
+               }
+       }
+
+       if (!dev->read_endpoint) {
+               dev_err(&interface->dev, "Could not find bulk-in endpoint");
+               goto error;
+       }
+
+       /* set intfdata */
+       usb_set_intfdata(interface, dev);
+
+       dprintk(100, "after intfdata %p\n", dev);
+
+       init_timer(&dev->timer);
+       dev->timer.function = s2255_timer;
+       dev->timer.data = (unsigned long)dev->fw_data;
+
+       init_waitqueue_head(&dev->fw_data->wait_fw);
+
+
+       dev->fw_data->fw_urb = usb_alloc_urb(0, GFP_KERNEL);
+
+       if (!dev->fw_data->fw_urb) {
+               dev_err(&interface->dev, "out of memory!\n");
+               goto error;
+       }
+       dev->fw_data->pfw_data = kzalloc(CHUNK_SIZE, GFP_KERNEL);
+       if (!dev->fw_data->pfw_data) {
+               dev_err(&interface->dev, "out of memory!\n");
+               goto error;
+       }
+       /* load the first chunk */
+       if (request_firmware(&dev->fw_data->fw,
+                            FIRMWARE_FILE_NAME, &dev->udev->dev)) {
+               printk(KERN_ERR "sensoray 2255 failed to get firmware\n");
+               goto error;
+       }
+
+       /* loads v4l specific */
+       s2255_probe_v4l(dev);
+       /* load 2255 board specific */
+       s2255_board_init(dev);
+
+       dprintk(4, "before probe done %p\n", dev);
+       spin_lock_init(&dev->slock);
+
+       s2255_fwload_start(dev);
+       dev_info(&interface->dev, "Sensoray 2255 detected\n");
+       return 0;
+error:
+       return retval;
+}
+
+/* disconnect routine. when board is removed physically or with rmmod */
+static void s2255_disconnect(struct usb_interface *interface)
+{
+       struct s2255_dev *dev = NULL;
+       dprintk(1, "s2255: disconnect interface %p\n", interface);
+       dev = usb_get_intfdata(interface);
+       if (dev) {
+               kref_put(&dev->kref, s2255_destroy);
+               dprintk(1, "s2255drv: disconnect\n");
+               dev_info(&interface->dev, "s2255usb now disconnected\n");
+       }
+       usb_set_intfdata(interface, NULL);
+}
+
+static struct usb_driver s2255_driver = {
+       .name = "s2255",
+       .probe = s2255_probe,
+       .disconnect = s2255_disconnect,
+       .id_table = s2255_table,
+};
+
+static int __init usb_s2255_init(void)
+{
+       int result;
+
+       /* register this driver with the USB subsystem */
+       result = usb_register(&s2255_driver);
+
+       if (result)
+               err("usb_register failed. Error number %d", result);
+
+       dprintk(2, "s2255_init: done\n");
+       return result;
+}
+
+static void __exit usb_s2255_exit(void)
+{
+       usb_deregister(&s2255_driver);
+}
+
+module_init(usb_s2255_init);
+module_exit(usb_s2255_exit);
+
+MODULE_DESCRIPTION("Sensoray 2255 Video for Linux driver");
+MODULE_AUTHOR("Dean Anderson (Sensoray Company Inc.)");
+MODULE_LICENSE("GPL");
index 996b49491f5a2f29c5ca6c3ed02604a47239faa1..03e772130b55f2edeec58f3febce448b1f970a72 100644 (file)
@@ -66,6 +66,7 @@ static struct video_device saa_template;      /* Declared near bottom */
 
 /* Addresses to scan */
 static unsigned short normal_i2c[]      = { I2C_ADDRESS, I2C_CLIENT_END };
+
 I2C_CLIENT_INSMOD;
 
 static struct i2c_client client_template;
index ec8c65dc8408506ae02786a4dc7e2e982f63f21f..fde99d9ee71f5716f7acdcefcc3625a3cc05c7e6 100644 (file)
@@ -130,6 +130,7 @@ static struct video_device saa_template;    /* Declared near bottom */
 
 /* Addresses to scan */
 static unsigned short normal_i2c[] = {34>>1,I2C_CLIENT_END};
+
 I2C_CLIENT_INSMOD;
 
 static struct i2c_client client_template;
index 716ee7f64df3c017deffdd15dbf899e48fb8840c..f05024259f0150707957dfc37b6e973ae0bcb011 100644 (file)
@@ -31,7 +31,6 @@
 #include <linux/wait.h>
 #include <asm/uaccess.h>
 
-
 #include <media/rds.h>
 
 /* Addresses to scan */
index 435c083cc542af91af65a2fca0cac67cae643e94..bcd1c8f6cf6b8bb5f3572a7a0caa4682680a1c25 100644 (file)
@@ -67,7 +67,6 @@ static unsigned short normal_i2c[] = {
                0x42 >> 1, 0x40 >> 1,   /* SAA7114, SAA7115 and SAA7118 */
                I2C_CLIENT_END };
 
-
 I2C_CLIENT_INSMOD;
 
 struct saa711x_state {
@@ -1558,7 +1557,7 @@ static int saa7115_remove(struct i2c_client *client)
 }
 
 static const struct i2c_device_id saa7115_id[] = {
-       { "saa711x", 1 }, /* autodetect */
+       { "saa7115_auto", 1 }, /* autodetect */
        { "saa7111", 0 },
        { "saa7113", 0 },
        { "saa7114", 0 },
@@ -1577,4 +1576,3 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = {
        .legacy_class = I2C_CLASS_TV_ANALOG | I2C_CLASS_TV_DIGITAL,
        .id_table = saa7115_id,
 };
-
diff --git a/drivers/media/video/saa711x.c b/drivers/media/video/saa711x.c
deleted file mode 100644 (file)
index cedb988..0000000
+++ /dev/null
@@ -1,584 +0,0 @@
-/*
- * saa711x - Philips SAA711x video decoder driver version 0.0.1
- *
- * To do: Now, it handles only saa7113/7114. Should be improved to
- * handle all Philips saa711x devices.
- *
- * Based on saa7113 driver from Dave Perks <dperks@ibm.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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/kernel.h>
-#include <linux/major.h>
-#include <linux/slab.h>
-#include <linux/mm.h>
-#include <linux/signal.h>
-#include <asm/io.h>
-#include <asm/pgtable.h>
-#include <asm/page.h>
-#include <linux/types.h>
-#include <asm/uaccess.h>
-#include <linux/videodev.h>
-
-MODULE_DESCRIPTION("Philips SAA711x video decoder driver");
-MODULE_AUTHOR("Dave Perks, Jose Ignacio Gijon, Joerg Heckenbach, Mark McClelland, Dwaine Garden");
-MODULE_LICENSE("GPL");
-
-#include <linux/i2c.h>
-
-#define I2C_NAME(s) (s)->name
-
-#include <linux/video_decoder.h>
-
-static int debug;
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, " Set the default Debug level.  Default: 0 (Off) - (0-1)");
-
-
-#define dprintk(num, format, args...) \
-       do { \
-               if (debug >= num) \
-                       printk(format, ##args); \
-       } while (0)
-
-/* ----------------------------------------------------------------------- */
-
-struct saa711x {
-       unsigned char reg[32];
-
-       int norm;
-       int input;
-       int enable;
-       int bright;
-       int contrast;
-       int hue;
-       int sat;
-};
-
-#define   I2C_SAA7113        0x4A
-#define   I2C_SAA7114        0x42
-
-/* ----------------------------------------------------------------------- */
-
-static inline int
-saa711x_write (struct i2c_client *client,
-              u8                 reg,
-              u8                 value)
-{
-       struct saa711x *decoder = i2c_get_clientdata(client);
-
-       decoder->reg[reg] = value;
-       return i2c_smbus_write_byte_data(client, reg, value);
-}
-
-static int
-saa711x_write_block (struct i2c_client *client,
-                    const u8          *data,
-                    unsigned int       len)
-{
-       int ret = -1;
-       u8 reg;
-
-       /* the saa711x has an autoincrement function, use it if
-        * the adapter understands raw I2C */
-       if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
-               /* do raw I2C, not smbus compatible */
-               struct saa711x *decoder = i2c_get_clientdata(client);
-               struct i2c_msg msg;
-               u8 block_data[32];
-
-               msg.addr = client->addr;
-               msg.flags = 0;
-               while (len >= 2) {
-                       msg.buf = (char *) block_data;
-                       msg.len = 0;
-                       block_data[msg.len++] = reg = data[0];
-                       do {
-                               block_data[msg.len++] =
-                                   decoder->reg[reg++] = data[1];
-                               len -= 2;
-                               data += 2;
-                       } while (len >= 2 && data[0] == reg &&
-                                msg.len < 32);
-                       if ((ret = i2c_transfer(client->adapter,
-                                               &msg, 1)) < 0)
-                               break;
-               }
-       } else {
-               /* do some slow I2C emulation kind of thing */
-               while (len >= 2) {
-                       reg = *data++;
-                       if ((ret = saa711x_write(client, reg,
-                                                *data++)) < 0)
-                               break;
-                       len -= 2;
-               }
-       }
-
-       return ret;
-}
-
-static int
-saa711x_init_decoder (struct i2c_client *client,
-             struct video_decoder_init *init)
-{
-       return saa711x_write_block(client, init->data, init->len);
-}
-
-static inline int
-saa711x_read (struct i2c_client *client,
-             u8                 reg)
-{
-       return i2c_smbus_read_byte_data(client, reg);
-}
-
-/* ----------------------------------------------------------------------- */
-
-static const unsigned char saa711x_i2c_init[] = {
-       0x00, 0x00,     /* PH711x_CHIP_VERSION 00 - ID byte */
-       0x01, 0x08,     /* PH711x_INCREMENT_DELAY - (1) (1) (1) (1) IDEL3 IDEL2 IDELL1 IDEL0 */
-       0x02, 0xc0,     /* PH711x_ANALOG_INPUT_CONTR_1 - FUSE1 FUSE0 GUDL1 GUDL0 MODE3 MODE2 MODE1 MODE0 */
-       0x03, 0x23,     /* PH711x_ANALOG_INPUT_CONTR_2 - (1) HLNRS VBSL WPOFF HOLDG GAFIX GAI28 GAI18 */
-       0x04, 0x00,     /* PH711x_ANALOG_INPUT_CONTR_3 - GAI17 GAI16 GAI15 GAI14 GAI13 GAI12 GAI11 GAI10 */
-       0x05, 0x00,     /* PH711x_ANALOG_INPUT_CONTR_4 - GAI27 GAI26 GAI25 GAI24 GAI23 GAI22 GAI21 GAI20 */
-       0x06, 0xeb,     /* PH711x_HORIZONTAL_SYNC_START - HSB7 HSB6 HSB5 HSB4 HSB3 HSB2 HSB1 HSB0 */
-       0x07, 0xe0,     /* PH711x_HORIZONTAL_SYNC_STOP - HSS7 HSS6 HSS5 HSS4 HSS3 HSS2 HSS1 HSS0 */
-       0x08, 0x88,     /* PH711x_SYNC_CONTROL - AUFD FSEL FOET HTC1 HTC0 HPLL VNOI1 VNOI0 */
-       0x09, 0x00,     /* PH711x_LUMINANCE_CONTROL - BYPS PREF BPSS1 BPSS0 VBLB UPTCV APER1 APER0 */
-       0x0a, 0x80,     /* PH711x_LUMINANCE_BRIGHTNESS - BRIG7 BRIG6 BRIG5 BRIG4 BRIG3 BRIG2 BRIG1 BRIG0 */
-       0x0b, 0x47,     /* PH711x_LUMINANCE_CONTRAST - CONT7 CONT6 CONT5 CONT4 CONT3 CONT2 CONT1 CONT0 */
-       0x0c, 0x40,     /* PH711x_CHROMA_SATURATION - SATN7 SATN6 SATN5 SATN4 SATN3 SATN2 SATN1 SATN0 */
-       0x0d, 0x00,     /* PH711x_CHROMA_HUE_CONTROL - HUEC7 HUEC6 HUEC5 HUEC4 HUEC3 HUEC2 HUEC1 HUEC0 */
-       0x0e, 0x01,     /* PH711x_CHROMA_CONTROL - CDTO CSTD2 CSTD1 CSTD0 DCCF FCTC CHBW1 CHBW0 */
-       0x0f, 0xaa,     /* PH711x_CHROMA_GAIN_CONTROL - ACGC CGAIN6 CGAIN5 CGAIN4 CGAIN3 CGAIN2 CGAIN1 CGAIN0 */
-       0x10, 0x00,     /* PH711x_FORMAT_DELAY_CONTROL - OFTS1 OFTS0 HDEL1 HDEL0 VRLN YDEL2 YDEL1 YDEL0 */
-       0x11, 0x1C,     /* PH711x_OUTPUT_CONTROL_1 - GPSW1 CM99 GPSW0 HLSEL OEYC OERT VIPB COLO */
-       0x12, 0x01,     /* PH711x_OUTPUT_CONTROL_2 - RTSE13 RTSE12 RTSE11 RTSE10 RTSE03 RTSE02 RTSE01 RTSE00 */
-       0x13, 0x00,     /* PH711x_OUTPUT_CONTROL_3 - ADLSB (1) (1) OLDSB FIDP (1) AOSL1 AOSL0 */
-       0x14, 0x00,     /* RESERVED 14 - (1) (1) (1) (1) (1) (1) (1) (1) */
-       0x15, 0x00,     /* PH711x_V_GATE1_START - VSTA7 VSTA6 VSTA5 VSTA4 VSTA3 VSTA2 VSTA1 VSTA0 */
-       0x16, 0x00,     /* PH711x_V_GATE1_STOP - VSTO7 VSTO6 VSTO5 VSTO4 VSTO3 VSTO2 VSTO1 VSTO0 */
-       0x17, 0x00,     /* PH711x_V_GATE1_MSB - (1) (1) (1) (1) (1) (1) VSTO8 VSTA8 */
-};
-
-static int
-saa711x_command (struct i2c_client *client,
-                unsigned int       cmd,
-                void              *arg)
-{
-       struct saa711x *decoder = i2c_get_clientdata(client);
-
-       switch (cmd) {
-
-       case 0:
-       case DECODER_INIT:
-       {
-               struct video_decoder_init *init = arg;
-               if (NULL != init)
-                       return saa711x_init_decoder(client, init);
-               else {
-                       struct video_decoder_init vdi;
-                       vdi.data = saa711x_i2c_init;
-                       vdi.len = sizeof(saa711x_i2c_init);
-                       return saa711x_init_decoder(client, &vdi);
-               }
-       }
-
-       case DECODER_DUMP:
-       {
-               int i;
-
-               for (i = 0; i < 32; i += 16) {
-                       int j;
-
-                       printk(KERN_DEBUG "%s: %03x", I2C_NAME(client), i);
-                       for (j = 0; j < 16; ++j) {
-                               printk(" %02x",
-                                      saa711x_read(client, i + j));
-                       }
-                       printk("\n");
-               }
-       }
-               break;
-
-       case DECODER_GET_CAPABILITIES:
-       {
-               struct video_decoder_capability *cap = arg;
-
-               cap->flags = VIDEO_DECODER_PAL |
-                            VIDEO_DECODER_NTSC |
-                            VIDEO_DECODER_SECAM |
-                            VIDEO_DECODER_AUTO |
-                            VIDEO_DECODER_CCIR;
-               cap->inputs = 8;
-               cap->outputs = 1;
-       }
-               break;
-
-       case DECODER_GET_STATUS:
-       {
-               int *iarg = arg;
-               int status;
-               int res;
-
-               status = saa711x_read(client, 0x1f);
-               dprintk(1, KERN_DEBUG "%s status: 0x%02x\n", I2C_NAME(client),
-                       status);
-               res = 0;
-               if ((status & (1 << 6)) == 0) {
-                       res |= DECODER_STATUS_GOOD;
-               }
-               switch (decoder->norm) {
-               case VIDEO_MODE_NTSC:
-                       res |= DECODER_STATUS_NTSC;
-                       break;
-               case VIDEO_MODE_PAL:
-                       res |= DECODER_STATUS_PAL;
-                       break;
-               case VIDEO_MODE_SECAM:
-                       res |= DECODER_STATUS_SECAM;
-                       break;
-               default:
-               case VIDEO_MODE_AUTO:
-                       if ((status & (1 << 5)) != 0) {
-                               res |= DECODER_STATUS_NTSC;
-                       } else {
-                               res |= DECODER_STATUS_PAL;
-                       }
-                       break;
-               }
-               if ((status & (1 << 0)) != 0) {
-                       res |= DECODER_STATUS_COLOR;
-               }
-               *iarg = res;
-       }
-               break;
-
-       case DECODER_SET_GPIO:
-       {
-               int *iarg = arg;
-               if (0 != *iarg) {
-                       saa711x_write(client, 0x11,
-                               (decoder->reg[0x11] | 0x80));
-               } else {
-                       saa711x_write(client, 0x11,
-                               (decoder->reg[0x11] & 0x7f));
-               }
-               break;
-       }
-
-       case DECODER_SET_VBI_BYPASS:
-       {
-               int *iarg = arg;
-               if (0 != *iarg) {
-                       saa711x_write(client, 0x13,
-                               (decoder->reg[0x13] & 0xf0) | 0x0a);
-               } else {
-                       saa711x_write(client, 0x13,
-                               (decoder->reg[0x13] & 0xf0));
-               }
-               break;
-       }
-
-       case DECODER_SET_NORM:
-       {
-               int *iarg = arg;
-
-               switch (*iarg) {
-
-               case VIDEO_MODE_NTSC:
-                       saa711x_write(client, 0x08,
-                                     (decoder->reg[0x08] & 0x3f) | 0x40);
-                       saa711x_write(client, 0x0e,
-                                     (decoder->reg[0x0e] & 0x8f));
-                       break;
-
-               case VIDEO_MODE_PAL:
-                       saa711x_write(client, 0x08,
-                                     (decoder->reg[0x08] & 0x3f) | 0x00);
-                       saa711x_write(client, 0x0e,
-                                     (decoder->reg[0x0e] & 0x8f));
-                       break;
-
-               case VIDEO_MODE_SECAM:
-                       saa711x_write(client, 0x08,
-                                     (decoder->reg[0x08] & 0x3f) | 0x00);
-                       saa711x_write(client, 0x0e,
-                                     (decoder->reg[0x0e] & 0x8f) | 0x50);
-                       break;
-
-               case VIDEO_MODE_AUTO:
-                       saa711x_write(client, 0x08,
-                                     (decoder->reg[0x08] & 0x3f) | 0x80);
-                       saa711x_write(client, 0x0e,
-                                     (decoder->reg[0x0e] & 0x8f));
-                       break;
-
-               default:
-                       return -EINVAL;
-
-               }
-               decoder->norm = *iarg;
-       }
-               break;
-
-       case DECODER_SET_INPUT:
-       {
-               int *iarg = arg;
-               if (*iarg < 0 || *iarg > 9) {
-                       return -EINVAL;
-               }
-               if (decoder->input != *iarg) {
-                       decoder->input = *iarg;
-                       /* select mode */
-                       saa711x_write(client, 0x02,
-                                     (decoder->reg[0x02] & 0xf0) | decoder->input);
-                       /* bypass chrominance trap for modes 4..7 */
-                       saa711x_write(client, 0x09,
-                                     (decoder->reg[0x09] & 0x7f) | ((decoder->input > 3) ? 0x80 : 0));
-               }
-       }
-               break;
-
-       case DECODER_SET_OUTPUT:
-       {
-               int *iarg = arg;
-
-               /* not much choice of outputs */
-               if (*iarg != 0) {
-                       return -EINVAL;
-               }
-       }
-               break;
-
-       case DECODER_ENABLE_OUTPUT:
-       {
-               int *iarg = arg;
-               int enable = (*iarg != 0);
-
-               if (decoder->enable != enable) {
-                       decoder->enable = enable;
-
-                       /* RJ: If output should be disabled (for
-                        * playing videos), we also need a open PLL.
-                        * The input is set to 0 (where no input
-                        * source is connected), although this
-                        * is not necessary.
-                        *
-                        * If output should be enabled, we have to
-                        * reverse the above.
-                        */
-
-                       if (decoder->enable) {
-                               saa711x_write(client, 0x02,
-                                             (decoder->
-                                              reg[0x02] & 0xf8) |
-                                             decoder->input);
-                               saa711x_write(client, 0x08,
-                                             (decoder->reg[0x08] & 0xfb));
-                               saa711x_write(client, 0x11,
-                                             (decoder->
-                                              reg[0x11] & 0xf3) | 0x0c);
-                       } else {
-                               saa711x_write(client, 0x02,
-                                             (decoder->reg[0x02] & 0xf8));
-                               saa711x_write(client, 0x08,
-                                             (decoder->
-                                              reg[0x08] & 0xfb) | 0x04);
-                               saa711x_write(client, 0x11,
-                                             (decoder->reg[0x11] & 0xf3));
-                       }
-               }
-       }
-               break;
-
-       case DECODER_SET_PICTURE:
-       {
-               struct video_picture *pic = arg;
-
-               if (decoder->bright != pic->brightness) {
-                       /* We want 0 to 255 we get 0-65535 */
-                       decoder->bright = pic->brightness;
-                       saa711x_write(client, 0x0a, decoder->bright >> 8);
-               }
-               if (decoder->contrast != pic->contrast) {
-                       /* We want 0 to 127 we get 0-65535 */
-                       decoder->contrast = pic->contrast;
-                       saa711x_write(client, 0x0b,
-                                     decoder->contrast >> 9);
-               }
-               if (decoder->sat != pic->colour) {
-                       /* We want 0 to 127 we get 0-65535 */
-                       decoder->sat = pic->colour;
-                       saa711x_write(client, 0x0c, decoder->sat >> 9);
-               }
-               if (decoder->hue != pic->hue) {
-                       /* We want -128 to 127 we get 0-65535 */
-                       decoder->hue = pic->hue;
-                       saa711x_write(client, 0x0d,
-                                     (decoder->hue - 32768) >> 8);
-               }
-       }
-               break;
-
-       default:
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-/* ----------------------------------------------------------------------- */
-
-/*
- * Generic i2c probe
- * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
- */
-
-/* standard i2c insmod options */
-static unsigned short normal_i2c[] = {
-       I2C_SAA7113>>1,         /* saa7113 */
-       I2C_SAA7114>>1,         /* saa7114 */
-       I2C_CLIENT_END
-};
-
-I2C_CLIENT_INSMOD;
-
-
-static struct i2c_driver i2c_driver_saa711x;
-
-static int
-saa711x_detect_client (struct i2c_adapter *adapter,
-                      int                 address,
-                      int                 kind)
-{
-       int i;
-       struct i2c_client *client;
-       struct saa711x *decoder;
-       struct video_decoder_init vdi;
-
-       dprintk(1,
-               KERN_INFO
-               "saa711x.c: detecting saa711x client on address 0x%x\n",
-               address << 1);
-
-       /* Check if the adapter supports the needed features */
-       if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
-               return 0;
-
-       client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
-       if (client == 0)
-               return -ENOMEM;
-       client->addr = address;
-       client->adapter = adapter;
-       client->driver = &i2c_driver_saa711x;
-       strlcpy(I2C_NAME(client), "saa711x", sizeof(I2C_NAME(client)));
-       decoder = kzalloc(sizeof(struct saa711x), GFP_KERNEL);
-       if (decoder == NULL) {
-               kfree(client);
-               return -ENOMEM;
-       }
-       decoder->norm = VIDEO_MODE_NTSC;
-       decoder->input = 0;
-       decoder->enable = 1;
-       decoder->bright = 32768;
-       decoder->contrast = 32768;
-       decoder->hue = 32768;
-       decoder->sat = 32768;
-       i2c_set_clientdata(client, decoder);
-
-       i = i2c_attach_client(client);
-       if (i) {
-               kfree(client);
-               kfree(decoder);
-               return i;
-       }
-
-       vdi.data = saa711x_i2c_init;
-       vdi.len = sizeof(saa711x_i2c_init);
-       i = saa711x_init_decoder(client, &vdi);
-       if (i < 0) {
-               dprintk(1, KERN_ERR "%s_attach error: init status %d\n",
-                       I2C_NAME(client), i);
-       } else {
-               dprintk(1,
-                       KERN_INFO
-                       "%s_attach: chip version %x at address 0x%x\n",
-                       I2C_NAME(client), saa711x_read(client, 0x00) >> 4,
-                       client->addr << 1);
-       }
-
-       return 0;
-}
-
-static int
-saa711x_attach_adapter (struct i2c_adapter *adapter)
-{
-       dprintk(1,
-               KERN_INFO
-               "saa711x.c: starting probe for adapter %s (0x%x)\n",
-               I2C_NAME(adapter), adapter->id);
-       return i2c_probe(adapter, &addr_data, &saa711x_detect_client);
-}
-
-static int
-saa711x_detach_client (struct i2c_client *client)
-{
-       struct saa711x *decoder = i2c_get_clientdata(client);
-       int err;
-
-       err = i2c_detach_client(client);
-       if (err) {
-               return err;
-       }
-
-       kfree(decoder);
-       kfree(client);
-
-       return 0;
-}
-
-/* ----------------------------------------------------------------------- */
-
-static struct i2c_driver i2c_driver_saa711x = {
-       .driver = {
-               .name = "saa711x",
-       },
-       .id = I2C_DRIVERID_SAA711X,
-       .attach_adapter = saa711x_attach_adapter,
-       .detach_client = saa711x_detach_client,
-       .command = saa711x_command,
-};
-
-static int __init
-saa711x_init (void)
-{
-       return i2c_add_driver(&i2c_driver_saa711x);
-}
-
-static void __exit
-saa711x_exit (void)
-{
-       i2c_del_driver(&i2c_driver_saa711x);
-}
-
-module_init(saa711x_init);
-module_exit(saa711x_exit);
index 79d11a658bdfff44b12e9128a0d3e0f5624d6e1d..d0e83fe0ff5104024da62f8e948f65eae064c936 100644 (file)
@@ -666,7 +666,6 @@ static int saa7127_probe(struct i2c_client *client,
 {
        struct saa7127_state *state;
        struct v4l2_sliced_vbi_data vbi = { 0, 0, 0, 0 };  /* set to disabled */
-       int read_result = 0;
 
        /* Check if the adapter supports the needed features */
        if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
@@ -710,20 +709,29 @@ static int saa7127_probe(struct i2c_client *client,
                saa7127_set_input_type(client, SAA7127_INPUT_TYPE_NORMAL);
        saa7127_set_video_enable(client, 1);
 
-       /* Detect if it's an saa7129 */
-       read_result = saa7127_read(client, SAA7129_REG_FADE_KEY_COL2);
-       saa7127_write(client, SAA7129_REG_FADE_KEY_COL2, 0xaa);
-       if (saa7127_read(client, SAA7129_REG_FADE_KEY_COL2) == 0xaa) {
-               v4l_info(client, "saa7129 found @ 0x%x (%s)\n",
-                               client->addr << 1, client->adapter->name);
-               saa7127_write(client, SAA7129_REG_FADE_KEY_COL2, read_result);
-               saa7127_write_inittab(client, saa7129_init_config_extra);
-               state->ident = V4L2_IDENT_SAA7129;
-       } else {
-               v4l_info(client, "saa7127 found @ 0x%x (%s)\n",
-                               client->addr << 1, client->adapter->name);
-               state->ident = V4L2_IDENT_SAA7127;
+       if (id->driver_data) {  /* Chip type is already known */
+               state->ident = id->driver_data;
+       } else {                /* Needs detection */
+               int read_result;
+
+               /* Detect if it's an saa7129 */
+               read_result = saa7127_read(client, SAA7129_REG_FADE_KEY_COL2);
+               saa7127_write(client, SAA7129_REG_FADE_KEY_COL2, 0xaa);
+               if (saa7127_read(client, SAA7129_REG_FADE_KEY_COL2) == 0xaa) {
+                       saa7127_write(client, SAA7129_REG_FADE_KEY_COL2,
+                                       read_result);
+                       state->ident = V4L2_IDENT_SAA7129;
+                       strlcpy(client->name, "saa7129", I2C_NAME_SIZE);
+               } else {
+                       state->ident = V4L2_IDENT_SAA7127;
+                       strlcpy(client->name, "saa7127", I2C_NAME_SIZE);
+               }
        }
+
+       v4l_info(client, "%s found @ 0x%x (%s)\n", client->name,
+                       client->addr << 1, client->adapter->name);
+       if (state->ident == V4L2_IDENT_SAA7129)
+               saa7127_write_inittab(client, saa7129_init_config_extra);
        return 0;
 }
 
@@ -740,7 +748,11 @@ static int saa7127_remove(struct i2c_client *client)
 /* ----------------------------------------------------------------------- */
 
 static struct i2c_device_id saa7127_id[] = {
-       { "saa7127", 0 },
+       { "saa7127_auto", 0 },  /* auto-detection */
+       { "saa7126", V4L2_IDENT_SAA7127 },
+       { "saa7127", V4L2_IDENT_SAA7127 },
+       { "saa7128", V4L2_IDENT_SAA7129 },
+       { "saa7129", V4L2_IDENT_SAA7129 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, saa7127_id);
@@ -753,4 +765,3 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = {
        .remove = saa7127_remove,
        .id_table = saa7127_id,
 };
-
index 002e70a33a4f6bf2fe847c6223a1a6518f25955a..707be175509dbebcf00f1833ae7fc622e43c7c0d 100644 (file)
@@ -13,7 +13,6 @@
 #include <linux/init.h>
 #include <linux/crc32.h>
 
-
 #define MPEG_VIDEO_TARGET_BITRATE_MAX  27000
 #define MPEG_VIDEO_MAX_BITRATE_MAX     27000
 #define MPEG_TOTAL_TARGET_BITRATE_MAX  27000
@@ -21,6 +20,7 @@
 
 /* Addresses to scan */
 static unsigned short normal_i2c[] = {0x20, I2C_CLIENT_END};
+
 I2C_CLIENT_INSMOD;
 
 MODULE_DESCRIPTION("device driver for saa6752hs MPEG2 encoder");
@@ -448,6 +448,104 @@ static int handle_ctrl(struct saa6752hs_mpeg_params *params,
        return 0;
 }
 
+static int saa6752hs_qctrl(struct saa6752hs_mpeg_params *params,
+               struct v4l2_queryctrl *qctrl)
+{
+       int err;
+
+       switch (qctrl->id) {
+       case V4L2_CID_MPEG_AUDIO_ENCODING:
+               return v4l2_ctrl_query_fill(qctrl,
+                               V4L2_MPEG_AUDIO_ENCODING_LAYER_2,
+                               V4L2_MPEG_AUDIO_ENCODING_LAYER_2, 1,
+                               V4L2_MPEG_AUDIO_ENCODING_LAYER_2);
+
+       case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
+               return v4l2_ctrl_query_fill(qctrl,
+                               V4L2_MPEG_AUDIO_L2_BITRATE_256K,
+                               V4L2_MPEG_AUDIO_L2_BITRATE_384K, 1,
+                               V4L2_MPEG_AUDIO_L2_BITRATE_256K);
+
+       case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
+               return v4l2_ctrl_query_fill(qctrl,
+                               V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000,
+                               V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000, 1,
+                               V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000);
+
+       case V4L2_CID_MPEG_VIDEO_ENCODING:
+               return v4l2_ctrl_query_fill(qctrl,
+                               V4L2_MPEG_VIDEO_ENCODING_MPEG_2,
+                               V4L2_MPEG_VIDEO_ENCODING_MPEG_2, 1,
+                               V4L2_MPEG_VIDEO_ENCODING_MPEG_2);
+
+       case V4L2_CID_MPEG_VIDEO_ASPECT:
+               return v4l2_ctrl_query_fill(qctrl,
+                               V4L2_MPEG_VIDEO_ASPECT_4x3,
+                               V4L2_MPEG_VIDEO_ASPECT_16x9, 1,
+                               V4L2_MPEG_VIDEO_ASPECT_4x3);
+
+       case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
+               err = v4l2_ctrl_query_fill_std(qctrl);
+               if (err == 0 &&
+                   params->vi_bitrate_mode ==
+                               V4L2_MPEG_VIDEO_BITRATE_MODE_CBR)
+                       qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+               return err;
+
+       case V4L2_CID_MPEG_STREAM_TYPE:
+               return v4l2_ctrl_query_fill(qctrl,
+                               V4L2_MPEG_STREAM_TYPE_MPEG2_TS,
+                               V4L2_MPEG_STREAM_TYPE_MPEG2_TS, 1,
+                               V4L2_MPEG_STREAM_TYPE_MPEG2_TS);
+
+       case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+       case V4L2_CID_MPEG_VIDEO_BITRATE:
+       case V4L2_CID_MPEG_STREAM_PID_PMT:
+       case V4L2_CID_MPEG_STREAM_PID_AUDIO:
+       case V4L2_CID_MPEG_STREAM_PID_VIDEO:
+       case V4L2_CID_MPEG_STREAM_PID_PCR:
+               return v4l2_ctrl_query_fill_std(qctrl);
+
+       default:
+               break;
+       }
+       return -EINVAL;
+}
+
+static int saa6752hs_qmenu(struct saa6752hs_mpeg_params *params,
+               struct v4l2_querymenu *qmenu)
+{
+       static const char *mpeg_audio_l2_bitrate[] = {
+               "",
+               "",
+               "",
+               "",
+               "",
+               "",
+               "",
+               "",
+               "",
+               "",
+               "",
+               "256 kbps",
+               "",
+               "384 kbps",
+               NULL
+       };
+       struct v4l2_queryctrl qctrl;
+       int err;
+
+       qctrl.id = qmenu->id;
+       err = saa6752hs_qctrl(params, &qctrl);
+       if (err)
+               return err;
+       if (qmenu->id == V4L2_CID_MPEG_AUDIO_L2_BITRATE)
+               return v4l2_ctrl_query_menu(qmenu, &qctrl,
+                               mpeg_audio_l2_bitrate);
+       return v4l2_ctrl_query_menu(qmenu, &qctrl,
+                       v4l2_ctrl_get_menu(qmenu->id));
+}
+
 static int saa6752hs_init(struct i2c_client* client)
 {
        unsigned char buf[9], buf2[4];
@@ -609,7 +707,6 @@ static int saa6752hs_attach(struct i2c_adapter *adap, int addr, int kind)
        i2c_attach_client(&h->client);
 
        v4l_info(&h->client,"saa6752hs: chip found @ 0x%x\n", addr<<1);
-
        return 0;
 }
 
@@ -662,6 +759,10 @@ saa6752hs_command(struct i2c_client *client, unsigned int cmd, void *arg)
                }
                h->params = params;
                break;
+       case VIDIOC_QUERYCTRL:
+               return saa6752hs_qctrl(&h->params, arg);
+       case VIDIOC_QUERYMENU:
+               return saa6752hs_qmenu(&h->params, arg);
        case VIDIOC_G_FMT:
        {
           struct v4l2_format *f = arg;
index f118de6e36722950b43b5cb21d64520a32386f1f..9929d20320b485b7f5f9740360d599db799ca6ce 100644 (file)
@@ -80,7 +80,6 @@ typedef struct snd_card_saa7134 {
 } snd_card_saa7134_t;
 
 
-
 /*
  * PCM structure
  */
@@ -1121,6 +1120,3 @@ late_initcall(saa7134_alsa_init);
 module_exit(saa7134_alsa_exit);
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Ricardo Cerqueira");
-
-
-
index 2618cfa592e7067287407a403f9501213575b8d0..6893f998d292957313facf76d9fdb69ecd4bba0f 100644 (file)
@@ -1287,6 +1287,22 @@ struct saa7134_board saa7134_boards[] = {
                        .vmux = 8,
                }},
        },
+       [SAA7134_BOARD_AVERMEDIA_M103] = {
+               /* Massimo Piccioni <dafastidio@libero.it> */
+               .name           = "AVerMedia MiniPCI DVB-T Hybrid M103",
+               .audio_clock    = 0x187de7,
+               .tuner_type     = TUNER_XC2028,
+               .radio_type     = UNSET,
+               .tuner_addr     = ADDR_UNSET,
+               .radio_addr     = ADDR_UNSET,
+                .mpeg           = SAA7134_MPEG_DVB,
+                .inputs         = {{
+                        .name = name_tv,
+                        .vmux = 1,
+                        .amux = TV,
+                        .tv   = 1,
+                } },
+       },
        [SAA7134_BOARD_NOVAC_PRIMETV7133] = {
                /* toshii@netbsd.org */
                .name           = "Noval Prime TV 7133",
@@ -3503,6 +3519,39 @@ struct saa7134_board saa7134_boards[] = {
                        .amux = TV,
                        .gpio = 0x0200000,
                },
+       },
+       [SAA7134_BOARD_ASUSTeK_P7131_ANALOG] = {
+              .name           = "ASUSTeK P7131 Analog",
+              .audio_clock    = 0x00187de7,
+              .tuner_type     = TUNER_PHILIPS_TDA8290,
+              .radio_type     = UNSET,
+              .tuner_addr     = ADDR_UNSET,
+              .radio_addr     = ADDR_UNSET,
+              .gpiomask       = 1 << 21,
+              .inputs         = {{
+                      .name = name_tv,
+                      .vmux = 1,
+                      .amux = TV,
+                      .tv   = 1,
+                      .gpio = 0x0000000,
+              }, {
+                      .name = name_comp1,
+                      .vmux = 3,
+                      .amux = LINE2,
+              }, {
+                      .name = name_comp2,
+                      .vmux = 0,
+                      .amux = LINE2,
+              }, {
+                      .name = name_svideo,
+                      .vmux = 8,
+                      .amux = LINE2,
+              } },
+              .radio = {
+                      .name = name_radio,
+                      .amux = TV,
+                      .gpio = 0x0200000,
+              },
        },
        [SAA7134_BOARD_SABRENT_TV_PCB05] = {
                .name           = "Sabrent PCMCIA TV-PCB05",
@@ -3940,32 +3989,111 @@ struct saa7134_board saa7134_boards[] = {
        [SAA7134_BOARD_BEHOLD_M6] = {
                /* Igor Kuznetsov <igk@igk.ru> */
                /* Andrey Melnikoff <temnota@kmv.ru> */
-               .name           = "Beholder BeholdTV M6 / BeholdTV M6 Extra",
+               /* Beholder Intl. Ltd. Dmitry Belimov <d.belimov@gmail.com> */
+               .name           = "Beholder BeholdTV M6",
                .audio_clock    = 0x00187de7,
                .tuner_type     = TUNER_PHILIPS_FM1216ME_MK3,
                .radio_type     = UNSET,
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
                .tda9887_conf   = TDA9887_PRESENT,
-               .inputs         = {{
+               .inputs         = { {
                        .name = name_tv,
                        .vmux = 3,
                        .amux = TV,
                        .tv   = 1,
-               },{
+               }, {
                        .name = name_comp1,
                        .vmux = 1,
                        .amux = LINE1,
-               },{
+               }, {
                        .name = name_svideo,
                        .vmux = 8,
                        .amux = LINE1,
-               }},
+               } },
                .radio = {
                        .name = name_radio,
                        .amux = LINE2,
                },
                .mpeg  = SAA7134_MPEG_EMPRESS,
+               .video_out = CCIR656,
+               .vid_port_opts  = (SET_T_CODE_POLARITY_NON_INVERTED |
+                                       SET_CLOCK_NOT_DELAYED |
+                                       SET_CLOCK_INVERTED |
+                                       SET_VSYNC_OFF),
+       },
+       [SAA7134_BOARD_BEHOLD_M63] = {
+               /* Igor Kuznetsov <igk@igk.ru> */
+               /* Andrey Melnikoff <temnota@kmv.ru> */
+               /* Beholder Intl. Ltd. Dmitry Belimov <d.belimov@gmail.com> */
+               .name           = "Beholder BeholdTV M63",
+               .audio_clock    = 0x00187de7,
+               .tuner_type     = TUNER_PHILIPS_FM1216ME_MK3,
+               .radio_type     = UNSET,
+               .tuner_addr     = ADDR_UNSET,
+               .radio_addr     = ADDR_UNSET,
+               .tda9887_conf   = TDA9887_PRESENT,
+               .inputs         = { {
+                       .name = name_tv,
+                       .vmux = 3,
+                       .amux = TV,
+                       .tv   = 1,
+               }, {
+                       .name = name_comp1,
+                       .vmux = 1,
+                       .amux = LINE1,
+               }, {
+                       .name = name_svideo,
+                       .vmux = 8,
+                       .amux = LINE1,
+               } },
+               .radio = {
+                       .name = name_radio,
+                       .amux = LINE2,
+               },
+               .mpeg  = SAA7134_MPEG_EMPRESS,
+               .video_out = CCIR656,
+               .vid_port_opts  = (SET_T_CODE_POLARITY_NON_INVERTED |
+                                       SET_CLOCK_NOT_DELAYED |
+                                       SET_CLOCK_INVERTED |
+                                       SET_VSYNC_OFF),
+       },
+       [SAA7134_BOARD_BEHOLD_M6_EXTRA] = {
+               /* Igor Kuznetsov <igk@igk.ru> */
+               /* Andrey Melnikoff <temnota@kmv.ru> */
+               /* Beholder Intl. Ltd. Dmitry Belimov <d.belimov@gmail.com> */
+               .name           = "Beholder BeholdTV M6 Extra",
+               .audio_clock    = 0x00187de7,
+               /* FIXME: Must be PHILIPS_FM1216ME_MK5*/
+               .tuner_type     = TUNER_PHILIPS_FM1216ME_MK3,
+               .radio_type     = UNSET,
+               .tuner_addr     = ADDR_UNSET,
+               .radio_addr     = ADDR_UNSET,
+               .tda9887_conf   = TDA9887_PRESENT,
+               .inputs         = { {
+                       .name = name_tv,
+                       .vmux = 3,
+                       .amux = TV,
+                       .tv   = 1,
+               }, {
+                       .name = name_comp1,
+                       .vmux = 1,
+                       .amux = LINE1,
+               }, {
+                       .name = name_svideo,
+                       .vmux = 8,
+                       .amux = LINE1,
+               } },
+               .radio = {
+                       .name = name_radio,
+                       .amux = LINE2,
+               },
+               .mpeg  = SAA7134_MPEG_EMPRESS,
+               .video_out = CCIR656,
+               .vid_port_opts  = (SET_T_CODE_POLARITY_NON_INVERTED |
+                                       SET_CLOCK_NOT_DELAYED |
+                                       SET_CLOCK_INVERTED |
+                                       SET_VSYNC_OFF),
        },
        [SAA7134_BOARD_TWINHAN_DTV_DVB_3056] = {
                .name           = "Twinhan Hybrid DTV-DVB 3056 PCI",
@@ -4121,9 +4249,9 @@ struct saa7134_board saa7134_boards[] = {
                         .amux = TV,
                         .tv   = 1,
                 }, {
-                        .name = name_comp1,
-                        .vmux = 3,
-                        .amux = LINE2,
+                        .name = name_comp,
+                        .vmux = 0,
+                        .amux = LINE1,
                 }, {
                         .name = name_svideo,
                         .vmux = 8,
@@ -4141,6 +4269,7 @@ struct saa7134_board saa7134_boards[] = {
                .radio_type     = UNSET,
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
+               .mpeg           = SAA7134_MPEG_DVB,
                .inputs         = {{
                        .name = name_tv,
                        .vmux = 1,
@@ -4150,6 +4279,10 @@ struct saa7134_board saa7134_boards[] = {
                        .name = name_svideo,
                        .vmux = 8,
                        .amux = LINE1,
+               }, {
+                       .name = name_comp,
+                       .vmux = 0,
+                       .amux = LINE1,
                } },
                .radio = {
                        .name = name_radio,
@@ -4163,7 +4296,6 @@ struct saa7134_board saa7134_boards[] = {
                .radio_type     = UNSET,
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
-               .mpeg           = SAA7134_MPEG_DVB,
                .inputs         = {{
                        .name = name_tv,
                        .vmux = 1,
@@ -5226,13 +5358,13 @@ struct pci_device_id saa7134_pci_tbl[] = {
                .device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
                .subvendor    = 0x5ace,
                .subdevice    = 0x6193,
-               .driver_data  = SAA7134_BOARD_BEHOLD_M6,
+               .driver_data  = SAA7134_BOARD_BEHOLD_M6_EXTRA,
        }, {
                .vendor       = PCI_VENDOR_ID_PHILIPS,
                .device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
                .subvendor    = 0x5ace,
                .subdevice    = 0x6191,
-               .driver_data  = SAA7134_BOARD_BEHOLD_M6,
+               .driver_data  = SAA7134_BOARD_BEHOLD_M63,
        },{
                .vendor       = PCI_VENDOR_ID_PHILIPS,
                .device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
@@ -5281,12 +5413,24 @@ struct pci_device_id saa7134_pci_tbl[] = {
                .subvendor    = 0x185b,
                .subdevice    = 0xc900,
                .driver_data  = SAA7134_BOARD_VIDEOMATE_T750,
+       }, {
+               .vendor       = PCI_VENDOR_ID_PHILIPS,
+               .device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+               .subvendor    = 0x5169,
+               .subdevice    = 0x1502,
+               .driver_data  = SAA7134_BOARD_FLYTVPLATINUM_MINI,
        }, {
                .vendor       = PCI_VENDOR_ID_PHILIPS,
                .device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
                .subvendor    = 0x5ace,
                .subdevice    = 0x6290,
                .driver_data  = SAA7134_BOARD_BEHOLD_H6,
+       }, {
+               .vendor       = PCI_VENDOR_ID_PHILIPS,
+               .device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+               .subvendor    = 0x1461, /* Avermedia Technologies Inc */
+               .subdevice    = 0xf636,
+               .driver_data  = SAA7134_BOARD_AVERMEDIA_M103,
        }, {
                /* --- boards without eeprom + subsystem ID --- */
                .vendor       = PCI_VENDOR_ID_PHILIPS,
@@ -5352,6 +5496,7 @@ static int saa7134_xc2028_callback(struct saa7134_dev *dev,
                saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x00008000, 0x00008000);
                switch (dev->board) {
                case SAA7134_BOARD_AVERMEDIA_CARDBUS_506:
+               case SAA7134_BOARD_AVERMEDIA_M103:
                        saa7134_set_gpio(dev, 23, 0);
                        msleep(10);
                        saa7134_set_gpio(dev, 23, 1);
@@ -5493,6 +5638,7 @@ int saa7134_board_init1(struct saa7134_dev *dev)
        case SAA7134_BOARD_FLYDVBT_LR301:
        case SAA7134_BOARD_ASUSTeK_P7131_DUAL:
        case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA:
+       case SAA7134_BOARD_ASUSTeK_P7131_ANALOG:
        case SAA7134_BOARD_FLYDVBTDUO:
        case SAA7134_BOARD_PROTEUS_2309:
        case SAA7134_BOARD_AVERMEDIA_A16AR:
@@ -5560,6 +5706,7 @@ int saa7134_board_init1(struct saa7134_dev *dev)
                msleep(10);
                break;
        case SAA7134_BOARD_AVERMEDIA_CARDBUS_506:
+       case SAA7134_BOARD_AVERMEDIA_M103:
                saa7134_set_gpio(dev, 23, 0);
                msleep(10);
                saa7134_set_gpio(dev, 23, 1);
@@ -5601,6 +5748,8 @@ int saa7134_board_init1(struct saa7134_dev *dev)
        case SAA7134_BOARD_HAUPPAUGE_HVR1110:
        case SAA7134_BOARD_BEHOLD_607_9FM:
        case SAA7134_BOARD_BEHOLD_M6:
+       case SAA7134_BOARD_BEHOLD_M63:
+       case SAA7134_BOARD_BEHOLD_M6_EXTRA:
                dev->has_remote = SAA7134_REMOTE_I2C;
                break;
        case SAA7134_BOARD_AVERMEDIA_A169_B:
@@ -5683,6 +5832,7 @@ static void saa7134_tuner_setup(struct saa7134_dev *dev)
                switch (dev->board) {
                case SAA7134_BOARD_AVERMEDIA_A16D:
                case SAA7134_BOARD_AVERMEDIA_CARDBUS_506:
+               case SAA7134_BOARD_AVERMEDIA_M103:
                        ctl.demod = XC3028_FE_ZARLINK456;
                        break;
                default:
@@ -5825,6 +5975,15 @@ int saa7134_board_init2(struct saa7134_dev *dev)
                i2c_transfer(&dev->i2c_adap, &msg, 1);
                break;
        }
+       case SAA7134_BOARD_ASUSTeK_TVFM7135:
+       /* The card below is detected as card=53, but is different */
+              if (dev->autodetected && (dev->eedata[0x27] == 0x03)) {
+                      dev->board = SAA7134_BOARD_ASUSTeK_P7131_ANALOG;
+                      printk(KERN_INFO "%s: P7131 analog only, using "
+                                                      "entry of %s\n",
+                      dev->name, saa7134_boards[dev->board].name);
+              }
+              break;
        case SAA7134_BOARD_HAUPPAUGE_HVR1110:
                hauppauge_eeprom(dev, dev->eedata+0x80);
                /* break intentionally omitted */
index 2c19cd0113c8d2e1faa1b54493562b456a8ed3c4..cfee84ee7a88a3e5c0a187ddd96532afe700549d 100644 (file)
@@ -150,7 +150,6 @@ void saa7134_set_gpio(struct saa7134_dev *dev, int bit_no, int value)
 
 #if defined(CONFIG_MODULES) && defined(MODULE)
 
-
 static void request_module_async(struct work_struct *work){
        struct saa7134_dev* dev = container_of(work, struct saa7134_dev, request_module_wk);
        if (card_is_empress(dev))
index 341b101b0357d457003438ecaa210d16640dc580..be48b9b66a67ef9833daaeb3a4709b1dbddbb39d 100644 (file)
@@ -1263,6 +1263,7 @@ static int dvb_init(struct saa7134_dev *dev)
                                                &avermedia_xc3028_mt352_dev,
                                                &dev->i2c_adap);
                attach_xc3028 = 1;
+               break;
        case SAA7134_BOARD_MD7134_BRIDGE_2:
                dev->dvb.frontend = dvb_attach(tda10086_attach,
                                                &sd1878_4m, &dev->i2c_adap);
@@ -1290,6 +1291,15 @@ static int dvb_init(struct saa7134_dev *dev)
                        fe->ops.enable_high_lnb_voltage = md8800_set_high_voltage;
                }
                break;
+       case SAA7134_BOARD_AVERMEDIA_M103:
+               saa7134_set_gpio(dev, 25, 0);
+               msleep(10);
+               saa7134_set_gpio(dev, 25, 1);
+               dev->dvb.frontend = dvb_attach(mt352_attach,
+                                               &avermedia_xc3028_mt352_dev,
+                                               &dev->i2c_adap);
+               attach_xc3028 = 1;
+               break;
        default:
                wprintk("Huh? unknown DVB card?\n");
                break;
index 3ae71a3408223800ce442efc413c8589bb18ae4a..2a5ab957542dfacab6e3e73dfe77da44c2975280 100644 (file)
@@ -208,7 +208,7 @@ static int empress_s_input(struct file *file, void *priv, unsigned int i)
        return 0;
 }
 
-static int empress_enum_fmt_cap(struct file *file, void  *priv,
+static int empress_enum_fmt_vid_cap(struct file *file, void  *priv,
                                        struct v4l2_fmtdesc *f)
 {
        if (f->index != 0)
@@ -220,7 +220,7 @@ static int empress_enum_fmt_cap(struct file *file, void  *priv,
        return 0;
 }
 
-static int empress_g_fmt_cap(struct file *file, void *priv,
+static int empress_g_fmt_vid_cap(struct file *file, void *priv,
                                struct v4l2_format *f)
 {
        struct saa7134_dev *dev = file->private_data;
@@ -233,7 +233,7 @@ static int empress_g_fmt_cap(struct file *file, void *priv,
        return 0;
 }
 
-static int empress_s_fmt_cap(struct file *file, void *priv,
+static int empress_s_fmt_vid_cap(struct file *file, void *priv,
                                struct v4l2_format *f)
 {
        struct saa7134_dev *dev = file->private_data;
@@ -294,10 +294,20 @@ static int empress_streamoff(struct file *file, void *priv,
        return videobuf_streamoff(&dev->empress_tsq);
 }
 
+static int saa7134_i2c_call_saa6752(struct saa7134_dev *dev,
+                                             unsigned int cmd, void *arg)
+{
+       if (dev->mpeg_i2c_client == NULL)
+               return -EINVAL;
+       return dev->mpeg_i2c_client->driver->command(dev->mpeg_i2c_client,
+                                                               cmd, arg);
+}
+
 static int empress_s_ext_ctrls(struct file *file, void *priv,
                               struct v4l2_ext_controls *ctrls)
 {
        struct saa7134_dev *dev = file->private_data;
+       int err;
 
        /* count == 0 is abused in saa6752hs.c, so that special
                case is handled here explicitly. */
@@ -307,10 +317,10 @@ static int empress_s_ext_ctrls(struct file *file, void *priv,
        if (ctrls->ctrl_class != V4L2_CTRL_CLASS_MPEG)
                return -EINVAL;
 
-       saa7134_i2c_call_clients(dev, VIDIOC_S_EXT_CTRLS, ctrls);
+       err = saa7134_i2c_call_saa6752(dev, VIDIOC_S_EXT_CTRLS, ctrls);
        ts_init_encoder(dev);
 
-       return 0;
+       return err;
 }
 
 static int empress_g_ext_ctrls(struct file *file, void *priv,
@@ -320,9 +330,62 @@ static int empress_g_ext_ctrls(struct file *file, void *priv,
 
        if (ctrls->ctrl_class != V4L2_CTRL_CLASS_MPEG)
                return -EINVAL;
-       saa7134_i2c_call_clients(dev, VIDIOC_G_EXT_CTRLS, ctrls);
+       return saa7134_i2c_call_saa6752(dev, VIDIOC_G_EXT_CTRLS, ctrls);
+}
 
-       return 0;
+static int empress_queryctrl(struct file *file, void *priv,
+                                       struct v4l2_queryctrl *c)
+{
+       static const u32 user_ctrls[] = {
+               V4L2_CID_USER_CLASS,
+               V4L2_CID_BRIGHTNESS,
+               V4L2_CID_CONTRAST,
+               V4L2_CID_SATURATION,
+               V4L2_CID_HUE,
+               V4L2_CID_AUDIO_VOLUME,
+               V4L2_CID_AUDIO_MUTE,
+               V4L2_CID_HFLIP,
+               0
+       };
+
+       static const u32 mpeg_ctrls[] = {
+               V4L2_CID_MPEG_CLASS,
+               V4L2_CID_MPEG_STREAM_TYPE,
+               V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ,
+               V4L2_CID_MPEG_AUDIO_ENCODING,
+               V4L2_CID_MPEG_AUDIO_L2_BITRATE,
+               V4L2_CID_MPEG_VIDEO_ENCODING,
+               V4L2_CID_MPEG_VIDEO_ASPECT,
+               V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
+               V4L2_CID_MPEG_VIDEO_BITRATE,
+               V4L2_CID_MPEG_VIDEO_BITRATE_PEAK,
+               0
+       };
+       static const u32 *ctrl_classes[] = {
+               user_ctrls,
+               mpeg_ctrls,
+               NULL
+       };
+       struct saa7134_dev *dev = file->private_data;
+
+       c->id = v4l2_ctrl_next(ctrl_classes, c->id);
+       if (c->id == 0)
+               return -EINVAL;
+       if (c->id == V4L2_CID_USER_CLASS || c->id == V4L2_CID_MPEG_CLASS)
+               return v4l2_ctrl_query_fill_std(c);
+       if (V4L2_CTRL_ID2CLASS(c->id) != V4L2_CTRL_CLASS_MPEG)
+               return saa7134_queryctrl(file, priv, c);
+       return saa7134_i2c_call_saa6752(dev, VIDIOC_QUERYCTRL, c);
+}
+
+static int empress_querymenu(struct file *file, void *priv,
+                                       struct v4l2_querymenu *c)
+{
+       struct saa7134_dev *dev = file->private_data;
+
+       if (V4L2_CTRL_ID2CLASS(c->id) != V4L2_CTRL_CLASS_MPEG)
+               return -EINVAL;
+       return saa7134_i2c_call_saa6752(dev, VIDIOC_QUERYMENU, c);
 }
 
 static const struct file_operations ts_fops =
@@ -348,9 +411,9 @@ static struct video_device saa7134_empress_template =
        .minor         = -1,
 
        .vidioc_querycap                = empress_querycap,
-       .vidioc_enum_fmt_cap            = empress_enum_fmt_cap,
-       .vidioc_s_fmt_cap               = empress_s_fmt_cap,
-       .vidioc_g_fmt_cap               = empress_g_fmt_cap,
+       .vidioc_enum_fmt_vid_cap        = empress_enum_fmt_vid_cap,
+       .vidioc_s_fmt_vid_cap           = empress_s_fmt_vid_cap,
+       .vidioc_g_fmt_vid_cap           = empress_g_fmt_vid_cap,
        .vidioc_reqbufs                 = empress_reqbufs,
        .vidioc_querybuf                = empress_querybuf,
        .vidioc_qbuf                    = empress_qbuf,
@@ -363,7 +426,8 @@ static struct video_device saa7134_empress_template =
        .vidioc_g_input                 = empress_g_input,
        .vidioc_s_input                 = empress_s_input,
 
-       .vidioc_queryctrl               = saa7134_queryctrl,
+       .vidioc_queryctrl               = empress_queryctrl,
+       .vidioc_querymenu               = empress_querymenu,
        .vidioc_g_ctrl                  = saa7134_g_ctrl,
        .vidioc_s_ctrl                  = saa7134_s_ctrl,
 
index d8af3863f2d3de675492e34d8a84ec2269d1e5ff..5f713e6376839a3cac595821c736866bf1cbcc65 100644 (file)
@@ -327,6 +327,8 @@ static int attach_inform(struct i2c_client *client)
 
        d1printk( "%s i2c attach [addr=0x%x,client=%s]\n",
                client->driver->driver.name, client->addr, client->name);
+       if (client->addr == 0x20 && client->driver && client->driver->command)
+               dev->mpeg_i2c_client = client;
 
        /* Am I an i2c remote control? */
 
index 76e6501d238d0d31ddaf68305484de9faa9c6368..ad08d13dffdd8eaba60feb65d5d9bfd2c521888b 100644 (file)
@@ -198,6 +198,84 @@ static int get_key_beholdm6xx(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
        return 1;
 }
 
+/* Common (grey or coloured) pinnacle PCTV remote handling
+ *
+ */
+static int get_key_pinnacle(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw,
+                           int parity_offset, int marker, int code_modulo)
+{
+       unsigned char b[4];
+       unsigned int start = 0,parity = 0,code = 0;
+
+       /* poll IR chip */
+       if (4 != i2c_master_recv(&ir->c, b, 4)) {
+               i2cdprintk("read error\n");
+               return -EIO;
+       }
+
+       for (start = 0; start < ARRAY_SIZE(b); start++) {
+               if (b[start] == marker) {
+                       code=b[(start+parity_offset + 1) % 4];
+                       parity=b[(start+parity_offset) % 4];
+               }
+       }
+
+       /* Empty Request */
+       if (parity == 0)
+               return 0;
+
+       /* Repeating... */
+       if (ir->old == parity)
+               return 0;
+
+       ir->old = parity;
+
+       /* drop special codes when a key is held down a long time for the grey controller
+          In this case, the second bit of the code is asserted */
+       if (marker == 0xfe && (code & 0x40))
+               return 0;
+
+       code %= code_modulo;
+
+       *ir_raw = code;
+       *ir_key = code;
+
+       i2cdprintk("Pinnacle PCTV key %02x\n", code);
+
+       return 1;
+}
+
+/* The grey pinnacle PCTV remote
+ *
+ *  There are one issue with this remote:
+ *   - I2c packet does not change when the same key is pressed quickly. The workaround
+ *     is to hold down each key for about half a second, so that another code is generated
+ *     in the i2c packet, and the function can distinguish key presses.
+ *
+ * Sylvain Pasche <sylvain.pasche@gmail.com>
+ */
+static int get_key_pinnacle_grey(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
+{
+
+       return get_key_pinnacle(ir, ir_key, ir_raw, 1, 0xfe, 0xff);
+}
+
+
+/* The new pinnacle PCTV remote (with the colored buttons)
+ *
+ * Ricardo Cerqueira <v4l@cerqueira.org>
+ */
+static int get_key_pinnacle_color(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
+{
+       /* code_modulo parameter (0x88) is used to reduce code value to fit inside IR_KEYTAB_SIZE
+        *
+        * this is the only value that results in 42 unique
+        * codes < 128
+        */
+
+       return get_key_pinnacle(ir, ir_key, ir_raw, 2, 0x80, 0x88);
+}
+
 void saa7134_input_irq(struct saa7134_dev *dev)
 {
        struct card_ir *ir = dev->remote;
@@ -409,6 +487,7 @@ int saa7134_input_init1(struct saa7134_dev *dev)
                break;
        case SAA7134_BOARD_ASUSTeK_P7131_DUAL:
        case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA:
+       case SAA7134_BOARD_ASUSTeK_P7131_ANALOG:
                ir_codes     = ir_codes_asus_pc39;
                mask_keydown = 0x0040000;
                rc5_gpio = 1;
@@ -540,6 +619,8 @@ void saa7134_set_i2c_ir(struct saa7134_dev *dev, struct IR_i2c *ir)
                break;
        case SAA7134_BOARD_BEHOLD_607_9FM:
        case SAA7134_BOARD_BEHOLD_M6:
+       case SAA7134_BOARD_BEHOLD_M63:
+       case SAA7134_BOARD_BEHOLD_M6_EXTRA:
        case SAA7134_BOARD_BEHOLD_H6:
                snprintf(ir->c.name, sizeof(ir->c.name), "BeholdTV");
                ir->get_key   = get_key_beholdm6xx;
index 86f5eefdb0f6d94f5f245e4ded082688e91f975c..cf89d96d7295221c77189d8e24799f7555a4d232 100644 (file)
 #define SAA7135_DSP_RWCLEAR                    0x586
 #define SAA7135_DSP_RWCLEAR_RERR                   1
 
+#define SAA7133_I2S_AUDIO_CONTROL               0x591
 /* ------------------------------------------------------------------ */
 /*
  * Local variables:
index 232af598d94709a9d66ef0bd898f13efca3cffeb..c5d0b44c179e02e8b8f66b4cafbb0346771300f3 100644 (file)
@@ -477,7 +477,6 @@ static int tvaudio_thread(void *data)
        unsigned int i, audio, nscan;
        int max1,max2,carrier,rx,mode,lastmode,default_carrier;
 
-
        set_freezable();
 
        for (;;) {
@@ -775,7 +774,6 @@ static int tvaudio_thread_ddep(void *data)
        struct saa7134_dev *dev = data;
        u32 value, norms;
 
-
        set_freezable();
        for (;;) {
                tvaudio_sleep(dev,-1);
@@ -873,13 +871,34 @@ void saa7134_enable_i2s(struct saa7134_dev *dev)
 
        if (!card_is_empress(dev))
                return;
-       i2s_format = (dev->input->amux == TV) ? 0x00 : 0x01;
 
-       /* enable I2S audio output for the mpeg encoder */
-       saa_writeb(SAA7134_I2S_OUTPUT_SELECT,  0x80);
-       saa_writeb(SAA7134_I2S_OUTPUT_FORMAT,  i2s_format);
-       saa_writeb(SAA7134_I2S_OUTPUT_LEVEL,   0x0F);
-       saa_writeb(SAA7134_I2S_AUDIO_OUTPUT,   0x01);
+       if (dev->pci->device == PCI_DEVICE_ID_PHILIPS_SAA7130)
+               return;
+
+       /* configure GPIO for out */
+       saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x0E000000, 0x00000000);
+
+       switch (dev->pci->device) {
+       case PCI_DEVICE_ID_PHILIPS_SAA7133:
+       case PCI_DEVICE_ID_PHILIPS_SAA7135:
+           /* Set I2S format (SONY) Â */
+           saa_writeb(SAA7133_I2S_AUDIO_CONTROL, 0x00);
+           /* Start I2S */
+           saa_writeb(SAA7134_I2S_AUDIO_OUTPUT, 0x11);
+           break;
+
+       case PCI_DEVICE_ID_PHILIPS_SAA7134:
+           i2s_format = (dev->input->amux == TV) ? 0x00 : 0x01;
+
+           /* enable I2S audio output for the mpeg encoder */
+           saa_writeb(SAA7134_I2S_OUTPUT_SELECT, 0x80);
+           saa_writeb(SAA7134_I2S_OUTPUT_FORMAT, i2s_format);
+           saa_writeb(SAA7134_I2S_OUTPUT_LEVEL,  0x0F);
+           saa_writeb(SAA7134_I2S_AUDIO_OUTPUT,  0x01);
+
+       default:
+           break;
+       }
 }
 
 int saa7134_tvaudio_rx2mode(u32 rx)
index 48e1a01718ec37390f72fa8ed731bfaa0d16bc78..1a5137550e7a8bafe217c91d637fba3fa9ca2b55 100644 (file)
@@ -1496,7 +1496,7 @@ static int video_mmap(struct file *file, struct vm_area_struct * vma)
 
 /* ------------------------------------------------------------------ */
 
-static int saa7134_try_get_set_fmt_vbi(struct file *file, void *priv,
+static int saa7134_try_get_set_fmt_vbi_cap(struct file *file, void *priv,
                                                struct v4l2_format *f)
 {
        struct saa7134_fh *fh = priv;
@@ -1516,7 +1516,7 @@ static int saa7134_try_get_set_fmt_vbi(struct file *file, void *priv,
        return 0;
 }
 
-static int saa7134_g_fmt_cap(struct file *file, void *priv,
+static int saa7134_g_fmt_vid_cap(struct file *file, void *priv,
                                struct v4l2_format *f)
 {
        struct saa7134_fh *fh = priv;
@@ -1532,7 +1532,7 @@ static int saa7134_g_fmt_cap(struct file *file, void *priv,
        return 0;
 }
 
-static int saa7134_g_fmt_overlay(struct file *file, void *priv,
+static int saa7134_g_fmt_vid_overlay(struct file *file, void *priv,
                                struct v4l2_format *f)
 {
        struct saa7134_fh *fh = priv;
@@ -1546,7 +1546,7 @@ static int saa7134_g_fmt_overlay(struct file *file, void *priv,
        return 0;
 }
 
-static int saa7134_try_fmt_cap(struct file *file, void *priv,
+static int saa7134_try_fmt_vid_cap(struct file *file, void *priv,
                                                struct v4l2_format *f)
 {
        struct saa7134_fh *fh = priv;
@@ -1597,7 +1597,7 @@ static int saa7134_try_fmt_cap(struct file *file, void *priv,
        return 0;
 }
 
-static int saa7134_try_fmt_overlay(struct file *file, void *priv,
+static int saa7134_try_fmt_vid_overlay(struct file *file, void *priv,
                                                struct v4l2_format *f)
 {
        struct saa7134_fh *fh = priv;
@@ -1611,13 +1611,13 @@ static int saa7134_try_fmt_overlay(struct file *file, void *priv,
        return verify_preview(dev, &f->fmt.win);
 }
 
-static int saa7134_s_fmt_cap(struct file *file, void *priv,
+static int saa7134_s_fmt_vid_cap(struct file *file, void *priv,
                                        struct v4l2_format *f)
 {
        struct saa7134_fh *fh = priv;
        int err;
 
-       err = saa7134_try_fmt_cap(file, priv, f);
+       err = saa7134_try_fmt_vid_cap(file, priv, f);
        if (0 != err)
                return err;
 
@@ -1628,7 +1628,7 @@ static int saa7134_s_fmt_cap(struct file *file, void *priv,
        return 0;
 }
 
-static int saa7134_s_fmt_overlay(struct file *file, void *priv,
+static int saa7134_s_fmt_vid_overlay(struct file *file, void *priv,
                                        struct v4l2_format *f)
 {
        struct saa7134_fh *fh = priv;
@@ -2028,7 +2028,7 @@ static int saa7134_s_priority(struct file *file, void *f,
        return v4l2_prio_change(&dev->prio, &fh->prio, prio);
 }
 
-static int saa7134_enum_fmt_cap(struct file *file, void  *priv,
+static int saa7134_enum_fmt_vid_cap(struct file *file, void  *priv,
                                        struct v4l2_fmtdesc *f)
 {
        if (f->index >= FORMATS)
@@ -2042,7 +2042,7 @@ static int saa7134_enum_fmt_cap(struct file *file, void  *priv,
        return 0;
 }
 
-static int saa7134_enum_fmt_overlay(struct file *file, void  *priv,
+static int saa7134_enum_fmt_vid_overlay(struct file *file, void  *priv,
                                        struct v4l2_fmtdesc *f)
 {
        if (saa7134_no_overlay > 0) {
@@ -2061,7 +2061,7 @@ static int saa7134_enum_fmt_overlay(struct file *file, void  *priv,
        return 0;
 }
 
-static int saa7134_enum_fmt_vbi(struct file *file, void  *priv,
+static int saa7134_enum_fmt_vbi_cap(struct file *file, void  *priv,
                                        struct v4l2_fmtdesc *f)
 {
        if (0 != f->index)
@@ -2208,6 +2208,32 @@ static int saa7134_g_parm(struct file *file, void *fh,
        return 0;
 }
 
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int vidioc_g_register (struct file *file, void *priv,
+                             struct v4l2_register *reg)
+{
+       struct saa7134_fh *fh = priv;
+       struct saa7134_dev *dev = fh->dev;
+
+       if (!v4l2_chip_match_host(reg->match_type, reg->match_chip))
+               return -EINVAL;
+       reg->val = saa_readb(reg->reg);
+       return 0;
+}
+
+static int vidioc_s_register (struct file *file, void *priv,
+                               struct v4l2_register *reg)
+{
+       struct saa7134_fh *fh = priv;
+       struct saa7134_dev *dev = fh->dev;
+
+       if (!v4l2_chip_match_host(reg->match_type, reg->match_chip))
+               return -EINVAL;
+       saa_writeb(reg->reg&0xffffff, reg->val);
+       return 0;
+}
+#endif
+
 static int radio_querycap(struct file *file, void *priv,
                                        struct v4l2_capability *cap)
 {
@@ -2348,18 +2374,18 @@ struct video_device saa7134_video_template =
        .fops                           = &video_fops,
        .minor                          = -1,
        .vidioc_querycap                = saa7134_querycap,
-       .vidioc_enum_fmt_cap            = saa7134_enum_fmt_cap,
-       .vidioc_g_fmt_cap               = saa7134_g_fmt_cap,
-       .vidioc_try_fmt_cap             = saa7134_try_fmt_cap,
-       .vidioc_s_fmt_cap               = saa7134_s_fmt_cap,
-       .vidioc_enum_fmt_overlay        = saa7134_enum_fmt_overlay,
-       .vidioc_g_fmt_overlay           = saa7134_g_fmt_overlay,
-       .vidioc_try_fmt_overlay         = saa7134_try_fmt_overlay,
-       .vidioc_s_fmt_overlay           = saa7134_s_fmt_overlay,
-       .vidioc_enum_fmt_vbi            = saa7134_enum_fmt_vbi,
-       .vidioc_g_fmt_vbi               = saa7134_try_get_set_fmt_vbi,
-       .vidioc_try_fmt_vbi             = saa7134_try_get_set_fmt_vbi,
-       .vidioc_s_fmt_vbi               = saa7134_try_get_set_fmt_vbi,
+       .vidioc_enum_fmt_vid_cap        = saa7134_enum_fmt_vid_cap,
+       .vidioc_g_fmt_vid_cap           = saa7134_g_fmt_vid_cap,
+       .vidioc_try_fmt_vid_cap         = saa7134_try_fmt_vid_cap,
+       .vidioc_s_fmt_vid_cap           = saa7134_s_fmt_vid_cap,
+       .vidioc_enum_fmt_vid_overlay    = saa7134_enum_fmt_vid_overlay,
+       .vidioc_g_fmt_vid_overlay       = saa7134_g_fmt_vid_overlay,
+       .vidioc_try_fmt_vid_overlay     = saa7134_try_fmt_vid_overlay,
+       .vidioc_s_fmt_vid_overlay       = saa7134_s_fmt_vid_overlay,
+       .vidioc_enum_fmt_vbi_cap        = saa7134_enum_fmt_vbi_cap,
+       .vidioc_g_fmt_vbi_cap           = saa7134_try_get_set_fmt_vbi_cap,
+       .vidioc_try_fmt_vbi_cap         = saa7134_try_get_set_fmt_vbi_cap,
+       .vidioc_s_fmt_vbi_cap           = saa7134_try_get_set_fmt_vbi_cap,
        .vidioc_g_audio                 = saa7134_g_audio,
        .vidioc_s_audio                 = saa7134_s_audio,
        .vidioc_cropcap                 = saa7134_cropcap,
@@ -2391,6 +2417,10 @@ struct video_device saa7134_video_template =
        .vidioc_g_parm                  = saa7134_g_parm,
        .vidioc_g_frequency             = saa7134_g_frequency,
        .vidioc_s_frequency             = saa7134_s_frequency,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+       .vidioc_g_register              = vidioc_g_register,
+       .vidioc_s_register              = vidioc_s_register,
+#endif
        .tvnorms                        = SAA7134_NORMS,
        .current_norm                   = V4L2_STD_PAL,
 };
@@ -2458,13 +2488,14 @@ int saa7134_videoport_init(struct saa7134_dev *dev)
        int vo = saa7134_boards[dev->board].video_out;
        int video_reg;
        unsigned int vid_port_opts = saa7134_boards[dev->board].vid_port_opts;
+
+       /* Configure videoport */
        saa_writeb(SAA7134_VIDEO_PORT_CTRL0, video_out[vo][0]);
        video_reg = video_out[vo][1];
        if (vid_port_opts & SET_T_CODE_POLARITY_NON_INVERTED)
                video_reg &= ~VP_T_CODE_P_INVERTED;
        saa_writeb(SAA7134_VIDEO_PORT_CTRL1, video_reg);
        saa_writeb(SAA7134_VIDEO_PORT_CTRL2, video_out[vo][2]);
-       saa_writeb(SAA7134_VIDEO_PORT_CTRL3, video_out[vo][3]);
        saa_writeb(SAA7134_VIDEO_PORT_CTRL4, video_out[vo][4]);
        video_reg = video_out[vo][5];
        if (vid_port_opts & SET_CLOCK_NOT_DELAYED)
@@ -2481,6 +2512,9 @@ int saa7134_videoport_init(struct saa7134_dev *dev)
        saa_writeb(SAA7134_VIDEO_PORT_CTRL7, video_out[vo][7]);
        saa_writeb(SAA7134_VIDEO_PORT_CTRL8, video_out[vo][8]);
 
+       /* Start videoport */
+       saa_writeb(SAA7134_VIDEO_PORT_CTRL3, video_out[vo][3]);
+
        return 0;
 }
 
index 34ff0d4998f382ea53e68cae6e4c22a787b76b1a..6927cbea86245b6e49cd268af82fc04da600ff76 100644 (file)
@@ -264,7 +264,10 @@ struct saa7134_format {
 #define SAA7134_BOARD_AVERMEDIA_A700_PRO    140
 #define SAA7134_BOARD_AVERMEDIA_A700_HYBRID 141
 #define SAA7134_BOARD_BEHOLD_H6      142
-
+#define SAA7134_BOARD_BEHOLD_M63      143
+#define SAA7134_BOARD_BEHOLD_M6_EXTRA    144
+#define SAA7134_BOARD_AVERMEDIA_M103    145
+#define SAA7134_BOARD_ASUSTeK_P7131_ANALOG 146
 
 #define SAA7134_MAXBOARDS 8
 #define SAA7134_INPUT_MAX 8
@@ -552,6 +555,7 @@ struct saa7134_dev {
        struct saa7134_ts          ts;
        struct saa7134_dmaqueue    ts_q;
        struct saa7134_mpeg_ops    *mops;
+       struct i2c_client          *mpeg_i2c_client;
 
        /* SAA7134_MPEG_EMPRESS only */
        struct video_device        *empress_dev;
diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c
new file mode 100644 (file)
index 0000000..012005e
--- /dev/null
@@ -0,0 +1,657 @@
+/*
+ * V4L2 Driver for SuperH Mobile CEU interface
+ *
+ * Copyright (C) 2008 Magnus Damm
+ *
+ * Based on V4L2 Driver for PXA camera host - "pxa_camera.c",
+ *
+ * Copyright (C) 2006, Sascha Hauer, Pengutronix
+ * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.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.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/moduleparam.h>
+#include <linux/time.h>
+#include <linux/version.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-dev.h>
+#include <media/soc_camera.h>
+#include <media/sh_mobile_ceu.h>
+#include <media/videobuf-dma-contig.h>
+
+/* register offsets for sh7722 / sh7723 */
+
+#define CAPSR  0x00
+#define CAPCR  0x04
+#define CAMCR  0x08
+#define CMCYR  0x0c
+#define CAMOR  0x10
+#define CAPWR  0x14
+#define CAIFR  0x18
+#define CSTCR  0x20 /* not on sh7723 */
+#define CSECR  0x24 /* not on sh7723 */
+#define CRCNTR 0x28
+#define CRCMPR 0x2c
+#define CFLCR  0x30
+#define CFSZR  0x34
+#define CDWDR  0x38
+#define CDAYR  0x3c
+#define CDACR  0x40
+#define CDBYR  0x44
+#define CDBCR  0x48
+#define CBDSR  0x4c
+#define CFWCR  0x5c
+#define CLFCR  0x60
+#define CDOCR  0x64
+#define CDDCR  0x68
+#define CDDAR  0x6c
+#define CEIER  0x70
+#define CETCR  0x74
+#define CSTSR  0x7c
+#define CSRTR  0x80
+#define CDSSR  0x84
+#define CDAYR2 0x90
+#define CDACR2 0x94
+#define CDBYR2 0x98
+#define CDBCR2 0x9c
+
+static DEFINE_MUTEX(camera_lock);
+
+/* per video frame buffer */
+struct sh_mobile_ceu_buffer {
+       struct videobuf_buffer vb; /* v4l buffer must be first */
+       const struct soc_camera_data_format *fmt;
+};
+
+struct sh_mobile_ceu_dev {
+       struct device *dev;
+       struct soc_camera_host ici;
+       struct soc_camera_device *icd;
+
+       unsigned int irq;
+       void __iomem *base;
+       unsigned long video_limit;
+
+       spinlock_t lock;
+       struct list_head capture;
+       struct videobuf_buffer *active;
+
+       struct sh_mobile_ceu_info *pdata;
+};
+
+static void ceu_write(struct sh_mobile_ceu_dev *priv,
+                     unsigned long reg_offs, unsigned long data)
+{
+       iowrite32(data, priv->base + reg_offs);
+}
+
+static unsigned long ceu_read(struct sh_mobile_ceu_dev *priv,
+                             unsigned long reg_offs)
+{
+       return ioread32(priv->base + reg_offs);
+}
+
+/*
+ *  Videobuf operations
+ */
+static int sh_mobile_ceu_videobuf_setup(struct videobuf_queue *vq,
+                                       unsigned int *count,
+                                       unsigned int *size)
+{
+       struct soc_camera_device *icd = vq->priv_data;
+       struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+       struct sh_mobile_ceu_dev *pcdev = ici->priv;
+       int bytes_per_pixel = (icd->current_fmt->depth + 7) >> 3;
+
+       *size = PAGE_ALIGN(icd->width * icd->height * bytes_per_pixel);
+
+       if (0 == *count)
+               *count = 2;
+
+       if (pcdev->video_limit) {
+               while (*size * *count > pcdev->video_limit)
+                       (*count)--;
+       }
+
+       dev_dbg(&icd->dev, "count=%d, size=%d\n", *count, *size);
+
+       return 0;
+}
+
+static void free_buffer(struct videobuf_queue *vq,
+                       struct sh_mobile_ceu_buffer *buf)
+{
+       struct soc_camera_device *icd = vq->priv_data;
+
+       dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %zd\n", __func__,
+               &buf->vb, buf->vb.baddr, buf->vb.bsize);
+
+       if (in_interrupt())
+               BUG();
+
+       videobuf_dma_contig_free(vq, &buf->vb);
+       dev_dbg(&icd->dev, "%s freed\n", __func__);
+       buf->vb.state = VIDEOBUF_NEEDS_INIT;
+}
+
+static void sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev)
+{
+       ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) & ~1);
+       ceu_write(pcdev, CETCR, ~ceu_read(pcdev, CETCR) & 0x0317f313);
+       ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) | 1);
+
+       ceu_write(pcdev, CAPCR, ceu_read(pcdev, CAPCR) & ~0x10000);
+
+       ceu_write(pcdev, CETCR, 0x0317f313 ^ 0x10);
+
+       if (pcdev->active) {
+               ceu_write(pcdev, CDAYR, videobuf_to_dma_contig(pcdev->active));
+               ceu_write(pcdev, CAPSR, 0x1); /* start capture */
+       }
+}
+
+static int sh_mobile_ceu_videobuf_prepare(struct videobuf_queue *vq,
+                                         struct videobuf_buffer *vb,
+                                         enum v4l2_field field)
+{
+       struct soc_camera_device *icd = vq->priv_data;
+       struct sh_mobile_ceu_buffer *buf;
+       int ret;
+
+       buf = container_of(vb, struct sh_mobile_ceu_buffer, vb);
+
+       dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %zd\n", __func__,
+               vb, vb->baddr, vb->bsize);
+
+       /* Added list head initialization on alloc */
+       WARN_ON(!list_empty(&vb->queue));
+
+#ifdef DEBUG
+       /* This can be useful if you want to see if we actually fill
+        * the buffer with something */
+       memset((void *)vb->baddr, 0xaa, vb->bsize);
+#endif
+
+       BUG_ON(NULL == icd->current_fmt);
+
+       if (buf->fmt    != icd->current_fmt ||
+           vb->width   != icd->width ||
+           vb->height  != icd->height ||
+           vb->field   != field) {
+               buf->fmt        = icd->current_fmt;
+               vb->width       = icd->width;
+               vb->height      = icd->height;
+               vb->field       = field;
+               vb->state       = VIDEOBUF_NEEDS_INIT;
+       }
+
+       vb->size = vb->width * vb->height * ((buf->fmt->depth + 7) >> 3);
+       if (0 != vb->baddr && vb->bsize < vb->size) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       if (vb->state == VIDEOBUF_NEEDS_INIT) {
+               ret = videobuf_iolock(vq, vb, NULL);
+               if (ret)
+                       goto fail;
+               vb->state = VIDEOBUF_PREPARED;
+       }
+
+       return 0;
+fail:
+       free_buffer(vq, buf);
+out:
+       return ret;
+}
+
+static void sh_mobile_ceu_videobuf_queue(struct videobuf_queue *vq,
+                                        struct videobuf_buffer *vb)
+{
+       struct soc_camera_device *icd = vq->priv_data;
+       struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+       struct sh_mobile_ceu_dev *pcdev = ici->priv;
+       unsigned long flags;
+
+       dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %zd\n", __func__,
+               vb, vb->baddr, vb->bsize);
+
+       vb->state = VIDEOBUF_ACTIVE;
+       spin_lock_irqsave(&pcdev->lock, flags);
+       list_add_tail(&vb->queue, &pcdev->capture);
+
+       if (!pcdev->active) {
+               pcdev->active = vb;
+               sh_mobile_ceu_capture(pcdev);
+       }
+
+       spin_unlock_irqrestore(&pcdev->lock, flags);
+}
+
+static void sh_mobile_ceu_videobuf_release(struct videobuf_queue *vq,
+                                          struct videobuf_buffer *vb)
+{
+       free_buffer(vq, container_of(vb, struct sh_mobile_ceu_buffer, vb));
+}
+
+static struct videobuf_queue_ops sh_mobile_ceu_videobuf_ops = {
+       .buf_setup      = sh_mobile_ceu_videobuf_setup,
+       .buf_prepare    = sh_mobile_ceu_videobuf_prepare,
+       .buf_queue      = sh_mobile_ceu_videobuf_queue,
+       .buf_release    = sh_mobile_ceu_videobuf_release,
+};
+
+static irqreturn_t sh_mobile_ceu_irq(int irq, void *data)
+{
+       struct sh_mobile_ceu_dev *pcdev = data;
+       struct videobuf_buffer *vb;
+       unsigned long flags;
+
+       spin_lock_irqsave(&pcdev->lock, flags);
+
+       vb = pcdev->active;
+       list_del_init(&vb->queue);
+
+       if (!list_empty(&pcdev->capture))
+               pcdev->active = list_entry(pcdev->capture.next,
+                                          struct videobuf_buffer, queue);
+       else
+               pcdev->active = NULL;
+
+       sh_mobile_ceu_capture(pcdev);
+
+       vb->state = VIDEOBUF_DONE;
+       do_gettimeofday(&vb->ts);
+       vb->field_count++;
+       wake_up(&vb->done);
+       spin_unlock_irqrestore(&pcdev->lock, flags);
+
+       return IRQ_HANDLED;
+}
+
+static int sh_mobile_ceu_add_device(struct soc_camera_device *icd)
+{
+       struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+       struct sh_mobile_ceu_dev *pcdev = ici->priv;
+       int ret = -EBUSY;
+
+       mutex_lock(&camera_lock);
+
+       if (pcdev->icd)
+               goto err;
+
+       dev_info(&icd->dev,
+                "SuperH Mobile CEU driver attached to camera %d\n",
+                icd->devnum);
+
+       if (pcdev->pdata->enable_camera)
+               pcdev->pdata->enable_camera();
+
+       ret = icd->ops->init(icd);
+       if (ret)
+               goto err;
+
+       ceu_write(pcdev, CAPSR, 1 << 16); /* reset */
+       while (ceu_read(pcdev, CSTSR) & 1)
+               msleep(1);
+
+       pcdev->icd = icd;
+err:
+       mutex_unlock(&camera_lock);
+
+       return ret;
+}
+
+static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd)
+{
+       struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+       struct sh_mobile_ceu_dev *pcdev = ici->priv;
+
+       BUG_ON(icd != pcdev->icd);
+
+       /* disable capture, disable interrupts */
+       ceu_write(pcdev, CEIER, 0);
+       ceu_write(pcdev, CAPSR, 1 << 16); /* reset */
+       icd->ops->release(icd);
+       if (pcdev->pdata->disable_camera)
+               pcdev->pdata->disable_camera();
+
+       dev_info(&icd->dev,
+                "SuperH Mobile CEU driver detached from camera %d\n",
+                icd->devnum);
+
+       pcdev->icd = NULL;
+}
+
+static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd,
+                                      __u32 pixfmt)
+{
+       struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+       struct sh_mobile_ceu_dev *pcdev = ici->priv;
+       int ret, buswidth, width, cfszr_width, cdwdr_width;
+       unsigned long camera_flags, common_flags, value;
+
+       camera_flags = icd->ops->query_bus_param(icd);
+       common_flags = soc_camera_bus_param_compatible(camera_flags,
+                                                      pcdev->pdata->flags);
+       if (!common_flags)
+               return -EINVAL;
+
+       ret = icd->ops->set_bus_param(icd, common_flags);
+       if (ret < 0)
+               return ret;
+
+       switch (common_flags & SOCAM_DATAWIDTH_MASK) {
+       case SOCAM_DATAWIDTH_8:
+               buswidth = 8;
+               break;
+       case SOCAM_DATAWIDTH_16:
+               buswidth = 16;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       ceu_write(pcdev, CRCNTR, 0);
+       ceu_write(pcdev, CRCMPR, 0);
+
+       value = 0x00000010;
+       value |= (common_flags & SOCAM_VSYNC_ACTIVE_LOW) ? (1 << 1) : 0;
+       value |= (common_flags & SOCAM_HSYNC_ACTIVE_LOW) ? (1 << 0) : 0;
+       value |= (buswidth == 16) ? (1 << 12) : 0;
+       ceu_write(pcdev, CAMCR, value);
+
+       ceu_write(pcdev, CAPCR, 0x00300000);
+       ceu_write(pcdev, CAIFR, 0);
+
+       mdelay(1);
+
+       width = icd->width * (icd->current_fmt->depth / 8);
+       width = (buswidth == 16) ? width / 2 : width;
+       cfszr_width = (buswidth == 8) ? width / 2 : width;
+       cdwdr_width = (buswidth == 16) ? width * 2 : width;
+
+       ceu_write(pcdev, CAMOR, 0);
+       ceu_write(pcdev, CAPWR, (icd->height << 16) | width);
+       ceu_write(pcdev, CFLCR, 0); /* data fetch mode - no scaling */
+       ceu_write(pcdev, CFSZR, (icd->height << 16) | cfszr_width);
+       ceu_write(pcdev, CLFCR, 0); /* data fetch mode - no lowpass filter */
+       ceu_write(pcdev, CDOCR, 0x00000016);
+
+       ceu_write(pcdev, CDWDR, cdwdr_width);
+       ceu_write(pcdev, CFWCR, 0); /* keep "datafetch firewall" disabled */
+
+       /* not in bundle mode: skip CBDSR, CDAYR2, CDACR2, CDBYR2, CDBCR2 */
+       /* in data fetch mode: no need for CDACR, CDBYR, CDBCR */
+
+       return 0;
+}
+
+static int sh_mobile_ceu_try_bus_param(struct soc_camera_device *icd,
+                                      __u32 pixfmt)
+{
+       struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+       struct sh_mobile_ceu_dev *pcdev = ici->priv;
+       unsigned long camera_flags, common_flags;
+
+       camera_flags = icd->ops->query_bus_param(icd);
+       common_flags = soc_camera_bus_param_compatible(camera_flags,
+                                                      pcdev->pdata->flags);
+       if (!common_flags)
+               return -EINVAL;
+
+       return 0;
+}
+
+static int sh_mobile_ceu_set_fmt_cap(struct soc_camera_device *icd,
+                                    __u32 pixfmt, struct v4l2_rect *rect)
+{
+       return icd->ops->set_fmt_cap(icd, pixfmt, rect);
+}
+
+static int sh_mobile_ceu_try_fmt_cap(struct soc_camera_device *icd,
+                                    struct v4l2_format *f)
+{
+       /* FIXME: calculate using depth and bus width */
+
+       if (f->fmt.pix.height < 4)
+               f->fmt.pix.height = 4;
+       if (f->fmt.pix.height > 1920)
+               f->fmt.pix.height = 1920;
+       if (f->fmt.pix.width < 2)
+               f->fmt.pix.width = 2;
+       if (f->fmt.pix.width > 2560)
+               f->fmt.pix.width = 2560;
+       f->fmt.pix.width &= ~0x01;
+       f->fmt.pix.height &= ~0x03;
+
+       /* limit to sensor capabilities */
+       return icd->ops->try_fmt_cap(icd, f);
+}
+
+static int sh_mobile_ceu_reqbufs(struct soc_camera_file *icf,
+                                struct v4l2_requestbuffers *p)
+{
+       int i;
+
+       /* This is for locking debugging only. I removed spinlocks and now I
+        * check whether .prepare is ever called on a linked buffer, or whether
+        * a dma IRQ can occur for an in-work or unlinked buffer. Until now
+        * it hadn't triggered */
+       for (i = 0; i < p->count; i++) {
+               struct sh_mobile_ceu_buffer *buf;
+
+               buf = container_of(icf->vb_vidq.bufs[i],
+                                  struct sh_mobile_ceu_buffer, vb);
+               INIT_LIST_HEAD(&buf->vb.queue);
+       }
+
+       return 0;
+}
+
+static unsigned int sh_mobile_ceu_poll(struct file *file, poll_table *pt)
+{
+       struct soc_camera_file *icf = file->private_data;
+       struct sh_mobile_ceu_buffer *buf;
+
+       buf = list_entry(icf->vb_vidq.stream.next,
+                        struct sh_mobile_ceu_buffer, vb.stream);
+
+       poll_wait(file, &buf->vb.done, pt);
+
+       if (buf->vb.state == VIDEOBUF_DONE ||
+           buf->vb.state == VIDEOBUF_ERROR)
+               return POLLIN|POLLRDNORM;
+
+       return 0;
+}
+
+static int sh_mobile_ceu_querycap(struct soc_camera_host *ici,
+                                 struct v4l2_capability *cap)
+{
+       strlcpy(cap->card, "SuperH_Mobile_CEU", sizeof(cap->card));
+       cap->version = KERNEL_VERSION(0, 0, 5);
+       cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+       return 0;
+}
+
+static void sh_mobile_ceu_init_videobuf(struct videobuf_queue *q,
+                                       struct soc_camera_device *icd)
+{
+       struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+       struct sh_mobile_ceu_dev *pcdev = ici->priv;
+
+       videobuf_queue_dma_contig_init(q,
+                                      &sh_mobile_ceu_videobuf_ops,
+                                      &ici->dev, &pcdev->lock,
+                                      V4L2_BUF_TYPE_VIDEO_CAPTURE,
+                                      V4L2_FIELD_NONE,
+                                      sizeof(struct sh_mobile_ceu_buffer),
+                                      icd);
+}
+
+static struct soc_camera_host_ops sh_mobile_ceu_host_ops = {
+       .owner          = THIS_MODULE,
+       .add            = sh_mobile_ceu_add_device,
+       .remove         = sh_mobile_ceu_remove_device,
+       .set_fmt_cap    = sh_mobile_ceu_set_fmt_cap,
+       .try_fmt_cap    = sh_mobile_ceu_try_fmt_cap,
+       .reqbufs        = sh_mobile_ceu_reqbufs,
+       .poll           = sh_mobile_ceu_poll,
+       .querycap       = sh_mobile_ceu_querycap,
+       .try_bus_param  = sh_mobile_ceu_try_bus_param,
+       .set_bus_param  = sh_mobile_ceu_set_bus_param,
+       .init_videobuf  = sh_mobile_ceu_init_videobuf,
+};
+
+static int sh_mobile_ceu_probe(struct platform_device *pdev)
+{
+       struct sh_mobile_ceu_dev *pcdev;
+       struct resource *res;
+       void __iomem *base;
+       unsigned int irq;
+       int err = 0;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       irq = platform_get_irq(pdev, 0);
+       if (!res || !irq) {
+               dev_err(&pdev->dev, "Not enough CEU platform resources.\n");
+               err = -ENODEV;
+               goto exit;
+       }
+
+       pcdev = kzalloc(sizeof(*pcdev), GFP_KERNEL);
+       if (!pcdev) {
+               dev_err(&pdev->dev, "Could not allocate pcdev\n");
+               err = -ENOMEM;
+               goto exit;
+       }
+
+       platform_set_drvdata(pdev, pcdev);
+       INIT_LIST_HEAD(&pcdev->capture);
+       spin_lock_init(&pcdev->lock);
+
+       pcdev->pdata = pdev->dev.platform_data;
+       if (!pcdev->pdata) {
+               err = -EINVAL;
+               dev_err(&pdev->dev, "CEU platform data not set.\n");
+               goto exit_kfree;
+       }
+
+       base = ioremap_nocache(res->start, res->end - res->start + 1);
+       if (!base) {
+               err = -ENXIO;
+               dev_err(&pdev->dev, "Unable to ioremap CEU registers.\n");
+               goto exit_kfree;
+       }
+
+       pcdev->irq = irq;
+       pcdev->base = base;
+       pcdev->video_limit = 0; /* only enabled if second resource exists */
+       pcdev->dev = &pdev->dev;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+       if (res) {
+               err = dma_declare_coherent_memory(&pdev->dev, res->start,
+                                                 res->start,
+                                                 (res->end - res->start) + 1,
+                                                 DMA_MEMORY_MAP |
+                                                 DMA_MEMORY_EXCLUSIVE);
+               if (!err) {
+                       dev_err(&pdev->dev, "Unable to declare CEU memory.\n");
+                       err = -ENXIO;
+                       goto exit_iounmap;
+               }
+
+               pcdev->video_limit = (res->end - res->start) + 1;
+       }
+
+       /* request irq */
+       err = request_irq(pcdev->irq, sh_mobile_ceu_irq, IRQF_DISABLED,
+                         pdev->dev.bus_id, pcdev);
+       if (err) {
+               dev_err(&pdev->dev, "Unable to register CEU interrupt.\n");
+               goto exit_release_mem;
+       }
+
+       pcdev->ici.priv = pcdev;
+       pcdev->ici.dev.parent = &pdev->dev;
+       pcdev->ici.nr = pdev->id;
+       pcdev->ici.drv_name = pdev->dev.bus_id,
+       pcdev->ici.ops = &sh_mobile_ceu_host_ops,
+
+       err = soc_camera_host_register(&pcdev->ici);
+       if (err)
+               goto exit_free_irq;
+
+       return 0;
+
+exit_free_irq:
+       free_irq(pcdev->irq, pcdev);
+exit_release_mem:
+       if (platform_get_resource(pdev, IORESOURCE_MEM, 1))
+               dma_release_declared_memory(&pdev->dev);
+exit_iounmap:
+       iounmap(base);
+exit_kfree:
+       kfree(pcdev);
+exit:
+       return err;
+}
+
+static int sh_mobile_ceu_remove(struct platform_device *pdev)
+{
+       struct sh_mobile_ceu_dev *pcdev = platform_get_drvdata(pdev);
+
+       soc_camera_host_unregister(&pcdev->ici);
+       free_irq(pcdev->irq, pcdev);
+       if (platform_get_resource(pdev, IORESOURCE_MEM, 1))
+               dma_release_declared_memory(&pdev->dev);
+       iounmap(pcdev->base);
+       kfree(pcdev);
+       return 0;
+}
+
+static struct platform_driver sh_mobile_ceu_driver = {
+       .driver         = {
+               .name   = "sh_mobile_ceu",
+       },
+       .probe          = sh_mobile_ceu_probe,
+       .remove         = sh_mobile_ceu_remove,
+};
+
+static int __init sh_mobile_ceu_init(void)
+{
+       return platform_driver_register(&sh_mobile_ceu_driver);
+}
+
+static void __exit sh_mobile_ceu_exit(void)
+{
+       return platform_driver_unregister(&sh_mobile_ceu_driver);
+}
+
+module_init(sh_mobile_ceu_init);
+module_exit(sh_mobile_ceu_exit);
+
+MODULE_DESCRIPTION("SuperH Mobile CEU driver");
+MODULE_AUTHOR("Magnus Damm");
+MODULE_LICENSE("GPL");
index 35223e0d7e49bb09deada63abd58f8428a59e18b..6ff489baacf3bad48ab8e0fd2ec4b4f0f861b47b 100644 (file)
@@ -44,7 +44,6 @@ static const struct usb_device_id sn9c102_id_table[] = {
        { SN9C102_USB_DEVICE(0x0c45, 0x6005, BRIDGE_SN9C102), },
        { SN9C102_USB_DEVICE(0x0c45, 0x6007, BRIDGE_SN9C102), },
        { SN9C102_USB_DEVICE(0x0c45, 0x6009, BRIDGE_SN9C102), },
-       { SN9C102_USB_DEVICE(0x0c45, 0x6011, BRIDGE_SN9C102), },
        { SN9C102_USB_DEVICE(0x0c45, 0x600d, BRIDGE_SN9C102), },
        { SN9C102_USB_DEVICE(0x0c45, 0x6019, BRIDGE_SN9C102), },
        { SN9C102_USB_DEVICE(0x0c45, 0x6024, BRIDGE_SN9C102), },
@@ -57,7 +56,6 @@ static const struct usb_device_id sn9c102_id_table[] = {
        { SN9C102_USB_DEVICE(0x0c45, 0x602d, BRIDGE_SN9C102), },
        { SN9C102_USB_DEVICE(0x0c45, 0x602e, BRIDGE_SN9C102), },
        { SN9C102_USB_DEVICE(0x0c45, 0x6030, BRIDGE_SN9C102), },
-       { SN9C102_USB_DEVICE(0x0c45, 0x603f, BRIDGE_SN9C102), },
        /* SN9C103 */
        { SN9C102_USB_DEVICE(0x0c45, 0x6080, BRIDGE_SN9C103), },
        { SN9C102_USB_DEVICE(0x0c45, 0x6082, BRIDGE_SN9C103), },
index d015bfe00950c5fcf1a943154cb6413ce8db60f5..e39b98f1eca4e97edc94ca6008436eef20449fdd 100644 (file)
@@ -26,6 +26,7 @@
 
 #include <media/v4l2-common.h>
 #include <media/v4l2-dev.h>
+#include <media/videobuf-core.h>
 #include <media/soc_camera.h>
 
 static LIST_HEAD(hosts);
@@ -44,7 +45,7 @@ format_by_fourcc(struct soc_camera_device *icd, unsigned int fourcc)
        return NULL;
 }
 
-static int soc_camera_try_fmt_cap(struct file *file, void *priv,
+static int soc_camera_try_fmt_vid_cap(struct file *file, void *priv,
                                  struct v4l2_format *f)
 {
        struct soc_camera_file *icf = file->private_data;
@@ -182,7 +183,6 @@ static int soc_camera_open(struct inode *inode, struct file *file)
        struct soc_camera_device *icd;
        struct soc_camera_host *ici;
        struct soc_camera_file *icf;
-       spinlock_t *lock;
        int ret;
 
        icf = vmalloc(sizeof(*icf));
@@ -209,13 +209,6 @@ static int soc_camera_open(struct inode *inode, struct file *file)
        }
 
        icf->icd = icd;
-
-       icf->lock = ici->ops->spinlock_alloc(icf);
-       if (!icf->lock) {
-               ret = -ENOMEM;
-               goto esla;
-       }
-
        icd->use_count++;
 
        /* Now we really have to activate the camera */
@@ -233,21 +226,12 @@ static int soc_camera_open(struct inode *inode, struct file *file)
        file->private_data = icf;
        dev_dbg(&icd->dev, "camera device open\n");
 
-       /* We must pass NULL as dev pointer, then all pci_* dma operations
-        * transform to normal dma_* ones. */
-       videobuf_queue_sg_init(&icf->vb_vidq, ici->vbq_ops, NULL, icf->lock,
-                               V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE,
-                               ici->msize, icd);
+       ici->ops->init_videobuf(&icf->vb_vidq, icd);
 
        return 0;
 
        /* All errors are entered with the video_lock held */
 eiciadd:
-       lock = icf->lock;
-       icf->lock = NULL;
-       if (ici->ops->spinlock_free)
-               ici->ops->spinlock_free(lock);
-esla:
        module_put(ici->ops->owner);
 emgi:
        module_put(icd->ops->owner);
@@ -263,15 +247,11 @@ static int soc_camera_close(struct inode *inode, struct file *file)
        struct soc_camera_device *icd = icf->icd;
        struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
        struct video_device *vdev = icd->vdev;
-       spinlock_t *lock = icf->lock;
 
        mutex_lock(&video_lock);
        icd->use_count--;
        if (!icd->use_count)
                ici->ops->remove(icd);
-       icf->lock = NULL;
-       if (ici->ops->spinlock_free)
-               ici->ops->spinlock_free(lock);
        module_put(icd->ops->owner);
        module_put(ici->ops->owner);
        mutex_unlock(&video_lock);
@@ -342,7 +322,7 @@ static struct file_operations soc_camera_fops = {
 };
 
 
-static int soc_camera_s_fmt_cap(struct file *file, void *priv,
+static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv,
                                struct v4l2_format *f)
 {
        struct soc_camera_file *icf = file->private_data;
@@ -362,7 +342,7 @@ static int soc_camera_s_fmt_cap(struct file *file, void *priv,
        /* buswidth may be further adjusted by the ici */
        icd->buswidth = data_fmt->depth;
 
-       ret = soc_camera_try_fmt_cap(file, icf, f);
+       ret = soc_camera_try_fmt_vid_cap(file, icf, f);
        if (ret < 0)
                return ret;
 
@@ -389,7 +369,7 @@ static int soc_camera_s_fmt_cap(struct file *file, void *priv,
        return ici->ops->set_bus_param(icd, f->fmt.pix.pixelformat);
 }
 
-static int soc_camera_enum_fmt_cap(struct file *file, void  *priv,
+static int soc_camera_enum_fmt_vid_cap(struct file *file, void  *priv,
                                   struct v4l2_fmtdesc *f)
 {
        struct soc_camera_file *icf = file->private_data;
@@ -408,7 +388,7 @@ static int soc_camera_enum_fmt_cap(struct file *file, void  *priv,
        return 0;
 }
 
-static int soc_camera_g_fmt_cap(struct file *file, void *priv,
+static int soc_camera_g_fmt_vid_cap(struct file *file, void *priv,
                                struct v4l2_format *f)
 {
        struct soc_camera_file *icf = file->private_data;
@@ -767,27 +747,12 @@ static void dummy_release(struct device *dev)
 {
 }
 
-static spinlock_t *spinlock_alloc(struct soc_camera_file *icf)
-{
-       spinlock_t *lock = kmalloc(sizeof(spinlock_t), GFP_KERNEL);
-
-       if (lock)
-               spin_lock_init(lock);
-
-       return lock;
-}
-
-static void spinlock_free(spinlock_t *lock)
-{
-       kfree(lock);
-}
-
 int soc_camera_host_register(struct soc_camera_host *ici)
 {
        int ret;
        struct soc_camera_host *ix;
 
-       if (!ici->vbq_ops || !ici->ops->add || !ici->ops->remove)
+       if (!ici->ops->init_videobuf || !ici->ops->add || !ici->ops->remove)
                return -EINVAL;
 
        /* Number might be equal to the platform device ID */
@@ -811,11 +776,6 @@ int soc_camera_host_register(struct soc_camera_host *ici)
        if (ret)
                goto edevr;
 
-       if (!ici->ops->spinlock_alloc) {
-               ici->ops->spinlock_alloc = spinlock_alloc;
-               ici->ops->spinlock_free = spinlock_free;
-       }
-
        scan_add_host(ici);
 
        return 0;
@@ -925,15 +885,15 @@ int soc_camera_video_start(struct soc_camera_device *icd)
        vdev->minor             = -1;
        vdev->tvnorms           = V4L2_STD_UNKNOWN,
        vdev->vidioc_querycap   = soc_camera_querycap;
-       vdev->vidioc_g_fmt_cap  = soc_camera_g_fmt_cap;
-       vdev->vidioc_enum_fmt_cap = soc_camera_enum_fmt_cap;
-       vdev->vidioc_s_fmt_cap  = soc_camera_s_fmt_cap;
+       vdev->vidioc_g_fmt_vid_cap = soc_camera_g_fmt_vid_cap;
+       vdev->vidioc_enum_fmt_vid_cap = soc_camera_enum_fmt_vid_cap;
+       vdev->vidioc_s_fmt_vid_cap = soc_camera_s_fmt_vid_cap;
        vdev->vidioc_enum_input = soc_camera_enum_input;
        vdev->vidioc_g_input    = soc_camera_g_input;
        vdev->vidioc_s_input    = soc_camera_s_input;
        vdev->vidioc_s_std      = soc_camera_s_std;
        vdev->vidioc_reqbufs    = soc_camera_reqbufs;
-       vdev->vidioc_try_fmt_cap = soc_camera_try_fmt_cap;
+       vdev->vidioc_try_fmt_vid_cap = soc_camera_try_fmt_vid_cap;
        vdev->vidioc_querybuf   = soc_camera_querybuf;
        vdev->vidioc_qbuf       = soc_camera_qbuf;
        vdev->vidioc_dqbuf      = soc_camera_dqbuf;
diff --git a/drivers/media/video/soc_camera_platform.c b/drivers/media/video/soc_camera_platform.c
new file mode 100644 (file)
index 0000000..eefb032
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ * Generic Platform Camera Driver
+ *
+ * Copyright (C) 2008 Magnus Damm
+ * Based on mt9m001 driver,
+ * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.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/slab.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <media/soc_camera.h>
+
+struct soc_camera_platform_info {
+       int iface;
+       char *format_name;
+       unsigned long format_depth;
+       struct v4l2_pix_format format;
+       unsigned long bus_param;
+       int (*set_capture)(struct soc_camera_platform_info *info, int enable);
+};
+
+struct soc_camera_platform_priv {
+       struct soc_camera_platform_info *info;
+       struct soc_camera_device icd;
+       struct soc_camera_data_format format;
+};
+
+static struct soc_camera_platform_info *
+soc_camera_platform_get_info(struct soc_camera_device *icd)
+{
+       struct soc_camera_platform_priv *priv;
+       priv = container_of(icd, struct soc_camera_platform_priv, icd);
+       return priv->info;
+}
+
+static int soc_camera_platform_init(struct soc_camera_device *icd)
+{
+       return 0;
+}
+
+static int soc_camera_platform_release(struct soc_camera_device *icd)
+{
+       return 0;
+}
+
+static int soc_camera_platform_start_capture(struct soc_camera_device *icd)
+{
+       struct soc_camera_platform_info *p = soc_camera_platform_get_info(icd);
+       return p->set_capture(p, 1);
+}
+
+static int soc_camera_platform_stop_capture(struct soc_camera_device *icd)
+{
+       struct soc_camera_platform_info *p = soc_camera_platform_get_info(icd);
+       return p->set_capture(p, 0);
+}
+
+static int soc_camera_platform_set_bus_param(struct soc_camera_device *icd,
+                                            unsigned long flags)
+{
+       return 0;
+}
+
+static unsigned long
+soc_camera_platform_query_bus_param(struct soc_camera_device *icd)
+{
+       struct soc_camera_platform_info *p = soc_camera_platform_get_info(icd);
+       return p->bus_param;
+}
+
+static int soc_camera_platform_set_fmt_cap(struct soc_camera_device *icd,
+                                          __u32 pixfmt, struct v4l2_rect *rect)
+{
+       return 0;
+}
+
+static int soc_camera_platform_try_fmt_cap(struct soc_camera_device *icd,
+                                          struct v4l2_format *f)
+{
+       struct soc_camera_platform_info *p = soc_camera_platform_get_info(icd);
+
+       f->fmt.pix.width = p->format.width;
+       f->fmt.pix.height = p->format.height;
+       return 0;
+}
+
+static int soc_camera_platform_video_probe(struct soc_camera_device *icd)
+{
+       struct soc_camera_platform_priv *priv;
+       priv = container_of(icd, struct soc_camera_platform_priv, icd);
+
+       priv->format.name = priv->info->format_name;
+       priv->format.depth = priv->info->format_depth;
+       priv->format.fourcc = priv->info->format.pixelformat;
+       priv->format.colorspace = priv->info->format.colorspace;
+
+       icd->formats = &priv->format;
+       icd->num_formats = 1;
+
+       return soc_camera_video_start(icd);
+}
+
+static void soc_camera_platform_video_remove(struct soc_camera_device *icd)
+{
+       soc_camera_video_stop(icd);
+}
+
+static struct soc_camera_ops soc_camera_platform_ops = {
+       .owner                  = THIS_MODULE,
+       .probe                  = soc_camera_platform_video_probe,
+       .remove                 = soc_camera_platform_video_remove,
+       .init                   = soc_camera_platform_init,
+       .release                = soc_camera_platform_release,
+       .start_capture          = soc_camera_platform_start_capture,
+       .stop_capture           = soc_camera_platform_stop_capture,
+       .set_fmt_cap            = soc_camera_platform_set_fmt_cap,
+       .try_fmt_cap            = soc_camera_platform_try_fmt_cap,
+       .set_bus_param          = soc_camera_platform_set_bus_param,
+       .query_bus_param        = soc_camera_platform_query_bus_param,
+};
+
+static int soc_camera_platform_probe(struct platform_device *pdev)
+{
+       struct soc_camera_platform_priv *priv;
+       struct soc_camera_platform_info *p;
+       struct soc_camera_device *icd;
+       int ret;
+
+       p = pdev->dev.platform_data;
+       if (!p)
+               return -EINVAL;
+
+       priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       priv->info = p;
+       platform_set_drvdata(pdev, priv);
+
+       icd = &priv->icd;
+       icd->ops        = &soc_camera_platform_ops;
+       icd->control    = &pdev->dev;
+       icd->width_min  = 0;
+       icd->width_max  = priv->info->format.width;
+       icd->height_min = 0;
+       icd->height_max = priv->info->format.height;
+       icd->y_skip_top = 0;
+       icd->iface      = priv->info->iface;
+
+       ret = soc_camera_device_register(icd);
+       if (ret)
+               kfree(priv);
+
+       return ret;
+}
+
+static int soc_camera_platform_remove(struct platform_device *pdev)
+{
+       struct soc_camera_platform_priv *priv = platform_get_drvdata(pdev);
+
+       soc_camera_device_unregister(&priv->icd);
+       kfree(priv);
+       return 0;
+}
+
+static struct platform_driver soc_camera_platform_driver = {
+       .driver         = {
+               .name   = "soc_camera_platform",
+       },
+       .probe          = soc_camera_platform_probe,
+       .remove         = soc_camera_platform_remove,
+};
+
+static int __init soc_camera_platform_module_init(void)
+{
+       return platform_driver_register(&soc_camera_platform_driver);
+}
+
+static void __exit soc_camera_platform_module_exit(void)
+{
+       return platform_driver_unregister(&soc_camera_platform_driver);
+}
+
+module_init(soc_camera_platform_module_init);
+module_exit(soc_camera_platform_module_exit);
+
+MODULE_DESCRIPTION("SoC Camera Platform driver");
+MODULE_AUTHOR("Magnus Damm");
+MODULE_LICENSE("GPL v2");
index b12c60cf5a0985749df66008e1b6f10a558ee28f..f308c38d744fb2e7747328baf339492282ca8beb 100644 (file)
@@ -981,7 +981,7 @@ static int stk_vidioc_s_ctrl(struct file *filp,
 }
 
 
-static int stk_vidioc_enum_fmt_cap(struct file *filp,
+static int stk_vidioc_enum_fmt_vid_cap(struct file *filp,
                void *priv, struct v4l2_fmtdesc *fmtd)
 {
        fmtd->flags = 0;
@@ -1025,7 +1025,7 @@ static struct stk_size {
        { .w = 176,  .h = 144,  .m = MODE_QCIF, },
 };
 
-static int stk_vidioc_g_fmt_cap(struct file *filp,
+static int stk_vidioc_g_fmt_vid_cap(struct file *filp,
                void *priv, struct v4l2_format *f)
 {
        struct v4l2_pix_format *pix_format = &f->fmt.pix;
@@ -1054,7 +1054,7 @@ static int stk_vidioc_g_fmt_cap(struct file *filp,
        return 0;
 }
 
-static int stk_vidioc_try_fmt_cap(struct file *filp,
+static int stk_vidioc_try_fmt_vid_cap(struct file *filp,
                void *priv, struct v4l2_format *fmtd)
 {
        int i;
@@ -1131,7 +1131,7 @@ static int stk_setup_format(struct stk_camera *dev)
        return stk_sensor_configure(dev);
 }
 
-static int stk_vidioc_s_fmt_cap(struct file *filp,
+static int stk_vidioc_s_fmt_vid_cap(struct file *filp,
                void *priv, struct v4l2_format *fmtd)
 {
        int ret;
@@ -1145,7 +1145,7 @@ static int stk_vidioc_s_fmt_cap(struct file *filp,
                return -EBUSY;
        if (dev->owner && dev->owner != filp)
                return -EBUSY;
-       ret = stk_vidioc_try_fmt_cap(filp, priv, fmtd);
+       ret = stk_vidioc_try_fmt_vid_cap(filp, priv, fmtd);
        if (ret)
                return ret;
        dev->owner = filp;
@@ -1342,10 +1342,10 @@ static struct video_device stk_v4l_data = {
        .release = stk_v4l_dev_release,
 
        .vidioc_querycap = stk_vidioc_querycap,
-       .vidioc_enum_fmt_cap = stk_vidioc_enum_fmt_cap,
-       .vidioc_try_fmt_cap = stk_vidioc_try_fmt_cap,
-       .vidioc_s_fmt_cap = stk_vidioc_s_fmt_cap,
-       .vidioc_g_fmt_cap = stk_vidioc_g_fmt_cap,
+       .vidioc_enum_fmt_vid_cap = stk_vidioc_enum_fmt_vid_cap,
+       .vidioc_try_fmt_vid_cap = stk_vidioc_try_fmt_vid_cap,
+       .vidioc_s_fmt_vid_cap = stk_vidioc_s_fmt_vid_cap,
+       .vidioc_g_fmt_vid_cap = stk_vidioc_g_fmt_vid_cap,
        .vidioc_enum_input = stk_vidioc_enum_input,
        .vidioc_s_input = stk_vidioc_s_input,
        .vidioc_g_input = stk_vidioc_g_input,
index 8f0100f67a91ea21bec97b6f708d337c18da1428..29991d1cf13e185fae6090f7a0467b490e2689a9 100644 (file)
@@ -523,6 +523,9 @@ static int ioctl_g_ctrl(struct v4l2_int_device *s,
        if (val < 0)
                return val;
 
+       if (vc->id == V4L2_CID_HFLIP || vc->id == V4L2_CID_VFLIP)
+               val ^= sensor->platform_data->is_upside_down();
+
        vc->value = val;
        return 0;
 }
@@ -556,6 +559,9 @@ static int ioctl_s_ctrl(struct v4l2_int_device *s,
        if (lvc == NULL)
                return -EINVAL;
 
+       if (vc->id == V4L2_CID_HFLIP || vc->id == V4L2_CID_VFLIP)
+               val ^= sensor->platform_data->is_upside_down();
+
        val = val << lvc->start_bit;
        if (tcm825x_write_reg_mask(client, lvc->reg, val))
                return -EIO;
index 966765b66b3ad89de2913a6469ed9f9cc944e163..770ebacfa344a109663d533f9dc049a226409bd1 100644 (file)
@@ -182,6 +182,7 @@ struct tcm825x_platform_data {
        int (*needs_reset)(struct v4l2_int_device *s, void *buf,
                           struct v4l2_pix_format *fmt);
        int (*ifparm)(struct v4l2_ifparm *p);
+       int (*is_upside_down)(void);
 };
 
 /* Array of image sizes supported by TCM825X.  These must be ordered from
index b4d10f7a4e57e3644d5d2bcfd7773f72807dcbb5..ae75c187da79baa0623da0667694aa308a67f015 100644 (file)
@@ -72,6 +72,7 @@ static unsigned short normal_i2c[] = {
        I2C_ADDR_TDA7432 >> 1,
        I2C_CLIENT_END,
 };
+
 I2C_CLIENT_INSMOD;
 
 /* Structure of address and subaddresses for the tda7432 */
index 0cee00242782bb0ec16a0736b822d43370cd9624..2437c1a269c58e98890ecf3f91c811c5d577938f 100644 (file)
@@ -34,6 +34,7 @@
 static int debug;              /* insmod parameter */
 module_param(debug, int, 0644);
 MODULE_PARM_DESC(debug, "Turn on/off device debugging (default:off).");
+
 #define dprintk(args...) \
            do { if (debug) { printk("%s: %s()[%d]: ", KBUILD_MODNAME, __func__, __LINE__); printk(args); } } while (0)
 
index 3c0557130a704ec1084899b18288be3b7a36f514..7a8ce8fb46dc88b4b13116a0875b98a28484af16 100644 (file)
@@ -30,7 +30,6 @@
 #include <linux/i2c.h>
 #include <linux/init.h>
 
-
 #include <media/i2c-addr.h>
 
 static int debug; /* insmod parameter */
@@ -42,6 +41,7 @@ static unsigned short normal_i2c[] =  {
     I2C_ADDR_TDA9875 >> 1,
     I2C_CLIENT_END
 };
+
 I2C_CLIENT_INSMOD;
 
 /* This is a superset of the TDA9875 */
index 9513d8611e8781431db6306fb5a2c8645df19817..421c1445e96cd5210ceddb22d2156b474292aa70 100644 (file)
@@ -36,6 +36,7 @@
 static int debug;              /* insmod parameter */
 module_param(debug, int, 0644);
 MODULE_PARM_DESC(debug, "Turn on/off device debugging (default:off).");
+
 #define dprintk(args...) \
            do { if (debug) { printk("%s: %s()[%d]: ", KBUILD_MODNAME, __func__, __LINE__); printk(args); } } while (0)
 
index 7fd53367c07caef877db4d21b55dbfc8d6355147..b5c8957d130e0115d450c3d0b56ab78b15a9ba03 100644 (file)
@@ -36,6 +36,7 @@
 static int debug;              /* insmod parameter */
 module_param(debug, int, 0644);
 MODULE_PARM_DESC(debug, "Turn on/off device debugging (default:off).");
+
 #define dprintk(args...) \
            do { if (debug) { printk("%s: %s()[%d]: ", KBUILD_MODNAME, __func__, __LINE__); printk(args); } } while (0)
 
index 28ab9f9d760a29aa0317a9ee4eda54a78f2da854..9220378a5637b6233cd0ec665f3c9b41e27e3e9d 100644 (file)
@@ -39,7 +39,6 @@ MODULE_LICENSE("GPL");
 
 static unsigned short normal_i2c[] = { 0x34 >> 1, I2C_CLIENT_END };
 
-
 I2C_CLIENT_INSMOD;
 
 /* ----------------------------------------------------------------------- */
index 0d12ace616658ce8f619d04a1e7815bfaf050a78..93d879dc510f2b5b0d5e0d148696942cb1cfed17 100644 (file)
@@ -1298,7 +1298,6 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = {
        .id_table = tuner_id,
 };
 
-
 /*
  * Overrides for Emacs so that we follow Linus's tabbing style.
  * ---------------------------------------------------------------------------
index c77914d99d15c5972c94e9763a08b5789098e90a..463680b13289c6515d658225b8711f6e39c26caf 100644 (file)
@@ -69,7 +69,6 @@ typedef struct AUDIOCMD {
 /* chip description */
 struct CHIPDESC {
        char       *name;             /* chip name         */
-       int        id;                /* ID */
        int        addr_lo, addr_hi;  /* i2c address range */
        int        registers;         /* # of registers    */
 
@@ -1256,7 +1255,6 @@ module_param(ta8874z, int, 0444);
 static struct CHIPDESC chiplist[] = {
        {
                .name       = "tda9840",
-               .id         = I2C_DRIVERID_TDA9840,
                .insmodopt  = &tda9840,
                .addr_lo    = I2C_ADDR_TDA9840 >> 1,
                .addr_hi    = I2C_ADDR_TDA9840 >> 1,
@@ -1272,7 +1270,6 @@ static struct CHIPDESC chiplist[] = {
        },
        {
                .name       = "tda9873h",
-               .id         = I2C_DRIVERID_TDA9873,
                .checkit    = tda9873_checkit,
                .insmodopt  = &tda9873,
                .addr_lo    = I2C_ADDR_TDA985x_L >> 1,
@@ -1293,7 +1290,6 @@ static struct CHIPDESC chiplist[] = {
        },
        {
                .name       = "tda9874h/a",
-               .id         = I2C_DRIVERID_TDA9874,
                .checkit    = tda9874a_checkit,
                .initialize = tda9874a_initialize,
                .insmodopt  = &tda9874a,
@@ -1306,7 +1302,6 @@ static struct CHIPDESC chiplist[] = {
        },
        {
                .name       = "tda9850",
-               .id         = I2C_DRIVERID_TDA9850,
                .insmodopt  = &tda9850,
                .addr_lo    = I2C_ADDR_TDA985x_L >> 1,
                .addr_hi    = I2C_ADDR_TDA985x_H >> 1,
@@ -1319,7 +1314,6 @@ static struct CHIPDESC chiplist[] = {
        },
        {
                .name       = "tda9855",
-               .id         = I2C_DRIVERID_TDA9855,
                .insmodopt  = &tda9855,
                .addr_lo    = I2C_ADDR_TDA985x_L >> 1,
                .addr_hi    = I2C_ADDR_TDA985x_H >> 1,
@@ -1344,7 +1338,6 @@ static struct CHIPDESC chiplist[] = {
        },
        {
                .name       = "tea6300",
-               .id         = I2C_DRIVERID_TEA6300,
                .insmodopt  = &tea6300,
                .addr_lo    = I2C_ADDR_TEA6300 >> 1,
                .addr_hi    = I2C_ADDR_TEA6300 >> 1,
@@ -1365,7 +1358,6 @@ static struct CHIPDESC chiplist[] = {
        },
        {
                .name       = "tea6320",
-               .id         = I2C_DRIVERID_TEA6300,
                .initialize = tea6320_initialize,
                .insmodopt  = &tea6320,
                .addr_lo    = I2C_ADDR_TEA6300 >> 1,
@@ -1387,7 +1379,6 @@ static struct CHIPDESC chiplist[] = {
        },
        {
                .name       = "tea6420",
-               .id         = I2C_DRIVERID_TEA6420,
                .insmodopt  = &tea6420,
                .addr_lo    = I2C_ADDR_TEA6420 >> 1,
                .addr_hi    = I2C_ADDR_TEA6420 >> 1,
@@ -1400,7 +1391,6 @@ static struct CHIPDESC chiplist[] = {
        },
        {
                .name       = "tda8425",
-               .id         = I2C_DRIVERID_TDA8425,
                .insmodopt  = &tda8425,
                .addr_lo    = I2C_ADDR_TDA8425 >> 1,
                .addr_hi    = I2C_ADDR_TDA8425 >> 1,
@@ -1424,7 +1414,6 @@ static struct CHIPDESC chiplist[] = {
        },
        {
                .name       = "pic16c54 (PV951)",
-               .id         = I2C_DRIVERID_PIC16C54_PV9,
                .insmodopt  = &pic16c54,
                .addr_lo    = I2C_ADDR_PIC16C54 >> 1,
                .addr_hi    = I2C_ADDR_PIC16C54>> 1,
@@ -1440,8 +1429,6 @@ static struct CHIPDESC chiplist[] = {
        },
        {
                .name       = "ta8874z",
-               .id         = -1,
-               /*.id         = I2C_DRIVERID_TA8874Z, */
                .checkit    = ta8874z_checkit,
                .insmodopt  = &ta8874z,
                .addr_lo    = I2C_ADDR_TDA9840 >> 1,
index a9c5e5adba3afb704c300df72bbbdfe31a2928b8..abf685464b7ca1f3335af4ca40f76fdaea8bf2aa 100644 (file)
@@ -169,7 +169,6 @@ static void usbvision_rvfree(void *mem, unsigned long size)
 }
 
 
-
 #if ENABLE_HEXDUMP
 static void usbvision_hexdump(const unsigned char *data, int len)
 {
@@ -2317,7 +2316,6 @@ static void usbvision_powerOffTimer(unsigned long data)
        del_timer(&usbvision->powerOffTimer);
        INIT_WORK(&usbvision->powerOffWork, call_usbvision_power_off);
        (void) schedule_work(&usbvision->powerOffWork);
-
 }
 
 void usbvision_init_powerOffTimer(struct usb_usbvision *usbvision)
@@ -2518,7 +2516,6 @@ int usbvision_init_isoc(struct usb_usbvision *usbvision)
                }
        }
 
-
        /* Submit all URBs */
        for (bufIdx = 0; bufIdx < USBVISION_NUMSBUF; bufIdx++) {
                        errCode = usb_submit_urb(usbvision->sbuf[bufIdx].urb,
@@ -2564,7 +2561,6 @@ void usbvision_stop_isoc(struct usb_usbvision *usbvision)
                usbvision->sbuf[bufIdx].urb = NULL;
        }
 
-
        PDEBUG(DBG_ISOC, "%s: streaming=Stream_Off\n", __func__);
        usbvision->streaming = Stream_Off;
 
index e2274d77ea295a532befda800eec9ab53e14eb0c..a6d00858b07ecad47da68bf54ccb06280fe55352 100644 (file)
@@ -190,7 +190,6 @@ static u32 functionality(struct i2c_adapter *adap)
        return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR;
 }
 
-
 /* -----exported algorithm data: ------------------------------------- */
 
 static struct i2c_algorithm usbvision_algo = {
@@ -514,11 +513,7 @@ static struct i2c_adapter i2c_adap_template = {
        .id                = I2C_HW_B_BT848, /* FIXME */
        .client_register   = attach_inform,
        .client_unregister = detach_inform,
-#ifdef I2C_ADAP_CLASS_TV_ANALOG
-       .class             = I2C_ADAP_CLASS_TV_ANALOG,
-#else
        .class             = I2C_CLASS_TV_ANALOG,
-#endif
 };
 
 static struct i2c_client i2c_client_template = {
index d97261ab430f9f3c772b9ef27f18558a2da5f5fe..cd6c41d67899280d9628e3f4e29fd3e57826866d 100644 (file)
@@ -179,7 +179,6 @@ MODULE_ALIAS(DRIVER_ALIAS);
 /*   /sys/bus/usb/drivers/USBVision Video Grabber                            */
 /*****************************************************************************/
 
-
 #define YES_NO(x) ((x) ? "Yes" : "No")
 
 static inline struct usb_usbvision *cd_to_usbvision(struct device *cd)
@@ -370,7 +369,6 @@ static void usbvision_remove_sysfs(struct video_device *vdev)
        }
 }
 
-
 /*
  * usbvision_open()
  *
@@ -388,7 +386,6 @@ static int usbvision_v4l2_open(struct inode *inode, struct file *file)
 
        PDEBUG(DBG_IO, "open");
 
-
        usbvision_reset_powerOffTimer(usbvision);
 
        if (usbvision->user)
@@ -442,9 +439,6 @@ static int usbvision_v4l2_open(struct inode *inode, struct file *file)
                mutex_unlock(&usbvision->lock);
        }
 
-       if (errCode) {
-       }
-
        /* prepare queues */
        usbvision_empty_framequeues(usbvision);
 
@@ -495,8 +489,6 @@ static int usbvision_v4l2_close(struct inode *inode, struct file *file)
        }
 
        PDEBUG(DBG_IO, "success");
-
-
        return 0;
 }
 
@@ -998,7 +990,7 @@ static int vidioc_streamoff(struct file *file,
        return 0;
 }
 
-static int vidioc_enum_fmt_cap (struct file *file, void  *priv,
+static int vidioc_enum_fmt_vid_cap (struct file *file, void  *priv,
                                        struct v4l2_fmtdesc *vfd)
 {
        if(vfd->index>=USBVISION_SUPPORTED_PALETTES-1) {
@@ -1012,7 +1004,7 @@ static int vidioc_enum_fmt_cap (struct file *file, void  *priv,
        return 0;
 }
 
-static int vidioc_g_fmt_cap (struct file *file, void *priv,
+static int vidioc_g_fmt_vid_cap (struct file *file, void *priv,
                                        struct v4l2_format *vf)
 {
        struct video_device *dev = video_devdata(file);
@@ -1030,7 +1022,7 @@ static int vidioc_g_fmt_cap (struct file *file, void *priv,
        return 0;
 }
 
-static int vidioc_try_fmt_cap (struct file *file, void *priv,
+static int vidioc_try_fmt_vid_cap (struct file *file, void *priv,
                               struct v4l2_format *vf)
 {
        struct video_device *dev = video_devdata(file);
@@ -1060,7 +1052,7 @@ static int vidioc_try_fmt_cap (struct file *file, void *priv,
        return 0;
 }
 
-static int vidioc_s_fmt_cap(struct file *file, void *priv,
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
                               struct v4l2_format *vf)
 {
        struct video_device *dev = video_devdata(file);
@@ -1068,7 +1060,7 @@ static int vidioc_s_fmt_cap(struct file *file, void *priv,
                (struct usb_usbvision *) video_get_drvdata(dev);
        int ret;
 
-       if( 0 != (ret=vidioc_try_fmt_cap (file, priv, vf)) ) {
+       if( 0 != (ret=vidioc_try_fmt_vid_cap (file, priv, vf)) ) {
                return ret;
        }
 
@@ -1346,9 +1338,7 @@ static int usbvision_radio_close(struct inode *inode, struct file *file)
                usbvision_release(usbvision);
        }
 
-
        PDEBUG(DBG_IO, "success");
-
        return errCode;
 }
 
@@ -1360,7 +1350,6 @@ static int usbvision_vbi_open(struct inode *inode, struct file *file)
 {
        /* TODO */
        return -ENODEV;
-
 }
 
 static int usbvision_vbi_close(struct inode *inode, struct file *file)
@@ -1407,10 +1396,10 @@ static struct video_device usbvision_video_template = {
        .release        = video_device_release,
        .minor          = -1,
        .vidioc_querycap      = vidioc_querycap,
-       .vidioc_enum_fmt_cap  = vidioc_enum_fmt_cap,
-       .vidioc_g_fmt_cap     = vidioc_g_fmt_cap,
-       .vidioc_try_fmt_cap   = vidioc_try_fmt_cap,
-       .vidioc_s_fmt_cap     = vidioc_s_fmt_cap,
+       .vidioc_enum_fmt_vid_cap  = vidioc_enum_fmt_vid_cap,
+       .vidioc_g_fmt_vid_cap     = vidioc_g_fmt_vid_cap,
+       .vidioc_try_fmt_vid_cap   = vidioc_try_fmt_vid_cap,
+       .vidioc_s_fmt_vid_cap     = vidioc_s_fmt_vid_cap,
        .vidioc_reqbufs       = vidioc_reqbufs,
        .vidioc_querybuf      = vidioc_querybuf,
        .vidioc_qbuf          = vidioc_qbuf,
@@ -1899,7 +1888,6 @@ static void __devexit usbvision_disconnect(struct usb_interface *intf)
        }
 
        PDEBUG(DBG_PROBE, "success");
-
 }
 
 static struct usb_driver usbvision_driver = {
diff --git a/drivers/media/video/uvc/Kconfig b/drivers/media/video/uvc/Kconfig
new file mode 100644 (file)
index 0000000..c2d9760
--- /dev/null
@@ -0,0 +1,17 @@
+config USB_VIDEO_CLASS
+       tristate "USB Video Class (UVC)"
+       ---help---
+         Support for the USB Video Class (UVC).  Currently only video
+         input devices, such as webcams, are supported.
+
+         For more information see: <http://linux-uvc.berlios.de/>
+
+config USB_VIDEO_CLASS_INPUT_EVDEV
+       bool "UVC input events device support"
+       default y
+       depends on USB_VIDEO_CLASS && INPUT
+       ---help---
+         This option makes USB Video Class devices register an input device
+         to report button events.
+
+         If you are in doubt, say Y.
index f0ee46d15540032bb4bff37293858031d0a01d58..3ae95512666f7685b54da8e495845cb44844a5e6 100644 (file)
@@ -1254,3 +1254,4 @@ void uvc_ctrl_init(void)
        for (; mapping < mend; ++mapping)
                uvc_ctrl_add_mapping(mapping);
 }
+
index 60ced589f89815d84de454fffb2e1cd6916bf4bd..f2b2983fe06270eae97d10f6c1f4a4117a159fa1 100644 (file)
@@ -298,7 +298,8 @@ static int uvc_parse_format(struct uvc_device *dev,
        switch (buffer[2]) {
        case VS_FORMAT_UNCOMPRESSED:
        case VS_FORMAT_FRAME_BASED:
-               if (buflen < 27) {
+               n = buffer[2] == VS_FORMAT_UNCOMPRESSED ? 27 : 28;
+               if (buflen < n) {
                        uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming"
                               "interface %d FORMAT error\n",
                               dev->udev->devnum,
@@ -1632,13 +1633,16 @@ static void uvc_disconnect(struct usb_interface *intf)
         * reference to the uvc_device instance after uvc_v4l2_open() received
         * the pointer to the device (video_devdata) but before it got the
         * chance to increase the reference count (kref_get).
+        *
+        * Note that the reference can't be released with the lock held,
+        * otherwise a AB-BA deadlock can occur with videodev_lock that
+        * videodev acquires in videodev_open() and video_unregister_device().
         */
        mutex_lock(&uvc_driver.open_mutex);
-
        dev->state |= UVC_DEV_DISCONNECTED;
-       kref_put(&dev->kref, uvc_delete);
-
        mutex_unlock(&uvc_driver.open_mutex);
+
+       kref_put(&dev->kref, uvc_delete);
 }
 
 static int uvc_suspend(struct usb_interface *intf, pm_message_t message)
@@ -1825,6 +1829,15 @@ static struct usb_device_id uvc_ids[] = {
          .bInterfaceSubClass   = 1,
          .bInterfaceProtocol   = 0,
          .driver_info          = UVC_QUIRK_STREAM_NO_FID },
+       /* Asus F9SG */
+       { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
+                               | USB_DEVICE_ID_MATCH_INT_INFO,
+         .idVendor             = 0x174f,
+         .idProduct            = 0x8a31,
+         .bInterfaceClass      = USB_CLASS_VIDEO,
+         .bInterfaceSubClass   = 1,
+         .bInterfaceProtocol   = 0,
+         .driver_info          = UVC_QUIRK_STREAM_NO_FID },
        /* Syntek (Asus U3S) */
        { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
                                | USB_DEVICE_ID_MATCH_INT_INFO,
@@ -1891,6 +1904,15 @@ static struct usb_device_id uvc_ids[] = {
          .bInterfaceSubClass   = 1,
          .bInterfaceProtocol   = 0,
          .driver_info          = UVC_QUIRK_PROBE_MINMAX },
+       /* Medion Akoya Mini E1210 */
+       { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
+                               | USB_DEVICE_ID_MATCH_INT_INFO,
+         .idVendor             = 0x5986,
+         .idProduct            = 0x0141,
+         .bInterfaceClass      = USB_CLASS_VIDEO,
+         .bInterfaceSubClass   = 1,
+         .bInterfaceProtocol   = 0,
+         .driver_info          = UVC_QUIRK_PROBE_MINMAX },
        /* Acer OrbiCam - Unknown vendor */
        { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
                                | USB_DEVICE_ID_MATCH_INT_INFO,
@@ -1953,3 +1975,4 @@ MODULE_AUTHOR(DRIVER_AUTHOR);
 MODULE_DESCRIPTION(DRIVER_DESC);
 MODULE_LICENSE("GPL");
 MODULE_VERSION(DRIVER_VERSION);
+
index 0923f0e3b3d4efa4646eb64a79aaa34579466e14..7388d0cee3d4745c2b9aa415c355a47a49b4df2e 100644 (file)
@@ -475,3 +475,4 @@ struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue,
        wake_up(&buf->wait);
        return nextbuf;
 }
+
index be9084e5eace8d3c3c83cf93f0bd6dec45179b55..75e678ac54ebbe027aae395133e8a30ad76900b0 100644 (file)
@@ -22,6 +22,7 @@
 /* --------------------------------------------------------------------------
  * Input device
  */
+#ifdef CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV
 static int uvc_input_init(struct uvc_device *dev)
 {
        struct usb_device *udev = dev->udev;
@@ -67,6 +68,19 @@ static void uvc_input_cleanup(struct uvc_device *dev)
                input_unregister_device(dev->input);
 }
 
+static void uvc_input_report_key(struct uvc_device *dev, unsigned int code,
+       int value)
+{
+       if (dev->input)
+               input_report_key(dev->input, code, value);
+}
+
+#else
+#define uvc_input_init(dev)
+#define uvc_input_cleanup(dev)
+#define uvc_input_report_key(dev, code, value)
+#endif /* CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV */
+
 /* --------------------------------------------------------------------------
  * Status interrupt endpoint
  */
@@ -83,8 +97,7 @@ static void uvc_event_streaming(struct uvc_device *dev, __u8 *data, int len)
                        return;
                uvc_trace(UVC_TRACE_STATUS, "Button (intf %u) %s len %d\n",
                        data[1], data[3] ? "pressed" : "released", len);
-               if (dev->input)
-                       input_report_key(dev->input, BTN_0, data[3]);
+               uvc_input_report_key(dev, BTN_0, data[3]);
        } else {
                uvc_trace(UVC_TRACE_STATUS, "Stream %u error event %02x %02x "
                        "len %d.\n", data[1], data[2], data[3], len);
@@ -203,5 +216,6 @@ int uvc_status_resume(struct uvc_device *dev)
        if (dev->int_urb == NULL)
                return 0;
 
-       return usb_submit_urb(dev->int_urb, GFP_KERNEL);
+       return usb_submit_urb(dev->int_urb, GFP_NOIO);
 }
+
index 2e0a66575bb4af709da78f4f4071b4408a755dc6..b5a11eb8f9fa9eb6cb4ff06840770da621092928 100644 (file)
@@ -1032,7 +1032,7 @@ static int uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma)
 {
        struct video_device *vdev = video_devdata(file);
        struct uvc_video_device *video = video_get_drvdata(vdev);
-       struct uvc_buffer *buffer;
+       struct uvc_buffer *uninitialized_var(buffer);
        struct page *page;
        unsigned long addr, start, size;
        unsigned int i;
@@ -1103,3 +1103,4 @@ struct file_operations uvc_fops = {
        .mmap           = uvc_v4l2_mmap,
        .poll           = uvc_v4l2_poll,
 };
+
index 6faf1fb21614a9731fc8e5f37bb62e04df87b41c..ad63794fda7784e2380bd4e1dd9248b39a74c857 100644 (file)
@@ -553,10 +553,57 @@ static void uvc_video_complete(struct urb *urb)
        }
 }
 
+/*
+ * Free transfer buffers.
+ */
+static void uvc_free_urb_buffers(struct uvc_video_device *video)
+{
+       unsigned int i;
+
+       for (i = 0; i < UVC_URBS; ++i) {
+               if (video->urb_buffer[i]) {
+                       usb_buffer_free(video->dev->udev, video->urb_size,
+                               video->urb_buffer[i], video->urb_dma[i]);
+                       video->urb_buffer[i] = NULL;
+               }
+       }
+
+       video->urb_size = 0;
+}
+
+/*
+ * Allocate transfer buffers. This function can be called with buffers
+ * already allocated when resuming from suspend, in which case it will
+ * return without touching the buffers.
+ *
+ * Return 0 on success or -ENOMEM when out of memory.
+ */
+static int uvc_alloc_urb_buffers(struct uvc_video_device *video,
+       unsigned int size)
+{
+       unsigned int i;
+
+       /* Buffers are already allocated, bail out. */
+       if (video->urb_size)
+               return 0;
+
+       for (i = 0; i < UVC_URBS; ++i) {
+               video->urb_buffer[i] = usb_buffer_alloc(video->dev->udev,
+                       size, GFP_KERNEL, &video->urb_dma[i]);
+               if (video->urb_buffer[i] == NULL) {
+                       uvc_free_urb_buffers(video);
+                       return -ENOMEM;
+               }
+       }
+
+       video->urb_size = size;
+       return 0;
+}
+
 /*
  * Uninitialize isochronous/bulk URBs and free transfer buffers.
  */
-static void uvc_uninit_video(struct uvc_video_device *video)
+static void uvc_uninit_video(struct uvc_video_device *video, int free_buffers)
 {
        struct urb *urb;
        unsigned int i;
@@ -566,19 +613,12 @@ static void uvc_uninit_video(struct uvc_video_device *video)
                        continue;
 
                usb_kill_urb(urb);
-               /* urb->transfer_buffer_length is not touched by USB core, so
-                * we can use it here as the buffer length.
-                */
-               if (video->urb_buffer[i]) {
-                       usb_buffer_free(video->dev->udev,
-                               urb->transfer_buffer_length,
-                               video->urb_buffer[i], urb->transfer_dma);
-                       video->urb_buffer[i] = NULL;
-               }
-
                usb_free_urb(urb);
                video->urb[i] = NULL;
        }
+
+       if (free_buffers)
+               uvc_free_urb_buffers(video);
 }
 
 /*
@@ -586,7 +626,7 @@ static void uvc_uninit_video(struct uvc_video_device *video)
  * is given by the endpoint.
  */
 static int uvc_init_video_isoc(struct uvc_video_device *video,
-       struct usb_host_endpoint *ep)
+       struct usb_host_endpoint *ep, gfp_t gfp_flags)
 {
        struct urb *urb;
        unsigned int npackets, i, j;
@@ -610,18 +650,13 @@ static int uvc_init_video_isoc(struct uvc_video_device *video,
 
        size = npackets * psize;
 
+       if (uvc_alloc_urb_buffers(video, size) < 0)
+               return -ENOMEM;
+
        for (i = 0; i < UVC_URBS; ++i) {
-               urb = usb_alloc_urb(npackets, GFP_KERNEL);
+               urb = usb_alloc_urb(npackets, gfp_flags);
                if (urb == NULL) {
-                       uvc_uninit_video(video);
-                       return -ENOMEM;
-               }
-
-               video->urb_buffer[i] = usb_buffer_alloc(video->dev->udev,
-                       size, GFP_KERNEL, &urb->transfer_dma);
-               if (video->urb_buffer[i] == NULL) {
-                       usb_free_urb(urb);
-                       uvc_uninit_video(video);
+                       uvc_uninit_video(video, 1);
                        return -ENOMEM;
                }
 
@@ -632,6 +667,7 @@ static int uvc_init_video_isoc(struct uvc_video_device *video,
                urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
                urb->interval = ep->desc.bInterval;
                urb->transfer_buffer = video->urb_buffer[i];
+               urb->transfer_dma = video->urb_dma[i];
                urb->complete = uvc_video_complete;
                urb->number_of_packets = npackets;
                urb->transfer_buffer_length = size;
@@ -652,7 +688,7 @@ static int uvc_init_video_isoc(struct uvc_video_device *video,
  * given by the endpoint.
  */
 static int uvc_init_video_bulk(struct uvc_video_device *video,
-       struct usb_host_endpoint *ep)
+       struct usb_host_endpoint *ep, gfp_t gfp_flags)
 {
        struct urb *urb;
        unsigned int pipe, i;
@@ -671,20 +707,15 @@ static int uvc_init_video_bulk(struct uvc_video_device *video,
        if (size > psize * UVC_MAX_ISO_PACKETS)
                size = psize * UVC_MAX_ISO_PACKETS;
 
+       if (uvc_alloc_urb_buffers(video, size) < 0)
+               return -ENOMEM;
+
        pipe = usb_rcvbulkpipe(video->dev->udev, ep->desc.bEndpointAddress);
 
        for (i = 0; i < UVC_URBS; ++i) {
-               urb = usb_alloc_urb(0, GFP_KERNEL);
+               urb = usb_alloc_urb(0, gfp_flags);
                if (urb == NULL) {
-                       uvc_uninit_video(video);
-                       return -ENOMEM;
-               }
-
-               video->urb_buffer[i] = usb_buffer_alloc(video->dev->udev,
-                       size, GFP_KERNEL, &urb->transfer_dma);
-               if (video->urb_buffer[i] == NULL) {
-                       usb_free_urb(urb);
-                       uvc_uninit_video(video);
+                       uvc_uninit_video(video, 1);
                        return -ENOMEM;
                }
 
@@ -692,6 +723,7 @@ static int uvc_init_video_bulk(struct uvc_video_device *video,
                        video->urb_buffer[i], size, uvc_video_complete,
                        video);
                urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
+               urb->transfer_dma = video->urb_dma[i];
 
                video->urb[i] = urb;
        }
@@ -702,7 +734,7 @@ static int uvc_init_video_bulk(struct uvc_video_device *video,
 /*
  * Initialize isochronous/bulk URBs and allocate transfer buffers.
  */
-static int uvc_init_video(struct uvc_video_device *video)
+static int uvc_init_video(struct uvc_video_device *video, gfp_t gfp_flags)
 {
        struct usb_interface *intf = video->streaming->intf;
        struct usb_host_interface *alts;
@@ -747,7 +779,7 @@ static int uvc_init_video(struct uvc_video_device *video)
                if ((ret = usb_set_interface(video->dev->udev, intfnum, i)) < 0)
                        return ret;
 
-               ret = uvc_init_video_isoc(video, ep);
+               ret = uvc_init_video_isoc(video, ep, gfp_flags);
        } else {
                /* Bulk endpoint, proceed to URB initialization. */
                ep = uvc_find_endpoint(&intf->altsetting[0],
@@ -755,7 +787,7 @@ static int uvc_init_video(struct uvc_video_device *video)
                if (ep == NULL)
                        return -EIO;
 
-               ret = uvc_init_video_bulk(video, ep);
+               ret = uvc_init_video_bulk(video, ep, gfp_flags);
        }
 
        if (ret < 0)
@@ -763,10 +795,10 @@ static int uvc_init_video(struct uvc_video_device *video)
 
        /* Submit the URBs. */
        for (i = 0; i < UVC_URBS; ++i) {
-               if ((ret = usb_submit_urb(video->urb[i], GFP_KERNEL)) < 0) {
+               if ((ret = usb_submit_urb(video->urb[i], gfp_flags)) < 0) {
                        uvc_printk(KERN_ERR, "Failed to submit URB %u "
                                        "(%d).\n", i, ret);
-                       uvc_uninit_video(video);
+                       uvc_uninit_video(video, 1);
                        return ret;
                }
        }
@@ -791,7 +823,7 @@ int uvc_video_suspend(struct uvc_video_device *video)
                return 0;
 
        video->frozen = 1;
-       uvc_uninit_video(video);
+       uvc_uninit_video(video, 0);
        usb_set_interface(video->dev->udev, video->streaming->intfnum, 0);
        return 0;
 }
@@ -818,7 +850,7 @@ int uvc_video_resume(struct uvc_video_device *video)
        if (!uvc_queue_streaming(&video->queue))
                return 0;
 
-       if ((ret = uvc_init_video(video)) < 0)
+       if ((ret = uvc_init_video(video, GFP_NOIO)) < 0)
                uvc_queue_enable(&video->queue, 0);
 
        return ret;
@@ -920,7 +952,7 @@ int uvc_video_enable(struct uvc_video_device *video, int enable)
        int ret;
 
        if (!enable) {
-               uvc_uninit_video(video);
+               uvc_uninit_video(video, 1);
                usb_set_interface(video->dev->udev,
                        video->streaming->intfnum, 0);
                uvc_queue_enable(&video->queue, 0);
@@ -930,5 +962,6 @@ int uvc_video_enable(struct uvc_video_device *video, int enable)
        if ((ret = uvc_queue_enable(&video->queue, 1)) < 0)
                return ret;
 
-       return uvc_init_video(video);
+       return uvc_init_video(video, GFP_KERNEL);
 }
+
index a995a780db1c0227ed0631be3e947ca7acd2b652..bafe3406e305bd035465851bdd70bd5438b1349a 100644 (file)
@@ -602,6 +602,8 @@ struct uvc_video_device {
 
        struct urb *urb[UVC_URBS];
        char *urb_buffer[UVC_URBS];
+       dma_addr_t urb_dma[UVC_URBS];
+       unsigned int urb_size;
 
        __u8 last_fid;
 };
@@ -794,3 +796,4 @@ void uvc_video_decode_isight(struct urb *urb, struct uvc_video_device *video,
 #endif /* __KERNEL__ */
 
 #endif
+
diff --git a/drivers/media/video/videobuf-dma-contig.c b/drivers/media/video/videobuf-dma-contig.c
new file mode 100644 (file)
index 0000000..03f20ac
--- /dev/null
@@ -0,0 +1,418 @@
+/*
+ * helper functions for physically contiguous capture buffers
+ *
+ * The functions support hardware lacking scatter gather support
+ * (i.e. the buffers must be linear in physical memory)
+ *
+ * Copyright (c) 2008 Magnus Damm
+ *
+ * Based on videobuf-vmalloc.c,
+ * (c) 2007 Mauro Carvalho Chehab, <mchehab@infradead.org>
+ *
+ * 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
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/dma-mapping.h>
+#include <media/videobuf-dma-contig.h>
+
+struct videobuf_dma_contig_memory {
+       u32 magic;
+       void *vaddr;
+       dma_addr_t dma_handle;
+       unsigned long size;
+};
+
+#define MAGIC_DC_MEM 0x0733ac61
+#define MAGIC_CHECK(is, should)                                                \
+       if (unlikely((is) != (should))) {                               \
+               pr_err("magic mismatch: %x expected %x\n", is, should); \
+               BUG();                                                  \
+       }
+
+static void
+videobuf_vm_open(struct vm_area_struct *vma)
+{
+       struct videobuf_mapping *map = vma->vm_private_data;
+
+       dev_dbg(map->q->dev, "vm_open %p [count=%u,vma=%08lx-%08lx]\n",
+               map, map->count, vma->vm_start, vma->vm_end);
+
+       map->count++;
+}
+
+static void videobuf_vm_close(struct vm_area_struct *vma)
+{
+       struct videobuf_mapping *map = vma->vm_private_data;
+       struct videobuf_queue *q = map->q;
+       int i;
+
+       dev_dbg(map->q->dev, "vm_close %p [count=%u,vma=%08lx-%08lx]\n",
+               map, map->count, vma->vm_start, vma->vm_end);
+
+       map->count--;
+       if (0 == map->count) {
+               struct videobuf_dma_contig_memory *mem;
+
+               dev_dbg(map->q->dev, "munmap %p q=%p\n", map, q);
+               mutex_lock(&q->vb_lock);
+
+               /* We need first to cancel streams, before unmapping */
+               if (q->streaming)
+                       videobuf_queue_cancel(q);
+
+               for (i = 0; i < VIDEO_MAX_FRAME; i++) {
+                       if (NULL == q->bufs[i])
+                               continue;
+
+                       if (q->bufs[i]->map != map)
+                               continue;
+
+                       mem = q->bufs[i]->priv;
+                       if (mem) {
+                               /* This callback is called only if kernel has
+                                  allocated memory and this memory is mmapped.
+                                  In this case, memory should be freed,
+                                  in order to do memory unmap.
+                                */
+
+                               MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
+
+                               /* vfree is not atomic - can't be
+                                  called with IRQ's disabled
+                                */
+                               dev_dbg(map->q->dev, "buf[%d] freeing %p\n",
+                                       i, mem->vaddr);
+
+                               dma_free_coherent(q->dev, mem->size,
+                                                 mem->vaddr, mem->dma_handle);
+                               mem->vaddr = NULL;
+                       }
+
+                       q->bufs[i]->map   = NULL;
+                       q->bufs[i]->baddr = 0;
+               }
+
+               kfree(map);
+
+               mutex_unlock(&q->vb_lock);
+       }
+}
+
+static struct vm_operations_struct videobuf_vm_ops = {
+       .open     = videobuf_vm_open,
+       .close    = videobuf_vm_close,
+};
+
+static void *__videobuf_alloc(size_t size)
+{
+       struct videobuf_dma_contig_memory *mem;
+       struct videobuf_buffer *vb;
+
+       vb = kzalloc(size + sizeof(*mem), GFP_KERNEL);
+       if (vb) {
+               mem = vb->priv = ((char *)vb) + size;
+               mem->magic = MAGIC_DC_MEM;
+       }
+
+       return vb;
+}
+
+static void *__videobuf_to_vmalloc(struct videobuf_buffer *buf)
+{
+       struct videobuf_dma_contig_memory *mem = buf->priv;
+
+       BUG_ON(!mem);
+       MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
+
+       return mem->vaddr;
+}
+
+static int __videobuf_iolock(struct videobuf_queue *q,
+                            struct videobuf_buffer *vb,
+                            struct v4l2_framebuffer *fbuf)
+{
+       struct videobuf_dma_contig_memory *mem = vb->priv;
+
+       BUG_ON(!mem);
+       MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
+
+       switch (vb->memory) {
+       case V4L2_MEMORY_MMAP:
+               dev_dbg(q->dev, "%s memory method MMAP\n", __func__);
+
+               /* All handling should be done by __videobuf_mmap_mapper() */
+               if (!mem->vaddr) {
+                       dev_err(q->dev, "memory is not alloced/mmapped.\n");
+                       return -EINVAL;
+               }
+               break;
+       case V4L2_MEMORY_USERPTR:
+               dev_dbg(q->dev, "%s memory method USERPTR\n", __func__);
+
+               /* The only USERPTR currently supported is the one needed for
+                  read() method.
+                */
+               if (vb->baddr)
+                       return -EINVAL;
+
+               mem->size = PAGE_ALIGN(vb->size);
+               mem->vaddr = dma_alloc_coherent(q->dev, mem->size,
+                                               &mem->dma_handle, GFP_KERNEL);
+               if (!mem->vaddr) {
+                       dev_err(q->dev, "dma_alloc_coherent %ld failed\n",
+                                        mem->size);
+                       return -ENOMEM;
+               }
+
+               dev_dbg(q->dev, "dma_alloc_coherent data is at %p (%ld)\n",
+                       mem->vaddr, mem->size);
+               break;
+       case V4L2_MEMORY_OVERLAY:
+       default:
+               dev_dbg(q->dev, "%s memory method OVERLAY/unknown\n",
+                       __func__);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int __videobuf_sync(struct videobuf_queue *q,
+                          struct videobuf_buffer *buf)
+{
+       struct videobuf_dma_contig_memory *mem = buf->priv;
+
+       BUG_ON(!mem);
+       MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
+
+       dma_sync_single_for_cpu(q->dev, mem->dma_handle, mem->size,
+                               DMA_FROM_DEVICE);
+       return 0;
+}
+
+static int __videobuf_mmap_free(struct videobuf_queue *q)
+{
+       unsigned int i;
+
+       dev_dbg(q->dev, "%s\n", __func__);
+       for (i = 0; i < VIDEO_MAX_FRAME; i++) {
+               if (q->bufs[i] && q->bufs[i]->map)
+                       return -EBUSY;
+       }
+
+       return 0;
+}
+
+static int __videobuf_mmap_mapper(struct videobuf_queue *q,
+                                 struct vm_area_struct *vma)
+{
+       struct videobuf_dma_contig_memory *mem;
+       struct videobuf_mapping *map;
+       unsigned int first;
+       int retval;
+       unsigned long size, offset = vma->vm_pgoff << PAGE_SHIFT;
+
+       dev_dbg(q->dev, "%s\n", __func__);
+       if (!(vma->vm_flags & VM_WRITE) || !(vma->vm_flags & VM_SHARED))
+               return -EINVAL;
+
+       /* look for first buffer to map */
+       for (first = 0; first < VIDEO_MAX_FRAME; first++) {
+               if (!q->bufs[first])
+                       continue;
+
+               if (V4L2_MEMORY_MMAP != q->bufs[first]->memory)
+                       continue;
+               if (q->bufs[first]->boff == offset)
+                       break;
+       }
+       if (VIDEO_MAX_FRAME == first) {
+               dev_dbg(q->dev, "invalid user space offset [offset=0x%lx]\n",
+                       offset);
+               return -EINVAL;
+       }
+
+       /* create mapping + update buffer list */
+       map = kzalloc(sizeof(struct videobuf_mapping), GFP_KERNEL);
+       if (!map)
+               return -ENOMEM;
+
+       q->bufs[first]->map = map;
+       map->start = vma->vm_start;
+       map->end = vma->vm_end;
+       map->q = q;
+
+       q->bufs[first]->baddr = vma->vm_start;
+
+       mem = q->bufs[first]->priv;
+       BUG_ON(!mem);
+       MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
+
+       mem->size = PAGE_ALIGN(q->bufs[first]->bsize);
+       mem->vaddr = dma_alloc_coherent(q->dev, mem->size,
+                                       &mem->dma_handle, GFP_KERNEL);
+       if (!mem->vaddr) {
+               dev_err(q->dev, "dma_alloc_coherent size %ld failed\n",
+                       mem->size);
+               goto error;
+       }
+       dev_dbg(q->dev, "dma_alloc_coherent data is at addr %p (size %ld)\n",
+               mem->vaddr, mem->size);
+
+       /* Try to remap memory */
+
+       size = vma->vm_end - vma->vm_start;
+       size = (size < mem->size) ? size : mem->size;
+
+       vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+       retval = remap_pfn_range(vma, vma->vm_start,
+                                mem->dma_handle >> PAGE_SHIFT,
+                                size, vma->vm_page_prot);
+       if (retval) {
+               dev_err(q->dev, "mmap: remap failed with error %d. ", retval);
+               dma_free_coherent(q->dev, mem->size,
+                                 mem->vaddr, mem->dma_handle);
+               goto error;
+       }
+
+       vma->vm_ops          = &videobuf_vm_ops;
+       vma->vm_flags       |= VM_DONTEXPAND;
+       vma->vm_private_data = map;
+
+       dev_dbg(q->dev, "mmap %p: q=%p %08lx-%08lx (%lx) pgoff %08lx buf %d\n",
+               map, q, vma->vm_start, vma->vm_end,
+               (long int) q->bufs[first]->bsize,
+               vma->vm_pgoff, first);
+
+       videobuf_vm_open(vma);
+
+       return 0;
+
+error:
+       kfree(map);
+       return -ENOMEM;
+}
+
+static int __videobuf_copy_to_user(struct videobuf_queue *q,
+                                  char __user *data, size_t count,
+                                  int nonblocking)
+{
+       struct videobuf_dma_contig_memory *mem = q->read_buf->priv;
+       void *vaddr;
+
+       BUG_ON(!mem);
+       MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
+       BUG_ON(!mem->vaddr);
+
+       /* copy to userspace */
+       if (count > q->read_buf->size - q->read_off)
+               count = q->read_buf->size - q->read_off;
+
+       vaddr = mem->vaddr;
+
+       if (copy_to_user(data, vaddr + q->read_off, count))
+               return -EFAULT;
+
+       return count;
+}
+
+static int __videobuf_copy_stream(struct videobuf_queue *q,
+                                 char __user *data, size_t count, size_t pos,
+                                 int vbihack, int nonblocking)
+{
+       unsigned int  *fc;
+       struct videobuf_dma_contig_memory *mem = q->read_buf->priv;
+
+       BUG_ON(!mem);
+       MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
+
+       if (vbihack) {
+               /* dirty, undocumented hack -- pass the frame counter
+                       * within the last four bytes of each vbi data block.
+                       * We need that one to maintain backward compatibility
+                       * to all vbi decoding software out there ... */
+               fc = (unsigned int *)mem->vaddr;
+               fc += (q->read_buf->size >> 2) - 1;
+               *fc = q->read_buf->field_count >> 1;
+               dev_dbg(q->dev, "vbihack: %d\n", *fc);
+       }
+
+       /* copy stuff using the common method */
+       count = __videobuf_copy_to_user(q, data, count, nonblocking);
+
+       if ((count == -EFAULT) && (pos == 0))
+               return -EFAULT;
+
+       return count;
+}
+
+static struct videobuf_qtype_ops qops = {
+       .magic        = MAGIC_QTYPE_OPS,
+
+       .alloc        = __videobuf_alloc,
+       .iolock       = __videobuf_iolock,
+       .sync         = __videobuf_sync,
+       .mmap_free    = __videobuf_mmap_free,
+       .mmap_mapper  = __videobuf_mmap_mapper,
+       .video_copy_to_user = __videobuf_copy_to_user,
+       .copy_stream  = __videobuf_copy_stream,
+       .vmalloc      = __videobuf_to_vmalloc,
+};
+
+void videobuf_queue_dma_contig_init(struct videobuf_queue *q,
+                                   struct videobuf_queue_ops *ops,
+                                   struct device *dev,
+                                   spinlock_t *irqlock,
+                                   enum v4l2_buf_type type,
+                                   enum v4l2_field field,
+                                   unsigned int msize,
+                                   void *priv)
+{
+       videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize,
+                                priv, &qops);
+}
+EXPORT_SYMBOL_GPL(videobuf_queue_dma_contig_init);
+
+dma_addr_t videobuf_to_dma_contig(struct videobuf_buffer *buf)
+{
+       struct videobuf_dma_contig_memory *mem = buf->priv;
+
+       BUG_ON(!mem);
+       MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
+
+       return mem->dma_handle;
+}
+EXPORT_SYMBOL_GPL(videobuf_to_dma_contig);
+
+void videobuf_dma_contig_free(struct videobuf_queue *q,
+                             struct videobuf_buffer *buf)
+{
+       struct videobuf_dma_contig_memory *mem = buf->priv;
+
+       /* mmapped memory can't be freed here, otherwise mmapped region
+          would be released, while still needed. In this case, the memory
+          release should happen inside videobuf_vm_close().
+          So, it should free memory only if the memory were allocated for
+          read() operation.
+        */
+       if ((buf->memory != V4L2_MEMORY_USERPTR) || !buf->baddr)
+               return;
+
+       if (!mem)
+               return;
+
+       MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
+
+       dma_free_coherent(q->dev, mem->size, mem->vaddr, mem->dma_handle);
+       mem->vaddr = NULL;
+}
+EXPORT_SYMBOL_GPL(videobuf_dma_contig_free);
+
+MODULE_DESCRIPTION("helper module to manage video4linux dma contig buffers");
+MODULE_AUTHOR("Magnus Damm");
+MODULE_LICENSE("GPL");
index 03a7b946bd5448b4b5e471836fd8469bdfc492c8..bc6d5aba0fe676089491687ec4d2f9f52f80c045 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * helper functions for SG DMA video4linux capture buffers
  *
- * The functions expect the hardware being able to scatter gatter
+ * The functions expect the hardware being able to scatter gather
  * (i.e. the buffers are not linear in physical memory, but fragmented
  * into PAGE_SIZE chunks).  They also assume the driver does not need
  * to touch the video data.
@@ -80,17 +80,15 @@ struct scatterlist*
 videobuf_pages_to_sg(struct page **pages, int nr_pages, int offset)
 {
        struct scatterlist *sglist;
-       int i = 0;
+       int i;
 
        if (NULL == pages[0])
                return NULL;
-       sglist = kcalloc(nr_pages, sizeof(*sglist), GFP_KERNEL);
+       sglist = kmalloc(nr_pages * sizeof(*sglist), GFP_KERNEL);
        if (NULL == sglist)
                return NULL;
        sg_init_table(sglist, nr_pages);
 
-       if (NULL == pages[0])
-               goto nopage;
        if (PageHighMem(pages[0]))
                /* DMA to highmem pages might not work */
                goto highmem;
index 6e4d73ec6855f274684df7e4b80cea0d4b9b0bd4..b56cffcbfd458152829b0973ade0f6646380aee8 100644 (file)
@@ -13,7 +13,6 @@
  * (at your option) any later version.
  */
 
-
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/device.h>
@@ -257,4 +256,3 @@ EXPORT_SYMBOL(videobuf_dvb_unregister);
  * compile-command: "make DVB=1"
  * End:
  */
-
index c91e1d8e38025d32c292d888cac3feeb20103d6d..a868b7ed75ff2cc60f09181c1b1812dd44888c56 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * helper functions for vmalloc video4linux capture buffers
  *
- * The functions expect the hardware being able to scatter gatter
+ * The functions expect the hardware being able to scatter gather
  * (i.e. the buffers are not linear in physical memory, but fragmented
  * into PAGE_SIZE chunks).  They also assume the driver does not need
  * to touch the video data.
index 7649860a388d0da519dbbb4d0def8238e3b80ac6..6616e6570557b84960d89afc2753e7bf4bf053f0 100644 (file)
  */
 
 #define dbgarg(cmd, fmt, arg...) \
-               if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) {                \
+               do {                                                    \
+                   if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) {            \
                        printk(KERN_DEBUG "%s: ",  vfd->name);          \
                        v4l_printk_ioctl(cmd);                          \
                        printk(" " fmt,  ## arg);                       \
-               }
+                   }                                                   \
+               } while (0)
 
 #define dbgarg2(fmt, arg...) \
-               if (vfd->debug & V4L2_DEBUG_IOCTL_ARG)                  \
-                       printk(KERN_DEBUG "%s: " fmt, vfd->name, ## arg);
+               do {                                                    \
+                   if (vfd->debug & V4L2_DEBUG_IOCTL_ARG)              \
+                       printk(KERN_DEBUG "%s: " fmt, vfd->name, ## arg);\
+               } while (0)
 
 #include <linux/module.h>
 #include <linux/types.h>
@@ -138,7 +142,7 @@ EXPORT_SYMBOL(v4l2_video_std_construct);
 /* ----------------------------------------------------------------- */
 /* some arrays for pretty-printing debug messages of enum types      */
 
-char *v4l2_field_names[] = {
+const char *v4l2_field_names[] = {
        [V4L2_FIELD_ANY]        = "any",
        [V4L2_FIELD_NONE]       = "none",
        [V4L2_FIELD_TOP]        = "top",
@@ -152,19 +156,19 @@ char *v4l2_field_names[] = {
 };
 EXPORT_SYMBOL(v4l2_field_names);
 
-char *v4l2_type_names[] = {
-       [V4L2_BUF_TYPE_VIDEO_CAPTURE]      = "video-cap",
-       [V4L2_BUF_TYPE_VIDEO_OVERLAY]      = "video-over",
-       [V4L2_BUF_TYPE_VIDEO_OUTPUT]       = "video-out",
+const char *v4l2_type_names[] = {
+       [V4L2_BUF_TYPE_VIDEO_CAPTURE]      = "vid-cap",
+       [V4L2_BUF_TYPE_VIDEO_OVERLAY]      = "vid-overlay",
+       [V4L2_BUF_TYPE_VIDEO_OUTPUT]       = "vid-out",
        [V4L2_BUF_TYPE_VBI_CAPTURE]        = "vbi-cap",
        [V4L2_BUF_TYPE_VBI_OUTPUT]         = "vbi-out",
        [V4L2_BUF_TYPE_SLICED_VBI_CAPTURE] = "sliced-vbi-cap",
        [V4L2_BUF_TYPE_SLICED_VBI_OUTPUT]  = "sliced-vbi-out",
-       [V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY] = "video-out-over",
+       [V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY] = "vid-out-overlay",
 };
 EXPORT_SYMBOL(v4l2_type_names);
 
-static char *v4l2_memory_names[] = {
+static const char *v4l2_memory_names[] = {
        [V4L2_MEMORY_MMAP]    = "mmap",
        [V4L2_MEMORY_USERPTR] = "userptr",
        [V4L2_MEMORY_OVERLAY] = "overlay",
@@ -278,6 +282,7 @@ static const char *v4l2_ioctls[] = {
        [_IOC_NR(VIDIOC_DBG_G_REGISTER)]   = "VIDIOC_DBG_G_REGISTER",
 
        [_IOC_NR(VIDIOC_G_CHIP_IDENT)]     = "VIDIOC_G_CHIP_IDENT",
+       [_IOC_NR(VIDIOC_S_HW_FREQ_SEEK)]   = "VIDIOC_S_HW_FREQ_SEEK",
 #endif
 };
 #define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls)
@@ -371,6 +376,14 @@ EXPORT_SYMBOL(v4l_printk_ioctl);
  *     sysfs stuff
  */
 
+static ssize_t show_index(struct device *cd,
+                        struct device_attribute *attr, char *buf)
+{
+       struct video_device *vfd = container_of(cd, struct video_device,
+                                               class_dev);
+       return sprintf(buf, "%i\n", vfd->index);
+}
+
 static ssize_t show_name(struct device *cd,
                         struct device_attribute *attr, char *buf)
 {
@@ -379,6 +392,12 @@ static ssize_t show_name(struct device *cd,
        return sprintf(buf, "%.*s\n", (int)sizeof(vfd->name), vfd->name);
 }
 
+static struct device_attribute video_device_attrs[] = {
+       __ATTR(name, S_IRUGO, show_name, NULL),
+       __ATTR(index, S_IRUGO, show_index, NULL),
+       __ATTR_NULL
+};
+
 struct video_device *video_device_alloc(void)
 {
        struct video_device *vfd;
@@ -407,11 +426,6 @@ static void video_release(struct device *cd)
        vfd->release(vfd);
 }
 
-static struct device_attribute video_device_attrs[] = {
-       __ATTR(name, S_IRUGO, show_name, NULL),
-       __ATTR_NULL
-};
-
 static struct class video_class = {
        .name    = VIDEO_NAME,
        .dev_attrs = video_device_attrs,
@@ -650,7 +664,7 @@ static void dbgbuf(unsigned int cmd, struct video_device *vfd,
                        p->field, p->sequence,
                        prt_names(p->memory, v4l2_memory_names),
                        p->m.userptr, p->length);
-       dbgarg2 ("timecode= %02d:%02d:%02d type=%d, "
+       dbgarg2("timecode=%02d:%02d:%02d type=%d, "
                "flags=0x%08d, frames=%d, userbits=0x%08x\n",
                        tc->hours,tc->minutes,tc->seconds,
                        tc->type, tc->flags, tc->frames, *(__u32 *) tc->userbits);
@@ -659,7 +673,7 @@ static void dbgbuf(unsigned int cmd, struct video_device *vfd,
 static inline void dbgrect(struct video_device *vfd, char *s,
                                                        struct v4l2_rect *r)
 {
-       dbgarg2 ("%sRect start at %dx%d, size= %dx%d\n", s, r->left, r->top,
+       dbgarg2("%sRect start at %dx%d, size=%dx%d\n", s, r->left, r->top,
                                                r->width, r->height);
 };
 
@@ -677,40 +691,85 @@ static inline void v4l_print_pix_fmt (struct video_device *vfd,
                fmt->bytesperline, fmt->sizeimage, fmt->colorspace);
 };
 
+static inline void v4l_print_ext_ctrls(unsigned int cmd,
+       struct video_device *vfd, struct v4l2_ext_controls *c, int show_vals)
+{
+       __u32 i;
+
+       if (!(vfd->debug & V4L2_DEBUG_IOCTL_ARG))
+               return;
+       dbgarg(cmd, "");
+       printk(KERN_CONT "class=0x%x", c->ctrl_class);
+       for (i = 0; i < c->count; i++) {
+               if (show_vals)
+                       printk(KERN_CONT " id/val=0x%x/0x%x",
+                               c->controls[i].id, c->controls[i].value);
+               else
+                       printk(KERN_CONT " id=0x%x", c->controls[i].id);
+       }
+       printk(KERN_CONT "\n");
+};
+
+static inline int check_ext_ctrls(struct v4l2_ext_controls *c, int allow_priv)
+{
+       __u32 i;
+
+       /* zero the reserved fields */
+       c->reserved[0] = c->reserved[1] = 0;
+       for (i = 0; i < c->count; i++) {
+               c->controls[i].reserved2[0] = 0;
+               c->controls[i].reserved2[1] = 0;
+       }
+       /* V4L2_CID_PRIVATE_BASE cannot be used as control class
+          when using extended controls.
+          Only when passed in through VIDIOC_G_CTRL and VIDIOC_S_CTRL
+          is it allowed for backwards compatibility.
+        */
+       if (!allow_priv && c->ctrl_class == V4L2_CID_PRIVATE_BASE)
+               return 0;
+       /* Check that all controls are from the same control class. */
+       for (i = 0; i < c->count; i++) {
+               if (V4L2_CTRL_ID2CLASS(c->controls[i].id) != c->ctrl_class) {
+                       c->error_idx = i;
+                       return 0;
+               }
+       }
+       return 1;
+}
 
 static int check_fmt (struct video_device *vfd, enum v4l2_buf_type type)
 {
        switch (type) {
        case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-               if (vfd->vidioc_try_fmt_cap)
+               if (vfd->vidioc_try_fmt_vid_cap)
                        return (0);
                break;
        case V4L2_BUF_TYPE_VIDEO_OVERLAY:
-               if (vfd->vidioc_try_fmt_overlay)
+               if (vfd->vidioc_try_fmt_vid_overlay)
                        return (0);
                break;
-       case V4L2_BUF_TYPE_VBI_CAPTURE:
-               if (vfd->vidioc_try_fmt_vbi)
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+               if (vfd->vidioc_try_fmt_vid_out)
                        return (0);
                break;
-       case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
-               if (vfd->vidioc_try_fmt_vbi_output)
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
+               if (vfd->vidioc_try_fmt_vid_out_overlay)
                        return (0);
                break;
-       case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
-               if (vfd->vidioc_try_fmt_vbi_capture)
+       case V4L2_BUF_TYPE_VBI_CAPTURE:
+               if (vfd->vidioc_try_fmt_vbi_cap)
                        return (0);
                break;
-       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
-               if (vfd->vidioc_try_fmt_video_output)
+       case V4L2_BUF_TYPE_VBI_OUTPUT:
+               if (vfd->vidioc_try_fmt_vbi_out)
                        return (0);
                break;
-       case V4L2_BUF_TYPE_VBI_OUTPUT:
-               if (vfd->vidioc_try_fmt_vbi_output)
+       case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+               if (vfd->vidioc_try_fmt_sliced_vbi_cap)
                        return (0);
                break;
-       case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
-               if (vfd->vidioc_try_fmt_output_overlay)
+       case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+               if (vfd->vidioc_try_fmt_sliced_vbi_out)
                        return (0);
                break;
        case V4L2_BUF_TYPE_PRIVATE:
@@ -827,46 +886,37 @@ static int __video_do_ioctl(struct inode *inode, struct file *file,
 
                switch (type) {
                case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-                       if (vfd->vidioc_enum_fmt_cap)
-                               ret=vfd->vidioc_enum_fmt_cap(file, fh, f);
+                       if (vfd->vidioc_enum_fmt_vid_cap)
+                               ret = vfd->vidioc_enum_fmt_vid_cap(file, fh, f);
                        break;
                case V4L2_BUF_TYPE_VIDEO_OVERLAY:
-                       if (vfd->vidioc_enum_fmt_overlay)
-                               ret=vfd->vidioc_enum_fmt_overlay(file, fh, f);
+                       if (vfd->vidioc_enum_fmt_vid_overlay)
+                               ret = vfd->vidioc_enum_fmt_vid_overlay(file,
+                                       fh, f);
                        break;
+#if 1
+               /* V4L2_BUF_TYPE_VBI_CAPTURE should not support VIDIOC_ENUM_FMT
+                * according to the spec. The bttv and saa7134 drivers support
+                * it though, so just warn that this is deprecated and will be
+                * removed in the near future. */
                case V4L2_BUF_TYPE_VBI_CAPTURE:
-                       if (vfd->vidioc_enum_fmt_vbi)
-                               ret=vfd->vidioc_enum_fmt_vbi(file, fh, f);
-                       break;
-               case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
-                       if (vfd->vidioc_enum_fmt_vbi_output)
-                               ret=vfd->vidioc_enum_fmt_vbi_output(file,
-                                                               fh, f);
-                       break;
-               case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
-                       if (vfd->vidioc_enum_fmt_vbi_capture)
-                               ret=vfd->vidioc_enum_fmt_vbi_capture(file,
-                                                               fh, f);
+                       if (vfd->vidioc_enum_fmt_vbi_cap) {
+                               printk(KERN_WARNING "vidioc_enum_fmt_vbi_cap will be removed in 2.6.28!\n");
+                               ret = vfd->vidioc_enum_fmt_vbi_cap(file, fh, f);
+                       }
                        break;
+#endif
                case V4L2_BUF_TYPE_VIDEO_OUTPUT:
-                       if (vfd->vidioc_enum_fmt_video_output)
-                               ret=vfd->vidioc_enum_fmt_video_output(file,
-                                                               fh, f);
-                       break;
-               case V4L2_BUF_TYPE_VBI_OUTPUT:
-                       if (vfd->vidioc_enum_fmt_vbi_output)
-                               ret=vfd->vidioc_enum_fmt_vbi_output(file,
-                                                               fh, f);
-                       break;
-               case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
-                       if (vfd->vidioc_enum_fmt_output_overlay)
-                               ret=vfd->vidioc_enum_fmt_output_overlay(file, fh, f);
+                       if (vfd->vidioc_enum_fmt_vid_out)
+                               ret = vfd->vidioc_enum_fmt_vid_out(file, fh, f);
                        break;
                case V4L2_BUF_TYPE_PRIVATE:
                        if (vfd->vidioc_enum_fmt_type_private)
-                               ret=vfd->vidioc_enum_fmt_type_private(file,
+                               ret = vfd->vidioc_enum_fmt_type_private(file,
                                                                fh, f);
                        break;
+               default:
+                       break;
                }
                if (!ret)
                        dbgarg (cmd, "index=%d, type=%d, flags=%d, "
@@ -882,54 +932,56 @@ static int __video_do_ioctl(struct inode *inode, struct file *file,
        case VIDIOC_G_FMT:
        {
                struct v4l2_format *f = (struct v4l2_format *)arg;
-               enum v4l2_buf_type type=f->type;
 
-               memset(&f->fmt.pix,0,sizeof(f->fmt.pix));
-               f->type=type;
+               memset(f->fmt.raw_data, 0, sizeof(f->fmt.raw_data));
 
                /* FIXME: Should be one dump per type */
-               dbgarg (cmd, "type=%s\n", prt_names(type,
-                                       v4l2_type_names));
+               dbgarg(cmd, "type=%s\n", prt_names(f->type, v4l2_type_names));
 
-               switch (type) {
+               switch (f->type) {
                case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-                       if (vfd->vidioc_g_fmt_cap)
-                               ret=vfd->vidioc_g_fmt_cap(file, fh, f);
+                       if (vfd->vidioc_g_fmt_vid_cap)
+                               ret = vfd->vidioc_g_fmt_vid_cap(file, fh, f);
                        if (!ret)
-                               v4l_print_pix_fmt(vfd,&f->fmt.pix);
+                               v4l_print_pix_fmt(vfd, &f->fmt.pix);
                        break;
                case V4L2_BUF_TYPE_VIDEO_OVERLAY:
-                       if (vfd->vidioc_g_fmt_overlay)
-                               ret=vfd->vidioc_g_fmt_overlay(file, fh, f);
-                       break;
-               case V4L2_BUF_TYPE_VBI_CAPTURE:
-                       if (vfd->vidioc_g_fmt_vbi)
-                               ret=vfd->vidioc_g_fmt_vbi(file, fh, f);
-                       break;
-               case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
-                       if (vfd->vidioc_g_fmt_vbi_output)
-                               ret=vfd->vidioc_g_fmt_vbi_output(file, fh, f);
-                       break;
-               case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
-                       if (vfd->vidioc_g_fmt_vbi_capture)
-                               ret=vfd->vidioc_g_fmt_vbi_capture(file, fh, f);
+                       if (vfd->vidioc_g_fmt_vid_overlay)
+                               ret = vfd->vidioc_g_fmt_vid_overlay(file,
+                                                                   fh, f);
                        break;
                case V4L2_BUF_TYPE_VIDEO_OUTPUT:
-                       if (vfd->vidioc_g_fmt_video_output)
-                               ret=vfd->vidioc_g_fmt_video_output(file,
-                                                               fh, f);
+                       if (vfd->vidioc_g_fmt_vid_out)
+                               ret = vfd->vidioc_g_fmt_vid_out(file, fh, f);
+                       if (!ret)
+                               v4l_print_pix_fmt(vfd, &f->fmt.pix);
                        break;
                case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
-                       if (vfd->vidioc_g_fmt_output_overlay)
-                               ret=vfd->vidioc_g_fmt_output_overlay(file, fh, f);
+                       if (vfd->vidioc_g_fmt_vid_out_overlay)
+                               ret = vfd->vidioc_g_fmt_vid_out_overlay(file,
+                                      fh, f);
+                       break;
+               case V4L2_BUF_TYPE_VBI_CAPTURE:
+                       if (vfd->vidioc_g_fmt_vbi_cap)
+                               ret = vfd->vidioc_g_fmt_vbi_cap(file, fh, f);
                        break;
                case V4L2_BUF_TYPE_VBI_OUTPUT:
-                       if (vfd->vidioc_g_fmt_vbi_output)
-                               ret=vfd->vidioc_g_fmt_vbi_output(file, fh, f);
+                       if (vfd->vidioc_g_fmt_vbi_out)
+                               ret = vfd->vidioc_g_fmt_vbi_out(file, fh, f);
+                       break;
+               case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+                       if (vfd->vidioc_g_fmt_sliced_vbi_cap)
+                               ret = vfd->vidioc_g_fmt_sliced_vbi_cap(file,
+                                                                       fh, f);
+                       break;
+               case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+                       if (vfd->vidioc_g_fmt_sliced_vbi_out)
+                               ret = vfd->vidioc_g_fmt_sliced_vbi_out(file,
+                                                                       fh, f);
                        break;
                case V4L2_BUF_TYPE_PRIVATE:
                        if (vfd->vidioc_g_fmt_type_private)
-                               ret=vfd->vidioc_g_fmt_type_private(file,
+                               ret = vfd->vidioc_g_fmt_type_private(file,
                                                                fh, f);
                        break;
                }
@@ -941,48 +993,50 @@ static int __video_do_ioctl(struct inode *inode, struct file *file,
                struct v4l2_format *f = (struct v4l2_format *)arg;
 
                /* FIXME: Should be one dump per type */
-               dbgarg (cmd, "type=%s\n", prt_names(f->type,
-                                       v4l2_type_names));
+               dbgarg(cmd, "type=%s\n", prt_names(f->type, v4l2_type_names));
 
                switch (f->type) {
                case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-                       v4l_print_pix_fmt(vfd,&f->fmt.pix);
-                       if (vfd->vidioc_s_fmt_cap)
-                               ret=vfd->vidioc_s_fmt_cap(file, fh, f);
+                       v4l_print_pix_fmt(vfd, &f->fmt.pix);
+                       if (vfd->vidioc_s_fmt_vid_cap)
+                               ret = vfd->vidioc_s_fmt_vid_cap(file, fh, f);
                        break;
                case V4L2_BUF_TYPE_VIDEO_OVERLAY:
-                       if (vfd->vidioc_s_fmt_overlay)
-                               ret=vfd->vidioc_s_fmt_overlay(file, fh, f);
-                       break;
-               case V4L2_BUF_TYPE_VBI_CAPTURE:
-                       if (vfd->vidioc_s_fmt_vbi)
-                               ret=vfd->vidioc_s_fmt_vbi(file, fh, f);
-                       break;
-               case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
-                       if (vfd->vidioc_s_fmt_vbi_output)
-                               ret=vfd->vidioc_s_fmt_vbi_output(file, fh, f);
-                       break;
-               case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
-                       if (vfd->vidioc_s_fmt_vbi_capture)
-                               ret=vfd->vidioc_s_fmt_vbi_capture(file, fh, f);
+                       if (vfd->vidioc_s_fmt_vid_overlay)
+                               ret = vfd->vidioc_s_fmt_vid_overlay(file,
+                                                                   fh, f);
                        break;
                case V4L2_BUF_TYPE_VIDEO_OUTPUT:
-                       if (vfd->vidioc_s_fmt_video_output)
-                               ret=vfd->vidioc_s_fmt_video_output(file,
-                                                               fh, f);
+                       v4l_print_pix_fmt(vfd, &f->fmt.pix);
+                       if (vfd->vidioc_s_fmt_vid_out)
+                               ret = vfd->vidioc_s_fmt_vid_out(file, fh, f);
                        break;
                case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
-                       if (vfd->vidioc_s_fmt_output_overlay)
-                               ret=vfd->vidioc_s_fmt_output_overlay(file, fh, f);
+                       if (vfd->vidioc_s_fmt_vid_out_overlay)
+                               ret = vfd->vidioc_s_fmt_vid_out_overlay(file,
+                                       fh, f);
+                       break;
+               case V4L2_BUF_TYPE_VBI_CAPTURE:
+                       if (vfd->vidioc_s_fmt_vbi_cap)
+                               ret = vfd->vidioc_s_fmt_vbi_cap(file, fh, f);
                        break;
                case V4L2_BUF_TYPE_VBI_OUTPUT:
-                       if (vfd->vidioc_s_fmt_vbi_output)
-                               ret=vfd->vidioc_s_fmt_vbi_output(file,
-                                                               fh, f);
+                       if (vfd->vidioc_s_fmt_vbi_out)
+                               ret = vfd->vidioc_s_fmt_vbi_out(file, fh, f);
+                       break;
+               case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+                       if (vfd->vidioc_s_fmt_sliced_vbi_cap)
+                               ret = vfd->vidioc_s_fmt_sliced_vbi_cap(file,
+                                                                       fh, f);
+                       break;
+               case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+                       if (vfd->vidioc_s_fmt_sliced_vbi_out)
+                               ret = vfd->vidioc_s_fmt_sliced_vbi_out(file,
+                                                                       fh, f);
                        break;
                case V4L2_BUF_TYPE_PRIVATE:
                        if (vfd->vidioc_s_fmt_type_private)
-                               ret=vfd->vidioc_s_fmt_type_private(file,
+                               ret = vfd->vidioc_s_fmt_type_private(file,
                                                                fh, f);
                        break;
                }
@@ -997,46 +1051,48 @@ static int __video_do_ioctl(struct inode *inode, struct file *file,
                                                v4l2_type_names));
                switch (f->type) {
                case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-                       if (vfd->vidioc_try_fmt_cap)
-                               ret=vfd->vidioc_try_fmt_cap(file, fh, f);
+                       if (vfd->vidioc_try_fmt_vid_cap)
+                               ret = vfd->vidioc_try_fmt_vid_cap(file, fh, f);
                        if (!ret)
-                               v4l_print_pix_fmt(vfd,&f->fmt.pix);
+                               v4l_print_pix_fmt(vfd, &f->fmt.pix);
                        break;
                case V4L2_BUF_TYPE_VIDEO_OVERLAY:
-                       if (vfd->vidioc_try_fmt_overlay)
-                               ret=vfd->vidioc_try_fmt_overlay(file, fh, f);
+                       if (vfd->vidioc_try_fmt_vid_overlay)
+                               ret = vfd->vidioc_try_fmt_vid_overlay(file,
+                                       fh, f);
+                       break;
+               case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+                       if (vfd->vidioc_try_fmt_vid_out)
+                               ret = vfd->vidioc_try_fmt_vid_out(file, fh, f);
+                       if (!ret)
+                               v4l_print_pix_fmt(vfd, &f->fmt.pix);
+                       break;
+               case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
+                       if (vfd->vidioc_try_fmt_vid_out_overlay)
+                               ret = vfd->vidioc_try_fmt_vid_out_overlay(file,
+                                      fh, f);
                        break;
                case V4L2_BUF_TYPE_VBI_CAPTURE:
-                       if (vfd->vidioc_try_fmt_vbi)
-                               ret=vfd->vidioc_try_fmt_vbi(file, fh, f);
+                       if (vfd->vidioc_try_fmt_vbi_cap)
+                               ret = vfd->vidioc_try_fmt_vbi_cap(file, fh, f);
                        break;
-               case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
-                       if (vfd->vidioc_try_fmt_vbi_output)
-                               ret=vfd->vidioc_try_fmt_vbi_output(file,
-                                                               fh, f);
+               case V4L2_BUF_TYPE_VBI_OUTPUT:
+                       if (vfd->vidioc_try_fmt_vbi_out)
+                               ret = vfd->vidioc_try_fmt_vbi_out(file, fh, f);
                        break;
                case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
-                       if (vfd->vidioc_try_fmt_vbi_capture)
-                               ret=vfd->vidioc_try_fmt_vbi_capture(file,
-                                                               fh, f);
-                       break;
-               case V4L2_BUF_TYPE_VIDEO_OUTPUT:
-                       if (vfd->vidioc_try_fmt_video_output)
-                               ret=vfd->vidioc_try_fmt_video_output(file,
+                       if (vfd->vidioc_try_fmt_sliced_vbi_cap)
+                               ret = vfd->vidioc_try_fmt_sliced_vbi_cap(file,
                                                                fh, f);
                        break;
-               case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
-                       if (vfd->vidioc_try_fmt_output_overlay)
-                               ret=vfd->vidioc_try_fmt_output_overlay(file, fh, f);
-                       break;
-               case V4L2_BUF_TYPE_VBI_OUTPUT:
-                       if (vfd->vidioc_try_fmt_vbi_output)
-                               ret=vfd->vidioc_try_fmt_vbi_output(file,
+               case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+                       if (vfd->vidioc_try_fmt_sliced_vbi_out)
+                               ret = vfd->vidioc_try_fmt_sliced_vbi_out(file,
                                                                fh, f);
                        break;
                case V4L2_BUF_TYPE_PRIVATE:
                        if (vfd->vidioc_try_fmt_type_private)
-                               ret=vfd->vidioc_try_fmt_type_private(file,
+                               ret = vfd->vidioc_try_fmt_type_private(file,
                                                                fh, f);
                        break;
                }
@@ -1120,29 +1176,29 @@ static int __video_do_ioctl(struct inode *inode, struct file *file,
        }
        case VIDIOC_G_FBUF:
        {
-               struct v4l2_framebuffer *p=arg;
+               struct v4l2_framebuffer *p = arg;
+
                if (!vfd->vidioc_g_fbuf)
                        break;
-               ret=vfd->vidioc_g_fbuf(file, fh, arg);
+               ret = vfd->vidioc_g_fbuf(file, fh, arg);
                if (!ret) {
-                       dbgarg (cmd, "capability=%d, flags=%d, base=0x%08lx\n",
-                                       p->capability,p->flags,
+                       dbgarg(cmd, "capability=0x%x, flags=%d, base=0x%08lx\n",
+                                       p->capability, p->flags,
                                        (unsigned long)p->base);
-                       v4l_print_pix_fmt (vfd, &p->fmt);
+                       v4l_print_pix_fmt(vfd, &p->fmt);
                }
                break;
        }
        case VIDIOC_S_FBUF:
        {
-               struct v4l2_framebuffer *p=arg;
+               struct v4l2_framebuffer *p = arg;
+
                if (!vfd->vidioc_s_fbuf)
                        break;
-
-               dbgarg (cmd, "capability=%d, flags=%d, base=0x%08lx\n",
-                               p->capability,p->flags,(unsigned long)p->base);
-               v4l_print_pix_fmt (vfd, &p->fmt);
-               ret=vfd->vidioc_s_fbuf(file, fh, arg);
-
+               dbgarg(cmd, "capability=0x%x, flags=%d, base=0x%08lx\n",
+                       p->capability, p->flags, (unsigned long)p->base);
+               v4l_print_pix_fmt(vfd, &p->fmt);
+               ret = vfd->vidioc_s_fbuf(file, fh, arg);
                break;
        }
        case VIDIOC_STREAMON:
@@ -1194,7 +1250,7 @@ static int __video_do_ioctl(struct inode *inode, struct file *file,
                v4l2_video_std_construct(p, curr_id, descr);
                p->index = index;
 
-               dbgarg(cmd, "index=%d, id=%Ld, name=%s, fps=%d/%d, "
+               dbgarg(cmd, "index=%d, id=0x%Lx, name=%s, fps=%d/%d, "
                                "framelines=%d\n", p->index,
                                (unsigned long long)p->id, p->name,
                                p->frameperiod.numerator,
@@ -1208,18 +1264,22 @@ static int __video_do_ioctl(struct inode *inode, struct file *file,
        {
                v4l2_std_id *id = arg;
 
-               *id = vfd->current_norm;
-
-               dbgarg (cmd, "value=%08Lx\n", (long long unsigned) *id);
+               ret = 0;
+               /* Calls the specific handler */
+               if (vfd->vidioc_g_std)
+                       ret = vfd->vidioc_g_std(file, fh, id);
+               else
+                       *id = vfd->current_norm;
 
-               ret=0;
+               if (!ret)
+                       dbgarg(cmd, "std=0x%08Lx\n", (long long unsigned)*id);
                break;
        }
        case VIDIOC_S_STD:
        {
                v4l2_std_id *id = arg,norm;
 
-               dbgarg (cmd, "value=%08Lx\n", (long long unsigned) *id);
+               dbgarg(cmd, "std=%08Lx\n", (long long unsigned)*id);
 
                norm = (*id) & vfd->tvnorms;
                if ( vfd->tvnorms && !norm)     /* Check if std is supported */
@@ -1295,6 +1355,25 @@ static int __video_do_ioctl(struct inode *inode, struct file *file,
        }
 
        /* ------ output switching ---------- */
+       case VIDIOC_ENUMOUTPUT:
+       {
+               struct v4l2_output *p = arg;
+               int i = p->index;
+
+               if (!vfd->vidioc_enum_output)
+                       break;
+               memset(p, 0, sizeof(*p));
+               p->index = i;
+
+               ret = vfd->vidioc_enum_output(file, fh, p);
+               if (!ret)
+                       dbgarg(cmd, "index=%d, name=%s, type=%d, "
+                               "audioset=0x%x, "
+                               "modulator=%d, std=0x%08Lx\n",
+                               p->index, p->name, p->type, p->audioset,
+                               p->modulator, (unsigned long long)p->std);
+               break;
+       }
        case VIDIOC_G_OUTPUT:
        {
                unsigned int *i = arg;
@@ -1320,132 +1399,172 @@ static int __video_do_ioctl(struct inode *inode, struct file *file,
        /* --- controls ---------------------------------------------- */
        case VIDIOC_QUERYCTRL:
        {
-               struct v4l2_queryctrl *p=arg;
+               struct v4l2_queryctrl *p = arg;
 
                if (!vfd->vidioc_queryctrl)
                        break;
-               ret=vfd->vidioc_queryctrl(file, fh, p);
-
+               ret = vfd->vidioc_queryctrl(file, fh, p);
                if (!ret)
-                       dbgarg (cmd, "id=%d, type=%d, name=%s, "
-                                       "min/max=%d/%d,"
-                                       " step=%d, default=%d, flags=0x%08x\n",
-                                       p->id,p->type,p->name,p->minimum,
-                                       p->maximum,p->step,p->default_value,
-                                       p->flags);
+                       dbgarg(cmd, "id=0x%x, type=%d, name=%s, min/max=%d/%d, "
+                                       "step=%d, default=%d, flags=0x%08x\n",
+                                       p->id, p->type, p->name,
+                                       p->minimum, p->maximum,
+                                       p->step, p->default_value, p->flags);
+               else
+                       dbgarg(cmd, "id=0x%x\n", p->id);
                break;
        }
        case VIDIOC_G_CTRL:
        {
                struct v4l2_control *p = arg;
 
-               if (!vfd->vidioc_g_ctrl)
+               if (vfd->vidioc_g_ctrl)
+                       ret = vfd->vidioc_g_ctrl(file, fh, p);
+               else if (vfd->vidioc_g_ext_ctrls) {
+                       struct v4l2_ext_controls ctrls;
+                       struct v4l2_ext_control ctrl;
+
+                       ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(p->id);
+                       ctrls.count = 1;
+                       ctrls.controls = &ctrl;
+                       ctrl.id = p->id;
+                       ctrl.value = p->value;
+                       if (check_ext_ctrls(&ctrls, 1)) {
+                               ret = vfd->vidioc_g_ext_ctrls(file, fh, &ctrls);
+                               if (ret == 0)
+                                       p->value = ctrl.value;
+                       }
+               } else
                        break;
-               dbgarg(cmd, "Enum for index=%d\n", p->id);
-
-               ret=vfd->vidioc_g_ctrl(file, fh, p);
                if (!ret)
-                       dbgarg2 ( "id=%d, value=%d\n", p->id, p->value);
+                       dbgarg(cmd, "id=0x%x, value=%d\n", p->id, p->value);
+               else
+                       dbgarg(cmd, "id=0x%x\n", p->id);
                break;
        }
        case VIDIOC_S_CTRL:
        {
                struct v4l2_control *p = arg;
+               struct v4l2_ext_controls ctrls;
+               struct v4l2_ext_control ctrl;
+
+               if (!vfd->vidioc_s_ctrl && !vfd->vidioc_s_ext_ctrls)
+                       break;
 
-               if (!vfd->vidioc_s_ctrl)
+               dbgarg(cmd, "id=0x%x, value=%d\n", p->id, p->value);
+
+               if (vfd->vidioc_s_ctrl) {
+                       ret = vfd->vidioc_s_ctrl(file, fh, p);
+                       break;
+               }
+               if (!vfd->vidioc_s_ext_ctrls)
                        break;
-               dbgarg (cmd, "id=%d, value=%d\n", p->id, p->value);
 
-               ret=vfd->vidioc_s_ctrl(file, fh, p);
+               ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(p->id);
+               ctrls.count = 1;
+               ctrls.controls = &ctrl;
+               ctrl.id = p->id;
+               ctrl.value = p->value;
+               if (check_ext_ctrls(&ctrls, 1))
+                       ret = vfd->vidioc_s_ext_ctrls(file, fh, &ctrls);
                break;
        }
        case VIDIOC_G_EXT_CTRLS:
        {
                struct v4l2_ext_controls *p = arg;
 
-               if (vfd->vidioc_g_ext_ctrls) {
-                       dbgarg(cmd, "count=%d\n", p->count);
-
-                       ret=vfd->vidioc_g_ext_ctrls(file, fh, p);
-               }
+               p->error_idx = p->count;
+               if (!vfd->vidioc_g_ext_ctrls)
+                       break;
+               if (check_ext_ctrls(p, 0))
+                       ret = vfd->vidioc_g_ext_ctrls(file, fh, p);
+               v4l_print_ext_ctrls(cmd, vfd, p, !ret);
                break;
        }
        case VIDIOC_S_EXT_CTRLS:
        {
                struct v4l2_ext_controls *p = arg;
 
-               if (vfd->vidioc_s_ext_ctrls) {
-                       dbgarg(cmd, "count=%d\n", p->count);
-
-                       ret=vfd->vidioc_s_ext_ctrls(file, fh, p);
-               }
+               p->error_idx = p->count;
+               if (!vfd->vidioc_s_ext_ctrls)
+                       break;
+               v4l_print_ext_ctrls(cmd, vfd, p, 1);
+               if (check_ext_ctrls(p, 0))
+                       ret = vfd->vidioc_s_ext_ctrls(file, fh, p);
                break;
        }
        case VIDIOC_TRY_EXT_CTRLS:
        {
                struct v4l2_ext_controls *p = arg;
 
-               if (vfd->vidioc_try_ext_ctrls) {
-                       dbgarg(cmd, "count=%d\n", p->count);
-
-                       ret=vfd->vidioc_try_ext_ctrls(file, fh, p);
-               }
+               p->error_idx = p->count;
+               if (!vfd->vidioc_try_ext_ctrls)
+                       break;
+               v4l_print_ext_ctrls(cmd, vfd, p, 1);
+               if (check_ext_ctrls(p, 0))
+                       ret = vfd->vidioc_try_ext_ctrls(file, fh, p);
                break;
        }
        case VIDIOC_QUERYMENU:
        {
-               struct v4l2_querymenu *p=arg;
+               struct v4l2_querymenu *p = arg;
+
                if (!vfd->vidioc_querymenu)
                        break;
-               ret=vfd->vidioc_querymenu(file, fh, p);
+               ret = vfd->vidioc_querymenu(file, fh, p);
                if (!ret)
-                       dbgarg (cmd, "id=%d, index=%d, name=%s\n",
-                                               p->id,p->index,p->name);
+                       dbgarg(cmd, "id=0x%x, index=%d, name=%s\n",
+                               p->id, p->index, p->name);
+               else
+                       dbgarg(cmd, "id=0x%x, index=%d\n",
+                               p->id, p->index);
                break;
        }
        /* --- audio ---------------------------------------------- */
        case VIDIOC_ENUMAUDIO:
        {
-               struct v4l2_audio *p=arg;
+               struct v4l2_audio *p = arg;
 
                if (!vfd->vidioc_enumaudio)
                        break;
-               dbgarg(cmd, "Enum for index=%d\n", p->index);
-               ret=vfd->vidioc_enumaudio(file, fh, p);
+               ret = vfd->vidioc_enumaudio(file, fh, p);
                if (!ret)
-                       dbgarg2("index=%d, name=%s, capability=%d, "
-                                       "mode=%d\n",p->index,p->name,
+                       dbgarg(cmd, "index=%d, name=%s, capability=0x%x, "
+                                       "mode=0x%x\n", p->index, p->name,
                                        p->capability, p->mode);
+               else
+                       dbgarg(cmd, "index=%d\n", p->index);
                break;
        }
        case VIDIOC_G_AUDIO:
        {
-               struct v4l2_audio *p=arg;
-               __u32 index=p->index;
+               struct v4l2_audio *p = arg;
+               __u32 index = p->index;
 
                if (!vfd->vidioc_g_audio)
                        break;
 
-               memset(p,0,sizeof(*p));
-               p->index=index;
-               dbgarg(cmd, "Get for index=%d\n", p->index);
-               ret=vfd->vidioc_g_audio(file, fh, p);
+               memset(p, 0, sizeof(*p));
+               p->index = index;
+               ret = vfd->vidioc_g_audio(file, fh, p);
                if (!ret)
-                       dbgarg2("index=%d, name=%s, capability=%d, "
-                                       "mode=%d\n",p->index,
-                                       p->name,p->capability, p->mode);
+                       dbgarg(cmd, "index=%d, name=%s, capability=0x%x, "
+                                       "mode=0x%x\n", p->index,
+                                       p->name, p->capability, p->mode);
+               else
+                       dbgarg(cmd, "index=%d\n", p->index);
                break;
        }
        case VIDIOC_S_AUDIO:
        {
-               struct v4l2_audio *p=arg;
+               struct v4l2_audio *p = arg;
 
                if (!vfd->vidioc_s_audio)
                        break;
-               dbgarg(cmd, "index=%d, name=%s, capability=%d, "
-                                       "mode=%d\n", p->index, p->name,
+               dbgarg(cmd, "index=%d, name=%s, capability=0x%x, "
+                                       "mode=0x%x\n", p->index, p->name,
                                        p->capability, p->mode);
-               ret=vfd->vidioc_s_audio(file, fh, p);
+               ret = vfd->vidioc_s_audio(file, fh, p);
                break;
        }
        case VIDIOC_ENUMAUDOUT:
@@ -1521,9 +1640,9 @@ static int __video_do_ioctl(struct inode *inode, struct file *file,
                struct v4l2_crop *p=arg;
                if (!vfd->vidioc_g_crop)
                        break;
+               dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names));
                ret=vfd->vidioc_g_crop(file, fh, p);
                if (!ret) {
-                       dbgarg(cmd, "type=%d\n", p->type);
                        dbgrect(vfd, "", &p->c);
                }
                break;
@@ -1533,21 +1652,24 @@ static int __video_do_ioctl(struct inode *inode, struct file *file,
                struct v4l2_crop *p=arg;
                if (!vfd->vidioc_s_crop)
                        break;
-               dbgarg(cmd, "type=%d\n", p->type);
+               dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names));
                dbgrect(vfd, "", &p->c);
                ret=vfd->vidioc_s_crop(file, fh, p);
                break;
        }
        case VIDIOC_CROPCAP:
        {
-               struct v4l2_cropcap *p=arg;
+               struct v4l2_cropcap *p = arg;
+
                /*FIXME: Should also show v4l2_fract pixelaspect */
                if (!vfd->vidioc_cropcap)
                        break;
-               dbgarg(cmd, "type=%d\n", p->type);
-               dbgrect(vfd, "bounds ", &p->bounds);
-               dbgrect(vfd, "defrect ", &p->defrect);
-               ret=vfd->vidioc_cropcap(file, fh, p);
+               dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names));
+               ret = vfd->vidioc_cropcap(file, fh, p);
+               if (!ret) {
+                       dbgrect(vfd, "bounds ", &p->bounds);
+                       dbgrect(vfd, "defrect ", &p->defrect);
+               }
                break;
        }
        case VIDIOC_G_JPEGCOMP:
@@ -1590,26 +1712,26 @@ static int __video_do_ioctl(struct inode *inode, struct file *file,
        }
        case VIDIOC_ENCODER_CMD:
        {
-               struct v4l2_encoder_cmd *p=arg;
+               struct v4l2_encoder_cmd *p = arg;
 
                if (!vfd->vidioc_encoder_cmd)
                        break;
-               ret=vfd->vidioc_encoder_cmd(file, fh, p);
+               memset(&p->raw, 0, sizeof(p->raw));
+               ret = vfd->vidioc_encoder_cmd(file, fh, p);
                if (!ret)
-                       dbgarg (cmd, "cmd=%d, flags=%d\n",
-                                       p->cmd,p->flags);
+                       dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags);
                break;
        }
        case VIDIOC_TRY_ENCODER_CMD:
        {
-               struct v4l2_encoder_cmd *p=arg;
+               struct v4l2_encoder_cmd *p = arg;
 
                if (!vfd->vidioc_try_encoder_cmd)
                        break;
-               ret=vfd->vidioc_try_encoder_cmd(file, fh, p);
+               memset(&p->raw, 0, sizeof(p->raw));
+               ret = vfd->vidioc_try_encoder_cmd(file, fh, p);
                if (!ret)
-                       dbgarg (cmd, "cmd=%d, flags=%d\n",
-                                       p->cmd,p->flags);
+                       dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags);
                break;
        }
        case VIDIOC_G_PARM:
@@ -1649,54 +1771,57 @@ static int __video_do_ioctl(struct inode *inode, struct file *file,
        }
        case VIDIOC_G_TUNER:
        {
-               struct v4l2_tuner *p=arg;
-               __u32 index=p->index;
+               struct v4l2_tuner *p = arg;
+               __u32 index = p->index;
 
                if (!vfd->vidioc_g_tuner)
                        break;
 
-               memset(p,0,sizeof(*p));
-               p->index=index;
+               memset(p, 0, sizeof(*p));
+               p->index = index;
 
-               ret=vfd->vidioc_g_tuner(file, fh, p);
+               ret = vfd->vidioc_g_tuner(file, fh, p);
                if (!ret)
-                       dbgarg (cmd, "index=%d, name=%s, type=%d, "
-                                       "capability=%d, rangelow=%d, "
+                       dbgarg(cmd, "index=%d, name=%s, type=%d, "
+                                       "capability=0x%x, rangelow=%d, "
                                        "rangehigh=%d, signal=%d, afc=%d, "
-                                       "rxsubchans=%d, audmode=%d\n",
+                                       "rxsubchans=0x%x, audmode=%d\n",
                                        p->index, p->name, p->type,
                                        p->capability, p->rangelow,
-                                       p->rangehigh, p->rxsubchans,
-                                       p->audmode, p->signal, p->afc);
+                                       p->rangehigh, p->signal, p->afc,
+                                       p->rxsubchans, p->audmode);
                break;
        }
        case VIDIOC_S_TUNER:
        {
-               struct v4l2_tuner *p=arg;
+               struct v4l2_tuner *p = arg;
+
                if (!vfd->vidioc_s_tuner)
                        break;
-               dbgarg (cmd, "index=%d, name=%s, type=%d, "
-                               "capability=%d, rangelow=%d, rangehigh=%d, "
-                               "signal=%d, afc=%d, rxsubchans=%d, "
-                               "audmode=%d\n",p->index, p->name, p->type,
-                               p->capability, p->rangelow,p->rangehigh,
-                               p->rxsubchans, p->audmode, p->signal,
-                               p->afc);
-               ret=vfd->vidioc_s_tuner(file, fh, p);
+               dbgarg(cmd, "index=%d, name=%s, type=%d, "
+                               "capability=0x%x, rangelow=%d, "
+                               "rangehigh=%d, signal=%d, afc=%d, "
+                               "rxsubchans=0x%x, audmode=%d\n",
+                               p->index, p->name, p->type,
+                               p->capability, p->rangelow,
+                               p->rangehigh, p->signal, p->afc,
+                               p->rxsubchans, p->audmode);
+               ret = vfd->vidioc_s_tuner(file, fh, p);
                break;
        }
        case VIDIOC_G_FREQUENCY:
        {
-               struct v4l2_frequency *p=arg;
+               struct v4l2_frequency *p = arg;
+
                if (!vfd->vidioc_g_frequency)
                        break;
 
-               memset(p,0,sizeof(*p));
+               memset(p->reserved, 0, sizeof(p->reserved));
 
-               ret=vfd->vidioc_g_frequency(file, fh, p);
+               ret = vfd->vidioc_g_frequency(file, fh, p);
                if (!ret)
-                       dbgarg (cmd, "tuner=%d, type=%d, frequency=%d\n",
-                                               p->tuner,p->type,p->frequency);
+                       dbgarg(cmd, "tuner=%d, type=%d, frequency=%d\n",
+                                       p->tuner, p->type, p->frequency);
                break;
        }
        case VIDIOC_S_FREQUENCY:
@@ -1711,12 +1836,17 @@ static int __video_do_ioctl(struct inode *inode, struct file *file,
        }
        case VIDIOC_G_SLICED_VBI_CAP:
        {
-               struct v4l2_sliced_vbi_cap *p=arg;
+               struct v4l2_sliced_vbi_cap *p = arg;
+               __u32 type = p->type;
+
                if (!vfd->vidioc_g_sliced_vbi_cap)
                        break;
-               ret=vfd->vidioc_g_sliced_vbi_cap(file, fh, p);
+               memset(p, 0, sizeof(*p));
+               p->type = type;
+               dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names));
+               ret = vfd->vidioc_g_sliced_vbi_cap(file, fh, p);
                if (!ret)
-                       dbgarg (cmd, "service_set=%d\n", p->service_set);
+                       dbgarg2("service_set=%d\n", p->service_set);
                break;
        }
        case VIDIOC_LOG_STATUS:
@@ -1763,13 +1893,23 @@ static int __video_do_ioctl(struct inode *inode, struct file *file,
                ret = vfd->vidioc_default(file, fh, cmd, arg);
                break;
        }
+       case VIDIOC_S_HW_FREQ_SEEK:
+       {
+               struct v4l2_hw_freq_seek *p = arg;
+               if (!vfd->vidioc_s_hw_freq_seek)
+                       break;
+               dbgarg(cmd,
+                       "tuner=%d, type=%d, seek_upward=%d, wrap_around=%d\n",
+                       p->tuner, p->type, p->seek_upward, p->wrap_around);
+               ret = vfd->vidioc_s_hw_freq_seek(file, fh, p);
+               break;
+       }
        } /* switch */
 
        if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) {
-               if (ret<0) {
-                       printk("%s: err: on ", vfd->name);
+               if (ret < 0) {
                        v4l_print_ioctl(vfd->name, cmd);
-                       printk("\n");
+                       printk(KERN_CONT " error %d\n", ret);
                }
        }
 
@@ -1871,8 +2011,55 @@ out:
 }
 EXPORT_SYMBOL(video_ioctl2);
 
+/**
+ * get_index - assign stream number based on parent device
+ * @vdev: video_device to assign index number to, vdev->dev should be assigned
+ * @num: -1 if auto assign, requested number otherwise
+ *
+ *
+ * returns -ENFILE if num is already in use, a free index number if
+ * successful.
+ */
+static int get_index(struct video_device *vdev, int num)
+{
+       u32 used = 0;
+       const int max_index = sizeof(used) * 8 - 1;
+       int i;
+
+       /* Currently a single v4l driver instance cannot create more than
+          32 devices.
+          Increase to u64 or an array of u32 if more are needed. */
+       if (num > max_index) {
+               printk(KERN_ERR "videodev: %s num is too large\n", __func__);
+               return -EINVAL;
+       }
+
+       for (i = 0; i < VIDEO_NUM_DEVICES; i++) {
+               if (video_device[i] != NULL &&
+                   video_device[i] != vdev &&
+                   video_device[i]->dev == vdev->dev) {
+                       used |= 1 << video_device[i]->index;
+               }
+       }
+
+       if (num >= 0) {
+               if (used & (1 << num))
+                       return -ENFILE;
+               return num;
+       }
+
+       i = ffz(used);
+       return i > max_index ? -ENFILE : i;
+}
+
 static const struct file_operations video_fops;
 
+int video_register_device(struct video_device *vfd, int type, int nr)
+{
+       return video_register_device_index(vfd, type, nr, -1);
+}
+EXPORT_SYMBOL(video_register_device);
+
 /**
  *     video_register_device - register video4linux devices
  *     @vfd:  video device structure we want to register
@@ -1898,7 +2085,8 @@ static const struct file_operations video_fops;
  *     %VFL_TYPE_RADIO - A radio card
  */
 
-int video_register_device(struct video_device *vfd, int type, int nr)
+int video_register_device_index(struct video_device *vfd, int type, int nr,
+                                       int index)
 {
        int i=0;
        int base;
@@ -1955,20 +2143,29 @@ int video_register_device(struct video_device *vfd, int type, int nr)
        }
        video_device[i]=vfd;
        vfd->minor=i;
+
+       ret = get_index(vfd, index);
+       vfd->index = ret;
+
        mutex_unlock(&videodev_lock);
+
+       if (ret < 0) {
+               printk(KERN_ERR "%s: get_index failed\n", __func__);
+               goto fail_minor;
+       }
+
        mutex_init(&vfd->lock);
 
        /* sysfs class */
        memset(&vfd->class_dev, 0x00, sizeof(vfd->class_dev));
-       if (vfd->dev)
-               vfd->class_dev.parent = vfd->dev;
        vfd->class_dev.class       = &video_class;
        vfd->class_dev.devt        = MKDEV(VIDEO_MAJOR, vfd->minor);
+       if (vfd->dev)
+               vfd->class_dev.parent = vfd->dev;
        sprintf(vfd->class_dev.bus_id, "%s%d", name_base, i - base);
        ret = device_register(&vfd->class_dev);
        if (ret < 0) {
-               printk(KERN_ERR "%s: device_register failed\n",
-                      __func__);
+               printk(KERN_ERR "%s: device_register failed\n", __func__);
                goto fail_minor;
        }
 
@@ -1988,7 +2185,7 @@ fail_minor:
        mutex_unlock(&videodev_lock);
        return ret;
 }
-EXPORT_SYMBOL(video_register_device);
+EXPORT_SYMBOL(video_register_device_index);
 
 /**
  *     video_unregister_device - unregister a video4linux device
index 5ff9a58b6135b98b8644af14d042cc31cc824a5a..059b01c11dc133e4abb708ace787fa9cc3242843 100644 (file)
@@ -39,6 +39,8 @@
 #include <linux/highmem.h>
 #include <linux/freezer.h>
 
+#define VIVI_MODULE_NAME "vivi"
+
 /* Wake up at about 30 fps */
 #define WAKE_NUMERATOR 30
 #define WAKE_DENOMINATOR 1001
@@ -47,7 +49,7 @@
 #include "font.h"
 
 #define VIVI_MAJOR_VERSION 0
-#define VIVI_MINOR_VERSION 4
+#define VIVI_MINOR_VERSION 5
 #define VIVI_RELEASE 0
 #define VIVI_VERSION \
        KERNEL_VERSION(VIVI_MAJOR_VERSION, VIVI_MINOR_VERSION, VIVI_RELEASE)
@@ -630,7 +632,7 @@ static int vidioc_querycap(struct file *file, void  *priv,
        return 0;
 }
 
-static int vidioc_enum_fmt_cap(struct file *file, void  *priv,
+static int vidioc_enum_fmt_vid_cap(struct file *file, void  *priv,
                                        struct v4l2_fmtdesc *f)
 {
        if (f->index > 0)
@@ -641,7 +643,7 @@ static int vidioc_enum_fmt_cap(struct file *file, void  *priv,
        return 0;
 }
 
-static int vidioc_g_fmt_cap(struct file *file, void *priv,
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
                                        struct v4l2_format *f)
 {
        struct vivi_fh *fh = priv;
@@ -658,7 +660,7 @@ static int vidioc_g_fmt_cap(struct file *file, void *priv,
        return (0);
 }
 
-static int vidioc_try_fmt_cap(struct file *file, void *priv,
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
                        struct v4l2_format *f)
 {
        struct vivi_fh  *fh  = priv;
@@ -706,13 +708,13 @@ static int vidioc_try_fmt_cap(struct file *file, void *priv,
 }
 
 /*FIXME: This seems to be generic enough to be at videodev2 */
-static int vidioc_s_fmt_cap(struct file *file, void *priv,
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
                                        struct v4l2_format *f)
 {
        struct vivi_fh  *fh = priv;
        struct videobuf_queue *q = &fh->vb_vidq;
 
-       int ret = vidioc_try_fmt_cap(file, fh, f);
+       int ret = vidioc_try_fmt_vid_cap(file, fh, f);
        if (ret < 0)
                return (ret);
 
@@ -1017,10 +1019,15 @@ static int vivi_release(void)
                list_del(list);
                dev = list_entry(list, struct vivi_dev, vivi_devlist);
 
-               if (-1 != dev->vfd->minor)
+               if (-1 != dev->vfd->minor) {
                        video_unregister_device(dev->vfd);
-               else
+                       printk(KERN_INFO "%s: /dev/video%d unregistered.\n",
+                               VIVI_MODULE_NAME, dev->vfd->minor);
+               } else {
                        video_device_release(dev->vfd);
+                       printk(KERN_INFO "%s: /dev/video%d released.\n",
+                               VIVI_MODULE_NAME, dev->vfd->minor);
+               }
 
                kfree(dev);
        }
@@ -1066,10 +1073,10 @@ static struct video_device vivi_template = {
        .release        = video_device_release,
 
        .vidioc_querycap      = vidioc_querycap,
-       .vidioc_enum_fmt_cap  = vidioc_enum_fmt_cap,
-       .vidioc_g_fmt_cap     = vidioc_g_fmt_cap,
-       .vidioc_try_fmt_cap   = vidioc_try_fmt_cap,
-       .vidioc_s_fmt_cap     = vidioc_s_fmt_cap,
+       .vidioc_enum_fmt_vid_cap  = vidioc_enum_fmt_vid_cap,
+       .vidioc_g_fmt_vid_cap     = vidioc_g_fmt_vid_cap,
+       .vidioc_try_fmt_vid_cap   = vidioc_try_fmt_vid_cap,
+       .vidioc_s_fmt_vid_cap     = vidioc_s_fmt_vid_cap,
        .vidioc_reqbufs       = vidioc_reqbufs,
        .vidioc_querybuf      = vidioc_querybuf,
        .vidioc_qbuf          = vidioc_qbuf,
@@ -1131,6 +1138,8 @@ static int __init vivi_init(void)
                        video_nr++;
 
                dev->vfd = vfd;
+               printk(KERN_INFO "%s: V4L2 device registered as /dev/video%d\n",
+                       VIVI_MODULE_NAME, vfd->minor);
        }
 
        if (ret < 0) {
@@ -1138,7 +1147,9 @@ static int __init vivi_init(void)
                printk(KERN_INFO "Error %d while loading vivi driver\n", ret);
        } else
                printk(KERN_INFO "Video Technology Magazine Virtual Video "
-                                "Capture Board successfully loaded.\n");
+                       "Capture Board ver %u.%u.%u successfully loaded.\n",
+                       (VIVI_VERSION >> 16) & 0xFF, (VIVI_VERSION >> 8) & 0xFF,
+                       VIVI_VERSION & 0xFF);
        return ret;
 }
 
index a1f76ee032e71c65725d39651235bc3a7de7c5aa..cbecb3cbbbaa83b9a6e1f5ddfe9283427f9af830 100644 (file)
@@ -166,4 +166,3 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = {
        .remove = vp27smpx_remove,
        .id_table = vp27smpx_id,
 };
-
index fc50299caa36f8f1715160a2499392efe77a2551..7be47a255853e13c1bedcb1439ba91844697ab9a 100644 (file)
@@ -327,4 +327,3 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = {
        .remove = wm8739_remove,
        .id_table = wm8739_id,
 };
-
index 506378a508b906e270f636be6748517c3bf8b875..c2ab70a04a740455c3da62ae421a6e6e3ee9db56 100644 (file)
@@ -42,7 +42,6 @@ MODULE_LICENSE("GPL");
 
 static unsigned short normal_i2c[] = { 0x36 >> 1, I2C_CLIENT_END };
 
-
 I2C_CLIENT_INSMOD;
 
 
@@ -230,4 +229,3 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = {
        .remove = wm8775_remove,
        .id_table = wm8775_id,
 };
-
index 006d48847e24a90396c0cfdaf29ad83554746a34..0929edb2d4f182a3a79ddf051e97692912db2267 100644 (file)
@@ -59,8 +59,6 @@
 #include "zoran_device.h"
 #include "zoran_procfs.h"
 
-#define I2C_NAME(x) (x)->name
-
 extern const struct zoran_format zoran_formats[];
 
 static int card[BUZ_MAX] = { -1, -1, -1, -1 };
@@ -360,14 +358,6 @@ i2cid_to_modulename (u16 i2c_id)
        case I2C_DRIVERID_VPX3220:
                name = "vpx3220";
                break;
-/*     case I2C_DRIVERID_VPX3224:
-               name = "vpx3224";
-               break;
-       case I2C_DRIVERID_MSE3000:
-               name = "mse3000";
-               break;*/
-       default:
-               break;
        }
 
        return name;
@@ -388,8 +378,6 @@ codecid_to_modulename (u16 codecid)
        case CODEC_TYPE_ZR36016:
                name = "zr36016";
                break;
-       default:
-               break;
        }
 
        return name;
@@ -430,7 +418,6 @@ static struct card_info zoran_cards[NUM_CARDS] __devinitdata = {
                .type = DC10_old,
                .name = "DC10(old)",
                .i2c_decoder = I2C_DRIVERID_VPX3220,
-               /*.i2c_encoder = I2C_DRIVERID_MSE3000,*/
                .video_codec = CODEC_TYPE_ZR36050,
                .video_vfe = CODEC_TYPE_ZR36016,
 
@@ -809,7 +796,7 @@ clientunreg_unlock_and_return:
        return res;
 }
 
-static struct i2c_algo_bit_data zoran_i2c_bit_data_template = {
+static const struct i2c_algo_bit_data zoran_i2c_bit_data_template = {
        .setsda = zoran_i2c_setsda,
        .setscl = zoran_i2c_setscl,
        .getsda = zoran_i2c_getsda,
@@ -818,24 +805,17 @@ static struct i2c_algo_bit_data zoran_i2c_bit_data_template = {
        .timeout = 100,
 };
 
-static struct i2c_adapter zoran_i2c_adapter_template = {
-       .name = "zr36057",
-       .id = I2C_HW_B_ZR36067,
-       .algo = NULL,
-       .client_register = zoran_i2c_client_register,
-       .client_unregister = zoran_i2c_client_unregister,
-};
-
 static int
 zoran_register_i2c (struct zoran *zr)
 {
        memcpy(&zr->i2c_algo, &zoran_i2c_bit_data_template,
               sizeof(struct i2c_algo_bit_data));
        zr->i2c_algo.data = zr;
-       memcpy(&zr->i2c_adapter, &zoran_i2c_adapter_template,
-              sizeof(struct i2c_adapter));
-       strncpy(I2C_NAME(&zr->i2c_adapter), ZR_DEVNAME(zr),
-               sizeof(I2C_NAME(&zr->i2c_adapter)) - 1);
+       zr->i2c_adapter.id = I2C_HW_B_ZR36067;
+       zr->i2c_adapter.client_register = zoran_i2c_client_register;
+       zr->i2c_adapter.client_unregister = zoran_i2c_client_unregister;
+       strlcpy(zr->i2c_adapter.name, ZR_DEVNAME(zr),
+               sizeof(zr->i2c_adapter.name));
        i2c_set_adapdata(&zr->i2c_adapter, zr);
        zr->i2c_adapter.algo_data = &zr->i2c_algo;
        zr->i2c_adapter.dev.parent = &zr->pci_dev->dev;
@@ -1147,7 +1127,7 @@ zr36057_init (struct zoran *zr)
                goto exit_free;
        }
        for (j = 0; j < BUZ_NUM_STAT_COM; j++) {
-               zr->stat_com[j] = 1;    /* mark as unavailable to zr36057 */
+               zr->stat_com[j] = cpu_to_le32(1); /* mark as unavailable to zr36057 */
        }
 
        /*
index 5394d7a5cfee686b34457f6289378193e648908a..c0675921fe20465ff80f2d72881264d8a43f07f6 100644 (file)
@@ -94,7 +94,6 @@
                                V4L2_CAP_VIDEO_OVERLAY \
                              )
 
-#include <asm/byteorder.h>
 
 #if defined(CONFIG_VIDEO_V4L1_COMPAT)
 #define ZFMT(pal, fcc, cs) \
@@ -2795,7 +2794,7 @@ zoran_do_ioctl (struct inode *inode,
        {
                struct v4l2_format *fmt = arg;
                int i, res = 0;
-               __u32 printformat;
+               __le32 printformat;
 
                dprintk(3, KERN_DEBUG "%s: VIDIOC_S_FMT - type=%d, ",
                        ZR_DEVNAME(zr), fmt->type);
@@ -3040,7 +3039,7 @@ zoran_do_ioctl (struct inode *inode,
        {
                int i, res = 0;
                struct v4l2_framebuffer *fb = arg;
-               __u32 printformat = __cpu_to_le32(fb->fmt.pixelformat);
+               __le32 printformat = __cpu_to_le32(fb->fmt.pixelformat);
 
                dprintk(3,
                        KERN_DEBUG
index a0e49dc66301f06af6130441c9cc1e0fac6b50b2..485df2e3613286292c842769efd8116f87c84b15 100644 (file)
@@ -521,7 +521,7 @@ static int zr364xx_vidioc_g_ctrl(struct file *file, void *priv,
        return 0;
 }
 
-static int zr364xx_vidioc_enum_fmt_cap(struct file *file,
+static int zr364xx_vidioc_enum_fmt_vid_cap(struct file *file,
                                       void *priv, struct v4l2_fmtdesc *f)
 {
        if (f->index > 0)
@@ -537,7 +537,7 @@ static int zr364xx_vidioc_enum_fmt_cap(struct file *file,
        return 0;
 }
 
-static int zr364xx_vidioc_try_fmt_cap(struct file *file, void *priv,
+static int zr364xx_vidioc_try_fmt_vid_cap(struct file *file, void *priv,
                                      struct v4l2_format *f)
 {
        struct video_device *vdev = video_devdata(file);
@@ -564,7 +564,7 @@ static int zr364xx_vidioc_try_fmt_cap(struct file *file, void *priv,
        return 0;
 }
 
-static int zr364xx_vidioc_g_fmt_cap(struct file *file, void *priv,
+static int zr364xx_vidioc_g_fmt_vid_cap(struct file *file, void *priv,
                                    struct v4l2_format *f)
 {
        struct video_device *vdev = video_devdata(file);
@@ -589,7 +589,7 @@ static int zr364xx_vidioc_g_fmt_cap(struct file *file, void *priv,
        return 0;
 }
 
-static int zr364xx_vidioc_s_fmt_cap(struct file *file, void *priv,
+static int zr364xx_vidioc_s_fmt_vid_cap(struct file *file, void *priv,
                                    struct v4l2_format *f)
 {
        struct video_device *vdev = video_devdata(file);
@@ -770,10 +770,10 @@ static struct video_device zr364xx_template = {
        .minor = -1,
 
        .vidioc_querycap        = zr364xx_vidioc_querycap,
-       .vidioc_enum_fmt_cap    = zr364xx_vidioc_enum_fmt_cap,
-       .vidioc_try_fmt_cap     = zr364xx_vidioc_try_fmt_cap,
-       .vidioc_s_fmt_cap       = zr364xx_vidioc_s_fmt_cap,
-       .vidioc_g_fmt_cap       = zr364xx_vidioc_g_fmt_cap,
+       .vidioc_enum_fmt_vid_cap = zr364xx_vidioc_enum_fmt_vid_cap,
+       .vidioc_try_fmt_vid_cap = zr364xx_vidioc_try_fmt_vid_cap,
+       .vidioc_s_fmt_vid_cap   = zr364xx_vidioc_s_fmt_vid_cap,
+       .vidioc_g_fmt_vid_cap   = zr364xx_vidioc_g_fmt_vid_cap,
        .vidioc_enum_input      = zr364xx_vidioc_enum_input,
        .vidioc_g_input         = zr364xx_vidioc_g_input,
        .vidioc_s_input         = zr364xx_vidioc_s_input,
index ef13b7c66df32bd5fdbf2c5aa2ee1e0d8fb17a2e..4862398e05bf033e3ee7721715fe9b398cd9a98d 100644 (file)
 
 #define I2C_DRIVERID_MSP3400    1
 #define I2C_DRIVERID_TUNER      2
-#define I2C_DRIVERID_TDA8425    4      /* stereo sound processor       */
 #define I2C_DRIVERID_TEA6420    5      /* audio matrix switch          */
 #define I2C_DRIVERID_TEA6415C   6      /* video matrix switch          */
 #define I2C_DRIVERID_TDA9840    7      /* stereo sound processor       */
 #define I2C_DRIVERID_SAA7111A   8      /* video input processor        */
 #define I2C_DRIVERID_SAA7185B  13      /* video encoder                */
-#define I2C_DRIVERID_TEA6300   18      /* audio mixer                  */
-#define I2C_DRIVERID_TDA9850   20      /* audio mixer                  */
-#define I2C_DRIVERID_TDA9855   21      /* audio mixer                  */
 #define I2C_DRIVERID_SAA7110   22      /* video decoder                */
 #define I2C_DRIVERID_MGATVO    23      /* Matrox TVOut                 */
 #define I2C_DRIVERID_SAA5249   24      /* SAA5249 and compatibles      */
@@ -50,9 +46,7 @@
 #define I2C_DRIVERID_TDA7432   27      /* Stereo sound processor       */
 #define I2C_DRIVERID_TVMIXER    28      /* Mixer driver for tv cards    */
 #define I2C_DRIVERID_TVAUDIO    29      /* Generic TV sound driver      */
-#define I2C_DRIVERID_TDA9873    31      /* TV sound decoder chip        */
 #define I2C_DRIVERID_TDA9875    32      /* TV sound decoder chip        */
-#define I2C_DRIVERID_PIC16C54_PV9 33    /* Audio mux/ir receiver        */
 #define I2C_DRIVERID_BT819     40     /* video decoder                 */
 #define I2C_DRIVERID_BT856     41     /* video encoder                 */
 #define I2C_DRIVERID_VPX3220   42     /* video decoder+vbi/vtxt        */
@@ -63,7 +57,6 @@
 #define I2C_DRIVERID_INDYCAM   58      /* SGI IndyCam                  */
 #define I2C_DRIVERID_OVCAMCHIP 61      /* OmniVision CMOS image sens.  */
 #define I2C_DRIVERID_MAX6900   63      /* MAX6900 real-time clock      */
-#define I2C_DRIVERID_TDA9874   66      /* TV sound decoder             */
 #define I2C_DRIVERID_SAA6752HS 67      /* MPEG2 encoder                */
 #define I2C_DRIVERID_TVEEPROM  68      /* TV EEPROM                    */
 #define I2C_DRIVERID_WM8775    69      /* wm8775 audio processor       */
 #define I2C_HW_SMBUS_W9968CF   0x04000d
 #define I2C_HW_SMBUS_OV511     0x04000e /* OV511(+) USB 1.1 webcam ICs */
 #define I2C_HW_SMBUS_OV518     0x04000f /* OV518(+) USB 1.1 webcam ICs */
-#define I2C_HW_SMBUS_OVFX2     0x040011 /* Cypress/OmniVision FX2 webcam */
 #define I2C_HW_SMBUS_CAFE      0x040012 /* Marvell 88ALP01 "CAFE" cam  */
 #define I2C_HW_SMBUS_ALI1563   0x040013
 
index 4a535ea1e1235eddd26f1865398006c9938037b8..2e66a95e8d32f5e19ee073891b0b103765cc865d 100644 (file)
@@ -246,6 +246,7 @@ struct v4l2_capability
 #define V4L2_CAP_SLICED_VBI_OUTPUT     0x00000080  /* Is a sliced VBI output device */
 #define V4L2_CAP_RDS_CAPTURE           0x00000100  /* RDS data capture */
 #define V4L2_CAP_VIDEO_OUTPUT_OVERLAY  0x00000200  /* Can do video output overlay */
+#define V4L2_CAP_HW_FREQ_SEEK          0x00000400  /* Can do hardware frequency seek  */
 
 #define V4L2_CAP_TUNER                 0x00010000  /* has a tuner */
 #define V4L2_CAP_AUDIO                 0x00020000  /* has audio support */
@@ -309,6 +310,7 @@ struct v4l2_pix_format
 
 /* see http://www.siliconimaging.com/RGB%20Bayer.htm */
 #define V4L2_PIX_FMT_SBGGR8  v4l2_fourcc('B','A','8','1') /*  8  BGBG.. GRGR.. */
+#define V4L2_PIX_FMT_SGBRG8  v4l2_fourcc('G','B','R','G') /*  8  GBGB.. RGRG.. */
 #define V4L2_PIX_FMT_SBGGR16 v4l2_fourcc('B','Y','R','2') /* 16  BGBG.. GRGR.. */
 
 /* compressed formats */
@@ -323,6 +325,9 @@ struct v4l2_pix_format
 #define V4L2_PIX_FMT_PWC1     v4l2_fourcc('P','W','C','1') /* pwc older webcam */
 #define V4L2_PIX_FMT_PWC2     v4l2_fourcc('P','W','C','2') /* pwc newer webcam */
 #define V4L2_PIX_FMT_ET61X251 v4l2_fourcc('E','6','2','5') /* ET61X251 compression */
+#define V4L2_PIX_FMT_SPCA501  v4l2_fourcc('S','5','0','1') /* YUYV per line */
+#define V4L2_PIX_FMT_SPCA561  v4l2_fourcc('S','5','6','1') /* compressed GBRG bayer */
+#define V4L2_PIX_FMT_PAC207   v4l2_fourcc('P','2','0','7') /* compressed BGGR bayer */
 
 /*
  *     F O R M A T   E N U M E R A T I O N
@@ -1156,6 +1161,14 @@ struct v4l2_frequency
        __u32                 reserved[8];
 };
 
+struct v4l2_hw_freq_seek {
+       __u32                 tuner;
+       enum v4l2_tuner_type  type;
+       __u32                 seek_upward;
+       __u32                 wrap_around;
+       __u32                 reserved[8];
+};
+
 /*
  *     A U D I O
  */
@@ -1441,6 +1454,7 @@ struct v4l2_chip_ident {
 
 #define VIDIOC_G_CHIP_IDENT     _IOWR ('V', 81, struct v4l2_chip_ident)
 #endif
+#define VIDIOC_S_HW_FREQ_SEEK  _IOW  ('V', 82, struct v4l2_hw_freq_seek)
 
 #ifdef __OLD_VIDIOC_
 /* for compatibility, will go away some day */
index 5f4608e8847647723f8028bc4b488a2b62e060ca..9ec4d5889ef5a9e1981c5a903d1db8a61e30da35 100644 (file)
@@ -27,6 +27,7 @@ enum cx2341x_port {
 
 enum cx2341x_cap {
        CX2341X_CAP_HAS_SLICED_VBI = 1 << 0,
+       CX2341X_CAP_HAS_TS         = 1 << 1,
 };
 
 struct cx2341x_mpeg_params {
@@ -88,13 +89,13 @@ typedef int (*cx2341x_mbox_func)(void *priv, u32 cmd, int in, int out,
 int cx2341x_update(void *priv, cx2341x_mbox_func func,
                const struct cx2341x_mpeg_params *old,
                const struct cx2341x_mpeg_params *new);
-int cx2341x_ctrl_query(struct cx2341x_mpeg_params *params,
+int cx2341x_ctrl_query(const struct cx2341x_mpeg_params *params,
                struct v4l2_queryctrl *qctrl);
-const char **cx2341x_ctrl_get_menu(u32 id);
+const char **cx2341x_ctrl_get_menu(const struct cx2341x_mpeg_params *p, u32 id);
 int cx2341x_ext_ctrls(struct cx2341x_mpeg_params *params, int busy,
                struct v4l2_ext_controls *ctrls, unsigned int cmd);
 void cx2341x_fill_defaults(struct cx2341x_mpeg_params *p);
-void cx2341x_log_status(struct cx2341x_mpeg_params *p, const char *prefix);
+void cx2341x_log_status(const struct cx2341x_mpeg_params *p, const char *prefix);
 
 /* Firmware names */
 #define CX2341X_FIRM_ENC_FILENAME "v4l-cx2341x-enc.fw"
index a455f7ce5ee8cbfdf423d4e5d0409b57954b7eb1..00fa57eb9fde928fc04d440ae39cdde2205266ee 100644 (file)
@@ -19,7 +19,4 @@ struct IR_i2c {
        char                   phys[32];
        int                    (*get_key)(struct IR_i2c*, u32*, u32*);
 };
-
-int get_key_pinnacle_grey(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw);
-int get_key_pinnacle_color(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw);
 #endif
index adc1254e887e3ae0d1fd8ae79a5eb01ab9df7b3d..0f19779c463453b83725c9a0599e72218994dcb7 100644 (file)
@@ -55,8 +55,7 @@
 #include <linux/types.h>
 #include <linux/version.h>
 
-
- /* Enumeration of image sizes */
+/* Enumeration of image sizes */
 #define PSZ_SQCIF      0x00
 #define PSZ_QSIF       0x01
 #define PSZ_QCIF       0x02
index 88b2b5a619aaf21ebf98811d02df6c50ef7b8e50..2f68f4cd00377ac06b7543f084d93f99219b7f49 100644 (file)
@@ -53,7 +53,7 @@ struct saa7146_vv;
 /* saa7146 page table */
 struct saa7146_pgtable {
        unsigned int    size;
-       u32             *cpu;
+       __le32          *cpu;
        dma_addr_t      dma;
        /* used for offsets for u,v planes for planar capture modes */
        unsigned long   offset;
@@ -101,7 +101,7 @@ struct saa7146_extension
 struct saa7146_dma
 {
        dma_addr_t      dma_handle;
-       u32             *cpu_addr;
+       __le32          *cpu_addr;
 };
 
 struct saa7146_dev
diff --git a/include/media/sh_mobile_ceu.h b/include/media/sh_mobile_ceu.h
new file mode 100644 (file)
index 0000000..234a471
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef __ASM_SH_MOBILE_CEU_H__
+#define __ASM_SH_MOBILE_CEU_H__
+
+#include <media/soc_camera.h>
+
+struct sh_mobile_ceu_info {
+       unsigned long flags; /* SOCAM_... */
+       void (*enable_camera)(void);
+       void (*disable_camera)(void);
+};
+
+#endif /* __ASM_SH_MOBILE_CEU_H__ */
index 6a8c8be7a1aed7694aac8148b605a3f8448f1a59..1de98f150e99e898e266c97f8de54c7c1aba0643 100644 (file)
@@ -13,7 +13,7 @@
 #define SOC_CAMERA_H
 
 #include <linux/videodev2.h>
-#include <media/videobuf-dma-sg.h>
+#include <media/videobuf-core.h>
 
 struct soc_camera_device {
        struct list_head list;
@@ -48,15 +48,12 @@ struct soc_camera_device {
 struct soc_camera_file {
        struct soc_camera_device *icd;
        struct videobuf_queue vb_vidq;
-       spinlock_t *lock;
 };
 
 struct soc_camera_host {
        struct list_head list;
        struct device dev;
        unsigned char nr;                               /* Host number */
-       size_t msize;
-       struct videobuf_queue_ops *vbq_ops;
        void *priv;
        char *drv_name;
        struct soc_camera_host_ops *ops;
@@ -69,13 +66,13 @@ struct soc_camera_host_ops {
        int (*set_fmt_cap)(struct soc_camera_device *, __u32,
                           struct v4l2_rect *);
        int (*try_fmt_cap)(struct soc_camera_device *, struct v4l2_format *);
+       void (*init_videobuf)(struct videobuf_queue *,
+                             struct soc_camera_device *);
        int (*reqbufs)(struct soc_camera_file *, struct v4l2_requestbuffers *);
        int (*querycap)(struct soc_camera_host *, struct v4l2_capability *);
        int (*try_bus_param)(struct soc_camera_device *, __u32);
        int (*set_bus_param)(struct soc_camera_device *, __u32);
        unsigned int (*poll)(struct file *, poll_table *);
-       spinlock_t* (*spinlock_alloc)(struct soc_camera_file *);
-       void (*spinlock_free)(spinlock_t *);
 };
 
 struct soc_camera_link {
@@ -156,11 +153,12 @@ static inline struct v4l2_queryctrl const *soc_camera_find_qctrl(
 #define SOCAM_DATAWIDTH_8              (1 << 6)
 #define SOCAM_DATAWIDTH_9              (1 << 7)
 #define SOCAM_DATAWIDTH_10             (1 << 8)
-#define SOCAM_PCLK_SAMPLE_RISING       (1 << 9)
-#define SOCAM_PCLK_SAMPLE_FALLING      (1 << 10)
+#define SOCAM_DATAWIDTH_16             (1 << 9)
+#define SOCAM_PCLK_SAMPLE_RISING       (1 << 10)
+#define SOCAM_PCLK_SAMPLE_FALLING      (1 << 11)
 
 #define SOCAM_DATAWIDTH_MASK (SOCAM_DATAWIDTH_8 | SOCAM_DATAWIDTH_9 | \
-                             SOCAM_DATAWIDTH_10)
+                             SOCAM_DATAWIDTH_10 | SOCAM_DATAWIDTH_16)
 
 static inline unsigned long soc_camera_bus_param_compatible(
                        unsigned long camera_flags, unsigned long bus_flags)
diff --git a/include/media/soc_camera_platform.h b/include/media/soc_camera_platform.h
new file mode 100644 (file)
index 0000000..851f182
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef __SOC_CAMERA_H__
+#define __SOC_CAMERA_H__
+
+#include <linux/videodev2.h>
+
+struct soc_camera_platform_info {
+       int iface;
+       char *format_name;
+       unsigned long format_depth;
+       struct v4l2_pix_format format;
+       unsigned long bus_param;
+       int (*set_capture)(struct soc_camera_platform_info *info, int enable);
+};
+
+#endif /* __SOC_CAMERA_H__ */
index 859f7a6f6f67ab0c5073deb703c928de9f614d2f..33f379b1ecfeac87bfaf3d0c3cdf92137e8af802 100644 (file)
@@ -59,8 +59,8 @@ enum v4l2_priority v4l2_prio_max(struct v4l2_prio_state *global);
 int v4l2_prio_check(struct v4l2_prio_state *global, enum v4l2_priority *local);
 
 /* names for fancy debug output */
-extern char *v4l2_field_names[];
-extern char *v4l2_type_names[];
+extern const char *v4l2_field_names[];
+extern const char *v4l2_type_names[];
 
 /*  Compatibility layer interface  --  v4l1-compat module */
 typedef int (*v4l2_kioctl)(struct inode *inode, struct file *file,
@@ -96,6 +96,8 @@ struct video_device
        int type;       /* v4l1 */
        int type2;      /* v4l2 */
        int minor;
+       /* attribute to diferentiate multiple indexs on one physical device */
+       int index;
 
        int debug;      /* Activates debug level*/
 
@@ -118,74 +120,76 @@ struct video_device
                                    enum v4l2_priority p);
 
        /* VIDIOC_ENUM_FMT handlers */
-       int (*vidioc_enum_fmt_cap)         (struct file *file, void *fh,
+       int (*vidioc_enum_fmt_vid_cap)     (struct file *file, void *fh,
                                            struct v4l2_fmtdesc *f);
-       int (*vidioc_enum_fmt_overlay)     (struct file *file, void *fh,
+       int (*vidioc_enum_fmt_vid_overlay) (struct file *file, void *fh,
                                            struct v4l2_fmtdesc *f);
-       int (*vidioc_enum_fmt_vbi)         (struct file *file, void *fh,
+       int (*vidioc_enum_fmt_vid_out)     (struct file *file, void *fh,
                                            struct v4l2_fmtdesc *f);
-       int (*vidioc_enum_fmt_vbi_capture) (struct file *file, void *fh,
-                                           struct v4l2_fmtdesc *f);
-       int (*vidioc_enum_fmt_video_output)(struct file *file, void *fh,
-                                           struct v4l2_fmtdesc *f);
-       int (*vidioc_enum_fmt_output_overlay) (struct file *file, void *fh,
-                                           struct v4l2_fmtdesc *f);
-       int (*vidioc_enum_fmt_vbi_output)  (struct file *file, void *fh,
+#if 1
+       /* deprecated, will be removed in 2.6.28 */
+       int (*vidioc_enum_fmt_vbi_cap)     (struct file *file, void *fh,
                                            struct v4l2_fmtdesc *f);
+#endif
        int (*vidioc_enum_fmt_type_private)(struct file *file, void *fh,
                                            struct v4l2_fmtdesc *f);
 
        /* VIDIOC_G_FMT handlers */
-       int (*vidioc_g_fmt_cap)        (struct file *file, void *fh,
+       int (*vidioc_g_fmt_vid_cap)    (struct file *file, void *fh,
                                        struct v4l2_format *f);
-       int (*vidioc_g_fmt_overlay)    (struct file *file, void *fh,
+       int (*vidioc_g_fmt_vid_overlay)(struct file *file, void *fh,
                                        struct v4l2_format *f);
-       int (*vidioc_g_fmt_vbi)        (struct file *file, void *fh,
+       int (*vidioc_g_fmt_vid_out)    (struct file *file, void *fh,
                                        struct v4l2_format *f);
-       int (*vidioc_g_fmt_vbi_output) (struct file *file, void *fh,
+       int (*vidioc_g_fmt_vid_out_overlay)(struct file *file, void *fh,
                                        struct v4l2_format *f);
-       int (*vidioc_g_fmt_vbi_capture)(struct file *file, void *fh,
+       int (*vidioc_g_fmt_vbi_cap)    (struct file *file, void *fh,
                                        struct v4l2_format *f);
-       int (*vidioc_g_fmt_video_output)(struct file *file, void *fh,
+       int (*vidioc_g_fmt_vbi_out)    (struct file *file, void *fh,
                                        struct v4l2_format *f);
-       int (*vidioc_g_fmt_output_overlay) (struct file *file, void *fh,
+       int (*vidioc_g_fmt_sliced_vbi_cap)(struct file *file, void *fh,
+                                       struct v4l2_format *f);
+       int (*vidioc_g_fmt_sliced_vbi_out)(struct file *file, void *fh,
                                        struct v4l2_format *f);
        int (*vidioc_g_fmt_type_private)(struct file *file, void *fh,
                                        struct v4l2_format *f);
 
        /* VIDIOC_S_FMT handlers */
-       int (*vidioc_s_fmt_cap)        (struct file *file, void *fh,
+       int (*vidioc_s_fmt_vid_cap)    (struct file *file, void *fh,
                                        struct v4l2_format *f);
-
-       int (*vidioc_s_fmt_overlay)    (struct file *file, void *fh,
+       int (*vidioc_s_fmt_vid_overlay)(struct file *file, void *fh,
                                        struct v4l2_format *f);
-       int (*vidioc_s_fmt_vbi)        (struct file *file, void *fh,
+       int (*vidioc_s_fmt_vid_out)    (struct file *file, void *fh,
                                        struct v4l2_format *f);
-       int (*vidioc_s_fmt_vbi_output) (struct file *file, void *fh,
+       int (*vidioc_s_fmt_vid_out_overlay)(struct file *file, void *fh,
                                        struct v4l2_format *f);
-       int (*vidioc_s_fmt_vbi_capture)(struct file *file, void *fh,
+       int (*vidioc_s_fmt_vbi_cap)    (struct file *file, void *fh,
                                        struct v4l2_format *f);
-       int (*vidioc_s_fmt_video_output)(struct file *file, void *fh,
+       int (*vidioc_s_fmt_vbi_out)    (struct file *file, void *fh,
                                        struct v4l2_format *f);
-       int (*vidioc_s_fmt_output_overlay) (struct file *file, void *fh,
+       int (*vidioc_s_fmt_sliced_vbi_cap)(struct file *file, void *fh,
+                                       struct v4l2_format *f);
+       int (*vidioc_s_fmt_sliced_vbi_out)(struct file *file, void *fh,
                                        struct v4l2_format *f);
        int (*vidioc_s_fmt_type_private)(struct file *file, void *fh,
                                        struct v4l2_format *f);
 
        /* VIDIOC_TRY_FMT handlers */
-       int (*vidioc_try_fmt_cap)        (struct file *file, void *fh,
+       int (*vidioc_try_fmt_vid_cap)    (struct file *file, void *fh,
+                                         struct v4l2_format *f);
+       int (*vidioc_try_fmt_vid_overlay)(struct file *file, void *fh,
                                          struct v4l2_format *f);
-       int (*vidioc_try_fmt_overlay)    (struct file *file, void *fh,
+       int (*vidioc_try_fmt_vid_out)    (struct file *file, void *fh,
                                          struct v4l2_format *f);
-       int (*vidioc_try_fmt_vbi)        (struct file *file, void *fh,
+       int (*vidioc_try_fmt_vid_out_overlay)(struct file *file, void *fh,
                                          struct v4l2_format *f);
-       int (*vidioc_try_fmt_vbi_output) (struct file *file, void *fh,
+       int (*vidioc_try_fmt_vbi_cap)    (struct file *file, void *fh,
                                          struct v4l2_format *f);
-       int (*vidioc_try_fmt_vbi_capture)(struct file *file, void *fh,
+       int (*vidioc_try_fmt_vbi_out)    (struct file *file, void *fh,
                                          struct v4l2_format *f);
-       int (*vidioc_try_fmt_video_output)(struct file *file, void *fh,
+       int (*vidioc_try_fmt_sliced_vbi_cap)(struct file *file, void *fh,
                                          struct v4l2_format *f);
-       int (*vidioc_try_fmt_output_overlay)(struct file *file, void *fh,
+       int (*vidioc_try_fmt_sliced_vbi_out)(struct file *file, void *fh,
                                          struct v4l2_format *f);
        int (*vidioc_try_fmt_type_private)(struct file *file, void *fh,
                                          struct v4l2_format *f);
@@ -212,8 +216,9 @@ struct video_device
        int (*vidioc_streamoff)(struct file *file, void *fh, enum v4l2_buf_type i);
 
                /* Standard handling
-                       G_STD and ENUMSTD are handled by videodev.c
+                       ENUMSTD is handled by videodev.c
                 */
+       int (*vidioc_g_std) (struct file *file, void *fh, v4l2_std_id *norm);
        int (*vidioc_s_std) (struct file *file, void *fh, v4l2_std_id *norm);
        int (*vidioc_querystd) (struct file *file, void *fh, v4l2_std_id *a);
 
@@ -224,7 +229,7 @@ struct video_device
        int (*vidioc_s_input)   (struct file *file, void *fh, unsigned int i);
 
                /* Output handling */
-       int (*vidioc_enumoutput) (struct file *file, void *fh,
+       int (*vidioc_enum_output) (struct file *file, void *fh,
                                  struct v4l2_output *a);
        int (*vidioc_g_output)   (struct file *file, void *fh, unsigned int *i);
        int (*vidioc_s_output)   (struct file *file, void *fh, unsigned int i);
@@ -306,6 +311,8 @@ struct video_device
        /* Log status ioctl */
        int (*vidioc_log_status)       (struct file *file, void *fh);
 
+       int (*vidioc_s_hw_freq_seek)   (struct file *file, void *fh,
+                                       struct v4l2_hw_freq_seek *a);
 
        /* Debugging ioctls */
 #ifdef CONFIG_VIDEO_ADV_DEBUG
@@ -342,6 +349,8 @@ void *priv;
 
 /* Version 2 functions */
 extern int video_register_device(struct video_device *vfd, int type, int nr);
+int video_register_device_index(struct video_device *vfd, int type, int nr,
+                                       int index);
 void video_unregister_device(struct video_device *);
 extern int video_ioctl2(struct inode *inode, struct file *file,
                          unsigned int cmd, unsigned long arg);
@@ -366,7 +375,7 @@ video_device_create_file(struct video_device *vfd,
 {
        int ret = device_create_file(&vfd->class_dev, attr);
        if (ret < 0)
-               printk(KERN_WARNING "%s error: %d\n", __FUNCTION__, ret);
+               printk(KERN_WARNING "%s error: %d\n", __func__, ret);
        return ret;
 }
 static inline void
index 878562278b674588577c88bfeb22cb7858bbd29c..975ffbf4e2c537a93cd6e9fc50de12df0c1a104b 100644 (file)
@@ -68,7 +68,6 @@ static int v4l2_i2c_drv_detach_legacy(struct i2c_client *client)
        if (err)
                return err;
        kfree(client);
-
        return 0;
 }
 
diff --git a/include/media/videobuf-dma-contig.h b/include/media/videobuf-dma-contig.h
new file mode 100644 (file)
index 0000000..5493866
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * helper functions for physically contiguous capture buffers
+ *
+ * The functions support hardware lacking scatter gather support
+ * (i.e. the buffers must be linear in physical memory)
+ *
+ * Copyright (c) 2008 Magnus Damm
+ *
+ * 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
+ */
+#ifndef _VIDEOBUF_DMA_CONTIG_H
+#define _VIDEOBUF_DMA_CONTIG_H
+
+#include <linux/dma-mapping.h>
+#include <media/videobuf-core.h>
+
+void videobuf_queue_dma_contig_init(struct videobuf_queue *q,
+                                   struct videobuf_queue_ops *ops,
+                                   struct device *dev,
+                                   spinlock_t *irqlock,
+                                   enum v4l2_buf_type type,
+                                   enum v4l2_field field,
+                                   unsigned int msize,
+                                   void *priv);
+
+dma_addr_t videobuf_to_dma_contig(struct videobuf_buffer *buf);
+void videobuf_dma_contig_free(struct videobuf_queue *q,
+                             struct videobuf_buffer *buf);
+
+#endif /* _VIDEOBUF_DMA_CONTIG_H */
index be8da269ee334c0fae41b01dc5def2877f63ed83..90edd22d343c96a2011380964d31ef61bea9ee37 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * helper functions for SG DMA video4linux capture buffers
  *
- * The functions expect the hardware being able to scatter gatter
+ * The functions expect the hardware being able to scatter gather
  * (i.e. the buffers are not linear in physical memory, but fragmented
  * into PAGE_SIZE chunks).  They also assume the driver does not need
  * to touch the video data.
index aed39460c154cf73a8d8ecb55fe5edd51dec35b9..e87222c6a12505e47475c5064ea92091a1ec5783 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * helper functions for vmalloc capture buffers
  *
- * The functions expect the hardware being able to scatter gatter
+ * The functions expect the hardware being able to scatter gather
  * (i.e. the buffers are not linear in physical memory, but fragmented
  * into PAGE_SIZE chunks).  They also assume the driver does not need
  * to touch the video data.