android_kernel_motorola_sm6225/drivers/media/video/gspca/sonixj.c
Jean-Francois Moine cebf3b67f7 V4L/DVB (8604): gspca: Fix of "scheduling while atomic" crash.
The crash is due to USB exchanges done at interrupt level.
These exchanges, tied to autogain, are now done by the application.
Also, there is a fix about autogain start.
Concerned subdrivers: etoms, pac7311, sonixj and spca561.

Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
2008-08-06 06:57:25 -03:00

1458 lines
43 KiB
C

/*
* 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 */
atomic_t 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 */
0x00, 0x03, 0x64, 0x00, 0x1a, 0x20, 0x20, 0x20,
/* reg8 reg9 rega regb regc regd rege regf */
0xa1, 0x11, 0x02, 0x09, 0x00, 0x00, 0x00, 0x10,
/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
0x03, 0x00, 0x00, 0x01, 0x03, 0x28, 0x1e, 0x41,
/* reg18 reg19 reg1a reg1b reg1c reg1d reg1e reg1f */
0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
static const __u8 sn_mi0360[] = {
/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
0x00, 0x61, 0x44, 0x00, 0x1a, 0x20, 0x20, 0x20,
/* reg8 reg9 rega regb regc regd rege regf */
0xb1, 0x5d, 0x07, 0x00, 0x00, 0x00, 0x00, 0x10,
/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
0x03, 0x00, 0x00, 0x02, 0x0a, 0x28, 0x1e, 0x61,
/* reg18 reg19 reg1a reg1b reg1c reg1d reg1e reg1f */
0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
static const __u8 sn_mo4000[] = {
/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
0x12, 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 reg1c reg1d reg1e reg1f */
0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
static const __u8 sn_ov7648[] = {
/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
0x00, 0x21, 0x62, 0x00, 0x1a, 0x20, 0x20, 0x20,
/* reg8 reg9 rega regb regc regd rege regf */
0xa1, 0x6e, 0x18, 0x65, 0x00, 0x00, 0x00, 0x10,
/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
0x03, 0x00, 0x00, 0x06, 0x06, 0x28, 0x1e, 0x82,
/* reg18 reg19 reg1a reg1b reg1c reg1d reg1e reg1f */
0x07, 0x00, 0x00, 0x00, 0x00, 0x00
};
static const __u8 sn_ov7660[] = {
/* 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, 0x21, 0x07, 0x00, 0x00, 0x00, 0x00, 0x10,
/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
0x03, 0x00, 0x01, 0x01, 0x08, 0x28, 0x1e, 0x20,
/* reg18 reg19 reg1a reg1b reg1c reg1d reg1e reg1f */
0x07, 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_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_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, 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, 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 */
/****** (some exchanges in the win trace) ******/
{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) ******/
{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 !!****/
{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},
{}
};
/* reg 0x04 reg 0x07 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_sn9c325[] =
{0x0a, 0x40, 0x38, 0x30, 0x00, 0x20};
static const __u8 regd4[] = {0x60, 0x00, 0x00};
reg_w1(gspca_dev, 0xf1, 0x00);
reg_w1(gspca_dev, 0x01, 0x00); /*jfm was sn9c1xx[1] in 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;
default:
reg9a = reg9a_def;
break;
}
reg_w(gspca_dev, 0x9a, reg9a, 6);
reg_w(gspca_dev, 0xd4, regd4, sizeof regd4); /*fixme:jfm was 60 only*/
reg_w(gspca_dev, 0x03, &sn9c1xx[3], 0x0f);
switch (sd->bridge) {
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;
sd->ag_cnt = -1;
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);
reg_w1(gspca_dev, 0xf1, gspca_dev->usb_buf[0]);
reg_r(gspca_dev, 0x00, 1); /* get sonix chip id */
regF1 = gspca_dev->usb_buf[0];
PDEBUG(D_PROBE, "Sonix chip id: %02x", regF1);
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:
case SENSOR_MO4000:
expo = sd->brightness >> 4;
sd->exposure = setexposure(gspca_dev, expo);
break;
}
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 };
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);
}
static void setautogain(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
switch (sd->sensor) {
case SENSOR_HV7131R:
case SENSOR_MO4000:
case SENSOR_MI0360:
if (sd->autogain)
sd->ag_cnt = AG_CNT_START;
else
sd->ag_cnt = -1;
break;
}
}
/* -- start the camera -- */
static void sd_start(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
int i;
__u8 reg1, reg17, reg18;
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 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);
/* reg_w1(gspca_dev, 0x01, 0x44); jfm from win trace*/
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);
reg_w1(gspca_dev, 0x18, sn9c1xx[0x18]);
switch (sd->bridge) {
case BRIDGE_SN9C325:
reg17 = 0xae;
break;
default:
reg17 = 0x60;
break;
}
reg_w1(gspca_dev, 0x17, reg17);
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;
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 = 0x22; /* 640 MCKSIZE */
reg1 = 0x06;
}
break;
}
reg_w(gspca_dev, 0xc0, C0, 6);
reg_w(gspca_dev, 0xca, CA, 4);
switch (sd->bridge) {
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 */
reg18 = sn9c1xx[0x18] | (mode << 4);
reg_w1(gspca_dev, 0x18, reg18 | 0x40);
reg_w(gspca_dev, 0x100, qtable4, 0x40);
reg_w(gspca_dev, 0x140, qtable4 + 0x40, 0x40);
reg_w1(gspca_dev, 0x18, reg18);
reg_w1(gspca_dev, 0x17, reg17);
reg_w1(gspca_dev, 0x01, reg1);
setbrightness(gspca_dev);
setcontrast(gspca_dev);
setautogain(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_OV7648:
data = 0x29;
break;
default:
/* case SENSOR_MO4000: */
/* 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 do_autogain(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
int delta;
int expotimes;
__u8 luma_mean = 130;
__u8 luma_delta = 20;
/* Thanks S., without your advice, autobright should not work :) */
if (sd->ag_cnt < 0)
return;
if (--sd->ag_cnt >= 0)
return;
sd->ag_cnt = AG_CNT_START;
delta = atomic_read(&sd->avg_lum);
PDEBUG(D_FRAM, "mean lum %d", delta);
if (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;
default:
/* 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;
}
}
}
/* scan the URB packets */
/* This function is run at interrupt level. */
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;
/* 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;
atomic_set(&sd->avg_lum, avg_lum);
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_OV7648: * jfm: is it ok for 7648? */
/* 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 = getexposure(gspca_dev) >> 4;
break;
case SENSOR_MI0360:
sd->brightness = getexposure(gspca_dev) << 4;
break;
case SENSOR_MO4000:
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 (gspca_dev->streaming)
setautogain(gspca_dev);
return 0;
}
static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
*val = sd->autogain;
return 0;
}
/* 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,
.dq_callback = do_autogain,
};
/* -- 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);