summaryrefslogtreecommitdiffstats
path: root/drivers/media/video/gspca/sonixj.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/video/gspca/sonixj.c')
-rw-r--r--drivers/media/video/gspca/sonixj.c1505
1 files changed, 1505 insertions, 0 deletions
diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c
new file mode 100644
index 000000000000..33a3df1f6915
--- /dev/null
+++ b/drivers/media/video/gspca/sonixj.c
@@ -0,0 +1,1505 @@
+/*
+ * Sonix sn9c102p sn9c105 sn9c120 (jpeg) library
+ * Copyright (C) 2005 Michel Xhaard mxhaard@magic.fr
+ *
+ * V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define MODULE_NAME "sonixj"
+
+#include "gspca.h"
+#include "jpeg.h"
+
+MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
+MODULE_DESCRIPTION("GSPCA/SONIX JPEG USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* specific webcam descriptor */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+
+ int avg_lum;
+ unsigned int exposure;
+
+ unsigned short brightness;
+ unsigned char contrast;
+ unsigned char colors;
+ unsigned char autogain;
+
+ signed char ag_cnt;
+#define AG_CNT_START 13
+
+ char qindex;
+ unsigned char bridge;
+#define BRIDGE_SN9C102P 0
+#define BRIDGE_SN9C105 1
+#define BRIDGE_SN9C110 2
+#define BRIDGE_SN9C120 3
+#define BRIDGE_SN9C325 4
+ char sensor; /* Type of image sensor chip */
+#define SENSOR_HV7131R 0
+#define SENSOR_MI0360 1
+#define SENSOR_MO4000 2
+#define SENSOR_OV7648 3
+#define SENSOR_OV7660 4
+ unsigned char i2c_base;
+};
+
+/* V4L2 controls supported by the driver */
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
+
+static struct ctrl sd_ctrls[] = {
+ {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Brightness",
+ .minimum = 0,
+ .maximum = 0xffff,
+ .step = 1,
+#define BRIGHTNESS_DEF 0x7fff
+ .default_value = BRIGHTNESS_DEF,
+ },
+ .set = sd_setbrightness,
+ .get = sd_getbrightness,
+ },
+ {
+ {
+ .id = V4L2_CID_CONTRAST,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Contrast",
+ .minimum = 0,
+ .maximum = 127,
+ .step = 1,
+#define CONTRAST_DEF 63
+ .default_value = CONTRAST_DEF,
+ },
+ .set = sd_setcontrast,
+ .get = sd_getcontrast,
+ },
+ {
+ {
+ .id = V4L2_CID_SATURATION,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Color",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+#define COLOR_DEF 127
+ .default_value = COLOR_DEF,
+ },
+ .set = sd_setcolors,
+ .get = sd_getcolors,
+ },
+ {
+ {
+ .id = V4L2_CID_AUTOGAIN,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Auto Gain",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+#define AUTOGAIN_DEF 1
+ .default_value = AUTOGAIN_DEF,
+ },
+ .set = sd_setautogain,
+ .get = sd_getautogain,
+ },
+};
+
+static struct v4l2_pix_format vga_mode[] = {
+ {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 160,
+ .sizeimage = 160 * 120 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 2},
+ {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 1},
+ {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 0},
+};
+
+/*Data from sn9c102p+hv71331r */
+static const __u8 sn_hv7131[] = {
+/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 reg9 */
+ 0x00, 0x03, 0x64, 0x00, 0x1A, 0x20, 0x20, 0x20, 0xA1, 0x11,
+/* rega regb regc regd rege regf reg10 reg11 */
+ 0x02, 0x09, 0x00, 0x00, 0x00, 0x10, 0x03, 0x00, /* 00 */
+/* reg12 reg13 reg14 reg15 reg16 reg17 reg18 reg19 reg1a reg1b */
+ 0x00, 0x01, 0x03, 0x28, 0x1e, 0x41, 0x0a, 0x00, 0x00, 0x00,
+/* reg1c reg1d reg1e reg1f reg20 reg21 reg22 reg23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static const __u8 sn_mi0360[] = {
+/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 reg9 */
+ 0x00, 0x61, 0x44, 0x00, 0x1a, 0x20, 0x20, 0x20, 0xb1, 0x5d,
+/* rega regb regc regd rege regf reg10 reg11 */
+ 0x07, 0x00, 0x00, 0x00, 0x00, 0x10, 0x03, 0x00,
+/* reg12 reg13 reg14 reg15 reg16 reg17 reg18 reg19 reg1a reg1b */
+ 0x00, 0x02, 0x0a, 0x28, 0x1e, 0x61, 0x06, 0x00, 0x00, 0x00,
+/* reg1c reg1d reg1e reg1f reg20 reg21 reg22 reg23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static const __u8 sn_mo4000[] = {
+/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 */
+ 0x12, 0x23, 0x60, 0x00, 0x1A, 0x00, 0x20, 0x18, 0x81,
+/* reg9 rega regb regc regd rege regf reg10 reg11*/
+ 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00,
+/* reg12 reg13 reg14 reg15 reg16 reg17 reg18 reg19 reg1a*/
+ 0x0b, 0x0f, 0x14, 0x28, 0x1e, 0x40, 0x08, 0x00, 0x00,
+/* reg1b reg1c reg1d reg1e reg1f reg20 reg21 reg22 reg23*/
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x25, 0x39, 0x4b,
+ 0x5c, 0x6b, 0x79, 0x87, 0x95, 0xa2, 0xaf, 0xbb, 0xc7,
+ 0xd3, 0xdf, 0xea, 0xf5
+};
+
+static const __u8 sn_ov7648[] = {
+ 0x00, 0x21, 0x62, 0x00, 0x1a, 0x20, 0x20, 0x20, 0xA1, 0x6E, 0x18, 0x65,
+ 0x00, 0x00, 0x00, 0x10, 0x03, 0x00, 0x00, 0x06, 0x06, 0x28, 0x1E, 0x82,
+ 0x07, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static const __u8 sn_ov7660[] = {
+/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 */
+ 0x00, 0x61, 0x40, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x81,
+/* reg9 rega regb regc regd rege regf reg10 reg11*/
+ 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00,
+/* reg12 reg13 reg14 reg15 reg16 reg17 reg18 reg19 reg1a*/
+ 0x01, 0x01, 0x14, 0x28, 0x1e, 0x00, 0x07, 0x00, 0x00,
+/* reg1b reg1c reg1d reg1e reg1f reg20 reg21 reg22 reg23*/
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/* sequence specific to the sensors - !! index = SENSOR_xxx */
+static const __u8 *sn_tb[] = {
+ sn_hv7131,
+ sn_mi0360,
+ sn_mo4000,
+ sn_ov7648,
+ sn_ov7660
+};
+
+static const __u8 regsn20[] = {
+ 0x00, 0x2d, 0x46, 0x5a, 0x6c, 0x7c, 0x8b, 0x99,
+ 0xa6, 0xb2, 0xbf, 0xca, 0xd5, 0xe0, 0xeb, 0xf5, 0xff
+};
+static const __u8 regsn20_sn9c120[] = {
+ 0x00, 0x25, 0x3c, 0x50, 0x62, 0x72, 0x81, 0x90,
+ 0x9e, 0xab, 0xb8, 0xc5, 0xd1, 0xdd, 0xe9, 0xf4, 0xff
+};
+static const __u8 regsn20_sn9c325[] = {
+ 0x0a, 0x3a, 0x56, 0x6c, 0x7e, 0x8d, 0x9a, 0xa4,
+ 0xaf, 0xbb, 0xc5, 0xcd, 0xd5, 0xde, 0xe8, 0xed, 0xf5
+};
+
+static const __u8 reg84[] = {
+ 0x14, 0x00, 0x27, 0x00, 0x07, 0x00, 0xe5, 0x0f,
+ 0xe4, 0x0f, 0x38, 0x00, 0x3e, 0x00, 0xc3, 0x0f,
+/* 0x00, 0x00, 0x00, 0x00, 0x00 */
+ 0xf7, 0x0f, 0x0a, 0x00, 0x00
+};
+static const __u8 reg84_sn9c120_1[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0c, 0x00, 0x00
+};
+static const __u8 reg84_sn9c120_2[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0c, 0x02, 0x3b
+};
+static const __u8 reg84_sn9c120_3[] = {
+ 0x14, 0x00, 0x27, 0x00, 0x08, 0x00, 0xeb, 0x0f,
+ 0xd5, 0x0f, 0x42, 0x00, 0x41, 0x00, 0xca, 0x0f,
+ 0xf5, 0x0f, 0x0c, 0x02, 0x3b
+};
+static const __u8 reg84_sn9c325[] = {
+ 0x14, 0x00, 0x27, 0x00, 0x07, 0x00, 0xe4, 0x0f,
+ 0xd3, 0x0f, 0x4b, 0x00, 0x48, 0x00, 0xc0, 0x0f,
+ 0xf8, 0x0f, 0x00, 0x00, 0x00
+};
+
+static const __u8 hv7131r_sensor_init[][8] = {
+ {0xC1, 0x11, 0x01, 0x08, 0x01, 0x00, 0x00, 0x10},
+ {0xB1, 0x11, 0x34, 0x17, 0x7F, 0x00, 0x00, 0x10},
+ {0xD1, 0x11, 0x40, 0xFF, 0x7F, 0x7F, 0x7F, 0x10},
+ {0x91, 0x11, 0x44, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xD1, 0x11, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xD1, 0x11, 0x14, 0x01, 0xE2, 0x02, 0x82, 0x10},
+ {0x91, 0x11, 0x18, 0x00, 0x00, 0x00, 0x00, 0x10},
+
+ {0xA1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10},
+ {0xA1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10},
+ {0xC1, 0x11, 0x25, 0x00, 0x61, 0xA8, 0x00, 0x10},
+ {0xA1, 0x11, 0x30, 0x22, 0x00, 0x00, 0x00, 0x10},
+ {0xC1, 0x11, 0x31, 0x20, 0x2E, 0x20, 0x00, 0x10},
+ {0xC1, 0x11, 0x25, 0x00, 0xC3, 0x50, 0x00, 0x10},
+ {0xA1, 0x11, 0x30, 0x07, 0x00, 0x00, 0x00, 0x10}, /* gain14 */
+ {0xC1, 0x11, 0x31, 0x10, 0x10, 0x10, 0x00, 0x10}, /* r g b 101a10 */
+
+ {0xA1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10},
+ {0xA1, 0x11, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xA1, 0x11, 0x21, 0xD0, 0x00, 0x00, 0x00, 0x10},
+ {0xA1, 0x11, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xA1, 0x11, 0x23, 0x09, 0x00, 0x00, 0x00, 0x10},
+
+ {0xA1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10},
+ {0xA1, 0x11, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xA1, 0x11, 0x21, 0xD0, 0x00, 0x00, 0x00, 0x10},
+ {0xA1, 0x11, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xA1, 0x11, 0x23, 0x10, 0x00, 0x00, 0x00, 0x10},
+ {}
+};
+static const __u8 mi0360_sensor_init[][8] = {
+ {0xB1, 0x5D, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10},
+ {0xB1, 0x5D, 0x0D, 0x00, 0x01, 0x00, 0x00, 0x10},
+ {0xB1, 0x5D, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xD1, 0x5D, 0x01, 0x00, 0x08, 0x00, 0x16, 0x10},
+ {0xD1, 0x5D, 0x03, 0x01, 0xE2, 0x02, 0x82, 0x10},
+ {0xD1, 0x5D, 0x05, 0x00, 0x09, 0x00, 0x53, 0x10},
+ {0xB1, 0x5D, 0x0D, 0x00, 0x02, 0x00, 0x00, 0x10},
+ {0xD1, 0x5D, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xD1, 0x5D, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xD1, 0x5D, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xD1, 0x5D, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xD1, 0x5D, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xD1, 0x5D, 0x14, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xD1, 0x5D, 0x16, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xD1, 0x5D, 0x18, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xD1, 0x5D, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xD1, 0x5D, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xB1, 0x5D, 0x32, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xD1, 0x5D, 0x20, 0x91, 0x01, 0x00, 0x00, 0x10},
+ {0xD1, 0x5D, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xD1, 0x5D, 0x24, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xD1, 0x5D, 0x26, 0x00, 0x00, 0x00, 0x24, 0x10},
+ {0xD1, 0x5D, 0x2F, 0xF7, 0xB0, 0x00, 0x04, 0x10},
+ {0xD1, 0x5D, 0x31, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xD1, 0x5D, 0x33, 0x00, 0x00, 0x01, 0x00, 0x10},
+ {0xB1, 0x5D, 0x3D, 0x06, 0x8F, 0x00, 0x00, 0x10},
+ {0xD1, 0x5D, 0x40, 0x01, 0xE0, 0x00, 0xD1, 0x10},
+ {0xB1, 0x5D, 0x44, 0x00, 0x82, 0x00, 0x00, 0x10},
+ {0xD1, 0x5D, 0x58, 0x00, 0x78, 0x00, 0x43, 0x10},
+ {0xD1, 0x5D, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xD1, 0x5D, 0x5C, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xD1, 0x5D, 0x5E, 0x00, 0x00, 0xA3, 0x1D, 0x10},
+ {0xB1, 0x5D, 0x62, 0x04, 0x11, 0x00, 0x00, 0x10},
+
+ {0xB1, 0x5D, 0x20, 0x91, 0x01, 0x00, 0x00, 0x10},
+ {0xB1, 0x5D, 0x20, 0x11, 0x01, 0x00, 0x00, 0x10},
+ {0xB1, 0x5D, 0x09, 0x00, 0x64, 0x00, 0x00, 0x10},
+ {0xD1, 0x5D, 0x2B, 0x00, 0xA0, 0x00, 0xB0, 0x10},
+ {0xD1, 0x5D, 0x2D, 0x00, 0xA0, 0x00, 0xA0, 0x10},
+
+ {0xB1, 0x5D, 0x0A, 0x00, 0x02, 0x00, 0x00, 0x10}, /* sensor clck ?2 */
+ {0xB1, 0x5D, 0x06, 0x00, 0x30, 0x00, 0x00, 0x10},
+ {0xB1, 0x5D, 0x05, 0x00, 0x0A, 0x00, 0x00, 0x10},
+ {0xB1, 0x5D, 0x09, 0x02, 0x35, 0x00, 0x00, 0x10}, /* exposure 2 */
+
+ {0xD1, 0x5D, 0x2B, 0x00, 0xB9, 0x00, 0xE3, 0x10},
+ {0xD1, 0x5D, 0x2D, 0x00, 0x5f, 0x00, 0xB9, 0x10}, /* 42 */
+/* {0xB1, 0x5D, 0x35, 0x00, 0x67, 0x00, 0x00, 0x10}, * gain orig */
+/* {0xB1, 0x5D, 0x35, 0x00, 0x20, 0x00, 0x00, 0x10}, * gain */
+ {0xB1, 0x5D, 0x07, 0x00, 0x03, 0x00, 0x00, 0x10}, /* update */
+ {0xB1, 0x5D, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10}, /* sensor on */
+ {}
+};
+static const __u8 mo4000_sensor_init[][8] = {
+ {0xa1, 0x21, 0x01, 0x02, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x02, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x04, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x05, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x05, 0x04, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x06, 0x80, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x06, 0x81, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x11, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x11, 0x20, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x11, 0x30, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x11, 0x38, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x11, 0x38, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x0f, 0x20, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x10, 0x20, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x11, 0x38, 0x00, 0x00, 0x00, 0x10},
+ {}
+};
+static const __u8 ov7660_sensor_init[][8] = {
+ {0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, /* reset SCCB */
+/* (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},
+/* {0xd1, 0x21, 0x00, 0x01, 0x74, 0x74, 0x00, 0x10}, */
+ /* GAIN BLUE RED VREF */
+ {0xd1, 0x21, 0x04, 0x00, 0x7d, 0x62, 0x00, 0x10},
+ /* COM 1 BAVE GEAVE AECHH */
+ {0xb1, 0x21, 0x08, 0x83, 0x01, 0x00, 0x00, 0x10}, /* RAVE COM2 */
+ {0xd1, 0x21, 0x0c, 0x00, 0x08, 0x04, 0x4f, 0x10}, /* COM 3 4 5 6 */
+ {0xd1, 0x21, 0x10, 0x7f, 0x40, 0x05, 0xf8, 0x10},
+/* {0xd1, 0x21, 0x10, 0x7f, 0x40, 0x05, 0xff, 0x10}, */
+ /* AECH CLKRC COM7 COM8 */
+ {0xc1, 0x21, 0x14, 0x2c, 0x00, 0x02, 0x00, 0x10}, /* COM9 COM10 */
+ {0xd1, 0x21, 0x17, 0x10, 0x60, 0x02, 0x7b, 0x10},
+ /* HSTART HSTOP VSTRT VSTOP */
+ {0xa1, 0x21, 0x1b, 0x02, 0x00, 0x00, 0x00, 0x10}, /* PSHFT */
+ {0xb1, 0x21, 0x1e, 0x01, 0x0e, 0x00, 0x00, 0x10}, /* MVFP LAEC */
+ {0xd1, 0x21, 0x20, 0x07, 0x07, 0x07, 0x07, 0x10},
+ /* BOS GBOS GROS ROS (BGGR offset) */
+ {0xd1, 0x21, 0x24, 0x68, 0x58, 0xd4, 0x80, 0x10},
+/* {0xd1, 0x21, 0x24, 0x78, 0x68, 0xd4, 0x80, 0x10}, */
+ /* AEW AEB VPT BBIAS */
+ {0xd1, 0x21, 0x28, 0x80, 0x30, 0x00, 0x00, 0x10},
+ /* GbBIAS RSVD EXHCH EXHCL */
+ {0xd1, 0x21, 0x2c, 0x80, 0x00, 0x00, 0x62, 0x10},
+ /* RBIAS ADVFL ASDVFH YAVE */
+ {0xc1, 0x21, 0x30, 0x08, 0x30, 0xb4, 0x00, 0x10},
+ /* HSYST HSYEN HREF */
+ {0xd1, 0x21, 0x33, 0x00, 0x07, 0x84, 0x00, 0x10}, /* reserved */
+ {0xd1, 0x21, 0x37, 0x0c, 0x02, 0x43, 0x00, 0x10},
+ /* ADC ACOM OFON TSLB */
+ {0xd1, 0x21, 0x3b, 0x02, 0x6c, 0x19, 0x0e, 0x10},
+ /* COM11 COM12 COM13 COM14 */
+ {0xd1, 0x21, 0x3f, 0x41, 0xc1, 0x22, 0x08, 0x10},
+ /* EDGE COM15 COM16 COM17 */
+ {0xd1, 0x21, 0x43, 0xf0, 0x10, 0x78, 0xa8, 0x10}, /* reserved */
+ {0xd1, 0x21, 0x47, 0x60, 0x80, 0x00, 0x00, 0x10}, /* reserved */
+ {0xd1, 0x21, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x10}, /* reserved */
+ {0xd1, 0x21, 0x4f, 0x46, 0x36, 0x0f, 0x17, 0x10}, /* MTX 1 2 3 4 */
+ {0xd1, 0x21, 0x53, 0x7f, 0x96, 0x40, 0x40, 0x10}, /* MTX 5 6 7 8 */
+ {0xb1, 0x21, 0x57, 0x40, 0x0f, 0x00, 0x00, 0x10}, /* MTX9 MTXS */
+ {0xd1, 0x21, 0x59, 0xba, 0x9a, 0x22, 0xb9, 0x10}, /* reserved */
+ {0xd1, 0x21, 0x5d, 0x9b, 0x10, 0xf0, 0x05, 0x10}, /* reserved */
+ {0xa1, 0x21, 0x61, 0x60, 0x00, 0x00, 0x00, 0x10}, /* reserved */
+ {0xd1, 0x21, 0x62, 0x00, 0x00, 0x50, 0x30, 0x10},
+ /* LCC1 LCC2 LCC3 LCC4 */
+ {0xa1, 0x21, 0x66, 0x00, 0x00, 0x00, 0x00, 0x10}, /* LCC5 */
+ {0xd1, 0x21, 0x67, 0x80, 0x7a, 0x90, 0x80, 0x10},
+ {0xa1, 0x21, 0x6b, 0x0a, 0x00, 0x00, 0x00, 0x10},
+ /* band gap reference [0..3] DBLV */
+ {0xd1, 0x21, 0x6c, 0x30, 0x48, 0x80, 0x74, 0x10}, /* gamma curve */
+ {0xd1, 0x21, 0x70, 0x64, 0x60, 0x5c, 0x58, 0x10}, /* gamma curve */
+ {0xd1, 0x21, 0x74, 0x54, 0x4c, 0x40, 0x38, 0x10}, /* gamma curve */
+ {0xd1, 0x21, 0x78, 0x34, 0x30, 0x2f, 0x2b, 0x10}, /* gamma curve */
+ {0xd1, 0x21, 0x7c, 0x03, 0x07, 0x17, 0x34, 0x10}, /* gamma curve */
+ {0xd1, 0x21, 0x80, 0x41, 0x4d, 0x58, 0x63, 0x10}, /* gamma curve */
+ {0xd1, 0x21, 0x84, 0x6e, 0x77, 0x87, 0x95, 0x10}, /* gamma curve */
+ {0xc1, 0x21, 0x88, 0xaf, 0xc7, 0xdf, 0x00, 0x10}, /* gamma curve */
+ {0xc1, 0x21, 0x8b, 0x99, 0x99, 0xcf, 0x00, 0x10}, /* reserved */
+ {0xb1, 0x21, 0x92, 0x00, 0x00, 0x00, 0x00, 0x10},
+/****** (some exchanges in the win trace) ******/
+ {0xa1, 0x21, 0x1e, 0x01, 0x00, 0x00, 0x00, 0x10},
+ /* bits[3..0]reserved */
+ {0xa1, 0x21, 0x1e, 0x01, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10},
+ /* VREF vertical frame ctrl */
+ {0xa1, 0x21, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x10, 0x20, 0x00, 0x00, 0x00, 0x10}, /* 0x20 */
+ {0xa1, 0x21, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x10},
+/* {0xa1, 0x21, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x10}, */
+ {0xa1, 0x21, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x10},
+ {0xb1, 0x21, 0x01, 0x78, 0x78, 0x00, 0x00, 0x10},
+/****** (some exchanges in the win trace) ******/
+ {0xa1, 0x21, 0x93, 0x00, 0x00, 0x00, 0x00, 0x10},/* dummy line hight */
+ {0xa1, 0x21, 0x92, 0x25, 0x00, 0x00, 0x00, 0x10},/* dummy line low */
+ {0xa1, 0x21, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x02, 0x90, 0x00, 0x00, 0x00, 0x10},
+/****** (some exchanges in the win trace) ******/
+/**********startsensor KO if changed !!****/
+ {0xa1, 0x21, 0x93, 0x01, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x92, 0xff, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x2b, 0xc3, 0x00, 0x00, 0x00, 0x10},
+/* here may start the isoc exchanges */
+ {}
+};
+/* reg0x04 reg0x07 reg 0x10 */
+/* expo = (COM1 & 0x02) | (AECHH & 0x2f <<10) [ (AECh << 2) */
+
+static const __u8 ov7648_sensor_init[][8] = {
+ {0xC1, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00},
+ {0xC1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00},
+ {0xC1, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00},
+ {0xA1, 0x6E, 0x3F, 0x20, 0x00, 0x00, 0x00, 0x10},
+ {0xA1, 0x6E, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xA1, 0x6E, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xD1, 0x6E, 0x04, 0x02, 0xB1, 0x02, 0x39, 0x10},
+ {0xD1, 0x6E, 0x08, 0x00, 0x01, 0x00, 0x00, 0x10},
+ {0xD1, 0x6E, 0x0C, 0x02, 0x7F, 0x01, 0xE0, 0x10},
+ {0xD1, 0x6E, 0x12, 0x03, 0x02, 0x00, 0x03, 0x10},
+ {0xD1, 0x6E, 0x16, 0x85, 0x40, 0x4A, 0x40, 0x10},
+ {0xC1, 0x6E, 0x1A, 0x00, 0x80, 0x00, 0x00, 0x10},
+ {0xD1, 0x6E, 0x1D, 0x08, 0x03, 0x00, 0x00, 0x10},
+ {0xD1, 0x6E, 0x23, 0x00, 0xB0, 0x00, 0x94, 0x10},
+ {0xD1, 0x6E, 0x27, 0x58, 0x00, 0x00, 0x00, 0x10},
+ {0xD1, 0x6E, 0x2D, 0x14, 0x35, 0x61, 0x84, 0x10},
+ {0xD1, 0x6E, 0x31, 0xA2, 0xBD, 0xD8, 0xFF, 0x10},
+ {0xD1, 0x6E, 0x35, 0x06, 0x1E, 0x12, 0x02, 0x10},
+ {0xD1, 0x6E, 0x39, 0xAA, 0x53, 0x37, 0xD5, 0x10},
+ {0xA1, 0x6E, 0x3D, 0xF2, 0x00, 0x00, 0x00, 0x10},
+ {0xD1, 0x6E, 0x3E, 0x00, 0x00, 0x80, 0x03, 0x10},
+ {0xD1, 0x6E, 0x42, 0x03, 0x00, 0x00, 0x00, 0x10},
+ {0xC1, 0x6E, 0x46, 0x00, 0x80, 0x80, 0x00, 0x10},
+ {0xD1, 0x6E, 0x4B, 0x02, 0xEF, 0x08, 0xCD, 0x10},
+ {0xD1, 0x6E, 0x4F, 0x00, 0xD0, 0x00, 0xA0, 0x10},
+ {0xD1, 0x6E, 0x53, 0x01, 0xAA, 0x01, 0x40, 0x10},
+ {0xD1, 0x6E, 0x5A, 0x50, 0x04, 0x30, 0x03, 0x10},
+ {0xA1, 0x6E, 0x5E, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xD1, 0x6E, 0x5F, 0x10, 0x40, 0xFF, 0x00, 0x10},
+ /* {0xD1, 0x6E, 0x63, 0x40, 0x40, 0x00, 0x00, 0x10},
+ {0xD1, 0x6E, 0x67, 0x00, 0x00, 0x00, 0x00, 0x10},
+ * This is currently setting a
+ * blue tint, and some things more , i leave it here for future test if
+ * somene is having problems with color on this sensor
+ {0xD1, 0x6E, 0x6B, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xD1, 0x6E, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xC1, 0x6E, 0x73, 0x10, 0x80, 0xEB, 0x00, 0x10},
+ {0xA1, 0x6E, 0x1E, 0x03, 0x00, 0x00, 0x00, 0x10},
+ {0xA1, 0x6E, 0x15, 0x01, 0x00, 0x00, 0x00, 0x10},
+ {0xC1, 0x6E, 0x16, 0x40, 0x40, 0x40, 0x00, 0x10},
+ {0xA1, 0x6E, 0x1D, 0x08, 0x00, 0x00, 0x00, 0x10},
+ {0xA1, 0x6E, 0x06, 0x02, 0x00, 0x00, 0x00, 0x10},
+ {0xA1, 0x6E, 0x07, 0xB5, 0x00, 0x00, 0x00, 0x10},
+ {0xA1, 0x6E, 0x18, 0x6B, 0x00, 0x00, 0x00, 0x10},
+ {0xA1, 0x6E, 0x1D, 0x08, 0x00, 0x00, 0x00, 0x10},
+ {0xA1, 0x6E, 0x06, 0x02, 0x00, 0x00, 0x00, 0x10},
+ {0xA1, 0x6E, 0x07, 0xB8, 0x00, 0x00, 0x00, 0x10}, */
+ {0xC1, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00},
+ {0xA1, 0x6E, 0x06, 0x03, 0x00, 0x00, 0x00, 0x10}, /* Bright... */
+ {0xA1, 0x6E, 0x07, 0x66, 0x00, 0x00, 0x00, 0x10}, /* B.. */
+ {0xC1, 0x6E, 0x1A, 0x03, 0x65, 0x90, 0x00, 0x10}, /* Bright/Witen....*/
+/* {0xC1, 0x6E, 0x16, 0x45, 0x40, 0x60, 0x00, 0x10}, * Bright/Witene */
+ {}
+};
+
+static const __u8 qtable4[] = {
+ 0x06, 0x04, 0x04, 0x06, 0x04, 0x04, 0x06, 0x06, 0x06, 0x06, 0x08, 0x06,
+ 0x06, 0x08, 0x0A, 0x11,
+ 0x0A, 0x0A, 0x08, 0x08, 0x0A, 0x15, 0x0F, 0x0F, 0x0C, 0x11, 0x19, 0x15,
+ 0x19, 0x19, 0x17, 0x15,
+ 0x17, 0x17, 0x1B, 0x1D, 0x25, 0x21, 0x1B, 0x1D, 0x23, 0x1D, 0x17, 0x17,
+ 0x21, 0x2E, 0x21, 0x23,
+ 0x27, 0x29, 0x2C, 0x2C, 0x2C, 0x19, 0x1F, 0x30, 0x32, 0x2E, 0x29, 0x32,
+ 0x25, 0x29, 0x2C, 0x29,
+ 0x06, 0x08, 0x08, 0x0A, 0x08, 0x0A, 0x13, 0x0A, 0x0A, 0x13, 0x29, 0x1B,
+ 0x17, 0x1B, 0x29, 0x29,
+ 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+ 0x29, 0x29, 0x29, 0x29,
+ 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+ 0x29, 0x29, 0x29, 0x29,
+ 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+ 0x29, 0x29, 0x29, 0x29
+};
+
+/* read <len> bytes (len < sizeof gspca_dev->usb_buf) to gspca_dev->usb_buf */
+static void reg_r(struct gspca_dev *gspca_dev,
+ __u16 value, int len)
+{
+ usb_control_msg(gspca_dev->dev,
+ usb_rcvctrlpipe(gspca_dev->dev, 0),
+ 0,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+ value, 0,
+ gspca_dev->usb_buf, len,
+ 500);
+ PDEBUG(D_USBI, "reg_r [%02x] -> %02x", value, gspca_dev->usb_buf[0]);
+}
+
+static void reg_w1(struct gspca_dev *gspca_dev,
+ __u16 value,
+ __u8 data)
+{
+ PDEBUG(D_USBO, "reg_w1 [%02x] = %02x", value, data);
+ gspca_dev->usb_buf[0] = data;
+ 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);
+}
+static void reg_w(struct gspca_dev *gspca_dev,
+ __u16 value,
+ const __u8 *buffer,
+ int len)
+{
+ PDEBUG(D_USBO, "reg_w [%02x] = %02x %02x ..",
+ value, buffer[0], buffer[1]);
+ if (len <= sizeof gspca_dev->usb_buf) {
+ memcpy(gspca_dev->usb_buf, buffer, len);
+ usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ 0x08,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+ value, 0,
+ gspca_dev->usb_buf, len,
+ 500);
+ } else {
+ __u8 *tmpbuf;
+
+ tmpbuf = kmalloc(len, GFP_KERNEL);
+ memcpy(tmpbuf, buffer, len);
+ usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ 0x08,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+ value, 0,
+ tmpbuf, len,
+ 500);
+ kfree(tmpbuf);
+ }
+}
+
+/* I2C write 1 byte */
+static void i2c_w1(struct gspca_dev *gspca_dev, __u8 reg, __u8 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ PDEBUG(D_USBO, "i2c_w2 [%02x] = %02x", reg, val);
+ gspca_dev->usb_buf[0] = 0x81 | (2 << 4); /* = a1 */
+ gspca_dev->usb_buf[1] = sd->i2c_base;
+ 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;
+ 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);
+}
+
+/* I2C write 8 bytes */
+static void i2c_w8(struct gspca_dev *gspca_dev,
+ const __u8 *buffer)
+{
+ memcpy(gspca_dev->usb_buf, buffer, 8);
+ 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);
+}
+
+/* read 5 bytes in gspca_dev->usb_buf */
+static void i2c_r5(struct gspca_dev *gspca_dev, __u8 reg)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ __u8 mode[8];
+
+ mode[0] = 0x81 | 0x10;
+ mode[1] = sd->i2c_base;
+ mode[2] = reg;
+ mode[3] = 0;
+ mode[4] = 0;
+ mode[5] = 0;
+ mode[6] = 0;
+ mode[7] = 0x10;
+ i2c_w8(gspca_dev, mode);
+ msleep(2);
+ mode[0] = 0x81 | (5 << 4) | 0x02;
+ mode[2] = 0;
+ i2c_w8(gspca_dev, mode);
+ msleep(2);
+ reg_r(gspca_dev, 0x0a, 5);
+}
+
+static int probesensor(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ i2c_w1(gspca_dev, 0x02, 0); /* sensor wakeup */
+ msleep(10);
+ reg_w1(gspca_dev, 0x02, 0x66); /* Gpio on */
+ msleep(10);
+ i2c_r5(gspca_dev, 0); /* read sensor id */
+ if (gspca_dev->usb_buf[0] == 0x02
+ && gspca_dev->usb_buf[1] == 0x09
+ && gspca_dev->usb_buf[2] == 0x01
+ && gspca_dev->usb_buf[3] == 0x00
+ && gspca_dev->usb_buf[4] == 0x00) {
+ PDEBUG(D_PROBE, "Find Sensor sn9c102P HV7131R");
+ sd->sensor = SENSOR_HV7131R;
+ return SENSOR_HV7131R;
+ }
+ PDEBUG(D_PROBE, "Find Sensor 0x%02x 0x%02x 0x%02x",
+ gspca_dev->usb_buf[0], gspca_dev->usb_buf[1],
+ gspca_dev->usb_buf[2]);
+ PDEBUG(D_PROBE, "Sensor sn9c102P Not found");
+ return -ENODEV;
+}
+
+static int configure_gpio(struct gspca_dev *gspca_dev,
+ const __u8 *sn9c1xx)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ const __u8 *reg9a;
+ static const __u8 reg9a_def[] =
+ {0x08, 0x40, 0x20, 0x10, 0x00, 0x04};
+ static const __u8 reg9a_sn9c120[] = /* from win trace */
+ {0x00, 0x40, 0x38, 0x30, 0x00, 0x20};
+ static const __u8 reg9a_sn9c325[] =
+ {0x0a, 0x40, 0x38, 0x30, 0x00, 0x20};
+
+ reg_w1(gspca_dev, 0xf1, 0x00);
+ reg_w1(gspca_dev, 0x01, sn9c1xx[0]); /*fixme:jfm was [1] en v1*/
+
+ /* configure gpio */
+ reg_w(gspca_dev, 0x01, &sn9c1xx[1], 2);
+ reg_w(gspca_dev, 0x08, &sn9c1xx[8], 2);
+ reg_w(gspca_dev, 0x17, &sn9c1xx[0x17], 5); /* jfm len was 3 */
+ switch (sd->bridge) {
+ case BRIDGE_SN9C325:
+ reg9a = reg9a_sn9c325;
+ break;
+ case BRIDGE_SN9C120:
+ reg9a = reg9a_sn9c120;
+ break;
+ default:
+ reg9a = reg9a_def;
+ break;
+ }
+ reg_w(gspca_dev, 0x9a, reg9a, 6);
+
+ reg_w1(gspca_dev, 0xd4, 0x60); /*fixme:jfm 60 00 00 (3) ? */
+
+ reg_w(gspca_dev, 0x03, &sn9c1xx[3], 0x0f);
+
+ switch (sd->bridge) {
+ case BRIDGE_SN9C120: /* from win trace */
+ reg_w1(gspca_dev, 0x01, 0x61);
+ reg_w1(gspca_dev, 0x17, 0x20);
+ reg_w1(gspca_dev, 0x01, 0x60);
+ break;
+ case BRIDGE_SN9C325:
+ reg_w1(gspca_dev, 0x01, 0x43);
+ reg_w1(gspca_dev, 0x17, 0xae);
+ reg_w1(gspca_dev, 0x01, 0x42);
+ break;
+ default:
+ reg_w1(gspca_dev, 0x01, 0x43);
+ reg_w1(gspca_dev, 0x17, 0x61);
+ reg_w1(gspca_dev, 0x01, 0x42);
+ }
+
+ if (sd->sensor == SENSOR_HV7131R) {
+ if (probesensor(gspca_dev) < 0)
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static void hv7131R_InitSensor(struct gspca_dev *gspca_dev)
+{
+ int i = 0;
+ static const __u8 SetSensorClk[] = /* 0x08 Mclk */
+ { 0xa1, 0x11, 0x01, 0x18, 0x00, 0x00, 0x00, 0x10 };
+
+ while (hv7131r_sensor_init[i][0]) {
+ i2c_w8(gspca_dev, hv7131r_sensor_init[i]);
+ i++;
+ }
+ i2c_w8(gspca_dev, SetSensorClk);
+}
+
+static void mi0360_InitSensor(struct gspca_dev *gspca_dev)
+{
+ int i = 0;
+
+ while (mi0360_sensor_init[i][0]) {
+ i2c_w8(gspca_dev, mi0360_sensor_init[i]);
+ i++;
+ }
+}
+
+static void mo4000_InitSensor(struct gspca_dev *gspca_dev)
+{
+ int i = 0;
+
+ while (mo4000_sensor_init[i][0]) {
+ i2c_w8(gspca_dev, mo4000_sensor_init[i]);
+ i++;
+ }
+}
+
+static void ov7648_InitSensor(struct gspca_dev *gspca_dev)
+{
+ int i = 0;
+
+ while (ov7648_sensor_init[i][0]) {
+ i2c_w8(gspca_dev, ov7648_sensor_init[i]);
+ i++;
+ }
+}
+
+static void ov7660_InitSensor(struct gspca_dev *gspca_dev)
+{
+ int i = 0;
+
+ i2c_w8(gspca_dev, ov7660_sensor_init[i]); /* reset SCCB */
+ i++;
+ msleep(20);
+ while (ov7660_sensor_init[i][0]) {
+ i2c_w8(gspca_dev, ov7660_sensor_init[i]);
+ i++;
+ }
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct cam *cam;
+
+ cam = &gspca_dev->cam;
+ cam->epaddr = 0x01;
+ cam->cam_mode = vga_mode;
+ cam->nmodes = ARRAY_SIZE(vga_mode);
+
+ sd->bridge = id->driver_info >> 16;
+ sd->sensor = id->driver_info >> 8;
+ sd->i2c_base = id->driver_info;
+
+ sd->qindex = 4; /* set the quantization table */
+ sd->brightness = BRIGHTNESS_DEF;
+ sd->contrast = CONTRAST_DEF;
+ sd->colors = COLOR_DEF;
+ sd->autogain = AUTOGAIN_DEF;
+ return 0;
+}
+
+/* this function is called at open time */
+static int sd_open(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+/* const __u8 *sn9c1xx; */
+ __u8 regGpio[] = { 0x29, 0x74 };
+ __u8 regF1;
+
+ /* setup a selector by bridge */
+ reg_w1(gspca_dev, 0xf1, 0x01);
+ reg_r(gspca_dev, 0x00, 1); /* -> regF1 = 0x00 */
+ reg_w1(gspca_dev, 0xf1, gspca_dev->usb_buf[0]);
+ reg_r(gspca_dev, 0x00, 1);
+ regF1 = gspca_dev->usb_buf[0];
+ switch (sd->bridge) {
+ case BRIDGE_SN9C102P:
+ if (regF1 != 0x11)
+ return -ENODEV;
+ reg_w1(gspca_dev, 0x02, regGpio[1]);
+ break;
+ case BRIDGE_SN9C105:
+ if (regF1 != 0x11)
+ return -ENODEV;
+ reg_w(gspca_dev, 0x02, regGpio, 2);
+ break;
+ case BRIDGE_SN9C120:
+ if (regF1 != 0x12)
+ return -ENODEV;
+ regGpio[1] = 0x70;
+ reg_w(gspca_dev, 0x02, regGpio, 2);
+ break;
+ default:
+/* case BRIDGE_SN9C110: */
+/* case BRIDGE_SN9C325: */
+ if (regF1 != 0x12)
+ return -ENODEV;
+ reg_w1(gspca_dev, 0x02, 0x62);
+ break;
+ }
+
+ reg_w1(gspca_dev, 0xf1, 0x01);
+
+ return 0;
+}
+
+static unsigned int setexposure(struct gspca_dev *gspca_dev,
+ unsigned int expo)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ static const __u8 doit[] = /* update sensor */
+ { 0xb1, 0x5d, 0x07, 0x00, 0x03, 0x00, 0x00, 0x10 };
+ static const __u8 sensorgo[] = /* sensor on */
+ { 0xb1, 0x5d, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10 };
+ static const __u8 gainMo[] =
+ { 0xa1, 0x21, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1d };
+
+ switch (sd->sensor) {
+ case SENSOR_HV7131R: {
+ __u8 Expodoit[] =
+ { 0xc1, 0x11, 0x25, 0x07, 0x27, 0xc0, 0x00, 0x16 };
+
+ Expodoit[3] = expo >> 16;
+ Expodoit[4] = expo >> 8;
+ Expodoit[5] = expo;
+ i2c_w8(gspca_dev, Expodoit);
+ break;
+ }
+ case SENSOR_MI0360: {
+ __u8 expoMi[] = /* exposure 0x0635 -> 4 fp/s 0x10 */
+ { 0xb1, 0x5d, 0x09, 0x06, 0x35, 0x00, 0x00, 0x16 };
+
+ if (expo > 0x0635)
+ expo = 0x0635;
+ else if (expo < 0x0001)
+ expo = 0x0001;
+ expoMi[3] = expo >> 8;
+ expoMi[4] = expo;
+ i2c_w8(gspca_dev, expoMi);
+ i2c_w8(gspca_dev, doit);
+ i2c_w8(gspca_dev, sensorgo);
+ break;
+ }
+ case SENSOR_MO4000: {
+ __u8 expoMof[] =
+ { 0xa1, 0x21, 0x0f, 0x20, 0x00, 0x00, 0x00, 0x10 };
+ __u8 expoMo10[] =
+ { 0xa1, 0x21, 0x10, 0x20, 0x00, 0x00, 0x00, 0x10 };
+
+ if (expo > 0x1fff)
+ expo = 0x1fff;
+ else if (expo < 0x0001)
+ expo = 0x0001;
+ expoMof[3] = (expo & 0x03fc) >> 2;
+ i2c_w8(gspca_dev, expoMof);
+ expoMo10[3] = ((expo & 0x1c00) >> 10)
+ | ((expo & 0x0003) << 4);
+ i2c_w8(gspca_dev, expoMo10);
+ i2c_w8(gspca_dev, gainMo);
+ PDEBUG(D_CONF, "set exposure %d",
+ ((expoMo10[3] & 0x07) << 10)
+ | (expoMof[3] << 2)
+ | ((expoMo10[3] & 0x30) >> 4));
+ break;
+ }
+ }
+ return expo;
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ unsigned int expo;
+ __u8 k2;
+
+ switch (sd->sensor) {
+ case SENSOR_HV7131R:
+ expo = sd->brightness << 4;
+ if (expo > 0x002dc6c0)
+ expo = 0x002dc6c0;
+ else if (expo < 0x02a0)
+ expo = 0x02a0;
+ sd->exposure = setexposure(gspca_dev, expo);
+ break;
+ case SENSOR_MI0360:
+ expo = sd->brightness >> 4;
+ sd->exposure = setexposure(gspca_dev, expo);
+ break;
+ case SENSOR_MO4000:
+ expo = sd->brightness >> 4;
+ sd->exposure = setexposure(gspca_dev, expo);
+ break;
+ case SENSOR_OV7660:
+ return; /*jfm??*/
+ }
+
+ k2 = sd->brightness >> 10;
+ reg_w1(gspca_dev, 0x96, k2);
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ __u8 k2;
+ __u8 contrast[] = { 0x00, 0x00, 0x28, 0x00, 0x07, 0x00 };
+
+ if (sd->sensor == SENSOR_OV7660)
+ return; /*jfm??*/
+ k2 = sd->contrast;
+ contrast[2] = k2;
+ contrast[0] = (k2 + 1) >> 1;
+ contrast[4] = (k2 + 1) / 5;
+ reg_w(gspca_dev, 0x84, contrast, 6);
+}
+
+static void setcolors(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ __u8 data;
+ int colour;
+
+ colour = sd->colors - 128;
+ if (colour > 0)
+ data = (colour + 32) & 0x7f; /* blue */
+ else
+ data = (-colour + 32) & 0x7f; /* red */
+ reg_w1(gspca_dev, 0x05, data);
+}
+
+/* -- start the camera -- */
+static void sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i;
+ __u8 data;
+ __u8 reg1;
+ __u8 reg17;
+ const __u8 *sn9c1xx;
+ int mode;
+ static const __u8 C0[] = { 0x2d, 0x2d, 0x3a, 0x05, 0x04, 0x3f };
+ static const __u8 CA[] = { 0x28, 0xd8, 0x14, 0xec };
+ static const __u8 CA_sn9c120[] =
+ { 0x14, 0xec, 0x0a, 0xf6 }; /* SN9C120 */
+ static const __u8 CE[] = { 0x32, 0xdd, 0x2d, 0xdd }; /* MI0360 */
+ static const __u8 CE_sn9c325[] =
+ { 0x32, 0xdd, 0x32, 0xdd }; /* OV7648 - SN9C325 */
+
+ sn9c1xx = sn_tb[(int) sd->sensor];
+ configure_gpio(gspca_dev, sn9c1xx);
+
+/*fixme:jfm this sequence should appear at end of sd_start */
+/* with
+ reg_w1(gspca_dev, 0x01, 0x44); */
+ 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]);
+ reg_w1(gspca_dev, 0xd2, 0x6a); /* DC29 */
+ reg_w1(gspca_dev, 0xd3, 0x50);
+ reg_w1(gspca_dev, 0xc6, 0x00);
+ reg_w1(gspca_dev, 0xc7, 0x00);
+ reg_w1(gspca_dev, 0xc8, 0x50);
+ reg_w1(gspca_dev, 0xc9, 0x3c);
+/*fixme:jfm end of ending sequence */
+ reg_w1(gspca_dev, 0x18, sn9c1xx[0x18]);
+ switch (sd->bridge) {
+ case BRIDGE_SN9C325:
+ data = 0xae;
+ break;
+ case BRIDGE_SN9C120:
+ data = 0xa0;
+ break;
+ default:
+ data = 0x60;
+ break;
+ }
+ reg_w1(gspca_dev, 0x17, data);
+ reg_w1(gspca_dev, 0x05, sn9c1xx[5]);
+ reg_w1(gspca_dev, 0x07, sn9c1xx[7]);
+ reg_w1(gspca_dev, 0x06, sn9c1xx[6]);
+ reg_w1(gspca_dev, 0x14, sn9c1xx[0x14]);
+ switch (sd->bridge) {
+ case BRIDGE_SN9C325:
+ reg_w(gspca_dev, 0x20, regsn20_sn9c325,
+ sizeof regsn20_sn9c325);
+ for (i = 0; i < 8; i++)
+ reg_w(gspca_dev, 0x84, reg84_sn9c325,
+ sizeof reg84_sn9c325);
+ reg_w1(gspca_dev, 0x9a, 0x0a);
+ reg_w1(gspca_dev, 0x99, 0x60);
+ break;
+ case BRIDGE_SN9C120:
+ reg_w(gspca_dev, 0x20, regsn20_sn9c120,
+ sizeof regsn20_sn9c120);
+ for (i = 0; i < 2; i++)
+ reg_w(gspca_dev, 0x84, reg84_sn9c120_1,
+ sizeof reg84_sn9c120_1);
+ for (i = 0; i < 6; i++)
+ reg_w(gspca_dev, 0x84, reg84_sn9c120_2,
+ sizeof reg84_sn9c120_2);
+ reg_w(gspca_dev, 0x84, reg84_sn9c120_3,
+ sizeof reg84_sn9c120_3);
+ reg_w1(gspca_dev, 0x9a, 0x05);
+ reg_w1(gspca_dev, 0x99, 0x5b);
+ break;
+ default:
+ reg_w(gspca_dev, 0x20, regsn20, sizeof regsn20);
+ for (i = 0; i < 8; i++)
+ reg_w(gspca_dev, 0x84, reg84, sizeof reg84);
+ reg_w1(gspca_dev, 0x9a, 0x08);
+ reg_w1(gspca_dev, 0x99, 0x59);
+ break;
+ }
+
+ mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;
+ if (mode)
+ reg1 = 0x46; /* 320 clk 48Mhz */
+ else
+ reg1 = 0x06; /* 640 clk 24Mz */
+ reg17 = 0x61;
+ switch (sd->sensor) {
+ case SENSOR_HV7131R:
+ hv7131R_InitSensor(gspca_dev);
+ break;
+ case SENSOR_MI0360:
+ mi0360_InitSensor(gspca_dev);
+ break;
+ case SENSOR_MO4000:
+ mo4000_InitSensor(gspca_dev);
+ if (mode) {
+/* reg1 = 0x46; * 320 clk 48Mhz 60fp/s */
+ reg1 = 0x06; /* clk 24Mz */
+ } else {
+ reg17 = 0x22; /* 640 MCKSIZE */
+/* reg1 = 0x06; * 640 clk 24Mz (done) */
+ }
+ break;
+ case SENSOR_OV7648:
+ ov7648_InitSensor(gspca_dev);
+ reg17 = 0xa2;
+ reg1 = 0x44;
+/* if (mode)
+ ; * 320x2...
+ else
+ ; * 640x... */
+ break;
+ default:
+/* case SENSOR_OV7660: */
+ ov7660_InitSensor(gspca_dev);
+ if (mode) {
+/* reg17 = 0x21; * 320 */
+/* reg1 = 0x44; */
+/* reg1 = 0x46; (done) */
+ } else {
+ reg17 = 0xa2; /* 640 */
+ reg1 = 0x40;
+ }
+ break;
+ }
+ reg_w(gspca_dev, 0xc0, C0, 6);
+ switch (sd->bridge) {
+ case BRIDGE_SN9C120: /*jfm ?? */
+ reg_w(gspca_dev, 0xca, CA_sn9c120, 4);
+ break;
+ default:
+ reg_w(gspca_dev, 0xca, CA, 4);
+ break;
+ }
+ switch (sd->bridge) {
+ case BRIDGE_SN9C120: /*jfm ?? */
+ case BRIDGE_SN9C325:
+ reg_w(gspca_dev, 0xce, CE_sn9c325, 4);
+ break;
+ default:
+ reg_w(gspca_dev, 0xce, CE, 4);
+ /* ?? {0x1e, 0xdd, 0x2d, 0xe7} */
+ break;
+ }
+
+ /* here change size mode 0 -> VGA; 1 -> CIF */
+ data = 0x40 | sn9c1xx[0x18] | (mode << 4);
+ reg_w1(gspca_dev, 0x18, data);
+
+ reg_w(gspca_dev, 0x100, qtable4, 0x40);
+ reg_w(gspca_dev, 0x140, qtable4 + 0x40, 0x40);
+
+ data = sn9c1xx[0x18] | (mode << 4);
+ reg_w1(gspca_dev, 0x18, data);
+
+ reg_w1(gspca_dev, 0x17, reg17);
+ reg_w1(gspca_dev, 0x01, reg1);
+ setbrightness(gspca_dev);
+ setcontrast(gspca_dev);
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ static const __u8 stophv7131[] =
+ { 0xa1, 0x11, 0x02, 0x09, 0x00, 0x00, 0x00, 0x10 };
+ static const __u8 stopmi0360[] =
+ { 0xb1, 0x5d, 0x07, 0x00, 0x00, 0x00, 0x00, 0x10 };
+ __u8 data;
+ const __u8 *sn9c1xx;
+
+ data = 0x0b;
+ switch (sd->sensor) {
+ case SENSOR_HV7131R:
+ i2c_w8(gspca_dev, stophv7131);
+ data = 0x2b;
+ break;
+ case SENSOR_MI0360:
+ i2c_w8(gspca_dev, stopmi0360);
+ data = 0x29;
+ break;
+ case SENSOR_MO4000:
+ break;
+ case SENSOR_OV7648:
+ data = 0x29;
+ break;
+ default:
+/* case SENSOR_OV7660: */
+ break;
+ }
+ sn9c1xx = sn_tb[(int) sd->sensor];
+ reg_w1(gspca_dev, 0x01, sn9c1xx[1]);
+ reg_w1(gspca_dev, 0x17, sn9c1xx[0x17]);
+ reg_w1(gspca_dev, 0x01, sn9c1xx[1]);
+ reg_w1(gspca_dev, 0x01, data);
+ reg_w1(gspca_dev, 0xf1, 0x01);
+}
+
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+}
+
+static void sd_close(struct gspca_dev *gspca_dev)
+{
+}
+
+static void setautogain(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ /* Thanks S., without your advice, autobright should not work :) */
+ int delta;
+ int expotimes = 0;
+ __u8 luma_mean = 130;
+ __u8 luma_delta = 20;
+
+ delta = sd->avg_lum;
+ if (delta < luma_mean - luma_delta ||
+ delta > luma_mean + luma_delta) {
+ switch (sd->sensor) {
+ case SENSOR_HV7131R:
+ expotimes = sd->exposure >> 8;
+ expotimes += (luma_mean - delta) >> 4;
+ if (expotimes < 0)
+ expotimes = 0;
+ sd->exposure = setexposure(gspca_dev,
+ (unsigned int) (expotimes << 8));
+ break;
+ case SENSOR_MO4000:
+ case SENSOR_MI0360:
+ expotimes = sd->exposure;
+ expotimes += (luma_mean - delta) >> 6;
+ if (expotimes < 0)
+ expotimes = 0;
+ sd->exposure = setexposure(gspca_dev,
+ (unsigned int) expotimes);
+ setcolors(gspca_dev);
+ break;
+ }
+ }
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ struct gspca_frame *frame, /* target */
+ __u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int sof, avg_lum;
+
+ sof = len - 64;
+ if (sof >= 0 && data[sof] == 0xff && data[sof + 1] == 0xd9) {
+
+ /* end of frame */
+ gspca_frame_add(gspca_dev, LAST_PACKET,
+ frame, data, sof + 2);
+ if (sd->ag_cnt < 0)
+ return;
+ if (--sd->ag_cnt >= 0)
+ return;
+ sd->ag_cnt = AG_CNT_START;
+/* w1 w2 w3 */
+/* w4 w5 w6 */
+/* w7 w8 */
+/* w4 */
+ avg_lum = ((data[sof + 29] << 8) | data[sof + 30]) >> 6;
+/* w6 */
+ avg_lum += ((data[sof + 33] << 8) | data[sof + 34]) >> 6;
+/* w2 */
+ avg_lum += ((data[sof + 25] << 8) | data[sof + 26]) >> 6;
+/* w8 */
+ avg_lum += ((data[sof + 37] << 8) | data[sof + 38]) >> 6;
+/* w5 */
+ avg_lum += ((data[sof + 31] << 8) | data[sof + 32]) >> 4;
+ avg_lum >>= 4;
+ sd->avg_lum = avg_lum;
+ PDEBUG(D_PACK, "mean lum %d", avg_lum);
+ setautogain(gspca_dev);
+ return;
+ }
+ if (gspca_dev->last_packet_type == LAST_PACKET) {
+
+ /* put the JPEG 422 header */
+ jpeg_put_header(gspca_dev, frame, sd->qindex, 0x21);
+ }
+ gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
+}
+
+static unsigned int getexposure(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ __u8 hexpo, mexpo, lexpo;
+
+ switch (sd->sensor) {
+ case SENSOR_HV7131R:
+ /* read sensor exposure */
+ i2c_r5(gspca_dev, 0x25);
+ return (gspca_dev->usb_buf[0] << 16)
+ | (gspca_dev->usb_buf[1] << 8)
+ | gspca_dev->usb_buf[2];
+ case SENSOR_MI0360:
+ /* read sensor exposure */
+ i2c_r5(gspca_dev, 0x09);
+ return (gspca_dev->usb_buf[0] << 8)
+ | gspca_dev->usb_buf[1];
+ case SENSOR_MO4000:
+ i2c_r5(gspca_dev, 0x0e);
+ hexpo = 0; /* gspca_dev->usb_buf[1] & 0x07; */
+ mexpo = 0x40; /* gspca_dev->usb_buf[2] & 0xff; */
+ lexpo = (gspca_dev->usb_buf[1] & 0x30) >> 4;
+ PDEBUG(D_CONF, "exposure %d",
+ (hexpo << 10) | (mexpo << 2) | lexpo);
+ return (hexpo << 10) | (mexpo << 2) | lexpo;
+ default:
+/* case SENSOR_OV7660: */
+ /* read sensor exposure */
+ i2c_r5(gspca_dev, 0x04);
+ hexpo = gspca_dev->usb_buf[3] & 0x2f;
+ lexpo = gspca_dev->usb_buf[0] & 0x02;
+ i2c_r5(gspca_dev, 0x08);
+ mexpo = gspca_dev->usb_buf[2];
+ return (hexpo << 10) | (mexpo << 2) | lexpo;
+ }
+}
+
+static void getbrightness(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ /* hardcoded registers seem not readable */
+ switch (sd->sensor) {
+ case SENSOR_HV7131R:
+/* sd->brightness = 0x7fff; */
+ sd->brightness = getexposure(gspca_dev) >> 4;
+ break;
+ case SENSOR_MI0360:
+ sd->brightness = getexposure(gspca_dev) << 4;
+ break;
+ case SENSOR_MO4000:
+/* sd->brightness = 0x1fff; */
+ sd->brightness = getexposure(gspca_dev) << 4;
+ break;
+ }
+}
+
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->brightness = val;
+ if (gspca_dev->streaming)
+ setbrightness(gspca_dev);
+ return 0;
+}
+
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ getbrightness(gspca_dev);
+ *val = sd->brightness;
+ return 0;
+}
+
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->contrast = val;
+ if (gspca_dev->streaming)
+ setcontrast(gspca_dev);
+ return 0;
+}
+
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->contrast;
+ return 0;
+}
+
+static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->colors = val;
+ if (gspca_dev->streaming)
+ setcolors(gspca_dev);
+ return 0;
+}
+
+static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->colors;
+ return 0;
+}
+
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->autogain = val;
+ if (val)
+ sd->ag_cnt = AG_CNT_START;
+ else
+ sd->ag_cnt = -1;
+ return 0;
+}
+
+static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->autogain;
+ return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls,
+ .nctrls = ARRAY_SIZE(sd_ctrls),
+ .config = sd_config,
+ .open = sd_open,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .stop0 = sd_stop0,
+ .close = sd_close,
+ .pkt_scan = sd_pkt_scan,
+};
+
+/* -- module initialisation -- */
+#define BSI(bridge, sensor, i2c_addr) \
+ .driver_info = (BRIDGE_ ## bridge << 16) \
+ | (SENSOR_ ## sensor << 8) \
+ | (i2c_addr)
+static const __devinitdata struct usb_device_id device_table[] = {
+#ifndef CONFIG_USB_SN9C102
+ {USB_DEVICE(0x0458, 0x7025), BSI(SN9C120, MI0360, 0x5d)},
+ {USB_DEVICE(0x045e, 0x00f5), BSI(SN9C105, OV7660, 0x21)},
+ {USB_DEVICE(0x045e, 0x00f7), BSI(SN9C105, OV7660, 0x21)},
+ {USB_DEVICE(0x0471, 0x0327), BSI(SN9C105, MI0360, 0x5d)},
+ {USB_DEVICE(0x0471, 0x0328), BSI(SN9C105, MI0360, 0x5d)},
+#endif
+ {USB_DEVICE(0x0471, 0x0330), BSI(SN9C105, MI0360, 0x5d)},
+ {USB_DEVICE(0x0c45, 0x6040), BSI(SN9C102P, HV7131R, 0x11)},
+/* bw600.inf:
+ {USB_DEVICE(0x0c45, 0x6040), BSI(SN9C102P, MI0360, 0x5d)}, */
+/* {USB_DEVICE(0x0c45, 0x603a), BSI(SN9C102P, OV7648, 0x??)}, */
+/* {USB_DEVICE(0x0c45, 0x607a), BSI(SN9C102P, OV7648, 0x??)}, */
+ {USB_DEVICE(0x0c45, 0x607c), BSI(SN9C102P, HV7131R, 0x11)},
+/* {USB_DEVICE(0x0c45, 0x607e), BSI(SN9C102P, OV7630, 0x??)}, */
+ {USB_DEVICE(0x0c45, 0x60c0), BSI(SN9C105, MI0360, 0x5d)},
+/* {USB_DEVICE(0x0c45, 0x60c8), BSI(SN9C105, OM6801, 0x??)}, */
+/* {USB_DEVICE(0x0c45, 0x60cc), BSI(SN9C105, HV7131GP, 0x??)}, */
+ {USB_DEVICE(0x0c45, 0x60ec), BSI(SN9C105, MO4000, 0x21)},
+/* {USB_DEVICE(0x0c45, 0x60ef), BSI(SN9C105, ICM105C, 0x??)}, */
+/* {USB_DEVICE(0x0c45, 0x60fa), BSI(SN9C105, OV7648, 0x??)}, */
+ {USB_DEVICE(0x0c45, 0x60fb), BSI(SN9C105, OV7660, 0x21)},
+ {USB_DEVICE(0x0c45, 0x60fc), BSI(SN9C105, HV7131R, 0x11)},
+/* {USB_DEVICE(0x0c45, 0x60fe), BSI(SN9C105, OV7630, 0x??)}, */
+/* {USB_DEVICE(0x0c45, 0x6108), BSI(SN9C120, OM6801, 0x??)}, */
+/* {USB_DEVICE(0x0c45, 0x6122), BSI(SN9C110, ICM105C, 0x??)}, */
+/* {USB_DEVICE(0x0c45, 0x6123), BSI(SN9C110, SanyoCCD, 0x??)}, */
+ {USB_DEVICE(0x0c45, 0x612a), BSI(SN9C325, OV7648, 0x21)},
+/* bw600.inf:
+ {USB_DEVICE(0x0c45, 0x612a), BSI(SN9C110, OV7648, 0x21)}, */
+ {USB_DEVICE(0x0c45, 0x612c), BSI(SN9C110, MO4000, 0x21)},
+/* {USB_DEVICE(0x0c45, 0x612e), BSI(SN9C110, OV7630, 0x??)}, */
+/* {USB_DEVICE(0x0c45, 0x612f), BSI(SN9C110, ICM105C, 0x??)}, */
+#ifndef CONFIG_USB_SN9C102
+ {USB_DEVICE(0x0c45, 0x6130), BSI(SN9C120, MI0360, 0x5d)},
+ {USB_DEVICE(0x0c45, 0x6138), BSI(SN9C120, MO4000, 0x21)},
+/* {USB_DEVICE(0x0c45, 0x613a), BSI(SN9C120, OV7648, 0x??)}, */
+ {USB_DEVICE(0x0c45, 0x613b), BSI(SN9C120, OV7660, 0x21)},
+ {USB_DEVICE(0x0c45, 0x613c), BSI(SN9C120, HV7131R, 0x11)},
+/* {USB_DEVICE(0x0c45, 0x613e), BSI(SN9C120, OV7630, 0x??)}, */
+#endif
+ {USB_DEVICE(0x0c45, 0x6143), BSI(SN9C120, MI0360, 0x5d)},
+ {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+};
+
+/* -- module insert / remove -- */
+static int __init sd_mod_init(void)
+{
+ if (usb_register(&sd_driver) < 0)
+ return -1;
+ info("registered");
+ return 0;
+}
+static void __exit sd_mod_exit(void)
+{
+ usb_deregister(&sd_driver);
+ info("deregistered");
+}
+
+module_init(sd_mod_init);
+module_exit(sd_mod_exit);