diff options
Diffstat (limited to '')
96 files changed, 77698 insertions, 0 deletions
diff --git a/drivers/media/usb/gspca/Kconfig b/drivers/media/usb/gspca/Kconfig new file mode 100644 index 000000000..9c1939ce6 --- /dev/null +++ b/drivers/media/usb/gspca/Kconfig @@ -0,0 +1,455 @@ +# SPDX-License-Identifier: GPL-2.0-only +menuconfig USB_GSPCA + tristate "GSPCA based webcams" + depends on VIDEO_DEV + depends on INPUT || INPUT=n + select VIDEOBUF2_VMALLOC + help + Say Y here if you want to enable selecting webcams based + on the GSPCA framework. + + See <file:Documentation/admin-guide/media/gspca-cardlist.rst> 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_DEV + +config USB_GSPCA_BENQ + tristate "Benq USB Camera Driver" + depends on VIDEO_DEV && 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_DEV && 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_DEV && USB_GSPCA + help + Say Y here if you want support for USB cameras based on the cpia + CPiA chip. Note that you need at least 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_DEV && 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_DEV && 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_DEV && 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_DEV && 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_DEV && 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_DEV && 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_DEV && 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_DEV && 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_DEV && 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_DEV && 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_DEV && 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_DEV && 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_DEV && 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_DEV && 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_DEV && 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_DEV && 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_DEV && 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_DEV && 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_DEV && 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_DEV && 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_DEV && 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_SPCA1528 + tristate "SPCA1528 USB Camera Driver" + depends on VIDEO_DEV && 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_SPCA500 + tristate "SPCA500 USB Camera Driver" + depends on VIDEO_DEV && 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_DEV && 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_DEV && 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_DEV && 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_DEV && 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_DEV && 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_SQ905 + tristate "SQ Technologies SQ905 based USB Camera Driver" + depends on VIDEO_DEV && 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_DEV && 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_DEV && 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_DEV && 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_DEV && 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_DEV && 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_DEV && 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_DEV && 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_DEV && 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_DEV && 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_DEV && 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_DEV && 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_DEV && 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_DEV && 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_DEV && 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. + +source "drivers/media/usb/gspca/gl860/Kconfig" +source "drivers/media/usb/gspca/m5602/Kconfig" +source "drivers/media/usb/gspca/stv06xx/Kconfig" + +endif diff --git a/drivers/media/usb/gspca/Makefile b/drivers/media/usb/gspca/Makefile new file mode 100644 index 000000000..a35c45006 --- /dev/null +++ b/drivers/media/usb/gspca/Makefile @@ -0,0 +1,100 @@ +# SPDX-License-Identifier: GPL-2.0 +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 000000000..7ae7c4395 --- /dev/null +++ b/drivers/media/usb/gspca/autogain_functions.c @@ -0,0 +1,165 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Functions for auto gain. + * + * Copyright (C) 2010-2012 Hans de Goede <hdegoede@redhat.com> + */ +#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; + + gspca_dbg(gspca_dev, D_FRAM, "autogain: lum: %d, desired: %d, steps: %d\n", + avg_lum, desired_avg_lum, steps); + + for (i = 0; i < steps; i++) { + if (avg_lum > desired_avg_lum) { + if (gain > gain_knee) + gain--; + else if (exposure > exposure_knee) + exposure--; + else if (gain > 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) + gspca_dbg(gspca_dev, D_FRAM, "autogain: changed gain: %d, expo: %d\n", + 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 oscillating 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; + + gspca_dbg(gspca_dev, D_FRAM, "autogain: lum: %d, desired: %d, steps: %d\n", + 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) + gspca_dbg(gspca_dev, D_FRAM, "autogain: changed gain: %d, expo: %d\n", + 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 000000000..25c40fb1b --- /dev/null +++ b/drivers/media/usb/gspca/benq.c @@ -0,0 +1,274 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Benq DC E300 subdriver + * + * Copyright (C) 2009 Jean-Francois Moine (http://moinejf.free.fr) + */ + +#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) + 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; + + gspca_dbg(gspca_dev, D_PACK, "sd isoc irq\n"); + 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) { + gspca_err(gspca_dev, "ISOC bad lengths %d / %d\n", + 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 checked) + * - (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 checked) + */ + 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 000000000..8b2691243 --- /dev/null +++ b/drivers/media/usb/gspca/conex.c @@ -0,0 +1,953 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Connexant Cx11646 library + * Copyright (C) 2004 Michel Xhaard mxhaard@magic.fr + * + * V4L2 by Jean-Francois Moine <http://moinejf.free.fr> + */ + +#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) { + gspca_err(gspca_dev, "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); + gspca_dbg(gspca_dev, D_USBI, "reg read [%02x] -> %02x ..\n", + 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) { + gspca_err(gspca_dev, "reg_w: buffer overflow\n"); + return; + } + gspca_dbg(gspca_dev, D_USBO, "reg write [%02x] = %02x..\n", + 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) + gspca_err(gspca_dev, "Damned Errors sending jpeg Table\n"); + /* 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 000000000..5f5fa851c --- /dev/null +++ b/drivers/media/usb/gspca/cpia1.c @@ -0,0 +1,1900 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * 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 + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define MODULE_NAME "cpia1" + +#include <linux/input.h> +#include <linux/sched/signal.h> +#include <linux/bitops.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 than 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 { + gspca_err(gspca_dev, "Unexpected first byte of command: %x\n", + 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 */ + ret = do_command(gspca_dev, CPIA_COMMAND_WriteMCPort, + 3, 0xdf, 0xdf, 0); + if (ret) + return ret; + ret = do_command(gspca_dev, CPIA_COMMAND_WriteMCPort, + 3, 0xff, 0xff, 0); + if (ret) + return ret; + } + + /* 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) +{ + gspca_dbg(gspca_dev, D_PROBE, "status: %02x %02x %02x %02x %02x %02x %02x %02x\n", + 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) { + gspca_err(gspca_dev, "unexpected state after lo power cmd: %02x\n", + sd->params.status.systemState); + printstatus(gspca_dev, &sd->params); + } + return -EIO; + } + + gspca_dbg(gspca_dev, D_CONF, "camera now in LOW power state\n"); + 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) { + gspca_err(gspca_dev, "unexpected state after hi power cmd: %02x\n", + sd->params.status.systemState); + printstatus(gspca_dev, &sd->params); + return -EIO; + } + + gspca_dbg(gspca_dev, D_CONF, "camera now in HIGH power state\n"); + 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; + } + if (sd->params.exposure.gain >= BITS_PER_TYPE(currentexp)) + return -EINVAL; + 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; + gspca_dbg(gspca_dev, D_CONF, "Automatically decreasing sensor_fps\n"); + + } 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; + gspca_dbg(gspca_dev, D_CONF, "Automatically increasing sensor_fps\n"); + } + } 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; + gspca_dbg(gspca_dev, D_CONF, "Automatically decreasing sensor_fps\n"); + + } 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; + gspca_dbg(gspca_dev, D_CONF, "Automatically increasing sensor_fps\n"); + } + } + + 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); + + gspca_dbg(gspca_dev, D_PROBE, "cpia CPiA camera detected (vid/pid 0x%04X:0x%04X)\n", + 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) { + gspca_err(gspca_dev, "only firmware version 1 is supported (got: %d)\n", + 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) { + gspca_err(gspca_dev, "unexpected systemstate: %02x\n", + 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) { + gspca_err(gspca_dev, "fatal_error: %04x, vp_status: %04x\n", + 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); + + gspca_dbg(gspca_dev, D_PROBE, "CPIA Version: %d.%02d (%d.%d)\n", + sd->params.version.firmwareVersion, + sd->params.version.firmwareRevision, + sd->params.version.vcVersion, + sd->params.version.vcRevision); + gspca_dbg(gspca_dev, D_PROBE, "CPIA PnP-ID: %04x:%04x:%04x", + sd->params.pnpID.vendor, sd->params.pnpID.product, + sd->params.pnpID.deviceRevision); + gspca_dbg(gspca_dev, 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 000000000..671b205c0 --- /dev/null +++ b/drivers/media/usb/gspca/dtcs033.c @@ -0,0 +1,430 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Subdriver for Scopium astro-camera (DTCS033, 0547:7303) + * + * Copyright (C) 2014 Robert Butora (robert.butora.fi@gmail.com) + */ + +#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) { + + gspca_err(gspca_dev, "usb error request no: %d / %d\n", + i, n_reqs); + } else if (preq->bRequestType & USB_DIR_IN) { + + gspca_dbg(gspca_dev, D_STREAM, + "USB IN (%d) returned[%d] %3ph %s", + i, + preq->wLength, + gspca_dev->usb_buf, + 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) + gspca_err(gspca_dev, "usb error in setexposure(gain) sequence\n"); + + reg_rw(gspca_dev, + bRequestType, bRequest, (xtimeVal<<4), 0x6300, 0); + if (gspca_dev->usb_err < 0) + gspca_err(gspca_dev, "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) { + gspca_err(gspca_dev, "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 000000000..c99fd9a1d --- /dev/null +++ b/drivers/media/usb/gspca/etoms.c @@ -0,0 +1,782 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Etoms Et61x151 GPL Linux driver by Michel Xhaard (09/09/2004) + * + * V4L2 by Jean-Francois Moine <http://moinejf.free.fr> + */ + +#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) { + gspca_err(gspca_dev, "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); + gspca_dbg(gspca_dev, D_USBI, "reg read [%02x] -> %02x ..\n", + 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; + } + gspca_dbg(gspca_dev, D_USBO, "reg write [%02x] = %02x..\n", + 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) + gspca_err(gspca_dev, "timeout video on/off\n"); + return ret; +} + +static void Et_init2(struct gspca_dev *gspca_dev) +{ + __u8 value; + static const __u8 FormLine[] = { 0x84, 0x03, 0x14, 0xf4, 0x01, 0x05 }; + + gspca_dbg(gspca_dev, D_STREAM, "Open Init2 ET\n"); + 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); + } +} + +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 ?? */ + + gspca_dbg(gspca_dev, D_STREAM, "Open Init1 ET\n\n"); + 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; + gspca_dbg(gspca_dev, D_STREAM, "Open mode %d Compression %d\n", + 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); + gspca_dbg(gspca_dev, D_CONF, "Etoms gain G %d\n", + 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); + gspca_dbg(gspca_dev, D_FRAM, "Etoms luma G %d\n", luma); + if (luma < luma_mean - luma_delta || luma > luma_mean + luma_delta) { + Gbright += (luma_mean - luma) >> spring; + Gbright = BLIMIT(Gbright); + gspca_dbg(gspca_dev, D_FRAM, "Etoms Gbright %d\n", 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) { + gspca_dbg(gspca_dev, D_FRAM, + "header packet found datalength %d !!\n", len); + gspca_dbg(gspca_dev, 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 000000000..bc6133b52 --- /dev/null +++ b/drivers/media/usb/gspca/finepix.c @@ -0,0 +1,293 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Fujifilm Finepix subdriver + * + * Copyright (C) 2008 Frank Zago + */ + +#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; +}; + +/* 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; + + gspca_dbg(gspca_dev, D_STREAM, "dostream started\n"); + + /* 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 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: + gspca_dbg(gspca_dev, D_STREAM, "dostream stopped\n"); +} + +/* 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); + + schedule_work(&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); + flush_work(&dev->work_struct); + mutex_lock(&gspca_dev->usb_lock); +} + +/* 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 000000000..e5a35ca72 --- /dev/null +++ b/drivers/media/usb/gspca/gl860/Kconfig @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0-only +config USB_GL860 + tristate "GL860 USB Camera Driver" + depends on VIDEO_DEV && 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 000000000..7bcfa36f1 --- /dev/null +++ b/drivers/media/usb/gspca/gl860/Makefile @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0 +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 000000000..d6a540ed3 --- /dev/null +++ b/drivers/media/usb/gspca/gl860/gl860-mi1320.c @@ -0,0 +1,551 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* Subdriver for the GL860 chip with the MI1320 sensor + * Author Olivier LORIN from own logs + */ + +/* 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[] = { + (u8[]){ + 0x0d, 0x80, 0xf1, 0x08, 0x03, 0x04, 0xf1, 0x00, + 0x04, 0x05, 0xf1, 0x02, 0x05, 0x00, 0xf1, 0xf1, + 0x06, 0x00, 0xf1, 0x0d, 0x20, 0x01, 0xf1, 0x00, + 0x21, 0x84, 0xf1, 0x00, 0x0d, 0x00, 0xf1, 0x08, + 0xf0, 0x00, 0xf1, 0x01, 0x34, 0x00, 0xf1, 0x00, + 0x9b, 0x43, 0xf1, 0x00, 0xa6, 0x05, 0xf1, 0x00, + 0xa9, 0x04, 0xf1, 0x00, 0xa1, 0x05, 0xf1, 0x00, + 0xa4, 0x04, 0xf1, 0x00, 0xae, 0x0a, 0xf1, 0x08 + }, (u8[]){ + 0xf0, 0x00, 0xf1, 0x02, 0x3a, 0x05, 0xf1, 0xf1, + 0x3c, 0x05, 0xf1, 0xf1, 0x59, 0x01, 0xf1, 0x47, + 0x5a, 0x01, 0xf1, 0x88, 0x5c, 0x0a, 0xf1, 0x06, + 0x5d, 0x0e, 0xf1, 0x0a, 0x64, 0x5e, 0xf1, 0x1c, + 0xd2, 0x00, 0xf1, 0xcf, 0xcb, 0x00, 0xf1, 0x01 + }, (u8[]){ + 0xd3, 0x02, 0xd4, 0x28, 0xd5, 0x01, 0xd0, 0x02, + 0xd1, 0x18, 0xd2, 0xc1 + } +}; + +static u8 *tbl_800[] = { + (u8[]){ + 0x0d, 0x80, 0xf1, 0x08, 0x03, 0x03, 0xf1, 0xc0, + 0x04, 0x05, 0xf1, 0x02, 0x05, 0x00, 0xf1, 0xf1, + 0x06, 0x00, 0xf1, 0x0d, 0x20, 0x01, 0xf1, 0x00, + 0x21, 0x84, 0xf1, 0x00, 0x0d, 0x00, 0xf1, 0x08, + 0xf0, 0x00, 0xf1, 0x01, 0x34, 0x00, 0xf1, 0x00, + 0x9b, 0x43, 0xf1, 0x00, 0xa6, 0x05, 0xf1, 0x00, + 0xa9, 0x03, 0xf1, 0xc0, 0xa1, 0x03, 0xf1, 0x20, + 0xa4, 0x02, 0xf1, 0x5a, 0xae, 0x0a, 0xf1, 0x08 + }, (u8[]){ + 0xf0, 0x00, 0xf1, 0x02, 0x3a, 0x05, 0xf1, 0xf1, + 0x3c, 0x05, 0xf1, 0xf1, 0x59, 0x01, 0xf1, 0x47, + 0x5a, 0x01, 0xf1, 0x88, 0x5c, 0x0a, 0xf1, 0x06, + 0x5d, 0x0e, 0xf1, 0x0a, 0x64, 0x5e, 0xf1, 0x1c, + 0xd2, 0x00, 0xf1, 0xcf, 0xcb, 0x00, 0xf1, 0x01 + }, (u8[]){ + 0xd3, 0x02, 0xd4, 0x18, 0xd5, 0x21, 0xd0, 0x02, + 0xd1, 0x10, 0xd2, 0x59 + } +}; + +static u8 *tbl_640[] = { + (u8[]){ + 0x0d, 0x80, 0xf1, 0x08, 0x03, 0x04, 0xf1, 0x04, + 0x04, 0x05, 0xf1, 0x02, 0x07, 0x01, 0xf1, 0x7c, + 0x08, 0x00, 0xf1, 0x0e, 0x21, 0x80, 0xf1, 0x00, + 0x0d, 0x00, 0xf1, 0x08, 0xf0, 0x00, 0xf1, 0x01, + 0x34, 0x10, 0xf1, 0x10, 0x3a, 0x43, 0xf1, 0x00, + 0xa6, 0x05, 0xf1, 0x02, 0xa9, 0x04, 0xf1, 0x04, + 0xa7, 0x02, 0xf1, 0x81, 0xaa, 0x01, 0xf1, 0xe2, + 0xae, 0x0c, 0xf1, 0x09 + }, (u8[]){ + 0xf0, 0x00, 0xf1, 0x02, 0x39, 0x03, 0xf1, 0xfc, + 0x3b, 0x04, 0xf1, 0x04, 0x57, 0x01, 0xf1, 0xb6, + 0x58, 0x02, 0xf1, 0x0d, 0x5c, 0x1f, 0xf1, 0x19, + 0x5d, 0x24, 0xf1, 0x1e, 0x64, 0x5e, 0xf1, 0x1c, + 0xd2, 0x00, 0xf1, 0x00, 0xcb, 0x00, 0xf1, 0x01 + }, (u8[]){ + 0xd3, 0x02, 0xd4, 0x10, 0xd5, 0x81, 0xd0, 0x02, + 0xd1, 0x08, 0xd2, 0xe1 + } +}; + +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 000000000..9e7a29154 --- /dev/null +++ b/drivers/media/usb/gspca/gl860/gl860-mi2020.c @@ -0,0 +1,793 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* 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. + */ + +/* 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 000000000..928c1eaf9 --- /dev/null +++ b/drivers/media/usb/gspca/gl860/gl860-ov2640.c @@ -0,0 +1,477 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* Subdriver for the GL860 chip with the OV2640 sensor + * Author Olivier LORIN, from Malmostoso's logs + */ + +/* 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 000000000..766677ebc --- /dev/null +++ b/drivers/media/usb/gspca/gl860/gl860-ov9655.c @@ -0,0 +1,373 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* Subdriver for the GL860 chip with the OV9655 sensor + * Author Olivier LORIN, from logs done by Simon (Sur3) and Almighurt + * on dsd's weblog + */ + +/* 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[] = { + (u8[]){ + 0x00, 0x40, 0x07, 0x6a, 0x06, 0xf3, 0x0d, 0x6a, + 0x10, 0x10, 0xc1, 0x01 + }, (u8[]){ + 0x12, 0x80, 0x00, 0x00, 0x01, 0x98, 0x02, 0x80, + 0x03, 0x12, 0x04, 0x03, 0x0b, 0x57, 0x0e, 0x61, + 0x0f, 0x42, 0x11, 0x01, 0x12, 0x60, 0x13, 0x00, + 0x14, 0x3a, 0x16, 0x24, 0x17, 0x14, 0x18, 0x00, + 0x19, 0x01, 0x1a, 0x3d, 0x1e, 0x04, 0x24, 0x3c, + 0x25, 0x36, 0x26, 0x72, 0x27, 0x08, 0x28, 0x08, + 0x29, 0x15, 0x2a, 0x00, 0x2b, 0x00, 0x2c, 0x08 + }, (u8[]){ + 0x32, 0xff, 0x33, 0x00, 0x34, 0x3d, 0x35, 0x00, + 0x36, 0xfa, 0x38, 0x72, 0x39, 0x57, 0x3a, 0x00, + 0x3b, 0x0c, 0x3d, 0x99, 0x3e, 0x0c, 0x3f, 0xc1, + 0x40, 0xc0, 0x41, 0x00, 0x42, 0xc0, 0x43, 0x0a, + 0x44, 0xf0, 0x45, 0x46, 0x46, 0x62, 0x47, 0x2a, + 0x48, 0x3c, 0x4a, 0xee, 0x4b, 0xe7, 0x4c, 0xe7, + 0x4d, 0xe7, 0x4e, 0xe7 + }, (u8[]){ + 0x4f, 0x98, 0x50, 0x98, 0x51, 0x00, 0x52, 0x28, + 0x53, 0x70, 0x54, 0x98, 0x58, 0x1a, 0x59, 0x85, + 0x5a, 0xa9, 0x5b, 0x64, 0x5c, 0x84, 0x5d, 0x53, + 0x5e, 0x0e, 0x5f, 0xf0, 0x60, 0xf0, 0x61, 0xf0, + 0x62, 0x00, 0x63, 0x00, 0x64, 0x02, 0x65, 0x20, + 0x66, 0x00, 0x69, 0x0a, 0x6b, 0x5a, 0x6c, 0x04, + 0x6d, 0x55, 0x6e, 0x00, 0x6f, 0x9d + }, (u8[]){ + 0x70, 0x15, 0x71, 0x78, 0x72, 0x00, 0x73, 0x00, + 0x74, 0x3a, 0x75, 0x35, 0x76, 0x01, 0x77, 0x02, + 0x7a, 0x24, 0x7b, 0x04, 0x7c, 0x07, 0x7d, 0x10, + 0x7e, 0x28, 0x7f, 0x36, 0x80, 0x44, 0x81, 0x52, + 0x82, 0x60, 0x83, 0x6c, 0x84, 0x78, 0x85, 0x8c, + 0x86, 0x9e, 0x87, 0xbb, 0x88, 0xd2, 0x89, 0xe5, + 0x8a, 0x23, 0x8c, 0x8d, 0x90, 0x7c, 0x91, 0x7b + }, (u8[]){ + 0x9d, 0x02, 0x9e, 0x02, 0x9f, 0x74, 0xa0, 0x73, + 0xa1, 0x40, 0xa4, 0x50, 0xa5, 0x68, 0xa6, 0x70, + 0xa8, 0xc1, 0xa9, 0xef, 0xaa, 0x92, 0xab, 0x04, + 0xac, 0x80, 0xad, 0x80, 0xae, 0x80, 0xaf, 0x80, + 0xb2, 0xf2, 0xb3, 0x20, 0xb4, 0x20, 0xb5, 0x00, + 0xb6, 0xaf + }, (u8[]){ + 0xbb, 0xae, 0xbc, 0x4f, 0xbd, 0x4e, 0xbe, 0x6a, + 0xbf, 0x68, 0xc0, 0xaa, 0xc1, 0xc0, 0xc2, 0x01, + 0xc3, 0x4e, 0xc6, 0x85, 0xc7, 0x81, 0xc9, 0xe0, + 0xca, 0xe8, 0xcb, 0xf0, 0xcc, 0xd8, 0xcd, 0x93 + }, (u8[]){ + 0xd0, 0x01, 0xd1, 0x08, 0xd2, 0xe0, 0xd3, 0x01, + 0xd4, 0x10, 0xd5, 0x80 + } +}; + +static u8 *tbl_1280[] = { + (u8[]){ + 0x00, 0x40, 0x07, 0x6a, 0x06, 0xf3, 0x0d, 0x6a, + 0x10, 0x10, 0xc1, 0x01 + }, + (u8[]){ + 0x12, 0x80, 0x00, 0x00, 0x01, 0x98, 0x02, 0x80, + 0x03, 0x12, 0x04, 0x01, 0x0b, 0x57, 0x0e, 0x61, + 0x0f, 0x42, 0x11, 0x00, 0x12, 0x00, 0x13, 0x00, + 0x14, 0x3a, 0x16, 0x24, 0x17, 0x1b, 0x18, 0xbb, + 0x19, 0x01, 0x1a, 0x81, 0x1e, 0x04, 0x24, 0x3c, + 0x25, 0x36, 0x26, 0x72, 0x27, 0x08, 0x28, 0x08, + 0x29, 0x15, 0x2a, 0x00, 0x2b, 0x00, 0x2c, 0x08 + }, + (u8[]){ + 0x32, 0xa4, 0x33, 0x00, 0x34, 0x3d, 0x35, 0x00, + 0x36, 0xf8, 0x38, 0x72, 0x39, 0x57, 0x3a, 0x00, + 0x3b, 0x0c, 0x3d, 0x99, 0x3e, 0x0c, 0x3f, 0xc2, + 0x40, 0xc0, 0x41, 0x00, 0x42, 0xc0, 0x43, 0x0a, + 0x44, 0xf0, 0x45, 0x46, 0x46, 0x62, 0x47, 0x2a, + 0x48, 0x3c, 0x4a, 0xec, 0x4b, 0xe8, 0x4c, 0xe8, + 0x4d, 0xe8, 0x4e, 0xe8 + }, + (u8[]){ + 0x4f, 0x98, 0x50, 0x98, 0x51, 0x00, 0x52, 0x28, + 0x53, 0x70, 0x54, 0x98, 0x58, 0x1a, 0x59, 0x85, + 0x5a, 0xa9, 0x5b, 0x64, 0x5c, 0x84, 0x5d, 0x53, + 0x5e, 0x0e, 0x5f, 0xf0, 0x60, 0xf0, 0x61, 0xf0, + 0x62, 0x00, 0x63, 0x00, 0x64, 0x02, 0x65, 0x20, + 0x66, 0x00, 0x69, 0x02, 0x6b, 0x5a, 0x6c, 0x04, + 0x6d, 0x55, 0x6e, 0x00, 0x6f, 0x9d + }, + (u8[]){ + 0x70, 0x08, 0x71, 0x78, 0x72, 0x00, 0x73, 0x01, + 0x74, 0x3a, 0x75, 0x35, 0x76, 0x01, 0x77, 0x02, + 0x7a, 0x24, 0x7b, 0x04, 0x7c, 0x07, 0x7d, 0x10, + 0x7e, 0x28, 0x7f, 0x36, 0x80, 0x44, 0x81, 0x52, + 0x82, 0x60, 0x83, 0x6c, 0x84, 0x78, 0x85, 0x8c, + 0x86, 0x9e, 0x87, 0xbb, 0x88, 0xd2, 0x89, 0xe5, + 0x8a, 0x23, 0x8c, 0x0d, 0x90, 0x90, 0x91, 0x90 + }, + (u8[]){ + 0x9d, 0x02, 0x9e, 0x02, 0x9f, 0x94, 0xa0, 0x94, + 0xa1, 0x01, 0xa4, 0x50, 0xa5, 0x68, 0xa6, 0x70, + 0xa8, 0xc1, 0xa9, 0xef, 0xaa, 0x92, 0xab, 0x04, + 0xac, 0x80, 0xad, 0x80, 0xae, 0x80, 0xaf, 0x80, + 0xb2, 0xf2, 0xb3, 0x20, 0xb4, 0x20, 0xb5, 0x00, + 0xb6, 0xaf + }, + (u8[]){ + 0xbb, 0xae, 0xbc, 0x38, 0xbd, 0x39, 0xbe, 0x01, + 0xbf, 0x01, 0xc0, 0xe2, 0xc1, 0xc0, 0xc2, 0x01, + 0xc3, 0x4e, 0xc6, 0x85, 0xc7, 0x81, 0xc9, 0xe0, + 0xca, 0xe8, 0xcb, 0xf0, 0xcc, 0xd8, 0xcd, 0x93 + }, + (u8[]){ + 0xd0, 0x21, 0xd1, 0x18, 0xd2, 0xe0, 0xd3, 0x01, + 0xd4, 0x28, 0xd5, 0x00 + } +}; + +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 000000000..ce4ee8bc7 --- /dev/null +++ b/drivers/media/usb/gspca/gl860/gl860.c @@ -0,0 +1,730 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* 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! + */ + +#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 { + gspca_err(gspca_dev, "zero-length read request\n"); + r = -EINVAL; + } + } + + 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) + gspca_err(gspca_dev, "short ctrl transfer %d/%d\n", 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); + + gspca_dbg(gspca_dev, D_PROBE, "probing for sensor MI2020 or OVXXXX\n"); + 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); + gspca_dbg(gspca_dev, D_PROBE, "probe=0x%02x\n", probe); + if (probe == 0xff) + nOV++; + } + + if (nOV) { + gspca_dbg(gspca_dev, D_PROBE, "0xff -> OVXXXX\n"); + gspca_dbg(gspca_dev, 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) { + gspca_dbg(gspca_dev, D_PROBE, + "probe=0x%02x -> OV2640\n", + probe); + sd->sensor = ID_OV2640; + nb26 += 4; + break; + } + if (probe == 0x96 || probe == 0x55) { + gspca_dbg(gspca_dev, D_PROBE, + "probe=0x%02x -> OV9655\n", + probe); + sd->sensor = ID_OV9655; + nb96 += 4; + break; + } + gspca_dbg(gspca_dev, D_PROBE, "probe=0x%02x\n", + probe); + if (probe == 0x00) + nb26++; + if (probe == 0xff) + nb96++; + msleep(3); + } + if (nb26 < 4 && nb96 < 4) + return -1; + } else { + gspca_dbg(gspca_dev, D_PROBE, "Not any 0xff -> MI2020\n"); + sd->sensor = ID_MI2020; + } + } + + if (_MI1320_) { + gspca_dbg(gspca_dev, D_PROBE, "05e3:f191 sensor MI1320 (1.3M)\n"); + } else if (_MI2020_) { + gspca_dbg(gspca_dev, D_PROBE, "05e3:0503 sensor MI2020 (2.0M)\n"); + } else if (_OV9655_) { + gspca_dbg(gspca_dev, D_PROBE, "05e3:0503 sensor OV9655 (1.3M)\n"); + } else if (_OV2640_) { + gspca_dbg(gspca_dev, D_PROBE, "05e3:0503 sensor OV2640 (2.0M)\n"); + } else { + gspca_dbg(gspca_dev, D_PROBE, "***** Unknown sensor *****\n"); + 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 000000000..a8a5d6450 --- /dev/null +++ b/drivers/media/usb/gspca/gl860/gl860.h @@ -0,0 +1,93 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* GSPCA subdrivers for Genesys Logic webcams with the GL860 chip + * Subdriver declarations + * + * 2009/10/14 Olivier LORIN <o.lorin@laposte.net> + */ +#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 000000000..770714c34 --- /dev/null +++ b/drivers/media/usb/gspca/gspca.c @@ -0,0 +1,1721 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * 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> + */ + +#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') { + gspca_dbg(gspca_dev, debug, "%s %c%c%c%c %dx%d\n", + txt, + pixfmt & 0xff, + (pixfmt >> 8) & 0xff, + (pixfmt >> 16) & 0xff, + pixfmt >> 24, + w, h); + } else { + gspca_dbg(gspca_dev, debug, "%s 0x%08x %dx%d\n", + 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 + +/* + * 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) { + gspca_err(gspca_dev, "Unknown packet received\n"); + } + 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: + gspca_err(gspca_dev, "URB error %i, resubmitting\n", + 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; + gspca_dbg(gspca_dev, D_CONF, "found int in endpoint: 0x%x, buffer_len=%u, interval=%u\n", + 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) { + gspca_err(gspca_dev, "submit int URB failed with error %i\n", + 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 + gspca_err(gspca_dev, "urb status: %d\n", 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) { + gspca_dbg(gspca_dev, D_PACK, "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 */ + gspca_dbg(gspca_dev, D_PACK, "packet [%d] o:%d l:%d\n", + 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: + if (!gspca_dev->streaming) + return; + /* 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; + + gspca_dbg(gspca_dev, D_PACK, "isoc irq\n"); + 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; + + gspca_dbg(gspca_dev, D_PACK, "bulk irq\n"); + 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 + gspca_err(gspca_dev, "urb status: %d\n", urb->status); + urb->status = 0; + goto resubmit; + } + + gspca_dbg(gspca_dev, D_PACK, "packet l:%d\n", urb->actual_length); + gspca_dev->sd_desc->pkt_scan(gspca_dev, + urb->transfer_buffer, + urb->actual_length); + +resubmit: + if (!gspca_dev->streaming) + return; + /* 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_buffer *buf; + unsigned long flags; + + gspca_dbg(gspca_dev, D_PACK, "add t:%d l:%d\n", packet_type, len); + + spin_lock_irqsave(&gspca_dev->qlock, flags); + buf = list_first_entry_or_null(&gspca_dev->buf_list, + typeof(*buf), list); + spin_unlock_irqrestore(&gspca_dev->qlock, flags); + + if (packet_type == FIRST_PACKET) { + /* if there is no queued buffer, discard the whole frame */ + if (!buf) { + gspca_dev->last_packet_type = DISCARD_PACKET; + gspca_dev->sequence++; + return; + } + gspca_dev->image = vb2_plane_vaddr(&buf->vb.vb2_buf, 0); + 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 > PAGE_ALIGN(gspca_dev->pixfmt.sizeimage)) { + gspca_err(gspca_dev, "frame overflow %d > %d\n", + gspca_dev->image_len + len, + PAGE_ALIGN(gspca_dev->pixfmt.sizeimage)); + 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) { + if (gspca_dev->image_len > gspca_dev->pixfmt.sizeimage) + gspca_dev->image_len = gspca_dev->pixfmt.sizeimage; + spin_lock_irqsave(&gspca_dev->qlock, flags); + list_del(&buf->list); + spin_unlock_irqrestore(&gspca_dev->qlock, flags); + buf->vb.vb2_buf.timestamp = ktime_get_ns(); + vb2_set_plane_payload(&buf->vb.vb2_buf, 0, + gspca_dev->image_len); + buf->vb.sequence = gspca_dev->sequence++; + buf->vb.field = V4L2_FIELD_NONE; + gspca_dbg(gspca_dev, D_FRAM, "frame complete len:%d\n", + gspca_dev->image_len); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); + gspca_dev->image = NULL; + gspca_dev->image_len = 0; + } +} +EXPORT_SYMBOL(gspca_frame_add); + +static void destroy_urbs(struct gspca_dev *gspca_dev) +{ + struct urb *urb; + unsigned int i; + + gspca_dbg(gspca_dev, D_STREAM, "kill transfer\n"); + + /* Killing all URBs guarantee that no URB completion + * handler is running. Therefore, there shouldn't + * be anyone trying to access gspca_dev->urb[i] + */ + for (i = 0; i < MAX_NURBS; i++) + usb_kill_urb(gspca_dev->urb[i]); + + gspca_dbg(gspca_dev, D_STREAM, "releasing urbs\n"); + for (i = 0; i < MAX_NURBS; i++) { + urb = gspca_dev->urb[i]; + if (!urb) + continue; + gspca_dev->urb[i] = NULL; + 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; +} + +/* + * 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 */ + } + + gspca_dbg(gspca_dev, D_STREAM, "min bandwidth: %d\n", 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; + gspca_dbg(gspca_dev, D_STREAM, "alt %d bandwidth %d\n", + 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) { + gspca_dbg(gspca_dev, D_STREAM, "dev has usb audio, skipping highest alt\n"); + 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; + gspca_dbg(gspca_dev, D_STREAM, + "isoc %d pkts size %d = bsize:%d\n", + npkt, psize, bsize); + nurbs = DEF_NURBS; + } else { /* bulk */ + npkt = 0; + bsize = gspca_dev->cam.bulk_size; + if (bsize == 0) + bsize = psize; + gspca_dbg(gspca_dev, D_STREAM, "bulk bsize:%d\n", 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) + 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; +} + +/* 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 = false; + 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); + if (gspca_dev->present) + gspca_input_create_urb(gspca_dev); + if (gspca_dev->sd_desc->stop0) + gspca_dev->sd_desc->stop0(gspca_dev); + gspca_dbg(gspca_dev, D_STREAM, "stream off OK\n"); +} + +/* + * 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->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) { + gspca_dbg(gspca_dev, D_STREAM, "init transfer alt %d\n", + 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; + } + v4l2_ctrl_handler_setup(gspca_dev->vdev.ctrl_handler); + gspca_dev->streaming = true; + + /* 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: + gspca_err(gspca_dev, "alt %d - bandwidth not wide enough, trying again\n", + 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, u32 pixelformat) +{ + int i; + + for (i = 0; i < gspca_dev->cam.nmodes; i++) { + if (width == gspca_dev->cam.cam_mode[i].width + && height == gspca_dev->cam.cam_mode[i].height + && pixelformat == gspca_dev->cam.cam_mode[i].pixelformat) + return i; + } + return -EINVAL; +} + +static int wxh_to_nearest_mode(struct gspca_dev *gspca_dev, + int width, int height, u32 pixelformat) +{ + 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 + && pixelformat == gspca_dev->cam.cam_mode[i].pixelformat) + return 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; + 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]; + 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); + u32 priv = fmt->fmt.pix.priv; + + fmt->fmt.pix = gspca_dev->pixfmt; + /* some drivers use priv internally, so keep the original value */ + fmt->fmt.pix.priv = priv; + 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 nearest mode for width and height */ + mode = wxh_to_nearest_mode(gspca_dev, w, h, fmt->fmt.pix.pixelformat); + + /* 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); + } + 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); + u32 priv = fmt->fmt.pix.priv; + + if (try_fmt_vid_cap(gspca_dev, fmt) < 0) + return -EINVAL; + /* some drivers use priv internally, so keep the original value */ + fmt->fmt.pix.priv = priv; + 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); + u32 priv = fmt->fmt.pix.priv; + int mode; + + if (vb2_is_busy(&gspca_dev->queue)) + return -EBUSY; + + mode = try_fmt_vid_cap(gspca_dev, fmt); + if (mode < 0) + return -EINVAL; + + gspca_dev->curr_mode = mode; + 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[mode]; + /* some drivers use priv internally, so keep the original value */ + fmt->fmt.pix.priv = priv; + return 0; +} + +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; + __u32 i; + + mode = wxh_to_mode(gspca_dev, fival->width, fival->height, + fival->pixel_format); + if (mode < 0) + return -EINVAL; + + 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_FRMIVAL_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 vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct gspca_dev *gspca_dev = video_drvdata(file); + + strscpy((char *)cap->driver, gspca_dev->sd_desc->name, + sizeof(cap->driver)); + if (gspca_dev->dev->product != NULL) { + strscpy((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)); + 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; + strscpy(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_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->queue.min_buffers_needed; + + if (!gspca_dev->sd_desc->get_streamparm) + return 0; + + parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; + gspca_dev->usb_err = 0; + gspca_dev->sd_desc->get_streamparm(gspca_dev, parm); + return gspca_dev->usb_err; +} + +static int vidioc_s_parm(struct file *filp, void *priv, + struct v4l2_streamparm *parm) +{ + struct gspca_dev *gspca_dev = video_drvdata(filp); + + parm->parm.capture.readbuffers = gspca_dev->queue.min_buffers_needed; + + if (!gspca_dev->sd_desc->set_streamparm) { + parm->parm.capture.capability = 0; + return 0; + } + + parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; + gspca_dev->usb_err = 0; + gspca_dev->sd_desc->set_streamparm(gspca_dev, parm); + return gspca_dev->usb_err; +} + +static int gspca_queue_setup(struct vb2_queue *vq, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], struct device *alloc_devs[]) +{ + struct gspca_dev *gspca_dev = vb2_get_drv_priv(vq); + unsigned int size = PAGE_ALIGN(gspca_dev->pixfmt.sizeimage); + + if (*nplanes) + return sizes[0] < size ? -EINVAL : 0; + *nplanes = 1; + sizes[0] = size; + return 0; +} + +static int gspca_buffer_prepare(struct vb2_buffer *vb) +{ + struct gspca_dev *gspca_dev = vb2_get_drv_priv(vb->vb2_queue); + unsigned long size = PAGE_ALIGN(gspca_dev->pixfmt.sizeimage); + + if (vb2_plane_size(vb, 0) < size) { + gspca_err(gspca_dev, "buffer too small (%lu < %lu)\n", + vb2_plane_size(vb, 0), size); + return -EINVAL; + } + return 0; +} + +static void gspca_buffer_finish(struct vb2_buffer *vb) +{ + struct gspca_dev *gspca_dev = vb2_get_drv_priv(vb->vb2_queue); + + if (!gspca_dev->sd_desc->dq_callback) + return; + + gspca_dev->usb_err = 0; + if (gspca_dev->present) + gspca_dev->sd_desc->dq_callback(gspca_dev); +} + +static void gspca_buffer_queue(struct vb2_buffer *vb) +{ + struct gspca_dev *gspca_dev = vb2_get_drv_priv(vb->vb2_queue); + struct gspca_buffer *buf = to_gspca_buffer(vb); + unsigned long flags; + + spin_lock_irqsave(&gspca_dev->qlock, flags); + list_add_tail(&buf->list, &gspca_dev->buf_list); + spin_unlock_irqrestore(&gspca_dev->qlock, flags); +} + +static void gspca_return_all_buffers(struct gspca_dev *gspca_dev, + enum vb2_buffer_state state) +{ + struct gspca_buffer *buf, *node; + unsigned long flags; + + spin_lock_irqsave(&gspca_dev->qlock, flags); + list_for_each_entry_safe(buf, node, &gspca_dev->buf_list, list) { + vb2_buffer_done(&buf->vb.vb2_buf, state); + list_del(&buf->list); + } + spin_unlock_irqrestore(&gspca_dev->qlock, flags); +} + +static int gspca_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct gspca_dev *gspca_dev = vb2_get_drv_priv(vq); + int ret; + + gspca_dev->sequence = 0; + + ret = gspca_init_transfer(gspca_dev); + if (ret) + gspca_return_all_buffers(gspca_dev, VB2_BUF_STATE_QUEUED); + return ret; +} + +static void gspca_stop_streaming(struct vb2_queue *vq) +{ + struct gspca_dev *gspca_dev = vb2_get_drv_priv(vq); + + gspca_stream_off(gspca_dev); + + /* Release all active buffers */ + gspca_return_all_buffers(gspca_dev, VB2_BUF_STATE_ERROR); +} + +static const struct vb2_ops gspca_qops = { + .queue_setup = gspca_queue_setup, + .buf_prepare = gspca_buffer_prepare, + .buf_finish = gspca_buffer_finish, + .buf_queue = gspca_buffer_queue, + .start_streaming = gspca_start_streaming, + .stop_streaming = gspca_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static const struct v4l2_file_operations dev_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .unlocked_ioctl = video_ioctl2, + .read = vb2_fop_read, + .mmap = vb2_fop_mmap, + .poll = vb2_fop_poll, +}; + +static const struct v4l2_ioctl_ops dev_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .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_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .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, + + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + +#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); + struct vb2_queue *q; + 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 = kzalloc(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->present = true; + gspca_dev->sd_desc = sd_desc; + 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; + gspca_dev->vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; + video_set_drvdata(&gspca_dev->vdev, gspca_dev); + gspca_dev->module = module; + + mutex_init(&gspca_dev->usb_lock); + gspca_dev->vdev.lock = &gspca_dev->usb_lock; + init_waitqueue_head(&gspca_dev->wq); + + /* Initialize the vb2 queue */ + q = &gspca_dev->queue; + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ; + q->drv_priv = gspca_dev; + q->buf_struct_size = sizeof(struct gspca_buffer); + q->ops = &gspca_qops; + q->mem_ops = &vb2_vmalloc_memops; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->min_buffers_needed = 2; + q->lock = &gspca_dev->usb_lock; + ret = vb2_queue_init(q); + if (ret) + goto out; + gspca_dev->vdev.queue = q; + + INIT_LIST_HEAD(&gspca_dev->buf_list); + spin_lock_init(&gspca_dev->qlock); + + /* 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; + +#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_VIDEO, + -1); + if (ret < 0) { + pr_err("video_register_device err %d\n", ret); + goto out; + } + + usb_set_intfdata(intf, gspca_dev); + gspca_dbg(gspca_dev, D_PROBE, "%s created\n", + 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); + v4l2_device_unregister(&gspca_dev->v4l2_dev); + if (sd_desc->probe_error) + sd_desc->probe_error(gspca_dev); + 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 + + gspca_dbg(gspca_dev, D_PROBE, "%s disconnect\n", + video_device_node_name(&gspca_dev->vdev)); + + mutex_lock(&gspca_dev->usb_lock); + gspca_dev->present = false; + destroy_urbs(gspca_dev); + gspca_input_destroy_urb(gspca_dev); + + vb2_queue_error(&gspca_dev->queue); + +#if IS_ENABLED(CONFIG_INPUT) + input_dev = gspca_dev->input_dev; + if (input_dev) { + gspca_dev->input_dev = NULL; + input_unregister_device(input_dev); + } +#endif + + 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 (!vb2_start_streaming_called(&gspca_dev->queue)) + 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 = vb2_start_streaming_called(&gspca_dev->queue); + 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 000000000..a6554d5e9 --- /dev/null +++ b/drivers/media/usb/gspca/gspca.h @@ -0,0 +1,243 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#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 <media/videobuf2-v4l2.h> +#include <media/videobuf2-vmalloc.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 gspca_dbg(gspca_dev, level, fmt, ...) \ + v4l2_dbg(level, gspca_debug, &(gspca_dev)->v4l2_dev, \ + fmt, ##__VA_ARGS__) + +#define gspca_err(gspca_dev, 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_v_op probe_error; /* called if probe failed, do cleanup here */ + 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_buffer { + struct vb2_v4l2_buffer vb; + struct list_head list; +}; + +static inline struct gspca_buffer *to_gspca_buffer(struct vb2_buffer *vb2) +{ + return container_of(vb2, struct gspca_buffer, vb.vb2_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; + +#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 *image; /* image being filled */ + u32 image_len; /* current length of image */ + __u8 last_packet_type; + __s8 empty_packet; /* if (-1) don't check empty packets */ + bool streaming; + + __u8 curr_mode; /* current camera mode */ + struct v4l2_pix_format pixfmt; /* current mode parameters */ + __u32 sequence; /* frame sequence number */ + + struct vb2_queue queue; + + spinlock_t qlock; + struct list_head buf_list; + + wait_queue_head_t wq; /* wait queue */ + struct mutex usb_lock; /* usb exchange protection */ + int usb_err; /* USB error - protected by usb_lock */ + u16 pkt_size; /* ISOC packet size */ +#ifdef CONFIG_PM + char frozen; /* suspend - resume */ +#endif + bool present; + 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 000000000..20b2468d0 --- /dev/null +++ b/drivers/media/usb/gspca/jeilinj.c @@ -0,0 +1,537 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * 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 + */ + +#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) + gspca_err(gspca_dev, "Start streaming command failed\n"); + 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; + + gspca_dbg(gspca_dev, D_STREAM, "Got %d bytes out of %d for Block 0\n", + len, JEILINJ_MAX_TRANSFER); + if (len != JEILINJ_MAX_TRANSFER) { + gspca_dbg(gspca_dev, D_PACK, "bad length\n"); + 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; + gspca_dbg(gspca_dev, D_STREAM, "blocks_left = 0x%x\n", + 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) { + gspca_dbg(gspca_dev, D_STREAM, "%d blocks remaining for frame\n", + 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); + gspca_dbg(gspca_dev, D_STREAM, "Start streaming at %dx%d\n", + 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 000000000..a408fcc3a --- /dev/null +++ b/drivers/media/usb/gspca/jl2005bcd.c @@ -0,0 +1,524 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Jeilin JL2005B/C/D library + * + * Copyright (C) 2011 Theodore Kilgore <kilgota@auburn.edu> + */ + +#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; + 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++; + } + gspca_dbg(gspca_dev, D_FRAM, "frame_brightness is 0x%02x\n", + 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; + static const unsigned char regs_to_read[] = { + 0x57, 0x02, 0x03, 0x5d, 0x5e, 0x5f + }; + + gspca_dbg(gspca_dev, D_PROBE, "Running jl2005c_get_firmware_id\n"); + /* Read the first ID byte once for warmup */ + retval = jl2005c_read_reg(gspca_dev, regs_to_read[0]); + gspca_dbg(gspca_dev, D_PROBE, "response is %02x\n", + 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]; + } + gspca_dbg(gspca_dev, D_PROBE, "firmware ID is %02x%02x%02x%02x%02x%02x\n", + 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) +{ + return jl2005c_write_reg(gspca_dev, 0x07, 0x00); +} + +/* + * 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); + 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); + gspca_dbg(gspca_dev, D_PACK, + "Got %d bytes out of %d for header\n", + 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; + gspca_dbg(gspca_dev, D_PACK, "bytes_left = 0x%x\n", + 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; + gspca_dbg(gspca_dev, D_PACK, + "Got %d bytes out of %d for frame\n", + 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: + gspca_dbg(gspca_dev, D_STREAM, "Start streaming at vga resolution\n"); + jl2005c_stream_start_vga_lg(gspca_dev); + break; + case 320: + gspca_dbg(gspca_dev, D_STREAM, "Start streaming at qvga resolution\n"); + jl2005c_stream_start_vga_small(gspca_dev); + break; + case 352: + gspca_dbg(gspca_dev, D_STREAM, "Start streaming at cif resolution\n"); + jl2005c_stream_start_cif_lg(gspca_dev); + break; + case 176: + gspca_dbg(gspca_dev, D_STREAM, "Start streaming at qcif resolution\n"); + jl2005c_stream_start_cif_small(gspca_dev); + break; + default: + pr_err("Unknown resolution specified\n"); + return -1; + } + + schedule_work(&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 */ + flush_work(&dev->work_struct); + 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 000000000..660081dcb --- /dev/null +++ b/drivers/media/usb/gspca/jpeg.h @@ -0,0 +1,156 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#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) + */ + +/* + * 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 000000000..57ea65863 --- /dev/null +++ b/drivers/media/usb/gspca/kinect.c @@ -0,0 +1,474 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * 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. + */ + +#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)); + gspca_dbg(gspca_dev, D_USBO, "Control cmd=%04x tag=%04x len=%04x: %d\n", + 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); + gspca_dbg(gspca_dev, D_USBO, "Control reply: %d\n", actual_len); + if (actual_len < (int)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); + + gspca_dbg(gspca_dev, D_USBO, "Write Reg 0x%04x <= 0x%02x\n", 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) +{ + gspca_dbg(gspca_dev, D_PROBE, "Kinect Camera device.\n"); + + 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 000000000..53db9a289 --- /dev/null +++ b/drivers/media/usb/gspca/konica.c @@ -0,0 +1,478 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * 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) + */ + +#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; + /* + * Make sure the buffer is zeroed to avoid uninitialized + * values. + */ + memset(gspca_dev->usb_buf, 0, 2); + } +} + +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) + 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; + + gspca_dbg(gspca_dev, D_PACK, "sd isoc irq\n"); + if (!gspca_dev->streaming) + return; + + if (urb->status != 0) { + if (urb->status == -ESHUTDOWN) + return; /* disconnection */ +#ifdef CONFIG_PM + if (gspca_dev->frozen) + return; +#endif + gspca_err(gspca_dev, "urb status: %d\n", 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) { + gspca_err(gspca_dev, "lost sync on frames\n"); + goto resubmit; + } + + if (data_urb->number_of_packets != status_urb->number_of_packets) { + gspca_err(gspca_dev, "no packets does not match, data: %d, status: %d\n", + 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) { + gspca_err(gspca_dev, "pkt %d data-status %d, status-status %d\n", + 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) { + gspca_err(gspca_dev, "bad status packet length %d\n", + 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) + gspca_err(gspca_dev, "usb_submit_urb(data_urb) ret %d\n", + st); + } + st = usb_submit_urb(status_urb, GFP_ATOMIC); + if (st < 0) + gspca_err(gspca_dev, "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 000000000..d616408b6 --- /dev/null +++ b/drivers/media/usb/gspca/m5602/Kconfig @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0-only +config USB_M5602 + tristate "ALi USB m5602 Camera Driver" + depends on VIDEO_DEV && USB_GSPCA + help + Say Y here if you want support for cameras based on the + ALi m5602 connected to various image sensors. + + 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 000000000..95c9db6dc --- /dev/null +++ b/drivers/media/usb/gspca/m5602/Makefile @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0 +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 000000000..4d63a9d24 --- /dev/null +++ b/drivers/media/usb/gspca/m5602/m5602_bridge.h @@ -0,0 +1,161 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * 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 + */ + +#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 + +/*****************************************************************************/ + +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 000000000..852ee6cce --- /dev/null +++ b/drivers/media/usb/gspca/m5602/m5602_core.c @@ -0,0 +1,446 @@ +// SPDX-License-Identifier: GPL-2.0-only + /* + * 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 + */ + +#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); + +/* 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 +}; + +/* A skeleton used for sending messages to the m5602 bridge */ +static const unsigned char bridge_urb_skeleton[] = { + 0x13, 0x00, 0x81, 0x00 +}; + +/* 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]; + + gspca_dbg(gspca_dev, D_CONF, "Reading bridge register 0x%x containing 0x%x\n", + 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; + + gspca_dbg(gspca_dev, D_CONF, "Writing bridge register 0x%x with 0x%x\n", + 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])); + + gspca_dbg(gspca_dev, D_CONF, "Reading sensor register 0x%x containing 0x%x\n", + 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; + gspca_dbg(gspca_dev, D_CONF, "Writing sensor register 0x%x with 0x%x\n", + 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; + + gspca_dbg(gspca_dev, D_CONF, "Initializing ALi m5602 webcam\n"); + /* 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); + + gspca_dbg(gspca_dev, D_STREAM, "Transfer started\n"); + 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) { + gspca_dbg(gspca_dev, D_PACK, "Packet is less than 6 bytes\n"); + return; + } + + /* Frame delimiter: ff xx xx xx ff ff */ + if (data[0] == 0xff && data[4] == 0xff && data[5] == 0xff && + data[2] != sd->frame_id) { + gspca_dbg(gspca_dev, D_FRAM, "Frame delimiter detected\n"); + 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); + + gspca_dbg(gspca_dev, D_FRAM, "Starting new frame %d\n", + 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->pixfmt.sizeimage) { + gspca_dbg(gspca_dev, D_FRAM, "Continuing frame %d copying %d bytes\n", + 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->pixfmt.sizeimage - 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: + gspca_err(gspca_dev, "ALi m5602 webcam failed\n"); + 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 000000000..bf1af6ed9 --- /dev/null +++ b/drivers/media/usb/gspca/m5602/m5602_mt9m111.c @@ -0,0 +1,600 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * 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 + */ + +#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 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}, +}; + +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, err; + 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; + } + + gspca_dbg(gspca_dev, D_PROBE, "Probing for a mt9m111 sensor\n"); + + /* Do the preinit */ + for (i = 0; i < ARRAY_SIZE(preinit_mt9m111); i++) { + if (preinit_mt9m111[i][0] == BRIDGE) { + err = 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]; + err = m5602_write_sensor(sd, + preinit_mt9m111[i][1], data, 2); + } + if (err < 0) + return err; + } + + 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: + gspca_dbg(gspca_dev, D_CONF, "Configuring camera for VGA mode\n"); + break; + + case 320: + gspca_dbg(gspca_dev, D_CONF, "Configuring camera for QVGA mode\n"); + 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; + + gspca_dbg(gspca_dev, D_CONF, "Set hvflip to %d %d\n", + 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); + + gspca_dbg(gspca_dev, D_CONF, "Set auto white balance %d\n", 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; + gspca_dbg(gspca_dev, D_CONF, "tmp=%d, data[1]=%d, data[0]=%d\n", 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; + + gspca_dbg(gspca_dev, D_CONF, "Set green balance %d\n", 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; + + gspca_dbg(gspca_dev, D_CONF, "Set blue balance %d\n", 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; + + gspca_dbg(gspca_dev, D_CONF, "Set red balance %d\n", 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 000000000..480afd4c9 --- /dev/null +++ b/drivers/media/usb/gspca/m5602/m5602_mt9m111.h @@ -0,0 +1,125 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * 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> + */ + +#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, +}; +#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 000000000..fadad530f --- /dev/null +++ b/drivers/media/usb/gspca/m5602/m5602_ov7660.c @@ -0,0 +1,468 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * 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 + */ + +#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 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}, +}; + +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; + + gspca_dbg(gspca_dev, D_CONF, "Setting gain to %d\n", 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; + + gspca_dbg(gspca_dev, D_CONF, "Set auto white balance to %d\n", 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; + + gspca_dbg(gspca_dev, D_CONF, "Set auto gain control to %d\n", 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; + + gspca_dbg(gspca_dev, D_CONF, "Set auto exposure control to %d\n", 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; + + gspca_dbg(gspca_dev, D_CONF, "Set hvflip to %d, %d\n", + 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 000000000..6146e8ef1 --- /dev/null +++ b/drivers/media/usb/gspca/m5602/m5602_ov7660.h @@ -0,0 +1,105 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * 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 + */ + +#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_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, +}; +#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 000000000..82a698052 --- /dev/null +++ b/drivers/media/usb/gspca/m5602/m5602_ov9650.c @@ -0,0 +1,784 @@ +// SPDX-License-Identifier: GPL-2.0-only + +/* + * 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 + */ + +#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); + +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} +}; + +/* 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; + } + + gspca_dbg(gspca_dev, D_PROBE, "Probing for an ov9650 sensor\n"); + + /* 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: + gspca_dbg(gspca_dev, D_CONF, "Configuring camera for VGA mode\n"); + + data = OV9650_VGA_SELECT | OV9650_RGB_SELECT | + OV9650_RAW_RGB_SELECT; + err = m5602_write_sensor(sd, OV9650_COM7, &data, 1); + break; + + case 352: + gspca_dbg(gspca_dev, D_CONF, "Configuring camera for CIF mode\n"); + + data = OV9650_CIF_SELECT | OV9650_RGB_SELECT | + OV9650_RAW_RGB_SELECT; + err = m5602_write_sensor(sd, OV9650_COM7, &data, 1); + break; + + case 320: + gspca_dbg(gspca_dev, D_CONF, "Configuring camera for QVGA mode\n"); + + data = OV9650_QVGA_SELECT | OV9650_RGB_SELECT | + OV9650_RAW_RGB_SELECT; + err = m5602_write_sensor(sd, OV9650_COM7, &data, 1); + break; + + case 176: + gspca_dbg(gspca_dev, D_CONF, "Configuring camera for QCIF mode\n"); + + 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; + + gspca_dbg(gspca_dev, D_CONF, "Set exposure to %d\n", 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; + + gspca_dbg(gspca_dev, D_CONF, "Setting gain to %d\n", 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; + + gspca_dbg(gspca_dev, D_CONF, "Set red gain to %d\n", 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; + + gspca_dbg(gspca_dev, D_CONF, "Set blue gain to %d\n", 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; + + gspca_dbg(gspca_dev, D_CONF, "Set hvflip to %d %d\n", 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; + + gspca_dbg(gspca_dev, D_CONF, "Set auto exposure control to %d\n", 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; + + gspca_dbg(gspca_dev, D_CONF, "Set auto white balance to %d\n", 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; + + gspca_dbg(gspca_dev, D_CONF, "Set auto gain control to %d\n", 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 000000000..929a7568c --- /dev/null +++ b/drivers/media/usb/gspca/m5602/m5602_ov9650.h @@ -0,0 +1,155 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * 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 + */ + +#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, +}; + +#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 000000000..8fd99ceee --- /dev/null +++ b/drivers/media/usb/gspca/m5602/m5602_po1030.c @@ -0,0 +1,623 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * 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 + */ + +#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 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}, +}; + +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; + int err; + 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; + } + + gspca_dbg(gspca_dev, D_PROBE, "Probing for a po1030 sensor\n"); + + /* 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) + err = m5602_write_sensor(sd, preinit_po1030[i][1], + &data, 1); + else + err = m5602_write_bridge(sd, preinit_po1030[i][1], + data); + if (err < 0) + return err; + } + + 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; + + gspca_dbg(gspca_dev, D_CONF, "Set exposure to %d\n", val & 0xffff); + + i2c_data = ((val & 0xff00) >> 8); + gspca_dbg(gspca_dev, D_CONF, "Set exposure to high byte to 0x%x\n", + i2c_data); + + err = m5602_write_sensor(sd, PO1030_INTEGLINES_H, + &i2c_data, 1); + if (err < 0) + return err; + + i2c_data = (val & 0xff); + gspca_dbg(gspca_dev, D_CONF, "Set exposure to low byte to 0x%x\n", + 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; + gspca_dbg(gspca_dev, D_CONF, "Set global gain to %d\n", 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; + + gspca_dbg(gspca_dev, D_CONF, "Set hvflip %d %d\n", + 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; + gspca_dbg(gspca_dev, D_CONF, "Set red gain to %d\n", 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; + gspca_dbg(gspca_dev, D_CONF, "Set blue gain to %d\n", 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; + gspca_dbg(gspca_dev, D_CONF, "Set green gain to %d\n", 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; + + gspca_dbg(gspca_dev, D_CONF, "Set auto white balance to %d\n", 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; + + gspca_dbg(gspca_dev, D_CONF, "Set auto exposure to %d\n", 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 000000000..af4a7d836 --- /dev/null +++ b/drivers/media/usb/gspca/m5602/m5602_po1030.h @@ -0,0 +1,166 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * 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 + */ + +#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, +}; +#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 000000000..c022265db --- /dev/null +++ b/drivers/media/usb/gspca/m5602/m5602_s5k4aa.c @@ -0,0 +1,759 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * 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 + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "m5602_s5k4aa.h" + +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}, +}; + + +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; + } + + gspca_dbg(gspca_dev, D_PROBE, "Probing for a s5k4aa sensor\n"); + + /* 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: + gspca_dbg(gspca_dev, D_CONF, "Configuring camera for SXGA mode\n"); + + 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: + gspca_dbg(gspca_dev, D_CONF, "Configuring camera for VGA mode\n"); + + 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; + + gspca_dbg(gspca_dev, D_CONF, "Set exposure to %d\n", 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; + + gspca_dbg(gspca_dev, D_CONF, "Set hvflip %d %d\n", 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; + + gspca_dbg(gspca_dev, D_CONF, "Set gain to %d\n", 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; + + gspca_dbg(gspca_dev, D_CONF, "Set brightness to %d\n", 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; + + gspca_dbg(gspca_dev, D_CONF, "Set noise to %d\n", 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 000000000..1789cd051 --- /dev/null +++ b/drivers/media/usb/gspca/m5602/m5602_s5k4aa.h @@ -0,0 +1,84 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * 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 + */ + +#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, +}; + +#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 000000000..8ef010a87 --- /dev/null +++ b/drivers/media/usb/gspca/m5602/m5602_s5k83a.c @@ -0,0 +1,576 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * 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 + */ + +#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 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}, +}; + +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; + } + + gspca_dbg(gspca_dev, D_PROBE, "Probing for a s5k83a sensor\n"); + + /* 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, ®); + 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_run(rotation_thread_function, + sd, "rotation thread"); + if (IS_ERR(sd->rotation_thread)) { + err = PTR_ERR(sd->rotation_thread); + sd->rotation_thread = NULL; + return err; + } + + /* 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) +{ + u8 data[1]; + struct sd *sd = (struct sd *) gspca_dev; + + data[0] = val; + return m5602_write_sensor(sd, S5K83A_BRIGHTNESS, data, 1); +} + +static int s5k83a_set_exposure(struct gspca_dev *gspca_dev, __s32 val) +{ + u8 data[2]; + struct sd *sd = (struct sd *) gspca_dev; + + data[0] = 0; + data[1] = val; + return m5602_write_sensor(sd, S5K83A_EXPOSURE, data, 2); +} + +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, ®); + 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 000000000..851e65156 --- /dev/null +++ b/drivers/media/usb/gspca/m5602/m5602_s5k83a.h @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * 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 + */ + +#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, +}; +#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 000000000..46b7297fb --- /dev/null +++ b/drivers/media/usb/gspca/m5602/m5602_sensor.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * 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 + */ + +#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 000000000..eb9342ee3 --- /dev/null +++ b/drivers/media/usb/gspca/mars.c @@ -0,0 +1,427 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Mars-Semi MR97311A library + * Copyright (C) 2005 <bradlch@hotmail.com> + * + * V4L2 by Jean-Francois Moine <http://moinejf.free.fr> + */ + +#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) { + gspca_dbg(gspca_dev, D_PACK, "sof offset: %d len: %d\n", + 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 000000000..3553788e8 --- /dev/null +++ b/drivers/media/usb/gspca/mr97310a.c @@ -0,0 +1,1077 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * 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. + */ + +#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) + gspca_dbg(gspca_dev, D_PROBE, "Register: %02x reads %02x%02x%02x\n", + 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) + gspca_err(gspca_dev, "status is %02x\n", 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); + 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) + gspca_err(gspca_dev, "Stream Stop failed\n"); +} + +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) + gspca_err(gspca_dev, "LCD Stop failed\n"); +} + +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; + } + gspca_dbg(gspca_dev, D_PROBE, "MR97310A CIF camera detected, sensor: %d\n", + 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; + gspca_dbg(gspca_dev, D_PROBE, "sensor_type corrected to 0\n"); + 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"); + } + } + gspca_dbg(gspca_dev, D_PROBE, "MR97310A VGA camera detected, sensor: %d\n", + 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; + gspca_dbg(gspca_dev, D_PROBE, "Forcing sensor type to: %d\n", + 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 */ + fallthrough; + 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 */ + fallthrough; + 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 */ + fallthrough; + case 320: + data[9] |= 0x04; /* reg 8, 2:1 scale down */ + fallthrough; + 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 */ + fallthrough; + 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 000000000..0f5f2464a --- /dev/null +++ b/drivers/media/usb/gspca/nw80x.c @@ -0,0 +1,2105 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * 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> + */ + +#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) + gspca_dbg(gspca_dev, D_USBO, "SET 00 0000 %04x %02x\n", + index, *data); + else + gspca_dbg(gspca_dev, D_USBO, "SET 00 0000 %04x %02x %02x ...\n", + 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; + /* + * Make sure the buffer is zeroed to avoid uninitialized + * values. + */ + memset(gspca_dev->usb_buf, 0, USB_BUF_SZ); + return; + } + if (len == 1) + gspca_dbg(gspca_dev, D_USBI, "GET 00 0000 %04x %02x\n", + index, gspca_dev->usb_buf[0]); + else + gspca_dbg(gspca_dev, D_USBI, "GET 00 0000 %04x %02x %02x ..\n", + 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 */ + gspca_dbg(gspca_dev, D_PROBE, "et31x110 sensor type %02x\n", + 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; + } + gspca_dbg(gspca_dev, D_PROBE, "Bridge nw80%d - type: %d\n", + 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); + fallthrough; + 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 000000000..bffa94e76 --- /dev/null +++ b/drivers/media/usb/gspca/ov519.c @@ -0,0 +1,5030 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * 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> + */ + +#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_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 that's 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; + + /* Avoid things going to fast for the bridge with a xhci host */ + udelay(150); + + switch (sd->bridge) { + case BRIDGE_OV511: + case BRIDGE_OV511PLUS: + req = 2; + break; + case BRIDGE_OVFX2: + req = 0x0a; + fallthrough; + case BRIDGE_W9968CF: + gspca_dbg(gspca_dev, D_USBO, "SET %02x %04x %04x\n", + 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; + } + + gspca_dbg(gspca_dev, D_USBO, "SET %02x 0000 %04x %02x\n", + 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) { + gspca_err(gspca_dev, "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; + } + + /* Avoid things going to fast for the bridge with a xhci host */ + udelay(150); + 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]; + gspca_dbg(gspca_dev, D_USBI, "GET %02x 0000 %04x %02x\n", + req, index, ret); + } else { + gspca_err(gspca_dev, "reg_r %02x failed %d\n", index, ret); + sd->gspca_dev.usb_err = ret; + /* + * Make sure the result is zeroed to avoid uninitialized + * values. + */ + gspca_dev->usb_buf[0] = 0; + } + + 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; + + /* Avoid things going to fast for the bridge with a xhci host */ + udelay(150); + 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 { + gspca_err(gspca_dev, "reg_r8 %02x failed %d\n", index, ret); + sd->gspca_dev.usb_err = ret; + /* + * Make sure the buffer is zeroed to avoid uninitialized + * values. + */ + memset(gspca_dev->usb_buf, 0, 8); + } + + 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); + + /* Avoid things going to fast for the bridge with a xhci host */ + udelay(150); + 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) { + gspca_err(gspca_dev, "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; + + gspca_dbg(gspca_dev, D_USBO, "ov511_i2c_w %02x %02x\n", 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) { + gspca_dbg(gspca_dev, D_USBO, "i2c write retries exhausted\n"); + 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) { + gspca_dbg(gspca_dev, D_USBI, "i2c write retries exhausted\n"); + 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) { + gspca_dbg(gspca_dev, D_USBI, "i2c read retries exhausted\n"); + return -1; + } + } + + value = reg_r(sd, R51x_I2C_DATA); + + gspca_dbg(gspca_dev, D_USBI, "ov511_i2c_r %02x %02x\n", 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; + + gspca_dbg(gspca_dev, D_USBO, "ov518_i2c_w %02x %02x\n", 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); + gspca_dbg(gspca_dev, D_USBI, "ov518_i2c_r %02x %02x\n", 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) { + gspca_err(gspca_dev, "ovfx2_i2c_w %02x failed %d\n", reg, ret); + sd->gspca_dev.usb_err = ret; + } + + gspca_dbg(gspca_dev, D_USBO, "ovfx2_i2c_w %02x %02x\n", 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]; + gspca_dbg(gspca_dev, D_USBI, "ovfx2_i2c_r %02x %02x\n", + reg, ret); + } else { + gspca_err(gspca_dev, "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; + + gspca_dbg(gspca_dev, D_STREAM, "stopping\n"); + 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; + + gspca_dbg(gspca_dev, D_STREAM, "restarting\n"); + 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) { + gspca_dbg(gspca_dev, D_PROBE, "I2C synced in %d attempt(s)\n", + 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) { + gspca_err(gspca_dev, "error hires sensors only supported with ovfx2\n"); + return; + } + + gspca_dbg(gspca_dev, D_PROBE, "starting ov hires configuration\n"); + + /* 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: + gspca_dbg(gspca_dev, D_PROBE, "Sensor is a OV2610\n"); + sd->sensor = SEN_OV2610; + return; + case 0x41: + gspca_dbg(gspca_dev, D_PROBE, "Sensor is a OV2610AE\n"); + sd->sensor = SEN_OV2610AE; + return; + case 0xb1: + gspca_dbg(gspca_dev, D_PROBE, "Sensor is a OV9600\n"); + sd->sensor = SEN_OV9600; + return; + } + break; + case 0x36: + if ((low & 0x0f) == 0x00) { + gspca_dbg(gspca_dev, D_PROBE, "Sensor is a OV3610\n"); + sd->sensor = SEN_OV3610; + return; + } + break; + } + gspca_err(gspca_dev, "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; + + gspca_dbg(gspca_dev, D_PROBE, "starting ov8xx0 configuration\n"); + + /* Detect sensor (sub)type */ + rc = i2c_r(sd, OV7610_REG_COM_I); + if (rc < 0) { + gspca_err(gspca_dev, "Error detecting sensor type\n"); + return; + } + if ((rc & 3) == 1) + sd->sensor = SEN_OV8610; + else + gspca_err(gspca_dev, "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; + + gspca_dbg(gspca_dev, D_PROBE, "starting OV7xx0 configuration\n"); + + /* 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) { + gspca_err(gspca_dev, "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) { + gspca_dbg(gspca_dev, D_PROBE, "Sensor is an OV76%02x\n", + low); + sd->sensor = SEN_OV7670; + } else { + gspca_dbg(gspca_dev, D_PROBE, "Sensor is an OV7610\n"); + 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) { + gspca_dbg(gspca_dev, D_PROBE, "Sensor is an OV7620AE\n"); + sd->sensor = SEN_OV7620AE; + } else { + gspca_dbg(gspca_dev, D_PROBE, "Sensor is an OV76BE\n"); + sd->sensor = SEN_OV76BE; + } + } else if ((rc & 3) == 0) { + /* try to read product id registers */ + high = i2c_r(sd, 0x0a); + if (high < 0) { + gspca_err(gspca_dev, "Error detecting camera chip PID\n"); + return; + } + low = i2c_r(sd, 0x0b); + if (low < 0) { + gspca_err(gspca_dev, "Error detecting camera chip VER\n"); + return; + } + if (high == 0x76) { + switch (low) { + case 0x30: + gspca_err(gspca_dev, "Sensor is an OV7630/OV7635\n"); + gspca_err(gspca_dev, "7630 is not supported by this driver\n"); + return; + case 0x40: + gspca_dbg(gspca_dev, D_PROBE, "Sensor is an OV7645\n"); + sd->sensor = SEN_OV7640; /* FIXME */ + break; + case 0x45: + gspca_dbg(gspca_dev, D_PROBE, "Sensor is an OV7645B\n"); + sd->sensor = SEN_OV7640; /* FIXME */ + break; + case 0x48: + gspca_dbg(gspca_dev, D_PROBE, "Sensor is an OV7648\n"); + sd->sensor = SEN_OV7648; + break; + case 0x60: + gspca_dbg(gspca_dev, D_PROBE, "Sensor is a OV7660\n"); + sd->sensor = SEN_OV7660; + break; + default: + gspca_err(gspca_dev, "Unknown sensor: 0x76%02x\n", + low); + return; + } + } else { + gspca_dbg(gspca_dev, D_PROBE, "Sensor is an OV7620\n"); + sd->sensor = SEN_OV7620; + } + } else { + gspca_err(gspca_dev, "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; + + gspca_dbg(gspca_dev, D_PROBE, "starting OV6xx0 configuration\n"); + + /* Detect sensor (sub)type */ + rc = i2c_r(sd, OV7610_REG_COM_I); + if (rc < 0) { + gspca_err(gspca_dev, "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; + gspca_dbg(gspca_dev, D_PROBE, "Sensor is an OV6620\n"); + break; + case 0x02: + sd->sensor = SEN_OV6630; + gspca_dbg(gspca_dev, D_PROBE, "Sensor is an OV66308AE\n"); + break; + case 0x03: + sd->sensor = SEN_OV66308AF; + gspca_dbg(gspca_dev, D_PROBE, "Sensor is an OV66308AF\n"); + 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: + gspca_err(gspca_dev, "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) +{ + static 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 + }; + + static 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) */ + static 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 + }; + static 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; + + gspca_dbg(gspca_dev, D_PROBE, "Uploading quantization tables\n"); + + 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+ */ + static 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 }, + }; + + static 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 }, + }; + + static 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 }, + }; + + static const struct ov_regvals compress_511[] = { + { 0x70, 0x1f }, + { 0x71, 0x05 }, + { 0x72, 0x06 }, + { 0x73, 0x06 }, + { 0x74, 0x14 }, + { 0x75, 0x03 }, + { 0x76, 0x04 }, + { 0x77, 0x04 }, + }; + + gspca_dbg(gspca_dev, D_PROBE, "Device custom id %x\n", + 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+ */ + static 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 }, + }; + + static 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 }, + }; + + static 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; + gspca_dbg(gspca_dev, D_PROBE, "Device revision %d\n", 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 { + gspca_err(gspca_dev, "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: + gspca_err(gspca_dev, "OV519 Config failed\n"); + 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) { + gspca_err(gspca_dev, "Couldn't get altsetting\n"); + sd->gspca_dev.usb_err = -EIO; + return; + } + + if (alt->desc.bNumEndpoints < 1) { + sd->gspca_dev.usb_err = -ENODEV; + 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; + fallthrough; + 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; + } + /* For 640x480 case */ + fallthrough; + 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) { + gspca_err(gspca_dev, "Couldn't get altsetting\n"); + sd->gspca_dev.usb_err = -EIO; + return; + } + + if (alt->desc.bNumEndpoints < 1) { + sd->gspca_dev.usb_err = -ENODEV; + 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 revision > 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 */ + gspca_dbg(gspca_dev, D_STREAM, "Setting framerate to %d fps\n", + (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) { + gspca_err(gspca_dev, "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]) { + gspca_err(gspca_dev, "Invalid packet nr: %d (expect: %d)\n", + (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) { + gspca_err(gspca_dev, "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 000000000..8b6a57f17 --- /dev/null +++ b/drivers/media/usb/gspca/ov534.c @@ -0,0 +1,1608 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * 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> https://nummist.com + */ + +#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 +#define DEFAULT_FRAME_RATE 30 + +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}, + {320, 240, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {640, 480, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480, + .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), + }, + { /* 320x240 SGBRG8 */ + .rates = qvga_rates, + .nrates = ARRAY_SIZE(qvga_rates), + }, + { /* 640x480 SGBRG8 */ + .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] = { + { 0x88, 0xf8 }, + { 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 }, + { 0x89, 0x00 }, + { 0x76, 0x00 }, + { 0xe7, 0x2e }, + { 0x31, 0xf9 }, + { 0x25, 0x42 }, + { 0x21, 0xf0 }, + + { 0x1c, 0x0a }, + { 0x1d, 0x08 }, /* turn on UVC header */ + { 0x1d, 0x0e }, /* .. */ +}; +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 }, + + { 0x2b, 0x00 }, + { 0x22, 0x7f }, + { 0x23, 0x03 }, + { 0x11, 0x01 }, + { 0x64, 0xff }, + { 0x0d, 0x41 }, + + { 0x14, 0x41 }, + { 0x0e, 0xcd }, + { 0xac, 0xbf }, + { 0x8e, 0x00 }, /* De-noise threshold */ +}; +static const u8 bridge_start_vga_yuyv_772x[][2] = { + {0x88, 0x00}, + {0x1c, 0x00}, + {0x1d, 0x40}, + {0x1d, 0x02}, + {0x1d, 0x00}, + {0x1d, 0x02}, + {0x1d, 0x58}, + {0x1d, 0x00}, + {0x8d, 0x1c}, + {0x8e, 0x80}, + {0xc0, 0x50}, + {0xc1, 0x3c}, + {0xc2, 0x0c}, + {0xc3, 0x69}, +}; +static const u8 sensor_start_vga_yuyv_772x[][2] = { + {0x12, 0x00}, + {0x17, 0x26}, + {0x18, 0xa0}, + {0x19, 0x07}, + {0x1a, 0xf0}, + {0x29, 0xa0}, + {0x2c, 0xf0}, + {0x65, 0x20}, + {0x67, 0x00}, +}; +static const u8 bridge_start_qvga_yuyv_772x[][2] = { + {0x88, 0x00}, + {0x1c, 0x00}, + {0x1d, 0x40}, + {0x1d, 0x02}, + {0x1d, 0x00}, + {0x1d, 0x01}, + {0x1d, 0x4b}, + {0x1d, 0x00}, + {0x8d, 0x1c}, + {0x8e, 0x80}, + {0xc0, 0x28}, + {0xc1, 0x1e}, + {0xc2, 0x0c}, + {0xc3, 0x69}, +}; +static const u8 sensor_start_qvga_yuyv_772x[][2] = { + {0x12, 0x40}, + {0x17, 0x3f}, + {0x18, 0x50}, + {0x19, 0x03}, + {0x1a, 0x78}, + {0x29, 0x50}, + {0x2c, 0x78}, + {0x65, 0x2f}, + {0x67, 0x00}, +}; +static const u8 bridge_start_vga_gbrg_772x[][2] = { + {0x88, 0x08}, + {0x1c, 0x00}, + {0x1d, 0x00}, + {0x1d, 0x02}, + {0x1d, 0x00}, + {0x1d, 0x01}, + {0x1d, 0x2c}, + {0x1d, 0x00}, + {0x8d, 0x00}, + {0x8e, 0x00}, + {0xc0, 0x50}, + {0xc1, 0x3c}, + {0xc2, 0x01}, + {0xc3, 0x01}, +}; +static const u8 sensor_start_vga_gbrg_772x[][2] = { + {0x12, 0x01}, + {0x17, 0x26}, + {0x18, 0xa0}, + {0x19, 0x07}, + {0x1a, 0xf0}, + {0x29, 0xa0}, + {0x2c, 0xf0}, + {0x65, 0x20}, + {0x67, 0x02}, +}; +static const u8 bridge_start_qvga_gbrg_772x[][2] = { + {0x88, 0x08}, + {0x1c, 0x00}, + {0x1d, 0x00}, + {0x1d, 0x02}, + {0x1d, 0x00}, + {0x1d, 0x00}, + {0x1d, 0x4b}, + {0x1d, 0x00}, + {0x8d, 0x00}, + {0x8e, 0x00}, + {0xc0, 0x28}, + {0xc1, 0x1e}, + {0xc2, 0x01}, + {0xc3, 0x01}, +}; +static const u8 sensor_start_qvga_gbrg_772x[][2] = { + {0x12, 0x41}, + {0x17, 0x3f}, + {0x18, 0x50}, + {0x19, 0x03}, + {0x1a, 0x78}, + {0x29, 0x50}, + {0x2c, 0x78}, + {0x65, 0x2f}, + {0x67, 0x02}, +}; + +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; + + gspca_dbg(gspca_dev, D_USBO, "SET 01 0000 %04x %02x\n", 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); + gspca_dbg(gspca_dev, D_USBI, "GET 01 0000 %04x %02x\n", + reg, gspca_dev->usb_buf[0]); + if (ret < 0) { + pr_err("read failed %d\n", ret); + gspca_dev->usb_err = ret; + /* + * Make sure the result is zeroed to avoid uninitialized + * values. + */ + gspca_dev->usb_buf[0] = 0; + } + 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; + + gspca_dbg(gspca_dev, D_CONF, "led status: %d\n", 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++) { + usleep_range(10000, 20000); + data = ov534_reg_read(gspca_dev, OV534_REG_STATUS); + + switch (data) { + case 0x00: + return 1; + case 0x04: + return 0; + case 0x03: + break; + default: + gspca_err(gspca_dev, "sccb status 0x%02x, attempt %d/5\n", + data, i + 1); + } + } + return 0; +} + +static void sccb_reg_write(struct gspca_dev *gspca_dev, u8 reg, u8 val) +{ + gspca_dbg(gspca_dev, D_USBO, "sccb write: %02x %02x\n", 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); + + gspca_dbg(gspca_dev, D_PROBE, "frame_rate: %d\n", 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 = DEFAULT_FRAME_RATE; + + 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); + usleep_range(10000, 20000); + + /* 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); + gspca_dbg(gspca_dev, D_PROBE, "Sensor ID: %04x\n", 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][4] = { + [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_yuyv_772x, + ARRAY_SIZE(bridge_start_qvga_yuyv_772x)}, + {bridge_start_vga_yuyv_772x, + ARRAY_SIZE(bridge_start_vga_yuyv_772x)}, + {bridge_start_qvga_gbrg_772x, + ARRAY_SIZE(bridge_start_qvga_gbrg_772x)}, + {bridge_start_vga_gbrg_772x, + ARRAY_SIZE(bridge_start_vga_gbrg_772x)} }, + }; + static const struct reg_array sensor_start[NSENSORS][4] = { + [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_yuyv_772x, + ARRAY_SIZE(sensor_start_qvga_yuyv_772x)}, + {sensor_start_vga_yuyv_772x, + ARRAY_SIZE(sensor_start_vga_yuyv_772x)}, + {sensor_start_qvga_gbrg_772x, + ARRAY_SIZE(sensor_start_qvga_gbrg_772x)}, + {sensor_start_vga_gbrg_772x, + ARRAY_SIZE(sensor_start_vga_gbrg_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) { + gspca_dbg(gspca_dev, D_PACK, "bad header\n"); + goto discard; + } + + /* Check errors */ + if (data[1] & UVC_STREAM_ERR) { + gspca_dbg(gspca_dev, D_PACK, "payload error\n"); + goto discard; + } + + /* Extract PTS and FID */ + if (!(data[1] & UVC_STREAM_PTS)) { + gspca_dbg(gspca_dev, D_PACK, "PTS not present\n"); + 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_JPEG + && gspca_dev->image_len + len - 12 != + gspca_dev->pixfmt.sizeimage) { + gspca_dbg(gspca_dev, D_PACK, "wrong sized frame\n"); + 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; + + 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) + sd->frame_rate = DEFAULT_FRAME_RATE; + else + 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 000000000..91efc650c --- /dev/null +++ b/drivers/media/usb/gspca/ov534_9.c @@ -0,0 +1,1821 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * 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/ + */ + +#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) +{ + gspca_dbg(gspca_dev, D_USBO, "reg_w [%04x] = %02x\n", 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); + gspca_dbg(gspca_dev, D_USBI, "reg_r [%04x] -> %02x\n", + reg, gspca_dev->usb_buf[0]); + 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 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: + gspca_dbg(gspca_dev, D_USBI|D_USBO, + "sccb status 0x%02x, attempt %d/5\n", + data, i + 1); + } + } + return 0; +} + +static void sccb_write(struct gspca_dev *gspca_dev, u8 reg, u8 val) +{ + gspca_dbg(gspca_dev, D_USBO, "sccb_write [%02x] = %02x\n", 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; + + gspca_dbg(gspca_dev, D_CONF, "led status: %d\n", 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); + gspca_dbg(gspca_dev, D_PROBE, "Sensor ID: %04x\n", 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) { + gspca_dbg(gspca_dev, D_PACK, "bad header\n"); + goto discard; + } + + /* Check errors */ + if (data[1] & UVC_STREAM_ERR) { + gspca_dbg(gspca_dev, D_PACK, "payload error\n"); + goto discard; + } + + /* Extract PTS and FID */ + if (!(data[1] & UVC_STREAM_PTS)) { + gspca_dbg(gspca_dev, D_PACK, "PTS not present\n"); + 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 000000000..34127114c --- /dev/null +++ b/drivers/media/usb/gspca/pac207.c @@ -0,0 +1,475 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * 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> + */ + +#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; + gspca_dbg(gspca_dev, D_PROBE, "Pixart Sensor ID 0x%02X Chips ID 0x%02X\n", + idreg[0], idreg[1]); + + if (idreg[0] != 0x27) { + gspca_dbg(gspca_dev, D_PROBE, "Error invalid sensor ID!\n"); + return -ENODEV; + } + + gspca_dbg(gspca_dev, D_PROBE, + "Pixart PAC207BCA Image Processor and Control Chip detected (vid/pid 0x%04X:0x%04X)\n", + 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; + gspca_dbg(gspca_dev, D_STREAM, "pac207_start mode 176x144\n"); + } else { /* 352x288 */ + gspca_dbg(gspca_dev, D_STREAM, "pac207_start mode 352x288\n"); + } + 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 000000000..608be0d64 --- /dev/null +++ b/drivers/media/usb/gspca/pac7302.c @@ -0,0 +1,951 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * 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> + */ + +/* + * 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) { + gspca_err(gspca_dev, "Incorrect variable sequence\n"); + 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; + } 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 000000000..f6addb29f --- /dev/null +++ b/drivers/media/usb/gspca/pac7311.c @@ -0,0 +1,687 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Pixart PAC7311 library + * Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li + * + * V4L2 by Jean-Francois Moine <http://moinejf.free.fr> + */ + +/* 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) { + gspca_err(gspca_dev, "Incorrect variable sequence\n"); + 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 000000000..acd1be936 --- /dev/null +++ b/drivers/media/usb/gspca/pac_common.h @@ -0,0 +1,119 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * 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> + */ + +/* 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 at least 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 */ + gspca_dbg(gspca_dev, D_FRAM, + "SOF found, bytes to analyze: %u - Frame starts at byte #%u\n", + 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 000000000..e087cfb59 --- /dev/null +++ b/drivers/media/usb/gspca/se401.c @@ -0,0 +1,730 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * 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) + */ + +#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; + /* + * Make sure the buffer is zeroed to avoid uninitialized + * values. + */ + memset(gspca_dev->usb_buf, 0, READ_REQ_SIZE); + } +} + +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 000000000..b0271409f --- /dev/null +++ b/drivers/media/usb/gspca/se401.h @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * 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) + */ + +#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 000000000..dbd1d6da3 --- /dev/null +++ b/drivers/media/usb/gspca/sn9c2028.c @@ -0,0 +1,963 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * SN9C2028 library + * + * Copyright (C) 2009 Theodore Kilgore <kilgota@auburn.edu> + */ + +#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; + + gspca_dbg(gspca_dev, D_USBO, "sending command %02x%02x%02x%02x%02x%02x\n", + 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; + } + gspca_dbg(gspca_dev, D_USBI, "read1 response %02x\n", + 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); + gspca_dbg(gspca_dev, D_USBI, "read4 response %02x%02x%02x%02x\n", + 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; + + gspca_dbg(gspca_dev, D_PROBE, "SN9C2028 camera detected (vid/pid 0x%04X:0x%04X)\n", + id->idVendor, id->idProduct); + + sd->model = id->idProduct; + + switch (sd->model) { + case 0x7005: + gspca_dbg(gspca_dev, D_PROBE, "Genius Smart 300 camera\n"); + break; + case 0x7003: + gspca_dbg(gspca_dev, D_PROBE, "Genius Videocam Live v2\n"); + break; + case 0x8000: + gspca_dbg(gspca_dev, D_PROBE, "DC31VC\n"); + break; + case 0x8001: + gspca_dbg(gspca_dev, D_PROBE, "Spy camera\n"); + break; + case 0x8003: + gspca_dbg(gspca_dev, D_PROBE, "CIF camera\n"); + break; + case 0x8008: + gspca_dbg(gspca_dev, D_PROBE, "Mini-Shotz ms-350 camera\n"); + break; + case 0x800a: + gspca_dbg(gspca_dev, D_PROBE, "Vivitar 3350b type camera\n"); + 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; + + 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) + gspca_err(gspca_dev, "Camera Stop read failed\n"); + + memset(data, 0, 6); + data[0] = 0x14; + result = sn9c2028_command(gspca_dev, data); + if (result < 0) + gspca_err(gspca_dev, "Camera Stop command failed\n"); +} + +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 000000000..beb5d2a5d --- /dev/null +++ b/drivers/media/usb/gspca/sn9c2028.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * SN9C2028 common functions + * + * Copyright (C) 2009 Theodore Kilgore <kilgota@auburn,edu> + * + * Based closely upon the file gspca/pac_common.h + */ + +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)) { + gspca_dbg(gspca_dev, D_FRAM, + "SOF found, bytes to analyze: %u - Frame starts at byte #%u\n", + 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 000000000..da916127a --- /dev/null +++ b/drivers/media/usb/gspca/sn9c20x.c @@ -0,0 +1,2409 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * 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> + */ + +#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 +#define HAS_LED_TORCH 0x8 + +/* 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 v4l2_ctrl *led_mode; + + struct work_struct work; + + 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-1039", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "MICRO-STAR INT'L CO.,LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, "MS-1039"), + } + }, + { + .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; + /* + * Make sure the buffer is zeroed to avoid uninitialized + * values. + */ + memset(gspca_dev->usb_buf, 0, USB_BUF_SZ); + } +} + +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_led_mode(struct gspca_dev *gspca_dev, s32 val) +{ + reg_w1(gspca_dev, 0x1007, 0x60); + reg_w1(gspca_dev, 0x1006, val ? 0x40 : 0x00); +} + +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 *) ®->val); + reg->size = 2; + } else { + i2c_r1(gspca_dev, reg->reg, (u8 *) ®->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) + strscpy(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 */ + fallthrough; + 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; + case V4L2_CID_FLASH_LED_MODE: + set_led_mode(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 (sd->flags & HAS_LED_TORCH) + sd->led_mode = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops, + V4L2_CID_FLASH_LED_MODE, V4L2_FLASH_LED_MODE_TORCH, + ~0x5, V4L2_FLASH_LED_MODE_NONE); + + 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; + } + if (sd->led_mode) + v4l2_ctrl_s_ctrl(sd->led_mode, 0); + + 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; + + mutex_unlock(&gspca_dev->usb_lock); + flush_work(&sd->work); + mutex_lock(&gspca_dev->usb_lock); +} + +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); + gspca_dbg(gspca_dev, D_STREAM, "qual_upd %d%%\n", 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; + schedule_work(&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, HAS_LED_TORCH)}, + {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 000000000..4d655e2da --- /dev/null +++ b/drivers/media/usb/gspca/sonixb.c @@ -0,0 +1,1469 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * 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 + */ + +/* 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 at least 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; + /* + * Make sure the result is zeroed to avoid uninitialized + * values. + */ + gspca_dev->usb_buf[0] = 0; + } +} + +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, ®, 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 oscillate 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(®s[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, ®s[0x01], 1); + /* reg 0x17 SensorClk enable inv Clk 0x60 */ + reg_w(gspca_dev, 0x17, ®s[0x17], 1); + /* Set the registers from the template */ + reg_w(gspca_dev, 0x01, ®s[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, ®s[0x15], 2); + /* compression register */ + reg_w(gspca_dev, 0x18, ®s[0x18], 1); + /* H_start */ + reg_w(gspca_dev, 0x12, ®s[0x12], 1); + /* V_START */ + reg_w(gspca_dev, 0x13, ®s[0x13], 1); + /* reset 0x17 SensorClk enable inv Clk 0x60 */ + /*fixme: ov7630 [17]=68 8f (+20 if 102)*/ + reg_w(gspca_dev, 0x17, ®s[0x17], 1); + /*MCKSIZE ->3 */ /*fixme: not ov7630*/ + reg_w(gspca_dev, 0x19, ®s[0x19], 1); + /* AE_STRX AE_STRY AE_ENDX AE_ENDY */ + reg_w(gspca_dev, 0x1c, ®s[0x1c], 4); + /* Enable video transfert */ + reg_w(gspca_dev, 0x01, ®s[0x01], 1); + /* Compression */ + reg_w(gspca_dev, 0x18, ®s[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 000000000..2e1bd2df8 --- /dev/null +++ b/drivers/media/usb/gspca/sonixj.c @@ -0,0 +1,2981 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * 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 + */ + +#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; + + 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) { + gspca_err(gspca_dev, "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); + gspca_dbg(gspca_dev, D_USBI, "reg_r [%02x] -> %02x\n", + value, gspca_dev->usb_buf[0]); + if (ret < 0) { + pr_err("reg_r err %d\n", ret); + gspca_dev->usb_err = ret; + /* + * Make sure the buffer is zeroed to avoid uninitialized + * values. + */ + memset(gspca_dev->usb_buf, 0, USB_BUF_SZ); + } +} + +static void reg_w1(struct gspca_dev *gspca_dev, + u16 value, + u8 data) +{ + int ret; + + if (gspca_dev->usb_err < 0) + return; + gspca_dbg(gspca_dev, D_USBO, "reg_w1 [%04x] = %02x\n", 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; + gspca_dbg(gspca_dev, D_USBO, "reg_w [%04x] = %02x %02x ..\n", + value, buffer[0], buffer[1]); + + if (len > USB_BUF_SZ) { + gspca_err(gspca_dev, "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; + gspca_dbg(gspca_dev, D_USBO, "i2c_w1 [%02x] = %02x\n", 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; + gspca_dbg(gspca_dev, D_USBO, "i2c_w8 [%02x] = %02x ..\n", + 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) { + gspca_dbg(gspca_dev, D_PROBE, "Sensor HV7131R found\n"); + 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: + gspca_dbg(gspca_dev, D_PROBE, "Sensor mi0360b\n"); + sd->sensor = SENSOR_MI0360B; + break; + case 0x823a: + gspca_dbg(gspca_dev, D_PROBE, "Sensor mt9v111\n"); + sd->sensor = SENSOR_MT9V111; + break; + case 0x8243: + gspca_dbg(gspca_dev, D_PROBE, "Sensor mi0360\n"); + break; + default: + gspca_dbg(gspca_dev, D_PROBE, "Unknown sensor %04x - forced to mi0360\n", + 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; + gspca_dbg(gspca_dev, D_PROBE, "Sensor soi768\n"); + return; + } + gspca_dbg(gspca_dev, D_PROBE, "Sensor ov%04x\n", 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 */ + gspca_dbg(gspca_dev, D_PROBE, "Sensor ov%04x\n", 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 */ + gspca_dbg(gspca_dev, D_PROBE, "Sensor po1030\n"); + 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 (?) */ + gspca_dbg(gspca_dev, D_PROBE, "Sensor gc0307\n"); + 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) { + gspca_dbg(gspca_dev, D_PROBE, "Sensor po2030n\n"); +/* 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; + gspca_dbg(gspca_dev, D_PROBE, "Sonix chip id: %02x\n", 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); + gspca_dbg(gspca_dev, D_FRAM, "set exposure %d\n", + ((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); + gspca_dbg(gspca_dev, D_FRAM, "set exposure %d\n", 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); + gspca_dbg(gspca_dev, D_STREAM, "qual_upd %d%%\n", 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; + + 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; + + mutex_unlock(&gspca_dev->usb_lock); + flush_work(&sd->work); + mutex_lock(&gspca_dev->usb_lock); +} + +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); + gspca_dbg(gspca_dev, D_FRAM, "mean lum %d\n", 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 split 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; + schedule_work(&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 000000000..ccc477944 --- /dev/null +++ b/drivers/media/usb/gspca/spca1528.c @@ -0,0 +1,438 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * spca1528 subdriver + * + * Copyright (C) 2010-2011 Jean-Francois Moine (http://moinejf.free.fr) + */ + +#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); + gspca_dbg(gspca_dev, D_USBI, "GET %02x 0000 %04x %02x\n", req, index, + gspca_dev->usb_buf[0]); + if (ret < 0) { + pr_err("reg_r err %d\n", ret); + gspca_dev->usb_err = ret; + /* + * Make sure the buffer is zeroed to avoid uninitialized + * values. + */ + memset(gspca_dev->usb_buf, 0, USB_BUF_SZ); + } +} + +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; + gspca_dbg(gspca_dev, D_USBO, "SET %02x %04x %04x\n", 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; + gspca_dbg(gspca_dev, D_USBO, "SET %02x %04x %04x %02x\n", + 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); + gspca_err(gspca_dev, "wait_status_0 timeout\n"); + 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); + gspca_err(gspca_dev, "wait_status_1 timeout\n"); + 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); + gspca_dbg(gspca_dev, D_PROBE, "%s%s\n", &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 000000000..b9ce5e35f --- /dev/null +++ b/drivers/media/usb/gspca/spca500.c @@ -0,0 +1,979 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * SPCA500 chip based cameras initialization data + * + * V4L2 by Jean-Francois Moine <http://moinejf.free.fr> + */ + +#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; + + gspca_dbg(gspca_dev, D_USBO, "reg write: [0x%02x] = 0x%02x\n", + 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); + gspca_dbg(gspca_dev, D_STREAM, "ClickSmart310 ping 0x0d04 0x%02x 0x%02x\n", + 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); + gspca_dbg(gspca_dev, D_STREAM, "ClickSmart310 init 0x0d05 0x%02x 0x%02x\n", + 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) { + gspca_err(gspca_dev, "reg_r_wait() failed\n"); + 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) { + gspca_err(gspca_dev, "Set packet size: set interface error\n"); + goto error; + } + spca500_ping310(gspca_dev); + + reg_r(gspca_dev, 0x0d00, 1); + + /* need alt setting here */ + gspca_dbg(gspca_dev, D_PACK, "ClickSmart310 sync alt: %d\n", + 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) { + gspca_err(gspca_dev, "Set packet size: set interface error\n"); + 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) + gspca_err(gspca_dev, "spca50x_setup_qtable failed on init\n"); + + /* 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 */ + gspca_dbg(gspca_dev, D_STREAM, "SPCA500 init\n"); + if (sd->subtype == LogitechClickSmart310) + spca500_clksmart310_init(gspca_dev); +/* else + spca500_initialise(gspca_dev); */ + gspca_dbg(gspca_dev, D_STREAM, "SPCA500 init done\n"); + 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); + gspca_dbg(gspca_dev, D_STREAM, "Spca500 Sensor Address 0x%02x\n", + gspca_dev->usb_buf[0]); + gspca_dbg(gspca_dev, 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) + gspca_err(gspca_dev, "spca50x_setup_qtable failed\n"); + /* 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) + gspca_err(gspca_dev, "reg_r_wait() failed\n"); + + 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) + gspca_err(gspca_dev, "failed to enable drop packet\n"); + reg_w(gspca_dev, 0x00, 0x8880, 3); + err = spca50x_setup_qtable(gspca_dev, + 0x00, 0x8800, 0x8840, + qtable_creative_pccam); + if (err < 0) + gspca_err(gspca_dev, "spca50x_setup_qtable failed\n"); + + /* 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) + gspca_err(gspca_dev, "reg_r_wait() failed\n"); + + 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) + gspca_err(gspca_dev, "spca500_full_reset failed\n"); + + /* enable drop packet */ + err = reg_w(gspca_dev, 0x00, 0x850a, 0x0001); + if (err < 0) + gspca_err(gspca_dev, "failed to enable drop packet\n"); + reg_w(gspca_dev, 0x00, 0x8880, 3); + err = spca50x_setup_qtable(gspca_dev, + 0x00, 0x8800, 0x8840, + qtable_creative_pccam); + if (err < 0) + gspca_err(gspca_dev, "spca50x_setup_qtable failed\n"); + + 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) + gspca_err(gspca_dev, "reg_r_wait() failed\n"); + + 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) + gspca_err(gspca_dev, "spca500_full_reset failed\n"); + /* 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) + gspca_err(gspca_dev, "spca50x_setup_qtable failed\n"); + 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) + gspca_err(gspca_dev, "reg_r_wait() failed\n"); + + 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) + gspca_err(gspca_dev, "spca50x_setup_qtable failed\n"); + 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) + gspca_err(gspca_dev, "spca50x_setup_qtable failed\n"); + 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); + gspca_dbg(gspca_dev, D_STREAM, "stop SPCA500 done reg8000: 0x%2x\n", + 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 000000000..f7c75d753 --- /dev/null +++ b/drivers/media/usb/gspca/spca501.c @@ -0,0 +1,2036 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * SPCA501 chip based cameras initialization data + * + * V4L2 by Jean-Francois Moine <http://moinejf.free.fr> + */ + +#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 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 Disable 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); + gspca_dbg(gspca_dev, D_USBO, "reg write: 0x%02x 0x%02x 0x%02x\n", + 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) { + gspca_err(gspca_dev, "Reg write failed for 0x%02x,0x%02x,0x%02x\n", + 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; + } + gspca_dbg(gspca_dev, D_STREAM, "Initializing SPCA501 finished\n"); + 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 000000000..0e8cccbb6 --- /dev/null +++ b/drivers/media/usb/gspca/spca505.c @@ -0,0 +1,791 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * SPCA505 chip based cameras initialization data + * + * V4L2 by Jean-Francis Moine <http://moinejf.free.fr> + */ + +#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); + gspca_dbg(gspca_dev, D_USBO, "reg write: 0x%02x,0x%02x:0x%02x, %d\n", + 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) { + gspca_err(gspca_dev, "register read failed err: %d\n", 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 000000000..0886587dc --- /dev/null +++ b/drivers/media/usb/gspca/spca506.c @@ -0,0 +1,599 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * 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> + */ + +#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; + + gspca_dbg(gspca_dev, D_STREAM, "** Open Set Norme **\n"); + 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; + gspca_dbg(gspca_dev, D_STREAM, "Set Video Byte to 0x%2x\n", videomask); + gspca_dbg(gspca_dev, 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; + gspca_dbg(gspca_dev, D_STREAM, "Get Norme: %d Channel %d\n", + *norme, *channel); +} + +static void spca506_Setsize(struct gspca_dev *gspca_dev, __u16 code, + __u16 xmult, __u16 ymult) +{ + struct usb_device *dev = gspca_dev->dev; + + gspca_dbg(gspca_dev, D_STREAM, "** SetSize **\n"); + 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); + gspca_dbg(gspca_dev, D_STREAM, "** Close Init *\n"); + 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); + gspca_dbg(gspca_dev, D_STREAM, "webcam started\n"); + 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)}, +/* {USB_DEVICE(0x0733, 0x0430)}, FIXME: may be IntelPCCameraPro BRIDGE_SPCA505 */ + {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 000000000..754ab1dbb --- /dev/null +++ b/drivers/media/usb/gspca/spca508.c @@ -0,0 +1,1526 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * SPCA508 chip based cameras subdriver + * + * Copyright (C) 2009 Jean-Francois Moine <http://moinejf.free.fr> + */ + +#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); + gspca_dbg(gspca_dev, D_USBO, "reg write i:0x%04x = 0x%02x\n", + 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 */ + gspca_dbg(gspca_dev, D_USBI, "reg read i:%04x --> %02x\n", + 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) { + gspca_err(gspca_dev, "ssi_w busy %02x\n", + 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); + gspca_dbg(gspca_dev, D_PROBE, "Webcam Vendor ID: 0x%02x%02x\n", + data2, data1); + + data1 = reg_read(gspca_dev, 0x8106); + data2 = reg_read(gspca_dev, 0x8107); + gspca_dbg(gspca_dev, D_PROBE, "Webcam Product ID: 0x%02x%02x\n", + data2, data1); + + data1 = reg_read(gspca_dev, 0x8621); + gspca_dbg(gspca_dev, D_PROBE, "Window 1 average luminance: %d\n", + 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 000000000..431527ed6 --- /dev/null +++ b/drivers/media/usb/gspca/spca561.c @@ -0,0 +1,911 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Sunplus spca561 subdriver + * + * Copyright (C) 2004 Michel Xhaard mxhaard@magic.fr + * + * V4L2 by Jean-Francois Moine <http://moinejf.free.fr> + */ + +#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); + gspca_dbg(gspca_dev, D_USBO, "reg write: 0x%02x:0x%02x\n", + 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) { + gspca_dbg(gspca_dev, D_PROBE, "Bad vendor / product from device\n"); + 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) +{ + gspca_dbg(gspca_dev, D_STREAM, "Chip revision: 012a\n"); + init_161rev12A(gspca_dev); + return 0; +} +static int sd_init_72a(struct gspca_dev *gspca_dev) +{ + gspca_dbg(gspca_dev, D_STREAM, "Chip revision: 072a\n"); + 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 */ + static const 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; */ + + if (y < luma_mean - luma_delta || + y > luma_mean + luma_delta) { + expotimes = i2c_read(gspca_dev, 0x09, 0x10); + pixelclk = 0x0800; + expotimes = expotimes & 0x07ff; + gainG = i2c_read(gspca_dev, 0x35, 0x10); + + expotimes += (luma_mean - y) >> spring; + gainG += (luma_mean - y) / 50; + + 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) { + gspca_err(gspca_dev, "Short SOF packet, ignoring\n\n\n\n\n"); + 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 000000000..32504ebcf --- /dev/null +++ b/drivers/media/usb/gspca/sq905.c @@ -0,0 +1,428 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * SQ905 subdriver + * + * Copyright (C) 2008, 2009 Adam Baker and Theodore Kilgore + */ + +/* + * History and Acknowledgments + * + * The original Linux driver for SQ905 based cameras was written by + * Marcell Lengyel and further 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_rcvctrlpipe(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 = 0; + + 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); + 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; + gspca_dbg(gspca_dev, D_PACK, + "Got %d bytes out of %d for frame\n", + 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; + gspca_dbg(gspca_dev, D_CONF, "SQ905 camera ID %08x detected\n", 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: */ + gspca_dbg(gspca_dev, D_STREAM, "Start streaming at high resolution\n"); + ret = sq905_command(&dev->gspca_dev, SQ905_CAPTURE_HIGH); + break; + case 1: + gspca_dbg(gspca_dev, D_STREAM, "Start streaming at medium resolution\n"); + ret = sq905_command(&dev->gspca_dev, SQ905_CAPTURE_MED); + break; + case 0: + gspca_dbg(gspca_dev, D_STREAM, "Start streaming at low resolution\n"); + ret = sq905_command(&dev->gspca_dev, SQ905_CAPTURE_LOW); + } + + if (ret < 0) { + gspca_err(gspca_dev, "Start streaming command failed\n"); + return ret; + } + /* Start the workqueue function to do the streaming */ + dev->work_thread = create_singlethread_workqueue(MODULE_NAME); + if (!dev->work_thread) + return -ENOMEM; + + 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 000000000..6ca947aef --- /dev/null +++ b/drivers/media/usb/gspca/sq905c.c @@ -0,0 +1,330 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * SQ905C subdriver + * + * Copyright (C) 2009 Theodore Kilgore + */ + +/* + * + * 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); + 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); + gspca_dbg(gspca_dev, D_STREAM, + "Got %d bytes out of %d for header\n", + 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); + gspca_dbg(gspca_dev, D_STREAM, "bytes_left = 0x%x\n", + 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; + gspca_dbg(gspca_dev, D_STREAM, + "Got %d bytes out of %d for frame\n", + 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; + + gspca_dbg(gspca_dev, D_PROBE, + "SQ9050 camera detected (vid/pid 0x%04X:0x%04X)\n", + id->idVendor, id->idProduct); + + ret = sq905c_command(gspca_dev, SQ905C_GET_ID, 0); + if (ret < 0) { + gspca_err(gspca_dev, "Get version command failed\n"); + return ret; + } + + ret = sq905c_read(gspca_dev, 0xf5, 0, 20); + if (ret < 0) { + gspca_err(gspca_dev, "Reading version command failed\n"); + return ret; + } + /* Note we leave out the usb id and the manufacturing date */ + gspca_dbg(gspca_dev, D_PROBE, + "SQ9050 ID string: %02x - %*ph\n", + 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) +{ + /* connect to the camera and reset it. */ + return sq905c_command(gspca_dev, SQ905C_CLEAR, 0); +} + +/* 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: + gspca_dbg(gspca_dev, D_STREAM, "Start streaming at high resolution\n"); + dev->cap_mode++; + ret = sq905c_command(gspca_dev, SQ905C_CAPTURE_HI, + SQ905C_CAPTURE_INDEX); + break; + default: /* 320 */ + gspca_dbg(gspca_dev, D_STREAM, "Start streaming at medium resolution\n"); + ret = sq905c_command(gspca_dev, SQ905C_CAPTURE_MED, + SQ905C_CAPTURE_INDEX); + } + + if (ret < 0) { + gspca_err(gspca_dev, "Start streaming command failed\n"); + return ret; + } + /* Start the workqueue function to do the streaming */ + dev->work_thread = create_singlethread_workqueue(MODULE_NAME); + if (!dev->work_thread) + return -ENOMEM; + + 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 000000000..c3610247a --- /dev/null +++ b/drivers/media/usb/gspca/sq930x.c @@ -0,0 +1,1156 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * 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> + */ + +#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; + /* + * Make sure the buffer is zeroed to avoid uninitialized + * values. + */ + memset(gspca_dev->usb_buf, 0, USB_BUF_SZ); + } +} + +static void reg_w(struct gspca_dev *gspca_dev, u16 value, u16 index) +{ + int ret; + + if (gspca_dev->usb_err < 0) + return; + gspca_dbg(gspca_dev, D_USBO, "reg_w v: %04x i: %04x\n", 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; + gspca_dbg(gspca_dev, D_USBO, "reg_wb v: %04x i: %04x %02x...%02x\n", + 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; + } + + gspca_dbg(gspca_dev, D_USBO, "i2c_w v: %04x i: %04x %02x...%02x\n", + 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) { + gspca_err(gspca_dev, "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) + gspca_dbg(gspca_dev, D_USBO, "ucbus v: %04x i: %04x %02x...%02x\n", + val, idx, + gspca_dev->usb_buf[0], buf[-1]); + else + gspca_dbg(gspca_dev, D_USBO, "ucbus v: %04x i: %04x\n", + 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) { + gspca_dbg(gspca_dev, D_PROBE, "mt9v111_init timeout\n"); + 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 + */ + gspca_dbg(gspca_dev, D_PROBE, "info: %*ph\n", 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) { + gspca_dbg(gspca_dev, D_PROBE, "Sensor %s\n", + 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_KERNEL); + 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 000000000..aa9e921e7 --- /dev/null +++ b/drivers/media/usb/gspca/stk014.c @@ -0,0 +1,434 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Syntek DV4000 (STK014) subdriver + * + * Copyright (C) 2008 Jean-Francois Moine (http://moinejf.free.fr) + */ + +#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) + gspca_dbg(gspca_dev, D_STREAM, "camera started alt: 0x%02x\n", + 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); + gspca_dbg(gspca_dev, D_STREAM, "camera stopped\n"); +} + +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 000000000..1a602af73 --- /dev/null +++ b/drivers/media/usb/gspca/stk1135.c @@ -0,0 +1,676 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Syntek STK1135 subdriver + * + * Copyright (c) 2013 Ondrej Zary + * + * Based on Syntekdriver (stk11xx) by Nicolas VIVIEN: + * http://syntekdriver.sourceforge.net + */ + +#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); + + gspca_dbg(gspca_dev, D_USBI, "reg_r 0x%x=0x%02x\n", + 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); + gspca_dbg(gspca_dev, D_USBO, "reg_w 0x%x:=0x%02x\n", 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) + gspca_dbg(gspca_dev, D_STREAM, "camera started alt: 0x%02x\n", + 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); + + gspca_dbg(gspca_dev, D_STREAM, "camera stopped\n"); +} + +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) { + gspca_dbg(gspca_dev, D_PACK, "received short packet (less than 4 bytes)\n"); + 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) { + gspca_dbg(gspca_dev, D_PACK, "received out-of-sequence packet\n"); + /* 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 000000000..9a77500cc --- /dev/null +++ b/drivers/media/usb/gspca/stk1135.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * STK1135 registers + * + * Copyright (c) 2013 Ondrej Zary + */ + +#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 000000000..b23988d8c --- /dev/null +++ b/drivers/media/usb/gspca/stv0680.c @@ -0,0 +1,339 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * 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. + */ + +#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; + 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 */ + gspca_err(gspca_dev, "last error: %i, command = 0x%x\n", + 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) { + gspca_err(gspca_dev, "Get_Camera_Mode failed\n"); + 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) { + gspca_err(gspca_dev, "Set_Camera_Mode failed\n"); + return stv0680_handle_error(gspca_dev, -EIO); + } + + /* Verify we got what we've asked for */ + if (stv0680_get_video_mode(gspca_dev) != mode) { + gspca_err(gspca_dev, "Error setting camera video mode!\n"); + 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) { + gspca_err(gspca_dev, "STV(e): camera ping failed!!\n"); + 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) { + gspca_err(gspca_dev, "Could not get descriptor 0200\n"); + 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)) { + gspca_err(gspca_dev, "Camera supports neither CIF nor QVGA mode\n"); + return -ENODEV; + } + if (gspca_dev->usb_buf[7] & 0x01) + gspca_dbg(gspca_dev, D_PROBE, "Camera supports CIF mode\n"); + if (gspca_dev->usb_buf[7] & 0x02) + gspca_dbg(gspca_dev, D_PROBE, "Camera supports VGA mode\n"); + if (gspca_dev->usb_buf[7] & 0x04) + gspca_dbg(gspca_dev, D_PROBE, "Camera supports QCIF mode\n"); + if (gspca_dev->usb_buf[7] & 0x08) + gspca_dbg(gspca_dev, D_PROBE, "Camera supports QVGA mode\n"); + + if (gspca_dev->usb_buf[7] & 0x01) + sd->video_mode = 0x00; /* CIF */ + else + sd->video_mode = 0x03; /* QVGA */ + + /* FW rev, ASIC rev, sensor ID */ + gspca_dbg(gspca_dev, D_PROBE, "Firmware rev is %i.%i\n", + gspca_dev->usb_buf[0], gspca_dev->usb_buf[1]); + gspca_dbg(gspca_dev, D_PROBE, "ASIC rev is %i.%i", + gspca_dev->usb_buf[2], gspca_dev->usb_buf[3]); + gspca_dbg(gspca_dev, 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 000000000..59373659a --- /dev/null +++ b/drivers/media/usb/gspca/stv06xx/Kconfig @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0-only +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 000000000..c4d7206e0 --- /dev/null +++ b/drivers/media/usb/gspca/stv06xx/Makefile @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0 +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 000000000..d9bc2aacc --- /dev/null +++ b/drivers/media/usb/gspca/stv06xx/stv06xx.c @@ -0,0 +1,647 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * 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 + * + * 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); + + gspca_dbg(gspca_dev, D_CONF, "Written 0x%x to address 0x%x, status: %d\n", + 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]; + + gspca_dbg(gspca_dev, D_CONF, "Reading 0x%x from address 0x%x, status %d\n", + *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; + + gspca_dbg(gspca_dev, D_CONF, "I2C: Command buffer contains %d entries\n", + 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]; + gspca_dbg(gspca_dev, D_CONF, "I2C: Writing 0x%02x to reg 0x%02x\n", + 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; + + gspca_dbg(gspca_dev, D_CONF, "I2C: Command buffer contains %d entries\n", + 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; + gspca_dbg(gspca_dev, D_CONF, "I2C: Writing 0x%04x to reg 0x%02x\n", + 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]; + + gspca_dbg(gspca_dev, D_CONF, "I2C: Read 0x%x from address 0x%x, status: %d\n", + *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; + + gspca_dbg(gspca_dev, D_PROBE, "Initializing camera\n"); + + /* 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; + + gspca_dbg(gspca_dev, D_PROBE, "Initializing controls\n"); + + 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) { + gspca_err(gspca_dev, "Couldn't get altsetting\n"); + return -EIO; + } + + if (alt->desc.bNumEndpoints < 1) + return -ENODEV; + + 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) + gspca_dbg(gspca_dev, D_STREAM, "Starting stream failed\n"); + else + gspca_dbg(gspca_dev, D_STREAM, "Started streaming\n"); + + return (err < 0) ? err : 0; +} + +static int stv06xx_isoc_init(struct gspca_dev *gspca_dev) +{ + struct usb_interface_cache *intfc; + struct usb_host_interface *alt; + struct sd *sd = (struct sd *) gspca_dev; + + intfc = gspca_dev->dev->actconfig->intf_cache[0]; + + if (intfc->num_altsetting < 2) + return -ENODEV; + + alt = &intfc->altsetting[1]; + + if (alt->desc.bNumEndpoints < 1) + return -ENODEV; + + /* Start isoc bandwidth "negotiation" at max isoc bandwidth */ + 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; + + /* + * Existence of altsetting and endpoint was verified in + * stv06xx_isoc_init() + */ + 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) + gspca_err(gspca_dev, "set alt 1 err %d\n", 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) + gspca_dbg(gspca_dev, D_STREAM, "Failed to stop stream\n"); + else + gspca_dbg(gspca_dev, D_STREAM, "Stopped streaming\n"); +} + +/* + * 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; + + gspca_dbg(gspca_dev, D_PACK, "Packet of length %d arrived\n", len); + + /* A packet may contain several frames + loop until the whole packet is reached */ + while (len) { + int id, chunk_len; + + if (len < 4) { + gspca_dbg(gspca_dev, D_PACK, "Packet is smaller than 4 bytes\n"); + return; + } + + /* Capture the id */ + id = (data[0] << 8) | data[1]; + + /* Capture the chunk length */ + chunk_len = (data[2] << 8) | data[3]; + gspca_dbg(gspca_dev, D_PACK, "Chunk id: %x, length: %d\n", + id, chunk_len); + + data += 4; + len -= 4; + + if (len < chunk_len) { + gspca_err(gspca_dev, "URB packet length is smaller than the specified chunk length\n"); + 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: + gspca_dbg(gspca_dev, D_PACK, "Frame data packet detected\n"); + + 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: + gspca_dbg(gspca_dev, D_PACK, "Starting new frame\n"); + + /* 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) + gspca_err(gspca_dev, "Chunk length is non-zero on a SOF\n"); + break; + + case 0x8002: + case 0x8006: + case 0xc002: + gspca_dbg(gspca_dev, D_PACK, "End of frame detected\n"); + + /* Complete the last frame (if any) */ + gspca_frame_add(gspca_dev, LAST_PACKET, + NULL, 0); + + if (chunk_len) + gspca_err(gspca_dev, "Chunk length is non-zero on a EOF\n"); + break; + + case 0x0005: + gspca_dbg(gspca_dev, D_PACK, "Chunk 0x005 detected\n"); + /* Unknown chunk with 11 bytes of data, + occurs just before end of each frame + in compressed mode */ + break; + + case 0x0100: + gspca_dbg(gspca_dev, D_PACK, "Chunk 0x0100 detected\n"); + /* Unknown chunk with 2 bytes of data, + occurs 2-3 times per USB interrupt */ + break; + case 0x42ff: + gspca_dbg(gspca_dev, D_PACK, "Chunk 0x42ff detected\n"); + /* Special chunk seen sometimes on the ST6422 */ + break; + default: + gspca_dbg(gspca_dev, D_PACK, "Unknown chunk 0x%04x detected\n", + 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); + +static void stv06xx_probe_error(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *)gspca_dev; + + kfree(sd->sensor_priv); + sd->sensor_priv = NULL; +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .config = stv06xx_config, + .init = stv06xx_init, + .init_controls = stv06xx_init_controls, + .probe_error = stv06xx_probe_error, + .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; + + gspca_dbg(gspca_dev, D_PROBE, "Configuring camera\n"); + + 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[] = { + {USB_DEVICE(0x046d, 0x0840), .driver_info = BRIDGE_STV600 }, /* QuickCam Express */ + {USB_DEVICE(0x046d, 0x0850), .driver_info = BRIDGE_STV610 }, /* LEGO cam / QuickCam Web */ + {USB_DEVICE(0x046d, 0x0870), .driver_info = BRIDGE_STV602 }, /* Dexxa WebCam USB */ + {USB_DEVICE(0x046D, 0x08F0), .driver_info = BRIDGE_ST6422 }, /* QuickCam Messenger */ + {USB_DEVICE(0x046D, 0x08F5), .driver_info = BRIDGE_ST6422 }, /* QuickCam Communicate */ + {USB_DEVICE(0x046D, 0x08F6), .driver_info = BRIDGE_ST6422 }, /* QuickCam Messenger (new) */ + {} +}; +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; + gspca_dbg(gspca_dev, D_PROBE, "Disconnecting the stv06xx device\n"); + + 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 000000000..f6bafa982 --- /dev/null +++ b/drivers/media/usb/gspca/stv06xx/stv06xx.h @@ -0,0 +1,103 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * 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 + * + * 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 000000000..5a47dcbf1 --- /dev/null +++ b/drivers/media/usb/gspca/stv06xx/stv06xx_hdcs.c @@ -0,0 +1,531 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * 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 + * + * 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; + } + gspca_dbg(gspca_dev, D_CONF, "Writing exposure %d, rowexp %d, srowexp %d\n", + 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) +{ + gspca_dbg(gspca_dev, D_CONF, "Writing gain %d\n", 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; + + gspca_dbg(gspca_dev, D_STREAM, "Starting stream\n"); + + return hdcs_set_state(sd, HDCS_STATE_RUN); +} + +static int hdcs_stop(struct sd *sd) +{ + struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; + + gspca_dbg(gspca_dev, D_STREAM, "Halting stream\n"); + + 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 000000000..326a6eb68 --- /dev/null +++ b/drivers/media/usb/gspca/stv06xx/stv06xx_hdcs.h @@ -0,0 +1,193 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * 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 + * + * 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 000000000..ae382b3b5 --- /dev/null +++ b/drivers/media/usb/gspca/stv06xx/stv06xx_pb0100.c @@ -0,0 +1,432 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * 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 + * + * 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; + + if (alt->desc.bNumEndpoints < 1) + 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)); + gspca_dbg(gspca_dev, D_STREAM, "Started stream, status: %d\n", 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)); + + gspca_dbg(gspca_dev, D_STREAM, "Halting stream\n"); +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); + gspca_dbg(gspca_dev, D_CONF, "Set green gain to %d, status: %d\n", + 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); + gspca_dbg(gspca_dev, D_CONF, "Set red gain to %d, status: %d\n", + 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); + gspca_dbg(gspca_dev, D_CONF, "Set blue gain to %d, status: %d\n", + 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); + gspca_dbg(gspca_dev, D_CONF, "Set exposure to %d, status: %d\n", + 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); + gspca_dbg(gspca_dev, D_CONF, "Set autogain to %d (natural: %d), status: %d\n", + 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); + + gspca_dbg(gspca_dev, D_CONF, "Set autogain target to %d, status: %d\n", + 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 000000000..c5a661457 --- /dev/null +++ b/drivers/media/usb/gspca/stv06xx/stv06xx_pb0100.h @@ -0,0 +1,131 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * 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 + * + * 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 000000000..67c13c689 --- /dev/null +++ b/drivers/media/usb/gspca/stv06xx/stv06xx_sensor.h @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * 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 + * + * 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 000000000..aac19d449 --- /dev/null +++ b/drivers/media/usb/gspca/stv06xx/stv06xx_st6422.c @@ -0,0 +1,273 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * 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 + */ + +#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; + + static 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; + + gspca_dbg(gspca_dev, D_STREAM, "Halting stream\n"); + + 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 000000000..68ba01c5a --- /dev/null +++ b/drivers/media/usb/gspca/stv06xx/stv06xx_st6422.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * 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 + */ + +#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 000000000..dde1950bd --- /dev/null +++ b/drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.c @@ -0,0 +1,265 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * 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 + * + * 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); + + 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) { + gspca_dbg(gspca_dev, D_CONF, "Enabling subsampling\n"); + 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; + + gspca_dbg(gspca_dev, D_STREAM, "Starting stream\n"); + + 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; + + gspca_dbg(gspca_dev, D_STREAM, "Halting stream\n"); + + 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; + + gspca_dbg(gspca_dev, D_CONF, "Set horizontal flip to %d\n", 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; + + gspca_dbg(gspca_dev, D_CONF, "Set vertical flip to %d\n", 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; + + gspca_dbg(gspca_dev, D_CONF, "Set analog gain to %d\n", 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); + + gspca_dbg(gspca_dev, D_CONF, "Set coarse exposure to %d, fine exposure to %d\n", + 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 000000000..343ddc304 --- /dev/null +++ b/drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.h @@ -0,0 +1,242 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * 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 + * + * 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 000000000..971dee0a5 --- /dev/null +++ b/drivers/media/usb/gspca/sunplus.c @@ -0,0 +1,1073 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Sunplus spca504(abc) spca533 spca536 library + * Copyright (C) 2005 Michel Xhaard mxhaard@magic.fr + * + * V4L2 by Jean-Francois Moine <http://moinejf.free.fr> + */ + +#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) { + gspca_err(gspca_dev, "reg_r: buffer overflow\n"); + return; + } + if (len == 0) { + gspca_err(gspca_dev, "reg_r: zero-length read\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, + gspca_dev->usb_buf, len, + 500); + if (ret < 0) { + pr_err("reg_r err %d\n", ret); + gspca_dev->usb_err = ret; + /* + * Make sure the buffer is zeroed to avoid uninitialized + * values. + */ + memset(gspca_dev->usb_buf, 0, USB_BUF_SZ); + } +} + +/* 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; + } + gspca_dbg(gspca_dev, D_USBO, "reg_w_riv: 0x%02x,0x%04x:0x%04x\n", + 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); + gspca_dbg(gspca_dev, D_FRAM, "before wait 0x%04x\n", + gspca_dev->usb_buf[0]); + reg_w_riv(gspca_dev, req, idx, val); + + msleep(200); + reg_r(gspca_dev, 0x01, 0x0001, 1); + gspca_dbg(gspca_dev, D_FRAM, "after wait 0x%04x\n", + 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]; + } + gspca_dbg(gspca_dev, D_STREAM, + "Read info: %d %d %d %d %d %d. Should be 1,0,2,2,0,0\n", + 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; + gspca_dbg(gspca_dev, D_FRAM, "Status 0x%02x Need 0x%02x\n", + 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) { + gspca_dbg(gspca_dev, D_FRAM, "status 0x%04x after wait %d\n", + 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); + gspca_dbg(gspca_dev, D_STREAM, "FirmWare: %d %d %d %d %d\n", + 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; + fallthrough; + 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); + fallthrough; + 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 */ + gspca_dbg(gspca_dev, D_STREAM, "Opening SPCA504 (PC-CAM 600)\n"); + 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: */ + gspca_dbg(gspca_dev, D_STREAM, "Opening SPCA504\n"); + 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_w_riv(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 000000000..33b37ccf1 --- /dev/null +++ b/drivers/media/usb/gspca/t613.c @@ -0,0 +1,1041 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * T613 subdriver + * + * Copyright (C) 2010 Jean-Francois Moine (http://moinejf.free.fr) + * + *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_array(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) +{ + gspca_dbg(gspca_dev, D_CONF, "Gamma: %d\n", 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 needed, because + * they are overridden by setbrigthness, setcontrast, etc., + * but won't 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: + gspca_dbg(gspca_dev, D_PROBE, "sensor tas5130a\n"); + sd->sensor = SENSOR_TAS5130A; + break; + case 0x0802: + gspca_dbg(gspca_dev, D_PROBE, "sensor lt168g\n"); + sd->sensor = SENSOR_LT168G; + break; + case 0x0803: + gspca_dbg(gspca_dev, D_PROBE, "sensor 'other'\n"); + sd->sensor = SENSOR_OTHER; + break; + case 0x0807: + gspca_dbg(gspca_dev, D_PROBE, "sensor om6802\n"); + 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]); + gspca_dbg(gspca_dev, D_STREAM, "Reg 0x%02x = 0x%02x\n", + 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); + gspca_dbg(gspca_dev, D_STREAM, "Reg 0x%02x = 0x%02x\n", 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); + gspca_dbg(gspca_dev, D_STREAM, "Reg 0x%02x = 0x%02x\n", 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}; + + gspca_dbg(gspca_dev, D_STREAM, "[Sensor requires polling]\n"); + 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 don't 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 000000000..aee444042 --- /dev/null +++ b/drivers/media/usb/gspca/topro.c @@ -0,0 +1,4972 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * 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) + */ + +#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 + +#define DEFAULT_FRAME_RATE 30 + +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 */ + gspca_dbg(gspca_dev, D_STREAM, "q %d -> %d\n", 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 = DEFAULT_FRAME_RATE; + 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); + gspca_dbg(gspca_dev, D_PROBE, "gpio: %02x\n", 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) { + gspca_dbg(gspca_dev, D_FRAM, "bad frame\n"); + 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; + + 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 = DEFAULT_FRAME_RATE; + 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 000000000..dde311c25 --- /dev/null +++ b/drivers/media/usb/gspca/touptek.c @@ -0,0 +1,727 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * 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 + */ + +#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 there's 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) { + gspca_err(gspca_dev, "reply has error %d\n", rc); + return -EIO; + } + if (rc != 1) { + gspca_err(gspca_dev, "Bad reply size %d\n", rc); + return -EIO; + } + if (reply[0] != 0x08) { + gspca_err(gspca_dev, "Bad reply 0x%02x\n", (int)reply[0]); + return -EIO; + } + return 0; +} + +static void reg_w(struct gspca_dev *gspca_dev, u16 value, u16 index) +{ + char *buff = gspca_dev->usb_buf; + int rc; + + gspca_dbg(gspca_dev, D_USBO, + "reg_w bReq=0x0B, bReqT=0xC0, wVal=0x%04X, wInd=0x%04X\n\n", + value, index); + rc = usb_control_msg(gspca_dev->dev, usb_rcvctrlpipe(gspca_dev->dev, 0), + 0x0B, 0xC0, value, index, buff, 1, 500); + gspca_dbg(gspca_dev, D_USBO, "rc=%d, ret={0x%02x}\n", rc, (int)buff[0]); + if (rc < 0) { + gspca_err(gspca_dev, "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)) { + gspca_err(gspca_dev, "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 { + gspca_err(gspca_dev, "Invalid width %u\n", w); + gspca_dev->usb_err = -EINVAL; + return; + } + gspca_dbg(gspca_dev, D_STREAM, "exposure: 0x%04X ms\n\n", value); + /* Wonder if there's 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); + gspca_dbg(gspca_dev, D_STREAM, "gain G1/G2 (0x%04X): 0x%04X (src 0x%04X)\n\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) { + gspca_dbg(gspca_dev, D_STREAM, "Truncating blue 0x%04X w/ value 0x%04X\n\n", + GAIN_MAX, normalized); + normalized = GAIN_MAX; + } + normalized = gainify(normalized); + gspca_dbg(gspca_dev, D_STREAM, "gain B (0x%04X): 0x%04X w/ source 0x%04X\n\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) { + gspca_dbg(gspca_dev, D_STREAM, "Truncating gain 0x%04X w/ value 0x%04X\n\n", + GAIN_MAX, normalized); + normalized = GAIN_MAX; + } + normalized = gainify(normalized); + gspca_dbg(gspca_dev, D_STREAM, "gain R (0x%04X): 0x%04X w / source 0x%04X\n\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; + + gspca_dbg(gspca_dev, D_STREAM, "configure_wh\n\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 { + gspca_err(gspca_dev, "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 { + gspca_err(gspca_dev, "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}, + }; + + gspca_dbg(gspca_dev, D_STREAM, "Encrypted begin, w = %u\n\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); + + gspca_dbg(gspca_dev, D_STREAM, "Encrypted end\n\n"); +} + +static int configure(struct gspca_dev *gspca_dev) +{ + int rc; + char *buff = gspca_dev->usb_buf; + + gspca_dbg(gspca_dev, D_STREAM, "configure()\n\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)) { + gspca_err(gspca_dev, "failed key req\n"); + 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) { + gspca_err(gspca_dev, "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) { + gspca_err(gspca_dev, "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) { + gspca_err(gspca_dev, "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) { + gspca_err(gspca_dev, "failed to replay final packet w/ rc %d\n", + rc); + return rc; + } + + gspca_dbg(gspca_dev, D_STREAM, "Configure complete\n\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) { + gspca_err(gspca_dev, "Failed configure\n"); + 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); + gspca_dbg(gspca_dev, D_FRAM, "finish frame sz %u/%u w/ len %u\n\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); + gspca_dbg(gspca_dev, D_FRAM, "abort frame sz %u/%u w/ len %u\n\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) { + gspca_err(gspca_dev, "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 000000000..c31baa72c --- /dev/null +++ b/drivers/media/usb/gspca/tv8532.c @@ -0,0 +1,365 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Quickcam cameras initialization data + * + * V4L2 by Jean-Francois Moine <http://moinejf.free.fr> + */ +#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 000000000..e7a534be0 --- /dev/null +++ b/drivers/media/usb/gspca/vc032x.c @@ -0,0 +1,3838 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * 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 + */ + +#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_NoFlicker[][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; + /* + * Make sure the buffer is zeroed to avoid uninitialized + * values. + */ + memset(gspca_dev->usb_buf, 0, USB_BUF_SZ); + } +} +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) + gspca_dbg(gspca_dev, D_USBI, "GET %02x 0001 %04x %02x\n", + req, index, + gspca_dev->usb_buf[0]); + else + gspca_dbg(gspca_dev, D_USBI, "GET %02x 0001 %04x %*ph\n", + 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; + gspca_dbg(gspca_dev, D_USBO, "SET %02x %04x %04x\n", 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) + gspca_dbg(gspca_dev, D_PROBE, "Read Sensor %02x%02x %02x\n", + 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); + gspca_dbg(gspca_dev, D_PROBE, "vc032%d check sensor header %02x\n", + 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) { + gspca_dbg(gspca_dev, D_PROBE, "Sensor ID %04x (%d)\n", + 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) + gspca_dbg(gspca_dev, D_USBO, "i2c_w %02x %02x\n", reg, *val); + else + gspca_dbg(gspca_dev, D_USBO, "i2c_w %02x %02x%02x\n", + 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: + gspca_dbg(gspca_dev, D_PROBE, "Find Sensor HV7131R\n"); + break; + case SENSOR_MI0360: + gspca_dbg(gspca_dev, D_PROBE, "Find Sensor MI0360\n"); + sd->bridge = BRIDGE_VC0323; + break; + case SENSOR_MI1310_SOC: + gspca_dbg(gspca_dev, D_PROBE, "Find Sensor MI1310_SOC\n"); + break; + case SENSOR_MI1320: + gspca_dbg(gspca_dev, D_PROBE, "Find Sensor MI1320\n"); + break; + case SENSOR_MI1320_SOC: + gspca_dbg(gspca_dev, D_PROBE, "Find Sensor MI1320_SOC\n"); + break; + case SENSOR_OV7660: + gspca_dbg(gspca_dev, D_PROBE, "Find Sensor OV7660\n"); + break; + case SENSOR_OV7670: + gspca_dbg(gspca_dev, D_PROBE, "Find Sensor OV7670\n"); + break; + case SENSOR_PO1200: + gspca_dbg(gspca_dev, D_PROBE, "Find Sensor PO1200\n"); + break; + case SENSOR_PO3130NC: + gspca_dbg(gspca_dev, D_PROBE, "Find Sensor PO3130NC\n"); + break; + case SENSOR_POxxxx: + gspca_dbg(gspca_dev, D_PROBE, "Sensor POxxxx\n"); + 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_NoFlicker, 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) { + gspca_dbg(gspca_dev, D_PACK, + "vc032x header packet found len %d\n", 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->pixfmt.sizeimage; + 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 000000000..d98343fd3 --- /dev/null +++ b/drivers/media/usb/gspca/vicam.c @@ -0,0 +1,347 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * 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) + */ + +#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; +}; + +/* 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); + 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 *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; + + schedule_work(&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 */ + flush_work(&dev->work_struct); + 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 000000000..79baa0c1a --- /dev/null +++ b/drivers/media/usb/gspca/w996Xcf.c @@ -0,0 +1,569 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * 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> + */ + +/* 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); + + /* Avoid things going to fast for the bridge with a xhci host */ + udelay(150); + 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; + + /* Avoid things going to fast for the bridge with a xhci host */ + udelay(150); + + /* 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; + + /* Avoid things going to fast for the bridge with a xhci host */ + udelay(150); + + /* 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; + /* + * Make sure the buffer is zeroed to avoid uninitialized + * values. + */ + memset(sd->gspca_dev.usb_buf, 0, 2); + } + + 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)) { + gspca_dbg(gspca_dev, D_USBI, "Did not receive i2c ACK\n"); + 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); + + gspca_dbg(gspca_dev, D_USBO, "i2c 0x%02x -> [0x%02x]\n", 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; + gspca_dbg(gspca_dev, D_USBI, "i2c [0x%02X] -> 0x%02X\n", + reg, value); + } else + gspca_err(gspca_dev, "i2c read [0x%02x] failed\n", 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 arithmetic */ + #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 000000000..acfb9a195 --- /dev/null +++ b/drivers/media/usb/gspca/xirlink_cit.c @@ -0,0 +1,3149 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * 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 + */ + +#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) + gspca_dbg(gspca_dev, D_PROBE, "Register %04x value: %02x\n", + 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; + static 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); + fallthrough; + 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; + } + + if (alt->desc.bNumEndpoints < 1) + return -ENODEV; + + 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--; + + gspca_dbg(gspca_dev, D_PROBE, + "PacketSize: %d, res: %dx%d -> using clockdiv: %d (%d fps)\n", + 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_interface_cache *intfc; + 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; + } + + intfc = gspca_dev->dev->actconfig->intf_cache[0]; + + if (intfc->num_altsetting < 2) + return -ENODEV; + + alt = &intfc->altsetting[1]; + + if (alt->desc.bNumEndpoints < 1) + return -ENODEV; + + /* Start isoc bandwidth "negotiation" at max isoc bandwidth */ + 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; + } + + /* + * Existence of altsetting and endpoint was verified in sd_isoc_init() + */ + 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); + fallthrough; + 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) + gspca_dbg(gspca_dev, D_FRAM, + "header found at offset: %d: %02x %02x 00 %3ph\n\n", + i - 1, + data[i - 4], + data[i - 3], + &data[i]); + else + gspca_dbg(gspca_dev, D_FRAM, + "header found at offset: %d: 00 %3ph\n\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 000000000..ab8f44a4f --- /dev/null +++ b/drivers/media/usb/gspca/zc3xx-reg.h @@ -0,0 +1,250 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * zc030x registers + * + * Copyright (c) 2008 Mauro Carvalho Chehab <mchehab@kernel.org> + * + * The register aliases used here came from this driver: + * http://zc0302.sourceforge.net/zc0302.php + */ + +/* 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 retrieving */ +#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 000000000..5bcbf0d40 --- /dev/null +++ b/drivers/media/usb/gspca/zc3xx.c @@ -0,0 +1,7029 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * 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 + */ + +#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; + + 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_NoFlicker[] = { + {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_NoFlickerScale[] = { + {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_NoFlicker[] = { + {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_NoFlicker[] = { + {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_NoFlicker[] = { + {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_NoFlicker[] = { /* 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_NoFlickerScale[] = { /* 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_NoFlicker[] = { + {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_NoFlickerScale[] = { + {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_NoFlickerScale[] = { + {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_NoFlicker[] = { + {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_NoFlicker[] = { + {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_NoFlickerScale[] = { + {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[] = { + {0xdd, 0x00, 0x0100}, /* 00,01,00,dd */ + {0xaa, 0x2b, 0x0096}, /* 00,2b,96,aa */ + /* enable 1/120s & 1/100s exposures for banding filter */ + {0xaa, 0x75, 0x008e}, + {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, 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[] = { + {0xdd, 0x00, 0x0100}, /* 00,01,00,dd */ + {0xaa, 0x2b, 0x0000}, /* 00,2b,00,aa */ + /* enable 1/120s & 1/100s exposures for banding filter */ + {0xaa, 0x75, 0x008e}, + {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, 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_NoFlicker[] = { + {0xdd, 0x00, 0x0100}, /* 00,01,00,dd */ + {0xaa, 0x2b, 0x0000}, /* 00,2b,00,aa */ + /* disable 1/120s & 1/100s exposures for banding filter */ + {0xaa, 0x75, 0x008a}, + {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 retrieving */ + {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 retrieving */ + {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_NoFlicker[] = { + {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_NoFlicker[] = { + {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_NoFlickerScale[] = { + {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_AENoFlicker[] = { + {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_AENoFlickerScale[] = { + {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_AENoFlicker[] = { + {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_AENoFlickerScale[] = { + {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_NoFlicker[] = { + {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_NoFlickerScale[] = { + {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_NoFlicker[] = { + {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_NoFlicker[] = { + {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_NoFlickerScale[] = { + {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_NoFlicker[] = { + {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_NoFlickerScale[] = { + {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) +{ + struct sd *sd = (struct sd *) gspca_dev; + + switch (sd->sensor) { + case SENSOR_HV7131R: + return (i2c_read(gspca_dev, 0x25) << 9) + | (i2c_read(gspca_dev, 0x26) << 1) + | (i2c_read(gspca_dev, 0x27) >> 7); + case SENSOR_OV7620: + return i2c_read(gspca_dev, 0x10); + default: + return -1; + } +} + +static void setexposure(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + switch (sd->sensor) { + case SENSOR_HV7131R: + i2c_write(gspca_dev, 0x25, val >> 9, 0x00); + i2c_write(gspca_dev, 0x26, val >> 1, 0x00); + i2c_write(gspca_dev, 0x27, val << 7, 0x00); + break; + case SENSOR_OV7620: + i2c_write(gspca_dev, 0x10, val, 0x00); + break; + } +} + +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 Flicker (for outdoor 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_NoFlicker, adcm2700_NoFlicker, + adcm2700_50HZ, adcm2700_50HZ, + adcm2700_60HZ, adcm2700_60HZ}, + [SENSOR_CS2102] = { + cs2102_NoFlicker, cs2102_NoFlickerScale, + cs2102_50HZ, cs2102_50HZScale, + cs2102_60HZ, cs2102_60HZScale}, + [SENSOR_CS2102K] = { + cs2102_NoFlicker, cs2102_NoFlickerScale, + NULL, NULL, /* currently disabled */ + NULL, NULL}, + [SENSOR_GC0303] = { + gc0303_NoFlicker, gc0303_NoFlickerScale, + gc0303_50HZ, gc0303_50HZScale, + gc0303_60HZ, gc0303_60HZScale}, + [SENSOR_GC0305] = { + gc0305_NoFlicker, gc0305_NoFlicker, + gc0305_50HZ, gc0305_50HZ, + gc0305_60HZ, gc0305_60HZ}, + [SENSOR_HDCS2020] = { + hdcs2020_NoFlicker, hdcs2020_NoFlicker, + hdcs2020_50HZ, hdcs2020_50HZ, + hdcs2020_60HZ, hdcs2020_60HZ}, + [SENSOR_HV7131B] = { + hv7131b_NoFlicker, hv7131b_NoFlickerScale, + hv7131b_50HZ, hv7131b_50HZScale, + hv7131b_60HZ, hv7131b_60HZScale}, + [SENSOR_HV7131R] = { + hv7131r_NoFlicker, hv7131r_NoFlickerScale, + hv7131r_50HZ, hv7131r_50HZScale, + hv7131r_60HZ, hv7131r_60HZScale}, + [SENSOR_ICM105A] = { + icm105a_NoFlicker, icm105a_NoFlickerScale, + icm105a_50HZ, icm105a_50HZScale, + icm105a_60HZ, icm105a_60HZScale}, + [SENSOR_MC501CB] = { + mc501cb_NoFlicker, mc501cb_NoFlickerScale, + mc501cb_50HZ, mc501cb_50HZScale, + mc501cb_60HZ, mc501cb_60HZScale}, + [SENSOR_MT9V111_1] = { + mt9v111_1_AENoFlicker, mt9v111_1_AENoFlickerScale, + mt9v111_1_AE50HZ, mt9v111_1_AE50HZScale, + mt9v111_1_AE60HZ, mt9v111_1_AE60HZScale}, + [SENSOR_MT9V111_3] = { + mt9v111_3_AENoFlicker, mt9v111_3_AENoFlickerScale, + mt9v111_3_AE50HZ, mt9v111_3_AE50HZScale, + mt9v111_3_AE60HZ, mt9v111_3_AE60HZScale}, + [SENSOR_OV7620] = { + ov7620_NoFlicker, ov7620_NoFlicker, + ov7620_50HZ, ov7620_50HZ, + ov7620_60HZ, ov7620_60HZ}, + [SENSOR_OV7630C] = { + NULL, NULL, + NULL, NULL, + NULL, NULL}, + [SENSOR_PAS106] = { + pas106b_NoFlicker, pas106b_NoFlicker, + pas106b_50HZ, pas106b_50HZ, + pas106b_60HZ, pas106b_60HZ}, + [SENSOR_PAS202B] = { + pas202b_NoFlicker, pas202b_NoFlickerScale, + pas202b_50HZ, pas202b_50HZScale, + pas202b_60HZ, pas202b_60HZScale}, + [SENSOR_PB0330] = { + pb0330_NoFlicker, pb0330_NoFlickerScale, + pb0330_50HZ, pb0330_50HZScale, + pb0330_60HZ, pb0330_60HZScale}, + [SENSOR_PO2030] = { + po2030_NoFlicker, po2030_NoFlicker, + po2030_50HZ, po2030_50HZ, + po2030_60HZ, po2030_60HZ}, + [SENSOR_TAS5130C] = { + tas5130c_NoFlicker, tas5130c_NoFlickerScale, + 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) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->sensor == SENSOR_OV7620) + i2c_write(gspca_dev, 0x13, val ? 0xa3 : 0x80, 0x00); + else + 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); + gspca_dbg(gspca_dev, D_PROBE, "probe sif 0x%04x\n", 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); + gspca_dbg(gspca_dev, D_PROBE, "probe 2wr ov vga 0x%04x\n", 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); + gspca_dbg(gspca_dev, D_PROBE, "probe 3wr vga 1 0x%04x\n", 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) { + gspca_dbg(gspca_dev, D_PROBE, "probe 3wr vga type 0a\n"); + 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) { + gspca_dbg(gspca_dev, D_PROBE, "probe 3wr vga type %02x\n", + 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 */ + gspca_dbg(gspca_dev, D_PROBE, "probe 3wr vga 2 0x%04x\n", retword); + if (retword == 0x2030) { + u8 retbyte; + + retbyte = i2c_read(gspca_dev, 0x02); /* revision number */ + gspca_dbg(gspca_dev, D_PROBE, "sensor PO2030 rev 0x%02x\n", + 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) { + gspca_dbg(gspca_dev, D_PROBE, "probe 3wr vga type 0a ? ret: %04x\n", + 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); + else if (sd->sensor == SENSOR_OV7620) + sd->exposure = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops, + V4L2_CID_EXPOSURE, 0, 255, 1, 0x41); + 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 || sd->sensor == SENSOR_OV7620) + 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) + gspca_dbg(gspca_dev, D_PROBE, "probe sensor -> %04x\n", sensor); + if ((unsigned) force_sensor < SENSOR_MAX) { + sd->sensor = force_sensor; + gspca_dbg(gspca_dev, D_PROBE, "sensor forced to %d\n", + force_sensor); + } else { + switch (sensor) { + case -1: + switch (sd->sensor) { + case SENSOR_MC501CB: + gspca_dbg(gspca_dev, D_PROBE, "Sensor MC501CB\n"); + break; + case SENSOR_GC0303: + gspca_dbg(gspca_dev, D_PROBE, "Sensor GC0303\n"); + 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); + gspca_dbg(gspca_dev, D_PROBE, "Sensor hv7131 type %d\n", + sensor); + switch (sensor) { + case 0: /* hv7131b */ + case 1: /* hv7131e */ + gspca_dbg(gspca_dev, D_PROBE, "Find Sensor HV7131B\n"); + sd->sensor = SENSOR_HV7131B; + break; + default: +/* case 2: * hv7131r */ + gspca_dbg(gspca_dev, D_PROBE, "Find Sensor HV7131R\n"); + sd->sensor = SENSOR_HV7131R; + break; + } + break; + case 0x02: + gspca_dbg(gspca_dev, D_PROBE, "Sensor TAS5130C\n"); + sd->sensor = SENSOR_TAS5130C; + break; + case 0x04: + gspca_dbg(gspca_dev, D_PROBE, "Find Sensor CS2102\n"); + sd->sensor = SENSOR_CS2102; + break; + case 0x08: + gspca_dbg(gspca_dev, D_PROBE, "Find Sensor HDCS2020\n"); + sd->sensor = SENSOR_HDCS2020; + break; + case 0x0a: + gspca_dbg(gspca_dev, D_PROBE, + "Find Sensor PB0330. Chip revision %x\n", + sd->chip_revision); + sd->sensor = SENSOR_PB0330; + break; + case 0x0c: + gspca_dbg(gspca_dev, D_PROBE, "Find Sensor ICM105A\n"); + sd->sensor = SENSOR_ICM105A; + break; + case 0x0e: + gspca_dbg(gspca_dev, D_PROBE, "Find Sensor PAS202B\n"); + sd->sensor = SENSOR_PAS202B; + break; + case 0x0f: + gspca_dbg(gspca_dev, D_PROBE, "Find Sensor PAS106\n"); + sd->sensor = SENSOR_PAS106; + break; + case 0x10: + case 0x12: + gspca_dbg(gspca_dev, D_PROBE, "Find Sensor TAS5130C\n"); + sd->sensor = SENSOR_TAS5130C; + break; + case 0x11: + gspca_dbg(gspca_dev, D_PROBE, "Find Sensor HV7131R\n"); + sd->sensor = SENSOR_HV7131R; + break; + case 0x13: + case 0x15: + gspca_dbg(gspca_dev, D_PROBE, + "Sensor MT9V111. Chip revision %04x\n", + sd->chip_revision); + sd->sensor = sd->bridge == BRIDGE_ZC301 + ? SENSOR_MT9V111_1 + : SENSOR_MT9V111_3; + break; + case 0x14: + gspca_dbg(gspca_dev, D_PROBE, + "Find Sensor CS2102K?. Chip revision %x\n", + sd->chip_revision); + sd->sensor = SENSOR_CS2102K; + break; + case 0x16: + gspca_dbg(gspca_dev, D_PROBE, "Find Sensor ADCM2700\n"); + sd->sensor = SENSOR_ADCM2700; + break; + case 0x29: + gspca_dbg(gspca_dev, D_PROBE, "Find Sensor GC0305\n"); + sd->sensor = SENSOR_GC0305; + break; + case 0x0303: + gspca_dbg(gspca_dev, D_PROBE, "Sensor GC0303\n"); + sd->sensor = SENSOR_GC0303; + break; + case 0x2030: + gspca_dbg(gspca_dev, D_PROBE, "Find Sensor PO2030\n"); + sd->sensor = SENSOR_PO2030; + break; + case 0x7620: + gspca_dbg(gspca_dev, D_PROBE, "Find Sensor OV7620\n"); + sd->sensor = SENSOR_OV7620; + break; + case 0x7631: + gspca_dbg(gspca_dev, D_PROBE, "Find Sensor OV7630C\n"); + sd->sensor = SENSOR_OV7630C; + break; + case 0x7648: + gspca_dbg(gspca_dev, D_PROBE, "Find Sensor OV7648\n"); + 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); + fallthrough; + 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); + fallthrough; + 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 */ + schedule_work(&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; + + mutex_unlock(&gspca_dev->usb_lock); + flush_work(&sd->work); + mutex_lock(&gspca_dev->usb_lock); + 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!!!"); |