File-copy from v4.4.100

This is the result of 'cp' from a linux-stable tree with the 'v4.4.100'
tag checked out (commit 26d6298789e695c9f627ce49a7bbd2286405798a) on
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git

Please refer to that tree for all history prior to this point.

Change-Id: I8a9ee2aea93cd29c52c847d0ce33091a73ae6afe
diff --git a/drivers/media/usb/gspca/Kconfig b/drivers/media/usb/gspca/Kconfig
new file mode 100644
index 0000000..3fd94fe
--- /dev/null
+++ b/drivers/media/usb/gspca/Kconfig
@@ -0,0 +1,455 @@
+menuconfig USB_GSPCA
+	tristate "GSPCA based webcams"
+	depends on VIDEO_V4L2
+	depends on INPUT || INPUT=n
+	default m
+	---help---
+	  Say Y here if you want to enable selecting webcams based
+	  on the GSPCA framework.
+
+	  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
+	  module will be called gspca_main.
+
+
+if USB_GSPCA && VIDEO_V4L2
+
+source "drivers/media/usb/gspca/m5602/Kconfig"
+source "drivers/media/usb/gspca/stv06xx/Kconfig"
+source "drivers/media/usb/gspca/gl860/Kconfig"
+
+config USB_GSPCA_BENQ
+	tristate "Benq USB Camera Driver"
+	depends on VIDEO_V4L2 && USB_GSPCA
+	help
+	  Say Y here if you want support for the Benq DC E300 camera.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gspca_benq.
+
+config USB_GSPCA_CONEX
+	tristate "Conexant Camera Driver"
+	depends on VIDEO_V4L2 && USB_GSPCA
+	help
+	  Say Y here if you want support for cameras based on the Conexant chip.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gspca_conex.
+
+config USB_GSPCA_CPIA1
+	tristate "cpia CPiA (version 1) Camera Driver"
+	depends on VIDEO_V4L2 && USB_GSPCA
+	help
+	  Say Y here if you want support for USB cameras based on the cpia
+	  CPiA chip. Note that you need atleast version 0.6.4 of libv4l for
+	  applications to understand the videoformat generated by this driver.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gspca_cpia1.
+
+config USB_GSPCA_DTCS033
+	tristate "DTCS033 (Scopium) USB Astro-Camera Driver"
+	depends on VIDEO_V4L2 && USB_GSPCA
+	help
+	  Say Y here if you want support for the Scopium camera
+	  for planetary astrophotography.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gspca_dtcs033.
+
+config USB_GSPCA_ETOMS
+	tristate "Etoms USB Camera Driver"
+	depends on VIDEO_V4L2 && USB_GSPCA
+	help
+	  Say Y here if you want support for cameras based on the Etoms chip.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gspca_etoms.
+
+config USB_GSPCA_FINEPIX
+	tristate "Fujifilm FinePix USB V4L2 driver"
+	depends on VIDEO_V4L2 && USB_GSPCA
+	help
+	  Say Y here if you want support for cameras based on the FinePix chip.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gspca_finepix.
+
+config USB_GSPCA_JEILINJ
+	tristate "Jeilin JPEG USB V4L2 driver"
+	depends on VIDEO_V4L2 && USB_GSPCA
+	help
+	  Say Y here if you want support for cameras based on this Jeilin chip.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gspca_jeilinj.
+
+config USB_GSPCA_JL2005BCD
+	tristate "JL2005B/C/D USB V4L2 driver"
+	depends on VIDEO_V4L2 && USB_GSPCA
+	help
+	  Say Y here if you want support for cameras based the
+	  JL2005B, JL2005C, or JL2005D chip.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gspca_jl2005bcd.
+
+config USB_GSPCA_KINECT
+	tristate "Kinect sensor device USB Camera Driver"
+	depends on VIDEO_V4L2 && USB_GSPCA
+	help
+	  Say Y here if you want support for the Microsoft Kinect sensor device.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gspca_kinect.
+
+config USB_GSPCA_KONICA
+	tristate "Konica USB Camera V4L2 driver"
+	depends on VIDEO_V4L2 && USB_GSPCA
+	help
+	  Say Y here if you want support for cameras based on the Konica chip.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gspca_konica.
+
+config USB_GSPCA_MARS
+	tristate "Mars USB Camera Driver"
+	depends on VIDEO_V4L2 && USB_GSPCA
+	help
+	  Say Y here if you want support for cameras based on the Mars chip.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gspca_mars.
+
+config USB_GSPCA_MR97310A
+	tristate "Mars-Semi MR97310A USB Camera Driver"
+	depends on VIDEO_V4L2 && USB_GSPCA
+	help
+	  Say Y here if you want support for cameras based on the MR97310A chip.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gspca_mr97310a.
+
+config USB_GSPCA_NW80X
+	tristate "Divio based (NW80x) USB Camera Driver"
+	depends on VIDEO_V4L2 && USB_GSPCA
+	help
+	  Say Y here if you want support for cameras based on the NW80x chips.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gspca_nw80x.
+
+config USB_GSPCA_OV519
+	tristate "OV51x / OVFX2 / W996xCF USB Camera Driver"
+	depends on VIDEO_V4L2 && USB_GSPCA
+	help
+	  Say Y here if you want support for cameras based on one of these:
+	  OV511(+), OV518(+), OV519, OVFX2, W9967CF, W9968CF
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gspca_ov519.
+
+config USB_GSPCA_OV534
+	tristate "OV534 OV772x USB Camera Driver"
+	depends on VIDEO_V4L2 && USB_GSPCA
+	help
+	  Say Y here if you want support for cameras based on the OV534 chip
+	  and sensor OV772x (e.g. Sony Playstation EYE)
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gspca_ov534.
+
+config USB_GSPCA_OV534_9
+	tristate "OV534 OV965x USB Camera Driver"
+	depends on VIDEO_V4L2 && USB_GSPCA
+	help
+	  Say Y here if you want support for cameras based on the OV534 chip
+	  and sensor OV965x (e.g. Hercules Dualpix)
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gspca_ov534_9.
+
+config USB_GSPCA_PAC207
+	tristate "Pixart PAC207 USB Camera Driver"
+	depends on VIDEO_V4L2 && USB_GSPCA
+	help
+	  Say Y here if you want support for cameras based on the PAC207 chip.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gspca_pac207.
+
+config USB_GSPCA_PAC7302
+	tristate "Pixart PAC7302 USB Camera Driver"
+	depends on VIDEO_V4L2 && USB_GSPCA
+	help
+	  Say Y here if you want support for cameras based on the PAC7302 chip.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gspca_pac7302.
+
+config USB_GSPCA_PAC7311
+	tristate "Pixart PAC7311 USB Camera Driver"
+	depends on VIDEO_V4L2 && USB_GSPCA
+	help
+	  Say Y here if you want support for cameras based on the PAC7311 chip.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gspca_pac7311.
+
+config USB_GSPCA_SE401
+	tristate "SE401 USB Camera Driver"
+	depends on VIDEO_V4L2 && USB_GSPCA
+	help
+	 Say Y here if you want support for cameras based on the
+	 Endpoints (formerly known as AOX) se401 chip.
+
+	 To compile this driver as a module, choose M here: the
+	 module will be called gspca_se401.
+
+config USB_GSPCA_SN9C2028
+	tristate "SONIX Dual-Mode USB Camera Driver"
+	depends on VIDEO_V4L2 && USB_GSPCA
+	help
+	  Say Y here if you want streaming support for Sonix SN9C2028 cameras.
+	  These are supported as stillcams in libgphoto2/camlibs/sonix.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gspca_sn9c2028.
+
+config USB_GSPCA_SN9C20X
+	tristate "SN9C20X USB Camera Driver"
+	depends on VIDEO_V4L2 && USB_GSPCA
+	help
+	 Say Y here if you want support for cameras based on the
+	 sn9c20x chips (SN9C201 and SN9C202).
+
+	 To compile this driver as a module, choose M here: the
+	 module will be called gspca_sn9c20x.
+
+config USB_GSPCA_SONIXB
+	tristate "SONIX Bayer USB Camera Driver"
+	depends on VIDEO_V4L2 && USB_GSPCA
+	help
+	  Say Y here if you want support for cameras based on the Sonix
+	  chips with Bayer format (SN9C101, SN9C102 and SN9C103).
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gspca_sonixb.
+
+config USB_GSPCA_SONIXJ
+	tristate "SONIX JPEG USB Camera Driver"
+	depends on VIDEO_V4L2 && USB_GSPCA
+	help
+	  Say Y here if you want support for cameras based on the Sonix
+	  chips with JPEG format (SN9C102P, SN9C105 and >= SN9C110).
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gspca_sonixj
+
+config USB_GSPCA_SPCA500
+	tristate "SPCA500 USB Camera Driver"
+	depends on VIDEO_V4L2 && USB_GSPCA
+	help
+	  Say Y here if you want support for cameras based on the SPCA500 chip.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gspca_spca500.
+
+config USB_GSPCA_SPCA501
+	tristate "SPCA501 USB Camera Driver"
+	depends on VIDEO_V4L2 && USB_GSPCA
+	help
+	  Say Y here if you want support for cameras based on the SPCA501 chip.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gspca_spca501.
+
+config USB_GSPCA_SPCA505
+	tristate "SPCA505 USB Camera Driver"
+	depends on VIDEO_V4L2 && USB_GSPCA
+	help
+	  Say Y here if you want support for cameras based on the SPCA505 chip.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gspca_spca505.
+
+config USB_GSPCA_SPCA506
+	tristate "SPCA506 USB Camera Driver"
+	depends on VIDEO_V4L2 && USB_GSPCA
+	help
+	  Say Y here if you want support for cameras based on the SPCA506 chip.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gspca_spca506.
+
+config USB_GSPCA_SPCA508
+	tristate "SPCA508 USB Camera Driver"
+	depends on VIDEO_V4L2 && USB_GSPCA
+	help
+	  Say Y here if you want support for cameras based on the SPCA508 chip.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gspca_spca508.
+
+config USB_GSPCA_SPCA561
+	tristate "SPCA561 USB Camera Driver"
+	depends on VIDEO_V4L2 && USB_GSPCA
+	help
+	  Say Y here if you want support for cameras based on the SPCA561 chip.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gspca_spca561.
+
+config USB_GSPCA_SPCA1528
+	tristate "SPCA1528 USB Camera Driver"
+	depends on VIDEO_V4L2 && USB_GSPCA
+	help
+	  Say Y here if you want support for cameras based on the SPCA1528 chip.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gspca_spca1528.
+
+config USB_GSPCA_SQ905
+	tristate "SQ Technologies SQ905 based USB Camera Driver"
+	depends on VIDEO_V4L2 && USB_GSPCA
+	help
+	  Say Y here if you want support for cameras based on the SQ905 chip.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gspca_sq905.
+
+config USB_GSPCA_SQ905C
+	tristate "SQ Technologies SQ905C based USB Camera Driver"
+	depends on VIDEO_V4L2 && USB_GSPCA
+	help
+	  Say Y here if you want support for cameras based on the SQ905C chip.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gspca_sq905c.
+
+config USB_GSPCA_SQ930X
+	tristate "SQ Technologies SQ930X based USB Camera Driver"
+	depends on VIDEO_V4L2 && USB_GSPCA
+	help
+	  Say Y here if you want support for cameras based on the SQ930X chip.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gspca_sq930x.
+
+config USB_GSPCA_STK014
+	tristate "Syntek DV4000 (STK014) USB Camera Driver"
+	depends on VIDEO_V4L2 && USB_GSPCA
+	help
+	  Say Y here if you want support for cameras based on the STK014 chip.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gspca_stk014.
+
+config USB_GSPCA_STK1135
+	tristate "Syntek STK1135 USB Camera Driver"
+	depends on VIDEO_V4L2 && USB_GSPCA
+	help
+	  Say Y here if you want support for cameras based on the STK1135 chip.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gspca_stk1135.
+
+config USB_GSPCA_STV0680
+	tristate "STV0680 USB Camera Driver"
+	depends on VIDEO_V4L2 && USB_GSPCA
+	help
+	  Say Y here if you want support for cameras based on the STV0680 chip.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gspca_stv0680.
+
+config USB_GSPCA_SUNPLUS
+	tristate "SUNPLUS USB Camera Driver"
+	depends on VIDEO_V4L2 && USB_GSPCA
+	help
+	  Say Y here if you want support for cameras based on the Sunplus
+	  SPCA504(abc) SPCA533 SPCA536 chips.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gspca_sunplus.
+
+config USB_GSPCA_T613
+	tristate "T613 (JPEG Compliance) USB Camera Driver"
+	depends on VIDEO_V4L2 && USB_GSPCA
+	help
+	  Say Y here if you want support for cameras based on the T613 chip.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gspca_t613.
+
+config USB_GSPCA_TOPRO
+	tristate "TOPRO USB Camera Driver"
+	depends on VIDEO_V4L2 && USB_GSPCA
+	help
+	  Say Y here if you want support for cameras based on the
+	  TP6800 and TP6810 Topro chips.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gspca_topro.
+
+config USB_GSPCA_TOUPTEK
+	tristate "Touptek USB Camera Driver"
+	depends on VIDEO_V4L2 && USB_GSPCA
+	help
+	  Say Y here if you want support for cameras based on the ToupTek UCMOS
+	  / AmScope MU series camera.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gspca_touptek.
+
+config USB_GSPCA_TV8532
+	tristate "TV8532 USB Camera Driver"
+	depends on VIDEO_V4L2 && USB_GSPCA
+	help
+	  Say Y here if you want support for cameras based on the TV8531 chip.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gspca_tv8532.
+
+config USB_GSPCA_VC032X
+	tristate "VC032X USB Camera Driver"
+	depends on VIDEO_V4L2 && USB_GSPCA
+	help
+	  Say Y here if you want support for cameras based on the VC032X chip.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gspca_vc032x.
+
+config USB_GSPCA_VICAM
+	tristate "ViCam USB Camera Driver"
+	depends on VIDEO_V4L2 && USB_GSPCA
+	help
+	  Say Y here if you want support for the 3com homeconnect camera
+	  (vicam).
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gspca_vicam.
+
+config USB_GSPCA_XIRLINK_CIT
+	tristate "Xirlink C-It USB Camera Driver"
+	depends on VIDEO_V4L2 && USB_GSPCA
+	help
+	  Say Y here if you want support for Xirlink C-It bases cameras.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gspca_xirlink_cit.
+
+config USB_GSPCA_ZC3XX
+	tristate "ZC3XX USB Camera Driver"
+	depends on VIDEO_V4L2 && USB_GSPCA
+	help
+	  Say Y here if you want support for cameras based on the ZC3XX chip.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gspca_zc3xx.
+
+endif
diff --git a/drivers/media/usb/gspca/Makefile b/drivers/media/usb/gspca/Makefile
new file mode 100644
index 0000000..9f5ccec
--- /dev/null
+++ b/drivers/media/usb/gspca/Makefile
@@ -0,0 +1,99 @@
+obj-$(CONFIG_USB_GSPCA)          += gspca_main.o
+obj-$(CONFIG_USB_GSPCA_BENQ)     += gspca_benq.o
+obj-$(CONFIG_USB_GSPCA_CONEX)    += gspca_conex.o
+obj-$(CONFIG_USB_GSPCA_CPIA1)    += gspca_cpia1.o
+obj-$(CONFIG_USB_GSPCA_DTCS033)  += gspca_dtcs033.o
+obj-$(CONFIG_USB_GSPCA_ETOMS)    += gspca_etoms.o
+obj-$(CONFIG_USB_GSPCA_FINEPIX)  += gspca_finepix.o
+obj-$(CONFIG_USB_GSPCA_JEILINJ)  += gspca_jeilinj.o
+obj-$(CONFIG_USB_GSPCA_JL2005BCD) += gspca_jl2005bcd.o
+obj-$(CONFIG_USB_GSPCA_KINECT)   += gspca_kinect.o
+obj-$(CONFIG_USB_GSPCA_KONICA)   += gspca_konica.o
+obj-$(CONFIG_USB_GSPCA_MARS)     += gspca_mars.o
+obj-$(CONFIG_USB_GSPCA_MR97310A) += gspca_mr97310a.o
+obj-$(CONFIG_USB_GSPCA_NW80X)    += gspca_nw80x.o
+obj-$(CONFIG_USB_GSPCA_OV519)    += gspca_ov519.o
+obj-$(CONFIG_USB_GSPCA_OV534)    += gspca_ov534.o
+obj-$(CONFIG_USB_GSPCA_OV534_9)  += gspca_ov534_9.o
+obj-$(CONFIG_USB_GSPCA_PAC207)   += gspca_pac207.o
+obj-$(CONFIG_USB_GSPCA_PAC7302)  += gspca_pac7302.o
+obj-$(CONFIG_USB_GSPCA_PAC7311)  += gspca_pac7311.o
+obj-$(CONFIG_USB_GSPCA_SE401)    += gspca_se401.o
+obj-$(CONFIG_USB_GSPCA_SN9C2028) += gspca_sn9c2028.o
+obj-$(CONFIG_USB_GSPCA_SN9C20X)  += gspca_sn9c20x.o
+obj-$(CONFIG_USB_GSPCA_SONIXB)   += gspca_sonixb.o
+obj-$(CONFIG_USB_GSPCA_SONIXJ)   += gspca_sonixj.o
+obj-$(CONFIG_USB_GSPCA_SPCA500)  += gspca_spca500.o
+obj-$(CONFIG_USB_GSPCA_SPCA501)  += gspca_spca501.o
+obj-$(CONFIG_USB_GSPCA_SPCA505)  += gspca_spca505.o
+obj-$(CONFIG_USB_GSPCA_SPCA506)  += gspca_spca506.o
+obj-$(CONFIG_USB_GSPCA_SPCA508)  += gspca_spca508.o
+obj-$(CONFIG_USB_GSPCA_SPCA561)  += gspca_spca561.o
+obj-$(CONFIG_USB_GSPCA_SPCA1528) += gspca_spca1528.o
+obj-$(CONFIG_USB_GSPCA_SQ905)    += gspca_sq905.o
+obj-$(CONFIG_USB_GSPCA_SQ905C)   += gspca_sq905c.o
+obj-$(CONFIG_USB_GSPCA_SQ930X)   += gspca_sq930x.o
+obj-$(CONFIG_USB_GSPCA_SUNPLUS)  += gspca_sunplus.o
+obj-$(CONFIG_USB_GSPCA_STK014)   += gspca_stk014.o
+obj-$(CONFIG_USB_GSPCA_STK1135)  += gspca_stk1135.o
+obj-$(CONFIG_USB_GSPCA_STV0680)  += gspca_stv0680.o
+obj-$(CONFIG_USB_GSPCA_T613)     += gspca_t613.o
+obj-$(CONFIG_USB_GSPCA_TOPRO)    += gspca_topro.o
+obj-$(CONFIG_USB_GSPCA_TOUPTEK)  += gspca_touptek.o
+obj-$(CONFIG_USB_GSPCA_TV8532)   += gspca_tv8532.o
+obj-$(CONFIG_USB_GSPCA_VC032X)   += gspca_vc032x.o
+obj-$(CONFIG_USB_GSPCA_VICAM)    += gspca_vicam.o
+obj-$(CONFIG_USB_GSPCA_XIRLINK_CIT) += gspca_xirlink_cit.o
+obj-$(CONFIG_USB_GSPCA_ZC3XX)    += gspca_zc3xx.o
+
+gspca_main-objs     := gspca.o autogain_functions.o
+gspca_benq-objs     := benq.o
+gspca_conex-objs    := conex.o
+gspca_cpia1-objs    := cpia1.o
+gspca_dtcs033-objs  := dtcs033.o
+gspca_etoms-objs    := etoms.o
+gspca_finepix-objs  := finepix.o
+gspca_jeilinj-objs  := jeilinj.o
+gspca_jl2005bcd-objs  := jl2005bcd.o
+gspca_kinect-objs   := kinect.o
+gspca_konica-objs   := konica.o
+gspca_mars-objs     := mars.o
+gspca_mr97310a-objs := mr97310a.o
+gspca_nw80x-objs    := nw80x.o
+gspca_ov519-objs    := ov519.o
+gspca_ov534-objs    := ov534.o
+gspca_ov534_9-objs  := ov534_9.o
+gspca_pac207-objs   := pac207.o
+gspca_pac7302-objs  := pac7302.o
+gspca_pac7311-objs  := pac7311.o
+gspca_se401-objs    := se401.o
+gspca_sn9c2028-objs := sn9c2028.o
+gspca_sn9c20x-objs  := sn9c20x.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_spca1528-objs := spca1528.o
+gspca_sq905-objs    := sq905.o
+gspca_sq905c-objs   := sq905c.o
+gspca_sq930x-objs   := sq930x.o
+gspca_stk014-objs   := stk014.o
+gspca_stk1135-objs  := stk1135.o
+gspca_stv0680-objs  := stv0680.o
+gspca_sunplus-objs  := sunplus.o
+gspca_t613-objs     := t613.o
+gspca_topro-objs    := topro.o
+gspca_touptek-objs  := touptek.o
+gspca_tv8532-objs   := tv8532.o
+gspca_vc032x-objs   := vc032x.o
+gspca_vicam-objs    := vicam.o
+gspca_xirlink_cit-objs := xirlink_cit.o
+gspca_zc3xx-objs    := zc3xx.o
+
+obj-$(CONFIG_USB_M5602)   += m5602/
+obj-$(CONFIG_USB_STV06XX) += stv06xx/
+obj-$(CONFIG_USB_GL860)   += gl860/
diff --git a/drivers/media/usb/gspca/autogain_functions.c b/drivers/media/usb/gspca/autogain_functions.c
new file mode 100644
index 0000000..0e9ee8b
--- /dev/null
+++ b/drivers/media/usb/gspca/autogain_functions.c
@@ -0,0 +1,178 @@
+/*
+ * Functions for auto gain.
+ *
+ * Copyright (C) 2010-2012 Hans de Goede <hdegoede@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include "gspca.h"
+
+/* 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_expo_autogain(
+			struct gspca_dev *gspca_dev,
+			int avg_lum,
+			int desired_avg_lum,
+			int deadzone,
+			int gain_knee,
+			int exposure_knee)
+{
+	s32 gain, orig_gain, exposure, orig_exposure;
+	int i, steps, retval = 0;
+
+	if (v4l2_ctrl_g_ctrl(gspca_dev->autogain) == 0)
+	        return 0;
+
+	orig_gain = gain = v4l2_ctrl_g_ctrl(gspca_dev->gain);
+	orig_exposure = exposure = v4l2_ctrl_g_ctrl(gspca_dev->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",
+		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 > gspca_dev->gain->default_value)
+				gain--;
+			else if (exposure > gspca_dev->exposure->minimum)
+				exposure--;
+			else if (gain > gspca_dev->gain->minimum)
+				gain--;
+			else
+				break;
+		} else {
+			if (gain < gspca_dev->gain->default_value)
+				gain++;
+			else if (exposure < exposure_knee)
+				exposure++;
+			else if (gain < gain_knee)
+				gain++;
+			else if (exposure < gspca_dev->exposure->maximum)
+				exposure++;
+			else if (gain < gspca_dev->gain->maximum)
+				gain++;
+			else
+				break;
+		}
+	}
+
+	if (gain != orig_gain) {
+	        v4l2_ctrl_s_ctrl(gspca_dev->gain, gain);
+		retval = 1;
+	}
+	if (exposure != orig_exposure) {
+	        v4l2_ctrl_s_ctrl(gspca_dev->exposure, exposure);
+		retval = 1;
+	}
+
+	if (retval)
+		PDEBUG(D_FRAM, "autogain: changed gain: %d, expo: %d",
+			gain, exposure);
+	return retval;
+}
+EXPORT_SYMBOL(gspca_expo_autogain);
+
+/* Autogain + exposure algorithm for cameras with a coarse exposure control
+   (usually this means we can only control the clockdiv to change exposure)
+   As changing the clockdiv so that the fps drops from 30 to 15 fps for
+   example, will lead to a huge exposure change (it effectively doubles),
+   this algorithm normally tries to only adjust the gain (between 40 and
+   80 %) and if that does not help, only then changes exposure. This leads
+   to a much more stable image then using the knee algorithm which at
+   certain points of the knee graph will only try to adjust exposure,
+   which leads to oscilating as one exposure step is huge.
+
+   Returns 0 if no changes were made, 1 if the gain and or exposure settings
+   where changed. */
+int gspca_coarse_grained_expo_autogain(
+			struct gspca_dev *gspca_dev,
+			int avg_lum,
+			int desired_avg_lum,
+			int deadzone)
+{
+	s32 gain_low, gain_high, gain, orig_gain, exposure, orig_exposure;
+	int steps, retval = 0;
+
+	if (v4l2_ctrl_g_ctrl(gspca_dev->autogain) == 0)
+	        return 0;
+
+	orig_gain = gain = v4l2_ctrl_g_ctrl(gspca_dev->gain);
+	orig_exposure = exposure = v4l2_ctrl_g_ctrl(gspca_dev->exposure);
+
+	gain_low  = (s32)(gspca_dev->gain->maximum - gspca_dev->gain->minimum) /
+		    5 * 2 + gspca_dev->gain->minimum;
+	gain_high = (s32)(gspca_dev->gain->maximum - gspca_dev->gain->minimum) /
+		    5 * 4 + gspca_dev->gain->minimum;
+
+	/* 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 = (desired_avg_lum - avg_lum) / deadzone;
+
+	PDEBUG(D_FRAM, "autogain: lum: %d, desired: %d, steps: %d",
+		avg_lum, desired_avg_lum, steps);
+
+	if ((gain + steps) > gain_high &&
+	    exposure < gspca_dev->exposure->maximum) {
+		gain = gain_high;
+		gspca_dev->exp_too_low_cnt++;
+		gspca_dev->exp_too_high_cnt = 0;
+	} else if ((gain + steps) < gain_low &&
+		   exposure > gspca_dev->exposure->minimum) {
+		gain = gain_low;
+		gspca_dev->exp_too_high_cnt++;
+		gspca_dev->exp_too_low_cnt = 0;
+	} else {
+		gain += steps;
+		if (gain > gspca_dev->gain->maximum)
+			gain = gspca_dev->gain->maximum;
+		else if (gain < gspca_dev->gain->minimum)
+			gain = gspca_dev->gain->minimum;
+		gspca_dev->exp_too_high_cnt = 0;
+		gspca_dev->exp_too_low_cnt = 0;
+	}
+
+	if (gspca_dev->exp_too_high_cnt > 3) {
+		exposure--;
+		gspca_dev->exp_too_high_cnt = 0;
+	} else if (gspca_dev->exp_too_low_cnt > 3) {
+		exposure++;
+		gspca_dev->exp_too_low_cnt = 0;
+	}
+
+	if (gain != orig_gain) {
+	        v4l2_ctrl_s_ctrl(gspca_dev->gain, gain);
+		retval = 1;
+	}
+	if (exposure != orig_exposure) {
+	        v4l2_ctrl_s_ctrl(gspca_dev->exposure, exposure);
+		retval = 1;
+	}
+
+	if (retval)
+		PDEBUG(D_FRAM, "autogain: changed gain: %d, expo: %d",
+			gain, exposure);
+	return retval;
+}
+EXPORT_SYMBOL(gspca_coarse_grained_expo_autogain);
diff --git a/drivers/media/usb/gspca/benq.c b/drivers/media/usb/gspca/benq.c
new file mode 100644
index 0000000..790baed
--- /dev/null
+++ b/drivers/media/usb/gspca/benq.c
@@ -0,0 +1,289 @@
+/*
+ * Benq DC E300 subdriver
+ *
+ * Copyright (C) 2009 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "benq"
+
+#include "gspca.h"
+
+MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>");
+MODULE_DESCRIPTION("Benq DC E300 USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* specific webcam descriptor */
+struct sd {
+	struct gspca_dev gspca_dev;	/* !! must be the first item */
+};
+
+static const 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},
+};
+
+static void sd_isoc_irq(struct urb *urb);
+
+/* -- write a register -- */
+static void reg_w(struct gspca_dev *gspca_dev,
+			u16 value, u16 index)
+{
+	struct usb_device *dev = gspca_dev->dev;
+	int ret;
+
+	if (gspca_dev->usb_err < 0)
+		return;
+	ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+			0x02,
+			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			value,
+			index,
+			NULL,
+			0,
+			500);
+	if (ret < 0) {
+		pr_err("reg_w err %d\n", ret);
+		gspca_dev->usb_err = ret;
+	}
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+			const struct usb_device_id *id)
+{
+	gspca_dev->cam.cam_mode = vga_mode;
+	gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode);
+	gspca_dev->cam.no_urb_create = 1;
+	return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+	return 0;
+}
+
+/* -- start the camera -- */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+	struct urb *urb;
+	int i, n;
+
+	/* create 4 URBs - 2 on endpoint 0x83 and 2 on 0x082 */
+#if MAX_NURBS < 4
+#error "Not enough URBs in the gspca table"
+#endif
+#define SD_PKT_SZ 64
+#define SD_NPKT 32
+	for (n = 0; n < 4; n++) {
+		urb = usb_alloc_urb(SD_NPKT, GFP_KERNEL);
+		if (!urb) {
+			pr_err("usb_alloc_urb failed\n");
+			return -ENOMEM;
+		}
+		gspca_dev->urb[n] = urb;
+		urb->transfer_buffer = usb_alloc_coherent(gspca_dev->dev,
+						SD_PKT_SZ * SD_NPKT,
+						GFP_KERNEL,
+						&urb->transfer_dma);
+
+		if (urb->transfer_buffer == NULL) {
+			pr_err("usb_alloc_coherent failed\n");
+			return -ENOMEM;
+		}
+		urb->dev = gspca_dev->dev;
+		urb->context = gspca_dev;
+		urb->transfer_buffer_length = SD_PKT_SZ * SD_NPKT;
+		urb->pipe = usb_rcvisocpipe(gspca_dev->dev,
+					n & 1 ? 0x82 : 0x83);
+		urb->transfer_flags = URB_ISO_ASAP
+					| URB_NO_TRANSFER_DMA_MAP;
+		urb->interval = 1;
+		urb->complete = sd_isoc_irq;
+		urb->number_of_packets = SD_NPKT;
+		for (i = 0; i < SD_NPKT; i++) {
+			urb->iso_frame_desc[i].length = SD_PKT_SZ;
+			urb->iso_frame_desc[i].offset = SD_PKT_SZ * i;
+		}
+	}
+
+	return gspca_dev->usb_err;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+	struct usb_interface *intf;
+
+	reg_w(gspca_dev, 0x003c, 0x0003);
+	reg_w(gspca_dev, 0x003c, 0x0004);
+	reg_w(gspca_dev, 0x003c, 0x0005);
+	reg_w(gspca_dev, 0x003c, 0x0006);
+	reg_w(gspca_dev, 0x003c, 0x0007);
+
+	intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface);
+	usb_set_interface(gspca_dev->dev, gspca_dev->iface,
+					intf->num_altsetting - 1);
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+			u8 *data,		/* isoc packet */
+			int len)		/* iso packet length */
+{
+	/* unused */
+}
+
+/* reception of an URB */
+static void sd_isoc_irq(struct urb *urb)
+{
+	struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context;
+	struct urb *urb0;
+	u8 *data;
+	int i, st;
+
+	PDEBUG(D_PACK, "sd isoc irq");
+	if (!gspca_dev->streaming)
+		return;
+	if (urb->status != 0) {
+		if (urb->status == -ESHUTDOWN)
+			return;		/* disconnection */
+#ifdef CONFIG_PM
+		if (gspca_dev->frozen)
+			return;
+#endif
+		pr_err("urb status: %d\n", urb->status);
+		return;
+	}
+
+	/* if this is a control URN (ep 0x83), wait */
+	if (urb == gspca_dev->urb[0] || urb == gspca_dev->urb[2])
+		return;
+
+	/* scan both received URBs */
+	if (urb == gspca_dev->urb[1])
+		urb0 = gspca_dev->urb[0];
+	else
+		urb0 = gspca_dev->urb[2];
+	for (i = 0; i < urb->number_of_packets; i++) {
+
+		/* check the packet status and length */
+		if (urb0->iso_frame_desc[i].actual_length != SD_PKT_SZ
+		    || urb->iso_frame_desc[i].actual_length != SD_PKT_SZ) {
+			PERR("ISOC bad lengths %d / %d",
+				urb0->iso_frame_desc[i].actual_length,
+				urb->iso_frame_desc[i].actual_length);
+			gspca_dev->last_packet_type = DISCARD_PACKET;
+			continue;
+		}
+		st = urb0->iso_frame_desc[i].status;
+		if (st == 0)
+			st = urb->iso_frame_desc[i].status;
+		if (st) {
+			pr_err("ISOC data error: [%d] status=%d\n",
+				i, st);
+			gspca_dev->last_packet_type = DISCARD_PACKET;
+			continue;
+		}
+
+		/*
+		 * The images are received in URBs of different endpoints
+		 * (0x83 and 0x82).
+		 * Image pieces in URBs of ep 0x83 are continuated in URBs of
+		 * ep 0x82 of the same index.
+		 * The packets in the URBs of endpoint 0x83 start with:
+		 *	- 80 ba/bb 00 00 = start of image followed by 'ff d8'
+		 *	- 04 ba/bb oo oo = image piece
+		 *		where 'oo oo' is the image offset
+						(not cheked)
+		 *	- (other -> bad frame)
+		 * The images are JPEG encoded with full header and
+		 * normal ff escape.
+		 * The end of image ('ff d9') may occur in any URB.
+		 * (not cheked)
+		 */
+		data = (u8 *) urb0->transfer_buffer
+					+ urb0->iso_frame_desc[i].offset;
+		if (data[0] == 0x80 && (data[1] & 0xfe) == 0xba) {
+
+			/* new image */
+			gspca_frame_add(gspca_dev, LAST_PACKET,
+					NULL, 0);
+			gspca_frame_add(gspca_dev, FIRST_PACKET,
+					data + 4, SD_PKT_SZ - 4);
+		} else if (data[0] == 0x04 && (data[1] & 0xfe) == 0xba) {
+			gspca_frame_add(gspca_dev, INTER_PACKET,
+					data + 4, SD_PKT_SZ - 4);
+		} else {
+			gspca_dev->last_packet_type = DISCARD_PACKET;
+			continue;
+		}
+		data = (u8 *) urb->transfer_buffer
+					+ urb->iso_frame_desc[i].offset;
+		gspca_frame_add(gspca_dev, INTER_PACKET,
+				data, SD_PKT_SZ);
+	}
+
+	/* resubmit the URBs */
+	st = usb_submit_urb(urb0, GFP_ATOMIC);
+	if (st < 0)
+		pr_err("usb_submit_urb(0) ret %d\n", st);
+	st = usb_submit_urb(urb, GFP_ATOMIC);
+	if (st < 0)
+		pr_err("usb_submit_urb() ret %d\n", st);
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+	.name = MODULE_NAME,
+	.config = sd_config,
+	.init = sd_init,
+	.start = sd_start,
+	.stopN = sd_stopN,
+	.pkt_scan = sd_pkt_scan,
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+	{USB_DEVICE(0x04a5, 0x3035)},
+	{}
+};
+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,
+#ifdef CONFIG_PM
+	.suspend = gspca_suspend,
+	.resume = gspca_resume,
+	.reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/usb/gspca/conex.c b/drivers/media/usb/gspca/conex.c
new file mode 100644
index 0000000..2e15c80
--- /dev/null
+++ b/drivers/media/usb/gspca/conex.c
@@ -0,0 +1,965 @@
+/*
+ *		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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "conex"
+
+#include "gspca.h"
+#define CONEX_CAM 1		/* special JPEG header */
+#include "jpeg.h"
+
+MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
+MODULE_DESCRIPTION("GSPCA USB Conexant Camera Driver");
+MODULE_LICENSE("GPL");
+
+#define QUALITY 50
+
+/* specific webcam descriptor */
+struct sd {
+	struct gspca_dev gspca_dev;	/* !! must be the first item */
+	struct v4l2_ctrl *brightness;
+	struct v4l2_ctrl *contrast;
+	struct v4l2_ctrl *sat;
+
+	u8 jpeg_hdr[JPEG_HDR_SZ];
+};
+
+static const 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;
+
+	if (len > USB_BUF_SZ) {
+		PERR("reg_r: buffer overflow\n");
+		return;
+	}
+
+	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;
+
+	if (len > USB_BUF_SZ) {
+		PERR("reg_w: buffer overflow\n");
+		return;
+	}
+	PDEBUG(D_USBO, "reg write [%02x] = %02x..", index, *buffer);
+
+	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, 2);
+	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, 2);
+	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[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;
+	do {
+		reg_r(gspca_dev, 0x0002, 1);
+							/* 0x07 until 0x00 */
+		if (gspca_dev->usb_buf[0] == 0x00)
+			break;
+		reg_w_val(gspca_dev, 0x0053, 0x00);
+	} while (--retry);
+	if (retry == 0)
+		PERR("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 cam *cam;
+
+	cam = &gspca_dev->cam;
+	cam->cam_mode = vga_mode;
+	cam->nmodes = ARRAY_SIZE(vga_mode);
+	return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(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 int sd_start(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	/* create the JPEG header */
+	jpeg_define(sd->jpeg_hdr, gspca_dev->pixfmt.height,
+			gspca_dev->pixfmt.width,
+			0x22);		/* JPEG 411 */
+	jpeg_set_qual(sd->jpeg_hdr, QUALITY);
+
+	cx11646_initsize(gspca_dev);
+	cx11646_fw(gspca_dev);
+	cx_sensor(gspca_dev);
+	cx11646_jpeg(gspca_dev);
+	return 0;
+}
+
+/* called on streamoff with alt 0 and on disconnect */
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+	int retry = 50;
+
+	if (!gspca_dev->present)
+		return;
+	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_pkt_scan(struct gspca_dev *gspca_dev,
+			u8 *data,			/* isoc packet */
+			int len)			/* iso packet length */
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (data[0] == 0xff && data[1] == 0xd8) {
+
+		/* start of frame */
+		gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+
+		/* put the JPEG header in the new frame */
+		gspca_frame_add(gspca_dev, FIRST_PACKET,
+				sd->jpeg_hdr, JPEG_HDR_SZ);
+		data += 2;
+		len -= 2;
+	}
+	gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev, s32 val, s32 sat)
+{
+	__u8 regE5cbx[] = { 0x88, 0x00, 0xd4, 0x01, 0x88, 0x01, 0x01, 0x01 };
+	__u8 reg51c[2];
+
+	regE5cbx[2] = val;
+	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 */
+
+	reg51c[0] = 0x77;
+	reg51c[1] = sat;
+	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, s32 val, s32 sat)
+{
+	__u8 regE5acx[] = { 0x88, 0x0a, 0x0c, 0x01 };	/* seem MSB */
+/*	__u8 regE5bcx[] = { 0x88, 0x0b, 0x12, 0x01};	 * LSB */
+	__u8 reg51c[2];
+
+	regE5acx[2] = val;
+	reg_w(gspca_dev, 0x00e5, regE5acx, 4);
+	reg_r(gspca_dev, 0x00e8, 1);		/* 0x00 */
+	reg51c[0] = 0x77;
+	reg51c[1] = sat;
+	reg_w(gspca_dev, 0x0051, reg51c, 2);
+	reg_w(gspca_dev, 0x0010, reg10, 2);
+	reg_w_val(gspca_dev, 0x0070, reg70);
+}
+
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct gspca_dev *gspca_dev =
+		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+	struct sd *sd = (struct sd *)gspca_dev;
+
+	gspca_dev->usb_err = 0;
+
+	if (!gspca_dev->streaming)
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_BRIGHTNESS:
+		setbrightness(gspca_dev, ctrl->val, sd->sat->cur.val);
+		break;
+	case V4L2_CID_CONTRAST:
+		setcontrast(gspca_dev, ctrl->val, sd->sat->cur.val);
+		break;
+	case V4L2_CID_SATURATION:
+		setbrightness(gspca_dev, sd->brightness->cur.val, ctrl->val);
+		setcontrast(gspca_dev, sd->contrast->cur.val, ctrl->val);
+		break;
+	}
+	return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+	.s_ctrl = sd_s_ctrl,
+};
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *)gspca_dev;
+	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+	gspca_dev->vdev.ctrl_handler = hdl;
+	v4l2_ctrl_handler_init(hdl, 3);
+	sd->brightness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_BRIGHTNESS, 0, 255, 1, 0xd4);
+	sd->contrast = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_CONTRAST, 0x0a, 0x1f, 1, 0x0c);
+	sd->sat = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_SATURATION, 0, 7, 1, 3);
+	if (hdl->error) {
+		pr_err("Could not initialize controls\n");
+		return hdl->error;
+	}
+	return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+	.name = MODULE_NAME,
+	.config = sd_config,
+	.init = sd_init,
+	.init_controls = sd_init_controls,
+	.start = sd_start,
+	.stop0 = sd_stop0,
+	.pkt_scan = sd_pkt_scan,
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+	{USB_DEVICE(0x0572, 0x0041)},
+	{}
+};
+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,
+#ifdef CONFIG_PM
+	.suspend = gspca_suspend,
+	.resume = gspca_resume,
+	.reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/usb/gspca/cpia1.c b/drivers/media/usb/gspca/cpia1.c
new file mode 100644
index 0000000..52b88e9
--- /dev/null
+++ b/drivers/media/usb/gspca/cpia1.c
@@ -0,0 +1,1902 @@
+/*
+ * cpia CPiA (1) gspca driver
+ *
+ * Copyright (C) 2010-2011 Hans de Goede <hdegoede@redhat.com>
+ *
+ * This module is adapted from the in kernel v4l1 cpia driver which is :
+ *
+ * (C) Copyright 1999-2000 Peter Pregler
+ * (C) Copyright 1999-2000 Scott J. Bertin
+ * (C) Copyright 1999-2000 Johannes Erdfelt <johannes@erdfelt.com>
+ * (C) Copyright 2000 STMicroelectronics
+ *
+ * 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "cpia1"
+
+#include <linux/input.h>
+#include "gspca.h"
+
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_DESCRIPTION("Vision CPiA");
+MODULE_LICENSE("GPL");
+
+/* constant value's */
+#define MAGIC_0		0x19
+#define MAGIC_1		0x68
+#define DATA_IN		0xc0
+#define DATA_OUT	0x40
+#define VIDEOSIZE_QCIF	0	/* 176x144 */
+#define VIDEOSIZE_CIF	1	/* 352x288 */
+#define SUBSAMPLE_420	0
+#define SUBSAMPLE_422	1
+#define YUVORDER_YUYV	0
+#define YUVORDER_UYVY	1
+#define NOT_COMPRESSED	0
+#define COMPRESSED	1
+#define NO_DECIMATION	0
+#define DECIMATION_ENAB	1
+#define EOI		0xff	/* End Of Image */
+#define EOL		0xfd	/* End Of Line */
+#define FRAME_HEADER_SIZE	64
+
+/* Image grab modes */
+#define CPIA_GRAB_SINGLE	0
+#define CPIA_GRAB_CONTINEOUS	1
+
+/* Compression parameters */
+#define CPIA_COMPRESSION_NONE	0
+#define CPIA_COMPRESSION_AUTO	1
+#define CPIA_COMPRESSION_MANUAL	2
+#define CPIA_COMPRESSION_TARGET_QUALITY         0
+#define CPIA_COMPRESSION_TARGET_FRAMERATE       1
+
+/* Return offsets for GetCameraState */
+#define SYSTEMSTATE	0
+#define GRABSTATE	1
+#define STREAMSTATE	2
+#define FATALERROR	3
+#define CMDERROR	4
+#define DEBUGFLAGS	5
+#define VPSTATUS	6
+#define ERRORCODE	7
+
+/* SystemState */
+#define UNINITIALISED_STATE	0
+#define PASS_THROUGH_STATE	1
+#define LO_POWER_STATE		2
+#define HI_POWER_STATE		3
+#define WARM_BOOT_STATE		4
+
+/* GrabState */
+#define GRAB_IDLE		0
+#define GRAB_ACTIVE		1
+#define GRAB_DONE		2
+
+/* StreamState */
+#define STREAM_NOT_READY	0
+#define STREAM_READY		1
+#define STREAM_OPEN		2
+#define STREAM_PAUSED		3
+#define STREAM_FINISHED		4
+
+/* Fatal Error, CmdError, and DebugFlags */
+#define CPIA_FLAG	  1
+#define SYSTEM_FLAG	  2
+#define INT_CTRL_FLAG	  4
+#define PROCESS_FLAG	  8
+#define COM_FLAG	 16
+#define VP_CTRL_FLAG	 32
+#define CAPTURE_FLAG	 64
+#define DEBUG_FLAG	128
+
+/* VPStatus */
+#define VP_STATE_OK			0x00
+
+#define VP_STATE_FAILED_VIDEOINIT	0x01
+#define VP_STATE_FAILED_AECACBINIT	0x02
+#define VP_STATE_AEC_MAX		0x04
+#define VP_STATE_ACB_BMAX		0x08
+
+#define VP_STATE_ACB_RMIN		0x10
+#define VP_STATE_ACB_GMIN		0x20
+#define VP_STATE_ACB_RMAX		0x40
+#define VP_STATE_ACB_GMAX		0x80
+
+/* default (minimum) compensation values */
+#define COMP_RED        220
+#define COMP_GREEN1     214
+#define COMP_GREEN2     COMP_GREEN1
+#define COMP_BLUE       230
+
+/* exposure status */
+#define EXPOSURE_VERY_LIGHT 0
+#define EXPOSURE_LIGHT      1
+#define EXPOSURE_NORMAL     2
+#define EXPOSURE_DARK       3
+#define EXPOSURE_VERY_DARK  4
+
+#define CPIA_MODULE_CPIA			(0 << 5)
+#define CPIA_MODULE_SYSTEM			(1 << 5)
+#define CPIA_MODULE_VP_CTRL			(5 << 5)
+#define CPIA_MODULE_CAPTURE			(6 << 5)
+#define CPIA_MODULE_DEBUG			(7 << 5)
+
+#define INPUT (DATA_IN << 8)
+#define OUTPUT (DATA_OUT << 8)
+
+#define CPIA_COMMAND_GetCPIAVersion	(INPUT | CPIA_MODULE_CPIA | 1)
+#define CPIA_COMMAND_GetPnPID		(INPUT | CPIA_MODULE_CPIA | 2)
+#define CPIA_COMMAND_GetCameraStatus	(INPUT | CPIA_MODULE_CPIA | 3)
+#define CPIA_COMMAND_GotoHiPower	(OUTPUT | CPIA_MODULE_CPIA | 4)
+#define CPIA_COMMAND_GotoLoPower	(OUTPUT | CPIA_MODULE_CPIA | 5)
+#define CPIA_COMMAND_GotoSuspend	(OUTPUT | CPIA_MODULE_CPIA | 7)
+#define CPIA_COMMAND_GotoPassThrough	(OUTPUT | CPIA_MODULE_CPIA | 8)
+#define CPIA_COMMAND_ModifyCameraStatus	(OUTPUT | CPIA_MODULE_CPIA | 10)
+
+#define CPIA_COMMAND_ReadVCRegs		(INPUT | CPIA_MODULE_SYSTEM | 1)
+#define CPIA_COMMAND_WriteVCReg		(OUTPUT | CPIA_MODULE_SYSTEM | 2)
+#define CPIA_COMMAND_ReadMCPorts	(INPUT | CPIA_MODULE_SYSTEM | 3)
+#define CPIA_COMMAND_WriteMCPort	(OUTPUT | CPIA_MODULE_SYSTEM | 4)
+#define CPIA_COMMAND_SetBaudRate	(OUTPUT | CPIA_MODULE_SYSTEM | 5)
+#define CPIA_COMMAND_SetECPTiming	(OUTPUT | CPIA_MODULE_SYSTEM | 6)
+#define CPIA_COMMAND_ReadIDATA		(INPUT | CPIA_MODULE_SYSTEM | 7)
+#define CPIA_COMMAND_WriteIDATA		(OUTPUT | CPIA_MODULE_SYSTEM | 8)
+#define CPIA_COMMAND_GenericCall	(OUTPUT | CPIA_MODULE_SYSTEM | 9)
+#define CPIA_COMMAND_I2CStart		(OUTPUT | CPIA_MODULE_SYSTEM | 10)
+#define CPIA_COMMAND_I2CStop		(OUTPUT | CPIA_MODULE_SYSTEM | 11)
+#define CPIA_COMMAND_I2CWrite		(OUTPUT | CPIA_MODULE_SYSTEM | 12)
+#define CPIA_COMMAND_I2CRead		(INPUT | CPIA_MODULE_SYSTEM | 13)
+
+#define CPIA_COMMAND_GetVPVersion	(INPUT | CPIA_MODULE_VP_CTRL | 1)
+#define CPIA_COMMAND_ResetFrameCounter	(INPUT | CPIA_MODULE_VP_CTRL | 2)
+#define CPIA_COMMAND_SetColourParams	(OUTPUT | CPIA_MODULE_VP_CTRL | 3)
+#define CPIA_COMMAND_SetExposure	(OUTPUT | CPIA_MODULE_VP_CTRL | 4)
+#define CPIA_COMMAND_SetColourBalance	(OUTPUT | CPIA_MODULE_VP_CTRL | 6)
+#define CPIA_COMMAND_SetSensorFPS	(OUTPUT | CPIA_MODULE_VP_CTRL | 7)
+#define CPIA_COMMAND_SetVPDefaults	(OUTPUT | CPIA_MODULE_VP_CTRL | 8)
+#define CPIA_COMMAND_SetApcor		(OUTPUT | CPIA_MODULE_VP_CTRL | 9)
+#define CPIA_COMMAND_SetFlickerCtrl	(OUTPUT | CPIA_MODULE_VP_CTRL | 10)
+#define CPIA_COMMAND_SetVLOffset	(OUTPUT | CPIA_MODULE_VP_CTRL | 11)
+#define CPIA_COMMAND_GetColourParams	(INPUT | CPIA_MODULE_VP_CTRL | 16)
+#define CPIA_COMMAND_GetColourBalance	(INPUT | CPIA_MODULE_VP_CTRL | 17)
+#define CPIA_COMMAND_GetExposure	(INPUT | CPIA_MODULE_VP_CTRL | 18)
+#define CPIA_COMMAND_SetSensorMatrix	(OUTPUT | CPIA_MODULE_VP_CTRL | 19)
+#define CPIA_COMMAND_ColourBars		(OUTPUT | CPIA_MODULE_VP_CTRL | 25)
+#define CPIA_COMMAND_ReadVPRegs		(INPUT | CPIA_MODULE_VP_CTRL | 30)
+#define CPIA_COMMAND_WriteVPReg		(OUTPUT | CPIA_MODULE_VP_CTRL | 31)
+
+#define CPIA_COMMAND_GrabFrame		(OUTPUT | CPIA_MODULE_CAPTURE | 1)
+#define CPIA_COMMAND_UploadFrame	(OUTPUT | CPIA_MODULE_CAPTURE | 2)
+#define CPIA_COMMAND_SetGrabMode	(OUTPUT | CPIA_MODULE_CAPTURE | 3)
+#define CPIA_COMMAND_InitStreamCap	(OUTPUT | CPIA_MODULE_CAPTURE | 4)
+#define CPIA_COMMAND_FiniStreamCap	(OUTPUT | CPIA_MODULE_CAPTURE | 5)
+#define CPIA_COMMAND_StartStreamCap	(OUTPUT | CPIA_MODULE_CAPTURE | 6)
+#define CPIA_COMMAND_EndStreamCap	(OUTPUT | CPIA_MODULE_CAPTURE | 7)
+#define CPIA_COMMAND_SetFormat		(OUTPUT | CPIA_MODULE_CAPTURE | 8)
+#define CPIA_COMMAND_SetROI		(OUTPUT | CPIA_MODULE_CAPTURE | 9)
+#define CPIA_COMMAND_SetCompression	(OUTPUT | CPIA_MODULE_CAPTURE | 10)
+#define CPIA_COMMAND_SetCompressionTarget (OUTPUT | CPIA_MODULE_CAPTURE | 11)
+#define CPIA_COMMAND_SetYUVThresh	(OUTPUT | CPIA_MODULE_CAPTURE | 12)
+#define CPIA_COMMAND_SetCompressionParams (OUTPUT | CPIA_MODULE_CAPTURE | 13)
+#define CPIA_COMMAND_DiscardFrame	(OUTPUT | CPIA_MODULE_CAPTURE | 14)
+#define CPIA_COMMAND_GrabReset		(OUTPUT | CPIA_MODULE_CAPTURE | 15)
+
+#define CPIA_COMMAND_OutputRS232	(OUTPUT | CPIA_MODULE_DEBUG | 1)
+#define CPIA_COMMAND_AbortProcess	(OUTPUT | CPIA_MODULE_DEBUG | 4)
+#define CPIA_COMMAND_SetDramPage	(OUTPUT | CPIA_MODULE_DEBUG | 5)
+#define CPIA_COMMAND_StartDramUpload	(OUTPUT | CPIA_MODULE_DEBUG | 6)
+#define CPIA_COMMAND_StartDummyDtream	(OUTPUT | CPIA_MODULE_DEBUG | 8)
+#define CPIA_COMMAND_AbortStream	(OUTPUT | CPIA_MODULE_DEBUG | 9)
+#define CPIA_COMMAND_DownloadDRAM	(OUTPUT | CPIA_MODULE_DEBUG | 10)
+#define CPIA_COMMAND_Null		(OUTPUT | CPIA_MODULE_DEBUG | 11)
+
+#define ROUND_UP_EXP_FOR_FLICKER 15
+
+/* Constants for automatic frame rate adjustment */
+#define MAX_EXP       302
+#define MAX_EXP_102   255
+#define LOW_EXP       140
+#define VERY_LOW_EXP   70
+#define TC             94
+#define	EXP_ACC_DARK   50
+#define	EXP_ACC_LIGHT  90
+#define HIGH_COMP_102 160
+#define MAX_COMP      239
+#define DARK_TIME       3
+#define LIGHT_TIME      3
+
+#define FIRMWARE_VERSION(x, y) (sd->params.version.firmwareVersion == (x) && \
+				sd->params.version.firmwareRevision == (y))
+
+#define CPIA1_CID_COMP_TARGET (V4L2_CTRL_CLASS_USER + 0x1000)
+#define BRIGHTNESS_DEF 50
+#define CONTRAST_DEF 48
+#define SATURATION_DEF 50
+#define FREQ_DEF V4L2_CID_POWER_LINE_FREQUENCY_50HZ
+#define ILLUMINATORS_1_DEF 0
+#define ILLUMINATORS_2_DEF 0
+#define COMP_TARGET_DEF CPIA_COMPRESSION_TARGET_QUALITY
+
+/* Developer's Guide Table 5 p 3-34
+ * indexed by [mains][sensorFps.baserate][sensorFps.divisor]*/
+static u8 flicker_jumps[2][2][4] =
+{ { { 76, 38, 19, 9 }, { 92, 46, 23, 11 } },
+  { { 64, 32, 16, 8 }, { 76, 38, 19, 9} }
+};
+
+struct cam_params {
+	struct {
+		u8 firmwareVersion;
+		u8 firmwareRevision;
+		u8 vcVersion;
+		u8 vcRevision;
+	} version;
+	struct {
+		u16 vendor;
+		u16 product;
+		u16 deviceRevision;
+	} pnpID;
+	struct {
+		u8 vpVersion;
+		u8 vpRevision;
+		u16 cameraHeadID;
+	} vpVersion;
+	struct {
+		u8 systemState;
+		u8 grabState;
+		u8 streamState;
+		u8 fatalError;
+		u8 cmdError;
+		u8 debugFlags;
+		u8 vpStatus;
+		u8 errorCode;
+	} status;
+	struct {
+		u8 brightness;
+		u8 contrast;
+		u8 saturation;
+	} colourParams;
+	struct {
+		u8 gainMode;
+		u8 expMode;
+		u8 compMode;
+		u8 centreWeight;
+		u8 gain;
+		u8 fineExp;
+		u8 coarseExpLo;
+		u8 coarseExpHi;
+		u8 redComp;
+		u8 green1Comp;
+		u8 green2Comp;
+		u8 blueComp;
+	} exposure;
+	struct {
+		u8 balanceMode;
+		u8 redGain;
+		u8 greenGain;
+		u8 blueGain;
+	} colourBalance;
+	struct {
+		u8 divisor;
+		u8 baserate;
+	} sensorFps;
+	struct {
+		u8 gain1;
+		u8 gain2;
+		u8 gain4;
+		u8 gain8;
+	} apcor;
+	struct {
+		u8 disabled;
+		u8 flickerMode;
+		u8 coarseJump;
+		u8 allowableOverExposure;
+	} flickerControl;
+	struct {
+		u8 gain1;
+		u8 gain2;
+		u8 gain4;
+		u8 gain8;
+	} vlOffset;
+	struct {
+		u8 mode;
+		u8 decimation;
+	} compression;
+	struct {
+		u8 frTargeting;
+		u8 targetFR;
+		u8 targetQ;
+	} compressionTarget;
+	struct {
+		u8 yThreshold;
+		u8 uvThreshold;
+	} yuvThreshold;
+	struct {
+		u8 hysteresis;
+		u8 threshMax;
+		u8 smallStep;
+		u8 largeStep;
+		u8 decimationHysteresis;
+		u8 frDiffStepThresh;
+		u8 qDiffStepThresh;
+		u8 decimationThreshMod;
+	} compressionParams;
+	struct {
+		u8 videoSize;		/* CIF/QCIF */
+		u8 subSample;
+		u8 yuvOrder;
+	} format;
+	struct {                        /* Intel QX3 specific data */
+		u8 qx3_detected;        /* a QX3 is present */
+		u8 toplight;            /* top light lit , R/W */
+		u8 bottomlight;         /* bottom light lit, R/W */
+		u8 button;              /* snapshot button pressed (R/O) */
+		u8 cradled;             /* microscope is in cradle (R/O) */
+	} qx3;
+	struct {
+		u8 colStart;		/* skip first 8*colStart pixels */
+		u8 colEnd;		/* finish at 8*colEnd pixels */
+		u8 rowStart;		/* skip first 4*rowStart lines */
+		u8 rowEnd;		/* finish at 4*rowEnd lines */
+	} roi;
+	u8 ecpTiming;
+	u8 streamStartLine;
+};
+
+/* specific webcam descriptor */
+struct sd {
+	struct gspca_dev gspca_dev;		/* !! must be the first item */
+	struct cam_params params;		/* camera settings */
+
+	atomic_t cam_exposure;
+	atomic_t fps;
+	int exposure_count;
+	u8 exposure_status;
+	struct v4l2_ctrl *freq;
+	u8 mainsFreq;				/* 0 = 50hz, 1 = 60hz */
+	u8 first_frame;
+};
+
+static const struct v4l2_pix_format mode[] = {
+	{160, 120, V4L2_PIX_FMT_CPIA1, V4L2_FIELD_NONE,
+		/* The sizeimage is trial and error, as with low framerates
+		   the camera will pad out usb frames, making the image
+		   data larger then strictly necessary */
+		.bytesperline = 160,
+		.sizeimage = 65536,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 3},
+	{176, 144, V4L2_PIX_FMT_CPIA1, V4L2_FIELD_NONE,
+		.bytesperline = 172,
+		.sizeimage = 65536,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 2},
+	{320, 240, V4L2_PIX_FMT_CPIA1, V4L2_FIELD_NONE,
+		.bytesperline = 320,
+		.sizeimage = 262144,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 1},
+	{352, 288, V4L2_PIX_FMT_CPIA1, V4L2_FIELD_NONE,
+		.bytesperline = 352,
+		.sizeimage = 262144,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 0},
+};
+
+/**********************************************************************
+ *
+ * General functions
+ *
+ **********************************************************************/
+
+static int cpia_usb_transferCmd(struct gspca_dev *gspca_dev, u8 *command)
+{
+	u8 requesttype;
+	unsigned int pipe;
+	int ret, databytes = command[6] | (command[7] << 8);
+	/* Sometimes we see spurious EPIPE errors */
+	int retries = 3;
+
+	if (command[0] == DATA_IN) {
+		pipe = usb_rcvctrlpipe(gspca_dev->dev, 0);
+		requesttype = USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE;
+	} else if (command[0] == DATA_OUT) {
+		pipe = usb_sndctrlpipe(gspca_dev->dev, 0);
+		requesttype = USB_TYPE_VENDOR | USB_RECIP_DEVICE;
+	} else {
+		PERR("Unexpected first byte of command: %x", command[0]);
+		return -EINVAL;
+	}
+
+retry:
+	ret = usb_control_msg(gspca_dev->dev, pipe,
+			      command[1],
+			      requesttype,
+			      command[2] | (command[3] << 8),
+			      command[4] | (command[5] << 8),
+			      gspca_dev->usb_buf, databytes, 1000);
+
+	if (ret < 0)
+		pr_err("usb_control_msg %02x, error %d\n", command[1], ret);
+
+	if (ret == -EPIPE && retries > 0) {
+		retries--;
+		goto retry;
+	}
+
+	return (ret < 0) ? ret : 0;
+}
+
+/* send an arbitrary command to the camera */
+static int do_command(struct gspca_dev *gspca_dev, u16 command,
+		      u8 a, u8 b, u8 c, u8 d)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int ret, datasize;
+	u8 cmd[8];
+
+	switch (command) {
+	case CPIA_COMMAND_GetCPIAVersion:
+	case CPIA_COMMAND_GetPnPID:
+	case CPIA_COMMAND_GetCameraStatus:
+	case CPIA_COMMAND_GetVPVersion:
+	case CPIA_COMMAND_GetColourParams:
+	case CPIA_COMMAND_GetColourBalance:
+	case CPIA_COMMAND_GetExposure:
+		datasize = 8;
+		break;
+	case CPIA_COMMAND_ReadMCPorts:
+	case CPIA_COMMAND_ReadVCRegs:
+		datasize = 4;
+		break;
+	default:
+		datasize = 0;
+		break;
+	}
+
+	cmd[0] = command >> 8;
+	cmd[1] = command & 0xff;
+	cmd[2] = a;
+	cmd[3] = b;
+	cmd[4] = c;
+	cmd[5] = d;
+	cmd[6] = datasize;
+	cmd[7] = 0;
+
+	ret = cpia_usb_transferCmd(gspca_dev, cmd);
+	if (ret)
+		return ret;
+
+	switch (command) {
+	case CPIA_COMMAND_GetCPIAVersion:
+		sd->params.version.firmwareVersion = gspca_dev->usb_buf[0];
+		sd->params.version.firmwareRevision = gspca_dev->usb_buf[1];
+		sd->params.version.vcVersion = gspca_dev->usb_buf[2];
+		sd->params.version.vcRevision = gspca_dev->usb_buf[3];
+		break;
+	case CPIA_COMMAND_GetPnPID:
+		sd->params.pnpID.vendor =
+			gspca_dev->usb_buf[0] | (gspca_dev->usb_buf[1] << 8);
+		sd->params.pnpID.product =
+			gspca_dev->usb_buf[2] | (gspca_dev->usb_buf[3] << 8);
+		sd->params.pnpID.deviceRevision =
+			gspca_dev->usb_buf[4] | (gspca_dev->usb_buf[5] << 8);
+		break;
+	case CPIA_COMMAND_GetCameraStatus:
+		sd->params.status.systemState = gspca_dev->usb_buf[0];
+		sd->params.status.grabState = gspca_dev->usb_buf[1];
+		sd->params.status.streamState = gspca_dev->usb_buf[2];
+		sd->params.status.fatalError = gspca_dev->usb_buf[3];
+		sd->params.status.cmdError = gspca_dev->usb_buf[4];
+		sd->params.status.debugFlags = gspca_dev->usb_buf[5];
+		sd->params.status.vpStatus = gspca_dev->usb_buf[6];
+		sd->params.status.errorCode = gspca_dev->usb_buf[7];
+		break;
+	case CPIA_COMMAND_GetVPVersion:
+		sd->params.vpVersion.vpVersion = gspca_dev->usb_buf[0];
+		sd->params.vpVersion.vpRevision = gspca_dev->usb_buf[1];
+		sd->params.vpVersion.cameraHeadID =
+			gspca_dev->usb_buf[2] | (gspca_dev->usb_buf[3] << 8);
+		break;
+	case CPIA_COMMAND_GetColourParams:
+		sd->params.colourParams.brightness = gspca_dev->usb_buf[0];
+		sd->params.colourParams.contrast = gspca_dev->usb_buf[1];
+		sd->params.colourParams.saturation = gspca_dev->usb_buf[2];
+		break;
+	case CPIA_COMMAND_GetColourBalance:
+		sd->params.colourBalance.redGain = gspca_dev->usb_buf[0];
+		sd->params.colourBalance.greenGain = gspca_dev->usb_buf[1];
+		sd->params.colourBalance.blueGain = gspca_dev->usb_buf[2];
+		break;
+	case CPIA_COMMAND_GetExposure:
+		sd->params.exposure.gain = gspca_dev->usb_buf[0];
+		sd->params.exposure.fineExp = gspca_dev->usb_buf[1];
+		sd->params.exposure.coarseExpLo = gspca_dev->usb_buf[2];
+		sd->params.exposure.coarseExpHi = gspca_dev->usb_buf[3];
+		sd->params.exposure.redComp = gspca_dev->usb_buf[4];
+		sd->params.exposure.green1Comp = gspca_dev->usb_buf[5];
+		sd->params.exposure.green2Comp = gspca_dev->usb_buf[6];
+		sd->params.exposure.blueComp = gspca_dev->usb_buf[7];
+		break;
+
+	case CPIA_COMMAND_ReadMCPorts:
+		/* test button press */
+		a = ((gspca_dev->usb_buf[1] & 0x02) == 0);
+		if (a != sd->params.qx3.button) {
+#if IS_ENABLED(CONFIG_INPUT)
+			input_report_key(gspca_dev->input_dev, KEY_CAMERA, a);
+			input_sync(gspca_dev->input_dev);
+#endif
+	        	sd->params.qx3.button = a;
+		}
+		if (sd->params.qx3.button) {
+			/* button pressed - unlock the latch */
+			do_command(gspca_dev, CPIA_COMMAND_WriteMCPort,
+				   3, 0xdf, 0xdf, 0);
+			do_command(gspca_dev, CPIA_COMMAND_WriteMCPort,
+				   3, 0xff, 0xff, 0);
+		}
+
+		/* test whether microscope is cradled */
+		sd->params.qx3.cradled = ((gspca_dev->usb_buf[2] & 0x40) == 0);
+		break;
+	}
+
+	return 0;
+}
+
+/* send a command to the camera with an additional data transaction */
+static int do_command_extended(struct gspca_dev *gspca_dev, u16 command,
+			       u8 a, u8 b, u8 c, u8 d,
+			       u8 e, u8 f, u8 g, u8 h,
+			       u8 i, u8 j, u8 k, u8 l)
+{
+	u8 cmd[8];
+
+	cmd[0] = command >> 8;
+	cmd[1] = command & 0xff;
+	cmd[2] = a;
+	cmd[3] = b;
+	cmd[4] = c;
+	cmd[5] = d;
+	cmd[6] = 8;
+	cmd[7] = 0;
+	gspca_dev->usb_buf[0] = e;
+	gspca_dev->usb_buf[1] = f;
+	gspca_dev->usb_buf[2] = g;
+	gspca_dev->usb_buf[3] = h;
+	gspca_dev->usb_buf[4] = i;
+	gspca_dev->usb_buf[5] = j;
+	gspca_dev->usb_buf[6] = k;
+	gspca_dev->usb_buf[7] = l;
+
+	return cpia_usb_transferCmd(gspca_dev, cmd);
+}
+
+/*  find_over_exposure
+ *  Finds a suitable value of OverExposure for use with SetFlickerCtrl
+ *  Some calculation is required because this value changes with the brightness
+ *  set with SetColourParameters
+ *
+ *  Parameters: Brightness - last brightness value set with SetColourParameters
+ *
+ *  Returns: OverExposure value to use with SetFlickerCtrl
+ */
+#define FLICKER_MAX_EXPOSURE                    250
+#define FLICKER_ALLOWABLE_OVER_EXPOSURE         146
+#define FLICKER_BRIGHTNESS_CONSTANT             59
+static int find_over_exposure(int brightness)
+{
+	int MaxAllowableOverExposure, OverExposure;
+
+	MaxAllowableOverExposure = FLICKER_MAX_EXPOSURE - brightness -
+				   FLICKER_BRIGHTNESS_CONSTANT;
+
+	if (MaxAllowableOverExposure < FLICKER_ALLOWABLE_OVER_EXPOSURE)
+		OverExposure = MaxAllowableOverExposure;
+	else
+		OverExposure = FLICKER_ALLOWABLE_OVER_EXPOSURE;
+
+	return OverExposure;
+}
+#undef FLICKER_MAX_EXPOSURE
+#undef FLICKER_ALLOWABLE_OVER_EXPOSURE
+#undef FLICKER_BRIGHTNESS_CONSTANT
+
+/* initialise cam_data structure  */
+static void reset_camera_params(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct cam_params *params = &sd->params;
+
+	/* The following parameter values are the defaults from
+	 * "Software Developer's Guide for CPiA Cameras".  Any changes
+	 * to the defaults are noted in comments. */
+	params->colourParams.brightness = BRIGHTNESS_DEF;
+	params->colourParams.contrast = CONTRAST_DEF;
+	params->colourParams.saturation = SATURATION_DEF;
+	params->exposure.gainMode = 4;
+	params->exposure.expMode = 2;		/* AEC */
+	params->exposure.compMode = 1;
+	params->exposure.centreWeight = 1;
+	params->exposure.gain = 0;
+	params->exposure.fineExp = 0;
+	params->exposure.coarseExpLo = 185;
+	params->exposure.coarseExpHi = 0;
+	params->exposure.redComp = COMP_RED;
+	params->exposure.green1Comp = COMP_GREEN1;
+	params->exposure.green2Comp = COMP_GREEN2;
+	params->exposure.blueComp = COMP_BLUE;
+	params->colourBalance.balanceMode = 2;	/* ACB */
+	params->colourBalance.redGain = 32;
+	params->colourBalance.greenGain = 6;
+	params->colourBalance.blueGain = 92;
+	params->apcor.gain1 = 0x18;
+	params->apcor.gain2 = 0x16;
+	params->apcor.gain4 = 0x24;
+	params->apcor.gain8 = 0x34;
+	params->vlOffset.gain1 = 20;
+	params->vlOffset.gain2 = 24;
+	params->vlOffset.gain4 = 26;
+	params->vlOffset.gain8 = 26;
+	params->compressionParams.hysteresis = 3;
+	params->compressionParams.threshMax = 11;
+	params->compressionParams.smallStep = 1;
+	params->compressionParams.largeStep = 3;
+	params->compressionParams.decimationHysteresis = 2;
+	params->compressionParams.frDiffStepThresh = 5;
+	params->compressionParams.qDiffStepThresh = 3;
+	params->compressionParams.decimationThreshMod = 2;
+	/* End of default values from Software Developer's Guide */
+
+	/* Set Sensor FPS to 15fps. This seems better than 30fps
+	 * for indoor lighting. */
+	params->sensorFps.divisor = 1;
+	params->sensorFps.baserate = 1;
+
+	params->flickerControl.flickerMode = 0;
+	params->flickerControl.disabled = 1;
+	params->flickerControl.coarseJump =
+		flicker_jumps[sd->mainsFreq]
+			     [params->sensorFps.baserate]
+			     [params->sensorFps.divisor];
+	params->flickerControl.allowableOverExposure =
+		find_over_exposure(params->colourParams.brightness);
+
+	params->yuvThreshold.yThreshold = 6; /* From windows driver */
+	params->yuvThreshold.uvThreshold = 6; /* From windows driver */
+
+	params->format.subSample = SUBSAMPLE_420;
+	params->format.yuvOrder = YUVORDER_YUYV;
+
+	params->compression.mode = CPIA_COMPRESSION_AUTO;
+	params->compression.decimation = NO_DECIMATION;
+
+	params->compressionTarget.frTargeting = COMP_TARGET_DEF;
+	params->compressionTarget.targetFR = 15; /* From windows driver */
+	params->compressionTarget.targetQ = 5; /* From windows driver */
+
+	params->qx3.qx3_detected = 0;
+	params->qx3.toplight = 0;
+	params->qx3.bottomlight = 0;
+	params->qx3.button = 0;
+	params->qx3.cradled = 0;
+}
+
+static void printstatus(struct gspca_dev *gspca_dev, struct cam_params *params)
+{
+	PDEBUG(D_PROBE, "status: %02x %02x %02x %02x %02x %02x %02x %02x",
+	       params->status.systemState, params->status.grabState,
+	       params->status.streamState, params->status.fatalError,
+	       params->status.cmdError, params->status.debugFlags,
+	       params->status.vpStatus, params->status.errorCode);
+}
+
+static int goto_low_power(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int ret;
+
+	ret = do_command(gspca_dev, CPIA_COMMAND_GotoLoPower, 0, 0, 0, 0);
+	if (ret)
+		return ret;
+
+	ret = do_command(gspca_dev, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
+	if (ret)
+		return ret;
+
+	if (sd->params.status.systemState != LO_POWER_STATE) {
+		if (sd->params.status.systemState != WARM_BOOT_STATE) {
+			PERR("unexpected state after lo power cmd: %02x",
+			     sd->params.status.systemState);
+			printstatus(gspca_dev, &sd->params);
+		}
+		return -EIO;
+	}
+
+	PDEBUG(D_CONF, "camera now in LOW power state");
+	return 0;
+}
+
+static int goto_high_power(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int ret;
+
+	ret = do_command(gspca_dev, CPIA_COMMAND_GotoHiPower, 0, 0, 0, 0);
+	if (ret)
+		return ret;
+
+	msleep_interruptible(40);	/* windows driver does it too */
+
+	if (signal_pending(current))
+		return -EINTR;
+
+	ret = do_command(gspca_dev, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
+	if (ret)
+		return ret;
+
+	if (sd->params.status.systemState != HI_POWER_STATE) {
+		PERR("unexpected state after hi power cmd: %02x",
+		     sd->params.status.systemState);
+		printstatus(gspca_dev, &sd->params);
+		return -EIO;
+	}
+
+	PDEBUG(D_CONF, "camera now in HIGH power state");
+	return 0;
+}
+
+static int get_version_information(struct gspca_dev *gspca_dev)
+{
+	int ret;
+
+	/* GetCPIAVersion */
+	ret = do_command(gspca_dev, CPIA_COMMAND_GetCPIAVersion, 0, 0, 0, 0);
+	if (ret)
+		return ret;
+
+	/* GetPnPID */
+	return do_command(gspca_dev, CPIA_COMMAND_GetPnPID, 0, 0, 0, 0);
+}
+
+static int save_camera_state(struct gspca_dev *gspca_dev)
+{
+	int ret;
+
+	ret = do_command(gspca_dev, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0);
+	if (ret)
+		return ret;
+
+	return do_command(gspca_dev, CPIA_COMMAND_GetExposure, 0, 0, 0, 0);
+}
+
+static int command_setformat(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int ret;
+
+	ret = do_command(gspca_dev, CPIA_COMMAND_SetFormat,
+			 sd->params.format.videoSize,
+			 sd->params.format.subSample,
+			 sd->params.format.yuvOrder, 0);
+	if (ret)
+		return ret;
+
+	return do_command(gspca_dev, CPIA_COMMAND_SetROI,
+			  sd->params.roi.colStart, sd->params.roi.colEnd,
+			  sd->params.roi.rowStart, sd->params.roi.rowEnd);
+}
+
+static int command_setcolourparams(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	return do_command(gspca_dev, CPIA_COMMAND_SetColourParams,
+			  sd->params.colourParams.brightness,
+			  sd->params.colourParams.contrast,
+			  sd->params.colourParams.saturation, 0);
+}
+
+static int command_setapcor(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	return do_command(gspca_dev, CPIA_COMMAND_SetApcor,
+			  sd->params.apcor.gain1,
+			  sd->params.apcor.gain2,
+			  sd->params.apcor.gain4,
+			  sd->params.apcor.gain8);
+}
+
+static int command_setvloffset(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	return do_command(gspca_dev, CPIA_COMMAND_SetVLOffset,
+			  sd->params.vlOffset.gain1,
+			  sd->params.vlOffset.gain2,
+			  sd->params.vlOffset.gain4,
+			  sd->params.vlOffset.gain8);
+}
+
+static int command_setexposure(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int ret;
+
+	ret = do_command_extended(gspca_dev, CPIA_COMMAND_SetExposure,
+				  sd->params.exposure.gainMode,
+				  1,
+				  sd->params.exposure.compMode,
+				  sd->params.exposure.centreWeight,
+				  sd->params.exposure.gain,
+				  sd->params.exposure.fineExp,
+				  sd->params.exposure.coarseExpLo,
+				  sd->params.exposure.coarseExpHi,
+				  sd->params.exposure.redComp,
+				  sd->params.exposure.green1Comp,
+				  sd->params.exposure.green2Comp,
+				  sd->params.exposure.blueComp);
+	if (ret)
+		return ret;
+
+	if (sd->params.exposure.expMode != 1) {
+		ret = do_command_extended(gspca_dev, CPIA_COMMAND_SetExposure,
+					  0,
+					  sd->params.exposure.expMode,
+					  0, 0,
+					  sd->params.exposure.gain,
+					  sd->params.exposure.fineExp,
+					  sd->params.exposure.coarseExpLo,
+					  sd->params.exposure.coarseExpHi,
+					  0, 0, 0, 0);
+	}
+
+	return ret;
+}
+
+static int command_setcolourbalance(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (sd->params.colourBalance.balanceMode == 1) {
+		int ret;
+
+		ret = do_command(gspca_dev, CPIA_COMMAND_SetColourBalance,
+				 1,
+				 sd->params.colourBalance.redGain,
+				 sd->params.colourBalance.greenGain,
+				 sd->params.colourBalance.blueGain);
+		if (ret)
+			return ret;
+
+		return do_command(gspca_dev, CPIA_COMMAND_SetColourBalance,
+				  3, 0, 0, 0);
+	}
+	if (sd->params.colourBalance.balanceMode == 2) {
+		return do_command(gspca_dev, CPIA_COMMAND_SetColourBalance,
+				  2, 0, 0, 0);
+	}
+	if (sd->params.colourBalance.balanceMode == 3) {
+		return do_command(gspca_dev, CPIA_COMMAND_SetColourBalance,
+				  3, 0, 0, 0);
+	}
+
+	return -EINVAL;
+}
+
+static int command_setcompressiontarget(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	return do_command(gspca_dev, CPIA_COMMAND_SetCompressionTarget,
+			  sd->params.compressionTarget.frTargeting,
+			  sd->params.compressionTarget.targetFR,
+			  sd->params.compressionTarget.targetQ, 0);
+}
+
+static int command_setyuvtresh(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	return do_command(gspca_dev, CPIA_COMMAND_SetYUVThresh,
+			  sd->params.yuvThreshold.yThreshold,
+			  sd->params.yuvThreshold.uvThreshold, 0, 0);
+}
+
+static int command_setcompressionparams(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	return do_command_extended(gspca_dev,
+			    CPIA_COMMAND_SetCompressionParams,
+			    0, 0, 0, 0,
+			    sd->params.compressionParams.hysteresis,
+			    sd->params.compressionParams.threshMax,
+			    sd->params.compressionParams.smallStep,
+			    sd->params.compressionParams.largeStep,
+			    sd->params.compressionParams.decimationHysteresis,
+			    sd->params.compressionParams.frDiffStepThresh,
+			    sd->params.compressionParams.qDiffStepThresh,
+			    sd->params.compressionParams.decimationThreshMod);
+}
+
+static int command_setcompression(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	return do_command(gspca_dev, CPIA_COMMAND_SetCompression,
+			  sd->params.compression.mode,
+			  sd->params.compression.decimation, 0, 0);
+}
+
+static int command_setsensorfps(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	return do_command(gspca_dev, CPIA_COMMAND_SetSensorFPS,
+			  sd->params.sensorFps.divisor,
+			  sd->params.sensorFps.baserate, 0, 0);
+}
+
+static int command_setflickerctrl(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	return do_command(gspca_dev, CPIA_COMMAND_SetFlickerCtrl,
+			  sd->params.flickerControl.flickerMode,
+			  sd->params.flickerControl.coarseJump,
+			  sd->params.flickerControl.allowableOverExposure,
+			  0);
+}
+
+static int command_setecptiming(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	return do_command(gspca_dev, CPIA_COMMAND_SetECPTiming,
+			  sd->params.ecpTiming, 0, 0, 0);
+}
+
+static int command_pause(struct gspca_dev *gspca_dev)
+{
+	return do_command(gspca_dev, CPIA_COMMAND_EndStreamCap, 0, 0, 0, 0);
+}
+
+static int command_resume(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	return do_command(gspca_dev, CPIA_COMMAND_InitStreamCap,
+			  0, sd->params.streamStartLine, 0, 0);
+}
+
+static int command_setlights(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int ret, p1, p2;
+
+	p1 = (sd->params.qx3.bottomlight == 0) << 1;
+	p2 = (sd->params.qx3.toplight == 0) << 3;
+
+	ret = do_command(gspca_dev, CPIA_COMMAND_WriteVCReg,
+			 0x90, 0x8f, 0x50, 0);
+	if (ret)
+		return ret;
+
+	return do_command(gspca_dev, CPIA_COMMAND_WriteMCPort, 2, 0,
+			  p1 | p2 | 0xe0, 0);
+}
+
+static int set_flicker(struct gspca_dev *gspca_dev, int on, int apply)
+{
+	/* Everything in here is from the Windows driver */
+/* define for compgain calculation */
+#if 0
+#define COMPGAIN(base, curexp, newexp) \
+    (u8) ((((float) base - 128.0) * ((float) curexp / (float) newexp)) + 128.5)
+#define EXP_FROM_COMP(basecomp, curcomp, curexp) \
+    (u16)((float)curexp * (float)(u8)(curcomp + 128) / \
+    (float)(u8)(basecomp - 128))
+#else
+  /* equivalent functions without floating point math */
+#define COMPGAIN(base, curexp, newexp) \
+    (u8)(128 + (((u32)(2*(base-128)*curexp + newexp)) / (2 * newexp)))
+#define EXP_FROM_COMP(basecomp, curcomp, curexp) \
+    (u16)(((u32)(curexp * (u8)(curcomp + 128)) / (u8)(basecomp - 128)))
+#endif
+
+	struct sd *sd = (struct sd *) gspca_dev;
+	int currentexp = sd->params.exposure.coarseExpLo +
+			 sd->params.exposure.coarseExpHi * 256;
+	int ret, startexp;
+
+	if (on) {
+		int cj = sd->params.flickerControl.coarseJump;
+		sd->params.flickerControl.flickerMode = 1;
+		sd->params.flickerControl.disabled = 0;
+		if (sd->params.exposure.expMode != 2) {
+			sd->params.exposure.expMode = 2;
+			sd->exposure_status = EXPOSURE_NORMAL;
+		}
+		currentexp = currentexp << sd->params.exposure.gain;
+		sd->params.exposure.gain = 0;
+		/* round down current exposure to nearest value */
+		startexp = (currentexp + ROUND_UP_EXP_FOR_FLICKER) / cj;
+		if (startexp < 1)
+			startexp = 1;
+		startexp = (startexp * cj) - 1;
+		if (FIRMWARE_VERSION(1, 2))
+			while (startexp > MAX_EXP_102)
+				startexp -= cj;
+		else
+			while (startexp > MAX_EXP)
+				startexp -= cj;
+		sd->params.exposure.coarseExpLo = startexp & 0xff;
+		sd->params.exposure.coarseExpHi = startexp >> 8;
+		if (currentexp > startexp) {
+			if (currentexp > (2 * startexp))
+				currentexp = 2 * startexp;
+			sd->params.exposure.redComp =
+				COMPGAIN(COMP_RED, currentexp, startexp);
+			sd->params.exposure.green1Comp =
+				COMPGAIN(COMP_GREEN1, currentexp, startexp);
+			sd->params.exposure.green2Comp =
+				COMPGAIN(COMP_GREEN2, currentexp, startexp);
+			sd->params.exposure.blueComp =
+				COMPGAIN(COMP_BLUE, currentexp, startexp);
+		} else {
+			sd->params.exposure.redComp = COMP_RED;
+			sd->params.exposure.green1Comp = COMP_GREEN1;
+			sd->params.exposure.green2Comp = COMP_GREEN2;
+			sd->params.exposure.blueComp = COMP_BLUE;
+		}
+		if (FIRMWARE_VERSION(1, 2))
+			sd->params.exposure.compMode = 0;
+		else
+			sd->params.exposure.compMode = 1;
+
+		sd->params.apcor.gain1 = 0x18;
+		sd->params.apcor.gain2 = 0x18;
+		sd->params.apcor.gain4 = 0x16;
+		sd->params.apcor.gain8 = 0x14;
+	} else {
+		sd->params.flickerControl.flickerMode = 0;
+		sd->params.flickerControl.disabled = 1;
+		/* Average equivalent coarse for each comp channel */
+		startexp = EXP_FROM_COMP(COMP_RED,
+				sd->params.exposure.redComp, currentexp);
+		startexp += EXP_FROM_COMP(COMP_GREEN1,
+				sd->params.exposure.green1Comp, currentexp);
+		startexp += EXP_FROM_COMP(COMP_GREEN2,
+				sd->params.exposure.green2Comp, currentexp);
+		startexp += EXP_FROM_COMP(COMP_BLUE,
+				sd->params.exposure.blueComp, currentexp);
+		startexp = startexp >> 2;
+		while (startexp > MAX_EXP && sd->params.exposure.gain <
+		       sd->params.exposure.gainMode - 1) {
+			startexp = startexp >> 1;
+			++sd->params.exposure.gain;
+		}
+		if (FIRMWARE_VERSION(1, 2) && startexp > MAX_EXP_102)
+			startexp = MAX_EXP_102;
+		if (startexp > MAX_EXP)
+			startexp = MAX_EXP;
+		sd->params.exposure.coarseExpLo = startexp & 0xff;
+		sd->params.exposure.coarseExpHi = startexp >> 8;
+		sd->params.exposure.redComp = COMP_RED;
+		sd->params.exposure.green1Comp = COMP_GREEN1;
+		sd->params.exposure.green2Comp = COMP_GREEN2;
+		sd->params.exposure.blueComp = COMP_BLUE;
+		sd->params.exposure.compMode = 1;
+		sd->params.apcor.gain1 = 0x18;
+		sd->params.apcor.gain2 = 0x16;
+		sd->params.apcor.gain4 = 0x24;
+		sd->params.apcor.gain8 = 0x34;
+	}
+	sd->params.vlOffset.gain1 = 20;
+	sd->params.vlOffset.gain2 = 24;
+	sd->params.vlOffset.gain4 = 26;
+	sd->params.vlOffset.gain8 = 26;
+
+	if (apply) {
+		ret = command_setexposure(gspca_dev);
+		if (ret)
+			return ret;
+
+		ret = command_setapcor(gspca_dev);
+		if (ret)
+			return ret;
+
+		ret = command_setvloffset(gspca_dev);
+		if (ret)
+			return ret;
+
+		ret = command_setflickerctrl(gspca_dev);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+#undef EXP_FROM_COMP
+#undef COMPGAIN
+}
+
+/* monitor the exposure and adjust the sensor frame rate if needed */
+static void monitor_exposure(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u8 exp_acc, bcomp, cmd[8];
+	int ret, light_exp, dark_exp, very_dark_exp;
+	int old_exposure, new_exposure, framerate;
+	int setfps = 0, setexp = 0, setflicker = 0;
+
+	/* get necessary stats and register settings from camera */
+	/* do_command can't handle this, so do it ourselves */
+	cmd[0] = CPIA_COMMAND_ReadVPRegs >> 8;
+	cmd[1] = CPIA_COMMAND_ReadVPRegs & 0xff;
+	cmd[2] = 30;
+	cmd[3] = 4;
+	cmd[4] = 9;
+	cmd[5] = 8;
+	cmd[6] = 8;
+	cmd[7] = 0;
+	ret = cpia_usb_transferCmd(gspca_dev, cmd);
+	if (ret) {
+		pr_err("ReadVPRegs(30,4,9,8) - failed: %d\n", ret);
+		return;
+	}
+	exp_acc = gspca_dev->usb_buf[0];
+	bcomp = gspca_dev->usb_buf[1];
+
+	light_exp = sd->params.colourParams.brightness +
+		    TC - 50 + EXP_ACC_LIGHT;
+	if (light_exp > 255)
+		light_exp = 255;
+	dark_exp = sd->params.colourParams.brightness +
+		   TC - 50 - EXP_ACC_DARK;
+	if (dark_exp < 0)
+		dark_exp = 0;
+	very_dark_exp = dark_exp / 2;
+
+	old_exposure = sd->params.exposure.coarseExpHi * 256 +
+		       sd->params.exposure.coarseExpLo;
+
+	if (!sd->params.flickerControl.disabled) {
+		/* Flicker control on */
+		int max_comp = FIRMWARE_VERSION(1, 2) ? MAX_COMP :
+							HIGH_COMP_102;
+		bcomp += 128;	/* decode */
+		if (bcomp >= max_comp && exp_acc < dark_exp) {
+			/* dark */
+			if (exp_acc < very_dark_exp) {
+				/* very dark */
+				if (sd->exposure_status == EXPOSURE_VERY_DARK)
+					++sd->exposure_count;
+				else {
+					sd->exposure_status =
+						EXPOSURE_VERY_DARK;
+					sd->exposure_count = 1;
+				}
+			} else {
+				/* just dark */
+				if (sd->exposure_status == EXPOSURE_DARK)
+					++sd->exposure_count;
+				else {
+					sd->exposure_status = EXPOSURE_DARK;
+					sd->exposure_count = 1;
+				}
+			}
+		} else if (old_exposure <= LOW_EXP || exp_acc > light_exp) {
+			/* light */
+			if (old_exposure <= VERY_LOW_EXP) {
+				/* very light */
+				if (sd->exposure_status == EXPOSURE_VERY_LIGHT)
+					++sd->exposure_count;
+				else {
+					sd->exposure_status =
+						EXPOSURE_VERY_LIGHT;
+					sd->exposure_count = 1;
+				}
+			} else {
+				/* just light */
+				if (sd->exposure_status == EXPOSURE_LIGHT)
+					++sd->exposure_count;
+				else {
+					sd->exposure_status = EXPOSURE_LIGHT;
+					sd->exposure_count = 1;
+				}
+			}
+		} else {
+			/* not dark or light */
+			sd->exposure_status = EXPOSURE_NORMAL;
+		}
+	} else {
+		/* Flicker control off */
+		if (old_exposure >= MAX_EXP && exp_acc < dark_exp) {
+			/* dark */
+			if (exp_acc < very_dark_exp) {
+				/* very dark */
+				if (sd->exposure_status == EXPOSURE_VERY_DARK)
+					++sd->exposure_count;
+				else {
+					sd->exposure_status =
+						EXPOSURE_VERY_DARK;
+					sd->exposure_count = 1;
+				}
+			} else {
+				/* just dark */
+				if (sd->exposure_status == EXPOSURE_DARK)
+					++sd->exposure_count;
+				else {
+					sd->exposure_status = EXPOSURE_DARK;
+					sd->exposure_count = 1;
+				}
+			}
+		} else if (old_exposure <= LOW_EXP || exp_acc > light_exp) {
+			/* light */
+			if (old_exposure <= VERY_LOW_EXP) {
+				/* very light */
+				if (sd->exposure_status == EXPOSURE_VERY_LIGHT)
+					++sd->exposure_count;
+				else {
+					sd->exposure_status =
+						EXPOSURE_VERY_LIGHT;
+					sd->exposure_count = 1;
+				}
+			} else {
+				/* just light */
+				if (sd->exposure_status == EXPOSURE_LIGHT)
+					++sd->exposure_count;
+				else {
+					sd->exposure_status = EXPOSURE_LIGHT;
+					sd->exposure_count = 1;
+				}
+			}
+		} else {
+			/* not dark or light */
+			sd->exposure_status = EXPOSURE_NORMAL;
+		}
+	}
+
+	framerate = atomic_read(&sd->fps);
+	if (framerate > 30 || framerate < 1)
+		framerate = 1;
+
+	if (!sd->params.flickerControl.disabled) {
+		/* Flicker control on */
+		if ((sd->exposure_status == EXPOSURE_VERY_DARK ||
+		     sd->exposure_status == EXPOSURE_DARK) &&
+		    sd->exposure_count >= DARK_TIME * framerate &&
+		    sd->params.sensorFps.divisor < 2) {
+
+			/* dark for too long */
+			++sd->params.sensorFps.divisor;
+			setfps = 1;
+
+			sd->params.flickerControl.coarseJump =
+				flicker_jumps[sd->mainsFreq]
+					     [sd->params.sensorFps.baserate]
+					     [sd->params.sensorFps.divisor];
+			setflicker = 1;
+
+			new_exposure = sd->params.flickerControl.coarseJump-1;
+			while (new_exposure < old_exposure / 2)
+				new_exposure +=
+					sd->params.flickerControl.coarseJump;
+			sd->params.exposure.coarseExpLo = new_exposure & 0xff;
+			sd->params.exposure.coarseExpHi = new_exposure >> 8;
+			setexp = 1;
+			sd->exposure_status = EXPOSURE_NORMAL;
+			PDEBUG(D_CONF, "Automatically decreasing sensor_fps");
+
+		} else if ((sd->exposure_status == EXPOSURE_VERY_LIGHT ||
+			    sd->exposure_status == EXPOSURE_LIGHT) &&
+			   sd->exposure_count >= LIGHT_TIME * framerate &&
+			   sd->params.sensorFps.divisor > 0) {
+
+			/* light for too long */
+			int max_exp = FIRMWARE_VERSION(1, 2) ? MAX_EXP_102 :
+							       MAX_EXP;
+			--sd->params.sensorFps.divisor;
+			setfps = 1;
+
+			sd->params.flickerControl.coarseJump =
+				flicker_jumps[sd->mainsFreq]
+					     [sd->params.sensorFps.baserate]
+					     [sd->params.sensorFps.divisor];
+			setflicker = 1;
+
+			new_exposure = sd->params.flickerControl.coarseJump-1;
+			while (new_exposure < 2 * old_exposure &&
+			       new_exposure +
+			       sd->params.flickerControl.coarseJump < max_exp)
+				new_exposure +=
+					sd->params.flickerControl.coarseJump;
+			sd->params.exposure.coarseExpLo = new_exposure & 0xff;
+			sd->params.exposure.coarseExpHi = new_exposure >> 8;
+			setexp = 1;
+			sd->exposure_status = EXPOSURE_NORMAL;
+			PDEBUG(D_CONF, "Automatically increasing sensor_fps");
+		}
+	} else {
+		/* Flicker control off */
+		if ((sd->exposure_status == EXPOSURE_VERY_DARK ||
+		     sd->exposure_status == EXPOSURE_DARK) &&
+		    sd->exposure_count >= DARK_TIME * framerate &&
+		    sd->params.sensorFps.divisor < 2) {
+
+			/* dark for too long */
+			++sd->params.sensorFps.divisor;
+			setfps = 1;
+
+			if (sd->params.exposure.gain > 0) {
+				--sd->params.exposure.gain;
+				setexp = 1;
+			}
+			sd->exposure_status = EXPOSURE_NORMAL;
+			PDEBUG(D_CONF, "Automatically decreasing sensor_fps");
+
+		} else if ((sd->exposure_status == EXPOSURE_VERY_LIGHT ||
+			    sd->exposure_status == EXPOSURE_LIGHT) &&
+			   sd->exposure_count >= LIGHT_TIME * framerate &&
+			   sd->params.sensorFps.divisor > 0) {
+
+			/* light for too long */
+			--sd->params.sensorFps.divisor;
+			setfps = 1;
+
+			if (sd->params.exposure.gain <
+			    sd->params.exposure.gainMode - 1) {
+				++sd->params.exposure.gain;
+				setexp = 1;
+			}
+			sd->exposure_status = EXPOSURE_NORMAL;
+			PDEBUG(D_CONF, "Automatically increasing sensor_fps");
+		}
+	}
+
+	if (setexp)
+		command_setexposure(gspca_dev);
+
+	if (setfps)
+		command_setsensorfps(gspca_dev);
+
+	if (setflicker)
+		command_setflickerctrl(gspca_dev);
+}
+
+/*-----------------------------------------------------------------*/
+/* if flicker is switched off, this function switches it back on.It checks,
+   however, that conditions are suitable before restarting it.
+   This should only be called for firmware version 1.2.
+
+   It also adjust the colour balance when an exposure step is detected - as
+   long as flicker is running
+*/
+static void restart_flicker(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int cam_exposure, old_exp;
+
+	if (!FIRMWARE_VERSION(1, 2))
+		return;
+
+	cam_exposure = atomic_read(&sd->cam_exposure);
+
+	if (sd->params.flickerControl.flickerMode == 0 ||
+	    cam_exposure == 0)
+		return;
+
+	old_exp = sd->params.exposure.coarseExpLo +
+		  sd->params.exposure.coarseExpHi*256;
+	/*
+	  see how far away camera exposure is from a valid
+	  flicker exposure value
+	*/
+	cam_exposure %= sd->params.flickerControl.coarseJump;
+	if (!sd->params.flickerControl.disabled &&
+	    cam_exposure <= sd->params.flickerControl.coarseJump - 3) {
+		/* Flicker control auto-disabled */
+		sd->params.flickerControl.disabled = 1;
+	}
+
+	if (sd->params.flickerControl.disabled &&
+	    old_exp > sd->params.flickerControl.coarseJump +
+		      ROUND_UP_EXP_FOR_FLICKER) {
+		/* exposure is now high enough to switch
+		   flicker control back on */
+		set_flicker(gspca_dev, 1, 1);
+	}
+}
+
+/* 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;
+
+	sd->mainsFreq = FREQ_DEF == V4L2_CID_POWER_LINE_FREQUENCY_60HZ;
+	reset_camera_params(gspca_dev);
+
+	PDEBUG(D_PROBE, "cpia CPiA camera detected (vid/pid 0x%04X:0x%04X)",
+	       id->idVendor, id->idProduct);
+
+	cam = &gspca_dev->cam;
+	cam->cam_mode = mode;
+	cam->nmodes = ARRAY_SIZE(mode);
+
+	goto_low_power(gspca_dev);
+	/* Check the firmware version. */
+	sd->params.version.firmwareVersion = 0;
+	get_version_information(gspca_dev);
+	if (sd->params.version.firmwareVersion != 1) {
+		PERR("only firmware version 1 is supported (got: %d)",
+		     sd->params.version.firmwareVersion);
+		return -ENODEV;
+	}
+
+	/* A bug in firmware 1-02 limits gainMode to 2 */
+	if (sd->params.version.firmwareRevision <= 2 &&
+	    sd->params.exposure.gainMode > 2) {
+		sd->params.exposure.gainMode = 2;
+	}
+
+	/* set QX3 detected flag */
+	sd->params.qx3.qx3_detected = (sd->params.pnpID.vendor == 0x0813 &&
+				       sd->params.pnpID.product == 0x0001);
+	return 0;
+}
+
+/* -- start the camera -- */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int priv, ret;
+
+	/* Start the camera in low power mode */
+	if (goto_low_power(gspca_dev)) {
+		if (sd->params.status.systemState != WARM_BOOT_STATE) {
+			PERR("unexpected systemstate: %02x",
+			     sd->params.status.systemState);
+			printstatus(gspca_dev, &sd->params);
+			return -ENODEV;
+		}
+
+		/* FIXME: this is just dirty trial and error */
+		ret = goto_high_power(gspca_dev);
+		if (ret)
+			return ret;
+
+		ret = do_command(gspca_dev, CPIA_COMMAND_DiscardFrame,
+				 0, 0, 0, 0);
+		if (ret)
+			return ret;
+
+		ret = goto_low_power(gspca_dev);
+		if (ret)
+			return ret;
+	}
+
+	/* procedure described in developer's guide p3-28 */
+
+	/* Check the firmware version. */
+	sd->params.version.firmwareVersion = 0;
+	get_version_information(gspca_dev);
+
+	/* The fatal error checking should be done after
+	 * the camera powers up (developer's guide p 3-38) */
+
+	/* Set streamState before transition to high power to avoid bug
+	 * in firmware 1-02 */
+	ret = do_command(gspca_dev, CPIA_COMMAND_ModifyCameraStatus,
+			 STREAMSTATE, 0, STREAM_NOT_READY, 0);
+	if (ret)
+		return ret;
+
+	/* GotoHiPower */
+	ret = goto_high_power(gspca_dev);
+	if (ret)
+		return ret;
+
+	/* Check the camera status */
+	ret = do_command(gspca_dev, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
+	if (ret)
+		return ret;
+
+	if (sd->params.status.fatalError) {
+		PERR("fatal_error: %04x, vp_status: %04x",
+		     sd->params.status.fatalError, sd->params.status.vpStatus);
+		return -EIO;
+	}
+
+	/* VPVersion can't be retrieved before the camera is in HiPower,
+	 * so get it here instead of in get_version_information. */
+	ret = do_command(gspca_dev, CPIA_COMMAND_GetVPVersion, 0, 0, 0, 0);
+	if (ret)
+		return ret;
+
+	/* Determine video mode settings */
+	sd->params.streamStartLine = 120;
+
+	priv = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
+	if (priv & 0x01) { /* crop */
+		sd->params.roi.colStart = 2;
+		sd->params.roi.rowStart = 6;
+	} else {
+		sd->params.roi.colStart = 0;
+		sd->params.roi.rowStart = 0;
+	}
+
+	if (priv & 0x02) { /* quarter */
+		sd->params.format.videoSize = VIDEOSIZE_QCIF;
+		sd->params.roi.colStart /= 2;
+		sd->params.roi.rowStart /= 2;
+		sd->params.streamStartLine /= 2;
+	} else
+		sd->params.format.videoSize = VIDEOSIZE_CIF;
+
+	sd->params.roi.colEnd = sd->params.roi.colStart +
+				(gspca_dev->pixfmt.width >> 3);
+	sd->params.roi.rowEnd = sd->params.roi.rowStart +
+				(gspca_dev->pixfmt.height >> 2);
+
+	/* And now set the camera to a known state */
+	ret = do_command(gspca_dev, CPIA_COMMAND_SetGrabMode,
+			 CPIA_GRAB_CONTINEOUS, 0, 0, 0);
+	if (ret)
+		return ret;
+	/* We start with compression disabled, as we need one uncompressed
+	   frame to handle later compressed frames */
+	ret = do_command(gspca_dev, CPIA_COMMAND_SetCompression,
+			 CPIA_COMPRESSION_NONE,
+			 NO_DECIMATION, 0, 0);
+	if (ret)
+		return ret;
+	ret = command_setcompressiontarget(gspca_dev);
+	if (ret)
+		return ret;
+	ret = command_setcolourparams(gspca_dev);
+	if (ret)
+		return ret;
+	ret = command_setformat(gspca_dev);
+	if (ret)
+		return ret;
+	ret = command_setyuvtresh(gspca_dev);
+	if (ret)
+		return ret;
+	ret = command_setecptiming(gspca_dev);
+	if (ret)
+		return ret;
+	ret = command_setcompressionparams(gspca_dev);
+	if (ret)
+		return ret;
+	ret = command_setexposure(gspca_dev);
+	if (ret)
+		return ret;
+	ret = command_setcolourbalance(gspca_dev);
+	if (ret)
+		return ret;
+	ret = command_setsensorfps(gspca_dev);
+	if (ret)
+		return ret;
+	ret = command_setapcor(gspca_dev);
+	if (ret)
+		return ret;
+	ret = command_setflickerctrl(gspca_dev);
+	if (ret)
+		return ret;
+	ret = command_setvloffset(gspca_dev);
+	if (ret)
+		return ret;
+
+	/* Start stream */
+	ret = command_resume(gspca_dev);
+	if (ret)
+		return ret;
+
+	/* Wait 6 frames before turning compression on for the sensor to get
+	   all settings and AEC/ACB to settle */
+	sd->first_frame = 6;
+	sd->exposure_status = EXPOSURE_NORMAL;
+	sd->exposure_count = 0;
+	atomic_set(&sd->cam_exposure, 0);
+	atomic_set(&sd->fps, 0);
+
+	return 0;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd __maybe_unused = (struct sd *) gspca_dev;
+
+	command_pause(gspca_dev);
+
+	/* save camera state for later open (developers guide ch 3.5.3) */
+	save_camera_state(gspca_dev);
+
+	/* GotoLoPower */
+	goto_low_power(gspca_dev);
+
+	/* Update the camera status */
+	do_command(gspca_dev, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
+
+#if IS_ENABLED(CONFIG_INPUT)
+	/* If the last button state is pressed, release it now! */
+	if (sd->params.qx3.button) {
+		/* The camera latch will hold the pressed state until we reset
+		   the latch, so we do not reset sd->params.qx3.button now, to
+		   avoid a false keypress being reported the next sd_start */
+		input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
+		input_sync(gspca_dev->input_dev);
+	}
+#endif
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int ret;
+
+	/* Start / Stop the camera to make sure we are talking to
+	   a supported camera, and to get some information from it
+	   to print. */
+	ret = sd_start(gspca_dev);
+	if (ret)
+		return ret;
+
+	/* Ensure the QX3 illuminators' states are restored upon resume,
+	   or disable the illuminator controls, if this isn't a QX3 */
+	if (sd->params.qx3.qx3_detected)
+		command_setlights(gspca_dev);
+
+	sd_stopN(gspca_dev);
+
+	PDEBUG(D_PROBE, "CPIA Version:             %d.%02d (%d.%d)",
+			sd->params.version.firmwareVersion,
+			sd->params.version.firmwareRevision,
+			sd->params.version.vcVersion,
+			sd->params.version.vcRevision);
+	PDEBUG(D_PROBE, "CPIA PnP-ID:              %04x:%04x:%04x",
+			sd->params.pnpID.vendor, sd->params.pnpID.product,
+			sd->params.pnpID.deviceRevision);
+	PDEBUG(D_PROBE, "VP-Version:               %d.%d %04x",
+			sd->params.vpVersion.vpVersion,
+			sd->params.vpVersion.vpRevision,
+			sd->params.vpVersion.cameraHeadID);
+
+	return 0;
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+			u8 *data,
+			int len)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	/* Check for SOF */
+	if (len >= 64 &&
+	    data[0] == MAGIC_0 && data[1] == MAGIC_1 &&
+	    data[16] == sd->params.format.videoSize &&
+	    data[17] == sd->params.format.subSample &&
+	    data[18] == sd->params.format.yuvOrder &&
+	    data[24] == sd->params.roi.colStart &&
+	    data[25] == sd->params.roi.colEnd &&
+	    data[26] == sd->params.roi.rowStart &&
+	    data[27] == sd->params.roi.rowEnd) {
+		u8 *image;
+
+		atomic_set(&sd->cam_exposure, data[39] * 2);
+		atomic_set(&sd->fps, data[41]);
+
+		/* Check for proper EOF for last frame */
+		image = gspca_dev->image;
+		if (image != NULL &&
+		    gspca_dev->image_len > 4 &&
+		    image[gspca_dev->image_len - 4] == 0xff &&
+		    image[gspca_dev->image_len - 3] == 0xff &&
+		    image[gspca_dev->image_len - 2] == 0xff &&
+		    image[gspca_dev->image_len - 1] == 0xff)
+			gspca_frame_add(gspca_dev, LAST_PACKET,
+						NULL, 0);
+
+		gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
+		return;
+	}
+
+	gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+}
+
+static void sd_dq_callback(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	/* Set the normal compression settings once we have captured a
+	   few uncompressed frames (and AEC has hopefully settled) */
+	if (sd->first_frame) {
+		sd->first_frame--;
+		if (sd->first_frame == 0)
+			command_setcompression(gspca_dev);
+	}
+
+	/* Switch flicker control back on if it got turned off */
+	restart_flicker(gspca_dev);
+
+	/* If AEC is enabled, monitor the exposure and
+	   adjust the sensor frame rate if needed */
+	if (sd->params.exposure.expMode == 2)
+		monitor_exposure(gspca_dev);
+
+	/* Update our knowledge of the camera state */
+	do_command(gspca_dev, CPIA_COMMAND_GetExposure, 0, 0, 0, 0);
+	do_command(gspca_dev, CPIA_COMMAND_ReadMCPorts, 0, 0, 0, 0);
+}
+
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct gspca_dev *gspca_dev =
+		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+	struct sd *sd = (struct sd *)gspca_dev;
+
+	gspca_dev->usb_err = 0;
+
+	if (!gspca_dev->streaming && ctrl->id != V4L2_CID_POWER_LINE_FREQUENCY)
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_BRIGHTNESS:
+		sd->params.colourParams.brightness = ctrl->val;
+		sd->params.flickerControl.allowableOverExposure =
+			find_over_exposure(sd->params.colourParams.brightness);
+		gspca_dev->usb_err = command_setcolourparams(gspca_dev);
+		if (!gspca_dev->usb_err)
+			gspca_dev->usb_err = command_setflickerctrl(gspca_dev);
+		break;
+	case V4L2_CID_CONTRAST:
+		sd->params.colourParams.contrast = ctrl->val;
+		gspca_dev->usb_err = command_setcolourparams(gspca_dev);
+		break;
+	case V4L2_CID_SATURATION:
+		sd->params.colourParams.saturation = ctrl->val;
+		gspca_dev->usb_err = command_setcolourparams(gspca_dev);
+		break;
+	case V4L2_CID_POWER_LINE_FREQUENCY:
+		sd->mainsFreq = ctrl->val == V4L2_CID_POWER_LINE_FREQUENCY_60HZ;
+		sd->params.flickerControl.coarseJump =
+			flicker_jumps[sd->mainsFreq]
+			[sd->params.sensorFps.baserate]
+			[sd->params.sensorFps.divisor];
+
+		gspca_dev->usb_err = set_flicker(gspca_dev,
+			ctrl->val != V4L2_CID_POWER_LINE_FREQUENCY_DISABLED,
+			gspca_dev->streaming);
+		break;
+	case V4L2_CID_ILLUMINATORS_1:
+		sd->params.qx3.bottomlight = ctrl->val;
+		gspca_dev->usb_err = command_setlights(gspca_dev);
+		break;
+	case V4L2_CID_ILLUMINATORS_2:
+		sd->params.qx3.toplight = ctrl->val;
+		gspca_dev->usb_err = command_setlights(gspca_dev);
+		break;
+	case CPIA1_CID_COMP_TARGET:
+		sd->params.compressionTarget.frTargeting = ctrl->val;
+		gspca_dev->usb_err = command_setcompressiontarget(gspca_dev);
+		break;
+	}
+	return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+	.s_ctrl = sd_s_ctrl,
+};
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *)gspca_dev;
+	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+	static const char * const comp_target_menu[] = {
+		"Quality",
+		"Framerate",
+		NULL
+	};
+	static const struct v4l2_ctrl_config comp_target = {
+		.ops = &sd_ctrl_ops,
+		.id = CPIA1_CID_COMP_TARGET,
+		.type = V4L2_CTRL_TYPE_MENU,
+		.name = "Compression Target",
+		.qmenu = comp_target_menu,
+		.max = 1,
+		.def = COMP_TARGET_DEF,
+	};
+
+	gspca_dev->vdev.ctrl_handler = hdl;
+	v4l2_ctrl_handler_init(hdl, 7);
+	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_BRIGHTNESS, 0, 100, 1, BRIGHTNESS_DEF);
+	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_CONTRAST, 0, 96, 8, CONTRAST_DEF);
+	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_SATURATION, 0, 100, 1, SATURATION_DEF);
+	sd->freq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
+			V4L2_CID_POWER_LINE_FREQUENCY,
+			V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0,
+			FREQ_DEF);
+	if (sd->params.qx3.qx3_detected) {
+		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+				V4L2_CID_ILLUMINATORS_1, 0, 1, 1,
+				ILLUMINATORS_1_DEF);
+		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+				V4L2_CID_ILLUMINATORS_2, 0, 1, 1,
+				ILLUMINATORS_2_DEF);
+	}
+	v4l2_ctrl_new_custom(hdl, &comp_target, NULL);
+
+	if (hdl->error) {
+		pr_err("Could not initialize controls\n");
+		return hdl->error;
+	}
+	return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+	.name = MODULE_NAME,
+	.config = sd_config,
+	.init = sd_init,
+	.init_controls = sd_init_controls,
+	.start = sd_start,
+	.stopN = sd_stopN,
+	.dq_callback = sd_dq_callback,
+	.pkt_scan = sd_pkt_scan,
+#if IS_ENABLED(CONFIG_INPUT)
+	.other_input = 1,
+#endif
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+	{USB_DEVICE(0x0553, 0x0002)},
+	{USB_DEVICE(0x0813, 0x0001)},
+	{}
+};
+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,
+#ifdef CONFIG_PM
+	.suspend = gspca_suspend,
+	.resume = gspca_resume,
+	.reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/usb/gspca/dtcs033.c b/drivers/media/usb/gspca/dtcs033.c
new file mode 100644
index 0000000..96bfd4e
--- /dev/null
+++ b/drivers/media/usb/gspca/dtcs033.c
@@ -0,0 +1,441 @@
+/*
+ * Subdriver for Scopium astro-camera (DTCS033, 0547:7303)
+ *
+ * Copyright (C) 2014 Robert Butora (robert.butora.fi@gmail.com)
+ *
+ * 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.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#define MODULE_NAME "dtcs033"
+#include "gspca.h"
+
+MODULE_AUTHOR("Robert Butora <robert.butora.fi@gmail.com>");
+MODULE_DESCRIPTION("Scopium DTCS033 astro-cam USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+struct dtcs033_usb_requests {
+	u8 bRequestType;
+	u8 bRequest;
+	u16 wValue;
+	u16 wIndex;
+	u16 wLength;
+};
+
+/* send a usb request */
+static void reg_rw(struct gspca_dev *gspca_dev,
+		u8 bRequestType, u8 bRequest,
+		u16 wValue, u16 wIndex, u16 wLength)
+{
+	struct usb_device *udev = gspca_dev->dev;
+	int ret;
+
+	if (gspca_dev->usb_err < 0)
+		return;
+
+	ret = usb_control_msg(udev,
+		usb_rcvctrlpipe(udev, 0),
+		bRequest,
+		bRequestType,
+		wValue, wIndex,
+		gspca_dev->usb_buf, wLength, 500);
+
+	if (ret < 0) {
+		gspca_dev->usb_err = ret;
+		pr_err("usb_control_msg error %d\n", ret);
+	}
+
+	return;
+}
+/* send several usb in/out requests */
+static int reg_reqs(struct gspca_dev *gspca_dev,
+		    const struct dtcs033_usb_requests *preqs, int n_reqs)
+{
+	int i = 0;
+	const struct dtcs033_usb_requests *preq;
+
+	while ((i < n_reqs) && (gspca_dev->usb_err >= 0)) {
+
+		preq = &preqs[i];
+
+		reg_rw(gspca_dev, preq->bRequestType, preq->bRequest,
+			preq->wValue, preq->wIndex, preq->wLength);
+
+		if (gspca_dev->usb_err < 0) {
+
+			PERR("usb error request no: %d / %d\n",
+				i, n_reqs);
+		} else if (preq->bRequestType & USB_DIR_IN) {
+
+			PDEBUG(D_STREAM,
+			"USB IN (%d) returned[%d] %02X %02X %02X %s",
+				i,
+				preq->wLength,
+				gspca_dev->usb_buf[0],
+				gspca_dev->usb_buf[1],
+				gspca_dev->usb_buf[2],
+				preq->wLength > 3 ? "...\n" : "\n");
+		}
+
+		i++;
+	}
+	return gspca_dev->usb_err;
+}
+
+/* -- subdriver interface implementation -- */
+
+#define DT_COLS (640)
+static const struct v4l2_pix_format dtcs033_mode[] = {
+	/* raw Bayer patterned output */
+	{DT_COLS, 480, V4L2_PIX_FMT_GREY, V4L2_FIELD_NONE,
+		.bytesperline = DT_COLS,
+		.sizeimage = DT_COLS*480,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+	},
+	/* this mode will demosaic the Bayer pattern */
+	{DT_COLS, 480, V4L2_PIX_FMT_SRGGB8, V4L2_FIELD_NONE,
+		.bytesperline = DT_COLS,
+		.sizeimage = DT_COLS*480,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+	}
+};
+
+/* config called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+		const struct usb_device_id *id)
+{
+	gspca_dev->cam.cam_mode = dtcs033_mode;
+	gspca_dev->cam.nmodes = ARRAY_SIZE(dtcs033_mode);
+
+	gspca_dev->cam.bulk = 1;
+	gspca_dev->cam.bulk_nurbs = 1;
+	gspca_dev->cam.bulk_size = DT_COLS*512;
+
+	return 0;
+}
+
+/* init called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+	return 0;
+}
+
+/* start stop the camera */
+static int  dtcs033_start(struct gspca_dev *gspca_dev);
+static void dtcs033_stopN(struct gspca_dev *gspca_dev);
+
+/* intercept camera image data */
+static void dtcs033_pkt_scan(struct gspca_dev *gspca_dev,
+			u8 *data,  /* packet data */
+			int len)   /* packet data length */
+{
+	/* drop incomplete frames */
+	if (len != DT_COLS*512) {
+		gspca_dev->last_packet_type = DISCARD_PACKET;
+		/* gspca.c: discard invalidates the whole frame. */
+		return;
+	}
+
+	/* forward complete frames */
+	gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0);
+	gspca_frame_add(gspca_dev, INTER_PACKET,
+		data + 16*DT_COLS,
+		len  - 32*DT_COLS); /* skip first & last 16 lines */
+	gspca_frame_add(gspca_dev, LAST_PACKET,  NULL, 0);
+
+	return;
+}
+
+/* -- controls: exposure and gain -- */
+
+static void dtcs033_setexposure(struct gspca_dev *gspca_dev,
+			s32 expo, s32 gain)
+{
+	/* gain [dB] encoding */
+	u16 sGain   = (u16)gain;
+	u16 gainVal = 224+(sGain-14)*(768-224)/(33-14);
+	u16 wIndex =  0x0100|(0x00FF&gainVal);
+	u16 wValue = (0xFF00&gainVal)>>8;
+
+	/* exposure time [msec] encoding */
+	u16 sXTime   = (u16)expo;
+	u16 xtimeVal = (524*(150-(sXTime-1)))/150;
+
+	const u8 bRequestType =
+		USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE;
+	const u8 bRequest = 0x18;
+
+	reg_rw(gspca_dev,
+		bRequestType, bRequest, wValue, wIndex, 0);
+	if (gspca_dev->usb_err < 0)
+		PERR("usb error in setexposure(gain) sequence.\n");
+
+	reg_rw(gspca_dev,
+		bRequestType, bRequest, (xtimeVal<<4), 0x6300, 0);
+	if (gspca_dev->usb_err < 0)
+		PERR("usb error in setexposure(time) sequence.\n");
+}
+
+/* specific webcam descriptor */
+struct sd {
+	struct gspca_dev gspca_dev;/* !! must be the first item */
+
+	/* exposure & gain controls */
+	struct {
+		struct v4l2_ctrl *exposure;
+		struct v4l2_ctrl *gain;
+	};
+};
+
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct gspca_dev *gspca_dev =
+	container_of(ctrl->handler,
+		struct gspca_dev, ctrl_handler);
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	gspca_dev->usb_err = 0;
+
+	if (!gspca_dev->streaming)
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_EXPOSURE:
+		dtcs033_setexposure(gspca_dev,
+				ctrl->val, sd->gain->val);
+		break;
+	case V4L2_CID_GAIN:
+		dtcs033_setexposure(gspca_dev,
+				sd->exposure->val, ctrl->val);
+		break;
+	}
+	return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+	.s_ctrl = sd_s_ctrl,
+};
+
+static int dtcs033_init_controls(struct gspca_dev *gspca_dev)
+{
+	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	gspca_dev->vdev.ctrl_handler = hdl;
+	v4l2_ctrl_handler_init(hdl, 2);
+	/*                               min max step default */
+	sd->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+				V4L2_CID_EXPOSURE,
+				1,  150,  1,  75);/* [msec] */
+	sd->gain     = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+				V4L2_CID_GAIN,
+				14,  33,  1,  24);/* [dB] */
+	if (hdl->error) {
+		PERR("Could not initialize controls: %d\n",
+			hdl->error);
+		return hdl->error;
+	}
+
+	v4l2_ctrl_cluster(2, &sd->exposure);
+	return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+	.name     = MODULE_NAME,
+	.config   = sd_config,
+	.init     = sd_init,
+	.start    = dtcs033_start,
+	.stopN    = dtcs033_stopN,
+	.pkt_scan = dtcs033_pkt_scan,
+	.init_controls = dtcs033_init_controls,
+};
+
+/* -- module initialisation -- */
+
+static const struct usb_device_id device_table[] = {
+	{USB_DEVICE(0x0547, 0x7303)},
+	{}
+};
+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,
+#ifdef CONFIG_PM
+	.suspend      = gspca_suspend,
+	.resume       = gspca_resume,
+	.reset_resume = gspca_resume,
+#endif
+};
+module_usb_driver(sd_driver);
+
+
+/* ---------------------------------------------------------
+ USB requests to start/stop the camera [USB 2.0 spec Ch.9].
+
+ bRequestType :
+ 0x40 =  USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0xC0 =  USB_DIR_IN  | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+*/
+static const struct dtcs033_usb_requests dtcs033_start_reqs[] = {
+/* -- bRequest,wValue,wIndex,wLength */
+{ 0x40, 0x01, 0x0001, 0x000F, 0x0000 },
+{ 0x40, 0x01, 0x0000, 0x000F, 0x0000 },
+{ 0x40, 0x01, 0x0001, 0x000F, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x7F00, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x1001, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x0004, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x7F01, 0x0000 },
+{ 0x40, 0x18, 0x30E0, 0x0009, 0x0000 },
+{ 0x40, 0x18, 0x0500, 0x012C, 0x0000 },
+{ 0x40, 0x18, 0x0380, 0x0200, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x035C, 0x0000 },
+{ 0x40, 0x18, 0x05C0, 0x0438, 0x0000 },
+{ 0x40, 0x18, 0x0440, 0x0500, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x0668, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x0700, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x0800, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x0900, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x0A00, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x0B00, 0x0000 },
+{ 0x40, 0x18, 0x30E0, 0x6009, 0x0000 },
+{ 0x40, 0x18, 0x0500, 0x612C, 0x0000 },
+{ 0x40, 0x18, 0x2090, 0x6274, 0x0000 },
+{ 0x40, 0x18, 0x05C0, 0x6338, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x6400, 0x0000 },
+{ 0x40, 0x18, 0x05C0, 0x6538, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x6600, 0x0000 },
+{ 0x40, 0x18, 0x0680, 0x6744, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x6800, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x6900, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x6A00, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x6B00, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x6C00, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x6D00, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x6E00, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x808C, 0x0000 },
+{ 0x40, 0x18, 0x0010, 0x8101, 0x0000 },
+{ 0x40, 0x18, 0x30E0, 0x8200, 0x0000 },
+{ 0x40, 0x18, 0x0810, 0x832C, 0x0000 },
+{ 0x40, 0x18, 0x0680, 0x842B, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x8500, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x8600, 0x0000 },
+{ 0x40, 0x18, 0x0280, 0x8715, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x880C, 0x0000 },
+{ 0x40, 0x18, 0x0010, 0x8901, 0x0000 },
+{ 0x40, 0x18, 0x30E0, 0x8A00, 0x0000 },
+{ 0x40, 0x18, 0x0810, 0x8B2C, 0x0000 },
+{ 0x40, 0x18, 0x0680, 0x8C2B, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x8D00, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x8E00, 0x0000 },
+{ 0x40, 0x18, 0x0280, 0x8F15, 0x0000 },
+{ 0x40, 0x18, 0x0010, 0xD040, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0xD100, 0x0000 },
+{ 0x40, 0x18, 0x00B0, 0xD20A, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0xD300, 0x0000 },
+{ 0x40, 0x18, 0x30E2, 0xD40D, 0x0000 },
+{ 0x40, 0x18, 0x0001, 0xD5C0, 0x0000 },
+{ 0x40, 0x18, 0x00A0, 0xD60A, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0xD700, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x7F00, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x1501, 0x0000 },
+{ 0x40, 0x18, 0x0001, 0x01FF, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x0200, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x0304, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x1101, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x1201, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x1300, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x1400, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x1601, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x1800, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x1900, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x1A00, 0x0000 },
+{ 0x40, 0x18, 0x2000, 0x1B00, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x1C00, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x2100, 0x0000 },
+{ 0x40, 0x18, 0x00C0, 0x228E, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x3001, 0x0000 },
+{ 0x40, 0x18, 0x0010, 0x3101, 0x0000 },
+{ 0x40, 0x18, 0x0008, 0x3301, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x3400, 0x0000 },
+{ 0x40, 0x18, 0x0012, 0x3549, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x3620, 0x0000 },
+{ 0x40, 0x18, 0x0001, 0x3700, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x4000, 0x0000 },
+{ 0x40, 0x18, 0xFFFF, 0x41FF, 0x0000 },
+{ 0x40, 0x18, 0xFFFF, 0x42FF, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x500F, 0x0000 },
+{ 0x40, 0x18, 0x2272, 0x5108, 0x0000 },
+{ 0x40, 0x18, 0x2272, 0x5208, 0x0000 },
+{ 0x40, 0x18, 0xFFFF, 0x53FF, 0x0000 },
+{ 0x40, 0x18, 0xFFFF, 0x54FF, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x6000, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x6102, 0x0000 },
+{ 0x40, 0x18, 0x0010, 0x6214, 0x0000 },
+{ 0x40, 0x18, 0x0C80, 0x6300, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x6401, 0x0000 },
+{ 0x40, 0x18, 0x0680, 0x6551, 0x0000 },
+{ 0x40, 0x18, 0xFFFF, 0x66FF, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x6702, 0x0000 },
+{ 0x40, 0x18, 0x0010, 0x6800, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x6900, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x6A00, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x6B00, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x6C00, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x6D01, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x6E00, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x6F00, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x7000, 0x0000 },
+{ 0x40, 0x18, 0x0001, 0x7118, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x2001, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x1101, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x1301, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x1300, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x1501, 0x0000 },
+{ 0xC0, 0x11, 0x0000, 0x24C0, 0x0003 },
+{ 0x40, 0x18, 0x0000, 0x3000, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x3620, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x1501, 0x0000 },
+{ 0x40, 0x18, 0x0010, 0x6300, 0x0000 },
+{ 0x40, 0x18, 0x0002, 0x01F0, 0x0000 },
+{ 0x40, 0x01, 0x0003, 0x000F, 0x0000 }
+};
+
+static const struct dtcs033_usb_requests dtcs033_stop_reqs[] = {
+/* -- bRequest,wValue,wIndex,wLength */
+{ 0x40, 0x01, 0x0001, 0x000F, 0x0000 },
+{ 0x40, 0x01, 0x0000, 0x000F, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x0003, 0x0000 }
+};
+static int dtcs033_start(struct gspca_dev *gspca_dev)
+{
+	return reg_reqs(gspca_dev, dtcs033_start_reqs,
+		ARRAY_SIZE(dtcs033_start_reqs));
+}
+
+static void dtcs033_stopN(struct gspca_dev *gspca_dev)
+{
+	reg_reqs(gspca_dev, dtcs033_stop_reqs,
+		ARRAY_SIZE(dtcs033_stop_reqs));
+	return;
+}
diff --git a/drivers/media/usb/gspca/etoms.c b/drivers/media/usb/gspca/etoms.c
new file mode 100644
index 0000000..26c9ee1
--- /dev/null
+++ b/drivers/media/usb/gspca/etoms.c
@@ -0,0 +1,795 @@
+/*
+ * 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "etoms"
+
+#include "gspca.h"
+
+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 autogain;
+
+	char sensor;
+#define SENSOR_PAS106 0
+#define SENSOR_TAS5130CXX 1
+	signed char ag_cnt;
+#define AG_CNT_START 13
+};
+
+static const 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 const 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;
+
+	if (len > USB_BUF_SZ) {
+		PERR("reg_r: buffer overflow\n");
+		return;
+	}
+
+	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;
+
+	if (len > USB_BUF_SZ) {
+		pr_err("reg_w: buffer overflow\n");
+		return;
+	}
+	PDEBUG(D_USBO, "reg write [%02x] = %02x..", index, *buffer);
+
+	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)
+		PERR("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 setbrightness(struct gspca_dev *gspca_dev, s32 val)
+{
+	int i;
+
+	for (i = 0; i < 4; i++)
+		reg_w_val(gspca_dev, ET_O_RED + i, val);
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
+{
+	__u8 RGBG[] = { 0x80, 0x80, 0x80, 0x80, 0x00, 0x00 };
+
+	memset(RGBG, val, sizeof(RGBG) - 2);
+	reg_w(gspca_dev, ET_G_RED, RGBG, 6);
+}
+
+static void setcolors(struct gspca_dev *gspca_dev, s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	__u8 I2cc[] = { 0x05, 0x02, 0x02, 0x05, 0x0d };
+	__u8 i2cflags = 0x01;
+	/* __u8 green = 0; */
+
+	I2cc[3] = val;	/* red */
+	I2cc[0] = 15 - val;	/* 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 s32 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 */
+		return gspca_dev->usb_buf[0] & 0x0f;
+	}
+	return 0;
+}
+
+static void setautogain(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (sd->autogain)
+		sd->ag_cnt = AG_CNT_START;
+	else
+		sd->ag_cnt = -1;
+}
+
+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);
+	setcolors(gspca_dev, getcolors(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;
+
+	cam = &gspca_dev->cam;
+	sd->sensor = id->driver_info;
+	if (sd->sensor == SENSOR_PAS106) {
+		cam->cam_mode = sif_mode;
+		cam->nmodes = ARRAY_SIZE(sif_mode);
+	} else {
+		cam->cam_mode = vga_mode;
+		cam->nmodes = ARRAY_SIZE(vga_mode);
+	}
+	sd->ag_cnt = -1;
+	return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(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 int 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);
+
+	setautogain(gspca_dev);
+
+	reg_w_val(gspca_dev, ET_RESET_ALL, 0x08);
+	et_video(gspca_dev, 1);		/* video on */
+	return 0;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+	et_video(gspca_dev, 0);		/* video off */
+}
+
+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) \
+	(u8)((color > 0xff) ? 0xff : ((color < 0) ? 0 : color))
+
+static void do_autogain(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	__u8 luma;
+	__u8 luma_mean = 128;
+	__u8 luma_delta = 20;
+	__u8 spring = 4;
+	int Gbright;
+	__u8 r, g, b;
+
+	if (sd->ag_cnt < 0)
+		return;
+	if (--sd->ag_cnt >= 0)
+		return;
+	sd->ag_cnt = AG_CNT_START;
+
+	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,
+			u8 *data,			/* isoc packet */
+			int len)			/* iso packet length */
+{
+	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 */
+		gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+		gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
+		return;
+	}
+	if (len) {
+		data += 8;
+		gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+	} else {			/* Drop Packet */
+		gspca_dev->last_packet_type = DISCARD_PACKET;
+	}
+}
+
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct gspca_dev *gspca_dev =
+		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+	struct sd *sd = (struct sd *)gspca_dev;
+
+	gspca_dev->usb_err = 0;
+
+	if (!gspca_dev->streaming)
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_BRIGHTNESS:
+		setbrightness(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_CONTRAST:
+		setcontrast(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_SATURATION:
+		setcolors(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_AUTOGAIN:
+		sd->autogain = ctrl->val;
+		setautogain(gspca_dev);
+		break;
+	}
+	return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+	.s_ctrl = sd_s_ctrl,
+};
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *)gspca_dev;
+	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+	gspca_dev->vdev.ctrl_handler = hdl;
+	v4l2_ctrl_handler_init(hdl, 4);
+	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_BRIGHTNESS, 1, 127, 1, 63);
+	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_CONTRAST, 0, 255, 1, 127);
+	if (sd->sensor == SENSOR_PAS106)
+		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_SATURATION, 0, 15, 1, 7);
+	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+	if (hdl->error) {
+		pr_err("Could not initialize controls\n");
+		return hdl->error;
+	}
+	return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+	.name = MODULE_NAME,
+	.config = sd_config,
+	.init = sd_init,
+	.init_controls = sd_init_controls,
+	.start = sd_start,
+	.stopN = sd_stopN,
+	.pkt_scan = sd_pkt_scan,
+	.dq_callback = do_autogain,
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+	{USB_DEVICE(0x102c, 0x6151), .driver_info = SENSOR_PAS106},
+	{USB_DEVICE(0x102c, 0x6251), .driver_info = SENSOR_TAS5130CXX},
+	{}
+};
+
+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,
+#ifdef CONFIG_PM
+	.suspend = gspca_suspend,
+	.resume = gspca_resume,
+	.reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/usb/gspca/finepix.c b/drivers/media/usb/gspca/finepix.c
new file mode 100644
index 0000000..52bdb56
--- /dev/null
+++ b/drivers/media/usb/gspca/finepix.c
@@ -0,0 +1,310 @@
+/*
+ * Fujifilm Finepix subdriver
+ *
+ * Copyright (C) 2008 Frank Zago
+ *
+ * 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "finepix"
+
+#include "gspca.h"
+
+MODULE_AUTHOR("Frank Zago <frank@zago.net>");
+MODULE_DESCRIPTION("Fujifilm FinePix USB V4L2 driver");
+MODULE_LICENSE("GPL");
+
+/* Default timeout, in ms */
+#define FPIX_TIMEOUT 250
+
+/* Maximum transfer size to use. The windows driver reads by chunks of
+ * 0x2000 bytes, so do the same. Note: reading more seems to work
+ * too. */
+#define FPIX_MAX_TRANSFER 0x2000
+
+/* Structure to hold all of our device specific stuff */
+struct usb_fpix {
+	struct gspca_dev gspca_dev;	/* !! must be the first item */
+
+	struct work_struct work_struct;
+	struct workqueue_struct *work_thread;
+};
+
+/* Delay after which claim the next frame. If the delay is too small,
+ * the camera will return old frames. On the 4800Z, 20ms is bad, 25ms
+ * will fail every 4 or 5 frames, but 30ms is perfect. On the A210,
+ * 30ms is bad while 35ms is perfect. */
+#define NEXT_FRAME_DELAY 35
+
+/* These cameras only support 320x200. */
+static const struct v4l2_pix_format fpix_mode[1] = {
+	{ 320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+		.bytesperline = 320,
+		.sizeimage = 320 * 240 * 3 / 8 + 590,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 0}
+};
+
+/* send a command to the webcam */
+static int command(struct gspca_dev *gspca_dev,
+		int order)	/* 0: reset, 1: frame request */
+{
+	static u8 order_values[2][12] = {
+		{0xc6, 0, 0, 0, 0, 0, 0,    0, 0x20, 0, 0, 0},	/* reset */
+		{0xd3, 0, 0, 0, 0, 0, 0, 0x01,    0, 0, 0, 0},	/* fr req */
+	};
+
+	memcpy(gspca_dev->usb_buf, order_values[order], 12);
+	return usb_control_msg(gspca_dev->dev,
+			usb_sndctrlpipe(gspca_dev->dev, 0),
+			USB_REQ_GET_STATUS,
+			USB_DIR_OUT | USB_TYPE_CLASS |
+			USB_RECIP_INTERFACE, 0, 0, gspca_dev->usb_buf,
+			12, FPIX_TIMEOUT);
+}
+
+/*
+ * This function is called as a workqueue function and runs whenever the camera
+ * is streaming data. Because it is a workqueue function it is allowed to sleep
+ * so we can use synchronous USB calls. To avoid possible collisions with other
+ * threads attempting to use gspca_dev->usb_buf we take the usb_lock when
+ * performing USB operations using it. In practice we don't really need this
+ * as the camera doesn't provide any controls.
+ */
+static void dostream(struct work_struct *work)
+{
+	struct usb_fpix *dev = container_of(work, struct usb_fpix, work_struct);
+	struct gspca_dev *gspca_dev = &dev->gspca_dev;
+	struct urb *urb = gspca_dev->urb[0];
+	u8 *data = urb->transfer_buffer;
+	int ret = 0;
+	int len;
+
+	PDEBUG(D_STREAM, "dostream started");
+
+	/* loop reading a frame */
+again:
+	while (gspca_dev->present && gspca_dev->streaming) {
+#ifdef CONFIG_PM
+		if (gspca_dev->frozen)
+			break;
+#endif
+
+		/* request a frame */
+		mutex_lock(&gspca_dev->usb_lock);
+		ret = command(gspca_dev, 1);
+		mutex_unlock(&gspca_dev->usb_lock);
+		if (ret < 0)
+			break;
+#ifdef CONFIG_PM
+		if (gspca_dev->frozen)
+			break;
+#endif
+		if (!gspca_dev->present || !gspca_dev->streaming)
+			break;
+
+		/* the frame comes in parts */
+		for (;;) {
+			ret = usb_bulk_msg(gspca_dev->dev,
+					urb->pipe,
+					data,
+					FPIX_MAX_TRANSFER,
+					&len, FPIX_TIMEOUT);
+			if (ret < 0) {
+				/* Most of the time we get a timeout
+				 * error. Just restart. */
+				goto again;
+			}
+#ifdef CONFIG_PM
+			if (gspca_dev->frozen)
+				goto out;
+#endif
+			if (!gspca_dev->present || !gspca_dev->streaming)
+				goto out;
+			if (len < FPIX_MAX_TRANSFER ||
+				(data[len - 2] == 0xff &&
+					data[len - 1] == 0xd9)) {
+
+				/* If the result is less than what was asked
+				 * for, then it's the end of the
+				 * frame. Sometimes the jpeg is not complete,
+				 * but there's nothing we can do. We also end
+				 * here if the the jpeg ends right at the end
+				 * of the frame. */
+				gspca_frame_add(gspca_dev, LAST_PACKET,
+						data, len);
+				break;
+			}
+
+			/* got a partial image */
+			gspca_frame_add(gspca_dev,
+					gspca_dev->last_packet_type
+						== LAST_PACKET
+					? FIRST_PACKET : INTER_PACKET,
+					data, len);
+		}
+
+		/* We must wait before trying reading the next
+		 * frame. If we don't, or if the delay is too short,
+		 * the camera will disconnect. */
+		msleep(NEXT_FRAME_DELAY);
+	}
+
+out:
+	PDEBUG(D_STREAM, "dostream stopped");
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+		const struct usb_device_id *id)
+{
+	struct usb_fpix *dev = (struct usb_fpix *) gspca_dev;
+	struct cam *cam = &gspca_dev->cam;
+
+	cam->cam_mode = fpix_mode;
+	cam->nmodes = 1;
+	cam->bulk = 1;
+	cam->bulk_size = FPIX_MAX_TRANSFER;
+
+	INIT_WORK(&dev->work_struct, dostream);
+
+	return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+	return 0;
+}
+
+/* start the camera */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+	struct usb_fpix *dev = (struct usb_fpix *) gspca_dev;
+	int ret, len;
+
+	/* Init the device */
+	ret = command(gspca_dev, 0);
+	if (ret < 0) {
+		pr_err("init failed %d\n", ret);
+		return ret;
+	}
+
+	/* Read the result of the command. Ignore the result, for it
+	 * varies with the device. */
+	ret = usb_bulk_msg(gspca_dev->dev,
+			gspca_dev->urb[0]->pipe,
+			gspca_dev->urb[0]->transfer_buffer,
+			FPIX_MAX_TRANSFER, &len,
+			FPIX_TIMEOUT);
+	if (ret < 0) {
+		pr_err("usb_bulk_msg failed %d\n", ret);
+		return ret;
+	}
+
+	/* Request a frame, but don't read it */
+	ret = command(gspca_dev, 1);
+	if (ret < 0) {
+		pr_err("frame request failed %d\n", ret);
+		return ret;
+	}
+
+	/* Again, reset bulk in endpoint */
+	usb_clear_halt(gspca_dev->dev, gspca_dev->urb[0]->pipe);
+
+	/* Start the workqueue function to do the streaming */
+	dev->work_thread = create_singlethread_workqueue(MODULE_NAME);
+	queue_work(dev->work_thread, &dev->work_struct);
+
+	return 0;
+}
+
+/* called on streamoff with alt==0 and on disconnect */
+/* the usb_lock is held at entry - restore on exit */
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+	struct usb_fpix *dev = (struct usb_fpix *) gspca_dev;
+
+	/* wait for the work queue to terminate */
+	mutex_unlock(&gspca_dev->usb_lock);
+	destroy_workqueue(dev->work_thread);
+	mutex_lock(&gspca_dev->usb_lock);
+	dev->work_thread = NULL;
+}
+
+/* Table of supported USB devices */
+static const struct usb_device_id device_table[] = {
+	{USB_DEVICE(0x04cb, 0x0104)},
+	{USB_DEVICE(0x04cb, 0x0109)},
+	{USB_DEVICE(0x04cb, 0x010b)},
+	{USB_DEVICE(0x04cb, 0x010f)},
+	{USB_DEVICE(0x04cb, 0x0111)},
+	{USB_DEVICE(0x04cb, 0x0113)},
+	{USB_DEVICE(0x04cb, 0x0115)},
+	{USB_DEVICE(0x04cb, 0x0117)},
+	{USB_DEVICE(0x04cb, 0x0119)},
+	{USB_DEVICE(0x04cb, 0x011b)},
+	{USB_DEVICE(0x04cb, 0x011d)},
+	{USB_DEVICE(0x04cb, 0x0121)},
+	{USB_DEVICE(0x04cb, 0x0123)},
+	{USB_DEVICE(0x04cb, 0x0125)},
+	{USB_DEVICE(0x04cb, 0x0127)},
+	{USB_DEVICE(0x04cb, 0x0129)},
+	{USB_DEVICE(0x04cb, 0x012b)},
+	{USB_DEVICE(0x04cb, 0x012d)},
+	{USB_DEVICE(0x04cb, 0x012f)},
+	{USB_DEVICE(0x04cb, 0x0131)},
+	{USB_DEVICE(0x04cb, 0x013b)},
+	{USB_DEVICE(0x04cb, 0x013d)},
+	{USB_DEVICE(0x04cb, 0x013f)},
+	{}
+};
+
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+	.name   = MODULE_NAME,
+	.config = sd_config,
+	.init   = sd_init,
+	.start  = sd_start,
+	.stop0  = sd_stop0,
+};
+
+/* -- 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 usb_fpix),
+			THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+	.name       = MODULE_NAME,
+	.id_table   = device_table,
+	.probe      = sd_probe,
+	.disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+	.suspend = gspca_suspend,
+	.resume  = gspca_resume,
+	.reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/usb/gspca/gl860/Kconfig b/drivers/media/usb/gspca/gl860/Kconfig
new file mode 100644
index 0000000..22772f5
--- /dev/null
+++ b/drivers/media/usb/gspca/gl860/Kconfig
@@ -0,0 +1,8 @@
+config USB_GL860
+	tristate "GL860 USB Camera Driver"
+	depends on VIDEO_V4L2 && USB_GSPCA
+	help
+	  Say Y here if you want support for cameras based on the GL860 chip.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gspca_gl860.
diff --git a/drivers/media/usb/gspca/gl860/Makefile b/drivers/media/usb/gspca/gl860/Makefile
new file mode 100644
index 0000000..cf63974
--- /dev/null
+++ b/drivers/media/usb/gspca/gl860/Makefile
@@ -0,0 +1,10 @@
+obj-$(CONFIG_USB_GL860) += gspca_gl860.o
+
+gspca_gl860-objs := gl860.o \
+		    gl860-mi1320.o \
+		    gl860-ov2640.o \
+		    gl860-ov9655.o \
+		    gl860-mi2020.o
+
+ccflags-y += -I$(srctree)/drivers/media/usb/gspca
+
diff --git a/drivers/media/usb/gspca/gl860/gl860-mi1320.c b/drivers/media/usb/gspca/gl860/gl860-mi1320.c
new file mode 100644
index 0000000..b57160e
--- /dev/null
+++ b/drivers/media/usb/gspca/gl860/gl860-mi1320.c
@@ -0,0 +1,536 @@
+/* Subdriver for the GL860 chip with the MI1320 sensor
+ * Author Olivier LORIN from own logs
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+/* Sensor : MI1320 */
+
+#include "gl860.h"
+
+static struct validx tbl_common[] = {
+	{0xba00, 0x00f0}, {0xba00, 0x00f1}, {0xba51, 0x0066}, {0xba02, 0x00f1},
+	{0xba05, 0x0067}, {0xba05, 0x00f1}, {0xbaa0, 0x0065}, {0xba00, 0x00f1},
+	{0xffff, 0xffff},
+	{0xba00, 0x00f0}, {0xba02, 0x00f1}, {0xbafa, 0x0028}, {0xba02, 0x00f1},
+	{0xba00, 0x00f0}, {0xba01, 0x00f1}, {0xbaf0, 0x0006}, {0xba0e, 0x00f1},
+	{0xba70, 0x0006}, {0xba0e, 0x00f1},
+	{0xffff, 0xffff},
+	{0xba74, 0x0006}, {0xba0e, 0x00f1},
+	{0xffff, 0xffff},
+	{0x0061, 0x0000}, {0x0068, 0x000d},
+};
+
+static struct validx tbl_init_at_startup[] = {
+	{0x0000, 0x0000}, {0x0010, 0x0010},
+	{35, 0xffff},
+	{0x0008, 0x00c0}, {0x0001, 0x00c1}, {0x0001, 0x00c2}, {0x0020, 0x0006},
+	{0x006a, 0x000d},
+};
+
+static struct validx tbl_sensor_settings_common[] = {
+	{0x0010, 0x0010}, {0x0003, 0x00c1}, {0x0042, 0x00c2}, {0x0040, 0x0000},
+	{0x006a, 0x0007}, {0x006a, 0x000d}, {0x0063, 0x0006},
+};
+static struct validx tbl_sensor_settings_1280[] = {
+	{0xba00, 0x00f0}, {0xba00, 0x00f1}, {0xba5a, 0x0066}, {0xba02, 0x00f1},
+	{0xba05, 0x0067}, {0xba05, 0x00f1}, {0xba20, 0x0065}, {0xba00, 0x00f1},
+};
+static struct validx tbl_sensor_settings_800[] = {
+	{0xba00, 0x00f0}, {0xba00, 0x00f1}, {0xba5a, 0x0066}, {0xba02, 0x00f1},
+	{0xba05, 0x0067}, {0xba05, 0x00f1}, {0xba20, 0x0065}, {0xba00, 0x00f1},
+};
+static struct validx tbl_sensor_settings_640[] = {
+	{0xba00, 0x00f0}, {0xba00, 0x00f1}, {0xbaa0, 0x0065}, {0xba00, 0x00f1},
+	{0xba51, 0x0066}, {0xba02, 0x00f1}, {0xba05, 0x0067}, {0xba05, 0x00f1},
+	{0xba20, 0x0065}, {0xba00, 0x00f1},
+};
+static struct validx tbl_post_unset_alt[] = {
+	{0xba00, 0x00f0}, {0xba00, 0x00f1}, {0xbaa0, 0x0065}, {0xba00, 0x00f1},
+	{0x0061, 0x0000}, {0x0068, 0x000d},
+};
+
+static u8 *tbl_1280[] = {
+	"\x0d\x80\xf1\x08\x03\x04\xf1\x00" "\x04\x05\xf1\x02\x05\x00\xf1\xf1"
+	"\x06\x00\xf1\x0d\x20\x01\xf1\x00" "\x21\x84\xf1\x00\x0d\x00\xf1\x08"
+	"\xf0\x00\xf1\x01\x34\x00\xf1\x00" "\x9b\x43\xf1\x00\xa6\x05\xf1\x00"
+	"\xa9\x04\xf1\x00\xa1\x05\xf1\x00" "\xa4\x04\xf1\x00\xae\x0a\xf1\x08"
+	,
+	"\xf0\x00\xf1\x02\x3a\x05\xf1\xf1" "\x3c\x05\xf1\xf1\x59\x01\xf1\x47"
+	"\x5a\x01\xf1\x88\x5c\x0a\xf1\x06" "\x5d\x0e\xf1\x0a\x64\x5e\xf1\x1c"
+	"\xd2\x00\xf1\xcf\xcb\x00\xf1\x01"
+	,
+	"\xd3\x02\xd4\x28\xd5\x01\xd0\x02" "\xd1\x18\xd2\xc1"
+};
+
+static u8 *tbl_800[] = {
+	"\x0d\x80\xf1\x08\x03\x03\xf1\xc0" "\x04\x05\xf1\x02\x05\x00\xf1\xf1"
+	"\x06\x00\xf1\x0d\x20\x01\xf1\x00" "\x21\x84\xf1\x00\x0d\x00\xf1\x08"
+	"\xf0\x00\xf1\x01\x34\x00\xf1\x00" "\x9b\x43\xf1\x00\xa6\x05\xf1\x00"
+	"\xa9\x03\xf1\xc0\xa1\x03\xf1\x20" "\xa4\x02\xf1\x5a\xae\x0a\xf1\x08"
+	,
+	"\xf0\x00\xf1\x02\x3a\x05\xf1\xf1" "\x3c\x05\xf1\xf1\x59\x01\xf1\x47"
+	"\x5a\x01\xf1\x88\x5c\x0a\xf1\x06" "\x5d\x0e\xf1\x0a\x64\x5e\xf1\x1c"
+	"\xd2\x00\xf1\xcf\xcb\x00\xf1\x01"
+	,
+	"\xd3\x02\xd4\x18\xd5\x21\xd0\x02" "\xd1\x10\xd2\x59"
+};
+
+static u8 *tbl_640[] = {
+	"\x0d\x80\xf1\x08\x03\x04\xf1\x04" "\x04\x05\xf1\x02\x07\x01\xf1\x7c"
+	"\x08\x00\xf1\x0e\x21\x80\xf1\x00" "\x0d\x00\xf1\x08\xf0\x00\xf1\x01"
+	"\x34\x10\xf1\x10\x3a\x43\xf1\x00" "\xa6\x05\xf1\x02\xa9\x04\xf1\x04"
+	"\xa7\x02\xf1\x81\xaa\x01\xf1\xe2" "\xae\x0c\xf1\x09"
+	,
+	"\xf0\x00\xf1\x02\x39\x03\xf1\xfc" "\x3b\x04\xf1\x04\x57\x01\xf1\xb6"
+	"\x58\x02\xf1\x0d\x5c\x1f\xf1\x19" "\x5d\x24\xf1\x1e\x64\x5e\xf1\x1c"
+	"\xd2\x00\xf1\x00\xcb\x00\xf1\x01"
+	,
+	"\xd3\x02\xd4\x10\xd5\x81\xd0\x02" "\xd1\x08\xd2\xe1"
+};
+
+static s32 tbl_sat[] = {0x25, 0x1d, 0x15, 0x0d, 0x05, 0x4d, 0x55, 0x5d, 0x2d};
+static s32 tbl_bright[] = {0, 8, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70};
+static s32 tbl_backlight[] = {0x0e, 0x06, 0x02};
+
+static s32 tbl_cntr1[] = {
+	0x90, 0x98, 0xa0, 0xa8, 0xb0, 0xb8, 0xc0, 0xc8, 0xd0, 0xe0, 0xf0};
+static s32 tbl_cntr2[] = {
+	0x70, 0x68, 0x60, 0x58, 0x50, 0x48, 0x40, 0x38, 0x30, 0x20, 0x10};
+
+static u8 dat_wbalNL[] =
+	"\xf0\x00\xf1\x01\x05\x00\xf1\x06" "\x3b\x04\xf1\x2a\x47\x10\xf1\x10"
+	"\x9d\x3c\xf1\xae\xaf\x10\xf1\x00" "\xf0\x00\xf1\x02\x2f\x91\xf1\x20"
+	"\x9c\x91\xf1\x20\x37\x03\xf1\x00" "\x9d\xc5\xf1\x0f\xf0\x00\xf1\x00";
+
+static u8 dat_wbalLL[] =
+	"\xf0\x00\xf1\x01\x05\x00\xf1\x0c" "\x3b\x04\xf1\x2a\x47\x40\xf1\x40"
+	"\x9d\x20\xf1\xae\xaf\x10\xf1\x00" "\xf0\x00\xf1\x02\x2f\xd1\xf1\x00"
+	"\x9c\xd1\xf1\x00\x37\x03\xf1\x00" "\x9d\xc5\xf1\x3f\xf0\x00\xf1\x00";
+
+static u8 dat_wbalBL[] =
+	"\xf0\x00\xf1\x01\x05\x00\xf1\x06" "\x47\x10\xf1\x30\x9d\x3c\xf1\xae"
+	"\xaf\x10\xf1\x00\xf0\x00\xf1\x02" "\x2f\x91\xf1\x20\x9c\x91\xf1\x20"
+	"\x37\x03\xf1\x00\x9d\xc5\xf1\x2f" "\xf0\x00\xf1\x00";
+
+static u8 dat_hvflip1[] = {0xf0, 0x00, 0xf1, 0x00};
+
+static u8 dat_common00[] =
+	"\x00\x01\x07\x6a\x06\x63\x0d\x6a" "\xc0\x00\x10\x10\xc1\x03\xc2\x42"
+	"\xd8\x04\x58\x00\x04\x02";
+static u8 dat_common01[] =
+	"\x0d\x00\xf1\x0b\x0d\x00\xf1\x08" "\x35\x00\xf1\x22\x68\x00\xf1\x5d"
+	"\xf0\x00\xf1\x01\x06\x70\xf1\x0e" "\xf0\x00\xf1\x02\xdd\x18\xf1\xe0";
+static u8 dat_common02[] =
+	"\x05\x01\xf1\x84\x06\x00\xf1\x44" "\x07\x00\xf1\xbe\x08\x00\xf1\x1e"
+	"\x20\x01\xf1\x03\x21\x84\xf1\x00" "\x22\x0d\xf1\x0f\x24\x80\xf1\x00"
+	"\x34\x18\xf1\x2d\x35\x00\xf1\x22" "\x43\x83\xf1\x83\x59\x00\xf1\xff";
+static u8 dat_common03[] =
+	"\xf0\x00\xf1\x02\x39\x06\xf1\x8c" "\x3a\x06\xf1\x8c\x3b\x03\xf1\xda"
+	"\x3c\x05\xf1\x30\x57\x01\xf1\x0c" "\x58\x01\xf1\x42\x59\x01\xf1\x0c"
+	"\x5a\x01\xf1\x42\x5c\x13\xf1\x0e" "\x5d\x17\xf1\x12\x64\x1e\xf1\x1c";
+static u8 dat_common04[] =
+	"\xf0\x00\xf1\x02\x24\x5f\xf1\x20" "\x28\xea\xf1\x02\x5f\x41\xf1\x43";
+static u8 dat_common05[] =
+	"\x02\x00\xf1\xee\x03\x29\xf1\x1a" "\x04\x02\xf1\xa4\x09\x00\xf1\x68"
+	"\x0a\x00\xf1\x2a\x0b\x00\xf1\x04" "\x0c\x00\xf1\x93\x0d\x00\xf1\x82"
+	"\x0e\x00\xf1\x40\x0f\x00\xf1\x5f" "\x10\x00\xf1\x4e\x11\x00\xf1\x5b";
+static u8 dat_common06[] =
+	"\x15\x00\xf1\xc9\x16\x00\xf1\x5e" "\x17\x00\xf1\x9d\x18\x00\xf1\x06"
+	"\x19\x00\xf1\x89\x1a\x00\xf1\x12" "\x1b\x00\xf1\xa1\x1c\x00\xf1\xe4"
+	"\x1d\x00\xf1\x7a\x1e\x00\xf1\x64" "\xf6\x00\xf1\x5f";
+static u8 dat_common07[] =
+	"\xf0\x00\xf1\x01\x53\x09\xf1\x03" "\x54\x3d\xf1\x1c\x55\x99\xf1\x72"
+	"\x56\xc1\xf1\xb1\x57\xd8\xf1\xce" "\x58\xe0\xf1\x00\xdc\x0a\xf1\x03"
+	"\xdd\x45\xf1\x20\xde\xae\xf1\x82" "\xdf\xdc\xf1\xc9\xe0\xf6\xf1\xea"
+	"\xe1\xff\xf1\x00";
+static u8 dat_common08[] =
+	"\xf0\x00\xf1\x01\x80\x00\xf1\x06" "\x81\xf6\xf1\x08\x82\xfb\xf1\xf7"
+	"\x83\x00\xf1\xfe\xb6\x07\xf1\x03" "\xb7\x18\xf1\x0c\x84\xfb\xf1\x06"
+	"\x85\xfb\xf1\xf9\x86\x00\xf1\xff" "\xb8\x07\xf1\x04\xb9\x16\xf1\x0a";
+static u8 dat_common09[] =
+	"\x87\xfa\xf1\x05\x88\xfc\xf1\xf9" "\x89\x00\xf1\xff\xba\x06\xf1\x03"
+	"\xbb\x17\xf1\x09\x8a\xe8\xf1\x14" "\x8b\xf7\xf1\xf0\x8c\xfd\xf1\xfa"
+	"\x8d\x00\xf1\x00\xbc\x05\xf1\x01" "\xbd\x0c\xf1\x08\xbe\x00\xf1\x14";
+static u8 dat_common10[] =
+	"\x8e\xea\xf1\x13\x8f\xf7\xf1\xf2" "\x90\xfd\xf1\xfa\x91\x00\xf1\x00"
+	"\xbf\x05\xf1\x01\xc0\x0a\xf1\x08" "\xc1\x00\xf1\x0c\x92\xed\xf1\x0f"
+	"\x93\xf9\xf1\xf4\x94\xfe\xf1\xfb" "\x95\x00\xf1\x00\xc2\x04\xf1\x01"
+	"\xc3\x0a\xf1\x07\xc4\x00\xf1\x10";
+static u8 dat_common11[] =
+	"\xf0\x00\xf1\x01\x05\x00\xf1\x06" "\x25\x00\xf1\x55\x34\x10\xf1\x10"
+	"\x35\xf0\xf1\x10\x3a\x02\xf1\x03" "\x3b\x04\xf1\x2a\x9b\x43\xf1\x00"
+	"\xa4\x03\xf1\xc0\xa7\x02\xf1\x81";
+
+static int  mi1320_init_at_startup(struct gspca_dev *gspca_dev);
+static int  mi1320_configure_alt(struct gspca_dev *gspca_dev);
+static int  mi1320_init_pre_alt(struct gspca_dev *gspca_dev);
+static int  mi1320_init_post_alt(struct gspca_dev *gspca_dev);
+static void mi1320_post_unset_alt(struct gspca_dev *gspca_dev);
+static int  mi1320_sensor_settings(struct gspca_dev *gspca_dev);
+static int  mi1320_camera_settings(struct gspca_dev *gspca_dev);
+/*==========================================================================*/
+
+void mi1320_init_settings(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	sd->vcur.backlight  =  0;
+	sd->vcur.brightness =  0;
+	sd->vcur.sharpness  =  6;
+	sd->vcur.contrast   = 10;
+	sd->vcur.gamma      = 20;
+	sd->vcur.hue        =  0;
+	sd->vcur.saturation =  6;
+	sd->vcur.whitebal   =  0;
+	sd->vcur.mirror     = 0;
+	sd->vcur.flip       = 0;
+	sd->vcur.AC50Hz     = 1;
+
+	sd->vmax.backlight  =  2;
+	sd->vmax.brightness =  8;
+	sd->vmax.sharpness  =  7;
+	sd->vmax.contrast   =  0; /* 10 but not working with this driver */
+	sd->vmax.gamma      = 40;
+	sd->vmax.hue        =  5 + 1;
+	sd->vmax.saturation =  8;
+	sd->vmax.whitebal   =  2;
+	sd->vmax.mirror     = 1;
+	sd->vmax.flip       = 1;
+	sd->vmax.AC50Hz     = 1;
+
+	sd->dev_camera_settings = mi1320_camera_settings;
+	sd->dev_init_at_startup = mi1320_init_at_startup;
+	sd->dev_configure_alt   = mi1320_configure_alt;
+	sd->dev_init_pre_alt    = mi1320_init_pre_alt;
+	sd->dev_post_unset_alt  = mi1320_post_unset_alt;
+}
+
+/*==========================================================================*/
+
+static void common(struct gspca_dev *gspca_dev)
+{
+	s32 n; /* reserved for FETCH functions */
+
+	ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 22, dat_common00);
+	ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x0000, 0, NULL);
+	ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 32, dat_common01);
+	n = fetch_validx(gspca_dev, tbl_common, ARRAY_SIZE(tbl_common));
+	ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, dat_common02);
+	ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, dat_common03);
+	ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 16, dat_common04);
+	ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, dat_common05);
+	ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 44, dat_common06);
+	keep_on_fetching_validx(gspca_dev, tbl_common,
+					ARRAY_SIZE(tbl_common), n);
+	ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 52, dat_common07);
+	ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, dat_common08);
+	ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, dat_common09);
+	ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 56, dat_common10);
+	keep_on_fetching_validx(gspca_dev, tbl_common,
+					ARRAY_SIZE(tbl_common), n);
+	ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 40, dat_common11);
+	keep_on_fetching_validx(gspca_dev, tbl_common,
+					ARRAY_SIZE(tbl_common), n);
+}
+
+static int mi1320_init_at_startup(struct gspca_dev *gspca_dev)
+{
+	fetch_validx(gspca_dev, tbl_init_at_startup,
+				ARRAY_SIZE(tbl_init_at_startup));
+
+	common(gspca_dev);
+
+/*	ctrl_out(gspca_dev, 0x40, 11, 0x0000, 0x0000, 0, NULL); */
+
+	return 0;
+}
+
+static int mi1320_init_pre_alt(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	sd->mirrorMask = 0;
+
+	sd->vold.backlight  = -1;
+	sd->vold.brightness = -1;
+	sd->vold.sharpness  = -1;
+	sd->vold.contrast   = -1;
+	sd->vold.saturation = -1;
+	sd->vold.gamma    = -1;
+	sd->vold.hue      = -1;
+	sd->vold.whitebal = -1;
+	sd->vold.mirror   = -1;
+	sd->vold.flip     = -1;
+	sd->vold.AC50Hz   = -1;
+
+	common(gspca_dev);
+
+	mi1320_sensor_settings(gspca_dev);
+
+	mi1320_init_post_alt(gspca_dev);
+
+	return 0;
+}
+
+static int mi1320_init_post_alt(struct gspca_dev *gspca_dev)
+{
+	mi1320_camera_settings(gspca_dev);
+
+	return 0;
+}
+
+static int mi1320_sensor_settings(struct gspca_dev *gspca_dev)
+{
+	s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv;
+
+	ctrl_out(gspca_dev, 0x40, 5, 0x0001, 0x0000, 0, NULL);
+
+	fetch_validx(gspca_dev, tbl_sensor_settings_common,
+				ARRAY_SIZE(tbl_sensor_settings_common));
+
+	switch (reso) {
+	case IMAGE_1280:
+		fetch_validx(gspca_dev, tbl_sensor_settings_1280,
+					ARRAY_SIZE(tbl_sensor_settings_1280));
+		ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 64, tbl_1280[0]);
+		ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 40, tbl_1280[1]);
+		ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, tbl_1280[2]);
+		break;
+
+	case IMAGE_800:
+		fetch_validx(gspca_dev, tbl_sensor_settings_800,
+					ARRAY_SIZE(tbl_sensor_settings_800));
+		ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 64, tbl_800[0]);
+		ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 40, tbl_800[1]);
+		ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, tbl_800[2]);
+		break;
+
+	default:
+		fetch_validx(gspca_dev, tbl_sensor_settings_640,
+					ARRAY_SIZE(tbl_sensor_settings_640));
+		ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 60, tbl_640[0]);
+		ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 40, tbl_640[1]);
+		ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, tbl_640[2]);
+		break;
+	}
+	return 0;
+}
+
+static int mi1320_configure_alt(struct gspca_dev *gspca_dev)
+{
+	s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv;
+
+	switch (reso) {
+	case IMAGE_640:
+		gspca_dev->alt = 3 + 1;
+		break;
+
+	case IMAGE_800:
+	case IMAGE_1280:
+		gspca_dev->alt = 1 + 1;
+		break;
+	}
+	return 0;
+}
+
+static int mi1320_camera_settings(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	s32 backlight = sd->vcur.backlight;
+	s32 bright = sd->vcur.brightness;
+	s32 sharp  = sd->vcur.sharpness;
+	s32 cntr   = sd->vcur.contrast;
+	s32 gam	   = sd->vcur.gamma;
+	s32 hue    = sd->vcur.hue;
+	s32 sat	   = sd->vcur.saturation;
+	s32 wbal   = sd->vcur.whitebal;
+	s32 mirror = (((sd->vcur.mirror > 0) ^ sd->mirrorMask) > 0);
+	s32 flip   = (((sd->vcur.flip   > 0) ^ sd->mirrorMask) > 0);
+	s32 freq   = (sd->vcur.AC50Hz > 0);
+	s32 i;
+
+	if (freq != sd->vold.AC50Hz) {
+		sd->vold.AC50Hz = freq;
+
+		freq = 2 * (freq == 0);
+		ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL);
+		ctrl_out(gspca_dev, 0x40, 1, 0xba02, 0x00f1, 0, NULL);
+		ctrl_out(gspca_dev, 0x40, 1, 0xba00       , 0x005b, 0, NULL);
+		ctrl_out(gspca_dev, 0x40, 1, 0xba01 + freq, 0x00f1, 0, NULL);
+	}
+
+	if (wbal != sd->vold.whitebal) {
+		sd->vold.whitebal = wbal;
+		if (wbal < 0 || wbal > sd->vmax.whitebal)
+			wbal = 0;
+
+		for (i = 0; i < 2; i++) {
+			if (wbal == 0) { /* Normal light */
+				ctrl_out(gspca_dev, 0x40, 1,
+						0x0010, 0x0010, 0, NULL);
+				ctrl_out(gspca_dev, 0x40, 1,
+						0x0003, 0x00c1, 0, NULL);
+				ctrl_out(gspca_dev, 0x40, 1,
+						0x0042, 0x00c2, 0, NULL);
+				ctrl_out(gspca_dev, 0x40, 3,
+						0xba00, 0x0200, 48, dat_wbalNL);
+			}
+
+			if (wbal == 1) { /* Low light */
+				ctrl_out(gspca_dev, 0x40, 1,
+						0x0010, 0x0010, 0, NULL);
+				ctrl_out(gspca_dev, 0x40, 1,
+						0x0004, 0x00c1, 0, NULL);
+				ctrl_out(gspca_dev, 0x40, 1,
+						0x0043, 0x00c2, 0, NULL);
+				ctrl_out(gspca_dev, 0x40, 3,
+						0xba00, 0x0200, 48, dat_wbalLL);
+			}
+
+			if (wbal == 2) { /* Back light */
+				ctrl_out(gspca_dev, 0x40, 1,
+						0x0010, 0x0010, 0, NULL);
+				ctrl_out(gspca_dev, 0x40, 1,
+						0x0003, 0x00c1, 0, NULL);
+				ctrl_out(gspca_dev, 0x40, 1,
+						0x0042, 0x00c2, 0, NULL);
+				ctrl_out(gspca_dev, 0x40, 3,
+						0xba00, 0x0200, 44, dat_wbalBL);
+			}
+		}
+	}
+
+	if (bright != sd->vold.brightness) {
+		sd->vold.brightness = bright;
+		if (bright < 0 || bright > sd->vmax.brightness)
+			bright = 0;
+
+		bright = tbl_bright[bright];
+		ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL);
+		ctrl_out(gspca_dev, 0x40, 1, 0xba01, 0x00f1, 0, NULL);
+		ctrl_out(gspca_dev, 0x40, 1, 0xba00 + bright, 0x0034, 0, NULL);
+		ctrl_out(gspca_dev, 0x40, 1, 0xba00 + bright, 0x00f1, 0, NULL);
+	}
+
+	if (sat != sd->vold.saturation) {
+		sd->vold.saturation = sat;
+		if (sat < 0 || sat > sd->vmax.saturation)
+			sat = 0;
+
+		sat = tbl_sat[sat];
+		ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL);
+		ctrl_out(gspca_dev, 0x40, 1, 0xba01, 0x00f1, 0, NULL);
+		ctrl_out(gspca_dev, 0x40, 1, 0xba00      , 0x0025, 0, NULL);
+		ctrl_out(gspca_dev, 0x40, 1, 0xba00 + sat, 0x00f1, 0, NULL);
+	}
+
+	if (sharp != sd->vold.sharpness) {
+		sd->vold.sharpness = sharp;
+		if (sharp < 0 || sharp > sd->vmax.sharpness)
+			sharp = 0;
+
+		ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL);
+		ctrl_out(gspca_dev, 0x40, 1, 0xba01, 0x00f1, 0, NULL);
+		ctrl_out(gspca_dev, 0x40, 1, 0xba00        , 0x0005, 0, NULL);
+		ctrl_out(gspca_dev, 0x40, 1, 0xba00 + sharp, 0x00f1, 0, NULL);
+	}
+
+	if (hue != sd->vold.hue) {
+		/* 0=normal  1=NB  2="sepia"  3=negative  4=other  5=other2 */
+		if (hue < 0 || hue > sd->vmax.hue)
+			hue = 0;
+		if (hue == sd->vmax.hue)
+			sd->swapRB = 1;
+		else
+			sd->swapRB = 0;
+
+		ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL);
+		ctrl_out(gspca_dev, 0x40, 1, 0xba01, 0x00f1, 0, NULL);
+		ctrl_out(gspca_dev, 0x40, 1, 0xba70, 0x00e2, 0, NULL);
+		ctrl_out(gspca_dev, 0x40, 1, 0xba00 + hue * (hue < 6), 0x00f1,
+							0, NULL);
+	}
+
+	if (backlight != sd->vold.backlight) {
+		sd->vold.backlight = backlight;
+		if (backlight < 0 || backlight > sd->vmax.backlight)
+			backlight = 0;
+
+		backlight = tbl_backlight[backlight];
+		for (i = 0; i < 2; i++) {
+			ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL);
+			ctrl_out(gspca_dev, 0x40, 1, 0xba01, 0x00f1, 0, NULL);
+			ctrl_out(gspca_dev, 0x40, 1, 0xba74, 0x0006, 0, NULL);
+			ctrl_out(gspca_dev, 0x40, 1, 0xba80 + backlight, 0x00f1,
+								0, NULL);
+		}
+	}
+
+	if (hue != sd->vold.hue) {
+		sd->vold.hue = hue;
+
+		ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL);
+		ctrl_out(gspca_dev, 0x40, 1, 0xba01, 0x00f1, 0, NULL);
+		ctrl_out(gspca_dev, 0x40, 1, 0xba70, 0x00e2, 0, NULL);
+		ctrl_out(gspca_dev, 0x40, 1, 0xba00 + hue * (hue < 6), 0x00f1,
+							0, NULL);
+	}
+
+	if (mirror != sd->vold.mirror || flip != sd->vold.flip) {
+		u8 dat_hvflip2[4] = {0x20, 0x01, 0xf1, 0x00};
+		sd->vold.mirror = mirror;
+		sd->vold.flip = flip;
+
+		dat_hvflip2[3] = flip + 2 * mirror;
+		ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 4, dat_hvflip1);
+		ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 4, dat_hvflip2);
+	}
+
+	if (gam != sd->vold.gamma) {
+		sd->vold.gamma = gam;
+		if (gam < 0 || gam > sd->vmax.gamma)
+			gam = 0;
+
+		gam = 2 * gam;
+		ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL);
+		ctrl_out(gspca_dev, 0x40, 1, 0xba01, 0x00f1, 0, NULL);
+		ctrl_out(gspca_dev, 0x40, 1, 0xba04      , 0x003b, 0, NULL);
+		ctrl_out(gspca_dev, 0x40, 1, 0xba02 + gam, 0x00f1, 0, NULL);
+	}
+
+	if (cntr != sd->vold.contrast) {
+		sd->vold.contrast = cntr;
+		if (cntr < 0 || cntr > sd->vmax.contrast)
+			cntr = 0;
+
+		ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL);
+		ctrl_out(gspca_dev, 0x40, 1, 0xba01, 0x00f1, 0, NULL);
+		ctrl_out(gspca_dev, 0x40, 1, 0xba00 + tbl_cntr1[cntr], 0x0035,
+							0, NULL);
+		ctrl_out(gspca_dev, 0x40, 1, 0xba00 + tbl_cntr2[cntr], 0x00f1,
+							0, NULL);
+	}
+
+	return 0;
+}
+
+static void mi1320_post_unset_alt(struct gspca_dev *gspca_dev)
+{
+	ctrl_out(gspca_dev, 0x40, 5, 0x0000, 0x0000, 0, NULL);
+
+	fetch_validx(gspca_dev, tbl_post_unset_alt,
+				ARRAY_SIZE(tbl_post_unset_alt));
+}
diff --git a/drivers/media/usb/gspca/gl860/gl860-mi2020.c b/drivers/media/usb/gspca/gl860/gl860-mi2020.c
new file mode 100644
index 0000000..a785828
--- /dev/null
+++ b/drivers/media/usb/gspca/gl860/gl860-mi2020.c
@@ -0,0 +1,805 @@
+/* Subdriver for the GL860 chip with the MI2020 sensor
+ * Author Olivier LORIN, from logs by Iceman/Soro2005 + Fret_saw/Hulkie/Tricid
+ * with the help of Kytrix/BUGabundo/Blazercist.
+ * Driver achieved thanks to a webcam gift by Kytrix.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+/* Sensor : MI2020 */
+
+#include "gl860.h"
+
+static u8 dat_wbal1[] = {0x8c, 0xa2, 0x0c};
+
+static u8 dat_bright1[] = {0x8c, 0xa2, 0x06};
+static u8 dat_bright3[] = {0x8c, 0xa1, 0x02};
+static u8 dat_bright4[] = {0x90, 0x00, 0x0f};
+static u8 dat_bright5[] = {0x8c, 0xa1, 0x03};
+static u8 dat_bright6[] = {0x90, 0x00, 0x05};
+
+static u8 dat_hvflip1[] = {0x8c, 0x27, 0x19};
+static u8 dat_hvflip3[] = {0x8c, 0x27, 0x3b};
+static u8 dat_hvflip5[] = {0x8c, 0xa1, 0x03};
+static u8 dat_hvflip6[] = {0x90, 0x00, 0x06};
+
+static struct idxdata tbl_middle_hvflip_low[] = {
+	{0x33, {0x90, 0x00, 0x06}},
+	{6, {0xff, 0xff, 0xff}},
+	{0x33, {0x90, 0x00, 0x06}},
+	{6, {0xff, 0xff, 0xff}},
+	{0x33, {0x90, 0x00, 0x06}},
+	{6, {0xff, 0xff, 0xff}},
+	{0x33, {0x90, 0x00, 0x06}},
+	{6, {0xff, 0xff, 0xff}},
+};
+
+static struct idxdata tbl_middle_hvflip_big[] = {
+	{0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x01}},
+	{0x33, {0x8c, 0xa1, 0x20}}, {0x33, {0x90, 0x00, 0x00}},
+	{0x33, {0x8c, 0xa7, 0x02}}, {0x33, {0x90, 0x00, 0x00}},
+	{102, {0xff, 0xff, 0xff}},
+	{0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x02}},
+	{0x33, {0x8c, 0xa1, 0x20}}, {0x33, {0x90, 0x00, 0x72}},
+	{0x33, {0x8c, 0xa7, 0x02}}, {0x33, {0x90, 0x00, 0x01}},
+};
+
+static struct idxdata tbl_end_hvflip[] = {
+	{0x33, {0x8c, 0xa1, 0x02}}, {0x33, {0x90, 0x00, 0x1f}},
+	{6, {0xff, 0xff, 0xff}},
+	{0x33, {0x8c, 0xa1, 0x02}}, {0x33, {0x90, 0x00, 0x1f}},
+	{6, {0xff, 0xff, 0xff}},
+	{0x33, {0x8c, 0xa1, 0x02}}, {0x33, {0x90, 0x00, 0x1f}},
+	{6, {0xff, 0xff, 0xff}},
+	{0x33, {0x8c, 0xa1, 0x02}}, {0x33, {0x90, 0x00, 0x1f}},
+};
+
+static u8 dat_freq1[] = { 0x8c, 0xa4, 0x04 };
+
+static u8 dat_multi5[] = { 0x8c, 0xa1, 0x03 };
+static u8 dat_multi6[] = { 0x90, 0x00, 0x05 };
+
+static struct validx tbl_init_at_startup[] = {
+	{0x0000, 0x0000}, {0x0010, 0x0010}, {0x0008, 0x00c0}, {0x0001, 0x00c1},
+	{0x0001, 0x00c2}, {0x0020, 0x0006}, {0x006a, 0x000d},
+	{53, 0xffff},
+	{0x0040, 0x0000}, {0x0063, 0x0006},
+};
+
+static struct validx tbl_common_0B[] = {
+	{0x0002, 0x0004}, {0x006a, 0x0007}, {0x00ef, 0x0006}, {0x006a, 0x000d},
+	{0x0000, 0x00c0}, {0x0010, 0x0010}, {0x0003, 0x00c1}, {0x0042, 0x00c2},
+	{0x0004, 0x00d8}, {0x0000, 0x0058}, {0x0041, 0x0000},
+};
+
+static struct idxdata tbl_common_3B[] = {
+	{0x33, {0x86, 0x25, 0x01}}, {0x33, {0x86, 0x25, 0x00}},
+	{2, {0xff, 0xff, 0xff}},
+	{0x30, {0x1a, 0x0a, 0xcc}}, {0x32, {0x02, 0x00, 0x08}},
+	{0x33, {0xf4, 0x03, 0x1d}},
+	{6, {0xff, 0xff, 0xff}}, /* 12 */
+	{0x34, {0x1e, 0x8f, 0x09}}, {0x34, {0x1c, 0x01, 0x28}},
+	{0x34, {0x1e, 0x8f, 0x09}},
+	{2, {0xff, 0xff, 0xff}}, /* - */
+	{0x34, {0x1e, 0x8f, 0x09}}, {0x32, {0x14, 0x06, 0xe6}},
+	{0x33, {0x8c, 0x22, 0x23}}, {0x33, {0x90, 0x00, 0x00}},
+	{0x33, {0x8c, 0xa2, 0x0f}}, {0x33, {0x90, 0x00, 0x0d}},
+	{0x33, {0x8c, 0xa2, 0x10}}, {0x33, {0x90, 0x00, 0x0b}},
+	{0x33, {0x8c, 0xa2, 0x11}}, {0x33, {0x90, 0x00, 0x07}},
+	{0x33, {0xf4, 0x03, 0x1d}}, {0x35, {0xa2, 0x00, 0xe2}},
+	{0x33, {0x8c, 0xab, 0x05}}, {0x33, {0x90, 0x00, 0x01}},
+	{0x32, {0x6e, 0x00, 0x86}}, {0x32, {0x70, 0x0f, 0xaa}},
+	{0x32, {0x72, 0x0f, 0xe4}}, {0x33, {0x8c, 0xa3, 0x4a}},
+	{0x33, {0x90, 0x00, 0x5a}}, {0x33, {0x8c, 0xa3, 0x4b}},
+	{0x33, {0x90, 0x00, 0xa6}}, {0x33, {0x8c, 0xa3, 0x61}},
+	{0x33, {0x90, 0x00, 0xc8}}, {0x33, {0x8c, 0xa3, 0x62}},
+	{0x33, {0x90, 0x00, 0xe1}}, {0x34, {0xce, 0x01, 0xa8}},
+	{0x34, {0xd0, 0x66, 0x33}}, {0x34, {0xd2, 0x31, 0x9a}},
+	{0x34, {0xd4, 0x94, 0x63}}, {0x34, {0xd6, 0x4b, 0x25}},
+	{0x34, {0xd8, 0x26, 0x70}}, {0x34, {0xda, 0x72, 0x4c}},
+	{0x34, {0xdc, 0xff, 0x04}}, {0x34, {0xde, 0x01, 0x5b}},
+	{0x34, {0xe6, 0x01, 0x13}}, {0x34, {0xee, 0x0b, 0xf0}},
+	{0x34, {0xf6, 0x0b, 0xa4}}, {0x35, {0x00, 0xf6, 0xe7}},
+	{0x35, {0x08, 0x0d, 0xfd}}, {0x35, {0x10, 0x25, 0x63}},
+	{0x35, {0x18, 0x35, 0x6c}}, {0x35, {0x20, 0x42, 0x7e}},
+	{0x35, {0x28, 0x19, 0x44}}, {0x35, {0x30, 0x39, 0xd4}},
+	{0x35, {0x38, 0xf5, 0xa8}}, {0x35, {0x4c, 0x07, 0x90}},
+	{0x35, {0x44, 0x07, 0xb8}}, {0x35, {0x5c, 0x06, 0x88}},
+	{0x35, {0x54, 0x07, 0xff}}, {0x34, {0xe0, 0x01, 0x52}},
+	{0x34, {0xe8, 0x00, 0xcc}}, {0x34, {0xf0, 0x0d, 0x83}},
+	{0x34, {0xf8, 0x0c, 0xb3}}, {0x35, {0x02, 0xfe, 0xba}},
+	{0x35, {0x0a, 0x04, 0xe0}}, {0x35, {0x12, 0x1c, 0x63}},
+	{0x35, {0x1a, 0x2b, 0x5a}}, {0x35, {0x22, 0x32, 0x5e}},
+	{0x35, {0x2a, 0x0d, 0x28}}, {0x35, {0x32, 0x2c, 0x02}},
+	{0x35, {0x3a, 0xf4, 0xfa}}, {0x35, {0x4e, 0x07, 0xef}},
+	{0x35, {0x46, 0x07, 0x88}}, {0x35, {0x5e, 0x07, 0xc1}},
+	{0x35, {0x56, 0x04, 0x64}}, {0x34, {0xe4, 0x01, 0x15}},
+	{0x34, {0xec, 0x00, 0x82}}, {0x34, {0xf4, 0x0c, 0xce}},
+	{0x34, {0xfc, 0x0c, 0xba}}, {0x35, {0x06, 0x1f, 0x02}},
+	{0x35, {0x0e, 0x02, 0xe3}}, {0x35, {0x16, 0x1a, 0x50}},
+	{0x35, {0x1e, 0x24, 0x39}}, {0x35, {0x26, 0x23, 0x4c}},
+	{0x35, {0x2e, 0xf9, 0x1b}}, {0x35, {0x36, 0x23, 0x19}},
+	{0x35, {0x3e, 0x12, 0x08}}, {0x35, {0x52, 0x07, 0x22}},
+	{0x35, {0x4a, 0x03, 0xd3}}, {0x35, {0x62, 0x06, 0x54}},
+	{0x35, {0x5a, 0x04, 0x5d}}, {0x34, {0xe2, 0x01, 0x04}},
+	{0x34, {0xea, 0x00, 0xa0}}, {0x34, {0xf2, 0x0c, 0xbc}},
+	{0x34, {0xfa, 0x0c, 0x5b}}, {0x35, {0x04, 0x17, 0xf2}},
+	{0x35, {0x0c, 0x02, 0x08}}, {0x35, {0x14, 0x28, 0x43}},
+	{0x35, {0x1c, 0x28, 0x62}}, {0x35, {0x24, 0x2b, 0x60}},
+	{0x35, {0x2c, 0x07, 0x33}}, {0x35, {0x34, 0x1f, 0xb0}},
+	{0x35, {0x3c, 0xed, 0xcd}}, {0x35, {0x50, 0x00, 0x06}},
+	{0x35, {0x48, 0x07, 0xff}}, {0x35, {0x60, 0x05, 0x89}},
+	{0x35, {0x58, 0x07, 0xff}}, {0x35, {0x40, 0x00, 0xa0}},
+	{0x35, {0x42, 0x00, 0x00}}, {0x32, {0x10, 0x01, 0xfc}},
+	{0x33, {0x8c, 0xa1, 0x18}}, {0x33, {0x90, 0x00, 0x3c}},
+	{0x33, {0x78, 0x00, 0x00}},
+	{2, {0xff, 0xff, 0xff}},
+	{0x35, {0xb8, 0x1f, 0x20}}, {0x33, {0x8c, 0xa2, 0x06}},
+	{0x33, {0x90, 0x00, 0x10}}, {0x33, {0x8c, 0xa2, 0x07}},
+	{0x33, {0x90, 0x00, 0x08}}, {0x33, {0x8c, 0xa2, 0x42}},
+	{0x33, {0x90, 0x00, 0x0b}}, {0x33, {0x8c, 0xa2, 0x4a}},
+	{0x33, {0x90, 0x00, 0x8c}}, {0x35, {0xba, 0xfa, 0x08}},
+	{0x33, {0x8c, 0xa2, 0x02}}, {0x33, {0x90, 0x00, 0x22}},
+	{0x33, {0x8c, 0xa2, 0x03}}, {0x33, {0x90, 0x00, 0xbb}},
+	{0x33, {0x8c, 0xa4, 0x04}}, {0x33, {0x90, 0x00, 0x80}},
+	{0x33, {0x8c, 0xa7, 0x9d}}, {0x33, {0x90, 0x00, 0x00}},
+	{0x33, {0x8c, 0xa7, 0x9e}}, {0x33, {0x90, 0x00, 0x00}},
+	{0x33, {0x8c, 0xa2, 0x0c}}, {0x33, {0x90, 0x00, 0x17}},
+	{0x33, {0x8c, 0xa2, 0x15}}, {0x33, {0x90, 0x00, 0x04}},
+	{0x33, {0x8c, 0xa2, 0x14}}, {0x33, {0x90, 0x00, 0x20}},
+	{0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x00}},
+	{0x33, {0x8c, 0x27, 0x17}}, {0x33, {0x90, 0x21, 0x11}},
+	{0x33, {0x8c, 0x27, 0x1b}}, {0x33, {0x90, 0x02, 0x4f}},
+	{0x33, {0x8c, 0x27, 0x25}}, {0x33, {0x90, 0x06, 0x0f}},
+	{0x33, {0x8c, 0x27, 0x39}}, {0x33, {0x90, 0x21, 0x11}},
+	{0x33, {0x8c, 0x27, 0x3d}}, {0x33, {0x90, 0x01, 0x20}},
+	{0x33, {0x8c, 0x27, 0x47}}, {0x33, {0x90, 0x09, 0x4c}},
+	{0x33, {0x8c, 0x27, 0x03}}, {0x33, {0x90, 0x02, 0x84}},
+	{0x33, {0x8c, 0x27, 0x05}}, {0x33, {0x90, 0x01, 0xe2}},
+	{0x33, {0x8c, 0x27, 0x07}}, {0x33, {0x90, 0x06, 0x40}},
+	{0x33, {0x8c, 0x27, 0x09}}, {0x33, {0x90, 0x04, 0xb0}},
+	{0x33, {0x8c, 0x27, 0x0d}}, {0x33, {0x90, 0x00, 0x00}},
+	{0x33, {0x8c, 0x27, 0x0f}}, {0x33, {0x90, 0x00, 0x00}},
+	{0x33, {0x8c, 0x27, 0x11}}, {0x33, {0x90, 0x04, 0xbd}},
+	{0x33, {0x8c, 0x27, 0x13}}, {0x33, {0x90, 0x06, 0x4d}},
+	{0x33, {0x8c, 0x27, 0x15}}, {0x33, {0x90, 0x00, 0x00}},
+	{0x33, {0x8c, 0x27, 0x17}}, {0x33, {0x90, 0x21, 0x11}},
+	{0x33, {0x8c, 0x27, 0x19}}, {0x33, {0x90, 0x04, 0x6c}},
+	{0x33, {0x8c, 0x27, 0x1b}}, {0x33, {0x90, 0x02, 0x4f}},
+	{0x33, {0x8c, 0x27, 0x1d}}, {0x33, {0x90, 0x01, 0x02}},
+	{0x33, {0x8c, 0x27, 0x1f}}, {0x33, {0x90, 0x02, 0x79}},
+	{0x33, {0x8c, 0x27, 0x21}}, {0x33, {0x90, 0x01, 0x55}},
+	{0x33, {0x8c, 0x27, 0x23}}, {0x33, {0x90, 0x02, 0x85}},
+	{0x33, {0x8c, 0x27, 0x25}}, {0x33, {0x90, 0x06, 0x0f}},
+	{0x33, {0x8c, 0x27, 0x27}}, {0x33, {0x90, 0x20, 0x20}},
+	{0x33, {0x8c, 0x27, 0x29}}, {0x33, {0x90, 0x20, 0x20}},
+	{0x33, {0x8c, 0x27, 0x2b}}, {0x33, {0x90, 0x10, 0x20}},
+	{0x33, {0x8c, 0x27, 0x2d}}, {0x33, {0x90, 0x20, 0x07}},
+	{0x33, {0x8c, 0x27, 0x2f}}, {0x33, {0x90, 0x00, 0x04}},
+	{0x33, {0x8c, 0x27, 0x31}}, {0x33, {0x90, 0x00, 0x04}},
+	{0x33, {0x8c, 0x27, 0x33}}, {0x33, {0x90, 0x04, 0xbb}},
+	{0x33, {0x8c, 0x27, 0x35}}, {0x33, {0x90, 0x06, 0x4b}},
+	{0x33, {0x8c, 0x27, 0x37}}, {0x33, {0x90, 0x00, 0x00}},
+	{0x33, {0x8c, 0x27, 0x39}}, {0x33, {0x90, 0x21, 0x11}},
+	{0x33, {0x8c, 0x27, 0x3b}}, {0x33, {0x90, 0x00, 0x24}},
+	{0x33, {0x8c, 0x27, 0x3d}}, {0x33, {0x90, 0x01, 0x20}},
+	{0x33, {0x8c, 0x27, 0x41}}, {0x33, {0x90, 0x01, 0x69}},
+	{0x33, {0x8c, 0x27, 0x45}}, {0x33, {0x90, 0x04, 0xed}},
+	{0x33, {0x8c, 0x27, 0x47}}, {0x33, {0x90, 0x09, 0x4c}},
+	{0x33, {0x8c, 0x27, 0x51}}, {0x33, {0x90, 0x00, 0x00}},
+	{0x33, {0x8c, 0x27, 0x53}}, {0x33, {0x90, 0x03, 0x20}},
+	{0x33, {0x8c, 0x27, 0x55}}, {0x33, {0x90, 0x00, 0x00}},
+	{0x33, {0x8c, 0x27, 0x57}}, {0x33, {0x90, 0x02, 0x58}},
+	{0x33, {0x8c, 0x27, 0x5f}}, {0x33, {0x90, 0x00, 0x00}},
+	{0x33, {0x8c, 0x27, 0x61}}, {0x33, {0x90, 0x06, 0x40}},
+	{0x33, {0x8c, 0x27, 0x63}}, {0x33, {0x90, 0x00, 0x00}},
+	{0x33, {0x8c, 0x27, 0x65}}, {0x33, {0x90, 0x04, 0xb0}},
+	{0x33, {0x8c, 0x22, 0x2e}}, {0x33, {0x90, 0x00, 0xa1}},
+	{0x33, {0x8c, 0xa4, 0x08}}, {0x33, {0x90, 0x00, 0x1f}},
+	{0x33, {0x8c, 0xa4, 0x09}}, {0x33, {0x90, 0x00, 0x21}},
+	{0x33, {0x8c, 0xa4, 0x0a}}, {0x33, {0x90, 0x00, 0x25}},
+	{0x33, {0x8c, 0xa4, 0x0b}}, {0x33, {0x90, 0x00, 0x27}},
+	{0x33, {0x8c, 0x24, 0x11}}, {0x33, {0x90, 0x00, 0xa1}},
+	{0x33, {0x8c, 0x24, 0x13}}, {0x33, {0x90, 0x00, 0xc1}},
+	{0x33, {0x8c, 0x24, 0x15}}, {0x33, {0x90, 0x00, 0x6a}},
+	{0x33, {0x8c, 0x24, 0x17}}, {0x33, {0x90, 0x00, 0x80}},
+	{0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x05}},
+	{2, {0xff, 0xff, 0xff}},
+	{0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x06}},
+	{3, {0xff, 0xff, 0xff}},
+};
+
+static struct idxdata tbl_init_post_alt_low1[] = {
+	{0x33, {0x8c, 0x27, 0x15}}, {0x33, {0x90, 0x00, 0x25}},
+	{0x33, {0x8c, 0x22, 0x2e}}, {0x33, {0x90, 0x00, 0x81}},
+	{0x33, {0x8c, 0xa4, 0x08}}, {0x33, {0x90, 0x00, 0x17}},
+	{0x33, {0x8c, 0xa4, 0x09}}, {0x33, {0x90, 0x00, 0x1a}},
+	{0x33, {0x8c, 0xa4, 0x0a}}, {0x33, {0x90, 0x00, 0x1d}},
+	{0x33, {0x8c, 0xa4, 0x0b}}, {0x33, {0x90, 0x00, 0x20}},
+	{0x33, {0x8c, 0x24, 0x11}}, {0x33, {0x90, 0x00, 0x81}},
+	{0x33, {0x8c, 0x24, 0x13}}, {0x33, {0x90, 0x00, 0x9b}},
+};
+
+static struct idxdata tbl_init_post_alt_low2[] = {
+	{0x33, {0x8c, 0x27, 0x03}}, {0x33, {0x90, 0x03, 0x24}},
+	{0x33, {0x8c, 0x27, 0x05}}, {0x33, {0x90, 0x02, 0x58}},
+	{0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x05}},
+	{2, {0xff, 0xff, 0xff}},
+	{0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x06}},
+	{2, {0xff, 0xff, 0xff}},
+};
+
+static struct idxdata tbl_init_post_alt_low3[] = {
+	{0x34, {0x1e, 0x8f, 0x09}}, {0x34, {0x1c, 0x01, 0x28}},
+	{0x34, {0x1e, 0x8f, 0x09}},
+	{2, {0xff, 0xff, 0xff}},
+	{0x34, {0x1e, 0x8f, 0x09}}, {0x32, {0x14, 0x06, 0xe6}},
+	{0x33, {0x8c, 0xa1, 0x20}}, {0x33, {0x90, 0x00, 0x00}},
+	{0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x01}},
+	{0x33, {0x2e, 0x01, 0x00}}, {0x34, {0x04, 0x00, 0x2a}},
+	{0x33, {0x8c, 0xa7, 0x02}}, {0x33, {0x90, 0x00, 0x00}},
+	{0x33, {0x8c, 0x27, 0x95}}, {0x33, {0x90, 0x01, 0x00}},
+	{2, {0xff, 0xff, 0xff}},
+	{0x33, {0x8c, 0xa1, 0x20}}, {0x33, {0x90, 0x00, 0x72}},
+	{0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x02}},
+	{0x33, {0x8c, 0xa7, 0x02}}, {0x33, {0x90, 0x00, 0x01}},
+	{2, {0xff, 0xff, 0xff}},
+	{0x33, {0x8c, 0xa1, 0x20}}, {0x33, {0x90, 0x00, 0x00}},
+	{0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x01}},
+	{0x33, {0x8c, 0xa7, 0x02}}, {0x33, {0x90, 0x00, 0x00}},
+	{2, {0xff, 0xff, 0xff}},
+	{0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x05}},
+	{2, {0xff, 0xff, 0xff}},
+	{0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x06}},
+	{2, {0xff, 0xff, 0xff}},
+	{0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x05}},
+	{2, {0xff, 0xff, 0xff}},
+	{0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x06}},
+};
+
+static struct idxdata tbl_init_post_alt_big[] = {
+	{0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x05}},
+	{2, {0xff, 0xff, 0xff}},
+	{0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x06}},
+	{2, {0xff, 0xff, 0xff}},
+	{0x34, {0x1e, 0x8f, 0x09}}, {0x34, {0x1c, 0x01, 0x28}},
+	{0x34, {0x1e, 0x8f, 0x09}},
+	{2, {0xff, 0xff, 0xff}},
+	{0x34, {0x1e, 0x8f, 0x09}}, {0x32, {0x14, 0x06, 0xe6}},
+	{0x33, {0x8c, 0xa1, 0x03}},
+	{0x33, {0x90, 0x00, 0x05}},
+	{2, {0xff, 0xff, 0xff}},
+	{0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x06}},
+	{2, {0xff, 0xff, 0xff}},
+	{0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x05}},
+	{2, {0xff, 0xff, 0xff}},
+	{0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x06}},
+	{0x33, {0x8c, 0xa1, 0x20}}, {0x33, {0x90, 0x00, 0x72}},
+	{0x33, {0x8c, 0xa1, 0x30}}, {0x33, {0x90, 0x00, 0x03}},
+	{0x33, {0x8c, 0xa1, 0x31}}, {0x33, {0x90, 0x00, 0x02}},
+	{0x33, {0x8c, 0xa1, 0x32}}, {0x33, {0x90, 0x00, 0x03}},
+	{0x33, {0x8c, 0xa1, 0x34}}, {0x33, {0x90, 0x00, 0x03}},
+	{0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x02}},
+	{0x33, {0x2e, 0x01, 0x00}}, {0x34, {0x04, 0x00, 0x2a}},
+	{0x33, {0x8c, 0xa7, 0x02}}, {0x33, {0x90, 0x00, 0x01}},
+	{0x33, {0x8c, 0x27, 0x97}}, {0x33, {0x90, 0x01, 0x00}},
+	{51, {0xff, 0xff, 0xff}},
+	{0x33, {0x8c, 0xa1, 0x20}}, {0x33, {0x90, 0x00, 0x00}},
+	{0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x01}},
+	{0x33, {0x8c, 0xa7, 0x02}}, {0x33, {0x90, 0x00, 0x00}},
+	{51, {0xff, 0xff, 0xff}},
+	{0x33, {0x8c, 0xa1, 0x20}}, {0x33, {0x90, 0x00, 0x72}},
+	{0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x02}},
+	{0x33, {0x8c, 0xa7, 0x02}}, {0x33, {0x90, 0x00, 0x01}},
+	{51, {0xff, 0xff, 0xff}},
+};
+
+static struct idxdata tbl_init_post_alt_3B[] = {
+	{0x32, {0x10, 0x01, 0xf8}}, {0x34, {0xce, 0x01, 0xa8}},
+	{0x34, {0xd0, 0x66, 0x33}}, {0x34, {0xd2, 0x31, 0x9a}},
+	{0x34, {0xd4, 0x94, 0x63}}, {0x34, {0xd6, 0x4b, 0x25}},
+	{0x34, {0xd8, 0x26, 0x70}}, {0x34, {0xda, 0x72, 0x4c}},
+	{0x34, {0xdc, 0xff, 0x04}}, {0x34, {0xde, 0x01, 0x5b}},
+	{0x34, {0xe6, 0x01, 0x13}}, {0x34, {0xee, 0x0b, 0xf0}},
+	{0x34, {0xf6, 0x0b, 0xa4}}, {0x35, {0x00, 0xf6, 0xe7}},
+	{0x35, {0x08, 0x0d, 0xfd}}, {0x35, {0x10, 0x25, 0x63}},
+	{0x35, {0x18, 0x35, 0x6c}}, {0x35, {0x20, 0x42, 0x7e}},
+	{0x35, {0x28, 0x19, 0x44}}, {0x35, {0x30, 0x39, 0xd4}},
+	{0x35, {0x38, 0xf5, 0xa8}}, {0x35, {0x4c, 0x07, 0x90}},
+	{0x35, {0x44, 0x07, 0xb8}}, {0x35, {0x5c, 0x06, 0x88}},
+	{0x35, {0x54, 0x07, 0xff}}, {0x34, {0xe0, 0x01, 0x52}},
+	{0x34, {0xe8, 0x00, 0xcc}}, {0x34, {0xf0, 0x0d, 0x83}},
+	{0x34, {0xf8, 0x0c, 0xb3}}, {0x35, {0x02, 0xfe, 0xba}},
+	{0x35, {0x0a, 0x04, 0xe0}}, {0x35, {0x12, 0x1c, 0x63}},
+	{0x35, {0x1a, 0x2b, 0x5a}}, {0x35, {0x22, 0x32, 0x5e}},
+	{0x35, {0x2a, 0x0d, 0x28}}, {0x35, {0x32, 0x2c, 0x02}},
+	{0x35, {0x3a, 0xf4, 0xfa}}, {0x35, {0x4e, 0x07, 0xef}},
+	{0x35, {0x46, 0x07, 0x88}}, {0x35, {0x5e, 0x07, 0xc1}},
+	{0x35, {0x56, 0x04, 0x64}}, {0x34, {0xe4, 0x01, 0x15}},
+	{0x34, {0xec, 0x00, 0x82}}, {0x34, {0xf4, 0x0c, 0xce}},
+	{0x34, {0xfc, 0x0c, 0xba}}, {0x35, {0x06, 0x1f, 0x02}},
+	{0x35, {0x0e, 0x02, 0xe3}}, {0x35, {0x16, 0x1a, 0x50}},
+	{0x35, {0x1e, 0x24, 0x39}}, {0x35, {0x26, 0x23, 0x4c}},
+	{0x35, {0x2e, 0xf9, 0x1b}}, {0x35, {0x36, 0x23, 0x19}},
+	{0x35, {0x3e, 0x12, 0x08}}, {0x35, {0x52, 0x07, 0x22}},
+	{0x35, {0x4a, 0x03, 0xd3}}, {0x35, {0x62, 0x06, 0x54}},
+	{0x35, {0x5a, 0x04, 0x5d}}, {0x34, {0xe2, 0x01, 0x04}},
+	{0x34, {0xea, 0x00, 0xa0}}, {0x34, {0xf2, 0x0c, 0xbc}},
+	{0x34, {0xfa, 0x0c, 0x5b}}, {0x35, {0x04, 0x17, 0xf2}},
+	{0x35, {0x0c, 0x02, 0x08}}, {0x35, {0x14, 0x28, 0x43}},
+	{0x35, {0x1c, 0x28, 0x62}}, {0x35, {0x24, 0x2b, 0x60}},
+	{0x35, {0x2c, 0x07, 0x33}}, {0x35, {0x34, 0x1f, 0xb0}},
+	{0x35, {0x3c, 0xed, 0xcd}}, {0x35, {0x50, 0x00, 0x06}},
+	{0x35, {0x48, 0x07, 0xff}}, {0x35, {0x60, 0x05, 0x89}},
+	{0x35, {0x58, 0x07, 0xff}}, {0x35, {0x40, 0x00, 0xa0}},
+	{0x35, {0x42, 0x00, 0x00}}, {0x32, {0x10, 0x01, 0xfc}},
+	{0x33, {0x8c, 0xa1, 0x18}}, {0x33, {0x90, 0x00, 0x3c}},
+};
+
+static u8 *dat_640  = "\xd0\x02\xd1\x08\xd2\xe1\xd3\x02\xd4\x10\xd5\x81";
+static u8 *dat_800  = "\xd0\x02\xd1\x10\xd2\x57\xd3\x02\xd4\x18\xd5\x21";
+static u8 *dat_1280 = "\xd0\x02\xd1\x20\xd2\x01\xd3\x02\xd4\x28\xd5\x01";
+static u8 *dat_1600 = "\xd0\x02\xd1\x20\xd2\xaf\xd3\x02\xd4\x30\xd5\x41";
+
+static int  mi2020_init_at_startup(struct gspca_dev *gspca_dev);
+static int  mi2020_configure_alt(struct gspca_dev *gspca_dev);
+static int  mi2020_init_pre_alt(struct gspca_dev *gspca_dev);
+static int  mi2020_init_post_alt(struct gspca_dev *gspca_dev);
+static void mi2020_post_unset_alt(struct gspca_dev *gspca_dev);
+static int  mi2020_camera_settings(struct gspca_dev *gspca_dev);
+/*==========================================================================*/
+
+void mi2020_init_settings(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	sd->vcur.backlight  =  0;
+	sd->vcur.brightness = 70;
+	sd->vcur.sharpness  = 20;
+	sd->vcur.contrast   =  0;
+	sd->vcur.gamma      =  0;
+	sd->vcur.hue        =  0;
+	sd->vcur.saturation = 60;
+	sd->vcur.whitebal   =  0; /* 50, not done by hardware */
+	sd->vcur.mirror = 0;
+	sd->vcur.flip   = 0;
+	sd->vcur.AC50Hz = 1;
+
+	sd->vmax.backlight  =  64;
+	sd->vmax.brightness = 128;
+	sd->vmax.sharpness  =  40;
+	sd->vmax.contrast   =   3;
+	sd->vmax.gamma      =   2;
+	sd->vmax.hue        =   0 + 1; /* 200, not done by hardware */
+	sd->vmax.saturation =   0;     /* 100, not done by hardware */
+	sd->vmax.whitebal   =   2;     /* 100, not done by hardware */
+	sd->vmax.mirror = 1;
+	sd->vmax.flip   = 1;
+	sd->vmax.AC50Hz = 1;
+
+	sd->dev_camera_settings = mi2020_camera_settings;
+	sd->dev_init_at_startup = mi2020_init_at_startup;
+	sd->dev_configure_alt   = mi2020_configure_alt;
+	sd->dev_init_pre_alt    = mi2020_init_pre_alt;
+	sd->dev_post_unset_alt  = mi2020_post_unset_alt;
+}
+
+/*==========================================================================*/
+
+static void common(struct gspca_dev *gspca_dev)
+{
+	fetch_validx(gspca_dev, tbl_common_0B, ARRAY_SIZE(tbl_common_0B));
+	fetch_idxdata(gspca_dev, tbl_common_3B, ARRAY_SIZE(tbl_common_3B));
+	ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x0000, 0, NULL);
+}
+
+static int mi2020_init_at_startup(struct gspca_dev *gspca_dev)
+{
+	u8 c;
+
+	ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0004, 1, &c);
+	ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0004, 1, &c);
+
+	fetch_validx(gspca_dev, tbl_init_at_startup,
+			ARRAY_SIZE(tbl_init_at_startup));
+
+	ctrl_out(gspca_dev, 0x40,  1, 0x7a00, 0x8030,  0, NULL);
+	ctrl_in(gspca_dev, 0xc0,  2, 0x7a00, 0x8030,  1, &c);
+
+	common(gspca_dev);
+
+	msleep(61);
+/*	ctrl_out(gspca_dev, 0x40, 11, 0x0000, 0x0000,  0, NULL); */
+/*	msleep(36); */
+	ctrl_out(gspca_dev, 0x40,  1, 0x0001, 0x0000,  0, NULL);
+
+	return 0;
+}
+
+static int mi2020_init_pre_alt(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	sd->mirrorMask =  0;
+	sd->vold.hue   = -1;
+
+	/* These controls need to be reset */
+	sd->vold.brightness = -1;
+	sd->vold.sharpness  = -1;
+
+	/* If not different from default, they do not need to be set */
+	sd->vold.contrast  = 0;
+	sd->vold.gamma     = 0;
+	sd->vold.backlight = 0;
+
+	mi2020_init_post_alt(gspca_dev);
+
+	return 0;
+}
+
+static int mi2020_init_post_alt(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv;
+
+	s32 mirror = (((sd->vcur.mirror > 0) ^ sd->mirrorMask) > 0);
+	s32 flip   = (((sd->vcur.flip   > 0) ^ sd->mirrorMask) > 0);
+	s32 freq   = (sd->vcur.AC50Hz  > 0);
+	s32 wbal   = sd->vcur.whitebal;
+
+	u8 dat_freq2[] = {0x90, 0x00, 0x80};
+	u8 dat_multi1[] = {0x8c, 0xa7, 0x00};
+	u8 dat_multi2[] = {0x90, 0x00, 0x00};
+	u8 dat_multi3[] = {0x8c, 0xa7, 0x00};
+	u8 dat_multi4[] = {0x90, 0x00, 0x00};
+	u8 dat_hvflip2[] = {0x90, 0x04, 0x6c};
+	u8 dat_hvflip4[] = {0x90, 0x00, 0x24};
+	u8 dat_wbal2[] = {0x90, 0x00, 0x00};
+	u8 c;
+
+	sd->nbIm = -1;
+
+	dat_freq2[2] = freq ? 0xc0 : 0x80;
+	dat_multi1[2] = 0x9d;
+	dat_multi3[2] = dat_multi1[2] + 1;
+	if (wbal == 0) {
+		dat_multi4[2] = dat_multi2[2] = 0;
+		dat_wbal2[2] = 0x17;
+	} else if (wbal == 1) {
+		dat_multi4[2] = dat_multi2[2] = 0;
+		dat_wbal2[2] = 0x35;
+	} else if (wbal == 2) {
+		dat_multi4[2] = dat_multi2[2] = 0x20;
+		dat_wbal2[2] = 0x17;
+	}
+	dat_hvflip2[2] = 0x6c + 2 * (1 - flip) + (1 - mirror);
+	dat_hvflip4[2] = 0x24 + 2 * (1 - flip) + (1 - mirror);
+
+	msleep(200);
+	ctrl_out(gspca_dev, 0x40, 5, 0x0001, 0x0000, 0, NULL);
+	msleep(2);
+
+	common(gspca_dev);
+
+	msleep(142);
+	ctrl_out(gspca_dev, 0x40,  1, 0x0010, 0x0010,  0, NULL);
+	ctrl_out(gspca_dev, 0x40,  1, 0x0003, 0x00c1,  0, NULL);
+	ctrl_out(gspca_dev, 0x40,  1, 0x0042, 0x00c2,  0, NULL);
+	ctrl_out(gspca_dev, 0x40,  1, 0x006a, 0x000d,  0, NULL);
+
+	switch (reso) {
+	case IMAGE_640:
+	case IMAGE_800:
+		if (reso != IMAGE_800)
+			ctrl_out(gspca_dev, 0x40,  3, 0x0000, 0x0200,
+				12, dat_640);
+		else
+			ctrl_out(gspca_dev, 0x40,  3, 0x0000, 0x0200,
+				12, dat_800);
+
+		fetch_idxdata(gspca_dev, tbl_init_post_alt_low1,
+					ARRAY_SIZE(tbl_init_post_alt_low1));
+
+		if (reso == IMAGE_800)
+			fetch_idxdata(gspca_dev, tbl_init_post_alt_low2,
+					ARRAY_SIZE(tbl_init_post_alt_low2));
+
+		fetch_idxdata(gspca_dev, tbl_init_post_alt_low3,
+				ARRAY_SIZE(tbl_init_post_alt_low3));
+
+		ctrl_out(gspca_dev, 0x40, 1, 0x0010, 0x0010, 0, NULL);
+		ctrl_out(gspca_dev, 0x40, 1, 0x0000, 0x00c1, 0, NULL);
+		ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x00c2, 0, NULL);
+		msleep(120);
+		break;
+
+	case IMAGE_1280:
+	case IMAGE_1600:
+		if (reso == IMAGE_1280) {
+			ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200,
+					12, dat_1280);
+			ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033,
+					3, "\x8c\x27\x07");
+			ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033,
+					3, "\x90\x05\x04");
+			ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033,
+					3, "\x8c\x27\x09");
+			ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033,
+					3, "\x90\x04\x02");
+		} else {
+			ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200,
+					12, dat_1600);
+			ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033,
+					3, "\x8c\x27\x07");
+			ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033,
+					3, "\x90\x06\x40");
+			ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033,
+					3, "\x8c\x27\x09");
+			ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033,
+					3, "\x90\x04\xb0");
+		}
+
+		fetch_idxdata(gspca_dev, tbl_init_post_alt_big,
+				ARRAY_SIZE(tbl_init_post_alt_big));
+
+		ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x0010, 0, NULL);
+		ctrl_out(gspca_dev, 0x40, 1, 0x0000, 0x00c1, 0, NULL);
+		ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x00c2, 0, NULL);
+		msleep(1850);
+	}
+
+	ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000, 0, NULL);
+	msleep(40);
+
+	/* AC power frequency */
+	ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_freq1);
+	ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_freq2);
+	msleep(33);
+	/* light source */
+	ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi1);
+	ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi2);
+	ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi3);
+	ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi4);
+	ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_wbal1);
+	ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_wbal2);
+	ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi5);
+	ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi6);
+	msleep(7);
+	ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, &c);
+
+	fetch_idxdata(gspca_dev, tbl_init_post_alt_3B,
+			ARRAY_SIZE(tbl_init_post_alt_3B));
+
+	/* hvflip */
+	ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip1);
+	ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip2);
+	ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip3);
+	ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip4);
+	ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip5);
+	ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip6);
+	msleep(250);
+
+	if (reso == IMAGE_640 || reso == IMAGE_800)
+		fetch_idxdata(gspca_dev, tbl_middle_hvflip_low,
+				ARRAY_SIZE(tbl_middle_hvflip_low));
+	else
+		fetch_idxdata(gspca_dev, tbl_middle_hvflip_big,
+				ARRAY_SIZE(tbl_middle_hvflip_big));
+
+	fetch_idxdata(gspca_dev, tbl_end_hvflip,
+			ARRAY_SIZE(tbl_end_hvflip));
+
+	sd->nbIm = 0;
+
+	sd->vold.mirror    = mirror;
+	sd->vold.flip      = flip;
+	sd->vold.AC50Hz    = freq;
+	sd->vold.whitebal  = wbal;
+
+	mi2020_camera_settings(gspca_dev);
+
+	return 0;
+}
+
+static int mi2020_configure_alt(struct gspca_dev *gspca_dev)
+{
+	s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv;
+
+	switch (reso) {
+	case IMAGE_640:
+		gspca_dev->alt = 3 + 1;
+		break;
+
+	case IMAGE_800:
+	case IMAGE_1280:
+	case IMAGE_1600:
+		gspca_dev->alt = 1 + 1;
+		break;
+	}
+	return 0;
+}
+
+static int mi2020_camera_settings(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv;
+
+	s32 backlight = sd->vcur.backlight;
+	s32 bright =  sd->vcur.brightness;
+	s32 sharp  =  sd->vcur.sharpness;
+	s32 cntr   =  sd->vcur.contrast;
+	s32 gam	   =  sd->vcur.gamma;
+	s32 hue    = (sd->vcur.hue > 0);
+	s32 mirror = (((sd->vcur.mirror > 0) ^ sd->mirrorMask) > 0);
+	s32 flip   = (((sd->vcur.flip   > 0) ^ sd->mirrorMask) > 0);
+	s32 freq   = (sd->vcur.AC50Hz > 0);
+	s32 wbal   = sd->vcur.whitebal;
+
+	u8 dat_sharp[] = {0x6c, 0x00, 0x08};
+	u8 dat_bright2[] = {0x90, 0x00, 0x00};
+	u8 dat_freq2[] = {0x90, 0x00, 0x80};
+	u8 dat_multi1[] = {0x8c, 0xa7, 0x00};
+	u8 dat_multi2[] = {0x90, 0x00, 0x00};
+	u8 dat_multi3[] = {0x8c, 0xa7, 0x00};
+	u8 dat_multi4[] = {0x90, 0x00, 0x00};
+	u8 dat_hvflip2[] = {0x90, 0x04, 0x6c};
+	u8 dat_hvflip4[] = {0x90, 0x00, 0x24};
+	u8 dat_wbal2[] = {0x90, 0x00, 0x00};
+
+	/* Less than 4 images received -> too early to set the settings */
+	if (sd->nbIm < 4) {
+		sd->waitSet = 1;
+		return 0;
+	}
+	sd->waitSet = 0;
+
+	if (freq != sd->vold.AC50Hz) {
+		sd->vold.AC50Hz = freq;
+
+		dat_freq2[2] = freq ? 0xc0 : 0x80;
+		ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_freq1);
+		ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_freq2);
+		msleep(20);
+	}
+
+	if (wbal != sd->vold.whitebal) {
+		sd->vold.whitebal = wbal;
+		if (wbal < 0 || wbal > sd->vmax.whitebal)
+			wbal = 0;
+
+		dat_multi1[2] = 0x9d;
+		dat_multi3[2] = dat_multi1[2] + 1;
+		if (wbal == 0) {
+			dat_multi4[2] = dat_multi2[2] = 0;
+			dat_wbal2[2] = 0x17;
+		} else if (wbal == 1) {
+			dat_multi4[2] = dat_multi2[2] = 0;
+			dat_wbal2[2] = 0x35;
+		} else if (wbal == 2) {
+			dat_multi4[2] = dat_multi2[2] = 0x20;
+			dat_wbal2[2] = 0x17;
+		}
+		ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi1);
+		ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi2);
+		ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi3);
+		ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi4);
+		ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_wbal1);
+		ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_wbal2);
+		ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi5);
+		ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi6);
+	}
+
+	if (mirror != sd->vold.mirror || flip != sd->vold.flip) {
+		sd->vold.mirror = mirror;
+		sd->vold.flip   = flip;
+
+		dat_hvflip2[2] = 0x6c + 2 * (1 - flip) + (1 - mirror);
+		dat_hvflip4[2] = 0x24 + 2 * (1 - flip) + (1 - mirror);
+
+		fetch_idxdata(gspca_dev, tbl_init_post_alt_3B,
+				ARRAY_SIZE(tbl_init_post_alt_3B));
+
+		ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip1);
+		ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip2);
+		ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip3);
+		ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip4);
+		ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip5);
+		ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip6);
+		msleep(40);
+
+		if (reso == IMAGE_640 || reso == IMAGE_800)
+			fetch_idxdata(gspca_dev, tbl_middle_hvflip_low,
+					ARRAY_SIZE(tbl_middle_hvflip_low));
+		else
+			fetch_idxdata(gspca_dev, tbl_middle_hvflip_big,
+					ARRAY_SIZE(tbl_middle_hvflip_big));
+
+		fetch_idxdata(gspca_dev, tbl_end_hvflip,
+				ARRAY_SIZE(tbl_end_hvflip));
+	}
+
+	if (bright != sd->vold.brightness) {
+		sd->vold.brightness = bright;
+		if (bright < 0 || bright > sd->vmax.brightness)
+			bright = 0;
+
+		dat_bright2[2] = bright;
+		ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright1);
+		ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright2);
+		ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright3);
+		ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright4);
+		ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright5);
+		ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright6);
+	}
+
+	if (cntr != sd->vold.contrast || gam != sd->vold.gamma) {
+		sd->vold.contrast = cntr;
+		if (cntr < 0 || cntr > sd->vmax.contrast)
+			cntr = 0;
+		sd->vold.gamma = gam;
+		if (gam < 0 || gam > sd->vmax.gamma)
+			gam = 0;
+
+		dat_multi1[2] = 0x6d;
+		dat_multi3[2] = dat_multi1[2] + 1;
+		if (cntr == 0)
+			cntr = 4;
+		dat_multi4[2] = dat_multi2[2] = cntr * 0x10 + 2 - gam;
+		ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi1);
+		ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi2);
+		ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi3);
+		ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi4);
+		ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi5);
+		ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi6);
+	}
+
+	if (backlight != sd->vold.backlight) {
+		sd->vold.backlight = backlight;
+		if (backlight < 0 || backlight > sd->vmax.backlight)
+			backlight = 0;
+
+		dat_multi1[2] = 0x9d;
+		dat_multi3[2] = dat_multi1[2] + 1;
+		dat_multi4[2] = dat_multi2[2] = backlight;
+		ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi1);
+		ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi2);
+		ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi3);
+		ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi4);
+		ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi5);
+		ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi6);
+	}
+
+	if (sharp != sd->vold.sharpness) {
+		sd->vold.sharpness = sharp;
+		if (sharp < 0 || sharp > sd->vmax.sharpness)
+			sharp = 0;
+
+		dat_sharp[1] = sharp;
+		ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0032, 3, dat_sharp);
+	}
+
+	if (hue != sd->vold.hue) {
+		sd->swapRB = hue;
+		sd->vold.hue = hue;
+	}
+
+	return 0;
+}
+
+static void mi2020_post_unset_alt(struct gspca_dev *gspca_dev)
+{
+	ctrl_out(gspca_dev, 0x40, 5, 0x0000, 0x0000, 0, NULL);
+	msleep(40);
+	ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x0000, 0, NULL);
+}
diff --git a/drivers/media/usb/gspca/gl860/gl860-ov2640.c b/drivers/media/usb/gspca/gl860/gl860-ov2640.c
new file mode 100644
index 0000000..768cac5
--- /dev/null
+++ b/drivers/media/usb/gspca/gl860/gl860-ov2640.c
@@ -0,0 +1,489 @@
+/* Subdriver for the GL860 chip with the OV2640 sensor
+ * Author Olivier LORIN, from Malmostoso's logs
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+/* Sensor : OV2640 */
+
+#include "gl860.h"
+
+static u8 dat_init1[] = "\x00\x41\x07\x6a\x06\x61\x0d\x6a" "\x10\x10\xc1\x01";
+
+static u8 c61[] = {0x61}; /* expected */
+static u8 c51[] = {0x51}; /* expected */
+static u8 c50[] = {0x50}; /* expected */
+static u8 c28[] = {0x28}; /* expected */
+static u8 ca8[] = {0xa8}; /* expected */
+
+static u8 dat_post[] =
+	"\x00\x41\x07\x6a\x06\xef\x0d\x6a" "\x10\x10\xc1\x01";
+
+static u8 dat_640[]  = "\xd0\x01\xd1\x08\xd2\xe0\xd3\x02\xd4\x10\xd5\x81";
+static u8 dat_800[]  = "\xd0\x01\xd1\x10\xd2\x58\xd3\x02\xd4\x18\xd5\x21";
+static u8 dat_1280[] = "\xd0\x01\xd1\x18\xd2\xc0\xd3\x02\xd4\x28\xd5\x01";
+static u8 dat_1600[] = "\xd0\x01\xd1\x20\xd2\xb0\xd3\x02\xd4\x30\xd5\x41";
+
+static struct validx tbl_init_at_startup[] = {
+	{0x0000, 0x0000}, {0x0010, 0x0010}, {0x0008, 0x00c0}, {0x0001, 0x00c1},
+	{0x0001, 0x00c2}, {0x0020, 0x0006}, {0x006a, 0x000d},
+	{0x0050, 0x0000}, {0x0041, 0x0000}, {0x006a, 0x0007}, {0x0061, 0x0006},
+	{0x006a, 0x000d}, {0x0000, 0x00c0}, {0x0010, 0x0010}, {0x0001, 0x00c1},
+	{0x0041, 0x00c2}, {0x0004, 0x00d8}, {0x0012, 0x0004}, {0x0000, 0x0058},
+	{0x0041, 0x0000}, {0x0061, 0x0000},
+};
+
+static struct validx tbl_common[] = {
+	{0x6000, 0x00ff}, {0x60ff, 0x002c}, {0x60df, 0x002e}, {0x6001, 0x00ff},
+	{0x6080, 0x0012}, {0x6000, 0x0000}, {0x6000, 0x0045}, {0x6000, 0x0010},
+	{0x6035, 0x003c}, {0x6000, 0x0011}, {0x6028, 0x0004}, {0x60e5, 0x0013},
+	{0x6088, 0x0014}, {0x600c, 0x002c}, {0x6078, 0x0033}, {0x60f7, 0x003b},
+	{0x6000, 0x003e}, {0x6011, 0x0043}, {0x6010, 0x0016}, {0x6082, 0x0039},
+	{0x6088, 0x0035}, {0x600a, 0x0022}, {0x6040, 0x0037}, {0x6000, 0x0023},
+	{0x60a0, 0x0034}, {0x601a, 0x0036}, {0x6002, 0x0006}, {0x60c0, 0x0007},
+	{0x60b7, 0x000d}, {0x6001, 0x000e}, {0x6000, 0x004c}, {0x6081, 0x004a},
+	{0x6099, 0x0021}, {0x6002, 0x0009}, {0x603e, 0x0024}, {0x6034, 0x0025},
+	{0x6081, 0x0026}, {0x6000, 0x0000}, {0x6000, 0x0045}, {0x6000, 0x0010},
+	{0x6000, 0x005c}, {0x6000, 0x0063}, {0x6000, 0x007c}, {0x6070, 0x0061},
+	{0x6080, 0x0062}, {0x6080, 0x0020}, {0x6030, 0x0028}, {0x6000, 0x006c},
+	{0x6000, 0x006e}, {0x6002, 0x0070}, {0x6094, 0x0071}, {0x60c1, 0x0073},
+	{0x6034, 0x003d}, {0x6057, 0x005a}, {0x60bb, 0x004f}, {0x609c, 0x0050},
+	{0x6080, 0x006d}, {0x6002, 0x0039}, {0x6033, 0x003a}, {0x60f1, 0x003b},
+	{0x6031, 0x003c}, {0x6000, 0x00ff}, {0x6014, 0x00e0}, {0x60ff, 0x0076},
+	{0x60a0, 0x0033}, {0x6020, 0x0042}, {0x6018, 0x0043}, {0x6000, 0x004c},
+	{0x60d0, 0x0087}, {0x600f, 0x0088}, {0x6003, 0x00d7}, {0x6010, 0x00d9},
+	{0x6005, 0x00da}, {0x6082, 0x00d3}, {0x60c0, 0x00f9}, {0x6006, 0x0044},
+	{0x6007, 0x00d1}, {0x6002, 0x00d2}, {0x6000, 0x00d2}, {0x6011, 0x00d8},
+	{0x6008, 0x00c8}, {0x6080, 0x00c9}, {0x6008, 0x007c}, {0x6020, 0x007d},
+	{0x6020, 0x007d}, {0x6000, 0x0090}, {0x600e, 0x0091}, {0x601a, 0x0091},
+	{0x6031, 0x0091}, {0x605a, 0x0091}, {0x6069, 0x0091}, {0x6075, 0x0091},
+	{0x607e, 0x0091}, {0x6088, 0x0091}, {0x608f, 0x0091}, {0x6096, 0x0091},
+	{0x60a3, 0x0091}, {0x60af, 0x0091}, {0x60c4, 0x0091}, {0x60d7, 0x0091},
+	{0x60e8, 0x0091}, {0x6020, 0x0091}, {0x6000, 0x0092}, {0x6006, 0x0093},
+	{0x60e3, 0x0093}, {0x6005, 0x0093}, {0x6005, 0x0093}, {0x6000, 0x0093},
+	{0x6004, 0x0093}, {0x6000, 0x0093}, {0x6000, 0x0093}, {0x6000, 0x0093},
+	{0x6000, 0x0093}, {0x6000, 0x0093}, {0x6000, 0x0093}, {0x6000, 0x0093},
+	{0x6000, 0x0096}, {0x6008, 0x0097}, {0x6019, 0x0097}, {0x6002, 0x0097},
+	{0x600c, 0x0097}, {0x6024, 0x0097}, {0x6030, 0x0097}, {0x6028, 0x0097},
+	{0x6026, 0x0097}, {0x6002, 0x0097}, {0x6098, 0x0097}, {0x6080, 0x0097},
+	{0x6000, 0x0097}, {0x6000, 0x0097}, {0x60ed, 0x00c3}, {0x609a, 0x00c4},
+	{0x6000, 0x00a4}, {0x6011, 0x00c5}, {0x6051, 0x00c6}, {0x6010, 0x00c7},
+	{0x6066, 0x00b6}, {0x60a5, 0x00b8}, {0x6064, 0x00b7}, {0x607c, 0x00b9},
+	{0x60af, 0x00b3}, {0x6097, 0x00b4}, {0x60ff, 0x00b5}, {0x60c5, 0x00b0},
+	{0x6094, 0x00b1}, {0x600f, 0x00b2}, {0x605c, 0x00c4}, {0x6000, 0x00a8},
+	{0x60c8, 0x00c0}, {0x6096, 0x00c1}, {0x601d, 0x0086}, {0x6000, 0x0050},
+	{0x6090, 0x0051}, {0x6018, 0x0052}, {0x6000, 0x0053}, {0x6000, 0x0054},
+	{0x6088, 0x0055}, {0x6000, 0x0057}, {0x6090, 0x005a}, {0x6018, 0x005b},
+	{0x6005, 0x005c}, {0x60ed, 0x00c3}, {0x6000, 0x007f}, {0x6005, 0x00da},
+	{0x601f, 0x00e5}, {0x6067, 0x00e1}, {0x6000, 0x00e0}, {0x60ff, 0x00dd},
+	{0x6000, 0x0005}, {0x6001, 0x00ff}, {0x6000, 0x0000}, {0x6000, 0x0045},
+	{0x6000, 0x0010},
+};
+
+static struct validx tbl_sensor_settings_common1[] = {
+	{0x0041, 0x0000}, {0x006a, 0x0007}, {0x00ef, 0x0006}, {0x006a, 0x000d},
+	{0x0000, 0x00c0}, {0x0010, 0x0010}, {0x0001, 0x00c1}, {0x0041, 0x00c2},
+	{0x0004, 0x00d8}, {0x0012, 0x0004}, {0x0000, 0x0058}, {0x0041, 0x0000},
+	{50, 0xffff},
+	{0x0061, 0x0000},
+	{0xffff, 0xffff},
+	{0x6000, 0x00ff}, {0x6000, 0x007c}, {0x6007, 0x007d},
+	{30, 0xffff},
+	{0x0040, 0x0000},
+};
+
+static struct validx tbl_sensor_settings_common2[] = {
+	{0x6001, 0x00ff}, {0x6038, 0x000c},
+	{10, 0xffff},
+	{0x6000, 0x0011},
+};
+
+static struct validx tbl_640[] = {
+	{0x6000, 0x00ff}, {0x60f1, 0x00dd}, {0x6004, 0x00e0}, {0x6067, 0x00e1},
+	{0x6004, 0x00da}, {0x6000, 0x00ff}, {0x60f1, 0x00dd}, {0x6004, 0x00e0},
+	{0x6001, 0x00ff}, {0x6000, 0x0012}, {0x6000, 0x0011}, {0x6011, 0x0017},
+	{0x6075, 0x0018}, {0x6001, 0x0019}, {0x6097, 0x001a}, {0x6036, 0x0032},
+	{0x60bb, 0x004f}, {0x6057, 0x005a}, {0x609c, 0x0050}, {0x6080, 0x006d},
+	{0x6092, 0x0026}, {0x60ff, 0x0020}, {0x6000, 0x0027}, {0x6000, 0x00ff},
+	{0x60c8, 0x00c0}, {0x6096, 0x00c1}, {0x6000, 0x008c}, {0x603d, 0x0086},
+	{0x6089, 0x0050}, {0x6090, 0x0051}, {0x602c, 0x0052}, {0x6000, 0x0053},
+	{0x6000, 0x0054}, {0x6088, 0x0055}, {0x6000, 0x0057}, {0x60a0, 0x005a},
+	{0x6078, 0x005b}, {0x6000, 0x005c}, {0x6004, 0x00d3}, {0x6000, 0x00e0},
+	{0x60ff, 0x00dd}, {0x60a1, 0x005a},
+};
+
+static struct validx tbl_800[] = {
+	{0x6000, 0x00ff}, {0x60f1, 0x00dd}, {0x6004, 0x00e0}, {0x6067, 0x00e1},
+	{0x6004, 0x00da}, {0x6000, 0x00ff}, {0x60f1, 0x00dd}, {0x6004, 0x00e0},
+	{0x6001, 0x00ff}, {0x6040, 0x0012}, {0x6000, 0x0011}, {0x6011, 0x0017},
+	{0x6043, 0x0018}, {0x6000, 0x0019}, {0x604b, 0x001a}, {0x6009, 0x0032},
+	{0x60ca, 0x004f}, {0x60a8, 0x0050}, {0x6000, 0x006d}, {0x6038, 0x003d},
+	{0x60c8, 0x0035}, {0x6000, 0x0022}, {0x6092, 0x0026}, {0x60ff, 0x0020},
+	{0x6000, 0x0027}, {0x6000, 0x00ff}, {0x6064, 0x00c0}, {0x604b, 0x00c1},
+	{0x6000, 0x008c}, {0x601d, 0x0086}, {0x6082, 0x00d3}, {0x6000, 0x00e0},
+	{0x60ff, 0x00dd}, {0x6020, 0x008c}, {0x6001, 0x00ff}, {0x6044, 0x0018},
+};
+
+static struct validx tbl_big1[] = {
+	{0x0002, 0x00c1}, {0x6000, 0x00ff}, {0x60f1, 0x00dd}, {0x6004, 0x00e0},
+	{0x6001, 0x00ff}, {0x6000, 0x0012}, {0x6000, 0x0000}, {0x6000, 0x0045},
+	{0x6000, 0x0010}, {0x6000, 0x0011}, {0x6011, 0x0017}, {0x6075, 0x0018},
+	{0x6001, 0x0019}, {0x6097, 0x001a}, {0x6036, 0x0032}, {0x60bb, 0x004f},
+	{0x609c, 0x0050}, {0x6057, 0x005a}, {0x6080, 0x006d}, {0x6043, 0x000f},
+	{0x608f, 0x0003}, {0x6005, 0x007c}, {0x6081, 0x0026}, {0x6000, 0x00ff},
+	{0x60c8, 0x00c0}, {0x6096, 0x00c1}, {0x6000, 0x008c},
+};
+
+static struct validx tbl_big2[] = {
+	{0x603d, 0x0086}, {0x6000, 0x0050}, {0x6090, 0x0051}, {0x602c, 0x0052},
+	{0x6000, 0x0053}, {0x6000, 0x0054}, {0x6088, 0x0055}, {0x6000, 0x0057},
+	{0x6040, 0x005a}, {0x60f0, 0x005b}, {0x6001, 0x005c}, {0x6082, 0x00d3},
+	{0x6000, 0x008e},
+};
+
+static struct validx tbl_big3[] = {
+	{0x6004, 0x00da}, {0x6000, 0x00e0}, {0x6067, 0x00e1}, {0x60ff, 0x00dd},
+	{0x6001, 0x00ff}, {0x6000, 0x00ff}, {0x60f1, 0x00dd}, {0x6004, 0x00e0},
+	{0x6001, 0x00ff}, {0x6000, 0x0011}, {0x6000, 0x00ff}, {0x6010, 0x00c7},
+	{0x6000, 0x0092}, {0x6006, 0x0093}, {0x60e3, 0x0093}, {0x6005, 0x0093},
+	{0x6005, 0x0093}, {0x60ed, 0x00c3}, {0x6000, 0x00a4}, {0x60d0, 0x0087},
+	{0x6003, 0x0096}, {0x600c, 0x0097}, {0x6024, 0x0097}, {0x6030, 0x0097},
+	{0x6028, 0x0097}, {0x6026, 0x0097}, {0x6002, 0x0097}, {0x6001, 0x00ff},
+	{0x6043, 0x000f}, {0x608f, 0x0003}, {0x6000, 0x002d}, {0x6000, 0x002e},
+	{0x600a, 0x0022}, {0x6002, 0x0070}, {0x6008, 0x0014}, {0x6048, 0x0014},
+	{0x6000, 0x00ff}, {0x6000, 0x00e0}, {0x60ff, 0x00dd},
+};
+
+static struct validx tbl_post_unset_alt[] = {
+	{0x006a, 0x000d}, {0x6001, 0x00ff}, {0x6081, 0x0026}, {0x6000, 0x0000},
+	{0x6000, 0x0045}, {0x6000, 0x0010}, {0x6068, 0x000d},
+	{50, 0xffff},
+	{0x0021, 0x0000},
+};
+
+static int  ov2640_init_at_startup(struct gspca_dev *gspca_dev);
+static int  ov2640_configure_alt(struct gspca_dev *gspca_dev);
+static int  ov2640_init_pre_alt(struct gspca_dev *gspca_dev);
+static int  ov2640_init_post_alt(struct gspca_dev *gspca_dev);
+static void ov2640_post_unset_alt(struct gspca_dev *gspca_dev);
+static int  ov2640_camera_settings(struct gspca_dev *gspca_dev);
+/*==========================================================================*/
+
+void ov2640_init_settings(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	sd->vcur.backlight  =  32;
+	sd->vcur.brightness =   0;
+	sd->vcur.sharpness  =   6;
+	sd->vcur.contrast   =   0;
+	sd->vcur.gamma      =  32;
+	sd->vcur.hue        =   0;
+	sd->vcur.saturation = 128;
+	sd->vcur.whitebal   =  64;
+	sd->vcur.mirror     =   0;
+	sd->vcur.flip       =   0;
+
+	sd->vmax.backlight  =  64;
+	sd->vmax.brightness = 255;
+	sd->vmax.sharpness  =  31;
+	sd->vmax.contrast   = 255;
+	sd->vmax.gamma      =  64;
+	sd->vmax.hue        = 254 + 2;
+	sd->vmax.saturation = 255;
+	sd->vmax.whitebal   = 128;
+	sd->vmax.mirror     = 1;
+	sd->vmax.flip       = 1;
+	sd->vmax.AC50Hz     = 0;
+
+	sd->dev_camera_settings = ov2640_camera_settings;
+	sd->dev_init_at_startup = ov2640_init_at_startup;
+	sd->dev_configure_alt   = ov2640_configure_alt;
+	sd->dev_init_pre_alt    = ov2640_init_pre_alt;
+	sd->dev_post_unset_alt  = ov2640_post_unset_alt;
+}
+
+/*==========================================================================*/
+
+static void common(struct gspca_dev *gspca_dev)
+{
+	fetch_validx(gspca_dev, tbl_common, ARRAY_SIZE(tbl_common));
+}
+
+static int ov2640_init_at_startup(struct gspca_dev *gspca_dev)
+{
+	fetch_validx(gspca_dev, tbl_init_at_startup,
+			ARRAY_SIZE(tbl_init_at_startup));
+
+	ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, dat_init1);
+
+	common(gspca_dev);
+
+	ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0006, 1, c61);
+
+	ctrl_out(gspca_dev, 0x40, 1, 0x00ef, 0x0006, 0, NULL);
+
+	ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, c51);
+
+	ctrl_out(gspca_dev, 0x40, 1, 0x0051, 0x0000, 0, NULL);
+/*	ctrl_out(gspca_dev, 0x40, 11, 0x0000, 0x0000, 0, NULL); */
+
+	return 0;
+}
+
+static int ov2640_init_pre_alt(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	sd->mirrorMask = 0;
+
+	sd->vold.backlight  = -1;
+	sd->vold.brightness = -1;
+	sd->vold.sharpness  = -1;
+	sd->vold.contrast   = -1;
+	sd->vold.saturation = -1;
+	sd->vold.gamma    = -1;
+	sd->vold.hue      = -1;
+	sd->vold.whitebal = -1;
+	sd->vold.mirror = -1;
+	sd->vold.flip   = -1;
+
+	ov2640_init_post_alt(gspca_dev);
+
+	return 0;
+}
+
+static int ov2640_init_post_alt(struct gspca_dev *gspca_dev)
+{
+	s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv;
+	s32 n; /* reserved for FETCH functions */
+
+	ctrl_out(gspca_dev, 0x40, 5, 0x0001, 0x0000, 0, NULL);
+
+	n = fetch_validx(gspca_dev, tbl_sensor_settings_common1,
+			ARRAY_SIZE(tbl_sensor_settings_common1));
+	ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, dat_post);
+	common(gspca_dev);
+	keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common1,
+				ARRAY_SIZE(tbl_sensor_settings_common1), n);
+
+	switch (reso) {
+	case IMAGE_640:
+		n = fetch_validx(gspca_dev, tbl_640, ARRAY_SIZE(tbl_640));
+		ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, dat_640);
+		break;
+
+	case IMAGE_800:
+		n = fetch_validx(gspca_dev, tbl_800, ARRAY_SIZE(tbl_800));
+		ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, dat_800);
+		break;
+
+	case IMAGE_1600:
+	case IMAGE_1280:
+		n = fetch_validx(gspca_dev, tbl_big1, ARRAY_SIZE(tbl_big1));
+
+		if (reso == IMAGE_1280) {
+			n = fetch_validx(gspca_dev, tbl_big2,
+					ARRAY_SIZE(tbl_big2));
+		} else {
+			ctrl_out(gspca_dev, 0x40, 1, 0x601d, 0x0086, 0, NULL);
+			ctrl_out(gspca_dev, 0x40, 1, 0x6001, 0x00d7, 0, NULL);
+			ctrl_out(gspca_dev, 0x40, 1, 0x6082, 0x00d3, 0, NULL);
+		}
+
+		n = fetch_validx(gspca_dev, tbl_big3, ARRAY_SIZE(tbl_big3));
+
+		if (reso == IMAGE_1280) {
+			ctrl_out(gspca_dev, 0x40, 1, 0x6001, 0x00ff, 0, NULL);
+			ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200,
+					12, dat_1280);
+		} else {
+			ctrl_out(gspca_dev, 0x40, 1, 0x6020, 0x008c, 0, NULL);
+			ctrl_out(gspca_dev, 0x40, 1, 0x6001, 0x00ff, 0, NULL);
+			ctrl_out(gspca_dev, 0x40, 1, 0x6076, 0x0018, 0, NULL);
+			ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200,
+					12, dat_1600);
+		}
+		break;
+	}
+
+	n = fetch_validx(gspca_dev, tbl_sensor_settings_common2,
+			ARRAY_SIZE(tbl_sensor_settings_common2));
+
+	ov2640_camera_settings(gspca_dev);
+
+	return 0;
+}
+
+static int ov2640_configure_alt(struct gspca_dev *gspca_dev)
+{
+	s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv;
+
+	switch (reso) {
+	case IMAGE_640:
+		gspca_dev->alt = 3 + 1;
+		break;
+
+	case IMAGE_800:
+	case IMAGE_1280:
+	case IMAGE_1600:
+		gspca_dev->alt = 1 + 1;
+		break;
+	}
+	return 0;
+}
+
+static int ov2640_camera_settings(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	s32 backlight = sd->vcur.backlight;
+	s32 bright = sd->vcur.brightness;
+	s32 sharp  = sd->vcur.sharpness;
+	s32 gam    = sd->vcur.gamma;
+	s32 cntr   = sd->vcur.contrast;
+	s32 sat    = sd->vcur.saturation;
+	s32 hue    = sd->vcur.hue;
+	s32 wbal   = sd->vcur.whitebal;
+	s32 mirror = (((sd->vcur.mirror > 0) ^ sd->mirrorMask) == 0);
+	s32 flip   = (((sd->vcur.flip   > 0) ^ sd->mirrorMask) == 0);
+
+	if (backlight != sd->vold.backlight) {
+		/* No sd->vold.backlight=backlight; (to be done again later) */
+		if (backlight < 0 || backlight > sd->vmax.backlight)
+			backlight = 0;
+
+		ctrl_out(gspca_dev, 0x40, 1, 0x6001                 , 0x00ff,
+				0, NULL);
+		ctrl_out(gspca_dev, 0x40, 1, 0x601e + backlight     , 0x0024,
+				0, NULL);
+		ctrl_out(gspca_dev, 0x40, 1, 0x601e + backlight - 10, 0x0025,
+				0, NULL);
+	}
+
+	if (bright != sd->vold.brightness) {
+		sd->vold.brightness = bright;
+		if (bright < 0 || bright > sd->vmax.brightness)
+			bright = 0;
+
+		ctrl_out(gspca_dev, 0x40, 1, 0x6000         , 0x00ff, 0, NULL);
+		ctrl_out(gspca_dev, 0x40, 1, 0x6009         , 0x007c, 0, NULL);
+		ctrl_out(gspca_dev, 0x40, 1, 0x6000 + bright, 0x007d, 0, NULL);
+	}
+
+	if (wbal != sd->vold.whitebal) {
+		sd->vold.whitebal = wbal;
+		if (wbal < 0 || wbal > sd->vmax.whitebal)
+			wbal = 0;
+
+		ctrl_out(gspca_dev, 0x40, 1, 0x6000       , 0x00ff, 0, NULL);
+		ctrl_out(gspca_dev, 0x40, 1, 0x6003       , 0x007c, 0, NULL);
+		ctrl_out(gspca_dev, 0x40, 1, 0x6000 + wbal, 0x007d, 0, NULL);
+	}
+
+	if (cntr != sd->vold.contrast) {
+		sd->vold.contrast = cntr;
+		if (cntr < 0 || cntr > sd->vmax.contrast)
+			cntr = 0;
+
+		ctrl_out(gspca_dev, 0x40, 1, 0x6000       , 0x00ff, 0, NULL);
+		ctrl_out(gspca_dev, 0x40, 1, 0x6007       , 0x007c, 0, NULL);
+		ctrl_out(gspca_dev, 0x40, 1, 0x6000 + cntr, 0x007d, 0, NULL);
+	}
+
+	if (sat != sd->vold.saturation) {
+		sd->vold.saturation = sat;
+		if (sat < 0 || sat > sd->vmax.saturation)
+			sat = 0;
+
+		ctrl_out(gspca_dev, 0x40, 1, 0x6000      , 0x00ff, 0, NULL);
+		ctrl_out(gspca_dev, 0x40, 1, 0x6001      , 0x007c, 0, NULL);
+		ctrl_out(gspca_dev, 0x40, 1, 0x6000 + sat, 0x007d, 0, NULL);
+	}
+
+	if (sharp != sd->vold.sharpness) {
+		sd->vold.sharpness = sharp;
+		if (sharp < 0 || sharp > sd->vmax.sharpness)
+			sharp = 0;
+
+		ctrl_out(gspca_dev, 0x40, 1, 0x6000        , 0x00ff, 0, NULL);
+		ctrl_out(gspca_dev, 0x40, 1, 0x6001        , 0x0092, 0, NULL);
+		ctrl_out(gspca_dev, 0x40, 1, 0x60c0 + sharp, 0x0093, 0, NULL);
+	}
+
+	if (hue != sd->vold.hue) {
+		sd->vold.hue = hue;
+		if (hue < 0 || hue > sd->vmax.hue)
+			hue = 0;
+
+		ctrl_out(gspca_dev, 0x40, 1, 0x6000     , 0x00ff, 0, NULL);
+		ctrl_out(gspca_dev, 0x40, 1, 0x6002     , 0x007c, 0, NULL);
+		ctrl_out(gspca_dev, 0x40, 1, 0x6000 + hue * (hue < 255), 0x007d,
+				0, NULL);
+		if (hue >= 255)
+			sd->swapRB = 1;
+		else
+			sd->swapRB = 0;
+	}
+
+	if (gam != sd->vold.gamma) {
+		sd->vold.gamma = gam;
+		if (gam < 0 || gam > sd->vmax.gamma)
+			gam = 0;
+
+		ctrl_out(gspca_dev, 0x40, 1, 0x6000      , 0x00ff, 0, NULL);
+		ctrl_out(gspca_dev, 0x40, 1, 0x6008      , 0x007c, 0, NULL);
+		ctrl_out(gspca_dev, 0x40, 1, 0x6000 + gam, 0x007d, 0, NULL);
+	}
+
+	if (mirror != sd->vold.mirror || flip != sd->vold.flip) {
+		sd->vold.mirror = mirror;
+		sd->vold.flip   = flip;
+
+		mirror = 0x80 * mirror;
+		ctrl_out(gspca_dev, 0x40, 1, 0x6001, 0x00ff, 0, NULL);
+		ctrl_out(gspca_dev, 0x40, 1, 0x6000, 0x8004, 0, NULL);
+		ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x8004, 1, c28);
+		ctrl_out(gspca_dev, 0x40, 1, 0x6028 + mirror, 0x0004, 0, NULL);
+
+		flip = 0x50 * flip + mirror;
+		ctrl_out(gspca_dev, 0x40, 1, 0x6001, 0x00ff, 0, NULL);
+		ctrl_out(gspca_dev, 0x40, 1, 0x6000, 0x8004, 0, NULL);
+		ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x8004, 1, ca8);
+		ctrl_out(gspca_dev, 0x40, 1, 0x6028 + flip, 0x0004, 0, NULL);
+
+		ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, c50);
+	}
+
+	if (backlight != sd->vold.backlight) {
+		sd->vold.backlight = backlight;
+
+		ctrl_out(gspca_dev, 0x40, 1, 0x6001                 , 0x00ff,
+				0, NULL);
+		ctrl_out(gspca_dev, 0x40, 1, 0x601e + backlight     , 0x0024,
+				0, NULL);
+		ctrl_out(gspca_dev, 0x40, 1, 0x601e + backlight - 10, 0x0025,
+				0, NULL);
+	}
+
+	return 0;
+}
+
+static void ov2640_post_unset_alt(struct gspca_dev *gspca_dev)
+{
+	ctrl_out(gspca_dev, 0x40, 5, 0x0000, 0x0000, 0, NULL);
+	msleep(20);
+	fetch_validx(gspca_dev, tbl_post_unset_alt,
+			ARRAY_SIZE(tbl_post_unset_alt));
+}
diff --git a/drivers/media/usb/gspca/gl860/gl860-ov9655.c b/drivers/media/usb/gspca/gl860/gl860-ov9655.c
new file mode 100644
index 0000000..5ae9619
--- /dev/null
+++ b/drivers/media/usb/gspca/gl860/gl860-ov9655.c
@@ -0,0 +1,336 @@
+/* Subdriver for the GL860 chip with the OV9655 sensor
+ * Author Olivier LORIN, from logs done by Simon (Sur3) and Almighurt
+ * on dsd's weblog
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+/* Sensor : OV9655 */
+
+#include "gl860.h"
+
+static struct validx tbl_init_at_startup[] = {
+	{0x0000, 0x0000}, {0x0010, 0x0010}, {0x0008, 0x00c0}, {0x0001, 0x00c1},
+	{0x0001, 0x00c2}, {0x0020, 0x0006}, {0x006a, 0x000d},
+
+	{0x0040, 0x0000},
+};
+
+static struct validx tbl_commmon[] = {
+	{0x0041, 0x0000}, {0x006a, 0x0007}, {0x0063, 0x0006}, {0x006a, 0x000d},
+	{0x0000, 0x00c0}, {0x0010, 0x0010}, {0x0001, 0x00c1}, {0x0041, 0x00c2},
+	{0x0004, 0x00d8}, {0x0012, 0x0004}, {0x0000, 0x0058}, {0x0040, 0x0000},
+	{0x00f3, 0x0006}, {0x0058, 0x0000}, {0x0048, 0x0000}, {0x0061, 0x0000},
+};
+
+static s32 tbl_length[] = {12, 56, 52, 54, 56, 42, 32, 12};
+
+static u8 *tbl_640[] = {
+	"\x00\x40\x07\x6a\x06\xf3\x0d\x6a" "\x10\x10\xc1\x01"
+	,
+	"\x12\x80\x00\x00\x01\x98\x02\x80" "\x03\x12\x04\x03\x0b\x57\x0e\x61"
+	"\x0f\x42\x11\x01\x12\x60\x13\x00" "\x14\x3a\x16\x24\x17\x14\x18\x00"
+	"\x19\x01\x1a\x3d\x1e\x04\x24\x3c" "\x25\x36\x26\x72\x27\x08\x28\x08"
+	"\x29\x15\x2a\x00\x2b\x00\x2c\x08"
+	,
+	"\x32\xff\x33\x00\x34\x3d\x35\x00" "\x36\xfa\x38\x72\x39\x57\x3a\x00"
+	"\x3b\x0c\x3d\x99\x3e\x0c\x3f\xc1" "\x40\xc0\x41\x00\x42\xc0\x43\x0a"
+	"\x44\xf0\x45\x46\x46\x62\x47\x2a" "\x48\x3c\x4a\xee\x4b\xe7\x4c\xe7"
+	"\x4d\xe7\x4e\xe7"
+	,
+	"\x4f\x98\x50\x98\x51\x00\x52\x28" "\x53\x70\x54\x98\x58\x1a\x59\x85"
+	"\x5a\xa9\x5b\x64\x5c\x84\x5d\x53" "\x5e\x0e\x5f\xf0\x60\xf0\x61\xf0"
+	"\x62\x00\x63\x00\x64\x02\x65\x20" "\x66\x00\x69\x0a\x6b\x5a\x6c\x04"
+	"\x6d\x55\x6e\x00\x6f\x9d"
+	,
+	"\x70\x15\x71\x78\x72\x00\x73\x00" "\x74\x3a\x75\x35\x76\x01\x77\x02"
+	"\x7a\x24\x7b\x04\x7c\x07\x7d\x10" "\x7e\x28\x7f\x36\x80\x44\x81\x52"
+	"\x82\x60\x83\x6c\x84\x78\x85\x8c" "\x86\x9e\x87\xbb\x88\xd2\x89\xe5"
+	"\x8a\x23\x8c\x8d\x90\x7c\x91\x7b"
+	,
+	"\x9d\x02\x9e\x02\x9f\x74\xa0\x73" "\xa1\x40\xa4\x50\xa5\x68\xa6\x70"
+	"\xa8\xc1\xa9\xef\xaa\x92\xab\x04" "\xac\x80\xad\x80\xae\x80\xaf\x80"
+	"\xb2\xf2\xb3\x20\xb4\x20\xb5\x00" "\xb6\xaf"
+	,
+	"\xbb\xae\xbc\x4f\xbd\x4e\xbe\x6a" "\xbf\x68\xc0\xaa\xc1\xc0\xc2\x01"
+	"\xc3\x4e\xc6\x85\xc7\x81\xc9\xe0" "\xca\xe8\xcb\xf0\xcc\xd8\xcd\x93"
+	,
+	"\xd0\x01\xd1\x08\xd2\xe0\xd3\x01" "\xd4\x10\xd5\x80"
+};
+
+static u8 *tbl_1280[] = {
+	"\x00\x40\x07\x6a\x06\xf3\x0d\x6a" "\x10\x10\xc1\x01"
+	,
+	"\x12\x80\x00\x00\x01\x98\x02\x80" "\x03\x12\x04\x01\x0b\x57\x0e\x61"
+	"\x0f\x42\x11\x00\x12\x00\x13\x00" "\x14\x3a\x16\x24\x17\x1b\x18\xbb"
+	"\x19\x01\x1a\x81\x1e\x04\x24\x3c" "\x25\x36\x26\x72\x27\x08\x28\x08"
+	"\x29\x15\x2a\x00\x2b\x00\x2c\x08"
+	,
+	"\x32\xa4\x33\x00\x34\x3d\x35\x00" "\x36\xf8\x38\x72\x39\x57\x3a\x00"
+	"\x3b\x0c\x3d\x99\x3e\x0c\x3f\xc2" "\x40\xc0\x41\x00\x42\xc0\x43\x0a"
+	"\x44\xf0\x45\x46\x46\x62\x47\x2a" "\x48\x3c\x4a\xec\x4b\xe8\x4c\xe8"
+	"\x4d\xe8\x4e\xe8"
+	,
+	"\x4f\x98\x50\x98\x51\x00\x52\x28" "\x53\x70\x54\x98\x58\x1a\x59\x85"
+	"\x5a\xa9\x5b\x64\x5c\x84\x5d\x53" "\x5e\x0e\x5f\xf0\x60\xf0\x61\xf0"
+	"\x62\x00\x63\x00\x64\x02\x65\x20" "\x66\x00\x69\x02\x6b\x5a\x6c\x04"
+	"\x6d\x55\x6e\x00\x6f\x9d"
+	,
+	"\x70\x08\x71\x78\x72\x00\x73\x01" "\x74\x3a\x75\x35\x76\x01\x77\x02"
+	"\x7a\x24\x7b\x04\x7c\x07\x7d\x10" "\x7e\x28\x7f\x36\x80\x44\x81\x52"
+	"\x82\x60\x83\x6c\x84\x78\x85\x8c" "\x86\x9e\x87\xbb\x88\xd2\x89\xe5"
+	"\x8a\x23\x8c\x0d\x90\x90\x91\x90"
+	,
+	"\x9d\x02\x9e\x02\x9f\x94\xa0\x94" "\xa1\x01\xa4\x50\xa5\x68\xa6\x70"
+	"\xa8\xc1\xa9\xef\xaa\x92\xab\x04" "\xac\x80\xad\x80\xae\x80\xaf\x80"
+	"\xb2\xf2\xb3\x20\xb4\x20\xb5\x00" "\xb6\xaf"
+	,
+	"\xbb\xae\xbc\x38\xbd\x39\xbe\x01" "\xbf\x01\xc0\xe2\xc1\xc0\xc2\x01"
+	"\xc3\x4e\xc6\x85\xc7\x81\xc9\xe0" "\xca\xe8\xcb\xf0\xcc\xd8\xcd\x93"
+	,
+	"\xd0\x21\xd1\x18\xd2\xe0\xd3\x01" "\xd4\x28\xd5\x00"
+};
+
+static u8 c04[] = {0x04};
+static u8 dat_post1[] = "\x04\x00\x10\x20\xa1\x00\x00\x02";
+static u8 dat_post2[] = "\x10\x10\xc1\x02";
+static u8 dat_post3[] = "\x04\x00\x10\x7c\xa1\x00\x00\x04";
+static u8 dat_post4[] = "\x10\x02\xc1\x06";
+static u8 dat_post5[] = "\x04\x00\x10\x7b\xa1\x00\x00\x08";
+static u8 dat_post6[] = "\x10\x10\xc1\x05";
+static u8 dat_post7[] = "\x04\x00\x10\x7c\xa1\x00\x00\x08";
+static u8 dat_post8[] = "\x04\x00\x10\x7c\xa1\x00\x00\x09";
+
+static struct validx tbl_init_post_alt[] = {
+	{0x6032, 0x00ff}, {0x6032, 0x00ff}, {0x6032, 0x00ff}, {0x603c, 0x00ff},
+	{0x6003, 0x00ff}, {0x6032, 0x00ff}, {0x6032, 0x00ff}, {0x6001, 0x00ff},
+	{0x6000, 0x801e},
+	{0xffff, 0xffff},
+	{0x6004, 0x001e}, {0x6000, 0x801e},
+	{0xffff, 0xffff},
+	{0x6004, 0x001e}, {0x6012, 0x0003}, {0x6000, 0x801e},
+	{0xffff, 0xffff},
+	{0x6004, 0x001e}, {0x6000, 0x801e},
+	{0xffff, 0xffff},
+	{0x6004, 0x001e}, {0x6012, 0x0003},
+	{0xffff, 0xffff},
+	{0x6000, 0x801e},
+	{0xffff, 0xffff},
+	{0x6004, 0x001e}, {0x6000, 0x801e},
+	{0xffff, 0xffff},
+	{0x6004, 0x001e}, {0x6012, 0x0003}, {0x6000, 0x801e},
+	{0xffff, 0xffff},
+	{0x6004, 0x001e}, {0x6000, 0x801e},
+	{0xffff, 0xffff},
+	{0x6004, 0x001e}, {0x6012, 0x0003},
+	{0xffff, 0xffff},
+	{0x6000, 0x801e},
+	{0xffff, 0xffff},
+	{0x6004, 0x001e}, {0x6000, 0x801e},
+	{0xffff, 0xffff},
+	{0x6004, 0x001e}, {0x6012, 0x0003},
+};
+
+static int  ov9655_init_at_startup(struct gspca_dev *gspca_dev);
+static int  ov9655_configure_alt(struct gspca_dev *gspca_dev);
+static int  ov9655_init_pre_alt(struct gspca_dev *gspca_dev);
+static int  ov9655_init_post_alt(struct gspca_dev *gspca_dev);
+static void ov9655_post_unset_alt(struct gspca_dev *gspca_dev);
+static int  ov9655_camera_settings(struct gspca_dev *gspca_dev);
+/*==========================================================================*/
+
+void ov9655_init_settings(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	sd->vcur.backlight  =   0;
+	sd->vcur.brightness = 128;
+	sd->vcur.sharpness  =   0;
+	sd->vcur.contrast   =   0;
+	sd->vcur.gamma      =   0;
+	sd->vcur.hue        =   0;
+	sd->vcur.saturation =   0;
+	sd->vcur.whitebal   =   0;
+
+	sd->vmax.backlight  =   0;
+	sd->vmax.brightness = 255;
+	sd->vmax.sharpness  =   0;
+	sd->vmax.contrast   =   0;
+	sd->vmax.gamma      =   0;
+	sd->vmax.hue        =   0 + 1;
+	sd->vmax.saturation =   0;
+	sd->vmax.whitebal   =   0;
+	sd->vmax.mirror     = 0;
+	sd->vmax.flip       = 0;
+	sd->vmax.AC50Hz     = 0;
+
+	sd->dev_camera_settings = ov9655_camera_settings;
+	sd->dev_init_at_startup = ov9655_init_at_startup;
+	sd->dev_configure_alt   = ov9655_configure_alt;
+	sd->dev_init_pre_alt    = ov9655_init_pre_alt;
+	sd->dev_post_unset_alt  = ov9655_post_unset_alt;
+}
+
+/*==========================================================================*/
+
+static int ov9655_init_at_startup(struct gspca_dev *gspca_dev)
+{
+	fetch_validx(gspca_dev, tbl_init_at_startup,
+			ARRAY_SIZE(tbl_init_at_startup));
+	fetch_validx(gspca_dev, tbl_commmon, ARRAY_SIZE(tbl_commmon));
+/*	ctrl_out(gspca_dev, 0x40, 11, 0x0000, 0x0000, 0, NULL);*/
+
+	return 0;
+}
+
+static int ov9655_init_pre_alt(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	sd->vold.brightness = -1;
+	sd->vold.hue = -1;
+
+	fetch_validx(gspca_dev, tbl_commmon, ARRAY_SIZE(tbl_commmon));
+
+	ov9655_init_post_alt(gspca_dev);
+
+	return 0;
+}
+
+static int ov9655_init_post_alt(struct gspca_dev *gspca_dev)
+{
+	s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv;
+	s32 n; /* reserved for FETCH functions */
+	s32 i;
+	u8 **tbl;
+
+	ctrl_out(gspca_dev, 0x40, 5, 0x0001, 0x0000, 0, NULL);
+
+	tbl = (reso == IMAGE_640) ? tbl_640 : tbl_1280;
+
+	ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200,
+			tbl_length[0], tbl[0]);
+	for (i = 1; i < 7; i++)
+		ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200,
+				tbl_length[i], tbl[i]);
+	ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200,
+			tbl_length[7], tbl[7]);
+
+	n = fetch_validx(gspca_dev, tbl_init_post_alt,
+			ARRAY_SIZE(tbl_init_post_alt));
+
+	ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04);
+	keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
+					ARRAY_SIZE(tbl_init_post_alt), n);
+	ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04);
+	keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
+					ARRAY_SIZE(tbl_init_post_alt), n);
+	ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04);
+	keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
+					ARRAY_SIZE(tbl_init_post_alt), n);
+	ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04);
+	keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
+					ARRAY_SIZE(tbl_init_post_alt), n);
+	ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post1);
+	keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
+					ARRAY_SIZE(tbl_init_post_alt), n);
+
+	ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04);
+	keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
+					ARRAY_SIZE(tbl_init_post_alt), n);
+	ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04);
+	keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
+					ARRAY_SIZE(tbl_init_post_alt), n);
+	ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04);
+	keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
+					ARRAY_SIZE(tbl_init_post_alt), n);
+	ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04);
+	keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
+					ARRAY_SIZE(tbl_init_post_alt), n);
+	ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post1);
+	keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
+					ARRAY_SIZE(tbl_init_post_alt), n);
+
+	ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04);
+	keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
+					ARRAY_SIZE(tbl_init_post_alt), n);
+	ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04);
+	keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
+					ARRAY_SIZE(tbl_init_post_alt), n);
+
+	ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post1);
+
+	ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post2);
+	ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post3);
+
+	ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post4);
+	ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post5);
+
+	ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post6);
+	ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post7);
+
+	ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post8);
+
+	ov9655_camera_settings(gspca_dev);
+
+	return 0;
+}
+
+static int ov9655_configure_alt(struct gspca_dev *gspca_dev)
+{
+	s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv;
+
+	switch (reso) {
+	case IMAGE_640:
+		gspca_dev->alt = 1 + 1;
+		break;
+
+	default:
+		gspca_dev->alt = 1 + 1;
+		break;
+	}
+	return 0;
+}
+
+static int ov9655_camera_settings(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	u8 dat_bright[] = "\x04\x00\x10\x7c\xa1\x00\x00\x70";
+
+	s32 bright = sd->vcur.brightness;
+	s32 hue    = sd->vcur.hue;
+
+	if (bright != sd->vold.brightness) {
+		sd->vold.brightness = bright;
+		if (bright < 0 || bright > sd->vmax.brightness)
+			bright = 0;
+
+		dat_bright[3] = bright;
+		ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_bright);
+	}
+
+	if (hue != sd->vold.hue) {
+		sd->vold.hue = hue;
+		sd->swapRB = (hue != 0);
+	}
+
+	return 0;
+}
+
+static void ov9655_post_unset_alt(struct gspca_dev *gspca_dev)
+{
+	ctrl_out(gspca_dev, 0x40, 5, 0x0000, 0x0000, 0, NULL);
+	ctrl_out(gspca_dev, 0x40, 1, 0x0061, 0x0000, 0, NULL);
+}
diff --git a/drivers/media/usb/gspca/gl860/gl860.c b/drivers/media/usb/gspca/gl860/gl860.c
new file mode 100644
index 0000000..cea8d7f
--- /dev/null
+++ b/drivers/media/usb/gspca/gl860/gl860.c
@@ -0,0 +1,741 @@
+/* GSPCA subdrivers for Genesys Logic webcams with the GL860 chip
+ * Subdriver core
+ *
+ * 2009/09/24 Olivier Lorin <o.lorin@laposte.net>
+ * GSPCA by Jean-Francois Moine <http://moinejf.free.fr>
+ * Thanks BUGabundo and Malmostoso for your amazing help!
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "gspca.h"
+#include "gl860.h"
+
+MODULE_AUTHOR("Olivier Lorin <o.lorin@laposte.net>");
+MODULE_DESCRIPTION("Genesys Logic USB PC Camera Driver");
+MODULE_LICENSE("GPL");
+
+/*======================== static function declarations ====================*/
+
+static void (*dev_init_settings)(struct gspca_dev *gspca_dev);
+
+static int  sd_config(struct gspca_dev *gspca_dev,
+			const struct usb_device_id *id);
+static int  sd_init(struct gspca_dev *gspca_dev);
+static int  sd_isoc_init(struct gspca_dev *gspca_dev);
+static int  sd_start(struct gspca_dev *gspca_dev);
+static void sd_stop0(struct gspca_dev *gspca_dev);
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+			u8 *data, int len);
+static void sd_callback(struct gspca_dev *gspca_dev);
+
+static int gl860_guess_sensor(struct gspca_dev *gspca_dev,
+				u16 vendor_id, u16 product_id);
+
+/*============================ driver options ==============================*/
+
+static s32 AC50Hz = 0xff;
+module_param(AC50Hz, int, 0644);
+MODULE_PARM_DESC(AC50Hz, " Does AC power frequency is 50Hz? (0/1)");
+
+static char sensor[7];
+module_param_string(sensor, sensor, sizeof(sensor), 0644);
+MODULE_PARM_DESC(sensor,
+		" Driver sensor ('MI1320'/'MI2020'/'OV9655'/'OV2640')");
+
+/*============================ webcam controls =============================*/
+
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct gspca_dev *gspca_dev =
+		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	switch (ctrl->id) {
+	case V4L2_CID_BRIGHTNESS:
+		sd->vcur.brightness = ctrl->val;
+		break;
+	case V4L2_CID_CONTRAST:
+		sd->vcur.contrast = ctrl->val;
+		break;
+	case V4L2_CID_SATURATION:
+		sd->vcur.saturation = ctrl->val;
+		break;
+	case V4L2_CID_HUE:
+		sd->vcur.hue = ctrl->val;
+		break;
+	case V4L2_CID_GAMMA:
+		sd->vcur.gamma = ctrl->val;
+		break;
+	case V4L2_CID_HFLIP:
+		sd->vcur.mirror = ctrl->val;
+		break;
+	case V4L2_CID_VFLIP:
+		sd->vcur.flip = ctrl->val;
+		break;
+	case V4L2_CID_POWER_LINE_FREQUENCY:
+		sd->vcur.AC50Hz = ctrl->val;
+		break;
+	case V4L2_CID_WHITE_BALANCE_TEMPERATURE:
+		sd->vcur.whitebal = ctrl->val;
+		break;
+	case V4L2_CID_SHARPNESS:
+		sd->vcur.sharpness = ctrl->val;
+		break;
+	case V4L2_CID_BACKLIGHT_COMPENSATION:
+		sd->vcur.backlight = ctrl->val;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (gspca_dev->streaming)
+		sd->waitSet = 1;
+
+	return 0;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+	.s_ctrl = sd_s_ctrl,
+};
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+	gspca_dev->vdev.ctrl_handler = hdl;
+	v4l2_ctrl_handler_init(hdl, 11);
+
+	if (sd->vmax.brightness)
+		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_BRIGHTNESS,
+				  0, sd->vmax.brightness, 1,
+				  sd->vcur.brightness);
+
+	if (sd->vmax.contrast)
+		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_CONTRAST,
+				  0, sd->vmax.contrast, 1,
+				  sd->vcur.contrast);
+
+	if (sd->vmax.saturation)
+		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_SATURATION,
+				  0, sd->vmax.saturation, 1,
+				  sd->vcur.saturation);
+
+	if (sd->vmax.hue)
+		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_HUE,
+				  0, sd->vmax.hue, 1, sd->vcur.hue);
+
+	if (sd->vmax.gamma)
+		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_GAMMA,
+				  0, sd->vmax.gamma, 1, sd->vcur.gamma);
+
+	if (sd->vmax.mirror)
+		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_HFLIP,
+				  0, sd->vmax.mirror, 1, sd->vcur.mirror);
+
+	if (sd->vmax.flip)
+		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_VFLIP,
+				  0, sd->vmax.flip, 1, sd->vcur.flip);
+
+	if (sd->vmax.AC50Hz)
+		v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
+				  V4L2_CID_POWER_LINE_FREQUENCY,
+				  sd->vmax.AC50Hz, 0, sd->vcur.AC50Hz);
+
+	if (sd->vmax.whitebal)
+		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+				  V4L2_CID_WHITE_BALANCE_TEMPERATURE,
+				  0, sd->vmax.whitebal, 1, sd->vcur.whitebal);
+
+	if (sd->vmax.sharpness)
+		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_SHARPNESS,
+				  0, sd->vmax.sharpness, 1,
+				  sd->vcur.sharpness);
+
+	if (sd->vmax.backlight)
+		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+				  V4L2_CID_BACKLIGHT_COMPENSATION,
+				  0, sd->vmax.backlight, 1,
+				  sd->vcur.backlight);
+
+	if (hdl->error) {
+		pr_err("Could not initialize controls\n");
+		return hdl->error;
+	}
+
+	return 0;
+}
+
+/*==================== sud-driver structure initialisation =================*/
+
+static const struct sd_desc sd_desc_mi1320 = {
+	.name        = MODULE_NAME,
+	.config      = sd_config,
+	.init        = sd_init,
+	.init_controls = sd_init_controls,
+	.isoc_init   = sd_isoc_init,
+	.start       = sd_start,
+	.stop0       = sd_stop0,
+	.pkt_scan    = sd_pkt_scan,
+	.dq_callback = sd_callback,
+};
+
+static const struct sd_desc sd_desc_mi2020 = {
+	.name        = MODULE_NAME,
+	.config      = sd_config,
+	.init        = sd_init,
+	.init_controls = sd_init_controls,
+	.isoc_init   = sd_isoc_init,
+	.start       = sd_start,
+	.stop0       = sd_stop0,
+	.pkt_scan    = sd_pkt_scan,
+	.dq_callback = sd_callback,
+};
+
+static const struct sd_desc sd_desc_ov2640 = {
+	.name        = MODULE_NAME,
+	.config      = sd_config,
+	.init        = sd_init,
+	.init_controls = sd_init_controls,
+	.isoc_init   = sd_isoc_init,
+	.start       = sd_start,
+	.stop0       = sd_stop0,
+	.pkt_scan    = sd_pkt_scan,
+	.dq_callback = sd_callback,
+};
+
+static const struct sd_desc sd_desc_ov9655 = {
+	.name        = MODULE_NAME,
+	.config      = sd_config,
+	.init        = sd_init,
+	.init_controls = sd_init_controls,
+	.isoc_init   = sd_isoc_init,
+	.start       = sd_start,
+	.stop0       = sd_stop0,
+	.pkt_scan    = sd_pkt_scan,
+	.dq_callback = sd_callback,
+};
+
+/*=========================== sub-driver image sizes =======================*/
+
+static struct v4l2_pix_format mi2020_mode[] = {
+	{ 640,  480, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
+		.bytesperline = 640,
+		.sizeimage = 640 * 480,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 0
+	},
+	{ 800,  598, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
+		.bytesperline = 800,
+		.sizeimage = 800 * 598,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 1
+	},
+	{1280, 1024, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
+		.bytesperline = 1280,
+		.sizeimage = 1280 * 1024,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 2
+	},
+	{1600, 1198, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
+		.bytesperline = 1600,
+		.sizeimage = 1600 * 1198,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 3
+	},
+};
+
+static struct v4l2_pix_format ov2640_mode[] = {
+	{ 640,  480, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
+		.bytesperline = 640,
+		.sizeimage = 640 * 480,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 0
+	},
+	{ 800,  600, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
+		.bytesperline = 800,
+		.sizeimage = 800 * 600,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 1
+	},
+	{1280,  960, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
+		.bytesperline = 1280,
+		.sizeimage = 1280 * 960,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 2
+	},
+	{1600, 1200, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
+		.bytesperline = 1600,
+		.sizeimage = 1600 * 1200,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 3
+	},
+};
+
+static struct v4l2_pix_format mi1320_mode[] = {
+	{ 640,  480, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
+		.bytesperline = 640,
+		.sizeimage = 640 * 480,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 0
+	},
+	{ 800,  600, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
+		.bytesperline = 800,
+		.sizeimage = 800 * 600,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 1
+	},
+	{1280,  960, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
+		.bytesperline = 1280,
+		.sizeimage = 1280 * 960,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 2
+	},
+};
+
+static struct v4l2_pix_format ov9655_mode[] = {
+	{ 640,  480, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
+		.bytesperline = 640,
+		.sizeimage = 640 * 480,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 0
+	},
+	{1280,  960, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
+		.bytesperline = 1280,
+		.sizeimage = 1280 * 960,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 1
+	},
+};
+
+/*========================= sud-driver functions ===========================*/
+
+/* 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_id, product_id;
+
+	/* Get USB VendorID and ProductID */
+	vendor_id  = id->idVendor;
+	product_id = id->idProduct;
+
+	sd->nbRightUp = 1;
+	sd->nbIm = -1;
+
+	sd->sensor = 0xff;
+	if (strcmp(sensor, "MI1320") == 0)
+		sd->sensor = ID_MI1320;
+	else if (strcmp(sensor, "OV2640") == 0)
+		sd->sensor = ID_OV2640;
+	else if (strcmp(sensor, "OV9655") == 0)
+		sd->sensor = ID_OV9655;
+	else if (strcmp(sensor, "MI2020") == 0)
+		sd->sensor = ID_MI2020;
+
+	/* Get sensor and set the suitable init/start/../stop functions */
+	if (gl860_guess_sensor(gspca_dev, vendor_id, product_id) == -1)
+		return -1;
+
+	cam = &gspca_dev->cam;
+
+	switch (sd->sensor) {
+	case ID_MI1320:
+		gspca_dev->sd_desc = &sd_desc_mi1320;
+		cam->cam_mode = mi1320_mode;
+		cam->nmodes = ARRAY_SIZE(mi1320_mode);
+		dev_init_settings   = mi1320_init_settings;
+		break;
+
+	case ID_MI2020:
+		gspca_dev->sd_desc = &sd_desc_mi2020;
+		cam->cam_mode = mi2020_mode;
+		cam->nmodes = ARRAY_SIZE(mi2020_mode);
+		dev_init_settings   = mi2020_init_settings;
+		break;
+
+	case ID_OV2640:
+		gspca_dev->sd_desc = &sd_desc_ov2640;
+		cam->cam_mode = ov2640_mode;
+		cam->nmodes = ARRAY_SIZE(ov2640_mode);
+		dev_init_settings   = ov2640_init_settings;
+		break;
+
+	case ID_OV9655:
+		gspca_dev->sd_desc = &sd_desc_ov9655;
+		cam->cam_mode = ov9655_mode;
+		cam->nmodes = ARRAY_SIZE(ov9655_mode);
+		dev_init_settings   = ov9655_init_settings;
+		break;
+	}
+
+	dev_init_settings(gspca_dev);
+	if (AC50Hz != 0xff)
+		((struct sd *) gspca_dev)->vcur.AC50Hz = AC50Hz;
+
+	return 0;
+}
+
+/* This function is called at probe time after sd_config */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	return sd->dev_init_at_startup(gspca_dev);
+}
+
+/* This function is called before to choose the alt setting */
+static int sd_isoc_init(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	return sd->dev_configure_alt(gspca_dev);
+}
+
+/* This function is called to start the webcam */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	return sd->dev_init_pre_alt(gspca_dev);
+}
+
+/* This function is called to stop the webcam */
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (!sd->gspca_dev.present)
+		return;
+
+	return sd->dev_post_unset_alt(gspca_dev);
+}
+
+/* This function is called when an image is being received */
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+			u8 *data, int len)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	static s32 nSkipped;
+
+	s32 mode = (s32) gspca_dev->curr_mode;
+	s32 nToSkip =
+		sd->swapRB * (gspca_dev->cam.cam_mode[mode].bytesperline + 1);
+
+	/* Test only against 0202h, so endianness does not matter */
+	switch (*(s16 *) data) {
+	case 0x0202:		/* End of frame, start a new one */
+		gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+		nSkipped = 0;
+		if (sd->nbIm >= 0 && sd->nbIm < 10)
+			sd->nbIm++;
+		gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0);
+		break;
+
+	default:
+		data += 2;
+		len  -= 2;
+		if (nSkipped + len <= nToSkip)
+			nSkipped += len;
+		else {
+			if (nSkipped < nToSkip && nSkipped + len > nToSkip) {
+				data += nToSkip - nSkipped;
+				len  -= nToSkip - nSkipped;
+				nSkipped = nToSkip + 1;
+			}
+			gspca_frame_add(gspca_dev,
+				INTER_PACKET, data, len);
+		}
+		break;
+	}
+}
+
+/* This function is called when an image has been read */
+/* This function is used to monitor webcam orientation */
+static void sd_callback(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (!_OV9655_) {
+		u8 state;
+		u8 upsideDown;
+
+		/* Probe sensor orientation */
+		ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, (void *)&state);
+
+		/* C8/40 means upside-down (looking backwards) */
+		/* D8/50 means right-up (looking onwards) */
+		upsideDown = (state == 0xc8 || state == 0x40);
+
+		if (upsideDown && sd->nbRightUp > -4) {
+			if (sd->nbRightUp > 0)
+				sd->nbRightUp = 0;
+			if (sd->nbRightUp == -3) {
+				sd->mirrorMask = 1;
+				sd->waitSet = 1;
+			}
+			sd->nbRightUp--;
+		}
+		if (!upsideDown && sd->nbRightUp < 4) {
+			if (sd->nbRightUp  < 0)
+				sd->nbRightUp = 0;
+			if (sd->nbRightUp == 3) {
+				sd->mirrorMask = 0;
+				sd->waitSet = 1;
+			}
+			sd->nbRightUp++;
+		}
+	}
+
+	if (sd->waitSet)
+		sd->dev_camera_settings(gspca_dev);
+}
+
+/*=================== USB driver structure initialisation ==================*/
+
+static const struct usb_device_id device_table[] = {
+	{USB_DEVICE(0x05e3, 0x0503)},
+	{USB_DEVICE(0x05e3, 0xf191)},
+	{}
+};
+
+MODULE_DEVICE_TABLE(usb, device_table);
+
+static int sd_probe(struct usb_interface *intf,
+				const struct usb_device_id *id)
+{
+	return gspca_dev_probe(intf, id,
+			&sd_desc_mi1320, sizeof(struct sd), THIS_MODULE);
+}
+
+static void sd_disconnect(struct usb_interface *intf)
+{
+	gspca_disconnect(intf);
+}
+
+static struct usb_driver sd_driver = {
+	.name       = MODULE_NAME,
+	.id_table   = device_table,
+	.probe      = sd_probe,
+	.disconnect = sd_disconnect,
+#ifdef CONFIG_PM
+	.suspend    = gspca_suspend,
+	.resume     = gspca_resume,
+	.reset_resume = gspca_resume,
+#endif
+};
+
+/*====================== Init and Exit module functions ====================*/
+
+module_usb_driver(sd_driver);
+
+/*==========================================================================*/
+
+int gl860_RTx(struct gspca_dev *gspca_dev,
+		unsigned char pref, u32 req, u16 val, u16 index,
+		s32 len, void *pdata)
+{
+	struct usb_device *udev = gspca_dev->dev;
+	s32 r = 0;
+
+	if (pref == 0x40) { /* Send */
+		if (len > 0) {
+			memcpy(gspca_dev->usb_buf, pdata, len);
+			r = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+					req, pref, val, index,
+					gspca_dev->usb_buf,
+					len, 400 + 200 * (len > 1));
+		} else {
+			r = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+					req, pref, val, index, NULL, len, 400);
+		}
+	} else { /* Receive */
+		if (len > 0) {
+			r = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+					req, pref, val, index,
+					gspca_dev->usb_buf,
+					len, 400 + 200 * (len > 1));
+			memcpy(pdata, gspca_dev->usb_buf, len);
+		} else {
+			r = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+					req, pref, val, index, NULL, len, 400);
+		}
+	}
+
+	if (r < 0)
+		pr_err("ctrl transfer failed %4d [p%02x r%d v%04x i%04x len%d]\n",
+		       r, pref, req, val, index, len);
+	else if (len > 1 && r < len)
+		PERR("short ctrl transfer %d/%d", r, len);
+
+	msleep(1);
+
+	return r;
+}
+
+int fetch_validx(struct gspca_dev *gspca_dev, struct validx *tbl, int len)
+{
+	int n;
+
+	for (n = 0; n < len; n++) {
+		if (tbl[n].idx != 0xffff)
+			ctrl_out(gspca_dev, 0x40, 1, tbl[n].val,
+					tbl[n].idx, 0, NULL);
+		else if (tbl[n].val == 0xffff)
+			break;
+		else
+			msleep(tbl[n].val);
+	}
+	return n;
+}
+
+int keep_on_fetching_validx(struct gspca_dev *gspca_dev, struct validx *tbl,
+				int len, int n)
+{
+	while (++n < len) {
+		if (tbl[n].idx != 0xffff)
+			ctrl_out(gspca_dev, 0x40, 1, tbl[n].val, tbl[n].idx,
+					0, NULL);
+		else if (tbl[n].val == 0xffff)
+			break;
+		else
+			msleep(tbl[n].val);
+	}
+	return n;
+}
+
+void fetch_idxdata(struct gspca_dev *gspca_dev, struct idxdata *tbl, int len)
+{
+	int n;
+
+	for (n = 0; n < len; n++) {
+		if (memcmp(tbl[n].data, "\xff\xff\xff", 3) != 0)
+			ctrl_out(gspca_dev, 0x40, 3, 0x7a00, tbl[n].idx,
+					3, tbl[n].data);
+		else
+			msleep(tbl[n].idx);
+	}
+}
+
+static int gl860_guess_sensor(struct gspca_dev *gspca_dev,
+				u16 vendor_id, u16 product_id)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u8 probe, nb26, nb96, nOV, ntry;
+
+	if (product_id == 0xf191)
+		sd->sensor = ID_MI1320;
+
+	if (sd->sensor == 0xff) {
+		ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0004, 1, &probe);
+		ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0004, 1, &probe);
+
+		ctrl_out(gspca_dev, 0x40, 1, 0x0000, 0x0000, 0, NULL);
+		msleep(3);
+		ctrl_out(gspca_dev, 0x40, 1, 0x0010, 0x0010, 0, NULL);
+		msleep(3);
+		ctrl_out(gspca_dev, 0x40, 1, 0x0008, 0x00c0, 0, NULL);
+		msleep(3);
+		ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x00c1, 0, NULL);
+		msleep(3);
+		ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x00c2, 0, NULL);
+		msleep(3);
+		ctrl_out(gspca_dev, 0x40, 1, 0x0020, 0x0006, 0, NULL);
+		msleep(3);
+		ctrl_out(gspca_dev, 0x40, 1, 0x006a, 0x000d, 0, NULL);
+		msleep(56);
+
+		PDEBUG(D_PROBE, "probing for sensor MI2020 or OVXXXX");
+		nOV = 0;
+		for (ntry = 0; ntry < 4; ntry++) {
+			ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000, 0, NULL);
+			msleep(3);
+			ctrl_out(gspca_dev, 0x40, 1, 0x0063, 0x0006, 0, NULL);
+			msleep(3);
+			ctrl_out(gspca_dev, 0x40, 1, 0x7a00, 0x8030, 0, NULL);
+			msleep(10);
+			ctrl_in(gspca_dev, 0xc0, 2, 0x7a00, 0x8030, 1, &probe);
+			PDEBUG(D_PROBE, "probe=0x%02x", probe);
+			if (probe == 0xff)
+				nOV++;
+		}
+
+		if (nOV) {
+			PDEBUG(D_PROBE, "0xff -> OVXXXX");
+			PDEBUG(D_PROBE, "probing for sensor OV2640 or OV9655");
+
+			nb26 = nb96 = 0;
+			for (ntry = 0; ntry < 4; ntry++) {
+				ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000,
+						0, NULL);
+				msleep(3);
+				ctrl_out(gspca_dev, 0x40, 1, 0x6000, 0x800a,
+						0, NULL);
+				msleep(10);
+
+				/* Wait for 26(OV2640) or 96(OV9655) */
+				ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x800a,
+						1, &probe);
+
+				if (probe == 0x26 || probe == 0x40) {
+					PDEBUG(D_PROBE,
+						"probe=0x%02x -> OV2640",
+						probe);
+					sd->sensor = ID_OV2640;
+					nb26 += 4;
+					break;
+				}
+				if (probe == 0x96 || probe == 0x55) {
+					PDEBUG(D_PROBE,
+						"probe=0x%02x -> OV9655",
+						probe);
+					sd->sensor = ID_OV9655;
+					nb96 += 4;
+					break;
+				}
+				PDEBUG(D_PROBE, "probe=0x%02x", probe);
+				if (probe == 0x00)
+					nb26++;
+				if (probe == 0xff)
+					nb96++;
+				msleep(3);
+			}
+			if (nb26 < 4 && nb96 < 4)
+				return -1;
+		} else {
+			PDEBUG(D_PROBE, "Not any 0xff -> MI2020");
+			sd->sensor = ID_MI2020;
+		}
+	}
+
+	if (_MI1320_) {
+		PDEBUG(D_PROBE, "05e3:f191 sensor MI1320 (1.3M)");
+	} else if (_MI2020_) {
+		PDEBUG(D_PROBE, "05e3:0503 sensor MI2020 (2.0M)");
+	} else if (_OV9655_) {
+		PDEBUG(D_PROBE, "05e3:0503 sensor OV9655 (1.3M)");
+	} else if (_OV2640_) {
+		PDEBUG(D_PROBE, "05e3:0503 sensor OV2640 (2.0M)");
+	} else {
+		PDEBUG(D_PROBE, "***** Unknown sensor *****");
+		return -1;
+	}
+
+	return 0;
+}
diff --git a/drivers/media/usb/gspca/gl860/gl860.h b/drivers/media/usb/gspca/gl860/gl860.h
new file mode 100644
index 0000000..0330a02
--- /dev/null
+++ b/drivers/media/usb/gspca/gl860/gl860.h
@@ -0,0 +1,105 @@
+/* GSPCA subdrivers for Genesys Logic webcams with the GL860 chip
+ * Subdriver declarations
+ *
+ * 2009/10/14 Olivier LORIN <o.lorin@laposte.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
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef GL860_DEV_H
+#define GL860_DEV_H
+
+#include "gspca.h"
+
+#define MODULE_NAME "gspca_gl860"
+#define DRIVER_VERSION "0.9d10"
+
+#define ctrl_in  gl860_RTx
+#define ctrl_out gl860_RTx
+
+#define ID_MI1320   1
+#define ID_OV2640   2
+#define ID_OV9655   4
+#define ID_MI2020   8
+
+#define _MI1320_  (((struct sd *) gspca_dev)->sensor == ID_MI1320)
+#define _MI2020_  (((struct sd *) gspca_dev)->sensor == ID_MI2020)
+#define _OV2640_  (((struct sd *) gspca_dev)->sensor == ID_OV2640)
+#define _OV9655_  (((struct sd *) gspca_dev)->sensor == ID_OV9655)
+
+#define IMAGE_640   0
+#define IMAGE_800   1
+#define IMAGE_1280  2
+#define IMAGE_1600  3
+
+struct sd_gl860 {
+	u16 backlight;
+	u16 brightness;
+	u16 sharpness;
+	u16 contrast;
+	u16 gamma;
+	u16 hue;
+	u16 saturation;
+	u16 whitebal;
+	u8  mirror;
+	u8  flip;
+	u8  AC50Hz;
+};
+
+/* Specific webcam descriptor */
+struct sd {
+	struct gspca_dev gspca_dev;	/* !! must be the first item */
+
+	struct sd_gl860 vcur;
+	struct sd_gl860 vold;
+	struct sd_gl860 vmax;
+
+	int  (*dev_configure_alt)  (struct gspca_dev *);
+	int  (*dev_init_at_startup)(struct gspca_dev *);
+	int  (*dev_init_pre_alt)   (struct gspca_dev *);
+	void (*dev_post_unset_alt) (struct gspca_dev *);
+	int  (*dev_camera_settings)(struct gspca_dev *);
+
+	u8   swapRB;
+	u8   mirrorMask;
+	u8   sensor;
+	s32  nbIm;
+	s32  nbRightUp;
+	u8   waitSet;
+};
+
+struct validx {
+	u16 val;
+	u16 idx;
+};
+
+struct idxdata {
+	u8 idx;
+	u8 data[3];
+};
+
+int fetch_validx(struct gspca_dev *gspca_dev, struct validx *tbl, int len);
+int keep_on_fetching_validx(struct gspca_dev *gspca_dev, struct validx *tbl,
+				int len, int n);
+void fetch_idxdata(struct gspca_dev *gspca_dev, struct idxdata *tbl, int len);
+
+int gl860_RTx(struct gspca_dev *gspca_dev,
+			unsigned char pref, u32 req, u16 val, u16 index,
+			s32 len, void *pdata);
+
+void mi1320_init_settings(struct gspca_dev *);
+void ov2640_init_settings(struct gspca_dev *);
+void ov9655_init_settings(struct gspca_dev *);
+void mi2020_init_settings(struct gspca_dev *);
+
+#endif
diff --git a/drivers/media/usb/gspca/gspca.c b/drivers/media/usb/gspca/gspca.c
new file mode 100644
index 0000000..af5cd82
--- /dev/null
+++ b/drivers/media/usb/gspca/gspca.c
@@ -0,0 +1,2277 @@
+/*
+ * Main USB camera driver
+ *
+ * Copyright (C) 2008-2011 Jean-François Moine <http://moinejf.free.fr>
+ *
+ * Camera button input handling by Márton Németh
+ * Copyright (C) 2009-2010 Márton Németh <nm127@freemail.hu>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define GSPCA_VERSION	"2.14.0"
+
+#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/ktime.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-event.h>
+
+#include "gspca.h"
+
+#if IS_ENABLED(CONFIG_INPUT)
+#include <linux/input.h>
+#include <linux/usb/input.h>
+#endif
+
+/* global values */
+#define DEF_NURBS 3		/* default number of URBs */
+#if DEF_NURBS > MAX_NURBS
+#error "DEF_NURBS too big"
+#endif
+
+MODULE_AUTHOR("Jean-François Moine <http://moinejf.free.fr>");
+MODULE_DESCRIPTION("GSPCA USB Camera Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(GSPCA_VERSION);
+
+int gspca_debug;
+EXPORT_SYMBOL(gspca_debug);
+
+static void PDEBUG_MODE(struct gspca_dev *gspca_dev, int debug, char *txt,
+			__u32 pixfmt, int w, int h)
+{
+	if ((pixfmt >> 24) >= '0' && (pixfmt >> 24) <= 'z') {
+		PDEBUG(debug, "%s %c%c%c%c %dx%d",
+			txt,
+			pixfmt & 0xff,
+			(pixfmt >> 8) & 0xff,
+			(pixfmt >> 16) & 0xff,
+			pixfmt >> 24,
+			w, h);
+	} else {
+		PDEBUG(debug, "%s 0x%08x %dx%d",
+			txt,
+			pixfmt,
+			w, h);
+	}
+}
+
+/* specific memory types - !! should be 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 const struct vm_operations_struct gspca_vm_ops = {
+	.open		= gspca_vm_open,
+	.close		= gspca_vm_close,
+};
+
+/*
+ * Input and interrupt endpoint handling functions
+ */
+#if IS_ENABLED(CONFIG_INPUT)
+static void int_irq(struct urb *urb)
+{
+	struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context;
+	int ret;
+
+	ret = urb->status;
+	switch (ret) {
+	case 0:
+		if (gspca_dev->sd_desc->int_pkt_scan(gspca_dev,
+		    urb->transfer_buffer, urb->actual_length) < 0) {
+			PERR("Unknown packet received");
+		}
+		break;
+
+	case -ENOENT:
+	case -ECONNRESET:
+	case -ENODEV:
+	case -ESHUTDOWN:
+		/* Stop is requested either by software or hardware is gone,
+		 * keep the ret value non-zero and don't resubmit later.
+		 */
+		break;
+
+	default:
+		PERR("URB error %i, resubmitting", urb->status);
+		urb->status = 0;
+		ret = 0;
+	}
+
+	if (ret == 0) {
+		ret = usb_submit_urb(urb, GFP_ATOMIC);
+		if (ret < 0)
+			pr_err("Resubmit URB failed with error %i\n", ret);
+	}
+}
+
+static int gspca_input_connect(struct gspca_dev *dev)
+{
+	struct input_dev *input_dev;
+	int err = 0;
+
+	dev->input_dev = NULL;
+	if (dev->sd_desc->int_pkt_scan || dev->sd_desc->other_input)  {
+		input_dev = input_allocate_device();
+		if (!input_dev)
+			return -ENOMEM;
+
+		usb_make_path(dev->dev, dev->phys, sizeof(dev->phys));
+		strlcat(dev->phys, "/input0", sizeof(dev->phys));
+
+		input_dev->name = dev->sd_desc->name;
+		input_dev->phys = dev->phys;
+
+		usb_to_input_id(dev->dev, &input_dev->id);
+
+		input_dev->evbit[0] = BIT_MASK(EV_KEY);
+		input_dev->keybit[BIT_WORD(KEY_CAMERA)] = BIT_MASK(KEY_CAMERA);
+		input_dev->dev.parent = &dev->dev->dev;
+
+		err = input_register_device(input_dev);
+		if (err) {
+			pr_err("Input device registration failed with error %i\n",
+			       err);
+			input_dev->dev.parent = NULL;
+			input_free_device(input_dev);
+		} else {
+			dev->input_dev = input_dev;
+		}
+	}
+
+	return err;
+}
+
+static int alloc_and_submit_int_urb(struct gspca_dev *gspca_dev,
+			  struct usb_endpoint_descriptor *ep)
+{
+	unsigned int buffer_len;
+	int interval;
+	struct urb *urb;
+	struct usb_device *dev;
+	void *buffer = NULL;
+	int ret = -EINVAL;
+
+	buffer_len = le16_to_cpu(ep->wMaxPacketSize);
+	interval = ep->bInterval;
+	PDEBUG(D_CONF, "found int in endpoint: 0x%x, "
+		"buffer_len=%u, interval=%u",
+		ep->bEndpointAddress, buffer_len, interval);
+
+	dev = gspca_dev->dev;
+
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!urb) {
+		ret = -ENOMEM;
+		goto error;
+	}
+
+	buffer = usb_alloc_coherent(dev, buffer_len,
+				GFP_KERNEL, &urb->transfer_dma);
+	if (!buffer) {
+		ret = -ENOMEM;
+		goto error_buffer;
+	}
+	usb_fill_int_urb(urb, dev,
+		usb_rcvintpipe(dev, ep->bEndpointAddress),
+		buffer, buffer_len,
+		int_irq, (void *)gspca_dev, interval);
+	urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+	ret = usb_submit_urb(urb, GFP_KERNEL);
+	if (ret < 0) {
+		PERR("submit int URB failed with error %i", ret);
+		goto error_submit;
+	}
+	gspca_dev->int_urb = urb;
+	return ret;
+
+error_submit:
+	usb_free_coherent(dev,
+			  urb->transfer_buffer_length,
+			  urb->transfer_buffer,
+			  urb->transfer_dma);
+error_buffer:
+	usb_free_urb(urb);
+error:
+	return ret;
+}
+
+static void gspca_input_create_urb(struct gspca_dev *gspca_dev)
+{
+	struct usb_interface *intf;
+	struct usb_host_interface *intf_desc;
+	struct usb_endpoint_descriptor *ep;
+	int i;
+
+	if (gspca_dev->sd_desc->int_pkt_scan)  {
+		intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface);
+		intf_desc = intf->cur_altsetting;
+		for (i = 0; i < intf_desc->desc.bNumEndpoints; i++) {
+			ep = &intf_desc->endpoint[i].desc;
+			if (usb_endpoint_dir_in(ep) &&
+			    usb_endpoint_xfer_int(ep)) {
+
+				alloc_and_submit_int_urb(gspca_dev, ep);
+				break;
+			}
+		}
+	}
+}
+
+static void gspca_input_destroy_urb(struct gspca_dev *gspca_dev)
+{
+	struct urb *urb;
+
+	urb = gspca_dev->int_urb;
+	if (urb) {
+		gspca_dev->int_urb = NULL;
+		usb_kill_urb(urb);
+		usb_free_coherent(gspca_dev->dev,
+				  urb->transfer_buffer_length,
+				  urb->transfer_buffer,
+				  urb->transfer_dma);
+		usb_free_urb(urb);
+	}
+}
+#else
+static inline void gspca_input_destroy_urb(struct gspca_dev *gspca_dev)
+{
+}
+
+static inline void gspca_input_create_urb(struct gspca_dev *gspca_dev)
+{
+}
+
+static inline int gspca_input_connect(struct gspca_dev *dev)
+{
+	return 0;
+}
+#endif
+
+/*
+ * fill a video frame from an URB and resubmit
+ */
+static void fill_frame(struct gspca_dev *gspca_dev,
+			struct urb *urb)
+{
+	u8 *data;		/* address of data in the iso message */
+	int i, len, st;
+	cam_pkt_op pkt_scan;
+
+	if (urb->status != 0) {
+		if (urb->status == -ESHUTDOWN)
+			return;		/* disconnection */
+#ifdef CONFIG_PM
+		if (gspca_dev->frozen)
+			return;
+#endif
+		PERR("urb status: %d", urb->status);
+		urb->status = 0;
+		goto resubmit;
+	}
+	pkt_scan = gspca_dev->sd_desc->pkt_scan;
+	for (i = 0; i < urb->number_of_packets; i++) {
+		len = urb->iso_frame_desc[i].actual_length;
+
+		/* check the packet status and length */
+		st = urb->iso_frame_desc[i].status;
+		if (st) {
+			pr_err("ISOC data error: [%d] len=%d, status=%d\n",
+			       i, len, st);
+			gspca_dev->last_packet_type = DISCARD_PACKET;
+			continue;
+		}
+		if (len == 0) {
+			if (gspca_dev->empty_packet == 0)
+				gspca_dev->empty_packet = 1;
+			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, data, len);
+	}
+
+resubmit:
+	/* resubmit the URB */
+	st = usb_submit_urb(urb, GFP_ATOMIC);
+	if (st < 0)
+		pr_err("usb_submit_urb() ret %d\n", 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);
+}
+
+/*
+ * bulk message interrupt from the USB device
+ */
+static void bulk_irq(struct urb *urb)
+{
+	struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context;
+	int st;
+
+	PDEBUG(D_PACK, "bulk irq");
+	if (!gspca_dev->streaming)
+		return;
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ESHUTDOWN:
+		return;		/* disconnection */
+	default:
+#ifdef CONFIG_PM
+		if (gspca_dev->frozen)
+			return;
+#endif
+		PERR("urb status: %d", urb->status);
+		urb->status = 0;
+		goto resubmit;
+	}
+
+	PDEBUG(D_PACK, "packet l:%d", urb->actual_length);
+	gspca_dev->sd_desc->pkt_scan(gspca_dev,
+				urb->transfer_buffer,
+				urb->actual_length);
+
+resubmit:
+	/* resubmit the URB */
+	if (gspca_dev->cam.bulk_nurbs != 0) {
+		st = usb_submit_urb(urb, GFP_ATOMIC);
+		if (st < 0)
+			pr_err("usb_submit_urb() ret %d\n", st);
+	}
+}
+
+/*
+ * 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.
+ */
+void gspca_frame_add(struct gspca_dev *gspca_dev,
+			enum gspca_packet_type packet_type,
+			const u8 *data,
+			int len)
+{
+	struct gspca_frame *frame;
+	int i, j;
+
+	PDEBUG(D_PACK, "add t:%d l:%d",	packet_type, len);
+
+	if (packet_type == FIRST_PACKET) {
+		i = atomic_read(&gspca_dev->fr_i);
+
+		/* if there are no queued buffer, discard the whole frame */
+		if (i == atomic_read(&gspca_dev->fr_q)) {
+			gspca_dev->last_packet_type = DISCARD_PACKET;
+			gspca_dev->sequence++;
+			return;
+		}
+		j = gspca_dev->fr_queue[i];
+		frame = &gspca_dev->frame[j];
+		v4l2_get_timestamp(&frame->v4l2_buf.timestamp);
+		frame->v4l2_buf.sequence = gspca_dev->sequence++;
+		gspca_dev->image = frame->data;
+		gspca_dev->image_len = 0;
+	} else {
+		switch (gspca_dev->last_packet_type) {
+		case DISCARD_PACKET:
+			if (packet_type == LAST_PACKET) {
+				gspca_dev->last_packet_type = packet_type;
+				gspca_dev->image = NULL;
+				gspca_dev->image_len = 0;
+			}
+			return;
+		case LAST_PACKET:
+			return;
+		}
+	}
+
+	/* append the packet to the frame buffer */
+	if (len > 0) {
+		if (gspca_dev->image_len + len > gspca_dev->frsz) {
+			PERR("frame overflow %d > %d",
+				gspca_dev->image_len + len,
+				gspca_dev->frsz);
+			packet_type = DISCARD_PACKET;
+		} else {
+/* !! image is NULL only when last pkt is LAST or DISCARD
+			if (gspca_dev->image == NULL) {
+				pr_err("gspca_frame_add() image == NULL\n");
+				return;
+			}
+ */
+			memcpy(gspca_dev->image + gspca_dev->image_len,
+				data, len);
+			gspca_dev->image_len += len;
+		}
+	}
+	gspca_dev->last_packet_type = packet_type;
+
+	/* if last packet, invalidate packet concatenation until
+	 * next first packet, wake up the application and advance
+	 * in the queue */
+	if (packet_type == LAST_PACKET) {
+		i = atomic_read(&gspca_dev->fr_i);
+		j = gspca_dev->fr_queue[i];
+		frame = &gspca_dev->frame[j];
+		frame->v4l2_buf.bytesused = gspca_dev->image_len;
+		frame->v4l2_buf.flags = (frame->v4l2_buf.flags
+					 | V4L2_BUF_FLAG_DONE)
+					& ~V4L2_BUF_FLAG_QUEUED;
+		i = (i + 1) % GSPCA_MAX_FRAMES;
+		atomic_set(&gspca_dev->fr_i, i);
+		wake_up_interruptible(&gspca_dev->wq);	/* event = new frame */
+		PDEBUG(D_FRAM, "frame complete len:%d",
+			frame->v4l2_buf.bytesused);
+		gspca_dev->image = NULL;
+		gspca_dev->image_len = 0;
+	}
+}
+EXPORT_SYMBOL(gspca_frame_add);
+
+static int frame_alloc(struct gspca_dev *gspca_dev, struct file *file,
+			enum v4l2_memory memory, unsigned int count)
+{
+	struct gspca_frame *frame;
+	unsigned int frsz;
+	int i;
+
+	frsz = gspca_dev->pixfmt.sizeimage;
+	PDEBUG(D_STREAM, "frame alloc frsz: %d", frsz);
+	frsz = PAGE_ALIGN(frsz);
+	if (count >= GSPCA_MAX_FRAMES)
+		count = GSPCA_MAX_FRAMES - 1;
+	gspca_dev->frbuf = vmalloc_32(frsz * count);
+	if (!gspca_dev->frbuf) {
+		pr_err("frame alloc failed\n");
+		return -ENOMEM;
+	}
+	gspca_dev->capt_file = file;
+	gspca_dev->memory = memory;
+	gspca_dev->frsz = frsz;
+	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 = memory;
+		frame->v4l2_buf.sequence = 0;
+		frame->data = gspca_dev->frbuf + i * frsz;
+		frame->v4l2_buf.m.offset = i * frsz;
+	}
+	atomic_set(&gspca_dev->fr_q, 0);
+	atomic_set(&gspca_dev->fr_i, 0);
+	gspca_dev->fr_o = 0;
+	return 0;
+}
+
+static void frame_free(struct gspca_dev *gspca_dev)
+{
+	int i;
+
+	PDEBUG(D_STREAM, "frame free");
+	if (gspca_dev->frbuf != NULL) {
+		vfree(gspca_dev->frbuf);
+		gspca_dev->frbuf = NULL;
+		for (i = 0; i < gspca_dev->nframes; i++)
+			gspca_dev->frame[i].data = NULL;
+	}
+	gspca_dev->nframes = 0;
+	gspca_dev->frsz = 0;
+	gspca_dev->capt_file = NULL;
+	gspca_dev->memory = GSPCA_MEMORY_NO;
+}
+
+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);
+		usb_free_coherent(gspca_dev->dev,
+				  urb->transfer_buffer_length,
+				  urb->transfer_buffer,
+				  urb->transfer_dma);
+		usb_free_urb(urb);
+	}
+}
+
+static int gspca_set_alt0(struct gspca_dev *gspca_dev)
+{
+	int ret;
+
+	if (gspca_dev->alt == 0)
+		return 0;
+	ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, 0);
+	if (ret < 0)
+		pr_err("set alt 0 err %d\n", ret);
+	return ret;
+}
+
+/* Note: both the queue and the usb locks should be held when calling this */
+static void gspca_stream_off(struct gspca_dev *gspca_dev)
+{
+	gspca_dev->streaming = 0;
+	gspca_dev->usb_err = 0;
+	if (gspca_dev->sd_desc->stopN)
+		gspca_dev->sd_desc->stopN(gspca_dev);
+	destroy_urbs(gspca_dev);
+	gspca_input_destroy_urb(gspca_dev);
+	gspca_set_alt0(gspca_dev);
+	gspca_input_create_urb(gspca_dev);
+	if (gspca_dev->sd_desc->stop0)
+		gspca_dev->sd_desc->stop0(gspca_dev);
+	PDEBUG(D_STREAM, "stream off OK");
+}
+
+/*
+ * look for an input transfer endpoint in an alternate setting.
+ *
+ * If xfer_ep is invalid, return the first valid ep found, otherwise
+ * look for exactly the ep with address equal to xfer_ep.
+ */
+static struct usb_host_endpoint *alt_xfer(struct usb_host_interface *alt,
+					  int xfer, int xfer_ep)
+{
+	struct usb_host_endpoint *ep;
+	int i, attr;
+
+	for (i = 0; i < alt->desc.bNumEndpoints; i++) {
+		ep = &alt->endpoint[i];
+		attr = ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+		if (attr == xfer
+		    && ep->desc.wMaxPacketSize != 0
+		    && usb_endpoint_dir_in(&ep->desc)
+		    && (xfer_ep < 0 || ep->desc.bEndpointAddress == xfer_ep))
+			return ep;
+	}
+	return NULL;
+}
+
+/* compute the minimum bandwidth for the current transfer */
+static u32 which_bandwidth(struct gspca_dev *gspca_dev)
+{
+	u32 bandwidth;
+
+	/* get the (max) image size */
+	bandwidth = gspca_dev->pixfmt.sizeimage;
+
+	/* if the image is compressed, estimate its mean size */
+	if (!gspca_dev->cam.needs_full_bandwidth &&
+	    bandwidth < gspca_dev->pixfmt.width *
+				gspca_dev->pixfmt.height)
+		bandwidth = bandwidth * 3 / 8;	/* 0.375 */
+
+	/* estimate the frame rate */
+	if (gspca_dev->sd_desc->get_streamparm) {
+		struct v4l2_streamparm parm;
+
+		gspca_dev->sd_desc->get_streamparm(gspca_dev, &parm);
+		bandwidth *= parm.parm.capture.timeperframe.denominator;
+		bandwidth /= parm.parm.capture.timeperframe.numerator;
+	} else {
+
+		/* don't hope more than 15 fps with USB 1.1 and
+		 * image resolution >= 640x480 */
+		if (gspca_dev->pixfmt.width >= 640
+		 && gspca_dev->dev->speed == USB_SPEED_FULL)
+			bandwidth *= 15;		/* 15 fps */
+		else
+			bandwidth *= 30;		/* 30 fps */
+	}
+
+	PDEBUG(D_STREAM, "min bandwidth: %d", bandwidth);
+	return bandwidth;
+}
+
+/* endpoint table */
+#define MAX_ALT 16
+struct ep_tb_s {
+	u32 alt;
+	u32 bandwidth;
+};
+
+/*
+ * build the table of the endpoints
+ * and compute the minimum bandwidth for the image transfer
+ */
+static int build_isoc_ep_tb(struct gspca_dev *gspca_dev,
+			struct usb_interface *intf,
+			struct ep_tb_s *ep_tb)
+{
+	struct usb_host_endpoint *ep;
+	int i, j, nbalt, psize, found;
+	u32 bandwidth, last_bw;
+
+	nbalt = intf->num_altsetting;
+	if (nbalt > MAX_ALT)
+		nbalt = MAX_ALT;	/* fixme: should warn */
+
+	/* build the endpoint table */
+	i = 0;
+	last_bw = 0;
+	for (;;) {
+		ep_tb->bandwidth = 2000 * 2000 * 120;
+		found = 0;
+		for (j = 0; j < nbalt; j++) {
+			ep = alt_xfer(&intf->altsetting[j],
+				      USB_ENDPOINT_XFER_ISOC,
+				      gspca_dev->xfer_ep);
+			if (ep == NULL)
+				continue;
+			if (ep->desc.bInterval == 0) {
+				pr_err("alt %d iso endp with 0 interval\n", j);
+				continue;
+			}
+			psize = le16_to_cpu(ep->desc.wMaxPacketSize);
+			psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3));
+			bandwidth = psize * 1000;
+			if (gspca_dev->dev->speed == USB_SPEED_HIGH
+			 || gspca_dev->dev->speed == USB_SPEED_SUPER)
+				bandwidth *= 8;
+			bandwidth /= 1 << (ep->desc.bInterval - 1);
+			if (bandwidth <= last_bw)
+				continue;
+			if (bandwidth < ep_tb->bandwidth) {
+				ep_tb->bandwidth = bandwidth;
+				ep_tb->alt = j;
+				found = 1;
+			}
+		}
+		if (!found)
+			break;
+		PDEBUG(D_STREAM, "alt %d bandwidth %d",
+				ep_tb->alt, ep_tb->bandwidth);
+		last_bw = ep_tb->bandwidth;
+		i++;
+		ep_tb++;
+	}
+
+	/*
+	 * If the camera:
+	 * has a usb audio class interface (a built in usb mic); and
+	 * is a usb 1 full speed device; and
+	 * uses the max full speed iso bandwidth; and
+	 * and has more than 1 alt setting
+	 * then skip the highest alt setting to spare bandwidth for the mic
+	 */
+	if (gspca_dev->audio &&
+			gspca_dev->dev->speed == USB_SPEED_FULL &&
+			last_bw >= 1000000 &&
+			i > 1) {
+		PDEBUG(D_STREAM, "dev has usb audio, skipping highest alt");
+		i--;
+		ep_tb--;
+	}
+
+	/* get the requested bandwidth and start at the highest atlsetting */
+	bandwidth = which_bandwidth(gspca_dev);
+	ep_tb--;
+	while (i > 1) {
+		ep_tb--;
+		if (ep_tb->bandwidth < bandwidth)
+			break;
+		i--;
+	}
+	return i;
+}
+
+/*
+ * create the URBs for image transfer
+ */
+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);
+
+	if (!gspca_dev->cam.bulk) {		/* isoc */
+
+		/* See paragraph 5.9 / table 5-11 of the usb 2.0 spec. */
+		if (gspca_dev->pkt_size == 0)
+			psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3));
+		else
+			psize = gspca_dev->pkt_size;
+		npkt = gspca_dev->cam.npkt;
+		if (npkt == 0)
+			npkt = 32;		/* default value */
+		bsize = psize * npkt;
+		PDEBUG(D_STREAM,
+			"isoc %d pkts size %d = bsize:%d",
+			npkt, psize, bsize);
+		nurbs = DEF_NURBS;
+	} else {				/* bulk */
+		npkt = 0;
+		bsize = gspca_dev->cam.bulk_size;
+		if (bsize == 0)
+			bsize = psize;
+		PDEBUG(D_STREAM, "bulk bsize:%d", bsize);
+		if (gspca_dev->cam.bulk_nurbs != 0)
+			nurbs = gspca_dev->cam.bulk_nurbs;
+		else
+			nurbs = 1;
+	}
+
+	for (n = 0; n < nurbs; n++) {
+		urb = usb_alloc_urb(npkt, GFP_KERNEL);
+		if (!urb) {
+			pr_err("usb_alloc_urb failed\n");
+			return -ENOMEM;
+		}
+		gspca_dev->urb[n] = urb;
+		urb->transfer_buffer = usb_alloc_coherent(gspca_dev->dev,
+						bsize,
+						GFP_KERNEL,
+						&urb->transfer_dma);
+
+		if (urb->transfer_buffer == NULL) {
+			pr_err("usb_alloc_coherent failed\n");
+			return -ENOMEM;
+		}
+		urb->dev = gspca_dev->dev;
+		urb->context = gspca_dev;
+		urb->transfer_buffer_length = bsize;
+		if (npkt != 0) {		/* ISOC */
+			urb->pipe = usb_rcvisocpipe(gspca_dev->dev,
+						    ep->desc.bEndpointAddress);
+			urb->transfer_flags = URB_ISO_ASAP
+					| URB_NO_TRANSFER_DMA_MAP;
+			urb->interval = 1 << (ep->desc.bInterval - 1);
+			urb->complete = isoc_irq;
+			urb->number_of_packets = npkt;
+			for (i = 0; i < npkt; i++) {
+				urb->iso_frame_desc[i].length = psize;
+				urb->iso_frame_desc[i].offset = psize * i;
+			}
+		} else {		/* bulk */
+			urb->pipe = usb_rcvbulkpipe(gspca_dev->dev,
+						ep->desc.bEndpointAddress);
+			urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
+			urb->complete = bulk_irq;
+		}
+	}
+	return 0;
+}
+
+/*
+ * start the USB transfer
+ */
+static int gspca_init_transfer(struct gspca_dev *gspca_dev)
+{
+	struct usb_interface *intf;
+	struct usb_host_endpoint *ep;
+	struct urb *urb;
+	struct ep_tb_s ep_tb[MAX_ALT];
+	int n, ret, xfer, alt, alt_idx;
+
+	/* reset the streaming variables */
+	gspca_dev->image = NULL;
+	gspca_dev->image_len = 0;
+	gspca_dev->last_packet_type = DISCARD_PACKET;
+	gspca_dev->sequence = 0;
+
+	gspca_dev->usb_err = 0;
+
+	/* do the specific subdriver stuff before endpoint selection */
+	intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface);
+	gspca_dev->alt = gspca_dev->cam.bulk ? intf->num_altsetting : 0;
+	if (gspca_dev->sd_desc->isoc_init) {
+		ret = gspca_dev->sd_desc->isoc_init(gspca_dev);
+		if (ret < 0)
+			return ret;
+	}
+	xfer = gspca_dev->cam.bulk ? USB_ENDPOINT_XFER_BULK
+				   : USB_ENDPOINT_XFER_ISOC;
+
+	/* if bulk or the subdriver forced an altsetting, get the endpoint */
+	if (gspca_dev->alt != 0) {
+		gspca_dev->alt--;	/* (previous version compatibility) */
+		ep = alt_xfer(&intf->altsetting[gspca_dev->alt], xfer,
+			      gspca_dev->xfer_ep);
+		if (ep == NULL) {
+			pr_err("bad altsetting %d\n", gspca_dev->alt);
+			return -EIO;
+		}
+		ep_tb[0].alt = gspca_dev->alt;
+		alt_idx = 1;
+	} else {
+		/* else, compute the minimum bandwidth
+		 * and build the endpoint table */
+		alt_idx = build_isoc_ep_tb(gspca_dev, intf, ep_tb);
+		if (alt_idx <= 0) {
+			pr_err("no transfer endpoint found\n");
+			return -EIO;
+		}
+	}
+
+	/* set the highest alternate setting and
+	 * loop until urb submit succeeds */
+	gspca_input_destroy_urb(gspca_dev);
+
+	gspca_dev->alt = ep_tb[--alt_idx].alt;
+	alt = -1;
+	for (;;) {
+		if (alt != gspca_dev->alt) {
+			alt = gspca_dev->alt;
+			if (intf->num_altsetting > 1) {
+				ret = usb_set_interface(gspca_dev->dev,
+							gspca_dev->iface,
+							alt);
+				if (ret < 0) {
+					if (ret == -ENOSPC)
+						goto retry; /*fixme: ugly*/
+					pr_err("set alt %d err %d\n", alt, ret);
+					goto out;
+				}
+			}
+		}
+		if (!gspca_dev->cam.no_urb_create) {
+			PDEBUG(D_STREAM, "init transfer alt %d", alt);
+			ret = create_urbs(gspca_dev,
+				alt_xfer(&intf->altsetting[alt], xfer,
+					 gspca_dev->xfer_ep));
+			if (ret < 0) {
+				destroy_urbs(gspca_dev);
+				goto out;
+			}
+		}
+
+		/* clear the bulk endpoint */
+		if (gspca_dev->cam.bulk)
+			usb_clear_halt(gspca_dev->dev,
+					gspca_dev->urb[0]->pipe);
+
+		/* start the cam */
+		ret = gspca_dev->sd_desc->start(gspca_dev);
+		if (ret < 0) {
+			destroy_urbs(gspca_dev);
+			goto out;
+		}
+		gspca_dev->streaming = 1;
+		v4l2_ctrl_handler_setup(gspca_dev->vdev.ctrl_handler);
+
+		/* some bulk transfers are started by the subdriver */
+		if (gspca_dev->cam.bulk && gspca_dev->cam.bulk_nurbs == 0)
+			break;
+
+		/* submit the URBs */
+		for (n = 0; n < MAX_NURBS; n++) {
+			urb = gspca_dev->urb[n];
+			if (urb == NULL)
+				break;
+			ret = usb_submit_urb(urb, GFP_KERNEL);
+			if (ret < 0)
+				break;
+		}
+		if (ret >= 0)
+			break;			/* transfer is started */
+
+		/* something when wrong
+		 * stop the webcam and free the transfer resources */
+		gspca_stream_off(gspca_dev);
+		if (ret != -ENOSPC) {
+			pr_err("usb_submit_urb alt %d err %d\n",
+			       gspca_dev->alt, ret);
+			goto out;
+		}
+
+		/* the bandwidth is not wide enough
+		 * negotiate or try a lower alternate setting */
+retry:
+		PERR("alt %d - bandwidth not wide enough, trying again", alt);
+		msleep(20);	/* wait for kill complete */
+		if (gspca_dev->sd_desc->isoc_nego) {
+			ret = gspca_dev->sd_desc->isoc_nego(gspca_dev);
+			if (ret < 0)
+				goto out;
+		} else {
+			if (alt_idx <= 0) {
+				pr_err("no transfer endpoint found\n");
+				ret = -EIO;
+				goto out;
+			}
+			gspca_dev->alt = ep_tb[--alt_idx].alt;
+		}
+	}
+out:
+	gspca_input_create_urb(gspca_dev);
+	return ret;
+}
+
+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->pixfmt = gspca_dev->cam.cam_mode[i];
+
+	/* does nothing if ctrl_handler == NULL */
+	v4l2_ctrl_handler_setup(gspca_dev->vdev.ctrl_handler);
+}
+
+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;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int vidioc_g_chip_info(struct file *file, void *priv,
+				struct v4l2_dbg_chip_info *chip)
+{
+	struct gspca_dev *gspca_dev = video_drvdata(file);
+
+	gspca_dev->usb_err = 0;
+	if (gspca_dev->sd_desc->get_chip_info)
+		return gspca_dev->sd_desc->get_chip_info(gspca_dev, chip);
+	return chip->match.addr ? -EINVAL : 0;
+}
+
+static int vidioc_g_register(struct file *file, void *priv,
+		struct v4l2_dbg_register *reg)
+{
+	struct gspca_dev *gspca_dev = video_drvdata(file);
+
+	gspca_dev->usb_err = 0;
+	return gspca_dev->sd_desc->get_register(gspca_dev, reg);
+}
+
+static int vidioc_s_register(struct file *file, void *priv,
+		const struct v4l2_dbg_register *reg)
+{
+	struct gspca_dev *gspca_dev = video_drvdata(file);
+
+	gspca_dev->usb_err = 0;
+	return gspca_dev->sd_desc->set_register(gspca_dev, reg);
+}
+#endif
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void  *priv,
+				struct v4l2_fmtdesc *fmtdesc)
+{
+	struct gspca_dev *gspca_dev = video_drvdata(file);
+	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 >= ARRAY_SIZE(fmt_tb))
+				return -EINVAL;
+		}
+	}
+	if (i < 0)
+		return -EINVAL;		/* no more format */
+
+	fmtdesc->pixelformat = fmt_tb[index];
+	if (gspca_dev->cam.cam_mode[i].sizeimage <
+			gspca_dev->cam.cam_mode[i].width *
+				gspca_dev->cam.cam_mode[i].height)
+		fmtdesc->flags = V4L2_FMT_FLAG_COMPRESSED;
+	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 = video_drvdata(file);
+
+	fmt->fmt.pix = gspca_dev->pixfmt;
+	/* some drivers use priv internally, zero it before giving it back to
+	   the core */
+	fmt->fmt.pix.priv = 0;
+	return 0;
+}
+
+static int try_fmt_vid_cap(struct gspca_dev *gspca_dev,
+			struct v4l2_format *fmt)
+{
+	int w, h, mode, mode2;
+
+	w = fmt->fmt.pix.width;
+	h = fmt->fmt.pix.height;
+
+	PDEBUG_MODE(gspca_dev, D_CONF, "try fmt cap",
+		    fmt->fmt.pix.pixelformat, w, h);
+
+	/* 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;
+	}
+	fmt->fmt.pix = gspca_dev->cam.cam_mode[mode];
+	if (gspca_dev->sd_desc->try_fmt) {
+		/* pass original resolution to subdriver try_fmt */
+		fmt->fmt.pix.width = w;
+		fmt->fmt.pix.height = h;
+		gspca_dev->sd_desc->try_fmt(gspca_dev, fmt);
+	}
+	/* some drivers use priv internally, zero it before giving it back to
+	   the core */
+	fmt->fmt.pix.priv = 0;
+	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 = video_drvdata(file);
+	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 = video_drvdata(file);
+	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 (gspca_dev->streaming) {
+		ret = -EBUSY;
+		goto out;
+	}
+	gspca_dev->curr_mode = ret;
+	if (gspca_dev->sd_desc->try_fmt)
+		/* subdriver try_fmt can modify format parameters */
+		gspca_dev->pixfmt = fmt->fmt.pix;
+	else
+		gspca_dev->pixfmt = gspca_dev->cam.cam_mode[ret];
+
+	ret = 0;
+out:
+	mutex_unlock(&gspca_dev->queue_lock);
+	return ret;
+}
+
+static int vidioc_enum_framesizes(struct file *file, void *priv,
+				  struct v4l2_frmsizeenum *fsize)
+{
+	struct gspca_dev *gspca_dev = video_drvdata(file);
+	int i;
+	__u32 index = 0;
+
+	if (gspca_dev->sd_desc->enum_framesizes)
+		return gspca_dev->sd_desc->enum_framesizes(gspca_dev, fsize);
+
+	for (i = 0; i < gspca_dev->cam.nmodes; i++) {
+		if (fsize->pixel_format !=
+				gspca_dev->cam.cam_mode[i].pixelformat)
+			continue;
+
+		if (fsize->index == index) {
+			fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+			fsize->discrete.width =
+				gspca_dev->cam.cam_mode[i].width;
+			fsize->discrete.height =
+				gspca_dev->cam.cam_mode[i].height;
+			return 0;
+		}
+		index++;
+	}
+
+	return -EINVAL;
+}
+
+static int vidioc_enum_frameintervals(struct file *filp, void *priv,
+				      struct v4l2_frmivalenum *fival)
+{
+	struct gspca_dev *gspca_dev = video_drvdata(filp);
+	int mode = wxh_to_mode(gspca_dev, fival->width, fival->height);
+	__u32 i;
+
+	if (gspca_dev->cam.mode_framerates == NULL ||
+			gspca_dev->cam.mode_framerates[mode].nrates == 0)
+		return -EINVAL;
+
+	if (fival->pixel_format !=
+			gspca_dev->cam.cam_mode[mode].pixelformat)
+		return -EINVAL;
+
+	for (i = 0; i < gspca_dev->cam.mode_framerates[mode].nrates; i++) {
+		if (fival->index == i) {
+			fival->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+			fival->discrete.numerator = 1;
+			fival->discrete.denominator =
+				gspca_dev->cam.mode_framerates[mode].rates[i];
+			return 0;
+		}
+	}
+
+	return -EINVAL;
+}
+
+static void gspca_release(struct v4l2_device *v4l2_device)
+{
+	struct gspca_dev *gspca_dev =
+		container_of(v4l2_device, struct gspca_dev, v4l2_dev);
+
+	v4l2_ctrl_handler_free(gspca_dev->vdev.ctrl_handler);
+	v4l2_device_unregister(&gspca_dev->v4l2_dev);
+	kfree(gspca_dev->usb_buf);
+	kfree(gspca_dev);
+}
+
+static int dev_open(struct file *file)
+{
+	struct gspca_dev *gspca_dev = video_drvdata(file);
+	int ret;
+
+	PDEBUG(D_STREAM, "[%s] open", current->comm);
+
+	/* protect the subdriver against rmmod */
+	if (!try_module_get(gspca_dev->module))
+		return -ENODEV;
+
+	ret = v4l2_fh_open(file);
+	if (ret)
+		module_put(gspca_dev->module);
+	return ret;
+}
+
+static int dev_close(struct file *file)
+{
+	struct gspca_dev *gspca_dev = video_drvdata(file);
+
+	PDEBUG(D_STREAM, "[%s] close", current->comm);
+
+	/* Needed for gspca_stream_off, always lock before queue_lock! */
+	if (mutex_lock_interruptible(&gspca_dev->usb_lock))
+		return -ERESTARTSYS;
+
+	if (mutex_lock_interruptible(&gspca_dev->queue_lock)) {
+		mutex_unlock(&gspca_dev->usb_lock);
+		return -ERESTARTSYS;
+	}
+
+	/* if the file did the capture, free the streaming resources */
+	if (gspca_dev->capt_file == file) {
+		if (gspca_dev->streaming)
+			gspca_stream_off(gspca_dev);
+		frame_free(gspca_dev);
+	}
+	module_put(gspca_dev->module);
+	mutex_unlock(&gspca_dev->queue_lock);
+	mutex_unlock(&gspca_dev->usb_lock);
+
+	PDEBUG(D_STREAM, "close done");
+
+	return v4l2_fh_release(file);
+}
+
+static int vidioc_querycap(struct file *file, void  *priv,
+			   struct v4l2_capability *cap)
+{
+	struct gspca_dev *gspca_dev = video_drvdata(file);
+
+	strlcpy((char *) cap->driver, gspca_dev->sd_desc->name,
+			sizeof cap->driver);
+	if (gspca_dev->dev->product != NULL) {
+		strlcpy((char *) cap->card, gspca_dev->dev->product,
+			sizeof cap->card);
+	} else {
+		snprintf((char *) cap->card, sizeof cap->card,
+			"USB Camera (%04x:%04x)",
+			le16_to_cpu(gspca_dev->dev->descriptor.idVendor),
+			le16_to_cpu(gspca_dev->dev->descriptor.idProduct));
+	}
+	usb_make_path(gspca_dev->dev, (char *) cap->bus_info,
+			sizeof(cap->bus_info));
+	cap->device_caps = V4L2_CAP_VIDEO_CAPTURE
+			  | V4L2_CAP_STREAMING
+			  | V4L2_CAP_READWRITE;
+	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+	return 0;
+}
+
+static int vidioc_enum_input(struct file *file, void *priv,
+				struct v4l2_input *input)
+{
+	struct gspca_dev *gspca_dev = video_drvdata(file);
+
+	if (input->index != 0)
+		return -EINVAL;
+	input->type = V4L2_INPUT_TYPE_CAMERA;
+	input->status = gspca_dev->cam.input_flags;
+	strlcpy(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 = video_drvdata(file);
+	int i, ret = 0, streaming;
+
+	i = rb->memory;			/* (avoid compilation warning) */
+	switch (i) {
+	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 != GSPCA_MEMORY_READ
+	    && 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 */
+	streaming = gspca_dev->streaming;
+	if (streaming) {
+		gspca_stream_off(gspca_dev);
+
+		/* Don't restart the stream when switching from read
+		 * to mmap mode */
+		if (gspca_dev->memory == GSPCA_MEMORY_READ)
+			streaming = 0;
+	}
+
+	/* free the previous allocated buffers, if any */
+	if (gspca_dev->nframes != 0)
+		frame_free(gspca_dev);
+	if (rb->count == 0)			/* unrequest */
+		goto out;
+	ret = frame_alloc(gspca_dev, file, rb->memory, rb->count);
+	if (ret == 0) {
+		rb->count = gspca_dev->nframes;
+		if (streaming)
+			ret = gspca_init_transfer(gspca_dev);
+	}
+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 = video_drvdata(file);
+	struct gspca_frame *frame;
+
+	if (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 = video_drvdata(file);
+	int ret;
+
+	if (buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+	if (mutex_lock_interruptible(&gspca_dev->queue_lock))
+		return -ERESTARTSYS;
+
+	/* check the capture file */
+	if (gspca_dev->capt_file != file) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	if (gspca_dev->nframes == 0
+	    || !(gspca_dev->frame[0].v4l2_buf.flags & V4L2_BUF_FLAG_QUEUED)) {
+		ret = -EINVAL;
+		goto out;
+	}
+	if (!gspca_dev->streaming) {
+		ret = gspca_init_transfer(gspca_dev);
+		if (ret < 0)
+			goto out;
+	}
+	PDEBUG_MODE(gspca_dev, D_STREAM, "stream on OK",
+		    gspca_dev->pixfmt.pixelformat,
+		    gspca_dev->pixfmt.width, gspca_dev->pixfmt.height);
+	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 = video_drvdata(file);
+	int i, ret;
+
+	if (buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	if (mutex_lock_interruptible(&gspca_dev->queue_lock))
+		return -ERESTARTSYS;
+
+	if (!gspca_dev->streaming) {
+		ret = 0;
+		goto out;
+	}
+
+	/* check the capture file */
+	if (gspca_dev->capt_file != file) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	/* stop streaming */
+	gspca_stream_off(gspca_dev);
+	/* In case another thread is waiting in dqbuf */
+	wake_up_interruptible(&gspca_dev->wq);
+
+	/* empty the transfer queues */
+	for (i = 0; i < gspca_dev->nframes; i++)
+		gspca_dev->frame[i].v4l2_buf.flags &= ~BUF_ALL_FLAGS;
+	atomic_set(&gspca_dev->fr_q, 0);
+	atomic_set(&gspca_dev->fr_i, 0);
+	gspca_dev->fr_o = 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 = video_drvdata(file);
+
+	gspca_dev->usb_err = 0;
+	return gspca_dev->sd_desc->get_jcomp(gspca_dev, jpegcomp);
+}
+
+static int vidioc_s_jpegcomp(struct file *file, void *priv,
+			const struct v4l2_jpegcompression *jpegcomp)
+{
+	struct gspca_dev *gspca_dev = video_drvdata(file);
+
+	gspca_dev->usb_err = 0;
+	return gspca_dev->sd_desc->set_jcomp(gspca_dev, jpegcomp);
+}
+
+static int vidioc_g_parm(struct file *filp, void *priv,
+			struct v4l2_streamparm *parm)
+{
+	struct gspca_dev *gspca_dev = video_drvdata(filp);
+
+	parm->parm.capture.readbuffers = gspca_dev->nbufread;
+
+	if (gspca_dev->sd_desc->get_streamparm) {
+		gspca_dev->usb_err = 0;
+		gspca_dev->sd_desc->get_streamparm(gspca_dev, parm);
+		return gspca_dev->usb_err;
+	}
+	return 0;
+}
+
+static int vidioc_s_parm(struct file *filp, void *priv,
+			struct v4l2_streamparm *parm)
+{
+	struct gspca_dev *gspca_dev = video_drvdata(filp);
+	unsigned 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;
+
+	if (gspca_dev->sd_desc->set_streamparm) {
+		gspca_dev->usb_err = 0;
+		gspca_dev->sd_desc->set_streamparm(gspca_dev, parm);
+		return gspca_dev->usb_err;
+	}
+
+	return 0;
+}
+
+static int dev_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct gspca_dev *gspca_dev = video_drvdata(file);
+	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->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;
+	}
+	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;
+}
+
+static int frame_ready_nolock(struct gspca_dev *gspca_dev, struct file *file,
+				enum v4l2_memory memory)
+{
+	if (!gspca_dev->present)
+		return -ENODEV;
+	if (gspca_dev->capt_file != file || gspca_dev->memory != memory ||
+			!gspca_dev->streaming)
+		return -EINVAL;
+
+	/* check if a frame is ready */
+	return gspca_dev->fr_o != atomic_read(&gspca_dev->fr_i);
+}
+
+static int frame_ready(struct gspca_dev *gspca_dev, struct file *file,
+			enum v4l2_memory memory)
+{
+	int ret;
+
+	if (mutex_lock_interruptible(&gspca_dev->queue_lock))
+		return -ERESTARTSYS;
+	ret = frame_ready_nolock(gspca_dev, file, memory);
+	mutex_unlock(&gspca_dev->queue_lock);
+	return ret;
+}
+
+/*
+ * 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 = video_drvdata(file);
+	struct gspca_frame *frame;
+	int i, j, ret;
+
+	PDEBUG(D_FRAM, "dqbuf");
+
+	if (mutex_lock_interruptible(&gspca_dev->queue_lock))
+		return -ERESTARTSYS;
+
+	for (;;) {
+		ret = frame_ready_nolock(gspca_dev, file, v4l2_buf->memory);
+		if (ret < 0)
+			goto out;
+		if (ret > 0)
+			break;
+
+		mutex_unlock(&gspca_dev->queue_lock);
+
+		if (file->f_flags & O_NONBLOCK)
+			return -EAGAIN;
+
+		/* wait till a frame is ready */
+		ret = wait_event_interruptible_timeout(gspca_dev->wq,
+			frame_ready(gspca_dev, file, v4l2_buf->memory),
+			msecs_to_jiffies(3000));
+		if (ret < 0)
+			return ret;
+		if (ret == 0)
+			return -EIO;
+
+		if (mutex_lock_interruptible(&gspca_dev->queue_lock))
+			return -ERESTARTSYS;
+	}
+
+	i = gspca_dev->fr_o;
+	j = gspca_dev->fr_queue[i];
+	frame = &gspca_dev->frame[j];
+
+	gspca_dev->fr_o = (i + 1) % GSPCA_MAX_FRAMES;
+
+	frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_DONE;
+	memcpy(v4l2_buf, &frame->v4l2_buf, sizeof *v4l2_buf);
+	PDEBUG(D_FRAM, "dqbuf %d", j);
+	ret = 0;
+
+	if (gspca_dev->memory == V4L2_MEMORY_USERPTR) {
+		if (copy_to_user((__u8 __user *) frame->v4l2_buf.m.userptr,
+				 frame->data,
+				 frame->v4l2_buf.bytesused)) {
+			PERR("dqbuf cp to user failed");
+			ret = -EFAULT;
+		}
+	}
+out:
+	mutex_unlock(&gspca_dev->queue_lock);
+
+	if (ret == 0 && gspca_dev->sd_desc->dq_callback) {
+		mutex_lock(&gspca_dev->usb_lock);
+		gspca_dev->usb_err = 0;
+		if (gspca_dev->present)
+			gspca_dev->sd_desc->dq_callback(gspca_dev);
+		mutex_unlock(&gspca_dev->usb_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 = video_drvdata(file);
+	struct gspca_frame *frame;
+	int i, index, ret;
+
+	PDEBUG(D_FRAM, "qbuf %d", v4l2_buf->index);
+
+	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;
+
+	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 = atomic_read(&gspca_dev->fr_q);
+	gspca_dev->fr_queue[i] = index;
+	atomic_set(&gspca_dev->fr_q, (i + 1) % GSPCA_MAX_FRAMES);
+
+	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 (mutex_lock_interruptible(&gspca_dev->usb_lock))
+		return -ERESTARTSYS;
+
+	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);
+			goto out;
+		}
+		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);
+				goto out;
+			}
+		}
+	}
+
+	/* start streaming */
+	ret = vidioc_streamon(file, gspca_dev, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+	if (ret != 0)
+		PDEBUG(D_STREAM, "read streamon err %d", ret);
+out:
+	mutex_unlock(&gspca_dev->usb_lock);
+	return ret;
+}
+
+static unsigned int dev_poll(struct file *file, poll_table *wait)
+{
+	struct gspca_dev *gspca_dev = video_drvdata(file);
+	unsigned long req_events = poll_requested_events(wait);
+	int ret = 0;
+
+	PDEBUG(D_FRAM, "poll");
+
+	if (req_events & POLLPRI)
+		ret |= v4l2_ctrl_poll(file, wait);
+
+	if (req_events & (POLLIN | POLLRDNORM)) {
+		/* if reqbufs is not done, the user would use read() */
+		if (gspca_dev->memory == GSPCA_MEMORY_NO) {
+			if (read_alloc(gspca_dev, file) != 0) {
+				ret |= POLLERR;
+				goto out;
+			}
+		}
+
+		poll_wait(file, &gspca_dev->wq, wait);
+
+		/* check if an image has been received */
+		if (mutex_lock_interruptible(&gspca_dev->queue_lock) != 0) {
+			ret |= POLLERR;
+			goto out;
+		}
+		if (gspca_dev->fr_o != atomic_read(&gspca_dev->fr_i))
+			ret |= POLLIN | POLLRDNORM;
+		mutex_unlock(&gspca_dev->queue_lock);
+	}
+
+out:
+	if (!gspca_dev->present)
+		ret |= POLLHUP;
+
+	return ret;
+}
+
+static ssize_t dev_read(struct file *file, char __user *data,
+		    size_t count, loff_t *ppos)
+{
+	struct gspca_dev *gspca_dev = video_drvdata(file);
+	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->memory == GSPCA_MEMORY_NO) { /* first time ? */
+		ret = read_alloc(gspca_dev, file);
+		if (ret != 0)
+			return ret;
+	}
+
+	/* get a frame */
+	v4l2_get_timestamp(&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 a newer 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) {
+		PERR("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 struct v4l2_file_operations dev_fops = {
+	.owner = THIS_MODULE,
+	.open = dev_open,
+	.release = dev_close,
+	.read = dev_read,
+	.mmap = dev_mmap,
+	.unlocked_ioctl = video_ioctl2,
+	.poll	= dev_poll,
+};
+
+static const struct v4l2_ioctl_ops dev_ioctl_ops = {
+	.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_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_enum_framesizes = vidioc_enum_framesizes,
+	.vidioc_enum_frameintervals = vidioc_enum_frameintervals,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+	.vidioc_g_chip_info	= vidioc_g_chip_info,
+	.vidioc_g_register	= vidioc_g_register,
+	.vidioc_s_register	= vidioc_s_register,
+#endif
+	.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static const struct video_device gspca_template = {
+	.name = "gspca main driver",
+	.fops = &dev_fops,
+	.ioctl_ops = &dev_ioctl_ops,
+	.release = video_device_release_empty, /* We use v4l2_dev.release */
+};
+
+/*
+ * 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_probe2(struct usb_interface *intf,
+		const struct usb_device_id *id,
+		const struct sd_desc *sd_desc,
+		int dev_size,
+		struct module *module)
+{
+	struct gspca_dev *gspca_dev;
+	struct usb_device *dev = interface_to_usbdev(intf);
+	int ret;
+
+	pr_info("%s-" GSPCA_VERSION " probing %04x:%04x\n",
+		sd_desc->name, id->idVendor, id->idProduct);
+
+	/* create the device */
+	if (dev_size < sizeof *gspca_dev)
+		dev_size = sizeof *gspca_dev;
+	gspca_dev = kzalloc(dev_size, GFP_KERNEL);
+	if (!gspca_dev) {
+		pr_err("couldn't kzalloc gspca struct\n");
+		return -ENOMEM;
+	}
+	gspca_dev->usb_buf = kmalloc(USB_BUF_SZ, GFP_KERNEL);
+	if (!gspca_dev->usb_buf) {
+		pr_err("out of memory\n");
+		ret = -ENOMEM;
+		goto out;
+	}
+	gspca_dev->dev = dev;
+	gspca_dev->iface = intf->cur_altsetting->desc.bInterfaceNumber;
+	gspca_dev->xfer_ep = -1;
+
+	/* check if any audio device */
+	if (dev->actconfig->desc.bNumInterfaces != 1) {
+		int i;
+		struct usb_interface *intf2;
+
+		for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++) {
+			intf2 = dev->actconfig->interface[i];
+			if (intf2 != NULL
+			 && intf2->altsetting != NULL
+			 && intf2->altsetting->desc.bInterfaceClass ==
+					 USB_CLASS_AUDIO) {
+				gspca_dev->audio = 1;
+				break;
+			}
+		}
+	}
+
+	gspca_dev->v4l2_dev.release = gspca_release;
+	ret = v4l2_device_register(&intf->dev, &gspca_dev->v4l2_dev);
+	if (ret)
+		goto out;
+	gspca_dev->sd_desc = sd_desc;
+	gspca_dev->nbufread = 2;
+	gspca_dev->empty_packet = -1;	/* don't check the empty packets */
+	gspca_dev->vdev = gspca_template;
+	gspca_dev->vdev.v4l2_dev = &gspca_dev->v4l2_dev;
+	video_set_drvdata(&gspca_dev->vdev, gspca_dev);
+	gspca_dev->module = module;
+	gspca_dev->present = 1;
+
+	mutex_init(&gspca_dev->usb_lock);
+	gspca_dev->vdev.lock = &gspca_dev->usb_lock;
+	mutex_init(&gspca_dev->queue_lock);
+	init_waitqueue_head(&gspca_dev->wq);
+
+	/* configure the subdriver and initialize the USB device */
+	ret = sd_desc->config(gspca_dev, id);
+	if (ret < 0)
+		goto out;
+	ret = sd_desc->init(gspca_dev);
+	if (ret < 0)
+		goto out;
+	if (sd_desc->init_controls)
+		ret = sd_desc->init_controls(gspca_dev);
+	if (ret < 0)
+		goto out;
+	gspca_set_default_mode(gspca_dev);
+
+	ret = gspca_input_connect(gspca_dev);
+	if (ret)
+		goto out;
+
+	/*
+	 * Don't take usb_lock for these ioctls. This improves latency if
+	 * usb_lock is taken for a long time, e.g. when changing a control
+	 * value, and a new frame is ready to be dequeued.
+	 */
+	v4l2_disable_ioctl_locking(&gspca_dev->vdev, VIDIOC_DQBUF);
+	v4l2_disable_ioctl_locking(&gspca_dev->vdev, VIDIOC_QBUF);
+	v4l2_disable_ioctl_locking(&gspca_dev->vdev, VIDIOC_QUERYBUF);
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+	if (!gspca_dev->sd_desc->get_register)
+		v4l2_disable_ioctl(&gspca_dev->vdev, VIDIOC_DBG_G_REGISTER);
+	if (!gspca_dev->sd_desc->set_register)
+		v4l2_disable_ioctl(&gspca_dev->vdev, VIDIOC_DBG_S_REGISTER);
+#endif
+	if (!gspca_dev->sd_desc->get_jcomp)
+		v4l2_disable_ioctl(&gspca_dev->vdev, VIDIOC_G_JPEGCOMP);
+	if (!gspca_dev->sd_desc->set_jcomp)
+		v4l2_disable_ioctl(&gspca_dev->vdev, VIDIOC_S_JPEGCOMP);
+
+	/* init video stuff */
+	ret = video_register_device(&gspca_dev->vdev,
+				  VFL_TYPE_GRABBER,
+				  -1);
+	if (ret < 0) {
+		pr_err("video_register_device err %d\n", ret);
+		goto out;
+	}
+
+	usb_set_intfdata(intf, gspca_dev);
+	PDEBUG(D_PROBE, "%s created", video_device_node_name(&gspca_dev->vdev));
+
+	gspca_input_create_urb(gspca_dev);
+
+	return 0;
+out:
+#if IS_ENABLED(CONFIG_INPUT)
+	if (gspca_dev->input_dev)
+		input_unregister_device(gspca_dev->input_dev);
+#endif
+	v4l2_ctrl_handler_free(gspca_dev->vdev.ctrl_handler);
+	kfree(gspca_dev->usb_buf);
+	kfree(gspca_dev);
+	return ret;
+}
+EXPORT_SYMBOL(gspca_dev_probe2);
+
+/* same function as the previous one, but check the interface */
+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_device *dev = interface_to_usbdev(intf);
+
+	/* we don't handle multi-config cameras */
+	if (dev->descriptor.bNumConfigurations != 1) {
+		pr_err("%04x:%04x too many config\n",
+		       id->idVendor, id->idProduct);
+		return -ENODEV;
+	}
+
+	/* the USB video interface must be the first one */
+	if (dev->actconfig->desc.bNumInterfaces != 1
+	 && intf->cur_altsetting->desc.bInterfaceNumber != 0)
+		return -ENODEV;
+
+	return gspca_dev_probe2(intf, id, sd_desc, dev_size, module);
+}
+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 IS_ENABLED(CONFIG_INPUT)
+	struct input_dev *input_dev;
+#endif
+
+	PDEBUG(D_PROBE, "%s disconnect",
+		video_device_node_name(&gspca_dev->vdev));
+
+	mutex_lock(&gspca_dev->usb_lock);
+
+	gspca_dev->present = 0;
+	destroy_urbs(gspca_dev);
+
+#if IS_ENABLED(CONFIG_INPUT)
+	gspca_input_destroy_urb(gspca_dev);
+	input_dev = gspca_dev->input_dev;
+	if (input_dev) {
+		gspca_dev->input_dev = NULL;
+		input_unregister_device(input_dev);
+	}
+#endif
+	/* Free subdriver's streaming resources / stop sd workqueue(s) */
+	if (gspca_dev->sd_desc->stop0 && gspca_dev->streaming)
+		gspca_dev->sd_desc->stop0(gspca_dev);
+	gspca_dev->streaming = 0;
+	gspca_dev->dev = NULL;
+	wake_up_interruptible(&gspca_dev->wq);
+
+	v4l2_device_disconnect(&gspca_dev->v4l2_dev);
+	video_unregister_device(&gspca_dev->vdev);
+
+	mutex_unlock(&gspca_dev->usb_lock);
+
+	/* (this will call gspca_release() immediately or on last close) */
+	v4l2_device_put(&gspca_dev->v4l2_dev);
+}
+EXPORT_SYMBOL(gspca_disconnect);
+
+#ifdef CONFIG_PM
+int gspca_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct gspca_dev *gspca_dev = usb_get_intfdata(intf);
+
+	gspca_input_destroy_urb(gspca_dev);
+
+	if (!gspca_dev->streaming)
+		return 0;
+
+	mutex_lock(&gspca_dev->usb_lock);
+	gspca_dev->frozen = 1;		/* avoid urb error messages */
+	gspca_dev->usb_err = 0;
+	if (gspca_dev->sd_desc->stopN)
+		gspca_dev->sd_desc->stopN(gspca_dev);
+	destroy_urbs(gspca_dev);
+	gspca_set_alt0(gspca_dev);
+	if (gspca_dev->sd_desc->stop0)
+		gspca_dev->sd_desc->stop0(gspca_dev);
+	mutex_unlock(&gspca_dev->usb_lock);
+
+	return 0;
+}
+EXPORT_SYMBOL(gspca_suspend);
+
+int gspca_resume(struct usb_interface *intf)
+{
+	struct gspca_dev *gspca_dev = usb_get_intfdata(intf);
+	int streaming, ret = 0;
+
+	mutex_lock(&gspca_dev->usb_lock);
+	gspca_dev->frozen = 0;
+	gspca_dev->usb_err = 0;
+	gspca_dev->sd_desc->init(gspca_dev);
+	/*
+	 * Most subdrivers send all ctrl values on sd_start and thus
+	 * only write to the device registers on s_ctrl when streaming ->
+	 * Clear streaming to avoid setting all ctrls twice.
+	 */
+	streaming = gspca_dev->streaming;
+	gspca_dev->streaming = 0;
+	if (streaming)
+		ret = gspca_init_transfer(gspca_dev);
+	else
+		gspca_input_create_urb(gspca_dev);
+	mutex_unlock(&gspca_dev->usb_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL(gspca_resume);
+#endif
+
+/* -- module insert / remove -- */
+static int __init gspca_init(void)
+{
+	pr_info("v" GSPCA_VERSION " registered\n");
+	return 0;
+}
+static void __exit gspca_exit(void)
+{
+}
+
+module_init(gspca_init);
+module_exit(gspca_exit);
+
+module_param_named(debug, gspca_debug, int, 0644);
+MODULE_PARM_DESC(debug,
+		"1:probe 2:config 3:stream 4:frame 5:packet 6:usbi 7:usbo");
diff --git a/drivers/media/usb/gspca/gspca.h b/drivers/media/usb/gspca/gspca.h
new file mode 100644
index 0000000..d39adf9
--- /dev/null
+++ b/drivers/media/usb/gspca/gspca.h
@@ -0,0 +1,240 @@
+#ifndef GSPCAV2_H
+#define GSPCAV2_H
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/usb.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <linux/mutex.h>
+
+
+
+/* GSPCA debug codes */
+
+#define D_PROBE  1
+#define D_CONF   2
+#define D_STREAM 3
+#define D_FRAM   4
+#define D_PACK   5
+#define D_USBI   6
+#define D_USBO   7
+
+extern int gspca_debug;
+
+
+#define PDEBUG(level, fmt, ...) \
+	v4l2_dbg(level, gspca_debug, &gspca_dev->v4l2_dev, fmt, ##__VA_ARGS__)
+
+#define PERR(fmt, ...) \
+	v4l2_err(&gspca_dev->v4l2_dev, fmt, ##__VA_ARGS__)
+
+#define GSPCA_MAX_FRAMES 16	/* maximum number of video frame buffers */
+/* image transfers */
+#define MAX_NURBS 4		/* max number of URBs */
+
+
+/* used to list framerates supported by a camera mode (resolution) */
+struct framerates {
+	const u8 *rates;
+	int nrates;
+};
+
+/* device information - set at probe time */
+struct cam {
+	const struct v4l2_pix_format *cam_mode;	/* size nmodes */
+	const struct framerates *mode_framerates; /* must have size nmodes,
+						   * just like cam_mode */
+	u32 bulk_size;		/* buffer size when image transfer by bulk */
+	u32 input_flags;	/* value for ENUM_INPUT status flags */
+	u8 nmodes;		/* size of cam_mode */
+	u8 no_urb_create;	/* don't create transfer URBs */
+	u8 bulk_nurbs;		/* number of URBs in bulk mode
+				 * - cannot be > MAX_NURBS
+				 * - when 0 and bulk_size != 0 means
+				 *   1 URB and submit done by subdriver */
+	u8 bulk;		/* image transfer by 0:isoc / 1:bulk */
+	u8 npkt;		/* number of packets in an ISOC message
+				 * 0 is the default value: 32 packets */
+	u8 needs_full_bandwidth;/* Set this flag to notify the bandwidth calc.
+				 * code that the cam fills all image buffers to
+				 * the max, even when using compression. */
+};
+
+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_get_jpg_op) (struct gspca_dev *,
+				struct v4l2_jpegcompression *);
+typedef int (*cam_set_jpg_op) (struct gspca_dev *,
+				const struct v4l2_jpegcompression *);
+typedef int (*cam_get_reg_op) (struct gspca_dev *,
+				struct v4l2_dbg_register *);
+typedef int (*cam_set_reg_op) (struct gspca_dev *,
+				const struct v4l2_dbg_register *);
+typedef int (*cam_chip_info_op) (struct gspca_dev *,
+				struct v4l2_dbg_chip_info *);
+typedef void (*cam_streamparm_op) (struct gspca_dev *,
+				  struct v4l2_streamparm *);
+typedef void (*cam_pkt_op) (struct gspca_dev *gspca_dev,
+				u8 *data,
+				int len);
+typedef int (*cam_int_pkt_op) (struct gspca_dev *gspca_dev,
+				u8 *data,
+				int len);
+typedef void (*cam_format_op) (struct gspca_dev *gspca_dev,
+				struct v4l2_format *fmt);
+typedef int (*cam_frmsize_op) (struct gspca_dev *gspca_dev,
+				struct v4l2_frmsizeenum *fsize);
+
+/* subdriver description */
+struct sd_desc {
+/* information */
+	const char *name;	/* sub-driver name */
+/* mandatory operations */
+	cam_cf_op config;	/* called on probe */
+	cam_op init;		/* called on probe and resume */
+	cam_op init_controls;	/* called on probe */
+	cam_op start;		/* called on stream on after URBs creation */
+	cam_pkt_op pkt_scan;
+/* optional operations */
+	cam_op isoc_init;	/* called on stream on before getting the EP */
+	cam_op isoc_nego;	/* called when URB submit failed with NOSPC */
+	cam_v_op stopN;		/* called on stream off - main alt */
+	cam_v_op stop0;		/* called on stream off & disconnect - alt 0 */
+	cam_v_op dq_callback;	/* called when a frame has been dequeued */
+	cam_get_jpg_op get_jcomp;
+	cam_set_jpg_op set_jcomp;
+	cam_streamparm_op get_streamparm;
+	cam_streamparm_op set_streamparm;
+	cam_format_op try_fmt;
+	cam_frmsize_op enum_framesizes;
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+	cam_set_reg_op set_register;
+	cam_get_reg_op get_register;
+	cam_chip_info_op get_chip_info;
+#endif
+#if IS_ENABLED(CONFIG_INPUT)
+	cam_int_pkt_op int_pkt_scan;
+	/* other_input makes the gspca core create gspca_dev->input even when
+	   int_pkt_scan is NULL, for cams with non interrupt driven buttons */
+	u8 other_input;
+#endif
+};
+
+/* packet types when moving from iso buf to frame buf */
+enum gspca_packet_type {
+	DISCARD_PACKET,
+	FIRST_PACKET,
+	INTER_PACKET,
+	LAST_PACKET
+};
+
+struct gspca_frame {
+	__u8 *data;			/* frame buffer */
+	int vma_use_count;
+	struct v4l2_buffer v4l2_buf;
+};
+
+struct gspca_dev {
+	struct video_device vdev;	/* !! must be the first item */
+	struct module *module;		/* subdriver handling the device */
+	struct v4l2_device v4l2_dev;
+	struct usb_device *dev;
+	struct file *capt_file;		/* file doing video capture */
+					/* protected by queue_lock */
+#if IS_ENABLED(CONFIG_INPUT)
+	struct input_dev *input_dev;
+	char phys[64];			/* physical device path */
+#endif
+
+	struct cam cam;				/* device information */
+	const struct sd_desc *sd_desc;		/* subdriver description */
+	struct v4l2_ctrl_handler ctrl_handler;
+
+	/* autogain and exposure or gain control cluster, these are global as
+	   the autogain/exposure functions in autogain_functions.c use them */
+	struct {
+		struct v4l2_ctrl *autogain;
+		struct v4l2_ctrl *exposure;
+		struct v4l2_ctrl *gain;
+		int exp_too_low_cnt, exp_too_high_cnt;
+	};
+
+#define USB_BUF_SZ 64
+	__u8 *usb_buf;				/* buffer for USB exchanges */
+	struct urb *urb[MAX_NURBS];
+#if IS_ENABLED(CONFIG_INPUT)
+	struct urb *int_urb;
+#endif
+
+	__u8 *frbuf;				/* buffer for nframes */
+	struct gspca_frame frame[GSPCA_MAX_FRAMES];
+	u8 *image;				/* image beeing filled */
+	__u32 frsz;				/* frame size */
+	u32 image_len;				/* current length of image */
+	atomic_t fr_q;				/* next frame to queue */
+	atomic_t fr_i;				/* frame being filled */
+	signed char fr_queue[GSPCA_MAX_FRAMES];	/* frame queue */
+	char nframes;				/* number of frames */
+	u8 fr_o;				/* next frame to dequeue */
+	__u8 last_packet_type;
+	__s8 empty_packet;		/* if (-1) don't check empty packets */
+	__u8 streaming;			/* protected by both mutexes (*) */
+
+	__u8 curr_mode;			/* current camera mode */
+	struct v4l2_pix_format pixfmt;	/* current mode parameters */
+	__u32 sequence;			/* frame sequence number */
+
+	wait_queue_head_t wq;		/* wait queue */
+	struct mutex usb_lock;		/* usb exchange protection */
+	struct mutex queue_lock;	/* ISOC queue protection */
+	int usb_err;			/* USB error - protected by usb_lock */
+	u16 pkt_size;			/* ISOC packet size */
+#ifdef CONFIG_PM
+	char frozen;			/* suspend - resume */
+#endif
+	char present;			/* device connected */
+	char nbufread;			/* number of buffers for read() */
+	char memory;			/* memory type (V4L2_MEMORY_xxx) */
+	__u8 iface;			/* USB interface number */
+	__u8 alt;			/* USB alternate setting */
+	int xfer_ep;			/* USB transfer endpoint address */
+	u8 audio;			/* presence of audio device */
+
+	/* (*) These variables are proteced by both usb_lock and queue_lock,
+	   that is any code setting them is holding *both*, which means that
+	   any code getting them needs to hold at least one of them */
+};
+
+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);
+int gspca_dev_probe2(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);
+void gspca_frame_add(struct gspca_dev *gspca_dev,
+			enum gspca_packet_type packet_type,
+			const u8 *data,
+			int len);
+#ifdef CONFIG_PM
+int gspca_suspend(struct usb_interface *intf, pm_message_t message);
+int gspca_resume(struct usb_interface *intf);
+#endif
+int gspca_expo_autogain(struct gspca_dev *gspca_dev, int avg_lum,
+	int desired_avg_lum, int deadzone, int gain_knee, int exposure_knee);
+int gspca_coarse_grained_expo_autogain(struct gspca_dev *gspca_dev,
+	int avg_lum, int desired_avg_lum, int deadzone);
+
+#endif /* GSPCAV2_H */
diff --git a/drivers/media/usb/gspca/jeilinj.c b/drivers/media/usb/gspca/jeilinj.c
new file mode 100644
index 0000000..19736e2
--- /dev/null
+++ b/drivers/media/usb/gspca/jeilinj.c
@@ -0,0 +1,549 @@
+/*
+ * Jeilinj subdriver
+ *
+ * Supports some Jeilin dual-mode cameras which use bulk transport and
+ * download raw JPEG data.
+ *
+ * Copyright (C) 2009 Theodore Kilgore
+ *
+ * Sportscam DV15 support and control settings are
+ * Copyright (C) 2011 Patrice Chotard
+ *
+ * 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "jeilinj"
+
+#include <linux/slab.h>
+#include "gspca.h"
+#include "jpeg.h"
+
+MODULE_AUTHOR("Theodore Kilgore <kilgota@auburn.edu>");
+MODULE_DESCRIPTION("GSPCA/JEILINJ USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* Default timeouts, in ms */
+#define JEILINJ_CMD_TIMEOUT 500
+#define JEILINJ_CMD_DELAY 160
+#define JEILINJ_DATA_TIMEOUT 1000
+
+/* Maximum transfer size to use. */
+#define JEILINJ_MAX_TRANSFER 0x200
+#define FRAME_HEADER_LEN 0x10
+#define FRAME_START 0xFFFFFFFF
+
+enum {
+	SAKAR_57379,
+	SPORTSCAM_DV15,
+};
+
+#define CAMQUALITY_MIN 0	/* highest cam quality */
+#define CAMQUALITY_MAX 97	/* lowest cam quality  */
+
+/* Structure to hold all of our device specific stuff */
+struct sd {
+	struct gspca_dev gspca_dev;	/* !! must be the first item */
+	int blocks_left;
+	const struct v4l2_pix_format *cap_mode;
+	struct v4l2_ctrl *freq;
+	struct v4l2_ctrl *jpegqual;
+	/* Driver stuff */
+	u8 type;
+	u8 quality;				 /* image quality */
+#define QUALITY_MIN 35
+#define QUALITY_MAX 85
+#define QUALITY_DEF 85
+	u8 jpeg_hdr[JPEG_HDR_SZ];
+};
+
+struct jlj_command {
+	unsigned char instruction[2];
+	unsigned char ack_wanted;
+	unsigned char delay;
+};
+
+/* AFAICT these cameras will only do 320x240. */
+static struct v4l2_pix_format jlj_mode[] = {
+	{ 320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+		.bytesperline = 320,
+		.sizeimage = 320 * 240,
+		.colorspace = V4L2_COLORSPACE_JPEG,
+		.priv = 0},
+	{ 640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+		.bytesperline = 640,
+		.sizeimage = 640 * 480,
+		.colorspace = V4L2_COLORSPACE_JPEG,
+		.priv = 0}
+};
+
+/*
+ * cam uses endpoint 0x03 to send commands, 0x84 for read commands,
+ * and 0x82 for bulk transfer.
+ */
+
+/* All commands are two bytes only */
+static void jlj_write2(struct gspca_dev *gspca_dev, unsigned char *command)
+{
+	int retval;
+
+	if (gspca_dev->usb_err < 0)
+		return;
+	memcpy(gspca_dev->usb_buf, command, 2);
+	retval = usb_bulk_msg(gspca_dev->dev,
+			usb_sndbulkpipe(gspca_dev->dev, 3),
+			gspca_dev->usb_buf, 2, NULL, 500);
+	if (retval < 0) {
+		pr_err("command write [%02x] error %d\n",
+		       gspca_dev->usb_buf[0], retval);
+		gspca_dev->usb_err = retval;
+	}
+}
+
+/* Responses are one byte only */
+static void jlj_read1(struct gspca_dev *gspca_dev, unsigned char *response)
+{
+	int retval;
+
+	if (gspca_dev->usb_err < 0)
+		return;
+	retval = usb_bulk_msg(gspca_dev->dev,
+	usb_rcvbulkpipe(gspca_dev->dev, 0x84),
+				gspca_dev->usb_buf, 1, NULL, 500);
+	*response = gspca_dev->usb_buf[0];
+	if (retval < 0) {
+		pr_err("read command [%02x] error %d\n",
+		       gspca_dev->usb_buf[0], retval);
+		gspca_dev->usb_err = retval;
+	}
+}
+
+static void setfreq(struct gspca_dev *gspca_dev, s32 val)
+{
+	u8 freq_commands[][2] = {
+		{0x71, 0x80},
+		{0x70, 0x07}
+	};
+
+	freq_commands[0][1] |= val >> 1;
+
+	jlj_write2(gspca_dev, freq_commands[0]);
+	jlj_write2(gspca_dev, freq_commands[1]);
+}
+
+static void setcamquality(struct gspca_dev *gspca_dev, s32 val)
+{
+	u8 quality_commands[][2] = {
+		{0x71, 0x1E},
+		{0x70, 0x06}
+	};
+	u8 camquality;
+
+	/* adapt camera quality from jpeg quality */
+	camquality = ((QUALITY_MAX - val) * CAMQUALITY_MAX)
+		/ (QUALITY_MAX - QUALITY_MIN);
+	quality_commands[0][1] += camquality;
+
+	jlj_write2(gspca_dev, quality_commands[0]);
+	jlj_write2(gspca_dev, quality_commands[1]);
+}
+
+static void setautogain(struct gspca_dev *gspca_dev, s32 val)
+{
+	u8 autogain_commands[][2] = {
+		{0x94, 0x02},
+		{0xcf, 0x00}
+	};
+
+	autogain_commands[1][1] = val << 4;
+
+	jlj_write2(gspca_dev, autogain_commands[0]);
+	jlj_write2(gspca_dev, autogain_commands[1]);
+}
+
+static void setred(struct gspca_dev *gspca_dev, s32 val)
+{
+	u8 setred_commands[][2] = {
+		{0x94, 0x02},
+		{0xe6, 0x00}
+	};
+
+	setred_commands[1][1] = val;
+
+	jlj_write2(gspca_dev, setred_commands[0]);
+	jlj_write2(gspca_dev, setred_commands[1]);
+}
+
+static void setgreen(struct gspca_dev *gspca_dev, s32 val)
+{
+	u8 setgreen_commands[][2] = {
+		{0x94, 0x02},
+		{0xe7, 0x00}
+	};
+
+	setgreen_commands[1][1] = val;
+
+	jlj_write2(gspca_dev, setgreen_commands[0]);
+	jlj_write2(gspca_dev, setgreen_commands[1]);
+}
+
+static void setblue(struct gspca_dev *gspca_dev, s32 val)
+{
+	u8 setblue_commands[][2] = {
+		{0x94, 0x02},
+		{0xe9, 0x00}
+	};
+
+	setblue_commands[1][1] = val;
+
+	jlj_write2(gspca_dev, setblue_commands[0]);
+	jlj_write2(gspca_dev, setblue_commands[1]);
+}
+
+static int jlj_start(struct gspca_dev *gspca_dev)
+{
+	int i;
+	int start_commands_size;
+	u8 response = 0xff;
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct jlj_command start_commands[] = {
+		{{0x71, 0x81}, 0, 0},
+		{{0x70, 0x05}, 0, JEILINJ_CMD_DELAY},
+		{{0x95, 0x70}, 1, 0},
+		{{0x71, 0x81 - gspca_dev->curr_mode}, 0, 0},
+		{{0x70, 0x04}, 0, JEILINJ_CMD_DELAY},
+		{{0x95, 0x70}, 1, 0},
+		{{0x71, 0x00}, 0, 0},   /* start streaming ??*/
+		{{0x70, 0x08}, 0, JEILINJ_CMD_DELAY},
+		{{0x95, 0x70}, 1, 0},
+#define SPORTSCAM_DV15_CMD_SIZE 9
+		{{0x94, 0x02}, 0, 0},
+		{{0xde, 0x24}, 0, 0},
+		{{0x94, 0x02}, 0, 0},
+		{{0xdd, 0xf0}, 0, 0},
+		{{0x94, 0x02}, 0, 0},
+		{{0xe3, 0x2c}, 0, 0},
+		{{0x94, 0x02}, 0, 0},
+		{{0xe4, 0x00}, 0, 0},
+		{{0x94, 0x02}, 0, 0},
+		{{0xe5, 0x00}, 0, 0},
+		{{0x94, 0x02}, 0, 0},
+		{{0xe6, 0x2c}, 0, 0},
+		{{0x94, 0x03}, 0, 0},
+		{{0xaa, 0x00}, 0, 0}
+	};
+
+	sd->blocks_left = 0;
+	/* Under Windows, USB spy shows that only the 9 first start
+	 * commands are used for SPORTSCAM_DV15 webcam
+	 */
+	if (sd->type == SPORTSCAM_DV15)
+		start_commands_size = SPORTSCAM_DV15_CMD_SIZE;
+	else
+		start_commands_size = ARRAY_SIZE(start_commands);
+
+	for (i = 0; i < start_commands_size; i++) {
+		jlj_write2(gspca_dev, start_commands[i].instruction);
+		if (start_commands[i].delay)
+			msleep(start_commands[i].delay);
+		if (start_commands[i].ack_wanted)
+			jlj_read1(gspca_dev, &response);
+	}
+	setcamquality(gspca_dev, v4l2_ctrl_g_ctrl(sd->jpegqual));
+	msleep(2);
+	setfreq(gspca_dev, v4l2_ctrl_g_ctrl(sd->freq));
+	if (gspca_dev->usb_err < 0)
+		PERR("Start streaming command failed");
+	return gspca_dev->usb_err;
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+			u8 *data, int len)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int packet_type;
+	u32 header_marker;
+
+	PDEBUG(D_STREAM, "Got %d bytes out of %d for Block 0",
+			len, JEILINJ_MAX_TRANSFER);
+	if (len != JEILINJ_MAX_TRANSFER) {
+		PDEBUG(D_PACK, "bad length");
+		goto discard;
+	}
+	/* check if it's start of frame */
+	header_marker = ((u32 *)data)[0];
+	if (header_marker == FRAME_START) {
+		sd->blocks_left = data[0x0a] - 1;
+		PDEBUG(D_STREAM, "blocks_left = 0x%x", sd->blocks_left);
+		/* Start a new frame, and add the JPEG header, first thing */
+		gspca_frame_add(gspca_dev, FIRST_PACKET,
+				sd->jpeg_hdr, JPEG_HDR_SZ);
+		/* Toss line 0 of data block 0, keep the rest. */
+		gspca_frame_add(gspca_dev, INTER_PACKET,
+				data + FRAME_HEADER_LEN,
+				JEILINJ_MAX_TRANSFER - FRAME_HEADER_LEN);
+	} else if (sd->blocks_left > 0) {
+		PDEBUG(D_STREAM, "%d blocks remaining for frame",
+				sd->blocks_left);
+		sd->blocks_left -= 1;
+		if (sd->blocks_left == 0)
+			packet_type = LAST_PACKET;
+		else
+			packet_type = INTER_PACKET;
+		gspca_frame_add(gspca_dev, packet_type,
+				data, JEILINJ_MAX_TRANSFER);
+	} else
+		goto discard;
+	return;
+discard:
+	/* Discard data until a new frame starts. */
+	gspca_dev->last_packet_type = DISCARD_PACKET;
+}
+
+/* This function is called at probe time just before sd_init */
+static int sd_config(struct gspca_dev *gspca_dev,
+		const struct usb_device_id *id)
+{
+	struct cam *cam = &gspca_dev->cam;
+	struct sd *dev  = (struct sd *) gspca_dev;
+
+	dev->type = id->driver_info;
+	dev->quality = QUALITY_DEF;
+
+	cam->cam_mode = jlj_mode;
+	cam->nmodes = ARRAY_SIZE(jlj_mode);
+	cam->bulk = 1;
+	cam->bulk_nurbs = 1;
+	cam->bulk_size = JEILINJ_MAX_TRANSFER;
+	return 0;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+	int i;
+	u8 *buf;
+	static u8 stop_commands[][2] = {
+		{0x71, 0x00},
+		{0x70, 0x09},
+		{0x71, 0x80},
+		{0x70, 0x05}
+	};
+
+	for (;;) {
+		/* get the image remaining blocks */
+		usb_bulk_msg(gspca_dev->dev,
+				gspca_dev->urb[0]->pipe,
+				gspca_dev->urb[0]->transfer_buffer,
+				JEILINJ_MAX_TRANSFER, NULL,
+				JEILINJ_DATA_TIMEOUT);
+
+		/* search for 0xff 0xd9  (EOF for JPEG) */
+		i = 0;
+		buf = gspca_dev->urb[0]->transfer_buffer;
+		while ((i < (JEILINJ_MAX_TRANSFER - 1)) &&
+			((buf[i] != 0xff) || (buf[i+1] != 0xd9)))
+			i++;
+
+		if (i != (JEILINJ_MAX_TRANSFER - 1))
+			/* last remaining block found */
+			break;
+		}
+
+	for (i = 0; i < ARRAY_SIZE(stop_commands); i++)
+		jlj_write2(gspca_dev, stop_commands[i]);
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+	return gspca_dev->usb_err;
+}
+
+/* Set up for getting frames. */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+	struct sd *dev = (struct sd *) gspca_dev;
+
+	/* create the JPEG header */
+	jpeg_define(dev->jpeg_hdr, gspca_dev->pixfmt.height,
+			gspca_dev->pixfmt.width,
+			0x21);          /* JPEG 422 */
+	jpeg_set_qual(dev->jpeg_hdr, dev->quality);
+	PDEBUG(D_STREAM, "Start streaming at %dx%d",
+		gspca_dev->pixfmt.height, gspca_dev->pixfmt.width);
+	jlj_start(gspca_dev);
+	return gspca_dev->usb_err;
+}
+
+/* Table of supported USB devices */
+static const struct usb_device_id device_table[] = {
+	{USB_DEVICE(0x0979, 0x0280), .driver_info = SAKAR_57379},
+	{USB_DEVICE(0x0979, 0x0270), .driver_info = SPORTSCAM_DV15},
+	{}
+};
+
+MODULE_DEVICE_TABLE(usb, device_table);
+
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct gspca_dev *gspca_dev =
+		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+	struct sd *sd = (struct sd *)gspca_dev;
+
+	gspca_dev->usb_err = 0;
+
+	if (!gspca_dev->streaming)
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_POWER_LINE_FREQUENCY:
+		setfreq(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_RED_BALANCE:
+		setred(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_GAIN:
+		setgreen(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_BLUE_BALANCE:
+		setblue(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_AUTOGAIN:
+		setautogain(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_JPEG_COMPRESSION_QUALITY:
+		jpeg_set_qual(sd->jpeg_hdr, ctrl->val);
+		setcamquality(gspca_dev, ctrl->val);
+		break;
+	}
+	return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+	.s_ctrl = sd_s_ctrl,
+};
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *)gspca_dev;
+	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+	static const struct v4l2_ctrl_config custom_autogain = {
+		.ops = &sd_ctrl_ops,
+		.id = V4L2_CID_AUTOGAIN,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.name = "Automatic Gain (and Exposure)",
+		.max = 3,
+		.step = 1,
+		.def = 0,
+	};
+
+	gspca_dev->vdev.ctrl_handler = hdl;
+	v4l2_ctrl_handler_init(hdl, 6);
+	sd->freq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
+			V4L2_CID_POWER_LINE_FREQUENCY,
+			V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 1,
+			V4L2_CID_POWER_LINE_FREQUENCY_60HZ);
+	v4l2_ctrl_new_custom(hdl, &custom_autogain, NULL);
+	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_RED_BALANCE, 0, 3, 1, 2);
+	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_GAIN, 0, 3, 1, 2);
+	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_BLUE_BALANCE, 0, 3, 1, 2);
+	sd->jpegqual = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_JPEG_COMPRESSION_QUALITY,
+			QUALITY_MIN, QUALITY_MAX, 1, QUALITY_DEF);
+
+	if (hdl->error) {
+		pr_err("Could not initialize controls\n");
+		return hdl->error;
+	}
+	return 0;
+}
+
+static int sd_set_jcomp(struct gspca_dev *gspca_dev,
+			const struct v4l2_jpegcompression *jcomp)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	v4l2_ctrl_s_ctrl(sd->jpegqual, jcomp->quality);
+	return 0;
+}
+
+static int sd_get_jcomp(struct gspca_dev *gspca_dev,
+			struct v4l2_jpegcompression *jcomp)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	memset(jcomp, 0, sizeof *jcomp);
+	jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual);
+	jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
+			| V4L2_JPEG_MARKER_DQT;
+	return 0;
+}
+
+
+/* sub-driver description */
+static const struct sd_desc sd_desc_sakar_57379 = {
+	.name   = MODULE_NAME,
+	.config = sd_config,
+	.init   = sd_init,
+	.start  = sd_start,
+	.stopN  = sd_stopN,
+	.pkt_scan = sd_pkt_scan,
+};
+
+/* sub-driver description */
+static const struct sd_desc sd_desc_sportscam_dv15 = {
+	.name   = MODULE_NAME,
+	.config = sd_config,
+	.init   = sd_init,
+	.init_controls = sd_init_controls,
+	.start  = sd_start,
+	.stopN  = sd_stopN,
+	.pkt_scan = sd_pkt_scan,
+	.get_jcomp = sd_get_jcomp,
+	.set_jcomp = sd_set_jcomp,
+};
+
+static const struct sd_desc *sd_desc[2] = {
+	&sd_desc_sakar_57379,
+	&sd_desc_sportscam_dv15
+};
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+		const struct usb_device_id *id)
+{
+	return gspca_dev_probe(intf, id,
+			sd_desc[id->driver_info],
+			sizeof(struct sd),
+			THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+	.name       = MODULE_NAME,
+	.id_table   = device_table,
+	.probe      = sd_probe,
+	.disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+	.suspend = gspca_suspend,
+	.resume  = gspca_resume,
+	.reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/usb/gspca/jl2005bcd.c b/drivers/media/usb/gspca/jl2005bcd.c
new file mode 100644
index 0000000..5b481fa
--- /dev/null
+++ b/drivers/media/usb/gspca/jl2005bcd.c
@@ -0,0 +1,539 @@
+/*
+ * Jeilin JL2005B/C/D library
+ *
+ * Copyright (C) 2011 Theodore Kilgore <kilgota@auburn.edu>
+ *
+ * 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 "jl2005bcd"
+
+#include <linux/workqueue.h>
+#include <linux/slab.h>
+#include "gspca.h"
+
+
+MODULE_AUTHOR("Theodore Kilgore <kilgota@auburn.edu>");
+MODULE_DESCRIPTION("JL2005B/C/D USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* Default timeouts, in ms */
+#define JL2005C_CMD_TIMEOUT 500
+#define JL2005C_DATA_TIMEOUT 1000
+
+/* Maximum transfer size to use. */
+#define JL2005C_MAX_TRANSFER 0x200
+#define FRAME_HEADER_LEN 16
+
+
+/* specific webcam descriptor */
+struct sd {
+	struct gspca_dev gspca_dev;  /* !! must be the first item */
+	unsigned char firmware_id[6];
+	const struct v4l2_pix_format *cap_mode;
+	/* Driver stuff */
+	struct work_struct work_struct;
+	struct workqueue_struct *work_thread;
+	u8 frame_brightness;
+	int block_size;	/* block size of camera */
+	int vga;	/* 1 if vga cam, 0 if cif cam */
+};
+
+
+/* Camera has two resolution settings. What they are depends on model. */
+static const struct v4l2_pix_format cif_mode[] = {
+	{176, 144, V4L2_PIX_FMT_JL2005BCD, V4L2_FIELD_NONE,
+		.bytesperline = 176,
+		.sizeimage = 176 * 144,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 0},
+	{352, 288, V4L2_PIX_FMT_JL2005BCD, V4L2_FIELD_NONE,
+		.bytesperline = 352,
+		.sizeimage = 352 * 288,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 0},
+};
+
+static const struct v4l2_pix_format vga_mode[] = {
+	{320, 240, V4L2_PIX_FMT_JL2005BCD, V4L2_FIELD_NONE,
+		.bytesperline = 320,
+		.sizeimage = 320 * 240,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 0},
+	{640, 480, V4L2_PIX_FMT_JL2005BCD, V4L2_FIELD_NONE,
+		.bytesperline = 640,
+		.sizeimage = 640 * 480,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 0},
+};
+
+/*
+ * cam uses endpoint 0x03 to send commands, 0x84 for read commands,
+ * and 0x82 for bulk data transfer.
+ */
+
+/* All commands are two bytes only */
+static int jl2005c_write2(struct gspca_dev *gspca_dev, unsigned char *command)
+{
+	int retval;
+
+	memcpy(gspca_dev->usb_buf, command, 2);
+	retval = usb_bulk_msg(gspca_dev->dev,
+			usb_sndbulkpipe(gspca_dev->dev, 3),
+			gspca_dev->usb_buf, 2, NULL, 500);
+	if (retval < 0)
+		pr_err("command write [%02x] error %d\n",
+		       gspca_dev->usb_buf[0], retval);
+	return retval;
+}
+
+/* Response to a command is one byte in usb_buf[0], only if requested. */
+static int jl2005c_read1(struct gspca_dev *gspca_dev)
+{
+	int retval;
+
+	retval = usb_bulk_msg(gspca_dev->dev,
+				usb_rcvbulkpipe(gspca_dev->dev, 0x84),
+				gspca_dev->usb_buf, 1, NULL, 500);
+	if (retval < 0)
+		pr_err("read command [0x%02x] error %d\n",
+		       gspca_dev->usb_buf[0], retval);
+	return retval;
+}
+
+/* Response appears in gspca_dev->usb_buf[0] */
+static int jl2005c_read_reg(struct gspca_dev *gspca_dev, unsigned char reg)
+{
+	int retval;
+
+	static u8 instruction[2] = {0x95, 0x00};
+	/* put register to read in byte 1 */
+	instruction[1] = reg;
+	/* Send the read request */
+	retval = jl2005c_write2(gspca_dev, instruction);
+	if (retval < 0)
+		return retval;
+	retval = jl2005c_read1(gspca_dev);
+
+	return retval;
+}
+
+static int jl2005c_start_new_frame(struct gspca_dev *gspca_dev)
+{
+	int i;
+	int retval;
+	int frame_brightness = 0;
+
+	static u8 instruction[2] = {0x7f, 0x01};
+
+	retval = jl2005c_write2(gspca_dev, instruction);
+	if (retval < 0)
+		return retval;
+
+	i = 0;
+	while (i < 20 && !frame_brightness) {
+		/* If we tried 20 times, give up. */
+		retval = jl2005c_read_reg(gspca_dev, 0x7e);
+		if (retval < 0)
+			return retval;
+		frame_brightness = gspca_dev->usb_buf[0];
+		retval = jl2005c_read_reg(gspca_dev, 0x7d);
+		if (retval < 0)
+			return retval;
+		i++;
+	}
+	PDEBUG(D_FRAM, "frame_brightness is 0x%02x", gspca_dev->usb_buf[0]);
+	return retval;
+}
+
+static int jl2005c_write_reg(struct gspca_dev *gspca_dev, unsigned char reg,
+						    unsigned char value)
+{
+	int retval;
+	u8 instruction[2];
+
+	instruction[0] = reg;
+	instruction[1] = value;
+
+	retval = jl2005c_write2(gspca_dev, instruction);
+	if (retval < 0)
+			return retval;
+
+	return retval;
+}
+
+static int jl2005c_get_firmware_id(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *)gspca_dev;
+	int i = 0;
+	int retval = -1;
+	unsigned char regs_to_read[] = {0x57, 0x02, 0x03, 0x5d, 0x5e, 0x5f};
+
+	PDEBUG(D_PROBE, "Running jl2005c_get_firmware_id");
+	/* Read the first ID byte once for warmup */
+	retval = jl2005c_read_reg(gspca_dev, regs_to_read[0]);
+	PDEBUG(D_PROBE, "response is %02x", gspca_dev->usb_buf[0]);
+	if (retval < 0)
+		return retval;
+	/* Now actually get the ID string */
+	for (i = 0; i < 6; i++) {
+		retval = jl2005c_read_reg(gspca_dev, regs_to_read[i]);
+		if (retval < 0)
+			return retval;
+		sd->firmware_id[i] = gspca_dev->usb_buf[0];
+	}
+	PDEBUG(D_PROBE, "firmware ID is %02x%02x%02x%02x%02x%02x",
+						sd->firmware_id[0],
+						sd->firmware_id[1],
+						sd->firmware_id[2],
+						sd->firmware_id[3],
+						sd->firmware_id[4],
+						sd->firmware_id[5]);
+	return 0;
+}
+
+static int jl2005c_stream_start_vga_lg
+		    (struct gspca_dev *gspca_dev)
+{
+	int i;
+	int retval = -1;
+	static u8 instruction[][2] = {
+		{0x05, 0x00},
+		{0x7c, 0x00},
+		{0x7d, 0x18},
+		{0x02, 0x00},
+		{0x01, 0x00},
+		{0x04, 0x52},
+	};
+
+	for (i = 0; i < ARRAY_SIZE(instruction); i++) {
+		msleep(60);
+		retval = jl2005c_write2(gspca_dev, instruction[i]);
+		if (retval < 0)
+			return retval;
+	}
+	msleep(60);
+	return retval;
+}
+
+static int jl2005c_stream_start_vga_small(struct gspca_dev *gspca_dev)
+{
+	int i;
+	int retval = -1;
+	static u8 instruction[][2] = {
+		{0x06, 0x00},
+		{0x7c, 0x00},
+		{0x7d, 0x1a},
+		{0x02, 0x00},
+		{0x01, 0x00},
+		{0x04, 0x52},
+	};
+
+	for (i = 0; i < ARRAY_SIZE(instruction); i++) {
+		msleep(60);
+		retval = jl2005c_write2(gspca_dev, instruction[i]);
+		if (retval < 0)
+			return retval;
+	}
+	msleep(60);
+	return retval;
+}
+
+static int jl2005c_stream_start_cif_lg(struct gspca_dev *gspca_dev)
+{
+	int i;
+	int retval = -1;
+	static u8 instruction[][2] = {
+		{0x05, 0x00},
+		{0x7c, 0x00},
+		{0x7d, 0x30},
+		{0x02, 0x00},
+		{0x01, 0x00},
+		{0x04, 0x42},
+	};
+
+	for (i = 0; i < ARRAY_SIZE(instruction); i++) {
+		msleep(60);
+		retval = jl2005c_write2(gspca_dev, instruction[i]);
+		if (retval < 0)
+			return retval;
+	}
+	msleep(60);
+	return retval;
+}
+
+static int jl2005c_stream_start_cif_small(struct gspca_dev *gspca_dev)
+{
+	int i;
+	int retval = -1;
+	static u8 instruction[][2] = {
+		{0x06, 0x00},
+		{0x7c, 0x00},
+		{0x7d, 0x32},
+		{0x02, 0x00},
+		{0x01, 0x00},
+		{0x04, 0x42},
+	};
+
+	for (i = 0; i < ARRAY_SIZE(instruction); i++) {
+		msleep(60);
+		retval = jl2005c_write2(gspca_dev, instruction[i]);
+		if (retval < 0)
+			return retval;
+	}
+	msleep(60);
+	return retval;
+}
+
+
+static int jl2005c_stop(struct gspca_dev *gspca_dev)
+{
+	int retval;
+
+	retval = jl2005c_write_reg(gspca_dev, 0x07, 0x00);
+	return retval;
+}
+
+/*
+ * This function is called as a workqueue function and runs whenever the camera
+ * is streaming data. Because it is a workqueue function it is allowed to sleep
+ * so we can use synchronous USB calls. To avoid possible collisions with other
+ * threads attempting to use gspca_dev->usb_buf we take the usb_lock when
+ * performing USB operations using it. In practice we don't really need this
+ * as the camera doesn't provide any controls.
+ */
+static void jl2005c_dostream(struct work_struct *work)
+{
+	struct sd *dev = container_of(work, struct sd, work_struct);
+	struct gspca_dev *gspca_dev = &dev->gspca_dev;
+	int bytes_left = 0; /* bytes remaining in current frame. */
+	int data_len;   /* size to use for the next read. */
+	int header_read = 0;
+	unsigned char header_sig[2] = {0x4a, 0x4c};
+	int act_len;
+	int packet_type;
+	int ret;
+	u8 *buffer;
+
+	buffer = kmalloc(JL2005C_MAX_TRANSFER, GFP_KERNEL | GFP_DMA);
+	if (!buffer) {
+		pr_err("Couldn't allocate USB buffer\n");
+		goto quit_stream;
+	}
+
+	while (gspca_dev->present && gspca_dev->streaming) {
+#ifdef CONFIG_PM
+		if (gspca_dev->frozen)
+			break;
+#endif
+		/* Check if this is a new frame. If so, start the frame first */
+		if (!header_read) {
+			mutex_lock(&gspca_dev->usb_lock);
+			ret = jl2005c_start_new_frame(gspca_dev);
+			mutex_unlock(&gspca_dev->usb_lock);
+			if (ret < 0)
+				goto quit_stream;
+			ret = usb_bulk_msg(gspca_dev->dev,
+				usb_rcvbulkpipe(gspca_dev->dev, 0x82),
+				buffer, JL2005C_MAX_TRANSFER, &act_len,
+				JL2005C_DATA_TIMEOUT);
+			PDEBUG(D_PACK,
+				"Got %d bytes out of %d for header",
+					act_len, JL2005C_MAX_TRANSFER);
+			if (ret < 0 || act_len < JL2005C_MAX_TRANSFER)
+				goto quit_stream;
+			/* Check whether we actually got the first blodk */
+			if (memcmp(header_sig, buffer, 2) != 0) {
+				pr_err("First block is not the first block\n");
+				goto quit_stream;
+			}
+			/* total size to fetch is byte 7, times blocksize
+			 * of which we already got act_len */
+			bytes_left = buffer[0x07] * dev->block_size - act_len;
+			PDEBUG(D_PACK, "bytes_left = 0x%x", bytes_left);
+			/* We keep the header. It has other information, too.*/
+			packet_type = FIRST_PACKET;
+			gspca_frame_add(gspca_dev, packet_type,
+					buffer, act_len);
+			header_read = 1;
+		}
+		while (bytes_left > 0 && gspca_dev->present) {
+			data_len = bytes_left > JL2005C_MAX_TRANSFER ?
+				JL2005C_MAX_TRANSFER : bytes_left;
+			ret = usb_bulk_msg(gspca_dev->dev,
+				usb_rcvbulkpipe(gspca_dev->dev, 0x82),
+				buffer, data_len, &act_len,
+				JL2005C_DATA_TIMEOUT);
+			if (ret < 0 || act_len < data_len)
+				goto quit_stream;
+			PDEBUG(D_PACK,
+				"Got %d bytes out of %d for frame",
+						data_len, bytes_left);
+			bytes_left -= data_len;
+			if (bytes_left == 0) {
+				packet_type = LAST_PACKET;
+				header_read = 0;
+			} else
+				packet_type = INTER_PACKET;
+			gspca_frame_add(gspca_dev, packet_type,
+					buffer, data_len);
+		}
+	}
+quit_stream:
+	if (gspca_dev->present) {
+		mutex_lock(&gspca_dev->usb_lock);
+		jl2005c_stop(gspca_dev);
+		mutex_unlock(&gspca_dev->usb_lock);
+	}
+	kfree(buffer);
+}
+
+
+
+
+/* This function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+			const struct usb_device_id *id)
+{
+	struct cam *cam;
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	cam = &gspca_dev->cam;
+	/* We don't use the buffer gspca allocates so make it small. */
+	cam->bulk_size = 64;
+	cam->bulk = 1;
+	/* For the rest, the camera needs to be detected */
+	jl2005c_get_firmware_id(gspca_dev);
+	/* Here are some known firmware IDs
+	 * First some JL2005B cameras
+	 * {0x41, 0x07, 0x04, 0x2c, 0xe8, 0xf2}	Sakar KidzCam
+	 * {0x45, 0x02, 0x08, 0xb9, 0x00, 0xd2}	No-name JL2005B
+	 * JL2005C cameras
+	 * {0x01, 0x0c, 0x16, 0x10, 0xf8, 0xc8}	Argus DC-1512
+	 * {0x12, 0x04, 0x03, 0xc0, 0x00, 0xd8}	ICarly
+	 * {0x86, 0x08, 0x05, 0x02, 0x00, 0xd4}	Jazz
+	 *
+	 * Based upon this scanty evidence, we can detect a CIF camera by
+	 * testing byte 0 for 0x4x.
+	 */
+	if ((sd->firmware_id[0] & 0xf0) == 0x40) {
+		cam->cam_mode	= cif_mode;
+		cam->nmodes	= ARRAY_SIZE(cif_mode);
+		sd->block_size	= 0x80;
+	} else {
+		cam->cam_mode	= vga_mode;
+		cam->nmodes	= ARRAY_SIZE(vga_mode);
+		sd->block_size	= 0x200;
+	}
+
+	INIT_WORK(&sd->work_struct, jl2005c_dostream);
+
+	return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+	return 0;
+}
+
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+
+	struct sd *sd = (struct sd *) gspca_dev;
+	sd->cap_mode = gspca_dev->cam.cam_mode;
+
+	switch (gspca_dev->pixfmt.width) {
+	case 640:
+		PDEBUG(D_STREAM, "Start streaming at vga resolution");
+		jl2005c_stream_start_vga_lg(gspca_dev);
+		break;
+	case 320:
+		PDEBUG(D_STREAM, "Start streaming at qvga resolution");
+		jl2005c_stream_start_vga_small(gspca_dev);
+		break;
+	case 352:
+		PDEBUG(D_STREAM, "Start streaming at cif resolution");
+		jl2005c_stream_start_cif_lg(gspca_dev);
+		break;
+	case 176:
+		PDEBUG(D_STREAM, "Start streaming at qcif resolution");
+		jl2005c_stream_start_cif_small(gspca_dev);
+		break;
+	default:
+		pr_err("Unknown resolution specified\n");
+		return -1;
+	}
+
+	/* Start the workqueue function to do the streaming */
+	sd->work_thread = create_singlethread_workqueue(MODULE_NAME);
+	queue_work(sd->work_thread, &sd->work_struct);
+
+	return 0;
+}
+
+/* called on streamoff with alt==0 and on disconnect */
+/* the usb_lock is held at entry - restore on exit */
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+	struct sd *dev = (struct sd *) gspca_dev;
+
+	/* wait for the work queue to terminate */
+	mutex_unlock(&gspca_dev->usb_lock);
+	/* This waits for sq905c_dostream to finish */
+	destroy_workqueue(dev->work_thread);
+	dev->work_thread = NULL;
+	mutex_lock(&gspca_dev->usb_lock);
+}
+
+
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+	.name = MODULE_NAME,
+	.config = sd_config,
+	.init = sd_init,
+	.start = sd_start,
+	.stop0 = sd_stop0,
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+	{USB_DEVICE(0x0979, 0x0227)},
+	{}
+};
+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,
+#ifdef CONFIG_PM
+	.suspend = gspca_suspend,
+	.resume = gspca_resume,
+	.reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/usb/gspca/jpeg.h b/drivers/media/usb/gspca/jpeg.h
new file mode 100644
index 0000000..0aa2b67
--- /dev/null
+++ b/drivers/media/usb/gspca/jpeg.h
@@ -0,0 +1,170 @@
+#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
+ *
+ */
+
+/*
+ * generation options
+ *	CONEX_CAM	Conexant if present
+ */
+
+/* JPEG header */
+static const u8 jpeg_head[] = {
+	0xff, 0xd8,			/* jpeg */
+
+/* quantization table quality 50% */
+	0xff, 0xdb, 0x00, 0x84,		/* DQT */
+0,
+#define JPEG_QT0_OFFSET 7
+	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,
+1,
+#define JPEG_QT1_OFFSET 72
+	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,
+
+/* huffman table */
+	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 */
+#define JPEG_HDR_SZ 556
+#else
+	0xff, 0xc0, 0x00, 0x11,		/* SOF0 (start of frame 0 */
+	0x08,				/* data precision */
+#define JPEG_HEIGHT_OFFSET 561
+	0x01, 0xe0,			/* height */
+	0x02, 0x80,			/* width */
+	0x03,				/* component number */
+		0x01,
+			0x21,		/* samples Y */
+			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
+#define JPEG_HDR_SZ 589
+#endif
+};
+
+/* define the JPEG header */
+static void jpeg_define(u8 *jpeg_hdr,
+			int height,
+			int width,
+			int samplesY)
+{
+	memcpy(jpeg_hdr, jpeg_head, sizeof jpeg_head);
+#ifndef CONEX_CAM
+	jpeg_hdr[JPEG_HEIGHT_OFFSET + 0] = height >> 8;
+	jpeg_hdr[JPEG_HEIGHT_OFFSET + 1] = height;
+	jpeg_hdr[JPEG_HEIGHT_OFFSET + 2] = width >> 8;
+	jpeg_hdr[JPEG_HEIGHT_OFFSET + 3] = width;
+	jpeg_hdr[JPEG_HEIGHT_OFFSET + 6] = samplesY;
+#endif
+}
+
+/* set the JPEG quality */
+static void jpeg_set_qual(u8 *jpeg_hdr,
+			  int quality)
+{
+	int i, sc;
+
+	if (quality <= 0)
+		sc = 5000;
+	else if (quality < 50)
+		sc = 5000 / quality;
+	else
+		sc = 200 - quality * 2;
+	for (i = 0; i < 64; i++) {
+		jpeg_hdr[JPEG_QT0_OFFSET + i] =
+			(jpeg_head[JPEG_QT0_OFFSET + i] * sc + 50) / 100;
+		jpeg_hdr[JPEG_QT1_OFFSET + i] =
+			(jpeg_head[JPEG_QT1_OFFSET + i] * sc + 50) / 100;
+	}
+}
+#endif
diff --git a/drivers/media/usb/gspca/kinect.c b/drivers/media/usb/gspca/kinect.c
new file mode 100644
index 0000000..3cb30a3
--- /dev/null
+++ b/drivers/media/usb/gspca/kinect.c
@@ -0,0 +1,486 @@
+/*
+ * kinect sensor device camera, gspca driver
+ *
+ * Copyright (C) 2011  Antonio Ospite <ospite@studenti.unina.it>
+ *
+ * Based on the OpenKinect project and libfreenect
+ * http://openkinect.org/wiki/Init_Analysis
+ *
+ * Special thanks to Steven Toth and kernellabs.com for sponsoring a Kinect
+ * sensor device which I tested the driver on.
+ *
+ * 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "kinect"
+
+#include "gspca.h"
+
+#define CTRL_TIMEOUT 500
+
+MODULE_AUTHOR("Antonio Ospite <ospite@studenti.unina.it>");
+MODULE_DESCRIPTION("GSPCA/Kinect Sensor Device USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+static bool depth_mode;
+
+struct pkt_hdr {
+	uint8_t magic[2];
+	uint8_t pad;
+	uint8_t flag;
+	uint8_t unk1;
+	uint8_t seq;
+	uint8_t unk2;
+	uint8_t unk3;
+	uint32_t timestamp;
+};
+
+struct cam_hdr {
+	uint8_t magic[2];
+	__le16 len;
+	__le16 cmd;
+	__le16 tag;
+};
+
+/* specific webcam descriptor */
+struct sd {
+	struct gspca_dev gspca_dev; /* !! must be the first item */
+	uint16_t cam_tag;           /* a sequence number for packets */
+	uint8_t stream_flag;        /* to identify different stream types */
+	uint8_t obuf[0x400];        /* output buffer for control commands */
+	uint8_t ibuf[0x200];        /* input buffer for control commands */
+};
+
+#define MODE_640x480   0x0001
+#define MODE_640x488   0x0002
+#define MODE_1280x1024 0x0004
+
+#define FORMAT_BAYER   0x0010
+#define FORMAT_UYVY    0x0020
+#define FORMAT_Y10B    0x0040
+
+#define FPS_HIGH       0x0100
+
+static const struct v4l2_pix_format depth_camera_mode[] = {
+	{640, 480, V4L2_PIX_FMT_Y10BPACK, V4L2_FIELD_NONE,
+	 .bytesperline = 640 * 10 / 8,
+	 .sizeimage =  640 * 480 * 10 / 8,
+	 .colorspace = V4L2_COLORSPACE_SRGB,
+	 .priv = MODE_640x488 | FORMAT_Y10B},
+};
+
+static const struct v4l2_pix_format video_camera_mode[] = {
+	{640, 480, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE,
+	 .bytesperline = 640,
+	 .sizeimage = 640 * 480,
+	 .colorspace = V4L2_COLORSPACE_SRGB,
+	 .priv = MODE_640x480 | FORMAT_BAYER | FPS_HIGH},
+	{640, 480, V4L2_PIX_FMT_UYVY, V4L2_FIELD_NONE,
+	 .bytesperline = 640 * 2,
+	 .sizeimage = 640 * 480 * 2,
+	 .colorspace = V4L2_COLORSPACE_SRGB,
+	 .priv = MODE_640x480 | FORMAT_UYVY},
+	{1280, 1024, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE,
+	 .bytesperline = 1280,
+	 .sizeimage = 1280 * 1024,
+	 .colorspace = V4L2_COLORSPACE_SRGB,
+	 .priv = MODE_1280x1024 | FORMAT_BAYER},
+	{640, 488, V4L2_PIX_FMT_Y10BPACK, V4L2_FIELD_NONE,
+	 .bytesperline = 640 * 10 / 8,
+	 .sizeimage =  640 * 488 * 10 / 8,
+	 .colorspace = V4L2_COLORSPACE_SRGB,
+	 .priv = MODE_640x488 | FORMAT_Y10B | FPS_HIGH},
+	{1280, 1024, V4L2_PIX_FMT_Y10BPACK, V4L2_FIELD_NONE,
+	 .bytesperline = 1280 * 10 / 8,
+	 .sizeimage =  1280 * 1024 * 10 / 8,
+	 .colorspace = V4L2_COLORSPACE_SRGB,
+	 .priv = MODE_1280x1024 | FORMAT_Y10B},
+};
+
+static int kinect_write(struct usb_device *udev, uint8_t *data,
+			uint16_t wLength)
+{
+	return usb_control_msg(udev,
+			      usb_sndctrlpipe(udev, 0),
+			      0x00,
+			      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			      0, 0, data, wLength, CTRL_TIMEOUT);
+}
+
+static int kinect_read(struct usb_device *udev, uint8_t *data, uint16_t wLength)
+{
+	return usb_control_msg(udev,
+			      usb_rcvctrlpipe(udev, 0),
+			      0x00,
+			      USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			      0, 0, data, wLength, CTRL_TIMEOUT);
+}
+
+static int send_cmd(struct gspca_dev *gspca_dev, uint16_t cmd, void *cmdbuf,
+		unsigned int cmd_len, void *replybuf, unsigned int reply_len)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct usb_device *udev = gspca_dev->dev;
+	int res, actual_len;
+	uint8_t *obuf = sd->obuf;
+	uint8_t *ibuf = sd->ibuf;
+	struct cam_hdr *chdr = (void *)obuf;
+	struct cam_hdr *rhdr = (void *)ibuf;
+
+	if (cmd_len & 1 || cmd_len > (0x400 - sizeof(*chdr))) {
+		pr_err("send_cmd: Invalid command length (0x%x)\n", cmd_len);
+		return -1;
+	}
+
+	chdr->magic[0] = 0x47;
+	chdr->magic[1] = 0x4d;
+	chdr->cmd = cpu_to_le16(cmd);
+	chdr->tag = cpu_to_le16(sd->cam_tag);
+	chdr->len = cpu_to_le16(cmd_len / 2);
+
+	memcpy(obuf+sizeof(*chdr), cmdbuf, cmd_len);
+
+	res = kinect_write(udev, obuf, cmd_len + sizeof(*chdr));
+	PDEBUG(D_USBO, "Control cmd=%04x tag=%04x len=%04x: %d", cmd,
+		sd->cam_tag, cmd_len, res);
+	if (res < 0) {
+		pr_err("send_cmd: Output control transfer failed (%d)\n", res);
+		return res;
+	}
+
+	do {
+		actual_len = kinect_read(udev, ibuf, 0x200);
+	} while (actual_len == 0);
+	PDEBUG(D_USBO, "Control reply: %d", actual_len);
+	if (actual_len < sizeof(*rhdr)) {
+		pr_err("send_cmd: Input control transfer failed (%d)\n",
+		       actual_len);
+		return actual_len < 0 ? actual_len : -EREMOTEIO;
+	}
+	actual_len -= sizeof(*rhdr);
+
+	if (rhdr->magic[0] != 0x52 || rhdr->magic[1] != 0x42) {
+		pr_err("send_cmd: Bad magic %02x %02x\n",
+		       rhdr->magic[0], rhdr->magic[1]);
+		return -1;
+	}
+	if (rhdr->cmd != chdr->cmd) {
+		pr_err("send_cmd: Bad cmd %02x != %02x\n",
+		       rhdr->cmd, chdr->cmd);
+		return -1;
+	}
+	if (rhdr->tag != chdr->tag) {
+		pr_err("send_cmd: Bad tag %04x != %04x\n",
+		       rhdr->tag, chdr->tag);
+		return -1;
+	}
+	if (le16_to_cpu(rhdr->len) != (actual_len/2)) {
+		pr_err("send_cmd: Bad len %04x != %04x\n",
+		       le16_to_cpu(rhdr->len), (int)(actual_len/2));
+		return -1;
+	}
+
+	if (actual_len > reply_len) {
+		pr_warn("send_cmd: Data buffer is %d bytes long, but got %d bytes\n",
+			reply_len, actual_len);
+		memcpy(replybuf, ibuf+sizeof(*rhdr), reply_len);
+	} else {
+		memcpy(replybuf, ibuf+sizeof(*rhdr), actual_len);
+	}
+
+	sd->cam_tag++;
+
+	return actual_len;
+}
+
+static int write_register(struct gspca_dev *gspca_dev, uint16_t reg,
+			uint16_t data)
+{
+	uint16_t reply[2];
+	__le16 cmd[2];
+	int res;
+
+	cmd[0] = cpu_to_le16(reg);
+	cmd[1] = cpu_to_le16(data);
+
+	PDEBUG(D_USBO, "Write Reg 0x%04x <= 0x%02x", reg, data);
+	res = send_cmd(gspca_dev, 0x03, cmd, 4, reply, 4);
+	if (res < 0)
+		return res;
+	if (res != 2) {
+		pr_warn("send_cmd returned %d [%04x %04x], 0000 expected\n",
+			res, reply[0], reply[1]);
+	}
+	return 0;
+}
+
+/* this function is called at probe time */
+static int sd_config_video(struct gspca_dev *gspca_dev,
+		     const struct usb_device_id *id)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct cam *cam;
+
+	sd->cam_tag = 0;
+
+	sd->stream_flag = 0x80;
+
+	cam = &gspca_dev->cam;
+
+	cam->cam_mode = video_camera_mode;
+	cam->nmodes = ARRAY_SIZE(video_camera_mode);
+
+	gspca_dev->xfer_ep = 0x81;
+
+#if 0
+	/* Setting those values is not needed for video stream */
+	cam->npkt = 15;
+	gspca_dev->pkt_size = 960 * 2;
+#endif
+
+	return 0;
+}
+
+static int sd_config_depth(struct gspca_dev *gspca_dev,
+		     const struct usb_device_id *id)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct cam *cam;
+
+	sd->cam_tag = 0;
+
+	sd->stream_flag = 0x70;
+
+	cam = &gspca_dev->cam;
+
+	cam->cam_mode = depth_camera_mode;
+	cam->nmodes = ARRAY_SIZE(depth_camera_mode);
+
+	gspca_dev->xfer_ep = 0x82;
+
+	return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+	PDEBUG(D_PROBE, "Kinect Camera device.");
+
+	return 0;
+}
+
+static int sd_start_video(struct gspca_dev *gspca_dev)
+{
+	int mode;
+	uint8_t fmt_reg, fmt_val;
+	uint8_t res_reg, res_val;
+	uint8_t fps_reg, fps_val;
+	uint8_t mode_val;
+
+	mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
+
+	if (mode & FORMAT_Y10B) {
+		fmt_reg = 0x19;
+		res_reg = 0x1a;
+		fps_reg = 0x1b;
+		mode_val = 0x03;
+	} else {
+		fmt_reg = 0x0c;
+		res_reg = 0x0d;
+		fps_reg = 0x0e;
+		mode_val = 0x01;
+	}
+
+	/* format */
+	if (mode & FORMAT_UYVY)
+		fmt_val = 0x05;
+	else
+		fmt_val = 0x00;
+
+	if (mode & MODE_1280x1024)
+		res_val = 0x02;
+	else
+		res_val = 0x01;
+
+	if (mode & FPS_HIGH)
+		fps_val = 0x1e;
+	else
+		fps_val = 0x0f;
+
+
+	/* turn off IR-reset function */
+	write_register(gspca_dev, 0x105, 0x00);
+
+	/* Reset video stream */
+	write_register(gspca_dev, 0x05, 0x00);
+
+	/* Due to some ridiculous condition in the firmware, we have to start
+	 * and stop the depth stream before the camera will hand us 1280x1024
+	 * IR.  This is a stupid workaround, but we've yet to find a better
+	 * solution.
+	 *
+	 * Thanks to Drew Fisher for figuring this out.
+	 */
+	if (mode & (FORMAT_Y10B | MODE_1280x1024)) {
+		write_register(gspca_dev, 0x13, 0x01);
+		write_register(gspca_dev, 0x14, 0x1e);
+		write_register(gspca_dev, 0x06, 0x02);
+		write_register(gspca_dev, 0x06, 0x00);
+	}
+
+	write_register(gspca_dev, fmt_reg, fmt_val);
+	write_register(gspca_dev, res_reg, res_val);
+	write_register(gspca_dev, fps_reg, fps_val);
+
+	/* Start video stream */
+	write_register(gspca_dev, 0x05, mode_val);
+
+	/* disable Hflip */
+	write_register(gspca_dev, 0x47, 0x00);
+
+	return 0;
+}
+
+static int sd_start_depth(struct gspca_dev *gspca_dev)
+{
+	/* turn off IR-reset function */
+	write_register(gspca_dev, 0x105, 0x00);
+
+	/* reset depth stream */
+	write_register(gspca_dev, 0x06, 0x00);
+	/* Depth Stream Format 0x03: 11 bit stream | 0x02: 10 bit */
+	write_register(gspca_dev, 0x12, 0x02);
+	/* Depth Stream Resolution 1: standard (640x480) */
+	write_register(gspca_dev, 0x13, 0x01);
+	/* Depth Framerate / 0x1e (30): 30 fps */
+	write_register(gspca_dev, 0x14, 0x1e);
+	/* Depth Stream Control  / 2: Open Depth Stream */
+	write_register(gspca_dev, 0x06, 0x02);
+	/* disable depth hflip / LSB = 0: Smoothing Disabled */
+	write_register(gspca_dev, 0x17, 0x00);
+
+	return 0;
+}
+
+static void sd_stopN_video(struct gspca_dev *gspca_dev)
+{
+	/* reset video stream */
+	write_register(gspca_dev, 0x05, 0x00);
+}
+
+static void sd_stopN_depth(struct gspca_dev *gspca_dev)
+{
+	/* reset depth stream */
+	write_register(gspca_dev, 0x06, 0x00);
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev, u8 *__data, int len)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	struct pkt_hdr *hdr = (void *)__data;
+	uint8_t *data = __data + sizeof(*hdr);
+	int datalen = len - sizeof(*hdr);
+
+	uint8_t sof = sd->stream_flag | 1;
+	uint8_t mof = sd->stream_flag | 2;
+	uint8_t eof = sd->stream_flag | 5;
+
+	if (len < 12)
+		return;
+
+	if (hdr->magic[0] != 'R' || hdr->magic[1] != 'B') {
+		pr_warn("[Stream %02x] Invalid magic %02x%02x\n",
+			sd->stream_flag, hdr->magic[0], hdr->magic[1]);
+		return;
+	}
+
+	if (hdr->flag == sof)
+		gspca_frame_add(gspca_dev, FIRST_PACKET, data, datalen);
+
+	else if (hdr->flag == mof)
+		gspca_frame_add(gspca_dev, INTER_PACKET, data, datalen);
+
+	else if (hdr->flag == eof)
+		gspca_frame_add(gspca_dev, LAST_PACKET, data, datalen);
+
+	else
+		pr_warn("Packet type not recognized...\n");
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc_video = {
+	.name      = MODULE_NAME,
+	.config    = sd_config_video,
+	.init      = sd_init,
+	.start     = sd_start_video,
+	.stopN     = sd_stopN_video,
+	.pkt_scan  = sd_pkt_scan,
+	/*
+	.get_streamparm = sd_get_streamparm,
+	.set_streamparm = sd_set_streamparm,
+	*/
+};
+static const struct sd_desc sd_desc_depth = {
+	.name      = MODULE_NAME,
+	.config    = sd_config_depth,
+	.init      = sd_init,
+	.start     = sd_start_depth,
+	.stopN     = sd_stopN_depth,
+	.pkt_scan  = sd_pkt_scan,
+	/*
+	.get_streamparm = sd_get_streamparm,
+	.set_streamparm = sd_set_streamparm,
+	*/
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+	{USB_DEVICE(0x045e, 0x02ae)},
+	{USB_DEVICE(0x045e, 0x02bf)},
+	{}
+};
+
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+	if (depth_mode)
+		return gspca_dev_probe(intf, id, &sd_desc_depth,
+				       sizeof(struct sd), THIS_MODULE);
+	else
+		return gspca_dev_probe(intf, id, &sd_desc_video,
+				       sizeof(struct sd), THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+	.name       = MODULE_NAME,
+	.id_table   = device_table,
+	.probe      = sd_probe,
+	.disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+	.suspend    = gspca_suspend,
+	.resume     = gspca_resume,
+	.reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
+
+module_param(depth_mode, bool, 0644);
+MODULE_PARM_DESC(depth_mode, "0=video 1=depth");
diff --git a/drivers/media/usb/gspca/konica.c b/drivers/media/usb/gspca/konica.c
new file mode 100644
index 0000000..0f6d57f
--- /dev/null
+++ b/drivers/media/usb/gspca/konica.c
@@ -0,0 +1,486 @@
+/*
+ * Driver for USB webcams based on Konica chipset. This
+ * chipset is used in Intel YC76 camera.
+ *
+ * Copyright (C) 2010 Hans de Goede <hdegoede@redhat.com>
+ *
+ * Based on the usbvideo v4l1 konicawc driver which is:
+ *
+ * Copyright (C) 2002 Simon Evans <spse@secret.org.uk>
+ *
+ * The code for making gspca work with a webcam with 2 isoc endpoints was
+ * taken from the benq gspca subdriver which is:
+ *
+ * Copyright (C) 2009 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "konica"
+
+#include <linux/input.h>
+#include "gspca.h"
+
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_DESCRIPTION("Konica chipset USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+#define WHITEBAL_REG   0x01
+#define BRIGHTNESS_REG 0x02
+#define SHARPNESS_REG  0x03
+#define CONTRAST_REG   0x04
+#define SATURATION_REG 0x05
+
+/* specific webcam descriptor */
+struct sd {
+	struct gspca_dev gspca_dev;	/* !! must be the first item */
+	struct urb *last_data_urb;
+	u8 snapshot_pressed;
+};
+
+
+/* .priv is what goes to register 8 for this mode, known working values:
+   0x00 -> 176x144, cropped
+   0x01 -> 176x144, cropped
+   0x02 -> 176x144, cropped
+   0x03 -> 176x144, cropped
+   0x04 -> 176x144, binned
+   0x05 -> 320x240
+   0x06 -> 320x240
+   0x07 -> 160x120, cropped
+   0x08 -> 160x120, cropped
+   0x09 -> 160x120, binned (note has 136 lines)
+   0x0a -> 160x120, binned (note has 136 lines)
+   0x0b -> 160x120, cropped
+*/
+static const struct v4l2_pix_format vga_mode[] = {
+	{160, 120, V4L2_PIX_FMT_KONICA420, V4L2_FIELD_NONE,
+		.bytesperline = 160,
+		.sizeimage = 160 * 136 * 3 / 2 + 960,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 0x0a},
+	{176, 144, V4L2_PIX_FMT_KONICA420, V4L2_FIELD_NONE,
+		.bytesperline = 176,
+		.sizeimage = 176 * 144 * 3 / 2 + 960,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 0x04},
+	{320, 240, V4L2_PIX_FMT_KONICA420, V4L2_FIELD_NONE,
+		.bytesperline = 320,
+		.sizeimage = 320 * 240 * 3 / 2 + 960,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 0x05},
+};
+
+static void sd_isoc_irq(struct urb *urb);
+
+static void reg_w(struct gspca_dev *gspca_dev, u16 value, u16 index)
+{
+	struct usb_device *dev = gspca_dev->dev;
+	int ret;
+
+	if (gspca_dev->usb_err < 0)
+		return;
+	ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+			0x02,
+			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			value,
+			index,
+			NULL,
+			0,
+			1000);
+	if (ret < 0) {
+		pr_err("reg_w err writing %02x to %02x: %d\n",
+		       value, index, ret);
+		gspca_dev->usb_err = ret;
+	}
+}
+
+static void reg_r(struct gspca_dev *gspca_dev, u16 value, u16 index)
+{
+	struct usb_device *dev = gspca_dev->dev;
+	int ret;
+
+	if (gspca_dev->usb_err < 0)
+		return;
+	ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+			0x03,
+			USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			value,
+			index,
+			gspca_dev->usb_buf,
+			2,
+			1000);
+	if (ret < 0) {
+		pr_err("reg_r err %d\n", ret);
+		gspca_dev->usb_err = ret;
+	}
+}
+
+static void konica_stream_on(struct gspca_dev *gspca_dev)
+{
+	reg_w(gspca_dev, 1, 0x0b);
+}
+
+static void konica_stream_off(struct gspca_dev *gspca_dev)
+{
+	reg_w(gspca_dev, 0, 0x0b);
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+			const struct usb_device_id *id)
+{
+	gspca_dev->cam.cam_mode = vga_mode;
+	gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode);
+	gspca_dev->cam.no_urb_create = 1;
+
+	return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+	int i;
+
+	/*
+	 * The konica needs a freaking large time to "boot" (approx 6.5 sec.),
+	 * and does not want to be bothered while doing so :|
+	 * Register 0x10 counts from 1 - 3, with 3 being "ready"
+	 */
+	msleep(6000);
+	for (i = 0; i < 20; i++) {
+		reg_r(gspca_dev, 0, 0x10);
+		if (gspca_dev->usb_buf[0] == 3)
+			break;
+		msleep(100);
+	}
+	reg_w(gspca_dev, 0, 0x0d);
+
+	return gspca_dev->usb_err;
+}
+
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct urb *urb;
+	int i, n, packet_size;
+	struct usb_host_interface *alt;
+	struct usb_interface *intf;
+
+	intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface);
+	alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt);
+	if (!alt) {
+		pr_err("Couldn't get altsetting\n");
+		return -EIO;
+	}
+
+	if (alt->desc.bNumEndpoints < 2)
+		return -ENODEV;
+
+	packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize);
+
+	n = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
+	reg_w(gspca_dev, n, 0x08);
+
+	konica_stream_on(gspca_dev);
+
+	if (gspca_dev->usb_err)
+		return gspca_dev->usb_err;
+
+	/* create 4 URBs - 2 on endpoint 0x83 and 2 on 0x082 */
+#if MAX_NURBS < 4
+#error "Not enough URBs in the gspca table"
+#endif
+#define SD_NPKT 32
+	for (n = 0; n < 4; n++) {
+		i = n & 1 ? 0 : 1;
+		packet_size =
+			le16_to_cpu(alt->endpoint[i].desc.wMaxPacketSize);
+		urb = usb_alloc_urb(SD_NPKT, GFP_KERNEL);
+		if (!urb) {
+			pr_err("usb_alloc_urb failed\n");
+			return -ENOMEM;
+		}
+		gspca_dev->urb[n] = urb;
+		urb->transfer_buffer = usb_alloc_coherent(gspca_dev->dev,
+						packet_size * SD_NPKT,
+						GFP_KERNEL,
+						&urb->transfer_dma);
+		if (urb->transfer_buffer == NULL) {
+			pr_err("usb_buffer_alloc failed\n");
+			return -ENOMEM;
+		}
+
+		urb->dev = gspca_dev->dev;
+		urb->context = gspca_dev;
+		urb->transfer_buffer_length = packet_size * SD_NPKT;
+		urb->pipe = usb_rcvisocpipe(gspca_dev->dev,
+					n & 1 ? 0x81 : 0x82);
+		urb->transfer_flags = URB_ISO_ASAP
+					| URB_NO_TRANSFER_DMA_MAP;
+		urb->interval = 1;
+		urb->complete = sd_isoc_irq;
+		urb->number_of_packets = SD_NPKT;
+		for (i = 0; i < SD_NPKT; i++) {
+			urb->iso_frame_desc[i].length = packet_size;
+			urb->iso_frame_desc[i].offset = packet_size * i;
+		}
+	}
+
+	return 0;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd __maybe_unused = (struct sd *) gspca_dev;
+
+	konica_stream_off(gspca_dev);
+#if IS_ENABLED(CONFIG_INPUT)
+	/* Don't keep the button in the pressed state "forever" if it was
+	   pressed when streaming is stopped */
+	if (sd->snapshot_pressed) {
+		input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
+		input_sync(gspca_dev->input_dev);
+		sd->snapshot_pressed = 0;
+	}
+#endif
+}
+
+/* reception of an URB */
+static void sd_isoc_irq(struct urb *urb)
+{
+	struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context;
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct urb *data_urb, *status_urb;
+	u8 *data;
+	int i, st;
+
+	PDEBUG(D_PACK, "sd isoc irq");
+	if (!gspca_dev->streaming)
+		return;
+
+	if (urb->status != 0) {
+		if (urb->status == -ESHUTDOWN)
+			return;		/* disconnection */
+#ifdef CONFIG_PM
+		if (gspca_dev->frozen)
+			return;
+#endif
+		PERR("urb status: %d", urb->status);
+		st = usb_submit_urb(urb, GFP_ATOMIC);
+		if (st < 0)
+			pr_err("resubmit urb error %d\n", st);
+		return;
+	}
+
+	/* if this is a data URB (ep 0x82), wait */
+	if (urb->transfer_buffer_length > 32) {
+		sd->last_data_urb = urb;
+		return;
+	}
+
+	status_urb = urb;
+	data_urb   = sd->last_data_urb;
+	sd->last_data_urb = NULL;
+
+	if (!data_urb || data_urb->start_frame != status_urb->start_frame) {
+		PERR("lost sync on frames");
+		goto resubmit;
+	}
+
+	if (data_urb->number_of_packets != status_urb->number_of_packets) {
+		PERR("no packets does not match, data: %d, status: %d",
+		     data_urb->number_of_packets,
+		     status_urb->number_of_packets);
+		goto resubmit;
+	}
+
+	for (i = 0; i < status_urb->number_of_packets; i++) {
+		if (data_urb->iso_frame_desc[i].status ||
+		    status_urb->iso_frame_desc[i].status) {
+			PERR("pkt %d data-status %d, status-status %d", i,
+			     data_urb->iso_frame_desc[i].status,
+			     status_urb->iso_frame_desc[i].status);
+			gspca_dev->last_packet_type = DISCARD_PACKET;
+			continue;
+		}
+
+		if (status_urb->iso_frame_desc[i].actual_length != 1) {
+			PERR("bad status packet length %d",
+			     status_urb->iso_frame_desc[i].actual_length);
+			gspca_dev->last_packet_type = DISCARD_PACKET;
+			continue;
+		}
+
+		st = *((u8 *)status_urb->transfer_buffer
+				+ status_urb->iso_frame_desc[i].offset);
+
+		data = (u8 *)data_urb->transfer_buffer
+				+ data_urb->iso_frame_desc[i].offset;
+
+		/* st: 0x80-0xff: frame start with frame number (ie 0-7f)
+		 * otherwise:
+		 * bit 0 0: keep packet
+		 *	 1: drop packet (padding data)
+		 *
+		 * bit 4 0 button not clicked
+		 *       1 button clicked
+		 * button is used to `take a picture' (in software)
+		 */
+		if (st & 0x80) {
+			gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+			gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0);
+		} else {
+#if IS_ENABLED(CONFIG_INPUT)
+			u8 button_state = st & 0x40 ? 1 : 0;
+			if (sd->snapshot_pressed != button_state) {
+				input_report_key(gspca_dev->input_dev,
+						 KEY_CAMERA,
+						 button_state);
+				input_sync(gspca_dev->input_dev);
+				sd->snapshot_pressed = button_state;
+			}
+#endif
+			if (st & 0x01)
+				continue;
+		}
+		gspca_frame_add(gspca_dev, INTER_PACKET, data,
+				data_urb->iso_frame_desc[i].actual_length);
+	}
+
+resubmit:
+	if (data_urb) {
+		st = usb_submit_urb(data_urb, GFP_ATOMIC);
+		if (st < 0)
+			PERR("usb_submit_urb(data_urb) ret %d", st);
+	}
+	st = usb_submit_urb(status_urb, GFP_ATOMIC);
+	if (st < 0)
+		PERR("usb_submit_urb(status_urb) ret %d\n", st);
+}
+
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct gspca_dev *gspca_dev =
+		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+
+	gspca_dev->usb_err = 0;
+
+	if (!gspca_dev->streaming)
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_BRIGHTNESS:
+		konica_stream_off(gspca_dev);
+		reg_w(gspca_dev, ctrl->val, BRIGHTNESS_REG);
+		konica_stream_on(gspca_dev);
+		break;
+	case V4L2_CID_CONTRAST:
+		konica_stream_off(gspca_dev);
+		reg_w(gspca_dev, ctrl->val, CONTRAST_REG);
+		konica_stream_on(gspca_dev);
+		break;
+	case V4L2_CID_SATURATION:
+		konica_stream_off(gspca_dev);
+		reg_w(gspca_dev, ctrl->val, SATURATION_REG);
+		konica_stream_on(gspca_dev);
+		break;
+	case V4L2_CID_WHITE_BALANCE_TEMPERATURE:
+		konica_stream_off(gspca_dev);
+		reg_w(gspca_dev, ctrl->val, WHITEBAL_REG);
+		konica_stream_on(gspca_dev);
+		break;
+	case V4L2_CID_SHARPNESS:
+		konica_stream_off(gspca_dev);
+		reg_w(gspca_dev, ctrl->val, SHARPNESS_REG);
+		konica_stream_on(gspca_dev);
+		break;
+	}
+	return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+	.s_ctrl = sd_s_ctrl,
+};
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+	gspca_dev->vdev.ctrl_handler = hdl;
+	v4l2_ctrl_handler_init(hdl, 5);
+	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_BRIGHTNESS, 0, 9, 1, 4);
+	/* Needs to be verified */
+	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_CONTRAST, 0, 9, 1, 4);
+	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_SATURATION, 0, 9, 1, 4);
+	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_WHITE_BALANCE_TEMPERATURE,
+			0, 33, 1, 25);
+	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_SHARPNESS, 0, 9, 1, 4);
+
+	if (hdl->error) {
+		pr_err("Could not initialize controls\n");
+		return hdl->error;
+	}
+	return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+	.name = MODULE_NAME,
+	.config = sd_config,
+	.init = sd_init,
+	.init_controls = sd_init_controls,
+	.start = sd_start,
+	.stopN = sd_stopN,
+#if IS_ENABLED(CONFIG_INPUT)
+	.other_input = 1,
+#endif
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+	{USB_DEVICE(0x04c8, 0x0720)}, /* Intel YC 76 */
+	{}
+};
+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,
+#ifdef CONFIG_PM
+	.suspend = gspca_suspend,
+	.resume = gspca_resume,
+	.reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/usb/gspca/m5602/Kconfig b/drivers/media/usb/gspca/m5602/Kconfig
new file mode 100644
index 0000000..5a69016
--- /dev/null
+++ b/drivers/media/usb/gspca/m5602/Kconfig
@@ -0,0 +1,11 @@
+config USB_M5602
+	tristate "ALi USB m5602 Camera Driver"
+	depends on VIDEO_V4L2 && USB_GSPCA
+	help
+	  Say Y here if you want support for cameras based on the
+	  ALi m5602 connected to various image sensors.
+
+	  See <file:Documentation/video4linux/m5602.txt> for more info.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gspca_m5602.
diff --git a/drivers/media/usb/gspca/m5602/Makefile b/drivers/media/usb/gspca/m5602/Makefile
new file mode 100644
index 0000000..8e1fb5a
--- /dev/null
+++ b/drivers/media/usb/gspca/m5602/Makefile
@@ -0,0 +1,11 @@
+obj-$(CONFIG_USB_M5602) += gspca_m5602.o
+
+gspca_m5602-objs := m5602_core.o \
+		    m5602_ov9650.o \
+		    m5602_ov7660.o \
+		    m5602_mt9m111.o \
+		    m5602_po1030.o \
+		    m5602_s5k83a.o \
+		    m5602_s5k4aa.o
+
+ccflags-y += -I$(srctree)/drivers/media/usb/gspca
diff --git a/drivers/media/usb/gspca/m5602/m5602_bridge.h b/drivers/media/usb/gspca/m5602/m5602_bridge.h
new file mode 100644
index 0000000..19eb1a6
--- /dev/null
+++ b/drivers/media/usb/gspca/m5602/m5602_bridge.h
@@ -0,0 +1,180 @@
+/*
+ * USB Driver for ALi m5602 based webcams
+ *
+ * Copyright (C) 2008 Erik Andrén
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * 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.
+ *
+ */
+
+#ifndef M5602_BRIDGE_H_
+#define M5602_BRIDGE_H_
+
+#include <linux/slab.h>
+#include "gspca.h"
+
+#define MODULE_NAME "ALi m5602"
+
+/*****************************************************************************/
+
+#define M5602_XB_SENSOR_TYPE		0x00
+#define M5602_XB_SENSOR_CTRL		0x01
+#define M5602_XB_LINE_OF_FRAME_H	0x02
+#define M5602_XB_LINE_OF_FRAME_L	0x03
+#define M5602_XB_PIX_OF_LINE_H		0x04
+#define M5602_XB_PIX_OF_LINE_L		0x05
+#define M5602_XB_VSYNC_PARA		0x06
+#define M5602_XB_HSYNC_PARA		0x07
+#define M5602_XB_TEST_MODE_1		0x08
+#define M5602_XB_TEST_MODE_2		0x09
+#define M5602_XB_SIG_INI		0x0a
+#define M5602_XB_DS_PARA		0x0e
+#define M5602_XB_TRIG_PARA		0x0f
+#define M5602_XB_CLK_PD			0x10
+#define M5602_XB_MCU_CLK_CTRL		0x12
+#define M5602_XB_MCU_CLK_DIV		0x13
+#define M5602_XB_SEN_CLK_CTRL		0x14
+#define M5602_XB_SEN_CLK_DIV		0x15
+#define M5602_XB_AUD_CLK_CTRL		0x16
+#define M5602_XB_AUD_CLK_DIV		0x17
+#define M5602_OB_AC_LINK_STATE		0x22
+#define M5602_OB_PCM_SLOT_INDEX		0x24
+#define M5602_OB_GPIO_SLOT_INDEX	0x25
+#define M5602_OB_ACRX_STATUS_ADDRESS_H	0x28
+#define M5602_OB_ACRX_STATUS_DATA_L	0x29
+#define M5602_OB_ACRX_STATUS_DATA_H	0x2a
+#define M5602_OB_ACTX_COMMAND_ADDRESS	0x31
+#define M5602_OB_ACRX_COMMAND_DATA_L	0x32
+#define M5602_OB_ACTX_COMMAND_DATA_H	0X33
+#define M5602_XB_DEVCTR1		0x41
+#define M5602_XB_EPSETR0		0x42
+#define M5602_XB_EPAFCTR		0x47
+#define M5602_XB_EPBFCTR		0x49
+#define M5602_XB_EPEFCTR		0x4f
+#define M5602_XB_TEST_REG		0x53
+#define M5602_XB_ALT2SIZE		0x54
+#define M5602_XB_ALT3SIZE		0x55
+#define M5602_XB_OBSFRAME		0x56
+#define M5602_XB_PWR_CTL		0x59
+#define M5602_XB_ADC_CTRL		0x60
+#define M5602_XB_ADC_DATA		0x61
+#define M5602_XB_MISC_CTRL		0x62
+#define M5602_XB_SNAPSHOT		0x63
+#define M5602_XB_SCRATCH_1		0x64
+#define M5602_XB_SCRATCH_2		0x65
+#define M5602_XB_SCRATCH_3		0x66
+#define M5602_XB_SCRATCH_4		0x67
+#define M5602_XB_I2C_CTRL		0x68
+#define M5602_XB_I2C_CLK_DIV		0x69
+#define M5602_XB_I2C_DEV_ADDR		0x6a
+#define M5602_XB_I2C_REG_ADDR		0x6b
+#define M5602_XB_I2C_DATA		0x6c
+#define M5602_XB_I2C_STATUS		0x6d
+#define M5602_XB_GPIO_DAT_H		0x70
+#define M5602_XB_GPIO_DAT_L		0x71
+#define M5602_XB_GPIO_DIR_H		0x72
+#define M5602_XB_GPIO_DIR_L		0x73
+#define M5602_XB_GPIO_EN_H		0x74
+#define M5602_XB_GPIO_EN_L		0x75
+#define M5602_XB_GPIO_DAT		0x76
+#define M5602_XB_GPIO_DIR		0x77
+#define M5602_XB_SEN_CLK_CONTROL	0x80
+#define M5602_XB_SEN_CLK_DIVISION	0x81
+#define M5602_XB_CPR_CLK_CONTROL	0x82
+#define M5602_XB_CPR_CLK_DIVISION	0x83
+#define M5602_XB_MCU_CLK_CONTROL	0x84
+#define M5602_XB_MCU_CLK_DIVISION	0x85
+#define M5602_XB_DCT_CLK_CONTROL	0x86
+#define M5602_XB_DCT_CLK_DIVISION	0x87
+#define M5602_XB_EC_CLK_CONTROL		0x88
+#define M5602_XB_EC_CLK_DIVISION	0x89
+#define M5602_XB_LBUF_CLK_CONTROL	0x8a
+#define M5602_XB_LBUF_CLK_DIVISION	0x8b
+
+#define I2C_BUSY 0x80
+
+/*****************************************************************************/
+
+/* Driver info */
+#define DRIVER_AUTHOR "ALi m5602 Linux Driver Project"
+#define DRIVER_DESC "ALi m5602 webcam driver"
+
+#define M5602_ISOC_ENDPOINT_ADDR 0x81
+#define M5602_INTR_ENDPOINT_ADDR 0x82
+
+#define M5602_URB_MSG_TIMEOUT   5000
+
+/*****************************************************************************/
+
+/* A skeleton used for sending messages to the m5602 bridge */
+static const unsigned char bridge_urb_skeleton[] = {
+	0x13, 0x00, 0x81, 0x00
+};
+
+/* A skeleton used for sending messages to the sensor */
+static const unsigned char sensor_urb_skeleton[] = {
+	0x23, M5602_XB_GPIO_EN_H, 0x81, 0x06,
+	0x23, M5602_XB_MISC_CTRL, 0x81, 0x80,
+	0x13, M5602_XB_I2C_DEV_ADDR, 0x81, 0x00,
+	0x13, M5602_XB_I2C_REG_ADDR, 0x81, 0x00,
+	0x13, M5602_XB_I2C_DATA, 0x81, 0x00,
+	0x13, M5602_XB_I2C_CTRL, 0x81, 0x11
+};
+
+struct sd {
+	struct gspca_dev gspca_dev;
+
+	/* A pointer to the currently connected sensor */
+	const struct m5602_sensor *sensor;
+
+	/* The current frame's id, used to detect frame boundaries */
+	u8 frame_id;
+
+	/* The current frame count */
+	u32 frame_count;
+
+	/* Camera rotation polling thread for "flipable" cams */
+	struct task_struct *rotation_thread;
+
+	struct { /* auto-white-bal + green/red/blue balance control cluster */
+		struct v4l2_ctrl *auto_white_bal;
+		struct v4l2_ctrl *red_bal;
+		struct v4l2_ctrl *blue_bal;
+		struct v4l2_ctrl *green_bal;
+	};
+	struct { /* autoexpo / expo cluster */
+		struct v4l2_ctrl *autoexpo;
+		struct v4l2_ctrl *expo;
+	};
+	struct { /* autogain / gain cluster */
+		struct v4l2_ctrl *autogain;
+		struct v4l2_ctrl *gain;
+	};
+	struct { /* hflip/vflip cluster */
+		struct v4l2_ctrl *hflip;
+		struct v4l2_ctrl *vflip;
+	};
+};
+
+int m5602_read_bridge(
+	struct sd *sd, const u8 address, u8 *i2c_data);
+
+int m5602_write_bridge(
+	struct sd *sd, const u8 address, const u8 i2c_data);
+
+int m5602_write_sensor(struct sd *sd, const u8 address,
+		       u8 *i2c_data, const u8 len);
+
+int m5602_read_sensor(struct sd *sd, const u8 address,
+		      u8 *i2c_data, const u8 len);
+
+#endif
diff --git a/drivers/media/usb/gspca/m5602/m5602_core.c b/drivers/media/usb/gspca/m5602/m5602_core.c
new file mode 100644
index 0000000..d926e62
--- /dev/null
+++ b/drivers/media/usb/gspca/m5602/m5602_core.c
@@ -0,0 +1,438 @@
+ /*
+ * USB Driver for ALi m5602 based webcams
+ *
+ * Copyright (C) 2008 Erik Andrén
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * 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.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "m5602_ov9650.h"
+#include "m5602_ov7660.h"
+#include "m5602_mt9m111.h"
+#include "m5602_po1030.h"
+#include "m5602_s5k83a.h"
+#include "m5602_s5k4aa.h"
+
+/* Kernel module parameters */
+int force_sensor;
+static bool dump_bridge;
+bool dump_sensor;
+
+static const struct usb_device_id m5602_table[] = {
+	{USB_DEVICE(0x0402, 0x5602)},
+	{}
+};
+
+MODULE_DEVICE_TABLE(usb, m5602_table);
+
+/* Reads a byte from the m5602 */
+int m5602_read_bridge(struct sd *sd, const u8 address, u8 *i2c_data)
+{
+	int err;
+	struct gspca_dev *gspca_dev = (struct gspca_dev *) sd;
+	struct usb_device *udev = sd->gspca_dev.dev;
+	__u8 *buf = sd->gspca_dev.usb_buf;
+
+	err = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+			      0x04, 0xc0, 0x14,
+			      0x8100 + address, buf,
+			      1, M5602_URB_MSG_TIMEOUT);
+	*i2c_data = buf[0];
+
+	PDEBUG(D_CONF, "Reading bridge register 0x%x containing 0x%x",
+	       address, *i2c_data);
+
+	/* usb_control_msg(...) returns the number of bytes sent upon success,
+	mask that and return zero instead*/
+	return (err < 0) ? err : 0;
+}
+
+/* Writes a byte to the m5602 */
+int m5602_write_bridge(struct sd *sd, const u8 address, const u8 i2c_data)
+{
+	int err;
+	struct gspca_dev *gspca_dev = (struct gspca_dev *) sd;
+	struct usb_device *udev = sd->gspca_dev.dev;
+	__u8 *buf = sd->gspca_dev.usb_buf;
+
+	PDEBUG(D_CONF, "Writing bridge register 0x%x with 0x%x",
+	       address, i2c_data);
+
+	memcpy(buf, bridge_urb_skeleton,
+	       sizeof(bridge_urb_skeleton));
+	buf[1] = address;
+	buf[3] = i2c_data;
+
+	err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+				0x04, 0x40, 0x19,
+				0x0000, buf,
+				4, M5602_URB_MSG_TIMEOUT);
+
+	/* usb_control_msg(...) returns the number of bytes sent upon success,
+	   mask that and return zero instead */
+	return (err < 0) ? err : 0;
+}
+
+static int m5602_wait_for_i2c(struct sd *sd)
+{
+	int err;
+	u8 data;
+
+	do {
+		err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, &data);
+	} while ((data & I2C_BUSY) && !err);
+	return err;
+}
+
+int m5602_read_sensor(struct sd *sd, const u8 address,
+		       u8 *i2c_data, const u8 len)
+{
+	int err, i;
+	struct gspca_dev *gspca_dev = (struct gspca_dev *) sd;
+
+	if (!len || len > sd->sensor->i2c_regW)
+		return -EINVAL;
+
+	err = m5602_wait_for_i2c(sd);
+	if (err < 0)
+		return err;
+
+	err = m5602_write_bridge(sd, M5602_XB_I2C_DEV_ADDR,
+				 sd->sensor->i2c_slave_id);
+	if (err < 0)
+		return err;
+
+	err = m5602_write_bridge(sd, M5602_XB_I2C_REG_ADDR, address);
+	if (err < 0)
+		return err;
+
+	/* Sensors with registers that are of only
+	   one byte width are differently read */
+
+	/* FIXME: This works with the ov9650, but has issues with the po1030 */
+	if (sd->sensor->i2c_regW == 1) {
+		err = m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 1);
+		if (err < 0)
+			return err;
+
+		err = m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x08);
+	} else {
+		err = m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x18 + len);
+	}
+
+	for (i = 0; (i < len) && !err; i++) {
+		err = m5602_wait_for_i2c(sd);
+		if (err < 0)
+			return err;
+
+		err = m5602_read_bridge(sd, M5602_XB_I2C_DATA, &(i2c_data[i]));
+
+		PDEBUG(D_CONF, "Reading sensor register "
+			       "0x%x containing 0x%x ", address, *i2c_data);
+	}
+	return err;
+}
+
+int m5602_write_sensor(struct sd *sd, const u8 address,
+			u8 *i2c_data, const u8 len)
+{
+	int err, i;
+	u8 *p;
+	struct gspca_dev *gspca_dev = (struct gspca_dev *) sd;
+	struct usb_device *udev = sd->gspca_dev.dev;
+	__u8 *buf = sd->gspca_dev.usb_buf;
+
+	/* No sensor with a data width larger than 16 bits has yet been seen */
+	if (len > sd->sensor->i2c_regW || !len)
+		return -EINVAL;
+
+	memcpy(buf, sensor_urb_skeleton,
+	       sizeof(sensor_urb_skeleton));
+
+	buf[11] = sd->sensor->i2c_slave_id;
+	buf[15] = address;
+
+	/* Special case larger sensor writes */
+	p = buf + 16;
+
+	/* Copy a four byte write sequence for each byte to be written to */
+	for (i = 0; i < len; i++) {
+		memcpy(p, sensor_urb_skeleton + 16, 4);
+		p[3] = i2c_data[i];
+		p += 4;
+		PDEBUG(D_CONF, "Writing sensor register 0x%x with 0x%x",
+		       address, i2c_data[i]);
+	}
+
+	/* Copy the tailer */
+	memcpy(p, sensor_urb_skeleton + 20, 4);
+
+	/* Set the total length */
+	p[3] = 0x10 + len;
+
+	err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+			      0x04, 0x40, 0x19,
+			      0x0000, buf,
+			      20 + len * 4, M5602_URB_MSG_TIMEOUT);
+
+	return (err < 0) ? err : 0;
+}
+
+/* Dump all the registers of the m5602 bridge,
+   unfortunately this breaks the camera until it's power cycled */
+static void m5602_dump_bridge(struct sd *sd)
+{
+	int i;
+	for (i = 0; i < 0x80; i++) {
+		unsigned char val = 0;
+		m5602_read_bridge(sd, i, &val);
+		pr_info("ALi m5602 address 0x%x contains 0x%x\n", i, val);
+	}
+	pr_info("Warning: The ALi m5602 webcam probably won't work until it's power cycled\n");
+}
+
+static int m5602_probe_sensor(struct sd *sd)
+{
+	/* Try the po1030 */
+	sd->sensor = &po1030;
+	if (!sd->sensor->probe(sd))
+		return 0;
+
+	/* Try the mt9m111 sensor */
+	sd->sensor = &mt9m111;
+	if (!sd->sensor->probe(sd))
+		return 0;
+
+	/* Try the s5k4aa */
+	sd->sensor = &s5k4aa;
+	if (!sd->sensor->probe(sd))
+		return 0;
+
+	/* Try the ov9650 */
+	sd->sensor = &ov9650;
+	if (!sd->sensor->probe(sd))
+		return 0;
+
+	/* Try the ov7660 */
+	sd->sensor = &ov7660;
+	if (!sd->sensor->probe(sd))
+		return 0;
+
+	/* Try the s5k83a */
+	sd->sensor = &s5k83a;
+	if (!sd->sensor->probe(sd))
+		return 0;
+
+	/* More sensor probe function goes here */
+	pr_info("Failed to find a sensor\n");
+	sd->sensor = NULL;
+	return -ENODEV;
+}
+
+static int m5602_configure(struct gspca_dev *gspca_dev,
+			   const struct usb_device_id *id);
+
+static int m5602_init(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int err;
+
+	PDEBUG(D_CONF, "Initializing ALi m5602 webcam");
+	/* Run the init sequence */
+	err = sd->sensor->init(sd);
+
+	return err;
+}
+
+static int m5602_init_controls(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (!sd->sensor->init_controls)
+		return 0;
+
+	return sd->sensor->init_controls(sd);
+}
+
+static int m5602_start_transfer(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	__u8 *buf = sd->gspca_dev.usb_buf;
+	int err;
+
+	/* Send start command to the camera */
+	const u8 buffer[4] = {0x13, 0xf9, 0x0f, 0x01};
+
+	if (sd->sensor->start)
+		sd->sensor->start(sd);
+
+	memcpy(buf, buffer, sizeof(buffer));
+	err = usb_control_msg(gspca_dev->dev,
+			      usb_sndctrlpipe(gspca_dev->dev, 0),
+			      0x04, 0x40, 0x19, 0x0000, buf,
+			      sizeof(buffer), M5602_URB_MSG_TIMEOUT);
+
+	PDEBUG(D_STREAM, "Transfer started");
+	return (err < 0) ? err : 0;
+}
+
+static void m5602_urb_complete(struct gspca_dev *gspca_dev,
+				u8 *data, int len)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (len < 6) {
+		PDEBUG(D_PACK, "Packet is less than 6 bytes");
+		return;
+	}
+
+	/* Frame delimiter: ff xx xx xx ff ff */
+	if (data[0] == 0xff && data[4] == 0xff && data[5] == 0xff &&
+	    data[2] != sd->frame_id) {
+		PDEBUG(D_FRAM, "Frame delimiter detected");
+		sd->frame_id = data[2];
+
+		/* Remove the extra fluff appended on each header */
+		data += 6;
+		len -= 6;
+
+		/* Complete the last frame (if any) */
+		gspca_frame_add(gspca_dev, LAST_PACKET,
+				NULL, 0);
+		sd->frame_count++;
+
+		/* Create a new frame */
+		gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
+
+		PDEBUG(D_FRAM, "Starting new frame %d",
+		       sd->frame_count);
+
+	} else {
+		int cur_frame_len;
+
+		cur_frame_len = gspca_dev->image_len;
+		/* Remove urb header */
+		data += 4;
+		len -= 4;
+
+		if (cur_frame_len + len <= gspca_dev->frsz) {
+			PDEBUG(D_FRAM, "Continuing frame %d copying %d bytes",
+			       sd->frame_count, len);
+
+			gspca_frame_add(gspca_dev, INTER_PACKET,
+					data, len);
+		} else {
+			/* Add the remaining data up to frame size */
+			gspca_frame_add(gspca_dev, INTER_PACKET, data,
+				    gspca_dev->frsz - cur_frame_len);
+		}
+	}
+}
+
+static void m5602_stop_transfer(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	/* Run the sensor specific end transfer sequence */
+	if (sd->sensor->stop)
+		sd->sensor->stop(sd);
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+	.name		= MODULE_NAME,
+	.config		= m5602_configure,
+	.init		= m5602_init,
+	.init_controls	= m5602_init_controls,
+	.start		= m5602_start_transfer,
+	.stopN		= m5602_stop_transfer,
+	.pkt_scan	= m5602_urb_complete
+};
+
+/* this function is called at probe time */
+static int m5602_configure(struct gspca_dev *gspca_dev,
+			   const struct usb_device_id *id)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct cam *cam;
+	int err;
+
+	cam = &gspca_dev->cam;
+
+	if (dump_bridge)
+		m5602_dump_bridge(sd);
+
+	/* Probe sensor */
+	err = m5602_probe_sensor(sd);
+	if (err)
+		goto fail;
+
+	return 0;
+
+fail:
+	PERR("ALi m5602 webcam failed");
+	cam->cam_mode = NULL;
+	cam->nmodes = 0;
+
+	return err;
+}
+
+static int m5602_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 void m5602_disconnect(struct usb_interface *intf)
+{
+	struct gspca_dev *gspca_dev = usb_get_intfdata(intf);
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (sd->sensor->disconnect)
+		sd->sensor->disconnect(sd);
+
+	gspca_disconnect(intf);
+}
+
+static struct usb_driver sd_driver = {
+	.name = MODULE_NAME,
+	.id_table = m5602_table,
+	.probe = m5602_probe,
+#ifdef CONFIG_PM
+	.suspend = gspca_suspend,
+	.resume = gspca_resume,
+	.reset_resume = gspca_resume,
+#endif
+	.disconnect = m5602_disconnect
+};
+
+module_usb_driver(sd_driver);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+module_param(force_sensor, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(force_sensor,
+		"forces detection of a sensor, "
+		"1 = OV9650, 2 = S5K83A, 3 = S5K4AA, "
+		"4 = MT9M111, 5 = PO1030, 6 = OV7660");
+
+module_param(dump_bridge, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(dump_bridge, "Dumps all usb bridge registers at startup");
+
+module_param(dump_sensor, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(dump_sensor, "Dumps all usb sensor registers "
+		"at startup providing a sensor is found");
diff --git a/drivers/media/usb/gspca/m5602/m5602_mt9m111.c b/drivers/media/usb/gspca/m5602/m5602_mt9m111.c
new file mode 100644
index 0000000..27fcef1
--- /dev/null
+++ b/drivers/media/usb/gspca/m5602/m5602_mt9m111.c
@@ -0,0 +1,457 @@
+/*
+ * Driver for the mt9m111 sensor
+ *
+ * Copyright (C) 2008 Erik Andrén
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * 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.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "m5602_mt9m111.h"
+
+static int mt9m111_s_ctrl(struct v4l2_ctrl *ctrl);
+static void mt9m111_dump_registers(struct sd *sd);
+
+static struct v4l2_pix_format mt9m111_modes[] = {
+	{
+		640,
+		480,
+		V4L2_PIX_FMT_SBGGR8,
+		V4L2_FIELD_NONE,
+		.sizeimage = 640 * 480,
+		.bytesperline = 640,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 0
+	}
+};
+
+static const struct v4l2_ctrl_ops mt9m111_ctrl_ops = {
+	.s_ctrl = mt9m111_s_ctrl,
+};
+
+static const struct v4l2_ctrl_config mt9m111_greenbal_cfg = {
+	.ops	= &mt9m111_ctrl_ops,
+	.id	= M5602_V4L2_CID_GREEN_BALANCE,
+	.name	= "Green Balance",
+	.type	= V4L2_CTRL_TYPE_INTEGER,
+	.min	= 0,
+	.max	= 0x7ff,
+	.step	= 1,
+	.def	= MT9M111_GREEN_GAIN_DEFAULT,
+	.flags	= V4L2_CTRL_FLAG_SLIDER,
+};
+
+int mt9m111_probe(struct sd *sd)
+{
+	u8 data[2] = {0x00, 0x00};
+	int i;
+	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
+
+	if (force_sensor) {
+		if (force_sensor == MT9M111_SENSOR) {
+			pr_info("Forcing a %s sensor\n", mt9m111.name);
+			goto sensor_found;
+		}
+		/* If we want to force another sensor, don't try to probe this
+		 * one */
+		return -ENODEV;
+	}
+
+	PDEBUG(D_PROBE, "Probing for a mt9m111 sensor");
+
+	/* Do the preinit */
+	for (i = 0; i < ARRAY_SIZE(preinit_mt9m111); i++) {
+		if (preinit_mt9m111[i][0] == BRIDGE) {
+			m5602_write_bridge(sd,
+				preinit_mt9m111[i][1],
+				preinit_mt9m111[i][2]);
+		} else {
+			data[0] = preinit_mt9m111[i][2];
+			data[1] = preinit_mt9m111[i][3];
+			m5602_write_sensor(sd,
+				preinit_mt9m111[i][1], data, 2);
+		}
+	}
+
+	if (m5602_read_sensor(sd, MT9M111_SC_CHIPVER, data, 2))
+		return -ENODEV;
+
+	if ((data[0] == 0x14) && (data[1] == 0x3a)) {
+		pr_info("Detected a mt9m111 sensor\n");
+		goto sensor_found;
+	}
+
+	return -ENODEV;
+
+sensor_found:
+	sd->gspca_dev.cam.cam_mode = mt9m111_modes;
+	sd->gspca_dev.cam.nmodes = ARRAY_SIZE(mt9m111_modes);
+
+	return 0;
+}
+
+int mt9m111_init(struct sd *sd)
+{
+	int i, err = 0;
+
+	/* Init the sensor */
+	for (i = 0; i < ARRAY_SIZE(init_mt9m111) && !err; i++) {
+		u8 data[2];
+
+		if (init_mt9m111[i][0] == BRIDGE) {
+			err = m5602_write_bridge(sd,
+				init_mt9m111[i][1],
+				init_mt9m111[i][2]);
+		} else {
+			data[0] = init_mt9m111[i][2];
+			data[1] = init_mt9m111[i][3];
+			err = m5602_write_sensor(sd,
+				init_mt9m111[i][1], data, 2);
+		}
+	}
+
+	if (dump_sensor)
+		mt9m111_dump_registers(sd);
+
+	return 0;
+}
+
+int mt9m111_init_controls(struct sd *sd)
+{
+	struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler;
+
+	sd->gspca_dev.vdev.ctrl_handler = hdl;
+	v4l2_ctrl_handler_init(hdl, 7);
+
+	sd->auto_white_bal = v4l2_ctrl_new_std(hdl, &mt9m111_ctrl_ops,
+					       V4L2_CID_AUTO_WHITE_BALANCE,
+					       0, 1, 1, 0);
+	sd->green_bal = v4l2_ctrl_new_custom(hdl, &mt9m111_greenbal_cfg, NULL);
+	sd->red_bal = v4l2_ctrl_new_std(hdl, &mt9m111_ctrl_ops,
+					V4L2_CID_RED_BALANCE, 0, 0x7ff, 1,
+					MT9M111_RED_GAIN_DEFAULT);
+	sd->blue_bal = v4l2_ctrl_new_std(hdl, &mt9m111_ctrl_ops,
+					V4L2_CID_BLUE_BALANCE, 0, 0x7ff, 1,
+					MT9M111_BLUE_GAIN_DEFAULT);
+
+	v4l2_ctrl_new_std(hdl, &mt9m111_ctrl_ops, V4L2_CID_GAIN, 0,
+			  (INITIAL_MAX_GAIN - 1) * 2 * 2 * 2, 1,
+			  MT9M111_DEFAULT_GAIN);
+
+	sd->hflip = v4l2_ctrl_new_std(hdl, &mt9m111_ctrl_ops, V4L2_CID_HFLIP,
+				      0, 1, 1, 0);
+	sd->vflip = v4l2_ctrl_new_std(hdl, &mt9m111_ctrl_ops, V4L2_CID_VFLIP,
+				      0, 1, 1, 0);
+
+	if (hdl->error) {
+		pr_err("Could not initialize controls\n");
+		return hdl->error;
+	}
+
+	v4l2_ctrl_auto_cluster(4, &sd->auto_white_bal, 0, false);
+	v4l2_ctrl_cluster(2, &sd->hflip);
+
+	return 0;
+}
+
+int mt9m111_start(struct sd *sd)
+{
+	int i, err = 0;
+	u8 data[2];
+	struct cam *cam = &sd->gspca_dev.cam;
+	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
+
+	int width = cam->cam_mode[sd->gspca_dev.curr_mode].width - 1;
+	int height = cam->cam_mode[sd->gspca_dev.curr_mode].height;
+
+	for (i = 0; i < ARRAY_SIZE(start_mt9m111) && !err; i++) {
+		if (start_mt9m111[i][0] == BRIDGE) {
+			err = m5602_write_bridge(sd,
+				start_mt9m111[i][1],
+				start_mt9m111[i][2]);
+		} else {
+			data[0] = start_mt9m111[i][2];
+			data[1] = start_mt9m111[i][3];
+			err = m5602_write_sensor(sd,
+				start_mt9m111[i][1], data, 2);
+		}
+	}
+	if (err < 0)
+		return err;
+
+	err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (height >> 8) & 0xff);
+	if (err < 0)
+		return err;
+
+	err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (height & 0xff));
+	if (err < 0)
+		return err;
+
+	for (i = 0; i < 2 && !err; i++)
+		err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, 0);
+	if (err < 0)
+		return err;
+
+	err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0);
+	if (err < 0)
+		return err;
+
+	err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 2);
+	if (err < 0)
+		return err;
+
+	for (i = 0; i < 2 && !err; i++)
+		err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, 0);
+	if (err < 0)
+		return err;
+
+	err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA,
+				 (width >> 8) & 0xff);
+	if (err < 0)
+		return err;
+
+	err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, width & 0xff);
+	if (err < 0)
+		return err;
+
+	err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0);
+	if (err < 0)
+		return err;
+
+	switch (width) {
+	case 640:
+		PDEBUG(D_CONF, "Configuring camera for VGA mode");
+		break;
+
+	case 320:
+		PDEBUG(D_CONF, "Configuring camera for QVGA mode");
+		break;
+	}
+	return err;
+}
+
+void mt9m111_disconnect(struct sd *sd)
+{
+	sd->sensor = NULL;
+}
+
+static int mt9m111_set_hvflip(struct gspca_dev *gspca_dev)
+{
+	int err;
+	u8 data[2] = {0x00, 0x00};
+	struct sd *sd = (struct sd *) gspca_dev;
+	int hflip;
+	int vflip;
+
+	PDEBUG(D_CONF, "Set hvflip to %d %d", sd->hflip->val, sd->vflip->val);
+
+	/* The mt9m111 is flipped by default */
+	hflip = !sd->hflip->val;
+	vflip = !sd->vflip->val;
+
+	/* Set the correct page map */
+	err = m5602_write_sensor(sd, MT9M111_PAGE_MAP, data, 2);
+	if (err < 0)
+		return err;
+
+	data[0] = MT9M111_RMB_OVER_SIZED;
+	if (gspca_dev->pixfmt.width == 640) {
+		data[1] = MT9M111_RMB_ROW_SKIP_2X |
+			  MT9M111_RMB_COLUMN_SKIP_2X |
+			  (hflip << 1) | vflip;
+	} else {
+		data[1] = MT9M111_RMB_ROW_SKIP_4X |
+			  MT9M111_RMB_COLUMN_SKIP_4X |
+			  (hflip << 1) | vflip;
+	}
+	err = m5602_write_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B,
+					data, 2);
+	return err;
+}
+
+static int mt9m111_set_auto_white_balance(struct gspca_dev *gspca_dev,
+					  __s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int err;
+	u8 data[2];
+
+	err = m5602_read_sensor(sd, MT9M111_CP_OPERATING_MODE_CTL, data, 2);
+	if (err < 0)
+		return err;
+
+	data[1] = ((data[1] & 0xfd) | ((val & 0x01) << 1));
+
+	err = m5602_write_sensor(sd, MT9M111_CP_OPERATING_MODE_CTL, data, 2);
+
+	PDEBUG(D_CONF, "Set auto white balance %d", val);
+	return err;
+}
+
+static int mt9m111_set_gain(struct gspca_dev *gspca_dev, __s32 val)
+{
+	int err, tmp;
+	u8 data[2] = {0x00, 0x00};
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	/* Set the correct page map */
+	err = m5602_write_sensor(sd, MT9M111_PAGE_MAP, data, 2);
+	if (err < 0)
+		return err;
+
+	if (val >= INITIAL_MAX_GAIN * 2 * 2 * 2)
+		return -EINVAL;
+
+	if ((val >= INITIAL_MAX_GAIN * 2 * 2) &&
+	    (val < (INITIAL_MAX_GAIN - 1) * 2 * 2 * 2))
+		tmp = (1 << 10) | (val << 9) |
+				(val << 8) | (val / 8);
+	else if ((val >= INITIAL_MAX_GAIN * 2) &&
+		 (val <  INITIAL_MAX_GAIN * 2 * 2))
+		tmp = (1 << 9) | (1 << 8) | (val / 4);
+	else if ((val >= INITIAL_MAX_GAIN) &&
+		 (val < INITIAL_MAX_GAIN * 2))
+		tmp = (1 << 8) | (val / 2);
+	else
+		tmp = val;
+
+	data[1] = (tmp & 0xff);
+	data[0] = (tmp & 0xff00) >> 8;
+	PDEBUG(D_CONF, "tmp=%d, data[1]=%d, data[0]=%d", tmp,
+	       data[1], data[0]);
+
+	err = m5602_write_sensor(sd, MT9M111_SC_GLOBAL_GAIN,
+				   data, 2);
+
+	return err;
+}
+
+static int mt9m111_set_green_balance(struct gspca_dev *gspca_dev, __s32 val)
+{
+	int err;
+	u8 data[2];
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	data[1] = (val & 0xff);
+	data[0] = (val & 0xff00) >> 8;
+
+	PDEBUG(D_CONF, "Set green balance %d", val);
+	err = m5602_write_sensor(sd, MT9M111_SC_GREEN_1_GAIN,
+				 data, 2);
+	if (err < 0)
+		return err;
+
+	return m5602_write_sensor(sd, MT9M111_SC_GREEN_2_GAIN,
+				  data, 2);
+}
+
+static int mt9m111_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val)
+{
+	u8 data[2];
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	data[1] = (val & 0xff);
+	data[0] = (val & 0xff00) >> 8;
+
+	PDEBUG(D_CONF, "Set blue balance %d", val);
+
+	return m5602_write_sensor(sd, MT9M111_SC_BLUE_GAIN,
+				  data, 2);
+}
+
+static int mt9m111_set_red_balance(struct gspca_dev *gspca_dev, __s32 val)
+{
+	u8 data[2];
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	data[1] = (val & 0xff);
+	data[0] = (val & 0xff00) >> 8;
+
+	PDEBUG(D_CONF, "Set red balance %d", val);
+
+	return m5602_write_sensor(sd, MT9M111_SC_RED_GAIN,
+				  data, 2);
+}
+
+static int mt9m111_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct gspca_dev *gspca_dev =
+		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+	struct sd *sd = (struct sd *) gspca_dev;
+	int err;
+
+	if (!gspca_dev->streaming)
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_AUTO_WHITE_BALANCE:
+		err = mt9m111_set_auto_white_balance(gspca_dev, ctrl->val);
+		if (err || ctrl->val)
+			return err;
+		err = mt9m111_set_green_balance(gspca_dev, sd->green_bal->val);
+		if (err)
+			return err;
+		err = mt9m111_set_red_balance(gspca_dev, sd->red_bal->val);
+		if (err)
+			return err;
+		err = mt9m111_set_blue_balance(gspca_dev, sd->blue_bal->val);
+		break;
+	case V4L2_CID_GAIN:
+		err = mt9m111_set_gain(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_HFLIP:
+		err = mt9m111_set_hvflip(gspca_dev);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return err;
+}
+
+static void mt9m111_dump_registers(struct sd *sd)
+{
+	u8 address, value[2] = {0x00, 0x00};
+
+	pr_info("Dumping the mt9m111 register state\n");
+
+	pr_info("Dumping the mt9m111 sensor core registers\n");
+	value[1] = MT9M111_SENSOR_CORE;
+	m5602_write_sensor(sd, MT9M111_PAGE_MAP, value, 2);
+	for (address = 0; address < 0xff; address++) {
+		m5602_read_sensor(sd, address, value, 2);
+		pr_info("register 0x%x contains 0x%x%x\n",
+			address, value[0], value[1]);
+	}
+
+	pr_info("Dumping the mt9m111 color pipeline registers\n");
+	value[1] = MT9M111_COLORPIPE;
+	m5602_write_sensor(sd, MT9M111_PAGE_MAP, value, 2);
+	for (address = 0; address < 0xff; address++) {
+		m5602_read_sensor(sd, address, value, 2);
+		pr_info("register 0x%x contains 0x%x%x\n",
+			address, value[0], value[1]);
+	}
+
+	pr_info("Dumping the mt9m111 camera control registers\n");
+	value[1] = MT9M111_CAMERA_CONTROL;
+	m5602_write_sensor(sd, MT9M111_PAGE_MAP, value, 2);
+	for (address = 0; address < 0xff; address++) {
+		m5602_read_sensor(sd, address, value, 2);
+		pr_info("register 0x%x contains 0x%x%x\n",
+			address, value[0], value[1]);
+	}
+
+	pr_info("mt9m111 register state dump complete\n");
+}
diff --git a/drivers/media/usb/gspca/m5602/m5602_mt9m111.h b/drivers/media/usb/gspca/m5602/m5602_mt9m111.h
new file mode 100644
index 0000000..07448d3
--- /dev/null
+++ b/drivers/media/usb/gspca/m5602/m5602_mt9m111.h
@@ -0,0 +1,273 @@
+/*
+ * Driver for the mt9m111 sensor
+ *
+ * Copyright (C) 2008 Erik Andrén
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * Some defines taken from the mt9m111 sensor driver
+ * Copyright (C) 2008, Robert Jarzmik <robert.jarzmik@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, version 2.
+ *
+ */
+
+#ifndef M5602_MT9M111_H_
+#define M5602_MT9M111_H_
+
+#include "m5602_sensor.h"
+
+/*****************************************************************************/
+
+#define MT9M111_SC_CHIPVER			0x00
+#define MT9M111_SC_ROWSTART			0x01
+#define MT9M111_SC_COLSTART			0x02
+#define MT9M111_SC_WINDOW_HEIGHT		0x03
+#define MT9M111_SC_WINDOW_WIDTH			0x04
+#define MT9M111_SC_HBLANK_CONTEXT_B		0x05
+#define MT9M111_SC_VBLANK_CONTEXT_B		0x06
+#define MT9M111_SC_HBLANK_CONTEXT_A		0x07
+#define MT9M111_SC_VBLANK_CONTEXT_A		0x08
+#define MT9M111_SC_SHUTTER_WIDTH		0x09
+#define MT9M111_SC_ROW_SPEED			0x0a
+#define MT9M111_SC_EXTRA_DELAY			0x0b
+#define MT9M111_SC_SHUTTER_DELAY		0x0c
+#define MT9M111_SC_RESET			0x0d
+#define MT9M111_SC_R_MODE_CONTEXT_B		0x20
+#define MT9M111_SC_R_MODE_CONTEXT_A		0x21
+#define MT9M111_SC_FLASH_CONTROL		0x23
+#define MT9M111_SC_GREEN_1_GAIN			0x2b
+#define MT9M111_SC_BLUE_GAIN			0x2c
+#define MT9M111_SC_RED_GAIN			0x2d
+#define MT9M111_SC_GREEN_2_GAIN			0x2e
+#define MT9M111_SC_GLOBAL_GAIN			0x2f
+
+#define MT9M111_CONTEXT_CONTROL			0xc8
+#define MT9M111_PAGE_MAP			0xf0
+#define MT9M111_BYTEWISE_ADDRESS		0xf1
+
+#define MT9M111_CP_OPERATING_MODE_CTL		0x06
+#define MT9M111_CP_LUMA_OFFSET			0x34
+#define MT9M111_CP_LUMA_CLIP			0x35
+#define MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_A 0x3a
+#define MT9M111_CP_LENS_CORRECTION_1		0x3b
+#define MT9M111_CP_DEFECT_CORR_CONTEXT_A	0x4c
+#define MT9M111_CP_DEFECT_CORR_CONTEXT_B	0x4d
+#define MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_B 0x9b
+#define MT9M111_CP_GLOBAL_CLK_CONTROL		0xb3
+
+#define MT9M111_CC_AUTO_EXPOSURE_PARAMETER_18   0x65
+#define MT9M111_CC_AWB_PARAMETER_7		0x28
+
+#define MT9M111_SENSOR_CORE			0x00
+#define MT9M111_COLORPIPE			0x01
+#define MT9M111_CAMERA_CONTROL			0x02
+
+#define MT9M111_RESET				(1 << 0)
+#define MT9M111_RESTART				(1 << 1)
+#define MT9M111_ANALOG_STANDBY			(1 << 2)
+#define MT9M111_CHIP_ENABLE			(1 << 3)
+#define MT9M111_CHIP_DISABLE			(0 << 3)
+#define MT9M111_OUTPUT_DISABLE			(1 << 4)
+#define MT9M111_SHOW_BAD_FRAMES			(1 << 0)
+#define MT9M111_RESTART_BAD_FRAMES		(1 << 1)
+#define MT9M111_SYNCHRONIZE_CHANGES		(1 << 7)
+
+#define MT9M111_RMB_OVER_SIZED			(1 << 0)
+#define MT9M111_RMB_MIRROR_ROWS			(1 << 0)
+#define MT9M111_RMB_MIRROR_COLS			(1 << 1)
+#define MT9M111_RMB_ROW_SKIP_2X			(1 << 2)
+#define MT9M111_RMB_COLUMN_SKIP_2X		(1 << 3)
+#define MT9M111_RMB_ROW_SKIP_4X			(1 << 4)
+#define MT9M111_RMB_COLUMN_SKIP_4X		(1 << 5)
+
+#define MT9M111_COLOR_MATRIX_BYPASS		(1 << 4)
+#define MT9M111_SEL_CONTEXT_B			(1 << 3)
+
+#define MT9M111_TRISTATE_PIN_IN_STANDBY		(1 << 1)
+#define MT9M111_SOC_SOFT_STANDBY		(1 << 0)
+
+#define MT9M111_2D_DEFECT_CORRECTION_ENABLE	(1 << 0)
+
+#define INITIAL_MAX_GAIN			64
+#define MT9M111_DEFAULT_GAIN			283
+#define MT9M111_GREEN_GAIN_DEFAULT		0x20
+#define MT9M111_BLUE_GAIN_DEFAULT		0x20
+#define MT9M111_RED_GAIN_DEFAULT		0x20
+
+/*****************************************************************************/
+
+/* Kernel module parameters */
+extern int force_sensor;
+extern bool dump_sensor;
+
+int mt9m111_probe(struct sd *sd);
+int mt9m111_init(struct sd *sd);
+int mt9m111_init_controls(struct sd *sd);
+int mt9m111_start(struct sd *sd);
+void mt9m111_disconnect(struct sd *sd);
+
+static const struct m5602_sensor mt9m111 = {
+	.name = "MT9M111",
+
+	.i2c_slave_id = 0xba,
+	.i2c_regW = 2,
+
+	.probe = mt9m111_probe,
+	.init = mt9m111_init,
+	.init_controls = mt9m111_init_controls,
+	.disconnect = mt9m111_disconnect,
+	.start = mt9m111_start,
+};
+
+static const unsigned char preinit_mt9m111[][4] = {
+	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
+	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00},
+	{BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+
+	{SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+	{SENSOR, MT9M111_SC_RESET,
+		MT9M111_RESET |
+		MT9M111_RESTART |
+		MT9M111_ANALOG_STANDBY |
+		MT9M111_CHIP_DISABLE,
+		MT9M111_SHOW_BAD_FRAMES |
+		MT9M111_RESTART_BAD_FRAMES |
+		MT9M111_SYNCHRONIZE_CHANGES},
+
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x05, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x04, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_H, 0x3e, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x3e, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x02, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
+
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x07, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x0b, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
+
+	{BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a, 0x00}
+};
+
+static const unsigned char init_mt9m111[][4] = {
+	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
+	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+
+	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x04, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x3e, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x02, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x07, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x0b, 0x00},
+	{BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a, 0x00},
+
+	{SENSOR, MT9M111_SC_RESET, 0x00, 0x29},
+	{SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+	{SENSOR, MT9M111_SC_RESET, 0x00, 0x08},
+	{SENSOR, MT9M111_PAGE_MAP, 0x00, 0x01},
+	{SENSOR, MT9M111_CP_OPERATING_MODE_CTL, 0x00,
+			MT9M111_CP_OPERATING_MODE_CTL},
+	{SENSOR, MT9M111_CP_LENS_CORRECTION_1, 0x04, 0x2a},
+	{SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_A, 0x00,
+				MT9M111_2D_DEFECT_CORRECTION_ENABLE},
+	{SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_B, 0x00,
+				MT9M111_2D_DEFECT_CORRECTION_ENABLE},
+	{SENSOR, MT9M111_CP_LUMA_OFFSET, 0x00, 0x00},
+	{SENSOR, MT9M111_CP_LUMA_CLIP, 0xff, 0x00},
+	{SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_A, 0x14, 0x00},
+	{SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_B, 0x14, 0x00},
+	{SENSOR, 0xcd, 0x00, 0x0e},
+	{SENSOR, 0xd0, 0x00, 0x40},
+
+	{SENSOR, MT9M111_PAGE_MAP, 0x00, 0x02},
+	{SENSOR, MT9M111_CC_AUTO_EXPOSURE_PARAMETER_18, 0x00, 0x00},
+	{SENSOR, MT9M111_CC_AWB_PARAMETER_7, 0xef, 0x03},
+
+	{SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+	{SENSOR, 0x33, 0x03, 0x49},
+	{SENSOR, 0x34, 0xc0, 0x19},
+	{SENSOR, 0x3f, 0x20, 0x20},
+	{SENSOR, 0x40, 0x20, 0x20},
+	{SENSOR, 0x5a, 0xc0, 0x0a},
+	{SENSOR, 0x70, 0x7b, 0x0a},
+	{SENSOR, 0x71, 0xff, 0x00},
+	{SENSOR, 0x72, 0x19, 0x0e},
+	{SENSOR, 0x73, 0x18, 0x0f},
+	{SENSOR, 0x74, 0x57, 0x32},
+	{SENSOR, 0x75, 0x56, 0x34},
+	{SENSOR, 0x76, 0x73, 0x35},
+	{SENSOR, 0x77, 0x30, 0x12},
+	{SENSOR, 0x78, 0x79, 0x02},
+	{SENSOR, 0x79, 0x75, 0x06},
+	{SENSOR, 0x7a, 0x77, 0x0a},
+	{SENSOR, 0x7b, 0x78, 0x09},
+	{SENSOR, 0x7c, 0x7d, 0x06},
+	{SENSOR, 0x7d, 0x31, 0x10},
+	{SENSOR, 0x7e, 0x00, 0x7e},
+	{SENSOR, 0x80, 0x59, 0x04},
+	{SENSOR, 0x81, 0x59, 0x04},
+	{SENSOR, 0x82, 0x57, 0x0a},
+	{SENSOR, 0x83, 0x58, 0x0b},
+	{SENSOR, 0x84, 0x47, 0x0c},
+	{SENSOR, 0x85, 0x48, 0x0e},
+	{SENSOR, 0x86, 0x5b, 0x02},
+	{SENSOR, 0x87, 0x00, 0x5c},
+	{SENSOR, MT9M111_CONTEXT_CONTROL, 0x00, MT9M111_SEL_CONTEXT_B},
+	{SENSOR, 0x60, 0x00, 0x80},
+	{SENSOR, 0x61, 0x00, 0x00},
+	{SENSOR, 0x62, 0x00, 0x00},
+	{SENSOR, 0x63, 0x00, 0x00},
+	{SENSOR, 0x64, 0x00, 0x00},
+
+	{SENSOR, MT9M111_SC_ROWSTART, 0x00, 0x0d}, /* 13 */
+	{SENSOR, MT9M111_SC_COLSTART, 0x00, 0x12}, /* 18 */
+	{SENSOR, MT9M111_SC_WINDOW_HEIGHT, 0x04, 0x00}, /* 1024 */
+	{SENSOR, MT9M111_SC_WINDOW_WIDTH, 0x05, 0x10}, /* 1296 */
+	{SENSOR, MT9M111_SC_HBLANK_CONTEXT_B, 0x01, 0x60}, /* 352 */
+	{SENSOR, MT9M111_SC_VBLANK_CONTEXT_B, 0x00, 0x11}, /* 17 */
+	{SENSOR, MT9M111_SC_HBLANK_CONTEXT_A, 0x01, 0x60}, /* 352 */
+	{SENSOR, MT9M111_SC_VBLANK_CONTEXT_A, 0x00, 0x11}, /* 17 */
+	{SENSOR, MT9M111_SC_R_MODE_CONTEXT_A, 0x01, 0x0f}, /* 271 */
+	{SENSOR, 0x30, 0x04, 0x00},
+	/* Set number of blank rows chosen to 400 */
+	{SENSOR, MT9M111_SC_SHUTTER_WIDTH, 0x01, 0x90},
+};
+
+static const unsigned char start_mt9m111[][4] = {
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+	{BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
+	{BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+};
+#endif
diff --git a/drivers/media/usb/gspca/m5602/m5602_ov7660.c b/drivers/media/usb/gspca/m5602/m5602_ov7660.c
new file mode 100644
index 0000000..64b3b03
--- /dev/null
+++ b/drivers/media/usb/gspca/m5602/m5602_ov7660.c
@@ -0,0 +1,318 @@
+/*
+ * Driver for the ov7660 sensor
+ *
+ * Copyright (C) 2009 Erik Andrén
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * 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.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "m5602_ov7660.h"
+
+static int ov7660_s_ctrl(struct v4l2_ctrl *ctrl);
+static void ov7660_dump_registers(struct sd *sd);
+
+static struct v4l2_pix_format ov7660_modes[] = {
+	{
+		640,
+		480,
+		V4L2_PIX_FMT_SBGGR8,
+		V4L2_FIELD_NONE,
+		.sizeimage =
+			640 * 480,
+		.bytesperline = 640,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 0
+	}
+};
+
+static const struct v4l2_ctrl_ops ov7660_ctrl_ops = {
+	.s_ctrl = ov7660_s_ctrl,
+};
+
+int ov7660_probe(struct sd *sd)
+{
+	int err = 0, i;
+	u8 prod_id = 0, ver_id = 0;
+
+	if (force_sensor) {
+		if (force_sensor == OV7660_SENSOR) {
+			pr_info("Forcing an %s sensor\n", ov7660.name);
+			goto sensor_found;
+		}
+		/* If we want to force another sensor,
+		don't try to probe this one */
+		return -ENODEV;
+	}
+
+	/* Do the preinit */
+	for (i = 0; i < ARRAY_SIZE(preinit_ov7660) && !err; i++) {
+		u8 data[2];
+
+		if (preinit_ov7660[i][0] == BRIDGE) {
+			err = m5602_write_bridge(sd,
+				preinit_ov7660[i][1],
+				preinit_ov7660[i][2]);
+		} else {
+			data[0] = preinit_ov7660[i][2];
+			err = m5602_write_sensor(sd,
+				preinit_ov7660[i][1], data, 1);
+		}
+	}
+	if (err < 0)
+		return err;
+
+	if (m5602_read_sensor(sd, OV7660_PID, &prod_id, 1))
+		return -ENODEV;
+
+	if (m5602_read_sensor(sd, OV7660_VER, &ver_id, 1))
+		return -ENODEV;
+
+	pr_info("Sensor reported 0x%x%x\n", prod_id, ver_id);
+
+	if ((prod_id == 0x76) && (ver_id == 0x60)) {
+		pr_info("Detected a ov7660 sensor\n");
+		goto sensor_found;
+	}
+	return -ENODEV;
+
+sensor_found:
+	sd->gspca_dev.cam.cam_mode = ov7660_modes;
+	sd->gspca_dev.cam.nmodes = ARRAY_SIZE(ov7660_modes);
+
+	return 0;
+}
+
+int ov7660_init(struct sd *sd)
+{
+	int i, err;
+
+	/* Init the sensor */
+	for (i = 0; i < ARRAY_SIZE(init_ov7660); i++) {
+		u8 data[2];
+
+		if (init_ov7660[i][0] == BRIDGE) {
+			err = m5602_write_bridge(sd,
+				init_ov7660[i][1],
+				init_ov7660[i][2]);
+		} else {
+			data[0] = init_ov7660[i][2];
+			err = m5602_write_sensor(sd,
+				init_ov7660[i][1], data, 1);
+		}
+		if (err < 0)
+			return err;
+	}
+
+	if (dump_sensor)
+		ov7660_dump_registers(sd);
+
+	return 0;
+}
+
+int ov7660_init_controls(struct sd *sd)
+{
+	struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler;
+
+	sd->gspca_dev.vdev.ctrl_handler = hdl;
+	v4l2_ctrl_handler_init(hdl, 6);
+
+	v4l2_ctrl_new_std(hdl, &ov7660_ctrl_ops, V4L2_CID_AUTO_WHITE_BALANCE,
+			  0, 1, 1, 1);
+	v4l2_ctrl_new_std_menu(hdl, &ov7660_ctrl_ops,
+			  V4L2_CID_EXPOSURE_AUTO, 1, 0, V4L2_EXPOSURE_AUTO);
+
+	sd->autogain = v4l2_ctrl_new_std(hdl, &ov7660_ctrl_ops,
+					 V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+	sd->gain = v4l2_ctrl_new_std(hdl, &ov7660_ctrl_ops, V4L2_CID_GAIN, 0,
+				     255, 1, OV7660_DEFAULT_GAIN);
+
+	sd->hflip = v4l2_ctrl_new_std(hdl, &ov7660_ctrl_ops, V4L2_CID_HFLIP,
+				      0, 1, 1, 0);
+	sd->vflip = v4l2_ctrl_new_std(hdl, &ov7660_ctrl_ops, V4L2_CID_VFLIP,
+				      0, 1, 1, 0);
+
+	if (hdl->error) {
+		pr_err("Could not initialize controls\n");
+		return hdl->error;
+	}
+
+	v4l2_ctrl_auto_cluster(2, &sd->autogain, 0, false);
+	v4l2_ctrl_cluster(2, &sd->hflip);
+
+	return 0;
+}
+
+int ov7660_start(struct sd *sd)
+{
+	return 0;
+}
+
+int ov7660_stop(struct sd *sd)
+{
+	return 0;
+}
+
+void ov7660_disconnect(struct sd *sd)
+{
+	ov7660_stop(sd);
+
+	sd->sensor = NULL;
+}
+
+static int ov7660_set_gain(struct gspca_dev *gspca_dev, __s32 val)
+{
+	int err;
+	u8 i2c_data = val;
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	PDEBUG(D_CONF, "Setting gain to %d", val);
+
+	err = m5602_write_sensor(sd, OV7660_GAIN, &i2c_data, 1);
+	return err;
+}
+
+static int ov7660_set_auto_white_balance(struct gspca_dev *gspca_dev,
+					 __s32 val)
+{
+	int err;
+	u8 i2c_data;
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	PDEBUG(D_CONF, "Set auto white balance to %d", val);
+
+	err = m5602_read_sensor(sd, OV7660_COM8, &i2c_data, 1);
+	if (err < 0)
+		return err;
+
+	i2c_data = ((i2c_data & 0xfd) | ((val & 0x01) << 1));
+	err = m5602_write_sensor(sd, OV7660_COM8, &i2c_data, 1);
+
+	return err;
+}
+
+static int ov7660_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val)
+{
+	int err;
+	u8 i2c_data;
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	PDEBUG(D_CONF, "Set auto gain control to %d", val);
+
+	err = m5602_read_sensor(sd, OV7660_COM8, &i2c_data, 1);
+	if (err < 0)
+		return err;
+
+	i2c_data = ((i2c_data & 0xfb) | ((val & 0x01) << 2));
+
+	return m5602_write_sensor(sd, OV7660_COM8, &i2c_data, 1);
+}
+
+static int ov7660_set_auto_exposure(struct gspca_dev *gspca_dev,
+				    __s32 val)
+{
+	int err;
+	u8 i2c_data;
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	PDEBUG(D_CONF, "Set auto exposure control to %d", val);
+
+	err = m5602_read_sensor(sd, OV7660_COM8, &i2c_data, 1);
+	if (err < 0)
+		return err;
+
+	val = (val == V4L2_EXPOSURE_AUTO);
+	i2c_data = ((i2c_data & 0xfe) | ((val & 0x01) << 0));
+
+	return m5602_write_sensor(sd, OV7660_COM8, &i2c_data, 1);
+}
+
+static int ov7660_set_hvflip(struct gspca_dev *gspca_dev)
+{
+	int err;
+	u8 i2c_data;
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	PDEBUG(D_CONF, "Set hvflip to %d, %d", sd->hflip->val, sd->vflip->val);
+
+	i2c_data = (sd->hflip->val << 5) | (sd->vflip->val << 4);
+
+	err = m5602_write_sensor(sd, OV7660_MVFP, &i2c_data, 1);
+
+	return err;
+}
+
+static int ov7660_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct gspca_dev *gspca_dev =
+		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+	struct sd *sd = (struct sd *) gspca_dev;
+	int err;
+
+	if (!gspca_dev->streaming)
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_AUTO_WHITE_BALANCE:
+		err = ov7660_set_auto_white_balance(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_EXPOSURE_AUTO:
+		err = ov7660_set_auto_exposure(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_AUTOGAIN:
+		err = ov7660_set_auto_gain(gspca_dev, ctrl->val);
+		if (err || ctrl->val)
+			return err;
+		err = ov7660_set_gain(gspca_dev, sd->gain->val);
+		break;
+	case V4L2_CID_HFLIP:
+		err = ov7660_set_hvflip(gspca_dev);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return err;
+}
+
+static void ov7660_dump_registers(struct sd *sd)
+{
+	int address;
+	pr_info("Dumping the ov7660 register state\n");
+	for (address = 0; address < 0xa9; address++) {
+		u8 value;
+		m5602_read_sensor(sd, address, &value, 1);
+		pr_info("register 0x%x contains 0x%x\n", address, value);
+	}
+
+	pr_info("ov7660 register state dump complete\n");
+
+	pr_info("Probing for which registers that are read/write\n");
+	for (address = 0; address < 0xff; address++) {
+		u8 old_value, ctrl_value;
+		u8 test_value[2] = {0xff, 0xff};
+
+		m5602_read_sensor(sd, address, &old_value, 1);
+		m5602_write_sensor(sd, address, test_value, 1);
+		m5602_read_sensor(sd, address, &ctrl_value, 1);
+
+		if (ctrl_value == test_value[0])
+			pr_info("register 0x%x is writeable\n", address);
+		else
+			pr_info("register 0x%x is read only\n", address);
+
+		/* Restore original value */
+		m5602_write_sensor(sd, address, &old_value, 1);
+	}
+}
diff --git a/drivers/media/usb/gspca/m5602/m5602_ov7660.h b/drivers/media/usb/gspca/m5602/m5602_ov7660.h
new file mode 100644
index 0000000..6fece1c
--- /dev/null
+++ b/drivers/media/usb/gspca/m5602/m5602_ov7660.h
@@ -0,0 +1,263 @@
+/*
+ * Driver for the ov7660 sensor
+ *
+ * Copyright (C) 2009 Erik Andrén
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * 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.
+ *
+ */
+
+#ifndef M5602_OV7660_H_
+#define M5602_OV7660_H_
+
+#include "m5602_sensor.h"
+
+#define OV7660_GAIN		0x00
+#define OV7660_BLUE_GAIN	0x01
+#define OV7660_RED_GAIN		0x02
+#define OV7660_VREF		0x03
+#define OV7660_COM1		0x04
+#define OV7660_BAVE		0x05
+#define OV7660_GEAVE		0x06
+#define OV7660_AECHH		0x07
+#define OV7660_RAVE		0x08
+#define OV7660_COM2		0x09
+#define OV7660_PID		0x0a
+#define OV7660_VER		0x0b
+#define OV7660_COM3		0x0c
+#define OV7660_COM4		0x0d
+#define OV7660_COM5		0x0e
+#define OV7660_COM6		0x0f
+#define OV7660_AECH		0x10
+#define OV7660_CLKRC		0x11
+#define OV7660_COM7		0x12
+#define OV7660_COM8		0x13
+#define OV7660_COM9		0x14
+#define OV7660_COM10		0x15
+#define OV7660_RSVD16		0x16
+#define OV7660_HSTART		0x17
+#define OV7660_HSTOP		0x18
+#define OV7660_VSTART		0x19
+#define OV7660_VSTOP		0x1a
+#define OV7660_PSHFT		0x1b
+#define OV7660_MIDH		0x1c
+#define OV7660_MIDL		0x1d
+#define OV7660_MVFP		0x1e
+#define OV7660_LAEC		0x1f
+#define OV7660_BOS		0x20
+#define OV7660_GBOS		0x21
+#define OV7660_GROS		0x22
+#define OV7660_ROS		0x23
+#define OV7660_AEW		0x24
+#define OV7660_AEB		0x25
+#define OV7660_VPT		0x26
+#define OV7660_BBIAS		0x27
+#define OV7660_GbBIAS		0x28
+#define OV7660_RSVD29		0x29
+#define OV7660_RBIAS		0x2c
+#define OV7660_HREF		0x32
+#define OV7660_ADC		0x37
+#define OV7660_OFON		0x39
+#define OV7660_TSLB		0x3a
+#define OV7660_COM12		0x3c
+#define OV7660_COM13		0x3d
+#define OV7660_LCC1		0x62
+#define OV7660_LCC2		0x63
+#define OV7660_LCC3		0x64
+#define OV7660_LCC4		0x65
+#define OV7660_LCC5		0x66
+#define OV7660_HV		0x69
+#define OV7660_RSVDA1		0xa1
+
+#define OV7660_DEFAULT_GAIN		0x0e
+#define OV7660_DEFAULT_RED_GAIN		0x80
+#define OV7660_DEFAULT_BLUE_GAIN	0x80
+#define OV7660_DEFAULT_SATURATION	0x00
+#define OV7660_DEFAULT_EXPOSURE		0x20
+
+/* Kernel module parameters */
+extern int force_sensor;
+extern bool dump_sensor;
+
+int ov7660_probe(struct sd *sd);
+int ov7660_init(struct sd *sd);
+int ov7660_init(struct sd *sd);
+int ov7660_init_controls(struct sd *sd);
+int ov7660_start(struct sd *sd);
+int ov7660_stop(struct sd *sd);
+void ov7660_disconnect(struct sd *sd);
+
+static const struct m5602_sensor ov7660 = {
+	.name = "ov7660",
+	.i2c_slave_id = 0x42,
+	.i2c_regW = 1,
+	.probe = ov7660_probe,
+	.init = ov7660_init,
+	.init_controls = ov7660_init_controls,
+	.start = ov7660_start,
+	.stop = ov7660_stop,
+	.disconnect = ov7660_disconnect,
+};
+
+static const unsigned char preinit_ov7660[][4] = {
+	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02},
+	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d},
+	{BRIDGE, M5602_XB_SENSOR_CTRL, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x03},
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x03},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
+
+	{SENSOR, OV7660_OFON, 0x0c},
+	{SENSOR, OV7660_COM2, 0x11},
+	{SENSOR, OV7660_COM7, 0x05},
+
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x01},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x04},
+	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
+	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x08},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+	{BRIDGE, M5602_XB_GPIO_EN_L, 0x00}
+};
+
+static const unsigned char init_ov7660[][4] = {
+	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02},
+	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d},
+	{BRIDGE, M5602_XB_SENSOR_CTRL, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x01},
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x01},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+	{BRIDGE, M5602_XB_GPIO_EN_L, 0x00},
+	{SENSOR, OV7660_COM7, 0x80},
+	{SENSOR, OV7660_CLKRC, 0x80},
+	{SENSOR, OV7660_COM9, 0x4c},
+	{SENSOR, OV7660_OFON, 0x43},
+	{SENSOR, OV7660_COM12, 0x28},
+	{SENSOR, OV7660_COM8, 0x00},
+	{SENSOR, OV7660_COM10, 0x40},
+	{SENSOR, OV7660_HSTART, 0x0c},
+	{SENSOR, OV7660_HSTOP, 0x61},
+	{SENSOR, OV7660_HREF, 0xa4},
+	{SENSOR, OV7660_PSHFT, 0x0b},
+	{SENSOR, OV7660_VSTART, 0x01},
+	{SENSOR, OV7660_VSTOP, 0x7a},
+	{SENSOR, OV7660_VSTOP, 0x00},
+	{SENSOR, OV7660_COM7, 0x05},
+	{SENSOR, OV7660_COM6, 0x42},
+	{SENSOR, OV7660_BBIAS, 0x94},
+	{SENSOR, OV7660_GbBIAS, 0x94},
+	{SENSOR, OV7660_RSVD29, 0x94},
+	{SENSOR, OV7660_RBIAS, 0x94},
+	{SENSOR, OV7660_COM1, 0x00},
+	{SENSOR, OV7660_AECH, 0x00},
+	{SENSOR, OV7660_AECHH, 0x00},
+	{SENSOR, OV7660_ADC, 0x05},
+	{SENSOR, OV7660_COM13, 0x00},
+	{SENSOR, OV7660_RSVDA1, 0x23},
+	{SENSOR, OV7660_TSLB, 0x0d},
+	{SENSOR, OV7660_HV, 0x80},
+	{SENSOR, OV7660_LCC1, 0x00},
+	{SENSOR, OV7660_LCC2, 0x00},
+	{SENSOR, OV7660_LCC3, 0x10},
+	{SENSOR, OV7660_LCC4, 0x40},
+	{SENSOR, OV7660_LCC5, 0x01},
+
+	{SENSOR, OV7660_AECH, 0x20},
+	{SENSOR, OV7660_COM1, 0x00},
+	{SENSOR, OV7660_OFON, 0x0c},
+	{SENSOR, OV7660_COM2, 0x11},
+	{SENSOR, OV7660_COM7, 0x05},
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x01},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x04},
+	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
+	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x08},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+	{BRIDGE, M5602_XB_GPIO_EN_L, 0x00},
+	{SENSOR, OV7660_AECH, 0x5f},
+	{SENSOR, OV7660_COM1, 0x03},
+	{SENSOR, OV7660_OFON, 0x0c},
+	{SENSOR, OV7660_COM2, 0x11},
+	{SENSOR, OV7660_COM7, 0x05},
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x01},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x04},
+	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
+	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x08},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+	{BRIDGE, M5602_XB_GPIO_EN_L, 0x00},
+
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
+	{BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81},
+	{BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82},
+	{BRIDGE, M5602_XB_SIG_INI, 0x01},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x08},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x01},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0xec},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x02},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x27},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x02},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0xa7},
+	{BRIDGE, M5602_XB_SIG_INI, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+};
+#endif
diff --git a/drivers/media/usb/gspca/m5602/m5602_ov9650.c b/drivers/media/usb/gspca/m5602/m5602_ov9650.c
new file mode 100644
index 0000000..59bc62b
--- /dev/null
+++ b/drivers/media/usb/gspca/m5602/m5602_ov9650.c
@@ -0,0 +1,636 @@
+/*
+ * Driver for the ov9650 sensor
+ *
+ * Copyright (C) 2008 Erik Andrén
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * 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.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "m5602_ov9650.h"
+
+static int ov9650_s_ctrl(struct v4l2_ctrl *ctrl);
+static void ov9650_dump_registers(struct sd *sd);
+
+/* Vertically and horizontally flips the image if matched, needed for machines
+   where the sensor is mounted upside down */
+static
+    const
+	struct dmi_system_id ov9650_flip_dmi_table[] = {
+	{
+		.ident = "ASUS A6Ja",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
+			DMI_MATCH(DMI_PRODUCT_NAME, "A6J")
+		}
+	},
+	{
+		.ident = "ASUS A6JC",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
+			DMI_MATCH(DMI_PRODUCT_NAME, "A6JC")
+		}
+	},
+	{
+		.ident = "ASUS A6K",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
+			DMI_MATCH(DMI_PRODUCT_NAME, "A6K")
+		}
+	},
+	{
+		.ident = "ASUS A6Kt",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
+			DMI_MATCH(DMI_PRODUCT_NAME, "A6Kt")
+		}
+	},
+	{
+		.ident = "ASUS A6VA",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
+			DMI_MATCH(DMI_PRODUCT_NAME, "A6VA")
+		}
+	},
+	{
+
+		.ident = "ASUS A6VC",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
+			DMI_MATCH(DMI_PRODUCT_NAME, "A6VC")
+		}
+	},
+	{
+		.ident = "ASUS A6VM",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
+			DMI_MATCH(DMI_PRODUCT_NAME, "A6VM")
+		}
+	},
+	{
+		.ident = "ASUS A7V",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
+			DMI_MATCH(DMI_PRODUCT_NAME, "A7V")
+		}
+	},
+	{
+		.ident = "Alienware Aurora m9700",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "alienware"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Aurora m9700")
+		}
+	},
+	{}
+};
+
+static struct v4l2_pix_format ov9650_modes[] = {
+	{
+		176,
+		144,
+		V4L2_PIX_FMT_SBGGR8,
+		V4L2_FIELD_NONE,
+		.sizeimage =
+			176 * 144,
+		.bytesperline = 176,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 9
+	}, {
+		320,
+		240,
+		V4L2_PIX_FMT_SBGGR8,
+		V4L2_FIELD_NONE,
+		.sizeimage =
+			320 * 240,
+		.bytesperline = 320,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 8
+	}, {
+		352,
+		288,
+		V4L2_PIX_FMT_SBGGR8,
+		V4L2_FIELD_NONE,
+		.sizeimage =
+			352 * 288,
+		.bytesperline = 352,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 9
+	}, {
+		640,
+		480,
+		V4L2_PIX_FMT_SBGGR8,
+		V4L2_FIELD_NONE,
+		.sizeimage =
+			640 * 480,
+		.bytesperline = 640,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 9
+	}
+};
+
+static const struct v4l2_ctrl_ops ov9650_ctrl_ops = {
+	.s_ctrl = ov9650_s_ctrl,
+};
+
+int ov9650_probe(struct sd *sd)
+{
+	int err = 0;
+	u8 prod_id = 0, ver_id = 0, i;
+	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
+
+	if (force_sensor) {
+		if (force_sensor == OV9650_SENSOR) {
+			pr_info("Forcing an %s sensor\n", ov9650.name);
+			goto sensor_found;
+		}
+		/* If we want to force another sensor,
+		   don't try to probe this one */
+		return -ENODEV;
+	}
+
+	PDEBUG(D_PROBE, "Probing for an ov9650 sensor");
+
+	/* Run the pre-init before probing the sensor */
+	for (i = 0; i < ARRAY_SIZE(preinit_ov9650) && !err; i++) {
+		u8 data = preinit_ov9650[i][2];
+		if (preinit_ov9650[i][0] == SENSOR)
+			err = m5602_write_sensor(sd,
+				preinit_ov9650[i][1], &data, 1);
+		else
+			err = m5602_write_bridge(sd,
+				preinit_ov9650[i][1], data);
+	}
+
+	if (err < 0)
+		return err;
+
+	if (m5602_read_sensor(sd, OV9650_PID, &prod_id, 1))
+		return -ENODEV;
+
+	if (m5602_read_sensor(sd, OV9650_VER, &ver_id, 1))
+		return -ENODEV;
+
+	if ((prod_id == 0x96) && (ver_id == 0x52)) {
+		pr_info("Detected an ov9650 sensor\n");
+		goto sensor_found;
+	}
+	return -ENODEV;
+
+sensor_found:
+	sd->gspca_dev.cam.cam_mode = ov9650_modes;
+	sd->gspca_dev.cam.nmodes = ARRAY_SIZE(ov9650_modes);
+
+	return 0;
+}
+
+int ov9650_init(struct sd *sd)
+{
+	int i, err = 0;
+	u8 data;
+
+	if (dump_sensor)
+		ov9650_dump_registers(sd);
+
+	for (i = 0; i < ARRAY_SIZE(init_ov9650) && !err; i++) {
+		data = init_ov9650[i][2];
+		if (init_ov9650[i][0] == SENSOR)
+			err = m5602_write_sensor(sd, init_ov9650[i][1],
+						  &data, 1);
+		else
+			err = m5602_write_bridge(sd, init_ov9650[i][1], data);
+	}
+
+	return 0;
+}
+
+int ov9650_init_controls(struct sd *sd)
+{
+	struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler;
+
+	sd->gspca_dev.vdev.ctrl_handler = hdl;
+	v4l2_ctrl_handler_init(hdl, 9);
+
+	sd->auto_white_bal = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops,
+					       V4L2_CID_AUTO_WHITE_BALANCE,
+					       0, 1, 1, 1);
+	sd->red_bal = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops,
+					V4L2_CID_RED_BALANCE, 0, 255, 1,
+					RED_GAIN_DEFAULT);
+	sd->blue_bal = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops,
+					V4L2_CID_BLUE_BALANCE, 0, 255, 1,
+					BLUE_GAIN_DEFAULT);
+
+	sd->autoexpo = v4l2_ctrl_new_std_menu(hdl, &ov9650_ctrl_ops,
+			  V4L2_CID_EXPOSURE_AUTO, 1, 0, V4L2_EXPOSURE_AUTO);
+	sd->expo = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops, V4L2_CID_EXPOSURE,
+			  0, 0x1ff, 4, EXPOSURE_DEFAULT);
+
+	sd->autogain = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops,
+					 V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+	sd->gain = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops, V4L2_CID_GAIN, 0,
+				     0x3ff, 1, GAIN_DEFAULT);
+
+	sd->hflip = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops, V4L2_CID_HFLIP,
+				      0, 1, 1, 0);
+	sd->vflip = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops, V4L2_CID_VFLIP,
+				      0, 1, 1, 0);
+
+	if (hdl->error) {
+		pr_err("Could not initialize controls\n");
+		return hdl->error;
+	}
+
+	v4l2_ctrl_auto_cluster(3, &sd->auto_white_bal, 0, false);
+	v4l2_ctrl_auto_cluster(2, &sd->autoexpo, 0, false);
+	v4l2_ctrl_auto_cluster(2, &sd->autogain, 0, false);
+	v4l2_ctrl_cluster(2, &sd->hflip);
+
+	return 0;
+}
+
+int ov9650_start(struct sd *sd)
+{
+	u8 data;
+	int i, err = 0;
+	struct cam *cam = &sd->gspca_dev.cam;
+
+	int width = cam->cam_mode[sd->gspca_dev.curr_mode].width;
+	int height = cam->cam_mode[sd->gspca_dev.curr_mode].height;
+	int ver_offs = cam->cam_mode[sd->gspca_dev.curr_mode].priv;
+	int hor_offs = OV9650_LEFT_OFFSET;
+	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
+
+	if ((!dmi_check_system(ov9650_flip_dmi_table) &&
+		sd->vflip->val) ||
+		(dmi_check_system(ov9650_flip_dmi_table) &&
+		!sd->vflip->val))
+		ver_offs--;
+
+	if (width <= 320)
+		hor_offs /= 2;
+
+	/* Synthesize the vsync/hsync setup */
+	for (i = 0; i < ARRAY_SIZE(res_init_ov9650) && !err; i++) {
+		if (res_init_ov9650[i][0] == BRIDGE)
+			err = m5602_write_bridge(sd, res_init_ov9650[i][1],
+				res_init_ov9650[i][2]);
+		else if (res_init_ov9650[i][0] == SENSOR) {
+			data = res_init_ov9650[i][2];
+			err = m5602_write_sensor(sd,
+				res_init_ov9650[i][1], &data, 1);
+		}
+	}
+	if (err < 0)
+		return err;
+
+	err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA,
+				 ((ver_offs >> 8) & 0xff));
+	if (err < 0)
+		return err;
+
+	err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (ver_offs & 0xff));
+	if (err < 0)
+		return err;
+
+	err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, 0);
+	if (err < 0)
+		return err;
+
+	err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (height >> 8) & 0xff);
+	if (err < 0)
+		return err;
+
+	err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (height & 0xff));
+	if (err < 0)
+		return err;
+
+	for (i = 0; i < 2 && !err; i++)
+		err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, 0);
+	if (err < 0)
+		return err;
+
+	err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0);
+	if (err < 0)
+		return err;
+
+	err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 2);
+	if (err < 0)
+		return err;
+
+	err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA,
+				 (hor_offs >> 8) & 0xff);
+	if (err < 0)
+		return err;
+
+	err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, hor_offs & 0xff);
+	if (err < 0)
+		return err;
+
+	err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA,
+				 ((width + hor_offs) >> 8) & 0xff);
+	if (err < 0)
+		return err;
+
+	err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA,
+				 ((width + hor_offs) & 0xff));
+	if (err < 0)
+		return err;
+
+	err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0);
+	if (err < 0)
+		return err;
+
+	switch (width) {
+	case 640:
+		PDEBUG(D_CONF, "Configuring camera for VGA mode");
+
+		data = OV9650_VGA_SELECT | OV9650_RGB_SELECT |
+		       OV9650_RAW_RGB_SELECT;
+		err = m5602_write_sensor(sd, OV9650_COM7, &data, 1);
+		break;
+
+	case 352:
+		PDEBUG(D_CONF, "Configuring camera for CIF mode");
+
+		data = OV9650_CIF_SELECT | OV9650_RGB_SELECT |
+				OV9650_RAW_RGB_SELECT;
+		err = m5602_write_sensor(sd, OV9650_COM7, &data, 1);
+		break;
+
+	case 320:
+		PDEBUG(D_CONF, "Configuring camera for QVGA mode");
+
+		data = OV9650_QVGA_SELECT | OV9650_RGB_SELECT |
+				OV9650_RAW_RGB_SELECT;
+		err = m5602_write_sensor(sd, OV9650_COM7, &data, 1);
+		break;
+
+	case 176:
+		PDEBUG(D_CONF, "Configuring camera for QCIF mode");
+
+		data = OV9650_QCIF_SELECT | OV9650_RGB_SELECT |
+			OV9650_RAW_RGB_SELECT;
+		err = m5602_write_sensor(sd, OV9650_COM7, &data, 1);
+		break;
+	}
+	return err;
+}
+
+int ov9650_stop(struct sd *sd)
+{
+	u8 data = OV9650_SOFT_SLEEP | OV9650_OUTPUT_DRIVE_2X;
+	return m5602_write_sensor(sd, OV9650_COM2, &data, 1);
+}
+
+void ov9650_disconnect(struct sd *sd)
+{
+	ov9650_stop(sd);
+
+	sd->sensor = NULL;
+}
+
+static int ov9650_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u8 i2c_data;
+	int err;
+
+	PDEBUG(D_CONF, "Set exposure to %d", val);
+
+	/* The 6 MSBs */
+	i2c_data = (val >> 10) & 0x3f;
+	err = m5602_write_sensor(sd, OV9650_AECHM,
+				  &i2c_data, 1);
+	if (err < 0)
+		return err;
+
+	/* The 8 middle bits */
+	i2c_data = (val >> 2) & 0xff;
+	err = m5602_write_sensor(sd, OV9650_AECH,
+				  &i2c_data, 1);
+	if (err < 0)
+		return err;
+
+	/* The 2 LSBs */
+	i2c_data = val & 0x03;
+	err = m5602_write_sensor(sd, OV9650_COM1, &i2c_data, 1);
+	return err;
+}
+
+static int ov9650_set_gain(struct gspca_dev *gspca_dev, __s32 val)
+{
+	int err;
+	u8 i2c_data;
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	PDEBUG(D_CONF, "Setting gain to %d", val);
+
+	/* The 2 MSB */
+	/* Read the OV9650_VREF register first to avoid
+	   corrupting the VREF high and low bits */
+	err = m5602_read_sensor(sd, OV9650_VREF, &i2c_data, 1);
+	if (err < 0)
+		return err;
+
+	/* Mask away all uninteresting bits */
+	i2c_data = ((val & 0x0300) >> 2) |
+			(i2c_data & 0x3f);
+	err = m5602_write_sensor(sd, OV9650_VREF, &i2c_data, 1);
+	if (err < 0)
+		return err;
+
+	/* The 8 LSBs */
+	i2c_data = val & 0xff;
+	err = m5602_write_sensor(sd, OV9650_GAIN, &i2c_data, 1);
+	return err;
+}
+
+static int ov9650_set_red_balance(struct gspca_dev *gspca_dev, __s32 val)
+{
+	int err;
+	u8 i2c_data;
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	PDEBUG(D_CONF, "Set red gain to %d", val);
+
+	i2c_data = val & 0xff;
+	err = m5602_write_sensor(sd, OV9650_RED, &i2c_data, 1);
+	return err;
+}
+
+static int ov9650_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val)
+{
+	int err;
+	u8 i2c_data;
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	PDEBUG(D_CONF, "Set blue gain to %d", val);
+
+	i2c_data = val & 0xff;
+	err = m5602_write_sensor(sd, OV9650_BLUE, &i2c_data, 1);
+	return err;
+}
+
+static int ov9650_set_hvflip(struct gspca_dev *gspca_dev)
+{
+	int err;
+	u8 i2c_data;
+	struct sd *sd = (struct sd *) gspca_dev;
+	int hflip = sd->hflip->val;
+	int vflip = sd->vflip->val;
+
+	PDEBUG(D_CONF, "Set hvflip to %d %d", hflip, vflip);
+
+	if (dmi_check_system(ov9650_flip_dmi_table))
+		vflip = !vflip;
+
+	i2c_data = (hflip << 5) | (vflip << 4);
+	err = m5602_write_sensor(sd, OV9650_MVFP, &i2c_data, 1);
+	if (err < 0)
+		return err;
+
+	/* When vflip is toggled we need to readjust the bridge hsync/vsync */
+	if (gspca_dev->streaming)
+		err = ov9650_start(sd);
+
+	return err;
+}
+
+static int ov9650_set_auto_exposure(struct gspca_dev *gspca_dev,
+				    __s32 val)
+{
+	int err;
+	u8 i2c_data;
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	PDEBUG(D_CONF, "Set auto exposure control to %d", val);
+
+	err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1);
+	if (err < 0)
+		return err;
+
+	val = (val == V4L2_EXPOSURE_AUTO);
+	i2c_data = ((i2c_data & 0xfe) | ((val & 0x01) << 0));
+
+	return m5602_write_sensor(sd, OV9650_COM8, &i2c_data, 1);
+}
+
+static int ov9650_set_auto_white_balance(struct gspca_dev *gspca_dev,
+					 __s32 val)
+{
+	int err;
+	u8 i2c_data;
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	PDEBUG(D_CONF, "Set auto white balance to %d", val);
+
+	err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1);
+	if (err < 0)
+		return err;
+
+	i2c_data = ((i2c_data & 0xfd) | ((val & 0x01) << 1));
+	err = m5602_write_sensor(sd, OV9650_COM8, &i2c_data, 1);
+
+	return err;
+}
+
+static int ov9650_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val)
+{
+	int err;
+	u8 i2c_data;
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	PDEBUG(D_CONF, "Set auto gain control to %d", val);
+
+	err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1);
+	if (err < 0)
+		return err;
+
+	i2c_data = ((i2c_data & 0xfb) | ((val & 0x01) << 2));
+
+	return m5602_write_sensor(sd, OV9650_COM8, &i2c_data, 1);
+}
+
+static int ov9650_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct gspca_dev *gspca_dev =
+		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+	struct sd *sd = (struct sd *) gspca_dev;
+	int err;
+
+	if (!gspca_dev->streaming)
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_AUTO_WHITE_BALANCE:
+		err = ov9650_set_auto_white_balance(gspca_dev, ctrl->val);
+		if (err || ctrl->val)
+			return err;
+		err = ov9650_set_red_balance(gspca_dev, sd->red_bal->val);
+		if (err)
+			return err;
+		err = ov9650_set_blue_balance(gspca_dev, sd->blue_bal->val);
+		break;
+	case V4L2_CID_EXPOSURE_AUTO:
+		err = ov9650_set_auto_exposure(gspca_dev, ctrl->val);
+		if (err || ctrl->val == V4L2_EXPOSURE_AUTO)
+			return err;
+		err = ov9650_set_exposure(gspca_dev, sd->expo->val);
+		break;
+	case V4L2_CID_AUTOGAIN:
+		err = ov9650_set_auto_gain(gspca_dev, ctrl->val);
+		if (err || ctrl->val)
+			return err;
+		err = ov9650_set_gain(gspca_dev, sd->gain->val);
+		break;
+	case V4L2_CID_HFLIP:
+		err = ov9650_set_hvflip(gspca_dev);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return err;
+}
+
+static void ov9650_dump_registers(struct sd *sd)
+{
+	int address;
+	pr_info("Dumping the ov9650 register state\n");
+	for (address = 0; address < 0xa9; address++) {
+		u8 value;
+		m5602_read_sensor(sd, address, &value, 1);
+		pr_info("register 0x%x contains 0x%x\n", address, value);
+	}
+
+	pr_info("ov9650 register state dump complete\n");
+
+	pr_info("Probing for which registers that are read/write\n");
+	for (address = 0; address < 0xff; address++) {
+		u8 old_value, ctrl_value;
+		u8 test_value[2] = {0xff, 0xff};
+
+		m5602_read_sensor(sd, address, &old_value, 1);
+		m5602_write_sensor(sd, address, test_value, 1);
+		m5602_read_sensor(sd, address, &ctrl_value, 1);
+
+		if (ctrl_value == test_value[0])
+			pr_info("register 0x%x is writeable\n", address);
+		else
+			pr_info("register 0x%x is read only\n", address);
+
+		/* Restore original value */
+		m5602_write_sensor(sd, address, &old_value, 1);
+	}
+}
diff --git a/drivers/media/usb/gspca/m5602/m5602_ov9650.h b/drivers/media/usb/gspca/m5602/m5602_ov9650.h
new file mode 100644
index 0000000..f9f5870
--- /dev/null
+++ b/drivers/media/usb/gspca/m5602/m5602_ov9650.h
@@ -0,0 +1,309 @@
+/*
+ * Driver for the ov9650 sensor
+ *
+ * Copyright (C) 2008 Erik Andrén
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * 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.
+ *
+ */
+
+#ifndef M5602_OV9650_H_
+#define M5602_OV9650_H_
+
+#include <linux/dmi.h>
+#include "m5602_sensor.h"
+
+/*****************************************************************************/
+
+#define OV9650_GAIN			0x00
+#define OV9650_BLUE			0x01
+#define OV9650_RED			0x02
+#define OV9650_VREF			0x03
+#define OV9650_COM1			0x04
+#define OV9650_BAVE			0x05
+#define OV9650_GEAVE			0x06
+#define OV9650_RSVD7			0x07
+#define OV9650_COM2			0x09
+#define OV9650_PID			0x0a
+#define OV9650_VER			0x0b
+#define OV9650_COM3			0x0c
+#define OV9650_COM4			0x0d
+#define OV9650_COM5			0x0e
+#define OV9650_COM6			0x0f
+#define OV9650_AECH			0x10
+#define OV9650_CLKRC			0x11
+#define OV9650_COM7			0x12
+#define OV9650_COM8			0x13
+#define OV9650_COM9			0x14
+#define OV9650_COM10			0x15
+#define OV9650_RSVD16			0x16
+#define OV9650_HSTART			0x17
+#define OV9650_HSTOP			0x18
+#define OV9650_VSTRT			0x19
+#define OV9650_VSTOP			0x1a
+#define OV9650_PSHFT			0x1b
+#define OV9650_MVFP			0x1e
+#define OV9650_AEW			0x24
+#define OV9650_AEB			0x25
+#define OV9650_VPT			0x26
+#define OV9650_BBIAS			0x27
+#define OV9650_GbBIAS			0x28
+#define OV9650_Gr_COM			0x29
+#define OV9650_RBIAS			0x2c
+#define OV9650_HREF			0x32
+#define OV9650_CHLF			0x33
+#define OV9650_ARBLM			0x34
+#define OV9650_RSVD35			0x35
+#define OV9650_RSVD36			0x36
+#define OV9650_ADC			0x37
+#define OV9650_ACOM38			0x38
+#define OV9650_OFON			0x39
+#define OV9650_TSLB			0x3a
+#define OV9650_COM12			0x3c
+#define OV9650_COM13			0x3d
+#define OV9650_COM15			0x40
+#define OV9650_COM16			0x41
+#define OV9650_LCC1			0x62
+#define OV9650_LCC2			0x63
+#define OV9650_LCC3			0x64
+#define OV9650_LCC4			0x65
+#define OV9650_LCC5			0x66
+#define OV9650_HV			0x69
+#define OV9650_DBLV			0x6b
+#define OV9650_COM21			0x8b
+#define OV9650_COM22			0x8c
+#define OV9650_COM24			0x8e
+#define OV9650_DBLC1			0x8f
+#define OV9650_RSVD94			0x94
+#define OV9650_RSVD95			0x95
+#define OV9650_RSVD96			0x96
+#define OV9650_LCCFB			0x9d
+#define OV9650_LCCFR			0x9e
+#define OV9650_AECHM			0xa1
+#define OV9650_COM26			0xa5
+#define OV9650_ACOMA8			0xa8
+#define OV9650_ACOMA9			0xa9
+
+#define OV9650_REGISTER_RESET		(1 << 7)
+#define OV9650_VGA_SELECT		(1 << 6)
+#define OV9650_CIF_SELECT		(1 << 5)
+#define OV9650_QVGA_SELECT		(1 << 4)
+#define OV9650_QCIF_SELECT		(1 << 3)
+#define OV9650_RGB_SELECT		(1 << 2)
+#define OV9650_RAW_RGB_SELECT		(1 << 0)
+
+#define OV9650_FAST_AGC_AEC		(1 << 7)
+#define OV9650_AEC_UNLIM_STEP_SIZE	(1 << 6)
+#define OV9650_BANDING			(1 << 5)
+#define OV9650_AGC_EN			(1 << 2)
+#define OV9650_AWB_EN			(1 << 1)
+#define OV9650_AEC_EN			(1 << 0)
+
+#define OV9650_VARIOPIXEL		(1 << 2)
+#define OV9650_SYSTEM_CLK_SEL		(1 << 7)
+#define OV9650_SLAM_MODE		(1 << 4)
+
+#define OV9650_QVGA_VARIOPIXEL		(1 << 7)
+
+#define OV9650_VFLIP			(1 << 4)
+#define OV9650_HFLIP			(1 << 5)
+
+#define OV9650_SOFT_SLEEP		(1 << 4)
+#define OV9650_OUTPUT_DRIVE_2X		(1 << 0)
+
+#define OV9650_DENOISE_ENABLE		(1 << 5)
+#define OV9650_WHITE_PIXEL_ENABLE	(1 << 1)
+#define OV9650_WHITE_PIXEL_OPTION	(1 << 0)
+
+#define OV9650_LEFT_OFFSET		0x62
+
+#define GAIN_DEFAULT			0x14
+#define RED_GAIN_DEFAULT		0x70
+#define BLUE_GAIN_DEFAULT		0x20
+#define EXPOSURE_DEFAULT		0x1ff
+
+/*****************************************************************************/
+
+/* Kernel module parameters */
+extern int force_sensor;
+extern bool dump_sensor;
+
+int ov9650_probe(struct sd *sd);
+int ov9650_init(struct sd *sd);
+int ov9650_init_controls(struct sd *sd);
+int ov9650_start(struct sd *sd);
+int ov9650_stop(struct sd *sd);
+void ov9650_disconnect(struct sd *sd);
+
+static const struct m5602_sensor ov9650 = {
+	.name = "OV9650",
+	.i2c_slave_id = 0x60,
+	.i2c_regW = 1,
+	.probe = ov9650_probe,
+	.init = ov9650_init,
+	.init_controls = ov9650_init_controls,
+	.start = ov9650_start,
+	.stop = ov9650_stop,
+	.disconnect = ov9650_disconnect,
+};
+
+static const unsigned char preinit_ov9650[][3] = {
+	/* [INITCAM] */
+	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02},
+	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+	{BRIDGE, M5602_XB_SENSOR_CTRL, 0x00},
+
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x08},
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x04},
+	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
+	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x00},
+	{BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a},
+	/* Reset chip */
+	{SENSOR, OV9650_COM7, OV9650_REGISTER_RESET},
+	/* Enable double clock */
+	{SENSOR, OV9650_CLKRC, 0x80},
+	/* Do something out of spec with the power */
+	{SENSOR, OV9650_OFON, 0x40}
+};
+
+static const unsigned char init_ov9650[][3] = {
+	/* [INITCAM] */
+	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02},
+	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+	{BRIDGE, M5602_XB_SENSOR_CTRL, 0x00},
+
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x08},
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x04},
+	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
+	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x00},
+	{BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a},
+
+	/* Reset chip */
+	{SENSOR, OV9650_COM7, OV9650_REGISTER_RESET},
+	/* One extra reset is needed in order to make the sensor behave
+	   properly when resuming from ram, could be a timing issue */
+	{SENSOR, OV9650_COM7, OV9650_REGISTER_RESET},
+
+	/* Enable double clock */
+	{SENSOR, OV9650_CLKRC, 0x80},
+	/* Do something out of spec with the power */
+	{SENSOR, OV9650_OFON, 0x40},
+
+	/* Set fast AGC/AEC algorithm with unlimited step size */
+	{SENSOR, OV9650_COM8, OV9650_FAST_AGC_AEC |
+			      OV9650_AEC_UNLIM_STEP_SIZE},
+
+	{SENSOR, OV9650_CHLF, 0x10},
+	{SENSOR, OV9650_ARBLM, 0xbf},
+	{SENSOR, OV9650_ACOM38, 0x81},
+	/* Turn off color matrix coefficient double option */
+	{SENSOR, OV9650_COM16, 0x00},
+	/* Enable color matrix for RGB/YUV, Delay Y channel,
+	set output Y/UV delay to 1 */
+	{SENSOR, OV9650_COM13, 0x19},
+	/* Enable digital BLC, Set output mode to U Y V Y */
+	{SENSOR, OV9650_TSLB, 0x0c},
+	/* Limit the AGC/AEC stable upper region */
+	{SENSOR, OV9650_COM24, 0x00},
+	/* Enable HREF and some out of spec things */
+	{SENSOR, OV9650_COM12, 0x73},
+	/* Set all DBLC offset signs to positive and
+	do some out of spec stuff */
+	{SENSOR, OV9650_DBLC1, 0xdf},
+	{SENSOR, OV9650_COM21, 0x06},
+	{SENSOR, OV9650_RSVD35, 0x91},
+	/* Necessary, no camera stream without it */
+	{SENSOR, OV9650_RSVD16, 0x06},
+	{SENSOR, OV9650_RSVD94, 0x99},
+	{SENSOR, OV9650_RSVD95, 0x99},
+	{SENSOR, OV9650_RSVD96, 0x04},
+	/* Enable full range output */
+	{SENSOR, OV9650_COM15, 0x0},
+	/* Enable HREF at optical black, enable ADBLC bias,
+	enable ADBLC, reset timings at format change */
+	{SENSOR, OV9650_COM6, 0x4b},
+	/* Subtract 32 from the B channel bias */
+	{SENSOR, OV9650_BBIAS, 0xa0},
+	/* Subtract 32 from the Gb channel bias */
+	{SENSOR, OV9650_GbBIAS, 0xa0},
+	/* Do not bypass the analog BLC and to some out of spec stuff */
+	{SENSOR, OV9650_Gr_COM, 0x00},
+	/* Subtract 32 from the R channel bias */
+	{SENSOR, OV9650_RBIAS, 0xa0},
+	/* Subtract 32 from the R channel bias */
+	{SENSOR, OV9650_RBIAS, 0x0},
+	{SENSOR, OV9650_COM26, 0x80},
+	{SENSOR, OV9650_ACOMA9, 0x98},
+	/* Set the AGC/AEC stable region upper limit */
+	{SENSOR, OV9650_AEW, 0x68},
+	/* Set the AGC/AEC stable region lower limit */
+	{SENSOR, OV9650_AEB, 0x5c},
+	/* Set the high and low limit nibbles to 3 */
+	{SENSOR, OV9650_VPT, 0xc3},
+	/* Set the Automatic Gain Ceiling (AGC) to 128x,
+	drop VSYNC at frame drop,
+	limit exposure timing,
+	drop frame when the AEC step is larger than the exposure gap */
+	{SENSOR, OV9650_COM9, 0x6e},
+	/* Set VSYNC negative, Set RESET to SLHS (slave mode horizontal sync)
+	and set PWDN to SLVS (slave mode vertical sync) */
+	{SENSOR, OV9650_COM10, 0x42},
+	/* Set horizontal column start high to default value */
+	{SENSOR, OV9650_HSTART, 0x1a}, /* 210 */
+	/* Set horizontal column end */
+	{SENSOR, OV9650_HSTOP, 0xbf}, /* 1534 */
+	/* Complementing register to the two writes above */
+	{SENSOR, OV9650_HREF, 0xb2},
+	/* Set vertical row start high bits */
+	{SENSOR, OV9650_VSTRT, 0x02},
+	/* Set vertical row end low bits */
+	{SENSOR, OV9650_VSTOP, 0x7e},
+	/* Set complementing vertical frame control */
+	{SENSOR, OV9650_VREF, 0x10},
+	{SENSOR, OV9650_ADC, 0x04},
+	{SENSOR, OV9650_HV, 0x40},
+
+	/* Enable denoise, and white-pixel erase */
+	{SENSOR, OV9650_COM22, OV9650_DENOISE_ENABLE |
+		 OV9650_WHITE_PIXEL_ENABLE |
+		 OV9650_WHITE_PIXEL_OPTION},
+
+	/* Enable VARIOPIXEL */
+	{SENSOR, OV9650_COM3, OV9650_VARIOPIXEL},
+	{SENSOR, OV9650_COM4, OV9650_QVGA_VARIOPIXEL},
+
+	/* Put the sensor in soft sleep mode */
+	{SENSOR, OV9650_COM2, OV9650_SOFT_SLEEP | OV9650_OUTPUT_DRIVE_2X},
+};
+
+static const unsigned char res_init_ov9650[][3] = {
+	{SENSOR, OV9650_COM2, OV9650_OUTPUT_DRIVE_2X},
+
+	{BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x82},
+	{BRIDGE, M5602_XB_LINE_OF_FRAME_L, 0x00},
+	{BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82},
+	{BRIDGE, M5602_XB_PIX_OF_LINE_L, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x01}
+};
+#endif
diff --git a/drivers/media/usb/gspca/m5602/m5602_po1030.c b/drivers/media/usb/gspca/m5602/m5602_po1030.c
new file mode 100644
index 0000000..4bf5c43
--- /dev/null
+++ b/drivers/media/usb/gspca/m5602/m5602_po1030.c
@@ -0,0 +1,518 @@
+/*
+ * Driver for the po1030 sensor
+ *
+ * Copyright (c) 2008 Erik Andrén
+ * Copyright (c) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (c) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * 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.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "m5602_po1030.h"
+
+static int po1030_s_ctrl(struct v4l2_ctrl *ctrl);
+static void po1030_dump_registers(struct sd *sd);
+
+static struct v4l2_pix_format po1030_modes[] = {
+	{
+		640,
+		480,
+		V4L2_PIX_FMT_SBGGR8,
+		V4L2_FIELD_NONE,
+		.sizeimage = 640 * 480,
+		.bytesperline = 640,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 2
+	}
+};
+
+static const struct v4l2_ctrl_ops po1030_ctrl_ops = {
+	.s_ctrl = po1030_s_ctrl,
+};
+
+static const struct v4l2_ctrl_config po1030_greenbal_cfg = {
+	.ops	= &po1030_ctrl_ops,
+	.id	= M5602_V4L2_CID_GREEN_BALANCE,
+	.name	= "Green Balance",
+	.type	= V4L2_CTRL_TYPE_INTEGER,
+	.min	= 0,
+	.max	= 255,
+	.step	= 1,
+	.def	= PO1030_GREEN_GAIN_DEFAULT,
+	.flags	= V4L2_CTRL_FLAG_SLIDER,
+};
+
+int po1030_probe(struct sd *sd)
+{
+	u8 dev_id_h = 0, i;
+	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
+
+	if (force_sensor) {
+		if (force_sensor == PO1030_SENSOR) {
+			pr_info("Forcing a %s sensor\n", po1030.name);
+			goto sensor_found;
+		}
+		/* If we want to force another sensor, don't try to probe this
+		 * one */
+		return -ENODEV;
+	}
+
+	PDEBUG(D_PROBE, "Probing for a po1030 sensor");
+
+	/* Run the pre-init to actually probe the unit */
+	for (i = 0; i < ARRAY_SIZE(preinit_po1030); i++) {
+		u8 data = preinit_po1030[i][2];
+		if (preinit_po1030[i][0] == SENSOR)
+			m5602_write_sensor(sd,
+				preinit_po1030[i][1], &data, 1);
+		else
+			m5602_write_bridge(sd, preinit_po1030[i][1], data);
+	}
+
+	if (m5602_read_sensor(sd, PO1030_DEVID_H, &dev_id_h, 1))
+		return -ENODEV;
+
+	if (dev_id_h == 0x30) {
+		pr_info("Detected a po1030 sensor\n");
+		goto sensor_found;
+	}
+	return -ENODEV;
+
+sensor_found:
+	sd->gspca_dev.cam.cam_mode = po1030_modes;
+	sd->gspca_dev.cam.nmodes = ARRAY_SIZE(po1030_modes);
+
+	return 0;
+}
+
+int po1030_init(struct sd *sd)
+{
+	int i, err = 0;
+
+	/* Init the sensor */
+	for (i = 0; i < ARRAY_SIZE(init_po1030) && !err; i++) {
+		u8 data[2] = {0x00, 0x00};
+
+		switch (init_po1030[i][0]) {
+		case BRIDGE:
+			err = m5602_write_bridge(sd,
+				init_po1030[i][1],
+				init_po1030[i][2]);
+			break;
+
+		case SENSOR:
+			data[0] = init_po1030[i][2];
+			err = m5602_write_sensor(sd,
+				init_po1030[i][1], data, 1);
+			break;
+
+		default:
+			pr_info("Invalid stream command, exiting init\n");
+			return -EINVAL;
+		}
+	}
+	if (err < 0)
+		return err;
+
+	if (dump_sensor)
+		po1030_dump_registers(sd);
+
+	return 0;
+}
+
+int po1030_init_controls(struct sd *sd)
+{
+	struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler;
+
+	sd->gspca_dev.vdev.ctrl_handler = hdl;
+	v4l2_ctrl_handler_init(hdl, 9);
+
+	sd->auto_white_bal = v4l2_ctrl_new_std(hdl, &po1030_ctrl_ops,
+					       V4L2_CID_AUTO_WHITE_BALANCE,
+					       0, 1, 1, 0);
+	sd->green_bal = v4l2_ctrl_new_custom(hdl, &po1030_greenbal_cfg, NULL);
+	sd->red_bal = v4l2_ctrl_new_std(hdl, &po1030_ctrl_ops,
+					V4L2_CID_RED_BALANCE, 0, 255, 1,
+					PO1030_RED_GAIN_DEFAULT);
+	sd->blue_bal = v4l2_ctrl_new_std(hdl, &po1030_ctrl_ops,
+					V4L2_CID_BLUE_BALANCE, 0, 255, 1,
+					PO1030_BLUE_GAIN_DEFAULT);
+
+	sd->autoexpo = v4l2_ctrl_new_std_menu(hdl, &po1030_ctrl_ops,
+			  V4L2_CID_EXPOSURE_AUTO, 1, 0, V4L2_EXPOSURE_MANUAL);
+	sd->expo = v4l2_ctrl_new_std(hdl, &po1030_ctrl_ops, V4L2_CID_EXPOSURE,
+			  0, 0x2ff, 1, PO1030_EXPOSURE_DEFAULT);
+
+	sd->gain = v4l2_ctrl_new_std(hdl, &po1030_ctrl_ops, V4L2_CID_GAIN, 0,
+				     0x4f, 1, PO1030_GLOBAL_GAIN_DEFAULT);
+
+	sd->hflip = v4l2_ctrl_new_std(hdl, &po1030_ctrl_ops, V4L2_CID_HFLIP,
+				      0, 1, 1, 0);
+	sd->vflip = v4l2_ctrl_new_std(hdl, &po1030_ctrl_ops, V4L2_CID_VFLIP,
+				      0, 1, 1, 0);
+
+	if (hdl->error) {
+		pr_err("Could not initialize controls\n");
+		return hdl->error;
+	}
+
+	v4l2_ctrl_auto_cluster(4, &sd->auto_white_bal, 0, false);
+	v4l2_ctrl_auto_cluster(2, &sd->autoexpo, 0, false);
+	v4l2_ctrl_cluster(2, &sd->hflip);
+
+	return 0;
+}
+
+int po1030_start(struct sd *sd)
+{
+	struct cam *cam = &sd->gspca_dev.cam;
+	int i, err = 0;
+	int width = cam->cam_mode[sd->gspca_dev.curr_mode].width;
+	int height = cam->cam_mode[sd->gspca_dev.curr_mode].height;
+	int ver_offs = cam->cam_mode[sd->gspca_dev.curr_mode].priv;
+	u8 data;
+
+	switch (width) {
+	case 320:
+		data = PO1030_SUBSAMPLING;
+		err = m5602_write_sensor(sd, PO1030_CONTROL3, &data, 1);
+		if (err < 0)
+			return err;
+
+		data = ((width + 3) >> 8) & 0xff;
+		err = m5602_write_sensor(sd, PO1030_WINDOWWIDTH_H, &data, 1);
+		if (err < 0)
+			return err;
+
+		data = (width + 3) & 0xff;
+		err = m5602_write_sensor(sd, PO1030_WINDOWWIDTH_L, &data, 1);
+		if (err < 0)
+			return err;
+
+		data = ((height + 1) >> 8) & 0xff;
+		err = m5602_write_sensor(sd, PO1030_WINDOWHEIGHT_H, &data, 1);
+		if (err < 0)
+			return err;
+
+		data = (height + 1) & 0xff;
+		err = m5602_write_sensor(sd, PO1030_WINDOWHEIGHT_L, &data, 1);
+
+		height += 6;
+		width -= 1;
+		break;
+
+	case 640:
+		data = 0;
+		err = m5602_write_sensor(sd, PO1030_CONTROL3, &data, 1);
+		if (err < 0)
+			return err;
+
+		data = ((width + 7) >> 8) & 0xff;
+		err = m5602_write_sensor(sd, PO1030_WINDOWWIDTH_H, &data, 1);
+		if (err < 0)
+			return err;
+
+		data = (width + 7) & 0xff;
+		err = m5602_write_sensor(sd, PO1030_WINDOWWIDTH_L, &data, 1);
+		if (err < 0)
+			return err;
+
+		data = ((height + 3) >> 8) & 0xff;
+		err = m5602_write_sensor(sd, PO1030_WINDOWHEIGHT_H, &data, 1);
+		if (err < 0)
+			return err;
+
+		data = (height + 3) & 0xff;
+		err = m5602_write_sensor(sd, PO1030_WINDOWHEIGHT_L, &data, 1);
+
+		height += 12;
+		width -= 2;
+		break;
+	}
+	err = m5602_write_bridge(sd, M5602_XB_SENSOR_TYPE, 0x0c);
+	if (err < 0)
+		return err;
+
+	err = m5602_write_bridge(sd, M5602_XB_LINE_OF_FRAME_H, 0x81);
+	if (err < 0)
+		return err;
+
+	err = m5602_write_bridge(sd, M5602_XB_PIX_OF_LINE_H, 0x82);
+	if (err < 0)
+		return err;
+
+	err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0x01);
+	if (err < 0)
+		return err;
+
+	err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA,
+				 ((ver_offs >> 8) & 0xff));
+	if (err < 0)
+		return err;
+
+	err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (ver_offs & 0xff));
+	if (err < 0)
+		return err;
+
+	for (i = 0; i < 2 && !err; i++)
+		err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, 0);
+	if (err < 0)
+		return err;
+
+	err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (height >> 8) & 0xff);
+	if (err < 0)
+		return err;
+
+	err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (height & 0xff));
+	if (err < 0)
+		return err;
+
+	for (i = 0; i < 2 && !err; i++)
+		err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, 0);
+
+	for (i = 0; i < 2 && !err; i++)
+		err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0);
+
+	for (i = 0; i < 2 && !err; i++)
+		err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, 0);
+	if (err < 0)
+		return err;
+
+	err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, (width >> 8) & 0xff);
+	if (err < 0)
+		return err;
+
+	err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, (width & 0xff));
+	if (err < 0)
+		return err;
+
+	err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0);
+	return err;
+}
+
+static int po1030_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u8 i2c_data;
+	int err;
+
+	PDEBUG(D_CONF, "Set exposure to %d", val & 0xffff);
+
+	i2c_data = ((val & 0xff00) >> 8);
+	PDEBUG(D_CONF, "Set exposure to high byte to 0x%x",
+	       i2c_data);
+
+	err = m5602_write_sensor(sd, PO1030_INTEGLINES_H,
+				  &i2c_data, 1);
+	if (err < 0)
+		return err;
+
+	i2c_data = (val & 0xff);
+	PDEBUG(D_CONF, "Set exposure to low byte to 0x%x",
+	       i2c_data);
+	err = m5602_write_sensor(sd, PO1030_INTEGLINES_M,
+				  &i2c_data, 1);
+
+	return err;
+}
+
+static int po1030_set_gain(struct gspca_dev *gspca_dev, __s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u8 i2c_data;
+	int err;
+
+	i2c_data = val & 0xff;
+	PDEBUG(D_CONF, "Set global gain to %d", i2c_data);
+	err = m5602_write_sensor(sd, PO1030_GLOBALGAIN,
+				 &i2c_data, 1);
+	return err;
+}
+
+static int po1030_set_hvflip(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u8 i2c_data;
+	int err;
+
+	PDEBUG(D_CONF, "Set hvflip %d %d", sd->hflip->val, sd->vflip->val);
+	err = m5602_read_sensor(sd, PO1030_CONTROL2, &i2c_data, 1);
+	if (err < 0)
+		return err;
+
+	i2c_data = (0x3f & i2c_data) | (sd->hflip->val << 7) |
+		   (sd->vflip->val << 6);
+
+	err = m5602_write_sensor(sd, PO1030_CONTROL2,
+				 &i2c_data, 1);
+
+	return err;
+}
+
+static int po1030_set_red_balance(struct gspca_dev *gspca_dev, __s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u8 i2c_data;
+	int err;
+
+	i2c_data = val & 0xff;
+	PDEBUG(D_CONF, "Set red gain to %d", i2c_data);
+	err = m5602_write_sensor(sd, PO1030_RED_GAIN,
+				  &i2c_data, 1);
+	return err;
+}
+
+static int po1030_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u8 i2c_data;
+	int err;
+
+	i2c_data = val & 0xff;
+	PDEBUG(D_CONF, "Set blue gain to %d", i2c_data);
+	err = m5602_write_sensor(sd, PO1030_BLUE_GAIN,
+				  &i2c_data, 1);
+
+	return err;
+}
+
+static int po1030_set_green_balance(struct gspca_dev *gspca_dev, __s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u8 i2c_data;
+	int err;
+
+	i2c_data = val & 0xff;
+	PDEBUG(D_CONF, "Set green gain to %d", i2c_data);
+
+	err = m5602_write_sensor(sd, PO1030_GREEN_1_GAIN,
+			   &i2c_data, 1);
+	if (err < 0)
+		return err;
+
+	return m5602_write_sensor(sd, PO1030_GREEN_2_GAIN,
+				 &i2c_data, 1);
+}
+
+static int po1030_set_auto_white_balance(struct gspca_dev *gspca_dev,
+					 __s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u8 i2c_data;
+	int err;
+
+	err = m5602_read_sensor(sd, PO1030_AUTOCTRL1, &i2c_data, 1);
+	if (err < 0)
+		return err;
+
+	PDEBUG(D_CONF, "Set auto white balance to %d", val);
+	i2c_data = (i2c_data & 0xfe) | (val & 0x01);
+	err = m5602_write_sensor(sd, PO1030_AUTOCTRL1, &i2c_data, 1);
+	return err;
+}
+
+static int po1030_set_auto_exposure(struct gspca_dev *gspca_dev,
+				    __s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u8 i2c_data;
+	int err;
+
+	err = m5602_read_sensor(sd, PO1030_AUTOCTRL1, &i2c_data, 1);
+	if (err < 0)
+		return err;
+
+	PDEBUG(D_CONF, "Set auto exposure to %d", val);
+	val = (val == V4L2_EXPOSURE_AUTO);
+	i2c_data = (i2c_data & 0xfd) | ((val & 0x01) << 1);
+	return m5602_write_sensor(sd, PO1030_AUTOCTRL1, &i2c_data, 1);
+}
+
+void po1030_disconnect(struct sd *sd)
+{
+	sd->sensor = NULL;
+}
+
+static int po1030_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct gspca_dev *gspca_dev =
+		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+	struct sd *sd = (struct sd *) gspca_dev;
+	int err;
+
+	if (!gspca_dev->streaming)
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_AUTO_WHITE_BALANCE:
+		err = po1030_set_auto_white_balance(gspca_dev, ctrl->val);
+		if (err || ctrl->val)
+			return err;
+		err = po1030_set_green_balance(gspca_dev, sd->green_bal->val);
+		if (err)
+			return err;
+		err = po1030_set_red_balance(gspca_dev, sd->red_bal->val);
+		if (err)
+			return err;
+		err = po1030_set_blue_balance(gspca_dev, sd->blue_bal->val);
+		break;
+	case V4L2_CID_EXPOSURE_AUTO:
+		err = po1030_set_auto_exposure(gspca_dev, ctrl->val);
+		if (err || ctrl->val == V4L2_EXPOSURE_AUTO)
+			return err;
+		err = po1030_set_exposure(gspca_dev, sd->expo->val);
+		break;
+	case V4L2_CID_GAIN:
+		err = po1030_set_gain(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_HFLIP:
+		err = po1030_set_hvflip(gspca_dev);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return err;
+}
+
+static void po1030_dump_registers(struct sd *sd)
+{
+	int address;
+	u8 value = 0;
+
+	pr_info("Dumping the po1030 sensor core registers\n");
+	for (address = 0; address < 0x7f; address++) {
+		m5602_read_sensor(sd, address, &value, 1);
+		pr_info("register 0x%x contains 0x%x\n", address, value);
+	}
+
+	pr_info("po1030 register state dump complete\n");
+
+	pr_info("Probing for which registers that are read/write\n");
+	for (address = 0; address < 0xff; address++) {
+		u8 old_value, ctrl_value;
+		u8 test_value[2] = {0xff, 0xff};
+
+		m5602_read_sensor(sd, address, &old_value, 1);
+		m5602_write_sensor(sd, address, test_value, 1);
+		m5602_read_sensor(sd, address, &ctrl_value, 1);
+
+		if (ctrl_value == test_value[0])
+			pr_info("register 0x%x is writeable\n", address);
+		else
+			pr_info("register 0x%x is read only\n", address);
+
+		/* Restore original value */
+		m5602_write_sensor(sd, address, &old_value, 1);
+	}
+}
diff --git a/drivers/media/usb/gspca/m5602/m5602_po1030.h b/drivers/media/usb/gspca/m5602/m5602_po1030.h
new file mode 100644
index 0000000..a6ab761
--- /dev/null
+++ b/drivers/media/usb/gspca/m5602/m5602_po1030.h
@@ -0,0 +1,274 @@
+/*
+ * Driver for the po1030 sensor.
+ *
+ * Copyright (c) 2008 Erik Andrén
+ * Copyright (c) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (c) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * Register defines taken from Pascal Stangs Procyon Armlib
+ *
+ * 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.
+ *
+ */
+
+#ifndef M5602_PO1030_H_
+#define M5602_PO1030_H_
+
+#include "m5602_sensor.h"
+
+/*****************************************************************************/
+
+#define PO1030_DEVID_H		0x00
+#define PO1030_DEVID_L		0x01
+#define PO1030_FRAMEWIDTH_H	0x04
+#define PO1030_FRAMEWIDTH_L	0x05
+#define PO1030_FRAMEHEIGHT_H	0x06
+#define PO1030_FRAMEHEIGHT_L	0x07
+#define PO1030_WINDOWX_H	0x08
+#define PO1030_WINDOWX_L	0x09
+#define PO1030_WINDOWY_H	0x0a
+#define PO1030_WINDOWY_L	0x0b
+#define PO1030_WINDOWWIDTH_H	0x0c
+#define PO1030_WINDOWWIDTH_L	0x0d
+#define PO1030_WINDOWHEIGHT_H	0x0e
+#define PO1030_WINDOWHEIGHT_L	0x0f
+
+#define PO1030_GLOBALIBIAS	0x12
+#define PO1030_PIXELIBIAS	0x13
+
+#define PO1030_GLOBALGAIN	0x15
+#define PO1030_RED_GAIN		0x16
+#define PO1030_GREEN_1_GAIN	0x17
+#define PO1030_BLUE_GAIN	0x18
+#define PO1030_GREEN_2_GAIN	0x19
+
+#define PO1030_INTEGLINES_H	0x1a
+#define PO1030_INTEGLINES_M	0x1b
+#define PO1030_INTEGLINES_L	0x1c
+
+#define PO1030_CONTROL1		0x1d
+#define PO1030_CONTROL2		0x1e
+#define PO1030_CONTROL3		0x1f
+#define PO1030_CONTROL4		0x20
+
+#define PO1030_PERIOD50_H	0x23
+#define PO1030_PERIOD50_L	0x24
+#define PO1030_PERIOD60_H	0x25
+#define PO1030_PERIOD60_L	0x26
+#define PO1030_REGCLK167	0x27
+#define PO1030_FLICKER_DELTA50	0x28
+#define PO1030_FLICKERDELTA60	0x29
+
+#define PO1030_ADCOFFSET	0x2c
+
+/* Gamma Correction Coeffs */
+#define PO1030_GC0		0x2d
+#define PO1030_GC1		0x2e
+#define PO1030_GC2		0x2f
+#define PO1030_GC3		0x30
+#define PO1030_GC4		0x31
+#define PO1030_GC5		0x32
+#define PO1030_GC6		0x33
+#define PO1030_GC7		0x34
+
+/* Color Transform Matrix */
+#define PO1030_CT0		0x35
+#define PO1030_CT1		0x36
+#define PO1030_CT2		0x37
+#define PO1030_CT3		0x38
+#define PO1030_CT4		0x39
+#define PO1030_CT5		0x3a
+#define PO1030_CT6		0x3b
+#define PO1030_CT7		0x3c
+#define PO1030_CT8		0x3d
+
+#define PO1030_AUTOCTRL1	0x3e
+#define PO1030_AUTOCTRL2	0x3f
+
+#define PO1030_YTARGET		0x40
+#define PO1030_GLOBALGAINMIN	0x41
+#define PO1030_GLOBALGAINMAX	0x42
+
+#define PO1030_AWB_RED_TUNING	0x47
+#define PO1030_AWB_BLUE_TUNING	0x48
+
+/* Output format control */
+#define PO1030_OUTFORMCTRL1	0x5a
+#define PO1030_OUTFORMCTRL2	0x5b
+#define PO1030_OUTFORMCTRL3	0x5c
+#define PO1030_OUTFORMCTRL4	0x5d
+#define PO1030_OUTFORMCTRL5	0x5e
+
+#define PO1030_EDGE_ENH_OFF	0x5f
+#define PO1030_EGA		0x60
+
+#define PO1030_Cb_U_GAIN	0x63
+#define PO1030_Cr_V_GAIN	0x64
+
+#define PO1030_YCONTRAST	0x74
+#define PO1030_YSATURATION	0x75
+
+#define PO1030_HFLIP		(1 << 7)
+#define PO1030_VFLIP		(1 << 6)
+
+#define PO1030_HREF_ENABLE	(1 << 6)
+
+#define PO1030_RAW_RGB_BAYER	0x4
+
+#define PO1030_FRAME_EQUAL	(1 << 3)
+#define PO1030_AUTO_SUBSAMPLING (1 << 4)
+
+#define PO1030_WEIGHT_WIN_2X	(1 << 3)
+
+#define PO1030_SHUTTER_MODE	(1 << 6)
+#define PO1030_AUTO_SUBSAMPLING	(1 << 4)
+#define PO1030_FRAME_EQUAL	(1 << 3)
+
+#define PO1030_SENSOR_RESET	(1 << 5)
+
+#define PO1030_SUBSAMPLING	(1 << 6)
+
+/*****************************************************************************/
+
+#define PO1030_GLOBAL_GAIN_DEFAULT	0x12
+#define PO1030_EXPOSURE_DEFAULT		0x0085
+#define PO1030_BLUE_GAIN_DEFAULT	0x36
+#define PO1030_RED_GAIN_DEFAULT		0x36
+#define PO1030_GREEN_GAIN_DEFAULT	0x40
+
+/*****************************************************************************/
+
+/* Kernel module parameters */
+extern int force_sensor;
+extern bool dump_sensor;
+
+int po1030_probe(struct sd *sd);
+int po1030_init(struct sd *sd);
+int po1030_init_controls(struct sd *sd);
+int po1030_start(struct sd *sd);
+void po1030_disconnect(struct sd *sd);
+
+static const struct m5602_sensor po1030 = {
+	.name = "PO1030",
+
+	.i2c_slave_id = 0xdc,
+	.i2c_regW = 1,
+
+	.probe = po1030_probe,
+	.init = po1030_init,
+	.init_controls = po1030_init_controls,
+	.start = po1030_start,
+	.disconnect = po1030_disconnect,
+};
+
+static const unsigned char preinit_po1030[][3] = {
+	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02},
+	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+	{BRIDGE, M5602_XB_SENSOR_CTRL, 0x00},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x04},
+	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
+	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x02},
+
+	{SENSOR, PO1030_AUTOCTRL2, PO1030_SENSOR_RESET | (1 << 2)},
+
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x00}
+};
+
+static const unsigned char init_po1030[][3] = {
+	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02},
+	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+	{BRIDGE, M5602_XB_SENSOR_CTRL, 0x00},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
+
+	{SENSOR, PO1030_AUTOCTRL2, PO1030_SENSOR_RESET | (1 << 2)},
+
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x04},
+	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+	{BRIDGE, M5602_XB_GPIO_EN_L, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
+	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x02},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x00},
+
+	{SENSOR, PO1030_AUTOCTRL2, 0x04},
+
+	{SENSOR, PO1030_OUTFORMCTRL2, PO1030_RAW_RGB_BAYER},
+	{SENSOR, PO1030_AUTOCTRL1, PO1030_WEIGHT_WIN_2X},
+
+	{SENSOR, PO1030_CONTROL2, 0x03},
+	{SENSOR, 0x21, 0x90},
+	{SENSOR, PO1030_YTARGET, 0x60},
+	{SENSOR, 0x59, 0x13},
+	{SENSOR, PO1030_OUTFORMCTRL1, PO1030_HREF_ENABLE},
+	{SENSOR, PO1030_EDGE_ENH_OFF, 0x00},
+	{SENSOR, PO1030_EGA, 0x80},
+	{SENSOR, 0x78, 0x14},
+	{SENSOR, 0x6f, 0x01},
+	{SENSOR, PO1030_GLOBALGAINMAX, 0x14},
+	{SENSOR, PO1030_Cb_U_GAIN, 0x38},
+	{SENSOR, PO1030_Cr_V_GAIN, 0x38},
+	{SENSOR, PO1030_CONTROL1, PO1030_SHUTTER_MODE |
+				  PO1030_AUTO_SUBSAMPLING |
+				  PO1030_FRAME_EQUAL},
+	{SENSOR, PO1030_GC0, 0x10},
+	{SENSOR, PO1030_GC1, 0x20},
+	{SENSOR, PO1030_GC2, 0x40},
+	{SENSOR, PO1030_GC3, 0x60},
+	{SENSOR, PO1030_GC4, 0x80},
+	{SENSOR, PO1030_GC5, 0xa0},
+	{SENSOR, PO1030_GC6, 0xc0},
+	{SENSOR, PO1030_GC7, 0xff},
+
+	/* Set the width to 751 */
+	{SENSOR, PO1030_FRAMEWIDTH_H, 0x02},
+	{SENSOR, PO1030_FRAMEWIDTH_L, 0xef},
+
+	/* Set the height to 540 */
+	{SENSOR, PO1030_FRAMEHEIGHT_H, 0x02},
+	{SENSOR, PO1030_FRAMEHEIGHT_L, 0x1c},
+
+	/* Set the x window to 1 */
+	{SENSOR, PO1030_WINDOWX_H, 0x00},
+	{SENSOR, PO1030_WINDOWX_L, 0x01},
+
+	/* Set the y window to 1 */
+	{SENSOR, PO1030_WINDOWY_H, 0x00},
+	{SENSOR, PO1030_WINDOWY_L, 0x01},
+
+	/* with a very low lighted environment increase the exposure but
+	 * decrease the FPS (Frame Per Second) */
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+	{BRIDGE, M5602_XB_GPIO_EN_L, 0x00},
+};
+#endif
diff --git a/drivers/media/usb/gspca/m5602/m5602_s5k4aa.c b/drivers/media/usb/gspca/m5602/m5602_s5k4aa.c
new file mode 100644
index 0000000..7d12599
--- /dev/null
+++ b/drivers/media/usb/gspca/m5602/m5602_s5k4aa.c
@@ -0,0 +1,564 @@
+/*
+ * Driver for the s5k4aa sensor
+ *
+ * Copyright (C) 2008 Erik Andrén
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * 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.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "m5602_s5k4aa.h"
+
+static int s5k4aa_s_ctrl(struct v4l2_ctrl *ctrl);
+static void s5k4aa_dump_registers(struct sd *sd);
+
+static const struct v4l2_ctrl_ops s5k4aa_ctrl_ops = {
+	.s_ctrl = s5k4aa_s_ctrl,
+};
+
+static
+    const
+	struct dmi_system_id s5k4aa_vflip_dmi_table[] = {
+	{
+		.ident = "BRUNEINIT",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "BRUNENIT"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "BRUNENIT"),
+			DMI_MATCH(DMI_BOARD_VERSION, "00030D0000000001")
+		}
+	}, {
+		.ident = "Fujitsu-Siemens Amilo Xa 2528",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xa 2528")
+		}
+	}, {
+		.ident = "Fujitsu-Siemens Amilo Xi 2428",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xi 2428")
+		}
+	}, {
+		.ident = "Fujitsu-Siemens Amilo Xi 2528",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xi 2528")
+		}
+	}, {
+		.ident = "Fujitsu-Siemens Amilo Xi 2550",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xi 2550")
+		}
+	}, {
+		.ident = "Fujitsu-Siemens Amilo Pa 2548",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pa 2548")
+		}
+	}, {
+		.ident = "Fujitsu-Siemens Amilo Pi 2530",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pi 2530")
+		}
+	}, {
+		.ident = "MSI GX700",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "GX700"),
+			DMI_MATCH(DMI_BIOS_DATE, "12/02/2008")
+		}
+	}, {
+		.ident = "MSI GX700",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "GX700"),
+			DMI_MATCH(DMI_BIOS_DATE, "07/26/2007")
+		}
+	}, {
+		.ident = "MSI GX700",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "GX700"),
+			DMI_MATCH(DMI_BIOS_DATE, "07/19/2007")
+		}
+	}, {
+		.ident = "MSI GX700/GX705/EX700",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "GX700/GX705/EX700")
+		}
+	}, {
+		.ident = "MSI L735",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "MS-1717X")
+		}
+	}, {
+		.ident = "Lenovo Y300",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "L3000 Y300"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Y300")
+		}
+	},
+	{ }
+};
+
+static struct v4l2_pix_format s5k4aa_modes[] = {
+	{
+		640,
+		480,
+		V4L2_PIX_FMT_SBGGR8,
+		V4L2_FIELD_NONE,
+		.sizeimage =
+			640 * 480,
+		.bytesperline = 640,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 0
+	},
+	{
+		1280,
+		1024,
+		V4L2_PIX_FMT_SBGGR8,
+		V4L2_FIELD_NONE,
+		.sizeimage =
+			1280 * 1024,
+		.bytesperline = 1280,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 0
+	}
+};
+
+int s5k4aa_probe(struct sd *sd)
+{
+	u8 prod_id[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+	const u8 expected_prod_id[6] = {0x00, 0x10, 0x00, 0x4b, 0x33, 0x75};
+	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
+	int i, err = 0;
+
+	if (force_sensor) {
+		if (force_sensor == S5K4AA_SENSOR) {
+			pr_info("Forcing a %s sensor\n", s5k4aa.name);
+			goto sensor_found;
+		}
+		/* If we want to force another sensor, don't try to probe this
+		 * one */
+		return -ENODEV;
+	}
+
+	PDEBUG(D_PROBE, "Probing for a s5k4aa sensor");
+
+	/* Preinit the sensor */
+	for (i = 0; i < ARRAY_SIZE(preinit_s5k4aa) && !err; i++) {
+		u8 data[2] = {0x00, 0x00};
+
+		switch (preinit_s5k4aa[i][0]) {
+		case BRIDGE:
+			err = m5602_write_bridge(sd,
+						 preinit_s5k4aa[i][1],
+						 preinit_s5k4aa[i][2]);
+			break;
+
+		case SENSOR:
+			data[0] = preinit_s5k4aa[i][2];
+			err = m5602_write_sensor(sd,
+						  preinit_s5k4aa[i][1],
+						  data, 1);
+			break;
+
+		case SENSOR_LONG:
+			data[0] = preinit_s5k4aa[i][2];
+			data[1] = preinit_s5k4aa[i][3];
+			err = m5602_write_sensor(sd,
+						  preinit_s5k4aa[i][1],
+						  data, 2);
+			break;
+		default:
+			pr_info("Invalid stream command, exiting init\n");
+			return -EINVAL;
+		}
+	}
+
+	/* Test some registers, but we don't know their exact meaning yet */
+	if (m5602_read_sensor(sd, 0x00, prod_id, 2))
+		return -ENODEV;
+	if (m5602_read_sensor(sd, 0x02, prod_id+2, 2))
+		return -ENODEV;
+	if (m5602_read_sensor(sd, 0x04, prod_id+4, 2))
+		return -ENODEV;
+
+	if (memcmp(prod_id, expected_prod_id, sizeof(prod_id)))
+		return -ENODEV;
+	else
+		pr_info("Detected a s5k4aa sensor\n");
+
+sensor_found:
+	sd->gspca_dev.cam.cam_mode = s5k4aa_modes;
+	sd->gspca_dev.cam.nmodes = ARRAY_SIZE(s5k4aa_modes);
+
+	return 0;
+}
+
+int s5k4aa_start(struct sd *sd)
+{
+	int i, err = 0;
+	u8 data[2];
+	struct cam *cam = &sd->gspca_dev.cam;
+	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
+
+	switch (cam->cam_mode[sd->gspca_dev.curr_mode].width) {
+	case 1280:
+		PDEBUG(D_CONF, "Configuring camera for SXGA mode");
+
+		for (i = 0; i < ARRAY_SIZE(SXGA_s5k4aa); i++) {
+			switch (SXGA_s5k4aa[i][0]) {
+			case BRIDGE:
+				err = m5602_write_bridge(sd,
+						 SXGA_s5k4aa[i][1],
+						 SXGA_s5k4aa[i][2]);
+			break;
+
+			case SENSOR:
+				data[0] = SXGA_s5k4aa[i][2];
+				err = m5602_write_sensor(sd,
+						 SXGA_s5k4aa[i][1],
+						 data, 1);
+			break;
+
+			case SENSOR_LONG:
+				data[0] = SXGA_s5k4aa[i][2];
+				data[1] = SXGA_s5k4aa[i][3];
+				err = m5602_write_sensor(sd,
+						  SXGA_s5k4aa[i][1],
+						  data, 2);
+			break;
+
+			default:
+				pr_err("Invalid stream command, exiting init\n");
+				return -EINVAL;
+			}
+		}
+		break;
+
+	case 640:
+		PDEBUG(D_CONF, "Configuring camera for VGA mode");
+
+		for (i = 0; i < ARRAY_SIZE(VGA_s5k4aa); i++) {
+			switch (VGA_s5k4aa[i][0]) {
+			case BRIDGE:
+				err = m5602_write_bridge(sd,
+						 VGA_s5k4aa[i][1],
+						 VGA_s5k4aa[i][2]);
+			break;
+
+			case SENSOR:
+				data[0] = VGA_s5k4aa[i][2];
+				err = m5602_write_sensor(sd,
+						 VGA_s5k4aa[i][1],
+						 data, 1);
+			break;
+
+			case SENSOR_LONG:
+				data[0] = VGA_s5k4aa[i][2];
+				data[1] = VGA_s5k4aa[i][3];
+				err = m5602_write_sensor(sd,
+						  VGA_s5k4aa[i][1],
+						  data, 2);
+			break;
+
+			default:
+				pr_err("Invalid stream command, exiting init\n");
+				return -EINVAL;
+			}
+		}
+		break;
+	}
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+
+int s5k4aa_init(struct sd *sd)
+{
+	int i, err = 0;
+
+	for (i = 0; i < ARRAY_SIZE(init_s5k4aa) && !err; i++) {
+		u8 data[2] = {0x00, 0x00};
+
+		switch (init_s5k4aa[i][0]) {
+		case BRIDGE:
+			err = m5602_write_bridge(sd,
+				init_s5k4aa[i][1],
+				init_s5k4aa[i][2]);
+			break;
+
+		case SENSOR:
+			data[0] = init_s5k4aa[i][2];
+			err = m5602_write_sensor(sd,
+				init_s5k4aa[i][1], data, 1);
+			break;
+
+		case SENSOR_LONG:
+			data[0] = init_s5k4aa[i][2];
+			data[1] = init_s5k4aa[i][3];
+			err = m5602_write_sensor(sd,
+				init_s5k4aa[i][1], data, 2);
+			break;
+		default:
+			pr_info("Invalid stream command, exiting init\n");
+			return -EINVAL;
+		}
+	}
+
+	if (dump_sensor)
+		s5k4aa_dump_registers(sd);
+
+	return err;
+}
+
+int s5k4aa_init_controls(struct sd *sd)
+{
+	struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler;
+
+	sd->gspca_dev.vdev.ctrl_handler = hdl;
+	v4l2_ctrl_handler_init(hdl, 6);
+
+	v4l2_ctrl_new_std(hdl, &s5k4aa_ctrl_ops, V4L2_CID_BRIGHTNESS,
+			  0, 0x1f, 1, S5K4AA_DEFAULT_BRIGHTNESS);
+
+	v4l2_ctrl_new_std(hdl, &s5k4aa_ctrl_ops, V4L2_CID_EXPOSURE,
+			  13, 0xfff, 1, 0x100);
+
+	v4l2_ctrl_new_std(hdl, &s5k4aa_ctrl_ops, V4L2_CID_GAIN,
+			  0, 127, 1, S5K4AA_DEFAULT_GAIN);
+
+	v4l2_ctrl_new_std(hdl, &s5k4aa_ctrl_ops, V4L2_CID_SHARPNESS,
+			  0, 1, 1, 1);
+
+	sd->hflip = v4l2_ctrl_new_std(hdl, &s5k4aa_ctrl_ops, V4L2_CID_HFLIP,
+				      0, 1, 1, 0);
+	sd->vflip = v4l2_ctrl_new_std(hdl, &s5k4aa_ctrl_ops, V4L2_CID_VFLIP,
+				      0, 1, 1, 0);
+
+	if (hdl->error) {
+		pr_err("Could not initialize controls\n");
+		return hdl->error;
+	}
+
+	v4l2_ctrl_cluster(2, &sd->hflip);
+
+	return 0;
+}
+
+static int s5k4aa_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u8 data = S5K4AA_PAGE_MAP_2;
+	int err;
+
+	PDEBUG(D_CONF, "Set exposure to %d", val);
+	err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+	if (err < 0)
+		return err;
+	data = (val >> 8) & 0xff;
+	err = m5602_write_sensor(sd, S5K4AA_EXPOSURE_HI, &data, 1);
+	if (err < 0)
+		return err;
+	data = val & 0xff;
+	err = m5602_write_sensor(sd, S5K4AA_EXPOSURE_LO, &data, 1);
+
+	return err;
+}
+
+static int s5k4aa_set_hvflip(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u8 data = S5K4AA_PAGE_MAP_2;
+	int err;
+	int hflip = sd->hflip->val;
+	int vflip = sd->vflip->val;
+
+	PDEBUG(D_CONF, "Set hvflip %d %d", hflip, vflip);
+	err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+	if (err < 0)
+		return err;
+
+	err = m5602_read_sensor(sd, S5K4AA_READ_MODE, &data, 1);
+	if (err < 0)
+		return err;
+
+	if (dmi_check_system(s5k4aa_vflip_dmi_table)) {
+		hflip = !hflip;
+		vflip = !vflip;
+	}
+
+	data = (data & 0x7f) | (vflip << 7) | (hflip << 6);
+	err = m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1);
+	if (err < 0)
+		return err;
+
+	err = m5602_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
+	if (err < 0)
+		return err;
+	if (hflip)
+		data &= 0xfe;
+	else
+		data |= 0x01;
+	err = m5602_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
+	if (err < 0)
+		return err;
+
+	err = m5602_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
+	if (err < 0)
+		return err;
+	if (vflip)
+		data &= 0xfe;
+	else
+		data |= 0x01;
+	err = m5602_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+
+static int s5k4aa_set_gain(struct gspca_dev *gspca_dev, __s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u8 data = S5K4AA_PAGE_MAP_2;
+	int err;
+
+	PDEBUG(D_CONF, "Set gain to %d", val);
+	err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+	if (err < 0)
+		return err;
+
+	data = val & 0xff;
+	err = m5602_write_sensor(sd, S5K4AA_GAIN, &data, 1);
+
+	return err;
+}
+
+static int s5k4aa_set_brightness(struct gspca_dev *gspca_dev, __s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u8 data = S5K4AA_PAGE_MAP_2;
+	int err;
+
+	PDEBUG(D_CONF, "Set brightness to %d", val);
+	err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+	if (err < 0)
+		return err;
+
+	data = val & 0xff;
+	return m5602_write_sensor(sd, S5K4AA_BRIGHTNESS, &data, 1);
+}
+
+static int s5k4aa_set_noise(struct gspca_dev *gspca_dev, __s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u8 data = S5K4AA_PAGE_MAP_2;
+	int err;
+
+	PDEBUG(D_CONF, "Set noise to %d", val);
+	err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+	if (err < 0)
+		return err;
+
+	data = val & 0x01;
+	return m5602_write_sensor(sd, S5K4AA_NOISE_SUPP, &data, 1);
+}
+
+static int s5k4aa_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct gspca_dev *gspca_dev =
+		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+	int err;
+
+	if (!gspca_dev->streaming)
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_BRIGHTNESS:
+		err = s5k4aa_set_brightness(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_EXPOSURE:
+		err = s5k4aa_set_exposure(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_GAIN:
+		err = s5k4aa_set_gain(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_SHARPNESS:
+		err = s5k4aa_set_noise(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_HFLIP:
+		err = s5k4aa_set_hvflip(gspca_dev);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return err;
+}
+
+void s5k4aa_disconnect(struct sd *sd)
+{
+	sd->sensor = NULL;
+}
+
+static void s5k4aa_dump_registers(struct sd *sd)
+{
+	int address;
+	u8 page, old_page;
+	m5602_read_sensor(sd, S5K4AA_PAGE_MAP, &old_page, 1);
+	for (page = 0; page < 16; page++) {
+		m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &page, 1);
+		pr_info("Dumping the s5k4aa register state for page 0x%x\n",
+			page);
+		for (address = 0; address <= 0xff; address++) {
+			u8 value = 0;
+			m5602_read_sensor(sd, address, &value, 1);
+			pr_info("register 0x%x contains 0x%x\n",
+				address, value);
+		}
+	}
+	pr_info("s5k4aa register state dump complete\n");
+
+	for (page = 0; page < 16; page++) {
+		m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &page, 1);
+		pr_info("Probing for which registers that are read/write for page 0x%x\n",
+			page);
+		for (address = 0; address <= 0xff; address++) {
+			u8 old_value, ctrl_value, test_value = 0xff;
+
+			m5602_read_sensor(sd, address, &old_value, 1);
+			m5602_write_sensor(sd, address, &test_value, 1);
+			m5602_read_sensor(sd, address, &ctrl_value, 1);
+
+			if (ctrl_value == test_value)
+				pr_info("register 0x%x is writeable\n",
+					address);
+			else
+				pr_info("register 0x%x is read only\n",
+					address);
+
+			/* Restore original value */
+			m5602_write_sensor(sd, address, &old_value, 1);
+		}
+	}
+	pr_info("Read/write register probing complete\n");
+	m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &old_page, 1);
+}
diff --git a/drivers/media/usb/gspca/m5602/m5602_s5k4aa.h b/drivers/media/usb/gspca/m5602/m5602_s5k4aa.h
new file mode 100644
index 0000000..9953e97
--- /dev/null
+++ b/drivers/media/usb/gspca/m5602/m5602_s5k4aa.h
@@ -0,0 +1,285 @@
+/*
+ * Driver for the s5k4aa sensor
+ *
+ * Copyright (C) 2008 Erik Andrén
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * 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.
+ *
+ */
+
+#ifndef M5602_S5K4AA_H_
+#define M5602_S5K4AA_H_
+
+#include <linux/dmi.h>
+
+#include "m5602_sensor.h"
+
+/*****************************************************************************/
+
+#define S5K4AA_PAGE_MAP			0xec
+
+#define S5K4AA_PAGE_MAP_0		0x00
+#define S5K4AA_PAGE_MAP_1		0x01
+#define S5K4AA_PAGE_MAP_2		0x02
+
+/* Sensor register definitions for page 0x02 */
+#define S5K4AA_READ_MODE		0x03
+#define S5K4AA_ROWSTART_HI		0x04
+#define S5K4AA_ROWSTART_LO		0x05
+#define S5K4AA_COLSTART_HI		0x06
+#define S5K4AA_COLSTART_LO		0x07
+#define S5K4AA_WINDOW_HEIGHT_HI		0x08
+#define S5K4AA_WINDOW_HEIGHT_LO		0x09
+#define S5K4AA_WINDOW_WIDTH_HI		0x0a
+#define S5K4AA_WINDOW_WIDTH_LO		0x0b
+#define S5K4AA_GLOBAL_GAIN__		0x0f
+/* sync lost, if too low, reduces frame rate if too high */
+#define S5K4AA_H_BLANK_HI__		0x1d
+#define S5K4AA_H_BLANK_LO__		0x1e
+#define S5K4AA_EXPOSURE_HI		0x17
+#define S5K4AA_EXPOSURE_LO		0x18
+#define S5K4AA_BRIGHTNESS		0x1f /* (digital?) gain : 5 bits */
+#define S5K4AA_GAIN			0x20 /* (analogue?) gain : 7 bits */
+#define S5K4AA_NOISE_SUPP		0x37
+
+#define S5K4AA_RM_ROW_SKIP_4X		0x08
+#define S5K4AA_RM_ROW_SKIP_2X		0x04
+#define S5K4AA_RM_COL_SKIP_4X		0x02
+#define S5K4AA_RM_COL_SKIP_2X		0x01
+#define S5K4AA_RM_H_FLIP		0x40
+#define S5K4AA_RM_V_FLIP		0x80
+
+#define S5K4AA_DEFAULT_GAIN		0x5f
+#define S5K4AA_DEFAULT_BRIGHTNESS	0x10
+
+/*****************************************************************************/
+
+/* Kernel module parameters */
+extern int force_sensor;
+extern bool dump_sensor;
+
+int s5k4aa_probe(struct sd *sd);
+int s5k4aa_init(struct sd *sd);
+int s5k4aa_init_controls(struct sd *sd);
+int s5k4aa_start(struct sd *sd);
+void s5k4aa_disconnect(struct sd *sd);
+
+static const struct m5602_sensor s5k4aa = {
+	.name = "S5K4AA",
+	.i2c_slave_id = 0x5a,
+	.i2c_regW = 2,
+
+	.probe = s5k4aa_probe,
+	.init = s5k4aa_init,
+	.init_controls = s5k4aa_init_controls,
+	.start = s5k4aa_start,
+	.disconnect = s5k4aa_disconnect,
+};
+
+static const unsigned char preinit_s5k4aa[][4] = {
+	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
+	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00},
+	{BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00},
+
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x00, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x08, 0x00},
+
+	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
+	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x14, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x1c, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
+	{BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00},
+
+	{SENSOR, S5K4AA_PAGE_MAP, 0x00, 0x00}
+};
+
+static const unsigned char init_s5k4aa[][4] = {
+	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
+	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00},
+	{BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00},
+
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x00, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x08, 0x00},
+
+	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
+	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x14, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x1c, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
+	{BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00},
+
+	{SENSOR, S5K4AA_PAGE_MAP, 0x07, 0x00},
+	{SENSOR, 0x36, 0x01, 0x00},
+	{SENSOR, S5K4AA_PAGE_MAP, 0x00, 0x00},
+	{SENSOR, 0x7b, 0xff, 0x00},
+	{SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00},
+	{SENSOR, 0x0c, 0x05, 0x00},
+	{SENSOR, 0x02, 0x0e, 0x00},
+	{SENSOR, S5K4AA_READ_MODE, 0xa0, 0x00},
+	{SENSOR, 0x37, 0x00, 0x00},
+};
+
+static const unsigned char VGA_s5k4aa[][4] = {
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x08, 0x00},
+	{BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
+	{BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	/* VSYNC_PARA, VSYNC_PARA : img height 480 = 0x01e0 */
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x01, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0xe0, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+	/* HSYNC_PARA, HSYNC_PARA : img width 640 = 0x0280 */
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x80, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xa0, 0x00}, /* 48 MHz */
+
+	{SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00},
+	{SENSOR, S5K4AA_READ_MODE, S5K4AA_RM_H_FLIP | S5K4AA_RM_ROW_SKIP_2X
+		| S5K4AA_RM_COL_SKIP_2X, 0x00},
+	/* 0x37 : Fix image stability when light is too bright and improves
+	 * image quality in 640x480, but worsens it in 1280x1024 */
+	{SENSOR, 0x37, 0x01, 0x00},
+	/* ROWSTART_HI, ROWSTART_LO : 10 + (1024-960)/2 = 42 = 0x002a */
+	{SENSOR, S5K4AA_ROWSTART_HI, 0x00, 0x00},
+	{SENSOR, S5K4AA_ROWSTART_LO, 0x29, 0x00},
+	{SENSOR, S5K4AA_COLSTART_HI, 0x00, 0x00},
+	{SENSOR, S5K4AA_COLSTART_LO, 0x0c, 0x00},
+	/* window_height_hi, window_height_lo : 960 = 0x03c0 */
+	{SENSOR, S5K4AA_WINDOW_HEIGHT_HI, 0x03, 0x00},
+	{SENSOR, S5K4AA_WINDOW_HEIGHT_LO, 0xc0, 0x00},
+	/* window_width_hi, window_width_lo : 1280 = 0x0500 */
+	{SENSOR, S5K4AA_WINDOW_WIDTH_HI, 0x05, 0x00},
+	{SENSOR, S5K4AA_WINDOW_WIDTH_LO, 0x00, 0x00},
+	{SENSOR, S5K4AA_H_BLANK_HI__, 0x00, 0x00},
+	{SENSOR, S5K4AA_H_BLANK_LO__, 0xa8, 0x00}, /* helps to sync... */
+	{SENSOR, S5K4AA_EXPOSURE_HI, 0x01, 0x00},
+	{SENSOR, S5K4AA_EXPOSURE_LO, 0x00, 0x00},
+	{SENSOR, 0x11, 0x04, 0x00},
+	{SENSOR, 0x12, 0xc3, 0x00},
+	{SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00},
+	{SENSOR, 0x02, 0x0e, 0x00},
+};
+
+static const unsigned char SXGA_s5k4aa[][4] = {
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x08, 0x00},
+	{BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
+	{BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	/* VSYNC_PARA, VSYNC_PARA : img height 1024 = 0x0400 */
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x04, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+	/* HSYNC_PARA, HSYNC_PARA : img width 1280 = 0x0500 */
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x05, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xa0, 0x00}, /* 48 MHz */
+
+	{SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00},
+	{SENSOR, S5K4AA_READ_MODE, S5K4AA_RM_H_FLIP, 0x00},
+	{SENSOR, 0x37, 0x01, 0x00},
+	{SENSOR, S5K4AA_ROWSTART_HI, 0x00, 0x00},
+	{SENSOR, S5K4AA_ROWSTART_LO, 0x09, 0x00},
+	{SENSOR, S5K4AA_COLSTART_HI, 0x00, 0x00},
+	{SENSOR, S5K4AA_COLSTART_LO, 0x0a, 0x00},
+	{SENSOR, S5K4AA_WINDOW_HEIGHT_HI, 0x04, 0x00},
+	{SENSOR, S5K4AA_WINDOW_HEIGHT_LO, 0x00, 0x00},
+	{SENSOR, S5K4AA_WINDOW_WIDTH_HI, 0x05, 0x00},
+	{SENSOR, S5K4AA_WINDOW_WIDTH_LO, 0x00, 0x00},
+	{SENSOR, S5K4AA_H_BLANK_HI__, 0x01, 0x00},
+	{SENSOR, S5K4AA_H_BLANK_LO__, 0xa8, 0x00},
+	{SENSOR, S5K4AA_EXPOSURE_HI, 0x01, 0x00},
+	{SENSOR, S5K4AA_EXPOSURE_LO, 0x00, 0x00},
+	{SENSOR, 0x11, 0x04, 0x00},
+	{SENSOR, 0x12, 0xc3, 0x00},
+	{SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00},
+	{SENSOR, 0x02, 0x0e, 0x00},
+};
+#endif
diff --git a/drivers/media/usb/gspca/m5602/m5602_s5k83a.c b/drivers/media/usb/gspca/m5602/m5602_s5k83a.c
new file mode 100644
index 0000000..bf6b215
--- /dev/null
+++ b/drivers/media/usb/gspca/m5602/m5602_s5k83a.c
@@ -0,0 +1,456 @@
+/*
+ * Driver for the s5k83a sensor
+ *
+ * Copyright (C) 2008 Erik Andrén
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * 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.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kthread.h>
+#include "m5602_s5k83a.h"
+
+static int s5k83a_s_ctrl(struct v4l2_ctrl *ctrl);
+
+static const struct v4l2_ctrl_ops s5k83a_ctrl_ops = {
+	.s_ctrl = s5k83a_s_ctrl,
+};
+
+static struct v4l2_pix_format s5k83a_modes[] = {
+	{
+		640,
+		480,
+		V4L2_PIX_FMT_SBGGR8,
+		V4L2_FIELD_NONE,
+		.sizeimage =
+			640 * 480,
+		.bytesperline = 640,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 0
+	}
+};
+
+static void s5k83a_dump_registers(struct sd *sd);
+static int s5k83a_get_rotation(struct sd *sd, u8 *reg_data);
+static int s5k83a_set_led_indication(struct sd *sd, u8 val);
+static int s5k83a_set_flip_real(struct gspca_dev *gspca_dev,
+				__s32 vflip, __s32 hflip);
+
+int s5k83a_probe(struct sd *sd)
+{
+	u8 prod_id = 0, ver_id = 0;
+	int i, err = 0;
+	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
+
+	if (force_sensor) {
+		if (force_sensor == S5K83A_SENSOR) {
+			pr_info("Forcing a %s sensor\n", s5k83a.name);
+			goto sensor_found;
+		}
+		/* If we want to force another sensor, don't try to probe this
+		 * one */
+		return -ENODEV;
+	}
+
+	PDEBUG(D_PROBE, "Probing for a s5k83a sensor");
+
+	/* Preinit the sensor */
+	for (i = 0; i < ARRAY_SIZE(preinit_s5k83a) && !err; i++) {
+		u8 data[2] = {preinit_s5k83a[i][2], preinit_s5k83a[i][3]};
+		if (preinit_s5k83a[i][0] == SENSOR)
+			err = m5602_write_sensor(sd, preinit_s5k83a[i][1],
+				data, 2);
+		else
+			err = m5602_write_bridge(sd, preinit_s5k83a[i][1],
+				data[0]);
+	}
+
+	/* We don't know what register (if any) that contain the product id
+	 * Just pick the first addresses that seem to produce the same results
+	 * on multiple machines */
+	if (m5602_read_sensor(sd, 0x00, &prod_id, 1))
+		return -ENODEV;
+
+	if (m5602_read_sensor(sd, 0x01, &ver_id, 1))
+		return -ENODEV;
+
+	if ((prod_id == 0xff) || (ver_id == 0xff))
+		return -ENODEV;
+	else
+		pr_info("Detected a s5k83a sensor\n");
+
+sensor_found:
+	sd->gspca_dev.cam.cam_mode = s5k83a_modes;
+	sd->gspca_dev.cam.nmodes = ARRAY_SIZE(s5k83a_modes);
+
+	/* null the pointer! thread is't running now */
+	sd->rotation_thread = NULL;
+
+	return 0;
+}
+
+int s5k83a_init(struct sd *sd)
+{
+	int i, err = 0;
+
+	for (i = 0; i < ARRAY_SIZE(init_s5k83a) && !err; i++) {
+		u8 data[2] = {0x00, 0x00};
+
+		switch (init_s5k83a[i][0]) {
+		case BRIDGE:
+			err = m5602_write_bridge(sd,
+					init_s5k83a[i][1],
+					init_s5k83a[i][2]);
+			break;
+
+		case SENSOR:
+			data[0] = init_s5k83a[i][2];
+			err = m5602_write_sensor(sd,
+				init_s5k83a[i][1], data, 1);
+			break;
+
+		case SENSOR_LONG:
+			data[0] = init_s5k83a[i][2];
+			data[1] = init_s5k83a[i][3];
+			err = m5602_write_sensor(sd,
+				init_s5k83a[i][1], data, 2);
+			break;
+		default:
+			pr_info("Invalid stream command, exiting init\n");
+			return -EINVAL;
+		}
+	}
+
+	if (dump_sensor)
+		s5k83a_dump_registers(sd);
+
+	return err;
+}
+
+int s5k83a_init_controls(struct sd *sd)
+{
+	struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler;
+
+	sd->gspca_dev.vdev.ctrl_handler = hdl;
+	v4l2_ctrl_handler_init(hdl, 6);
+
+	v4l2_ctrl_new_std(hdl, &s5k83a_ctrl_ops, V4L2_CID_BRIGHTNESS,
+			  0, 255, 1, S5K83A_DEFAULT_BRIGHTNESS);
+
+	v4l2_ctrl_new_std(hdl, &s5k83a_ctrl_ops, V4L2_CID_EXPOSURE,
+			  0, S5K83A_MAXIMUM_EXPOSURE, 1,
+			  S5K83A_DEFAULT_EXPOSURE);
+
+	v4l2_ctrl_new_std(hdl, &s5k83a_ctrl_ops, V4L2_CID_GAIN,
+			  0, 255, 1, S5K83A_DEFAULT_GAIN);
+
+	sd->hflip = v4l2_ctrl_new_std(hdl, &s5k83a_ctrl_ops, V4L2_CID_HFLIP,
+				      0, 1, 1, 0);
+	sd->vflip = v4l2_ctrl_new_std(hdl, &s5k83a_ctrl_ops, V4L2_CID_VFLIP,
+				      0, 1, 1, 0);
+
+	if (hdl->error) {
+		pr_err("Could not initialize controls\n");
+		return hdl->error;
+	}
+
+	v4l2_ctrl_cluster(2, &sd->hflip);
+
+	return 0;
+}
+
+static int rotation_thread_function(void *data)
+{
+	struct sd *sd = (struct sd *) data;
+	u8 reg, previous_rotation = 0;
+	__s32 vflip, hflip;
+
+	set_current_state(TASK_INTERRUPTIBLE);
+	while (!schedule_timeout(msecs_to_jiffies(100))) {
+		if (mutex_lock_interruptible(&sd->gspca_dev.usb_lock))
+			break;
+
+		s5k83a_get_rotation(sd, &reg);
+		if (previous_rotation != reg) {
+			previous_rotation = reg;
+			pr_info("Camera was flipped\n");
+
+			hflip = sd->hflip->val;
+			vflip = sd->vflip->val;
+
+			if (reg) {
+				vflip = !vflip;
+				hflip = !hflip;
+			}
+			s5k83a_set_flip_real((struct gspca_dev *) sd,
+					      vflip, hflip);
+		}
+
+		mutex_unlock(&sd->gspca_dev.usb_lock);
+		set_current_state(TASK_INTERRUPTIBLE);
+	}
+
+	/* return to "front" flip */
+	if (previous_rotation) {
+		hflip = sd->hflip->val;
+		vflip = sd->vflip->val;
+		s5k83a_set_flip_real((struct gspca_dev *) sd, vflip, hflip);
+	}
+
+	sd->rotation_thread = NULL;
+	return 0;
+}
+
+int s5k83a_start(struct sd *sd)
+{
+	int i, err = 0;
+
+	/* Create another thread, polling the GPIO ports of the camera to check
+	   if it got rotated. This is how the windows driver does it so we have
+	   to assume that there is no better way of accomplishing this */
+	sd->rotation_thread = kthread_create(rotation_thread_function,
+					     sd, "rotation thread");
+	wake_up_process(sd->rotation_thread);
+
+	/* Preinit the sensor */
+	for (i = 0; i < ARRAY_SIZE(start_s5k83a) && !err; i++) {
+		u8 data[2] = {start_s5k83a[i][2], start_s5k83a[i][3]};
+		if (start_s5k83a[i][0] == SENSOR)
+			err = m5602_write_sensor(sd, start_s5k83a[i][1],
+				data, 2);
+		else
+			err = m5602_write_bridge(sd, start_s5k83a[i][1],
+				data[0]);
+	}
+	if (err < 0)
+		return err;
+
+	return s5k83a_set_led_indication(sd, 1);
+}
+
+int s5k83a_stop(struct sd *sd)
+{
+	if (sd->rotation_thread)
+		kthread_stop(sd->rotation_thread);
+
+	return s5k83a_set_led_indication(sd, 0);
+}
+
+void s5k83a_disconnect(struct sd *sd)
+{
+	s5k83a_stop(sd);
+
+	sd->sensor = NULL;
+}
+
+static int s5k83a_set_gain(struct gspca_dev *gspca_dev, __s32 val)
+{
+	int err;
+	u8 data[2];
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	data[0] = 0x00;
+	data[1] = 0x20;
+	err = m5602_write_sensor(sd, 0x14, data, 2);
+	if (err < 0)
+		return err;
+
+	data[0] = 0x01;
+	data[1] = 0x00;
+	err = m5602_write_sensor(sd, 0x0d, data, 2);
+	if (err < 0)
+		return err;
+
+	/* FIXME: This is not sane, we need to figure out the composition
+		  of these registers */
+	data[0] = val >> 3; /* gain, high 5 bits */
+	data[1] = val >> 1; /* gain, high 7 bits */
+	err = m5602_write_sensor(sd, S5K83A_GAIN, data, 2);
+
+	return err;
+}
+
+static int s5k83a_set_brightness(struct gspca_dev *gspca_dev, __s32 val)
+{
+	int err;
+	u8 data[1];
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	data[0] = val;
+	err = m5602_write_sensor(sd, S5K83A_BRIGHTNESS, data, 1);
+	return err;
+}
+
+static int s5k83a_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
+{
+	int err;
+	u8 data[2];
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	data[0] = 0;
+	data[1] = val;
+	err = m5602_write_sensor(sd, S5K83A_EXPOSURE, data, 2);
+	return err;
+}
+
+static int s5k83a_set_flip_real(struct gspca_dev *gspca_dev,
+				__s32 vflip, __s32 hflip)
+{
+	int err;
+	u8 data[1];
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	data[0] = 0x05;
+	err = m5602_write_sensor(sd, S5K83A_PAGE_MAP, data, 1);
+	if (err < 0)
+		return err;
+
+	/* six bit is vflip, seven is hflip */
+	data[0] = S5K83A_FLIP_MASK;
+	data[0] = (vflip) ? data[0] | 0x40 : data[0];
+	data[0] = (hflip) ? data[0] | 0x80 : data[0];
+
+	err = m5602_write_sensor(sd, S5K83A_FLIP, data, 1);
+	if (err < 0)
+		return err;
+
+	data[0] = (vflip) ? 0x0b : 0x0a;
+	err = m5602_write_sensor(sd, S5K83A_VFLIP_TUNE, data, 1);
+	if (err < 0)
+		return err;
+
+	data[0] = (hflip) ? 0x0a : 0x0b;
+	err = m5602_write_sensor(sd, S5K83A_HFLIP_TUNE, data, 1);
+	return err;
+}
+
+static int s5k83a_set_hvflip(struct gspca_dev *gspca_dev)
+{
+	int err;
+	u8 reg;
+	struct sd *sd = (struct sd *) gspca_dev;
+	int hflip = sd->hflip->val;
+	int vflip = sd->vflip->val;
+
+	err = s5k83a_get_rotation(sd, &reg);
+	if (err < 0)
+		return err;
+	if (reg) {
+		hflip = !hflip;
+		vflip = !vflip;
+	}
+
+	err = s5k83a_set_flip_real(gspca_dev, vflip, hflip);
+	return err;
+}
+
+static int s5k83a_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct gspca_dev *gspca_dev =
+		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+	int err;
+
+	if (!gspca_dev->streaming)
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_BRIGHTNESS:
+		err = s5k83a_set_brightness(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_EXPOSURE:
+		err = s5k83a_set_exposure(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_GAIN:
+		err = s5k83a_set_gain(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_HFLIP:
+		err = s5k83a_set_hvflip(gspca_dev);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return err;
+}
+
+static int s5k83a_set_led_indication(struct sd *sd, u8 val)
+{
+	int err = 0;
+	u8 data[1];
+
+	err = m5602_read_bridge(sd, M5602_XB_GPIO_DAT, data);
+	if (err < 0)
+		return err;
+
+	if (val)
+		data[0] = data[0] | S5K83A_GPIO_LED_MASK;
+	else
+		data[0] = data[0] & ~S5K83A_GPIO_LED_MASK;
+
+	err = m5602_write_bridge(sd, M5602_XB_GPIO_DAT, data[0]);
+
+	return err;
+}
+
+/* Get camera rotation on Acer notebooks */
+static int s5k83a_get_rotation(struct sd *sd, u8 *reg_data)
+{
+	int err = m5602_read_bridge(sd, M5602_XB_GPIO_DAT, reg_data);
+	*reg_data = (*reg_data & S5K83A_GPIO_ROTATION_MASK) ? 0 : 1;
+	return err;
+}
+
+static void s5k83a_dump_registers(struct sd *sd)
+{
+	int address;
+	u8 page, old_page;
+	m5602_read_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1);
+
+	for (page = 0; page < 16; page++) {
+		m5602_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1);
+		pr_info("Dumping the s5k83a register state for page 0x%x\n",
+			page);
+		for (address = 0; address <= 0xff; address++) {
+			u8 val = 0;
+			m5602_read_sensor(sd, address, &val, 1);
+			pr_info("register 0x%x contains 0x%x\n", address, val);
+		}
+	}
+	pr_info("s5k83a register state dump complete\n");
+
+	for (page = 0; page < 16; page++) {
+		m5602_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1);
+		pr_info("Probing for which registers that are read/write for page 0x%x\n",
+			page);
+		for (address = 0; address <= 0xff; address++) {
+			u8 old_val, ctrl_val, test_val = 0xff;
+
+			m5602_read_sensor(sd, address, &old_val, 1);
+			m5602_write_sensor(sd, address, &test_val, 1);
+			m5602_read_sensor(sd, address, &ctrl_val, 1);
+
+			if (ctrl_val == test_val)
+				pr_info("register 0x%x is writeable\n",
+					address);
+			else
+				pr_info("register 0x%x is read only\n",
+					address);
+
+			/* Restore original val */
+			m5602_write_sensor(sd, address, &old_val, 1);
+		}
+	}
+	pr_info("Read/write register probing complete\n");
+	m5602_write_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1);
+}
diff --git a/drivers/media/usb/gspca/m5602/m5602_s5k83a.h b/drivers/media/usb/gspca/m5602/m5602_s5k83a.h
new file mode 100644
index 0000000..d61b918
--- /dev/null
+++ b/drivers/media/usb/gspca/m5602/m5602_s5k83a.h
@@ -0,0 +1,188 @@
+/*
+ * Driver for the s5k83a sensor
+ *
+ * Copyright (C) 2008 Erik Andrén
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * 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.
+ *
+ */
+
+#ifndef M5602_S5K83A_H_
+#define M5602_S5K83A_H_
+
+#include "m5602_sensor.h"
+
+#define S5K83A_FLIP			0x01
+#define S5K83A_HFLIP_TUNE		0x03
+#define S5K83A_VFLIP_TUNE		0x05
+#define S5K83A_BRIGHTNESS		0x0a
+#define S5K83A_EXPOSURE			0x18
+#define S5K83A_GAIN			0x1b
+#define S5K83A_PAGE_MAP			0xec
+
+#define S5K83A_DEFAULT_GAIN		0x71
+#define S5K83A_DEFAULT_BRIGHTNESS	0x7e
+#define S5K83A_DEFAULT_EXPOSURE		0x00
+#define S5K83A_MAXIMUM_EXPOSURE		0x3c
+#define S5K83A_FLIP_MASK		0x10
+#define S5K83A_GPIO_LED_MASK		0x10
+#define S5K83A_GPIO_ROTATION_MASK	0x40
+
+/*****************************************************************************/
+
+/* Kernel module parameters */
+extern int force_sensor;
+extern bool dump_sensor;
+
+int s5k83a_probe(struct sd *sd);
+int s5k83a_init(struct sd *sd);
+int s5k83a_init_controls(struct sd *sd);
+int s5k83a_start(struct sd *sd);
+int s5k83a_stop(struct sd *sd);
+void s5k83a_disconnect(struct sd *sd);
+
+static const struct m5602_sensor s5k83a = {
+	.name = "S5K83A",
+	.probe = s5k83a_probe,
+	.init = s5k83a_init,
+	.init_controls = s5k83a_init_controls,
+	.start = s5k83a_start,
+	.stop = s5k83a_stop,
+	.disconnect = s5k83a_disconnect,
+	.i2c_slave_id = 0x5a,
+	.i2c_regW = 2,
+};
+
+static const unsigned char preinit_s5k83a[][4] = {
+	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
+	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00},
+	{BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00},
+
+	{BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
+	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x1c, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
+	{BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00},
+};
+
+/* This could probably be considerably shortened.
+   I don't have the hardware to experiment with it, patches welcome
+*/
+static const unsigned char init_s5k83a[][4] = {
+	/* The following sequence is useless after a clean boot
+	   but is necessary after resume from suspend */
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
+	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00},
+	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
+	{BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
+	{BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00},
+
+	{SENSOR, S5K83A_PAGE_MAP, 0x04, 0x00},
+	{SENSOR, 0xaf, 0x01, 0x00},
+	{SENSOR, S5K83A_PAGE_MAP, 0x00, 0x00},
+	{SENSOR, 0x7b, 0xff, 0x00},
+	{SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00},
+	{SENSOR, 0x01, 0x50, 0x00},
+	{SENSOR, 0x12, 0x20, 0x00},
+	{SENSOR, 0x17, 0x40, 0x00},
+	{SENSOR, 0x1c, 0x00, 0x00},
+	{SENSOR, 0x02, 0x70, 0x00},
+	{SENSOR, 0x03, 0x0b, 0x00},
+	{SENSOR, 0x04, 0xf0, 0x00},
+	{SENSOR, 0x05, 0x0b, 0x00},
+	{SENSOR, 0x06, 0x71, 0x00},
+	{SENSOR, 0x07, 0xe8, 0x00}, /* 488 */
+	{SENSOR, 0x08, 0x02, 0x00},
+	{SENSOR, 0x09, 0x88, 0x00}, /* 648 */
+	{SENSOR, 0x14, 0x00, 0x00},
+	{SENSOR, 0x15, 0x20, 0x00}, /* 32 */
+	{SENSOR, 0x19, 0x00, 0x00},
+	{SENSOR, 0x1a, 0x98, 0x00}, /* 152 */
+	{SENSOR, 0x0f, 0x02, 0x00},
+	{SENSOR, 0x10, 0xe5, 0x00}, /* 741 */
+	/* normal colors
+	(this is value after boot, but after tries can be different) */
+	{SENSOR, 0x00, 0x06, 0x00},
+};
+
+static const unsigned char start_s5k83a[][4] = {
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+	{BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
+	{BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x01, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0xe4, 0x00}, /* 484 */
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00},
+	{BRIDGE, M5602_XB_HSYNC_PARA, 0x7f, 0x00}, /* 639 */
+	{BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+};
+#endif
diff --git a/drivers/media/usb/gspca/m5602/m5602_sensor.h b/drivers/media/usb/gspca/m5602/m5602_sensor.h
new file mode 100644
index 0000000..48341b4
--- /dev/null
+++ b/drivers/media/usb/gspca/m5602/m5602_sensor.h
@@ -0,0 +1,73 @@
+/*
+ * USB Driver for ALi m5602 based webcams
+ *
+ * Copyright (C) 2008 Erik Andrén
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * 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.
+ *
+ */
+
+#ifndef M5602_SENSOR_H_
+#define M5602_SENSOR_H_
+
+#include "m5602_bridge.h"
+
+#define M5602_V4L2_CID_GREEN_BALANCE	(V4L2_CID_PRIVATE_BASE + 0)
+#define M5602_V4L2_CID_NOISE_SUPPRESION	(V4L2_CID_PRIVATE_BASE + 1)
+
+/* Enumerates all supported sensors */
+enum sensors {
+	OV9650_SENSOR	= 1,
+	S5K83A_SENSOR	= 2,
+	S5K4AA_SENSOR	= 3,
+	MT9M111_SENSOR	= 4,
+	PO1030_SENSOR	= 5,
+	OV7660_SENSOR   = 6,
+};
+
+/* Enumerates all possible instruction types */
+enum instruction {
+	BRIDGE,
+	SENSOR,
+	SENSOR_LONG
+};
+
+struct m5602_sensor {
+	/* Defines the name of a sensor */
+	char name[32];
+
+	/* What i2c address the sensor is connected to */
+	u8 i2c_slave_id;
+
+	/* Width of each i2c register (in bytes) */
+	u8 i2c_regW;
+
+	/* Probes if the sensor is connected */
+	int (*probe)(struct sd *sd);
+
+	/* Performs a initialization sequence */
+	int (*init)(struct sd *sd);
+
+	/* Controls initialization, maybe NULL */
+	int (*init_controls)(struct sd *sd);
+
+	/* Executed when the camera starts to send data */
+	int (*start)(struct sd *sd);
+
+	/* Executed when the camera ends to send data */
+	int (*stop)(struct sd *sd);
+
+	/* Executed when the device is disconnected */
+	void (*disconnect)(struct sd *sd);
+};
+
+#endif
diff --git a/drivers/media/usb/gspca/mars.c b/drivers/media/usb/gspca/mars.c
new file mode 100644
index 0000000..779a878
--- /dev/null
+++ b/drivers/media/usb/gspca/mars.c
@@ -0,0 +1,440 @@
+/*
+ *		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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "mars"
+
+#include "gspca.h"
+#include "jpeg.h"
+
+MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
+MODULE_DESCRIPTION("GSPCA/Mars USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+#define QUALITY 50
+
+/* specific webcam descriptor */
+struct sd {
+	struct gspca_dev gspca_dev;	/* !! must be the first item */
+
+	struct v4l2_ctrl *brightness;
+	struct v4l2_ctrl *saturation;
+	struct v4l2_ctrl *sharpness;
+	struct v4l2_ctrl *gamma;
+	struct { /* illuminator control cluster */
+		struct v4l2_ctrl *illum_top;
+		struct v4l2_ctrl *illum_bottom;
+	};
+	u8 jpeg_hdr[JPEG_HDR_SZ];
+};
+
+/* V4L2 controls supported by the driver */
+static void setbrightness(struct gspca_dev *gspca_dev, s32 val);
+static void setcolors(struct gspca_dev *gspca_dev, s32 val);
+static void setgamma(struct gspca_dev *gspca_dev, s32 val);
+static void setsharpness(struct gspca_dev *gspca_dev, s32 val);
+
+static const 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 const __u8 mi_data[0x20] = {
+/*	 01    02   03     04    05    06    07    08 */
+	0x48, 0x22, 0x01, 0x47, 0x10, 0x00, 0x00, 0x00,
+/*	 09    0a   0b     0c    0d    0e    0f    10 */
+	0x00, 0x01, 0x30, 0x01, 0x30, 0x01, 0x30, 0x01,
+/*	 11    12   13     14    15    16    17    18 */
+	0x30, 0x00, 0x04, 0x00, 0x06, 0x01, 0xe2, 0x02,
+/*	 19    1a   1b     1c    1d    1e    1f    20 */
+	0x82, 0x00, 0x20, 0x17, 0x80, 0x08, 0x0c, 0x00
+};
+
+/* write <len> bytes from gspca_dev->usb_buf */
+static void reg_w(struct gspca_dev *gspca_dev,
+		 int len)
+{
+	int alen, ret;
+
+	if (gspca_dev->usb_err < 0)
+		return;
+
+	ret = usb_bulk_msg(gspca_dev->dev,
+			usb_sndbulkpipe(gspca_dev->dev, 4),
+			gspca_dev->usb_buf,
+			len,
+			&alen,
+			500);	/* timeout in milliseconds */
+	if (ret < 0) {
+		pr_err("reg write [%02x] error %d\n",
+		       gspca_dev->usb_buf[0], ret);
+		gspca_dev->usb_err = ret;
+	}
+}
+
+static void mi_w(struct gspca_dev *gspca_dev,
+		 u8 addr,
+		 u8 value)
+{
+	gspca_dev->usb_buf[0] = 0x1f;
+	gspca_dev->usb_buf[1] = 0;			/* control byte */
+	gspca_dev->usb_buf[2] = addr;
+	gspca_dev->usb_buf[3] = value;
+
+	reg_w(gspca_dev, 4);
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
+{
+	gspca_dev->usb_buf[0] = 0x61;
+	gspca_dev->usb_buf[1] = val;
+	reg_w(gspca_dev, 2);
+}
+
+static void setcolors(struct gspca_dev *gspca_dev, s32 val)
+{
+	gspca_dev->usb_buf[0] = 0x5f;
+	gspca_dev->usb_buf[1] = val << 3;
+	gspca_dev->usb_buf[2] = ((val >> 2) & 0xf8) | 0x04;
+	reg_w(gspca_dev, 3);
+}
+
+static void setgamma(struct gspca_dev *gspca_dev, s32 val)
+{
+	gspca_dev->usb_buf[0] = 0x06;
+	gspca_dev->usb_buf[1] = val * 0x40;
+	reg_w(gspca_dev, 2);
+}
+
+static void setsharpness(struct gspca_dev *gspca_dev, s32 val)
+{
+	gspca_dev->usb_buf[0] = 0x67;
+	gspca_dev->usb_buf[1] = val * 4 + 3;
+	reg_w(gspca_dev, 2);
+}
+
+static void setilluminators(struct gspca_dev *gspca_dev, bool top, bool bottom)
+{
+	/* both are off if not streaming */
+	gspca_dev->usb_buf[0] = 0x22;
+	if (top)
+		gspca_dev->usb_buf[1] = 0x76;
+	else if (bottom)
+		gspca_dev->usb_buf[1] = 0x7a;
+	else
+		gspca_dev->usb_buf[1] = 0x7e;
+	reg_w(gspca_dev, 2);
+}
+
+static int mars_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct gspca_dev *gspca_dev =
+		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+	struct sd *sd = (struct sd *)gspca_dev;
+
+	gspca_dev->usb_err = 0;
+
+	if (ctrl->id == V4L2_CID_ILLUMINATORS_1) {
+		/* only one can be on at a time */
+		if (ctrl->is_new && ctrl->val)
+			sd->illum_bottom->val = 0;
+		if (sd->illum_bottom->is_new && sd->illum_bottom->val)
+			sd->illum_top->val = 0;
+	}
+
+	if (!gspca_dev->streaming)
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_BRIGHTNESS:
+		setbrightness(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_SATURATION:
+		setcolors(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_GAMMA:
+		setgamma(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_ILLUMINATORS_1:
+		setilluminators(gspca_dev, sd->illum_top->val,
+					   sd->illum_bottom->val);
+		break;
+	case V4L2_CID_SHARPNESS:
+		setsharpness(gspca_dev, ctrl->val);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops mars_ctrl_ops = {
+	.s_ctrl = mars_s_ctrl,
+};
+
+/* this function is called at probe time */
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+	gspca_dev->vdev.ctrl_handler = hdl;
+	v4l2_ctrl_handler_init(hdl, 6);
+	sd->brightness = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops,
+			V4L2_CID_BRIGHTNESS, 0, 30, 1, 15);
+	sd->saturation = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops,
+			V4L2_CID_SATURATION, 0, 255, 1, 200);
+	sd->gamma = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops,
+			V4L2_CID_GAMMA, 0, 3, 1, 1);
+	sd->sharpness = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops,
+			V4L2_CID_SHARPNESS, 0, 2, 1, 1);
+	sd->illum_top = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops,
+			V4L2_CID_ILLUMINATORS_1, 0, 1, 1, 0);
+	sd->illum_top->flags |= V4L2_CTRL_FLAG_UPDATE;
+	sd->illum_bottom = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops,
+			V4L2_CID_ILLUMINATORS_2, 0, 1, 1, 0);
+	sd->illum_bottom->flags |= V4L2_CTRL_FLAG_UPDATE;
+	if (hdl->error) {
+		pr_err("Could not initialize controls\n");
+		return hdl->error;
+	}
+	v4l2_ctrl_cluster(2, &sd->illum_top);
+	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 cam *cam;
+
+	cam = &gspca_dev->cam;
+	cam->cam_mode = vga_mode;
+	cam->nmodes = ARRAY_SIZE(vga_mode);
+	return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+	return 0;
+}
+
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u8 *data;
+	int i;
+
+	/* create the JPEG header */
+	jpeg_define(sd->jpeg_hdr, gspca_dev->pixfmt.height,
+			gspca_dev->pixfmt.width,
+			0x21);		/* JPEG 422 */
+	jpeg_set_qual(sd->jpeg_hdr, QUALITY);
+
+	data = gspca_dev->usb_buf;
+
+	data[0] = 0x01;		/* address */
+	data[1] = 0x01;
+	reg_w(gspca_dev, 2);
+
+	/*
+	   Initialize the MR97113 chip register
+	 */
+	data[0] = 0x00;		/* address */
+	data[1] = 0x0c | 0x01;	/* reg 0 */
+	data[2] = 0x01;		/* reg 1 */
+	data[3] = gspca_dev->pixfmt.width / 8;	/* h_size , reg 2 */
+	data[4] = gspca_dev->pixfmt.height / 8;	/* v_size , reg 3 */
+	data[5] = 0x30;		/* reg 4, MI, PAS5101 :
+				 *	0x30 for 24mhz , 0x28 for 12mhz */
+	data[6] = 0x02;		/* reg 5, H start - was 0x04 */
+	data[7] = v4l2_ctrl_g_ctrl(sd->gamma) * 0x40;	/* reg 0x06: gamma */
+	data[8] = 0x01;		/* reg 7, V start - was 0x03 */
+/*	if (h_size == 320 ) */
+/*		data[9]= 0x56;	 * reg 8, 24MHz, 2:1 scale down */
+/*	else */
+	data[9] = 0x52;		/* reg 8, 24MHz, no scale down */
+/*jfm: from win trace*/
+	data[10] = 0x18;
+
+	reg_w(gspca_dev, 11);
+
+	data[0] = 0x23;		/* address */
+	data[1] = 0x09;		/* reg 35, append frame header */
+
+	reg_w(gspca_dev, 2);
+
+	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 */
+	reg_w(gspca_dev, 2);
+
+	/* auto dark-gain */
+	data[0] = 0x5e;		/* address */
+	data[1] = 0;		/* reg 94, Y Gain (auto) */
+/*jfm: from win trace*/
+				/* reg 0x5f/0x60 (LE) = saturation */
+				/* h (60): xxxx x100
+				 * l (5f): xxxx x000 */
+	data[2] = v4l2_ctrl_g_ctrl(sd->saturation) << 3;
+	data[3] = ((v4l2_ctrl_g_ctrl(sd->saturation) >> 2) & 0xf8) | 0x04;
+	data[4] = v4l2_ctrl_g_ctrl(sd->brightness); /* reg 0x61 = brightness */
+	data[5] = 0x00;
+
+	reg_w(gspca_dev, 6);
+
+	data[0] = 0x67;
+/*jfm: from win trace*/
+	data[1] = v4l2_ctrl_g_ctrl(sd->sharpness) * 4 + 3;
+	data[2] = 0x14;
+	reg_w(gspca_dev, 3);
+
+	data[0] = 0x69;
+	data[1] = 0x2f;
+	data[2] = 0x28;
+	data[3] = 0x42;
+	reg_w(gspca_dev, 4);
+
+	data[0] = 0x63;
+	data[1] = 0x07;
+	reg_w(gspca_dev, 2);
+/*jfm: win trace - many writes here to reg 0x64*/
+
+	/* initialize the MI sensor */
+	for (i = 0; i < sizeof mi_data; i++)
+		mi_w(gspca_dev, i + 1, mi_data[i]);
+
+	data[0] = 0x00;
+	data[1] = 0x4d;		/* ISOC transferring enable... */
+	reg_w(gspca_dev, 2);
+
+	setilluminators(gspca_dev, v4l2_ctrl_g_ctrl(sd->illum_top),
+				   v4l2_ctrl_g_ctrl(sd->illum_bottom));
+
+	return gspca_dev->usb_err;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (v4l2_ctrl_g_ctrl(sd->illum_top) ||
+	    v4l2_ctrl_g_ctrl(sd->illum_bottom)) {
+		setilluminators(gspca_dev, false, false);
+		msleep(20);
+	}
+
+	gspca_dev->usb_buf[0] = 1;
+	gspca_dev->usb_buf[1] = 0;
+	reg_w(gspca_dev, 2);
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+			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 len: %d",
+					p, len);
+				gspca_frame_add(gspca_dev, LAST_PACKET,
+						data, p);
+
+				/* put the JPEG header */
+				gspca_frame_add(gspca_dev, FIRST_PACKET,
+					sd->jpeg_hdr, JPEG_HDR_SZ);
+				data += p + 16;
+				len -= p + 16;
+				break;
+			}
+		}
+	}
+	gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+	.name = MODULE_NAME,
+	.config = sd_config,
+	.init = sd_init,
+	.init_controls = sd_init_controls,
+	.start = sd_start,
+	.stopN = sd_stopN,
+	.pkt_scan = sd_pkt_scan,
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+	{USB_DEVICE(0x093a, 0x050f)},
+	{}
+};
+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,
+#ifdef CONFIG_PM
+	.suspend = gspca_suspend,
+	.resume = gspca_resume,
+	.reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/usb/gspca/mr97310a.c b/drivers/media/usb/gspca/mr97310a.c
new file mode 100644
index 0000000..f006e29
--- /dev/null
+++ b/drivers/media/usb/gspca/mr97310a.c
@@ -0,0 +1,1091 @@
+/*
+ * Mars MR97310A library
+ *
+ * The original mr97310a driver, which supported the Aiptek Pencam VGA+, is
+ * Copyright (C) 2009 Kyle Guinn <elyk03@gmail.com>
+ *
+ * Support for the MR97310A cameras in addition to the Aiptek Pencam VGA+
+ * and for the routines for detecting and classifying these various cameras,
+ * is Copyright (C) 2009 Theodore Kilgore <kilgota@auburn.edu>
+ *
+ * Support for the control settings for the CIF cameras is
+ * Copyright (C) 2009 Hans de Goede <hdegoede@redhat.com> and
+ * Thomas Kaiser <thomas@kaiser-linux.li>
+ *
+ * Support for the control settings for the VGA cameras is
+ * Copyright (C) 2009 Theodore Kilgore <kilgota@auburn.edu>
+ *
+ * Several previously unsupported cameras are owned and have been tested by
+ * Hans de Goede <hdegoede@redhat.com> and
+ * Thomas Kaiser <thomas@kaiser-linux.li> and
+ * Theodore Kilgore <kilgota@auburn.edu> and
+ * Edmond Rodriguez <erodrig_97@yahoo.com> and
+ * Aurelien Jacobs <aurel@gnuage.org>
+ *
+ * The MR97311A support in gspca/mars.c has been helpful in understanding some
+ * of the registers in these cameras.
+ *
+ * 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "mr97310a"
+
+#include "gspca.h"
+
+#define CAM_TYPE_CIF			0
+#define CAM_TYPE_VGA			1
+
+#define MR97310A_BRIGHTNESS_DEFAULT	0
+
+#define MR97310A_EXPOSURE_MIN		0
+#define MR97310A_EXPOSURE_MAX		4095
+#define MR97310A_EXPOSURE_DEFAULT	1000
+
+#define MR97310A_GAIN_MIN		0
+#define MR97310A_GAIN_MAX		31
+#define MR97310A_GAIN_DEFAULT		25
+
+#define MR97310A_CONTRAST_MIN		0
+#define MR97310A_CONTRAST_MAX		31
+#define MR97310A_CONTRAST_DEFAULT	23
+
+#define MR97310A_CS_GAIN_MIN		0
+#define MR97310A_CS_GAIN_MAX		0x7ff
+#define MR97310A_CS_GAIN_DEFAULT	0x110
+
+#define MR97310A_CID_CLOCKDIV (V4L2_CTRL_CLASS_USER + 0x1000)
+#define MR97310A_MIN_CLOCKDIV_MIN	3
+#define MR97310A_MIN_CLOCKDIV_MAX	8
+#define MR97310A_MIN_CLOCKDIV_DEFAULT	3
+
+MODULE_AUTHOR("Kyle Guinn <elyk03@gmail.com>,"
+	      "Theodore Kilgore <kilgota@auburn.edu>");
+MODULE_DESCRIPTION("GSPCA/Mars-Semi MR97310A USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* global parameters */
+static int force_sensor_type = -1;
+module_param(force_sensor_type, int, 0644);
+MODULE_PARM_DESC(force_sensor_type, "Force sensor type (-1 (auto), 0 or 1)");
+
+/* specific webcam descriptor */
+struct sd {
+	struct gspca_dev gspca_dev;  /* !! must be the first item */
+	struct { /* exposure/min_clockdiv control cluster */
+		struct v4l2_ctrl *exposure;
+		struct v4l2_ctrl *min_clockdiv;
+	};
+	u8 sof_read;
+	u8 cam_type;	/* 0 is CIF and 1 is VGA */
+	u8 sensor_type;	/* We use 0 and 1 here, too. */
+	u8 do_lcd_stop;
+	u8 adj_colors;
+};
+
+struct sensor_w_data {
+	u8 reg;
+	u8 flags;
+	u8 data[16];
+	int len;
+};
+
+static void sd_stopN(struct gspca_dev *gspca_dev);
+
+static const struct v4l2_pix_format vga_mode[] = {
+	{160, 120, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE,
+		.bytesperline = 160,
+		.sizeimage = 160 * 120,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 4},
+	{176, 144, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE,
+		.bytesperline = 176,
+		.sizeimage = 176 * 144,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 3},
+	{320, 240, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE,
+		.bytesperline = 320,
+		.sizeimage = 320 * 240,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 2},
+	{352, 288, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE,
+		.bytesperline = 352,
+		.sizeimage = 352 * 288,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 1},
+	{640, 480, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE,
+		.bytesperline = 640,
+		.sizeimage = 640 * 480,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 0},
+};
+
+/* the bytes to write are in gspca_dev->usb_buf */
+static int mr_write(struct gspca_dev *gspca_dev, int len)
+{
+	int rc;
+
+	rc = usb_bulk_msg(gspca_dev->dev,
+			  usb_sndbulkpipe(gspca_dev->dev, 4),
+			  gspca_dev->usb_buf, len, NULL, 500);
+	if (rc < 0)
+		pr_err("reg write [%02x] error %d\n",
+		       gspca_dev->usb_buf[0], rc);
+	return rc;
+}
+
+/* the bytes are read into gspca_dev->usb_buf */
+static int mr_read(struct gspca_dev *gspca_dev, int len)
+{
+	int rc;
+
+	rc = usb_bulk_msg(gspca_dev->dev,
+			  usb_rcvbulkpipe(gspca_dev->dev, 3),
+			  gspca_dev->usb_buf, len, NULL, 500);
+	if (rc < 0)
+		pr_err("reg read [%02x] error %d\n",
+		       gspca_dev->usb_buf[0], rc);
+	return rc;
+}
+
+static int sensor_write_reg(struct gspca_dev *gspca_dev, u8 reg, u8 flags,
+	const u8 *data, int len)
+{
+	gspca_dev->usb_buf[0] = 0x1f;
+	gspca_dev->usb_buf[1] = flags;
+	gspca_dev->usb_buf[2] = reg;
+	memcpy(gspca_dev->usb_buf + 3, data, len);
+
+	return mr_write(gspca_dev, len + 3);
+}
+
+static int sensor_write_regs(struct gspca_dev *gspca_dev,
+	const struct sensor_w_data *data, int len)
+{
+	int i, rc;
+
+	for (i = 0; i < len; i++) {
+		rc = sensor_write_reg(gspca_dev, data[i].reg, data[i].flags,
+					  data[i].data, data[i].len);
+		if (rc < 0)
+			return rc;
+	}
+
+	return 0;
+}
+
+static int sensor_write1(struct gspca_dev *gspca_dev, u8 reg, u8 data)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u8 buf, confirm_reg;
+	int rc;
+
+	buf = data;
+	if (sd->cam_type == CAM_TYPE_CIF) {
+		rc = sensor_write_reg(gspca_dev, reg, 0x01, &buf, 1);
+		confirm_reg = sd->sensor_type ? 0x13 : 0x11;
+	} else {
+		rc = sensor_write_reg(gspca_dev, reg, 0x00, &buf, 1);
+		confirm_reg = 0x11;
+	}
+	if (rc < 0)
+		return rc;
+
+	buf = 0x01;
+	rc = sensor_write_reg(gspca_dev, confirm_reg, 0x00, &buf, 1);
+	if (rc < 0)
+		return rc;
+
+	return 0;
+}
+
+static int cam_get_response16(struct gspca_dev *gspca_dev, u8 reg, int verbose)
+{
+	int err_code;
+
+	gspca_dev->usb_buf[0] = reg;
+	err_code = mr_write(gspca_dev, 1);
+	if (err_code < 0)
+		return err_code;
+
+	err_code = mr_read(gspca_dev, 16);
+	if (err_code < 0)
+		return err_code;
+
+	if (verbose)
+		PDEBUG(D_PROBE, "Register: %02x reads %02x%02x%02x", reg,
+		       gspca_dev->usb_buf[0],
+		       gspca_dev->usb_buf[1],
+		       gspca_dev->usb_buf[2]);
+
+	return 0;
+}
+
+static int zero_the_pointer(struct gspca_dev *gspca_dev)
+{
+	__u8 *data = gspca_dev->usb_buf;
+	int err_code;
+	u8 status = 0;
+	int tries = 0;
+
+	err_code = cam_get_response16(gspca_dev, 0x21, 0);
+	if (err_code < 0)
+		return err_code;
+
+	data[0] = 0x19;
+	data[1] = 0x51;
+	err_code = mr_write(gspca_dev, 2);
+	if (err_code < 0)
+		return err_code;
+
+	err_code = cam_get_response16(gspca_dev, 0x21, 0);
+	if (err_code < 0)
+		return err_code;
+
+	data[0] = 0x19;
+	data[1] = 0xba;
+	err_code = mr_write(gspca_dev, 2);
+	if (err_code < 0)
+		return err_code;
+
+	err_code = cam_get_response16(gspca_dev, 0x21, 0);
+	if (err_code < 0)
+		return err_code;
+
+	data[0] = 0x19;
+	data[1] = 0x00;
+	err_code = mr_write(gspca_dev, 2);
+	if (err_code < 0)
+		return err_code;
+
+	err_code = cam_get_response16(gspca_dev, 0x21, 0);
+	if (err_code < 0)
+		return err_code;
+
+	data[0] = 0x19;
+	data[1] = 0x00;
+	err_code = mr_write(gspca_dev, 2);
+	if (err_code < 0)
+		return err_code;
+
+	while (status != 0x0a && tries < 256) {
+		err_code = cam_get_response16(gspca_dev, 0x21, 0);
+		status = data[0];
+		tries++;
+		if (err_code < 0)
+			return err_code;
+	}
+	if (status != 0x0a)
+		PERR("status is %02x", status);
+
+	tries = 0;
+	while (tries < 4) {
+		data[0] = 0x19;
+		data[1] = 0x00;
+		err_code = mr_write(gspca_dev, 2);
+		if (err_code < 0)
+			return err_code;
+
+		err_code = cam_get_response16(gspca_dev, 0x21, 0);
+		status = data[0];
+		tries++;
+		if (err_code < 0)
+			return err_code;
+	}
+
+	data[0] = 0x19;
+	err_code = mr_write(gspca_dev, 1);
+	if (err_code < 0)
+		return err_code;
+
+	err_code = mr_read(gspca_dev, 16);
+	if (err_code < 0)
+		return err_code;
+
+	return 0;
+}
+
+static int stream_start(struct gspca_dev *gspca_dev)
+{
+	gspca_dev->usb_buf[0] = 0x01;
+	gspca_dev->usb_buf[1] = 0x01;
+	return mr_write(gspca_dev, 2);
+}
+
+static void stream_stop(struct gspca_dev *gspca_dev)
+{
+	gspca_dev->usb_buf[0] = 0x01;
+	gspca_dev->usb_buf[1] = 0x00;
+	if (mr_write(gspca_dev, 2) < 0)
+		PERR("Stream Stop failed");
+}
+
+static void lcd_stop(struct gspca_dev *gspca_dev)
+{
+	gspca_dev->usb_buf[0] = 0x19;
+	gspca_dev->usb_buf[1] = 0x54;
+	if (mr_write(gspca_dev, 2) < 0)
+		PERR("LCD Stop failed");
+}
+
+static int isoc_enable(struct gspca_dev *gspca_dev)
+{
+	gspca_dev->usb_buf[0] = 0x00;
+	gspca_dev->usb_buf[1] = 0x4d;  /* ISOC transferring enable... */
+	return mr_write(gspca_dev, 2);
+}
+
+/* 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 err_code;
+
+	cam = &gspca_dev->cam;
+	cam->cam_mode = vga_mode;
+	cam->nmodes = ARRAY_SIZE(vga_mode);
+	sd->do_lcd_stop = 0;
+
+	/* Several of the supported CIF cameras share the same USB ID but
+	 * require different initializations and different control settings.
+	 * The same is true of the VGA cameras. Therefore, we are forced
+	 * to start the initialization process in order to determine which
+	 * camera is present. Some of the supported cameras require the
+	 * memory pointer to be set to 0 as the very first item of business
+	 * or else they will not stream. So we do that immediately.
+	 */
+	err_code = zero_the_pointer(gspca_dev);
+	if (err_code < 0)
+		return err_code;
+
+	err_code = stream_start(gspca_dev);
+	if (err_code < 0)
+		return err_code;
+
+	/* Now, the query for sensor type. */
+	err_code = cam_get_response16(gspca_dev, 0x07, 1);
+	if (err_code < 0)
+		return err_code;
+
+	if (id->idProduct == 0x0110 || id->idProduct == 0x010e) {
+		sd->cam_type = CAM_TYPE_CIF;
+		cam->nmodes--;
+		/*
+		 * All but one of the known CIF cameras share the same USB ID,
+		 * but two different init routines are in use, and the control
+		 * settings are different, too. We need to detect which camera
+		 * of the two known varieties is connected!
+		 *
+		 * A list of known CIF cameras follows. They all report either
+		 * 0200 for type 0 or 0300 for type 1.
+		 * If you have another to report, please do
+		 *
+		 * Name		sd->sensor_type		reported by
+		 *
+		 * Sakar 56379 Spy-shot	0		T. Kilgore
+		 * Innovage		0		T. Kilgore
+		 * Vivitar Mini		0		H. De Goede
+		 * Vivitar Mini		0		E. Rodriguez
+		 * Vivitar Mini		1		T. Kilgore
+		 * Elta-Media 8212dc	1		T. Kaiser
+		 * Philips dig. keych.	1		T. Kilgore
+		 * Trust Spyc@m 100	1		A. Jacobs
+		 */
+		switch (gspca_dev->usb_buf[0]) {
+		case 2:
+			sd->sensor_type = 0;
+			break;
+		case 3:
+			sd->sensor_type = 1;
+			break;
+		default:
+			pr_err("Unknown CIF Sensor id : %02x\n",
+			       gspca_dev->usb_buf[1]);
+			return -ENODEV;
+		}
+		PDEBUG(D_PROBE, "MR97310A CIF camera detected, sensor: %d",
+		       sd->sensor_type);
+	} else {
+		sd->cam_type = CAM_TYPE_VGA;
+
+		/*
+		 * Here is a table of the responses to the query for sensor
+		 * type, from the known MR97310A VGA cameras. Six different
+		 * cameras of which five share the same USB ID.
+		 *
+		 * Name			gspca_dev->usb_buf[]	sd->sensor_type
+		 *				sd->do_lcd_stop
+		 * Aiptek Pencam VGA+	0300		0		1
+		 * ION digital		0300		0		1
+		 * Argus DC-1620	0450		1		0
+		 * Argus QuickClix	0420		1		1
+		 * Sakar 77379 Digital	0350		0		1
+		 * Sakar 1638x CyberPix	0120		0		2
+		 *
+		 * Based upon these results, we assume default settings
+		 * and then correct as necessary, as follows.
+		 *
+		 */
+
+		sd->sensor_type = 1;
+		sd->do_lcd_stop = 0;
+		sd->adj_colors = 0;
+		if (gspca_dev->usb_buf[0] == 0x01) {
+			sd->sensor_type = 2;
+		} else if ((gspca_dev->usb_buf[0] != 0x03) &&
+					(gspca_dev->usb_buf[0] != 0x04)) {
+			pr_err("Unknown VGA Sensor id Byte 0: %02x\n",
+			       gspca_dev->usb_buf[0]);
+			pr_err("Defaults assumed, may not work\n");
+			pr_err("Please report this\n");
+		}
+		/* Sakar Digital color needs to be adjusted. */
+		if ((gspca_dev->usb_buf[0] == 0x03) &&
+					(gspca_dev->usb_buf[1] == 0x50))
+			sd->adj_colors = 1;
+		if (gspca_dev->usb_buf[0] == 0x04) {
+			sd->do_lcd_stop = 1;
+			switch (gspca_dev->usb_buf[1]) {
+			case 0x50:
+				sd->sensor_type = 0;
+				PDEBUG(D_PROBE, "sensor_type corrected to 0");
+				break;
+			case 0x20:
+				/* Nothing to do here. */
+				break;
+			default:
+				pr_err("Unknown VGA Sensor id Byte 1: %02x\n",
+				       gspca_dev->usb_buf[1]);
+				pr_err("Defaults assumed, may not work\n");
+				pr_err("Please report this\n");
+			}
+		}
+		PDEBUG(D_PROBE, "MR97310A VGA camera detected, sensor: %d",
+		       sd->sensor_type);
+	}
+	/* Stop streaming as we've started it only to probe the sensor type. */
+	sd_stopN(gspca_dev);
+
+	if (force_sensor_type != -1) {
+		sd->sensor_type = !!force_sensor_type;
+		PDEBUG(D_PROBE, "Forcing sensor type to: %d",
+		       sd->sensor_type);
+	}
+
+	return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+	return 0;
+}
+
+static int start_cif_cam(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	__u8 *data = gspca_dev->usb_buf;
+	int err_code;
+	static const __u8 startup_string[] = {
+		0x00,
+		0x0d,
+		0x01,
+		0x00, /* Hsize/8 for 352 or 320 */
+		0x00, /* Vsize/4 for 288 or 240 */
+		0x13, /* or 0xbb, depends on sensor */
+		0x00, /* Hstart, depends on res. */
+		0x00, /* reserved ? */
+		0x00, /* Vstart, depends on res. and sensor */
+		0x50, /* 0x54 to get 176 or 160 */
+		0xc0
+	};
+
+	/* Note: Some of the above descriptions guessed from MR97113A driver */
+
+	memcpy(data, startup_string, 11);
+	if (sd->sensor_type)
+		data[5] = 0xbb;
+
+	switch (gspca_dev->pixfmt.width) {
+	case 160:
+		data[9] |= 0x04;  /* reg 8, 2:1 scale down from 320 */
+		/* fall thru */
+	case 320:
+	default:
+		data[3] = 0x28;			   /* reg 2, H size/8 */
+		data[4] = 0x3c;			   /* reg 3, V size/4 */
+		data[6] = 0x14;			   /* reg 5, H start  */
+		data[8] = 0x1a + sd->sensor_type;  /* reg 7, V start  */
+		break;
+	case 176:
+		data[9] |= 0x04;  /* reg 8, 2:1 scale down from 352 */
+		/* fall thru */
+	case 352:
+		data[3] = 0x2c;			   /* reg 2, H size/8 */
+		data[4] = 0x48;			   /* reg 3, V size/4 */
+		data[6] = 0x06;			   /* reg 5, H start  */
+		data[8] = 0x06 - sd->sensor_type;  /* reg 7, V start  */
+		break;
+	}
+	err_code = mr_write(gspca_dev, 11);
+	if (err_code < 0)
+		return err_code;
+
+	if (!sd->sensor_type) {
+		static const struct sensor_w_data cif_sensor0_init_data[] = {
+			{0x02, 0x00, {0x03, 0x5a, 0xb5, 0x01,
+				      0x0f, 0x14, 0x0f, 0x10}, 8},
+			{0x0c, 0x00, {0x04, 0x01, 0x01, 0x00, 0x1f}, 5},
+			{0x12, 0x00, {0x07}, 1},
+			{0x1f, 0x00, {0x06}, 1},
+			{0x27, 0x00, {0x04}, 1},
+			{0x29, 0x00, {0x0c}, 1},
+			{0x40, 0x00, {0x40, 0x00, 0x04}, 3},
+			{0x50, 0x00, {0x60}, 1},
+			{0x60, 0x00, {0x06}, 1},
+			{0x6b, 0x00, {0x85, 0x85, 0xc8, 0xc8, 0xc8, 0xc8}, 6},
+			{0x72, 0x00, {0x1e, 0x56}, 2},
+			{0x75, 0x00, {0x58, 0x40, 0xa2, 0x02, 0x31, 0x02,
+				      0x31, 0x80, 0x00}, 9},
+			{0x11, 0x00, {0x01}, 1},
+			{0, 0, {0}, 0}
+		};
+		err_code = sensor_write_regs(gspca_dev, cif_sensor0_init_data,
+					 ARRAY_SIZE(cif_sensor0_init_data));
+	} else {	/* sd->sensor_type = 1 */
+		static const struct sensor_w_data cif_sensor1_init_data[] = {
+			/* Reg 3,4, 7,8 get set by the controls */
+			{0x02, 0x00, {0x10}, 1},
+			{0x05, 0x01, {0x22}, 1}, /* 5/6 also seen as 65h/32h */
+			{0x06, 0x01, {0x00}, 1},
+			{0x09, 0x02, {0x0e}, 1},
+			{0x0a, 0x02, {0x05}, 1},
+			{0x0b, 0x02, {0x05}, 1},
+			{0x0c, 0x02, {0x0f}, 1},
+			{0x0d, 0x02, {0x07}, 1},
+			{0x0e, 0x02, {0x0c}, 1},
+			{0x0f, 0x00, {0x00}, 1},
+			{0x10, 0x00, {0x06}, 1},
+			{0x11, 0x00, {0x07}, 1},
+			{0x12, 0x00, {0x00}, 1},
+			{0x13, 0x00, {0x01}, 1},
+			{0, 0, {0}, 0}
+		};
+		/* Without this command the cam won't work with USB-UHCI */
+		gspca_dev->usb_buf[0] = 0x0a;
+		gspca_dev->usb_buf[1] = 0x00;
+		err_code = mr_write(gspca_dev, 2);
+		if (err_code < 0)
+			return err_code;
+		err_code = sensor_write_regs(gspca_dev, cif_sensor1_init_data,
+					 ARRAY_SIZE(cif_sensor1_init_data));
+	}
+	return err_code;
+}
+
+static int start_vga_cam(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	__u8 *data = gspca_dev->usb_buf;
+	int err_code;
+	static const __u8 startup_string[] =
+		{0x00, 0x0d, 0x01, 0x00, 0x00, 0x2b, 0x00, 0x00,
+		 0x00, 0x50, 0xc0};
+	/* What some of these mean is explained in start_cif_cam(), above */
+
+	memcpy(data, startup_string, 11);
+	if (!sd->sensor_type) {
+		data[5]  = 0x00;
+		data[10] = 0x91;
+	}
+	if (sd->sensor_type == 2) {
+		data[5]  = 0x00;
+		data[10] = 0x18;
+	}
+
+	switch (gspca_dev->pixfmt.width) {
+	case 160:
+		data[9] |= 0x0c;  /* reg 8, 4:1 scale down */
+		/* fall thru */
+	case 320:
+		data[9] |= 0x04;  /* reg 8, 2:1 scale down */
+		/* fall thru */
+	case 640:
+	default:
+		data[3] = 0x50;  /* reg 2, H size/8 */
+		data[4] = 0x78;  /* reg 3, V size/4 */
+		data[6] = 0x04;  /* reg 5, H start */
+		data[8] = 0x03;  /* reg 7, V start */
+		if (sd->sensor_type == 2) {
+			data[6] = 2;
+			data[8] = 1;
+		}
+		if (sd->do_lcd_stop)
+			data[8] = 0x04;  /* Bayer tile shifted */
+		break;
+
+	case 176:
+		data[9] |= 0x04;  /* reg 8, 2:1 scale down */
+		/* fall thru */
+	case 352:
+		data[3] = 0x2c;  /* reg 2, H size */
+		data[4] = 0x48;  /* reg 3, V size */
+		data[6] = 0x94;  /* reg 5, H start */
+		data[8] = 0x63;  /* reg 7, V start */
+		if (sd->do_lcd_stop)
+			data[8] = 0x64;  /* Bayer tile shifted */
+		break;
+	}
+
+	err_code = mr_write(gspca_dev, 11);
+	if (err_code < 0)
+		return err_code;
+
+	if (!sd->sensor_type) {
+		static const struct sensor_w_data vga_sensor0_init_data[] = {
+			{0x01, 0x00, {0x0c, 0x00, 0x04}, 3},
+			{0x14, 0x00, {0x01, 0xe4, 0x02, 0x84}, 4},
+			{0x20, 0x00, {0x00, 0x80, 0x00, 0x08}, 4},
+			{0x25, 0x00, {0x03, 0xa9, 0x80}, 3},
+			{0x30, 0x00, {0x30, 0x18, 0x10, 0x18}, 4},
+			{0, 0, {0}, 0}
+		};
+		err_code = sensor_write_regs(gspca_dev, vga_sensor0_init_data,
+					 ARRAY_SIZE(vga_sensor0_init_data));
+	} else if (sd->sensor_type == 1) {
+		static const struct sensor_w_data color_adj[] = {
+			{0x02, 0x00, {0x06, 0x59, 0x0c, 0x16, 0x00,
+				/* adjusted blue, green, red gain correct
+				   too much blue from the Sakar Digital */
+				0x05, 0x01, 0x04}, 8}
+		};
+
+		static const struct sensor_w_data color_no_adj[] = {
+			{0x02, 0x00, {0x06, 0x59, 0x0c, 0x16, 0x00,
+				/* default blue, green, red gain settings */
+				0x07, 0x00, 0x01}, 8}
+		};
+
+		static const struct sensor_w_data vga_sensor1_init_data[] = {
+			{0x11, 0x04, {0x01}, 1},
+			{0x0a, 0x00, {0x00, 0x01, 0x00, 0x00, 0x01,
+			/* These settings may be better for some cameras */
+			/* {0x0a, 0x00, {0x01, 0x06, 0x00, 0x00, 0x01, */
+				0x00, 0x0a}, 7},
+			{0x11, 0x04, {0x01}, 1},
+			{0x12, 0x00, {0x00, 0x63, 0x00, 0x70, 0x00, 0x00}, 6},
+			{0x11, 0x04, {0x01}, 1},
+			{0, 0, {0}, 0}
+		};
+
+		if (sd->adj_colors)
+			err_code = sensor_write_regs(gspca_dev, color_adj,
+					 ARRAY_SIZE(color_adj));
+		else
+			err_code = sensor_write_regs(gspca_dev, color_no_adj,
+					 ARRAY_SIZE(color_no_adj));
+
+		if (err_code < 0)
+			return err_code;
+
+		err_code = sensor_write_regs(gspca_dev, vga_sensor1_init_data,
+					 ARRAY_SIZE(vga_sensor1_init_data));
+	} else {	/* sensor type == 2 */
+		static const struct sensor_w_data vga_sensor2_init_data[] = {
+
+			{0x01, 0x00, {0x48}, 1},
+			{0x02, 0x00, {0x22}, 1},
+			/* Reg 3 msb and 4 is lsb of the exposure setting*/
+			{0x05, 0x00, {0x10}, 1},
+			{0x06, 0x00, {0x00}, 1},
+			{0x07, 0x00, {0x00}, 1},
+			{0x08, 0x00, {0x00}, 1},
+			{0x09, 0x00, {0x00}, 1},
+			/* The following are used in the gain control
+			 * which is BTW completely borked in the OEM driver
+			 * The values for each color go from 0 to 0x7ff
+			 *{0x0a, 0x00, {0x01}, 1},  green1 gain msb
+			 *{0x0b, 0x00, {0x10}, 1},  green1 gain lsb
+			 *{0x0c, 0x00, {0x01}, 1},  red gain msb
+			 *{0x0d, 0x00, {0x10}, 1},  red gain lsb
+			 *{0x0e, 0x00, {0x01}, 1},  blue gain msb
+			 *{0x0f, 0x00, {0x10}, 1},  blue gain lsb
+			 *{0x10, 0x00, {0x01}, 1}, green2 gain msb
+			 *{0x11, 0x00, {0x10}, 1}, green2 gain lsb
+			 */
+			{0x12, 0x00, {0x00}, 1},
+			{0x13, 0x00, {0x04}, 1}, /* weird effect on colors */
+			{0x14, 0x00, {0x00}, 1},
+			{0x15, 0x00, {0x06}, 1},
+			{0x16, 0x00, {0x01}, 1},
+			{0x17, 0x00, {0xe2}, 1}, /* vertical alignment */
+			{0x18, 0x00, {0x02}, 1},
+			{0x19, 0x00, {0x82}, 1}, /* don't mess with */
+			{0x1a, 0x00, {0x00}, 1},
+			{0x1b, 0x00, {0x20}, 1},
+			/* {0x1c, 0x00, {0x17}, 1}, contrast control */
+			{0x1d, 0x00, {0x80}, 1}, /* moving causes a mess */
+			{0x1e, 0x00, {0x08}, 1}, /* moving jams the camera */
+			{0x1f, 0x00, {0x0c}, 1},
+			{0x20, 0x00, {0x00}, 1},
+			{0, 0, {0}, 0}
+		};
+		err_code = sensor_write_regs(gspca_dev, vga_sensor2_init_data,
+					 ARRAY_SIZE(vga_sensor2_init_data));
+	}
+	return err_code;
+}
+
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int err_code;
+
+	sd->sof_read = 0;
+
+	/* Some of the VGA cameras require the memory pointer
+	 * to be set to 0 again. We have been forced to start the
+	 * stream in sd_config() to detect the hardware, and closed it.
+	 * Thus, we need here to do a completely fresh and clean start. */
+	err_code = zero_the_pointer(gspca_dev);
+	if (err_code < 0)
+		return err_code;
+
+	err_code = stream_start(gspca_dev);
+	if (err_code < 0)
+		return err_code;
+
+	if (sd->cam_type == CAM_TYPE_CIF) {
+		err_code = start_cif_cam(gspca_dev);
+	} else {
+		err_code = start_vga_cam(gspca_dev);
+	}
+	if (err_code < 0)
+		return err_code;
+
+	return isoc_enable(gspca_dev);
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	stream_stop(gspca_dev);
+	/* Not all the cams need this, but even if not, probably a good idea */
+	zero_the_pointer(gspca_dev);
+	if (sd->do_lcd_stop)
+		lcd_stop(gspca_dev);
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u8 sign_reg = 7;  /* This reg and the next one used on CIF cams. */
+	u8 value_reg = 8; /* VGA cams seem to use regs 0x0b and 0x0c */
+	static const u8 quick_clix_table[] =
+	/*	  0  1  2   3  4  5  6  7  8  9  10  11  12  13  14  15 */
+		{ 0, 4, 8, 12, 1, 2, 3, 5, 6, 9,  7, 10, 13, 11, 14, 15};
+	if (sd->cam_type == CAM_TYPE_VGA) {
+		sign_reg += 4;
+		value_reg += 4;
+	}
+
+	/* Note register 7 is also seen as 0x8x or 0xCx in some dumps */
+	if (val > 0) {
+		sensor_write1(gspca_dev, sign_reg, 0x00);
+	} else {
+		sensor_write1(gspca_dev, sign_reg, 0x01);
+		val = 257 - val;
+	}
+	/* Use lookup table for funky Argus QuickClix brightness */
+	if (sd->do_lcd_stop)
+		val = quick_clix_table[val];
+
+	sensor_write1(gspca_dev, value_reg, val);
+}
+
+static void setexposure(struct gspca_dev *gspca_dev, s32 expo, s32 min_clockdiv)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int exposure = MR97310A_EXPOSURE_DEFAULT;
+	u8 buf[2];
+
+	if (sd->cam_type == CAM_TYPE_CIF && sd->sensor_type == 1) {
+		/* This cam does not like exposure settings < 300,
+		   so scale 0 - 4095 to 300 - 4095 */
+		exposure = (expo * 9267) / 10000 + 300;
+		sensor_write1(gspca_dev, 3, exposure >> 4);
+		sensor_write1(gspca_dev, 4, exposure & 0x0f);
+	} else if (sd->sensor_type == 2) {
+		exposure = expo;
+		exposure >>= 3;
+		sensor_write1(gspca_dev, 3, exposure >> 8);
+		sensor_write1(gspca_dev, 4, exposure & 0xff);
+	} else {
+		/* We have both a clock divider and an exposure register.
+		   We first calculate the clock divider, as that determines
+		   the maximum exposure and then we calculate the exposure
+		   register setting (which goes from 0 - 511).
+
+		   Note our 0 - 4095 exposure is mapped to 0 - 511
+		   milliseconds exposure time */
+		u8 clockdiv = (60 * expo + 7999) / 8000;
+
+		/* Limit framerate to not exceed usb bandwidth */
+		if (clockdiv < min_clockdiv && gspca_dev->pixfmt.width >= 320)
+			clockdiv = min_clockdiv;
+		else if (clockdiv < 2)
+			clockdiv = 2;
+
+		if (sd->cam_type == CAM_TYPE_VGA && clockdiv < 4)
+			clockdiv = 4;
+
+		/* Frame exposure time in ms = 1000 * clockdiv / 60 ->
+		exposure = (sd->exposure / 8) * 511 / (1000 * clockdiv / 60) */
+		exposure = (60 * 511 * expo) / (8000 * clockdiv);
+		if (exposure > 511)
+			exposure = 511;
+
+		/* exposure register value is reversed! */
+		exposure = 511 - exposure;
+
+		buf[0] = exposure & 0xff;
+		buf[1] = exposure >> 8;
+		sensor_write_reg(gspca_dev, 0x0e, 0, buf, 2);
+		sensor_write1(gspca_dev, 0x02, clockdiv);
+	}
+}
+
+static void setgain(struct gspca_dev *gspca_dev, s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u8 gainreg;
+
+	if (sd->cam_type == CAM_TYPE_CIF && sd->sensor_type == 1)
+		sensor_write1(gspca_dev, 0x0e, val);
+	else if (sd->cam_type == CAM_TYPE_VGA && sd->sensor_type == 2)
+		for (gainreg = 0x0a; gainreg < 0x11; gainreg += 2) {
+			sensor_write1(gspca_dev, gainreg, val >> 8);
+			sensor_write1(gspca_dev, gainreg + 1, val & 0xff);
+		}
+	else
+		sensor_write1(gspca_dev, 0x10, val);
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
+{
+	sensor_write1(gspca_dev, 0x1c, val);
+}
+
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct gspca_dev *gspca_dev =
+		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+	struct sd *sd = (struct sd *)gspca_dev;
+
+	gspca_dev->usb_err = 0;
+
+	if (!gspca_dev->streaming)
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_BRIGHTNESS:
+		setbrightness(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_CONTRAST:
+		setcontrast(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_EXPOSURE:
+		setexposure(gspca_dev, sd->exposure->val,
+			    sd->min_clockdiv ? sd->min_clockdiv->val : 0);
+		break;
+	case V4L2_CID_GAIN:
+		setgain(gspca_dev, ctrl->val);
+		break;
+	}
+	return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+	.s_ctrl = sd_s_ctrl,
+};
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *)gspca_dev;
+	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+	static const struct v4l2_ctrl_config clockdiv = {
+		.ops = &sd_ctrl_ops,
+		.id = MR97310A_CID_CLOCKDIV,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.name = "Minimum Clock Divider",
+		.min = MR97310A_MIN_CLOCKDIV_MIN,
+		.max = MR97310A_MIN_CLOCKDIV_MAX,
+		.step = 1,
+		.def = MR97310A_MIN_CLOCKDIV_DEFAULT,
+	};
+	bool has_brightness = false;
+	bool has_argus_brightness = false;
+	bool has_contrast = false;
+	bool has_gain = false;
+	bool has_cs_gain = false;
+	bool has_exposure = false;
+	bool has_clockdiv = false;
+
+	gspca_dev->vdev.ctrl_handler = hdl;
+	v4l2_ctrl_handler_init(hdl, 4);
+
+	/* Setup controls depending on camera type */
+	if (sd->cam_type == CAM_TYPE_CIF) {
+		/* No brightness for sensor_type 0 */
+		if (sd->sensor_type == 0)
+			has_exposure = has_gain = has_clockdiv = true;
+		else
+			has_exposure = has_gain = has_brightness = true;
+	} else {
+		/* All controls need to be disabled if VGA sensor_type is 0 */
+		if (sd->sensor_type == 0)
+			; /* no controls! */
+		else if (sd->sensor_type == 2)
+			has_exposure = has_cs_gain = has_contrast = true;
+		else if (sd->do_lcd_stop)
+			has_exposure = has_gain = has_argus_brightness =
+				has_clockdiv = true;
+		else
+			has_exposure = has_gain = has_brightness =
+				has_clockdiv = true;
+	}
+
+	/* Separate brightness control description for Argus QuickClix as it has
+	 * different limits from the other mr97310a cameras, and separate gain
+	 * control for Sakar CyberPix camera. */
+	/*
+	 * This control is disabled for CIF type 1 and VGA type 0 cameras.
+	 * It does not quite act linearly for the Argus QuickClix camera,
+	 * but it does control brightness. The values are 0 - 15 only, and
+	 * the table above makes them act consecutively.
+	 */
+	if (has_brightness)
+		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_BRIGHTNESS, -254, 255, 1,
+			MR97310A_BRIGHTNESS_DEFAULT);
+	else if (has_argus_brightness)
+		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_BRIGHTNESS, 0, 15, 1,
+			MR97310A_BRIGHTNESS_DEFAULT);
+	if (has_contrast)
+		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_CONTRAST, MR97310A_CONTRAST_MIN,
+			MR97310A_CONTRAST_MAX, 1, MR97310A_CONTRAST_DEFAULT);
+	if (has_gain)
+		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_GAIN, MR97310A_GAIN_MIN, MR97310A_GAIN_MAX,
+			1, MR97310A_GAIN_DEFAULT);
+	else if (has_cs_gain)
+		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_GAIN,
+			MR97310A_CS_GAIN_MIN, MR97310A_CS_GAIN_MAX,
+			1, MR97310A_CS_GAIN_DEFAULT);
+	if (has_exposure)
+		sd->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_EXPOSURE, MR97310A_EXPOSURE_MIN,
+			MR97310A_EXPOSURE_MAX, 1, MR97310A_EXPOSURE_DEFAULT);
+	if (has_clockdiv)
+		sd->min_clockdiv = v4l2_ctrl_new_custom(hdl, &clockdiv, NULL);
+
+	if (hdl->error) {
+		pr_err("Could not initialize controls\n");
+		return hdl->error;
+	}
+	if (has_exposure && has_clockdiv)
+		v4l2_ctrl_cluster(2, &sd->exposure);
+	return 0;
+}
+
+/* Include pac common sof detection functions */
+#include "pac_common.h"
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+			u8 *data,		/* isoc packet */
+			int len)		/* iso packet length */
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	unsigned char *sof;
+
+	sof = pac_find_sof(gspca_dev, &sd->sof_read, data, len);
+	if (sof) {
+		int n;
+
+		/* finish decoding current frame */
+		n = sof - data;
+		if (n > sizeof pac_sof_marker)
+			n -= sizeof pac_sof_marker;
+		else
+			n = 0;
+		gspca_frame_add(gspca_dev, LAST_PACKET,
+					data, n);
+		/* Start next frame. */
+		gspca_frame_add(gspca_dev, FIRST_PACKET,
+			pac_sof_marker, sizeof pac_sof_marker);
+		len -= sof - data;
+		data = sof;
+	}
+	gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+	.name = MODULE_NAME,
+	.config = sd_config,
+	.init = sd_init,
+	.init_controls = sd_init_controls,
+	.start = sd_start,
+	.stopN = sd_stopN,
+	.pkt_scan = sd_pkt_scan,
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+	{USB_DEVICE(0x08ca, 0x0110)},	/* Trust Spyc@m 100 */
+	{USB_DEVICE(0x08ca, 0x0111)},	/* Aiptek Pencam VGA+ */
+	{USB_DEVICE(0x093a, 0x010f)},	/* All other known MR97310A VGA cams */
+	{USB_DEVICE(0x093a, 0x010e)},	/* All known MR97310A CIF 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,
+#ifdef CONFIG_PM
+	.suspend = gspca_suspend,
+	.resume = gspca_resume,
+	.reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/usb/gspca/nw80x.c b/drivers/media/usb/gspca/nw80x.c
new file mode 100644
index 0000000..599f755
--- /dev/null
+++ b/drivers/media/usb/gspca/nw80x.c
@@ -0,0 +1,2111 @@
+/*
+ * DivIO nw80x subdriver
+ *
+ * Copyright (C) 2011 Jean-François Moine (http://moinejf.free.fr)
+ * Copyright (C) 2003 Sylvain Munaut <tnt@246tNt.com>
+ *			Kjell Claesson <keyson@users.sourceforge.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
+ * 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "nw80x"
+
+#include "gspca.h"
+
+MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>");
+MODULE_DESCRIPTION("NW80x USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+static int webcam;
+
+/* specific webcam descriptor */
+struct sd {
+	struct gspca_dev gspca_dev;	/* !! must be the first item */
+
+	u32 ae_res;
+	s8 ag_cnt;
+#define AG_CNT_START 13
+	u8 exp_too_low_cnt;
+	u8 exp_too_high_cnt;
+
+	u8 bridge;
+	u8 webcam;
+};
+
+enum bridges {
+	BRIDGE_NW800,	/* and et31x110 */
+	BRIDGE_NW801,
+	BRIDGE_NW802,
+};
+enum webcams {
+	Generic800,
+	SpaceCam,	/* Trust 120 SpaceCam */
+	SpaceCam2,	/* other Trust 120 SpaceCam */
+	Cvideopro,	/* Conceptronic Video Pro */
+	Dlink350c,
+	DS3303u,
+	Kr651us,
+	Kritter,
+	Mustek300,
+	Proscope,
+	Twinkle,
+	DvcV6,
+	P35u,
+	Generic802,
+	NWEBCAMS	/* number of webcams */
+};
+
+static const u8 webcam_chip[NWEBCAMS] = {
+	[Generic800]	= BRIDGE_NW800,	/* 06a5:0000
+					 * Typhoon Webcam 100 USB */
+
+	[SpaceCam]	= BRIDGE_NW800,	/* 06a5:d800
+				* Trust SpaceCam120 or SpaceCam100 PORTABLE */
+
+	[SpaceCam2]	= BRIDGE_NW800,	/* 06a5:d800 - pas106
+			* other Trust SpaceCam120 or SpaceCam100 PORTABLE */
+
+	[Cvideopro]	= BRIDGE_NW802,	/* 06a5:d001
+			* Conceptronic Video Pro 'CVIDEOPRO USB Webcam CCD' */
+
+	[Dlink350c]	= BRIDGE_NW802,	/* 06a5:d001
+					 * D-Link NetQam Pro 250plus */
+
+	[DS3303u]	= BRIDGE_NW801,	/* 06a5:d001
+				* Plustek Opticam 500U or ProLink DS3303u */
+
+	[Kr651us]	= BRIDGE_NW802,	/* 06a5:d001
+					 * Panasonic GP-KR651US */
+
+	[Kritter]	= BRIDGE_NW802,	/* 06a5:d001
+					 * iRez Kritter cam */
+
+	[Mustek300]	= BRIDGE_NW802,	/* 055f:d001
+					 * Mustek Wcam 300 mini */
+
+	[Proscope]	= BRIDGE_NW802,	/* 06a5:d001
+					 * Scalar USB Microscope (ProScope) */
+
+	[Twinkle]	= BRIDGE_NW800,	/* 06a5:d800 - hv7121b? (seems pas106)
+					 * Divio Chicony TwinkleCam
+					 * DSB-C110 */
+
+	[DvcV6]		= BRIDGE_NW802,	/* 0502:d001
+					 * DVC V6 */
+
+	[P35u]		= BRIDGE_NW801,	/* 052b:d001, 06a5:d001 and 06be:d001
+					 * EZCam Pro p35u */
+
+	[Generic802]	= BRIDGE_NW802,
+};
+/*
+ * other webcams:
+ *	- nw801 046d:d001
+ *		Logitech QuickCam Pro (dark focus ring)
+ *	- nw801 0728:d001
+ *		AVerMedia Camguard
+ *	- nw??? 06a5:d001
+ *		D-Link NetQam Pro 250plus
+ *	- nw800 065a:d800
+ *		Showcam NGS webcam
+ *	- nw??? ????:????
+ *		Sceptre svc300
+ */
+
+/*
+ * registers
+ *    nw800/et31x110	  nw801		  nw802
+ *	0000..009e	0000..00a1	0000..009e
+ *	0200..0211	   id		   id
+ *	0300..0302	   id		   id
+ *	0400..0406	  (inex)	0400..0406
+ *	0500..0505	0500..0506	  (inex)
+ *	0600..061a	0600..0601	0600..0601
+ *	0800..0814	   id		   id
+ *	1000..109c	1000..10a1	1000..109a
+ */
+
+/* resolutions
+ *	nw800: 320x240, 352x288
+ *	nw801/802: 320x240, 640x480
+ */
+static const struct v4l2_pix_format cif_mode[] = {
+	{320, 240, V4L2_PIX_FMT_JPGL, V4L2_FIELD_NONE,
+		.bytesperline = 320,
+		.sizeimage = 320 * 240 * 4 / 8,
+		.colorspace = V4L2_COLORSPACE_JPEG},
+	{352, 288, V4L2_PIX_FMT_JPGL, V4L2_FIELD_NONE,
+		.bytesperline = 352,
+		.sizeimage = 352 * 288 * 4 / 8,
+		.colorspace = V4L2_COLORSPACE_JPEG}
+};
+static const struct v4l2_pix_format vga_mode[] = {
+	{320, 240, V4L2_PIX_FMT_JPGL, V4L2_FIELD_NONE,
+		.bytesperline = 320,
+		.sizeimage = 320 * 240 * 4 / 8,
+		.colorspace = V4L2_COLORSPACE_JPEG},
+	{640, 480, V4L2_PIX_FMT_JPGL, V4L2_FIELD_NONE,
+		.bytesperline = 640,
+		.sizeimage = 640 * 480 * 3 / 8,
+		.colorspace = V4L2_COLORSPACE_JPEG},
+};
+
+/*
+ * The sequences below contain:
+ *	- 1st and 2nd bytes: either
+ *		- register number (BE)
+ *		- I2C0 + i2c address
+ *	- 3rd byte: data length (=0 for end of sequence)
+ *	- n bytes: data
+ */
+#define I2C0 0xff
+
+static const u8 nw800_init[] = {
+	0x04, 0x05, 0x01, 0x61,
+	0x04, 0x04, 0x01, 0x01,
+	0x04, 0x06, 0x01, 0x04,
+	0x04, 0x04, 0x03, 0x00, 0x00, 0x00,
+	0x05, 0x05, 0x01, 0x00,
+	0, 0, 0
+};
+static const u8 nw800_start[] = {
+	0x04, 0x06, 0x01, 0xc0,
+	0x00, 0x00, 0x40, 0x10, 0x43, 0x00, 0xb4, 0x01, 0x10, 0x00, 0x4f,
+			  0xef, 0x0e, 0x00, 0x74, 0x01, 0x01, 0x00, 0x19,
+			  0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19,
+			  0x00, 0x01, 0x00, 0x19, 0x00, 0x3e, 0x00, 0x24,
+			  0x03, 0x3e, 0x00, 0x86, 0x00, 0x3e, 0x00, 0x86,
+			  0x00, 0x3e, 0x00, 0x86, 0x00, 0x01, 0x00, 0x01,
+			  0x00, 0x56, 0x00, 0x9e, 0x00, 0x56, 0x00, 0x9e,
+			  0x00, 0x56, 0x00, 0x9e, 0x00, 0x01, 0x00, 0x01,
+	0x00, 0x40, 0x40, 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78,
+			  0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01,
+			  0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78,
+			  0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01,
+			  0x00, 0xca, 0x03, 0x46, 0x04, 0xca, 0x03, 0x46,
+			  0x04, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xf0,
+			  0x00, 0x3e, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x2e,
+	0x00, 0x80, 0x1f, 0xa0, 0x48, 0xc3, 0x02, 0x88, 0x0c, 0x68, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0xa8, 0x06, 0x00, 0x08,
+			  0x00, 0x32, 0x01, 0x01, 0x00, 0x16, 0x00, 0x04,
+			  0x00, 0x4b, 0x00, 0x76, 0x00, 0x86, 0x00,
+	0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00,
+			  0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0,
+			  0x40, 0x20,
+	0x03, 0x00, 0x03, 0x03, 0x00, 0x00,
+	0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0x61, 0xc0,
+	0x05, 0x00, 0x06, 0xe8, 0x00, 0x00, 0x00, 0x20, 0x20,
+	0x06, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00,
+	0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00,
+	0x10, 0x00, 0x40, 0x83, 0x02, 0x20, 0x00, 0x13, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x0a,
+			  0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x49, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00,
+			  0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08,
+			  0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06,
+			  0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80,
+	0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99,
+			  0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc,
+			  0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54,
+			  0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2,
+			  0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43,
+			  0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3,
+			  0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32,
+			  0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3,
+	0x10, 0x80, 0x1d, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x62,
+			  0x01, 0x24, 0x01, 0x62, 0x01, 0x24, 0x01, 0x20,
+			  0x01, 0x60, 0x01, 0x00, 0x00,
+
+	0x04, 0x04, 0x01, 0xff,
+	0x04, 0x06, 0x01, 0xc4,
+
+	0x04, 0x06, 0x01, 0xc0,
+	0x00, 0x00, 0x40, 0x10, 0x43, 0x00, 0xb4, 0x01, 0x10, 0x00, 0x4f,
+			  0xef, 0x0e, 0x00, 0x74, 0x01, 0x01, 0x00, 0x19,
+			  0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19,
+			  0x00, 0x01, 0x00, 0x19, 0x00, 0x3e, 0x00, 0x24,
+			  0x03, 0x3e, 0x00, 0x86, 0x00, 0x3e, 0x00, 0x86,
+			  0x00, 0x3e, 0x00, 0x86, 0x00, 0x01, 0x00, 0x01,
+			  0x00, 0x56, 0x00, 0x9e, 0x00, 0x56, 0x00, 0x9e,
+			  0x00, 0x56, 0x00, 0x9e, 0x00, 0x01, 0x00, 0x01,
+	0x00, 0x40, 0x40, 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78,
+			  0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01,
+			  0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78,
+			  0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01,
+			  0x00, 0xca, 0x03, 0x46, 0x04, 0xca, 0x03, 0x46,
+			  0x04, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xf0,
+			  0x00, 0x3e, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x2e,
+	0x00, 0x80, 0x1f, 0xa0, 0x48, 0xc3, 0x02, 0x88, 0x0c, 0x68, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0xa8, 0x06, 0x00, 0x08,
+			  0x00, 0x32, 0x01, 0x01, 0x00, 0x16, 0x00, 0x04,
+			  0x00, 0x4b, 0x00, 0x76, 0x00, 0x86, 0x00,
+	0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00,
+			  0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0,
+			  0x40, 0x20,
+	0x03, 0x00, 0x03, 0x03, 0x00, 0x00,
+	0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0x61, 0xc0,
+	0x05, 0x00, 0x06, 0xe8, 0x00, 0x00, 0x00, 0x20, 0x20,
+	0x06, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00,
+	0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00,
+	0x10, 0x00, 0x40, 0x83, 0x02, 0x20, 0x00, 0x13, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x0a,
+			  0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x49, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00,
+			  0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08,
+			  0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06,
+			  0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80,
+	0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99,
+			  0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc,
+			  0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54,
+			  0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2,
+			  0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43,
+			  0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3,
+			  0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32,
+			  0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3,
+	0x10, 0x80, 0x1d, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x62,
+			  0x01, 0x24, 0x01, 0x62, 0x01, 0x24, 0x01, 0x20,
+			  0x01, 0x60, 0x01, 0x00, 0x00,
+
+	0x02, 0x00, 0x11, 0x48, 0x58, 0x9e, 0x48, 0x58, 0x00, 0x00, 0x00,
+			  0x00, 0x84, 0x36, 0x05, 0x01, 0xf2, 0x86, 0x65,
+			  0x40,
+	0x00, 0x80, 0x01, 0xa0,
+	0x10, 0x1a, 0x01, 0x00,
+	0x00, 0x91, 0x02, 0x6c, 0x01,
+	0x00, 0x03, 0x02, 0xc8, 0x01,
+	0x10, 0x1a, 0x01, 0x00,
+	0x10, 0x00, 0x01, 0x83,
+	0x10, 0x8f, 0x0c, 0x62, 0x01, 0x24, 0x01, 0x62, 0x01, 0x24, 0x01,
+			  0x20, 0x01, 0x60, 0x01,
+	0x10, 0x85, 0x08, 0x00, 0x00, 0x5f, 0x01, 0x00, 0x00, 0x1f, 0x01,
+	0x10, 0x1b, 0x02, 0x69, 0x00,
+	0x10, 0x11, 0x08, 0x00, 0x00, 0x5f, 0x01, 0x00, 0x00, 0x1f, 0x01,
+	0x05, 0x02, 0x01, 0x02,
+	0x06, 0x00, 0x02, 0x04, 0xd9,
+	0x05, 0x05, 0x01, 0x20,
+	0x05, 0x05, 0x01, 0x21,
+	0x10, 0x0e, 0x01, 0x08,
+	0x10, 0x41, 0x11, 0x00, 0x08, 0x21, 0x3d, 0x52, 0x63, 0x75, 0x83,
+			  0x91, 0x9e, 0xaa, 0xb6, 0xc1, 0xcc, 0xd6, 0xe0,
+			  0xea,
+	0x10, 0x03, 0x01, 0x00,
+	0x10, 0x0f, 0x02, 0x13, 0x13,
+	0x10, 0x03, 0x01, 0x14,
+	0x10, 0x41, 0x11, 0x00, 0x08, 0x21, 0x3d, 0x52, 0x63, 0x75, 0x83,
+			  0x91, 0x9e, 0xaa, 0xb6, 0xc1, 0xcc, 0xd6, 0xe0,
+			  0xea,
+	0x10, 0x0b, 0x01, 0x14,
+	0x10, 0x0d, 0x01, 0x20,
+	0x10, 0x0c, 0x01, 0x34,
+	0x04, 0x06, 0x01, 0xc3,
+	0x04, 0x04, 0x01, 0x00,
+	0x05, 0x02, 0x01, 0x02,
+	0x06, 0x00, 0x02, 0x00, 0x48,
+	0x05, 0x05, 0x01, 0x20,
+	0x05, 0x05, 0x01, 0x21,
+	0, 0, 0
+};
+
+/* 06a5:d001 - nw801 - Panasonic
+ *		P35u */
+static const u8 nw801_start_1[] = {
+	0x05, 0x06, 0x01, 0x04,
+	0x00, 0x00, 0x40, 0x0e, 0x00, 0x00, 0xf9, 0x02, 0x11, 0x00, 0x0e,
+			  0x01, 0x1f, 0x00, 0x0d, 0x02, 0x01, 0x00, 0x19,
+			  0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19,
+			  0x00, 0x01, 0x00, 0x19, 0x00, 0xce, 0x00, 0xf4,
+			  0x05, 0x3e, 0x00, 0x86, 0x00, 0x3e, 0x00, 0x86,
+			  0x00, 0x3e, 0x00, 0x86, 0x00, 0x01, 0x00, 0x01,
+			  0x00, 0x56, 0x00, 0x9e, 0x00, 0x56, 0x00, 0x9e,
+			  0x00, 0x56, 0x00, 0x9e, 0x00, 0x01, 0x00, 0x01,
+	0x00, 0x40, 0x40, 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78,
+			  0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01,
+			  0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78,
+			  0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01,
+			  0x00, 0xca, 0x03, 0x46, 0x04, 0xca, 0x03, 0x46,
+			  0x04, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xf0,
+			  0x00, 0x3e, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x2e,
+	0x00, 0x80, 0x22, 0xb4, 0x6f, 0x3f, 0x0f, 0x88, 0x20, 0x08, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x69, 0xa8, 0x1f, 0x00,
+			  0x0d, 0x02, 0x07, 0x00, 0x01, 0x00, 0x19, 0x00,
+			  0xf2, 0x00, 0x18, 0x06, 0x10, 0x06, 0x10, 0x00,
+			  0x36, 0x00,
+	0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00,
+			  0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0,
+			  0x40, 0x20,
+	0x03, 0x00, 0x03, 0x00, 0x00, 0x00,
+	0x05, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x06, 0x00, 0x02, 0x09, 0x99,
+	0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00,
+	0x10, 0x00, 0x40, 0x22, 0x02, 0x80, 0x00, 0x1e, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x0a, 0x15, 0x08, 0x08, 0x0a,
+			  0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x01, 0x35, 0xfd, 0x07, 0x3d, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x14, 0x02,
+			  0x00, 0x01, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
+			  0x40, 0x00, 0x00, 0x00, 0x40, 0x20, 0x10, 0x06,
+			  0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06, 0xf7,
+	0x10, 0x40, 0x40, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80, 0x80,
+			  0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99, 0xa4,
+			  0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc, 0xcf,
+			  0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, 0x64,
+			  0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, 0xe2,
+			  0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54,
+			  0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2,
+			  0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43,
+	0x10, 0x80, 0x22, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3,
+			  0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x82, 0x02,
+			  0xe4, 0x01, 0x40, 0x01, 0xf0, 0x00, 0x40, 0x01,
+			  0xf0, 0x00,
+	0, 0, 0,
+};
+static const u8 nw801_start_qvga[] = {
+	0x02, 0x00, 0x10, 0x3c, 0x50, 0x9e, 0x3c, 0x50, 0x00, 0x00, 0x00,
+			  0x00, 0x78, 0x18, 0x0b, 0x06, 0xa2, 0x86, 0x78,
+	0x02, 0x0f, 0x01, 0x6b,
+	0x10, 0x1a, 0x01, 0x15,
+	0x00, 0x00, 0x01, 0x1e,
+	0x10, 0x00, 0x01, 0x2f,
+	0x10, 0x8c, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00,
+	0x10, 0x11, 0x08, 0x29, 0x00, 0x18, 0x01, 0x1f, 0x00, 0xd2, 0x00,
+							/* AE window */
+	0, 0, 0,
+};
+static const u8 nw801_start_vga[] = {
+	0x02, 0x00, 0x10, 0x78, 0xa0, 0x97, 0x78, 0xa0, 0x00, 0x00, 0x00,
+			  0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xf0,
+	0x02, 0x0f, 0x01, 0xd5,
+	0x10, 0x1a, 0x01, 0x15,
+	0x00, 0x00, 0x01, 0x0e,
+	0x10, 0x00, 0x01, 0x22,
+	0x10, 0x8c, 0x08, 0x00, 0x00, 0x7f, 0x02, 0x00, 0x00, 0xdf, 0x01,
+	0x10, 0x11, 0x08, 0x51, 0x00, 0x30, 0x02, 0x3d, 0x00, 0xa4, 0x01,
+	0, 0, 0,
+};
+static const u8 nw801_start_2[] = {
+	0x10, 0x04, 0x01, 0x1a,
+	0x10, 0x19, 0x01, 0x09,				/* clock */
+	0x10, 0x24, 0x06, 0xc0, 0x00, 0x3f, 0x02, 0x00, 0x01,
+							 /* .. gain .. */
+	0x00, 0x03, 0x02, 0x92, 0x03,
+	0x00, 0x1d, 0x04, 0xf2, 0x00, 0x24, 0x07,
+	0x00, 0x7b, 0x01, 0xcf,
+	0x10, 0x94, 0x01, 0x07,
+	0x05, 0x05, 0x01, 0x01,
+	0x05, 0x04, 0x01, 0x01,
+	0x10, 0x0e, 0x01, 0x08,
+	0x10, 0x48, 0x11, 0x00, 0x37, 0x55, 0x6b, 0x7d, 0x8d, 0x9b, 0xa8,
+			  0xb4, 0xbf, 0xca, 0xd4, 0xdd, 0xe6, 0xef, 0xf0,
+			  0xf0,
+	0x10, 0x03, 0x01, 0x00,
+	0x10, 0x0f, 0x02, 0x0c, 0x0c,
+	0x10, 0x03, 0x01, 0x08,
+	0x10, 0x48, 0x11, 0x00, 0x37, 0x55, 0x6b, 0x7d, 0x8d, 0x9b, 0xa8,
+			  0xb4, 0xbf, 0xca, 0xd4, 0xdd, 0xe6, 0xef, 0xf0,
+			  0xf0,
+	0x10, 0x0b, 0x01, 0x0b,
+	0x10, 0x0d, 0x01, 0x0b,
+	0x10, 0x0c, 0x01, 0x1f,
+	0x05, 0x06, 0x01, 0x03,
+	0, 0, 0
+};
+
+/* nw802 (sharp IR3Y38M?) */
+static const u8 nw802_start[] = {
+	0x04, 0x06, 0x01, 0x04,
+	0x00, 0x00, 0x40, 0x10, 0x00, 0x00, 0xf9, 0x02, 0x10, 0x00, 0x4d,
+			  0x0f, 0x1f, 0x00, 0x0d, 0x02, 0x01, 0x00, 0x19,
+			  0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19,
+			  0x00, 0x01, 0x00, 0x19, 0x00, 0xce, 0x00, 0xf4,
+			  0x05, 0x3e, 0x00, 0x86, 0x00, 0x3e, 0x00, 0x86,
+			  0x00, 0x3e, 0x00, 0x86, 0x00, 0x01, 0x00, 0x01,
+			  0x00, 0x56, 0x00, 0x9e, 0x00, 0x56, 0x00, 0x9e,
+			  0x00, 0x56, 0x00, 0x9e, 0x00, 0x01, 0x00, 0x01,
+	0x00, 0x40, 0x40, 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78,
+			  0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01,
+			  0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78,
+			  0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01,
+			  0x00, 0xca, 0x03, 0x46, 0x04, 0xca, 0x03, 0x46,
+			  0x04, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xf0,
+			  0x00, 0x3e, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x2e,
+	0x00, 0x80, 0x1f, 0xb4, 0x6f, 0x3f, 0x0f, 0x88, 0x20, 0x68, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0xa8, 0x08, 0x00, 0x11,
+			  0x00, 0x0c, 0x02, 0x01, 0x00, 0x16, 0x00, 0x94,
+			  0x00, 0x10, 0x06, 0x08, 0x00, 0x18, 0x00,
+	0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00,
+			  0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0,
+			  0x40, 0x20,
+	0x03, 0x00, 0x03, 0x03, 0x00, 0x00,
+	0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0x21, 0x00,
+	0x06, 0x00, 0x02, 0x09, 0x99,
+	0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00,
+	0x10, 0x00, 0x40, 0xa1, 0x02, 0x80, 0x00, 0x1d, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x0a,
+			  0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x49, 0x13, 0xff, 0x01, 0xc0, 0x00, 0x14,
+			  0x02, 0x00, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00,
+			  0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08,
+			  0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06,
+			  0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80,
+	0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99,
+			  0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc,
+			  0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54,
+			  0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2,
+			  0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43,
+			  0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3,
+			  0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32,
+			  0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3,
+	0x10, 0x80, 0x1b, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x05, 0x82,
+			  0x02, 0xe4, 0x01, 0x40, 0x01, 0xf0, 0x00, 0x40,
+			  0x01, 0xf0, 0x00,
+	0x02, 0x00, 0x11, 0x3c, 0x50, 0x9e, 0x3c, 0x50, 0x00, 0x00, 0x00,
+			  0x00, 0x78, 0x3f, 0x10, 0x02, 0xf2, 0x8f, 0x78,
+			  0x40,
+	0x10, 0x1a, 0x01, 0x00,
+	0x10, 0x00, 0x01, 0xad,
+	0x00, 0x00, 0x01, 0x08,
+	0x10, 0x85, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00,
+	0x10, 0x1b, 0x02, 0x00, 0x00,
+	0x10, 0x11, 0x08, 0x51, 0x00, 0xf0, 0x00, 0x3d, 0x00, 0xb4, 0x00,
+	0x10, 0x1d, 0x08, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0,
+	0x10, 0x0e, 0x01, 0x27,
+	0x10, 0x41, 0x11, 0x00, 0x0e, 0x35, 0x4f, 0x62, 0x71, 0x7f, 0x8b,
+			  0x96, 0xa0, 0xa9, 0xb2, 0xbb, 0xc3, 0xca, 0xd2,
+			  0xd8,
+	0x10, 0x03, 0x01, 0x00,
+	0x10, 0x0f, 0x02, 0x14, 0x14,
+	0x10, 0x03, 0x01, 0x0c,
+	0x10, 0x41, 0x11, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, 0x64, 0x74,
+			  0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, 0xe2, 0xf1,
+			  0xff,
+/*			  0x00, 0x0e, 0x35, 0x4f, 0x62, 0x71, 0x7f, 0x8b,
+ *			  0x96, 0xa0, 0xa9, 0xb2, 0xbb, 0xc3, 0xca, 0xd2,
+ *			  0xd8,	*/
+	0x10, 0x0b, 0x01, 0x10,
+	0x10, 0x0d, 0x01, 0x11,
+	0x10, 0x0c, 0x01, 0x1c,
+	0x04, 0x06, 0x01, 0x03,
+	0x04, 0x04, 0x01, 0x00,
+	0, 0, 0
+};
+/* et31x110 - Trust 120 SpaceCam */
+static const u8 spacecam_init[] = {
+	0x04, 0x05, 0x01, 0x01,
+	0x04, 0x04, 0x01, 0x01,
+	0x04, 0x06, 0x01, 0x04,
+	0x04, 0x04, 0x03, 0x00, 0x00, 0x00,
+	0x05, 0x05, 0x01, 0x00,
+	0, 0, 0
+};
+static const u8 spacecam_start[] = {
+	0x04, 0x06, 0x01, 0x44,
+	0x00, 0x00, 0x40, 0x10, 0x43, 0x00, 0xb4, 0x01, 0x10, 0x00, 0x4f,
+			  0xef, 0x0e, 0x00, 0x74, 0x01, 0x01, 0x00, 0x19,
+			  0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19,
+			  0x00, 0x01, 0x00, 0x19, 0x00, 0x3e, 0x00, 0x24,
+			  0x03, 0x3e, 0x00, 0x86, 0x00, 0x3e, 0x00, 0x86,
+			  0x00, 0x3e, 0x00, 0x86, 0x00, 0x01, 0x00, 0x01,
+			  0x00, 0x56, 0x00, 0x9e, 0x00, 0x56, 0x00, 0x9e,
+			  0x00, 0x56, 0x00, 0x9e, 0x00, 0x01, 0x00, 0x01,
+	0x00, 0x40, 0x40, 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78,
+			  0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01,
+			  0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78,
+			  0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01,
+			  0x00, 0xca, 0x03, 0x46, 0x04, 0xca, 0x03, 0x46,
+			  0x04, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xf0,
+			  0x00, 0x3e, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x2e,
+	0x00, 0x80, 0x1f, 0xa0, 0x48, 0xc3, 0x02, 0x88, 0x0c, 0x68, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0xa8, 0x06, 0x00, 0x08,
+			  0x00, 0x32, 0x01, 0x01, 0x00, 0x16, 0x00, 0x04,
+			  0x00, 0x4b, 0x00, 0x7c, 0x00, 0x80, 0x00,
+	0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00,
+			  0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0,
+			  0x40, 0x20,
+	0x03, 0x00, 0x03, 0x03, 0x00, 0x00,
+	0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x05, 0x00, 0x06, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x06, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00,
+	0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00,
+	0x10, 0x00, 0x40, 0x83, 0x02, 0x20, 0x00, 0x11, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x0a,
+			  0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x49, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00,
+			  0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08,
+			  0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06,
+			  0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80,
+	0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99,
+			  0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc,
+			  0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54,
+			  0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2,
+			  0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43,
+			  0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3,
+			  0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32,
+			  0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3,
+	0x10, 0x80, 0x1d, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x62,
+			  0x01, 0x24, 0x01, 0x62, 0x01, 0x24, 0x01, 0x20,
+			  0x01, 0x60, 0x01, 0x00, 0x00,
+	0x04, 0x06, 0x01, 0xc0,
+	0x10, 0x85, 0x08, 0x00, 0x00, 0x5f, 0x01, 0x00, 0x00, 0x1f, 0x01,
+	0x02, 0x00, 0x11, 0x48, 0x58, 0x9e, 0x48, 0x58, 0x00, 0x00, 0x00,
+			  0x00, 0x84, 0x36, 0x05, 0x01, 0xf2, 0x86, 0x65,
+			  0x40,
+	0x00, 0x80, 0x01, 0xa0,
+	0x10, 0x1a, 0x01, 0x00,
+	0x00, 0x91, 0x02, 0x32, 0x01,
+	0x00, 0x03, 0x02, 0x08, 0x02,
+	0x10, 0x00, 0x01, 0x83,
+	0x10, 0x8f, 0x0c, 0x62, 0x01, 0x24, 0x01, 0x62, 0x01, 0x24, 0x01,
+			  0x20, 0x01, 0x60, 0x01,
+	0x10, 0x11, 0x08, 0x00, 0x00, 0x5f, 0x01, 0x00, 0x00, 0x1f, 0x01,
+	0x10, 0x0e, 0x01, 0x08,
+	0x10, 0x41, 0x11, 0x00, 0x64, 0x99, 0xc0, 0xe2, 0xf9, 0xf9, 0xf9,
+			  0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9,
+			  0xf9,
+	0x10, 0x03, 0x01, 0x00,
+	0x10, 0x0f, 0x02, 0x13, 0x13,
+	0x10, 0x03, 0x01, 0x06,
+	0x10, 0x41, 0x11, 0x00, 0x64, 0x99, 0xc0, 0xe2, 0xf9, 0xf9, 0xf9,
+			  0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9,
+			  0xf9,
+	0x10, 0x0b, 0x01, 0x08,
+	0x10, 0x0d, 0x01, 0x10,
+	0x10, 0x0c, 0x01, 0x1f,
+	0x04, 0x06, 0x01, 0xc3,
+	0x04, 0x05, 0x01, 0x40,
+	0x04, 0x04, 0x01, 0x40,
+	0, 0, 0
+};
+/* et31x110 - pas106 - other Trust SpaceCam120 */
+static const u8 spacecam2_start[] = {
+	0x04, 0x06, 0x01, 0x44,
+	0x04, 0x06, 0x01, 0x00,
+	0x00, 0x00, 0x40, 0x14, 0x83, 0x00, 0xba, 0x01, 0x10, 0x00, 0x4f,
+			  0xef, 0x00, 0x00, 0x60, 0x00, 0x01, 0x00, 0x19,
+			  0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19,
+			  0x00, 0x01, 0x00, 0x19, 0x00, 0x06, 0x00, 0xfc,
+			  0x01, 0x3e, 0x00, 0x86, 0x00, 0x3e, 0x00, 0x86,
+			  0x00, 0x3e, 0x00, 0x86, 0x00, 0x01, 0x00, 0x01,
+			  0x00, 0x56, 0x00, 0x9e, 0x00, 0x56, 0x00, 0x9e,
+			  0x00, 0x56, 0x00, 0x9e, 0x00, 0x01, 0x00, 0x01,
+	0x00, 0x40, 0x40, 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78,
+			  0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01,
+			  0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78,
+			  0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01,
+			  0x00, 0xca, 0x03, 0x46, 0x04, 0xca, 0x03, 0x46,
+			  0x04, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xf0,
+			  0x00, 0x3e, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x2e,
+	0x00, 0x80, 0x1f, 0xb8, 0x48, 0x0f, 0x04, 0x88, 0x14, 0x68, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0xa8, 0x01, 0x00, 0x03,
+			  0x00, 0x24, 0x01, 0x01, 0x00, 0x16, 0x00, 0x04,
+			  0x00, 0x4b, 0x00, 0x76, 0x00, 0x86, 0x00,
+	0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00,
+			  0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0,
+			  0x40, 0x20,
+	0x03, 0x00, 0x03, 0x03, 0x00, 0x00,
+	0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0x61, 0x00,
+	0x05, 0x00, 0x06, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x06, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00,
+	0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00,
+	0x10, 0x00, 0x40, 0x80, 0x02, 0x20, 0x00, 0x13, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x0a,
+			  0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x49, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00,
+			  0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08,
+			  0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06,
+			  0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80,
+	0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99,
+			  0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc,
+			  0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54,
+			  0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2,
+			  0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43,
+			  0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3,
+			  0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32,
+			  0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3,
+	0x10, 0x80, 0x1d, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x62,
+			  0x01, 0x24, 0x01, 0x62, 0x01, 0x24, 0x01, 0x20,
+			  0x01, 0x60, 0x01, 0x00, 0x00,
+	0x10, 0x85, 0x08, 0x00, 0x00, 0x5f, 0x01, 0x00, 0x00, 0x1f, 0x01,
+	0x04, 0x04, 0x01, 0x40,
+	0x04, 0x04, 0x01, 0x00,
+	I2C0, 0x40, 0x0c, 0x02, 0x0c, 0x12, 0x07, 0x00, 0x00, 0x00, 0x05,
+			  0x00, 0x00, 0x05, 0x05,
+	I2C0, 0x40, 0x02, 0x11, 0x06,
+	I2C0, 0x40, 0x02, 0x14, 0x00,
+	I2C0, 0x40, 0x02, 0x13, 0x01,		/* i2c end */
+	0x02, 0x00, 0x11, 0x48, 0x58, 0x9e, 0x48, 0x58, 0x00, 0x00, 0x00,
+			  0x00, 0x84, 0x36, 0x05, 0x01, 0xf2, 0x86, 0x65,
+			  0x40,
+	I2C0, 0x40, 0x02, 0x02, 0x0c,		/* pixel clock */
+	I2C0, 0x40, 0x02, 0x0f, 0x00,
+	I2C0, 0x40, 0x02, 0x13, 0x01,		/* i2c end */
+	0x10, 0x00, 0x01, 0x01,
+	0x10, 0x8f, 0x0c, 0x62, 0x01, 0x24, 0x01, 0x62, 0x01, 0x24, 0x01,
+			  0x20, 0x01, 0x60, 0x01,
+	I2C0, 0x40, 0x02, 0x05, 0x0f,		/* exposure */
+	I2C0, 0x40, 0x02, 0x13, 0x01,		/* i2c end */
+	I2C0, 0x40, 0x07, 0x09, 0x0b, 0x0f, 0x05, 0x05, 0x0f, 0x00,
+						/* gains */
+	I2C0, 0x40, 0x03, 0x12, 0x04, 0x01,
+	0x10, 0x11, 0x08, 0x00, 0x00, 0x5f, 0x01, 0x00, 0x00, 0x1f, 0x01,
+	0x10, 0x0e, 0x01, 0x08,
+	0x10, 0x41, 0x11, 0x00, 0x17, 0x3f, 0x69, 0x7b, 0x8c, 0x9a, 0xa7,
+			  0xb3, 0xbf, 0xc9, 0xd3, 0xdd, 0xe6, 0xef, 0xf7,
+			  0xf9,
+	0x10, 0x03, 0x01, 0x00,
+	0x10, 0x0f, 0x02, 0x13, 0x13,
+	0x10, 0x03, 0x01, 0x06,
+	0x10, 0x41, 0x11, 0x00, 0x17, 0x3f, 0x69, 0x7b, 0x8c, 0x9a, 0xa7,
+			  0xb3, 0xbf, 0xc9, 0xd3, 0xdd, 0xe6, 0xef, 0xf7,
+			  0xf9,
+	0x10, 0x0b, 0x01, 0x11,
+	0x10, 0x0d, 0x01, 0x10,
+	0x10, 0x0c, 0x01, 0x14,
+	0x04, 0x06, 0x01, 0x03,
+	0x04, 0x05, 0x01, 0x61,
+	0x04, 0x04, 0x01, 0x00,
+	0, 0, 0
+};
+
+/* nw802 - Conceptronic Video Pro */
+static const u8 cvideopro_start[] = {
+	0x04, 0x06, 0x01, 0x04,
+	0x00, 0x00, 0x40, 0x54, 0x96, 0x98, 0xf9, 0x02, 0x18, 0x00, 0x4c,
+			  0x0f, 0x1f, 0x00, 0x0d, 0x02, 0x01, 0x00, 0x19,
+			  0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19,
+			  0x00, 0x0b, 0x00, 0x1b, 0x00, 0xc8, 0x00, 0xf4,
+			  0x05, 0xb4, 0x00, 0xcc, 0x00, 0x01, 0x00, 0x01,
+			  0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
+			  0x00, 0xa2, 0x00, 0xc6, 0x00, 0x60, 0x00, 0xc6,
+			  0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
+	0x00, 0x40, 0x40, 0x00, 0xae, 0x00, 0xd2, 0x00, 0xae, 0x00, 0xd2,
+			  0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
+			  0x00, 0xa8, 0x00, 0xc0, 0x00, 0x66, 0x00, 0xc0,
+			  0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
+			  0x00, 0x0a, 0x00, 0x54, 0x00, 0x0a, 0x00, 0x54,
+			  0x00, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6,
+			  0x00, 0x5d, 0x00, 0xc7, 0x00, 0x7e, 0x00, 0x30,
+	0x00, 0x80, 0x1f, 0x98, 0x43, 0x3f, 0x0d, 0x88, 0x20, 0x80, 0x3f,
+			  0x47, 0xaf, 0x00, 0x00, 0xa8, 0x08, 0x00, 0x11,
+			  0x00, 0x0c, 0x02, 0x0c, 0x00, 0x1c, 0x00, 0x94,
+			  0x00, 0x10, 0x06, 0x24, 0x00, 0x4a, 0x00,
+	0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00,
+			  0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0,
+			  0x40, 0x20,
+	0x03, 0x00, 0x03, 0x03, 0x00, 0x00,
+	0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0xff, 0x00,
+	0x06, 0x00, 0x02, 0x09, 0x99,
+	0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00,
+	0x10, 0x00, 0x40, 0xa0, 0x02, 0x80, 0x00, 0x12, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x0a,
+			  0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x49, 0x13, 0x00, 0x00, 0xe0, 0x00, 0x0c,
+			  0x00, 0x52, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00,
+			  0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08,
+			  0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06,
+			  0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80,
+	0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99,
+			  0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc,
+			  0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54,
+			  0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2,
+			  0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43,
+			  0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3,
+			  0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32,
+			  0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3,
+	0x10, 0x80, 0x1b, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x05, 0x82,
+			  0x02, 0xe4, 0x01, 0x40, 0x01, 0xf0, 0x00, 0x40,
+			  0x01, 0xf0, 0x00,
+	0x02, 0x00, 0x11, 0x3c, 0x50, 0x8c, 0x3c, 0x50, 0x00, 0x00, 0x00,
+			  0x00, 0x78, 0x3f, 0x3f, 0x06, 0xf2, 0x8f, 0xf0,
+			  0x40,
+	0x10, 0x1a, 0x01, 0x03,
+	0x10, 0x00, 0x01, 0xac,
+	0x10, 0x85, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00,
+	0x10, 0x1b, 0x02, 0x3b, 0x01,
+	0x10, 0x11, 0x08, 0x61, 0x00, 0xe0, 0x00, 0x49, 0x00, 0xa8, 0x00,
+	0x10, 0x1f, 0x06, 0x01, 0x20, 0x02, 0xe8, 0x03, 0x00,
+	0x10, 0x1d, 0x02, 0x40, 0x06,
+	0x10, 0x0e, 0x01, 0x08,
+	0x10, 0x41, 0x11, 0x00, 0x0f, 0x46, 0x62, 0x76, 0x86, 0x94, 0xa0,
+			  0xab, 0xb6, 0xbf, 0xc8, 0xcf, 0xd7, 0xdc, 0xdc,
+			  0xdc,
+	0x10, 0x03, 0x01, 0x00,
+	0x10, 0x0f, 0x02, 0x12, 0x12,
+	0x10, 0x03, 0x01, 0x0c,
+	0x10, 0x41, 0x11, 0x00, 0x0f, 0x46, 0x62, 0x76, 0x86, 0x94, 0xa0,
+			  0xab, 0xb6, 0xbf, 0xc8, 0xcf, 0xd7, 0xdc, 0xdc,
+			  0xdc,
+	0x10, 0x0b, 0x01, 0x09,
+	0x10, 0x0d, 0x01, 0x10,
+	0x10, 0x0c, 0x01, 0x2f,
+	0x04, 0x06, 0x01, 0x03,
+	0x04, 0x04, 0x01, 0x00,
+	0, 0, 0
+};
+
+/* nw802 - D-link dru-350c cam */
+static const u8 dlink_start[] = {
+	0x04, 0x06, 0x01, 0x04,
+	0x00, 0x00, 0x40, 0x10, 0x00, 0x00, 0x92, 0x03, 0x10, 0x00, 0x4d,
+			  0x0f, 0x1f, 0x00, 0x0d, 0x02, 0x01, 0x00, 0x19,
+			  0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19,
+			  0x00, 0x01, 0x00, 0x19, 0x00, 0xce, 0x00, 0xf4,
+			  0x05, 0x3e, 0x00, 0x86, 0x00, 0x3e, 0x00, 0x86,
+			  0x00, 0x3e, 0x00, 0x86, 0x00, 0x01, 0x00, 0x01,
+			  0x00, 0x56, 0x00, 0x9e, 0x00, 0x56, 0x00, 0x9e,
+			  0x00, 0x56, 0x00, 0x9e, 0x00, 0x01, 0x00, 0x01,
+	0x00, 0x40, 0x40, 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78,
+			  0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01,
+			  0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78,
+			  0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01,
+			  0x00, 0xca, 0x03, 0x46, 0x04, 0xca, 0x03, 0x46,
+			  0x04, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xf0,
+			  0x00, 0x3e, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x2e,
+	0x00, 0x80, 0x1f, 0xb4, 0x6f, 0x3f, 0x0f, 0x88, 0x20, 0x68, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0xa8, 0x08, 0x00, 0x11,
+			  0x00, 0x0c, 0x02, 0x01, 0x00, 0x16, 0x00, 0x94,
+			  0x00, 0x10, 0x06, 0x10, 0x00, 0x36, 0x00,
+	0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00,
+			  0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0,
+			  0x40, 0x20,
+	0x03, 0x00, 0x03, 0x03, 0x00, 0x00,
+	0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0x21, 0x00,
+	0x06, 0x00, 0x02, 0x09, 0x99,
+	0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00,
+	0x10, 0x00, 0x40, 0xa1, 0x02, 0x80, 0x00, 0x12, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x0a,
+			  0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x49, 0x13, 0x00, 0x00, 0xc0, 0x00, 0x14,
+			  0x02, 0x00, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00,
+			  0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08,
+			  0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06,
+			  0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80,
+	0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99,
+			  0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc,
+			  0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54,
+			  0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2,
+			  0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43,
+			  0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3,
+			  0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32,
+			  0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3,
+	0x10, 0x80, 0x1b, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x01, 0x82,
+			  0x02, 0xe4, 0x01, 0x40, 0x01, 0xf0, 0x00, 0x40,
+			  0x01, 0xf0, 0x00,
+	0x02, 0x00, 0x11, 0x3c, 0x50, 0x9e, 0x3c, 0x50, 0x00, 0x00, 0x00,
+			  0x00, 0x78, 0x3f, 0x10, 0x02, 0xf2, 0x8f, 0x78,
+			  0x40,
+	0x10, 0x1a, 0x01, 0x00,
+	0x10, 0x00, 0x01, 0xad,
+	0x00, 0x00, 0x01, 0x08,
+	0x10, 0x85, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00,
+	0x10, 0x1b, 0x02, 0x00, 0x00,
+	0x10, 0x11, 0x08, 0x51, 0x00, 0xf0, 0x00, 0x3d, 0x00, 0xb4, 0x00,
+	0x10, 0x1d, 0x08, 0x40, 0x06, 0x01, 0x20, 0x02, 0xe8, 0x03, 0x00,
+	0x10, 0x0e, 0x01, 0x20,
+	0x10, 0x41, 0x11, 0x00, 0x07, 0x1e, 0x38, 0x4d, 0x60, 0x70, 0x7f,
+			  0x8e, 0x9b, 0xa8, 0xb4, 0xbf, 0xca, 0xd5, 0xdf,
+			  0xea,
+	0x10, 0x03, 0x01, 0x00,
+	0x10, 0x0f, 0x02, 0x11, 0x11,
+	0x10, 0x03, 0x01, 0x10,
+	0x10, 0x41, 0x11, 0x00, 0x07, 0x1e, 0x38, 0x4d, 0x60, 0x70, 0x7f,
+			  0x8e, 0x9b, 0xa8, 0xb4, 0xbf, 0xca, 0xd5, 0xdf,
+			  0xea,
+	0x10, 0x0b, 0x01, 0x19,
+	0x10, 0x0d, 0x01, 0x10,
+	0x10, 0x0c, 0x01, 0x1e,
+	0x04, 0x06, 0x01, 0x03,
+	0x04, 0x04, 0x01, 0x00,
+	0, 0, 0
+};
+
+/* 06a5:d001 - nw801 - Sony
+ *		Plustek Opticam 500U or ProLink DS3303u (Hitachi HD49322BF) */
+/*fixme: 320x240 only*/
+static const u8 ds3303_start[] = {
+	0x05, 0x06, 0x01, 0x04,
+	0x00, 0x00, 0x40, 0x16, 0x00, 0x00, 0xf9, 0x02, 0x11, 0x00, 0x0e,
+			  0x01, 0x1f, 0x00, 0x0d, 0x02, 0x01, 0x00, 0x19,
+			  0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19,
+			  0x00, 0x01, 0x00, 0x19, 0x00, 0xce, 0x00, 0xf4,
+			  0x05, 0x3e, 0x00, 0x86, 0x00, 0x3e, 0x00, 0x86,
+			  0x00, 0x3e, 0x00, 0x86, 0x00, 0x01, 0x00, 0x01,
+			  0x00, 0x56, 0x00, 0x9e, 0x00, 0x56, 0x00, 0x9e,
+			  0x00, 0x56, 0x00, 0x9e, 0x00, 0x01, 0x00, 0x01,
+	0x00, 0x40, 0x40, 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78,
+			  0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01,
+			  0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78,
+			  0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01,
+			  0x00, 0xca, 0x03, 0x46, 0x04, 0xca, 0x03, 0x46,
+			  0x04, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xf0,
+			  0x00, 0x3e, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x2e,
+	0x00, 0x80, 0x22, 0xb4, 0x6f, 0x3f, 0x0f, 0x88, 0x20, 0x08, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0xa9, 0xa8, 0x1f, 0x00,
+			  0x0d, 0x02, 0x07, 0x00, 0x01, 0x00, 0x19, 0x00,
+			  0xf2, 0x00, 0x18, 0x06, 0x10, 0x06, 0x10, 0x00,
+			  0x36, 0x00,
+	0x02, 0x00, 0x12, 0x03, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00,
+			  0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0x50,
+			  0x40, 0x20,
+	0x03, 0x00, 0x03, 0x03, 0x00, 0x00,
+	0x05, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0xff, 0x00,
+	0x06, 0x00, 0x02, 0x09, 0x99,
+	0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00,
+	0x10, 0x00, 0x40, 0x2f, 0x02, 0x80, 0x00, 0x12, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x10, 0x1f, 0x10, 0x08, 0x0a,
+			  0x0a, 0x51, 0x00, 0xf1, 0x00, 0x3c, 0x00, 0xb4,
+			  0x00, 0x01, 0x15, 0xfd, 0x07, 0x3d, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x8c, 0x04, 0x01, 0x20,
+			  0x02, 0x00, 0x03, 0x00, 0x20, 0x00, 0x00, 0x00,
+			  0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08, 0x03,
+			  0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06, 0xf7,
+	0x10, 0x40, 0x40, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80, 0x80,
+			  0x00, 0x2d, 0x46, 0x58, 0x67, 0x74, 0x7f, 0x88,
+			  0x94, 0x9d, 0xa6, 0xae, 0xb5, 0xbd, 0xc4, 0xcb,
+			  0xd1, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, 0x64,
+			  0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, 0xe2,
+			  0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54,
+			  0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2,
+			  0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43,
+	0x10, 0x80, 0x22, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3,
+			  0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x3f, 0x01,
+			  0x00, 0x00, 0xef, 0x00, 0x02, 0x0a, 0x82, 0x02,
+			  0xe4, 0x01, 0x40, 0x01, 0xf0, 0x00, 0x40, 0x01,
+			  0xf0, 0x00,
+
+	0x02, 0x00, 0x11, 0x3c, 0x50, 0x9e, 0x3c, 0x50, 0x00, 0x00, 0x00,
+			  0x00, 0x78, 0x3f, 0x3f, 0x00, 0xf2, 0x8f, 0x81,
+			  0x40,
+	0x10, 0x1a, 0x01, 0x15,
+	0x10, 0x00, 0x01, 0x2f,
+	0x10, 0x8c, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00,
+	0x10, 0x1b, 0x02, 0x00, 0x00,
+	0x10, 0x11, 0x08, 0x61, 0x00, 0xe0, 0x00, 0x49, 0x00, 0xa8, 0x00,
+	0x10, 0x26, 0x06, 0x01, 0x20, 0x02, 0xe8, 0x03, 0x00,
+	0x10, 0x24, 0x02, 0x40, 0x06,
+	0x10, 0x0e, 0x01, 0x08,
+	0x10, 0x48, 0x11, 0x00, 0x15, 0x40, 0x67, 0x84, 0x9d, 0xb2, 0xc6,
+			  0xd6, 0xe7, 0xf6, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9,
+			  0xf9,
+	0x10, 0x03, 0x01, 0x00,
+	0x10, 0x0f, 0x02, 0x16, 0x16,
+	0x10, 0x03, 0x01, 0x0c,
+	0x10, 0x48, 0x11, 0x00, 0x15, 0x40, 0x67, 0x84, 0x9d, 0xb2, 0xc6,
+			  0xd6, 0xe7, 0xf6, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9,
+			  0xf9,
+	0x10, 0x0b, 0x01, 0x26,
+	0x10, 0x0d, 0x01, 0x10,
+	0x10, 0x0c, 0x01, 0x1c,
+	0x05, 0x06, 0x01, 0x03,
+	0x05, 0x04, 0x01, 0x00,
+	0, 0, 0
+};
+
+/* 06a5:d001 - nw802 - Panasonic
+ *		GP-KR651US (Philips TDA8786) */
+static const u8 kr651_start_1[] = {
+	0x04, 0x06, 0x01, 0x04,
+	0x00, 0x00, 0x40, 0x44, 0x96, 0x98, 0xf9, 0x02, 0x18, 0x00, 0x48,
+			  0x0f, 0x1f, 0x00, 0x0d, 0x02, 0x01, 0x00, 0x19,
+			  0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19,
+			  0x00, 0x0b, 0x00, 0x1b, 0x00, 0xc8, 0x00, 0xf4,
+			  0x05, 0xb4, 0x00, 0xcc, 0x00, 0x01, 0x00, 0x01,
+			  0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
+			  0x00, 0xa2, 0x00, 0xc6, 0x00, 0x60, 0x00, 0xc6,
+			  0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
+	0x00, 0x40, 0x40, 0x00, 0xae, 0x00, 0xd2, 0x00, 0xae, 0x00, 0xd2,
+			  0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
+			  0x00, 0xa8, 0x00, 0xc0, 0x00, 0x66, 0x00, 0xc0,
+			  0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
+			  0x00, 0x0a, 0x00, 0x54, 0x00, 0x0a, 0x00, 0x54,
+			  0x00, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6,
+			  0x00, 0x5d, 0x00, 0xc7, 0x00, 0x7e, 0x00, 0x30,
+	0x00, 0x80, 0x1f, 0x18, 0x43, 0x3f, 0x0d, 0x88, 0x20, 0x80, 0x3f,
+			  0x47, 0xaf, 0x00, 0x00, 0xa8, 0x08, 0x00, 0x11,
+			  0x00, 0x0c, 0x02, 0x0c, 0x00, 0x1c, 0x00, 0x94,
+			  0x00, 0x10, 0x06, 0x24, 0x00, 0x4a, 0x00,
+	0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00,
+			  0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0,
+			  0x40, 0x20,
+	0x03, 0x00, 0x03, 0x02, 0x00, 0x00,
+	0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0x21, 0x00,
+	0x06, 0x00, 0x02, 0x09, 0x99,
+	0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00,
+	0x10, 0x00, 0x40, 0xa0, 0x02, 0x80, 0x00, 0x12, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x0a,
+			  0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x49, 0x13, 0x00, 0x00, 0xe0, 0x00, 0x0c,
+			  0x00, 0x52, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00,
+			  0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08,
+			  0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06,
+			  0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80,
+	0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99,
+			  0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc,
+			  0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54,
+			  0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2,
+			  0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43,
+			  0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3,
+			  0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32,
+			  0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3,
+	0x10, 0x80, 0x1b, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x05, 0x82,
+			  0x02, 0xe4, 0x01, 0x40, 0x01, 0xf0, 0x00, 0x40,
+			  0x01, 0xf0, 0x00,
+	0, 0, 0
+};
+static const u8 kr651_start_qvga[] = {
+	0x02, 0x00, 0x11, 0x3c, 0x50, 0x9e, 0x3c, 0x50, 0x00, 0x00, 0x00,
+			  0x00, 0x78, 0x3f, 0x10, 0x02, 0xf2, 0x8f, 0x78,
+			  0x40,
+	0x10, 0x1a, 0x01, 0x03,
+	0x10, 0x00, 0x01, 0xac,
+	0x10, 0x85, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00,
+	0x10, 0x1b, 0x02, 0x00, 0x00,
+	0x10, 0x11, 0x08, 0x29, 0x00, 0x18, 0x01, 0x1f, 0x00, 0xd2, 0x00,
+	0x10, 0x1d, 0x06, 0xe0, 0x00, 0x0c, 0x00, 0x52, 0x00,
+	0x10, 0x1d, 0x02, 0x28, 0x01,
+	0, 0, 0
+};
+static const u8 kr651_start_vga[] = {
+	0x02, 0x00, 0x11, 0x78, 0xa0, 0x8c, 0x78, 0xa0, 0x00, 0x00, 0x00,
+			  0x00, 0xf0, 0x30, 0x03, 0x01, 0x82, 0x82, 0x98,
+			  0x80,
+	0x10, 0x1a, 0x01, 0x03,
+	0x10, 0x00, 0x01, 0xa0,
+	0x10, 0x85, 0x08, 0x00, 0x00, 0x7f, 0x02, 0x00, 0x00, 0xdf, 0x01,
+	0x10, 0x1b, 0x02, 0x00, 0x00,
+	0x10, 0x11, 0x08, 0x51, 0x00, 0x30, 0x02, 0x3d, 0x00, 0xa4, 0x01,
+	0x10, 0x1d, 0x06, 0xe0, 0x00, 0x0c, 0x00, 0x52, 0x00,
+	0x10, 0x1d, 0x02, 0x68, 0x00,
+};
+static const u8 kr651_start_2[] = {
+	0x10, 0x0e, 0x01, 0x08,
+	0x10, 0x41, 0x11, 0x00, 0x11, 0x3c, 0x5c, 0x74, 0x88, 0x99, 0xa8,
+			  0xb7, 0xc4, 0xd0, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc,
+			  0xdc,
+	0x10, 0x03, 0x01, 0x00,
+	0x10, 0x0f, 0x02, 0x0c, 0x0c,
+	0x10, 0x03, 0x01, 0x0c,
+	0x10, 0x41, 0x11, 0x00, 0x11, 0x3c, 0x5c, 0x74, 0x88, 0x99, 0xa8,
+			  0xb7, 0xc4, 0xd0, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc,
+			  0xdc,
+	0x10, 0x0b, 0x01, 0x10,
+	0x10, 0x0d, 0x01, 0x10,
+	0x10, 0x0c, 0x01, 0x2d,
+	0x04, 0x06, 0x01, 0x03,
+	0x04, 0x04, 0x01, 0x00,
+	0, 0, 0
+};
+
+/* nw802 - iRez Kritter cam */
+static const u8 kritter_start[] = {
+	0x04, 0x06, 0x01, 0x06,
+	0x00, 0x00, 0x40, 0x44, 0x96, 0x98, 0x94, 0x03, 0x18, 0x00, 0x48,
+			  0x0f, 0x1e, 0x00, 0x0c, 0x02, 0x01, 0x00, 0x19,
+			  0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19,
+			  0x00, 0x0b, 0x00, 0x1b, 0x00, 0x0a, 0x01, 0x28,
+			  0x07, 0xb4, 0x00, 0xcc, 0x00, 0x01, 0x00, 0x01,
+			  0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
+			  0x00, 0xa2, 0x00, 0xc6, 0x00, 0x60, 0x00, 0xc6,
+			  0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
+	0x00, 0x40, 0x40, 0x00, 0xae, 0x00, 0xd2, 0x00, 0xae, 0x00, 0xd2,
+			  0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
+			  0x00, 0xa8, 0x00, 0xc0, 0x00, 0x66, 0x00, 0xc0,
+			  0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
+			  0x00, 0x0a, 0x00, 0x54, 0x00, 0x0a, 0x00, 0x54,
+			  0x00, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6,
+			  0x00, 0x5d, 0x00, 0x0e, 0x00, 0x7e, 0x00, 0x30,
+	0x00, 0x80, 0x1f, 0x18, 0x43, 0x3f, 0x0d, 0x88, 0x20, 0x80, 0x3f,
+			  0x47, 0xaf, 0x00, 0x00, 0xa8, 0x08, 0x00, 0x11,
+			  0x00, 0x0b, 0x02, 0x0c, 0x00, 0x1c, 0x00, 0x94,
+			  0x00, 0x10, 0x06, 0x24, 0x00, 0x4a, 0x00,
+	0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00,
+			  0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0,
+			  0x40, 0x20,
+	0x03, 0x00, 0x03, 0x02, 0x00, 0x00,
+	0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0xff, 0x00,
+	0x06, 0x00, 0x02, 0x09, 0x99,
+	0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00,
+	0x10, 0x00, 0x40, 0xa0, 0x02, 0x80, 0x00, 0x12, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x0a,
+			  0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x49, 0x13, 0x00, 0x00, 0xe0, 0x00, 0x0c,
+			  0x00, 0x52, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00,
+			  0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08,
+			  0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06,
+			  0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80,
+	0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99,
+			  0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc,
+			  0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54,
+			  0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2,
+			  0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43,
+			  0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3,
+			  0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32,
+			  0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3,
+	0x10, 0x80, 0x1b, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x82,
+			  0x02, 0xe4, 0x01, 0x40, 0x01, 0xf0, 0x00, 0x40,
+			  0x01, 0xf0, 0x00,
+	0x02, 0x00, 0x11, 0x3c, 0x50, 0x8c, 0x3c, 0x50, 0x00, 0x00, 0x00,
+			  0x00, 0x78, 0x3f, 0x3f, 0x06, 0xf2, 0x8f, 0xf0,
+			  0x40,
+	0x10, 0x1a, 0x01, 0x03,
+	0x10, 0x00, 0x01, 0xaf,
+	0x10, 0x85, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00,
+	0x10, 0x1b, 0x02, 0x3b, 0x01,
+	0x10, 0x11, 0x08, 0x61, 0x00, 0xe0, 0x00, 0x49, 0x00, 0xa8, 0x00,
+	0x10, 0x1d, 0x06, 0xe0, 0x00, 0x0c, 0x00, 0x52, 0x00,
+	0x10, 0x1d, 0x02, 0x00, 0x00,
+	0x10, 0x0e, 0x01, 0x08,
+	0x10, 0x41, 0x11, 0x00, 0x0d, 0x36, 0x4e, 0x60, 0x6f, 0x7b, 0x86,
+			  0x90, 0x98, 0xa1, 0xa9, 0xb1, 0xb7, 0xbe, 0xc4,
+			  0xcb,
+	0x10, 0x03, 0x01, 0x00,
+	0x10, 0x0f, 0x02, 0x0d, 0x0d,
+	0x10, 0x03, 0x01, 0x02,
+	0x10, 0x41, 0x11, 0x00, 0x0d, 0x36, 0x4e, 0x60, 0x6f, 0x7b, 0x86,
+			  0x90, 0x98, 0xa1, 0xa9, 0xb1, 0xb7, 0xbe, 0xc4,
+			  0xcb,
+	0x10, 0x0b, 0x01, 0x17,
+	0x10, 0x0d, 0x01, 0x10,
+	0x10, 0x0c, 0x01, 0x1e,
+	0x04, 0x06, 0x01, 0x03,
+	0x04, 0x04, 0x01, 0x00,
+	0, 0, 0
+};
+
+/* nw802 - Mustek Wcam 300 mini */
+static const u8 mustek_start[] = {
+	0x04, 0x06, 0x01, 0x04,
+	0x00, 0x00, 0x40, 0x10, 0x00, 0x00, 0x92, 0x03, 0x10, 0x00, 0x4d,
+			  0x0f, 0x1f, 0x00, 0x0d, 0x02, 0x01, 0x00, 0x19,
+			  0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19,
+			  0x00, 0x01, 0x00, 0x19, 0x00, 0xce, 0x00, 0xf4,
+			  0x05, 0x3e, 0x00, 0x86, 0x00, 0x3e, 0x00, 0x86,
+			  0x00, 0x3e, 0x00, 0x86, 0x00, 0x01, 0x00, 0x01,
+			  0x00, 0x56, 0x00, 0x9e, 0x00, 0x56, 0x00, 0x9e,
+			  0x00, 0x56, 0x00, 0x9e, 0x00, 0x01, 0x00, 0x01,
+	0x00, 0x40, 0x40, 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78,
+			  0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01,
+			  0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78,
+			  0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01,
+			  0x00, 0xca, 0x03, 0x46, 0x04, 0xca, 0x03, 0x46,
+			  0x04, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xf0,
+			  0x00, 0x3e, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x2e,
+	0x00, 0x80, 0x1f, 0xb4, 0x6f, 0x3f, 0x0f, 0x88, 0x20, 0x68, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0xa8, 0x08, 0x00, 0x11,
+			  0x00, 0x0c, 0x02, 0x01, 0x00, 0x16, 0x00, 0x94,
+			  0x00, 0x10, 0x06, 0xfc, 0x05, 0x0c, 0x06,
+	0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00,
+			  0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0,
+			  0x40, 0x20,
+	0x03, 0x00, 0x03, 0x03, 0x00, 0x00,
+	0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0x21, 0x00,
+	0x06, 0x00, 0x02, 0x09, 0x99,
+	0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00,
+	0x10, 0x00, 0x40, 0xa1, 0x02, 0x80, 0x00, 0x13, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x0a,
+			  0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x49, 0x13, 0x00, 0x00, 0xc0, 0x00, 0x14,
+			  0x02, 0x00, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00,
+			  0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08,
+			  0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06,
+			  0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80,
+	0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99,
+			  0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc,
+			  0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54,
+			  0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2,
+			  0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43,
+			  0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3,
+			  0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32,
+			  0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3,
+	0x10, 0x80, 0x1b, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x01, 0x82,
+			  0x02, 0xe4, 0x01, 0x40, 0x01, 0xf0, 0x00, 0x40,
+			  0x01, 0xf0, 0x00,
+	0x02, 0x00, 0x11, 0x3c, 0x50, 0x9e, 0x3c, 0x50, 0x00, 0x00, 0x00,
+			  0x00, 0x78, 0x3f, 0x10, 0x02, 0xf2, 0x8f, 0x78,
+			  0x40,
+	0x10, 0x1a, 0x01, 0x00,
+	0x10, 0x00, 0x01, 0xad,
+	0x00, 0x00, 0x01, 0x08,
+	0x10, 0x85, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00,
+	0x10, 0x1b, 0x02, 0x00, 0x00,
+	0x10, 0x11, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00,
+	0x10, 0x1d, 0x08, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20,
+	0x10, 0x0e, 0x01, 0x0f,
+	0x10, 0x41, 0x11, 0x00, 0x0f, 0x29, 0x4a, 0x64, 0x7a, 0x8c, 0x9e,
+			  0xad, 0xba, 0xc7, 0xd3, 0xde, 0xe8, 0xf1, 0xf9,
+			  0xff,
+	0x10, 0x0f, 0x02, 0x11, 0x11,
+	0x10, 0x03, 0x01, 0x0c,
+	0x10, 0x41, 0x11, 0x00, 0x0f, 0x29, 0x4a, 0x64, 0x7a, 0x8c, 0x9e,
+			  0xad, 0xba, 0xc7, 0xd3, 0xde, 0xe8, 0xf1, 0xf9,
+			  0xff,
+	0x10, 0x0b, 0x01, 0x1c,
+	0x10, 0x0d, 0x01, 0x1a,
+	0x10, 0x0c, 0x01, 0x34,
+	0x04, 0x05, 0x01, 0x61,
+	0x04, 0x04, 0x01, 0x40,
+	0x04, 0x06, 0x01, 0x03,
+	0, 0, 0
+};
+
+/* nw802 - Scope USB Microscope M2 (ProScope) (Hitachi HD49322BF) */
+static const u8 proscope_init[] = {
+	0x04, 0x05, 0x01, 0x21,
+	0x04, 0x04, 0x01, 0x01,
+	0, 0, 0
+};
+static const u8 proscope_start_1[] = {
+	0x04, 0x06, 0x01, 0x04,
+	0x00, 0x00, 0x40, 0x10, 0x01, 0x00, 0xf9, 0x02, 0x10, 0x00, 0x04,
+			  0x0f, 0x1f, 0x00, 0x0d, 0x02, 0x01, 0x00, 0x19,
+			  0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19,
+			  0x00, 0x08, 0x00, 0x17, 0x00, 0xce, 0x00, 0xf4,
+			  0x05, 0x3e, 0x00, 0x86, 0x00, 0x3e, 0x00, 0x86,
+			  0x00, 0xce, 0x00, 0xf8, 0x03, 0x3e, 0x00, 0x86,
+			  0x00, 0x56, 0x00, 0x9e, 0x00, 0x56, 0x00, 0x9e,
+			  0x00, 0x56, 0x00, 0x9e, 0x00, 0x01, 0x00, 0x01,
+	0x00, 0x40, 0x40, 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0xb6,
+			  0x00, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01,
+			  0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78,
+			  0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01,
+			  0x00, 0xf6, 0x03, 0x34, 0x04, 0xf6, 0x03, 0x34,
+			  0x04, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xe8,
+			  0x00, 0x3e, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x2e,
+	0x00, 0x80, 0x1f, 0xb4, 0x6f, 0x1f, 0x0f, 0x08, 0x20, 0xa8, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0xa8, 0x08, 0x00, 0x11,
+			  0x00, 0x0c, 0x02, 0x01, 0x00, 0x19, 0x00, 0x94,
+			  0x00, 0x10, 0x06, 0x10, 0x00, 0x36, 0x00,
+	0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00,
+			  0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0,
+			  0x40, 0x20,
+	0x03, 0x00, 0x03, 0x03, 0x00, 0x00,
+	0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0x21, 0x00,
+	0x06, 0x00, 0x02, 0x09, 0x99,
+	0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00,
+	0x10, 0x00, 0x40, 0xad, 0x02, 0x80, 0x00, 0x12, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x10, 0x1f, 0x10, 0x08, 0x0a,
+			  0x0a, 0x51, 0x00, 0xf1, 0x00, 0x3c, 0x00, 0xb4,
+			  0x00, 0x49, 0x13, 0x00, 0x00, 0x8c, 0x04, 0x01,
+			  0x20, 0x02, 0x00, 0x03, 0x00, 0x20, 0x00, 0x00,
+			  0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08,
+			  0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06,
+			  0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80,
+	0x10, 0x40, 0x40, 0x80, 0x00, 0x2d, 0x46, 0x58, 0x67, 0x74, 0x7f,
+			  0x88, 0x94, 0x9d, 0xa6, 0xae, 0xb5, 0xbd, 0xc4,
+			  0xcb, 0xd1, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54,
+			  0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2,
+			  0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43,
+			  0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3,
+			  0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32,
+			  0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3,
+	0x10, 0x80, 0x1b, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x3f,
+			  0x01, 0x00, 0x00, 0xef, 0x00, 0x09, 0x05, 0x82,
+			  0x02, 0xe4, 0x01, 0x40, 0x01, 0xf0, 0x00, 0x40,
+			  0x01, 0xf0, 0x00,
+	0, 0, 0
+};
+static const u8 proscope_start_qvga[] = {
+	0x02, 0x00, 0x11, 0x3c, 0x50, 0x9e, 0x3c, 0x50, 0x00, 0x00, 0x00,
+			  0x00, 0x78, 0x3f, 0x10, 0x02, 0xf2, 0x8f, 0x78,
+			  0x40,
+	0x10, 0x1a, 0x01, 0x06,
+	0x00, 0x03, 0x02, 0xf9, 0x02,
+	0x10, 0x85, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00,
+	0x10, 0x1b, 0x02, 0x00, 0x00,
+	0x10, 0x11, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00,
+	0x10, 0x1d, 0x08, 0xc0, 0x0d, 0x01, 0x20, 0x02, 0xe8, 0x03, 0x00,
+	0x10, 0x0e, 0x01, 0x10,
+	0, 0, 0
+};
+static const u8 proscope_start_vga[] = {
+	0x00, 0x03, 0x02, 0xf9, 0x02,
+	0x10, 0x85, 0x08, 0x00, 0x00, 0x7f, 0x02, 0x00, 0x00, 0xdf, 0x01,
+	0x02, 0x00, 0x11, 0x78, 0xa0, 0x8c, 0x78, 0xa0, 0x00, 0x00, 0x00,
+			  0x00, 0xf0, 0x16, 0x00, 0x00, 0x82, 0x84, 0x00,
+			  0x80,
+	0x10, 0x1a, 0x01, 0x06,
+	0x10, 0x00, 0x01, 0xa1,
+	0x10, 0x1b, 0x02, 0x00, 0x00,
+	0x10, 0x1d, 0x08, 0xc0, 0x0d, 0x01, 0x20, 0x02, 0xe8, 0x03, 0x00,
+	0x10, 0x11, 0x08, 0x00, 0x00, 0x7f, 0x02, 0x00, 0x00, 0xdf, 0x01,
+	0x10, 0x0e, 0x01, 0x10,
+	0x10, 0x41, 0x11, 0x00, 0x10, 0x51, 0x6e, 0x83, 0x93, 0xa1, 0xae,
+			  0xb9, 0xc3, 0xcc, 0xd4, 0xdd, 0xe4, 0xeb, 0xf2,
+			  0xf9,
+	0x10, 0x03, 0x01, 0x00,
+	0, 0, 0
+};
+static const u8 proscope_start_2[] = {
+	0x10, 0x0f, 0x02, 0x0c, 0x0c,
+	0x10, 0x03, 0x01, 0x0c,
+	0x10, 0x41, 0x11, 0x00, 0x10, 0x51, 0x6e, 0x83, 0x93, 0xa1, 0xae,
+			  0xb9, 0xc3, 0xcc, 0xd4, 0xdd, 0xe4, 0xeb, 0xf2,
+			  0xf9,
+	0x10, 0x0b, 0x01, 0x0b,
+	0x10, 0x0d, 0x01, 0x10,
+	0x10, 0x0c, 0x01, 0x1b,
+	0x04, 0x06, 0x01, 0x03,
+	0x04, 0x05, 0x01, 0x21,
+	0x04, 0x04, 0x01, 0x00,
+	0, 0, 0
+};
+
+/* nw800 - hv7121b? (seems pas106) - Divio Chicony TwinkleCam */
+static const u8 twinkle_start[] = {
+	0x04, 0x06, 0x01, 0x44,
+	0x04, 0x06, 0x01, 0x00,
+	0x00, 0x00, 0x40, 0x14, 0x83, 0x00, 0xba, 0x01, 0x10, 0x00, 0x4f,
+			  0xef, 0x00, 0x00, 0x60, 0x00, 0x01, 0x00, 0x19,
+			  0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19,
+			  0x00, 0x01, 0x00, 0x19, 0x00, 0x06, 0x00, 0xfc,
+			  0x01, 0x3e, 0x00, 0x86, 0x00, 0x3e, 0x00, 0x86,
+			  0x00, 0x3e, 0x00, 0x86, 0x00, 0x01, 0x00, 0x01,
+			  0x00, 0x56, 0x00, 0x9e, 0x00, 0x56, 0x00, 0x9e,
+			  0x00, 0x56, 0x00, 0x9e, 0x00, 0x01, 0x00, 0x01,
+	0x00, 0x40, 0x40, 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78,
+			  0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01,
+			  0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78,
+			  0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01,
+			  0x00, 0xca, 0x03, 0x46, 0x04, 0xca, 0x03, 0x46,
+			  0x04, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xf0,
+			  0x00, 0x3e, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x2e,
+	0x00, 0x80, 0x1f, 0xb8, 0x48, 0x0f, 0x04, 0x88, 0x14, 0x68, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0xa8, 0x01, 0x00, 0x03,
+			  0x00, 0x24, 0x01, 0x01, 0x00, 0x16, 0x00, 0x04,
+			  0x00, 0x4b, 0x00, 0x76, 0x00, 0x86, 0x00,
+	0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00,
+			  0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0,
+			  0x40, 0x20,
+	0x03, 0x00, 0x03, 0x03, 0x00, 0x00,
+	0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0x61, 0x00,
+	0x05, 0x00, 0x06, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x06, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00,
+	0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00,
+	0x10, 0x00, 0x40, 0x80, 0x02, 0x20, 0x00, 0x11, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x08,
+			  0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x49, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00,
+			  0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08,
+			  0x03, 0x00, 0x00, 0x10, 0x00, 0x20, 0x10, 0x06,
+			  0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x00, 0x80,
+	0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99,
+			  0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc,
+			  0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54,
+			  0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2,
+			  0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43,
+			  0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3,
+			  0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32,
+			  0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3,
+	0x10, 0x80, 0x1d, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x62,
+			  0x01, 0x24, 0x01, 0x62, 0x01, 0x24, 0x01, 0x20,
+			  0x01, 0x60, 0x01, 0x00, 0x00,
+
+	0x10, 0x85, 0x08, 0x00, 0x00, 0x5f, 0x01, 0x00, 0x00, 0x1f, 0x01,
+	0x04, 0x04, 0x01, 0x10,
+	0x04, 0x04, 0x01, 0x00,
+	0x04, 0x05, 0x01, 0x61,
+	0x04, 0x04, 0x01, 0x01,
+	I2C0, 0x40, 0x0c, 0x02, 0x0c, 0x12, 0x07, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x0a,
+	I2C0, 0x40, 0x02, 0x11, 0x06,
+	I2C0, 0x40, 0x02, 0x14, 0x00,
+	I2C0, 0x40, 0x02, 0x13, 0x01,		/* i2c end */
+	I2C0, 0x40, 0x02, 0x07, 0x01,
+	0x02, 0x00, 0x11, 0x48, 0x58, 0x9e, 0x48, 0x58, 0x00, 0x00, 0x00,
+			  0x00, 0x84, 0x36, 0x05, 0x01, 0xf2, 0x86, 0x65,
+			  0x40,
+	I2C0, 0x40, 0x02, 0x02, 0x0c,
+	I2C0, 0x40, 0x02, 0x13, 0x01,
+	0x10, 0x00, 0x01, 0x01,
+	0x10, 0x8f, 0x0c, 0x62, 0x01, 0x24, 0x01, 0x62, 0x01, 0x24, 0x01,
+			  0x20, 0x01, 0x60, 0x01,
+	I2C0, 0x40, 0x02, 0x05, 0x0f,
+	I2C0, 0x40, 0x02, 0x13, 0x01,
+	I2C0, 0x40, 0x08, 0x08, 0x04, 0x0b, 0x01, 0x01, 0x02, 0x00, 0x17,
+	I2C0, 0x40, 0x03, 0x12, 0x00, 0x01,
+	0x10, 0x11, 0x08, 0x00, 0x00, 0x5f, 0x01, 0x00, 0x00, 0x1f, 0x01,
+	I2C0, 0x40, 0x02, 0x12, 0x00,
+	I2C0, 0x40, 0x02, 0x0e, 0x00,
+	I2C0, 0x40, 0x02, 0x11, 0x06,
+	0x10, 0x41, 0x11, 0x00, 0x17, 0x3f, 0x69, 0x7b, 0x8c, 0x9a, 0xa7,
+			  0xb3, 0xbf, 0xc9, 0xd3, 0xdd, 0xe6, 0xef, 0xf7,
+			  0xf9,
+	0x10, 0x03, 0x01, 0x00,
+	0x10, 0x0f, 0x02, 0x0c, 0x0c,
+	0x10, 0x03, 0x01, 0x06,
+	0x10, 0x41, 0x11, 0x00, 0x17, 0x3f, 0x69, 0x7b, 0x8c, 0x9a, 0xa7,
+			  0xb3, 0xbf, 0xc9, 0xd3, 0xdd, 0xe6, 0xef, 0xf7,
+			  0xf9,
+	0x10, 0x0b, 0x01, 0x19,
+	0x10, 0x0d, 0x01, 0x10,
+	0x10, 0x0c, 0x01, 0x0d,
+	0x04, 0x06, 0x01, 0x03,
+	0x04, 0x05, 0x01, 0x61,
+	0x04, 0x04, 0x01, 0x41,
+	0, 0, 0
+};
+
+/* nw802 dvc-v6 */
+static const u8 dvcv6_start[] = {
+	0x04, 0x06, 0x01, 0x06,
+	0x00, 0x00, 0x40, 0x54, 0x96, 0x98, 0xf9, 0x02, 0x18, 0x00, 0x4c,
+			  0x0f, 0x1f, 0x00, 0x0d, 0x02, 0x01, 0x00, 0x19,
+			  0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19,
+			  0x00, 0x0b, 0x00, 0x1b, 0x00, 0xc8, 0x00, 0xf4,
+			  0x05, 0xb4, 0x00, 0xcc, 0x00, 0x01, 0x00, 0x01,
+			  0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
+			  0x00, 0xa2, 0x00, 0xc6, 0x00, 0x60, 0x00, 0xc6,
+			  0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
+	0x00, 0x40, 0x40, 0x00, 0xae, 0x00, 0xd2, 0x00, 0xae, 0x00, 0xd2,
+			  0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
+			  0x00, 0xa8, 0x00, 0xc0, 0x00, 0x66, 0x00, 0xc0,
+			  0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
+			  0x00, 0x0a, 0x00, 0x54, 0x00, 0x0a, 0x00, 0x54,
+			  0x00, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6,
+			  0x00, 0x5d, 0x00, 0xc7, 0x00, 0x7e, 0x00, 0x30,
+	0x00, 0x80, 0x1f, 0x98, 0x43, 0x3f, 0x0d, 0x88, 0x20, 0x80, 0x3f,
+			  0x47, 0xaf, 0x00, 0x00, 0xa8, 0x08, 0x00, 0x11,
+			  0x00, 0x0c, 0x02, 0x0c, 0x00, 0x1c, 0x00, 0x94,
+			  0x00, 0x10, 0x06, 0x24, 0x00, 0x4a, 0x00,
+	0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00,
+			  0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0,
+			  0x40, 0x20,
+	0x03, 0x00, 0x03, 0x03, 0x00, 0x00,
+	0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0xff, 0x00,
+	0x06, 0x00, 0x02, 0x09, 0x99,
+	0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00,
+	0x10, 0x00, 0x40, 0xa0, 0x02, 0x80, 0x00, 0x12, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x0a,
+			  0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			  0x00, 0x49, 0x13, 0x00, 0x00, 0xe0, 0x00, 0x0c,
+			  0x00, 0x52, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00,
+			  0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08,
+			  0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06,
+			  0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80,
+	0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99,
+			  0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc,
+			  0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54,
+			  0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2,
+			  0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43,
+			  0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3,
+			  0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32,
+			  0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3,
+	0x10, 0x80, 0x1b, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00,
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x05, 0x82,
+			  0x02, 0xe4, 0x01, 0x40, 0x01, 0xf0, 0x00, 0x40,
+			  0x01, 0xf0, 0x00,
+	0x00, 0x03, 0x02, 0x94, 0x03,
+	0x00, 0x1d, 0x04, 0x0a, 0x01, 0x28, 0x07,
+	0x00, 0x7b, 0x02, 0xe0, 0x00,
+	0x10, 0x8d, 0x01, 0x00,
+	0x00, 0x09, 0x04, 0x1e, 0x00, 0x0c, 0x02,
+	0x00, 0x91, 0x02, 0x0b, 0x02,
+	0x10, 0x00, 0x01, 0xaf,
+	0x02, 0x00, 0x11, 0x3c, 0x50, 0x8f, 0x3c, 0x50, 0x00, 0x00, 0x00,
+			  0x00, 0x78, 0x3f, 0x3f, 0x06, 0xf2, 0x8f, 0xf0,
+			  0x40,
+	0x10, 0x1a, 0x01, 0x02,
+	0x10, 0x00, 0x01, 0xaf,
+	0x10, 0x85, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00,
+	0x10, 0x1b, 0x02, 0x07, 0x01,
+	0x10, 0x11, 0x08, 0x61, 0x00, 0xe0, 0x00, 0x49, 0x00, 0xa8, 0x00,
+	0x10, 0x1f, 0x06, 0x01, 0x20, 0x02, 0xe8, 0x03, 0x00,
+	0x10, 0x1d, 0x02, 0x40, 0x06,
+	0x10, 0x0e, 0x01, 0x08,
+	0x10, 0x41, 0x11, 0x00, 0x0f, 0x54, 0x6f, 0x82, 0x91, 0x9f, 0xaa,
+			  0xb4, 0xbd, 0xc5, 0xcd, 0xd5, 0xdb, 0xdc, 0xdc,
+			  0xdc,
+	0x10, 0x03, 0x01, 0x00,
+	0x10, 0x0f, 0x02, 0x12, 0x12,
+	0x10, 0x03, 0x01, 0x11,
+	0x10, 0x41, 0x11, 0x00, 0x0f, 0x54, 0x6f, 0x82, 0x91, 0x9f, 0xaa,
+			  0xb4, 0xbd, 0xc5, 0xcd, 0xd5, 0xdb, 0xdc, 0xdc,
+			  0xdc,
+	0x10, 0x0b, 0x01, 0x16,
+	0x10, 0x0d, 0x01, 0x10,
+	0x10, 0x0c, 0x01, 0x1a,
+	0x04, 0x06, 0x01, 0x03,
+	0x04, 0x04, 0x01, 0x00,
+};
+
+static const u8 *webcam_start[] = {
+	[Generic800] = nw800_start,
+	[SpaceCam] = spacecam_start,
+	[SpaceCam2] = spacecam2_start,
+	[Cvideopro] = cvideopro_start,
+	[Dlink350c] = dlink_start,
+	[DS3303u] = ds3303_start,
+	[Kr651us] = kr651_start_1,
+	[Kritter] = kritter_start,
+	[Mustek300] = mustek_start,
+	[Proscope] = proscope_start_1,
+	[Twinkle] = twinkle_start,
+	[DvcV6] = dvcv6_start,
+	[P35u] = nw801_start_1,
+	[Generic802] = nw802_start,
+};
+
+/* -- write a register -- */
+static void reg_w(struct gspca_dev *gspca_dev,
+			u16 index,
+			const u8 *data,
+			int len)
+{
+	struct usb_device *dev = gspca_dev->dev;
+	int ret;
+
+	if (gspca_dev->usb_err < 0)
+		return;
+	if (len == 1)
+		PDEBUG(D_USBO, "SET 00 0000 %04x %02x", index, *data);
+	else
+		PDEBUG(D_USBO, "SET 00 0000 %04x %02x %02x ...",
+				index, *data, data[1]);
+	memcpy(gspca_dev->usb_buf, data, len);
+	ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+			0x00,
+			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			0x00,		/* value */
+			index,
+			gspca_dev->usb_buf,
+			len,
+			500);
+	if (ret < 0) {
+		pr_err("reg_w err %d\n", ret);
+		gspca_dev->usb_err = ret;
+	}
+}
+
+/* -- read registers in usb_buf -- */
+static void reg_r(struct gspca_dev *gspca_dev,
+			u16 index,
+			int len)
+{
+	struct usb_device *dev = gspca_dev->dev;
+	int ret;
+
+	if (gspca_dev->usb_err < 0)
+		return;
+	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, len, 500);
+	if (ret < 0) {
+		pr_err("reg_r err %d\n", ret);
+		gspca_dev->usb_err = ret;
+		return;
+	}
+	if (len == 1)
+		PDEBUG(D_USBI, "GET 00 0000 %04x %02x",
+				index, gspca_dev->usb_buf[0]);
+	else
+		PDEBUG(D_USBI, "GET 00 0000 %04x %02x %02x ..",
+				index, gspca_dev->usb_buf[0],
+				gspca_dev->usb_buf[1]);
+}
+
+static void i2c_w(struct gspca_dev *gspca_dev,
+			u8 i2c_addr,
+			const u8 *data,
+			int len)
+{
+	u8 val[2];
+	int i;
+
+	reg_w(gspca_dev, 0x0600, data + 1, len - 1);
+	reg_w(gspca_dev, 0x0600, data, len);
+	val[0] = len;
+	val[1] = i2c_addr;
+	reg_w(gspca_dev, 0x0502, val, 2);
+	val[0] = 0x01;
+	reg_w(gspca_dev, 0x0501, val, 1);
+	for (i = 5; --i >= 0; ) {
+		msleep(4);
+		reg_r(gspca_dev, 0x0505, 1);
+		if (gspca_dev->usb_err < 0)
+			return;
+		if (gspca_dev->usb_buf[0] == 0)
+			return;
+	}
+	gspca_dev->usb_err = -ETIME;
+}
+
+static void reg_w_buf(struct gspca_dev *gspca_dev,
+			const u8 *cmd)
+{
+	u16 reg;
+	int len;
+
+	for (;;) {
+		reg = *cmd++ << 8;
+		reg += *cmd++;
+		len = *cmd++;
+		if (len == 0)
+			break;
+		if (cmd[-3] != I2C0)
+			reg_w(gspca_dev, reg, cmd, len);
+		else
+			i2c_w(gspca_dev, reg, cmd, len);
+		cmd += len;
+	}
+}
+
+static int swap_bits(int v)
+{
+	int r, i;
+
+	r = 0;
+	for (i = 0; i < 8; i++) {
+		r <<= 1;
+		if (v & 1)
+			r++;
+		v >>= 1;
+	}
+	return r;
+}
+
+static void setgain(struct gspca_dev *gspca_dev, u8 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u8 v[2];
+
+	switch (sd->webcam) {
+	case P35u:
+		reg_w(gspca_dev, 0x1026, &val, 1);
+		break;
+	case Kr651us:
+		/* 0 - 253 */
+		val = swap_bits(val);
+		v[0] = val << 3;
+		v[1] = val >> 5;
+		reg_w(gspca_dev, 0x101d, v, 2);	/* SIF reg0/1 (AGC) */
+		break;
+	}
+}
+
+static void setexposure(struct gspca_dev *gspca_dev, s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u8 v[2];
+
+	switch (sd->webcam) {
+	case P35u:
+		v[0] = ((9 - val) << 3) | 0x01;
+		reg_w(gspca_dev, 0x1019, v, 1);
+		break;
+	case Cvideopro:
+	case DvcV6:
+	case Kritter:
+	case Kr651us:
+		v[0] = val;
+		v[1] = val >> 8;
+		reg_w(gspca_dev, 0x101b, v, 2);
+		break;
+	}
+}
+
+static void setautogain(struct gspca_dev *gspca_dev, s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int w, h;
+
+	if (!val) {
+		sd->ag_cnt = -1;
+		return;
+	}
+	sd->ag_cnt = AG_CNT_START;
+
+	reg_r(gspca_dev, 0x1004, 1);
+	if (gspca_dev->usb_buf[0] & 0x04) {	/* if AE_FULL_FRM */
+		sd->ae_res = gspca_dev->pixfmt.width * gspca_dev->pixfmt.height;
+	} else {				/* get the AE window size */
+		reg_r(gspca_dev, 0x1011, 8);
+		w = (gspca_dev->usb_buf[1] << 8) + gspca_dev->usb_buf[0]
+		  - (gspca_dev->usb_buf[3] << 8) - gspca_dev->usb_buf[2];
+		h = (gspca_dev->usb_buf[5] << 8) + gspca_dev->usb_buf[4]
+		  - (gspca_dev->usb_buf[7] << 8) - gspca_dev->usb_buf[6];
+		sd->ae_res = h * w;
+		if (sd->ae_res == 0)
+			sd->ae_res = gspca_dev->pixfmt.width *
+					gspca_dev->pixfmt.height;
+	}
+}
+
+static int nw802_test_reg(struct gspca_dev *gspca_dev,
+			u16 index,
+			u8 value)
+{
+	/* write the value */
+	reg_w(gspca_dev, index, &value, 1);
+
+	/* read it */
+	reg_r(gspca_dev, index, 1);
+
+	return gspca_dev->usb_buf[0] == value;
+}
+
+/* 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;
+
+	if ((unsigned) webcam >= NWEBCAMS)
+		webcam = 0;
+	sd->webcam = webcam;
+	gspca_dev->cam.needs_full_bandwidth = 1;
+	sd->ag_cnt = -1;
+
+	/*
+	 * Autodetect sequence inspired from some log.
+	 * We try to detect what registers exist or not.
+	 * If 0x0500 does not exist => NW802
+	 * If it does, test 0x109b. If it doesn't exist,
+	 * then it's a NW801. Else, a NW800
+	 * If a et31x110 (nw800 and 06a5:d800)
+	 *	get the sensor ID
+	 */
+	if (!nw802_test_reg(gspca_dev, 0x0500, 0x55)) {
+		sd->bridge = BRIDGE_NW802;
+		if (sd->webcam == Generic800)
+			sd->webcam = Generic802;
+	} else if (!nw802_test_reg(gspca_dev, 0x109b, 0xaa)) {
+		sd->bridge = BRIDGE_NW801;
+		if (sd->webcam == Generic800)
+			sd->webcam = P35u;
+	} else if (id->idVendor == 0x06a5 && id->idProduct == 0xd800) {
+		reg_r(gspca_dev, 0x0403, 1);		/* GPIO */
+		PDEBUG(D_PROBE, "et31x110 sensor type %02x",
+				gspca_dev->usb_buf[0]);
+		switch (gspca_dev->usb_buf[0] >> 1) {
+		case 0x00:				/* ?? */
+			if (sd->webcam == Generic800)
+				sd->webcam = SpaceCam;
+			break;
+		case 0x01:				/* Hynix? */
+			if (sd->webcam == Generic800)
+				sd->webcam = Twinkle;
+			break;
+		case 0x0a:				/* Pixart */
+			if (sd->webcam == Generic800)
+				sd->webcam = SpaceCam2;
+			break;
+		}
+	}
+	if (webcam_chip[sd->webcam] != sd->bridge) {
+		pr_err("Bad webcam type %d for NW80%d\n",
+		       sd->webcam, sd->bridge);
+		gspca_dev->usb_err = -ENODEV;
+		return gspca_dev->usb_err;
+	}
+	PDEBUG(D_PROBE, "Bridge nw80%d - type: %d", sd->bridge, sd->webcam);
+
+	if (sd->bridge == BRIDGE_NW800) {
+		switch (sd->webcam) {
+		case DS3303u:
+			gspca_dev->cam.cam_mode = cif_mode;	/* qvga */
+			break;
+		default:
+			gspca_dev->cam.cam_mode = &cif_mode[1];	/* cif */
+			break;
+		}
+		gspca_dev->cam.nmodes = 1;
+	} else {
+		gspca_dev->cam.cam_mode = vga_mode;
+		switch (sd->webcam) {
+		case Kr651us:
+		case Proscope:
+		case P35u:
+			gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode);
+			break;
+		default:
+			gspca_dev->cam.nmodes = 1;	/* qvga only */
+			break;
+		}
+	}
+
+	return gspca_dev->usb_err;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	switch (sd->bridge) {
+	case BRIDGE_NW800:
+		switch (sd->webcam) {
+		case SpaceCam:
+			reg_w_buf(gspca_dev, spacecam_init);
+			break;
+		default:
+			reg_w_buf(gspca_dev, nw800_init);
+			break;
+		}
+		break;
+	default:
+		switch (sd->webcam) {
+		case Mustek300:
+		case P35u:
+		case Proscope:
+			reg_w_buf(gspca_dev, proscope_init);
+			break;
+		}
+		break;
+	}
+	return gspca_dev->usb_err;
+}
+
+/* -- start the camera -- */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	const u8 *cmd;
+
+	cmd = webcam_start[sd->webcam];
+	reg_w_buf(gspca_dev, cmd);
+	switch (sd->webcam) {
+	case P35u:
+		if (gspca_dev->pixfmt.width == 320)
+			reg_w_buf(gspca_dev, nw801_start_qvga);
+		else
+			reg_w_buf(gspca_dev, nw801_start_vga);
+		reg_w_buf(gspca_dev, nw801_start_2);
+		break;
+	case Kr651us:
+		if (gspca_dev->pixfmt.width == 320)
+			reg_w_buf(gspca_dev, kr651_start_qvga);
+		else
+			reg_w_buf(gspca_dev, kr651_start_vga);
+		reg_w_buf(gspca_dev, kr651_start_2);
+		break;
+	case Proscope:
+		if (gspca_dev->pixfmt.width == 320)
+			reg_w_buf(gspca_dev, proscope_start_qvga);
+		else
+			reg_w_buf(gspca_dev, proscope_start_vga);
+		reg_w_buf(gspca_dev, proscope_start_2);
+		break;
+	}
+
+	sd->exp_too_high_cnt = 0;
+	sd->exp_too_low_cnt = 0;
+	return gspca_dev->usb_err;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u8 value;
+
+	/* 'go' off */
+	if (sd->bridge != BRIDGE_NW801) {
+		value = 0x02;
+		reg_w(gspca_dev, 0x0406, &value, 1);
+	}
+
+	/* LED off */
+	switch (sd->webcam) {
+	case Cvideopro:
+	case Kr651us:
+	case DvcV6:
+	case Kritter:
+		value = 0xff;
+		break;
+	case Dlink350c:
+		value = 0x21;
+		break;
+	case SpaceCam:
+	case SpaceCam2:
+	case Proscope:
+	case Twinkle:
+		value = 0x01;
+		break;
+	default:
+		return;
+	}
+	reg_w(gspca_dev, 0x0404, &value, 1);
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+			u8 *data,			/* isoc packet */
+			int len)			/* iso packet length */
+{
+	/*
+	 * frame header = '00 00 hh ww ss xx ff ff'
+	 * with:
+	 *	- 'hh': height / 4
+	 *	- 'ww': width / 4
+	 *	- 'ss': frame sequence number c0..dd
+	 */
+	if (data[0] == 0x00 && data[1] == 0x00
+	 && data[6] == 0xff && data[7] == 0xff) {
+		gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+		gspca_frame_add(gspca_dev, FIRST_PACKET, data + 8, len - 8);
+	} else {
+		gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+	}
+}
+
+static void do_autogain(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int luma;
+
+	if (sd->ag_cnt < 0)
+		return;
+	if (--sd->ag_cnt >= 0)
+		return;
+	sd->ag_cnt = AG_CNT_START;
+
+	/* get the average luma */
+	reg_r(gspca_dev, sd->bridge == BRIDGE_NW801 ? 0x080d : 0x080c, 4);
+	luma = (gspca_dev->usb_buf[3] << 24) + (gspca_dev->usb_buf[2] << 16)
+		+ (gspca_dev->usb_buf[1] << 8) + gspca_dev->usb_buf[0];
+	luma /= sd->ae_res;
+
+	switch (sd->webcam) {
+	case P35u:
+		gspca_coarse_grained_expo_autogain(gspca_dev, luma, 100, 5);
+		break;
+	default:
+		gspca_expo_autogain(gspca_dev, luma, 100, 5, 230, 0);
+		break;
+	}
+}
+
+
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct gspca_dev *gspca_dev =
+		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+
+	gspca_dev->usb_err = 0;
+
+	if (!gspca_dev->streaming)
+		return 0;
+
+	switch (ctrl->id) {
+	/* autogain/gain/exposure control cluster */
+	case V4L2_CID_AUTOGAIN:
+		if (ctrl->is_new)
+			setautogain(gspca_dev, ctrl->val);
+		if (!ctrl->val) {
+			if (gspca_dev->gain->is_new)
+				setgain(gspca_dev, gspca_dev->gain->val);
+			if (gspca_dev->exposure->is_new)
+				setexposure(gspca_dev,
+					    gspca_dev->exposure->val);
+		}
+		break;
+	/* Some webcams only have exposure, so handle that separately from the
+	   autogain/gain/exposure cluster in the previous case. */
+	case V4L2_CID_EXPOSURE:
+		setexposure(gspca_dev, gspca_dev->exposure->val);
+		break;
+	}
+	return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+	.s_ctrl = sd_s_ctrl,
+};
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *)gspca_dev;
+	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+	gspca_dev->vdev.ctrl_handler = hdl;
+	v4l2_ctrl_handler_init(hdl, 3);
+	switch (sd->webcam) {
+	case P35u:
+		gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+		/* For P35u choose coarse expo auto gain function gain minimum,
+		 * to avoid a large settings jump the first auto adjustment */
+		gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_GAIN, 0, 127, 1, 127 / 5 * 2);
+		gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_EXPOSURE, 0, 9, 1, 9);
+		break;
+	case Kr651us:
+		gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+		gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_GAIN, 0, 253, 1, 128);
+		/* fall through */
+	case Cvideopro:
+	case DvcV6:
+	case Kritter:
+		gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_EXPOSURE, 0, 315, 1, 150);
+		break;
+	default:
+		break;
+	}
+
+	if (hdl->error) {
+		pr_err("Could not initialize controls\n");
+		return hdl->error;
+	}
+	if (gspca_dev->autogain)
+		v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false);
+	return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+	.name = MODULE_NAME,
+	.config = sd_config,
+	.init = sd_init,
+	.init_controls = sd_init_controls,
+	.start = sd_start,
+	.stopN = sd_stopN,
+	.pkt_scan = sd_pkt_scan,
+	.dq_callback = do_autogain,
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+	{USB_DEVICE(0x046d, 0xd001)},
+	{USB_DEVICE(0x0502, 0xd001)},
+	{USB_DEVICE(0x052b, 0xd001)},
+	{USB_DEVICE(0x055f, 0xd001)},
+	{USB_DEVICE(0x06a5, 0x0000)},
+	{USB_DEVICE(0x06a5, 0xd001)},
+	{USB_DEVICE(0x06a5, 0xd800)},
+	{USB_DEVICE(0x06be, 0xd001)},
+	{USB_DEVICE(0x0728, 0xd001)},
+	{}
+};
+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,
+#ifdef CONFIG_PM
+	.suspend = gspca_suspend,
+	.resume = gspca_resume,
+	.reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
+
+module_param(webcam, int, 0644);
+MODULE_PARM_DESC(webcam,
+	"Webcam type\n"
+	"0: generic\n"
+	"1: Trust 120 SpaceCam\n"
+	"2: other Trust 120 SpaceCam\n"
+	"3: Conceptronic Video Pro\n"
+	"4: D-link dru-350c\n"
+	"5: Plustek Opticam 500U\n"
+	"6: Panasonic GP-KR651US\n"
+	"7: iRez Kritter\n"
+	"8: Mustek Wcam 300 mini\n"
+	"9: Scalar USB Microscope M2 (Proscope)\n"
+	"10: Divio Chicony TwinkleCam\n"
+	"11: DVC-V6\n");
diff --git a/drivers/media/usb/gspca/ov519.c b/drivers/media/usb/gspca/ov519.c
new file mode 100644
index 0000000..c95f32a
--- /dev/null
+++ b/drivers/media/usb/gspca/ov519.c
@@ -0,0 +1,5040 @@
+/**
+ * OV519 driver
+ *
+ * Copyright (C) 2008-2011 Jean-François Moine <moinejf@free.fr>
+ * Copyright (C) 2009 Hans de Goede <hdegoede@redhat.com>
+ *
+ * This module is adapted from the ov51x-jpeg package, which itself
+ * was adapted from the ov511 driver.
+ *
+ * Original copyright for the ov511 driver is:
+ *
+ * Copyright (c) 1999-2006 Mark W. McClelland
+ * Support for OV519, OV8610 Copyright (c) 2003 Joerg Heckenbach
+ * Many improvements by Bret Wallach <bwallac1@san.rr.com>
+ * Color fixes by by Orion Sky Lawlor <olawlor@acm.org> (2/26/2000)
+ * OV7620 fixes by Charl P. Botha <cpbotha@ieee.org>
+ * Changes by Claudio Matsuoka <claudio@conectiva.com>
+ *
+ * ov51x-jpeg original copyright is:
+ *
+ * Copyright (c) 2004-2007 Romain Beauxis <toots@rastageeks.org>
+ * Support for OV7670 sensors was contributed by Sam Skipsey <aoanla@yahoo.com>
+ *
+ * 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "ov519"
+
+#include <linux/input.h>
+#include "gspca.h"
+
+/* The jpeg_hdr is used by w996Xcf only */
+/* The CONEX_CAM define for jpeg.h needs renaming, now its used here too */
+#define CONEX_CAM
+#include "jpeg.h"
+
+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 */
+
+	struct v4l2_ctrl *jpegqual;
+	struct v4l2_ctrl *freq;
+	struct { /* h/vflip control cluster */
+		struct v4l2_ctrl *hflip;
+		struct v4l2_ctrl *vflip;
+	};
+	struct { /* autobrightness/brightness control cluster */
+		struct v4l2_ctrl *autobright;
+		struct v4l2_ctrl *brightness;
+	};
+
+	u8 revision;
+
+	u8 packet_nr;
+
+	char bridge;
+#define BRIDGE_OV511		0
+#define BRIDGE_OV511PLUS	1
+#define BRIDGE_OV518		2
+#define BRIDGE_OV518PLUS	3
+#define BRIDGE_OV519		4		/* = ov530 */
+#define BRIDGE_OVFX2		5
+#define BRIDGE_W9968CF		6
+#define BRIDGE_MASK		7
+
+	char invert_led;
+#define BRIDGE_INVERT_LED	8
+
+	char snapshot_pressed;
+	char snapshot_needs_reset;
+
+	/* Determined by sensor type */
+	u8 sif;
+
+#define QUALITY_MIN 50
+#define QUALITY_MAX 70
+#define QUALITY_DEF 50
+
+	u8 stopped;		/* Streaming is temporarily paused */
+	u8 first_frame;
+
+	u8 frame_rate;		/* current Framerate */
+	u8 clockdiv;		/* clockdiv override */
+
+	s8 sensor;		/* Type of image sensor chip (SEN_*) */
+
+	u8 sensor_addr;
+	u16 sensor_width;
+	u16 sensor_height;
+	s16 sensor_reg_cache[256];
+
+	u8 jpeg_hdr[JPEG_HDR_SZ];
+};
+enum sensors {
+	SEN_OV2610,
+	SEN_OV2610AE,
+	SEN_OV3610,
+	SEN_OV6620,
+	SEN_OV6630,
+	SEN_OV66308AF,
+	SEN_OV7610,
+	SEN_OV7620,
+	SEN_OV7620AE,
+	SEN_OV7640,
+	SEN_OV7648,
+	SEN_OV7660,
+	SEN_OV7670,
+	SEN_OV76BE,
+	SEN_OV8610,
+	SEN_OV9600,
+};
+
+/* Note this is a bit of a hack, but the w9968cf driver needs the code for all
+   the ov sensors which is already present here. When we have the time we
+   really should move the sensor drivers to v4l2 sub drivers. */
+#include "w996Xcf.c"
+
+/* table of the disabled controls */
+struct ctrl_valid {
+	unsigned int has_brightness:1;
+	unsigned int has_contrast:1;
+	unsigned int has_exposure:1;
+	unsigned int has_autogain:1;
+	unsigned int has_sat:1;
+	unsigned int has_hvflip:1;
+	unsigned int has_autobright:1;
+	unsigned int has_freq:1;
+};
+
+static const struct ctrl_valid valid_controls[] = {
+	[SEN_OV2610] = {
+		.has_exposure = 1,
+		.has_autogain = 1,
+	},
+	[SEN_OV2610AE] = {
+		.has_exposure = 1,
+		.has_autogain = 1,
+	},
+	[SEN_OV3610] = {
+		/* No controls */
+	},
+	[SEN_OV6620] = {
+		.has_brightness = 1,
+		.has_contrast = 1,
+		.has_sat = 1,
+		.has_autobright = 1,
+		.has_freq = 1,
+	},
+	[SEN_OV6630] = {
+		.has_brightness = 1,
+		.has_contrast = 1,
+		.has_sat = 1,
+		.has_autobright = 1,
+		.has_freq = 1,
+	},
+	[SEN_OV66308AF] = {
+		.has_brightness = 1,
+		.has_contrast = 1,
+		.has_sat = 1,
+		.has_autobright = 1,
+		.has_freq = 1,
+	},
+	[SEN_OV7610] = {
+		.has_brightness = 1,
+		.has_contrast = 1,
+		.has_sat = 1,
+		.has_autobright = 1,
+		.has_freq = 1,
+	},
+	[SEN_OV7620] = {
+		.has_brightness = 1,
+		.has_contrast = 1,
+		.has_sat = 1,
+		.has_autobright = 1,
+		.has_freq = 1,
+	},
+	[SEN_OV7620AE] = {
+		.has_brightness = 1,
+		.has_contrast = 1,
+		.has_sat = 1,
+		.has_autobright = 1,
+		.has_freq = 1,
+	},
+	[SEN_OV7640] = {
+		.has_brightness = 1,
+		.has_sat = 1,
+		.has_freq = 1,
+	},
+	[SEN_OV7648] = {
+		.has_brightness = 1,
+		.has_sat = 1,
+		.has_freq = 1,
+	},
+	[SEN_OV7660] = {
+		.has_brightness = 1,
+		.has_contrast = 1,
+		.has_sat = 1,
+		.has_hvflip = 1,
+		.has_freq = 1,
+	},
+	[SEN_OV7670] = {
+		.has_brightness = 1,
+		.has_contrast = 1,
+		.has_hvflip = 1,
+		.has_freq = 1,
+	},
+	[SEN_OV76BE] = {
+		.has_brightness = 1,
+		.has_contrast = 1,
+		.has_sat = 1,
+		.has_autobright = 1,
+		.has_freq = 1,
+	},
+	[SEN_OV8610] = {
+		.has_brightness = 1,
+		.has_contrast = 1,
+		.has_sat = 1,
+		.has_autobright = 1,
+	},
+	[SEN_OV9600] = {
+		.has_exposure = 1,
+		.has_autogain = 1,
+	},
+};
+
+static const struct v4l2_pix_format ov519_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 const struct v4l2_pix_format ov519_sif_mode[] = {
+	{160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+		.bytesperline = 160,
+		.sizeimage = 160 * 120 * 3 / 8 + 590,
+		.colorspace = V4L2_COLORSPACE_JPEG,
+		.priv = 3},
+	{176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+		.bytesperline = 176,
+		.sizeimage = 176 * 144 * 3 / 8 + 590,
+		.colorspace = V4L2_COLORSPACE_JPEG,
+		.priv = 1},
+	{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 = 0},
+};
+
+/* Note some of the sizeimage values for the ov511 / ov518 may seem
+   larger then necessary, however they need to be this big as the ov511 /
+   ov518 always fills the entire isoc frame, using 0 padding bytes when
+   it doesn't have any data. So with low framerates the amount of data
+   transferred can become quite large (libv4l will remove all the 0 padding
+   in userspace). */
+static const struct v4l2_pix_format ov518_vga_mode[] = {
+	{320, 240, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE,
+		.bytesperline = 320,
+		.sizeimage = 320 * 240 * 3,
+		.colorspace = V4L2_COLORSPACE_JPEG,
+		.priv = 1},
+	{640, 480, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE,
+		.bytesperline = 640,
+		.sizeimage = 640 * 480 * 2,
+		.colorspace = V4L2_COLORSPACE_JPEG,
+		.priv = 0},
+};
+static const struct v4l2_pix_format ov518_sif_mode[] = {
+	{160, 120, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE,
+		.bytesperline = 160,
+		.sizeimage = 70000,
+		.colorspace = V4L2_COLORSPACE_JPEG,
+		.priv = 3},
+	{176, 144, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE,
+		.bytesperline = 176,
+		.sizeimage = 70000,
+		.colorspace = V4L2_COLORSPACE_JPEG,
+		.priv = 1},
+	{320, 240, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE,
+		.bytesperline = 320,
+		.sizeimage = 320 * 240 * 3,
+		.colorspace = V4L2_COLORSPACE_JPEG,
+		.priv = 2},
+	{352, 288, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE,
+		.bytesperline = 352,
+		.sizeimage = 352 * 288 * 3,
+		.colorspace = V4L2_COLORSPACE_JPEG,
+		.priv = 0},
+};
+
+static const struct v4l2_pix_format ov511_vga_mode[] = {
+	{320, 240, V4L2_PIX_FMT_OV511, V4L2_FIELD_NONE,
+		.bytesperline = 320,
+		.sizeimage = 320 * 240 * 3,
+		.colorspace = V4L2_COLORSPACE_JPEG,
+		.priv = 1},
+	{640, 480, V4L2_PIX_FMT_OV511, V4L2_FIELD_NONE,
+		.bytesperline = 640,
+		.sizeimage = 640 * 480 * 2,
+		.colorspace = V4L2_COLORSPACE_JPEG,
+		.priv = 0},
+};
+static const struct v4l2_pix_format ov511_sif_mode[] = {
+	{160, 120, V4L2_PIX_FMT_OV511, V4L2_FIELD_NONE,
+		.bytesperline = 160,
+		.sizeimage = 70000,
+		.colorspace = V4L2_COLORSPACE_JPEG,
+		.priv = 3},
+	{176, 144, V4L2_PIX_FMT_OV511, V4L2_FIELD_NONE,
+		.bytesperline = 176,
+		.sizeimage = 70000,
+		.colorspace = V4L2_COLORSPACE_JPEG,
+		.priv = 1},
+	{320, 240, V4L2_PIX_FMT_OV511, V4L2_FIELD_NONE,
+		.bytesperline = 320,
+		.sizeimage = 320 * 240 * 3,
+		.colorspace = V4L2_COLORSPACE_JPEG,
+		.priv = 2},
+	{352, 288, V4L2_PIX_FMT_OV511, V4L2_FIELD_NONE,
+		.bytesperline = 352,
+		.sizeimage = 352 * 288 * 3,
+		.colorspace = V4L2_COLORSPACE_JPEG,
+		.priv = 0},
+};
+
+static const struct v4l2_pix_format ovfx2_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 const struct v4l2_pix_format ovfx2_cif_mode[] = {
+	{160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+		.bytesperline = 160,
+		.sizeimage = 160 * 120,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 3},
+	{176, 144, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+		.bytesperline = 176,
+		.sizeimage = 176 * 144,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 1},
+	{320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+		.bytesperline = 320,
+		.sizeimage = 320 * 240,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 2},
+	{352, 288, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+		.bytesperline = 352,
+		.sizeimage = 352 * 288,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 0},
+};
+static const struct v4l2_pix_format ovfx2_ov2610_mode[] = {
+	{800, 600, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+		.bytesperline = 800,
+		.sizeimage = 800 * 600,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 1},
+	{1600, 1200, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+		.bytesperline = 1600,
+		.sizeimage = 1600 * 1200,
+		.colorspace = V4L2_COLORSPACE_SRGB},
+};
+static const struct v4l2_pix_format ovfx2_ov3610_mode[] = {
+	{640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+		.bytesperline = 640,
+		.sizeimage = 640 * 480,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 1},
+	{800, 600, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+		.bytesperline = 800,
+		.sizeimage = 800 * 600,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 1},
+	{1024, 768, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+		.bytesperline = 1024,
+		.sizeimage = 1024 * 768,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 1},
+	{1600, 1200, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+		.bytesperline = 1600,
+		.sizeimage = 1600 * 1200,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 0},
+	{2048, 1536, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+		.bytesperline = 2048,
+		.sizeimage = 2048 * 1536,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 0},
+};
+static const struct v4l2_pix_format ovfx2_ov9600_mode[] = {
+	{640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+		.bytesperline = 640,
+		.sizeimage = 640 * 480,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 1},
+	{1280, 1024, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+		.bytesperline = 1280,
+		.sizeimage = 1280 * 1024,
+		.colorspace = V4L2_COLORSPACE_SRGB},
+};
+
+/* Registers common to OV511 / OV518 */
+#define R51x_FIFO_PSIZE			0x30	/* 2 bytes wide w/ OV518(+) */
+#define R51x_SYS_RESET			0x50
+	/* Reset type flags */
+	#define	OV511_RESET_OMNICE	0x08
+#define R51x_SYS_INIT			0x53
+#define R51x_SYS_SNAP			0x52
+#define R51x_SYS_CUST_ID		0x5f
+#define R51x_COMP_LUT_BEGIN		0x80
+
+/* OV511 Camera interface register numbers */
+#define R511_CAM_DELAY			0x10
+#define R511_CAM_EDGE			0x11
+#define R511_CAM_PXCNT			0x12
+#define R511_CAM_LNCNT			0x13
+#define R511_CAM_PXDIV			0x14
+#define R511_CAM_LNDIV			0x15
+#define R511_CAM_UV_EN			0x16
+#define R511_CAM_LINE_MODE		0x17
+#define R511_CAM_OPTS			0x18
+
+#define R511_SNAP_FRAME			0x19
+#define R511_SNAP_PXCNT			0x1a
+#define R511_SNAP_LNCNT			0x1b
+#define R511_SNAP_PXDIV			0x1c
+#define R511_SNAP_LNDIV			0x1d
+#define R511_SNAP_UV_EN			0x1e
+#define R511_SNAP_OPTS			0x1f
+
+#define R511_DRAM_FLOW_CTL		0x20
+#define R511_FIFO_OPTS			0x31
+#define R511_I2C_CTL			0x40
+#define R511_SYS_LED_CTL		0x55	/* OV511+ only */
+#define R511_COMP_EN			0x78
+#define R511_COMP_LUT_EN		0x79
+
+/* OV518 Camera interface register numbers */
+#define R518_GPIO_OUT			0x56	/* OV518(+) only */
+#define R518_GPIO_CTL			0x57	/* OV518(+) only */
+
+/* OV519 Camera interface register numbers */
+#define OV519_R10_H_SIZE		0x10
+#define OV519_R11_V_SIZE		0x11
+#define OV519_R12_X_OFFSETL		0x12
+#define OV519_R13_X_OFFSETH		0x13
+#define OV519_R14_Y_OFFSETL		0x14
+#define OV519_R15_Y_OFFSETH		0x15
+#define OV519_R16_DIVIDER		0x16
+#define OV519_R20_DFR			0x20
+#define OV519_R25_FORMAT		0x25
+
+/* OV519 System Controller register numbers */
+#define OV519_R51_RESET1		0x51
+#define OV519_R54_EN_CLK1		0x54
+#define OV519_R57_SNAPSHOT		0x57
+
+#define OV519_GPIO_DATA_OUT0		0x71
+#define OV519_GPIO_IO_CTRL0		0x72
+
+/*#define OV511_ENDPOINT_ADDRESS 1	 * Isoc endpoint number */
+
+/*
+ * The FX2 chip does not give us a zero length read at end of frame.
+ * It does, however, give a short read at the end of a frame, if
+ * necessary, rather than run two frames together.
+ *
+ * By choosing the right bulk transfer size, we are guaranteed to always
+ * get a short read for the last read of each frame.  Frame sizes are
+ * always a composite number (width * height, or a multiple) so if we
+ * choose a prime number, we are guaranteed that the last read of a
+ * frame will be short.
+ *
+ * But it isn't that easy: the 2.6 kernel requires a multiple of 4KB,
+ * otherwise EOVERFLOW "babbling" errors occur.  I have not been able
+ * to figure out why.  [PMiller]
+ *
+ * The constant (13 * 4096) is the largest "prime enough" number less than 64KB.
+ *
+ * It isn't enough to know the number of bytes per frame, in case we
+ * have data dropouts or buffer overruns (even though the FX2 double
+ * buffers, there are some pretty strict real time constraints for
+ * isochronous transfer for larger frame sizes).
+ */
+/*jfm: this value does not work for 800x600 - see isoc_init */
+#define OVFX2_BULK_SIZE (13 * 4096)
+
+/* 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 */
+#define OVFX2_I2C_ADDR		0x00
+
+/* I2C ADDRESSES */
+#define OV7xx0_SID   0x42
+#define OV_HIRES_SID 0x60		/* OV9xxx / OV2xxx / OV3xxx */
+#define OV8xx0_SID   0xa0
+#define OV6xx0_SID   0xc0
+
+/* OV7610 registers */
+#define OV7610_REG_GAIN		0x00	/* gain setting (5:0) */
+#define OV7610_REG_BLUE		0x01	/* blue channel balance */
+#define OV7610_REG_RED		0x02	/* red channel balance */
+#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 */
+
+/* OV7660 and OV7670 registers */
+#define OV7670_R00_GAIN		0x00	/* Gain lower 8 bits (rest in vref) */
+#define OV7670_R01_BLUE		0x01	/* blue gain */
+#define OV7670_R02_RED		0x02	/* red gain */
+#define OV7670_R03_VREF		0x03	/* Pieces of GAIN, VSTART, VSTOP */
+#define OV7670_R04_COM1		0x04	/* Control 1 */
+/*#define OV7670_R07_AECHH	0x07	 * AEC MS 5 bits */
+#define OV7670_R0C_COM3		0x0c	/* Control 3 */
+#define OV7670_R0D_COM4		0x0d	/* Control 4 */
+#define OV7670_R0E_COM5		0x0e	/* All "reserved" */
+#define OV7670_R0F_COM6		0x0f	/* Control 6 */
+#define OV7670_R10_AECH		0x10	/* More bits of AEC value */
+#define OV7670_R11_CLKRC	0x11	/* Clock control */
+#define OV7670_R12_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_R13_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_R14_COM9		0x14	/* Control 9 - gain ceiling */
+#define OV7670_R15_COM10	0x15	/* Control 10 */
+#define OV7670_R17_HSTART	0x17	/* Horiz start high bits */
+#define OV7670_R18_HSTOP	0x18	/* Horiz stop high bits */
+#define OV7670_R19_VSTART	0x19	/* Vert start high bits */
+#define OV7670_R1A_VSTOP	0x1a	/* Vert stop high bits */
+#define OV7670_R1E_MVFP		0x1e	/* Mirror / vflip */
+#define   OV7670_MVFP_VFLIP	 0x10	/* vertical flip */
+#define   OV7670_MVFP_MIRROR	 0x20	/* Mirror image */
+#define OV7670_R24_AEW		0x24	/* AGC upper limit */
+#define OV7670_R25_AEB		0x25	/* AGC lower limit */
+#define OV7670_R26_VPT		0x26	/* AGC/AEC fast mode op region */
+#define OV7670_R32_HREF		0x32	/* HREF pieces */
+#define OV7670_R3A_TSLB		0x3a	/* lots of stuff */
+#define OV7670_R3B_COM11	0x3b	/* Control 11 */
+#define   OV7670_COM11_EXP	 0x02
+#define   OV7670_COM11_HZAUTO	 0x10	/* Auto detect 50/60 Hz */
+#define OV7670_R3C_COM12	0x3c	/* Control 12 */
+#define OV7670_R3D_COM13	0x3d	/* Control 13 */
+#define   OV7670_COM13_GAMMA	 0x80	/* Gamma enable */
+#define   OV7670_COM13_UVSAT	 0x40	/* UV saturation auto adjustment */
+#define OV7670_R3E_COM14	0x3e	/* Control 14 */
+#define OV7670_R3F_EDGE		0x3f	/* Edge enhancement factor */
+#define OV7670_R40_COM15	0x40	/* Control 15 */
+/*#define   OV7670_COM15_R00FF	 0xc0	 *	00 to FF */
+#define OV7670_R41_COM16	0x41	/* Control 16 */
+#define   OV7670_COM16_AWBGAIN	 0x08	/* AWB gain enable */
+/* end of ov7660 common registers */
+#define OV7670_R55_BRIGHT	0x55	/* Brightness */
+#define OV7670_R56_CONTRAS	0x56	/* Contrast control */
+#define OV7670_R69_GFIX		0x69	/* Fix gain control */
+/*#define OV7670_R8C_RGB444	0x8c	 * RGB 444 control */
+#define OV7670_R9F_HAECC1	0x9f	/* Hist AEC/AGC control 1 */
+#define OV7670_RA0_HAECC2	0xa0	/* Hist AEC/AGC control 2 */
+#define OV7670_RA5_BD50MAX	0xa5	/* 50hz banding step limit */
+#define OV7670_RA6_HAECC3	0xa6	/* Hist AEC/AGC control 3 */
+#define OV7670_RA7_HAECC4	0xa7	/* Hist AEC/AGC control 4 */
+#define OV7670_RA8_HAECC5	0xa8	/* Hist AEC/AGC control 5 */
+#define OV7670_RA9_HAECC6	0xa9	/* Hist AEC/AGC control 6 */
+#define OV7670_RAA_HAECC7	0xaa	/* Hist AEC/AGC control 7 */
+#define OV7670_RAB_BD60MAX	0xab	/* 60hz banding step limit */
+
+struct ov_regvals {
+	u8 reg;
+	u8 val;
+};
+struct ov_i2c_regvals {
+	u8 reg;
+	u8 val;
+};
+
+/* Settings for OV2610 camera chip */
+static const struct ov_i2c_regvals norm_2610[] = {
+	{ 0x12, 0x80 },	/* reset */
+};
+
+static const struct ov_i2c_regvals norm_2610ae[] = {
+	{0x12, 0x80},	/* reset */
+	{0x13, 0xcd},
+	{0x09, 0x01},
+	{0x0d, 0x00},
+	{0x11, 0x80},
+	{0x12, 0x20},	/* 1600x1200 */
+	{0x33, 0x0c},
+	{0x35, 0x90},
+	{0x36, 0x37},
+/* ms-win traces */
+	{0x11, 0x83},	/* clock / 3 ? */
+	{0x2d, 0x00},	/* 60 Hz filter */
+	{0x24, 0xb0},	/* normal colors */
+	{0x25, 0x90},
+	{0x10, 0x43},
+};
+
+static const struct ov_i2c_regvals norm_3620b[] = {
+	/*
+	 * From the datasheet: "Note that after writing to register COMH
+	 * (0x12) to change the sensor mode, registers related to the
+	 * sensor’s cropping window will be reset back to their default
+	 * values."
+	 *
+	 * "wait 4096 external clock ... to make sure the sensor is
+	 * stable and ready to access registers" i.e. 160us at 24MHz
+	 */
+	{ 0x12, 0x80 }, /* COMH reset */
+	{ 0x12, 0x00 }, /* QXGA, master */
+
+	/*
+	 * 11 CLKRC "Clock Rate Control"
+	 * [7] internal frequency doublers: on
+	 * [6] video port mode: master
+	 * [5:0] clock divider: 1
+	 */
+	{ 0x11, 0x80 },
+
+	/*
+	 * 13 COMI "Common Control I"
+	 *                  = 192 (0xC0) 11000000
+	 *    COMI[7] "AEC speed selection"
+	 *                  =   1 (0x01) 1....... "Faster AEC correction"
+	 *    COMI[6] "AEC speed step selection"
+	 *                  =   1 (0x01) .1...... "Big steps, fast"
+	 *    COMI[5] "Banding filter on off"
+	 *                  =   0 (0x00) ..0..... "Off"
+	 *    COMI[4] "Banding filter option"
+	 *                  =   0 (0x00) ...0.... "Main clock is 48 MHz and
+	 *                                         the PLL is ON"
+	 *    COMI[3] "Reserved"
+	 *                  =   0 (0x00) ....0...
+	 *    COMI[2] "AGC auto manual control selection"
+	 *                  =   0 (0x00) .....0.. "Manual"
+	 *    COMI[1] "AWB auto manual control selection"
+	 *                  =   0 (0x00) ......0. "Manual"
+	 *    COMI[0] "Exposure control"
+	 *                  =   0 (0x00) .......0 "Manual"
+	 */
+	{ 0x13, 0xc0 },
+
+	/*
+	 * 09 COMC "Common Control C"
+	 *                  =   8 (0x08) 00001000
+	 *    COMC[7:5] "Reserved"
+	 *                  =   0 (0x00) 000.....
+	 *    COMC[4] "Sleep Mode Enable"
+	 *                  =   0 (0x00) ...0.... "Normal mode"
+	 *    COMC[3:2] "Sensor sampling reset timing selection"
+	 *                  =   2 (0x02) ....10.. "Longer reset time"
+	 *    COMC[1:0] "Output drive current select"
+	 *                  =   0 (0x00) ......00 "Weakest"
+	 */
+	{ 0x09, 0x08 },
+
+	/*
+	 * 0C COMD "Common Control D"
+	 *                  =   8 (0x08) 00001000
+	 *    COMD[7] "Reserved"
+	 *                  =   0 (0x00) 0.......
+	 *    COMD[6] "Swap MSB and LSB at the output port"
+	 *                  =   0 (0x00) .0...... "False"
+	 *    COMD[5:3] "Reserved"
+	 *                  =   1 (0x01) ..001...
+	 *    COMD[2] "Output Average On Off"
+	 *                  =   0 (0x00) .....0.. "Output Normal"
+	 *    COMD[1] "Sensor precharge voltage selection"
+	 *                  =   0 (0x00) ......0. "Selects internal
+	 *                                         reference precharge
+	 *                                         voltage"
+	 *    COMD[0] "Snapshot option"
+	 *                  =   0 (0x00) .......0 "Enable live video output
+	 *                                         after snapshot sequence"
+	 */
+	{ 0x0c, 0x08 },
+
+	/*
+	 * 0D COME "Common Control E"
+	 *                  = 161 (0xA1) 10100001
+	 *    COME[7] "Output average option"
+	 *                  =   1 (0x01) 1....... "Output average of 4 pixels"
+	 *    COME[6] "Anti-blooming control"
+	 *                  =   0 (0x00) .0...... "Off"
+	 *    COME[5:3] "Reserved"
+	 *                  =   4 (0x04) ..100...
+	 *    COME[2] "Clock output power down pin status"
+	 *                  =   0 (0x00) .....0.. "Tri-state data output pin
+	 *                                         on power down"
+	 *    COME[1] "Data output pin status selection at power down"
+	 *                  =   0 (0x00) ......0. "Tri-state VSYNC, PCLK,
+	 *                                         HREF, and CHSYNC pins on
+	 *                                         power down"
+	 *    COME[0] "Auto zero circuit select"
+	 *                  =   1 (0x01) .......1 "On"
+	 */
+	{ 0x0d, 0xa1 },
+
+	/*
+	 * 0E COMF "Common Control F"
+	 *                  = 112 (0x70) 01110000
+	 *    COMF[7] "System clock selection"
+	 *                  =   0 (0x00) 0....... "Use 24 MHz system clock"
+	 *    COMF[6:4] "Reserved"
+	 *                  =   7 (0x07) .111....
+	 *    COMF[3] "Manual auto negative offset canceling selection"
+	 *                  =   0 (0x00) ....0... "Auto detect negative
+	 *                                         offset and cancel it"
+	 *    COMF[2:0] "Reserved"
+	 *                  =   0 (0x00) .....000
+	 */
+	{ 0x0e, 0x70 },
+
+	/*
+	 * 0F COMG "Common Control G"
+	 *                  =  66 (0x42) 01000010
+	 *    COMG[7] "Optical black output selection"
+	 *                  =   0 (0x00) 0....... "Disable"
+	 *    COMG[6] "Black level calibrate selection"
+	 *                  =   1 (0x01) .1...... "Use optical black pixels
+	 *                                         to calibrate"
+	 *    COMG[5:4] "Reserved"
+	 *                  =   0 (0x00) ..00....
+	 *    COMG[3] "Channel offset adjustment"
+	 *                  =   0 (0x00) ....0... "Disable offset adjustment"
+	 *    COMG[2] "ADC black level calibration option"
+	 *                  =   0 (0x00) .....0.. "Use B/G line and G/R
+	 *                                         line to calibrate each
+	 *                                         channel's black level"
+	 *    COMG[1] "Reserved"
+	 *                  =   1 (0x01) ......1.
+	 *    COMG[0] "ADC black level calibration enable"
+	 *                  =   0 (0x00) .......0 "Disable"
+	 */
+	{ 0x0f, 0x42 },
+
+	/*
+	 * 14 COMJ "Common Control J"
+	 *                  = 198 (0xC6) 11000110
+	 *    COMJ[7:6] "AGC gain ceiling"
+	 *                  =   3 (0x03) 11...... "8x"
+	 *    COMJ[5:4] "Reserved"
+	 *                  =   0 (0x00) ..00....
+	 *    COMJ[3] "Auto banding filter"
+	 *                  =   0 (0x00) ....0... "Banding filter is always
+	 *                                         on off depending on
+	 *                                         COMI[5] setting"
+	 *    COMJ[2] "VSYNC drop option"
+	 *                  =   1 (0x01) .....1.. "SYNC is dropped if frame
+	 *                                         data is dropped"
+	 *    COMJ[1] "Frame data drop"
+	 *                  =   1 (0x01) ......1. "Drop frame data if
+	 *                                         exposure is not within
+	 *                                         tolerance.  In AEC mode,
+	 *                                         data is normally dropped
+	 *                                         when data is out of
+	 *                                         range."
+	 *    COMJ[0] "Reserved"
+	 *                  =   0 (0x00) .......0
+	 */
+	{ 0x14, 0xc6 },
+
+	/*
+	 * 15 COMK "Common Control K"
+	 *                  =   2 (0x02) 00000010
+	 *    COMK[7] "CHSYNC pin output swap"
+	 *                  =   0 (0x00) 0....... "CHSYNC"
+	 *    COMK[6] "HREF pin output swap"
+	 *                  =   0 (0x00) .0...... "HREF"
+	 *    COMK[5] "PCLK output selection"
+	 *                  =   0 (0x00) ..0..... "PCLK always output"
+	 *    COMK[4] "PCLK edge selection"
+	 *                  =   0 (0x00) ...0.... "Data valid on falling edge"
+	 *    COMK[3] "HREF output polarity"
+	 *                  =   0 (0x00) ....0... "positive"
+	 *    COMK[2] "Reserved"
+	 *                  =   0 (0x00) .....0..
+	 *    COMK[1] "VSYNC polarity"
+	 *                  =   1 (0x01) ......1. "negative"
+	 *    COMK[0] "HSYNC polarity"
+	 *                  =   0 (0x00) .......0 "positive"
+	 */
+	{ 0x15, 0x02 },
+
+	/*
+	 * 33 CHLF "Current Control"
+	 *                  =   9 (0x09) 00001001
+	 *    CHLF[7:6] "Sensor current control"
+	 *                  =   0 (0x00) 00......
+	 *    CHLF[5] "Sensor current range control"
+	 *                  =   0 (0x00) ..0..... "normal range"
+	 *    CHLF[4] "Sensor current"
+	 *                  =   0 (0x00) ...0.... "normal current"
+	 *    CHLF[3] "Sensor buffer current control"
+	 *                  =   1 (0x01) ....1... "half current"
+	 *    CHLF[2] "Column buffer current control"
+	 *                  =   0 (0x00) .....0.. "normal current"
+	 *    CHLF[1] "Analog DSP current control"
+	 *                  =   0 (0x00) ......0. "normal current"
+	 *    CHLF[1] "ADC current control"
+	 *                  =   0 (0x00) ......0. "normal current"
+	 */
+	{ 0x33, 0x09 },
+
+	/*
+	 * 34 VBLM "Blooming Control"
+	 *                  =  80 (0x50) 01010000
+	 *    VBLM[7] "Hard soft reset switch"
+	 *                  =   0 (0x00) 0....... "Hard reset"
+	 *    VBLM[6:4] "Blooming voltage selection"
+	 *                  =   5 (0x05) .101....
+	 *    VBLM[3:0] "Sensor current control"
+	 *                  =   0 (0x00) ....0000
+	 */
+	{ 0x34, 0x50 },
+
+	/*
+	 * 36 VCHG "Sensor Precharge Voltage Control"
+	 *                  =   0 (0x00) 00000000
+	 *    VCHG[7] "Reserved"
+	 *                  =   0 (0x00) 0.......
+	 *    VCHG[6:4] "Sensor precharge voltage control"
+	 *                  =   0 (0x00) .000....
+	 *    VCHG[3:0] "Sensor array common reference"
+	 *                  =   0 (0x00) ....0000
+	 */
+	{ 0x36, 0x00 },
+
+	/*
+	 * 37 ADC "ADC Reference Control"
+	 *                  =   4 (0x04) 00000100
+	 *    ADC[7:4] "Reserved"
+	 *                  =   0 (0x00) 0000....
+	 *    ADC[3] "ADC input signal range"
+	 *                  =   0 (0x00) ....0... "Input signal 1.0x"
+	 *    ADC[2:0] "ADC range control"
+	 *                  =   4 (0x04) .....100
+	 */
+	{ 0x37, 0x04 },
+
+	/*
+	 * 38 ACOM "Analog Common Ground"
+	 *                  =  82 (0x52) 01010010
+	 *    ACOM[7] "Analog gain control"
+	 *                  =   0 (0x00) 0....... "Gain 1x"
+	 *    ACOM[6] "Analog black level calibration"
+	 *                  =   1 (0x01) .1...... "On"
+	 *    ACOM[5:0] "Reserved"
+	 *                  =  18 (0x12) ..010010
+	 */
+	{ 0x38, 0x52 },
+
+	/*
+	 * 3A FREFA "Internal Reference Adjustment"
+	 *                  =   0 (0x00) 00000000
+	 *    FREFA[7:0] "Range"
+	 *                  =   0 (0x00) 00000000
+	 */
+	{ 0x3a, 0x00 },
+
+	/*
+	 * 3C FVOPT "Internal Reference Adjustment"
+	 *                  =  31 (0x1F) 00011111
+	 *    FVOPT[7:0] "Range"
+	 *                  =  31 (0x1F) 00011111
+	 */
+	{ 0x3c, 0x1f },
+
+	/*
+	 * 44 Undocumented  =   0 (0x00) 00000000
+	 *    44[7:0] "It's a secret"
+	 *                  =   0 (0x00) 00000000
+	 */
+	{ 0x44, 0x00 },
+
+	/*
+	 * 40 Undocumented  =   0 (0x00) 00000000
+	 *    40[7:0] "It's a secret"
+	 *                  =   0 (0x00) 00000000
+	 */
+	{ 0x40, 0x00 },
+
+	/*
+	 * 41 Undocumented  =   0 (0x00) 00000000
+	 *    41[7:0] "It's a secret"
+	 *                  =   0 (0x00) 00000000
+	 */
+	{ 0x41, 0x00 },
+
+	/*
+	 * 42 Undocumented  =   0 (0x00) 00000000
+	 *    42[7:0] "It's a secret"
+	 *                  =   0 (0x00) 00000000
+	 */
+	{ 0x42, 0x00 },
+
+	/*
+	 * 43 Undocumented  =   0 (0x00) 00000000
+	 *    43[7:0] "It's a secret"
+	 *                  =   0 (0x00) 00000000
+	 */
+	{ 0x43, 0x00 },
+
+	/*
+	 * 45 Undocumented  = 128 (0x80) 10000000
+	 *    45[7:0] "It's a secret"
+	 *                  = 128 (0x80) 10000000
+	 */
+	{ 0x45, 0x80 },
+
+	/*
+	 * 48 Undocumented  = 192 (0xC0) 11000000
+	 *    48[7:0] "It's a secret"
+	 *                  = 192 (0xC0) 11000000
+	 */
+	{ 0x48, 0xc0 },
+
+	/*
+	 * 49 Undocumented  =  25 (0x19) 00011001
+	 *    49[7:0] "It's a secret"
+	 *                  =  25 (0x19) 00011001
+	 */
+	{ 0x49, 0x19 },
+
+	/*
+	 * 4B Undocumented  = 128 (0x80) 10000000
+	 *    4B[7:0] "It's a secret"
+	 *                  = 128 (0x80) 10000000
+	 */
+	{ 0x4b, 0x80 },
+
+	/*
+	 * 4D Undocumented  = 196 (0xC4) 11000100
+	 *    4D[7:0] "It's a secret"
+	 *                  = 196 (0xC4) 11000100
+	 */
+	{ 0x4d, 0xc4 },
+
+	/*
+	 * 35 VREF "Reference Voltage Control"
+	 *                  =  76 (0x4c) 01001100
+	 *    VREF[7:5] "Column high reference control"
+	 *                  =   2 (0x02) 010..... "higher voltage"
+	 *    VREF[4:2] "Column low reference control"
+	 *                  =   3 (0x03) ...011.. "Highest voltage"
+	 *    VREF[1:0] "Reserved"
+	 *                  =   0 (0x00) ......00
+	 */
+	{ 0x35, 0x4c },
+
+	/*
+	 * 3D Undocumented  =   0 (0x00) 00000000
+	 *    3D[7:0] "It's a secret"
+	 *                  =   0 (0x00) 00000000
+	 */
+	{ 0x3d, 0x00 },
+
+	/*
+	 * 3E Undocumented  =   0 (0x00) 00000000
+	 *    3E[7:0] "It's a secret"
+	 *                  =   0 (0x00) 00000000
+	 */
+	{ 0x3e, 0x00 },
+
+	/*
+	 * 3B FREFB "Internal Reference Adjustment"
+	 *                  =  24 (0x18) 00011000
+	 *    FREFB[7:0] "Range"
+	 *                  =  24 (0x18) 00011000
+	 */
+	{ 0x3b, 0x18 },
+
+	/*
+	 * 33 CHLF "Current Control"
+	 *                  =  25 (0x19) 00011001
+	 *    CHLF[7:6] "Sensor current control"
+	 *                  =   0 (0x00) 00......
+	 *    CHLF[5] "Sensor current range control"
+	 *                  =   0 (0x00) ..0..... "normal range"
+	 *    CHLF[4] "Sensor current"
+	 *                  =   1 (0x01) ...1.... "double current"
+	 *    CHLF[3] "Sensor buffer current control"
+	 *                  =   1 (0x01) ....1... "half current"
+	 *    CHLF[2] "Column buffer current control"
+	 *                  =   0 (0x00) .....0.. "normal current"
+	 *    CHLF[1] "Analog DSP current control"
+	 *                  =   0 (0x00) ......0. "normal current"
+	 *    CHLF[1] "ADC current control"
+	 *                  =   0 (0x00) ......0. "normal current"
+	 */
+	{ 0x33, 0x19 },
+
+	/*
+	 * 34 VBLM "Blooming Control"
+	 *                  =  90 (0x5A) 01011010
+	 *    VBLM[7] "Hard soft reset switch"
+	 *                  =   0 (0x00) 0....... "Hard reset"
+	 *    VBLM[6:4] "Blooming voltage selection"
+	 *                  =   5 (0x05) .101....
+	 *    VBLM[3:0] "Sensor current control"
+	 *                  =  10 (0x0A) ....1010
+	 */
+	{ 0x34, 0x5a },
+
+	/*
+	 * 3B FREFB "Internal Reference Adjustment"
+	 *                  =   0 (0x00) 00000000
+	 *    FREFB[7:0] "Range"
+	 *                  =   0 (0x00) 00000000
+	 */
+	{ 0x3b, 0x00 },
+
+	/*
+	 * 33 CHLF "Current Control"
+	 *                  =   9 (0x09) 00001001
+	 *    CHLF[7:6] "Sensor current control"
+	 *                  =   0 (0x00) 00......
+	 *    CHLF[5] "Sensor current range control"
+	 *                  =   0 (0x00) ..0..... "normal range"
+	 *    CHLF[4] "Sensor current"
+	 *                  =   0 (0x00) ...0.... "normal current"
+	 *    CHLF[3] "Sensor buffer current control"
+	 *                  =   1 (0x01) ....1... "half current"
+	 *    CHLF[2] "Column buffer current control"
+	 *                  =   0 (0x00) .....0.. "normal current"
+	 *    CHLF[1] "Analog DSP current control"
+	 *                  =   0 (0x00) ......0. "normal current"
+	 *    CHLF[1] "ADC current control"
+	 *                  =   0 (0x00) ......0. "normal current"
+	 */
+	{ 0x33, 0x09 },
+
+	/*
+	 * 34 VBLM "Blooming Control"
+	 *                  =  80 (0x50) 01010000
+	 *    VBLM[7] "Hard soft reset switch"
+	 *                  =   0 (0x00) 0....... "Hard reset"
+	 *    VBLM[6:4] "Blooming voltage selection"
+	 *                  =   5 (0x05) .101....
+	 *    VBLM[3:0] "Sensor current control"
+	 *                  =   0 (0x00) ....0000
+	 */
+	{ 0x34, 0x50 },
+
+	/*
+	 * 12 COMH "Common Control H"
+	 *                  =  64 (0x40) 01000000
+	 *    COMH[7] "SRST"
+	 *                  =   0 (0x00) 0....... "No-op"
+	 *    COMH[6:4] "Resolution selection"
+	 *                  =   4 (0x04) .100.... "XGA"
+	 *    COMH[3] "Master slave selection"
+	 *                  =   0 (0x00) ....0... "Master mode"
+	 *    COMH[2] "Internal B/R channel option"
+	 *                  =   0 (0x00) .....0.. "B/R use same channel"
+	 *    COMH[1] "Color bar test pattern"
+	 *                  =   0 (0x00) ......0. "Off"
+	 *    COMH[0] "Reserved"
+	 *                  =   0 (0x00) .......0
+	 */
+	{ 0x12, 0x40 },
+
+	/*
+	 * 17 HREFST "Horizontal window start"
+	 *                  =  31 (0x1F) 00011111
+	 *    HREFST[7:0] "Horizontal window start, 8 MSBs"
+	 *                  =  31 (0x1F) 00011111
+	 */
+	{ 0x17, 0x1f },
+
+	/*
+	 * 18 HREFEND "Horizontal window end"
+	 *                  =  95 (0x5F) 01011111
+	 *    HREFEND[7:0] "Horizontal Window End, 8 MSBs"
+	 *                  =  95 (0x5F) 01011111
+	 */
+	{ 0x18, 0x5f },
+
+	/*
+	 * 19 VSTRT "Vertical window start"
+	 *                  =   0 (0x00) 00000000
+	 *    VSTRT[7:0] "Vertical Window Start, 8 MSBs"
+	 *                  =   0 (0x00) 00000000
+	 */
+	{ 0x19, 0x00 },
+
+	/*
+	 * 1A VEND "Vertical window end"
+	 *                  =  96 (0x60) 01100000
+	 *    VEND[7:0] "Vertical Window End, 8 MSBs"
+	 *                  =  96 (0x60) 01100000
+	 */
+	{ 0x1a, 0x60 },
+
+	/*
+	 * 32 COMM "Common Control M"
+	 *                  =  18 (0x12) 00010010
+	 *    COMM[7:6] "Pixel clock divide option"
+	 *                  =   0 (0x00) 00...... "/1"
+	 *    COMM[5:3] "Horizontal window end position, 3 LSBs"
+	 *                  =   2 (0x02) ..010...
+	 *    COMM[2:0] "Horizontal window start position, 3 LSBs"
+	 *                  =   2 (0x02) .....010
+	 */
+	{ 0x32, 0x12 },
+
+	/*
+	 * 03 COMA "Common Control A"
+	 *                  =  74 (0x4A) 01001010
+	 *    COMA[7:4] "AWB Update Threshold"
+	 *                  =   4 (0x04) 0100....
+	 *    COMA[3:2] "Vertical window end line control 2 LSBs"
+	 *                  =   2 (0x02) ....10..
+	 *    COMA[1:0] "Vertical window start line control 2 LSBs"
+	 *                  =   2 (0x02) ......10
+	 */
+	{ 0x03, 0x4a },
+
+	/*
+	 * 11 CLKRC "Clock Rate Control"
+	 *                  = 128 (0x80) 10000000
+	 *    CLKRC[7] "Internal frequency doublers on off seclection"
+	 *                  =   1 (0x01) 1....... "On"
+	 *    CLKRC[6] "Digital video master slave selection"
+	 *                  =   0 (0x00) .0...... "Master mode, sensor
+	 *                                         provides PCLK"
+	 *    CLKRC[5:0] "Clock divider { CLK = PCLK/(1+CLKRC[5:0]) }"
+	 *                  =   0 (0x00) ..000000
+	 */
+	{ 0x11, 0x80 },
+
+	/*
+	 * 12 COMH "Common Control H"
+	 *                  =   0 (0x00) 00000000
+	 *    COMH[7] "SRST"
+	 *                  =   0 (0x00) 0....... "No-op"
+	 *    COMH[6:4] "Resolution selection"
+	 *                  =   0 (0x00) .000.... "QXGA"
+	 *    COMH[3] "Master slave selection"
+	 *                  =   0 (0x00) ....0... "Master mode"
+	 *    COMH[2] "Internal B/R channel option"
+	 *                  =   0 (0x00) .....0.. "B/R use same channel"
+	 *    COMH[1] "Color bar test pattern"
+	 *                  =   0 (0x00) ......0. "Off"
+	 *    COMH[0] "Reserved"
+	 *                  =   0 (0x00) .......0
+	 */
+	{ 0x12, 0x00 },
+
+	/*
+	 * 12 COMH "Common Control H"
+	 *                  =  64 (0x40) 01000000
+	 *    COMH[7] "SRST"
+	 *                  =   0 (0x00) 0....... "No-op"
+	 *    COMH[6:4] "Resolution selection"
+	 *                  =   4 (0x04) .100.... "XGA"
+	 *    COMH[3] "Master slave selection"
+	 *                  =   0 (0x00) ....0... "Master mode"
+	 *    COMH[2] "Internal B/R channel option"
+	 *                  =   0 (0x00) .....0.. "B/R use same channel"
+	 *    COMH[1] "Color bar test pattern"
+	 *                  =   0 (0x00) ......0. "Off"
+	 *    COMH[0] "Reserved"
+	 *                  =   0 (0x00) .......0
+	 */
+	{ 0x12, 0x40 },
+
+	/*
+	 * 17 HREFST "Horizontal window start"
+	 *                  =  31 (0x1F) 00011111
+	 *    HREFST[7:0] "Horizontal window start, 8 MSBs"
+	 *                  =  31 (0x1F) 00011111
+	 */
+	{ 0x17, 0x1f },
+
+	/*
+	 * 18 HREFEND "Horizontal window end"
+	 *                  =  95 (0x5F) 01011111
+	 *    HREFEND[7:0] "Horizontal Window End, 8 MSBs"
+	 *                  =  95 (0x5F) 01011111
+	 */
+	{ 0x18, 0x5f },
+
+	/*
+	 * 19 VSTRT "Vertical window start"
+	 *                  =   0 (0x00) 00000000
+	 *    VSTRT[7:0] "Vertical Window Start, 8 MSBs"
+	 *                  =   0 (0x00) 00000000
+	 */
+	{ 0x19, 0x00 },
+
+	/*
+	 * 1A VEND "Vertical window end"
+	 *                  =  96 (0x60) 01100000
+	 *    VEND[7:0] "Vertical Window End, 8 MSBs"
+	 *                  =  96 (0x60) 01100000
+	 */
+	{ 0x1a, 0x60 },
+
+	/*
+	 * 32 COMM "Common Control M"
+	 *                  =  18 (0x12) 00010010
+	 *    COMM[7:6] "Pixel clock divide option"
+	 *                  =   0 (0x00) 00...... "/1"
+	 *    COMM[5:3] "Horizontal window end position, 3 LSBs"
+	 *                  =   2 (0x02) ..010...
+	 *    COMM[2:0] "Horizontal window start position, 3 LSBs"
+	 *                  =   2 (0x02) .....010
+	 */
+	{ 0x32, 0x12 },
+
+	/*
+	 * 03 COMA "Common Control A"
+	 *                  =  74 (0x4A) 01001010
+	 *    COMA[7:4] "AWB Update Threshold"
+	 *                  =   4 (0x04) 0100....
+	 *    COMA[3:2] "Vertical window end line control 2 LSBs"
+	 *                  =   2 (0x02) ....10..
+	 *    COMA[1:0] "Vertical window start line control 2 LSBs"
+	 *                  =   2 (0x02) ......10
+	 */
+	{ 0x03, 0x4a },
+
+	/*
+	 * 02 RED "Red Gain Control"
+	 *                  = 175 (0xAF) 10101111
+	 *    RED[7] "Action"
+	 *                  =   1 (0x01) 1....... "gain = 1/(1+bitrev([6:0]))"
+	 *    RED[6:0] "Value"
+	 *                  =  47 (0x2F) .0101111
+	 */
+	{ 0x02, 0xaf },
+
+	/*
+	 * 2D ADDVSL "VSYNC Pulse Width"
+	 *                  = 210 (0xD2) 11010010
+	 *    ADDVSL[7:0] "VSYNC pulse width, LSB"
+	 *                  = 210 (0xD2) 11010010
+	 */
+	{ 0x2d, 0xd2 },
+
+	/*
+	 * 00 GAIN          =  24 (0x18) 00011000
+	 *    GAIN[7:6] "Reserved"
+	 *                  =   0 (0x00) 00......
+	 *    GAIN[5] "Double"
+	 *                  =   0 (0x00) ..0..... "False"
+	 *    GAIN[4] "Double"
+	 *                  =   1 (0x01) ...1.... "True"
+	 *    GAIN[3:0] "Range"
+	 *                  =   8 (0x08) ....1000
+	 */
+	{ 0x00, 0x18 },
+
+	/*
+	 * 01 BLUE "Blue Gain Control"
+	 *                  = 240 (0xF0) 11110000
+	 *    BLUE[7] "Action"
+	 *                  =   1 (0x01) 1....... "gain = 1/(1+bitrev([6:0]))"
+	 *    BLUE[6:0] "Value"
+	 *                  = 112 (0x70) .1110000
+	 */
+	{ 0x01, 0xf0 },
+
+	/*
+	 * 10 AEC "Automatic Exposure Control"
+	 *                  =  10 (0x0A) 00001010
+	 *    AEC[7:0] "Automatic Exposure Control, 8 MSBs"
+	 *                  =  10 (0x0A) 00001010
+	 */
+	{ 0x10, 0x0a },
+
+	{ 0xe1, 0x67 },
+	{ 0xe3, 0x03 },
+	{ 0xe4, 0x26 },
+	{ 0xe5, 0x3e },
+	{ 0xf8, 0x01 },
+	{ 0xff, 0x01 },
+};
+
+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, 0x85 },
+	{ 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, 0xa0 }, /* Was 0x20, bit7 enables a 2x gain which we need */
+	{ 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 },
+};
+
+/* 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[] = {
+	{ 0x12, 0x80 },		/* reset */
+	{ 0x00, 0x00 },		/* gain */
+	{ 0x01, 0x80 },		/* blue gain */
+	{ 0x02, 0x80 },		/* red gain */
+	{ 0x03, 0xc0 },		/* OV7670_R03_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, 0x22 }, /* Was 0x20, bit1 enables a 2x gain which we need */
+	{ 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 },
+};
+
+static const struct ov_regvals init_519_ov7660[] = {
+	{ 0x5d,	0x03 }, /* Turn off suspend mode */
+	{ 0x53,	0x9b }, /* 0x9f enables the (unused) microcontroller */
+	{ 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 },	/* 0x0d does U <-> V swap */
+	{ 0x21,	0x38 },
+	{ 0x22,	0x1d },
+	{ 0x17,	0x50 }, /* undocumented */
+	{ 0x37,	0x00 }, /* undocumented */
+	{ 0x40,	0xff }, /* I2C timeout counter */
+	{ 0x46,	0x00 }, /* I2C clock prescaler */
+};
+static const struct ov_i2c_regvals norm_7660[] = {
+	{OV7670_R12_COM7, OV7670_COM7_RESET},
+	{OV7670_R11_CLKRC, 0x81},
+	{0x92, 0x00},			/* DM_LNL */
+	{0x93, 0x00},			/* DM_LNH */
+	{0x9d, 0x4c},			/* BD50ST */
+	{0x9e, 0x3f},			/* BD60ST */
+	{OV7670_R3B_COM11, 0x02},
+	{OV7670_R13_COM8, 0xf5},
+	{OV7670_R10_AECH, 0x00},
+	{OV7670_R00_GAIN, 0x00},
+	{OV7670_R01_BLUE, 0x7c},
+	{OV7670_R02_RED, 0x9d},
+	{OV7670_R12_COM7, 0x00},
+	{OV7670_R04_COM1, 00},
+	{OV7670_R18_HSTOP, 0x01},
+	{OV7670_R17_HSTART, 0x13},
+	{OV7670_R32_HREF, 0x92},
+	{OV7670_R19_VSTART, 0x02},
+	{OV7670_R1A_VSTOP, 0x7a},
+	{OV7670_R03_VREF, 0x00},
+	{OV7670_R0E_COM5, 0x04},
+	{OV7670_R0F_COM6, 0x62},
+	{OV7670_R15_COM10, 0x00},
+	{0x16, 0x02},			/* RSVD */
+	{0x1b, 0x00},			/* PSHFT */
+	{OV7670_R1E_MVFP, 0x01},
+	{0x29, 0x3c},			/* RSVD */
+	{0x33, 0x00},			/* CHLF */
+	{0x34, 0x07},			/* ARBLM */
+	{0x35, 0x84},			/* RSVD */
+	{0x36, 0x00},			/* RSVD */
+	{0x37, 0x04},			/* ADC */
+	{0x39, 0x43},			/* OFON */
+	{OV7670_R3A_TSLB, 0x00},
+	{OV7670_R3C_COM12, 0x6c},
+	{OV7670_R3D_COM13, 0x98},
+	{OV7670_R3F_EDGE, 0x23},
+	{OV7670_R40_COM15, 0xc1},
+	{OV7670_R41_COM16, 0x22},
+	{0x6b, 0x0a},			/* DBLV */
+	{0xa1, 0x08},			/* RSVD */
+	{0x69, 0x80},			/* HV */
+	{0x43, 0xf0},			/* RSVD.. */
+	{0x44, 0x10},
+	{0x45, 0x78},
+	{0x46, 0xa8},
+	{0x47, 0x60},
+	{0x48, 0x80},
+	{0x59, 0xba},
+	{0x5a, 0x9a},
+	{0x5b, 0x22},
+	{0x5c, 0xb9},
+	{0x5d, 0x9b},
+	{0x5e, 0x10},
+	{0x5f, 0xe0},
+	{0x60, 0x85},
+	{0x61, 0x60},
+	{0x9f, 0x9d},			/* RSVD */
+	{0xa0, 0xa0},			/* DSPC2 */
+	{0x4f, 0x60},			/* matrix */
+	{0x50, 0x64},
+	{0x51, 0x04},
+	{0x52, 0x18},
+	{0x53, 0x3c},
+	{0x54, 0x54},
+	{0x55, 0x40},
+	{0x56, 0x40},
+	{0x57, 0x40},
+	{0x58, 0x0d},			/* matrix sign */
+	{0x8b, 0xcc},			/* RSVD */
+	{0x8c, 0xcc},
+	{0x8d, 0xcf},
+	{0x6c, 0x40},			/* gamma curve */
+	{0x6d, 0xe0},
+	{0x6e, 0xa0},
+	{0x6f, 0x80},
+	{0x70, 0x70},
+	{0x71, 0x80},
+	{0x72, 0x60},
+	{0x73, 0x60},
+	{0x74, 0x50},
+	{0x75, 0x40},
+	{0x76, 0x38},
+	{0x77, 0x3c},
+	{0x78, 0x32},
+	{0x79, 0x1a},
+	{0x7a, 0x28},
+	{0x7b, 0x24},
+	{0x7c, 0x04},			/* gamma curve */
+	{0x7d, 0x12},
+	{0x7e, 0x26},
+	{0x7f, 0x46},
+	{0x80, 0x54},
+	{0x81, 0x64},
+	{0x82, 0x70},
+	{0x83, 0x7c},
+	{0x84, 0x86},
+	{0x85, 0x8e},
+	{0x86, 0x9c},
+	{0x87, 0xab},
+	{0x88, 0xc4},
+	{0x89, 0xd1},
+	{0x8a, 0xe5},
+	{OV7670_R14_COM9, 0x1e},
+	{OV7670_R24_AEW, 0x80},
+	{OV7670_R25_AEB, 0x72},
+	{OV7670_R26_VPT, 0xb3},
+	{0x62, 0x80},			/* LCC1 */
+	{0x63, 0x80},			/* LCC2 */
+	{0x64, 0x06},			/* LCC3 */
+	{0x65, 0x00},			/* LCC4 */
+	{0x66, 0x01},			/* LCC5 */
+	{0x94, 0x0e},			/* RSVD.. */
+	{0x95, 0x14},
+	{OV7670_R13_COM8, OV7670_COM8_FASTAEC
+			| OV7670_COM8_AECSTEP
+			| OV7670_COM8_BFILT
+			| 0x10
+			| OV7670_COM8_AGC
+			| OV7670_COM8_AWB
+			| OV7670_COM8_AEC},
+	{0xa1, 0xc8}
+};
+static const struct ov_i2c_regvals norm_9600[] = {
+	{0x12, 0x80},
+	{0x0c, 0x28},
+	{0x11, 0x80},
+	{0x13, 0xb5},
+	{0x14, 0x3e},
+	{0x1b, 0x04},
+	{0x24, 0xb0},
+	{0x25, 0x90},
+	{0x26, 0x94},
+	{0x35, 0x90},
+	{0x37, 0x07},
+	{0x38, 0x08},
+	{0x01, 0x8e},
+	{0x02, 0x85}
+};
+
+/* 7670. Defaults taken from OmniVision provided data,
+*  as provided by Jonathan Corbet of OLPC		*/
+static const struct ov_i2c_regvals norm_7670[] = {
+	{ OV7670_R12_COM7, OV7670_COM7_RESET },
+	{ OV7670_R3A_TSLB, 0x04 },		/* OV */
+	{ OV7670_R12_COM7, OV7670_COM7_FMT_VGA }, /* VGA */
+	{ OV7670_R11_CLKRC, 0x01 },
+/*
+ * Set the hardware window.  These values from OV don't entirely
+ * make sense - hstop is less than hstart.  But they work...
+ */
+	{ OV7670_R17_HSTART, 0x13 },
+	{ OV7670_R18_HSTOP, 0x01 },
+	{ OV7670_R32_HREF, 0xb6 },
+	{ OV7670_R19_VSTART, 0x02 },
+	{ OV7670_R1A_VSTOP, 0x7a },
+	{ OV7670_R03_VREF, 0x0a },
+
+	{ OV7670_R0C_COM3, 0x00 },
+	{ OV7670_R3E_COM14, 0x00 },
+/* Mystery scaling numbers */
+	{ 0x70, 0x3a },
+	{ 0x71, 0x35 },
+	{ 0x72, 0x11 },
+	{ 0x73, 0xf0 },
+	{ 0xa2, 0x02 },
+/*	{ OV7670_R15_COM10, 0x0 }, */
+
+/* Gamma curve values */
+	{ 0x7a, 0x20 },
+	{ 0x7b, 0x10 },
+	{ 0x7c, 0x1e },
+	{ 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_R13_COM8, OV7670_COM8_FASTAEC
+			 | OV7670_COM8_AECSTEP
+			 | OV7670_COM8_BFILT },
+	{ OV7670_R00_GAIN, 0x00 },
+	{ OV7670_R10_AECH, 0x00 },
+	{ OV7670_R0D_COM4, 0x40 }, /* magic reserved bit */
+	{ OV7670_R14_COM9, 0x18 }, /* 4x gain + magic rsvd bit */
+	{ OV7670_RA5_BD50MAX, 0x05 },
+	{ OV7670_RAB_BD60MAX, 0x07 },
+	{ OV7670_R24_AEW, 0x95 },
+	{ OV7670_R25_AEB, 0x33 },
+	{ OV7670_R26_VPT, 0xe3 },
+	{ OV7670_R9F_HAECC1, 0x78 },
+	{ OV7670_RA0_HAECC2, 0x68 },
+	{ 0xa1, 0x03 }, /* magic */
+	{ OV7670_RA6_HAECC3, 0xd8 },
+	{ OV7670_RA7_HAECC4, 0xd8 },
+	{ OV7670_RA8_HAECC5, 0xf0 },
+	{ OV7670_RA9_HAECC6, 0x90 },
+	{ OV7670_RAA_HAECC7, 0x94 },
+	{ OV7670_R13_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_R0E_COM5, 0x61 },
+	{ OV7670_R0F_COM6, 0x4b },
+	{ 0x16, 0x02 },
+	{ OV7670_R1E_MVFP, 0x07 },
+	{ 0x21, 0x02 },
+	{ 0x22, 0x91 },
+	{ 0x29, 0x07 },
+	{ 0x33, 0x0b },
+	{ 0x35, 0x0b },
+	{ 0x37, 0x1d },
+	{ 0x38, 0x71 },
+	{ 0x39, 0x2a },
+	{ OV7670_R3C_COM12, 0x78 },
+	{ 0x4d, 0x40 },
+	{ 0x4e, 0x20 },
+	{ OV7670_R69_GFIX, 0x00 },
+	{ 0x6b, 0x4a },
+	{ 0x74, 0x10 },
+	{ 0x8d, 0x4f },
+	{ 0x8e, 0x00 },
+	{ 0x8f, 0x00 },
+	{ 0x90, 0x00 },
+	{ 0x91, 0x00 },
+	{ 0x96, 0x00 },
+	{ 0x9a, 0x00 },
+	{ 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_R01_BLUE, 0x40 },
+	{ OV7670_R02_RED, 0x60 },
+	{ OV7670_R13_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, 0x00 },
+	{ 0x52, 0x22 },
+	{ 0x53, 0x5e },
+	{ 0x54, 0x80 },
+	{ 0x58, 0x9e },
+
+	{ OV7670_R41_COM16, OV7670_COM16_AWBGAIN },
+	{ OV7670_R3F_EDGE, 0x00 },
+	{ 0x75, 0x05 },
+	{ 0x76, 0xe1 },
+	{ 0x4c, 0x00 },
+	{ 0x77, 0x01 },
+	{ OV7670_R3D_COM13, OV7670_COM13_GAMMA
+			  | OV7670_COM13_UVSAT
+			  | 2},		/* was 3 */
+	{ 0x4b, 0x09 },
+	{ 0xc9, 0x60 },
+	{ OV7670_R41_COM16, 0x38 },
+	{ 0x56, 0x40 },
+
+	{ 0x34, 0x11 },
+	{ OV7670_R3B_COM11, OV7670_COM11_EXP|OV7670_COM11_HZAUTO },
+	{ 0xa4, 0x88 },
+	{ 0x96, 0x00 },
+	{ 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 },
+};
+
+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 */
+};
+
+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 void reg_w(struct sd *sd, u16 index, u16 value)
+{
+	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
+	int ret, req = 0;
+
+	if (sd->gspca_dev.usb_err < 0)
+		return;
+
+	switch (sd->bridge) {
+	case BRIDGE_OV511:
+	case BRIDGE_OV511PLUS:
+		req = 2;
+		break;
+	case BRIDGE_OVFX2:
+		req = 0x0a;
+		/* fall through */
+	case BRIDGE_W9968CF:
+		PDEBUG(D_USBO, "SET %02x %04x %04x",
+				req, value, index);
+		ret = usb_control_msg(sd->gspca_dev.dev,
+			usb_sndctrlpipe(sd->gspca_dev.dev, 0),
+			req,
+			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			value, index, NULL, 0, 500);
+		goto leave;
+	default:
+		req = 1;
+	}
+
+	PDEBUG(D_USBO, "SET %02x 0000 %04x %02x",
+			req, index, value);
+	sd->gspca_dev.usb_buf[0] = value;
+	ret = usb_control_msg(sd->gspca_dev.dev,
+			usb_sndctrlpipe(sd->gspca_dev.dev, 0),
+			req,
+			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			0, index,
+			sd->gspca_dev.usb_buf, 1, 500);
+leave:
+	if (ret < 0) {
+		PERR("reg_w %02x failed %d\n", index, ret);
+		sd->gspca_dev.usb_err = ret;
+		return;
+	}
+}
+
+/* Read from a OV519 register, note not valid for the w9968cf!! */
+/* returns: negative is error, pos or zero is data */
+static int reg_r(struct sd *sd, u16 index)
+{
+	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
+	int ret;
+	int req;
+
+	if (sd->gspca_dev.usb_err < 0)
+		return -1;
+
+	switch (sd->bridge) {
+	case BRIDGE_OV511:
+	case BRIDGE_OV511PLUS:
+		req = 3;
+		break;
+	case BRIDGE_OVFX2:
+		req = 0x0b;
+		break;
+	default:
+		req = 1;
+	}
+
+	ret = usb_control_msg(sd->gspca_dev.dev,
+			usb_rcvctrlpipe(sd->gspca_dev.dev, 0),
+			req,
+			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];
+		PDEBUG(D_USBI, "GET %02x 0000 %04x %02x",
+			req, index, ret);
+	} else {
+		PERR("reg_r %02x failed %d\n", index, ret);
+		sd->gspca_dev.usb_err = ret;
+	}
+
+	return ret;
+}
+
+/* Read 8 values from a OV519 register */
+static int reg_r8(struct sd *sd,
+		  u16 index)
+{
+	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
+	int ret;
+
+	if (sd->gspca_dev.usb_err < 0)
+		return -1;
+
+	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 {
+		PERR("reg_r8 %02x failed %d\n", index, ret);
+		sd->gspca_dev.usb_err = ret;
+	}
+
+	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 void 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;
+
+		oldval = ret & ~mask;		/* Clear the masked bits */
+		value |= oldval;		/* Set the desired bits */
+	}
+	reg_w(sd, index, value);
+}
+
+/*
+ * Writes multiple (n) byte value to a single register. Only valid with certain
+ * registers (0x30 and 0xc4 - 0xce).
+ */
+static void ov518_reg_w32(struct sd *sd, u16 index, u32 value, int n)
+{
+	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
+	int ret;
+
+	if (sd->gspca_dev.usb_err < 0)
+		return;
+
+	*((__le32 *) sd->gspca_dev.usb_buf) = __cpu_to_le32(value);
+
+	ret = usb_control_msg(sd->gspca_dev.dev,
+			usb_sndctrlpipe(sd->gspca_dev.dev, 0),
+			1 /* REG_IO */,
+			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			0, index,
+			sd->gspca_dev.usb_buf, n, 500);
+	if (ret < 0) {
+		PERR("reg_w32 %02x failed %d\n", index, ret);
+		sd->gspca_dev.usb_err = ret;
+	}
+}
+
+static void ov511_i2c_w(struct sd *sd, u8 reg, u8 value)
+{
+	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
+	int rc, retries;
+
+	PDEBUG(D_USBO, "ov511_i2c_w %02x %02x", reg, value);
+
+	/* Three byte write cycle */
+	for (retries = 6; ; ) {
+		/* Select camera register */
+		reg_w(sd, R51x_I2C_SADDR_3, reg);
+
+		/* Write "value" to I2C data port of OV511 */
+		reg_w(sd, R51x_I2C_DATA, value);
+
+		/* Initiate 3-byte write cycle */
+		reg_w(sd, R511_I2C_CTL, 0x01);
+
+		do {
+			rc = reg_r(sd, R511_I2C_CTL);
+		} while (rc > 0 && ((rc & 1) == 0)); /* Retry until idle */
+
+		if (rc < 0)
+			return;
+
+		if ((rc & 2) == 0) /* Ack? */
+			break;
+		if (--retries < 0) {
+			PDEBUG(D_USBO, "i2c write retries exhausted");
+			return;
+		}
+	}
+}
+
+static int ov511_i2c_r(struct sd *sd, u8 reg)
+{
+	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
+	int rc, value, retries;
+
+	/* Two byte write cycle */
+	for (retries = 6; ; ) {
+		/* Select camera register */
+		reg_w(sd, R51x_I2C_SADDR_2, reg);
+
+		/* Initiate 2-byte write cycle */
+		reg_w(sd, R511_I2C_CTL, 0x03);
+
+		do {
+			rc = reg_r(sd, R511_I2C_CTL);
+		} while (rc > 0 && ((rc & 1) == 0)); /* Retry until idle */
+
+		if (rc < 0)
+			return rc;
+
+		if ((rc & 2) == 0) /* Ack? */
+			break;
+
+		/* I2C abort */
+		reg_w(sd, R511_I2C_CTL, 0x10);
+
+		if (--retries < 0) {
+			PDEBUG(D_USBI, "i2c write retries exhausted");
+			return -1;
+		}
+	}
+
+	/* Two byte read cycle */
+	for (retries = 6; ; ) {
+		/* Initiate 2-byte read cycle */
+		reg_w(sd, R511_I2C_CTL, 0x05);
+
+		do {
+			rc = reg_r(sd, R511_I2C_CTL);
+		} while (rc > 0 && ((rc & 1) == 0)); /* Retry until idle */
+
+		if (rc < 0)
+			return rc;
+
+		if ((rc & 2) == 0) /* Ack? */
+			break;
+
+		/* I2C abort */
+		reg_w(sd, R511_I2C_CTL, 0x10);
+
+		if (--retries < 0) {
+			PDEBUG(D_USBI, "i2c read retries exhausted");
+			return -1;
+		}
+	}
+
+	value = reg_r(sd, R51x_I2C_DATA);
+
+	PDEBUG(D_USBI, "ov511_i2c_r %02x %02x", reg, value);
+
+	/* This is needed to make i2c_w() work */
+	reg_w(sd, R511_I2C_CTL, 0x05);
+
+	return 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 void ov518_i2c_w(struct sd *sd,
+		u8 reg,
+		u8 value)
+{
+	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
+
+	PDEBUG(D_USBO, "ov518_i2c_w %02x %02x", reg, value);
+
+	/* Select camera register */
+	reg_w(sd, R51x_I2C_SADDR_3, reg);
+
+	/* Write "value" to I2C data port of OV511 */
+	reg_w(sd, R51x_I2C_DATA, value);
+
+	/* Initiate 3-byte write cycle */
+	reg_w(sd, R518_I2C_CTL, 0x01);
+
+	/* wait for write complete */
+	msleep(4);
+	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 ov518_i2c_r(struct sd *sd, u8 reg)
+{
+	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
+	int value;
+
+	/* Select camera register */
+	reg_w(sd, R51x_I2C_SADDR_2, reg);
+
+	/* Initiate 2-byte write cycle */
+	reg_w(sd, R518_I2C_CTL, 0x03);
+	reg_r8(sd, R518_I2C_CTL);
+
+	/* Initiate 2-byte read cycle */
+	reg_w(sd, R518_I2C_CTL, 0x05);
+	reg_r8(sd, R518_I2C_CTL);
+
+	value = reg_r(sd, R51x_I2C_DATA);
+	PDEBUG(D_USBI, "ov518_i2c_r %02x %02x", reg, value);
+	return value;
+}
+
+static void ovfx2_i2c_w(struct sd *sd, u8 reg, u8 value)
+{
+	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
+	int ret;
+
+	if (sd->gspca_dev.usb_err < 0)
+		return;
+
+	ret = usb_control_msg(sd->gspca_dev.dev,
+			usb_sndctrlpipe(sd->gspca_dev.dev, 0),
+			0x02,
+			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			(u16) value, (u16) reg, NULL, 0, 500);
+
+	if (ret < 0) {
+		PERR("ovfx2_i2c_w %02x failed %d\n", reg, ret);
+		sd->gspca_dev.usb_err = ret;
+	}
+
+	PDEBUG(D_USBO, "ovfx2_i2c_w %02x %02x", reg, value);
+}
+
+static int ovfx2_i2c_r(struct sd *sd, u8 reg)
+{
+	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
+	int ret;
+
+	if (sd->gspca_dev.usb_err < 0)
+		return -1;
+
+	ret = usb_control_msg(sd->gspca_dev.dev,
+			usb_rcvctrlpipe(sd->gspca_dev.dev, 0),
+			0x03,
+			USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			0, (u16) reg, sd->gspca_dev.usb_buf, 1, 500);
+
+	if (ret >= 0) {
+		ret = sd->gspca_dev.usb_buf[0];
+		PDEBUG(D_USBI, "ovfx2_i2c_r %02x %02x", reg, ret);
+	} else {
+		PERR("ovfx2_i2c_r %02x failed %d\n", reg, ret);
+		sd->gspca_dev.usb_err = ret;
+	}
+
+	return ret;
+}
+
+static void i2c_w(struct sd *sd, u8 reg, u8 value)
+{
+	if (sd->sensor_reg_cache[reg] == value)
+		return;
+
+	switch (sd->bridge) {
+	case BRIDGE_OV511:
+	case BRIDGE_OV511PLUS:
+		ov511_i2c_w(sd, reg, value);
+		break;
+	case BRIDGE_OV518:
+	case BRIDGE_OV518PLUS:
+	case BRIDGE_OV519:
+		ov518_i2c_w(sd, reg, value);
+		break;
+	case BRIDGE_OVFX2:
+		ovfx2_i2c_w(sd, reg, value);
+		break;
+	case BRIDGE_W9968CF:
+		w9968cf_i2c_w(sd, reg, value);
+		break;
+	}
+
+	if (sd->gspca_dev.usb_err >= 0) {
+		/* Up on sensor reset empty the register cache */
+		if (reg == 0x12 && (value & 0x80))
+			memset(sd->sensor_reg_cache, -1,
+				sizeof(sd->sensor_reg_cache));
+		else
+			sd->sensor_reg_cache[reg] = value;
+	}
+}
+
+static int i2c_r(struct sd *sd, u8 reg)
+{
+	int ret = -1;
+
+	if (sd->sensor_reg_cache[reg] != -1)
+		return sd->sensor_reg_cache[reg];
+
+	switch (sd->bridge) {
+	case BRIDGE_OV511:
+	case BRIDGE_OV511PLUS:
+		ret = ov511_i2c_r(sd, reg);
+		break;
+	case BRIDGE_OV518:
+	case BRIDGE_OV518PLUS:
+	case BRIDGE_OV519:
+		ret = ov518_i2c_r(sd, reg);
+		break;
+	case BRIDGE_OVFX2:
+		ret = ovfx2_i2c_r(sd, reg);
+		break;
+	case BRIDGE_W9968CF:
+		ret = w9968cf_i2c_r(sd, reg);
+		break;
+	}
+
+	if (ret >= 0)
+		sd->sensor_reg_cache[reg] = ret;
+
+	return ret;
+}
+
+/* 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 void 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;
+	oldval = rc & ~mask;		/* Clear the masked bits */
+	value |= oldval;		/* Set the desired bits */
+	i2c_w(sd, reg, value);
+}
+
+/* Temporarily stops OV511 from functioning. Must do this before changing
+ * registers while the camera is streaming */
+static inline void ov51x_stop(struct sd *sd)
+{
+	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
+
+	PDEBUG(D_STREAM, "stopping");
+	sd->stopped = 1;
+	switch (sd->bridge) {
+	case BRIDGE_OV511:
+	case BRIDGE_OV511PLUS:
+		reg_w(sd, R51x_SYS_RESET, 0x3d);
+		break;
+	case BRIDGE_OV518:
+	case BRIDGE_OV518PLUS:
+		reg_w_mask(sd, R51x_SYS_RESET, 0x3a, 0x3a);
+		break;
+	case BRIDGE_OV519:
+		reg_w(sd, OV519_R51_RESET1, 0x0f);
+		reg_w(sd, OV519_R51_RESET1, 0x00);
+		reg_w(sd, 0x22, 0x00);		/* FRAR */
+		break;
+	case BRIDGE_OVFX2:
+		reg_w_mask(sd, 0x0f, 0x00, 0x02);
+		break;
+	case BRIDGE_W9968CF:
+		reg_w(sd, 0x3c, 0x0a05); /* stop USB transfer */
+		break;
+	}
+}
+
+/* Restarts OV511 after ov511_stop() is called. Has no effect if it is not
+ * actually stopped (for performance). */
+static inline void ov51x_restart(struct sd *sd)
+{
+	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
+
+	PDEBUG(D_STREAM, "restarting");
+	if (!sd->stopped)
+		return;
+	sd->stopped = 0;
+
+	/* Reinitialize the stream */
+	switch (sd->bridge) {
+	case BRIDGE_OV511:
+	case BRIDGE_OV511PLUS:
+		reg_w(sd, R51x_SYS_RESET, 0x00);
+		break;
+	case BRIDGE_OV518:
+	case BRIDGE_OV518PLUS:
+		reg_w(sd, 0x2f, 0x80);
+		reg_w(sd, R51x_SYS_RESET, 0x00);
+		break;
+	case BRIDGE_OV519:
+		reg_w(sd, OV519_R51_RESET1, 0x0f);
+		reg_w(sd, OV519_R51_RESET1, 0x00);
+		reg_w(sd, 0x22, 0x1d);		/* FRAR */
+		break;
+	case BRIDGE_OVFX2:
+		reg_w_mask(sd, 0x0f, 0x02, 0x02);
+		break;
+	case BRIDGE_W9968CF:
+		reg_w(sd, 0x3c, 0x8a05); /* USB FIFO enable */
+		break;
+	}
+}
+
+static void ov51x_set_slave_ids(struct sd *sd, u8 slave);
+
+/* 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, u8 slave)
+{
+	int i;
+	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
+
+	ov51x_set_slave_ids(sd, slave);
+
+	/* Reset the sensor */
+	i2c_w(sd, 0x12, 0x80);
+
+	/* Wait for it to initialize */
+	msleep(150);
+
+	for (i = 0; i < i2c_detect_tries; i++) {
+		if (i2c_r(sd, OV7610_REG_ID_HIGH) == 0x7f &&
+		    i2c_r(sd, OV7610_REG_ID_LOW) == 0xa2) {
+			PDEBUG(D_PROBE, "I2C synced in %d attempt(s)", i);
+			return 0;
+		}
+
+		/* Reset the sensor */
+		i2c_w(sd, 0x12, 0x80);
+
+		/* Wait for it to initialize */
+		msleep(150);
+
+		/* Dummy read to sync I2C */
+		if (i2c_r(sd, 0x00) < 0)
+			return -1;
+	}
+	return -1;
+}
+
+/* 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 void ov51x_set_slave_ids(struct sd *sd,
+				u8 slave)
+{
+	switch (sd->bridge) {
+	case BRIDGE_OVFX2:
+		reg_w(sd, OVFX2_I2C_ADDR, slave);
+		return;
+	case BRIDGE_W9968CF:
+		sd->sensor_addr = slave;
+		return;
+	}
+
+	reg_w(sd, R51x_I2C_W_SID, slave);
+	reg_w(sd, R51x_I2C_R_SID, slave + 1);
+}
+
+static void write_regvals(struct sd *sd,
+			 const struct ov_regvals *regvals,
+			 int n)
+{
+	while (--n >= 0) {
+		reg_w(sd, regvals->reg, regvals->val);
+		regvals++;
+	}
+}
+
+static void write_i2c_regvals(struct sd *sd,
+			const struct ov_i2c_regvals *regvals,
+			int n)
+{
+	while (--n >= 0) {
+		i2c_w(sd, regvals->reg, regvals->val);
+		regvals++;
+	}
+}
+
+/****************************************************************************
+ *
+ * OV511 and sensor configuration
+ *
+ ***************************************************************************/
+
+/* This initializes the OV2x10 / OV3610 / OV3620 / OV9600 */
+static void ov_hires_configure(struct sd *sd)
+{
+	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
+	int high, low;
+
+	if (sd->bridge != BRIDGE_OVFX2) {
+		PERR("error hires sensors only supported with ovfx2\n");
+		return;
+	}
+
+	PDEBUG(D_PROBE, "starting ov hires configuration");
+
+	/* Detect sensor (sub)type */
+	high = i2c_r(sd, 0x0a);
+	low = i2c_r(sd, 0x0b);
+	/* info("%x, %x", high, low); */
+	switch (high) {
+	case 0x96:
+		switch (low) {
+		case 0x40:
+			PDEBUG(D_PROBE, "Sensor is a OV2610");
+			sd->sensor = SEN_OV2610;
+			return;
+		case 0x41:
+			PDEBUG(D_PROBE, "Sensor is a OV2610AE");
+			sd->sensor = SEN_OV2610AE;
+			return;
+		case 0xb1:
+			PDEBUG(D_PROBE, "Sensor is a OV9600");
+			sd->sensor = SEN_OV9600;
+			return;
+		}
+		break;
+	case 0x36:
+		if ((low & 0x0f) == 0x00) {
+			PDEBUG(D_PROBE, "Sensor is a OV3610");
+			sd->sensor = SEN_OV3610;
+			return;
+		}
+		break;
+	}
+	PERR("Error unknown sensor type: %02x%02x\n", high, low);
+}
+
+/* This initializes the OV8110, OV8610 sensor. The OV8110 uses
+ * the same register settings as the OV8610, since they are very similar.
+ */
+static void ov8xx0_configure(struct sd *sd)
+{
+	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
+	int rc;
+
+	PDEBUG(D_PROBE, "starting ov8xx0 configuration");
+
+	/* Detect sensor (sub)type */
+	rc = i2c_r(sd, OV7610_REG_COM_I);
+	if (rc < 0) {
+		PERR("Error detecting sensor type");
+		return;
+	}
+	if ((rc & 3) == 1)
+		sd->sensor = SEN_OV8610;
+	else
+		PERR("Unknown image sensor version: %d\n", rc & 3);
+}
+
+/* This initializes the OV7610, OV7620, or OV76BE sensor. The OV76BE uses
+ * the same register settings as the OV7610, since they are very similar.
+ */
+static void ov7xx0_configure(struct sd *sd)
+{
+	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
+	int rc, high, low;
+
+	PDEBUG(D_PROBE, "starting OV7xx0 configuration");
+
+	/* 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) {
+		PERR("Error detecting sensor type\n");
+		return;
+	}
+	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 & 0xf0) == 0x70) {
+			PDEBUG(D_PROBE, "Sensor is an OV76%02x", low);
+			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");
+			sd->sensor = SEN_OV7620AE;
+		} else {
+			PDEBUG(D_PROBE, "Sensor is an OV76BE");
+			sd->sensor = SEN_OV76BE;
+		}
+	} else if ((rc & 3) == 0) {
+		/* try to read product id registers */
+		high = i2c_r(sd, 0x0a);
+		if (high < 0) {
+			PERR("Error detecting camera chip PID\n");
+			return;
+		}
+		low = i2c_r(sd, 0x0b);
+		if (low < 0) {
+			PERR("Error detecting camera chip VER\n");
+			return;
+		}
+		if (high == 0x76) {
+			switch (low) {
+			case 0x30:
+				PERR("Sensor is an OV7630/OV7635\n");
+				PERR("7630 is not supported by this driver\n");
+				return;
+			case 0x40:
+				PDEBUG(D_PROBE, "Sensor is an OV7645");
+				sd->sensor = SEN_OV7640; /* FIXME */
+				break;
+			case 0x45:
+				PDEBUG(D_PROBE, "Sensor is an OV7645B");
+				sd->sensor = SEN_OV7640; /* FIXME */
+				break;
+			case 0x48:
+				PDEBUG(D_PROBE, "Sensor is an OV7648");
+				sd->sensor = SEN_OV7648;
+				break;
+			case 0x60:
+				PDEBUG(D_PROBE, "Sensor is a OV7660");
+				sd->sensor = SEN_OV7660;
+				break;
+			default:
+				PERR("Unknown sensor: 0x76%02x\n", low);
+				return;
+			}
+		} else {
+			PDEBUG(D_PROBE, "Sensor is an OV7620");
+			sd->sensor = SEN_OV7620;
+		}
+	} else {
+		PERR("Unknown image sensor version: %d\n", rc & 3);
+	}
+}
+
+/* This initializes the OV6620, OV6630, OV6630AE, or OV6630AF sensor. */
+static void ov6xx0_configure(struct sd *sd)
+{
+	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
+	int rc;
+
+	PDEBUG(D_PROBE, "starting OV6xx0 configuration");
+
+	/* Detect sensor (sub)type */
+	rc = i2c_r(sd, OV7610_REG_COM_I);
+	if (rc < 0) {
+		PERR("Error detecting sensor type\n");
+		return;
+	}
+
+	/* 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. */
+	switch (rc) {
+	case 0x00:
+		sd->sensor = SEN_OV6630;
+		pr_warn("WARNING: Sensor is an OV66308. Your camera may have been misdetected in previous driver versions.\n");
+		break;
+	case 0x01:
+		sd->sensor = SEN_OV6620;
+		PDEBUG(D_PROBE, "Sensor is an OV6620");
+		break;
+	case 0x02:
+		sd->sensor = SEN_OV6630;
+		PDEBUG(D_PROBE, "Sensor is an OV66308AE");
+		break;
+	case 0x03:
+		sd->sensor = SEN_OV66308AF;
+		PDEBUG(D_PROBE, "Sensor is an OV66308AF");
+		break;
+	case 0x90:
+		sd->sensor = SEN_OV6630;
+		pr_warn("WARNING: Sensor is an OV66307. Your camera may have been misdetected in previous driver versions.\n");
+		break;
+	default:
+		PERR("FATAL: Unknown sensor version: 0x%02x\n", rc);
+		return;
+	}
+
+	/* Set sensor-specific vars */
+	sd->sif = 1;
+}
+
+/* Turns on or off the LED. Only has an effect with OV511+/OV518(+)/OV519 */
+static void ov51x_led_control(struct sd *sd, int on)
+{
+	if (sd->invert_led)
+		on = !on;
+
+	switch (sd->bridge) {
+	/* OV511 has no LED control */
+	case BRIDGE_OV511PLUS:
+		reg_w(sd, R511_SYS_LED_CTL, on);
+		break;
+	case BRIDGE_OV518:
+	case BRIDGE_OV518PLUS:
+		reg_w_mask(sd, R518_GPIO_OUT, 0x02 * on, 0x02);
+		break;
+	case BRIDGE_OV519:
+		reg_w_mask(sd, OV519_GPIO_DATA_OUT0, on, 1);
+		break;
+	}
+}
+
+static void sd_reset_snapshot(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (!sd->snapshot_needs_reset)
+		return;
+
+	/* Note it is important that we clear sd->snapshot_needs_reset,
+	   before actually clearing the snapshot state in the bridge
+	   otherwise we might race with the pkt_scan interrupt handler */
+	sd->snapshot_needs_reset = 0;
+
+	switch (sd->bridge) {
+	case BRIDGE_OV511:
+	case BRIDGE_OV511PLUS:
+		reg_w(sd, R51x_SYS_SNAP, 0x02);
+		reg_w(sd, R51x_SYS_SNAP, 0x00);
+		break;
+	case BRIDGE_OV518:
+	case BRIDGE_OV518PLUS:
+		reg_w(sd, R51x_SYS_SNAP, 0x02); /* Reset */
+		reg_w(sd, R51x_SYS_SNAP, 0x01); /* Enable */
+		break;
+	case BRIDGE_OV519:
+		reg_w(sd, R51x_SYS_RESET, 0x40);
+		reg_w(sd, R51x_SYS_RESET, 0x00);
+		break;
+	}
+}
+
+static void ov51x_upload_quan_tables(struct sd *sd)
+{
+	const unsigned char yQuanTable511[] = {
+		0, 1, 1, 2, 2, 3, 3, 4,
+		1, 1, 1, 2, 2, 3, 4, 4,
+		1, 1, 2, 2, 3, 4, 4, 4,
+		2, 2, 2, 3, 4, 4, 4, 4,
+		2, 2, 3, 4, 4, 5, 5, 5,
+		3, 3, 4, 4, 5, 5, 5, 5,
+		3, 4, 4, 4, 5, 5, 5, 5,
+		4, 4, 4, 4, 5, 5, 5, 5
+	};
+
+	const unsigned char uvQuanTable511[] = {
+		0, 2, 2, 3, 4, 4, 4, 4,
+		2, 2, 2, 4, 4, 4, 4, 4,
+		2, 2, 3, 4, 4, 4, 4, 4,
+		3, 4, 4, 4, 4, 4, 4, 4,
+		4, 4, 4, 4, 4, 4, 4, 4,
+		4, 4, 4, 4, 4, 4, 4, 4,
+		4, 4, 4, 4, 4, 4, 4, 4,
+		4, 4, 4, 4, 4, 4, 4, 4
+	};
+
+	/* OV518 quantization tables are 8x4 (instead of 8x8) */
+	const unsigned char yQuanTable518[] = {
+		5, 4, 5, 6, 6, 7, 7, 7,
+		5, 5, 5, 5, 6, 7, 7, 7,
+		6, 6, 6, 6, 7, 7, 7, 8,
+		7, 7, 6, 7, 7, 7, 8, 8
+	};
+	const unsigned char uvQuanTable518[] = {
+		6, 6, 6, 7, 7, 7, 7, 7,
+		6, 6, 6, 7, 7, 7, 7, 7,
+		6, 6, 6, 7, 7, 7, 7, 8,
+		7, 7, 7, 7, 7, 7, 8, 8
+	};
+
+	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
+	const unsigned char *pYTable, *pUVTable;
+	unsigned char val0, val1;
+	int i, size, reg = R51x_COMP_LUT_BEGIN;
+
+	PDEBUG(D_PROBE, "Uploading quantization tables");
+
+	if (sd->bridge == BRIDGE_OV511 || sd->bridge == BRIDGE_OV511PLUS) {
+		pYTable = yQuanTable511;
+		pUVTable = uvQuanTable511;
+		size = 32;
+	} else {
+		pYTable = yQuanTable518;
+		pUVTable = uvQuanTable518;
+		size = 16;
+	}
+
+	for (i = 0; i < size; i++) {
+		val0 = *pYTable++;
+		val1 = *pYTable++;
+		val0 &= 0x0f;
+		val1 &= 0x0f;
+		val0 |= val1 << 4;
+		reg_w(sd, reg, val0);
+
+		val0 = *pUVTable++;
+		val1 = *pUVTable++;
+		val0 &= 0x0f;
+		val1 &= 0x0f;
+		val0 |= val1 << 4;
+		reg_w(sd, reg + size, val0);
+
+		reg++;
+	}
+}
+
+/* This initializes the OV511/OV511+ and the sensor */
+static void ov511_configure(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	/* For 511 and 511+ */
+	const struct ov_regvals init_511[] = {
+		{ R51x_SYS_RESET,	0x7f },
+		{ R51x_SYS_INIT,	0x01 },
+		{ R51x_SYS_RESET,	0x7f },
+		{ R51x_SYS_INIT,	0x01 },
+		{ R51x_SYS_RESET,	0x3f },
+		{ R51x_SYS_INIT,	0x01 },
+		{ R51x_SYS_RESET,	0x3d },
+	};
+
+	const struct ov_regvals norm_511[] = {
+		{ R511_DRAM_FLOW_CTL,	0x01 },
+		{ R51x_SYS_SNAP,	0x00 },
+		{ R51x_SYS_SNAP,	0x02 },
+		{ R51x_SYS_SNAP,	0x00 },
+		{ R511_FIFO_OPTS,	0x1f },
+		{ R511_COMP_EN,		0x00 },
+		{ R511_COMP_LUT_EN,	0x03 },
+	};
+
+	const struct ov_regvals norm_511_p[] = {
+		{ R511_DRAM_FLOW_CTL,	0xff },
+		{ R51x_SYS_SNAP,	0x00 },
+		{ R51x_SYS_SNAP,	0x02 },
+		{ R51x_SYS_SNAP,	0x00 },
+		{ R511_FIFO_OPTS,	0xff },
+		{ R511_COMP_EN,		0x00 },
+		{ R511_COMP_LUT_EN,	0x03 },
+	};
+
+	const struct ov_regvals compress_511[] = {
+		{ 0x70, 0x1f },
+		{ 0x71, 0x05 },
+		{ 0x72, 0x06 },
+		{ 0x73, 0x06 },
+		{ 0x74, 0x14 },
+		{ 0x75, 0x03 },
+		{ 0x76, 0x04 },
+		{ 0x77, 0x04 },
+	};
+
+	PDEBUG(D_PROBE, "Device custom id %x", reg_r(sd, R51x_SYS_CUST_ID));
+
+	write_regvals(sd, init_511, ARRAY_SIZE(init_511));
+
+	switch (sd->bridge) {
+	case BRIDGE_OV511:
+		write_regvals(sd, norm_511, ARRAY_SIZE(norm_511));
+		break;
+	case BRIDGE_OV511PLUS:
+		write_regvals(sd, norm_511_p, ARRAY_SIZE(norm_511_p));
+		break;
+	}
+
+	/* Init compression */
+	write_regvals(sd, compress_511, ARRAY_SIZE(compress_511));
+
+	ov51x_upload_quan_tables(sd);
+}
+
+/* This initializes the OV518/OV518+ and the sensor */
+static void ov518_configure(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	/* For 518 and 518+ */
+	const struct ov_regvals init_518[] = {
+		{ R51x_SYS_RESET,	0x40 },
+		{ R51x_SYS_INIT,	0xe1 },
+		{ R51x_SYS_RESET,	0x3e },
+		{ R51x_SYS_INIT,	0xe1 },
+		{ R51x_SYS_RESET,	0x00 },
+		{ R51x_SYS_INIT,	0xe1 },
+		{ 0x46,			0x00 },
+		{ 0x5d,			0x03 },
+	};
+
+	const struct ov_regvals norm_518[] = {
+		{ R51x_SYS_SNAP,	0x02 }, /* Reset */
+		{ R51x_SYS_SNAP,	0x01 }, /* Enable */
+		{ 0x31,			0x0f },
+		{ 0x5d,			0x03 },
+		{ 0x24,			0x9f },
+		{ 0x25,			0x90 },
+		{ 0x20,			0x00 },
+		{ 0x51,			0x04 },
+		{ 0x71,			0x19 },
+		{ 0x2f,			0x80 },
+	};
+
+	const struct ov_regvals norm_518_p[] = {
+		{ R51x_SYS_SNAP,	0x02 }, /* Reset */
+		{ R51x_SYS_SNAP,	0x01 }, /* Enable */
+		{ 0x31,			0x0f },
+		{ 0x5d,			0x03 },
+		{ 0x24,			0x9f },
+		{ 0x25,			0x90 },
+		{ 0x20,			0x60 },
+		{ 0x51,			0x02 },
+		{ 0x71,			0x19 },
+		{ 0x40,			0xff },
+		{ 0x41,			0x42 },
+		{ 0x46,			0x00 },
+		{ 0x33,			0x04 },
+		{ 0x21,			0x19 },
+		{ 0x3f,			0x10 },
+		{ 0x2f,			0x80 },
+	};
+
+	/* First 5 bits of custom ID reg are a revision ID on OV518 */
+	sd->revision = reg_r(sd, R51x_SYS_CUST_ID) & 0x1f;
+	PDEBUG(D_PROBE, "Device revision %d", sd->revision);
+
+	write_regvals(sd, init_518, ARRAY_SIZE(init_518));
+
+	/* Set LED GPIO pin to output mode */
+	reg_w_mask(sd, R518_GPIO_CTL, 0x00, 0x02);
+
+	switch (sd->bridge) {
+	case BRIDGE_OV518:
+		write_regvals(sd, norm_518, ARRAY_SIZE(norm_518));
+		break;
+	case BRIDGE_OV518PLUS:
+		write_regvals(sd, norm_518_p, ARRAY_SIZE(norm_518_p));
+		break;
+	}
+
+	ov51x_upload_quan_tables(sd);
+
+	reg_w(sd, 0x2f, 0x80);
+}
+
+static void ov519_configure(struct sd *sd)
+{
+	static const struct ov_regvals init_519[] = {
+		{ 0x5a, 0x6d }, /* EnableSystem */
+		{ 0x53, 0x9b }, /* don't enable the microcontroller */
+		{ OV519_R54_EN_CLK1, 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 },
+		{ OV519_R51_RESET1, 0x0f },
+		{ OV519_R51_RESET1, 0x00 },
+		{ 0x22, 0x00 },
+		/* windows reads 0x55 at this point*/
+	};
+
+	write_regvals(sd, init_519, ARRAY_SIZE(init_519));
+}
+
+static void ovfx2_configure(struct sd *sd)
+{
+	static const struct ov_regvals init_fx2[] = {
+		{ 0x00, 0x60 },
+		{ 0x02, 0x01 },
+		{ 0x0f, 0x1d },
+		{ 0xe9, 0x82 },
+		{ 0xea, 0xc7 },
+		{ 0xeb, 0x10 },
+		{ 0xec, 0xf6 },
+	};
+
+	sd->stopped = 1;
+
+	write_regvals(sd, init_fx2, ARRAY_SIZE(init_fx2));
+}
+
+/* set the mode */
+/* This function works for ov7660 only */
+static void ov519_set_mode(struct sd *sd)
+{
+	static const struct ov_regvals bridge_ov7660[2][10] = {
+		{{0x10, 0x14}, {0x11, 0x1e}, {0x12, 0x00}, {0x13, 0x00},
+		 {0x14, 0x00}, {0x15, 0x00}, {0x16, 0x00}, {0x20, 0x0c},
+		 {0x25, 0x01}, {0x26, 0x00}},
+		{{0x10, 0x28}, {0x11, 0x3c}, {0x12, 0x00}, {0x13, 0x00},
+		 {0x14, 0x00}, {0x15, 0x00}, {0x16, 0x00}, {0x20, 0x0c},
+		 {0x25, 0x03}, {0x26, 0x00}}
+	};
+	static const struct ov_i2c_regvals sensor_ov7660[2][3] = {
+		{{0x12, 0x00}, {0x24, 0x00}, {0x0c, 0x0c}},
+		{{0x12, 0x00}, {0x04, 0x00}, {0x0c, 0x00}}
+	};
+	static const struct ov_i2c_regvals sensor_ov7660_2[] = {
+		{OV7670_R17_HSTART, 0x13},
+		{OV7670_R18_HSTOP, 0x01},
+		{OV7670_R32_HREF, 0x92},
+		{OV7670_R19_VSTART, 0x02},
+		{OV7670_R1A_VSTOP, 0x7a},
+		{OV7670_R03_VREF, 0x00},
+/*		{0x33, 0x00}, */
+/*		{0x34, 0x07}, */
+/*		{0x36, 0x00}, */
+/*		{0x6b, 0x0a}, */
+	};
+
+	write_regvals(sd, bridge_ov7660[sd->gspca_dev.curr_mode],
+			ARRAY_SIZE(bridge_ov7660[0]));
+	write_i2c_regvals(sd, sensor_ov7660[sd->gspca_dev.curr_mode],
+			ARRAY_SIZE(sensor_ov7660[0]));
+	write_i2c_regvals(sd, sensor_ov7660_2,
+			ARRAY_SIZE(sensor_ov7660_2));
+}
+
+/* set the frame rate */
+/* This function works for sensors ov7640, ov7648 ov7660 and ov7670 only */
+static void ov519_set_fr(struct sd *sd)
+{
+	int fr;
+	u8 clock;
+	/* frame rate table with indices:
+	 *	- mode = 0: 320x240, 1: 640x480
+	 *	- fr rate = 0: 30, 1: 25, 2: 20, 3: 15, 4: 10, 5: 5
+	 *	- reg = 0: bridge a4, 1: bridge 23, 2: sensor 11 (clock)
+	 */
+	static const u8 fr_tb[2][6][3] = {
+		{{0x04, 0xff, 0x00},
+		 {0x04, 0x1f, 0x00},
+		 {0x04, 0x1b, 0x00},
+		 {0x04, 0x15, 0x00},
+		 {0x04, 0x09, 0x00},
+		 {0x04, 0x01, 0x00}},
+		{{0x0c, 0xff, 0x00},
+		 {0x0c, 0x1f, 0x00},
+		 {0x0c, 0x1b, 0x00},
+		 {0x04, 0xff, 0x01},
+		 {0x04, 0x1f, 0x01},
+		 {0x04, 0x1b, 0x01}},
+	};
+
+	if (frame_rate > 0)
+		sd->frame_rate = frame_rate;
+	if (sd->frame_rate >= 30)
+		fr = 0;
+	else if (sd->frame_rate >= 25)
+		fr = 1;
+	else if (sd->frame_rate >= 20)
+		fr = 2;
+	else if (sd->frame_rate >= 15)
+		fr = 3;
+	else if (sd->frame_rate >= 10)
+		fr = 4;
+	else
+		fr = 5;
+	reg_w(sd, 0xa4, fr_tb[sd->gspca_dev.curr_mode][fr][0]);
+	reg_w(sd, 0x23, fr_tb[sd->gspca_dev.curr_mode][fr][1]);
+	clock = fr_tb[sd->gspca_dev.curr_mode][fr][2];
+	if (sd->sensor == SEN_OV7660)
+		clock |= 0x80;		/* enable double clock */
+	ov518_i2c_w(sd, OV7670_R11_CLKRC, clock);
+}
+
+static void setautogain(struct gspca_dev *gspca_dev, s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	i2c_w_mask(sd, 0x13, val ? 0x05 : 0x00, 0x05);
+}
+
+/* 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;
+
+	sd->bridge = id->driver_info & BRIDGE_MASK;
+	sd->invert_led = (id->driver_info & BRIDGE_INVERT_LED) != 0;
+
+	switch (sd->bridge) {
+	case BRIDGE_OV511:
+	case BRIDGE_OV511PLUS:
+		cam->cam_mode = ov511_vga_mode;
+		cam->nmodes = ARRAY_SIZE(ov511_vga_mode);
+		break;
+	case BRIDGE_OV518:
+	case BRIDGE_OV518PLUS:
+		cam->cam_mode = ov518_vga_mode;
+		cam->nmodes = ARRAY_SIZE(ov518_vga_mode);
+		break;
+	case BRIDGE_OV519:
+		cam->cam_mode = ov519_vga_mode;
+		cam->nmodes = ARRAY_SIZE(ov519_vga_mode);
+		break;
+	case BRIDGE_OVFX2:
+		cam->cam_mode = ov519_vga_mode;
+		cam->nmodes = ARRAY_SIZE(ov519_vga_mode);
+		cam->bulk_size = OVFX2_BULK_SIZE;
+		cam->bulk_nurbs = MAX_NURBS;
+		cam->bulk = 1;
+		break;
+	case BRIDGE_W9968CF:
+		cam->cam_mode = w9968cf_vga_mode;
+		cam->nmodes = ARRAY_SIZE(w9968cf_vga_mode);
+		break;
+	}
+
+	sd->frame_rate = 15;
+
+	return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct cam *cam = &gspca_dev->cam;
+
+	switch (sd->bridge) {
+	case BRIDGE_OV511:
+	case BRIDGE_OV511PLUS:
+		ov511_configure(gspca_dev);
+		break;
+	case BRIDGE_OV518:
+	case BRIDGE_OV518PLUS:
+		ov518_configure(gspca_dev);
+		break;
+	case BRIDGE_OV519:
+		ov519_configure(sd);
+		break;
+	case BRIDGE_OVFX2:
+		ovfx2_configure(sd);
+		break;
+	case BRIDGE_W9968CF:
+		w9968cf_configure(sd);
+		break;
+	}
+
+	/* 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 */
+	sd->sensor = -1;
+
+	/* Test for 76xx */
+	if (init_ov_sensor(sd, OV7xx0_SID) >= 0) {
+		ov7xx0_configure(sd);
+
+	/* Test for 6xx0 */
+	} else if (init_ov_sensor(sd, OV6xx0_SID) >= 0) {
+		ov6xx0_configure(sd);
+
+	/* Test for 8xx0 */
+	} else if (init_ov_sensor(sd, OV8xx0_SID) >= 0) {
+		ov8xx0_configure(sd);
+
+	/* Test for 3xxx / 2xxx */
+	} else if (init_ov_sensor(sd, OV_HIRES_SID) >= 0) {
+		ov_hires_configure(sd);
+	} else {
+		PERR("Can't determine sensor slave IDs\n");
+		goto error;
+	}
+
+	if (sd->sensor < 0)
+		goto error;
+
+	ov51x_led_control(sd, 0);	/* turn LED off */
+
+	switch (sd->bridge) {
+	case BRIDGE_OV511:
+	case BRIDGE_OV511PLUS:
+		if (sd->sif) {
+			cam->cam_mode = ov511_sif_mode;
+			cam->nmodes = ARRAY_SIZE(ov511_sif_mode);
+		}
+		break;
+	case BRIDGE_OV518:
+	case BRIDGE_OV518PLUS:
+		if (sd->sif) {
+			cam->cam_mode = ov518_sif_mode;
+			cam->nmodes = ARRAY_SIZE(ov518_sif_mode);
+		}
+		break;
+	case BRIDGE_OV519:
+		if (sd->sif) {
+			cam->cam_mode = ov519_sif_mode;
+			cam->nmodes = ARRAY_SIZE(ov519_sif_mode);
+		}
+		break;
+	case BRIDGE_OVFX2:
+		switch (sd->sensor) {
+		case SEN_OV2610:
+		case SEN_OV2610AE:
+			cam->cam_mode = ovfx2_ov2610_mode;
+			cam->nmodes = ARRAY_SIZE(ovfx2_ov2610_mode);
+			break;
+		case SEN_OV3610:
+			cam->cam_mode = ovfx2_ov3610_mode;
+			cam->nmodes = ARRAY_SIZE(ovfx2_ov3610_mode);
+			break;
+		case SEN_OV9600:
+			cam->cam_mode = ovfx2_ov9600_mode;
+			cam->nmodes = ARRAY_SIZE(ovfx2_ov9600_mode);
+			break;
+		default:
+			if (sd->sif) {
+				cam->cam_mode = ov519_sif_mode;
+				cam->nmodes = ARRAY_SIZE(ov519_sif_mode);
+			}
+			break;
+		}
+		break;
+	case BRIDGE_W9968CF:
+		if (sd->sif)
+			cam->nmodes = ARRAY_SIZE(w9968cf_vga_mode) - 1;
+
+		/* w9968cf needs initialisation once the sensor is known */
+		w9968cf_init(sd);
+		break;
+	}
+
+	/* initialize the sensor */
+	switch (sd->sensor) {
+	case SEN_OV2610:
+		write_i2c_regvals(sd, norm_2610, ARRAY_SIZE(norm_2610));
+
+		/* Enable autogain, autoexpo, awb, bandfilter */
+		i2c_w_mask(sd, 0x13, 0x27, 0x27);
+		break;
+	case SEN_OV2610AE:
+		write_i2c_regvals(sd, norm_2610ae, ARRAY_SIZE(norm_2610ae));
+
+		/* enable autoexpo */
+		i2c_w_mask(sd, 0x13, 0x05, 0x05);
+		break;
+	case SEN_OV3610:
+		write_i2c_regvals(sd, norm_3620b, ARRAY_SIZE(norm_3620b));
+
+		/* Enable autogain, autoexpo, awb, bandfilter */
+		i2c_w_mask(sd, 0x13, 0x27, 0x27);
+		break;
+	case SEN_OV6620:
+		write_i2c_regvals(sd, norm_6x20, ARRAY_SIZE(norm_6x20));
+		break;
+	case SEN_OV6630:
+	case SEN_OV66308AF:
+		write_i2c_regvals(sd, norm_6x30, ARRAY_SIZE(norm_6x30));
+		break;
+	default:
+/*	case SEN_OV7610: */
+/*	case SEN_OV76BE: */
+		write_i2c_regvals(sd, norm_7610, ARRAY_SIZE(norm_7610));
+		i2c_w_mask(sd, 0x0e, 0x00, 0x40);
+		break;
+	case SEN_OV7620:
+	case SEN_OV7620AE:
+		write_i2c_regvals(sd, norm_7620, ARRAY_SIZE(norm_7620));
+		break;
+	case SEN_OV7640:
+	case SEN_OV7648:
+		write_i2c_regvals(sd, norm_7640, ARRAY_SIZE(norm_7640));
+		break;
+	case SEN_OV7660:
+		i2c_w(sd, OV7670_R12_COM7, OV7670_COM7_RESET);
+		msleep(14);
+		reg_w(sd, OV519_R57_SNAPSHOT, 0x23);
+		write_regvals(sd, init_519_ov7660,
+				ARRAY_SIZE(init_519_ov7660));
+		write_i2c_regvals(sd, norm_7660, ARRAY_SIZE(norm_7660));
+		sd->gspca_dev.curr_mode = 1;	/* 640x480 */
+		ov519_set_mode(sd);
+		ov519_set_fr(sd);
+		sd_reset_snapshot(gspca_dev);
+		ov51x_restart(sd);
+		ov51x_stop(sd);			/* not in win traces */
+		ov51x_led_control(sd, 0);
+		break;
+	case SEN_OV7670:
+		write_i2c_regvals(sd, norm_7670, ARRAY_SIZE(norm_7670));
+		break;
+	case SEN_OV8610:
+		write_i2c_regvals(sd, norm_8610, ARRAY_SIZE(norm_8610));
+		break;
+	case SEN_OV9600:
+		write_i2c_regvals(sd, norm_9600, ARRAY_SIZE(norm_9600));
+
+		/* enable autoexpo */
+/*		i2c_w_mask(sd, 0x13, 0x05, 0x05); */
+		break;
+	}
+	return gspca_dev->usb_err;
+error:
+	PERR("OV519 Config failed");
+	return -EINVAL;
+}
+
+/* function called at start time before URB creation */
+static int sd_isoc_init(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	switch (sd->bridge) {
+	case BRIDGE_OVFX2:
+		if (gspca_dev->pixfmt.width != 800)
+			gspca_dev->cam.bulk_size = OVFX2_BULK_SIZE;
+		else
+			gspca_dev->cam.bulk_size = 7 * 4096;
+		break;
+	}
+	return 0;
+}
+
+/* Set up the OV511/OV511+ with the given image parameters.
+ *
+ * Do not put any sensor-specific code in here (including I2C I/O functions)
+ */
+static void ov511_mode_init_regs(struct sd *sd)
+{
+	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
+	int hsegs, vsegs, packet_size, fps, needed;
+	int interlaced = 0;
+	struct usb_host_interface *alt;
+	struct usb_interface *intf;
+
+	intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface);
+	alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt);
+	if (!alt) {
+		PERR("Couldn't get altsetting\n");
+		sd->gspca_dev.usb_err = -EIO;
+		return;
+	}
+
+	packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize);
+	reg_w(sd, R51x_FIFO_PSIZE, packet_size >> 5);
+
+	reg_w(sd, R511_CAM_UV_EN, 0x01);
+	reg_w(sd, R511_SNAP_UV_EN, 0x01);
+	reg_w(sd, R511_SNAP_OPTS, 0x03);
+
+	/* Here I'm assuming that snapshot size == image size.
+	 * I hope that's always true. --claudio
+	 */
+	hsegs = (sd->gspca_dev.pixfmt.width >> 3) - 1;
+	vsegs = (sd->gspca_dev.pixfmt.height >> 3) - 1;
+
+	reg_w(sd, R511_CAM_PXCNT, hsegs);
+	reg_w(sd, R511_CAM_LNCNT, vsegs);
+	reg_w(sd, R511_CAM_PXDIV, 0x00);
+	reg_w(sd, R511_CAM_LNDIV, 0x00);
+
+	/* YUV420, low pass filter on */
+	reg_w(sd, R511_CAM_OPTS, 0x03);
+
+	/* Snapshot additions */
+	reg_w(sd, R511_SNAP_PXCNT, hsegs);
+	reg_w(sd, R511_SNAP_LNCNT, vsegs);
+	reg_w(sd, R511_SNAP_PXDIV, 0x00);
+	reg_w(sd, R511_SNAP_LNDIV, 0x00);
+
+	/******** Set the framerate ********/
+	if (frame_rate > 0)
+		sd->frame_rate = frame_rate;
+
+	switch (sd->sensor) {
+	case SEN_OV6620:
+		/* No framerate control, doesn't like higher rates yet */
+		sd->clockdiv = 3;
+		break;
+
+	/* Note once the FIXME's in mode_init_ov_sensor_regs() are fixed
+	   for more sensors we need to do this for them too */
+	case SEN_OV7620:
+	case SEN_OV7620AE:
+	case SEN_OV7640:
+	case SEN_OV7648:
+	case SEN_OV76BE:
+		if (sd->gspca_dev.pixfmt.width == 320)
+			interlaced = 1;
+		/* Fall through */
+	case SEN_OV6630:
+	case SEN_OV7610:
+	case SEN_OV7670:
+		switch (sd->frame_rate) {
+		case 30:
+		case 25:
+			/* Not enough bandwidth to do 640x480 @ 30 fps */
+			if (sd->gspca_dev.pixfmt.width != 640) {
+				sd->clockdiv = 0;
+				break;
+			}
+			/* Fall through for 640x480 case */
+		default:
+/*		case 20: */
+/*		case 15: */
+			sd->clockdiv = 1;
+			break;
+		case 10:
+			sd->clockdiv = 2;
+			break;
+		case 5:
+			sd->clockdiv = 5;
+			break;
+		}
+		if (interlaced) {
+			sd->clockdiv = (sd->clockdiv + 1) * 2 - 1;
+			/* Higher then 10 does not work */
+			if (sd->clockdiv > 10)
+				sd->clockdiv = 10;
+		}
+		break;
+
+	case SEN_OV8610:
+		/* No framerate control ?? */
+		sd->clockdiv = 0;
+		break;
+	}
+
+	/* Check if we have enough bandwidth to disable compression */
+	fps = (interlaced ? 60 : 30) / (sd->clockdiv + 1) + 1;
+	needed = fps * sd->gspca_dev.pixfmt.width *
+			sd->gspca_dev.pixfmt.height * 3 / 2;
+	/* 1000 isoc packets/sec */
+	if (needed > 1000 * packet_size) {
+		/* Enable Y and UV quantization and compression */
+		reg_w(sd, R511_COMP_EN, 0x07);
+		reg_w(sd, R511_COMP_LUT_EN, 0x03);
+	} else {
+		reg_w(sd, R511_COMP_EN, 0x06);
+		reg_w(sd, R511_COMP_LUT_EN, 0x00);
+	}
+
+	reg_w(sd, R51x_SYS_RESET, OV511_RESET_OMNICE);
+	reg_w(sd, R51x_SYS_RESET, 0);
+}
+
+/* Sets up the OV518/OV518+ with the given image parameters
+ *
+ * OV518 needs a completely different approach, until we can figure out what
+ * the individual registers do. Also, only 15 FPS is supported now.
+ *
+ * Do not put any sensor-specific code in here (including I2C I/O functions)
+ */
+static void ov518_mode_init_regs(struct sd *sd)
+{
+	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
+	int hsegs, vsegs, packet_size;
+	struct usb_host_interface *alt;
+	struct usb_interface *intf;
+
+	intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface);
+	alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt);
+	if (!alt) {
+		PERR("Couldn't get altsetting\n");
+		sd->gspca_dev.usb_err = -EIO;
+		return;
+	}
+
+	packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize);
+	ov518_reg_w32(sd, R51x_FIFO_PSIZE, packet_size & ~7, 2);
+
+	/******** Set the mode ********/
+	reg_w(sd, 0x2b, 0);
+	reg_w(sd, 0x2c, 0);
+	reg_w(sd, 0x2d, 0);
+	reg_w(sd, 0x2e, 0);
+	reg_w(sd, 0x3b, 0);
+	reg_w(sd, 0x3c, 0);
+	reg_w(sd, 0x3d, 0);
+	reg_w(sd, 0x3e, 0);
+
+	if (sd->bridge == BRIDGE_OV518) {
+		/* Set 8-bit (YVYU) input format */
+		reg_w_mask(sd, 0x20, 0x08, 0x08);
+
+		/* Set 12-bit (4:2:0) output format */
+		reg_w_mask(sd, 0x28, 0x80, 0xf0);
+		reg_w_mask(sd, 0x38, 0x80, 0xf0);
+	} else {
+		reg_w(sd, 0x28, 0x80);
+		reg_w(sd, 0x38, 0x80);
+	}
+
+	hsegs = sd->gspca_dev.pixfmt.width / 16;
+	vsegs = sd->gspca_dev.pixfmt.height / 4;
+
+	reg_w(sd, 0x29, hsegs);
+	reg_w(sd, 0x2a, vsegs);
+
+	reg_w(sd, 0x39, hsegs);
+	reg_w(sd, 0x3a, vsegs);
+
+	/* Windows driver does this here; who knows why */
+	reg_w(sd, 0x2f, 0x80);
+
+	/******** Set the framerate ********/
+	if (sd->bridge == BRIDGE_OV518PLUS && sd->revision == 0 &&
+					      sd->sensor == SEN_OV7620AE)
+		sd->clockdiv = 0;
+	else
+		sd->clockdiv = 1;
+
+	/* Mode independent, but framerate dependent, regs */
+	/* 0x51: Clock divider; Only works on some cams which use 2 crystals */
+	reg_w(sd, 0x51, 0x04);
+	reg_w(sd, 0x22, 0x18);
+	reg_w(sd, 0x23, 0xff);
+
+	if (sd->bridge == BRIDGE_OV518PLUS) {
+		switch (sd->sensor) {
+		case SEN_OV7620AE:
+			/*
+			 * HdG: 640x480 needs special handling on device
+			 * revision 2, we check for device revison > 0 to
+			 * avoid regressions, as we don't know the correct
+			 * thing todo for revision 1.
+			 *
+			 * Also this likely means we don't need to
+			 * differentiate between the OV7620 and OV7620AE,
+			 * earlier testing hitting this same problem likely
+			 * happened to be with revision < 2 cams using an
+			 * OV7620 and revision 2 cams using an OV7620AE.
+			 */
+			if (sd->revision > 0 &&
+					sd->gspca_dev.pixfmt.width == 640) {
+				reg_w(sd, 0x20, 0x60);
+				reg_w(sd, 0x21, 0x1f);
+			} else {
+				reg_w(sd, 0x20, 0x00);
+				reg_w(sd, 0x21, 0x19);
+			}
+			break;
+		case SEN_OV7620:
+			reg_w(sd, 0x20, 0x00);
+			reg_w(sd, 0x21, 0x19);
+			break;
+		default:
+			reg_w(sd, 0x21, 0x19);
+		}
+	} else
+		reg_w(sd, 0x71, 0x17);	/* Compression-related? */
+
+	/* FIXME: Sensor-specific */
+	/* Bit 5 is what matters here. Of course, it is "reserved" */
+	i2c_w(sd, 0x54, 0x23);
+
+	reg_w(sd, 0x2f, 0x80);
+
+	if (sd->bridge == BRIDGE_OV518PLUS) {
+		reg_w(sd, 0x24, 0x94);
+		reg_w(sd, 0x25, 0x90);
+		ov518_reg_w32(sd, 0xc4,    400, 2);	/* 190h   */
+		ov518_reg_w32(sd, 0xc6,    540, 2);	/* 21ch   */
+		ov518_reg_w32(sd, 0xc7,    540, 2);	/* 21ch   */
+		ov518_reg_w32(sd, 0xc8,    108, 2);	/* 6ch    */
+		ov518_reg_w32(sd, 0xca, 131098, 3);	/* 2001ah */
+		ov518_reg_w32(sd, 0xcb,    532, 2);	/* 214h   */
+		ov518_reg_w32(sd, 0xcc,   2400, 2);	/* 960h   */
+		ov518_reg_w32(sd, 0xcd,     32, 2);	/* 20h    */
+		ov518_reg_w32(sd, 0xce,    608, 2);	/* 260h   */
+	} else {
+		reg_w(sd, 0x24, 0x9f);
+		reg_w(sd, 0x25, 0x90);
+		ov518_reg_w32(sd, 0xc4,    400, 2);	/* 190h   */
+		ov518_reg_w32(sd, 0xc6,    381, 2);	/* 17dh   */
+		ov518_reg_w32(sd, 0xc7,    381, 2);	/* 17dh   */
+		ov518_reg_w32(sd, 0xc8,    128, 2);	/* 80h    */
+		ov518_reg_w32(sd, 0xca, 183331, 3);	/* 2cc23h */
+		ov518_reg_w32(sd, 0xcb,    746, 2);	/* 2eah   */
+		ov518_reg_w32(sd, 0xcc,   1750, 2);	/* 6d6h   */
+		ov518_reg_w32(sd, 0xcd,     45, 2);	/* 2dh    */
+		ov518_reg_w32(sd, 0xce,    851, 2);	/* 353h   */
+	}
+
+	reg_w(sd, 0x2f, 0x80);
+}
+
+/* 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 void ov519_mode_init_regs(struct sd *sd)
+{
+	static const struct ov_regvals mode_init_519_ov7670[] = {
+		{ 0x5d,	0x03 }, /* Turn off suspend mode */
+		{ 0x53,	0x9f }, /* was 9b in 1.65-1.08 */
+		{ OV519_R54_EN_CLK1, 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 */
+		{ OV519_R54_EN_CLK1, 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? */
+	};
+
+	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
+
+	/******** Set the mode ********/
+	switch (sd->sensor) {
+	default:
+		write_regvals(sd, mode_init_519, ARRAY_SIZE(mode_init_519));
+		if (sd->sensor == SEN_OV7640 ||
+		    sd->sensor == SEN_OV7648) {
+			/* Select 8-bit input mode */
+			reg_w_mask(sd, OV519_R20_DFR, 0x10, 0x10);
+		}
+		break;
+	case SEN_OV7660:
+		return;		/* done by ov519_set_mode/fr() */
+	case SEN_OV7670:
+		write_regvals(sd, mode_init_519_ov7670,
+				ARRAY_SIZE(mode_init_519_ov7670));
+		break;
+	}
+
+	reg_w(sd, OV519_R10_H_SIZE,	sd->gspca_dev.pixfmt.width >> 4);
+	reg_w(sd, OV519_R11_V_SIZE,	sd->gspca_dev.pixfmt.height >> 3);
+	if (sd->sensor == SEN_OV7670 &&
+	    sd->gspca_dev.cam.cam_mode[sd->gspca_dev.curr_mode].priv)
+		reg_w(sd, OV519_R12_X_OFFSETL, 0x04);
+	else if (sd->sensor == SEN_OV7648 &&
+	    sd->gspca_dev.cam.cam_mode[sd->gspca_dev.curr_mode].priv)
+		reg_w(sd, OV519_R12_X_OFFSETL, 0x01);
+	else
+		reg_w(sd, OV519_R12_X_OFFSETL, 0x00);
+	reg_w(sd, OV519_R13_X_OFFSETH,	0x00);
+	reg_w(sd, OV519_R14_Y_OFFSETL,	0x00);
+	reg_w(sd, OV519_R15_Y_OFFSETH,	0x00);
+	reg_w(sd, OV519_R16_DIVIDER,	0x00);
+	reg_w(sd, OV519_R25_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;
+	switch (sd->sensor) {
+	case SEN_OV7640:
+	case SEN_OV7648:
+		switch (sd->frame_rate) {
+		default:
+/*		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;
+		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;
+		}
+		break;
+	case 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;
+		}
+		break;
+	case SEN_OV7670:		/* guesses, based on 7640 */
+		PDEBUG(D_STREAM, "Setting framerate to %d fps",
+				 (sd->frame_rate == 0) ? 15 : sd->frame_rate);
+		reg_w(sd, 0xa4, 0x10);
+		switch (sd->frame_rate) {
+		case 30:
+			reg_w(sd, 0x23, 0xff);
+			break;
+		case 20:
+			reg_w(sd, 0x23, 0x1b);
+			break;
+		default:
+/*		case 15: */
+			reg_w(sd, 0x23, 0xff);
+			sd->clockdiv = 1;
+			break;
+		}
+		break;
+	}
+}
+
+static void mode_init_ov_sensor_regs(struct sd *sd)
+{
+	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
+	int qvga, xstart, xend, ystart, yend;
+	u8 v;
+
+	qvga = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv & 1;
+
+	/******** Mode (VGA/QVGA) and sensor specific regs ********/
+	switch (sd->sensor) {
+	case SEN_OV2610:
+		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);
+		return;
+	case SEN_OV2610AE: {
+		u8 v;
+
+		/* frame rates:
+		 *	10fps / 5 fps for 1600x1200
+		 *	40fps / 20fps for 800x600
+		 */
+		v = 80;
+		if (qvga) {
+			if (sd->frame_rate < 25)
+				v = 0x81;
+		} else {
+			if (sd->frame_rate < 10)
+				v = 0x81;
+		}
+		i2c_w(sd, 0x11, v);
+		i2c_w(sd, 0x12, qvga ? 0x60 : 0x20);
+		return;
+	    }
+	case SEN_OV3610:
+		if (qvga) {
+			xstart = (1040 - gspca_dev->pixfmt.width) / 2 +
+				(0x1f << 4);
+			ystart = (776 - gspca_dev->pixfmt.height) / 2;
+		} else {
+			xstart = (2076 - gspca_dev->pixfmt.width) / 2 +
+				(0x10 << 4);
+			ystart = (1544 - gspca_dev->pixfmt.height) / 2;
+		}
+		xend = xstart + gspca_dev->pixfmt.width;
+		yend = ystart + gspca_dev->pixfmt.height;
+		/* Writing to the COMH register resets the other windowing regs
+		   to their default values, so we must do this first. */
+		i2c_w_mask(sd, 0x12, qvga ? 0x40 : 0x00, 0xf0);
+		i2c_w_mask(sd, 0x32,
+			   (((xend >> 1) & 7) << 3) | ((xstart >> 1) & 7),
+			   0x3f);
+		i2c_w_mask(sd, 0x03,
+			   (((yend >> 1) & 3) << 2) | ((ystart >> 1) & 3),
+			   0x0f);
+		i2c_w(sd, 0x17, xstart >> 4);
+		i2c_w(sd, 0x18, xend >> 4);
+		i2c_w(sd, 0x19, ystart >> 3);
+		i2c_w(sd, 0x1a, yend >> 3);
+		return;
+	case SEN_OV8610:
+		/* For OV8610 qvga means qsvga */
+		i2c_w_mask(sd, OV7610_REG_COM_C, qvga ? (1 << 5) : 0, 1 << 5);
+		i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */
+		i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */
+		i2c_w_mask(sd, 0x2d, 0x00, 0x40); /* from windrv 090403 */
+		i2c_w_mask(sd, 0x28, 0x20, 0x20); /* progressive mode on */
+		break;
+	case SEN_OV7610:
+		i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20);
+		i2c_w(sd, 0x35, qvga ? 0x1e : 0x9e);
+		i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */
+		i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */
+		break;
+	case SEN_OV7620:
+	case SEN_OV7620AE:
+	case SEN_OV76BE:
+		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 ? 0xb0 : 0x90, 0xf0);
+		i2c_w_mask(sd, 0x74, qvga ? 0x20 : 0x00, 0x20);
+		i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */
+		i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */
+		if (sd->sensor == SEN_OV76BE)
+			i2c_w(sd, 0x35, qvga ? 0x1e : 0x9e);
+		break;
+	case SEN_OV7640:
+	case SEN_OV7648:
+		i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20);
+		i2c_w_mask(sd, 0x28, qvga ? 0x00 : 0x20, 0x20);
+		/* Setting this undocumented bit in qvga mode removes a very
+		   annoying vertical shaking of the image */
+		i2c_w_mask(sd, 0x2d, qvga ? 0x40 : 0x00, 0x40);
+		/* Unknown */
+		i2c_w_mask(sd, 0x67, qvga ? 0xf0 : 0x90, 0xf0);
+		/* Allow higher automatic gain (to allow higher framerates) */
+		i2c_w_mask(sd, 0x74, qvga ? 0x20 : 0x00, 0x20);
+		i2c_w_mask(sd, 0x12, 0x04, 0x04); /* AWB: 1 */
+		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_R12_COM7,
+			 qvga ? OV7670_COM7_FMT_QVGA : OV7670_COM7_FMT_VGA,
+			 OV7670_COM7_FMT_MASK);
+		i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */
+		i2c_w_mask(sd, OV7670_R13_COM8, OV7670_COM8_AWB,
+				OV7670_COM8_AWB);
+		if (qvga) {		/* QVGA from ov7670.c by
+					 * Jonathan Corbet */
+			xstart = 164;
+			xend = 28;
+			ystart = 14;
+			yend = 494;
+		} else {		/* VGA */
+			xstart = 158;
+			xend = 14;
+			ystart = 10;
+			yend = 490;
+		}
+		/* OV7670 hardware window registers are split across
+		 * multiple locations */
+		i2c_w(sd, OV7670_R17_HSTART, xstart >> 3);
+		i2c_w(sd, OV7670_R18_HSTOP, xend >> 3);
+		v = i2c_r(sd, OV7670_R32_HREF);
+		v = (v & 0xc0) | ((xend & 0x7) << 3) | (xstart & 0x07);
+		msleep(10);	/* need to sleep between read and write to
+				 * same reg! */
+		i2c_w(sd, OV7670_R32_HREF, v);
+
+		i2c_w(sd, OV7670_R19_VSTART, ystart >> 2);
+		i2c_w(sd, OV7670_R1A_VSTOP, yend >> 2);
+		v = i2c_r(sd, OV7670_R03_VREF);
+		v = (v & 0xc0) | ((yend & 0x3) << 2) | (ystart & 0x03);
+		msleep(10);	/* need to sleep between read and write to
+				 * same reg! */
+		i2c_w(sd, OV7670_R03_VREF, v);
+		break;
+	case SEN_OV6620:
+		i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20);
+		i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */
+		i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */
+		break;
+	case SEN_OV6630:
+	case SEN_OV66308AF:
+		i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20);
+		i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */
+		break;
+	case SEN_OV9600: {
+		const struct ov_i2c_regvals *vals;
+		static const struct ov_i2c_regvals sxga_15[] = {
+			{0x11, 0x80}, {0x14, 0x3e}, {0x24, 0x85}, {0x25, 0x75}
+		};
+		static const struct ov_i2c_regvals sxga_7_5[] = {
+			{0x11, 0x81}, {0x14, 0x3e}, {0x24, 0x85}, {0x25, 0x75}
+		};
+		static const struct ov_i2c_regvals vga_30[] = {
+			{0x11, 0x81}, {0x14, 0x7e}, {0x24, 0x70}, {0x25, 0x60}
+		};
+		static const struct ov_i2c_regvals vga_15[] = {
+			{0x11, 0x83}, {0x14, 0x3e}, {0x24, 0x80}, {0x25, 0x70}
+		};
+
+		/* frame rates:
+		 *	15fps / 7.5 fps for 1280x1024
+		 *	30fps / 15fps for 640x480
+		 */
+		i2c_w_mask(sd, 0x12, qvga ? 0x40 : 0x00, 0x40);
+		if (qvga)
+			vals = sd->frame_rate < 30 ? vga_15 : vga_30;
+		else
+			vals = sd->frame_rate < 15 ? sxga_7_5 : sxga_15;
+		write_i2c_regvals(sd, vals, ARRAY_SIZE(sxga_15));
+		return;
+	    }
+	default:
+		return;
+	}
+
+	/******** Clock programming ********/
+	i2c_w(sd, 0x11, sd->clockdiv);
+}
+
+/* this function works for bridge ov519 and sensors ov7660 and ov7670 only */
+static void sethvflip(struct gspca_dev *gspca_dev, s32 hflip, s32 vflip)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (sd->gspca_dev.streaming)
+		reg_w(sd, OV519_R51_RESET1, 0x0f);	/* block stream */
+	i2c_w_mask(sd, OV7670_R1E_MVFP,
+		OV7670_MVFP_MIRROR * hflip | OV7670_MVFP_VFLIP * vflip,
+		OV7670_MVFP_MIRROR | OV7670_MVFP_VFLIP);
+	if (sd->gspca_dev.streaming)
+		reg_w(sd, OV519_R51_RESET1, 0x00);	/* restart stream */
+}
+
+static void set_ov_sensor_window(struct sd *sd)
+{
+	struct gspca_dev *gspca_dev;
+	int qvga, crop;
+	int hwsbase, hwebase, vwsbase, vwebase, hwscale, vwscale;
+
+	/* mode setup is fully handled in mode_init_ov_sensor_regs for these */
+	switch (sd->sensor) {
+	case SEN_OV2610:
+	case SEN_OV2610AE:
+	case SEN_OV3610:
+	case SEN_OV7670:
+	case SEN_OV9600:
+		mode_init_ov_sensor_regs(sd);
+		return;
+	case SEN_OV7660:
+		ov519_set_mode(sd);
+		ov519_set_fr(sd);
+		return;
+	}
+
+	gspca_dev = &sd->gspca_dev;
+	qvga = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv & 1;
+	crop = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv & 2;
+
+	/* 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:
+	case SEN_OV66308AF:
+		hwsbase = 0x38;
+		hwebase = 0x3a;
+		vwsbase = 0x05;
+		vwebase = 0x06;
+		if (sd->sensor == SEN_OV66308AF && qvga)
+			/* HDG: this fixes U and V getting swapped */
+			hwsbase++;
+		if (crop) {
+			hwsbase += 8;
+			hwebase += 8;
+			vwsbase += 11;
+			vwebase += 11;
+		}
+		break;
+	case SEN_OV7620:
+	case SEN_OV7620AE:
+		hwsbase = 0x2f;		/* From 7620.SET (spec is wrong) */
+		hwebase = 0x2f;
+		vwsbase = vwebase = 0x05;
+		break;
+	case SEN_OV7640:
+	case SEN_OV7648:
+		hwsbase = 0x1a;
+		hwebase = 0x1a;
+		vwsbase = vwebase = 0x03;
+		break;
+	default:
+		return;
+	}
+
+	switch (sd->sensor) {
+	case SEN_OV6620:
+	case SEN_OV6630:
+	case SEN_OV66308AF:
+		if (qvga) {		/* QCIF */
+			hwscale = 0;
+			vwscale = 0;
+		} else {		/* CIF */
+			hwscale = 1;
+			vwscale = 1;	/* The datasheet says 0;
+					 * it's wrong */
+		}
+		break;
+	case SEN_OV8610:
+		if (qvga) {		/* QSVGA */
+			hwscale = 1;
+			vwscale = 1;
+		} else {		/* SVGA */
+			hwscale = 2;
+			vwscale = 2;
+		}
+		break;
+	default:			/* SEN_OV7xx0 */
+		if (qvga) {		/* QVGA */
+			hwscale = 1;
+			vwscale = 0;
+		} else {		/* VGA */
+			hwscale = 2;
+			vwscale = 1;
+		}
+	}
+
+	mode_init_ov_sensor_regs(sd);
+
+	i2c_w(sd, 0x17, hwsbase);
+	i2c_w(sd, 0x18, hwebase + (sd->sensor_width >> hwscale));
+	i2c_w(sd, 0x19, vwsbase);
+	i2c_w(sd, 0x1a, vwebase + (sd->sensor_height >> vwscale));
+}
+
+/* -- start the camera -- */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	/* Default for most bridges, allow bridge_mode_init_regs to override */
+	sd->sensor_width = sd->gspca_dev.pixfmt.width;
+	sd->sensor_height = sd->gspca_dev.pixfmt.height;
+
+	switch (sd->bridge) {
+	case BRIDGE_OV511:
+	case BRIDGE_OV511PLUS:
+		ov511_mode_init_regs(sd);
+		break;
+	case BRIDGE_OV518:
+	case BRIDGE_OV518PLUS:
+		ov518_mode_init_regs(sd);
+		break;
+	case BRIDGE_OV519:
+		ov519_mode_init_regs(sd);
+		break;
+	/* case BRIDGE_OVFX2: nothing to do */
+	case BRIDGE_W9968CF:
+		w9968cf_mode_init_regs(sd);
+		break;
+	}
+
+	set_ov_sensor_window(sd);
+
+	/* Force clear snapshot state in case the snapshot button was
+	   pressed while we weren't streaming */
+	sd->snapshot_needs_reset = 1;
+	sd_reset_snapshot(gspca_dev);
+
+	sd->first_frame = 3;
+
+	ov51x_restart(sd);
+	ov51x_led_control(sd, 1);
+	return gspca_dev->usb_err;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	ov51x_stop(sd);
+	ov51x_led_control(sd, 0);
+}
+
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (!sd->gspca_dev.present)
+		return;
+	if (sd->bridge == BRIDGE_W9968CF)
+		w9968cf_stop0(sd);
+
+#if IS_ENABLED(CONFIG_INPUT)
+	/* If the last button state is pressed, release it now! */
+	if (sd->snapshot_pressed) {
+		input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
+		input_sync(gspca_dev->input_dev);
+		sd->snapshot_pressed = 0;
+	}
+#endif
+	if (sd->bridge == BRIDGE_OV519)
+		reg_w(sd, OV519_R57_SNAPSHOT, 0x23);
+}
+
+static void ov51x_handle_button(struct gspca_dev *gspca_dev, u8 state)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (sd->snapshot_pressed != state) {
+#if IS_ENABLED(CONFIG_INPUT)
+		input_report_key(gspca_dev->input_dev, KEY_CAMERA, state);
+		input_sync(gspca_dev->input_dev);
+#endif
+		if (state)
+			sd->snapshot_needs_reset = 1;
+
+		sd->snapshot_pressed = state;
+	} else {
+		/* On the ov511 / ov519 we need to reset the button state
+		   multiple times, as resetting does not work as long as the
+		   button stays pressed */
+		switch (sd->bridge) {
+		case BRIDGE_OV511:
+		case BRIDGE_OV511PLUS:
+		case BRIDGE_OV519:
+			if (state)
+				sd->snapshot_needs_reset = 1;
+			break;
+		}
+	}
+}
+
+static void ov511_pkt_scan(struct gspca_dev *gspca_dev,
+			u8 *in,			/* isoc packet */
+			int len)		/* iso packet length */
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	/* SOF/EOF packets have 1st to 8th bytes zeroed and the 9th
+	 * byte non-zero. The EOF packet has image width/height in the
+	 * 10th and 11th bytes. The 9th byte is given as follows:
+	 *
+	 * bit 7: EOF
+	 *     6: compression enabled
+	 *     5: 422/420/400 modes
+	 *     4: 422/420/400 modes
+	 *     3: 1
+	 *     2: snapshot button on
+	 *     1: snapshot frame
+	 *     0: even/odd field
+	 */
+	if (!(in[0] | in[1] | in[2] | in[3] | in[4] | in[5] | in[6] | in[7]) &&
+	    (in[8] & 0x08)) {
+		ov51x_handle_button(gspca_dev, (in[8] >> 2) & 1);
+		if (in[8] & 0x80) {
+			/* Frame end */
+			if ((in[9] + 1) * 8 != gspca_dev->pixfmt.width ||
+			    (in[10] + 1) * 8 != gspca_dev->pixfmt.height) {
+				PERR("Invalid frame size, got: %dx%d,"
+					" requested: %dx%d\n",
+					(in[9] + 1) * 8, (in[10] + 1) * 8,
+					gspca_dev->pixfmt.width,
+					gspca_dev->pixfmt.height);
+				gspca_dev->last_packet_type = DISCARD_PACKET;
+				return;
+			}
+			/* Add 11 byte footer to frame, might be useful */
+			gspca_frame_add(gspca_dev, LAST_PACKET, in, 11);
+			return;
+		} else {
+			/* Frame start */
+			gspca_frame_add(gspca_dev, FIRST_PACKET, in, 0);
+			sd->packet_nr = 0;
+		}
+	}
+
+	/* Ignore the packet number */
+	len--;
+
+	/* intermediate packet */
+	gspca_frame_add(gspca_dev, INTER_PACKET, in, len);
+}
+
+static void ov518_pkt_scan(struct gspca_dev *gspca_dev,
+			u8 *data,			/* isoc packet */
+			int len)			/* iso packet length */
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	/* A false positive here is likely, until OVT gives me
+	 * the definitive SOF/EOF format */
+	if ((!(data[0] | data[1] | data[2] | data[3] | data[5])) && data[6]) {
+		ov51x_handle_button(gspca_dev, (data[6] >> 1) & 1);
+		gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+		gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0);
+		sd->packet_nr = 0;
+	}
+
+	if (gspca_dev->last_packet_type == DISCARD_PACKET)
+		return;
+
+	/* Does this device use packet numbers ? */
+	if (len & 7) {
+		len--;
+		if (sd->packet_nr == data[len])
+			sd->packet_nr++;
+		/* The last few packets of the frame (which are all 0's
+		   except that they may contain part of the footer), are
+		   numbered 0 */
+		else if (sd->packet_nr == 0 || data[len]) {
+			PERR("Invalid packet nr: %d (expect: %d)",
+				(int)data[len], (int)sd->packet_nr);
+			gspca_dev->last_packet_type = DISCARD_PACKET;
+			return;
+		}
+	}
+
+	/* intermediate packet */
+	gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+}
+
+static void ov519_pkt_scan(struct gspca_dev *gspca_dev,
+			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 */
+			/* Don't check the button state here, as the state
+			   usually (always ?) changes at EOF and checking it
+			   here leads to unnecessary snapshot state resets. */
+#define HDRSZ 16
+			data += HDRSZ;
+			len -= HDRSZ;
+#undef HDRSZ
+			if (data[0] == 0xff || data[1] == 0xd8)
+				gspca_frame_add(gspca_dev, FIRST_PACKET,
+						data, len);
+			else
+				gspca_dev->last_packet_type = DISCARD_PACKET;
+			return;
+		case 0x51:		/* end of frame */
+			ov51x_handle_button(gspca_dev, data[11] & 1);
+			if (data[9] != 0)
+				gspca_dev->last_packet_type = DISCARD_PACKET;
+			gspca_frame_add(gspca_dev, LAST_PACKET,
+					NULL, 0);
+			return;
+		}
+	}
+
+	/* intermediate packet */
+	gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+}
+
+static void ovfx2_pkt_scan(struct gspca_dev *gspca_dev,
+			u8 *data,			/* isoc packet */
+			int len)			/* iso packet length */
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+
+	/* A short read signals EOF */
+	if (len < gspca_dev->cam.bulk_size) {
+		/* If the frame is short, and it is one of the first ones
+		   the sensor and bridge are still syncing, so drop it. */
+		if (sd->first_frame) {
+			sd->first_frame--;
+			if (gspca_dev->image_len <
+				  sd->gspca_dev.pixfmt.width *
+					sd->gspca_dev.pixfmt.height)
+				gspca_dev->last_packet_type = DISCARD_PACKET;
+		}
+		gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+		gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0);
+	}
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+			u8 *data,			/* isoc packet */
+			int len)			/* iso packet length */
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	switch (sd->bridge) {
+	case BRIDGE_OV511:
+	case BRIDGE_OV511PLUS:
+		ov511_pkt_scan(gspca_dev, data, len);
+		break;
+	case BRIDGE_OV518:
+	case BRIDGE_OV518PLUS:
+		ov518_pkt_scan(gspca_dev, data, len);
+		break;
+	case BRIDGE_OV519:
+		ov519_pkt_scan(gspca_dev, data, len);
+		break;
+	case BRIDGE_OVFX2:
+		ovfx2_pkt_scan(gspca_dev, data, len);
+		break;
+	case BRIDGE_W9968CF:
+		w9968cf_pkt_scan(gspca_dev, data, len);
+		break;
+	}
+}
+
+/* -- management routines -- */
+
+static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	static const struct ov_i2c_regvals brit_7660[][7] = {
+		{{0x0f, 0x6a}, {0x24, 0x40}, {0x25, 0x2b}, {0x26, 0x90},
+			{0x27, 0xe0}, {0x28, 0xe0}, {0x2c, 0xe0}},
+		{{0x0f, 0x6a}, {0x24, 0x50}, {0x25, 0x40}, {0x26, 0xa1},
+			{0x27, 0xc0}, {0x28, 0xc0}, {0x2c, 0xc0}},
+		{{0x0f, 0x6a}, {0x24, 0x68}, {0x25, 0x58}, {0x26, 0xc2},
+			{0x27, 0xa0}, {0x28, 0xa0}, {0x2c, 0xa0}},
+		{{0x0f, 0x6a}, {0x24, 0x70}, {0x25, 0x68}, {0x26, 0xd3},
+			{0x27, 0x80}, {0x28, 0x80}, {0x2c, 0x80}},
+		{{0x0f, 0x6a}, {0x24, 0x80}, {0x25, 0x70}, {0x26, 0xd3},
+			{0x27, 0x20}, {0x28, 0x20}, {0x2c, 0x20}},
+		{{0x0f, 0x6a}, {0x24, 0x88}, {0x25, 0x78}, {0x26, 0xd3},
+			{0x27, 0x40}, {0x28, 0x40}, {0x2c, 0x40}},
+		{{0x0f, 0x6a}, {0x24, 0x90}, {0x25, 0x80}, {0x26, 0xd4},
+			{0x27, 0x60}, {0x28, 0x60}, {0x2c, 0x60}}
+	};
+
+	switch (sd->sensor) {
+	case SEN_OV8610:
+	case SEN_OV7610:
+	case SEN_OV76BE:
+	case SEN_OV6620:
+	case SEN_OV6630:
+	case SEN_OV66308AF:
+	case SEN_OV7640:
+	case SEN_OV7648:
+		i2c_w(sd, OV7610_REG_BRT, val);
+		break;
+	case SEN_OV7620:
+	case SEN_OV7620AE:
+		i2c_w(sd, OV7610_REG_BRT, val);
+		break;
+	case SEN_OV7660:
+		write_i2c_regvals(sd, brit_7660[val],
+				ARRAY_SIZE(brit_7660[0]));
+		break;
+	case SEN_OV7670:
+/*win trace
+ *		i2c_w_mask(sd, OV7670_R13_COM8, 0, OV7670_COM8_AEC); */
+		i2c_w(sd, OV7670_R55_BRIGHT, ov7670_abs_to_sm(val));
+		break;
+	}
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	static const struct ov_i2c_regvals contrast_7660[][31] = {
+		{{0x6c, 0xf0}, {0x6d, 0xf0}, {0x6e, 0xf8}, {0x6f, 0xa0},
+		 {0x70, 0x58}, {0x71, 0x38}, {0x72, 0x30}, {0x73, 0x30},
+		 {0x74, 0x28}, {0x75, 0x28}, {0x76, 0x24}, {0x77, 0x24},
+		 {0x78, 0x22}, {0x79, 0x28}, {0x7a, 0x2a}, {0x7b, 0x34},
+		 {0x7c, 0x0f}, {0x7d, 0x1e}, {0x7e, 0x3d}, {0x7f, 0x65},
+		 {0x80, 0x70}, {0x81, 0x77}, {0x82, 0x7d}, {0x83, 0x83},
+		 {0x84, 0x88}, {0x85, 0x8d}, {0x86, 0x96}, {0x87, 0x9f},
+		 {0x88, 0xb0}, {0x89, 0xc4}, {0x8a, 0xd9}},
+		{{0x6c, 0xf0}, {0x6d, 0xf0}, {0x6e, 0xf8}, {0x6f, 0x94},
+		 {0x70, 0x58}, {0x71, 0x40}, {0x72, 0x30}, {0x73, 0x30},
+		 {0x74, 0x30}, {0x75, 0x30}, {0x76, 0x2c}, {0x77, 0x24},
+		 {0x78, 0x22}, {0x79, 0x28}, {0x7a, 0x2a}, {0x7b, 0x31},
+		 {0x7c, 0x0f}, {0x7d, 0x1e}, {0x7e, 0x3d}, {0x7f, 0x62},
+		 {0x80, 0x6d}, {0x81, 0x75}, {0x82, 0x7b}, {0x83, 0x81},
+		 {0x84, 0x87}, {0x85, 0x8d}, {0x86, 0x98}, {0x87, 0xa1},
+		 {0x88, 0xb2}, {0x89, 0xc6}, {0x8a, 0xdb}},
+		{{0x6c, 0xf0}, {0x6d, 0xf0}, {0x6e, 0xf0}, {0x6f, 0x84},
+		 {0x70, 0x58}, {0x71, 0x48}, {0x72, 0x40}, {0x73, 0x40},
+		 {0x74, 0x28}, {0x75, 0x28}, {0x76, 0x28}, {0x77, 0x24},
+		 {0x78, 0x26}, {0x79, 0x28}, {0x7a, 0x28}, {0x7b, 0x34},
+		 {0x7c, 0x0f}, {0x7d, 0x1e}, {0x7e, 0x3c}, {0x7f, 0x5d},
+		 {0x80, 0x68}, {0x81, 0x71}, {0x82, 0x79}, {0x83, 0x81},
+		 {0x84, 0x86}, {0x85, 0x8b}, {0x86, 0x95}, {0x87, 0x9e},
+		 {0x88, 0xb1}, {0x89, 0xc5}, {0x8a, 0xd9}},
+		{{0x6c, 0xf0}, {0x6d, 0xf0}, {0x6e, 0xf0}, {0x6f, 0x70},
+		 {0x70, 0x58}, {0x71, 0x58}, {0x72, 0x48}, {0x73, 0x48},
+		 {0x74, 0x38}, {0x75, 0x40}, {0x76, 0x34}, {0x77, 0x34},
+		 {0x78, 0x2e}, {0x79, 0x28}, {0x7a, 0x24}, {0x7b, 0x22},
+		 {0x7c, 0x0f}, {0x7d, 0x1e}, {0x7e, 0x3c}, {0x7f, 0x58},
+		 {0x80, 0x63}, {0x81, 0x6e}, {0x82, 0x77}, {0x83, 0x80},
+		 {0x84, 0x87}, {0x85, 0x8f}, {0x86, 0x9c}, {0x87, 0xa9},
+		 {0x88, 0xc0}, {0x89, 0xd4}, {0x8a, 0xe6}},
+		{{0x6c, 0xa0}, {0x6d, 0xf0}, {0x6e, 0x90}, {0x6f, 0x80},
+		 {0x70, 0x70}, {0x71, 0x80}, {0x72, 0x60}, {0x73, 0x60},
+		 {0x74, 0x58}, {0x75, 0x60}, {0x76, 0x4c}, {0x77, 0x38},
+		 {0x78, 0x38}, {0x79, 0x2a}, {0x7a, 0x20}, {0x7b, 0x0e},
+		 {0x7c, 0x0a}, {0x7d, 0x14}, {0x7e, 0x26}, {0x7f, 0x46},
+		 {0x80, 0x54}, {0x81, 0x64}, {0x82, 0x70}, {0x83, 0x7c},
+		 {0x84, 0x87}, {0x85, 0x93}, {0x86, 0xa6}, {0x87, 0xb4},
+		 {0x88, 0xd0}, {0x89, 0xe5}, {0x8a, 0xf5}},
+		{{0x6c, 0x60}, {0x6d, 0x80}, {0x6e, 0x60}, {0x6f, 0x80},
+		 {0x70, 0x80}, {0x71, 0x80}, {0x72, 0x88}, {0x73, 0x30},
+		 {0x74, 0x70}, {0x75, 0x68}, {0x76, 0x64}, {0x77, 0x50},
+		 {0x78, 0x3c}, {0x79, 0x22}, {0x7a, 0x10}, {0x7b, 0x08},
+		 {0x7c, 0x06}, {0x7d, 0x0e}, {0x7e, 0x1a}, {0x7f, 0x3a},
+		 {0x80, 0x4a}, {0x81, 0x5a}, {0x82, 0x6b}, {0x83, 0x7b},
+		 {0x84, 0x89}, {0x85, 0x96}, {0x86, 0xaf}, {0x87, 0xc3},
+		 {0x88, 0xe1}, {0x89, 0xf2}, {0x8a, 0xfa}},
+		{{0x6c, 0x20}, {0x6d, 0x40}, {0x6e, 0x20}, {0x6f, 0x60},
+		 {0x70, 0x88}, {0x71, 0xc8}, {0x72, 0xc0}, {0x73, 0xb8},
+		 {0x74, 0xa8}, {0x75, 0xb8}, {0x76, 0x80}, {0x77, 0x5c},
+		 {0x78, 0x26}, {0x79, 0x10}, {0x7a, 0x08}, {0x7b, 0x04},
+		 {0x7c, 0x02}, {0x7d, 0x06}, {0x7e, 0x0a}, {0x7f, 0x22},
+		 {0x80, 0x33}, {0x81, 0x4c}, {0x82, 0x64}, {0x83, 0x7b},
+		 {0x84, 0x90}, {0x85, 0xa7}, {0x86, 0xc7}, {0x87, 0xde},
+		 {0x88, 0xf1}, {0x89, 0xf9}, {0x8a, 0xfd}},
+	};
+
+	switch (sd->sensor) {
+	case SEN_OV7610:
+	case SEN_OV6620:
+		i2c_w(sd, OV7610_REG_CNT, val);
+		break;
+	case SEN_OV6630:
+	case SEN_OV66308AF:
+		i2c_w_mask(sd, OV7610_REG_CNT, val >> 4, 0x0f);
+		break;
+	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:
+	case SEN_OV7620AE: {
+		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_OV7660:
+		write_i2c_regvals(sd, contrast_7660[val],
+					ARRAY_SIZE(contrast_7660[0]));
+		break;
+	case SEN_OV7670:
+		/* check that this isn't just the same as ov7610 */
+		i2c_w(sd, OV7670_R56_CONTRAS, val >> 1);
+		break;
+	}
+}
+
+static void setexposure(struct gspca_dev *gspca_dev, s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	i2c_w(sd, 0x10, val);
+}
+
+static void setcolors(struct gspca_dev *gspca_dev, s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	static const struct ov_i2c_regvals colors_7660[][6] = {
+		{{0x4f, 0x28}, {0x50, 0x2a}, {0x51, 0x02}, {0x52, 0x0a},
+		 {0x53, 0x19}, {0x54, 0x23}},
+		{{0x4f, 0x47}, {0x50, 0x4a}, {0x51, 0x03}, {0x52, 0x11},
+		 {0x53, 0x2c}, {0x54, 0x3e}},
+		{{0x4f, 0x66}, {0x50, 0x6b}, {0x51, 0x05}, {0x52, 0x19},
+		 {0x53, 0x40}, {0x54, 0x59}},
+		{{0x4f, 0x84}, {0x50, 0x8b}, {0x51, 0x06}, {0x52, 0x20},
+		 {0x53, 0x53}, {0x54, 0x73}},
+		{{0x4f, 0xa3}, {0x50, 0xab}, {0x51, 0x08}, {0x52, 0x28},
+		 {0x53, 0x66}, {0x54, 0x8e}},
+	};
+
+	switch (sd->sensor) {
+	case SEN_OV8610:
+	case SEN_OV7610:
+	case SEN_OV76BE:
+	case SEN_OV6620:
+	case SEN_OV6630:
+	case SEN_OV66308AF:
+		i2c_w(sd, OV7610_REG_SAT, val);
+		break;
+	case SEN_OV7620:
+	case SEN_OV7620AE:
+		/* 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:
+	case SEN_OV7648:
+		i2c_w(sd, OV7610_REG_SAT, val & 0xf0);
+		break;
+	case SEN_OV7660:
+		write_i2c_regvals(sd, colors_7660[val],
+					ARRAY_SIZE(colors_7660[0]));
+		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;
+	}
+}
+
+static void setautobright(struct gspca_dev *gspca_dev, s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	i2c_w_mask(sd, 0x2d, val ? 0x10 : 0x00, 0x10);
+}
+
+static void setfreq_i(struct sd *sd, s32 val)
+{
+	if (sd->sensor == SEN_OV7660
+	 || sd->sensor == SEN_OV7670) {
+		switch (val) {
+		case 0: /* Banding filter disabled */
+			i2c_w_mask(sd, OV7670_R13_COM8, 0, OV7670_COM8_BFILT);
+			break;
+		case 1: /* 50 hz */
+			i2c_w_mask(sd, OV7670_R13_COM8, OV7670_COM8_BFILT,
+				   OV7670_COM8_BFILT);
+			i2c_w_mask(sd, OV7670_R3B_COM11, 0x08, 0x18);
+			break;
+		case 2: /* 60 hz */
+			i2c_w_mask(sd, OV7670_R13_COM8, OV7670_COM8_BFILT,
+				   OV7670_COM8_BFILT);
+			i2c_w_mask(sd, OV7670_R3B_COM11, 0x00, 0x18);
+			break;
+		case 3: /* Auto hz - ov7670 only */
+			i2c_w_mask(sd, OV7670_R13_COM8, OV7670_COM8_BFILT,
+				   OV7670_COM8_BFILT);
+			i2c_w_mask(sd, OV7670_R3B_COM11, OV7670_COM11_HZAUTO,
+				   0x18);
+			break;
+		}
+	} else {
+		switch (val) {
+		case 0: /* Banding filter disabled */
+			i2c_w_mask(sd, 0x2d, 0x00, 0x04);
+			i2c_w_mask(sd, 0x2a, 0x00, 0x80);
+			break;
+		case 1: /* 50 hz (filter on and framerate adj) */
+			i2c_w_mask(sd, 0x2d, 0x04, 0x04);
+			i2c_w_mask(sd, 0x2a, 0x80, 0x80);
+			/* 20 fps -> 16.667 fps */
+			if (sd->sensor == SEN_OV6620 ||
+			    sd->sensor == SEN_OV6630 ||
+			    sd->sensor == SEN_OV66308AF)
+				i2c_w(sd, 0x2b, 0x5e);
+			else
+				i2c_w(sd, 0x2b, 0xac);
+			break;
+		case 2: /* 60 hz (filter on, ...) */
+			i2c_w_mask(sd, 0x2d, 0x04, 0x04);
+			if (sd->sensor == SEN_OV6620 ||
+			    sd->sensor == SEN_OV6630 ||
+			    sd->sensor == SEN_OV66308AF) {
+				/* 20 fps -> 15 fps */
+				i2c_w_mask(sd, 0x2a, 0x80, 0x80);
+				i2c_w(sd, 0x2b, 0xa8);
+			} else {
+				/* no framerate adj. */
+				i2c_w_mask(sd, 0x2a, 0x00, 0x80);
+			}
+			break;
+		}
+	}
+}
+
+static void setfreq(struct gspca_dev *gspca_dev, s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	setfreq_i(sd, val);
+
+	/* Ugly but necessary */
+	if (sd->bridge == BRIDGE_W9968CF)
+		w9968cf_set_crop_window(sd);
+}
+
+static int sd_get_jcomp(struct gspca_dev *gspca_dev,
+			struct v4l2_jpegcompression *jcomp)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (sd->bridge != BRIDGE_W9968CF)
+		return -ENOTTY;
+
+	memset(jcomp, 0, sizeof *jcomp);
+	jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual);
+	jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT | V4L2_JPEG_MARKER_DQT |
+			      V4L2_JPEG_MARKER_DRI;
+	return 0;
+}
+
+static int sd_set_jcomp(struct gspca_dev *gspca_dev,
+			const struct v4l2_jpegcompression *jcomp)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (sd->bridge != BRIDGE_W9968CF)
+		return -ENOTTY;
+
+	v4l2_ctrl_s_ctrl(sd->jpegqual, jcomp->quality);
+	return 0;
+}
+
+static int sd_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct gspca_dev *gspca_dev =
+		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+	struct sd *sd = (struct sd *)gspca_dev;
+
+	gspca_dev->usb_err = 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_AUTOGAIN:
+		gspca_dev->exposure->val = i2c_r(sd, 0x10);
+		break;
+	}
+	return 0;
+}
+
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct gspca_dev *gspca_dev =
+		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+	struct sd *sd = (struct sd *)gspca_dev;
+
+	gspca_dev->usb_err = 0;
+
+	if (!gspca_dev->streaming)
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_BRIGHTNESS:
+		setbrightness(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_CONTRAST:
+		setcontrast(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_POWER_LINE_FREQUENCY:
+		setfreq(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_AUTOBRIGHTNESS:
+		if (ctrl->is_new)
+			setautobright(gspca_dev, ctrl->val);
+		if (!ctrl->val && sd->brightness->is_new)
+			setbrightness(gspca_dev, sd->brightness->val);
+		break;
+	case V4L2_CID_SATURATION:
+		setcolors(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_HFLIP:
+		sethvflip(gspca_dev, ctrl->val, sd->vflip->val);
+		break;
+	case V4L2_CID_AUTOGAIN:
+		if (ctrl->is_new)
+			setautogain(gspca_dev, ctrl->val);
+		if (!ctrl->val && gspca_dev->exposure->is_new)
+			setexposure(gspca_dev, gspca_dev->exposure->val);
+		break;
+	case V4L2_CID_JPEG_COMPRESSION_QUALITY:
+		return -EBUSY; /* Should never happen, as we grab the ctrl */
+	}
+	return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+	.g_volatile_ctrl = sd_g_volatile_ctrl,
+	.s_ctrl = sd_s_ctrl,
+};
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *)gspca_dev;
+	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+	gspca_dev->vdev.ctrl_handler = hdl;
+	v4l2_ctrl_handler_init(hdl, 10);
+	if (valid_controls[sd->sensor].has_brightness)
+		sd->brightness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_BRIGHTNESS, 0,
+			sd->sensor == SEN_OV7660 ? 6 : 255, 1,
+			sd->sensor == SEN_OV7660 ? 3 : 127);
+	if (valid_controls[sd->sensor].has_contrast) {
+		if (sd->sensor == SEN_OV7660)
+			v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+				V4L2_CID_CONTRAST, 0, 6, 1, 3);
+		else
+			v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+				V4L2_CID_CONTRAST, 0, 255, 1,
+				(sd->sensor == SEN_OV6630 ||
+				 sd->sensor == SEN_OV66308AF) ? 200 : 127);
+	}
+	if (valid_controls[sd->sensor].has_sat)
+		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_SATURATION, 0,
+			sd->sensor == SEN_OV7660 ? 4 : 255, 1,
+			sd->sensor == SEN_OV7660 ? 2 : 127);
+	if (valid_controls[sd->sensor].has_exposure)
+		gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_EXPOSURE, 0, 255, 1, 127);
+	if (valid_controls[sd->sensor].has_hvflip) {
+		sd->hflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_HFLIP, 0, 1, 1, 0);
+		sd->vflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_VFLIP, 0, 1, 1, 0);
+	}
+	if (valid_controls[sd->sensor].has_autobright)
+		sd->autobright = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_AUTOBRIGHTNESS, 0, 1, 1, 1);
+	if (valid_controls[sd->sensor].has_autogain)
+		gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+	if (valid_controls[sd->sensor].has_freq) {
+		if (sd->sensor == SEN_OV7670)
+			sd->freq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
+				V4L2_CID_POWER_LINE_FREQUENCY,
+				V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0,
+				V4L2_CID_POWER_LINE_FREQUENCY_AUTO);
+		else
+			sd->freq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
+				V4L2_CID_POWER_LINE_FREQUENCY,
+				V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0, 0);
+	}
+	if (sd->bridge == BRIDGE_W9968CF)
+		sd->jpegqual = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_JPEG_COMPRESSION_QUALITY,
+			QUALITY_MIN, QUALITY_MAX, 1, QUALITY_DEF);
+
+	if (hdl->error) {
+		PERR("Could not initialize controls\n");
+		return hdl->error;
+	}
+	if (gspca_dev->autogain)
+		v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, true);
+	if (sd->autobright)
+		v4l2_ctrl_auto_cluster(2, &sd->autobright, 0, false);
+	if (sd->hflip)
+		v4l2_ctrl_cluster(2, &sd->hflip);
+	return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+	.name = MODULE_NAME,
+	.config = sd_config,
+	.init = sd_init,
+	.init_controls = sd_init_controls,
+	.isoc_init = sd_isoc_init,
+	.start = sd_start,
+	.stopN = sd_stopN,
+	.stop0 = sd_stop0,
+	.pkt_scan = sd_pkt_scan,
+	.dq_callback = sd_reset_snapshot,
+	.get_jcomp = sd_get_jcomp,
+	.set_jcomp = sd_set_jcomp,
+#if IS_ENABLED(CONFIG_INPUT)
+	.other_input = 1,
+#endif
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+	{USB_DEVICE(0x041e, 0x4003), .driver_info = BRIDGE_W9968CF },
+	{USB_DEVICE(0x041e, 0x4052),
+		.driver_info = BRIDGE_OV519 | BRIDGE_INVERT_LED },
+	{USB_DEVICE(0x041e, 0x405f), .driver_info = BRIDGE_OV519 },
+	{USB_DEVICE(0x041e, 0x4060), .driver_info = BRIDGE_OV519 },
+	{USB_DEVICE(0x041e, 0x4061), .driver_info = BRIDGE_OV519 },
+	{USB_DEVICE(0x041e, 0x4064), .driver_info = BRIDGE_OV519 },
+	{USB_DEVICE(0x041e, 0x4067), .driver_info = BRIDGE_OV519 },
+	{USB_DEVICE(0x041e, 0x4068), .driver_info = BRIDGE_OV519 },
+	{USB_DEVICE(0x045e, 0x028c),
+		.driver_info = BRIDGE_OV519 | BRIDGE_INVERT_LED },
+	{USB_DEVICE(0x054c, 0x0154), .driver_info = BRIDGE_OV519 },
+	{USB_DEVICE(0x054c, 0x0155), .driver_info = BRIDGE_OV519 },
+	{USB_DEVICE(0x05a9, 0x0511), .driver_info = BRIDGE_OV511 },
+	{USB_DEVICE(0x05a9, 0x0518), .driver_info = BRIDGE_OV518 },
+	{USB_DEVICE(0x05a9, 0x0519),
+		.driver_info = BRIDGE_OV519 | BRIDGE_INVERT_LED },
+	{USB_DEVICE(0x05a9, 0x0530),
+		.driver_info = BRIDGE_OV519 | BRIDGE_INVERT_LED },
+	{USB_DEVICE(0x05a9, 0x2800), .driver_info = BRIDGE_OVFX2 },
+	{USB_DEVICE(0x05a9, 0x4519), .driver_info = BRIDGE_OV519 },
+	{USB_DEVICE(0x05a9, 0x8519), .driver_info = BRIDGE_OV519 },
+	{USB_DEVICE(0x05a9, 0xa511), .driver_info = BRIDGE_OV511PLUS },
+	{USB_DEVICE(0x05a9, 0xa518), .driver_info = BRIDGE_OV518PLUS },
+	{USB_DEVICE(0x0813, 0x0002), .driver_info = BRIDGE_OV511PLUS },
+	{USB_DEVICE(0x0b62, 0x0059), .driver_info = BRIDGE_OVFX2 },
+	{USB_DEVICE(0x0e96, 0xc001), .driver_info = BRIDGE_OVFX2 },
+	{USB_DEVICE(0x1046, 0x9967), .driver_info = BRIDGE_W9968CF },
+	{USB_DEVICE(0x8020, 0xef04), .driver_info = BRIDGE_OVFX2 },
+	{}
+};
+
+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,
+#ifdef CONFIG_PM
+	.suspend = gspca_suspend,
+	.resume = gspca_resume,
+	.reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
+
+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/usb/gspca/ov534.c b/drivers/media/usb/gspca/ov534.c
new file mode 100644
index 0000000..bfff1d1
--- /dev/null
+++ b/drivers/media/usb/gspca/ov534.c
@@ -0,0 +1,1550 @@
+/*
+ * ov534-ov7xxx gspca driver
+ *
+ * Copyright (C) 2008 Antonio Ospite <ospite@studenti.unina.it>
+ * Copyright (C) 2008 Jim Paris <jim@jtan.com>
+ * Copyright (C) 2009 Jean-Francois Moine http://moinejf.free.fr
+ *
+ * Based on a prototype written by Mark Ferrell <majortrips@gmail.com>
+ * USB protocol reverse engineered by Jim Paris <jim@jtan.com>
+ * https://jim.sh/svn/jim/devl/playstation/ps3/eye/test/
+ *
+ * PS3 Eye camera enhanced by Richard Kaswy http://kaswy.free.fr
+ * PS3 Eye camera - brightness, contrast, awb, agc, aec controls
+ *                  added by Max Thrun <bear24rw@gmail.com>
+ * PS3 Eye camera - FPS range extended by Joseph Howse
+ *                  <josephhowse@nummist.com> http://nummist.com
+ *
+ * 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "ov534"
+
+#include "gspca.h"
+
+#include <linux/fixp-arith.h>
+#include <media/v4l2-ctrls.h>
+
+#define OV534_REG_ADDRESS	0xf1	/* sensor address */
+#define OV534_REG_SUBADDR	0xf2
+#define OV534_REG_WRITE		0xf3
+#define OV534_REG_READ		0xf4
+#define OV534_REG_OPERATION	0xf5
+#define OV534_REG_STATUS	0xf6
+
+#define OV534_OP_WRITE_3	0x37
+#define OV534_OP_WRITE_2	0x33
+#define OV534_OP_READ_2		0xf9
+
+#define CTRL_TIMEOUT 500
+
+MODULE_AUTHOR("Antonio Ospite <ospite@studenti.unina.it>");
+MODULE_DESCRIPTION("GSPCA/OV534 USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* specific webcam descriptor */
+struct sd {
+	struct gspca_dev gspca_dev;	/* !! must be the first item */
+
+	struct v4l2_ctrl_handler ctrl_handler;
+	struct v4l2_ctrl *hue;
+	struct v4l2_ctrl *saturation;
+	struct v4l2_ctrl *brightness;
+	struct v4l2_ctrl *contrast;
+	struct { /* gain control cluster */
+		struct v4l2_ctrl *autogain;
+		struct v4l2_ctrl *gain;
+	};
+	struct v4l2_ctrl *autowhitebalance;
+	struct { /* exposure control cluster */
+		struct v4l2_ctrl *autoexposure;
+		struct v4l2_ctrl *exposure;
+	};
+	struct v4l2_ctrl *sharpness;
+	struct v4l2_ctrl *hflip;
+	struct v4l2_ctrl *vflip;
+	struct v4l2_ctrl *plfreq;
+
+	__u32 last_pts;
+	u16 last_fid;
+	u8 frame_rate;
+
+	u8 sensor;
+};
+enum sensors {
+	SENSOR_OV767x,
+	SENSOR_OV772x,
+	NSENSORS
+};
+
+static int sd_start(struct gspca_dev *gspca_dev);
+static void sd_stopN(struct gspca_dev *gspca_dev);
+
+
+static const struct v4l2_pix_format ov772x_mode[] = {
+	{320, 240, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE,
+	 .bytesperline = 320 * 2,
+	 .sizeimage = 320 * 240 * 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},
+};
+static const struct v4l2_pix_format ov767x_mode[] = {
+	{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+		.bytesperline = 320,
+		.sizeimage = 320 * 240 * 3 / 8 + 590,
+		.colorspace = V4L2_COLORSPACE_JPEG},
+	{640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+		.bytesperline = 640,
+		.sizeimage = 640 * 480 * 3 / 8 + 590,
+		.colorspace = V4L2_COLORSPACE_JPEG},
+};
+
+static const u8 qvga_rates[] = {187, 150, 137, 125, 100, 75, 60, 50, 37, 30};
+static const u8 vga_rates[] = {60, 50, 40, 30, 15};
+
+static const struct framerates ov772x_framerates[] = {
+	{ /* 320x240 */
+		.rates = qvga_rates,
+		.nrates = ARRAY_SIZE(qvga_rates),
+	},
+	{ /* 640x480 */
+		.rates = vga_rates,
+		.nrates = ARRAY_SIZE(vga_rates),
+	},
+};
+
+struct reg_array {
+	const u8 (*val)[2];
+	int len;
+};
+
+static const u8 bridge_init_767x[][2] = {
+/* comments from the ms-win file apollo7670.set */
+/* str1 */
+	{0xf1, 0x42},
+	{0x88, 0xf8},
+	{0x89, 0xff},
+	{0x76, 0x03},
+	{0x92, 0x03},
+	{0x95, 0x10},
+	{0xe2, 0x00},
+	{0xe7, 0x3e},
+	{0x8d, 0x1c},
+	{0x8e, 0x00},
+	{0x8f, 0x00},
+	{0x1f, 0x00},
+	{0xc3, 0xf9},
+	{0x89, 0xff},
+	{0x88, 0xf8},
+	{0x76, 0x03},
+	{0x92, 0x01},
+	{0x93, 0x18},
+	{0x1c, 0x00},
+	{0x1d, 0x48},
+	{0x1d, 0x00},
+	{0x1d, 0xff},
+	{0x1d, 0x02},
+	{0x1d, 0x58},
+	{0x1d, 0x00},
+	{0x1c, 0x0a},
+	{0x1d, 0x0a},
+	{0x1d, 0x0e},
+	{0xc0, 0x50},	/* HSize 640 */
+	{0xc1, 0x3c},	/* VSize 480 */
+	{0x34, 0x05},	/* enable Audio Suspend mode */
+	{0xc2, 0x0c},	/* Input YUV */
+	{0xc3, 0xf9},	/* enable PRE */
+	{0x34, 0x05},	/* enable Audio Suspend mode */
+	{0xe7, 0x2e},	/* this solves failure of "SuspendResumeTest" */
+	{0x31, 0xf9},	/* enable 1.8V Suspend */
+	{0x35, 0x02},	/* turn on JPEG */
+	{0xd9, 0x10},
+	{0x25, 0x42},	/* GPIO[8]:Input */
+	{0x94, 0x11},	/* If the default setting is loaded when
+			 * system boots up, this flag is closed here */
+};
+static const u8 sensor_init_767x[][2] = {
+	{0x12, 0x80},
+	{0x11, 0x03},
+	{0x3a, 0x04},
+	{0x12, 0x00},
+	{0x17, 0x13},
+	{0x18, 0x01},
+	{0x32, 0xb6},
+	{0x19, 0x02},
+	{0x1a, 0x7a},
+	{0x03, 0x0a},
+	{0x0c, 0x00},
+	{0x3e, 0x00},
+	{0x70, 0x3a},
+	{0x71, 0x35},
+	{0x72, 0x11},
+	{0x73, 0xf0},
+	{0xa2, 0x02},
+	{0x7a, 0x2a},	/* set Gamma=1.6 below */
+	{0x7b, 0x12},
+	{0x7c, 0x1d},
+	{0x7d, 0x2d},
+	{0x7e, 0x45},
+	{0x7f, 0x50},
+	{0x80, 0x59},
+	{0x81, 0x62},
+	{0x82, 0x6b},
+	{0x83, 0x73},
+	{0x84, 0x7b},
+	{0x85, 0x8a},
+	{0x86, 0x98},
+	{0x87, 0xb2},
+	{0x88, 0xca},
+	{0x89, 0xe0},
+	{0x13, 0xe0},
+	{0x00, 0x00},
+	{0x10, 0x00},
+	{0x0d, 0x40},
+	{0x14, 0x38},	/* gain max 16x */
+	{0xa5, 0x05},
+	{0xab, 0x07},
+	{0x24, 0x95},
+	{0x25, 0x33},
+	{0x26, 0xe3},
+	{0x9f, 0x78},
+	{0xa0, 0x68},
+	{0xa1, 0x03},
+	{0xa6, 0xd8},
+	{0xa7, 0xd8},
+	{0xa8, 0xf0},
+	{0xa9, 0x90},
+	{0xaa, 0x94},
+	{0x13, 0xe5},
+	{0x0e, 0x61},
+	{0x0f, 0x4b},
+	{0x16, 0x02},
+	{0x21, 0x02},
+	{0x22, 0x91},
+	{0x29, 0x07},
+	{0x33, 0x0b},
+	{0x35, 0x0b},
+	{0x37, 0x1d},
+	{0x38, 0x71},
+	{0x39, 0x2a},
+	{0x3c, 0x78},
+	{0x4d, 0x40},
+	{0x4e, 0x20},
+	{0x69, 0x00},
+	{0x6b, 0x4a},
+	{0x74, 0x10},
+	{0x8d, 0x4f},
+	{0x8e, 0x00},
+	{0x8f, 0x00},
+	{0x90, 0x00},
+	{0x91, 0x00},
+	{0x96, 0x00},
+	{0x9a, 0x80},
+	{0xb0, 0x84},
+	{0xb1, 0x0c},
+	{0xb2, 0x0e},
+	{0xb3, 0x82},
+	{0xb8, 0x0a},
+	{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},
+	{0x6a, 0x40},
+	{0x01, 0x40},
+	{0x02, 0x40},
+	{0x13, 0xe7},
+	{0x4f, 0x80},
+	{0x50, 0x80},
+	{0x51, 0x00},
+	{0x52, 0x22},
+	{0x53, 0x5e},
+	{0x54, 0x80},
+	{0x58, 0x9e},
+	{0x41, 0x08},
+	{0x3f, 0x00},
+	{0x75, 0x04},
+	{0x76, 0xe1},
+	{0x4c, 0x00},
+	{0x77, 0x01},
+	{0x3d, 0xc2},
+	{0x4b, 0x09},
+	{0xc9, 0x60},
+	{0x41, 0x38},	/* jfm: auto sharpness + auto de-noise  */
+	{0x56, 0x40},
+	{0x34, 0x11},
+	{0x3b, 0xc2},
+	{0xa4, 0x8a},	/* Night mode trigger point */
+	{0x96, 0x00},
+	{0x97, 0x30},
+	{0x98, 0x20},
+	{0x99, 0x20},
+	{0x9a, 0x84},
+	{0x9b, 0x29},
+	{0x9c, 0x03},
+	{0x9d, 0x4c},
+	{0x9e, 0x3f},
+	{0x78, 0x04},
+	{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, 0x20},
+	{0x79, 0x26},
+};
+static const u8 bridge_start_vga_767x[][2] = {
+/* str59 JPG */
+	{0x94, 0xaa},
+	{0xf1, 0x42},
+	{0xe5, 0x04},
+	{0xc0, 0x50},
+	{0xc1, 0x3c},
+	{0xc2, 0x0c},
+	{0x35, 0x02},	/* turn on JPEG */
+	{0xd9, 0x10},
+	{0xda, 0x00},	/* for higher clock rate(30fps) */
+	{0x34, 0x05},	/* enable Audio Suspend mode */
+	{0xc3, 0xf9},	/* enable PRE */
+	{0x8c, 0x00},	/* CIF VSize LSB[2:0] */
+	{0x8d, 0x1c},	/* output YUV */
+/*	{0x34, 0x05},	 * enable Audio Suspend mode (?) */
+	{0x50, 0x00},	/* H/V divider=0 */
+	{0x51, 0xa0},	/* input H=640/4 */
+	{0x52, 0x3c},	/* input V=480/4 */
+	{0x53, 0x00},	/* offset X=0 */
+	{0x54, 0x00},	/* offset Y=0 */
+	{0x55, 0x00},	/* H/V size[8]=0 */
+	{0x57, 0x00},	/* H-size[9]=0 */
+	{0x5c, 0x00},	/* output size[9:8]=0 */
+	{0x5a, 0xa0},	/* output H=640/4 */
+	{0x5b, 0x78},	/* output V=480/4 */
+	{0x1c, 0x0a},
+	{0x1d, 0x0a},
+	{0x94, 0x11},
+};
+static const u8 sensor_start_vga_767x[][2] = {
+	{0x11, 0x01},
+	{0x1e, 0x04},
+	{0x19, 0x02},
+	{0x1a, 0x7a},
+};
+static const u8 bridge_start_qvga_767x[][2] = {
+/* str86 JPG */
+	{0x94, 0xaa},
+	{0xf1, 0x42},
+	{0xe5, 0x04},
+	{0xc0, 0x80},
+	{0xc1, 0x60},
+	{0xc2, 0x0c},
+	{0x35, 0x02},	/* turn on JPEG */
+	{0xd9, 0x10},
+	{0xc0, 0x50},	/* CIF HSize 640 */
+	{0xc1, 0x3c},	/* CIF VSize 480 */
+	{0x8c, 0x00},	/* CIF VSize LSB[2:0] */
+	{0x8d, 0x1c},	/* output YUV */
+	{0x34, 0x05},	/* enable Audio Suspend mode */
+	{0xc2, 0x4c},	/* output YUV and Enable DCW */
+	{0xc3, 0xf9},	/* enable PRE */
+	{0x1c, 0x00},	/* indirect addressing */
+	{0x1d, 0x48},	/* output YUV422 */
+	{0x50, 0x89},	/* H/V divider=/2; plus DCW AVG */
+	{0x51, 0xa0},	/* DCW input H=640/4 */
+	{0x52, 0x78},	/* DCW input V=480/4 */
+	{0x53, 0x00},	/* offset X=0 */
+	{0x54, 0x00},	/* offset Y=0 */
+	{0x55, 0x00},	/* H/V size[8]=0 */
+	{0x57, 0x00},	/* H-size[9]=0 */
+	{0x5c, 0x00},	/* DCW output size[9:8]=0 */
+	{0x5a, 0x50},	/* DCW output H=320/4 */
+	{0x5b, 0x3c},	/* DCW output V=240/4 */
+	{0x1c, 0x0a},
+	{0x1d, 0x0a},
+	{0x94, 0x11},
+};
+static const u8 sensor_start_qvga_767x[][2] = {
+	{0x11, 0x01},
+	{0x1e, 0x04},
+	{0x19, 0x02},
+	{0x1a, 0x7a},
+};
+
+static const u8 bridge_init_772x[][2] = {
+	{ 0xc2, 0x0c },
+	{ 0x88, 0xf8 },
+	{ 0xc3, 0x69 },
+	{ 0x89, 0xff },
+	{ 0x76, 0x03 },
+	{ 0x92, 0x01 },
+	{ 0x93, 0x18 },
+	{ 0x94, 0x10 },
+	{ 0x95, 0x10 },
+	{ 0xe2, 0x00 },
+	{ 0xe7, 0x3e },
+
+	{ 0x96, 0x00 },
+
+	{ 0x97, 0x20 },
+	{ 0x97, 0x20 },
+	{ 0x97, 0x20 },
+	{ 0x97, 0x0a },
+	{ 0x97, 0x3f },
+	{ 0x97, 0x4a },
+	{ 0x97, 0x20 },
+	{ 0x97, 0x15 },
+	{ 0x97, 0x0b },
+
+	{ 0x8e, 0x40 },
+	{ 0x1f, 0x81 },
+	{ 0x34, 0x05 },
+	{ 0xe3, 0x04 },
+	{ 0x88, 0x00 },
+	{ 0x89, 0x00 },
+	{ 0x76, 0x00 },
+	{ 0xe7, 0x2e },
+	{ 0x31, 0xf9 },
+	{ 0x25, 0x42 },
+	{ 0x21, 0xf0 },
+
+	{ 0x1c, 0x00 },
+	{ 0x1d, 0x40 },
+	{ 0x1d, 0x02 }, /* payload size 0x0200 * 4 = 2048 bytes */
+	{ 0x1d, 0x00 }, /* payload size */
+
+	{ 0x1d, 0x02 }, /* frame size 0x025800 * 4 = 614400 */
+	{ 0x1d, 0x58 }, /* frame size */
+	{ 0x1d, 0x00 }, /* frame size */
+
+	{ 0x1c, 0x0a },
+	{ 0x1d, 0x08 }, /* turn on UVC header */
+	{ 0x1d, 0x0e }, /* .. */
+
+	{ 0x8d, 0x1c },
+	{ 0x8e, 0x80 },
+	{ 0xe5, 0x04 },
+
+	{ 0xc0, 0x50 },
+	{ 0xc1, 0x3c },
+	{ 0xc2, 0x0c },
+};
+static const u8 sensor_init_772x[][2] = {
+	{ 0x12, 0x80 },
+	{ 0x11, 0x01 },
+/*fixme: better have a delay?*/
+	{ 0x11, 0x01 },
+	{ 0x11, 0x01 },
+	{ 0x11, 0x01 },
+	{ 0x11, 0x01 },
+	{ 0x11, 0x01 },
+	{ 0x11, 0x01 },
+	{ 0x11, 0x01 },
+	{ 0x11, 0x01 },
+	{ 0x11, 0x01 },
+	{ 0x11, 0x01 },
+
+	{ 0x3d, 0x03 },
+	{ 0x17, 0x26 },
+	{ 0x18, 0xa0 },
+	{ 0x19, 0x07 },
+	{ 0x1a, 0xf0 },
+	{ 0x32, 0x00 },
+	{ 0x29, 0xa0 },
+	{ 0x2c, 0xf0 },
+	{ 0x65, 0x20 },
+	{ 0x11, 0x01 },
+	{ 0x42, 0x7f },
+	{ 0x63, 0xaa },		/* AWB - was e0 */
+	{ 0x64, 0xff },
+	{ 0x66, 0x00 },
+	{ 0x13, 0xf0 },		/* com8 */
+	{ 0x0d, 0x41 },
+	{ 0x0f, 0xc5 },
+	{ 0x14, 0x11 },
+
+	{ 0x22, 0x7f },
+	{ 0x23, 0x03 },
+	{ 0x24, 0x40 },
+	{ 0x25, 0x30 },
+	{ 0x26, 0xa1 },
+	{ 0x2a, 0x00 },
+	{ 0x2b, 0x00 },
+	{ 0x6b, 0xaa },
+	{ 0x13, 0xff },		/* AWB */
+
+	{ 0x90, 0x05 },
+	{ 0x91, 0x01 },
+	{ 0x92, 0x03 },
+	{ 0x93, 0x00 },
+	{ 0x94, 0x60 },
+	{ 0x95, 0x3c },
+	{ 0x96, 0x24 },
+	{ 0x97, 0x1e },
+	{ 0x98, 0x62 },
+	{ 0x99, 0x80 },
+	{ 0x9a, 0x1e },
+	{ 0x9b, 0x08 },
+	{ 0x9c, 0x20 },
+	{ 0x9e, 0x81 },
+
+	{ 0xa6, 0x07 },
+	{ 0x7e, 0x0c },
+	{ 0x7f, 0x16 },
+	{ 0x80, 0x2a },
+	{ 0x81, 0x4e },
+	{ 0x82, 0x61 },
+	{ 0x83, 0x6f },
+	{ 0x84, 0x7b },
+	{ 0x85, 0x86 },
+	{ 0x86, 0x8e },
+	{ 0x87, 0x97 },
+	{ 0x88, 0xa4 },
+	{ 0x89, 0xaf },
+	{ 0x8a, 0xc5 },
+	{ 0x8b, 0xd7 },
+	{ 0x8c, 0xe8 },
+	{ 0x8d, 0x20 },
+
+	{ 0x0c, 0x90 },
+
+	{ 0x2b, 0x00 },
+	{ 0x22, 0x7f },
+	{ 0x23, 0x03 },
+	{ 0x11, 0x01 },
+	{ 0x0c, 0xd0 },
+	{ 0x64, 0xff },
+	{ 0x0d, 0x41 },
+
+	{ 0x14, 0x41 },
+	{ 0x0e, 0xcd },
+	{ 0xac, 0xbf },
+	{ 0x8e, 0x00 },		/* De-noise threshold */
+	{ 0x0c, 0xd0 }
+};
+static const u8 bridge_start_vga_772x[][2] = {
+	{0x1c, 0x00},
+	{0x1d, 0x40},
+	{0x1d, 0x02},
+	{0x1d, 0x00},
+	{0x1d, 0x02},
+	{0x1d, 0x58},
+	{0x1d, 0x00},
+	{0xc0, 0x50},
+	{0xc1, 0x3c},
+};
+static const u8 sensor_start_vga_772x[][2] = {
+	{0x12, 0x00},
+	{0x17, 0x26},
+	{0x18, 0xa0},
+	{0x19, 0x07},
+	{0x1a, 0xf0},
+	{0x29, 0xa0},
+	{0x2c, 0xf0},
+	{0x65, 0x20},
+};
+static const u8 bridge_start_qvga_772x[][2] = {
+	{0x1c, 0x00},
+	{0x1d, 0x40},
+	{0x1d, 0x02},
+	{0x1d, 0x00},
+	{0x1d, 0x01},
+	{0x1d, 0x4b},
+	{0x1d, 0x00},
+	{0xc0, 0x28},
+	{0xc1, 0x1e},
+};
+static const u8 sensor_start_qvga_772x[][2] = {
+	{0x12, 0x40},
+	{0x17, 0x3f},
+	{0x18, 0x50},
+	{0x19, 0x03},
+	{0x1a, 0x78},
+	{0x29, 0x50},
+	{0x2c, 0x78},
+	{0x65, 0x2f},
+};
+
+static void ov534_reg_write(struct gspca_dev *gspca_dev, u16 reg, u8 val)
+{
+	struct usb_device *udev = gspca_dev->dev;
+	int ret;
+
+	if (gspca_dev->usb_err < 0)
+		return;
+
+	PDEBUG(D_USBO, "SET 01 0000 %04x %02x", reg, val);
+	gspca_dev->usb_buf[0] = val;
+	ret = usb_control_msg(udev,
+			      usb_sndctrlpipe(udev, 0),
+			      0x01,
+			      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			      0x00, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT);
+	if (ret < 0) {
+		pr_err("write failed %d\n", ret);
+		gspca_dev->usb_err = ret;
+	}
+}
+
+static u8 ov534_reg_read(struct gspca_dev *gspca_dev, u16 reg)
+{
+	struct usb_device *udev = gspca_dev->dev;
+	int ret;
+
+	if (gspca_dev->usb_err < 0)
+		return 0;
+	ret = usb_control_msg(udev,
+			      usb_rcvctrlpipe(udev, 0),
+			      0x01,
+			      USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			      0x00, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT);
+	PDEBUG(D_USBI, "GET 01 0000 %04x %02x", reg, gspca_dev->usb_buf[0]);
+	if (ret < 0) {
+		pr_err("read failed %d\n", ret);
+		gspca_dev->usb_err = ret;
+	}
+	return gspca_dev->usb_buf[0];
+}
+
+/* Two bits control LED: 0x21 bit 7 and 0x23 bit 7.
+ * (direction and output)? */
+static void ov534_set_led(struct gspca_dev *gspca_dev, int status)
+{
+	u8 data;
+
+	PDEBUG(D_CONF, "led status: %d", status);
+
+	data = ov534_reg_read(gspca_dev, 0x21);
+	data |= 0x80;
+	ov534_reg_write(gspca_dev, 0x21, data);
+
+	data = ov534_reg_read(gspca_dev, 0x23);
+	if (status)
+		data |= 0x80;
+	else
+		data &= ~0x80;
+
+	ov534_reg_write(gspca_dev, 0x23, data);
+
+	if (!status) {
+		data = ov534_reg_read(gspca_dev, 0x21);
+		data &= ~0x80;
+		ov534_reg_write(gspca_dev, 0x21, data);
+	}
+}
+
+static int sccb_check_status(struct gspca_dev *gspca_dev)
+{
+	u8 data;
+	int i;
+
+	for (i = 0; i < 5; i++) {
+		msleep(10);
+		data = ov534_reg_read(gspca_dev, OV534_REG_STATUS);
+
+		switch (data) {
+		case 0x00:
+			return 1;
+		case 0x04:
+			return 0;
+		case 0x03:
+			break;
+		default:
+			PERR("sccb status 0x%02x, attempt %d/5",
+			       data, i + 1);
+		}
+	}
+	return 0;
+}
+
+static void sccb_reg_write(struct gspca_dev *gspca_dev, u8 reg, u8 val)
+{
+	PDEBUG(D_USBO, "sccb write: %02x %02x", reg, val);
+	ov534_reg_write(gspca_dev, OV534_REG_SUBADDR, reg);
+	ov534_reg_write(gspca_dev, OV534_REG_WRITE, val);
+	ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_3);
+
+	if (!sccb_check_status(gspca_dev)) {
+		pr_err("sccb_reg_write failed\n");
+		gspca_dev->usb_err = -EIO;
+	}
+}
+
+static u8 sccb_reg_read(struct gspca_dev *gspca_dev, u16 reg)
+{
+	ov534_reg_write(gspca_dev, OV534_REG_SUBADDR, reg);
+	ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_2);
+	if (!sccb_check_status(gspca_dev))
+		pr_err("sccb_reg_read failed 1\n");
+
+	ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_READ_2);
+	if (!sccb_check_status(gspca_dev))
+		pr_err("sccb_reg_read failed 2\n");
+
+	return ov534_reg_read(gspca_dev, OV534_REG_READ);
+}
+
+/* output a bridge sequence (reg - val) */
+static void reg_w_array(struct gspca_dev *gspca_dev,
+			const u8 (*data)[2], int len)
+{
+	while (--len >= 0) {
+		ov534_reg_write(gspca_dev, (*data)[0], (*data)[1]);
+		data++;
+	}
+}
+
+/* output a sensor sequence (reg - val) */
+static void sccb_w_array(struct gspca_dev *gspca_dev,
+			const u8 (*data)[2], int len)
+{
+	while (--len >= 0) {
+		if ((*data)[0] != 0xff) {
+			sccb_reg_write(gspca_dev, (*data)[0], (*data)[1]);
+		} else {
+			sccb_reg_read(gspca_dev, (*data)[1]);
+			sccb_reg_write(gspca_dev, 0xff, 0x00);
+		}
+		data++;
+	}
+}
+
+/* ov772x specific controls */
+static void set_frame_rate(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int i;
+	struct rate_s {
+		u8 fps;
+		u8 r11;
+		u8 r0d;
+		u8 re5;
+	};
+	const struct rate_s *r;
+	static const struct rate_s rate_0[] = {	/* 640x480 */
+		{60, 0x01, 0xc1, 0x04},
+		{50, 0x01, 0x41, 0x02},
+		{40, 0x02, 0xc1, 0x04},
+		{30, 0x04, 0x81, 0x02},
+		{15, 0x03, 0x41, 0x04},
+	};
+	static const struct rate_s rate_1[] = {	/* 320x240 */
+/*		{205, 0x01, 0xc1, 0x02},  * 205 FPS: video is partly corrupt */
+		{187, 0x01, 0x81, 0x02}, /* 187 FPS or below: video is valid */
+		{150, 0x01, 0xc1, 0x04},
+		{137, 0x02, 0xc1, 0x02},
+		{125, 0x02, 0x81, 0x02},
+		{100, 0x02, 0xc1, 0x04},
+		{75, 0x03, 0xc1, 0x04},
+		{60, 0x04, 0xc1, 0x04},
+		{50, 0x02, 0x41, 0x04},
+		{37, 0x03, 0x41, 0x04},
+		{30, 0x04, 0x41, 0x04},
+	};
+
+	if (sd->sensor != SENSOR_OV772x)
+		return;
+	if (gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv == 0) {
+		r = rate_0;
+		i = ARRAY_SIZE(rate_0);
+	} else {
+		r = rate_1;
+		i = ARRAY_SIZE(rate_1);
+	}
+	while (--i > 0) {
+		if (sd->frame_rate >= r->fps)
+			break;
+		r++;
+	}
+
+	sccb_reg_write(gspca_dev, 0x11, r->r11);
+	sccb_reg_write(gspca_dev, 0x0d, r->r0d);
+	ov534_reg_write(gspca_dev, 0xe5, r->re5);
+
+	PDEBUG(D_PROBE, "frame_rate: %d", r->fps);
+}
+
+static void sethue(struct gspca_dev *gspca_dev, s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (sd->sensor == SENSOR_OV767x) {
+		/* TBD */
+	} else {
+		s16 huesin;
+		s16 huecos;
+
+		/* According to the datasheet the registers expect HUESIN and
+		 * HUECOS to be the result of the trigonometric functions,
+		 * scaled by 0x80.
+		 *
+		 * The 0x7fff here represents the maximum absolute value
+		 * returned byt fixp_sin and fixp_cos, so the scaling will
+		 * consider the result like in the interval [-1.0, 1.0].
+		 */
+		huesin = fixp_sin16(val) * 0x80 / 0x7fff;
+		huecos = fixp_cos16(val) * 0x80 / 0x7fff;
+
+		if (huesin < 0) {
+			sccb_reg_write(gspca_dev, 0xab,
+				sccb_reg_read(gspca_dev, 0xab) | 0x2);
+			huesin = -huesin;
+		} else {
+			sccb_reg_write(gspca_dev, 0xab,
+				sccb_reg_read(gspca_dev, 0xab) & ~0x2);
+
+		}
+		sccb_reg_write(gspca_dev, 0xa9, (u8)huecos);
+		sccb_reg_write(gspca_dev, 0xaa, (u8)huesin);
+	}
+}
+
+static void setsaturation(struct gspca_dev *gspca_dev, s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (sd->sensor == SENSOR_OV767x) {
+		int i;
+		static u8 color_tb[][6] = {
+			{0x42, 0x42, 0x00, 0x11, 0x30, 0x41},
+			{0x52, 0x52, 0x00, 0x16, 0x3c, 0x52},
+			{0x66, 0x66, 0x00, 0x1b, 0x4b, 0x66},
+			{0x80, 0x80, 0x00, 0x22, 0x5e, 0x80},
+			{0x9a, 0x9a, 0x00, 0x29, 0x71, 0x9a},
+			{0xb8, 0xb8, 0x00, 0x31, 0x87, 0xb8},
+			{0xdd, 0xdd, 0x00, 0x3b, 0xa2, 0xdd},
+		};
+
+		for (i = 0; i < ARRAY_SIZE(color_tb[0]); i++)
+			sccb_reg_write(gspca_dev, 0x4f + i, color_tb[val][i]);
+	} else {
+		sccb_reg_write(gspca_dev, 0xa7, val); /* U saturation */
+		sccb_reg_write(gspca_dev, 0xa8, val); /* V saturation */
+	}
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (sd->sensor == SENSOR_OV767x) {
+		if (val < 0)
+			val = 0x80 - val;
+		sccb_reg_write(gspca_dev, 0x55, val);	/* bright */
+	} else {
+		sccb_reg_write(gspca_dev, 0x9b, val);
+	}
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (sd->sensor == SENSOR_OV767x)
+		sccb_reg_write(gspca_dev, 0x56, val);	/* contras */
+	else
+		sccb_reg_write(gspca_dev, 0x9c, val);
+}
+
+static void setgain(struct gspca_dev *gspca_dev, s32 val)
+{
+	switch (val & 0x30) {
+	case 0x00:
+		val &= 0x0f;
+		break;
+	case 0x10:
+		val &= 0x0f;
+		val |= 0x30;
+		break;
+	case 0x20:
+		val &= 0x0f;
+		val |= 0x70;
+		break;
+	default:
+/*	case 0x30: */
+		val &= 0x0f;
+		val |= 0xf0;
+		break;
+	}
+	sccb_reg_write(gspca_dev, 0x00, val);
+}
+
+static s32 getgain(struct gspca_dev *gspca_dev)
+{
+	return sccb_reg_read(gspca_dev, 0x00);
+}
+
+static void setexposure(struct gspca_dev *gspca_dev, s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (sd->sensor == SENSOR_OV767x) {
+
+		/* set only aec[9:2] */
+		sccb_reg_write(gspca_dev, 0x10, val);	/* aech */
+	} else {
+
+		/* 'val' is one byte and represents half of the exposure value
+		 * we are going to set into registers, a two bytes value:
+		 *
+		 *    MSB: ((u16) val << 1) >> 8   == val >> 7
+		 *    LSB: ((u16) val << 1) & 0xff == val << 1
+		 */
+		sccb_reg_write(gspca_dev, 0x08, val >> 7);
+		sccb_reg_write(gspca_dev, 0x10, val << 1);
+	}
+}
+
+static s32 getexposure(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (sd->sensor == SENSOR_OV767x) {
+		/* get only aec[9:2] */
+		return sccb_reg_read(gspca_dev, 0x10);	/* aech */
+	} else {
+		u8 hi = sccb_reg_read(gspca_dev, 0x08);
+		u8 lo = sccb_reg_read(gspca_dev, 0x10);
+		return (hi << 8 | lo) >> 1;
+	}
+}
+
+static void setagc(struct gspca_dev *gspca_dev, s32 val)
+{
+	if (val) {
+		sccb_reg_write(gspca_dev, 0x13,
+				sccb_reg_read(gspca_dev, 0x13) | 0x04);
+		sccb_reg_write(gspca_dev, 0x64,
+				sccb_reg_read(gspca_dev, 0x64) | 0x03);
+	} else {
+		sccb_reg_write(gspca_dev, 0x13,
+				sccb_reg_read(gspca_dev, 0x13) & ~0x04);
+		sccb_reg_write(gspca_dev, 0x64,
+				sccb_reg_read(gspca_dev, 0x64) & ~0x03);
+	}
+}
+
+static void setawb(struct gspca_dev *gspca_dev, s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (val) {
+		sccb_reg_write(gspca_dev, 0x13,
+				sccb_reg_read(gspca_dev, 0x13) | 0x02);
+		if (sd->sensor == SENSOR_OV772x)
+			sccb_reg_write(gspca_dev, 0x63,
+				sccb_reg_read(gspca_dev, 0x63) | 0xc0);
+	} else {
+		sccb_reg_write(gspca_dev, 0x13,
+				sccb_reg_read(gspca_dev, 0x13) & ~0x02);
+		if (sd->sensor == SENSOR_OV772x)
+			sccb_reg_write(gspca_dev, 0x63,
+				sccb_reg_read(gspca_dev, 0x63) & ~0xc0);
+	}
+}
+
+static void setaec(struct gspca_dev *gspca_dev, s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u8 data;
+
+	data = sd->sensor == SENSOR_OV767x ?
+			0x05 :		/* agc + aec */
+			0x01;		/* agc */
+	switch (val) {
+	case V4L2_EXPOSURE_AUTO:
+		sccb_reg_write(gspca_dev, 0x13,
+				sccb_reg_read(gspca_dev, 0x13) | data);
+		break;
+	case V4L2_EXPOSURE_MANUAL:
+		sccb_reg_write(gspca_dev, 0x13,
+				sccb_reg_read(gspca_dev, 0x13) & ~data);
+		break;
+	}
+}
+
+static void setsharpness(struct gspca_dev *gspca_dev, s32 val)
+{
+	sccb_reg_write(gspca_dev, 0x91, val);	/* Auto de-noise threshold */
+	sccb_reg_write(gspca_dev, 0x8e, val);	/* De-noise threshold */
+}
+
+static void sethvflip(struct gspca_dev *gspca_dev, s32 hflip, s32 vflip)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u8 val;
+
+	if (sd->sensor == SENSOR_OV767x) {
+		val = sccb_reg_read(gspca_dev, 0x1e);	/* mvfp */
+		val &= ~0x30;
+		if (hflip)
+			val |= 0x20;
+		if (vflip)
+			val |= 0x10;
+		sccb_reg_write(gspca_dev, 0x1e, val);
+	} else {
+		val = sccb_reg_read(gspca_dev, 0x0c);
+		val &= ~0xc0;
+		if (hflip == 0)
+			val |= 0x40;
+		if (vflip == 0)
+			val |= 0x80;
+		sccb_reg_write(gspca_dev, 0x0c, val);
+	}
+}
+
+static void setlightfreq(struct gspca_dev *gspca_dev, s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	val = val ? 0x9e : 0x00;
+	if (sd->sensor == SENSOR_OV767x) {
+		sccb_reg_write(gspca_dev, 0x2a, 0x00);
+		if (val)
+			val = 0x9d;	/* insert dummy to 25fps for 50Hz */
+	}
+	sccb_reg_write(gspca_dev, 0x2b, val);
+}
+
+
+/* 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->cam_mode = ov772x_mode;
+	cam->nmodes = ARRAY_SIZE(ov772x_mode);
+
+	sd->frame_rate = 30;
+
+	return 0;
+}
+
+static int ov534_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct sd *sd = container_of(ctrl->handler, struct sd, ctrl_handler);
+	struct gspca_dev *gspca_dev = &sd->gspca_dev;
+
+	switch (ctrl->id) {
+	case V4L2_CID_AUTOGAIN:
+		gspca_dev->usb_err = 0;
+		if (ctrl->val && sd->gain && gspca_dev->streaming)
+			sd->gain->val = getgain(gspca_dev);
+		return gspca_dev->usb_err;
+
+	case V4L2_CID_EXPOSURE_AUTO:
+		gspca_dev->usb_err = 0;
+		if (ctrl->val == V4L2_EXPOSURE_AUTO && sd->exposure &&
+		    gspca_dev->streaming)
+			sd->exposure->val = getexposure(gspca_dev);
+		return gspca_dev->usb_err;
+	}
+	return -EINVAL;
+}
+
+static int ov534_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct sd *sd = container_of(ctrl->handler, struct sd, ctrl_handler);
+	struct gspca_dev *gspca_dev = &sd->gspca_dev;
+
+	gspca_dev->usb_err = 0;
+	if (!gspca_dev->streaming)
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_HUE:
+		sethue(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_SATURATION:
+		setsaturation(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_BRIGHTNESS:
+		setbrightness(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_CONTRAST:
+		setcontrast(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_AUTOGAIN:
+	/* case V4L2_CID_GAIN: */
+		setagc(gspca_dev, ctrl->val);
+		if (!gspca_dev->usb_err && !ctrl->val && sd->gain)
+			setgain(gspca_dev, sd->gain->val);
+		break;
+	case V4L2_CID_AUTO_WHITE_BALANCE:
+		setawb(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_EXPOSURE_AUTO:
+	/* case V4L2_CID_EXPOSURE: */
+		setaec(gspca_dev, ctrl->val);
+		if (!gspca_dev->usb_err && ctrl->val == V4L2_EXPOSURE_MANUAL &&
+		    sd->exposure)
+			setexposure(gspca_dev, sd->exposure->val);
+		break;
+	case V4L2_CID_SHARPNESS:
+		setsharpness(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_HFLIP:
+		sethvflip(gspca_dev, ctrl->val, sd->vflip->val);
+		break;
+	case V4L2_CID_VFLIP:
+		sethvflip(gspca_dev, sd->hflip->val, ctrl->val);
+		break;
+	case V4L2_CID_POWER_LINE_FREQUENCY:
+		setlightfreq(gspca_dev, ctrl->val);
+		break;
+	}
+	return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops ov534_ctrl_ops = {
+	.g_volatile_ctrl = ov534_g_volatile_ctrl,
+	.s_ctrl = ov534_s_ctrl,
+};
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct v4l2_ctrl_handler *hdl = &sd->ctrl_handler;
+	/* parameters with different values between the supported sensors */
+	int saturation_min;
+	int saturation_max;
+	int saturation_def;
+	int brightness_min;
+	int brightness_max;
+	int brightness_def;
+	int contrast_max;
+	int contrast_def;
+	int exposure_min;
+	int exposure_max;
+	int exposure_def;
+	int hflip_def;
+
+	if (sd->sensor == SENSOR_OV767x) {
+		saturation_min = 0,
+		saturation_max = 6,
+		saturation_def = 3,
+		brightness_min = -127;
+		brightness_max = 127;
+		brightness_def = 0;
+		contrast_max = 0x80;
+		contrast_def = 0x40;
+		exposure_min = 0x08;
+		exposure_max = 0x60;
+		exposure_def = 0x13;
+		hflip_def = 1;
+	} else {
+		saturation_min = 0,
+		saturation_max = 255,
+		saturation_def = 64,
+		brightness_min = 0;
+		brightness_max = 255;
+		brightness_def = 0;
+		contrast_max = 255;
+		contrast_def = 32;
+		exposure_min = 0;
+		exposure_max = 255;
+		exposure_def = 120;
+		hflip_def = 0;
+	}
+
+	gspca_dev->vdev.ctrl_handler = hdl;
+
+	v4l2_ctrl_handler_init(hdl, 13);
+
+	if (sd->sensor == SENSOR_OV772x)
+		sd->hue = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops,
+				V4L2_CID_HUE, -90, 90, 1, 0);
+
+	sd->saturation = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops,
+			V4L2_CID_SATURATION, saturation_min, saturation_max, 1,
+			saturation_def);
+	sd->brightness = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops,
+			V4L2_CID_BRIGHTNESS, brightness_min, brightness_max, 1,
+			brightness_def);
+	sd->contrast = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops,
+			V4L2_CID_CONTRAST, 0, contrast_max, 1, contrast_def);
+
+	if (sd->sensor == SENSOR_OV772x) {
+		sd->autogain = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops,
+				V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+		sd->gain = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops,
+				V4L2_CID_GAIN, 0, 63, 1, 20);
+	}
+
+	sd->autoexposure = v4l2_ctrl_new_std_menu(hdl, &ov534_ctrl_ops,
+			V4L2_CID_EXPOSURE_AUTO,
+			V4L2_EXPOSURE_MANUAL, 0,
+			V4L2_EXPOSURE_AUTO);
+	sd->exposure = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops,
+			V4L2_CID_EXPOSURE, exposure_min, exposure_max, 1,
+			exposure_def);
+
+	sd->autowhitebalance = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops,
+			V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1);
+
+	if (sd->sensor == SENSOR_OV772x)
+		sd->sharpness = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops,
+				V4L2_CID_SHARPNESS, 0, 63, 1, 0);
+
+	sd->hflip = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops,
+			V4L2_CID_HFLIP, 0, 1, 1, hflip_def);
+	sd->vflip = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops,
+			V4L2_CID_VFLIP, 0, 1, 1, 0);
+	sd->plfreq = v4l2_ctrl_new_std_menu(hdl, &ov534_ctrl_ops,
+			V4L2_CID_POWER_LINE_FREQUENCY,
+			V4L2_CID_POWER_LINE_FREQUENCY_50HZ, 0,
+			V4L2_CID_POWER_LINE_FREQUENCY_DISABLED);
+
+	if (hdl->error) {
+		pr_err("Could not initialize controls\n");
+		return hdl->error;
+	}
+
+	if (sd->sensor == SENSOR_OV772x)
+		v4l2_ctrl_auto_cluster(2, &sd->autogain, 0, true);
+
+	v4l2_ctrl_auto_cluster(2, &sd->autoexposure, V4L2_EXPOSURE_MANUAL,
+			       true);
+
+	return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u16 sensor_id;
+	static const struct reg_array bridge_init[NSENSORS] = {
+	[SENSOR_OV767x] = {bridge_init_767x, ARRAY_SIZE(bridge_init_767x)},
+	[SENSOR_OV772x] = {bridge_init_772x, ARRAY_SIZE(bridge_init_772x)},
+	};
+	static const struct reg_array sensor_init[NSENSORS] = {
+	[SENSOR_OV767x] = {sensor_init_767x, ARRAY_SIZE(sensor_init_767x)},
+	[SENSOR_OV772x] = {sensor_init_772x, ARRAY_SIZE(sensor_init_772x)},
+	};
+
+	/* reset bridge */
+	ov534_reg_write(gspca_dev, 0xe7, 0x3a);
+	ov534_reg_write(gspca_dev, 0xe0, 0x08);
+	msleep(100);
+
+	/* initialize the sensor address */
+	ov534_reg_write(gspca_dev, OV534_REG_ADDRESS, 0x42);
+
+	/* reset sensor */
+	sccb_reg_write(gspca_dev, 0x12, 0x80);
+	msleep(10);
+
+	/* probe the sensor */
+	sccb_reg_read(gspca_dev, 0x0a);
+	sensor_id = sccb_reg_read(gspca_dev, 0x0a) << 8;
+	sccb_reg_read(gspca_dev, 0x0b);
+	sensor_id |= sccb_reg_read(gspca_dev, 0x0b);
+	PDEBUG(D_PROBE, "Sensor ID: %04x", sensor_id);
+
+	if ((sensor_id & 0xfff0) == 0x7670) {
+		sd->sensor = SENSOR_OV767x;
+		gspca_dev->cam.cam_mode = ov767x_mode;
+		gspca_dev->cam.nmodes = ARRAY_SIZE(ov767x_mode);
+	} else {
+		sd->sensor = SENSOR_OV772x;
+		gspca_dev->cam.bulk = 1;
+		gspca_dev->cam.bulk_size = 16384;
+		gspca_dev->cam.bulk_nurbs = 2;
+		gspca_dev->cam.mode_framerates = ov772x_framerates;
+	}
+
+	/* initialize */
+	reg_w_array(gspca_dev, bridge_init[sd->sensor].val,
+			bridge_init[sd->sensor].len);
+	ov534_set_led(gspca_dev, 1);
+	sccb_w_array(gspca_dev, sensor_init[sd->sensor].val,
+			sensor_init[sd->sensor].len);
+
+	sd_stopN(gspca_dev);
+/*	set_frame_rate(gspca_dev);	*/
+
+	return gspca_dev->usb_err;
+}
+
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int mode;
+	static const struct reg_array bridge_start[NSENSORS][2] = {
+	[SENSOR_OV767x] = {{bridge_start_qvga_767x,
+					ARRAY_SIZE(bridge_start_qvga_767x)},
+			{bridge_start_vga_767x,
+					ARRAY_SIZE(bridge_start_vga_767x)}},
+	[SENSOR_OV772x] = {{bridge_start_qvga_772x,
+					ARRAY_SIZE(bridge_start_qvga_772x)},
+			{bridge_start_vga_772x,
+					ARRAY_SIZE(bridge_start_vga_772x)}},
+	};
+	static const struct reg_array sensor_start[NSENSORS][2] = {
+	[SENSOR_OV767x] = {{sensor_start_qvga_767x,
+					ARRAY_SIZE(sensor_start_qvga_767x)},
+			{sensor_start_vga_767x,
+					ARRAY_SIZE(sensor_start_vga_767x)}},
+	[SENSOR_OV772x] = {{sensor_start_qvga_772x,
+					ARRAY_SIZE(sensor_start_qvga_772x)},
+			{sensor_start_vga_772x,
+					ARRAY_SIZE(sensor_start_vga_772x)}},
+	};
+
+	/* (from ms-win trace) */
+	if (sd->sensor == SENSOR_OV767x)
+		sccb_reg_write(gspca_dev, 0x1e, 0x04);
+					/* black sun enable ? */
+
+	mode = gspca_dev->curr_mode;	/* 0: 320x240, 1: 640x480 */
+	reg_w_array(gspca_dev, bridge_start[sd->sensor][mode].val,
+				bridge_start[sd->sensor][mode].len);
+	sccb_w_array(gspca_dev, sensor_start[sd->sensor][mode].val,
+				sensor_start[sd->sensor][mode].len);
+
+	set_frame_rate(gspca_dev);
+
+	if (sd->hue)
+		sethue(gspca_dev, v4l2_ctrl_g_ctrl(sd->hue));
+	setsaturation(gspca_dev, v4l2_ctrl_g_ctrl(sd->saturation));
+	if (sd->autogain)
+		setagc(gspca_dev, v4l2_ctrl_g_ctrl(sd->autogain));
+	setawb(gspca_dev, v4l2_ctrl_g_ctrl(sd->autowhitebalance));
+	setaec(gspca_dev, v4l2_ctrl_g_ctrl(sd->autoexposure));
+	if (sd->gain)
+		setgain(gspca_dev, v4l2_ctrl_g_ctrl(sd->gain));
+	setexposure(gspca_dev, v4l2_ctrl_g_ctrl(sd->exposure));
+	setbrightness(gspca_dev, v4l2_ctrl_g_ctrl(sd->brightness));
+	setcontrast(gspca_dev, v4l2_ctrl_g_ctrl(sd->contrast));
+	if (sd->sharpness)
+		setsharpness(gspca_dev, v4l2_ctrl_g_ctrl(sd->sharpness));
+	sethvflip(gspca_dev, v4l2_ctrl_g_ctrl(sd->hflip),
+		  v4l2_ctrl_g_ctrl(sd->vflip));
+	setlightfreq(gspca_dev, v4l2_ctrl_g_ctrl(sd->plfreq));
+
+	ov534_set_led(gspca_dev, 1);
+	ov534_reg_write(gspca_dev, 0xe0, 0x00);
+	return gspca_dev->usb_err;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+	ov534_reg_write(gspca_dev, 0xe0, 0x09);
+	ov534_set_led(gspca_dev, 0);
+}
+
+/* Values for bmHeaderInfo (Video and Still Image Payload Headers, 2.4.3.3) */
+#define UVC_STREAM_EOH	(1 << 7)
+#define UVC_STREAM_ERR	(1 << 6)
+#define UVC_STREAM_STI	(1 << 5)
+#define UVC_STREAM_RES	(1 << 4)
+#define UVC_STREAM_SCR	(1 << 3)
+#define UVC_STREAM_PTS	(1 << 2)
+#define UVC_STREAM_EOF	(1 << 1)
+#define UVC_STREAM_FID	(1 << 0)
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+			u8 *data, int len)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	__u32 this_pts;
+	u16 this_fid;
+	int remaining_len = len;
+	int payload_len;
+
+	payload_len = gspca_dev->cam.bulk ? 2048 : 2040;
+	do {
+		len = min(remaining_len, payload_len);
+
+		/* Payloads are prefixed with a UVC-style header.  We
+		   consider a frame to start when the FID toggles, or the PTS
+		   changes.  A frame ends when EOF is set, and we've received
+		   the correct number of bytes. */
+
+		/* Verify UVC header.  Header length is always 12 */
+		if (data[0] != 12 || len < 12) {
+			PDEBUG(D_PACK, "bad header");
+			goto discard;
+		}
+
+		/* Check errors */
+		if (data[1] & UVC_STREAM_ERR) {
+			PDEBUG(D_PACK, "payload error");
+			goto discard;
+		}
+
+		/* Extract PTS and FID */
+		if (!(data[1] & UVC_STREAM_PTS)) {
+			PDEBUG(D_PACK, "PTS not present");
+			goto discard;
+		}
+		this_pts = (data[5] << 24) | (data[4] << 16)
+						| (data[3] << 8) | data[2];
+		this_fid = (data[1] & UVC_STREAM_FID) ? 1 : 0;
+
+		/* If PTS or FID has changed, start a new frame. */
+		if (this_pts != sd->last_pts || this_fid != sd->last_fid) {
+			if (gspca_dev->last_packet_type == INTER_PACKET)
+				gspca_frame_add(gspca_dev, LAST_PACKET,
+						NULL, 0);
+			sd->last_pts = this_pts;
+			sd->last_fid = this_fid;
+			gspca_frame_add(gspca_dev, FIRST_PACKET,
+					data + 12, len - 12);
+		/* If this packet is marked as EOF, end the frame */
+		} else if (data[1] & UVC_STREAM_EOF) {
+			sd->last_pts = 0;
+			if (gspca_dev->pixfmt.pixelformat == V4L2_PIX_FMT_YUYV
+			 && gspca_dev->image_len + len - 12 !=
+				   gspca_dev->pixfmt.width *
+					gspca_dev->pixfmt.height * 2) {
+				PDEBUG(D_PACK, "wrong sized frame");
+				goto discard;
+			}
+			gspca_frame_add(gspca_dev, LAST_PACKET,
+					data + 12, len - 12);
+		} else {
+
+			/* Add the data from this payload */
+			gspca_frame_add(gspca_dev, INTER_PACKET,
+					data + 12, len - 12);
+		}
+
+		/* Done this payload */
+		goto scan_next;
+
+discard:
+		/* Discard data until a new frame starts. */
+		gspca_dev->last_packet_type = DISCARD_PACKET;
+
+scan_next:
+		remaining_len -= len;
+		data += len;
+	} while (remaining_len > 0);
+}
+
+/* get stream parameters (framerate) */
+static void sd_get_streamparm(struct gspca_dev *gspca_dev,
+			     struct v4l2_streamparm *parm)
+{
+	struct v4l2_captureparm *cp = &parm->parm.capture;
+	struct v4l2_fract *tpf = &cp->timeperframe;
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	cp->capability |= V4L2_CAP_TIMEPERFRAME;
+	tpf->numerator = 1;
+	tpf->denominator = sd->frame_rate;
+}
+
+/* set stream parameters (framerate) */
+static void sd_set_streamparm(struct gspca_dev *gspca_dev,
+			     struct v4l2_streamparm *parm)
+{
+	struct v4l2_captureparm *cp = &parm->parm.capture;
+	struct v4l2_fract *tpf = &cp->timeperframe;
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (tpf->numerator == 0 || tpf->denominator == 0)
+		/* Set default framerate */
+		sd->frame_rate = 30;
+	else
+		/* Set requested framerate */
+		sd->frame_rate = tpf->denominator / tpf->numerator;
+
+	if (gspca_dev->streaming)
+		set_frame_rate(gspca_dev);
+
+	/* Return the actual framerate */
+	tpf->numerator = 1;
+	tpf->denominator = sd->frame_rate;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+	.name     = MODULE_NAME,
+	.config   = sd_config,
+	.init     = sd_init,
+	.init_controls = sd_init_controls,
+	.start    = sd_start,
+	.stopN    = sd_stopN,
+	.pkt_scan = sd_pkt_scan,
+	.get_streamparm = sd_get_streamparm,
+	.set_streamparm = sd_set_streamparm,
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+	{USB_DEVICE(0x1415, 0x2000)},
+	{USB_DEVICE(0x06f8, 0x3002)},
+	{}
+};
+
+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,
+#ifdef CONFIG_PM
+	.suspend    = gspca_suspend,
+	.resume     = gspca_resume,
+	.reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/usb/gspca/ov534_9.c b/drivers/media/usb/gspca/ov534_9.c
new file mode 100644
index 0000000..47085cf
--- /dev/null
+++ b/drivers/media/usb/gspca/ov534_9.c
@@ -0,0 +1,1832 @@
+/*
+ * ov534-ov9xxx gspca driver
+ *
+ * Copyright (C) 2009-2011 Jean-Francois Moine http://moinejf.free.fr
+ * Copyright (C) 2008 Antonio Ospite <ospite@studenti.unina.it>
+ * Copyright (C) 2008 Jim Paris <jim@jtan.com>
+ *
+ * Based on a prototype written by Mark Ferrell <majortrips@gmail.com>
+ * USB protocol reverse engineered by Jim Paris <jim@jtan.com>
+ * https://jim.sh/svn/jim/devl/playstation/ps3/eye/test/
+ *
+ * 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "ov534_9"
+
+#include "gspca.h"
+
+#define OV534_REG_ADDRESS	0xf1	/* sensor address */
+#define OV534_REG_SUBADDR	0xf2
+#define OV534_REG_WRITE		0xf3
+#define OV534_REG_READ		0xf4
+#define OV534_REG_OPERATION	0xf5
+#define OV534_REG_STATUS	0xf6
+
+#define OV534_OP_WRITE_3	0x37
+#define OV534_OP_WRITE_2	0x33
+#define OV534_OP_READ_2		0xf9
+
+#define CTRL_TIMEOUT 500
+
+MODULE_AUTHOR("Jean-Francois Moine <moinejf@free.fr>");
+MODULE_DESCRIPTION("GSPCA/OV534_9 USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* specific webcam descriptor */
+struct sd {
+	struct gspca_dev gspca_dev;	/* !! must be the first item */
+	__u32 last_pts;
+	u8 last_fid;
+
+	u8 sensor;
+};
+enum sensors {
+	SENSOR_OV965x,		/* ov9657 */
+	SENSOR_OV971x,		/* ov9712 */
+	SENSOR_OV562x,		/* ov5621 */
+	SENSOR_OV361x,		/* ov3610 */
+	NSENSORS
+};
+
+static const struct v4l2_pix_format ov965x_mode[] = {
+#define QVGA_MODE 0
+	{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+		.bytesperline = 320,
+		.sizeimage = 320 * 240 * 3 / 8 + 590,
+		.colorspace = V4L2_COLORSPACE_JPEG},
+#define VGA_MODE 1
+	{640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+		.bytesperline = 640,
+		.sizeimage = 640 * 480 * 3 / 8 + 590,
+		.colorspace = V4L2_COLORSPACE_JPEG},
+#define SVGA_MODE 2
+	{800, 600, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+		.bytesperline = 800,
+		.sizeimage = 800 * 600 * 3 / 8 + 590,
+		.colorspace = V4L2_COLORSPACE_JPEG},
+#define XGA_MODE 3
+	{1024, 768, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+		.bytesperline = 1024,
+		.sizeimage = 1024 * 768 * 3 / 8 + 590,
+		.colorspace = V4L2_COLORSPACE_JPEG},
+#define SXGA_MODE 4
+	{1280, 1024, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+		.bytesperline = 1280,
+		.sizeimage = 1280 * 1024 * 3 / 8 + 590,
+		.colorspace = V4L2_COLORSPACE_JPEG},
+};
+
+static const struct v4l2_pix_format ov971x_mode[] = {
+	{640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+		.bytesperline = 640,
+		.sizeimage = 640 * 480,
+		.colorspace = V4L2_COLORSPACE_SRGB
+	}
+};
+
+static const struct v4l2_pix_format ov562x_mode[] = {
+	{2592, 1680, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+		.bytesperline = 2592,
+		.sizeimage = 2592 * 1680,
+		.colorspace = V4L2_COLORSPACE_SRGB
+	}
+};
+
+enum ov361x {
+	ov361x_2048 = 0,
+	ov361x_1600,
+	ov361x_1024,
+	ov361x_640,
+	ov361x_320,
+	ov361x_160,
+	ov361x_last
+};
+
+static const struct v4l2_pix_format ov361x_mode[] = {
+	{0x800, 0x600, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+		.bytesperline = 0x800,
+		.sizeimage = 0x800 * 0x600,
+		.colorspace = V4L2_COLORSPACE_SRGB},
+	{1600, 1200, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+		.bytesperline = 1600,
+		.sizeimage = 1600 * 1200,
+		.colorspace = V4L2_COLORSPACE_SRGB},
+	{1024, 768, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+		.bytesperline = 768,
+		.sizeimage = 1024 * 768,
+		.colorspace = V4L2_COLORSPACE_SRGB},
+	{640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+		.bytesperline = 640,
+		.sizeimage = 640 * 480,
+		.colorspace = V4L2_COLORSPACE_SRGB},
+	{320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+		.bytesperline = 320,
+		.sizeimage = 320 * 240,
+		.colorspace = V4L2_COLORSPACE_SRGB},
+	{160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+		.bytesperline = 160,
+		.sizeimage = 160 * 120,
+		.colorspace = V4L2_COLORSPACE_SRGB}
+};
+
+static const u8 ov361x_start_2048[][2] = {
+	{0x12, 0x80},
+	{0x13, 0xcf},
+	{0x14, 0x40},
+	{0x15, 0x00},
+	{0x01, 0x80},
+	{0x02, 0x80},
+	{0x04, 0x70},
+	{0x0d, 0x40},
+	{0x0f, 0x47},
+	{0x11, 0x81},
+	{0x32, 0x36},
+	{0x33, 0x0c},
+	{0x34, 0x00},
+	{0x35, 0x90},
+	{0x12, 0x00},
+	{0x17, 0x10},
+	{0x18, 0x90},
+	{0x19, 0x00},
+	{0x1a, 0xc0},
+};
+static const u8 ov361x_bridge_start_2048[][2] = {
+	{0xf1, 0x60},
+	{0x88, 0x00},
+	{0x89, 0x08},
+	{0x8a, 0x00},
+	{0x8b, 0x06},
+	{0x8c, 0x01},
+	{0x8d, 0x10},
+	{0x1c, 0x00},
+	{0x1d, 0x48},
+	{0x1d, 0x00},
+	{0x1d, 0xff},
+	{0x1c, 0x0a},
+	{0x1d, 0x2e},
+	{0x1d, 0x1e},
+};
+
+static const u8 ov361x_start_1600[][2] = {
+	{0x12, 0x80},
+	{0x13, 0xcf},
+	{0x14, 0x40},
+	{0x15, 0x00},
+	{0x01, 0x80},
+	{0x02, 0x80},
+	{0x04, 0x70},
+	{0x0d, 0x40},
+	{0x0f, 0x47},
+	{0x11, 0x81},
+	{0x32, 0x36},
+	{0x33, 0x0C},
+	{0x34, 0x00},
+	{0x35, 0x90},
+	{0x12, 0x00},
+	{0x17, 0x10},
+	{0x18, 0x90},
+	{0x19, 0x00},
+	{0x1a, 0xc0},
+};
+static const u8 ov361x_bridge_start_1600[][2] = {
+	{0xf1, 0x60},  /* Hsize[7:0] */
+	{0x88, 0x00},  /* Hsize[15:8] Write Only, can't read */
+	{0x89, 0x08},  /* Vsize[7:0] */
+	{0x8a, 0x00},  /* Vsize[15:8] Write Only, can't read */
+	{0x8b, 0x06},  /* for Iso */
+	{0x8c, 0x01},  /* RAW input */
+	{0x8d, 0x10},
+	{0x1c, 0x00},  /* RAW output, Iso transfer */
+	{0x1d, 0x48},
+	{0x1d, 0x00},
+	{0x1d, 0xff},
+	{0x1c, 0x0a},  /* turn off JPEG, Iso mode */
+	{0x1d, 0x2e},  /* for Iso */
+	{0x1d, 0x1e},
+};
+
+static const u8 ov361x_start_1024[][2] = {
+	{0x12, 0x80},
+	{0x13, 0xcf},
+	{0x14, 0x40},
+	{0x15, 0x00},
+	{0x01, 0x80},
+	{0x02, 0x80},
+	{0x04, 0x70},
+	{0x0d, 0x40},
+	{0x0f, 0x47},
+	{0x11, 0x81},
+	{0x32, 0x36},
+	{0x33, 0x0C},
+	{0x34, 0x00},
+	{0x35, 0x90},
+	{0x12, 0x40},
+	{0x17, 0x1f},
+	{0x18, 0x5f},
+	{0x19, 0x00},
+	{0x1a, 0x68},
+};
+static const u8 ov361x_bridge_start_1024[][2] = {
+	{0xf1, 0x60},  /* Hsize[7:0] */
+	{0x88, 0x00},  /* Hsize[15:8] Write Only, can't read */
+	{0x89, 0x04},  /* Vsize[7:0] */
+	{0x8a, 0x00},  /* Vsize[15:8] Write Only, can't read */
+	{0x8b, 0x03},  /* for Iso */
+	{0x8c, 0x01},  /* RAW input  */
+	{0x8d, 0x10},
+	{0x1c, 0x00},  /* RAW output, Iso transfer */
+	{0x1d, 0x48},
+	{0x1d, 0x00},
+	{0x1d, 0xff},
+	{0x1c, 0x0a},  /* turn off JPEG, Iso mode */
+	{0x1d, 0x2e},  /* for Iso */
+	{0x1d, 0x1e},
+};
+
+static const u8 ov361x_start_640[][2] = {
+	{0x12, 0x80},
+	{0x13, 0xcf},
+	{0x14, 0x40},
+	{0x15, 0x00},
+	{0x01, 0x80},
+	{0x02, 0x80},
+	{0x04, 0x70},
+	{0x0d, 0x40},
+	{0x0f, 0x47},
+	{0x11, 0x81},
+	{0x32, 0x36},
+	{0x33, 0x0C},
+	{0x34, 0x00},
+	{0x35, 0x90},
+	{0x12, 0x40},
+	{0x17, 0x1f},
+	{0x18, 0x5f},
+	{0x19, 0x00},
+	{0x1a, 0x68},
+};
+
+static const u8 ov361x_bridge_start_640[][2] = {
+	{0xf1, 0x60},  /* Hsize[7:0]*/
+	{0x88, 0x00},  /* Hsize[15:8] Write Only, can't read */
+	{0x89, 0x04},  /* Vsize[7:0] */
+	{0x8a, 0x00},  /* Vsize[15:8] Write Only, can't read */
+	{0x8b, 0x03},  /* for Iso */
+	{0x8c, 0x01},  /* RAW input */
+	{0x8d, 0x10},
+	{0x1c, 0x00},  /* RAW output, Iso transfer */
+	{0x1d, 0x48},
+	{0x1d, 0x00},
+	{0x1d, 0xff},
+	{0x1c, 0x0a},  /* turn off JPEG, Iso mode */
+	{0x1d, 0x2e},  /* for Iso */
+	{0x1d, 0x1e},
+};
+
+static const u8 ov361x_start_320[][2] = {
+	{0x12, 0x80},
+	{0x13, 0xcf},
+	{0x14, 0x40},
+	{0x15, 0x00},
+	{0x01, 0x80},
+	{0x02, 0x80},
+	{0x04, 0x70},
+	{0x0d, 0x40},
+	{0x0f, 0x47},
+	{0x11, 0x81},
+	{0x32, 0x36},
+	{0x33, 0x0C},
+	{0x34, 0x00},
+	{0x35, 0x90},
+	{0x12, 0x40},
+	{0x17, 0x1f},
+	{0x18, 0x5f},
+	{0x19, 0x00},
+	{0x1a, 0x68},
+};
+
+static const u8 ov361x_bridge_start_320[][2] = {
+	{0xf1, 0x60},  /* Hsize[7:0] */
+	{0x88, 0x00},  /* Hsize[15:8] Write Only, can't read */
+	{0x89, 0x04},  /* Vsize[7:0] */
+	{0x8a, 0x00},  /* Vsize[15:8] Write Only, can't read */
+	{0x8b, 0x03},  /* for Iso */
+	{0x8c, 0x01},  /* RAW input */
+	{0x8d, 0x10},
+	{0x1c, 0x00},  /* RAW output, Iso transfer; */
+	{0x1d, 0x48},
+	{0x1d, 0x00},
+	{0x1d, 0xff},
+	{0x1c, 0x0a},  /* turn off JPEG, Iso mode */
+	{0x1d, 0x2e},  /* for Iso */
+	{0x1d, 0x1e},
+};
+
+static const u8 ov361x_start_160[][2] = {
+	{0x12, 0x80},
+	{0x13, 0xcf},
+	{0x14, 0x40},
+	{0x15, 0x00},
+	{0x01, 0x80},
+	{0x02, 0x80},
+	{0x04, 0x70},
+	{0x0d, 0x40},
+	{0x0f, 0x47},
+	{0x11, 0x81},
+	{0x32, 0x36},
+	{0x33, 0x0C},
+	{0x34, 0x00},
+	{0x35, 0x90},
+	{0x12, 0x40},
+	{0x17, 0x1f},
+	{0x18, 0x5f},
+	{0x19, 0x00},
+	{0x1a, 0x68},
+};
+
+static const u8 ov361x_bridge_start_160[][2] = {
+	{0xf1, 0x60},  /* Hsize[7:0] */
+	{0x88, 0x00},  /* Hsize[15:8] Write Only, can't read */
+	{0x89, 0x04},  /* Vsize[7:0] */
+	{0x8a, 0x00},  /* Vsize[15:8] Write Only, can't read */
+	{0x8b, 0x03},  /* for Iso */
+	{0x8c, 0x01},  /* RAW input */
+	{0x8d, 0x10},
+	{0x1c, 0x00},  /* RAW output, Iso transfer */
+	{0x1d, 0x48},
+	{0x1d, 0x00},
+	{0x1d, 0xff},
+	{0x1c, 0x0a},  /* turn off JPEG, Iso mode */
+	{0x1d, 0x2e},  /* for Iso */
+	{0x1d, 0x1e},
+};
+
+static const u8 bridge_init[][2] = {
+	{0x88, 0xf8},
+	{0x89, 0xff},
+	{0x76, 0x03},
+	{0x92, 0x03},
+	{0x95, 0x10},
+	{0xe2, 0x00},
+	{0xe7, 0x3e},
+	{0x8d, 0x1c},
+	{0x8e, 0x00},
+	{0x8f, 0x00},
+	{0x1f, 0x00},
+	{0xc3, 0xf9},
+	{0x89, 0xff},
+	{0x88, 0xf8},
+	{0x76, 0x03},
+	{0x92, 0x01},
+	{0x93, 0x18},
+	{0x1c, 0x0a},
+	{0x1d, 0x48},
+	{0xc0, 0x50},
+	{0xc1, 0x3c},
+	{0x34, 0x05},
+	{0xc2, 0x0c},
+	{0xc3, 0xf9},
+	{0x34, 0x05},
+	{0xe7, 0x2e},
+	{0x31, 0xf9},
+	{0x35, 0x02},
+	{0xd9, 0x10},
+	{0x25, 0x42},
+	{0x94, 0x11},
+};
+
+static const u8 ov965x_init[][2] = {
+	{0x12, 0x80},	/* com7 - SSCB reset */
+	{0x00, 0x00},	/* gain */
+	{0x01, 0x80},	/* blue */
+	{0x02, 0x80},	/* red */
+	{0x03, 0x1b},	/* vref */
+	{0x04, 0x03},	/* com1 - exposure low bits */
+	{0x0b, 0x57},	/* ver */
+	{0x0e, 0x61},	/* com5 */
+	{0x0f, 0x42},	/* com6 */
+	{0x11, 0x00},	/* clkrc */
+	{0x12, 0x02},	/* com7 - 15fps VGA YUYV */
+	{0x13, 0xe7},	/* com8 - everything (AGC, AWB and AEC) */
+	{0x14, 0x28},	/* com9 */
+	{0x16, 0x24},	/* reg16 */
+	{0x17, 0x1d},	/* hstart*/
+	{0x18, 0xbd},	/* hstop */
+	{0x19, 0x01},	/* vstrt */
+	{0x1a, 0x81},	/* vstop*/
+	{0x1e, 0x04},	/* mvfp */
+	{0x24, 0x3c},	/* aew */
+	{0x25, 0x36},	/* aeb */
+	{0x26, 0x71},	/* vpt */
+	{0x27, 0x08},	/* bbias */
+	{0x28, 0x08},	/* gbbias */
+	{0x29, 0x15},	/* gr com */
+	{0x2a, 0x00},	/* exhch */
+	{0x2b, 0x00},	/* exhcl */
+	{0x2c, 0x08},	/* rbias */
+	{0x32, 0xff},	/* href */
+	{0x33, 0x00},	/* chlf */
+	{0x34, 0x3f},	/* aref1 */
+	{0x35, 0x00},	/* aref2 */
+	{0x36, 0xf8},	/* aref3 */
+	{0x38, 0x72},	/* adc2 */
+	{0x39, 0x57},	/* aref4 */
+	{0x3a, 0x80},	/* tslb - yuyv */
+	{0x3b, 0xc4},	/* com11 - night mode 1/4 frame rate */
+	{0x3d, 0x99},	/* com13 */
+	{0x3f, 0xc1},	/* edge */
+	{0x40, 0xc0},	/* com15 */
+	{0x41, 0x40},	/* com16 */
+	{0x42, 0xc0},	/* com17 */
+	{0x43, 0x0a},	/* rsvd */
+	{0x44, 0xf0},
+	{0x45, 0x46},
+	{0x46, 0x62},
+	{0x47, 0x2a},
+	{0x48, 0x3c},
+	{0x4a, 0xfc},
+	{0x4b, 0xfc},
+	{0x4c, 0x7f},
+	{0x4d, 0x7f},
+	{0x4e, 0x7f},
+	{0x4f, 0x98},	/* matrix */
+	{0x50, 0x98},
+	{0x51, 0x00},
+	{0x52, 0x28},
+	{0x53, 0x70},
+	{0x54, 0x98},
+	{0x58, 0x1a},	/* matrix coef sign */
+	{0x59, 0x85},	/* AWB control */
+	{0x5a, 0xa9},
+	{0x5b, 0x64},
+	{0x5c, 0x84},
+	{0x5d, 0x53},
+	{0x5e, 0x0e},
+	{0x5f, 0xf0},	/* AWB blue limit */
+	{0x60, 0xf0},	/* AWB red limit */
+	{0x61, 0xf0},	/* AWB green limit */
+	{0x62, 0x00},	/* lcc1 */
+	{0x63, 0x00},	/* lcc2 */
+	{0x64, 0x02},	/* lcc3 */
+	{0x65, 0x16},	/* lcc4 */
+	{0x66, 0x01},	/* lcc5 */
+	{0x69, 0x02},	/* hv */
+	{0x6b, 0x5a},	/* dbvl */
+	{0x6c, 0x04},
+	{0x6d, 0x55},
+	{0x6e, 0x00},
+	{0x6f, 0x9d},
+	{0x70, 0x21},	/* dnsth */
+	{0x71, 0x78},
+	{0x72, 0x00},	/* poidx */
+	{0x73, 0x01},	/* pckdv */
+	{0x74, 0x3a},	/* xindx */
+	{0x75, 0x35},	/* yindx */
+	{0x76, 0x01},
+	{0x77, 0x02},
+	{0x7a, 0x12},	/* gamma curve */
+	{0x7b, 0x08},
+	{0x7c, 0x16},
+	{0x7d, 0x30},
+	{0x7e, 0x5e},
+	{0x7f, 0x72},
+	{0x80, 0x82},
+	{0x81, 0x8e},
+	{0x82, 0x9a},
+	{0x83, 0xa4},
+	{0x84, 0xac},
+	{0x85, 0xb8},
+	{0x86, 0xc3},
+	{0x87, 0xd6},
+	{0x88, 0xe6},
+	{0x89, 0xf2},
+	{0x8a, 0x03},
+	{0x8c, 0x89},	/* com19 */
+	{0x14, 0x28},	/* com9 */
+	{0x90, 0x7d},
+	{0x91, 0x7b},
+	{0x9d, 0x03},	/* lcc6 */
+	{0x9e, 0x04},	/* lcc7 */
+	{0x9f, 0x7a},
+	{0xa0, 0x79},
+	{0xa1, 0x40},	/* aechm */
+	{0xa4, 0x50},	/* com21 */
+	{0xa5, 0x68},	/* com26 */
+	{0xa6, 0x4a},	/* AWB green */
+	{0xa8, 0xc1},	/* refa8 */
+	{0xa9, 0xef},	/* refa9 */
+	{0xaa, 0x92},
+	{0xab, 0x04},
+	{0xac, 0x80},	/* black level control */
+	{0xad, 0x80},
+	{0xae, 0x80},
+	{0xaf, 0x80},
+	{0xb2, 0xf2},
+	{0xb3, 0x20},
+	{0xb4, 0x20},	/* ctrlb4 */
+	{0xb5, 0x00},
+	{0xb6, 0xaf},
+	{0xbb, 0xae},
+	{0xbc, 0x7f},	/* ADC channel offsets */
+	{0xdb, 0x7f},
+	{0xbe, 0x7f},
+	{0xbf, 0x7f},
+	{0xc0, 0xe2},
+	{0xc1, 0xc0},
+	{0xc2, 0x01},
+	{0xc3, 0x4e},
+	{0xc6, 0x85},
+	{0xc7, 0x80},	/* com24 */
+	{0xc9, 0xe0},
+	{0xca, 0xe8},
+	{0xcb, 0xf0},
+	{0xcc, 0xd8},
+	{0xcd, 0xf1},
+	{0x4f, 0x98},	/* matrix */
+	{0x50, 0x98},
+	{0x51, 0x00},
+	{0x52, 0x28},
+	{0x53, 0x70},
+	{0x54, 0x98},
+	{0x58, 0x1a},
+	{0xff, 0x41},	/* read 41, write ff 00 */
+	{0x41, 0x40},	/* com16 */
+
+	{0xc5, 0x03},	/* 60 Hz banding filter */
+	{0x6a, 0x02},	/* 50 Hz banding filter */
+
+	{0x12, 0x62},	/* com7 - 30fps VGA YUV */
+	{0x36, 0xfa},	/* aref3 */
+	{0x69, 0x0a},	/* hv */
+	{0x8c, 0x89},	/* com22 */
+	{0x14, 0x28},	/* com9 */
+	{0x3e, 0x0c},
+	{0x41, 0x40},	/* com16 */
+	{0x72, 0x00},
+	{0x73, 0x00},
+	{0x74, 0x3a},
+	{0x75, 0x35},
+	{0x76, 0x01},
+	{0xc7, 0x80},
+	{0x03, 0x12},	/* vref */
+	{0x17, 0x16},	/* hstart */
+	{0x18, 0x02},	/* hstop */
+	{0x19, 0x01},	/* vstrt */
+	{0x1a, 0x3d},	/* vstop */
+	{0x32, 0xff},	/* href */
+	{0xc0, 0xaa},
+};
+
+static const u8 bridge_init_2[][2] = {
+	{0x94, 0xaa},
+	{0xf1, 0x60},
+	{0xe5, 0x04},
+	{0xc0, 0x50},
+	{0xc1, 0x3c},
+	{0x8c, 0x00},
+	{0x8d, 0x1c},
+	{0x34, 0x05},
+
+	{0xc2, 0x0c},
+	{0xc3, 0xf9},
+	{0xda, 0x01},
+	{0x50, 0x00},
+	{0x51, 0xa0},
+	{0x52, 0x3c},
+	{0x53, 0x00},
+	{0x54, 0x00},
+	{0x55, 0x00},
+	{0x57, 0x00},
+	{0x5c, 0x00},
+	{0x5a, 0xa0},
+	{0x5b, 0x78},
+	{0x35, 0x02},
+	{0xd9, 0x10},
+	{0x94, 0x11},
+};
+
+static const u8 ov965x_init_2[][2] = {
+	{0x3b, 0xc4},
+	{0x1e, 0x04},	/* mvfp */
+	{0x13, 0xe0},	/* com8 */
+	{0x00, 0x00},	/* gain */
+	{0x13, 0xe7},	/* com8 - everything (AGC, AWB and AEC) */
+	{0x11, 0x03},	/* clkrc */
+	{0x6b, 0x5a},	/* dblv */
+	{0x6a, 0x05},
+	{0xc5, 0x07},
+	{0xa2, 0x4b},
+	{0xa3, 0x3e},
+	{0x2d, 0x00},
+	{0xff, 0x42},	/* read 42, write ff 00 */
+	{0x42, 0xc0},	/* com17 */
+	{0x2d, 0x00},
+	{0xff, 0x42},	/* read 42, write ff 00 */
+	{0x42, 0xc1},	/* com17 */
+/* sharpness */
+	{0x3f, 0x01},
+	{0xff, 0x42},	/* read 42, write ff 00 */
+	{0x42, 0xc1},	/* com17 */
+/* saturation */
+	{0x4f, 0x98},	/* matrix */
+	{0x50, 0x98},
+	{0x51, 0x00},
+	{0x52, 0x28},
+	{0x53, 0x70},
+	{0x54, 0x98},
+	{0x58, 0x1a},
+	{0xff, 0x41},	/* read 41, write ff 00 */
+	{0x41, 0x40},	/* com16 */
+/* contrast */
+	{0x56, 0x40},
+/* brightness */
+	{0x55, 0x8f},
+/* expo */
+	{0x10, 0x25},	/* aech - exposure high bits */
+	{0xff, 0x13},	/* read 13, write ff 00 */
+	{0x13, 0xe7},	/* com8 - everything (AGC, AWB and AEC) */
+};
+
+static const u8 ov971x_init[][2] = {
+	{0x12, 0x80},
+	{0x09, 0x10},
+	{0x1e, 0x07},
+	{0x5f, 0x18},
+	{0x69, 0x04},
+	{0x65, 0x2a},
+	{0x68, 0x0a},
+	{0x39, 0x28},
+	{0x4d, 0x90},
+	{0xc1, 0x80},
+	{0x0c, 0x30},
+	{0x6d, 0x02},
+	{0x96, 0xf1},
+	{0xbc, 0x68},
+	{0x12, 0x00},
+	{0x3b, 0x00},
+	{0x97, 0x80},
+	{0x17, 0x25},
+	{0x18, 0xa2},
+	{0x19, 0x01},
+	{0x1a, 0xca},
+	{0x03, 0x0a},
+	{0x32, 0x07},
+	{0x98, 0x40},	/*{0x98, 0x00},*/
+	{0x99, 0xA0},	/*{0x99, 0x00},*/
+	{0x9a, 0x01},	/*{0x9a, 0x00},*/
+	{0x57, 0x00},
+	{0x58, 0x78},	/*{0x58, 0xc8},*/
+	{0x59, 0x50},	/*{0x59, 0xa0},*/
+	{0x4c, 0x13},
+	{0x4b, 0x36},
+	{0x3d, 0x3c},
+	{0x3e, 0x03},
+	{0xbd, 0x50},	/*{0xbd, 0xa0},*/
+	{0xbe, 0x78},	/*{0xbe, 0xc8},*/
+	{0x4e, 0x55},
+	{0x4f, 0x55},
+	{0x50, 0x55},
+	{0x51, 0x55},
+	{0x24, 0x55},
+	{0x25, 0x40},
+	{0x26, 0xa1},
+	{0x5c, 0x59},
+	{0x5d, 0x00},
+	{0x11, 0x00},
+	{0x2a, 0x98},
+	{0x2b, 0x06},
+	{0x2d, 0x00},
+	{0x2e, 0x00},
+	{0x13, 0xa5},
+	{0x14, 0x40},
+	{0x4a, 0x00},
+	{0x49, 0xce},
+	{0x22, 0x03},
+	{0x09, 0x00}
+};
+
+static const u8 ov965x_start_1_vga[][2] = {	/* same for qvga */
+	{0x12, 0x62},	/* com7 - 30fps VGA YUV */
+	{0x36, 0xfa},	/* aref3 */
+	{0x69, 0x0a},	/* hv */
+	{0x8c, 0x89},	/* com22 */
+	{0x14, 0x28},	/* com9 */
+	{0x3e, 0x0c},	/* com14 */
+	{0x41, 0x40},	/* com16 */
+	{0x72, 0x00},
+	{0x73, 0x00},
+	{0x74, 0x3a},
+	{0x75, 0x35},
+	{0x76, 0x01},
+	{0xc7, 0x80},	/* com24 */
+	{0x03, 0x12},	/* vref */
+	{0x17, 0x16},	/* hstart */
+	{0x18, 0x02},	/* hstop */
+	{0x19, 0x01},	/* vstrt */
+	{0x1a, 0x3d},	/* vstop */
+	{0x32, 0xff},	/* href */
+	{0xc0, 0xaa},
+};
+
+static const u8 ov965x_start_1_svga[][2] = {
+	{0x12, 0x02},	/* com7 - YUYV - VGA 15 full resolution */
+	{0x36, 0xf8},	/* aref3 */
+	{0x69, 0x02},	/* hv */
+	{0x8c, 0x0d},	/* com22 */
+	{0x3e, 0x0c},	/* com14 */
+	{0x41, 0x40},	/* com16 */
+	{0x72, 0x00},
+	{0x73, 0x01},
+	{0x74, 0x3a},
+	{0x75, 0x35},
+	{0x76, 0x01},
+	{0xc7, 0x80},	/* com24 */
+	{0x03, 0x1b},	/* vref */
+	{0x17, 0x1d},	/* hstart */
+	{0x18, 0xbd},	/* hstop */
+	{0x19, 0x01},	/* vstrt */
+	{0x1a, 0x81},	/* vstop */
+	{0x32, 0xff},	/* href */
+	{0xc0, 0xe2},
+};
+
+static const u8 ov965x_start_1_xga[][2] = {
+	{0x12, 0x02},	/* com7 */
+	{0x36, 0xf8},	/* aref3 */
+	{0x69, 0x02},	/* hv */
+	{0x8c, 0x89},	/* com22 */
+	{0x14, 0x28},	/* com9 */
+	{0x3e, 0x0c},	/* com14 */
+	{0x41, 0x40},	/* com16 */
+	{0x72, 0x00},
+	{0x73, 0x01},
+	{0x74, 0x3a},
+	{0x75, 0x35},
+	{0x76, 0x01},
+	{0xc7, 0x80},	/* com24 */
+	{0x03, 0x1b},	/* vref */
+	{0x17, 0x1d},	/* hstart */
+	{0x18, 0xbd},	/* hstop */
+	{0x19, 0x01},	/* vstrt */
+	{0x1a, 0x81},	/* vstop */
+	{0x32, 0xff},	/* href */
+	{0xc0, 0xe2},
+};
+
+static const u8 ov965x_start_1_sxga[][2] = {
+	{0x12, 0x02},	/* com7 */
+	{0x36, 0xf8},	/* aref3 */
+	{0x69, 0x02},	/* hv */
+	{0x8c, 0x89},	/* com22 */
+	{0x14, 0x28},	/* com9 */
+	{0x3e, 0x0c},	/* com14 */
+	{0x41, 0x40},	/* com16 */
+	{0x72, 0x00},
+	{0x73, 0x01},
+	{0x74, 0x3a},
+	{0x75, 0x35},
+	{0x76, 0x01},
+	{0xc7, 0x80},	/* com24 */
+	{0x03, 0x1b},	/* vref */
+	{0x17, 0x1d},	/* hstart */
+	{0x18, 0x02},	/* hstop */
+	{0x19, 0x01},	/* vstrt */
+	{0x1a, 0x81},	/* vstop */
+	{0x32, 0xff},	/* href */
+	{0xc0, 0xe2},
+};
+
+static const u8 bridge_start_qvga[][2] = {
+	{0x94, 0xaa},
+	{0xf1, 0x60},
+	{0xe5, 0x04},
+	{0xc0, 0x50},
+	{0xc1, 0x3c},
+	{0x8c, 0x00},
+	{0x8d, 0x1c},
+	{0x34, 0x05},
+
+	{0xc2, 0x4c},
+	{0xc3, 0xf9},
+	{0xda, 0x00},
+	{0x50, 0x00},
+	{0x51, 0xa0},
+	{0x52, 0x78},
+	{0x53, 0x00},
+	{0x54, 0x00},
+	{0x55, 0x00},
+	{0x57, 0x00},
+	{0x5c, 0x00},
+	{0x5a, 0x50},
+	{0x5b, 0x3c},
+	{0x35, 0x02},
+	{0xd9, 0x10},
+	{0x94, 0x11},
+};
+
+static const u8 bridge_start_vga[][2] = {
+	{0x94, 0xaa},
+	{0xf1, 0x60},
+	{0xe5, 0x04},
+	{0xc0, 0x50},
+	{0xc1, 0x3c},
+	{0x8c, 0x00},
+	{0x8d, 0x1c},
+	{0x34, 0x05},
+	{0xc2, 0x0c},
+	{0xc3, 0xf9},
+	{0xda, 0x01},
+	{0x50, 0x00},
+	{0x51, 0xa0},
+	{0x52, 0x3c},
+	{0x53, 0x00},
+	{0x54, 0x00},
+	{0x55, 0x00},
+	{0x57, 0x00},
+	{0x5c, 0x00},
+	{0x5a, 0xa0},
+	{0x5b, 0x78},
+	{0x35, 0x02},
+	{0xd9, 0x10},
+	{0x94, 0x11},
+};
+
+static const u8 bridge_start_svga[][2] = {
+	{0x94, 0xaa},
+	{0xf1, 0x60},
+	{0xe5, 0x04},
+	{0xc0, 0xa0},
+	{0xc1, 0x80},
+	{0x8c, 0x00},
+	{0x8d, 0x1c},
+	{0x34, 0x05},
+	{0xc2, 0x4c},
+	{0xc3, 0xf9},
+	{0x50, 0x00},
+	{0x51, 0x40},
+	{0x52, 0x00},
+	{0x53, 0x00},
+	{0x54, 0x00},
+	{0x55, 0x88},
+	{0x57, 0x00},
+	{0x5c, 0x00},
+	{0x5a, 0xc8},
+	{0x5b, 0x96},
+	{0x35, 0x02},
+	{0xd9, 0x10},
+	{0xda, 0x00},
+	{0x94, 0x11},
+};
+
+static const u8 bridge_start_xga[][2] = {
+	{0x94, 0xaa},
+	{0xf1, 0x60},
+	{0xe5, 0x04},
+	{0xc0, 0xa0},
+	{0xc1, 0x80},
+	{0x8c, 0x00},
+	{0x8d, 0x1c},
+	{0x34, 0x05},
+	{0xc2, 0x4c},
+	{0xc3, 0xf9},
+	{0x50, 0x00},
+	{0x51, 0x40},
+	{0x52, 0x00},
+	{0x53, 0x00},
+	{0x54, 0x00},
+	{0x55, 0x88},
+	{0x57, 0x00},
+	{0x5c, 0x01},
+	{0x5a, 0x00},
+	{0x5b, 0xc0},
+	{0x35, 0x02},
+	{0xd9, 0x10},
+	{0xda, 0x01},
+	{0x94, 0x11},
+};
+
+static const u8 bridge_start_sxga[][2] = {
+	{0x94, 0xaa},
+	{0xf1, 0x60},
+	{0xe5, 0x04},
+	{0xc0, 0xa0},
+	{0xc1, 0x80},
+	{0x8c, 0x00},
+	{0x8d, 0x1c},
+	{0x34, 0x05},
+	{0xc2, 0x0c},
+	{0xc3, 0xf9},
+	{0xda, 0x00},
+	{0x35, 0x02},
+	{0xd9, 0x10},
+	{0x94, 0x11},
+};
+
+static const u8 ov965x_start_2_qvga[][2] = {
+	{0x3b, 0xe4},	/* com11 - night mode 1/4 frame rate */
+	{0x1e, 0x04},	/* mvfp */
+	{0x13, 0xe0},	/* com8 */
+	{0x00, 0x00},
+	{0x13, 0xe7},	/* com8 - everything (AGC, AWB and AEC) */
+	{0x11, 0x01},	/* clkrc */
+	{0x6b, 0x5a},	/* dblv */
+	{0x6a, 0x02},	/* 50 Hz banding filter */
+	{0xc5, 0x03},	/* 60 Hz banding filter */
+	{0xa2, 0x96},	/* bd50 */
+	{0xa3, 0x7d},	/* bd60 */
+
+	{0xff, 0x13},	/* read 13, write ff 00 */
+	{0x13, 0xe7},
+	{0x3a, 0x80},	/* tslb - yuyv */
+};
+
+static const u8 ov965x_start_2_vga[][2] = {
+	{0x3b, 0xc4},	/* com11 - night mode 1/4 frame rate */
+	{0x1e, 0x04},	/* mvfp */
+	{0x13, 0xe0},	/* com8 */
+	{0x00, 0x00},
+	{0x13, 0xe7},	/* com8 - everything (AGC, AWB and AEC) */
+	{0x11, 0x03},	/* clkrc */
+	{0x6b, 0x5a},	/* dblv */
+	{0x6a, 0x05},	/* 50 Hz banding filter */
+	{0xc5, 0x07},	/* 60 Hz banding filter */
+	{0xa2, 0x4b},	/* bd50 */
+	{0xa3, 0x3e},	/* bd60 */
+
+	{0x2d, 0x00},	/* advfl */
+};
+
+static const u8 ov965x_start_2_svga[][2] = {	/* same for xga */
+	{0x3b, 0xc4},	/* com11 - night mode 1/4 frame rate */
+	{0x1e, 0x04},	/* mvfp */
+	{0x13, 0xe0},	/* com8 */
+	{0x00, 0x00},
+	{0x13, 0xe7},	/* com8 - everything (AGC, AWB and AEC) */
+	{0x11, 0x01},	/* clkrc */
+	{0x6b, 0x5a},	/* dblv */
+	{0x6a, 0x0c},	/* 50 Hz banding filter */
+	{0xc5, 0x0f},	/* 60 Hz banding filter */
+	{0xa2, 0x4e},	/* bd50 */
+	{0xa3, 0x41},	/* bd60 */
+};
+
+static const u8 ov965x_start_2_sxga[][2] = {
+	{0x13, 0xe0},	/* com8 */
+	{0x00, 0x00},
+	{0x13, 0xe7},	/* com8 - everything (AGC, AWB and AEC) */
+	{0x3b, 0xc4},	/* com11 - night mode 1/4 frame rate */
+	{0x1e, 0x04},	/* mvfp */
+	{0x11, 0x01},	/* clkrc */
+	{0x6b, 0x5a},	/* dblv */
+	{0x6a, 0x0c},	/* 50 Hz banding filter */
+	{0xc5, 0x0f},	/* 60 Hz banding filter */
+	{0xa2, 0x4e},	/* bd50 */
+	{0xa3, 0x41},	/* bd60 */
+};
+
+static const u8 ov562x_init[][2] = {
+	{0x88, 0x20},
+	{0x89, 0x0a},
+	{0x8a, 0x90},
+	{0x8b, 0x06},
+	{0x8c, 0x01},
+	{0x8d, 0x10},
+	{0x1c, 0x00},
+	{0x1d, 0x48},
+	{0x1d, 0x00},
+	{0x1d, 0xff},
+	{0x1c, 0x0a},
+	{0x1d, 0x2e},
+	{0x1d, 0x1e},
+};
+
+static const u8 ov562x_init_2[][2] = {
+	{0x12, 0x80},
+	{0x11, 0x41},
+	{0x13, 0x00},
+	{0x10, 0x1e},
+	{0x3b, 0x07},
+	{0x5b, 0x40},
+	{0x39, 0x07},
+	{0x53, 0x02},
+	{0x54, 0x60},
+	{0x04, 0x20},
+	{0x27, 0x04},
+	{0x3d, 0x40},
+	{0x36, 0x00},
+	{0xc5, 0x04},
+	{0x4e, 0x00},
+	{0x4f, 0x93},
+	{0x50, 0x7b},
+	{0xca, 0x0c},
+	{0xcb, 0x0f},
+	{0x39, 0x07},
+	{0x4a, 0x10},
+	{0x3e, 0x0a},
+	{0x3d, 0x00},
+	{0x0c, 0x38},
+	{0x38, 0x90},
+	{0x46, 0x30},
+	{0x4f, 0x93},
+	{0x50, 0x7b},
+	{0xab, 0x00},
+	{0xca, 0x0c},
+	{0xcb, 0x0f},
+	{0x37, 0x02},
+	{0x44, 0x48},
+	{0x8d, 0x44},
+	{0x2a, 0x00},
+	{0x2b, 0x00},
+	{0x32, 0x00},
+	{0x38, 0x90},
+	{0x53, 0x02},
+	{0x54, 0x60},
+	{0x12, 0x00},
+	{0x17, 0x12},
+	{0x18, 0xb4},
+	{0x19, 0x0c},
+	{0x1a, 0xf4},
+	{0x03, 0x4a},
+	{0x89, 0x20},
+	{0x83, 0x80},
+	{0xb7, 0x9d},
+	{0xb6, 0x11},
+	{0xb5, 0x55},
+	{0xb4, 0x00},
+	{0xa9, 0xf0},
+	{0xa8, 0x0a},
+	{0xb8, 0xf0},
+	{0xb9, 0xf0},
+	{0xba, 0xf0},
+	{0x81, 0x07},
+	{0x63, 0x44},
+	{0x13, 0xc7},
+	{0x14, 0x60},
+	{0x33, 0x75},
+	{0x2c, 0x00},
+	{0x09, 0x00},
+	{0x35, 0x30},
+	{0x27, 0x04},
+	{0x3c, 0x07},
+	{0x3a, 0x0a},
+	{0x3b, 0x07},
+	{0x01, 0x40},
+	{0x02, 0x40},
+	{0x16, 0x40},
+	{0x52, 0xb0},
+	{0x51, 0x83},
+	{0x21, 0xbb},
+	{0x22, 0x10},
+	{0x23, 0x03},
+	{0x35, 0x38},
+	{0x20, 0x90},
+	{0x28, 0x30},
+	{0x73, 0xe1},
+	{0x6c, 0x00},
+	{0x6d, 0x80},
+	{0x6e, 0x00},
+	{0x70, 0x04},
+	{0x71, 0x00},
+	{0x8d, 0x04},
+	{0x64, 0x00},
+	{0x65, 0x00},
+	{0x66, 0x00},
+	{0x67, 0x00},
+	{0x68, 0x00},
+	{0x69, 0x00},
+	{0x6a, 0x00},
+	{0x6b, 0x00},
+	{0x71, 0x94},
+	{0x74, 0x20},
+	{0x80, 0x09},
+	{0x85, 0xc0},
+};
+
+static void reg_w_i(struct gspca_dev *gspca_dev, u16 reg, u8 val)
+{
+	struct usb_device *udev = gspca_dev->dev;
+	int ret;
+
+	if (gspca_dev->usb_err < 0)
+		return;
+	gspca_dev->usb_buf[0] = val;
+	ret = usb_control_msg(udev,
+			      usb_sndctrlpipe(udev, 0),
+			      0x01,
+			      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			      0x00, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT);
+	if (ret < 0) {
+		pr_err("reg_w failed %d\n", ret);
+		gspca_dev->usb_err = ret;
+	}
+}
+
+static void reg_w(struct gspca_dev *gspca_dev, u16 reg, u8 val)
+{
+	PDEBUG(D_USBO, "reg_w [%04x] = %02x", reg, val);
+	reg_w_i(gspca_dev, reg, val);
+}
+
+static u8 reg_r(struct gspca_dev *gspca_dev, u16 reg)
+{
+	struct usb_device *udev = gspca_dev->dev;
+	int ret;
+
+	if (gspca_dev->usb_err < 0)
+		return 0;
+	ret = usb_control_msg(udev,
+			      usb_rcvctrlpipe(udev, 0),
+			      0x01,
+			      USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			      0x00, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT);
+	PDEBUG(D_USBI, "reg_r [%04x] -> %02x", reg, gspca_dev->usb_buf[0]);
+	if (ret < 0) {
+		pr_err("reg_r err %d\n", ret);
+		gspca_dev->usb_err = ret;
+	}
+	return gspca_dev->usb_buf[0];
+}
+
+static int sccb_check_status(struct gspca_dev *gspca_dev)
+{
+	u8 data;
+	int i;
+
+	for (i = 0; i < 5; i++) {
+		msleep(20);
+		data = reg_r(gspca_dev, OV534_REG_STATUS);
+
+		switch (data) {
+		case 0x00:
+			return 1;
+		case 0x04:
+			return 0;
+		case 0x03:
+			break;
+		default:
+			PDEBUG(D_USBI|D_USBO,
+				"sccb status 0x%02x, attempt %d/5",
+				data, i + 1);
+		}
+	}
+	return 0;
+}
+
+static void sccb_write(struct gspca_dev *gspca_dev, u8 reg, u8 val)
+{
+	PDEBUG(D_USBO, "sccb_write [%02x] = %02x", reg, val);
+	reg_w_i(gspca_dev, OV534_REG_SUBADDR, reg);
+	reg_w_i(gspca_dev, OV534_REG_WRITE, val);
+	reg_w_i(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_3);
+
+	if (!sccb_check_status(gspca_dev))
+		pr_err("sccb_write failed\n");
+}
+
+static u8 sccb_read(struct gspca_dev *gspca_dev, u16 reg)
+{
+	reg_w(gspca_dev, OV534_REG_SUBADDR, reg);
+	reg_w(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_2);
+	if (!sccb_check_status(gspca_dev))
+		pr_err("sccb_read failed 1\n");
+
+	reg_w(gspca_dev, OV534_REG_OPERATION, OV534_OP_READ_2);
+	if (!sccb_check_status(gspca_dev))
+		pr_err("sccb_read failed 2\n");
+
+	return reg_r(gspca_dev, OV534_REG_READ);
+}
+
+/* output a bridge sequence (reg - val) */
+static void reg_w_array(struct gspca_dev *gspca_dev,
+			const u8 (*data)[2], int len)
+{
+	while (--len >= 0) {
+		reg_w(gspca_dev, (*data)[0], (*data)[1]);
+		data++;
+	}
+}
+
+/* output a sensor sequence (reg - val) */
+static void sccb_w_array(struct gspca_dev *gspca_dev,
+			const u8 (*data)[2], int len)
+{
+	while (--len >= 0) {
+		if ((*data)[0] != 0xff) {
+			sccb_write(gspca_dev, (*data)[0], (*data)[1]);
+		} else {
+			sccb_read(gspca_dev, (*data)[1]);
+			sccb_write(gspca_dev, 0xff, 0x00);
+		}
+		data++;
+	}
+}
+
+/* Two bits control LED: 0x21 bit 7 and 0x23 bit 7.
+ * (direction and output)? */
+static void set_led(struct gspca_dev *gspca_dev, int status)
+{
+	u8 data;
+
+	PDEBUG(D_CONF, "led status: %d", status);
+
+	data = reg_r(gspca_dev, 0x21);
+	data |= 0x80;
+	reg_w(gspca_dev, 0x21, data);
+
+	data = reg_r(gspca_dev, 0x23);
+	if (status)
+		data |= 0x80;
+	else
+		data &= ~0x80;
+
+	reg_w(gspca_dev, 0x23, data);
+
+	if (!status) {
+		data = reg_r(gspca_dev, 0x21);
+		data &= ~0x80;
+		reg_w(gspca_dev, 0x21, data);
+	}
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev, s32 brightness)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u8 val;
+	s8 sval;
+
+	if (sd->sensor == SENSOR_OV562x) {
+		sval = brightness;
+		val = 0x76;
+		val += sval;
+		sccb_write(gspca_dev, 0x24, val);
+		val = 0x6a;
+		val += sval;
+		sccb_write(gspca_dev, 0x25, val);
+		if (sval < -40)
+			val = 0x71;
+		else if (sval < 20)
+			val = 0x94;
+		else
+			val = 0xe6;
+		sccb_write(gspca_dev, 0x26, val);
+	} else {
+		val = brightness;
+		if (val < 8)
+			val = 15 - val;		/* f .. 8 */
+		else
+			val = val - 8;		/* 0 .. 7 */
+		sccb_write(gspca_dev, 0x55,	/* brtn - brightness adjustment */
+				0x0f | (val << 4));
+	}
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
+{
+	sccb_write(gspca_dev, 0x56,	/* cnst1 - contrast 1 ctrl coeff */
+			val << 4);
+}
+
+static void setautogain(struct gspca_dev *gspca_dev, s32 autogain)
+{
+	u8 val;
+
+/*fixme: should adjust agc/awb/aec by different controls */
+	val = sccb_read(gspca_dev, 0x13);		/* com8 */
+	sccb_write(gspca_dev, 0xff, 0x00);
+	if (autogain)
+		val |= 0x05;		/* agc & aec */
+	else
+		val &= 0xfa;
+	sccb_write(gspca_dev, 0x13, val);
+}
+
+static void setexposure(struct gspca_dev *gspca_dev, s32 exposure)
+{
+	static const u8 expo[4] = {0x00, 0x25, 0x38, 0x5e};
+	u8 val;
+
+	sccb_write(gspca_dev, 0x10, expo[exposure]);	/* aec[9:2] */
+
+	val = sccb_read(gspca_dev, 0x13);		/* com8 */
+	sccb_write(gspca_dev, 0xff, 0x00);
+	sccb_write(gspca_dev, 0x13, val);
+
+	val = sccb_read(gspca_dev, 0xa1);		/* aech */
+	sccb_write(gspca_dev, 0xff, 0x00);
+	sccb_write(gspca_dev, 0xa1, val & 0xe0);	/* aec[15:10] = 0 */
+}
+
+static void setsharpness(struct gspca_dev *gspca_dev, s32 val)
+{
+	if (val < 0) {				/* auto */
+		val = sccb_read(gspca_dev, 0x42);	/* com17 */
+		sccb_write(gspca_dev, 0xff, 0x00);
+		sccb_write(gspca_dev, 0x42, val | 0x40);
+				/* Edge enhancement strength auto adjust */
+		return;
+	}
+	if (val != 0)
+		val = 1 << (val - 1);
+	sccb_write(gspca_dev, 0x3f,	/* edge - edge enhance. factor */
+			val);
+	val = sccb_read(gspca_dev, 0x42);		/* com17 */
+	sccb_write(gspca_dev, 0xff, 0x00);
+	sccb_write(gspca_dev, 0x42, val & 0xbf);
+}
+
+static void setsatur(struct gspca_dev *gspca_dev, s32 val)
+{
+	u8 val1, val2, val3;
+	static const u8 matrix[5][2] = {
+		{0x14, 0x38},
+		{0x1e, 0x54},
+		{0x28, 0x70},
+		{0x32, 0x8c},
+		{0x48, 0x90}
+	};
+
+	val1 = matrix[val][0];
+	val2 = matrix[val][1];
+	val3 = val1 + val2;
+	sccb_write(gspca_dev, 0x4f, val3);	/* matrix coeff */
+	sccb_write(gspca_dev, 0x50, val3);
+	sccb_write(gspca_dev, 0x51, 0x00);
+	sccb_write(gspca_dev, 0x52, val1);
+	sccb_write(gspca_dev, 0x53, val2);
+	sccb_write(gspca_dev, 0x54, val3);
+	sccb_write(gspca_dev, 0x58, 0x1a);	/* mtxs - coeff signs */
+
+	val1 = sccb_read(gspca_dev, 0x41);	/* com16 */
+	sccb_write(gspca_dev, 0xff, 0x00);
+	sccb_write(gspca_dev, 0x41, val1);
+}
+
+static void setlightfreq(struct gspca_dev *gspca_dev, s32 freq)
+{
+	u8 val;
+
+	val = sccb_read(gspca_dev, 0x13);		/* com8 */
+	sccb_write(gspca_dev, 0xff, 0x00);
+	if (freq == 0) {
+		sccb_write(gspca_dev, 0x13, val & 0xdf);
+		return;
+	}
+	sccb_write(gspca_dev, 0x13, val | 0x20);
+
+	val = sccb_read(gspca_dev, 0x42);		/* com17 */
+	sccb_write(gspca_dev, 0xff, 0x00);
+	if (freq == 1)
+		val |= 0x01;
+	else
+		val &= 0xfe;
+	sccb_write(gspca_dev, 0x42, val);
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+		     const struct usb_device_id *id)
+{
+	return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u16 sensor_id;
+
+	/* reset bridge */
+	reg_w(gspca_dev, 0xe7, 0x3a);
+	reg_w(gspca_dev, 0xe0, 0x08);
+	msleep(100);
+
+	/* initialize the sensor address */
+	reg_w(gspca_dev, OV534_REG_ADDRESS, 0x60);
+
+	/* reset sensor */
+	sccb_write(gspca_dev, 0x12, 0x80);
+	msleep(10);
+
+	/* probe the sensor */
+	sccb_read(gspca_dev, 0x0a);
+	sensor_id = sccb_read(gspca_dev, 0x0a) << 8;
+	sccb_read(gspca_dev, 0x0b);
+	sensor_id |= sccb_read(gspca_dev, 0x0b);
+	PDEBUG(D_PROBE, "Sensor ID: %04x", sensor_id);
+
+	/* initialize */
+	if ((sensor_id & 0xfff0) == 0x9650) {
+		sd->sensor = SENSOR_OV965x;
+
+		gspca_dev->cam.cam_mode = ov965x_mode;
+		gspca_dev->cam.nmodes = ARRAY_SIZE(ov965x_mode);
+
+		reg_w_array(gspca_dev, bridge_init,
+				ARRAY_SIZE(bridge_init));
+		sccb_w_array(gspca_dev, ov965x_init,
+				ARRAY_SIZE(ov965x_init));
+		reg_w_array(gspca_dev, bridge_init_2,
+				ARRAY_SIZE(bridge_init_2));
+		sccb_w_array(gspca_dev, ov965x_init_2,
+				ARRAY_SIZE(ov965x_init_2));
+		reg_w(gspca_dev, 0xe0, 0x00);
+		reg_w(gspca_dev, 0xe0, 0x01);
+		set_led(gspca_dev, 0);
+		reg_w(gspca_dev, 0xe0, 0x00);
+	} else if ((sensor_id & 0xfff0) == 0x9710) {
+		const char *p;
+		int l;
+
+		sd->sensor = SENSOR_OV971x;
+
+		gspca_dev->cam.cam_mode = ov971x_mode;
+		gspca_dev->cam.nmodes = ARRAY_SIZE(ov971x_mode);
+
+		gspca_dev->cam.bulk = 1;
+		gspca_dev->cam.bulk_size = 16384;
+		gspca_dev->cam.bulk_nurbs = 2;
+
+		sccb_w_array(gspca_dev, ov971x_init,
+				ARRAY_SIZE(ov971x_init));
+
+		/* set video format on bridge processor */
+		/* access bridge processor's video format registers at: 0x00 */
+		reg_w(gspca_dev, 0x1c, 0x00);
+		/*set register: 0x00 is 'RAW8', 0x40 is 'YUV422' (YUYV?)*/
+		reg_w(gspca_dev, 0x1d, 0x00);
+
+		/* Will W. specific stuff
+		 * set VSYNC to
+		 *	output (0x1f) if first webcam
+		 *	input (0x17) if 2nd or 3rd webcam */
+		p = video_device_node_name(&gspca_dev->vdev);
+		l = strlen(p) - 1;
+		if (p[l] == '0')
+			reg_w(gspca_dev, 0x56, 0x1f);
+		else
+			reg_w(gspca_dev, 0x56, 0x17);
+	} else if ((sensor_id & 0xfff0) == 0x5620) {
+		sd->sensor = SENSOR_OV562x;
+		gspca_dev->cam.cam_mode = ov562x_mode;
+		gspca_dev->cam.nmodes = ARRAY_SIZE(ov562x_mode);
+
+		reg_w_array(gspca_dev, ov562x_init,
+				ARRAY_SIZE(ov562x_init));
+		sccb_w_array(gspca_dev, ov562x_init_2,
+				ARRAY_SIZE(ov562x_init_2));
+		reg_w(gspca_dev, 0xe0, 0x00);
+	} else if ((sensor_id & 0xfff0) == 0x3610) {
+		sd->sensor = SENSOR_OV361x;
+		gspca_dev->cam.cam_mode = ov361x_mode;
+		gspca_dev->cam.nmodes = ARRAY_SIZE(ov361x_mode);
+		reg_w(gspca_dev, 0xe7, 0x3a);
+		reg_w(gspca_dev, 0xf1, 0x60);
+		sccb_write(gspca_dev, 0x12, 0x80);
+	} else {
+		pr_err("Unknown sensor %04x", sensor_id);
+		return -EINVAL;
+	}
+
+	return gspca_dev->usb_err;
+}
+
+static int sd_start_ov361x(struct gspca_dev *gspca_dev)
+{
+	sccb_write(gspca_dev, 0x12, 0x80);
+	msleep(20);
+	switch (gspca_dev->curr_mode % (ov361x_last)) {
+	case ov361x_2048:
+		reg_w_array(gspca_dev, ov361x_bridge_start_2048,
+			    ARRAY_SIZE(ov361x_bridge_start_2048));
+		sccb_w_array(gspca_dev, ov361x_start_2048,
+			     ARRAY_SIZE(ov361x_start_2048));
+		break;
+	case ov361x_1600:
+		reg_w_array(gspca_dev, ov361x_bridge_start_1600,
+			    ARRAY_SIZE(ov361x_bridge_start_1600));
+		sccb_w_array(gspca_dev, ov361x_start_1600,
+			     ARRAY_SIZE(ov361x_start_1600));
+		break;
+	case ov361x_1024:
+		reg_w_array(gspca_dev, ov361x_bridge_start_1024,
+			    ARRAY_SIZE(ov361x_bridge_start_1024));
+		sccb_w_array(gspca_dev, ov361x_start_1024,
+			     ARRAY_SIZE(ov361x_start_1024));
+		break;
+	case ov361x_640:
+		reg_w_array(gspca_dev, ov361x_bridge_start_640,
+			    ARRAY_SIZE(ov361x_bridge_start_640));
+		sccb_w_array(gspca_dev, ov361x_start_640,
+			     ARRAY_SIZE(ov361x_start_640));
+		break;
+	case ov361x_320:
+		reg_w_array(gspca_dev, ov361x_bridge_start_320,
+			    ARRAY_SIZE(ov361x_bridge_start_320));
+		sccb_w_array(gspca_dev, ov361x_start_320,
+			     ARRAY_SIZE(ov361x_start_320));
+		break;
+	case ov361x_160:
+		reg_w_array(gspca_dev, ov361x_bridge_start_160,
+			    ARRAY_SIZE(ov361x_bridge_start_160));
+		sccb_w_array(gspca_dev, ov361x_start_160,
+			     ARRAY_SIZE(ov361x_start_160));
+		break;
+	}
+	reg_w(gspca_dev, 0xe0, 0x00); /* start transfer */
+
+	return gspca_dev->usb_err;
+}
+
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (sd->sensor == SENSOR_OV971x)
+		return gspca_dev->usb_err;
+	if (sd->sensor == SENSOR_OV562x)
+		return gspca_dev->usb_err;
+	if (sd->sensor == SENSOR_OV361x)
+		return sd_start_ov361x(gspca_dev);
+
+	switch (gspca_dev->curr_mode) {
+	case QVGA_MODE:			/* 320x240 */
+		sccb_w_array(gspca_dev, ov965x_start_1_vga,
+				ARRAY_SIZE(ov965x_start_1_vga));
+		reg_w_array(gspca_dev, bridge_start_qvga,
+				ARRAY_SIZE(bridge_start_qvga));
+		sccb_w_array(gspca_dev, ov965x_start_2_qvga,
+				ARRAY_SIZE(ov965x_start_2_qvga));
+		break;
+	case VGA_MODE:			/* 640x480 */
+		sccb_w_array(gspca_dev, ov965x_start_1_vga,
+				ARRAY_SIZE(ov965x_start_1_vga));
+		reg_w_array(gspca_dev, bridge_start_vga,
+				ARRAY_SIZE(bridge_start_vga));
+		sccb_w_array(gspca_dev, ov965x_start_2_vga,
+				ARRAY_SIZE(ov965x_start_2_vga));
+		break;
+	case SVGA_MODE:			/* 800x600 */
+		sccb_w_array(gspca_dev, ov965x_start_1_svga,
+				ARRAY_SIZE(ov965x_start_1_svga));
+		reg_w_array(gspca_dev, bridge_start_svga,
+				ARRAY_SIZE(bridge_start_svga));
+		sccb_w_array(gspca_dev, ov965x_start_2_svga,
+				ARRAY_SIZE(ov965x_start_2_svga));
+		break;
+	case XGA_MODE:			/* 1024x768 */
+		sccb_w_array(gspca_dev, ov965x_start_1_xga,
+				ARRAY_SIZE(ov965x_start_1_xga));
+		reg_w_array(gspca_dev, bridge_start_xga,
+				ARRAY_SIZE(bridge_start_xga));
+		sccb_w_array(gspca_dev, ov965x_start_2_svga,
+				ARRAY_SIZE(ov965x_start_2_svga));
+		break;
+	default:
+/*	case SXGA_MODE:			 * 1280x1024 */
+		sccb_w_array(gspca_dev, ov965x_start_1_sxga,
+				ARRAY_SIZE(ov965x_start_1_sxga));
+		reg_w_array(gspca_dev, bridge_start_sxga,
+				ARRAY_SIZE(bridge_start_sxga));
+		sccb_w_array(gspca_dev, ov965x_start_2_sxga,
+				ARRAY_SIZE(ov965x_start_2_sxga));
+		break;
+	}
+
+	reg_w(gspca_dev, 0xe0, 0x00);
+	reg_w(gspca_dev, 0xe0, 0x00);
+	set_led(gspca_dev, 1);
+	return gspca_dev->usb_err;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+	if (((struct sd *)gspca_dev)->sensor == SENSOR_OV361x) {
+		reg_w(gspca_dev, 0xe0, 0x01); /* stop transfer */
+		/* reg_w(gspca_dev, 0x31, 0x09); */
+		return;
+	}
+	reg_w(gspca_dev, 0xe0, 0x01);
+	set_led(gspca_dev, 0);
+	reg_w(gspca_dev, 0xe0, 0x00);
+}
+
+/* Values for bmHeaderInfo (Video and Still Image Payload Headers, 2.4.3.3) */
+#define UVC_STREAM_EOH	(1 << 7)
+#define UVC_STREAM_ERR	(1 << 6)
+#define UVC_STREAM_STI	(1 << 5)
+#define UVC_STREAM_RES	(1 << 4)
+#define UVC_STREAM_SCR	(1 << 3)
+#define UVC_STREAM_PTS	(1 << 2)
+#define UVC_STREAM_EOF	(1 << 1)
+#define UVC_STREAM_FID	(1 << 0)
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+			u8 *data, int len)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	__u32 this_pts;
+	u8 this_fid;
+	int remaining_len = len;
+	int payload_len;
+
+	payload_len = gspca_dev->cam.bulk ? 2048 : 2040;
+	do {
+		len = min(remaining_len, payload_len);
+
+		/* Payloads are prefixed with a UVC-style header.  We
+		   consider a frame to start when the FID toggles, or the PTS
+		   changes.  A frame ends when EOF is set, and we've received
+		   the correct number of bytes. */
+
+		/* Verify UVC header.  Header length is always 12 */
+		if (data[0] != 12 || len < 12) {
+			PDEBUG(D_PACK, "bad header");
+			goto discard;
+		}
+
+		/* Check errors */
+		if (data[1] & UVC_STREAM_ERR) {
+			PDEBUG(D_PACK, "payload error");
+			goto discard;
+		}
+
+		/* Extract PTS and FID */
+		if (!(data[1] & UVC_STREAM_PTS)) {
+			PDEBUG(D_PACK, "PTS not present");
+			goto discard;
+		}
+		this_pts = (data[5] << 24) | (data[4] << 16)
+						| (data[3] << 8) | data[2];
+		this_fid = data[1] & UVC_STREAM_FID;
+
+		/* If PTS or FID has changed, start a new frame. */
+		if (this_pts != sd->last_pts || this_fid != sd->last_fid) {
+			if (gspca_dev->last_packet_type == INTER_PACKET)
+				gspca_frame_add(gspca_dev, LAST_PACKET,
+						NULL, 0);
+			sd->last_pts = this_pts;
+			sd->last_fid = this_fid;
+			gspca_frame_add(gspca_dev, FIRST_PACKET,
+					data + 12, len - 12);
+		/* If this packet is marked as EOF, end the frame */
+		} else if (data[1] & UVC_STREAM_EOF) {
+			sd->last_pts = 0;
+			gspca_frame_add(gspca_dev, LAST_PACKET,
+					data + 12, len - 12);
+		} else {
+
+			/* Add the data from this payload */
+			gspca_frame_add(gspca_dev, INTER_PACKET,
+					data + 12, len - 12);
+		}
+
+		/* Done this payload */
+		goto scan_next;
+
+discard:
+		/* Discard data until a new frame starts. */
+		gspca_dev->last_packet_type = DISCARD_PACKET;
+
+scan_next:
+		remaining_len -= len;
+		data += len;
+	} while (remaining_len > 0);
+}
+
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct gspca_dev *gspca_dev =
+		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+
+	gspca_dev->usb_err = 0;
+
+	if (!gspca_dev->streaming)
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_BRIGHTNESS:
+		setbrightness(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_CONTRAST:
+		setcontrast(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_SATURATION:
+		setsatur(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_POWER_LINE_FREQUENCY:
+		setlightfreq(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_SHARPNESS:
+		setsharpness(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_AUTOGAIN:
+		if (ctrl->is_new)
+			setautogain(gspca_dev, ctrl->val);
+		if (!ctrl->val && gspca_dev->exposure->is_new)
+			setexposure(gspca_dev, gspca_dev->exposure->val);
+		break;
+	}
+	return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+	.s_ctrl = sd_s_ctrl,
+};
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *)gspca_dev;
+	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+	if (sd->sensor == SENSOR_OV971x)
+		return 0;
+	if (sd->sensor == SENSOR_OV361x)
+		return 0;
+	gspca_dev->vdev.ctrl_handler = hdl;
+	v4l2_ctrl_handler_init(hdl, 7);
+	if (sd->sensor == SENSOR_OV562x) {
+		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_BRIGHTNESS, -90, 90, 1, 0);
+	} else {
+		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_BRIGHTNESS, 0, 15, 1, 7);
+		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_CONTRAST, 0, 15, 1, 3);
+		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_SATURATION, 0, 4, 1, 2);
+		/* -1 = auto */
+		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_SHARPNESS, -1, 4, 1, -1);
+		gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+		gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_EXPOSURE, 0, 3, 1, 0);
+		v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
+			V4L2_CID_POWER_LINE_FREQUENCY,
+			V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0, 0);
+		v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false);
+	}
+
+	if (hdl->error) {
+		pr_err("Could not initialize controls\n");
+		return hdl->error;
+	}
+	return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+	.name     = MODULE_NAME,
+	.config   = sd_config,
+	.init     = sd_init,
+	.init_controls = sd_init_controls,
+	.start    = sd_start,
+	.stopN    = sd_stopN,
+	.pkt_scan = sd_pkt_scan,
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+	{USB_DEVICE(0x05a9, 0x8065)},
+	{USB_DEVICE(0x06f8, 0x3003)},
+	{USB_DEVICE(0x05a9, 0x1550)},
+	{}
+};
+
+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,
+#ifdef CONFIG_PM
+	.suspend    = gspca_suspend,
+	.resume     = gspca_resume,
+	.reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/usb/gspca/pac207.c b/drivers/media/usb/gspca/pac207.c
new file mode 100644
index 0000000..07529e5
--- /dev/null
+++ b/drivers/media/usb/gspca/pac207.c
@@ -0,0 +1,489 @@
+/*
+ * Pixart PAC207BCA library
+ *
+ * Copyright (C) 2008 Hans de Goede <hdegoede@redhat.com>
+ * 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "pac207"
+
+#include <linux/input.h>
+#include "gspca.h"
+/* Include pac common sof detection functions */
+#include "pac_common.h"
+
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+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	46
+#define PAC207_BRIGHTNESS_REG		0x08
+
+#define PAC207_EXPOSURE_MIN		3
+#define PAC207_EXPOSURE_MAX		90 /* 1 sec expo time / 1 fps */
+#define PAC207_EXPOSURE_DEFAULT		5 /* power on default: 3 */
+#define PAC207_EXPOSURE_REG		0x02
+
+#define PAC207_GAIN_MIN			0
+#define PAC207_GAIN_MAX			31
+#define PAC207_GAIN_DEFAULT		7 /* power on default: 9 */
+#define PAC207_GAIN_REG			0x0e
+
+#define PAC207_AUTOGAIN_DEADZONE	30
+
+/* global parameters */
+static int led_invert;
+module_param(led_invert, int, 0644);
+MODULE_PARM_DESC(led_invert, "Invert led");
+
+/* specific webcam descriptor */
+struct sd {
+	struct gspca_dev gspca_dev;		/* !! must be the first item */
+
+	struct v4l2_ctrl *brightness;
+
+	u8 mode;
+	u8 sof_read;
+	u8 header_read;
+	u8 autogain_ignore_frames;
+
+	atomic_t avg_lum;
+};
+
+static const 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, 0x84},
+	{0x49, 0x64, 0x64, 0x64, 0x04, 0x10, 0xf0, 0x30},
+	{0x00, 0x00, 0x00, 0x70, 0xa0, 0xf8, 0x00, 0x00},
+	{0x32, 0x00, 0x96, 0x00, 0xa2, 0x02, 0xaf, 0x00},
+};
+
+static void pac207_write_regs(struct gspca_dev *gspca_dev, u16 index,
+	const u8 *buffer, u16 length)
+{
+	struct usb_device *udev = gspca_dev->dev;
+	int err;
+
+	if (gspca_dev->usb_err < 0)
+		return;
+
+	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) {
+		pr_err("Failed to write registers to index 0x%04X, error %d\n",
+		       index, err);
+		gspca_dev->usb_err = err;
+	}
+}
+
+static void pac207_write_reg(struct gspca_dev *gspca_dev, u16 index, u16 value)
+{
+	struct usb_device *udev = gspca_dev->dev;
+	int err;
+
+	if (gspca_dev->usb_err < 0)
+		return;
+
+	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) {
+		pr_err("Failed to write a register (index 0x%04X, value 0x%02X, error %d)\n",
+		       index, value, err);
+		gspca_dev->usb_err = err;
+	}
+}
+
+static int pac207_read_reg(struct gspca_dev *gspca_dev, u16 index)
+{
+	struct usb_device *udev = gspca_dev->dev;
+	int res;
+
+	if (gspca_dev->usb_err < 0)
+		return 0;
+
+	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) {
+		pr_err("Failed to read a register (index 0x%04X, error %d)\n",
+		       index, res);
+		gspca_dev->usb_err = res;
+		return 0;
+	}
+
+	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 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;
+	}
+
+	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->cam_mode = sif_mode;
+	cam->nmodes = ARRAY_SIZE(sif_mode);
+
+	return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+	u8 mode;
+
+	/* mode: Image Format (Bit 0), LED (1), Compr. test mode (2) */
+	if (led_invert)
+		mode = 0x02;
+	else
+		mode = 0x00;
+	pac207_write_reg(gspca_dev, 0x41, mode);
+	pac207_write_reg(gspca_dev, 0x0f, 0x00); /* Power Control */
+
+	return gspca_dev->usb_err;
+}
+
+static void setcontrol(struct gspca_dev *gspca_dev, u16 reg, u16 val)
+{
+	pac207_write_reg(gspca_dev, reg, val);
+	pac207_write_reg(gspca_dev, 0x13, 0x01);	/* Bit 0, auto clear */
+	pac207_write_reg(gspca_dev, 0x1c, 0x01);	/* not documented */
+}
+
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct gspca_dev *gspca_dev =
+		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+	struct sd *sd = (struct sd *)gspca_dev;
+
+	gspca_dev->usb_err = 0;
+
+	if (ctrl->id == V4L2_CID_AUTOGAIN && ctrl->is_new && ctrl->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. */
+		gspca_dev->exposure->val    = PAC207_EXPOSURE_DEFAULT;
+		gspca_dev->gain->val        = PAC207_GAIN_DEFAULT;
+		sd->autogain_ignore_frames  = PAC_AUTOGAIN_IGNORE_FRAMES;
+	}
+
+	if (!gspca_dev->streaming)
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_BRIGHTNESS:
+		setcontrol(gspca_dev, PAC207_BRIGHTNESS_REG, ctrl->val);
+		break;
+	case V4L2_CID_AUTOGAIN:
+		if (gspca_dev->exposure->is_new || (ctrl->is_new && ctrl->val))
+			setcontrol(gspca_dev, PAC207_EXPOSURE_REG,
+				   gspca_dev->exposure->val);
+		if (gspca_dev->gain->is_new || (ctrl->is_new && ctrl->val))
+			setcontrol(gspca_dev, PAC207_GAIN_REG,
+				   gspca_dev->gain->val);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+	.s_ctrl = sd_s_ctrl,
+};
+
+/* this function is called at probe time */
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+	gspca_dev->vdev.ctrl_handler = hdl;
+	v4l2_ctrl_handler_init(hdl, 4);
+
+	sd->brightness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+				V4L2_CID_BRIGHTNESS,
+				PAC207_BRIGHTNESS_MIN, PAC207_BRIGHTNESS_MAX,
+				1, PAC207_BRIGHTNESS_DEFAULT);
+	gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+				V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+	gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+				V4L2_CID_EXPOSURE,
+				PAC207_EXPOSURE_MIN, PAC207_EXPOSURE_MAX,
+				1, PAC207_EXPOSURE_DEFAULT);
+	gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+				V4L2_CID_GAIN,
+				PAC207_GAIN_MIN, PAC207_GAIN_MAX,
+				1, PAC207_GAIN_DEFAULT);
+	if (hdl->error) {
+		pr_err("Could not initialize controls\n");
+		return hdl->error;
+	}
+	v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false);
+	return 0;
+}
+
+/* -- start the camera -- */
+static int 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, 0x0042, pac207_sensor_init[3], 8);
+
+	/* Compression Balance */
+	if (gspca_dev->pixfmt.width == 176)
+		pac207_write_reg(gspca_dev, 0x4a, 0xff);
+	else
+		pac207_write_reg(gspca_dev, 0x4a, 0x30);
+	pac207_write_reg(gspca_dev, 0x4b, 0x00); /* Sram test value */
+	pac207_write_reg(gspca_dev, 0x08, v4l2_ctrl_g_ctrl(sd->brightness));
+
+	/* PGA global gain (Bit 4-0) */
+	pac207_write_reg(gspca_dev, 0x0e,
+		v4l2_ctrl_g_ctrl(gspca_dev->gain));
+	pac207_write_reg(gspca_dev, 0x02,
+		v4l2_ctrl_g_ctrl(gspca_dev->exposure)); /* PXCK = 12MHz /n */
+
+	/* mode: Image Format (Bit 0), LED (1), Compr. test mode (2) */
+	if (led_invert)
+		mode = 0x00;
+	else
+		mode = 0x02;
+	if (gspca_dev->pixfmt.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);
+	return gspca_dev->usb_err;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+	u8 mode;
+
+	/* mode: Image Format (Bit 0), LED (1), Compr. test mode (2) */
+	if (led_invert)
+		mode = 0x02;
+	else
+		mode = 0x00;
+	pac207_write_reg(gspca_dev, 0x40, 0x00); /* Stop ISO pipe */
+	pac207_write_reg(gspca_dev, 0x41, mode); /* Turn off LED */
+	pac207_write_reg(gspca_dev, 0x0f, 0x00); /* Power Control */
+}
+
+
+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_coarse_grained_expo_autogain(gspca_dev, avg_lum,
+			90, PAC207_AUTOGAIN_DEADZONE))
+		sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES;
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+			u8 *data,
+			int len)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	unsigned char *sof;
+
+	sof = pac_find_sof(gspca_dev, &sd->sof_read, data, len);
+	if (sof) {
+		int n;
+
+		/* finish decoding current frame */
+		n = sof - data;
+		if (n > sizeof pac_sof_marker)
+			n -= sizeof pac_sof_marker;
+		else
+			n = 0;
+		gspca_frame_add(gspca_dev, LAST_PACKET,
+				data, n);
+		sd->header_read = 0;
+		gspca_frame_add(gspca_dev, FIRST_PACKET, 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, data, len);
+}
+
+#if IS_ENABLED(CONFIG_INPUT)
+static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
+			u8 *data,		/* interrupt packet data */
+			int len)		/* interrupt packet length */
+{
+	int ret = -EINVAL;
+
+	if (len == 2 && data[0] == 0x5a && data[1] == 0x5a) {
+		input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1);
+		input_sync(gspca_dev->input_dev);
+		input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
+		input_sync(gspca_dev->input_dev);
+		ret = 0;
+	}
+
+	return ret;
+}
+#endif
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+	.name = MODULE_NAME,
+	.config = sd_config,
+	.init = sd_init,
+	.init_controls = sd_init_controls,
+	.start = sd_start,
+	.stopN = sd_stopN,
+	.dq_callback = pac207_do_auto_gain,
+	.pkt_scan = sd_pkt_scan,
+#if IS_ENABLED(CONFIG_INPUT)
+	.int_pkt_scan = sd_int_pkt_scan,
+#endif
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+	{USB_DEVICE(0x041e, 0x4028)},
+	{USB_DEVICE(0x093a, 0x2460)},
+	{USB_DEVICE(0x093a, 0x2461)},
+	{USB_DEVICE(0x093a, 0x2463)},
+	{USB_DEVICE(0x093a, 0x2464)},
+	{USB_DEVICE(0x093a, 0x2468)},
+	{USB_DEVICE(0x093a, 0x2470)},
+	{USB_DEVICE(0x093a, 0x2471)},
+	{USB_DEVICE(0x093a, 0x2472)},
+	{USB_DEVICE(0x093a, 0x2474)},
+	{USB_DEVICE(0x093a, 0x2476)},
+	{USB_DEVICE(0x145f, 0x013a)},
+	{USB_DEVICE(0x2001, 0xf115)},
+	{}
+};
+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,
+#ifdef CONFIG_PM
+	.suspend = gspca_suspend,
+	.resume = gspca_resume,
+	.reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/usb/gspca/pac7302.c b/drivers/media/usb/gspca/pac7302.c
new file mode 100644
index 0000000..8b08bd0
--- /dev/null
+++ b/drivers/media/usb/gspca/pac7302.c
@@ -0,0 +1,966 @@
+/*
+ * Pixart PAC7302 driver
+ *
+ * Copyright (C) 2008-2012 Jean-Francois Moine <http://moinejf.free.fr>
+ * Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li
+ *
+ * Separated from Pixart PAC7311 library by Márton Németh
+ * Camera button input handling by Márton Németh <nm127@freemail.hu>
+ * Copyright (C) 2009-2010 Márton Németh <nm127@freemail.hu>
+ *
+ * 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
+ */
+
+/*
+ * Some documentation about various registers as determined by trial and error.
+ *
+ * Register page 0:
+ *
+ * Address	Description
+ * 0x01		Red balance control
+ * 0x02		Green balance control
+ * 0x03		Blue balance control
+ *		     The Windows driver uses a quadratic approach to map
+ *		     the settable values (0-200) on register values:
+ *		     min=0x20, default=0x40, max=0x80
+ * 0x0f-0x20	Color and saturation control
+ * 0xa2-0xab	Brightness, contrast and gamma control
+ * 0xb6		Sharpness control (bits 0-4)
+ *
+ * Register page 1:
+ *
+ * Address	Description
+ * 0x78		Global control, bit 6 controls the LED (inverted)
+ * 0x80		Compression balance, 2 interesting settings:
+ *		0x0f Default
+ *		0x50 Values >= this switch the camera to a lower compression,
+ *		     using the same table for both luminance and chrominance.
+ *		     This gives a sharper picture. Only usable when running
+ *		     at < 15 fps! Note currently the driver does not use this
+ *		     as the quality gain is small and the generated JPG-s are
+ *		     only understood by v4l-utils >= 0.8.9
+ *
+ * Register page 3:
+ *
+ * Address	Description
+ * 0x02		Clock divider 3-63, fps = 90 / val. Must be a multiple of 3 on
+ *		the 7302, so one of 3, 6, 9, ..., except when between 6 and 12?
+ * 0x03		Variable framerate ctrl reg2==3: 0 -> ~30 fps, 255 -> ~22fps
+ * 0x04		Another var framerate ctrl reg2==3, reg3==0: 0 -> ~30 fps,
+ *		63 -> ~27 fps, the 2 msb's must always be 1 !!
+ * 0x05		Another var framerate ctrl reg2==3, reg3==0, reg4==0xc0:
+ *		1 -> ~30 fps, 2 -> ~20 fps
+ * 0x0e		Exposure bits 0-7, 0-448, 0 = use full frame time
+ * 0x0f		Exposure bit 8, 0-448, 448 = no exposure at all
+ * 0x10		Gain 0-31
+ * 0x12		Another gain 0-31, unlike 0x10 this one seems to start with an
+ *		amplification value of 1 rather then 0 at its lowest setting
+ * 0x21		Bitfield: 0-1 unused, 2-3 vflip/hflip, 4-5 unknown, 6-7 unused
+ * 0x80		Another framerate control, best left at 1, moving it from 1 to
+ *		2 causes the framerate to become 3/4th of what it was, and
+ *		also seems to cause pixel averaging, resulting in an effective
+ *		resolution of 320x240 and thus a much blockier image
+ *
+ * The registers are accessed in the following functions:
+ *
+ * Page | Register   | Function
+ * -----+------------+---------------------------------------------------
+ *  0   | 0x01       | setredbalance()
+ *  0   | 0x03       | setbluebalance()
+ *  0   | 0x0f..0x20 | setcolors()
+ *  0   | 0xa2..0xab | setbrightcont()
+ *  0   | 0xb6       | setsharpness()
+ *  0   | 0xc6       | setwhitebalance()
+ *  0   | 0xdc       | setbrightcont(), setcolors()
+ *  3   | 0x02       | setexposure()
+ *  3   | 0x10, 0x12 | setgain()
+ *  3   | 0x11       | setcolors(), setgain(), setexposure(), sethvflip()
+ *  3   | 0x21       | sethvflip()
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/input.h>
+#include "gspca.h"
+/* Include pac common sof detection functions */
+#include "pac_common.h"
+
+#define PAC7302_RGB_BALANCE_MIN		  0
+#define PAC7302_RGB_BALANCE_MAX		200
+#define PAC7302_RGB_BALANCE_DEFAULT	100
+#define PAC7302_GAIN_DEFAULT		 15
+#define PAC7302_GAIN_KNEE		 42
+#define PAC7302_EXPOSURE_DEFAULT	 66 /* 33 ms / 30 fps */
+#define PAC7302_EXPOSURE_KNEE		133 /* 66 ms / 15 fps */
+
+MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>, "
+		"Thomas Kaiser thomas@kaiser-linux.li");
+MODULE_DESCRIPTION("Pixart PAC7302");
+MODULE_LICENSE("GPL");
+
+struct sd {
+	struct gspca_dev gspca_dev;		/* !! must be the first item */
+
+	struct { /* brightness / contrast cluster */
+		struct v4l2_ctrl *brightness;
+		struct v4l2_ctrl *contrast;
+	};
+	struct v4l2_ctrl *saturation;
+	struct v4l2_ctrl *white_balance;
+	struct v4l2_ctrl *red_balance;
+	struct v4l2_ctrl *blue_balance;
+	struct { /* flip cluster */
+		struct v4l2_ctrl *hflip;
+		struct v4l2_ctrl *vflip;
+	};
+	struct v4l2_ctrl *sharpness;
+	u8 flags;
+#define FL_HFLIP 0x01		/* mirrored by default */
+#define FL_VFLIP 0x02		/* vertical flipped by default */
+
+	u8 sof_read;
+	s8 autogain_ignore_frames;
+
+	atomic_t avg_lum;
+};
+
+static const struct v4l2_pix_format vga_mode[] = {
+	{640, 480, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE,
+		.bytesperline = 640,
+		.sizeimage = 640 * 480 * 3 / 8 + 590,
+		.colorspace = V4L2_COLORSPACE_JPEG,
+	},
+};
+
+#define LOAD_PAGE3		255
+#define END_OF_SEQUENCE		0
+
+static const u8 init_7302[] = {
+/*	index,value */
+	0xff, 0x01,		/* page 1 */
+	0x78, 0x00,		/* deactivate */
+	0xff, 0x01,
+	0x78, 0x40,		/* led off */
+};
+static const u8 start_7302[] = {
+/*	index, len, [value]* */
+	0xff, 1,	0x00,		/* page 0 */
+	0x00, 12,	0x01, 0x40, 0x40, 0x40, 0x01, 0xe0, 0x02, 0x80,
+			0x00, 0x00, 0x00, 0x00,
+	0x0d, 24,	0x03, 0x01, 0x00, 0xb5, 0x07, 0xcb, 0x00, 0x00,
+			0x07, 0xc8, 0x00, 0xea, 0x07, 0xcf, 0x07, 0xf7,
+			0x07, 0x7e, 0x01, 0x0b, 0x00, 0x00, 0x00, 0x11,
+	0x26, 2,	0xaa, 0xaa,
+	0x2e, 1,	0x31,
+	0x38, 1,	0x01,
+	0x3a, 3,	0x14, 0xff, 0x5a,
+	0x43, 11,	0x00, 0x0a, 0x18, 0x11, 0x01, 0x2c, 0x88, 0x11,
+			0x00, 0x54, 0x11,
+	0x55, 1,	0x00,
+	0x62, 4,	0x10, 0x1e, 0x1e, 0x18,
+	0x6b, 1,	0x00,
+	0x6e, 3,	0x08, 0x06, 0x00,
+	0x72, 3,	0x00, 0xff, 0x00,
+	0x7d, 23,	0x01, 0x01, 0x58, 0x46, 0x50, 0x3c, 0x50, 0x3c,
+			0x54, 0x46, 0x54, 0x56, 0x52, 0x50, 0x52, 0x50,
+			0x56, 0x64, 0xa4, 0x00, 0xda, 0x00, 0x00,
+	0xa2, 10,	0x22, 0x2c, 0x3c, 0x54, 0x69, 0x7c, 0x9c, 0xb9,
+			0xd2, 0xeb,
+	0xaf, 1,	0x02,
+	0xb5, 2,	0x08, 0x08,
+	0xb8, 2,	0x08, 0x88,
+	0xc4, 4,	0xae, 0x01, 0x04, 0x01,
+	0xcc, 1,	0x00,
+	0xd1, 11,	0x01, 0x30, 0x49, 0x5e, 0x6f, 0x7f, 0x8e, 0xa9,
+			0xc1, 0xd7, 0xec,
+	0xdc, 1,	0x01,
+	0xff, 1,	0x01,		/* page 1 */
+	0x12, 3,	0x02, 0x00, 0x01,
+	0x3e, 2,	0x00, 0x00,
+	0x76, 5,	0x01, 0x20, 0x40, 0x00, 0xf2,
+	0x7c, 1,	0x00,
+	0x7f, 10,	0x4b, 0x0f, 0x01, 0x2c, 0x02, 0x58, 0x03, 0x20,
+			0x02, 0x00,
+	0x96, 5,	0x01, 0x10, 0x04, 0x01, 0x04,
+	0xc8, 14,	0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00,
+			0x07, 0x00, 0x01, 0x07, 0x04, 0x01,
+	0xd8, 1,	0x01,
+	0xdb, 2,	0x00, 0x01,
+	0xde, 7,	0x00, 0x01, 0x04, 0x04, 0x00, 0x00, 0x00,
+	0xe6, 4,	0x00, 0x00, 0x00, 0x01,
+	0xeb, 1,	0x00,
+	0xff, 1,	0x02,		/* page 2 */
+	0x22, 1,	0x00,
+	0xff, 1,	0x03,		/* page 3 */
+	0, LOAD_PAGE3,			/* load the page 3 */
+	0x11, 1,	0x01,
+	0xff, 1,	0x02,		/* page 2 */
+	0x13, 1,	0x00,
+	0x22, 4,	0x1f, 0xa4, 0xf0, 0x96,
+	0x27, 2,	0x14, 0x0c,
+	0x2a, 5,	0xc8, 0x00, 0x18, 0x12, 0x22,
+	0x64, 8,	0x00, 0x00, 0xf0, 0x01, 0x14, 0x44, 0x44, 0x44,
+	0x6e, 1,	0x08,
+	0xff, 1,	0x01,		/* page 1 */
+	0x78, 1,	0x00,
+	0, END_OF_SEQUENCE		/* end of sequence */
+};
+
+#define SKIP		0xaa
+/* page 3 - the value SKIP says skip the index - see reg_w_page() */
+static const u8 page3_7302[] = {
+	0x90, 0x40, 0x03, 0x00, 0xc0, 0x01, 0x14, 0x16,
+	0x14, 0x12, 0x00, 0x00, 0x00, 0x02, 0x33, 0x00,
+	0x0f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x47, 0x01, 0xb3, 0x01, 0x00,
+	0x00, 0x08, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x21,
+	0x00, 0x00, 0x00, 0x54, 0xf4, 0x02, 0x52, 0x54,
+	0xa4, 0xb8, 0xe0, 0x2a, 0xf6, 0x00, 0x00, 0x00,
+	0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0xfc, 0x00, 0xf2, 0x1f, 0x04, 0x00, 0x00,
+	SKIP, 0x00, 0x00, 0xc0, 0xc0, 0x10, 0x00, 0x00,
+	0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x40, 0xff, 0x03, 0x19, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0xc8, 0xc8,
+	0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50,
+	0x08, 0x10, 0x24, 0x40, 0x00, 0x00, 0x00, 0x00,
+	0x01, 0x00, 0x02, 0x47, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x02, 0xfa, 0x00, 0x64, 0x5a, 0x28, 0x00,
+	0x00
+};
+
+static void reg_w_buf(struct gspca_dev *gspca_dev,
+		u8 index,
+		  const u8 *buffer, int len)
+{
+	int ret;
+
+	if (gspca_dev->usb_err < 0)
+		return;
+	memcpy(gspca_dev->usb_buf, buffer, len);
+	ret = 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);
+	if (ret < 0) {
+		pr_err("reg_w_buf failed i: %02x error %d\n",
+		       index, ret);
+		gspca_dev->usb_err = ret;
+	}
+}
+
+
+static void reg_w(struct gspca_dev *gspca_dev,
+		u8 index,
+		u8 value)
+{
+	int ret;
+
+	if (gspca_dev->usb_err < 0)
+		return;
+	gspca_dev->usb_buf[0] = value;
+	ret = usb_control_msg(gspca_dev->dev,
+			usb_sndctrlpipe(gspca_dev->dev, 0),
+			0,			/* request */
+			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			0, index, gspca_dev->usb_buf, 1,
+			500);
+	if (ret < 0) {
+		pr_err("reg_w() failed i: %02x v: %02x error %d\n",
+		       index, value, ret);
+		gspca_dev->usb_err = ret;
+	}
+}
+
+static void reg_w_seq(struct gspca_dev *gspca_dev,
+		const u8 *seq, int len)
+{
+	while (--len >= 0) {
+		reg_w(gspca_dev, seq[0], seq[1]);
+		seq += 2;
+	}
+}
+
+/* load the beginning of a page */
+static void reg_w_page(struct gspca_dev *gspca_dev,
+			const u8 *page, int len)
+{
+	int index;
+	int ret = 0;
+
+	if (gspca_dev->usb_err < 0)
+		return;
+	for (index = 0; index < len; index++) {
+		if (page[index] == SKIP)		/* skip this index */
+			continue;
+		gspca_dev->usb_buf[0] = page[index];
+		ret = usb_control_msg(gspca_dev->dev,
+				usb_sndctrlpipe(gspca_dev->dev, 0),
+				0,			/* request */
+			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+				0, index, gspca_dev->usb_buf, 1,
+				500);
+		if (ret < 0) {
+			pr_err("reg_w_page() failed i: %02x v: %02x error %d\n",
+			       index, page[index], ret);
+			gspca_dev->usb_err = ret;
+			break;
+		}
+	}
+}
+
+/* output a variable sequence */
+static void reg_w_var(struct gspca_dev *gspca_dev,
+			const u8 *seq,
+			const u8 *page3, unsigned int page3_len)
+{
+	int index, len;
+
+	for (;;) {
+		index = *seq++;
+		len = *seq++;
+		switch (len) {
+		case END_OF_SEQUENCE:
+			return;
+		case LOAD_PAGE3:
+			reg_w_page(gspca_dev, page3, page3_len);
+			break;
+		default:
+			if (len > USB_BUF_SZ) {
+				PERR("Incorrect variable sequence");
+				return;
+			}
+			while (len > 0) {
+				if (len < 8) {
+					reg_w_buf(gspca_dev,
+						index, seq, len);
+					seq += len;
+					break;
+				}
+				reg_w_buf(gspca_dev, index, seq, 8);
+				seq += 8;
+				index += 8;
+				len -= 8;
+			}
+		}
+	}
+	/* not reached */
+}
+
+/* this function is called at probe time for pac7302 */
+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->cam_mode = vga_mode;	/* only 640x480 */
+	cam->nmodes = ARRAY_SIZE(vga_mode);
+
+	sd->flags = id->driver_info;
+	return 0;
+}
+
+static void setbrightcont(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int i, v;
+	static const u8 max[10] =
+		{0x29, 0x33, 0x42, 0x5a, 0x6e, 0x80, 0x9f, 0xbb,
+		 0xd4, 0xec};
+	static const u8 delta[10] =
+		{0x35, 0x33, 0x33, 0x2f, 0x2a, 0x25, 0x1e, 0x17,
+		 0x11, 0x0b};
+
+	reg_w(gspca_dev, 0xff, 0x00);		/* page 0 */
+	for (i = 0; i < 10; i++) {
+		v = max[i];
+		v += (sd->brightness->val - (s32)sd->brightness->maximum)
+			* 150 / (s32)sd->brightness->maximum; /* 200 ? */
+		v -= delta[i] * sd->contrast->val / (s32)sd->contrast->maximum;
+		if (v < 0)
+			v = 0;
+		else if (v > 0xff)
+			v = 0xff;
+		reg_w(gspca_dev, 0xa2 + i, v);
+	}
+	reg_w(gspca_dev, 0xdc, 0x01);
+}
+
+static void setcolors(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int i, v;
+	static const int a[9] =
+		{217, -212, 0, -101, 170, -67, -38, -315, 355};
+	static const int b[9] =
+		{19, 106, 0, 19, 106, 1, 19, 106, 1};
+
+	reg_w(gspca_dev, 0xff, 0x03);			/* page 3 */
+	reg_w(gspca_dev, 0x11, 0x01);
+	reg_w(gspca_dev, 0xff, 0x00);			/* page 0 */
+	for (i = 0; i < 9; i++) {
+		v = a[i] * sd->saturation->val / (s32)sd->saturation->maximum;
+		v += b[i];
+		reg_w(gspca_dev, 0x0f + 2 * i, (v >> 8) & 0x07);
+		reg_w(gspca_dev, 0x0f + 2 * i + 1, v);
+	}
+	reg_w(gspca_dev, 0xdc, 0x01);
+}
+
+static void setwhitebalance(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	reg_w(gspca_dev, 0xff, 0x00);		/* page 0 */
+	reg_w(gspca_dev, 0xc6, sd->white_balance->val);
+
+	reg_w(gspca_dev, 0xdc, 0x01);
+}
+
+static u8 rgbbalance_ctrl_to_reg_value(s32 rgb_ctrl_val)
+{
+	const unsigned int k = 1000;	/* precision factor */
+	unsigned int norm;
+
+	/* Normed value [0...k] */
+	norm = k * (rgb_ctrl_val - PAC7302_RGB_BALANCE_MIN)
+		    / (PAC7302_RGB_BALANCE_MAX - PAC7302_RGB_BALANCE_MIN);
+	/* Qudratic apporach improves control at small (register) values: */
+	return 64 * norm * norm / (k*k)  +  32 * norm / k  +  32;
+	/* Y = 64*X*X + 32*X + 32
+	 * => register values 0x20-0x80; Windows driver uses these limits */
+
+	/* NOTE: for full value range (0x00-0xff) use
+	 *         Y = 254*X*X + X
+	 *         => 254 * norm * norm / (k*k)  +  1 * norm / k	*/
+}
+
+static void setredbalance(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	reg_w(gspca_dev, 0xff, 0x00);			/* page 0 */
+	reg_w(gspca_dev, 0x01,
+	      rgbbalance_ctrl_to_reg_value(sd->red_balance->val));
+
+	reg_w(gspca_dev, 0xdc, 0x01);
+}
+
+static void setbluebalance(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	reg_w(gspca_dev, 0xff, 0x00);			/* page 0 */
+	reg_w(gspca_dev, 0x03,
+	      rgbbalance_ctrl_to_reg_value(sd->blue_balance->val));
+
+	reg_w(gspca_dev, 0xdc, 0x01);
+}
+
+static void setgain(struct gspca_dev *gspca_dev)
+{
+	u8 reg10, reg12;
+
+	if (gspca_dev->gain->val < 32) {
+		reg10 = gspca_dev->gain->val;
+		reg12 = 0;
+	} else {
+		reg10 = 31;
+		reg12 = gspca_dev->gain->val - 31;
+	}
+
+	reg_w(gspca_dev, 0xff, 0x03);			/* page 3 */
+	reg_w(gspca_dev, 0x10, reg10);
+	reg_w(gspca_dev, 0x12, reg12);
+
+	/* load registers to sensor (Bit 0, auto clear) */
+	reg_w(gspca_dev, 0x11, 0x01);
+}
+
+static void setexposure(struct gspca_dev *gspca_dev)
+{
+	u8 clockdiv;
+	u16 exposure;
+
+	/*
+	 * Register 2 of frame 3 contains the clock divider configuring the
+	 * no fps according to the formula: 90 / reg. sd->exposure is the
+	 * desired exposure time in 0.5 ms.
+	 */
+	clockdiv = (90 * gspca_dev->exposure->val + 1999) / 2000;
+
+	/*
+	 * Note clockdiv = 3 also works, but when running at 30 fps, depending
+	 * on the scene being recorded, the camera switches to another
+	 * quantization table for certain JPEG blocks, and we don't know how
+	 * to decompress these blocks. So we cap the framerate at 15 fps.
+	 */
+	if (clockdiv < 6)
+		clockdiv = 6;
+	else if (clockdiv > 63)
+		clockdiv = 63;
+
+	/*
+	 * Register 2 MUST be a multiple of 3, except when between 6 and 12?
+	 * Always round up, otherwise we cannot get the desired frametime
+	 * using the partial frame time exposure control.
+	 */
+	if (clockdiv < 6 || clockdiv > 12)
+		clockdiv = ((clockdiv + 2) / 3) * 3;
+
+	/*
+	 * frame exposure time in ms = 1000 * clockdiv / 90    ->
+	 * exposure = (sd->exposure / 2) * 448 / (1000 * clockdiv / 90)
+	 */
+	exposure = (gspca_dev->exposure->val * 45 * 448) / (1000 * clockdiv);
+	/* 0 = use full frametime, 448 = no exposure, reverse it */
+	exposure = 448 - exposure;
+
+	reg_w(gspca_dev, 0xff, 0x03);			/* page 3 */
+	reg_w(gspca_dev, 0x02, clockdiv);
+	reg_w(gspca_dev, 0x0e, exposure & 0xff);
+	reg_w(gspca_dev, 0x0f, exposure >> 8);
+
+	/* load registers to sensor (Bit 0, auto clear) */
+	reg_w(gspca_dev, 0x11, 0x01);
+}
+
+static void sethvflip(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u8 data, hflip, vflip;
+
+	hflip = sd->hflip->val;
+	if (sd->flags & FL_HFLIP)
+		hflip = !hflip;
+	vflip = sd->vflip->val;
+	if (sd->flags & FL_VFLIP)
+		vflip = !vflip;
+
+	reg_w(gspca_dev, 0xff, 0x03);			/* page 3 */
+	data = (hflip ? 0x08 : 0x00) | (vflip ? 0x04 : 0x00);
+	reg_w(gspca_dev, 0x21, data);
+
+	/* load registers to sensor (Bit 0, auto clear) */
+	reg_w(gspca_dev, 0x11, 0x01);
+}
+
+static void setsharpness(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	reg_w(gspca_dev, 0xff, 0x00);		/* page 0 */
+	reg_w(gspca_dev, 0xb6, sd->sharpness->val);
+
+	reg_w(gspca_dev, 0xdc, 0x01);
+}
+
+/* this function is called at probe and resume time for pac7302 */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+	reg_w_seq(gspca_dev, init_7302, sizeof(init_7302)/2);
+	return gspca_dev->usb_err;
+}
+
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct gspca_dev *gspca_dev =
+		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+	struct sd *sd = (struct sd *)gspca_dev;
+
+	gspca_dev->usb_err = 0;
+
+	if (ctrl->id == V4L2_CID_AUTOGAIN && ctrl->is_new && ctrl->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. */
+		gspca_dev->exposure->val    = PAC7302_EXPOSURE_DEFAULT;
+		gspca_dev->gain->val        = PAC7302_GAIN_DEFAULT;
+		sd->autogain_ignore_frames  = PAC_AUTOGAIN_IGNORE_FRAMES;
+	}
+
+	if (!gspca_dev->streaming)
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_BRIGHTNESS:
+		setbrightcont(gspca_dev);
+		break;
+	case V4L2_CID_SATURATION:
+		setcolors(gspca_dev);
+		break;
+	case V4L2_CID_WHITE_BALANCE_TEMPERATURE:
+		setwhitebalance(gspca_dev);
+		break;
+	case V4L2_CID_RED_BALANCE:
+		setredbalance(gspca_dev);
+		break;
+	case V4L2_CID_BLUE_BALANCE:
+		setbluebalance(gspca_dev);
+		break;
+	case V4L2_CID_AUTOGAIN:
+		if (gspca_dev->exposure->is_new || (ctrl->is_new && ctrl->val))
+			setexposure(gspca_dev);
+		if (gspca_dev->gain->is_new || (ctrl->is_new && ctrl->val))
+			setgain(gspca_dev);
+		break;
+	case V4L2_CID_HFLIP:
+		sethvflip(gspca_dev);
+		break;
+	case V4L2_CID_SHARPNESS:
+		setsharpness(gspca_dev);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+	.s_ctrl = sd_s_ctrl,
+};
+
+/* this function is called at probe time */
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+	gspca_dev->vdev.ctrl_handler = hdl;
+	v4l2_ctrl_handler_init(hdl, 12);
+
+	sd->brightness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+					V4L2_CID_BRIGHTNESS, 0, 32, 1, 16);
+	sd->contrast = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+					V4L2_CID_CONTRAST, 0, 255, 1, 127);
+
+	sd->saturation = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+					V4L2_CID_SATURATION, 0, 255, 1, 127);
+	sd->white_balance = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+					V4L2_CID_WHITE_BALANCE_TEMPERATURE,
+					0, 255, 1, 55);
+	sd->red_balance = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+					V4L2_CID_RED_BALANCE,
+					PAC7302_RGB_BALANCE_MIN,
+					PAC7302_RGB_BALANCE_MAX,
+					1, PAC7302_RGB_BALANCE_DEFAULT);
+	sd->blue_balance = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+					V4L2_CID_BLUE_BALANCE,
+					PAC7302_RGB_BALANCE_MIN,
+					PAC7302_RGB_BALANCE_MAX,
+					1, PAC7302_RGB_BALANCE_DEFAULT);
+
+	gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+					V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+	gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+					V4L2_CID_EXPOSURE, 0, 1023, 1,
+					PAC7302_EXPOSURE_DEFAULT);
+	gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+					V4L2_CID_GAIN, 0, 62, 1,
+					PAC7302_GAIN_DEFAULT);
+
+	sd->hflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+		V4L2_CID_HFLIP, 0, 1, 1, 0);
+	sd->vflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+		V4L2_CID_VFLIP, 0, 1, 1, 0);
+
+	sd->sharpness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+					V4L2_CID_SHARPNESS, 0, 15, 1, 8);
+
+	if (hdl->error) {
+		pr_err("Could not initialize controls\n");
+		return hdl->error;
+	}
+
+	v4l2_ctrl_cluster(2, &sd->brightness);
+	v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false);
+	v4l2_ctrl_cluster(2, &sd->hflip);
+	return 0;
+}
+
+/* -- start the camera -- */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	reg_w_var(gspca_dev, start_7302,
+		page3_7302, sizeof(page3_7302));
+
+	sd->sof_read = 0;
+	sd->autogain_ignore_frames = 0;
+	atomic_set(&sd->avg_lum, 270 + sd->brightness->val);
+
+	/* start stream */
+	reg_w(gspca_dev, 0xff, 0x01);
+	reg_w(gspca_dev, 0x78, 0x01);
+
+	return gspca_dev->usb_err;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+
+	/* stop stream */
+	reg_w(gspca_dev, 0xff, 0x01);
+	reg_w(gspca_dev, 0x78, 0x00);
+}
+
+/* called on streamoff with alt 0 and on disconnect for pac7302 */
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+	if (!gspca_dev->present)
+		return;
+	reg_w(gspca_dev, 0xff, 0x01);
+	reg_w(gspca_dev, 0x78, 0x40);
+}
+
+static void do_autogain(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int avg_lum = atomic_read(&sd->avg_lum);
+	int desired_lum;
+	const int deadzone = 30;
+
+	if (sd->autogain_ignore_frames < 0)
+		return;
+
+	if (sd->autogain_ignore_frames > 0) {
+		sd->autogain_ignore_frames--;
+	} else {
+		desired_lum = 270 + sd->brightness->val;
+
+		if (gspca_expo_autogain(gspca_dev, avg_lum, desired_lum,
+					deadzone, PAC7302_GAIN_KNEE,
+					PAC7302_EXPOSURE_KNEE))
+			sd->autogain_ignore_frames =
+						PAC_AUTOGAIN_IGNORE_FRAMES;
+	}
+}
+
+/* JPEG header */
+static const u8 jpeg_header[] = {
+	0xff, 0xd8,	/* SOI: Start of Image */
+
+	0xff, 0xc0,	/* SOF0: Start of Frame (Baseline DCT) */
+	0x00, 0x11,	/* length = 17 bytes (including this length field) */
+	0x08,		/* Precision: 8 */
+	0x02, 0x80,	/* height = 640 (image rotated) */
+	0x01, 0xe0,	/* width = 480 */
+	0x03,		/* Number of image components: 3 */
+	0x01, 0x21, 0x00, /* ID=1, Subsampling 1x1, Quantization table: 0 */
+	0x02, 0x11, 0x01, /* ID=2, Subsampling 2x1, Quantization table: 1 */
+	0x03, 0x11, 0x01, /* ID=3, Subsampling 2x1, Quantization table: 1 */
+
+	0xff, 0xda,	/* SOS: Start Of Scan */
+	0x00, 0x0c,	/* length = 12 bytes (including this length field) */
+	0x03,		/* number of components: 3 */
+	0x01, 0x00,	/* selector 1, table 0x00 */
+	0x02, 0x11,	/* selector 2, table 0x11 */
+	0x03, 0x11,	/* selector 3, table 0x11 */
+	0x00, 0x3f,	/* Spectral selection: 0 .. 63 */
+	0x00		/* Successive approximation: 0 */
+};
+
+/* this function is run at interrupt level */
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+			u8 *data,			/* isoc packet */
+			int len)			/* iso packet length */
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u8 *image;
+	u8 *sof;
+
+	sof = pac_find_sof(gspca_dev, &sd->sof_read, data, len);
+	if (sof) {
+		int n, lum_offset, footer_length;
+
+		/*
+		 * 6 bytes after the FF D9 EOF marker a number of lumination
+		 * bytes are send corresponding to different parts of the
+		 * image, the 14th and 15th byte after the EOF seem to
+		 * correspond to the center of the image.
+		 */
+		lum_offset = 61 + sizeof pac_sof_marker;
+		footer_length = 74;
+
+		/* Finish decoding current frame */
+		n = (sof - data) - (footer_length + sizeof pac_sof_marker);
+		if (n < 0) {
+			gspca_dev->image_len += n;
+			n = 0;
+		} else {
+			gspca_frame_add(gspca_dev, INTER_PACKET, data, n);
+		}
+
+		image = gspca_dev->image;
+		if (image != NULL
+		 && image[gspca_dev->image_len - 2] == 0xff
+		 && image[gspca_dev->image_len - 1] == 0xd9)
+			gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+
+		n = sof - data;
+		len -= n;
+		data = sof;
+
+		/* Get average lumination */
+		if (gspca_dev->last_packet_type == LAST_PACKET &&
+				n >= lum_offset)
+			atomic_set(&sd->avg_lum, data[-lum_offset] +
+						data[-lum_offset + 1]);
+
+		/* Start the new frame with the jpeg header */
+		/* The PAC7302 has the image rotated 90 degrees */
+		gspca_frame_add(gspca_dev, FIRST_PACKET,
+				jpeg_header, sizeof jpeg_header);
+	}
+	gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int sd_dbg_s_register(struct gspca_dev *gspca_dev,
+			const struct v4l2_dbg_register *reg)
+{
+	u8 index;
+	u8 value;
+
+	/*
+	 * reg->reg: bit0..15: reserved for register index (wIndex is 16bit
+	 *		       long on the USB bus)
+	 */
+	if (reg->match.addr == 0 &&
+	    (reg->reg < 0x000000ff) &&
+	    (reg->val <= 0x000000ff)
+	) {
+		/* Currently writing to page 0 is only supported. */
+		/* reg_w() only supports 8bit index */
+		index = reg->reg;
+		value = reg->val;
+
+		/*
+		 * Note that there shall be no access to other page
+		 * by any other function between the page switch and
+		 * the actual register write.
+		 */
+		reg_w(gspca_dev, 0xff, 0x00);		/* page 0 */
+		reg_w(gspca_dev, index, value);
+
+		reg_w(gspca_dev, 0xdc, 0x01);
+	}
+	return gspca_dev->usb_err;
+}
+#endif
+
+#if IS_ENABLED(CONFIG_INPUT)
+static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
+			u8 *data,		/* interrupt packet data */
+			int len)		/* interrupt packet length */
+{
+	int ret = -EINVAL;
+	u8 data0, data1;
+
+	if (len == 2) {
+		data0 = data[0];
+		data1 = data[1];
+		if ((data0 == 0x00 && data1 == 0x11) ||
+		    (data0 == 0x22 && data1 == 0x33) ||
+		    (data0 == 0x44 && data1 == 0x55) ||
+		    (data0 == 0x66 && data1 == 0x77) ||
+		    (data0 == 0x88 && data1 == 0x99) ||
+		    (data0 == 0xaa && data1 == 0xbb) ||
+		    (data0 == 0xcc && data1 == 0xdd) ||
+		    (data0 == 0xee && data1 == 0xff)) {
+			input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1);
+			input_sync(gspca_dev->input_dev);
+			input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
+			input_sync(gspca_dev->input_dev);
+			ret = 0;
+		}
+	}
+
+	return ret;
+}
+#endif
+
+/* sub-driver description for pac7302 */
+static const struct sd_desc sd_desc = {
+	.name = KBUILD_MODNAME,
+	.config = sd_config,
+	.init = sd_init,
+	.init_controls = sd_init_controls,
+	.start = sd_start,
+	.stopN = sd_stopN,
+	.stop0 = sd_stop0,
+	.pkt_scan = sd_pkt_scan,
+	.dq_callback = do_autogain,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+	.set_register = sd_dbg_s_register,
+#endif
+#if IS_ENABLED(CONFIG_INPUT)
+	.int_pkt_scan = sd_int_pkt_scan,
+#endif
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+	{USB_DEVICE(0x06f8, 0x3009)},
+	{USB_DEVICE(0x06f8, 0x301b)},
+	{USB_DEVICE(0x093a, 0x2620)},
+	{USB_DEVICE(0x093a, 0x2621)},
+	{USB_DEVICE(0x093a, 0x2622), .driver_info = FL_VFLIP},
+	{USB_DEVICE(0x093a, 0x2623), .driver_info = FL_VFLIP},
+	{USB_DEVICE(0x093a, 0x2624), .driver_info = FL_VFLIP},
+	{USB_DEVICE(0x093a, 0x2625)},
+	{USB_DEVICE(0x093a, 0x2626)},
+	{USB_DEVICE(0x093a, 0x2627), .driver_info = FL_VFLIP},
+	{USB_DEVICE(0x093a, 0x2628)},
+	{USB_DEVICE(0x093a, 0x2629), .driver_info = FL_VFLIP},
+	{USB_DEVICE(0x093a, 0x262a)},
+	{USB_DEVICE(0x093a, 0x262c)},
+	{USB_DEVICE(0x145f, 0x013c)},
+	{USB_DEVICE(0x1ae7, 0x2001)}, /* SpeedLink Snappy Mic SL-6825-SBK */
+	{}
+};
+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 = KBUILD_MODNAME,
+	.id_table = device_table,
+	.probe = sd_probe,
+	.disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+	.suspend = gspca_suspend,
+	.resume = gspca_resume,
+	.reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/usb/gspca/pac7311.c b/drivers/media/usb/gspca/pac7311.c
new file mode 100644
index 0000000..25f86b1
--- /dev/null
+++ b/drivers/media/usb/gspca/pac7311.c
@@ -0,0 +1,700 @@
+/*
+ *		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
+ */
+
+/* Some documentation about various registers as determined by trial and error.
+ *
+ * Register page 1:
+ *
+ * Address	Description
+ * 0x08		Unknown compressor related, must always be 8 except when not
+ *		in 640x480 resolution and page 4 reg 2 <= 3 then set it to 9 !
+ * 0x1b		Auto white balance related, bit 0 is AWB enable (inverted)
+ *		bits 345 seem to toggle per color gains on/off (inverted)
+ * 0x78		Global control, bit 6 controls the LED (inverted)
+ * 0x80		Compression balance, interesting settings:
+ *		0x01 Use this to allow the camera to switch to higher compr.
+ *		     on the fly. Needed to stay within bandwidth @ 640x480@30
+ *		0x1c From usb captures under Windows for 640x480
+ *		0x2a Values >= this switch the camera to a lower compression,
+ *		     using the same table for both luminance and chrominance.
+ *		     This gives a sharper picture. Usable only at 640x480@ <
+ *		     15 fps or 320x240 / 160x120. Note currently the driver
+ *		     does not use this as the quality gain is small and the
+ *		     generated JPG-s are only understood by v4l-utils >= 0.8.9
+ *		0x3f From usb captures under Windows for 320x240
+ *		0x69 From usb captures under Windows for 160x120
+ *
+ * Register page 4:
+ *
+ * Address	Description
+ * 0x02		Clock divider 2-63, fps =~ 60 / val. Must be a multiple of 3 on
+ *		the 7302, so one of 3, 6, 9, ..., except when between 6 and 12?
+ * 0x0f		Master gain 1-245, low value = high gain
+ * 0x10		Another gain 0-15, limited influence (1-2x gain I guess)
+ * 0x21		Bitfield: 0-1 unused, 2-3 vflip/hflip, 4-5 unknown, 6-7 unused
+ *		Note setting vflip disabled leads to a much lower image quality,
+ *		so we always vflip, and tell userspace to flip it back
+ * 0x27		Seems to toggle various gains on / off, Setting bit 7 seems to
+ *		completely disable the analog amplification block. Set to 0x68
+ *		for max gain, 0x14 for minimal gain.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "pac7311"
+
+#include <linux/input.h>
+#include "gspca.h"
+/* Include pac common sof detection functions */
+#include "pac_common.h"
+
+#define PAC7311_GAIN_DEFAULT     122
+#define PAC7311_EXPOSURE_DEFAULT   3 /* 20 fps, avoid using high compr. */
+
+MODULE_AUTHOR("Thomas Kaiser thomas@kaiser-linux.li");
+MODULE_DESCRIPTION("Pixart PAC7311");
+MODULE_LICENSE("GPL");
+
+struct sd {
+	struct gspca_dev gspca_dev;		/* !! must be the first item */
+
+	struct v4l2_ctrl *contrast;
+	struct v4l2_ctrl *hflip;
+
+	u8 sof_read;
+	u8 autogain_ignore_frames;
+
+	atomic_t avg_lum;
+};
+
+static const struct v4l2_pix_format vga_mode[] = {
+	{160, 120, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE,
+		.bytesperline = 160,
+		.sizeimage = 160 * 120 * 3 / 8 + 590,
+		.colorspace = V4L2_COLORSPACE_JPEG,
+		.priv = 2},
+	{320, 240, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE,
+		.bytesperline = 320,
+		.sizeimage = 320 * 240 * 3 / 8 + 590,
+		.colorspace = V4L2_COLORSPACE_JPEG,
+		.priv = 1},
+	{640, 480, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE,
+		.bytesperline = 640,
+		.sizeimage = 640 * 480 * 3 / 8 + 590,
+		.colorspace = V4L2_COLORSPACE_JPEG,
+		.priv = 0},
+};
+
+#define LOAD_PAGE4		254
+#define END_OF_SEQUENCE		0
+
+static const __u8 init_7311[] = {
+	0xff, 0x01,
+	0x78, 0x40,	/* Bit_0=start stream, Bit_6=LED */
+	0x78, 0x40,	/* Bit_0=start stream, Bit_6=LED */
+	0x78, 0x44,	/* Bit_0=start stream, Bit_6=LED */
+	0xff, 0x04,
+	0x27, 0x80,
+	0x28, 0xca,
+	0x29, 0x53,
+	0x2a, 0x0e,
+	0xff, 0x01,
+	0x3e, 0x20,
+};
+
+static const __u8 start_7311[] = {
+/*	index, len, [value]* */
+	0xff, 1,	0x01,		/* page 1 */
+	0x02, 43,	0x48, 0x0a, 0x40, 0x08, 0x00, 0x00, 0x08, 0x00,
+			0x06, 0xff, 0x11, 0xff, 0x5a, 0x30, 0x90, 0x4c,
+			0x00, 0x07, 0x00, 0x0a, 0x10, 0x00, 0xa0, 0x10,
+			0x02, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x01, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00,
+	0x3e, 42,	0x00, 0x00, 0x78, 0x52, 0x4a, 0x52, 0x78, 0x6e,
+			0x48, 0x46, 0x48, 0x6e, 0x5f, 0x49, 0x42, 0x49,
+			0x5f, 0x5f, 0x49, 0x42, 0x49, 0x5f, 0x6e, 0x48,
+			0x46, 0x48, 0x6e, 0x78, 0x52, 0x4a, 0x52, 0x78,
+			0x00, 0x00, 0x09, 0x1b, 0x34, 0x49, 0x5c, 0x9b,
+			0xd0, 0xff,
+	0x78, 6,	0x44, 0x00, 0xf2, 0x01, 0x01, 0x80,
+	0x7f, 18,	0x2a, 0x1c, 0x00, 0xc8, 0x02, 0x58, 0x03, 0x84,
+			0x12, 0x00, 0x1a, 0x04, 0x08, 0x0c, 0x10, 0x14,
+			0x18, 0x20,
+	0x96, 3,	0x01, 0x08, 0x04,
+	0xa0, 4,	0x44, 0x44, 0x44, 0x04,
+	0xf0, 13,	0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x20, 0x00,
+			0x3f, 0x00, 0x0a, 0x01, 0x00,
+	0xff, 1,	0x04,		/* page 4 */
+	0, LOAD_PAGE4,			/* load the page 4 */
+	0x11, 1,	0x01,
+	0, END_OF_SEQUENCE		/* end of sequence */
+};
+
+#define SKIP		0xaa
+/* page 4 - the value SKIP says skip the index - see reg_w_page() */
+static const __u8 page4_7311[] = {
+	SKIP, SKIP, 0x04, 0x54, 0x07, 0x2b, 0x09, 0x0f,
+	0x09, 0x00, SKIP, SKIP, 0x07, 0x00, 0x00, 0x62,
+	0x08, SKIP, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x03, 0xa0, 0x01, 0xf4, SKIP,
+	SKIP, 0x00, 0x08, SKIP, 0x03, SKIP, 0x00, 0x68,
+	0xca, 0x10, 0x06, 0x78, 0x00, 0x00, 0x00, 0x00,
+	0x23, 0x28, 0x04, 0x11, 0x00, 0x00
+};
+
+static void reg_w_buf(struct gspca_dev *gspca_dev,
+		  __u8 index,
+		  const u8 *buffer, int len)
+{
+	int ret;
+
+	if (gspca_dev->usb_err < 0)
+		return;
+	memcpy(gspca_dev->usb_buf, buffer, len);
+	ret = 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);
+	if (ret < 0) {
+		pr_err("reg_w_buf() failed index 0x%02x, error %d\n",
+		       index, ret);
+		gspca_dev->usb_err = ret;
+	}
+}
+
+
+static void reg_w(struct gspca_dev *gspca_dev,
+		  __u8 index,
+		  __u8 value)
+{
+	int ret;
+
+	if (gspca_dev->usb_err < 0)
+		return;
+	gspca_dev->usb_buf[0] = value;
+	ret = usb_control_msg(gspca_dev->dev,
+			usb_sndctrlpipe(gspca_dev->dev, 0),
+			0,			/* request */
+			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			0, index, gspca_dev->usb_buf, 1,
+			500);
+	if (ret < 0) {
+		pr_err("reg_w() failed index 0x%02x, value 0x%02x, error %d\n",
+		       index, value, ret);
+		gspca_dev->usb_err = ret;
+	}
+}
+
+static void reg_w_seq(struct gspca_dev *gspca_dev,
+		const __u8 *seq, int len)
+{
+	while (--len >= 0) {
+		reg_w(gspca_dev, seq[0], seq[1]);
+		seq += 2;
+	}
+}
+
+/* load the beginning of a page */
+static void reg_w_page(struct gspca_dev *gspca_dev,
+			const __u8 *page, int len)
+{
+	int index;
+	int ret = 0;
+
+	if (gspca_dev->usb_err < 0)
+		return;
+	for (index = 0; index < len; index++) {
+		if (page[index] == SKIP)		/* skip this index */
+			continue;
+		gspca_dev->usb_buf[0] = page[index];
+		ret = usb_control_msg(gspca_dev->dev,
+				usb_sndctrlpipe(gspca_dev->dev, 0),
+				0,			/* request */
+			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+				0, index, gspca_dev->usb_buf, 1,
+				500);
+		if (ret < 0) {
+			pr_err("reg_w_page() failed index 0x%02x, value 0x%02x, error %d\n",
+			       index, page[index], ret);
+			gspca_dev->usb_err = ret;
+			break;
+		}
+	}
+}
+
+/* output a variable sequence */
+static void reg_w_var(struct gspca_dev *gspca_dev,
+			const __u8 *seq,
+			const __u8 *page4, unsigned int page4_len)
+{
+	int index, len;
+
+	for (;;) {
+		index = *seq++;
+		len = *seq++;
+		switch (len) {
+		case END_OF_SEQUENCE:
+			return;
+		case LOAD_PAGE4:
+			reg_w_page(gspca_dev, page4, page4_len);
+			break;
+		default:
+			if (len > USB_BUF_SZ) {
+				PERR("Incorrect variable sequence");
+				return;
+			}
+			while (len > 0) {
+				if (len < 8) {
+					reg_w_buf(gspca_dev,
+						index, seq, len);
+					seq += len;
+					break;
+				}
+				reg_w_buf(gspca_dev, index, seq, 8);
+				seq += 8;
+				index += 8;
+				len -= 8;
+			}
+		}
+	}
+	/* not reached */
+}
+
+/* this function is called at probe time for pac7311 */
+static int sd_config(struct gspca_dev *gspca_dev,
+			const struct usb_device_id *id)
+{
+	struct cam *cam = &gspca_dev->cam;
+
+	cam->cam_mode = vga_mode;
+	cam->nmodes = ARRAY_SIZE(vga_mode);
+	cam->input_flags = V4L2_IN_ST_VFLIP;
+
+	return 0;
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
+{
+	reg_w(gspca_dev, 0xff, 0x04);
+	reg_w(gspca_dev, 0x10, val);
+	/* load registers to sensor (Bit 0, auto clear) */
+	reg_w(gspca_dev, 0x11, 0x01);
+}
+
+static void setgain(struct gspca_dev *gspca_dev, s32 val)
+{
+	reg_w(gspca_dev, 0xff, 0x04);			/* page 4 */
+	reg_w(gspca_dev, 0x0e, 0x00);
+	reg_w(gspca_dev, 0x0f, gspca_dev->gain->maximum - val + 1);
+
+	/* load registers to sensor (Bit 0, auto clear) */
+	reg_w(gspca_dev, 0x11, 0x01);
+}
+
+static void setexposure(struct gspca_dev *gspca_dev, s32 val)
+{
+	reg_w(gspca_dev, 0xff, 0x04);			/* page 4 */
+	reg_w(gspca_dev, 0x02, val);
+
+	/* load registers to sensor (Bit 0, auto clear) */
+	reg_w(gspca_dev, 0x11, 0x01);
+
+	/*
+	 * Page 1 register 8 must always be 0x08 except when not in
+	 *  640x480 mode and page 4 reg 2 <= 3 then it must be 9
+	 */
+	reg_w(gspca_dev, 0xff, 0x01);
+	if (gspca_dev->pixfmt.width != 640 && val <= 3)
+		reg_w(gspca_dev, 0x08, 0x09);
+	else
+		reg_w(gspca_dev, 0x08, 0x08);
+
+	/*
+	 * Page1 register 80 sets the compression balance, normally we
+	 * want / use 0x1c, but for 640x480@30fps we must allow the
+	 * camera to use higher compression or we may run out of
+	 * bandwidth.
+	 */
+	if (gspca_dev->pixfmt.width == 640 && val == 2)
+		reg_w(gspca_dev, 0x80, 0x01);
+	else
+		reg_w(gspca_dev, 0x80, 0x1c);
+
+	/* load registers to sensor (Bit 0, auto clear) */
+	reg_w(gspca_dev, 0x11, 0x01);
+}
+
+static void sethvflip(struct gspca_dev *gspca_dev, s32 hflip, s32 vflip)
+{
+	__u8 data;
+
+	reg_w(gspca_dev, 0xff, 0x04);			/* page 4 */
+	data = (hflip ? 0x04 : 0x00) |
+	       (vflip ? 0x08 : 0x00);
+	reg_w(gspca_dev, 0x21, data);
+
+	/* load registers to sensor (Bit 0, auto clear) */
+	reg_w(gspca_dev, 0x11, 0x01);
+}
+
+/* this function is called at probe and resume time for pac7311 */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+	reg_w_seq(gspca_dev, init_7311, sizeof(init_7311)/2);
+	return gspca_dev->usb_err;
+}
+
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct gspca_dev *gspca_dev =
+		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+	struct sd *sd = (struct sd *)gspca_dev;
+
+	gspca_dev->usb_err = 0;
+
+	if (ctrl->id == V4L2_CID_AUTOGAIN && ctrl->is_new && ctrl->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. */
+		gspca_dev->exposure->val    = PAC7311_EXPOSURE_DEFAULT;
+		gspca_dev->gain->val        = PAC7311_GAIN_DEFAULT;
+		sd->autogain_ignore_frames  = PAC_AUTOGAIN_IGNORE_FRAMES;
+	}
+
+	if (!gspca_dev->streaming)
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_CONTRAST:
+		setcontrast(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_AUTOGAIN:
+		if (gspca_dev->exposure->is_new || (ctrl->is_new && ctrl->val))
+			setexposure(gspca_dev, gspca_dev->exposure->val);
+		if (gspca_dev->gain->is_new || (ctrl->is_new && ctrl->val))
+			setgain(gspca_dev, gspca_dev->gain->val);
+		break;
+	case V4L2_CID_HFLIP:
+		sethvflip(gspca_dev, sd->hflip->val, 1);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+	.s_ctrl = sd_s_ctrl,
+};
+
+/* this function is called at probe time */
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+	gspca_dev->vdev.ctrl_handler = hdl;
+	v4l2_ctrl_handler_init(hdl, 5);
+
+	sd->contrast = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+					V4L2_CID_CONTRAST, 0, 15, 1, 7);
+	gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+					V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+	gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+					V4L2_CID_EXPOSURE, 2, 63, 1,
+					PAC7311_EXPOSURE_DEFAULT);
+	gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+					V4L2_CID_GAIN, 0, 244, 1,
+					PAC7311_GAIN_DEFAULT);
+	sd->hflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+		V4L2_CID_HFLIP, 0, 1, 1, 0);
+
+	if (hdl->error) {
+		pr_err("Could not initialize controls\n");
+		return hdl->error;
+	}
+
+	v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false);
+	return 0;
+}
+
+/* -- start the camera -- */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	sd->sof_read = 0;
+
+	reg_w_var(gspca_dev, start_7311,
+		page4_7311, sizeof(page4_7311));
+	setcontrast(gspca_dev, v4l2_ctrl_g_ctrl(sd->contrast));
+	setgain(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->gain));
+	setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure));
+	sethvflip(gspca_dev, v4l2_ctrl_g_ctrl(sd->hflip), 1);
+
+	/* set correct resolution */
+	switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) {
+	case 2:					/* 160x120 */
+		reg_w(gspca_dev, 0xff, 0x01);
+		reg_w(gspca_dev, 0x17, 0x20);
+		reg_w(gspca_dev, 0x87, 0x10);
+		break;
+	case 1:					/* 320x240 */
+		reg_w(gspca_dev, 0xff, 0x01);
+		reg_w(gspca_dev, 0x17, 0x30);
+		reg_w(gspca_dev, 0x87, 0x11);
+		break;
+	case 0:					/* 640x480 */
+		reg_w(gspca_dev, 0xff, 0x01);
+		reg_w(gspca_dev, 0x17, 0x00);
+		reg_w(gspca_dev, 0x87, 0x12);
+		break;
+	}
+
+	sd->sof_read = 0;
+	sd->autogain_ignore_frames = 0;
+	atomic_set(&sd->avg_lum, -1);
+
+	/* start stream */
+	reg_w(gspca_dev, 0xff, 0x01);
+	reg_w(gspca_dev, 0x78, 0x05);
+
+	return gspca_dev->usb_err;
+}
+
+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, 0x44); /* Bit_0=start stream, Bit_6=LED */
+	reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */
+	reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */
+}
+
+static void do_autogain(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int avg_lum = atomic_read(&sd->avg_lum);
+	int desired_lum, deadzone;
+
+	if (avg_lum == -1)
+		return;
+
+	desired_lum = 170;
+	deadzone = 20;
+
+	if (sd->autogain_ignore_frames > 0)
+		sd->autogain_ignore_frames--;
+	else if (gspca_coarse_grained_expo_autogain(gspca_dev, avg_lum,
+						    desired_lum, deadzone))
+		sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES;
+}
+
+/* JPEG header, part 1 */
+static const unsigned char pac_jpeg_header1[] = {
+  0xff, 0xd8,		/* SOI: Start of Image */
+
+  0xff, 0xc0,		/* SOF0: Start of Frame (Baseline DCT) */
+  0x00, 0x11,		/* length = 17 bytes (including this length field) */
+  0x08			/* Precision: 8 */
+  /* 2 bytes is placed here: number of image lines */
+  /* 2 bytes is placed here: samples per line */
+};
+
+/* JPEG header, continued */
+static const unsigned char pac_jpeg_header2[] = {
+  0x03,			/* Number of image components: 3 */
+  0x01, 0x21, 0x00,	/* ID=1, Subsampling 1x1, Quantization table: 0 */
+  0x02, 0x11, 0x01,	/* ID=2, Subsampling 2x1, Quantization table: 1 */
+  0x03, 0x11, 0x01,	/* ID=3, Subsampling 2x1, Quantization table: 1 */
+
+  0xff, 0xda,		/* SOS: Start Of Scan */
+  0x00, 0x0c,		/* length = 12 bytes (including this length field) */
+  0x03,			/* number of components: 3 */
+  0x01, 0x00,		/* selector 1, table 0x00 */
+  0x02, 0x11,		/* selector 2, table 0x11 */
+  0x03, 0x11,		/* selector 3, table 0x11 */
+  0x00, 0x3f,		/* Spectral selection: 0 .. 63 */
+  0x00			/* Successive approximation: 0 */
+};
+
+static void pac_start_frame(struct gspca_dev *gspca_dev,
+		__u16 lines, __u16 samples_per_line)
+{
+	unsigned char tmpbuf[4];
+
+	gspca_frame_add(gspca_dev, FIRST_PACKET,
+		pac_jpeg_header1, sizeof(pac_jpeg_header1));
+
+	tmpbuf[0] = lines >> 8;
+	tmpbuf[1] = lines & 0xff;
+	tmpbuf[2] = samples_per_line >> 8;
+	tmpbuf[3] = samples_per_line & 0xff;
+
+	gspca_frame_add(gspca_dev, INTER_PACKET,
+		tmpbuf, sizeof(tmpbuf));
+	gspca_frame_add(gspca_dev, INTER_PACKET,
+		pac_jpeg_header2, sizeof(pac_jpeg_header2));
+}
+
+/* this function is run at interrupt level */
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+			u8 *data,			/* isoc packet */
+			int len)			/* iso packet length */
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u8 *image;
+	unsigned char *sof;
+
+	sof = pac_find_sof(gspca_dev, &sd->sof_read, data, len);
+	if (sof) {
+		int n, lum_offset, footer_length;
+
+		/*
+		 * 6 bytes after the FF D9 EOF marker a number of lumination
+		 * bytes are send corresponding to different parts of the
+		 * image, the 14th and 15th byte after the EOF seem to
+		 * correspond to the center of the image.
+		 */
+		lum_offset = 24 + sizeof pac_sof_marker;
+		footer_length = 26;
+
+		/* Finish decoding current frame */
+		n = (sof - data) - (footer_length + sizeof pac_sof_marker);
+		if (n < 0) {
+			gspca_dev->image_len += n;
+			n = 0;
+		} else {
+			gspca_frame_add(gspca_dev, INTER_PACKET, data, n);
+		}
+		image = gspca_dev->image;
+		if (image != NULL
+		 && image[gspca_dev->image_len - 2] == 0xff
+		 && image[gspca_dev->image_len - 1] == 0xd9)
+			gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+
+		n = sof - data;
+		len -= n;
+		data = sof;
+
+		/* Get average lumination */
+		if (gspca_dev->last_packet_type == LAST_PACKET &&
+				n >= lum_offset)
+			atomic_set(&sd->avg_lum, data[-lum_offset] +
+						data[-lum_offset + 1]);
+		else
+			atomic_set(&sd->avg_lum, -1);
+
+		/* Start the new frame with the jpeg header */
+		pac_start_frame(gspca_dev,
+			gspca_dev->pixfmt.height, gspca_dev->pixfmt.width);
+	}
+	gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+}
+
+#if IS_ENABLED(CONFIG_INPUT)
+static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
+			u8 *data,		/* interrupt packet data */
+			int len)		/* interrupt packet length */
+{
+	int ret = -EINVAL;
+	u8 data0, data1;
+
+	if (len == 2) {
+		data0 = data[0];
+		data1 = data[1];
+		if ((data0 == 0x00 && data1 == 0x11) ||
+		    (data0 == 0x22 && data1 == 0x33) ||
+		    (data0 == 0x44 && data1 == 0x55) ||
+		    (data0 == 0x66 && data1 == 0x77) ||
+		    (data0 == 0x88 && data1 == 0x99) ||
+		    (data0 == 0xaa && data1 == 0xbb) ||
+		    (data0 == 0xcc && data1 == 0xdd) ||
+		    (data0 == 0xee && data1 == 0xff)) {
+			input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1);
+			input_sync(gspca_dev->input_dev);
+			input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
+			input_sync(gspca_dev->input_dev);
+			ret = 0;
+		}
+	}
+
+	return ret;
+}
+#endif
+
+static const struct sd_desc sd_desc = {
+	.name = MODULE_NAME,
+	.config = sd_config,
+	.init = sd_init,
+	.init_controls = sd_init_controls,
+	.start = sd_start,
+	.stopN = sd_stopN,
+	.pkt_scan = sd_pkt_scan,
+	.dq_callback = do_autogain,
+#if IS_ENABLED(CONFIG_INPUT)
+	.int_pkt_scan = sd_int_pkt_scan,
+#endif
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+	{USB_DEVICE(0x093a, 0x2600)},
+	{USB_DEVICE(0x093a, 0x2601)},
+	{USB_DEVICE(0x093a, 0x2603)},
+	{USB_DEVICE(0x093a, 0x2608)},
+	{USB_DEVICE(0x093a, 0x260e)},
+	{USB_DEVICE(0x093a, 0x260f)},
+	{}
+};
+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,
+#ifdef CONFIG_PM
+	.suspend = gspca_suspend,
+	.resume = gspca_resume,
+	.reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/usb/gspca/pac_common.h b/drivers/media/usb/gspca/pac_common.h
new file mode 100644
index 0000000..fbc5e22
--- /dev/null
+++ b/drivers/media/usb/gspca/pac_common.h
@@ -0,0 +1,134 @@
+/*
+ * Pixart PAC207BCA / PAC73xx common functions
+ *
+ * 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
+ *
+ */
+
+/* We calculate the autogain at the end of the transfer of a frame, at this
+   moment a frame with the old settings is being captured and transmitted. So
+   if we adjust the gain or exposure we must ignore atleast the next frame for
+   the new settings to come into effect before doing any other adjustments. */
+#define PAC_AUTOGAIN_IGNORE_FRAMES	2
+
+static const unsigned char pac_sof_marker[5] =
+		{ 0xff, 0xff, 0x00, 0xff, 0x96 };
+
+/*
+   The following state machine finds the SOF marker sequence
+   0xff, 0xff, 0x00, 0xff, 0x96 in a byte stream.
+
+	   +----------+
+	   | 0: START |<---------------\
+	   +----------+<-\             |
+	     |       \---/otherwise    |
+	     v 0xff                    |
+	   +----------+ otherwise      |
+	   |     1    |--------------->*
+	   |          |                ^
+	   +----------+                |
+	     |                         |
+	     v 0xff                    |
+	   +----------+<-\0xff         |
+	/->|          |--/             |
+	|  |     2    |--------------->*
+	|  |          | otherwise      ^
+	|  +----------+                |
+	|    |                         |
+	|    v 0x00                    |
+	|  +----------+                |
+	|  |     3    |                |
+	|  |          |--------------->*
+	|  +----------+ otherwise      ^
+	|    |                         |
+   0xff |    v 0xff                    |
+	|  +----------+                |
+	\--|     4    |                |
+	   |          |----------------/
+	   +----------+ otherwise
+	     |
+	     v 0x96
+	   +----------+
+	   |  FOUND   |
+	   +----------+
+*/
+
+static unsigned char *pac_find_sof(struct gspca_dev *gspca_dev, u8 *sof_read,
+					unsigned char *m, int len)
+{
+	int i;
+
+	/* Search for the SOF marker (fixed part) in the header */
+	for (i = 0; i < len; i++) {
+		switch (*sof_read) {
+		case 0:
+			if (m[i] == 0xff)
+				*sof_read = 1;
+			break;
+		case 1:
+			if (m[i] == 0xff)
+				*sof_read = 2;
+			else
+				*sof_read = 0;
+			break;
+		case 2:
+			switch (m[i]) {
+			case 0x00:
+				*sof_read = 3;
+				break;
+			case 0xff:
+				/* stay in this state */
+				break;
+			default:
+				*sof_read = 0;
+			}
+			break;
+		case 3:
+			if (m[i] == 0xff)
+				*sof_read = 4;
+			else
+				*sof_read = 0;
+			break;
+		case 4:
+			switch (m[i]) {
+			case 0x96:
+				/* Pattern found */
+				PDEBUG(D_FRAM,
+					"SOF found, bytes to analyze: %u."
+					" Frame starts at byte #%u",
+					len, i + 1);
+				*sof_read = 0;
+				return m + i + 1;
+				break;
+			case 0xff:
+				*sof_read = 2;
+				break;
+			default:
+				*sof_read = 0;
+			}
+			break;
+		default:
+			*sof_read = 0;
+		}
+	}
+
+	return NULL;
+}
diff --git a/drivers/media/usb/gspca/se401.c b/drivers/media/usb/gspca/se401.c
new file mode 100644
index 0000000..5102cea
--- /dev/null
+++ b/drivers/media/usb/gspca/se401.c
@@ -0,0 +1,739 @@
+/*
+ * GSPCA Endpoints (formerly known as AOX) se401 USB Camera sub Driver
+ *
+ * Copyright (C) 2011 Hans de Goede <hdegoede@redhat.com>
+ *
+ * Based on the v4l1 se401 driver which is:
+ *
+ * Copyright (c) 2000 Jeroen B. Vreeken (pe1rxq@amsat.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 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "se401"
+
+#define BULK_SIZE 4096
+#define PACKET_SIZE 1024
+#define READ_REQ_SIZE 64
+#define MAX_MODES ((READ_REQ_SIZE - 6) / 4)
+/* The se401 compression algorithm uses a fixed quant factor, which
+   can be configured by setting the high nibble of the SE401_OPERATINGMODE
+   feature. This needs to exactly match what is in libv4l! */
+#define SE401_QUANT_FACT 8
+
+#include <linux/input.h>
+#include <linux/slab.h>
+#include "gspca.h"
+#include "se401.h"
+
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_DESCRIPTION("Endpoints se401");
+MODULE_LICENSE("GPL");
+
+/* exposure change state machine states */
+enum {
+	EXPO_CHANGED,
+	EXPO_DROP_FRAME,
+	EXPO_NO_CHANGE,
+};
+
+/* specific webcam descriptor */
+struct sd {
+	struct gspca_dev gspca_dev;	/* !! must be the first item */
+	struct { /* exposure/freq control cluster */
+		struct v4l2_ctrl *exposure;
+		struct v4l2_ctrl *freq;
+	};
+	bool has_brightness;
+	struct v4l2_pix_format fmts[MAX_MODES];
+	int pixels_read;
+	int packet_read;
+	u8 packet[PACKET_SIZE];
+	u8 restart_stream;
+	u8 button_state;
+	u8 resetlevel;
+	u8 resetlevel_frame_count;
+	int resetlevel_adjust_dir;
+	int expo_change_state;
+};
+
+
+static void se401_write_req(struct gspca_dev *gspca_dev, u16 req, u16 value,
+			    int silent)
+{
+	int err;
+
+	if (gspca_dev->usb_err < 0)
+		return;
+
+	err = usb_control_msg(gspca_dev->dev,
+			      usb_sndctrlpipe(gspca_dev->dev, 0), req,
+			      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			      value, 0, NULL, 0, 1000);
+	if (err < 0) {
+		if (!silent)
+			pr_err("write req failed req %#04x val %#04x error %d\n",
+			       req, value, err);
+		gspca_dev->usb_err = err;
+	}
+}
+
+static void se401_read_req(struct gspca_dev *gspca_dev, u16 req, int silent)
+{
+	int err;
+
+	if (gspca_dev->usb_err < 0)
+		return;
+
+	if (USB_BUF_SZ < READ_REQ_SIZE) {
+		pr_err("USB_BUF_SZ too small!!\n");
+		gspca_dev->usb_err = -ENOBUFS;
+		return;
+	}
+
+	err = usb_control_msg(gspca_dev->dev,
+			      usb_rcvctrlpipe(gspca_dev->dev, 0), req,
+			      USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			      0, 0, gspca_dev->usb_buf, READ_REQ_SIZE, 1000);
+	if (err < 0) {
+		if (!silent)
+			pr_err("read req failed req %#04x error %d\n",
+			       req, err);
+		gspca_dev->usb_err = err;
+	}
+}
+
+static void se401_set_feature(struct gspca_dev *gspca_dev,
+			      u16 selector, u16 param)
+{
+	int err;
+
+	if (gspca_dev->usb_err < 0)
+		return;
+
+	err = usb_control_msg(gspca_dev->dev,
+			      usb_sndctrlpipe(gspca_dev->dev, 0),
+			      SE401_REQ_SET_EXT_FEATURE,
+			      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			      param, selector, NULL, 0, 1000);
+	if (err < 0) {
+		pr_err("set feature failed sel %#04x param %#04x error %d\n",
+		       selector, param, err);
+		gspca_dev->usb_err = err;
+	}
+}
+
+static int se401_get_feature(struct gspca_dev *gspca_dev, u16 selector)
+{
+	int err;
+
+	if (gspca_dev->usb_err < 0)
+		return gspca_dev->usb_err;
+
+	if (USB_BUF_SZ < 2) {
+		pr_err("USB_BUF_SZ too small!!\n");
+		gspca_dev->usb_err = -ENOBUFS;
+		return gspca_dev->usb_err;
+	}
+
+	err = usb_control_msg(gspca_dev->dev,
+			      usb_rcvctrlpipe(gspca_dev->dev, 0),
+			      SE401_REQ_GET_EXT_FEATURE,
+			      USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			      0, selector, gspca_dev->usb_buf, 2, 1000);
+	if (err < 0) {
+		pr_err("get feature failed sel %#04x error %d\n",
+		       selector, err);
+		gspca_dev->usb_err = err;
+		return err;
+	}
+	return gspca_dev->usb_buf[0] | (gspca_dev->usb_buf[1] << 8);
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
+{
+	/* HDG: this does not seem to do anything on my cam */
+	se401_write_req(gspca_dev, SE401_REQ_SET_BRT, val, 0);
+}
+
+static void setgain(struct gspca_dev *gspca_dev, s32 val)
+{
+	u16 gain = 63 - val;
+
+	/* red color gain */
+	se401_set_feature(gspca_dev, HV7131_REG_ARCG, gain);
+	/* green color gain */
+	se401_set_feature(gspca_dev, HV7131_REG_AGCG, gain);
+	/* blue color gain */
+	se401_set_feature(gspca_dev, HV7131_REG_ABCG, gain);
+}
+
+static void setexposure(struct gspca_dev *gspca_dev, s32 val, s32 freq)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int integration = val << 6;
+	u8 expose_h, expose_m, expose_l;
+
+	/* Do this before the set_feature calls, for proper timing wrt
+	   the interrupt driven pkt_scan. Note we may still race but that
+	   is not a big issue, the expo change state machine is merely for
+	   avoiding underexposed frames getting send out, if one sneaks
+	   through so be it */
+	sd->expo_change_state = EXPO_CHANGED;
+
+	if (freq == V4L2_CID_POWER_LINE_FREQUENCY_50HZ)
+		integration = integration - integration % 106667;
+	if (freq == V4L2_CID_POWER_LINE_FREQUENCY_60HZ)
+		integration = integration - integration % 88889;
+
+	expose_h = (integration >> 16);
+	expose_m = (integration >> 8);
+	expose_l = integration;
+
+	/* integration time low */
+	se401_set_feature(gspca_dev, HV7131_REG_TITL, expose_l);
+	/* integration time mid */
+	se401_set_feature(gspca_dev, HV7131_REG_TITM, expose_m);
+	/* integration time high */
+	se401_set_feature(gspca_dev, HV7131_REG_TITU, expose_h);
+}
+
+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;
+	u8 *cd = gspca_dev->usb_buf;
+	int i, j, n;
+	int widths[MAX_MODES], heights[MAX_MODES];
+
+	/* Read the camera descriptor */
+	se401_read_req(gspca_dev, SE401_REQ_GET_CAMERA_DESCRIPTOR, 1);
+	if (gspca_dev->usb_err) {
+		/* Sometimes after being idle for a while the se401 won't
+		   respond and needs a good kicking  */
+		usb_reset_device(gspca_dev->dev);
+		gspca_dev->usb_err = 0;
+		se401_read_req(gspca_dev, SE401_REQ_GET_CAMERA_DESCRIPTOR, 0);
+	}
+
+	/* Some cameras start with their LED on */
+	se401_write_req(gspca_dev, SE401_REQ_LED_CONTROL, 0, 0);
+	if (gspca_dev->usb_err)
+		return gspca_dev->usb_err;
+
+	if (cd[1] != 0x41) {
+		pr_err("Wrong descriptor type\n");
+		return -ENODEV;
+	}
+
+	if (!(cd[2] & SE401_FORMAT_BAYER)) {
+		pr_err("Bayer format not supported!\n");
+		return -ENODEV;
+	}
+
+	if (cd[3])
+		pr_info("ExtraFeatures: %d\n", cd[3]);
+
+	n = cd[4] | (cd[5] << 8);
+	if (n > MAX_MODES) {
+		pr_err("Too many frame sizes\n");
+		return -ENODEV;
+	}
+
+	for (i = 0; i < n ; i++) {
+		widths[i] = cd[6 + i * 4 + 0] | (cd[6 + i * 4 + 1] << 8);
+		heights[i] = cd[6 + i * 4 + 2] | (cd[6 + i * 4 + 3] << 8);
+	}
+
+	for (i = 0; i < n ; i++) {
+		sd->fmts[i].width = widths[i];
+		sd->fmts[i].height = heights[i];
+		sd->fmts[i].field = V4L2_FIELD_NONE;
+		sd->fmts[i].colorspace = V4L2_COLORSPACE_SRGB;
+		sd->fmts[i].priv = 1;
+
+		/* janggu compression only works for 1/4th or 1/16th res */
+		for (j = 0; j < n; j++) {
+			if (widths[j] / 2 == widths[i] &&
+			    heights[j] / 2 == heights[i]) {
+				sd->fmts[i].priv = 2;
+				break;
+			}
+		}
+		/* 1/16th if available too is better then 1/4th, because
+		   we then use a larger area of the sensor */
+		for (j = 0; j < n; j++) {
+			if (widths[j] / 4 == widths[i] &&
+			    heights[j] / 4 == heights[i]) {
+				sd->fmts[i].priv = 4;
+				break;
+			}
+		}
+
+		if (sd->fmts[i].priv == 1) {
+			/* Not a 1/4th or 1/16th res, use bayer */
+			sd->fmts[i].pixelformat = V4L2_PIX_FMT_SBGGR8;
+			sd->fmts[i].bytesperline = widths[i];
+			sd->fmts[i].sizeimage = widths[i] * heights[i];
+			pr_info("Frame size: %dx%d bayer\n",
+				widths[i], heights[i]);
+		} else {
+			/* Found a match use janggu compression */
+			sd->fmts[i].pixelformat = V4L2_PIX_FMT_SE401;
+			sd->fmts[i].bytesperline = 0;
+			sd->fmts[i].sizeimage = widths[i] * heights[i] * 3;
+			pr_info("Frame size: %dx%d 1/%dth janggu\n",
+				widths[i], heights[i],
+				sd->fmts[i].priv * sd->fmts[i].priv);
+		}
+	}
+
+	cam->cam_mode = sd->fmts;
+	cam->nmodes = n;
+	cam->bulk = 1;
+	cam->bulk_size = BULK_SIZE;
+	cam->bulk_nurbs = 4;
+	sd->resetlevel = 0x2d; /* Set initial resetlevel */
+
+	/* See if the camera supports brightness */
+	se401_read_req(gspca_dev, SE401_REQ_GET_BRT, 1);
+	sd->has_brightness = !!gspca_dev->usb_err;
+	gspca_dev->usb_err = 0;
+
+	return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+	return 0;
+}
+
+/* function called at start time before URB creation */
+static int sd_isoc_init(struct gspca_dev *gspca_dev)
+{
+	gspca_dev->alt = 1;	/* Ignore the bogus isoc alt settings */
+
+	return gspca_dev->usb_err;
+}
+
+/* -- start the camera -- */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *)gspca_dev;
+	int mult = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
+	int mode = 0;
+
+	se401_write_req(gspca_dev, SE401_REQ_CAMERA_POWER, 1, 1);
+	if (gspca_dev->usb_err) {
+		/* Sometimes after being idle for a while the se401 won't
+		   respond and needs a good kicking  */
+		usb_reset_device(gspca_dev->dev);
+		gspca_dev->usb_err = 0;
+		se401_write_req(gspca_dev, SE401_REQ_CAMERA_POWER, 1, 0);
+	}
+	se401_write_req(gspca_dev, SE401_REQ_LED_CONTROL, 1, 0);
+
+	se401_set_feature(gspca_dev, HV7131_REG_MODE_B, 0x05);
+
+	/* set size + mode */
+	se401_write_req(gspca_dev, SE401_REQ_SET_WIDTH,
+			gspca_dev->pixfmt.width * mult, 0);
+	se401_write_req(gspca_dev, SE401_REQ_SET_HEIGHT,
+			gspca_dev->pixfmt.height * mult, 0);
+	/*
+	 * HDG: disabled this as it does not seem to do anything
+	 * se401_write_req(gspca_dev, SE401_REQ_SET_OUTPUT_MODE,
+	 *		   SE401_FORMAT_BAYER, 0);
+	 */
+
+	switch (mult) {
+	case 1: /* Raw bayer */
+		mode = 0x03; break;
+	case 2: /* 1/4th janggu */
+		mode = SE401_QUANT_FACT << 4; break;
+	case 4: /* 1/16th janggu */
+		mode = (SE401_QUANT_FACT << 4) | 0x02; break;
+	}
+	se401_set_feature(gspca_dev, SE401_OPERATINGMODE, mode);
+
+	se401_set_feature(gspca_dev, HV7131_REG_ARLV, sd->resetlevel);
+
+	sd->packet_read = 0;
+	sd->pixels_read = 0;
+	sd->restart_stream = 0;
+	sd->resetlevel_frame_count = 0;
+	sd->resetlevel_adjust_dir = 0;
+	sd->expo_change_state = EXPO_NO_CHANGE;
+
+	se401_write_req(gspca_dev, SE401_REQ_START_CONTINUOUS_CAPTURE, 0, 0);
+
+	return gspca_dev->usb_err;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+	se401_write_req(gspca_dev, SE401_REQ_STOP_CONTINUOUS_CAPTURE, 0, 0);
+	se401_write_req(gspca_dev, SE401_REQ_LED_CONTROL, 0, 0);
+	se401_write_req(gspca_dev, SE401_REQ_CAMERA_POWER, 0, 0);
+}
+
+static void sd_dq_callback(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *)gspca_dev;
+	unsigned int ahrc, alrc;
+	int oldreset, adjust_dir;
+
+	/* Restart the stream if requested do so by pkt_scan */
+	if (sd->restart_stream) {
+		sd_stopN(gspca_dev);
+		sd_start(gspca_dev);
+		sd->restart_stream = 0;
+	}
+
+	/* Automatically adjust sensor reset level
+	   Hyundai have some really nice docs about this and other sensor
+	   related stuff on their homepage: www.hei.co.kr */
+	sd->resetlevel_frame_count++;
+	if (sd->resetlevel_frame_count < 20)
+		return;
+
+	/* For some reason this normally read-only register doesn't get reset
+	   to zero after reading them just once... */
+	se401_get_feature(gspca_dev, HV7131_REG_HIREFNOH);
+	se401_get_feature(gspca_dev, HV7131_REG_HIREFNOL);
+	se401_get_feature(gspca_dev, HV7131_REG_LOREFNOH);
+	se401_get_feature(gspca_dev, HV7131_REG_LOREFNOL);
+	ahrc = 256*se401_get_feature(gspca_dev, HV7131_REG_HIREFNOH) +
+	    se401_get_feature(gspca_dev, HV7131_REG_HIREFNOL);
+	alrc = 256*se401_get_feature(gspca_dev, HV7131_REG_LOREFNOH) +
+	    se401_get_feature(gspca_dev, HV7131_REG_LOREFNOL);
+
+	/* Not an exact science, but it seems to work pretty well... */
+	oldreset = sd->resetlevel;
+	if (alrc > 10) {
+		while (alrc >= 10 && sd->resetlevel < 63) {
+			sd->resetlevel++;
+			alrc /= 2;
+		}
+	} else if (ahrc > 20) {
+		while (ahrc >= 20 && sd->resetlevel > 0) {
+			sd->resetlevel--;
+			ahrc /= 2;
+		}
+	}
+	/* Detect ping-pong-ing and halve adjustment to avoid overshoot */
+	if (sd->resetlevel > oldreset)
+		adjust_dir = 1;
+	else
+		adjust_dir = -1;
+	if (sd->resetlevel_adjust_dir &&
+	    sd->resetlevel_adjust_dir != adjust_dir)
+		sd->resetlevel = oldreset + (sd->resetlevel - oldreset) / 2;
+
+	if (sd->resetlevel != oldreset) {
+		sd->resetlevel_adjust_dir = adjust_dir;
+		se401_set_feature(gspca_dev, HV7131_REG_ARLV, sd->resetlevel);
+	}
+
+	sd->resetlevel_frame_count = 0;
+}
+
+static void sd_complete_frame(struct gspca_dev *gspca_dev, u8 *data, int len)
+{
+	struct sd *sd = (struct sd *)gspca_dev;
+
+	switch (sd->expo_change_state) {
+	case EXPO_CHANGED:
+		/* The exposure was changed while this frame
+		   was being send, so this frame is ok */
+		sd->expo_change_state = EXPO_DROP_FRAME;
+		break;
+	case EXPO_DROP_FRAME:
+		/* The exposure was changed while this frame
+		   was being captured, drop it! */
+		gspca_dev->last_packet_type = DISCARD_PACKET;
+		sd->expo_change_state = EXPO_NO_CHANGE;
+		break;
+	case EXPO_NO_CHANGE:
+		break;
+	}
+	gspca_frame_add(gspca_dev, LAST_PACKET, data, len);
+}
+
+static void sd_pkt_scan_janggu(struct gspca_dev *gspca_dev, u8 *data, int len)
+{
+	struct sd *sd = (struct sd *)gspca_dev;
+	int imagesize = gspca_dev->pixfmt.width * gspca_dev->pixfmt.height;
+	int i, plen, bits, pixels, info, count;
+
+	if (sd->restart_stream)
+		return;
+
+	/* Sometimes a 1024 bytes garbage bulk packet is send between frames */
+	if (gspca_dev->last_packet_type == LAST_PACKET && len == 1024) {
+		gspca_dev->last_packet_type = DISCARD_PACKET;
+		return;
+	}
+
+	i = 0;
+	while (i < len) {
+		/* Read header if not already be present from prev bulk pkt */
+		if (sd->packet_read < 4) {
+			count = 4 - sd->packet_read;
+			if (count > len - i)
+				count = len - i;
+			memcpy(&sd->packet[sd->packet_read], &data[i], count);
+			sd->packet_read += count;
+			i += count;
+			if (sd->packet_read < 4)
+				break;
+		}
+		bits   = sd->packet[3] + (sd->packet[2] << 8);
+		pixels = sd->packet[1] + ((sd->packet[0] & 0x3f) << 8);
+		info   = (sd->packet[0] & 0xc0) >> 6;
+		plen   = ((bits + 47) >> 4) << 1;
+		/* Sanity checks */
+		if (plen > 1024) {
+			pr_err("invalid packet len %d restarting stream\n",
+			       plen);
+			goto error;
+		}
+		if (info == 3) {
+			pr_err("unknown frame info value restarting stream\n");
+			goto error;
+		}
+
+		/* Read (remainder of) packet contents */
+		count = plen - sd->packet_read;
+		if (count > len - i)
+			count = len - i;
+		memcpy(&sd->packet[sd->packet_read], &data[i], count);
+		sd->packet_read += count;
+		i += count;
+		if (sd->packet_read < plen)
+			break;
+
+		sd->pixels_read += pixels;
+		sd->packet_read = 0;
+
+		switch (info) {
+		case 0: /* Frame data */
+			gspca_frame_add(gspca_dev, INTER_PACKET, sd->packet,
+					plen);
+			break;
+		case 1: /* EOF */
+			if (sd->pixels_read != imagesize) {
+				pr_err("frame size %d expected %d\n",
+				       sd->pixels_read, imagesize);
+				goto error;
+			}
+			sd_complete_frame(gspca_dev, sd->packet, plen);
+			return; /* Discard the rest of the bulk packet !! */
+		case 2: /* SOF */
+			gspca_frame_add(gspca_dev, FIRST_PACKET, sd->packet,
+					plen);
+			sd->pixels_read = pixels;
+			break;
+		}
+	}
+	return;
+
+error:
+	sd->restart_stream = 1;
+	/* Give userspace a 0 bytes frame, so our dq callback gets
+	   called and it can restart the stream */
+	gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0);
+	gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+}
+
+static void sd_pkt_scan_bayer(struct gspca_dev *gspca_dev, u8 *data, int len)
+{
+	struct cam *cam = &gspca_dev->cam;
+	int imagesize = cam->cam_mode[gspca_dev->curr_mode].sizeimage;
+
+	if (gspca_dev->image_len == 0) {
+		gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
+		return;
+	}
+
+	if (gspca_dev->image_len + len >= imagesize) {
+		sd_complete_frame(gspca_dev, data, len);
+		return;
+	}
+
+	gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, int len)
+{
+	int mult = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
+
+	if (len == 0)
+		return;
+
+	if (mult == 1) /* mult == 1 means raw bayer */
+		sd_pkt_scan_bayer(gspca_dev, data, len);
+	else
+		sd_pkt_scan_janggu(gspca_dev, data, len);
+}
+
+#if IS_ENABLED(CONFIG_INPUT)
+static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, int len)
+{
+	struct sd *sd = (struct sd *)gspca_dev;
+	u8 state;
+
+	if (len != 2)
+		return -EINVAL;
+
+	switch (data[0]) {
+	case 0:
+	case 1:
+		state = data[0];
+		break;
+	default:
+		return -EINVAL;
+	}
+	if (sd->button_state != state) {
+		input_report_key(gspca_dev->input_dev, KEY_CAMERA, state);
+		input_sync(gspca_dev->input_dev);
+		sd->button_state = state;
+	}
+
+	return 0;
+}
+#endif
+
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct gspca_dev *gspca_dev =
+		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+	struct sd *sd = (struct sd *)gspca_dev;
+
+	gspca_dev->usb_err = 0;
+
+	if (!gspca_dev->streaming)
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_BRIGHTNESS:
+		setbrightness(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_GAIN:
+		setgain(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_EXPOSURE:
+		setexposure(gspca_dev, ctrl->val, sd->freq->val);
+		break;
+	}
+	return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+	.s_ctrl = sd_s_ctrl,
+};
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *)gspca_dev;
+	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+	gspca_dev->vdev.ctrl_handler = hdl;
+	v4l2_ctrl_handler_init(hdl, 4);
+	if (sd->has_brightness)
+		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_BRIGHTNESS, 0, 255, 1, 15);
+	/* max is really 63 but > 50 is not pretty */
+	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_GAIN, 0, 50, 1, 25);
+	sd->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_EXPOSURE, 0, 32767, 1, 15000);
+	sd->freq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
+			V4L2_CID_POWER_LINE_FREQUENCY,
+			V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0, 0);
+
+	if (hdl->error) {
+		pr_err("Could not initialize controls\n");
+		return hdl->error;
+	}
+	v4l2_ctrl_cluster(2, &sd->exposure);
+	return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+	.name = MODULE_NAME,
+	.config = sd_config,
+	.init = sd_init,
+	.init_controls = sd_init_controls,
+	.isoc_init = sd_isoc_init,
+	.start = sd_start,
+	.stopN = sd_stopN,
+	.dq_callback = sd_dq_callback,
+	.pkt_scan = sd_pkt_scan,
+#if IS_ENABLED(CONFIG_INPUT)
+	.int_pkt_scan = sd_int_pkt_scan,
+#endif
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+	{USB_DEVICE(0x03e8, 0x0004)}, /* Endpoints/Aox SE401 */
+	{USB_DEVICE(0x0471, 0x030b)}, /* Philips PCVC665K */
+	{USB_DEVICE(0x047d, 0x5001)}, /* Kensington 67014 */
+	{USB_DEVICE(0x047d, 0x5002)}, /* Kensington 6701(5/7) */
+	{USB_DEVICE(0x047d, 0x5003)}, /* Kensington 67016 */
+	{}
+};
+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 int sd_pre_reset(struct usb_interface *intf)
+{
+	return 0;
+}
+
+static int sd_post_reset(struct usb_interface *intf)
+{
+	return 0;
+}
+
+static struct usb_driver sd_driver = {
+	.name = MODULE_NAME,
+	.id_table = device_table,
+	.probe = sd_probe,
+	.disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+	.suspend = gspca_suspend,
+	.resume = gspca_resume,
+	.reset_resume = gspca_resume,
+#endif
+	.pre_reset = sd_pre_reset,
+	.post_reset = sd_post_reset,
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/usb/gspca/se401.h b/drivers/media/usb/gspca/se401.h
new file mode 100644
index 0000000..96d8ebf
--- /dev/null
+++ b/drivers/media/usb/gspca/se401.h
@@ -0,0 +1,90 @@
+/*
+ * GSPCA Endpoints (formerly known as AOX) se401 USB Camera sub Driver
+ *
+ * Copyright (C) 2011 Hans de Goede <hdegoede@redhat.com>
+ *
+ * Based on the v4l1 se401 driver which is:
+ *
+ * Copyright (c) 2000 Jeroen B. Vreeken (pe1rxq@amsat.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 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 SE401_REQ_GET_CAMERA_DESCRIPTOR		0x06
+#define SE401_REQ_START_CONTINUOUS_CAPTURE	0x41
+#define SE401_REQ_STOP_CONTINUOUS_CAPTURE	0x42
+#define SE401_REQ_CAPTURE_FRAME			0x43
+#define SE401_REQ_GET_BRT			0x44
+#define SE401_REQ_SET_BRT			0x45
+#define SE401_REQ_GET_WIDTH			0x4c
+#define SE401_REQ_SET_WIDTH			0x4d
+#define SE401_REQ_GET_HEIGHT			0x4e
+#define SE401_REQ_SET_HEIGHT			0x4f
+#define SE401_REQ_GET_OUTPUT_MODE		0x50
+#define SE401_REQ_SET_OUTPUT_MODE		0x51
+#define SE401_REQ_GET_EXT_FEATURE		0x52
+#define SE401_REQ_SET_EXT_FEATURE		0x53
+#define SE401_REQ_CAMERA_POWER			0x56
+#define SE401_REQ_LED_CONTROL			0x57
+#define SE401_REQ_BIOS				0xff
+
+#define SE401_BIOS_READ				0x07
+
+#define SE401_FORMAT_BAYER	0x40
+
+/* Hyundai hv7131b registers
+   7121 and 7141 should be the same (haven't really checked...) */
+/* Mode registers: */
+#define HV7131_REG_MODE_A		0x00
+#define HV7131_REG_MODE_B		0x01
+#define HV7131_REG_MODE_C		0x02
+/* Frame registers: */
+#define HV7131_REG_FRSU		0x10
+#define HV7131_REG_FRSL		0x11
+#define HV7131_REG_FCSU		0x12
+#define HV7131_REG_FCSL		0x13
+#define HV7131_REG_FWHU		0x14
+#define HV7131_REG_FWHL		0x15
+#define HV7131_REG_FWWU		0x16
+#define HV7131_REG_FWWL		0x17
+/* Timing registers: */
+#define HV7131_REG_THBU		0x20
+#define HV7131_REG_THBL		0x21
+#define HV7131_REG_TVBU		0x22
+#define HV7131_REG_TVBL		0x23
+#define HV7131_REG_TITU		0x25
+#define HV7131_REG_TITM		0x26
+#define HV7131_REG_TITL		0x27
+#define HV7131_REG_TMCD		0x28
+/* Adjust Registers: */
+#define HV7131_REG_ARLV		0x30
+#define HV7131_REG_ARCG		0x31
+#define HV7131_REG_AGCG		0x32
+#define HV7131_REG_ABCG		0x33
+#define HV7131_REG_APBV		0x34
+#define HV7131_REG_ASLP		0x54
+/* Offset Registers: */
+#define HV7131_REG_OFSR		0x50
+#define HV7131_REG_OFSG		0x51
+#define HV7131_REG_OFSB		0x52
+/* REset level statistics registers: */
+#define HV7131_REG_LOREFNOH	0x57
+#define HV7131_REG_LOREFNOL	0x58
+#define HV7131_REG_HIREFNOH	0x59
+#define HV7131_REG_HIREFNOL	0x5a
+
+/* se401 registers */
+#define SE401_OPERATINGMODE	0x2000
diff --git a/drivers/media/usb/gspca/sn9c2028.c b/drivers/media/usb/gspca/sn9c2028.c
new file mode 100644
index 0000000..4f2050a
--- /dev/null
+++ b/drivers/media/usb/gspca/sn9c2028.c
@@ -0,0 +1,974 @@
+/*
+ * SN9C2028 library
+ *
+ * Copyright (C) 2009 Theodore Kilgore <kilgota@auburn.edu>
+ *
+ * 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "sn9c2028"
+
+#include "gspca.h"
+
+MODULE_AUTHOR("Theodore Kilgore");
+MODULE_DESCRIPTION("Sonix SN9C2028 USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* specific webcam descriptor */
+struct sd {
+	struct gspca_dev gspca_dev;  /* !! must be the first item */
+	u8 sof_read;
+	u16 model;
+
+#define MIN_AVG_LUM 8500
+#define MAX_AVG_LUM 10000
+	int avg_lum;
+	u8 avg_lum_l;
+
+	struct { /* autogain and gain control cluster */
+		struct v4l2_ctrl *autogain;
+		struct v4l2_ctrl *gain;
+	};
+};
+
+struct init_command {
+	unsigned char instruction[6];
+	unsigned char to_read; /* length to read. 0 means no reply requested */
+};
+
+/* How to change the resolution of any of the VGA cams is unknown */
+static const struct v4l2_pix_format vga_mode[] = {
+	{640, 480, V4L2_PIX_FMT_SN9C2028, V4L2_FIELD_NONE,
+		.bytesperline = 640,
+		.sizeimage = 640 * 480 * 3 / 4,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 0},
+};
+
+/* No way to change the resolution of the CIF cams is known */
+static const struct v4l2_pix_format cif_mode[] = {
+	{352, 288, V4L2_PIX_FMT_SN9C2028, V4L2_FIELD_NONE,
+		.bytesperline = 352,
+		.sizeimage = 352 * 288 * 3 / 4,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 0},
+};
+
+/* the bytes to write are in gspca_dev->usb_buf */
+static int sn9c2028_command(struct gspca_dev *gspca_dev, u8 *command)
+{
+	int rc;
+
+	PDEBUG(D_USBO, "sending command %02x%02x%02x%02x%02x%02x", command[0],
+	       command[1], command[2], command[3], command[4], command[5]);
+
+	memcpy(gspca_dev->usb_buf, command, 6);
+	rc = usb_control_msg(gspca_dev->dev,
+			usb_sndctrlpipe(gspca_dev->dev, 0),
+			USB_REQ_GET_CONFIGURATION,
+			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+			2, 0, gspca_dev->usb_buf, 6, 500);
+	if (rc < 0) {
+		pr_err("command write [%02x] error %d\n",
+		       gspca_dev->usb_buf[0], rc);
+		return rc;
+	}
+
+	return 0;
+}
+
+static int sn9c2028_read1(struct gspca_dev *gspca_dev)
+{
+	int rc;
+
+	rc = usb_control_msg(gspca_dev->dev,
+			usb_rcvctrlpipe(gspca_dev->dev, 0),
+			USB_REQ_GET_STATUS,
+			USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+			1, 0, gspca_dev->usb_buf, 1, 500);
+	if (rc != 1) {
+		pr_err("read1 error %d\n", rc);
+		return (rc < 0) ? rc : -EIO;
+	}
+	PDEBUG(D_USBI, "read1 response %02x", gspca_dev->usb_buf[0]);
+	return gspca_dev->usb_buf[0];
+}
+
+static int sn9c2028_read4(struct gspca_dev *gspca_dev, u8 *reading)
+{
+	int rc;
+	rc = usb_control_msg(gspca_dev->dev,
+			usb_rcvctrlpipe(gspca_dev->dev, 0),
+			USB_REQ_GET_STATUS,
+			USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+			4, 0, gspca_dev->usb_buf, 4, 500);
+	if (rc != 4) {
+		pr_err("read4 error %d\n", rc);
+		return (rc < 0) ? rc : -EIO;
+	}
+	memcpy(reading, gspca_dev->usb_buf, 4);
+	PDEBUG(D_USBI, "read4 response %02x%02x%02x%02x", reading[0],
+	       reading[1], reading[2], reading[3]);
+	return rc;
+}
+
+static int sn9c2028_long_command(struct gspca_dev *gspca_dev, u8 *command)
+{
+	int i, status;
+	__u8 reading[4];
+
+	status = sn9c2028_command(gspca_dev, command);
+	if (status < 0)
+		return status;
+
+	status = -1;
+	for (i = 0; i < 256 && status < 2; i++)
+		status = sn9c2028_read1(gspca_dev);
+	if (status < 0) {
+		pr_err("long command status read error %d\n", status);
+		return status;
+	}
+
+	memset(reading, 0, 4);
+	status = sn9c2028_read4(gspca_dev, reading);
+	if (status < 0)
+		return status;
+
+	/* in general, the first byte of the response is the first byte of
+	 * the command, or'ed with 8 */
+	status = sn9c2028_read1(gspca_dev);
+	if (status < 0)
+		return status;
+
+	return 0;
+}
+
+static int sn9c2028_short_command(struct gspca_dev *gspca_dev, u8 *command)
+{
+	int err_code;
+
+	err_code = sn9c2028_command(gspca_dev, command);
+	if (err_code < 0)
+		return err_code;
+
+	err_code = sn9c2028_read1(gspca_dev);
+	if (err_code < 0)
+		return err_code;
+
+	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 = &gspca_dev->cam;
+
+	PDEBUG(D_PROBE, "SN9C2028 camera detected (vid/pid 0x%04X:0x%04X)",
+	       id->idVendor, id->idProduct);
+
+	sd->model = id->idProduct;
+
+	switch (sd->model) {
+	case 0x7005:
+		PDEBUG(D_PROBE, "Genius Smart 300 camera");
+		break;
+	case 0x7003:
+		PDEBUG(D_PROBE, "Genius Videocam Live v2");
+		break;
+	case 0x8000:
+		PDEBUG(D_PROBE, "DC31VC");
+		break;
+	case 0x8001:
+		PDEBUG(D_PROBE, "Spy camera");
+		break;
+	case 0x8003:
+		PDEBUG(D_PROBE, "CIF camera");
+		break;
+	case 0x8008:
+		PDEBUG(D_PROBE, "Mini-Shotz ms-350 camera");
+		break;
+	case 0x800a:
+		PDEBUG(D_PROBE, "Vivitar 3350b type camera");
+		cam->input_flags = V4L2_IN_ST_VFLIP | V4L2_IN_ST_HFLIP;
+		break;
+	}
+
+	switch (sd->model) {
+	case 0x8000:
+	case 0x8001:
+	case 0x8003:
+		cam->cam_mode = cif_mode;
+		cam->nmodes = ARRAY_SIZE(cif_mode);
+		break;
+	default:
+		cam->cam_mode = vga_mode;
+		cam->nmodes = ARRAY_SIZE(vga_mode);
+	}
+	return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+	int status = -1;
+
+	sn9c2028_read1(gspca_dev);
+	sn9c2028_read1(gspca_dev);
+	status = sn9c2028_read1(gspca_dev);
+
+	return (status < 0) ? status : 0;
+}
+
+static int run_start_commands(struct gspca_dev *gspca_dev,
+			      struct init_command *cam_commands, int n)
+{
+	int i, err_code = -1;
+
+	for (i = 0; i < n; i++) {
+		switch (cam_commands[i].to_read) {
+		case 4:
+			err_code = sn9c2028_long_command(gspca_dev,
+					cam_commands[i].instruction);
+			break;
+		case 1:
+			err_code = sn9c2028_short_command(gspca_dev,
+					cam_commands[i].instruction);
+			break;
+		case 0:
+			err_code = sn9c2028_command(gspca_dev,
+					cam_commands[i].instruction);
+			break;
+		}
+		if (err_code < 0)
+			return err_code;
+	}
+	return 0;
+}
+
+static void set_gain(struct gspca_dev *gspca_dev, s32 g)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	struct init_command genius_vcam_live_gain_cmds[] = {
+		{{0x1d, 0x25, 0x10 /* This byte is gain */,
+		  0x20, 0xab, 0x00}, 0},
+	};
+	if (!gspca_dev->streaming)
+		return;
+
+	switch (sd->model) {
+	case 0x7003:
+		genius_vcam_live_gain_cmds[0].instruction[2] = g;
+		run_start_commands(gspca_dev, genius_vcam_live_gain_cmds,
+				   ARRAY_SIZE(genius_vcam_live_gain_cmds));
+		break;
+	default:
+		break;
+	}
+}
+
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct gspca_dev *gspca_dev =
+		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+	struct sd *sd = (struct sd *)gspca_dev;
+
+	gspca_dev->usb_err = 0;
+
+	if (!gspca_dev->streaming)
+		return 0;
+
+	switch (ctrl->id) {
+	/* standalone gain control */
+	case V4L2_CID_GAIN:
+		set_gain(gspca_dev, ctrl->val);
+		break;
+	/* autogain */
+	case V4L2_CID_AUTOGAIN:
+		set_gain(gspca_dev, sd->gain->val);
+		break;
+	}
+	return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+	.s_ctrl = sd_s_ctrl,
+};
+
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+	struct sd *sd = (struct sd *)gspca_dev;
+
+	gspca_dev->vdev.ctrl_handler = hdl;
+	v4l2_ctrl_handler_init(hdl, 2);
+
+	switch (sd->model) {
+	case 0x7003:
+		sd->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_GAIN, 0, 20, 1, 0);
+		sd->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+static int start_spy_cam(struct gspca_dev *gspca_dev)
+{
+	struct init_command spy_start_commands[] = {
+		{{0x0c, 0x01, 0x00, 0x00, 0x00, 0x00}, 4},
+		{{0x13, 0x20, 0x01, 0x00, 0x00, 0x00}, 4},
+		{{0x13, 0x21, 0x01, 0x00, 0x00, 0x00}, 4},
+		{{0x13, 0x22, 0x01, 0x04, 0x00, 0x00}, 4},
+		{{0x13, 0x23, 0x01, 0x03, 0x00, 0x00}, 4},
+		{{0x13, 0x24, 0x01, 0x00, 0x00, 0x00}, 4},
+		{{0x13, 0x25, 0x01, 0x16, 0x00, 0x00}, 4}, /* width  352 */
+		{{0x13, 0x26, 0x01, 0x12, 0x00, 0x00}, 4}, /* height 288 */
+		/* {{0x13, 0x27, 0x01, 0x28, 0x00, 0x00}, 4}, */
+		{{0x13, 0x27, 0x01, 0x68, 0x00, 0x00}, 4},
+		{{0x13, 0x28, 0x01, 0x09, 0x00, 0x00}, 4}, /* red gain ?*/
+		/* {{0x13, 0x28, 0x01, 0x00, 0x00, 0x00}, 4}, */
+		{{0x13, 0x29, 0x01, 0x00, 0x00, 0x00}, 4},
+		/* {{0x13, 0x29, 0x01, 0x0c, 0x00, 0x00}, 4}, */
+		{{0x13, 0x2a, 0x01, 0x00, 0x00, 0x00}, 4},
+		{{0x13, 0x2b, 0x01, 0x00, 0x00, 0x00}, 4},
+		/* {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4}, */
+		{{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4},
+		{{0x13, 0x2d, 0x01, 0x02, 0x00, 0x00}, 4},
+		/* {{0x13, 0x2e, 0x01, 0x09, 0x00, 0x00}, 4}, */
+		{{0x13, 0x2e, 0x01, 0x09, 0x00, 0x00}, 4},
+		{{0x13, 0x2f, 0x01, 0x07, 0x00, 0x00}, 4},
+		{{0x12, 0x34, 0x01, 0x00, 0x00, 0x00}, 4},
+		{{0x13, 0x34, 0x01, 0xa1, 0x00, 0x00}, 4},
+		{{0x13, 0x35, 0x01, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x02, 0x06, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x03, 0x13, 0x00, 0x00, 0x00}, 4}, /*don't mess with*/
+		/*{{0x11, 0x04, 0x06, 0x00, 0x00, 0x00}, 4}, observed */
+		{{0x11, 0x04, 0x00, 0x00, 0x00, 0x00}, 4}, /* brighter */
+		/*{{0x11, 0x05, 0x65, 0x00, 0x00, 0x00}, 4}, observed */
+		{{0x11, 0x05, 0x00, 0x00, 0x00, 0x00}, 4}, /* brighter */
+		{{0x11, 0x06, 0xb1, 0x00, 0x00, 0x00}, 4}, /* observed */
+		{{0x11, 0x07, 0x00, 0x00, 0x00, 0x00}, 4},
+		/*{{0x11, 0x08, 0x06, 0x00, 0x00, 0x00}, 4}, observed */
+		{{0x11, 0x08, 0x0b, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x09, 0x01, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x0a, 0x01, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x0b, 0x01, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x0c, 0x01, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x0d, 0x00, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x0e, 0x04, 0x00, 0x00, 0x00}, 4},
+		/* {{0x11, 0x0f, 0x00, 0x00, 0x00, 0x00}, 4}, */
+		/* brightness or gain. 0 is default. 4 is good
+		 * indoors at night with incandescent lighting */
+		{{0x11, 0x0f, 0x04, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x10, 0x06, 0x00, 0x00, 0x00}, 4}, /*hstart or hoffs*/
+		{{0x11, 0x11, 0x06, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x12, 0x00, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x14, 0x02, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x13, 0x01, 0x00, 0x00, 0x00}, 4},
+		/* {{0x1b, 0x02, 0x06, 0x00, 0x00, 0x00}, 1}, observed */
+		{{0x1b, 0x02, 0x11, 0x00, 0x00, 0x00}, 1}, /* brighter */
+		/* {{0x1b, 0x13, 0x01, 0x00, 0x00, 0x00}, 1}, observed */
+		{{0x1b, 0x13, 0x11, 0x00, 0x00, 0x00}, 1},
+		{{0x20, 0x34, 0xa1, 0x00, 0x00, 0x00}, 1}, /* compresses */
+		/* Camera should start to capture now. */
+	};
+
+	return run_start_commands(gspca_dev, spy_start_commands,
+				  ARRAY_SIZE(spy_start_commands));
+}
+
+static int start_cif_cam(struct gspca_dev *gspca_dev)
+{
+	struct init_command cif_start_commands[] = {
+		{{0x0c, 0x01, 0x00, 0x00, 0x00, 0x00}, 4},
+		/* The entire sequence below seems redundant */
+		/* {{0x13, 0x20, 0x01, 0x00, 0x00, 0x00}, 4},
+		{{0x13, 0x21, 0x01, 0x00, 0x00, 0x00}, 4},
+		{{0x13, 0x22, 0x01, 0x06, 0x00, 0x00}, 4},
+		{{0x13, 0x23, 0x01, 0x02, 0x00, 0x00}, 4},
+		{{0x13, 0x24, 0x01, 0x00, 0x00, 0x00}, 4},
+		{{0x13, 0x25, 0x01, 0x16, 0x00, 0x00}, 4}, width?
+		{{0x13, 0x26, 0x01, 0x12, 0x00, 0x00}, 4}, height?
+		{{0x13, 0x27, 0x01, 0x68, 0x00, 0x00}, 4}, subsample?
+		{{0x13, 0x28, 0x01, 0x00, 0x00, 0x00}, 4},
+		{{0x13, 0x29, 0x01, 0x20, 0x00, 0x00}, 4},
+		{{0x13, 0x2a, 0x01, 0x00, 0x00, 0x00}, 4},
+		{{0x13, 0x2b, 0x01, 0x00, 0x00, 0x00}, 4},
+		{{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4},
+		{{0x13, 0x2d, 0x01, 0x03, 0x00, 0x00}, 4},
+		{{0x13, 0x2e, 0x01, 0x0f, 0x00, 0x00}, 4},
+		{{0x13, 0x2f, 0x01, 0x0c, 0x00, 0x00}, 4},
+		{{0x12, 0x34, 0x01, 0x00, 0x00, 0x00}, 4},
+		{{0x13, 0x34, 0x01, 0xa1, 0x00, 0x00}, 4},
+		{{0x13, 0x35, 0x01, 0x00, 0x00, 0x00}, 4},*/
+		{{0x1b, 0x21, 0x00, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x17, 0x00, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x19, 0x00, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x02, 0x06, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x03, 0x5a, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x04, 0x27, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x05, 0x01, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x12, 0x14, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x13, 0x00, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x14, 0x00, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x15, 0x00, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x16, 0x00, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x77, 0xa2, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x06, 0x0f, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x07, 0x14, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x08, 0x0f, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x09, 0x10, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x0e, 0x00, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x0f, 0x00, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x12, 0x07, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x10, 0x1f, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x11, 0x01, 0x00, 0x00, 0x00}, 1},
+		{{0x13, 0x25, 0x01, 0x16, 0x00, 0x00}, 1}, /* width/8 */
+		{{0x13, 0x26, 0x01, 0x12, 0x00, 0x00}, 1}, /* height/8 */
+		/* {{0x13, 0x27, 0x01, 0x68, 0x00, 0x00}, 4}, subsample?
+		 * {{0x13, 0x28, 0x01, 0x1e, 0x00, 0x00}, 4}, does nothing
+		 * {{0x13, 0x27, 0x01, 0x20, 0x00, 0x00}, 4}, */
+		/* {{0x13, 0x29, 0x01, 0x22, 0x00, 0x00}, 4},
+		 * causes subsampling
+		 * but not a change in the resolution setting! */
+		{{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4},
+		{{0x13, 0x2d, 0x01, 0x01, 0x00, 0x00}, 4},
+		{{0x13, 0x2e, 0x01, 0x08, 0x00, 0x00}, 4},
+		{{0x13, 0x2f, 0x01, 0x06, 0x00, 0x00}, 4},
+		{{0x13, 0x28, 0x01, 0x00, 0x00, 0x00}, 4},
+		{{0x1b, 0x04, 0x6d, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x05, 0x03, 0x00, 0x00, 0x00}, 1},
+		{{0x20, 0x36, 0x06, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x0e, 0x01, 0x00, 0x00, 0x00}, 1},
+		{{0x12, 0x27, 0x01, 0x00, 0x00, 0x00}, 4},
+		{{0x1b, 0x0f, 0x00, 0x00, 0x00, 0x00}, 1},
+		{{0x20, 0x36, 0x05, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x10, 0x0f, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x02, 0x06, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x11, 0x01, 0x00, 0x00, 0x00}, 1},
+		{{0x20, 0x34, 0xa1, 0x00, 0x00, 0x00}, 1},/* use compression */
+		/* Camera should start to capture now. */
+	};
+
+	return run_start_commands(gspca_dev, cif_start_commands,
+				  ARRAY_SIZE(cif_start_commands));
+}
+
+static int start_ms350_cam(struct gspca_dev *gspca_dev)
+{
+	struct init_command ms350_start_commands[] = {
+		{{0x0c, 0x01, 0x00, 0x00, 0x00, 0x00}, 4},
+		{{0x16, 0x01, 0x00, 0x00, 0x00, 0x00}, 4},
+		{{0x13, 0x20, 0x01, 0x00, 0x00, 0x00}, 4},
+		{{0x13, 0x21, 0x01, 0x00, 0x00, 0x00}, 4},
+		{{0x13, 0x22, 0x01, 0x04, 0x00, 0x00}, 4},
+		{{0x13, 0x23, 0x01, 0x03, 0x00, 0x00}, 4},
+		{{0x13, 0x24, 0x01, 0x00, 0x00, 0x00}, 4},
+		{{0x13, 0x25, 0x01, 0x16, 0x00, 0x00}, 4},
+		{{0x13, 0x26, 0x01, 0x12, 0x00, 0x00}, 4},
+		{{0x13, 0x27, 0x01, 0x28, 0x00, 0x00}, 4},
+		{{0x13, 0x28, 0x01, 0x09, 0x00, 0x00}, 4},
+		{{0x13, 0x29, 0x01, 0x00, 0x00, 0x00}, 4},
+		{{0x13, 0x2a, 0x01, 0x00, 0x00, 0x00}, 4},
+		{{0x13, 0x2b, 0x01, 0x00, 0x00, 0x00}, 4},
+		{{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4},
+		{{0x13, 0x2d, 0x01, 0x03, 0x00, 0x00}, 4},
+		{{0x13, 0x2e, 0x01, 0x0f, 0x00, 0x00}, 4},
+		{{0x13, 0x2f, 0x01, 0x0c, 0x00, 0x00}, 4},
+		{{0x12, 0x34, 0x01, 0x00, 0x00, 0x00}, 4},
+		{{0x13, 0x34, 0x01, 0xa1, 0x00, 0x00}, 4},
+		{{0x13, 0x35, 0x01, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x00, 0x01, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x01, 0x70, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x02, 0x05, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x03, 0x5d, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x04, 0x07, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x05, 0x25, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x06, 0x00, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x07, 0x09, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x08, 0x01, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x09, 0x00, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x0a, 0x00, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x0b, 0x01, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x0c, 0x00, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x0d, 0x0c, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x0e, 0x01, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x0f, 0x00, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x10, 0x00, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x11, 0x00, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x12, 0x00, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x13, 0x63, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x15, 0x70, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x18, 0x00, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x11, 0x01, 0x00, 0x00, 0x00}, 4},
+		{{0x13, 0x25, 0x01, 0x28, 0x00, 0x00}, 4}, /* width  */
+		{{0x13, 0x26, 0x01, 0x1e, 0x00, 0x00}, 4}, /* height */
+		{{0x13, 0x28, 0x01, 0x09, 0x00, 0x00}, 4}, /* vstart? */
+		{{0x13, 0x27, 0x01, 0x28, 0x00, 0x00}, 4},
+		{{0x13, 0x29, 0x01, 0x40, 0x00, 0x00}, 4}, /* hstart? */
+		{{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4},
+		{{0x13, 0x2d, 0x01, 0x03, 0x00, 0x00}, 4},
+		{{0x13, 0x2e, 0x01, 0x0f, 0x00, 0x00}, 4},
+		{{0x13, 0x2f, 0x01, 0x0c, 0x00, 0x00}, 4},
+		{{0x1b, 0x02, 0x05, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x11, 0x01, 0x00, 0x00, 0x00}, 1},
+		{{0x20, 0x18, 0x00, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x02, 0x0a, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x11, 0x01, 0x00, 0x00, 0x00}, 0},
+		/* Camera should start to capture now. */
+	};
+
+	return run_start_commands(gspca_dev, ms350_start_commands,
+				  ARRAY_SIZE(ms350_start_commands));
+}
+
+static int start_genius_cam(struct gspca_dev *gspca_dev)
+{
+	struct init_command genius_start_commands[] = {
+		{{0x0c, 0x01, 0x00, 0x00, 0x00, 0x00}, 4},
+		{{0x16, 0x01, 0x00, 0x00, 0x00, 0x00}, 4},
+		{{0x10, 0x00, 0x00, 0x00, 0x00, 0x00}, 4},
+		{{0x13, 0x25, 0x01, 0x16, 0x00, 0x00}, 4},
+		{{0x13, 0x26, 0x01, 0x12, 0x00, 0x00}, 4},
+		/* "preliminary" width and height settings */
+		{{0x13, 0x28, 0x01, 0x0e, 0x00, 0x00}, 4},
+		{{0x13, 0x27, 0x01, 0x20, 0x00, 0x00}, 4},
+		{{0x13, 0x29, 0x01, 0x22, 0x00, 0x00}, 4},
+		{{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4},
+		{{0x13, 0x2d, 0x01, 0x02, 0x00, 0x00}, 4},
+		{{0x13, 0x2e, 0x01, 0x09, 0x00, 0x00}, 4},
+		{{0x13, 0x2f, 0x01, 0x07, 0x00, 0x00}, 4},
+		{{0x11, 0x20, 0x00, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x21, 0x2d, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x22, 0x00, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x23, 0x03, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x10, 0x00, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x11, 0x64, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x12, 0x00, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x13, 0x91, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x14, 0x01, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x15, 0x20, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x16, 0x01, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x17, 0x60, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x20, 0x00, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x21, 0x2d, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x22, 0x00, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x23, 0x03, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x25, 0x00, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x26, 0x02, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x27, 0x88, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x30, 0x38, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x31, 0x2a, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x32, 0x2a, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x33, 0x2a, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x34, 0x02, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x5b, 0x0a, 0x00, 0x00, 0x00}, 4},
+		{{0x13, 0x25, 0x01, 0x28, 0x00, 0x00}, 4}, /* real width */
+		{{0x13, 0x26, 0x01, 0x1e, 0x00, 0x00}, 4}, /* real height */
+		{{0x13, 0x28, 0x01, 0x0e, 0x00, 0x00}, 4},
+		{{0x13, 0x27, 0x01, 0x20, 0x00, 0x00}, 4},
+		{{0x13, 0x29, 0x01, 0x62, 0x00, 0x00}, 4},
+		{{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4},
+		{{0x13, 0x2d, 0x01, 0x03, 0x00, 0x00}, 4},
+		{{0x13, 0x2e, 0x01, 0x0f, 0x00, 0x00}, 4},
+		{{0x13, 0x2f, 0x01, 0x0c, 0x00, 0x00}, 4},
+		{{0x11, 0x20, 0x00, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x21, 0x2a, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x22, 0x00, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x23, 0x28, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x10, 0x00, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x11, 0x04, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x12, 0x00, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x13, 0x03, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x14, 0x01, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x15, 0xe0, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x16, 0x02, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x17, 0x80, 0x00, 0x00, 0x00}, 4},
+		{{0x1c, 0x20, 0x00, 0x2a, 0x00, 0x00}, 1},
+		{{0x1c, 0x20, 0x00, 0x2a, 0x00, 0x00}, 1},
+		{{0x20, 0x34, 0xa1, 0x00, 0x00, 0x00}, 0}
+		/* Camera should start to capture now. */
+	};
+
+	return run_start_commands(gspca_dev, genius_start_commands,
+				  ARRAY_SIZE(genius_start_commands));
+}
+
+static int start_genius_videocam_live(struct gspca_dev *gspca_dev)
+{
+	int r;
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct init_command genius_vcam_live_start_commands[] = {
+		{{0x0c, 0x01, 0x00, 0x00, 0x00, 0x00}, 0},
+		{{0x16, 0x01, 0x00, 0x00, 0x00, 0x00}, 4},
+		{{0x10, 0x00, 0x00, 0x00, 0x00, 0x00}, 4},
+		{{0x13, 0x25, 0x01, 0x16, 0x00, 0x00}, 4},
+		{{0x13, 0x26, 0x01, 0x12, 0x00, 0x00}, 4},
+
+		{{0x13, 0x28, 0x01, 0x0e, 0x00, 0x00}, 4},
+		{{0x13, 0x27, 0x01, 0x20, 0x00, 0x00}, 4},
+		{{0x13, 0x29, 0x01, 0x22, 0x00, 0x00}, 4},
+		{{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4},
+		{{0x13, 0x2d, 0x01, 0x02, 0x00, 0x00}, 4},
+		{{0x13, 0x2e, 0x01, 0x09, 0x00, 0x00}, 4},
+		{{0x13, 0x2f, 0x01, 0x07, 0x00, 0x00}, 4},
+		{{0x11, 0x20, 0x00, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x21, 0x2d, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x22, 0x00, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x23, 0x03, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x10, 0x00, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x11, 0x64, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x12, 0x00, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x13, 0x91, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x14, 0x01, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x15, 0x20, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x16, 0x01, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x17, 0x60, 0x00, 0x00, 0x00}, 4},
+		{{0x1c, 0x20, 0x00, 0x2d, 0x00, 0x00}, 4},
+		{{0x13, 0x20, 0x01, 0x00, 0x00, 0x00}, 4},
+		{{0x13, 0x21, 0x01, 0x00, 0x00, 0x00}, 4},
+		{{0x13, 0x22, 0x01, 0x00, 0x00, 0x00}, 4},
+		{{0x13, 0x23, 0x01, 0x01, 0x00, 0x00}, 4},
+		{{0x13, 0x24, 0x01, 0x00, 0x00, 0x00}, 4},
+		{{0x13, 0x25, 0x01, 0x16, 0x00, 0x00}, 4},
+		{{0x13, 0x26, 0x01, 0x12, 0x00, 0x00}, 4},
+		{{0x13, 0x27, 0x01, 0x20, 0x00, 0x00}, 4},
+		{{0x13, 0x28, 0x01, 0x0e, 0x00, 0x00}, 4},
+		{{0x13, 0x29, 0x01, 0x22, 0x00, 0x00}, 4},
+		{{0x13, 0x2a, 0x01, 0x00, 0x00, 0x00}, 4},
+		{{0x13, 0x2b, 0x01, 0x00, 0x00, 0x00}, 4},
+		{{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4},
+		{{0x13, 0x2d, 0x01, 0x02, 0x00, 0x00}, 4},
+		{{0x13, 0x2e, 0x01, 0x09, 0x00, 0x00}, 4},
+		{{0x13, 0x2f, 0x01, 0x07, 0x00, 0x00}, 4},
+		{{0x12, 0x34, 0x01, 0x00, 0x00, 0x00}, 4},
+		{{0x13, 0x34, 0x01, 0xa1, 0x00, 0x00}, 4},
+		{{0x13, 0x35, 0x01, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x01, 0x04, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x02, 0x92, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x10, 0x00, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x11, 0x64, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x12, 0x00, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x13, 0x91, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x14, 0x01, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x15, 0x20, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x16, 0x01, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x17, 0x60, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x20, 0x00, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x21, 0x2d, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x22, 0x00, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x23, 0x03, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x25, 0x00, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x26, 0x02, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x27, 0x88, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x30, 0x38, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x31, 0x2a, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x32, 0x2a, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x33, 0x2a, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x34, 0x02, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x5b, 0x0a, 0x00, 0x00, 0x00}, 4},
+		{{0x13, 0x25, 0x01, 0x28, 0x00, 0x00}, 4},
+		{{0x13, 0x26, 0x01, 0x1e, 0x00, 0x00}, 4},
+		{{0x13, 0x28, 0x01, 0x0e, 0x00, 0x00}, 4},
+		{{0x13, 0x27, 0x01, 0x20, 0x00, 0x00}, 4},
+		{{0x13, 0x29, 0x01, 0x62, 0x00, 0x00}, 4},
+		{{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4},
+		{{0x13, 0x2d, 0x01, 0x03, 0x00, 0x00}, 4},
+		{{0x13, 0x2e, 0x01, 0x0f, 0x00, 0x00}, 4},
+		{{0x13, 0x2f, 0x01, 0x0c, 0x00, 0x00}, 4},
+		{{0x11, 0x20, 0x00, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x21, 0x2a, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x22, 0x00, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x23, 0x28, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x10, 0x00, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x11, 0x04, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x12, 0x00, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x13, 0x03, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x14, 0x01, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x15, 0xe0, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x16, 0x02, 0x00, 0x00, 0x00}, 4},
+		{{0x11, 0x17, 0x80, 0x00, 0x00, 0x00}, 4},
+		{{0x1c, 0x20, 0x00, 0x2a, 0x00, 0x00}, 1},
+		{{0x20, 0x34, 0xa1, 0x00, 0x00, 0x00}, 0},
+		/* Camera should start to capture now. */
+		{{0x12, 0x27, 0x01, 0x00, 0x00, 0x00}, 0},
+		{{0x1b, 0x32, 0x26, 0x00, 0x00, 0x00}, 0},
+		{{0x1d, 0x25, 0x10, 0x20, 0xab, 0x00}, 0},
+	};
+
+	r = run_start_commands(gspca_dev, genius_vcam_live_start_commands,
+				  ARRAY_SIZE(genius_vcam_live_start_commands));
+	if (r < 0)
+		return r;
+
+	if (sd->gain)
+		set_gain(gspca_dev, v4l2_ctrl_g_ctrl(sd->gain));
+
+	return r;
+}
+
+static int start_vivitar_cam(struct gspca_dev *gspca_dev)
+{
+	struct init_command vivitar_start_commands[] = {
+		{{0x0c, 0x01, 0x00, 0x00, 0x00, 0x00}, 4},
+		{{0x13, 0x20, 0x01, 0x00, 0x00, 0x00}, 4},
+		{{0x13, 0x21, 0x01, 0x00, 0x00, 0x00}, 4},
+		{{0x13, 0x22, 0x01, 0x01, 0x00, 0x00}, 4},
+		{{0x13, 0x23, 0x01, 0x01, 0x00, 0x00}, 4},
+		{{0x13, 0x24, 0x01, 0x00, 0x00, 0x00}, 4},
+		{{0x13, 0x25, 0x01, 0x28, 0x00, 0x00}, 4},
+		{{0x13, 0x26, 0x01, 0x1e, 0x00, 0x00}, 4},
+		{{0x13, 0x27, 0x01, 0x20, 0x00, 0x00}, 4},
+		{{0x13, 0x28, 0x01, 0x0a, 0x00, 0x00}, 4},
+		/*
+		 * Above is changed from OEM 0x0b. Fixes Bayer tiling.
+		 * Presumably gives a vertical shift of one row.
+		 */
+		{{0x13, 0x29, 0x01, 0x20, 0x00, 0x00}, 4},
+		/* Above seems to do horizontal shift. */
+		{{0x13, 0x2a, 0x01, 0x00, 0x00, 0x00}, 4},
+		{{0x13, 0x2b, 0x01, 0x00, 0x00, 0x00}, 4},
+		{{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4},
+		{{0x13, 0x2d, 0x01, 0x03, 0x00, 0x00}, 4},
+		{{0x13, 0x2e, 0x01, 0x0f, 0x00, 0x00}, 4},
+		{{0x13, 0x2f, 0x01, 0x0c, 0x00, 0x00}, 4},
+		/* Above three commands seem to relate to brightness. */
+		{{0x12, 0x34, 0x01, 0x00, 0x00, 0x00}, 4},
+		{{0x13, 0x34, 0x01, 0xa1, 0x00, 0x00}, 4},
+		{{0x13, 0x35, 0x01, 0x00, 0x00, 0x00}, 4},
+		{{0x1b, 0x12, 0x80, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x01, 0x77, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x02, 0x3a, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x12, 0x78, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x13, 0x00, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x14, 0x80, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x15, 0x34, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x1b, 0x04, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x20, 0x44, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x23, 0xee, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x26, 0xa0, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x27, 0x9a, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x28, 0xa0, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x29, 0x30, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x2a, 0x80, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x2b, 0x00, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x2f, 0x3d, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x30, 0x24, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x32, 0x86, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x60, 0xa9, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x61, 0x42, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x65, 0x00, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x69, 0x38, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x6f, 0x88, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x70, 0x0b, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x71, 0x00, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x74, 0x21, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x75, 0x86, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x76, 0x00, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x7d, 0xf3, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x17, 0x1c, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x18, 0xc0, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x19, 0x05, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x1a, 0xf6, 0x00, 0x00, 0x00}, 1},
+		/* {{0x13, 0x25, 0x01, 0x28, 0x00, 0x00}, 4},
+		{{0x13, 0x26, 0x01, 0x1e, 0x00, 0x00}, 4},
+		{{0x13, 0x28, 0x01, 0x0b, 0x00, 0x00}, 4}, */
+		{{0x20, 0x36, 0x06, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x10, 0x26, 0x00, 0x00, 0x00}, 1},
+		{{0x12, 0x27, 0x01, 0x00, 0x00, 0x00}, 4},
+		{{0x1b, 0x76, 0x03, 0x00, 0x00, 0x00}, 1},
+		{{0x20, 0x36, 0x05, 0x00, 0x00, 0x00}, 1},
+		{{0x1b, 0x00, 0x3f, 0x00, 0x00, 0x00}, 1},
+		/* Above is brightness; OEM driver setting is 0x10 */
+		{{0x12, 0x27, 0x01, 0x00, 0x00, 0x00}, 4},
+		{{0x20, 0x29, 0x30, 0x00, 0x00, 0x00}, 1},
+		{{0x20, 0x34, 0xa1, 0x00, 0x00, 0x00}, 1}
+	};
+
+	return run_start_commands(gspca_dev, vivitar_start_commands,
+				  ARRAY_SIZE(vivitar_start_commands));
+}
+
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int err_code;
+
+	sd->sof_read = 0;
+
+	switch (sd->model) {
+	case 0x7005:
+		err_code = start_genius_cam(gspca_dev);
+		break;
+	case 0x7003:
+		err_code = start_genius_videocam_live(gspca_dev);
+		break;
+	case 0x8001:
+		err_code = start_spy_cam(gspca_dev);
+		break;
+	case 0x8003:
+		err_code = start_cif_cam(gspca_dev);
+		break;
+	case 0x8008:
+		err_code = start_ms350_cam(gspca_dev);
+		break;
+	case 0x800a:
+		err_code = start_vivitar_cam(gspca_dev);
+		break;
+	default:
+		pr_err("Starting unknown camera, please report this\n");
+		return -ENXIO;
+	}
+
+	sd->avg_lum = -1;
+
+	return err_code;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+	int result;
+	__u8 data[6];
+
+	result = sn9c2028_read1(gspca_dev);
+	if (result < 0)
+		PERR("Camera Stop read failed");
+
+	memset(data, 0, 6);
+	data[0] = 0x14;
+	result = sn9c2028_command(gspca_dev, data);
+	if (result < 0)
+		PERR("Camera Stop command failed");
+}
+
+static void do_autogain(struct gspca_dev *gspca_dev, int avg_lum)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	s32 cur_gain = v4l2_ctrl_g_ctrl(sd->gain);
+
+	if (avg_lum == -1)
+		return;
+
+	if (avg_lum < MIN_AVG_LUM) {
+		if (cur_gain == sd->gain->maximum)
+			return;
+		cur_gain++;
+		v4l2_ctrl_s_ctrl(sd->gain, cur_gain);
+	}
+	if (avg_lum > MAX_AVG_LUM) {
+		if (cur_gain == sd->gain->minimum)
+			return;
+		cur_gain--;
+		v4l2_ctrl_s_ctrl(sd->gain, cur_gain);
+	}
+
+}
+
+static void sd_dqcallback(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (sd->autogain == NULL || !v4l2_ctrl_g_ctrl(sd->autogain))
+		return;
+
+	do_autogain(gspca_dev, sd->avg_lum);
+}
+
+/* Include sn9c2028 sof detection functions */
+#include "sn9c2028.h"
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+			__u8 *data,			/* isoc packet */
+			int len)			/* iso packet length */
+{
+	unsigned char *sof;
+
+	sof = sn9c2028_find_sof(gspca_dev, data, len);
+	if (sof) {
+		int n;
+
+		/* finish decoding current frame */
+		n = sof - data;
+		if (n > sizeof sn9c2028_sof_marker)
+			n -= sizeof sn9c2028_sof_marker;
+		else
+			n = 0;
+		gspca_frame_add(gspca_dev, LAST_PACKET, data, n);
+		/* Start next frame. */
+		gspca_frame_add(gspca_dev, FIRST_PACKET,
+			sn9c2028_sof_marker, sizeof sn9c2028_sof_marker);
+		len -= sof - data;
+		data = sof;
+	}
+	gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+	.name = MODULE_NAME,
+	.config = sd_config,
+	.init = sd_init,
+	.init_controls = sd_init_controls,
+	.start = sd_start,
+	.stopN = sd_stopN,
+	.dq_callback = sd_dqcallback,
+	.pkt_scan = sd_pkt_scan,
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+	{USB_DEVICE(0x0458, 0x7005)}, /* Genius Smart 300, version 2 */
+	{USB_DEVICE(0x0458, 0x7003)}, /* Genius Videocam Live v2  */
+	/* The Genius Smart is untested. I can't find an owner ! */
+	/* {USB_DEVICE(0x0c45, 0x8000)}, DC31VC, Don't know this camera */
+	{USB_DEVICE(0x0c45, 0x8001)}, /* Wild Planet digital spy cam */
+	{USB_DEVICE(0x0c45, 0x8003)}, /* Several small CIF cameras */
+	/* {USB_DEVICE(0x0c45, 0x8006)}, Unknown VGA camera */
+	{USB_DEVICE(0x0c45, 0x8008)}, /* Mini-Shotz ms-350 */
+	{USB_DEVICE(0x0c45, 0x800a)}, /* Vivicam 3350B */
+	{}
+};
+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,
+#ifdef CONFIG_PM
+	.suspend = gspca_suspend,
+	.resume = gspca_resume,
+	.reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/usb/gspca/sn9c2028.h b/drivers/media/usb/gspca/sn9c2028.h
new file mode 100644
index 0000000..f85bc10
--- /dev/null
+++ b/drivers/media/usb/gspca/sn9c2028.h
@@ -0,0 +1,63 @@
+/*
+ * SN9C2028 common functions
+ *
+ * Copyright (C) 2009 Theodore Kilgore <kilgota@auburn,edu>
+ *
+ * Based closely upon the file gspca/pac_common.h
+ *
+ * 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
+ *
+ */
+
+static const unsigned char sn9c2028_sof_marker[] = {
+	0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96,
+	0x00,
+	0x00, /* seq */
+	0x00,
+	0x00,
+	0x00, /* avg luminance lower 8 bit */
+	0x00, /* avg luminance higher 8 bit */
+};
+
+static unsigned char *sn9c2028_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] == sn9c2028_sof_marker[sd->sof_read]) ||
+		    (sd->sof_read > 5)) {
+			sd->sof_read++;
+			if (sd->sof_read == 11)
+				sd->avg_lum_l = m[i];
+			if (sd->sof_read == 12)
+				sd->avg_lum = (m[i] << 8) + sd->avg_lum_l;
+			if (sd->sof_read == sizeof(sn9c2028_sof_marker)) {
+				PDEBUG(D_FRAM,
+					"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;
+}
diff --git a/drivers/media/usb/gspca/sn9c20x.c b/drivers/media/usb/gspca/sn9c20x.c
new file mode 100644
index 0000000..d0ee899
--- /dev/null
+++ b/drivers/media/usb/gspca/sn9c20x.c
@@ -0,0 +1,2398 @@
+/*
+ *	Sonix sn9c201 sn9c202 library
+ *
+ * Copyright (C) 2012 Jean-Francois Moine <http://moinejf.free.fr>
+ *	Copyright (C) 2008-2009 microdia project <microdia@googlegroups.com>
+ *	Copyright (C) 2009 Brian Johnson <brijohn@gmail.com>
+ *
+ * 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/input.h>
+
+#include "gspca.h"
+#include "jpeg.h"
+
+#include <linux/dmi.h>
+
+MODULE_AUTHOR("Brian Johnson <brijohn@gmail.com>, "
+		"microdia project <microdia@googlegroups.com>");
+MODULE_DESCRIPTION("GSPCA/SN9C20X USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/*
+ * Pixel format private data
+ */
+#define SCALE_MASK	0x0f
+#define SCALE_160x120	0
+#define SCALE_320x240	1
+#define SCALE_640x480	2
+#define SCALE_1280x1024	3
+#define MODE_RAW	0x10
+#define MODE_JPEG	0x20
+#define MODE_SXGA	0x80
+
+#define SENSOR_OV9650	0
+#define SENSOR_OV9655	1
+#define SENSOR_SOI968	2
+#define SENSOR_OV7660	3
+#define SENSOR_OV7670	4
+#define SENSOR_MT9V011	5
+#define SENSOR_MT9V111	6
+#define SENSOR_MT9V112	7
+#define SENSOR_MT9M001	8
+#define SENSOR_MT9M111	9
+#define SENSOR_MT9M112  10
+#define SENSOR_HV7131R	11
+#define SENSOR_MT9VPRB	12
+
+/* camera flags */
+#define HAS_NO_BUTTON	0x1
+#define LED_REVERSE	0x2 /* some cameras unset gpio to turn on leds */
+#define FLIP_DETECT	0x4
+
+/* specific webcam descriptor */
+struct sd {
+	struct gspca_dev gspca_dev;
+
+	struct { /* color control cluster */
+		struct v4l2_ctrl *brightness;
+		struct v4l2_ctrl *contrast;
+		struct v4l2_ctrl *saturation;
+		struct v4l2_ctrl *hue;
+	};
+	struct { /* blue/red balance control cluster */
+		struct v4l2_ctrl *blue;
+		struct v4l2_ctrl *red;
+	};
+	struct { /* h/vflip control cluster */
+		struct v4l2_ctrl *hflip;
+		struct v4l2_ctrl *vflip;
+	};
+	struct v4l2_ctrl *gamma;
+	struct { /* autogain and exposure or gain control cluster */
+		struct v4l2_ctrl *autogain;
+		struct v4l2_ctrl *exposure;
+		struct v4l2_ctrl *gain;
+	};
+	struct v4l2_ctrl *jpegqual;
+
+	struct work_struct work;
+	struct workqueue_struct *work_thread;
+
+	u32 pktsz;			/* (used by pkt_scan) */
+	u16 npkt;
+	s8 nchg;
+	u8 fmt;				/* (used for JPEG QTAB update */
+
+#define MIN_AVG_LUM 80
+#define MAX_AVG_LUM 130
+	atomic_t avg_lum;
+	u8 old_step;
+	u8 older_step;
+	u8 exposure_step;
+
+	u8 i2c_addr;
+	u8 i2c_intf;
+	u8 sensor;
+	u8 hstart;
+	u8 vstart;
+
+	u8 jpeg_hdr[JPEG_HDR_SZ];
+
+	u8 flags;
+};
+
+static void qual_upd(struct work_struct *work);
+
+struct i2c_reg_u8 {
+	u8 reg;
+	u8 val;
+};
+
+struct i2c_reg_u16 {
+	u8 reg;
+	u16 val;
+};
+
+static const struct dmi_system_id flip_dmi_table[] = {
+	{
+		.ident = "MSI MS-1034",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "MICRO-STAR INT'L CO.,LTD."),
+			DMI_MATCH(DMI_PRODUCT_NAME, "MS-1034"),
+			DMI_MATCH(DMI_PRODUCT_VERSION, "0341")
+		}
+	},
+	{
+		.ident = "MSI MS-1632",
+		.matches = {
+			DMI_MATCH(DMI_BOARD_VENDOR, "MSI"),
+			DMI_MATCH(DMI_BOARD_NAME, "MS-1632")
+		}
+	},
+	{
+		.ident = "MSI MS-1633X",
+		.matches = {
+			DMI_MATCH(DMI_BOARD_VENDOR, "MSI"),
+			DMI_MATCH(DMI_BOARD_NAME, "MS-1633X")
+		}
+	},
+	{
+		.ident = "MSI MS-1635X",
+		.matches = {
+			DMI_MATCH(DMI_BOARD_VENDOR, "MSI"),
+			DMI_MATCH(DMI_BOARD_NAME, "MS-1635X")
+		}
+	},
+	{
+		.ident = "ASUSTeK W7J",
+		.matches = {
+			DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer Inc."),
+			DMI_MATCH(DMI_BOARD_NAME, "W7J       ")
+		}
+	},
+	{}
+};
+
+static const struct v4l2_pix_format vga_mode[] = {
+	{160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+		.bytesperline = 160,
+		.sizeimage = 160 * 120 * 4 / 8 + 590,
+		.colorspace = V4L2_COLORSPACE_JPEG,
+		.priv = SCALE_160x120 | MODE_JPEG},
+	{160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+		.bytesperline = 160,
+		.sizeimage = 160 * 120,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = SCALE_160x120 | MODE_RAW},
+	{160, 120, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE,
+		.bytesperline = 160,
+		.sizeimage = 240 * 120,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = SCALE_160x120},
+	{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+		.bytesperline = 320,
+		.sizeimage = 320 * 240 * 4 / 8 + 590,
+		.colorspace = V4L2_COLORSPACE_JPEG,
+		.priv = SCALE_320x240 | MODE_JPEG},
+	{320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+		.bytesperline = 320,
+		.sizeimage = 320 * 240 ,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = SCALE_320x240 | MODE_RAW},
+	{320, 240, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE,
+		.bytesperline = 320,
+		.sizeimage = 480 * 240 ,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = SCALE_320x240},
+	{640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+		.bytesperline = 640,
+		.sizeimage = 640 * 480 * 4 / 8 + 590,
+		.colorspace = V4L2_COLORSPACE_JPEG,
+		.priv = SCALE_640x480 | MODE_JPEG},
+	{640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+		.bytesperline = 640,
+		.sizeimage = 640 * 480,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = SCALE_640x480 | MODE_RAW},
+	{640, 480, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE,
+		.bytesperline = 640,
+		.sizeimage = 960 * 480,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = SCALE_640x480},
+};
+
+static const struct v4l2_pix_format sxga_mode[] = {
+	{160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+		.bytesperline = 160,
+		.sizeimage = 160 * 120 * 4 / 8 + 590,
+		.colorspace = V4L2_COLORSPACE_JPEG,
+		.priv = SCALE_160x120 | MODE_JPEG},
+	{160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+		.bytesperline = 160,
+		.sizeimage = 160 * 120,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = SCALE_160x120 | MODE_RAW},
+	{160, 120, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE,
+		.bytesperline = 160,
+		.sizeimage = 240 * 120,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = SCALE_160x120},
+	{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+		.bytesperline = 320,
+		.sizeimage = 320 * 240 * 4 / 8 + 590,
+		.colorspace = V4L2_COLORSPACE_JPEG,
+		.priv = SCALE_320x240 | MODE_JPEG},
+	{320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+		.bytesperline = 320,
+		.sizeimage = 320 * 240 ,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = SCALE_320x240 | MODE_RAW},
+	{320, 240, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE,
+		.bytesperline = 320,
+		.sizeimage = 480 * 240 ,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = SCALE_320x240},
+	{640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+		.bytesperline = 640,
+		.sizeimage = 640 * 480 * 4 / 8 + 590,
+		.colorspace = V4L2_COLORSPACE_JPEG,
+		.priv = SCALE_640x480 | MODE_JPEG},
+	{640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+		.bytesperline = 640,
+		.sizeimage = 640 * 480,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = SCALE_640x480 | MODE_RAW},
+	{640, 480, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE,
+		.bytesperline = 640,
+		.sizeimage = 960 * 480,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = SCALE_640x480},
+	{1280, 1024, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+		.bytesperline = 1280,
+		.sizeimage = 1280 * 1024,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = SCALE_1280x1024 | MODE_RAW | MODE_SXGA},
+};
+
+static const struct v4l2_pix_format mono_mode[] = {
+	{160, 120, V4L2_PIX_FMT_GREY, V4L2_FIELD_NONE,
+		.bytesperline = 160,
+		.sizeimage = 160 * 120,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = SCALE_160x120 | MODE_RAW},
+	{320, 240, V4L2_PIX_FMT_GREY, V4L2_FIELD_NONE,
+		.bytesperline = 320,
+		.sizeimage = 320 * 240 ,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = SCALE_320x240 | MODE_RAW},
+	{640, 480, V4L2_PIX_FMT_GREY, V4L2_FIELD_NONE,
+		.bytesperline = 640,
+		.sizeimage = 640 * 480,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = SCALE_640x480 | MODE_RAW},
+	{1280, 1024, V4L2_PIX_FMT_GREY, V4L2_FIELD_NONE,
+		.bytesperline = 1280,
+		.sizeimage = 1280 * 1024,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = SCALE_1280x1024 | MODE_RAW | MODE_SXGA},
+};
+
+static const s16 hsv_red_x[] = {
+	41,  44,  46,  48,  50,  52,  54,  56,
+	58,  60,  62,  64,  66,  68,  70,  72,
+	74,  76,  78,  80,  81,  83,  85,  87,
+	88,  90,  92,  93,  95,  97,  98, 100,
+	101, 102, 104, 105, 107, 108, 109, 110,
+	112, 113, 114, 115, 116, 117, 118, 119,
+	120, 121, 122, 123, 123, 124, 125, 125,
+	126, 127, 127, 128, 128, 129, 129, 129,
+	130, 130, 130, 130, 131, 131, 131, 131,
+	131, 131, 131, 131, 130, 130, 130, 130,
+	129, 129, 129, 128, 128, 127, 127, 126,
+	125, 125, 124, 123, 122, 122, 121, 120,
+	119, 118, 117, 116, 115, 114, 112, 111,
+	110, 109, 107, 106, 105, 103, 102, 101,
+	99,  98,  96,  94,  93,  91,  90,  88,
+	86,  84,  83,  81,  79,  77,  75,  74,
+	72,  70,  68,  66,  64,  62,  60,  58,
+	56,  54,  52,  49,  47,  45,  43,  41,
+	39,  36,  34,  32,  30,  28,  25,  23,
+	21,  19,  16,  14,  12,   9,   7,   5,
+	3,   0,  -1,  -3,  -6,  -8, -10, -12,
+	-15, -17, -19, -22, -24, -26, -28, -30,
+	-33, -35, -37, -39, -41, -44, -46, -48,
+	-50, -52, -54, -56, -58, -60, -62, -64,
+	-66, -68, -70, -72, -74, -76, -78, -80,
+	-81, -83, -85, -87, -88, -90, -92, -93,
+	-95, -97, -98, -100, -101, -102, -104, -105,
+	-107, -108, -109, -110, -112, -113, -114, -115,
+	-116, -117, -118, -119, -120, -121, -122, -123,
+	-123, -124, -125, -125, -126, -127, -127, -128,
+	-128, -128, -128, -128, -128, -128, -128, -128,
+	-128, -128, -128, -128, -128, -128, -128, -128,
+	-128, -128, -128, -128, -128, -128, -128, -128,
+	-128, -127, -127, -126, -125, -125, -124, -123,
+	-122, -122, -121, -120, -119, -118, -117, -116,
+	-115, -114, -112, -111, -110, -109, -107, -106,
+	-105, -103, -102, -101, -99, -98, -96, -94,
+	-93, -91, -90, -88, -86, -84, -83, -81,
+	-79, -77, -75, -74, -72, -70, -68, -66,
+	-64, -62, -60, -58, -56, -54, -52, -49,
+	-47, -45, -43, -41, -39, -36, -34, -32,
+	-30, -28, -25, -23, -21, -19, -16, -14,
+	-12,  -9,  -7,  -5,  -3,   0,   1,   3,
+	6,   8,  10,  12,  15,  17,  19,  22,
+	24,  26,  28,  30,  33,  35,  37,  39, 41
+};
+
+static const s16 hsv_red_y[] = {
+	82,  80,  78,  76,  74,  73,  71,  69,
+	67,  65,  63,  61,  58,  56,  54,  52,
+	50,  48,  46,  44,  41,  39,  37,  35,
+	32,  30,  28,  26,  23,  21,  19,  16,
+	14,  12,  10,   7,   5,   3,   0,  -1,
+	-3,  -6,  -8, -10, -13, -15, -17, -19,
+	-22, -24, -26, -29, -31, -33, -35, -38,
+	-40, -42, -44, -46, -48, -51, -53, -55,
+	-57, -59, -61, -63, -65, -67, -69, -71,
+	-73, -75, -77, -79, -81, -82, -84, -86,
+	-88, -89, -91, -93, -94, -96, -98, -99,
+	-101, -102, -104, -105, -106, -108, -109, -110,
+	-112, -113, -114, -115, -116, -117, -119, -120,
+	-120, -121, -122, -123, -124, -125, -126, -126,
+	-127, -128, -128, -128, -128, -128, -128, -128,
+	-128, -128, -128, -128, -128, -128, -128, -128,
+	-128, -128, -128, -128, -128, -128, -128, -128,
+	-128, -128, -128, -128, -128, -128, -128, -128,
+	-127, -127, -126, -125, -125, -124, -123, -122,
+	-121, -120, -119, -118, -117, -116, -115, -114,
+	-113, -111, -110, -109, -107, -106, -105, -103,
+	-102, -100, -99, -97, -96, -94, -92, -91,
+	-89, -87, -85, -84, -82, -80, -78, -76,
+	-74, -73, -71, -69, -67, -65, -63, -61,
+	-58, -56, -54, -52, -50, -48, -46, -44,
+	-41, -39, -37, -35, -32, -30, -28, -26,
+	-23, -21, -19, -16, -14, -12, -10,  -7,
+	-5,  -3,   0,   1,   3,   6,   8,  10,
+	13,  15,  17,  19,  22,  24,  26,  29,
+	31,  33,  35,  38,  40,  42,  44,  46,
+	48,  51,  53,  55,  57,  59,  61,  63,
+	65,  67,  69,  71,  73,  75,  77,  79,
+	81,  82,  84,  86,  88,  89,  91,  93,
+	94,  96,  98,  99, 101, 102, 104, 105,
+	106, 108, 109, 110, 112, 113, 114, 115,
+	116, 117, 119, 120, 120, 121, 122, 123,
+	124, 125, 126, 126, 127, 128, 128, 129,
+	129, 130, 130, 131, 131, 131, 131, 132,
+	132, 132, 132, 132, 132, 132, 132, 132,
+	132, 132, 132, 131, 131, 131, 130, 130,
+	130, 129, 129, 128, 127, 127, 126, 125,
+	125, 124, 123, 122, 121, 120, 119, 118,
+	117, 116, 115, 114, 113, 111, 110, 109,
+	107, 106, 105, 103, 102, 100,  99,  97,
+	96, 94, 92, 91, 89, 87, 85, 84, 82
+};
+
+static const s16 hsv_green_x[] = {
+	-124, -124, -125, -125, -125, -125, -125, -125,
+	-125, -126, -126, -125, -125, -125, -125, -125,
+	-125, -124, -124, -124, -123, -123, -122, -122,
+	-121, -121, -120, -120, -119, -118, -117, -117,
+	-116, -115, -114, -113, -112, -111, -110, -109,
+	-108, -107, -105, -104, -103, -102, -100, -99,
+	-98, -96, -95, -93, -92, -91, -89, -87,
+	-86, -84, -83, -81, -79, -77, -76, -74,
+	-72, -70, -69, -67, -65, -63, -61, -59,
+	-57, -55, -53, -51, -49, -47, -45, -43,
+	-41, -39, -37, -35, -33, -30, -28, -26,
+	-24, -22, -20, -18, -15, -13, -11,  -9,
+	-7,  -4,  -2,   0,   1,   3,   6,   8,
+	10,  12,  14,  17,  19,  21,  23,  25,
+	27,  29,  32,  34,  36,  38,  40,  42,
+	44,  46,  48,  50,  52,  54,  56,  58,
+	60,  62,  64,  66,  68,  70,  71,  73,
+	75,  77,  78,  80,  82,  83,  85,  87,
+	88,  90,  91,  93,  94,  96,  97,  98,
+	100, 101, 102, 104, 105, 106, 107, 108,
+	109, 111, 112, 113, 113, 114, 115, 116,
+	117, 118, 118, 119, 120, 120, 121, 122,
+	122, 123, 123, 124, 124, 124, 125, 125,
+	125, 125, 125, 125, 125, 126, 126, 125,
+	125, 125, 125, 125, 125, 124, 124, 124,
+	123, 123, 122, 122, 121, 121, 120, 120,
+	119, 118, 117, 117, 116, 115, 114, 113,
+	112, 111, 110, 109, 108, 107, 105, 104,
+	103, 102, 100,  99,  98,  96,  95,  93,
+	92,  91,  89,  87,  86,  84,  83,  81,
+	79,  77,  76,  74,  72,  70,  69,  67,
+	65,  63,  61,  59,  57,  55,  53,  51,
+	49,  47,  45,  43,  41,  39,  37,  35,
+	33,  30,  28,  26,  24,  22,  20,  18,
+	15,  13,  11,   9,   7,   4,   2,   0,
+	-1,  -3,  -6,  -8, -10, -12, -14, -17,
+	-19, -21, -23, -25, -27, -29, -32, -34,
+	-36, -38, -40, -42, -44, -46, -48, -50,
+	-52, -54, -56, -58, -60, -62, -64, -66,
+	-68, -70, -71, -73, -75, -77, -78, -80,
+	-82, -83, -85, -87, -88, -90, -91, -93,
+	-94, -96, -97, -98, -100, -101, -102, -104,
+	-105, -106, -107, -108, -109, -111, -112, -113,
+	-113, -114, -115, -116, -117, -118, -118, -119,
+	-120, -120, -121, -122, -122, -123, -123, -124, -124
+};
+
+static const s16 hsv_green_y[] = {
+	-100, -99, -98, -97, -95, -94, -93, -91,
+	-90, -89, -87, -86, -84, -83, -81, -80,
+	-78, -76, -75, -73, -71, -70, -68, -66,
+	-64, -63, -61, -59, -57, -55, -53, -51,
+	-49, -48, -46, -44, -42, -40, -38, -36,
+	-34, -32, -30, -27, -25, -23, -21, -19,
+	-17, -15, -13, -11,  -9,  -7,  -4,  -2,
+	0,   1,   3,   5,   7,   9,  11,  14,
+	16,  18,  20,  22,  24,  26,  28,  30,
+	32,  34,  36,  38,  40,  42,  44,  46,
+	48,  50,  52,  54,  56,  58,  59,  61,
+	63,  65,  67,  68,  70,  72,  74,  75,
+	77,  78,  80,  82,  83,  85,  86,  88,
+	89,  90,  92,  93,  95,  96,  97,  98,
+	100, 101, 102, 103, 104, 105, 106, 107,
+	108, 109, 110, 111, 112, 112, 113, 114,
+	115, 115, 116, 116, 117, 117, 118, 118,
+	119, 119, 119, 120, 120, 120, 120, 120,
+	121, 121, 121, 121, 121, 121, 120, 120,
+	120, 120, 120, 119, 119, 119, 118, 118,
+	117, 117, 116, 116, 115, 114, 114, 113,
+	112, 111, 111, 110, 109, 108, 107, 106,
+	105, 104, 103, 102, 100,  99,  98,  97,
+	95,  94,  93,  91,  90,  89,  87,  86,
+	84,  83,  81,  80,  78,  76,  75,  73,
+	71,  70,  68,  66,  64,  63,  61,  59,
+	57,  55,  53,  51,  49,  48,  46,  44,
+	42,  40,  38,  36,  34,  32,  30,  27,
+	25,  23,  21,  19,  17,  15,  13,  11,
+	9,   7,   4,   2,   0,  -1,  -3,  -5,
+	-7,  -9, -11, -14, -16, -18, -20, -22,
+	-24, -26, -28, -30, -32, -34, -36, -38,
+	-40, -42, -44, -46, -48, -50, -52, -54,
+	-56, -58, -59, -61, -63, -65, -67, -68,
+	-70, -72, -74, -75, -77, -78, -80, -82,
+	-83, -85, -86, -88, -89, -90, -92, -93,
+	-95, -96, -97, -98, -100, -101, -102, -103,
+	-104, -105, -106, -107, -108, -109, -110, -111,
+	-112, -112, -113, -114, -115, -115, -116, -116,
+	-117, -117, -118, -118, -119, -119, -119, -120,
+	-120, -120, -120, -120, -121, -121, -121, -121,
+	-121, -121, -120, -120, -120, -120, -120, -119,
+	-119, -119, -118, -118, -117, -117, -116, -116,
+	-115, -114, -114, -113, -112, -111, -111, -110,
+	-109, -108, -107, -106, -105, -104, -103, -102, -100
+};
+
+static const s16 hsv_blue_x[] = {
+	112, 113, 114, 114, 115, 116, 117, 117,
+	118, 118, 119, 119, 120, 120, 120, 121,
+	121, 121, 122, 122, 122, 122, 122, 122,
+	122, 122, 122, 122, 122, 122, 121, 121,
+	121, 120, 120, 120, 119, 119, 118, 118,
+	117, 116, 116, 115, 114, 113, 113, 112,
+	111, 110, 109, 108, 107, 106, 105, 104,
+	103, 102, 100,  99,  98,  97,  95,  94,
+	93,  91,  90,  88,  87,  85,  84,  82,
+	80,  79,  77,  76,  74,  72,  70,  69,
+	67,  65,  63,  61,  60,  58,  56,  54,
+	52,  50,  48,  46,  44,  42,  40,  38,
+	36,  34,  32,  30,  28,  26,  24,  22,
+	19,  17,  15,  13,  11,   9,   7,   5,
+	2,   0,  -1,  -3,  -5,  -7,  -9, -12,
+	-14, -16, -18, -20, -22, -24, -26, -28,
+	-31, -33, -35, -37, -39, -41, -43, -45,
+	-47, -49, -51, -53, -54, -56, -58, -60,
+	-62, -64, -66, -67, -69, -71, -73, -74,
+	-76, -78, -79, -81, -83, -84, -86, -87,
+	-89, -90, -92, -93, -94, -96, -97, -98,
+	-99, -101, -102, -103, -104, -105, -106, -107,
+	-108, -109, -110, -111, -112, -113, -114, -114,
+	-115, -116, -117, -117, -118, -118, -119, -119,
+	-120, -120, -120, -121, -121, -121, -122, -122,
+	-122, -122, -122, -122, -122, -122, -122, -122,
+	-122, -122, -121, -121, -121, -120, -120, -120,
+	-119, -119, -118, -118, -117, -116, -116, -115,
+	-114, -113, -113, -112, -111, -110, -109, -108,
+	-107, -106, -105, -104, -103, -102, -100, -99,
+	-98, -97, -95, -94, -93, -91, -90, -88,
+	-87, -85, -84, -82, -80, -79, -77, -76,
+	-74, -72, -70, -69, -67, -65, -63, -61,
+	-60, -58, -56, -54, -52, -50, -48, -46,
+	-44, -42, -40, -38, -36, -34, -32, -30,
+	-28, -26, -24, -22, -19, -17, -15, -13,
+	-11,  -9,  -7,  -5,  -2,   0,   1,   3,
+	5,   7,   9,  12,  14,  16,  18,  20,
+	22,  24,  26,  28,  31,  33,  35,  37,
+	39,  41,  43,  45,  47,  49,  51,  53,
+	54,  56,  58,  60,  62,  64,  66,  67,
+	69,  71,  73,  74,  76,  78,  79,  81,
+	83,  84,  86,  87,  89,  90,  92,  93,
+	94,  96,  97,  98,  99, 101, 102, 103,
+	104, 105, 106, 107, 108, 109, 110, 111, 112
+};
+
+static const s16 hsv_blue_y[] = {
+	-11, -13, -15, -17, -19, -21, -23, -25,
+	-27, -29, -31, -33, -35, -37, -39, -41,
+	-43, -45, -46, -48, -50, -52, -54, -55,
+	-57, -59, -61, -62, -64, -66, -67, -69,
+	-71, -72, -74, -75, -77, -78, -80, -81,
+	-83, -84, -86, -87, -88, -90, -91, -92,
+	-93, -95, -96, -97, -98, -99, -100, -101,
+	-102, -103, -104, -105, -106, -106, -107, -108,
+	-109, -109, -110, -111, -111, -112, -112, -113,
+	-113, -114, -114, -114, -115, -115, -115, -115,
+	-116, -116, -116, -116, -116, -116, -116, -116,
+	-116, -115, -115, -115, -115, -114, -114, -114,
+	-113, -113, -112, -112, -111, -111, -110, -110,
+	-109, -108, -108, -107, -106, -105, -104, -103,
+	-102, -101, -100, -99, -98, -97, -96, -95,
+	-94, -93, -91, -90, -89, -88, -86, -85,
+	-84, -82, -81, -79, -78, -76, -75, -73,
+	-71, -70, -68, -67, -65, -63, -62, -60,
+	-58, -56, -55, -53, -51, -49, -47, -45,
+	-44, -42, -40, -38, -36, -34, -32, -30,
+	-28, -26, -24, -22, -20, -18, -16, -14,
+	-12, -10,  -8,  -6,  -4,  -2,   0,   1,
+	3,   5,   7,   9,  11,  13,  15,  17,
+	19,  21,  23,  25,  27,  29,  31,  33,
+	35,  37,  39,  41,  43,  45,  46,  48,
+	50,  52,  54,  55,  57,  59,  61,  62,
+	64,  66,  67,  69,  71,  72,  74,  75,
+	77,  78,  80,  81,  83,  84,  86,  87,
+	88,  90,  91,  92,  93,  95,  96,  97,
+	98,  99, 100, 101, 102, 103, 104, 105,
+	106, 106, 107, 108, 109, 109, 110, 111,
+	111, 112, 112, 113, 113, 114, 114, 114,
+	115, 115, 115, 115, 116, 116, 116, 116,
+	116, 116, 116, 116, 116, 115, 115, 115,
+	115, 114, 114, 114, 113, 113, 112, 112,
+	111, 111, 110, 110, 109, 108, 108, 107,
+	106, 105, 104, 103, 102, 101, 100,  99,
+	98,  97,  96,  95,  94,  93,  91,  90,
+	89,  88,  86,  85,  84,  82,  81,  79,
+	78,  76,  75,  73,  71,  70,  68,  67,
+	65,  63,  62,  60,  58,  56,  55,  53,
+	51,  49,  47,  45,  44,  42,  40,  38,
+	36,  34,  32,  30,  28,  26,  24,  22,
+	20,  18,  16,  14,  12,  10,   8,   6,
+	4,   2,   0,  -1,  -3,  -5,  -7,  -9, -11
+};
+
+static const u16 bridge_init[][2] = {
+	{0x1000, 0x78}, {0x1001, 0x40}, {0x1002, 0x1c},
+	{0x1020, 0x80}, {0x1061, 0x01}, {0x1067, 0x40},
+	{0x1068, 0x30}, {0x1069, 0x20},	{0x106a, 0x10},
+	{0x106b, 0x08},	{0x1188, 0x87},	{0x11a1, 0x00},
+	{0x11a2, 0x00},	{0x11a3, 0x6a},	{0x11a4, 0x50},
+	{0x11ab, 0x00},	{0x11ac, 0x00},	{0x11ad, 0x50},
+	{0x11ae, 0x3c},	{0x118a, 0x04},	{0x0395, 0x04},
+	{0x11b8, 0x3a},	{0x118b, 0x0e},	{0x10f7, 0x05},
+	{0x10f8, 0x14},	{0x10fa, 0xff},	{0x10f9, 0x00},
+	{0x11ba, 0x0a},	{0x11a5, 0x2d},	{0x11a6, 0x2d},
+	{0x11a7, 0x3a},	{0x11a8, 0x05},	{0x11a9, 0x04},
+	{0x11aa, 0x3f},	{0x11af, 0x28},	{0x11b0, 0xd8},
+	{0x11b1, 0x14},	{0x11b2, 0xec},	{0x11b3, 0x32},
+	{0x11b4, 0xdd},	{0x11b5, 0x32},	{0x11b6, 0xdd},
+	{0x10e0, 0x2c},	{0x11bc, 0x40},	{0x11bd, 0x01},
+	{0x11be, 0xf0},	{0x11bf, 0x00},	{0x118c, 0x1f},
+	{0x118d, 0x1f},	{0x118e, 0x1f},	{0x118f, 0x1f},
+	{0x1180, 0x01},	{0x1181, 0x00},	{0x1182, 0x01},
+	{0x1183, 0x00},	{0x1184, 0x50},	{0x1185, 0x80},
+	{0x1007, 0x00}
+};
+
+/* Gain = (bit[3:0] / 16 + 1) * (bit[4] + 1) * (bit[5] + 1) * (bit[6] + 1) */
+static const u8 ov_gain[] = {
+	0x00 /* 1x */, 0x04 /* 1.25x */, 0x08 /* 1.5x */, 0x0c /* 1.75x */,
+	0x10 /* 2x */, 0x12 /* 2.25x */, 0x14 /* 2.5x */, 0x16 /* 2.75x */,
+	0x18 /* 3x */, 0x1a /* 3.25x */, 0x1c /* 3.5x */, 0x1e /* 3.75x */,
+	0x30 /* 4x */, 0x31 /* 4.25x */, 0x32 /* 4.5x */, 0x33 /* 4.75x */,
+	0x34 /* 5x */, 0x35 /* 5.25x */, 0x36 /* 5.5x */, 0x37 /* 5.75x */,
+	0x38 /* 6x */, 0x39 /* 6.25x */, 0x3a /* 6.5x */, 0x3b /* 6.75x */,
+	0x3c /* 7x */, 0x3d /* 7.25x */, 0x3e /* 7.5x */, 0x3f /* 7.75x */,
+	0x70 /* 8x */
+};
+
+/* Gain = (bit[8] + 1) * (bit[7] + 1) * (bit[6:0] * 0.03125) */
+static const u16 micron1_gain[] = {
+	/* 1x   1.25x   1.5x    1.75x */
+	0x0020, 0x0028, 0x0030, 0x0038,
+	/* 2x   2.25x   2.5x    2.75x */
+	0x00a0, 0x00a4, 0x00a8, 0x00ac,
+	/* 3x   3.25x   3.5x    3.75x */
+	0x00b0, 0x00b4, 0x00b8, 0x00bc,
+	/* 4x   4.25x   4.5x    4.75x */
+	0x00c0, 0x00c4, 0x00c8, 0x00cc,
+	/* 5x   5.25x   5.5x    5.75x */
+	0x00d0, 0x00d4, 0x00d8, 0x00dc,
+	/* 6x   6.25x   6.5x    6.75x */
+	0x00e0, 0x00e4, 0x00e8, 0x00ec,
+	/* 7x   7.25x   7.5x    7.75x */
+	0x00f0, 0x00f4, 0x00f8, 0x00fc,
+	/* 8x */
+	0x01c0
+};
+
+/* mt9m001 sensor uses a different gain formula then other micron sensors */
+/* Gain = (bit[6] + 1) * (bit[5-0] * 0.125) */
+static const u16 micron2_gain[] = {
+	/* 1x   1.25x   1.5x    1.75x */
+	0x0008, 0x000a, 0x000c, 0x000e,
+	/* 2x   2.25x   2.5x    2.75x */
+	0x0010, 0x0012, 0x0014, 0x0016,
+	/* 3x   3.25x   3.5x    3.75x */
+	0x0018, 0x001a, 0x001c, 0x001e,
+	/* 4x   4.25x   4.5x    4.75x */
+	0x0020, 0x0051, 0x0052, 0x0053,
+	/* 5x   5.25x   5.5x    5.75x */
+	0x0054, 0x0055, 0x0056, 0x0057,
+	/* 6x   6.25x   6.5x    6.75x */
+	0x0058, 0x0059, 0x005a, 0x005b,
+	/* 7x   7.25x   7.5x    7.75x */
+	0x005c, 0x005d, 0x005e, 0x005f,
+	/* 8x */
+	0x0060
+};
+
+/* Gain = .5 + bit[7:0] / 16 */
+static const u8 hv7131r_gain[] = {
+	0x08 /* 1x */, 0x0c /* 1.25x */, 0x10 /* 1.5x */, 0x14 /* 1.75x */,
+	0x18 /* 2x */, 0x1c /* 2.25x */, 0x20 /* 2.5x */, 0x24 /* 2.75x */,
+	0x28 /* 3x */, 0x2c /* 3.25x */, 0x30 /* 3.5x */, 0x34 /* 3.75x */,
+	0x38 /* 4x */, 0x3c /* 4.25x */, 0x40 /* 4.5x */, 0x44 /* 4.75x */,
+	0x48 /* 5x */, 0x4c /* 5.25x */, 0x50 /* 5.5x */, 0x54 /* 5.75x */,
+	0x58 /* 6x */, 0x5c /* 6.25x */, 0x60 /* 6.5x */, 0x64 /* 6.75x */,
+	0x68 /* 7x */, 0x6c /* 7.25x */, 0x70 /* 7.5x */, 0x74 /* 7.75x */,
+	0x78 /* 8x */
+};
+
+static const struct i2c_reg_u8 soi968_init[] = {
+	{0x0c, 0x00}, {0x0f, 0x1f},
+	{0x11, 0x80}, {0x38, 0x52}, {0x1e, 0x00},
+	{0x33, 0x08}, {0x35, 0x8c}, {0x36, 0x0c},
+	{0x37, 0x04}, {0x45, 0x04}, {0x47, 0xff},
+	{0x3e, 0x00}, {0x3f, 0x00}, {0x3b, 0x20},
+	{0x3a, 0x96}, {0x3d, 0x0a}, {0x14, 0x8e},
+	{0x13, 0x8b}, {0x12, 0x40}, {0x17, 0x13},
+	{0x18, 0x63}, {0x19, 0x01}, {0x1a, 0x79},
+	{0x32, 0x24}, {0x03, 0x00}, {0x11, 0x40},
+	{0x2a, 0x10}, {0x2b, 0xe0}, {0x10, 0x32},
+	{0x00, 0x00}, {0x01, 0x80}, {0x02, 0x80},
+};
+
+static const struct i2c_reg_u8 ov7660_init[] = {
+	{0x0e, 0x80}, {0x0d, 0x08}, {0x0f, 0xc3},
+	{0x04, 0xc3}, {0x10, 0x40}, {0x11, 0x40},
+	{0x12, 0x05}, {0x13, 0xba}, {0x14, 0x2a},
+	/* HDG Set hstart and hstop, datasheet default 0x11, 0x61, using
+	   0x10, 0x61 and sd->hstart, vstart = 3, fixes ugly colored borders */
+	{0x17, 0x10}, {0x18, 0x61},
+	{0x37, 0x0f}, {0x38, 0x02}, {0x39, 0x43},
+	{0x3a, 0x00}, {0x69, 0x90}, {0x2d, 0x00},
+	{0x2e, 0x00}, {0x01, 0x78}, {0x02, 0x50},
+};
+
+static const struct i2c_reg_u8 ov7670_init[] = {
+	{0x11, 0x80}, {0x3a, 0x04}, {0x12, 0x01},
+	{0x32, 0xb6}, {0x03, 0x0a}, {0x0c, 0x00}, {0x3e, 0x00},
+	{0x70, 0x3a}, {0x71, 0x35}, {0x72, 0x11}, {0x73, 0xf0},
+	{0xa2, 0x02}, {0x13, 0xe0}, {0x00, 0x00}, {0x10, 0x00},
+	{0x0d, 0x40}, {0x14, 0x28}, {0xa5, 0x05}, {0xab, 0x07},
+	{0x24, 0x95}, {0x25, 0x33}, {0x26, 0xe3}, {0x9f, 0x75},
+	{0xa0, 0x65}, {0xa1, 0x0b}, {0xa6, 0xd8}, {0xa7, 0xd8},
+	{0xa8, 0xf0}, {0xa9, 0x90}, {0xaa, 0x94}, {0x13, 0xe5},
+	{0x0e, 0x61}, {0x0f, 0x4b}, {0x16, 0x02}, {0x1e, 0x27},
+	{0x21, 0x02}, {0x22, 0x91}, {0x29, 0x07}, {0x33, 0x0b},
+	{0x35, 0x0b}, {0x37, 0x1d}, {0x38, 0x71}, {0x39, 0x2a},
+	{0x3c, 0x78}, {0x4d, 0x40}, {0x4e, 0x20}, {0x69, 0x00},
+	{0x74, 0x19}, {0x8d, 0x4f}, {0x8e, 0x00}, {0x8f, 0x00},
+	{0x90, 0x00}, {0x91, 0x00}, {0x96, 0x00}, {0x9a, 0x80},
+	{0xb0, 0x84}, {0xb1, 0x0c}, {0xb2, 0x0e}, {0xb3, 0x82},
+	{0xb8, 0x0a}, {0x43, 0x0a}, {0x44, 0xf0}, {0x45, 0x20},
+	{0x46, 0x7d}, {0x47, 0x29}, {0x48, 0x4a}, {0x59, 0x8c},
+	{0x5a, 0xa5}, {0x5b, 0xde}, {0x5c, 0x96}, {0x5d, 0x66},
+	{0x5e, 0x10}, {0x6c, 0x0a}, {0x6d, 0x55}, {0x6e, 0x11},
+	{0x6f, 0x9e}, {0x6a, 0x40}, {0x01, 0x40}, {0x02, 0x40},
+	{0x13, 0xe7}, {0x4f, 0x6e}, {0x50, 0x70}, {0x51, 0x02},
+	{0x52, 0x1d}, {0x53, 0x56}, {0x54, 0x73}, {0x55, 0x0a},
+	{0x56, 0x55}, {0x57, 0x80}, {0x58, 0x9e}, {0x41, 0x08},
+	{0x3f, 0x02}, {0x75, 0x03}, {0x76, 0x63}, {0x4c, 0x04},
+	{0x77, 0x06}, {0x3d, 0x02}, {0x4b, 0x09}, {0xc9, 0x30},
+	{0x41, 0x08}, {0x56, 0x48}, {0x34, 0x11}, {0xa4, 0x88},
+	{0x96, 0x00}, {0x97, 0x30}, {0x98, 0x20}, {0x99, 0x30},
+	{0x9a, 0x84}, {0x9b, 0x29}, {0x9c, 0x03}, {0x9d, 0x99},
+	{0x9e, 0x7f}, {0x78, 0x04}, {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}, {0x62, 0x20}, {0x63, 0x00}, {0x64, 0x06},
+	{0x65, 0x00}, {0x66, 0x05}, {0x94, 0x05}, {0x95, 0x0a},
+	{0x17, 0x13}, {0x18, 0x01}, {0x19, 0x02}, {0x1a, 0x7a},
+	{0x46, 0x59}, {0x47, 0x30}, {0x58, 0x9a}, {0x59, 0x84},
+	{0x5a, 0x91}, {0x5b, 0x57}, {0x5c, 0x75}, {0x5d, 0x6d},
+	{0x5e, 0x13}, {0x64, 0x07}, {0x94, 0x07}, {0x95, 0x0d},
+	{0xa6, 0xdf}, {0xa7, 0xdf}, {0x48, 0x4d}, {0x51, 0x00},
+	{0x6b, 0x0a}, {0x11, 0x80}, {0x2a, 0x00}, {0x2b, 0x00},
+	{0x92, 0x00}, {0x93, 0x00}, {0x55, 0x0a}, {0x56, 0x60},
+	{0x4f, 0x6e}, {0x50, 0x70}, {0x51, 0x00}, {0x52, 0x1d},
+	{0x53, 0x56}, {0x54, 0x73}, {0x58, 0x9a}, {0x4f, 0x6e},
+	{0x50, 0x70}, {0x51, 0x00}, {0x52, 0x1d}, {0x53, 0x56},
+	{0x54, 0x73}, {0x58, 0x9a}, {0x3f, 0x01}, {0x7b, 0x03},
+	{0x7c, 0x09}, {0x7d, 0x16}, {0x7e, 0x38}, {0x7f, 0x47},
+	{0x80, 0x53}, {0x81, 0x5e}, {0x82, 0x6a}, {0x83, 0x74},
+	{0x84, 0x80}, {0x85, 0x8c}, {0x86, 0x9b}, {0x87, 0xb2},
+	{0x88, 0xcc}, {0x89, 0xe5}, {0x7a, 0x24}, {0x3b, 0x00},
+	{0x9f, 0x76}, {0xa0, 0x65}, {0x13, 0xe2}, {0x6b, 0x0a},
+	{0x11, 0x80}, {0x2a, 0x00}, {0x2b, 0x00}, {0x92, 0x00},
+	{0x93, 0x00},
+};
+
+static const struct i2c_reg_u8 ov9650_init[] = {
+	{0x00, 0x00}, {0x01, 0x78},
+	{0x02, 0x78}, {0x03, 0x36}, {0x04, 0x03},
+	{0x05, 0x00}, {0x06, 0x00}, {0x08, 0x00},
+	{0x09, 0x01}, {0x0c, 0x00}, {0x0d, 0x00},
+	{0x0e, 0xa0}, {0x0f, 0x52}, {0x10, 0x7c},
+	{0x11, 0x80}, {0x12, 0x45}, {0x13, 0xc2},
+	{0x14, 0x2e}, {0x15, 0x00}, {0x16, 0x07},
+	{0x17, 0x24}, {0x18, 0xc5}, {0x19, 0x00},
+	{0x1a, 0x3c}, {0x1b, 0x00}, {0x1e, 0x04},
+	{0x1f, 0x00}, {0x24, 0x78}, {0x25, 0x68},
+	{0x26, 0xd4}, {0x27, 0x80}, {0x28, 0x80},
+	{0x29, 0x30}, {0x2a, 0x00}, {0x2b, 0x00},
+	{0x2c, 0x80}, {0x2d, 0x00}, {0x2e, 0x00},
+	{0x2f, 0x00}, {0x30, 0x08}, {0x31, 0x30},
+	{0x32, 0x84}, {0x33, 0xe2}, {0x34, 0xbf},
+	{0x35, 0x81}, {0x36, 0xf9}, {0x37, 0x00},
+	{0x38, 0x93}, {0x39, 0x50}, {0x3a, 0x01},
+	{0x3b, 0x01}, {0x3c, 0x73}, {0x3d, 0x19},
+	{0x3e, 0x0b}, {0x3f, 0x80}, {0x40, 0xc1},
+	{0x41, 0x00}, {0x42, 0x08}, {0x67, 0x80},
+	{0x68, 0x80}, {0x69, 0x40}, {0x6a, 0x00},
+	{0x6b, 0x0a}, {0x8b, 0x06}, {0x8c, 0x20},
+	{0x8d, 0x00}, {0x8e, 0x00}, {0x8f, 0xdf},
+	{0x92, 0x00}, {0x93, 0x00}, {0x94, 0x88},
+	{0x95, 0x88}, {0x96, 0x04}, {0xa1, 0x00},
+	{0xa5, 0x80}, {0xa8, 0x80}, {0xa9, 0xb8},
+	{0xaa, 0x92}, {0xab, 0x0a},
+};
+
+static const struct i2c_reg_u8 ov9655_init[] = {
+	{0x0e, 0x61}, {0x11, 0x80}, {0x13, 0xba},
+	{0x14, 0x2e}, {0x16, 0x24}, {0x1e, 0x04}, {0x27, 0x08},
+	{0x28, 0x08}, {0x29, 0x15}, {0x2c, 0x08}, {0x34, 0x3d},
+	{0x35, 0x00}, {0x38, 0x12}, {0x0f, 0x42}, {0x39, 0x57},
+	{0x3a, 0x00}, {0x3b, 0xcc}, {0x3c, 0x0c}, {0x3d, 0x19},
+	{0x3e, 0x0c}, {0x3f, 0x01}, {0x41, 0x40}, {0x42, 0x80},
+	{0x45, 0x46}, {0x46, 0x62}, {0x47, 0x2a}, {0x48, 0x3c},
+	{0x4a, 0xf0}, {0x4b, 0xdc}, {0x4c, 0xdc}, {0x4d, 0xdc},
+	{0x4e, 0xdc}, {0x6c, 0x04}, {0x6f, 0x9e}, {0x70, 0x05},
+	{0x71, 0x78}, {0x77, 0x02}, {0x8a, 0x23}, {0x90, 0x7e},
+	{0x91, 0x7c}, {0x9f, 0x6e}, {0xa0, 0x6e}, {0xa5, 0x68},
+	{0xa6, 0x60}, {0xa8, 0xc1}, {0xa9, 0xfa}, {0xaa, 0x92},
+	{0xab, 0x04}, {0xac, 0x80}, {0xad, 0x80}, {0xae, 0x80},
+	{0xaf, 0x80}, {0xb2, 0xf2}, {0xb3, 0x20}, {0xb5, 0x00},
+	{0xb6, 0xaf}, {0xbb, 0xae}, {0xbc, 0x44}, {0xbd, 0x44},
+	{0xbe, 0x3b}, {0xbf, 0x3a}, {0xc1, 0xc8}, {0xc2, 0x01},
+	{0xc4, 0x00}, {0xc6, 0x85}, {0xc7, 0x81}, {0xc9, 0xe0},
+	{0xca, 0xe8}, {0xcc, 0xd8}, {0xcd, 0x93}, {0x2d, 0x00},
+	{0x2e, 0x00}, {0x01, 0x80}, {0x02, 0x80}, {0x12, 0x61},
+	{0x36, 0xfa}, {0x8c, 0x8d}, {0xc0, 0xaa}, {0x69, 0x0a},
+	{0x03, 0x09}, {0x17, 0x16}, {0x18, 0x6e}, {0x19, 0x01},
+	{0x1a, 0x3e}, {0x32, 0x09}, {0x2a, 0x10}, {0x2b, 0x0a},
+	{0x92, 0x00}, {0x93, 0x00}, {0xa1, 0x00}, {0x10, 0x7c},
+	{0x04, 0x03}, {0x00, 0x13},
+};
+
+static const struct i2c_reg_u16 mt9v112_init[] = {
+	{0xf0, 0x0000}, {0x0d, 0x0021}, {0x0d, 0x0020},
+	{0x34, 0xc019}, {0x0a, 0x0011}, {0x0b, 0x000b},
+	{0x20, 0x0703}, {0x35, 0x2022}, {0xf0, 0x0001},
+	{0x05, 0x0000}, {0x06, 0x340c}, {0x3b, 0x042a},
+	{0x3c, 0x0400}, {0xf0, 0x0002}, {0x2e, 0x0c58},
+	{0x5b, 0x0001}, {0xc8, 0x9f0b}, {0xf0, 0x0001},
+	{0x9b, 0x5300}, {0xf0, 0x0000}, {0x2b, 0x0020},
+	{0x2c, 0x002a}, {0x2d, 0x0032}, {0x2e, 0x0020},
+	{0x09, 0x01dc}, {0x01, 0x000c}, {0x02, 0x0020},
+	{0x03, 0x01e0}, {0x04, 0x0280}, {0x06, 0x000c},
+	{0x05, 0x0098}, {0x20, 0x0703}, {0x09, 0x01f2},
+	{0x2b, 0x00a0}, {0x2c, 0x00a0}, {0x2d, 0x00a0},
+	{0x2e, 0x00a0}, {0x01, 0x000c}, {0x02, 0x0020},
+	{0x03, 0x01e0}, {0x04, 0x0280}, {0x06, 0x000c},
+	{0x05, 0x0098}, {0x09, 0x01c1}, {0x2b, 0x00ae},
+	{0x2c, 0x00ae}, {0x2d, 0x00ae}, {0x2e, 0x00ae},
+};
+
+static const struct i2c_reg_u16 mt9v111_init[] = {
+	{0x01, 0x0004}, {0x0d, 0x0001}, {0x0d, 0x0000},
+	{0x01, 0x0001}, {0x05, 0x0004}, {0x2d, 0xe0a0},
+	{0x2e, 0x0c64},	{0x2f, 0x0064}, {0x06, 0x600e},
+	{0x08, 0x0480}, {0x01, 0x0004}, {0x02, 0x0016},
+	{0x03, 0x01e7}, {0x04, 0x0287}, {0x05, 0x0004},
+	{0x06, 0x002d},	{0x07, 0x3002}, {0x08, 0x0008},
+	{0x0e, 0x0008}, {0x20, 0x0000}
+};
+
+static const struct i2c_reg_u16 mt9v011_init[] = {
+	{0x07, 0x0002},	{0x0d, 0x0001},	{0x0d, 0x0000},
+	{0x01, 0x0008},	{0x02, 0x0016},	{0x03, 0x01e1},
+	{0x04, 0x0281},	{0x05, 0x0083},	{0x06, 0x0006},
+	{0x0d, 0x0002}, {0x0a, 0x0000},	{0x0b, 0x0000},
+	{0x0c, 0x0000},	{0x0d, 0x0000},	{0x0e, 0x0000},
+	{0x0f, 0x0000},	{0x10, 0x0000},	{0x11, 0x0000},
+	{0x12, 0x0000},	{0x13, 0x0000},	{0x14, 0x0000},
+	{0x15, 0x0000},	{0x16, 0x0000},	{0x17, 0x0000},
+	{0x18, 0x0000},	{0x19, 0x0000},	{0x1a, 0x0000},
+	{0x1b, 0x0000},	{0x1c, 0x0000},	{0x1d, 0x0000},
+	{0x32, 0x0000},	{0x20, 0x1101},	{0x21, 0x0000},
+	{0x22, 0x0000},	{0x23, 0x0000},	{0x24, 0x0000},
+	{0x25, 0x0000},	{0x26, 0x0000},	{0x27, 0x0024},
+	{0x2f, 0xf7b0},	{0x30, 0x0005},	{0x31, 0x0000},
+	{0x32, 0x0000},	{0x33, 0x0000},	{0x34, 0x0100},
+	{0x3d, 0x068f},	{0x40, 0x01e0},	{0x41, 0x00d1},
+	{0x44, 0x0082},	{0x5a, 0x0000},	{0x5b, 0x0000},
+	{0x5c, 0x0000},	{0x5d, 0x0000},	{0x5e, 0x0000},
+	{0x5f, 0xa31d},	{0x62, 0x0611},	{0x0a, 0x0000},
+	{0x06, 0x0029},	{0x05, 0x0009},	{0x20, 0x1101},
+	{0x20, 0x1101},	{0x09, 0x0064},	{0x07, 0x0003},
+	{0x2b, 0x0033},	{0x2c, 0x00a0},	{0x2d, 0x00a0},
+	{0x2e, 0x0033},	{0x07, 0x0002},	{0x06, 0x0000},
+	{0x06, 0x0029},	{0x05, 0x0009},
+};
+
+static const struct i2c_reg_u16 mt9m001_init[] = {
+	{0x0d, 0x0001},
+	{0x0d, 0x0000},
+	{0x04, 0x0500},		/* hres = 1280 */
+	{0x03, 0x0400},		/* vres = 1024 */
+	{0x20, 0x1100},
+	{0x06, 0x0010},
+	{0x2b, 0x0024},
+	{0x2e, 0x0024},
+	{0x35, 0x0024},
+	{0x2d, 0x0020},
+	{0x2c, 0x0020},
+	{0x09, 0x0ad4},
+	{0x35, 0x0057},
+};
+
+static const struct i2c_reg_u16 mt9m111_init[] = {
+	{0xf0, 0x0000}, {0x0d, 0x0021}, {0x0d, 0x0008},
+	{0xf0, 0x0001}, {0x3a, 0x4300}, {0x9b, 0x4300},
+	{0x06, 0x708e}, {0xf0, 0x0002}, {0x2e, 0x0a1e},
+	{0xf0, 0x0000},
+};
+
+static const struct i2c_reg_u16 mt9m112_init[] = {
+	{0xf0, 0x0000}, {0x0d, 0x0021}, {0x0d, 0x0008},
+	{0xf0, 0x0001}, {0x3a, 0x4300}, {0x9b, 0x4300},
+	{0x06, 0x708e}, {0xf0, 0x0002}, {0x2e, 0x0a1e},
+	{0xf0, 0x0000},
+};
+
+static const struct i2c_reg_u8 hv7131r_init[] = {
+	{0x02, 0x08}, {0x02, 0x00}, {0x01, 0x08},
+	{0x02, 0x00}, {0x20, 0x00}, {0x21, 0xd0},
+	{0x22, 0x00}, {0x23, 0x09}, {0x01, 0x08},
+	{0x01, 0x08}, {0x01, 0x08}, {0x25, 0x07},
+	{0x26, 0xc3}, {0x27, 0x50}, {0x30, 0x62},
+	{0x31, 0x10}, {0x32, 0x06}, {0x33, 0x10},
+	{0x20, 0x00}, {0x21, 0xd0}, {0x22, 0x00},
+	{0x23, 0x09}, {0x01, 0x08},
+};
+
+static void reg_r(struct gspca_dev *gspca_dev, u16 reg, u16 length)
+{
+	struct usb_device *dev = gspca_dev->dev;
+	int result;
+
+	if (gspca_dev->usb_err < 0)
+		return;
+	result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+			0x00,
+			USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+			reg,
+			0x00,
+			gspca_dev->usb_buf,
+			length,
+			500);
+	if (unlikely(result < 0 || result != length)) {
+		pr_err("Read register %02x failed %d\n", reg, result);
+		gspca_dev->usb_err = result;
+	}
+}
+
+static void reg_w(struct gspca_dev *gspca_dev, u16 reg,
+		 const u8 *buffer, int length)
+{
+	struct usb_device *dev = gspca_dev->dev;
+	int result;
+
+	if (gspca_dev->usb_err < 0)
+		return;
+	memcpy(gspca_dev->usb_buf, buffer, length);
+	result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+			0x08,
+			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+			reg,
+			0x00,
+			gspca_dev->usb_buf,
+			length,
+			500);
+	if (unlikely(result < 0 || result != length)) {
+		pr_err("Write register %02x failed %d\n", reg, result);
+		gspca_dev->usb_err = result;
+	}
+}
+
+static void reg_w1(struct gspca_dev *gspca_dev, u16 reg, const u8 value)
+{
+	reg_w(gspca_dev, reg, &value, 1);
+}
+
+static void i2c_w(struct gspca_dev *gspca_dev, const u8 *buffer)
+{
+	int i;
+
+	reg_w(gspca_dev, 0x10c0, buffer, 8);
+	for (i = 0; i < 5; i++) {
+		reg_r(gspca_dev, 0x10c0, 1);
+		if (gspca_dev->usb_err < 0)
+			return;
+		if (gspca_dev->usb_buf[0] & 0x04) {
+			if (gspca_dev->usb_buf[0] & 0x08) {
+				pr_err("i2c_w error\n");
+				gspca_dev->usb_err = -EIO;
+			}
+			return;
+		}
+		msleep(10);
+	}
+	pr_err("i2c_w reg %02x no response\n", buffer[2]);
+/*	gspca_dev->usb_err = -EIO;	fixme: may occur */
+}
+
+static void i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u8 row[8];
+
+	/*
+	 * from the point of view of the bridge, the length
+	 * includes the address
+	 */
+	row[0] = sd->i2c_intf | (2 << 4);
+	row[1] = sd->i2c_addr;
+	row[2] = reg;
+	row[3] = val;
+	row[4] = 0x00;
+	row[5] = 0x00;
+	row[6] = 0x00;
+	row[7] = 0x10;
+
+	i2c_w(gspca_dev, row);
+}
+
+static void i2c_w1_buf(struct gspca_dev *gspca_dev,
+			const struct i2c_reg_u8 *buf, int sz)
+{
+	while (--sz >= 0) {
+		i2c_w1(gspca_dev, buf->reg, buf->val);
+		buf++;
+	}
+}
+
+static void i2c_w2(struct gspca_dev *gspca_dev, u8 reg, u16 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u8 row[8];
+
+	/*
+	 * from the point of view of the bridge, the length
+	 * includes the address
+	 */
+	row[0] = sd->i2c_intf | (3 << 4);
+	row[1] = sd->i2c_addr;
+	row[2] = reg;
+	row[3] = val >> 8;
+	row[4] = val;
+	row[5] = 0x00;
+	row[6] = 0x00;
+	row[7] = 0x10;
+
+	i2c_w(gspca_dev, row);
+}
+
+static void i2c_w2_buf(struct gspca_dev *gspca_dev,
+			const struct i2c_reg_u16 *buf, int sz)
+{
+	while (--sz >= 0) {
+		i2c_w2(gspca_dev, buf->reg, buf->val);
+		buf++;
+	}
+}
+
+static void i2c_r1(struct gspca_dev *gspca_dev, u8 reg, u8 *val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u8 row[8];
+
+	row[0] = sd->i2c_intf | (1 << 4);
+	row[1] = sd->i2c_addr;
+	row[2] = reg;
+	row[3] = 0;
+	row[4] = 0;
+	row[5] = 0;
+	row[6] = 0;
+	row[7] = 0x10;
+	i2c_w(gspca_dev, row);
+	row[0] = sd->i2c_intf | (1 << 4) | 0x02;
+	row[2] = 0;
+	i2c_w(gspca_dev, row);
+	reg_r(gspca_dev, 0x10c2, 5);
+	*val = gspca_dev->usb_buf[4];
+}
+
+static void i2c_r2(struct gspca_dev *gspca_dev, u8 reg, u16 *val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u8 row[8];
+
+	row[0] = sd->i2c_intf | (1 << 4);
+	row[1] = sd->i2c_addr;
+	row[2] = reg;
+	row[3] = 0;
+	row[4] = 0;
+	row[5] = 0;
+	row[6] = 0;
+	row[7] = 0x10;
+	i2c_w(gspca_dev, row);
+	row[0] = sd->i2c_intf | (2 << 4) | 0x02;
+	row[2] = 0;
+	i2c_w(gspca_dev, row);
+	reg_r(gspca_dev, 0x10c2, 5);
+	*val = (gspca_dev->usb_buf[3] << 8) | gspca_dev->usb_buf[4];
+}
+
+static void ov9650_init_sensor(struct gspca_dev *gspca_dev)
+{
+	u16 id;
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	i2c_r2(gspca_dev, 0x1c, &id);
+	if (gspca_dev->usb_err < 0)
+		return;
+
+	if (id != 0x7fa2) {
+		pr_err("sensor id for ov9650 doesn't match (0x%04x)\n", id);
+		gspca_dev->usb_err = -ENODEV;
+		return;
+	}
+
+	i2c_w1(gspca_dev, 0x12, 0x80);		/* sensor reset */
+	msleep(200);
+	i2c_w1_buf(gspca_dev, ov9650_init, ARRAY_SIZE(ov9650_init));
+	if (gspca_dev->usb_err < 0)
+		pr_err("OV9650 sensor initialization failed\n");
+	sd->hstart = 1;
+	sd->vstart = 7;
+}
+
+static void ov9655_init_sensor(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	i2c_w1(gspca_dev, 0x12, 0x80);		/* sensor reset */
+	msleep(200);
+	i2c_w1_buf(gspca_dev, ov9655_init, ARRAY_SIZE(ov9655_init));
+	if (gspca_dev->usb_err < 0)
+		pr_err("OV9655 sensor initialization failed\n");
+
+	sd->hstart = 1;
+	sd->vstart = 2;
+}
+
+static void soi968_init_sensor(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	i2c_w1(gspca_dev, 0x12, 0x80);		/* sensor reset */
+	msleep(200);
+	i2c_w1_buf(gspca_dev, soi968_init, ARRAY_SIZE(soi968_init));
+	if (gspca_dev->usb_err < 0)
+		pr_err("SOI968 sensor initialization failed\n");
+
+	sd->hstart = 60;
+	sd->vstart = 11;
+}
+
+static void ov7660_init_sensor(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	i2c_w1(gspca_dev, 0x12, 0x80);		/* sensor reset */
+	msleep(200);
+	i2c_w1_buf(gspca_dev, ov7660_init, ARRAY_SIZE(ov7660_init));
+	if (gspca_dev->usb_err < 0)
+		pr_err("OV7660 sensor initialization failed\n");
+	sd->hstart = 3;
+	sd->vstart = 3;
+}
+
+static void ov7670_init_sensor(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	i2c_w1(gspca_dev, 0x12, 0x80);		/* sensor reset */
+	msleep(200);
+	i2c_w1_buf(gspca_dev, ov7670_init, ARRAY_SIZE(ov7670_init));
+	if (gspca_dev->usb_err < 0)
+		pr_err("OV7670 sensor initialization failed\n");
+
+	sd->hstart = 0;
+	sd->vstart = 1;
+}
+
+static void mt9v_init_sensor(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u16 value;
+
+	sd->i2c_addr = 0x5d;
+	i2c_r2(gspca_dev, 0xff, &value);
+	if (gspca_dev->usb_err >= 0
+	 && value == 0x8243) {
+		i2c_w2_buf(gspca_dev, mt9v011_init, ARRAY_SIZE(mt9v011_init));
+		if (gspca_dev->usb_err < 0) {
+			pr_err("MT9V011 sensor initialization failed\n");
+			return;
+		}
+		sd->hstart = 2;
+		sd->vstart = 2;
+		sd->sensor = SENSOR_MT9V011;
+		pr_info("MT9V011 sensor detected\n");
+		return;
+	}
+
+	gspca_dev->usb_err = 0;
+	sd->i2c_addr = 0x5c;
+	i2c_w2(gspca_dev, 0x01, 0x0004);
+	i2c_r2(gspca_dev, 0xff, &value);
+	if (gspca_dev->usb_err >= 0
+	 && value == 0x823a) {
+		i2c_w2_buf(gspca_dev, mt9v111_init, ARRAY_SIZE(mt9v111_init));
+		if (gspca_dev->usb_err < 0) {
+			pr_err("MT9V111 sensor initialization failed\n");
+			return;
+		}
+		sd->hstart = 2;
+		sd->vstart = 2;
+		sd->sensor = SENSOR_MT9V111;
+		pr_info("MT9V111 sensor detected\n");
+		return;
+	}
+
+	gspca_dev->usb_err = 0;
+	sd->i2c_addr = 0x5d;
+	i2c_w2(gspca_dev, 0xf0, 0x0000);
+	if (gspca_dev->usb_err < 0) {
+		gspca_dev->usb_err = 0;
+		sd->i2c_addr = 0x48;
+		i2c_w2(gspca_dev, 0xf0, 0x0000);
+	}
+	i2c_r2(gspca_dev, 0x00, &value);
+	if (gspca_dev->usb_err >= 0
+	 && value == 0x1229) {
+		i2c_w2_buf(gspca_dev, mt9v112_init, ARRAY_SIZE(mt9v112_init));
+		if (gspca_dev->usb_err < 0) {
+			pr_err("MT9V112 sensor initialization failed\n");
+			return;
+		}
+		sd->hstart = 6;
+		sd->vstart = 2;
+		sd->sensor = SENSOR_MT9V112;
+		pr_info("MT9V112 sensor detected\n");
+		return;
+	}
+
+	gspca_dev->usb_err = -ENODEV;
+}
+
+static void mt9m112_init_sensor(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	i2c_w2_buf(gspca_dev, mt9m112_init, ARRAY_SIZE(mt9m112_init));
+	if (gspca_dev->usb_err < 0)
+		pr_err("MT9M112 sensor initialization failed\n");
+
+	sd->hstart = 0;
+	sd->vstart = 2;
+}
+
+static void mt9m111_init_sensor(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	i2c_w2_buf(gspca_dev, mt9m111_init, ARRAY_SIZE(mt9m111_init));
+	if (gspca_dev->usb_err < 0)
+		pr_err("MT9M111 sensor initialization failed\n");
+
+	sd->hstart = 0;
+	sd->vstart = 2;
+}
+
+static void mt9m001_init_sensor(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u16 id;
+
+	i2c_r2(gspca_dev, 0x00, &id);
+	if (gspca_dev->usb_err < 0)
+		return;
+
+	/* must be 0x8411 or 0x8421 for colour sensor and 8431 for bw */
+	switch (id) {
+	case 0x8411:
+	case 0x8421:
+		pr_info("MT9M001 color sensor detected\n");
+		break;
+	case 0x8431:
+		pr_info("MT9M001 mono sensor detected\n");
+		break;
+	default:
+		pr_err("No MT9M001 chip detected, ID = %x\n\n", id);
+		gspca_dev->usb_err = -ENODEV;
+		return;
+	}
+
+	i2c_w2_buf(gspca_dev, mt9m001_init, ARRAY_SIZE(mt9m001_init));
+	if (gspca_dev->usb_err < 0)
+		pr_err("MT9M001 sensor initialization failed\n");
+
+	sd->hstart = 1;
+	sd->vstart = 1;
+}
+
+static void hv7131r_init_sensor(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	i2c_w1_buf(gspca_dev, hv7131r_init, ARRAY_SIZE(hv7131r_init));
+	if (gspca_dev->usb_err < 0)
+		pr_err("HV7131R Sensor initialization failed\n");
+
+	sd->hstart = 0;
+	sd->vstart = 1;
+}
+
+static void set_cmatrix(struct gspca_dev *gspca_dev,
+		s32 brightness, s32 contrast, s32 satur, s32 hue)
+{
+	s32 hue_coord, hue_index = 180 + hue;
+	u8 cmatrix[21];
+
+	memset(cmatrix, 0, sizeof(cmatrix));
+	cmatrix[2] = (contrast * 0x25 / 0x100) + 0x26;
+	cmatrix[0] = 0x13 + (cmatrix[2] - 0x26) * 0x13 / 0x25;
+	cmatrix[4] = 0x07 + (cmatrix[2] - 0x26) * 0x07 / 0x25;
+	cmatrix[18] = brightness - 0x80;
+
+	hue_coord = (hsv_red_x[hue_index] * satur) >> 8;
+	cmatrix[6] = hue_coord;
+	cmatrix[7] = (hue_coord >> 8) & 0x0f;
+
+	hue_coord = (hsv_red_y[hue_index] * satur) >> 8;
+	cmatrix[8] = hue_coord;
+	cmatrix[9] = (hue_coord >> 8) & 0x0f;
+
+	hue_coord = (hsv_green_x[hue_index] * satur) >> 8;
+	cmatrix[10] = hue_coord;
+	cmatrix[11] = (hue_coord >> 8) & 0x0f;
+
+	hue_coord = (hsv_green_y[hue_index] * satur) >> 8;
+	cmatrix[12] = hue_coord;
+	cmatrix[13] = (hue_coord >> 8) & 0x0f;
+
+	hue_coord = (hsv_blue_x[hue_index] * satur) >> 8;
+	cmatrix[14] = hue_coord;
+	cmatrix[15] = (hue_coord >> 8) & 0x0f;
+
+	hue_coord = (hsv_blue_y[hue_index] * satur) >> 8;
+	cmatrix[16] = hue_coord;
+	cmatrix[17] = (hue_coord >> 8) & 0x0f;
+
+	reg_w(gspca_dev, 0x10e1, cmatrix, 21);
+}
+
+static void set_gamma(struct gspca_dev *gspca_dev, s32 val)
+{
+	u8 gamma[17];
+	u8 gval = val * 0xb8 / 0x100;
+
+	gamma[0] = 0x0a;
+	gamma[1] = 0x13 + (gval * (0xcb - 0x13) / 0xb8);
+	gamma[2] = 0x25 + (gval * (0xee - 0x25) / 0xb8);
+	gamma[3] = 0x37 + (gval * (0xfa - 0x37) / 0xb8);
+	gamma[4] = 0x45 + (gval * (0xfc - 0x45) / 0xb8);
+	gamma[5] = 0x55 + (gval * (0xfb - 0x55) / 0xb8);
+	gamma[6] = 0x65 + (gval * (0xfc - 0x65) / 0xb8);
+	gamma[7] = 0x74 + (gval * (0xfd - 0x74) / 0xb8);
+	gamma[8] = 0x83 + (gval * (0xfe - 0x83) / 0xb8);
+	gamma[9] = 0x92 + (gval * (0xfc - 0x92) / 0xb8);
+	gamma[10] = 0xa1 + (gval * (0xfc - 0xa1) / 0xb8);
+	gamma[11] = 0xb0 + (gval * (0xfc - 0xb0) / 0xb8);
+	gamma[12] = 0xbf + (gval * (0xfb - 0xbf) / 0xb8);
+	gamma[13] = 0xce + (gval * (0xfb - 0xce) / 0xb8);
+	gamma[14] = 0xdf + (gval * (0xfd - 0xdf) / 0xb8);
+	gamma[15] = 0xea + (gval * (0xf9 - 0xea) / 0xb8);
+	gamma[16] = 0xf5;
+
+	reg_w(gspca_dev, 0x1190, gamma, 17);
+}
+
+static void set_redblue(struct gspca_dev *gspca_dev, s32 blue, s32 red)
+{
+	reg_w1(gspca_dev, 0x118c, red);
+	reg_w1(gspca_dev, 0x118f, blue);
+}
+
+static void set_hvflip(struct gspca_dev *gspca_dev, s32 hflip, s32 vflip)
+{
+	u8 value, tslb;
+	u16 value2;
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if ((sd->flags & FLIP_DETECT) && dmi_check_system(flip_dmi_table)) {
+		hflip = !hflip;
+		vflip = !vflip;
+	}
+
+	switch (sd->sensor) {
+	case SENSOR_OV7660:
+		value = 0x01;
+		if (hflip)
+			value |= 0x20;
+		if (vflip) {
+			value |= 0x10;
+			sd->vstart = 2;
+		} else {
+			sd->vstart = 3;
+		}
+		reg_w1(gspca_dev, 0x1182, sd->vstart);
+		i2c_w1(gspca_dev, 0x1e, value);
+		break;
+	case SENSOR_OV9650:
+		i2c_r1(gspca_dev, 0x1e, &value);
+		value &= ~0x30;
+		tslb = 0x01;
+		if (hflip)
+			value |= 0x20;
+		if (vflip) {
+			value |= 0x10;
+			tslb = 0x49;
+		}
+		i2c_w1(gspca_dev, 0x1e, value);
+		i2c_w1(gspca_dev, 0x3a, tslb);
+		break;
+	case SENSOR_MT9V111:
+	case SENSOR_MT9V011:
+		i2c_r2(gspca_dev, 0x20, &value2);
+		value2 &= ~0xc0a0;
+		if (hflip)
+			value2 |= 0x8080;
+		if (vflip)
+			value2 |= 0x4020;
+		i2c_w2(gspca_dev, 0x20, value2);
+		break;
+	case SENSOR_MT9M112:
+	case SENSOR_MT9M111:
+	case SENSOR_MT9V112:
+		i2c_r2(gspca_dev, 0x20, &value2);
+		value2 &= ~0x0003;
+		if (hflip)
+			value2 |= 0x0002;
+		if (vflip)
+			value2 |= 0x0001;
+		i2c_w2(gspca_dev, 0x20, value2);
+		break;
+	case SENSOR_HV7131R:
+		i2c_r1(gspca_dev, 0x01, &value);
+		value &= ~0x03;
+		if (vflip)
+			value |= 0x01;
+		if (hflip)
+			value |= 0x02;
+		i2c_w1(gspca_dev, 0x01, value);
+		break;
+	}
+}
+
+static void set_exposure(struct gspca_dev *gspca_dev, s32 expo)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u8 exp[8] = {sd->i2c_intf, sd->i2c_addr,
+				0x00, 0x00, 0x00, 0x00, 0x00, 0x10};
+	int expo2;
+
+	if (gspca_dev->streaming)
+		exp[7] = 0x1e;
+
+	switch (sd->sensor) {
+	case SENSOR_OV7660:
+	case SENSOR_OV7670:
+	case SENSOR_OV9655:
+	case SENSOR_OV9650:
+		if (expo > 547)
+			expo2 = 547;
+		else
+			expo2 = expo;
+		exp[0] |= (2 << 4);
+		exp[2] = 0x10;			/* AECH */
+		exp[3] = expo2 >> 2;
+		exp[7] = 0x10;
+		i2c_w(gspca_dev, exp);
+		exp[2] = 0x04;			/* COM1 */
+		exp[3] = expo2 & 0x0003;
+		exp[7] = 0x10;
+		i2c_w(gspca_dev, exp);
+		expo -= expo2;
+		exp[7] = 0x1e;
+		exp[0] |= (3 << 4);
+		exp[2] = 0x2d;			/* ADVFL & ADVFH */
+		exp[3] = expo;
+		exp[4] = expo >> 8;
+		break;
+	case SENSOR_MT9M001:
+	case SENSOR_MT9V112:
+	case SENSOR_MT9V011:
+		exp[0] |= (3 << 4);
+		exp[2] = 0x09;
+		exp[3] = expo >> 8;
+		exp[4] = expo;
+		break;
+	case SENSOR_HV7131R:
+		exp[0] |= (4 << 4);
+		exp[2] = 0x25;
+		exp[3] = expo >> 5;
+		exp[4] = expo << 3;
+		exp[5] = 0;
+		break;
+	default:
+		return;
+	}
+	i2c_w(gspca_dev, exp);
+}
+
+static void set_gain(struct gspca_dev *gspca_dev, s32 g)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u8 gain[8] = {sd->i2c_intf, sd->i2c_addr,
+				0x00, 0x00, 0x00, 0x00, 0x00, 0x10};
+
+	if (gspca_dev->streaming)
+		gain[7] = 0x15;		/* or 1d ? */
+
+	switch (sd->sensor) {
+	case SENSOR_OV7660:
+	case SENSOR_OV7670:
+	case SENSOR_SOI968:
+	case SENSOR_OV9655:
+	case SENSOR_OV9650:
+		gain[0] |= (2 << 4);
+		gain[3] = ov_gain[g];
+		break;
+	case SENSOR_MT9V011:
+		gain[0] |= (3 << 4);
+		gain[2] = 0x35;
+		gain[3] = micron1_gain[g] >> 8;
+		gain[4] = micron1_gain[g];
+		break;
+	case SENSOR_MT9V112:
+		gain[0] |= (3 << 4);
+		gain[2] = 0x2f;
+		gain[3] = micron1_gain[g] >> 8;
+		gain[4] = micron1_gain[g];
+		break;
+	case SENSOR_MT9M001:
+		gain[0] |= (3 << 4);
+		gain[2] = 0x2f;
+		gain[3] = micron2_gain[g] >> 8;
+		gain[4] = micron2_gain[g];
+		break;
+	case SENSOR_HV7131R:
+		gain[0] |= (2 << 4);
+		gain[2] = 0x30;
+		gain[3] = hv7131r_gain[g];
+		break;
+	default:
+		return;
+	}
+	i2c_w(gspca_dev, gain);
+}
+
+static void set_quality(struct gspca_dev *gspca_dev, s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	jpeg_set_qual(sd->jpeg_hdr, val);
+	reg_w1(gspca_dev, 0x1061, 0x01);	/* stop transfer */
+	reg_w1(gspca_dev, 0x10e0, sd->fmt | 0x20); /* write QTAB */
+	reg_w(gspca_dev, 0x1100, &sd->jpeg_hdr[JPEG_QT0_OFFSET], 64);
+	reg_w(gspca_dev, 0x1140, &sd->jpeg_hdr[JPEG_QT1_OFFSET], 64);
+	reg_w1(gspca_dev, 0x1061, 0x03);	/* restart transfer */
+	reg_w1(gspca_dev, 0x10e0, sd->fmt);
+	sd->fmt ^= 0x0c;			/* invert QTAB use + write */
+	reg_w1(gspca_dev, 0x10e0, sd->fmt);
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int sd_dbg_g_register(struct gspca_dev *gspca_dev,
+			struct v4l2_dbg_register *reg)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	reg->size = 1;
+	switch (reg->match.addr) {
+	case 0:
+		if (reg->reg < 0x1000 || reg->reg > 0x11ff)
+			return -EINVAL;
+		reg_r(gspca_dev, reg->reg, 1);
+		reg->val = gspca_dev->usb_buf[0];
+		return gspca_dev->usb_err;
+	case 1:
+		if (sd->sensor >= SENSOR_MT9V011 &&
+		    sd->sensor <= SENSOR_MT9M112) {
+			i2c_r2(gspca_dev, reg->reg, (u16 *) &reg->val);
+			reg->size = 2;
+		} else {
+			i2c_r1(gspca_dev, reg->reg, (u8 *) &reg->val);
+		}
+		return gspca_dev->usb_err;
+	}
+	return -EINVAL;
+}
+
+static int sd_dbg_s_register(struct gspca_dev *gspca_dev,
+			const struct v4l2_dbg_register *reg)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	switch (reg->match.addr) {
+	case 0:
+		if (reg->reg < 0x1000 || reg->reg > 0x11ff)
+			return -EINVAL;
+		reg_w1(gspca_dev, reg->reg, reg->val);
+		return gspca_dev->usb_err;
+	case 1:
+		if (sd->sensor >= SENSOR_MT9V011 &&
+		    sd->sensor <= SENSOR_MT9M112) {
+			i2c_w2(gspca_dev, reg->reg, reg->val);
+		} else {
+			i2c_w1(gspca_dev, reg->reg, reg->val);
+		}
+		return gspca_dev->usb_err;
+	}
+	return -EINVAL;
+}
+
+static int sd_chip_info(struct gspca_dev *gspca_dev,
+			struct v4l2_dbg_chip_info *chip)
+{
+	if (chip->match.addr > 1)
+		return -EINVAL;
+	if (chip->match.addr == 1)
+		strlcpy(chip->name, "sensor", sizeof(chip->name));
+	return 0;
+}
+#endif
+
+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->needs_full_bandwidth = 1;
+
+	sd->sensor = id->driver_info >> 8;
+	sd->i2c_addr = id->driver_info;
+	sd->flags = id->driver_info >> 16;
+	sd->i2c_intf = 0x80;			/* i2c 100 Kb/s */
+
+	switch (sd->sensor) {
+	case SENSOR_MT9M112:
+	case SENSOR_MT9M111:
+	case SENSOR_OV9650:
+	case SENSOR_SOI968:
+		cam->cam_mode = sxga_mode;
+		cam->nmodes = ARRAY_SIZE(sxga_mode);
+		break;
+	case SENSOR_MT9M001:
+		cam->cam_mode = mono_mode;
+		cam->nmodes = ARRAY_SIZE(mono_mode);
+		break;
+	case SENSOR_HV7131R:
+		sd->i2c_intf = 0x81;			/* i2c 400 Kb/s */
+		/* fall thru */
+	default:
+		cam->cam_mode = vga_mode;
+		cam->nmodes = ARRAY_SIZE(vga_mode);
+		break;
+	}
+
+	sd->old_step = 0;
+	sd->older_step = 0;
+	sd->exposure_step = 16;
+
+	INIT_WORK(&sd->work, qual_upd);
+
+	return 0;
+}
+
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct gspca_dev *gspca_dev =
+		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+	struct sd *sd = (struct sd *)gspca_dev;
+
+	gspca_dev->usb_err = 0;
+
+	if (!gspca_dev->streaming)
+		return 0;
+
+	switch (ctrl->id) {
+	/* color control cluster */
+	case V4L2_CID_BRIGHTNESS:
+		set_cmatrix(gspca_dev, sd->brightness->val,
+			sd->contrast->val, sd->saturation->val, sd->hue->val);
+		break;
+	case V4L2_CID_GAMMA:
+		set_gamma(gspca_dev, ctrl->val);
+		break;
+	/* blue/red balance cluster */
+	case V4L2_CID_BLUE_BALANCE:
+		set_redblue(gspca_dev, sd->blue->val, sd->red->val);
+		break;
+	/* h/vflip cluster */
+	case V4L2_CID_HFLIP:
+		set_hvflip(gspca_dev, sd->hflip->val, sd->vflip->val);
+		break;
+	/* standalone exposure control */
+	case V4L2_CID_EXPOSURE:
+		set_exposure(gspca_dev, ctrl->val);
+		break;
+	/* standalone gain control */
+	case V4L2_CID_GAIN:
+		set_gain(gspca_dev, ctrl->val);
+		break;
+	/* autogain + exposure or gain control cluster */
+	case V4L2_CID_AUTOGAIN:
+		if (sd->sensor == SENSOR_SOI968)
+			set_gain(gspca_dev, sd->gain->val);
+		else
+			set_exposure(gspca_dev, sd->exposure->val);
+		break;
+	case V4L2_CID_JPEG_COMPRESSION_QUALITY:
+		set_quality(gspca_dev, ctrl->val);
+		break;
+	}
+	return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+	.s_ctrl = sd_s_ctrl,
+};
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+	gspca_dev->vdev.ctrl_handler = hdl;
+	v4l2_ctrl_handler_init(hdl, 13);
+
+	sd->brightness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_BRIGHTNESS, 0, 255, 1, 127);
+	sd->contrast = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_CONTRAST, 0, 255, 1, 127);
+	sd->saturation = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_SATURATION, 0, 255, 1, 127);
+	sd->hue = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_HUE, -180, 180, 1, 0);
+
+	sd->gamma = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_GAMMA, 0, 255, 1, 0x10);
+
+	sd->blue = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_BLUE_BALANCE, 0, 127, 1, 0x28);
+	sd->red = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_RED_BALANCE, 0, 127, 1, 0x28);
+
+	if (sd->sensor != SENSOR_OV9655 && sd->sensor != SENSOR_SOI968 &&
+	    sd->sensor != SENSOR_OV7670 && sd->sensor != SENSOR_MT9M001 &&
+	    sd->sensor != SENSOR_MT9VPRB) {
+		sd->hflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_HFLIP, 0, 1, 1, 0);
+		sd->vflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_VFLIP, 0, 1, 1, 0);
+	}
+
+	if (sd->sensor != SENSOR_SOI968 && sd->sensor != SENSOR_MT9VPRB &&
+	    sd->sensor != SENSOR_MT9M112 && sd->sensor != SENSOR_MT9M111 &&
+	    sd->sensor != SENSOR_MT9V111)
+		sd->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_EXPOSURE, 0, 0x1780, 1, 0x33);
+
+	if (sd->sensor != SENSOR_MT9VPRB && sd->sensor != SENSOR_MT9M112 &&
+	    sd->sensor != SENSOR_MT9M111 && sd->sensor != SENSOR_MT9V111) {
+		sd->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_GAIN, 0, 28, 1, 0);
+		sd->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+	}
+
+	sd->jpegqual = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_JPEG_COMPRESSION_QUALITY, 50, 90, 1, 80);
+	if (hdl->error) {
+		pr_err("Could not initialize controls\n");
+		return hdl->error;
+	}
+
+	v4l2_ctrl_cluster(4, &sd->brightness);
+	v4l2_ctrl_cluster(2, &sd->blue);
+	if (sd->hflip)
+		v4l2_ctrl_cluster(2, &sd->hflip);
+	if (sd->autogain) {
+		if (sd->sensor == SENSOR_SOI968)
+			/* this sensor doesn't have the exposure control and
+			   autogain is clustered with gain instead. This works
+			   because sd->exposure == NULL. */
+			v4l2_ctrl_auto_cluster(3, &sd->autogain, 0, false);
+		else
+			/* Otherwise autogain is clustered with exposure. */
+			v4l2_ctrl_auto_cluster(2, &sd->autogain, 0, false);
+	}
+	return 0;
+}
+
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int i;
+	u8 value;
+	u8 i2c_init[9] = {
+		0x80, sd->i2c_addr, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03
+	};
+
+	for (i = 0; i < ARRAY_SIZE(bridge_init); i++) {
+		value = bridge_init[i][1];
+		reg_w(gspca_dev, bridge_init[i][0], &value, 1);
+		if (gspca_dev->usb_err < 0) {
+			pr_err("Device initialization failed\n");
+			return gspca_dev->usb_err;
+		}
+	}
+
+	if (sd->flags & LED_REVERSE)
+		reg_w1(gspca_dev, 0x1006, 0x00);
+	else
+		reg_w1(gspca_dev, 0x1006, 0x20);
+
+	reg_w(gspca_dev, 0x10c0, i2c_init, 9);
+	if (gspca_dev->usb_err < 0) {
+		pr_err("Device initialization failed\n");
+		return gspca_dev->usb_err;
+	}
+
+	switch (sd->sensor) {
+	case SENSOR_OV9650:
+		ov9650_init_sensor(gspca_dev);
+		if (gspca_dev->usb_err < 0)
+			break;
+		pr_info("OV9650 sensor detected\n");
+		break;
+	case SENSOR_OV9655:
+		ov9655_init_sensor(gspca_dev);
+		if (gspca_dev->usb_err < 0)
+			break;
+		pr_info("OV9655 sensor detected\n");
+		break;
+	case SENSOR_SOI968:
+		soi968_init_sensor(gspca_dev);
+		if (gspca_dev->usb_err < 0)
+			break;
+		pr_info("SOI968 sensor detected\n");
+		break;
+	case SENSOR_OV7660:
+		ov7660_init_sensor(gspca_dev);
+		if (gspca_dev->usb_err < 0)
+			break;
+		pr_info("OV7660 sensor detected\n");
+		break;
+	case SENSOR_OV7670:
+		ov7670_init_sensor(gspca_dev);
+		if (gspca_dev->usb_err < 0)
+			break;
+		pr_info("OV7670 sensor detected\n");
+		break;
+	case SENSOR_MT9VPRB:
+		mt9v_init_sensor(gspca_dev);
+		if (gspca_dev->usb_err < 0)
+			break;
+		pr_info("MT9VPRB sensor detected\n");
+		break;
+	case SENSOR_MT9M111:
+		mt9m111_init_sensor(gspca_dev);
+		if (gspca_dev->usb_err < 0)
+			break;
+		pr_info("MT9M111 sensor detected\n");
+		break;
+	case SENSOR_MT9M112:
+		mt9m112_init_sensor(gspca_dev);
+		if (gspca_dev->usb_err < 0)
+			break;
+		pr_info("MT9M112 sensor detected\n");
+		break;
+	case SENSOR_MT9M001:
+		mt9m001_init_sensor(gspca_dev);
+		if (gspca_dev->usb_err < 0)
+			break;
+		break;
+	case SENSOR_HV7131R:
+		hv7131r_init_sensor(gspca_dev);
+		if (gspca_dev->usb_err < 0)
+			break;
+		pr_info("HV7131R sensor detected\n");
+		break;
+	default:
+		pr_err("Unsupported sensor\n");
+		gspca_dev->usb_err = -ENODEV;
+	}
+	return gspca_dev->usb_err;
+}
+
+static void configure_sensor_output(struct gspca_dev *gspca_dev, int mode)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u8 value;
+
+	switch (sd->sensor) {
+	case SENSOR_SOI968:
+		if (mode & MODE_SXGA) {
+			i2c_w1(gspca_dev, 0x17, 0x1d);
+			i2c_w1(gspca_dev, 0x18, 0xbd);
+			i2c_w1(gspca_dev, 0x19, 0x01);
+			i2c_w1(gspca_dev, 0x1a, 0x81);
+			i2c_w1(gspca_dev, 0x12, 0x00);
+			sd->hstart = 140;
+			sd->vstart = 19;
+		} else {
+			i2c_w1(gspca_dev, 0x17, 0x13);
+			i2c_w1(gspca_dev, 0x18, 0x63);
+			i2c_w1(gspca_dev, 0x19, 0x01);
+			i2c_w1(gspca_dev, 0x1a, 0x79);
+			i2c_w1(gspca_dev, 0x12, 0x40);
+			sd->hstart = 60;
+			sd->vstart = 11;
+		}
+		break;
+	case SENSOR_OV9650:
+		if (mode & MODE_SXGA) {
+			i2c_w1(gspca_dev, 0x17, 0x1b);
+			i2c_w1(gspca_dev, 0x18, 0xbc);
+			i2c_w1(gspca_dev, 0x19, 0x01);
+			i2c_w1(gspca_dev, 0x1a, 0x82);
+			i2c_r1(gspca_dev, 0x12, &value);
+			i2c_w1(gspca_dev, 0x12, value & 0x07);
+		} else {
+			i2c_w1(gspca_dev, 0x17, 0x24);
+			i2c_w1(gspca_dev, 0x18, 0xc5);
+			i2c_w1(gspca_dev, 0x19, 0x00);
+			i2c_w1(gspca_dev, 0x1a, 0x3c);
+			i2c_r1(gspca_dev, 0x12, &value);
+			i2c_w1(gspca_dev, 0x12, (value & 0x7) | 0x40);
+		}
+		break;
+	case SENSOR_MT9M112:
+	case SENSOR_MT9M111:
+		if (mode & MODE_SXGA) {
+			i2c_w2(gspca_dev, 0xf0, 0x0002);
+			i2c_w2(gspca_dev, 0xc8, 0x970b);
+			i2c_w2(gspca_dev, 0xf0, 0x0000);
+		} else {
+			i2c_w2(gspca_dev, 0xf0, 0x0002);
+			i2c_w2(gspca_dev, 0xc8, 0x8000);
+			i2c_w2(gspca_dev, 0xf0, 0x0000);
+		}
+		break;
+	}
+}
+
+static int sd_isoc_init(struct gspca_dev *gspca_dev)
+{
+	struct usb_interface *intf;
+	u32 flags = gspca_dev->cam.cam_mode[(int)gspca_dev->curr_mode].priv;
+
+	/*
+	 * When using the SN9C20X_I420 fmt the sn9c20x needs more bandwidth
+	 * than our regular bandwidth calculations reserve, so we force the
+	 * use of a specific altsetting when using the SN9C20X_I420 fmt.
+	 */
+	if (!(flags & (MODE_RAW | MODE_JPEG))) {
+		intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface);
+
+		if (intf->num_altsetting != 9) {
+			pr_warn("sn9c20x camera with unknown number of alt "
+				"settings (%d), please report!\n",
+				intf->num_altsetting);
+			gspca_dev->alt = intf->num_altsetting;
+			return 0;
+		}
+
+		switch (gspca_dev->pixfmt.width) {
+		case 160: /* 160x120 */
+			gspca_dev->alt = 2;
+			break;
+		case 320: /* 320x240 */
+			gspca_dev->alt = 6;
+			break;
+		default:  /* >= 640x480 */
+			gspca_dev->alt = 9;
+			break;
+		}
+	}
+
+	return 0;
+}
+
+#define HW_WIN(mode, hstart, vstart) \
+((const u8 []){hstart, 0, vstart, 0, \
+(mode & MODE_SXGA ? 1280 >> 4 : 640 >> 4), \
+(mode & MODE_SXGA ? 1024 >> 3 : 480 >> 3)})
+
+#define CLR_WIN(width, height) \
+((const u8 [])\
+{0, width >> 2, 0, height >> 1,\
+((width >> 10) & 0x01) | ((height >> 8) & 0x6)})
+
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;
+	int width = gspca_dev->pixfmt.width;
+	int height = gspca_dev->pixfmt.height;
+	u8 fmt, scale = 0;
+
+	jpeg_define(sd->jpeg_hdr, height, width,
+			0x21);
+	jpeg_set_qual(sd->jpeg_hdr, v4l2_ctrl_g_ctrl(sd->jpegqual));
+
+	if (mode & MODE_RAW)
+		fmt = 0x2d;
+	else if (mode & MODE_JPEG)
+		fmt = 0x24;
+	else
+		fmt = 0x2f;	/* YUV 420 */
+	sd->fmt = fmt;
+
+	switch (mode & SCALE_MASK) {
+	case SCALE_1280x1024:
+		scale = 0xc0;
+		pr_info("Set 1280x1024\n");
+		break;
+	case SCALE_640x480:
+		scale = 0x80;
+		pr_info("Set 640x480\n");
+		break;
+	case SCALE_320x240:
+		scale = 0x90;
+		pr_info("Set 320x240\n");
+		break;
+	case SCALE_160x120:
+		scale = 0xa0;
+		pr_info("Set 160x120\n");
+		break;
+	}
+
+	configure_sensor_output(gspca_dev, mode);
+	reg_w(gspca_dev, 0x1100, &sd->jpeg_hdr[JPEG_QT0_OFFSET], 64);
+	reg_w(gspca_dev, 0x1140, &sd->jpeg_hdr[JPEG_QT1_OFFSET], 64);
+	reg_w(gspca_dev, 0x10fb, CLR_WIN(width, height), 5);
+	reg_w(gspca_dev, 0x1180, HW_WIN(mode, sd->hstart, sd->vstart), 6);
+	reg_w1(gspca_dev, 0x1189, scale);
+	reg_w1(gspca_dev, 0x10e0, fmt);
+
+	set_cmatrix(gspca_dev, v4l2_ctrl_g_ctrl(sd->brightness),
+			v4l2_ctrl_g_ctrl(sd->contrast),
+			v4l2_ctrl_g_ctrl(sd->saturation),
+			v4l2_ctrl_g_ctrl(sd->hue));
+	set_gamma(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma));
+	set_redblue(gspca_dev, v4l2_ctrl_g_ctrl(sd->blue),
+			v4l2_ctrl_g_ctrl(sd->red));
+	if (sd->gain)
+		set_gain(gspca_dev, v4l2_ctrl_g_ctrl(sd->gain));
+	if (sd->exposure)
+		set_exposure(gspca_dev, v4l2_ctrl_g_ctrl(sd->exposure));
+	if (sd->hflip)
+		set_hvflip(gspca_dev, v4l2_ctrl_g_ctrl(sd->hflip),
+				v4l2_ctrl_g_ctrl(sd->vflip));
+
+	reg_w1(gspca_dev, 0x1007, 0x20);
+	reg_w1(gspca_dev, 0x1061, 0x03);
+
+	/* if JPEG, prepare the compression quality update */
+	if (mode & MODE_JPEG) {
+		sd->pktsz = sd->npkt = 0;
+		sd->nchg = 0;
+		sd->work_thread =
+			create_singlethread_workqueue(KBUILD_MODNAME);
+	}
+
+	return gspca_dev->usb_err;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+	reg_w1(gspca_dev, 0x1007, 0x00);
+	reg_w1(gspca_dev, 0x1061, 0x01);
+}
+
+/* called on streamoff with alt==0 and on disconnect */
+/* the usb_lock is held at entry - restore on exit */
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (sd->work_thread != NULL) {
+		mutex_unlock(&gspca_dev->usb_lock);
+		destroy_workqueue(sd->work_thread);
+		mutex_lock(&gspca_dev->usb_lock);
+		sd->work_thread = NULL;
+	}
+}
+
+static void do_autoexposure(struct gspca_dev *gspca_dev, u16 avg_lum)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	s32 cur_exp = v4l2_ctrl_g_ctrl(sd->exposure);
+	s32 max = sd->exposure->maximum - sd->exposure_step;
+	s32 min = sd->exposure->minimum + sd->exposure_step;
+	s16 new_exp;
+
+	/*
+	 * some hardcoded values are present
+	 * like those for maximal/minimal exposure
+	 * and exposure steps
+	 */
+	if (avg_lum < MIN_AVG_LUM) {
+		if (cur_exp > max)
+			return;
+
+		new_exp = cur_exp + sd->exposure_step;
+		if (new_exp > max)
+			new_exp = max;
+		if (new_exp < min)
+			new_exp = min;
+		v4l2_ctrl_s_ctrl(sd->exposure, new_exp);
+
+		sd->older_step = sd->old_step;
+		sd->old_step = 1;
+
+		if (sd->old_step ^ sd->older_step)
+			sd->exposure_step /= 2;
+		else
+			sd->exposure_step += 2;
+	}
+	if (avg_lum > MAX_AVG_LUM) {
+		if (cur_exp < min)
+			return;
+		new_exp = cur_exp - sd->exposure_step;
+		if (new_exp > max)
+			new_exp = max;
+		if (new_exp < min)
+			new_exp = min;
+		v4l2_ctrl_s_ctrl(sd->exposure, new_exp);
+		sd->older_step = sd->old_step;
+		sd->old_step = 0;
+
+		if (sd->old_step ^ sd->older_step)
+			sd->exposure_step /= 2;
+		else
+			sd->exposure_step += 2;
+	}
+}
+
+static void do_autogain(struct gspca_dev *gspca_dev, u16 avg_lum)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	s32 cur_gain = v4l2_ctrl_g_ctrl(sd->gain);
+
+	if (avg_lum < MIN_AVG_LUM && cur_gain < sd->gain->maximum)
+		v4l2_ctrl_s_ctrl(sd->gain, cur_gain + 1);
+	if (avg_lum > MAX_AVG_LUM && cur_gain > sd->gain->minimum)
+		v4l2_ctrl_s_ctrl(sd->gain, cur_gain - 1);
+}
+
+static void sd_dqcallback(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int avg_lum;
+
+	if (sd->autogain == NULL || !v4l2_ctrl_g_ctrl(sd->autogain))
+		return;
+
+	avg_lum = atomic_read(&sd->avg_lum);
+	if (sd->sensor == SENSOR_SOI968)
+		do_autogain(gspca_dev, avg_lum);
+	else
+		do_autoexposure(gspca_dev, avg_lum);
+}
+
+/* JPEG quality update */
+/* This function is executed from a work queue. */
+static void qual_upd(struct work_struct *work)
+{
+	struct sd *sd = container_of(work, struct sd, work);
+	struct gspca_dev *gspca_dev = &sd->gspca_dev;
+	s32 qual = v4l2_ctrl_g_ctrl(sd->jpegqual);
+
+	/* To protect gspca_dev->usb_buf and gspca_dev->usb_err */
+	mutex_lock(&gspca_dev->usb_lock);
+	PDEBUG(D_STREAM, "qual_upd %d%%", qual);
+	gspca_dev->usb_err = 0;
+	set_quality(gspca_dev, qual);
+	mutex_unlock(&gspca_dev->usb_lock);
+}
+
+#if IS_ENABLED(CONFIG_INPUT)
+static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
+			u8 *data,		/* interrupt packet */
+			int len)		/* interrupt packet length */
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (!(sd->flags & HAS_NO_BUTTON) && len == 1) {
+		input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1);
+		input_sync(gspca_dev->input_dev);
+		input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
+		input_sync(gspca_dev->input_dev);
+		return 0;
+	}
+	return -EINVAL;
+}
+#endif
+
+/* check the JPEG compression */
+static void transfer_check(struct gspca_dev *gspca_dev,
+			u8 *data)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int new_qual, r;
+
+	new_qual = 0;
+
+	/* if USB error, discard the frame and decrease the quality */
+	if (data[6] & 0x08) {				/* USB FIFO full */
+		gspca_dev->last_packet_type = DISCARD_PACKET;
+		new_qual = -5;
+	} else {
+
+		/* else, compute the filling rate and a new JPEG quality */
+		r = (sd->pktsz * 100) /
+			(sd->npkt *
+				gspca_dev->urb[0]->iso_frame_desc[0].length);
+		if (r >= 85)
+			new_qual = -3;
+		else if (r < 75)
+			new_qual = 2;
+	}
+	if (new_qual != 0) {
+		sd->nchg += new_qual;
+		if (sd->nchg < -6 || sd->nchg >= 12) {
+			/* Note: we are in interrupt context, so we can't
+			   use v4l2_ctrl_g/s_ctrl here. Access the value
+			   directly instead. */
+			s32 curqual = sd->jpegqual->cur.val;
+			sd->nchg = 0;
+			new_qual += curqual;
+			if (new_qual < sd->jpegqual->minimum)
+				new_qual = sd->jpegqual->minimum;
+			else if (new_qual > sd->jpegqual->maximum)
+				new_qual = sd->jpegqual->maximum;
+			if (new_qual != curqual) {
+				sd->jpegqual->cur.val = new_qual;
+				queue_work(sd->work_thread, &sd->work);
+			}
+		}
+	} else {
+		sd->nchg = 0;
+	}
+	sd->pktsz = sd->npkt = 0;
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+			u8 *data,			/* isoc packet */
+			int len)			/* iso packet length */
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int avg_lum, is_jpeg;
+	static const u8 frame_header[] = {
+		0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96
+	};
+
+	is_jpeg = (sd->fmt & 0x03) == 0;
+	if (len >= 64 && memcmp(data, frame_header, 6) == 0) {
+		avg_lum = ((data[35] >> 2) & 3) |
+			   (data[20] << 2) |
+			   (data[19] << 10);
+		avg_lum += ((data[35] >> 4) & 3) |
+			    (data[22] << 2) |
+			    (data[21] << 10);
+		avg_lum += ((data[35] >> 6) & 3) |
+			    (data[24] << 2) |
+			    (data[23] << 10);
+		avg_lum += (data[36] & 3) |
+			   (data[26] << 2) |
+			   (data[25] << 10);
+		avg_lum += ((data[36] >> 2) & 3) |
+			    (data[28] << 2) |
+			    (data[27] << 10);
+		avg_lum += ((data[36] >> 4) & 3) |
+			    (data[30] << 2) |
+			    (data[29] << 10);
+		avg_lum += ((data[36] >> 6) & 3) |
+			    (data[32] << 2) |
+			    (data[31] << 10);
+		avg_lum += ((data[44] >> 4) & 3) |
+			    (data[34] << 2) |
+			    (data[33] << 10);
+		avg_lum >>= 9;
+		atomic_set(&sd->avg_lum, avg_lum);
+
+		if (is_jpeg)
+			transfer_check(gspca_dev, data);
+
+		gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+		len -= 64;
+		if (len == 0)
+			return;
+		data += 64;
+	}
+	if (gspca_dev->last_packet_type == LAST_PACKET) {
+		if (is_jpeg) {
+			gspca_frame_add(gspca_dev, FIRST_PACKET,
+				sd->jpeg_hdr, JPEG_HDR_SZ);
+			gspca_frame_add(gspca_dev, INTER_PACKET,
+				data, len);
+		} else {
+			gspca_frame_add(gspca_dev, FIRST_PACKET,
+				data, len);
+		}
+	} else {
+		/* if JPEG, count the packets and their size */
+		if (is_jpeg) {
+			sd->npkt++;
+			sd->pktsz += len;
+		}
+		gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+	}
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+	.name = KBUILD_MODNAME,
+	.config = sd_config,
+	.init = sd_init,
+	.init_controls = sd_init_controls,
+	.isoc_init = sd_isoc_init,
+	.start = sd_start,
+	.stopN = sd_stopN,
+	.stop0 = sd_stop0,
+	.pkt_scan = sd_pkt_scan,
+#if IS_ENABLED(CONFIG_INPUT)
+	.int_pkt_scan = sd_int_pkt_scan,
+#endif
+	.dq_callback = sd_dqcallback,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+	.set_register = sd_dbg_s_register,
+	.get_register = sd_dbg_g_register,
+	.get_chip_info = sd_chip_info,
+#endif
+};
+
+#define SN9C20X(sensor, i2c_addr, flags) \
+	.driver_info =  ((flags & 0xff) << 16) \
+			| (SENSOR_ ## sensor << 8) \
+			| (i2c_addr)
+
+static const struct usb_device_id device_table[] = {
+	{USB_DEVICE(0x0c45, 0x6240), SN9C20X(MT9M001, 0x5d, 0)},
+	{USB_DEVICE(0x0c45, 0x6242), SN9C20X(MT9M111, 0x5d, 0)},
+	{USB_DEVICE(0x0c45, 0x6248), SN9C20X(OV9655, 0x30, 0)},
+	{USB_DEVICE(0x0c45, 0x624c), SN9C20X(MT9M112, 0x5d, 0)},
+	{USB_DEVICE(0x0c45, 0x624e), SN9C20X(SOI968, 0x30, LED_REVERSE)},
+	{USB_DEVICE(0x0c45, 0x624f), SN9C20X(OV9650, 0x30,
+					     (FLIP_DETECT | HAS_NO_BUTTON))},
+	{USB_DEVICE(0x0c45, 0x6251), SN9C20X(OV9650, 0x30, 0)},
+	{USB_DEVICE(0x0c45, 0x6253), SN9C20X(OV9650, 0x30, 0)},
+	{USB_DEVICE(0x0c45, 0x6260), SN9C20X(OV7670, 0x21, 0)},
+	{USB_DEVICE(0x0c45, 0x6270), SN9C20X(MT9VPRB, 0x00, 0)},
+	{USB_DEVICE(0x0c45, 0x627b), SN9C20X(OV7660, 0x21, FLIP_DETECT)},
+	{USB_DEVICE(0x0c45, 0x627c), SN9C20X(HV7131R, 0x11, 0)},
+	{USB_DEVICE(0x0c45, 0x627f), SN9C20X(OV9650, 0x30, 0)},
+	{USB_DEVICE(0x0c45, 0x6280), SN9C20X(MT9M001, 0x5d, 0)},
+	{USB_DEVICE(0x0c45, 0x6282), SN9C20X(MT9M111, 0x5d, 0)},
+	{USB_DEVICE(0x0c45, 0x6288), SN9C20X(OV9655, 0x30, 0)},
+	{USB_DEVICE(0x0c45, 0x628c), SN9C20X(MT9M112, 0x5d, 0)},
+	{USB_DEVICE(0x0c45, 0x628e), SN9C20X(SOI968, 0x30, 0)},
+	{USB_DEVICE(0x0c45, 0x628f), SN9C20X(OV9650, 0x30, 0)},
+	{USB_DEVICE(0x0c45, 0x62a0), SN9C20X(OV7670, 0x21, 0)},
+	{USB_DEVICE(0x0c45, 0x62b0), SN9C20X(MT9VPRB, 0x00, 0)},
+	{USB_DEVICE(0x0c45, 0x62b3), SN9C20X(OV9655, 0x30, LED_REVERSE)},
+	{USB_DEVICE(0x0c45, 0x62bb), SN9C20X(OV7660, 0x21, LED_REVERSE)},
+	{USB_DEVICE(0x0c45, 0x62bc), SN9C20X(HV7131R, 0x11, 0)},
+	{USB_DEVICE(0x045e, 0x00f4), SN9C20X(OV9650, 0x30, 0)},
+	{USB_DEVICE(0x145f, 0x013d), SN9C20X(OV7660, 0x21, 0)},
+	{USB_DEVICE(0x0458, 0x7029), SN9C20X(HV7131R, 0x11, 0)},
+	{USB_DEVICE(0x0458, 0x7045), SN9C20X(MT9M112, 0x5d, LED_REVERSE)},
+	{USB_DEVICE(0x0458, 0x704a), SN9C20X(MT9M112, 0x5d, 0)},
+	{USB_DEVICE(0x0458, 0x704c), SN9C20X(MT9M112, 0x5d, 0)},
+	{USB_DEVICE(0xa168, 0x0610), SN9C20X(HV7131R, 0x11, 0)},
+	{USB_DEVICE(0xa168, 0x0611), SN9C20X(HV7131R, 0x11, 0)},
+	{USB_DEVICE(0xa168, 0x0613), SN9C20X(HV7131R, 0x11, 0)},
+	{USB_DEVICE(0xa168, 0x0618), SN9C20X(HV7131R, 0x11, 0)},
+	{USB_DEVICE(0xa168, 0x0614), SN9C20X(MT9M111, 0x5d, 0)},
+	{USB_DEVICE(0xa168, 0x0615), SN9C20X(MT9M111, 0x5d, 0)},
+	{USB_DEVICE(0xa168, 0x0617), SN9C20X(MT9M111, 0x5d, 0)},
+	{}
+};
+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 = KBUILD_MODNAME,
+	.id_table = device_table,
+	.probe = sd_probe,
+	.disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+	.suspend = gspca_suspend,
+	.resume = gspca_resume,
+	.reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/usb/gspca/sonixb.c b/drivers/media/usb/gspca/sonixb.c
new file mode 100644
index 0000000..6696b2e
--- /dev/null
+++ b/drivers/media/usb/gspca/sonixb.c
@@ -0,0 +1,1477 @@
+/*
+ *		sonix sn9c102 (bayer) library
+ *
+ * Copyright (C) 2009-2011 Jean-François Moine <http://moinejf.free.fr>
+ * Copyright (C) 2003 2004 Michel Xhaard mxhaard@magic.fr
+ * Add Pas106 Stefano Mozzi (C) 2004
+ *
+ * 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
+ */
+
+/* Some documentation on known sonixb registers:
+
+Reg	Use
+sn9c101 / sn9c102:
+0x10	high nibble red gain low nibble blue gain
+0x11	low nibble green gain
+sn9c103:
+0x05	red gain 0-127
+0x06	blue gain 0-127
+0x07	green gain 0-127
+all:
+0x08-0x0f i2c / 3wire registers
+0x12	hstart
+0x13	vstart
+0x15	hsize (hsize = register-value * 16)
+0x16	vsize (vsize = register-value * 16)
+0x17	bit 0 toggle compression quality (according to sn9c102 driver)
+0x18	bit 7 enables compression, bit 4-5 set image down scaling:
+	00 scale 1, 01 scale 1/2, 10, scale 1/4
+0x19	high-nibble is sensor clock divider, changes exposure on sensors which
+	use a clock generated by the bridge. Some sensors have their own clock.
+0x1c	auto_exposure area (for avg_lum) startx (startx = register-value * 32)
+0x1d	auto_exposure area (for avg_lum) starty (starty = register-value * 32)
+0x1e	auto_exposure area (for avg_lum) stopx (hsize = (0x1e - 0x1c) * 32)
+0x1f	auto_exposure area (for avg_lum) stopy (vsize = (0x1f - 0x1d) * 32)
+*/
+
+#define MODULE_NAME "sonixb"
+
+#include <linux/input.h>
+#include "gspca.h"
+
+MODULE_AUTHOR("Jean-François Moine <http://moinejf.free.fr>");
+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 v4l2_ctrl *brightness;
+	struct v4l2_ctrl *plfreq;
+
+	atomic_t avg_lum;
+	int prev_avg_lum;
+	int exposure_knee;
+	int header_read;
+	u8 header[12]; /* Header without sof marker */
+
+	unsigned char autogain_ignore_frames;
+	unsigned char frames_to_drop;
+
+	__u8 bridge;			/* Type of bridge */
+#define BRIDGE_101 0
+#define BRIDGE_102 0 /* We make no difference between 101 and 102 */
+#define BRIDGE_103 1
+
+	__u8 sensor;			/* Type of image sensor chip */
+#define SENSOR_HV7131D 0
+#define SENSOR_HV7131R 1
+#define SENSOR_OV6650 2
+#define SENSOR_OV7630 3
+#define SENSOR_PAS106 4
+#define SENSOR_PAS202 5
+#define SENSOR_TAS5110C 6
+#define SENSOR_TAS5110D 7
+#define SENSOR_TAS5130CXX 8
+	__u8 reg11;
+};
+
+typedef const __u8 sensor_init_t[8];
+
+struct sensor_data {
+	const __u8 *bridge_init;
+	sensor_init_t *sensor_init;
+	int sensor_init_size;
+	int flags;
+	__u8 sensor_addr;
+};
+
+/* sensor_data flags */
+#define F_SIF		0x01	/* sif or vga */
+
+/* priv field of struct v4l2_pix_format flags (do not use low nibble!) */
+#define MODE_RAW 0x10		/* raw bayer mode */
+#define MODE_REDUCED_SIF 0x20	/* vga mode (320x240 / 160x120) on sif cam */
+
+#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
+
+#define SENS(bridge, sensor, _flags, _sensor_addr) \
+{ \
+	.bridge_init = bridge, \
+	.sensor_init = sensor, \
+	.sensor_init_size = sizeof(sensor), \
+	.flags = _flags, .sensor_addr = _sensor_addr \
+}
+
+/* We calculate the autogain at the end of the transfer of a frame, at this
+   moment a frame with the old settings is being captured and transmitted. So
+   if we adjust the gain or exposure we must ignore atleast the next frame for
+   the new settings to come into effect before doing any other adjustments. */
+#define AUTOGAIN_IGNORE_FRAMES 1
+
+static const struct v4l2_pix_format vga_mode[] = {
+	{160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+		.bytesperline = 160,
+		.sizeimage = 160 * 120,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 2 | MODE_RAW},
+	{160, 120, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE,
+		.bytesperline = 160,
+		.sizeimage = 160 * 120 * 5 / 4,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 2},
+	{320, 240, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE,
+		.bytesperline = 320,
+		.sizeimage = 320 * 240 * 5 / 4,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 1},
+	{640, 480, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE,
+		.bytesperline = 640,
+		.sizeimage = 640 * 480 * 5 / 4,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 0},
+};
+static const struct v4l2_pix_format sif_mode[] = {
+	{160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+		.bytesperline = 160,
+		.sizeimage = 160 * 120,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 1 | MODE_RAW | MODE_REDUCED_SIF},
+	{160, 120, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE,
+		.bytesperline = 160,
+		.sizeimage = 160 * 120 * 5 / 4,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 1 | MODE_REDUCED_SIF},
+	{176, 144, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+		.bytesperline = 176,
+		.sizeimage = 176 * 144,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 1 | MODE_RAW},
+	{176, 144, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE,
+		.bytesperline = 176,
+		.sizeimage = 176 * 144 * 5 / 4,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 1},
+	{320, 240, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE,
+		.bytesperline = 320,
+		.sizeimage = 320 * 240 * 5 / 4,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 0 | MODE_REDUCED_SIF},
+	{352, 288, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE,
+		.bytesperline = 352,
+		.sizeimage = 352 * 288 * 5 / 4,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 0},
+};
+
+static const __u8 initHv7131d[] = {
+	0x04, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x80, 0x11, 0x00, 0x00, 0x00,
+	0x00, 0x00,
+	0x00, 0x00, 0x00, 0x02, 0x02, 0x00,
+	0x28, 0x1e, 0x60, 0x8e, 0x42,
+};
+static const __u8 hv7131d_sensor_init[][8] = {
+	{0xa0, 0x11, 0x01, 0x04, 0x00, 0x00, 0x00, 0x17},
+	{0xa0, 0x11, 0x02, 0x00, 0x00, 0x00, 0x00, 0x17},
+	{0xa0, 0x11, 0x28, 0x00, 0x00, 0x00, 0x00, 0x17},
+	{0xa0, 0x11, 0x30, 0x30, 0x00, 0x00, 0x00, 0x17}, /* reset level */
+	{0xa0, 0x11, 0x34, 0x02, 0x00, 0x00, 0x00, 0x17}, /* pixel bias volt */
+};
+
+static const __u8 initHv7131r[] = {
+	0x46, 0x77, 0x00, 0x04, 0x00, 0x00, 0x00, 0x80, 0x11, 0x00, 0x00, 0x00,
+	0x00, 0x00,
+	0x00, 0x00, 0x00, 0x02, 0x01, 0x00,
+	0x28, 0x1e, 0x60, 0x8a, 0x20,
+};
+static const __u8 hv7131r_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, 0x01, 0x01, 0x0a, 0x16, 0x12, 0x68, 0x8b,
+	0x10,
+};
+static const __u8 ov6650_sensor_init[][8] = {
+	/* Bright, contrast, etc are set through 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, 0x01, 0x01, 0x0a,				/* r11 .. r14 */
+	0x28, 0x1e,			/* H & V sizes     r15 .. r16 */
+	0x68, 0x8f, MCK_INIT1,				/* r17 .. r19 */
+};
+static const __u8 ov7630_sensor_init[][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, 0x5c, 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 initPas106[] = {
+	0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x40, 0x00, 0x00, 0x00,
+	0x00, 0x00,
+	0x00, 0x00, 0x00, 0x04, 0x01, 0x00,
+	0x16, 0x12, 0x24, COMP1, MCK_INIT1,
+};
+/* compression 0x86 mckinit1 0x2b */
+
+/* "Known" PAS106B registers:
+  0x02 clock divider
+  0x03 Variable framerate bits 4-11
+  0x04 Var framerate bits 0-3, one must leave the 4 msb's at 0 !!
+       The variable framerate control must never be set lower then 300,
+       which sets the framerate at 90 / reg02, otherwise vsync is lost.
+  0x05 Shutter Time Line Offset, this can be used as an exposure control:
+       0 = use full frame time, 255 = no exposure at all
+       Note this may never be larger then "var-framerate control" / 2 - 2.
+       When var-framerate control is < 514, no exposure is reached at the max
+       allowed value for the framerate control value, rather then at 255.
+  0x06 Shutter Time Pixel Offset, like reg05 this influences exposure, but
+       only a very little bit, leave at 0xcd
+  0x07 offset sign bit (bit0 1 > negative offset)
+  0x08 offset
+  0x09 Blue Gain
+  0x0a Green1 Gain
+  0x0b Green2 Gain
+  0x0c Red Gain
+  0x0e Global gain
+  0x13 Write 1 to commit settings to sensor
+*/
+
+static const __u8 pas106_sensor_init[][8] = {
+	/* Pixel Clock Divider 6 */
+	{ 0xa1, 0x40, 0x02, 0x04, 0x00, 0x00, 0x00, 0x14 },
+	/* Frame Time MSB (also seen as 0x12) */
+	{ 0xa1, 0x40, 0x03, 0x13, 0x00, 0x00, 0x00, 0x14 },
+	/* Frame Time LSB (also seen as 0x05) */
+	{ 0xa1, 0x40, 0x04, 0x06, 0x00, 0x00, 0x00, 0x14 },
+	/* Shutter Time Line Offset (also seen as 0x6d) */
+	{ 0xa1, 0x40, 0x05, 0x65, 0x00, 0x00, 0x00, 0x14 },
+	/* Shutter Time Pixel Offset (also seen as 0xb1) */
+	{ 0xa1, 0x40, 0x06, 0xcd, 0x00, 0x00, 0x00, 0x14 },
+	/* Black Level Subtract Sign (also seen 0x00) */
+	{ 0xa1, 0x40, 0x07, 0xc1, 0x00, 0x00, 0x00, 0x14 },
+	/* Black Level Subtract Level (also seen 0x01) */
+	{ 0xa1, 0x40, 0x08, 0x06, 0x00, 0x00, 0x00, 0x14 },
+	{ 0xa1, 0x40, 0x08, 0x06, 0x00, 0x00, 0x00, 0x14 },
+	/* Color Gain B Pixel 5 a */
+	{ 0xa1, 0x40, 0x09, 0x05, 0x00, 0x00, 0x00, 0x14 },
+	/* Color Gain G1 Pixel 1 5 */
+	{ 0xa1, 0x40, 0x0a, 0x04, 0x00, 0x00, 0x00, 0x14 },
+	/* Color Gain G2 Pixel 1 0 5 */
+	{ 0xa1, 0x40, 0x0b, 0x04, 0x00, 0x00, 0x00, 0x14 },
+	/* Color Gain R Pixel 3 1 */
+	{ 0xa1, 0x40, 0x0c, 0x05, 0x00, 0x00, 0x00, 0x14 },
+	/* Color GainH  Pixel */
+	{ 0xa1, 0x40, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x14 },
+	/* Global Gain */
+	{ 0xa1, 0x40, 0x0e, 0x0e, 0x00, 0x00, 0x00, 0x14 },
+	/* Contrast */
+	{ 0xa1, 0x40, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x14 },
+	/* H&V synchro polarity */
+	{ 0xa1, 0x40, 0x10, 0x06, 0x00, 0x00, 0x00, 0x14 },
+	/* ?default */
+	{ 0xa1, 0x40, 0x11, 0x06, 0x00, 0x00, 0x00, 0x14 },
+	/* DAC scale */
+	{ 0xa1, 0x40, 0x12, 0x06, 0x00, 0x00, 0x00, 0x14 },
+	/* ?default */
+	{ 0xa1, 0x40, 0x14, 0x02, 0x00, 0x00, 0x00, 0x14 },
+	/* Validate Settings */
+	{ 0xa1, 0x40, 0x13, 0x01, 0x00, 0x00, 0x00, 0x14 },
+};
+
+static const __u8 initPas202[] = {
+	0x44, 0x44, 0x21, 0x30, 0x00, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, 0x00,
+	0x00, 0x00,
+	0x00, 0x00, 0x00, 0x06, 0x03, 0x0a,
+	0x28, 0x1e, 0x20, 0x89, 0x20,
+};
+
+/* "Known" PAS202BCB registers:
+  0x02 clock divider
+  0x04 Variable framerate bits 6-11 (*)
+  0x05 Var framerate  bits 0-5, one must leave the 2 msb's at 0 !!
+  0x07 Blue Gain
+  0x08 Green Gain
+  0x09 Red Gain
+  0x0b offset sign bit (bit0 1 > negative offset)
+  0x0c offset
+  0x0e Unknown image is slightly brighter when bit 0 is 0, if reg0f is 0 too,
+       leave at 1 otherwise we get a jump in our exposure control
+  0x0f Exposure 0-255, 0 = use full frame time, 255 = no exposure at all
+  0x10 Master gain 0 - 31
+  0x11 write 1 to apply changes
+  (*) The variable framerate control must never be set lower then 500
+      which sets the framerate at 30 / reg02, otherwise vsync is lost.
+*/
+static const __u8 pas202_sensor_init[][8] = {
+	/* Set the clock divider to 4 -> 30 / 4 = 7.5 fps, we would like
+	   to set it lower, but for some reason the bridge starts missing
+	   vsync's then */
+	{0xa0, 0x40, 0x02, 0x04, 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, 0x01, 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},
+};
+
+static const __u8 initTas5110c[] = {
+	0x44, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x11, 0x00, 0x00, 0x00,
+	0x00, 0x00,
+	0x00, 0x00, 0x00, 0x45, 0x09, 0x0a,
+	0x16, 0x12, 0x60, 0x86, 0x2b,
+};
+/* Same as above, except a different hstart */
+static const __u8 initTas5110d[] = {
+	0x44, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x11, 0x00, 0x00, 0x00,
+	0x00, 0x00,
+	0x00, 0x00, 0x00, 0x41, 0x09, 0x0a,
+	0x16, 0x12, 0x60, 0x86, 0x2b,
+};
+/* tas5110c is 3 wire, tas5110d is 2 wire (regular i2c) */
+static const __u8 tas5110c_sensor_init[][8] = {
+	{0x30, 0x11, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x10},
+	{0x30, 0x11, 0x02, 0x20, 0xa9, 0x00, 0x00, 0x10},
+};
+/* Known TAS5110D registers
+ * reg02: gain, bit order reversed!! 0 == max gain, 255 == min gain
+ * reg03: bit3: vflip, bit4: ~hflip, bit7: ~gainboost (~ == inverted)
+ *        Note: writing reg03 seems to only work when written together with 02
+ */
+static const __u8 tas5110d_sensor_init[][8] = {
+	{0xa0, 0x61, 0x9a, 0xca, 0x00, 0x00, 0x00, 0x17}, /* reset */
+};
+
+static const __u8 initTas5130[] = {
+	0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x11, 0x00, 0x00, 0x00,
+	0x00, 0x00,
+	0x00, 0x00, 0x00, 0x68, 0x0c, 0x0a,
+	0x28, 0x1e, 0x60, COMP, MCK_INIT,
+};
+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},
+};
+
+static const struct sensor_data sensor_data[] = {
+	SENS(initHv7131d, hv7131d_sensor_init, 0, 0),
+	SENS(initHv7131r, hv7131r_sensor_init, 0, 0),
+	SENS(initOv6650, ov6650_sensor_init, F_SIF, 0x60),
+	SENS(initOv7630, ov7630_sensor_init, 0, 0x21),
+	SENS(initPas106, pas106_sensor_init, F_SIF, 0),
+	SENS(initPas202, pas202_sensor_init, 0, 0),
+	SENS(initTas5110c, tas5110c_sensor_init, F_SIF, 0),
+	SENS(initTas5110d, tas5110d_sensor_init, F_SIF, 0),
+	SENS(initTas5130, tas5130_sensor_init, 0, 0),
+};
+
+/* get one byte in gspca_dev->usb_buf */
+static void reg_r(struct gspca_dev *gspca_dev,
+		  __u16 value)
+{
+	int res;
+
+	if (gspca_dev->usb_err < 0)
+		return;
+
+	res = 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);
+
+	if (res < 0) {
+		dev_err(gspca_dev->v4l2_dev.dev,
+			"Error reading register %02x: %d\n", value, res);
+		gspca_dev->usb_err = res;
+	}
+}
+
+static void reg_w(struct gspca_dev *gspca_dev,
+		  __u16 value,
+		  const __u8 *buffer,
+		  int len)
+{
+	int res;
+
+	if (gspca_dev->usb_err < 0)
+		return;
+
+	memcpy(gspca_dev->usb_buf, buffer, len);
+	res = 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);
+
+	if (res < 0) {
+		dev_err(gspca_dev->v4l2_dev.dev,
+			"Error writing register %02x: %d\n", value, res);
+		gspca_dev->usb_err = res;
+	}
+}
+
+static void i2c_w(struct gspca_dev *gspca_dev, const u8 *buf)
+{
+	int retry = 60;
+
+	if (gspca_dev->usb_err < 0)
+		return;
+
+	/* is i2c ready */
+	reg_w(gspca_dev, 0x08, buf, 8);
+	while (retry--) {
+		if (gspca_dev->usb_err < 0)
+			return;
+		msleep(1);
+		reg_r(gspca_dev, 0x08);
+		if (gspca_dev->usb_buf[0] & 0x04) {
+			if (gspca_dev->usb_buf[0] & 0x08) {
+				dev_err(gspca_dev->v4l2_dev.dev,
+					"i2c error writing %8ph\n", buf);
+				gspca_dev->usb_err = -EIO;
+			}
+			return;
+		}
+	}
+
+	dev_err(gspca_dev->v4l2_dev.dev, "i2c write timeout\n");
+	gspca_dev->usb_err = -EIO;
+}
+
+static void i2c_w_vector(struct gspca_dev *gspca_dev,
+			const __u8 buffer[][8], int len)
+{
+	for (;;) {
+		if (gspca_dev->usb_err < 0)
+			return;
+		i2c_w(gspca_dev, *buffer);
+		len -= 8;
+		if (len <= 0)
+			break;
+		buffer++;
+	}
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	switch (sd->sensor) {
+	case  SENSOR_OV6650:
+	case  SENSOR_OV7630: {
+		__u8 i2cOV[] =
+			{0xa0, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x10};
+
+		/* change reg 0x06 */
+		i2cOV[1] = sensor_data[sd->sensor].sensor_addr;
+		i2cOV[3] = sd->brightness->val;
+		i2c_w(gspca_dev, i2cOV);
+		break;
+	}
+	case SENSOR_PAS106:
+	case SENSOR_PAS202: {
+		__u8 i2cpbright[] =
+			{0xb0, 0x40, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x16};
+		__u8 i2cpdoit[] =
+			{0xa0, 0x40, 0x11, 0x01, 0x00, 0x00, 0x00, 0x16};
+
+		/* PAS106 uses reg 7 and 8 instead of b and c */
+		if (sd->sensor == SENSOR_PAS106) {
+			i2cpbright[2] = 7;
+			i2cpdoit[2] = 0x13;
+		}
+
+		if (sd->brightness->val < 127) {
+			/* change reg 0x0b, signreg */
+			i2cpbright[3] = 0x01;
+			/* set reg 0x0c, offset */
+			i2cpbright[4] = 127 - sd->brightness->val;
+		} else
+			i2cpbright[4] = sd->brightness->val - 127;
+
+		i2c_w(gspca_dev, i2cpbright);
+		i2c_w(gspca_dev, i2cpdoit);
+		break;
+	}
+	default:
+		break;
+	}
+}
+
+static void setgain(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u8 gain = gspca_dev->gain->val;
+
+	switch (sd->sensor) {
+	case SENSOR_HV7131D: {
+		__u8 i2c[] =
+			{0xc0, 0x11, 0x31, 0x00, 0x00, 0x00, 0x00, 0x17};
+
+		i2c[3] = 0x3f - gain;
+		i2c[4] = 0x3f - gain;
+		i2c[5] = 0x3f - gain;
+
+		i2c_w(gspca_dev, i2c);
+		break;
+	}
+	case SENSOR_TAS5110C:
+	case SENSOR_TAS5130CXX: {
+		__u8 i2c[] =
+			{0x30, 0x11, 0x02, 0x20, 0x70, 0x00, 0x00, 0x10};
+
+		i2c[4] = 255 - gain;
+		i2c_w(gspca_dev, i2c);
+		break;
+	}
+	case SENSOR_TAS5110D: {
+		__u8 i2c[] = {
+			0xb0, 0x61, 0x02, 0x00, 0x10, 0x00, 0x00, 0x17 };
+		gain = 255 - gain;
+		/* The bits in the register are the wrong way around!! */
+		i2c[3] |= (gain & 0x80) >> 7;
+		i2c[3] |= (gain & 0x40) >> 5;
+		i2c[3] |= (gain & 0x20) >> 3;
+		i2c[3] |= (gain & 0x10) >> 1;
+		i2c[3] |= (gain & 0x08) << 1;
+		i2c[3] |= (gain & 0x04) << 3;
+		i2c[3] |= (gain & 0x02) << 5;
+		i2c[3] |= (gain & 0x01) << 7;
+		i2c_w(gspca_dev, i2c);
+		break;
+	}
+	case SENSOR_OV6650:
+	case SENSOR_OV7630: {
+		__u8 i2c[] = {0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10};
+
+		/*
+		 * The ov7630's gain is weird, at 32 the gain drops to the
+		 * same level as at 16, so skip 32-47 (of the 0-63 scale).
+		 */
+		if (sd->sensor == SENSOR_OV7630 && gain >= 32)
+			gain += 16;
+
+		i2c[1] = sensor_data[sd->sensor].sensor_addr;
+		i2c[3] = gain;
+		i2c_w(gspca_dev, i2c);
+		break;
+	}
+	case SENSOR_PAS106:
+	case SENSOR_PAS202: {
+		__u8 i2cpgain[] =
+			{0xa0, 0x40, 0x10, 0x00, 0x00, 0x00, 0x00, 0x15};
+		__u8 i2cpcolorgain[] =
+			{0xc0, 0x40, 0x07, 0x00, 0x00, 0x00, 0x00, 0x15};
+		__u8 i2cpdoit[] =
+			{0xa0, 0x40, 0x11, 0x01, 0x00, 0x00, 0x00, 0x16};
+
+		/* PAS106 uses different regs (and has split green gains) */
+		if (sd->sensor == SENSOR_PAS106) {
+			i2cpgain[2] = 0x0e;
+			i2cpcolorgain[0] = 0xd0;
+			i2cpcolorgain[2] = 0x09;
+			i2cpdoit[2] = 0x13;
+		}
+
+		i2cpgain[3] = gain;
+		i2cpcolorgain[3] = gain >> 1;
+		i2cpcolorgain[4] = gain >> 1;
+		i2cpcolorgain[5] = gain >> 1;
+		i2cpcolorgain[6] = gain >> 1;
+
+		i2c_w(gspca_dev, i2cpgain);
+		i2c_w(gspca_dev, i2cpcolorgain);
+		i2c_w(gspca_dev, i2cpdoit);
+		break;
+	}
+	default:
+		if (sd->bridge == BRIDGE_103) {
+			u8 buf[3] = { gain, gain, gain }; /* R, G, B */
+			reg_w(gspca_dev, 0x05, buf, 3);
+		} else {
+			u8 buf[2];
+			buf[0] = gain << 4 | gain; /* Red and blue */
+			buf[1] = gain; /* Green */
+			reg_w(gspca_dev, 0x10, buf, 2);
+		}
+	}
+}
+
+static void setexposure(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	switch (sd->sensor) {
+	case SENSOR_HV7131D: {
+		/* Note the datasheet wrongly says line mode exposure uses reg
+		   0x26 and 0x27, testing has shown 0x25 + 0x26 */
+		__u8 i2c[] = {0xc0, 0x11, 0x25, 0x00, 0x00, 0x00, 0x00, 0x17};
+		u16 reg = gspca_dev->exposure->val;
+
+		i2c[3] = reg >> 8;
+		i2c[4] = reg & 0xff;
+		i2c_w(gspca_dev, i2c);
+		break;
+	}
+	case SENSOR_TAS5110C:
+	case SENSOR_TAS5110D: {
+		/* 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 */
+		u8 reg = gspca_dev->exposure->val;
+
+		reg = (reg << 4) | 0x0b;
+		reg_w(gspca_dev, 0x19, &reg, 1);
+		break;
+	}
+	case SENSOR_OV6650:
+	case SENSOR_OV7630: {
+		/* 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 useful) 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, 0x00, 0x00, 0x00, 0x10};
+		int reg10, reg11, reg10_max;
+
+		/* 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. */
+		if (sd->sensor == SENSOR_OV6650) {
+			reg10_max = 0x4d;
+			i2c[4] = 0xc0; /* OV6650 needs non default vsync pol */
+		} else
+			reg10_max = 0x41;
+
+		reg11 = (15 * gspca_dev->exposure->val + 999) / 1000;
+		if (reg11 < 1)
+			reg11 = 1;
+		else if (reg11 > 16)
+			reg11 = 16;
+
+		/* In 640x480, if the reg11 has less than 4, the image is
+		   unstable (the bridge goes into a higher compression mode
+		   which we have not reverse engineered yet). */
+		if (gspca_dev->pixfmt.width == 640 && reg11 < 4)
+			reg11 = 4;
+
+		/* frame exposure time in ms = 1000 * reg11 / 30    ->
+		reg10 = (gspca_dev->exposure->val / 2) * reg10_max
+				/ (1000 * reg11 / 30) */
+		reg10 = (gspca_dev->exposure->val * 15 * 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 (gspca_dev->autogain->val && reg10 < 10)
+			reg10 = 10;
+		else if (reg10 > reg10_max)
+			reg10 = reg10_max;
+
+		/* Write reg 10 and reg11 low nibble */
+		i2c[1] = sensor_data[sd->sensor].sensor_addr;
+		i2c[3] = reg10;
+		i2c[4] |= reg11 - 1;
+
+		/* If register 11 didn't change, don't change it */
+		if (sd->reg11 == reg11)
+			i2c[0] = 0xa0;
+
+		i2c_w(gspca_dev, i2c);
+		if (gspca_dev->usb_err == 0)
+			sd->reg11 = reg11;
+		break;
+	}
+	case SENSOR_PAS202: {
+		__u8 i2cpframerate[] =
+			{0xb0, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x16};
+		__u8 i2cpexpo[] =
+			{0xa0, 0x40, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x16};
+		const __u8 i2cpdoit[] =
+			{0xa0, 0x40, 0x11, 0x01, 0x00, 0x00, 0x00, 0x16};
+		int framerate_ctrl;
+
+		/* The exposure knee for the autogain algorithm is 200
+		   (100 ms / 10 fps on other sensors), for values below this
+		   use the control for setting the partial frame expose time,
+		   above that use variable framerate. This way we run at max
+		   framerate (640x480@7.5 fps, 320x240@10fps) until the knee
+		   is reached. Using the variable framerate control above 200
+		   is better then playing around with both clockdiv + partial
+		   frame exposure times (like we are doing with the ov chips),
+		   as that sometimes leads to jumps in the exposure control,
+		   which are bad for auto exposure. */
+		if (gspca_dev->exposure->val < 200) {
+			i2cpexpo[3] = 255 - (gspca_dev->exposure->val * 255)
+						/ 200;
+			framerate_ctrl = 500;
+		} else {
+			/* The PAS202's exposure control goes from 0 - 4095,
+			   but anything below 500 causes vsync issues, so scale
+			   our 200-1023 to 500-4095 */
+			framerate_ctrl = (gspca_dev->exposure->val - 200)
+							* 1000 / 229 +  500;
+		}
+
+		i2cpframerate[3] = framerate_ctrl >> 6;
+		i2cpframerate[4] = framerate_ctrl & 0x3f;
+		i2c_w(gspca_dev, i2cpframerate);
+		i2c_w(gspca_dev, i2cpexpo);
+		i2c_w(gspca_dev, i2cpdoit);
+		break;
+	}
+	case SENSOR_PAS106: {
+		__u8 i2cpframerate[] =
+			{0xb1, 0x40, 0x03, 0x00, 0x00, 0x00, 0x00, 0x14};
+		__u8 i2cpexpo[] =
+			{0xa1, 0x40, 0x05, 0x00, 0x00, 0x00, 0x00, 0x14};
+		const __u8 i2cpdoit[] =
+			{0xa1, 0x40, 0x13, 0x01, 0x00, 0x00, 0x00, 0x14};
+		int framerate_ctrl;
+
+		/* For values below 150 use partial frame exposure, above
+		   that use framerate ctrl */
+		if (gspca_dev->exposure->val < 150) {
+			i2cpexpo[3] = 150 - gspca_dev->exposure->val;
+			framerate_ctrl = 300;
+		} else {
+			/* The PAS106's exposure control goes from 0 - 4095,
+			   but anything below 300 causes vsync issues, so scale
+			   our 150-1023 to 300-4095 */
+			framerate_ctrl = (gspca_dev->exposure->val - 150)
+						* 1000 / 230 + 300;
+		}
+
+		i2cpframerate[3] = framerate_ctrl >> 4;
+		i2cpframerate[4] = framerate_ctrl & 0x0f;
+		i2c_w(gspca_dev, i2cpframerate);
+		i2c_w(gspca_dev, i2cpexpo);
+		i2c_w(gspca_dev, i2cpdoit);
+		break;
+	}
+	default:
+		break;
+	}
+}
+
+static void setfreq(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (sd->sensor == SENSOR_OV6650 || sd->sensor == SENSOR_OV7630) {
+		/* Framerate adjust register for artificial light 50 hz flicker
+		   compensation, for the ov6650 this is identical to ov6630
+		   0x2b register, see ov6630 datasheet.
+		   0x4f / 0x8a -> (30 fps -> 25 fps), 0x00 -> no adjustment */
+		__u8 i2c[] = {0xa0, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x10};
+		switch (sd->plfreq->val) {
+		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] = sensor_data[sd->sensor].sensor_addr;
+		i2c_w(gspca_dev, i2c);
+	}
+}
+
+static void do_autogain(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int deadzone, desired_avg_lum, avg_lum;
+
+	avg_lum = atomic_read(&sd->avg_lum);
+	if (avg_lum == -1)
+		return;
+
+	if (sd->autogain_ignore_frames > 0) {
+		sd->autogain_ignore_frames--;
+		return;
+	}
+
+	/* SIF / VGA sensors have a different autoexposure area and thus
+	   different avg_lum values for the same picture brightness */
+	if (sensor_data[sd->sensor].flags & F_SIF) {
+		deadzone = 500;
+		/* SIF sensors tend to overexpose, so keep this small */
+		desired_avg_lum = 5000;
+	} else {
+		deadzone = 1500;
+		desired_avg_lum = 13000;
+	}
+
+	if (sd->brightness)
+		desired_avg_lum = sd->brightness->val * desired_avg_lum / 127;
+
+	if (gspca_dev->exposure->maximum < 500) {
+		if (gspca_coarse_grained_expo_autogain(gspca_dev, avg_lum,
+				desired_avg_lum, deadzone))
+			sd->autogain_ignore_frames = AUTOGAIN_IGNORE_FRAMES;
+	} else {
+		int gain_knee = (s32)gspca_dev->gain->maximum * 9 / 10;
+		if (gspca_expo_autogain(gspca_dev, avg_lum, desired_avg_lum,
+				deadzone, gain_knee, sd->exposure_knee))
+			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;
+
+	reg_r(gspca_dev, 0x00);
+	if (gspca_dev->usb_buf[0] != 0x10)
+		return -ENODEV;
+
+	/* copy the webcam info from the device id */
+	sd->sensor = id->driver_info >> 8;
+	sd->bridge = id->driver_info & 0xff;
+
+	cam = &gspca_dev->cam;
+	if (!(sensor_data[sd->sensor].flags & F_SIF)) {
+		cam->cam_mode = vga_mode;
+		cam->nmodes = ARRAY_SIZE(vga_mode);
+	} else {
+		cam->cam_mode = sif_mode;
+		cam->nmodes = ARRAY_SIZE(sif_mode);
+	}
+	cam->npkt = 36;			/* 36 packets per ISOC message */
+
+	return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+	const __u8 stop = 0x09; /* Disable stream turn of LED */
+
+	reg_w(gspca_dev, 0x01, &stop, 1);
+
+	return gspca_dev->usb_err;
+}
+
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct gspca_dev *gspca_dev =
+		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+	struct sd *sd = (struct sd *)gspca_dev;
+
+	gspca_dev->usb_err = 0;
+
+	if (ctrl->id == V4L2_CID_AUTOGAIN && ctrl->is_new && ctrl->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. */
+		gspca_dev->gain->val = gspca_dev->gain->default_value;
+		gspca_dev->exposure->val = gspca_dev->exposure->default_value;
+		sd->autogain_ignore_frames = AUTOGAIN_IGNORE_FRAMES;
+	}
+
+	if (!gspca_dev->streaming)
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_BRIGHTNESS:
+		setbrightness(gspca_dev);
+		break;
+	case V4L2_CID_AUTOGAIN:
+		if (gspca_dev->exposure->is_new || (ctrl->is_new && ctrl->val))
+			setexposure(gspca_dev);
+		if (gspca_dev->gain->is_new || (ctrl->is_new && ctrl->val))
+			setgain(gspca_dev);
+		break;
+	case V4L2_CID_POWER_LINE_FREQUENCY:
+		setfreq(gspca_dev);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+	.s_ctrl = sd_s_ctrl,
+};
+
+/* this function is called at probe time */
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+	gspca_dev->vdev.ctrl_handler = hdl;
+	v4l2_ctrl_handler_init(hdl, 5);
+
+	if (sd->sensor == SENSOR_OV6650 || sd->sensor == SENSOR_OV7630 ||
+	    sd->sensor == SENSOR_PAS106 || sd->sensor == SENSOR_PAS202)
+		sd->brightness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+					V4L2_CID_BRIGHTNESS, 0, 255, 1, 127);
+
+	/* Gain range is sensor dependent */
+	switch (sd->sensor) {
+	case SENSOR_OV6650:
+	case SENSOR_PAS106:
+	case SENSOR_PAS202:
+		gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+					V4L2_CID_GAIN, 0, 31, 1, 15);
+		break;
+	case SENSOR_OV7630:
+		gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+					V4L2_CID_GAIN, 0, 47, 1, 31);
+		break;
+	case SENSOR_HV7131D:
+		gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+					V4L2_CID_GAIN, 0, 63, 1, 31);
+		break;
+	case SENSOR_TAS5110C:
+	case SENSOR_TAS5110D:
+	case SENSOR_TAS5130CXX:
+		gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+					V4L2_CID_GAIN, 0, 255, 1, 127);
+		break;
+	default:
+		if (sd->bridge == BRIDGE_103) {
+			gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+						V4L2_CID_GAIN, 0, 127, 1, 63);
+		} else {
+			gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+						V4L2_CID_GAIN, 0, 15, 1, 7);
+		}
+	}
+
+	/* Exposure range is sensor dependent, and not all have exposure */
+	switch (sd->sensor) {
+	case SENSOR_HV7131D:
+		gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+					V4L2_CID_EXPOSURE, 0, 8191, 1, 482);
+		sd->exposure_knee = 964;
+		break;
+	case SENSOR_OV6650:
+	case SENSOR_OV7630:
+	case SENSOR_PAS106:
+	case SENSOR_PAS202:
+		gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+					V4L2_CID_EXPOSURE, 0, 1023, 1, 66);
+		sd->exposure_knee = 200;
+		break;
+	case SENSOR_TAS5110C:
+	case SENSOR_TAS5110D:
+		gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+					V4L2_CID_EXPOSURE, 2, 15, 1, 2);
+		break;
+	}
+
+	if (gspca_dev->exposure) {
+		gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+						V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+	}
+
+	if (sd->sensor == SENSOR_OV6650 || sd->sensor == SENSOR_OV7630)
+		sd->plfreq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
+			V4L2_CID_POWER_LINE_FREQUENCY,
+			V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0,
+			V4L2_CID_POWER_LINE_FREQUENCY_DISABLED);
+
+	if (hdl->error) {
+		pr_err("Could not initialize controls\n");
+		return hdl->error;
+	}
+
+	if (gspca_dev->autogain)
+		v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false);
+
+	return 0;
+}
+
+/* -- start the camera -- */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct cam *cam = &gspca_dev->cam;
+	int i, mode;
+	__u8 regs[0x31];
+
+	mode = cam->cam_mode[gspca_dev->curr_mode].priv & 0x07;
+	/* Copy registers 0x01 - 0x19 from the template */
+	memcpy(&regs[0x01], sensor_data[sd->sensor].bridge_init, 0x19);
+	/* Set the mode */
+	regs[0x18] |= mode << 4;
+
+	/* Set bridge gain to 1.0 */
+	if (sd->bridge == BRIDGE_103) {
+		regs[0x05] = 0x20; /* Red */
+		regs[0x06] = 0x20; /* Green */
+		regs[0x07] = 0x20; /* Blue */
+	} else {
+		regs[0x10] = 0x00; /* Red and blue */
+		regs[0x11] = 0x00; /* Green */
+	}
+
+	/* Setup pixel numbers and auto exposure window */
+	if (sensor_data[sd->sensor].flags & F_SIF) {
+		regs[0x1a] = 0x14; /* HO_SIZE 640, makes no sense */
+		regs[0x1b] = 0x0a; /* VO_SIZE 320, makes no sense */
+		regs[0x1c] = 0x02; /* AE H-start 64 */
+		regs[0x1d] = 0x02; /* AE V-start 64 */
+		regs[0x1e] = 0x09; /* AE H-end 288 */
+		regs[0x1f] = 0x07; /* AE V-end 224 */
+	} else {
+		regs[0x1a] = 0x1d; /* HO_SIZE 960, makes no sense */
+		regs[0x1b] = 0x10; /* VO_SIZE 512, makes no sense */
+		regs[0x1c] = 0x05; /* AE H-start 160 */
+		regs[0x1d] = 0x03; /* AE V-start 96 */
+		regs[0x1e] = 0x0f; /* AE H-end 480 */
+		regs[0x1f] = 0x0c; /* AE V-end 384 */
+	}
+
+	/* Setup the gamma table (only used with the sn9c103 bridge) */
+	for (i = 0; i < 16; i++)
+		regs[0x20 + i] = i * 16;
+	regs[0x20 + i] = 255;
+
+	/* Special cases where some regs depend on mode or bridge */
+	switch (sd->sensor) {
+	case SENSOR_TAS5130CXX:
+		/* FIXME / TESTME
+		   probably not mode specific at all most likely the upper
+		   nibble of 0x19 is exposure (clock divider) just as with
+		   the tas5110, we need someone to test this. */
+		regs[0x19] = mode ? 0x23 : 0x43;
+		break;
+	case SENSOR_OV7630:
+		/* FIXME / TESTME for some reason with the 101/102 bridge the
+		   clock is set to 12 Mhz (reg1 == 0x04), rather then 24.
+		   Also the hstart needs to go from 1 to 2 when using a 103,
+		   which is likely related. This does not seem right. */
+		if (sd->bridge == BRIDGE_103) {
+			regs[0x01] = 0x44; /* Select 24 Mhz clock */
+			regs[0x12] = 0x02; /* Set hstart to 2 */
+		}
+		break;
+	case SENSOR_PAS202:
+		/* For some unknown reason we need to increase hstart by 1 on
+		   the sn9c103, otherwise we get wrong colors (bayer shift). */
+		if (sd->bridge == BRIDGE_103)
+			regs[0x12] += 1;
+		break;
+	}
+	/* Disable compression when the raw bayer format has been selected */
+	if (cam->cam_mode[gspca_dev->curr_mode].priv & MODE_RAW)
+		regs[0x18] &= ~0x80;
+
+	/* Vga mode emulation on SIF sensor? */
+	if (cam->cam_mode[gspca_dev->curr_mode].priv & MODE_REDUCED_SIF) {
+		regs[0x12] += 16;	/* hstart adjust */
+		regs[0x13] += 24;	/* vstart adjust */
+		regs[0x15]  = 320 / 16; /* hsize */
+		regs[0x16]  = 240 / 16; /* vsize */
+	}
+
+	/* reg 0x01 bit 2 video transfert on */
+	reg_w(gspca_dev, 0x01, &regs[0x01], 1);
+	/* reg 0x17 SensorClk enable inv Clk 0x60 */
+	reg_w(gspca_dev, 0x17, &regs[0x17], 1);
+	/* Set the registers from the template */
+	reg_w(gspca_dev, 0x01, &regs[0x01],
+	      (sd->bridge == BRIDGE_103) ? 0x30 : 0x1f);
+
+	/* Init the sensor */
+	i2c_w_vector(gspca_dev, sensor_data[sd->sensor].sensor_init,
+			sensor_data[sd->sensor].sensor_init_size);
+
+	/* Mode / bridge specific sensor setup */
+	switch (sd->sensor) {
+	case SENSOR_PAS202: {
+		const __u8 i2cpclockdiv[] =
+			{0xa0, 0x40, 0x02, 0x03, 0x00, 0x00, 0x00, 0x10};
+		/* clockdiv from 4 to 3 (7.5 -> 10 fps) when in low res mode */
+		if (mode)
+			i2c_w(gspca_dev, i2cpclockdiv);
+		break;
+	    }
+	case SENSOR_OV7630:
+		/* FIXME / TESTME We should be able to handle this identical
+		   for the 101/102 and the 103 case */
+		if (sd->bridge == BRIDGE_103) {
+			const __u8 i2c[] = { 0xa0, 0x21, 0x13,
+					     0x80, 0x00, 0x00, 0x00, 0x10 };
+			i2c_w(gspca_dev, i2c);
+		}
+		break;
+	}
+	/* H_size V_size 0x28, 0x1e -> 640x480. 0x16, 0x12 -> 352x288 */
+	reg_w(gspca_dev, 0x15, &regs[0x15], 2);
+	/* compression register */
+	reg_w(gspca_dev, 0x18, &regs[0x18], 1);
+	/* H_start */
+	reg_w(gspca_dev, 0x12, &regs[0x12], 1);
+	/* V_START */
+	reg_w(gspca_dev, 0x13, &regs[0x13], 1);
+	/* reset 0x17 SensorClk enable inv Clk 0x60 */
+				/*fixme: ov7630 [17]=68 8f (+20 if 102)*/
+	reg_w(gspca_dev, 0x17, &regs[0x17], 1);
+	/*MCKSIZE ->3 */	/*fixme: not ov7630*/
+	reg_w(gspca_dev, 0x19, &regs[0x19], 1);
+	/* AE_STRX AE_STRY AE_ENDX AE_ENDY */
+	reg_w(gspca_dev, 0x1c, &regs[0x1c], 4);
+	/* Enable video transfert */
+	reg_w(gspca_dev, 0x01, &regs[0x01], 1);
+	/* Compression */
+	reg_w(gspca_dev, 0x18, &regs[0x18], 2);
+	msleep(20);
+
+	sd->reg11 = -1;
+
+	setgain(gspca_dev);
+	setbrightness(gspca_dev);
+	setexposure(gspca_dev);
+	setfreq(gspca_dev);
+
+	sd->frames_to_drop = 0;
+	sd->autogain_ignore_frames = 0;
+	gspca_dev->exp_too_high_cnt = 0;
+	gspca_dev->exp_too_low_cnt = 0;
+	atomic_set(&sd->avg_lum, -1);
+	return gspca_dev->usb_err;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+	sd_init(gspca_dev);
+}
+
+static u8* find_sof(struct gspca_dev *gspca_dev, u8 *data, int len)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int i, header_size = (sd->bridge == BRIDGE_103) ? 18 : 12;
+
+	/* 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
+	 */
+	for (i = 0; i < len; i++) {
+		switch (sd->header_read) {
+		case 0:
+			if (data[i] == 0xff)
+				sd->header_read++;
+			break;
+		case 1:
+			if (data[i] == 0xff)
+				sd->header_read++;
+			else
+				sd->header_read = 0;
+			break;
+		case 2:
+			if (data[i] == 0x00)
+				sd->header_read++;
+			else if (data[i] != 0xff)
+				sd->header_read = 0;
+			break;
+		case 3:
+			if (data[i] == 0xc4)
+				sd->header_read++;
+			else if (data[i] == 0xff)
+				sd->header_read = 1;
+			else
+				sd->header_read = 0;
+			break;
+		case 4:
+			if (data[i] == 0xc4)
+				sd->header_read++;
+			else if (data[i] == 0xff)
+				sd->header_read = 1;
+			else
+				sd->header_read = 0;
+			break;
+		case 5:
+			if (data[i] == 0x96)
+				sd->header_read++;
+			else if (data[i] == 0xff)
+				sd->header_read = 1;
+			else
+				sd->header_read = 0;
+			break;
+		default:
+			sd->header[sd->header_read - 6] = data[i];
+			sd->header_read++;
+			if (sd->header_read == header_size) {
+				sd->header_read = 0;
+				return data + i + 1;
+			}
+		}
+	}
+	return NULL;
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+			u8 *data,			/* isoc packet */
+			int len)			/* iso packet length */
+{
+	int fr_h_sz = 0, lum_offset = 0, len_after_sof = 0;
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct cam *cam = &gspca_dev->cam;
+	u8 *sof;
+
+	sof = find_sof(gspca_dev, data, len);
+	if (sof) {
+		if (sd->bridge == BRIDGE_103) {
+			fr_h_sz = 18;
+			lum_offset = 3;
+		} else {
+			fr_h_sz = 12;
+			lum_offset = 2;
+		}
+
+		len_after_sof = len - (sof - data);
+		len = (sof - data) - fr_h_sz;
+		if (len < 0)
+			len = 0;
+	}
+
+	if (cam->cam_mode[gspca_dev->curr_mode].priv & MODE_RAW) {
+		/* In raw mode we sometimes get some garbage after the frame
+		   ignore this */
+		int used;
+		int size = cam->cam_mode[gspca_dev->curr_mode].sizeimage;
+
+		used = gspca_dev->image_len;
+		if (used + len > size)
+			len = size - used;
+	}
+
+	gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+
+	if (sof) {
+		int  lum = sd->header[lum_offset] +
+			  (sd->header[lum_offset + 1] << 8);
+
+		/* When exposure changes midway a frame we
+		   get a lum of 0 in this case drop 2 frames
+		   as the frames directly after an exposure
+		   change have an unstable image. Sometimes lum
+		   *really* is 0 (cam used in low light with
+		   low exposure setting), so do not drop frames
+		   if the previous lum was 0 too. */
+		if (lum == 0 && sd->prev_avg_lum != 0) {
+			lum = -1;
+			sd->frames_to_drop = 2;
+			sd->prev_avg_lum = 0;
+		} else
+			sd->prev_avg_lum = lum;
+		atomic_set(&sd->avg_lum, lum);
+
+		if (sd->frames_to_drop)
+			sd->frames_to_drop--;
+		else
+			gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+
+		gspca_frame_add(gspca_dev, FIRST_PACKET, sof, len_after_sof);
+	}
+}
+
+#if IS_ENABLED(CONFIG_INPUT)
+static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
+			u8 *data,		/* interrupt packet data */
+			int len)		/* interrupt packet length */
+{
+	int ret = -EINVAL;
+
+	if (len == 1 && data[0] == 1) {
+		input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1);
+		input_sync(gspca_dev->input_dev);
+		input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
+		input_sync(gspca_dev->input_dev);
+		ret = 0;
+	}
+
+	return ret;
+}
+#endif
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+	.name = MODULE_NAME,
+	.config = sd_config,
+	.init = sd_init,
+	.init_controls = sd_init_controls,
+	.start = sd_start,
+	.stopN = sd_stopN,
+	.pkt_scan = sd_pkt_scan,
+	.dq_callback = do_autogain,
+#if IS_ENABLED(CONFIG_INPUT)
+	.int_pkt_scan = sd_int_pkt_scan,
+#endif
+};
+
+/* -- module initialisation -- */
+#define SB(sensor, bridge) \
+	.driver_info = (SENSOR_ ## sensor << 8) | BRIDGE_ ## bridge
+
+
+static const struct usb_device_id device_table[] = {
+	{USB_DEVICE(0x0c45, 0x6001), SB(TAS5110C, 102)}, /* TAS5110C1B */
+	{USB_DEVICE(0x0c45, 0x6005), SB(TAS5110C, 101)}, /* TAS5110C1B */
+	{USB_DEVICE(0x0c45, 0x6007), SB(TAS5110D, 101)}, /* TAS5110D */
+	{USB_DEVICE(0x0c45, 0x6009), SB(PAS106, 101)},
+	{USB_DEVICE(0x0c45, 0x600d), SB(PAS106, 101)},
+	{USB_DEVICE(0x0c45, 0x6011), SB(OV6650, 101)},
+	{USB_DEVICE(0x0c45, 0x6019), SB(OV7630, 101)},
+	{USB_DEVICE(0x0c45, 0x6024), SB(TAS5130CXX, 102)},
+	{USB_DEVICE(0x0c45, 0x6025), SB(TAS5130CXX, 102)},
+	{USB_DEVICE(0x0c45, 0x6027), SB(OV7630, 101)}, /* Genius Eye 310 */
+	{USB_DEVICE(0x0c45, 0x6028), SB(PAS202, 102)},
+	{USB_DEVICE(0x0c45, 0x6029), SB(PAS106, 102)},
+	{USB_DEVICE(0x0c45, 0x602a), SB(HV7131D, 102)},
+	/* {USB_DEVICE(0x0c45, 0x602b), SB(MI0343, 102)}, */
+	{USB_DEVICE(0x0c45, 0x602c), SB(OV7630, 102)},
+	{USB_DEVICE(0x0c45, 0x602d), SB(HV7131R, 102)},
+	{USB_DEVICE(0x0c45, 0x602e), SB(OV7630, 102)},
+	/* {USB_DEVICE(0x0c45, 0x6030), SB(MI03XX, 102)}, */ /* MI0343 MI0360 MI0330 */
+	/* {USB_DEVICE(0x0c45, 0x6082), SB(MI03XX, 103)}, */ /* MI0343 MI0360 */
+	{USB_DEVICE(0x0c45, 0x6083), SB(HV7131D, 103)},
+	{USB_DEVICE(0x0c45, 0x608c), SB(HV7131R, 103)},
+	/* {USB_DEVICE(0x0c45, 0x608e), SB(CISVF10, 103)}, */
+	{USB_DEVICE(0x0c45, 0x608f), SB(OV7630, 103)},
+	{USB_DEVICE(0x0c45, 0x60a8), SB(PAS106, 103)},
+	{USB_DEVICE(0x0c45, 0x60aa), SB(TAS5130CXX, 103)},
+	{USB_DEVICE(0x0c45, 0x60af), SB(PAS202, 103)},
+	{USB_DEVICE(0x0c45, 0x60b0), SB(OV7630, 103)},
+	{}
+};
+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,
+#ifdef CONFIG_PM
+	.suspend = gspca_suspend,
+	.resume = gspca_resume,
+	.reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/usb/gspca/sonixj.c b/drivers/media/usb/gspca/sonixj.c
new file mode 100644
index 0000000..fd1c870
--- /dev/null
+++ b/drivers/media/usb/gspca/sonixj.c
@@ -0,0 +1,2992 @@
+/*
+ * Sonix sn9c102p sn9c105 sn9c120 (jpeg) subdriver
+ *
+ * Copyright (C) 2009-2011 Jean-François Moine <http://moinejf.free.fr>
+ * Copyright (C) 2005 Michel Xhaard mxhaard@magic.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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "sonixj"
+
+#include <linux/input.h>
+#include "gspca.h"
+#include "jpeg.h"
+
+MODULE_AUTHOR("Jean-François Moine <http://moinejf.free.fr>");
+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 */
+
+	atomic_t avg_lum;
+	struct v4l2_ctrl *brightness;
+	struct v4l2_ctrl *contrast;
+	struct v4l2_ctrl *saturation;
+	struct { /* red/blue balance control cluster */
+		struct v4l2_ctrl *red_bal;
+		struct v4l2_ctrl *blue_bal;
+	};
+	struct { /* hflip/vflip control cluster */
+		struct v4l2_ctrl *vflip;
+		struct v4l2_ctrl *hflip;
+	};
+	struct v4l2_ctrl *gamma;
+	struct v4l2_ctrl *illum;
+	struct v4l2_ctrl *sharpness;
+	struct v4l2_ctrl *freq;
+	u32 exposure;
+
+	struct work_struct work;
+	struct workqueue_struct *work_thread;
+
+	u32 pktsz;			/* (used by pkt_scan) */
+	u16 npkt;
+	s8 nchg;
+	s8 short_mark;
+
+	u8 quality;			/* image quality */
+#define QUALITY_MIN 25
+#define QUALITY_MAX 90
+#define QUALITY_DEF 70
+
+	u8 reg01;
+	u8 reg17;
+	u8 reg18;
+	u8 flags;
+
+	s8 ag_cnt;
+#define AG_CNT_START 13
+
+	u8 bridge;
+#define BRIDGE_SN9C102P 0
+#define BRIDGE_SN9C105 1
+#define BRIDGE_SN9C110 2
+#define BRIDGE_SN9C120 3
+	u8 sensor;			/* Type of image sensor chip */
+	u8 i2c_addr;
+
+	u8 jpeg_hdr[JPEG_HDR_SZ];
+};
+enum sensors {
+	SENSOR_ADCM1700,
+	SENSOR_GC0307,
+	SENSOR_HV7131R,
+	SENSOR_MI0360,
+	SENSOR_MI0360B,
+	SENSOR_MO4000,
+	SENSOR_MT9V111,
+	SENSOR_OM6802,
+	SENSOR_OV7630,
+	SENSOR_OV7648,
+	SENSOR_OV7660,
+	SENSOR_PO1030,
+	SENSOR_PO2030N,
+	SENSOR_SOI768,
+	SENSOR_SP80708,
+};
+
+static void qual_upd(struct work_struct *work);
+
+/* device flags */
+#define F_PDN_INV	0x01	/* inverse pin S_PWR_DN / sn_xxx tables */
+#define F_ILLUM		0x02	/* presence of illuminator */
+
+/* sn9c1xx definitions */
+/* register 0x01 */
+#define S_PWR_DN	0x01	/* sensor power down */
+#define S_PDN_INV	0x02	/* inverse pin S_PWR_DN */
+#define V_TX_EN		0x04	/* video transfer enable */
+#define LED		0x08	/* output to pin LED */
+#define SCL_SEL_OD	0x20	/* open-drain mode */
+#define SYS_SEL_48M	0x40	/* system clock 0: 24MHz, 1: 48MHz */
+/* register 0x17 */
+#define MCK_SIZE_MASK	0x1f	/* sensor master clock */
+#define SEN_CLK_EN	0x20	/* enable sensor clock */
+#define DEF_EN		0x80	/* defect pixel by 0: soft, 1: hard */
+
+static const struct v4l2_pix_format cif_mode[] = {
+	{352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+		.bytesperline = 352,
+		.sizeimage = 352 * 288 * 4 / 8 + 590,
+		.colorspace = V4L2_COLORSPACE_JPEG,
+		.priv = 0},
+};
+static const struct v4l2_pix_format vga_mode[] = {
+	{160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+		.bytesperline = 160,
+		.sizeimage = 160 * 120 * 4 / 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,
+		/* Note 3 / 8 is not large enough, not even 5 / 8 is ?! */
+		.sizeimage = 640 * 480 * 3 / 4 + 590,
+		.colorspace = V4L2_COLORSPACE_JPEG,
+		.priv = 0},
+};
+
+static const u8 sn_adcm1700[0x1c] = {
+/*	reg0	reg1	reg2	reg3	reg4	reg5	reg6	reg7 */
+	0x00,	0x43,	0x60,	0x00,	0x1a,	0x00,	0x00,	0x00,
+/*	reg8	reg9	rega	regb	regc	regd	rege	regf */
+	0x80,	0x51,	0x00,	0x00,	0x00,	0x00,	0x00,	0x00,
+/*	reg10	reg11	reg12	reg13	reg14	reg15	reg16	reg17 */
+	0x03,	0x00,	0x05,	0x01,	0x05,	0x16,	0x12,	0x42,
+/*	reg18	reg19	reg1a	reg1b */
+	0x06,	0x00,	0x00,	0x00
+};
+
+static const u8 sn_gc0307[0x1c] = {
+/*	reg0	reg1	reg2	reg3	reg4	reg5	reg6	reg7 */
+	0x00,	0x61,	0x62,	0x00,	0x1a,	0x00,	0x00,	0x00,
+/*	reg8	reg9	rega	regb	regc	regd	rege	regf */
+	0x80,	0x21,	0x00,	0x00,	0x00,	0x00,	0x00,	0x00,
+/*	reg10	reg11	reg12	reg13	reg14	reg15	reg16	reg17 */
+	0x03,	0x00,	0x03,	0x01,	0x08,	0x28,	0x1e,	0x02,
+/*	reg18	reg19	reg1a	reg1b */
+	0x06,	0x00,	0x00,	0x00
+};
+
+static const u8 sn_hv7131[0x1c] = {
+/*	reg0	reg1	reg2	reg3	reg4	reg5	reg6	reg7 */
+	0x00,	0x03,	0x60,	0x00,	0x1a,	0x20,	0x20,	0x20,
+/*	reg8	reg9	rega	regb	regc	regd	rege	regf */
+	0x81,	0x11,	0x00,	0x00,	0x00,	0x00,	0x00,	0x00,
+/*	reg10	reg11	reg12	reg13	reg14	reg15	reg16	reg17 */
+	0x03,	0x00,	0x00,	0x01,	0x03,	0x28,	0x1e,	0x41,
+/*	reg18	reg19	reg1a	reg1b */
+	0x0a,	0x00,	0x00,	0x00
+};
+
+static const u8 sn_mi0360[0x1c] = {
+/*	reg0	reg1	reg2	reg3	reg4	reg5	reg6	reg7 */
+	0x00,	0x63,	0x40,	0x00,	0x1a,	0x20,	0x20,	0x20,
+/*	reg8	reg9	rega	regb	regc	regd	rege	regf */
+	0x81,	0x5d,	0x00,	0x00,	0x00,	0x00,	0x00,	0x00,
+/*	reg10	reg11	reg12	reg13	reg14	reg15	reg16	reg17 */
+	0x03,	0x00,	0x00,	0x02,	0x0a,	0x28,	0x1e,	0x61,
+/*	reg18	reg19	reg1a	reg1b */
+	0x06,	0x00,	0x00,	0x00
+};
+
+static const u8 sn_mi0360b[0x1c] = {
+/*	reg0	reg1	reg2	reg3	reg4	reg5	reg6	reg7 */
+	0x00,	0x61,	0x40,	0x00,	0x1a,	0x00,	0x00,	0x00,
+/*	reg8	reg9	rega	regb	regc	regd	rege	regf */
+	0x81,	0x5d,	0x00,	0x00,	0x00,	0x00,	0x00,	0x00,
+/*	reg10	reg11	reg12	reg13	reg14	reg15	reg16	reg17 */
+	0x03,	0x00,	0x00,	0x02,	0x0a,	0x28,	0x1e,	0x40,
+/*	reg18	reg19	reg1a	reg1b */
+	0x06,	0x00,	0x00,	0x00
+};
+
+static const u8 sn_mo4000[0x1c] = {
+/*	reg0	reg1	reg2	reg3	reg4	reg5	reg6	reg7 */
+	0x00,	0x23,	0x60,	0x00,	0x1a,	0x00,	0x20,	0x18,
+/*	reg8	reg9	rega	regb	regc	regd	rege	regf */
+	0x81,	0x21,	0x00,	0x00,	0x00,	0x00,	0x00,	0x00,
+/*	reg10	reg11	reg12	reg13	reg14	reg15	reg16	reg17 */
+	0x03,	 0x00,	0x0b,	0x0f,	0x14,	0x28,	0x1e,	0x40,
+/*	reg18	reg19	reg1a	reg1b */
+	0x08,	0x00,	0x00,	0x00
+};
+
+static const u8 sn_mt9v111[0x1c] = {
+/*	reg0	reg1	reg2	reg3	reg4	reg5	reg6	reg7 */
+	0x00,	0x61,	0x40,	0x00,	0x1a,	0x20,	0x20,	0x20,
+/*	reg8	reg9	rega	regb	regc	regd	rege	regf */
+	0x81,	0x5c,	0x00,	0x00,	0x00,	0x00,	0x00,	0x00,
+/*	reg10	reg11	reg12	reg13	reg14	reg15	reg16	reg17 */
+	0x03,	0x00,	0x00,	0x02,	0x1c,	0x28,	0x1e,	0x40,
+/*	reg18	reg19	reg1a	reg1b */
+	0x06,	0x00,	0x00,	0x00
+};
+
+static const u8 sn_om6802[0x1c] = {
+/*	reg0	reg1	reg2	reg3	reg4	reg5	reg6	reg7 */
+	0x00,	0x23,	0x72,	0x00,	0x1a,	0x20,	0x20,	0x19,
+/*	reg8	reg9	rega	regb	regc	regd	rege	regf */
+	0x80,	0x34,	0x00,	0x00,	0x00,	0x00,	0x00,	0x00,
+/*	reg10	reg11	reg12	reg13	reg14	reg15	reg16	reg17 */
+	0x03,	0x00,	0x51,	0x01,	0x00,	0x28,	0x1e,	0x40,
+/*	reg18	reg19	reg1a	reg1b */
+	0x05,	0x00,	0x00,	0x00
+};
+
+static const u8 sn_ov7630[0x1c] = {
+/*	reg0	reg1	reg2	reg3	reg4	reg5	reg6	reg7 */
+	0x00,	0x21,	0x40,	0x00,	0x1a,	0x00,	0x00,	0x00,
+/*	reg8	reg9	rega	regb	regc	regd	rege	regf */
+	0x81,	0x21,	0x00,	0x00,	0x00,	0x00,	0x00,	0x00,
+/*	reg10	reg11	reg12	reg13	reg14	reg15	reg16	reg17 */
+	0x03,	0x00,	0x04,	0x01,	0x0a,	0x28,	0x1e,	0xc2,
+/*	reg18	reg19	reg1a	reg1b */
+	0x0b,	0x00,	0x00,	0x00
+};
+
+static const u8 sn_ov7648[0x1c] = {
+/*	reg0	reg1	reg2	reg3	reg4	reg5	reg6	reg7 */
+	0x00,	0x63,	0x40,	0x00,	0x1a,	0x20,	0x20,	0x20,
+/*	reg8	reg9	rega	regb	regc	regd	rege	regf */
+	0x81,	0x21,	0x00,	0x00,	0x00,	0x00,	0x00,	0x00,
+/*	reg10	reg11	reg12	reg13	reg14	reg15	reg16	reg17 */
+	0x03,	0x00,	0x00,	0x01,	0x00,	0x28,	0x1e,	0x00,
+/*	reg18	reg19	reg1a	reg1b */
+	0x0b,	0x00,	0x00,	0x00
+};
+
+static const u8 sn_ov7660[0x1c] = {
+/*	reg0	reg1	reg2	reg3	reg4	reg5	reg6	reg7 */
+	0x00,	0x61,	0x40,	0x00,	0x1a,	0x00,	0x00,	0x00,
+/*	reg8	reg9	rega	regb	regc	regd	rege	regf */
+	0x81,	0x21,	0x00,	0x00,	0x00,	0x00,	0x00,	0x00,
+/*	reg10	reg11	reg12	reg13	reg14	reg15	reg16	reg17 */
+	0x03,	0x00,	0x01,	0x01,	0x08,	0x28,	0x1e,	0x20,
+/*	reg18	reg19	reg1a	reg1b */
+	0x07,	0x00,	0x00,	0x00
+};
+
+static const u8 sn_po1030[0x1c] = {
+/*	reg0	reg1	reg2	reg3	reg4	reg5	reg6	reg7 */
+	0x00,	0x21,	0x62,	0x00,	0x1a,	0x20,	0x20,	0x20,
+/*	reg8	reg9	rega	regb	regc	regd	rege	regf */
+	0x81,	0x6e,	0x00,	0x00,	0x00,	0x00,	0x00,	0x00,
+/*	reg10	reg11	reg12	reg13	reg14	reg15	reg16	reg17 */
+	0x03,	0x00,	0x00,	0x06,	0x06,	0x28,	0x1e,	0x00,
+/*	reg18	reg19	reg1a	reg1b */
+	0x07,	0x00,	0x00,	0x00
+};
+
+static const u8 sn_po2030n[0x1c] = {
+/*	reg0	reg1	reg2	reg3	reg4	reg5	reg6	reg7 */
+	0x00,	0x63,	0x40,	0x00,	0x1a,	0x00,	0x00,	0x00,
+/*	reg8	reg9	rega	regb	regc	regd	rege	regf */
+	0x81,	0x6e,	0x00,	0x00,	0x00,	0x00,	0x00,	0x00,
+/*	reg10	reg11	reg12	reg13	reg14	reg15	reg16	reg17 */
+	0x03,	0x00,	0x00,	0x01,	0x14,	0x28,	0x1e,	0x00,
+/*	reg18	reg19	reg1a	reg1b */
+	0x07,	0x00,	0x00,	0x00
+};
+
+static const u8 sn_soi768[0x1c] = {
+/*	reg0	reg1	reg2	reg3	reg4	reg5	reg6	reg7 */
+	0x00,	0x21,	0x40,	0x00,	0x1a,	0x00,	0x00,	0x00,
+/*	reg8	reg9	rega	regb	regc	regd	rege	regf */
+	0x81,	0x21,	0x00,	0x00,	0x00,	0x00,	0x00,	0x00,
+/*	reg10	reg11	reg12	reg13	reg14	reg15	reg16	reg17 */
+	0x03,	0x00,	0x00,	0x01,	0x08,	0x28,	0x1e,	0x00,
+/*	reg18	reg19	reg1a	reg1b */
+	0x07,	0x00,	0x00,	0x00
+};
+
+static const u8 sn_sp80708[0x1c] = {
+/*	reg0	reg1	reg2	reg3	reg4	reg5	reg6	reg7 */
+	0x00,	0x63,	0x60,	0x00,	0x1a,	0x20,	0x20,	0x20,
+/*	reg8	reg9	rega	regb	regc	regd	rege	regf */
+	0x81,	0x18,	0x00,	0x00,	0x00,	0x00,	0x00,	0x00,
+/*	reg10	reg11	reg12	reg13	reg14	reg15	reg16	reg17 */
+	0x03,	0x00,	0x00,	0x03,	0x04,	0x28,	0x1e,	0x00,
+/*	reg18	reg19	reg1a	reg1b */
+	0x07,	0x00,	0x00,	0x00
+};
+
+/* sequence specific to the sensors - !! index = SENSOR_xxx */
+static const u8 *sn_tb[] = {
+[SENSOR_ADCM1700] =	sn_adcm1700,
+[SENSOR_GC0307] =	sn_gc0307,
+[SENSOR_HV7131R] =	sn_hv7131,
+[SENSOR_MI0360] =	sn_mi0360,
+[SENSOR_MI0360B] =	sn_mi0360b,
+[SENSOR_MO4000] =	sn_mo4000,
+[SENSOR_MT9V111] =	sn_mt9v111,
+[SENSOR_OM6802] =	sn_om6802,
+[SENSOR_OV7630] =	sn_ov7630,
+[SENSOR_OV7648] =	sn_ov7648,
+[SENSOR_OV7660] =	sn_ov7660,
+[SENSOR_PO1030] =	sn_po1030,
+[SENSOR_PO2030N] =	sn_po2030n,
+[SENSOR_SOI768] =	sn_soi768,
+[SENSOR_SP80708] =	sn_sp80708,
+};
+
+/* default gamma table */
+static const u8 gamma_def[17] = {
+	0x00, 0x2d, 0x46, 0x5a, 0x6c, 0x7c, 0x8b, 0x99,
+	0xa6, 0xb2, 0xbf, 0xca, 0xd5, 0xe0, 0xeb, 0xf5, 0xff
+};
+/* gamma for sensor ADCM1700 */
+static const u8 gamma_spec_0[17] = {
+	0x0f, 0x39, 0x5a, 0x74, 0x86, 0x95, 0xa6, 0xb4,
+	0xbd, 0xc4, 0xcc, 0xd4, 0xd5, 0xde, 0xe4, 0xed, 0xf5
+};
+/* gamma for sensors HV7131R and MT9V111 */
+static const u8 gamma_spec_1[17] = {
+	0x08, 0x3a, 0x52, 0x65, 0x75, 0x83, 0x91, 0x9d,
+	0xa9, 0xb4, 0xbe, 0xc8, 0xd2, 0xdb, 0xe4, 0xed, 0xf5
+};
+/* gamma for sensor GC0307 */
+static const u8 gamma_spec_2[17] = {
+	0x14, 0x37, 0x50, 0x6a, 0x7c, 0x8d, 0x9d, 0xab,
+	0xb5, 0xbf, 0xc2, 0xcb, 0xd1, 0xd6, 0xdb, 0xe1, 0xeb
+};
+/* gamma for sensor SP80708 */
+static const u8 gamma_spec_3[17] = {
+	0x0a, 0x2d, 0x4e, 0x68, 0x7d, 0x8f, 0x9f, 0xab,
+	0xb7, 0xc2, 0xcc, 0xd3, 0xd8, 0xde, 0xe2, 0xe5, 0xe6
+};
+
+/* color matrix and offsets */
+static const u8 reg84[] = {
+	0x14, 0x00, 0x27, 0x00, 0x07, 0x00,	/* YR YG YB gains */
+	0xe8, 0x0f, 0xda, 0x0f, 0x40, 0x00,	/* UR UG UB */
+	0x3e, 0x00, 0xcd, 0x0f, 0xf7, 0x0f,	/* VR VG VB */
+	0x00, 0x00, 0x00			/* YUV offsets */
+};
+
+#define DELAY	0xdd
+
+static const u8 adcm1700_sensor_init[][8] = {
+	{0xa0, 0x51, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xb0, 0x51, 0x04, 0x08, 0x00, 0x00, 0x00, 0x10},	/* reset */
+	{DELAY, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+	{0xb0, 0x51, 0x04, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{DELAY, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+	{0xb0, 0x51, 0x0c, 0xe0, 0x2e, 0x00, 0x00, 0x10},
+	{0xb0, 0x51, 0x10, 0x02, 0x02, 0x00, 0x00, 0x10},
+	{0xb0, 0x51, 0x14, 0x0e, 0x0e, 0x00, 0x00, 0x10},
+	{0xb0, 0x51, 0x1c, 0x00, 0x80, 0x00, 0x00, 0x10},
+	{0xb0, 0x51, 0x20, 0x01, 0x00, 0x00, 0x00, 0x10},
+	{DELAY, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+	{0xb0, 0x51, 0x04, 0x04, 0x00, 0x00, 0x00, 0x10},
+	{DELAY, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+	{0xb0, 0x51, 0x04, 0x01, 0x00, 0x00, 0x00, 0x10},
+	{0xa0, 0x51, 0xfe, 0x10, 0x00, 0x00, 0x00, 0x10},
+	{0xb0, 0x51, 0x14, 0x01, 0x00, 0x00, 0x00, 0x10},
+	{0xb0, 0x51, 0x32, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{}
+};
+static const u8 adcm1700_sensor_param1[][8] = {
+	{0xb0, 0x51, 0x26, 0xf9, 0x01, 0x00, 0x00, 0x10},	/* exposure? */
+	{0xd0, 0x51, 0x1e, 0x8e, 0x8e, 0x8e, 0x8e, 0x10},
+
+	{0xa0, 0x51, 0xfe, 0x01, 0x00, 0x00, 0x00, 0x10},
+	{0xb0, 0x51, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10},
+	{0xa0, 0x51, 0xfe, 0x10, 0x00, 0x00, 0x00, 0x10},
+	{0xb0, 0x51, 0x32, 0x00, 0x72, 0x00, 0x00, 0x10},
+	{0xd0, 0x51, 0x1e, 0xbe, 0xd7, 0xe8, 0xbe, 0x10},	/* exposure? */
+
+	{0xa0, 0x51, 0xfe, 0x01, 0x00, 0x00, 0x00, 0x10},
+	{0xb0, 0x51, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10},
+	{0xa0, 0x51, 0xfe, 0x10, 0x00, 0x00, 0x00, 0x10},
+	{0xb0, 0x51, 0x32, 0x00, 0xa2, 0x00, 0x00, 0x10},
+	{}
+};
+static const u8 gc0307_sensor_init[][8] = {
+	{0xa0, 0x21, 0x43, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xa0, 0x21, 0x44, 0xa2, 0x00, 0x00, 0x00, 0x10},
+	{0xa0, 0x21, 0x01, 0x6a, 0x00, 0x00, 0x00, 0x10},
+	{0xa0, 0x21, 0x02, 0x70, 0x00, 0x00, 0x00, 0x10},
+	{0xa0, 0x21, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xa0, 0x21, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xa0, 0x21, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xa0, 0x21, 0x11, 0x05, 0x00, 0x00, 0x00, 0x10},
+	{0xa0, 0x21, 0x05, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xa0, 0x21, 0x06, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xa0, 0x21, 0x07, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xa0, 0x21, 0x08, 0x02, 0x00, 0x00, 0x00, 0x10},
+	{0xa0, 0x21, 0x09, 0x01, 0x00, 0x00, 0x00, 0x10},
+	{0xa0, 0x21, 0x0a, 0xe8, 0x00, 0x00, 0x00, 0x10},
+	{0xa0, 0x21, 0x0b, 0x02, 0x00, 0x00, 0x00, 0x10},
+	{0xa0, 0x21, 0x0c, 0x80, 0x00, 0x00, 0x00, 0x10},
+	{0xa0, 0x21, 0x0d, 0x22, 0x00, 0x00, 0x00, 0x10},
+	{0xa0, 0x21, 0x0e, 0x02, 0x00, 0x00, 0x00, 0x10},
+	{0xa0, 0x21, 0x0f, 0xb2, 0x00, 0x00, 0x00, 0x10},
+	{0xa0, 0x21, 0x12, 0x70, 0x00, 0x00, 0x00, 0x10},
+	{DELAY, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*delay 10ms*/
+	{0xa0, 0x21, 0x13, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xa0, 0x21, 0x15, 0xb8, 0x00, 0x00, 0x00, 0x10},
+	{0xa0, 0x21, 0x16, 0x13, 0x00, 0x00, 0x00, 0x10},
+	{0xa0, 0x21, 0x17, 0x52, 0x00, 0x00, 0x00, 0x10},
+	{0xa0, 0x21, 0x18, 0x50, 0x00, 0x00, 0x00, 0x10},
+	{0xa0, 0x21, 0x1e, 0x0d, 0x00, 0x00, 0x00, 0x10},
+	{0xa0, 0x21, 0x1f, 0x32, 0x00, 0x00, 0x00, 0x10},
+	{0xa0, 0x21, 0x61, 0x90, 0x00, 0x00, 0x00, 0x10},
+	{0xa0, 0x21, 0x63, 0x70, 0x00, 0x00, 0x00, 0x10},
+	{0xa0, 0x21, 0x65, 0x98, 0x00, 0x00, 0x00, 0x10},
+	{0xa0, 0x21, 0x67, 0x90, 0x00, 0x00, 0x00, 0x10},
+	{0xa0, 0x21, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xa0, 0x21, 0x04, 0x96, 0x00, 0x00, 0x00, 0x10},
+	{0xa0, 0x21, 0x45, 0x27, 0x00, 0x00, 0x00, 0x10},
+	{0xa0, 0x21, 0x47, 0x2c, 0x00, 0x00, 0x00, 0x10},
+	{0xa0, 0x21, 0x43, 0x47, 0x00, 0x00, 0x00, 0x10},
+	{0xa0, 0x21, 0x44, 0xd8, 0x00, 0x00, 0x00, 0x10},
+	{}
+};
+static const u8 gc0307_sensor_param1[][8] = {
+	{0xa0, 0x21, 0x68, 0x13, 0x00, 0x00, 0x00, 0x10},
+	{0xd0, 0x21, 0x61, 0x80, 0x00, 0x80, 0x00, 0x10},
+	{0xc0, 0x21, 0x65, 0x80, 0x00, 0x80, 0x00, 0x10},
+	{0xc0, 0x21, 0x63, 0xa0, 0x00, 0xa6, 0x00, 0x10},
+/*param3*/
+	{0xa0, 0x21, 0x01, 0x6e, 0x00, 0x00, 0x00, 0x10},
+	{0xa0, 0x21, 0x02, 0x88, 0x00, 0x00, 0x00, 0x10},
+	{}
+};
+
+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},
+	{0xa1, 0x11, 0x01, 0x18, 0x00, 0x00, 0x00, 0x10},
+							/* set sensor clock */
+	{}
+};
+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 mi0360b_sensor_init[][8] = {
+	{0xb1, 0x5d, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10},
+	{0xb1, 0x5d, 0x0d, 0x00, 0x01, 0x00, 0x00, 0x10},
+	{DELAY, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*delay 20ms*/
+	{0xb1, 0x5d, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{DELAY, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*delay 20ms*/
+	{0xd1, 0x5d, 0x01, 0x00, 0x08, 0x00, 0x16, 0x10},
+	{0xd1, 0x5d, 0x03, 0x01, 0xe2, 0x02, 0x82, 0x10},
+	{0xd1, 0x5d, 0x05, 0x00, 0x00, 0x00, 0x00, 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, 0x11, 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, 0x11, 0x01, 0x00, 0x00, 0x10},
+	{0xb1, 0x5d, 0x20, 0x11, 0x01, 0x00, 0x00, 0x10},
+	{0xb1, 0x5d, 0x09, 0x00, 0x64, 0x00, 0x00, 0x10},
+	{0xd1, 0x5d, 0x2b, 0x00, 0x33, 0x00, 0xa0, 0x10},
+	{0xd1, 0x5d, 0x2d, 0x00, 0xa0, 0x00, 0x33, 0x10},
+	{}
+};
+static const u8 mi0360b_sensor_param1[][8] = {
+	{0xb1, 0x5d, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xb1, 0x5d, 0x06, 0x00, 0x53, 0x00, 0x00, 0x10},
+	{0xb1, 0x5d, 0x05, 0x00, 0x09, 0x00, 0x00, 0x10},
+	{0xb1, 0x5d, 0x09, 0x02, 0x35, 0x00, 0x00, 0x10}, /* exposure 2 */
+
+	{0xd1, 0x5d, 0x2b, 0x00, 0xd1, 0x01, 0xc9, 0x10},
+	{0xd1, 0x5d, 0x2d, 0x00, 0xed, 0x00, 0xd1, 0x10},
+	{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 mt9v111_sensor_init[][8] = {
+	{0xb1, 0x5c, 0x0d, 0x00, 0x01, 0x00, 0x00, 0x10}, /* reset? */
+	{DELAY, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */
+	{0xb1, 0x5c, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xb1, 0x5c, 0x01, 0x00, 0x01, 0x00, 0x00, 0x10}, /* IFP select */
+	{0xb1, 0x5c, 0x08, 0x04, 0x80, 0x00, 0x00, 0x10}, /* output fmt ctrl */
+	{0xb1, 0x5c, 0x06, 0x00, 0x00, 0x00, 0x00, 0x10}, /* op mode ctrl */
+	{0xb1, 0x5c, 0x01, 0x00, 0x04, 0x00, 0x00, 0x10}, /* sensor select */
+	{0xb1, 0x5c, 0x08, 0x00, 0x08, 0x00, 0x00, 0x10}, /* row start */
+	{0xb1, 0x5c, 0x02, 0x00, 0x16, 0x00, 0x00, 0x10}, /* col start */
+	{0xb1, 0x5c, 0x03, 0x01, 0xe7, 0x00, 0x00, 0x10}, /* window height */
+	{0xb1, 0x5c, 0x04, 0x02, 0x87, 0x00, 0x00, 0x10}, /* window width */
+	{0xb1, 0x5c, 0x07, 0x30, 0x02, 0x00, 0x00, 0x10}, /* output ctrl */
+	{0xb1, 0x5c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x10}, /* shutter delay */
+	{0xb1, 0x5c, 0x12, 0x00, 0xb0, 0x00, 0x00, 0x10}, /* zoom col start */
+	{0xb1, 0x5c, 0x13, 0x00, 0x7c, 0x00, 0x00, 0x10}, /* zoom row start */
+	{0xb1, 0x5c, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x10}, /* digital zoom */
+	{0xb1, 0x5c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10}, /* read mode */
+	{0xb1, 0x5c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{}
+};
+static const u8 mt9v111_sensor_param1[][8] = {
+	{0xd1, 0x5c, 0x2b, 0x00, 0x33, 0x00, 0xad, 0x10}, /* G1 and B gains */
+	{0xd1, 0x5c, 0x2d, 0x00, 0xad, 0x00, 0x33, 0x10}, /* R and G2 gains */
+	{0xb1, 0x5c, 0x06, 0x00, 0x40, 0x00, 0x00, 0x10}, /* vert blanking */
+	{0xb1, 0x5c, 0x05, 0x00, 0x09, 0x00, 0x00, 0x10}, /* horiz blanking */
+	{0xb1, 0x5c, 0x35, 0x01, 0xc0, 0x00, 0x00, 0x10}, /* global gain */
+	{}
+};
+static const u8 om6802_init0[2][8] = {
+/*fixme: variable*/
+	{0xa0, 0x34, 0x29, 0x0e, 0x00, 0x00, 0x00, 0x10},
+	{0xa0, 0x34, 0x23, 0xb0, 0x00, 0x00, 0x00, 0x10},
+};
+static const u8 om6802_sensor_init[][8] = {
+	{0xa0, 0x34, 0xdf, 0x6d, 0x00, 0x00, 0x00, 0x10},
+						/* factory mode */
+	{0xa0, 0x34, 0xdd, 0x18, 0x00, 0x00, 0x00, 0x10},
+						/* output raw RGB */
+	{0xa0, 0x34, 0x5a, 0xc0, 0x00, 0x00, 0x00, 0x10},
+/*	{0xa0, 0x34, 0xfb, 0x11, 0x00, 0x00, 0x00, 0x10}, */
+	{0xa0, 0x34, 0xf0, 0x04, 0x00, 0x00, 0x00, 0x10},
+		/* auto-exposure speed (0) / white balance mode (auto RGB) */
+/*	{0xa0, 0x34, 0xf1, 0x02, 0x00, 0x00, 0x00, 0x10},
+							 * set color mode */
+/*	{0xa0, 0x34, 0xfe, 0x5b, 0x00, 0x00, 0x00, 0x10},
+						 * max AGC value in AE */
+/*	{0xa0, 0x34, 0xe5, 0x00, 0x00, 0x00, 0x00, 0x10},
+							 * preset AGC */
+/*	{0xa0, 0x34, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x10},
+						 * preset brightness */
+/*	{0xa0, 0x34, 0xe7, 0x00, 0x00, 0x00, 0x00, 0x10},
+							 * preset contrast */
+/*	{0xa0, 0x34, 0xe8, 0x31, 0x00, 0x00, 0x00, 0x10},
+							 * preset gamma */
+	{0xa0, 0x34, 0xe9, 0x0f, 0x00, 0x00, 0x00, 0x10},
+				/* luminance mode (0x4f -> AutoExpo on) */
+	{0xa0, 0x34, 0xe4, 0xff, 0x00, 0x00, 0x00, 0x10},
+							/* preset shutter */
+/*	{0xa0, 0x34, 0xef, 0x00, 0x00, 0x00, 0x00, 0x10},
+							 * auto frame rate */
+/*	{0xa0, 0x34, 0xfb, 0xee, 0x00, 0x00, 0x00, 0x10}, */
+	{0xa0, 0x34, 0x5d, 0x80, 0x00, 0x00, 0x00, 0x10},
+	{}
+};
+static const u8 om6802_sensor_param1[][8] = {
+	{0xa0, 0x34, 0x71, 0x84, 0x00, 0x00, 0x00, 0x10},
+	{0xa0, 0x34, 0x72, 0x05, 0x00, 0x00, 0x00, 0x10},
+	{0xa0, 0x34, 0x68, 0x80, 0x00, 0x00, 0x00, 0x10},
+	{0xa0, 0x34, 0x69, 0x01, 0x00, 0x00, 0x00, 0x10},
+	{}
+};
+static const u8 ov7630_sensor_init[][8] = {
+	{0xa1, 0x21, 0x76, 0x01, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x21, 0x12, 0xc8, 0x00, 0x00, 0x00, 0x10},
+	{DELAY, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */
+	{0xa1, 0x21, 0x12, 0x48, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x21, 0x12, 0xc8, 0x00, 0x00, 0x00, 0x10},
+	{DELAY, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */
+	{0xa1, 0x21, 0x12, 0x48, 0x00, 0x00, 0x00, 0x10},
+/* win: i2c_r from 00 to 80 */
+	{0xd1, 0x21, 0x03, 0x80, 0x10, 0x20, 0x80, 0x10},
+	{0xb1, 0x21, 0x0c, 0x20, 0x20, 0x00, 0x00, 0x10},
+/* HDG: 0x11 was 0x00 change to 0x01 for better exposure (15 fps instead of 30)
+	0x13 was 0xc0 change to 0xc3 for auto gain and exposure */
+	{0xd1, 0x21, 0x11, 0x01, 0x48, 0xc3, 0x00, 0x10},
+	{0xb1, 0x21, 0x15, 0x80, 0x03, 0x00, 0x00, 0x10},
+	{0xd1, 0x21, 0x17, 0x1b, 0xbd, 0x05, 0xf6, 0x10},
+	{0xa1, 0x21, 0x1b, 0x04, 0x00, 0x00, 0x00, 0x10},
+	{0xd1, 0x21, 0x1f, 0x00, 0x80, 0x80, 0x80, 0x10},
+	{0xd1, 0x21, 0x23, 0xde, 0x10, 0x8a, 0xa0, 0x10},
+	{0xc1, 0x21, 0x27, 0xca, 0xa2, 0x74, 0x00, 0x10},
+	{0xd1, 0x21, 0x2a, 0x88, 0x00, 0x88, 0x01, 0x10},
+	{0xc1, 0x21, 0x2e, 0x80, 0x00, 0x18, 0x00, 0x10},
+	{0xa1, 0x21, 0x21, 0x08, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x21, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x21, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xb1, 0x21, 0x32, 0xc2, 0x08, 0x00, 0x00, 0x10},
+	{0xb1, 0x21, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xd1, 0x21, 0x60, 0x05, 0x40, 0x12, 0x57, 0x10},
+	{0xa1, 0x21, 0x64, 0x73, 0x00, 0x00, 0x00, 0x10},
+	{0xd1, 0x21, 0x65, 0x00, 0x55, 0x01, 0xac, 0x10},
+	{0xa1, 0x21, 0x69, 0x38, 0x00, 0x00, 0x00, 0x10},
+	{0xd1, 0x21, 0x6f, 0x1f, 0x01, 0x00, 0x10, 0x10},
+	{0xd1, 0x21, 0x73, 0x50, 0x20, 0x02, 0x01, 0x10},
+	{0xd1, 0x21, 0x77, 0xf3, 0x90, 0x98, 0x98, 0x10},
+	{0xc1, 0x21, 0x7b, 0x00, 0x4c, 0xf7, 0x00, 0x10},
+	{0xd1, 0x21, 0x17, 0x1b, 0xbd, 0x05, 0xf6, 0x10},
+	{0xa1, 0x21, 0x1b, 0x04, 0x00, 0x00, 0x00, 0x10},
+	{}
+};
+static const u8 ov7630_sensor_param1[][8] = {
+	{0xa1, 0x21, 0x12, 0x48, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x21, 0x12, 0x48, 0x00, 0x00, 0x00, 0x10},
+/*fixme: + 0x12, 0x04*/
+/*	{0xa1, 0x21, 0x75, 0x82, 0x00, 0x00, 0x00, 0x10},  * COMN
+							 * set by setvflip */
+	{0xa1, 0x21, 0x10, 0x32, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xb1, 0x21, 0x01, 0x80, 0x80, 0x00, 0x00, 0x10},
+/* */
+/*	{0xa1, 0x21, 0x2a, 0x88, 0x00, 0x00, 0x00, 0x10}, * set by setfreq */
+/*	{0xa1, 0x21, 0x2b, 0x34, 0x00, 0x00, 0x00, 0x10}, * set by setfreq */
+/* */
+	{0xa1, 0x21, 0x10, 0x83, 0x00, 0x00, 0x00, 0x10},
+/*	{0xb1, 0x21, 0x01, 0x88, 0x70, 0x00, 0x00, 0x10}, */
+	{}
+};
+
+static const u8 ov7648_sensor_init[][8] = {
+	{0xa1, 0x21, 0x76, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10},	/* reset */
+	{DELAY, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */
+	{0xa1, 0x21, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xd1, 0x21, 0x03, 0xa4, 0x30, 0x88, 0x00, 0x10},
+	{0xb1, 0x21, 0x11, 0x80, 0x08, 0x00, 0x00, 0x10},
+	{0xc1, 0x21, 0x13, 0xa0, 0x04, 0x84, 0x00, 0x10},
+	{0xd1, 0x21, 0x17, 0x1a, 0x02, 0xba, 0xf4, 0x10},
+	{0xa1, 0x21, 0x1b, 0x04, 0x00, 0x00, 0x00, 0x10},
+	{0xd1, 0x21, 0x1f, 0x41, 0xc0, 0x80, 0x80, 0x10},
+	{0xd1, 0x21, 0x23, 0xde, 0xa0, 0x80, 0x32, 0x10},
+	{0xd1, 0x21, 0x27, 0xfe, 0xa0, 0x00, 0x91, 0x10},
+	{0xd1, 0x21, 0x2b, 0x00, 0x88, 0x85, 0x80, 0x10},
+	{0xc1, 0x21, 0x2f, 0x9c, 0x00, 0xc4, 0x00, 0x10},
+	{0xd1, 0x21, 0x60, 0xa6, 0x60, 0x88, 0x12, 0x10},
+	{0xd1, 0x21, 0x64, 0x88, 0x00, 0x00, 0x94, 0x10},
+	{0xd1, 0x21, 0x68, 0x7a, 0x0c, 0x00, 0x00, 0x10},
+	{0xd1, 0x21, 0x6c, 0x11, 0x33, 0x22, 0x00, 0x10},
+	{0xd1, 0x21, 0x70, 0x11, 0x00, 0x10, 0x50, 0x10},
+	{0xd1, 0x21, 0x74, 0x20, 0x06, 0x00, 0xb5, 0x10},
+	{0xd1, 0x21, 0x78, 0x8a, 0x00, 0x00, 0x00, 0x10},
+	{0xb1, 0x21, 0x7c, 0x00, 0x43, 0x00, 0x00, 0x10},
+
+	{0xd1, 0x21, 0x21, 0x86, 0x00, 0xde, 0xa0, 0x10},
+/*	{0xd1, 0x21, 0x25, 0x80, 0x32, 0xfe, 0xa0, 0x10}, jfm done */
+/*	{0xd1, 0x21, 0x29, 0x00, 0x91, 0x00, 0x88, 0x10}, jfm done */
+/*	{0xb1, 0x21, 0x2d, 0x85, 0x00, 0x00, 0x00, 0x10}, set by setfreq */
+	{}
+};
+static const u8 ov7648_sensor_param1[][8] = {
+/*	{0xa1, 0x21, 0x12, 0x08, 0x00, 0x00, 0x00, 0x10}, jfm done */
+/*	{0xa1, 0x21, 0x75, 0x06, 0x00, 0x00, 0x00, 0x10},   * COMN
+							 * set by setvflip */
+	{0xa1, 0x21, 0x19, 0x02, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x21, 0x10, 0x32, 0x00, 0x00, 0x00, 0x10},
+/*	{0xa1, 0x21, 0x16, 0x00, 0x00, 0x00, 0x00, 0x10}, jfm done */
+/*	{0xa1, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10},  * GAIN - def */
+/*	{0xb1, 0x21, 0x01, 0x6c, 0x6c, 0x00, 0x00, 0x10},  * B R - def: 80 */
+/*...*/
+	{0xa1, 0x21, 0x11, 0x81, 0x00, 0x00, 0x00, 0x10}, /* CLKRC */
+/*	{0xa1, 0x21, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x10}, jfm done */
+/*	{0xa1, 0x21, 0x16, 0x00, 0x00, 0x00, 0x00, 0x10}, jfm done */
+/*	{0xa1, 0x21, 0x2a, 0x91, 0x00, 0x00, 0x00, 0x10}, jfm done */
+/*	{0xa1, 0x21, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x10}, jfm done */
+/*	{0xb1, 0x21, 0x01, 0x64, 0x84, 0x00, 0x00, 0x10},  * B R - def: 80 */
+
+	{}
+};
+
+static const u8 ov7660_sensor_init[][8] = {
+	{0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, /* reset SCCB */
+	{DELAY, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */
+	{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},
+						/* 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, 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}, /* MANU */
+	{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}, /* DM_LNL/H */
+/* not in all ms-win traces*/
+	{0xa1, 0x21, 0xa1, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{}
+};
+static const u8 ov7660_sensor_param1[][8] = {
+	{0xa1, 0x21, 0x1e, 0x01, 0x00, 0x00, 0x00, 0x10}, /* MVFP */
+						/* 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}, /* AECH 0x20 */
+	{0xa1, 0x21, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x10}, /* ADVFL */
+	{0xa1, 0x21, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x10}, /* ADVFH */
+	{0xa1, 0x21, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x10}, /* GAIN */
+/*	{0xb1, 0x21, 0x01, 0x78, 0x78, 0x00, 0x00, 0x10}, * BLUE */
+/****** (some exchanges in the win trace) ******/
+/*fixme:param2*/
+	{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}, /* EXHCH */
+	{0xa1, 0x21, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x10}, /* EXHCL */
+/*	{0xa1, 0x21, 0x02, 0x90, 0x00, 0x00, 0x00, 0x10},  * RED */
+/****** (some exchanges in the win trace) ******/
+/******!! startsensor KO if changed !!****/
+/*fixme: param3*/
+	{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},
+	{}
+};
+
+static const u8 po1030_sensor_init[][8] = {
+/* the sensor registers are described in m5602/m5602_po1030.h */
+	{0xa1, 0x6e, 0x3f, 0x20, 0x00, 0x00, 0x00, 0x10}, /* sensor reset */
+	{DELAY, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */
+	{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}, /* r/g1/b/g2 gains */
+	{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},
+	{0xb1, 0x6e, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xd1, 0x6e, 0x2d, 0x14, 0x35, 0x61, 0x84, 0x10}, /* gamma corr */
+	{0xd1, 0x6e, 0x31, 0xa2, 0xbd, 0xd8, 0xff, 0x10},
+	{0xd1, 0x6e, 0x35, 0x06, 0x1e, 0x12, 0x02, 0x10}, /* color matrix */
+	{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}, /* raw rgb bayer */
+	{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},
+	{0xd1, 0x6e, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xd1, 0x6e, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xc1, 0x6e, 0x73, 0x10, 0x80, 0xeb, 0x00, 0x10},
+	{}
+};
+static const u8 po1030_sensor_param1[][8] = {
+/* from ms-win traces - these values change with auto gain/expo/wb.. */
+	{0xa1, 0x6e, 0x1e, 0x03, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x6e, 0x1e, 0x03, 0x00, 0x00, 0x00, 0x10},
+/* mean values */
+	{0xc1, 0x6e, 0x1a, 0x02, 0xd4, 0xa4, 0x00, 0x10}, /* integlines */
+	{0xa1, 0x6e, 0x15, 0x04, 0x00, 0x00, 0x00, 0x10}, /* global gain */
+	{0xc1, 0x6e, 0x16, 0x40, 0x40, 0x40, 0x00, 0x10}, /* r/g1/b gains */
+
+	{0xa1, 0x6e, 0x1d, 0x08, 0x00, 0x00, 0x00, 0x10}, /* control1 */
+	{0xa1, 0x6e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x10}, /* frameheight */
+	{0xa1, 0x6e, 0x07, 0xd5, 0x00, 0x00, 0x00, 0x10},
+/*	{0xc1, 0x6e, 0x16, 0x49, 0x40, 0x45, 0x00, 0x10}, */
+	{}
+};
+
+static const u8 po2030n_sensor_init[][8] = {
+	{0xa1, 0x6e, 0x1e, 0x1a, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x6e, 0x1f, 0x99, 0x00, 0x00, 0x00, 0x10},
+	{DELAY, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 10ms */
+	{0xa1, 0x6e, 0x1e, 0x0a, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x6e, 0x1f, 0x19, 0x00, 0x00, 0x00, 0x10},
+	{DELAY, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 10ms */
+	{0xa1, 0x6e, 0x20, 0x44, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x6e, 0x04, 0x03, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x6e, 0x05, 0x70, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x6e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x6e, 0x07, 0x25, 0x00, 0x00, 0x00, 0x10},
+	{0xd1, 0x6e, 0x08, 0x00, 0xd0, 0x00, 0x08, 0x10},
+	{0xd1, 0x6e, 0x0c, 0x03, 0x50, 0x01, 0xe8, 0x10},
+	{0xd1, 0x6e, 0x1d, 0x20, 0x0a, 0x19, 0x44, 0x10},
+	{0xd1, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xd1, 0x6e, 0x25, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xd1, 0x6e, 0x29, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xd1, 0x6e, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xd1, 0x6e, 0x31, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xd1, 0x6e, 0x35, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xd1, 0x6e, 0x39, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xd1, 0x6e, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xd1, 0x6e, 0x41, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xd1, 0x6e, 0x45, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xd1, 0x6e, 0x49, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xd1, 0x6e, 0x4d, 0x00, 0x00, 0x00, 0xed, 0x10},
+	{0xd1, 0x6e, 0x51, 0x17, 0x4a, 0x2f, 0xc0, 0x10},
+	{0xd1, 0x6e, 0x55, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xd1, 0x6e, 0x59, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xd1, 0x6e, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xd1, 0x6e, 0x61, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xd1, 0x6e, 0x65, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xd1, 0x6e, 0x69, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xd1, 0x6e, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xd1, 0x6e, 0x71, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xd1, 0x6e, 0x75, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xd1, 0x6e, 0x79, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xd1, 0x6e, 0x7d, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xd1, 0x6e, 0x81, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xd1, 0x6e, 0x85, 0x00, 0x00, 0x00, 0x08, 0x10},
+	{0xd1, 0x6e, 0x89, 0x01, 0xe8, 0x00, 0x01, 0x10},
+	{0xa1, 0x6e, 0x8d, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xd1, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xd1, 0x6e, 0x25, 0x00, 0x00, 0x00, 0x01, 0x10},
+	{0xd1, 0x6e, 0x29, 0xe6, 0x00, 0xbd, 0x03, 0x10},
+	{0xd1, 0x6e, 0x2d, 0x41, 0x38, 0x68, 0x40, 0x10},
+	{0xd1, 0x6e, 0x31, 0x2b, 0x00, 0x36, 0x00, 0x10},
+	{0xd1, 0x6e, 0x35, 0x30, 0x30, 0x08, 0x00, 0x10},
+	{0xd1, 0x6e, 0x39, 0x00, 0x00, 0x33, 0x06, 0x10},
+	{0xb1, 0x6e, 0x3d, 0x06, 0x02, 0x00, 0x00, 0x10},
+	{}
+};
+static const u8 po2030n_sensor_param1[][8] = {
+	{0xa1, 0x6e, 0x1a, 0x01, 0x00, 0x00, 0x00, 0x10},
+	{DELAY, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 8ms */
+	{0xa1, 0x6e, 0x1b, 0xf4, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x6e, 0x15, 0x04, 0x00, 0x00, 0x00, 0x10},
+	{0xd1, 0x6e, 0x16, 0x40, 0x40, 0x40, 0x40, 0x10}, /* RGBG gains */
+/*param2*/
+	{0xa1, 0x6e, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x6e, 0x04, 0x03, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x6e, 0x05, 0x6f, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x6e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x6e, 0x07, 0x25, 0x00, 0x00, 0x00, 0x10},
+	{}
+};
+
+static const u8 soi768_sensor_init[][8] = {
+	{0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, /* reset */
+	{DELAY, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 96ms */
+	{0xa1, 0x21, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x21, 0x13, 0x80, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x21, 0x0f, 0x03, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x21, 0x19, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{}
+};
+static const u8 soi768_sensor_param1[][8] = {
+	{0xa1, 0x21, 0x10, 0x10, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x21, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x21, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xb1, 0x21, 0x01, 0x7f, 0x7f, 0x00, 0x00, 0x10},
+/* */
+/*	{0xa1, 0x21, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x10}, */
+/*	{0xa1, 0x21, 0x2d, 0x25, 0x00, 0x00, 0x00, 0x10}, */
+	{0xa1, 0x21, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x10},
+/*	{0xb1, 0x21, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x10}, */
+	{0xa1, 0x21, 0x02, 0x8d, 0x00, 0x00, 0x00, 0x10},
+/* the next sequence should be used for auto gain */
+	{0xa1, 0x21, 0x00, 0x07, 0x00, 0x00, 0x00, 0x10},
+			/* global gain ? : 07 - change with 0x15 at the end */
+	{0xa1, 0x21, 0x10, 0x3f, 0x00, 0x00, 0x00, 0x10}, /* ???? : 063f */
+	{0xa1, 0x21, 0x04, 0x06, 0x00, 0x00, 0x00, 0x10},
+	{0xb1, 0x21, 0x2d, 0x63, 0x03, 0x00, 0x00, 0x10},
+			/* exposure ? : 0200 - change with 0x1e at the end */
+	{}
+};
+
+static const u8 sp80708_sensor_init[][8] = {
+	{0xa1, 0x18, 0x06, 0xf9, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x09, 0x1f, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x0d, 0xc0, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x0c, 0x04, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x10, 0x40, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x11, 0x4e, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x12, 0x53, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x15, 0x80, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x19, 0x18, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x1a, 0x10, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x1b, 0x10, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x1c, 0x28, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x1d, 0x02, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x1e, 0x10, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x26, 0x04, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x27, 0x1e, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x28, 0x5a, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x29, 0x28, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x2a, 0x78, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x2b, 0x01, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x2c, 0xf7, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x2d, 0x2d, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x2e, 0xd5, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x39, 0x42, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x3a, 0x67, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x3b, 0x87, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x3c, 0xa3, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x3d, 0xb0, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x3e, 0xbc, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x3f, 0xc8, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x40, 0xd4, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x41, 0xdf, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x42, 0xea, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x43, 0xf5, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x45, 0x80, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x46, 0x60, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x47, 0x50, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x48, 0x30, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x49, 0x01, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x4d, 0xae, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x4e, 0x03, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x4f, 0x66, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x50, 0x1c, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x44, 0x10, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x4a, 0x30, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x51, 0x80, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x52, 0x80, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x53, 0x80, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x54, 0x80, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x55, 0x80, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x56, 0x80, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x57, 0xe0, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x58, 0xc0, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x59, 0xab, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x5a, 0xa0, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x5b, 0x99, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x5c, 0x90, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x5e, 0x24, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x60, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x61, 0x73, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x63, 0x42, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x64, 0x42, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x65, 0x42, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x66, 0x24, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x67, 0x24, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x68, 0x08, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x2f, 0xc9, 0x00, 0x00, 0x00, 0x10},
+	{}
+};
+static const u8 sp80708_sensor_param1[][8] = {
+	{0xa1, 0x18, 0x0c, 0x04, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x0c, 0x04, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x03, 0x01, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x04, 0xa4, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x14, 0x3f, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x18, 0x5d, 0x80, 0x00, 0x00, 0x00, 0x10},
+	{0xb1, 0x18, 0x11, 0x40, 0x40, 0x00, 0x00, 0x10},
+	{}
+};
+
+static const u8 (*sensor_init[])[8] = {
+[SENSOR_ADCM1700] =	adcm1700_sensor_init,
+[SENSOR_GC0307] =	gc0307_sensor_init,
+[SENSOR_HV7131R] =	hv7131r_sensor_init,
+[SENSOR_MI0360] =	mi0360_sensor_init,
+[SENSOR_MI0360B] =	mi0360b_sensor_init,
+[SENSOR_MO4000] =	mo4000_sensor_init,
+[SENSOR_MT9V111] =	mt9v111_sensor_init,
+[SENSOR_OM6802] =	om6802_sensor_init,
+[SENSOR_OV7630] =	ov7630_sensor_init,
+[SENSOR_OV7648] =	ov7648_sensor_init,
+[SENSOR_OV7660] =	ov7660_sensor_init,
+[SENSOR_PO1030] =	po1030_sensor_init,
+[SENSOR_PO2030N] =	po2030n_sensor_init,
+[SENSOR_SOI768] =	soi768_sensor_init,
+[SENSOR_SP80708] =	sp80708_sensor_init,
+};
+
+/* read <len> bytes to gspca_dev->usb_buf */
+static void reg_r(struct gspca_dev *gspca_dev,
+		  u16 value, int len)
+{
+	int ret;
+
+	if (gspca_dev->usb_err < 0)
+		return;
+	if (len > USB_BUF_SZ) {
+		PERR("reg_r: buffer overflow\n");
+		return;
+	}
+
+	ret = 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);
+	PDEBUG(D_USBI, "reg_r [%02x] -> %02x", value, gspca_dev->usb_buf[0]);
+	if (ret < 0) {
+		pr_err("reg_r err %d\n", ret);
+		gspca_dev->usb_err = ret;
+	}
+}
+
+static void reg_w1(struct gspca_dev *gspca_dev,
+		   u16 value,
+		   u8 data)
+{
+	int ret;
+
+	if (gspca_dev->usb_err < 0)
+		return;
+	PDEBUG(D_USBO, "reg_w1 [%04x] = %02x", value, data);
+	gspca_dev->usb_buf[0] = data;
+	ret = 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, 1,
+			500);
+	if (ret < 0) {
+		pr_err("reg_w1 err %d\n", ret);
+		gspca_dev->usb_err = ret;
+	}
+}
+static void reg_w(struct gspca_dev *gspca_dev,
+			  u16 value,
+			  const u8 *buffer,
+			  int len)
+{
+	int ret;
+
+	if (gspca_dev->usb_err < 0)
+		return;
+	PDEBUG(D_USBO, "reg_w [%04x] = %02x %02x ..",
+		value, buffer[0], buffer[1]);
+
+	if (len > USB_BUF_SZ) {
+		PERR("reg_w: buffer overflow\n");
+		return;
+	}
+
+	memcpy(gspca_dev->usb_buf, buffer, len);
+	ret = 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);
+	if (ret < 0) {
+		pr_err("reg_w err %d\n", ret);
+		gspca_dev->usb_err = ret;
+	}
+}
+
+/* I2C write 1 byte */
+static void i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int ret;
+
+	if (gspca_dev->usb_err < 0)
+		return;
+	PDEBUG(D_USBO, "i2c_w1 [%02x] = %02x", reg, val);
+	switch (sd->sensor) {
+	case SENSOR_ADCM1700:
+	case SENSOR_OM6802:
+	case SENSOR_GC0307:		/* i2c command = a0 (100 kHz) */
+		gspca_dev->usb_buf[0] = 0x80 | (2 << 4);
+		break;
+	default:			/* i2c command = a1 (400 kHz) */
+		gspca_dev->usb_buf[0] = 0x81 | (2 << 4);
+		break;
+	}
+	gspca_dev->usb_buf[1] = sd->i2c_addr;
+	gspca_dev->usb_buf[2] = reg;
+	gspca_dev->usb_buf[3] = val;
+	gspca_dev->usb_buf[4] = 0;
+	gspca_dev->usb_buf[5] = 0;
+	gspca_dev->usb_buf[6] = 0;
+	gspca_dev->usb_buf[7] = 0x10;
+	ret = usb_control_msg(gspca_dev->dev,
+			usb_sndctrlpipe(gspca_dev->dev, 0),
+			0x08,
+			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+			0x08,			/* value = i2c */
+			0,
+			gspca_dev->usb_buf, 8,
+			500);
+	msleep(2);
+	if (ret < 0) {
+		pr_err("i2c_w1 err %d\n", ret);
+		gspca_dev->usb_err = ret;
+	}
+}
+
+/* I2C write 8 bytes */
+static void i2c_w8(struct gspca_dev *gspca_dev,
+		   const u8 *buffer)
+{
+	int ret;
+
+	if (gspca_dev->usb_err < 0)
+		return;
+	PDEBUG(D_USBO, "i2c_w8 [%02x] = %02x ..",
+		buffer[2], buffer[3]);
+	memcpy(gspca_dev->usb_buf, buffer, 8);
+	ret = usb_control_msg(gspca_dev->dev,
+			usb_sndctrlpipe(gspca_dev->dev, 0),
+			0x08,
+			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+			0x08, 0,		/* value, index */
+			gspca_dev->usb_buf, 8,
+			500);
+	msleep(2);
+	if (ret < 0) {
+		pr_err("i2c_w8 err %d\n", ret);
+		gspca_dev->usb_err = ret;
+	}
+}
+
+/* sensor read 'len' (1..5) bytes in gspca_dev->usb_buf */
+static void i2c_r(struct gspca_dev *gspca_dev, u8 reg, int len)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u8 mode[8];
+
+	switch (sd->sensor) {
+	case SENSOR_ADCM1700:
+	case SENSOR_OM6802:
+	case SENSOR_GC0307:		/* i2c command = a0 (100 kHz) */
+		mode[0] = 0x80 | 0x10;
+		break;
+	default:			/* i2c command = 91 (400 kHz) */
+		mode[0] = 0x81 | 0x10;
+		break;
+	}
+	mode[1] = sd->i2c_addr;
+	mode[2] = reg;
+	mode[3] = 0;
+	mode[4] = 0;
+	mode[5] = 0;
+	mode[6] = 0;
+	mode[7] = 0x10;
+	i2c_w8(gspca_dev, mode);
+	msleep(2);
+	mode[0] = (mode[0] & 0x81) | (len << 4) | 0x02;
+	mode[2] = 0;
+	i2c_w8(gspca_dev, mode);
+	msleep(2);
+	reg_r(gspca_dev, 0x0a, 5);
+}
+
+static void i2c_w_seq(struct gspca_dev *gspca_dev,
+			const u8 (*data)[8])
+{
+	while ((*data)[0] != 0) {
+		if ((*data)[0] != DELAY)
+			i2c_w8(gspca_dev, *data);
+		else
+			msleep((*data)[1]);
+		data++;
+	}
+}
+
+/* check the ID of the hv7131 sensor */
+/* this sequence is needed because it activates the sensor */
+static void hv7131r_probe(struct gspca_dev *gspca_dev)
+{
+	i2c_w1(gspca_dev, 0x02, 0);		/* sensor wakeup */
+	msleep(10);
+	reg_w1(gspca_dev, 0x02, 0x66);		/* Gpio on */
+	msleep(10);
+	i2c_r(gspca_dev, 0, 5);			/* read sensor id */
+	if (gspca_dev->usb_buf[0] == 0x02	/* chip ID (02 is R) */
+	    && gspca_dev->usb_buf[1] == 0x09
+	    && gspca_dev->usb_buf[2] == 0x01) {
+		PDEBUG(D_PROBE, "Sensor HV7131R found");
+		return;
+	}
+	pr_warn("Erroneous HV7131R ID 0x%02x 0x%02x 0x%02x\n",
+		gspca_dev->usb_buf[0], gspca_dev->usb_buf[1],
+		gspca_dev->usb_buf[2]);
+}
+
+static void mi0360_probe(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int i, j;
+	u16 val = 0;
+	static const u8 probe_tb[][4][8] = {
+	    {					/* mi0360 */
+		{0xb0, 0x5d, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10},
+		{0x90, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10},
+		{0xa2, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10},
+		{0xb0, 0x5d, 0x07, 0x00, 0x00, 0x00, 0x00, 0x10}
+	    },
+	    {					/* mt9v111 */
+		{0xb0, 0x5c, 0x01, 0x00, 0x04, 0x00, 0x00, 0x10},
+		{0x90, 0x5c, 0x36, 0x00, 0x00, 0x00, 0x00, 0x10},
+		{0xa2, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10},
+		{}
+	    },
+	};
+
+	for (i = 0; i < ARRAY_SIZE(probe_tb); i++) {
+		reg_w1(gspca_dev, 0x17, 0x62);
+		reg_w1(gspca_dev, 0x01, 0x08);
+		for (j = 0; j < 3; j++)
+			i2c_w8(gspca_dev, probe_tb[i][j]);
+		msleep(2);
+		reg_r(gspca_dev, 0x0a, 5);
+		val = (gspca_dev->usb_buf[3] << 8) | gspca_dev->usb_buf[4];
+		if (probe_tb[i][3][0] != 0)
+			i2c_w8(gspca_dev, probe_tb[i][3]);
+		reg_w1(gspca_dev, 0x01, 0x29);
+		reg_w1(gspca_dev, 0x17, 0x42);
+		if (val != 0xffff)
+			break;
+	}
+	if (gspca_dev->usb_err < 0)
+		return;
+	switch (val) {
+	case 0x8221:
+		PDEBUG(D_PROBE, "Sensor mi0360b");
+		sd->sensor = SENSOR_MI0360B;
+		break;
+	case 0x823a:
+		PDEBUG(D_PROBE, "Sensor mt9v111");
+		sd->sensor = SENSOR_MT9V111;
+		break;
+	case 0x8243:
+		PDEBUG(D_PROBE, "Sensor mi0360");
+		break;
+	default:
+		PDEBUG(D_PROBE, "Unknown sensor %04x - forced to mi0360", val);
+		break;
+	}
+}
+
+static void ov7630_probe(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u16 val;
+
+	/* check ov76xx */
+	reg_w1(gspca_dev, 0x17, 0x62);
+	reg_w1(gspca_dev, 0x01, 0x08);
+	sd->i2c_addr = 0x21;
+	i2c_r(gspca_dev, 0x0a, 2);
+	val = (gspca_dev->usb_buf[3] << 8) | gspca_dev->usb_buf[4];
+	reg_w1(gspca_dev, 0x01, 0x29);
+	reg_w1(gspca_dev, 0x17, 0x42);
+	if (gspca_dev->usb_err < 0)
+		return;
+	if (val == 0x7628) {			/* soi768 */
+		sd->sensor = SENSOR_SOI768;
+/*fixme: only valid for 0c45:613e?*/
+		gspca_dev->cam.input_flags =
+				V4L2_IN_ST_VFLIP | V4L2_IN_ST_HFLIP;
+		PDEBUG(D_PROBE, "Sensor soi768");
+		return;
+	}
+	PDEBUG(D_PROBE, "Sensor ov%04x", val);
+}
+
+static void ov7648_probe(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u16 val;
+
+	/* check ov76xx */
+	reg_w1(gspca_dev, 0x17, 0x62);
+	reg_w1(gspca_dev, 0x01, 0x08);
+	sd->i2c_addr = 0x21;
+	i2c_r(gspca_dev, 0x0a, 2);
+	val = (gspca_dev->usb_buf[3] << 8) | gspca_dev->usb_buf[4];
+	reg_w1(gspca_dev, 0x01, 0x29);
+	reg_w1(gspca_dev, 0x17, 0x42);
+	if ((val & 0xff00) == 0x7600) {		/* ov76xx */
+		PDEBUG(D_PROBE, "Sensor ov%04x", val);
+		return;
+	}
+
+	/* check po1030 */
+	reg_w1(gspca_dev, 0x17, 0x62);
+	reg_w1(gspca_dev, 0x01, 0x08);
+	sd->i2c_addr = 0x6e;
+	i2c_r(gspca_dev, 0x00, 2);
+	val = (gspca_dev->usb_buf[3] << 8) | gspca_dev->usb_buf[4];
+	reg_w1(gspca_dev, 0x01, 0x29);
+	reg_w1(gspca_dev, 0x17, 0x42);
+	if (gspca_dev->usb_err < 0)
+		return;
+	if (val == 0x1030) {			/* po1030 */
+		PDEBUG(D_PROBE, "Sensor po1030");
+		sd->sensor = SENSOR_PO1030;
+		return;
+	}
+	pr_err("Unknown sensor %04x\n", val);
+}
+
+/* 0c45:6142 sensor may be po2030n, gc0305 or gc0307 */
+static void po2030n_probe(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u16 val;
+
+	/* check gc0307 */
+	reg_w1(gspca_dev, 0x17, 0x62);
+	reg_w1(gspca_dev, 0x01, 0x08);
+	reg_w1(gspca_dev, 0x02, 0x22);
+	sd->i2c_addr = 0x21;
+	i2c_r(gspca_dev, 0x00, 1);
+	val = gspca_dev->usb_buf[4];
+	reg_w1(gspca_dev, 0x01, 0x29);		/* reset */
+	reg_w1(gspca_dev, 0x17, 0x42);
+	if (val == 0x99) {			/* gc0307 (?) */
+		PDEBUG(D_PROBE, "Sensor gc0307");
+		sd->sensor = SENSOR_GC0307;
+		return;
+	}
+
+	/* check po2030n */
+	reg_w1(gspca_dev, 0x17, 0x62);
+	reg_w1(gspca_dev, 0x01, 0x0a);
+	sd->i2c_addr = 0x6e;
+	i2c_r(gspca_dev, 0x00, 2);
+	val = (gspca_dev->usb_buf[3] << 8) | gspca_dev->usb_buf[4];
+	reg_w1(gspca_dev, 0x01, 0x29);
+	reg_w1(gspca_dev, 0x17, 0x42);
+	if (gspca_dev->usb_err < 0)
+		return;
+	if (val == 0x2030) {
+		PDEBUG(D_PROBE, "Sensor po2030n");
+/*		sd->sensor = SENSOR_PO2030N; */
+	} else {
+		pr_err("Unknown sensor ID %04x\n", val);
+	}
+}
+
+/* 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;
+
+	sd->bridge = id->driver_info >> 16;
+	sd->sensor = id->driver_info >> 8;
+	sd->flags = id->driver_info;
+
+	cam = &gspca_dev->cam;
+	if (sd->sensor == SENSOR_ADCM1700) {
+		cam->cam_mode = cif_mode;
+		cam->nmodes = ARRAY_SIZE(cif_mode);
+	} else {
+		cam->cam_mode = vga_mode;
+		cam->nmodes = ARRAY_SIZE(vga_mode);
+	}
+	cam->npkt = 24;			/* 24 packets per ISOC message */
+
+	sd->ag_cnt = -1;
+	sd->quality = QUALITY_DEF;
+
+	INIT_WORK(&sd->work, qual_upd);
+
+	return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	const u8 *sn9c1xx;
+	u8 regGpio[] = { 0x29, 0x70 };		/* no audio */
+	u8 regF1;
+
+	/* setup a selector by bridge */
+	reg_w1(gspca_dev, 0xf1, 0x01);
+	reg_r(gspca_dev, 0x00, 1);
+	reg_w1(gspca_dev, 0xf1, 0x00);
+	reg_r(gspca_dev, 0x00, 1);		/* get sonix chip id */
+	regF1 = gspca_dev->usb_buf[0];
+	if (gspca_dev->usb_err < 0)
+		return gspca_dev->usb_err;
+	PDEBUG(D_PROBE, "Sonix chip id: %02x", regF1);
+	if (gspca_dev->audio)
+		regGpio[1] |= 0x04;		/* with audio */
+	switch (sd->bridge) {
+	case BRIDGE_SN9C102P:
+	case BRIDGE_SN9C105:
+		if (regF1 != 0x11)
+			return -ENODEV;
+		break;
+	default:
+/*	case BRIDGE_SN9C110: */
+/*	case BRIDGE_SN9C120: */
+		if (regF1 != 0x12)
+			return -ENODEV;
+	}
+
+	switch (sd->sensor) {
+	case SENSOR_MI0360:
+		mi0360_probe(gspca_dev);
+		break;
+	case SENSOR_OV7630:
+		ov7630_probe(gspca_dev);
+		break;
+	case SENSOR_OV7648:
+		ov7648_probe(gspca_dev);
+		break;
+	case SENSOR_PO2030N:
+		po2030n_probe(gspca_dev);
+		break;
+	}
+
+	switch (sd->bridge) {
+	case BRIDGE_SN9C102P:
+		reg_w1(gspca_dev, 0x02, regGpio[1]);
+		break;
+	default:
+		reg_w(gspca_dev, 0x01, regGpio, 2);
+		break;
+	}
+
+	/* Note we do not disable the sensor clock here (power saving mode),
+	   as that also disables the button on the cam. */
+	reg_w1(gspca_dev, 0xf1, 0x00);
+
+	/* set the i2c address */
+	sn9c1xx = sn_tb[sd->sensor];
+	sd->i2c_addr = sn9c1xx[9];
+
+	return gspca_dev->usb_err;
+}
+
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl);
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+	.s_ctrl = sd_s_ctrl,
+};
+
+/* this function is called at probe time */
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+	gspca_dev->vdev.ctrl_handler = hdl;
+	v4l2_ctrl_handler_init(hdl, 14);
+
+	sd->brightness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+#define CONTRAST_MAX 127
+	sd->contrast = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_CONTRAST, 0, CONTRAST_MAX, 1, 20);
+#define COLORS_DEF 25
+	sd->saturation = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_SATURATION, 0, 40, 1, COLORS_DEF);
+	sd->red_bal = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_RED_BALANCE, 24, 40, 1, 32);
+	sd->blue_bal = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_BLUE_BALANCE, 24, 40, 1, 32);
+#define GAMMA_DEF 20
+	sd->gamma = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_GAMMA, 0, 40, 1, GAMMA_DEF);
+
+	if (sd->sensor == SENSOR_OM6802)
+		sd->sharpness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_SHARPNESS, 0, 255, 1, 16);
+	else
+		sd->sharpness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_SHARPNESS, 0, 255, 1, 90);
+
+	if (sd->flags & F_ILLUM)
+		sd->illum = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_ILLUMINATORS_1, 0, 1, 1, 0);
+
+	if (sd->sensor == SENSOR_PO2030N) {
+		gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_EXPOSURE, 500, 1500, 1, 1024);
+		gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_GAIN, 4, 49, 1, 15);
+		sd->hflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_HFLIP, 0, 1, 1, 0);
+	}
+
+	if (sd->sensor != SENSOR_ADCM1700 && sd->sensor != SENSOR_OV7660 &&
+	    sd->sensor != SENSOR_PO1030 && sd->sensor != SENSOR_SOI768 &&
+	    sd->sensor != SENSOR_SP80708)
+		gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+
+	if (sd->sensor == SENSOR_HV7131R || sd->sensor == SENSOR_OV7630 ||
+	    sd->sensor == SENSOR_OV7648 || sd->sensor == SENSOR_PO2030N)
+		sd->vflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_VFLIP, 0, 1, 1, 0);
+
+	if (sd->sensor == SENSOR_OV7630 || sd->sensor == SENSOR_OV7648 ||
+	    sd->sensor == SENSOR_OV7660)
+		sd->freq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
+			V4L2_CID_POWER_LINE_FREQUENCY,
+			V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0,
+			V4L2_CID_POWER_LINE_FREQUENCY_50HZ);
+
+	if (hdl->error) {
+		pr_err("Could not initialize controls\n");
+		return hdl->error;
+	}
+
+	v4l2_ctrl_cluster(2, &sd->red_bal);
+	if (sd->sensor == SENSOR_PO2030N) {
+		v4l2_ctrl_cluster(2, &sd->vflip);
+		v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false);
+	}
+
+	return 0;
+}
+
+static u32 expo_adjust(struct gspca_dev *gspca_dev,
+			u32 expo)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	switch (sd->sensor) {
+	case SENSOR_GC0307: {
+		int a, b;
+
+		/* expo = 0..255 -> a = 19..43 */
+		a = 19 + expo * 25 / 256;
+		i2c_w1(gspca_dev, 0x68, a);
+		a -= 12;
+		b = a * a * 4;			/* heuristic */
+		i2c_w1(gspca_dev, 0x03, b >> 8);
+		i2c_w1(gspca_dev, 0x04, b);
+		break;
+	    }
+	case SENSOR_HV7131R: {
+		u8 Expodoit[] =
+			{ 0xc1, 0x11, 0x25, 0x00, 0x00, 0x00, 0x00, 0x16 };
+
+		Expodoit[3] = expo >> 16;
+		Expodoit[4] = expo >> 8;
+		Expodoit[5] = expo;
+		i2c_w8(gspca_dev, Expodoit);
+		break;
+	    }
+	case SENSOR_MI0360:
+	case SENSOR_MI0360B: {
+		u8 expoMi[] =		/* exposure 0x0635 -> 4 fp/s 0x10 */
+			{ 0xb1, 0x5d, 0x09, 0x00, 0x00, 0x00, 0x00, 0x16 };
+		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 };
+
+		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, 0x00, 0x00, 0x00, 0x00, 0x10 };
+		u8 expoMo10[] =
+			{ 0xa1, 0x21, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10 };
+		static const u8 gainMo[] =
+			{ 0xa1, 0x21, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1d };
+
+		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_FRAM, "set exposure %d",
+			((expoMo10[3] & 0x07) << 10)
+			| (expoMof[3] << 2)
+			| ((expoMo10[3] & 0x30) >> 4));
+		break;
+	    }
+	case SENSOR_MT9V111: {
+		u8 expo_c1[] =
+			{ 0xb1, 0x5c, 0x09, 0x00, 0x00, 0x00, 0x00, 0x10 };
+
+		if (expo > 0x0390)
+			expo = 0x0390;
+		else if (expo < 0x0060)
+			expo = 0x0060;
+		expo_c1[3] = expo >> 8;
+		expo_c1[4] = expo;
+		i2c_w8(gspca_dev, expo_c1);
+		break;
+	    }
+	case SENSOR_OM6802: {
+		u8 gainOm[] =
+			{ 0xa0, 0x34, 0xe5, 0x00, 0x00, 0x00, 0x00, 0x10 };
+				/* preset AGC - works when AutoExpo = off */
+
+		if (expo > 0x03ff)
+			expo = 0x03ff;
+		if (expo < 0x0001)
+			expo = 0x0001;
+		gainOm[3] = expo >> 2;
+		i2c_w8(gspca_dev, gainOm);
+		reg_w1(gspca_dev, 0x96, expo >> 5);
+		PDEBUG(D_FRAM, "set exposure %d", gainOm[3]);
+		break;
+	    }
+	}
+	return expo;
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	unsigned int expo;
+	int brightness = sd->brightness->val;
+	u8 k2;
+
+	k2 = (brightness - 0x80) >> 2;
+	switch (sd->sensor) {
+	case SENSOR_ADCM1700:
+		if (k2 > 0x1f)
+			k2 = 0;		/* only positive Y offset */
+		break;
+	case SENSOR_HV7131R:
+		expo = brightness << 12;
+		if (expo > 0x002dc6c0)
+			expo = 0x002dc6c0;
+		else if (expo < 0x02a0)
+			expo = 0x02a0;
+		sd->exposure = expo_adjust(gspca_dev, expo);
+		break;
+	case SENSOR_MI0360:
+	case SENSOR_MO4000:
+		expo = brightness << 4;
+		sd->exposure = expo_adjust(gspca_dev, expo);
+		break;
+	case SENSOR_MI0360B:
+		expo = brightness << 2;
+		sd->exposure = expo_adjust(gspca_dev, expo);
+		break;
+	case SENSOR_GC0307:
+		expo = brightness;
+		sd->exposure = expo_adjust(gspca_dev, expo);
+		return;			/* don't set the Y offset */
+	case SENSOR_MT9V111:
+		expo = brightness << 2;
+		sd->exposure = expo_adjust(gspca_dev, expo);
+		return;			/* don't set the Y offset */
+	case SENSOR_OM6802:
+		expo = brightness << 2;
+		sd->exposure = expo_adjust(gspca_dev, expo);
+		return;			/* Y offset already set */
+	}
+
+	reg_w1(gspca_dev, 0x96, k2);	/* color matrix Y offset */
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u8 k2;
+	u8 contrast[6];
+
+	k2 = sd->contrast->val * 37 / (CONTRAST_MAX + 1)
+				+ 37;		/* 37..73 */
+	contrast[0] = (k2 + 1) / 2;		/* red */
+	contrast[1] = 0;
+	contrast[2] = k2;			/* green */
+	contrast[3] = 0;
+	contrast[4] = k2 / 5;			/* blue */
+	contrast[5] = 0;
+	reg_w(gspca_dev, 0x84, contrast, sizeof contrast);
+}
+
+static void setcolors(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int i, v, colors;
+	const s16 *uv;
+	u8 reg8a[12];			/* U & V gains */
+	static const s16 uv_com[6] = {	/* same as reg84 in signed decimal */
+		-24, -38, 64,		/* UR UG UB */
+		 62, -51, -9		/* VR VG VB */
+	};
+	static const s16 uv_mi0360b[6] = {
+		-20, -38, 64,		/* UR UG UB */
+		 60, -51, -9		/* VR VG VB */
+	};
+
+	colors = sd->saturation->val;
+	if (sd->sensor == SENSOR_MI0360B)
+		uv = uv_mi0360b;
+	else
+		uv = uv_com;
+	for (i = 0; i < 6; i++) {
+		v = uv[i] * colors / COLORS_DEF;
+		reg8a[i * 2] = v;
+		reg8a[i * 2 + 1] = (v >> 8) & 0x0f;
+	}
+	reg_w(gspca_dev, 0x8a, reg8a, sizeof reg8a);
+}
+
+static void setredblue(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (sd->sensor == SENSOR_PO2030N) {
+		u8 rg1b[] =		/* red  green1 blue (no g2) */
+			{0xc1, 0x6e, 0x16, 0x00, 0x40, 0x00, 0x00, 0x10};
+
+		/* 0x40 = normal value = gain x 1 */
+		rg1b[3] = sd->red_bal->val * 2;
+		rg1b[5] = sd->blue_bal->val * 2;
+		i2c_w8(gspca_dev, rg1b);
+		return;
+	}
+	reg_w1(gspca_dev, 0x05, sd->red_bal->val);
+/*	reg_w1(gspca_dev, 0x07, 32); */
+	reg_w1(gspca_dev, 0x06, sd->blue_bal->val);
+}
+
+static void setgamma(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int i, val;
+	u8 gamma[17];
+	const u8 *gamma_base;
+	static const u8 delta[17] = {
+		0x00, 0x14, 0x1c, 0x1c, 0x1c, 0x1c, 0x1b, 0x1a,
+		0x18, 0x13, 0x10, 0x0e, 0x08, 0x07, 0x04, 0x02, 0x00
+	};
+
+	switch (sd->sensor) {
+	case SENSOR_ADCM1700:
+		gamma_base = gamma_spec_0;
+		break;
+	case SENSOR_HV7131R:
+	case SENSOR_MI0360B:
+	case SENSOR_MT9V111:
+		gamma_base = gamma_spec_1;
+		break;
+	case SENSOR_GC0307:
+		gamma_base = gamma_spec_2;
+		break;
+	case SENSOR_SP80708:
+		gamma_base = gamma_spec_3;
+		break;
+	default:
+		gamma_base = gamma_def;
+		break;
+	}
+
+	val = sd->gamma->val;
+	for (i = 0; i < sizeof gamma; i++)
+		gamma[i] = gamma_base[i]
+			+ delta[i] * (val - GAMMA_DEF) / 32;
+	reg_w(gspca_dev, 0x20, gamma, sizeof gamma);
+}
+
+static void setexposure(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (sd->sensor == SENSOR_PO2030N) {
+		u8 rexpo[] =		/* 1a: expo H, 1b: expo M */
+			{0xa1, 0x6e, 0x1a, 0x00, 0x40, 0x00, 0x00, 0x10};
+
+		rexpo[3] = gspca_dev->exposure->val >> 8;
+		i2c_w8(gspca_dev, rexpo);
+		msleep(6);
+		rexpo[2] = 0x1b;
+		rexpo[3] = gspca_dev->exposure->val;
+		i2c_w8(gspca_dev, rexpo);
+	}
+}
+
+static void setautogain(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	switch (sd->sensor) {
+	case SENSOR_OV7630:
+	case SENSOR_OV7648: {
+		u8 comb;
+
+		if (sd->sensor == SENSOR_OV7630)
+			comb = 0xc0;
+		else
+			comb = 0xa0;
+		if (gspca_dev->autogain->val)
+			comb |= 0x03;
+		i2c_w1(&sd->gspca_dev, 0x13, comb);
+		return;
+	    }
+	}
+	if (gspca_dev->autogain->val)
+		sd->ag_cnt = AG_CNT_START;
+	else
+		sd->ag_cnt = -1;
+}
+
+static void setgain(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (sd->sensor == SENSOR_PO2030N) {
+		u8 rgain[] =		/* 15: gain */
+			{0xa1, 0x6e, 0x15, 0x00, 0x40, 0x00, 0x00, 0x15};
+
+		rgain[3] = gspca_dev->gain->val;
+		i2c_w8(gspca_dev, rgain);
+	}
+}
+
+static void sethvflip(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u8 comn;
+
+	switch (sd->sensor) {
+	case SENSOR_HV7131R:
+		comn = 0x18;			/* clkdiv = 1, ablcen = 1 */
+		if (sd->vflip->val)
+			comn |= 0x01;
+		i2c_w1(gspca_dev, 0x01, comn);	/* sctra */
+		break;
+	case SENSOR_OV7630:
+		comn = 0x02;
+		if (!sd->vflip->val)
+			comn |= 0x80;
+		i2c_w1(gspca_dev, 0x75, comn);
+		break;
+	case SENSOR_OV7648:
+		comn = 0x06;
+		if (sd->vflip->val)
+			comn |= 0x80;
+		i2c_w1(gspca_dev, 0x75, comn);
+		break;
+	case SENSOR_PO2030N:
+		/* Reg. 0x1E: Timing Generator Control Register 2 (Tgcontrol2)
+		 * (reset value: 0x0A)
+		 * bit7: HM: Horizontal Mirror: 0: disable, 1: enable
+		 * bit6: VM: Vertical Mirror: 0: disable, 1: enable
+		 * bit5: ST: Shutter Selection: 0: electrical, 1: mechanical
+		 * bit4: FT: Single Frame Transfer: 0: disable, 1: enable
+		 * bit3-0: X
+		 */
+		comn = 0x0a;
+		if (sd->hflip->val)
+			comn |= 0x80;
+		if (sd->vflip->val)
+			comn |= 0x40;
+		i2c_w1(&sd->gspca_dev, 0x1e, comn);
+		break;
+	}
+}
+
+static void setsharpness(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	reg_w1(gspca_dev, 0x99, sd->sharpness->val);
+}
+
+static void setillum(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	switch (sd->sensor) {
+	case SENSOR_ADCM1700:
+		reg_w1(gspca_dev, 0x02,				/* gpio */
+			sd->illum->val ? 0x64 : 0x60);
+		break;
+	case SENSOR_MT9V111:
+		reg_w1(gspca_dev, 0x02,
+			sd->illum->val ? 0x77 : 0x74);
+/* should have been: */
+/*						0x55 : 0x54);	* 370i */
+/*						0x66 : 0x64);	* Clip */
+		break;
+	}
+}
+
+static void setfreq(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (sd->sensor == SENSOR_OV7660) {
+		u8 com8;
+
+		com8 = 0xdf;		/* auto gain/wb/expo */
+		switch (sd->freq->val) {
+		case 0: /* Banding filter disabled */
+			i2c_w1(gspca_dev, 0x13, com8 | 0x20);
+			break;
+		case 1: /* 50 hz */
+			i2c_w1(gspca_dev, 0x13, com8);
+			i2c_w1(gspca_dev, 0x3b, 0x0a);
+			break;
+		case 2: /* 60 hz */
+			i2c_w1(gspca_dev, 0x13, com8);
+			i2c_w1(gspca_dev, 0x3b, 0x02);
+			break;
+		}
+	} else {
+		u8 reg2a = 0, reg2b = 0, reg2d = 0;
+
+		/* Get reg2a / reg2d base values */
+		switch (sd->sensor) {
+		case SENSOR_OV7630:
+			reg2a = 0x08;
+			reg2d = 0x01;
+			break;
+		case SENSOR_OV7648:
+			reg2a = 0x11;
+			reg2d = 0x81;
+			break;
+		}
+
+		switch (sd->freq->val) {
+		case 0: /* Banding filter disabled */
+			break;
+		case 1: /* 50 hz (filter on and framerate adj) */
+			reg2a |= 0x80;
+			reg2b = 0xac;
+			reg2d |= 0x04;
+			break;
+		case 2: /* 60 hz (filter on, no framerate adj) */
+			reg2a |= 0x80;
+			reg2d |= 0x04;
+			break;
+		}
+		i2c_w1(gspca_dev, 0x2a, reg2a);
+		i2c_w1(gspca_dev, 0x2b, reg2b);
+		i2c_w1(gspca_dev, 0x2d, reg2d);
+	}
+}
+
+static void setjpegqual(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	jpeg_set_qual(sd->jpeg_hdr, sd->quality);
+#if USB_BUF_SZ < 64
+#error "No room enough in usb_buf for quantization table"
+#endif
+	memcpy(gspca_dev->usb_buf, &sd->jpeg_hdr[JPEG_QT0_OFFSET], 64);
+	usb_control_msg(gspca_dev->dev,
+			usb_sndctrlpipe(gspca_dev->dev, 0),
+			0x08,
+			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+			0x0100, 0,
+			gspca_dev->usb_buf, 64,
+			500);
+	memcpy(gspca_dev->usb_buf, &sd->jpeg_hdr[JPEG_QT1_OFFSET], 64);
+	usb_control_msg(gspca_dev->dev,
+			usb_sndctrlpipe(gspca_dev->dev, 0),
+			0x08,
+			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+			0x0140, 0,
+			gspca_dev->usb_buf, 64,
+			500);
+
+	sd->reg18 ^= 0x40;
+	reg_w1(gspca_dev, 0x18, sd->reg18);
+}
+
+/* JPEG quality update */
+/* This function is executed from a work queue. */
+static void qual_upd(struct work_struct *work)
+{
+	struct sd *sd = container_of(work, struct sd, work);
+	struct gspca_dev *gspca_dev = &sd->gspca_dev;
+
+	/* To protect gspca_dev->usb_buf and gspca_dev->usb_err */
+	mutex_lock(&gspca_dev->usb_lock);
+	PDEBUG(D_STREAM, "qual_upd %d%%", sd->quality);
+	gspca_dev->usb_err = 0;
+	setjpegqual(gspca_dev);
+	mutex_unlock(&gspca_dev->usb_lock);
+}
+
+/* -- start the camera -- */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int i;
+	u8 reg01, reg17;
+	u8 reg0102[2];
+	const u8 *sn9c1xx;
+	const u8 (*init)[8];
+	const u8 *reg9a;
+	int mode;
+	static const u8 reg9a_def[] =
+		{0x00, 0x40, 0x20, 0x00, 0x00, 0x00};
+	static const u8 reg9a_spec[] =
+		{0x00, 0x40, 0x38, 0x30, 0x00, 0x20};
+	static const u8 regd4[] = {0x60, 0x00, 0x00};
+	static const u8 C0[] = { 0x2d, 0x2d, 0x3a, 0x05, 0x04, 0x3f };
+	static const u8 CA[] = { 0x28, 0xd8, 0x14, 0xec };
+	static const u8 CA_adcm1700[] =
+				{ 0x14, 0xec, 0x0a, 0xf6 };
+	static const u8 CA_po2030n[] =
+				{ 0x1e, 0xe2, 0x14, 0xec };
+	static const u8 CE[] = { 0x32, 0xdd, 0x2d, 0xdd };	/* MI0360 */
+	static const u8 CE_gc0307[] =
+				{ 0x32, 0xce, 0x2d, 0xd3 };
+	static const u8 CE_ov76xx[] =
+				{ 0x32, 0xdd, 0x32, 0xdd };
+	static const u8 CE_po2030n[] =
+				{ 0x14, 0xe7, 0x1e, 0xdd };
+
+	/* create the JPEG header */
+	jpeg_define(sd->jpeg_hdr, gspca_dev->pixfmt.height,
+			gspca_dev->pixfmt.width,
+			0x21);		/* JPEG 422 */
+
+	/* initialize the bridge */
+	sn9c1xx = sn_tb[sd->sensor];
+
+	/* sensor clock already enabled in sd_init */
+	/* reg_w1(gspca_dev, 0xf1, 0x00); */
+	reg01 = sn9c1xx[1];
+	if (sd->flags & F_PDN_INV)
+		reg01 ^= S_PDN_INV;		/* power down inverted */
+	reg_w1(gspca_dev, 0x01, reg01);
+
+	/* configure gpio */
+	reg0102[0] = reg01;
+	reg0102[1] = sn9c1xx[2];
+	if (gspca_dev->audio)
+		reg0102[1] |= 0x04;	/* keep the audio connection */
+	reg_w(gspca_dev, 0x01, reg0102, 2);
+	reg_w(gspca_dev, 0x08, &sn9c1xx[8], 2);
+	reg_w(gspca_dev, 0x17, &sn9c1xx[0x17], 5);
+	switch (sd->sensor) {
+	case SENSOR_GC0307:
+	case SENSOR_OV7660:
+	case SENSOR_PO1030:
+	case SENSOR_PO2030N:
+	case SENSOR_SOI768:
+	case SENSOR_SP80708:
+		reg9a = reg9a_spec;
+		break;
+	default:
+		reg9a = reg9a_def;
+		break;
+	}
+	reg_w(gspca_dev, 0x9a, reg9a, 6);
+
+	reg_w(gspca_dev, 0xd4, regd4, sizeof regd4);
+
+	reg_w(gspca_dev, 0x03, &sn9c1xx[3], 0x0f);
+
+	reg17 = sn9c1xx[0x17];
+	switch (sd->sensor) {
+	case SENSOR_GC0307:
+		msleep(50);		/*fixme: is it useful? */
+		break;
+	case SENSOR_OM6802:
+		msleep(10);
+		reg_w1(gspca_dev, 0x02, 0x73);
+		reg17 |= SEN_CLK_EN;
+		reg_w1(gspca_dev, 0x17, reg17);
+		reg_w1(gspca_dev, 0x01, 0x22);
+		msleep(100);
+		reg01 = SCL_SEL_OD | S_PDN_INV;
+		reg17 &= ~MCK_SIZE_MASK;
+		reg17 |= 0x04;		/* clock / 4 */
+		break;
+	}
+	reg01 |= SYS_SEL_48M;
+	reg_w1(gspca_dev, 0x01, reg01);
+	reg17 |= SEN_CLK_EN;
+	reg_w1(gspca_dev, 0x17, reg17);
+	reg01 &= ~S_PWR_DN;		/* sensor power on */
+	reg_w1(gspca_dev, 0x01, reg01);
+	reg01 &= ~SCL_SEL_OD;		/* remove open-drain mode */
+	reg_w1(gspca_dev, 0x01, reg01);
+
+	switch (sd->sensor) {
+	case SENSOR_HV7131R:
+		hv7131r_probe(gspca_dev);	/*fixme: is it useful? */
+		break;
+	case SENSOR_OM6802:
+		msleep(10);
+		reg_w1(gspca_dev, 0x01, reg01);
+		i2c_w8(gspca_dev, om6802_init0[0]);
+		i2c_w8(gspca_dev, om6802_init0[1]);
+		msleep(15);
+		reg_w1(gspca_dev, 0x02, 0x71);
+		msleep(150);
+		break;
+	case SENSOR_SP80708:
+		msleep(100);
+		reg_w1(gspca_dev, 0x02, 0x62);
+		break;
+	}
+
+	/* initialize the sensor */
+	i2c_w_seq(gspca_dev, sensor_init[sd->sensor]);
+
+	reg_w1(gspca_dev, 0x15, sn9c1xx[0x15]);
+	reg_w1(gspca_dev, 0x16, sn9c1xx[0x16]);
+	reg_w1(gspca_dev, 0x12, sn9c1xx[0x12]);
+	reg_w1(gspca_dev, 0x13, sn9c1xx[0x13]);
+	reg_w1(gspca_dev, 0x18, sn9c1xx[0x18]);
+	if (sd->sensor == SENSOR_ADCM1700) {
+		reg_w1(gspca_dev, 0xd2, 0x3a);	/* AE_H_SIZE = 116 */
+		reg_w1(gspca_dev, 0xd3, 0x30);	/* AE_V_SIZE = 96 */
+	} else {
+		reg_w1(gspca_dev, 0xd2, 0x6a);	/* AE_H_SIZE = 212 */
+		reg_w1(gspca_dev, 0xd3, 0x50);	/* AE_V_SIZE = 160 */
+	}
+	reg_w1(gspca_dev, 0xc6, 0x00);
+	reg_w1(gspca_dev, 0xc7, 0x00);
+	if (sd->sensor == SENSOR_ADCM1700) {
+		reg_w1(gspca_dev, 0xc8, 0x2c);	/* AW_H_STOP = 352 */
+		reg_w1(gspca_dev, 0xc9, 0x24);	/* AW_V_STOP = 288 */
+	} else {
+		reg_w1(gspca_dev, 0xc8, 0x50);	/* AW_H_STOP = 640 */
+		reg_w1(gspca_dev, 0xc9, 0x3c);	/* AW_V_STOP = 480 */
+	}
+	reg_w1(gspca_dev, 0x18, sn9c1xx[0x18]);
+	switch (sd->sensor) {
+	case SENSOR_OM6802:
+/*	case SENSOR_OV7648:		* fixme: sometimes */
+		break;
+	default:
+		reg17 |= DEF_EN;
+		break;
+	}
+	reg_w1(gspca_dev, 0x17, reg17);
+
+	reg_w1(gspca_dev, 0x05, 0x00);		/* red */
+	reg_w1(gspca_dev, 0x07, 0x00);		/* green */
+	reg_w1(gspca_dev, 0x06, 0x00);		/* blue */
+	reg_w1(gspca_dev, 0x14, sn9c1xx[0x14]);
+
+	setgamma(gspca_dev);
+
+/*fixme: 8 times with all zeroes and 1 or 2 times with normal values */
+	for (i = 0; i < 8; i++)
+		reg_w(gspca_dev, 0x84, reg84, sizeof reg84);
+	switch (sd->sensor) {
+	case SENSOR_ADCM1700:
+	case SENSOR_OV7660:
+	case SENSOR_SP80708:
+		reg_w1(gspca_dev, 0x9a, 0x05);
+		break;
+	case SENSOR_GC0307:
+	case SENSOR_MT9V111:
+	case SENSOR_MI0360B:
+		reg_w1(gspca_dev, 0x9a, 0x07);
+		break;
+	case SENSOR_OV7630:
+	case SENSOR_OV7648:
+		reg_w1(gspca_dev, 0x9a, 0x0a);
+		break;
+	case SENSOR_PO2030N:
+	case SENSOR_SOI768:
+		reg_w1(gspca_dev, 0x9a, 0x06);
+		break;
+	default:
+		reg_w1(gspca_dev, 0x9a, 0x08);
+		break;
+	}
+	setsharpness(gspca_dev);
+
+	reg_w(gspca_dev, 0x84, reg84, sizeof reg84);
+	reg_w1(gspca_dev, 0x05, 0x20);		/* red */
+	reg_w1(gspca_dev, 0x07, 0x20);		/* green */
+	reg_w1(gspca_dev, 0x06, 0x20);		/* blue */
+
+	init = NULL;
+	mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
+	reg01 |= SYS_SEL_48M | V_TX_EN;
+	reg17 &= ~MCK_SIZE_MASK;
+	reg17 |= 0x02;			/* clock / 2 */
+	switch (sd->sensor) {
+	case SENSOR_ADCM1700:
+		init = adcm1700_sensor_param1;
+		break;
+	case SENSOR_GC0307:
+		init = gc0307_sensor_param1;
+		break;
+	case SENSOR_HV7131R:
+	case SENSOR_MI0360:
+		if (!mode)
+			reg01 &= ~SYS_SEL_48M;	/* 640x480: clk 24Mhz */
+		reg17 &= ~MCK_SIZE_MASK;
+		reg17 |= 0x01;			/* clock / 1 */
+		break;
+	case SENSOR_MI0360B:
+		init = mi0360b_sensor_param1;
+		break;
+	case SENSOR_MO4000:
+		if (mode) {			/* if 320x240 */
+			reg01 &= ~SYS_SEL_48M;	/* clk 24Mz */
+			reg17 &= ~MCK_SIZE_MASK;
+			reg17 |= 0x01;		/* clock / 1 */
+		}
+		break;
+	case SENSOR_MT9V111:
+		init = mt9v111_sensor_param1;
+		break;
+	case SENSOR_OM6802:
+		init = om6802_sensor_param1;
+		if (!mode) {			/* if 640x480 */
+			reg17 &= ~MCK_SIZE_MASK;
+			reg17 |= 0x04;		/* clock / 4 */
+		} else {
+			reg01 &= ~SYS_SEL_48M;	/* clk 24Mz */
+			reg17 &= ~MCK_SIZE_MASK;
+			reg17 |= 0x02;		/* clock / 2 */
+		}
+		break;
+	case SENSOR_OV7630:
+		init = ov7630_sensor_param1;
+		break;
+	case SENSOR_OV7648:
+		init = ov7648_sensor_param1;
+		reg17 &= ~MCK_SIZE_MASK;
+		reg17 |= 0x01;			/* clock / 1 */
+		break;
+	case SENSOR_OV7660:
+		init = ov7660_sensor_param1;
+		break;
+	case SENSOR_PO1030:
+		init = po1030_sensor_param1;
+		break;
+	case SENSOR_PO2030N:
+		init = po2030n_sensor_param1;
+		break;
+	case SENSOR_SOI768:
+		init = soi768_sensor_param1;
+		break;
+	case SENSOR_SP80708:
+		init = sp80708_sensor_param1;
+		break;
+	}
+
+	/* more sensor initialization - param1 */
+	if (init != NULL) {
+		i2c_w_seq(gspca_dev, init);
+/*		init = NULL; */
+	}
+
+	reg_w(gspca_dev, 0xc0, C0, 6);
+	switch (sd->sensor) {
+	case SENSOR_ADCM1700:
+	case SENSOR_GC0307:
+	case SENSOR_SOI768:
+		reg_w(gspca_dev, 0xca, CA_adcm1700, 4);
+		break;
+	case SENSOR_PO2030N:
+		reg_w(gspca_dev, 0xca, CA_po2030n, 4);
+		break;
+	default:
+		reg_w(gspca_dev, 0xca, CA, 4);
+		break;
+	}
+	switch (sd->sensor) {
+	case SENSOR_ADCM1700:
+	case SENSOR_OV7630:
+	case SENSOR_OV7648:
+	case SENSOR_OV7660:
+	case SENSOR_SOI768:
+		reg_w(gspca_dev, 0xce, CE_ov76xx, 4);
+		break;
+	case SENSOR_GC0307:
+		reg_w(gspca_dev, 0xce, CE_gc0307, 4);
+		break;
+	case SENSOR_PO2030N:
+		reg_w(gspca_dev, 0xce, CE_po2030n, 4);
+		break;
+	default:
+		reg_w(gspca_dev, 0xce, CE, 4);
+					/* ?? {0x1e, 0xdd, 0x2d, 0xe7} */
+		break;
+	}
+
+	/* here change size mode 0 -> VGA; 1 -> CIF */
+	sd->reg18 = sn9c1xx[0x18] | (mode << 4) | 0x40;
+	reg_w1(gspca_dev, 0x18, sd->reg18);
+	setjpegqual(gspca_dev);
+
+	reg_w1(gspca_dev, 0x17, reg17);
+	reg_w1(gspca_dev, 0x01, reg01);
+	sd->reg01 = reg01;
+	sd->reg17 = reg17;
+
+	sd->pktsz = sd->npkt = 0;
+	sd->nchg = sd->short_mark = 0;
+	sd->work_thread = create_singlethread_workqueue(MODULE_NAME);
+
+	return gspca_dev->usb_err;
+}
+
+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 };
+	static const u8 stopov7648[] =
+		{ 0xa1, 0x21, 0x76, 0x20, 0x00, 0x00, 0x00, 0x10 };
+	static const u8 stopsoi768[] =
+		{ 0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10 };
+	u8 reg01;
+	u8 reg17;
+
+	reg01 = sd->reg01;
+	reg17 = sd->reg17 & ~SEN_CLK_EN;
+	switch (sd->sensor) {
+	case SENSOR_ADCM1700:
+	case SENSOR_GC0307:
+	case SENSOR_PO2030N:
+	case SENSOR_SP80708:
+		reg01 |= LED;
+		reg_w1(gspca_dev, 0x01, reg01);
+		reg01 &= ~(LED | V_TX_EN);
+		reg_w1(gspca_dev, 0x01, reg01);
+/*		reg_w1(gspca_dev, 0x02, 0x??);	 * LED off ? */
+		break;
+	case SENSOR_HV7131R:
+		reg01 &= ~V_TX_EN;
+		reg_w1(gspca_dev, 0x01, reg01);
+		i2c_w8(gspca_dev, stophv7131);
+		break;
+	case SENSOR_MI0360:
+	case SENSOR_MI0360B:
+		reg01 &= ~V_TX_EN;
+		reg_w1(gspca_dev, 0x01, reg01);
+/*		reg_w1(gspca_dev, 0x02, 0x40);	  * LED off ? */
+		i2c_w8(gspca_dev, stopmi0360);
+		break;
+	case SENSOR_MT9V111:
+	case SENSOR_OM6802:
+	case SENSOR_PO1030:
+		reg01 &= ~V_TX_EN;
+		reg_w1(gspca_dev, 0x01, reg01);
+		break;
+	case SENSOR_OV7630:
+	case SENSOR_OV7648:
+		reg01 &= ~V_TX_EN;
+		reg_w1(gspca_dev, 0x01, reg01);
+		i2c_w8(gspca_dev, stopov7648);
+		break;
+	case SENSOR_OV7660:
+		reg01 &= ~V_TX_EN;
+		reg_w1(gspca_dev, 0x01, reg01);
+		break;
+	case SENSOR_SOI768:
+		i2c_w8(gspca_dev, stopsoi768);
+		break;
+	}
+
+	reg01 |= SCL_SEL_OD;
+	reg_w1(gspca_dev, 0x01, reg01);
+	reg01 |= S_PWR_DN;		/* sensor power down */
+	reg_w1(gspca_dev, 0x01, reg01);
+	reg_w1(gspca_dev, 0x17, reg17);
+	reg01 &= ~SYS_SEL_48M;		/* clock 24MHz */
+	reg_w1(gspca_dev, 0x01, reg01);
+	reg01 |= LED;
+	reg_w1(gspca_dev, 0x01, reg01);
+	/* Don't disable sensor clock as that disables the button on the cam */
+	/* reg_w1(gspca_dev, 0xf1, 0x01); */
+}
+
+/* called on streamoff with alt==0 and on disconnect */
+/* the usb_lock is held at entry - restore on exit */
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (sd->work_thread != NULL) {
+		mutex_unlock(&gspca_dev->usb_lock);
+		destroy_workqueue(sd->work_thread);
+		mutex_lock(&gspca_dev->usb_lock);
+		sd->work_thread = NULL;
+	}
+}
+
+static void do_autogain(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int delta;
+	int expotimes;
+	u8 luma_mean = 130;
+	u8 luma_delta = 20;
+
+	/* Thanks S., without your advice, autobright should not work :) */
+	if (sd->ag_cnt < 0)
+		return;
+	if (--sd->ag_cnt >= 0)
+		return;
+	sd->ag_cnt = AG_CNT_START;
+
+	delta = atomic_read(&sd->avg_lum);
+	PDEBUG(D_FRAM, "mean lum %d", delta);
+
+	if (sd->sensor == SENSOR_PO2030N) {
+		gspca_expo_autogain(gspca_dev, delta, luma_mean, luma_delta,
+					15, 1024);
+		return;
+	}
+
+	if (delta < luma_mean - luma_delta ||
+	    delta > luma_mean + luma_delta) {
+		switch (sd->sensor) {
+		case SENSOR_GC0307:
+			expotimes = sd->exposure;
+			expotimes += (luma_mean - delta) >> 6;
+			if (expotimes < 0)
+				expotimes = 0;
+			sd->exposure = expo_adjust(gspca_dev,
+						   (unsigned int) expotimes);
+			break;
+		case SENSOR_HV7131R:
+			expotimes = sd->exposure >> 8;
+			expotimes += (luma_mean - delta) >> 4;
+			if (expotimes < 0)
+				expotimes = 0;
+			sd->exposure = expo_adjust(gspca_dev,
+					(unsigned int) (expotimes << 8));
+			break;
+		case SENSOR_OM6802:
+		case SENSOR_MT9V111:
+			expotimes = sd->exposure;
+			expotimes += (luma_mean - delta) >> 2;
+			if (expotimes < 0)
+				expotimes = 0;
+			sd->exposure = expo_adjust(gspca_dev,
+						   (unsigned int) expotimes);
+			setredblue(gspca_dev);
+			break;
+		default:
+/*		case SENSOR_MO4000: */
+/*		case SENSOR_MI0360: */
+/*		case SENSOR_MI0360B: */
+			expotimes = sd->exposure;
+			expotimes += (luma_mean - delta) >> 6;
+			if (expotimes < 0)
+				expotimes = 0;
+			sd->exposure = expo_adjust(gspca_dev,
+						   (unsigned int) expotimes);
+			setredblue(gspca_dev);
+			break;
+		}
+	}
+}
+
+/* set the average luminosity from an isoc marker */
+static void set_lum(struct sd *sd,
+		    u8 *data)
+{
+	int avg_lum;
+
+	/*	w0 w1 w2
+	 *	w3 w4 w5
+	 *	w6 w7 w8
+	 */
+	avg_lum = (data[27] << 8) + data[28]		/* w3 */
+
+		+ (data[31] << 8) + data[32]		/* w5 */
+
+		+ (data[23] << 8) + data[24]		/* w1 */
+
+		+ (data[35] << 8) + data[36]		/* w7 */
+
+		+ (data[29] << 10) + (data[30] << 2);	/* w4 * 4 */
+	avg_lum >>= 10;
+	atomic_set(&sd->avg_lum, avg_lum);
+}
+
+/* scan the URB packets */
+/* This function is run at interrupt level. */
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+			u8 *data,			/* isoc packet */
+			int len)			/* iso packet length */
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int i, new_qual;
+
+	/*
+	 * A frame ends on the marker
+	 *		ff ff 00 c4 c4 96 ..
+	 * which is 62 bytes long and is followed by various information
+	 * including statuses and luminosity.
+	 *
+	 * A marker may be splitted on two packets.
+	 *
+	 * The 6th byte of a marker contains the bits:
+	 *	0x08: USB full
+	 *	0xc0: frame sequence
+	 * When the bit 'USB full' is set, the frame must be discarded;
+	 * this is also the case when the 2 bytes before the marker are
+	 * not the JPEG end of frame ('ff d9').
+	 */
+
+	/* count the packets and their size */
+	sd->npkt++;
+	sd->pktsz += len;
+
+/*fixme: assumption about the following code:
+ *	- there can be only one marker in a packet
+ */
+
+	/* skip the remaining bytes of a short marker */
+	i = sd->short_mark;
+	if (i != 0) {
+		sd->short_mark = 0;
+		if (i < 0	/* if 'ff' at end of previous packet */
+		 && data[0] == 0xff
+		 && data[1] == 0x00)
+			goto marker_found;
+		if (data[0] == 0xff && data[1] == 0xff) {
+			i = 0;
+			goto marker_found;
+		}
+		len -= i;
+		if (len <= 0)
+			return;
+		data += i;
+	}
+
+	/* search backwards if there is a marker in the packet */
+	for (i = len - 1; --i >= 0; ) {
+		if (data[i] != 0xff) {
+			i--;
+			continue;
+		}
+		if (data[i + 1] == 0xff) {
+
+			/* (there may be 'ff ff' inside a marker) */
+			if (i + 2 >= len || data[i + 2] == 0x00)
+				goto marker_found;
+		}
+	}
+
+	/* no marker found */
+	/* add the JPEG header if first fragment */
+	if (data[len - 1] == 0xff)
+		sd->short_mark = -1;
+	if (gspca_dev->last_packet_type == LAST_PACKET)
+		gspca_frame_add(gspca_dev, FIRST_PACKET,
+				sd->jpeg_hdr, JPEG_HDR_SZ);
+	gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+	return;
+
+	/* marker found */
+	/* if some error, discard the frame and decrease the quality */
+marker_found:
+	new_qual = 0;
+	if (i > 2) {
+		if (data[i - 2] != 0xff || data[i - 1] != 0xd9) {
+			gspca_dev->last_packet_type = DISCARD_PACKET;
+			new_qual = -3;
+		}
+	} else if (i + 6 < len) {
+		if (data[i + 6] & 0x08) {
+			gspca_dev->last_packet_type = DISCARD_PACKET;
+			new_qual = -5;
+		}
+	}
+
+	gspca_frame_add(gspca_dev, LAST_PACKET, data, i);
+
+	/* compute the filling rate and a new JPEG quality */
+	if (new_qual == 0) {
+		int r;
+
+		r = (sd->pktsz * 100) /
+			(sd->npkt *
+				gspca_dev->urb[0]->iso_frame_desc[0].length);
+		if (r >= 85)
+			new_qual = -3;
+		else if (r < 75)
+			new_qual = 2;
+	}
+	if (new_qual != 0) {
+		sd->nchg += new_qual;
+		if (sd->nchg < -6 || sd->nchg >= 12) {
+			sd->nchg = 0;
+			new_qual += sd->quality;
+			if (new_qual < QUALITY_MIN)
+				new_qual = QUALITY_MIN;
+			else if (new_qual > QUALITY_MAX)
+				new_qual = QUALITY_MAX;
+			if (new_qual != sd->quality) {
+				sd->quality = new_qual;
+				queue_work(sd->work_thread, &sd->work);
+			}
+		}
+	} else {
+		sd->nchg = 0;
+	}
+	sd->pktsz = sd->npkt = 0;
+
+	/* if the marker is smaller than 62 bytes,
+	 * memorize the number of bytes to skip in the next packet */
+	if (i + 62 > len) {			/* no more usable data */
+		sd->short_mark = i + 62 - len;
+		return;
+	}
+	if (sd->ag_cnt >= 0)
+		set_lum(sd, data + i);
+
+	/* if more data, start a new frame */
+	i += 62;
+	if (i < len) {
+		data += i;
+		len -= i;
+		gspca_frame_add(gspca_dev, FIRST_PACKET,
+				sd->jpeg_hdr, JPEG_HDR_SZ);
+		gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+	}
+}
+
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct gspca_dev *gspca_dev =
+		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+
+	gspca_dev->usb_err = 0;
+
+	if (!gspca_dev->streaming)
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_BRIGHTNESS:
+		setbrightness(gspca_dev);
+		break;
+	case V4L2_CID_CONTRAST:
+		setcontrast(gspca_dev);
+		break;
+	case V4L2_CID_SATURATION:
+		setcolors(gspca_dev);
+		break;
+	case V4L2_CID_RED_BALANCE:
+		setredblue(gspca_dev);
+		break;
+	case V4L2_CID_GAMMA:
+		setgamma(gspca_dev);
+		break;
+	case V4L2_CID_AUTOGAIN:
+		setautogain(gspca_dev);
+		setexposure(gspca_dev);
+		setgain(gspca_dev);
+		break;
+	case V4L2_CID_VFLIP:
+		sethvflip(gspca_dev);
+		break;
+	case V4L2_CID_SHARPNESS:
+		setsharpness(gspca_dev);
+		break;
+	case V4L2_CID_ILLUMINATORS_1:
+		setillum(gspca_dev);
+		break;
+	case V4L2_CID_POWER_LINE_FREQUENCY:
+		setfreq(gspca_dev);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return gspca_dev->usb_err;
+}
+
+#if IS_ENABLED(CONFIG_INPUT)
+static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
+			u8 *data,		/* interrupt packet data */
+			int len)		/* interrupt packet length */
+{
+	int ret = -EINVAL;
+
+	if (len == 1 && data[0] == 1) {
+		input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1);
+		input_sync(gspca_dev->input_dev);
+		input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
+		input_sync(gspca_dev->input_dev);
+		ret = 0;
+	}
+
+	return ret;
+}
+#endif
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+	.name = MODULE_NAME,
+	.config = sd_config,
+	.init = sd_init,
+	.init_controls = sd_init_controls,
+	.start = sd_start,
+	.stopN = sd_stopN,
+	.stop0 = sd_stop0,
+	.pkt_scan = sd_pkt_scan,
+	.dq_callback = do_autogain,
+#if IS_ENABLED(CONFIG_INPUT)
+	.int_pkt_scan = sd_int_pkt_scan,
+#endif
+};
+
+/* -- module initialisation -- */
+#define BS(bridge, sensor) \
+	.driver_info = (BRIDGE_ ## bridge << 16) \
+			| (SENSOR_ ## sensor << 8)
+#define BSF(bridge, sensor, flags) \
+	.driver_info = (BRIDGE_ ## bridge << 16) \
+			| (SENSOR_ ## sensor << 8) \
+			| (flags)
+static const struct usb_device_id device_table[] = {
+	{USB_DEVICE(0x0458, 0x7025), BSF(SN9C120, MI0360B, F_PDN_INV)},
+	{USB_DEVICE(0x0458, 0x702e), BS(SN9C120, OV7660)},
+	{USB_DEVICE(0x045e, 0x00f5), BSF(SN9C105, OV7660, F_PDN_INV)},
+	{USB_DEVICE(0x045e, 0x00f7), BSF(SN9C105, OV7660, F_PDN_INV)},
+	{USB_DEVICE(0x0471, 0x0327), BS(SN9C105, MI0360)},
+	{USB_DEVICE(0x0471, 0x0328), BS(SN9C105, MI0360)},
+	{USB_DEVICE(0x0471, 0x0330), BS(SN9C105, MI0360)},
+	{USB_DEVICE(0x06f8, 0x3004), BS(SN9C105, OV7660)},
+	{USB_DEVICE(0x06f8, 0x3008), BS(SN9C105, OV7660)},
+/*	{USB_DEVICE(0x0c45, 0x603a), BS(SN9C102P, OV7648)}, */
+	{USB_DEVICE(0x0c45, 0x6040), BS(SN9C102P, HV7131R)},
+/*	{USB_DEVICE(0x0c45, 0x607a), BS(SN9C102P, OV7648)}, */
+/*	{USB_DEVICE(0x0c45, 0x607b), BS(SN9C102P, OV7660)}, */
+	{USB_DEVICE(0x0c45, 0x607c), BS(SN9C102P, HV7131R)},
+/*	{USB_DEVICE(0x0c45, 0x607e), BS(SN9C102P, OV7630)}, */
+	{USB_DEVICE(0x0c45, 0x60c0), BSF(SN9C105, MI0360, F_ILLUM)},
+						/* or MT9V111 */
+/*	{USB_DEVICE(0x0c45, 0x60c2), BS(SN9C105, P1030xC)}, */
+/*	{USB_DEVICE(0x0c45, 0x60c8), BS(SN9C105, OM6802)}, */
+/*	{USB_DEVICE(0x0c45, 0x60cc), BS(SN9C105, HV7131GP)}, */
+	{USB_DEVICE(0x0c45, 0x60ce), BS(SN9C105, SP80708)},
+	{USB_DEVICE(0x0c45, 0x60ec), BS(SN9C105, MO4000)},
+/*	{USB_DEVICE(0x0c45, 0x60ef), BS(SN9C105, ICM105C)}, */
+/*	{USB_DEVICE(0x0c45, 0x60fa), BS(SN9C105, OV7648)}, */
+/*	{USB_DEVICE(0x0c45, 0x60f2), BS(SN9C105, OV7660)}, */
+	{USB_DEVICE(0x0c45, 0x60fb), BS(SN9C105, OV7660)},
+	{USB_DEVICE(0x0c45, 0x60fc), BS(SN9C105, HV7131R)},
+	{USB_DEVICE(0x0c45, 0x60fe), BS(SN9C105, OV7630)},
+	{USB_DEVICE(0x0c45, 0x6100), BS(SN9C120, MI0360)},	/*sn9c128*/
+	{USB_DEVICE(0x0c45, 0x6102), BS(SN9C120, PO2030N)},	/* /GC0305*/
+/*	{USB_DEVICE(0x0c45, 0x6108), BS(SN9C120, OM6802)}, */
+	{USB_DEVICE(0x0c45, 0x610a), BS(SN9C120, OV7648)},	/*sn9c128*/
+	{USB_DEVICE(0x0c45, 0x610b), BS(SN9C120, OV7660)},	/*sn9c128*/
+	{USB_DEVICE(0x0c45, 0x610c), BS(SN9C120, HV7131R)},	/*sn9c128*/
+	{USB_DEVICE(0x0c45, 0x610e), BS(SN9C120, OV7630)},	/*sn9c128*/
+/*	{USB_DEVICE(0x0c45, 0x610f), BS(SN9C120, S5K53BEB)}, */
+/*	{USB_DEVICE(0x0c45, 0x6122), BS(SN9C110, ICM105C)}, */
+/*	{USB_DEVICE(0x0c45, 0x6123), BS(SN9C110, SanyoCCD)}, */
+	{USB_DEVICE(0x0c45, 0x6128), BS(SN9C120, OM6802)},	/*sn9c325?*/
+/*bw600.inf:*/
+	{USB_DEVICE(0x0c45, 0x612a), BS(SN9C120, OV7648)},	/*sn9c325?*/
+	{USB_DEVICE(0x0c45, 0x612b), BS(SN9C110, ADCM1700)},
+	{USB_DEVICE(0x0c45, 0x612c), BS(SN9C110, MO4000)},
+	{USB_DEVICE(0x0c45, 0x612e), BS(SN9C110, OV7630)},
+/*	{USB_DEVICE(0x0c45, 0x612f), BS(SN9C110, ICM105C)}, */
+	{USB_DEVICE(0x0c45, 0x6130), BS(SN9C120, MI0360)},
+						/* or MT9V111 / MI0360B */
+/*	{USB_DEVICE(0x0c45, 0x6132), BS(SN9C120, OV7670)}, */
+	{USB_DEVICE(0x0c45, 0x6138), BS(SN9C120, MO4000)},
+	{USB_DEVICE(0x0c45, 0x613a), BS(SN9C120, OV7648)},
+	{USB_DEVICE(0x0c45, 0x613b), BS(SN9C120, OV7660)},
+	{USB_DEVICE(0x0c45, 0x613c), BS(SN9C120, HV7131R)},
+	{USB_DEVICE(0x0c45, 0x613e), BS(SN9C120, OV7630)},
+	{USB_DEVICE(0x0c45, 0x6142), BS(SN9C120, PO2030N)},	/*sn9c120b*/
+						/* or GC0305 / GC0307 */
+	{USB_DEVICE(0x0c45, 0x6143), BS(SN9C120, SP80708)},	/*sn9c120b*/
+	{USB_DEVICE(0x0c45, 0x6148), BS(SN9C120, OM6802)},	/*sn9c120b*/
+	{USB_DEVICE(0x0c45, 0x614a), BSF(SN9C120, ADCM1700, F_ILLUM)},
+/*	{USB_DEVICE(0x0c45, 0x614c), BS(SN9C120, GC0306)}, */	/*sn9c120b*/
+	{}
+};
+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,
+#ifdef CONFIG_PM
+	.suspend = gspca_suspend,
+	.resume = gspca_resume,
+	.reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/usb/gspca/spca1528.c b/drivers/media/usb/gspca/spca1528.c
new file mode 100644
index 0000000..f38fd89
--- /dev/null
+++ b/drivers/media/usb/gspca/spca1528.c
@@ -0,0 +1,445 @@
+/*
+ * spca1528 subdriver
+ *
+ * Copyright (C) 2010-2011 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "spca1528"
+
+#include "gspca.h"
+#include "jpeg.h"
+
+MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>");
+MODULE_DESCRIPTION("SPCA1528 USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* specific webcam descriptor */
+struct sd {
+	struct gspca_dev gspca_dev;	/* !! must be the first item */
+
+	u8 pkt_seq;
+
+	u8 jpeg_hdr[JPEG_HDR_SZ];
+};
+
+static const struct v4l2_pix_format vga_mode[] = {
+/*		(does not work correctly)
+	{176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+		.bytesperline = 176,
+		.sizeimage = 176 * 144 * 5 / 8 + 590,
+		.colorspace = V4L2_COLORSPACE_JPEG,
+		.priv = 3},
+*/
+	{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+		.bytesperline = 320,
+		.sizeimage = 320 * 240 * 4 / 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},
+};
+
+/* read <len> bytes to gspca usb_buf */
+static void reg_r(struct gspca_dev *gspca_dev,
+			u8 req,
+			u16 index,
+			int len)
+{
+#if USB_BUF_SZ < 64
+#error "USB buffer too small"
+#endif
+	struct usb_device *dev = gspca_dev->dev;
+	int ret;
+
+	if (gspca_dev->usb_err < 0)
+		return;
+	ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+			req,
+			USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			0x0000,			/* value */
+			index,
+			gspca_dev->usb_buf, len,
+			500);
+	PDEBUG(D_USBI, "GET %02x 0000 %04x %02x", req, index,
+			 gspca_dev->usb_buf[0]);
+	if (ret < 0) {
+		pr_err("reg_r err %d\n", ret);
+		gspca_dev->usb_err = ret;
+	}
+}
+
+static void reg_w(struct gspca_dev *gspca_dev,
+			u8 req,
+			u16 value,
+			u16 index)
+{
+	struct usb_device *dev = gspca_dev->dev;
+	int ret;
+
+	if (gspca_dev->usb_err < 0)
+		return;
+	PDEBUG(D_USBO, "SET %02x %04x %04x", req, value, index);
+	ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+			req,
+			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			value, index,
+			NULL, 0, 500);
+	if (ret < 0) {
+		pr_err("reg_w err %d\n", ret);
+		gspca_dev->usb_err = ret;
+	}
+}
+
+static void reg_wb(struct gspca_dev *gspca_dev,
+			u8 req,
+			u16 value,
+			u16 index,
+			u8 byte)
+{
+	struct usb_device *dev = gspca_dev->dev;
+	int ret;
+
+	if (gspca_dev->usb_err < 0)
+		return;
+	PDEBUG(D_USBO, "SET %02x %04x %04x %02x", req, value, index, byte);
+	gspca_dev->usb_buf[0] = byte;
+	ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+			req,
+			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			value, index,
+			gspca_dev->usb_buf, 1, 500);
+	if (ret < 0) {
+		pr_err("reg_w err %d\n", ret);
+		gspca_dev->usb_err = ret;
+	}
+}
+
+static void wait_status_0(struct gspca_dev *gspca_dev)
+{
+	int i, w;
+
+	i = 16;
+	w = 0;
+	do {
+		reg_r(gspca_dev, 0x21, 0x0000, 1);
+		if (gspca_dev->usb_buf[0] == 0)
+			return;
+		w += 15;
+		msleep(w);
+	} while (--i > 0);
+	PERR("wait_status_0 timeout");
+	gspca_dev->usb_err = -ETIME;
+}
+
+static void wait_status_1(struct gspca_dev *gspca_dev)
+{
+	int i;
+
+	i = 10;
+	do {
+		reg_r(gspca_dev, 0x21, 0x0001, 1);
+		msleep(10);
+		if (gspca_dev->usb_buf[0] == 1) {
+			reg_wb(gspca_dev, 0x21, 0x0000, 0x0001, 0x00);
+			reg_r(gspca_dev, 0x21, 0x0001, 1);
+			return;
+		}
+	} while (--i > 0);
+	PERR("wait_status_1 timeout");
+	gspca_dev->usb_err = -ETIME;
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
+{
+	reg_wb(gspca_dev, 0xc0, 0x0000, 0x00c0, val);
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
+{
+	reg_wb(gspca_dev, 0xc1, 0x0000, 0x00c1, val);
+}
+
+static void sethue(struct gspca_dev *gspca_dev, s32 val)
+{
+	reg_wb(gspca_dev, 0xc2, 0x0000, 0x0000, val);
+}
+
+static void setcolor(struct gspca_dev *gspca_dev, s32 val)
+{
+	reg_wb(gspca_dev, 0xc3, 0x0000, 0x00c3, val);
+}
+
+static void setsharpness(struct gspca_dev *gspca_dev, s32 val)
+{
+	reg_wb(gspca_dev, 0xc4, 0x0000, 0x00c4, val);
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+			const struct usb_device_id *id)
+{
+	gspca_dev->cam.cam_mode = vga_mode;
+	gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode);
+	gspca_dev->cam.npkt = 128; /* number of packets per ISOC message */
+			/*fixme: 256 in ms-win traces*/
+
+	return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+	reg_w(gspca_dev, 0x00, 0x0001, 0x2067);
+	reg_w(gspca_dev, 0x00, 0x00d0, 0x206b);
+	reg_w(gspca_dev, 0x00, 0x0000, 0x206c);
+	reg_w(gspca_dev, 0x00, 0x0001, 0x2069);
+	msleep(8);
+	reg_w(gspca_dev, 0x00, 0x00c0, 0x206b);
+	reg_w(gspca_dev, 0x00, 0x0000, 0x206c);
+	reg_w(gspca_dev, 0x00, 0x0001, 0x2069);
+
+	reg_r(gspca_dev, 0x20, 0x0000, 1);
+	reg_r(gspca_dev, 0x20, 0x0000, 5);
+	reg_r(gspca_dev, 0x23, 0x0000, 64);
+	PDEBUG(D_PROBE, "%s%s", &gspca_dev->usb_buf[0x1c],
+				&gspca_dev->usb_buf[0x30]);
+	reg_r(gspca_dev, 0x23, 0x0001, 64);
+	return gspca_dev->usb_err;
+}
+
+/* function called at start time before URB creation */
+static int sd_isoc_init(struct gspca_dev *gspca_dev)
+{
+	u8 mode;
+
+	reg_r(gspca_dev, 0x00, 0x2520, 1);
+	wait_status_0(gspca_dev);
+	reg_w(gspca_dev, 0xc5, 0x0003, 0x0000);
+	wait_status_1(gspca_dev);
+
+	wait_status_0(gspca_dev);
+	mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
+	reg_wb(gspca_dev, 0x25, 0x0000, 0x0004, mode);
+	reg_r(gspca_dev, 0x25, 0x0004, 1);
+	reg_wb(gspca_dev, 0x27, 0x0000, 0x0000, 0x06);	/* 420 */
+	reg_r(gspca_dev, 0x27, 0x0000, 1);
+
+/* not useful..
+	gspca_dev->alt = 4;		* use alternate setting 3 */
+
+	return gspca_dev->usb_err;
+}
+
+/* -- start the camera -- */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	/* initialize the JPEG header */
+	jpeg_define(sd->jpeg_hdr, gspca_dev->pixfmt.height,
+			gspca_dev->pixfmt.width,
+			0x22);		/* JPEG 411 */
+
+	/* the JPEG quality shall be 85% */
+	jpeg_set_qual(sd->jpeg_hdr, 85);
+
+	reg_r(gspca_dev, 0x00, 0x2520, 1);
+	msleep(8);
+
+	/* start the capture */
+	wait_status_0(gspca_dev);
+	reg_w(gspca_dev, 0x31, 0x0000, 0x0004);	/* start request */
+	wait_status_1(gspca_dev);
+	wait_status_0(gspca_dev);
+	msleep(200);
+
+	sd->pkt_seq = 0;
+	return gspca_dev->usb_err;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+	/* stop the capture */
+	wait_status_0(gspca_dev);
+	reg_w(gspca_dev, 0x31, 0x0000, 0x0000);	/* stop request */
+	wait_status_1(gspca_dev);
+	wait_status_0(gspca_dev);
+}
+
+/* move a packet adding 0x00 after 0xff */
+static void add_packet(struct gspca_dev *gspca_dev,
+			u8 *data,
+			int len)
+{
+	int i;
+
+	i = 0;
+	do {
+		if (data[i] == 0xff) {
+			gspca_frame_add(gspca_dev, INTER_PACKET,
+					data, i + 1);
+			len -= i;
+			data += i;
+			*data = 0x00;
+			i = 0;
+		}
+	} while (++i < len);
+	gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+			u8 *data,			/* isoc packet */
+			int len)			/* iso packet length */
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	static const u8 ffd9[] = {0xff, 0xd9};
+
+	/* image packets start with:
+	 *	02 8n
+	 * with <n> bit:
+	 *	0x01: even (0) / odd (1) image
+	 *	0x02: end of image when set
+	 */
+	if (len < 3)
+		return;				/* empty packet */
+	if (*data == 0x02) {
+		if (data[1] & 0x02) {
+			sd->pkt_seq = !(data[1] & 1);
+			add_packet(gspca_dev, data + 2, len - 2);
+			gspca_frame_add(gspca_dev, LAST_PACKET,
+					ffd9, 2);
+			return;
+		}
+		if ((data[1] & 1) != sd->pkt_seq)
+			goto err;
+		if (gspca_dev->last_packet_type == LAST_PACKET)
+			gspca_frame_add(gspca_dev, FIRST_PACKET,
+					sd->jpeg_hdr, JPEG_HDR_SZ);
+		add_packet(gspca_dev, data + 2, len - 2);
+		return;
+	}
+err:
+	gspca_dev->last_packet_type = DISCARD_PACKET;
+}
+
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct gspca_dev *gspca_dev =
+		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+
+	gspca_dev->usb_err = 0;
+
+	if (!gspca_dev->streaming)
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_BRIGHTNESS:
+		setbrightness(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_CONTRAST:
+		setcontrast(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_HUE:
+		sethue(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_SATURATION:
+		setcolor(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_SHARPNESS:
+		setsharpness(gspca_dev, ctrl->val);
+		break;
+	}
+	return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+	.s_ctrl = sd_s_ctrl,
+};
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+	gspca_dev->vdev.ctrl_handler = hdl;
+	v4l2_ctrl_handler_init(hdl, 5);
+	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_CONTRAST, 0, 8, 1, 1);
+	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_HUE, 0, 255, 1, 0);
+	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_SATURATION, 0, 8, 1, 1);
+	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_SHARPNESS, 0, 255, 1, 0);
+
+	if (hdl->error) {
+		pr_err("Could not initialize controls\n");
+		return hdl->error;
+	}
+	return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+	.name = MODULE_NAME,
+	.config = sd_config,
+	.init = sd_init,
+	.init_controls = sd_init_controls,
+	.isoc_init = sd_isoc_init,
+	.start = sd_start,
+	.stopN = sd_stopN,
+	.pkt_scan = sd_pkt_scan,
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+	{USB_DEVICE(0x04fc, 0x1528)},
+	{}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+			const struct usb_device_id *id)
+{
+	/* the video interface for isochronous transfer is 1 */
+	if (intf->cur_altsetting->desc.bInterfaceNumber != 1)
+		return -ENODEV;
+
+	return gspca_dev_probe2(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,
+#ifdef CONFIG_PM
+	.suspend = gspca_suspend,
+	.resume = gspca_resume,
+	.reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/usb/gspca/spca500.c b/drivers/media/usb/gspca/spca500.c
new file mode 100644
index 0000000..f011a30
--- /dev/null
+++ b/drivers/media/usb/gspca/spca500.c
@@ -0,0 +1,991 @@
+/*
+ * 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "spca500"
+
+#include "gspca.h"
+#include "jpeg.h"
+
+MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
+MODULE_DESCRIPTION("GSPCA/SPCA500 USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+#define QUALITY 85
+
+/* specific webcam descriptor */
+struct sd {
+	struct gspca_dev gspca_dev;		/* !! must be the first item */
+
+	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
+
+	u8 jpeg_hdr[JPEG_HDR_SZ];
+};
+
+static const 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 const 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)
+		pr_err("reg write: error %d\n", 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) {
+		pr_err("reg_r_12 err %d\n", ret);
+		return ret;
+	}
+	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) {
+		PERR("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) {
+		PERR("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) {
+		PERR("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 unknown 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)
+		PERR("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 between 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;
+
+	cam = &gspca_dev->cam;
+	sd->subtype = id->driver_info;
+	if (sd->subtype != LogitechClickSmart310) {
+		cam->cam_mode = vga_mode;
+		cam->nmodes = ARRAY_SIZE(vga_mode);
+	} else {
+		cam->cam_mode = sif_mode;
+		cam->nmodes = ARRAY_SIZE(sif_mode);
+	}
+	return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(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 int sd_start(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int err;
+	__u8 Data;
+	__u8 xmult, ymult;
+
+	/* create the JPEG header */
+	jpeg_define(sd->jpeg_hdr, gspca_dev->pixfmt.height,
+			gspca_dev->pixfmt.width,
+			0x22);		/* JPEG 411 */
+	jpeg_set_qual(sd->jpeg_hdr, QUALITY);
+
+	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)
+			PERR("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)
+			PERR("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 */
+		err = reg_w(gspca_dev, 0x00, 0x850a, 0x0001);
+		if (err < 0)
+			PERR("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)
+			PERR("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)
+			PERR("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)
+			PERR("spca500_full_reset failed");
+
+		/* enable drop packet */
+		err = reg_w(gspca_dev, 0x00, 0x850a, 0x0001);
+		if (err < 0)
+			PERR("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)
+			PERR("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)
+			PERR("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)
+			PERR("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)
+			PERR("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)
+			PERR("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)
+			PERR("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 between 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)
+			PERR("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;
+	}
+	return 0;
+}
+
+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_pkt_scan(struct gspca_dev *gspca_dev,
+			u8 *data,			/* isoc packet */
+			int len)			/* iso packet length */
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int i;
+	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;
+		}
+		gspca_frame_add(gspca_dev, LAST_PACKET,
+					ffd9, 2);
+
+		/* put the JPEG header in the new frame */
+		gspca_frame_add(gspca_dev, FIRST_PACKET,
+			sd->jpeg_hdr, JPEG_HDR_SZ);
+
+		data += SPCA500_OFFSET_DATA;
+		len -= SPCA500_OFFSET_DATA;
+	} else {
+		data += 1;
+		len -= 1;
+	}
+
+	/* add 0x00 after 0xff */
+	i = 0;
+	do {
+		if (data[i] == 0xff) {
+			gspca_frame_add(gspca_dev, INTER_PACKET,
+					data, i + 1);
+			len -= i;
+			data += i;
+			*data = 0x00;
+			i = 0;
+		}
+		i++;
+	} while (i < len);
+	gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
+{
+	reg_w(gspca_dev, 0x00, 0x8167,
+			(__u8) (val - 128));
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
+{
+	reg_w(gspca_dev, 0x00, 0x8168, val);
+}
+
+static void setcolors(struct gspca_dev *gspca_dev, s32 val)
+{
+	reg_w(gspca_dev, 0x00, 0x8169, val);
+}
+
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct gspca_dev *gspca_dev =
+		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+
+	gspca_dev->usb_err = 0;
+
+	if (!gspca_dev->streaming)
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_BRIGHTNESS:
+		setbrightness(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_CONTRAST:
+		setcontrast(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_SATURATION:
+		setcolors(gspca_dev, ctrl->val);
+		break;
+	}
+	return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+	.s_ctrl = sd_s_ctrl,
+};
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+	gspca_dev->vdev.ctrl_handler = hdl;
+	v4l2_ctrl_handler_init(hdl, 3);
+	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_BRIGHTNESS, 0, 255, 1, 127);
+	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_CONTRAST, 0, 63, 1, 31);
+	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_SATURATION, 0, 63, 1, 31);
+
+	if (hdl->error) {
+		pr_err("Could not initialize controls\n");
+		return hdl->error;
+	}
+	return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+	.name = MODULE_NAME,
+	.config = sd_config,
+	.init = sd_init,
+	.init_controls = sd_init_controls,
+	.start = sd_start,
+	.stopN = sd_stopN,
+	.pkt_scan = sd_pkt_scan,
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+	{USB_DEVICE(0x040a, 0x0300), .driver_info = KodakEZ200},
+	{USB_DEVICE(0x041e, 0x400a), .driver_info = CreativePCCam300},
+	{USB_DEVICE(0x046d, 0x0890), .driver_info = LogitechTraveler},
+	{USB_DEVICE(0x046d, 0x0900), .driver_info = LogitechClickSmart310},
+	{USB_DEVICE(0x046d, 0x0901), .driver_info = LogitechClickSmart510},
+	{USB_DEVICE(0x04a5, 0x300c), .driver_info = BenqDC1016},
+	{USB_DEVICE(0x04fc, 0x7333), .driver_info = PalmPixDC85},
+	{USB_DEVICE(0x055f, 0xc200), .driver_info = MustekGsmart300},
+	{USB_DEVICE(0x055f, 0xc220), .driver_info = Gsmartmini},
+	{USB_DEVICE(0x06bd, 0x0404), .driver_info = AgfaCl20},
+	{USB_DEVICE(0x06be, 0x0800), .driver_info = Optimedia},
+	{USB_DEVICE(0x084d, 0x0003), .driver_info = DLinkDSC350},
+	{USB_DEVICE(0x08ca, 0x0103), .driver_info = AiptekPocketDV},
+	{USB_DEVICE(0x2899, 0x012c), .driver_info = ToptroIndus},
+	{USB_DEVICE(0x8086, 0x0630), .driver_info = IntelPocketPCCamera},
+	{}
+};
+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,
+#ifdef CONFIG_PM
+	.suspend = gspca_suspend,
+	.resume = gspca_resume,
+	.reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/usb/gspca/spca501.c b/drivers/media/usb/gspca/spca501.c
new file mode 100644
index 0000000..d92fd17
--- /dev/null
+++ b/drivers/media/usb/gspca/spca501.c
@@ -0,0 +1,2050 @@
+/*
+ * 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "spca501"
+
+#include "gspca.h"
+
+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;
+	__u8 blue_balance;
+	__u8 red_balance;
+
+	char subtype;
+#define Arowana300KCMOSCamera 0
+#define IntelCreateAndShare 1
+#define KodakDVC325 2
+#define MystFromOriUnknownCamera 3
+#define SmileIntlCamera 4
+#define ThreeComHomeConnectLite 5
+#define ViewQuestM318B 6
+};
+
+static const 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},
+	{}
+};
+
+/* Unknown 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 gspca_dev *gspca_dev,
+					__u16 req, __u16 index, __u16 value)
+{
+	int ret;
+	struct usb_device *dev = gspca_dev->dev;
+
+	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)
+		pr_err("reg write: error %d\n", ret);
+	return ret;
+}
+
+
+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_write(gspca_dev, data[i][0], data[i][2],
+								data[i][1]);
+		if (ret < 0) {
+			PERR("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, s32 val)
+{
+	reg_write(gspca_dev, SPCA501_REG_CCDSP, 0x12, val);
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
+{
+	reg_write(gspca_dev, 0x00, 0x00, (val >> 8) & 0xff);
+	reg_write(gspca_dev, 0x00, 0x01, val & 0xff);
+}
+
+static void setcolors(struct gspca_dev *gspca_dev, s32 val)
+{
+	reg_write(gspca_dev, SPCA501_REG_CCDSP, 0x0c, val);
+}
+
+static void setblue_balance(struct gspca_dev *gspca_dev, s32 val)
+{
+	reg_write(gspca_dev, SPCA501_REG_CCDSP, 0x11, val);
+}
+
+static void setred_balance(struct gspca_dev *gspca_dev, s32 val)
+{
+	reg_write(gspca_dev, SPCA501_REG_CCDSP, 0x13, val);
+}
+
+/* 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->cam_mode = vga_mode;
+	cam->nmodes = ARRAY_SIZE(vga_mode);
+	sd->subtype = id->driver_info;
+
+	return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	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:
+		/* Unknown 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;
+	}
+	PDEBUG(D_STREAM, "Initializing SPCA501 finished");
+	return 0;
+error:
+	return -EINVAL;
+}
+
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int mode;
+
+	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:
+		/* Unknown CMOS Camera data */
+		write_vector(gspca_dev, spca501c_mysterious_init_data);
+		break;
+	default:
+		/* Generic 501 open data */
+		write_vector(gspca_dev, spca501_open_data);
+	}
+
+	/* 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(gspca_dev, SPCA50X_REG_USB, 0x6, 0x94);
+	switch (mode) {
+	case 0: /* 640x480 */
+		reg_write(gspca_dev, SPCA50X_REG_USB, 0x07, 0x004a);
+		break;
+	case 1: /* 320x240 */
+		reg_write(gspca_dev, SPCA50X_REG_USB, 0x07, 0x104a);
+		break;
+	default:
+/*	case 2:  * 160x120 */
+		reg_write(gspca_dev, SPCA50X_REG_USB, 0x07, 0x204a);
+		break;
+	}
+	reg_write(gspca_dev, SPCA501_REG_CTLRL, 0x01, 0x02);
+
+	return 0;
+}
+
+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, SPCA501_REG_CTLRL, 0x01, 0x00);
+}
+
+/* called on streamoff with alt 0 and on disconnect */
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+	if (!gspca_dev->present)
+		return;
+	reg_write(gspca_dev, SPCA501_REG_CTLRL, 0x05, 0x00);
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+			u8 *data,			/* isoc packet */
+			int len)			/* iso packet length */
+{
+	switch (data[0]) {
+	case 0:				/* start of frame */
+		gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+		data += SPCA501_OFFSET_DATA;
+		len -= SPCA501_OFFSET_DATA;
+		gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
+		return;
+	case 0xff:			/* drop */
+/*		gspca_dev->last_packet_type = DISCARD_PACKET; */
+		return;
+	}
+	data++;
+	len--;
+	gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+}
+
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct gspca_dev *gspca_dev =
+		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+
+	gspca_dev->usb_err = 0;
+
+	if (!gspca_dev->streaming)
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_BRIGHTNESS:
+		setbrightness(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_CONTRAST:
+		setcontrast(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_SATURATION:
+		setcolors(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_BLUE_BALANCE:
+		setblue_balance(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_RED_BALANCE:
+		setred_balance(gspca_dev, ctrl->val);
+		break;
+	}
+	return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+	.s_ctrl = sd_s_ctrl,
+};
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+	gspca_dev->vdev.ctrl_handler = hdl;
+	v4l2_ctrl_handler_init(hdl, 5);
+	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_BRIGHTNESS, 0, 127, 1, 0);
+	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_CONTRAST, 0, 64725, 1, 64725);
+	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_SATURATION, 0, 63, 1, 20);
+	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_BLUE_BALANCE, 0, 127, 1, 0);
+	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_RED_BALANCE, 0, 127, 1, 0);
+
+	if (hdl->error) {
+		pr_err("Could not initialize controls\n");
+		return hdl->error;
+	}
+	return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+	.name = MODULE_NAME,
+	.config = sd_config,
+	.init = sd_init,
+	.init_controls = sd_init_controls,
+	.start = sd_start,
+	.stopN = sd_stopN,
+	.stop0 = sd_stop0,
+	.pkt_scan = sd_pkt_scan,
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+	{USB_DEVICE(0x040a, 0x0002), .driver_info = KodakDVC325},
+	{USB_DEVICE(0x0497, 0xc001), .driver_info = SmileIntlCamera},
+	{USB_DEVICE(0x0506, 0x00df), .driver_info = ThreeComHomeConnectLite},
+	{USB_DEVICE(0x0733, 0x0401), .driver_info = IntelCreateAndShare},
+	{USB_DEVICE(0x0733, 0x0402), .driver_info = ViewQuestM318B},
+	{USB_DEVICE(0x1776, 0x501c), .driver_info = Arowana300KCMOSCamera},
+	{USB_DEVICE(0x0000, 0x0000), .driver_info = MystFromOriUnknownCamera},
+	{}
+};
+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,
+#ifdef CONFIG_PM
+	.suspend = gspca_suspend,
+	.resume = gspca_resume,
+	.reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/usb/gspca/spca505.c b/drivers/media/usb/gspca/spca505.c
new file mode 100644
index 0000000..232b330
--- /dev/null
+++ b/drivers/media/usb/gspca/spca505.c
@@ -0,0 +1,805 @@
+/*
+ * 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "spca505"
+
+#include "gspca.h"
+
+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 */
+
+	u8 subtype;
+#define IntelPCCameraPro 0
+#define Nxultra 1
+};
+
+static const struct v4l2_pix_format vga_mode[] = {
+	{160, 120, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE,
+		.bytesperline = 160,
+		.sizeimage = 160 * 120 * 3 / 2,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 4},
+	{176, 144, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE,
+		.bytesperline = 176,
+		.sizeimage = 176 * 144 * 3 / 2,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 3},
+	{320, 240, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE,
+		.bytesperline = 320,
+		.sizeimage = 320 * 240 * 3 / 2,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 2},
+	{352, 288, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE,
+		.bytesperline = 352,
+		.sizeimage = 352 * 288 * 3 / 2,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 1},
+	{640, 480, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE,
+		.bytesperline = 640,
+		.sizeimage = 640 * 480 * 3 / 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 */
+
+/* Image format and compression control */
+#define SPCA50X_REG_COMPRESS 0x04
+
+/*
+ * Data to initialize a SPCA505. Common to the CCD and external modes
+ */
+static const u8 spca505_init_data[][3] = {
+	/* bmRequest,value,index */
+	{SPCA50X_REG_GLOBAL, SPCA50X_GMISC3_SAA7113RST, SPCA50X_GLOBAL_MISC3},
+	/* Sensor reset */
+	{SPCA50X_REG_GLOBAL, 0x00, SPCA50X_GLOBAL_MISC3},
+	{SPCA50X_REG_GLOBAL, 0x00, SPCA50X_GLOBAL_MISC1},
+	/* Block USB reset */
+	{SPCA50X_REG_GLOBAL, SPCA50X_GMISC0_IDSEL, SPCA50X_GLOBAL_MISC0},
+
+	{0x05, 0x01, 0x10},
+					/* Maybe power down some stuff */
+	{0x05, 0x0f, 0x11},
+
+	/* Setup internal CCD  ? */
+	{0x06, 0x10, 0x08},
+	{0x06, 0x00, 0x09},
+	{0x06, 0x00, 0x0a},
+	{0x06, 0x00, 0x0b},
+	{0x06, 0x10, 0x0c},
+	{0x06, 0x00, 0x0d},
+	{0x06, 0x00, 0x0e},
+	{0x06, 0x00, 0x0f},
+	{0x06, 0x10, 0x10},
+	{0x06, 0x02, 0x11},
+	{0x06, 0x00, 0x12},
+	{0x06, 0x04, 0x13},
+	{0x06, 0x02, 0x14},
+	{0x06, 0x8a, 0x51},
+	{0x06, 0x40, 0x52},
+	{0x06, 0xb6, 0x53},
+	{0x06, 0x3d, 0x54},
+	{}
+};
+
+/*
+ * Data to initialize the camera using the internal CCD
+ */
+static const u8 spca505_open_data_ccd[][3] = {
+	/* bmRequest,value,index */
+	/* Internal CCD data set */
+	{0x03, 0x04, 0x01},
+	/* This could be a reset */
+	{0x03, 0x00, 0x01},
+
+	/* Setup compression and image registers. 0x6 and 0x7 seem to be
+	   related to H&V hold, and are resolution mode specific */
+		{0x04, 0x10, 0x01},
+		/* DIFF(0x50), was (0x10) */
+	{0x04, 0x00, 0x04},
+	{0x04, 0x00, 0x05},
+	{0x04, 0x20, 0x06},
+	{0x04, 0x20, 0x07},
+
+	{0x08, 0x0a, 0x00},
+	/* DIFF (0x4a), was (0xa) */
+
+	{0x05, 0x00, 0x10},
+	{0x05, 0x00, 0x11},
+	{0x05, 0x00, 0x00},
+	/* DIFF not written */
+	{0x05, 0x00, 0x01},
+	/* DIFF not written */
+	{0x05, 0x00, 0x02},
+	/* DIFF not written */
+	{0x05, 0x00, 0x03},
+	/* DIFF not written */
+	{0x05, 0x00, 0x04},
+	/* DIFF not written */
+		{0x05, 0x80, 0x05},
+		/* DIFF not written */
+		{0x05, 0xe0, 0x06},
+		/* DIFF not written */
+		{0x05, 0x20, 0x07},
+		/* DIFF not written */
+		{0x05, 0xa0, 0x08},
+		/* DIFF not written */
+		{0x05, 0x0, 0x12},
+		/* DIFF not written */
+	{0x05, 0x02, 0x0f},
+	/* DIFF not written */
+		{0x05, 0x10, 0x46},
+		/* DIFF not written */
+		{0x05, 0x8, 0x4a},
+		/* DIFF not written */
+
+	{0x03, 0x08, 0x03},
+	/* DIFF (0x3,0x28,0x3) */
+	{0x03, 0x08, 0x01},
+	{0x03, 0x0c, 0x03},
+	/* DIFF not written */
+		{0x03, 0x21, 0x00},
+		/* DIFF (0x39) */
+
+/* Extra block copied from init to hopefully ensure CCD is in a sane state */
+	{0x06, 0x10, 0x08},
+	{0x06, 0x00, 0x09},
+	{0x06, 0x00, 0x0a},
+	{0x06, 0x00, 0x0b},
+	{0x06, 0x10, 0x0c},
+	{0x06, 0x00, 0x0d},
+	{0x06, 0x00, 0x0e},
+	{0x06, 0x00, 0x0f},
+	{0x06, 0x10, 0x10},
+	{0x06, 0x02, 0x11},
+	{0x06, 0x00, 0x12},
+	{0x06, 0x04, 0x13},
+	{0x06, 0x02, 0x14},
+	{0x06, 0x8a, 0x51},
+	{0x06, 0x40, 0x52},
+	{0x06, 0xb6, 0x53},
+	{0x06, 0x3d, 0x54},
+	/* End of extra block */
+
+		{0x06, 0x3f, 0x1},
+		/* Block skipped */
+	{0x06, 0x10, 0x02},
+	{0x06, 0x64, 0x07},
+	{0x06, 0x10, 0x08},
+	{0x06, 0x00, 0x09},
+	{0x06, 0x00, 0x0a},
+	{0x06, 0x00, 0x0b},
+	{0x06, 0x10, 0x0c},
+	{0x06, 0x00, 0x0d},
+	{0x06, 0x00, 0x0e},
+	{0x06, 0x00, 0x0f},
+	{0x06, 0x10, 0x10},
+	{0x06, 0x02, 0x11},
+	{0x06, 0x00, 0x12},
+	{0x06, 0x04, 0x13},
+	{0x06, 0x02, 0x14},
+	{0x06, 0x8a, 0x51},
+	{0x06, 0x40, 0x52},
+	{0x06, 0xb6, 0x53},
+	{0x06, 0x3d, 0x54},
+	{0x06, 0x60, 0x57},
+	{0x06, 0x20, 0x58},
+	{0x06, 0x15, 0x59},
+	{0x06, 0x05, 0x5a},
+
+	{0x05, 0x01, 0xc0},
+	{0x05, 0x10, 0xcb},
+		{0x05, 0x80, 0xc1},
+		/* */
+		{0x05, 0x0, 0xc2},
+		/* 4 was 0 */
+	{0x05, 0x00, 0xca},
+		{0x05, 0x80, 0xc1},
+		/*  */
+	{0x05, 0x04, 0xc2},
+	{0x05, 0x00, 0xca},
+		{0x05, 0x0, 0xc1},
+		/*  */
+	{0x05, 0x00, 0xc2},
+	{0x05, 0x00, 0xca},
+		{0x05, 0x40, 0xc1},
+		/* */
+	{0x05, 0x17, 0xc2},
+	{0x05, 0x00, 0xca},
+		{0x05, 0x80, 0xc1},
+		/* */
+	{0x05, 0x06, 0xc2},
+	{0x05, 0x00, 0xca},
+		{0x05, 0x80, 0xc1},
+		/* */
+	{0x05, 0x04, 0xc2},
+	{0x05, 0x00, 0xca},
+
+	{0x03, 0x4c, 0x3},
+	{0x03, 0x18, 0x1},
+
+	{0x06, 0x70, 0x51},
+	{0x06, 0xbe, 0x53},
+	{0x06, 0x71, 0x57},
+	{0x06, 0x20, 0x58},
+	{0x06, 0x05, 0x59},
+	{0x06, 0x15, 0x5a},
+
+	{0x04, 0x00, 0x08},
+	/* Compress = OFF (0x1 to turn on) */
+	{0x04, 0x12, 0x09},
+	{0x04, 0x21, 0x0a},
+	{0x04, 0x10, 0x0b},
+	{0x04, 0x21, 0x0c},
+	{0x04, 0x05, 0x00},
+	/* was 5 (Image Type ? ) */
+	{0x04, 0x00, 0x01},
+
+	{0x06, 0x3f, 0x01},
+
+	{0x04, 0x00, 0x04},
+	{0x04, 0x00, 0x05},
+	{0x04, 0x40, 0x06},
+	{0x04, 0x40, 0x07},
+
+	{0x06, 0x1c, 0x17},
+	{0x06, 0xe2, 0x19},
+	{0x06, 0x1c, 0x1b},
+	{0x06, 0xe2, 0x1d},
+	{0x06, 0xaa, 0x1f},
+	{0x06, 0x70, 0x20},
+
+	{0x05, 0x01, 0x10},
+	{0x05, 0x00, 0x11},
+	{0x05, 0x01, 0x00},
+	{0x05, 0x05, 0x01},
+		{0x05, 0x00, 0xc1},
+		/* */
+	{0x05, 0x00, 0xc2},
+	{0x05, 0x00, 0xca},
+
+	{0x06, 0x70, 0x51},
+	{0x06, 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 u8 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},
+	{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 u8 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) & 0xff, 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, 0x80, 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) & 0xff, 0x01},
+	{0x05, 0x06, 0xc1},
+	{0x05, 0x58, 0xc2},
+	{0x05, 0x00, 0xca},
+	{0x05, 0x00, 0x11},
+	{}
+};
+
+static int reg_write(struct gspca_dev *gspca_dev,
+		     u16 req, u16 index, u16 value)
+{
+	int ret;
+	struct usb_device *dev = gspca_dev->dev;
+
+	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, %d",
+		req, index, value, ret);
+	if (ret < 0)
+		pr_err("reg write: error %d\n", 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 */
+{
+	int ret;
+
+	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, 2,
+			500);			/* timeout */
+	if (ret < 0)
+		return ret;
+	return (gspca_dev->usb_buf[1] << 8) + gspca_dev->usb_buf[0];
+}
+
+static int write_vector(struct gspca_dev *gspca_dev,
+			const u8 data[][3])
+{
+	int ret, i = 0;
+
+	while (data[i][0] != 0) {
+		ret = reg_write(gspca_dev, data[i][0], data[i][2],
+								data[i][1]);
+		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;
+
+	cam = &gspca_dev->cam;
+	cam->cam_mode = vga_mode;
+	sd->subtype = id->driver_info;
+	if (sd->subtype != IntelPCCameraPro)
+		cam->nmodes = ARRAY_SIZE(vga_mode);
+	else			/* no 640x480 for IntelPCCameraPro */
+		cam->nmodes = ARRAY_SIZE(vga_mode) - 1;
+
+	return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (write_vector(gspca_dev,
+			 sd->subtype == Nxultra
+				? spca505b_init_data
+				: spca505_init_data))
+		return -EIO;
+	return 0;
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev, s32 brightness)
+{
+	reg_write(gspca_dev, 0x05, 0x00, (255 - brightness) >> 6);
+	reg_write(gspca_dev, 0x05, 0x01, (255 - brightness) << 2);
+}
+
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int ret, mode;
+	static u8 mode_tb[][3] = {
+	/*	  r00   r06   r07	*/
+		{0x00, 0x10, 0x10},	/* 640x480 */
+		{0x01, 0x1a, 0x1a},	/* 352x288 */
+		{0x02, 0x1c, 0x1d},	/* 320x240 */
+		{0x04, 0x34, 0x34},	/* 176x144 */
+		{0x05, 0x40, 0x40}	/* 160x120 */
+	};
+
+	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, 0x06, 0x16);
+
+	if (ret < 0) {
+		PERR("register read failed err: %d", ret);
+		return ret;
+	}
+	if (ret != 0x0101) {
+		pr_err("After vector read returns 0x%04x should be 0x0101\n",
+		       ret);
+	}
+
+	ret = reg_write(gspca_dev, 0x06, 0x16, 0x0a);
+	if (ret < 0)
+		return ret;
+	reg_write(gspca_dev, 0x05, 0xc2, 0x12);
+
+	/* necessary because without it we can see stream
+	 * only once after loading module */
+	/* stopping usb registers Tomasz change */
+	reg_write(gspca_dev, 0x02, 0x00, 0x00);
+
+	mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;
+	reg_write(gspca_dev, SPCA50X_REG_COMPRESS, 0x00, mode_tb[mode][0]);
+	reg_write(gspca_dev, SPCA50X_REG_COMPRESS, 0x06, mode_tb[mode][1]);
+	reg_write(gspca_dev, SPCA50X_REG_COMPRESS, 0x07, mode_tb[mode][2]);
+
+	return reg_write(gspca_dev, SPCA50X_REG_USB,
+			 SPCA50X_USB_CTRL,
+			 SPCA50X_CUSB_ENABLE);
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+	/* Disable ISO packet machine */
+	reg_write(gspca_dev, 0x02, 0x00, 0x00);
+}
+
+/* called on streamoff with alt 0 and on disconnect */
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+	if (!gspca_dev->present)
+		return;
+
+	/* This maybe reset or power control */
+	reg_write(gspca_dev, 0x03, 0x03, 0x20);
+	reg_write(gspca_dev, 0x03, 0x01, 0x00);
+	reg_write(gspca_dev, 0x03, 0x00, 0x01);
+	reg_write(gspca_dev, 0x05, 0x10, 0x01);
+	reg_write(gspca_dev, 0x05, 0x11, 0x0f);
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+			u8 *data,			/* isoc packet */
+			int len)			/* iso packet length */
+{
+	switch (data[0]) {
+	case 0:				/* start of frame */
+		gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+		data += SPCA50X_OFFSET_DATA;
+		len -= SPCA50X_OFFSET_DATA;
+		gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
+		break;
+	case 0xff:			/* drop */
+		break;
+	default:
+		data += 1;
+		len -= 1;
+		gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+		break;
+	}
+}
+
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct gspca_dev *gspca_dev =
+		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+
+	gspca_dev->usb_err = 0;
+
+	if (!gspca_dev->streaming)
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_BRIGHTNESS:
+		setbrightness(gspca_dev, ctrl->val);
+		break;
+	}
+	return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+	.s_ctrl = sd_s_ctrl,
+};
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+	gspca_dev->vdev.ctrl_handler = hdl;
+	v4l2_ctrl_handler_init(hdl, 5);
+	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_BRIGHTNESS, 0, 255, 1, 127);
+
+	if (hdl->error) {
+		pr_err("Could not initialize controls\n");
+		return hdl->error;
+	}
+	return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+	.name = MODULE_NAME,
+	.config = sd_config,
+	.init_controls = sd_init_controls,
+	.init = sd_init,
+	.start = sd_start,
+	.stopN = sd_stopN,
+	.stop0 = sd_stop0,
+	.pkt_scan = sd_pkt_scan,
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+	{USB_DEVICE(0x041e, 0x401d), .driver_info = Nxultra},
+	{USB_DEVICE(0x0733, 0x0430), .driver_info = IntelPCCameraPro},
+/*fixme: may be UsbGrabberPV321 BRIDGE_SPCA506 SENSOR_SAA7113 */
+	{}
+};
+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,
+#ifdef CONFIG_PM
+	.suspend = gspca_suspend,
+	.resume = gspca_resume,
+	.reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/usb/gspca/spca506.c b/drivers/media/usb/gspca/spca506.c
new file mode 100644
index 0000000..bcd2c04
--- /dev/null
+++ b/drivers/media/usb/gspca/spca506.c
@@ -0,0 +1,611 @@
+/*
+ * 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"
+
+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 */
+
+	char norme;
+	char channel;
+};
+
+static const struct v4l2_pix_format vga_mode[] = {
+	{160, 120, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE,
+		.bytesperline = 160,
+		.sizeimage = 160 * 120 * 3 / 2,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 5},
+	{176, 144, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE,
+		.bytesperline = 176,
+		.sizeimage = 176 * 144 * 3 / 2,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 4},
+	{320, 240, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE,
+		.bytesperline = 320,
+		.sizeimage = 320 * 240 * 3 / 2,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 2},
+	{352, 288, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE,
+		.bytesperline = 352,
+		.sizeimage = 352 * 288 * 3 / 2,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 1},
+	{640, 480, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE,
+		.bytesperline = 640,
+		.sizeimage = 640 * 480 * 3 / 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 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 cam *cam;
+
+	cam = &gspca_dev->cam;
+	cam->cam_mode = vga_mode;
+	cam->nmodes = ARRAY_SIZE(vga_mode);
+	return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(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 int 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);
+	return 0;
+}
+
+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_pkt_scan(struct gspca_dev *gspca_dev,
+			u8 *data,			/* isoc packet */
+			int len)			/* iso packet length */
+{
+	switch (data[0]) {
+	case 0:				/* start of frame */
+		gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+		data += SPCA50X_OFFSET_DATA;
+		len -= SPCA50X_OFFSET_DATA;
+		gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
+		break;
+	case 0xff:			/* drop */
+/*		gspca_dev->last_packet_type = DISCARD_PACKET; */
+		break;
+	default:
+		data += 1;
+		len -= 1;
+		gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+		break;
+	}
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
+{
+	spca506_Initi2c(gspca_dev);
+	spca506_WriteI2c(gspca_dev, val, SAA7113_bright);
+	spca506_WriteI2c(gspca_dev, 0x01, 0x09);
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
+{
+	spca506_Initi2c(gspca_dev);
+	spca506_WriteI2c(gspca_dev, val, SAA7113_contrast);
+	spca506_WriteI2c(gspca_dev, 0x01, 0x09);
+}
+
+static void setcolors(struct gspca_dev *gspca_dev, s32 val)
+{
+	spca506_Initi2c(gspca_dev);
+	spca506_WriteI2c(gspca_dev, val, SAA7113_saturation);
+	spca506_WriteI2c(gspca_dev, 0x01, 0x09);
+}
+
+static void sethue(struct gspca_dev *gspca_dev, s32 val)
+{
+	spca506_Initi2c(gspca_dev);
+	spca506_WriteI2c(gspca_dev, val, SAA7113_hue);
+	spca506_WriteI2c(gspca_dev, 0x01, 0x09);
+}
+
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct gspca_dev *gspca_dev =
+		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+
+	gspca_dev->usb_err = 0;
+
+	if (!gspca_dev->streaming)
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_BRIGHTNESS:
+		setbrightness(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_CONTRAST:
+		setcontrast(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_SATURATION:
+		setcolors(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_HUE:
+		sethue(gspca_dev, ctrl->val);
+		break;
+	}
+	return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+	.s_ctrl = sd_s_ctrl,
+};
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+	gspca_dev->vdev.ctrl_handler = hdl;
+	v4l2_ctrl_handler_init(hdl, 4);
+	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_CONTRAST, 0, 255, 1, 0x47);
+	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_SATURATION, 0, 255, 1, 0x40);
+	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_HUE, 0, 255, 1, 0);
+
+	if (hdl->error) {
+		pr_err("Could not initialize controls\n");
+		return hdl->error;
+	}
+	return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+	.name = MODULE_NAME,
+	.config = sd_config,
+	.init = sd_init,
+	.init_controls = sd_init_controls,
+	.start = sd_start,
+	.stopN = sd_stopN,
+	.pkt_scan = sd_pkt_scan,
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+	{USB_DEVICE(0x06e1, 0xa190)},
+/*fixme: may be IntelPCCameraPro BRIDGE_SPCA505
+	{USB_DEVICE(0x0733, 0x0430)}, */
+	{USB_DEVICE(0x0734, 0x043b)},
+	{USB_DEVICE(0x99fa, 0x8988)},
+	{}
+};
+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,
+#ifdef CONFIG_PM
+	.suspend = gspca_suspend,
+	.resume = gspca_resume,
+	.reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/usb/gspca/spca508.c b/drivers/media/usb/gspca/spca508.c
new file mode 100644
index 0000000..75f2beb
--- /dev/null
+++ b/drivers/media/usb/gspca/spca508.c
@@ -0,0 +1,1535 @@
+/*
+ * SPCA508 chip based cameras subdriver
+ *
+ * Copyright (C) 2009 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "spca508"
+
+#include "gspca.h"
+
+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 */
+
+	u8 subtype;
+#define CreativeVista 0
+#define HamaUSBSightcam 1
+#define HamaUSBSightcam2 2
+#define IntelEasyPCCamera 3
+#define MicroInnovationIC200 4
+#define ViewQuestVQ110 5
+};
+
+static const struct v4l2_pix_format sif_mode[] = {
+	{160, 120, V4L2_PIX_FMT_SPCA508, V4L2_FIELD_NONE,
+		.bytesperline = 160,
+		.sizeimage = 160 * 120 * 3 / 2,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 3},
+	{176, 144, V4L2_PIX_FMT_SPCA508, V4L2_FIELD_NONE,
+		.bytesperline = 176,
+		.sizeimage = 176 * 144 * 3 / 2,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 2},
+	{320, 240, V4L2_PIX_FMT_SPCA508, V4L2_FIELD_NONE,
+		.bytesperline = 320,
+		.sizeimage = 320 * 240 * 3 / 2,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 1},
+	{352, 288, V4L2_PIX_FMT_SPCA508, V4L2_FIELD_NONE,
+		.bytesperline = 352,
+		.sizeimage = 352 * 288 * 3 / 2,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 0},
+};
+
+/* Frame packet header offsets for the spca508 */
+#define SPCA508_OFFSET_DATA 37
+
+/*
+ * Initialization data: this is the first set-up data written to the
+ * device (before the open data).
+ */
+static const u16 spca508_init_data[][2] = {
+	{0x0000, 0x870b},
+
+	{0x0020, 0x8112},	/* Video drop enable, ISO streaming disable */
+	{0x0003, 0x8111},	/* Reset compression & memory */
+	{0x0000, 0x8110},	/* Disable all outputs */
+	/* READ {0x0000, 0x8114} -> 0000: 00  */
+	{0x0000, 0x8114},	/* SW GPIO data */
+	{0x0008, 0x8110},	/* Enable charge pump output */
+	{0x0002, 0x8116},	/* 200 kHz pump clock */
+	/* UNKNOWN DIRECTION (URB_FUNCTION_SELECT_INTERFACE:) */
+	{0x0003, 0x8111},	/* Reset compression & memory */
+	{0x0000, 0x8111},	/* Normal mode (not reset) */
+	{0x0098, 0x8110},
+		/* Enable charge pump output, sync.serial,external 2x clock */
+	{0x000d, 0x8114},	/* SW GPIO data */
+	{0x0002, 0x8116},	/* 200 kHz pump clock */
+	{0x0020, 0x8112},	/* Video drop enable, ISO streaming disable */
+/* --------------------------------------- */
+	{0x000f, 0x8402},	/* memory bank */
+	{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. */
+	{0x00c0, 0x8804},	/* SSI slave addr */
+	{0x0008, 0x8802},	/* 375 Khz SSI clock */
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8802 } -> 0000: 08  */
+	{0x0008, 0x8802},	/* 375 Khz SSI clock */
+	{0x0012, 0x8801},	/* SSI reg addr */
+	{0x0080, 0x8800},	/* SSI data to write */
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8802 } -> 0000: 08  */
+	{0x0008, 0x8802},	/* 375 Khz SSI clock */
+	{0x0012, 0x8801},	/* SSI reg addr */
+	{0x0000, 0x8800},	/* SSI data to write */
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8802 } -> 0000: 08  */
+	{0x0008, 0x8802},	/* 375 Khz SSI clock */
+	{0x0011, 0x8801},	/* SSI reg addr */
+	{0x0040, 0x8800},	/* SSI data to write */
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8802 } -> 0000: 08  */
+	{0x0008, 0x8802},
+	{0x0013, 0x8801},
+	{0x0000, 0x8800},
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8802 } -> 0000: 08  */
+	{0x0008, 0x8802},
+	{0x0014, 0x8801},
+	{0x0000, 0x8800},
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8802 } -> 0000: 08  */
+	{0x0008, 0x8802},
+	{0x0015, 0x8801},
+	{0x0001, 0x8800},
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8802 } -> 0000: 08  */
+	{0x0008, 0x8802},
+	{0x0016, 0x8801},
+	{0x0003, 0x8800},
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8802 } -> 0000: 08  */
+	{0x0008, 0x8802},
+	{0x0017, 0x8801},
+	{0x0036, 0x8800},
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8802 } -> 0000: 08  */
+	{0x0008, 0x8802},
+	{0x0018, 0x8801},
+	{0x00ec, 0x8800},
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8802 } -> 0000: 08  */
+	{0x0008, 0x8802},
+	{0x001a, 0x8801},
+	{0x0094, 0x8800},
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8802 } -> 0000: 08  */
+	{0x0008, 0x8802},
+	{0x001b, 0x8801},
+	{0x0000, 0x8800},
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8802 } -> 0000: 08  */
+	{0x0008, 0x8802},
+	{0x0027, 0x8801},
+	{0x00a2, 0x8800},
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8802 } -> 0000: 08  */
+	{0x0008, 0x8802},
+	{0x0028, 0x8801},
+	{0x0040, 0x8800},
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8802 } -> 0000: 08  */
+	{0x0008, 0x8802},
+	{0x002a, 0x8801},
+	{0x0084, 0x8800},
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8803 } -> 0000: 00 */
+	/* READ { 0x0001, 0x8802 } -> 0000: 08  */
+	{0x0008, 0x8802},
+	{0x002b, 0x8801},
+	{0x00a8, 0x8800},
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8802 } -> 0000: 08  */
+	{0x0008, 0x8802},
+	{0x002c, 0x8801},
+	{0x00fe, 0x8800},
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8802 } -> 0000: 08  */
+	{0x0008, 0x8802},
+	{0x002d, 0x8801},
+	{0x0003, 0x8800},
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8802 } -> 0000: 08  */
+	{0x0008, 0x8802},
+	{0x0038, 0x8801},
+	{0x0083, 0x8800},
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8802 } -> 0000: 08  */
+	{0x0008, 0x8802},
+	{0x0033, 0x8801},
+	{0x0081, 0x8800},
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8802 } -> 0000: 08  */
+	{0x0008, 0x8802},
+	{0x0034, 0x8801},
+	{0x004a, 0x8800},
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8802 } -> 0000: 08  */
+	{0x0008, 0x8802},
+	{0x0039, 0x8801},
+	{0x0000, 0x8800},
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8802 } -> 0000: 08  */
+	{0x0008, 0x8802},
+	{0x0010, 0x8801},
+	{0x00a8, 0x8800},
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8802 } -> 0000: 08  */
+	{0x0008, 0x8802},
+	{0x0006, 0x8801},
+	{0x0058, 0x8800},
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8803 } -> 0000: 00 */
+	/* READ { 0x0001, 0x8802 } -> 0000: 08  */
+	{0x0008, 0x8802},
+	{0x0000, 0x8801},
+	{0x0004, 0x8800},
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8802 } -> 0000: 08  */
+	{0x0008, 0x8802},
+	{0x0040, 0x8801},
+	{0x0080, 0x8800},
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8802 } -> 0000: 08  */
+	{0x0008, 0x8802},
+	{0x0041, 0x8801},
+	{0x000c, 0x8800},
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8802 } -> 0000: 08  */
+	{0x0008, 0x8802},
+	{0x0042, 0x8801},
+	{0x000c, 0x8800},
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8802 } -> 0000: 08  */
+	{0x0008, 0x8802},
+	{0x0043, 0x8801},
+	{0x0028, 0x8800},
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8802 } -> 0000: 08  */
+	{0x0008, 0x8802},
+	{0x0044, 0x8801},
+	{0x0080, 0x8800},
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8802 } -> 0000: 08  */
+	{0x0008, 0x8802},
+	{0x0045, 0x8801},
+	{0x0020, 0x8800},
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8802 } -> 0000: 08  */
+	{0x0008, 0x8802},
+	{0x0046, 0x8801},
+	{0x0020, 0x8800},
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8802 } -> 0000: 08  */
+	{0x0008, 0x8802},
+	{0x0047, 0x8801},
+	{0x0080, 0x8800},
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8802 } -> 0000: 08  */
+	{0x0008, 0x8802},
+	{0x0048, 0x8801},
+	{0x004c, 0x8800},
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8802 } -> 0000: 08  */
+	{0x0008, 0x8802},
+	{0x0049, 0x8801},
+	{0x0084, 0x8800},
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8802 } -> 0000: 08  */
+	{0x0008, 0x8802},
+	{0x004a, 0x8801},
+	{0x0084, 0x8800},
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8802 } -> 0000: 08  */
+	{0x0008, 0x8802},
+	{0x004b, 0x8801},
+	{0x0084, 0x8800},
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* --------------------------------------- */
+	{0x0012, 0x8700},	/* Clock speed 48Mhz/(2+2)/2= 6 Mhz */
+	{0x0000, 0x8701},	/* CKx1 clock delay adj */
+	{0x0000, 0x8701},	/* CKx1 clock delay adj */
+	{0x0001, 0x870c},	/* CKOx2 output */
+	/* --------------------------------------- */
+	{0x0080, 0x8600},	/* Line memory read counter (L) */
+	{0x0001, 0x8606},	/* reserved */
+	{0x0064, 0x8607},	/* Line memory read counter (H) 0x6480=25,728 */
+	{0x002a, 0x8601},	/* CDSP sharp interpolation mode,
+	 *			line sel for color sep, edge enhance enab */
+	{0x0000, 0x8602},	/* optical black level for user settng = 0 */
+	{0x0080, 0x8600},	/* Line memory read counter (L) */
+	{0x000a, 0x8603},	/* optical black level calc mode:
+				 * auto; optical black offset = 10 */
+	{0x00df, 0x865b},	/* Horiz offset for valid pixels (L)=0xdf */
+	{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. */
+	{0x0058, 0x865d},	/* Horiz valid pixels (*4) (L) = 352 */
+	{0x0048, 0x865e},	/* Vert valid lines (*4) (L) = 288 */
+
+	{0x0015, 0x8608},	/* A11 Coef ... */
+	{0x0030, 0x8609},
+	{0x00fb, 0x860a},
+	{0x003e, 0x860b},
+	{0x00ce, 0x860c},
+	{0x00f4, 0x860d},
+	{0x00eb, 0x860e},
+	{0x00dc, 0x860f},
+	{0x0039, 0x8610},
+	{0x0001, 0x8611},	/* R offset for white balance ... */
+	{0x0000, 0x8612},
+	{0x0001, 0x8613},
+	{0x0000, 0x8614},
+	{0x005b, 0x8651},	/* R gain for white balance ... */
+	{0x0040, 0x8652},
+	{0x0060, 0x8653},
+	{0x0040, 0x8654},
+	{0x0000, 0x8655},
+	{0x0001, 0x863f},	/* Fixed gamma correction enable, USB control,
+				 * lum filter disable, lum noise clip disable */
+	{0x00a1, 0x8656},	/* Window1 size 256x256, Windows2 size 64x64,
+				 * gamma look-up disable,
+				 * new edge enhancement enable */
+	{0x0018, 0x8657},	/* Edge gain high thresh */
+	{0x0020, 0x8658},	/* Edge gain low thresh */
+	{0x000a, 0x8659},	/* Edge bandwidth high threshold */
+	{0x0005, 0x865a},	/* Edge bandwidth low threshold */
+	/* -------------------------------- */
+	{0x0030, 0x8112},	/* Video drop enable, ISO streaming enable */
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8802 } -> 0000: 08  */
+	{0xa908, 0x8802},
+	{0x0034, 0x8801},	/* SSI reg addr */
+	{0x00ca, 0x8800},
+	/* SSI data to write */
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8802 } -> 0000: 08  */
+	{0x1f08, 0x8802},
+	{0x0006, 0x8801},
+	{0x0080, 0x8800},
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+
+/* ----- Read back coefs we wrote earlier. */
+	/* READ { 0x0000, 0x8608 } -> 0000: 15  */
+	/* READ { 0x0000, 0x8609 } -> 0000: 30  */
+	/* READ { 0x0000, 0x860a } -> 0000: fb  */
+	/* READ { 0x0000, 0x860b } -> 0000: 3e  */
+	/* READ { 0x0000, 0x860c } -> 0000: ce  */
+	/* READ { 0x0000, 0x860d } -> 0000: f4  */
+	/* READ { 0x0000, 0x860e } -> 0000: eb  */
+	/* READ { 0x0000, 0x860f } -> 0000: dc  */
+	/* READ { 0x0000, 0x8610 } -> 0000: 39  */
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8802 } -> 0000: 08  */
+	{0xb008, 0x8802},
+	{0x0006, 0x8801},
+	{0x007d, 0x8800},
+	/* READ { 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. */
+	{0x0015, 0x8608},
+	{0x0030, 0x8609},
+	{0xfffb, 0x860a},
+	{0x003e, 0x860b},
+	{0xffce, 0x860c},
+	{0xfff4, 0x860d},
+	{0xffeb, 0x860e},
+	{0xffdc, 0x860f},
+	{0x0039, 0x8610},
+	{0x0018, 0x8657},
+
+	{0x0000, 0x8508},	/* Disable compression. */
+	/* Previous line was:
+	{0x0021, 0x8508},	 * Enable compression. */
+	{0x0032, 0x850b},	/* compression stuff */
+	{0x0003, 0x8509},	/* compression stuff */
+	{0x0011, 0x850a},	/* compression stuff */
+	{0x0021, 0x850d},	/* compression stuff */
+	{0x0010, 0x850c},	/* compression stuff */
+	{0x0003, 0x8500},	/* *** Video mode: 160x120 */
+	{0x0001, 0x8501},	/* Hardware-dominated snap control */
+	{0x0061, 0x8656},	/* Window1 size 128x128, Windows2 size 128x128,
+				 * gamma look-up disable,
+				 * new edge enhancement enable */
+	{0x0018, 0x8617},	/* Window1 start X (*2) */
+	{0x0008, 0x8618},	/* Window1 start Y (*2) */
+	{0x0061, 0x8656},	/* Window1 size 128x128, Windows2 size 128x128,
+				 * gamma look-up disable,
+				 * new edge enhancement enable */
+	{0x0058, 0x8619},	/* Window2 start X (*2) */
+	{0x0008, 0x861a},	/* Window2 start Y (*2) */
+	{0x00ff, 0x8615},	/* High lum thresh for white balance */
+	{0x0000, 0x8616},	/* Low lum thresh for white balance */
+	{0x0012, 0x8700},	/* Clock speed 48Mhz/(2+2)/2= 6 Mhz */
+	{0x0012, 0x8700},	/* Clock speed 48Mhz/(2+2)/2= 6 Mhz */
+	/* READ { 0x0000, 0x8656 } -> 0000: 61  */
+	{0x0028, 0x8802},    /* 375 Khz SSI clock, SSI r/w sync with VSYNC */
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8802 } -> 0000: 28  */
+	{0x1f28, 0x8802},    /* 375 Khz SSI clock, SSI r/w sync with VSYNC */
+	{0x0010, 0x8801},	/* SSI reg addr */
+	{0x003e, 0x8800},	/* SSI data to write */
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	{0x0028, 0x8802},
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8802 } -> 0000: 28  */
+	{0x1f28, 0x8802},
+	{0x0000, 0x8801},
+	{0x001f, 0x8800},
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	{0x0001, 0x8602},    /* optical black level for user settning = 1 */
+
+	/* Original: */
+	{0x0023, 0x8700},	/* Clock speed 48Mhz/(3+2)/4= 2.4 Mhz */
+	{0x000f, 0x8602},    /* optical black level for user settning = 15 */
+
+	{0x0028, 0x8802},
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8802 } -> 0000: 28  */
+	{0x1f28, 0x8802},
+	{0x0010, 0x8801},
+	{0x007b, 0x8800},
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	{0x002f, 0x8651},	/* R gain for white balance ... */
+	{0x0080, 0x8653},
+	/* READ { 0x0000, 0x8655 } -> 0000: 00  */
+	{0x0000, 0x8655},
+
+	{0x0030, 0x8112},	/* Video drop enable, ISO streaming enable */
+	{0x0020, 0x8112},	/* Video drop enable, ISO streaming disable */
+	/* UNKNOWN DIRECTION (URB_FUNCTION_SELECT_INTERFACE: (ALT=0) ) */
+	{}
+};
+
+/*
+ * Initialization data for Intel EasyPC Camera CS110
+ */
+static const u16 spca508cs110_init_data[][2] = {
+	{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 */
+	{0x0000, 0x8603},	/* Optical black level Offset */
+
+/*	{0x0000, 0x8611},	 * 0 R  Offset for white Balance */
+/*	{0x0000, 0x8612},	 * 1 Gr Offset for white Balance */
+/*	{0x0000, 0x8613},	 * 1f B  Offset for white Balance */
+/*	{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) */
+
+	{0x0000, 0x8655},
+		/* High bits for white balance*****brightness control*** */
+	{}
+};
+
+static const u16 spca508_sightcam_init_data[][2] = {
+/* This line seems to setup the frame/canvas */
+	{0x000f, 0x8402},
+
+/* These 6 lines are needed to startup the webcam */
+	{0x0090, 0x8110},
+	{0x0001, 0x8114},
+	{0x0001, 0x8114},
+	{0x0001, 0x8114},
+	{0x0003, 0x8114},
+	{0x0080, 0x8804},
+
+/* This part seems to make the pictures darker? (autobrightness?) */
+	{0x0001, 0x8801},
+	{0x0004, 0x8800},
+	{0x0003, 0x8801},
+	{0x00e0, 0x8800},
+	{0x0004, 0x8801},
+	{0x00b4, 0x8800},
+	{0x0005, 0x8801},
+	{0x0000, 0x8800},
+
+	{0x0006, 0x8801},
+	{0x00e0, 0x8800},
+	{0x0007, 0x8801},
+	{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.
+ */
+	{0x0014, 0x8801},
+	{0x0008, 0x8800},
+	{0x0015, 0x8801},
+	{0x0067, 0x8800},
+	{0x0016, 0x8801},
+	{0x0000, 0x8800},
+	{0x0017, 0x8801},
+	{0x0020, 0x8800},
+	{0x0018, 0x8801},
+	{0x0044, 0x8800},
+
+/* Makes the picture darker - and the
+ * cam won't start if not included
+ */
+	{0x001e, 0x8801},
+	{0x00ea, 0x8800},
+	{0x001f, 0x8801},
+	{0x0001, 0x8800},
+	{0x0003, 0x8801},
+	{0x00e0, 0x8800},
+
+/* seems to place the colors ontop of each other #1 */
+	{0x0006, 0x8704},
+	{0x0001, 0x870c},
+	{0x0016, 0x8600},
+	{0x0002, 0x8606},
+
+/* if not included the pictures becomes _very_ dark */
+	{0x0064, 0x8607},
+	{0x003a, 0x8601},
+	{0x0000, 0x8602},
+
+/* seems to place the colors ontop of each other #2 */
+	{0x0016, 0x8600},
+	{0x0018, 0x8617},
+	{0x0008, 0x8618},
+	{0x00a1, 0x8656},
+
+/* webcam won't start if not included */
+	{0x0007, 0x865b},
+	{0x0001, 0x865c},
+	{0x0058, 0x865d},
+	{0x0048, 0x865e},
+
+/* adjusts the colors */
+	{0x0049, 0x8651},
+	{0x0040, 0x8652},
+	{0x004c, 0x8653},
+	{0x0040, 0x8654},
+	{}
+};
+
+static const u16 spca508_sightcam2_init_data[][2] = {
+	{0x0020, 0x8112},
+
+	{0x000f, 0x8402},
+	{0x0000, 0x8403},
+
+	{0x0008, 0x8201},
+	{0x0008, 0x8200},
+	{0x0001, 0x8200},
+	{0x0009, 0x8201},
+	{0x0008, 0x8200},
+	{0x0001, 0x8200},
+	{0x000a, 0x8201},
+	{0x0008, 0x8200},
+	{0x0001, 0x8200},
+	{0x000b, 0x8201},
+	{0x0008, 0x8200},
+	{0x0001, 0x8200},
+	{0x000c, 0x8201},
+	{0x0008, 0x8200},
+	{0x0001, 0x8200},
+	{0x000d, 0x8201},
+	{0x0008, 0x8200},
+	{0x0001, 0x8200},
+	{0x000e, 0x8201},
+	{0x0008, 0x8200},
+	{0x0001, 0x8200},
+	{0x0007, 0x8201},
+	{0x0008, 0x8200},
+	{0x0001, 0x8200},
+	{0x000f, 0x8201},
+	{0x0008, 0x8200},
+	{0x0001, 0x8200},
+
+	{0x0018, 0x8660},
+	{0x0010, 0x8201},
+
+	{0x0008, 0x8200},
+	{0x0001, 0x8200},
+	{0x0011, 0x8201},
+	{0x0008, 0x8200},
+	{0x0001, 0x8200},
+
+	{0x0000, 0x86b0},
+	{0x0034, 0x86b1},
+	{0x0000, 0x86b2},
+	{0x0049, 0x86b3},
+	{0x0000, 0x86b4},
+	{0x0000, 0x86b4},
+
+	{0x0012, 0x8201},
+	{0x0008, 0x8200},
+	{0x0001, 0x8200},
+	{0x0013, 0x8201},
+	{0x0008, 0x8200},
+	{0x0001, 0x8200},
+
+	{0x0001, 0x86b0},
+	{0x00aa, 0x86b1},
+	{0x0000, 0x86b2},
+	{0x00e4, 0x86b3},
+	{0x0000, 0x86b4},
+	{0x0000, 0x86b4},
+
+	{0x0018, 0x8660},
+
+	{0x0090, 0x8110},
+	{0x0001, 0x8114},
+	{0x0001, 0x8114},
+	{0x0001, 0x8114},
+	{0x0003, 0x8114},
+
+	{0x0080, 0x8804},
+	{0x0003, 0x8801},
+	{0x0012, 0x8800},
+	{0x0004, 0x8801},
+	{0x0005, 0x8800},
+	{0x0005, 0x8801},
+	{0x0000, 0x8800},
+	{0x0006, 0x8801},
+	{0x0000, 0x8800},
+	{0x0007, 0x8801},
+	{0x0000, 0x8800},
+	{0x0008, 0x8801},
+	{0x0005, 0x8800},
+	{0x000a, 0x8700},
+	{0x000e, 0x8801},
+	{0x0004, 0x8800},
+	{0x0005, 0x8801},
+	{0x0047, 0x8800},
+	{0x0006, 0x8801},
+	{0x0000, 0x8800},
+	{0x0007, 0x8801},
+	{0x00c0, 0x8800},
+	{0x0008, 0x8801},
+	{0x0003, 0x8800},
+	{0x0013, 0x8801},
+	{0x0001, 0x8800},
+	{0x0009, 0x8801},
+	{0x0000, 0x8800},
+	{0x000a, 0x8801},
+	{0x0000, 0x8800},
+	{0x000b, 0x8801},
+	{0x0000, 0x8800},
+	{0x000c, 0x8801},
+	{0x0000, 0x8800},
+	{0x000e, 0x8801},
+	{0x0004, 0x8800},
+	{0x000f, 0x8801},
+	{0x0000, 0x8800},
+	{0x0010, 0x8801},
+	{0x0006, 0x8800},
+	{0x0011, 0x8801},
+	{0x0006, 0x8800},
+	{0x0012, 0x8801},
+	{0x0000, 0x8800},
+	{0x0013, 0x8801},
+	{0x0001, 0x8800},
+
+	{0x000a, 0x8700},
+	{0x0000, 0x8702},
+	{0x0000, 0x8703},
+	{0x00c2, 0x8704},
+	{0x0001, 0x870c},
+
+	{0x0044, 0x8600},
+	{0x0002, 0x8606},
+	{0x0064, 0x8607},
+	{0x003a, 0x8601},
+	{0x0008, 0x8602},
+	{0x0044, 0x8600},
+	{0x0018, 0x8617},
+	{0x0008, 0x8618},
+	{0x00a1, 0x8656},
+	{0x0004, 0x865b},
+	{0x0002, 0x865c},
+	{0x0058, 0x865d},
+	{0x0048, 0x865e},
+	{0x0012, 0x8608},
+	{0x002c, 0x8609},
+	{0x0002, 0x860a},
+	{0x002c, 0x860b},
+	{0x00db, 0x860c},
+	{0x00f9, 0x860d},
+	{0x00f1, 0x860e},
+	{0x00e3, 0x860f},
+	{0x002c, 0x8610},
+	{0x006c, 0x8651},
+	{0x0041, 0x8652},
+	{0x0059, 0x8653},
+	{0x0040, 0x8654},
+	{0x00fa, 0x8611},
+	{0x00ff, 0x8612},
+	{0x00f8, 0x8613},
+	{0x0000, 0x8614},
+	{0x0001, 0x863f},
+	{0x0000, 0x8640},
+	{0x0026, 0x8641},
+	{0x0045, 0x8642},
+	{0x0060, 0x8643},
+	{0x0075, 0x8644},
+	{0x0088, 0x8645},
+	{0x009b, 0x8646},
+	{0x00b0, 0x8647},
+	{0x00c5, 0x8648},
+	{0x00d2, 0x8649},
+	{0x00dc, 0x864a},
+	{0x00e5, 0x864b},
+	{0x00eb, 0x864c},
+	{0x00f0, 0x864d},
+	{0x00f6, 0x864e},
+	{0x00fa, 0x864f},
+	{0x00ff, 0x8650},
+	{0x0060, 0x8657},
+	{0x0010, 0x8658},
+	{0x0018, 0x8659},
+	{0x0005, 0x865a},
+	{0x0018, 0x8660},
+	{0x0003, 0x8509},
+	{0x0011, 0x850a},
+	{0x0032, 0x850b},
+	{0x0010, 0x850c},
+	{0x0021, 0x850d},
+	{0x0001, 0x8500},
+	{0x0000, 0x8508},
+	{0x0012, 0x8608},
+	{0x002c, 0x8609},
+	{0x0002, 0x860a},
+	{0x0039, 0x860b},
+	{0x00d0, 0x860c},
+	{0x00f7, 0x860d},
+	{0x00ed, 0x860e},
+	{0x00db, 0x860f},
+	{0x0039, 0x8610},
+	{0x0012, 0x8657},
+	{0x000c, 0x8619},
+	{0x0004, 0x861a},
+	{0x00a1, 0x8656},
+	{0x00c8, 0x8615},
+	{0x0032, 0x8616},
+
+	{0x0030, 0x8112},
+	{0x0020, 0x8112},
+	{0x0020, 0x8112},
+	{0x000f, 0x8402},
+	{0x0000, 0x8403},
+
+	{0x0090, 0x8110},
+	{0x0001, 0x8114},
+	{0x0001, 0x8114},
+	{0x0001, 0x8114},
+	{0x0003, 0x8114},
+	{0x0080, 0x8804},
+
+	{0x0003, 0x8801},
+	{0x0012, 0x8800},
+	{0x0004, 0x8801},
+	{0x0005, 0x8800},
+	{0x0005, 0x8801},
+	{0x0047, 0x8800},
+	{0x0006, 0x8801},
+	{0x0000, 0x8800},
+	{0x0007, 0x8801},
+	{0x00c0, 0x8800},
+	{0x0008, 0x8801},
+	{0x0003, 0x8800},
+	{0x000a, 0x8700},
+	{0x000e, 0x8801},
+	{0x0004, 0x8800},
+	{0x0005, 0x8801},
+	{0x0047, 0x8800},
+	{0x0006, 0x8801},
+	{0x0000, 0x8800},
+	{0x0007, 0x8801},
+	{0x00c0, 0x8800},
+	{0x0008, 0x8801},
+	{0x0003, 0x8800},
+	{0x0013, 0x8801},
+	{0x0001, 0x8800},
+	{0x0009, 0x8801},
+	{0x0000, 0x8800},
+	{0x000a, 0x8801},
+	{0x0000, 0x8800},
+	{0x000b, 0x8801},
+	{0x0000, 0x8800},
+	{0x000c, 0x8801},
+	{0x0000, 0x8800},
+	{0x000e, 0x8801},
+	{0x0004, 0x8800},
+	{0x000f, 0x8801},
+	{0x0000, 0x8800},
+	{0x0010, 0x8801},
+	{0x0006, 0x8800},
+	{0x0011, 0x8801},
+	{0x0006, 0x8800},
+	{0x0012, 0x8801},
+	{0x0000, 0x8800},
+	{0x0013, 0x8801},
+	{0x0001, 0x8800},
+	{0x000a, 0x8700},
+	{0x0000, 0x8702},
+	{0x0000, 0x8703},
+	{0x00c2, 0x8704},
+	{0x0001, 0x870c},
+	{0x0044, 0x8600},
+	{0x0002, 0x8606},
+	{0x0064, 0x8607},
+	{0x003a, 0x8601},
+	{0x0008, 0x8602},
+	{0x0044, 0x8600},
+	{0x0018, 0x8617},
+	{0x0008, 0x8618},
+	{0x00a1, 0x8656},
+	{0x0004, 0x865b},
+	{0x0002, 0x865c},
+	{0x0058, 0x865d},
+	{0x0048, 0x865e},
+	{0x0012, 0x8608},
+	{0x002c, 0x8609},
+	{0x0002, 0x860a},
+	{0x002c, 0x860b},
+	{0x00db, 0x860c},
+	{0x00f9, 0x860d},
+	{0x00f1, 0x860e},
+	{0x00e3, 0x860f},
+	{0x002c, 0x8610},
+	{0x006c, 0x8651},
+	{0x0041, 0x8652},
+	{0x0059, 0x8653},
+	{0x0040, 0x8654},
+	{0x00fa, 0x8611},
+	{0x00ff, 0x8612},
+	{0x00f8, 0x8613},
+	{0x0000, 0x8614},
+	{0x0001, 0x863f},
+	{0x0000, 0x8640},
+	{0x0026, 0x8641},
+	{0x0045, 0x8642},
+	{0x0060, 0x8643},
+	{0x0075, 0x8644},
+	{0x0088, 0x8645},
+	{0x009b, 0x8646},
+	{0x00b0, 0x8647},
+	{0x00c5, 0x8648},
+	{0x00d2, 0x8649},
+	{0x00dc, 0x864a},
+	{0x00e5, 0x864b},
+	{0x00eb, 0x864c},
+	{0x00f0, 0x864d},
+	{0x00f6, 0x864e},
+	{0x00fa, 0x864f},
+	{0x00ff, 0x8650},
+	{0x0060, 0x8657},
+	{0x0010, 0x8658},
+	{0x0018, 0x8659},
+	{0x0005, 0x865a},
+	{0x0018, 0x8660},
+	{0x0003, 0x8509},
+	{0x0011, 0x850a},
+	{0x0032, 0x850b},
+	{0x0010, 0x850c},
+	{0x0021, 0x850d},
+	{0x0001, 0x8500},
+	{0x0000, 0x8508},
+
+	{0x0012, 0x8608},
+	{0x002c, 0x8609},
+	{0x0002, 0x860a},
+	{0x0039, 0x860b},
+	{0x00d0, 0x860c},
+	{0x00f7, 0x860d},
+	{0x00ed, 0x860e},
+	{0x00db, 0x860f},
+	{0x0039, 0x8610},
+	{0x0012, 0x8657},
+	{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 */
+/*	{0x0030, 0x8112}, */
+	{}
+};
+
+/*
+ * Initialization data for Creative Webcam Vista
+ */
+static const u16 spca508_vista_init_data[][2] = {
+	{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 { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 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 { 0x0001, 0x8803 } -> 0000: 00  */
+
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8802 } -> 0000: 10  */
+	{0x0010, 0x8802},
+	{0x0009, 0x8801},
+	{0x0042, 0x8805},
+	{0x0001, 0x8800},
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8802 } -> 0000: 10  */
+	{0x0010, 0x8802},
+	{0x003c, 0x8801},
+	{0x0001, 0x8805},
+	{0x0000, 0x8800},
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8802 } -> 0000: 10  */
+	{0x0010, 0x8802},
+	{0x0001, 0x8801},
+	{0x000a, 0x8805},
+	{0x0000, 0x8800},
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8802 } -> 0000: 10  */
+	{0x0010, 0x8802},
+	{0x0002, 0x8801},
+	{0x0000, 0x8805},
+	{0x0000, 0x8800},
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8802 } -> 0000: 10  */
+	{0x0010, 0x8802},
+	{0x0003, 0x8801},
+	{0x0027, 0x8805},
+	{0x0001, 0x8800},
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8802 } -> 0000: 10  */
+	{0x0010, 0x8802},
+	{0x0004, 0x8801},
+	{0x0065, 0x8805},
+	{0x0001, 0x8800},
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8802 } -> 0000: 10  */
+	{0x0010, 0x8802},
+	{0x0005, 0x8801},
+	{0x0003, 0x8805},
+	{0x0000, 0x8800},
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8802 } -> 0000: 10  */
+	{0x0010, 0x8802},
+	{0x0006, 0x8801},
+	{0x001c, 0x8805},
+	{0x0000, 0x8800},
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8802 } -> 0000: 10  */
+	{0x0010, 0x8802},
+	{0x0007, 0x8801},
+	{0x002a, 0x8805},
+	{0x0000, 0x8800},
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8802 } -> 0000: 10  */
+	{0x0010, 0x8802},
+	{0x000e, 0x8801},
+	{0x0000, 0x8805},
+	{0x0000, 0x8800},
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8802 } -> 0000: 10  */
+	{0x0010, 0x8802},
+	{0x0028, 0x8801},
+	{0x002e, 0x8805},
+	{0x0000, 0x8800},
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8802 } -> 0000: 10  */
+	{0x0010, 0x8802},
+	{0x0039, 0x8801},
+	{0x0013, 0x8805},
+	{0x0000, 0x8800},
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8802 } -> 0000: 10  */
+	{0x0010, 0x8802},
+	{0x003b, 0x8801},
+	{0x000c, 0x8805},
+	{0x0000, 0x8800},
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8802 } -> 0000: 10  */
+	{0x0010, 0x8802},
+	{0x0035, 0x8801},
+	{0x0028, 0x8805},
+	{0x0000, 0x8800},
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+
+	/* READ { 0x0001, 0x8803 } -> 0000: 00  */
+	/* READ { 0x0001, 0x8802 } -> 0000: 10  */
+	{0x0010, 0x8802},
+	{0x0009, 0x8801},
+	{0x0042, 0x8805},
+	{0x0001, 0x8800},
+	/* READ { 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 { 0x0000, 0x8608 } -> 0000: 13  */
+	/* READ { 0x0000, 0x8609 } -> 0000: 28  */
+	/* READ { 0x0000, 0x8610 } -> 0000: 05  */
+	/* READ { 0x0000, 0x8611 } -> 0000: 25  */
+	/* READ { 0x0000, 0x8612 } -> 0000: e1  */
+	/* READ { 0x0000, 0x8613 } -> 0000: fa  */
+	/* READ { 0x0000, 0x8614 } -> 0000: f4  */
+	/* READ { 0x0000, 0x8615 } -> 0000: e8  */
+	/* READ { 0x0000, 0x8616 } -> 0000: 25  */
+	{}
+};
+
+static int reg_write(struct gspca_dev *gspca_dev, u16 index, u16 value)
+{
+	int ret;
+	struct usb_device *dev = gspca_dev->dev;
+
+	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)
+		pr_err("reg write: error %d\n", 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) {
+		pr_err("reg_read err %d\n", ret);
+		return ret;
+	}
+	return gspca_dev->usb_buf[0];
+}
+
+/* send 1 or 2 bytes to the sensor via the Synchronous Serial Interface */
+static int ssi_w(struct gspca_dev *gspca_dev,
+		u16 reg, u16 val)
+{
+	int ret, retry;
+
+	ret = reg_write(gspca_dev, 0x8802, reg >> 8);
+	if (ret < 0)
+		goto out;
+	ret = reg_write(gspca_dev, 0x8801, reg & 0x00ff);
+	if (ret < 0)
+		goto out;
+	if ((reg & 0xff00) == 0x1000) {		/* if 2 bytes */
+		ret = reg_write(gspca_dev, 0x8805, val & 0x00ff);
+		if (ret < 0)
+			goto out;
+		val >>= 8;
+	}
+	ret = reg_write(gspca_dev, 0x8800, val);
+	if (ret < 0)
+		goto out;
+
+	/* poll until not busy */
+	retry = 10;
+	for (;;) {
+		ret = reg_read(gspca_dev, 0x8803);
+		if (ret < 0)
+			break;
+		if (gspca_dev->usb_buf[0] == 0)
+			break;
+		if (--retry <= 0) {
+			PERR("ssi_w busy %02x", gspca_dev->usb_buf[0]);
+			ret = -1;
+			break;
+		}
+		msleep(8);
+	}
+
+out:
+	return ret;
+}
+
+static int write_vector(struct gspca_dev *gspca_dev,
+			const u16 (*data)[2])
+{
+	int ret = 0;
+
+	while ((*data)[1] != 0) {
+		if ((*data)[1] & 0x8000) {
+			if ((*data)[1] == 0xdd00)	/* delay */
+				msleep((*data)[0]);
+			else
+				ret = reg_write(gspca_dev, (*data)[1],
+								(*data)[0]);
+		} else {
+			ret = ssi_w(gspca_dev, (*data)[1], (*data)[0]);
+		}
+		if (ret < 0)
+			break;
+		data++;
+	}
+	return ret;
+}
+
+/* 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;
+	const u16 (*init_data)[2];
+	static const u16 (*(init_data_tb[]))[2] = {
+		spca508_vista_init_data,	/* CreativeVista 0 */
+		spca508_sightcam_init_data,	/* HamaUSBSightcam 1 */
+		spca508_sightcam2_init_data,	/* HamaUSBSightcam2 2 */
+		spca508cs110_init_data,		/* IntelEasyPCCamera 3 */
+		spca508cs110_init_data,		/* MicroInnovationIC200 4 */
+		spca508_init_data,		/* ViewQuestVQ110 5 */
+	};
+	int data1, data2;
+
+	/* 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->cam_mode = sif_mode;
+	cam->nmodes = ARRAY_SIZE(sif_mode);
+
+	sd->subtype = id->driver_info;
+
+	init_data = init_data_tb[sd->subtype];
+	return write_vector(gspca_dev, init_data);
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+	return 0;
+}
+
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+	int mode;
+
+	mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
+	reg_write(gspca_dev, 0x8500, mode);
+	switch (mode) {
+	case 0:
+	case 1:
+		reg_write(gspca_dev, 0x8700, 0x28); /* clock */
+		break;
+	default:
+/*	case 2: */
+/*	case 3: */
+		reg_write(gspca_dev, 0x8700, 0x23); /* clock */
+		break;
+	}
+	reg_write(gspca_dev, 0x8112, 0x10 | 0x20);
+	return 0;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+	/* Video ISO disable, Video Drop Packet enable: */
+	reg_write(gspca_dev, 0x8112, 0x20);
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+			u8 *data,			/* isoc packet */
+			int len)			/* iso packet length */
+{
+	switch (data[0]) {
+	case 0:				/* start of frame */
+		gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+		data += SPCA508_OFFSET_DATA;
+		len -= SPCA508_OFFSET_DATA;
+		gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
+		break;
+	case 0xff:			/* drop */
+		break;
+	default:
+		data += 1;
+		len -= 1;
+		gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+		break;
+	}
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev, s32 brightness)
+{
+	/* MX seem contrast */
+	reg_write(gspca_dev, 0x8651, brightness);
+	reg_write(gspca_dev, 0x8652, brightness);
+	reg_write(gspca_dev, 0x8653, brightness);
+	reg_write(gspca_dev, 0x8654, brightness);
+}
+
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct gspca_dev *gspca_dev =
+		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+
+	gspca_dev->usb_err = 0;
+
+	if (!gspca_dev->streaming)
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_BRIGHTNESS:
+		setbrightness(gspca_dev, ctrl->val);
+		break;
+	}
+	return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+	.s_ctrl = sd_s_ctrl,
+};
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+	gspca_dev->vdev.ctrl_handler = hdl;
+	v4l2_ctrl_handler_init(hdl, 5);
+	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+
+	if (hdl->error) {
+		pr_err("Could not initialize controls\n");
+		return hdl->error;
+	}
+	return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+	.name = MODULE_NAME,
+	.config = sd_config,
+	.init = sd_init,
+	.init_controls = sd_init_controls,
+	.start = sd_start,
+	.stopN = sd_stopN,
+	.pkt_scan = sd_pkt_scan,
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+	{USB_DEVICE(0x0130, 0x0130), .driver_info = HamaUSBSightcam},
+	{USB_DEVICE(0x041e, 0x4018), .driver_info = CreativeVista},
+	{USB_DEVICE(0x0733, 0x0110), .driver_info = ViewQuestVQ110},
+	{USB_DEVICE(0x0af9, 0x0010), .driver_info = HamaUSBSightcam},
+	{USB_DEVICE(0x0af9, 0x0011), .driver_info = HamaUSBSightcam2},
+	{USB_DEVICE(0x8086, 0x0110), .driver_info = IntelEasyPCCamera},
+	{}
+};
+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,
+#ifdef CONFIG_PM
+	.suspend = gspca_suspend,
+	.resume = gspca_resume,
+	.reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/usb/gspca/spca561.c b/drivers/media/usb/gspca/spca561.c
new file mode 100644
index 0000000..403d71c
--- /dev/null
+++ b/drivers/media/usb/gspca/spca561.c
@@ -0,0 +1,932 @@
+/*
+ * 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "spca561"
+
+#include <linux/input.h>
+#include "gspca.h"
+
+MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
+MODULE_DESCRIPTION("GSPCA/SPCA561 USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+#define EXPOSURE_MAX (2047 + 325)
+
+/* specific webcam descriptor */
+struct sd {
+	struct gspca_dev gspca_dev;	/* !! must be the first item */
+
+	struct { /* hue/contrast control cluster */
+		struct v4l2_ctrl *contrast;
+		struct v4l2_ctrl *hue;
+	};
+	struct v4l2_ctrl *autogain;
+
+#define EXPO12A_DEF 3
+	__u8 expo12a;		/* expo/gain? for rev 12a */
+
+	__u8 chip_revision;
+#define Rev012A 0
+#define Rev072A 1
+
+	signed char ag_cnt;
+#define AG_CNT_START 13
+};
+
+static const struct v4l2_pix_format sif_012a_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},
+};
+
+static const struct v4l2_pix_format sif_072a_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_SGBRG8, V4L2_FIELD_NONE,
+		.bytesperline = 320,
+		.sizeimage = 320 * 240,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 1},
+	{352, 288, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
+		.bytesperline = 352,
+		.sizeimage = 352 * 288,
+		.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_INDEX_I2C_BASE 0x8800
+#define SPCA561_SNAPBIT 0x20
+#define SPCA561_SNAPCTRL 0x40
+
+static const u16 rev72a_reset[][2] = {
+	{0x0000, 0x8114},	/* Software GPIO output data */
+	{0x0001, 0x8114},	/* Software GPIO output data */
+	{0x0000, 0x8112},	/* Some kind of reset */
+	{}
+};
+static const __u16 rev72a_init_data1[][2] = {
+	{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 */
+	{}
+};
+static const u16 rev72a_init_sensor1[][2] = {
+	{0x0001, 0x000d},
+	{0x0002, 0x0018},
+	{0x0004, 0x0165},
+	{0x0005, 0x0021},
+	{0x0007, 0x00aa},
+	{0x0020, 0x1504},
+	{0x0039, 0x0002},
+	{0x0035, 0x0010},
+	{0x0009, 0x1049},
+	{0x0028, 0x000b},
+	{0x003b, 0x000f},
+	{0x003c, 0x0000},
+	{}
+};
+static const __u16 rev72a_init_data2[][2] = {
+	{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 */
+/* from ms-win */
+	{0x0000, 0x8611},	/* R offset for white balance */
+	{0x00fd, 0x8612},	/* Gr offset for white balance */
+	{0x0003, 0x8613},	/* B offset for white balance */
+	{0x0000, 0x8614},	/* Gb offset for white balance */
+/* from ms-win */
+	{0x0035, 0x8651},	/* R gain for white balance */
+	{0x0040, 0x8652},	/* Gr gain for white balance */
+	{0x005f, 0x8653},	/* B gain for white balance */
+	{0x0040, 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 */
+	{}
+};
+static const u16 rev72a_init_sensor2[][2] = {
+	{0x0003, 0x0121},
+	{0x0004, 0x0165},
+	{0x0005, 0x002f},	/* blanking control column */
+	{0x0006, 0x0000},	/* blanking mode row*/
+	{0x000a, 0x0002},
+	{0x0009, 0x1061},	/* setexposure times && pixel clock
+				 * 0001 0 | 000 0110 0001 */
+	{0x0035, 0x0014},
+	{}
+};
+
+/******************** QC Express etch2 stuff ********************/
+static const __u16 Pb100_1map8300[][2] = {
+	/* reg, value */
+	{0x8320, 0x3304},
+
+	{0x8303, 0x0125},	/* image area */
+	{0x8304, 0x0169},
+	{0x8328, 0x000b},
+	{0x833c, 0x0001},		/*fixme: win:07*/
+
+	{0x832f, 0x1904},		/*fixme: was 0419*/
+	{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] = {
+	{0x29, 0x8118},		/* Control register (various enable bits) */
+	{0x08, 0x8114},		/* GPIO: Led off */
+	{0x0e, 0x8112},		/* 0x0e stream off 0x3e stream on */
+	{0x00, 0x8102},		/* white balance - new */
+	{0x92, 0x8804},
+	{0x04, 0x8802},		/* windows uses 08 */
+	{}
+};
+static const __u16 spca561_161rev12A_data2[][2] = {
+	{0x21, 0x8118},
+	{0x10, 0x8500},
+	{0x07, 0x8601},
+	{0x07, 0x8602},
+	{0x04, 0x8501},
+
+	{0x07, 0x8201},		/* windows uses 02 */
+	{0x08, 0x8200},
+	{0x01, 0x8200},
+
+	{0x90, 0x8604},
+	{0x00, 0x8605},
+	{0xb0, 0x8603},
+
+	/* sensor gains */
+	{0x07, 0x8601},		/* white balance - new */
+	{0x07, 0x8602},		/* white balance - new */
+	{0x00, 0x8610},		/* *red */
+	{0x00, 0x8611},		/* 3f   *green */
+	{0x00, 0x8612},		/* green *blue */
+	{0x00, 0x8613},		/* blue *green */
+	{0x43, 0x8614},		/* green *red - white balance - was 0x35 */
+	{0x40, 0x8615},		/* 40   *green - white balance - was 0x35 */
+	{0x71, 0x8616},		/* 7a   *blue - white balance - was 0x35 */
+	{0x40, 0x8617},		/* 40   *green - white balance - was 0x35 */
+
+	{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},
+/*	{0x99, 0x8700},		 * - white balance - new (removed) */
+	/* HDG we used to do this in stop0, making the init state and the state
+	   after a start / stop different, so do this here instead. */
+	{0x29, 0x8118},
+	{}
+};
+
+static void reg_w_val(struct gspca_dev *gspca_dev, __u16 index, __u8 value)
+{
+	int ret;
+	struct usb_device *dev = gspca_dev->dev;
+
+	ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+			      0,		/* request */
+			      USB_DIR_OUT | 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)
+		pr_err("reg write: error %d\n", ret);
+}
+
+static void write_vector(struct gspca_dev *gspca_dev,
+			const __u16 data[][2])
+{
+	int i;
+
+	i = 0;
+	while (data[i][1] != 0) {
+		reg_w_val(gspca_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);
+}
+
+/* write 'len' bytes from gspca_dev->usb_buf */
+static void reg_w_buf(struct gspca_dev *gspca_dev,
+		      __u16 index, __u16 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_write(struct gspca_dev *gspca_dev, __u16 value, __u16 reg)
+{
+	int retry = 60;
+
+	reg_w_val(gspca_dev, 0x8801, reg);
+	reg_w_val(gspca_dev, 0x8805, value);
+	reg_w_val(gspca_dev, 0x8800, value >> 8);
+	do {
+		reg_r(gspca_dev, 0x8803, 1);
+		if (!gspca_dev->usb_buf[0])
+			return;
+		msleep(10);
+	} while (--retry);
+}
+
+static int i2c_read(struct gspca_dev *gspca_dev, __u16 reg, __u8 mode)
+{
+	int retry = 60;
+	__u8 value;
+
+	reg_w_val(gspca_dev, 0x8804, 0x92);
+	reg_w_val(gspca_dev, 0x8801, reg);
+	reg_w_val(gspca_dev, 0x8802, mode | 0x01);
+	do {
+		reg_r(gspca_dev, 0x8803, 1);
+		if (!gspca_dev->usb_buf[0]) {
+			reg_r(gspca_dev, 0x8800, 1);
+			value = gspca_dev->usb_buf[0];
+			reg_r(gspca_dev, 0x8805, 1);
+			return ((int) value << 8) | gspca_dev->usb_buf[0];
+		}
+		msleep(10);
+	} while (--retry);
+	return -1;
+}
+
+static void sensor_mapwrite(struct gspca_dev *gspca_dev,
+			    const __u16 (*sensormap)[2])
+{
+	while ((*sensormap)[0]) {
+		gspca_dev->usb_buf[0] = (*sensormap)[1];
+		gspca_dev->usb_buf[1] = (*sensormap)[1] >> 8;
+		reg_w_buf(gspca_dev, (*sensormap)[0], 2);
+		sensormap++;
+	}
+}
+
+static void write_sensor_72a(struct gspca_dev *gspca_dev,
+			    const __u16 (*sensor)[2])
+{
+	while ((*sensor)[0]) {
+		i2c_write(gspca_dev, (*sensor)[1], (*sensor)[0]);
+		sensor++;
+	}
+}
+
+static void init_161rev12A(struct gspca_dev *gspca_dev)
+{
+	write_vector(gspca_dev, spca561_161rev12A_data1);
+	sensor_mapwrite(gspca_dev, Pb100_1map8300);
+/*fixme: should be in sd_start*/
+	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;
+	}
+
+	cam = &gspca_dev->cam;
+	cam->needs_full_bandwidth = 1;
+
+	sd->chip_revision = id->driver_info;
+	if (sd->chip_revision == Rev012A) {
+		cam->cam_mode = sif_012a_mode;
+		cam->nmodes = ARRAY_SIZE(sif_012a_mode);
+	} else {
+		cam->cam_mode = sif_072a_mode;
+		cam->nmodes = ARRAY_SIZE(sif_072a_mode);
+	}
+	sd->expo12a = EXPO12A_DEF;
+	return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init_12a(struct gspca_dev *gspca_dev)
+{
+	PDEBUG(D_STREAM, "Chip revision: 012a");
+	init_161rev12A(gspca_dev);
+	return 0;
+}
+static int sd_init_72a(struct gspca_dev *gspca_dev)
+{
+	PDEBUG(D_STREAM, "Chip revision: 072a");
+	write_vector(gspca_dev, rev72a_reset);
+	msleep(200);
+	write_vector(gspca_dev, rev72a_init_data1);
+	write_sensor_72a(gspca_dev, rev72a_init_sensor1);
+	write_vector(gspca_dev, rev72a_init_data2);
+	write_sensor_72a(gspca_dev, rev72a_init_sensor2);
+	reg_w_val(gspca_dev, 0x8112, 0x30);
+	return 0;
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	__u16 reg;
+
+	if (sd->chip_revision == Rev012A)
+		reg = 0x8610;
+	else
+		reg = 0x8611;
+
+	reg_w_val(gspca_dev, reg + 0, val);		/* R */
+	reg_w_val(gspca_dev, reg + 1, val);		/* Gr */
+	reg_w_val(gspca_dev, reg + 2, val);		/* B */
+	reg_w_val(gspca_dev, reg + 3, val);		/* Gb */
+}
+
+static void setwhite(struct gspca_dev *gspca_dev, s32 white, s32 contrast)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	__u8 blue, red;
+	__u16 reg;
+
+	/* try to emulate MS-win as possible */
+	red = 0x20 + white * 3 / 8;
+	blue = 0x90 - white * 5 / 8;
+	if (sd->chip_revision == Rev012A) {
+		reg = 0x8614;
+	} else {
+		reg = 0x8651;
+		red += contrast - 0x20;
+		blue += contrast - 0x20;
+		reg_w_val(gspca_dev, 0x8652, contrast + 0x20); /* Gr */
+		reg_w_val(gspca_dev, 0x8654, contrast + 0x20); /* Gb */
+	}
+	reg_w_val(gspca_dev, reg, red);
+	reg_w_val(gspca_dev, reg + 2, blue);
+}
+
+/* rev 12a only */
+static void setexposure(struct gspca_dev *gspca_dev, s32 val)
+{
+	int i, expo = 0;
+
+	/* Register 0x8309 controls exposure for the spca561,
+	   the basic exposure setting goes from 1-2047, where 1 is completely
+	   dark and 2047 is very bright. It not only influences exposure but
+	   also the framerate (to allow for longer exposure) from 1 - 300 it
+	   only raises the exposure time then from 300 - 600 it halves the
+	   framerate to be able to further raise the exposure time and for every
+	   300 more it halves the framerate again. This allows for a maximum
+	   exposure time of circa 0.2 - 0.25 seconds (30 / (2000/3000) fps).
+	   Sometimes this is not enough, the 1-2047 uses bits 0-10, bits 11-12
+	   configure a divider for the base framerate which us used at the
+	   exposure setting of 1-300. These bits configure the base framerate
+	   according to the following formula: fps = 60 / (value + 2) */
+
+	/* We choose to use the high bits setting the fixed framerate divisor
+	   asap, as setting high basic exposure setting without the fixed
+	   divider in combination with high gains makes the cam stop */
+	int table[] =  { 0, 450, 550, 625, EXPOSURE_MAX };
+
+	for (i = 0; i < ARRAY_SIZE(table) - 1; i++) {
+		if (val <= table[i + 1]) {
+			expo  = val - table[i];
+			if (i)
+				expo += 300;
+			expo |= i << 11;
+			break;
+		}
+	}
+
+	gspca_dev->usb_buf[0] = expo;
+	gspca_dev->usb_buf[1] = expo >> 8;
+	reg_w_buf(gspca_dev, 0x8309, 2);
+}
+
+/* rev 12a only */
+static void setgain(struct gspca_dev *gspca_dev, s32 val)
+{
+	/* gain reg low 6 bits  0-63 gain, bit 6 and 7, both double the
+	   sensitivity when set, so 31 + one of them set == 63, and 15
+	   with both of them set == 63 */
+	if (val < 64)
+		gspca_dev->usb_buf[0] = val;
+	else if (val < 128)
+		gspca_dev->usb_buf[0] = (val / 2) | 0x40;
+	else
+		gspca_dev->usb_buf[0] = (val / 4) | 0xc0;
+
+	gspca_dev->usb_buf[1] = 0;
+	reg_w_buf(gspca_dev, 0x8335, 2);
+}
+
+static void setautogain(struct gspca_dev *gspca_dev, s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (val)
+		sd->ag_cnt = AG_CNT_START;
+	else
+		sd->ag_cnt = -1;
+}
+
+static int sd_start_12a(struct gspca_dev *gspca_dev)
+{
+	int mode;
+	static const __u8 Reg8391[8] =
+		{0x92, 0x30, 0x20, 0x00, 0x0c, 0x00, 0x00, 0x00};
+
+	mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;
+	if (mode <= 1) {
+		/* Use compression on 320x240 and above */
+		reg_w_val(gspca_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(gspca_dev, 0x8500, mode);
+	}		/* -- qq@kuku.eu.org */
+
+	gspca_dev->usb_buf[0] = 0xaa;
+	gspca_dev->usb_buf[1] = 0x00;
+	reg_w_buf(gspca_dev, 0x8307, 2);
+	/* clock - lower 0x8X values lead to fps > 30 */
+	reg_w_val(gspca_dev, 0x8700, 0x8a);
+					/* 0x8f 0x85 0x27 clock */
+	reg_w_val(gspca_dev, 0x8112, 0x1e | 0x20);
+	reg_w_val(gspca_dev, 0x850b, 0x03);
+	memcpy(gspca_dev->usb_buf, Reg8391, 8);
+	reg_w_buf(gspca_dev, 0x8391, 8);
+	reg_w_buf(gspca_dev, 0x8390, 8);
+
+	/* Led ON (bit 3 -> 0 */
+	reg_w_val(gspca_dev, 0x8114, 0x00);
+	return 0;
+}
+static int sd_start_72a(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int Clck;
+	int mode;
+
+	write_vector(gspca_dev, rev72a_reset);
+	msleep(200);
+	write_vector(gspca_dev, rev72a_init_data1);
+	write_sensor_72a(gspca_dev, rev72a_init_sensor1);
+
+	mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;
+	switch (mode) {
+	default:
+	case 0:
+		Clck = 0x27;		/* ms-win 0x87 */
+		break;
+	case 1:
+		Clck = 0x25;
+		break;
+	case 2:
+		Clck = 0x22;
+		break;
+	case 3:
+		Clck = 0x21;
+		break;
+	}
+	reg_w_val(gspca_dev, 0x8700, Clck);	/* 0x27 clock */
+	reg_w_val(gspca_dev, 0x8702, 0x81);
+	reg_w_val(gspca_dev, 0x8500, mode);	/* mode */
+	write_sensor_72a(gspca_dev, rev72a_init_sensor2);
+	setwhite(gspca_dev, v4l2_ctrl_g_ctrl(sd->hue),
+			v4l2_ctrl_g_ctrl(sd->contrast));
+/*	setbrightness(gspca_dev);	 * fixme: bad values */
+	setautogain(gspca_dev, v4l2_ctrl_g_ctrl(sd->autogain));
+	reg_w_val(gspca_dev, 0x8112, 0x10 | 0x20);
+	return 0;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (sd->chip_revision == Rev012A) {
+		reg_w_val(gspca_dev, 0x8112, 0x0e);
+		/* Led Off (bit 3 -> 1 */
+		reg_w_val(gspca_dev, 0x8114, 0x08);
+	} else {
+		reg_w_val(gspca_dev, 0x8112, 0x20);
+/*		reg_w_val(gspca_dev, 0x8102, 0x00); ?? */
+	}
+}
+
+static void do_autogain(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int expotimes;
+	int pixelclk;
+	int gainG;
+	__u8 R, Gr, Gb, B;
+	int y;
+	__u8 luma_mean = 110;
+	__u8 luma_delta = 20;
+	__u8 spring = 4;
+
+	if (sd->ag_cnt < 0)
+		return;
+	if (--sd->ag_cnt >= 0)
+		return;
+	sd->ag_cnt = AG_CNT_START;
+
+	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 < 3)
+				gainG = 3;
+			i2c_write(gspca_dev, gainG, 0x35);
+
+			if (expotimes > 0x0256)
+				expotimes = 0x0256;
+			else if (expotimes < 3)
+				expotimes = 3;
+			i2c_write(gspca_dev, expotimes | pixelclk, 0x09);
+		}
+		break;
+	}
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+			u8 *data,		/* isoc packet */
+			int len)		/* iso packet length */
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	len--;
+	switch (*data++) {			/* sequence number */
+	case 0:					/* start of frame */
+		gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+
+		/* This should never happen */
+		if (len < 2) {
+			PERR("Short SOF packet, ignoring");
+			gspca_dev->last_packet_type = DISCARD_PACKET;
+			return;
+		}
+
+#if IS_ENABLED(CONFIG_INPUT)
+		if (data[0] & 0x20) {
+			input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1);
+			input_sync(gspca_dev->input_dev);
+			input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
+			input_sync(gspca_dev->input_dev);
+		}
+#endif
+
+		if (data[1] & 0x10) {
+			/* compressed bayer */
+			gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
+		} else {
+			/* raw bayer (with a header, which we skip) */
+			if (sd->chip_revision == Rev012A) {
+				data += 20;
+				len -= 20;
+			} else {
+				data += 16;
+				len -= 16;
+			}
+			gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
+		}
+		return;
+	case 0xff:			/* drop (empty mpackets) */
+		return;
+	}
+	gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+}
+
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct gspca_dev *gspca_dev =
+		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+	struct sd *sd = (struct sd *)gspca_dev;
+
+	gspca_dev->usb_err = 0;
+
+	if (!gspca_dev->streaming)
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_BRIGHTNESS:
+		setbrightness(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_CONTRAST:
+		/* hue/contrast control cluster for 72a */
+		setwhite(gspca_dev, sd->hue->val, ctrl->val);
+		break;
+	case V4L2_CID_HUE:
+		/* just plain hue control for 12a */
+		setwhite(gspca_dev, ctrl->val, 0);
+		break;
+	case V4L2_CID_EXPOSURE:
+		setexposure(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_GAIN:
+		setgain(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_AUTOGAIN:
+		setautogain(gspca_dev, ctrl->val);
+		break;
+	}
+	return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+	.s_ctrl = sd_s_ctrl,
+};
+
+static int sd_init_controls_12a(struct gspca_dev *gspca_dev)
+{
+	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+	gspca_dev->vdev.ctrl_handler = hdl;
+	v4l2_ctrl_handler_init(hdl, 3);
+	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_HUE, 1, 0x7f, 1, 0x40);
+	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_BRIGHTNESS, -128, 127, 1, 0);
+	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_EXPOSURE, 1, EXPOSURE_MAX, 1, 700);
+	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_GAIN, 0, 255, 1, 63);
+
+	if (hdl->error) {
+		pr_err("Could not initialize controls\n");
+		return hdl->error;
+	}
+	return 0;
+}
+
+static int sd_init_controls_72a(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *)gspca_dev;
+	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+	gspca_dev->vdev.ctrl_handler = hdl;
+	v4l2_ctrl_handler_init(hdl, 4);
+	sd->contrast = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_CONTRAST, 0, 0x3f, 1, 0x20);
+	sd->hue = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_HUE, 1, 0x7f, 1, 0x40);
+	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_BRIGHTNESS, 0, 0x3f, 1, 0x20);
+	sd->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+
+	if (hdl->error) {
+		pr_err("Could not initialize controls\n");
+		return hdl->error;
+	}
+	v4l2_ctrl_cluster(2, &sd->contrast);
+	return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc_12a = {
+	.name = MODULE_NAME,
+	.init_controls = sd_init_controls_12a,
+	.config = sd_config,
+	.init = sd_init_12a,
+	.start = sd_start_12a,
+	.stopN = sd_stopN,
+	.pkt_scan = sd_pkt_scan,
+#if IS_ENABLED(CONFIG_INPUT)
+	.other_input = 1,
+#endif
+};
+static const struct sd_desc sd_desc_72a = {
+	.name = MODULE_NAME,
+	.init_controls = sd_init_controls_72a,
+	.config = sd_config,
+	.init = sd_init_72a,
+	.start = sd_start_72a,
+	.stopN = sd_stopN,
+	.pkt_scan = sd_pkt_scan,
+	.dq_callback = do_autogain,
+#if IS_ENABLED(CONFIG_INPUT)
+	.other_input = 1,
+#endif
+};
+static const struct sd_desc *sd_desc[2] = {
+	&sd_desc_12a,
+	&sd_desc_72a
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+	{USB_DEVICE(0x041e, 0x401a), .driver_info = Rev072A},
+	{USB_DEVICE(0x041e, 0x403b), .driver_info = Rev012A},
+	{USB_DEVICE(0x0458, 0x7004), .driver_info = Rev072A},
+	{USB_DEVICE(0x0461, 0x0815), .driver_info = Rev072A},
+	{USB_DEVICE(0x046d, 0x0928), .driver_info = Rev012A},
+	{USB_DEVICE(0x046d, 0x0929), .driver_info = Rev012A},
+	{USB_DEVICE(0x046d, 0x092a), .driver_info = Rev012A},
+	{USB_DEVICE(0x046d, 0x092b), .driver_info = Rev012A},
+	{USB_DEVICE(0x046d, 0x092c), .driver_info = Rev012A},
+	{USB_DEVICE(0x046d, 0x092d), .driver_info = Rev012A},
+	{USB_DEVICE(0x046d, 0x092e), .driver_info = Rev012A},
+	{USB_DEVICE(0x046d, 0x092f), .driver_info = Rev012A},
+	{USB_DEVICE(0x04fc, 0x0561), .driver_info = Rev072A},
+	{USB_DEVICE(0x060b, 0xa001), .driver_info = Rev072A},
+	{USB_DEVICE(0x10fd, 0x7e50), .driver_info = Rev072A},
+	{USB_DEVICE(0xabcd, 0xcdee), .driver_info = Rev072A},
+	{}
+};
+
+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[id->driver_info],
+				sizeof(struct sd),
+			       THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+	.name = MODULE_NAME,
+	.id_table = device_table,
+	.probe = sd_probe,
+	.disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+	.suspend = gspca_suspend,
+	.resume = gspca_resume,
+	.reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/usb/gspca/sq905.c b/drivers/media/usb/gspca/sq905.c
new file mode 100644
index 0000000..a7ae0ec
--- /dev/null
+++ b/drivers/media/usb/gspca/sq905.c
@@ -0,0 +1,439 @@
+/*
+ * SQ905 subdriver
+ *
+ * Copyright (C) 2008, 2009 Adam Baker and Theodore Kilgore
+ *
+ * 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
+ */
+
+/*
+ * History and Acknowledgments
+ *
+ * The original Linux driver for SQ905 based cameras was written by
+ * Marcell Lengyel and furter developed by many other contributors
+ * and is available from http://sourceforge.net/projects/sqcam/
+ *
+ * This driver takes advantage of the reverse engineering work done for
+ * that driver and for libgphoto2 but shares no code with them.
+ *
+ * This driver has used as a base the finepix driver and other gspca
+ * based drivers and may still contain code fragments taken from those
+ * drivers.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "sq905"
+
+#include <linux/workqueue.h>
+#include <linux/slab.h>
+#include "gspca.h"
+
+MODULE_AUTHOR("Adam Baker <linux@baker-net.org.uk>, "
+		"Theodore Kilgore <kilgota@auburn.edu>");
+MODULE_DESCRIPTION("GSPCA/SQ905 USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* Default timeouts, in ms */
+#define SQ905_CMD_TIMEOUT 500
+#define SQ905_DATA_TIMEOUT 1000
+
+/* Maximum transfer size to use. */
+#define SQ905_MAX_TRANSFER 0x8000
+#define FRAME_HEADER_LEN 64
+
+/* The known modes, or registers. These go in the "value" slot. */
+
+/* 00 is "none" obviously */
+
+#define SQ905_BULK_READ	0x03	/* precedes any bulk read */
+#define SQ905_COMMAND	0x06	/* precedes the command codes below */
+#define SQ905_PING	0x07	/* when reading an "idling" command */
+#define SQ905_READ_DONE 0xc0    /* ack bulk read completed */
+
+/* Any non-zero value in the bottom 2 bits of the 2nd byte of
+ * the ID appears to indicate the camera can do 640*480. If the
+ * LSB of that byte is set the image is just upside down, otherwise
+ * it is rotated 180 degrees. */
+#define SQ905_HIRES_MASK	0x00000300
+#define SQ905_ORIENTATION_MASK	0x00000100
+
+/* Some command codes. These go in the "index" slot. */
+
+#define SQ905_ID      0xf0	/* asks for model string */
+#define SQ905_CONFIG  0x20	/* gets photo alloc. table, not used here */
+#define SQ905_DATA    0x30	/* accesses photo data, not used here */
+#define SQ905_CLEAR   0xa0	/* clear everything */
+#define SQ905_CAPTURE_LOW  0x60	/* Starts capture at 160x120 */
+#define SQ905_CAPTURE_MED  0x61	/* Starts capture at 320x240 */
+#define SQ905_CAPTURE_HIGH 0x62	/* Starts capture at 640x480 (some cams only) */
+/* note that the capture command also controls the output dimensions */
+
+/* Structure to hold all of our device specific stuff */
+struct sd {
+	struct gspca_dev gspca_dev;	/* !! must be the first item */
+
+	/*
+	 * Driver stuff
+	 */
+	struct work_struct work_struct;
+	struct workqueue_struct *work_thread;
+};
+
+static struct v4l2_pix_format sq905_mode[] = {
+	{ 160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+		.bytesperline = 160,
+		.sizeimage = 160 * 120,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 0},
+	{ 320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+		.bytesperline = 320,
+		.sizeimage = 320 * 240,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 0},
+	{ 640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+		.bytesperline = 640,
+		.sizeimage = 640 * 480,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 0}
+};
+
+/*
+ * Send a command to the camera.
+ */
+static int sq905_command(struct gspca_dev *gspca_dev, u16 index)
+{
+	int ret;
+
+	gspca_dev->usb_buf[0] = '\0';
+	ret = usb_control_msg(gspca_dev->dev,
+			      usb_sndctrlpipe(gspca_dev->dev, 0),
+			      USB_REQ_SYNCH_FRAME,                /* request */
+			      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			      SQ905_COMMAND, index, gspca_dev->usb_buf, 1,
+			      SQ905_CMD_TIMEOUT);
+	if (ret < 0) {
+		pr_err("%s: usb_control_msg failed (%d)\n", __func__, ret);
+		return ret;
+	}
+
+	ret = usb_control_msg(gspca_dev->dev,
+			      usb_sndctrlpipe(gspca_dev->dev, 0),
+			      USB_REQ_SYNCH_FRAME,                /* request */
+			      USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			      SQ905_PING, 0, gspca_dev->usb_buf, 1,
+			      SQ905_CMD_TIMEOUT);
+	if (ret < 0) {
+		pr_err("%s: usb_control_msg failed 2 (%d)\n", __func__, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+/*
+ * Acknowledge the end of a frame - see warning on sq905_command.
+ */
+static int sq905_ack_frame(struct gspca_dev *gspca_dev)
+{
+	int ret;
+
+	gspca_dev->usb_buf[0] = '\0';
+	ret = usb_control_msg(gspca_dev->dev,
+			      usb_sndctrlpipe(gspca_dev->dev, 0),
+			      USB_REQ_SYNCH_FRAME,                /* request */
+			      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			      SQ905_READ_DONE, 0, gspca_dev->usb_buf, 1,
+			      SQ905_CMD_TIMEOUT);
+	if (ret < 0) {
+		pr_err("%s: usb_control_msg failed (%d)\n", __func__, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+/*
+ *  request and read a block of data - see warning on sq905_command.
+ */
+static int
+sq905_read_data(struct gspca_dev *gspca_dev, u8 *data, int size, int need_lock)
+{
+	int ret;
+	int act_len;
+
+	gspca_dev->usb_buf[0] = '\0';
+	if (need_lock)
+		mutex_lock(&gspca_dev->usb_lock);
+	ret = usb_control_msg(gspca_dev->dev,
+			      usb_sndctrlpipe(gspca_dev->dev, 0),
+			      USB_REQ_SYNCH_FRAME,                /* request */
+			      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			      SQ905_BULK_READ, size, gspca_dev->usb_buf,
+			      1, SQ905_CMD_TIMEOUT);
+	if (need_lock)
+		mutex_unlock(&gspca_dev->usb_lock);
+	if (ret < 0) {
+		pr_err("%s: usb_control_msg failed (%d)\n", __func__, ret);
+		return ret;
+	}
+	ret = usb_bulk_msg(gspca_dev->dev,
+			   usb_rcvbulkpipe(gspca_dev->dev, 0x81),
+			   data, size, &act_len, SQ905_DATA_TIMEOUT);
+
+	/* successful, it returns 0, otherwise  negative */
+	if (ret < 0 || act_len != size) {
+		pr_err("bulk read fail (%d) len %d/%d\n", ret, act_len, size);
+		return -EIO;
+	}
+	return 0;
+}
+
+/*
+ * This function is called as a workqueue function and runs whenever the camera
+ * is streaming data. Because it is a workqueue function it is allowed to sleep
+ * so we can use synchronous USB calls. To avoid possible collisions with other
+ * threads attempting to use gspca_dev->usb_buf we take the usb_lock when
+ * performing USB operations using it. In practice we don't really need this
+ * as the camera doesn't provide any controls.
+ */
+static void sq905_dostream(struct work_struct *work)
+{
+	struct sd *dev = container_of(work, struct sd, work_struct);
+	struct gspca_dev *gspca_dev = &dev->gspca_dev;
+	int bytes_left; /* bytes remaining in current frame. */
+	int data_len;   /* size to use for the next read. */
+	int header_read; /* true if we have already read the frame header. */
+	int packet_type;
+	int frame_sz;
+	int ret;
+	u8 *data;
+	u8 *buffer;
+
+	buffer = kmalloc(SQ905_MAX_TRANSFER, GFP_KERNEL | GFP_DMA);
+	if (!buffer) {
+		pr_err("Couldn't allocate USB buffer\n");
+		goto quit_stream;
+	}
+
+	frame_sz = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].sizeimage
+			+ FRAME_HEADER_LEN;
+
+	while (gspca_dev->present && gspca_dev->streaming) {
+#ifdef CONFIG_PM
+		if (gspca_dev->frozen)
+			break;
+#endif
+		/* request some data and then read it until we have
+		 * a complete frame. */
+		bytes_left = frame_sz;
+		header_read = 0;
+
+		/* Note we do not check for gspca_dev->streaming here, as
+		   we must finish reading an entire frame, otherwise the
+		   next time we stream we start reading in the middle of a
+		   frame. */
+		while (bytes_left > 0 && gspca_dev->present) {
+			data_len = bytes_left > SQ905_MAX_TRANSFER ?
+				SQ905_MAX_TRANSFER : bytes_left;
+			ret = sq905_read_data(gspca_dev, buffer, data_len, 1);
+			if (ret < 0)
+				goto quit_stream;
+			PDEBUG(D_PACK,
+				"Got %d bytes out of %d for frame",
+				data_len, bytes_left);
+			bytes_left -= data_len;
+			data = buffer;
+			if (!header_read) {
+				packet_type = FIRST_PACKET;
+				/* The first 64 bytes of each frame are
+				 * a header full of FF 00 bytes */
+				data += FRAME_HEADER_LEN;
+				data_len -= FRAME_HEADER_LEN;
+				header_read = 1;
+			} else if (bytes_left == 0) {
+				packet_type = LAST_PACKET;
+			} else {
+				packet_type = INTER_PACKET;
+			}
+			gspca_frame_add(gspca_dev, packet_type,
+					data, data_len);
+			/* If entire frame fits in one packet we still
+			   need to add a LAST_PACKET */
+			if (packet_type == FIRST_PACKET &&
+			    bytes_left == 0)
+				gspca_frame_add(gspca_dev, LAST_PACKET,
+						NULL, 0);
+		}
+		if (gspca_dev->present) {
+			/* acknowledge the frame */
+			mutex_lock(&gspca_dev->usb_lock);
+			ret = sq905_ack_frame(gspca_dev);
+			mutex_unlock(&gspca_dev->usb_lock);
+			if (ret < 0)
+				goto quit_stream;
+		}
+	}
+quit_stream:
+	if (gspca_dev->present) {
+		mutex_lock(&gspca_dev->usb_lock);
+		sq905_command(gspca_dev, SQ905_CLEAR);
+		mutex_unlock(&gspca_dev->usb_lock);
+	}
+	kfree(buffer);
+}
+
+/* This function is called at probe time just before sd_init */
+static int sd_config(struct gspca_dev *gspca_dev,
+		const struct usb_device_id *id)
+{
+	struct cam *cam = &gspca_dev->cam;
+	struct sd *dev = (struct sd *) gspca_dev;
+
+	/* We don't use the buffer gspca allocates so make it small. */
+	cam->bulk = 1;
+	cam->bulk_size = 64;
+
+	INIT_WORK(&dev->work_struct, sq905_dostream);
+
+	return 0;
+}
+
+/* called on streamoff with alt==0 and on disconnect */
+/* the usb_lock is held at entry - restore on exit */
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+	struct sd *dev = (struct sd *) gspca_dev;
+
+	/* wait for the work queue to terminate */
+	mutex_unlock(&gspca_dev->usb_lock);
+	/* This waits for sq905_dostream to finish */
+	destroy_workqueue(dev->work_thread);
+	dev->work_thread = NULL;
+	mutex_lock(&gspca_dev->usb_lock);
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+	u32 ident;
+	int ret;
+
+	/* connect to the camera and read
+	 * the model ID and process that and put it away.
+	 */
+	ret = sq905_command(gspca_dev, SQ905_CLEAR);
+	if (ret < 0)
+		return ret;
+	ret = sq905_command(gspca_dev, SQ905_ID);
+	if (ret < 0)
+		return ret;
+	ret = sq905_read_data(gspca_dev, gspca_dev->usb_buf, 4, 0);
+	if (ret < 0)
+		return ret;
+	/* usb_buf is allocated with kmalloc so is aligned.
+	 * Camera model number is the right way round if we assume this
+	 * reverse engineered ID is supposed to be big endian. */
+	ident = be32_to_cpup((__be32 *)gspca_dev->usb_buf);
+	ret = sq905_command(gspca_dev, SQ905_CLEAR);
+	if (ret < 0)
+		return ret;
+	PDEBUG(D_CONF, "SQ905 camera ID %08x detected", ident);
+	gspca_dev->cam.cam_mode = sq905_mode;
+	gspca_dev->cam.nmodes = ARRAY_SIZE(sq905_mode);
+	if (!(ident & SQ905_HIRES_MASK))
+		gspca_dev->cam.nmodes--;
+
+	if (ident & SQ905_ORIENTATION_MASK)
+		gspca_dev->cam.input_flags = V4L2_IN_ST_VFLIP;
+	else
+		gspca_dev->cam.input_flags = V4L2_IN_ST_VFLIP |
+					     V4L2_IN_ST_HFLIP;
+	return 0;
+}
+
+/* Set up for getting frames. */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+	struct sd *dev = (struct sd *) gspca_dev;
+	int ret;
+
+	/* "Open the shutter" and set size, to start capture */
+	switch (gspca_dev->curr_mode) {
+	default:
+/*	case 2: */
+		PDEBUG(D_STREAM, "Start streaming at high resolution");
+		ret = sq905_command(&dev->gspca_dev, SQ905_CAPTURE_HIGH);
+		break;
+	case 1:
+		PDEBUG(D_STREAM, "Start streaming at medium resolution");
+		ret = sq905_command(&dev->gspca_dev, SQ905_CAPTURE_MED);
+		break;
+	case 0:
+		PDEBUG(D_STREAM, "Start streaming at low resolution");
+		ret = sq905_command(&dev->gspca_dev, SQ905_CAPTURE_LOW);
+	}
+
+	if (ret < 0) {
+		PERR("Start streaming command failed");
+		return ret;
+	}
+	/* Start the workqueue function to do the streaming */
+	dev->work_thread = create_singlethread_workqueue(MODULE_NAME);
+	queue_work(dev->work_thread, &dev->work_struct);
+
+	return 0;
+}
+
+/* Table of supported USB devices */
+static const struct usb_device_id device_table[] = {
+	{USB_DEVICE(0x2770, 0x9120)},
+	{}
+};
+
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+	.name   = MODULE_NAME,
+	.config = sd_config,
+	.init   = sd_init,
+	.start  = sd_start,
+	.stop0  = sd_stop0,
+};
+
+/* -- 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,
+#ifdef CONFIG_PM
+	.suspend = gspca_suspend,
+	.resume  = gspca_resume,
+	.reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/usb/gspca/sq905c.c b/drivers/media/usb/gspca/sq905c.c
new file mode 100644
index 0000000..aa21edc
--- /dev/null
+++ b/drivers/media/usb/gspca/sq905c.c
@@ -0,0 +1,342 @@
+/*
+ * SQ905C subdriver
+ *
+ * Copyright (C) 2009 Theodore Kilgore
+ *
+ * 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
+ */
+
+/*
+ *
+ * This driver uses work done in
+ * libgphoto2/camlibs/digigr8, Copyright (C) Theodore Kilgore.
+ *
+ * This driver has also used as a base the sq905c driver
+ * and may contain code fragments from it.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "sq905c"
+
+#include <linux/workqueue.h>
+#include <linux/slab.h>
+#include "gspca.h"
+
+MODULE_AUTHOR("Theodore Kilgore <kilgota@auburn.edu>");
+MODULE_DESCRIPTION("GSPCA/SQ905C USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* Default timeouts, in ms */
+#define SQ905C_CMD_TIMEOUT 500
+#define SQ905C_DATA_TIMEOUT 1000
+
+/* Maximum transfer size to use. */
+#define SQ905C_MAX_TRANSFER 0x8000
+
+#define FRAME_HEADER_LEN 0x50
+
+/* Commands. These go in the "value" slot. */
+#define SQ905C_CLEAR   0xa0		/* clear everything */
+#define SQ905C_GET_ID  0x14f4		/* Read version number */
+#define SQ905C_CAPTURE_LOW 0xa040	/* Starts capture at 160x120 */
+#define SQ905C_CAPTURE_MED 0x1440	/* Starts capture at 320x240 */
+#define SQ905C_CAPTURE_HI 0x2840	/* Starts capture at 320x240 */
+
+/* For capture, this must go in the "index" slot. */
+#define SQ905C_CAPTURE_INDEX 0x110f
+
+/* Structure to hold all of our device specific stuff */
+struct sd {
+	struct gspca_dev gspca_dev;	/* !! must be the first item */
+	const struct v4l2_pix_format *cap_mode;
+	/* Driver stuff */
+	struct work_struct work_struct;
+	struct workqueue_struct *work_thread;
+};
+
+/*
+ * Most of these cameras will do 640x480 and 320x240. 160x120 works
+ * in theory but gives very poor output. Therefore, not supported.
+ * The 0x2770:0x9050 cameras have max resolution of 320x240.
+ */
+static struct v4l2_pix_format sq905c_mode[] = {
+	{ 320, 240, V4L2_PIX_FMT_SQ905C, V4L2_FIELD_NONE,
+		.bytesperline = 320,
+		.sizeimage = 320 * 240,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 0},
+	{ 640, 480, V4L2_PIX_FMT_SQ905C, V4L2_FIELD_NONE,
+		.bytesperline = 640,
+		.sizeimage = 640 * 480,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 0}
+};
+
+/* Send a command to the camera. */
+static int sq905c_command(struct gspca_dev *gspca_dev, u16 command, u16 index)
+{
+	int ret;
+
+	ret = usb_control_msg(gspca_dev->dev,
+			      usb_sndctrlpipe(gspca_dev->dev, 0),
+			      USB_REQ_SYNCH_FRAME,                /* request */
+			      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			      command, index, NULL, 0,
+			      SQ905C_CMD_TIMEOUT);
+	if (ret < 0) {
+		pr_err("%s: usb_control_msg failed (%d)\n", __func__, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int sq905c_read(struct gspca_dev *gspca_dev, u16 command, u16 index,
+		       int size)
+{
+	int ret;
+
+	ret = usb_control_msg(gspca_dev->dev,
+			      usb_rcvctrlpipe(gspca_dev->dev, 0),
+			      USB_REQ_SYNCH_FRAME,		/* request */
+			      USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			      command, index, gspca_dev->usb_buf, size,
+			      SQ905C_CMD_TIMEOUT);
+	if (ret < 0) {
+		pr_err("%s: usb_control_msg failed (%d)\n", __func__, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+/*
+ * This function is called as a workqueue function and runs whenever the camera
+ * is streaming data. Because it is a workqueue function it is allowed to sleep
+ * so we can use synchronous USB calls. To avoid possible collisions with other
+ * threads attempting to use gspca_dev->usb_buf we take the usb_lock when
+ * performing USB operations using it. In practice we don't really need this
+ * as the camera doesn't provide any controls.
+ */
+static void sq905c_dostream(struct work_struct *work)
+{
+	struct sd *dev = container_of(work, struct sd, work_struct);
+	struct gspca_dev *gspca_dev = &dev->gspca_dev;
+	int bytes_left; /* bytes remaining in current frame. */
+	int data_len;   /* size to use for the next read. */
+	int act_len;
+	int packet_type;
+	int ret;
+	u8 *buffer;
+
+	buffer = kmalloc(SQ905C_MAX_TRANSFER, GFP_KERNEL | GFP_DMA);
+	if (!buffer) {
+		pr_err("Couldn't allocate USB buffer\n");
+		goto quit_stream;
+	}
+
+	while (gspca_dev->present && gspca_dev->streaming) {
+#ifdef CONFIG_PM
+		if (gspca_dev->frozen)
+			break;
+#endif
+		/* Request the header, which tells the size to download */
+		ret = usb_bulk_msg(gspca_dev->dev,
+				usb_rcvbulkpipe(gspca_dev->dev, 0x81),
+				buffer, FRAME_HEADER_LEN, &act_len,
+				SQ905C_DATA_TIMEOUT);
+		PDEBUG(D_STREAM,
+			"Got %d bytes out of %d for header",
+			act_len, FRAME_HEADER_LEN);
+		if (ret < 0 || act_len < FRAME_HEADER_LEN)
+			goto quit_stream;
+		/* size is read from 4 bytes starting 0x40, little endian */
+		bytes_left = buffer[0x40]|(buffer[0x41]<<8)|(buffer[0x42]<<16)
+					|(buffer[0x43]<<24);
+		PDEBUG(D_STREAM, "bytes_left = 0x%x", bytes_left);
+		/* We keep the header. It has other information, too. */
+		packet_type = FIRST_PACKET;
+		gspca_frame_add(gspca_dev, packet_type,
+				buffer, FRAME_HEADER_LEN);
+		while (bytes_left > 0 && gspca_dev->present) {
+			data_len = bytes_left > SQ905C_MAX_TRANSFER ?
+				SQ905C_MAX_TRANSFER : bytes_left;
+			ret = usb_bulk_msg(gspca_dev->dev,
+				usb_rcvbulkpipe(gspca_dev->dev, 0x81),
+				buffer, data_len, &act_len,
+				SQ905C_DATA_TIMEOUT);
+			if (ret < 0 || act_len < data_len)
+				goto quit_stream;
+			PDEBUG(D_STREAM,
+				"Got %d bytes out of %d for frame",
+				data_len, bytes_left);
+			bytes_left -= data_len;
+			if (bytes_left == 0)
+				packet_type = LAST_PACKET;
+			else
+				packet_type = INTER_PACKET;
+			gspca_frame_add(gspca_dev, packet_type,
+					buffer, data_len);
+		}
+	}
+quit_stream:
+	if (gspca_dev->present) {
+		mutex_lock(&gspca_dev->usb_lock);
+		sq905c_command(gspca_dev, SQ905C_CLEAR, 0);
+		mutex_unlock(&gspca_dev->usb_lock);
+	}
+	kfree(buffer);
+}
+
+/* This function is called at probe time just before sd_init */
+static int sd_config(struct gspca_dev *gspca_dev,
+		const struct usb_device_id *id)
+{
+	struct cam *cam = &gspca_dev->cam;
+	struct sd *dev = (struct sd *) gspca_dev;
+	int ret;
+
+	PDEBUG(D_PROBE,
+		"SQ9050 camera detected"
+		" (vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct);
+
+	ret = sq905c_command(gspca_dev, SQ905C_GET_ID, 0);
+	if (ret < 0) {
+		PERR("Get version command failed");
+		return ret;
+	}
+
+	ret = sq905c_read(gspca_dev, 0xf5, 0, 20);
+	if (ret < 0) {
+		PERR("Reading version command failed");
+		return ret;
+	}
+	/* Note we leave out the usb id and the manufacturing date */
+	PDEBUG(D_PROBE,
+	       "SQ9050 ID string: %02x - %*ph",
+		gspca_dev->usb_buf[3], 6, gspca_dev->usb_buf + 14);
+
+	cam->cam_mode = sq905c_mode;
+	cam->nmodes = 2;
+	if (gspca_dev->usb_buf[15] == 0)
+		cam->nmodes = 1;
+	/* We don't use the buffer gspca allocates so make it small. */
+	cam->bulk_size = 32;
+	cam->bulk = 1;
+	INIT_WORK(&dev->work_struct, sq905c_dostream);
+	return 0;
+}
+
+/* called on streamoff with alt==0 and on disconnect */
+/* the usb_lock is held at entry - restore on exit */
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+	struct sd *dev = (struct sd *) gspca_dev;
+
+	/* wait for the work queue to terminate */
+	mutex_unlock(&gspca_dev->usb_lock);
+	/* This waits for sq905c_dostream to finish */
+	destroy_workqueue(dev->work_thread);
+	dev->work_thread = NULL;
+	mutex_lock(&gspca_dev->usb_lock);
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+	int ret;
+
+	/* connect to the camera and reset it. */
+	ret = sq905c_command(gspca_dev, SQ905C_CLEAR, 0);
+	return ret;
+}
+
+/* Set up for getting frames. */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+	struct sd *dev = (struct sd *) gspca_dev;
+	int ret;
+
+	dev->cap_mode = gspca_dev->cam.cam_mode;
+	/* "Open the shutter" and set size, to start capture */
+	switch (gspca_dev->pixfmt.width) {
+	case 640:
+		PDEBUG(D_STREAM, "Start streaming at high resolution");
+		dev->cap_mode++;
+		ret = sq905c_command(gspca_dev, SQ905C_CAPTURE_HI,
+						SQ905C_CAPTURE_INDEX);
+		break;
+	default: /* 320 */
+	PDEBUG(D_STREAM, "Start streaming at medium resolution");
+		ret = sq905c_command(gspca_dev, SQ905C_CAPTURE_MED,
+						SQ905C_CAPTURE_INDEX);
+	}
+
+	if (ret < 0) {
+		PERR("Start streaming command failed");
+		return ret;
+	}
+	/* Start the workqueue function to do the streaming */
+	dev->work_thread = create_singlethread_workqueue(MODULE_NAME);
+	queue_work(dev->work_thread, &dev->work_struct);
+
+	return 0;
+}
+
+/* Table of supported USB devices */
+static const struct usb_device_id device_table[] = {
+	{USB_DEVICE(0x2770, 0x905c)},
+	{USB_DEVICE(0x2770, 0x9050)},
+	{USB_DEVICE(0x2770, 0x9051)},
+	{USB_DEVICE(0x2770, 0x9052)},
+	{USB_DEVICE(0x2770, 0x913d)},
+	{}
+};
+
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+	.name   = MODULE_NAME,
+	.config = sd_config,
+	.init   = sd_init,
+	.start  = sd_start,
+	.stop0  = sd_stop0,
+};
+
+/* -- 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,
+#ifdef CONFIG_PM
+	.suspend = gspca_suspend,
+	.resume  = gspca_resume,
+	.reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/usb/gspca/sq930x.c b/drivers/media/usb/gspca/sq930x.c
new file mode 100644
index 0000000..e274cf1
--- /dev/null
+++ b/drivers/media/usb/gspca/sq930x.c
@@ -0,0 +1,1163 @@
+/*
+ * SQ930x subdriver
+ *
+ * Copyright (C) 2010 Jean-François Moine <http://moinejf.free.fr>
+ * Copyright (C) 2006 -2008 Gerard Klaver <gerard at gkall dot hobby dot nl>
+ * Copyright (C) 2007 Sam Revitch <samr7@cs.washington.edu>
+ *
+ * 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "sq930x"
+
+#include "gspca.h"
+
+MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>\n"
+		"Gerard Klaver <gerard at gkall dot hobby dot nl\n"
+		"Sam Revitch <samr7@cs.washington.edu>");
+MODULE_DESCRIPTION("GSPCA/SQ930x USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* Structure to hold all of our device specific stuff */
+struct sd {
+	struct gspca_dev gspca_dev;	/* !! must be the first item */
+
+	struct { /* exposure/gain control cluster */
+		struct v4l2_ctrl *exposure;
+		struct v4l2_ctrl *gain;
+	};
+
+	u8 do_ctrl;
+	u8 gpio[2];
+	u8 sensor;
+	u8 type;
+#define Generic 0
+#define Creative_live_motion 1
+};
+enum sensors {
+	SENSOR_ICX098BQ,
+	SENSOR_LZ24BP,
+	SENSOR_MI0360,
+	SENSOR_MT9V111,		/* = MI360SOC */
+	SENSOR_OV7660,
+	SENSOR_OV9630,
+};
+
+static struct v4l2_pix_format vga_mode[] = {
+	{320, 240, V4L2_PIX_FMT_SRGGB8, V4L2_FIELD_NONE,
+		.bytesperline = 320,
+		.sizeimage = 320 * 240,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 0},
+	{640, 480, V4L2_PIX_FMT_SRGGB8, V4L2_FIELD_NONE,
+		.bytesperline = 640,
+		.sizeimage = 640 * 480,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 1},
+};
+
+/* sq930x registers */
+#define SQ930_CTRL_UCBUS_IO	0x0001
+#define SQ930_CTRL_I2C_IO	0x0002
+#define SQ930_CTRL_GPIO		0x0005
+#define SQ930_CTRL_CAP_START	0x0010
+#define SQ930_CTRL_CAP_STOP	0x0011
+#define SQ930_CTRL_SET_EXPOSURE 0x001d
+#define SQ930_CTRL_RESET	0x001e
+#define SQ930_CTRL_GET_DEV_INFO 0x001f
+
+/* gpio 1 (8..15) */
+#define SQ930_GPIO_DFL_I2C_SDA	0x0001
+#define SQ930_GPIO_DFL_I2C_SCL	0x0002
+#define SQ930_GPIO_RSTBAR	0x0004
+#define SQ930_GPIO_EXTRA1	0x0040
+#define SQ930_GPIO_EXTRA2	0x0080
+/* gpio 3 (24..31) */
+#define SQ930_GPIO_POWER	0x0200
+#define SQ930_GPIO_DFL_LED	0x1000
+
+struct ucbus_write_cmd {
+	u16	bw_addr;
+	u8	bw_data;
+};
+struct i2c_write_cmd {
+	u8	reg;
+	u16	val;
+};
+
+static const struct ucbus_write_cmd icx098bq_start_0[] = {
+	{0x0354, 0x00}, {0x03fa, 0x00}, {0xf800, 0x02}, {0xf801, 0xce},
+	{0xf802, 0xc1}, {0xf804, 0x00}, {0xf808, 0x00}, {0xf809, 0x0e},
+	{0xf80a, 0x01}, {0xf80b, 0xee}, {0xf807, 0x60}, {0xf80c, 0x02},
+	{0xf80d, 0xf0}, {0xf80e, 0x03}, {0xf80f, 0x0a}, {0xf81c, 0x02},
+	{0xf81d, 0xf0}, {0xf81e, 0x03}, {0xf81f, 0x0a}, {0xf83a, 0x00},
+	{0xf83b, 0x10}, {0xf83c, 0x00}, {0xf83d, 0x4e}, {0xf810, 0x04},
+	{0xf811, 0x00}, {0xf812, 0x02}, {0xf813, 0x10}, {0xf803, 0x00},
+	{0xf814, 0x01}, {0xf815, 0x18}, {0xf816, 0x00}, {0xf817, 0x48},
+	{0xf818, 0x00}, {0xf819, 0x25}, {0xf81a, 0x00}, {0xf81b, 0x3c},
+	{0xf82f, 0x03}, {0xf820, 0xff}, {0xf821, 0x0d}, {0xf822, 0xff},
+	{0xf823, 0x07}, {0xf824, 0xff}, {0xf825, 0x03}, {0xf826, 0xff},
+	{0xf827, 0x06}, {0xf828, 0xff}, {0xf829, 0x03}, {0xf82a, 0xff},
+	{0xf82b, 0x0c}, {0xf82c, 0xfd}, {0xf82d, 0x01}, {0xf82e, 0x00},
+	{0xf830, 0x00}, {0xf831, 0x47}, {0xf832, 0x00}, {0xf833, 0x00},
+	{0xf850, 0x00}, {0xf851, 0x00}, {0xf852, 0x00}, {0xf853, 0x24},
+	{0xf854, 0x00}, {0xf855, 0x18}, {0xf856, 0x00}, {0xf857, 0x3c},
+	{0xf858, 0x00}, {0xf859, 0x0c}, {0xf85a, 0x00}, {0xf85b, 0x30},
+	{0xf85c, 0x00}, {0xf85d, 0x0c}, {0xf85e, 0x00}, {0xf85f, 0x30},
+	{0xf860, 0x00}, {0xf861, 0x48}, {0xf862, 0x01}, {0xf863, 0xdc},
+	{0xf864, 0xff}, {0xf865, 0x98}, {0xf866, 0xff}, {0xf867, 0xc0},
+	{0xf868, 0xff}, {0xf869, 0x70}, {0xf86c, 0xff}, {0xf86d, 0x00},
+	{0xf86a, 0xff}, {0xf86b, 0x48}, {0xf86e, 0xff}, {0xf86f, 0x00},
+	{0xf870, 0x01}, {0xf871, 0xdb}, {0xf872, 0x01}, {0xf873, 0xfa},
+	{0xf874, 0x01}, {0xf875, 0xdb}, {0xf876, 0x01}, {0xf877, 0xfa},
+	{0xf878, 0x0f}, {0xf879, 0x0f}, {0xf87a, 0xff}, {0xf87b, 0xff},
+	{0xf800, 0x03}
+};
+static const struct ucbus_write_cmd icx098bq_start_1[] = {
+	{0xf5f0, 0x00}, {0xf5f1, 0xcd}, {0xf5f2, 0x80}, {0xf5f3, 0x80},
+	{0xf5f4, 0xc0},
+	{0xf5f0, 0x49}, {0xf5f1, 0xcd}, {0xf5f2, 0x80}, {0xf5f3, 0x80},
+	{0xf5f4, 0xc0},
+	{0xf5fa, 0x00}, {0xf5f6, 0x00}, {0xf5f7, 0x00}, {0xf5f8, 0x00},
+	{0xf5f9, 0x00}
+};
+
+static const struct ucbus_write_cmd icx098bq_start_2[] = {
+	{0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x82}, {0xf806, 0x00},
+	{0xf807, 0x7f}, {0xf800, 0x03},
+	{0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x40}, {0xf806, 0x00},
+	{0xf807, 0x7f}, {0xf800, 0x03},
+	{0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0xcf}, {0xf806, 0xd0},
+	{0xf807, 0x7f}, {0xf800, 0x03},
+	{0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x00}, {0xf806, 0x00},
+	{0xf807, 0x7f}, {0xf800, 0x03}
+};
+
+static const struct ucbus_write_cmd lz24bp_start_0[] = {
+	{0x0354, 0x00}, {0x03fa, 0x00}, {0xf800, 0x02}, {0xf801, 0xbe},
+	{0xf802, 0xc6}, {0xf804, 0x00}, {0xf808, 0x00}, {0xf809, 0x06},
+	{0xf80a, 0x01}, {0xf80b, 0xfe}, {0xf807, 0x84}, {0xf80c, 0x02},
+	{0xf80d, 0xf7}, {0xf80e, 0x03}, {0xf80f, 0x0b}, {0xf81c, 0x00},
+	{0xf81d, 0x49}, {0xf81e, 0x03}, {0xf81f, 0x0b}, {0xf83a, 0x00},
+	{0xf83b, 0x01}, {0xf83c, 0x00}, {0xf83d, 0x6b}, {0xf810, 0x03},
+	{0xf811, 0x10}, {0xf812, 0x02}, {0xf813, 0x6f}, {0xf803, 0x00},
+	{0xf814, 0x00}, {0xf815, 0x44}, {0xf816, 0x00}, {0xf817, 0x48},
+	{0xf818, 0x00}, {0xf819, 0x25}, {0xf81a, 0x00}, {0xf81b, 0x3c},
+	{0xf82f, 0x03}, {0xf820, 0xff}, {0xf821, 0x0d}, {0xf822, 0xff},
+	{0xf823, 0x07}, {0xf824, 0xfd}, {0xf825, 0x07}, {0xf826, 0xf0},
+	{0xf827, 0x0c}, {0xf828, 0xff}, {0xf829, 0x03}, {0xf82a, 0xff},
+	{0xf82b, 0x0c}, {0xf82c, 0xfc}, {0xf82d, 0x01}, {0xf82e, 0x00},
+	{0xf830, 0x00}, {0xf831, 0x47}, {0xf832, 0x00}, {0xf833, 0x00},
+	{0xf850, 0x00}, {0xf851, 0x00}, {0xf852, 0x00}, {0xf853, 0x24},
+	{0xf854, 0x00}, {0xf855, 0x0c}, {0xf856, 0x00}, {0xf857, 0x30},
+	{0xf858, 0x00}, {0xf859, 0x18}, {0xf85a, 0x00}, {0xf85b, 0x3c},
+	{0xf85c, 0x00}, {0xf85d, 0x18}, {0xf85e, 0x00}, {0xf85f, 0x3c},
+	{0xf860, 0xff}, {0xf861, 0x37}, {0xf862, 0xff}, {0xf863, 0x1d},
+	{0xf864, 0xff}, {0xf865, 0x98}, {0xf866, 0xff}, {0xf867, 0xc0},
+	{0xf868, 0x00}, {0xf869, 0x37}, {0xf86c, 0x02}, {0xf86d, 0x1d},
+	{0xf86a, 0x00}, {0xf86b, 0x37}, {0xf86e, 0x02}, {0xf86f, 0x1d},
+	{0xf870, 0x01}, {0xf871, 0xc6}, {0xf872, 0x02}, {0xf873, 0x04},
+	{0xf874, 0x01}, {0xf875, 0xc6}, {0xf876, 0x02}, {0xf877, 0x04},
+	{0xf878, 0x0f}, {0xf879, 0x0f}, {0xf87a, 0xff}, {0xf87b, 0xff},
+	{0xf800, 0x03}
+};
+static const struct ucbus_write_cmd lz24bp_start_1_gen[] = {
+	{0xf5f0, 0x00}, {0xf5f1, 0xff}, {0xf5f2, 0x80}, {0xf5f3, 0x80},
+	{0xf5f4, 0xb3},
+	{0xf5f0, 0x40}, {0xf5f1, 0xff}, {0xf5f2, 0x80}, {0xf5f3, 0x80},
+	{0xf5f4, 0xb3},
+	{0xf5fa, 0x00}, {0xf5f6, 0x00}, {0xf5f7, 0x00}, {0xf5f8, 0x00},
+	{0xf5f9, 0x00}
+};
+
+static const struct ucbus_write_cmd lz24bp_start_1_clm[] = {
+	{0xf5f0, 0x00}, {0xf5f1, 0xff}, {0xf5f2, 0x88}, {0xf5f3, 0x88},
+	{0xf5f4, 0xc0},
+	{0xf5f0, 0x40}, {0xf5f1, 0xff}, {0xf5f2, 0x88}, {0xf5f3, 0x88},
+	{0xf5f4, 0xc0},
+	{0xf5fa, 0x00}, {0xf5f6, 0x00}, {0xf5f7, 0x00}, {0xf5f8, 0x00},
+	{0xf5f9, 0x00}
+};
+
+static const struct ucbus_write_cmd lz24bp_start_2[] = {
+	{0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x80}, {0xf806, 0x00},
+	{0xf807, 0x7f}, {0xf800, 0x03},
+	{0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x4e}, {0xf806, 0x00},
+	{0xf807, 0x7f}, {0xf800, 0x03},
+	{0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0xc0}, {0xf806, 0x48},
+	{0xf807, 0x7f}, {0xf800, 0x03},
+	{0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x00}, {0xf806, 0x00},
+	{0xf807, 0x7f}, {0xf800, 0x03}
+};
+
+static const struct ucbus_write_cmd mi0360_start_0[] = {
+	{0x0354, 0x00}, {0x03fa, 0x00}, {0xf332, 0xcc}, {0xf333, 0xcc},
+	{0xf334, 0xcc}, {0xf335, 0xcc}, {0xf33f, 0x00}
+};
+static const struct i2c_write_cmd mi0360_init_23[] = {
+	{0x30, 0x0040},		/* reserved - def 0x0005 */
+	{0x31, 0x0000},		/* reserved - def 0x002a */
+	{0x34, 0x0100},		/* reserved - def 0x0100 */
+	{0x3d, 0x068f},		/* reserved - def 0x068f */
+};
+static const struct i2c_write_cmd mi0360_init_24[] = {
+	{0x03, 0x01e5},		/* window height */
+	{0x04, 0x0285},		/* window width */
+};
+static const struct i2c_write_cmd mi0360_init_25[] = {
+	{0x35, 0x0020},		/* global gain */
+	{0x2b, 0x0020},		/* green1 gain */
+	{0x2c, 0x002a},		/* blue gain */
+	{0x2d, 0x0028},		/* red gain */
+	{0x2e, 0x0020},		/* green2 gain */
+};
+static const struct ucbus_write_cmd mi0360_start_1[] = {
+	{0xf5f0, 0x11}, {0xf5f1, 0x99}, {0xf5f2, 0x80}, {0xf5f3, 0x80},
+	{0xf5f4, 0xa6},
+	{0xf5f0, 0x51}, {0xf5f1, 0x99}, {0xf5f2, 0x80}, {0xf5f3, 0x80},
+	{0xf5f4, 0xa6},
+	{0xf5fa, 0x00}, {0xf5f6, 0x00}, {0xf5f7, 0x00}, {0xf5f8, 0x00},
+	{0xf5f9, 0x00}
+};
+static const struct i2c_write_cmd mi0360_start_2[] = {
+	{0x62, 0x041d},		/* reserved - def 0x0418 */
+};
+static const struct i2c_write_cmd mi0360_start_3[] = {
+	{0x05, 0x007b},		/* horiz blanking */
+};
+static const struct i2c_write_cmd mi0360_start_4[] = {
+	{0x05, 0x03f5},		/* horiz blanking */
+};
+
+static const struct i2c_write_cmd mt9v111_init_0[] = {
+	{0x01, 0x0001},		/* select IFP/SOC registers */
+	{0x06, 0x300c},		/* operating mode control */
+	{0x08, 0xcc00},		/* output format control (RGB) */
+	{0x01, 0x0004},		/* select sensor core registers */
+};
+static const struct i2c_write_cmd mt9v111_init_1[] = {
+	{0x03, 0x01e5},		/* window height */
+	{0x04, 0x0285},		/* window width */
+};
+static const struct i2c_write_cmd mt9v111_init_2[] = {
+	{0x30, 0x7800},
+	{0x31, 0x0000},
+	{0x07, 0x3002},		/* output control */
+	{0x35, 0x0020},		/* global gain */
+	{0x2b, 0x0020},		/* green1 gain */
+	{0x2c, 0x0020},		/* blue gain */
+	{0x2d, 0x0020},		/* red gain */
+	{0x2e, 0x0020},		/* green2 gain */
+};
+static const struct ucbus_write_cmd mt9v111_start_1[] = {
+	{0xf5f0, 0x11}, {0xf5f1, 0x96}, {0xf5f2, 0x80}, {0xf5f3, 0x80},
+	{0xf5f4, 0xaa},
+	{0xf5f0, 0x51}, {0xf5f1, 0x96}, {0xf5f2, 0x80}, {0xf5f3, 0x80},
+	{0xf5f4, 0xaa},
+	{0xf5fa, 0x00}, {0xf5f6, 0x0a}, {0xf5f7, 0x0a}, {0xf5f8, 0x0a},
+	{0xf5f9, 0x0a}
+};
+static const struct i2c_write_cmd mt9v111_init_3[] = {
+	{0x62, 0x0405},
+};
+static const struct i2c_write_cmd mt9v111_init_4[] = {
+/*	{0x05, 0x00ce}, */
+	{0x05, 0x005d},		/* horizontal blanking */
+};
+
+static const struct ucbus_write_cmd ov7660_start_0[] = {
+	{0x0354, 0x00}, {0x03fa, 0x00}, {0xf332, 0x00}, {0xf333, 0xc0},
+	{0xf334, 0x39}, {0xf335, 0xe7}, {0xf33f, 0x03}
+};
+
+static const struct ucbus_write_cmd ov9630_start_0[] = {
+	{0x0354, 0x00}, {0x03fa, 0x00}, {0xf332, 0x00}, {0xf333, 0x00},
+	{0xf334, 0x3e}, {0xf335, 0xf8}, {0xf33f, 0x03}
+};
+
+/* start parameters indexed by [sensor][mode] */
+static const struct cap_s {
+	u8	cc_sizeid;
+	u8	cc_bytes[32];
+} capconfig[4][2] = {
+	[SENSOR_ICX098BQ] = {
+		{2,				/* Bayer 320x240 */
+		  {0x05, 0x1f, 0x20, 0x0e, 0x00, 0x9f, 0x02, 0xee,
+		   0x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
+		   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0,
+		   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
+		{4,				/* Bayer 640x480 */
+		  {0x01, 0x1f, 0x20, 0x0e, 0x00, 0x9f, 0x02, 0xee,
+		   0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
+		   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
+	},
+	[SENSOR_LZ24BP] = {
+		{2,				/* Bayer 320x240 */
+		  {0x05, 0x22, 0x20, 0x0e, 0x00, 0xa2, 0x02, 0xee,
+		   0x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
+		   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
+		{4,				/* Bayer 640x480 */
+		  {0x01, 0x22, 0x20, 0x0e, 0x00, 0xa2, 0x02, 0xee,
+		   0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
+		   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
+	},
+	[SENSOR_MI0360] = {
+		{2,				/* Bayer 320x240 */
+		  {0x05, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1,
+		   0x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
+		   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
+		{4,				/* Bayer 640x480 */
+		  {0x01, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1,
+		   0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
+		   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
+	},
+	[SENSOR_MT9V111] = {
+		{2,				/* Bayer 320x240 */
+		  {0x05, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1,
+		   0x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
+		   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
+		{4,				/* Bayer 640x480 */
+		  {0x01, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1,
+		   0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
+		   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
+	},
+};
+
+struct sensor_s {
+	const char *name;
+	u8 i2c_addr;
+	u8 i2c_dum;
+	u8 gpio[5];
+	u8 cmd_len;
+	const struct ucbus_write_cmd *cmd;
+};
+
+static const struct sensor_s sensor_tb[] = {
+	[SENSOR_ICX098BQ] = {
+		"icx098bp",
+		0x00, 0x00,
+		{0,
+		 SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL,
+		 SQ930_GPIO_DFL_I2C_SDA,
+		 0,
+		 SQ930_GPIO_RSTBAR
+		},
+		8, icx098bq_start_0
+	    },
+	[SENSOR_LZ24BP] = {
+		"lz24bp",
+		0x00, 0x00,
+		{0,
+		 SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL,
+		 SQ930_GPIO_DFL_I2C_SDA,
+		 0,
+		 SQ930_GPIO_RSTBAR
+		},
+		8, lz24bp_start_0
+	    },
+	[SENSOR_MI0360] = {
+		"mi0360",
+		0x5d, 0x80,
+		{SQ930_GPIO_RSTBAR,
+		 SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL,
+		 SQ930_GPIO_DFL_I2C_SDA,
+		 0,
+		 0
+		},
+		7, mi0360_start_0
+	    },
+	[SENSOR_MT9V111] = {
+		"mt9v111",
+		0x5c, 0x7f,
+		{SQ930_GPIO_RSTBAR,
+		 SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL,
+		 SQ930_GPIO_DFL_I2C_SDA,
+		 0,
+		 0
+		},
+		7, mi0360_start_0
+	    },
+	[SENSOR_OV7660] = {
+		"ov7660",
+		0x21, 0x00,
+		{0,
+		 SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL,
+		 SQ930_GPIO_DFL_I2C_SDA,
+		 0,
+		 SQ930_GPIO_RSTBAR
+		},
+		7, ov7660_start_0
+	    },
+	[SENSOR_OV9630] = {
+		"ov9630",
+		0x30, 0x00,
+		{0,
+		 SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL,
+		 SQ930_GPIO_DFL_I2C_SDA,
+		 0,
+		 SQ930_GPIO_RSTBAR
+		},
+		7, ov9630_start_0
+	    },
+};
+
+static void reg_r(struct gspca_dev *gspca_dev,
+		u16 value, int len)
+{
+	int ret;
+
+	if (gspca_dev->usb_err < 0)
+		return;
+	ret = usb_control_msg(gspca_dev->dev,
+			usb_rcvctrlpipe(gspca_dev->dev, 0),
+			0x0c,
+			USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			value, 0, gspca_dev->usb_buf, len,
+			500);
+	if (ret < 0) {
+		pr_err("reg_r %04x failed %d\n", value, ret);
+		gspca_dev->usb_err = ret;
+	}
+}
+
+static void reg_w(struct gspca_dev *gspca_dev, u16 value, u16 index)
+{
+	int ret;
+
+	if (gspca_dev->usb_err < 0)
+		return;
+	PDEBUG(D_USBO, "reg_w v: %04x i: %04x", value, index);
+	ret = usb_control_msg(gspca_dev->dev,
+			usb_sndctrlpipe(gspca_dev->dev, 0),
+			0x0c,			/* request */
+			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			value, index, NULL, 0,
+			500);
+	msleep(30);
+	if (ret < 0) {
+		pr_err("reg_w %04x %04x failed %d\n", value, index, ret);
+		gspca_dev->usb_err = ret;
+	}
+}
+
+static void reg_wb(struct gspca_dev *gspca_dev, u16 value, u16 index,
+		const u8 *data, int len)
+{
+	int ret;
+
+	if (gspca_dev->usb_err < 0)
+		return;
+	PDEBUG(D_USBO, "reg_wb v: %04x i: %04x %02x...%02x",
+			value, index, *data, data[len - 1]);
+	memcpy(gspca_dev->usb_buf, data, len);
+	ret = usb_control_msg(gspca_dev->dev,
+			usb_sndctrlpipe(gspca_dev->dev, 0),
+			0x0c,			/* request */
+			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			value, index, gspca_dev->usb_buf, len,
+			1000);
+	msleep(30);
+	if (ret < 0) {
+		pr_err("reg_wb %04x %04x failed %d\n", value, index, ret);
+		gspca_dev->usb_err = ret;
+	}
+}
+
+static void i2c_write(struct sd *sd,
+			const struct i2c_write_cmd *cmd,
+			int ncmds)
+{
+	struct gspca_dev *gspca_dev = &sd->gspca_dev;
+	const struct sensor_s *sensor;
+	u16 val, idx;
+	u8 *buf;
+	int ret;
+
+	if (gspca_dev->usb_err < 0)
+		return;
+
+	sensor = &sensor_tb[sd->sensor];
+
+	val = (sensor->i2c_addr << 8) | SQ930_CTRL_I2C_IO;
+	idx = (cmd->val & 0xff00) | cmd->reg;
+
+	buf = gspca_dev->usb_buf;
+	*buf++ = sensor->i2c_dum;
+	*buf++ = cmd->val;
+
+	while (--ncmds > 0) {
+		cmd++;
+		*buf++ = cmd->reg;
+		*buf++ = cmd->val >> 8;
+		*buf++ = sensor->i2c_dum;
+		*buf++ = cmd->val;
+	}
+
+	PDEBUG(D_USBO, "i2c_w v: %04x i: %04x %02x...%02x",
+			val, idx, gspca_dev->usb_buf[0], buf[-1]);
+	ret = usb_control_msg(gspca_dev->dev,
+			usb_sndctrlpipe(gspca_dev->dev, 0),
+			0x0c,			/* request */
+			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			val, idx,
+			gspca_dev->usb_buf, buf - gspca_dev->usb_buf,
+			500);
+	if (ret < 0) {
+		pr_err("i2c_write failed %d\n", ret);
+		gspca_dev->usb_err = ret;
+	}
+}
+
+static void ucbus_write(struct gspca_dev *gspca_dev,
+			const struct ucbus_write_cmd *cmd,
+			int ncmds,
+			int batchsize)
+{
+	u8 *buf;
+	u16 val, idx;
+	int len, ret;
+
+	if (gspca_dev->usb_err < 0)
+		return;
+
+	if ((batchsize - 1) * 3 > USB_BUF_SZ) {
+		PERR("Bug: usb_buf overflow\n");
+		gspca_dev->usb_err = -ENOMEM;
+		return;
+	}
+
+	for (;;) {
+		len = ncmds;
+		if (len > batchsize)
+			len = batchsize;
+		ncmds -= len;
+
+		val = (cmd->bw_addr << 8) | SQ930_CTRL_UCBUS_IO;
+		idx = (cmd->bw_data << 8) | (cmd->bw_addr >> 8);
+
+		buf = gspca_dev->usb_buf;
+		while (--len > 0) {
+			cmd++;
+			*buf++ = cmd->bw_addr;
+			*buf++ = cmd->bw_addr >> 8;
+			*buf++ = cmd->bw_data;
+		}
+		if (buf != gspca_dev->usb_buf)
+			PDEBUG(D_USBO, "ucbus v: %04x i: %04x %02x...%02x",
+					val, idx,
+					gspca_dev->usb_buf[0], buf[-1]);
+		else
+			PDEBUG(D_USBO, "ucbus v: %04x i: %04x",
+					val, idx);
+		ret = usb_control_msg(gspca_dev->dev,
+				usb_sndctrlpipe(gspca_dev->dev, 0),
+				0x0c,			/* request */
+			   USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+				val, idx,
+				gspca_dev->usb_buf, buf - gspca_dev->usb_buf,
+				500);
+		if (ret < 0) {
+			pr_err("ucbus_write failed %d\n", ret);
+			gspca_dev->usb_err = ret;
+			return;
+		}
+		msleep(30);
+		if (ncmds <= 0)
+			break;
+		cmd++;
+	}
+}
+
+static void gpio_set(struct sd *sd, u16 val, u16 mask)
+{
+	struct gspca_dev *gspca_dev = &sd->gspca_dev;
+
+	if (mask & 0x00ff) {
+		sd->gpio[0] &= ~mask;
+		sd->gpio[0] |= val;
+		reg_w(gspca_dev, 0x0100 | SQ930_CTRL_GPIO,
+			~sd->gpio[0] << 8);
+	}
+	mask >>= 8;
+	val >>= 8;
+	if (mask) {
+		sd->gpio[1] &= ~mask;
+		sd->gpio[1] |= val;
+		reg_w(gspca_dev, 0x0300 | SQ930_CTRL_GPIO,
+			~sd->gpio[1] << 8);
+	}
+}
+
+static void gpio_init(struct sd *sd,
+			const u8 *gpio)
+{
+	gpio_set(sd, *gpio++, 0x000f);
+	gpio_set(sd, *gpio++, 0x000f);
+	gpio_set(sd, *gpio++, 0x000f);
+	gpio_set(sd, *gpio++, 0x000f);
+	gpio_set(sd, *gpio, 0x000f);
+}
+
+static void bridge_init(struct sd *sd)
+{
+	static const struct ucbus_write_cmd clkfreq_cmd = {
+				0xf031, 0	/* SQ930_CLKFREQ_60MHZ */
+	};
+
+	ucbus_write(&sd->gspca_dev, &clkfreq_cmd, 1, 1);
+
+	gpio_set(sd, SQ930_GPIO_POWER, 0xff00);
+}
+
+static void cmos_probe(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int i;
+	const struct sensor_s *sensor;
+	static const u8 probe_order[] = {
+/*		SENSOR_LZ24BP,		(tested as ccd) */
+		SENSOR_OV9630,
+		SENSOR_MI0360,
+		SENSOR_OV7660,
+		SENSOR_MT9V111,
+	};
+
+	for (i = 0; i < ARRAY_SIZE(probe_order); i++) {
+		sensor = &sensor_tb[probe_order[i]];
+		ucbus_write(&sd->gspca_dev, sensor->cmd, sensor->cmd_len, 8);
+		gpio_init(sd, sensor->gpio);
+		msleep(100);
+		reg_r(gspca_dev, (sensor->i2c_addr << 8) | 0x001c, 1);
+		msleep(100);
+		if (gspca_dev->usb_buf[0] != 0)
+			break;
+	}
+	if (i >= ARRAY_SIZE(probe_order)) {
+		pr_err("Unknown sensor\n");
+		gspca_dev->usb_err = -EINVAL;
+		return;
+	}
+	sd->sensor = probe_order[i];
+	switch (sd->sensor) {
+	case SENSOR_OV7660:
+	case SENSOR_OV9630:
+		pr_err("Sensor %s not yet treated\n",
+		       sensor_tb[sd->sensor].name);
+		gspca_dev->usb_err = -EINVAL;
+		break;
+	}
+}
+
+static void mt9v111_init(struct gspca_dev *gspca_dev)
+{
+	int i, nwait;
+	static const u8 cmd_001b[] = {
+		0x00, 0x3b, 0xf6, 0x01, 0x03, 0x02, 0x00, 0x00,
+		0x00, 0x00, 0x00
+	};
+	static const u8 cmd_011b[][7] = {
+		{0x10, 0x01, 0x66, 0x08, 0x00, 0x00, 0x00},
+		{0x01, 0x00, 0x1a, 0x04, 0x00, 0x00, 0x00},
+		{0x20, 0x00, 0x10, 0x04, 0x00, 0x00, 0x00},
+		{0x02, 0x01, 0xae, 0x01, 0x00, 0x00, 0x00},
+	};
+
+	reg_wb(gspca_dev, 0x001b, 0x0000, cmd_001b, sizeof cmd_001b);
+	for (i = 0; i < ARRAY_SIZE(cmd_011b); i++) {
+		reg_wb(gspca_dev, 0x001b, 0x0000, cmd_011b[i],
+				ARRAY_SIZE(cmd_011b[0]));
+		msleep(400);
+		nwait = 20;
+		for (;;) {
+			reg_r(gspca_dev, 0x031b, 1);
+			if (gspca_dev->usb_buf[0] == 0
+			 || gspca_dev->usb_err != 0)
+				break;
+			if (--nwait < 0) {
+				PDEBUG(D_PROBE, "mt9v111_init timeout");
+				gspca_dev->usb_err = -ETIME;
+				return;
+			}
+			msleep(50);
+		}
+	}
+}
+
+static void global_init(struct sd *sd, int first_time)
+{
+	switch (sd->sensor) {
+	case SENSOR_ICX098BQ:
+		if (first_time)
+			ucbus_write(&sd->gspca_dev,
+					icx098bq_start_0,
+					8, 8);
+		gpio_init(sd, sensor_tb[sd->sensor].gpio);
+		break;
+	case SENSOR_LZ24BP:
+		if (sd->type != Creative_live_motion)
+			gpio_set(sd, SQ930_GPIO_EXTRA1, 0x00ff);
+		else
+			gpio_set(sd, 0, 0x00ff);
+		msleep(50);
+		if (first_time)
+			ucbus_write(&sd->gspca_dev,
+					lz24bp_start_0,
+					8, 8);
+		gpio_init(sd, sensor_tb[sd->sensor].gpio);
+		break;
+	case SENSOR_MI0360:
+		if (first_time)
+			ucbus_write(&sd->gspca_dev,
+					mi0360_start_0,
+					ARRAY_SIZE(mi0360_start_0),
+					8);
+		gpio_init(sd, sensor_tb[sd->sensor].gpio);
+		gpio_set(sd, SQ930_GPIO_EXTRA2, SQ930_GPIO_EXTRA2);
+		break;
+	default:
+/*	case SENSOR_MT9V111: */
+		if (first_time)
+			mt9v111_init(&sd->gspca_dev);
+		else
+			gpio_init(sd, sensor_tb[sd->sensor].gpio);
+		break;
+	}
+}
+
+static void lz24bp_ppl(struct sd *sd, u16 ppl)
+{
+	struct ucbus_write_cmd cmds[2] = {
+		{0xf810, ppl >> 8},
+		{0xf811, ppl}
+	};
+
+	ucbus_write(&sd->gspca_dev, cmds, ARRAY_SIZE(cmds), 2);
+}
+
+static void setexposure(struct gspca_dev *gspca_dev, s32 expo, s32 gain)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int i, integclks, intstartclk, frameclks, min_frclk;
+	const struct sensor_s *sensor;
+	u16 cmd;
+	u8 buf[15];
+
+	integclks = expo;
+	i = 0;
+	cmd = SQ930_CTRL_SET_EXPOSURE;
+
+	switch (sd->sensor) {
+	case SENSOR_ICX098BQ:			/* ccd */
+	case SENSOR_LZ24BP:
+		min_frclk = sd->sensor == SENSOR_ICX098BQ ? 0x210 : 0x26f;
+		if (integclks >= min_frclk) {
+			intstartclk = 0;
+			frameclks = integclks;
+		} else {
+			intstartclk = min_frclk - integclks;
+			frameclks = min_frclk;
+		}
+		buf[i++] = intstartclk >> 8;
+		buf[i++] = intstartclk;
+		buf[i++] = frameclks >> 8;
+		buf[i++] = frameclks;
+		buf[i++] = gain;
+		break;
+	default:				/* cmos */
+/*	case SENSOR_MI0360: */
+/*	case SENSOR_MT9V111: */
+		cmd |= 0x0100;
+		sensor = &sensor_tb[sd->sensor];
+		buf[i++] = sensor->i2c_addr;	/* i2c_slave_addr */
+		buf[i++] = 0x08;	/* 2 * ni2c */
+		buf[i++] = 0x09;	/* reg = shutter width */
+		buf[i++] = integclks >> 8; /* val H */
+		buf[i++] = sensor->i2c_dum;
+		buf[i++] = integclks;	/* val L */
+		buf[i++] = 0x35;	/* reg = global gain */
+		buf[i++] = 0x00;	/* val H */
+		buf[i++] = sensor->i2c_dum;
+		buf[i++] = 0x80 + gain / 2; /* val L */
+		buf[i++] = 0x00;
+		buf[i++] = 0x00;
+		buf[i++] = 0x00;
+		buf[i++] = 0x00;
+		buf[i++] = 0x83;
+		break;
+	}
+	reg_wb(gspca_dev, cmd, 0, buf, i);
+}
+
+/* This function is called at probe time just before sd_init */
+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;
+
+	sd->sensor = id->driver_info >> 8;
+	sd->type = id->driver_info;
+
+	cam->cam_mode = vga_mode;
+	cam->nmodes = ARRAY_SIZE(vga_mode);
+
+	cam->bulk = 1;
+
+	return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	sd->gpio[0] = sd->gpio[1] = 0xff;	/* force gpio rewrite */
+
+/*fixme: is this needed for icx098bp and mi0360?
+	if (sd->sensor != SENSOR_LZ24BP)
+		reg_w(gspca_dev, SQ930_CTRL_RESET, 0x0000);
+ */
+
+	reg_r(gspca_dev, SQ930_CTRL_GET_DEV_INFO, 8);
+	if (gspca_dev->usb_err < 0)
+		return gspca_dev->usb_err;
+
+/* it returns:
+ * 03 00 12 93 0b f6 c9 00	live! ultra
+ * 03 00 07 93 0b f6 ca 00	live! ultra for notebook
+ * 03 00 12 93 0b fe c8 00	Trust WB-3500T
+ * 02 00 06 93 0b fe c8 00	Joy-IT 318S
+ * 03 00 12 93 0b f6 cf 00	icam tracer - sensor icx098bq
+ * 02 00 12 93 0b fe cf 00	ProQ Motion Webcam
+ *
+ * byte
+ * 0: 02 = usb 1.0 (12Mbit) / 03 = usb2.0 (480Mbit)
+ * 1: 00
+ * 2: 06 / 07 / 12 = mode webcam? firmware??
+ * 3: 93 chip = 930b (930b or 930c)
+ * 4: 0b
+ * 5: f6 = cdd (icx098bq, lz24bp) / fe or de = cmos (i2c) (other sensors)
+ * 6: c8 / c9 / ca / cf = mode webcam?, sensor? webcam?
+ * 7: 00
+ */
+	PDEBUG(D_PROBE, "info: %*ph", 8, gspca_dev->usb_buf);
+
+	bridge_init(sd);
+
+	if (sd->sensor == SENSOR_MI0360) {
+
+		/* no sensor probe for icam tracer */
+		if (gspca_dev->usb_buf[5] == 0xf6)	/* if ccd */
+			sd->sensor = SENSOR_ICX098BQ;
+		else
+			cmos_probe(gspca_dev);
+	}
+	if (gspca_dev->usb_err >= 0) {
+		PDEBUG(D_PROBE, "Sensor %s", sensor_tb[sd->sensor].name);
+		global_init(sd, 1);
+	}
+	return gspca_dev->usb_err;
+}
+
+/* send the start/stop commands to the webcam */
+static void send_start(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	const struct cap_s *cap;
+	int mode;
+
+	mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
+	cap = &capconfig[sd->sensor][mode];
+	reg_wb(gspca_dev, 0x0900 | SQ930_CTRL_CAP_START,
+			0x0a00 | cap->cc_sizeid,
+			cap->cc_bytes, 32);
+}
+
+static void send_stop(struct gspca_dev *gspca_dev)
+{
+	reg_w(gspca_dev, SQ930_CTRL_CAP_STOP, 0);
+}
+
+/* function called at start time before URB creation */
+static int sd_isoc_init(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	gspca_dev->cam.bulk_nurbs = 1;	/* there must be one URB only */
+	sd->do_ctrl = 0;
+	gspca_dev->cam.bulk_size = gspca_dev->pixfmt.width *
+			gspca_dev->pixfmt.height + 8;
+	return 0;
+}
+
+/* start the capture */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int mode;
+
+	bridge_init(sd);
+	global_init(sd, 0);
+	msleep(100);
+
+	switch (sd->sensor) {
+	case SENSOR_ICX098BQ:
+		ucbus_write(gspca_dev, icx098bq_start_0,
+				ARRAY_SIZE(icx098bq_start_0),
+				8);
+		ucbus_write(gspca_dev, icx098bq_start_1,
+				ARRAY_SIZE(icx098bq_start_1),
+				5);
+		ucbus_write(gspca_dev, icx098bq_start_2,
+				ARRAY_SIZE(icx098bq_start_2),
+				6);
+		msleep(50);
+
+		/* 1st start */
+		send_start(gspca_dev);
+		gpio_set(sd, SQ930_GPIO_EXTRA2 | SQ930_GPIO_RSTBAR, 0x00ff);
+		msleep(70);
+		reg_w(gspca_dev, SQ930_CTRL_CAP_STOP, 0x0000);
+		gpio_set(sd, 0x7f, 0x00ff);
+
+		/* 2nd start */
+		send_start(gspca_dev);
+		gpio_set(sd, SQ930_GPIO_EXTRA2 | SQ930_GPIO_RSTBAR, 0x00ff);
+		goto out;
+	case SENSOR_LZ24BP:
+		ucbus_write(gspca_dev, lz24bp_start_0,
+				ARRAY_SIZE(lz24bp_start_0),
+				8);
+		if (sd->type != Creative_live_motion)
+			ucbus_write(gspca_dev, lz24bp_start_1_gen,
+					ARRAY_SIZE(lz24bp_start_1_gen),
+					5);
+		else
+			ucbus_write(gspca_dev, lz24bp_start_1_clm,
+					ARRAY_SIZE(lz24bp_start_1_clm),
+					5);
+		ucbus_write(gspca_dev, lz24bp_start_2,
+				ARRAY_SIZE(lz24bp_start_2),
+				6);
+		mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
+		lz24bp_ppl(sd, mode == 1 ? 0x0564 : 0x0310);
+		msleep(10);
+		break;
+	case SENSOR_MI0360:
+		ucbus_write(gspca_dev, mi0360_start_0,
+				ARRAY_SIZE(mi0360_start_0),
+				8);
+		i2c_write(sd, mi0360_init_23,
+				ARRAY_SIZE(mi0360_init_23));
+		i2c_write(sd, mi0360_init_24,
+				ARRAY_SIZE(mi0360_init_24));
+		i2c_write(sd, mi0360_init_25,
+				ARRAY_SIZE(mi0360_init_25));
+		ucbus_write(gspca_dev, mi0360_start_1,
+				ARRAY_SIZE(mi0360_start_1),
+				5);
+		i2c_write(sd, mi0360_start_2,
+				ARRAY_SIZE(mi0360_start_2));
+		i2c_write(sd, mi0360_start_3,
+				ARRAY_SIZE(mi0360_start_3));
+
+		/* 1st start */
+		send_start(gspca_dev);
+		msleep(60);
+		send_stop(gspca_dev);
+
+		i2c_write(sd,
+			mi0360_start_4, ARRAY_SIZE(mi0360_start_4));
+		break;
+	default:
+/*	case SENSOR_MT9V111: */
+		ucbus_write(gspca_dev, mi0360_start_0,
+				ARRAY_SIZE(mi0360_start_0),
+				8);
+		i2c_write(sd, mt9v111_init_0,
+				ARRAY_SIZE(mt9v111_init_0));
+		i2c_write(sd, mt9v111_init_1,
+				ARRAY_SIZE(mt9v111_init_1));
+		i2c_write(sd, mt9v111_init_2,
+				ARRAY_SIZE(mt9v111_init_2));
+		ucbus_write(gspca_dev, mt9v111_start_1,
+				ARRAY_SIZE(mt9v111_start_1),
+				5);
+		i2c_write(sd, mt9v111_init_3,
+				ARRAY_SIZE(mt9v111_init_3));
+		i2c_write(sd, mt9v111_init_4,
+				ARRAY_SIZE(mt9v111_init_4));
+		break;
+	}
+
+	send_start(gspca_dev);
+out:
+	msleep(1000);
+
+	if (sd->sensor == SENSOR_MT9V111)
+		gpio_set(sd, SQ930_GPIO_DFL_LED, SQ930_GPIO_DFL_LED);
+
+	sd->do_ctrl = 1;	/* set the exposure */
+
+	return gspca_dev->usb_err;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (sd->sensor == SENSOR_MT9V111)
+		gpio_set(sd, 0, SQ930_GPIO_DFL_LED);
+	send_stop(gspca_dev);
+}
+
+/* function called when the application gets a new frame */
+/* It sets the exposure if required and restart the bulk transfer. */
+static void sd_dq_callback(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int ret;
+
+	if (!sd->do_ctrl || gspca_dev->cam.bulk_nurbs != 0)
+		return;
+	sd->do_ctrl = 0;
+
+	setexposure(gspca_dev, v4l2_ctrl_g_ctrl(sd->exposure),
+			v4l2_ctrl_g_ctrl(sd->gain));
+
+	gspca_dev->cam.bulk_nurbs = 1;
+	ret = usb_submit_urb(gspca_dev->urb[0], GFP_ATOMIC);
+	if (ret < 0)
+		pr_err("sd_dq_callback() err %d\n", ret);
+
+	/* wait a little time, otherwise the webcam crashes */
+	msleep(100);
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+			u8 *data,		/* isoc packet */
+			int len)		/* iso packet length */
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (sd->do_ctrl)
+		gspca_dev->cam.bulk_nurbs = 0;
+	gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0);
+	gspca_frame_add(gspca_dev, INTER_PACKET, data, len - 8);
+	gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+}
+
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct gspca_dev *gspca_dev =
+		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	gspca_dev->usb_err = 0;
+
+	if (!gspca_dev->streaming)
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_EXPOSURE:
+		setexposure(gspca_dev, ctrl->val, sd->gain->val);
+		break;
+	}
+	return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+	.s_ctrl = sd_s_ctrl,
+};
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	gspca_dev->vdev.ctrl_handler = hdl;
+	v4l2_ctrl_handler_init(hdl, 2);
+	sd->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_EXPOSURE, 1, 0xfff, 1, 0x356);
+	sd->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_GAIN, 1, 255, 1, 0x8d);
+
+	if (hdl->error) {
+		pr_err("Could not initialize controls\n");
+		return hdl->error;
+	}
+	v4l2_ctrl_cluster(2, &sd->exposure);
+	return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+	.name   = MODULE_NAME,
+	.config = sd_config,
+	.init   = sd_init,
+	.init_controls = sd_init_controls,
+	.isoc_init = sd_isoc_init,
+	.start  = sd_start,
+	.stopN  = sd_stopN,
+	.pkt_scan = sd_pkt_scan,
+	.dq_callback = sd_dq_callback,
+};
+
+/* Table of supported USB devices */
+#define ST(sensor, type) \
+	.driver_info = (SENSOR_ ## sensor << 8) \
+			| (type)
+static const struct usb_device_id device_table[] = {
+	{USB_DEVICE(0x041e, 0x4038), ST(MI0360, 0)},
+	{USB_DEVICE(0x041e, 0x403c), ST(LZ24BP, 0)},
+	{USB_DEVICE(0x041e, 0x403d), ST(LZ24BP, 0)},
+	{USB_DEVICE(0x041e, 0x4041), ST(LZ24BP, Creative_live_motion)},
+	{USB_DEVICE(0x2770, 0x930b), ST(MI0360, 0)},
+	{USB_DEVICE(0x2770, 0x930c), ST(MI0360, 0)},
+	{}
+};
+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,
+#ifdef CONFIG_PM
+	.suspend    = gspca_suspend,
+	.resume     = gspca_resume,
+	.reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/usb/gspca/stk014.c b/drivers/media/usb/gspca/stk014.c
new file mode 100644
index 0000000..d324d00
--- /dev/null
+++ b/drivers/media/usb/gspca/stk014.c
@@ -0,0 +1,447 @@
+/*
+ * 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "stk014"
+
+#include "gspca.h"
+#include "jpeg.h"
+
+MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>");
+MODULE_DESCRIPTION("Syntek DV4000 (STK014) USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+#define QUALITY 50
+
+/* specific webcam descriptor */
+struct sd {
+	struct gspca_dev gspca_dev;	/* !! must be the first item */
+	u8 jpeg_hdr[JPEG_HDR_SZ];
+};
+
+static const 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 u8 reg_r(struct gspca_dev *gspca_dev,
+			__u16 index)
+{
+	struct usb_device *dev = gspca_dev->dev;
+	int ret;
+
+	if (gspca_dev->usb_err < 0)
+		return 0;
+	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) {
+		pr_err("reg_r err %d\n", ret);
+		gspca_dev->usb_err = ret;
+		return 0;
+	}
+	return gspca_dev->usb_buf[0];
+}
+
+/* -- write a register -- */
+static void reg_w(struct gspca_dev *gspca_dev,
+			__u16 index, __u16 value)
+{
+	struct usb_device *dev = gspca_dev->dev;
+	int ret;
+
+	if (gspca_dev->usb_err < 0)
+		return;
+	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) {
+		pr_err("reg_w err %d\n", ret);
+		gspca_dev->usb_err = ret;
+	}
+}
+
+/* -- get a bulk value (4 bytes) -- */
+static void 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);
+	if (gspca_dev->usb_err < 0)
+		return;
+	ret = usb_bulk_msg(dev,
+			usb_rcvbulkpipe(dev, 0x05),
+			gspca_dev->usb_buf,
+			4,		/* length */
+			&alen,
+			500);		/* timeout in milliseconds */
+	if (ret < 0) {
+		pr_err("rcv_val err %d\n", ret);
+		gspca_dev->usb_err = ret;
+	}
+}
+
+/* -- send a bulk value -- */
+static void 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) {
+		reg_r(gspca_dev, 0x0704);
+		seq = reg_r(gspca_dev, 0x0705);
+		reg_r(gspca_dev, 0x0650);
+		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);
+	if (gspca_dev->usb_err < 0)
+		return;
+	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) {
+		pr_err("snd_val err %d\n", ret);
+		gspca_dev->usb_err = ret;
+	} else {
+		if (ads == 0x003f08) {
+			seq += 4;
+			seq &= 0x3f;
+			reg_w(gspca_dev, 0x705, seq);
+		}
+	}
+}
+
+/* set a camera parameter */
+static void set_par(struct gspca_dev *gspca_dev,
+		   int parval)
+{
+	snd_val(gspca_dev, 0x003f08, parval);
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
+{
+	int parval;
+
+	parval = 0x06000000		/* whiteness */
+		+ (val << 16);
+	set_par(gspca_dev, parval);
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
+{
+	int parval;
+
+	parval = 0x07000000		/* contrast */
+		+ (val << 16);
+	set_par(gspca_dev, parval);
+}
+
+static void setcolors(struct gspca_dev *gspca_dev, s32 val)
+{
+	int parval;
+
+	parval = 0x08000000		/* saturation */
+		+ (val << 16);
+	set_par(gspca_dev, parval);
+}
+
+static void setlightfreq(struct gspca_dev *gspca_dev, s32 val)
+{
+	set_par(gspca_dev, val == 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)
+{
+	gspca_dev->cam.cam_mode = vga_mode;
+	gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode);
+	return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+	u8 ret;
+
+	/* check if the device responds */
+	usb_set_interface(gspca_dev->dev, gspca_dev->iface, 1);
+	ret = reg_r(gspca_dev, 0x0740);
+	if (gspca_dev->usb_err >= 0) {
+		if (ret != 0xff) {
+			pr_err("init reg: 0x%02x\n", ret);
+			gspca_dev->usb_err = -EIO;
+		}
+	}
+	return gspca_dev->usb_err;
+}
+
+/* -- start the camera -- */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int ret, value;
+
+	/* create the JPEG header */
+	jpeg_define(sd->jpeg_hdr, gspca_dev->pixfmt.height,
+			gspca_dev->pixfmt.width,
+			0x22);		/* JPEG 411 */
+	jpeg_set_qual(sd->jpeg_hdr, QUALITY);
+
+	/* 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->pixfmt.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) {
+		pr_err("set intf %d %d failed\n",
+		       gspca_dev->iface, gspca_dev->alt);
+		gspca_dev->usb_err = ret;
+		goto out;
+	}
+	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);
+	set_par(gspca_dev, 0x09800000);		/* Red ? */
+	set_par(gspca_dev, 0x0a800000);		/* Green ? */
+	set_par(gspca_dev, 0x0b800000);		/* Blue ? */
+	set_par(gspca_dev, 0x0d030000);		/* Gamma ? */
+
+	/* start the video flow */
+	set_par(gspca_dev, 0x01000000);
+	set_par(gspca_dev, 0x01000000);
+	if (gspca_dev->usb_err >= 0)
+		PDEBUG(D_STREAM, "camera started alt: 0x%02x",
+				gspca_dev->alt);
+out:
+	return gspca_dev->usb_err;
+}
+
+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_pkt_scan(struct gspca_dev *gspca_dev,
+			u8 *data,			/* isoc packet */
+			int len)			/* iso packet length */
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	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) {
+		gspca_frame_add(gspca_dev, LAST_PACKET,
+				ffd9, 2);
+
+		/* put the JPEG 411 header */
+		gspca_frame_add(gspca_dev, FIRST_PACKET,
+			sd->jpeg_hdr, JPEG_HDR_SZ);
+
+		/* beginning of the frame */
+#define STKHDRSZ 12
+		data += STKHDRSZ;
+		len -= STKHDRSZ;
+	}
+	gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+}
+
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct gspca_dev *gspca_dev =
+		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+
+	gspca_dev->usb_err = 0;
+
+	if (!gspca_dev->streaming)
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_BRIGHTNESS:
+		setbrightness(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_CONTRAST:
+		setcontrast(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_SATURATION:
+		setcolors(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_POWER_LINE_FREQUENCY:
+		setlightfreq(gspca_dev, ctrl->val);
+		break;
+	}
+	return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+	.s_ctrl = sd_s_ctrl,
+};
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+	gspca_dev->vdev.ctrl_handler = hdl;
+	v4l2_ctrl_handler_init(hdl, 4);
+	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_BRIGHTNESS, 0, 255, 1, 127);
+	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_CONTRAST, 0, 255, 1, 127);
+	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_SATURATION, 0, 255, 1, 127);
+	v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
+			V4L2_CID_POWER_LINE_FREQUENCY,
+			V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 1,
+			V4L2_CID_POWER_LINE_FREQUENCY_50HZ);
+
+	if (hdl->error) {
+		pr_err("Could not initialize controls\n");
+		return hdl->error;
+	}
+	return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+	.name = MODULE_NAME,
+	.config = sd_config,
+	.init = sd_init,
+	.init_controls = sd_init_controls,
+	.start = sd_start,
+	.stopN = sd_stopN,
+	.pkt_scan = sd_pkt_scan,
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+	{USB_DEVICE(0x05e1, 0x0893)},
+	{}
+};
+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,
+#ifdef CONFIG_PM
+	.suspend = gspca_suspend,
+	.resume = gspca_resume,
+	.reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/usb/gspca/stk1135.c b/drivers/media/usb/gspca/stk1135.c
new file mode 100644
index 0000000..48234c9
--- /dev/null
+++ b/drivers/media/usb/gspca/stk1135.c
@@ -0,0 +1,688 @@
+/*
+ * Syntek STK1135 subdriver
+ *
+ * Copyright (c) 2013 Ondrej Zary
+ *
+ * Based on Syntekdriver (stk11xx) by Nicolas VIVIEN:
+ *   http://syntekdriver.sourceforge.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
+ * 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "stk1135"
+
+#include "gspca.h"
+#include "stk1135.h"
+
+MODULE_AUTHOR("Ondrej Zary");
+MODULE_DESCRIPTION("Syntek STK1135 USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+
+/* specific webcam descriptor */
+struct sd {
+	struct gspca_dev gspca_dev;	/* !! must be the first item */
+
+	u8 pkt_seq;
+	u8 sensor_page;
+
+	bool flip_status;
+	u8 flip_debounce;
+
+	struct v4l2_ctrl *hflip;
+	struct v4l2_ctrl *vflip;
+};
+
+static const struct v4l2_pix_format stk1135_modes[] = {
+	/* default mode (this driver supports variable resolution) */
+	{640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+		.bytesperline = 640,
+		.sizeimage = 640 * 480,
+		.colorspace = V4L2_COLORSPACE_SRGB},
+};
+
+/* -- read a register -- */
+static u8 reg_r(struct gspca_dev *gspca_dev, u16 index)
+{
+	struct usb_device *dev = gspca_dev->dev;
+	int ret;
+
+	if (gspca_dev->usb_err < 0)
+		return 0;
+	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);
+
+	PDEBUG(D_USBI, "reg_r 0x%x=0x%02x", index, gspca_dev->usb_buf[0]);
+	if (ret < 0) {
+		pr_err("reg_r 0x%x err %d\n", index, ret);
+		gspca_dev->usb_err = ret;
+		return 0;
+	}
+
+	return gspca_dev->usb_buf[0];
+}
+
+/* -- write a register -- */
+static void reg_w(struct gspca_dev *gspca_dev, u16 index, u8 val)
+{
+	int ret;
+	struct usb_device *dev = gspca_dev->dev;
+
+	if (gspca_dev->usb_err < 0)
+		return;
+	ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+			0x01,
+			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			val,
+			index,
+			NULL,
+			0,
+			500);
+	PDEBUG(D_USBO, "reg_w 0x%x:=0x%02x", index, val);
+	if (ret < 0) {
+		pr_err("reg_w 0x%x err %d\n", index, ret);
+		gspca_dev->usb_err = ret;
+	}
+}
+
+static void reg_w_mask(struct gspca_dev *gspca_dev, u16 index, u8 val, u8 mask)
+{
+	val = (reg_r(gspca_dev, index) & ~mask) | (val & mask);
+	reg_w(gspca_dev, index, val);
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+			const struct usb_device_id *id)
+{
+	gspca_dev->cam.cam_mode = stk1135_modes;
+	gspca_dev->cam.nmodes = ARRAY_SIZE(stk1135_modes);
+	return 0;
+}
+
+static int stk1135_serial_wait_ready(struct gspca_dev *gspca_dev)
+{
+	int i = 0;
+	u8 val;
+
+	do {
+		val = reg_r(gspca_dev, STK1135_REG_SICTL + 1);
+		if (i++ > 500) { /* maximum retry count */
+			pr_err("serial bus timeout: status=0x%02x\n", val);
+			return -1;
+		}
+	/* repeat if BUSY or WRITE/READ not finished */
+	} while ((val & 0x10) || !(val & 0x05));
+
+	return 0;
+}
+
+static u8 sensor_read_8(struct gspca_dev *gspca_dev, u8 addr)
+{
+	reg_w(gspca_dev, STK1135_REG_SBUSR, addr);
+	/* begin read */
+	reg_w(gspca_dev, STK1135_REG_SICTL, 0x20);
+	/* wait until finished */
+	if (stk1135_serial_wait_ready(gspca_dev)) {
+		pr_err("Sensor read failed\n");
+		return 0;
+	}
+
+	return reg_r(gspca_dev, STK1135_REG_SBUSR + 1);
+}
+
+static u16 sensor_read_16(struct gspca_dev *gspca_dev, u8 addr)
+{
+	return (sensor_read_8(gspca_dev, addr) << 8) |
+		sensor_read_8(gspca_dev, 0xf1);
+}
+
+static void sensor_write_8(struct gspca_dev *gspca_dev, u8 addr, u8 data)
+{
+	/* load address and data registers */
+	reg_w(gspca_dev, STK1135_REG_SBUSW, addr);
+	reg_w(gspca_dev, STK1135_REG_SBUSW + 1, data);
+	/* begin write */
+	reg_w(gspca_dev, STK1135_REG_SICTL, 0x01);
+	/* wait until finished */
+	if (stk1135_serial_wait_ready(gspca_dev)) {
+		pr_err("Sensor write failed\n");
+		return;
+	}
+}
+
+static void sensor_write_16(struct gspca_dev *gspca_dev, u8 addr, u16 data)
+{
+	sensor_write_8(gspca_dev, addr, data >> 8);
+	sensor_write_8(gspca_dev, 0xf1, data & 0xff);
+}
+
+static void sensor_set_page(struct gspca_dev *gspca_dev, u8 page)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (page != sd->sensor_page) {
+		sensor_write_16(gspca_dev, 0xf0, page);
+		sd->sensor_page = page;
+	}
+}
+
+static u16 sensor_read(struct gspca_dev *gspca_dev, u16 reg)
+{
+	sensor_set_page(gspca_dev, reg >> 8);
+	return sensor_read_16(gspca_dev, reg & 0xff);
+}
+
+static void sensor_write(struct gspca_dev *gspca_dev, u16 reg, u16 val)
+{
+	sensor_set_page(gspca_dev, reg >> 8);
+	sensor_write_16(gspca_dev, reg & 0xff, val);
+}
+
+static void sensor_write_mask(struct gspca_dev *gspca_dev,
+			u16 reg, u16 val, u16 mask)
+{
+	val = (sensor_read(gspca_dev, reg) & ~mask) | (val & mask);
+	sensor_write(gspca_dev, reg, val);
+}
+
+struct sensor_val {
+	u16 reg;
+	u16 val;
+};
+
+/* configure MT9M112 sensor */
+static void stk1135_configure_mt9m112(struct gspca_dev *gspca_dev)
+{
+	static const struct sensor_val cfg[] = {
+		/* restart&reset, chip enable, reserved */
+		{ 0x00d, 0x000b }, { 0x00d, 0x0008 }, { 0x035, 0x0022 },
+		/* mode ctl: AWB on, AE both, clip aper corr, defect corr, AE */
+		{ 0x106, 0x700e },
+
+		{ 0x2dd, 0x18e0 }, /* B-R thresholds, */
+
+		/* AWB */
+		{ 0x21f, 0x0180 }, /* Cb and Cr limits */
+		{ 0x220, 0xc814 }, { 0x221, 0x8080 }, /* lum limits, RGB gain */
+		{ 0x222, 0xa078 }, { 0x223, 0xa078 }, /* R, B limit */
+		{ 0x224, 0x5f20 }, { 0x228, 0xea02 }, /* mtx adj lim, adv ctl */
+		{ 0x229, 0x867a }, /* wide gates */
+
+		/* Color correction */
+		/* imager gains base, delta, delta signs */
+		{ 0x25e, 0x594c }, { 0x25f, 0x4d51 }, { 0x260, 0x0002 },
+		/* AWB adv ctl 2, gain offs */
+		{ 0x2ef, 0x0008 }, { 0x2f2, 0x0000 },
+		/* base matrix signs, scale K1-5, K6-9 */
+		{ 0x202, 0x00ee }, { 0x203, 0x3923 }, { 0x204, 0x0724 },
+		/* base matrix coef */
+		{ 0x209, 0x00cd }, { 0x20a, 0x0093 }, { 0x20b, 0x0004 },/*K1-3*/
+		{ 0x20c, 0x005c }, { 0x20d, 0x00d9 }, { 0x20e, 0x0053 },/*K4-6*/
+		{ 0x20f, 0x0008 }, { 0x210, 0x0091 }, { 0x211, 0x00cf },/*K7-9*/
+		{ 0x215, 0x0000 }, /* delta mtx signs */
+		/* delta matrix coef */
+		{ 0x216, 0x0000 }, { 0x217, 0x0000 }, { 0x218, 0x0000 },/*D1-3*/
+		{ 0x219, 0x0000 }, { 0x21a, 0x0000 }, { 0x21b, 0x0000 },/*D4-6*/
+		{ 0x21c, 0x0000 }, { 0x21d, 0x0000 }, { 0x21e, 0x0000 },/*D7-9*/
+		/* enable & disable manual WB to apply color corr. settings */
+		{ 0x106, 0xf00e }, { 0x106, 0x700e },
+
+		/* Lens shading correction */
+		{ 0x180, 0x0007 }, /* control */
+		/* vertical knee 0, 2+1, 4+3 */
+		{ 0x181, 0xde13 }, { 0x182, 0xebe2 }, { 0x183, 0x00f6 }, /* R */
+		{ 0x184, 0xe114 }, { 0x185, 0xeadd }, { 0x186, 0xfdf6 }, /* G */
+		{ 0x187, 0xe511 }, { 0x188, 0xede6 }, { 0x189, 0xfbf7 }, /* B */
+		/* horizontal knee 0, 2+1, 4+3, 5 */
+		{ 0x18a, 0xd613 }, { 0x18b, 0xedec }, /* R .. */
+		{ 0x18c, 0xf9f2 }, { 0x18d, 0x0000 }, /* .. R */
+		{ 0x18e, 0xd815 }, { 0x18f, 0xe9ea }, /* G .. */
+		{ 0x190, 0xf9f1 }, { 0x191, 0x0002 }, /* .. G */
+		{ 0x192, 0xde10 }, { 0x193, 0xefef }, /* B .. */
+		{ 0x194, 0xfbf4 }, { 0x195, 0x0002 }, /* .. B */
+		/* vertical knee 6+5, 8+7 */
+		{ 0x1b6, 0x0e06 }, { 0x1b7, 0x2713 }, /* R */
+		{ 0x1b8, 0x1106 }, { 0x1b9, 0x2713 }, /* G */
+		{ 0x1ba, 0x0c03 }, { 0x1bb, 0x2a0f }, /* B */
+		/* horizontal knee 7+6, 9+8, 10 */
+		{ 0x1bc, 0x1208 }, { 0x1bd, 0x1a16 }, { 0x1be, 0x0022 }, /* R */
+		{ 0x1bf, 0x150a }, { 0x1c0, 0x1c1a }, { 0x1c1, 0x002d }, /* G */
+		{ 0x1c2, 0x1109 }, { 0x1c3, 0x1414 }, { 0x1c4, 0x002a }, /* B */
+		{ 0x106, 0x740e }, /* enable lens shading correction */
+
+		/* Gamma correction - context A */
+		{ 0x153, 0x0b03 }, { 0x154, 0x4722 }, { 0x155, 0xac82 },
+		{ 0x156, 0xdac7 }, { 0x157, 0xf5e9 }, { 0x158, 0xff00 },
+		/* Gamma correction - context B */
+		{ 0x1dc, 0x0b03 }, { 0x1dd, 0x4722 }, { 0x1de, 0xac82 },
+		{ 0x1df, 0xdac7 }, { 0x1e0, 0xf5e9 }, { 0x1e1, 0xff00 },
+
+		/* output format: RGB, invert output pixclock, output bayer */
+		{ 0x13a, 0x4300 }, { 0x19b, 0x4300 }, /* for context A, B */
+		{ 0x108, 0x0180 }, /* format control - enable bayer row flip */
+
+		{ 0x22f, 0xd100 }, { 0x29c, 0xd100 }, /* AE A, B */
+
+		/* default prg conf, prg ctl - by 0x2d2, prg advance - PA1 */
+		{ 0x2d2, 0x0000 }, { 0x2cc, 0x0004 }, { 0x2cb, 0x0001 },
+
+		{ 0x22e, 0x0c3c }, { 0x267, 0x1010 }, /* AE tgt ctl, gain lim */
+
+		/* PLL */
+		{ 0x065, 0xa000 }, /* clk ctl - enable PLL (clear bit 14) */
+		{ 0x066, 0x2003 }, { 0x067, 0x0501 }, /* PLL M=128, N=3, P=1 */
+		{ 0x065, 0x2000 }, /* disable PLL bypass (clear bit 15) */
+
+		{ 0x005, 0x01b8 }, { 0x007, 0x00d8 }, /* horiz blanking B, A */
+
+		/* AE line size, shutter delay limit */
+		{ 0x239, 0x06c0 }, { 0x23b, 0x040e }, /* for context A */
+		{ 0x23a, 0x06c0 }, { 0x23c, 0x0564 }, /* for context B */
+		/* shutter width basis 60Hz, 50Hz */
+		{ 0x257, 0x0208 }, { 0x258, 0x0271 }, /* for context A */
+		{ 0x259, 0x0209 }, { 0x25a, 0x0271 }, /* for context B */
+
+		{ 0x25c, 0x120d }, { 0x25d, 0x1712 }, /* flicker 60Hz, 50Hz */
+		{ 0x264, 0x5e1c }, /* reserved */
+		/* flicker, AE gain limits, gain zone limits */
+		{ 0x25b, 0x0003 }, { 0x236, 0x7810 }, { 0x237, 0x8304 },
+
+		{ 0x008, 0x0021 }, /* vert blanking A */
+	};
+	int i;
+	u16 width, height;
+
+	for (i = 0; i < ARRAY_SIZE(cfg); i++)
+		sensor_write(gspca_dev, cfg[i].reg, cfg[i].val);
+
+	/* set output size */
+	width = gspca_dev->pixfmt.width;
+	height = gspca_dev->pixfmt.height;
+	if (width <= 640 && height <= 512) { /* context A (half readout speed)*/
+		sensor_write(gspca_dev, 0x1a7, width);
+		sensor_write(gspca_dev, 0x1aa, height);
+		/* set read mode context A */
+		sensor_write(gspca_dev, 0x0c8, 0x0000);
+		/* set resize, read mode, vblank, hblank context A */
+		sensor_write(gspca_dev, 0x2c8, 0x0000);
+	} else { /* context B (full readout speed) */
+		sensor_write(gspca_dev, 0x1a1, width);
+		sensor_write(gspca_dev, 0x1a4, height);
+		/* set read mode context B */
+		sensor_write(gspca_dev, 0x0c8, 0x0008);
+		/* set resize, read mode, vblank, hblank context B */
+		sensor_write(gspca_dev, 0x2c8, 0x040b);
+	}
+}
+
+static void stk1135_configure_clock(struct gspca_dev *gspca_dev)
+{
+	/* configure SCLKOUT */
+	reg_w(gspca_dev, STK1135_REG_TMGEN, 0x12);
+	/* set 1 clock per pixel */
+	/* and positive edge clocked pulse high when pixel counter = 0 */
+	reg_w(gspca_dev, STK1135_REG_TCP1 + 0, 0x41);
+	reg_w(gspca_dev, STK1135_REG_TCP1 + 1, 0x00);
+	reg_w(gspca_dev, STK1135_REG_TCP1 + 2, 0x00);
+	reg_w(gspca_dev, STK1135_REG_TCP1 + 3, 0x00);
+
+	/* enable CLKOUT for sensor */
+	reg_w(gspca_dev, STK1135_REG_SENSO + 0, 0x10);
+	/* disable STOP clock */
+	reg_w(gspca_dev, STK1135_REG_SENSO + 1, 0x00);
+	/* set lower 8 bits of PLL feedback divider */
+	reg_w(gspca_dev, STK1135_REG_SENSO + 3, 0x07);
+	/* set other PLL parameters */
+	reg_w(gspca_dev, STK1135_REG_PLLFD, 0x06);
+	/* enable timing generator */
+	reg_w(gspca_dev, STK1135_REG_TMGEN, 0x80);
+	/* enable PLL */
+	reg_w(gspca_dev, STK1135_REG_SENSO + 2, 0x04);
+
+	/* set serial interface clock divider (30MHz/0x1f*16+2) = 60240 kHz) */
+	reg_w(gspca_dev, STK1135_REG_SICTL + 2, 0x1f);
+
+	/* wait a while for sensor to catch up */
+	udelay(1000);
+}
+
+static void stk1135_camera_disable(struct gspca_dev *gspca_dev)
+{
+	/* set capture end Y position to 0 */
+	reg_w(gspca_dev, STK1135_REG_CIEPO + 2, 0x00);
+	reg_w(gspca_dev, STK1135_REG_CIEPO + 3, 0x00);
+	/* disable capture */
+	reg_w_mask(gspca_dev, STK1135_REG_SCTRL, 0x00, 0x80);
+
+	/* enable sensor standby and diasble chip enable */
+	sensor_write_mask(gspca_dev, 0x00d, 0x0004, 0x000c);
+
+	/* disable PLL */
+	reg_w_mask(gspca_dev, STK1135_REG_SENSO + 2, 0x00, 0x01);
+	/* disable timing generator */
+	reg_w(gspca_dev, STK1135_REG_TMGEN, 0x00);
+	/* enable STOP clock */
+	reg_w(gspca_dev, STK1135_REG_SENSO + 1, 0x20);
+	/* disable CLKOUT for sensor */
+	reg_w(gspca_dev, STK1135_REG_SENSO, 0x00);
+
+	/* disable sensor (GPIO5) and enable GPIO0,3,6 (?) - sensor standby? */
+	reg_w(gspca_dev, STK1135_REG_GCTRL, 0x49);
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+	u16 sensor_id;
+	char *sensor_name;
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	/* set GPIO3,4,5,6 direction to output */
+	reg_w(gspca_dev, STK1135_REG_GCTRL + 2, 0x78);
+	/* enable sensor (GPIO5) */
+	reg_w(gspca_dev, STK1135_REG_GCTRL, (1 << 5));
+	/* disable ROM interface */
+	reg_w(gspca_dev, STK1135_REG_GCTRL + 3, 0x80);
+	/* enable interrupts from GPIO8 (flip sensor) and GPIO9 (???) */
+	reg_w(gspca_dev, STK1135_REG_ICTRL + 1, 0x00);
+	reg_w(gspca_dev, STK1135_REG_ICTRL + 3, 0x03);
+	/* enable remote wakeup from GPIO9 (???) */
+	reg_w(gspca_dev, STK1135_REG_RMCTL + 1, 0x00);
+	reg_w(gspca_dev, STK1135_REG_RMCTL + 3, 0x02);
+
+	/* reset serial interface */
+	reg_w(gspca_dev, STK1135_REG_SICTL, 0x80);
+	reg_w(gspca_dev, STK1135_REG_SICTL, 0x00);
+	/* set sensor address */
+	reg_w(gspca_dev, STK1135_REG_SICTL + 3, 0xba);
+	/* disable alt 2-wire serial interface */
+	reg_w(gspca_dev, STK1135_REG_ASIC + 3, 0x00);
+
+	stk1135_configure_clock(gspca_dev);
+
+	/* read sensor ID */
+	sd->sensor_page = 0xff;
+	sensor_id = sensor_read(gspca_dev, 0x000);
+
+	switch (sensor_id) {
+	case 0x148c:
+		sensor_name = "MT9M112";
+		break;
+	default:
+		sensor_name = "unknown";
+	}
+	pr_info("Detected sensor type %s (0x%x)\n", sensor_name, sensor_id);
+
+	stk1135_camera_disable(gspca_dev);
+
+	return gspca_dev->usb_err;
+}
+
+/* -- start the camera -- */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u16 width, height;
+
+	/* enable sensor (GPIO5) */
+	reg_w(gspca_dev, STK1135_REG_GCTRL, (1 << 5));
+
+	stk1135_configure_clock(gspca_dev);
+
+	/* set capture start position X = 0, Y = 0 */
+	reg_w(gspca_dev, STK1135_REG_CISPO + 0, 0x00);
+	reg_w(gspca_dev, STK1135_REG_CISPO + 1, 0x00);
+	reg_w(gspca_dev, STK1135_REG_CISPO + 2, 0x00);
+	reg_w(gspca_dev, STK1135_REG_CISPO + 3, 0x00);
+
+	/* set capture end position */
+	width = gspca_dev->pixfmt.width;
+	height = gspca_dev->pixfmt.height;
+	reg_w(gspca_dev, STK1135_REG_CIEPO + 0, width & 0xff);
+	reg_w(gspca_dev, STK1135_REG_CIEPO + 1, width >> 8);
+	reg_w(gspca_dev, STK1135_REG_CIEPO + 2, height & 0xff);
+	reg_w(gspca_dev, STK1135_REG_CIEPO + 3, height >> 8);
+
+	/* set 8-bit mode */
+	reg_w(gspca_dev, STK1135_REG_SCTRL, 0x20);
+
+	stk1135_configure_mt9m112(gspca_dev);
+
+	/* enable capture */
+	reg_w_mask(gspca_dev, STK1135_REG_SCTRL, 0x80, 0x80);
+
+	if (gspca_dev->usb_err >= 0)
+		PDEBUG(D_STREAM, "camera started alt: 0x%02x",
+				gspca_dev->alt);
+
+	sd->pkt_seq = 0;
+
+	return gspca_dev->usb_err;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+	struct usb_device *dev = gspca_dev->dev;
+
+	usb_set_interface(dev, gspca_dev->iface, 0);
+
+	stk1135_camera_disable(gspca_dev);
+
+	PDEBUG(D_STREAM, "camera stopped");
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+			u8 *data,			/* isoc packet */
+			int len)			/* iso packet length */
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int skip = sizeof(struct stk1135_pkt_header);
+	bool flip;
+	enum gspca_packet_type pkt_type = INTER_PACKET;
+	struct stk1135_pkt_header *hdr = (void *)data;
+	u8 seq;
+
+	if (len < 4) {
+		PDEBUG(D_PACK, "received short packet (less than 4 bytes)");
+		return;
+	}
+
+	/* GPIO 8 is flip sensor (1 = normal position, 0 = flipped to back) */
+	flip = !(le16_to_cpu(hdr->gpio) & (1 << 8));
+	/* it's a switch, needs software debounce */
+	if (sd->flip_status != flip)
+		sd->flip_debounce++;
+	else
+		sd->flip_debounce = 0;
+
+	/* check sequence number (not present in new frame packets) */
+	if (!(hdr->flags & STK1135_HDR_FRAME_START)) {
+		seq = hdr->seq & STK1135_HDR_SEQ_MASK;
+		if (seq != sd->pkt_seq) {
+			PDEBUG(D_PACK, "received out-of-sequence packet");
+			/* resync sequence and discard packet */
+			sd->pkt_seq = seq;
+			gspca_dev->last_packet_type = DISCARD_PACKET;
+			return;
+		}
+	}
+	sd->pkt_seq++;
+	if (sd->pkt_seq > STK1135_HDR_SEQ_MASK)
+		sd->pkt_seq = 0;
+
+	if (len == sizeof(struct stk1135_pkt_header))
+		return;
+
+	if (hdr->flags & STK1135_HDR_FRAME_START) { /* new frame */
+		skip = 8;	/* the header is longer */
+		gspca_frame_add(gspca_dev, LAST_PACKET, data, 0);
+		pkt_type = FIRST_PACKET;
+	}
+	gspca_frame_add(gspca_dev, pkt_type, data + skip, len - skip);
+}
+
+static void sethflip(struct gspca_dev *gspca_dev, s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (sd->flip_status)
+		val = !val;
+	sensor_write_mask(gspca_dev, 0x020, val ? 0x0002 : 0x0000 , 0x0002);
+}
+
+static void setvflip(struct gspca_dev *gspca_dev, s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (sd->flip_status)
+		val = !val;
+	sensor_write_mask(gspca_dev, 0x020, val ? 0x0001 : 0x0000 , 0x0001);
+}
+
+static void stk1135_dq_callback(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (sd->flip_debounce > 100) {
+		sd->flip_status = !sd->flip_status;
+		sethflip(gspca_dev, v4l2_ctrl_g_ctrl(sd->hflip));
+		setvflip(gspca_dev, v4l2_ctrl_g_ctrl(sd->vflip));
+	}
+}
+
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct gspca_dev *gspca_dev =
+		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+
+	gspca_dev->usb_err = 0;
+
+	if (!gspca_dev->streaming)
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_HFLIP:
+		sethflip(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_VFLIP:
+		setvflip(gspca_dev, ctrl->val);
+		break;
+	}
+
+	return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+	.s_ctrl = sd_s_ctrl,
+};
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+	gspca_dev->vdev.ctrl_handler = hdl;
+	v4l2_ctrl_handler_init(hdl, 2);
+	sd->hflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_HFLIP, 0, 1, 1, 0);
+	sd->vflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_VFLIP, 0, 1, 1, 0);
+
+	if (hdl->error) {
+		pr_err("Could not initialize controls\n");
+		return hdl->error;
+	}
+	return 0;
+}
+
+static void stk1135_try_fmt(struct gspca_dev *gspca_dev, struct v4l2_format *fmt)
+{
+	fmt->fmt.pix.width = clamp(fmt->fmt.pix.width, 32U, 1280U);
+	fmt->fmt.pix.height = clamp(fmt->fmt.pix.height, 32U, 1024U);
+	/* round up to even numbers */
+	fmt->fmt.pix.width += (fmt->fmt.pix.width & 1);
+	fmt->fmt.pix.height += (fmt->fmt.pix.height & 1);
+
+	fmt->fmt.pix.bytesperline = fmt->fmt.pix.width;
+	fmt->fmt.pix.sizeimage = fmt->fmt.pix.width * fmt->fmt.pix.height;
+}
+
+static int stk1135_enum_framesizes(struct gspca_dev *gspca_dev,
+			struct v4l2_frmsizeenum *fsize)
+{
+	if (fsize->index != 0 || fsize->pixel_format != V4L2_PIX_FMT_SBGGR8)
+		return -EINVAL;
+
+	fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
+	fsize->stepwise.min_width = 32;
+	fsize->stepwise.min_height = 32;
+	fsize->stepwise.max_width = 1280;
+	fsize->stepwise.max_height = 1024;
+	fsize->stepwise.step_width = 2;
+	fsize->stepwise.step_height = 2;
+
+	return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+	.name = MODULE_NAME,
+	.config = sd_config,
+	.init = sd_init,
+	.init_controls = sd_init_controls,
+	.start = sd_start,
+	.stopN = sd_stopN,
+	.pkt_scan = sd_pkt_scan,
+	.dq_callback = stk1135_dq_callback,
+	.try_fmt = stk1135_try_fmt,
+	.enum_framesizes = stk1135_enum_framesizes,
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+	{USB_DEVICE(0x174f, 0x6a31)},	/* ASUS laptop, MT9M112 sensor */
+	{}
+};
+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,
+#ifdef CONFIG_PM
+	.suspend = gspca_suspend,
+	.resume = gspca_resume,
+	.reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/usb/gspca/stk1135.h b/drivers/media/usb/gspca/stk1135.h
new file mode 100644
index 0000000..e1dd92a
--- /dev/null
+++ b/drivers/media/usb/gspca/stk1135.h
@@ -0,0 +1,57 @@
+/*
+ * STK1135 registers
+ *
+ * Copyright (c) 2013 Ondrej Zary
+ *
+ * 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 STK1135_REG_GCTRL	0x000	/* GPIO control */
+#define STK1135_REG_ICTRL	0x004	/* Interrupt control */
+#define STK1135_REG_IDATA	0x008	/* Interrupt data */
+#define STK1135_REG_RMCTL	0x00c	/* Remote wakeup control */
+#define STK1135_REG_POSVA	0x010	/* Power-on strapping data */
+
+#define STK1135_REG_SENSO	0x018	/* Sensor select options */
+#define STK1135_REG_PLLFD	0x01c	/* PLL frequency divider */
+
+#define STK1135_REG_SCTRL	0x100	/* Sensor control register */
+#define STK1135_REG_DCTRL	0x104	/* Decimation control register */
+#define STK1135_REG_CISPO	0x110	/* Capture image starting position */
+#define STK1135_REG_CIEPO	0x114	/* Capture image ending position */
+#define STK1135_REG_TCTRL	0x120	/* Test data control */
+
+#define STK1135_REG_SICTL	0x200	/* Serial interface control register */
+#define STK1135_REG_SBUSW	0x204	/* Serial bus write */
+#define STK1135_REG_SBUSR	0x208	/* Serial bus read */
+#define STK1135_REG_SCSI	0x20c	/* Software control serial interface */
+#define STK1135_REG_GSBWP	0x210	/* General serial bus write port */
+#define STK1135_REG_GSBRP	0x214	/* General serial bus read port */
+#define STK1135_REG_ASIC	0x2fc	/* Alternate serial interface control */
+
+#define STK1135_REG_TMGEN	0x300	/* Timing generator */
+#define STK1135_REG_TCP1	0x350	/* Timing control parameter 1 */
+
+struct stk1135_pkt_header {
+	u8 flags;
+	u8 seq;
+	__le16 gpio;
+} __packed;
+
+#define STK1135_HDR_FRAME_START	(1 << 7)
+#define STK1135_HDR_ODD		(1 << 6)
+#define STK1135_HDR_I2C_VBLANK	(1 << 5)
+
+#define STK1135_HDR_SEQ_MASK	0x3f
diff --git a/drivers/media/usb/gspca/stv0680.c b/drivers/media/usb/gspca/stv0680.c
new file mode 100644
index 0000000..7f94ec7
--- /dev/null
+++ b/drivers/media/usb/gspca/stv0680.c
@@ -0,0 +1,353 @@
+/*
+ * STV0680 USB Camera Driver
+ *
+ * Copyright (C) 2009 Hans de Goede <hdegoede@redhat.com>
+ *
+ * This module is adapted from the in kernel v4l1 stv680 driver:
+ *
+ *  STV0680 USB Camera Driver, by Kevin Sisson (kjsisson@bellsouth.net)
+ *
+ * Thanks to STMicroelectronics for information on the usb commands, and
+ * to Steve Miller at STM for his help and encouragement while I was
+ * writing this driver.
+ *
+ * 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "stv0680"
+
+#include "gspca.h"
+
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_DESCRIPTION("STV0680 USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* specific webcam descriptor */
+struct sd {
+	struct gspca_dev gspca_dev;		/* !! must be the first item */
+	struct v4l2_pix_format mode;
+	u8 orig_mode;
+	u8 video_mode;
+	u8 current_mode;
+};
+
+static int stv_sndctrl(struct gspca_dev *gspca_dev, int set, u8 req, u16 val,
+		       int size)
+{
+	int ret = -1;
+	u8 req_type = 0;
+	unsigned int pipe = 0;
+
+	switch (set) {
+	case 0: /*  0xc1  */
+		req_type = USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT;
+		pipe = usb_rcvctrlpipe(gspca_dev->dev, 0);
+		break;
+	case 1: /*  0x41  */
+		req_type = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT;
+		pipe = usb_sndctrlpipe(gspca_dev->dev, 0);
+		break;
+	case 2:	/*  0x80  */
+		req_type = USB_DIR_IN | USB_RECIP_DEVICE;
+		pipe = usb_rcvctrlpipe(gspca_dev->dev, 0);
+		break;
+	case 3:	/*  0x40  */
+		req_type = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE;
+		pipe = usb_sndctrlpipe(gspca_dev->dev, 0);
+		break;
+	}
+
+	ret = usb_control_msg(gspca_dev->dev, pipe,
+			      req, req_type,
+			      val, 0, gspca_dev->usb_buf, size, 500);
+
+	if ((ret < 0) && (req != 0x0a))
+		pr_err("usb_control_msg error %i, request = 0x%x, error = %i\n",
+		       set, req, ret);
+
+	return ret;
+}
+
+static int stv0680_handle_error(struct gspca_dev *gspca_dev, int ret)
+{
+	stv_sndctrl(gspca_dev, 0, 0x80, 0, 0x02); /* Get Last Error */
+	PERR("last error: %i,  command = 0x%x",
+	       gspca_dev->usb_buf[0], gspca_dev->usb_buf[1]);
+	return ret;
+}
+
+static int stv0680_get_video_mode(struct gspca_dev *gspca_dev)
+{
+	/* Note not sure if this init of usb_buf is really necessary */
+	memset(gspca_dev->usb_buf, 0, 8);
+	gspca_dev->usb_buf[0] = 0x0f;
+
+	if (stv_sndctrl(gspca_dev, 0, 0x87, 0, 0x08) != 0x08) {
+		PERR("Get_Camera_Mode failed");
+		return stv0680_handle_error(gspca_dev, -EIO);
+	}
+
+	return gspca_dev->usb_buf[0]; /* 01 = VGA, 03 = QVGA, 00 = CIF */
+}
+
+static int stv0680_set_video_mode(struct gspca_dev *gspca_dev, u8 mode)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (sd->current_mode == mode)
+		return 0;
+
+	memset(gspca_dev->usb_buf, 0, 8);
+	gspca_dev->usb_buf[0] = mode;
+
+	if (stv_sndctrl(gspca_dev, 3, 0x07, 0x0100, 0x08) != 0x08) {
+		PERR("Set_Camera_Mode failed");
+		return stv0680_handle_error(gspca_dev, -EIO);
+	}
+
+	/* Verify we got what we've asked for */
+	if (stv0680_get_video_mode(gspca_dev) != mode) {
+		PERR("Error setting camera video mode!");
+		return -EIO;
+	}
+
+	sd->current_mode = mode;
+
+	return 0;
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+			const struct usb_device_id *id)
+{
+	int ret;
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct cam *cam = &gspca_dev->cam;
+
+	/* Give the camera some time to settle, otherwise initialization will
+	   fail on hotplug, and yes it really needs a full second. */
+	msleep(1000);
+
+	/* ping camera to be sure STV0680 is present */
+	if (stv_sndctrl(gspca_dev, 0, 0x88, 0x5678, 0x02) != 0x02 ||
+	    gspca_dev->usb_buf[0] != 0x56 || gspca_dev->usb_buf[1] != 0x78) {
+		PERR("STV(e): camera ping failed!!");
+		return stv0680_handle_error(gspca_dev, -ENODEV);
+	}
+
+	/* get camera descriptor */
+	if (stv_sndctrl(gspca_dev, 2, 0x06, 0x0200, 0x09) != 0x09)
+		return stv0680_handle_error(gspca_dev, -ENODEV);
+
+	if (stv_sndctrl(gspca_dev, 2, 0x06, 0x0200, 0x22) != 0x22 ||
+	    gspca_dev->usb_buf[7] != 0xa0 || gspca_dev->usb_buf[8] != 0x23) {
+		PERR("Could not get descriptor 0200.");
+		return stv0680_handle_error(gspca_dev, -ENODEV);
+	}
+	if (stv_sndctrl(gspca_dev, 0, 0x8a, 0, 0x02) != 0x02)
+		return stv0680_handle_error(gspca_dev, -ENODEV);
+	if (stv_sndctrl(gspca_dev, 0, 0x8b, 0, 0x24) != 0x24)
+		return stv0680_handle_error(gspca_dev, -ENODEV);
+	if (stv_sndctrl(gspca_dev, 0, 0x85, 0, 0x10) != 0x10)
+		return stv0680_handle_error(gspca_dev, -ENODEV);
+
+	if (!(gspca_dev->usb_buf[7] & 0x09)) {
+		PERR("Camera supports neither CIF nor QVGA mode");
+		return -ENODEV;
+	}
+	if (gspca_dev->usb_buf[7] & 0x01)
+		PDEBUG(D_PROBE, "Camera supports CIF mode");
+	if (gspca_dev->usb_buf[7] & 0x02)
+		PDEBUG(D_PROBE, "Camera supports VGA mode");
+	if (gspca_dev->usb_buf[7] & 0x04)
+		PDEBUG(D_PROBE, "Camera supports QCIF mode");
+	if (gspca_dev->usb_buf[7] & 0x08)
+		PDEBUG(D_PROBE, "Camera supports QVGA mode");
+
+	if (gspca_dev->usb_buf[7] & 0x01)
+		sd->video_mode = 0x00; /* CIF */
+	else
+		sd->video_mode = 0x03; /* QVGA */
+
+	/* FW rev, ASIC rev, sensor ID  */
+	PDEBUG(D_PROBE, "Firmware rev is %i.%i",
+	       gspca_dev->usb_buf[0], gspca_dev->usb_buf[1]);
+	PDEBUG(D_PROBE, "ASIC rev is %i.%i",
+	       gspca_dev->usb_buf[2], gspca_dev->usb_buf[3]);
+	PDEBUG(D_PROBE, "Sensor ID is %i",
+	       (gspca_dev->usb_buf[4]*16) + (gspca_dev->usb_buf[5]>>4));
+
+
+	ret = stv0680_get_video_mode(gspca_dev);
+	if (ret < 0)
+		return ret;
+	sd->current_mode = sd->orig_mode = ret;
+
+	ret = stv0680_set_video_mode(gspca_dev, sd->video_mode);
+	if (ret < 0)
+		return ret;
+
+	/* Get mode details */
+	if (stv_sndctrl(gspca_dev, 0, 0x8f, 0, 0x10) != 0x10)
+		return stv0680_handle_error(gspca_dev, -EIO);
+
+	cam->bulk = 1;
+	cam->bulk_nurbs = 1; /* The cam cannot handle more */
+	cam->bulk_size = (gspca_dev->usb_buf[0] << 24) |
+			 (gspca_dev->usb_buf[1] << 16) |
+			 (gspca_dev->usb_buf[2] << 8) |
+			 (gspca_dev->usb_buf[3]);
+	sd->mode.width = (gspca_dev->usb_buf[4] << 8) |
+			 (gspca_dev->usb_buf[5]);  /* 322, 356, 644 */
+	sd->mode.height = (gspca_dev->usb_buf[6] << 8) |
+			  (gspca_dev->usb_buf[7]); /* 242, 292, 484 */
+	sd->mode.pixelformat = V4L2_PIX_FMT_STV0680;
+	sd->mode.field = V4L2_FIELD_NONE;
+	sd->mode.bytesperline = sd->mode.width;
+	sd->mode.sizeimage = cam->bulk_size;
+	sd->mode.colorspace = V4L2_COLORSPACE_SRGB;
+
+	/* origGain = gspca_dev->usb_buf[12]; */
+
+	cam->cam_mode = &sd->mode;
+	cam->nmodes = 1;
+
+
+	ret = stv0680_set_video_mode(gspca_dev, sd->orig_mode);
+	if (ret < 0)
+		return ret;
+
+	if (stv_sndctrl(gspca_dev, 2, 0x06, 0x0100, 0x12) != 0x12 ||
+	    gspca_dev->usb_buf[8] != 0x53 || gspca_dev->usb_buf[9] != 0x05) {
+		pr_err("Could not get descriptor 0100\n");
+		return stv0680_handle_error(gspca_dev, -EIO);
+	}
+
+	return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+	return 0;
+}
+
+/* -- start the camera -- */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+	int ret;
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	ret = stv0680_set_video_mode(gspca_dev, sd->video_mode);
+	if (ret < 0)
+		return ret;
+
+	if (stv_sndctrl(gspca_dev, 0, 0x85, 0, 0x10) != 0x10)
+		return stv0680_handle_error(gspca_dev, -EIO);
+
+	/* Start stream at:
+	   0x0000 = CIF (352x288)
+	   0x0100 = VGA (640x480)
+	   0x0300 = QVGA (320x240) */
+	if (stv_sndctrl(gspca_dev, 1, 0x09, sd->video_mode << 8, 0x0) != 0x0)
+		return stv0680_handle_error(gspca_dev, -EIO);
+
+	return 0;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+	/* This is a high priority command; it stops all lower order cmds */
+	if (stv_sndctrl(gspca_dev, 1, 0x04, 0x0000, 0x0) != 0x0)
+		stv0680_handle_error(gspca_dev, -EIO);
+}
+
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (!sd->gspca_dev.present)
+		return;
+
+	stv0680_set_video_mode(gspca_dev, sd->orig_mode);
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+			u8 *data,
+			int len)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	/* Every now and then the camera sends a 16 byte packet, no idea
+	   what it contains, but it is not image data, when this
+	   happens the frame received before this packet is corrupt,
+	   so discard it. */
+	if (len != sd->mode.sizeimage) {
+		gspca_dev->last_packet_type = DISCARD_PACKET;
+		return;
+	}
+
+	/* Finish the previous frame, we do this upon reception of the next
+	   packet, even though it is already complete so that the strange 16
+	   byte packets send after a corrupt frame can discard it. */
+	gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+
+	/* Store the just received frame */
+	gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+	.name = MODULE_NAME,
+	.config = sd_config,
+	.init = sd_init,
+	.start = sd_start,
+	.stopN = sd_stopN,
+	.stop0 = sd_stop0,
+	.pkt_scan = sd_pkt_scan,
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+	{USB_DEVICE(0x0553, 0x0202)},
+	{USB_DEVICE(0x041e, 0x4007)},
+	{}
+};
+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,
+#ifdef CONFIG_PM
+	.suspend = gspca_suspend,
+	.resume = gspca_resume,
+	.reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/usb/gspca/stv06xx/Kconfig b/drivers/media/usb/gspca/stv06xx/Kconfig
new file mode 100644
index 0000000..634ad38
--- /dev/null
+++ b/drivers/media/usb/gspca/stv06xx/Kconfig
@@ -0,0 +1,9 @@
+config USB_STV06XX
+	tristate "STV06XX USB Camera Driver"
+	depends on USB_GSPCA
+	help
+	  Say Y here if you want support for cameras based on
+	  the ST STV06XX chip.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gspca_stv06xx.
diff --git a/drivers/media/usb/gspca/stv06xx/Makefile b/drivers/media/usb/gspca/stv06xx/Makefile
new file mode 100644
index 0000000..3a4b2f8
--- /dev/null
+++ b/drivers/media/usb/gspca/stv06xx/Makefile
@@ -0,0 +1,10 @@
+obj-$(CONFIG_USB_STV06XX) += gspca_stv06xx.o
+
+gspca_stv06xx-objs := stv06xx.o \
+		      stv06xx_vv6410.o \
+		      stv06xx_hdcs.o \
+		      stv06xx_pb0100.o \
+		      stv06xx_st6422.o
+
+ccflags-y += -I$(srctree)/drivers/media/usb/gspca
+
diff --git a/drivers/media/usb/gspca/stv06xx/stv06xx.c b/drivers/media/usb/gspca/stv06xx/stv06xx.c
new file mode 100644
index 0000000..6ac93d8
--- /dev/null
+++ b/drivers/media/usb/gspca/stv06xx/stv06xx.c
@@ -0,0 +1,639 @@
+/*
+ * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
+ *		      Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
+ * Copyright (c) 2002, 2003 Tuukka Toivonen
+ * Copyright (c) 2008 Erik Andrén
+ *
+ * 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
+ *
+ * P/N 861037:      Sensor HDCS1000        ASIC STV0600
+ * P/N 861050-0010: Sensor HDCS1000        ASIC STV0600
+ * P/N 861050-0020: Sensor Photobit PB100  ASIC STV0600-1 - QuickCam Express
+ * P/N 861055:      Sensor ST VV6410       ASIC STV0610   - LEGO cam
+ * P/N 861075-0040: Sensor HDCS1000        ASIC
+ * P/N 961179-0700: Sensor ST VV6410       ASIC STV0602   - Dexxa WebCam USB
+ * P/N 861040-0000: Sensor ST VV6410       ASIC STV0610   - QuickCam Web
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/input.h>
+#include "stv06xx_sensor.h"
+
+MODULE_AUTHOR("Erik Andrén");
+MODULE_DESCRIPTION("STV06XX USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+static bool dump_bridge;
+static bool dump_sensor;
+
+int stv06xx_write_bridge(struct sd *sd, u16 address, u16 i2c_data)
+{
+	int err;
+	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
+	struct usb_device *udev = sd->gspca_dev.dev;
+	__u8 *buf = sd->gspca_dev.usb_buf;
+
+	u8 len = (i2c_data > 0xff) ? 2 : 1;
+
+	buf[0] = i2c_data & 0xff;
+	buf[1] = (i2c_data >> 8) & 0xff;
+
+	err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+			      0x04, 0x40, address, 0, buf, len,
+			      STV06XX_URB_MSG_TIMEOUT);
+
+	PDEBUG(D_CONF, "Written 0x%x to address 0x%x, status: %d",
+	       i2c_data, address, err);
+
+	return (err < 0) ? err : 0;
+}
+
+int stv06xx_read_bridge(struct sd *sd, u16 address, u8 *i2c_data)
+{
+	int err;
+	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
+	struct usb_device *udev = sd->gspca_dev.dev;
+	__u8 *buf = sd->gspca_dev.usb_buf;
+
+	err = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+			      0x04, 0xc0, address, 0, buf, 1,
+			      STV06XX_URB_MSG_TIMEOUT);
+
+	*i2c_data = buf[0];
+
+	PDEBUG(D_CONF, "Reading 0x%x from address 0x%x, status %d",
+	       *i2c_data, address, err);
+
+	return (err < 0) ? err : 0;
+}
+
+/* Wraps the normal write sensor bytes / words functions for writing a
+   single value */
+int stv06xx_write_sensor(struct sd *sd, u8 address, u16 value)
+{
+	if (sd->sensor->i2c_len == 2) {
+		u16 data[2] = { address, value };
+		return stv06xx_write_sensor_words(sd, data, 1);
+	} else {
+		u8 data[2] = { address, value };
+		return stv06xx_write_sensor_bytes(sd, data, 1);
+	}
+}
+
+static int stv06xx_write_sensor_finish(struct sd *sd)
+{
+	int err = 0;
+
+	if (sd->bridge == BRIDGE_STV610) {
+		struct usb_device *udev = sd->gspca_dev.dev;
+		__u8 *buf = sd->gspca_dev.usb_buf;
+
+		buf[0] = 0;
+		err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+				      0x04, 0x40, 0x1704, 0, buf, 1,
+				      STV06XX_URB_MSG_TIMEOUT);
+	}
+
+	return (err < 0) ? err : 0;
+}
+
+int stv06xx_write_sensor_bytes(struct sd *sd, const u8 *data, u8 len)
+{
+	int err, i, j;
+	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
+	struct usb_device *udev = sd->gspca_dev.dev;
+	__u8 *buf = sd->gspca_dev.usb_buf;
+
+	PDEBUG(D_CONF, "I2C: Command buffer contains %d entries", len);
+	for (i = 0; i < len;) {
+		/* Build the command buffer */
+		memset(buf, 0, I2C_BUFFER_LENGTH);
+		for (j = 0; j < I2C_MAX_BYTES && i < len; j++, i++) {
+			buf[j] = data[2*i];
+			buf[0x10 + j] = data[2*i+1];
+			PDEBUG(D_CONF, "I2C: Writing 0x%02x to reg 0x%02x",
+			data[2*i+1], data[2*i]);
+		}
+		buf[0x20] = sd->sensor->i2c_addr;
+		buf[0x21] = j - 1; /* Number of commands to send - 1 */
+		buf[0x22] = I2C_WRITE_CMD;
+		err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+				      0x04, 0x40, 0x0400, 0, buf,
+				      I2C_BUFFER_LENGTH,
+				      STV06XX_URB_MSG_TIMEOUT);
+		if (err < 0)
+			return err;
+	}
+	return stv06xx_write_sensor_finish(sd);
+}
+
+int stv06xx_write_sensor_words(struct sd *sd, const u16 *data, u8 len)
+{
+	int err, i, j;
+	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
+	struct usb_device *udev = sd->gspca_dev.dev;
+	__u8 *buf = sd->gspca_dev.usb_buf;
+
+	PDEBUG(D_CONF, "I2C: Command buffer contains %d entries", len);
+
+	for (i = 0; i < len;) {
+		/* Build the command buffer */
+		memset(buf, 0, I2C_BUFFER_LENGTH);
+		for (j = 0; j < I2C_MAX_WORDS && i < len; j++, i++) {
+			buf[j] = data[2*i];
+			buf[0x10 + j * 2] = data[2*i+1];
+			buf[0x10 + j * 2 + 1] = data[2*i+1] >> 8;
+			PDEBUG(D_CONF, "I2C: Writing 0x%04x to reg 0x%02x",
+				data[2*i+1], data[2*i]);
+		}
+		buf[0x20] = sd->sensor->i2c_addr;
+		buf[0x21] = j - 1; /* Number of commands to send - 1 */
+		buf[0x22] = I2C_WRITE_CMD;
+		err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+				0x04, 0x40, 0x0400, 0, buf,
+				I2C_BUFFER_LENGTH,
+				STV06XX_URB_MSG_TIMEOUT);
+		if (err < 0)
+			return err;
+	}
+	return stv06xx_write_sensor_finish(sd);
+}
+
+int stv06xx_read_sensor(struct sd *sd, const u8 address, u16 *value)
+{
+	int err;
+	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
+	struct usb_device *udev = sd->gspca_dev.dev;
+	__u8 *buf = sd->gspca_dev.usb_buf;
+
+	err = stv06xx_write_bridge(sd, STV_I2C_FLUSH, sd->sensor->i2c_flush);
+	if (err < 0)
+		return err;
+
+	/* Clear mem */
+	memset(buf, 0, I2C_BUFFER_LENGTH);
+
+	buf[0] = address;
+	buf[0x20] = sd->sensor->i2c_addr;
+	buf[0x21] = 0;
+
+	/* Read I2C register */
+	buf[0x22] = I2C_READ_CMD;
+
+	err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+			      0x04, 0x40, 0x1400, 0, buf, I2C_BUFFER_LENGTH,
+			      STV06XX_URB_MSG_TIMEOUT);
+	if (err < 0) {
+		pr_err("I2C: Read error writing address: %d\n", err);
+		return err;
+	}
+
+	err = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+			      0x04, 0xc0, 0x1410, 0, buf, sd->sensor->i2c_len,
+			      STV06XX_URB_MSG_TIMEOUT);
+	if (sd->sensor->i2c_len == 2)
+		*value = buf[0] | (buf[1] << 8);
+	else
+		*value = buf[0];
+
+	PDEBUG(D_CONF, "I2C: Read 0x%x from address 0x%x, status: %d",
+	       *value, address, err);
+
+	return (err < 0) ? err : 0;
+}
+
+/* Dumps all bridge registers */
+static void stv06xx_dump_bridge(struct sd *sd)
+{
+	int i;
+	u8 data, buf;
+
+	pr_info("Dumping all stv06xx bridge registers\n");
+	for (i = 0x1400; i < 0x160f; i++) {
+		stv06xx_read_bridge(sd, i, &data);
+
+		pr_info("Read 0x%x from address 0x%x\n", data, i);
+	}
+
+	pr_info("Testing stv06xx bridge registers for writability\n");
+	for (i = 0x1400; i < 0x160f; i++) {
+		stv06xx_read_bridge(sd, i, &data);
+		buf = data;
+
+		stv06xx_write_bridge(sd, i, 0xff);
+		stv06xx_read_bridge(sd, i, &data);
+		if (data == 0xff)
+			pr_info("Register 0x%x is read/write\n", i);
+		else if (data != buf)
+			pr_info("Register 0x%x is read/write, but only partially\n",
+				i);
+		else
+			pr_info("Register 0x%x is read-only\n", i);
+
+		stv06xx_write_bridge(sd, i, buf);
+	}
+}
+
+/* this function is called at probe and resume time */
+static int stv06xx_init(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int err;
+
+	PDEBUG(D_PROBE, "Initializing camera");
+
+	/* Let the usb init settle for a bit
+	   before performing the initialization */
+	msleep(250);
+
+	err = sd->sensor->init(sd);
+
+	if (dump_sensor && sd->sensor->dump)
+		sd->sensor->dump(sd);
+
+	return (err < 0) ? err : 0;
+}
+
+/* this function is called at probe time */
+static int stv06xx_init_controls(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	PDEBUG(D_PROBE, "Initializing controls");
+
+	gspca_dev->vdev.ctrl_handler = &gspca_dev->ctrl_handler;
+	return sd->sensor->init_controls(sd);
+}
+
+/* Start the camera */
+static int stv06xx_start(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct usb_host_interface *alt;
+	struct usb_interface *intf;
+	int err, packet_size;
+
+	intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface);
+	alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt);
+	if (!alt) {
+		PERR("Couldn't get altsetting");
+		return -EIO;
+	}
+
+	packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize);
+	err = stv06xx_write_bridge(sd, STV_ISO_SIZE_L, packet_size);
+	if (err < 0)
+		return err;
+
+	/* Prepare the sensor for start */
+	err = sd->sensor->start(sd);
+	if (err < 0)
+		goto out;
+
+	/* Start isochronous streaming */
+	err = stv06xx_write_bridge(sd, STV_ISO_ENABLE, 1);
+
+out:
+	if (err < 0)
+		PDEBUG(D_STREAM, "Starting stream failed");
+	else
+		PDEBUG(D_STREAM, "Started streaming");
+
+	return (err < 0) ? err : 0;
+}
+
+static int stv06xx_isoc_init(struct gspca_dev *gspca_dev)
+{
+	struct usb_host_interface *alt;
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	/* Start isoc bandwidth "negotiation" at max isoc bandwidth */
+	alt = &gspca_dev->dev->actconfig->intf_cache[0]->altsetting[1];
+	alt->endpoint[0].desc.wMaxPacketSize =
+		cpu_to_le16(sd->sensor->max_packet_size[gspca_dev->curr_mode]);
+
+	return 0;
+}
+
+static int stv06xx_isoc_nego(struct gspca_dev *gspca_dev)
+{
+	int ret, packet_size, min_packet_size;
+	struct usb_host_interface *alt;
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	alt = &gspca_dev->dev->actconfig->intf_cache[0]->altsetting[1];
+	packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize);
+	min_packet_size = sd->sensor->min_packet_size[gspca_dev->curr_mode];
+	if (packet_size <= min_packet_size)
+		return -EIO;
+
+	packet_size -= 100;
+	if (packet_size < min_packet_size)
+		packet_size = min_packet_size;
+	alt->endpoint[0].desc.wMaxPacketSize = cpu_to_le16(packet_size);
+
+	ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, 1);
+	if (ret < 0)
+		PERR("set alt 1 err %d", ret);
+
+	return ret;
+}
+
+static void stv06xx_stopN(struct gspca_dev *gspca_dev)
+{
+	int err;
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	/* stop ISO-streaming */
+	err = stv06xx_write_bridge(sd, STV_ISO_ENABLE, 0);
+	if (err < 0)
+		goto out;
+
+	err = sd->sensor->stop(sd);
+
+out:
+	if (err < 0)
+		PDEBUG(D_STREAM, "Failed to stop stream");
+	else
+		PDEBUG(D_STREAM, "Stopped streaming");
+}
+
+/*
+ * Analyse an USB packet of the data stream and store it appropriately.
+ * Each packet contains an integral number of chunks. Each chunk has
+ * 2-bytes identification, followed by 2-bytes that describe the chunk
+ * length. Known/guessed chunk identifications are:
+ * 8001/8005/C001/C005 - Begin new frame
+ * 8002/8006/C002/C006 - End frame
+ * 0200/4200           - Contains actual image data, bayer or compressed
+ * 0005                - 11 bytes of unknown data
+ * 0100                - 2 bytes of unknown data
+ * The 0005 and 0100 chunks seem to appear only in compressed stream.
+ */
+static void stv06xx_pkt_scan(struct gspca_dev *gspca_dev,
+			u8 *data,			/* isoc packet */
+			int len)			/* iso packet length */
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	PDEBUG(D_PACK, "Packet of length %d arrived", len);
+
+	/* A packet may contain several frames
+	   loop until the whole packet is reached */
+	while (len) {
+		int id, chunk_len;
+
+		if (len < 4) {
+			PDEBUG(D_PACK, "Packet is smaller than 4 bytes");
+			return;
+		}
+
+		/* Capture the id */
+		id = (data[0] << 8) | data[1];
+
+		/* Capture the chunk length */
+		chunk_len = (data[2] << 8) | data[3];
+		PDEBUG(D_PACK, "Chunk id: %x, length: %d", id, chunk_len);
+
+		data += 4;
+		len -= 4;
+
+		if (len < chunk_len) {
+			PERR("URB packet length is smaller"
+				" than the specified chunk length");
+			gspca_dev->last_packet_type = DISCARD_PACKET;
+			return;
+		}
+
+		/* First byte seem to be 02=data 2nd byte is unknown??? */
+		if (sd->bridge == BRIDGE_ST6422 && (id & 0xff00) == 0x0200)
+			goto frame_data;
+
+		switch (id) {
+		case 0x0200:
+		case 0x4200:
+frame_data:
+			PDEBUG(D_PACK, "Frame data packet detected");
+
+			if (sd->to_skip) {
+				int skip = (sd->to_skip < chunk_len) ?
+					    sd->to_skip : chunk_len;
+				data += skip;
+				len -= skip;
+				chunk_len -= skip;
+				sd->to_skip -= skip;
+			}
+
+			gspca_frame_add(gspca_dev, INTER_PACKET,
+					data, chunk_len);
+			break;
+
+		case 0x8001:
+		case 0x8005:
+		case 0xc001:
+		case 0xc005:
+			PDEBUG(D_PACK, "Starting new frame");
+
+			/* Create a new frame, chunk length should be zero */
+			gspca_frame_add(gspca_dev, FIRST_PACKET,
+					NULL, 0);
+
+			if (sd->bridge == BRIDGE_ST6422)
+				sd->to_skip = gspca_dev->pixfmt.width * 4;
+
+			if (chunk_len)
+				PERR("Chunk length is "
+					      "non-zero on a SOF");
+			break;
+
+		case 0x8002:
+		case 0x8006:
+		case 0xc002:
+			PDEBUG(D_PACK, "End of frame detected");
+
+			/* Complete the last frame (if any) */
+			gspca_frame_add(gspca_dev, LAST_PACKET,
+					NULL, 0);
+
+			if (chunk_len)
+				PERR("Chunk length is "
+					      "non-zero on a EOF");
+			break;
+
+		case 0x0005:
+			PDEBUG(D_PACK, "Chunk 0x005 detected");
+			/* Unknown chunk with 11 bytes of data,
+			   occurs just before end of each frame
+			   in compressed mode */
+			break;
+
+		case 0x0100:
+			PDEBUG(D_PACK, "Chunk 0x0100 detected");
+			/* Unknown chunk with 2 bytes of data,
+			   occurs 2-3 times per USB interrupt */
+			break;
+		case 0x42ff:
+			PDEBUG(D_PACK, "Chunk 0x42ff detected");
+			/* Special chunk seen sometimes on the ST6422 */
+			break;
+		default:
+			PDEBUG(D_PACK, "Unknown chunk 0x%04x detected", id);
+			/* Unknown chunk */
+		}
+		data    += chunk_len;
+		len     -= chunk_len;
+	}
+}
+
+#if IS_ENABLED(CONFIG_INPUT)
+static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
+			u8 *data,		/* interrupt packet data */
+			int len)		/* interrupt packet length */
+{
+	int ret = -EINVAL;
+
+	if (len == 1 && (data[0] == 0x80 || data[0] == 0x10)) {
+		input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1);
+		input_sync(gspca_dev->input_dev);
+		ret = 0;
+	}
+
+	if (len == 1 && (data[0] == 0x88 || data[0] == 0x11)) {
+		input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
+		input_sync(gspca_dev->input_dev);
+		ret = 0;
+	}
+
+	return ret;
+}
+#endif
+
+static int stv06xx_config(struct gspca_dev *gspca_dev,
+			  const struct usb_device_id *id);
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+	.name = MODULE_NAME,
+	.config = stv06xx_config,
+	.init = stv06xx_init,
+	.init_controls = stv06xx_init_controls,
+	.start = stv06xx_start,
+	.stopN = stv06xx_stopN,
+	.pkt_scan = stv06xx_pkt_scan,
+	.isoc_init = stv06xx_isoc_init,
+	.isoc_nego = stv06xx_isoc_nego,
+#if IS_ENABLED(CONFIG_INPUT)
+	.int_pkt_scan = sd_int_pkt_scan,
+#endif
+};
+
+/* This function is called at probe time */
+static int stv06xx_config(struct gspca_dev *gspca_dev,
+			  const struct usb_device_id *id)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	PDEBUG(D_PROBE, "Configuring camera");
+
+	sd->bridge = id->driver_info;
+	gspca_dev->sd_desc = &sd_desc;
+
+	if (dump_bridge)
+		stv06xx_dump_bridge(sd);
+
+	sd->sensor = &stv06xx_sensor_st6422;
+	if (!sd->sensor->probe(sd))
+		return 0;
+
+	sd->sensor = &stv06xx_sensor_vv6410;
+	if (!sd->sensor->probe(sd))
+		return 0;
+
+	sd->sensor = &stv06xx_sensor_hdcs1x00;
+	if (!sd->sensor->probe(sd))
+		return 0;
+
+	sd->sensor = &stv06xx_sensor_hdcs1020;
+	if (!sd->sensor->probe(sd))
+		return 0;
+
+	sd->sensor = &stv06xx_sensor_pb0100;
+	if (!sd->sensor->probe(sd))
+		return 0;
+
+	sd->sensor = NULL;
+	return -ENODEV;
+}
+
+
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+	/* QuickCam Express */
+	{USB_DEVICE(0x046d, 0x0840), .driver_info = BRIDGE_STV600 },
+	/* LEGO cam / QuickCam Web */
+	{USB_DEVICE(0x046d, 0x0850), .driver_info = BRIDGE_STV610 },
+	/* Dexxa WebCam USB */
+	{USB_DEVICE(0x046d, 0x0870), .driver_info = BRIDGE_STV602 },
+	/* QuickCam Messenger */
+	{USB_DEVICE(0x046D, 0x08F0), .driver_info = BRIDGE_ST6422 },
+	/* QuickCam Communicate */
+	{USB_DEVICE(0x046D, 0x08F5), .driver_info = BRIDGE_ST6422 },
+	/* QuickCam Messenger (new) */
+	{USB_DEVICE(0x046D, 0x08F6), .driver_info = BRIDGE_ST6422 },
+	{}
+};
+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 void sd_disconnect(struct usb_interface *intf)
+{
+	struct gspca_dev *gspca_dev = usb_get_intfdata(intf);
+	struct sd *sd = (struct sd *) gspca_dev;
+	void *priv = sd->sensor_priv;
+	PDEBUG(D_PROBE, "Disconnecting the stv06xx device");
+
+	sd->sensor = NULL;
+	gspca_disconnect(intf);
+	kfree(priv);
+}
+
+static struct usb_driver sd_driver = {
+	.name = MODULE_NAME,
+	.id_table = device_table,
+	.probe = sd_probe,
+	.disconnect = sd_disconnect,
+#ifdef CONFIG_PM
+	.suspend = gspca_suspend,
+	.resume = gspca_resume,
+	.reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
+
+module_param(dump_bridge, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(dump_bridge, "Dumps all usb bridge registers at startup");
+
+module_param(dump_sensor, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(dump_sensor, "Dumps all sensor registers at startup");
diff --git a/drivers/media/usb/gspca/stv06xx/stv06xx.h b/drivers/media/usb/gspca/stv06xx/stv06xx.h
new file mode 100644
index 0000000..34957a4
--- /dev/null
+++ b/drivers/media/usb/gspca/stv06xx/stv06xx.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
+ *		      Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
+ * Copyright (c) 2002, 2003 Tuukka Toivonen
+ * Copyright (c) 2008 Erik Andrén
+ *
+ * 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
+ *
+ * P/N 861037:      Sensor HDCS1000        ASIC STV0600
+ * P/N 861050-0010: Sensor HDCS1000        ASIC STV0600
+ * P/N 861050-0020: Sensor Photobit PB100  ASIC STV0600-1 - QuickCam Express
+ * P/N 861055:      Sensor ST VV6410       ASIC STV0610   - LEGO cam
+ * P/N 861075-0040: Sensor HDCS1000        ASIC
+ * P/N 961179-0700: Sensor ST VV6410       ASIC STV0602   - Dexxa WebCam USB
+ * P/N 861040-0000: Sensor ST VV6410       ASIC STV0610   - QuickCam Web
+ */
+
+#ifndef STV06XX_H_
+#define STV06XX_H_
+
+#include <linux/slab.h>
+#include "gspca.h"
+
+#define MODULE_NAME "STV06xx"
+
+#define STV_ISOC_ENDPOINT_ADDR		0x81
+
+#define STV_R                           0x0509
+
+#define STV_REG23			0x0423
+
+/* Control registers of the STV0600 ASIC */
+#define STV_I2C_PARTNER			0x1420
+#define STV_I2C_VAL_REG_VAL_PAIRS_MIN1	0x1421
+#define STV_I2C_READ_WRITE_TOGGLE	0x1422
+#define STV_I2C_FLUSH			0x1423
+#define STV_I2C_SUCC_READ_REG_VALS	0x1424
+
+#define STV_ISO_ENABLE			0x1440
+#define STV_SCAN_RATE			0x1443
+#define STV_LED_CTRL			0x1445
+#define STV_STV0600_EMULATION		0x1446
+#define STV_REG00			0x1500
+#define STV_REG01			0x1501
+#define STV_REG02			0x1502
+#define STV_REG03			0x1503
+#define STV_REG04			0x1504
+
+#define STV_ISO_SIZE_L			0x15c1
+#define STV_ISO_SIZE_H			0x15c2
+
+/* Refers to the CIF 352x288 and QCIF 176x144 */
+/* 1: 288 lines, 2: 144 lines */
+#define STV_Y_CTRL		        0x15c3
+
+#define STV_RESET                       0x1620
+
+/* 0xa: 352 columns, 0x6: 176 columns */
+#define STV_X_CTRL			0x1680
+
+#define STV06XX_URB_MSG_TIMEOUT		5000
+
+#define I2C_MAX_BYTES			16
+#define I2C_MAX_WORDS			8
+
+#define I2C_BUFFER_LENGTH		0x23
+#define I2C_READ_CMD			3
+#define I2C_WRITE_CMD			1
+
+#define LED_ON				1
+#define LED_OFF				0
+
+/* STV06xx device descriptor */
+struct sd {
+	struct gspca_dev gspca_dev;
+
+	/* A pointer to the currently connected sensor */
+	const struct stv06xx_sensor *sensor;
+
+	/* Sensor private data */
+	void *sensor_priv;
+
+	/* The first 4 lines produced by the stv6422 are no good, this keeps
+	   track of how many bytes we still need to skip during a frame */
+	int to_skip;
+
+	/* Bridge / Camera type */
+	u8 bridge;
+	#define BRIDGE_STV600 0
+	#define BRIDGE_STV602 1
+	#define BRIDGE_STV610 2
+	#define BRIDGE_ST6422 3 /* With integrated sensor */
+};
+
+int stv06xx_write_bridge(struct sd *sd, u16 address, u16 i2c_data);
+int stv06xx_read_bridge(struct sd *sd, u16 address, u8 *i2c_data);
+
+int stv06xx_write_sensor_bytes(struct sd *sd, const u8 *data, u8 len);
+int stv06xx_write_sensor_words(struct sd *sd, const u16 *data, u8 len);
+
+int stv06xx_read_sensor(struct sd *sd, const u8 address, u16 *value);
+int stv06xx_write_sensor(struct sd *sd, u8 address, u16 value);
+
+#endif
diff --git a/drivers/media/usb/gspca/stv06xx/stv06xx_hdcs.c b/drivers/media/usb/gspca/stv06xx/stv06xx_hdcs.c
new file mode 100644
index 0000000..2220b70
--- /dev/null
+++ b/drivers/media/usb/gspca/stv06xx/stv06xx_hdcs.c
@@ -0,0 +1,544 @@
+/*
+ * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
+ *		      Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
+ * Copyright (c) 2002, 2003 Tuukka Toivonen
+ * Copyright (c) 2008 Erik Andrén
+ * Copyright (c) 2008 Chia-I Wu
+ *
+ * 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
+ *
+ * P/N 861037:      Sensor HDCS1000        ASIC STV0600
+ * P/N 861050-0010: Sensor HDCS1000        ASIC STV0600
+ * P/N 861050-0020: Sensor Photobit PB100  ASIC STV0600-1 - QuickCam Express
+ * P/N 861055:      Sensor ST VV6410       ASIC STV0610   - LEGO cam
+ * P/N 861075-0040: Sensor HDCS1000        ASIC
+ * P/N 961179-0700: Sensor ST VV6410       ASIC STV0602   - Dexxa WebCam USB
+ * P/N 861040-0000: Sensor ST VV6410       ASIC STV0610   - QuickCam Web
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "stv06xx_hdcs.h"
+
+static struct v4l2_pix_format hdcs1x00_mode[] = {
+	{
+		HDCS_1X00_DEF_WIDTH,
+		HDCS_1X00_DEF_HEIGHT,
+		V4L2_PIX_FMT_SGRBG8,
+		V4L2_FIELD_NONE,
+		.sizeimage =
+			HDCS_1X00_DEF_WIDTH * HDCS_1X00_DEF_HEIGHT,
+		.bytesperline = HDCS_1X00_DEF_WIDTH,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 1
+	}
+};
+
+static struct v4l2_pix_format hdcs1020_mode[] = {
+	{
+		HDCS_1020_DEF_WIDTH,
+		HDCS_1020_DEF_HEIGHT,
+		V4L2_PIX_FMT_SGRBG8,
+		V4L2_FIELD_NONE,
+		.sizeimage =
+			HDCS_1020_DEF_WIDTH * HDCS_1020_DEF_HEIGHT,
+		.bytesperline = HDCS_1020_DEF_WIDTH,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 1
+	}
+};
+
+enum hdcs_power_state {
+	HDCS_STATE_SLEEP,
+	HDCS_STATE_IDLE,
+	HDCS_STATE_RUN
+};
+
+/* no lock? */
+struct hdcs {
+	enum hdcs_power_state state;
+	int w, h;
+
+	/* visible area of the sensor array */
+	struct {
+		int left, top;
+		int width, height;
+		int border;
+	} array;
+
+	struct {
+		/* Column timing overhead */
+		u8 cto;
+		/* Column processing overhead */
+		u8 cpo;
+		/* Row sample period constant */
+		u16 rs;
+		/* Exposure reset duration */
+		u16 er;
+	} exp;
+
+	int psmp;
+};
+
+static int hdcs_reg_write_seq(struct sd *sd, u8 reg, u8 *vals, u8 len)
+{
+	u8 regs[I2C_MAX_BYTES * 2];
+	int i;
+
+	if (unlikely((len <= 0) || (len >= I2C_MAX_BYTES) ||
+		     (reg + len > 0xff)))
+		return -EINVAL;
+
+	for (i = 0; i < len; i++) {
+		regs[2 * i] = reg;
+		regs[2 * i + 1] = vals[i];
+		/* All addresses are shifted left one bit
+		 * as bit 0 toggles r/w */
+		reg += 2;
+	}
+
+	return stv06xx_write_sensor_bytes(sd, regs, len);
+}
+
+static int hdcs_set_state(struct sd *sd, enum hdcs_power_state state)
+{
+	struct hdcs *hdcs = sd->sensor_priv;
+	u8 val;
+	int ret;
+
+	if (hdcs->state == state)
+		return 0;
+
+	/* we need to go idle before running or sleeping */
+	if (hdcs->state != HDCS_STATE_IDLE) {
+		ret = stv06xx_write_sensor(sd, HDCS_REG_CONTROL(sd), 0);
+		if (ret)
+			return ret;
+	}
+
+	hdcs->state = HDCS_STATE_IDLE;
+
+	if (state == HDCS_STATE_IDLE)
+		return 0;
+
+	switch (state) {
+	case HDCS_STATE_SLEEP:
+		val = HDCS_SLEEP_MODE;
+		break;
+
+	case HDCS_STATE_RUN:
+		val = HDCS_RUN_ENABLE;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	ret = stv06xx_write_sensor(sd, HDCS_REG_CONTROL(sd), val);
+
+	/* Update the state if the write succeeded */
+	if (!ret)
+		hdcs->state = state;
+
+	return ret;
+}
+
+static int hdcs_reset(struct sd *sd)
+{
+	struct hdcs *hdcs = sd->sensor_priv;
+	int err;
+
+	err = stv06xx_write_sensor(sd, HDCS_REG_CONTROL(sd), 1);
+	if (err < 0)
+		return err;
+
+	err = stv06xx_write_sensor(sd, HDCS_REG_CONTROL(sd), 0);
+	if (err < 0)
+		hdcs->state = HDCS_STATE_IDLE;
+
+	return err;
+}
+
+static int hdcs_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct hdcs *hdcs = sd->sensor_priv;
+	int rowexp, srowexp;
+	int max_srowexp;
+	/* Column time period */
+	int ct;
+	/* Column processing period */
+	int cp;
+	/* Row processing period */
+	int rp;
+	/* Minimum number of column timing periods
+	   within the column processing period */
+	int mnct;
+	int cycles, err;
+	u8 exp[14];
+
+	cycles = val * HDCS_CLK_FREQ_MHZ * 257;
+
+	ct = hdcs->exp.cto + hdcs->psmp + (HDCS_ADC_START_SIG_DUR + 2);
+	cp = hdcs->exp.cto + (hdcs->w * ct / 2);
+
+	/* the cycles one row takes */
+	rp = hdcs->exp.rs + cp;
+
+	rowexp = cycles / rp;
+
+	/* the remaining cycles */
+	cycles -= rowexp * rp;
+
+	/* calculate sub-row exposure */
+	if (IS_1020(sd)) {
+		/* see HDCS-1020 datasheet 3.5.6.4, p. 63 */
+		srowexp = hdcs->w - (cycles + hdcs->exp.er + 13) / ct;
+
+		mnct = (hdcs->exp.er + 12 + ct - 1) / ct;
+		max_srowexp = hdcs->w - mnct;
+	} else {
+		/* see HDCS-1000 datasheet 3.4.5.5, p. 61 */
+		srowexp = cp - hdcs->exp.er - 6 - cycles;
+
+		mnct = (hdcs->exp.er + 5 + ct - 1) / ct;
+		max_srowexp = cp - mnct * ct - 1;
+	}
+
+	if (srowexp < 0)
+		srowexp = 0;
+	else if (srowexp > max_srowexp)
+		srowexp = max_srowexp;
+
+	if (IS_1020(sd)) {
+		exp[0] = HDCS20_CONTROL;
+		exp[1] = 0x00;		/* Stop streaming */
+		exp[2] = HDCS_ROWEXPL;
+		exp[3] = rowexp & 0xff;
+		exp[4] = HDCS_ROWEXPH;
+		exp[5] = rowexp >> 8;
+		exp[6] = HDCS20_SROWEXP;
+		exp[7] = (srowexp >> 2) & 0xff;
+		exp[8] = HDCS20_ERROR;
+		exp[9] = 0x10;		/* Clear exposure error flag*/
+		exp[10] = HDCS20_CONTROL;
+		exp[11] = 0x04;		/* Restart streaming */
+		err = stv06xx_write_sensor_bytes(sd, exp, 6);
+	} else {
+		exp[0] = HDCS00_CONTROL;
+		exp[1] = 0x00;         /* Stop streaming */
+		exp[2] = HDCS_ROWEXPL;
+		exp[3] = rowexp & 0xff;
+		exp[4] = HDCS_ROWEXPH;
+		exp[5] = rowexp >> 8;
+		exp[6] = HDCS00_SROWEXPL;
+		exp[7] = srowexp & 0xff;
+		exp[8] = HDCS00_SROWEXPH;
+		exp[9] = srowexp >> 8;
+		exp[10] = HDCS_STATUS;
+		exp[11] = 0x10;         /* Clear exposure error flag*/
+		exp[12] = HDCS00_CONTROL;
+		exp[13] = 0x04;         /* Restart streaming */
+		err = stv06xx_write_sensor_bytes(sd, exp, 7);
+		if (err < 0)
+			return err;
+	}
+	PDEBUG(D_CONF, "Writing exposure %d, rowexp %d, srowexp %d",
+	       val, rowexp, srowexp);
+	return err;
+}
+
+static int hdcs_set_gains(struct sd *sd, u8 g)
+{
+	int err;
+	u8 gains[4];
+
+	/* the voltage gain Av = (1 + 19 * val / 127) * (1 + bit7) */
+	if (g > 127)
+		g = 0x80 | (g / 2);
+
+	gains[0] = g;
+	gains[1] = g;
+	gains[2] = g;
+	gains[3] = g;
+
+	err = hdcs_reg_write_seq(sd, HDCS_ERECPGA, gains, 4);
+	return err;
+}
+
+static int hdcs_set_gain(struct gspca_dev *gspca_dev, __s32 val)
+{
+	PDEBUG(D_CONF, "Writing gain %d", val);
+	return hdcs_set_gains((struct sd *) gspca_dev,
+			       val & 0xff);
+}
+
+static int hdcs_set_size(struct sd *sd,
+		unsigned int width, unsigned int height)
+{
+	struct hdcs *hdcs = sd->sensor_priv;
+	u8 win[4];
+	unsigned int x, y;
+	int err;
+
+	/* must be multiple of 4 */
+	width = (width + 3) & ~0x3;
+	height = (height + 3) & ~0x3;
+
+	if (width > hdcs->array.width)
+		width = hdcs->array.width;
+
+	if (IS_1020(sd)) {
+		/* the borders are also invalid */
+		if (height + 2 * hdcs->array.border + HDCS_1020_BOTTOM_Y_SKIP
+				  > hdcs->array.height)
+			height = hdcs->array.height - 2 * hdcs->array.border -
+				HDCS_1020_BOTTOM_Y_SKIP;
+
+		y = (hdcs->array.height - HDCS_1020_BOTTOM_Y_SKIP - height) / 2
+				+ hdcs->array.top;
+	} else {
+		if (height > hdcs->array.height)
+			height = hdcs->array.height;
+
+		y = hdcs->array.top + (hdcs->array.height - height) / 2;
+	}
+
+	x = hdcs->array.left + (hdcs->array.width - width) / 2;
+
+	win[0] = y / 4;
+	win[1] = x / 4;
+	win[2] = (y + height) / 4 - 1;
+	win[3] = (x + width) / 4 - 1;
+
+	err = hdcs_reg_write_seq(sd, HDCS_FWROW, win, 4);
+	if (err < 0)
+		return err;
+
+	/* Update the current width and height */
+	hdcs->w = width;
+	hdcs->h = height;
+	return err;
+}
+
+static int hdcs_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct gspca_dev *gspca_dev =
+		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+	int err = -EINVAL;
+
+	switch (ctrl->id) {
+	case V4L2_CID_GAIN:
+		err = hdcs_set_gain(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_EXPOSURE:
+		err = hdcs_set_exposure(gspca_dev, ctrl->val);
+		break;
+	}
+	return err;
+}
+
+static const struct v4l2_ctrl_ops hdcs_ctrl_ops = {
+	.s_ctrl = hdcs_s_ctrl,
+};
+
+static int hdcs_init_controls(struct sd *sd)
+{
+	struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler;
+
+	v4l2_ctrl_handler_init(hdl, 2);
+	v4l2_ctrl_new_std(hdl, &hdcs_ctrl_ops,
+			V4L2_CID_EXPOSURE, 0, 0xff, 1, HDCS_DEFAULT_EXPOSURE);
+	v4l2_ctrl_new_std(hdl, &hdcs_ctrl_ops,
+			V4L2_CID_GAIN, 0, 0xff, 1, HDCS_DEFAULT_GAIN);
+	return hdl->error;
+}
+
+static int hdcs_probe_1x00(struct sd *sd)
+{
+	struct hdcs *hdcs;
+	u16 sensor;
+	int ret;
+
+	ret = stv06xx_read_sensor(sd, HDCS_IDENT, &sensor);
+	if (ret < 0 || sensor != 0x08)
+		return -ENODEV;
+
+	pr_info("HDCS-1000/1100 sensor detected\n");
+
+	sd->gspca_dev.cam.cam_mode = hdcs1x00_mode;
+	sd->gspca_dev.cam.nmodes = ARRAY_SIZE(hdcs1x00_mode);
+
+	hdcs = kmalloc(sizeof(struct hdcs), GFP_KERNEL);
+	if (!hdcs)
+		return -ENOMEM;
+
+	hdcs->array.left = 8;
+	hdcs->array.top = 8;
+	hdcs->array.width = HDCS_1X00_DEF_WIDTH;
+	hdcs->array.height = HDCS_1X00_DEF_HEIGHT;
+	hdcs->array.border = 4;
+
+	hdcs->exp.cto = 4;
+	hdcs->exp.cpo = 2;
+	hdcs->exp.rs = 186;
+	hdcs->exp.er = 100;
+
+	/*
+	 * Frame rate on HDCS-1000 with STV600 depends on PSMP:
+	 *  4 = doesn't work at all
+	 *  5 = 7.8 fps,
+	 *  6 = 6.9 fps,
+	 *  8 = 6.3 fps,
+	 * 10 = 5.5 fps,
+	 * 15 = 4.4 fps,
+	 * 31 = 2.8 fps
+	 *
+	 * Frame rate on HDCS-1000 with STV602 depends on PSMP:
+	 * 15 = doesn't work at all
+	 * 18 = doesn't work at all
+	 * 19 = 7.3 fps
+	 * 20 = 7.4 fps
+	 * 21 = 7.4 fps
+	 * 22 = 7.4 fps
+	 * 24 = 6.3 fps
+	 * 30 = 5.4 fps
+	 */
+	hdcs->psmp = (sd->bridge == BRIDGE_STV602) ? 20 : 5;
+
+	sd->sensor_priv = hdcs;
+
+	return 0;
+}
+
+static int hdcs_probe_1020(struct sd *sd)
+{
+	struct hdcs *hdcs;
+	u16 sensor;
+	int ret;
+
+	ret = stv06xx_read_sensor(sd, HDCS_IDENT, &sensor);
+	if (ret < 0 || sensor != 0x10)
+		return -ENODEV;
+
+	pr_info("HDCS-1020 sensor detected\n");
+
+	sd->gspca_dev.cam.cam_mode = hdcs1020_mode;
+	sd->gspca_dev.cam.nmodes = ARRAY_SIZE(hdcs1020_mode);
+
+	hdcs = kmalloc(sizeof(struct hdcs), GFP_KERNEL);
+	if (!hdcs)
+		return -ENOMEM;
+
+	/*
+	 * From Andrey's test image: looks like HDCS-1020 upper-left
+	 * visible pixel is at 24,8 (y maybe even smaller?) and lower-right
+	 * visible pixel at 375,299 (x maybe even larger?)
+	 */
+	hdcs->array.left = 24;
+	hdcs->array.top  = 4;
+	hdcs->array.width = HDCS_1020_DEF_WIDTH;
+	hdcs->array.height = 304;
+	hdcs->array.border = 4;
+
+	hdcs->psmp = 6;
+
+	hdcs->exp.cto = 3;
+	hdcs->exp.cpo = 3;
+	hdcs->exp.rs = 155;
+	hdcs->exp.er = 96;
+
+	sd->sensor_priv = hdcs;
+
+	return 0;
+}
+
+static int hdcs_start(struct sd *sd)
+{
+	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
+
+	PDEBUG(D_STREAM, "Starting stream");
+
+	return hdcs_set_state(sd, HDCS_STATE_RUN);
+}
+
+static int hdcs_stop(struct sd *sd)
+{
+	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
+
+	PDEBUG(D_STREAM, "Halting stream");
+
+	return hdcs_set_state(sd, HDCS_STATE_SLEEP);
+}
+
+static int hdcs_init(struct sd *sd)
+{
+	struct hdcs *hdcs = sd->sensor_priv;
+	int i, err = 0;
+
+	/* Set the STV0602AA in STV0600 emulation mode */
+	if (sd->bridge == BRIDGE_STV602)
+		stv06xx_write_bridge(sd, STV_STV0600_EMULATION, 1);
+
+	/* Execute the bridge init */
+	for (i = 0; i < ARRAY_SIZE(stv_bridge_init) && !err; i++) {
+		err = stv06xx_write_bridge(sd, stv_bridge_init[i][0],
+					   stv_bridge_init[i][1]);
+	}
+	if (err < 0)
+		return err;
+
+	/* sensor soft reset */
+	hdcs_reset(sd);
+
+	/* Execute the sensor init */
+	for (i = 0; i < ARRAY_SIZE(stv_sensor_init) && !err; i++) {
+		err = stv06xx_write_sensor(sd, stv_sensor_init[i][0],
+					     stv_sensor_init[i][1]);
+	}
+	if (err < 0)
+		return err;
+
+	/* Enable continuous frame capture, bit 2: stop when frame complete */
+	err = stv06xx_write_sensor(sd, HDCS_REG_CONFIG(sd), BIT(3));
+	if (err < 0)
+		return err;
+
+	/* Set PGA sample duration
+	(was 0x7E for the STV602, but caused slow framerate with HDCS-1020) */
+	if (IS_1020(sd))
+		err = stv06xx_write_sensor(sd, HDCS_TCTRL,
+				(HDCS_ADC_START_SIG_DUR << 6) | hdcs->psmp);
+	else
+		err = stv06xx_write_sensor(sd, HDCS_TCTRL,
+				(HDCS_ADC_START_SIG_DUR << 5) | hdcs->psmp);
+	if (err < 0)
+		return err;
+
+	return hdcs_set_size(sd, hdcs->array.width, hdcs->array.height);
+}
+
+static int hdcs_dump(struct sd *sd)
+{
+	u16 reg, val;
+
+	pr_info("Dumping sensor registers:\n");
+
+	for (reg = HDCS_IDENT; reg <= HDCS_ROWEXPH; reg++) {
+		stv06xx_read_sensor(sd, reg, &val);
+		pr_info("reg 0x%02x = 0x%02x\n", reg, val);
+	}
+	return 0;
+}
diff --git a/drivers/media/usb/gspca/stv06xx/stv06xx_hdcs.h b/drivers/media/usb/gspca/stv06xx/stv06xx_hdcs.h
new file mode 100644
index 0000000..1ba9158
--- /dev/null
+++ b/drivers/media/usb/gspca/stv06xx/stv06xx_hdcs.h
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
+ *		      Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
+ * Copyright (c) 2002, 2003 Tuukka Toivonen
+ * Copyright (c) 2008 Erik Andrén
+ * Copyright (c) 2008 Chia-I Wu
+ *
+ * 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
+ *
+ * P/N 861037:      Sensor HDCS1000        ASIC STV0600
+ * P/N 861050-0010: Sensor HDCS1000        ASIC STV0600
+ * P/N 861050-0020: Sensor Photobit PB100  ASIC STV0600-1 - QuickCam Express
+ * P/N 861055:      Sensor ST VV6410       ASIC STV0610   - LEGO cam
+ * P/N 861075-0040: Sensor HDCS1000        ASIC
+ * P/N 961179-0700: Sensor ST VV6410       ASIC STV0602   - Dexxa WebCam USB
+ * P/N 861040-0000: Sensor ST VV6410       ASIC STV0610   - QuickCam Web
+ */
+
+#ifndef STV06XX_HDCS_H_
+#define STV06XX_HDCS_H_
+
+#include "stv06xx_sensor.h"
+
+#define HDCS_REG_CONFIG(sd)	(IS_1020(sd) ? HDCS20_CONFIG : HDCS00_CONFIG)
+#define HDCS_REG_CONTROL(sd)	(IS_1020(sd) ? HDCS20_CONTROL : HDCS00_CONTROL)
+
+#define HDCS_1X00_DEF_WIDTH	360
+#define HDCS_1X00_DEF_HEIGHT	296
+
+#define HDCS_1020_DEF_WIDTH	352
+#define HDCS_1020_DEF_HEIGHT	292
+
+#define HDCS_1020_BOTTOM_Y_SKIP	4
+
+#define HDCS_CLK_FREQ_MHZ	25
+
+#define HDCS_ADC_START_SIG_DUR	3
+
+/* LSB bit of I2C or register address signifies write (0) or read (1) */
+/* I2C Registers common for both HDCS-1000/1100 and HDCS-1020 */
+/* Identifications Register */
+#define HDCS_IDENT		(0x00 << 1)
+/* Status Register */
+#define HDCS_STATUS		(0x01 << 1)
+/* Interrupt Mask Register */
+#define HDCS_IMASK		(0x02 << 1)
+/* Pad Control Register */
+#define HDCS_PCTRL		(0x03 << 1)
+/* Pad Drive Control Register */
+#define HDCS_PDRV		(0x04 << 1)
+/* Interface Control Register */
+#define HDCS_ICTRL		(0x05 << 1)
+/* Interface Timing Register */
+#define HDCS_ITMG		(0x06 << 1)
+/* Baud Fraction Register */
+#define HDCS_BFRAC		(0x07 << 1)
+/* Baud Rate Register */
+#define HDCS_BRATE		(0x08 << 1)
+/* ADC Control Register */
+#define HDCS_ADCCTRL		(0x09 << 1)
+/* First Window Row Register */
+#define HDCS_FWROW		(0x0a << 1)
+/* First Window Column Register */
+#define HDCS_FWCOL		(0x0b << 1)
+/* Last Window Row Register */
+#define HDCS_LWROW		(0x0c << 1)
+/* Last Window Column Register */
+#define HDCS_LWCOL		(0x0d << 1)
+/* Timing Control Register */
+#define HDCS_TCTRL		(0x0e << 1)
+/* PGA Gain Register: Even Row, Even Column */
+#define HDCS_ERECPGA		(0x0f << 1)
+/* PGA Gain Register: Even Row, Odd Column */
+#define HDCS_EROCPGA		(0x10 << 1)
+/* PGA Gain Register: Odd Row, Even Column */
+#define HDCS_ORECPGA		(0x11 << 1)
+/* PGA Gain Register: Odd Row, Odd Column */
+#define HDCS_OROCPGA		(0x12 << 1)
+/* Row Exposure Low Register */
+#define HDCS_ROWEXPL		(0x13 << 1)
+/* Row Exposure High Register */
+#define HDCS_ROWEXPH		(0x14 << 1)
+
+/* I2C Registers only for HDCS-1000/1100 */
+/* Sub-Row Exposure Low Register */
+#define HDCS00_SROWEXPL		(0x15 << 1)
+/* Sub-Row Exposure High Register */
+#define HDCS00_SROWEXPH		(0x16 << 1)
+/* Configuration Register */
+#define HDCS00_CONFIG		(0x17 << 1)
+/* Control Register */
+#define HDCS00_CONTROL		(0x18 << 1)
+
+/* I2C Registers only for HDCS-1020 */
+/* Sub-Row Exposure Register */
+#define HDCS20_SROWEXP		(0x15 << 1)
+/* Error Control Register */
+#define HDCS20_ERROR		(0x16 << 1)
+/* Interface Timing 2 Register */
+#define HDCS20_ITMG2		(0x17 << 1)
+/* Interface Control 2 Register	*/
+#define HDCS20_ICTRL2		(0x18 << 1)
+/* Horizontal Blank Register */
+#define HDCS20_HBLANK		(0x19 << 1)
+/* Vertical Blank Register */
+#define HDCS20_VBLANK		(0x1a << 1)
+/* Configuration Register */
+#define HDCS20_CONFIG		(0x1b << 1)
+/* Control Register */
+#define HDCS20_CONTROL		(0x1c << 1)
+
+#define HDCS_RUN_ENABLE		(1 << 2)
+#define HDCS_SLEEP_MODE		(1 << 1)
+
+#define HDCS_DEFAULT_EXPOSURE	48
+#define HDCS_DEFAULT_GAIN	50
+
+static int hdcs_probe_1x00(struct sd *sd);
+static int hdcs_probe_1020(struct sd *sd);
+static int hdcs_start(struct sd *sd);
+static int hdcs_init(struct sd *sd);
+static int hdcs_init_controls(struct sd *sd);
+static int hdcs_stop(struct sd *sd);
+static int hdcs_dump(struct sd *sd);
+
+static int hdcs_set_exposure(struct gspca_dev *gspca_dev, __s32 val);
+static int hdcs_set_gain(struct gspca_dev *gspca_dev, __s32 val);
+
+const struct stv06xx_sensor stv06xx_sensor_hdcs1x00 = {
+	.name = "HP HDCS-1000/1100",
+	.i2c_flush = 0,
+	.i2c_addr = (0x55 << 1),
+	.i2c_len = 1,
+
+	/* FIXME (see if we can lower min_packet_size, needs testing, and also
+	   adjusting framerate when the bandwidth gets lower) */
+	.min_packet_size = { 847 },
+	.max_packet_size = { 847 },
+
+	.init = hdcs_init,
+	.init_controls = hdcs_init_controls,
+	.probe = hdcs_probe_1x00,
+	.start = hdcs_start,
+	.stop = hdcs_stop,
+	.dump = hdcs_dump,
+};
+
+const struct stv06xx_sensor stv06xx_sensor_hdcs1020 = {
+	.name = "HDCS-1020",
+	.i2c_flush = 0,
+	.i2c_addr = (0x55 << 1),
+	.i2c_len = 1,
+
+	/* FIXME (see if we can lower min_packet_size, needs testing, and also
+	   adjusting framerate when the bandwidthm gets lower) */
+	.min_packet_size = { 847 },
+	.max_packet_size = { 847 },
+
+	.init = hdcs_init,
+	.init_controls = hdcs_init_controls,
+	.probe = hdcs_probe_1020,
+	.start = hdcs_start,
+	.stop = hdcs_stop,
+	.dump = hdcs_dump,
+};
+
+static const u16 stv_bridge_init[][2] = {
+	{STV_ISO_ENABLE, 0},
+	{STV_REG23, 0},
+	{STV_REG00, 0x1d},
+	{STV_REG01, 0xb5},
+	{STV_REG02, 0xa8},
+	{STV_REG03, 0x95},
+	{STV_REG04, 0x07},
+
+	{STV_SCAN_RATE, 0x20},
+	{STV_Y_CTRL, 0x01},
+	{STV_X_CTRL, 0x0a}
+};
+
+static const u8 stv_sensor_init[][2] = {
+	/* Clear status (writing 1 will clear the corresponding status bit) */
+	{HDCS_STATUS, BIT(6) | BIT(5) | BIT(4) | BIT(3) | BIT(2) | BIT(1)},
+	/* Disable all interrupts */
+	{HDCS_IMASK, 0x00},
+	{HDCS_PCTRL, BIT(6) | BIT(5) | BIT(1) | BIT(0)},
+	{HDCS_PDRV,  0x00},
+	{HDCS_ICTRL, BIT(5)},
+	{HDCS_ITMG,  BIT(4) | BIT(1)},
+	/* ADC output resolution to 10 bits */
+	{HDCS_ADCCTRL, 10}
+};
+
+#endif
diff --git a/drivers/media/usb/gspca/stv06xx/stv06xx_pb0100.c b/drivers/media/usb/gspca/stv06xx/stv06xx_pb0100.c
new file mode 100644
index 0000000..8d785ed
--- /dev/null
+++ b/drivers/media/usb/gspca/stv06xx/stv06xx_pb0100.c
@@ -0,0 +1,436 @@
+/*
+ * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
+ *		      Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
+ * Copyright (c) 2002, 2003 Tuukka Toivonen
+ * Copyright (c) 2008 Erik Andrén
+ *
+ * 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
+ *
+ * P/N 861037:      Sensor HDCS1000        ASIC STV0600
+ * P/N 861050-0010: Sensor HDCS1000        ASIC STV0600
+ * P/N 861050-0020: Sensor Photobit PB100  ASIC STV0600-1 - QuickCam Express
+ * P/N 861055:      Sensor ST VV6410       ASIC STV0610   - LEGO cam
+ * P/N 861075-0040: Sensor HDCS1000        ASIC
+ * P/N 961179-0700: Sensor ST VV6410       ASIC STV0602   - Dexxa WebCam USB
+ * P/N 861040-0000: Sensor ST VV6410       ASIC STV0610   - QuickCam Web
+ */
+
+/*
+ * The spec file for the PB-0100 suggests the following for best quality
+ * images after the sensor has been reset :
+ *
+ * PB_ADCGAINL      = R60 = 0x03 (3 dec)      : sets low reference of ADC
+						to produce good black level
+ * PB_PREADCTRL     = R32 = 0x1400 (5120 dec) : Enables global gain changes
+						through R53
+ * PB_ADCMINGAIN    = R52 = 0x10 (16 dec)     : Sets the minimum gain for
+						auto-exposure
+ * PB_ADCGLOBALGAIN = R53 = 0x10 (16 dec)     : Sets the global gain
+ * PB_EXPGAIN       = R14 = 0x11 (17 dec)     : Sets the auto-exposure value
+ * PB_UPDATEINT     = R23 = 0x02 (2 dec)      : Sets the speed on
+						auto-exposure routine
+ * PB_CFILLIN       = R5  = 0x0E (14 dec)     : Sets the frame rate
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "stv06xx_pb0100.h"
+
+struct pb0100_ctrls {
+	struct { /* one big happy control cluster... */
+		struct v4l2_ctrl *autogain;
+		struct v4l2_ctrl *gain;
+		struct v4l2_ctrl *exposure;
+		struct v4l2_ctrl *red;
+		struct v4l2_ctrl *blue;
+		struct v4l2_ctrl *natural;
+	};
+	struct v4l2_ctrl *target;
+};
+
+static struct v4l2_pix_format pb0100_mode[] = {
+/* low res / subsample modes disabled as they are only half res horizontal,
+   halving the vertical resolution does not seem to work */
+	{
+		320,
+		240,
+		V4L2_PIX_FMT_SGRBG8,
+		V4L2_FIELD_NONE,
+		.sizeimage = 320 * 240,
+		.bytesperline = 320,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = PB0100_CROP_TO_VGA
+	},
+	{
+		352,
+		288,
+		V4L2_PIX_FMT_SGRBG8,
+		V4L2_FIELD_NONE,
+		.sizeimage = 352 * 288,
+		.bytesperline = 352,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 0
+	}
+};
+
+static int pb0100_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct gspca_dev *gspca_dev =
+		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+	struct sd *sd = (struct sd *)gspca_dev;
+	struct pb0100_ctrls *ctrls = sd->sensor_priv;
+	int err = -EINVAL;
+
+	switch (ctrl->id) {
+	case V4L2_CID_AUTOGAIN:
+		err = pb0100_set_autogain(gspca_dev, ctrl->val);
+		if (err)
+			break;
+		if (ctrl->val)
+			break;
+		err = pb0100_set_gain(gspca_dev, ctrls->gain->val);
+		if (err)
+			break;
+		err = pb0100_set_exposure(gspca_dev, ctrls->exposure->val);
+		break;
+	case V4L2_CTRL_CLASS_USER + 0x1001:
+		err = pb0100_set_autogain_target(gspca_dev, ctrl->val);
+		break;
+	}
+	return err;
+}
+
+static const struct v4l2_ctrl_ops pb0100_ctrl_ops = {
+	.s_ctrl = pb0100_s_ctrl,
+};
+
+static int pb0100_init_controls(struct sd *sd)
+{
+	struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler;
+	struct pb0100_ctrls *ctrls;
+	static const struct v4l2_ctrl_config autogain_target = {
+		.ops = &pb0100_ctrl_ops,
+		.id = V4L2_CTRL_CLASS_USER + 0x1000,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.name = "Automatic Gain Target",
+		.max = 255,
+		.step = 1,
+		.def = 128,
+	};
+	static const struct v4l2_ctrl_config natural_light = {
+		.ops = &pb0100_ctrl_ops,
+		.id = V4L2_CTRL_CLASS_USER + 0x1001,
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.name = "Natural Light Source",
+		.max = 1,
+		.step = 1,
+		.def = 1,
+	};
+
+	ctrls = kzalloc(sizeof(*ctrls), GFP_KERNEL);
+	if (!ctrls)
+		return -ENOMEM;
+
+	v4l2_ctrl_handler_init(hdl, 6);
+	ctrls->autogain = v4l2_ctrl_new_std(hdl, &pb0100_ctrl_ops,
+			V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+	ctrls->exposure = v4l2_ctrl_new_std(hdl, &pb0100_ctrl_ops,
+			V4L2_CID_EXPOSURE, 0, 511, 1, 12);
+	ctrls->gain = v4l2_ctrl_new_std(hdl, &pb0100_ctrl_ops,
+			V4L2_CID_GAIN, 0, 255, 1, 128);
+	ctrls->red = v4l2_ctrl_new_std(hdl, &pb0100_ctrl_ops,
+			V4L2_CID_RED_BALANCE, -255, 255, 1, 0);
+	ctrls->blue = v4l2_ctrl_new_std(hdl, &pb0100_ctrl_ops,
+			V4L2_CID_BLUE_BALANCE, -255, 255, 1, 0);
+	ctrls->natural = v4l2_ctrl_new_custom(hdl, &natural_light, NULL);
+	ctrls->target = v4l2_ctrl_new_custom(hdl, &autogain_target, NULL);
+	if (hdl->error) {
+		kfree(ctrls);
+		return hdl->error;
+	}
+	sd->sensor_priv = ctrls;
+	v4l2_ctrl_auto_cluster(5, &ctrls->autogain, 0, false);
+	return 0;
+}
+
+static int pb0100_probe(struct sd *sd)
+{
+	u16 sensor;
+	int err;
+
+	err = stv06xx_read_sensor(sd, PB_IDENT, &sensor);
+
+	if (err < 0)
+		return -ENODEV;
+	if ((sensor >> 8) != 0x64)
+		return -ENODEV;
+
+	pr_info("Photobit pb0100 sensor detected\n");
+
+	sd->gspca_dev.cam.cam_mode = pb0100_mode;
+	sd->gspca_dev.cam.nmodes = ARRAY_SIZE(pb0100_mode);
+
+	return 0;
+}
+
+static int pb0100_start(struct sd *sd)
+{
+	int err, packet_size, max_packet_size;
+	struct usb_host_interface *alt;
+	struct usb_interface *intf;
+	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
+	struct cam *cam = &sd->gspca_dev.cam;
+	u32 mode = cam->cam_mode[sd->gspca_dev.curr_mode].priv;
+
+	intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface);
+	alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt);
+	if (!alt)
+		return -ENODEV;
+	packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize);
+
+	/* If we don't have enough bandwidth use a lower framerate */
+	max_packet_size = sd->sensor->max_packet_size[sd->gspca_dev.curr_mode];
+	if (packet_size < max_packet_size)
+		stv06xx_write_sensor(sd, PB_ROWSPEED, BIT(4)|BIT(3)|BIT(1));
+	else
+		stv06xx_write_sensor(sd, PB_ROWSPEED, BIT(5)|BIT(3)|BIT(1));
+
+	/* Setup sensor window */
+	if (mode & PB0100_CROP_TO_VGA) {
+		stv06xx_write_sensor(sd, PB_RSTART, 30);
+		stv06xx_write_sensor(sd, PB_CSTART, 20);
+		stv06xx_write_sensor(sd, PB_RWSIZE, 240 - 1);
+		stv06xx_write_sensor(sd, PB_CWSIZE, 320 - 1);
+	} else {
+		stv06xx_write_sensor(sd, PB_RSTART, 8);
+		stv06xx_write_sensor(sd, PB_CSTART, 4);
+		stv06xx_write_sensor(sd, PB_RWSIZE, 288 - 1);
+		stv06xx_write_sensor(sd, PB_CWSIZE, 352 - 1);
+	}
+
+	if (mode & PB0100_SUBSAMPLE) {
+		stv06xx_write_bridge(sd, STV_Y_CTRL, 0x02); /* Wrong, FIXME */
+		stv06xx_write_bridge(sd, STV_X_CTRL, 0x06);
+
+		stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x10);
+	} else {
+		stv06xx_write_bridge(sd, STV_Y_CTRL, 0x01);
+		stv06xx_write_bridge(sd, STV_X_CTRL, 0x0a);
+		/* larger -> slower */
+		stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x20);
+	}
+
+	err = stv06xx_write_sensor(sd, PB_CONTROL, BIT(5)|BIT(3)|BIT(1));
+	PDEBUG(D_STREAM, "Started stream, status: %d", err);
+
+	return (err < 0) ? err : 0;
+}
+
+static int pb0100_stop(struct sd *sd)
+{
+	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
+	int err;
+
+	err = stv06xx_write_sensor(sd, PB_ABORTFRAME, 1);
+
+	if (err < 0)
+		goto out;
+
+	/* Set bit 1 to zero */
+	err = stv06xx_write_sensor(sd, PB_CONTROL, BIT(5)|BIT(3));
+
+	PDEBUG(D_STREAM, "Halting stream");
+out:
+	return (err < 0) ? err : 0;
+}
+
+/* FIXME: Sort the init commands out and put them into tables,
+	  this is only for getting the camera to work */
+/* FIXME: No error handling for now,
+	  add this once the init has been converted to proper tables */
+static int pb0100_init(struct sd *sd)
+{
+	stv06xx_write_bridge(sd, STV_REG00, 1);
+	stv06xx_write_bridge(sd, STV_SCAN_RATE, 0);
+
+	/* Reset sensor */
+	stv06xx_write_sensor(sd, PB_RESET, 1);
+	stv06xx_write_sensor(sd, PB_RESET, 0);
+
+	/* Disable chip */
+	stv06xx_write_sensor(sd, PB_CONTROL, BIT(5)|BIT(3));
+
+	/* Gain stuff...*/
+	stv06xx_write_sensor(sd, PB_PREADCTRL, BIT(12)|BIT(10)|BIT(6));
+	stv06xx_write_sensor(sd, PB_ADCGLOBALGAIN, 12);
+
+	/* Set up auto-exposure */
+	/* ADC VREF_HI new setting for a transition
+	  from the Expose1 to the Expose2 setting */
+	stv06xx_write_sensor(sd, PB_R28, 12);
+	/* gain max for autoexposure */
+	stv06xx_write_sensor(sd, PB_ADCMAXGAIN, 180);
+	/* gain min for autoexposure  */
+	stv06xx_write_sensor(sd, PB_ADCMINGAIN, 12);
+	/* Maximum frame integration time (programmed into R8)
+	   allowed for auto-exposure routine */
+	stv06xx_write_sensor(sd, PB_R54, 3);
+	/* Minimum frame integration time (programmed into R8)
+	   allowed for auto-exposure routine */
+	stv06xx_write_sensor(sd, PB_R55, 0);
+	stv06xx_write_sensor(sd, PB_UPDATEINT, 1);
+	/* R15  Expose0 (maximum that auto-exposure may use) */
+	stv06xx_write_sensor(sd, PB_R15, 800);
+	/* R17  Expose2 (minimum that auto-exposure may use) */
+	stv06xx_write_sensor(sd, PB_R17, 10);
+
+	stv06xx_write_sensor(sd, PB_EXPGAIN, 0);
+
+	/* 0x14 */
+	stv06xx_write_sensor(sd, PB_VOFFSET, 0);
+	/* 0x0D */
+	stv06xx_write_sensor(sd, PB_ADCGAINH, 11);
+	/* Set black level (important!) */
+	stv06xx_write_sensor(sd, PB_ADCGAINL, 0);
+
+	/* ??? */
+	stv06xx_write_bridge(sd, STV_REG00, 0x11);
+	stv06xx_write_bridge(sd, STV_REG03, 0x45);
+	stv06xx_write_bridge(sd, STV_REG04, 0x07);
+
+	/* Scan/timing for the sensor */
+	stv06xx_write_sensor(sd, PB_ROWSPEED, BIT(4)|BIT(3)|BIT(1));
+	stv06xx_write_sensor(sd, PB_CFILLIN, 14);
+	stv06xx_write_sensor(sd, PB_VBL, 0);
+	stv06xx_write_sensor(sd, PB_FINTTIME, 0);
+	stv06xx_write_sensor(sd, PB_RINTTIME, 123);
+
+	stv06xx_write_bridge(sd, STV_REG01, 0xc2);
+	stv06xx_write_bridge(sd, STV_REG02, 0xb0);
+	return 0;
+}
+
+static int pb0100_dump(struct sd *sd)
+{
+	return 0;
+}
+
+static int pb0100_set_gain(struct gspca_dev *gspca_dev, __s32 val)
+{
+	int err;
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct pb0100_ctrls *ctrls = sd->sensor_priv;
+
+	err = stv06xx_write_sensor(sd, PB_G1GAIN, val);
+	if (!err)
+		err = stv06xx_write_sensor(sd, PB_G2GAIN, val);
+	PDEBUG(D_CONF, "Set green gain to %d, status: %d", val, err);
+
+	if (!err)
+		err = pb0100_set_red_balance(gspca_dev, ctrls->red->val);
+	if (!err)
+		err = pb0100_set_blue_balance(gspca_dev, ctrls->blue->val);
+
+	return err;
+}
+
+static int pb0100_set_red_balance(struct gspca_dev *gspca_dev, __s32 val)
+{
+	int err;
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct pb0100_ctrls *ctrls = sd->sensor_priv;
+
+	val += ctrls->gain->val;
+	if (val < 0)
+		val = 0;
+	else if (val > 255)
+		val = 255;
+
+	err = stv06xx_write_sensor(sd, PB_RGAIN, val);
+	PDEBUG(D_CONF, "Set red gain to %d, status: %d", val, err);
+
+	return err;
+}
+
+static int pb0100_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val)
+{
+	int err;
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct pb0100_ctrls *ctrls = sd->sensor_priv;
+
+	val += ctrls->gain->val;
+	if (val < 0)
+		val = 0;
+	else if (val > 255)
+		val = 255;
+
+	err = stv06xx_write_sensor(sd, PB_BGAIN, val);
+	PDEBUG(D_CONF, "Set blue gain to %d, status: %d", val, err);
+
+	return err;
+}
+
+static int pb0100_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int err;
+
+	err = stv06xx_write_sensor(sd, PB_RINTTIME, val);
+	PDEBUG(D_CONF, "Set exposure to %d, status: %d", val, err);
+
+	return err;
+}
+
+static int pb0100_set_autogain(struct gspca_dev *gspca_dev, __s32 val)
+{
+	int err;
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct pb0100_ctrls *ctrls = sd->sensor_priv;
+
+	if (val) {
+		if (ctrls->natural->val)
+			val = BIT(6)|BIT(4)|BIT(0);
+		else
+			val = BIT(4)|BIT(0);
+	} else
+		val = 0;
+
+	err = stv06xx_write_sensor(sd, PB_EXPGAIN, val);
+	PDEBUG(D_CONF, "Set autogain to %d (natural: %d), status: %d",
+	       val, ctrls->natural->val, err);
+
+	return err;
+}
+
+static int pb0100_set_autogain_target(struct gspca_dev *gspca_dev, __s32 val)
+{
+	int err, totalpixels, brightpixels, darkpixels;
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	/* Number of pixels counted by the sensor when subsampling the pixels.
+	 * Slightly larger than the real value to avoid oscillation */
+	totalpixels = gspca_dev->pixfmt.width * gspca_dev->pixfmt.height;
+	totalpixels = totalpixels/(8*8) + totalpixels/(64*64);
+
+	brightpixels = (totalpixels * val) >> 8;
+	darkpixels   = totalpixels - brightpixels;
+	err = stv06xx_write_sensor(sd, PB_R21, brightpixels);
+	if (!err)
+		err = stv06xx_write_sensor(sd, PB_R22, darkpixels);
+
+	PDEBUG(D_CONF, "Set autogain target to %d, status: %d", val, err);
+
+	return err;
+}
diff --git a/drivers/media/usb/gspca/stv06xx/stv06xx_pb0100.h b/drivers/media/usb/gspca/stv06xx/stv06xx_pb0100.h
new file mode 100644
index 0000000..5071e53
--- /dev/null
+++ b/drivers/media/usb/gspca/stv06xx/stv06xx_pb0100.h
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
+ *		      Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
+ * Copyright (c) 2002, 2003 Tuukka Toivonen
+ * Copyright (c) 2008 Erik Andrén
+ *
+ * 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
+ *
+ * P/N 861037:      Sensor HDCS1000        ASIC STV0600
+ * P/N 861050-0010: Sensor HDCS1000        ASIC STV0600
+ * P/N 861050-0020: Sensor Photobit PB100  ASIC STV0600-1 - QuickCam Express
+ * P/N 861055:      Sensor ST VV6410       ASIC STV0610   - LEGO cam
+ * P/N 861075-0040: Sensor HDCS1000        ASIC
+ * P/N 961179-0700: Sensor ST VV6410       ASIC STV0602   - Dexxa WebCam USB
+ * P/N 861040-0000: Sensor ST VV6410       ASIC STV0610   - QuickCam Web
+ */
+
+#ifndef STV06XX_PB0100_H_
+#define STV06XX_PB0100_H_
+
+#include "stv06xx_sensor.h"
+
+/* mode priv field flags */
+#define PB0100_CROP_TO_VGA	0x01
+#define PB0100_SUBSAMPLE	0x02
+
+/* I2C Registers */
+#define PB_IDENT		0x00	/* Chip Version */
+#define PB_RSTART		0x01	/* Row Window Start */
+#define PB_CSTART		0x02	/* Column Window Start */
+#define PB_RWSIZE		0x03	/* Row Window Size */
+#define PB_CWSIZE		0x04	/* Column  Window Size */
+#define PB_CFILLIN		0x05	/* Column Fill-In */
+#define PB_VBL			0x06	/* Vertical Blank Count */
+#define PB_CONTROL		0x07	/* Control Mode */
+#define PB_FINTTIME		0x08	/* Integration Time/Frame Unit Count */
+#define PB_RINTTIME		0x09	/* Integration Time/Row Unit Count */
+#define PB_ROWSPEED		0x0a	/* Row Speed Control */
+#define PB_ABORTFRAME		0x0b	/* Abort Frame */
+#define PB_R12			0x0c	/* Reserved */
+#define PB_RESET		0x0d	/* Reset */
+#define PB_EXPGAIN		0x0e	/* Exposure Gain Command */
+#define PB_R15			0x0f	/* Expose0 */
+#define PB_R16			0x10	/* Expose1 */
+#define PB_R17			0x11	/* Expose2 */
+#define PB_R18			0x12	/* Low0_DAC */
+#define PB_R19			0x13	/* Low1_DAC */
+#define PB_R20			0x14	/* Low2_DAC */
+#define PB_R21			0x15	/* Threshold11 */
+#define PB_R22			0x16	/* Threshold0x */
+#define PB_UPDATEINT		0x17	/* Update Interval */
+#define PB_R24			0x18	/* High_DAC */
+#define PB_R25			0x19	/* Trans0H */
+#define PB_R26			0x1a	/* Trans1L */
+#define PB_R27			0x1b	/* Trans1H */
+#define PB_R28			0x1c	/* Trans2L */
+#define PB_R29			0x1d	/* Reserved */
+#define PB_R30			0x1e	/* Reserved */
+#define PB_R31			0x1f	/* Wait to Read */
+#define PB_PREADCTRL		0x20	/* Pixel Read Control Mode */
+#define PB_R33			0x21	/* IREF_VLN */
+#define PB_R34			0x22	/* IREF_VLP */
+#define PB_R35			0x23	/* IREF_VLN_INTEG */
+#define PB_R36			0x24	/* IREF_MASTER */
+#define PB_R37			0x25	/* IDACP */
+#define PB_R38			0x26	/* IDACN */
+#define PB_R39			0x27	/* DAC_Control_Reg */
+#define PB_R40			0x28	/* VCL */
+#define PB_R41			0x29	/* IREF_VLN_ADCIN */
+#define PB_R42			0x2a	/* Reserved */
+#define PB_G1GAIN		0x2b	/* Green 1 Gain */
+#define PB_BGAIN		0x2c	/* Blue Gain */
+#define PB_RGAIN		0x2d	/* Red Gain */
+#define PB_G2GAIN		0x2e	/* Green 2 Gain */
+#define PB_R47			0x2f	/* Dark Row Address */
+#define PB_R48			0x30	/* Dark Row Options */
+#define PB_R49			0x31	/* Reserved */
+#define PB_R50			0x32	/* Image Test Data */
+#define PB_ADCMAXGAIN		0x33	/* Maximum Gain */
+#define PB_ADCMINGAIN		0x34	/* Minimum Gain */
+#define PB_ADCGLOBALGAIN	0x35	/* Global Gain */
+#define PB_R54			0x36	/* Maximum Frame */
+#define PB_R55			0x37	/* Minimum Frame */
+#define PB_R56			0x38	/* Reserved */
+#define PB_VOFFSET		0x39	/* VOFFSET */
+#define PB_R58			0x3a	/* Snap-Shot Sequence Trigger */
+#define PB_ADCGAINH		0x3b	/* VREF_HI */
+#define PB_ADCGAINL		0x3c	/* VREF_LO */
+#define PB_R61			0x3d	/* Reserved */
+#define PB_R62			0x3e	/* Reserved */
+#define PB_R63			0x3f	/* Reserved */
+#define PB_R64			0x40	/* Red/Blue Gain */
+#define PB_R65			0x41	/* Green 2/Green 1 Gain */
+#define PB_R66			0x42	/* VREF_HI/LO */
+#define PB_R67			0x43	/* Integration Time/Row Unit Count */
+#define PB_R240			0xf0	/* ADC Test */
+#define PB_R241			0xf1    /* Chip Enable */
+#define PB_R242			0xf2	/* Reserved */
+
+static int pb0100_probe(struct sd *sd);
+static int pb0100_start(struct sd *sd);
+static int pb0100_init(struct sd *sd);
+static int pb0100_init_controls(struct sd *sd);
+static int pb0100_stop(struct sd *sd);
+static int pb0100_dump(struct sd *sd);
+
+/* V4L2 controls supported by the driver */
+static int pb0100_set_gain(struct gspca_dev *gspca_dev, __s32 val);
+static int pb0100_set_red_balance(struct gspca_dev *gspca_dev, __s32 val);
+static int pb0100_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val);
+static int pb0100_set_exposure(struct gspca_dev *gspca_dev, __s32 val);
+static int pb0100_set_autogain(struct gspca_dev *gspca_dev, __s32 val);
+static int pb0100_set_autogain_target(struct gspca_dev *gspca_dev, __s32 val);
+
+const struct stv06xx_sensor stv06xx_sensor_pb0100 = {
+	.name = "PB-0100",
+	.i2c_flush = 1,
+	.i2c_addr = 0xba,
+	.i2c_len = 2,
+
+	.min_packet_size = { 635, 847 },
+	.max_packet_size = { 847, 923 },
+
+	.init = pb0100_init,
+	.init_controls = pb0100_init_controls,
+	.probe = pb0100_probe,
+	.start = pb0100_start,
+	.stop = pb0100_stop,
+	.dump = pb0100_dump,
+};
+
+#endif
diff --git a/drivers/media/usb/gspca/stv06xx/stv06xx_sensor.h b/drivers/media/usb/gspca/stv06xx/stv06xx_sensor.h
new file mode 100644
index 0000000..3a498c2
--- /dev/null
+++ b/drivers/media/usb/gspca/stv06xx/stv06xx_sensor.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
+ *		      Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
+ * Copyright (c) 2002, 2003 Tuukka Toivonen
+ * Copyright (c) 2008 Erik Andrén
+ *
+ * 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
+ *
+ * P/N 861037:      Sensor HDCS1000        ASIC STV0600
+ * P/N 861050-0010: Sensor HDCS1000        ASIC STV0600
+ * P/N 861050-0020: Sensor Photobit PB100  ASIC STV0600-1 - QuickCam Express
+ * P/N 861055:      Sensor ST VV6410       ASIC STV0610   - LEGO cam
+ * P/N 861075-0040: Sensor HDCS1000        ASIC
+ * P/N 961179-0700: Sensor ST VV6410       ASIC STV0602   - Dexxa WebCam USB
+ * P/N 861040-0000: Sensor ST VV6410       ASIC STV0610   - QuickCam Web
+ */
+
+#ifndef STV06XX_SENSOR_H_
+#define STV06XX_SENSOR_H_
+
+#include "stv06xx.h"
+
+#define IS_1020(sd)	((sd)->sensor == &stv06xx_sensor_hdcs1020)
+
+extern const struct stv06xx_sensor stv06xx_sensor_vv6410;
+extern const struct stv06xx_sensor stv06xx_sensor_hdcs1x00;
+extern const struct stv06xx_sensor stv06xx_sensor_hdcs1020;
+extern const struct stv06xx_sensor stv06xx_sensor_pb0100;
+extern const struct stv06xx_sensor stv06xx_sensor_st6422;
+
+struct stv06xx_sensor {
+	/* Defines the name of a sensor */
+	char name[32];
+
+	/* Sensor i2c address */
+	u8 i2c_addr;
+
+	/* Flush value*/
+	u8 i2c_flush;
+
+	/* length of an i2c word */
+	u8 i2c_len;
+
+	/* Isoc packet size (per mode) */
+	int min_packet_size[4];
+	int max_packet_size[4];
+
+	/* Probes if the sensor is connected */
+	int (*probe)(struct sd *sd);
+
+	/* Performs a initialization sequence */
+	int (*init)(struct sd *sd);
+
+	/* Initializes the controls */
+	int (*init_controls)(struct sd *sd);
+
+	/* Reads a sensor register */
+	int (*read_sensor)(struct sd *sd, const u8 address,
+	      u8 *i2c_data, const u8 len);
+
+	/* Writes to a sensor register */
+	int (*write_sensor)(struct sd *sd, const u8 address,
+	      u8 *i2c_data, const u8 len);
+
+	/* Instructs the sensor to start streaming */
+	int (*start)(struct sd *sd);
+
+	/* Instructs the sensor to stop streaming */
+	int (*stop)(struct sd *sd);
+
+	/* Instructs the sensor to dump all its contents */
+	int (*dump)(struct sd *sd);
+};
+
+#endif
diff --git a/drivers/media/usb/gspca/stv06xx/stv06xx_st6422.c b/drivers/media/usb/gspca/stv06xx/stv06xx_st6422.c
new file mode 100644
index 0000000..515a9e1
--- /dev/null
+++ b/drivers/media/usb/gspca/stv06xx/stv06xx_st6422.c
@@ -0,0 +1,287 @@
+/*
+ * Support for the sensor part which is integrated (I think) into the
+ * st6422 stv06xx alike bridge, as its integrated there are no i2c writes
+ * but instead direct bridge writes.
+ *
+ * Copyright (c) 2009 Hans de Goede <hdegoede@redhat.com>
+ *
+ * Strongly based on qc-usb-messenger, which is:
+ * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
+ *		      Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
+ * Copyright (c) 2002, 2003 Tuukka Toivonen
+ *
+ * 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "stv06xx_st6422.h"
+
+static struct v4l2_pix_format st6422_mode[] = {
+	/* Note we actually get 124 lines of data, of which we skip the 4st
+	   4 as they are garbage */
+	{
+		162,
+		120,
+		V4L2_PIX_FMT_SGRBG8,
+		V4L2_FIELD_NONE,
+		.sizeimage = 162 * 120,
+		.bytesperline = 162,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 1
+	},
+	/* Note we actually get 248 lines of data, of which we skip the 4st
+	   4 as they are garbage, and we tell the app it only gets the
+	   first 240 of the 244 lines it actually gets, so that it ignores
+	   the last 4. */
+	{
+		324,
+		240,
+		V4L2_PIX_FMT_SGRBG8,
+		V4L2_FIELD_NONE,
+		.sizeimage = 324 * 244,
+		.bytesperline = 324,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 0
+	},
+};
+
+/* V4L2 controls supported by the driver */
+static int setbrightness(struct sd *sd, s32 val);
+static int setcontrast(struct sd *sd, s32 val);
+static int setgain(struct sd *sd, u8 gain);
+static int setexposure(struct sd *sd, s16 expo);
+
+static int st6422_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct gspca_dev *gspca_dev =
+		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+	struct sd *sd = (struct sd *)gspca_dev;
+	int err = -EINVAL;
+
+	switch (ctrl->id) {
+	case V4L2_CID_BRIGHTNESS:
+		err = setbrightness(sd, ctrl->val);
+		break;
+	case V4L2_CID_CONTRAST:
+		err = setcontrast(sd, ctrl->val);
+		break;
+	case V4L2_CID_GAIN:
+		err = setgain(sd, ctrl->val);
+		break;
+	case V4L2_CID_EXPOSURE:
+		err = setexposure(sd, ctrl->val);
+		break;
+	}
+
+	/* commit settings */
+	if (err >= 0)
+		err = stv06xx_write_bridge(sd, 0x143f, 0x01);
+	sd->gspca_dev.usb_err = err;
+	return err;
+}
+
+static const struct v4l2_ctrl_ops st6422_ctrl_ops = {
+	.s_ctrl = st6422_s_ctrl,
+};
+
+static int st6422_init_controls(struct sd *sd)
+{
+	struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler;
+
+	v4l2_ctrl_handler_init(hdl, 4);
+	v4l2_ctrl_new_std(hdl, &st6422_ctrl_ops,
+			V4L2_CID_BRIGHTNESS, 0, 31, 1, 3);
+	v4l2_ctrl_new_std(hdl, &st6422_ctrl_ops,
+			V4L2_CID_CONTRAST, 0, 15, 1, 11);
+	v4l2_ctrl_new_std(hdl, &st6422_ctrl_ops,
+			V4L2_CID_EXPOSURE, 0, 1023, 1, 256);
+	v4l2_ctrl_new_std(hdl, &st6422_ctrl_ops,
+			V4L2_CID_GAIN, 0, 255, 1, 64);
+
+	return hdl->error;
+}
+
+static int st6422_probe(struct sd *sd)
+{
+	if (sd->bridge != BRIDGE_ST6422)
+		return -ENODEV;
+
+	pr_info("st6422 sensor detected\n");
+
+	sd->gspca_dev.cam.cam_mode = st6422_mode;
+	sd->gspca_dev.cam.nmodes = ARRAY_SIZE(st6422_mode);
+	return 0;
+}
+
+static int st6422_init(struct sd *sd)
+{
+	int err = 0, i;
+
+	const u16 st6422_bridge_init[][2] = {
+		{ STV_ISO_ENABLE, 0x00 }, /* disable capture */
+		{ 0x1436, 0x00 },
+		{ 0x1432, 0x03 },	/* 0x00-0x1F brightness */
+		{ 0x143a, 0xf9 },	/* 0x00-0x0F contrast */
+		{ 0x0509, 0x38 },	/* R */
+		{ 0x050a, 0x38 },	/* G */
+		{ 0x050b, 0x38 },	/* B */
+		{ 0x050c, 0x2a },
+		{ 0x050d, 0x01 },
+
+
+		{ 0x1431, 0x00 },	/* 0x00-0x07 ??? */
+		{ 0x1433, 0x34 },	/* 160x120, 0x00-0x01 night filter */
+		{ 0x1438, 0x18 },	/* 640x480 */
+/* 18 bayes */
+/* 10 compressed? */
+
+		{ 0x1439, 0x00 },
+/* anti-noise?  0xa2 gives a perfect image */
+
+		{ 0x143b, 0x05 },
+		{ 0x143c, 0x00 },	/* 0x00-0x01 - ??? */
+
+
+/* shutter time 0x0000-0x03FF */
+/* low value  give good picures on moving objects (but requires much light) */
+/* high value gives good picures in darkness (but tends to be overexposed) */
+		{ 0x143e, 0x01 },
+		{ 0x143d, 0x00 },
+
+		{ 0x1442, 0xe2 },
+/* write: 1x1x xxxx */
+/* read:  1x1x xxxx */
+/*        bit 5 == button pressed and hold if 0 */
+/* write 0xe2,0xea */
+
+/* 0x144a */
+/* 0x00 init */
+/* bit 7 == button has been pressed, but not handled */
+
+/* interrupt */
+/* if(urb->iso_frame_desc[i].status == 0x80) { */
+/* if(urb->iso_frame_desc[i].status == 0x88) { */
+
+		{ 0x1500, 0xd0 },
+		{ 0x1500, 0xd0 },
+		{ 0x1500, 0x50 },	/* 0x00 - 0xFF  0x80 == compr ? */
+
+		{ 0x1501, 0xaf },
+/* high val-> light area gets darker */
+/* low val -> light area gets lighter */
+		{ 0x1502, 0xc2 },
+/* high val-> light area gets darker */
+/* low val -> light area gets lighter */
+		{ 0x1503, 0x45 },
+/* high val-> light area gets darker */
+/* low val -> light area gets lighter */
+		{ 0x1505, 0x02 },
+/* 2  : 324x248  80352 bytes */
+/* 7  : 248x162  40176 bytes */
+/* c+f: 162*124  20088 bytes */
+
+		{ 0x150e, 0x8e },
+		{ 0x150f, 0x37 },
+		{ 0x15c0, 0x00 },
+		{ 0x15c3, 0x08 },	/* 0x04/0x14 ... test pictures ??? */
+
+
+		{ 0x143f, 0x01 },	/* commit settings */
+
+	};
+
+	for (i = 0; i < ARRAY_SIZE(st6422_bridge_init) && !err; i++) {
+		err = stv06xx_write_bridge(sd, st6422_bridge_init[i][0],
+					       st6422_bridge_init[i][1]);
+	}
+
+	return err;
+}
+
+static int setbrightness(struct sd *sd, s32 val)
+{
+	/* val goes from 0 -> 31 */
+	return stv06xx_write_bridge(sd, 0x1432, val);
+}
+
+static int setcontrast(struct sd *sd, s32 val)
+{
+	/* Val goes from 0 -> 15 */
+	return stv06xx_write_bridge(sd, 0x143a, val | 0xf0);
+}
+
+static int setgain(struct sd *sd, u8 gain)
+{
+	int err;
+
+	/* Set red, green, blue, gain */
+	err = stv06xx_write_bridge(sd, 0x0509, gain);
+	if (err < 0)
+		return err;
+
+	err = stv06xx_write_bridge(sd, 0x050a, gain);
+	if (err < 0)
+		return err;
+
+	err = stv06xx_write_bridge(sd, 0x050b, gain);
+	if (err < 0)
+		return err;
+
+	/* 2 mystery writes */
+	err = stv06xx_write_bridge(sd, 0x050c, 0x2a);
+	if (err < 0)
+		return err;
+
+	return stv06xx_write_bridge(sd, 0x050d, 0x01);
+}
+
+static int setexposure(struct sd *sd, s16 expo)
+{
+	int err;
+
+	err = stv06xx_write_bridge(sd, 0x143d, expo & 0xff);
+	if (err < 0)
+		return err;
+
+	return stv06xx_write_bridge(sd, 0x143e, expo >> 8);
+}
+
+static int st6422_start(struct sd *sd)
+{
+	int err;
+	struct cam *cam = &sd->gspca_dev.cam;
+
+	if (cam->cam_mode[sd->gspca_dev.curr_mode].priv)
+		err = stv06xx_write_bridge(sd, 0x1505, 0x0f);
+	else
+		err = stv06xx_write_bridge(sd, 0x1505, 0x02);
+	if (err < 0)
+		return err;
+
+	/* commit settings */
+	err = stv06xx_write_bridge(sd, 0x143f, 0x01);
+	return (err < 0) ? err : 0;
+}
+
+static int st6422_stop(struct sd *sd)
+{
+	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
+
+	PDEBUG(D_STREAM, "Halting stream");
+
+	return 0;
+}
diff --git a/drivers/media/usb/gspca/stv06xx/stv06xx_st6422.h b/drivers/media/usb/gspca/stv06xx/stv06xx_st6422.h
new file mode 100644
index 0000000..8f20fbf
--- /dev/null
+++ b/drivers/media/usb/gspca/stv06xx/stv06xx_st6422.h
@@ -0,0 +1,52 @@
+/*
+ * Support for the sensor part which is integrated (I think) into the
+ * st6422 stv06xx alike bridge, as its integrated there are no i2c writes
+ * but instead direct bridge writes.
+ *
+ * Copyright (c) 2009 Hans de Goede <hdegoede@redhat.com>
+ *
+ * Strongly based on qc-usb-messenger, which is:
+ * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
+ *		      Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
+ * Copyright (c) 2002, 2003 Tuukka Toivonen
+ *
+ * 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
+ *
+ */
+
+#ifndef STV06XX_ST6422_H_
+#define STV06XX_ST6422_H_
+
+#include "stv06xx_sensor.h"
+
+static int st6422_probe(struct sd *sd);
+static int st6422_start(struct sd *sd);
+static int st6422_init(struct sd *sd);
+static int st6422_init_controls(struct sd *sd);
+static int st6422_stop(struct sd *sd);
+
+const struct stv06xx_sensor stv06xx_sensor_st6422 = {
+	.name = "ST6422",
+	/* No known way to lower framerate in case of less bandwidth */
+	.min_packet_size = { 300, 847 },
+	.max_packet_size = { 300, 847 },
+	.init = st6422_init,
+	.init_controls = st6422_init_controls,
+	.probe = st6422_probe,
+	.start = st6422_start,
+	.stop = st6422_stop,
+};
+
+#endif
diff --git a/drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.c b/drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.c
new file mode 100644
index 0000000..f86cec0
--- /dev/null
+++ b/drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.c
@@ -0,0 +1,281 @@
+/*
+ * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
+ *		      Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
+ * Copyright (c) 2002, 2003 Tuukka Toivonen
+ * Copyright (c) 2008 Erik Andrén
+ *
+ * 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
+ *
+ * P/N 861037:      Sensor HDCS1000        ASIC STV0600
+ * P/N 861050-0010: Sensor HDCS1000        ASIC STV0600
+ * P/N 861050-0020: Sensor Photobit PB100  ASIC STV0600-1 - QuickCam Express
+ * P/N 861055:      Sensor ST VV6410       ASIC STV0610   - LEGO cam
+ * P/N 861075-0040: Sensor HDCS1000        ASIC
+ * P/N 961179-0700: Sensor ST VV6410       ASIC STV0602   - Dexxa WebCam USB
+ * P/N 861040-0000: Sensor ST VV6410       ASIC STV0610   - QuickCam Web
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "stv06xx_vv6410.h"
+
+static struct v4l2_pix_format vv6410_mode[] = {
+	{
+		356,
+		292,
+		V4L2_PIX_FMT_SGRBG8,
+		V4L2_FIELD_NONE,
+		.sizeimage = 356 * 292,
+		.bytesperline = 356,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 0
+	}
+};
+
+static int vv6410_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct gspca_dev *gspca_dev =
+		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+	int err = -EINVAL;
+
+	switch (ctrl->id) {
+	case V4L2_CID_HFLIP:
+		if (!gspca_dev->streaming)
+			return 0;
+		err = vv6410_set_hflip(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_VFLIP:
+		if (!gspca_dev->streaming)
+			return 0;
+		err = vv6410_set_vflip(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_GAIN:
+		err = vv6410_set_analog_gain(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_EXPOSURE:
+		err = vv6410_set_exposure(gspca_dev, ctrl->val);
+		break;
+	}
+	return err;
+}
+
+static const struct v4l2_ctrl_ops vv6410_ctrl_ops = {
+	.s_ctrl = vv6410_s_ctrl,
+};
+
+static int vv6410_probe(struct sd *sd)
+{
+	u16 data;
+	int err;
+
+	err = stv06xx_read_sensor(sd, VV6410_DEVICEH, &data);
+	if (err < 0)
+		return -ENODEV;
+
+	if (data != 0x19)
+		return -ENODEV;
+
+	pr_info("vv6410 sensor detected\n");
+
+	sd->gspca_dev.cam.cam_mode = vv6410_mode;
+	sd->gspca_dev.cam.nmodes = ARRAY_SIZE(vv6410_mode);
+	return 0;
+}
+
+static int vv6410_init_controls(struct sd *sd)
+{
+	struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler;
+
+	v4l2_ctrl_handler_init(hdl, 2);
+	/* Disable the hardware VFLIP and HFLIP as we currently lack a
+	   mechanism to adjust the image offset in such a way that
+	   we don't need to renegotiate the announced format */
+	/* v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, */
+	/*		V4L2_CID_HFLIP, 0, 1, 1, 0); */
+	/* v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, */
+	/*		V4L2_CID_VFLIP, 0, 1, 1, 0); */
+	v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops,
+			V4L2_CID_EXPOSURE, 0, 32768, 1, 20000);
+	v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops,
+			V4L2_CID_GAIN, 0, 15, 1, 10);
+	return hdl->error;
+}
+
+static int vv6410_init(struct sd *sd)
+{
+	int err = 0, i;
+
+	for (i = 0; i < ARRAY_SIZE(stv_bridge_init); i++)
+		stv06xx_write_bridge(sd, stv_bridge_init[i].addr, stv_bridge_init[i].data);
+
+	if (err < 0)
+		return err;
+
+	err = stv06xx_write_sensor_bytes(sd, (u8 *) vv6410_sensor_init,
+					 ARRAY_SIZE(vv6410_sensor_init));
+	return (err < 0) ? err : 0;
+}
+
+static int vv6410_start(struct sd *sd)
+{
+	int err;
+	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
+	struct cam *cam = &sd->gspca_dev.cam;
+	u32 priv = cam->cam_mode[sd->gspca_dev.curr_mode].priv;
+
+	if (priv & VV6410_SUBSAMPLE) {
+		PDEBUG(D_CONF, "Enabling subsampling");
+		stv06xx_write_bridge(sd, STV_Y_CTRL, 0x02);
+		stv06xx_write_bridge(sd, STV_X_CTRL, 0x06);
+
+		stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x10);
+	} else {
+		stv06xx_write_bridge(sd, STV_Y_CTRL, 0x01);
+		stv06xx_write_bridge(sd, STV_X_CTRL, 0x0a);
+		stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x00);
+
+	}
+
+	/* Turn on LED */
+	err = stv06xx_write_bridge(sd, STV_LED_CTRL, LED_ON);
+	if (err < 0)
+		return err;
+
+	err = stv06xx_write_sensor(sd, VV6410_SETUP0, 0);
+	if (err < 0)
+		return err;
+
+	PDEBUG(D_STREAM, "Starting stream");
+
+	return 0;
+}
+
+static int vv6410_stop(struct sd *sd)
+{
+	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
+	int err;
+
+	/* Turn off LED */
+	err = stv06xx_write_bridge(sd, STV_LED_CTRL, LED_OFF);
+	if (err < 0)
+		return err;
+
+	err = stv06xx_write_sensor(sd, VV6410_SETUP0, VV6410_LOW_POWER_MODE);
+	if (err < 0)
+		return err;
+
+	PDEBUG(D_STREAM, "Halting stream");
+
+	return 0;
+}
+
+static int vv6410_dump(struct sd *sd)
+{
+	u8 i;
+	int err = 0;
+
+	pr_info("Dumping all vv6410 sensor registers\n");
+	for (i = 0; i < 0xff && !err; i++) {
+		u16 data;
+		err = stv06xx_read_sensor(sd, i, &data);
+		pr_info("Register 0x%x contained 0x%x\n", i, data);
+	}
+	return (err < 0) ? err : 0;
+}
+
+static int vv6410_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
+{
+	int err;
+	u16 i2c_data;
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	err = stv06xx_read_sensor(sd, VV6410_DATAFORMAT, &i2c_data);
+	if (err < 0)
+		return err;
+
+	if (val)
+		i2c_data |= VV6410_HFLIP;
+	else
+		i2c_data &= ~VV6410_HFLIP;
+
+	PDEBUG(D_CONF, "Set horizontal flip to %d", val);
+	err = stv06xx_write_sensor(sd, VV6410_DATAFORMAT, i2c_data);
+
+	return (err < 0) ? err : 0;
+}
+
+static int vv6410_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
+{
+	int err;
+	u16 i2c_data;
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	err = stv06xx_read_sensor(sd, VV6410_DATAFORMAT, &i2c_data);
+	if (err < 0)
+		return err;
+
+	if (val)
+		i2c_data |= VV6410_VFLIP;
+	else
+		i2c_data &= ~VV6410_VFLIP;
+
+	PDEBUG(D_CONF, "Set vertical flip to %d", val);
+	err = stv06xx_write_sensor(sd, VV6410_DATAFORMAT, i2c_data);
+
+	return (err < 0) ? err : 0;
+}
+
+static int vv6410_set_analog_gain(struct gspca_dev *gspca_dev, __s32 val)
+{
+	int err;
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	PDEBUG(D_CONF, "Set analog gain to %d", val);
+	err = stv06xx_write_sensor(sd, VV6410_ANALOGGAIN, 0xf0 | (val & 0xf));
+
+	return (err < 0) ? err : 0;
+}
+
+static int vv6410_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
+{
+	int err;
+	struct sd *sd = (struct sd *) gspca_dev;
+	unsigned int fine, coarse;
+
+	val = (val * val >> 14) + val / 4;
+
+	fine = val % VV6410_CIF_LINELENGTH;
+	coarse = min(512, val / VV6410_CIF_LINELENGTH);
+
+	PDEBUG(D_CONF, "Set coarse exposure to %d, fine exposure to %d",
+	       coarse, fine);
+
+	err = stv06xx_write_sensor(sd, VV6410_FINEH, fine >> 8);
+	if (err < 0)
+		goto out;
+
+	err = stv06xx_write_sensor(sd, VV6410_FINEL, fine & 0xff);
+	if (err < 0)
+		goto out;
+
+	err = stv06xx_write_sensor(sd, VV6410_COARSEH, coarse >> 8);
+	if (err < 0)
+		goto out;
+
+	err = stv06xx_write_sensor(sd, VV6410_COARSEL, coarse & 0xff);
+
+out:
+	return err;
+}
diff --git a/drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.h b/drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.h
new file mode 100644
index 0000000..53e67b4
--- /dev/null
+++ b/drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.h
@@ -0,0 +1,255 @@
+/*
+ * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
+ *		      Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
+ * Copyright (c) 2002, 2003 Tuukka Toivonen
+ * Copyright (c) 2008 Erik Andrén
+ *
+ * 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
+ *
+ * P/N 861037:      Sensor HDCS1000        ASIC STV0600
+ * P/N 861050-0010: Sensor HDCS1000        ASIC STV0600
+ * P/N 861050-0020: Sensor Photobit PB100  ASIC STV0600-1 - QuickCam Express
+ * P/N 861055:      Sensor ST VV6410       ASIC STV0610   - LEGO cam
+ * P/N 861075-0040: Sensor HDCS1000        ASIC
+ * P/N 961179-0700: Sensor ST VV6410       ASIC STV0602   - Dexxa WebCam USB
+ * P/N 861040-0000: Sensor ST VV6410       ASIC STV0610   - QuickCam Web
+ */
+
+#ifndef STV06XX_VV6410_H_
+#define STV06XX_VV6410_H_
+
+#include "stv06xx_sensor.h"
+
+#define VV6410_COLS			416
+#define VV6410_ROWS			320
+
+/* Status registers */
+/* Chip identification number including revision indicator */
+#define VV6410_DEVICEH			0x00
+#define VV6410_DEVICEL			0x01
+
+/* User can determine whether timed I2C data
+   has been consumed by interrogating flag states */
+#define VV6410_STATUS0			0x02
+
+/* Current line counter value */
+#define VV6410_LINECOUNTH		0x03
+#define VV6410_LINECOUNTL		0x04
+
+/* End x coordinate of image size */
+#define VV6410_XENDH			0x05
+#define VV6410_XENDL			0x06
+
+/* End y coordinate of image size */
+#define VV6410_YENDH			0x07
+#define VV6410_YENDL			0x08
+
+/* This is the average pixel value returned from the
+   dark line offset cancellation algorithm */
+#define VV6410_DARKAVGH			0x09
+#define VV6410_DARKAVGL			0x0a
+
+/* This is the average pixel value returned from the
+   black line offset cancellation algorithm  */
+#define VV6410_BLACKAVGH		0x0b
+#define VV6410_BLACKAVGL		0x0c
+
+/* Flags to indicate whether the x or y image coordinates have been clipped */
+#define VV6410_STATUS1			0x0d
+
+/* Setup registers */
+
+/* Low-power/sleep modes & video timing */
+#define VV6410_SETUP0			0x10
+
+/* Various parameters */
+#define VV6410_SETUP1			0x11
+
+/* Contains pixel counter reset value used by external sync */
+#define VV6410_SYNCVALUE		0x12
+
+/* Frame grabbing modes (FST, LST and QCK) */
+#define VV6410_FGMODES			0x14
+
+/* FST and QCK mapping modes. */
+#define VV6410_PINMAPPING		0x15
+
+/* Data resolution */
+#define VV6410_DATAFORMAT		0x16
+
+/* Output coding formats */
+#define VV6410_OPFORMAT			0x17
+
+/* Various mode select bits */
+#define VV6410_MODESELECT		0x18
+
+/* Exposure registers */
+/* Fine exposure. */
+#define VV6410_FINEH			0x20
+#define VV6410_FINEL			0x21
+
+/* Coarse exposure */
+#define VV6410_COARSEH			0x22
+#define VV6410_COARSEL			0x23
+
+/* Analog gain setting */
+#define VV6410_ANALOGGAIN		0x24
+
+/* Clock division */
+#define VV6410_CLKDIV			0x25
+
+/* Dark line offset cancellation value */
+#define VV6410_DARKOFFSETH		0x2c
+#define VV6410_DARKOFFSETL		0x2d
+
+/* Dark line offset cancellation enable */
+#define VV6410_DARKOFFSETSETUP		0x2e
+
+/* Video timing registers */
+/* Line Length (Pixel Clocks) */
+#define VV6410_LINELENGTHH		0x52
+#define VV6410_LINELENGTHL		0x53
+
+/* X-co-ordinate of top left corner of region of interest (x-offset) */
+#define VV6410_XOFFSETH			0x57
+#define VV6410_XOFFSETL			0x58
+
+/* Y-coordinate of top left corner of region of interest (y-offset) */
+#define VV6410_YOFFSETH			0x59
+#define VV6410_YOFFSETL			0x5a
+
+/* Field length (Lines) */
+#define VV6410_FIELDLENGTHH		0x61
+#define VV6410_FIELDLENGTHL		0x62
+
+/* System registers */
+/* Black offset cancellation default value */
+#define VV6410_BLACKOFFSETH		0x70
+#define VV6410_BLACKOFFSETL		0x71
+
+/* Black offset cancellation setup */
+#define VV6410_BLACKOFFSETSETUP		0x72
+
+/* Analog Control Register 0 */
+#define VV6410_CR0			0x75
+
+/* Analog Control Register 1 */
+#define VV6410_CR1			0x76
+
+/* ADC Setup Register */
+#define VV6410_AS0			0x77
+
+/* Analog Test Register */
+#define VV6410_AT0			0x78
+
+/* Audio Amplifier Setup Register */
+#define VV6410_AT1			0x79
+
+#define VV6410_HFLIP			(1 << 3)
+#define VV6410_VFLIP			(1 << 4)
+
+#define VV6410_LOW_POWER_MODE		(1 << 0)
+#define VV6410_SOFT_RESET		(1 << 2)
+#define VV6410_PAL_25_FPS		(0 << 3)
+
+#define VV6410_CLK_DIV_2		(1 << 1)
+
+#define VV6410_FINE_EXPOSURE		320
+#define VV6410_COARSE_EXPOSURE		192
+#define VV6410_DEFAULT_GAIN		5
+
+#define VV6410_SUBSAMPLE		0x01
+#define VV6410_CROP_TO_QVGA		0x02
+
+#define VV6410_CIF_LINELENGTH		415
+
+static int vv6410_probe(struct sd *sd);
+static int vv6410_start(struct sd *sd);
+static int vv6410_init(struct sd *sd);
+static int vv6410_init_controls(struct sd *sd);
+static int vv6410_stop(struct sd *sd);
+static int vv6410_dump(struct sd *sd);
+
+/* V4L2 controls supported by the driver */
+static int vv6410_set_hflip(struct gspca_dev *gspca_dev, __s32 val);
+static int vv6410_set_vflip(struct gspca_dev *gspca_dev, __s32 val);
+static int vv6410_set_analog_gain(struct gspca_dev *gspca_dev, __s32 val);
+static int vv6410_set_exposure(struct gspca_dev *gspca_dev, __s32 val);
+
+const struct stv06xx_sensor stv06xx_sensor_vv6410 = {
+	.name = "ST VV6410",
+	.i2c_flush = 5,
+	.i2c_addr = 0x20,
+	.i2c_len = 1,
+	/* FIXME (see if we can lower packet_size-s, needs testing, and also
+	   adjusting framerate when the bandwidth gets lower) */
+	.min_packet_size = { 1023 },
+	.max_packet_size = { 1023 },
+	.init = vv6410_init,
+	.init_controls = vv6410_init_controls,
+	.probe = vv6410_probe,
+	.start = vv6410_start,
+	.stop = vv6410_stop,
+	.dump = vv6410_dump,
+};
+
+/* If NULL, only single value to write, stored in len */
+struct stv_init {
+	u16 addr;
+	u8 data;
+};
+
+static const struct stv_init stv_bridge_init[] = {
+	/* This reg is written twice. Some kind of reset? */
+	{STV_RESET, 0x80},
+	{STV_RESET, 0x00},
+	{STV_SCAN_RATE, 0x00},
+	{STV_I2C_FLUSH, 0x04},
+	{STV_REG00, 0x0b},
+	{STV_REG01, 0xa7},
+	{STV_REG02, 0xb7},
+	{STV_REG03, 0x00},
+	{STV_REG04, 0x00},
+	{0x1536, 0x02},
+	{0x1537, 0x00},
+	{0x1538, 0x60},
+	{0x1539, 0x01},
+	{0x153a, 0x20},
+	{0x153b, 0x01},
+};
+
+static const u8 vv6410_sensor_init[][2] = {
+	/* Setup registers */
+	{VV6410_SETUP0,	VV6410_SOFT_RESET},
+	{VV6410_SETUP0,	VV6410_LOW_POWER_MODE},
+	/* Use shuffled read-out mode */
+	{VV6410_SETUP1,	BIT(6)},
+	/* All modes to 1, FST, Fast QCK, Free running QCK, Free running LST, FST will qualify visible pixels */
+	{VV6410_FGMODES, BIT(6) | BIT(4) | BIT(2) | BIT(0)},
+	{VV6410_PINMAPPING, 0x00},
+	/* Pre-clock generator divide off */
+	{VV6410_DATAFORMAT, BIT(7) | BIT(0)},
+
+	{VV6410_CLKDIV,	VV6410_CLK_DIV_2},
+
+	/* System registers */
+	/* Enable voltage doubler */
+	{VV6410_AS0, BIT(6) | BIT(4) | BIT(3) | BIT(2) | BIT(1)},
+	{VV6410_AT0, 0x00},
+	/* Power up audio, differential */
+	{VV6410_AT1, BIT(4) | BIT(0)},
+};
+
+#endif
diff --git a/drivers/media/usb/gspca/sunplus.c b/drivers/media/usb/gspca/sunplus.c
new file mode 100644
index 0000000..46c9f22
--- /dev/null
+++ b/drivers/media/usb/gspca/sunplus.c
@@ -0,0 +1,1076 @@
+/*
+ *		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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "sunplus"
+
+#include "gspca.h"
+#include "jpeg.h"
+
+MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
+MODULE_DESCRIPTION("GSPCA/SPCA5xx USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+#define QUALITY 85
+
+/* specific webcam descriptor */
+struct sd {
+	struct gspca_dev gspca_dev;	/* !! must be the first item */
+
+	bool autogain;
+
+	u8 bridge;
+#define BRIDGE_SPCA504 0
+#define BRIDGE_SPCA504B 1
+#define BRIDGE_SPCA504C 2
+#define BRIDGE_SPCA533 3
+#define BRIDGE_SPCA536 4
+	u8 subtype;
+#define AiptekMiniPenCam13 1
+#define LogitechClickSmart420 2
+#define LogitechClickSmart820 3
+#define MegapixV4 4
+#define MegaImageVI 5
+
+	u8 jpeg_hdr[JPEG_HDR_SZ];
+};
+
+static const 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 const 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 const 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
+
+struct cmd {
+	u8 req;
+	u16 val;
+	u16 idx;
+};
+
+/* Initialisation data for the Creative PC-CAM 600 */
+static const struct cmd spca504_pccam600_init_data[] = {
+/*	{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 struct cmd spca504_pccam600_open_data[] = {
+	{0x00, 0x0001, 0x2501},
+	{0x20, 0x0500, 0x0001},	/* snapshot mode */
+	{0x00, 0x0003, 0x2880},
+	{0x00, 0x0001, 0x2881},
+};
+
+/* Initialisation data for the logitech clicksmart 420 */
+static const struct cmd spca504A_clicksmart420_init_data[] = {
+/*	{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},
+
+	{0xa1, 0x0080, 0x0001},
+	{0x30, 0x0049, 0x0000},
+	{0x30, 0x0060, 0x0005},
+	{0x0c, 0x0004, 0x0000},
+	{0x00, 0x0000, 0x0000},
+	{0x00, 0x0000, 0x2000},
+	{0x00, 0x0013, 0x2301},
+	{0x00, 0x0003, 0x2000},
+};
+
+/* clicksmart 420 open data ? */
+static const struct cmd spca504A_clicksmart420_open_data[] = {
+	{0x00, 0x0001, 0x2501},
+	{0x20, 0x0502, 0x0000},
+	{0x06, 0x0000, 0x0000},
+	{0x00, 0x0004, 0x2880},
+	{0x00, 0x0001, 0x2881},
+
+	{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}
+};
+
+/* read <len> bytes to gspca_dev->usb_buf */
+static void reg_r(struct gspca_dev *gspca_dev,
+		  u8 req,
+		  u16 index,
+		  u16 len)
+{
+	int ret;
+
+	if (len > USB_BUF_SZ) {
+		PERR("reg_r: buffer overflow\n");
+		return;
+	}
+	if (gspca_dev->usb_err < 0)
+		return;
+	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,
+			len ? gspca_dev->usb_buf : NULL, len,
+			500);
+	if (ret < 0) {
+		pr_err("reg_r err %d\n", ret);
+		gspca_dev->usb_err = ret;
+	}
+}
+
+/* write one byte */
+static void reg_w_1(struct gspca_dev *gspca_dev,
+		   u8 req,
+		   u16 value,
+		   u16 index,
+		   u16 byte)
+{
+	int ret;
+
+	if (gspca_dev->usb_err < 0)
+		return;
+	gspca_dev->usb_buf[0] = byte;
+	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,
+			gspca_dev->usb_buf, 1,
+			500);
+	if (ret < 0) {
+		pr_err("reg_w_1 err %d\n", ret);
+		gspca_dev->usb_err = ret;
+	}
+}
+
+/* write req / index / value */
+static void reg_w_riv(struct gspca_dev *gspca_dev,
+		     u8 req, u16 index, u16 value)
+{
+	struct usb_device *dev = gspca_dev->dev;
+	int ret;
+
+	if (gspca_dev->usb_err < 0)
+		return;
+	ret = usb_control_msg(dev,
+			usb_sndctrlpipe(dev, 0),
+			req,
+			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			value, index, NULL, 0, 500);
+	if (ret < 0) {
+		pr_err("reg_w_riv err %d\n", ret);
+		gspca_dev->usb_err = ret;
+		return;
+	}
+	PDEBUG(D_USBO, "reg_w_riv: 0x%02x,0x%04x:0x%04x",
+		req, index, value);
+}
+
+static void write_vector(struct gspca_dev *gspca_dev,
+			const struct cmd *data, int ncmds)
+{
+	while (--ncmds >= 0) {
+		reg_w_riv(gspca_dev, data->req, data->idx, data->val);
+		data++;
+	}
+}
+
+static void setup_qtable(struct gspca_dev *gspca_dev,
+			const u8 qtable[2][64])
+{
+	int i;
+
+	/* loop over y components */
+	for (i = 0; i < 64; i++)
+		reg_w_riv(gspca_dev, 0x00, 0x2800 + i, qtable[0][i]);
+
+	/* loop over c components */
+	for (i = 0; i < 64; i++)
+		reg_w_riv(gspca_dev, 0x00, 0x2840 + i, qtable[1][i]);
+}
+
+static void spca504_acknowledged_command(struct gspca_dev *gspca_dev,
+			     u8 req, u16 idx, u16 val)
+{
+	reg_w_riv(gspca_dev, req, idx, val);
+	reg_r(gspca_dev, 0x01, 0x0001, 1);
+	PDEBUG(D_FRAM, "before wait 0x%04x", gspca_dev->usb_buf[0]);
+	reg_w_riv(gspca_dev, req, idx, val);
+
+	msleep(200);
+	reg_r(gspca_dev, 0x01, 0x0001, 1);
+	PDEBUG(D_FRAM, "after wait 0x%04x", gspca_dev->usb_buf[0]);
+}
+
+static void spca504_read_info(struct gspca_dev *gspca_dev)
+{
+	int i;
+	u8 info[6];
+
+	if (gspca_debug < D_STREAM)
+		return;
+
+	for (i = 0; i < 6; i++) {
+		reg_r(gspca_dev, 0, i, 1);
+		info[i] = gspca_dev->usb_buf[0];
+	}
+	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]);
+}
+
+static void spca504A_acknowledged_command(struct gspca_dev *gspca_dev,
+			u8 req,
+			u16 idx, u16 val, u8 endcode, u8 count)
+{
+	u16 status;
+
+	reg_w_riv(gspca_dev, req, idx, val);
+	reg_r(gspca_dev, 0x01, 0x0001, 1);
+	if (gspca_dev->usb_err < 0)
+		return;
+	PDEBUG(D_FRAM, "Status 0x%02x Need 0x%02x",
+			gspca_dev->usb_buf[0], endcode);
+	if (!count)
+		return;
+	count = 200;
+	while (--count > 0) {
+		msleep(10);
+		/* gsmart mini2 write a each wait setting 1 ms is enough */
+/*		reg_w_riv(gspca_dev, req, idx, val); */
+		reg_r(gspca_dev, 0x01, 0x0001, 1);
+		status = gspca_dev->usb_buf[0];
+		if (status == endcode) {
+			PDEBUG(D_FRAM, "status 0x%04x after wait %d",
+				status, 200 - count);
+				break;
+		}
+	}
+}
+
+static void spca504B_PollingDataReady(struct gspca_dev *gspca_dev)
+{
+	int count = 10;
+
+	while (--count > 0) {
+		reg_r(gspca_dev, 0x21, 0, 1);
+		if ((gspca_dev->usb_buf[0] & 0x01) == 0)
+			break;
+		msleep(10);
+	}
+}
+
+static void spca504B_WaitCmdStatus(struct gspca_dev *gspca_dev)
+{
+	int count = 50;
+
+	while (--count > 0) {
+		reg_r(gspca_dev, 0x21, 1, 1);
+		if (gspca_dev->usb_buf[0] != 0) {
+			reg_w_1(gspca_dev, 0x21, 0, 1, 0);
+			reg_r(gspca_dev, 0x21, 1, 1);
+			spca504B_PollingDataReady(gspca_dev);
+			break;
+		}
+		msleep(10);
+	}
+}
+
+static void spca50x_GetFirmware(struct gspca_dev *gspca_dev)
+{
+	u8 *data;
+
+	if (gspca_debug < D_STREAM)
+		return;
+
+	data = gspca_dev->usb_buf;
+	reg_r(gspca_dev, 0x20, 0, 5);
+	PDEBUG(D_STREAM, "FirmWare: %d %d %d %d %d",
+		data[0], data[1], data[2], data[3], data[4]);
+	reg_r(gspca_dev, 0x23, 0, 64);
+	reg_r(gspca_dev, 0x23, 1, 64);
+}
+
+static void spca504B_SetSizeType(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u8 Size;
+
+	Size = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
+	switch (sd->bridge) {
+	case BRIDGE_SPCA533:
+		reg_w_riv(gspca_dev, 0x31, 0, 0);
+		spca504B_WaitCmdStatus(gspca_dev);
+		spca504B_PollingDataReady(gspca_dev);
+		spca50x_GetFirmware(gspca_dev);
+
+		reg_w_1(gspca_dev, 0x24, 0, 8, 2);		/* type */
+		reg_r(gspca_dev, 0x24, 8, 1);
+
+		reg_w_1(gspca_dev, 0x25, 0, 4, Size);
+		reg_r(gspca_dev, 0x25, 4, 1);			/* size */
+		spca504B_PollingDataReady(gspca_dev);
+
+		/* Init the cam width height with some values get on init ? */
+		reg_w_riv(gspca_dev, 0x31, 0x0004, 0x00);
+		spca504B_WaitCmdStatus(gspca_dev);
+		spca504B_PollingDataReady(gspca_dev);
+		break;
+	default:
+/* case BRIDGE_SPCA504B: */
+/* case BRIDGE_SPCA536: */
+		reg_w_1(gspca_dev, 0x25, 0, 4, Size);
+		reg_r(gspca_dev, 0x25, 4, 1);			/* size */
+		reg_w_1(gspca_dev, 0x27, 0, 0, 6);
+		reg_r(gspca_dev, 0x27, 0, 1);			/* type */
+		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(gspca_dev, 0xa0, (0x0500 | (Size & 0x0f)), 0x00);
+		reg_w_riv(gspca_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 */
+		reg_r(gspca_dev, 0x06, 0x00, 1);
+		if (gspca_dev->usb_buf[0] == 0)
+			return;
+		msleep(10);
+	}
+}
+
+static void spca504B_setQtable(struct gspca_dev *gspca_dev)
+{
+	reg_w_1(gspca_dev, 0x26, 0, 0, 3);
+	reg_r(gspca_dev, 0x26, 0, 1);
+	spca504B_PollingDataReady(gspca_dev);
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u16 reg;
+
+	reg = sd->bridge == BRIDGE_SPCA536 ? 0x20f0 : 0x21a7;
+	reg_w_riv(gspca_dev, 0x00, reg, val);
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u16 reg;
+
+	reg = sd->bridge == BRIDGE_SPCA536 ? 0x20f1 : 0x21a8;
+	reg_w_riv(gspca_dev, 0x00, reg, val);
+}
+
+static void setcolors(struct gspca_dev *gspca_dev, s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u16 reg;
+
+	reg = sd->bridge == BRIDGE_SPCA536 ? 0x20f6 : 0x21ae;
+	reg_w_riv(gspca_dev, 0x00, reg, val);
+}
+
+static void init_ctl_reg(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_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_riv(gspca_dev, 0, 0x21ad, 0x00);	/* hue */
+		reg_w_riv(gspca_dev, 0, 0x21ac, 0x01);	/* sat/hue */
+		reg_w_riv(gspca_dev, 0, 0x21a3, 0x00);	/* gamma */
+		break;
+	case BRIDGE_SPCA536:
+		reg_w_riv(gspca_dev, 0, 0x20f5, 0x40);
+		reg_w_riv(gspca_dev, 0, 0x20f4, 0x01);
+		reg_w_riv(gspca_dev, 0, 0x2089, 0x00);
+		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 cam *cam;
+
+	cam = &gspca_dev->cam;
+
+	sd->bridge = id->driver_info >> 8;
+	sd->subtype = id->driver_info;
+
+	if (sd->subtype == AiptekMiniPenCam13) {
+
+		/* 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(gspca_dev, 0x20, 0, 1);
+		switch (gspca_dev->usb_buf[0]) {
+		case 1:
+			break;		/* (right bridge/subtype) */
+		case 2:
+			sd->bridge = BRIDGE_SPCA504B;
+			sd->subtype = 0;
+			break;
+		default:
+			return -ENODEV;
+		}
+	}
+
+	switch (sd->bridge) {
+	default:
+/*	case BRIDGE_SPCA504B: */
+/*	case BRIDGE_SPCA504: */
+/*	case BRIDGE_SPCA536: */
+		cam->cam_mode = vga_mode;
+		cam->nmodes = ARRAY_SIZE(vga_mode);
+		break;
+	case BRIDGE_SPCA533:
+		cam->cam_mode = custom_mode;
+		if (sd->subtype == MegaImageVI)		/* 320x240 only */
+			cam->nmodes = ARRAY_SIZE(custom_mode) - 1;
+		else
+			cam->nmodes = ARRAY_SIZE(custom_mode);
+		break;
+	case BRIDGE_SPCA504C:
+		cam->cam_mode = vga_mode2;
+		cam->nmodes = ARRAY_SIZE(vga_mode2);
+		break;
+	}
+	return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	switch (sd->bridge) {
+	case BRIDGE_SPCA504B:
+		reg_w_riv(gspca_dev, 0x1d, 0x00, 0);
+		reg_w_riv(gspca_dev, 0x00, 0x2306, 0x01);
+		reg_w_riv(gspca_dev, 0x00, 0x0d04, 0x00);
+		reg_w_riv(gspca_dev, 0x00, 0x2000, 0x00);
+		reg_w_riv(gspca_dev, 0x00, 0x2301, 0x13);
+		reg_w_riv(gspca_dev, 0x00, 0x2306, 0x00);
+		/* fall thru */
+	case BRIDGE_SPCA533:
+		spca504B_PollingDataReady(gspca_dev);
+		spca50x_GetFirmware(gspca_dev);
+		break;
+	case BRIDGE_SPCA536:
+		spca50x_GetFirmware(gspca_dev);
+		reg_r(gspca_dev, 0x00, 0x5002, 1);
+		reg_w_1(gspca_dev, 0x24, 0, 0, 0);
+		reg_r(gspca_dev, 0x24, 0, 1);
+		spca504B_PollingDataReady(gspca_dev);
+		reg_w_riv(gspca_dev, 0x34, 0, 0);
+		spca504B_WaitCmdStatus(gspca_dev);
+		break;
+	case BRIDGE_SPCA504C:	/* pccam600 */
+		PDEBUG(D_STREAM, "Opening SPCA504 (PC-CAM 600)");
+		reg_w_riv(gspca_dev, 0xe0, 0x0000, 0x0000);
+		reg_w_riv(gspca_dev, 0xe0, 0x0000, 0x0001);	/* reset */
+		spca504_wait_status(gspca_dev);
+		if (sd->subtype == LogitechClickSmart420)
+			write_vector(gspca_dev,
+				spca504A_clicksmart420_open_data,
+				ARRAY_SIZE(spca504A_clicksmart420_open_data));
+		else
+			write_vector(gspca_dev, spca504_pccam600_open_data,
+				ARRAY_SIZE(spca504_pccam600_open_data));
+		setup_qtable(gspca_dev, qtable_creative_pccam);
+		break;
+	default:
+/*	case BRIDGE_SPCA504: */
+		PDEBUG(D_STREAM, "Opening SPCA504");
+		if (sd->subtype == AiptekMiniPenCam13) {
+			spca504_read_info(gspca_dev);
+
+			/* Set AE AWB Banding Type 3-> 50Hz 2-> 60Hz */
+			spca504A_acknowledged_command(gspca_dev, 0x24,
+							8, 3, 0x9e, 1);
+			/* Twice sequential 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(gspca_dev, 0x00, 0x270c, 0x05);
+							/* L92 sno1t.txt */
+			reg_w_riv(gspca_dev, 0x00, 0x2310, 0x05);
+			spca504A_acknowledged_command(gspca_dev, 0x01,
+							0x0f, 0, 0xff, 0);
+		}
+		/* setup qtable */
+		reg_w_riv(gspca_dev, 0, 0x2000, 0);
+		reg_w_riv(gspca_dev, 0, 0x2883, 1);
+		setup_qtable(gspca_dev, qtable_spca504_default);
+		break;
+	}
+	return gspca_dev->usb_err;
+}
+
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int enable;
+
+	/* create the JPEG header */
+	jpeg_define(sd->jpeg_hdr, gspca_dev->pixfmt.height,
+			gspca_dev->pixfmt.width,
+			0x22);		/* JPEG 411 */
+	jpeg_set_qual(sd->jpeg_hdr, QUALITY);
+
+	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: */
+		switch (sd->subtype) {
+		case MegapixV4:
+		case LogitechClickSmart820:
+		case MegaImageVI:
+			reg_w_riv(gspca_dev, 0xf0, 0, 0);
+			spca504B_WaitCmdStatus(gspca_dev);
+			reg_r(gspca_dev, 0xf0, 4, 0);
+			spca504B_WaitCmdStatus(gspca_dev);
+			break;
+		default:
+			reg_w_riv(gspca_dev, 0x31, 0x0004, 0x00);
+			spca504B_WaitCmdStatus(gspca_dev);
+			spca504B_PollingDataReady(gspca_dev);
+			break;
+		}
+		break;
+	case BRIDGE_SPCA504:
+		if (sd->subtype == AiptekMiniPenCam13) {
+			spca504_read_info(gspca_dev);
+
+			/* Set AE AWB Banding Type 3-> 50Hz 2-> 60Hz */
+			spca504A_acknowledged_command(gspca_dev, 0x24,
+							8, 3, 0x9e, 1);
+			/* Twice sequential 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);
+			spca504_read_info(gspca_dev);
+			spca504_acknowledged_command(gspca_dev, 0x24, 8, 3);
+			spca504_acknowledged_command(gspca_dev, 0x24, 0, 0);
+		}
+		spca504B_SetSizeType(gspca_dev);
+		reg_w_riv(gspca_dev, 0x00, 0x270c, 0x05);
+							/* L92 sno1t.txt */
+		reg_w_riv(gspca_dev, 0x00, 0x2310, 0x05);
+		break;
+	case BRIDGE_SPCA504C:
+		if (sd->subtype == LogitechClickSmart420) {
+			write_vector(gspca_dev,
+				spca504A_clicksmart420_init_data,
+				ARRAY_SIZE(spca504A_clicksmart420_init_data));
+		} else {
+			write_vector(gspca_dev, spca504_pccam600_init_data,
+				ARRAY_SIZE(spca504_pccam600_init_data));
+		}
+		enable = (sd->autogain ? 0x04 : 0x01);
+		reg_w_riv(gspca_dev, 0x0c, 0x0000, enable);
+							/* auto exposure */
+		reg_w_riv(gspca_dev, 0xb0, 0x0000, enable);
+							/* auto whiteness */
+
+		/* set default exposure compensation and whiteness balance */
+		reg_w_riv(gspca_dev, 0x30, 0x0001, 800);	/* ~ 20 fps */
+		reg_w_riv(gspca_dev, 0x30, 0x0002, 1600);
+		spca504B_SetSizeType(gspca_dev);
+		break;
+	}
+	init_ctl_reg(gspca_dev);
+	return gspca_dev->usb_err;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	switch (sd->bridge) {
+	default:
+/*	case BRIDGE_SPCA533: */
+/*	case BRIDGE_SPCA536: */
+/*	case BRIDGE_SPCA504B: */
+		reg_w_riv(gspca_dev, 0x31, 0, 0);
+		spca504B_WaitCmdStatus(gspca_dev);
+		spca504B_PollingDataReady(gspca_dev);
+		break;
+	case BRIDGE_SPCA504:
+	case BRIDGE_SPCA504C:
+		reg_w_riv(gspca_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(gspca_dev, 0x01, 0x000f, 0x0000);
+		}
+		break;
+	}
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+			u8 *data,			/* isoc packet */
+			int len)			/* iso packet length */
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int i, sof = 0;
+	static u8 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 */
+		gspca_frame_add(gspca_dev, LAST_PACKET,
+				ffd9, 2);
+
+		/* put the JPEG header in the new frame */
+		gspca_frame_add(gspca_dev, FIRST_PACKET,
+			sd->jpeg_hdr, JPEG_HDR_SZ);
+	}
+
+	/* add 0x00 after 0xff */
+	i = 0;
+	do {
+		if (data[i] == 0xff) {
+			gspca_frame_add(gspca_dev, INTER_PACKET,
+					data, i + 1);
+			len -= i;
+			data += i;
+			*data = 0x00;
+			i = 0;
+		}
+		i++;
+	} while (i < len);
+	gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+}
+
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct gspca_dev *gspca_dev =
+		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+	struct sd *sd = (struct sd *)gspca_dev;
+
+	gspca_dev->usb_err = 0;
+
+	if (!gspca_dev->streaming)
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_BRIGHTNESS:
+		setbrightness(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_CONTRAST:
+		setcontrast(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_SATURATION:
+		setcolors(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_AUTOGAIN:
+		sd->autogain = ctrl->val;
+		break;
+	}
+	return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+	.s_ctrl = sd_s_ctrl,
+};
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+	gspca_dev->vdev.ctrl_handler = hdl;
+	v4l2_ctrl_handler_init(hdl, 4);
+	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_BRIGHTNESS, -128, 127, 1, 0);
+	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_CONTRAST, 0, 255, 1, 0x20);
+	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_SATURATION, 0, 255, 1, 0x1a);
+	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+
+	if (hdl->error) {
+		pr_err("Could not initialize controls\n");
+		return hdl->error;
+	}
+	return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+	.name = MODULE_NAME,
+	.config = sd_config,
+	.init = sd_init,
+	.init_controls = sd_init_controls,
+	.start = sd_start,
+	.stopN = sd_stopN,
+	.pkt_scan = sd_pkt_scan,
+};
+
+/* -- module initialisation -- */
+#define BS(bridge, subtype) \
+	.driver_info = (BRIDGE_ ## bridge << 8) \
+			| (subtype)
+static const struct usb_device_id device_table[] = {
+	{USB_DEVICE(0x041e, 0x400b), BS(SPCA504C, 0)},
+	{USB_DEVICE(0x041e, 0x4012), BS(SPCA504C, 0)},
+	{USB_DEVICE(0x041e, 0x4013), BS(SPCA504C, 0)},
+	{USB_DEVICE(0x0458, 0x7006), BS(SPCA504B, 0)},
+	{USB_DEVICE(0x0461, 0x0821), BS(SPCA533, 0)},
+	{USB_DEVICE(0x046d, 0x0905), BS(SPCA533, LogitechClickSmart820)},
+	{USB_DEVICE(0x046d, 0x0960), BS(SPCA504C, LogitechClickSmart420)},
+	{USB_DEVICE(0x0471, 0x0322), BS(SPCA504B, 0)},
+	{USB_DEVICE(0x04a5, 0x3003), BS(SPCA504B, 0)},
+	{USB_DEVICE(0x04a5, 0x3008), BS(SPCA533, 0)},
+	{USB_DEVICE(0x04a5, 0x300a), BS(SPCA533, 0)},
+	{USB_DEVICE(0x04f1, 0x1001), BS(SPCA504B, 0)},
+	{USB_DEVICE(0x04fc, 0x500c), BS(SPCA504B, 0)},
+	{USB_DEVICE(0x04fc, 0x504a), BS(SPCA504, AiptekMiniPenCam13)},
+	{USB_DEVICE(0x04fc, 0x504b), BS(SPCA504B, 0)},
+	{USB_DEVICE(0x04fc, 0x5330), BS(SPCA533, 0)},
+	{USB_DEVICE(0x04fc, 0x5360), BS(SPCA536, 0)},
+	{USB_DEVICE(0x04fc, 0xffff), BS(SPCA504B, 0)},
+	{USB_DEVICE(0x052b, 0x1507), BS(SPCA533, MegapixV4)},
+	{USB_DEVICE(0x052b, 0x1513), BS(SPCA533, MegapixV4)},
+	{USB_DEVICE(0x052b, 0x1803), BS(SPCA533, MegaImageVI)},
+	{USB_DEVICE(0x0546, 0x3155), BS(SPCA533, 0)},
+	{USB_DEVICE(0x0546, 0x3191), BS(SPCA504B, 0)},
+	{USB_DEVICE(0x0546, 0x3273), BS(SPCA504B, 0)},
+	{USB_DEVICE(0x055f, 0xc211), BS(SPCA536, 0)},
+	{USB_DEVICE(0x055f, 0xc230), BS(SPCA533, 0)},
+	{USB_DEVICE(0x055f, 0xc232), BS(SPCA533, 0)},
+	{USB_DEVICE(0x055f, 0xc360), BS(SPCA536, 0)},
+	{USB_DEVICE(0x055f, 0xc420), BS(SPCA504, 0)},
+	{USB_DEVICE(0x055f, 0xc430), BS(SPCA533, 0)},
+	{USB_DEVICE(0x055f, 0xc440), BS(SPCA533, 0)},
+	{USB_DEVICE(0x055f, 0xc520), BS(SPCA504, 0)},
+	{USB_DEVICE(0x055f, 0xc530), BS(SPCA533, 0)},
+	{USB_DEVICE(0x055f, 0xc540), BS(SPCA533, 0)},
+	{USB_DEVICE(0x055f, 0xc630), BS(SPCA533, 0)},
+	{USB_DEVICE(0x055f, 0xc650), BS(SPCA533, 0)},
+	{USB_DEVICE(0x05da, 0x1018), BS(SPCA504B, 0)},
+	{USB_DEVICE(0x06d6, 0x0031), BS(SPCA533, 0)},
+	{USB_DEVICE(0x06d6, 0x0041), BS(SPCA504B, 0)},
+	{USB_DEVICE(0x0733, 0x1311), BS(SPCA533, 0)},
+	{USB_DEVICE(0x0733, 0x1314), BS(SPCA533, 0)},
+	{USB_DEVICE(0x0733, 0x2211), BS(SPCA533, 0)},
+	{USB_DEVICE(0x0733, 0x2221), BS(SPCA533, 0)},
+	{USB_DEVICE(0x0733, 0x3261), BS(SPCA536, 0)},
+	{USB_DEVICE(0x0733, 0x3281), BS(SPCA536, 0)},
+	{USB_DEVICE(0x08ca, 0x0104), BS(SPCA533, 0)},
+	{USB_DEVICE(0x08ca, 0x0106), BS(SPCA533, 0)},
+	{USB_DEVICE(0x08ca, 0x2008), BS(SPCA504B, 0)},
+	{USB_DEVICE(0x08ca, 0x2010), BS(SPCA533, 0)},
+	{USB_DEVICE(0x08ca, 0x2016), BS(SPCA504B, 0)},
+	{USB_DEVICE(0x08ca, 0x2018), BS(SPCA504B, 0)},
+	{USB_DEVICE(0x08ca, 0x2020), BS(SPCA533, 0)},
+	{USB_DEVICE(0x08ca, 0x2022), BS(SPCA533, 0)},
+	{USB_DEVICE(0x08ca, 0x2024), BS(SPCA536, 0)},
+	{USB_DEVICE(0x08ca, 0x2028), BS(SPCA533, 0)},
+	{USB_DEVICE(0x08ca, 0x2040), BS(SPCA536, 0)},
+	{USB_DEVICE(0x08ca, 0x2042), BS(SPCA536, 0)},
+	{USB_DEVICE(0x08ca, 0x2050), BS(SPCA536, 0)},
+	{USB_DEVICE(0x08ca, 0x2060), BS(SPCA536, 0)},
+	{USB_DEVICE(0x0d64, 0x0303), BS(SPCA536, 0)},
+	{}
+};
+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,
+#ifdef CONFIG_PM
+	.suspend = gspca_suspend,
+	.resume = gspca_resume,
+	.reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/usb/gspca/t613.c b/drivers/media/usb/gspca/t613.c
new file mode 100644
index 0000000..bb52fc1
--- /dev/null
+++ b/drivers/media/usb/gspca/t613.c
@@ -0,0 +1,1054 @@
+/*
+ * T613 subdriver
+ *
+ * Copyright (C) 2010 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
+ *
+ *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
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "t613"
+
+#include <linux/input.h>
+#include <linux/slab.h>
+#include "gspca.h"
+
+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 */
+	struct v4l2_ctrl *freq;
+	struct { /* awb / color gains control cluster */
+		struct v4l2_ctrl *awb;
+		struct v4l2_ctrl *gain;
+		struct v4l2_ctrl *red_balance;
+		struct v4l2_ctrl *blue_balance;
+	};
+
+	u8 sensor;
+	u8 button_pressed;
+};
+enum sensors {
+	SENSOR_OM6802,
+	SENSOR_OTHER,
+	SENSOR_TAS5130A,
+	SENSOR_LT168G,		/* must verify if this is the actual model */
+};
+
+static const struct v4l2_pix_format vga_mode_t16[] = {
+	{160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+		.bytesperline = 160,
+		.sizeimage = 160 * 120 * 4 / 8 + 590,
+		.colorspace = V4L2_COLORSPACE_JPEG,
+		.priv = 4},
+#if 0 /* HDG: broken with my test cam, so lets disable it */
+	{176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+		.bytesperline = 176,
+		.sizeimage = 176 * 144 * 3 / 8 + 590,
+		.colorspace = V4L2_COLORSPACE_JPEG,
+		.priv = 3},
+#endif
+	{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+		.bytesperline = 320,
+		.sizeimage = 320 * 240 * 3 / 8 + 590,
+		.colorspace = V4L2_COLORSPACE_JPEG,
+		.priv = 2},
+#if 0 /* HDG: broken with my test cam, so lets disable it */
+	{352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+		.bytesperline = 352,
+		.sizeimage = 352 * 288 * 3 / 8 + 590,
+		.colorspace = V4L2_COLORSPACE_JPEG,
+		.priv = 1},
+#endif
+	{640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+		.bytesperline = 640,
+		.sizeimage = 640 * 480 * 3 / 8 + 590,
+		.colorspace = V4L2_COLORSPACE_JPEG,
+		.priv = 0},
+};
+
+/* sensor specific data */
+struct additional_sensor_data {
+	const u8 n3[6];
+	const u8 *n4, n4sz;
+	const u8 reg80, reg8e;
+	const u8 nset8[6];
+	const u8 data1[10];
+	const u8 data2[9];
+	const u8 data3[9];
+	const u8 data5[6];
+	const u8 stream[4];
+};
+
+static const u8 n4_om6802[] = {
+	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 n4_other[] = {
+	0x66, 0x00, 0x7f, 0x00, 0x80, 0xac, 0x81, 0x69,
+	0x84, 0x40, 0x85, 0x70, 0x86, 0x20, 0x8a, 0x68,
+	0x8b, 0x58, 0x8c, 0x88, 0x8d, 0xff, 0x8e, 0xb8,
+	0x8f, 0x28, 0xa2, 0x60, 0xa5, 0x40, 0xa8, 0xa8,
+	0xac, 0x84, 0xad, 0x84, 0xae, 0x24, 0xaf, 0x56,
+	0xb0, 0x68, 0xb1, 0x00, 0xb2, 0x88, 0xbb, 0xc5,
+	0xbc, 0x4a, 0xbe, 0x36, 0xc2, 0x88, 0xc5, 0xc0,
+	0xc6, 0xda, 0xe9, 0x26, 0xeb, 0x00
+};
+static const u8 n4_tas5130a[] = {
+	0x80, 0x3c, 0x81, 0x68, 0x83, 0xa0, 0x84, 0x20,
+	0x8a, 0x68, 0x8b, 0x58, 0x8c, 0x88, 0x8e, 0xb4,
+	0x8f, 0x24, 0xa1, 0xb1, 0xa2, 0x30, 0xa5, 0x10,
+	0xa6, 0x4a, 0xae, 0x03, 0xb1, 0x44, 0xb2, 0x08,
+	0xb7, 0x06, 0xb9, 0xe7, 0xbb, 0xc4, 0xbc, 0x4a,
+	0xbe, 0x36, 0xbf, 0xff, 0xc2, 0x88, 0xc5, 0xc8,
+	0xc6, 0xda
+};
+static const u8 n4_lt168g[] = {
+	0x66, 0x01, 0x7f, 0x00, 0x80, 0x7c, 0x81, 0x28,
+	0x83, 0x44, 0x84, 0x20, 0x86, 0x20, 0x8a, 0x70,
+	0x8b, 0x58, 0x8c, 0x88, 0x8d, 0xa0, 0x8e, 0xb3,
+	0x8f, 0x24, 0xa1, 0xb0, 0xa2, 0x38, 0xa5, 0x20,
+	0xa6, 0x4a, 0xa8, 0xe8, 0xaf, 0x38, 0xb0, 0x68,
+	0xb1, 0x44, 0xb2, 0x88, 0xbb, 0x86, 0xbd, 0x40,
+	0xbe, 0x26, 0xc1, 0x05, 0xc2, 0x88, 0xc5, 0xc0,
+	0xda, 0x8e, 0xdb, 0xca, 0xdc, 0xa8, 0xdd, 0x8c,
+	0xde, 0x44, 0xdf, 0x0c, 0xe9, 0x80
+};
+
+static const struct additional_sensor_data sensor_data[] = {
+[SENSOR_OM6802] = {
+	.n3 =
+		{0x61, 0x68, 0x65, 0x0a, 0x60, 0x04},
+	.n4 = n4_om6802,
+	.n4sz = sizeof n4_om6802,
+	.reg80 = 0x3c,
+	.reg8e = 0x33,
+	.nset8 = {0xa8, 0xf0, 0xc6, 0x88, 0xc0, 0x00},
+	.data1 =
+		{0xc2, 0x28, 0x0f, 0x22, 0xcd, 0x27, 0x2c, 0x06,
+		 0xb3, 0xfc},
+	.data2 =
+		{0x80, 0xff, 0xff, 0x80, 0xff, 0xff, 0x80, 0xff,
+		 0xff},
+	.data3 =
+		{0x80, 0xff, 0xff, 0x80, 0xff, 0xff, 0x80, 0xff,
+		 0xff},
+	.data5 =	/* this could be removed later */
+		{0x0c, 0x03, 0xab, 0x13, 0x81, 0x23},
+	.stream =
+		{0x0b, 0x04, 0x0a, 0x78},
+    },
+[SENSOR_OTHER] = {
+	.n3 =
+		{0x61, 0xc2, 0x65, 0x88, 0x60, 0x00},
+	.n4 = n4_other,
+	.n4sz = sizeof n4_other,
+	.reg80 = 0xac,
+	.reg8e = 0xb8,
+	.nset8 = {0xa8, 0xa8, 0xc6, 0xda, 0xc0, 0x00},
+	.data1 =
+		{0xc1, 0x48, 0x04, 0x1b, 0xca, 0x2e, 0x33, 0x3a,
+		 0xe8, 0xfc},
+	.data2 =
+		{0x4e, 0x9c, 0xec, 0x40, 0x80, 0xc0, 0x48, 0x96,
+		 0xd9},
+	.data3 =
+		{0x4e, 0x9c, 0xec, 0x40, 0x80, 0xc0, 0x48, 0x96,
+		 0xd9},
+	.data5 =
+		{0x0c, 0x03, 0xab, 0x29, 0x81, 0x69},
+	.stream =
+		{0x0b, 0x04, 0x0a, 0x00},
+    },
+[SENSOR_TAS5130A] = {
+	.n3 =
+		{0x61, 0xc2, 0x65, 0x0d, 0x60, 0x08},
+	.n4 = n4_tas5130a,
+	.n4sz = sizeof n4_tas5130a,
+	.reg80 = 0x3c,
+	.reg8e = 0xb4,
+	.nset8 = {0xa8, 0xf0, 0xc6, 0xda, 0xc0, 0x00},
+	.data1 =
+		{0xbb, 0x28, 0x10, 0x10, 0xbb, 0x28, 0x1e, 0x27,
+		 0xc8, 0xfc},
+	.data2 =
+		{0x60, 0xa8, 0xe0, 0x60, 0xa8, 0xe0, 0x60, 0xa8,
+		 0xe0},
+	.data3 =
+		{0x60, 0xa8, 0xe0, 0x60, 0xa8, 0xe0, 0x60, 0xa8,
+		 0xe0},
+	.data5 =
+		{0x0c, 0x03, 0xab, 0x10, 0x81, 0x20},
+	.stream =
+		{0x0b, 0x04, 0x0a, 0x40},
+    },
+[SENSOR_LT168G] = {
+	.n3 = {0x61, 0xc2, 0x65, 0x68, 0x60, 0x00},
+	.n4 = n4_lt168g,
+	.n4sz = sizeof n4_lt168g,
+	.reg80 = 0x7c,
+	.reg8e = 0xb3,
+	.nset8 = {0xa8, 0xf0, 0xc6, 0xba, 0xc0, 0x00},
+	.data1 = {0xc0, 0x38, 0x08, 0x10, 0xc0, 0x30, 0x10, 0x40,
+		 0xb0, 0xf4},
+	.data2 = {0x40, 0x80, 0xc0, 0x50, 0xa0, 0xf0, 0x53, 0xa6,
+		 0xff},
+	.data3 = {0x40, 0x80, 0xc0, 0x50, 0xa0, 0xf0, 0x53, 0xa6,
+		 0xff},
+	.data5 = {0x0c, 0x03, 0xab, 0x4b, 0x81, 0x2b},
+	.stream = {0x0b, 0x04, 0x0a, 0x28},
+    },
+};
+
+#define MAX_EFFECTS 7
+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 */
+};
+
+#define GAMMA_MAX (15)
+static const u8 gamma_table[GAMMA_MAX+1][17] = {
+/* gamma table from cam1690.ini */
+	{0x00, 0x00, 0x01, 0x04, 0x08, 0x0e, 0x16, 0x21,	/* 0 */
+	 0x2e, 0x3d, 0x50, 0x65, 0x7d, 0x99, 0xb8, 0xdb,
+	 0xff},
+	{0x00, 0x01, 0x03, 0x08, 0x0e, 0x16, 0x21, 0x2d,	/* 1 */
+	 0x3c, 0x4d, 0x60, 0x75, 0x8d, 0xa6, 0xc2, 0xe1,
+	 0xff},
+	{0x00, 0x01, 0x05, 0x0b, 0x12, 0x1c, 0x28, 0x35,	/* 2 */
+	 0x45, 0x56, 0x69, 0x7e, 0x95, 0xad, 0xc7, 0xe3,
+	 0xff},
+	{0x00, 0x02, 0x07, 0x0f, 0x18, 0x24, 0x30, 0x3f,	/* 3 */
+	 0x4f, 0x61, 0x73, 0x88, 0x9d, 0xb4, 0xcd, 0xe6,
+	 0xff},
+	{0x00, 0x04, 0x0b, 0x15, 0x20, 0x2d, 0x3b, 0x4a,	/* 4 */
+	 0x5b, 0x6c, 0x7f, 0x92, 0xa7, 0xbc, 0xd2, 0xe9,
+	 0xff},
+	{0x00, 0x07, 0x11, 0x15, 0x20, 0x2d, 0x48, 0x58,	/* 5 */
+	 0x68, 0x79, 0x8b, 0x9d, 0xb0, 0xc4, 0xd7, 0xec,
+	 0xff},
+	{0x00, 0x0c, 0x1a, 0x29, 0x38, 0x47, 0x57, 0x67,	/* 6 */
+	 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee,
+	 0xff},
+	{0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70,	/* 7 */
+	 0x80, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0,
+	 0xff},
+	{0x00, 0x15, 0x27, 0x38, 0x49, 0x59, 0x69, 0x79,	/* 8 */
+	 0x88, 0x97, 0xa7, 0xb6, 0xc4, 0xd3, 0xe2, 0xf0,
+	 0xff},
+	{0x00, 0x1c, 0x30, 0x43, 0x54, 0x65, 0x75, 0x84,	/* 9 */
+	 0x93, 0xa1, 0xb0, 0xbd, 0xca, 0xd8, 0xe5, 0xf2,
+	 0xff},
+	{0x00, 0x24, 0x3b, 0x4f, 0x60, 0x70, 0x80, 0x8e,	/* 10 */
+	 0x9c, 0xaa, 0xb7, 0xc4, 0xd0, 0xdc, 0xe8, 0xf3,
+	 0xff},
+	{0x00, 0x2a, 0x3c, 0x5d, 0x6e, 0x7e, 0x8d, 0x9b,	/* 11 */
+	 0xa8, 0xb4, 0xc0, 0xcb, 0xd6, 0xe1, 0xeb, 0xf5,
+	 0xff},
+	{0x00, 0x3f, 0x5a, 0x6e, 0x7f, 0x8e, 0x9c, 0xa8,	/* 12 */
+	 0xb4, 0xbf, 0xc9, 0xd3, 0xdc, 0xe5, 0xee, 0xf6,
+	 0xff},
+	{0x00, 0x54, 0x6f, 0x83, 0x93, 0xa0, 0xad, 0xb7,	/* 13 */
+	 0xc2, 0xcb, 0xd4, 0xdc, 0xe4, 0xeb, 0xf2, 0xf9,
+	 0xff},
+	{0x00, 0x6e, 0x88, 0x9a, 0xa8, 0xb3, 0xbd, 0xc6,	/* 14 */
+	 0xcf, 0xd6, 0xdd, 0xe3, 0xe9, 0xef, 0xf4, 0xfa,
+	 0xff},
+	{0x00, 0x93, 0xa8, 0xb7, 0xc1, 0xca, 0xd2, 0xd8,	/* 15 */
+	 0xde, 0xe3, 0xe8, 0xed, 0xf1, 0xf5, 0xf8, 0xfc,
+	 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},
+};
+
+static u8 sensor_reset[] = {0x61, 0x68, 0x62, 0xff, 0x60, 0x07};
+
+/* read 1 byte */
+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)
+{
+	usb_control_msg(gspca_dev->dev,
+			usb_sndctrlpipe(gspca_dev->dev, 0),
+			0,
+			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			0, index,
+			NULL, 0, 500);
+}
+
+static void reg_w_buf(struct gspca_dev *gspca_dev,
+		  const u8 *buffer, u16 len)
+{
+	if (len <= USB_BUF_SZ) {
+		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_DEVICE,
+				0x01, 0,
+				gspca_dev->usb_buf, len, 500);
+	} else {
+		u8 *tmpbuf;
+
+		tmpbuf = kmemdup(buffer, len, GFP_KERNEL);
+		if (!tmpbuf) {
+			pr_err("Out of memory\n");
+			return;
+		}
+		usb_control_msg(gspca_dev->dev,
+				usb_sndctrlpipe(gspca_dev->dev, 0),
+				0,
+			   USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+				0x01, 0,
+				tmpbuf, len, 500);
+		kfree(tmpbuf);
+	}
+}
+
+/* write values to consecutive registers */
+static void reg_w_ixbuf(struct gspca_dev *gspca_dev,
+			u8 reg,
+			const u8 *buffer, u16 len)
+{
+	int i;
+	u8 *p, *tmpbuf;
+
+	if (len * 2 <= USB_BUF_SZ) {
+		p = tmpbuf = gspca_dev->usb_buf;
+	} else {
+		p = tmpbuf = kmalloc(len * 2, GFP_KERNEL);
+		if (!tmpbuf) {
+			pr_err("Out of memory\n");
+			return;
+		}
+	}
+	i = len;
+	while (--i >= 0) {
+		*p++ = reg++;
+		*p++ = *buffer++;
+	}
+	usb_control_msg(gspca_dev->dev,
+			usb_sndctrlpipe(gspca_dev->dev, 0),
+			0,
+			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			0x01, 0,
+			tmpbuf, len * 2, 500);
+	if (len * 2 > USB_BUF_SZ)
+		kfree(tmpbuf);
+}
+
+static void om6802_sensor_init(struct gspca_dev *gspca_dev)
+{
+	int i;
+	const u8 *p;
+	u8 byte;
+	u8 val[6] = {0x62, 0, 0x64, 0, 0x60, 0x05};
+	static const u8 sensor_init[] = {
+		0xdf, 0x6d,
+		0xdd, 0x18,
+		0x5a, 0xe0,
+		0x5c, 0x07,
+		0x5d, 0xb0,
+		0x5e, 0x1e,
+		0x60, 0x71,
+		0xef, 0x00,
+		0xe9, 0x00,
+		0xea, 0x00,
+		0x90, 0x24,
+		0x91, 0xb2,
+		0x82, 0x32,
+		0xfd, 0x41,
+		0x00			/* table end */
+	};
+
+	reg_w_buf(gspca_dev, sensor_reset, sizeof sensor_reset);
+	msleep(100);
+	i = 4;
+	while (--i > 0) {
+		byte = reg_r(gspca_dev, 0x0060);
+		if (!(byte & 0x01))
+			break;
+		msleep(100);
+	}
+	byte = reg_r(gspca_dev, 0x0063);
+	if (byte != 0x17) {
+		pr_err("Bad sensor reset %02x\n", byte);
+		/* continue? */
+	}
+
+	p = sensor_init;
+	while (*p != 0) {
+		val[1] = *p++;
+		val[3] = *p++;
+		if (*p == 0)
+			reg_w(gspca_dev, 0x3c80);
+		reg_w_buf(gspca_dev, val, sizeof val);
+		i = 4;
+		while (--i >= 0) {
+			msleep(15);
+			byte = reg_r(gspca_dev, 0x60);
+			if (!(byte & 0x01))
+				break;
+		}
+	}
+	msleep(15);
+	reg_w(gspca_dev, 0x3c80);
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+		     const struct usb_device_id *id)
+{
+	struct cam *cam  = &gspca_dev->cam;
+
+	cam->cam_mode = vga_mode_t16;
+	cam->nmodes = ARRAY_SIZE(vga_mode_t16);
+
+	return 0;
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev, s32 brightness)
+{
+	u8 set6[4] = { 0x8f, 0x24, 0xc3, 0x00 };
+
+	if (brightness < 7) {
+		set6[1] = 0x26;
+		set6[3] = 0x70 - brightness * 0x10;
+	} else {
+		set6[3] = 0x00 + ((brightness - 7) * 0x10);
+	}
+
+	reg_w_buf(gspca_dev, set6, sizeof set6);
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev, s32 contrast)
+{
+	u16 reg_to_write;
+
+	if (contrast < 7)
+		reg_to_write = 0x8ea9 - contrast * 0x200;
+	else
+		reg_to_write = 0x00a9 + (contrast - 7) * 0x200;
+
+	reg_w(gspca_dev, reg_to_write);
+}
+
+static void setcolors(struct gspca_dev *gspca_dev, s32 val)
+{
+	u16 reg_to_write;
+
+	reg_to_write = 0x80bb + val * 0x100;	/* was 0xc0 */
+	reg_w(gspca_dev, reg_to_write);
+}
+
+static void setgamma(struct gspca_dev *gspca_dev, s32 val)
+{
+	PDEBUG(D_CONF, "Gamma: %d", val);
+	reg_w_ixbuf(gspca_dev, 0x90,
+		gamma_table[val], sizeof gamma_table[0]);
+}
+
+static void setawb_n_RGB(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u8 all_gain_reg[8] = {
+		0x87, 0x00, 0x88, 0x00, 0x89, 0x00, 0x80, 0x00 };
+	s32 red_gain, blue_gain, green_gain;
+
+	green_gain = sd->gain->val;
+
+	red_gain = green_gain + sd->red_balance->val;
+	if (red_gain > 0x40)
+		red_gain = 0x40;
+	else if (red_gain < 0x10)
+		red_gain = 0x10;
+
+	blue_gain = green_gain + sd->blue_balance->val;
+	if (blue_gain > 0x40)
+		blue_gain = 0x40;
+	else if (blue_gain < 0x10)
+		blue_gain = 0x10;
+
+	all_gain_reg[1] = red_gain;
+	all_gain_reg[3] = blue_gain;
+	all_gain_reg[5] = green_gain;
+	all_gain_reg[7] = sensor_data[sd->sensor].reg80;
+	if (!sd->awb->val)
+		all_gain_reg[7] &= ~0x04; /* AWB off */
+
+	reg_w_buf(gspca_dev, all_gain_reg, sizeof all_gain_reg);
+}
+
+static void setsharpness(struct gspca_dev *gspca_dev, s32 val)
+{
+	u16 reg_to_write;
+
+	reg_to_write = 0x0aa6 + 0x1000 * val;
+
+	reg_w(gspca_dev, reg_to_write);
+}
+
+static void setfreq(struct gspca_dev *gspca_dev, s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u8 reg66;
+	u8 freq[4] = { 0x66, 0x00, 0xa8, 0xe8 };
+
+	switch (sd->sensor) {
+	case SENSOR_LT168G:
+		if (val != 0)
+			freq[3] = 0xa8;
+		reg66 = 0x41;
+		break;
+	case SENSOR_OM6802:
+		reg66 = 0xca;
+		break;
+	default:
+		reg66 = 0x40;
+		break;
+	}
+	switch (val) {
+	case 0:				/* no flicker */
+		freq[3] = 0xf0;
+		break;
+	case 2:				/* 60Hz */
+		reg66 &= ~0x40;
+		break;
+	}
+	freq[1] = reg66;
+
+	reg_w_buf(gspca_dev, freq, sizeof freq);
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(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.*/
+	struct sd *sd = (struct sd *) gspca_dev;
+	const struct additional_sensor_data *sensor;
+	int i;
+	u16 sensor_id;
+	u8 test_byte = 0;
+
+	static const u8 read_indexs[] =
+		{ 0x0a, 0x0b, 0x66, 0x80, 0x81, 0x8e, 0x8f, 0xa5,
+		  0xa6, 0xa8, 0xbb, 0xbc, 0xc6, 0x00 };
+	static const u8 n1[] =
+			{0x08, 0x03, 0x09, 0x03, 0x12, 0x04};
+	static const u8 n2[] =
+			{0x08, 0x00};
+
+	sensor_id = (reg_r(gspca_dev, 0x06) << 8)
+			| reg_r(gspca_dev, 0x07);
+	switch (sensor_id & 0xff0f) {
+	case 0x0801:
+		PDEBUG(D_PROBE, "sensor tas5130a");
+		sd->sensor = SENSOR_TAS5130A;
+		break;
+	case 0x0802:
+		PDEBUG(D_PROBE, "sensor lt168g");
+		sd->sensor = SENSOR_LT168G;
+		break;
+	case 0x0803:
+		PDEBUG(D_PROBE, "sensor 'other'");
+		sd->sensor = SENSOR_OTHER;
+		break;
+	case 0x0807:
+		PDEBUG(D_PROBE, "sensor om6802");
+		sd->sensor = SENSOR_OM6802;
+		break;
+	default:
+		pr_err("unknown sensor %04x\n", sensor_id);
+		return -EINVAL;
+	}
+
+	if (sd->sensor == SENSOR_OM6802) {
+		reg_w_buf(gspca_dev, n1, sizeof n1);
+		i = 5;
+		while (--i >= 0) {
+			reg_w_buf(gspca_dev, sensor_reset, sizeof sensor_reset);
+			test_byte = reg_r(gspca_dev, 0x0063);
+			msleep(100);
+			if (test_byte == 0x17)
+				break;		/* OK */
+		}
+		if (i < 0) {
+			pr_err("Bad sensor reset %02x\n", test_byte);
+			return -EIO;
+		}
+		reg_w_buf(gspca_dev, n2, sizeof n2);
+	}
+
+	i = 0;
+	while (read_indexs[i] != 0x00) {
+		test_byte = reg_r(gspca_dev, read_indexs[i]);
+		PDEBUG(D_STREAM, "Reg 0x%02x = 0x%02x", read_indexs[i],
+		       test_byte);
+		i++;
+	}
+
+	sensor = &sensor_data[sd->sensor];
+	reg_w_buf(gspca_dev, sensor->n3, sizeof sensor->n3);
+	reg_w_buf(gspca_dev, sensor->n4, sensor->n4sz);
+
+	if (sd->sensor == SENSOR_LT168G) {
+		test_byte = reg_r(gspca_dev, 0x80);
+		PDEBUG(D_STREAM, "Reg 0x%02x = 0x%02x", 0x80,
+		       test_byte);
+		reg_w(gspca_dev, 0x6c80);
+	}
+
+	reg_w_ixbuf(gspca_dev, 0xd0, sensor->data1, sizeof sensor->data1);
+	reg_w_ixbuf(gspca_dev, 0xc7, sensor->data2, sizeof sensor->data2);
+	reg_w_ixbuf(gspca_dev, 0xe0, sensor->data3, sizeof sensor->data3);
+
+	reg_w(gspca_dev, (sensor->reg80 << 8) + 0x80);
+	reg_w(gspca_dev, (sensor->reg80 << 8) + 0x80);
+	reg_w(gspca_dev, (sensor->reg8e << 8) + 0x8e);
+	reg_w(gspca_dev, (0x20 << 8) + 0x87);
+	reg_w(gspca_dev, (0x20 << 8) + 0x88);
+	reg_w(gspca_dev, (0x20 << 8) + 0x89);
+
+	reg_w_buf(gspca_dev, sensor->data5, sizeof sensor->data5);
+	reg_w_buf(gspca_dev, sensor->nset8, sizeof sensor->nset8);
+	reg_w_buf(gspca_dev, sensor->stream, sizeof sensor->stream);
+
+	if (sd->sensor == SENSOR_LT168G) {
+		test_byte = reg_r(gspca_dev, 0x80);
+		PDEBUG(D_STREAM, "Reg 0x%02x = 0x%02x", 0x80,
+		       test_byte);
+		reg_w(gspca_dev, 0x6c80);
+	}
+
+	reg_w_ixbuf(gspca_dev, 0xd0, sensor->data1, sizeof sensor->data1);
+	reg_w_ixbuf(gspca_dev, 0xc7, sensor->data2, sizeof sensor->data2);
+	reg_w_ixbuf(gspca_dev, 0xe0, sensor->data3, sizeof sensor->data3);
+
+	return 0;
+}
+
+static void setmirror(struct gspca_dev *gspca_dev, s32 val)
+{
+	u8 hflipcmd[8] =
+		{0x62, 0x07, 0x63, 0x03, 0x64, 0x00, 0x60, 0x09};
+
+	if (val)
+		hflipcmd[3] = 0x01;
+
+	reg_w_buf(gspca_dev, hflipcmd, sizeof hflipcmd);
+}
+
+static void seteffect(struct gspca_dev *gspca_dev, s32 val)
+{
+	int idx = 0;
+
+	switch (val) {
+	case V4L2_COLORFX_NONE:
+		break;
+	case V4L2_COLORFX_BW:
+		idx = 2;
+		break;
+	case V4L2_COLORFX_SEPIA:
+		idx = 3;
+		break;
+	case V4L2_COLORFX_SKETCH:
+		idx = 4;
+		break;
+	case V4L2_COLORFX_NEGATIVE:
+		idx = 6;
+		break;
+	default:
+		break;
+	}
+
+	reg_w_buf(gspca_dev, effects_table[idx],
+				sizeof effects_table[0]);
+
+	if (val == V4L2_COLORFX_SKETCH)
+		reg_w(gspca_dev, 0x4aa6);
+	else
+		reg_w(gspca_dev, 0xfaa6);
+}
+
+/* Is this really needed?
+ * i added some module parameters for test with some users */
+static void poll_sensor(struct gspca_dev *gspca_dev)
+{
+	static const u8 poll1[] =
+		{0x67, 0x05, 0x68, 0x81, 0x69, 0x80, 0x6a, 0x82,
+		 0x6b, 0x68, 0x6c, 0x69, 0x72, 0xd9, 0x73, 0x34,
+		 0x74, 0x32, 0x75, 0x92, 0x76, 0x00, 0x09, 0x01,
+		 0x60, 0x14};
+	static const u8 poll2[] =
+		{0x67, 0x02, 0x68, 0x71, 0x69, 0x72, 0x72, 0xa9,
+		 0x73, 0x02, 0x73, 0x02, 0x60, 0x14};
+	static const u8 noise03[] =	/* (some differences / ms-drv) */
+		{0xa6, 0x0a, 0xea, 0xcf, 0xbe, 0x26, 0xb1, 0x5f,
+		 0xa1, 0xb1, 0xda, 0x6b, 0xdb, 0x98, 0xdf, 0x0c,
+		 0xc2, 0x80, 0xc3, 0x10};
+
+	PDEBUG(D_STREAM, "[Sensor requires polling]");
+	reg_w_buf(gspca_dev, poll1, sizeof poll1);
+	reg_w_buf(gspca_dev, poll2, sizeof poll2);
+	reg_w_buf(gspca_dev, noise03, sizeof noise03);
+}
+
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	const struct additional_sensor_data *sensor;
+	int i, mode;
+	u8 t2[] = { 0x07, 0x00, 0x0d, 0x60, 0x0e, 0x80 };
+	static const u8 t3[] =
+		{ 0x07, 0x00, 0x88, 0x02, 0x06, 0x00, 0xe7, 0x01 };
+
+	mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
+	switch (mode) {
+	case 0:		/* 640x480 (0x00) */
+		break;
+	case 1:		/* 352x288 */
+		t2[1] = 0x40;
+		break;
+	case 2:		/* 320x240 */
+		t2[1] = 0x10;
+		break;
+	case 3:		/* 176x144 */
+		t2[1] = 0x50;
+		break;
+	default:
+/*	case 4:		 * 160x120 */
+		t2[1] = 0x20;
+		break;
+	}
+
+	switch (sd->sensor) {
+	case SENSOR_OM6802:
+		om6802_sensor_init(gspca_dev);
+		break;
+	case SENSOR_TAS5130A:
+		i = 0;
+		for (;;) {
+			reg_w_buf(gspca_dev, tas5130a_sensor_init[i],
+					 sizeof tas5130a_sensor_init[0]);
+			if (i >= ARRAY_SIZE(tas5130a_sensor_init) - 1)
+				break;
+			i++;
+		}
+		reg_w(gspca_dev, 0x3c80);
+		/* just in case and to keep sync with logs (for mine) */
+		reg_w_buf(gspca_dev, tas5130a_sensor_init[i],
+				 sizeof tas5130a_sensor_init[0]);
+		reg_w(gspca_dev, 0x3c80);
+		break;
+	}
+	sensor = &sensor_data[sd->sensor];
+	setfreq(gspca_dev, v4l2_ctrl_g_ctrl(sd->freq));
+	reg_r(gspca_dev, 0x0012);
+	reg_w_buf(gspca_dev, t2, sizeof t2);
+	reg_w_ixbuf(gspca_dev, 0xb3, t3, sizeof t3);
+	reg_w(gspca_dev, 0x0013);
+	msleep(15);
+	reg_w_buf(gspca_dev, sensor->stream, sizeof sensor->stream);
+	reg_w_buf(gspca_dev, sensor->stream, sizeof sensor->stream);
+
+	if (sd->sensor == SENSOR_OM6802)
+		poll_sensor(gspca_dev);
+
+	return 0;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	reg_w_buf(gspca_dev, sensor_data[sd->sensor].stream,
+			sizeof sensor_data[sd->sensor].stream);
+	reg_w_buf(gspca_dev, sensor_data[sd->sensor].stream,
+			sizeof sensor_data[sd->sensor].stream);
+	if (sd->sensor == SENSOR_OM6802) {
+		msleep(20);
+		reg_w(gspca_dev, 0x0309);
+	}
+#if IS_ENABLED(CONFIG_INPUT)
+	/* If the last button state is pressed, release it now! */
+	if (sd->button_pressed) {
+		input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
+		input_sync(gspca_dev->input_dev);
+		sd->button_pressed = 0;
+	}
+#endif
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+			u8 *data,			/* isoc packet */
+			int len)			/* iso packet length */
+{
+	struct sd *sd __maybe_unused = (struct sd *) gspca_dev;
+	int pkt_type;
+
+	if (data[0] == 0x5a) {
+#if IS_ENABLED(CONFIG_INPUT)
+		if (len > 20) {
+			u8 state = (data[20] & 0x80) ? 1 : 0;
+			if (sd->button_pressed != state) {
+				input_report_key(gspca_dev->input_dev,
+						 KEY_CAMERA, state);
+				input_sync(gspca_dev->input_dev);
+				sd->button_pressed = state;
+			}
+		}
+#endif
+		/* Control Packet, after this came the header again,
+		 * but extra bytes came in the packet before this,
+		 * sometimes an EOF arrives, sometimes not... */
+		return;
+	}
+	data += 2;
+	len -= 2;
+	if (data[0] == 0xff && data[1] == 0xd8)
+		pkt_type = FIRST_PACKET;
+	else if (data[len - 2] == 0xff && data[len - 1] == 0xd9)
+		pkt_type = LAST_PACKET;
+	else
+		pkt_type = INTER_PACKET;
+	gspca_frame_add(gspca_dev, pkt_type, data, len);
+}
+
+static int sd_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct gspca_dev *gspca_dev =
+		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+	struct sd *sd = (struct sd *)gspca_dev;
+	s32 red_gain, blue_gain, green_gain;
+
+	gspca_dev->usb_err = 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_AUTO_WHITE_BALANCE:
+		red_gain = reg_r(gspca_dev, 0x0087);
+		if (red_gain > 0x40)
+			red_gain = 0x40;
+		else if (red_gain < 0x10)
+			red_gain = 0x10;
+
+		blue_gain = reg_r(gspca_dev, 0x0088);
+		if (blue_gain > 0x40)
+			blue_gain = 0x40;
+		else if (blue_gain < 0x10)
+			blue_gain = 0x10;
+
+		green_gain = reg_r(gspca_dev, 0x0089);
+		if (green_gain > 0x40)
+			green_gain = 0x40;
+		else if (green_gain < 0x10)
+			green_gain = 0x10;
+
+		sd->gain->val = green_gain;
+		sd->red_balance->val = red_gain - green_gain;
+		sd->blue_balance->val = blue_gain - green_gain;
+		break;
+	}
+	return 0;
+}
+
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct gspca_dev *gspca_dev =
+		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+
+	gspca_dev->usb_err = 0;
+
+	if (!gspca_dev->streaming)
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_BRIGHTNESS:
+		setbrightness(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_CONTRAST:
+		setcontrast(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_SATURATION:
+		setcolors(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_GAMMA:
+		setgamma(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_HFLIP:
+		setmirror(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_SHARPNESS:
+		setsharpness(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_POWER_LINE_FREQUENCY:
+		setfreq(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_BACKLIGHT_COMPENSATION:
+		reg_w(gspca_dev, ctrl->val ? 0xf48e : 0xb48e);
+		break;
+	case V4L2_CID_AUTO_WHITE_BALANCE:
+		setawb_n_RGB(gspca_dev);
+		break;
+	case V4L2_CID_COLORFX:
+		seteffect(gspca_dev, ctrl->val);
+		break;
+	}
+	return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+	.g_volatile_ctrl = sd_g_volatile_ctrl,
+	.s_ctrl = sd_s_ctrl,
+};
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *)gspca_dev;
+	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+	gspca_dev->vdev.ctrl_handler = hdl;
+	v4l2_ctrl_handler_init(hdl, 12);
+	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_BRIGHTNESS, 0, 14, 1, 8);
+	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_CONTRAST, 0, 0x0d, 1, 7);
+	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_SATURATION, 0, 0xf, 1, 5);
+	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_GAMMA, 0, GAMMA_MAX, 1, 10);
+	/* Activate lowlight, some apps dont bring up the
+	   backlight_compensation control) */
+	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_BACKLIGHT_COMPENSATION, 0, 1, 1, 1);
+	if (sd->sensor == SENSOR_TAS5130A)
+		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+				V4L2_CID_HFLIP, 0, 1, 1, 0);
+	sd->awb = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1);
+	sd->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_GAIN, 0x10, 0x40, 1, 0x20);
+	sd->blue_balance = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_BLUE_BALANCE, -0x30, 0x30, 1, 0);
+	sd->red_balance = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_RED_BALANCE, -0x30, 0x30, 1, 0);
+	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_SHARPNESS, 0, 15, 1, 6);
+	v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
+			V4L2_CID_COLORFX, V4L2_COLORFX_SKETCH,
+			~((1 << V4L2_COLORFX_NONE) |
+			  (1 << V4L2_COLORFX_BW) |
+			  (1 << V4L2_COLORFX_SEPIA) |
+			  (1 << V4L2_COLORFX_SKETCH) |
+			  (1 << V4L2_COLORFX_NEGATIVE)),
+			V4L2_COLORFX_NONE);
+	sd->freq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
+			V4L2_CID_POWER_LINE_FREQUENCY,
+			V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 1,
+			V4L2_CID_POWER_LINE_FREQUENCY_50HZ);
+
+	if (hdl->error) {
+		pr_err("Could not initialize controls\n");
+		return hdl->error;
+	}
+
+	v4l2_ctrl_auto_cluster(4, &sd->awb, 0, true);
+
+	return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+	.name = MODULE_NAME,
+	.config = sd_config,
+	.init = sd_init,
+	.init_controls = sd_init_controls,
+	.start = sd_start,
+	.stopN = sd_stopN,
+	.pkt_scan = sd_pkt_scan,
+#if IS_ENABLED(CONFIG_INPUT)
+	.other_input = 1,
+#endif
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+	{USB_DEVICE(0x17a1, 0x0128)},
+	{}
+};
+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,
+#ifdef CONFIG_PM
+	.suspend = gspca_suspend,
+	.resume = gspca_resume,
+	.reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/usb/gspca/topro.c b/drivers/media/usb/gspca/topro.c
new file mode 100644
index 0000000..c028a5c
--- /dev/null
+++ b/drivers/media/usb/gspca/topro.c
@@ -0,0 +1,4984 @@
+/*
+ * Topro TP6800/6810 webcam driver.
+ *
+ * Copyright (C) 2011 Jean-François Moine (http://moinejf.free.fr)
+ * Copyright (C) 2009 Anders Blomdell (anders.blomdell@control.lth.se)
+ * Copyright (C) 2008 Thomas Champagne (lafeuil@gmail.com)
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "gspca.h"
+
+MODULE_DESCRIPTION("Topro TP6800/6810 gspca webcam driver");
+MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>, "
+		"Anders Blomdell <anders.blomdell@control.lth.se>");
+MODULE_LICENSE("GPL");
+
+static int force_sensor = -1;
+
+/* JPEG header */
+static const u8 jpeg_head[] = {
+	0xff, 0xd8,			/* jpeg */
+
+/* quantization table quality 50% */
+	0xff, 0xdb, 0x00, 0x84,		/* DQT */
+0,
+#define JPEG_QT0_OFFSET 7
+	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,
+1,
+#define JPEG_QT1_OFFSET 72
+	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,
+
+	/* Define Huffman table (thanks to Thomas Kaiser) */
+	0xff, 0xc4, 0x01, 0x5e,
+	0x00, 0x00, 0x02, 0x03,
+	0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02,
+	0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10,
+	0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04,
+	0x07, 0x05, 0x04, 0x06, 0x01, 0x00, 0x00, 0x57,
+	0x01, 0x02, 0x03, 0x00, 0x11, 0x04, 0x12, 0x21,
+	0x31, 0x13, 0x41, 0x51, 0x61, 0x05, 0x22, 0x32,
+	0x14, 0x71, 0x81, 0x91, 0x15, 0x23, 0x42, 0x52,
+	0x62, 0xa1, 0xb1, 0x06, 0x33, 0x72, 0xc1, 0xd1,
+	0x24, 0x43, 0x53, 0x82, 0x16, 0x34, 0x92, 0xa2,
+	0xe1, 0xf1, 0xf0, 0x07, 0x08, 0x17, 0x18, 0x25,
+	0x26, 0x27, 0x28, 0x35, 0x36, 0x37, 0x38, 0x44,
+	0x45, 0x46, 0x47, 0x48, 0x54, 0x55, 0x56, 0x57,
+	0x58, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x73,
+	0x74, 0x75, 0x76, 0x77, 0x78, 0x83, 0x84, 0x85,
+	0x86, 0x87, 0x88, 0x93, 0x94, 0x95, 0x96, 0x97,
+	0x98, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xb2,
+	0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xc2, 0xc3,
+	0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xd2, 0xd3, 0xd4,
+	0xd5, 0xd6, 0xd7, 0xd8, 0xe2, 0xe3, 0xe4, 0xe5,
+	0xe6, 0xe7, 0xe8, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6,
+	0xf7, 0xf8, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+	0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04,
+	0x05, 0x06, 0x07, 0x08, 0x09, 0x11, 0x00, 0x02,
+	0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05,
+	0x04, 0x06, 0x01, 0x00, 0x00, 0x57, 0x00, 0x01,
+	0x11, 0x02, 0x21, 0x03, 0x12, 0x31, 0x41, 0x13,
+	0x22, 0x51, 0x61, 0x04, 0x32, 0x71, 0x05, 0x14,
+	0x23, 0x42, 0x33, 0x52, 0x81, 0x91, 0xa1, 0xb1,
+	0xf0, 0x06, 0x15, 0xc1, 0xd1, 0xe1, 0x24, 0x43,
+	0x62, 0xf1, 0x16, 0x25, 0x34, 0x53, 0x72, 0x82,
+	0x92, 0x07, 0x08, 0x17, 0x18, 0x26, 0x27, 0x28,
+	0x35, 0x36, 0x37, 0x38, 0x44, 0x45, 0x46, 0x47,
+	0x48, 0x54, 0x55, 0x56, 0x57, 0x58, 0x63, 0x64,
+	0x65, 0x66, 0x67, 0x68, 0x73, 0x74, 0x75, 0x76,
+	0x77, 0x78, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
+	0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0xa2, 0xa3,
+	0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xb2, 0xb3, 0xb4,
+	0xb5, 0xb6, 0xb7, 0xb8, 0xc2, 0xc3, 0xc4, 0xc5,
+	0xc6, 0xc7, 0xc8, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6,
+	0xd7, 0xd8, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
+	0xe8, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
+	0xff, 0xc0, 0x00, 0x11,		/* SOF0 (start of frame 0 */
+	0x08,				/* data precision */
+#define JPEG_HEIGHT_OFFSET 493
+	0x01, 0xe0,			/* height */
+	0x02, 0x80,			/* width */
+	0x03,				/* component number */
+		0x01,
+			0x21,		/* samples Y = jpeg 422 */
+			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
+#define JPEG_HDR_SZ 521
+};
+
+struct sd {
+	struct gspca_dev gspca_dev;	/* !! must be the first item */
+	struct v4l2_ctrl *jpegqual;
+	struct v4l2_ctrl *sharpness;
+	struct v4l2_ctrl *gamma;
+	struct v4l2_ctrl *blue;
+	struct v4l2_ctrl *red;
+
+	u8 framerate;
+	u8 quality;		/* webcam current JPEG quality (0..16) */
+	s8 ag_cnt;		/* autogain / start counter for tp6810 */
+#define AG_CNT_START 13		/* check gain every N frames */
+
+	u8 bridge;
+	u8 sensor;
+
+	u8 jpeg_hdr[JPEG_HDR_SZ];
+};
+
+enum bridges {
+	BRIDGE_TP6800,
+	BRIDGE_TP6810,
+};
+
+enum sensors {
+	SENSOR_CX0342,
+	SENSOR_SOI763A,		/* ~= ov7630 / ov7648 */
+	NSENSORS
+};
+
+static const struct v4l2_pix_format vga_mode[] = {
+	{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+		.bytesperline = 320,
+		.sizeimage = 320 * 240 * 4 / 8 + 590,
+		.colorspace = V4L2_COLORSPACE_JPEG},
+	{640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+		.bytesperline = 640,
+		.sizeimage = 640 * 480 * 3 / 8 + 590,
+		.colorspace = V4L2_COLORSPACE_JPEG}
+};
+
+/*
+ * JPEG quality
+ * index: webcam compression
+ * value: JPEG quality in %
+ */
+static const u8 jpeg_q[17] = {
+	88, 77, 67, 57, 55, 55, 45, 45, 36, 36, 30, 30, 26, 26, 22, 22, 94
+};
+
+#define BULK_OUT_SIZE		0x20
+#if BULK_OUT_SIZE > USB_BUF_SZ
+#error "USB buffer too small"
+#endif
+
+static const u8 rates[] = {30, 20, 15, 10, 7, 5};
+static const struct framerates framerates[] = {
+	{
+		.rates = rates,
+		.nrates = ARRAY_SIZE(rates)
+	},
+	{
+		.rates = rates,
+		.nrates = ARRAY_SIZE(rates)
+	}
+};
+static const u8 rates_6810[] = {30, 15, 10, 7, 5};
+static const struct framerates framerates_6810[] = {
+	{
+		.rates = rates_6810,
+		.nrates = ARRAY_SIZE(rates_6810)
+	},
+	{
+		.rates = rates_6810,
+		.nrates = ARRAY_SIZE(rates_6810)
+	}
+};
+
+/*
+ * webcam quality in %
+ * the last value is the ultra fine quality
+ */
+
+/* TP6800 register offsets */
+#define TP6800_R10_SIF_TYPE		0x10
+#define TP6800_R11_SIF_CONTROL		0x11
+#define TP6800_R12_SIF_ADDR_S		0x12
+#define TP6800_R13_SIF_TX_DATA		0x13
+#define TP6800_R14_SIF_RX_DATA		0x14
+#define TP6800_R15_GPIO_PU		0x15
+#define TP6800_R16_GPIO_PD		0x16
+#define TP6800_R17_GPIO_IO		0x17
+#define TP6800_R18_GPIO_DATA		0x18
+#define TP6800_R19_SIF_ADDR_S2		0x19
+#define TP6800_R1A_SIF_TX_DATA2		0x1a
+#define TP6800_R1B_SIF_RX_DATA2		0x1b
+#define TP6800_R21_ENDP_1_CTL		0x21
+#define TP6800_R2F_TIMING_CFG		0x2f
+#define TP6800_R30_SENSOR_CFG		0x30
+#define TP6800_R31_PIXEL_START		0x31
+#define TP6800_R32_PIXEL_END_L		0x32
+#define TP6800_R33_PIXEL_END_H		0x33
+#define TP6800_R34_LINE_START		0x34
+#define TP6800_R35_LINE_END_L		0x35
+#define TP6800_R36_LINE_END_H		0x36
+#define TP6800_R37_FRONT_DARK_ST	0x37
+#define TP6800_R38_FRONT_DARK_END	0x38
+#define TP6800_R39_REAR_DARK_ST_L	0x39
+#define TP6800_R3A_REAR_DARK_ST_H	0x3a
+#define TP6800_R3B_REAR_DARK_END_L	0x3b
+#define TP6800_R3C_REAR_DARK_END_H	0x3c
+#define TP6800_R3D_HORIZ_DARK_LINE_L	0x3d
+#define TP6800_R3E_HORIZ_DARK_LINE_H	0x3e
+#define TP6800_R3F_FRAME_RATE		0x3f
+#define TP6800_R50			0x50
+#define TP6800_R51			0x51
+#define TP6800_R52			0x52
+#define TP6800_R53			0x53
+#define TP6800_R54_DARK_CFG		0x54
+#define TP6800_R55_GAMMA_R		0x55
+#define TP6800_R56_GAMMA_G		0x56
+#define TP6800_R57_GAMMA_B		0x57
+#define TP6800_R5C_EDGE_THRLD		0x5c
+#define TP6800_R5D_DEMOSAIC_CFG		0x5d
+#define TP6800_R78_FORMAT		0x78
+#define TP6800_R79_QUALITY		0x79
+#define TP6800_R7A_BLK_THRLD		0x7a
+
+/* CX0342 register offsets */
+
+#define CX0342_SENSOR_ID		0x00
+#define CX0342_VERSION_NO		0x01
+#define CX0342_ORG_X_L			0x02
+#define CX0342_ORG_X_H			0x03
+#define CX0342_ORG_Y_L			0x04
+#define CX0342_ORG_Y_H			0x05
+#define CX0342_STOP_X_L			0x06
+#define CX0342_STOP_X_H			0x07
+#define CX0342_STOP_Y_L			0x08
+#define CX0342_STOP_Y_H			0x09
+#define CX0342_FRAME_WIDTH_L		0x0a
+#define CX0342_FRAME_WIDTH_H		0x0b
+#define CX0342_FRAME_HEIGH_L		0x0c
+#define CX0342_FRAME_HEIGH_H		0x0d
+#define CX0342_EXPO_LINE_L		0x10
+#define CX0342_EXPO_LINE_H		0x11
+#define CX0342_EXPO_CLK_L		0x12
+#define CX0342_EXPO_CLK_H		0x13
+#define CX0342_RAW_GRGAIN_L		0x14
+#define CX0342_RAW_GRGAIN_H		0x15
+#define CX0342_RAW_GBGAIN_L		0x16
+#define CX0342_RAW_GBGAIN_H		0x17
+#define CX0342_RAW_RGAIN_L		0x18
+#define CX0342_RAW_RGAIN_H		0x19
+#define CX0342_RAW_BGAIN_L		0x1a
+#define CX0342_RAW_BGAIN_H		0x1b
+#define CX0342_GLOBAL_GAIN		0x1c
+#define CX0342_SYS_CTRL_0		0x20
+#define CX0342_SYS_CTRL_1		0x21
+#define CX0342_SYS_CTRL_2		0x22
+#define CX0342_BYPASS_MODE		0x23
+#define CX0342_SYS_CTRL_3		0x24
+#define CX0342_TIMING_EN		0x25
+#define CX0342_OUTPUT_CTRL		0x26
+#define CX0342_AUTO_ADC_CALIB		0x27
+#define CX0342_SYS_CTRL_4		0x28
+#define CX0342_ADCGN			0x30
+#define CX0342_SLPCR			0x31
+#define CX0342_SLPFN_LO			0x32
+#define CX0342_ADC_CTL			0x33
+#define CX0342_LVRST_BLBIAS		0x34
+#define CX0342_VTHSEL			0x35
+#define CX0342_RAMP_RIV			0x36
+#define CX0342_LDOSEL			0x37
+#define CX0342_CLOCK_GEN		0x40
+#define CX0342_SOFT_RESET		0x41
+#define CX0342_PLL			0x42
+#define CX0342_DR_ENH_PULSE_OFFSET_L	0x43
+#define CX0342_DR_ENH_PULSE_OFFSET_H	0x44
+#define CX0342_DR_ENH_PULSE_POS_L	0x45
+#define CX0342_DR_ENH_PULSE_POS_H	0x46
+#define CX0342_DR_ENH_PULSE_WIDTH	0x47
+#define CX0342_AS_CURRENT_CNT_L		0x48
+#define CX0342_AS_CURRENT_CNT_H		0x49
+#define CX0342_AS_PREVIOUS_CNT_L	0x4a
+#define CX0342_AS_PREVIOUS_CNT_H	0x4b
+#define CX0342_SPV_VALUE_L		0x4c
+#define CX0342_SPV_VALUE_H		0x4d
+#define CX0342_GPXLTHD_L		0x50
+#define CX0342_GPXLTHD_H		0x51
+#define CX0342_RBPXLTHD_L		0x52
+#define CX0342_RBPXLTHD_H		0x53
+#define CX0342_PLANETHD_L		0x54
+#define CX0342_PLANETHD_H		0x55
+#define CX0342_ROWDARK_TH		0x56
+#define CX0342_ROWDARK_TOL		0x57
+#define CX0342_RB_GAP_L			0x58
+#define CX0342_RB_GAP_H			0x59
+#define CX0342_G_GAP_L			0x5a
+#define CX0342_G_GAP_H			0x5b
+#define CX0342_AUTO_ROW_DARK		0x60
+#define CX0342_MANUAL_DARK_VALUE	0x61
+#define CX0342_GB_DARK_OFFSET		0x62
+#define CX0342_GR_DARK_OFFSET		0x63
+#define CX0342_RED_DARK_OFFSET		0x64
+#define CX0342_BLUE_DARK_OFFSET		0x65
+#define CX0342_DATA_SCALING_MULTI	0x66
+#define CX0342_AUTOD_Q_FRAME		0x67
+#define CX0342_AUTOD_ALLOW_VARI		0x68
+#define CX0342_AUTO_DARK_VALUE_L	0x69
+#define CX0342_AUTO_DARK_VALUE_H	0x6a
+#define CX0342_IO_CTRL_0		0x70
+#define CX0342_IO_CTRL_1		0x71
+#define CX0342_IO_CTRL_2		0x72
+#define CX0342_IDLE_CTRL		0x73
+#define CX0342_TEST_MODE		0x74
+#define CX0342_FRAME_FIX_DATA_TEST	0x75
+#define CX0342_FRAME_CNT_TEST		0x76
+#define CX0342_RST_OVERFLOW_L		0x80
+#define CX0342_RST_OVERFLOW_H		0x81
+#define CX0342_RST_UNDERFLOW_L		0x82
+#define CX0342_RST_UNDERFLOW_H		0x83
+#define CX0342_DATA_OVERFLOW_L		0x84
+#define CX0342_DATA_OVERFLOW_H		0x85
+#define CX0342_DATA_UNDERFLOW_L		0x86
+#define CX0342_DATA_UNDERFLOW_H		0x87
+#define CX0342_CHANNEL_0_0_L_irst	0x90
+#define CX0342_CHANNEL_0_0_H_irst	0x91
+#define CX0342_CHANNEL_0_1_L_irst	0x92
+#define CX0342_CHANNEL_0_1_H_irst	0x93
+#define CX0342_CHANNEL_0_2_L_irst	0x94
+#define CX0342_CHANNEL_0_2_H_irst	0x95
+#define CX0342_CHANNEL_0_3_L_irst	0x96
+#define CX0342_CHANNEL_0_3_H_irst	0x97
+#define CX0342_CHANNEL_0_4_L_irst	0x98
+#define CX0342_CHANNEL_0_4_H_irst	0x99
+#define CX0342_CHANNEL_0_5_L_irst	0x9a
+#define CX0342_CHANNEL_0_5_H_irst	0x9b
+#define CX0342_CHANNEL_0_6_L_irst	0x9c
+#define CX0342_CHANNEL_0_6_H_irst	0x9d
+#define CX0342_CHANNEL_0_7_L_irst	0x9e
+#define CX0342_CHANNEL_0_7_H_irst	0x9f
+#define CX0342_CHANNEL_1_0_L_itx	0xa0
+#define CX0342_CHANNEL_1_0_H_itx	0xa1
+#define CX0342_CHANNEL_1_1_L_itx	0xa2
+#define CX0342_CHANNEL_1_1_H_itx	0xa3
+#define CX0342_CHANNEL_1_2_L_itx	0xa4
+#define CX0342_CHANNEL_1_2_H_itx	0xa5
+#define CX0342_CHANNEL_1_3_L_itx	0xa6
+#define CX0342_CHANNEL_1_3_H_itx	0xa7
+#define CX0342_CHANNEL_1_4_L_itx	0xa8
+#define CX0342_CHANNEL_1_4_H_itx	0xa9
+#define CX0342_CHANNEL_1_5_L_itx	0xaa
+#define CX0342_CHANNEL_1_5_H_itx	0xab
+#define CX0342_CHANNEL_1_6_L_itx	0xac
+#define CX0342_CHANNEL_1_6_H_itx	0xad
+#define CX0342_CHANNEL_1_7_L_itx	0xae
+#define CX0342_CHANNEL_1_7_H_itx	0xaf
+#define CX0342_CHANNEL_2_0_L_iwl	0xb0
+#define CX0342_CHANNEL_2_0_H_iwl	0xb1
+#define CX0342_CHANNEL_2_1_L_iwl	0xb2
+#define CX0342_CHANNEL_2_1_H_iwl	0xb3
+#define CX0342_CHANNEL_2_2_L_iwl	0xb4
+#define CX0342_CHANNEL_2_2_H_iwl	0xb5
+#define CX0342_CHANNEL_2_3_L_iwl	0xb6
+#define CX0342_CHANNEL_2_3_H_iwl	0xb7
+#define CX0342_CHANNEL_2_4_L_iwl	0xb8
+#define CX0342_CHANNEL_2_4_H_iwl	0xb9
+#define CX0342_CHANNEL_2_5_L_iwl	0xba
+#define CX0342_CHANNEL_2_5_H_iwl	0xbb
+#define CX0342_CHANNEL_2_6_L_iwl	0xbc
+#define CX0342_CHANNEL_2_6_H_iwl	0xbd
+#define CX0342_CHANNEL_2_7_L_iwl	0xbe
+#define CX0342_CHANNEL_2_7_H_iwl	0xbf
+#define CX0342_CHANNEL_3_0_L_ensp	0xc0
+#define CX0342_CHANNEL_3_0_H_ensp	0xc1
+#define CX0342_CHANNEL_3_1_L_ensp	0xc2
+#define CX0342_CHANNEL_3_1_H_ensp	0xc3
+#define CX0342_CHANNEL_3_2_L_ensp	0xc4
+#define CX0342_CHANNEL_3_2_H_ensp	0xc5
+#define CX0342_CHANNEL_3_3_L_ensp	0xc6
+#define CX0342_CHANNEL_3_3_H_ensp	0xc7
+#define CX0342_CHANNEL_3_4_L_ensp	0xc8
+#define CX0342_CHANNEL_3_4_H_ensp	0xc9
+#define CX0342_CHANNEL_3_5_L_ensp	0xca
+#define CX0342_CHANNEL_3_5_H_ensp	0xcb
+#define CX0342_CHANNEL_3_6_L_ensp	0xcc
+#define CX0342_CHANNEL_3_6_H_ensp	0xcd
+#define CX0342_CHANNEL_3_7_L_ensp	0xce
+#define CX0342_CHANNEL_3_7_H_ensp	0xcf
+#define CX0342_CHANNEL_4_0_L_sela	0xd0
+#define CX0342_CHANNEL_4_0_H_sela	0xd1
+#define CX0342_CHANNEL_4_1_L_sela	0xd2
+#define CX0342_CHANNEL_4_1_H_sela	0xd3
+#define CX0342_CHANNEL_5_0_L_intla	0xe0
+#define CX0342_CHANNEL_5_0_H_intla	0xe1
+#define CX0342_CHANNEL_5_1_L_intla	0xe2
+#define CX0342_CHANNEL_5_1_H_intla	0xe3
+#define CX0342_CHANNEL_5_2_L_intla	0xe4
+#define CX0342_CHANNEL_5_2_H_intla	0xe5
+#define CX0342_CHANNEL_5_3_L_intla	0xe6
+#define CX0342_CHANNEL_5_3_H_intla	0xe7
+#define CX0342_CHANNEL_6_0_L_xa_sel_pos 0xf0
+#define CX0342_CHANNEL_6_0_H_xa_sel_pos 0xf1
+#define CX0342_CHANNEL_7_1_L_cds_pos	0xf2
+#define CX0342_CHANNEL_7_1_H_cds_pos	0xf3
+#define CX0342_SENSOR_HEIGHT_L		0xfb
+#define CX0342_SENSOR_HEIGHT_H		0xfc
+#define CX0342_SENSOR_WIDTH_L		0xfd
+#define CX0342_SENSOR_WIDTH_H		0xfe
+#define CX0342_VSYNC_HSYNC_READ		0xff
+
+struct cmd {
+	u8 reg;
+	u8 val;
+};
+
+static const u8 DQT[17][130] = {
+	/* Define quantization table (thanks to Thomas Kaiser) */
+	{			/* Quality 0 */
+	 0x00,
+	 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+	 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+	 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+	 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+	 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+	 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+	 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+	 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+	 0x01,
+	 0x04, 0x04, 0x04, 0x06, 0x05, 0x06, 0x0b, 0x06,
+	 0x06, 0x0b, 0x18, 0x10, 0x0e, 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,
+	 },
+	{			/* Quality 1 */
+	 0x00,
+	 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+	 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+	 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+	 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+	 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+	 0x04, 0x04, 0x04, 0x09, 0x09, 0x09, 0x09, 0x09,
+	 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+	 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+	 0x01,
+	 0x08, 0x09, 0x09, 0x0c, 0x0a, 0x0c, 0x17, 0x0d,
+	 0x0d, 0x17, 0x31, 0x21, 0x1c, 0x21, 0x31, 0x31,
+	 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+	 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+	 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+	 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+	 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+	 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+	 },
+	{			/* Quality 2 */
+	 0x00,
+	 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+	 0x04, 0x04, 0x06, 0x06, 0x06, 0x04, 0x04, 0x04,
+	 0x04, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+	 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+	 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+	 0x06, 0x06, 0x06, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+	 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+	 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+	 0x01,
+	 0x0c, 0x0d, 0x0d, 0x12, 0x0f, 0x12, 0x23, 0x13,
+	 0x13, 0x23, 0x4a, 0x31, 0x2a, 0x31, 0x4a, 0x4a,
+	 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a,
+	 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a,
+	 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a,
+	 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a,
+	 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a,
+	 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a,
+	 },
+	{			/* Quality 3 */
+	 0x00,
+	 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+	 0x04, 0x04, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04,
+	 0x04, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+	 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+	 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+	 0x08, 0x08, 0x08, 0x13, 0x13, 0x13, 0x13, 0x13,
+	 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+	 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+	 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,
+	 },
+	{			/* Quality 4 */
+	 0x00,
+	 0x04, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+	 0x05, 0x05, 0x0a, 0x0a, 0x0a, 0x05, 0x05, 0x05,
+	 0x05, 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, 0x17, 0x17, 0x17, 0x17, 0x17,
+	 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+	 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+	 0x01,
+	 0x11, 0x16, 0x16, 0x1e, 0x1a, 0x1e, 0x3a, 0x20,
+	 0x20, 0x3a, 0x7b, 0x52, 0x46, 0x52, 0x7b, 0x7b,
+	 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b,
+	 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b,
+	 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b,
+	 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b,
+	 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b,
+	 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b,
+	 },
+	{			/* Quality 5 */
+	 0x00,
+	 0x04, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+	 0x06, 0x06, 0x0c, 0x0c, 0x0c, 0x06, 0x06, 0x06,
+	 0x06, 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, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+	 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+	 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+	 0x01,
+	 0x11, 0x1b, 0x1b, 0x24, 0x1f, 0x24, 0x46, 0x27,
+	 0x27, 0x46, 0x94, 0x63, 0x54, 0x63, 0x94, 0x94,
+	 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+	 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+	 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+	 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+	 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+	 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+	 },
+	{			/* Quality 6 */
+	 0x00,
+	 0x05, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	 0x07, 0x07, 0x0e, 0x0e, 0x0e, 0x07, 0x07, 0x07,
+	 0x07, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+	 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+	 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+	 0x0e, 0x0e, 0x0e, 0x21, 0x21, 0x21, 0x21, 0x21,
+	 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+	 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+	 0x01,
+	 0x15, 0x1f, 0x1f, 0x2a, 0x24, 0x2a, 0x52, 0x2d,
+	 0x2d, 0x52, 0xad, 0x73, 0x62, 0x73, 0xad, 0xad,
+	 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+	 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+	 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+	 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+	 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+	 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+	 },
+	{			/* Quality 7 */
+	 0x00,
+	 0x05, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+	 0x08, 0x08, 0x10, 0x10, 0x10, 0x08, 0x08, 0x08,
+	 0x08, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+	 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+	 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+	 0x10, 0x10, 0x10, 0x26, 0x26, 0x26, 0x26, 0x26,
+	 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+	 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+	 0x01,
+	 0x15, 0x24, 0x24, 0x30, 0x2a, 0x30, 0x5e, 0x34,
+	 0x34, 0x5e, 0xc6, 0x84, 0x70, 0x84, 0xc6, 0xc6,
+	 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+	 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+	 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+	 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+	 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+	 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+	 },
+	{			/* Quality 8 */
+	 0x00,
+	 0x06, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+	 0x0a, 0x0a, 0x14, 0x14, 0x14, 0x0a, 0x0a, 0x0a,
+	 0x0a, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+	 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+	 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+	 0x14, 0x14, 0x14, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+	 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+	 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+	 0x01,
+	 0x19, 0x2d, 0x2d, 0x3c, 0x34, 0x3c, 0x75, 0x41,
+	 0x41, 0x75, 0xf7, 0xa5, 0x8c, 0xa5, 0xf7, 0xf7,
+	 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
+	 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
+	 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
+	 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
+	 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
+	 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
+	 },
+	{			/* Quality 9 */
+	 0x00,
+	 0x06, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+	 0x0c, 0x0c, 0x18, 0x18, 0x18, 0x0c, 0x0c, 0x0c,
+	 0x0c, 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, 0x39, 0x39, 0x39, 0x39, 0x39,
+	 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
+	 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
+	 0x01,
+	 0x19, 0x36, 0x36, 0x48, 0x3f, 0x48, 0x8d, 0x4e,
+	 0x4e, 0x8d, 0xff, 0xc6, 0xa8, 0xc6, 0xff, 0xff,
+	 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	 },
+	{			/* Quality 10 */
+	 0x00,
+	 0x07, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+	 0x0e, 0x0e, 0x1c, 0x1c, 0x1c, 0x0e, 0x0e, 0x0e,
+	 0x0e, 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, 0x42, 0x42, 0x42, 0x42, 0x42,
+	 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+	 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+	 0x01,
+	 0x1d, 0x3f, 0x3f, 0x54, 0x49, 0x54, 0xa4, 0x5b,
+	 0x5b, 0xa4, 0xff, 0xe7, 0xc4, 0xe7, 0xff, 0xff,
+	 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	 },
+	{			/* Quality 11 */
+	 0x00,
+	 0x07, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+	 0x10, 0x10, 0x20, 0x20, 0x20, 0x10, 0x10, 0x10,
+	 0x10, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	 0x20, 0x20, 0x20, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c,
+	 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c,
+	 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c,
+	 0x01,
+	 0x1d, 0x48, 0x48, 0x60, 0x54, 0x60, 0xbc, 0x68,
+	 0x68, 0xbc, 0xff, 0xff, 0xe0, 0xff, 0xff, 0xff,
+	 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	 },
+	{			/* Quality 12 */
+	 0x00,
+	 0x08, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+	 0x14, 0x14, 0x28, 0x28, 0x28, 0x14, 0x14, 0x14,
+	 0x14, 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, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f,
+	 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f,
+	 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f,
+	 0x01,
+	 0x22, 0x5a, 0x5a, 0x78, 0x69, 0x78, 0xeb, 0x82,
+	 0x82, 0xeb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	 },
+	{			/* Quality 13 */
+	 0x00,
+	 0x08, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+	 0x18, 0x18, 0x30, 0x30, 0x30, 0x18, 0x18, 0x18,
+	 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+	 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+	 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+	 0x30, 0x30, 0x30, 0x72, 0x72, 0x72, 0x72, 0x72,
+	 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72,
+	 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72,
+	 0x01,
+	 0x22, 0x6c, 0x6c, 0x90, 0x7e, 0x90, 0xff, 0x9c,
+	 0x9c, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	 },
+	{			/* Quality 14 */
+	 0x00,
+	 0x0a, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+	 0x1c, 0x1c, 0x38, 0x38, 0x38, 0x1c, 0x1c, 0x1c,
+	 0x1c, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
+	 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
+	 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
+	 0x38, 0x38, 0x38, 0x85, 0x85, 0x85, 0x85, 0x85,
+	 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+	 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+	 0x01,
+	 0x2a, 0x7e, 0x7e, 0xa8, 0x93, 0xa8, 0xff, 0xb6,
+	 0xb6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	 },
+	{			/* Quality 15 */
+	 0x00,
+	 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	 0x20, 0x20, 0x40, 0x40, 0x40, 0x20, 0x20, 0x20,
+	 0x20, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+	 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+	 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+	 0x40, 0x40, 0x40, 0x98, 0x98, 0x98, 0x98, 0x98,
+	 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+	 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+	 0x01,
+	 0x2a, 0x90, 0x90, 0xc0, 0xa8, 0xc0, 0xff, 0xd0,
+	 0xd0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	 },
+	{			/* Quality 16-31 */
+	 0x00,
+	 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+	 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+	 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+	 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+	 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+	 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+	 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+	 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+	 0x01,
+	 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+	 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+	 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+	 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+	 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+	 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+	 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+	 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+	 }
+};
+
+static const struct cmd tp6810_cx_init_common[] = {
+	{0x1c, 0x00},
+	{TP6800_R10_SIF_TYPE, 0x00},
+	{0x4e, 0x00},
+	{0x4f, 0x00},
+	{TP6800_R50, 0xff},
+	{TP6800_R51, 0x03},
+	{0x00, 0x07},
+	{TP6800_R79_QUALITY, 0x03},
+	{TP6800_R2F_TIMING_CFG, 0x37},
+	{TP6800_R30_SENSOR_CFG, 0x10},
+	{TP6800_R21_ENDP_1_CTL, 0x00},
+	{TP6800_R52, 0x40},
+	{TP6800_R53, 0x40},
+	{TP6800_R54_DARK_CFG, 0x40},
+	{TP6800_R30_SENSOR_CFG, 0x18},
+	{0x4b, 0x00},
+	{TP6800_R3F_FRAME_RATE, 0x83},
+	{TP6800_R79_QUALITY, 0x05},
+	{TP6800_R21_ENDP_1_CTL, 0x00},
+	{0x7c, 0x04},
+	{0x25, 0x14},
+	{0x26, 0x0f},
+	{0x7b, 0x10},
+};
+
+static const struct cmd tp6810_ov_init_common[] = {
+	{0x1c, 0x00},
+	{TP6800_R10_SIF_TYPE, 0x00},
+	{0x4e, 0x00},
+	{0x4f, 0x00},
+	{TP6800_R50, 0xff},
+	{TP6800_R51, 0x03},
+	{0x00, 0x07},
+	{TP6800_R52, 0x40},
+	{TP6800_R53, 0x40},
+	{TP6800_R54_DARK_CFG, 0x40},
+	{TP6800_R79_QUALITY, 0x03},
+	{TP6800_R2F_TIMING_CFG, 0x17},
+	{TP6800_R30_SENSOR_CFG, 0x18},
+	{TP6800_R21_ENDP_1_CTL, 0x00},
+	{TP6800_R3F_FRAME_RATE, 0x86},
+	{0x25, 0x18},
+	{0x26, 0x0f},
+	{0x7b, 0x90},
+};
+
+static const struct cmd tp6810_bridge_start[] = {
+	{0x59, 0x88},
+	{0x5a, 0x0f},
+	{0x5b, 0x4e},
+	{TP6800_R5C_EDGE_THRLD, 0x63},
+	{TP6800_R5D_DEMOSAIC_CFG, 0x00},
+	{0x03, 0x7f},
+	{0x04, 0x80},
+	{0x06, 0x00},
+	{0x00, 0x00},
+};
+
+static const struct cmd tp6810_late_start[] = {
+	{0x7d, 0x01},
+	{0xb0, 0x04},
+	{0xb1, 0x04},
+	{0xb2, 0x04},
+	{0xb3, 0x04},
+	{0xb4, 0x04},
+	{0xb5, 0x04},
+	{0xb6, 0x08},
+	{0xb7, 0x08},
+	{0xb8, 0x04},
+	{0xb9, 0x04},
+	{0xba, 0x04},
+	{0xbb, 0x04},
+	{0xbc, 0x04},
+	{0xbd, 0x08},
+	{0xbe, 0x08},
+	{0xbf, 0x08},
+	{0xc0, 0x04},
+	{0xc1, 0x04},
+	{0xc2, 0x08},
+	{0xc3, 0x08},
+	{0xc4, 0x08},
+	{0xc5, 0x08},
+	{0xc6, 0x08},
+	{0xc7, 0x13},
+	{0xc8, 0x04},
+	{0xc9, 0x08},
+	{0xca, 0x08},
+	{0xcb, 0x08},
+	{0xcc, 0x08},
+	{0xcd, 0x08},
+	{0xce, 0x13},
+	{0xcf, 0x13},
+	{0xd0, 0x08},
+	{0xd1, 0x08},
+	{0xd2, 0x08},
+	{0xd3, 0x08},
+	{0xd4, 0x08},
+	{0xd5, 0x13},
+	{0xd6, 0x13},
+	{0xd7, 0x13},
+	{0xd8, 0x08},
+	{0xd9, 0x08},
+	{0xda, 0x08},
+	{0xdb, 0x08},
+	{0xdc, 0x13},
+	{0xdd, 0x13},
+	{0xde, 0x13},
+	{0xdf, 0x13},
+	{0xe0, 0x08},
+	{0xe1, 0x08},
+	{0xe2, 0x08},
+	{0xe3, 0x13},
+	{0xe4, 0x13},
+	{0xe5, 0x13},
+	{0xe6, 0x13},
+	{0xe7, 0x13},
+	{0xe8, 0x08},
+	{0xe9, 0x08},
+	{0xea, 0x13},
+	{0xeb, 0x13},
+	{0xec, 0x13},
+	{0xed, 0x13},
+	{0xee, 0x13},
+	{0xef, 0x13},
+	{0x7d, 0x02},
+
+	/* later after isoc start */
+	{0x7d, 0x08},
+	{0x7d, 0x00},
+};
+
+static const struct cmd cx0342_timing_seq[] = {
+	{CX0342_CHANNEL_0_1_L_irst, 0x20},
+	{CX0342_CHANNEL_0_2_L_irst, 0x24},
+	{CX0342_CHANNEL_0_2_H_irst, 0x00},
+	{CX0342_CHANNEL_0_3_L_irst, 0x2f},
+	{CX0342_CHANNEL_0_3_H_irst, 0x00},
+	{CX0342_CHANNEL_1_0_L_itx, 0x02},
+	{CX0342_CHANNEL_1_0_H_itx, 0x00},
+	{CX0342_CHANNEL_1_1_L_itx, 0x20},
+	{CX0342_CHANNEL_1_1_H_itx, 0x00},
+	{CX0342_CHANNEL_1_2_L_itx, 0xe4},
+	{CX0342_CHANNEL_1_2_H_itx, 0x00},
+	{CX0342_CHANNEL_1_3_L_itx, 0xee},
+	{CX0342_CHANNEL_1_3_H_itx, 0x00},
+	{CX0342_CHANNEL_2_0_L_iwl, 0x30},
+	{CX0342_CHANNEL_2_0_H_iwl, 0x00},
+	{CX0342_CHANNEL_3_0_L_ensp, 0x34},
+	{CX0342_CHANNEL_3_1_L_ensp, 0xe2},
+	{CX0342_CHANNEL_3_1_H_ensp, 0x00},
+	{CX0342_CHANNEL_3_2_L_ensp, 0xf6},
+	{CX0342_CHANNEL_3_2_H_ensp, 0x00},
+	{CX0342_CHANNEL_3_3_L_ensp, 0xf4},
+	{CX0342_CHANNEL_3_3_H_ensp, 0x02},
+	{CX0342_CHANNEL_4_0_L_sela, 0x26},
+	{CX0342_CHANNEL_4_0_H_sela, 0x00},
+	{CX0342_CHANNEL_4_1_L_sela, 0xe2},
+	{CX0342_CHANNEL_4_1_H_sela, 0x00},
+	{CX0342_CHANNEL_5_0_L_intla, 0x26},
+	{CX0342_CHANNEL_5_1_L_intla, 0x29},
+	{CX0342_CHANNEL_5_2_L_intla, 0xf0},
+	{CX0342_CHANNEL_5_2_H_intla, 0x00},
+	{CX0342_CHANNEL_5_3_L_intla, 0xf3},
+	{CX0342_CHANNEL_5_3_H_intla, 0x00},
+	{CX0342_CHANNEL_6_0_L_xa_sel_pos, 0x24},
+	{CX0342_CHANNEL_7_1_L_cds_pos, 0x02},
+	{CX0342_TIMING_EN, 0x01},
+};
+
+/* define the JPEG header */
+static void jpeg_define(u8 *jpeg_hdr,
+			int height,
+			int width)
+{
+	memcpy(jpeg_hdr, jpeg_head, sizeof jpeg_head);
+	jpeg_hdr[JPEG_HEIGHT_OFFSET + 0] = height >> 8;
+	jpeg_hdr[JPEG_HEIGHT_OFFSET + 1] = height;
+	jpeg_hdr[JPEG_HEIGHT_OFFSET + 2] = width >> 8;
+	jpeg_hdr[JPEG_HEIGHT_OFFSET + 3] = width;
+}
+
+/* set the JPEG quality for sensor soi763a */
+static void jpeg_set_qual(u8 *jpeg_hdr,
+			  int quality)
+{
+	int i, sc;
+
+	if (quality <= 0)
+		sc = 5000;
+	else if (quality < 50)
+		sc = 5000 / quality;
+	else
+		sc = 200 - quality * 2;
+	for (i = 0; i < 64; i++) {
+		jpeg_hdr[JPEG_QT0_OFFSET + i] =
+			(jpeg_head[JPEG_QT0_OFFSET + i] * sc + 50) / 100;
+		jpeg_hdr[JPEG_QT1_OFFSET + i] =
+			(jpeg_head[JPEG_QT1_OFFSET + i] * sc + 50) / 100;
+	}
+}
+
+static void reg_w(struct gspca_dev *gspca_dev, u8 index, u8 value)
+{
+	struct usb_device *dev = gspca_dev->dev;
+	int ret;
+
+	if (gspca_dev->usb_err < 0)
+		return;
+	ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+			0x0e,
+			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			value, index, NULL, 0, 500);
+	if (ret < 0) {
+		pr_err("reg_w err %d\n", ret);
+		gspca_dev->usb_err = ret;
+	}
+}
+
+/* the returned value is in gspca_dev->usb_buf */
+static void reg_r(struct gspca_dev *gspca_dev, u8 index)
+{
+	struct usb_device *dev = gspca_dev->dev;
+	int ret;
+
+	if (gspca_dev->usb_err < 0)
+		return;
+	ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+			0x0d,
+			USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			0, index, gspca_dev->usb_buf, 1, 500);
+	if (ret < 0) {
+		pr_err("reg_r err %d\n", ret);
+		gspca_dev->usb_err = ret;
+	}
+}
+
+static void reg_w_buf(struct gspca_dev *gspca_dev,
+			const struct cmd *p, int l)
+{
+	do {
+		reg_w(gspca_dev, p->reg, p->val);
+		p++;
+	} while (--l > 0);
+}
+
+static int i2c_w(struct gspca_dev *gspca_dev, u8 index, u8 value)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	reg_w(gspca_dev, TP6800_R11_SIF_CONTROL, 0x00);
+	reg_w(gspca_dev, TP6800_R19_SIF_ADDR_S2, index);
+	reg_w(gspca_dev, TP6800_R13_SIF_TX_DATA, value);
+	reg_w(gspca_dev, TP6800_R11_SIF_CONTROL, 0x01);
+	if (sd->bridge == BRIDGE_TP6800)
+		return 0;
+	msleep(5);
+	reg_r(gspca_dev, TP6800_R11_SIF_CONTROL);
+	if (gspca_dev->usb_buf[0] == 0)
+		return 0;
+	reg_w(gspca_dev, TP6800_R11_SIF_CONTROL, 0x00);
+	return -1;				/* error */
+}
+
+static void i2c_w_buf(struct gspca_dev *gspca_dev,
+			const struct cmd *p, int l)
+{
+	do {
+		i2c_w(gspca_dev, p->reg, p->val);
+		p++;
+	} while (--l > 0);
+}
+
+static int i2c_r(struct gspca_dev *gspca_dev, u8 index, int len)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int v;
+
+	reg_w(gspca_dev, TP6800_R19_SIF_ADDR_S2, index);
+	reg_w(gspca_dev, TP6800_R11_SIF_CONTROL, 0x02);
+	msleep(5);
+	reg_r(gspca_dev, TP6800_R14_SIF_RX_DATA);
+	v = gspca_dev->usb_buf[0];
+	if (sd->bridge == BRIDGE_TP6800)
+		return v;
+	if (len > 1) {
+		reg_r(gspca_dev, TP6800_R1B_SIF_RX_DATA2);
+		v |= (gspca_dev->usb_buf[0] << 8);
+	}
+	reg_r(gspca_dev, TP6800_R11_SIF_CONTROL);
+	if (gspca_dev->usb_buf[0] == 0)
+		return v;
+	reg_w(gspca_dev, TP6800_R11_SIF_CONTROL, 0x00);
+	return -1;
+}
+
+static void bulk_w(struct gspca_dev *gspca_dev,
+		  u8 tag,
+		  const u8 *data,
+		  int length)
+{
+	struct usb_device *dev = gspca_dev->dev;
+	int count, actual_count, ret;
+
+	if (gspca_dev->usb_err < 0)
+		return;
+	for (;;) {
+		count = length > BULK_OUT_SIZE - 1
+				? BULK_OUT_SIZE - 1 : length;
+		gspca_dev->usb_buf[0] = tag;
+		memcpy(&gspca_dev->usb_buf[1], data, count);
+		ret = usb_bulk_msg(dev,
+				   usb_sndbulkpipe(dev, 3),
+				   gspca_dev->usb_buf, count + 1,
+				   &actual_count, 500);
+		if (ret < 0) {
+			pr_err("bulk write error %d tag=%02x\n",
+				ret, tag);
+			gspca_dev->usb_err = ret;
+			return;
+		}
+		length -= count;
+		if (length <= 0)
+			break;
+		data += count;
+	}
+}
+
+static int probe_6810(struct gspca_dev *gspca_dev)
+{
+	u8 gpio;
+	int ret;
+
+	reg_r(gspca_dev, TP6800_R18_GPIO_DATA);
+	gpio = gspca_dev->usb_buf[0];
+	reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio);
+	reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+	reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio);
+	reg_w(gspca_dev, TP6800_R10_SIF_TYPE, 0x04);	/* i2c 16 bits */
+	reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x21);	/* ov??? */
+	reg_w(gspca_dev, TP6800_R1A_SIF_TX_DATA2, 0x00);
+	if (i2c_w(gspca_dev, 0x00, 0x00) >= 0)
+		return SENSOR_SOI763A;
+
+	reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+	reg_w(gspca_dev, TP6800_R10_SIF_TYPE, 0x00);	/* i2c 8 bits */
+	reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x7f);	/* (unknown i2c) */
+	if (i2c_w(gspca_dev, 0x00, 0x00) >= 0)
+		return -2;
+
+	reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+	reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio);
+	reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+	reg_w(gspca_dev, TP6800_R10_SIF_TYPE, 0x00);	/* i2c 8 bits */
+	reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x11);	/* tas??? / hv??? */
+	ret = i2c_r(gspca_dev, 0x00, 1);
+	if (ret > 0)
+		return -3;
+
+	reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+	reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio);
+	reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+	reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x6e);	/* po??? */
+	ret = i2c_r(gspca_dev, 0x00, 1);
+	if (ret > 0)
+		return -4;
+
+	ret = i2c_r(gspca_dev, 0x01, 1);
+	if (ret > 0)
+		return -5;
+
+	reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+	reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio);
+	reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+	reg_w(gspca_dev, TP6800_R10_SIF_TYPE, 0x04);	/* i2c 16 bits */
+	reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x5d);	/* mi/mt??? */
+	ret = i2c_r(gspca_dev, 0x00, 2);
+	if (ret > 0)
+		return -6;
+
+	reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+	reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio);
+	reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+	reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x5c);	/* mi/mt??? */
+	ret = i2c_r(gspca_dev, 0x36, 2);
+	if (ret > 0)
+		return -7;
+
+	reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio);
+	reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+	reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio);
+	reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x61);	/* (unknown i2c) */
+	reg_w(gspca_dev, TP6800_R1A_SIF_TX_DATA2, 0x10);
+	if (i2c_w(gspca_dev, 0xff, 0x00) >= 0)
+		return -8;
+
+	reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+	reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio);
+	reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+	reg_w(gspca_dev, TP6800_R10_SIF_TYPE, 0x00);	/* i2c 8 bits */
+	reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x20);	/* cx0342 */
+	ret = i2c_r(gspca_dev, 0x00, 1);
+	if (ret > 0)
+		return SENSOR_CX0342;
+	return -9;
+}
+
+static void cx0342_6810_init(struct gspca_dev *gspca_dev)
+{
+	static const struct cmd reg_init_1[] = {
+		{TP6800_R2F_TIMING_CFG, 0x2f},
+		{0x25, 0x02},
+		{TP6800_R21_ENDP_1_CTL, 0x00},
+		{TP6800_R3F_FRAME_RATE, 0x80},
+		{TP6800_R2F_TIMING_CFG, 0x2f},
+		{TP6800_R18_GPIO_DATA, 0xe1},
+		{TP6800_R18_GPIO_DATA, 0xc1},
+		{TP6800_R18_GPIO_DATA, 0xe1},
+		{TP6800_R11_SIF_CONTROL, 0x00},
+	};
+	static const struct cmd reg_init_2[] = {
+		{TP6800_R78_FORMAT, 0x48},
+		{TP6800_R11_SIF_CONTROL, 0x00},
+	};
+	static const struct cmd sensor_init[] = {
+		{CX0342_OUTPUT_CTRL, 0x07},
+		{CX0342_BYPASS_MODE, 0x58},
+		{CX0342_GPXLTHD_L, 0x28},
+		{CX0342_RBPXLTHD_L, 0x28},
+		{CX0342_PLANETHD_L, 0x50},
+		{CX0342_PLANETHD_H, 0x03},
+		{CX0342_RB_GAP_L, 0xff},
+		{CX0342_RB_GAP_H, 0x07},
+		{CX0342_G_GAP_L, 0xff},
+		{CX0342_G_GAP_H, 0x07},
+		{CX0342_RST_OVERFLOW_L, 0x5c},
+		{CX0342_RST_OVERFLOW_H, 0x01},
+		{CX0342_DATA_OVERFLOW_L, 0xfc},
+		{CX0342_DATA_OVERFLOW_H, 0x03},
+		{CX0342_DATA_UNDERFLOW_L, 0x00},
+		{CX0342_DATA_UNDERFLOW_H, 0x00},
+		{CX0342_SYS_CTRL_0, 0x40},
+		{CX0342_GLOBAL_GAIN, 0x01},
+		{CX0342_CLOCK_GEN, 0x00},
+		{CX0342_SYS_CTRL_0, 0x02},
+		{CX0342_IDLE_CTRL, 0x05},
+		{CX0342_ADCGN, 0x00},
+		{CX0342_ADC_CTL, 0x00},
+		{CX0342_LVRST_BLBIAS, 0x01},
+		{CX0342_VTHSEL, 0x0b},
+		{CX0342_RAMP_RIV, 0x0b},
+		{CX0342_LDOSEL, 0x07},
+		{CX0342_SPV_VALUE_L, 0x40},
+		{CX0342_SPV_VALUE_H, 0x02},
+
+		{CX0342_AUTO_ADC_CALIB, 0x81},
+		{CX0342_TIMING_EN, 0x01},
+	};
+
+	reg_w_buf(gspca_dev, reg_init_1, ARRAY_SIZE(reg_init_1));
+	reg_w_buf(gspca_dev, tp6810_cx_init_common,
+			ARRAY_SIZE(tp6810_cx_init_common));
+	reg_w_buf(gspca_dev, reg_init_2, ARRAY_SIZE(reg_init_2));
+
+	reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x20);	/* cx0342 I2C addr */
+	i2c_w_buf(gspca_dev, sensor_init, ARRAY_SIZE(sensor_init));
+	i2c_w_buf(gspca_dev, cx0342_timing_seq, ARRAY_SIZE(cx0342_timing_seq));
+}
+
+static void soi763a_6810_init(struct gspca_dev *gspca_dev)
+{
+	static const struct cmd reg_init_1[] = {
+		{TP6800_R2F_TIMING_CFG, 0x2f},
+		{TP6800_R18_GPIO_DATA, 0xe1},
+		{0x25, 0x02},
+		{TP6800_R21_ENDP_1_CTL, 0x00},
+		{TP6800_R3F_FRAME_RATE, 0x80},
+		{TP6800_R2F_TIMING_CFG, 0x2f},
+		{TP6800_R18_GPIO_DATA, 0xc1},
+	};
+	static const struct cmd reg_init_2[] = {
+		{TP6800_R78_FORMAT, 0x54},
+	};
+	static const struct cmd sensor_init[] = {
+		{0x00, 0x00},
+		{0x01, 0x80},
+		{0x02, 0x80},
+		{0x03, 0x90},
+		{0x04, 0x20},
+		{0x05, 0x20},
+		{0x06, 0x80},
+		{0x07, 0x00},
+		{0x08, 0xff},
+		{0x09, 0xff},
+		{0x0a, 0x76},		/* 7630 = soi673a */
+		{0x0b, 0x30},
+		{0x0c, 0x20},
+		{0x0d, 0x20},
+		{0x0e, 0xff},
+		{0x0f, 0xff},
+		{0x10, 0x41},
+		{0x15, 0x14},
+		{0x11, 0x40},
+		{0x12, 0x48},
+		{0x13, 0x80},
+		{0x14, 0x80},
+		{0x16, 0x03},
+		{0x28, 0xb0},
+		{0x71, 0x20},
+		{0x75, 0x8e},
+		{0x17, 0x1b},
+		{0x18, 0xbd},
+		{0x19, 0x05},
+		{0x1a, 0xf6},
+		{0x1b, 0x04},
+		{0x1c, 0x7f},		/* omnivision */
+		{0x1d, 0xa2},
+		{0x1e, 0x00},
+		{0x1f, 0x00},
+		{0x20, 0x45},
+		{0x21, 0x80},
+		{0x22, 0x80},
+		{0x23, 0xee},
+		{0x24, 0x50},
+		{0x25, 0x7a},
+		{0x26, 0xa0},
+		{0x27, 0x9a},
+		{0x29, 0x30},
+		{0x2a, 0x80},
+		{0x2b, 0x00},
+		{0x2c, 0xac},
+		{0x2d, 0x05},
+		{0x2e, 0x80},
+		{0x2f, 0x3c},
+		{0x30, 0x22},
+		{0x31, 0x00},
+		{0x32, 0x86},
+		{0x33, 0x08},
+		{0x34, 0xff},
+		{0x35, 0xff},
+		{0x36, 0xff},
+		{0x37, 0xff},
+		{0x38, 0xff},
+		{0x39, 0xff},
+		{0x3a, 0xfe},
+		{0x3b, 0xfe},
+		{0x3c, 0xfe},
+		{0x3d, 0xfe},
+		{0x3e, 0xfe},
+		{0x3f, 0x71},
+		{0x40, 0xff},
+		{0x41, 0xff},
+		{0x42, 0xff},
+		{0x43, 0xff},
+		{0x44, 0xff},
+		{0x45, 0xff},
+		{0x46, 0xff},
+		{0x47, 0xff},
+		{0x48, 0xff},
+		{0x49, 0xff},
+		{0x4a, 0xfe},
+		{0x4b, 0xff},
+		{0x4c, 0x00},
+		{0x4d, 0x00},
+		{0x4e, 0xff},
+		{0x4f, 0xff},
+		{0x50, 0xff},
+		{0x51, 0xff},
+		{0x52, 0xff},
+		{0x53, 0xff},
+		{0x54, 0xff},
+		{0x55, 0xff},
+		{0x56, 0xff},
+		{0x57, 0xff},
+		{0x58, 0xff},
+		{0x59, 0xff},
+		{0x5a, 0xff},
+		{0x5b, 0xfe},
+		{0x5c, 0xff},
+		{0x5d, 0x8f},
+		{0x5e, 0xff},
+		{0x5f, 0x8f},
+		{0x60, 0xa2},
+		{0x61, 0x4a},
+		{0x62, 0xf3},
+		{0x63, 0x75},
+		{0x64, 0xf0},
+		{0x65, 0x00},
+		{0x66, 0x55},
+		{0x67, 0x92},
+		{0x68, 0xa0},
+		{0x69, 0x4a},
+		{0x6a, 0x22},
+		{0x6b, 0x00},
+		{0x6c, 0x33},
+		{0x6d, 0x44},
+		{0x6e, 0x22},
+		{0x6f, 0x84},
+		{0x70, 0x0b},
+		{0x72, 0x10},
+		{0x73, 0x50},
+		{0x74, 0x21},
+		{0x76, 0x00},
+		{0x77, 0xa5},
+		{0x78, 0x80},
+		{0x79, 0x80},
+		{0x7a, 0x80},
+		{0x7b, 0xe2},
+		{0x7c, 0x00},
+		{0x7d, 0xf7},
+		{0x7e, 0x00},
+		{0x7f, 0x00},
+	};
+
+	reg_w_buf(gspca_dev, reg_init_1, ARRAY_SIZE(reg_init_1));
+	reg_w_buf(gspca_dev, tp6810_ov_init_common,
+			ARRAY_SIZE(tp6810_ov_init_common));
+	reg_w_buf(gspca_dev, reg_init_2, ARRAY_SIZE(reg_init_2));
+
+	i2c_w(gspca_dev, 0x12, 0x80);		/* sensor reset */
+	msleep(10);
+	i2c_w_buf(gspca_dev, sensor_init, ARRAY_SIZE(sensor_init));
+}
+
+/* set the gain and exposure */
+static void setexposure(struct gspca_dev *gspca_dev, s32 expo, s32 gain,
+							s32 blue, s32 red)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (sd->sensor == SENSOR_CX0342) {
+		expo = (expo << 2) - 1;
+		i2c_w(gspca_dev, CX0342_EXPO_LINE_L, expo);
+		i2c_w(gspca_dev, CX0342_EXPO_LINE_H, expo >> 8);
+		if (sd->bridge == BRIDGE_TP6800)
+			i2c_w(gspca_dev, CX0342_RAW_GBGAIN_H,
+						gain >> 8);
+		i2c_w(gspca_dev, CX0342_RAW_GBGAIN_L, gain);
+		if (sd->bridge == BRIDGE_TP6800)
+			i2c_w(gspca_dev, CX0342_RAW_GRGAIN_H,
+					gain >> 8);
+		i2c_w(gspca_dev, CX0342_RAW_GRGAIN_L, gain);
+		if (sd->sensor == SENSOR_CX0342) {
+			if (sd->bridge == BRIDGE_TP6800)
+				i2c_w(gspca_dev, CX0342_RAW_BGAIN_H,
+						blue >> 8);
+			i2c_w(gspca_dev, CX0342_RAW_BGAIN_L, blue);
+			if (sd->bridge == BRIDGE_TP6800)
+				i2c_w(gspca_dev, CX0342_RAW_RGAIN_H,
+						red >> 8);
+			i2c_w(gspca_dev, CX0342_RAW_RGAIN_L, red);
+		}
+		i2c_w(gspca_dev, CX0342_SYS_CTRL_0,
+				sd->bridge == BRIDGE_TP6800 ? 0x80 : 0x81);
+		return;
+	}
+
+	/* soi763a */
+	i2c_w(gspca_dev, 0x10,		/* AEC_H (exposure time) */
+			 expo);
+/*	i2c_w(gspca_dev, 0x76, 0x02);	 * AEC_L ([1:0] */
+	i2c_w(gspca_dev, 0x00,		/* gain */
+			 gain);
+}
+
+/* set the JPEG quantization tables */
+static void set_dqt(struct gspca_dev *gspca_dev, u8 q)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	/* update the jpeg quantization tables */
+	PDEBUG(D_STREAM, "q %d -> %d", sd->quality, q);
+	sd->quality = q;
+	if (q > 16)
+		q = 16;
+	if (sd->sensor == SENSOR_SOI763A)
+		jpeg_set_qual(sd->jpeg_hdr, jpeg_q[q]);
+	else
+		memcpy(&sd->jpeg_hdr[JPEG_QT0_OFFSET - 1],
+			DQT[q], sizeof DQT[0]);
+}
+
+/* set the JPEG compression quality factor */
+static void setquality(struct gspca_dev *gspca_dev, s32 q)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (q != 16)
+		q = 15 - q;
+
+	reg_w(gspca_dev, TP6800_R7A_BLK_THRLD, 0x00);
+	reg_w(gspca_dev, TP6800_R79_QUALITY, 0x04);
+	reg_w(gspca_dev, TP6800_R79_QUALITY, q);
+
+	/* auto quality */
+	if (q == 15 && sd->bridge == BRIDGE_TP6810) {
+		msleep(4);
+		reg_w(gspca_dev, TP6800_R7A_BLK_THRLD, 0x19);
+	}
+}
+
+static const u8 color_null[18] = {
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+static const u8 color_gain[NSENSORS][18] = {
+[SENSOR_CX0342] =
+	{0x4c, 0x00, 0xa9, 0x00, 0x31, 0x00,	/* Y R/G/B (LE values) */
+	 0xb6, 0x03, 0x6c, 0x03, 0xe0, 0x00,	/* U R/G/B */
+	 0xdf, 0x00, 0x46, 0x03, 0xdc, 0x03},	/* V R/G/B */
+[SENSOR_SOI763A] =
+	{0x4c, 0x00, 0x95, 0x00, 0x1d, 0x00,	/* Y R/G/B (LE values) */
+	 0xb6, 0x03, 0x6c, 0x03, 0xd7, 0x00,	/* U R/G/B */
+	 0xd5, 0x00, 0x46, 0x03, 0xdc, 0x03},	/* V R/G/B */
+};
+
+static void setgamma(struct gspca_dev *gspca_dev, s32 gamma)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+#define NGAMMA 6
+	static const u8 gamma_tb[NGAMMA][3][1024] = {
+	    {				/* gamma 0 - from tp6800 + soi763a */
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+		 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02,
+		 0x02, 0x03, 0x05, 0x07, 0x07, 0x08, 0x09, 0x09,
+		 0x0a, 0x0c, 0x0c, 0x0d, 0x0e, 0x0e, 0x10, 0x11,
+		 0x11, 0x12, 0x14, 0x14, 0x15, 0x16, 0x16, 0x17,
+		 0x17, 0x18, 0x1a, 0x1a, 0x1b, 0x1b, 0x1c, 0x1e,
+		 0x1e, 0x1f, 0x1f, 0x20, 0x20, 0x22, 0x23, 0x23,
+		 0x25, 0x25, 0x26, 0x26, 0x27, 0x27, 0x28, 0x28,
+		 0x29, 0x29, 0x2b, 0x2c, 0x2c, 0x2d, 0x2d, 0x2f,
+		 0x2f, 0x30, 0x30, 0x31, 0x31, 0x33, 0x33, 0x34,
+		 0x34, 0x34, 0x35, 0x35, 0x37, 0x37, 0x38, 0x38,
+		 0x39, 0x39, 0x3a, 0x3a, 0x3b, 0x3b, 0x3b, 0x3c,
+		 0x3c, 0x3d, 0x3d, 0x3f, 0x3f, 0x40, 0x40, 0x40,
+		 0x42, 0x42, 0x43, 0x43, 0x44, 0x44, 0x44, 0x45,
+		 0x45, 0x47, 0x47, 0x47, 0x48, 0x48, 0x49, 0x49,
+		 0x4a, 0x4a, 0x4a, 0x4b, 0x4b, 0x4b, 0x4c, 0x4c,
+		 0x4d, 0x4d, 0x4d, 0x4f, 0x4f, 0x50, 0x50, 0x50,
+		 0x52, 0x52, 0x52, 0x53, 0x53, 0x54, 0x54, 0x54,
+		 0x55, 0x55, 0x55, 0x56, 0x56, 0x58, 0x58, 0x58,
+		 0x59, 0x59, 0x59, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b,
+		 0x5b, 0x5c, 0x5c, 0x5c, 0x5e, 0x5e, 0x5e, 0x5f,
+		 0x5f, 0x5f, 0x60, 0x60, 0x60, 0x61, 0x61, 0x61,
+		 0x62, 0x62, 0x62, 0x63, 0x63, 0x63, 0x65, 0x65,
+		 0x65, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67, 0x68,
+		 0x68, 0x68, 0x69, 0x69, 0x69, 0x69, 0x6a, 0x6a,
+		 0x6a, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6e,
+		 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70,
+		 0x70, 0x71, 0x71, 0x71, 0x71, 0x73, 0x73, 0x73,
+		 0x74, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x77,
+		 0x77, 0x77, 0x77, 0x78, 0x78, 0x78, 0x79, 0x79,
+		 0x79, 0x79, 0x7a, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b,
+		 0x7b, 0x7c, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d,
+		 0x7d, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80, 0x80,
+		 0x81, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x82,
+		 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85, 0x86,
+		 0x86, 0x86, 0x86, 0x88, 0x88, 0x88, 0x88, 0x89,
+		 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8a, 0x8b,
+		 0x8b, 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8d, 0x8e,
+		 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f, 0x8f, 0x90,
+		 0x90, 0x90, 0x90, 0x90, 0x91, 0x91, 0x91, 0x91,
+		 0x92, 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x93,
+		 0x94, 0x94, 0x94, 0x94, 0x96, 0x96, 0x96, 0x96,
+		 0x96, 0x97, 0x97, 0x97, 0x97, 0x98, 0x98, 0x98,
+		 0x98, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a, 0x9a,
+		 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c,
+		 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e,
+		 0x9e, 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa0,
+		 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2,
+		 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4,
+		 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa6,
+		 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8,
+		 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab,
+		 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xac, 0xad,
+		 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xae, 0xae,
+		 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0,
+		 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2,
+		 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3,
+		 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6,
+		 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb8,
+		 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9,
+		 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba, 0xbc, 0xbc,
+		 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+		 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf,
+		 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2,
+		 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3,
+		 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5,
+		 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+		 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc9, 0xc9, 0xc9,
+		 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xca, 0xca,
+		 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc, 0xcc,
+		 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xce,
+		 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf,
+		 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1,
+		 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3,
+		 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6,
+		 0xd6, 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7,
+		 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9,
+		 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb,
+		 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, 0xdd, 0xdd,
+		 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdf,
+		 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0,
+		 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2,
+		 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3,
+		 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5,
+		 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7,
+		 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8,
+		 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb,
+		 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed,
+		 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee,
+		 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0,
+		 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3,
+		 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4,
+		 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6,
+		 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8,
+		 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa,
+		 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+		 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02,
+		 0x02, 0x03, 0x05, 0x07, 0x07, 0x08, 0x09, 0x09,
+		 0x0a, 0x0c, 0x0c, 0x0d, 0x0e, 0x0e, 0x10, 0x11,
+		 0x11, 0x12, 0x14, 0x14, 0x15, 0x16, 0x16, 0x17,
+		 0x17, 0x18, 0x1a, 0x1a, 0x1b, 0x1b, 0x1c, 0x1e,
+		 0x1e, 0x1f, 0x1f, 0x20, 0x20, 0x22, 0x23, 0x23,
+		 0x25, 0x25, 0x26, 0x26, 0x27, 0x27, 0x28, 0x28,
+		 0x29, 0x29, 0x2b, 0x2c, 0x2c, 0x2d, 0x2d, 0x2f,
+		 0x2f, 0x30, 0x30, 0x31, 0x31, 0x33, 0x33, 0x34,
+		 0x34, 0x34, 0x35, 0x35, 0x37, 0x37, 0x38, 0x38,
+		 0x39, 0x39, 0x3a, 0x3a, 0x3b, 0x3b, 0x3b, 0x3c,
+		 0x3c, 0x3d, 0x3d, 0x3f, 0x3f, 0x40, 0x40, 0x40,
+		 0x42, 0x42, 0x43, 0x43, 0x44, 0x44, 0x44, 0x45,
+		 0x45, 0x47, 0x47, 0x47, 0x48, 0x48, 0x49, 0x49,
+		 0x4a, 0x4a, 0x4a, 0x4b, 0x4b, 0x4b, 0x4c, 0x4c,
+		 0x4d, 0x4d, 0x4d, 0x4f, 0x4f, 0x50, 0x50, 0x50,
+		 0x52, 0x52, 0x52, 0x53, 0x53, 0x54, 0x54, 0x54,
+		 0x55, 0x55, 0x55, 0x56, 0x56, 0x58, 0x58, 0x58,
+		 0x59, 0x59, 0x59, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b,
+		 0x5b, 0x5c, 0x5c, 0x5c, 0x5e, 0x5e, 0x5e, 0x5f,
+		 0x5f, 0x5f, 0x60, 0x60, 0x60, 0x61, 0x61, 0x61,
+		 0x62, 0x62, 0x62, 0x63, 0x63, 0x63, 0x65, 0x65,
+		 0x65, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67, 0x68,
+		 0x68, 0x68, 0x69, 0x69, 0x69, 0x69, 0x6a, 0x6a,
+		 0x6a, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6e,
+		 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70,
+		 0x70, 0x71, 0x71, 0x71, 0x71, 0x73, 0x73, 0x73,
+		 0x74, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x77,
+		 0x77, 0x77, 0x77, 0x78, 0x78, 0x78, 0x79, 0x79,
+		 0x79, 0x79, 0x7a, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b,
+		 0x7b, 0x7c, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d,
+		 0x7d, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80, 0x80,
+		 0x81, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x82,
+		 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85, 0x86,
+		 0x86, 0x86, 0x86, 0x88, 0x88, 0x88, 0x88, 0x89,
+		 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8a, 0x8b,
+		 0x8b, 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8d, 0x8e,
+		 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f, 0x8f, 0x90,
+		 0x90, 0x90, 0x90, 0x90, 0x91, 0x91, 0x91, 0x91,
+		 0x92, 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x93,
+		 0x94, 0x94, 0x94, 0x94, 0x96, 0x96, 0x96, 0x96,
+		 0x96, 0x97, 0x97, 0x97, 0x97, 0x98, 0x98, 0x98,
+		 0x98, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a, 0x9a,
+		 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c,
+		 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e,
+		 0x9e, 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa0,
+		 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2,
+		 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4,
+		 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa6,
+		 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8,
+		 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab,
+		 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xac, 0xad,
+		 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xae, 0xae,
+		 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0,
+		 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2,
+		 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3,
+		 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6,
+		 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb8,
+		 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9,
+		 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba, 0xbc, 0xbc,
+		 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+		 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf,
+		 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2,
+		 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3,
+		 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5,
+		 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+		 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc9, 0xc9, 0xc9,
+		 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xca, 0xca,
+		 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc, 0xcc,
+		 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xce,
+		 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf,
+		 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1,
+		 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3,
+		 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6,
+		 0xd6, 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7,
+		 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9,
+		 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb,
+		 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, 0xdd, 0xdd,
+		 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdf,
+		 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0,
+		 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2,
+		 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3,
+		 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5,
+		 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7,
+		 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8,
+		 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb,
+		 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed,
+		 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee,
+		 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0,
+		 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3,
+		 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4,
+		 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6,
+		 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8,
+		 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa,
+		 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+		 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02,
+		 0x02, 0x03, 0x05, 0x07, 0x07, 0x08, 0x09, 0x09,
+		 0x0a, 0x0c, 0x0c, 0x0d, 0x0e, 0x0e, 0x10, 0x11,
+		 0x11, 0x12, 0x14, 0x14, 0x15, 0x16, 0x16, 0x17,
+		 0x17, 0x18, 0x1a, 0x1a, 0x1b, 0x1b, 0x1c, 0x1e,
+		 0x1e, 0x1f, 0x1f, 0x20, 0x20, 0x22, 0x23, 0x23,
+		 0x25, 0x25, 0x26, 0x26, 0x27, 0x27, 0x28, 0x28,
+		 0x29, 0x29, 0x2b, 0x2c, 0x2c, 0x2d, 0x2d, 0x2f,
+		 0x2f, 0x30, 0x30, 0x31, 0x31, 0x33, 0x33, 0x34,
+		 0x34, 0x34, 0x35, 0x35, 0x37, 0x37, 0x38, 0x38,
+		 0x39, 0x39, 0x3a, 0x3a, 0x3b, 0x3b, 0x3b, 0x3c,
+		 0x3c, 0x3d, 0x3d, 0x3f, 0x3f, 0x40, 0x40, 0x40,
+		 0x42, 0x42, 0x43, 0x43, 0x44, 0x44, 0x44, 0x45,
+		 0x45, 0x47, 0x47, 0x47, 0x48, 0x48, 0x49, 0x49,
+		 0x4a, 0x4a, 0x4a, 0x4b, 0x4b, 0x4b, 0x4c, 0x4c,
+		 0x4d, 0x4d, 0x4d, 0x4f, 0x4f, 0x50, 0x50, 0x50,
+		 0x52, 0x52, 0x52, 0x53, 0x53, 0x54, 0x54, 0x54,
+		 0x55, 0x55, 0x55, 0x56, 0x56, 0x58, 0x58, 0x58,
+		 0x59, 0x59, 0x59, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b,
+		 0x5b, 0x5c, 0x5c, 0x5c, 0x5e, 0x5e, 0x5e, 0x5f,
+		 0x5f, 0x5f, 0x60, 0x60, 0x60, 0x61, 0x61, 0x61,
+		 0x62, 0x62, 0x62, 0x63, 0x63, 0x63, 0x65, 0x65,
+		 0x65, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67, 0x68,
+		 0x68, 0x68, 0x69, 0x69, 0x69, 0x69, 0x6a, 0x6a,
+		 0x6a, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6e,
+		 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70,
+		 0x70, 0x71, 0x71, 0x71, 0x71, 0x73, 0x73, 0x73,
+		 0x74, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x76,
+		 0x77, 0x77, 0x77, 0x78, 0x78, 0x78, 0x79, 0x79,
+		 0x79, 0x79, 0x7a, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b,
+		 0x7b, 0x7c, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d,
+		 0x7d, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80, 0x80,
+		 0x81, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x82,
+		 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85, 0x86,
+		 0x86, 0x86, 0x86, 0x88, 0x88, 0x88, 0x88, 0x89,
+		 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8a, 0x8b,
+		 0x8b, 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8d, 0x8e,
+		 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f, 0x8f, 0x90,
+		 0x90, 0x90, 0x90, 0x90, 0x91, 0x91, 0x91, 0x91,
+		 0x92, 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x93,
+		 0x94, 0x94, 0x94, 0x94, 0x96, 0x96, 0x96, 0x96,
+		 0x96, 0x97, 0x97, 0x97, 0x97, 0x98, 0x98, 0x98,
+		 0x98, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a, 0x9a,
+		 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c,
+		 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e,
+		 0x9e, 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa0,
+		 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2,
+		 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4,
+		 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa6,
+		 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8,
+		 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab,
+		 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xac, 0xad,
+		 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xae, 0xae,
+		 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0,
+		 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2,
+		 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3,
+		 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6,
+		 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb8,
+		 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9,
+		 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba, 0xbc, 0xbc,
+		 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+		 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf,
+		 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2,
+		 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3,
+		 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5,
+		 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+		 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc9, 0xc9, 0xc9,
+		 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xca, 0xca,
+		 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc, 0xcc,
+		 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xce,
+		 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf,
+		 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1,
+		 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3,
+		 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6,
+		 0xd6, 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7,
+		 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9,
+		 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb,
+		 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, 0xdd, 0xdd,
+		 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdf,
+		 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0,
+		 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2,
+		 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3,
+		 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5,
+		 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7,
+		 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8,
+		 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb,
+		 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed,
+		 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee,
+		 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0,
+		 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3,
+		 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4,
+		 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6,
+		 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8,
+		 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa,
+		 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb}
+	    },
+	    {				/* gamma 1 - from tp6810 + soi763a */
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		 0x01, 0x02, 0x03, 0x05, 0x07, 0x08, 0x09, 0x0a,
+		 0x0c, 0x0d, 0x0e, 0x10, 0x11, 0x12, 0x14, 0x15,
+		 0x16, 0x17, 0x18, 0x1a, 0x1a, 0x1b, 0x1c, 0x1e,
+		 0x1f, 0x20, 0x22, 0x22, 0x23, 0x25, 0x26, 0x27,
+		 0x27, 0x28, 0x29, 0x2b, 0x2b, 0x2c, 0x2d, 0x2f,
+		 0x2f, 0x30, 0x31, 0x33, 0x33, 0x34, 0x35, 0x35,
+		 0x37, 0x38, 0x38, 0x39, 0x3a, 0x3a, 0x3b, 0x3c,
+		 0x3c, 0x3d, 0x3f, 0x3f, 0x40, 0x42, 0x42, 0x43,
+		 0x43, 0x44, 0x45, 0x45, 0x47, 0x47, 0x48, 0x49,
+		 0x49, 0x4a, 0x4a, 0x4b, 0x4b, 0x4c, 0x4d, 0x4d,
+		 0x4f, 0x4f, 0x50, 0x50, 0x52, 0x52, 0x53, 0x53,
+		 0x54, 0x54, 0x55, 0x56, 0x56, 0x58, 0x58, 0x59,
+		 0x59, 0x5a, 0x5a, 0x5b, 0x5b, 0x5c, 0x5c, 0x5e,
+		 0x5e, 0x5e, 0x5f, 0x5f, 0x60, 0x60, 0x61, 0x61,
+		 0x62, 0x62, 0x63, 0x63, 0x65, 0x65, 0x65, 0x66,
+		 0x66, 0x67, 0x67, 0x68, 0x68, 0x69, 0x69, 0x69,
+		 0x6a, 0x6a, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6e,
+		 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70, 0x71, 0x71,
+		 0x73, 0x73, 0x73, 0x74, 0x74, 0x74, 0x75, 0x75,
+		 0x77, 0x77, 0x77, 0x78, 0x78, 0x79, 0x79, 0x79,
+		 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7c, 0x7c, 0x7c,
+		 0x7d, 0x7d, 0x7d, 0x7f, 0x7f, 0x80, 0x80, 0x80,
+		 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x84, 0x84,
+		 0x84, 0x85, 0x85, 0x85, 0x86, 0x86, 0x86, 0x88,
+		 0x88, 0x88, 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8a,
+		 0x8b, 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e,
+		 0x8e, 0x8f, 0x8f, 0x8f, 0x90, 0x90, 0x90, 0x91,
+		 0x91, 0x91, 0x92, 0x92, 0x92, 0x92, 0x93, 0x93,
+		 0x93, 0x94, 0x94, 0x94, 0x96, 0x96, 0x96, 0x97,
+		 0x97, 0x97, 0x97, 0x98, 0x98, 0x98, 0x99, 0x99,
+		 0x99, 0x9a, 0x9a, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b,
+		 0x9c, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9e,
+		 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1,
+		 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa2, 0xa3, 0xa3,
+		 0xa3, 0xa4, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5,
+		 0xa5, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8,
+		 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab, 0xab,
+		 0xac, 0xac, 0xac, 0xad, 0xad, 0xad, 0xad, 0xae,
+		 0xae, 0xae, 0xae, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0,
+		 0xb0, 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2,
+		 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3, 0xb4,
+		 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb6, 0xb7,
+		 0xb7, 0xb7, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb8,
+		 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xba,
+		 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd,
+		 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf,
+		 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0xc2,
+		 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3, 0xc4, 0xc4,
+		 0xc4, 0xc4, 0xc4, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6,
+		 0xc6, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7,
+		 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca,
+		 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc,
+		 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, 0xce,
+		 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf,
+		 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1,
+		 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd4,
+		 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6,
+		 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8,
+		 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xda,
+		 0xda, 0xda, 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdb,
+		 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde,
+		 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0,
+		 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe1,
+		 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3,
+		 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4,
+		 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6,
+		 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe8,
+		 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9, 0xe9,
+		 0xe9, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec,
+		 0xec, 0xec, 0xec, 0xed, 0xed, 0xed, 0xed, 0xed,
+		 0xee, 0xee, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef,
+		 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+		 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3, 0xf3, 0xf3,
+		 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5,
+		 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6,
+		 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8,
+		 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9,
+		 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+		 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
+		 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe,
+		 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03,
+		 0x05, 0x07, 0x07, 0x08, 0x09, 0x0a, 0x0c, 0x0d,
+		 0x0e, 0x10, 0x10, 0x11, 0x12, 0x14, 0x15, 0x15,
+		 0x16, 0x17, 0x18, 0x1a, 0x1a, 0x1b, 0x1c, 0x1e,
+		 0x1e, 0x1f, 0x20, 0x20, 0x22, 0x23, 0x25, 0x25,
+		 0x26, 0x27, 0x27, 0x28, 0x29, 0x29, 0x2b, 0x2c,
+		 0x2c, 0x2d, 0x2d, 0x2f, 0x30, 0x30, 0x31, 0x31,
+		 0x33, 0x34, 0x34, 0x35, 0x35, 0x37, 0x38, 0x38,
+		 0x39, 0x39, 0x3a, 0x3a, 0x3b, 0x3b, 0x3c, 0x3d,
+		 0x3d, 0x3f, 0x3f, 0x40, 0x40, 0x42, 0x42, 0x43,
+		 0x43, 0x44, 0x44, 0x45, 0x45, 0x47, 0x47, 0x48,
+		 0x48, 0x49, 0x49, 0x4a, 0x4a, 0x4b, 0x4b, 0x4c,
+		 0x4c, 0x4d, 0x4d, 0x4d, 0x4f, 0x4f, 0x50, 0x50,
+		 0x52, 0x52, 0x53, 0x53, 0x53, 0x54, 0x54, 0x55,
+		 0x55, 0x56, 0x56, 0x56, 0x58, 0x58, 0x59, 0x59,
+		 0x5a, 0x5a, 0x5a, 0x5b, 0x5b, 0x5c, 0x5c, 0x5c,
+		 0x5e, 0x5e, 0x5f, 0x5f, 0x5f, 0x60, 0x60, 0x60,
+		 0x61, 0x61, 0x62, 0x62, 0x62, 0x63, 0x63, 0x65,
+		 0x65, 0x65, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67,
+		 0x68, 0x68, 0x69, 0x69, 0x69, 0x6a, 0x6a, 0x6a,
+		 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6e, 0x6e,
+		 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70, 0x70, 0x71,
+		 0x71, 0x71, 0x73, 0x73, 0x73, 0x74, 0x74, 0x74,
+		 0x75, 0x75, 0x75, 0x77, 0x77, 0x77, 0x78, 0x78,
+		 0x78, 0x79, 0x79, 0x79, 0x79, 0x7a, 0x7a, 0x7a,
+		 0x7b, 0x7b, 0x7b, 0x7c, 0x7c, 0x7c, 0x7c, 0x7d,
+		 0x7d, 0x7d, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80,
+		 0x80, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x82,
+		 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85, 0x86,
+		 0x86, 0x86, 0x88, 0x88, 0x88, 0x88, 0x89, 0x89,
+		 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8b, 0x8b, 0x8b,
+		 0x8b, 0x8d, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e,
+		 0x8e, 0x8f, 0x8f, 0x8f, 0x90, 0x90, 0x90, 0x90,
+		 0x91, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92, 0x92,
+		 0x93, 0x93, 0x93, 0x93, 0x94, 0x94, 0x94, 0x94,
+		 0x96, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97, 0x97,
+		 0x98, 0x98, 0x98, 0x98, 0x99, 0x99, 0x99, 0x99,
+		 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b,
+		 0x9b, 0x9c, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d,
+		 0x9d, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0xa0, 0xa0,
+		 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2,
+		 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4,
+		 0xa4, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5,
+		 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8,
+		 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab,
+		 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xac, 0xad,
+		 0xad, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xae,
+		 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0,
+		 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2,
+		 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3,
+		 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6,
+		 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb8,
+		 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9,
+		 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba, 0xbc, 0xbc,
+		 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+		 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf,
+		 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
+		 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3,
+		 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5,
+		 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6,
+		 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc9,
+		 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca,
+		 0xca, 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc,
+		 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, 0xcd,
+		 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce, 0xce, 0xcf,
+		 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0,
+		 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1,
+		 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4,
+		 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6,
+		 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8,
+		 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9,
+		 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb, 0xdb,
+		 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, 0xdd, 0xdd,
+		 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdf,
+		 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0,
+		 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2,
+		 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3,
+		 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4,
+		 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6,
+		 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7,
+		 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9,
+		 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb,
+		 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed,
+		 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee,
+		 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0,
+		 0xf0, 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1,
+		 0xf1, 0xf1, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4,
+		 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5,
+		 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7,
+		 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8,
+		 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa,
+		 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+		 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc,
+		 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
+		 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe,
+		 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		 0x00, 0x00, 0x01, 0x02, 0x03, 0x05, 0x05, 0x07,
+		 0x08, 0x09, 0x0a, 0x0a, 0x0c, 0x0d, 0x0e, 0x0e,
+		 0x10, 0x11, 0x12, 0x12, 0x14, 0x15, 0x16, 0x16,
+		 0x17, 0x18, 0x18, 0x1a, 0x1b, 0x1b, 0x1c, 0x1e,
+		 0x1e, 0x1f, 0x1f, 0x20, 0x22, 0x22, 0x23, 0x23,
+		 0x25, 0x26, 0x26, 0x27, 0x27, 0x28, 0x29, 0x29,
+		 0x2b, 0x2b, 0x2c, 0x2c, 0x2d, 0x2d, 0x2f, 0x30,
+		 0x30, 0x31, 0x31, 0x33, 0x33, 0x34, 0x34, 0x35,
+		 0x35, 0x37, 0x37, 0x38, 0x38, 0x39, 0x39, 0x3a,
+		 0x3a, 0x3b, 0x3b, 0x3b, 0x3c, 0x3c, 0x3d, 0x3d,
+		 0x3f, 0x3f, 0x40, 0x40, 0x42, 0x42, 0x42, 0x43,
+		 0x43, 0x44, 0x44, 0x45, 0x45, 0x47, 0x47, 0x47,
+		 0x48, 0x48, 0x49, 0x49, 0x49, 0x4a, 0x4a, 0x4b,
+		 0x4b, 0x4b, 0x4c, 0x4c, 0x4d, 0x4d, 0x4d, 0x4f,
+		 0x4f, 0x50, 0x50, 0x50, 0x52, 0x52, 0x52, 0x53,
+		 0x53, 0x54, 0x54, 0x54, 0x55, 0x55, 0x55, 0x56,
+		 0x56, 0x58, 0x58, 0x58, 0x59, 0x59, 0x59, 0x5a,
+		 0x5a, 0x5a, 0x5b, 0x5b, 0x5b, 0x5c, 0x5c, 0x5c,
+		 0x5e, 0x5e, 0x5e, 0x5f, 0x5f, 0x5f, 0x60, 0x60,
+		 0x60, 0x61, 0x61, 0x61, 0x62, 0x62, 0x62, 0x63,
+		 0x63, 0x63, 0x65, 0x65, 0x65, 0x66, 0x66, 0x66,
+		 0x66, 0x67, 0x67, 0x67, 0x68, 0x68, 0x68, 0x69,
+		 0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6a, 0x6c, 0x6c,
+		 0x6c, 0x6d, 0x6d, 0x6d, 0x6d, 0x6e, 0x6e, 0x6e,
+		 0x6f, 0x6f, 0x6f, 0x6f, 0x70, 0x70, 0x70, 0x71,
+		 0x71, 0x71, 0x71, 0x73, 0x73, 0x73, 0x74, 0x74,
+		 0x74, 0x74, 0x75, 0x75, 0x75, 0x75, 0x77, 0x77,
+		 0x77, 0x78, 0x78, 0x78, 0x78, 0x79, 0x79, 0x79,
+		 0x79, 0x7a, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7b,
+		 0x7b, 0x7c, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d,
+		 0x7d, 0x7f, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80,
+		 0x80, 0x81, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82,
+		 0x82, 0x84, 0x84, 0x84, 0x84, 0x85, 0x85, 0x85,
+		 0x85, 0x86, 0x86, 0x86, 0x86, 0x88, 0x88, 0x88,
+		 0x88, 0x88, 0x89, 0x89, 0x89, 0x89, 0x8a, 0x8a,
+		 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8d,
+		 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+		 0x8f, 0x8f, 0x8f, 0x8f, 0x90, 0x90, 0x90, 0x90,
+		 0x90, 0x91, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92,
+		 0x92, 0x92, 0x93, 0x93, 0x93, 0x93, 0x94, 0x94,
+		 0x94, 0x94, 0x94, 0x96, 0x96, 0x96, 0x96, 0x96,
+		 0x97, 0x97, 0x97, 0x97, 0x97, 0x98, 0x98, 0x98,
+		 0x98, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a, 0x9a,
+		 0x9a, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+		 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d,
+		 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0xa0,
+		 0xa0, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa1,
+		 0xa1, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa3, 0xa3,
+		 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+		 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa6, 0xa6,
+		 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+		 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab,
+		 0xab, 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xac,
+		 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xae, 0xae,
+		 0xae, 0xae, 0xae, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+		 0xaf, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1,
+		 0xb1, 0xb1, 0xb1, 0xb1, 0xb2, 0xb2, 0xb2, 0xb2,
+		 0xb2, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb4,
+		 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb6,
+		 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
+		 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9,
+		 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba,
+		 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd,
+		 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe,
+		 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+		 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0xc2,
+		 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3,
+		 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5,
+		 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6,
+		 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7,
+		 0xc7, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca,
+		 0xca, 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb, 0xcb,
+		 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
+		 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce,
+		 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf,
+		 0xcf, 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0,
+		 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3,
+		 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4,
+		 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd7,
+		 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8,
+		 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9,
+		 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb,
+		 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, 0xdd, 0xdd,
+		 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde,
+		 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0,
+		 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe1,
+		 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3,
+		 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, 0xe4,
+		 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5,
+		 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7,
+		 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8,
+		 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb,
+		 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec,
+		 0xec, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xee,
+		 0xee, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef, 0xef,
+		 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf1,
+		 0xf1, 0xf1, 0xf1, 0xf1, 0xf3, 0xf3, 0xf3, 0xf3,
+		 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5,
+		 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6,
+		 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8,
+		 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa,
+		 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+		 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc,
+		 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd,
+		 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
+	    },
+	    {							/* gamma 2 */
+		{0x00, 0x01, 0x02, 0x05, 0x07, 0x08, 0x0a, 0x0c,
+		 0x0d, 0x0e, 0x10, 0x12, 0x14, 0x15, 0x16, 0x17,
+		 0x18, 0x1a, 0x1b, 0x1c, 0x1e, 0x1f, 0x20, 0x22,
+		 0x23, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2b, 0x2c,
+		 0x2d, 0x2d, 0x2f, 0x30, 0x31, 0x33, 0x34, 0x34,
+		 0x35, 0x37, 0x38, 0x38, 0x39, 0x3a, 0x3b, 0x3b,
+		 0x3c, 0x3d, 0x3f, 0x3f, 0x40, 0x42, 0x42, 0x43,
+		 0x44, 0x44, 0x45, 0x47, 0x47, 0x48, 0x49, 0x49,
+		 0x4a, 0x4b, 0x4b, 0x4c, 0x4c, 0x4d, 0x4f, 0x4f,
+		 0x50, 0x50, 0x52, 0x53, 0x53, 0x54, 0x54, 0x55,
+		 0x55, 0x56, 0x56, 0x58, 0x58, 0x59, 0x5a, 0x5a,
+		 0x5b, 0x5b, 0x5c, 0x5c, 0x5e, 0x5e, 0x5f, 0x5f,
+		 0x60, 0x60, 0x61, 0x61, 0x62, 0x62, 0x63, 0x63,
+		 0x65, 0x65, 0x65, 0x66, 0x66, 0x67, 0x67, 0x68,
+		 0x68, 0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6c, 0x6c,
+		 0x6d, 0x6d, 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x70,
+		 0x70, 0x70, 0x71, 0x71, 0x73, 0x73, 0x73, 0x74,
+		 0x74, 0x75, 0x75, 0x75, 0x77, 0x77, 0x78, 0x78,
+		 0x78, 0x79, 0x79, 0x79, 0x7a, 0x7a, 0x7b, 0x7b,
+		 0x7b, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7f, 0x7f,
+		 0x7f, 0x80, 0x80, 0x80, 0x81, 0x81, 0x81, 0x82,
+		 0x82, 0x82, 0x84, 0x84, 0x84, 0x85, 0x85, 0x85,
+		 0x86, 0x86, 0x86, 0x88, 0x88, 0x88, 0x89, 0x89,
+		 0x89, 0x8a, 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, 0x8d,
+		 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8f, 0x8f,
+		 0x8f, 0x90, 0x90, 0x90, 0x91, 0x91, 0x91, 0x91,
+		 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x93, 0x94,
+		 0x94, 0x94, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97,
+		 0x97, 0x98, 0x98, 0x98, 0x98, 0x99, 0x99, 0x99,
+		 0x9a, 0x9a, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b,
+		 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e,
+		 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa0, 0xa1,
+		 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa2, 0xa3,
+		 0xa3, 0xa3, 0xa4, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5,
+		 0xa5, 0xa5, 0xa5, 0xa6, 0xa6, 0xa6, 0xa6, 0xa8,
+		 0xa8, 0xa8, 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xab,
+		 0xab, 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xad,
+		 0xad, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xae,
+		 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb0,
+		 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2, 0xb2, 0xb2,
+		 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3, 0xb4, 0xb4,
+		 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb6, 0xb7,
+		 0xb7, 0xb7, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb8,
+		 0xb8, 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba,
+		 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd,
+		 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe,
+		 0xbe, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xc0, 0xc0,
+		 0xc0, 0xc0, 0xc0, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3,
+		 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4,
+		 0xc4, 0xc4, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6,
+		 0xc6, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7,
+		 0xc7, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca,
+		 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb,
+		 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd,
+		 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce, 0xce,
+		 0xce, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0,
+		 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, 0xd1,
+		 0xd1, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4,
+		 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6,
+		 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8,
+		 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9,
+		 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda,
+		 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd,
+		 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde,
+		 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0,
+		 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1,
+		 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2,
+		 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4, 0xe4,
+		 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe5,
+		 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6,
+		 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8,
+		 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9, 0xe9,
+		 0xe9, 0xe9, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb,
+		 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed, 0xed,
+		 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, 0xee,
+		 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0,
+		 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1,
+		 0xf1, 0xf1, 0xf1, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3,
+		 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5,
+		 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6,
+		 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
+		 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9,
+		 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa,
+		 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+		 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb},
+		{0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x05,
+		 0x07, 0x08, 0x09, 0x0a, 0x0d, 0x0e, 0x10, 0x11,
+		 0x12, 0x14, 0x15, 0x16, 0x16, 0x17, 0x18, 0x1a,
+		 0x1b, 0x1c, 0x1e, 0x1f, 0x20, 0x20, 0x22, 0x23,
+		 0x25, 0x26, 0x26, 0x27, 0x28, 0x29, 0x29, 0x2b,
+		 0x2c, 0x2d, 0x2d, 0x2f, 0x30, 0x30, 0x31, 0x33,
+		 0x33, 0x34, 0x35, 0x35, 0x37, 0x38, 0x38, 0x39,
+		 0x3a, 0x3a, 0x3b, 0x3b, 0x3c, 0x3d, 0x3d, 0x3f,
+		 0x3f, 0x40, 0x42, 0x42, 0x43, 0x43, 0x44, 0x44,
+		 0x45, 0x45, 0x47, 0x47, 0x48, 0x48, 0x49, 0x4a,
+		 0x4a, 0x4b, 0x4b, 0x4c, 0x4c, 0x4d, 0x4d, 0x4d,
+		 0x4f, 0x4f, 0x50, 0x50, 0x52, 0x52, 0x53, 0x53,
+		 0x54, 0x54, 0x55, 0x55, 0x56, 0x56, 0x56, 0x58,
+		 0x58, 0x59, 0x59, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b,
+		 0x5c, 0x5c, 0x5c, 0x5e, 0x5e, 0x5f, 0x5f, 0x5f,
+		 0x60, 0x60, 0x61, 0x61, 0x61, 0x62, 0x62, 0x63,
+		 0x63, 0x63, 0x65, 0x65, 0x65, 0x66, 0x66, 0x67,
+		 0x67, 0x67, 0x68, 0x68, 0x68, 0x69, 0x69, 0x69,
+		 0x6a, 0x6a, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d,
+		 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70,
+		 0x70, 0x71, 0x71, 0x71, 0x73, 0x73, 0x73, 0x73,
+		 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x77, 0x77,
+		 0x77, 0x78, 0x78, 0x78, 0x79, 0x79, 0x79, 0x79,
+		 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7b, 0x7b, 0x7c,
+		 0x7c, 0x7c, 0x7d, 0x7d, 0x7d, 0x7d, 0x7f, 0x7f,
+		 0x7f, 0x80, 0x80, 0x80, 0x80, 0x81, 0x81, 0x81,
+		 0x82, 0x82, 0x82, 0x82, 0x84, 0x84, 0x84, 0x84,
+		 0x85, 0x85, 0x85, 0x85, 0x86, 0x86, 0x86, 0x88,
+		 0x88, 0x88, 0x88, 0x89, 0x89, 0x89, 0x89, 0x8a,
+		 0x8a, 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, 0x8b, 0x8d,
+		 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8e, 0x8f,
+		 0x8f, 0x8f, 0x8f, 0x90, 0x90, 0x90, 0x90, 0x91,
+		 0x91, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92, 0x92,
+		 0x93, 0x93, 0x93, 0x93, 0x94, 0x94, 0x94, 0x94,
+		 0x94, 0x96, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97,
+		 0x97, 0x98, 0x98, 0x98, 0x98, 0x98, 0x99, 0x99,
+		 0x99, 0x99, 0x99, 0x9a, 0x9a, 0x9a, 0x9a, 0x9b,
+		 0x9b, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c, 0x9c,
+		 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e, 0x9e, 0x9e,
+		 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa1,
+		 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa2,
+		 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4,
+		 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+		 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8,
+		 0xa8, 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xab,
+		 0xab, 0xab, 0xab, 0xab, 0xac, 0xac, 0xac, 0xac,
+		 0xac, 0xac, 0xad, 0xad, 0xad, 0xad, 0xad, 0xae,
+		 0xae, 0xae, 0xae, 0xae, 0xaf, 0xaf, 0xaf, 0xaf,
+		 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb1,
+		 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2, 0xb2, 0xb2,
+		 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
+		 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6,
+		 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
+		 0xb7, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb9,
+		 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xba,
+		 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
+		 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe,
+		 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, 0xbf,
+		 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
+		 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3,
+		 0xc3, 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4,
+		 0xc4, 0xc4, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5,
+		 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7,
+		 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc9, 0xc9, 0xc9,
+		 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xca, 0xca,
+		 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc,
+		 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd,
+		 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce,
+		 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf,
+		 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1,
+		 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3,
+		 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4,
+		 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6,
+		 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8,
+		 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9,
+		 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda,
+		 0xda, 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb,
+		 0xdb, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+		 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdf, 0xdf,
+		 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0,
+		 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1,
+		 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2,
+		 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4,
+		 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5,
+		 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6,
+		 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7,
+		 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9,
+		 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb,
+		 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec,
+		 0xec, 0xec, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed,
+		 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xef,
+		 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0,
+		 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+		 0xf1, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4,
+		 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5,
+		 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6,
+		 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
+		 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9,
+		 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa,
+		 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb},
+		{0x00, 0x00, 0x00, 0x01, 0x02, 0x05, 0x07, 0x08,
+		 0x09, 0x0a, 0x0c, 0x0e, 0x10, 0x11, 0x12, 0x14,
+		 0x15, 0x16, 0x17, 0x18, 0x1a, 0x1b, 0x1c, 0x1e,
+		 0x1f, 0x20, 0x20, 0x22, 0x23, 0x25, 0x26, 0x27,
+		 0x28, 0x28, 0x29, 0x2b, 0x2c, 0x2d, 0x2d, 0x2f,
+		 0x30, 0x31, 0x31, 0x33, 0x34, 0x35, 0x35, 0x37,
+		 0x38, 0x38, 0x39, 0x3a, 0x3a, 0x3b, 0x3c, 0x3c,
+		 0x3d, 0x3f, 0x3f, 0x40, 0x40, 0x42, 0x43, 0x43,
+		 0x44, 0x44, 0x45, 0x47, 0x47, 0x48, 0x48, 0x49,
+		 0x4a, 0x4a, 0x4b, 0x4b, 0x4c, 0x4c, 0x4d, 0x4d,
+		 0x4f, 0x4f, 0x50, 0x50, 0x52, 0x52, 0x53, 0x53,
+		 0x54, 0x54, 0x55, 0x55, 0x56, 0x56, 0x58, 0x58,
+		 0x59, 0x59, 0x5a, 0x5a, 0x5b, 0x5b, 0x5c, 0x5c,
+		 0x5c, 0x5e, 0x5e, 0x5f, 0x5f, 0x60, 0x60, 0x61,
+		 0x61, 0x61, 0x62, 0x62, 0x63, 0x63, 0x65, 0x65,
+		 0x65, 0x66, 0x66, 0x67, 0x67, 0x67, 0x68, 0x68,
+		 0x69, 0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6c, 0x6c,
+		 0x6d, 0x6d, 0x6d, 0x6e, 0x6e, 0x6e, 0x6f, 0x6f,
+		 0x70, 0x70, 0x70, 0x71, 0x71, 0x71, 0x73, 0x73,
+		 0x73, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x77,
+		 0x77, 0x78, 0x78, 0x78, 0x79, 0x79, 0x79, 0x7a,
+		 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7b, 0x7c, 0x7c,
+		 0x7c, 0x7d, 0x7d, 0x7d, 0x7f, 0x7f, 0x7f, 0x80,
+		 0x80, 0x80, 0x81, 0x81, 0x81, 0x81, 0x82, 0x82,
+		 0x82, 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85,
+		 0x86, 0x86, 0x86, 0x88, 0x88, 0x88, 0x88, 0x89,
+		 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8a, 0x8b, 0x8b,
+		 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e,
+		 0x8e, 0x8f, 0x8f, 0x8f, 0x8f, 0x90, 0x90, 0x90,
+		 0x91, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92, 0x92,
+		 0x93, 0x93, 0x93, 0x93, 0x94, 0x94, 0x94, 0x94,
+		 0x96, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97, 0x97,
+		 0x98, 0x98, 0x98, 0x98, 0x99, 0x99, 0x99, 0x99,
+		 0x9a, 0x9a, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b,
+		 0x9b, 0x9c, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d,
+		 0x9d, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0xa0, 0xa0,
+		 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2,
+		 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4,
+		 0xa4, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5,
+		 0xa5, 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8,
+		 0xa8, 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xab,
+		 0xab, 0xab, 0xab, 0xab, 0xac, 0xac, 0xac, 0xac,
+		 0xad, 0xad, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae,
+		 0xae, 0xae, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0,
+		 0xb0, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1,
+		 0xb1, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3,
+		 0xb3, 0xb3, 0xb3, 0xb4, 0xb3, 0xb4, 0xb4, 0xb4,
+		 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7,
+		 0xb7, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
+		 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba,
+		 0xba, 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
+		 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe,
+		 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+		 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0xc2,
+		 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3,
+		 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5,
+		 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6,
+		 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7,
+		 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca,
+		 0xca, 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb, 0xcb,
+		 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
+		 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce,
+		 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf,
+		 0xcf, 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0,
+		 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3,
+		 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4,
+		 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6,
+		 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8,
+		 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0xd9,
+		 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda,
+		 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdd,
+		 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde,
+		 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf,
+		 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0,
+		 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2,
+		 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3,
+		 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4,
+		 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6,
+		 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7,
+		 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8,
+		 0xe8, 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9,
+		 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec,
+		 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed, 0xed,
+		 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, 0xee,
+		 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0,
+		 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf1, 0xf1,
+		 0xf1, 0xf1, 0xf1, 0xf1, 0xf3, 0xf3, 0xf3, 0xf3,
+		 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4,
+		 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6,
+		 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7,
+		 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8,
+		 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa,
+		 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+		 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb}
+	    },
+	    {				/* gamma 3 - from tp6810 + cx0342 */
+		{0x08, 0x09, 0x0c, 0x0d, 0x10, 0x11, 0x14, 0x15,
+		 0x17, 0x18, 0x1a, 0x1c, 0x1e, 0x1f, 0x20, 0x23,
+		 0x25, 0x26, 0x27, 0x28, 0x2b, 0x2c, 0x2d, 0x2f,
+		 0x30, 0x31, 0x33, 0x34, 0x35, 0x37, 0x38, 0x39,
+		 0x3a, 0x3b, 0x3c, 0x3d, 0x3f, 0x40, 0x42, 0x43,
+		 0x44, 0x45, 0x47, 0x48, 0x48, 0x49, 0x4a, 0x4b,
+		 0x4c, 0x4d, 0x4d, 0x4f, 0x50, 0x52, 0x53, 0x53,
+		 0x54, 0x55, 0x56, 0x56, 0x58, 0x59, 0x5a, 0x5a,
+		 0x5b, 0x5c, 0x5c, 0x5e, 0x5f, 0x5f, 0x60, 0x61,
+		 0x61, 0x62, 0x63, 0x63, 0x65, 0x66, 0x66, 0x67,
+		 0x68, 0x68, 0x69, 0x69, 0x6a, 0x6c, 0x6c, 0x6d,
+		 0x6d, 0x6e, 0x6f, 0x6f, 0x70, 0x70, 0x71, 0x73,
+		 0x73, 0x74, 0x74, 0x75, 0x75, 0x77, 0x77, 0x78,
+		 0x78, 0x79, 0x7a, 0x7a, 0x7b, 0x7b, 0x7c, 0x7c,
+		 0x7d, 0x7d, 0x7f, 0x7f, 0x80, 0x80, 0x81, 0x81,
+		 0x82, 0x82, 0x84, 0x84, 0x85, 0x85, 0x86, 0x86,
+		 0x86, 0x88, 0x88, 0x89, 0x89, 0x8a, 0x8a, 0x8b,
+		 0x8b, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8f, 0x8f,
+		 0x90, 0x90, 0x91, 0x91, 0x91, 0x92, 0x92, 0x93,
+		 0x93, 0x93, 0x94, 0x94, 0x96, 0x96, 0x97, 0x97,
+		 0x97, 0x98, 0x98, 0x99, 0x99, 0x99, 0x9a, 0x9a,
+		 0x9a, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d,
+		 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1,
+		 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4,
+		 0xa4, 0xa5, 0xa5, 0xa5, 0xa6, 0xa6, 0xa8, 0xa8,
+		 0xa8, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab, 0xac,
+		 0xac, 0xac, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae,
+		 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1,
+		 0xb1, 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3,
+		 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7,
+		 0xb7, 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9,
+		 0xba, 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd,
+		 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf,
+		 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0xc2, 0xc2,
+		 0xc3, 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc5,
+		 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc7,
+		 0xc7, 0xc7, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca,
+		 0xca, 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc,
+		 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce,
+		 0xce, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0,
+		 0xd0, 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3,
+		 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6,
+		 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8,
+		 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda, 0xda,
+		 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd,
+		 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde, 0xdf, 0xdf,
+		 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1,
+		 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2,
+		 0xe3, 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4,
+		 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6,
+		 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8,
+		 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb,
+		 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec,
+		 0xed, 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee,
+		 0xee, 0xee, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0,
+		 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+		 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4,
+		 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6,
+		 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
+		 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9,
+		 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+		 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
+		 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd,
+		 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+		{0x03, 0x05, 0x07, 0x09, 0x0a, 0x0c, 0x0d, 0x10,
+		 0x11, 0x12, 0x14, 0x15, 0x17, 0x18, 0x1a, 0x1b,
+		 0x1c, 0x1e, 0x1f, 0x20, 0x22, 0x23, 0x25, 0x26,
+		 0x27, 0x28, 0x29, 0x2b, 0x2c, 0x2c, 0x2d, 0x2f,
+		 0x30, 0x31, 0x33, 0x33, 0x34, 0x35, 0x37, 0x38,
+		 0x38, 0x39, 0x3a, 0x3b, 0x3b, 0x3c, 0x3d, 0x3f,
+		 0x3f, 0x40, 0x42, 0x42, 0x43, 0x44, 0x45, 0x45,
+		 0x47, 0x47, 0x48, 0x49, 0x49, 0x4a, 0x4b, 0x4b,
+		 0x4c, 0x4d, 0x4d, 0x4f, 0x4f, 0x50, 0x52, 0x52,
+		 0x53, 0x53, 0x54, 0x54, 0x55, 0x55, 0x56, 0x58,
+		 0x58, 0x59, 0x59, 0x5a, 0x5a, 0x5b, 0x5b, 0x5c,
+		 0x5c, 0x5e, 0x5e, 0x5f, 0x5f, 0x60, 0x60, 0x61,
+		 0x61, 0x62, 0x62, 0x63, 0x63, 0x65, 0x65, 0x66,
+		 0x66, 0x67, 0x67, 0x67, 0x68, 0x68, 0x69, 0x69,
+		 0x6a, 0x6a, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6e,
+		 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70, 0x71, 0x71,
+		 0x71, 0x73, 0x73, 0x74, 0x74, 0x74, 0x75, 0x75,
+		 0x77, 0x77, 0x77, 0x78, 0x78, 0x79, 0x79, 0x79,
+		 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7b, 0x7c, 0x7c,
+		 0x7d, 0x7d, 0x7d, 0x7f, 0x7f, 0x7f, 0x80, 0x80,
+		 0x80, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x84,
+		 0x84, 0x84, 0x85, 0x85, 0x85, 0x86, 0x86, 0x86,
+		 0x88, 0x88, 0x88, 0x89, 0x89, 0x89, 0x8a, 0x8a,
+		 0x8a, 0x8b, 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8e,
+		 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f, 0x90, 0x90,
+		 0x90, 0x91, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92,
+		 0x93, 0x93, 0x93, 0x93, 0x94, 0x94, 0x94, 0x96,
+		 0x96, 0x96, 0x96, 0x97, 0x97, 0x97, 0x98, 0x98,
+		 0x98, 0x98, 0x99, 0x99, 0x99, 0x9a, 0x9a, 0x9a,
+		 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c,
+		 0x9c, 0x9d, 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, 0x9e,
+		 0xa0, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa1,
+		 0xa2, 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa3,
+		 0xa4, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5,
+		 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8,
+		 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab,
+		 0xab, 0xac, 0xac, 0xac, 0xac, 0xad, 0xad, 0xad,
+		 0xad, 0xad, 0xae, 0xae, 0xae, 0xae, 0xaf, 0xaf,
+		 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb1,
+		 0xb1, 0xb1, 0xb1, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,
+		 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb4, 0xb4, 0xb4,
+		 0xb4, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7,
+		 0xb7, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb8, 0xb9,
+		 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xba,
+		 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd,
+		 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
+		 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0,
+		 0xc0, 0xc0, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3,
+		 0xc3, 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4,
+		 0xc4, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6,
+		 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7,
+		 0xc7, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca,
+		 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb,
+		 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd,
+		 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce,
+		 0xce, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0,
+		 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, 0xd1,
+		 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd4,
+		 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6,
+		 0xd6, 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7,
+		 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9,
+		 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda,
+		 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdd,
+		 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde,
+		 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf,
+		 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1,
+		 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, 0xe2,
+		 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3,
+		 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5,
+		 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6,
+		 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7,
+		 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9,
+		 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb,
+		 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec,
+		 0xec, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xee,
+		 0xee, 0xee, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef,
+		 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+		 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3,
+		 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4,
+		 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5,
+		 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6,
+		 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8,
+		 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9,
+		 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+		 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc,
+		 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
+		 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe,
+		 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+		{0x07, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14,
+		 0x16, 0x17, 0x18, 0x1b, 0x1c, 0x1e, 0x1f, 0x20,
+		 0x23, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2b, 0x2d,
+		 0x2f, 0x30, 0x31, 0x33, 0x34, 0x35, 0x37, 0x38,
+		 0x39, 0x3a, 0x3b, 0x3b, 0x3c, 0x3d, 0x3f, 0x40,
+		 0x42, 0x43, 0x44, 0x44, 0x45, 0x47, 0x48, 0x49,
+		 0x4a, 0x4a, 0x4b, 0x4c, 0x4d, 0x4d, 0x4f, 0x50,
+		 0x52, 0x52, 0x53, 0x54, 0x55, 0x55, 0x56, 0x58,
+		 0x58, 0x59, 0x5a, 0x5b, 0x5b, 0x5c, 0x5e, 0x5e,
+		 0x5f, 0x5f, 0x60, 0x61, 0x61, 0x62, 0x63, 0x63,
+		 0x65, 0x65, 0x66, 0x67, 0x67, 0x68, 0x68, 0x69,
+		 0x6a, 0x6a, 0x6c, 0x6c, 0x6d, 0x6d, 0x6e, 0x6e,
+		 0x6f, 0x70, 0x70, 0x71, 0x71, 0x73, 0x73, 0x74,
+		 0x74, 0x75, 0x75, 0x77, 0x77, 0x78, 0x78, 0x79,
+		 0x79, 0x7a, 0x7a, 0x7b, 0x7b, 0x7c, 0x7c, 0x7d,
+		 0x7d, 0x7f, 0x7f, 0x80, 0x80, 0x81, 0x81, 0x81,
+		 0x82, 0x82, 0x84, 0x84, 0x85, 0x85, 0x86, 0x86,
+		 0x88, 0x88, 0x88, 0x89, 0x89, 0x8a, 0x8a, 0x8b,
+		 0x8b, 0x8b, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8f,
+		 0x8f, 0x90, 0x90, 0x90, 0x91, 0x91, 0x92, 0x92,
+		 0x92, 0x93, 0x93, 0x94, 0x94, 0x94, 0x96, 0x96,
+		 0x96, 0x97, 0x97, 0x98, 0x98, 0x98, 0x99, 0x99,
+		 0x99, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c,
+		 0x9c, 0x9d, 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, 0xa0,
+		 0xa0, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa3,
+		 0xa3, 0xa3, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5,
+		 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8, 0xa9,
+		 0xa9, 0xa9, 0xab, 0xab, 0xab, 0xac, 0xac, 0xac,
+		 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xaf, 0xaf,
+		 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1, 0xb1,
+		 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb4,
+		 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7,
+		 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9,
+		 0xb9, 0xba, 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbc,
+		 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbf,
+		 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2,
+		 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3, 0xc4,
+		 0xc4, 0xc4, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6,
+		 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, 0xc9, 0xc9,
+		 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xca, 0xcb, 0xcb,
+		 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd,
+		 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce, 0xcf,
+		 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1,
+		 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3,
+		 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6,
+		 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8,
+		 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda,
+		 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdd,
+		 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde,
+		 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0,
+		 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2,
+		 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4,
+		 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe5,
+		 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7,
+		 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9,
+		 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb, 0xeb,
+		 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed,
+		 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, 0xee,
+		 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0,
+		 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3,
+		 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4,
+		 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6,
+		 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
+		 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9,
+		 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+		 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
+		 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd,
+		 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
+	    },
+	    {				/* gamma 4 - from tp6800 + soi763a */
+		{0x11, 0x14, 0x15, 0x17, 0x1a, 0x1b, 0x1e, 0x1f,
+		 0x22, 0x23, 0x25, 0x27, 0x28, 0x2b, 0x2c, 0x2d,
+		 0x2f, 0x31, 0x33, 0x34, 0x35, 0x38, 0x39, 0x3a,
+		 0x3b, 0x3c, 0x3d, 0x40, 0x42, 0x43, 0x44, 0x45,
+		 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4f,
+		 0x50, 0x52, 0x53, 0x53, 0x54, 0x55, 0x56, 0x58,
+		 0x59, 0x5a, 0x5b, 0x5b, 0x5c, 0x5e, 0x5f, 0x60,
+		 0x61, 0x61, 0x62, 0x63, 0x65, 0x65, 0x66, 0x67,
+		 0x68, 0x68, 0x69, 0x6a, 0x6c, 0x6c, 0x6d, 0x6e,
+		 0x6f, 0x6f, 0x70, 0x71, 0x71, 0x73, 0x74, 0x74,
+		 0x75, 0x77, 0x77, 0x78, 0x79, 0x79, 0x7a, 0x7a,
+		 0x7b, 0x7c, 0x7c, 0x7d, 0x7f, 0x7f, 0x80, 0x80,
+		 0x81, 0x81, 0x82, 0x84, 0x84, 0x85, 0x85, 0x86,
+		 0x86, 0x88, 0x89, 0x89, 0x8a, 0x8a, 0x8b, 0x8b,
+		 0x8d, 0x8d, 0x8e, 0x8e, 0x8f, 0x90, 0x90, 0x91,
+		 0x91, 0x92, 0x92, 0x93, 0x93, 0x94, 0x94, 0x96,
+		 0x96, 0x97, 0x97, 0x98, 0x98, 0x98, 0x99, 0x99,
+		 0x9a, 0x9a, 0x9b, 0x9b, 0x9c, 0x9c, 0x9d, 0x9d,
+		 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, 0xa2,
+		 0xa2, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, 0xa5, 0xa5,
+		 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa9, 0xa9, 0xab,
+		 0xab, 0xab, 0xac, 0xac, 0xad, 0xad, 0xad, 0xae,
+		 0xae, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb1, 0xb1,
+		 0xb1, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb4, 0xb4,
+		 0xb4, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, 0xb8, 0xb8,
+		 0xb8, 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xbc,
+		 0xbc, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbf,
+		 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc2, 0xc2, 0xc2,
+		 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5,
+		 0xc5, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc9,
+		 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb,
+		 0xcb, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xce,
+		 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0,
+		 0xd0, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3,
+		 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd7, 0xd7,
+		 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9,
+		 0xd9, 0xda, 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdb,
+		 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde, 0xdf,
+		 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1,
+		 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3,
+		 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5,
+		 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7,
+		 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9,
+		 0xe9, 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec,
+		 0xec, 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee,
+		 0xee, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0,
+		 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3, 0xf3, 0xf3,
+		 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5,
+		 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7,
+		 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9,
+		 0xf9, 0xf9, 0xfa, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa,
+		 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb},
+		{0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x11, 0x14, 0x15,
+		 0x16, 0x17, 0x1a, 0x1b, 0x1c, 0x1e, 0x1f, 0x20,
+		 0x23, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2b, 0x2c,
+		 0x2d, 0x2f, 0x30, 0x31, 0x33, 0x34, 0x34, 0x35,
+		 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3c, 0x3d,
+		 0x3f, 0x40, 0x42, 0x42, 0x43, 0x44, 0x45, 0x45,
+		 0x47, 0x48, 0x49, 0x49, 0x4a, 0x4b, 0x4b, 0x4c,
+		 0x4d, 0x4f, 0x4f, 0x50, 0x52, 0x52, 0x53, 0x54,
+		 0x54, 0x55, 0x55, 0x56, 0x58, 0x58, 0x59, 0x5a,
+		 0x5a, 0x5b, 0x5b, 0x5c, 0x5e, 0x5e, 0x5f, 0x5f,
+		 0x60, 0x60, 0x61, 0x61, 0x62, 0x63, 0x63, 0x65,
+		 0x65, 0x66, 0x66, 0x67, 0x67, 0x68, 0x68, 0x69,
+		 0x69, 0x6a, 0x6a, 0x6c, 0x6c, 0x6d, 0x6d, 0x6e,
+		 0x6e, 0x6f, 0x6f, 0x70, 0x70, 0x71, 0x71, 0x73,
+		 0x73, 0x74, 0x74, 0x74, 0x75, 0x75, 0x77, 0x77,
+		 0x78, 0x78, 0x79, 0x79, 0x79, 0x7a, 0x7a, 0x7b,
+		 0x7b, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7f, 0x7f,
+		 0x7f, 0x80, 0x80, 0x81, 0x81, 0x81, 0x82, 0x82,
+		 0x84, 0x84, 0x84, 0x85, 0x85, 0x86, 0x86, 0x86,
+		 0x88, 0x88, 0x88, 0x89, 0x89, 0x8a, 0x8a, 0x8a,
+		 0x8b, 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e,
+		 0x8e, 0x8f, 0x8f, 0x90, 0x90, 0x90, 0x91, 0x91,
+		 0x91, 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x94,
+		 0x94, 0x94, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97,
+		 0x98, 0x98, 0x98, 0x98, 0x99, 0x99, 0x99, 0x9a,
+		 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c,
+		 0x9c, 0x9d, 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, 0xa0,
+		 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2,
+		 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, 0xa4,
+		 0xa5, 0xa5, 0xa5, 0xa5, 0xa6, 0xa6, 0xa6, 0xa6,
+		 0xa8, 0xa8, 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xab,
+		 0xaa, 0xab, 0xab, 0xac, 0xac, 0xac, 0xad, 0xad,
+		 0xad, 0xad, 0xae, 0xae, 0xae, 0xae, 0xaf, 0xaf,
+		 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1,
+		 0xb1, 0xb1, 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3,
+		 0xb3, 0xb3, 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6,
+		 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb8, 0xb8,
+		 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba,
+		 0xba, 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd,
+		 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
+		 0xbf, 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0,
+		 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3,
+		 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5,
+		 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc7,
+		 0xc7, 0xc7, 0xc7, 0xc7, 0xc9, 0xc9, 0xc9, 0xc9,
+		 0xca, 0xca, 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb,
+		 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd,
+		 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce,
+		 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0,
+		 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd3,
+		 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4,
+		 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd7, 0xd7,
+		 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8,
+		 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda,
+		 0xda, 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb,
+		 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde,
+		 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf,
+		 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1,
+		 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2,
+		 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4, 0xe4,
+		 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5,
+		 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7,
+		 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8,
+		 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb,
+		 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec,
+		 0xec, 0xec, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed,
+		 0xee, 0xee, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef,
+		 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+		 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3,
+		 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4,
+		 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5,
+		 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7,
+		 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8,
+		 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa,
+		 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+		 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb},
+		{0x0d, 0x10, 0x11, 0x14, 0x15, 0x17, 0x18, 0x1b,
+		 0x1c, 0x1e, 0x20, 0x22, 0x23, 0x26, 0x27, 0x28,
+		 0x29, 0x2b, 0x2d, 0x2f, 0x30, 0x31, 0x33, 0x34,
+		 0x35, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d,
+		 0x3f, 0x40, 0x42, 0x43, 0x44, 0x45, 0x47, 0x48,
+		 0x49, 0x4a, 0x4b, 0x4b, 0x4c, 0x4d, 0x4f, 0x50,
+		 0x52, 0x52, 0x53, 0x54, 0x55, 0x56, 0x56, 0x58,
+		 0x59, 0x5a, 0x5a, 0x5b, 0x5c, 0x5e, 0x5e, 0x5f,
+		 0x60, 0x60, 0x61, 0x62, 0x62, 0x63, 0x65, 0x65,
+		 0x66, 0x67, 0x67, 0x68, 0x69, 0x69, 0x6a, 0x6c,
+		 0x6c, 0x6d, 0x6d, 0x6e, 0x6f, 0x6f, 0x70, 0x70,
+		 0x71, 0x73, 0x73, 0x74, 0x74, 0x75, 0x75, 0x77,
+		 0x78, 0x78, 0x79, 0x79, 0x7a, 0x7a, 0x7b, 0x7b,
+		 0x7c, 0x7c, 0x7d, 0x7d, 0x7f, 0x7f, 0x80, 0x80,
+		 0x81, 0x81, 0x82, 0x82, 0x84, 0x84, 0x85, 0x85,
+		 0x86, 0x86, 0x88, 0x88, 0x89, 0x89, 0x8a, 0x8a,
+		 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8f,
+		 0x8f, 0x90, 0x90, 0x91, 0x91, 0x91, 0x92, 0x92,
+		 0x93, 0x93, 0x94, 0x94, 0x94, 0x96, 0x96, 0x97,
+		 0x97, 0x98, 0x98, 0x98, 0x99, 0x99, 0x9a, 0x9a,
+		 0x9a, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d,
+		 0x9d, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1,
+		 0xa1, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4,
+		 0xa4, 0xa5, 0xa5, 0xa5, 0xa6, 0xa6, 0xa8, 0xa8,
+		 0xa8, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab, 0xac,
+		 0xac, 0xac, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae,
+		 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1,
+		 0xb1, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb4,
+		 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7,
+		 0xb7, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xba,
+		 0xba, 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd,
+		 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf,
+		 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0xc2, 0xc2, 0xc3,
+		 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5,
+		 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7,
+		 0xc7, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca,
+		 0xca, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, 0xcc,
+		 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce,
+		 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0, 0xd0,
+		 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3,
+		 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6,
+		 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, 0xd8,
+		 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda,
+		 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, 0xdd, 0xdd,
+		 0xdd, 0xde, 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf,
+		 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1,
+		 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3,
+		 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5,
+		 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6,
+		 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8,
+		 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb,
+		 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed,
+		 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee,
+		 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0,
+		 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3,
+		 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4,
+		 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6,
+		 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8,
+		 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9,
+		 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+		 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb}
+	    },
+	    {							/* gamma 5 */
+		{0x16, 0x18, 0x19, 0x1b, 0x1d, 0x1e, 0x20, 0x21,
+		 0x23, 0x24, 0x25, 0x27, 0x28, 0x2a, 0x2b, 0x2c,
+		 0x2d, 0x2f, 0x30, 0x31, 0x32, 0x34, 0x35, 0x36,
+		 0x37, 0x38, 0x39, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+		 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+		 0x48, 0x49, 0x4a, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e,
+		 0x4f, 0x50, 0x51, 0x51, 0x52, 0x53, 0x54, 0x55,
+		 0x56, 0x56, 0x57, 0x58, 0x59, 0x59, 0x5a, 0x5b,
+		 0x5c, 0x5c, 0x5d, 0x5e, 0x5f, 0x5f, 0x60, 0x61,
+		 0x62, 0x62, 0x63, 0x64, 0x64, 0x65, 0x66, 0x66,
+		 0x67, 0x68, 0x68, 0x69, 0x6a, 0x6a, 0x6b, 0x6b,
+		 0x6c, 0x6d, 0x6d, 0x6e, 0x6f, 0x6f, 0x70, 0x70,
+		 0x71, 0x71, 0x72, 0x73, 0x73, 0x74, 0x74, 0x75,
+		 0x75, 0x76, 0x77, 0x77, 0x78, 0x78, 0x79, 0x79,
+		 0x7a, 0x7a, 0x7b, 0x7b, 0x7c, 0x7d, 0x7d, 0x7e,
+		 0x7e, 0x7f, 0x7f, 0x80, 0x80, 0x81, 0x81, 0x82,
+		 0x82, 0x83, 0x83, 0x84, 0x84, 0x84, 0x85, 0x85,
+		 0x86, 0x86, 0x87, 0x87, 0x88, 0x88, 0x89, 0x89,
+		 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, 0x8c, 0x8c, 0x8d,
+		 0x8d, 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x90, 0x90,
+		 0x91, 0x91, 0x91, 0x92, 0x92, 0x93, 0x93, 0x94,
+		 0x94, 0x94, 0x95, 0x95, 0x96, 0x96, 0x96, 0x97,
+		 0x97, 0x98, 0x98, 0x98, 0x99, 0x99, 0x9a, 0x9a,
+		 0x9a, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, 0x9d, 0x9d,
+		 0x9d, 0x9e, 0x9e, 0x9e, 0x9f, 0x9f, 0xa0, 0xa0,
+		 0xa0, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa3,
+		 0xa3, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa6,
+		 0xa6, 0xa6, 0xa7, 0xa7, 0xa7, 0xa8, 0xa8, 0xa8,
+		 0xa9, 0xa9, 0xa9, 0xaa, 0xaa, 0xaa, 0xab, 0xab,
+		 0xab, 0xac, 0xac, 0xac, 0xad, 0xad, 0xad, 0xae,
+		 0xae, 0xae, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0,
+		 0xb0, 0xb1, 0xb1, 0xb1, 0xb2, 0xb2, 0xb2, 0xb3,
+		 0xb3, 0xb3, 0xb4, 0xb4, 0xb4, 0xb4, 0xb5, 0xb5,
+		 0xb5, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7,
+		 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xba, 0xba,
+		 0xba, 0xba, 0xbb, 0xbb, 0xbb, 0xbc, 0xbc, 0xbc,
+		 0xbc, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe,
+		 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc1,
+		 0xc1, 0xc1, 0xc1, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3,
+		 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5,
+		 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7,
+		 0xc7, 0xc8, 0xc8, 0xc8, 0xc8, 0xc9, 0xc9, 0xc9,
+		 0xc9, 0xca, 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb,
+		 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, 0xcd,
+		 0xcd, 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf,
+		 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1,
+		 0xd1, 0xd2, 0xd2, 0xd2, 0xd2, 0xd3, 0xd3, 0xd3,
+		 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd5, 0xd5, 0xd5,
+		 0xd5, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd7, 0xd7,
+		 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9,
+		 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb,
+		 0xdb, 0xdb, 0xdb, 0xdc, 0xdc, 0xdc, 0xdc, 0xdd,
+		 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde,
+		 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0,
+		 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2,
+		 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4,
+		 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5,
+		 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7,
+		 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9,
+		 0xe9, 0xe9, 0xea, 0xea, 0xea, 0xea, 0xea, 0xeb,
+		 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec,
+		 0xed, 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee,
+		 0xee, 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0,
+		 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+		 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf3, 0xf3, 0xf3,
+		 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5,
+		 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6,
+		 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8,
+		 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa,
+		 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd,
+		 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+		{0x0f, 0x11, 0x12, 0x14, 0x15, 0x16, 0x18, 0x19,
+		 0x1a, 0x1b, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22,
+		 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
+		 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x31, 0x32,
+		 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x38, 0x39,
+		 0x3a, 0x3b, 0x3c, 0x3c, 0x3d, 0x3e, 0x3f, 0x3f,
+		 0x40, 0x41, 0x42, 0x42, 0x43, 0x44, 0x44, 0x45,
+		 0x46, 0x47, 0x47, 0x48, 0x49, 0x49, 0x4a, 0x4b,
+		 0x4b, 0x4c, 0x4c, 0x4d, 0x4e, 0x4e, 0x4f, 0x50,
+		 0x50, 0x51, 0x51, 0x52, 0x53, 0x53, 0x54, 0x54,
+		 0x55, 0x55, 0x56, 0x56, 0x57, 0x58, 0x58, 0x59,
+		 0x59, 0x5a, 0x5a, 0x5b, 0x5b, 0x5c, 0x5c, 0x5d,
+		 0x5d, 0x5e, 0x5e, 0x5f, 0x5f, 0x60, 0x60, 0x61,
+		 0x61, 0x62, 0x62, 0x63, 0x63, 0x64, 0x64, 0x65,
+		 0x65, 0x66, 0x66, 0x66, 0x67, 0x67, 0x68, 0x68,
+		 0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6b, 0x6b, 0x6c,
+		 0x6c, 0x6d, 0x6d, 0x6d, 0x6e, 0x6e, 0x6f, 0x6f,
+		 0x6f, 0x70, 0x70, 0x71, 0x71, 0x71, 0x72, 0x72,
+		 0x73, 0x73, 0x73, 0x74, 0x74, 0x75, 0x75, 0x75,
+		 0x76, 0x76, 0x76, 0x77, 0x77, 0x78, 0x78, 0x78,
+		 0x79, 0x79, 0x79, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b,
+		 0x7b, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d, 0x7e, 0x7e,
+		 0x7e, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80, 0x81,
+		 0x81, 0x81, 0x82, 0x82, 0x82, 0x83, 0x83, 0x83,
+		 0x84, 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x86,
+		 0x86, 0x86, 0x87, 0x87, 0x87, 0x88, 0x88, 0x88,
+		 0x88, 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8b,
+		 0x8b, 0x8b, 0x8b, 0x8c, 0x8c, 0x8c, 0x8d, 0x8d,
+		 0x8d, 0x8e, 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f,
+		 0x90, 0x90, 0x90, 0x90, 0x91, 0x91, 0x91, 0x91,
+		 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x93, 0x94,
+		 0x94, 0x94, 0x94, 0x95, 0x95, 0x95, 0x96, 0x96,
+		 0x96, 0x96, 0x97, 0x97, 0x97, 0x97, 0x98, 0x98,
+		 0x98, 0x98, 0x99, 0x99, 0x99, 0x99, 0x9a, 0x9a,
+		 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c,
+		 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e, 0x9e,
+		 0x9e, 0x9e, 0x9f, 0x9f, 0x9f, 0x9f, 0xa0, 0xa0,
+		 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2,
+		 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4,
+		 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+		 0xa6, 0xa6, 0xa6, 0xa6, 0xa7, 0xa7, 0xa7, 0xa7,
+		 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa9, 0xa9, 0xa9,
+		 0xa9, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xab, 0xab,
+		 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xac, 0xad,
+		 0xad, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xae,
+		 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0,
+		 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2,
+		 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3,
+		 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb5, 0xb5, 0xb5,
+		 0xb5, 0xb5, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb7,
+		 0xb7, 0xb7, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb8,
+		 0xb8, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba,
+		 0xba, 0xba, 0xba, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+		 0xbb, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd,
+		 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
+		 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0,
+		 0xc0, 0xc0, 0xc0, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1,
+		 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3,
+		 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4,
+		 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6,
+		 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7,
+		 0xc7, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc9, 0xc9,
+		 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xca,
+		 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc,
+		 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xcd,
+		 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce,
+		 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0,
+		 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1,
+		 0xd1, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd3,
+		 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4,
+		 0xd4, 0xd4, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5,
+		 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd7, 0xd7,
+		 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8,
+		 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xda,
+		 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb, 0xdb, 0xdb,
+		 0xdb, 0xdb, 0xdb, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc,
+		 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde,
+		 0xde, 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf,
+		 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0,
+		 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2,
+		 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3,
+		 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5,
+		 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6,
+		 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7,
+		 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9,
+		 0xe9, 0xe9, 0xe9, 0xe9, 0xea, 0xea, 0xea, 0xea,
+		 0xea, 0xea, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb,
+		 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed,
+		 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, 0xee,
+		 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0,
+		 0xf0, 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1,
+		 0xf1, 0xf1, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
+		 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4,
+		 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5,
+		 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7,
+		 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8,
+		 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa,
+		 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd,
+		 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+		 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+		{0x13, 0x15, 0x16, 0x18, 0x19, 0x1b, 0x1c, 0x1e,
+		 0x1f, 0x20, 0x22, 0x23, 0x24, 0x26, 0x27, 0x28,
+		 0x29, 0x2a, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31,
+		 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
+		 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41,
+		 0x42, 0x43, 0x44, 0x44, 0x45, 0x46, 0x47, 0x48,
+		 0x49, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4d, 0x4e,
+		 0x4f, 0x50, 0x50, 0x51, 0x52, 0x53, 0x53, 0x54,
+		 0x55, 0x55, 0x56, 0x57, 0x57, 0x58, 0x59, 0x59,
+		 0x5a, 0x5b, 0x5b, 0x5c, 0x5d, 0x5d, 0x5e, 0x5f,
+		 0x5f, 0x60, 0x60, 0x61, 0x62, 0x62, 0x63, 0x63,
+		 0x64, 0x65, 0x65, 0x66, 0x66, 0x67, 0x67, 0x68,
+		 0x69, 0x69, 0x6a, 0x6a, 0x6b, 0x6b, 0x6c, 0x6c,
+		 0x6d, 0x6d, 0x6e, 0x6e, 0x6f, 0x6f, 0x70, 0x70,
+		 0x71, 0x71, 0x72, 0x72, 0x73, 0x73, 0x74, 0x74,
+		 0x75, 0x75, 0x76, 0x76, 0x77, 0x77, 0x78, 0x78,
+		 0x79, 0x79, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7c,
+		 0x7c, 0x7d, 0x7d, 0x7e, 0x7e, 0x7e, 0x7f, 0x7f,
+		 0x80, 0x80, 0x81, 0x81, 0x81, 0x82, 0x82, 0x83,
+		 0x83, 0x84, 0x84, 0x84, 0x85, 0x85, 0x86, 0x86,
+		 0x86, 0x87, 0x87, 0x88, 0x88, 0x88, 0x89, 0x89,
+		 0x89, 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, 0x8c, 0x8c,
+		 0x8c, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8f, 0x8f,
+		 0x8f, 0x90, 0x90, 0x90, 0x91, 0x91, 0x92, 0x92,
+		 0x92, 0x93, 0x93, 0x93, 0x94, 0x94, 0x94, 0x95,
+		 0x95, 0x95, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97,
+		 0x98, 0x98, 0x98, 0x99, 0x99, 0x99, 0x9a, 0x9a,
+		 0x9a, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c, 0x9d,
+		 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, 0x9e, 0x9f, 0x9f,
+		 0x9f, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa2,
+		 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4,
+		 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa6, 0xa6, 0xa6,
+		 0xa7, 0xa7, 0xa7, 0xa7, 0xa8, 0xa8, 0xa8, 0xa9,
+		 0xa9, 0xa9, 0xa9, 0xaa, 0xaa, 0xaa, 0xab, 0xab,
+		 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xad, 0xad,
+		 0xad, 0xae, 0xae, 0xae, 0xae, 0xaf, 0xaf, 0xaf,
+		 0xaf, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1,
+		 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3,
+		 0xb4, 0xb4, 0xb4, 0xb4, 0xb5, 0xb5, 0xb5, 0xb5,
+		 0xb6, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7,
+		 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9,
+		 0xba, 0xba, 0xba, 0xba, 0xbb, 0xbb, 0xbb, 0xbb,
+		 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd,
+		 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, 0xbf,
+		 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc1, 0xc1, 0xc1,
+		 0xc1, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3,
+		 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5,
+		 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc7,
+		 0xc7, 0xc7, 0xc7, 0xc7, 0xc8, 0xc8, 0xc8, 0xc8,
+		 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca,
+		 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc,
+		 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xce,
+		 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf,
+		 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1,
+		 0xd1, 0xd1, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd3,
+		 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4,
+		 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd6, 0xd6, 0xd6,
+		 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8,
+		 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0xd9,
+		 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb, 0xdb,
+		 0xdb, 0xdb, 0xdb, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc,
+		 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde,
+		 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0,
+		 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe1,
+		 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3,
+		 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4,
+		 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6,
+		 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7,
+		 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9,
+		 0xe9, 0xe9, 0xe9, 0xea, 0xea, 0xea, 0xea, 0xea,
+		 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec,
+		 0xec, 0xec, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed,
+		 0xee, 0xee, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef,
+		 0xef, 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+		 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf2, 0xf2, 0xf2,
+		 0xf2, 0xf2, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3,
+		 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5,
+		 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6,
+		 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8,
+		 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9,
+		 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb,
+		 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
+		 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe,
+		 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+	    },
+	};
+
+	reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x00);
+	if (sd->bridge == BRIDGE_TP6810)
+		reg_w(gspca_dev, 0x02, 0x28);
+/*	msleep(50); */
+	bulk_w(gspca_dev, 0x00, gamma_tb[gamma][0], 1024);
+	bulk_w(gspca_dev, 0x01, gamma_tb[gamma][1], 1024);
+	bulk_w(gspca_dev, 0x02, gamma_tb[gamma][2], 1024);
+	if (sd->bridge == BRIDGE_TP6810) {
+		int i;
+
+		reg_w(gspca_dev, 0x02, 0x2b);
+		reg_w(gspca_dev, 0x02, 0x28);
+		for (i = 0; i < 6; i++)
+			reg_w(gspca_dev, TP6800_R55_GAMMA_R,
+				gamma_tb[gamma][0][i]);
+		reg_w(gspca_dev, 0x02, 0x2b);
+		reg_w(gspca_dev, 0x02, 0x28);
+		for (i = 0; i < 6; i++)
+			reg_w(gspca_dev, TP6800_R56_GAMMA_G,
+				gamma_tb[gamma][1][i]);
+		reg_w(gspca_dev, 0x02, 0x2b);
+		reg_w(gspca_dev, 0x02, 0x28);
+		for (i = 0; i < 6; i++)
+			reg_w(gspca_dev, TP6800_R57_GAMMA_B,
+				gamma_tb[gamma][2][i]);
+		reg_w(gspca_dev, 0x02, 0x28);
+	}
+	reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x03);
+/*	msleep(50); */
+}
+
+static void setsharpness(struct gspca_dev *gspca_dev, s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (sd->bridge == BRIDGE_TP6800) {
+		val |= 0x08;		/* grid compensation enable */
+		if (gspca_dev->pixfmt.width == 640)
+			reg_w(gspca_dev, TP6800_R78_FORMAT, 0x00); /* vga */
+		else
+			val |= 0x04;		/* scaling down enable */
+		reg_w(gspca_dev, TP6800_R5D_DEMOSAIC_CFG, val);
+	} else {
+		val = (val << 5) | 0x08;
+		reg_w(gspca_dev, 0x59, val);
+	}
+}
+
+static void setautogain(struct gspca_dev *gspca_dev, s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	sd->ag_cnt = val ? AG_CNT_START : -1;
+}
+
+/* set the resolution for sensor cx0342 */
+static void set_resolution(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x00);
+	if (gspca_dev->pixfmt.width == 320) {
+		reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0x06);
+		msleep(100);
+		i2c_w(gspca_dev, CX0342_AUTO_ADC_CALIB, 0x01);
+		msleep(100);
+		reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x03);
+		reg_w(gspca_dev, TP6800_R78_FORMAT, 0x01);	/* qvga */
+		reg_w(gspca_dev, TP6800_R5D_DEMOSAIC_CFG, 0x0d);
+		i2c_w(gspca_dev, CX0342_EXPO_LINE_L, 0x37);
+		i2c_w(gspca_dev, CX0342_EXPO_LINE_H, 0x01);
+	} else {
+		reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0x05);
+		msleep(100);
+		i2c_w(gspca_dev, CX0342_AUTO_ADC_CALIB, 0x01);
+		msleep(100);
+		reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x03);
+		reg_w(gspca_dev, TP6800_R78_FORMAT, 0x00);	/* vga */
+		reg_w(gspca_dev, TP6800_R5D_DEMOSAIC_CFG, 0x09);
+		i2c_w(gspca_dev, CX0342_EXPO_LINE_L, 0xcf);
+		i2c_w(gspca_dev, CX0342_EXPO_LINE_H, 0x00);
+	}
+	i2c_w(gspca_dev, CX0342_SYS_CTRL_0, 0x01);
+	bulk_w(gspca_dev, 0x03, color_gain[SENSOR_CX0342],
+				ARRAY_SIZE(color_gain[0]));
+	setgamma(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma));
+	if (sd->sensor == SENSOR_SOI763A)
+		setquality(gspca_dev, v4l2_ctrl_g_ctrl(sd->jpegqual));
+}
+
+/* convert the frame rate to a tp68x0 value */
+static int get_fr_idx(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int i;
+
+	if (sd->bridge == BRIDGE_TP6800) {
+		for (i = 0; i < ARRAY_SIZE(rates) - 1; i++) {
+			if (sd->framerate >= rates[i])
+				break;
+		}
+		i = 6 - i;		/* 1 = 5fps .. 6 = 30fps */
+
+		/* 640x480 * 30 fps does not work */
+		if (i == 6			/* if 30 fps */
+		 && gspca_dev->pixfmt.width == 640)
+			i = 0x05;		/* 15 fps */
+	} else {
+		for (i = 0; i < ARRAY_SIZE(rates_6810) - 1; i++) {
+			if (sd->framerate >= rates_6810[i])
+				break;
+		}
+		i = 7 - i;		/* 3 = 5fps .. 7 = 30fps */
+
+		/* 640x480 * 30 fps does not work */
+		if (i == 7			/* if 30 fps */
+		 && gspca_dev->pixfmt.width == 640)
+			i = 6;			/* 15 fps */
+		i |= 0x80;			/* clock * 1 */
+	}
+	return i;
+}
+
+static void setframerate(struct gspca_dev *gspca_dev, s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u8 fr_idx;
+
+	fr_idx = get_fr_idx(gspca_dev);
+
+	if (sd->bridge == BRIDGE_TP6810) {
+		reg_r(gspca_dev, 0x7b);
+		reg_w(gspca_dev, 0x7b,
+			sd->sensor == SENSOR_CX0342 ? 0x10 : 0x90);
+		if (val >= 128)
+			fr_idx = 0xf0;		/* lower frame rate */
+	}
+
+	reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, fr_idx);
+
+	if (sd->sensor == SENSOR_CX0342)
+		i2c_w(gspca_dev, CX0342_AUTO_ADC_CALIB, 0x01);
+}
+
+static void setrgain(struct gspca_dev *gspca_dev, s32 rgain)
+{
+	i2c_w(gspca_dev, CX0342_RAW_RGAIN_H, rgain >> 8);
+	i2c_w(gspca_dev, CX0342_RAW_RGAIN_L, rgain);
+	i2c_w(gspca_dev, CX0342_SYS_CTRL_0, 0x80);
+}
+
+static int sd_setgain(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	s32 val = gspca_dev->gain->val;
+
+	if (sd->sensor == SENSOR_CX0342) {
+		s32 old = gspca_dev->gain->cur.val ?
+					gspca_dev->gain->cur.val : 1;
+
+		sd->blue->val = sd->blue->val * val / old;
+		if (sd->blue->val > 4095)
+			sd->blue->val = 4095;
+		sd->red->val = sd->red->val * val / old;
+		if (sd->red->val > 4095)
+			sd->red->val = 4095;
+	}
+	if (gspca_dev->streaming) {
+		if (sd->sensor == SENSOR_CX0342)
+			setexposure(gspca_dev, gspca_dev->exposure->val,
+					gspca_dev->gain->val,
+					sd->blue->val, sd->red->val);
+		else
+			setexposure(gspca_dev, gspca_dev->exposure->val,
+					gspca_dev->gain->val, 0, 0);
+	}
+	return gspca_dev->usb_err;
+}
+
+static void setbgain(struct gspca_dev *gspca_dev, s32 bgain)
+{
+	i2c_w(gspca_dev, CX0342_RAW_BGAIN_H, bgain >> 8);
+	i2c_w(gspca_dev, CX0342_RAW_BGAIN_L, bgain);
+	i2c_w(gspca_dev, CX0342_SYS_CTRL_0, 0x80);
+}
+
+/* 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;
+
+	sd->bridge = id->driver_info;
+
+	gspca_dev->cam.cam_mode = vga_mode;
+	gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode);
+	gspca_dev->cam.mode_framerates = sd->bridge == BRIDGE_TP6800 ?
+			framerates : framerates_6810;
+
+	sd->framerate = 30;		/* default: 30 fps */
+	return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	static const struct cmd tp6800_preinit[] = {
+		{TP6800_R10_SIF_TYPE, 0x01},	/* sif */
+		{TP6800_R11_SIF_CONTROL, 0x01},
+		{TP6800_R15_GPIO_PU, 0x9f},
+		{TP6800_R16_GPIO_PD, 0x9f},
+		{TP6800_R17_GPIO_IO, 0x80},
+		{TP6800_R18_GPIO_DATA, 0x40},	/* LED off */
+	};
+	static const struct cmd tp6810_preinit[] = {
+		{TP6800_R2F_TIMING_CFG, 0x2f},
+		{TP6800_R15_GPIO_PU, 0x6f},
+		{TP6800_R16_GPIO_PD, 0x40},
+		{TP6800_R17_GPIO_IO, 0x9f},
+		{TP6800_R18_GPIO_DATA, 0xc1},	/* LED off */
+	};
+
+	if (sd->bridge == BRIDGE_TP6800)
+		reg_w_buf(gspca_dev, tp6800_preinit,
+				ARRAY_SIZE(tp6800_preinit));
+	else
+		reg_w_buf(gspca_dev, tp6810_preinit,
+				ARRAY_SIZE(tp6810_preinit));
+	msleep(15);
+	reg_r(gspca_dev, TP6800_R18_GPIO_DATA);
+	PDEBUG(D_PROBE, "gpio: %02x", gspca_dev->usb_buf[0]);
+/* values:
+ *	0x80: snapshot button
+ *	0x40: LED
+ *	0x20: (bridge / sensor) reset for tp6810 ?
+ *	0x07: sensor type ?
+ */
+
+	/* guess the sensor type */
+	if (force_sensor >= 0) {
+		sd->sensor = force_sensor;
+	} else {
+		if (sd->bridge == BRIDGE_TP6800) {
+/*fixme: not sure this is working*/
+			switch (gspca_dev->usb_buf[0] & 0x07) {
+			case 0:
+				sd->sensor = SENSOR_SOI763A;
+				break;
+			case 1:
+				sd->sensor = SENSOR_CX0342;
+				break;
+			}
+		} else {
+			int sensor;
+
+			sensor = probe_6810(gspca_dev);
+			if (sensor < 0) {
+				pr_warn("Unknown sensor %d - forced to soi763a\n",
+					-sensor);
+				sensor = SENSOR_SOI763A;
+			}
+			sd->sensor = sensor;
+		}
+	}
+	if (sd->sensor == SENSOR_SOI763A) {
+		pr_info("Sensor soi763a\n");
+		if (sd->bridge == BRIDGE_TP6810) {
+			soi763a_6810_init(gspca_dev);
+		}
+	} else {
+		pr_info("Sensor cx0342\n");
+		if (sd->bridge == BRIDGE_TP6810) {
+			cx0342_6810_init(gspca_dev);
+		}
+	}
+
+	set_dqt(gspca_dev, 0);
+	return 0;
+}
+
+/* This function is called before choosing the alt setting */
+static int sd_isoc_init(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	static const struct cmd cx_sensor_init[] = {
+		{CX0342_AUTO_ADC_CALIB, 0x81},
+		{CX0342_EXPO_LINE_L, 0x37},
+		{CX0342_EXPO_LINE_H, 0x01},
+		{CX0342_RAW_GRGAIN_L, 0x00},
+		{CX0342_RAW_GBGAIN_L, 0x00},
+		{CX0342_RAW_RGAIN_L, 0x00},
+		{CX0342_RAW_BGAIN_L, 0x00},
+		{CX0342_SYS_CTRL_0, 0x81},
+	};
+	static const struct cmd cx_bridge_init[] = {
+		{0x4d, 0x00},
+		{0x4c, 0xff},
+		{0x4e, 0xff},
+		{0x4f, 0x00},
+	};
+	static const struct cmd ov_sensor_init[] = {
+		{0x10, 0x75},		/* exposure */
+		{0x76, 0x03},
+		{0x00, 0x00},		/* gain */
+	};
+	static const struct cmd ov_bridge_init[] = {
+		{0x7b, 0x90},
+		{TP6800_R3F_FRAME_RATE, 0x87},
+	};
+
+	if (sd->bridge == BRIDGE_TP6800)
+		return 0;
+	if (sd->sensor == SENSOR_CX0342) {
+		reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x20);
+		reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0x87);
+		i2c_w_buf(gspca_dev, cx_sensor_init,
+				ARRAY_SIZE(cx_sensor_init));
+		reg_w_buf(gspca_dev, cx_bridge_init,
+				ARRAY_SIZE(cx_bridge_init));
+		bulk_w(gspca_dev, 0x03, color_null, sizeof color_null);
+		reg_w(gspca_dev, 0x59, 0x40);
+	} else {
+		reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x21);
+		i2c_w_buf(gspca_dev, ov_sensor_init,
+				ARRAY_SIZE(ov_sensor_init));
+		reg_r(gspca_dev, 0x7b);
+		reg_w_buf(gspca_dev, ov_bridge_init,
+				ARRAY_SIZE(ov_bridge_init));
+	}
+	reg_w(gspca_dev, TP6800_R78_FORMAT,
+			gspca_dev->curr_mode ? 0x00 : 0x01);
+	return gspca_dev->usb_err;
+}
+
+static void set_led(struct gspca_dev *gspca_dev, int on)
+{
+	u8 data;
+
+	reg_r(gspca_dev, TP6800_R18_GPIO_DATA);
+	data = gspca_dev->usb_buf[0];
+	if (on)
+		data &= ~0x40;
+	else
+		data |= 0x40;
+	reg_w(gspca_dev, TP6800_R18_GPIO_DATA, data);
+}
+
+static void cx0342_6800_start(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	static const struct cmd reg_init[] = {
+		/* fixme: is this useful? */
+		{TP6800_R17_GPIO_IO, 0x9f},
+		{TP6800_R16_GPIO_PD, 0x40},
+		{TP6800_R10_SIF_TYPE, 0x00},	/* i2c 8 bits */
+		{TP6800_R50, 0x00},
+		{TP6800_R51, 0x00},
+		{TP6800_R52, 0xff},
+		{TP6800_R53, 0x03},
+		{TP6800_R54_DARK_CFG, 0x07},
+		{TP6800_R5C_EDGE_THRLD, 0x40},
+		{TP6800_R7A_BLK_THRLD, 0x40},
+		{TP6800_R2F_TIMING_CFG, 0x17},
+		{TP6800_R30_SENSOR_CFG, 0x18},	/* G1B..RG0 */
+		{TP6800_R37_FRONT_DARK_ST, 0x00},
+		{TP6800_R38_FRONT_DARK_END, 0x00},
+		{TP6800_R39_REAR_DARK_ST_L, 0x00},
+		{TP6800_R3A_REAR_DARK_ST_H, 0x00},
+		{TP6800_R3B_REAR_DARK_END_L, 0x00},
+		{TP6800_R3C_REAR_DARK_END_H, 0x00},
+		{TP6800_R3D_HORIZ_DARK_LINE_L, 0x00},
+		{TP6800_R3E_HORIZ_DARK_LINE_H, 0x00},
+		{TP6800_R21_ENDP_1_CTL, 0x03},
+
+		{TP6800_R31_PIXEL_START, 0x0b},
+		{TP6800_R32_PIXEL_END_L, 0x8a},
+		{TP6800_R33_PIXEL_END_H, 0x02},
+		{TP6800_R34_LINE_START, 0x0e},
+		{TP6800_R35_LINE_END_L, 0xf4},
+		{TP6800_R36_LINE_END_H, 0x01},
+		{TP6800_R78_FORMAT, 0x00},
+		{TP6800_R12_SIF_ADDR_S, 0x20},	/* cx0342 i2c addr */
+	};
+	static const struct cmd sensor_init[] = {
+		{CX0342_OUTPUT_CTRL, 0x07},
+		{CX0342_BYPASS_MODE, 0x58},
+		{CX0342_GPXLTHD_L, 0x16},
+		{CX0342_RBPXLTHD_L, 0x16},
+		{CX0342_PLANETHD_L, 0xc0},
+		{CX0342_PLANETHD_H, 0x03},
+		{CX0342_RB_GAP_L, 0xff},
+		{CX0342_RB_GAP_H, 0x07},
+		{CX0342_G_GAP_L, 0xff},
+		{CX0342_G_GAP_H, 0x07},
+		{CX0342_RST_OVERFLOW_L, 0x5c},
+		{CX0342_RST_OVERFLOW_H, 0x01},
+		{CX0342_DATA_OVERFLOW_L, 0xfc},
+		{CX0342_DATA_OVERFLOW_H, 0x03},
+		{CX0342_DATA_UNDERFLOW_L, 0x00},
+		{CX0342_DATA_UNDERFLOW_H, 0x00},
+		{CX0342_SYS_CTRL_0, 0x40},
+		{CX0342_GLOBAL_GAIN, 0x01},
+		{CX0342_CLOCK_GEN, 0x00},
+		{CX0342_SYS_CTRL_0, 0x02},
+		{CX0342_IDLE_CTRL, 0x05},
+		{CX0342_ADCGN, 0x00},
+		{CX0342_ADC_CTL, 0x00},
+		{CX0342_LVRST_BLBIAS, 0x01},
+		{CX0342_VTHSEL, 0x0b},
+		{CX0342_RAMP_RIV, 0x0b},
+		{CX0342_LDOSEL, 0x07},
+		{CX0342_SPV_VALUE_L, 0x40},
+		{CX0342_SPV_VALUE_H, 0x02},
+	};
+
+	reg_w_buf(gspca_dev, reg_init, ARRAY_SIZE(reg_init));
+	i2c_w_buf(gspca_dev, sensor_init, ARRAY_SIZE(sensor_init));
+	i2c_w_buf(gspca_dev, cx0342_timing_seq, ARRAY_SIZE(cx0342_timing_seq));
+	reg_w(gspca_dev, TP6800_R5C_EDGE_THRLD, 0x10);
+	reg_w(gspca_dev, TP6800_R54_DARK_CFG, 0x00);
+	i2c_w(gspca_dev, CX0342_EXPO_LINE_H, 0x00);
+	i2c_w(gspca_dev, CX0342_SYS_CTRL_0, 0x01);
+	if (sd->sensor == SENSOR_CX0342)
+		setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
+			v4l2_ctrl_g_ctrl(gspca_dev->gain),
+			v4l2_ctrl_g_ctrl(sd->blue),
+			v4l2_ctrl_g_ctrl(sd->red));
+	else
+		setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
+			v4l2_ctrl_g_ctrl(gspca_dev->gain), 0, 0);
+	set_led(gspca_dev, 1);
+	set_resolution(gspca_dev);
+}
+
+static void cx0342_6810_start(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	static const struct cmd sensor_init_2[] = {
+		{CX0342_EXPO_LINE_L, 0x6f},
+		{CX0342_EXPO_LINE_H, 0x02},
+		{CX0342_RAW_GRGAIN_L, 0x00},
+		{CX0342_RAW_GBGAIN_L, 0x00},
+		{CX0342_RAW_RGAIN_L, 0x00},
+		{CX0342_RAW_BGAIN_L, 0x00},
+		{CX0342_SYS_CTRL_0, 0x81},
+	};
+	static const struct cmd bridge_init_2[] = {
+		{0x4d, 0x00},
+		{0x4c, 0xff},
+		{0x4e, 0xff},
+		{0x4f, 0x00},
+		{TP6800_R7A_BLK_THRLD, 0x00},
+		{TP6800_R79_QUALITY, 0x04},
+		{TP6800_R79_QUALITY, 0x01},
+	};
+	static const struct cmd bridge_init_3[] = {
+		{TP6800_R31_PIXEL_START, 0x08},
+		{TP6800_R32_PIXEL_END_L, 0x87},
+		{TP6800_R33_PIXEL_END_H, 0x02},
+		{TP6800_R34_LINE_START, 0x0e},
+		{TP6800_R35_LINE_END_L, 0xf4},
+		{TP6800_R36_LINE_END_H, 0x01},
+	};
+	static const struct cmd sensor_init_3[] = {
+		{CX0342_AUTO_ADC_CALIB, 0x81},
+		{CX0342_EXPO_LINE_L, 0x6f},
+		{CX0342_EXPO_LINE_H, 0x02},
+		{CX0342_RAW_GRGAIN_L, 0x00},
+		{CX0342_RAW_GBGAIN_L, 0x00},
+		{CX0342_RAW_RGAIN_L, 0x00},
+		{CX0342_RAW_BGAIN_L, 0x00},
+		{CX0342_SYS_CTRL_0, 0x81},
+	};
+	static const struct cmd bridge_init_5[] = {
+		{0x4d, 0x00},
+		{0x4c, 0xff},
+		{0x4e, 0xff},
+		{0x4f, 0x00},
+	};
+	static const struct cmd sensor_init_4[] = {
+		{CX0342_EXPO_LINE_L, 0xd3},
+		{CX0342_EXPO_LINE_H, 0x01},
+/*fixme: gains, but 00..80 only*/
+		{CX0342_RAW_GRGAIN_L, 0x40},
+		{CX0342_RAW_GBGAIN_L, 0x40},
+		{CX0342_RAW_RGAIN_L, 0x40},
+		{CX0342_RAW_BGAIN_L, 0x40},
+		{CX0342_SYS_CTRL_0, 0x81},
+	};
+	static const struct cmd sensor_init_5[] = {
+		{CX0342_IDLE_CTRL, 0x05},
+		{CX0342_ADCGN, 0x00},
+		{CX0342_ADC_CTL, 0x00},
+		{CX0342_LVRST_BLBIAS, 0x01},
+		{CX0342_VTHSEL, 0x0b},
+		{CX0342_RAMP_RIV, 0x0b},
+		{CX0342_LDOSEL, 0x07},
+		{CX0342_SPV_VALUE_L, 0x40},
+		{CX0342_SPV_VALUE_H, 0x02},
+		{CX0342_AUTO_ADC_CALIB, 0x81},
+	};
+
+	reg_w(gspca_dev, 0x22, gspca_dev->alt);
+	i2c_w_buf(gspca_dev, sensor_init_2, ARRAY_SIZE(sensor_init_2));
+	reg_w_buf(gspca_dev, bridge_init_2, ARRAY_SIZE(bridge_init_2));
+	reg_w_buf(gspca_dev, tp6810_cx_init_common,
+			ARRAY_SIZE(tp6810_cx_init_common));
+	reg_w_buf(gspca_dev, bridge_init_3, ARRAY_SIZE(bridge_init_3));
+	if (gspca_dev->curr_mode) {
+		reg_w(gspca_dev, 0x4a, 0x7f);
+		reg_w(gspca_dev, 0x07, 0x05);
+		reg_w(gspca_dev, TP6800_R78_FORMAT, 0x00);	/* vga */
+	} else {
+		reg_w(gspca_dev, 0x4a, 0xff);
+		reg_w(gspca_dev, 0x07, 0x85);
+		reg_w(gspca_dev, TP6800_R78_FORMAT, 0x01);	/* qvga */
+	}
+	setgamma(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma));
+	reg_w_buf(gspca_dev, tp6810_bridge_start,
+			ARRAY_SIZE(tp6810_bridge_start));
+	setsharpness(gspca_dev, v4l2_ctrl_g_ctrl(sd->sharpness));
+	bulk_w(gspca_dev, 0x03, color_gain[SENSOR_CX0342],
+				ARRAY_SIZE(color_gain[0]));
+	reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0x87);
+	i2c_w_buf(gspca_dev, sensor_init_3, ARRAY_SIZE(sensor_init_3));
+	reg_w_buf(gspca_dev, bridge_init_5, ARRAY_SIZE(bridge_init_5));
+	i2c_w_buf(gspca_dev, sensor_init_4, ARRAY_SIZE(sensor_init_4));
+	reg_w_buf(gspca_dev, bridge_init_5, ARRAY_SIZE(bridge_init_5));
+	i2c_w_buf(gspca_dev, sensor_init_5, ARRAY_SIZE(sensor_init_5));
+
+	set_led(gspca_dev, 1);
+/*	setquality(gspca_dev, v4l2_ctrl_g_ctrl(sd->jpegqual)); */
+}
+
+static void soi763a_6800_start(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	static const struct cmd reg_init[] = {
+		{TP6800_R79_QUALITY, 0x04},
+		{TP6800_R79_QUALITY, 0x01},
+		{TP6800_R10_SIF_TYPE, 0x00},	/* i2c 8 bits */
+
+		{TP6800_R50, 0x00},
+		{TP6800_R51, 0x00},
+		{TP6800_R52, 0xff},
+		{TP6800_R53, 0x03},
+		{TP6800_R54_DARK_CFG, 0x07},
+		{TP6800_R5C_EDGE_THRLD, 0x40},
+
+		{TP6800_R79_QUALITY, 0x03},
+		{TP6800_R7A_BLK_THRLD, 0x40},
+
+		{TP6800_R2F_TIMING_CFG, 0x46},
+		{TP6800_R30_SENSOR_CFG, 0x10},	/* BG1..G0R */
+		{TP6800_R37_FRONT_DARK_ST, 0x00},
+		{TP6800_R38_FRONT_DARK_END, 0x00},
+		{TP6800_R39_REAR_DARK_ST_L, 0x00},
+		{TP6800_R3A_REAR_DARK_ST_H, 0x00},
+		{TP6800_R3B_REAR_DARK_END_L, 0x00},
+		{TP6800_R3C_REAR_DARK_END_H, 0x00},
+		{TP6800_R3D_HORIZ_DARK_LINE_L, 0x00},
+		{TP6800_R3E_HORIZ_DARK_LINE_H, 0x00},
+		{TP6800_R21_ENDP_1_CTL, 0x03},
+
+		{TP6800_R3F_FRAME_RATE, 0x04},	/* 15 fps */
+		{TP6800_R5D_DEMOSAIC_CFG, 0x0e}, /* scale down - medium edge */
+
+		{TP6800_R31_PIXEL_START, 0x1b},
+		{TP6800_R32_PIXEL_END_L, 0x9a},
+		{TP6800_R33_PIXEL_END_H, 0x02},
+		{TP6800_R34_LINE_START, 0x0f},
+		{TP6800_R35_LINE_END_L, 0xf4},
+		{TP6800_R36_LINE_END_H, 0x01},
+		{TP6800_R78_FORMAT, 0x01},	/* qvga */
+		{TP6800_R12_SIF_ADDR_S, 0x21},	/* soi763a i2c addr */
+		{TP6800_R1A_SIF_TX_DATA2, 0x00},
+	};
+	static const struct cmd sensor_init[] = {
+		{0x12, 0x48},		/* mirror - RGB */
+		{0x13, 0xa0},		/* clock - no AGC nor AEC */
+		{0x03, 0xa4},		/* saturation */
+		{0x04, 0x30},		/* hue */
+		{0x05, 0x88},		/* contrast */
+		{0x06, 0x60},		/* brightness */
+		{0x10, 0x41},		/* AEC */
+		{0x11, 0x40},		/* clock rate */
+		{0x13, 0xa0},
+		{0x14, 0x00},		/* 640x480 */
+		{0x15, 0x14},
+		{0x1f, 0x41},
+		{0x20, 0x80},
+		{0x23, 0xee},
+		{0x24, 0x50},
+		{0x25, 0x7a},
+		{0x26, 0x00},
+		{0x27, 0xe2},
+		{0x28, 0xb0},
+		{0x2a, 0x00},
+		{0x2b, 0x00},
+		{0x2d, 0x81},
+		{0x2f, 0x9d},
+		{0x60, 0x80},
+		{0x61, 0x00},
+		{0x62, 0x88},
+		{0x63, 0x11},
+		{0x64, 0x89},
+		{0x65, 0x00},
+		{0x67, 0x94},
+		{0x68, 0x7a},
+		{0x69, 0x0f},
+		{0x6c, 0x80},
+		{0x6d, 0x80},
+		{0x6e, 0x80},
+		{0x6f, 0xff},
+		{0x71, 0x20},
+		{0x74, 0x20},
+		{0x75, 0x86},
+		{0x77, 0xb5},
+		{0x17, 0x18},		/* H href start */
+		{0x18, 0xbf},		/* H href end */
+		{0x19, 0x03},		/* V start */
+		{0x1a, 0xf8},		/* V end */
+		{0x01, 0x80},		/* blue gain */
+		{0x02, 0x80},		/* red gain */
+	};
+
+	reg_w_buf(gspca_dev, reg_init, ARRAY_SIZE(reg_init));
+
+	i2c_w(gspca_dev, 0x12, 0x80);		/* sensor reset */
+	msleep(10);
+
+	i2c_w_buf(gspca_dev, sensor_init, ARRAY_SIZE(sensor_init));
+
+	reg_w(gspca_dev, TP6800_R5C_EDGE_THRLD, 0x10);
+	reg_w(gspca_dev, TP6800_R54_DARK_CFG, 0x00);
+
+	setsharpness(gspca_dev, v4l2_ctrl_g_ctrl(sd->sharpness));
+
+	bulk_w(gspca_dev, 0x03, color_gain[SENSOR_SOI763A],
+				ARRAY_SIZE(color_gain[0]));
+
+	set_led(gspca_dev, 1);
+	if (sd->sensor == SENSOR_CX0342)
+		setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
+			v4l2_ctrl_g_ctrl(gspca_dev->gain),
+			v4l2_ctrl_g_ctrl(sd->blue),
+			v4l2_ctrl_g_ctrl(sd->red));
+	else
+		setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
+			v4l2_ctrl_g_ctrl(gspca_dev->gain), 0, 0);
+	if (sd->sensor == SENSOR_SOI763A)
+		setquality(gspca_dev, v4l2_ctrl_g_ctrl(sd->jpegqual));
+	setgamma(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma));
+}
+
+static void soi763a_6810_start(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	static const struct cmd bridge_init_2[] = {
+		{TP6800_R7A_BLK_THRLD, 0x00},
+		{TP6800_R79_QUALITY, 0x04},
+		{TP6800_R79_QUALITY, 0x01},
+	};
+	static const struct cmd bridge_init_3[] = {
+		{TP6800_R31_PIXEL_START, 0x20},
+		{TP6800_R32_PIXEL_END_L, 0x9f},
+		{TP6800_R33_PIXEL_END_H, 0x02},
+		{TP6800_R34_LINE_START, 0x13},
+		{TP6800_R35_LINE_END_L, 0xf8},
+		{TP6800_R36_LINE_END_H, 0x01},
+	};
+	static const struct cmd bridge_init_6[] = {
+		{0x08, 0xff},
+		{0x09, 0xff},
+		{0x0a, 0x5f},
+		{0x0b, 0x80},
+	};
+
+	reg_w(gspca_dev, 0x22, gspca_dev->alt);
+	bulk_w(gspca_dev, 0x03, color_null, sizeof color_null);
+	reg_w(gspca_dev, 0x59, 0x40);
+	if (sd->sensor == SENSOR_CX0342)
+		setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
+			v4l2_ctrl_g_ctrl(gspca_dev->gain),
+			v4l2_ctrl_g_ctrl(sd->blue),
+			v4l2_ctrl_g_ctrl(sd->red));
+	else
+		setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
+			v4l2_ctrl_g_ctrl(gspca_dev->gain), 0, 0);
+	reg_w_buf(gspca_dev, bridge_init_2, ARRAY_SIZE(bridge_init_2));
+	reg_w_buf(gspca_dev, tp6810_ov_init_common,
+			ARRAY_SIZE(tp6810_ov_init_common));
+	reg_w_buf(gspca_dev, bridge_init_3, ARRAY_SIZE(bridge_init_3));
+	if (gspca_dev->curr_mode) {
+		reg_w(gspca_dev, 0x4a, 0x7f);
+		reg_w(gspca_dev, 0x07, 0x05);
+		reg_w(gspca_dev, TP6800_R78_FORMAT, 0x00);	/* vga */
+	} else {
+		reg_w(gspca_dev, 0x4a, 0xff);
+		reg_w(gspca_dev, 0x07, 0x85);
+		reg_w(gspca_dev, TP6800_R78_FORMAT, 0x01);	/* qvga */
+	}
+	setgamma(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma));
+	reg_w_buf(gspca_dev, tp6810_bridge_start,
+			ARRAY_SIZE(tp6810_bridge_start));
+
+	if (gspca_dev->curr_mode) {
+		reg_w(gspca_dev, 0x4f, 0x00);
+		reg_w(gspca_dev, 0x4e, 0x7c);
+	}
+
+	reg_w(gspca_dev, 0x00, 0x00);
+
+	setsharpness(gspca_dev, v4l2_ctrl_g_ctrl(sd->sharpness));
+	bulk_w(gspca_dev, 0x03, color_gain[SENSOR_SOI763A],
+				ARRAY_SIZE(color_gain[0]));
+	set_led(gspca_dev, 1);
+	reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0xf0);
+	if (sd->sensor == SENSOR_CX0342)
+		setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
+			v4l2_ctrl_g_ctrl(gspca_dev->gain),
+			v4l2_ctrl_g_ctrl(sd->blue),
+			v4l2_ctrl_g_ctrl(sd->red));
+	else
+		setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
+			v4l2_ctrl_g_ctrl(gspca_dev->gain), 0, 0);
+	reg_w_buf(gspca_dev, bridge_init_6, ARRAY_SIZE(bridge_init_6));
+}
+
+/* -- start the camera -- */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	jpeg_define(sd->jpeg_hdr, gspca_dev->pixfmt.height,
+			gspca_dev->pixfmt.width);
+	set_dqt(gspca_dev, sd->quality);
+	if (sd->bridge == BRIDGE_TP6800) {
+		if (sd->sensor == SENSOR_CX0342)
+			cx0342_6800_start(gspca_dev);
+		else
+			soi763a_6800_start(gspca_dev);
+	} else {
+		if (sd->sensor == SENSOR_CX0342)
+			cx0342_6810_start(gspca_dev);
+		else
+			soi763a_6810_start(gspca_dev);
+		reg_w_buf(gspca_dev, tp6810_late_start,
+				ARRAY_SIZE(tp6810_late_start));
+		reg_w(gspca_dev, 0x80, 0x03);
+		reg_w(gspca_dev, 0x82, gspca_dev->curr_mode ? 0x0a : 0x0e);
+
+		if (sd->sensor == SENSOR_CX0342)
+			setexposure(gspca_dev,
+				v4l2_ctrl_g_ctrl(gspca_dev->exposure),
+				v4l2_ctrl_g_ctrl(gspca_dev->gain),
+				v4l2_ctrl_g_ctrl(sd->blue),
+				v4l2_ctrl_g_ctrl(sd->red));
+		else
+			setexposure(gspca_dev,
+				v4l2_ctrl_g_ctrl(gspca_dev->exposure),
+				v4l2_ctrl_g_ctrl(gspca_dev->gain), 0, 0);
+		if (sd->sensor == SENSOR_SOI763A)
+			setquality(gspca_dev,
+				   v4l2_ctrl_g_ctrl(sd->jpegqual));
+		if (sd->bridge == BRIDGE_TP6810)
+			setautogain(gspca_dev,
+				    v4l2_ctrl_g_ctrl(gspca_dev->autogain));
+	}
+
+	setframerate(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure));
+
+	return gspca_dev->usb_err;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (sd->bridge == BRIDGE_TP6800)
+		reg_w(gspca_dev, TP6800_R2F_TIMING_CFG, 0x03);
+	set_led(gspca_dev, 0);
+	reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x00);
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+			u8 *data,
+			int len)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	/* the start of frame contains:
+	 *	ff d8
+	 *	ff fe
+	 *	width / 16
+	 *	height / 8
+	 *	quality
+	 */
+	if (sd->bridge == BRIDGE_TP6810) {
+		if (*data != 0x5a) {
+/*fixme: don't discard the whole frame..*/
+			if (*data == 0xaa || *data == 0x00)
+				return;
+			if (*data > 0xc0) {
+				PDEBUG(D_FRAM, "bad frame");
+				gspca_dev->last_packet_type = DISCARD_PACKET;
+				return;
+			}
+		}
+		data++;
+		len--;
+		if (len < 2) {
+			gspca_dev->last_packet_type = DISCARD_PACKET;
+			return;
+		}
+		if (*data == 0xff && data[1] == 0xd8) {
+/*fixme: there may be information in the 4 high bits*/
+			if (len < 7) {
+				gspca_dev->last_packet_type = DISCARD_PACKET;
+				return;
+			}
+			if ((data[6] & 0x0f) != sd->quality)
+				set_dqt(gspca_dev, data[6] & 0x0f);
+			gspca_frame_add(gspca_dev, FIRST_PACKET,
+					sd->jpeg_hdr, JPEG_HDR_SZ);
+			gspca_frame_add(gspca_dev, INTER_PACKET,
+					data + 7, len - 7);
+		} else if (data[len - 2] == 0xff && data[len - 1] == 0xd9) {
+			gspca_frame_add(gspca_dev, LAST_PACKET,
+					data, len);
+		} else {
+			gspca_frame_add(gspca_dev, INTER_PACKET,
+					data, len);
+		}
+		return;
+	}
+
+	switch (*data) {
+	case 0x55:
+		gspca_frame_add(gspca_dev, LAST_PACKET, data, 0);
+
+		if (len < 8
+		 || data[1] != 0xff || data[2] != 0xd8
+		 || data[3] != 0xff || data[4] != 0xfe) {
+
+			/* Have only seen this with corrupt frames */
+			gspca_dev->last_packet_type = DISCARD_PACKET;
+			return;
+		}
+		if (data[7] != sd->quality)
+			set_dqt(gspca_dev, data[7]);
+		gspca_frame_add(gspca_dev, FIRST_PACKET,
+				sd->jpeg_hdr, JPEG_HDR_SZ);
+		gspca_frame_add(gspca_dev, INTER_PACKET,
+				data + 8, len - 8);
+		break;
+	case 0xaa:
+		gspca_dev->last_packet_type = DISCARD_PACKET;
+		break;
+	case 0xcc:
+		if (len >= 3 && (data[1] != 0xff || data[2] != 0xd8))
+			gspca_frame_add(gspca_dev, INTER_PACKET,
+					data + 1, len - 1);
+		else
+			gspca_dev->last_packet_type = DISCARD_PACKET;
+		break;
+	}
+}
+
+static void sd_dq_callback(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int ret, alen;
+	int luma, expo;
+
+	if (sd->ag_cnt < 0)
+		return;
+	if (--sd->ag_cnt > 5)
+		return;
+	switch (sd->ag_cnt) {
+/*	case 5: */
+	default:
+		reg_w(gspca_dev, 0x7d, 0x00);
+		break;
+	case 4:
+		reg_w(gspca_dev, 0x27, 0xb0);
+		break;
+	case 3:
+		reg_w(gspca_dev, 0x0c, 0x01);
+		break;
+	case 2:
+		ret = usb_bulk_msg(gspca_dev->dev,
+				usb_rcvbulkpipe(gspca_dev->dev, 0x02),
+				gspca_dev->usb_buf,
+				32,
+				&alen,
+				500);
+		if (ret < 0) {
+			pr_err("bulk err %d\n", ret);
+			break;
+		}
+		/* values not used (unknown) */
+		break;
+	case 1:
+		reg_w(gspca_dev, 0x27, 0xd0);
+		break;
+	case 0:
+		ret = usb_bulk_msg(gspca_dev->dev,
+				usb_rcvbulkpipe(gspca_dev->dev, 0x02),
+				gspca_dev->usb_buf,
+				32,
+				&alen,
+				500);
+		if (ret < 0) {
+			pr_err("bulk err %d\n", ret);
+			break;
+		}
+		luma = ((gspca_dev->usb_buf[8] << 8) + gspca_dev->usb_buf[7] +
+			(gspca_dev->usb_buf[11] << 8) + gspca_dev->usb_buf[10] +
+			(gspca_dev->usb_buf[14] << 8) + gspca_dev->usb_buf[13] +
+			(gspca_dev->usb_buf[17] << 8) + gspca_dev->usb_buf[16] +
+			(gspca_dev->usb_buf[20] << 8) + gspca_dev->usb_buf[19] +
+			(gspca_dev->usb_buf[23] << 8) + gspca_dev->usb_buf[22] +
+			(gspca_dev->usb_buf[26] << 8) + gspca_dev->usb_buf[25] +
+			(gspca_dev->usb_buf[29] << 8) + gspca_dev->usb_buf[28])
+				/ 8;
+		if (gspca_dev->pixfmt.width == 640)
+			luma /= 4;
+		reg_w(gspca_dev, 0x7d, 0x00);
+
+		expo = v4l2_ctrl_g_ctrl(gspca_dev->exposure);
+		ret = gspca_expo_autogain(gspca_dev, luma,
+				60,	/* desired luma */
+				6,	/* dead zone */
+				2,	/* gain knee */
+				70);	/* expo knee */
+		sd->ag_cnt = AG_CNT_START;
+		if (sd->bridge == BRIDGE_TP6810) {
+			int new_expo = v4l2_ctrl_g_ctrl(gspca_dev->exposure);
+
+			if ((expo >= 128 && new_expo < 128)
+			 || (expo < 128 && new_expo >= 128))
+				setframerate(gspca_dev, new_expo);
+		}
+		break;
+	}
+}
+
+/* get stream parameters (framerate) */
+static void sd_get_streamparm(struct gspca_dev *gspca_dev,
+			     struct v4l2_streamparm *parm)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct v4l2_captureparm *cp = &parm->parm.capture;
+	struct v4l2_fract *tpf = &cp->timeperframe;
+	int fr, i;
+
+	cp->capability |= V4L2_CAP_TIMEPERFRAME;
+	tpf->numerator = 1;
+	i = get_fr_idx(gspca_dev);
+	if (i & 0x80) {
+		if (sd->bridge == BRIDGE_TP6800)
+			fr = rates[6 - (i & 0x07)];
+		else
+			fr = rates_6810[7 - (i & 0x07)];
+	} else {
+		fr = rates[6 - i];
+	}
+	tpf->denominator = fr;
+}
+
+/* set stream parameters (framerate) */
+static void sd_set_streamparm(struct gspca_dev *gspca_dev,
+			     struct v4l2_streamparm *parm)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct v4l2_captureparm *cp = &parm->parm.capture;
+	struct v4l2_fract *tpf = &cp->timeperframe;
+	int fr, i;
+
+	if (tpf->numerator == 0 || tpf->denominator == 0)
+		sd->framerate = 30;
+	else
+		sd->framerate = tpf->denominator / tpf->numerator;
+
+	if (gspca_dev->streaming)
+		setframerate(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure));
+
+	/* Return the actual framerate */
+	i = get_fr_idx(gspca_dev);
+	if (i & 0x80)
+		fr = rates_6810[7 - (i & 0x07)];
+	else
+		fr = rates[6 - i];
+	tpf->numerator = 1;
+	tpf->denominator = fr;
+}
+
+static int sd_set_jcomp(struct gspca_dev *gspca_dev,
+			const struct v4l2_jpegcompression *jcomp)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (sd->sensor != SENSOR_SOI763A)
+		return -ENOTTY;
+	v4l2_ctrl_s_ctrl(sd->jpegqual, jcomp->quality);
+	return 0;
+}
+
+static int sd_get_jcomp(struct gspca_dev *gspca_dev,
+			struct v4l2_jpegcompression *jcomp)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (sd->sensor != SENSOR_SOI763A)
+		return -ENOTTY;
+	memset(jcomp, 0, sizeof *jcomp);
+	jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual);
+	jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
+			| V4L2_JPEG_MARKER_DQT;
+	return 0;
+}
+
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct gspca_dev *gspca_dev =
+		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+	struct sd *sd = (struct sd *)gspca_dev;
+
+	gspca_dev->usb_err = 0;
+
+	if (!gspca_dev->streaming)
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_SHARPNESS:
+		setsharpness(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_GAMMA:
+		setgamma(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_BLUE_BALANCE:
+		setbgain(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_RED_BALANCE:
+		setrgain(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_EXPOSURE:
+		sd_setgain(gspca_dev);
+		break;
+	case V4L2_CID_AUTOGAIN:
+		if (ctrl->val)
+			break;
+		sd_setgain(gspca_dev);
+		break;
+	case V4L2_CID_JPEG_COMPRESSION_QUALITY:
+		jpeg_set_qual(sd->jpeg_hdr, ctrl->val);
+		break;
+	}
+	return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+	.s_ctrl = sd_s_ctrl,
+};
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *)gspca_dev;
+	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+	gspca_dev->vdev.ctrl_handler = hdl;
+	v4l2_ctrl_handler_init(hdl, 4);
+	gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_EXPOSURE, 1, 0xdc, 1, 0x4e);
+	if (sd->sensor == SENSOR_CX0342) {
+		sd->red = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_RED_BALANCE, 0, 4095, 1, 256);
+		sd->blue = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_BLUE_BALANCE, 0, 4095, 1, 256);
+	}
+	if (sd->sensor == SENSOR_SOI763A)
+		gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_GAIN, 0, 15, 1, 3);
+	else
+		gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_GAIN, 0, 4095, 1, 256);
+	sd->sharpness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_SHARPNESS, 0, 3, 1, 2);
+	sd->gamma = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_GAMMA, 0, NGAMMA - 1, 1,
+			(sd->sensor == SENSOR_SOI763A &&
+			 sd->bridge == BRIDGE_TP6800) ? 0 : 1);
+	if (sd->bridge == BRIDGE_TP6810)
+		gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+	if (sd->sensor == SENSOR_SOI763A)
+		sd->jpegqual = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_JPEG_COMPRESSION_QUALITY,
+			0, 15, 1, (sd->bridge == BRIDGE_TP6810) ? 0 : 13);
+
+	if (hdl->error) {
+		pr_err("Could not initialize controls\n");
+		return hdl->error;
+	}
+	if (gspca_dev->autogain)
+		v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false);
+	else
+		v4l2_ctrl_cluster(2, &gspca_dev->exposure);
+	return 0;
+}
+
+static const struct sd_desc sd_desc = {
+	.name = KBUILD_MODNAME,
+	.config = sd_config,
+	.init = sd_init,
+	.init_controls = sd_init_controls,
+	.isoc_init = sd_isoc_init,
+	.start = sd_start,
+	.stopN = sd_stopN,
+	.pkt_scan = sd_pkt_scan,
+	.dq_callback = sd_dq_callback,
+	.get_streamparm = sd_get_streamparm,
+	.set_streamparm = sd_set_streamparm,
+	.get_jcomp = sd_get_jcomp,
+	.set_jcomp = sd_set_jcomp,
+};
+
+static const struct usb_device_id device_table[] = {
+	{USB_DEVICE(0x06a2, 0x0003), .driver_info = BRIDGE_TP6800},
+	{USB_DEVICE(0x06a2, 0x6810), .driver_info = BRIDGE_TP6810},
+	{}			/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, device_table);
+
+static int sd_probe(struct usb_interface *interface,
+		    const struct usb_device_id *id)
+{
+	return gspca_dev_probe(interface, id, &sd_desc, sizeof(struct sd),
+			       THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+	.name = KBUILD_MODNAME,
+	.id_table = device_table,
+	.probe = sd_probe,
+	.disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+	.suspend = gspca_suspend,
+	.resume = gspca_resume,
+	.reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
+
+module_param(force_sensor, int, 0644);
+MODULE_PARM_DESC(force_sensor,
+	"Force sensor. 0: cx0342, 1: soi763a");
diff --git a/drivers/media/usb/gspca/touptek.c b/drivers/media/usb/gspca/touptek.c
new file mode 100644
index 0000000..7bac6bc
--- /dev/null
+++ b/drivers/media/usb/gspca/touptek.c
@@ -0,0 +1,731 @@
+/*
+ * ToupTek UCMOS / AmScope MU series camera driver
+ * TODO: contrast with ScopeTek / AmScope MDC cameras
+ *
+ * Copyright (C) 2012-2014 John McMaster <JohnDMcMaster@gmail.com>
+ *
+ * Special thanks to Bushing for helping with the decrypt algorithm and
+ * Sean O'Sullivan / the Rensselaer Center for Open Source
+ * Software (RCOS) for helping me learn kernel development
+ *
+ * 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.
+ */
+
+#include "gspca.h"
+
+#define MODULE_NAME "touptek"
+
+MODULE_AUTHOR("John McMaster");
+MODULE_DESCRIPTION("ToupTek UCMOS / Amscope MU microscope camera driver");
+MODULE_LICENSE("GPL");
+
+/*
+ * Exposure reg is linear with exposure time
+ * Exposure (sec), E (reg)
+ * 0.000400, 0x0002
+ * 0.001000, 0x0005
+ * 0.005000, 0x0019
+ * 0.020000, 0x0064
+ * 0.080000, 0x0190
+ * 0.400000, 0x07D0
+ * 1.000000, 0x1388
+ * 2.000000, 0x2710
+ *
+ * Three gain stages
+ * 0x1000: master channel enable bit
+ * 0x007F: low gain bits
+ * 0x0080: medium gain bit
+ * 0x0100: high gain bit
+ * gain = enable * (1 + regH) * (1 + regM) * z * regL
+ *
+ * Gain implementation
+ * Want to do something similar to mt9v011.c's set_balance
+ *
+ * Gain does not vary with resolution (checked 640x480 vs 1600x1200)
+ *
+ * Constant derivation:
+ *
+ * Raw data:
+ * Gain,   GTOP,   B,	  R,	  GBOT
+ * 1.00,   0x105C, 0x1068, 0x10C8, 0x105C
+ * 1.20,   0x106E, 0x107E, 0x10D6, 0x106E
+ * 1.40,   0x10C0, 0x10CA, 0x10E5, 0x10C0
+ * 1.60,   0x10C9, 0x10D4, 0x10F3, 0x10C9
+ * 1.80,   0x10D2, 0x10DE, 0x11C1, 0x10D2
+ * 2.00,   0x10DC, 0x10E9, 0x11C8, 0x10DC
+ * 2.20,   0x10E5, 0x10F3, 0x11CF, 0x10E5
+ * 2.40,   0x10EE, 0x10FE, 0x11D7, 0x10EE
+ * 2.60,   0x10F7, 0x11C4, 0x11DE, 0x10F7
+ * 2.80,   0x11C0, 0x11CA, 0x11E5, 0x11C0
+ * 3.00,   0x11C5, 0x11CF, 0x11ED, 0x11C5
+ *
+ * zR = 0.0069605943152454778
+ *	about 3/431 = 0.0069605568445475635
+ * zB = 0.0095695970695970703
+ *	about 6/627 = 0.0095693779904306216
+ * zG = 0.010889328063241107
+ *	about 6/551 = 0.010889292196007259
+ * about 10 bits for constant + 7 bits for value => at least 17 bit
+ * intermediate with 32 bit ints should be fine for overflow etc
+ * Essentially gains are in range 0-0x001FF
+ *
+ * However, V4L expects a main gain channel + R and B balance
+ * To keep things simple for now saturate the values of balance is too high/low
+ * This isn't really ideal but easy way to fit the Linux model
+ *
+ * Converted using gain model turns out to be quite linear:
+ * Gain, GTOP, B, R, GBOT
+ * 1.00, 92, 104, 144, 92
+ * 1.20, 110, 126, 172, 110
+ * 1.40, 128, 148, 202, 128
+ * 1.60, 146, 168, 230, 146
+ * 1.80, 164, 188, 260, 164
+ * 2.00, 184, 210, 288, 184
+ * 2.20, 202, 230, 316, 202
+ * 2.40, 220, 252, 348, 220
+ * 2.60, 238, 272, 376, 238
+ * 2.80, 256, 296, 404, 256
+ * 3.00, 276, 316, 436, 276
+ *
+ * Maximum gain is 0x7FF * 2 * 2 => 0x1FFC (8188)
+ * or about 13 effective bits of gain
+ * The highest the commercial driver goes in my setup 436
+ * However, because could *maybe* damage circuits
+ * limit the gain until have a reason to go higher
+ * Solution: gain clipped and warning emitted
+ */
+#define GAIN_MAX		511
+
+/* Frame sync is a short read */
+#define BULK_SIZE		0x4000
+
+/* MT9E001 reg names to give a rough approximation */
+#define REG_COARSE_INTEGRATION_TIME_	0x3012
+#define REG_GROUPED_PARAMETER_HOLD_	0x3022
+#define REG_MODE_SELECT			0x0100
+#define REG_OP_SYS_CLK_DIV		0x030A
+#define REG_VT_SYS_CLK_DIV		0x0302
+#define REG_PRE_PLL_CLK_DIV		0x0304
+#define REG_VT_PIX_CLK_DIV		0x0300
+#define REG_OP_PIX_CLK_DIV		0x0308
+#define REG_PLL_MULTIPLIER		0x0306
+#define REG_COARSE_INTEGRATION_TIME_	0x3012
+#define REG_FRAME_LENGTH_LINES		0x0340
+#define REG_FRAME_LENGTH_LINES_		0x300A
+#define REG_GREEN1_GAIN			0x3056
+#define REG_GREEN2_GAIN			0x305C
+#define REG_GROUPED_PARAMETER_HOLD	0x0104
+#define REG_LINE_LENGTH_PCK_		0x300C
+#define REG_MODE_SELECT			0x0100
+#define REG_PLL_MULTIPLIER		0x0306
+#define REG_READ_MODE			0x3040
+#define REG_BLUE_GAIN			0x3058
+#define REG_RED_GAIN			0x305A
+#define REG_RESET_REGISTER		0x301A
+#define REG_SCALE_M			0x0404
+#define REG_SCALING_MODE		0x0400
+#define REG_SOFTWARE_RESET		0x0103
+#define REG_X_ADDR_END			0x0348
+#define REG_X_ADDR_START		0x0344
+#define REG_X_ADDR_START		0x0344
+#define REG_X_OUTPUT_SIZE		0x034C
+#define REG_Y_ADDR_END			0x034A
+#define REG_Y_ADDR_START		0x0346
+#define REG_Y_OUTPUT_SIZE		0x034E
+
+
+/* specific webcam descriptor */
+struct sd {
+	struct gspca_dev gspca_dev;	/* !! must be the first item */
+	/* How many bytes this frame */
+	unsigned int this_f;
+
+	/*
+	Device has separate gains for each Bayer quadrant
+	V4L supports master gain which is referenced to G1/G2 and supplies
+	individual balance controls for R/B
+	*/
+	struct v4l2_ctrl *blue;
+	struct v4l2_ctrl *red;
+};
+
+/* Used to simplify reg write error handling */
+struct cmd {
+	u16 value;
+	u16 index;
+};
+
+static const struct v4l2_pix_format vga_mode[] = {
+	{800, 600,
+		V4L2_PIX_FMT_SGRBG8,
+		V4L2_FIELD_NONE,
+		.bytesperline = 800,
+		.sizeimage = 800 * 600,
+		.colorspace = V4L2_COLORSPACE_SRGB},
+	{1600, 1200,
+		V4L2_PIX_FMT_SGRBG8,
+		V4L2_FIELD_NONE,
+		.bytesperline = 1600,
+		.sizeimage = 1600 * 1200,
+		.colorspace = V4L2_COLORSPACE_SRGB},
+	{3264, 2448,
+		V4L2_PIX_FMT_SGRBG8,
+		V4L2_FIELD_NONE,
+		.bytesperline = 3264,
+		.sizeimage = 3264 * 2448,
+		.colorspace = V4L2_COLORSPACE_SRGB},
+};
+
+/*
+ * As theres no known frame sync, the only way to keep synced is to try hard
+ * to never miss any packets
+ */
+#if MAX_NURBS < 4
+#error "Not enough URBs in the gspca table"
+#endif
+
+static int val_reply(struct gspca_dev *gspca_dev, const char *reply, int rc)
+{
+	if (rc < 0) {
+		PERR("reply has error %d", rc);
+		return -EIO;
+	}
+	if (rc != 1) {
+		PERR("Bad reply size %d", rc);
+		return -EIO;
+	}
+	if (reply[0] != 0x08) {
+		PERR("Bad reply 0x%02X", reply[0]);
+		return -EIO;
+	}
+	return 0;
+}
+
+static void reg_w(struct gspca_dev *gspca_dev, u16 value, u16 index)
+{
+	char buff[1];
+	int rc;
+
+	PDEBUG(D_USBO,
+		"reg_w bReq=0x0B, bReqT=0xC0, wVal=0x%04X, wInd=0x%04X\n",
+		value, index);
+	rc = usb_control_msg(gspca_dev->dev, usb_rcvctrlpipe(gspca_dev->dev, 0),
+		0x0B, 0xC0, value, index, buff, 1, 500);
+	PDEBUG(D_USBO, "rc=%d, ret={0x%02X}", rc, buff[0]);
+	if (rc < 0) {
+		PERR("Failed reg_w(0x0B, 0xC0, 0x%04X, 0x%04X) w/ rc %d\n",
+			value, index, rc);
+		gspca_dev->usb_err = rc;
+		return;
+	}
+	if (val_reply(gspca_dev, buff, rc)) {
+		PERR("Bad reply to reg_w(0x0B, 0xC0, 0x%04X, 0x%04X\n",
+			value, index);
+		gspca_dev->usb_err = -EIO;
+	}
+}
+
+static void reg_w_buf(struct gspca_dev *gspca_dev,
+		const struct cmd *p, int l)
+{
+	do {
+		reg_w(gspca_dev, p->value, p->index);
+		p++;
+	} while (--l > 0);
+}
+
+static void setexposure(struct gspca_dev *gspca_dev, s32 val)
+{
+	u16 value;
+	unsigned int w = gspca_dev->pixfmt.width;
+
+	if (w == 800)
+		value = val * 5;
+	else if (w == 1600)
+		value = val * 3;
+	else if (w == 3264)
+		value = val * 3 / 2;
+	else {
+		PERR("Invalid width %u\n", w);
+		gspca_dev->usb_err = -EINVAL;
+		return;
+	}
+	PDEBUG(D_STREAM, "exposure: 0x%04X ms\n", value);
+	/* Wonder if theres a good reason for sending it twice */
+	/* probably not but leave it in because...why not */
+	reg_w(gspca_dev, value, REG_COARSE_INTEGRATION_TIME_);
+	reg_w(gspca_dev, value, REG_COARSE_INTEGRATION_TIME_);
+}
+
+static int gainify(int in)
+{
+	/*
+	 * TODO: check if there are any issues with corner cases
+	 * 0x000 (0):0x07F (127): regL
+	 * 0x080 (128) - 0x0FF (255): regM, regL
+	 * 0x100 (256) - max: regH, regM, regL
+	 */
+	if (in <= 0x7F)
+		return 0x1000 | in;
+	else if (in <= 0xFF)
+		return 0x1080 | in / 2;
+	else
+		return 0x1180 | in / 4;
+}
+
+static void setggain(struct gspca_dev *gspca_dev, u16 global_gain)
+{
+	u16 normalized;
+
+	normalized = gainify(global_gain);
+	PDEBUG(D_STREAM, "gain G1/G2 (0x%04X): 0x%04X (src 0x%04X)\n",
+		 REG_GREEN1_GAIN,
+		 normalized, global_gain);
+
+	reg_w(gspca_dev, normalized, REG_GREEN1_GAIN);
+	reg_w(gspca_dev, normalized, REG_GREEN2_GAIN);
+}
+
+static void setbgain(struct gspca_dev *gspca_dev,
+		u16 gain, u16 global_gain)
+{
+	u16 normalized;
+
+	normalized = global_gain +
+		((u32)global_gain) * gain / GAIN_MAX;
+	if (normalized > GAIN_MAX) {
+		PDEBUG(D_STREAM, "Truncating blue 0x%04X w/ value 0x%04X\n",
+			 GAIN_MAX, normalized);
+		normalized = GAIN_MAX;
+	}
+	normalized = gainify(normalized);
+	PDEBUG(D_STREAM, "gain B (0x%04X): 0x%04X w/ source 0x%04X\n",
+		 REG_BLUE_GAIN, normalized, gain);
+
+	reg_w(gspca_dev, normalized, REG_BLUE_GAIN);
+}
+
+static void setrgain(struct gspca_dev *gspca_dev,
+		u16 gain, u16 global_gain)
+{
+	u16 normalized;
+
+	normalized = global_gain +
+		((u32)global_gain) * gain / GAIN_MAX;
+	if (normalized > GAIN_MAX) {
+		PDEBUG(D_STREAM, "Truncating gain 0x%04X w/ value 0x%04X\n",
+			 GAIN_MAX, normalized);
+		normalized = GAIN_MAX;
+	}
+	normalized = gainify(normalized);
+	PDEBUG(D_STREAM, "gain R (0x%04X): 0x%04X w / source 0x%04X\n",
+		 REG_RED_GAIN, normalized, gain);
+
+	reg_w(gspca_dev, normalized, REG_RED_GAIN);
+}
+
+static void configure_wh(struct gspca_dev *gspca_dev)
+{
+	unsigned int w = gspca_dev->pixfmt.width;
+
+	PDEBUG(D_STREAM, "configure_wh\n");
+
+	if (w == 800) {
+		static const struct cmd reg_init_res[] = {
+			{0x0060, REG_X_ADDR_START},
+			{0x0CD9, REG_X_ADDR_END},
+			{0x0036, REG_Y_ADDR_START},
+			{0x098F, REG_Y_ADDR_END},
+			{0x07C7, REG_READ_MODE},
+		};
+
+		reg_w_buf(gspca_dev,
+			       reg_init_res, ARRAY_SIZE(reg_init_res));
+	} else if (w == 1600) {
+		static const struct cmd reg_init_res[] = {
+			{0x009C, REG_X_ADDR_START},
+			{0x0D19, REG_X_ADDR_END},
+			{0x0068, REG_Y_ADDR_START},
+			{0x09C5, REG_Y_ADDR_END},
+			{0x06C3, REG_READ_MODE},
+		};
+
+		reg_w_buf(gspca_dev,
+			       reg_init_res, ARRAY_SIZE(reg_init_res));
+	} else if (w == 3264) {
+		static const struct cmd reg_init_res[] = {
+			{0x00E8, REG_X_ADDR_START},
+			{0x0DA7, REG_X_ADDR_END},
+			{0x009E, REG_Y_ADDR_START},
+			{0x0A2D, REG_Y_ADDR_END},
+			{0x0241, REG_READ_MODE},
+		};
+
+		reg_w_buf(gspca_dev,
+			       reg_init_res, ARRAY_SIZE(reg_init_res));
+	} else {
+		PERR("bad width %u\n", w);
+		gspca_dev->usb_err = -EINVAL;
+		return;
+	}
+
+	reg_w(gspca_dev, 0x0000, REG_SCALING_MODE);
+	reg_w(gspca_dev, 0x0010, REG_SCALE_M);
+	reg_w(gspca_dev, w, REG_X_OUTPUT_SIZE);
+	reg_w(gspca_dev, gspca_dev->pixfmt.height, REG_Y_OUTPUT_SIZE);
+
+	if (w == 800) {
+		reg_w(gspca_dev, 0x0384, REG_FRAME_LENGTH_LINES_);
+		reg_w(gspca_dev, 0x0960, REG_LINE_LENGTH_PCK_);
+	} else if (w == 1600) {
+		reg_w(gspca_dev, 0x0640, REG_FRAME_LENGTH_LINES_);
+		reg_w(gspca_dev, 0x0FA0, REG_LINE_LENGTH_PCK_);
+	} else if (w == 3264) {
+		reg_w(gspca_dev, 0x0B4B, REG_FRAME_LENGTH_LINES_);
+		reg_w(gspca_dev, 0x1F40, REG_LINE_LENGTH_PCK_);
+	} else {
+		PERR("bad width %u\n", w);
+		gspca_dev->usb_err = -EINVAL;
+		return;
+	}
+}
+
+/* Packets that were encrypted, no idea if the grouping is significant */
+static void configure_encrypted(struct gspca_dev *gspca_dev)
+{
+	static const struct cmd reg_init_begin[] = {
+		{0x0100, REG_SOFTWARE_RESET},
+		{0x0000, REG_MODE_SELECT},
+		{0x0100, REG_GROUPED_PARAMETER_HOLD},
+		{0x0004, REG_VT_PIX_CLK_DIV},
+		{0x0001, REG_VT_SYS_CLK_DIV},
+		{0x0008, REG_OP_PIX_CLK_DIV},
+		{0x0001, REG_OP_SYS_CLK_DIV},
+		{0x0004, REG_PRE_PLL_CLK_DIV},
+		{0x0040, REG_PLL_MULTIPLIER},
+		{0x0000, REG_GROUPED_PARAMETER_HOLD},
+		{0x0100, REG_GROUPED_PARAMETER_HOLD},
+	};
+	static const struct cmd reg_init_end[] = {
+		{0x0000, REG_GROUPED_PARAMETER_HOLD},
+		{0x0301, 0x31AE},
+		{0x0805, 0x3064},
+		{0x0071, 0x3170},
+		{0x10DE, REG_RESET_REGISTER},
+		{0x0000, REG_MODE_SELECT},
+		{0x0010, REG_PLL_MULTIPLIER},
+		{0x0100, REG_MODE_SELECT},
+	};
+
+	PDEBUG(D_STREAM, "Encrypted begin, w = %u\n", gspca_dev->pixfmt.width);
+	reg_w_buf(gspca_dev, reg_init_begin, ARRAY_SIZE(reg_init_begin));
+	configure_wh(gspca_dev);
+	reg_w_buf(gspca_dev, reg_init_end, ARRAY_SIZE(reg_init_end));
+	reg_w(gspca_dev, 0x0100, REG_GROUPED_PARAMETER_HOLD);
+	reg_w(gspca_dev, 0x0000, REG_GROUPED_PARAMETER_HOLD);
+
+	PDEBUG(D_STREAM, "Encrypted end\n");
+}
+
+static int configure(struct gspca_dev *gspca_dev)
+{
+	int rc;
+	uint8_t buff[4];
+
+	PDEBUG(D_STREAM, "configure()\n");
+
+	/*
+	 * First driver sets a sort of encryption key
+	 * A number of futur requests of this type have wValue and wIndex
+	 * encrypted as follows:
+	 * -Compute key = this wValue rotate left by 4 bits
+	 *	(decrypt.py rotates right because we are decrypting)
+	 * -Later packets encrypt packets by XOR'ing with key
+	 *	XOR encrypt/decrypt is symmetrical
+	 *	wValue, and wIndex are encrypted
+	 *	bRequest is not and bRequestType is always 0xC0
+	 *		This allows resyncing if key is unknown?
+	 * By setting 0 we XOR with 0 and the shifting and XOR drops out
+	 */
+	rc = usb_control_msg(gspca_dev->dev, usb_rcvctrlpipe(gspca_dev->dev, 0),
+			     0x16, 0xC0, 0x0000, 0x0000, buff, 2, 500);
+	if (val_reply(gspca_dev, buff, rc)) {
+		PERR("failed key req");
+		return -EIO;
+	}
+
+	/*
+	 * Next does some sort of 2 packet challenge / response
+	 * evidence suggests its an Atmel I2C crypto part but nobody cares to
+	 * look
+	 * (to make sure its not cloned hardware?)
+	 * Ignore: I want to work with their hardware, not clone it
+	 * 16 bytes out challenge, requestType: 0x40
+	 * 16 bytes in response, requestType: 0xC0
+	 */
+
+	rc = usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0),
+			     0x01, 0x40, 0x0001, 0x000F, NULL, 0, 500);
+	if (rc < 0) {
+		PERR("failed to replay packet 176 w/ rc %d\n", rc);
+		return rc;
+	}
+
+	rc = usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0),
+			     0x01, 0x40, 0x0000, 0x000F, NULL, 0, 500);
+	if (rc < 0) {
+		PERR("failed to replay packet 178 w/ rc %d\n", rc);
+		return rc;
+	}
+
+	rc = usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0),
+			     0x01, 0x40, 0x0001, 0x000F, NULL, 0, 500);
+	if (rc < 0) {
+		PERR("failed to replay packet 180 w/ rc %d\n", rc);
+		return rc;
+	}
+
+	/*
+	 * Serial number?  Doesn't seem to be required
+	 * cam1: \xE6\x0D\x00\x00, cam2: \x70\x19\x00\x00
+	 * rc = usb_control_msg(gspca_dev->dev,
+	 *			usb_rcvctrlpipe(gspca_dev->dev, 0),
+	 *			0x20, 0xC0, 0x0000, 0x0000, buff, 4, 500);
+	 */
+
+	/* Large (EEPROM?) read, skip it since no idea what to do with it */
+	gspca_dev->usb_err = 0;
+	configure_encrypted(gspca_dev);
+	if (gspca_dev->usb_err)
+		return gspca_dev->usb_err;
+
+	/* Omitted this by accident, does not work without it */
+	rc = usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0),
+			     0x01, 0x40, 0x0003, 0x000F, NULL, 0, 500);
+	if (rc < 0) {
+		PERR("failed to replay final packet w/ rc %d\n", rc);
+		return rc;
+	}
+
+	PDEBUG(D_STREAM, "Configure complete\n");
+	return 0;
+}
+
+static int sd_config(struct gspca_dev *gspca_dev,
+		     const struct usb_device_id *id)
+{
+	gspca_dev->cam.cam_mode = vga_mode;
+	gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode);
+
+	/* Yes we want URBs and we want them now! */
+	gspca_dev->cam.no_urb_create = 0;
+	gspca_dev->cam.bulk_nurbs = 4;
+	/* Largest size the windows driver uses */
+	gspca_dev->cam.bulk_size = BULK_SIZE;
+	/* Def need to use bulk transfers */
+	gspca_dev->cam.bulk = 1;
+
+	return 0;
+}
+
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int rc;
+
+	sd->this_f = 0;
+
+	rc = configure(gspca_dev);
+	if (rc < 0) {
+		PERR("Failed configure");
+		return rc;
+	}
+	/* First two frames have messed up gains
+	Drop them to avoid special cases in user apps? */
+	return 0;
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+			u8 *data,	/* isoc packet */
+			int len)	/* iso packet length */
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (len != BULK_SIZE) {
+		/* can we finish a frame? */
+		if (sd->this_f + len == gspca_dev->pixfmt.sizeimage) {
+			gspca_frame_add(gspca_dev, LAST_PACKET, data, len);
+			PDEBUG(D_FRAM, "finish frame sz %u/%u w/ len %u\n",
+				 sd->this_f, gspca_dev->pixfmt.sizeimage, len);
+		/* lost some data, discard the frame */
+		} else {
+			gspca_frame_add(gspca_dev, DISCARD_PACKET, NULL, 0);
+			PDEBUG(D_FRAM, "abort frame sz %u/%u w/ len %u\n",
+				 sd->this_f, gspca_dev->pixfmt.sizeimage, len);
+		}
+		sd->this_f = 0;
+	} else {
+		if (sd->this_f == 0)
+			gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
+		else
+			gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+		sd->this_f += len;
+	}
+}
+
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+	return 0;
+}
+
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct gspca_dev *gspca_dev =
+		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	gspca_dev->usb_err = 0;
+
+	if (!gspca_dev->streaming)
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_EXPOSURE:
+		setexposure(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_GAIN:
+		/* gspca_dev->gain automatically updated */
+		setggain(gspca_dev, gspca_dev->gain->val);
+		break;
+	case V4L2_CID_BLUE_BALANCE:
+		sd->blue->val = ctrl->val;
+		setbgain(gspca_dev, sd->blue->val, gspca_dev->gain->val);
+		break;
+	case V4L2_CID_RED_BALANCE:
+		sd->red->val = ctrl->val;
+		setrgain(gspca_dev, sd->red->val, gspca_dev->gain->val);
+		break;
+	}
+	return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+	.s_ctrl = sd_s_ctrl,
+};
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+	gspca_dev->vdev.ctrl_handler = hdl;
+	v4l2_ctrl_handler_init(hdl, 4);
+
+	gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+	/* Mostly limited by URB timeouts */
+	/* XXX: make dynamic based on frame rate? */
+		V4L2_CID_EXPOSURE, 0, 800, 1, 350);
+	gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_GAIN, 0, 511, 1, 128);
+	sd->blue = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_BLUE_BALANCE, 0, 1023, 1, 80);
+	sd->red = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_RED_BALANCE, 0, 1023, 1, 295);
+
+	if (hdl->error) {
+		PERR("Could not initialize controls\n");
+		return hdl->error;
+	}
+	return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+	.name = MODULE_NAME,
+	.config = sd_config,
+	.init = sd_init,
+	.init_controls = sd_init_controls,
+	.start = sd_start,
+	.pkt_scan = sd_pkt_scan,
+};
+
+/* Table of supported USB devices */
+static const struct usb_device_id device_table[] = {
+	/* Commented out devices should be related */
+	/* AS: AmScope, TT: ToupTek */
+	/* { USB_DEVICE(0x0547, 0x6035) },  TT UCMOS00350KPA */
+	/* { USB_DEVICE(0x0547, 0x6130) },  TT UCMOS01300KPA */
+	/* { USB_DEVICE(0x0547, 0x6200) },  TT UCMOS02000KPA */
+	/* { USB_DEVICE(0x0547, 0x6310) },  TT UCMOS03100KPA */
+	/* { USB_DEVICE(0x0547, 0x6510) },  TT UCMOS05100KPA */
+	/* { USB_DEVICE(0x0547, 0x6800) },  TT UCMOS08000KPA */
+	/* { USB_DEVICE(0x0547, 0x6801) },  TT UCMOS08000KPB */
+	{ USB_DEVICE(0x0547, 0x6801) }, /* TT UCMOS08000KPB, AS MU800 */
+	/* { USB_DEVICE(0x0547, 0x6900) },  TT UCMOS09000KPA */
+	/* { USB_DEVICE(0x0547, 0x6901) },  TT UCMOS09000KPB */
+	/* { USB_DEVICE(0x0547, 0x6010) },  TT UCMOS10000KPA */
+	/* { USB_DEVICE(0x0547, 0x6014) },  TT UCMOS14000KPA */
+	/* { USB_DEVICE(0x0547, 0x6131) },  TT UCMOS01300KMA */
+	/* { USB_DEVICE(0x0547, 0x6511) },  TT UCMOS05100KMA */
+	/* { USB_DEVICE(0x0547, 0x8080) },  TT UHCCD00800KPA */
+	/* { USB_DEVICE(0x0547, 0x8140) },  TT UHCCD01400KPA */
+	/* { USB_DEVICE(0x0547, 0x8141) },  TT EXCCD01400KPA */
+	/* { USB_DEVICE(0x0547, 0x8200) },  TT UHCCD02000KPA */
+	/* { USB_DEVICE(0x0547, 0x8201) },  TT UHCCD02000KPB */
+	/* { USB_DEVICE(0x0547, 0x8310) },  TT UHCCD03100KPA */
+	/* { USB_DEVICE(0x0547, 0x8500) },  TT UHCCD05000KPA */
+	/* { USB_DEVICE(0x0547, 0x8510) },  TT UHCCD05100KPA */
+	/* { USB_DEVICE(0x0547, 0x8600) },  TT UHCCD06000KPA */
+	/* { USB_DEVICE(0x0547, 0x8800) },  TT UHCCD08000KPA */
+	/* { USB_DEVICE(0x0547, 0x8315) },  TT UHCCD03150KPA */
+	/* { USB_DEVICE(0x0547, 0x7800) },  TT UHCCD00800KMA */
+	/* { USB_DEVICE(0x0547, 0x7140) },  TT UHCCD01400KMA */
+	/* { USB_DEVICE(0x0547, 0x7141) },  TT UHCCD01400KMB */
+	/* { USB_DEVICE(0x0547, 0x7200) },  TT UHCCD02000KMA */
+	/* { USB_DEVICE(0x0547, 0x7315) },  TT UHCCD03150KMA */
+	{ }
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+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,
+#ifdef CONFIG_PM
+	.suspend = gspca_suspend,
+	.resume = gspca_resume,
+#endif
+};
+
+static int __init sd_mod_init(void)
+{
+	int ret;
+
+	ret = usb_register(&sd_driver);
+	if (ret < 0)
+		return ret;
+	return 0;
+}
+static void __exit sd_mod_exit(void)
+{
+	usb_deregister(&sd_driver);
+}
+
+module_init(sd_mod_init);
+module_exit(sd_mod_exit);
diff --git a/drivers/media/usb/gspca/tv8532.c b/drivers/media/usb/gspca/tv8532.c
new file mode 100644
index 0000000..d497ba3
--- /dev/null
+++ b/drivers/media/usb/gspca/tv8532.c
@@ -0,0 +1,379 @@
+/*
+ * 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"
+
+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 */
+
+	__u8 packet;
+};
+
+static const 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},
+};
+
+/* TV-8532A (ICM532A) registers (LE) */
+#define R00_PART_CONTROL 0x00
+#define		LATENT_CHANGE	0x80
+#define		EXPO_CHANGE	0x04
+#define R01_TIMING_CONTROL_LOW 0x01
+#define		CMD_EEprom_Open 0x30
+#define		CMD_EEprom_Close 0x29
+#define R03_TABLE_ADDR 0x03
+#define R04_WTRAM_DATA_L 0x04
+#define R05_WTRAM_DATA_M 0x05
+#define R06_WTRAM_DATA_H 0x06
+#define R07_TABLE_LEN	0x07
+#define R08_RAM_WRITE_ACTION 0x08
+#define R0C_AD_WIDTHL	0x0c
+#define R0D_AD_WIDTHH	0x0d
+#define R0E_AD_HEIGHTL	0x0e
+#define R0F_AD_HEIGHTH	0x0f
+#define R10_AD_COL_BEGINL 0x10
+#define R11_AD_COL_BEGINH 0x11
+#define		MIRROR		0x04	/* [10] */
+#define R14_AD_ROW_BEGINL 0x14
+#define R15_AD_ROWBEGINH  0x15
+#define R1C_AD_EXPOSE_TIMEL 0x1c
+#define R20_GAIN_G1L	0x20
+#define R21_GAIN_G1H	0x21
+#define R22_GAIN_RL	0x22
+#define R23_GAIN_RH	0x23
+#define R24_GAIN_BL	0x24
+#define R25_GAIN_BH	0x25
+#define R26_GAIN_G2L	0x26
+#define R27_GAIN_G2H	0x27
+#define R28_QUANT	0x28
+#define R29_LINE	0x29
+#define R2C_POLARITY	0x2c
+#define R2D_POINT	0x2d
+#define R2E_POINTH	0x2e
+#define R2F_POINTB	0x2f
+#define R30_POINTBH	0x30
+#define R31_UPD		0x31
+#define R2A_HIGH_BUDGET 0x2a
+#define R2B_LOW_BUDGET	0x2b
+#define R34_VID		0x34
+#define R35_VIDH	0x35
+#define R36_PID		0x36
+#define R37_PIDH	0x37
+#define R39_Test1	0x39		/* GPIO */
+#define R3B_Test3	0x3b		/* GPIO */
+#define R83_AD_IDH	0x83
+#define R91_AD_SLOPEREG 0x91
+#define R94_AD_BITCONTROL 0x94
+
+static const u8 eeprom_data[][3] = {
+/*	dataH dataM dataL */
+	{0x01, 0x00, 0x01},
+	{0x01, 0x80, 0x11},
+	{0x05, 0x00, 0x14},
+	{0x05, 0x00, 0x1c},
+	{0x0d, 0x00, 0x1e},
+	{0x05, 0x00, 0x1f},
+	{0x05, 0x05, 0x19},
+	{0x05, 0x01, 0x1b},
+	{0x05, 0x09, 0x1e},
+	{0x0d, 0x89, 0x2e},
+	{0x05, 0x89, 0x2f},
+	{0x05, 0x0d, 0xd9},
+	{0x05, 0x09, 0xf1},
+};
+
+
+/* write 1 byte */
+static void reg_w1(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),
+			0x02,
+			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			0,	/* value */
+			index, gspca_dev->usb_buf, 1, 500);
+}
+
+/* write 2 bytes */
+static void reg_w2(struct gspca_dev *gspca_dev,
+		  u16 index, u16 value)
+{
+	gspca_dev->usb_buf[0] = value;
+	gspca_dev->usb_buf[1] = value >> 8;
+	usb_control_msg(gspca_dev->dev,
+			usb_sndctrlpipe(gspca_dev->dev, 0),
+			0x02,
+			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;
+
+	reg_w1(gspca_dev, R01_TIMING_CONTROL_LOW, CMD_EEprom_Open);
+	for (i = 0; i < ARRAY_SIZE(eeprom_data); i++) {
+		reg_w1(gspca_dev, R03_TABLE_ADDR, i);
+		reg_w1(gspca_dev, R04_WTRAM_DATA_L, eeprom_data[i][2]);
+		reg_w1(gspca_dev, R05_WTRAM_DATA_M, eeprom_data[i][1]);
+		reg_w1(gspca_dev, R06_WTRAM_DATA_H, eeprom_data[i][0]);
+		reg_w1(gspca_dev, R08_RAM_WRITE_ACTION, 0);
+	}
+	reg_w1(gspca_dev, R07_TABLE_LEN, i);
+	reg_w1(gspca_dev, R01_TIMING_CONTROL_LOW, CMD_EEprom_Close);
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+		     const struct usb_device_id *id)
+{
+	struct cam *cam;
+
+	cam = &gspca_dev->cam;
+	cam->cam_mode = sif_mode;
+	cam->nmodes = ARRAY_SIZE(sif_mode);
+
+	return 0;
+}
+
+static void tv_8532_setReg(struct gspca_dev *gspca_dev)
+{
+	reg_w1(gspca_dev, R3B_Test3, 0x0a);	/* Test0Sel = 10 */
+	/******************************************************/
+	reg_w1(gspca_dev, R0E_AD_HEIGHTL, 0x90);
+	reg_w1(gspca_dev, R0F_AD_HEIGHTH, 0x01);
+	reg_w2(gspca_dev, R1C_AD_EXPOSE_TIMEL, 0x018f);
+	reg_w1(gspca_dev, R10_AD_COL_BEGINL, 0x44);
+						/* begin active line */
+	reg_w1(gspca_dev, R11_AD_COL_BEGINH, 0x00);
+						/* mirror and digital gain */
+	reg_w1(gspca_dev, R14_AD_ROW_BEGINL, 0x0a);
+
+	reg_w1(gspca_dev, R94_AD_BITCONTROL, 0x02);
+	reg_w1(gspca_dev, R91_AD_SLOPEREG, 0x00);
+	reg_w1(gspca_dev, R00_PART_CONTROL, LATENT_CHANGE | EXPO_CHANGE);
+						/* = 0x84 */
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+	tv_8532WriteEEprom(gspca_dev);
+
+	return 0;
+}
+
+static void setexposure(struct gspca_dev *gspca_dev, s32 val)
+{
+	reg_w2(gspca_dev, R1C_AD_EXPOSE_TIMEL, val);
+	reg_w1(gspca_dev, R00_PART_CONTROL, LATENT_CHANGE | EXPO_CHANGE);
+						/* 0x84 */
+}
+
+static void setgain(struct gspca_dev *gspca_dev, s32 val)
+{
+	reg_w2(gspca_dev, R20_GAIN_G1L, val);
+	reg_w2(gspca_dev, R22_GAIN_RL, val);
+	reg_w2(gspca_dev, R24_GAIN_BL, val);
+	reg_w2(gspca_dev, R26_GAIN_G2L, val);
+}
+
+/* -- start the camera -- */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	reg_w1(gspca_dev, R0C_AD_WIDTHL, 0xe8);		/* 0x20; 0x0c */
+	reg_w1(gspca_dev, R0D_AD_WIDTHH, 0x03);
+
+	/************************************************/
+	reg_w1(gspca_dev, R28_QUANT, 0x90);
+					/* 0x72 compressed mode 0x28 */
+	if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) {
+		/* 176x144 */
+		reg_w1(gspca_dev, R29_LINE, 0x41);
+					/* CIF - 2 lines/packet */
+	} else {
+		/* 352x288 */
+		reg_w1(gspca_dev, R29_LINE, 0x81);
+					/* CIF - 2 lines/packet */
+	}
+	/************************************************/
+	reg_w1(gspca_dev, R2C_POLARITY, 0x10);		/* slow clock */
+	reg_w1(gspca_dev, R2D_POINT, 0x14);
+	reg_w1(gspca_dev, R2E_POINTH, 0x01);
+	reg_w1(gspca_dev, R2F_POINTB, 0x12);
+	reg_w1(gspca_dev, R30_POINTBH, 0x01);
+
+	tv_8532_setReg(gspca_dev);
+
+	/************************************************/
+	reg_w1(gspca_dev, R31_UPD, 0x01);	/* update registers */
+	msleep(200);
+	reg_w1(gspca_dev, R31_UPD, 0x00);	/* end update */
+
+	gspca_dev->empty_packet = 0;		/* check the empty packets */
+	sd->packet = 0;				/* ignore the first packets */
+
+	return 0;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+	reg_w1(gspca_dev, R3B_Test3, 0x0b);	/* Test0Sel = 11 = GPIO */
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+			u8 *data,			/* isoc packet */
+			int len)			/* iso packet length */
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int packet_type0, packet_type1;
+
+	packet_type0 = packet_type1 = INTER_PACKET;
+	if (gspca_dev->empty_packet) {
+		gspca_dev->empty_packet = 0;
+		sd->packet = gspca_dev->pixfmt.height / 2;
+		packet_type0 = FIRST_PACKET;
+	} else if (sd->packet == 0)
+		return;			/* 2 more lines in 352x288 ! */
+	sd->packet--;
+	if (sd->packet == 0)
+		packet_type1 = LAST_PACKET;
+
+	/* each packet contains:
+	 * - header 2 bytes
+	 * - RGRG line
+	 * - 4 bytes
+	 * - GBGB line
+	 * - 4 bytes
+	 */
+	gspca_frame_add(gspca_dev, packet_type0,
+			data + 2, gspca_dev->pixfmt.width);
+	gspca_frame_add(gspca_dev, packet_type1,
+			data + gspca_dev->pixfmt.width + 5,
+			gspca_dev->pixfmt.width);
+}
+
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct gspca_dev *gspca_dev =
+		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+
+	gspca_dev->usb_err = 0;
+
+	if (!gspca_dev->streaming)
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_EXPOSURE:
+		setexposure(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_GAIN:
+		setgain(gspca_dev, ctrl->val);
+		break;
+	}
+	return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+	.s_ctrl = sd_s_ctrl,
+};
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+	gspca_dev->vdev.ctrl_handler = hdl;
+	v4l2_ctrl_handler_init(hdl, 2);
+	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_EXPOSURE, 0, 0x18f, 1, 0x18f);
+	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_GAIN, 0, 0x7ff, 1, 0x100);
+
+	if (hdl->error) {
+		pr_err("Could not initialize controls\n");
+		return hdl->error;
+	}
+	return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+	.name = MODULE_NAME,
+	.config = sd_config,
+	.init = sd_init,
+	.init_controls = sd_init_controls,
+	.start = sd_start,
+	.stopN = sd_stopN,
+	.pkt_scan = sd_pkt_scan,
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+	{USB_DEVICE(0x046d, 0x0920)},
+	{USB_DEVICE(0x046d, 0x0921)},
+	{USB_DEVICE(0x0545, 0x808b)},
+	{USB_DEVICE(0x0545, 0x8333)},
+	{USB_DEVICE(0x0923, 0x010f)},
+	{}
+};
+
+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,
+#ifdef CONFIG_PM
+	.suspend = gspca_suspend,
+	.resume = gspca_resume,
+	.reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/usb/gspca/vc032x.c b/drivers/media/usb/gspca/vc032x.c
new file mode 100644
index 0000000..b4efb2f
--- /dev/null
+++ b/drivers/media/usb/gspca/vc032x.c
@@ -0,0 +1,3843 @@
+/*
+ * Z-star vc0321 library
+ *
+ * Copyright (C) 2009-2010 Jean-François Moine <http://moinejf.free.fr>
+ * Copyright (C) 2006 Koninski Artur takeshi87@o2.pl
+ * Copyright (C) 2006 Michel Xhaard
+ *
+ * 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "vc032x"
+
+#include "gspca.h"
+
+MODULE_AUTHOR("Jean-François Moine <http://moinejf.free.fr>");
+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 */
+	struct { /* hvflip cluster */
+		struct v4l2_ctrl *hflip;
+		struct v4l2_ctrl *vflip;
+	};
+
+	u8 image_offset;
+
+	u8 bridge;
+	u8 sensor;
+	u8 flags;
+#define FL_SAMSUNG 0x01		/* SamsungQ1 (2 sensors) */
+#define FL_HFLIP 0x02		/* mirrored by default */
+#define FL_VFLIP 0x04		/* vertical flipped by default */
+};
+enum bridges {
+	BRIDGE_VC0321,
+	BRIDGE_VC0323,
+};
+enum sensors {
+	SENSOR_HV7131R,
+	SENSOR_MI0360,
+	SENSOR_MI1310_SOC,
+	SENSOR_MI1320,
+	SENSOR_MI1320_SOC,
+	SENSOR_OV7660,
+	SENSOR_OV7670,
+	SENSOR_PO1200,
+	SENSOR_PO3130NC,
+	SENSOR_POxxxx,
+	NSENSORS
+};
+
+
+static const struct v4l2_pix_format vc0321_mode[] = {
+	{320, 240, V4L2_PIX_FMT_YVYU, V4L2_FIELD_NONE,
+		.bytesperline = 320 * 2,
+		.sizeimage = 320 * 240 * 2,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 1},
+	{640, 480, V4L2_PIX_FMT_YVYU, V4L2_FIELD_NONE,
+		.bytesperline = 640 * 2,
+		.sizeimage = 640 * 480 * 2,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 0},
+};
+static const 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},
+	{1280, 960, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, /* mi1310_soc only */
+		.bytesperline = 1280,
+		.sizeimage = 1280 * 960 * 3 / 8 + 590,
+		.colorspace = V4L2_COLORSPACE_JPEG,
+		.priv = 2},
+};
+static const struct v4l2_pix_format bi_mode[] = {
+	{320, 240, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE,
+		.bytesperline = 320 * 2,
+		.sizeimage = 320 * 240 * 2,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 2},
+	{640, 480, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE,
+		.bytesperline = 640 * 2,
+		.sizeimage = 640 * 480 * 2,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 1},
+	{1280, 1024, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE,
+		.bytesperline = 1280 * 2,
+		.sizeimage = 1280 * 1024 * 2,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 0},
+};
+static const struct v4l2_pix_format svga_mode[] = {
+	{800, 600, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+		.bytesperline = 800,
+		.sizeimage = 800 * 600 * 1 / 4 + 590,
+		.colorspace = V4L2_COLORSPACE_JPEG,
+		.priv = 0},
+};
+
+/* OV7660/7670 registers */
+#define OV7660_REG_MVFP 0x1e
+#define OV7660_MVFP_MIRROR	0x20
+#define OV7660_MVFP_VFLIP	0x10
+
+static const u8 mi0360_matrix[9] = {
+	0x50, 0xf8, 0xf8, 0xf5, 0x50, 0xfb, 0xff, 0xf1, 0x50
+};
+
+static const u8 mi0360_initVGA_JPG[][4] = {
+	{0xb0, 0x03, 0x19, 0xcc},
+	{0xb0, 0x04, 0x02, 0xcc},
+	{0xb3, 0x00, 0x24, 0xcc},
+	{0xb3, 0x00, 0x25, 0xcc},
+	{0xb3, 0x08, 0x01, 0xcc},
+	{0xb3, 0x09, 0x0c, 0xcc},
+	{0xb3, 0x05, 0x01, 0xcc},
+	{0xb3, 0x06, 0x03, 0xcc},
+	{0xb3, 0x03, 0x0a, 0xcc},
+	{0xb3, 0x20, 0x00, 0xcc},
+	{0xb3, 0x21, 0x00, 0xcc},
+	{0xb3, 0x22, 0x01, 0xcc},
+	{0xb3, 0x23, 0xe0, 0xcc},
+	{0xb3, 0x04, 0x05, 0xcc},
+	{0xb3, 0x14, 0x00, 0xcc},
+	{0xb3, 0x15, 0x00, 0xcc},
+	{0xb3, 0x16, 0x02, 0xcc},
+	{0xb3, 0x17, 0x7f, 0xcc},
+	{0xb3, 0x35, 0xdd, 0xcc},	/* i2c add: 5d */
+	{0xb3, 0x34, 0x02, 0xcc},
+	{0xb3, 0x00, 0x25, 0xcc},
+	{0xbc, 0x00, 0x71, 0xcc},
+	{0xb8, 0x00, 0x13, 0xcc},
+	{0xb8, 0x27, 0x20, 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},
+	{0xb8, 0x01, 0x79, 0xcc},
+	{0xb8, 0x08, 0xe0, 0xcc},
+	{0xb3, 0x01, 0x41, 0xcc},
+	{0xb8, 0x01, 0x79, 0xcc},
+	{0xb8, 0x14, 0x18, 0xcc},
+	{0xb8, 0xb2, 0x0a, 0xcc},
+	{0xb8, 0xb4, 0x0a, 0xcc},
+	{0xb8, 0xb5, 0x0a, 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},
+	{0xb8, 0x81, 0x09, 0xcc},
+	{0x31, 0x00, 0x00, 0xbb},
+	{0x09, 0x01, 0xc7, 0xbb},
+	{0x34, 0x01, 0x00, 0xbb},
+	{0x2b, 0x00, 0x28, 0xbb},
+	{0x2c, 0x00, 0x30, 0xbb},
+	{0x2d, 0x00, 0x30, 0xbb},
+	{0x2e, 0x00, 0x28, 0xbb},
+	{0x62, 0x04, 0x11, 0xbb},
+	{0x03, 0x01, 0xe0, 0xbb},
+	{0x2c, 0x00, 0x2c, 0xbb},
+	{0x20, 0xd0, 0x00, 0xbb},
+	{0x01, 0x00, 0x08, 0xbb},
+	{0x06, 0x00, 0x10, 0xbb},
+	{0x05, 0x00, 0x20, 0xbb},
+	{0x20, 0x00, 0x00, 0xbb},
+	{0xb6, 0x00, 0x00, 0xcc},
+	{0xb6, 0x03, 0x02, 0xcc},
+	{0xb6, 0x02, 0x80, 0xcc},
+	{0xb6, 0x05, 0x01, 0xcc},
+	{0xb6, 0x04, 0xe0, 0xcc},
+	{0xb6, 0x12, 0x78, 0xcc},
+	{0xb6, 0x18, 0x02, 0xcc},
+	{0xb6, 0x17, 0x58, 0xcc},
+	{0xb6, 0x16, 0x00, 0xcc},
+	{0xb6, 0x22, 0x12, 0xcc},
+	{0xb6, 0x23, 0x0b, 0xcc},
+	{0xb3, 0x02, 0x02, 0xcc},
+	{0xbf, 0xc0, 0x39, 0xcc},
+	{0xbf, 0xc1, 0x04, 0xcc},
+	{0xbf, 0xcc, 0x10, 0xcc},
+	{0xb9, 0x12, 0x00, 0xcc},
+	{0xb9, 0x13, 0x0a, 0xcc},
+	{0xb9, 0x14, 0x0a, 0xcc},
+	{0xb9, 0x15, 0x0a, 0xcc},
+	{0xb9, 0x16, 0x0a, 0xcc},
+	{0xb9, 0x18, 0x00, 0xcc},
+	{0xb9, 0x19, 0x0f, 0xcc},
+	{0xb9, 0x1a, 0x0f, 0xcc},
+	{0xb9, 0x1b, 0x0f, 0xcc},
+	{0xb9, 0x1c, 0x0f, 0xcc},
+	{0xb8, 0x8e, 0x00, 0xcc},
+	{0xb8, 0x8f, 0xff, 0xcc},
+	{0xb6, 0x12, 0xf8, 0xcc},
+	{0xb8, 0x0c, 0x20, 0xcc},
+	{0xb8, 0x0d, 0x70, 0xcc},
+	{0xb6, 0x13, 0x13, 0xcc},
+	{0x35, 0x00, 0x60, 0xbb},
+	{0xb3, 0x5c, 0x01, 0xcc},
+	{}
+};
+static const u8 mi0360_initQVGA_JPG[][4] = {
+	{0xb0, 0x03, 0x19, 0xcc},
+	{0xb0, 0x04, 0x02, 0xcc},
+	{0xb3, 0x00, 0x24, 0xcc},
+	{0xb3, 0x00, 0x25, 0xcc},
+	{0xb3, 0x08, 0x01, 0xcc},
+	{0xb3, 0x09, 0x0c, 0xcc},
+	{0xb3, 0x05, 0x01, 0xcc},
+	{0xb3, 0x06, 0x03, 0xcc},
+	{0xb3, 0x03, 0x0a, 0xcc},
+	{0xb3, 0x20, 0x00, 0xcc},
+	{0xb3, 0x21, 0x00, 0xcc},
+	{0xb3, 0x22, 0x01, 0xcc},
+	{0xb3, 0x23, 0xe0, 0xcc},
+	{0xb3, 0x04, 0x05, 0xcc},
+	{0xb3, 0x14, 0x00, 0xcc},
+	{0xb3, 0x15, 0x00, 0xcc},
+	{0xb3, 0x16, 0x02, 0xcc},
+	{0xb3, 0x17, 0x7f, 0xcc},
+	{0xb3, 0x35, 0xdd, 0xcc},
+	{0xb3, 0x34, 0x02, 0xcc},
+	{0xb3, 0x00, 0x25, 0xcc},
+	{0xbc, 0x00, 0xd1, 0xcc},
+	{0xb8, 0x00, 0x13, 0xcc},
+	{0xb8, 0x27, 0x20, 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},
+	{0xb8, 0x01, 0x79, 0xcc},
+	{0xb8, 0x08, 0xe0, 0xcc},
+	{0xb3, 0x01, 0x41, 0xcc},
+	{0xb8, 0x01, 0x79, 0xcc},
+	{0xb8, 0x14, 0x18, 0xcc},
+	{0xb8, 0xb2, 0x0a, 0xcc},
+	{0xb8, 0xb4, 0x0a, 0xcc},
+	{0xb8, 0xb5, 0x0a, 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},
+	{0xb8, 0x81, 0x09, 0xcc},
+	{0x31, 0x00, 0x00, 0xbb},
+	{0x09, 0x01, 0xc7, 0xbb},
+	{0x34, 0x01, 0x00, 0xbb},
+	{0x2b, 0x00, 0x28, 0xbb},
+	{0x2c, 0x00, 0x30, 0xbb},
+	{0x2d, 0x00, 0x30, 0xbb},
+	{0x2e, 0x00, 0x28, 0xbb},
+	{0x62, 0x04, 0x11, 0xbb},
+	{0x03, 0x01, 0xe0, 0xbb},
+	{0x2c, 0x00, 0x2c, 0xbb},
+	{0x20, 0xd0, 0x00, 0xbb},
+	{0x01, 0x00, 0x08, 0xbb},
+	{0x06, 0x00, 0x10, 0xbb},
+	{0x05, 0x00, 0x20, 0xbb},
+	{0x20, 0x00, 0x00, 0xbb},
+	{0xb6, 0x00, 0x00, 0xcc},
+	{0xb6, 0x03, 0x01, 0xcc},
+	{0xb6, 0x02, 0x40, 0xcc},
+	{0xb6, 0x05, 0x00, 0xcc},
+	{0xb6, 0x04, 0xf0, 0xcc},
+	{0xb6, 0x12, 0x78, 0xcc},
+	{0xb6, 0x18, 0x00, 0xcc},
+	{0xb6, 0x17, 0x96, 0xcc},
+	{0xb6, 0x16, 0x00, 0xcc},
+	{0xb6, 0x22, 0x12, 0xcc},
+	{0xb6, 0x23, 0x0b, 0xcc},
+	{0xb3, 0x02, 0x02, 0xcc},
+	{0xbf, 0xc0, 0x39, 0xcc},
+	{0xbf, 0xc1, 0x04, 0xcc},
+	{0xbf, 0xcc, 0x10, 0xcc},
+	{0xb9, 0x12, 0x00, 0xcc},
+	{0xb9, 0x13, 0x0a, 0xcc},
+	{0xb9, 0x14, 0x0a, 0xcc},
+	{0xb9, 0x15, 0x0a, 0xcc},
+	{0xb9, 0x16, 0x0a, 0xcc},
+	{0xb9, 0x18, 0x00, 0xcc},
+	{0xb9, 0x19, 0x0f, 0xcc},
+	{0xb9, 0x1a, 0x0f, 0xcc},
+	{0xb9, 0x1b, 0x0f, 0xcc},
+	{0xb9, 0x1c, 0x0f, 0xcc},
+	{0xb8, 0x8e, 0x00, 0xcc},
+	{0xb8, 0x8f, 0xff, 0xcc},
+	{0xb6, 0x12, 0xf8, 0xcc},
+	{0xb6, 0x13, 0x13, 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},
+	{0xb8, 0x0c, 0x20, 0xcc},
+	{0xb8, 0x0d, 0x70, 0xcc},
+	{0xbc, 0x0b, 0x00, 0xcc},
+	{0xbc, 0x0c, 0x00, 0xcc},
+	{0x35, 0x00, 0xef, 0xbb},
+	{0xb3, 0x5c, 0x01, 0xcc},
+	{}
+};
+
+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},	/* i2c add: 5d */
+	{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},	/* h/v flip */
+	{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},	/* h/v flip */
+	{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 mi1310_soc_InitSXGA_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, 0x0d, 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, 0x70, 0xcc},
+	{0xbc, 0x01, 0x01, 0xcc},
+	{0xf0, 0x00, 0x02, 0xbb},
+	{0xc8, 0x9f, 0x0b, 0xbb},
+	{0x5b, 0x00, 0x01, 0xbb},
+	{0xf0, 0x00, 0x00, 0xbb},
+	{0x20, 0x03, 0x02, 0xbb},	/* h/v flip */
+	{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, 0x60, 0xbb},
+	{0x21, 0xb6, 0x6e, 0xbb},
+	{0x37, 0x01, 0x40, 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},
+	{0xb6, 0x00, 0x00, 0xcc},
+	{0xb6, 0x03, 0x05, 0xcc},
+	{0xb6, 0x02, 0x00, 0xcc},
+	{0xb6, 0x05, 0x03, 0xcc},
+	{0xb6, 0x04, 0xc0, 0xcc},
+	{0xb6, 0x12, 0xf8, 0xcc},
+	{0xb6, 0x13, 0x29, 0xcc},
+	{0xb6, 0x18, 0x09, 0xcc},
+	{0xb6, 0x17, 0x60, 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, 0x01, 0x41, 0xcc},
+	{0x00, 0x00, 0x80, 0xdd},
+	{0xf0, 0x00, 0x02, 0xbb},
+	{0x00, 0x00, 0x10, 0xdd},
+	{0x22, 0xa0, 0x78, 0xbb},
+	{0x23, 0xa0, 0x78, 0xbb},
+	{0x24, 0x7f, 0x00, 0xbb},
+	{0x28, 0xea, 0x02, 0xbb},
+	{0x29, 0x86, 0x7a, 0xbb},
+	{0x5e, 0x52, 0x4c, 0xbb},
+	{0x5f, 0x20, 0x24, 0xbb},
+	{0x60, 0x00, 0x02, 0xbb},
+	{0x02, 0x00, 0xee, 0xbb},
+	{0x03, 0x39, 0x23, 0xbb},
+	{0x04, 0x07, 0x24, 0xbb},
+	{0x09, 0x00, 0xc0, 0xbb},
+	{0x0a, 0x00, 0x79, 0xbb},
+	{0x0b, 0x00, 0x04, 0xbb},
+	{0x0c, 0x00, 0x5c, 0xbb},
+	{0x0d, 0x00, 0xd9, 0xbb},
+	{0x0e, 0x00, 0x53, 0xbb},
+	{0x0f, 0x00, 0x21, 0xbb},
+	{0x10, 0x00, 0xa4, 0xbb},
+	{0x11, 0x00, 0xe5, 0xbb},
+	{0x15, 0x00, 0x00, 0xbb},
+	{0x16, 0x00, 0x00, 0xbb},
+	{0x17, 0x00, 0x00, 0xbb},
+	{0x18, 0x00, 0x00, 0xbb},
+	{0x19, 0x00, 0x00, 0xbb},
+	{0x1a, 0x00, 0x00, 0xbb},
+	{0x1b, 0x00, 0x00, 0xbb},
+	{0x1c, 0x00, 0x00, 0xbb},
+	{0x1d, 0x00, 0x00, 0xbb},
+	{0x1e, 0x00, 0x00, 0xbb},
+	{0xf0, 0x00, 0x01, 0xbb},
+	{0x00, 0x00, 0x20, 0xdd},
+	{0x06, 0xf0, 0x8e, 0xbb},
+	{0x00, 0x00, 0x80, 0xdd},
+	{0x06, 0x70, 0x8e, 0xbb},
+	{0xf0, 0x00, 0x02, 0xbb},
+	{0x00, 0x00, 0x20, 0xdd},
+	{0x5e, 0x6a, 0x53, 0xbb},
+	{0x5f, 0x40, 0x2c, 0xbb},
+	{0xf0, 0x00, 0x01, 0xbb},
+	{0x00, 0x00, 0x20, 0xdd},
+	{0x58, 0x00, 0x00, 0xbb},
+	{0x53, 0x09, 0x03, 0xbb},
+	{0x54, 0x31, 0x18, 0xbb},
+	{0x55, 0x8b, 0x5f, 0xbb},
+	{0x56, 0xc0, 0xa9, 0xbb},
+	{0x57, 0xe0, 0xd2, 0xbb},
+	{0xe1, 0x00, 0x00, 0xbb},
+	{0xdc, 0x09, 0x03, 0xbb},
+	{0xdd, 0x31, 0x18, 0xbb},
+	{0xde, 0x8b, 0x5f, 0xbb},
+	{0xdf, 0xc0, 0xa9, 0xbb},
+	{0xe0, 0xe0, 0xd2, 0xbb},
+	{0xb3, 0x5c, 0x01, 0xcc},
+	{0xf0, 0x00, 0x01, 0xbb},
+	{0x06, 0xf0, 0x8e, 0xbb},
+	{0xf0, 0x00, 0x02, 0xbb},
+	{0x2f, 0xde, 0x20, 0xbb},
+	{0xf0, 0x00, 0x02, 0xbb},
+	{0x24, 0x50, 0x20, 0xbb},
+	{0xbc, 0x0e, 0x00, 0xcc},
+	{0xbc, 0x0f, 0x05, 0xcc},
+	{0xbc, 0x10, 0xc0, 0xcc},
+	{0xf0, 0x00, 0x02, 0xbb},
+	{0x34, 0x0c, 0x50, 0xbb},
+	{0xbc, 0x11, 0x03, 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, 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},	/* i2c add: 48 */
+	{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, 0x00, 0xbb},	/* h/v flips - was 03 */
+	{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 mi1320_soc_InitVGA[][4] = {
+	{0xb3, 0x01, 0x01, 0xcc},
+	{0xb0, 0x03, 0x19, 0xcc},
+	{0xb0, 0x04, 0x02, 0xcc},
+	{0x00, 0x00, 0x30, 0xdd},
+	{0xb3, 0x00, 0x64, 0xcc},
+	{0xb3, 0x00, 0x67, 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},	/* i2c add: 48 */
+	{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, 0x67, 0xcc},
+	{0xb8, 0x00, 0x00, 0xcc},
+	{0xbc, 0x00, 0x71, 0xcc},
+	{0xbc, 0x01, 0x01, 0xcc},
+	{0xb3, 0x5c, 0x01, 0xcc},
+	{0xf0, 0x00, 0x02, 0xbb},
+	{0x00, 0x00, 0x10, 0xdd},
+	{0xc8, 0x00, 0x00, 0xbb},
+	{0x00, 0x00, 0x30, 0xdd},
+	{0xf0, 0x00, 0x00, 0xbb},
+	{0x00, 0x00, 0x10, 0xdd},
+	{0x07, 0x00, 0xe0, 0xbb},
+	{0x08, 0x00, 0x0b, 0xbb},
+	{0x21, 0x00, 0x0c, 0xbb},
+	{0x20, 0x01, 0x03, 0xbb},	/* h/v flip */
+	{0xbf, 0xc0, 0x26, 0xcc},
+	{0xbf, 0xc1, 0x02, 0xcc},
+	{0xbf, 0xcc, 0x04, 0xcc},
+	{0xb3, 0x01, 0x41, 0xcc},
+	{0xf0, 0x00, 0x00, 0xbb},
+	{0x05, 0x01, 0x78, 0xbb},
+	{0x06, 0x00, 0x11, 0xbb},
+	{0x07, 0x01, 0x42, 0xbb},
+	{0x08, 0x00, 0x11, 0xbb},
+	{0x20, 0x01, 0x03, 0xbb},	/* h/v flip */
+	{0x21, 0x80, 0x00, 0xbb},
+	{0x22, 0x0d, 0x0f, 0xbb},
+	{0x24, 0x80, 0x00, 0xbb},
+	{0x59, 0x00, 0xff, 0xbb},
+	{0xf0, 0x00, 0x02, 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, 0x00, 0xbb},
+	{0xf0, 0x00, 0x02, 0xbb},
+	{0x22, 0xa0, 0x78, 0xbb},
+	{0x23, 0xa0, 0x78, 0xbb},
+	{0x24, 0x7f, 0x00, 0xbb},
+	{0x28, 0xea, 0x02, 0xbb},
+	{0x29, 0x86, 0x7a, 0xbb},
+	{0x5e, 0x52, 0x4c, 0xbb},
+	{0x5f, 0x20, 0x24, 0xbb},
+	{0x60, 0x00, 0x02, 0xbb},
+	{0x02, 0x00, 0xee, 0xbb},
+	{0x03, 0x39, 0x23, 0xbb},
+	{0x04, 0x07, 0x24, 0xbb},
+	{0x09, 0x00, 0xc0, 0xbb},
+	{0x0a, 0x00, 0x79, 0xbb},
+	{0x0b, 0x00, 0x04, 0xbb},
+	{0x0c, 0x00, 0x5c, 0xbb},
+	{0x0d, 0x00, 0xd9, 0xbb},
+	{0x0e, 0x00, 0x53, 0xbb},
+	{0x0f, 0x00, 0x21, 0xbb},
+	{0x10, 0x00, 0xa4, 0xbb},
+	{0x11, 0x00, 0xe5, 0xbb},
+	{0x15, 0x00, 0x00, 0xbb},
+	{0x16, 0x00, 0x00, 0xbb},
+	{0x17, 0x00, 0x00, 0xbb},
+	{0x18, 0x00, 0x00, 0xbb},
+	{0x19, 0x00, 0x00, 0xbb},
+	{0x1a, 0x00, 0x00, 0xbb},
+	{0x1b, 0x00, 0x00, 0xbb},
+	{0x1c, 0x00, 0x00, 0xbb},
+	{0x1d, 0x00, 0x00, 0xbb},
+	{0x1e, 0x00, 0x00, 0xbb},
+	{0xf0, 0x00, 0x01, 0xbb},
+	{0x06, 0xe0, 0x0e, 0xbb},
+	{0x06, 0x60, 0x0e, 0xbb},
+	{0xb3, 0x5c, 0x01, 0xcc},
+	{}
+};
+static const u8 mi1320_soc_InitQVGA[][4] = {
+	{0xb3, 0x01, 0x01, 0xcc},
+	{0xb0, 0x03, 0x19, 0xcc},
+	{0xb0, 0x04, 0x02, 0xcc},
+	{0x00, 0x00, 0x30, 0xdd},
+	{0xb3, 0x00, 0x64, 0xcc},
+	{0xb3, 0x00, 0x67, 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, 0x67, 0xcc},
+	{0xb8, 0x00, 0x00, 0xcc},
+	{0xbc, 0x00, 0xd1, 0xcc},
+	{0xbc, 0x01, 0x01, 0xcc},
+	{0xb3, 0x5c, 0x01, 0xcc},
+	{0xf0, 0x00, 0x02, 0xbb},
+	{0x00, 0x00, 0x10, 0xdd},
+	{0xc8, 0x00, 0x00, 0xbb},
+	{0x00, 0x00, 0x30, 0xdd},
+	{0xf0, 0x00, 0x00, 0xbb},
+	{0x00, 0x00, 0x10, 0xdd},
+	{0x07, 0x00, 0xe0, 0xbb},
+	{0x08, 0x00, 0x0b, 0xbb},
+	{0x21, 0x00, 0x0c, 0xbb},
+	{0x20, 0x01, 0x03, 0xbb},	/* h/v flip */
+	{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, 0x01, 0x41, 0xcc},
+	{0xf0, 0x00, 0x00, 0xbb},
+	{0x05, 0x01, 0x78, 0xbb},
+	{0x06, 0x00, 0x11, 0xbb},
+	{0x07, 0x01, 0x42, 0xbb},
+	{0x08, 0x00, 0x11, 0xbb},
+	{0x20, 0x01, 0x03, 0xbb},	/* h/v flip */
+	{0x21, 0x80, 0x00, 0xbb},
+	{0x22, 0x0d, 0x0f, 0xbb},
+	{0x24, 0x80, 0x00, 0xbb},
+	{0x59, 0x00, 0xff, 0xbb},
+	{0xf0, 0x00, 0x02, 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, 0x00, 0xbb},
+	{0xf0, 0x00, 0x02, 0xbb},
+	{0x22, 0xa0, 0x78, 0xbb},
+	{0x23, 0xa0, 0x78, 0xbb},
+	{0x24, 0x7f, 0x00, 0xbb},
+	{0x28, 0xea, 0x02, 0xbb},
+	{0x29, 0x86, 0x7a, 0xbb},
+	{0x5e, 0x52, 0x4c, 0xbb},
+	{0x5f, 0x20, 0x24, 0xbb},
+	{0x60, 0x00, 0x02, 0xbb},
+	{0x02, 0x00, 0xee, 0xbb},
+	{0x03, 0x39, 0x23, 0xbb},
+	{0x04, 0x07, 0x24, 0xbb},
+	{0x09, 0x00, 0xc0, 0xbb},
+	{0x0a, 0x00, 0x79, 0xbb},
+	{0x0b, 0x00, 0x04, 0xbb},
+	{0x0c, 0x00, 0x5c, 0xbb},
+	{0x0d, 0x00, 0xd9, 0xbb},
+	{0x0e, 0x00, 0x53, 0xbb},
+	{0x0f, 0x00, 0x21, 0xbb},
+	{0x10, 0x00, 0xa4, 0xbb},
+	{0x11, 0x00, 0xe5, 0xbb},
+	{0x15, 0x00, 0x00, 0xbb},
+	{0x16, 0x00, 0x00, 0xbb},
+	{0x17, 0x00, 0x00, 0xbb},
+	{0x18, 0x00, 0x00, 0xbb},
+	{0x19, 0x00, 0x00, 0xbb},
+	{0x1a, 0x00, 0x00, 0xbb},
+	{0x1b, 0x00, 0x00, 0xbb},
+	{0x1c, 0x00, 0x00, 0xbb},
+	{0x1d, 0x00, 0x00, 0xbb},
+	{0x1e, 0x00, 0x00, 0xbb},
+	{0xf0, 0x00, 0x01, 0xbb},
+	{0x06, 0xe0, 0x0e, 0xbb},
+	{0x06, 0x60, 0x0e, 0xbb},
+	{0xb3, 0x5c, 0x01, 0xcc},
+	{}
+};
+static const u8 mi1320_soc_InitSXGA[][4] = {
+	{0xb3, 0x01, 0x01, 0xcc},
+	{0xb0, 0x03, 0x19, 0xcc},
+	{0x00, 0x00, 0x30, 0xdd},
+	{0xb3, 0x00, 0x64, 0xcc},
+	{0xb3, 0x00, 0x67, 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, 0x04, 0xcc},
+	{0xb3, 0x23, 0x00, 0xcc},
+	{0xb3, 0x14, 0x00, 0xcc},
+	{0xb3, 0x15, 0x00, 0xcc},
+	{0xb3, 0x16, 0x04, 0xcc},
+	{0xb3, 0x17, 0xff, 0xcc},
+	{0xb3, 0x00, 0x67, 0xcc},
+	{0xbc, 0x00, 0x71, 0xcc},
+	{0xbc, 0x01, 0x01, 0xcc},
+	{0xb3, 0x5c, 0x01, 0xcc},
+	{0xf0, 0x00, 0x02, 0xbb},
+	{0x00, 0x00, 0x30, 0xdd},
+	{0xc8, 0x9f, 0x0b, 0xbb},
+	{0x00, 0x00, 0x20, 0xdd},
+	{0x5b, 0x00, 0x01, 0xbb},
+	{0x00, 0x00, 0x20, 0xdd},
+	{0xf0, 0x00, 0x00, 0xbb},
+	{0x00, 0x00, 0x30, 0xdd},
+	{0x20, 0x01, 0x03, 0xbb},	/* h/v flip */
+	{0x00, 0x00, 0x20, 0xdd},
+	{0xbf, 0xc0, 0x26, 0xcc},
+	{0xbf, 0xc1, 0x02, 0xcc},
+	{0xbf, 0xcc, 0x04, 0xcc},
+	{0xb3, 0x01, 0x41, 0xcc},
+	{0xf0, 0x00, 0x00, 0xbb},
+	{0x05, 0x01, 0x78, 0xbb},
+	{0x06, 0x00, 0x11, 0xbb},
+	{0x07, 0x01, 0x42, 0xbb},
+	{0x08, 0x00, 0x11, 0xbb},
+	{0x20, 0x01, 0x03, 0xbb},	/* h/v flip */
+	{0x21, 0x80, 0x00, 0xbb},
+	{0x22, 0x0d, 0x0f, 0xbb},
+	{0x24, 0x80, 0x00, 0xbb},
+	{0x59, 0x00, 0xff, 0xbb},
+	{0xf0, 0x00, 0x02, 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, 0x00, 0xbb},
+	{0xf0, 0x00, 0x02, 0xbb},
+	{0x22, 0xa0, 0x78, 0xbb},
+	{0x23, 0xa0, 0x78, 0xbb},
+	{0x24, 0x7f, 0x00, 0xbb},
+	{0x28, 0xea, 0x02, 0xbb},
+	{0x29, 0x86, 0x7a, 0xbb},
+	{0x5e, 0x52, 0x4c, 0xbb},
+	{0x5f, 0x20, 0x24, 0xbb},
+	{0x60, 0x00, 0x02, 0xbb},
+	{0x02, 0x00, 0xee, 0xbb},
+	{0x03, 0x39, 0x23, 0xbb},
+	{0x04, 0x07, 0x24, 0xbb},
+	{0x09, 0x00, 0xc0, 0xbb},
+	{0x0a, 0x00, 0x79, 0xbb},
+	{0x0b, 0x00, 0x04, 0xbb},
+	{0x0c, 0x00, 0x5c, 0xbb},
+	{0x0d, 0x00, 0xd9, 0xbb},
+	{0x0e, 0x00, 0x53, 0xbb},
+	{0x0f, 0x00, 0x21, 0xbb},
+	{0x10, 0x00, 0xa4, 0xbb},
+	{0x11, 0x00, 0xe5, 0xbb},
+	{0x15, 0x00, 0x00, 0xbb},
+	{0x16, 0x00, 0x00, 0xbb},
+	{0x17, 0x00, 0x00, 0xbb},
+	{0x18, 0x00, 0x00, 0xbb},
+	{0x19, 0x00, 0x00, 0xbb},
+	{0x1a, 0x00, 0x00, 0xbb},
+	{0x1b, 0x00, 0x00, 0xbb},
+	{0x1c, 0x00, 0x00, 0xbb},
+	{0x1d, 0x00, 0x00, 0xbb},
+	{0x1e, 0x00, 0x00, 0xbb},
+	{0xf0, 0x00, 0x01, 0xbb},
+	{0x06, 0xe0, 0x0e, 0xbb},
+	{0x06, 0x60, 0x0e, 0xbb},
+	{0xb3, 0x5c, 0x01, 0xcc},
+	{0xf0, 0x00, 0x00, 0xbb},
+	{0x05, 0x01, 0x13, 0xbb},
+	{0x06, 0x00, 0x11, 0xbb},
+	{0x07, 0x00, 0x85, 0xbb},
+	{0x08, 0x00, 0x27, 0xbb},
+	{0x20, 0x01, 0x03, 0xbb},	/* h/v flip */
+	{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},
+	{}
+};
+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},	/* i2c add: 76 */
+	{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
+};
+static const u8 hv7131r_matrix[9] = {
+	0x5f, 0xec, 0xf5, 0xf1, 0x5a, 0xf5, 0xf1, 0xec, 0x63
+};
+static const u8 hv7131r_initVGA_data[][4] = {
+	{0xb3, 0x01, 0x01, 0xcc},
+	{0xb0, 0x03, 0x19, 0xcc},
+	{0xb0, 0x04, 0x02, 0xcc},
+	{0x00, 0x00, 0x20, 0xdd},
+	{0xb3, 0x00, 0x24, 0xcc},
+	{0xb3, 0x00, 0x25, 0xcc},
+	{0xb3, 0x08, 0x01, 0xcc},
+	{0xb3, 0x09, 0x0c, 0xcc},
+	{0xb3, 0x05, 0x01, 0xcc},
+	{0xb3, 0x06, 0x03, 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, 0x02, 0xcc},
+	{0xb3, 0x16, 0x02, 0xcc},
+	{0xb3, 0x17, 0x7f, 0xcc},
+	{0xb3, 0x34, 0x01, 0xcc},
+	{0xb3, 0x35, 0x91, 0xcc},	/* i2c add: 11 */
+	{0xb3, 0x00, 0x27, 0xcc},
+	{0xbc, 0x00, 0x73, 0xcc},
+	{0xb8, 0x00, 0x23, 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, 0x58, 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, 0x8e, 0x00, 0xcc},
+	{0xb8, 0x8f, 0xff, 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, 0x03, 0xaa},
+	{0x00, 0x26, 0xa9, 0xaa},
+	{0x00, 0x27, 0x80, 0xaa},
+	{0x00, 0x30, 0x18, 0xaa},
+	{0xb6, 0x00, 0x00, 0xcc},
+	{0xb6, 0x03, 0x02, 0xcc},
+	{0xb6, 0x02, 0x80, 0xcc},
+	{0xb6, 0x05, 0x01, 0xcc},
+	{0xb6, 0x04, 0xe0, 0xcc},
+	{0xb6, 0x12, 0x78, 0xcc},
+	{0xb6, 0x18, 0x02, 0xcc},
+	{0xb6, 0x17, 0x58, 0xcc},
+	{0xb6, 0x16, 0x00, 0xcc},
+	{0xb6, 0x22, 0x12, 0xcc},
+	{0xb6, 0x23, 0x0b, 0xcc},
+	{0xb3, 0x02, 0x02, 0xcc},
+	{0xbf, 0xc0, 0x39, 0xcc},
+	{0xbf, 0xc1, 0x04, 0xcc},
+	{0xbf, 0xcc, 0x10, 0xcc},
+	{0xb6, 0x12, 0xf8, 0xcc},
+	{0xb6, 0x13, 0x13, 0xcc},
+	{0xb9, 0x12, 0x00, 0xcc},
+	{0xb9, 0x13, 0x0a, 0xcc},
+	{0xb9, 0x14, 0x0a, 0xcc},
+	{0xb9, 0x15, 0x0a, 0xcc},
+	{0xb9, 0x16, 0x0a, 0xcc},
+	{0xb8, 0x0c, 0x20, 0xcc},
+	{0xb8, 0x0d, 0x70, 0xcc},
+	{0xb9, 0x18, 0x00, 0xcc},
+	{0xb9, 0x19, 0x0f, 0xcc},
+	{0xb9, 0x1a, 0x0f, 0xcc},
+	{0xb9, 0x1b, 0x0f, 0xcc},
+	{0xb9, 0x1c, 0x0f, 0xcc},
+	{0xb3, 0x5c, 0x01, 0xcc},
+	{}
+};
+
+static const u8 hv7131r_initQVGA_data[][4] = {
+	{0xb3, 0x01, 0x01, 0xcc},
+	{0xb0, 0x03, 0x19, 0xcc},
+	{0xb0, 0x04, 0x02, 0xcc},
+	{0x00, 0x00, 0x20, 0xdd},
+	{0xb3, 0x00, 0x24, 0xcc},
+	{0xb3, 0x00, 0x25, 0xcc},
+	{0xb3, 0x08, 0x01, 0xcc},
+	{0xb3, 0x09, 0x0c, 0xcc},
+	{0xb3, 0x05, 0x01, 0xcc},
+	{0xb3, 0x06, 0x03, 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, 0x02, 0xcc},
+	{0xb3, 0x16, 0x02, 0xcc},
+	{0xb3, 0x17, 0x7f, 0xcc},
+	{0xb3, 0x34, 0x01, 0xcc},
+	{0xb3, 0x35, 0x91, 0xcc},
+	{0xb3, 0x00, 0x27, 0xcc},
+	{0xbc, 0x00, 0xd3, 0xcc},
+	{0xb8, 0x00, 0x23, 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, 0x58, 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, 0x8e, 0x00, 0xcc},
+	{0xb8, 0x8f, 0xff, 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, 0x03, 0xaa},
+	{0x00, 0x26, 0xa9, 0xaa},
+	{0x00, 0x27, 0x80, 0xaa},
+	{0x00, 0x30, 0x18, 0xaa},
+	{0xb6, 0x00, 0x00, 0xcc},
+	{0xb6, 0x03, 0x01, 0xcc},
+	{0xb6, 0x02, 0x40, 0xcc},
+	{0xb6, 0x05, 0x00, 0xcc},
+	{0xb6, 0x04, 0xf0, 0xcc},
+	{0xb6, 0x12, 0x78, 0xcc},
+	{0xb6, 0x18, 0x00, 0xcc},
+	{0xb6, 0x17, 0x96, 0xcc},
+	{0xb6, 0x16, 0x00, 0xcc},
+	{0xb6, 0x22, 0x12, 0xcc},
+	{0xb6, 0x23, 0x0b, 0xcc},
+	{0xb3, 0x02, 0x02, 0xcc},
+	{0xbf, 0xc0, 0x39, 0xcc},
+	{0xbf, 0xc1, 0x04, 0xcc},
+	{0xbf, 0xcc, 0x10, 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},
+	{0xb9, 0x12, 0x00, 0xcc},
+	{0xb9, 0x13, 0x0a, 0xcc},
+	{0xb9, 0x14, 0x0a, 0xcc},
+	{0xb9, 0x15, 0x0a, 0xcc},
+	{0xb9, 0x16, 0x0a, 0xcc},
+	{0xb9, 0x18, 0x00, 0xcc},
+	{0xb9, 0x19, 0x0f, 0xcc},
+	{0xb8, 0x0c, 0x20, 0xcc},
+	{0xb8, 0x0d, 0x70, 0xcc},
+	{0xb9, 0x1a, 0x0f, 0xcc},
+	{0xb9, 0x1b, 0x0f, 0xcc},
+	{0xb9, 0x1c, 0x0f, 0xcc},
+	{0xb6, 0x12, 0xf8, 0xcc},
+	{0xb6, 0x13, 0x13, 0xcc},
+	{0xb3, 0x5c, 0x01, 0xcc},
+	{}
+};
+
+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},	/* i2c add: 21 */
+	{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},	/* MVFP */
+	{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},	/* MVFP */
+	{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[][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},
+	{0xb0, 0x16, 0x01, 0xcc},
+	{0xb3, 0x35, 0xa1, 0xcc},	/* i2c add: 21 */
+	{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},	/* MVFP */
+	{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},	/* MVFP */
+	{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},
+	{0xbf, 0xc0, 0x26, 0xcc},
+	{0xbf, 0xc1, 0x02, 0xcc},
+	{0xbf, 0xcc, 0x04, 0xcc},
+	{0xb3, 0x5c, 0x01, 0xcc},
+	{0xb3, 0x01, 0x45, 0xcc},
+	{0x00, 0x77, 0x05, 0xaa},
+	{},
+};
+
+static const u8 ov7670_InitQVGA[][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},
+	{0xb0, 0x16, 0x01, 0xcc},
+	{0xb3, 0x35, 0xa1, 0xcc},	/* i2c add: 21 */
+	{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},	/* MVFP */
+	{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},	/* MVFP */
+	{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},
+	{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, 0x45, 0xcc},
+	{0x00, 0x77, 0x05, 0xaa},
+	{},
+};
+
+/* PO1200 - values from usbvm326.inf and ms-win trace */
+static const u8 po1200_gamma[17] = {
+	0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8,
+	0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff
+};
+static const u8 po1200_matrix[9] = {
+	0x60, 0xf9, 0xe5, 0xe7, 0x50, 0x05, 0xf3, 0xe6, 0x5e
+};
+static const u8 po1200_initVGA_data[][4] = {
+	{0xb0, 0x03, 0x19, 0xcc},	/* reset? */
+	{0xb0, 0x03, 0x19, 0xcc},
+/*	{0x00, 0x00, 0x33, 0xdd}, */
+	{0xb0, 0x04, 0x02, 0xcc},
+	{0xb0, 0x02, 0x02, 0xcc},
+	{0xb3, 0x5d, 0x00, 0xcc},
+	{0xb3, 0x01, 0x01, 0xcc},
+	{0xb3, 0x00, 0x64, 0xcc},
+	{0xb3, 0x00, 0x65, 0xcc},
+	{0xb3, 0x05, 0x01, 0xcc},
+	{0xb3, 0x06, 0x01, 0xcc},
+	{0xb3, 0x5c, 0x01, 0xcc},
+	{0xb3, 0x08, 0x01, 0xcc},
+	{0xb3, 0x09, 0x0c, 0xcc},
+	{0xb3, 0x00, 0x67, 0xcc},
+	{0xb3, 0x02, 0xb2, 0xcc},
+	{0xb3, 0x03, 0x18, 0xcc},
+	{0xb3, 0x04, 0x15, 0xcc},
+	{0xb3, 0x20, 0x00, 0xcc},
+	{0xb3, 0x21, 0x00, 0xcc},
+	{0xb3, 0x22, 0x02, 0xcc},
+	{0xb3, 0x23, 0x58, 0xcc},
+	{0xb3, 0x14, 0x00, 0xcc},
+	{0xb3, 0x15, 0x00, 0xcc},
+	{0xb3, 0x16, 0x03, 0xcc},
+	{0xb3, 0x17, 0x1f, 0xcc},
+	{0xbc, 0x00, 0x71, 0xcc},
+	{0xbc, 0x01, 0x01, 0xcc},
+	{0xb0, 0x54, 0x13, 0xcc},
+	{0xb3, 0x00, 0x67, 0xcc},
+	{0xb3, 0x34, 0x01, 0xcc},
+	{0xb3, 0x35, 0xdc, 0xcc},	/* i2c add: 5c */
+	{0x00, 0x03, 0x00, 0xaa},
+	{0x00, 0x12, 0x05, 0xaa},
+	{0x00, 0x13, 0x02, 0xaa},
+	{0x00, 0x1e, 0xc6, 0xaa},	/* h/v flip */
+	{0x00, 0x21, 0x00, 0xaa},
+	{0x00, 0x25, 0x02, 0xaa},
+	{0x00, 0x3c, 0x4f, 0xaa},
+	{0x00, 0x3f, 0xe0, 0xaa},
+	{0x00, 0x42, 0xff, 0xaa},
+	{0x00, 0x45, 0x34, 0xaa},
+	{0x00, 0x55, 0xfe, 0xaa},
+	{0x00, 0x59, 0xd3, 0xaa},
+	{0x00, 0x5e, 0x04, 0xaa},
+	{0x00, 0x61, 0xb8, 0xaa},	/* sharpness */
+	{0x00, 0x62, 0x02, 0xaa},
+	{0x00, 0xa7, 0x31, 0xaa},
+	{0x00, 0xa9, 0x66, 0xaa},
+	{0x00, 0xb0, 0x00, 0xaa},
+	{0x00, 0xb1, 0x00, 0xaa},
+	{0x00, 0xb3, 0x11, 0xaa},
+	{0x00, 0xb6, 0x26, 0xaa},
+	{0x00, 0xb7, 0x20, 0xaa},
+	{0x00, 0xba, 0x04, 0xaa},
+	{0x00, 0x88, 0x42, 0xaa},
+	{0x00, 0x89, 0x9a, 0xaa},
+	{0x00, 0x8a, 0x88, 0xaa},
+	{0x00, 0x8b, 0x8e, 0xaa},
+	{0x00, 0x8c, 0x3e, 0xaa},
+	{0x00, 0x8d, 0x90, 0xaa},
+	{0x00, 0x8e, 0x87, 0xaa},
+	{0x00, 0x8f, 0x96, 0xaa},
+	{0x00, 0x90, 0x3d, 0xaa},
+	{0x00, 0x64, 0x00, 0xaa},
+	{0x00, 0x65, 0x10, 0xaa},
+	{0x00, 0x66, 0x20, 0xaa},
+	{0x00, 0x67, 0x2b, 0xaa},
+	{0x00, 0x68, 0x36, 0xaa},
+	{0x00, 0x69, 0x49, 0xaa},
+	{0x00, 0x6a, 0x5a, 0xaa},
+	{0x00, 0x6b, 0x7f, 0xaa},
+	{0x00, 0x6c, 0x9b, 0xaa},
+	{0x00, 0x6d, 0xba, 0xaa},
+	{0x00, 0x6e, 0xd4, 0xaa},
+	{0x00, 0x6f, 0xea, 0xaa},
+	{0x00, 0x70, 0x00, 0xaa},
+	{0x00, 0x71, 0x10, 0xaa},
+	{0x00, 0x72, 0x20, 0xaa},
+	{0x00, 0x73, 0x2b, 0xaa},
+	{0x00, 0x74, 0x36, 0xaa},
+	{0x00, 0x75, 0x49, 0xaa},
+	{0x00, 0x76, 0x5a, 0xaa},
+	{0x00, 0x77, 0x7f, 0xaa},
+	{0x00, 0x78, 0x9b, 0xaa},
+	{0x00, 0x79, 0xba, 0xaa},
+	{0x00, 0x7a, 0xd4, 0xaa},
+	{0x00, 0x7b, 0xea, 0xaa},
+	{0x00, 0x7c, 0x00, 0xaa},
+	{0x00, 0x7d, 0x10, 0xaa},
+	{0x00, 0x7e, 0x20, 0xaa},
+	{0x00, 0x7f, 0x2b, 0xaa},
+	{0x00, 0x80, 0x36, 0xaa},
+	{0x00, 0x81, 0x49, 0xaa},
+	{0x00, 0x82, 0x5a, 0xaa},
+	{0x00, 0x83, 0x7f, 0xaa},
+	{0x00, 0x84, 0x9b, 0xaa},
+	{0x00, 0x85, 0xba, 0xaa},
+	{0x00, 0x86, 0xd4, 0xaa},
+	{0x00, 0x87, 0xea, 0xaa},
+	{0x00, 0x57, 0x2a, 0xaa},
+	{0x00, 0x03, 0x01, 0xaa},
+	{0x00, 0x04, 0x10, 0xaa},
+	{0x00, 0x05, 0x10, 0xaa},
+	{0x00, 0x06, 0x10, 0xaa},
+	{0x00, 0x07, 0x10, 0xaa},
+	{0x00, 0x08, 0x13, 0xaa},
+	{0x00, 0x0a, 0x00, 0xaa},
+	{0x00, 0x0b, 0x10, 0xaa},
+	{0x00, 0x0c, 0x20, 0xaa},
+	{0x00, 0x0d, 0x18, 0xaa},
+	{0x00, 0x22, 0x01, 0xaa},
+	{0x00, 0x23, 0x60, 0xaa},
+	{0x00, 0x25, 0x08, 0xaa},
+	{0x00, 0x26, 0x82, 0xaa},
+	{0x00, 0x2e, 0x0f, 0xaa},
+	{0x00, 0x2f, 0x1e, 0xaa},
+	{0x00, 0x30, 0x2d, 0xaa},
+	{0x00, 0x31, 0x3c, 0xaa},
+	{0x00, 0x32, 0x4b, 0xaa},
+	{0x00, 0x33, 0x5a, 0xaa},
+	{0x00, 0x34, 0x69, 0xaa},
+	{0x00, 0x35, 0x78, 0xaa},
+	{0x00, 0x36, 0x87, 0xaa},
+	{0x00, 0x37, 0x96, 0xaa},
+	{0x00, 0x38, 0xa5, 0xaa},
+	{0x00, 0x39, 0xb4, 0xaa},
+	{0x00, 0x3a, 0xc3, 0xaa},
+	{0x00, 0x3b, 0xd2, 0xaa},
+	{0x00, 0x3c, 0xe1, 0xaa},
+	{0x00, 0x3e, 0xff, 0xaa},
+	{0x00, 0x3f, 0xff, 0xaa},
+	{0x00, 0x40, 0xff, 0xaa},
+	{0x00, 0x41, 0xff, 0xaa},
+	{0x00, 0x42, 0xff, 0xaa},
+	{0x00, 0x43, 0xff, 0xaa},
+	{0x00, 0x03, 0x00, 0xaa},
+	{0x00, 0x03, 0x00, 0xaa},
+	{0x00, 0x20, 0xc4, 0xaa},
+	{0x00, 0x13, 0x03, 0xaa},
+	{0x00, 0x3c, 0x50, 0xaa},
+	{0x00, 0x61, 0x6a, 0xaa},	/* sharpness? */
+	{0x00, 0x51, 0x5b, 0xaa},
+	{0x00, 0x52, 0x91, 0xaa},
+	{0x00, 0x53, 0x4c, 0xaa},
+	{0x00, 0x54, 0x50, 0xaa},
+	{0x00, 0x56, 0x02, 0xaa},
+	{0xb6, 0x00, 0x00, 0xcc},
+	{0xb6, 0x03, 0x03, 0xcc},
+	{0xb6, 0x02, 0x20, 0xcc},
+	{0xb6, 0x05, 0x02, 0xcc},
+	{0xb6, 0x04, 0x58, 0xcc},
+	{0xb6, 0x12, 0xf8, 0xcc},
+	{0xb6, 0x13, 0x21, 0xcc},
+	{0xb6, 0x18, 0x03, 0xcc},
+	{0xb6, 0x17, 0xa9, 0xcc},
+	{0xb6, 0x16, 0x80, 0xcc},
+	{0xb6, 0x22, 0x12, 0xcc},
+	{0xb6, 0x23, 0x0b, 0xcc},
+	{0xbf, 0xc0, 0x39, 0xcc},
+	{0xbf, 0xc1, 0x04, 0xcc},
+	{0xbf, 0xcc, 0x00, 0xcc},
+	{0xb8, 0x06, 0x20, 0xcc},
+	{0xb8, 0x07, 0x03, 0xcc},
+	{0xb8, 0x08, 0x58, 0xcc},
+	{0xb8, 0x09, 0x02, 0xcc},
+	{0xb3, 0x01, 0x41, 0xcc},
+	{0x00, 0x03, 0x00, 0xaa},
+	{0x00, 0xd9, 0x0f, 0xaa},
+	{0x00, 0xda, 0xaa, 0xaa},
+	{0x00, 0xd9, 0x10, 0xaa},
+	{0x00, 0xda, 0xaa, 0xaa},
+	{0x00, 0xd9, 0x11, 0xaa},
+	{0x00, 0xda, 0x00, 0xaa},
+	{0x00, 0xd9, 0x12, 0xaa},
+	{0x00, 0xda, 0xff, 0xaa},
+	{0x00, 0xd9, 0x13, 0xaa},
+	{0x00, 0xda, 0xff, 0xaa},
+	{0x00, 0xe8, 0x11, 0xaa},
+	{0x00, 0xe9, 0x12, 0xaa},
+	{0x00, 0xea, 0x5c, 0xaa},
+	{0x00, 0xeb, 0xff, 0xaa},
+	{0x00, 0xd8, 0x80, 0xaa},
+	{0x00, 0xe6, 0x02, 0xaa},
+	{0x00, 0xd6, 0x40, 0xaa},
+	{0x00, 0xe3, 0x05, 0xaa},
+	{0x00, 0xe0, 0x40, 0xaa},
+	{0x00, 0xde, 0x03, 0xaa},
+	{0x00, 0xdf, 0x03, 0xaa},
+	{0x00, 0xdb, 0x02, 0xaa},
+	{0x00, 0xdc, 0x00, 0xaa},
+	{0x00, 0xdd, 0x03, 0xaa},
+	{0x00, 0xe1, 0x08, 0xaa},
+	{0x00, 0xe2, 0x01, 0xaa},
+	{0x00, 0xd6, 0x40, 0xaa},
+	{0x00, 0xe4, 0x40, 0xaa},
+	{0x00, 0xa8, 0x8f, 0xaa},
+	{0x00, 0xb4, 0x16, 0xaa},
+	{0xb0, 0x02, 0x06, 0xcc},
+	{0xb0, 0x18, 0x06, 0xcc},
+	{0xb0, 0x19, 0x06, 0xcc},
+	{0xb3, 0x5d, 0x18, 0xcc},
+	{0xb3, 0x05, 0x00, 0xcc},
+	{0xb3, 0x06, 0x00, 0xcc},
+	{0x00, 0xb4, 0x0e, 0xaa},
+	{0x00, 0xb5, 0x49, 0xaa},
+	{0x00, 0xb6, 0x1c, 0xaa},
+	{0x00, 0xb7, 0x96, 0xaa},
+/* end of usbvm326.inf - start of ms-win trace */
+	{0xb6, 0x12, 0xf8, 0xcc},
+	{0xb6, 0x13, 0x3d, 0xcc},
+/*read b306*/
+	{0x00, 0x03, 0x00, 0xaa},
+	{0x00, 0x1a, 0x09, 0xaa},
+	{0x00, 0x1b, 0x8a, 0xaa},
+/*read b827*/
+	{0xb8, 0x27, 0x00, 0xcc},
+	{0xb8, 0x26, 0x60, 0xcc},
+	{0xb8, 0x26, 0x60, 0xcc},
+/*gamma - to do?*/
+	{0x00, 0x03, 0x00, 0xaa},
+	{0x00, 0xae, 0x84, 0xaa},
+/*gamma again*/
+	{0x00, 0x03, 0x00, 0xaa},
+	{0x00, 0x96, 0xa0, 0xaa},
+/*matrix*/
+	{0x00, 0x03, 0x00, 0xaa},
+	{0x00, 0x91, 0x35, 0xaa},
+	{0x00, 0x92, 0x22, 0xaa},
+/*gamma*/
+	{0x00, 0x03, 0x00, 0xaa},
+	{0x00, 0x95, 0x85, 0xaa},
+/*matrix*/
+	{0x00, 0x03, 0x00, 0xaa},
+	{0x00, 0x4d, 0x20, 0xaa},
+	{0xb8, 0x22, 0x40, 0xcc},
+	{0xb8, 0x23, 0x40, 0xcc},
+	{0xb8, 0x24, 0x40, 0xcc},
+	{0xb8, 0x81, 0x09, 0xcc},
+	{0x00, 0x00, 0x64, 0xdd},
+	{0x00, 0x03, 0x01, 0xaa},
+/*read 46*/
+	{0x00, 0x46, 0x3c, 0xaa},
+	{0x00, 0x03, 0x00, 0xaa},
+	{0x00, 0x16, 0x40, 0xaa},
+	{0x00, 0x17, 0x40, 0xaa},
+	{0x00, 0x18, 0x40, 0xaa},
+	{0x00, 0x19, 0x41, 0xaa},
+	{0x00, 0x03, 0x01, 0xaa},
+	{0x00, 0x46, 0x3c, 0xaa},
+	{0x00, 0x00, 0x18, 0xdd},
+/*read bfff*/
+	{0x00, 0x03, 0x00, 0xaa},
+	{0x00, 0xb4, 0x1c, 0xaa},
+	{0x00, 0xb5, 0x92, 0xaa},
+	{0x00, 0xb6, 0x39, 0xaa},
+	{0x00, 0xb7, 0x24, 0xaa},
+/*write 89 0400 1415*/
+	{}
+};
+
+static const u8 poxxxx_init_common[][4] = {
+	{0xb3, 0x00, 0x04, 0xcc},
+	{0x00, 0x00, 0x10, 0xdd},
+	{0xb3, 0x00, 0x64, 0xcc},
+	{0x00, 0x00, 0x10, 0xdd},
+	{0xb3, 0x00, 0x65, 0xcc},
+	{0x00, 0x00, 0x10, 0xdd},
+	{0xb3, 0x00, 0x67, 0xcc},
+	{0xb0, 0x03, 0x09, 0xcc},
+	{0xb3, 0x05, 0x00, 0xcc},
+	{0xb3, 0x06, 0x00, 0xcc},
+	{0xb3, 0x5c, 0x01, 0xcc},
+	{0xb3, 0x08, 0x01, 0xcc},
+	{0xb3, 0x09, 0x0c, 0xcc},
+	{0xb3, 0x34, 0x01, 0xcc},
+	{0xb3, 0x35, 0xf6, 0xcc},	/* i2c add: 76 */
+	{0xb3, 0x02, 0xb0, 0xcc},
+	{0xb3, 0x03, 0x18, 0xcc},
+	{0xb3, 0x04, 0x15, 0xcc},
+	{0xb3, 0x20, 0x00, 0xcc},
+	{0xb3, 0x21, 0x00, 0xcc},
+	{0xb3, 0x22, 0x04, 0xcc},	/* sensor height = 1024 */
+	{0xb3, 0x23, 0x00, 0xcc},
+	{0xb3, 0x14, 0x00, 0xcc},
+	{0xb3, 0x15, 0x00, 0xcc},
+	{0xb3, 0x16, 0x04, 0xcc},	/* sensor width = 1280 */
+	{0xb3, 0x17, 0xff, 0xcc},
+	{0xb3, 0x2c, 0x03, 0xcc},
+	{0xb3, 0x2d, 0x56, 0xcc},
+	{0xb3, 0x2e, 0x02, 0xcc},
+	{0xb3, 0x2f, 0x0a, 0xcc},
+	{0xb3, 0x40, 0x00, 0xcc},
+	{0xb3, 0x41, 0x34, 0xcc},
+	{0xb3, 0x42, 0x01, 0xcc},
+	{0xb3, 0x43, 0xe0, 0xcc},
+	{0xbc, 0x00, 0x71, 0xcc},
+	{0xbc, 0x01, 0x01, 0xcc},
+	{0xb3, 0x01, 0x41, 0xcc},
+	{0xb3, 0x4d, 0x00, 0xcc},
+	{0x00, 0x0b, 0x2a, 0xaa},
+	{0x00, 0x0e, 0x03, 0xaa},
+	{0x00, 0x0f, 0xea, 0xaa},
+	{0x00, 0x12, 0x08, 0xaa},
+	{0x00, 0x1e, 0x06, 0xaa},
+	{0x00, 0x21, 0x00, 0xaa},
+	{0x00, 0x31, 0x1f, 0xaa},
+	{0x00, 0x33, 0x38, 0xaa},
+	{0x00, 0x36, 0xc0, 0xaa},
+	{0x00, 0x37, 0xc8, 0xaa},
+	{0x00, 0x3b, 0x36, 0xaa},
+	{0x00, 0x4b, 0xfe, 0xaa},
+	{0x00, 0x4d, 0x2e, 0xaa},
+	{0x00, 0x51, 0x1c, 0xaa},
+	{0x00, 0x52, 0x01, 0xaa},
+	{0x00, 0x55, 0x0a, 0xaa},
+	{0x00, 0x56, 0x0a, 0xaa},
+	{0x00, 0x57, 0x07, 0xaa},
+	{0x00, 0x58, 0x07, 0xaa},
+	{0x00, 0x59, 0x04, 0xaa},
+	{0x00, 0x70, 0x68, 0xaa},
+	{0x00, 0x71, 0x04, 0xaa},
+	{0x00, 0x72, 0x10, 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, 0x8b, 0x25, 0xaa},
+	{0x00, 0x8c, 0x00, 0xaa},
+	{0x00, 0x8d, 0x86, 0xaa},
+	{0x00, 0x8e, 0x82, 0xaa},
+	{0x00, 0x8f, 0x2d, 0xaa},
+	{0x00, 0x90, 0x8b, 0xaa},
+	{0x00, 0x91, 0x81, 0xaa},
+	{0x00, 0x92, 0x81, 0xaa},
+	{0x00, 0x93, 0x23, 0xaa},
+	{0x00, 0xa3, 0x2a, 0xaa},
+	{0x00, 0xa4, 0x03, 0xaa},
+	{0x00, 0xa5, 0xea, 0xaa},
+	{0x00, 0xb0, 0x68, 0xaa},
+	{0x00, 0xbc, 0x04, 0xaa},
+	{0x00, 0xbe, 0x3b, 0xaa},
+	{0x00, 0x4e, 0x40, 0xaa},
+	{0x00, 0x06, 0x04, 0xaa},
+	{0x00, 0x07, 0x03, 0xaa},
+	{0x00, 0xcd, 0x18, 0xaa},
+	{0x00, 0x28, 0x03, 0xaa},
+	{0x00, 0x29, 0xef, 0xaa},
+/* reinit on alt 2 (qvga) or alt7 (vga) */
+	{0xb3, 0x05, 0x00, 0xcc},
+	{0xb3, 0x06, 0x00, 0xcc},
+	{0xb8, 0x00, 0x01, 0xcc},
+
+	{0x00, 0x1d, 0x85, 0xaa},
+	{0x00, 0x1e, 0xc6, 0xaa},
+	{0x00, 0x00, 0x40, 0xdd},
+	{0x00, 0x1d, 0x05, 0xaa},
+	{}
+};
+static const u8 poxxxx_gamma[][4] = {
+	{0x00, 0xd6, 0x22, 0xaa},	/* gamma 0 */
+	{0x00, 0x73, 0x00, 0xaa},
+	{0x00, 0x74, 0x0a, 0xaa},
+	{0x00, 0x75, 0x16, 0xaa},
+	{0x00, 0x76, 0x25, 0xaa},
+	{0x00, 0x77, 0x34, 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},	/* gamma 1 */
+	{0x00, 0x73, 0x00, 0xaa},
+	{0x00, 0x74, 0x0a, 0xaa},
+	{0x00, 0x75, 0x16, 0xaa},
+	{0x00, 0x76, 0x25, 0xaa},
+	{0x00, 0x77, 0x34, 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},	/* gamma 2 */
+	{0x00, 0x73, 0x00, 0xaa},
+	{0x00, 0x74, 0x0a, 0xaa},
+	{0x00, 0x75, 0x16, 0xaa},
+	{0x00, 0x76, 0x25, 0xaa},
+	{0x00, 0x77, 0x34, 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},
+	{}
+};
+static const u8 poxxxx_init_start_3[][4] = {
+	{0x00, 0xb8, 0x28, 0xaa},
+	{0x00, 0xb9, 0x1e, 0xaa},
+	{0x00, 0xb6, 0x14, 0xaa},
+	{0x00, 0xb7, 0x0f, 0xaa},
+	{0x00, 0x5c, 0x10, 0xaa},
+	{0x00, 0x5d, 0x18, 0xaa},
+	{0x00, 0x5e, 0x24, 0xaa},
+	{0x00, 0x5f, 0x24, 0xaa},
+	{0x00, 0x86, 0x1a, 0xaa},
+	{0x00, 0x60, 0x00, 0xaa},
+	{0x00, 0x61, 0x1b, 0xaa},
+	{0x00, 0x62, 0x30, 0xaa},
+	{0x00, 0x63, 0x40, 0xaa},
+	{0x00, 0x87, 0x1a, 0xaa},
+	{0x00, 0x64, 0x00, 0xaa},
+	{0x00, 0x65, 0x08, 0xaa},
+	{0x00, 0x66, 0x10, 0xaa},
+	{0x00, 0x67, 0x20, 0xaa},
+	{0x00, 0x88, 0x10, 0xaa},
+	{0x00, 0x68, 0x00, 0xaa},
+	{0x00, 0x69, 0x08, 0xaa},
+	{0x00, 0x6a, 0x0f, 0xaa},
+	{0x00, 0x6b, 0x0f, 0xaa},
+	{0x00, 0x89, 0x07, 0xaa},
+	{0x00, 0xd5, 0x4c, 0xaa},
+	{0x00, 0x0a, 0x00, 0xaa},
+	{0x00, 0x0b, 0x2a, 0xaa},
+	{0x00, 0x0e, 0x03, 0xaa},
+	{0x00, 0x0f, 0xea, 0xaa},
+	{0x00, 0xa2, 0x00, 0xaa},
+	{0x00, 0xa3, 0x2a, 0xaa},
+	{0x00, 0xa4, 0x03, 0xaa},
+	{0x00, 0xa5, 0xea, 0xaa},
+	{}
+};
+static const u8 poxxxx_initVGA[][4] = {
+	{0x00, 0x20, 0x11, 0xaa},
+	{0x00, 0x33, 0x38, 0xaa},
+	{0x00, 0xbb, 0x0d, 0xaa},
+	{0xb3, 0x22, 0x01, 0xcc},	/* change to 640x480 */
+	{0xb3, 0x23, 0xe0, 0xcc},
+	{0xb3, 0x16, 0x02, 0xcc},
+	{0xb3, 0x17, 0x7f, 0xcc},
+	{0xb3, 0x02, 0xb0, 0xcc},
+	{0xb3, 0x06, 0x00, 0xcc},
+	{0xb3, 0x5c, 0x01, 0xcc},
+	{0x00, 0x04, 0x06, 0xaa},
+	{0x00, 0x05, 0x3f, 0xaa},
+	{0x00, 0x04, 0x00, 0xdd},	/* delay 1s */
+	{}
+};
+static const u8 poxxxx_initQVGA[][4] = {
+	{0x00, 0x20, 0x33, 0xaa},
+	{0x00, 0x33, 0x38, 0xaa},
+	{0x00, 0xbb, 0x0d, 0xaa},
+	{0xb3, 0x22, 0x00, 0xcc},	/* change to 320x240 */
+	{0xb3, 0x23, 0xf0, 0xcc},
+	{0xb3, 0x16, 0x01, 0xcc},
+	{0xb3, 0x17, 0x3f, 0xcc},
+	{0xb3, 0x02, 0xb0, 0xcc},
+	{0xb3, 0x06, 0x01, 0xcc},
+	{0xb3, 0x5c, 0x00, 0xcc},
+	{0x00, 0x04, 0x06, 0xaa},
+	{0x00, 0x05, 0x3f, 0xaa},
+	{0x00, 0x04, 0x00, 0xdd},	/* delay 1s */
+	{}
+};
+static const u8 poxxxx_init_end_1[][4] = {
+	{0x00, 0x47, 0x25, 0xaa},
+	{0x00, 0x48, 0x80, 0xaa},
+	{0x00, 0x49, 0x1f, 0xaa},
+	{0x00, 0x4a, 0x40, 0xaa},
+	{0x00, 0x44, 0x40, 0xaa},
+	{0x00, 0xab, 0x4a, 0xaa},
+	{0x00, 0xb1, 0x00, 0xaa},
+	{0x00, 0xb2, 0x04, 0xaa},
+	{0x00, 0xb3, 0x08, 0xaa},
+	{0x00, 0xb4, 0x0b, 0xaa},
+	{0x00, 0xb5, 0x0d, 0xaa},
+	{}
+};
+static const u8 poxxxx_init_end_2[][4] = {
+	{0x00, 0x1d, 0x85, 0xaa},
+	{0x00, 0x1e, 0x06, 0xaa},
+	{0x00, 0x1d, 0x05, 0xaa},
+	{}
+};
+
+struct sensor_info {
+	s8 sensorId;
+	u8 I2cAdd;
+	u8 IdAdd;
+	u16 VpId;
+	u8 m1;
+	u8 m2;
+	u8 op;
+};
+
+/* probe values */
+static const struct sensor_info vc0321_probe_data[] = {
+/*      sensorId,	   I2cAdd,	IdAdd,  VpId,  m1,    m2,  op */
+/* 0 OV9640 */
+	{-1,		    0x80 | 0x30, 0x0a, 0x0000, 0x25, 0x24, 0x05},
+/* 1 ICM108T (may respond on IdAdd == 0x83 - tested in vc032x_probe_sensor) */
+	{-1,		    0x80 | 0x20, 0x82, 0x0000, 0x24, 0x25, 0x01},
+/* 2 PO2130 (may detect PO3130NC - tested in vc032x_probe_sensor)*/
+	{-1,		    0x80 | 0x76, 0x00, 0x0000, 0x24, 0x25, 0x01},
+/* 3 MI1310 */
+	{-1,		    0x80 | 0x5d, 0x00, 0x0000, 0x24, 0x25, 0x01},
+/* 4 MI360 - tested in vc032x_probe_sensor */
+/*	{SENSOR_MI0360,	    0x80 | 0x5d, 0x00, 0x8243, 0x24, 0x25, 0x01}, */
+/* 5 7131R */
+	{SENSOR_HV7131R,    0x80 | 0x11, 0x00, 0x0209, 0x24, 0x25, 0x01},
+/* 6 OV7649 */
+	{-1,		    0x80 | 0x21, 0x0a, 0x0000, 0x21, 0x20, 0x05},
+/* 7 PAS302BCW */
+	{-1,		    0x80 | 0x40, 0x00, 0x0000, 0x20, 0x22, 0x05},
+/* 8 OV7660 */
+	{SENSOR_OV7660,     0x80 | 0x21, 0x0a, 0x7660, 0x26, 0x26, 0x05},
+/* 9 PO3130NC - (tested in vc032x_probe_sensor) */
+/*	{SENSOR_PO3130NC,   0x80 | 0x76, 0x00, 0x3130, 0x24, 0x25, 0x01}, */
+/* 10 PO1030KC */
+	{-1,		    0x80 | 0x6e, 0x00, 0x0000, 0x24, 0x25, 0x01},
+/* 11 MI1310_SOC */
+	{SENSOR_MI1310_SOC, 0x80 | 0x5d, 0x00, 0x143a, 0x24, 0x25, 0x01},
+/* 12 OV9650 */
+	{-1,		    0x80 | 0x30, 0x0a, 0x0000, 0x25, 0x24, 0x05},
+/* 13 S5K532 */
+	{-1,		    0x80 | 0x11, 0x39, 0x0000, 0x24, 0x25, 0x01},
+/* 14 MI360_SOC - ??? */
+/* 15 PO1200N */
+	{SENSOR_PO1200,     0x80 | 0x5c, 0x00, 0x1200, 0x67, 0x67, 0x01},
+/* 16 PO3030K */
+	{-1,		    0x80 | 0x18, 0x00, 0x0000, 0x24, 0x25, 0x01},
+/* 17 PO2030 */
+	{-1,		    0x80 | 0x6e, 0x00, 0x0000, 0x24, 0x25, 0x01},
+/* ?? */
+	{-1,		    0x80 | 0x56, 0x01, 0x0000, 0x64, 0x67, 0x01},
+	{SENSOR_MI1320,     0x80 | 0x48, 0x00, 0x148c, 0x64, 0x65, 0x01},
+};
+static const struct sensor_info vc0323_probe_data[] = {
+/*      sensorId,	   I2cAdd,	IdAdd,  VpId,  m1,    m2,  op */
+/* 0 OV9640 */
+	{-1,		    0x80 | 0x30, 0x0a, 0x0000, 0x25, 0x24, 0x05},
+/* 1 ICM108T (may respond on IdAdd == 0x83 - tested in vc032x_probe_sensor) */
+	{-1,		    0x80 | 0x20, 0x82, 0x0000, 0x24, 0x25, 0x01},
+/* 2 PO2130 (may detect PO3130NC - tested in vc032x_probe_sensor)*/
+	{-1,		    0x80 | 0x76, 0x00, 0x0000, 0x24, 0x25, 0x01},
+/* 3 MI1310 */
+	{-1,		    0x80 | 0x5d, 0x00, 0x0000, 0x24, 0x25, 0x01},
+/* 4 MI360 - tested in vc032x_probe_sensor */
+/*	{SENSOR_MI0360,	    0x80 | 0x5d, 0x00, 0x8243, 0x24, 0x25, 0x01}, */
+/* 5 7131R */
+	{SENSOR_HV7131R,    0x80 | 0x11, 0x00, 0x0209, 0x24, 0x25, 0x01},
+/* 6 OV7649 */
+	{-1,		    0x80 | 0x21, 0x0a, 0x0000, 0x21, 0x20, 0x05},
+/* 7 PAS302BCW */
+	{-1,		    0x80 | 0x40, 0x00, 0x0000, 0x20, 0x22, 0x05},
+/* 8 OV7660 */
+	{SENSOR_OV7660,     0x80 | 0x21, 0x0a, 0x7660, 0x26, 0x26, 0x05},
+/* 9 PO3130NC - (tested in vc032x_probe_sensor) */
+/*	{SENSOR_PO3130NC,   0x80 | 0x76, 0x00, 0x3130, 0x24, 0x25, 0x01}, */
+/* 10 PO1030KC */
+	{-1,		    0x80 | 0x6e, 0x00, 0x0000, 0x24, 0x25, 0x01},
+/* 11 MI1310_SOC */
+	{SENSOR_MI1310_SOC, 0x80 | 0x5d, 0x00, 0x143a, 0x24, 0x25, 0x01},
+/* 12 OV9650 */
+	{-1,		    0x80 | 0x30, 0x0a, 0x0000, 0x25, 0x24, 0x05},
+/* 13 S5K532 */
+	{-1,		    0x80 | 0x11, 0x39, 0x0000, 0x24, 0x25, 0x01},
+/* 14 MI360_SOC - ??? */
+/* 15 PO1200N */
+	{SENSOR_PO1200,     0x80 | 0x5c, 0x00, 0x1200, 0x67, 0x67, 0x01},
+/* 16 ?? */
+	{-1,		    0x80 | 0x2d, 0x00, 0x0000, 0x65, 0x67, 0x01},
+/* 17 PO2030 */
+	{-1,		    0x80 | 0x6e, 0x00, 0x0000, 0x24, 0x25, 0x01},
+/* ?? */
+	{-1,		    0x80 | 0x56, 0x01, 0x0000, 0x64, 0x67, 0x01},
+	{SENSOR_MI1320_SOC, 0x80 | 0x48, 0x00, 0x148c, 0x64, 0x67, 0x01},
+/*fixme: not in the ms-win probe - may be found before? */
+	{SENSOR_OV7670,     0x80 | 0x21, 0x0a, 0x7673, 0x66, 0x67, 0x05},
+};
+
+/* read 'len' bytes in gspca_dev->usb_buf */
+static void reg_r_i(struct gspca_dev *gspca_dev,
+		  u16 req,
+		  u16 index,
+		  u16 len)
+{
+	int ret;
+
+	if (gspca_dev->usb_err < 0)
+		return;
+	ret = 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);
+	if (ret < 0) {
+		pr_err("reg_r err %d\n", ret);
+		gspca_dev->usb_err = ret;
+	}
+}
+static void reg_r(struct gspca_dev *gspca_dev,
+		  u16 req,
+		  u16 index,
+		  u16 len)
+{
+	reg_r_i(gspca_dev, req, index, len);
+	if (gspca_dev->usb_err < 0)
+		return;
+	if (len == 1)
+		PDEBUG(D_USBI, "GET %02x 0001 %04x %02x", req, index,
+				gspca_dev->usb_buf[0]);
+	else
+		PDEBUG(D_USBI, "GET %02x 0001 %04x %*ph",
+				req, index, 3, gspca_dev->usb_buf);
+}
+
+static void reg_w_i(struct gspca_dev *gspca_dev,
+			    u16 req,
+			    u16 value,
+			    u16 index)
+{
+	int ret;
+
+	if (gspca_dev->usb_err < 0)
+		return;
+	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) {
+		pr_err("reg_w err %d\n", ret);
+		gspca_dev->usb_err = ret;
+	}
+}
+static void reg_w(struct gspca_dev *gspca_dev,
+			    u16 req,
+			    u16 value,
+			    u16 index)
+{
+	if (gspca_dev->usb_err < 0)
+		return;
+	PDEBUG(D_USBO, "SET %02x %04x %04x", req, value, index);
+	reg_w_i(gspca_dev, req, value, index);
+}
+
+static u16 read_sensor_register(struct gspca_dev *gspca_dev,
+				u16 address)
+{
+	u8 ldata, mdata, hdata;
+	int retry = 50;
+
+	reg_r(gspca_dev, 0xa1, 0xb33f, 1);
+	if (!(gspca_dev->usb_buf[0] & 0x02)) {
+		pr_err("I2c Bus Busy Wait %02x\n", gspca_dev->usb_buf[0]);
+		return 0;
+	}
+	reg_w(gspca_dev, 0xa0, address, 0xb33a);
+	reg_w(gspca_dev, 0xa0, 0x02, 0xb339);
+
+	do {
+		reg_r(gspca_dev, 0xa1, 0xb33b, 1);
+		if (gspca_dev->usb_buf[0] == 0x00)
+			break;
+		msleep(40);
+	} while (--retry >= 0);
+
+	reg_r(gspca_dev, 0xa1, 0xb33e, 1);
+	ldata = 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);
+	hdata = gspca_dev->usb_buf[0];
+	if (hdata != 0 && mdata != 0 && ldata != 0)
+		PDEBUG(D_PROBE, "Read Sensor %02x%02x %02x",
+			hdata, mdata, ldata);
+	reg_r(gspca_dev, 0xa1, 0xb334, 1);
+	if (gspca_dev->usb_buf[0] == 0x02)
+		return (hdata << 8) + mdata;
+	return hdata;
+}
+
+static int vc032x_probe_sensor(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int i, n;
+	u16 value;
+	const struct sensor_info *ptsensor_info;
+
+/*fixme: should also check the other sensor (back mi1320_soc, front mc501cb)*/
+	if (sd->flags & FL_SAMSUNG) {
+		reg_w(gspca_dev, 0xa0, 0x01, 0xb301);
+		reg_w(gspca_dev, 0x89, 0xf0ff, 0xffff);
+						/* select the back sensor */
+	}
+
+	reg_r(gspca_dev, 0xa1, 0xbfcf, 1);
+	PDEBUG(D_PROBE, "vc032%d check sensor header %02x",
+		sd->bridge == BRIDGE_VC0321 ? 1 : 3, gspca_dev->usb_buf[0]);
+	if (sd->bridge == BRIDGE_VC0321) {
+		ptsensor_info = vc0321_probe_data;
+		n = ARRAY_SIZE(vc0321_probe_data);
+	} else {
+		ptsensor_info = vc0323_probe_data;
+		n = ARRAY_SIZE(vc0323_probe_data);
+	}
+	for (i = 0; i < n; i++) {
+		reg_w(gspca_dev, 0xa0, 0x02, 0xb334);
+		reg_w(gspca_dev, 0xa0, ptsensor_info->m1, 0xb300);
+		reg_w(gspca_dev, 0xa0, ptsensor_info->m2, 0xb300);
+		reg_w(gspca_dev, 0xa0, 0x01, 0xb308);
+		reg_w(gspca_dev, 0xa0, 0x0c, 0xb309);
+		reg_w(gspca_dev, 0xa0, ptsensor_info->I2cAdd, 0xb335);
+		reg_w(gspca_dev, 0xa0, ptsensor_info->op, 0xb301);
+		value = read_sensor_register(gspca_dev, ptsensor_info->IdAdd);
+		if (value == 0 && ptsensor_info->IdAdd == 0x82)
+			value = read_sensor_register(gspca_dev, 0x83);
+		if (value != 0) {
+			PDEBUG(D_PROBE, "Sensor ID %04x (%d)", value, i);
+			if (value == ptsensor_info->VpId)
+				return ptsensor_info->sensorId;
+
+			switch (value) {
+			case 0x3130:
+				return SENSOR_PO3130NC;
+			case 0x7673:
+				return SENSOR_OV7670;
+			case 0x8243:
+				return SENSOR_MI0360;
+			}
+		}
+		ptsensor_info++;
+	}
+	return -1;
+}
+
+static void i2c_write(struct gspca_dev *gspca_dev,
+			u8 reg, const u8 *val,
+			u8 size)		/* 1 or 2 */
+{
+	int retry;
+
+	if (gspca_dev->usb_err < 0)
+		return;
+	if (size == 1)
+		PDEBUG(D_USBO, "i2c_w %02x %02x", reg, *val);
+	else
+		PDEBUG(D_USBO, "i2c_w %02x %02x%02x", reg, *val, val[1]);
+	reg_r_i(gspca_dev, 0xa1, 0xb33f, 1);
+/*fixme:should check if (!(gspca_dev->usb_buf[0] & 0x02)) error*/
+	reg_w_i(gspca_dev, 0xa0, size, 0xb334);
+	reg_w_i(gspca_dev, 0xa0, reg, 0xb33a);
+	reg_w_i(gspca_dev, 0xa0, val[0], 0xb336);
+	if (size > 1)
+		reg_w_i(gspca_dev, 0xa0, val[1], 0xb337);
+	reg_w_i(gspca_dev, 0xa0, 0x01, 0xb339);
+	retry = 4;
+	do {
+		reg_r_i(gspca_dev, 0xa1, 0xb33b, 1);
+		if (gspca_dev->usb_buf[0] == 0)
+			break;
+		msleep(20);
+	} while (--retry > 0);
+	if (retry <= 0)
+		pr_err("i2c_write timeout\n");
+}
+
+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, 0xa0, tab[j], ad++);
+}
+
+static void usb_exchange(struct gspca_dev *gspca_dev,
+			const u8 data[][4])
+{
+	int i = 0;
+
+	for (;;) {
+		switch (data[i][3]) {
+		default:
+			return;
+		case 0xcc:			/* normal write */
+			reg_w(gspca_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][1] * 256 + data[i][2] + 10);
+			break;
+		}
+		i++;
+	}
+	/*not reached*/
+}
+
+
+/* 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;
+
+	sd->bridge = id->driver_info >> 8;
+	sd->flags = id->driver_info & 0xff;
+
+	if (id->idVendor == 0x046d &&
+	    (id->idProduct == 0x0892 || id->idProduct == 0x0896))
+		sd->sensor = SENSOR_POxxxx;	/* no probe */
+
+	return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct cam *cam;
+	int sensor;
+	/* number of packets per ISOC message */
+	static u8 npkt[NSENSORS] = {
+		[SENSOR_HV7131R] =	64,
+		[SENSOR_MI0360] =	32,
+		[SENSOR_MI1310_SOC] =	32,
+		[SENSOR_MI1320] =	64,
+		[SENSOR_MI1320_SOC] =	128,
+		[SENSOR_OV7660] =	32,
+		[SENSOR_OV7670] =	64,
+		[SENSOR_PO1200] =	128,
+		[SENSOR_PO3130NC] =	128,
+		[SENSOR_POxxxx] =	128,
+	};
+
+	if (sd->sensor != SENSOR_POxxxx)
+		sensor = vc032x_probe_sensor(gspca_dev);
+	else
+		sensor = sd->sensor;
+
+	switch (sensor) {
+	case -1:
+		pr_err("Unknown sensor...\n");
+		return -EINVAL;
+	case SENSOR_HV7131R:
+		PDEBUG(D_PROBE, "Find Sensor HV7131R");
+		break;
+	case SENSOR_MI0360:
+		PDEBUG(D_PROBE, "Find Sensor MI0360");
+		sd->bridge = BRIDGE_VC0323;
+		break;
+	case SENSOR_MI1310_SOC:
+		PDEBUG(D_PROBE, "Find Sensor MI1310_SOC");
+		break;
+	case SENSOR_MI1320:
+		PDEBUG(D_PROBE, "Find Sensor MI1320");
+		break;
+	case SENSOR_MI1320_SOC:
+		PDEBUG(D_PROBE, "Find Sensor MI1320_SOC");
+		break;
+	case SENSOR_OV7660:
+		PDEBUG(D_PROBE, "Find Sensor OV7660");
+		break;
+	case SENSOR_OV7670:
+		PDEBUG(D_PROBE, "Find Sensor OV7670");
+		break;
+	case SENSOR_PO1200:
+		PDEBUG(D_PROBE, "Find Sensor PO1200");
+		break;
+	case SENSOR_PO3130NC:
+		PDEBUG(D_PROBE, "Find Sensor PO3130NC");
+		break;
+	case SENSOR_POxxxx:
+		PDEBUG(D_PROBE, "Sensor POxxxx");
+		break;
+	}
+	sd->sensor = sensor;
+
+	cam = &gspca_dev->cam;
+	if (sd->bridge == BRIDGE_VC0321) {
+		cam->cam_mode = vc0321_mode;
+		cam->nmodes = ARRAY_SIZE(vc0321_mode);
+	} else {
+		switch (sensor) {
+		case SENSOR_PO1200:
+			cam->cam_mode = svga_mode;
+			cam->nmodes = ARRAY_SIZE(svga_mode);
+			break;
+		case SENSOR_MI1310_SOC:
+			cam->cam_mode = vc0323_mode;
+			cam->nmodes = ARRAY_SIZE(vc0323_mode);
+			break;
+		case SENSOR_MI1320_SOC:
+			cam->cam_mode = bi_mode;
+			cam->nmodes = ARRAY_SIZE(bi_mode);
+			break;
+		case SENSOR_OV7670:
+			cam->cam_mode = bi_mode;
+			cam->nmodes = ARRAY_SIZE(bi_mode) - 1;
+			break;
+		default:
+			cam->cam_mode = vc0323_mode;
+			cam->nmodes = ARRAY_SIZE(vc0323_mode) - 1;
+			break;
+		}
+	}
+	cam->npkt = npkt[sd->sensor];
+
+	if (sd->sensor == SENSOR_OV7670)
+		sd->flags |= FL_HFLIP | FL_VFLIP;
+
+	if (sd->bridge == BRIDGE_VC0321) {
+		reg_r(gspca_dev, 0x8a, 0, 3);
+		reg_w(gspca_dev, 0x87, 0x00, 0x0f0f);
+		reg_r(gspca_dev, 0x8b, 0, 3);
+		reg_w(gspca_dev, 0x88, 0x00, 0x0202);
+		if (sd->sensor == SENSOR_POxxxx) {
+			reg_r(gspca_dev, 0xa1, 0xb300, 1);
+			if (gspca_dev->usb_buf[0] != 0) {
+				reg_w(gspca_dev, 0xa0, 0x26, 0xb300);
+				reg_w(gspca_dev, 0xa0, 0x04, 0xb300);
+			}
+			reg_w(gspca_dev, 0xa0, 0x00, 0xb300);
+		}
+	}
+	return gspca_dev->usb_err;
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
+{
+	u8 data;
+
+	data = val;
+	if (data >= 0x80)
+		data &= 0x7f;
+	else
+		data = 0xff ^ data;
+	i2c_write(gspca_dev, 0x98, &data, 1);
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev, u8 val)
+{
+	i2c_write(gspca_dev, 0x99, &val, 1);
+}
+
+static void setcolors(struct gspca_dev *gspca_dev, u8 val)
+{
+	u8 data;
+
+	data = val - (val >> 3) - 1;
+	i2c_write(gspca_dev, 0x94, &data, 1);
+	i2c_write(gspca_dev, 0x95, &val, 1);
+}
+
+static void sethvflip(struct gspca_dev *gspca_dev, bool hflip, bool vflip)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u8 data[2];
+
+	if (sd->flags & FL_HFLIP)
+		hflip = !hflip;
+	if (sd->flags & FL_VFLIP)
+		vflip = !vflip;
+	switch (sd->sensor) {
+	case SENSOR_MI1310_SOC:
+	case SENSOR_MI1320:
+	case SENSOR_MI1320_SOC:
+		data[0] = data[1] = 0;		/* select page 0 */
+		i2c_write(gspca_dev, 0xf0, data, 2);
+		data[0] = sd->sensor == SENSOR_MI1310_SOC ? 0x03 : 0x01;
+		data[1] = 0x02 * hflip
+			| 0x01 * vflip;
+		i2c_write(gspca_dev, 0x20, data, 2);
+		break;
+	case SENSOR_OV7660:
+	case SENSOR_OV7670:
+		data[0] = sd->sensor == SENSOR_OV7660 ? 0x01 : 0x07;
+		data[0] |= OV7660_MVFP_MIRROR * hflip
+			| OV7660_MVFP_VFLIP * vflip;
+		i2c_write(gspca_dev, OV7660_REG_MVFP, data, 1);
+		break;
+	case SENSOR_PO1200:
+		data[0] = 0;
+		i2c_write(gspca_dev, 0x03, data, 1);
+		data[0] = 0x80 * hflip
+			| 0x40 * vflip
+			| 0x06;
+		i2c_write(gspca_dev, 0x1e, data, 1);
+		break;
+	}
+}
+
+static void setlightfreq(struct gspca_dev *gspca_dev, s32 val)
+{
+	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[val]);
+}
+
+static void setsharpness(struct gspca_dev *gspca_dev, s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u8 data;
+
+	switch (sd->sensor) {
+	case SENSOR_PO1200:
+		data = 0;
+		i2c_write(gspca_dev, 0x03, &data, 1);
+		if (val < 0)
+			data = 0x6a;
+		else
+			data = 0xb5 + val * 3;
+		i2c_write(gspca_dev, 0x61, &data, 1);
+		break;
+	case SENSOR_POxxxx:
+		if (val < 0)
+			data = 0x7e;	/* def = max */
+		else
+			data = 0x60 + val * 0x0f;
+		i2c_write(gspca_dev, 0x59, &data, 1);
+		break;
+	}
+}
+static void setgain(struct gspca_dev *gspca_dev, u8 val)
+{
+	i2c_write(gspca_dev, 0x15, &val, 1);
+}
+
+static void setexposure(struct gspca_dev *gspca_dev, s32 val)
+{
+	u8 data;
+
+	data = val >> 8;
+	i2c_write(gspca_dev, 0x1a, &data, 1);
+	data = val;
+	i2c_write(gspca_dev, 0x1b, &data, 1);
+}
+
+static void setautogain(struct gspca_dev *gspca_dev, s32 val)
+{
+	static const u8 data[2] = {0x28, 0x3c};
+
+	i2c_write(gspca_dev, 0xd1, &data[val], 1);
+}
+
+static void setgamma(struct gspca_dev *gspca_dev)
+{
+/*fixme:to do */
+	usb_exchange(gspca_dev, poxxxx_gamma);
+}
+
+static void setbacklight(struct gspca_dev *gspca_dev, s32 val)
+{
+	u16 v;
+	u8 data;
+
+	data = (val << 4) | 0x0f;
+	i2c_write(gspca_dev, 0xaa, &data, 1);
+	v = 613 + 12 * val;
+	data = v >> 8;
+	i2c_write(gspca_dev, 0xc4, &data, 1);
+	data = v;
+	i2c_write(gspca_dev, 0xc5, &data, 1);
+	v = 1093 - 12 * val;
+	data = v >> 8;
+	i2c_write(gspca_dev, 0xc6, &data, 1);
+	data = v;
+	i2c_write(gspca_dev, 0xc7, &data, 1);
+	v = 342 + 9 * val;
+	data = v >> 8;
+	i2c_write(gspca_dev, 0xc8, &data, 1);
+	data = v;
+	i2c_write(gspca_dev, 0xc9, &data, 1);
+	v = 702 - 9 * val;
+	data = v >> 8;
+	i2c_write(gspca_dev, 0xca, &data, 1);
+	data = v;
+	i2c_write(gspca_dev, 0xcb, &data, 1);
+}
+
+static void setwb(struct gspca_dev *gspca_dev)
+{
+/*fixme:to do - valid when reg d1 = 0x1c - (reg16 + reg15 = 0xa3)*/
+	static const u8 data[2] = {0x00, 0x00};
+
+	i2c_write(gspca_dev, 0x16, &data[0], 1);
+	i2c_write(gspca_dev, 0x18, &data[1], 1);
+}
+
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	const u8 (*init)[4];
+	const u8 *GammaT = NULL;
+	const u8 *MatrixT = NULL;
+	int mode;
+	static const u8 (*mi1320_soc_init[])[4] = {
+		mi1320_soc_InitSXGA,
+		mi1320_soc_InitVGA,
+		mi1320_soc_InitQVGA,
+	};
+
+/*fixme: back sensor only*/
+	if (sd->flags & FL_SAMSUNG) {
+		reg_w(gspca_dev, 0x89, 0xf0ff, 0xffff);
+		reg_w(gspca_dev, 0xa9, 0x8348, 0x000e);
+		reg_w(gspca_dev, 0xa9, 0x0000, 0x001a);
+	}
+
+	/* Assume start use the good resolution from gspca_dev->mode */
+	if (sd->bridge == BRIDGE_VC0321) {
+		reg_w(gspca_dev, 0xa0, 0xff, 0xbfec);
+		reg_w(gspca_dev, 0xa0, 0xff, 0xbfed);
+		reg_w(gspca_dev, 0xa0, 0xff, 0xbfee);
+		reg_w(gspca_dev, 0xa0, 0xff, 0xbfef);
+		sd->image_offset = 46;
+	} else {
+		if (gspca_dev->cam.cam_mode[gspca_dev->curr_mode].pixelformat
+				== V4L2_PIX_FMT_JPEG)
+			sd->image_offset = 0;
+		else
+			sd->image_offset = 32;
+	}
+
+	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)
+			init = hv7131r_initQVGA_data;	/* 320x240 */
+		else
+			init = hv7131r_initVGA_data;	/* 640x480 */
+		break;
+	case SENSOR_OV7660:
+		GammaT = ov7660_gamma;
+		MatrixT = ov7660_matrix;
+		if (mode)
+			init = ov7660_initQVGA_data;	/* 320x240 */
+		else
+			init = ov7660_initVGA_data;	/* 640x480 */
+		break;
+	case SENSOR_MI0360:
+		GammaT = mi1320_gamma;
+		MatrixT = mi0360_matrix;
+		if (mode)
+			init = mi0360_initQVGA_JPG;	/* 320x240 */
+		else
+			init = mi0360_initVGA_JPG;	/* 640x480 */
+		break;
+	case SENSOR_MI1310_SOC:
+		GammaT = mi1320_gamma;
+		MatrixT = mi1320_matrix;
+		switch (mode) {
+		case 1:
+			init = mi1310_socinitQVGA_JPG;	/* 320x240 */
+			break;
+		case 0:
+			init = mi1310_socinitVGA_JPG;	/* 640x480 */
+			break;
+		default:
+			init = mi1310_soc_InitSXGA_JPG;	/* 1280x1024 */
+			break;
+		}
+		break;
+	case SENSOR_MI1320:
+		GammaT = mi1320_gamma;
+		MatrixT = mi1320_matrix;
+		if (mode)
+			init = mi1320_initQVGA_data;	/* 320x240 */
+		else
+			init = mi1320_initVGA_data;	/* 640x480 */
+		break;
+	case SENSOR_MI1320_SOC:
+		GammaT = mi1320_gamma;
+		MatrixT = mi1320_matrix;
+		init = mi1320_soc_init[mode];
+		break;
+	case SENSOR_OV7670:
+		init = mode == 1 ? ov7670_InitVGA : ov7670_InitQVGA;
+		break;
+	case SENSOR_PO3130NC:
+		GammaT = po3130_gamma;
+		MatrixT = po3130_matrix;
+		if (mode)
+			init = po3130_initQVGA_data;	/* 320x240 */
+		else
+			init = po3130_initVGA_data;	/* 640x480 */
+		usb_exchange(gspca_dev, init);
+		init = po3130_rundata;
+		break;
+	case SENSOR_PO1200:
+		GammaT = po1200_gamma;
+		MatrixT = po1200_matrix;
+		init = po1200_initVGA_data;
+		break;
+	default:
+/*	case SENSOR_POxxxx: */
+		usb_exchange(gspca_dev, poxxxx_init_common);
+		setgamma(gspca_dev);
+		usb_exchange(gspca_dev, poxxxx_init_start_3);
+		if (mode)
+			init = poxxxx_initQVGA;
+		else
+			init = poxxxx_initVGA;
+		usb_exchange(gspca_dev, init);
+		reg_r(gspca_dev, 0x8c, 0x0000, 3);
+		reg_w(gspca_dev, 0xa0,
+				gspca_dev->usb_buf[2] & 1 ? 0 : 1,
+				0xb35c);
+		msleep(300);
+/*fixme: i2c read 04 and 05*/
+		init = poxxxx_init_end_1;
+		break;
+	}
+	usb_exchange(gspca_dev, init);
+	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);
+
+		switch (sd->sensor) {
+		case SENSOR_PO1200:
+		case SENSOR_HV7131R:
+			reg_w(gspca_dev, 0x89, 0x0400, 0x1415);
+			break;
+		case SENSOR_MI1310_SOC:
+			reg_w(gspca_dev, 0x89, 0x058c, 0x0000);
+			break;
+		}
+		msleep(100);
+	}
+	switch (sd->sensor) {
+	case SENSOR_OV7670:
+		reg_w(gspca_dev, 0x87, 0xffff, 0xffff);
+		reg_w(gspca_dev, 0x88, 0xff00, 0xf0f1);
+		reg_w(gspca_dev, 0xa0, 0x0000, 0xbfff);
+		break;
+	case SENSOR_POxxxx:
+		usb_exchange(gspca_dev, poxxxx_init_end_2);
+		setwb(gspca_dev);
+		msleep(80);		/* led on */
+		reg_w(gspca_dev, 0x89, 0xffff, 0xfdff);
+		break;
+	}
+	return gspca_dev->usb_err;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	switch (sd->sensor) {
+	case SENSOR_MI1310_SOC:
+		reg_w(gspca_dev, 0x89, 0x058c, 0x00ff);
+		break;
+	case SENSOR_POxxxx:
+		return;
+	default:
+		if (!(sd->flags & FL_SAMSUNG))
+			reg_w(gspca_dev, 0x89, 0xffff, 0xffff);
+		break;
+	}
+	reg_w(gspca_dev, 0xa0, 0x01, 0xb301);
+	reg_w(gspca_dev, 0xa0, 0x09, 0xb003);
+}
+
+/* called on streamoff with alt 0 and on disconnect */
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (!gspca_dev->present)
+		return;
+/*fixme: is this useful?*/
+	if (sd->sensor == SENSOR_MI1310_SOC)
+		reg_w(gspca_dev, 0x89, 0x058c, 0x00ff);
+	else if (!(sd->flags & FL_SAMSUNG))
+		reg_w(gspca_dev, 0x89, 0xffff, 0xffff);
+
+	if (sd->sensor == SENSOR_POxxxx) {
+		reg_w(gspca_dev, 0xa0, 0x26, 0xb300);
+		reg_w(gspca_dev, 0xa0, 0x04, 0xb300);
+		reg_w(gspca_dev, 0xa0, 0x00, 0xb300);
+	}
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+			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);
+		gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+		data += sd->image_offset;
+		len -= sd->image_offset;
+		gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
+		return;
+	}
+
+	/* The vc0321 sends some additional data after sending the complete
+	 * frame, we ignore this. */
+	if (sd->bridge == BRIDGE_VC0321) {
+		int size, l;
+
+		l = gspca_dev->image_len;
+		size = gspca_dev->frsz;
+		if (len > size - l)
+			len = size - l;
+	}
+	gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+}
+
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct gspca_dev *gspca_dev =
+		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+	struct sd *sd = (struct sd *)gspca_dev;
+
+	gspca_dev->usb_err = 0;
+
+	if (!gspca_dev->streaming && ctrl->id != V4L2_CID_POWER_LINE_FREQUENCY)
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_BRIGHTNESS:
+		setbrightness(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_CONTRAST:
+		setcontrast(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_SATURATION:
+		setcolors(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_HFLIP:
+		sethvflip(gspca_dev, sd->hflip->val, sd->vflip->val);
+		break;
+	case V4L2_CID_SHARPNESS:
+		setsharpness(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_AUTOGAIN:
+		setautogain(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_GAIN:
+		setgain(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_EXPOSURE:
+		setexposure(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_BACKLIGHT_COMPENSATION:
+		setbacklight(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_POWER_LINE_FREQUENCY:
+		setlightfreq(gspca_dev, ctrl->val);
+		break;
+	}
+	return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+	.s_ctrl = sd_s_ctrl,
+};
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *)gspca_dev;
+	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+	bool has_brightness = false;
+	bool has_contrast = false;
+	bool has_sat = false;
+	bool has_hvflip = false;
+	bool has_freq = false;
+	bool has_backlight = false;
+	bool has_exposure = false;
+	bool has_autogain = false;
+	bool has_gain = false;
+	bool has_sharpness = false;
+
+	switch (sd->sensor) {
+	case SENSOR_HV7131R:
+	case SENSOR_MI0360:
+	case SENSOR_PO3130NC:
+		break;
+	case SENSOR_MI1310_SOC:
+	case SENSOR_MI1320:
+	case SENSOR_MI1320_SOC:
+	case SENSOR_OV7660:
+		has_hvflip = true;
+		break;
+	case SENSOR_OV7670:
+		has_hvflip = has_freq = true;
+		break;
+	case SENSOR_PO1200:
+		has_hvflip = has_sharpness = true;
+		break;
+	case SENSOR_POxxxx:
+		has_brightness = has_contrast = has_sat = has_backlight =
+			has_exposure = has_autogain = has_gain =
+			has_sharpness = true;
+		break;
+	}
+
+	gspca_dev->vdev.ctrl_handler = hdl;
+	v4l2_ctrl_handler_init(hdl, 8);
+	if (has_brightness)
+		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+	if (has_contrast)
+		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_CONTRAST, 0, 255, 1, 127);
+	if (has_sat)
+		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_SATURATION, 1, 127, 1, 63);
+	if (has_hvflip) {
+		sd->hflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_HFLIP, 0, 1, 1, 0);
+		sd->vflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_VFLIP, 0, 1, 1, 0);
+	}
+	if (has_sharpness)
+		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_SHARPNESS, -1, 2, 1, -1);
+	if (has_freq)
+		v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
+			V4L2_CID_POWER_LINE_FREQUENCY,
+			V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0,
+			V4L2_CID_POWER_LINE_FREQUENCY_50HZ);
+	if (has_autogain)
+		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+	if (has_gain)
+		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_GAIN, 0, 78, 1, 0);
+	if (has_exposure)
+		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_EXPOSURE, 0, 4095, 1, 450);
+	if (has_backlight)
+		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_BACKLIGHT_COMPENSATION, 0, 15, 1, 15);
+
+	if (hdl->error) {
+		pr_err("Could not initialize controls\n");
+		return hdl->error;
+	}
+	if (sd->hflip)
+		v4l2_ctrl_cluster(2, &sd->hflip);
+	return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+	.name = MODULE_NAME,
+	.init_controls = sd_init_controls,
+	.config = sd_config,
+	.init = sd_init,
+	.start = sd_start,
+	.stopN = sd_stopN,
+	.stop0 = sd_stop0,
+	.pkt_scan = sd_pkt_scan,
+};
+
+/* -- module initialisation -- */
+#define BF(bridge, flags) \
+	.driver_info = (BRIDGE_ ## bridge << 8) \
+		| (flags)
+static const struct usb_device_id device_table[] = {
+	{USB_DEVICE(0x041e, 0x405b), BF(VC0323, FL_VFLIP)},
+	{USB_DEVICE(0x046d, 0x0892), BF(VC0321, 0)},
+	{USB_DEVICE(0x046d, 0x0896), BF(VC0321, 0)},
+	{USB_DEVICE(0x046d, 0x0897), BF(VC0321, 0)},
+	{USB_DEVICE(0x0ac8, 0x0321), BF(VC0321, 0)},
+	{USB_DEVICE(0x0ac8, 0x0323), BF(VC0323, 0)},
+	{USB_DEVICE(0x0ac8, 0x0328), BF(VC0321, 0)},
+	{USB_DEVICE(0x0ac8, 0xc001), BF(VC0321, 0)},
+	{USB_DEVICE(0x0ac8, 0xc002), BF(VC0321, 0)},
+	{USB_DEVICE(0x0ac8, 0xc301), BF(VC0323, FL_SAMSUNG)},
+	{USB_DEVICE(0x15b8, 0x6001), BF(VC0323, 0)},
+	{USB_DEVICE(0x15b8, 0x6002), BF(VC0323, 0)},
+	{USB_DEVICE(0x17ef, 0x4802), BF(VC0323, 0)},
+	{}
+};
+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,
+#ifdef CONFIG_PM
+	.suspend = gspca_suspend,
+	.resume = gspca_resume,
+	.reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/usb/gspca/vicam.c b/drivers/media/usb/gspca/vicam.c
new file mode 100644
index 0000000..103f6c4
--- /dev/null
+++ b/drivers/media/usb/gspca/vicam.c
@@ -0,0 +1,364 @@
+/*
+ * gspca ViCam subdriver
+ *
+ * Copyright (C) 2011 Hans de Goede <hdegoede@redhat.com>
+ *
+ * Based on the usbvideo vicam driver, which is:
+ *
+ * Copyright (c) 2002 Joe Burks (jburks@wavicle.org),
+ *                    Chris Cheney (chris.cheney@gmail.com),
+ *                    Pavel Machek (pavel@ucw.cz),
+ *                    John Tyner (jtyner@cs.ucr.edu),
+ *                    Monroe Williams (monroe@pobox.com)
+ *
+ * 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "vicam"
+#define HEADER_SIZE 64
+
+#include <linux/workqueue.h>
+#include <linux/slab.h>
+#include <linux/firmware.h>
+#include <linux/ihex.h>
+#include "gspca.h"
+
+#define VICAM_FIRMWARE "vicam/firmware.fw"
+
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_DESCRIPTION("GSPCA ViCam USB Camera Driver");
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(VICAM_FIRMWARE);
+
+struct sd {
+	struct gspca_dev gspca_dev;	/* !! must be the first item */
+	struct work_struct work_struct;
+	struct workqueue_struct *work_thread;
+};
+
+/* The vicam sensor has a resolution of 512 x 244, with I believe square
+   pixels, but this is forced to a 4:3 ratio by optics. So it has
+   non square pixels :( */
+static struct v4l2_pix_format vicam_mode[] = {
+	{ 256, 122, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE,
+		.bytesperline = 256,
+		.sizeimage = 256 * 122,
+		.colorspace = V4L2_COLORSPACE_SRGB,},
+	/* 2 modes with somewhat more square pixels */
+	{ 256, 200, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE,
+		.bytesperline = 256,
+		.sizeimage = 256 * 200,
+		.colorspace = V4L2_COLORSPACE_SRGB,},
+	{ 256, 240, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE,
+		.bytesperline = 256,
+		.sizeimage = 256 * 240,
+		.colorspace = V4L2_COLORSPACE_SRGB,},
+#if 0   /* This mode has extremely non square pixels, testing use only */
+	{ 512, 122, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE,
+		.bytesperline = 512,
+		.sizeimage = 512 * 122,
+		.colorspace = V4L2_COLORSPACE_SRGB,},
+#endif
+	{ 512, 244, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE,
+		.bytesperline = 512,
+		.sizeimage = 512 * 244,
+		.colorspace = V4L2_COLORSPACE_SRGB,},
+};
+
+static int vicam_control_msg(struct gspca_dev *gspca_dev, u8 request,
+	u16 value, u16 index, u8 *data, u16 len)
+{
+	int ret;
+
+	ret = usb_control_msg(gspca_dev->dev,
+			      usb_sndctrlpipe(gspca_dev->dev, 0),
+			      request,
+			      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			      value, index, data, len, 1000);
+	if (ret < 0)
+		pr_err("control msg req %02X error %d\n", request, ret);
+
+	return ret;
+}
+
+static int vicam_set_camera_power(struct gspca_dev *gspca_dev, int state)
+{
+	int ret;
+
+	ret = vicam_control_msg(gspca_dev, 0x50, state, 0, NULL, 0);
+	if (ret < 0)
+		return ret;
+
+	if (state)
+		ret = vicam_control_msg(gspca_dev, 0x55, 1, 0, NULL, 0);
+
+	return ret;
+}
+
+/*
+ *  request and read a block of data
+ */
+static int vicam_read_frame(struct gspca_dev *gspca_dev, u8 *data, int size)
+{
+	int ret, unscaled_height, act_len = 0;
+	u8 *req_data = gspca_dev->usb_buf;
+	s32 expo = v4l2_ctrl_g_ctrl(gspca_dev->exposure);
+	s32 gain = v4l2_ctrl_g_ctrl(gspca_dev->gain);
+
+	memset(req_data, 0, 16);
+	req_data[0] = gain;
+	if (gspca_dev->pixfmt.width == 256)
+		req_data[1] |= 0x01; /* low nibble x-scale */
+	if (gspca_dev->pixfmt.height <= 122) {
+		req_data[1] |= 0x10; /* high nibble y-scale */
+		unscaled_height = gspca_dev->pixfmt.height * 2;
+	} else
+		unscaled_height = gspca_dev->pixfmt.height;
+	req_data[2] = 0x90; /* unknown, does not seem to do anything */
+	if (unscaled_height <= 200)
+		req_data[3] = 0x06; /* vend? */
+	else if (unscaled_height <= 242) /* Yes 242 not 240 */
+		req_data[3] = 0x07; /* vend? */
+	else /* Up to 244 lines with req_data[3] == 0x08 */
+		req_data[3] = 0x08; /* vend? */
+
+	if (expo < 256) {
+		/* Frame rate maxed out, use partial frame expo time */
+		req_data[4] = 255 - expo;
+		req_data[5] = 0x00;
+		req_data[6] = 0x00;
+		req_data[7] = 0x01;
+	} else {
+		/* Modify frame rate */
+		req_data[4] = 0x00;
+		req_data[5] = 0x00;
+		req_data[6] = expo & 0xFF;
+		req_data[7] = expo >> 8;
+	}
+	req_data[8] = ((244 - unscaled_height) / 2) & ~0x01; /* vstart */
+	/* bytes 9-15 do not seem to affect exposure or image quality */
+
+	mutex_lock(&gspca_dev->usb_lock);
+	ret = vicam_control_msg(gspca_dev, 0x51, 0x80, 0, req_data, 16);
+	mutex_unlock(&gspca_dev->usb_lock);
+	if (ret < 0)
+		return ret;
+
+	ret = usb_bulk_msg(gspca_dev->dev,
+			   usb_rcvbulkpipe(gspca_dev->dev, 0x81),
+			   data, size, &act_len, 10000);
+	/* successful, it returns 0, otherwise  negative */
+	if (ret < 0 || act_len != size) {
+		pr_err("bulk read fail (%d) len %d/%d\n",
+		       ret, act_len, size);
+		return -EIO;
+	}
+	return 0;
+}
+
+/*
+ * This function is called as a workqueue function and runs whenever the camera
+ * is streaming data. Because it is a workqueue function it is allowed to sleep
+ * so we can use synchronous USB calls. To avoid possible collisions with other
+ * threads attempting to use gspca_dev->usb_buf we take the usb_lock when
+ * performing USB operations using it. In practice we don't really need this
+ * as the cameras controls are only written from the workqueue.
+ */
+static void vicam_dostream(struct work_struct *work)
+{
+	struct sd *sd = container_of(work, struct sd, work_struct);
+	struct gspca_dev *gspca_dev = &sd->gspca_dev;
+	int ret, frame_sz;
+	u8 *buffer;
+
+	frame_sz = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].sizeimage +
+		   HEADER_SIZE;
+	buffer = kmalloc(frame_sz, GFP_KERNEL | GFP_DMA);
+	if (!buffer) {
+		pr_err("Couldn't allocate USB buffer\n");
+		goto exit;
+	}
+
+	while (gspca_dev->present && gspca_dev->streaming) {
+#ifdef CONFIG_PM
+		if (gspca_dev->frozen)
+			break;
+#endif
+		ret = vicam_read_frame(gspca_dev, buffer, frame_sz);
+		if (ret < 0)
+			break;
+
+		/* Note the frame header contents seem to be completely
+		   constant, they do not change with either image, or
+		   settings. So we simply discard it. The frames have
+		   a very similar 64 byte footer, which we don't even
+		   bother reading from the cam */
+		gspca_frame_add(gspca_dev, FIRST_PACKET,
+				buffer + HEADER_SIZE,
+				frame_sz - HEADER_SIZE);
+		gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+	}
+exit:
+	kfree(buffer);
+}
+
+/* This function is called at probe time just before sd_init */
+static int sd_config(struct gspca_dev *gspca_dev,
+		const struct usb_device_id *id)
+{
+	struct cam *cam = &gspca_dev->cam;
+	struct sd *sd = (struct sd *)gspca_dev;
+
+	/* We don't use the buffer gspca allocates so make it small. */
+	cam->bulk = 1;
+	cam->bulk_size = 64;
+	cam->cam_mode = vicam_mode;
+	cam->nmodes = ARRAY_SIZE(vicam_mode);
+
+	INIT_WORK(&sd->work_struct, vicam_dostream);
+
+	return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+	int ret;
+	const struct ihex_binrec *rec;
+	const struct firmware *uninitialized_var(fw);
+	u8 *firmware_buf;
+
+	ret = request_ihex_firmware(&fw, VICAM_FIRMWARE,
+				    &gspca_dev->dev->dev);
+	if (ret) {
+		pr_err("Failed to load \"vicam/firmware.fw\": %d\n", ret);
+		return ret;
+	}
+
+	firmware_buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+	if (!firmware_buf) {
+		ret = -ENOMEM;
+		goto exit;
+	}
+	for (rec = (void *)fw->data; rec; rec = ihex_next_binrec(rec)) {
+		memcpy(firmware_buf, rec->data, be16_to_cpu(rec->len));
+		ret = vicam_control_msg(gspca_dev, 0xff, 0, 0, firmware_buf,
+					be16_to_cpu(rec->len));
+		if (ret < 0)
+			break;
+	}
+
+	kfree(firmware_buf);
+exit:
+	release_firmware(fw);
+	return ret;
+}
+
+/* Set up for getting frames. */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *)gspca_dev;
+	int ret;
+
+	ret = vicam_set_camera_power(gspca_dev, 1);
+	if (ret < 0)
+		return ret;
+
+	/* Start the workqueue function to do the streaming */
+	sd->work_thread = create_singlethread_workqueue(MODULE_NAME);
+	queue_work(sd->work_thread, &sd->work_struct);
+
+	return 0;
+}
+
+/* called on streamoff with alt==0 and on disconnect */
+/* the usb_lock is held at entry - restore on exit */
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+	struct sd *dev = (struct sd *)gspca_dev;
+
+	/* wait for the work queue to terminate */
+	mutex_unlock(&gspca_dev->usb_lock);
+	/* This waits for vicam_dostream to finish */
+	destroy_workqueue(dev->work_thread);
+	dev->work_thread = NULL;
+	mutex_lock(&gspca_dev->usb_lock);
+
+	if (gspca_dev->present)
+		vicam_set_camera_power(gspca_dev, 0);
+}
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+	gspca_dev->vdev.ctrl_handler = hdl;
+	v4l2_ctrl_handler_init(hdl, 2);
+	gspca_dev->exposure = v4l2_ctrl_new_std(hdl, NULL,
+			V4L2_CID_EXPOSURE, 0, 2047, 1, 256);
+	gspca_dev->gain = v4l2_ctrl_new_std(hdl, NULL,
+			V4L2_CID_GAIN, 0, 255, 1, 200);
+
+	if (hdl->error) {
+		pr_err("Could not initialize controls\n");
+		return hdl->error;
+	}
+	return 0;
+}
+
+/* Table of supported USB devices */
+static const struct usb_device_id device_table[] = {
+	{USB_DEVICE(0x04c1, 0x009d)},
+	{USB_DEVICE(0x0602, 0x1001)},
+	{}
+};
+
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+	.name   = MODULE_NAME,
+	.config = sd_config,
+	.init   = sd_init,
+	.init_controls = sd_init_controls,
+	.start  = sd_start,
+	.stop0  = sd_stop0,
+};
+
+/* -- 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,
+#ifdef CONFIG_PM
+	.suspend = gspca_suspend,
+	.resume  = gspca_resume,
+	.reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/usb/gspca/w996Xcf.c b/drivers/media/usb/gspca/w996Xcf.c
new file mode 100644
index 0000000..fb9fe2e
--- /dev/null
+++ b/drivers/media/usb/gspca/w996Xcf.c
@@ -0,0 +1,570 @@
+/**
+ *
+ * GSPCA sub driver for W996[78]CF JPEG USB Dual Mode Camera Chip.
+ *
+ * Copyright (C) 2009 Hans de Goede <hdegoede@redhat.com>
+ *
+ * This module is adapted from the in kernel v4l1 w9968cf driver:
+ *
+ * Copyright (C) 2002-2004 by Luca Risolia <luca.risolia@studio.unibo.it>
+ *
+ * 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
+ *
+ */
+
+/* Note this is not a stand alone driver, it gets included in ov519.c, this
+   is a bit of a hack, but it needs the driver code for a lot of different
+   ov sensors which is already present in ov519.c (the old v4l1 driver used
+   the ovchipcam framework). When we have the time we really should move
+   the sensor drivers to v4l2 sub drivers, and properly split of this
+   driver from ov519.c */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define W9968CF_I2C_BUS_DELAY    4 /* delay in us for I2C bit r/w operations */
+
+#define Y_QUANTABLE (&sd->jpeg_hdr[JPEG_QT0_OFFSET])
+#define UV_QUANTABLE (&sd->jpeg_hdr[JPEG_QT1_OFFSET])
+
+static const struct v4l2_pix_format w9968cf_vga_mode[] = {
+	{160, 120, V4L2_PIX_FMT_UYVY, V4L2_FIELD_NONE,
+		.bytesperline = 160 * 2,
+		.sizeimage = 160 * 120 * 2,
+		.colorspace = V4L2_COLORSPACE_JPEG},
+	{176, 144, V4L2_PIX_FMT_UYVY, V4L2_FIELD_NONE,
+		.bytesperline = 176 * 2,
+		.sizeimage = 176 * 144 * 2,
+		.colorspace = V4L2_COLORSPACE_JPEG},
+	{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+		.bytesperline = 320 * 2,
+		.sizeimage = 320 * 240 * 2,
+		.colorspace = V4L2_COLORSPACE_JPEG},
+	{352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+		.bytesperline = 352 * 2,
+		.sizeimage = 352 * 288 * 2,
+		.colorspace = V4L2_COLORSPACE_JPEG},
+	{640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+		.bytesperline = 640 * 2,
+		.sizeimage = 640 * 480 * 2,
+		.colorspace = V4L2_COLORSPACE_JPEG},
+};
+
+static void reg_w(struct sd *sd, u16 index, u16 value);
+
+/*--------------------------------------------------------------------------
+  Write 64-bit data to the fast serial bus registers.
+  Return 0 on success, -1 otherwise.
+  --------------------------------------------------------------------------*/
+static void w9968cf_write_fsb(struct sd *sd, u16* data)
+{
+	struct usb_device *udev = sd->gspca_dev.dev;
+	u16 value;
+	int ret;
+
+	if (sd->gspca_dev.usb_err < 0)
+		return;
+
+	value = *data++;
+	memcpy(sd->gspca_dev.usb_buf, data, 6);
+
+	ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0,
+			      USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE,
+			      value, 0x06, sd->gspca_dev.usb_buf, 6, 500);
+	if (ret < 0) {
+		pr_err("Write FSB registers failed (%d)\n", ret);
+		sd->gspca_dev.usb_err = ret;
+	}
+}
+
+/*--------------------------------------------------------------------------
+  Write data to the serial bus control register.
+  Return 0 on success, a negative number otherwise.
+  --------------------------------------------------------------------------*/
+static void w9968cf_write_sb(struct sd *sd, u16 value)
+{
+	int ret;
+
+	if (sd->gspca_dev.usb_err < 0)
+		return;
+
+	/* We don't use reg_w here, as that would cause all writes when
+	   bitbanging i2c to be logged, making the logs impossible to read */
+	ret = usb_control_msg(sd->gspca_dev.dev,
+		usb_sndctrlpipe(sd->gspca_dev.dev, 0),
+		0,
+		USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+		value, 0x01, NULL, 0, 500);
+
+	udelay(W9968CF_I2C_BUS_DELAY);
+
+	if (ret < 0) {
+		pr_err("Write SB reg [01] %04x failed\n", value);
+		sd->gspca_dev.usb_err = ret;
+	}
+}
+
+/*--------------------------------------------------------------------------
+  Read data from the serial bus control register.
+  Return 0 on success, a negative number otherwise.
+  --------------------------------------------------------------------------*/
+static int w9968cf_read_sb(struct sd *sd)
+{
+	int ret;
+
+	if (sd->gspca_dev.usb_err < 0)
+		return -1;
+
+	/* We don't use reg_r here, as the w9968cf is special and has 16
+	   bit registers instead of 8 bit */
+	ret = usb_control_msg(sd->gspca_dev.dev,
+			usb_rcvctrlpipe(sd->gspca_dev.dev, 0),
+			1,
+			USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			0, 0x01, sd->gspca_dev.usb_buf, 2, 500);
+	if (ret >= 0) {
+		ret = sd->gspca_dev.usb_buf[0] |
+		      (sd->gspca_dev.usb_buf[1] << 8);
+	} else {
+		pr_err("Read SB reg [01] failed\n");
+		sd->gspca_dev.usb_err = ret;
+	}
+
+	udelay(W9968CF_I2C_BUS_DELAY);
+
+	return ret;
+}
+
+/*--------------------------------------------------------------------------
+  Upload quantization tables for the JPEG compression.
+  This function is called by w9968cf_start_transfer().
+  Return 0 on success, a negative number otherwise.
+  --------------------------------------------------------------------------*/
+static void w9968cf_upload_quantizationtables(struct sd *sd)
+{
+	u16 a, b;
+	int i, j;
+
+	reg_w(sd, 0x39, 0x0010); /* JPEG clock enable */
+
+	for (i = 0, j = 0; i < 32; i++, j += 2) {
+		a = Y_QUANTABLE[j] | ((unsigned)(Y_QUANTABLE[j + 1]) << 8);
+		b = UV_QUANTABLE[j] | ((unsigned)(UV_QUANTABLE[j + 1]) << 8);
+		reg_w(sd, 0x40 + i, a);
+		reg_w(sd, 0x60 + i, b);
+	}
+	reg_w(sd, 0x39, 0x0012); /* JPEG encoder enable */
+}
+
+/****************************************************************************
+ * Low-level I2C I/O functions.                                             *
+ * The adapter supports the following I2C transfer functions:               *
+ * i2c_adap_fastwrite_byte_data() (at 400 kHz bit frequency only)           *
+ * i2c_adap_read_byte_data()                                                *
+ * i2c_adap_read_byte()                                                     *
+ ****************************************************************************/
+
+static void w9968cf_smbus_start(struct sd *sd)
+{
+	w9968cf_write_sb(sd, 0x0011); /* SDE=1, SDA=0, SCL=1 */
+	w9968cf_write_sb(sd, 0x0010); /* SDE=1, SDA=0, SCL=0 */
+}
+
+static void w9968cf_smbus_stop(struct sd *sd)
+{
+	w9968cf_write_sb(sd, 0x0010); /* SDE=1, SDA=0, SCL=0 */
+	w9968cf_write_sb(sd, 0x0011); /* SDE=1, SDA=0, SCL=1 */
+	w9968cf_write_sb(sd, 0x0013); /* SDE=1, SDA=1, SCL=1 */
+}
+
+static void w9968cf_smbus_write_byte(struct sd *sd, u8 v)
+{
+	u8 bit;
+	int sda;
+
+	for (bit = 0 ; bit < 8 ; bit++) {
+		sda = (v & 0x80) ? 2 : 0;
+		v <<= 1;
+		/* SDE=1, SDA=sda, SCL=0 */
+		w9968cf_write_sb(sd, 0x10 | sda);
+		/* SDE=1, SDA=sda, SCL=1 */
+		w9968cf_write_sb(sd, 0x11 | sda);
+		/* SDE=1, SDA=sda, SCL=0 */
+		w9968cf_write_sb(sd, 0x10 | sda);
+	}
+}
+
+static void w9968cf_smbus_read_byte(struct sd *sd, u8 *v)
+{
+	u8 bit;
+
+	/* No need to ensure SDA is high as we are always called after
+	   read_ack which ends with SDA high */
+	*v = 0;
+	for (bit = 0 ; bit < 8 ; bit++) {
+		*v <<= 1;
+		/* SDE=1, SDA=1, SCL=1 */
+		w9968cf_write_sb(sd, 0x0013);
+		*v |= (w9968cf_read_sb(sd) & 0x0008) ? 1 : 0;
+		/* SDE=1, SDA=1, SCL=0 */
+		w9968cf_write_sb(sd, 0x0012);
+	}
+}
+
+static void w9968cf_smbus_write_nack(struct sd *sd)
+{
+	/* No need to ensure SDA is high as we are always called after
+	   read_byte which ends with SDA high */
+	w9968cf_write_sb(sd, 0x0013); /* SDE=1, SDA=1, SCL=1 */
+	w9968cf_write_sb(sd, 0x0012); /* SDE=1, SDA=1, SCL=0 */
+}
+
+static void w9968cf_smbus_read_ack(struct sd *sd)
+{
+	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
+	int sda;
+
+	/* Ensure SDA is high before raising clock to avoid a spurious stop */
+	w9968cf_write_sb(sd, 0x0012); /* SDE=1, SDA=1, SCL=0 */
+	w9968cf_write_sb(sd, 0x0013); /* SDE=1, SDA=1, SCL=1 */
+	sda = w9968cf_read_sb(sd);
+	w9968cf_write_sb(sd, 0x0012); /* SDE=1, SDA=1, SCL=0 */
+	if (sda >= 0 && (sda & 0x08)) {
+		PDEBUG(D_USBI, "Did not receive i2c ACK");
+		sd->gspca_dev.usb_err = -EIO;
+	}
+}
+
+/* SMBus protocol: S Addr Wr [A] Subaddr [A] Value [A] P */
+static void w9968cf_i2c_w(struct sd *sd, u8 reg, u8 value)
+{
+	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
+	u16* data = (u16 *)sd->gspca_dev.usb_buf;
+
+	data[0] = 0x082f | ((sd->sensor_addr & 0x80) ? 0x1500 : 0x0);
+	data[0] |= (sd->sensor_addr & 0x40) ? 0x4000 : 0x0;
+	data[1] = 0x2082 | ((sd->sensor_addr & 0x40) ? 0x0005 : 0x0);
+	data[1] |= (sd->sensor_addr & 0x20) ? 0x0150 : 0x0;
+	data[1] |= (sd->sensor_addr & 0x10) ? 0x5400 : 0x0;
+	data[2] = 0x8208 | ((sd->sensor_addr & 0x08) ? 0x0015 : 0x0);
+	data[2] |= (sd->sensor_addr & 0x04) ? 0x0540 : 0x0;
+	data[2] |= (sd->sensor_addr & 0x02) ? 0x5000 : 0x0;
+	data[3] = 0x1d20 | ((sd->sensor_addr & 0x02) ? 0x0001 : 0x0);
+	data[3] |= (sd->sensor_addr & 0x01) ? 0x0054 : 0x0;
+
+	w9968cf_write_fsb(sd, data);
+
+	data[0] = 0x8208 | ((reg & 0x80) ? 0x0015 : 0x0);
+	data[0] |= (reg & 0x40) ? 0x0540 : 0x0;
+	data[0] |= (reg & 0x20) ? 0x5000 : 0x0;
+	data[1] = 0x0820 | ((reg & 0x20) ? 0x0001 : 0x0);
+	data[1] |= (reg & 0x10) ? 0x0054 : 0x0;
+	data[1] |= (reg & 0x08) ? 0x1500 : 0x0;
+	data[1] |= (reg & 0x04) ? 0x4000 : 0x0;
+	data[2] = 0x2082 | ((reg & 0x04) ? 0x0005 : 0x0);
+	data[2] |= (reg & 0x02) ? 0x0150 : 0x0;
+	data[2] |= (reg & 0x01) ? 0x5400 : 0x0;
+	data[3] = 0x001d;
+
+	w9968cf_write_fsb(sd, data);
+
+	data[0] = 0x8208 | ((value & 0x80) ? 0x0015 : 0x0);
+	data[0] |= (value & 0x40) ? 0x0540 : 0x0;
+	data[0] |= (value & 0x20) ? 0x5000 : 0x0;
+	data[1] = 0x0820 | ((value & 0x20) ? 0x0001 : 0x0);
+	data[1] |= (value & 0x10) ? 0x0054 : 0x0;
+	data[1] |= (value & 0x08) ? 0x1500 : 0x0;
+	data[1] |= (value & 0x04) ? 0x4000 : 0x0;
+	data[2] = 0x2082 | ((value & 0x04) ? 0x0005 : 0x0);
+	data[2] |= (value & 0x02) ? 0x0150 : 0x0;
+	data[2] |= (value & 0x01) ? 0x5400 : 0x0;
+	data[3] = 0xfe1d;
+
+	w9968cf_write_fsb(sd, data);
+
+	PDEBUG(D_USBO, "i2c 0x%02x -> [0x%02x]", value, reg);
+}
+
+/* SMBus protocol: S Addr Wr [A] Subaddr [A] P S Addr+1 Rd [A] [Value] NA P */
+static int w9968cf_i2c_r(struct sd *sd, u8 reg)
+{
+	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
+	int ret = 0;
+	u8 value;
+
+	/* Fast serial bus data control disable */
+	w9968cf_write_sb(sd, 0x0013); /* don't change ! */
+
+	w9968cf_smbus_start(sd);
+	w9968cf_smbus_write_byte(sd, sd->sensor_addr);
+	w9968cf_smbus_read_ack(sd);
+	w9968cf_smbus_write_byte(sd, reg);
+	w9968cf_smbus_read_ack(sd);
+	w9968cf_smbus_stop(sd);
+	w9968cf_smbus_start(sd);
+	w9968cf_smbus_write_byte(sd, sd->sensor_addr + 1);
+	w9968cf_smbus_read_ack(sd);
+	w9968cf_smbus_read_byte(sd, &value);
+	/* signal we don't want to read anymore, the v4l1 driver used to
+	   send an ack here which is very wrong! (and then fixed
+	   the issues this gave by retrying reads) */
+	w9968cf_smbus_write_nack(sd);
+	w9968cf_smbus_stop(sd);
+
+	/* Fast serial bus data control re-enable */
+	w9968cf_write_sb(sd, 0x0030);
+
+	if (sd->gspca_dev.usb_err >= 0) {
+		ret = value;
+		PDEBUG(D_USBI, "i2c [0x%02X] -> 0x%02X", reg, value);
+	} else
+		PERR("i2c read [0x%02x] failed", reg);
+
+	return ret;
+}
+
+/*--------------------------------------------------------------------------
+  Turn on the LED on some webcams. A beep should be heard too.
+  Return 0 on success, a negative number otherwise.
+  --------------------------------------------------------------------------*/
+static void w9968cf_configure(struct sd *sd)
+{
+	reg_w(sd, 0x00, 0xff00); /* power-down */
+	reg_w(sd, 0x00, 0xbf17); /* reset everything */
+	reg_w(sd, 0x00, 0xbf10); /* normal operation */
+	reg_w(sd, 0x01, 0x0010); /* serial bus, SDS high */
+	reg_w(sd, 0x01, 0x0000); /* serial bus, SDS low */
+	reg_w(sd, 0x01, 0x0010); /* ..high 'beep-beep' */
+	reg_w(sd, 0x01, 0x0030); /* Set sda scl to FSB mode */
+
+	sd->stopped = 1;
+}
+
+static void w9968cf_init(struct sd *sd)
+{
+	unsigned long hw_bufsize = sd->sif ? (352 * 288 * 2) : (640 * 480 * 2),
+		      y0 = 0x0000,
+		      u0 = y0 + hw_bufsize / 2,
+		      v0 = u0 + hw_bufsize / 4,
+		      y1 = v0 + hw_bufsize / 4,
+		      u1 = y1 + hw_bufsize / 2,
+		      v1 = u1 + hw_bufsize / 4;
+
+	reg_w(sd, 0x00, 0xff00); /* power off */
+	reg_w(sd, 0x00, 0xbf10); /* power on */
+
+	reg_w(sd, 0x03, 0x405d); /* DRAM timings */
+	reg_w(sd, 0x04, 0x0030); /* SDRAM timings */
+
+	reg_w(sd, 0x20, y0 & 0xffff); /* Y buf.0, low */
+	reg_w(sd, 0x21, y0 >> 16);    /* Y buf.0, high */
+	reg_w(sd, 0x24, u0 & 0xffff); /* U buf.0, low */
+	reg_w(sd, 0x25, u0 >> 16);    /* U buf.0, high */
+	reg_w(sd, 0x28, v0 & 0xffff); /* V buf.0, low */
+	reg_w(sd, 0x29, v0 >> 16);    /* V buf.0, high */
+
+	reg_w(sd, 0x22, y1 & 0xffff); /* Y buf.1, low */
+	reg_w(sd, 0x23, y1 >> 16);    /* Y buf.1, high */
+	reg_w(sd, 0x26, u1 & 0xffff); /* U buf.1, low */
+	reg_w(sd, 0x27, u1 >> 16);    /* U buf.1, high */
+	reg_w(sd, 0x2a, v1 & 0xffff); /* V buf.1, low */
+	reg_w(sd, 0x2b, v1 >> 16);    /* V buf.1, high */
+
+	reg_w(sd, 0x32, y1 & 0xffff); /* JPEG buf 0 low */
+	reg_w(sd, 0x33, y1 >> 16);    /* JPEG buf 0 high */
+
+	reg_w(sd, 0x34, y1 & 0xffff); /* JPEG buf 1 low */
+	reg_w(sd, 0x35, y1 >> 16);    /* JPEG bug 1 high */
+
+	reg_w(sd, 0x36, 0x0000);/* JPEG restart interval */
+	reg_w(sd, 0x37, 0x0804);/*JPEG VLE FIFO threshold*/
+	reg_w(sd, 0x38, 0x0000);/* disable hw up-scaling */
+	reg_w(sd, 0x3f, 0x0000); /* JPEG/MCTL test data */
+}
+
+static void w9968cf_set_crop_window(struct sd *sd)
+{
+	int start_cropx, start_cropy,  x, y, fw, fh, cw, ch,
+	    max_width, max_height;
+
+	if (sd->sif) {
+		max_width  = 352;
+		max_height = 288;
+	} else {
+		max_width  = 640;
+		max_height = 480;
+	}
+
+	if (sd->sensor == SEN_OV7620) {
+		/*
+		 * Sigh, this is dependend on the clock / framerate changes
+		 * made by the frequency control, sick.
+		 *
+		 * Note we cannot use v4l2_ctrl_g_ctrl here, as we get called
+		 * from ov519.c:setfreq() with the ctrl lock held!
+		 */
+		if (sd->freq->val == 1) {
+			start_cropx = 277;
+			start_cropy = 37;
+		} else {
+			start_cropx = 105;
+			start_cropy = 37;
+		}
+	} else {
+		start_cropx = 320;
+		start_cropy = 35;
+	}
+
+	/* Work around to avoid FP arithmetics */
+	#define SC(x) ((x) << 10)
+
+	/* Scaling factors */
+	fw = SC(sd->gspca_dev.pixfmt.width) / max_width;
+	fh = SC(sd->gspca_dev.pixfmt.height) / max_height;
+
+	cw = (fw >= fh) ? max_width : SC(sd->gspca_dev.pixfmt.width) / fh;
+	ch = (fw >= fh) ? SC(sd->gspca_dev.pixfmt.height) / fw : max_height;
+
+	sd->sensor_width = max_width;
+	sd->sensor_height = max_height;
+
+	x = (max_width - cw) / 2;
+	y = (max_height - ch) / 2;
+
+	reg_w(sd, 0x10, start_cropx + x);
+	reg_w(sd, 0x11, start_cropy + y);
+	reg_w(sd, 0x12, start_cropx + x + cw);
+	reg_w(sd, 0x13, start_cropy + y + ch);
+}
+
+static void w9968cf_mode_init_regs(struct sd *sd)
+{
+	int val, vs_polarity, hs_polarity;
+
+	w9968cf_set_crop_window(sd);
+
+	reg_w(sd, 0x14, sd->gspca_dev.pixfmt.width);
+	reg_w(sd, 0x15, sd->gspca_dev.pixfmt.height);
+
+	/* JPEG width & height */
+	reg_w(sd, 0x30, sd->gspca_dev.pixfmt.width);
+	reg_w(sd, 0x31, sd->gspca_dev.pixfmt.height);
+
+	/* Y & UV frame buffer strides (in WORD) */
+	if (w9968cf_vga_mode[sd->gspca_dev.curr_mode].pixelformat ==
+	    V4L2_PIX_FMT_JPEG) {
+		reg_w(sd, 0x2c, sd->gspca_dev.pixfmt.width / 2);
+		reg_w(sd, 0x2d, sd->gspca_dev.pixfmt.width / 4);
+	} else
+		reg_w(sd, 0x2c, sd->gspca_dev.pixfmt.width);
+
+	reg_w(sd, 0x00, 0xbf17); /* reset everything */
+	reg_w(sd, 0x00, 0xbf10); /* normal operation */
+
+	/* Transfer size in WORDS (for UYVY format only) */
+	val = sd->gspca_dev.pixfmt.width * sd->gspca_dev.pixfmt.height;
+	reg_w(sd, 0x3d, val & 0xffff); /* low bits */
+	reg_w(sd, 0x3e, val >> 16);    /* high bits */
+
+	if (w9968cf_vga_mode[sd->gspca_dev.curr_mode].pixelformat ==
+	    V4L2_PIX_FMT_JPEG) {
+		/* We may get called multiple times (usb isoc bw negotiat.) */
+		jpeg_define(sd->jpeg_hdr, sd->gspca_dev.pixfmt.height,
+			    sd->gspca_dev.pixfmt.width, 0x22); /* JPEG 420 */
+		jpeg_set_qual(sd->jpeg_hdr, v4l2_ctrl_g_ctrl(sd->jpegqual));
+		w9968cf_upload_quantizationtables(sd);
+		v4l2_ctrl_grab(sd->jpegqual, true);
+	}
+
+	/* Video Capture Control Register */
+	if (sd->sensor == SEN_OV7620) {
+		/* Seems to work around a bug in the image sensor */
+		vs_polarity = 1;
+		hs_polarity = 1;
+	} else {
+		vs_polarity = 1;
+		hs_polarity = 0;
+	}
+
+	val = (vs_polarity << 12) | (hs_polarity << 11);
+
+	/* NOTE: We may not have enough memory to do double buffering while
+	   doing compression (amount of memory differs per model cam).
+	   So we use the second image buffer also as jpeg stream buffer
+	   (see w9968cf_init), and disable double buffering. */
+	if (w9968cf_vga_mode[sd->gspca_dev.curr_mode].pixelformat ==
+	    V4L2_PIX_FMT_JPEG) {
+		/* val |= 0x0002; YUV422P */
+		val |= 0x0003; /* YUV420P */
+	} else
+		val |= 0x0080; /* Enable HW double buffering */
+
+	/* val |= 0x0020; enable clamping */
+	/* val |= 0x0008; enable (1-2-1) filter */
+	/* val |= 0x000c; enable (2-3-6-3-2) filter */
+
+	val |= 0x8000; /* capt. enable */
+
+	reg_w(sd, 0x16, val);
+
+	sd->gspca_dev.empty_packet = 0;
+}
+
+static void w9968cf_stop0(struct sd *sd)
+{
+	v4l2_ctrl_grab(sd->jpegqual, false);
+	reg_w(sd, 0x39, 0x0000); /* disable JPEG encoder */
+	reg_w(sd, 0x16, 0x0000); /* stop video capture */
+}
+
+/* The w9968cf docs say that a 0 sized packet means EOF (and also SOF
+   for the next frame). This seems to simply not be true when operating
+   in JPEG mode, in this case there may be empty packets within the
+   frame. So in JPEG mode use the JPEG SOI marker to detect SOF.
+
+   Note to make things even more interesting the w9968cf sends *PLANAR* jpeg,
+   to be precise it sends: SOI, SOF, DRI, SOS, Y-data, SOS, U-data, SOS,
+   V-data, EOI. */
+static void w9968cf_pkt_scan(struct gspca_dev *gspca_dev,
+			u8 *data,			/* isoc packet */
+			int len)			/* iso packet length */
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (w9968cf_vga_mode[gspca_dev->curr_mode].pixelformat ==
+	    V4L2_PIX_FMT_JPEG) {
+		if (len >= 2 &&
+		    data[0] == 0xff &&
+		    data[1] == 0xd8) {
+			gspca_frame_add(gspca_dev, LAST_PACKET,
+					NULL, 0);
+			gspca_frame_add(gspca_dev, FIRST_PACKET,
+					sd->jpeg_hdr, JPEG_HDR_SZ);
+			/* Strip the ff d8, our own header (which adds
+			   huffman and quantization tables) already has this */
+			len -= 2;
+			data += 2;
+		}
+	} else {
+		/* In UYVY mode an empty packet signals EOF */
+		if (gspca_dev->empty_packet) {
+			gspca_frame_add(gspca_dev, LAST_PACKET,
+						NULL, 0);
+			gspca_frame_add(gspca_dev, FIRST_PACKET,
+					NULL, 0);
+			gspca_dev->empty_packet = 0;
+		}
+	}
+	gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+}
diff --git a/drivers/media/usb/gspca/xirlink_cit.c b/drivers/media/usb/gspca/xirlink_cit.c
new file mode 100644
index 0000000..d5ed9d3
--- /dev/null
+++ b/drivers/media/usb/gspca/xirlink_cit.c
@@ -0,0 +1,3145 @@
+/*
+ * USB IBM C-It Video Camera driver
+ *
+ * Supports Xirlink C-It Video Camera, IBM PC Camera,
+ * IBM NetCamera and Veo Stingray.
+ *
+ * Copyright (C) 2010 Hans de Goede <hdegoede@redhat.com>
+ *
+ * This driver is based on earlier work of:
+ *
+ * (C) Copyright 1999 Johannes Erdfelt
+ * (C) Copyright 1999 Randy Dunlap
+ *
+ * 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "xirlink-cit"
+
+#include <linux/input.h>
+#include "gspca.h"
+
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_DESCRIPTION("Xirlink C-IT");
+MODULE_LICENSE("GPL");
+
+/* FIXME we should autodetect this */
+static int ibm_netcam_pro;
+module_param(ibm_netcam_pro, int, 0);
+MODULE_PARM_DESC(ibm_netcam_pro,
+		 "Use IBM Netcamera Pro init sequences for Model 3 cams");
+
+/* FIXME this should be handled through the V4L2 input selection API */
+static int rca_input;
+module_param(rca_input, int, 0644);
+MODULE_PARM_DESC(rca_input,
+		 "Use rca input instead of ccd sensor on Model 3 cams");
+
+/* specific webcam descriptor */
+struct sd {
+	struct gspca_dev gspca_dev;		/* !! must be the first item */
+	struct v4l2_ctrl *lighting;
+	u8 model;
+#define CIT_MODEL0 0 /* bcd version 0.01 cams ie the xvp-500 */
+#define CIT_MODEL1 1 /* The model 1 - 4 nomenclature comes from the old */
+#define CIT_MODEL2 2 /* ibmcam driver */
+#define CIT_MODEL3 3
+#define CIT_MODEL4 4
+#define CIT_IBM_NETCAM_PRO 5
+	u8 input_index;
+	u8 button_state;
+	u8 stop_on_control_change;
+	u8 sof_read;
+	u8 sof_len;
+};
+
+static void sd_stop0(struct gspca_dev *gspca_dev);
+
+static const struct v4l2_pix_format cif_yuv_mode[] = {
+	{176, 144, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE,
+		.bytesperline = 176,
+		.sizeimage = 176 * 144 * 3 / 2 + 4,
+		.colorspace = V4L2_COLORSPACE_SRGB},
+	{352, 288, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE,
+		.bytesperline = 352,
+		.sizeimage = 352 * 288 * 3 / 2 + 4,
+		.colorspace = V4L2_COLORSPACE_SRGB},
+};
+
+static const struct v4l2_pix_format vga_yuv_mode[] = {
+	{160, 120, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE,
+		.bytesperline = 160,
+		.sizeimage = 160 * 120 * 3 / 2 + 4,
+		.colorspace = V4L2_COLORSPACE_SRGB},
+	{320, 240, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE,
+		.bytesperline = 320,
+		.sizeimage = 320 * 240 * 3 / 2 + 4,
+		.colorspace = V4L2_COLORSPACE_SRGB},
+	{640, 480, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE,
+		.bytesperline = 640,
+		.sizeimage = 640 * 480 * 3 / 2 + 4,
+		.colorspace = V4L2_COLORSPACE_SRGB},
+};
+
+static const struct v4l2_pix_format model0_mode[] = {
+	{160, 120, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE,
+		.bytesperline = 160,
+		.sizeimage = 160 * 120 * 3 / 2 + 4,
+		.colorspace = V4L2_COLORSPACE_SRGB},
+	{176, 144, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE,
+		.bytesperline = 176,
+		.sizeimage = 176 * 144 * 3 / 2 + 4,
+		.colorspace = V4L2_COLORSPACE_SRGB},
+	{320, 240, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE,
+		.bytesperline = 320,
+		.sizeimage = 320 * 240 * 3 / 2 + 4,
+		.colorspace = V4L2_COLORSPACE_SRGB},
+};
+
+static const struct v4l2_pix_format model2_mode[] = {
+	{160, 120, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE,
+		.bytesperline = 160,
+		.sizeimage = 160 * 120 * 3 / 2 + 4,
+		.colorspace = V4L2_COLORSPACE_SRGB},
+	{176, 144, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE,
+		.bytesperline = 176,
+		.sizeimage = 176 * 144 * 3 / 2 + 4,
+		.colorspace = V4L2_COLORSPACE_SRGB},
+	{320, 240, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE,
+		.bytesperline = 320,
+		.sizeimage = 320 * 240 + 4,
+		.colorspace = V4L2_COLORSPACE_SRGB},
+	{352, 288, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE,
+		.bytesperline = 352,
+		.sizeimage = 352 * 288 + 4,
+		.colorspace = V4L2_COLORSPACE_SRGB},
+};
+
+/*
+ * 01.01.08 - Added for RCA video in support -LO
+ * This struct is used to init the Model3 cam to use the RCA video in port
+ * instead of the CCD sensor.
+ */
+static const u16 rca_initdata[][3] = {
+	{0, 0x0000, 0x010c},
+	{0, 0x0006, 0x012c},
+	{0, 0x0078, 0x012d},
+	{0, 0x0046, 0x012f},
+	{0, 0xd141, 0x0124},
+	{0, 0x0000, 0x0127},
+	{0, 0xfea8, 0x0124},
+	{1, 0x0000, 0x0116},
+	{0, 0x0064, 0x0116},
+	{1, 0x0000, 0x0115},
+	{0, 0x0003, 0x0115},
+	{0, 0x0008, 0x0123},
+	{0, 0x0000, 0x0117},
+	{0, 0x0000, 0x0112},
+	{0, 0x0080, 0x0100},
+	{0, 0x0000, 0x0100},
+	{1, 0x0000, 0x0116},
+	{0, 0x0060, 0x0116},
+	{0, 0x0002, 0x0112},
+	{0, 0x0000, 0x0123},
+	{0, 0x0001, 0x0117},
+	{0, 0x0040, 0x0108},
+	{0, 0x0019, 0x012c},
+	{0, 0x0040, 0x0116},
+	{0, 0x000a, 0x0115},
+	{0, 0x000b, 0x0115},
+	{0, 0x0078, 0x012d},
+	{0, 0x0046, 0x012f},
+	{0, 0xd141, 0x0124},
+	{0, 0x0000, 0x0127},
+	{0, 0xfea8, 0x0124},
+	{0, 0x0064, 0x0116},
+	{0, 0x0000, 0x0115},
+	{0, 0x0001, 0x0115},
+	{0, 0xffff, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x00aa, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0000, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xffff, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x00f2, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x000f, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xffff, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x00f8, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x00fc, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xffff, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x00f9, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x003c, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xffff, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0027, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0019, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0037, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0000, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0021, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0038, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0006, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0045, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0037, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0001, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x002a, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0038, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0000, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x000e, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0037, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0001, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x002b, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0038, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0001, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x00f4, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0037, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0001, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x002c, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0038, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0001, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0004, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0037, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0001, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x002d, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0038, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0000, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0014, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0037, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0001, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x002e, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0038, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0003, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0000, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0037, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0001, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x002f, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0038, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0003, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0014, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0037, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0001, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0040, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0038, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0000, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0040, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0037, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0001, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0053, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0038, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0000, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0038, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0x0000, 0x0101},
+	{0, 0x00a0, 0x0103},
+	{0, 0x0078, 0x0105},
+	{0, 0x0000, 0x010a},
+	{0, 0x0024, 0x010b},
+	{0, 0x0028, 0x0119},
+	{0, 0x0088, 0x011b},
+	{0, 0x0002, 0x011d},
+	{0, 0x0003, 0x011e},
+	{0, 0x0000, 0x0129},
+	{0, 0x00fc, 0x012b},
+	{0, 0x0008, 0x0102},
+	{0, 0x0000, 0x0104},
+	{0, 0x0008, 0x011a},
+	{0, 0x0028, 0x011c},
+	{0, 0x0021, 0x012a},
+	{0, 0x0000, 0x0118},
+	{0, 0x0000, 0x0132},
+	{0, 0x0000, 0x0109},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0037, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0001, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0031, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0038, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0000, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0000, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0037, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0001, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0040, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0038, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0000, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0040, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0037, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0000, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x00dc, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0038, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0000, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0000, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0037, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0001, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0032, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0038, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0001, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0020, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0037, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0001, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0040, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0038, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0000, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0040, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0037, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0000, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0030, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0038, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0008, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0000, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0x0003, 0x0111},
+};
+
+/* TESTME the old ibmcam driver repeats certain commands to Model1 cameras, we
+   do the same for now (testing needed to see if this is really necessary) */
+static const int cit_model1_ntries = 5;
+static const int cit_model1_ntries2 = 2;
+
+static int cit_write_reg(struct gspca_dev *gspca_dev, u16 value, u16 index)
+{
+	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_ENDPOINT,
+			value, index, NULL, 0, 1000);
+	if (err < 0)
+		pr_err("Failed to write a register (index 0x%04X, value 0x%02X, error %d)\n",
+		       index, value, err);
+
+	return 0;
+}
+
+static int cit_read_reg(struct gspca_dev *gspca_dev, u16 index, int verbose)
+{
+	struct usb_device *udev = gspca_dev->dev;
+	__u8 *buf = gspca_dev->usb_buf;
+	int res;
+
+	res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x01,
+			USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT,
+			0x00, index, buf, 8, 1000);
+	if (res < 0) {
+		pr_err("Failed to read a register (index 0x%04X, error %d)\n",
+		       index, res);
+		return res;
+	}
+
+	if (verbose)
+		PDEBUG(D_PROBE, "Register %04x value: %02x", index, buf[0]);
+
+	return 0;
+}
+
+/*
+ * cit_send_FF_04_02()
+ *
+ * This procedure sends magic 3-command prefix to the camera.
+ * The purpose of this prefix is not known.
+ *
+ * History:
+ * 1/2/00   Created.
+ */
+static void cit_send_FF_04_02(struct gspca_dev *gspca_dev)
+{
+	cit_write_reg(gspca_dev, 0x00FF, 0x0127);
+	cit_write_reg(gspca_dev, 0x0004, 0x0124);
+	cit_write_reg(gspca_dev, 0x0002, 0x0124);
+}
+
+static void cit_send_00_04_06(struct gspca_dev *gspca_dev)
+{
+	cit_write_reg(gspca_dev, 0x0000, 0x0127);
+	cit_write_reg(gspca_dev, 0x0004, 0x0124);
+	cit_write_reg(gspca_dev, 0x0006, 0x0124);
+}
+
+static void cit_send_x_00(struct gspca_dev *gspca_dev, unsigned short x)
+{
+	cit_write_reg(gspca_dev, x,      0x0127);
+	cit_write_reg(gspca_dev, 0x0000, 0x0124);
+}
+
+static void cit_send_x_00_05(struct gspca_dev *gspca_dev, unsigned short x)
+{
+	cit_send_x_00(gspca_dev, x);
+	cit_write_reg(gspca_dev, 0x0005, 0x0124);
+}
+
+static void cit_send_x_00_05_02(struct gspca_dev *gspca_dev, unsigned short x)
+{
+	cit_write_reg(gspca_dev, x,      0x0127);
+	cit_write_reg(gspca_dev, 0x0000, 0x0124);
+	cit_write_reg(gspca_dev, 0x0005, 0x0124);
+	cit_write_reg(gspca_dev, 0x0002, 0x0124);
+}
+
+static void cit_send_x_01_00_05(struct gspca_dev *gspca_dev, u16 x)
+{
+	cit_write_reg(gspca_dev, x,      0x0127);
+	cit_write_reg(gspca_dev, 0x0001, 0x0124);
+	cit_write_reg(gspca_dev, 0x0000, 0x0124);
+	cit_write_reg(gspca_dev, 0x0005, 0x0124);
+}
+
+static void cit_send_x_00_05_02_01(struct gspca_dev *gspca_dev, u16 x)
+{
+	cit_write_reg(gspca_dev, x,      0x0127);
+	cit_write_reg(gspca_dev, 0x0000, 0x0124);
+	cit_write_reg(gspca_dev, 0x0005, 0x0124);
+	cit_write_reg(gspca_dev, 0x0002, 0x0124);
+	cit_write_reg(gspca_dev, 0x0001, 0x0124);
+}
+
+static void cit_send_x_00_05_02_08_01(struct gspca_dev *gspca_dev, u16 x)
+{
+	cit_write_reg(gspca_dev, x,      0x0127);
+	cit_write_reg(gspca_dev, 0x0000, 0x0124);
+	cit_write_reg(gspca_dev, 0x0005, 0x0124);
+	cit_write_reg(gspca_dev, 0x0002, 0x0124);
+	cit_write_reg(gspca_dev, 0x0008, 0x0124);
+	cit_write_reg(gspca_dev, 0x0001, 0x0124);
+}
+
+static void cit_Packet_Format1(struct gspca_dev *gspca_dev, u16 fkey, u16 val)
+{
+	cit_send_x_01_00_05(gspca_dev, 0x0088);
+	cit_send_x_00_05(gspca_dev, fkey);
+	cit_send_x_00_05_02_08_01(gspca_dev, val);
+	cit_send_x_00_05(gspca_dev, 0x0088);
+	cit_send_x_00_05_02_01(gspca_dev, fkey);
+	cit_send_x_00_05(gspca_dev, 0x0089);
+	cit_send_x_00(gspca_dev, fkey);
+	cit_send_00_04_06(gspca_dev);
+	cit_read_reg(gspca_dev, 0x0126, 0);
+	cit_send_FF_04_02(gspca_dev);
+}
+
+static void cit_PacketFormat2(struct gspca_dev *gspca_dev, u16 fkey, u16 val)
+{
+	cit_send_x_01_00_05(gspca_dev, 0x0088);
+	cit_send_x_00_05(gspca_dev, fkey);
+	cit_send_x_00_05_02(gspca_dev, val);
+}
+
+static void cit_model2_Packet2(struct gspca_dev *gspca_dev)
+{
+	cit_write_reg(gspca_dev, 0x00ff, 0x012d);
+	cit_write_reg(gspca_dev, 0xfea3, 0x0124);
+}
+
+static void cit_model2_Packet1(struct gspca_dev *gspca_dev, u16 v1, u16 v2)
+{
+	cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+	cit_write_reg(gspca_dev, 0x00ff, 0x012e);
+	cit_write_reg(gspca_dev, v1,     0x012f);
+	cit_write_reg(gspca_dev, 0x00ff, 0x0130);
+	cit_write_reg(gspca_dev, 0xc719, 0x0124);
+	cit_write_reg(gspca_dev, v2,     0x0127);
+
+	cit_model2_Packet2(gspca_dev);
+}
+
+/*
+ * cit_model3_Packet1()
+ *
+ * 00_0078_012d
+ * 00_0097_012f
+ * 00_d141_0124
+ * 00_0096_0127
+ * 00_fea8_0124
+*/
+static void cit_model3_Packet1(struct gspca_dev *gspca_dev, u16 v1, u16 v2)
+{
+	cit_write_reg(gspca_dev, 0x0078, 0x012d);
+	cit_write_reg(gspca_dev, v1,     0x012f);
+	cit_write_reg(gspca_dev, 0xd141, 0x0124);
+	cit_write_reg(gspca_dev, v2,     0x0127);
+	cit_write_reg(gspca_dev, 0xfea8, 0x0124);
+}
+
+static void cit_model4_Packet1(struct gspca_dev *gspca_dev, u16 v1, u16 v2)
+{
+	cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+	cit_write_reg(gspca_dev, v1,     0x012f);
+	cit_write_reg(gspca_dev, 0xd141, 0x0124);
+	cit_write_reg(gspca_dev, v2,     0x0127);
+	cit_write_reg(gspca_dev, 0xfea8, 0x0124);
+}
+
+static void cit_model4_BrightnessPacket(struct gspca_dev *gspca_dev, u16 val)
+{
+	cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+	cit_write_reg(gspca_dev, 0x0026, 0x012f);
+	cit_write_reg(gspca_dev, 0xd141, 0x0124);
+	cit_write_reg(gspca_dev, val,    0x0127);
+	cit_write_reg(gspca_dev, 0x00aa, 0x0130);
+	cit_write_reg(gspca_dev, 0x82a8, 0x0124);
+	cit_write_reg(gspca_dev, 0x0038, 0x012d);
+	cit_write_reg(gspca_dev, 0x0004, 0x012f);
+	cit_write_reg(gspca_dev, 0xd145, 0x0124);
+	cit_write_reg(gspca_dev, 0xfffa, 0x0124);
+}
+
+/* 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;
+
+	sd->model = id->driver_info;
+	if (sd->model == CIT_MODEL3 && ibm_netcam_pro)
+		sd->model = CIT_IBM_NETCAM_PRO;
+
+	cam = &gspca_dev->cam;
+	switch (sd->model) {
+	case CIT_MODEL0:
+		cam->cam_mode = model0_mode;
+		cam->nmodes = ARRAY_SIZE(model0_mode);
+		sd->sof_len = 4;
+		break;
+	case CIT_MODEL1:
+		cam->cam_mode = cif_yuv_mode;
+		cam->nmodes = ARRAY_SIZE(cif_yuv_mode);
+		sd->sof_len = 4;
+		break;
+	case CIT_MODEL2:
+		cam->cam_mode = model2_mode + 1; /* no 160x120 */
+		cam->nmodes = 3;
+		break;
+	case CIT_MODEL3:
+		cam->cam_mode = vga_yuv_mode;
+		cam->nmodes = ARRAY_SIZE(vga_yuv_mode);
+		sd->stop_on_control_change = 1;
+		sd->sof_len = 4;
+		break;
+	case CIT_MODEL4:
+		cam->cam_mode = model2_mode;
+		cam->nmodes = ARRAY_SIZE(model2_mode);
+		break;
+	case CIT_IBM_NETCAM_PRO:
+		cam->cam_mode = vga_yuv_mode;
+		cam->nmodes = 2; /* no 640 x 480 */
+		cam->input_flags = V4L2_IN_ST_VFLIP;
+		sd->stop_on_control_change = 1;
+		sd->sof_len = 4;
+		break;
+	}
+
+	return 0;
+}
+
+static int cit_init_model0(struct gspca_dev *gspca_dev)
+{
+	cit_write_reg(gspca_dev, 0x0000, 0x0100); /* turn on led */
+	cit_write_reg(gspca_dev, 0x0001, 0x0112); /* turn on autogain ? */
+	cit_write_reg(gspca_dev, 0x0000, 0x0400);
+	cit_write_reg(gspca_dev, 0x0001, 0x0400);
+	cit_write_reg(gspca_dev, 0x0000, 0x0420);
+	cit_write_reg(gspca_dev, 0x0001, 0x0420);
+	cit_write_reg(gspca_dev, 0x000d, 0x0409);
+	cit_write_reg(gspca_dev, 0x0002, 0x040a);
+	cit_write_reg(gspca_dev, 0x0018, 0x0405);
+	cit_write_reg(gspca_dev, 0x0008, 0x0435);
+	cit_write_reg(gspca_dev, 0x0026, 0x040b);
+	cit_write_reg(gspca_dev, 0x0007, 0x0437);
+	cit_write_reg(gspca_dev, 0x0015, 0x042f);
+	cit_write_reg(gspca_dev, 0x002b, 0x0439);
+	cit_write_reg(gspca_dev, 0x0026, 0x043a);
+	cit_write_reg(gspca_dev, 0x0008, 0x0438);
+	cit_write_reg(gspca_dev, 0x001e, 0x042b);
+	cit_write_reg(gspca_dev, 0x0041, 0x042c);
+
+	return 0;
+}
+
+static int cit_init_ibm_netcam_pro(struct gspca_dev *gspca_dev)
+{
+	cit_read_reg(gspca_dev, 0x128, 1);
+	cit_write_reg(gspca_dev, 0x0003, 0x0133);
+	cit_write_reg(gspca_dev, 0x0000, 0x0117);
+	cit_write_reg(gspca_dev, 0x0008, 0x0123);
+	cit_write_reg(gspca_dev, 0x0000, 0x0100);
+	cit_read_reg(gspca_dev, 0x0116, 0);
+	cit_write_reg(gspca_dev, 0x0060, 0x0116);
+	cit_write_reg(gspca_dev, 0x0002, 0x0112);
+	cit_write_reg(gspca_dev, 0x0000, 0x0133);
+	cit_write_reg(gspca_dev, 0x0000, 0x0123);
+	cit_write_reg(gspca_dev, 0x0001, 0x0117);
+	cit_write_reg(gspca_dev, 0x0040, 0x0108);
+	cit_write_reg(gspca_dev, 0x0019, 0x012c);
+	cit_write_reg(gspca_dev, 0x0060, 0x0116);
+	cit_write_reg(gspca_dev, 0x0002, 0x0115);
+	cit_write_reg(gspca_dev, 0x000b, 0x0115);
+
+	cit_write_reg(gspca_dev, 0x0078, 0x012d);
+	cit_write_reg(gspca_dev, 0x0001, 0x012f);
+	cit_write_reg(gspca_dev, 0xd141, 0x0124);
+	cit_write_reg(gspca_dev, 0x0079, 0x012d);
+	cit_write_reg(gspca_dev, 0x00ff, 0x0130);
+	cit_write_reg(gspca_dev, 0xcd41, 0x0124);
+	cit_write_reg(gspca_dev, 0xfffa, 0x0124);
+	cit_read_reg(gspca_dev, 0x0126, 1);
+
+	cit_model3_Packet1(gspca_dev, 0x0000, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x0000, 0x0001);
+	cit_model3_Packet1(gspca_dev, 0x000b, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x000c, 0x0008);
+	cit_model3_Packet1(gspca_dev, 0x000d, 0x003a);
+	cit_model3_Packet1(gspca_dev, 0x000e, 0x0060);
+	cit_model3_Packet1(gspca_dev, 0x000f, 0x0060);
+	cit_model3_Packet1(gspca_dev, 0x0010, 0x0008);
+	cit_model3_Packet1(gspca_dev, 0x0011, 0x0004);
+	cit_model3_Packet1(gspca_dev, 0x0012, 0x0028);
+	cit_model3_Packet1(gspca_dev, 0x0013, 0x0002);
+	cit_model3_Packet1(gspca_dev, 0x0014, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x0015, 0x00fb);
+	cit_model3_Packet1(gspca_dev, 0x0016, 0x0002);
+	cit_model3_Packet1(gspca_dev, 0x0017, 0x0037);
+	cit_model3_Packet1(gspca_dev, 0x0018, 0x0036);
+	cit_model3_Packet1(gspca_dev, 0x001e, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x001f, 0x0008);
+	cit_model3_Packet1(gspca_dev, 0x0020, 0x00c1);
+	cit_model3_Packet1(gspca_dev, 0x0021, 0x0034);
+	cit_model3_Packet1(gspca_dev, 0x0022, 0x0034);
+	cit_model3_Packet1(gspca_dev, 0x0025, 0x0002);
+	cit_model3_Packet1(gspca_dev, 0x0028, 0x0022);
+	cit_model3_Packet1(gspca_dev, 0x0029, 0x000a);
+	cit_model3_Packet1(gspca_dev, 0x002b, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x002c, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x002d, 0x00ff);
+	cit_model3_Packet1(gspca_dev, 0x002e, 0x00ff);
+	cit_model3_Packet1(gspca_dev, 0x002f, 0x00ff);
+	cit_model3_Packet1(gspca_dev, 0x0030, 0x00ff);
+	cit_model3_Packet1(gspca_dev, 0x0031, 0x00ff);
+	cit_model3_Packet1(gspca_dev, 0x0032, 0x0007);
+	cit_model3_Packet1(gspca_dev, 0x0033, 0x0005);
+	cit_model3_Packet1(gspca_dev, 0x0037, 0x0040);
+	cit_model3_Packet1(gspca_dev, 0x0039, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x003a, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x003b, 0x0001);
+	cit_model3_Packet1(gspca_dev, 0x003c, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x0040, 0x000c);
+	cit_model3_Packet1(gspca_dev, 0x0041, 0x00fb);
+	cit_model3_Packet1(gspca_dev, 0x0042, 0x0002);
+	cit_model3_Packet1(gspca_dev, 0x0043, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x0045, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x0046, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x0047, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x0048, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x0049, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x004a, 0x00ff);
+	cit_model3_Packet1(gspca_dev, 0x004b, 0x00ff);
+	cit_model3_Packet1(gspca_dev, 0x004c, 0x00ff);
+	cit_model3_Packet1(gspca_dev, 0x004f, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x0050, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x0051, 0x0002);
+	cit_model3_Packet1(gspca_dev, 0x0055, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x0056, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x0057, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x0058, 0x0002);
+	cit_model3_Packet1(gspca_dev, 0x0059, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x005c, 0x0016);
+	cit_model3_Packet1(gspca_dev, 0x005d, 0x0022);
+	cit_model3_Packet1(gspca_dev, 0x005e, 0x003c);
+	cit_model3_Packet1(gspca_dev, 0x005f, 0x0050);
+	cit_model3_Packet1(gspca_dev, 0x0060, 0x0044);
+	cit_model3_Packet1(gspca_dev, 0x0061, 0x0005);
+	cit_model3_Packet1(gspca_dev, 0x006a, 0x007e);
+	cit_model3_Packet1(gspca_dev, 0x006f, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x0072, 0x001b);
+	cit_model3_Packet1(gspca_dev, 0x0073, 0x0005);
+	cit_model3_Packet1(gspca_dev, 0x0074, 0x000a);
+	cit_model3_Packet1(gspca_dev, 0x0075, 0x001b);
+	cit_model3_Packet1(gspca_dev, 0x0076, 0x002a);
+	cit_model3_Packet1(gspca_dev, 0x0077, 0x003c);
+	cit_model3_Packet1(gspca_dev, 0x0078, 0x0050);
+	cit_model3_Packet1(gspca_dev, 0x007b, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x007c, 0x0011);
+	cit_model3_Packet1(gspca_dev, 0x007d, 0x0024);
+	cit_model3_Packet1(gspca_dev, 0x007e, 0x0043);
+	cit_model3_Packet1(gspca_dev, 0x007f, 0x005a);
+	cit_model3_Packet1(gspca_dev, 0x0084, 0x0020);
+	cit_model3_Packet1(gspca_dev, 0x0085, 0x0033);
+	cit_model3_Packet1(gspca_dev, 0x0086, 0x000a);
+	cit_model3_Packet1(gspca_dev, 0x0087, 0x0030);
+	cit_model3_Packet1(gspca_dev, 0x0088, 0x0070);
+	cit_model3_Packet1(gspca_dev, 0x008b, 0x0008);
+	cit_model3_Packet1(gspca_dev, 0x008f, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x0090, 0x0006);
+	cit_model3_Packet1(gspca_dev, 0x0091, 0x0028);
+	cit_model3_Packet1(gspca_dev, 0x0092, 0x005a);
+	cit_model3_Packet1(gspca_dev, 0x0093, 0x0082);
+	cit_model3_Packet1(gspca_dev, 0x0096, 0x0014);
+	cit_model3_Packet1(gspca_dev, 0x0097, 0x0020);
+	cit_model3_Packet1(gspca_dev, 0x0098, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x00b0, 0x0046);
+	cit_model3_Packet1(gspca_dev, 0x00b1, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x00b2, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x00b3, 0x0004);
+	cit_model3_Packet1(gspca_dev, 0x00b4, 0x0007);
+	cit_model3_Packet1(gspca_dev, 0x00b6, 0x0002);
+	cit_model3_Packet1(gspca_dev, 0x00b7, 0x0004);
+	cit_model3_Packet1(gspca_dev, 0x00bb, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x00bc, 0x0001);
+	cit_model3_Packet1(gspca_dev, 0x00bd, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x00bf, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x00c0, 0x00c8);
+	cit_model3_Packet1(gspca_dev, 0x00c1, 0x0014);
+	cit_model3_Packet1(gspca_dev, 0x00c2, 0x0001);
+	cit_model3_Packet1(gspca_dev, 0x00c3, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x00c4, 0x0004);
+	cit_model3_Packet1(gspca_dev, 0x00cb, 0x00bf);
+	cit_model3_Packet1(gspca_dev, 0x00cc, 0x00bf);
+	cit_model3_Packet1(gspca_dev, 0x00cd, 0x00bf);
+	cit_model3_Packet1(gspca_dev, 0x00ce, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x00cf, 0x0020);
+	cit_model3_Packet1(gspca_dev, 0x00d0, 0x0040);
+	cit_model3_Packet1(gspca_dev, 0x00d1, 0x00bf);
+	cit_model3_Packet1(gspca_dev, 0x00d1, 0x00bf);
+	cit_model3_Packet1(gspca_dev, 0x00d2, 0x00bf);
+	cit_model3_Packet1(gspca_dev, 0x00d3, 0x00bf);
+	cit_model3_Packet1(gspca_dev, 0x00ea, 0x0008);
+	cit_model3_Packet1(gspca_dev, 0x00eb, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x00ec, 0x00e8);
+	cit_model3_Packet1(gspca_dev, 0x00ed, 0x0001);
+	cit_model3_Packet1(gspca_dev, 0x00ef, 0x0022);
+	cit_model3_Packet1(gspca_dev, 0x00f0, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x00f2, 0x0028);
+	cit_model3_Packet1(gspca_dev, 0x00f4, 0x0002);
+	cit_model3_Packet1(gspca_dev, 0x00f5, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x00fa, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x00fb, 0x0001);
+	cit_model3_Packet1(gspca_dev, 0x00fc, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x00fd, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x00fe, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x00ff, 0x0000);
+
+	cit_model3_Packet1(gspca_dev, 0x00be, 0x0003);
+	cit_model3_Packet1(gspca_dev, 0x00c8, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x00c9, 0x0020);
+	cit_model3_Packet1(gspca_dev, 0x00ca, 0x0040);
+	cit_model3_Packet1(gspca_dev, 0x0053, 0x0001);
+	cit_model3_Packet1(gspca_dev, 0x0082, 0x000e);
+	cit_model3_Packet1(gspca_dev, 0x0083, 0x0020);
+	cit_model3_Packet1(gspca_dev, 0x0034, 0x003c);
+	cit_model3_Packet1(gspca_dev, 0x006e, 0x0055);
+	cit_model3_Packet1(gspca_dev, 0x0062, 0x0005);
+	cit_model3_Packet1(gspca_dev, 0x0063, 0x0008);
+	cit_model3_Packet1(gspca_dev, 0x0066, 0x000a);
+	cit_model3_Packet1(gspca_dev, 0x0067, 0x0006);
+	cit_model3_Packet1(gspca_dev, 0x006b, 0x0010);
+	cit_model3_Packet1(gspca_dev, 0x005a, 0x0001);
+	cit_model3_Packet1(gspca_dev, 0x005b, 0x000a);
+	cit_model3_Packet1(gspca_dev, 0x0023, 0x0006);
+	cit_model3_Packet1(gspca_dev, 0x0026, 0x0004);
+	cit_model3_Packet1(gspca_dev, 0x0036, 0x0069);
+	cit_model3_Packet1(gspca_dev, 0x0038, 0x0064);
+	cit_model3_Packet1(gspca_dev, 0x003d, 0x0003);
+	cit_model3_Packet1(gspca_dev, 0x003e, 0x0001);
+	cit_model3_Packet1(gspca_dev, 0x00b8, 0x0014);
+	cit_model3_Packet1(gspca_dev, 0x00b9, 0x0014);
+	cit_model3_Packet1(gspca_dev, 0x00e6, 0x0004);
+	cit_model3_Packet1(gspca_dev, 0x00e8, 0x0001);
+
+	return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	switch (sd->model) {
+	case CIT_MODEL0:
+		cit_init_model0(gspca_dev);
+		sd_stop0(gspca_dev);
+		break;
+	case CIT_MODEL1:
+	case CIT_MODEL2:
+	case CIT_MODEL3:
+	case CIT_MODEL4:
+		break; /* All is done in sd_start */
+	case CIT_IBM_NETCAM_PRO:
+		cit_init_ibm_netcam_pro(gspca_dev);
+		sd_stop0(gspca_dev);
+		break;
+	}
+	return 0;
+}
+
+static int cit_set_brightness(struct gspca_dev *gspca_dev, s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int i;
+
+	switch (sd->model) {
+	case CIT_MODEL0:
+	case CIT_IBM_NETCAM_PRO:
+		/* No (known) brightness control for these */
+		break;
+	case CIT_MODEL1:
+		/* Model 1: Brightness range 0 - 63 */
+		cit_Packet_Format1(gspca_dev, 0x0031, val);
+		cit_Packet_Format1(gspca_dev, 0x0032, val);
+		cit_Packet_Format1(gspca_dev, 0x0033, val);
+		break;
+	case CIT_MODEL2:
+		/* Model 2: Brightness range 0x60 - 0xee */
+		/* Scale 0 - 63 to 0x60 - 0xee */
+		i = 0x60 + val * 2254 / 1000;
+		cit_model2_Packet1(gspca_dev, 0x001a, i);
+		break;
+	case CIT_MODEL3:
+		/* Model 3: Brightness range 'i' in [0x0C..0x3F] */
+		i = val;
+		if (i < 0x0c)
+			i = 0x0c;
+		cit_model3_Packet1(gspca_dev, 0x0036, i);
+		break;
+	case CIT_MODEL4:
+		/* Model 4: Brightness range 'i' in [0x04..0xb4] */
+		/* Scale 0 - 63 to 0x04 - 0xb4 */
+		i = 0x04 + val * 2794 / 1000;
+		cit_model4_BrightnessPacket(gspca_dev, i);
+		break;
+	}
+
+	return 0;
+}
+
+static int cit_set_contrast(struct gspca_dev *gspca_dev, s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	switch (sd->model) {
+	case CIT_MODEL0: {
+		int i;
+		/* gain 0-15, 0-20 -> 0-15 */
+		i = val * 1000 / 1333;
+		cit_write_reg(gspca_dev, i, 0x0422);
+		/* gain 0-31, may not be lower then 0x0422, 0-20 -> 0-31 */
+		i = val * 2000 / 1333;
+		cit_write_reg(gspca_dev, i, 0x0423);
+		/* gain 0-127, may not be lower then 0x0423, 0-20 -> 0-63  */
+		i = val * 4000 / 1333;
+		cit_write_reg(gspca_dev, i, 0x0424);
+		/* gain 0-127, may not be lower then 0x0424, , 0-20 -> 0-127 */
+		i = val * 8000 / 1333;
+		cit_write_reg(gspca_dev, i, 0x0425);
+		break;
+	}
+	case CIT_MODEL2:
+	case CIT_MODEL4:
+		/* These models do not have this control. */
+		break;
+	case CIT_MODEL1:
+	{
+		/* Scale 0 - 20 to 15 - 0 */
+		int i, new_contrast = (20 - val) * 1000 / 1333;
+		for (i = 0; i < cit_model1_ntries; i++) {
+			cit_Packet_Format1(gspca_dev, 0x0014, new_contrast);
+			cit_send_FF_04_02(gspca_dev);
+		}
+		break;
+	}
+	case CIT_MODEL3:
+	{	/* Preset hardware values */
+		static const struct {
+			unsigned short cv1;
+			unsigned short cv2;
+			unsigned short cv3;
+		} cv[7] = {
+			{ 0x05, 0x05, 0x0f },	/* Minimum */
+			{ 0x04, 0x04, 0x16 },
+			{ 0x02, 0x03, 0x16 },
+			{ 0x02, 0x08, 0x16 },
+			{ 0x01, 0x0c, 0x16 },
+			{ 0x01, 0x0e, 0x16 },
+			{ 0x01, 0x10, 0x16 }	/* Maximum */
+		};
+		int i = val / 3;
+		cit_model3_Packet1(gspca_dev, 0x0067, cv[i].cv1);
+		cit_model3_Packet1(gspca_dev, 0x005b, cv[i].cv2);
+		cit_model3_Packet1(gspca_dev, 0x005c, cv[i].cv3);
+		break;
+	}
+	case CIT_IBM_NETCAM_PRO:
+		cit_model3_Packet1(gspca_dev, 0x005b, val + 1);
+		break;
+	}
+	return 0;
+}
+
+static int cit_set_hue(struct gspca_dev *gspca_dev, s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	switch (sd->model) {
+	case CIT_MODEL0:
+	case CIT_MODEL1:
+	case CIT_IBM_NETCAM_PRO:
+		/* No hue control for these models */
+		break;
+	case CIT_MODEL2:
+		cit_model2_Packet1(gspca_dev, 0x0024, val);
+		/* cit_model2_Packet1(gspca_dev, 0x0020, sat); */
+		break;
+	case CIT_MODEL3: {
+		/* Model 3: Brightness range 'i' in [0x05..0x37] */
+		/* TESTME according to the ibmcam driver this does not work */
+		if (0) {
+			/* Scale 0 - 127 to 0x05 - 0x37 */
+			int i = 0x05 + val * 1000 / 2540;
+			cit_model3_Packet1(gspca_dev, 0x007e, i);
+		}
+		break;
+	}
+	case CIT_MODEL4:
+		/* HDG: taken from ibmcam, setting the color gains does not
+		 * really belong here.
+		 *
+		 * I am not sure r/g/b_gain variables exactly control gain
+		 * of those channels. Most likely they subtly change some
+		 * very internal image processing settings in the camera.
+		 * In any case, here is what they do, and feel free to tweak:
+		 *
+		 * r_gain: seriously affects red gain
+		 * g_gain: seriously affects green gain
+		 * b_gain: seriously affects blue gain
+		 * hue: changes average color from violet (0) to red (0xFF)
+		 */
+		cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+		cit_write_reg(gspca_dev, 0x001e, 0x012f);
+		cit_write_reg(gspca_dev, 0xd141, 0x0124);
+		cit_write_reg(gspca_dev,    160, 0x0127);  /* Green gain */
+		cit_write_reg(gspca_dev,    160, 0x012e);  /* Red gain */
+		cit_write_reg(gspca_dev,    160, 0x0130);  /* Blue gain */
+		cit_write_reg(gspca_dev, 0x8a28, 0x0124);
+		cit_write_reg(gspca_dev, val, 0x012d); /* Hue */
+		cit_write_reg(gspca_dev, 0xf545, 0x0124);
+		break;
+	}
+	return 0;
+}
+
+static int cit_set_sharpness(struct gspca_dev *gspca_dev, s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	switch (sd->model) {
+	case CIT_MODEL0:
+	case CIT_MODEL2:
+	case CIT_MODEL4:
+	case CIT_IBM_NETCAM_PRO:
+		/* These models do not have this control */
+		break;
+	case CIT_MODEL1: {
+		int i;
+		const unsigned short sa[] = {
+			0x11, 0x13, 0x16, 0x18, 0x1a, 0x8, 0x0a };
+
+		for (i = 0; i < cit_model1_ntries; i++)
+			cit_PacketFormat2(gspca_dev, 0x0013, sa[val]);
+		break;
+	}
+	case CIT_MODEL3:
+	{	/*
+		 * "Use a table of magic numbers.
+		 *  This setting doesn't really change much.
+		 *  But that's how Windows does it."
+		 */
+		static const struct {
+			unsigned short sv1;
+			unsigned short sv2;
+			unsigned short sv3;
+			unsigned short sv4;
+		} sv[7] = {
+			{ 0x00, 0x00, 0x05, 0x14 },	/* Smoothest */
+			{ 0x01, 0x04, 0x05, 0x14 },
+			{ 0x02, 0x04, 0x05, 0x14 },
+			{ 0x03, 0x04, 0x05, 0x14 },
+			{ 0x03, 0x05, 0x05, 0x14 },
+			{ 0x03, 0x06, 0x05, 0x14 },
+			{ 0x03, 0x07, 0x05, 0x14 }	/* Sharpest */
+		};
+		cit_model3_Packet1(gspca_dev, 0x0060, sv[val].sv1);
+		cit_model3_Packet1(gspca_dev, 0x0061, sv[val].sv2);
+		cit_model3_Packet1(gspca_dev, 0x0062, sv[val].sv3);
+		cit_model3_Packet1(gspca_dev, 0x0063, sv[val].sv4);
+		break;
+	}
+	}
+	return 0;
+}
+
+/*
+ * cit_set_lighting()
+ *
+ * Camera model 1:
+ * We have 3 levels of lighting conditions: 0=Bright, 1=Medium, 2=Low.
+ *
+ * Camera model 2:
+ * We have 16 levels of lighting, 0 for bright light and up to 15 for
+ * low light. But values above 5 or so are useless because camera is
+ * not really capable to produce anything worth viewing at such light.
+ * This setting may be altered only in certain camera state.
+ *
+ * Low lighting forces slower FPS.
+ *
+ * History:
+ * 1/5/00   Created.
+ * 2/20/00  Added support for Model 2 cameras.
+ */
+static void cit_set_lighting(struct gspca_dev *gspca_dev, s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	switch (sd->model) {
+	case CIT_MODEL0:
+	case CIT_MODEL2:
+	case CIT_MODEL3:
+	case CIT_MODEL4:
+	case CIT_IBM_NETCAM_PRO:
+		break;
+	case CIT_MODEL1: {
+		int i;
+		for (i = 0; i < cit_model1_ntries; i++)
+			cit_Packet_Format1(gspca_dev, 0x0027, val);
+		break;
+	}
+	}
+}
+
+static void cit_set_hflip(struct gspca_dev *gspca_dev, s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	switch (sd->model) {
+	case CIT_MODEL0:
+		if (val)
+			cit_write_reg(gspca_dev, 0x0020, 0x0115);
+		else
+			cit_write_reg(gspca_dev, 0x0040, 0x0115);
+		break;
+	case CIT_MODEL1:
+	case CIT_MODEL2:
+	case CIT_MODEL3:
+	case CIT_MODEL4:
+	case CIT_IBM_NETCAM_PRO:
+		break;
+	}
+}
+
+static int cit_restart_stream(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	switch (sd->model) {
+	case CIT_MODEL0:
+	case CIT_MODEL1:
+		cit_write_reg(gspca_dev, 0x0001, 0x0114);
+		/* Fall through */
+	case CIT_MODEL2:
+	case CIT_MODEL4:
+		cit_write_reg(gspca_dev, 0x00c0, 0x010c); /* Go! */
+		usb_clear_halt(gspca_dev->dev, gspca_dev->urb[0]->pipe);
+		break;
+	case CIT_MODEL3:
+	case CIT_IBM_NETCAM_PRO:
+		cit_write_reg(gspca_dev, 0x0001, 0x0114);
+		cit_write_reg(gspca_dev, 0x00c0, 0x010c); /* Go! */
+		usb_clear_halt(gspca_dev->dev, gspca_dev->urb[0]->pipe);
+		/* Clear button events from while we were not streaming */
+		cit_write_reg(gspca_dev, 0x0001, 0x0113);
+		break;
+	}
+
+	sd->sof_read = 0;
+
+	return 0;
+}
+
+static int cit_get_packet_size(struct gspca_dev *gspca_dev)
+{
+	struct usb_host_interface *alt;
+	struct usb_interface *intf;
+
+	intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface);
+	alt = usb_altnum_to_altsetting(intf, gspca_dev->alt);
+	if (!alt) {
+		pr_err("Couldn't get altsetting\n");
+		return -EIO;
+	}
+
+	return le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize);
+}
+
+/* Calculate the clockdiv giving us max fps given the available bandwidth */
+static int cit_get_clock_div(struct gspca_dev *gspca_dev)
+{
+	int clock_div = 7; /* 0=30 1=25 2=20 3=15 4=12 5=7.5 6=6 7=3fps ?? */
+	int fps[8] = { 30, 25, 20, 15, 12, 8, 6, 3 };
+	int packet_size;
+
+	packet_size = cit_get_packet_size(gspca_dev);
+	if (packet_size < 0)
+		return packet_size;
+
+	while (clock_div > 3 &&
+			1000 * packet_size >
+			gspca_dev->pixfmt.width * gspca_dev->pixfmt.height *
+			fps[clock_div - 1] * 3 / 2)
+		clock_div--;
+
+	PDEBUG(D_PROBE,
+	       "PacketSize: %d, res: %dx%d -> using clockdiv: %d (%d fps)",
+	       packet_size, gspca_dev->pixfmt.width, gspca_dev->pixfmt.height,
+	       clock_div, fps[clock_div]);
+
+	return clock_div;
+}
+
+static int cit_start_model0(struct gspca_dev *gspca_dev)
+{
+	const unsigned short compression = 0; /* 0=none, 7=best frame rate */
+	int clock_div;
+
+	clock_div = cit_get_clock_div(gspca_dev);
+	if (clock_div < 0)
+		return clock_div;
+
+	cit_write_reg(gspca_dev, 0x0000, 0x0100); /* turn on led */
+	cit_write_reg(gspca_dev, 0x0003, 0x0438);
+	cit_write_reg(gspca_dev, 0x001e, 0x042b);
+	cit_write_reg(gspca_dev, 0x0041, 0x042c);
+	cit_write_reg(gspca_dev, 0x0008, 0x0436);
+	cit_write_reg(gspca_dev, 0x0024, 0x0403);
+	cit_write_reg(gspca_dev, 0x002c, 0x0404);
+	cit_write_reg(gspca_dev, 0x0002, 0x0426);
+	cit_write_reg(gspca_dev, 0x0014, 0x0427);
+
+	switch (gspca_dev->pixfmt.width) {
+	case 160: /* 160x120 */
+		cit_write_reg(gspca_dev, 0x0004, 0x010b);
+		cit_write_reg(gspca_dev, 0x0001, 0x010a);
+		cit_write_reg(gspca_dev, 0x0010, 0x0102);
+		cit_write_reg(gspca_dev, 0x00a0, 0x0103);
+		cit_write_reg(gspca_dev, 0x0000, 0x0104);
+		cit_write_reg(gspca_dev, 0x0078, 0x0105);
+		break;
+
+	case 176: /* 176x144 */
+		cit_write_reg(gspca_dev, 0x0006, 0x010b);
+		cit_write_reg(gspca_dev, 0x0000, 0x010a);
+		cit_write_reg(gspca_dev, 0x0005, 0x0102);
+		cit_write_reg(gspca_dev, 0x00b0, 0x0103);
+		cit_write_reg(gspca_dev, 0x0000, 0x0104);
+		cit_write_reg(gspca_dev, 0x0090, 0x0105);
+		break;
+
+	case 320: /* 320x240 */
+		cit_write_reg(gspca_dev, 0x0008, 0x010b);
+		cit_write_reg(gspca_dev, 0x0004, 0x010a);
+		cit_write_reg(gspca_dev, 0x0005, 0x0102);
+		cit_write_reg(gspca_dev, 0x00a0, 0x0103);
+		cit_write_reg(gspca_dev, 0x0010, 0x0104);
+		cit_write_reg(gspca_dev, 0x0078, 0x0105);
+		break;
+	}
+
+	cit_write_reg(gspca_dev, compression, 0x0109);
+	cit_write_reg(gspca_dev, clock_div, 0x0111);
+
+	return 0;
+}
+
+static int cit_start_model1(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int i, clock_div;
+
+	clock_div = cit_get_clock_div(gspca_dev);
+	if (clock_div < 0)
+		return clock_div;
+
+	cit_read_reg(gspca_dev, 0x0128, 1);
+	cit_read_reg(gspca_dev, 0x0100, 0);
+	cit_write_reg(gspca_dev, 0x01, 0x0100);	/* LED On  */
+	cit_read_reg(gspca_dev, 0x0100, 0);
+	cit_write_reg(gspca_dev, 0x81, 0x0100);	/* LED Off */
+	cit_read_reg(gspca_dev, 0x0100, 0);
+	cit_write_reg(gspca_dev, 0x01, 0x0100);	/* LED On  */
+	cit_write_reg(gspca_dev, 0x01, 0x0108);
+
+	cit_write_reg(gspca_dev, 0x03, 0x0112);
+	cit_read_reg(gspca_dev, 0x0115, 0);
+	cit_write_reg(gspca_dev, 0x06, 0x0115);
+	cit_read_reg(gspca_dev, 0x0116, 0);
+	cit_write_reg(gspca_dev, 0x44, 0x0116);
+	cit_read_reg(gspca_dev, 0x0116, 0);
+	cit_write_reg(gspca_dev, 0x40, 0x0116);
+	cit_read_reg(gspca_dev, 0x0115, 0);
+	cit_write_reg(gspca_dev, 0x0e, 0x0115);
+	cit_write_reg(gspca_dev, 0x19, 0x012c);
+
+	cit_Packet_Format1(gspca_dev, 0x00, 0x1e);
+	cit_Packet_Format1(gspca_dev, 0x39, 0x0d);
+	cit_Packet_Format1(gspca_dev, 0x39, 0x09);
+	cit_Packet_Format1(gspca_dev, 0x3b, 0x00);
+	cit_Packet_Format1(gspca_dev, 0x28, 0x22);
+	cit_Packet_Format1(gspca_dev, 0x27, 0x00);
+	cit_Packet_Format1(gspca_dev, 0x2b, 0x1f);
+	cit_Packet_Format1(gspca_dev, 0x39, 0x08);
+
+	for (i = 0; i < cit_model1_ntries; i++)
+		cit_Packet_Format1(gspca_dev, 0x2c, 0x00);
+
+	for (i = 0; i < cit_model1_ntries; i++)
+		cit_Packet_Format1(gspca_dev, 0x30, 0x14);
+
+	cit_PacketFormat2(gspca_dev, 0x39, 0x02);
+	cit_PacketFormat2(gspca_dev, 0x01, 0xe1);
+	cit_PacketFormat2(gspca_dev, 0x02, 0xcd);
+	cit_PacketFormat2(gspca_dev, 0x03, 0xcd);
+	cit_PacketFormat2(gspca_dev, 0x04, 0xfa);
+	cit_PacketFormat2(gspca_dev, 0x3f, 0xff);
+	cit_PacketFormat2(gspca_dev, 0x39, 0x00);
+
+	cit_PacketFormat2(gspca_dev, 0x39, 0x02);
+	cit_PacketFormat2(gspca_dev, 0x0a, 0x37);
+	cit_PacketFormat2(gspca_dev, 0x0b, 0xb8);
+	cit_PacketFormat2(gspca_dev, 0x0c, 0xf3);
+	cit_PacketFormat2(gspca_dev, 0x0d, 0xe3);
+	cit_PacketFormat2(gspca_dev, 0x0e, 0x0d);
+	cit_PacketFormat2(gspca_dev, 0x0f, 0xf2);
+	cit_PacketFormat2(gspca_dev, 0x10, 0xd5);
+	cit_PacketFormat2(gspca_dev, 0x11, 0xba);
+	cit_PacketFormat2(gspca_dev, 0x12, 0x53);
+	cit_PacketFormat2(gspca_dev, 0x3f, 0xff);
+	cit_PacketFormat2(gspca_dev, 0x39, 0x00);
+
+	cit_PacketFormat2(gspca_dev, 0x39, 0x02);
+	cit_PacketFormat2(gspca_dev, 0x16, 0x00);
+	cit_PacketFormat2(gspca_dev, 0x17, 0x28);
+	cit_PacketFormat2(gspca_dev, 0x18, 0x7d);
+	cit_PacketFormat2(gspca_dev, 0x19, 0xbe);
+	cit_PacketFormat2(gspca_dev, 0x3f, 0xff);
+	cit_PacketFormat2(gspca_dev, 0x39, 0x00);
+
+	for (i = 0; i < cit_model1_ntries; i++)
+		cit_Packet_Format1(gspca_dev, 0x00, 0x18);
+	for (i = 0; i < cit_model1_ntries; i++)
+		cit_Packet_Format1(gspca_dev, 0x13, 0x18);
+	for (i = 0; i < cit_model1_ntries; i++)
+		cit_Packet_Format1(gspca_dev, 0x14, 0x06);
+
+	/* TESTME These are handled through controls
+	   KEEP until someone can test leaving this out is ok */
+	if (0) {
+		/* This is default brightness */
+		for (i = 0; i < cit_model1_ntries; i++)
+			cit_Packet_Format1(gspca_dev, 0x31, 0x37);
+		for (i = 0; i < cit_model1_ntries; i++)
+			cit_Packet_Format1(gspca_dev, 0x32, 0x46);
+		for (i = 0; i < cit_model1_ntries; i++)
+			cit_Packet_Format1(gspca_dev, 0x33, 0x55);
+	}
+
+	cit_Packet_Format1(gspca_dev, 0x2e, 0x04);
+	for (i = 0; i < cit_model1_ntries; i++)
+		cit_Packet_Format1(gspca_dev, 0x2d, 0x04);
+	for (i = 0; i < cit_model1_ntries; i++)
+		cit_Packet_Format1(gspca_dev, 0x29, 0x80);
+	cit_Packet_Format1(gspca_dev, 0x2c, 0x01);
+	cit_Packet_Format1(gspca_dev, 0x30, 0x17);
+	cit_Packet_Format1(gspca_dev, 0x39, 0x08);
+	for (i = 0; i < cit_model1_ntries; i++)
+		cit_Packet_Format1(gspca_dev, 0x34, 0x00);
+
+	cit_write_reg(gspca_dev, 0x00, 0x0101);
+	cit_write_reg(gspca_dev, 0x00, 0x010a);
+
+	switch (gspca_dev->pixfmt.width) {
+	case 128: /* 128x96 */
+		cit_write_reg(gspca_dev, 0x80, 0x0103);
+		cit_write_reg(gspca_dev, 0x60, 0x0105);
+		cit_write_reg(gspca_dev, 0x0c, 0x010b);
+		cit_write_reg(gspca_dev, 0x04, 0x011b);	/* Same everywhere */
+		cit_write_reg(gspca_dev, 0x0b, 0x011d);
+		cit_write_reg(gspca_dev, 0x00, 0x011e);	/* Same everywhere */
+		cit_write_reg(gspca_dev, 0x00, 0x0129);
+		break;
+	case 176: /* 176x144 */
+		cit_write_reg(gspca_dev, 0xb0, 0x0103);
+		cit_write_reg(gspca_dev, 0x8f, 0x0105);
+		cit_write_reg(gspca_dev, 0x06, 0x010b);
+		cit_write_reg(gspca_dev, 0x04, 0x011b);	/* Same everywhere */
+		cit_write_reg(gspca_dev, 0x0d, 0x011d);
+		cit_write_reg(gspca_dev, 0x00, 0x011e);	/* Same everywhere */
+		cit_write_reg(gspca_dev, 0x03, 0x0129);
+		break;
+	case 352: /* 352x288 */
+		cit_write_reg(gspca_dev, 0xb0, 0x0103);
+		cit_write_reg(gspca_dev, 0x90, 0x0105);
+		cit_write_reg(gspca_dev, 0x02, 0x010b);
+		cit_write_reg(gspca_dev, 0x04, 0x011b);	/* Same everywhere */
+		cit_write_reg(gspca_dev, 0x05, 0x011d);
+		cit_write_reg(gspca_dev, 0x00, 0x011e);	/* Same everywhere */
+		cit_write_reg(gspca_dev, 0x00, 0x0129);
+		break;
+	}
+
+	cit_write_reg(gspca_dev, 0xff, 0x012b);
+
+	/* TESTME These are handled through controls
+	   KEEP until someone can test leaving this out is ok */
+	if (0) {
+		/* This is another brightness - don't know why */
+		for (i = 0; i < cit_model1_ntries; i++)
+			cit_Packet_Format1(gspca_dev, 0x31, 0xc3);
+		for (i = 0; i < cit_model1_ntries; i++)
+			cit_Packet_Format1(gspca_dev, 0x32, 0xd2);
+		for (i = 0; i < cit_model1_ntries; i++)
+			cit_Packet_Format1(gspca_dev, 0x33, 0xe1);
+
+		/* Default contrast */
+		for (i = 0; i < cit_model1_ntries; i++)
+			cit_Packet_Format1(gspca_dev, 0x14, 0x0a);
+
+		/* Default sharpness */
+		for (i = 0; i < cit_model1_ntries2; i++)
+			cit_PacketFormat2(gspca_dev, 0x13, 0x1a);
+
+		/* Default lighting conditions */
+		cit_Packet_Format1(gspca_dev, 0x0027,
+				   v4l2_ctrl_g_ctrl(sd->lighting));
+	}
+
+	/* Assorted init */
+	switch (gspca_dev->pixfmt.width) {
+	case 128: /* 128x96 */
+		cit_Packet_Format1(gspca_dev, 0x2b, 0x1e);
+		cit_write_reg(gspca_dev, 0xc9, 0x0119);	/* Same everywhere */
+		cit_write_reg(gspca_dev, 0x80, 0x0109);	/* Same everywhere */
+		cit_write_reg(gspca_dev, 0x36, 0x0102);
+		cit_write_reg(gspca_dev, 0x1a, 0x0104);
+		cit_write_reg(gspca_dev, 0x04, 0x011a);	/* Same everywhere */
+		cit_write_reg(gspca_dev, 0x2b, 0x011c);
+		cit_write_reg(gspca_dev, 0x23, 0x012a);	/* Same everywhere */
+		break;
+	case 176: /* 176x144 */
+		cit_Packet_Format1(gspca_dev, 0x2b, 0x1e);
+		cit_write_reg(gspca_dev, 0xc9, 0x0119);	/* Same everywhere */
+		cit_write_reg(gspca_dev, 0x80, 0x0109);	/* Same everywhere */
+		cit_write_reg(gspca_dev, 0x04, 0x0102);
+		cit_write_reg(gspca_dev, 0x02, 0x0104);
+		cit_write_reg(gspca_dev, 0x04, 0x011a);	/* Same everywhere */
+		cit_write_reg(gspca_dev, 0x2b, 0x011c);
+		cit_write_reg(gspca_dev, 0x23, 0x012a);	/* Same everywhere */
+		break;
+	case 352: /* 352x288 */
+		cit_Packet_Format1(gspca_dev, 0x2b, 0x1f);
+		cit_write_reg(gspca_dev, 0xc9, 0x0119);	/* Same everywhere */
+		cit_write_reg(gspca_dev, 0x80, 0x0109);	/* Same everywhere */
+		cit_write_reg(gspca_dev, 0x08, 0x0102);
+		cit_write_reg(gspca_dev, 0x01, 0x0104);
+		cit_write_reg(gspca_dev, 0x04, 0x011a);	/* Same everywhere */
+		cit_write_reg(gspca_dev, 0x2f, 0x011c);
+		cit_write_reg(gspca_dev, 0x23, 0x012a);	/* Same everywhere */
+		break;
+	}
+
+	cit_write_reg(gspca_dev, 0x01, 0x0100);	/* LED On  */
+	cit_write_reg(gspca_dev, clock_div, 0x0111);
+
+	return 0;
+}
+
+static int cit_start_model2(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int clock_div = 0;
+
+	cit_write_reg(gspca_dev, 0x0000, 0x0100);	/* LED on */
+	cit_read_reg(gspca_dev, 0x0116, 0);
+	cit_write_reg(gspca_dev, 0x0060, 0x0116);
+	cit_write_reg(gspca_dev, 0x0002, 0x0112);
+	cit_write_reg(gspca_dev, 0x00bc, 0x012c);
+	cit_write_reg(gspca_dev, 0x0008, 0x012b);
+	cit_write_reg(gspca_dev, 0x0000, 0x0108);
+	cit_write_reg(gspca_dev, 0x0001, 0x0133);
+	cit_write_reg(gspca_dev, 0x0001, 0x0102);
+	switch (gspca_dev->pixfmt.width) {
+	case 176: /* 176x144 */
+		cit_write_reg(gspca_dev, 0x002c, 0x0103);	/* All except 320x240 */
+		cit_write_reg(gspca_dev, 0x0000, 0x0104);	/* Same */
+		cit_write_reg(gspca_dev, 0x0024, 0x0105);	/* 176x144, 352x288 */
+		cit_write_reg(gspca_dev, 0x00b9, 0x010a);	/* Unique to this mode */
+		cit_write_reg(gspca_dev, 0x0038, 0x0119);	/* Unique to this mode */
+		/* TESTME HDG: this does not seem right
+		   (it is 2 for all other resolutions) */
+		sd->sof_len = 10;
+		break;
+	case 320: /* 320x240 */
+		cit_write_reg(gspca_dev, 0x0028, 0x0103);	/* Unique to this mode */
+		cit_write_reg(gspca_dev, 0x0000, 0x0104);	/* Same */
+		cit_write_reg(gspca_dev, 0x001e, 0x0105);	/* 320x240, 352x240 */
+		cit_write_reg(gspca_dev, 0x0039, 0x010a);	/* All except 176x144 */
+		cit_write_reg(gspca_dev, 0x0070, 0x0119);	/* All except 176x144 */
+		sd->sof_len = 2;
+		break;
+#if 0
+	case VIDEOSIZE_352x240:
+		cit_write_reg(gspca_dev, 0x002c, 0x0103);	/* All except 320x240 */
+		cit_write_reg(gspca_dev, 0x0000, 0x0104);	/* Same */
+		cit_write_reg(gspca_dev, 0x001e, 0x0105);	/* 320x240, 352x240 */
+		cit_write_reg(gspca_dev, 0x0039, 0x010a);	/* All except 176x144 */
+		cit_write_reg(gspca_dev, 0x0070, 0x0119);	/* All except 176x144 */
+		sd->sof_len = 2;
+		break;
+#endif
+	case 352: /* 352x288 */
+		cit_write_reg(gspca_dev, 0x002c, 0x0103);	/* All except 320x240 */
+		cit_write_reg(gspca_dev, 0x0000, 0x0104);	/* Same */
+		cit_write_reg(gspca_dev, 0x0024, 0x0105);	/* 176x144, 352x288 */
+		cit_write_reg(gspca_dev, 0x0039, 0x010a);	/* All except 176x144 */
+		cit_write_reg(gspca_dev, 0x0070, 0x0119);	/* All except 176x144 */
+		sd->sof_len = 2;
+		break;
+	}
+
+	cit_write_reg(gspca_dev, 0x0000, 0x0100);	/* LED on */
+
+	switch (gspca_dev->pixfmt.width) {
+	case 176: /* 176x144 */
+		cit_write_reg(gspca_dev, 0x0050, 0x0111);
+		cit_write_reg(gspca_dev, 0x00d0, 0x0111);
+		break;
+	case 320: /* 320x240 */
+	case 352: /* 352x288 */
+		cit_write_reg(gspca_dev, 0x0040, 0x0111);
+		cit_write_reg(gspca_dev, 0x00c0, 0x0111);
+		break;
+	}
+	cit_write_reg(gspca_dev, 0x009b, 0x010f);
+	cit_write_reg(gspca_dev, 0x00bb, 0x010f);
+
+	/*
+	 * Hardware settings, may affect CMOS sensor; not user controls!
+	 * -------------------------------------------------------------
+	 * 0x0004: no effect
+	 * 0x0006: hardware effect
+	 * 0x0008: no effect
+	 * 0x000a: stops video stream, probably important h/w setting
+	 * 0x000c: changes color in hardware manner (not user setting)
+	 * 0x0012: changes number of colors (does not affect speed)
+	 * 0x002a: no effect
+	 * 0x002c: hardware setting (related to scan lines)
+	 * 0x002e: stops video stream, probably important h/w setting
+	 */
+	cit_model2_Packet1(gspca_dev, 0x000a, 0x005c);
+	cit_model2_Packet1(gspca_dev, 0x0004, 0x0000);
+	cit_model2_Packet1(gspca_dev, 0x0006, 0x00fb);
+	cit_model2_Packet1(gspca_dev, 0x0008, 0x0000);
+	cit_model2_Packet1(gspca_dev, 0x000c, 0x0009);
+	cit_model2_Packet1(gspca_dev, 0x0012, 0x000a);
+	cit_model2_Packet1(gspca_dev, 0x002a, 0x0000);
+	cit_model2_Packet1(gspca_dev, 0x002c, 0x0000);
+	cit_model2_Packet1(gspca_dev, 0x002e, 0x0008);
+
+	/*
+	 * Function 0x0030 pops up all over the place. Apparently
+	 * it is a hardware control register, with every bit assigned to
+	 * do something.
+	 */
+	cit_model2_Packet1(gspca_dev, 0x0030, 0x0000);
+
+	/*
+	 * Magic control of CMOS sensor. Only lower values like
+	 * 0-3 work, and picture shifts left or right. Don't change.
+	 */
+	switch (gspca_dev->pixfmt.width) {
+	case 176: /* 176x144 */
+		cit_model2_Packet1(gspca_dev, 0x0014, 0x0002);
+		cit_model2_Packet1(gspca_dev, 0x0016, 0x0002); /* Horizontal shift */
+		cit_model2_Packet1(gspca_dev, 0x0018, 0x004a); /* Another hardware setting */
+		clock_div = 6;
+		break;
+	case 320: /* 320x240 */
+		cit_model2_Packet1(gspca_dev, 0x0014, 0x0009);
+		cit_model2_Packet1(gspca_dev, 0x0016, 0x0005); /* Horizontal shift */
+		cit_model2_Packet1(gspca_dev, 0x0018, 0x0044); /* Another hardware setting */
+		clock_div = 8;
+		break;
+#if 0
+	case VIDEOSIZE_352x240:
+		/* This mode doesn't work as Windows programs it; changed to work */
+		cit_model2_Packet1(gspca_dev, 0x0014, 0x0009); /* Windows sets this to 8 */
+		cit_model2_Packet1(gspca_dev, 0x0016, 0x0003); /* Horizontal shift */
+		cit_model2_Packet1(gspca_dev, 0x0018, 0x0044); /* Windows sets this to 0x0045 */
+		clock_div = 10;
+		break;
+#endif
+	case 352: /* 352x288 */
+		cit_model2_Packet1(gspca_dev, 0x0014, 0x0003);
+		cit_model2_Packet1(gspca_dev, 0x0016, 0x0002); /* Horizontal shift */
+		cit_model2_Packet1(gspca_dev, 0x0018, 0x004a); /* Another hardware setting */
+		clock_div = 16;
+		break;
+	}
+
+	/* TESTME These are handled through controls
+	   KEEP until someone can test leaving this out is ok */
+	if (0)
+		cit_model2_Packet1(gspca_dev, 0x001a, 0x005a);
+
+	/*
+	 * We have our own frame rate setting varying from 0 (slowest) to 6
+	 * (fastest). The camera model 2 allows frame rate in range [0..0x1F]
+	 # where 0 is also the slowest setting. However for all practical
+	 # reasons high settings make no sense because USB is not fast enough
+	 # to support high FPS. Be aware that the picture datastream will be
+	 # severely disrupted if you ask for frame rate faster than allowed
+	 # for the video size - see below:
+	 *
+	 * Allowable ranges (obtained experimentally on OHCI, K6-3, 450 MHz):
+	 * -----------------------------------------------------------------
+	 * 176x144: [6..31]
+	 * 320x240: [8..31]
+	 * 352x240: [10..31]
+	 * 352x288: [16..31] I have to raise lower threshold for stability...
+	 *
+	 * As usual, slower FPS provides better sensitivity.
+	 */
+	cit_model2_Packet1(gspca_dev, 0x001c, clock_div);
+
+	/*
+	 * This setting does not visibly affect pictures; left it here
+	 * because it was present in Windows USB data stream. This function
+	 * does not allow arbitrary values and apparently is a bit mask, to
+	 * be activated only at appropriate time. Don't change it randomly!
+	 */
+	switch (gspca_dev->pixfmt.width) {
+	case 176: /* 176x144 */
+		cit_model2_Packet1(gspca_dev, 0x0026, 0x00c2);
+		break;
+	case 320: /* 320x240 */
+		cit_model2_Packet1(gspca_dev, 0x0026, 0x0044);
+		break;
+#if 0
+	case VIDEOSIZE_352x240:
+		cit_model2_Packet1(gspca_dev, 0x0026, 0x0046);
+		break;
+#endif
+	case 352: /* 352x288 */
+		cit_model2_Packet1(gspca_dev, 0x0026, 0x0048);
+		break;
+	}
+
+	cit_model2_Packet1(gspca_dev, 0x0028, v4l2_ctrl_g_ctrl(sd->lighting));
+	/* model2 cannot change the backlight compensation while streaming */
+	v4l2_ctrl_grab(sd->lighting, true);
+
+	/* color balance rg2 */
+	cit_model2_Packet1(gspca_dev, 0x001e, 0x002f);
+	/* saturation */
+	cit_model2_Packet1(gspca_dev, 0x0020, 0x0034);
+	/* color balance yb */
+	cit_model2_Packet1(gspca_dev, 0x0022, 0x00a0);
+
+	/* Hardware control command */
+	cit_model2_Packet1(gspca_dev, 0x0030, 0x0004);
+
+	return 0;
+}
+
+static int cit_start_model3(struct gspca_dev *gspca_dev)
+{
+	const unsigned short compression = 0; /* 0=none, 7=best frame rate */
+	int i, clock_div = 0;
+
+	/* HDG not in ibmcam driver, added to see if it helps with
+	   auto-detecting between model3 and ibm netcamera pro */
+	cit_read_reg(gspca_dev, 0x128, 1);
+
+	cit_write_reg(gspca_dev, 0x0000, 0x0100);
+	cit_read_reg(gspca_dev, 0x0116, 0);
+	cit_write_reg(gspca_dev, 0x0060, 0x0116);
+	cit_write_reg(gspca_dev, 0x0002, 0x0112);
+	cit_write_reg(gspca_dev, 0x0000, 0x0123);
+	cit_write_reg(gspca_dev, 0x0001, 0x0117);
+	cit_write_reg(gspca_dev, 0x0040, 0x0108);
+	cit_write_reg(gspca_dev, 0x0019, 0x012c);
+	cit_write_reg(gspca_dev, 0x0060, 0x0116);
+	cit_write_reg(gspca_dev, 0x0002, 0x0115);
+	cit_write_reg(gspca_dev, 0x0003, 0x0115);
+	cit_read_reg(gspca_dev, 0x0115, 0);
+	cit_write_reg(gspca_dev, 0x000b, 0x0115);
+
+	/* TESTME HDG not in ibmcam driver, added to see if it helps with
+	   auto-detecting between model3 and ibm netcamera pro */
+	if (0) {
+		cit_write_reg(gspca_dev, 0x0078, 0x012d);
+		cit_write_reg(gspca_dev, 0x0001, 0x012f);
+		cit_write_reg(gspca_dev, 0xd141, 0x0124);
+		cit_write_reg(gspca_dev, 0x0079, 0x012d);
+		cit_write_reg(gspca_dev, 0x00ff, 0x0130);
+		cit_write_reg(gspca_dev, 0xcd41, 0x0124);
+		cit_write_reg(gspca_dev, 0xfffa, 0x0124);
+		cit_read_reg(gspca_dev, 0x0126, 1);
+	}
+
+	cit_model3_Packet1(gspca_dev, 0x000a, 0x0040);
+	cit_model3_Packet1(gspca_dev, 0x000b, 0x00f6);
+	cit_model3_Packet1(gspca_dev, 0x000c, 0x0002);
+	cit_model3_Packet1(gspca_dev, 0x000d, 0x0020);
+	cit_model3_Packet1(gspca_dev, 0x000e, 0x0033);
+	cit_model3_Packet1(gspca_dev, 0x000f, 0x0007);
+	cit_model3_Packet1(gspca_dev, 0x0010, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x0011, 0x0070);
+	cit_model3_Packet1(gspca_dev, 0x0012, 0x0030);
+	cit_model3_Packet1(gspca_dev, 0x0013, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x0014, 0x0001);
+	cit_model3_Packet1(gspca_dev, 0x0015, 0x0001);
+	cit_model3_Packet1(gspca_dev, 0x0016, 0x0001);
+	cit_model3_Packet1(gspca_dev, 0x0017, 0x0001);
+	cit_model3_Packet1(gspca_dev, 0x0018, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x001e, 0x00c3);
+	cit_model3_Packet1(gspca_dev, 0x0020, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x0028, 0x0010);
+	cit_model3_Packet1(gspca_dev, 0x0029, 0x0054);
+	cit_model3_Packet1(gspca_dev, 0x002a, 0x0013);
+	cit_model3_Packet1(gspca_dev, 0x002b, 0x0007);
+	cit_model3_Packet1(gspca_dev, 0x002d, 0x0028);
+	cit_model3_Packet1(gspca_dev, 0x002e, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x0031, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x0032, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x0033, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x0034, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x0035, 0x0038);
+	cit_model3_Packet1(gspca_dev, 0x003a, 0x0001);
+	cit_model3_Packet1(gspca_dev, 0x003c, 0x001e);
+	cit_model3_Packet1(gspca_dev, 0x003f, 0x000a);
+	cit_model3_Packet1(gspca_dev, 0x0041, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x0046, 0x003f);
+	cit_model3_Packet1(gspca_dev, 0x0047, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x0050, 0x0005);
+	cit_model3_Packet1(gspca_dev, 0x0052, 0x001a);
+	cit_model3_Packet1(gspca_dev, 0x0053, 0x0003);
+	cit_model3_Packet1(gspca_dev, 0x005a, 0x006b);
+	cit_model3_Packet1(gspca_dev, 0x005d, 0x001e);
+	cit_model3_Packet1(gspca_dev, 0x005e, 0x0030);
+	cit_model3_Packet1(gspca_dev, 0x005f, 0x0041);
+	cit_model3_Packet1(gspca_dev, 0x0064, 0x0008);
+	cit_model3_Packet1(gspca_dev, 0x0065, 0x0015);
+	cit_model3_Packet1(gspca_dev, 0x0068, 0x000f);
+	cit_model3_Packet1(gspca_dev, 0x0079, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x007a, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x007c, 0x003f);
+	cit_model3_Packet1(gspca_dev, 0x0082, 0x000f);
+	cit_model3_Packet1(gspca_dev, 0x0085, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x0099, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x009b, 0x0023);
+	cit_model3_Packet1(gspca_dev, 0x009c, 0x0022);
+	cit_model3_Packet1(gspca_dev, 0x009d, 0x0096);
+	cit_model3_Packet1(gspca_dev, 0x009e, 0x0096);
+	cit_model3_Packet1(gspca_dev, 0x009f, 0x000a);
+
+	switch (gspca_dev->pixfmt.width) {
+	case 160:
+		cit_write_reg(gspca_dev, 0x0000, 0x0101); /* Same on 160x120, 320x240 */
+		cit_write_reg(gspca_dev, 0x00a0, 0x0103); /* Same on 160x120, 320x240 */
+		cit_write_reg(gspca_dev, 0x0078, 0x0105); /* Same on 160x120, 320x240 */
+		cit_write_reg(gspca_dev, 0x0000, 0x010a); /* Same */
+		cit_write_reg(gspca_dev, 0x0024, 0x010b); /* Differs everywhere */
+		cit_write_reg(gspca_dev, 0x00a9, 0x0119);
+		cit_write_reg(gspca_dev, 0x0016, 0x011b);
+		cit_write_reg(gspca_dev, 0x0002, 0x011d); /* Same on 160x120, 320x240 */
+		cit_write_reg(gspca_dev, 0x0003, 0x011e); /* Same on 160x120, 640x480 */
+		cit_write_reg(gspca_dev, 0x0000, 0x0129); /* Same */
+		cit_write_reg(gspca_dev, 0x00fc, 0x012b); /* Same */
+		cit_write_reg(gspca_dev, 0x0018, 0x0102);
+		cit_write_reg(gspca_dev, 0x0004, 0x0104);
+		cit_write_reg(gspca_dev, 0x0004, 0x011a);
+		cit_write_reg(gspca_dev, 0x0028, 0x011c);
+		cit_write_reg(gspca_dev, 0x0022, 0x012a); /* Same */
+		cit_write_reg(gspca_dev, 0x0000, 0x0118);
+		cit_write_reg(gspca_dev, 0x0000, 0x0132);
+		cit_model3_Packet1(gspca_dev, 0x0021, 0x0001); /* Same */
+		cit_write_reg(gspca_dev, compression, 0x0109);
+		clock_div = 3;
+		break;
+	case 320:
+		cit_write_reg(gspca_dev, 0x0000, 0x0101); /* Same on 160x120, 320x240 */
+		cit_write_reg(gspca_dev, 0x00a0, 0x0103); /* Same on 160x120, 320x240 */
+		cit_write_reg(gspca_dev, 0x0078, 0x0105); /* Same on 160x120, 320x240 */
+		cit_write_reg(gspca_dev, 0x0000, 0x010a); /* Same */
+		cit_write_reg(gspca_dev, 0x0028, 0x010b); /* Differs everywhere */
+		cit_write_reg(gspca_dev, 0x0002, 0x011d); /* Same */
+		cit_write_reg(gspca_dev, 0x0000, 0x011e);
+		cit_write_reg(gspca_dev, 0x0000, 0x0129); /* Same */
+		cit_write_reg(gspca_dev, 0x00fc, 0x012b); /* Same */
+		/* 4 commands from 160x120 skipped */
+		cit_write_reg(gspca_dev, 0x0022, 0x012a); /* Same */
+		cit_model3_Packet1(gspca_dev, 0x0021, 0x0001); /* Same */
+		cit_write_reg(gspca_dev, compression, 0x0109);
+		cit_write_reg(gspca_dev, 0x00d9, 0x0119);
+		cit_write_reg(gspca_dev, 0x0006, 0x011b);
+		cit_write_reg(gspca_dev, 0x0021, 0x0102); /* Same on 320x240, 640x480 */
+		cit_write_reg(gspca_dev, 0x0010, 0x0104);
+		cit_write_reg(gspca_dev, 0x0004, 0x011a);
+		cit_write_reg(gspca_dev, 0x003f, 0x011c);
+		cit_write_reg(gspca_dev, 0x001c, 0x0118);
+		cit_write_reg(gspca_dev, 0x0000, 0x0132);
+		clock_div = 5;
+		break;
+	case 640:
+		cit_write_reg(gspca_dev, 0x00f0, 0x0105);
+		cit_write_reg(gspca_dev, 0x0000, 0x010a); /* Same */
+		cit_write_reg(gspca_dev, 0x0038, 0x010b); /* Differs everywhere */
+		cit_write_reg(gspca_dev, 0x00d9, 0x0119); /* Same on 320x240, 640x480 */
+		cit_write_reg(gspca_dev, 0x0006, 0x011b); /* Same on 320x240, 640x480 */
+		cit_write_reg(gspca_dev, 0x0004, 0x011d); /* NC */
+		cit_write_reg(gspca_dev, 0x0003, 0x011e); /* Same on 160x120, 640x480 */
+		cit_write_reg(gspca_dev, 0x0000, 0x0129); /* Same */
+		cit_write_reg(gspca_dev, 0x00fc, 0x012b); /* Same */
+		cit_write_reg(gspca_dev, 0x0021, 0x0102); /* Same on 320x240, 640x480 */
+		cit_write_reg(gspca_dev, 0x0016, 0x0104); /* NC */
+		cit_write_reg(gspca_dev, 0x0004, 0x011a); /* Same on 320x240, 640x480 */
+		cit_write_reg(gspca_dev, 0x003f, 0x011c); /* Same on 320x240, 640x480 */
+		cit_write_reg(gspca_dev, 0x0022, 0x012a); /* Same */
+		cit_write_reg(gspca_dev, 0x001c, 0x0118); /* Same on 320x240, 640x480 */
+		cit_model3_Packet1(gspca_dev, 0x0021, 0x0001); /* Same */
+		cit_write_reg(gspca_dev, compression, 0x0109);
+		cit_write_reg(gspca_dev, 0x0040, 0x0101);
+		cit_write_reg(gspca_dev, 0x0040, 0x0103);
+		cit_write_reg(gspca_dev, 0x0000, 0x0132); /* Same on 320x240, 640x480 */
+		clock_div = 7;
+		break;
+	}
+
+	cit_model3_Packet1(gspca_dev, 0x007e, 0x000e);	/* Hue */
+	cit_model3_Packet1(gspca_dev, 0x0036, 0x0011);	/* Brightness */
+	cit_model3_Packet1(gspca_dev, 0x0060, 0x0002);	/* Sharpness */
+	cit_model3_Packet1(gspca_dev, 0x0061, 0x0004);	/* Sharpness */
+	cit_model3_Packet1(gspca_dev, 0x0062, 0x0005);	/* Sharpness */
+	cit_model3_Packet1(gspca_dev, 0x0063, 0x0014);	/* Sharpness */
+	cit_model3_Packet1(gspca_dev, 0x0096, 0x00a0);	/* Red sharpness */
+	cit_model3_Packet1(gspca_dev, 0x0097, 0x0096);	/* Blue sharpness */
+	cit_model3_Packet1(gspca_dev, 0x0067, 0x0001);	/* Contrast */
+	cit_model3_Packet1(gspca_dev, 0x005b, 0x000c);	/* Contrast */
+	cit_model3_Packet1(gspca_dev, 0x005c, 0x0016);	/* Contrast */
+	cit_model3_Packet1(gspca_dev, 0x0098, 0x000b);
+	cit_model3_Packet1(gspca_dev, 0x002c, 0x0003);	/* Was 1, broke 640x480 */
+	cit_model3_Packet1(gspca_dev, 0x002f, 0x002a);
+	cit_model3_Packet1(gspca_dev, 0x0030, 0x0029);
+	cit_model3_Packet1(gspca_dev, 0x0037, 0x0002);
+	cit_model3_Packet1(gspca_dev, 0x0038, 0x0059);
+	cit_model3_Packet1(gspca_dev, 0x003d, 0x002e);
+	cit_model3_Packet1(gspca_dev, 0x003e, 0x0028);
+	cit_model3_Packet1(gspca_dev, 0x0078, 0x0005);
+	cit_model3_Packet1(gspca_dev, 0x007b, 0x0011);
+	cit_model3_Packet1(gspca_dev, 0x007d, 0x004b);
+	cit_model3_Packet1(gspca_dev, 0x007f, 0x0022);
+	cit_model3_Packet1(gspca_dev, 0x0080, 0x000c);
+	cit_model3_Packet1(gspca_dev, 0x0081, 0x000b);
+	cit_model3_Packet1(gspca_dev, 0x0083, 0x00fd);
+	cit_model3_Packet1(gspca_dev, 0x0086, 0x000b);
+	cit_model3_Packet1(gspca_dev, 0x0087, 0x000b);
+	cit_model3_Packet1(gspca_dev, 0x007e, 0x000e);
+	cit_model3_Packet1(gspca_dev, 0x0096, 0x00a0);	/* Red sharpness */
+	cit_model3_Packet1(gspca_dev, 0x0097, 0x0096);	/* Blue sharpness */
+	cit_model3_Packet1(gspca_dev, 0x0098, 0x000b);
+
+	/* FIXME we should probably use cit_get_clock_div() here (in
+	   combination with isoc negotiation using the programmable isoc size)
+	   like with the IBM netcam pro). */
+	cit_write_reg(gspca_dev, clock_div, 0x0111); /* Clock Divider */
+
+	switch (gspca_dev->pixfmt.width) {
+	case 160:
+		cit_model3_Packet1(gspca_dev, 0x001f, 0x0000); /* Same */
+		cit_model3_Packet1(gspca_dev, 0x0039, 0x001f); /* Same */
+		cit_model3_Packet1(gspca_dev, 0x003b, 0x003c); /* Same */
+		cit_model3_Packet1(gspca_dev, 0x0040, 0x000a);
+		cit_model3_Packet1(gspca_dev, 0x0051, 0x000a);
+		break;
+	case 320:
+		cit_model3_Packet1(gspca_dev, 0x001f, 0x0000); /* Same */
+		cit_model3_Packet1(gspca_dev, 0x0039, 0x001f); /* Same */
+		cit_model3_Packet1(gspca_dev, 0x003b, 0x003c); /* Same */
+		cit_model3_Packet1(gspca_dev, 0x0040, 0x0008);
+		cit_model3_Packet1(gspca_dev, 0x0051, 0x000b);
+		break;
+	case 640:
+		cit_model3_Packet1(gspca_dev, 0x001f, 0x0002);	/* !Same */
+		cit_model3_Packet1(gspca_dev, 0x0039, 0x003e);	/* !Same */
+		cit_model3_Packet1(gspca_dev, 0x0040, 0x0008);
+		cit_model3_Packet1(gspca_dev, 0x0051, 0x000a);
+		break;
+	}
+
+/*	if (sd->input_index) { */
+	if (rca_input) {
+		for (i = 0; i < ARRAY_SIZE(rca_initdata); i++) {
+			if (rca_initdata[i][0])
+				cit_read_reg(gspca_dev, rca_initdata[i][2], 0);
+			else
+				cit_write_reg(gspca_dev, rca_initdata[i][1],
+					      rca_initdata[i][2]);
+		}
+	}
+
+	return 0;
+}
+
+static int cit_start_model4(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	cit_write_reg(gspca_dev, 0x0000, 0x0100);
+	cit_write_reg(gspca_dev, 0x00c0, 0x0111);
+	cit_write_reg(gspca_dev, 0x00bc, 0x012c);
+	cit_write_reg(gspca_dev, 0x0080, 0x012b);
+	cit_write_reg(gspca_dev, 0x0000, 0x0108);
+	cit_write_reg(gspca_dev, 0x0001, 0x0133);
+	cit_write_reg(gspca_dev, 0x009b, 0x010f);
+	cit_write_reg(gspca_dev, 0x00bb, 0x010f);
+	cit_model4_Packet1(gspca_dev, 0x0038, 0x0000);
+	cit_model4_Packet1(gspca_dev, 0x000a, 0x005c);
+
+	cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+	cit_write_reg(gspca_dev, 0x0004, 0x012f);
+	cit_write_reg(gspca_dev, 0xd141, 0x0124);
+	cit_write_reg(gspca_dev, 0x0000, 0x0127);
+	cit_write_reg(gspca_dev, 0x00fb, 0x012e);
+	cit_write_reg(gspca_dev, 0x0000, 0x0130);
+	cit_write_reg(gspca_dev, 0x8a28, 0x0124);
+	cit_write_reg(gspca_dev, 0x00aa, 0x012f);
+	cit_write_reg(gspca_dev, 0xd055, 0x0124);
+	cit_write_reg(gspca_dev, 0x000c, 0x0127);
+	cit_write_reg(gspca_dev, 0x0009, 0x012e);
+	cit_write_reg(gspca_dev, 0xaa28, 0x0124);
+
+	cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+	cit_write_reg(gspca_dev, 0x0012, 0x012f);
+	cit_write_reg(gspca_dev, 0xd141, 0x0124);
+	cit_write_reg(gspca_dev, 0x0008, 0x0127);
+	cit_write_reg(gspca_dev, 0x00aa, 0x0130);
+	cit_write_reg(gspca_dev, 0x82a8, 0x0124);
+	cit_write_reg(gspca_dev, 0x002a, 0x012d);
+	cit_write_reg(gspca_dev, 0x0000, 0x012f);
+	cit_write_reg(gspca_dev, 0xd145, 0x0124);
+	cit_write_reg(gspca_dev, 0xfffa, 0x0124);
+	cit_model4_Packet1(gspca_dev, 0x0034, 0x0000);
+
+	switch (gspca_dev->pixfmt.width) {
+	case 128: /* 128x96 */
+		cit_write_reg(gspca_dev, 0x0070, 0x0119);
+		cit_write_reg(gspca_dev, 0x00d0, 0x0111);
+		cit_write_reg(gspca_dev, 0x0039, 0x010a);
+		cit_write_reg(gspca_dev, 0x0001, 0x0102);
+		cit_write_reg(gspca_dev, 0x0028, 0x0103);
+		cit_write_reg(gspca_dev, 0x0000, 0x0104);
+		cit_write_reg(gspca_dev, 0x001e, 0x0105);
+		cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+		cit_write_reg(gspca_dev, 0x0016, 0x012f);
+		cit_write_reg(gspca_dev, 0xd141, 0x0124);
+		cit_write_reg(gspca_dev, 0x000a, 0x0127);
+		cit_write_reg(gspca_dev, 0x00aa, 0x0130);
+		cit_write_reg(gspca_dev, 0x82a8, 0x0124);
+		cit_write_reg(gspca_dev, 0x0014, 0x012d);
+		cit_write_reg(gspca_dev, 0x0008, 0x012f);
+		cit_write_reg(gspca_dev, 0xd145, 0x0124);
+		cit_write_reg(gspca_dev, 0x00aa, 0x012e);
+		cit_write_reg(gspca_dev, 0x001a, 0x0130);
+		cit_write_reg(gspca_dev, 0x8a0a, 0x0124);
+		cit_write_reg(gspca_dev, 0x005a, 0x012d);
+		cit_write_reg(gspca_dev, 0x9545, 0x0124);
+		cit_write_reg(gspca_dev, 0x00aa, 0x0127);
+		cit_write_reg(gspca_dev, 0x0018, 0x012e);
+		cit_write_reg(gspca_dev, 0x0043, 0x0130);
+		cit_write_reg(gspca_dev, 0x8a28, 0x0124);
+		cit_write_reg(gspca_dev, 0x00aa, 0x012f);
+		cit_write_reg(gspca_dev, 0xd055, 0x0124);
+		cit_write_reg(gspca_dev, 0x001c, 0x0127);
+		cit_write_reg(gspca_dev, 0x00eb, 0x012e);
+		cit_write_reg(gspca_dev, 0xaa28, 0x0124);
+		cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+		cit_write_reg(gspca_dev, 0x0032, 0x012f);
+		cit_write_reg(gspca_dev, 0xd141, 0x0124);
+		cit_write_reg(gspca_dev, 0x0000, 0x0127);
+		cit_write_reg(gspca_dev, 0x00aa, 0x0130);
+		cit_write_reg(gspca_dev, 0x82a8, 0x0124);
+		cit_write_reg(gspca_dev, 0x0036, 0x012d);
+		cit_write_reg(gspca_dev, 0x0008, 0x012f);
+		cit_write_reg(gspca_dev, 0xd145, 0x0124);
+		cit_write_reg(gspca_dev, 0xfffa, 0x0124);
+		cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+		cit_write_reg(gspca_dev, 0x001e, 0x012f);
+		cit_write_reg(gspca_dev, 0xd141, 0x0124);
+		cit_write_reg(gspca_dev, 0x0017, 0x0127);
+		cit_write_reg(gspca_dev, 0x0013, 0x012e);
+		cit_write_reg(gspca_dev, 0x0031, 0x0130);
+		cit_write_reg(gspca_dev, 0x8a28, 0x0124);
+		cit_write_reg(gspca_dev, 0x0017, 0x012d);
+		cit_write_reg(gspca_dev, 0x0078, 0x012f);
+		cit_write_reg(gspca_dev, 0xd145, 0x0124);
+		cit_write_reg(gspca_dev, 0x0000, 0x0127);
+		cit_write_reg(gspca_dev, 0xfea8, 0x0124);
+		sd->sof_len = 2;
+		break;
+	case 160: /* 160x120 */
+		cit_write_reg(gspca_dev, 0x0038, 0x0119);
+		cit_write_reg(gspca_dev, 0x00d0, 0x0111);
+		cit_write_reg(gspca_dev, 0x00b9, 0x010a);
+		cit_write_reg(gspca_dev, 0x0001, 0x0102);
+		cit_write_reg(gspca_dev, 0x0028, 0x0103);
+		cit_write_reg(gspca_dev, 0x0000, 0x0104);
+		cit_write_reg(gspca_dev, 0x001e, 0x0105);
+		cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+		cit_write_reg(gspca_dev, 0x0016, 0x012f);
+		cit_write_reg(gspca_dev, 0xd141, 0x0124);
+		cit_write_reg(gspca_dev, 0x000b, 0x0127);
+		cit_write_reg(gspca_dev, 0x00aa, 0x0130);
+		cit_write_reg(gspca_dev, 0x82a8, 0x0124);
+		cit_write_reg(gspca_dev, 0x0014, 0x012d);
+		cit_write_reg(gspca_dev, 0x0008, 0x012f);
+		cit_write_reg(gspca_dev, 0xd145, 0x0124);
+		cit_write_reg(gspca_dev, 0x00aa, 0x012e);
+		cit_write_reg(gspca_dev, 0x001a, 0x0130);
+		cit_write_reg(gspca_dev, 0x8a0a, 0x0124);
+		cit_write_reg(gspca_dev, 0x005a, 0x012d);
+		cit_write_reg(gspca_dev, 0x9545, 0x0124);
+		cit_write_reg(gspca_dev, 0x00aa, 0x0127);
+		cit_write_reg(gspca_dev, 0x0018, 0x012e);
+		cit_write_reg(gspca_dev, 0x0043, 0x0130);
+		cit_write_reg(gspca_dev, 0x8a28, 0x0124);
+		cit_write_reg(gspca_dev, 0x00aa, 0x012f);
+		cit_write_reg(gspca_dev, 0xd055, 0x0124);
+		cit_write_reg(gspca_dev, 0x001c, 0x0127);
+		cit_write_reg(gspca_dev, 0x00c7, 0x012e);
+		cit_write_reg(gspca_dev, 0xaa28, 0x0124);
+		cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+		cit_write_reg(gspca_dev, 0x0032, 0x012f);
+		cit_write_reg(gspca_dev, 0xd141, 0x0124);
+		cit_write_reg(gspca_dev, 0x0025, 0x0127);
+		cit_write_reg(gspca_dev, 0x00aa, 0x0130);
+		cit_write_reg(gspca_dev, 0x82a8, 0x0124);
+		cit_write_reg(gspca_dev, 0x0036, 0x012d);
+		cit_write_reg(gspca_dev, 0x0008, 0x012f);
+		cit_write_reg(gspca_dev, 0xd145, 0x0124);
+		cit_write_reg(gspca_dev, 0xfffa, 0x0124);
+		cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+		cit_write_reg(gspca_dev, 0x001e, 0x012f);
+		cit_write_reg(gspca_dev, 0xd141, 0x0124);
+		cit_write_reg(gspca_dev, 0x0048, 0x0127);
+		cit_write_reg(gspca_dev, 0x0035, 0x012e);
+		cit_write_reg(gspca_dev, 0x00d0, 0x0130);
+		cit_write_reg(gspca_dev, 0x8a28, 0x0124);
+		cit_write_reg(gspca_dev, 0x0048, 0x012d);
+		cit_write_reg(gspca_dev, 0x0090, 0x012f);
+		cit_write_reg(gspca_dev, 0xd145, 0x0124);
+		cit_write_reg(gspca_dev, 0x0001, 0x0127);
+		cit_write_reg(gspca_dev, 0xfea8, 0x0124);
+		sd->sof_len = 2;
+		break;
+	case 176: /* 176x144 */
+		cit_write_reg(gspca_dev, 0x0038, 0x0119);
+		cit_write_reg(gspca_dev, 0x00d0, 0x0111);
+		cit_write_reg(gspca_dev, 0x00b9, 0x010a);
+		cit_write_reg(gspca_dev, 0x0001, 0x0102);
+		cit_write_reg(gspca_dev, 0x002c, 0x0103);
+		cit_write_reg(gspca_dev, 0x0000, 0x0104);
+		cit_write_reg(gspca_dev, 0x0024, 0x0105);
+		cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+		cit_write_reg(gspca_dev, 0x0016, 0x012f);
+		cit_write_reg(gspca_dev, 0xd141, 0x0124);
+		cit_write_reg(gspca_dev, 0x0007, 0x0127);
+		cit_write_reg(gspca_dev, 0x00aa, 0x0130);
+		cit_write_reg(gspca_dev, 0x82a8, 0x0124);
+		cit_write_reg(gspca_dev, 0x0014, 0x012d);
+		cit_write_reg(gspca_dev, 0x0001, 0x012f);
+		cit_write_reg(gspca_dev, 0xd145, 0x0124);
+		cit_write_reg(gspca_dev, 0x00aa, 0x012e);
+		cit_write_reg(gspca_dev, 0x001a, 0x0130);
+		cit_write_reg(gspca_dev, 0x8a0a, 0x0124);
+		cit_write_reg(gspca_dev, 0x005e, 0x012d);
+		cit_write_reg(gspca_dev, 0x9545, 0x0124);
+		cit_write_reg(gspca_dev, 0x00aa, 0x0127);
+		cit_write_reg(gspca_dev, 0x0018, 0x012e);
+		cit_write_reg(gspca_dev, 0x0049, 0x0130);
+		cit_write_reg(gspca_dev, 0x8a28, 0x0124);
+		cit_write_reg(gspca_dev, 0x00aa, 0x012f);
+		cit_write_reg(gspca_dev, 0xd055, 0x0124);
+		cit_write_reg(gspca_dev, 0x001c, 0x0127);
+		cit_write_reg(gspca_dev, 0x00c7, 0x012e);
+		cit_write_reg(gspca_dev, 0xaa28, 0x0124);
+		cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+		cit_write_reg(gspca_dev, 0x0032, 0x012f);
+		cit_write_reg(gspca_dev, 0xd141, 0x0124);
+		cit_write_reg(gspca_dev, 0x0028, 0x0127);
+		cit_write_reg(gspca_dev, 0x00aa, 0x0130);
+		cit_write_reg(gspca_dev, 0x82a8, 0x0124);
+		cit_write_reg(gspca_dev, 0x0036, 0x012d);
+		cit_write_reg(gspca_dev, 0x0008, 0x012f);
+		cit_write_reg(gspca_dev, 0xd145, 0x0124);
+		cit_write_reg(gspca_dev, 0xfffa, 0x0124);
+		cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+		cit_write_reg(gspca_dev, 0x001e, 0x012f);
+		cit_write_reg(gspca_dev, 0xd141, 0x0124);
+		cit_write_reg(gspca_dev, 0x0010, 0x0127);
+		cit_write_reg(gspca_dev, 0x0013, 0x012e);
+		cit_write_reg(gspca_dev, 0x002a, 0x0130);
+		cit_write_reg(gspca_dev, 0x8a28, 0x0124);
+		cit_write_reg(gspca_dev, 0x0010, 0x012d);
+		cit_write_reg(gspca_dev, 0x006d, 0x012f);
+		cit_write_reg(gspca_dev, 0xd145, 0x0124);
+		cit_write_reg(gspca_dev, 0x0001, 0x0127);
+		cit_write_reg(gspca_dev, 0xfea8, 0x0124);
+		/* TESTME HDG: this does not seem right
+		   (it is 2 for all other resolutions) */
+		sd->sof_len = 10;
+		break;
+	case 320: /* 320x240 */
+		cit_write_reg(gspca_dev, 0x0070, 0x0119);
+		cit_write_reg(gspca_dev, 0x00d0, 0x0111);
+		cit_write_reg(gspca_dev, 0x0039, 0x010a);
+		cit_write_reg(gspca_dev, 0x0001, 0x0102);
+		cit_write_reg(gspca_dev, 0x0028, 0x0103);
+		cit_write_reg(gspca_dev, 0x0000, 0x0104);
+		cit_write_reg(gspca_dev, 0x001e, 0x0105);
+		cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+		cit_write_reg(gspca_dev, 0x0016, 0x012f);
+		cit_write_reg(gspca_dev, 0xd141, 0x0124);
+		cit_write_reg(gspca_dev, 0x000a, 0x0127);
+		cit_write_reg(gspca_dev, 0x00aa, 0x0130);
+		cit_write_reg(gspca_dev, 0x82a8, 0x0124);
+		cit_write_reg(gspca_dev, 0x0014, 0x012d);
+		cit_write_reg(gspca_dev, 0x0008, 0x012f);
+		cit_write_reg(gspca_dev, 0xd145, 0x0124);
+		cit_write_reg(gspca_dev, 0x00aa, 0x012e);
+		cit_write_reg(gspca_dev, 0x001a, 0x0130);
+		cit_write_reg(gspca_dev, 0x8a0a, 0x0124);
+		cit_write_reg(gspca_dev, 0x005a, 0x012d);
+		cit_write_reg(gspca_dev, 0x9545, 0x0124);
+		cit_write_reg(gspca_dev, 0x00aa, 0x0127);
+		cit_write_reg(gspca_dev, 0x0018, 0x012e);
+		cit_write_reg(gspca_dev, 0x0043, 0x0130);
+		cit_write_reg(gspca_dev, 0x8a28, 0x0124);
+		cit_write_reg(gspca_dev, 0x00aa, 0x012f);
+		cit_write_reg(gspca_dev, 0xd055, 0x0124);
+		cit_write_reg(gspca_dev, 0x001c, 0x0127);
+		cit_write_reg(gspca_dev, 0x00eb, 0x012e);
+		cit_write_reg(gspca_dev, 0xaa28, 0x0124);
+		cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+		cit_write_reg(gspca_dev, 0x0032, 0x012f);
+		cit_write_reg(gspca_dev, 0xd141, 0x0124);
+		cit_write_reg(gspca_dev, 0x0000, 0x0127);
+		cit_write_reg(gspca_dev, 0x00aa, 0x0130);
+		cit_write_reg(gspca_dev, 0x82a8, 0x0124);
+		cit_write_reg(gspca_dev, 0x0036, 0x012d);
+		cit_write_reg(gspca_dev, 0x0008, 0x012f);
+		cit_write_reg(gspca_dev, 0xd145, 0x0124);
+		cit_write_reg(gspca_dev, 0xfffa, 0x0124);
+		cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+		cit_write_reg(gspca_dev, 0x001e, 0x012f);
+		cit_write_reg(gspca_dev, 0xd141, 0x0124);
+		cit_write_reg(gspca_dev, 0x0017, 0x0127);
+		cit_write_reg(gspca_dev, 0x0013, 0x012e);
+		cit_write_reg(gspca_dev, 0x0031, 0x0130);
+		cit_write_reg(gspca_dev, 0x8a28, 0x0124);
+		cit_write_reg(gspca_dev, 0x0017, 0x012d);
+		cit_write_reg(gspca_dev, 0x0078, 0x012f);
+		cit_write_reg(gspca_dev, 0xd145, 0x0124);
+		cit_write_reg(gspca_dev, 0x0000, 0x0127);
+		cit_write_reg(gspca_dev, 0xfea8, 0x0124);
+		sd->sof_len = 2;
+		break;
+	case 352: /* 352x288 */
+		cit_write_reg(gspca_dev, 0x0070, 0x0119);
+		cit_write_reg(gspca_dev, 0x00c0, 0x0111);
+		cit_write_reg(gspca_dev, 0x0039, 0x010a);
+		cit_write_reg(gspca_dev, 0x0001, 0x0102);
+		cit_write_reg(gspca_dev, 0x002c, 0x0103);
+		cit_write_reg(gspca_dev, 0x0000, 0x0104);
+		cit_write_reg(gspca_dev, 0x0024, 0x0105);
+		cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+		cit_write_reg(gspca_dev, 0x0016, 0x012f);
+		cit_write_reg(gspca_dev, 0xd141, 0x0124);
+		cit_write_reg(gspca_dev, 0x0006, 0x0127);
+		cit_write_reg(gspca_dev, 0x00aa, 0x0130);
+		cit_write_reg(gspca_dev, 0x82a8, 0x0124);
+		cit_write_reg(gspca_dev, 0x0014, 0x012d);
+		cit_write_reg(gspca_dev, 0x0002, 0x012f);
+		cit_write_reg(gspca_dev, 0xd145, 0x0124);
+		cit_write_reg(gspca_dev, 0x00aa, 0x012e);
+		cit_write_reg(gspca_dev, 0x001a, 0x0130);
+		cit_write_reg(gspca_dev, 0x8a0a, 0x0124);
+		cit_write_reg(gspca_dev, 0x005e, 0x012d);
+		cit_write_reg(gspca_dev, 0x9545, 0x0124);
+		cit_write_reg(gspca_dev, 0x00aa, 0x0127);
+		cit_write_reg(gspca_dev, 0x0018, 0x012e);
+		cit_write_reg(gspca_dev, 0x0049, 0x0130);
+		cit_write_reg(gspca_dev, 0x8a28, 0x0124);
+		cit_write_reg(gspca_dev, 0x00aa, 0x012f);
+		cit_write_reg(gspca_dev, 0xd055, 0x0124);
+		cit_write_reg(gspca_dev, 0x001c, 0x0127);
+		cit_write_reg(gspca_dev, 0x00cf, 0x012e);
+		cit_write_reg(gspca_dev, 0xaa28, 0x0124);
+		cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+		cit_write_reg(gspca_dev, 0x0032, 0x012f);
+		cit_write_reg(gspca_dev, 0xd141, 0x0124);
+		cit_write_reg(gspca_dev, 0x0000, 0x0127);
+		cit_write_reg(gspca_dev, 0x00aa, 0x0130);
+		cit_write_reg(gspca_dev, 0x82a8, 0x0124);
+		cit_write_reg(gspca_dev, 0x0036, 0x012d);
+		cit_write_reg(gspca_dev, 0x0008, 0x012f);
+		cit_write_reg(gspca_dev, 0xd145, 0x0124);
+		cit_write_reg(gspca_dev, 0xfffa, 0x0124);
+		cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+		cit_write_reg(gspca_dev, 0x001e, 0x012f);
+		cit_write_reg(gspca_dev, 0xd141, 0x0124);
+		cit_write_reg(gspca_dev, 0x0010, 0x0127);
+		cit_write_reg(gspca_dev, 0x0013, 0x012e);
+		cit_write_reg(gspca_dev, 0x0025, 0x0130);
+		cit_write_reg(gspca_dev, 0x8a28, 0x0124);
+		cit_write_reg(gspca_dev, 0x0010, 0x012d);
+		cit_write_reg(gspca_dev, 0x0048, 0x012f);
+		cit_write_reg(gspca_dev, 0xd145, 0x0124);
+		cit_write_reg(gspca_dev, 0x0000, 0x0127);
+		cit_write_reg(gspca_dev, 0xfea8, 0x0124);
+		sd->sof_len = 2;
+		break;
+	}
+
+	cit_model4_Packet1(gspca_dev, 0x0038, 0x0004);
+
+	return 0;
+}
+
+static int cit_start_ibm_netcam_pro(struct gspca_dev *gspca_dev)
+{
+	const unsigned short compression = 0; /* 0=none, 7=best frame rate */
+	int i, clock_div;
+
+	clock_div = cit_get_clock_div(gspca_dev);
+	if (clock_div < 0)
+		return clock_div;
+
+	cit_write_reg(gspca_dev, 0x0003, 0x0133);
+	cit_write_reg(gspca_dev, 0x0000, 0x0117);
+	cit_write_reg(gspca_dev, 0x0008, 0x0123);
+	cit_write_reg(gspca_dev, 0x0000, 0x0100);
+	cit_write_reg(gspca_dev, 0x0060, 0x0116);
+	/* cit_write_reg(gspca_dev, 0x0002, 0x0112); see sd_stop0 */
+	cit_write_reg(gspca_dev, 0x0000, 0x0133);
+	cit_write_reg(gspca_dev, 0x0000, 0x0123);
+	cit_write_reg(gspca_dev, 0x0001, 0x0117);
+	cit_write_reg(gspca_dev, 0x0040, 0x0108);
+	cit_write_reg(gspca_dev, 0x0019, 0x012c);
+	cit_write_reg(gspca_dev, 0x0060, 0x0116);
+	/* cit_write_reg(gspca_dev, 0x000b, 0x0115); see sd_stop0 */
+
+	cit_model3_Packet1(gspca_dev, 0x0049, 0x0000);
+
+	cit_write_reg(gspca_dev, 0x0000, 0x0101); /* Same on 160x120, 320x240 */
+	cit_write_reg(gspca_dev, 0x003a, 0x0102); /* Hstart */
+	cit_write_reg(gspca_dev, 0x00a0, 0x0103); /* Same on 160x120, 320x240 */
+	cit_write_reg(gspca_dev, 0x0078, 0x0105); /* Same on 160x120, 320x240 */
+	cit_write_reg(gspca_dev, 0x0000, 0x010a); /* Same */
+	cit_write_reg(gspca_dev, 0x0002, 0x011d); /* Same on 160x120, 320x240 */
+	cit_write_reg(gspca_dev, 0x0000, 0x0129); /* Same */
+	cit_write_reg(gspca_dev, 0x00fc, 0x012b); /* Same */
+	cit_write_reg(gspca_dev, 0x0022, 0x012a); /* Same */
+
+	switch (gspca_dev->pixfmt.width) {
+	case 160: /* 160x120 */
+		cit_write_reg(gspca_dev, 0x0024, 0x010b);
+		cit_write_reg(gspca_dev, 0x0089, 0x0119);
+		cit_write_reg(gspca_dev, 0x000a, 0x011b);
+		cit_write_reg(gspca_dev, 0x0003, 0x011e);
+		cit_write_reg(gspca_dev, 0x0007, 0x0104);
+		cit_write_reg(gspca_dev, 0x0009, 0x011a);
+		cit_write_reg(gspca_dev, 0x008b, 0x011c);
+		cit_write_reg(gspca_dev, 0x0008, 0x0118);
+		cit_write_reg(gspca_dev, 0x0000, 0x0132);
+		break;
+	case 320: /* 320x240 */
+		cit_write_reg(gspca_dev, 0x0028, 0x010b);
+		cit_write_reg(gspca_dev, 0x00d9, 0x0119);
+		cit_write_reg(gspca_dev, 0x0006, 0x011b);
+		cit_write_reg(gspca_dev, 0x0000, 0x011e);
+		cit_write_reg(gspca_dev, 0x000e, 0x0104);
+		cit_write_reg(gspca_dev, 0x0004, 0x011a);
+		cit_write_reg(gspca_dev, 0x003f, 0x011c);
+		cit_write_reg(gspca_dev, 0x000c, 0x0118);
+		cit_write_reg(gspca_dev, 0x0000, 0x0132);
+		break;
+	}
+
+	cit_model3_Packet1(gspca_dev, 0x0019, 0x0031);
+	cit_model3_Packet1(gspca_dev, 0x001a, 0x0003);
+	cit_model3_Packet1(gspca_dev, 0x001b, 0x0038);
+	cit_model3_Packet1(gspca_dev, 0x001c, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x0024, 0x0001);
+	cit_model3_Packet1(gspca_dev, 0x0027, 0x0001);
+	cit_model3_Packet1(gspca_dev, 0x002a, 0x0004);
+	cit_model3_Packet1(gspca_dev, 0x0035, 0x000b);
+	cit_model3_Packet1(gspca_dev, 0x003f, 0x0001);
+	cit_model3_Packet1(gspca_dev, 0x0044, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x0054, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x00c4, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x00e7, 0x0001);
+	cit_model3_Packet1(gspca_dev, 0x00e9, 0x0001);
+	cit_model3_Packet1(gspca_dev, 0x00ee, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x00f3, 0x00c0);
+
+	cit_write_reg(gspca_dev, compression, 0x0109);
+	cit_write_reg(gspca_dev, clock_div, 0x0111);
+
+/*	if (sd->input_index) { */
+	if (rca_input) {
+		for (i = 0; i < ARRAY_SIZE(rca_initdata); i++) {
+			if (rca_initdata[i][0])
+				cit_read_reg(gspca_dev, rca_initdata[i][2], 0);
+			else
+				cit_write_reg(gspca_dev, rca_initdata[i][1],
+					      rca_initdata[i][2]);
+		}
+	}
+
+	return 0;
+}
+
+/* -- start the camera -- */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int packet_size;
+
+	packet_size = cit_get_packet_size(gspca_dev);
+	if (packet_size < 0)
+		return packet_size;
+
+	switch (sd->model) {
+	case CIT_MODEL0:
+		cit_start_model0(gspca_dev);
+		break;
+	case CIT_MODEL1:
+		cit_start_model1(gspca_dev);
+		break;
+	case CIT_MODEL2:
+		cit_start_model2(gspca_dev);
+		break;
+	case CIT_MODEL3:
+		cit_start_model3(gspca_dev);
+		break;
+	case CIT_MODEL4:
+		cit_start_model4(gspca_dev);
+		break;
+	case CIT_IBM_NETCAM_PRO:
+		cit_start_ibm_netcam_pro(gspca_dev);
+		break;
+	}
+
+	/* Program max isoc packet size */
+	cit_write_reg(gspca_dev, packet_size >> 8, 0x0106);
+	cit_write_reg(gspca_dev, packet_size & 0xff, 0x0107);
+
+	cit_restart_stream(gspca_dev);
+
+	return 0;
+}
+
+static int sd_isoc_init(struct gspca_dev *gspca_dev)
+{
+	struct usb_host_interface *alt;
+	int max_packet_size;
+
+	switch (gspca_dev->pixfmt.width) {
+	case 160:
+		max_packet_size = 450;
+		break;
+	case 176:
+		max_packet_size = 600;
+		break;
+	default:
+		max_packet_size = 1022;
+		break;
+	}
+
+	/* Start isoc bandwidth "negotiation" at max isoc bandwidth */
+	alt = &gspca_dev->dev->actconfig->intf_cache[0]->altsetting[1];
+	alt->endpoint[0].desc.wMaxPacketSize = cpu_to_le16(max_packet_size);
+
+	return 0;
+}
+
+static int sd_isoc_nego(struct gspca_dev *gspca_dev)
+{
+	int ret, packet_size, min_packet_size;
+	struct usb_host_interface *alt;
+
+	switch (gspca_dev->pixfmt.width) {
+	case 160:
+		min_packet_size = 200;
+		break;
+	case 176:
+		min_packet_size = 266;
+		break;
+	default:
+		min_packet_size = 400;
+		break;
+	}
+
+	alt = &gspca_dev->dev->actconfig->intf_cache[0]->altsetting[1];
+	packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize);
+	if (packet_size <= min_packet_size)
+		return -EIO;
+
+	packet_size -= 100;
+	if (packet_size < min_packet_size)
+		packet_size = min_packet_size;
+	alt->endpoint[0].desc.wMaxPacketSize = cpu_to_le16(packet_size);
+
+	ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, 1);
+	if (ret < 0)
+		pr_err("set alt 1 err %d\n", ret);
+
+	return ret;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+	cit_write_reg(gspca_dev, 0x0000, 0x010c);
+}
+
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (!gspca_dev->present)
+		return;
+
+	switch (sd->model) {
+	case CIT_MODEL0:
+		/* HDG windows does this, but it causes the cams autogain to
+		   restart from a gain of 0, which does not look good when
+		   changing resolutions. */
+		/* cit_write_reg(gspca_dev, 0x0000, 0x0112); */
+		cit_write_reg(gspca_dev, 0x00c0, 0x0100); /* LED Off */
+		break;
+	case CIT_MODEL1:
+		cit_send_FF_04_02(gspca_dev);
+		cit_read_reg(gspca_dev, 0x0100, 0);
+		cit_write_reg(gspca_dev, 0x81, 0x0100);	/* LED Off */
+		break;
+	case CIT_MODEL2:
+		v4l2_ctrl_grab(sd->lighting, false);
+		/* Fall through! */
+	case CIT_MODEL4:
+		cit_model2_Packet1(gspca_dev, 0x0030, 0x0004);
+
+		cit_write_reg(gspca_dev, 0x0080, 0x0100);	/* LED Off */
+		cit_write_reg(gspca_dev, 0x0020, 0x0111);
+		cit_write_reg(gspca_dev, 0x00a0, 0x0111);
+
+		cit_model2_Packet1(gspca_dev, 0x0030, 0x0002);
+
+		cit_write_reg(gspca_dev, 0x0020, 0x0111);
+		cit_write_reg(gspca_dev, 0x0000, 0x0112);
+		break;
+	case CIT_MODEL3:
+		cit_write_reg(gspca_dev, 0x0006, 0x012c);
+		cit_model3_Packet1(gspca_dev, 0x0046, 0x0000);
+		cit_read_reg(gspca_dev, 0x0116, 0);
+		cit_write_reg(gspca_dev, 0x0064, 0x0116);
+		cit_read_reg(gspca_dev, 0x0115, 0);
+		cit_write_reg(gspca_dev, 0x0003, 0x0115);
+		cit_write_reg(gspca_dev, 0x0008, 0x0123);
+		cit_write_reg(gspca_dev, 0x0000, 0x0117);
+		cit_write_reg(gspca_dev, 0x0000, 0x0112);
+		cit_write_reg(gspca_dev, 0x0080, 0x0100);
+		break;
+	case CIT_IBM_NETCAM_PRO:
+		cit_model3_Packet1(gspca_dev, 0x0049, 0x00ff);
+		cit_write_reg(gspca_dev, 0x0006, 0x012c);
+		cit_write_reg(gspca_dev, 0x0000, 0x0116);
+		/* HDG windows does this, but I cannot get the camera
+		   to restart with this without redoing the entire init
+		   sequence which makes switching modes really slow */
+		/* cit_write_reg(gspca_dev, 0x0006, 0x0115); */
+		cit_write_reg(gspca_dev, 0x0008, 0x0123);
+		cit_write_reg(gspca_dev, 0x0000, 0x0117);
+		cit_write_reg(gspca_dev, 0x0003, 0x0133);
+		cit_write_reg(gspca_dev, 0x0000, 0x0111);
+		/* HDG windows does this, but I get a green picture when
+		   restarting the stream after this */
+		/* cit_write_reg(gspca_dev, 0x0000, 0x0112); */
+		cit_write_reg(gspca_dev, 0x00c0, 0x0100);
+		break;
+	}
+
+#if IS_ENABLED(CONFIG_INPUT)
+	/* If the last button state is pressed, release it now! */
+	if (sd->button_state) {
+		input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
+		input_sync(gspca_dev->input_dev);
+		sd->button_state = 0;
+	}
+#endif
+}
+
+static u8 *cit_find_sof(struct gspca_dev *gspca_dev, u8 *data, int len)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u8 byte3 = 0, byte4 = 0;
+	int i;
+
+	switch (sd->model) {
+	case CIT_MODEL0:
+	case CIT_MODEL1:
+	case CIT_MODEL3:
+	case CIT_IBM_NETCAM_PRO:
+		switch (gspca_dev->pixfmt.width) {
+		case 160: /* 160x120 */
+			byte3 = 0x02;
+			byte4 = 0x0a;
+			break;
+		case 176: /* 176x144 */
+			byte3 = 0x02;
+			byte4 = 0x0e;
+			break;
+		case 320: /* 320x240 */
+			byte3 = 0x02;
+			byte4 = 0x08;
+			break;
+		case 352: /* 352x288 */
+			byte3 = 0x02;
+			byte4 = 0x00;
+			break;
+		case 640:
+			byte3 = 0x03;
+			byte4 = 0x08;
+			break;
+		}
+
+		/* These have a different byte3 */
+		if (sd->model <= CIT_MODEL1)
+			byte3 = 0x00;
+
+		for (i = 0; i < len; i++) {
+			/* For this model the SOF always starts at offset 0
+			   so no need to search the entire frame */
+			if (sd->model == CIT_MODEL0 && sd->sof_read != i)
+				break;
+
+			switch (sd->sof_read) {
+			case 0:
+				if (data[i] == 0x00)
+					sd->sof_read++;
+				break;
+			case 1:
+				if (data[i] == 0xff)
+					sd->sof_read++;
+				else if (data[i] == 0x00)
+					sd->sof_read = 1;
+				else
+					sd->sof_read = 0;
+				break;
+			case 2:
+				if (data[i] == byte3)
+					sd->sof_read++;
+				else if (data[i] == 0x00)
+					sd->sof_read = 1;
+				else
+					sd->sof_read = 0;
+				break;
+			case 3:
+				if (data[i] == byte4) {
+					sd->sof_read = 0;
+					return data + i + (sd->sof_len - 3);
+				}
+				if (byte3 == 0x00 && data[i] == 0xff)
+					sd->sof_read = 2;
+				else if (data[i] == 0x00)
+					sd->sof_read = 1;
+				else
+					sd->sof_read = 0;
+				break;
+			}
+		}
+		break;
+	case CIT_MODEL2:
+	case CIT_MODEL4:
+		/* TESTME we need to find a longer sof signature to avoid
+		   false positives */
+		for (i = 0; i < len; i++) {
+			switch (sd->sof_read) {
+			case 0:
+				if (data[i] == 0x00)
+					sd->sof_read++;
+				break;
+			case 1:
+				sd->sof_read = 0;
+				if (data[i] == 0xff) {
+					if (i >= 4)
+						PDEBUG(D_FRAM,
+						       "header found at offset: %d: %02x %02x 00 %3ph\n",
+						       i - 1,
+						       data[i - 4],
+						       data[i - 3],
+						       &data[i]);
+					else
+						PDEBUG(D_FRAM,
+						       "header found at offset: %d: 00 %3ph\n",
+						       i - 1,
+						       &data[i]);
+					return data + i + (sd->sof_len - 1);
+				}
+				break;
+			}
+		}
+		break;
+	}
+	return NULL;
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+			u8 *data, int len)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	unsigned char *sof;
+
+	sof = cit_find_sof(gspca_dev, data, len);
+	if (sof) {
+		int n;
+
+		/* finish decoding current frame */
+		n = sof - data;
+		if (n > sd->sof_len)
+			n -= sd->sof_len;
+		else
+			n = 0;
+		gspca_frame_add(gspca_dev, LAST_PACKET,
+				data, n);
+		gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0);
+		len -= sof - data;
+		data = sof;
+	}
+
+	gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+}
+
+#if IS_ENABLED(CONFIG_INPUT)
+static void cit_check_button(struct gspca_dev *gspca_dev)
+{
+	int new_button_state;
+	struct sd *sd = (struct sd *)gspca_dev;
+
+	switch (sd->model) {
+	case CIT_MODEL3:
+	case CIT_IBM_NETCAM_PRO:
+		break;
+	default: /* TEST ME unknown if this works on other models too */
+		return;
+	}
+
+	/* Read the button state */
+	cit_read_reg(gspca_dev, 0x0113, 0);
+	new_button_state = !gspca_dev->usb_buf[0];
+
+	/* Tell the cam we've seen the button press, notice that this
+	   is a nop (iow the cam keeps reporting pressed) until the
+	   button is actually released. */
+	if (new_button_state)
+		cit_write_reg(gspca_dev, 0x01, 0x0113);
+
+	if (sd->button_state != new_button_state) {
+		input_report_key(gspca_dev->input_dev, KEY_CAMERA,
+				 new_button_state);
+		input_sync(gspca_dev->input_dev);
+		sd->button_state = new_button_state;
+	}
+}
+#endif
+
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct gspca_dev *gspca_dev =
+		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+	struct sd *sd = (struct sd *)gspca_dev;
+
+	gspca_dev->usb_err = 0;
+
+	if (!gspca_dev->streaming)
+		return 0;
+
+	if (sd->stop_on_control_change)
+		sd_stopN(gspca_dev);
+	switch (ctrl->id) {
+	case V4L2_CID_BRIGHTNESS:
+		cit_set_brightness(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_CONTRAST:
+		cit_set_contrast(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_HUE:
+		cit_set_hue(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_HFLIP:
+		cit_set_hflip(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_SHARPNESS:
+		cit_set_sharpness(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_BACKLIGHT_COMPENSATION:
+		cit_set_lighting(gspca_dev, ctrl->val);
+		break;
+	}
+	if (sd->stop_on_control_change)
+		cit_restart_stream(gspca_dev);
+	return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+	.s_ctrl = sd_s_ctrl,
+};
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *)gspca_dev;
+	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+	bool has_brightness;
+	bool has_contrast;
+	bool has_hue;
+	bool has_sharpness;
+	bool has_lighting;
+	bool has_hflip;
+
+	has_brightness = has_contrast = has_hue =
+		has_sharpness = has_hflip = has_lighting = false;
+	switch (sd->model) {
+	case CIT_MODEL0:
+		has_contrast = has_hflip = true;
+		break;
+	case CIT_MODEL1:
+		has_brightness = has_contrast =
+			has_sharpness = has_lighting = true;
+		break;
+	case CIT_MODEL2:
+		has_brightness = has_hue = has_lighting = true;
+		break;
+	case CIT_MODEL3:
+		has_brightness = has_contrast = has_sharpness = true;
+		break;
+	case CIT_MODEL4:
+		has_brightness = has_hue = true;
+		break;
+	case CIT_IBM_NETCAM_PRO:
+		has_brightness = has_hue =
+			has_sharpness = has_hflip = has_lighting = true;
+		break;
+	}
+	gspca_dev->vdev.ctrl_handler = hdl;
+	v4l2_ctrl_handler_init(hdl, 5);
+	if (has_brightness)
+		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_BRIGHTNESS, 0, 63, 1, 32);
+	if (has_contrast)
+		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_CONTRAST, 0, 20, 1, 10);
+	if (has_hue)
+		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_HUE, 0, 127, 1, 63);
+	if (has_sharpness)
+		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_SHARPNESS, 0, 6, 1, 3);
+	if (has_lighting)
+		sd->lighting = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_BACKLIGHT_COMPENSATION, 0, 2, 1, 1);
+	if (has_hflip)
+		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_HFLIP, 0, 1, 1, 0);
+
+	if (hdl->error) {
+		pr_err("Could not initialize controls\n");
+		return hdl->error;
+	}
+	return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+	.name = MODULE_NAME,
+	.config = sd_config,
+	.init = sd_init,
+	.init_controls = sd_init_controls,
+	.start = sd_start,
+	.stopN = sd_stopN,
+	.stop0 = sd_stop0,
+	.pkt_scan = sd_pkt_scan,
+#if IS_ENABLED(CONFIG_INPUT)
+	.dq_callback = cit_check_button,
+	.other_input = 1,
+#endif
+};
+
+static const struct sd_desc sd_desc_isoc_nego = {
+	.name = MODULE_NAME,
+	.config = sd_config,
+	.init = sd_init,
+	.init_controls = sd_init_controls,
+	.start = sd_start,
+	.isoc_init = sd_isoc_init,
+	.isoc_nego = sd_isoc_nego,
+	.stopN = sd_stopN,
+	.stop0 = sd_stop0,
+	.pkt_scan = sd_pkt_scan,
+#if IS_ENABLED(CONFIG_INPUT)
+	.dq_callback = cit_check_button,
+	.other_input = 1,
+#endif
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+	{ USB_DEVICE_VER(0x0545, 0x8080, 0x0001, 0x0001), .driver_info = CIT_MODEL0 },
+	{ USB_DEVICE_VER(0x0545, 0x8080, 0x0002, 0x0002), .driver_info = CIT_MODEL1 },
+	{ USB_DEVICE_VER(0x0545, 0x8080, 0x030a, 0x030a), .driver_info = CIT_MODEL2 },
+	{ USB_DEVICE_VER(0x0545, 0x8080, 0x0301, 0x0301), .driver_info = CIT_MODEL3 },
+	{ USB_DEVICE_VER(0x0545, 0x8002, 0x030a, 0x030a), .driver_info = CIT_MODEL4 },
+	{ USB_DEVICE_VER(0x0545, 0x800c, 0x030a, 0x030a), .driver_info = CIT_MODEL2 },
+	{ USB_DEVICE_VER(0x0545, 0x800d, 0x030a, 0x030a), .driver_info = CIT_MODEL4 },
+	{}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+			const struct usb_device_id *id)
+{
+	const struct sd_desc *desc = &sd_desc;
+
+	switch (id->driver_info) {
+	case CIT_MODEL0:
+	case CIT_MODEL1:
+		if (intf->cur_altsetting->desc.bInterfaceNumber != 2)
+			return -ENODEV;
+		break;
+	case CIT_MODEL2:
+	case CIT_MODEL4:
+		if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
+			return -ENODEV;
+		break;
+	case CIT_MODEL3:
+		if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
+			return -ENODEV;
+		/* FIXME this likely applies to all model3 cams and probably
+		   to other models too. */
+		if (ibm_netcam_pro)
+			desc = &sd_desc_isoc_nego;
+		break;
+	}
+
+	return gspca_dev_probe2(intf, id, 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,
+#ifdef CONFIG_PM
+	.suspend = gspca_suspend,
+	.resume = gspca_resume,
+	.reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/usb/gspca/zc3xx-reg.h b/drivers/media/usb/gspca/zc3xx-reg.h
new file mode 100644
index 0000000..a1bd94e
--- /dev/null
+++ b/drivers/media/usb/gspca/zc3xx-reg.h
@@ -0,0 +1,251 @@
+/*
+ * 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
+
+/* 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
+
+/* 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/usb/gspca/zc3xx.c b/drivers/media/usb/gspca/zc3xx.c
new file mode 100644
index 0000000..c5d8ee6
--- /dev/null
+++ b/drivers/media/usb/gspca/zc3xx.c
@@ -0,0 +1,7021 @@
+/*
+ * Z-Star/Vimicro zc301/zc302p/vc30x driver
+ *
+ * Copyright (C) 2009-2012 Jean-Francois Moine <http://moinejf.free.fr>
+ * Copyright (C) 2004 2005 2006 Michel Xhaard mxhaard@magic.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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/input.h>
+#include "gspca.h"
+#include "jpeg.h"
+
+MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>, "
+		"Serge A. Suchkov <Serge.A.S@tochka.ru>");
+MODULE_DESCRIPTION("GSPCA ZC03xx/VC3xx USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+static int force_sensor = -1;
+
+#define REG08_DEF 3		/* default JPEG compression (75%) */
+#include "zc3xx-reg.h"
+
+/* specific webcam descriptor */
+struct sd {
+	struct gspca_dev gspca_dev;	/* !! must be the first item */
+
+	struct { /* gamma/brightness/contrast control cluster */
+		struct v4l2_ctrl *gamma;
+		struct v4l2_ctrl *brightness;
+		struct v4l2_ctrl *contrast;
+	};
+	struct { /* autogain/exposure control cluster */
+		struct v4l2_ctrl *autogain;
+		struct v4l2_ctrl *exposure;
+	};
+	struct v4l2_ctrl *plfreq;
+	struct v4l2_ctrl *sharpness;
+	struct v4l2_ctrl *jpegqual;
+
+	struct work_struct work;
+	struct workqueue_struct *work_thread;
+
+	u8 reg08;		/* webcam compression quality */
+
+	u8 bridge;
+	u8 sensor;		/* Type of image sensor chip */
+	u16 chip_revision;
+
+	u8 jpeg_hdr[JPEG_HDR_SZ];
+};
+enum bridges {
+	BRIDGE_ZC301,
+	BRIDGE_ZC303,
+};
+enum sensors {
+	SENSOR_ADCM2700,
+	SENSOR_CS2102,
+	SENSOR_CS2102K,
+	SENSOR_GC0303,
+	SENSOR_GC0305,
+	SENSOR_HDCS2020,
+	SENSOR_HV7131B,
+	SENSOR_HV7131R,
+	SENSOR_ICM105A,
+	SENSOR_MC501CB,
+	SENSOR_MT9V111_1,	/* (mi360soc) zc301 */
+	SENSOR_MT9V111_3,	/* (mi360soc) zc303 */
+	SENSOR_OV7620,		/* OV7648 - same values */
+	SENSOR_OV7630C,
+	SENSOR_PAS106,
+	SENSOR_PAS202B,
+	SENSOR_PB0330,
+	SENSOR_PO2030,
+	SENSOR_TAS5130C,
+	SENSOR_MAX
+};
+
+static const 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 const struct v4l2_pix_format broken_vga_mode[] = {
+	{320, 232, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+		.bytesperline = 320,
+		.sizeimage = 320 * 232 * 4 / 8 + 590,
+		.colorspace = V4L2_COLORSPACE_JPEG,
+		.priv = 1},
+	{640, 472, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+		.bytesperline = 640,
+		.sizeimage = 640 * 472 * 3 / 8 + 590,
+		.colorspace = V4L2_COLORSPACE_JPEG,
+		.priv = 0},
+};
+
+static const 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},
+};
+
+/*
+ * Bridge reg08 bits 1-2 -> JPEG quality conversion table. Note the highest
+ * quality setting is not usable as USB 1 does not have enough bandwidth.
+ */
+static u8 jpeg_qual[] = {50, 75, 87, /* 94 */};
+
+/* usb exchanges */
+struct usb_action {
+	u8	req;
+	u8	val;
+	u16	idx;
+};
+
+static const struct usb_action adcm2700_Initial[] = {
+	{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},		/* 00,00,01,cc */
+	{0xa0, 0x04, ZC3XX_R002_CLOCKSELECT},		/* 00,02,04,cc */
+	{0xa0, 0x00, ZC3XX_R008_CLOCKSETTING},		/* 00,08,03,cc */
+	{0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},	/* 00,10,0a,cc */
+	{0xa0, 0xd3, ZC3XX_R08B_I2CDEVICEADDR},		/* 00,8b,d3,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, 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, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},	/* 00,12,05,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 */
+	{0xbb, 0x00, 0x0400},				/* 04,00,00,bb */
+	{0xdd, 0x00, 0x0010},				/* 00,00,10,dd */
+	{0xbb, 0x0f, 0x140f},				/* 14,0f,0f,bb */
+	{0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION},	/* 01,01,37,cc */
+	{0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},		/* 01,00,0d,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, 0x58, ZC3XX_R116_RGAIN},			/* 01,16,58,cc */
+	{0xa0, 0x5a, ZC3XX_R118_BGAIN},			/* 01,18,5a,cc */
+	{0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE},	/* 01,80,02,cc */
+	{0xa0, 0xd3, ZC3XX_R08B_I2CDEVICEADDR},		/* 00,8b,d3,cc */
+	{0xbb, 0x00, 0x0408},				/* 04,00,08,bb */
+	{0xdd, 0x00, 0x0200},				/* 00,02,00,dd */
+	{0xbb, 0x00, 0x0400},				/* 04,00,00,bb */
+	{0xdd, 0x00, 0x0010},				/* 00,00,10,dd */
+	{0xbb, 0x0f, 0x140f},				/* 14,0f,0f,bb */
+	{0xbb, 0xe0, 0x0c2e},				/* 0c,e0,2e,bb */
+	{0xbb, 0x01, 0x2000},				/* 20,01,00,bb */
+	{0xbb, 0x96, 0x2400},				/* 24,96,00,bb */
+	{0xbb, 0x06, 0x1006},				/* 10,06,06,bb */
+	{0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT},	/* 00,10,01,cc */
+	{0xdd, 0x00, 0x0010},				/* 00,00,10,dd */
+	{0xaa, 0xfe, 0x0002},				/* 00,fe,02,aa */
+	{0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},	/* 00,10,0a,cc */
+	{0xdd, 0x00, 0x0010},				/* 00,00,10,dd */
+	{0xbb, 0x5f, 0x2090},				/* 20,5f,90,bb */
+	{0xbb, 0x01, 0x8000},				/* 80,01,00,bb */
+	{0xbb, 0x09, 0x8400},				/* 84,09,00,bb */
+	{0xbb, 0x86, 0x0002},				/* 00,86,02,bb */
+	{0xbb, 0xe6, 0x0401},				/* 04,e6,01,bb */
+	{0xbb, 0x86, 0x0802},				/* 08,86,02,bb */
+	{0xbb, 0xe6, 0x0c01},				/* 0c,e6,01,bb */
+	{0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT},	/* 00,10,01,cc */
+	{0xdd, 0x00, 0x0010},				/* 00,00,10,dd */
+	{0xaa, 0xfe, 0x0000},				/* 00,fe,00,aa */
+	{0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},	/* 00,10,0a,cc */
+	{0xdd, 0x00, 0x0010},				/* 00,00,10,dd */
+	{0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT},	/* 00,10,01,cc */
+	{0xaa, 0xfe, 0x0020},				/* 00,fe,20,aa */
+/*mswin+*/
+	{0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT},
+	{0xaa, 0xfe, 0x0002},
+	{0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
+	{0xaa, 0xb4, 0xcd37},
+	{0xaa, 0xa4, 0x0004},
+	{0xaa, 0xa8, 0x0007},
+	{0xaa, 0xac, 0x0004},
+/*mswin-*/
+	{0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},	/* 00,10,0a,cc */
+	{0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT},	/* 00,10,01,cc */
+	{0xdd, 0x00, 0x0010},				/* 00,00,10,dd */
+	{0xaa, 0xfe, 0x0000},				/* 00,fe,00,aa */
+	{0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},	/* 00,10,0a,cc */
+	{0xdd, 0x00, 0x0010},				/* 00,00,10,dd */
+	{0xbb, 0x04, 0x0400},				/* 04,04,00,bb */
+	{0xdd, 0x00, 0x0100},				/* 00,01,00,dd */
+	{0xbb, 0x01, 0x0400},				/* 04,01,00,bb */
+	{0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT},	/* 00,10,01,cc */
+	{0xaa, 0xfe, 0x0002},				/* 00,fe,02,aa */
+	{0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},	/* 00,10,0a,cc */
+	{0xbb, 0x41, 0x2803},				/* 28,41,03,bb */
+	{0xbb, 0x40, 0x2c03},				/* 2c,40,03,bb */
+	{0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT},	/* 00,10,01,cc */
+	{0xaa, 0xfe, 0x0010},				/* 00,fe,10,aa */
+	{}
+};
+static const struct usb_action adcm2700_InitialScale[] = {
+	{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},		/* 00,00,01,cc */
+	{0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},		/* 00,02,10,cc */
+	{0xa0, 0x00, ZC3XX_R008_CLOCKSETTING},		/* 00,08,03,cc */
+	{0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},	/* 00,10,0a,cc */
+	{0xa0, 0xd3, ZC3XX_R08B_I2CDEVICEADDR},		/* 00,8b,d3,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, 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, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},	/* 00,12,05,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, 0xd8, ZC3XX_R09C_WINHEIGHTLOW},		/* 00,9c,d8,cc */
+	{0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW},		/* 00,9e,88,cc */
+	{0xbb, 0x00, 0x0400},				/* 04,00,00,bb */
+	{0xdd, 0x00, 0x0010},				/* 00,00,10,dd */
+	{0xbb, 0x0f, 0x140f},				/* 14,0f,0f,bb */
+	{0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION},	/* 01,01,37,cc */
+	{0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},		/* 01,00,0d,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, 0x58, ZC3XX_R116_RGAIN},			/* 01,16,58,cc */
+	{0xa0, 0x5a, ZC3XX_R118_BGAIN},			/* 01,18,5a,cc */
+	{0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE},	/* 01,80,02,cc */
+	{0xa0, 0xd3, ZC3XX_R08B_I2CDEVICEADDR},		/* 00,8b,d3,cc */
+	{0xbb, 0x00, 0x0408},				/* 04,00,08,bb */
+	{0xdd, 0x00, 0x0200},				/* 00,02,00,dd */
+	{0xbb, 0x00, 0x0400},				/* 04,00,00,bb */
+	{0xdd, 0x00, 0x0050},				/* 00,00,50,dd */
+	{0xbb, 0x0f, 0x140f},				/* 14,0f,0f,bb */
+	{0xbb, 0xe0, 0x0c2e},				/* 0c,e0,2e,bb */
+	{0xbb, 0x01, 0x2000},				/* 20,01,00,bb */
+	{0xbb, 0x96, 0x2400},				/* 24,96,00,bb */
+	{0xbb, 0x06, 0x1006},				/* 10,06,06,bb */
+	{0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT},	/* 00,10,01,cc */
+	{0xdd, 0x00, 0x0010},				/* 00,00,10,dd */
+	{0xaa, 0xfe, 0x0002},				/* 00,fe,02,aa */
+	{0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},	/* 00,10,0a,cc */
+	{0xdd, 0x00, 0x0010},				/* 00,00,10,dd */
+	{0xbb, 0x5f, 0x2090},				/* 20,5f,90,bb */
+	{0xbb, 0x01, 0x8000},				/* 80,01,00,bb */
+	{0xbb, 0x09, 0x8400},				/* 84,09,00,bb */
+	{0xbb, 0x86, 0x0002},				/* 00,88,02,bb */
+	{0xbb, 0xe6, 0x0401},				/* 04,e6,01,bb */
+	{0xbb, 0x86, 0x0802},				/* 08,88,02,bb */
+	{0xbb, 0xe6, 0x0c01},				/* 0c,e6,01,bb */
+	{0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT},	/* 00,10,01,cc */
+	{0xdd, 0x00, 0x0010},				/* 00,00,10,dd */
+	{0xaa, 0xfe, 0x0000},				/* 00,fe,00,aa */
+	{0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},	/* 00,10,0a,cc */
+	{0xdd, 0x00, 0x0010},				/* 00,00,10,dd */
+	{0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT},	/* 00,10,01,cc */
+	{0xaa, 0xfe, 0x0020},				/* 00,fe,20,aa */
+	/*******/
+	{0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},	/* 00,10,0a,cc */
+	{0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT},	/* 00,10,01,cc */
+	{0xdd, 0x00, 0x0010},				/* 00,00,10,dd */
+	{0xaa, 0xfe, 0x0000},				/* 00,fe,00,aa */
+	{0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},	/* 00,10,0a,cc */
+	{0xdd, 0x00, 0x0010},				/* 00,00,10,dd */
+	{0xbb, 0x04, 0x0400},				/* 04,04,00,bb */
+	{0xdd, 0x00, 0x0100},				/* 00,01,00,dd */
+	{0xbb, 0x01, 0x0400},				/* 04,01,00,bb */
+	{0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT},	/* 00,10,01,cc */
+	{0xaa, 0xfe, 0x0002},				/* 00,fe,02,aa */
+	{0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},	/* 00,10,0a,cc */
+	{0xbb, 0x41, 0x2803},				/* 28,41,03,bb */
+	{0xbb, 0x40, 0x2c03},				/* 2c,40,03,bb */
+	{0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT},	/* 00,10,01,cc */
+	{0xaa, 0xfe, 0x0010},				/* 00,fe,10,aa */
+	{}
+};
+static const struct usb_action adcm2700_50HZ[] = {
+	{0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT},	/* 00,10,01,cc */
+	{0xaa, 0xfe, 0x0002},				/* 00,fe,02,aa */
+	{0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},	/* 00,10,0a,cc */
+	{0xbb, 0x05, 0x8400},				/* 84,05,00,bb */
+	{0xbb, 0xd0, 0xb007},				/* b0,d0,07,bb */
+	{0xbb, 0xa0, 0xb80f},				/* b8,a0,0f,bb */
+	{0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT},	/* 00,10,01,cc */
+	{0xaa, 0xfe, 0x0010},				/* 00,fe,10,aa */
+	{0xaa, 0x26, 0x00d0},				/* 00,26,d0,aa */
+	{0xaa, 0x28, 0x0002},				/* 00,28,02,aa */
+	{}
+};
+static const struct usb_action adcm2700_60HZ[] = {
+	{0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT},	/* 00,10,01,cc */
+	{0xaa, 0xfe, 0x0002},				/* 00,fe,02,aa */
+	{0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},	/* 00,10,0a,cc */
+	{0xbb, 0x07, 0x8400},				/* 84,07,00,bb */
+	{0xbb, 0x82, 0xb006},				/* b0,82,06,bb */
+	{0xbb, 0x04, 0xb80d},				/* b8,04,0d,bb */
+	{0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT},	/* 00,10,01,cc */
+	{0xaa, 0xfe, 0x0010},				/* 00,fe,10,aa */
+	{0xaa, 0x26, 0x0057},				/* 00,26,57,aa */
+	{0xaa, 0x28, 0x0002},				/* 00,28,02,aa */
+	{}
+};
+static const struct usb_action adcm2700_NoFliker[] = {
+	{0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT},	/* 00,10,01,cc */
+	{0xaa, 0xfe, 0x0002},				/* 00,fe,02,aa */
+	{0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},	/* 00,10,0a,cc */
+	{0xbb, 0x07, 0x8400},				/* 84,07,00,bb */
+	{0xbb, 0x05, 0xb000},				/* b0,05,00,bb */
+	{0xbb, 0xa0, 0xb801},				/* b8,a0,01,bb */
+	{0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT},	/* 00,10,01,cc */
+	{0xaa, 0xfe, 0x0010},				/* 00,fe,10,aa */
+	{}
+};
+static const struct usb_action cs2102_InitialScale[] = {	/* 320x240 */
+	{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},
+	{}
+};
+
+static const struct usb_action cs2102_Initial[] = {	/* 640x480 */
+	{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},
+	{}
+};
+static const struct usb_action cs2102_50HZScale[] = {
+	{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+	{0xaa, 0x23, 0x0001},
+	{0xaa, 0x24, 0x005f},
+	{0xaa, 0x25, 0x0090},
+	{0xaa, 0x21, 0x00dd},
+	{0xa0, 0x02, ZC3XX_R190_EXPOSURELIMITHIGH},
+	{0xa0, 0xbf, ZC3XX_R191_EXPOSURELIMITMID},
+	{0xa0, 0x20, ZC3XX_R192_EXPOSURELIMITLOW},
+	{0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+	{0xa0, 0x3a, ZC3XX_R196_ANTIFLICKERMID},
+	{0xa0, 0x98, ZC3XX_R197_ANTIFLICKERLOW},
+	{0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+	{0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+	{0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
+	{0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+	{0xa0, 0xdd, ZC3XX_R01D_HSYNC_0},
+	{0xa0, 0xe4, ZC3XX_R01E_HSYNC_1},
+	{0xa0, 0xf0, ZC3XX_R01F_HSYNC_2},
+	{0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+	{}
+};
+static const struct usb_action cs2102_50HZ[] = {
+	{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+	{0xaa, 0x23, 0x0000},
+	{0xaa, 0x24, 0x00af},
+	{0xaa, 0x25, 0x00c8},
+	{0xaa, 0x21, 0x0068},
+	{0xa0, 0x01, ZC3XX_R190_EXPOSURELIMITHIGH},
+	{0xa0, 0x5f, ZC3XX_R191_EXPOSURELIMITMID},
+	{0xa0, 0x90, ZC3XX_R192_EXPOSURELIMITLOW},
+	{0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+	{0xa0, 0x1d, ZC3XX_R196_ANTIFLICKERMID},
+	{0xa0, 0x4c, ZC3XX_R197_ANTIFLICKERLOW},
+	{0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+	{0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+	{0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
+	{0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+	{0xa0, 0x68, ZC3XX_R01D_HSYNC_0},
+	{0xa0, 0xe3, ZC3XX_R01E_HSYNC_1},
+	{0xa0, 0xf0, ZC3XX_R01F_HSYNC_2},
+	{0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+	{}
+};
+static const struct usb_action cs2102_60HZScale[] = {
+	{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},
+	{}
+};
+static const struct usb_action cs2102_60HZ[] = {
+	{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},
+	{}
+};
+static const struct usb_action cs2102_NoFlikerScale[] = {
+	{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+	{0xaa, 0x23, 0x0001},
+	{0xaa, 0x24, 0x005f},
+	{0xaa, 0x25, 0x0000},
+	{0xaa, 0x21, 0x0001},
+	{0xa0, 0x02, ZC3XX_R190_EXPOSURELIMITHIGH},
+	{0xa0, 0xbf, ZC3XX_R191_EXPOSURELIMITMID},
+	{0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW},
+	{0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+	{0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+	{0xa0, 0x80, ZC3XX_R197_ANTIFLICKERLOW},
+	{0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+	{0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+	{0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
+	{0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
+	{0xa0, 0x01, ZC3XX_R01D_HSYNC_0},
+	{0xa0, 0x40, ZC3XX_R01E_HSYNC_1},
+	{0xa0, 0xa0, ZC3XX_R01F_HSYNC_2},
+	{0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+	{}
+};
+static const struct usb_action cs2102_NoFliker[] = {
+	{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+	{0xaa, 0x23, 0x0000},
+	{0xaa, 0x24, 0x00af},
+	{0xaa, 0x25, 0x0080},
+	{0xaa, 0x21, 0x0001},
+	{0xa0, 0x01, ZC3XX_R190_EXPOSURELIMITHIGH},
+	{0xa0, 0x5f, ZC3XX_R191_EXPOSURELIMITMID},
+	{0xa0, 0x80, ZC3XX_R192_EXPOSURELIMITLOW},
+	{0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+	{0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+	{0xa0, 0x80, ZC3XX_R197_ANTIFLICKERLOW},
+	{0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+	{0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+	{0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
+	{0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
+	{0xa0, 0x01, ZC3XX_R01D_HSYNC_0},
+	{0xa0, 0x40, ZC3XX_R01E_HSYNC_1},
+	{0xa0, 0xa0, ZC3XX_R01F_HSYNC_2},
+	{0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+	{}
+};
+
+/* CS2102_KOCOM */
+static const struct usb_action cs2102K_InitialScale[] = {
+	{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_Initial[] = {
+	{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},
+/*fixme: next sequence = i2c exchanges*/
+	{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},
+/*fixme:what does the next sequence?*/
+	{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 */
+	{}
+};
+
+static const struct usb_action hdcs2020_InitialScale[] = {
+	{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 hdcs2020_Initial[] = {
+	{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 hdcs2020_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 hdcs2020_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 hdcs2020_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 hv7131b_InitialScale[] = {	/* 320x240 */
+	{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},		/* 00,17,88,aa */
+	{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, 0x0090},			/* 00,02,80,aa */
+	{}
+};
+
+static const struct usb_action hv7131b_Initial[] = {	/* 640x480*/
+	{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}, */
+	{}
+};
+static const struct usb_action hv7131b_50HZ[] = {	/* 640x480*/
+	{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},	/* 00,19,00,cc */
+	{0xaa, 0x25, 0x0007},			/* 00,25,07,aa */
+	{0xaa, 0x26, 0x0053},			/* 00,26,53,aa */
+	{0xaa, 0x27, 0x0000},			/* 00,27,00,aa */
+	{0xaa, 0x20, 0x0000},			/* 00,20,00,aa */
+	{0xaa, 0x21, 0x0050},			/* 00,21,50,aa */
+	{0xaa, 0x22, 0x001b},			/* 00,22,1b,aa */
+	{0xaa, 0x23, 0x00fc},			/* 00,23,fc,aa */
+	{0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH},	/* 01,90,2f,cc */
+	{0xa0, 0x9b, ZC3XX_R191_EXPOSURELIMITMID},	/* 01,91,9b,cc */
+	{0xa0, 0x80, ZC3XX_R192_EXPOSURELIMITLOW},	/* 01,92,80,cc */
+	{0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},	/* 01,95,00,cc */
+	{0xa0, 0xea, ZC3XX_R196_ANTIFLICKERMID},	/* 01,96,ea,cc */
+	{0xa0, 0x60, ZC3XX_R197_ANTIFLICKERLOW},	/* 01,97,60,cc */
+	{0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},	/* 01,8c,0c,cc */
+	{0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},	/* 01,8f,18,cc */
+	{0xa0, 0x18, ZC3XX_R1A9_DIGITALLIMITDIFF},	/* 01,a9,18,cc */
+	{0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},	/* 01,aa,24,cc */
+	{0xa0, 0x00, ZC3XX_R01D_HSYNC_0},	/* 00,1d,00,cc */
+	{0xa0, 0x50, ZC3XX_R01E_HSYNC_1},	/* 00,1e,50,cc */
+	{0xa0, 0x1b, ZC3XX_R01F_HSYNC_2},	/* 00,1f,1b,cc */
+	{0xa0, 0xfc, ZC3XX_R020_HSYNC_3},	/* 00,20,fc,cc */
+	{}
+};
+static const struct usb_action hv7131b_50HZScale[] = {	/* 320x240 */
+	{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},	/* 00,19,00,cc */
+	{0xaa, 0x25, 0x0007},			/* 00,25,07,aa */
+	{0xaa, 0x26, 0x0053},			/* 00,26,53,aa */
+	{0xaa, 0x27, 0x0000},			/* 00,27,00,aa */
+	{0xaa, 0x20, 0x0000},			/* 00,20,00,aa */
+	{0xaa, 0x21, 0x0050},			/* 00,21,50,aa */
+	{0xaa, 0x22, 0x0012},			/* 00,22,12,aa */
+	{0xaa, 0x23, 0x0080},			/* 00,23,80,aa */
+	{0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH},	/* 01,90,2f,cc */
+	{0xa0, 0x9b, ZC3XX_R191_EXPOSURELIMITMID},	/* 01,91,9b,cc */
+	{0xa0, 0x80, ZC3XX_R192_EXPOSURELIMITLOW},	/* 01,92,80,cc */
+	{0xa0, 0x01, ZC3XX_R195_ANTIFLICKERHIGH},	/* 01,95,01,cc */
+	{0xa0, 0xd4, ZC3XX_R196_ANTIFLICKERMID},	/* 01,96,d4,cc */
+	{0xa0, 0xc0, ZC3XX_R197_ANTIFLICKERLOW},	/* 01,97,c0,cc */
+	{0xa0, 0x07, ZC3XX_R18C_AEFREEZE},	/* 01,8c,07,cc */
+	{0xa0, 0x0f, ZC3XX_R18F_AEUNFREEZE},	/* 01,8f,0f,cc */
+	{0xa0, 0x18, ZC3XX_R1A9_DIGITALLIMITDIFF},	/* 01,a9,18,cc */
+	{0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},	/* 01,aa,24,cc */
+	{0xa0, 0x00, ZC3XX_R01D_HSYNC_0},	/* 00,1d,00,cc */
+	{0xa0, 0x50, ZC3XX_R01E_HSYNC_1},	/* 00,1e,50,cc */
+	{0xa0, 0x12, ZC3XX_R01F_HSYNC_2},	/* 00,1f,12,cc */
+	{0xa0, 0x80, ZC3XX_R020_HSYNC_3},	/* 00,20,80,cc */
+	{}
+};
+static const struct usb_action hv7131b_60HZ[] = {	/* 640x480*/
+	{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},	/* 00,19,00,cc */
+	{0xaa, 0x25, 0x0007},			/* 00,25,07,aa */
+	{0xaa, 0x26, 0x00a1},			/* 00,26,a1,aa */
+	{0xaa, 0x27, 0x0020},			/* 00,27,20,aa */
+	{0xaa, 0x20, 0x0000},			/* 00,20,00,aa */
+	{0xaa, 0x21, 0x0040},			/* 00,21,40,aa */
+	{0xaa, 0x22, 0x0013},			/* 00,22,13,aa */
+	{0xaa, 0x23, 0x004c},			/* 00,23,4c,aa */
+	{0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH},	/* 01,90,2f,cc */
+	{0xa0, 0x4d, ZC3XX_R191_EXPOSURELIMITMID},	/* 01,91,4d,cc */
+	{0xa0, 0x60, ZC3XX_R192_EXPOSURELIMITLOW},	/* 01,92,60,cc */
+	{0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},	/* 01,95,00,cc */
+	{0xa0, 0xc3, ZC3XX_R196_ANTIFLICKERMID},	/* 01,96,c3,cc */
+	{0xa0, 0x50, ZC3XX_R197_ANTIFLICKERLOW},	/* 01,97,50,cc */
+	{0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},	/* 01,8c,0c,cc */
+	{0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},	/* 01,8f,18,cc */
+	{0xa0, 0x18, ZC3XX_R1A9_DIGITALLIMITDIFF},	/* 01,a9,18,cc */
+	{0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},	/* 01,aa,24,cc */
+	{0xa0, 0x00, ZC3XX_R01D_HSYNC_0},	/* 00,1d,00,cc */
+	{0xa0, 0x40, ZC3XX_R01E_HSYNC_1},	/* 00,1e,40,cc */
+	{0xa0, 0x13, ZC3XX_R01F_HSYNC_2},	/* 00,1f,13,cc */
+	{0xa0, 0x4c, ZC3XX_R020_HSYNC_3},	/* 00,20,4c,cc */
+	{}
+};
+static const struct usb_action hv7131b_60HZScale[] = {	/* 320x240 */
+	{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},	/* 00,19,00,cc */
+	{0xaa, 0x25, 0x0007},			/* 00,25,07,aa */
+	{0xaa, 0x26, 0x00a1},			/* 00,26,a1,aa */
+	{0xaa, 0x27, 0x0020},			/* 00,27,20,aa */
+	{0xaa, 0x20, 0x0000},			/* 00,20,00,aa */
+	{0xaa, 0x21, 0x00a0},			/* 00,21,a0,aa */
+	{0xaa, 0x22, 0x0016},			/* 00,22,16,aa */
+	{0xaa, 0x23, 0x0040},			/* 00,23,40,aa */
+	{0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH},	/* 01,90,2f,cc */
+	{0xa0, 0x4d, ZC3XX_R191_EXPOSURELIMITMID},	/* 01,91,4d,cc */
+	{0xa0, 0x60, ZC3XX_R192_EXPOSURELIMITLOW},	/* 01,92,60,cc */
+	{0xa0, 0x01, ZC3XX_R195_ANTIFLICKERHIGH},	/* 01,95,01,cc */
+	{0xa0, 0x86, ZC3XX_R196_ANTIFLICKERMID},	/* 01,96,86,cc */
+	{0xa0, 0xa0, ZC3XX_R197_ANTIFLICKERLOW},	/* 01,97,a0,cc */
+	{0xa0, 0x07, ZC3XX_R18C_AEFREEZE},	/* 01,8c,07,cc */
+	{0xa0, 0x0f, ZC3XX_R18F_AEUNFREEZE},	/* 01,8f,0f,cc */
+	{0xa0, 0x18, ZC3XX_R1A9_DIGITALLIMITDIFF},	/* 01,a9,18,cc */
+	{0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},	/* 01,aa,24,cc */
+	{0xa0, 0x00, ZC3XX_R01D_HSYNC_0},	/* 00,1d,00,cc */
+	{0xa0, 0xa0, ZC3XX_R01E_HSYNC_1},	/* 00,1e,a0,cc */
+	{0xa0, 0x16, ZC3XX_R01F_HSYNC_2},	/* 00,1f,16,cc */
+	{0xa0, 0x40, ZC3XX_R020_HSYNC_3},	/* 00,20,40,cc */
+	{}
+};
+static const struct usb_action hv7131b_NoFliker[] = {	/* 640x480*/
+	{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},	/* 00,19,00,cc */
+	{0xaa, 0x25, 0x0003},			/* 00,25,03,aa */
+	{0xaa, 0x26, 0x0000},			/* 00,26,00,aa */
+	{0xaa, 0x27, 0x0000},			/* 00,27,00,aa */
+	{0xaa, 0x20, 0x0000},			/* 00,20,00,aa */
+	{0xaa, 0x21, 0x0010},			/* 00,21,10,aa */
+	{0xaa, 0x22, 0x0000},			/* 00,22,00,aa */
+	{0xaa, 0x23, 0x0003},			/* 00,23,03,aa */
+	{0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH},	/* 01,90,2f,cc */
+	{0xa0, 0xf8, ZC3XX_R191_EXPOSURELIMITMID},	/* 01,91,f8,cc */
+	{0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW},	/* 01,92,00,cc */
+	{0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},	/* 01,95,00,cc */
+	{0xa0, 0x02, ZC3XX_R196_ANTIFLICKERMID},	/* 01,96,02,cc */
+	{0xa0, 0x00, ZC3XX_R197_ANTIFLICKERLOW},	/* 01,97,00,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, 0x00, ZC3XX_R01D_HSYNC_0},	/* 00,1d,00,cc */
+	{0xa0, 0x10, ZC3XX_R01E_HSYNC_1},	/* 00,1e,10,cc */
+	{0xa0, 0x00, ZC3XX_R01F_HSYNC_2},	/* 00,1f,00,cc */
+	{0xa0, 0x03, ZC3XX_R020_HSYNC_3},	/* 00,20,03,cc */
+	{}
+};
+static const struct usb_action hv7131b_NoFlikerScale[] = { /* 320x240 */
+	{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},	/* 00,19,00,cc */
+	{0xaa, 0x25, 0x0003},			/* 00,25,03,aa */
+	{0xaa, 0x26, 0x0000},			/* 00,26,00,aa */
+	{0xaa, 0x27, 0x0000},			/* 00,27,00,aa */
+	{0xaa, 0x20, 0x0000},			/* 00,20,00,aa */
+	{0xaa, 0x21, 0x00a0},			/* 00,21,a0,aa */
+	{0xaa, 0x22, 0x0016},			/* 00,22,16,aa */
+	{0xaa, 0x23, 0x0040},			/* 00,23,40,aa */
+	{0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH},	/* 01,90,2f,cc */
+	{0xa0, 0xf8, ZC3XX_R191_EXPOSURELIMITMID},	/* 01,91,f8,cc */
+	{0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW},	/* 01,92,00,cc */
+	{0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},	/* 01,95,00,cc */
+	{0xa0, 0x02, ZC3XX_R196_ANTIFLICKERMID},	/* 01,96,02,cc */
+	{0xa0, 0x00, ZC3XX_R197_ANTIFLICKERLOW},	/* 01,97,00,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, 0x00, ZC3XX_R01D_HSYNC_0},	/* 00,1d,00,cc */
+	{0xa0, 0xa0, ZC3XX_R01E_HSYNC_1},	/* 00,1e,a0,cc */
+	{0xa0, 0x16, ZC3XX_R01F_HSYNC_2},	/* 00,1f,16,cc */
+	{0xa0, 0x40, ZC3XX_R020_HSYNC_3},	/* 00,20,40,cc */
+	{}
+};
+
+/* from lPEPI264v.inf (hv7131b!) */
+static const struct usb_action hv7131r_InitialScale[] = {
+	{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, 0x07, 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, 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, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
+	{0xdd, 0x00, 0x0200},
+	{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},
+	{0xaa, 0x30, 0x000b},
+	{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+	{0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+	{0xa0, 0x78, 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},
+	{}
+};
+static const struct usb_action hv7131r_Initial[] = {
+	{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+	{0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
+	{0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT},
+	{0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+	{0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION},
+	{0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+	{0xa0, 0x07, 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, 0x01, ZC3XX_R09B_WINHEIGHTHIGH},
+	{0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW},
+	{0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH},
+	{0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW},
+	{0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+	{0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+	{0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
+	{0xdd, 0x00, 0x0200},
+	{0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+	{0xaa, 0x01, 0x000c},
+	{0xaa, 0x11, 0x0000},
+	{0xaa, 0x13, 0x0000},
+	{0xaa, 0x14, 0x0001},
+	{0xaa, 0x15, 0x00e6},
+	{0xaa, 0x16, 0x0002},
+	{0xaa, 0x17, 0x0086},
+	{0xaa, 0x30, 0x000b},
+	{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+	{0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+	{0xa0, 0x78, 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},
+	{}
+};
+static const struct usb_action hv7131r_50HZ[] = {
+	{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+	{0xa0, 0x06, ZC3XX_R190_EXPOSURELIMITHIGH},
+	{0xa0, 0x68, ZC3XX_R191_EXPOSURELIMITMID},
+	{0xa0, 0xa0, ZC3XX_R192_EXPOSURELIMITLOW},
+	{0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+	{0xa0, 0xea, ZC3XX_R196_ANTIFLICKERMID},
+	{0xa0, 0x60, ZC3XX_R197_ANTIFLICKERLOW},
+	{0xa0, 0x18, ZC3XX_R18C_AEFREEZE},
+	{0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+	{0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
+	{0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+	{0xa0, 0x00, ZC3XX_R01D_HSYNC_0},
+	{0xa0, 0xd0, ZC3XX_R01E_HSYNC_1},
+	{0xa0, 0x00, ZC3XX_R01F_HSYNC_2},
+	{0xa0, 0x08, ZC3XX_R020_HSYNC_3},
+	{}
+};
+static const struct usb_action hv7131r_50HZScale[] = {
+	{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+	{0xa0, 0x0c, ZC3XX_R190_EXPOSURELIMITHIGH},
+	{0xa0, 0xd1, ZC3XX_R191_EXPOSURELIMITMID},
+	{0xa0, 0x40, ZC3XX_R192_EXPOSURELIMITLOW},
+	{0xa0, 0x01, ZC3XX_R195_ANTIFLICKERHIGH},
+	{0xa0, 0xd4, ZC3XX_R196_ANTIFLICKERMID},
+	{0xa0, 0xc0, ZC3XX_R197_ANTIFLICKERLOW},
+	{0xa0, 0x18, ZC3XX_R18C_AEFREEZE},
+	{0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+	{0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
+	{0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+	{0xa0, 0x00, ZC3XX_R01D_HSYNC_0},
+	{0xa0, 0xd0, ZC3XX_R01E_HSYNC_1},
+	{0xa0, 0x00, ZC3XX_R01F_HSYNC_2},
+	{0xa0, 0x08, ZC3XX_R020_HSYNC_3},
+	{}
+};
+static const struct usb_action hv7131r_60HZ[] = {
+	{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+	{0xa0, 0x06, ZC3XX_R190_EXPOSURELIMITHIGH},
+	{0xa0, 0x1a, ZC3XX_R191_EXPOSURELIMITMID},
+	{0xa0, 0x80, ZC3XX_R192_EXPOSURELIMITLOW},
+	{0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+	{0xa0, 0xc3, ZC3XX_R196_ANTIFLICKERMID},
+	{0xa0, 0x50, ZC3XX_R197_ANTIFLICKERLOW},
+	{0xa0, 0x18, ZC3XX_R18C_AEFREEZE},
+	{0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+	{0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
+	{0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+	{0xa0, 0x00, ZC3XX_R01D_HSYNC_0},
+	{0xa0, 0xd0, ZC3XX_R01E_HSYNC_1},
+	{0xa0, 0x00, ZC3XX_R01F_HSYNC_2},
+	{0xa0, 0x08, ZC3XX_R020_HSYNC_3},
+	{}
+};
+static const struct usb_action hv7131r_60HZScale[] = {
+	{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+	{0xa0, 0x0c, ZC3XX_R190_EXPOSURELIMITHIGH},
+	{0xa0, 0x35, ZC3XX_R191_EXPOSURELIMITMID},
+	{0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW},
+	{0xa0, 0x01, ZC3XX_R195_ANTIFLICKERHIGH},
+	{0xa0, 0x86, ZC3XX_R196_ANTIFLICKERMID},
+	{0xa0, 0xa0, ZC3XX_R197_ANTIFLICKERLOW},
+	{0xa0, 0x18, ZC3XX_R18C_AEFREEZE},
+	{0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+	{0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
+	{0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+	{0xa0, 0x00, ZC3XX_R01D_HSYNC_0},
+	{0xa0, 0xd0, ZC3XX_R01E_HSYNC_1},
+	{0xa0, 0x00, ZC3XX_R01F_HSYNC_2},
+	{0xa0, 0x08, ZC3XX_R020_HSYNC_3},
+	{}
+};
+static const struct usb_action hv7131r_NoFliker[] = {
+	{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+	{0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH},
+	{0xa0, 0xf8, ZC3XX_R191_EXPOSURELIMITMID},
+	{0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW},
+	{0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+	{0xa0, 0x02, ZC3XX_R196_ANTIFLICKERMID},
+	{0xa0, 0x58, ZC3XX_R197_ANTIFLICKERLOW},
+	{0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
+	{0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
+	{0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
+	{0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
+	{0xa0, 0x00, ZC3XX_R01D_HSYNC_0},
+	{0xa0, 0xd0, ZC3XX_R01E_HSYNC_1},
+	{0xa0, 0x00, ZC3XX_R01F_HSYNC_2},
+	{0xa0, 0x08, ZC3XX_R020_HSYNC_3},
+	{}
+};
+static const struct usb_action hv7131r_NoFlikerScale[] = {
+	{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+	{0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH},
+	{0xa0, 0xf8, ZC3XX_R191_EXPOSURELIMITMID},
+	{0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW},
+	{0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+	{0xa0, 0x04, ZC3XX_R196_ANTIFLICKERMID},
+	{0xa0, 0xb0, ZC3XX_R197_ANTIFLICKERLOW},
+	{0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
+	{0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
+	{0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
+	{0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
+	{0xa0, 0x00, ZC3XX_R01D_HSYNC_0},
+	{0xa0, 0xd0, ZC3XX_R01E_HSYNC_1},
+	{0xa0, 0x00, ZC3XX_R01F_HSYNC_2},
+	{0xa0, 0x08, ZC3XX_R020_HSYNC_3},
+	{}
+};
+
+static const struct usb_action icm105a_InitialScale[] = {
+	{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 icm105a_Initial[] = {
+	{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_50HZScale[] = {
+	{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_50HZ[] = {
+	{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_60HZScale[] = {
+	{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_60HZ[] = {
+	{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_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, 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_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, 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_Initial[] = {
+	{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, 0x001a}, /* 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_InitialScale[] = {	/* 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, 0x001a}, /* 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 */
+	{}
+};
+
+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 */
+	{}
+};
+
+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 */
+	{}
+};
+
+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 */
+	{}
+};
+
+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 */
+	{}
+};
+
+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 */
+	{}
+};
+
+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 */
+static const struct usb_action ov7620_Initial[] = {	/* 640x480 */
+	{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 */
+	{}
+};
+static const struct usb_action ov7620_InitialScale[] = {	/* 320x240 */
+	{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 */
+	{}
+};
+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) */
+	{}
+};
+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},		*/
+	{}
+};
+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_InitialScale[] = {
+	{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},
+	{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_Initial[] = {
+	{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_InitialScale[] = {	/* 176x144 */
+/* JPEG control */
+	{0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+/* Sream and Sensor specific */
+	{0xa0, 0x0f, ZC3XX_R010_CMOSSENSORSELECT},
+/* Picture size */
+	{0xa0, 0x00, ZC3XX_R003_FRAMEWIDTHHIGH},
+	{0xa0, 0xb0, ZC3XX_R004_FRAMEWIDTHLOW},
+	{0xa0, 0x00, ZC3XX_R005_FRAMEHEIGHTHIGH},
+	{0xa0, 0x90, ZC3XX_R006_FRAMEHEIGHTLOW},
+/* System */
+	{0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+/* Sream and Sensor specific */
+	{0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
+	{0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+/* Sensor Interface */
+	{0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE},
+/* Window inside sensor array */
+	{0xa0, 0x03, ZC3XX_R09A_WINXSTARTLOW},
+	{0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+	{0xa0, 0x03, ZC3XX_R11C_FIRSTXLOW},
+	{0xa0, 0x28, ZC3XX_R09C_WINHEIGHTLOW},
+	{0xa0, 0x68, ZC3XX_R09E_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 registers */
+	{0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
+/* Frame retreiving */
+	{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+/* Gains */
+	{0xa0, 0xa0, ZC3XX_R1A8_DIGITALGAIN},
+/* Unknown */
+	{0xa0, 0x00, 0x01ad},
+/* Sharpness */
+	{0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+	{0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+/* Other registers */
+	{0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+/* Auto exposure and white balance */
+	{0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+/*Dead pixels */
+	{0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+/* EEPROM */
+	{0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+/* JPEG control */
+	{0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+	{0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},
+	{0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05},
+/* Other registers */
+	{0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+/* Auto exposure and white balance */
+	{0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+/*Dead pixels */
+	{0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+/* EEPROM */
+	{0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+/* JPEG control */
+	{0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+	{0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},
+	{0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05},
+
+	{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},
+	{0xa0, 0x08, ZC3XX_R182_WINXWIDTH},
+	{0xa0, 0x16, ZC3XX_R183_WINXCENTER},
+	{0xa0, 0x03, ZC3XX_R184_WINYSTART},
+	{0xa0, 0x05, ZC3XX_R185_WINYWIDTH},
+	{0xa0, 0x14, ZC3XX_R186_WINYCENTER},
+	{0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+/* Auto exposure and white balance */
+	{0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+	{0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID},
+	{0xa0, 0xb1, ZC3XX_R192_EXPOSURELIMITLOW},
+	{0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+	{0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+	{0xa0, 0x87, ZC3XX_R197_ANTIFLICKERLOW},
+	{0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
+	{0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
+/* sensor on */
+	{0xaa, 0x07, 0x00b1},
+	{0xaa, 0x05, 0x0003},
+	{0xaa, 0x04, 0x0001},
+	{0xaa, 0x03, 0x003b},
+/* Gains */
+	{0xa0, 0x20, ZC3XX_R1A9_DIGITALLIMITDIFF},
+	{0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP},
+	{0xa0, 0xa0, ZC3XX_R11D_GLOBALGAIN},
+	{0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+/* Auto correction */
+	{0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
+	{0xa1, 0x01, 0x0180},				/* AutoCorrectEnable */
+	{0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+/* Gains */
+	{0xa0, 0x40, ZC3XX_R116_RGAIN},
+	{0xa0, 0x40, ZC3XX_R117_GGAIN},
+	{0xa0, 0x40, ZC3XX_R118_BGAIN},
+	{}
+};
+
+static const struct usb_action pas106b_Initial[] = {	/* 352x288 */
+/* JPEG control */
+	{0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+/* Sream and Sensor specific */
+	{0xa0, 0x0f, ZC3XX_R010_CMOSSENSORSELECT},
+/* Picture size */
+	{0xa0, 0x01, ZC3XX_R003_FRAMEWIDTHHIGH},
+	{0xa0, 0x60, ZC3XX_R004_FRAMEWIDTHLOW},
+	{0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+	{0xa0, 0x20, ZC3XX_R006_FRAMEHEIGHTLOW},
+/* System */
+	{0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+/* Sream and Sensor specific */
+	{0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
+	{0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+/* Sensor Interface */
+	{0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE},
+/* Window inside sensor array */
+	{0xa0, 0x03, ZC3XX_R09A_WINXSTARTLOW},
+	{0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+	{0xa0, 0x03, ZC3XX_R11C_FIRSTXLOW},
+	{0xa0, 0x28, ZC3XX_R09C_WINHEIGHTLOW},
+	{0xa0, 0x68, ZC3XX_R09E_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 registers */
+	{0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
+/* Frame retreiving */
+	{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+/* Gains */
+	{0xa0, 0xa0, ZC3XX_R1A8_DIGITALGAIN},
+/* Unknown */
+	{0xa0, 0x00, 0x01ad},
+/* Sharpness */
+	{0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+	{0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+/* Other registers */
+	{0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+/* Auto exposure and white balance */
+	{0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+	{0xa0, 0x80, ZC3XX_R18D_YTARGET},
+/*Dead pixels */
+	{0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+/* EEPROM */
+	{0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+/* JPEG control */
+	{0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+	{0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},
+	{0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05},
+/* Other registers */
+	{0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+/* Auto exposure and white balance */
+	{0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+/*Dead pixels */
+	{0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+/* EEPROM */
+	{0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+/* JPEG control */
+	{0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+	{0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},
+	{0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05},
+
+	{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},
+	{0xa0, 0x08, ZC3XX_R182_WINXWIDTH},
+	{0xa0, 0x16, ZC3XX_R183_WINXCENTER},
+	{0xa0, 0x03, ZC3XX_R184_WINYSTART},
+	{0xa0, 0x05, ZC3XX_R185_WINYWIDTH},
+	{0xa0, 0x14, ZC3XX_R186_WINYCENTER},
+	{0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+
+/* Auto exposure and white balance */
+	{0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+	{0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID},
+	{0xa0, 0xb1, ZC3XX_R192_EXPOSURELIMITLOW},
+
+	{0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+	{0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+	{0xa0, 0x87, ZC3XX_R197_ANTIFLICKERLOW},
+
+	{0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+	{0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+/* sensor on */
+	{0xaa, 0x07, 0x00b1},
+	{0xaa, 0x05, 0x0003},
+	{0xaa, 0x04, 0x0001},
+	{0xaa, 0x03, 0x003b},
+/* Gains */
+	{0xa0, 0x20, ZC3XX_R1A9_DIGITALLIMITDIFF},
+	{0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP},
+	{0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+/* Auto correction */
+	{0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
+	{0xa1, 0x01, 0x0180},				/* AutoCorrectEnable */
+	{0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+/* Gains */
+	{0xa0, 0x40, ZC3XX_R116_RGAIN},
+	{0xa0, 0x40, ZC3XX_R117_GGAIN},
+	{0xa0, 0x40, ZC3XX_R118_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 */
+	{}
+};
+
+/* from lvWIMv.inf 046d:08a2/:08aa 2007/06/03 */
+static const struct usb_action pas202b_Initial[] = {	/* 640x480 */
+	{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},		/* 00,00,01,cc */
+	{0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+	{0xa0, 0x0e, ZC3XX_R010_CMOSSENSORSELECT},	/* 00,10,0e,cc */
+	{0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},		/* 00,02,00,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, 0x08, ZC3XX_R08D_COMPABILITYMODE},	/* 00,8d,08,cc */
+	{0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},		/* 00,98,00,cc */
+	{0xa0, 0x03, ZC3XX_R09A_WINXSTARTLOW},		/* 00,9a,03,cc */
+	{0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},		/* 01,1a,00,cc */
+	{0xa0, 0x03, ZC3XX_R11C_FIRSTXLOW},		/* 01,1c,03,cc */
+	{0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH},		/* 00,9b,01,cc */
+	{0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW},		/* 00,9c,e6,cc */
+	{0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH},		/* 00,9d,02,cc */
+	{0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW},		/* 00,9e,86,cc */
+	{0xaa, 0x02, 0x0002},			/* 00,02,04,aa --> 02 */
+	{0xaa, 0x07, 0x0006},				/* 00,07,06,aa */
+	{0xaa, 0x08, 0x0002},				/* 00,08,02,aa */
+	{0xaa, 0x09, 0x0006},				/* 00,09,06,aa */
+	{0xaa, 0x0a, 0x0001},				/* 00,0a,01,aa */
+	{0xaa, 0x0b, 0x0001},				/* 00,0b,01,aa */
+	{0xaa, 0x0c, 0x0006},
+	{0xaa, 0x0d, 0x0000},				/* 00,0d,00,aa */
+	{0xaa, 0x10, 0x0000},				/* 00,10,00,aa */
+	{0xaa, 0x12, 0x0005},				/* 00,12,05,aa */
+	{0xaa, 0x13, 0x0063},				/* 00,13,63,aa */
+	{0xaa, 0x15, 0x0070},				/* 00,15,70,aa */
+	{0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION},	/* 01,01,b7,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, 0x70, ZC3XX_R18D_YTARGET},		/* 01,8d,70,cc */
+	{}
+};
+static const struct usb_action pas202b_InitialScale[] = {	/* 320x240 */
+	{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},		/* 00,00,01,cc */
+	{0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+	{0xa0, 0x0e, ZC3XX_R010_CMOSSENSORSELECT},	/* 00,10,0e,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},
+	{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, 0x08, ZC3XX_R08D_COMPABILITYMODE},	/* 00,8d,08,cc */
+	{0xa0, 0x08, ZC3XX_R098_WINYSTARTLOW},		/* 00,98,08,cc */
+	{0xa0, 0x02, ZC3XX_R09A_WINXSTARTLOW},		/* 00,9a,02,cc */
+	{0xa0, 0x08, ZC3XX_R11A_FIRSTYLOW},		/* 01,1a,08,cc */
+	{0xa0, 0x02, ZC3XX_R11C_FIRSTXLOW},		/* 01,1c,02,cc */
+	{0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH},		/* 00,9b,01,cc */
+	{0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW},
+	{0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH},		/* 00,9d,02,cc */
+	{0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW},		/* 00,9e,88,cc */
+	{0xaa, 0x02, 0x0002},				/* 00,02,02,aa */
+	{0xaa, 0x07, 0x0006},				/* 00,07,06,aa */
+	{0xaa, 0x08, 0x0002},				/* 00,08,02,aa */
+	{0xaa, 0x09, 0x0006},				/* 00,09,06,aa */
+	{0xaa, 0x0a, 0x0001},				/* 00,0a,01,aa */
+	{0xaa, 0x0b, 0x0001},				/* 00,0b,01,aa */
+	{0xaa, 0x0c, 0x0006},
+	{0xaa, 0x0d, 0x0000},				/* 00,0d,00,aa */
+	{0xaa, 0x10, 0x0000},				/* 00,10,00,aa */
+	{0xaa, 0x12, 0x0005},				/* 00,12,05,aa */
+	{0xaa, 0x13, 0x0063},				/* 00,13,63,aa */
+	{0xaa, 0x15, 0x0070},				/* 00,15,70,aa */
+	{0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},	/* 01,01,37,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, 0x70, ZC3XX_R18D_YTARGET},		/* 01,8d,70,cc */
+	{0xa0, 0xff, ZC3XX_R097_WINYSTARTHIGH},
+	{0xa0, 0xfe, ZC3XX_R098_WINYSTARTLOW},
+	{}
+};
+static const struct usb_action pas202b_50HZ[] = {
+	{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},		/* 00,19,00,cc */
+	{0xa0, 0x20, ZC3XX_R087_EXPTIMEMID},		/* 00,87,20,cc */
+	{0xa0, 0x21, ZC3XX_R088_EXPTIMELOW},		/* 00,88,21,cc */
+	{0xaa, 0x20, 0x0002},				/* 00,20,02,aa */
+	{0xaa, 0x21, 0x001b},
+	{0xaa, 0x03, 0x0044},				/* 00,03,44,aa */
+	{0xaa, 0x04, 0x0008},
+	{0xaa, 0x05, 0x001b},
+	{0xaa, 0x0e, 0x0001},				/* 00,0e,01,aa */
+	{0xaa, 0x0f, 0x0000},				/* 00,0f,00,aa */
+	{0xa0, 0x1c, ZC3XX_R1A9_DIGITALLIMITDIFF},
+	{0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},	/* 01,aa,24,cc */
+	{0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},	/* 01,90,00,cc */
+	{0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID},
+	{0xa0, 0x1b, ZC3XX_R192_EXPOSURELIMITLOW},
+	{0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},	/* 01,95,00,cc */
+	{0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},	/* 01,96,00,cc */
+	{0xa0, 0x4d, ZC3XX_R197_ANTIFLICKERLOW},	/* 01,97,4d,cc */
+	{0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+	{0xa0, 0x1b, ZC3XX_R18F_AEUNFREEZE},
+	{0xa0, 0x44, ZC3XX_R01D_HSYNC_0},		/* 00,1d,44,cc */
+	{0xa0, 0x6f, ZC3XX_R01E_HSYNC_1},		/* 00,1e,6f,cc */
+	{0xa0, 0xad, ZC3XX_R01F_HSYNC_2},		/* 00,1f,ad,cc */
+	{0xa0, 0xeb, ZC3XX_R020_HSYNC_3},		/* 00,20,eb,cc */
+	{0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID},		/* 00,87,0f,cc */
+	{0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW},		/* 00,88,0e,cc */
+	{}
+};
+static const struct usb_action pas202b_50HZScale[] = {
+	{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},		/* 00,19,00,cc */
+	{0xa0, 0x20, ZC3XX_R087_EXPTIMEMID},		/* 00,87,20,cc */
+	{0xa0, 0x21, ZC3XX_R088_EXPTIMELOW},		/* 00,88,21,cc */
+	{0xaa, 0x20, 0x0004},
+	{0xaa, 0x21, 0x003d},
+	{0xaa, 0x03, 0x0041},				/* 00,03,41,aa */
+	{0xaa, 0x04, 0x0010},
+	{0xaa, 0x05, 0x003d},
+	{0xaa, 0x0e, 0x0001},				/* 00,0e,01,aa */
+	{0xaa, 0x0f, 0x0000},				/* 00,0f,00,aa */
+	{0xa0, 0x1c, ZC3XX_R1A9_DIGITALLIMITDIFF},
+	{0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},	/* 01,aa,24,cc */
+	{0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},	/* 01,90,00,cc */
+	{0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID},
+	{0xa0, 0x3d, ZC3XX_R192_EXPOSURELIMITLOW},
+	{0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},	/* 01,95,00,cc */
+	{0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},	/* 01,96,00,cc */
+	{0xa0, 0x9b, ZC3XX_R197_ANTIFLICKERLOW},	/* 01,97,9b,cc */
+	{0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+	{0xa0, 0x1b, ZC3XX_R18F_AEUNFREEZE},
+	{0xa0, 0x41, ZC3XX_R01D_HSYNC_0},		/* 00,1d,41,cc */
+	{0xa0, 0x6f, ZC3XX_R01E_HSYNC_1},		/* 00,1e,6f,cc */
+	{0xa0, 0xad, ZC3XX_R01F_HSYNC_2},		/* 00,1f,ad,cc */
+	{0xa0, 0xff, ZC3XX_R020_HSYNC_3},		/* 00,20,ff,cc */
+	{0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID},		/* 00,87,0f,cc */
+	{0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW},		/* 00,88,0e,cc */
+	{}
+};
+static const struct usb_action pas202b_60HZ[] = {
+	{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},		/* 00,19,00,cc */
+	{0xa0, 0x20, ZC3XX_R087_EXPTIMEMID},		/* 00,87,20,cc */
+	{0xa0, 0x21, ZC3XX_R088_EXPTIMELOW},		/* 00,88,21,cc */
+	{0xaa, 0x20, 0x0002},				/* 00,20,02,aa */
+	{0xaa, 0x21, 0x0000},				/* 00,21,00,aa */
+	{0xaa, 0x03, 0x0045},				/* 00,03,45,aa */
+	{0xaa, 0x04, 0x0008},				/* 00,04,08,aa */
+	{0xaa, 0x05, 0x0000},				/* 00,05,00,aa */
+	{0xaa, 0x0e, 0x0001},				/* 00,0e,01,aa */
+	{0xaa, 0x0f, 0x0000},				/* 00,0f,00,aa */
+	{0xa0, 0x1c, ZC3XX_R1A9_DIGITALLIMITDIFF},
+	{0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},	/* 01,aa,24,cc */
+	{0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},	/* 01,90,00,cc */
+	{0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID},
+	{0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW},
+	{0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},	/* 01,95,00,cc */
+	{0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},	/* 01,96,00,cc */
+	{0xa0, 0x40, ZC3XX_R197_ANTIFLICKERLOW},	/* 01,97,40,cc */
+	{0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+	{0xa0, 0x1b, ZC3XX_R18F_AEUNFREEZE},
+	{0xa0, 0x45, ZC3XX_R01D_HSYNC_0},		/* 00,1d,45,cc */
+	{0xa0, 0x8e, ZC3XX_R01E_HSYNC_1},		/* 00,1e,8e,cc */
+	{0xa0, 0xc1, ZC3XX_R01F_HSYNC_2},		/* 00,1f,c1,cc */
+	{0xa0, 0xf5, ZC3XX_R020_HSYNC_3},		/* 00,20,f5,cc */
+	{0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID},		/* 00,87,0f,cc */
+	{0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW},		/* 00,88,0e,cc */
+	{}
+};
+static const struct usb_action pas202b_60HZScale[] = {
+	{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},		/* 00,19,00,cc */
+	{0xa0, 0x20, ZC3XX_R087_EXPTIMEMID},		/* 00,87,20,cc */
+	{0xa0, 0x21, ZC3XX_R088_EXPTIMELOW},		/* 00,88,21,cc */
+	{0xaa, 0x20, 0x0004},
+	{0xaa, 0x21, 0x0008},
+	{0xaa, 0x03, 0x0042},				/* 00,03,42,aa */
+	{0xaa, 0x04, 0x0010},
+	{0xaa, 0x05, 0x0008},
+	{0xaa, 0x0e, 0x0001},				/* 00,0e,01,aa */
+	{0xaa, 0x0f, 0x0000},				/* 00,0f,00,aa */
+	{0xa0, 0x1c, ZC3XX_R1A9_DIGITALLIMITDIFF},
+	{0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},	/* 01,aa,24,cc */
+	{0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},	/* 01,90,00,cc */
+	{0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID},
+	{0xa0, 0x08, ZC3XX_R192_EXPOSURELIMITLOW},
+	{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, 0x0e, ZC3XX_R18C_AEFREEZE},
+	{0xa0, 0x1b, ZC3XX_R18F_AEUNFREEZE},
+	{0xa0, 0x42, ZC3XX_R01D_HSYNC_0},		/* 00,1d,42,cc */
+	{0xa0, 0x6f, ZC3XX_R01E_HSYNC_1},		/* 00,1e,6f,cc */
+	{0xa0, 0xaf, ZC3XX_R01F_HSYNC_2},		/* 00,1f,af,cc */
+	{0xa0, 0xff, ZC3XX_R020_HSYNC_3},		/* 00,20,ff,cc */
+	{0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID},		/* 00,87,0f,cc */
+	{0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW},		/* 00,88,0e,cc */
+	{}
+};
+static const struct usb_action pas202b_NoFliker[] = {
+	{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},		/* 00,19,00,cc */
+	{0xa0, 0x20, ZC3XX_R087_EXPTIMEMID},		/* 00,87,20,cc */
+	{0xa0, 0x21, ZC3XX_R088_EXPTIMELOW},		/* 00,88,21,cc */
+	{0xaa, 0x20, 0x0002},				/* 00,20,02,aa */
+	{0xaa, 0x21, 0x0006},
+	{0xaa, 0x03, 0x0040},				/* 00,03,40,aa */
+	{0xaa, 0x04, 0x0008},				/* 00,04,08,aa */
+	{0xaa, 0x05, 0x0006},
+	{0xaa, 0x0e, 0x0001},				/* 00,0e,01,aa */
+	{0xaa, 0x0f, 0x0000},				/* 00,0f,00,aa */
+	{0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},	/* 01,90,00,cc */
+	{0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID},
+	{0xa0, 0x06, ZC3XX_R192_EXPOSURELIMITLOW},
+	{0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},	/* 01,95,00,cc */
+	{0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},	/* 01,96,00,cc */
+	{0xa0, 0x01, ZC3XX_R197_ANTIFLICKERLOW},
+	{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, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+	{0xa0, 0x40, ZC3XX_R01D_HSYNC_0},		/* 00,1d,40,cc */
+	{0xa0, 0x60, ZC3XX_R01E_HSYNC_1},		/* 00,1e,60,cc */
+	{0xa0, 0x90, ZC3XX_R01F_HSYNC_2},		/* 00,1f,90,cc */
+	{0xa0, 0xff, ZC3XX_R020_HSYNC_3},		/* 00,20,ff,cc */
+	{0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID},		/* 00,87,0f,cc */
+	{0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW},		/* 00,88,0e,cc */
+	{}
+};
+static const struct usb_action pas202b_NoFlikerScale[] = {
+	{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},		/* 00,19,00,cc */
+	{0xa0, 0x20, ZC3XX_R087_EXPTIMEMID},		/* 00,87,20,cc */
+	{0xa0, 0x21, ZC3XX_R088_EXPTIMELOW},		/* 00,88,21,cc */
+	{0xaa, 0x20, 0x0004},
+	{0xaa, 0x21, 0x000c},
+	{0xaa, 0x03, 0x0040},				/* 00,03,40,aa */
+	{0xaa, 0x04, 0x0010},
+	{0xaa, 0x05, 0x000c},
+	{0xaa, 0x0e, 0x0001},				/* 00,0e,01,aa */
+	{0xaa, 0x0f, 0x0000},				/* 00,0f,00,aa */
+	{0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},	/* 01,90,00,cc */
+	{0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID},
+	{0xa0, 0x0c, ZC3XX_R192_EXPOSURELIMITLOW},
+	{0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},	/* 01,95,00,cc */
+	{0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},	/* 01,96,00,cc */
+	{0xa0, 0x02, ZC3XX_R197_ANTIFLICKERLOW},	/* 01,97,02,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, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+	{0xa0, 0x40, ZC3XX_R01D_HSYNC_0},		/* 00,1d,40,cc */
+	{0xa0, 0x60, ZC3XX_R01E_HSYNC_1},		/* 00,1e,60,cc */
+	{0xa0, 0x90, ZC3XX_R01F_HSYNC_2},		/* 00,1f,90,cc */
+	{0xa0, 0xff, ZC3XX_R020_HSYNC_3},		/* 00,20,ff,cc */
+	{0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID},		/* 00,87,0f,cc */
+	{0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW},		/* 00,88,0e,cc */
+	{}
+};
+
+/* mt9v111 (mi0360soc) and pb0330 from vm30x.inf 0ac8:301b 07/02/13 */
+static const struct usb_action mt9v111_1_Initial[] = {	/* 640x480 */
+	{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},
+	{0xdd, 0x00, 0x0200},
+	{0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+	{0xaa, 0x01, 0x0001},
+	{0xaa, 0x06, 0x0000},
+	{0xaa, 0x08, 0x0483},
+	{0xaa, 0x01, 0x0004},
+	{0xaa, 0x08, 0x0006},
+	{0xaa, 0x02, 0x0011},
+	{0xaa, 0x03, 0x01e5},			/*jfm: was 01e7*/
+	{0xaa, 0x04, 0x0285},			/*jfm: was 0287*/
+	{0xaa, 0x07, 0x3002},
+	{0xaa, 0x20, 0x5100},
+	{0xaa, 0x35, 0x507f},
+	{0xaa, 0x30, 0x0005},
+	{0xaa, 0x31, 0x0000},
+	{0xaa, 0x58, 0x0078},
+	{0xaa, 0x62, 0x0411},
+	{0xaa, 0x2b, 0x007f},
+	{0xaa, 0x2c, 0x007f},			/*jfm: was 0030*/
+	{0xaa, 0x2d, 0x007f},			/*jfm: was 0030*/
+	{0xaa, 0x2e, 0x007f},			/*jfm: was 0030*/
+	{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},			/*jfm: was 00*/
+	{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},
+	{}
+};
+static const struct usb_action mt9v111_1_InitialScale[] = {	/* 320x240 */
+	{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},
+	{0xdd, 0x00, 0x0200},
+	{0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+	{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, 0x5100},
+	{0xaa, 0x35, 0x007f},			/*jfm: was 0050*/
+	{0xaa, 0x30, 0x0005},
+	{0xaa, 0x31, 0x0000},
+	{0xaa, 0x58, 0x0078},
+	{0xaa, 0x62, 0x0411},
+	{0xaa, 0x2b, 0x007f},			/*jfm: was 28*/
+	{0xaa, 0x2c, 0x007f},			/*jfm: was 30*/
+	{0xaa, 0x2d, 0x007f},			/*jfm: was 30*/
+	{0xaa, 0x2e, 0x007f},			/*jfm: was 28*/
+	{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},			/*jfm: was 00*/
+	{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},
+	{}
+};
+static const struct usb_action mt9v111_1_AE50HZ[] = {
+	{0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+	{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+	{0xbb, 0x00, 0x0562},
+	{0xbb, 0x01, 0x09aa},
+	{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},
+	{}
+};
+static const struct usb_action mt9v111_1_AE50HZScale[] = {
+	{0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+	{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+	{0xbb, 0x00, 0x0509},
+	{0xbb, 0x01, 0x0934},
+	{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},
+	{}
+};
+static const struct usb_action mt9v111_1_AE60HZ[] = {
+	{0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+	{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+	{0xaa, 0x05, 0x003d},
+	{0xaa, 0x09, 0x016e},
+	{0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+	{0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+	{0xa0, 0xdd, ZC3XX_R192_EXPOSURELIMITLOW},
+	{0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+	{0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+	{0xa0, 0x3d, ZC3XX_R197_ANTIFLICKERLOW},
+	{0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+	{0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
+	{0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+	{0xa0, 0x24, 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},
+	{}
+};
+static const struct usb_action mt9v111_1_AE60HZScale[] = {
+	{0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+	{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+	{0xbb, 0x00, 0x0509},
+	{0xbb, 0x01, 0x0983},
+	{0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+	{0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+	{0xa0, 0x8f, ZC3XX_R192_EXPOSURELIMITLOW},
+	{0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+	{0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+	{0xa0, 0x81, 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},
+	{}
+};
+static const struct usb_action mt9v111_1_AENoFliker[] = {
+	{0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+	{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+	{0xbb, 0x00, 0x0509},
+	{0xbb, 0x01, 0x0960},
+	{0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+	{0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+	{0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW},
+	{0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+	{0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+	{0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW},
+	{0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+	{0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
+	{0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
+	{0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
+	{0xa0, 0x09, ZC3XX_R01D_HSYNC_0},
+	{0xa0, 0x40, ZC3XX_R01E_HSYNC_1},
+	{0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
+	{0xa0, 0xe0, ZC3XX_R020_HSYNC_3},
+	{0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+	{0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+	{}
+};
+static const struct usb_action mt9v111_1_AENoFlikerScale[] = {
+	{0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+	{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+	{0xbb, 0x00, 0x0534},
+	{0xbb, 0x02, 0x0960},
+	{0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+	{0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+	{0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW},
+	{0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+	{0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+	{0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW},
+	{0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+	{0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
+	{0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
+	{0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
+	{0xa0, 0x34, ZC3XX_R01D_HSYNC_0},
+	{0xa0, 0x60, ZC3XX_R01E_HSYNC_1},
+	{0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
+	{0xa0, 0xe0, ZC3XX_R020_HSYNC_3},
+	{0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+	{0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+	{}
+};
+/* from usbvm303.inf 0ac8:303b 07/03/25 (3 - tas5130c) */
+static const struct usb_action mt9v111_3_Initial[] = {
+	{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},
+	{0xdd, 0x00, 0x0200},
+	{0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+	{0xaa, 0x01, 0x0001},		/* select IFP/SOC registers */
+	{0xaa, 0x06, 0x0000},		/* operating mode control */
+	{0xaa, 0x08, 0x0483},		/* output format control */
+					/* H red first, V red or blue first,
+					 * raw Bayer, auto flicker */
+	{0xaa, 0x01, 0x0004},		/* select sensor core registers */
+	{0xaa, 0x08, 0x0006},		/* row start */
+	{0xaa, 0x02, 0x0011},		/* column start */
+	{0xaa, 0x03, 0x01e5},		/* window height - 1 */
+	{0xaa, 0x04, 0x0285},		/* window width - 1 */
+	{0xaa, 0x07, 0x3002},		/* output control */
+	{0xaa, 0x20, 0x1100},		/* read mode: bits 8 & 12 (?) */
+	{0xaa, 0x35, 0x007f},		/* global gain */
+	{0xaa, 0x30, 0x0005},
+	{0xaa, 0x31, 0x0000},
+	{0xaa, 0x58, 0x0078},
+	{0xaa, 0x62, 0x0411},
+	{0xaa, 0x2b, 0x007f},		/* green1 gain */
+	{0xaa, 0x2c, 0x007f},		/* blue gain */
+	{0xaa, 0x2d, 0x007f},		/* red gain */
+	{0xaa, 0x2e, 0x007f},		/* green2 gain */
+	{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, 0x80, ZC3XX_R18D_YTARGET},
+	{0xa0, 0x61, ZC3XX_R116_RGAIN},
+	{0xa0, 0x65, ZC3XX_R118_BGAIN},
+	{}
+};
+static const struct usb_action mt9v111_3_InitialScale[] = {
+	{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},
+	{0xdd, 0x00, 0x0200},
+	{0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+	{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, 0x007f},
+	{0xaa, 0x30, 0x0005},
+	{0xaa, 0x31, 0x0000},
+	{0xaa, 0x58, 0x0078},
+	{0xaa, 0x62, 0x0411},
+	{0xaa, 0x2b, 0x007f},
+	{0xaa, 0x2c, 0x007f},
+	{0xaa, 0x2d, 0x007f},
+	{0xaa, 0x2e, 0x007f},
+	{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, 0x80, ZC3XX_R18D_YTARGET},
+	{0xa0, 0x61, ZC3XX_R116_RGAIN},
+	{0xa0, 0x65, ZC3XX_R118_BGAIN},
+	{}
+};
+static const struct usb_action mt9v111_3_AE50HZ[] = {
+	{0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+	{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+	{0xaa, 0x05, 0x0009},		/* horizontal blanking */
+	{0xaa, 0x09, 0x01ce},		/* shutter width */
+	{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, 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},
+	{}
+};
+static const struct usb_action mt9v111_3_AE50HZScale[] = {
+	{0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+	{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+	{0xaa, 0x05, 0x0009},
+	{0xaa, 0x09, 0x01ce},
+	{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, 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},
+	{}
+};
+static const struct usb_action mt9v111_3_AE60HZ[] = {
+	{0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+	{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+	{0xaa, 0x05, 0x0009},
+	{0xaa, 0x09, 0x0083},
+	{0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+	{0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+	{0xa0, 0x8f, ZC3XX_R192_EXPOSURELIMITLOW},
+	{0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+	{0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+	{0xa0, 0x81, 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},
+	{}
+};
+static const struct usb_action mt9v111_3_AE60HZScale[] = {
+	{0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+	{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+	{0xaa, 0x05, 0x0009},
+	{0xaa, 0x09, 0x0083},
+	{0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+	{0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+	{0xa0, 0x8f, ZC3XX_R192_EXPOSURELIMITLOW},
+	{0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+	{0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+	{0xa0, 0x81, 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},
+	{}
+};
+static const struct usb_action mt9v111_3_AENoFliker[] = {
+	{0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+	{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+	{0xaa, 0x05, 0x0034},
+	{0xaa, 0x09, 0x0260},
+	{0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+	{0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+	{0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW},
+	{0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+	{0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+	{0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW},
+	{0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+	{0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
+	{0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
+	{0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
+	{0xa0, 0x34, ZC3XX_R01D_HSYNC_0},
+	{0xa0, 0x60, ZC3XX_R01E_HSYNC_1},
+	{0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
+	{0xa0, 0xe0, ZC3XX_R020_HSYNC_3},
+	{0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+	{0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+	{}
+};
+static const struct usb_action mt9v111_3_AENoFlikerScale[] = {
+	{0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+	{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+	{0xaa, 0x05, 0x0034},
+	{0xaa, 0x09, 0x0260},
+	{0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+	{0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+	{0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW},
+	{0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+	{0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+	{0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW},
+	{0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+	{0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
+	{0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
+	{0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
+	{0xa0, 0x34, ZC3XX_R01D_HSYNC_0},
+	{0xa0, 0x60, ZC3XX_R01E_HSYNC_1},
+	{0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
+	{0xa0, 0xe0, ZC3XX_R020_HSYNC_3},
+	{0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+	{0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+	{}
+};
+
+static const struct usb_action pb0330_Initial[] = {	/* 640x480 */
+	{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+	{0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},	/* 00 */
+	{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, 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},
+	{0xdd, 0x00, 0x0200},
+	{0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+	{0xaa, 0x01, 0x0006},
+	{0xaa, 0x02, 0x0011},
+	{0xaa, 0x03, 0x01e5},			/*jfm: was 1e7*/
+	{0xaa, 0x04, 0x0285},			/*jfm: was 0287*/
+	{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, 0x09, 0x01ad},			/*jfm: was 00 */
+	{0xa0, 0x15, 0x01ae},
+	{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},	/*jfm: was 6c*/
+	{}
+};
+static const struct usb_action pb0330_InitialScale[] = {	/* 320x240 */
+	{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},
+	{0xdd, 0x00, 0x0200},
+	{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, 0x09, 0x01ad},
+	{0xa0, 0x15, 0x01ae},
+	{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},	/*jfm: was 6c*/
+	{}
+};
+static const struct usb_action pb0330_50HZ[] = {
+	{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+	{0xbb, 0x00, 0x055c},
+	{0xbb, 0x01, 0x09aa},
+	{0xbb, 0x00, 0x1001},
+	{0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+	{0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+	{0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+	{0xa0, 0xc4, ZC3XX_R192_EXPOSURELIMITLOW},
+	{0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+	{0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+	{0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW},
+	{0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+	{0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE},
+	{0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+	{0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+	{0xa0, 0x5c, ZC3XX_R01D_HSYNC_0},
+	{0xa0, 0x90, ZC3XX_R01E_HSYNC_1},
+	{0xa0, 0xc8, ZC3XX_R01F_HSYNC_2},
+	{0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+	{}
+};
+static const struct usb_action pb0330_50HZScale[] = {
+	{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+	{0xbb, 0x00, 0x0566},
+	{0xbb, 0x02, 0x09b2},
+	{0xbb, 0x00, 0x1002},
+	{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, 0x0e, ZC3XX_R18C_AEFREEZE},
+	{0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE},
+	{0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+	{0xa0, 0x66, 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},
+	{}
+};
+static const struct usb_action pb0330_60HZ[] = {
+	{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+	{0xbb, 0x00, 0x0535},
+	{0xbb, 0x01, 0x0974},
+	{0xbb, 0x00, 0x1001},
+	{0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+	{0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+	{0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+	{0xa0, 0xfe, ZC3XX_R192_EXPOSURELIMITLOW},
+	{0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+	{0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+	{0xa0, 0x3e, ZC3XX_R197_ANTIFLICKERLOW},
+	{0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+	{0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE},
+	{0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+	{0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+	{0xa0, 0x35, ZC3XX_R01D_HSYNC_0},
+	{0xa0, 0x50, ZC3XX_R01E_HSYNC_1},
+	{0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
+	{0xa0, 0xd0, ZC3XX_R020_HSYNC_3},
+	{}
+};
+static const struct usb_action pb0330_60HZScale[] = {
+	{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+	{0xbb, 0x00, 0x0535},
+	{0xbb, 0x02, 0x096c},
+	{0xbb, 0x00, 0x1002},
+	{0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+	{0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+	{0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+	{0xa0, 0xc0, ZC3XX_R192_EXPOSURELIMITLOW},
+	{0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+	{0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+	{0xa0, 0x7c, ZC3XX_R197_ANTIFLICKERLOW},
+	{0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+	{0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE},
+	{0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+	{0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+	{0xa0, 0x35, ZC3XX_R01D_HSYNC_0},
+	{0xa0, 0x50, ZC3XX_R01E_HSYNC_1},
+	{0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
+	{0xa0, 0xd0, ZC3XX_R020_HSYNC_3},
+	{}
+};
+static const struct usb_action pb0330_NoFliker[] = {
+	{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+	{0xbb, 0x00, 0x0509},
+	{0xbb, 0x02, 0x0940},
+	{0xbb, 0x00, 0x1002},
+	{0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+	{0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+	{0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+	{0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW},
+	{0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+	{0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+	{0xa0, 0x01, ZC3XX_R197_ANTIFLICKERLOW},
+	{0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+	{0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+	{0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
+	{0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
+	{0xa0, 0x09, ZC3XX_R01D_HSYNC_0},
+	{0xa0, 0x40, ZC3XX_R01E_HSYNC_1},
+	{0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
+	{0xa0, 0xe0, ZC3XX_R020_HSYNC_3},
+	{}
+};
+static const struct usb_action pb0330_NoFlikerScale[] = {
+	{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+	{0xbb, 0x00, 0x0535},
+	{0xbb, 0x01, 0x0980},
+	{0xbb, 0x00, 0x1001},
+	{0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+	{0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+	{0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+	{0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW},
+	{0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+	{0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+	{0xa0, 0x01, ZC3XX_R197_ANTIFLICKERLOW},
+	{0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+	{0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+	{0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
+	{0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
+	{0xa0, 0x35, ZC3XX_R01D_HSYNC_0},
+	{0xa0, 0x60, ZC3XX_R01E_HSYNC_1},
+	{0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
+	{0xa0, 0xe0, ZC3XX_R020_HSYNC_3},
+	{}
+};
+
+/* from oem9.inf */
+static const struct usb_action po2030_Initial[] = {	/* 640x480 */
+	{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 */
+static const struct usb_action po2030_InitialScale[] = {	/* 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, 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 */
+	{}
+};
+
+static const struct usb_action tas5130c_InitialScale[] = {	/* 320x240 */
+	{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, 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, 0x70, ZC3XX_R18D_YTARGET},
+	{0xa0, 0x50, 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},
+	{0xa0, 0x07, ZC3XX_R0A5_EXPOSUREGAIN},
+	{0xa0, 0x02, ZC3XX_R0A6_EXPOSUREBLACKLVL},
+	{}
+};
+static const struct usb_action tas5130c_Initial[] = {	/* 640x480 */
+	{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+	{0xa0, 0x40, ZC3XX_R002_CLOCKSELECT},
+	{0xa0, 0x00, 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, 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, 0x70, ZC3XX_R18D_YTARGET},
+	{0xa0, 0x50, 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},
+	{0xa0, 0x07, ZC3XX_R0A5_EXPOSUREGAIN},
+	{0xa0, 0x02, ZC3XX_R0A6_EXPOSUREBLACKLVL},
+	{}
+};
+static const struct usb_action tas5130c_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, 0x04, ZC3XX_R191_EXPOSURELIMITMID},
+	{0xa0, 0xfe, ZC3XX_R192_EXPOSURELIMITLOW},
+	{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, 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}, /* 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 */
+	{0xa0, 0x4c, ZC3XX_R0A0_MAXXLOW},
+	{0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
+	{}
+};
+static const struct usb_action tas5130c_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, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+	{0xa0, 0xd0, ZC3XX_R192_EXPOSURELIMITLOW},
+	{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, 0x0c, ZC3XX_R18C_AEFREEZE},
+	{0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
+	{0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF},
+	{0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+	{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 */
+	{0xa0, 0xc0, ZC3XX_R0A0_MAXXLOW},
+	{0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
+	{}
+};
+static const struct usb_action tas5130c_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, 0x05, ZC3XX_R191_EXPOSURELIMITMID},
+	{0xa0, 0x54, ZC3XX_R192_EXPOSURELIMITLOW},
+	{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, 0x0c, ZC3XX_R18C_AEFREEZE},
+	{0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
+	{0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF},
+	{0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+	{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 */
+	{0xa0, 0x28, ZC3XX_R0A0_MAXXLOW},
+	{0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
+	{}
+};
+static const struct usb_action tas5130c_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, 0x09, ZC3XX_R191_EXPOSURELIMITMID},
+	{0xa0, 0x47, ZC3XX_R192_EXPOSURELIMITLOW},
+	{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, 0x0c, ZC3XX_R18C_AEFREEZE},
+	{0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
+	{0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF},
+	{0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+	{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 */
+	{0xa0, 0x20, ZC3XX_R0A0_MAXXLOW},
+	{0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
+	{}
+};
+static const struct usb_action tas5130c_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, 0x05, ZC3XX_R191_EXPOSURELIMITMID},
+	{0xa0, 0xa0, ZC3XX_R192_EXPOSURELIMITLOW},
+	{0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+	{0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+	{0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW},
+	{0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
+	{0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
+	{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 */
+	{0xa0, 0xf0, ZC3XX_R0A0_MAXXLOW},
+	{0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
+	{}
+};
+
+static const struct usb_action tas5130c_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, 0x0a, ZC3XX_R191_EXPOSURELIMITMID},
+	{0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW},
+	{0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+	{0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+	{0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW},
+	{0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
+	{0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
+	{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 */
+	{0xa0, 0xf0, ZC3XX_R0A0_MAXXLOW},
+	{0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
+	{}
+};
+
+/* from usbvm305.inf 0ac8:305b 07/06/15 (3 - tas5130c) */
+static const struct usb_action gc0303_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, 0x00, ZC3XX_R002_CLOCKSELECT},
+	{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, 0x01, 0x0000},
+	{0xaa, 0x1a, 0x0000},		/* 00,1a,00,aa, */
+	{0xaa, 0x1c, 0x0017},		/* 00,1c,17,aa, */
+	{0xaa, 0x1b, 0x0000},
+	{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, 0x0002},
+	{0xaa, 0x0b, 0x0000},
+	{0xaa, 0x0c, 0x0002},
+	{0xaa, 0x0d, 0x0000},
+	{0xaa, 0x0e, 0x0002},
+	{0xaa, 0x0f, 0x0000},
+	{0xaa, 0x10, 0x0002},
+	{0xaa, 0x11, 0x0000},
+	{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, 0x58, ZC3XX_R1A8_DIGITALGAIN},
+	{0xa0, 0x61, ZC3XX_R116_RGAIN},			/* 01,16,61,cc, */
+	{0xa0, 0x65, ZC3XX_R118_BGAIN},			/* 01,18,65,cc */
+	{0xaa, 0x1b, 0x0000},
+	{}
+};
+
+static const struct usb_action gc0303_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, 0x10, ZC3XX_R002_CLOCKSELECT},
+	{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, 0x01, 0x0000},
+	{0xaa, 0x1a, 0x0000},		/* 00,1a,00,aa, */
+	{0xaa, 0x1c, 0x0017},		/* 00,1c,17,aa, */
+	{0xaa, 0x1b, 0x0000},
+	{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, 0x0001},
+	{0xaa, 0x0b, 0x0000},
+	{0xaa, 0x0c, 0x0001},
+	{0xaa, 0x0d, 0x0000},
+	{0xaa, 0x0e, 0x0001},
+	{0xaa, 0x0f, 0x0000},
+	{0xaa, 0x10, 0x0001},
+	{0xaa, 0x11, 0x0000},
+	{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, */
+	{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, 0x58, ZC3XX_R1A8_DIGITALGAIN},
+	{0xa0, 0x61, ZC3XX_R116_RGAIN},		/* 01,16,61,cc, */
+	{0xa0, 0x65, ZC3XX_R118_BGAIN},		/* 01,18,65,cc */
+	{0xaa, 0x1b, 0x0000},
+	{}
+};
+static const struct usb_action gc0303_50HZ[] = {
+	{0xaa, 0x82, 0x0000},		/* 00,82,00,aa */
+	{0xaa, 0x83, 0x0001},		/* 00,83,01,aa */
+	{0xaa, 0x84, 0x0063},
+	{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, 0x47, 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, 0x48, ZC3XX_R1AA_DIGITALGAINSTEP},
+	{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, 0x7f, ZC3XX_R18D_YTARGET},
+	{}
+};
+
+static const struct usb_action gc0303_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, 0x48, 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, 0x7f, ZC3XX_R18D_YTARGET},
+	{}
+};
+
+static const struct usb_action gc0303_60HZ[] = {
+	{0xaa, 0x82, 0x0000},		/* 00,82,00,aa */
+	{0xaa, 0x83, 0x0000},
+	{0xaa, 0x84, 0x003b},
+	{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, 0x80, ZC3XX_R18D_YTARGET},
+	{}
+};
+
+static const struct usb_action gc0303_60HZScale[] = {
+	{0xaa, 0x82, 0x0000},		/* 00,82,00,aa */
+	{0xaa, 0x83, 0x0000},
+	{0xaa, 0x84, 0x0076},
+	{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, 0x80, ZC3XX_R18D_YTARGET},
+	{}
+};
+
+static const struct usb_action gc0303_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, 0x00, ZC3XX_R191_EXPOSURELIMITMID},
+	{0xa0, 0x48, ZC3XX_R192_EXPOSURELIMITLOW},
+	{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 const struct usb_action gc0303_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, 0x00, ZC3XX_R191_EXPOSURELIMITMID},
+	{0xa0, 0x48, ZC3XX_R192_EXPOSURELIMITLOW},
+	{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 u8 reg_r(struct gspca_dev *gspca_dev,
+		u16 index)
+{
+	int ret;
+
+	if (gspca_dev->usb_err < 0)
+		return 0;
+	ret = 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);
+	if (ret < 0) {
+		pr_err("reg_r err %d\n", ret);
+		gspca_dev->usb_err = ret;
+		return 0;
+	}
+	return gspca_dev->usb_buf[0];
+}
+
+static void reg_w(struct gspca_dev *gspca_dev,
+			u8 value,
+			u16 index)
+{
+	int ret;
+
+	if (gspca_dev->usb_err < 0)
+		return;
+	ret = usb_control_msg(gspca_dev->dev,
+			usb_sndctrlpipe(gspca_dev->dev, 0),
+			0xa0,
+			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			value, index, NULL, 0,
+			500);
+	if (ret < 0) {
+		pr_err("reg_w_i err %d\n", ret);
+		gspca_dev->usb_err = ret;
+	}
+}
+
+static u16 i2c_read(struct gspca_dev *gspca_dev,
+			u8 reg)
+{
+	u8 retbyte;
+	u16 retval;
+
+	if (gspca_dev->usb_err < 0)
+		return 0;
+	reg_w(gspca_dev, reg, 0x0092);
+	reg_w(gspca_dev, 0x02, 0x0090);			/* <- read command */
+	msleep(20);
+	retbyte = reg_r(gspca_dev, 0x0091);		/* read status */
+	if (retbyte != 0x00)
+		pr_err("i2c_r status error %02x\n", retbyte);
+	retval = reg_r(gspca_dev, 0x0095);		/* read Lowbyte */
+	retval |= reg_r(gspca_dev, 0x0096) << 8;	/* read Hightbyte */
+	return retval;
+}
+
+static u8 i2c_write(struct gspca_dev *gspca_dev,
+			u8 reg,
+			u8 valL,
+			u8 valH)
+{
+	u8 retbyte;
+
+	if (gspca_dev->usb_err < 0)
+		return 0;
+	reg_w(gspca_dev, reg, 0x92);
+	reg_w(gspca_dev, valL, 0x93);
+	reg_w(gspca_dev, valH, 0x94);
+	reg_w(gspca_dev, 0x01, 0x90);		/* <- write command */
+	msleep(1);
+	retbyte = reg_r(gspca_dev, 0x0091);		/* read status */
+	if (retbyte != 0x00)
+		pr_err("i2c_w status error %02x\n", 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, 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;
+		case 0xbb:
+			i2c_write(gspca_dev,
+				  action->idx >> 8,		/* reg */
+				  action->idx & 0xff,		/* valL */
+				  action->val);			/* valH */
+			break;
+		default:
+/*		case 0xdd:	 * delay */
+			msleep(action->idx);
+			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 adcm2700_matrix[9] =
+/*		{0x66, 0xed, 0xed, 0xed, 0x66, 0xed, 0xed, 0xed, 0x66}; */
+/*ms-win*/
+		{0x74, 0xed, 0xed, 0xed, 0x74, 0xed, 0xed, 0xed, 0x74};
+	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 pas202b_matrix[9] =
+		{0x4c, 0xf5, 0xff, 0xf9, 0x51, 0xf5, 0xfb, 0xed, 0x5f};
+	static const u8 po2030_matrix[9] =
+		{0x60, 0xf0, 0xf0, 0xf0, 0x60, 0xf0, 0xf0, 0xf0, 0x60};
+	static const u8 tas5130c_matrix[9] =
+		{0x68, 0xec, 0xec, 0xec, 0x68, 0xec, 0xec, 0xec, 0x68};
+	static const u8 gc0303_matrix[9] =
+		{0x6c, 0xea, 0xea, 0xea, 0x6c, 0xea, 0xea, 0xea, 0x6c};
+	static const u8 *matrix_tb[SENSOR_MAX] = {
+		[SENSOR_ADCM2700] =	adcm2700_matrix,
+		[SENSOR_CS2102] =	ov7620_matrix,
+		[SENSOR_CS2102K] =	NULL,
+		[SENSOR_GC0303] =	gc0303_matrix,
+		[SENSOR_GC0305] =	gc0305_matrix,
+		[SENSOR_HDCS2020] =	NULL,
+		[SENSOR_HV7131B] =	NULL,
+		[SENSOR_HV7131R] =	po2030_matrix,
+		[SENSOR_ICM105A] =	po2030_matrix,
+		[SENSOR_MC501CB] =	NULL,
+		[SENSOR_MT9V111_1] =	gc0305_matrix,
+		[SENSOR_MT9V111_3] =	gc0305_matrix,
+		[SENSOR_OV7620] =	ov7620_matrix,
+		[SENSOR_OV7630C] =	NULL,
+		[SENSOR_PAS106] =	NULL,
+		[SENSOR_PAS202B] =	pas202b_matrix,
+		[SENSOR_PB0330] =	gc0305_matrix,
+		[SENSOR_PO2030] =	po2030_matrix,
+		[SENSOR_TAS5130C] =	tas5130c_matrix,
+	};
+
+	matrix = matrix_tb[sd->sensor];
+	if (matrix == NULL)
+		return;		/* matrix already loaded */
+	for (i = 0; i < ARRAY_SIZE(ov7620_matrix); i++)
+		reg_w(gspca_dev, matrix[i], 0x010a + i);
+}
+
+static void setsharpness(struct gspca_dev *gspca_dev, s32 val)
+{
+	static const u8 sharpness_tb[][2] = {
+		{0x02, 0x03},
+		{0x04, 0x07},
+		{0x08, 0x0f},
+		{0x10, 0x1e}
+	};
+
+	reg_w(gspca_dev, sharpness_tb[val][0], 0x01c6);
+	reg_r(gspca_dev, 0x01c8);
+	reg_r(gspca_dev, 0x01c9);
+	reg_r(gspca_dev, 0x01ca);
+	reg_w(gspca_dev, sharpness_tb[val][1], 0x01cb);
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev,
+		s32 gamma, s32 brightness, s32 contrast)
+{
+	const u8 *Tgamma;
+	int g, i, adj, gp1, gp2;
+	u8 gr[16];
+	static const u8 delta_b[16] =		/* delta for brightness */
+		{0x50, 0x38, 0x2d, 0x28, 0x24, 0x21, 0x1e, 0x1d,
+		 0x1d, 0x1b, 0x1b, 0x1b, 0x19, 0x18, 0x18, 0x18};
+	static const u8 delta_c[16] =		/* delta for contrast */
+		{0x2c, 0x1a, 0x12, 0x0c, 0x0a, 0x06, 0x06, 0x06,
+		 0x04, 0x06, 0x04, 0x04, 0x03, 0x03, 0x02, 0x02};
+	static const u8 gamma_tb[6][16] = {
+		{0x00, 0x00, 0x03, 0x0d, 0x1b, 0x2e, 0x45, 0x5f,
+		 0x79, 0x93, 0xab, 0xc1, 0xd4, 0xe5, 0xf3, 0xff},
+		{0x01, 0x0c, 0x1f, 0x3a, 0x53, 0x6d, 0x85, 0x9c,
+		 0xb0, 0xc2, 0xd1, 0xde, 0xe9, 0xf2, 0xf9, 0xff},
+		{0x04, 0x16, 0x30, 0x4e, 0x68, 0x81, 0x98, 0xac,
+		 0xbe, 0xcd, 0xda, 0xe4, 0xed, 0xf5, 0xfb, 0xff},
+		{0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8,
+		 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff},
+		{0x20, 0x4b, 0x6e, 0x8d, 0xa3, 0xb5, 0xc5, 0xd2,
+		 0xdc, 0xe5, 0xec, 0xf2, 0xf6, 0xfa, 0xfd, 0xff},
+		{0x24, 0x44, 0x64, 0x84, 0x9d, 0xb2, 0xc4, 0xd3,
+		 0xe0, 0xeb, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff},
+	};
+
+	Tgamma = gamma_tb[gamma - 1];
+
+	contrast -= 128; /* -128 / 127 */
+	brightness -= 128; /* -128 / 92 */
+	adj = 0;
+	gp1 = gp2 = 0;
+	for (i = 0; i < 16; i++) {
+		g = Tgamma[i] + delta_b[i] * brightness / 256
+				- delta_c[i] * contrast / 256 - adj / 2;
+		if (g > 0xff)
+			g = 0xff;
+		else if (g < 0)
+			g = 0;
+		reg_w(gspca_dev, g, 0x0120 + i);	/* gamma */
+		if (contrast > 0)
+			adj--;
+		else if (contrast < 0)
+			adj++;
+		if (i > 1)
+			gr[i - 1] = (g - gp2) / 2;
+		else if (i != 0)
+			gr[0] = gp1 == 0 ? 0 : (g - gp1);
+		gp2 = gp1;
+		gp1 = g;
+	}
+	gr[15] = (0xff - gp2) / 2;
+	for (i = 0; i < 16; i++)
+		reg_w(gspca_dev, gr[i], 0x0130 + i);	/* gradient */
+}
+
+static s32 getexposure(struct gspca_dev *gspca_dev)
+{
+	return (i2c_read(gspca_dev, 0x25) << 9)
+		| (i2c_read(gspca_dev, 0x26) << 1)
+		| (i2c_read(gspca_dev, 0x27) >> 7);
+}
+
+static void setexposure(struct gspca_dev *gspca_dev, s32 val)
+{
+	i2c_write(gspca_dev, 0x25, val >> 9, 0x00);
+	i2c_write(gspca_dev, 0x26, val >> 1, 0x00);
+	i2c_write(gspca_dev, 0x27, val << 7, 0x00);
+}
+
+static void setquality(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	jpeg_set_qual(sd->jpeg_hdr, jpeg_qual[sd->reg08 >> 1]);
+	reg_w(gspca_dev, sd->reg08, ZC3XX_R008_CLOCKSETTING);
+}
+
+/* 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)
+ */
+static void setlightfreq(struct gspca_dev *gspca_dev, s32 val)
+{
+	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_ADCM2700] =
+		{adcm2700_NoFliker, adcm2700_NoFliker,
+		 adcm2700_50HZ, adcm2700_50HZ,
+		 adcm2700_60HZ, adcm2700_60HZ},
+	[SENSOR_CS2102] =
+		{cs2102_NoFliker, cs2102_NoFlikerScale,
+		 cs2102_50HZ, cs2102_50HZScale,
+		 cs2102_60HZ, cs2102_60HZScale},
+	[SENSOR_CS2102K] =
+		{cs2102_NoFliker, cs2102_NoFlikerScale,
+		 NULL, NULL, /* currently disabled */
+		 NULL, NULL},
+	[SENSOR_GC0303] =
+		{gc0303_NoFliker, gc0303_NoFlikerScale,
+		 gc0303_50HZ, gc0303_50HZScale,
+		 gc0303_60HZ, gc0303_60HZScale},
+	[SENSOR_GC0305] =
+		{gc0305_NoFliker, gc0305_NoFliker,
+		 gc0305_50HZ, gc0305_50HZ,
+		 gc0305_60HZ, gc0305_60HZ},
+	[SENSOR_HDCS2020] =
+		{hdcs2020_NoFliker, hdcs2020_NoFliker,
+		 hdcs2020_50HZ, hdcs2020_50HZ,
+		 hdcs2020_60HZ, hdcs2020_60HZ},
+	[SENSOR_HV7131B] =
+		{hv7131b_NoFliker, hv7131b_NoFlikerScale,
+		 hv7131b_50HZ, hv7131b_50HZScale,
+		 hv7131b_60HZ, hv7131b_60HZScale},
+	[SENSOR_HV7131R] =
+		{hv7131r_NoFliker, hv7131r_NoFlikerScale,
+		 hv7131r_50HZ, hv7131r_50HZScale,
+		 hv7131r_60HZ, hv7131r_60HZScale},
+	[SENSOR_ICM105A] =
+		{icm105a_NoFliker, icm105a_NoFlikerScale,
+		 icm105a_50HZ, icm105a_50HZScale,
+		 icm105a_60HZ, icm105a_60HZScale},
+	[SENSOR_MC501CB] =
+		{mc501cb_NoFliker, mc501cb_NoFlikerScale,
+		 mc501cb_50HZ, mc501cb_50HZScale,
+		 mc501cb_60HZ, mc501cb_60HZScale},
+	[SENSOR_MT9V111_1] =
+		{mt9v111_1_AENoFliker, mt9v111_1_AENoFlikerScale,
+		 mt9v111_1_AE50HZ, mt9v111_1_AE50HZScale,
+		 mt9v111_1_AE60HZ, mt9v111_1_AE60HZScale},
+	[SENSOR_MT9V111_3] =
+		{mt9v111_3_AENoFliker, mt9v111_3_AENoFlikerScale,
+		 mt9v111_3_AE50HZ, mt9v111_3_AE50HZScale,
+		 mt9v111_3_AE60HZ, mt9v111_3_AE60HZScale},
+	[SENSOR_OV7620] =
+		{ov7620_NoFliker, ov7620_NoFliker,
+		 ov7620_50HZ, ov7620_50HZ,
+		 ov7620_60HZ, ov7620_60HZ},
+	[SENSOR_OV7630C] =
+		{NULL, NULL,
+		 NULL, NULL,
+		 NULL, NULL},
+	[SENSOR_PAS106] =
+		{pas106b_NoFliker, pas106b_NoFliker,
+		 pas106b_50HZ, pas106b_50HZ,
+		 pas106b_60HZ, pas106b_60HZ},
+	[SENSOR_PAS202B] =
+		{pas202b_NoFliker, pas202b_NoFlikerScale,
+		 pas202b_50HZ, pas202b_50HZScale,
+		 pas202b_60HZ, pas202b_60HZScale},
+	[SENSOR_PB0330] =
+		{pb0330_NoFliker, pb0330_NoFlikerScale,
+		 pb0330_50HZ, pb0330_50HZScale,
+		 pb0330_60HZ, pb0330_60HZScale},
+	[SENSOR_PO2030] =
+		{po2030_NoFliker, po2030_NoFliker,
+		 po2030_50HZ, po2030_50HZ,
+		 po2030_60HZ, po2030_60HZ},
+	[SENSOR_TAS5130C] =
+		{tas5130c_NoFliker, tas5130c_NoFlikerScale,
+		 tas5130c_50HZ, tas5130c_50HZScale,
+		 tas5130c_60HZ, tas5130c_60HZScale},
+	};
+
+	i = val * 2;
+	mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
+	if (mode)
+		i++;			/* 320x240 */
+	zc3_freq = freq_tb[sd->sensor][i];
+	if (zc3_freq == NULL)
+		return;
+	usb_exchange(gspca_dev, zc3_freq);
+	switch (sd->sensor) {
+	case SENSOR_GC0305:
+		if (mode		/* if 320x240 */
+		    && val == 1)	/* and 50Hz */
+			reg_w(gspca_dev, 0x85, 0x018d);
+					/* win: 0x80, 0x018d */
+		break;
+	case SENSOR_OV7620:
+		if (!mode) {		/* if 640x480 */
+			if (val != 0)	/* and filter */
+				reg_w(gspca_dev, 0x40, 0x0002);
+			else
+				reg_w(gspca_dev, 0x44, 0x0002);
+		}
+		break;
+	case SENSOR_PAS202B:
+		reg_w(gspca_dev, 0x00, 0x01a7);
+		break;
+	}
+}
+
+static void setautogain(struct gspca_dev *gspca_dev, s32 val)
+{
+	reg_w(gspca_dev, val ? 0x42 : 0x02, 0x0180);
+}
+
+/*
+ * Update the transfer parameters.
+ * This function is executed from a work queue.
+ */
+static void transfer_update(struct work_struct *work)
+{
+	struct sd *sd = container_of(work, struct sd, work);
+	struct gspca_dev *gspca_dev = &sd->gspca_dev;
+	int change, good;
+	u8 reg07, reg11;
+
+	/* reg07 gets set to 0 by sd_start before starting us */
+	reg07 = 0;
+
+	good = 0;
+	while (1) {
+		msleep(100);
+
+		/* To protect gspca_dev->usb_buf and gspca_dev->usb_err */
+		mutex_lock(&gspca_dev->usb_lock);
+#ifdef CONFIG_PM
+		if (gspca_dev->frozen)
+			break;
+#endif
+		if (!gspca_dev->present || !gspca_dev->streaming)
+			break;
+
+		/* Bit 0 of register 11 indicates FIFO overflow */
+		gspca_dev->usb_err = 0;
+		reg11 = reg_r(gspca_dev, 0x0011);
+		if (gspca_dev->usb_err)
+			break;
+
+		change = reg11 & 0x01;
+		if (change) {				/* overflow */
+			good = 0;
+
+			if (reg07 == 0) /* Bit Rate Control not enabled? */
+				reg07 = 0x32; /* Allow 98 bytes / unit */
+			else if (reg07 > 2)
+				reg07 -= 2; /* Decrease allowed bytes / unit */
+			else
+				change = 0;
+		} else {				/* no overflow */
+			good++;
+			if (good >= 10) {
+				good = 0;
+				if (reg07) { /* BRC enabled? */
+					change = 1;
+					if (reg07 < 0x32)
+						reg07 += 2;
+					else
+						reg07 = 0;
+				}
+			}
+		}
+		if (change) {
+			gspca_dev->usb_err = 0;
+			reg_w(gspca_dev, reg07, 0x0007);
+			if (gspca_dev->usb_err)
+				break;
+		}
+		mutex_unlock(&gspca_dev->usb_lock);
+	}
+
+	/* Something went wrong. Unlock and return */
+	mutex_unlock(&gspca_dev->usb_lock);
+}
+
+static void send_unknown(struct gspca_dev *gspca_dev, int sensor)
+{
+	reg_w(gspca_dev, 0x01, 0x0000);		/* bridge reset */
+	switch (sensor) {
+	case SENSOR_PAS106:
+		reg_w(gspca_dev, 0x03, 0x003a);
+		reg_w(gspca_dev, 0x0c, 0x003b);
+		reg_w(gspca_dev, 0x08, 0x0038);
+		break;
+	case SENSOR_ADCM2700:
+	case SENSOR_GC0305:
+	case SENSOR_OV7620:
+	case SENSOR_MT9V111_1:
+	case SENSOR_MT9V111_3:
+	case SENSOR_PB0330:
+	case SENSOR_PO2030:
+		reg_w(gspca_dev, 0x0d, 0x003a);
+		reg_w(gspca_dev, 0x02, 0x003b);
+		reg_w(gspca_dev, 0x00, 0x0038);
+		break;
+	case SENSOR_HV7131R:
+	case SENSOR_PAS202B:
+		reg_w(gspca_dev, 0x03, 0x003b);
+		reg_w(gspca_dev, 0x0c, 0x003a);
+		reg_w(gspca_dev, 0x0b, 0x0039);
+		if (sensor == SENSOR_PAS202B)
+			reg_w(gspca_dev, 0x0b, 0x0038);
+		break;
+	}
+}
+
+/* start probe 2 wires */
+static void start_2wr_probe(struct gspca_dev *gspca_dev, int sensor)
+{
+	reg_w(gspca_dev, 0x01, 0x0000);
+	reg_w(gspca_dev, sensor, 0x0010);
+	reg_w(gspca_dev, 0x01, 0x0001);
+	reg_w(gspca_dev, 0x03, 0x0012);
+	reg_w(gspca_dev, 0x01, 0x0012);
+/*	msleep(2); */
+}
+
+static int sif_probe(struct gspca_dev *gspca_dev)
+{
+	u16 checkword;
+
+	start_2wr_probe(gspca_dev, 0x0f);		/* PAS106 */
+	reg_w(gspca_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, SENSOR_PAS106);
+		return 0x0f;			/* PAS106 */
+	}
+	return -1;
+}
+
+static int vga_2wr_probe(struct gspca_dev *gspca_dev)
+{
+	u16 retword;
+
+	start_2wr_probe(gspca_dev, 0x00);	/* HV7131B */
+	i2c_write(gspca_dev, 0x01, 0xaa, 0x00);
+	retword = i2c_read(gspca_dev, 0x01);
+	if (retword != 0)
+		return 0x00;			/* HV7131B */
+
+	start_2wr_probe(gspca_dev, 0x04);	/* CS2102 */
+	i2c_write(gspca_dev, 0x01, 0xaa, 0x00);
+	retword = i2c_read(gspca_dev, 0x01);
+	if (retword != 0)
+		return 0x04;			/* CS2102 */
+
+	start_2wr_probe(gspca_dev, 0x06);	/* OmniVision */
+	reg_w(gspca_dev, 0x08, 0x008d);
+	i2c_write(gspca_dev, 0x11, 0xaa, 0x00);
+	retword = i2c_read(gspca_dev, 0x11);
+	if (retword != 0) {
+		/* (should have returned 0xaa) --> Omnivision? */
+		/* reg_r 0x10 -> 0x06 -->  */
+		goto ov_check;
+	}
+
+	start_2wr_probe(gspca_dev, 0x08);	/* HDCS2020 */
+	i2c_write(gspca_dev, 0x1c, 0x00, 0x00);
+	i2c_write(gspca_dev, 0x15, 0xaa, 0x00);
+	retword = i2c_read(gspca_dev, 0x15);
+	if (retword != 0)
+		return 0x08;			/* HDCS2020 */
+
+	start_2wr_probe(gspca_dev, 0x0a);	/* PB0330 */
+	i2c_write(gspca_dev, 0x07, 0xaa, 0xaa);
+	retword = i2c_read(gspca_dev, 0x07);
+	if (retword != 0)
+		return 0x0a;			/* PB0330 */
+	retword = i2c_read(gspca_dev, 0x03);
+	if (retword != 0)
+		return 0x0a;			/* PB0330 ?? */
+	retword = i2c_read(gspca_dev, 0x04);
+	if (retword != 0)
+		return 0x0a;			/* PB0330 ?? */
+
+	start_2wr_probe(gspca_dev, 0x0c);	/* ICM105A */
+	i2c_write(gspca_dev, 0x01, 0x11, 0x00);
+	retword = i2c_read(gspca_dev, 0x01);
+	if (retword != 0)
+		return 0x0c;			/* ICM105A */
+
+	start_2wr_probe(gspca_dev, 0x0e);	/* PAS202BCB */
+	reg_w(gspca_dev, 0x08, 0x008d);
+	i2c_write(gspca_dev, 0x03, 0xaa, 0x00);
+	msleep(50);
+	retword = i2c_read(gspca_dev, 0x03);
+	if (retword != 0) {
+		send_unknown(gspca_dev, SENSOR_PAS202B);
+		return 0x0e;			/* PAS202BCB */
+	}
+
+	start_2wr_probe(gspca_dev, 0x02);	/* TAS5130C */
+	i2c_write(gspca_dev, 0x01, 0xaa, 0x00);
+	retword = i2c_read(gspca_dev, 0x01);
+	if (retword != 0)
+		return 0x02;			/* TAS5130C */
+ov_check:
+	reg_r(gspca_dev, 0x0010);		/* ?? */
+	reg_r(gspca_dev, 0x0010);
+
+	reg_w(gspca_dev, 0x01, 0x0000);
+	reg_w(gspca_dev, 0x01, 0x0001);
+	reg_w(gspca_dev, 0x06, 0x0010);		/* OmniVision */
+	reg_w(gspca_dev, 0xa1, 0x008b);
+	reg_w(gspca_dev, 0x08, 0x008d);
+	msleep(500);
+	reg_w(gspca_dev, 0x01, 0x0012);
+	i2c_write(gspca_dev, 0x12, 0x80, 0x00);	/* sensor reset */
+	retword = i2c_read(gspca_dev, 0x0a) << 8;
+	retword |= i2c_read(gspca_dev, 0x0b);
+	PDEBUG(D_PROBE, "probe 2wr ov vga 0x%04x", retword);
+	switch (retword) {
+	case 0x7631:				/* OV7630C */
+		reg_w(gspca_dev, 0x06, 0x0010);
+		break;
+	case 0x7620:				/* OV7620 */
+	case 0x7648:				/* OV7648 */
+		break;
+	default:
+		return -1;			/* not OmniVision */
+	}
+	return retword;
+}
+
+struct sensor_by_chipset_revision {
+	u16 revision;
+	u8 internal_sensor_id;
+};
+static const struct sensor_by_chipset_revision chipset_revision_sensor[] = {
+	{0xc000, 0x12},		/* TAS5130C */
+	{0xc001, 0x13},		/* MT9V111 */
+	{0xe001, 0x13},
+	{0x8001, 0x13},
+	{0x8000, 0x14},		/* CS2102K */
+	{0x8400, 0x15},		/* MT9V111 */
+	{0xe400, 0x15},
+};
+
+static int vga_3wr_probe(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int i;
+	u16 retword;
+
+/*fixme: lack of 8b=b3 (11,12)-> 10, 8b=e0 (14,15,16)-> 12 found in gspcav1*/
+	reg_w(gspca_dev, 0x02, 0x0010);
+	reg_r(gspca_dev, 0x0010);
+	reg_w(gspca_dev, 0x01, 0x0000);
+	reg_w(gspca_dev, 0x00, 0x0010);
+	reg_w(gspca_dev, 0x01, 0x0001);
+	reg_w(gspca_dev, 0x91, 0x008b);
+	reg_w(gspca_dev, 0x03, 0x0012);
+	reg_w(gspca_dev, 0x01, 0x0012);
+	reg_w(gspca_dev, 0x05, 0x0012);
+	retword = i2c_read(gspca_dev, 0x14);
+	if (retword != 0)
+		return 0x11;			/* HV7131R */
+	retword = i2c_read(gspca_dev, 0x15);
+	if (retword != 0)
+		return 0x11;			/* HV7131R */
+	retword = i2c_read(gspca_dev, 0x16);
+	if (retword != 0)
+		return 0x11;			/* HV7131R */
+
+	reg_w(gspca_dev, 0x02, 0x0010);
+	retword = reg_r(gspca_dev, 0x000b) << 8;
+	retword |= reg_r(gspca_dev, 0x000a);
+	PDEBUG(D_PROBE, "probe 3wr vga 1 0x%04x", retword);
+	reg_r(gspca_dev, 0x0010);
+	if ((retword & 0xff00) == 0x6400)
+		return 0x02;		/* TAS5130C */
+	for (i = 0; i < ARRAY_SIZE(chipset_revision_sensor); i++) {
+		if (chipset_revision_sensor[i].revision == retword) {
+			sd->chip_revision = retword;
+			send_unknown(gspca_dev, SENSOR_PB0330);
+			return chipset_revision_sensor[i].internal_sensor_id;
+		}
+	}
+
+	reg_w(gspca_dev, 0x01, 0x0000);	/* check PB0330 */
+	reg_w(gspca_dev, 0x01, 0x0001);
+	reg_w(gspca_dev, 0xdd, 0x008b);
+	reg_w(gspca_dev, 0x0a, 0x0010);
+	reg_w(gspca_dev, 0x03, 0x0012);
+	reg_w(gspca_dev, 0x01, 0x0012);
+	retword = i2c_read(gspca_dev, 0x00);
+	if (retword != 0) {
+		PDEBUG(D_PROBE, "probe 3wr vga type 0a");
+		return 0x0a;			/* PB0330 */
+	}
+
+	/* probe gc0303 / gc0305 */
+	reg_w(gspca_dev, 0x01, 0x0000);
+	reg_w(gspca_dev, 0x01, 0x0001);
+	reg_w(gspca_dev, 0x98, 0x008b);
+	reg_w(gspca_dev, 0x01, 0x0010);
+	reg_w(gspca_dev, 0x03, 0x0012);
+	msleep(2);
+	reg_w(gspca_dev, 0x01, 0x0012);
+	retword = i2c_read(gspca_dev, 0x00);
+	if (retword != 0) {
+		PDEBUG(D_PROBE, "probe 3wr vga type %02x", retword);
+		if (retword == 0x0011)			/* gc0303 */
+			return 0x0303;
+		if (retword == 0x0029)			/* gc0305 */
+			send_unknown(gspca_dev, SENSOR_GC0305);
+		return retword;
+	}
+
+	reg_w(gspca_dev, 0x01, 0x0000);	/* check OmniVision */
+	reg_w(gspca_dev, 0x01, 0x0001);
+	reg_w(gspca_dev, 0xa1, 0x008b);
+	reg_w(gspca_dev, 0x08, 0x008d);
+	reg_w(gspca_dev, 0x06, 0x0010);
+	reg_w(gspca_dev, 0x01, 0x0012);
+	reg_w(gspca_dev, 0x05, 0x0012);
+	if (i2c_read(gspca_dev, 0x1c) == 0x007f	/* OV7610 - manufacturer ID */
+	    && i2c_read(gspca_dev, 0x1d) == 0x00a2) {
+		send_unknown(gspca_dev, SENSOR_OV7620);
+		return 0x06;		/* OmniVision confirm ? */
+	}
+
+	reg_w(gspca_dev, 0x01, 0x0000);
+	reg_w(gspca_dev, 0x00, 0x0002);
+	reg_w(gspca_dev, 0x01, 0x0010);
+	reg_w(gspca_dev, 0x01, 0x0001);
+	reg_w(gspca_dev, 0xee, 0x008b);
+	reg_w(gspca_dev, 0x03, 0x0012);
+	reg_w(gspca_dev, 0x01, 0x0012);
+	reg_w(gspca_dev, 0x05, 0x0012);
+	retword = i2c_read(gspca_dev, 0x00) << 8;	/* ID 0 */
+	retword |= i2c_read(gspca_dev, 0x01);		/* ID 1 */
+	PDEBUG(D_PROBE, "probe 3wr vga 2 0x%04x", retword);
+	if (retword == 0x2030) {
+		u8 retbyte;
+
+		retbyte = i2c_read(gspca_dev, 0x02);	/* revision number */
+		PDEBUG(D_PROBE, "sensor PO2030 rev 0x%02x", retbyte);
+
+		send_unknown(gspca_dev, SENSOR_PO2030);
+		return retword;
+	}
+
+	reg_w(gspca_dev, 0x01, 0x0000);
+	reg_w(gspca_dev, 0x0a, 0x0010);
+	reg_w(gspca_dev, 0xd3, 0x008b);
+	reg_w(gspca_dev, 0x01, 0x0001);
+	reg_w(gspca_dev, 0x03, 0x0012);
+	reg_w(gspca_dev, 0x01, 0x0012);
+	reg_w(gspca_dev, 0x05, 0x0012);
+	reg_w(gspca_dev, 0xd3, 0x008b);
+	retword = i2c_read(gspca_dev, 0x01);
+	if (retword != 0) {
+		PDEBUG(D_PROBE, "probe 3wr vga type 0a ? ret: %04x", retword);
+		return 0x16;			/* adcm2700 (6100/6200) */
+	}
+	return -1;
+}
+
+static int zcxx_probeSensor(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int sensor;
+
+	switch (sd->sensor) {
+	case SENSOR_MC501CB:
+		return -1;		/* don't probe */
+	case SENSOR_GC0303:
+			/* may probe but with no write in reg 0x0010 */
+		return -1;		/* don't probe */
+	case SENSOR_PAS106:
+		sensor =  sif_probe(gspca_dev);
+		if (sensor >= 0)
+			return sensor;
+		break;
+	}
+	sensor = vga_2wr_probe(gspca_dev);
+	if (sensor >= 0)
+		return sensor;
+	return vga_3wr_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;
+
+	if (id->idProduct == 0x301b)
+		sd->bridge = BRIDGE_ZC301;
+	else
+		sd->bridge = BRIDGE_ZC303;
+
+	/* define some sensors from the vendor/product */
+	sd->sensor = id->driver_info;
+
+	sd->reg08 = REG08_DEF;
+
+	INIT_WORK(&sd->work, transfer_update);
+
+	return 0;
+}
+
+static int zcxx_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct gspca_dev *gspca_dev =
+		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+	struct sd *sd = (struct sd *)gspca_dev;
+
+	switch (ctrl->id) {
+	case V4L2_CID_AUTOGAIN:
+		gspca_dev->usb_err = 0;
+		if (ctrl->val && sd->exposure && gspca_dev->streaming)
+			sd->exposure->val = getexposure(gspca_dev);
+		return gspca_dev->usb_err;
+	}
+	return -EINVAL;
+}
+
+static int zcxx_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct gspca_dev *gspca_dev =
+		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+	struct sd *sd = (struct sd *)gspca_dev;
+	int i, qual;
+
+	gspca_dev->usb_err = 0;
+
+	if (ctrl->id == V4L2_CID_JPEG_COMPRESSION_QUALITY) {
+		qual = sd->reg08 >> 1;
+
+		for (i = 0; i < ARRAY_SIZE(jpeg_qual); i++) {
+			if (ctrl->val <= jpeg_qual[i])
+				break;
+		}
+		if (i == ARRAY_SIZE(jpeg_qual) || (i > 0 && i == qual && ctrl->val < jpeg_qual[i]))
+			i--;
+
+		/* With high quality settings we need max bandwidth */
+		if (i >= 2 && gspca_dev->streaming &&
+		    !gspca_dev->cam.needs_full_bandwidth)
+			return -EBUSY;
+
+		sd->reg08 = (i << 1) | 1;
+		ctrl->val = jpeg_qual[i];
+	}
+
+	if (!gspca_dev->streaming)
+		return 0;
+
+	switch (ctrl->id) {
+	/* gamma/brightness/contrast cluster */
+	case V4L2_CID_GAMMA:
+		setcontrast(gspca_dev, sd->gamma->val,
+				sd->brightness->val, sd->contrast->val);
+		break;
+	/* autogain/exposure cluster */
+	case V4L2_CID_AUTOGAIN:
+		setautogain(gspca_dev, ctrl->val);
+		if (!gspca_dev->usb_err && !ctrl->val && sd->exposure)
+			setexposure(gspca_dev, sd->exposure->val);
+		break;
+	case V4L2_CID_POWER_LINE_FREQUENCY:
+		setlightfreq(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_SHARPNESS:
+		setsharpness(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_JPEG_COMPRESSION_QUALITY:
+		setquality(gspca_dev);
+		break;
+	}
+	return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops zcxx_ctrl_ops = {
+	.g_volatile_ctrl = zcxx_g_volatile_ctrl,
+	.s_ctrl = zcxx_s_ctrl,
+};
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *)gspca_dev;
+	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+	static const u8 gamma[SENSOR_MAX] = {
+		[SENSOR_ADCM2700] =	4,
+		[SENSOR_CS2102] =	4,
+		[SENSOR_CS2102K] =	5,
+		[SENSOR_GC0303] =	3,
+		[SENSOR_GC0305] =	4,
+		[SENSOR_HDCS2020] =	4,
+		[SENSOR_HV7131B] =	4,
+		[SENSOR_HV7131R] =	4,
+		[SENSOR_ICM105A] =	4,
+		[SENSOR_MC501CB] =	4,
+		[SENSOR_MT9V111_1] =	4,
+		[SENSOR_MT9V111_3] =	4,
+		[SENSOR_OV7620] =	3,
+		[SENSOR_OV7630C] =	4,
+		[SENSOR_PAS106] =	4,
+		[SENSOR_PAS202B] =	4,
+		[SENSOR_PB0330] =	4,
+		[SENSOR_PO2030] =	4,
+		[SENSOR_TAS5130C] =	3,
+	};
+
+	gspca_dev->vdev.ctrl_handler = hdl;
+	v4l2_ctrl_handler_init(hdl, 8);
+	sd->brightness = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops,
+			V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+	sd->contrast = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops,
+			V4L2_CID_CONTRAST, 0, 255, 1, 128);
+	sd->gamma = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops,
+			V4L2_CID_GAMMA, 1, 6, 1, gamma[sd->sensor]);
+	if (sd->sensor == SENSOR_HV7131R)
+		sd->exposure = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops,
+			V4L2_CID_EXPOSURE, 0x30d, 0x493e, 1, 0x927);
+	sd->autogain = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops,
+			V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+	if (sd->sensor != SENSOR_OV7630C)
+		sd->plfreq = v4l2_ctrl_new_std_menu(hdl, &zcxx_ctrl_ops,
+			V4L2_CID_POWER_LINE_FREQUENCY,
+			V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0,
+			V4L2_CID_POWER_LINE_FREQUENCY_DISABLED);
+	sd->sharpness = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops,
+			V4L2_CID_SHARPNESS, 0, 3, 1,
+			sd->sensor == SENSOR_PO2030 ? 0 : 2);
+	sd->jpegqual = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops,
+			V4L2_CID_JPEG_COMPRESSION_QUALITY,
+			jpeg_qual[0], jpeg_qual[ARRAY_SIZE(jpeg_qual) - 1], 1,
+			jpeg_qual[REG08_DEF >> 1]);
+	if (hdl->error) {
+		pr_err("Could not initialize controls\n");
+		return hdl->error;
+	}
+	v4l2_ctrl_cluster(3, &sd->gamma);
+	if (sd->sensor == SENSOR_HV7131R)
+		v4l2_ctrl_auto_cluster(2, &sd->autogain, 0, true);
+	return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct cam *cam;
+	int sensor;
+	static const u8 mode_tb[SENSOR_MAX] = {
+		[SENSOR_ADCM2700] =	2,
+		[SENSOR_CS2102] =	1,
+		[SENSOR_CS2102K] =	1,
+		[SENSOR_GC0303] =	1,
+		[SENSOR_GC0305] =	1,
+		[SENSOR_HDCS2020] =	1,
+		[SENSOR_HV7131B] =	1,
+		[SENSOR_HV7131R] =	1,
+		[SENSOR_ICM105A] =	1,
+		[SENSOR_MC501CB] =	2,
+		[SENSOR_MT9V111_1] =	1,
+		[SENSOR_MT9V111_3] =	1,
+		[SENSOR_OV7620] =	2,
+		[SENSOR_OV7630C] =	1,
+		[SENSOR_PAS106] =	0,
+		[SENSOR_PAS202B] =	1,
+		[SENSOR_PB0330] =	1,
+		[SENSOR_PO2030] =	1,
+		[SENSOR_TAS5130C] =	1,
+	};
+
+	sensor = zcxx_probeSensor(gspca_dev);
+	if (sensor >= 0)
+		PDEBUG(D_PROBE, "probe sensor -> %04x", 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_GC0303:
+				PDEBUG(D_PROBE, "Sensor GC0303");
+				break;
+			default:
+				pr_warn("Unknown sensor - set to TAS5130C\n");
+				sd->sensor = SENSOR_TAS5130C;
+			}
+			break;
+		case 0:
+			/* check the sensor type */
+			sensor = i2c_read(gspca_dev, 0x00);
+			PDEBUG(D_PROBE, "Sensor hv7131 type %d", sensor);
+			switch (sensor) {
+			case 0:			/* hv7131b */
+			case 1:			/* hv7131e */
+				PDEBUG(D_PROBE, "Find Sensor HV7131B");
+				sd->sensor = SENSOR_HV7131B;
+				break;
+			default:
+/*			case 2:			 * hv7131r */
+				PDEBUG(D_PROBE, "Find Sensor HV7131R");
+				sd->sensor = SENSOR_HV7131R;
+				break;
+			}
+			break;
+		case 0x02:
+			PDEBUG(D_PROBE, "Sensor TAS5130C");
+			sd->sensor = SENSOR_TAS5130C;
+			break;
+		case 0x04:
+			PDEBUG(D_PROBE, "Find Sensor CS2102");
+			sd->sensor = SENSOR_CS2102;
+			break;
+		case 0x08:
+			PDEBUG(D_PROBE, "Find Sensor HDCS2020");
+			sd->sensor = SENSOR_HDCS2020;
+			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 PAS202B");
+			sd->sensor = SENSOR_PAS202B;
+			break;
+		case 0x0f:
+			PDEBUG(D_PROBE, "Find Sensor PAS106");
+			sd->sensor = SENSOR_PAS106;
+			break;
+		case 0x10:
+		case 0x12:
+			PDEBUG(D_PROBE, "Find Sensor TAS5130C");
+			sd->sensor = SENSOR_TAS5130C;
+			break;
+		case 0x11:
+			PDEBUG(D_PROBE, "Find Sensor HV7131R");
+			sd->sensor = SENSOR_HV7131R;
+			break;
+		case 0x13:
+		case 0x15:
+			PDEBUG(D_PROBE,
+				"Sensor MT9V111. Chip revision %04x",
+				sd->chip_revision);
+			sd->sensor = sd->bridge == BRIDGE_ZC301
+					? SENSOR_MT9V111_1
+					: SENSOR_MT9V111_3;
+			break;
+		case 0x14:
+			PDEBUG(D_PROBE,
+				"Find Sensor CS2102K?. Chip revision %x",
+				sd->chip_revision);
+			sd->sensor = SENSOR_CS2102K;
+			break;
+		case 0x16:
+			PDEBUG(D_PROBE, "Find Sensor ADCM2700");
+			sd->sensor = SENSOR_ADCM2700;
+			break;
+		case 0x29:
+			PDEBUG(D_PROBE, "Find Sensor GC0305");
+			sd->sensor = SENSOR_GC0305;
+			break;
+		case 0x0303:
+			PDEBUG(D_PROBE, "Sensor GC0303");
+			sd->sensor =  SENSOR_GC0303;
+			break;
+		case 0x2030:
+			PDEBUG(D_PROBE, "Find Sensor PO2030");
+			sd->sensor = SENSOR_PO2030;
+			break;
+		case 0x7620:
+			PDEBUG(D_PROBE, "Find Sensor OV7620");
+			sd->sensor = SENSOR_OV7620;
+			break;
+		case 0x7631:
+			PDEBUG(D_PROBE, "Find Sensor OV7630C");
+			sd->sensor = SENSOR_OV7630C;
+			break;
+		case 0x7648:
+			PDEBUG(D_PROBE, "Find Sensor OV7648");
+			sd->sensor = SENSOR_OV7620;	/* same sensor (?) */
+			break;
+		default:
+			pr_err("Unknown sensor %04x\n", sensor);
+			return -EINVAL;
+		}
+	}
+	if (sensor < 0x20) {
+		if (sensor == -1 || sensor == 0x10 || sensor == 0x12)
+			reg_w(gspca_dev, 0x02, 0x0010);
+		reg_r(gspca_dev, 0x0010);
+	}
+
+	cam = &gspca_dev->cam;
+	switch (mode_tb[sd->sensor]) {
+	case 0:
+		cam->cam_mode = sif_mode;
+		cam->nmodes = ARRAY_SIZE(sif_mode);
+		break;
+	case 1:
+		cam->cam_mode = vga_mode;
+		cam->nmodes = ARRAY_SIZE(vga_mode);
+		break;
+	default:
+/*	case 2: */
+		cam->cam_mode = broken_vga_mode;
+		cam->nmodes = ARRAY_SIZE(broken_vga_mode);
+		break;
+	}
+
+	/* switch off the led */
+	reg_w(gspca_dev, 0x01, 0x0000);
+	return gspca_dev->usb_err;
+}
+
+static int sd_pre_start(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	gspca_dev->cam.needs_full_bandwidth = (sd->reg08 >= 4) ? 1 : 0;
+	return 0;
+}
+
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int mode;
+	static const struct usb_action *init_tb[SENSOR_MAX][2] = {
+	[SENSOR_ADCM2700] =
+			{adcm2700_Initial, adcm2700_InitialScale},
+	[SENSOR_CS2102]	=
+			{cs2102_Initial, cs2102_InitialScale},
+	[SENSOR_CS2102K] =
+			{cs2102K_Initial, cs2102K_InitialScale},
+	[SENSOR_GC0303] =
+		{gc0303_Initial, gc0303_InitialScale},
+	[SENSOR_GC0305] =
+			{gc0305_Initial, gc0305_InitialScale},
+	[SENSOR_HDCS2020] =
+			{hdcs2020_Initial, hdcs2020_InitialScale},
+	[SENSOR_HV7131B] =
+			{hv7131b_Initial, hv7131b_InitialScale},
+	[SENSOR_HV7131R] =
+			{hv7131r_Initial, hv7131r_InitialScale},
+	[SENSOR_ICM105A] =
+			{icm105a_Initial, icm105a_InitialScale},
+	[SENSOR_MC501CB] =
+			{mc501cb_Initial, mc501cb_InitialScale},
+	[SENSOR_MT9V111_1] =
+			{mt9v111_1_Initial, mt9v111_1_InitialScale},
+	[SENSOR_MT9V111_3] =
+			{mt9v111_3_Initial, mt9v111_3_InitialScale},
+	[SENSOR_OV7620] =
+			{ov7620_Initial, ov7620_InitialScale},
+	[SENSOR_OV7630C] =
+			{ov7630c_Initial, ov7630c_InitialScale},
+	[SENSOR_PAS106] =
+			{pas106b_Initial, pas106b_InitialScale},
+	[SENSOR_PAS202B] =
+			{pas202b_Initial, pas202b_InitialScale},
+	[SENSOR_PB0330] =
+			{pb0330_Initial, pb0330_InitialScale},
+	[SENSOR_PO2030] =
+			{po2030_Initial, po2030_InitialScale},
+	[SENSOR_TAS5130C] =
+			{tas5130c_Initial, tas5130c_InitialScale},
+	};
+
+	/* create the JPEG header */
+	jpeg_define(sd->jpeg_hdr, gspca_dev->pixfmt.height,
+			gspca_dev->pixfmt.width,
+			0x21);		/* JPEG 422 */
+
+	mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
+	switch (sd->sensor) {
+	case SENSOR_HV7131R:
+		zcxx_probeSensor(gspca_dev);
+		break;
+	case SENSOR_PAS106:
+		usb_exchange(gspca_dev, pas106b_Initial_com);
+		break;
+	}
+	usb_exchange(gspca_dev, init_tb[sd->sensor][mode]);
+
+	switch (sd->sensor) {
+	case SENSOR_ADCM2700:
+	case SENSOR_GC0305:
+	case SENSOR_OV7620:
+	case SENSOR_PO2030:
+	case SENSOR_TAS5130C:
+	case SENSOR_GC0303:
+/*		msleep(100);			 * ?? */
+		reg_r(gspca_dev, 0x0002);	/* --> 0x40 */
+		reg_w(gspca_dev, 0x09, 0x01ad);	/* (from win traces) */
+		reg_w(gspca_dev, 0x15, 0x01ae);
+		if (sd->sensor == SENSOR_TAS5130C)
+			break;
+		reg_w(gspca_dev, 0x0d, 0x003a);
+		reg_w(gspca_dev, 0x02, 0x003b);
+		reg_w(gspca_dev, 0x00, 0x0038);
+		break;
+	case SENSOR_HV7131R:
+	case SENSOR_PAS202B:
+		reg_w(gspca_dev, 0x03, 0x003b);
+		reg_w(gspca_dev, 0x0c, 0x003a);
+		reg_w(gspca_dev, 0x0b, 0x0039);
+		if (sd->sensor == SENSOR_HV7131R)
+			reg_w(gspca_dev, 0x50, ZC3XX_R11D_GLOBALGAIN);
+		break;
+	}
+
+	setmatrix(gspca_dev);
+	switch (sd->sensor) {
+	case SENSOR_ADCM2700:
+	case SENSOR_OV7620:
+		reg_r(gspca_dev, 0x0008);
+		reg_w(gspca_dev, 0x00, 0x0008);
+		break;
+	case SENSOR_PAS202B:
+	case SENSOR_GC0305:
+	case SENSOR_HV7131R:
+	case SENSOR_TAS5130C:
+		reg_r(gspca_dev, 0x0008);
+		/* fall thru */
+	case SENSOR_PO2030:
+		reg_w(gspca_dev, 0x03, 0x0008);
+		break;
+	}
+	setsharpness(gspca_dev, v4l2_ctrl_g_ctrl(sd->sharpness));
+
+	/* set the gamma tables when not set */
+	switch (sd->sensor) {
+	case SENSOR_CS2102K:		/* gamma set in xxx_Initial */
+	case SENSOR_HDCS2020:
+	case SENSOR_OV7630C:
+		break;
+	default:
+		setcontrast(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma),
+				v4l2_ctrl_g_ctrl(sd->brightness),
+				v4l2_ctrl_g_ctrl(sd->contrast));
+		break;
+	}
+	setmatrix(gspca_dev);			/* one more time? */
+	switch (sd->sensor) {
+	case SENSOR_OV7620:
+	case SENSOR_PAS202B:
+		reg_r(gspca_dev, 0x0180);	/* from win */
+		reg_w(gspca_dev, 0x00, 0x0180);
+		break;
+	}
+	setquality(gspca_dev);
+	/* Start with BRC disabled, transfer_update will enable it if needed */
+	reg_w(gspca_dev, 0x00, 0x0007);
+	if (sd->plfreq)
+		setlightfreq(gspca_dev, v4l2_ctrl_g_ctrl(sd->plfreq));
+
+	switch (sd->sensor) {
+	case SENSOR_ADCM2700:
+		reg_w(gspca_dev, 0x09, 0x01ad);	/* (from win traces) */
+		reg_w(gspca_dev, 0x15, 0x01ae);
+		reg_w(gspca_dev, 0x02, 0x0180);
+						/* ms-win + */
+		reg_w(gspca_dev, 0x40, 0x0117);
+		break;
+	case SENSOR_HV7131R:
+		setexposure(gspca_dev, v4l2_ctrl_g_ctrl(sd->exposure));
+		reg_w(gspca_dev, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN);
+		break;
+	case SENSOR_GC0305:
+	case SENSOR_TAS5130C:
+		reg_w(gspca_dev, 0x09, 0x01ad);	/* (from win traces) */
+		reg_w(gspca_dev, 0x15, 0x01ae);
+		/* fall thru */
+	case SENSOR_PAS202B:
+	case SENSOR_PO2030:
+/*		reg_w(gspca_dev, 0x40, ZC3XX_R117_GGAIN); in win traces */
+		reg_r(gspca_dev, 0x0180);
+		break;
+	case SENSOR_OV7620:
+		reg_w(gspca_dev, 0x09, 0x01ad);
+		reg_w(gspca_dev, 0x15, 0x01ae);
+		i2c_read(gspca_dev, 0x13);	/*fixme: returns 0xa3 */
+		i2c_write(gspca_dev, 0x13, 0xa3, 0x00);
+					/*fixme: returned value to send? */
+		reg_w(gspca_dev, 0x40, 0x0117);
+		reg_r(gspca_dev, 0x0180);
+		break;
+	}
+
+	setautogain(gspca_dev, v4l2_ctrl_g_ctrl(sd->autogain));
+
+	if (gspca_dev->usb_err < 0)
+		return gspca_dev->usb_err;
+
+	/* Start the transfer parameters update thread */
+	sd->work_thread = create_singlethread_workqueue(KBUILD_MODNAME);
+	queue_work(sd->work_thread, &sd->work);
+
+	return 0;
+}
+
+/* called on streamoff with alt==0 and on disconnect */
+/* the usb_lock is held at entry - restore on exit */
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (sd->work_thread != NULL) {
+		mutex_unlock(&gspca_dev->usb_lock);
+		destroy_workqueue(sd->work_thread);
+		mutex_lock(&gspca_dev->usb_lock);
+		sd->work_thread = NULL;
+	}
+	if (!gspca_dev->present)
+		return;
+	send_unknown(gspca_dev, sd->sensor);
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+			u8 *data,
+			int len)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	/* check the JPEG end of frame */
+	if (len >= 3
+	 && data[len - 3] == 0xff && data[len - 2] == 0xd9) {
+/*fixme: what does the last byte mean?*/
+		gspca_frame_add(gspca_dev, LAST_PACKET,
+					data, len - 1);
+		return;
+	}
+
+	/* check the JPEG start of a frame */
+	if (data[0] == 0xff && data[1] == 0xd8) {
+		/* put the JPEG header in the new frame */
+		gspca_frame_add(gspca_dev, FIRST_PACKET,
+			sd->jpeg_hdr, JPEG_HDR_SZ);
+
+		/* 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, data, len);
+}
+
+static int sd_set_jcomp(struct gspca_dev *gspca_dev,
+			const struct v4l2_jpegcompression *jcomp)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	return v4l2_ctrl_s_ctrl(sd->jpegqual, jcomp->quality);
+}
+
+static int sd_get_jcomp(struct gspca_dev *gspca_dev,
+			struct v4l2_jpegcompression *jcomp)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	memset(jcomp, 0, sizeof *jcomp);
+	jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual);
+	jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
+			| V4L2_JPEG_MARKER_DQT;
+	return 0;
+}
+
+#if IS_ENABLED(CONFIG_INPUT)
+static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
+			u8 *data,		/* interrupt packet data */
+			int len)		/* interrupt packet length */
+{
+	if (len == 8 && data[4] == 1) {
+		input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1);
+		input_sync(gspca_dev->input_dev);
+		input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
+		input_sync(gspca_dev->input_dev);
+	}
+
+	return 0;
+}
+#endif
+
+static const struct sd_desc sd_desc = {
+	.name = KBUILD_MODNAME,
+	.config = sd_config,
+	.init = sd_init,
+	.init_controls = sd_init_controls,
+	.isoc_init = sd_pre_start,
+	.start = sd_start,
+	.stop0 = sd_stop0,
+	.pkt_scan = sd_pkt_scan,
+	.get_jcomp = sd_get_jcomp,
+	.set_jcomp = sd_set_jcomp,
+#if IS_ENABLED(CONFIG_INPUT)
+	.int_pkt_scan = sd_int_pkt_scan,
+#endif
+};
+
+static const struct usb_device_id device_table[] = {
+	{USB_DEVICE(0x03f0, 0x1b07)},
+	{USB_DEVICE(0x041e, 0x041e)},
+	{USB_DEVICE(0x041e, 0x4017)},
+	{USB_DEVICE(0x041e, 0x401c), .driver_info = SENSOR_PAS106},
+	{USB_DEVICE(0x041e, 0x401e)},
+	{USB_DEVICE(0x041e, 0x401f)},
+	{USB_DEVICE(0x041e, 0x4022)},
+	{USB_DEVICE(0x041e, 0x4029)},
+	{USB_DEVICE(0x041e, 0x4034), .driver_info = SENSOR_PAS106},
+	{USB_DEVICE(0x041e, 0x4035), .driver_info = SENSOR_PAS106},
+	{USB_DEVICE(0x041e, 0x4036)},
+	{USB_DEVICE(0x041e, 0x403a)},
+	{USB_DEVICE(0x041e, 0x4051), .driver_info = SENSOR_GC0303},
+	{USB_DEVICE(0x041e, 0x4053), .driver_info = SENSOR_GC0303},
+	{USB_DEVICE(0x0458, 0x7007)},
+	{USB_DEVICE(0x0458, 0x700c)},
+	{USB_DEVICE(0x0458, 0x700f)},
+	{USB_DEVICE(0x0461, 0x0a00)},
+	{USB_DEVICE(0x046d, 0x089d), .driver_info = SENSOR_MC501CB},
+	{USB_DEVICE(0x046d, 0x08a0)},
+	{USB_DEVICE(0x046d, 0x08a1)},
+	{USB_DEVICE(0x046d, 0x08a2)},
+	{USB_DEVICE(0x046d, 0x08a3)},
+	{USB_DEVICE(0x046d, 0x08a6)},
+	{USB_DEVICE(0x046d, 0x08a7)},
+	{USB_DEVICE(0x046d, 0x08a9)},
+	{USB_DEVICE(0x046d, 0x08aa)},
+	{USB_DEVICE(0x046d, 0x08ac)},
+	{USB_DEVICE(0x046d, 0x08ad)},
+	{USB_DEVICE(0x046d, 0x08ae)},
+	{USB_DEVICE(0x046d, 0x08af)},
+	{USB_DEVICE(0x046d, 0x08b9)},
+	{USB_DEVICE(0x046d, 0x08d7)},
+	{USB_DEVICE(0x046d, 0x08d8)},
+	{USB_DEVICE(0x046d, 0x08d9)},
+	{USB_DEVICE(0x046d, 0x08da)},
+	{USB_DEVICE(0x046d, 0x08dd), .driver_info = SENSOR_MC501CB},
+	{USB_DEVICE(0x0471, 0x0325), .driver_info = SENSOR_PAS106},
+	{USB_DEVICE(0x0471, 0x0326), .driver_info = SENSOR_PAS106},
+	{USB_DEVICE(0x0471, 0x032d), .driver_info = SENSOR_PAS106},
+	{USB_DEVICE(0x0471, 0x032e), .driver_info = SENSOR_PAS106},
+	{USB_DEVICE(0x055f, 0xc005)},
+	{USB_DEVICE(0x055f, 0xd003)},
+	{USB_DEVICE(0x055f, 0xd004)},
+	{USB_DEVICE(0x0698, 0x2003)},
+	{USB_DEVICE(0x0ac8, 0x0301), .driver_info = SENSOR_PAS106},
+	{USB_DEVICE(0x0ac8, 0x0302), .driver_info = SENSOR_PAS106},
+	{USB_DEVICE(0x0ac8, 0x301b)},
+	{USB_DEVICE(0x0ac8, 0x303b)},
+	{USB_DEVICE(0x0ac8, 0x305b)},
+	{USB_DEVICE(0x0ac8, 0x307b)},
+	{USB_DEVICE(0x10fd, 0x0128)},
+	{USB_DEVICE(0x10fd, 0x804d)},
+	{USB_DEVICE(0x10fd, 0x8050)},
+	{}			/* end of entry */
+};
+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 = KBUILD_MODNAME,
+	.id_table = device_table,
+	.probe = sd_probe,
+	.disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+	.suspend = gspca_suspend,
+	.resume = gspca_resume,
+	.reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
+
+module_param(force_sensor, int, 0644);
+MODULE_PARM_DESC(force_sensor,
+	"Force sensor. Only for experts!!!");