add idl4k kernel firmware version 1.13.0.105

This commit is contained in:
Jaroslav Kysela
2015-03-26 17:22:37 +01:00
parent 5194d2792e
commit e9070cdc77
31064 changed files with 12769984 additions and 0 deletions

View File

@@ -0,0 +1,278 @@
menuconfig USB_GSPCA
tristate "GSPCA based webcams"
depends on VIDEO_V4L2
default m
---help---
Say Y here if you want to enable selecting webcams based
on the GSPCA framework.
See <file:Documentation/video4linux/gspca.txt> for more info.
This driver uses the Video For Linux API. You must say Y or M to
"Video For Linux" to use this driver.
To compile this driver as modules, choose M here: the
module will be called gspca_main.
if USB_GSPCA && VIDEO_V4L2
source "drivers/media/video/gspca/m5602/Kconfig"
source "drivers/media/video/gspca/stv06xx/Kconfig"
source "drivers/media/video/gspca/gl860/Kconfig"
config USB_GSPCA_CONEX
tristate "Conexant Camera Driver"
depends on VIDEO_V4L2 && USB_GSPCA
help
Say Y here if you want support for cameras based on the Conexant chip.
To compile this driver as a module, choose M here: the
module will be called gspca_conex.
config USB_GSPCA_ETOMS
tristate "Etoms USB Camera Driver"
depends on VIDEO_V4L2 && USB_GSPCA
help
Say Y here if you want support for cameras based on the Etoms chip.
To compile this driver as a module, choose M here: the
module will be called gspca_etoms.
config USB_GSPCA_FINEPIX
tristate "Fujifilm FinePix USB V4L2 driver"
depends on VIDEO_V4L2 && USB_GSPCA
help
Say Y here if you want support for cameras based on the FinePix chip.
To compile this driver as a module, choose M here: the
module will be called gspca_finepix.
config USB_GSPCA_JEILINJ
tristate "Jeilin JPEG USB V4L2 driver"
depends on VIDEO_V4L2 && USB_GSPCA
help
Say Y here if you want support for cameras based on this Jeilin chip.
To compile this driver as a module, choose M here: the
module will be called gspca_jeilinj.
config USB_GSPCA_MARS
tristate "Mars USB Camera Driver"
depends on VIDEO_V4L2 && USB_GSPCA
help
Say Y here if you want support for cameras based on the Mars chip.
To compile this driver as a module, choose M here: the
module will be called gspca_mars.
config USB_GSPCA_MR97310A
tristate "Mars-Semi MR97310A USB Camera Driver"
depends on VIDEO_V4L2 && USB_GSPCA
help
Say Y here if you want support for cameras based on the MR97310A chip.
To compile this driver as a module, choose M here: the
module will be called gspca_mr97310a.
config USB_GSPCA_OV519
tristate "OV519 USB Camera Driver"
depends on VIDEO_V4L2 && USB_GSPCA
help
Say Y here if you want support for cameras based on the OV519 chip.
To compile this driver as a module, choose M here: the
module will be called gspca_ov519.
config USB_GSPCA_OV534
tristate "OV534 USB Camera Driver"
depends on VIDEO_V4L2 && USB_GSPCA
help
Say Y here if you want support for cameras based on the OV534 chip.
(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_PAC207
tristate "Pixart PAC207 USB Camera Driver"
depends on VIDEO_V4L2 && USB_GSPCA
help
Say Y here if you want support for cameras based on the PAC207 chip.
To compile this driver as a module, choose M here: the
module will be called gspca_pac207.
config USB_GSPCA_PAC7311
tristate "Pixart PAC7311 USB Camera Driver"
depends on VIDEO_V4L2 && USB_GSPCA
help
Say Y here if you want support for cameras based on the PAC7311 chip.
To compile this driver as a module, choose M here: the
module will be called gspca_pac7311.
config USB_GSPCA_SN9C20X
tristate "SN9C20X USB Camera Driver"
depends on VIDEO_V4L2 && USB_GSPCA
help
Say Y here if you want support for cameras based on the
sn9c20x chips (SN9C201 and SN9C202).
To compile this driver as a module, choose M here: the
module will be called gspca_sn9c20x.
config USB_GSPCA_SN9C20X_EVDEV
bool "Enable evdev support"
depends on USB_GSPCA_SN9C20X && INPUT
---help---
Say Y here in order to enable evdev support for sn9c20x webcam button.
config USB_GSPCA_SONIXB
tristate "SONIX Bayer USB Camera Driver"
depends on VIDEO_V4L2 && USB_GSPCA
help
Say Y here if you want support for cameras based on the Sonix
chips with Bayer format (SN9C101, SN9C102 and SN9C103).
To compile this driver as a module, choose M here: the
module will be called gspca_sonixb.
config USB_GSPCA_SONIXJ
tristate "SONIX JPEG USB Camera Driver"
depends on VIDEO_V4L2 && USB_GSPCA
help
Say Y here if you want support for cameras based on the Sonix
chips with JPEG format (SN9C102P, SN9C105 and >= SN9C110).
To compile this driver as a module, choose M here: the
module will be called gspca_sonixj
config USB_GSPCA_SPCA500
tristate "SPCA500 USB Camera Driver"
depends on VIDEO_V4L2 && USB_GSPCA
help
Say Y here if you want support for cameras based on the SPCA500 chip.
To compile this driver as a module, choose M here: the
module will be called gspca_spca500.
config USB_GSPCA_SPCA501
tristate "SPCA501 USB Camera Driver"
depends on VIDEO_V4L2 && USB_GSPCA
help
Say Y here if you want support for cameras based on the SPCA501 chip.
To compile this driver as a module, choose M here: the
module will be called gspca_spca501.
config USB_GSPCA_SPCA505
tristate "SPCA505 USB Camera Driver"
depends on VIDEO_V4L2 && USB_GSPCA
help
Say Y here if you want support for cameras based on the SPCA505 chip.
To compile this driver as a module, choose M here: the
module will be called gspca_spca505.
config USB_GSPCA_SPCA506
tristate "SPCA506 USB Camera Driver"
depends on VIDEO_V4L2 && USB_GSPCA
help
Say Y here if you want support for cameras based on the SPCA506 chip.
To compile this driver as a module, choose M here: the
module will be called gspca_spca506.
config USB_GSPCA_SPCA508
tristate "SPCA508 USB Camera Driver"
depends on VIDEO_V4L2 && USB_GSPCA
help
Say Y here if you want support for cameras based on the SPCA508 chip.
To compile this driver as a module, choose M here: the
module will be called gspca_spca508.
config USB_GSPCA_SPCA561
tristate "SPCA561 USB Camera Driver"
depends on VIDEO_V4L2 && USB_GSPCA
help
Say Y here if you want support for cameras based on the SPCA561 chip.
To compile this driver as a module, choose M here: the
module will be called gspca_spca561.
config USB_GSPCA_SQ905
tristate "SQ Technologies SQ905 based USB Camera Driver"
depends on VIDEO_V4L2 && USB_GSPCA
help
Say Y here if you want support for cameras based on the SQ905 chip.
To compile this driver as a module, choose M here: the
module will be called gspca_sq905.
config USB_GSPCA_SQ905C
tristate "SQ Technologies SQ905C based USB Camera Driver"
depends on VIDEO_V4L2 && USB_GSPCA
help
Say Y here if you want support for cameras based on the SQ905C chip.
To compile this driver as a module, choose M here: the
module will be called gspca_sq905c.
config USB_GSPCA_STK014
tristate "Syntek DV4000 (STK014) USB Camera Driver"
depends on VIDEO_V4L2 && USB_GSPCA
help
Say Y here if you want support for cameras based on the STK014 chip.
To compile this driver as a module, choose M here: the
module will be called gspca_stk014.
config USB_GSPCA_SUNPLUS
tristate "SUNPLUS USB Camera Driver"
depends on VIDEO_V4L2 && USB_GSPCA
help
Say Y here if you want support for cameras based on the Sunplus
SPCA504(abc) SPCA533 SPCA536 chips.
To compile this driver as a module, choose M here: the
module will be called gspca_sunplus.
config USB_GSPCA_T613
tristate "T613 (JPEG Compliance) USB Camera Driver"
depends on VIDEO_V4L2 && USB_GSPCA
help
Say Y here if you want support for cameras based on the T613 chip.
To compile this driver as a module, choose M here: the
module will be called gspca_t613.
config USB_GSPCA_TV8532
tristate "TV8532 USB Camera Driver"
depends on VIDEO_V4L2 && USB_GSPCA
help
Say Y here if you want support for cameras based on the TV8531 chip.
To compile this driver as a module, choose M here: the
module will be called gspca_tv8532.
config USB_GSPCA_VC032X
tristate "VC032X USB Camera Driver"
depends on VIDEO_V4L2 && USB_GSPCA
help
Say Y here if you want support for cameras based on the VC032X chip.
To compile this driver as a module, choose M here: the
module will be called gspca_vc032x.
config USB_GSPCA_ZC3XX
tristate "ZC3XX USB Camera Driver"
depends on VIDEO_V4L2 && USB_GSPCA
help
Say Y here if you want support for cameras based on the ZC3XX chip.
To compile this driver as a module, choose M here: the
module will be called gspca_zc3xx.
endif

View File

@@ -0,0 +1,61 @@
obj-$(CONFIG_USB_GSPCA) += gspca_main.o
obj-$(CONFIG_USB_GSPCA_CONEX) += gspca_conex.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_MARS) += gspca_mars.o
obj-$(CONFIG_USB_GSPCA_MR97310A) += gspca_mr97310a.o
obj-$(CONFIG_USB_GSPCA_OV519) += gspca_ov519.o
obj-$(CONFIG_USB_GSPCA_OV534) += gspca_ov534.o
obj-$(CONFIG_USB_GSPCA_PAC207) += gspca_pac207.o
obj-$(CONFIG_USB_GSPCA_PAC7311) += gspca_pac7311.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_SQ905) += gspca_sq905.o
obj-$(CONFIG_USB_GSPCA_SQ905C) += gspca_sq905c.o
obj-$(CONFIG_USB_GSPCA_SUNPLUS) += gspca_sunplus.o
obj-$(CONFIG_USB_GSPCA_STK014) += gspca_stk014.o
obj-$(CONFIG_USB_GSPCA_T613) += gspca_t613.o
obj-$(CONFIG_USB_GSPCA_TV8532) += gspca_tv8532.o
obj-$(CONFIG_USB_GSPCA_VC032X) += gspca_vc032x.o
obj-$(CONFIG_USB_GSPCA_ZC3XX) += gspca_zc3xx.o
gspca_main-objs := gspca.o
gspca_conex-objs := conex.o
gspca_etoms-objs := etoms.o
gspca_finepix-objs := finepix.o
gspca_jeilinj-objs := jeilinj.o
gspca_mars-objs := mars.o
gspca_mr97310a-objs := mr97310a.o
gspca_ov519-objs := ov519.o
gspca_ov534-objs := ov534.o
gspca_pac207-objs := pac207.o
gspca_pac7311-objs := pac7311.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_sq905-objs := sq905.o
gspca_sq905c-objs := sq905c.o
gspca_stk014-objs := stk014.o
gspca_sunplus-objs := sunplus.o
gspca_t613-objs := t613.o
gspca_tv8532-objs := tv8532.o
gspca_vc032x-objs := vc032x.o
gspca_zc3xx-objs := zc3xx.o
obj-$(CONFIG_USB_M5602) += m5602/
obj-$(CONFIG_USB_STV06XX) += stv06xx/
obj-$(CONFIG_USB_GL860) += gl860/

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,916 @@
/*
* Etoms Et61x151 GPL Linux driver by Michel Xhaard (09/09/2004)
*
* V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define MODULE_NAME "etoms"
#include "gspca.h"
MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
MODULE_DESCRIPTION("Etoms USB Camera Driver");
MODULE_LICENSE("GPL");
/* specific webcam descriptor */
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
unsigned char brightness;
unsigned char contrast;
unsigned char colors;
unsigned char autogain;
char sensor;
#define SENSOR_PAS106 0
#define SENSOR_TAS5130CXX 1
signed char ag_cnt;
#define AG_CNT_START 13
};
/* V4L2 controls supported by the driver */
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
static struct ctrl sd_ctrls[] = {
{
{
.id = V4L2_CID_BRIGHTNESS,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Brightness",
.minimum = 1,
.maximum = 127,
.step = 1,
#define BRIGHTNESS_DEF 63
.default_value = BRIGHTNESS_DEF,
},
.set = sd_setbrightness,
.get = sd_getbrightness,
},
{
{
.id = V4L2_CID_CONTRAST,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Contrast",
.minimum = 0,
.maximum = 255,
.step = 1,
#define CONTRAST_DEF 127
.default_value = CONTRAST_DEF,
},
.set = sd_setcontrast,
.get = sd_getcontrast,
},
#define COLOR_IDX 2
{
{
.id = V4L2_CID_SATURATION,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Color",
.minimum = 0,
.maximum = 15,
.step = 1,
#define COLOR_DEF 7
.default_value = COLOR_DEF,
},
.set = sd_setcolors,
.get = sd_getcolors,
},
{
{
.id = V4L2_CID_AUTOGAIN,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.name = "Auto Gain",
.minimum = 0,
.maximum = 1,
.step = 1,
#define AUTOGAIN_DEF 1
.default_value = AUTOGAIN_DEF,
},
.set = sd_setautogain,
.get = sd_getautogain,
},
};
static 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;
#ifdef GSPCA_DEBUG
if (len > USB_BUF_SZ) {
err("reg_r: buffer overflow");
return;
}
#endif
usb_control_msg(dev,
usb_rcvctrlpipe(dev, 0),
0,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
0,
index, gspca_dev->usb_buf, len, 500);
PDEBUG(D_USBI, "reg read [%02x] -> %02x ..",
index, gspca_dev->usb_buf[0]);
}
static void reg_w_val(struct gspca_dev *gspca_dev,
__u16 index,
__u8 val)
{
struct usb_device *dev = gspca_dev->dev;
gspca_dev->usb_buf[0] = val;
usb_control_msg(dev,
usb_sndctrlpipe(dev, 0),
0,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
0,
index, gspca_dev->usb_buf, 1, 500);
}
static void reg_w(struct gspca_dev *gspca_dev,
__u16 index,
const __u8 *buffer,
__u16 len)
{
struct usb_device *dev = gspca_dev->dev;
#ifdef GSPCA_DEBUG
if (len > USB_BUF_SZ) {
err("reg_w: buffer overflow");
return;
}
PDEBUG(D_USBO, "reg write [%02x] = %02x..", index, *buffer);
#endif
memcpy(gspca_dev->usb_buf, buffer, len);
usb_control_msg(dev,
usb_sndctrlpipe(dev, 0),
0,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
0, index, gspca_dev->usb_buf, len, 500);
}
static int i2c_w(struct gspca_dev *gspca_dev,
__u8 reg,
const __u8 *buffer,
int len, __u8 mode)
{
/* buffer should be [D0..D7] */
__u8 ptchcount;
/* set the base address */
reg_w_val(gspca_dev, ET_I2C_BASE, 0x40);
/* sensor base for the pas106 */
/* set count and prefetch */
ptchcount = ((len & 0x07) << 4) | (mode & 0x03);
reg_w_val(gspca_dev, ET_I2C_COUNT, ptchcount);
/* set the register base */
reg_w_val(gspca_dev, ET_I2C_REG, reg);
while (--len >= 0)
reg_w_val(gspca_dev, ET_I2C_DATA0 + len, buffer[len]);
return 0;
}
static int i2c_r(struct gspca_dev *gspca_dev,
__u8 reg)
{
/* set the base address */
reg_w_val(gspca_dev, ET_I2C_BASE, 0x40);
/* sensor base for the pas106 */
/* set count and prefetch (cnd: 4 bits - mode: 4 bits) */
reg_w_val(gspca_dev, ET_I2C_COUNT, 0x11);
reg_w_val(gspca_dev, ET_I2C_REG, reg); /* set the register base */
reg_w_val(gspca_dev, ET_I2C_PREFETCH, 0x02); /* prefetch */
reg_w_val(gspca_dev, ET_I2C_PREFETCH, 0x00);
reg_r(gspca_dev, ET_I2C_DATA0, 1); /* read one byte */
return 0;
}
static int Et_WaitStatus(struct gspca_dev *gspca_dev)
{
int retry = 10;
while (retry--) {
reg_r(gspca_dev, ET_ClCK, 1);
if (gspca_dev->usb_buf[0] != 0)
return 1;
}
return 0;
}
static int et_video(struct gspca_dev *gspca_dev,
int on)
{
int ret;
reg_w_val(gspca_dev, ET_GPIO_OUT,
on ? 0x10 /* startvideo - set Bit5 */
: 0); /* stopvideo */
ret = Et_WaitStatus(gspca_dev);
if (ret != 0)
PDEBUG(D_ERR, "timeout video on/off");
return ret;
}
static void Et_init2(struct gspca_dev *gspca_dev)
{
__u8 value;
static const __u8 FormLine[] = { 0x84, 0x03, 0x14, 0xf4, 0x01, 0x05 };
PDEBUG(D_STREAM, "Open Init2 ET");
reg_w_val(gspca_dev, ET_GPIO_DIR_CTRL, 0x2f);
reg_w_val(gspca_dev, ET_GPIO_OUT, 0x10);
reg_r(gspca_dev, ET_GPIO_IN, 1);
reg_w_val(gspca_dev, ET_ClCK, 0x14); /* 0x14 // 0x16 enabled pattern */
reg_w_val(gspca_dev, ET_CTRL, 0x1b);
/* compression et subsampling */
if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv)
value = ET_COMP_VAL1; /* 320 */
else
value = ET_COMP_VAL0; /* 640 */
reg_w_val(gspca_dev, ET_COMP, value);
reg_w_val(gspca_dev, ET_MAXQt, 0x1f);
reg_w_val(gspca_dev, ET_MINQt, 0x04);
/* undocumented registers */
reg_w_val(gspca_dev, ET_REG1d, 0xff);
reg_w_val(gspca_dev, ET_REG1e, 0xff);
reg_w_val(gspca_dev, ET_REG1f, 0xff);
reg_w_val(gspca_dev, ET_REG20, 0x35);
reg_w_val(gspca_dev, ET_REG21, 0x01);
reg_w_val(gspca_dev, ET_REG22, 0x00);
reg_w_val(gspca_dev, ET_REG23, 0xff);
reg_w_val(gspca_dev, ET_REG24, 0xff);
reg_w_val(gspca_dev, ET_REG25, 0x0f);
/* colors setting */
reg_w_val(gspca_dev, 0x30, 0x11); /* 0x30 */
reg_w_val(gspca_dev, 0x31, 0x40);
reg_w_val(gspca_dev, 0x32, 0x00);
reg_w_val(gspca_dev, ET_O_RED, 0x00); /* 0x34 */
reg_w_val(gspca_dev, ET_O_GREEN1, 0x00);
reg_w_val(gspca_dev, ET_O_BLUE, 0x00);
reg_w_val(gspca_dev, ET_O_GREEN2, 0x00);
/*************/
reg_w_val(gspca_dev, ET_G_RED, 0x80); /* 0x4d */
reg_w_val(gspca_dev, ET_G_GREEN1, 0x80);
reg_w_val(gspca_dev, ET_G_BLUE, 0x80);
reg_w_val(gspca_dev, ET_G_GREEN2, 0x80);
reg_w_val(gspca_dev, ET_G_GR_H, 0x00);
reg_w_val(gspca_dev, ET_G_GB_H, 0x00); /* 0x52 */
/* Window control registers */
reg_w_val(gspca_dev, 0x61, 0x80); /* use cmc_out */
reg_w_val(gspca_dev, 0x62, 0x02);
reg_w_val(gspca_dev, 0x63, 0x03);
reg_w_val(gspca_dev, 0x64, 0x14);
reg_w_val(gspca_dev, 0x65, 0x0e);
reg_w_val(gspca_dev, 0x66, 0x02);
reg_w_val(gspca_dev, 0x67, 0x02);
/**************************************/
reg_w_val(gspca_dev, ET_SYNCHRO, 0x8f); /* 0x68 */
reg_w_val(gspca_dev, ET_STARTX, 0x69); /* 0x6a //0x69 */
reg_w_val(gspca_dev, ET_STARTY, 0x0d); /* 0x0d //0x0c */
reg_w_val(gspca_dev, ET_WIDTH_LOW, 0x80);
reg_w_val(gspca_dev, ET_HEIGTH_LOW, 0xe0);
reg_w_val(gspca_dev, ET_W_H_HEIGTH, 0x60); /* 6d */
reg_w_val(gspca_dev, ET_REG6e, 0x86);
reg_w_val(gspca_dev, ET_REG6f, 0x01);
reg_w_val(gspca_dev, ET_REG70, 0x26);
reg_w_val(gspca_dev, ET_REG71, 0x7a);
reg_w_val(gspca_dev, ET_REG72, 0x01);
/* Clock Pattern registers ***************** */
reg_w_val(gspca_dev, ET_REG73, 0x00);
reg_w_val(gspca_dev, ET_REG74, 0x18); /* 0x28 */
reg_w_val(gspca_dev, ET_REG75, 0x0f); /* 0x01 */
/**********************************************/
reg_w_val(gspca_dev, 0x8a, 0x20);
reg_w_val(gspca_dev, 0x8d, 0x0f);
reg_w_val(gspca_dev, 0x8e, 0x08);
/**************************************/
reg_w_val(gspca_dev, 0x03, 0x08);
reg_w_val(gspca_dev, ET_PXL_CLK, 0x03);
reg_w_val(gspca_dev, 0x81, 0xff);
reg_w_val(gspca_dev, 0x80, 0x00);
reg_w_val(gspca_dev, 0x81, 0xff);
reg_w_val(gspca_dev, 0x80, 0x20);
reg_w_val(gspca_dev, 0x03, 0x01);
reg_w_val(gspca_dev, 0x03, 0x00);
reg_w_val(gspca_dev, 0x03, 0x08);
/********************************************/
/* reg_r(gspca_dev, ET_I2C_BASE, 1);
always 0x40 as the pas106 ??? */
/* set the sensor */
if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv)
value = 0x04; /* 320 */
else /* 640 */
value = 0x1e; /* 0x17 * setting PixelClock
* 0x03 mean 24/(3+1) = 6 Mhz
* 0x05 -> 24/(5+1) = 4 Mhz
* 0x0b -> 24/(11+1) = 2 Mhz
* 0x17 -> 24/(23+1) = 1 Mhz
*/
reg_w_val(gspca_dev, ET_PXL_CLK, value);
/* now set by fifo the FormatLine setting */
reg_w(gspca_dev, 0x62, FormLine, 6);
/* set exposure times [ 0..0x78] 0->longvalue 0x78->shortvalue */
reg_w_val(gspca_dev, 0x81, 0x47); /* 0x47; */
reg_w_val(gspca_dev, 0x80, 0x40); /* 0x40; */
/* Pedro change */
/* Brightness change Brith+ decrease value */
/* Brigth- increase value */
/* original value = 0x70; */
reg_w_val(gspca_dev, 0x81, 0x30); /* 0x20; - set brightness */
reg_w_val(gspca_dev, 0x80, 0x20); /* 0x20; */
}
static void setbrightness(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
int i;
__u8 brightness = sd->brightness;
for (i = 0; i < 4; i++)
reg_w_val(gspca_dev, ET_O_RED + i, brightness);
}
static void setcontrast(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
__u8 RGBG[] = { 0x80, 0x80, 0x80, 0x80, 0x00, 0x00 };
__u8 contrast = sd->contrast;
memset(RGBG, contrast, sizeof(RGBG) - 2);
reg_w(gspca_dev, ET_G_RED, RGBG, 6);
}
static void setcolors(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
__u8 I2cc[] = { 0x05, 0x02, 0x02, 0x05, 0x0d };
__u8 i2cflags = 0x01;
/* __u8 green = 0; */
__u8 colors = sd->colors;
I2cc[3] = colors; /* red */
I2cc[0] = 15 - colors; /* blue */
/* green = 15 - ((((7*I2cc[0]) >> 2 ) + I2cc[3]) >> 1); */
/* I2cc[1] = I2cc[2] = green; */
if (sd->sensor == SENSOR_PAS106) {
i2c_w(gspca_dev, PAS106_REG13, &i2cflags, 1, 3);
i2c_w(gspca_dev, PAS106_REG9, I2cc, sizeof I2cc, 1);
}
/* PDEBUG(D_CONF , "Etoms red %d blue %d green %d",
I2cc[3], I2cc[0], green); */
}
static void getcolors(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
if (sd->sensor == SENSOR_PAS106) {
/* i2c_r(gspca_dev, PAS106_REG9); * blue */
i2c_r(gspca_dev, PAS106_REG9 + 3); /* red */
sd->colors = gspca_dev->usb_buf[0] & 0x0f;
}
}
static void setautogain(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
if (sd->autogain)
sd->ag_cnt = AG_CNT_START;
else
sd->ag_cnt = -1;
}
static void Et_init1(struct gspca_dev *gspca_dev)
{
__u8 value;
/* __u8 I2c0 [] = {0x0a, 0x12, 0x05, 0x22, 0xac, 0x00, 0x01, 0x00}; */
__u8 I2c0[] = { 0x0a, 0x12, 0x05, 0x6d, 0xcd, 0x00, 0x01, 0x00 };
/* try 1/120 0x6d 0xcd 0x40 */
/* __u8 I2c0 [] = {0x0a, 0x12, 0x05, 0xfe, 0xfe, 0xc0, 0x01, 0x00};
* 1/60000 hmm ?? */
PDEBUG(D_STREAM, "Open Init1 ET");
reg_w_val(gspca_dev, ET_GPIO_DIR_CTRL, 7);
reg_r(gspca_dev, ET_GPIO_IN, 1);
reg_w_val(gspca_dev, ET_RESET_ALL, 1);
reg_w_val(gspca_dev, ET_RESET_ALL, 0);
reg_w_val(gspca_dev, ET_ClCK, 0x10);
reg_w_val(gspca_dev, ET_CTRL, 0x19);
/* compression et subsampling */
if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv)
value = ET_COMP_VAL1;
else
value = ET_COMP_VAL0;
PDEBUG(D_STREAM, "Open mode %d Compression %d",
gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv,
value);
reg_w_val(gspca_dev, ET_COMP, value);
reg_w_val(gspca_dev, ET_MAXQt, 0x1d);
reg_w_val(gspca_dev, ET_MINQt, 0x02);
/* undocumented registers */
reg_w_val(gspca_dev, ET_REG1d, 0xff);
reg_w_val(gspca_dev, ET_REG1e, 0xff);
reg_w_val(gspca_dev, ET_REG1f, 0xff);
reg_w_val(gspca_dev, ET_REG20, 0x35);
reg_w_val(gspca_dev, ET_REG21, 0x01);
reg_w_val(gspca_dev, ET_REG22, 0x00);
reg_w_val(gspca_dev, ET_REG23, 0xf7);
reg_w_val(gspca_dev, ET_REG24, 0xff);
reg_w_val(gspca_dev, ET_REG25, 0x07);
/* colors setting */
reg_w_val(gspca_dev, ET_G_RED, 0x80);
reg_w_val(gspca_dev, ET_G_GREEN1, 0x80);
reg_w_val(gspca_dev, ET_G_BLUE, 0x80);
reg_w_val(gspca_dev, ET_G_GREEN2, 0x80);
reg_w_val(gspca_dev, ET_G_GR_H, 0x00);
reg_w_val(gspca_dev, ET_G_GB_H, 0x00);
/* Window control registers */
reg_w_val(gspca_dev, ET_SYNCHRO, 0xf0);
reg_w_val(gspca_dev, ET_STARTX, 0x56); /* 0x56 */
reg_w_val(gspca_dev, ET_STARTY, 0x05); /* 0x04 */
reg_w_val(gspca_dev, ET_WIDTH_LOW, 0x60);
reg_w_val(gspca_dev, ET_HEIGTH_LOW, 0x20);
reg_w_val(gspca_dev, ET_W_H_HEIGTH, 0x50);
reg_w_val(gspca_dev, ET_REG6e, 0x86);
reg_w_val(gspca_dev, ET_REG6f, 0x01);
reg_w_val(gspca_dev, ET_REG70, 0x86);
reg_w_val(gspca_dev, ET_REG71, 0x14);
reg_w_val(gspca_dev, ET_REG72, 0x00);
/* Clock Pattern registers */
reg_w_val(gspca_dev, ET_REG73, 0x00);
reg_w_val(gspca_dev, ET_REG74, 0x00);
reg_w_val(gspca_dev, ET_REG75, 0x0a);
reg_w_val(gspca_dev, ET_I2C_CLK, 0x04);
reg_w_val(gspca_dev, ET_PXL_CLK, 0x01);
/* set the sensor */
if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) {
I2c0[0] = 0x06;
i2c_w(gspca_dev, PAS106_REG2, I2c0, sizeof I2c0, 1);
i2c_w(gspca_dev, PAS106_REG9, I2c2, sizeof I2c2, 1);
value = 0x06;
i2c_w(gspca_dev, PAS106_REG2, &value, 1, 1);
i2c_w(gspca_dev, PAS106_REG3, I2c3, sizeof I2c3, 1);
/* value = 0x1f; */
value = 0x04;
i2c_w(gspca_dev, PAS106_REG0e, &value, 1, 1);
} else {
I2c0[0] = 0x0a;
i2c_w(gspca_dev, PAS106_REG2, I2c0, sizeof I2c0, 1);
i2c_w(gspca_dev, PAS106_REG9, I2c2, sizeof I2c2, 1);
value = 0x0a;
i2c_w(gspca_dev, PAS106_REG2, &value, 1, 1);
i2c_w(gspca_dev, PAS106_REG3, I2c3, sizeof I2c3, 1);
value = 0x04;
/* value = 0x10; */
i2c_w(gspca_dev, PAS106_REG0e, &value, 1, 1);
/* bit 2 enable bit 1:2 select 0 1 2 3
value = 0x07; * curve 0 *
i2c_w(gspca_dev, PAS106_REG0f, &value, 1, 1);
*/
}
/* value = 0x01; */
/* value = 0x22; */
/* i2c_w(gspca_dev, PAS106_REG5, &value, 1, 1); */
/* magnetude and sign bit for DAC */
i2c_w(gspca_dev, PAS106_REG7, I2c4, sizeof I2c4, 1);
/* now set by fifo the whole colors setting */
reg_w(gspca_dev, ET_G_RED, GainRGBG, 6);
getcolors(gspca_dev);
setcolors(gspca_dev);
}
/* this function is called at probe time */
static int sd_config(struct gspca_dev *gspca_dev,
const struct usb_device_id *id)
{
struct sd *sd = (struct sd *) gspca_dev;
struct cam *cam;
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);
gspca_dev->ctrl_dis = (1 << COLOR_IDX);
}
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 probe and resume time */
static int sd_init(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
if (sd->sensor == SENSOR_PAS106)
Et_init1(gspca_dev);
else
Et_init2(gspca_dev);
reg_w_val(gspca_dev, ET_RESET_ALL, 0x08);
et_video(gspca_dev, 0); /* video off */
return 0;
}
/* -- start the camera -- */
static int sd_start(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
if (sd->sensor == SENSOR_PAS106)
Et_init1(gspca_dev);
else
Et_init2(gspca_dev);
setautogain(gspca_dev);
reg_w_val(gspca_dev, ET_RESET_ALL, 0x08);
et_video(gspca_dev, 1); /* video on */
return 0;
}
static void sd_stopN(struct gspca_dev *gspca_dev)
{
et_video(gspca_dev, 0); /* video off */
}
static __u8 Et_getgainG(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
if (sd->sensor == SENSOR_PAS106) {
i2c_r(gspca_dev, PAS106_REG0e);
PDEBUG(D_CONF, "Etoms gain G %d", gspca_dev->usb_buf[0]);
return gspca_dev->usb_buf[0];
}
return 0x1f;
}
static void Et_setgainG(struct gspca_dev *gspca_dev, __u8 gain)
{
struct sd *sd = (struct sd *) gspca_dev;
if (sd->sensor == SENSOR_PAS106) {
__u8 i2cflags = 0x01;
i2c_w(gspca_dev, PAS106_REG13, &i2cflags, 1, 3);
i2c_w(gspca_dev, PAS106_REG0e, &gain, 1, 1);
}
}
#define BLIMIT(bright) \
(__u8)((bright > 0x1f)?0x1f:((bright < 4)?3:bright))
#define LIMIT(color) \
(unsigned char)((color > 0xff)?0xff:((color < 0)?0:color))
static void do_autogain(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
__u8 luma;
__u8 luma_mean = 128;
__u8 luma_delta = 20;
__u8 spring = 4;
int Gbright;
__u8 r, g, b;
if (sd->ag_cnt < 0)
return;
if (--sd->ag_cnt >= 0)
return;
sd->ag_cnt = AG_CNT_START;
Gbright = Et_getgainG(gspca_dev);
reg_r(gspca_dev, ET_LUMA_CENTER, 4);
g = (gspca_dev->usb_buf[0] + gspca_dev->usb_buf[3]) >> 1;
r = gspca_dev->usb_buf[1];
b = gspca_dev->usb_buf[2];
r = ((r << 8) - (r << 4) - (r << 3)) >> 10;
b = ((b << 7) >> 10);
g = ((g << 9) + (g << 7) + (g << 5)) >> 10;
luma = LIMIT(r + g + b);
PDEBUG(D_FRAM, "Etoms luma G %d", luma);
if (luma < luma_mean - luma_delta || luma > luma_mean + luma_delta) {
Gbright += (luma_mean - luma) >> spring;
Gbright = BLIMIT(Gbright);
PDEBUG(D_FRAM, "Etoms Gbright %d", Gbright);
Et_setgainG(gspca_dev, (__u8) Gbright);
}
}
#undef BLIMIT
#undef LIMIT
static void sd_pkt_scan(struct gspca_dev *gspca_dev,
struct gspca_frame *frame, /* target */
__u8 *data, /* isoc packet */
int len) /* iso packet length */
{
int seqframe;
seqframe = data[0] & 0x3f;
len = (int) (((data[0] & 0xc0) << 2) | data[1]);
if (seqframe == 0x3f) {
PDEBUG(D_FRAM,
"header packet found datalength %d !!", len);
PDEBUG(D_FRAM, "G %d R %d G %d B %d",
data[2], data[3], data[4], data[5]);
data += 30;
/* don't change datalength as the chips provided it */
frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
data, 0);
gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, len);
return;
}
if (len) {
data += 8;
gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
} else { /* Drop Packet */
gspca_dev->last_packet_type = DISCARD_PACKET;
}
}
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
sd->brightness = val;
if (gspca_dev->streaming)
setbrightness(gspca_dev);
return 0;
}
static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
*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 struct sd_desc sd_desc = {
.name = MODULE_NAME,
.ctrls = sd_ctrls,
.nctrls = ARRAY_SIZE(sd_ctrls),
.config = sd_config,
.init = sd_init,
.start = sd_start,
.stopN = sd_stopN,
.pkt_scan = sd_pkt_scan,
.dq_callback = do_autogain,
};
/* -- module initialisation -- */
static __devinitdata struct usb_device_id device_table[] = {
{USB_DEVICE(0x102c, 0x6151), .driver_info = SENSOR_PAS106},
#if !defined CONFIG_USB_ET61X251 && !defined CONFIG_USB_ET61X251_MODULE
{USB_DEVICE(0x102c, 0x6251), .driver_info = SENSOR_TAS5130CXX},
#endif
{}
};
MODULE_DEVICE_TABLE(usb, device_table);
/* -- device connect -- */
static int sd_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
THIS_MODULE);
}
static struct usb_driver sd_driver = {
.name = MODULE_NAME,
.id_table = device_table,
.probe = sd_probe,
.disconnect = gspca_disconnect,
#ifdef CONFIG_PM
.suspend = gspca_suspend,
.resume = gspca_resume,
#endif
};
/* -- module insert / remove -- */
static int __init sd_mod_init(void)
{
int ret;
ret = usb_register(&sd_driver);
if (ret < 0)
return ret;
PDEBUG(D_PROBE, "registered");
return 0;
}
static void __exit sd_mod_exit(void)
{
usb_deregister(&sd_driver);
PDEBUG(D_PROBE, "deregistered");
}
module_init(sd_mod_init);
module_exit(sd_mod_exit);

View File

@@ -0,0 +1,319 @@
/*
* Fujifilm Finepix subdriver
*
* Copyright (C) 2008 Frank Zago
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define MODULE_NAME "finepix"
#include "gspca.h"
MODULE_AUTHOR("Frank Zago <frank@zago.net>");
MODULE_DESCRIPTION("Fujifilm FinePix USB V4L2 driver");
MODULE_LICENSE("GPL");
/* Default timeout, in ms */
#define FPIX_TIMEOUT 250
/* Maximum transfer size to use. The windows driver reads by chunks of
* 0x2000 bytes, so do the same. Note: reading more seems to work
* too. */
#define FPIX_MAX_TRANSFER 0x2000
/* Structure to hold all of our device specific stuff */
struct usb_fpix {
struct gspca_dev gspca_dev; /* !! must be the first item */
struct work_struct work_struct;
struct workqueue_struct *work_thread;
};
/* Delay after which claim the next frame. If the delay is too small,
* the camera will return old frames. On the 4800Z, 20ms is bad, 25ms
* will fail every 4 or 5 frames, but 30ms is perfect. On the A210,
* 30ms is bad while 35ms is perfect. */
#define NEXT_FRAME_DELAY 35
/* These cameras only support 320x200. */
static const struct v4l2_pix_format fpix_mode[1] = {
{ 320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 320,
.sizeimage = 320 * 240 * 3 / 8 + 590,
.colorspace = V4L2_COLORSPACE_SRGB,
.priv = 0}
};
/* send a command to the webcam */
static int command(struct gspca_dev *gspca_dev,
int order) /* 0: reset, 1: frame request */
{
static u8 order_values[2][12] = {
{0xc6, 0, 0, 0, 0, 0, 0, 0, 0x20, 0, 0, 0}, /* reset */
{0xd3, 0, 0, 0, 0, 0, 0, 0x01, 0, 0, 0, 0}, /* fr req */
};
memcpy(gspca_dev->usb_buf, order_values[order], 12);
return usb_control_msg(gspca_dev->dev,
usb_sndctrlpipe(gspca_dev->dev, 0),
USB_REQ_GET_STATUS,
USB_DIR_OUT | USB_TYPE_CLASS |
USB_RECIP_INTERFACE, 0, 0, gspca_dev->usb_buf,
12, FPIX_TIMEOUT);
}
/* workqueue */
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;
struct gspca_frame *frame;
int ret = 0;
int len;
/* synchronize with the main driver */
mutex_lock(&gspca_dev->usb_lock);
mutex_unlock(&gspca_dev->usb_lock);
PDEBUG(D_STREAM, "dostream started");
/* loop reading a frame */
again:
while (gspca_dev->present && gspca_dev->streaming) {
/* request a frame */
mutex_lock(&gspca_dev->usb_lock);
ret = command(gspca_dev, 1);
mutex_unlock(&gspca_dev->usb_lock);
if (ret < 0)
break;
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;
}
if (!gspca_dev->present || !gspca_dev->streaming)
goto out;
frame = gspca_get_i_frame(&dev->gspca_dev);
if (frame == NULL)
gspca_dev->last_packet_type = DISCARD_PACKET;
if (len < FPIX_MAX_TRANSFER ||
(data[len - 2] == 0xff &&
data[len - 1] == 0xd9)) {
/* If the result is less than what was asked
* for, then it's the end of the
* frame. Sometimes the jpeg is not complete,
* but there's nothing we can do. We also end
* here if the the jpeg ends right at the end
* of the frame. */
if (frame)
frame = gspca_frame_add(gspca_dev,
LAST_PACKET,
frame,
data, len);
break;
}
/* got a partial image */
if (frame)
gspca_frame_add(gspca_dev,
gspca_dev->last_packet_type
== LAST_PACKET
? FIRST_PACKET : INTER_PACKET,
frame, data, len);
}
/* We must wait before trying reading the next
* frame. If we don't, or if the delay is too short,
* the camera will disconnect. */
msleep(NEXT_FRAME_DELAY);
}
out:
PDEBUG(D_STREAM, "dostream stopped");
}
/* this function is called at probe time */
static int sd_config(struct gspca_dev *gspca_dev,
const struct usb_device_id *id)
{
struct usb_fpix *dev = (struct usb_fpix *) gspca_dev;
struct cam *cam = &gspca_dev->cam;
cam->cam_mode = fpix_mode;
cam->nmodes = 1;
cam->bulk = 1;
cam->bulk_size = FPIX_MAX_TRANSFER;
INIT_WORK(&dev->work_struct, dostream);
return 0;
}
/* this function is called at probe and resume time */
static int sd_init(struct gspca_dev *gspca_dev)
{
return 0;
}
/* start the camera */
static int sd_start(struct gspca_dev *gspca_dev)
{
struct usb_fpix *dev = (struct usb_fpix *) gspca_dev;
int ret, len;
/* Init the device */
ret = command(gspca_dev, 0);
if (ret < 0) {
PDEBUG(D_STREAM, "init failed %d", 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) {
PDEBUG(D_STREAM, "usb_bulk_msg failed %d", ret);
return ret;
}
/* Request a frame, but don't read it */
ret = command(gspca_dev, 1);
if (ret < 0) {
PDEBUG(D_STREAM, "frame request failed %d", ret);
return ret;
}
/* Again, reset bulk in endpoint */
usb_clear_halt(gspca_dev->dev, gspca_dev->urb[0]->pipe);
/* Start the workqueue function to do the streaming */
dev->work_thread = create_singlethread_workqueue(MODULE_NAME);
queue_work(dev->work_thread, &dev->work_struct);
return 0;
}
/* called on streamoff with alt==0 and on disconnect */
/* the usb_lock is held at entry - restore on exit */
static void sd_stop0(struct gspca_dev *gspca_dev)
{
struct usb_fpix *dev = (struct usb_fpix *) gspca_dev;
/* wait for the work queue to terminate */
mutex_unlock(&gspca_dev->usb_lock);
destroy_workqueue(dev->work_thread);
mutex_lock(&gspca_dev->usb_lock);
dev->work_thread = NULL;
}
/* Table of supported USB devices */
static const __devinitdata 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,
#endif
};
/* -- module insert / remove -- */
static int __init sd_mod_init(void)
{
int ret;
ret = usb_register(&sd_driver);
if (ret < 0)
return ret;
PDEBUG(D_PROBE, "registered");
return 0;
}
static void __exit sd_mod_exit(void)
{
usb_deregister(&sd_driver);
PDEBUG(D_PROBE, "deregistered");
}
module_init(sd_mod_init);
module_exit(sd_mod_exit);

View File

@@ -0,0 +1,8 @@
config USB_GL860
tristate "GL860 USB Camera Driver"
depends on VIDEO_V4L2 && USB_GSPCA
help
Say Y here if you want support for cameras based on the GL860 chip.
To compile this driver as a module, choose M here: the
module will be called gspca_gl860.

View File

@@ -0,0 +1,10 @@
obj-$(CONFIG_USB_GL860) += gspca_gl860.o
gspca_gl860-objs := gl860.o \
gl860-mi1320.o \
gl860-ov2640.o \
gl860-ov9655.o \
gl860-mi2020.o
EXTRA_CFLAGS += -Idrivers/media/video/gspca

View File

@@ -0,0 +1,537 @@
/* @file gl860-mi1320.c
* @author Olivier LORIN from my logs
* @date 2009-08-27
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* Sensor : MI1320 */
#include "gl860.h"
static struct validx tbl_common[] = {
{0xba00, 0x00f0}, {0xba00, 0x00f1}, {0xba51, 0x0066}, {0xba02, 0x00f1},
{0xba05, 0x0067}, {0xba05, 0x00f1}, {0xbaa0, 0x0065}, {0xba00, 0x00f1},
{0xffff, 0xffff},
{0xba00, 0x00f0}, {0xba02, 0x00f1}, {0xbafa, 0x0028}, {0xba02, 0x00f1},
{0xba00, 0x00f0}, {0xba01, 0x00f1}, {0xbaf0, 0x0006}, {0xba0e, 0x00f1},
{0xba70, 0x0006}, {0xba0e, 0x00f1},
{0xffff, 0xffff},
{0xba74, 0x0006}, {0xba0e, 0x00f1},
{0xffff, 0xffff},
{0x0061, 0x0000}, {0x0068, 0x000d},
};
static struct validx tbl_init_at_startup[] = {
{0x0000, 0x0000}, {0x0010, 0x0010},
{35, 0xffff},
{0x0008, 0x00c0}, {0x0001, 0x00c1}, {0x0001, 0x00c2}, {0x0020, 0x0006},
{0x006a, 0x000d},
};
static struct validx tbl_sensor_settings_common[] = {
{0x0010, 0x0010}, {0x0003, 0x00c1}, {0x0042, 0x00c2}, {0x0040, 0x0000},
{0x006a, 0x0007}, {0x006a, 0x000d}, {0x0063, 0x0006},
};
static struct validx tbl_sensor_settings_1280[] = {
{0xba00, 0x00f0}, {0xba00, 0x00f1}, {0xba5a, 0x0066}, {0xba02, 0x00f1},
{0xba05, 0x0067}, {0xba05, 0x00f1}, {0xba20, 0x0065}, {0xba00, 0x00f1},
};
static struct validx tbl_sensor_settings_800[] = {
{0xba00, 0x00f0}, {0xba00, 0x00f1}, {0xba5a, 0x0066}, {0xba02, 0x00f1},
{0xba05, 0x0067}, {0xba05, 0x00f1}, {0xba20, 0x0065}, {0xba00, 0x00f1},
};
static struct validx tbl_sensor_settings_640[] = {
{0xba00, 0x00f0}, {0xba00, 0x00f1}, {0xbaa0, 0x0065}, {0xba00, 0x00f1},
{0xba51, 0x0066}, {0xba02, 0x00f1}, {0xba05, 0x0067}, {0xba05, 0x00f1},
{0xba20, 0x0065}, {0xba00, 0x00f1},
};
static struct validx tbl_post_unset_alt[] = {
{0xba00, 0x00f0}, {0xba00, 0x00f1}, {0xbaa0, 0x0065}, {0xba00, 0x00f1},
{0x0061, 0x0000}, {0x0068, 0x000d},
};
static u8 *tbl_1280[] = {
"\x0d\x80\xf1\x08\x03\x04\xf1\x00" "\x04\x05\xf1\x02\x05\x00\xf1\xf1"
"\x06\x00\xf1\x0d\x20\x01\xf1\x00" "\x21\x84\xf1\x00\x0d\x00\xf1\x08"
"\xf0\x00\xf1\x01\x34\x00\xf1\x00" "\x9b\x43\xf1\x00\xa6\x05\xf1\x00"
"\xa9\x04\xf1\x00\xa1\x05\xf1\x00" "\xa4\x04\xf1\x00\xae\x0a\xf1\x08"
,
"\xf0\x00\xf1\x02\x3a\x05\xf1\xf1" "\x3c\x05\xf1\xf1\x59\x01\xf1\x47"
"\x5a\x01\xf1\x88\x5c\x0a\xf1\x06" "\x5d\x0e\xf1\x0a\x64\x5e\xf1\x1c"
"\xd2\x00\xf1\xcf\xcb\x00\xf1\x01"
,
"\xd3\x02\xd4\x28\xd5\x01\xd0\x02" "\xd1\x18\xd2\xc1"
};
static u8 *tbl_800[] = {
"\x0d\x80\xf1\x08\x03\x03\xf1\xc0" "\x04\x05\xf1\x02\x05\x00\xf1\xf1"
"\x06\x00\xf1\x0d\x20\x01\xf1\x00" "\x21\x84\xf1\x00\x0d\x00\xf1\x08"
"\xf0\x00\xf1\x01\x34\x00\xf1\x00" "\x9b\x43\xf1\x00\xa6\x05\xf1\x00"
"\xa9\x03\xf1\xc0\xa1\x03\xf1\x20" "\xa4\x02\xf1\x5a\xae\x0a\xf1\x08"
,
"\xf0\x00\xf1\x02\x3a\x05\xf1\xf1" "\x3c\x05\xf1\xf1\x59\x01\xf1\x47"
"\x5a\x01\xf1\x88\x5c\x0a\xf1\x06" "\x5d\x0e\xf1\x0a\x64\x5e\xf1\x1c"
"\xd2\x00\xf1\xcf\xcb\x00\xf1\x01"
,
"\xd3\x02\xd4\x18\xd5\x21\xd0\x02" "\xd1\x10\xd2\x59"
};
static u8 *tbl_640[] = {
"\x0d\x80\xf1\x08\x03\x04\xf1\x04" "\x04\x05\xf1\x02\x07\x01\xf1\x7c"
"\x08\x00\xf1\x0e\x21\x80\xf1\x00" "\x0d\x00\xf1\x08\xf0\x00\xf1\x01"
"\x34\x10\xf1\x10\x3a\x43\xf1\x00" "\xa6\x05\xf1\x02\xa9\x04\xf1\x04"
"\xa7\x02\xf1\x81\xaa\x01\xf1\xe2" "\xae\x0c\xf1\x09"
,
"\xf0\x00\xf1\x02\x39\x03\xf1\xfc" "\x3b\x04\xf1\x04\x57\x01\xf1\xb6"
"\x58\x02\xf1\x0d\x5c\x1f\xf1\x19" "\x5d\x24\xf1\x1e\x64\x5e\xf1\x1c"
"\xd2\x00\xf1\x00\xcb\x00\xf1\x01"
,
"\xd3\x02\xd4\x10\xd5\x81\xd0\x02" "\xd1\x08\xd2\xe1"
};
static s32 tbl_sat[] = {0x25, 0x1d, 0x15, 0x0d, 0x05, 0x4d, 0x55, 0x5d, 0x2d};
static s32 tbl_bright[] = {0, 8, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70};
static s32 tbl_backlight[] = {0x0e, 0x06, 0x02};
static s32 tbl_cntr1[] = {
0x90, 0x98, 0xa0, 0xa8, 0xb0, 0xb8, 0xc0, 0xc8, 0xd0, 0xe0, 0xf0};
static s32 tbl_cntr2[] = {
0x70, 0x68, 0x60, 0x58, 0x50, 0x48, 0x40, 0x38, 0x30, 0x20, 0x10};
static u8 dat_wbalNL[] =
"\xf0\x00\xf1\x01\x05\x00\xf1\x06" "\x3b\x04\xf1\x2a\x47\x10\xf1\x10"
"\x9d\x3c\xf1\xae\xaf\x10\xf1\x00" "\xf0\x00\xf1\x02\x2f\x91\xf1\x20"
"\x9c\x91\xf1\x20\x37\x03\xf1\x00" "\x9d\xc5\xf1\x0f\xf0\x00\xf1\x00";
static u8 dat_wbalLL[] =
"\xf0\x00\xf1\x01\x05\x00\xf1\x0c" "\x3b\x04\xf1\x2a\x47\x40\xf1\x40"
"\x9d\x20\xf1\xae\xaf\x10\xf1\x00" "\xf0\x00\xf1\x02\x2f\xd1\xf1\x00"
"\x9c\xd1\xf1\x00\x37\x03\xf1\x00" "\x9d\xc5\xf1\x3f\xf0\x00\xf1\x00";
static u8 dat_wbalBL[] =
"\xf0\x00\xf1\x01\x05\x00\xf1\x06" "\x47\x10\xf1\x30\x9d\x3c\xf1\xae"
"\xaf\x10\xf1\x00\xf0\x00\xf1\x02" "\x2f\x91\xf1\x20\x9c\x91\xf1\x20"
"\x37\x03\xf1\x00\x9d\xc5\xf1\x2f" "\xf0\x00\xf1\x00";
static u8 dat_hvflip1[] = {0xf0, 0x00, 0xf1, 0x00};
static u8 s000[] =
"\x00\x01\x07\x6a\x06\x63\x0d\x6a" "\xc0\x00\x10\x10\xc1\x03\xc2\x42"
"\xd8\x04\x58\x00\x04\x02";
static u8 s001[] =
"\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 s002[] =
"\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 s003[] =
"\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 s004[] =
"\xf0\x00\xf1\x02\x24\x5f\xf1\x20" "\x28\xea\xf1\x02\x5f\x41\xf1\x43";
static u8 s005[] =
"\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 s006[] =
"\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 s007[] =
"\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 s008[] =
"\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 s009[] =
"\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 s010[] =
"\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 s011[] =
"\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 tihs 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 macros */
ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 22, s000);
ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x0000, 0, NULL);
ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 32, s001);
n = fetch_validx(gspca_dev, tbl_common, ARRAY_SIZE(tbl_common));
ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, s002);
ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, s003);
ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 16, s004);
ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, s005);
ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 44, s006);
keep_on_fetching_validx(gspca_dev, tbl_common,
ARRAY_SIZE(tbl_common), n);
ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 52, s007);
ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, s008);
ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, s009);
ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 56, s010);
keep_on_fetching_validx(gspca_dev, tbl_common,
ARRAY_SIZE(tbl_common), n);
ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 40, s011);
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;
}
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));
}

View File

@@ -0,0 +1,937 @@
/* @file gl860-mi2020.c
* @author Olivier LORIN, from Ice/Soro2005's logs(A), Fret_saw/Hulkie's
* logs(B) and Tricid"s logs(C). With the help of Kytrix/BUGabundo/Blazercist.
* @date 2009-08-27
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* Sensor : MI2020 */
#include "gl860.h"
static u8 dat_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_dummy1[] = {0x90, 0x00, 0x06};
/*static u8 dummy2[] = {0x8c, 0xa1, 0x02};*/
/*static u8 dummy3[] = {0x90, 0x00, 0x1f};*/
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 u8 dat_freq1[] = { 0x8c, 0xa4, 0x04 };
static u8 dat_multi5[] = { 0x8c, 0xa1, 0x03 };
static u8 dat_multi6[] = { 0x90, 0x00, 0x05 };
static struct validx tbl_common_a[] = {
{0x0000, 0x0000},
{1, 0xffff}, /* msleep(35); */
{0x006a, 0x0007}, {0x0063, 0x0006}, {0x006a, 0x000d}, {0x0000, 0x00c0},
{0x0010, 0x0010}, {0x0003, 0x00c1}, {0x0042, 0x00c2}, {0x0004, 0x00d8},
{0x0000, 0x0058}, {0x0002, 0x0004}, {0x0041, 0x0000},
};
static struct validx tbl_common_b[] = {
{0x006a, 0x0007},
{35, 0xffff},
{0x00ef, 0x0006},
{35, 0xffff},
{0x006a, 0x000d},
{35, 0xffff},
{0x0000, 0x00c0}, {0x0010, 0x0010}, {0x0003, 0x00c1}, {0x0042, 0x00c2},
{0x0004, 0x00d8}, {0x0000, 0x0058}, {0x0041, 0x0000},
};
static struct idxdata tbl_common_c[] = {
{0x32, "\x02\x00\x08"}, {0x33, "\xf4\x03\x1d"},
{6, "\xff\xff\xff"}, /* 12 */
{0x34, "\x1e\x8f\x09"}, {0x34, "\x1c\x01\x28"}, {0x34, "\x1e\x8f\x09"},
{2, "\xff\xff\xff"}, /* - */
{0x34, "\x1e\x8f\x09"}, {0x32, "\x14\x06\xe6"}, {0x33, "\x8c\x22\x23"},
{0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa2\x0f"}, {0x33, "\x90\x00\x0d"},
{0x33, "\x8c\xa2\x10"}, {0x33, "\x90\x00\x0b"}, {0x33, "\x8c\xa2\x11"},
{0x33, "\x90\x00\x07"}, {0x33, "\xf4\x03\x1d"}, {0x35, "\xa2\x00\xe2"},
{0x33, "\x8c\xab\x05"}, {0x33, "\x90\x00\x01"}, {0x32, "\x6e\x00\x86"},
{0x32, "\x70\x0f\xaa"}, {0x32, "\x72\x0f\xe4"}, {0x33, "\x8c\xa3\x4a"},
{0x33, "\x90\x00\x5a"}, {0x33, "\x8c\xa3\x4b"}, {0x33, "\x90\x00\xa6"},
{0x33, "\x8c\xa3\x61"}, {0x33, "\x90\x00\xc8"}, {0x33, "\x8c\xa3\x62"},
{0x33, "\x90\x00\xe1"}, {0x34, "\xce\x01\xa8"}, {0x34, "\xd0\x66\x33"},
{0x34, "\xd2\x31\x9a"}, {0x34, "\xd4\x94\x63"}, {0x34, "\xd6\x4b\x25"},
{0x34, "\xd8\x26\x70"}, {0x34, "\xda\x72\x4c"}, {0x34, "\xdc\xff\x04"},
{0x34, "\xde\x01\x5b"}, {0x34, "\xe6\x01\x13"}, {0x34, "\xee\x0b\xf0"},
{0x34, "\xf6\x0b\xa4"}, {0x35, "\x00\xf6\xe7"}, {0x35, "\x08\x0d\xfd"},
{0x35, "\x10\x25\x63"}, {0x35, "\x18\x35\x6c"}, {0x35, "\x20\x42\x7e"},
{0x35, "\x28\x19\x44"}, {0x35, "\x30\x39\xd4"}, {0x35, "\x38\xf5\xa8"},
{0x35, "\x4c\x07\x90"}, {0x35, "\x44\x07\xb8"}, {0x35, "\x5c\x06\x88"},
{0x35, "\x54\x07\xff"}, {0x34, "\xe0\x01\x52"}, {0x34, "\xe8\x00\xcc"},
{0x34, "\xf0\x0d\x83"}, {0x34, "\xf8\x0c\xb3"}, {0x35, "\x02\xfe\xba"},
{0x35, "\x0a\x04\xe0"}, {0x35, "\x12\x1c\x63"}, {0x35, "\x1a\x2b\x5a"},
{0x35, "\x22\x32\x5e"}, {0x35, "\x2a\x0d\x28"}, {0x35, "\x32\x2c\x02"},
{0x35, "\x3a\xf4\xfa"}, {0x35, "\x4e\x07\xef"}, {0x35, "\x46\x07\x88"},
{0x35, "\x5e\x07\xc1"}, {0x35, "\x56\x04\x64"}, {0x34, "\xe4\x01\x15"},
{0x34, "\xec\x00\x82"}, {0x34, "\xf4\x0c\xce"}, {0x34, "\xfc\x0c\xba"},
{0x35, "\x06\x1f\x02"}, {0x35, "\x0e\x02\xe3"}, {0x35, "\x16\x1a\x50"},
{0x35, "\x1e\x24\x39"}, {0x35, "\x26\x23\x4c"}, {0x35, "\x2e\xf9\x1b"},
{0x35, "\x36\x23\x19"}, {0x35, "\x3e\x12\x08"}, {0x35, "\x52\x07\x22"},
{0x35, "\x4a\x03\xd3"}, {0x35, "\x62\x06\x54"}, {0x35, "\x5a\x04\x5d"},
{0x34, "\xe2\x01\x04"}, {0x34, "\xea\x00\xa0"}, {0x34, "\xf2\x0c\xbc"},
{0x34, "\xfa\x0c\x5b"}, {0x35, "\x04\x17\xf2"}, {0x35, "\x0c\x02\x08"},
{0x35, "\x14\x28\x43"}, {0x35, "\x1c\x28\x62"}, {0x35, "\x24\x2b\x60"},
{0x35, "\x2c\x07\x33"}, {0x35, "\x34\x1f\xb0"}, {0x35, "\x3c\xed\xcd"},
{0x35, "\x50\x00\x06"}, {0x35, "\x48\x07\xff"}, {0x35, "\x60\x05\x89"},
{0x35, "\x58\x07\xff"}, {0x35, "\x40\x00\xa0"}, {0x35, "\x42\x00\x00"},
{0x32, "\x10\x01\xfc"}, {0x33, "\x8c\xa1\x18"}, {0x33, "\x90\x00\x3c"},
{1, "\xff\xff\xff"},
{0x33, "\x78\x00\x00"},
{1, "\xff\xff\xff"},
{0x35, "\xb8\x1f\x20"}, {0x33, "\x8c\xa2\x06"}, {0x33, "\x90\x00\x10"},
{0x33, "\x8c\xa2\x07"}, {0x33, "\x90\x00\x08"}, {0x33, "\x8c\xa2\x42"},
{0x33, "\x90\x00\x0b"}, {0x33, "\x8c\xa2\x4a"}, {0x33, "\x90\x00\x8c"},
{0x35, "\xba\xfa\x08"}, {0x33, "\x8c\xa2\x02"}, {0x33, "\x90\x00\x22"},
{0x33, "\x8c\xa2\x03"}, {0x33, "\x90\x00\xbb"},
};
static struct idxdata tbl_common_d[] = {
{0x33, "\x8c\x22\x2e"}, {0x33, "\x90\x00\xa0"}, {0x33, "\x8c\xa4\x08"},
{0x33, "\x90\x00\x1f"}, {0x33, "\x8c\xa4\x09"}, {0x33, "\x90\x00\x21"},
{0x33, "\x8c\xa4\x0a"}, {0x33, "\x90\x00\x25"}, {0x33, "\x8c\xa4\x0b"},
{0x33, "\x90\x00\x27"}, {0x33, "\x8c\x24\x11"}, {0x33, "\x90\x00\xa0"},
{0x33, "\x8c\x24\x13"}, {0x33, "\x90\x00\xc0"}, {0x33, "\x8c\x24\x15"},
{0x33, "\x90\x00\xa0"}, {0x33, "\x8c\x24\x17"}, {0x33, "\x90\x00\xc0"},
};
static struct idxdata tbl_common_e[] = {
{0x33, "\x8c\xa4\x04"}, {0x33, "\x90\x00\x80"}, {0x33, "\x8c\xa7\x9d"},
{0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa7\x9e"}, {0x33, "\x90\x00\x00"},
{0x33, "\x8c\xa2\x0c"}, {0x33, "\x90\x00\x17"}, {0x33, "\x8c\xa2\x15"},
{0x33, "\x90\x00\x04"}, {0x33, "\x8c\xa2\x14"}, {0x33, "\x90\x00\x20"},
{0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x17"},
/* msleep(53); */
{0x33, "\x90\x21\x11"}, {0x33, "\x8c\x27\x1b"}, {0x33, "\x90\x02\x4f"},
{0x33, "\x8c\x27\x25"}, {0x33, "\x90\x06\x0f"}, {0x33, "\x8c\x27\x39"},
{0x33, "\x90\x21\x11"}, {0x33, "\x8c\x27\x3d"}, {0x33, "\x90\x01\x20"},
{0x33, "\x8c\x27\x47"}, {0x33, "\x90\x09\x4c"}, {0x33, "\x8c\x27\x03"},
{0x33, "\x90\x02\x84"}, {0x33, "\x8c\x27\x05"}, {0x33, "\x90\x01\xe2"},
{0x33, "\x8c\x27\x07"}, {0x33, "\x90\x06\x40"}, {0x33, "\x8c\x27\x09"},
{0x33, "\x90\x04\xb0"}, {0x33, "\x8c\x27\x0d"}, {0x33, "\x90\x00\x00"},
{0x33, "\x8c\x27\x0f"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x11"},
{0x33, "\x90\x04\xbd"}, {0x33, "\x8c\x27\x13"}, {0x33, "\x90\x06\x4d"},
{0x33, "\x8c\x27\x15"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x17"},
{0x33, "\x90\x21\x11"}, {0x33, "\x8c\x27\x19"}, {0x33, "\x90\x04\x6c"},
{0x33, "\x8c\x27\x1b"}, {0x33, "\x90\x02\x4f"}, {0x33, "\x8c\x27\x1d"},
{0x33, "\x90\x01\x02"}, {0x33, "\x8c\x27\x1f"}, {0x33, "\x90\x02\x79"},
{0x33, "\x8c\x27\x21"}, {0x33, "\x90\x01\x55"}, {0x33, "\x8c\x27\x23"},
{0x33, "\x90\x02\x85"}, {0x33, "\x8c\x27\x25"}, {0x33, "\x90\x06\x0f"},
{0x33, "\x8c\x27\x27"}, {0x33, "\x90\x20\x20"}, {0x33, "\x8c\x27\x29"},
{0x33, "\x90\x20\x20"}, {0x33, "\x8c\x27\x2b"}, {0x33, "\x90\x10\x20"},
{0x33, "\x8c\x27\x2d"}, {0x33, "\x90\x20\x07"}, {0x33, "\x8c\x27\x2f"},
{0x33, "\x90\x00\x04"}, {0x33, "\x8c\x27\x31"}, {0x33, "\x90\x00\x04"},
{0x33, "\x8c\x27\x33"}, {0x33, "\x90\x04\xbb"}, {0x33, "\x8c\x27\x35"},
{0x33, "\x90\x06\x4b"}, {0x33, "\x8c\x27\x37"}, {0x33, "\x90\x00\x00"},
{0x33, "\x8c\x27\x39"}, {0x33, "\x90\x21\x11"}, {0x33, "\x8c\x27\x3b"},
{0x33, "\x90\x00\x24"}, {0x33, "\x8c\x27\x3d"}, {0x33, "\x90\x01\x20"},
{0x33, "\x8c\x27\x41"}, {0x33, "\x90\x01\x69"}, {0x33, "\x8c\x27\x45"},
{0x33, "\x90\x04\xed"}, {0x33, "\x8c\x27\x47"}, {0x33, "\x90\x09\x4c"},
{0x33, "\x8c\x27\x51"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x53"},
{0x33, "\x90\x03\x20"}, {0x33, "\x8c\x27\x55"}, {0x33, "\x90\x00\x00"},
{0x33, "\x8c\x27\x57"}, {0x33, "\x90\x02\x58"}, {0x33, "\x8c\x27\x5f"},
{0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x61"}, {0x33, "\x90\x06\x40"},
{0x33, "\x8c\x27\x63"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x65"},
{0x33, "\x90\x04\xb0"}, {0x33, "\x8c\x22\x2e"}, {0x33, "\x90\x00\xa1"},
{0x33, "\x8c\xa4\x08"}, {0x33, "\x90\x00\x1f"}, {0x33, "\x8c\xa4\x09"},
{0x33, "\x90\x00\x21"}, {0x33, "\x8c\xa4\x0a"}, {0x33, "\x90\x00\x25"},
{0x33, "\x8c\xa4\x0b"}, {0x33, "\x90\x00\x27"}, {0x33, "\x8c\x24\x11"},
{0x33, "\x90\x00\xa1"}, {0x33, "\x8c\x24\x13"}, {0x33, "\x90\x00\xc1"},
{0x33, "\x8c\x24\x15"},
};
static struct validx tbl_init_at_startup[] = {
{0x0000, 0x0000},
{53, 0xffff},
{0x0010, 0x0010},
{53, 0xffff},
{0x0008, 0x00c0},
{53, 0xffff},
{0x0001, 0x00c1},
{53, 0xffff},
{0x0001, 0x00c2},
{53, 0xffff},
{0x0020, 0x0006},
{53, 0xffff},
{0x006a, 0x000d},
{53, 0xffff},
};
static struct idxdata tbl_init_post_alt_low_a[] = {
{0x33, "\x8c\x27\x15"}, {0x33, "\x90\x00\x25"}, {0x33, "\x8c\x22\x2e"},
{0x33, "\x90\x00\x81"}, {0x33, "\x8c\xa4\x08"}, {0x33, "\x90\x00\x17"},
{0x33, "\x8c\xa4\x09"}, {0x33, "\x90\x00\x1a"}, {0x33, "\x8c\xa4\x0a"},
{0x33, "\x90\x00\x1d"}, {0x33, "\x8c\xa4\x0b"}, {0x33, "\x90\x00\x20"},
{0x33, "\x8c\x24\x11"}, {0x33, "\x90\x00\x81"}, {0x33, "\x8c\x24\x13"},
{0x33, "\x90\x00\x9b"},
};
static struct idxdata tbl_init_post_alt_low_b[] = {
{0x33, "\x8c\x27\x03"}, {0x33, "\x90\x03\x24"}, {0x33, "\x8c\x27\x05"},
{0x33, "\x90\x02\x58"}, {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"},
{2, "\xff\xff\xff"},
{0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"},
{2, "\xff\xff\xff"},
};
static struct idxdata tbl_init_post_alt_low_c[] = {
{0x34, "\x1e\x8f\x09"}, {0x34, "\x1c\x01\x28"}, {0x34, "\x1e\x8f\x09"},
{2, "\xff\xff\xff"},
{0x34, "\x1e\x8f\x09"}, {0x32, "\x14\x06\xe6"}, {0x33, "\x8c\xa1\x20"},
{0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x01"},
{0x33, "\x2e\x01\x00"}, {0x34, "\x04\x00\x2a"}, {0x33, "\x8c\xa7\x02"},
{0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x95"}, {0x33, "\x90\x01\x00"},
{2, "\xff\xff\xff"},
{0x33, "\x8c\xa1\x20"}, {0x33, "\x90\x00\x72"}, {0x33, "\x8c\xa1\x03"},
{0x33, "\x90\x00\x02"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x01"},
{2, "\xff\xff\xff"},
{0x33, "\x8c\xa1\x20"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa1\x03"},
{0x33, "\x90\x00\x01"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x00"},
{2, "\xff\xff\xff"}, /* - * */
{0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"},
{2, "\xff\xff\xff"},
{0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"},
{2, "\xff\xff\xff"},
{0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"},
{2, "\xff\xff\xff"},
{0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"},
{1, "\xff\xff\xff"},
};
static struct idxdata tbl_init_post_alt_low_d[] = {
{0x32, "\x10\x01\xf8"}, {0x34, "\xce\x01\xa8"}, {0x34, "\xd0\x66\x33"},
{0x34, "\xd2\x31\x9a"}, {0x34, "\xd4\x94\x63"}, {0x34, "\xd6\x4b\x25"},
{0x34, "\xd8\x26\x70"}, {0x34, "\xda\x72\x4c"}, {0x34, "\xdc\xff\x04"},
{0x34, "\xde\x01\x5b"}, {0x34, "\xe6\x01\x13"}, {0x34, "\xee\x0b\xf0"},
{0x34, "\xf6\x0b\xa4"}, {0x35, "\x00\xf6\xe7"}, {0x35, "\x08\x0d\xfd"},
{0x35, "\x10\x25\x63"}, {0x35, "\x18\x35\x6c"}, {0x35, "\x20\x42\x7e"},
{0x35, "\x28\x19\x44"}, {0x35, "\x30\x39\xd4"}, {0x35, "\x38\xf5\xa8"},
{0x35, "\x4c\x07\x90"}, {0x35, "\x44\x07\xb8"}, {0x35, "\x5c\x06\x88"},
{0x35, "\x54\x07\xff"}, {0x34, "\xe0\x01\x52"}, {0x34, "\xe8\x00\xcc"},
{0x34, "\xf0\x0d\x83"}, {0x34, "\xf8\x0c\xb3"}, {0x35, "\x02\xfe\xba"},
{0x35, "\x0a\x04\xe0"}, {0x35, "\x12\x1c\x63"}, {0x35, "\x1a\x2b\x5a"},
{0x35, "\x22\x32\x5e"}, {0x35, "\x2a\x0d\x28"}, {0x35, "\x32\x2c\x02"},
{0x35, "\x3a\xf4\xfa"}, {0x35, "\x4e\x07\xef"}, {0x35, "\x46\x07\x88"},
{0x35, "\x5e\x07\xc1"}, {0x35, "\x56\x04\x64"}, {0x34, "\xe4\x01\x15"},
{0x34, "\xec\x00\x82"}, {0x34, "\xf4\x0c\xce"}, {0x34, "\xfc\x0c\xba"},
{0x35, "\x06\x1f\x02"}, {0x35, "\x0e\x02\xe3"}, {0x35, "\x16\x1a\x50"},
{0x35, "\x1e\x24\x39"}, {0x35, "\x26\x23\x4c"}, {0x35, "\x2e\xf9\x1b"},
{0x35, "\x36\x23\x19"}, {0x35, "\x3e\x12\x08"}, {0x35, "\x52\x07\x22"},
{0x35, "\x4a\x03\xd3"}, {0x35, "\x62\x06\x54"}, {0x35, "\x5a\x04\x5d"},
{0x34, "\xe2\x01\x04"}, {0x34, "\xea\x00\xa0"}, {0x34, "\xf2\x0c\xbc"},
{0x34, "\xfa\x0c\x5b"}, {0x35, "\x04\x17\xf2"}, {0x35, "\x0c\x02\x08"},
{0x35, "\x14\x28\x43"}, {0x35, "\x1c\x28\x62"}, {0x35, "\x24\x2b\x60"},
{0x35, "\x2c\x07\x33"}, {0x35, "\x34\x1f\xb0"}, {0x35, "\x3c\xed\xcd"},
{0x35, "\x50\x00\x06"}, {0x35, "\x48\x07\xff"}, {0x35, "\x60\x05\x89"},
{0x35, "\x58\x07\xff"}, {0x35, "\x40\x00\xa0"}, {0x35, "\x42\x00\x00"},
{0x32, "\x10\x01\xfc"}, {0x33, "\x8c\xa1\x18"},
/* Flip/Mirror h/v=1 */
{0x33, "\x90\x00\x3c"}, {0x33, "\x8c\x27\x19"}, {0x33, "\x90\x04\x6c"},
{0x33, "\x8c\x27\x3b"}, {0x33, "\x90\x00\x24"}, {0x33, "\x8c\xa1\x03"},
{0x33, "\x90\x00\x06"},
{130, "\xff\xff\xff"},
{0x33, "\x90\x00\x06"}, {0x33, "\x90\x00\x06"}, {0x33, "\x90\x00\x06"},
{0x33, "\x90\x00\x06"}, {0x33, "\x90\x00\x06"}, {0x33, "\x90\x00\x06"},
{100, "\xff\xff\xff"},
/* ?? */
{0x33, "\x8c\xa1\x02"}, {0x33, "\x90\x00\x1f"}, {0x33, "\x8c\xa1\x02"},
{0x33, "\x90\x00\x1f"}, {0x33, "\x8c\xa1\x02"}, {0x33, "\x90\x00\x1f"},
{0x33, "\x8c\xa1\x02"}, {0x33, "\x90\x00\x1f"},
/* Brigthness=70 */
{0x33, "\x8c\xa2\x06"}, {0x33, "\x90\x00\x46"}, {0x33, "\x8c\xa1\x02"},
{0x33, "\x90\x00\x0f"}, {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"},
/* Sharpness=20 */
{0x32, "\x6c\x14\x08"},
};
static struct idxdata tbl_init_post_alt_big_a[] = {
{0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"},
{2, "\xff\xff\xff"},
{0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"},
{2, "\xff\xff\xff"},
{0x34, "\x1e\x8f\x09"}, {0x34, "\x1c\x01\x28"}, {0x34, "\x1e\x8f\x09"},
{0x34, "\x1e\x8f\x09"}, {0x32, "\x14\x06\xe6"}, {0x33, "\x8c\xa1\x03"},
{0x33, "\x90\x00\x05"},
{2, "\xff\xff\xff"},
{0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"},
{2, "\xff\xff\xff"},
{0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"},
{2, "\xff\xff\xff"},
{0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"}, {0x33, "\x8c\xa1\x20"},
{0x33, "\x90\x00\x72"}, {0x33, "\x8c\xa1\x30"}, {0x33, "\x90\x00\x03"},
{0x33, "\x8c\xa1\x31"}, {0x33, "\x90\x00\x02"}, {0x33, "\x8c\xa1\x32"},
{0x33, "\x90\x00\x03"}, {0x33, "\x8c\xa1\x34"}, {0x33, "\x90\x00\x03"},
{0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x02"}, {0x33, "\x2e\x01\x00"},
{0x34, "\x04\x00\x2a"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x01"},
};
static struct idxdata tbl_init_post_alt_big_b[] = {
{0x32, "\x10\x01\xf8"}, {0x34, "\xce\x01\xa8"}, {0x34, "\xd0\x66\x33"},
{0x34, "\xd2\x31\x9a"}, {0x34, "\xd4\x94\x63"}, {0x34, "\xd6\x4b\x25"},
{0x34, "\xd8\x26\x70"}, {0x34, "\xda\x72\x4c"}, {0x34, "\xdc\xff\x04"},
{0x34, "\xde\x01\x5b"}, {0x34, "\xe6\x01\x13"}, {0x34, "\xee\x0b\xf0"},
{0x34, "\xf6\x0b\xa4"}, {0x35, "\x00\xf6\xe7"}, {0x35, "\x08\x0d\xfd"},
{0x35, "\x10\x25\x63"}, {0x35, "\x18\x35\x6c"}, {0x35, "\x20\x42\x7e"},
{0x35, "\x28\x19\x44"}, {0x35, "\x30\x39\xd4"}, {0x35, "\x38\xf5\xa8"},
{0x35, "\x4c\x07\x90"}, {0x35, "\x44\x07\xb8"}, {0x35, "\x5c\x06\x88"},
{0x35, "\x54\x07\xff"}, {0x34, "\xe0\x01\x52"}, {0x34, "\xe8\x00\xcc"},
{0x34, "\xf0\x0d\x83"}, {0x34, "\xf8\x0c\xb3"}, {0x35, "\x02\xfe\xba"},
{0x35, "\x0a\x04\xe0"}, {0x35, "\x12\x1c\x63"}, {0x35, "\x1a\x2b\x5a"},
{0x35, "\x22\x32\x5e"}, {0x35, "\x2a\x0d\x28"}, {0x35, "\x32\x2c\x02"},
{0x35, "\x3a\xf4\xfa"}, {0x35, "\x4e\x07\xef"}, {0x35, "\x46\x07\x88"},
{0x35, "\x5e\x07\xc1"}, {0x35, "\x56\x04\x64"}, {0x34, "\xe4\x01\x15"},
{0x34, "\xec\x00\x82"}, {0x34, "\xf4\x0c\xce"}, {0x34, "\xfc\x0c\xba"},
{0x35, "\x06\x1f\x02"}, {0x35, "\x0e\x02\xe3"}, {0x35, "\x16\x1a\x50"},
{0x35, "\x1e\x24\x39"}, {0x35, "\x26\x23\x4c"}, {0x35, "\x2e\xf9\x1b"},
{0x35, "\x36\x23\x19"}, {0x35, "\x3e\x12\x08"}, {0x35, "\x52\x07\x22"},
{0x35, "\x4a\x03\xd3"}, {0x35, "\x62\x06\x54"}, {0x35, "\x5a\x04\x5d"},
{0x34, "\xe2\x01\x04"}, {0x34, "\xea\x00\xa0"}, {0x34, "\xf2\x0c\xbc"},
{0x34, "\xfa\x0c\x5b"}, {0x35, "\x04\x17\xf2"}, {0x35, "\x0c\x02\x08"},
{0x35, "\x14\x28\x43"}, {0x35, "\x1c\x28\x62"}, {0x35, "\x24\x2b\x60"},
{0x35, "\x2c\x07\x33"}, {0x35, "\x34\x1f\xb0"}, {0x35, "\x3c\xed\xcd"},
{0x35, "\x50\x00\x06"}, {0x35, "\x48\x07\xff"}, {0x35, "\x60\x05\x89"},
{0x35, "\x58\x07\xff"}, {0x35, "\x40\x00\xa0"}, {0x35, "\x42\x00\x00"},
{0x32, "\x10\x01\xfc"}, {0x33, "\x8c\xa1\x18"}, {0x33, "\x90\x00\x3c"},
};
static struct idxdata tbl_init_post_alt_big_c[] = {
{0x33, "\x8c\xa1\x02"},
{0x33, "\x90\x00\x1f"},
{0x33, "\x8c\xa1\x02"},
{0x33, "\x90\x00\x1f"},
{0x33, "\x8c\xa1\x02"},
{0x33, "\x90\x00\x1f"},
{0x33, "\x8c\xa1\x02"},
{0x33, "\x90\x00\x1f"},
};
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 = 50;
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 */
sd->vmax.saturation = 0; /* 100 */
sd->vmax.whitebal = 0; /* 100 */
sd->vmax.mirror = 1;
sd->vmax.flip = 1;
sd->vmax.AC50Hz = 1;
if (_MI2020b_) {
sd->vmax.contrast = 0;
sd->vmax.gamma = 0;
sd->vmax.backlight = 0;
}
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)
{
s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv;
if (_MI2020b_) {
fetch_validx(gspca_dev, tbl_common_a, ARRAY_SIZE(tbl_common_a));
} else {
if (_MI2020_)
ctrl_out(gspca_dev, 0x40, 1, 0x0008, 0x0004, 0, NULL);
else
ctrl_out(gspca_dev, 0x40, 1, 0x0002, 0x0004, 0, NULL);
msleep(35);
fetch_validx(gspca_dev, tbl_common_b, ARRAY_SIZE(tbl_common_b));
}
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x86\x25\x01");
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x86\x25\x00");
msleep(2); /* - * */
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0030, 3, "\x1a\x0a\xcc");
if (reso == IMAGE_1600)
msleep(2); /* 1600 */
fetch_idxdata(gspca_dev, tbl_common_c, ARRAY_SIZE(tbl_common_c));
if (_MI2020b_ || _MI2020_)
fetch_idxdata(gspca_dev, tbl_common_d,
ARRAY_SIZE(tbl_common_d));
fetch_idxdata(gspca_dev, tbl_common_e, ARRAY_SIZE(tbl_common_e));
if (_MI2020b_ || _MI2020_) {
/* Different from fret */
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x78");
/* Same as fret */
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\x24\x17");
/* Different from fret */
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x90");
} else {
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x6a");
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\x24\x17");
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x80");
}
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x03");
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x05");
msleep(2);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x03");
if (reso == IMAGE_1600)
msleep(14); /* 1600 */
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x06");
msleep(2);
}
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));
common(gspca_dev);
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.backlight = -1;
sd->vold.brightness = -1;
sd->vold.sharpness = -1;
sd->vold.contrast = -1;
sd->vold.gamma = -1;
sd->vold.hue = -1;
sd->vold.mirror = -1;
sd->vold.flip = -1;
sd->vold.AC50Hz = -1;
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 backlight = sd->vcur.backlight;
s32 mirror = (((sd->vcur.mirror > 0) ^ sd->mirrorMask) > 0);
s32 flip = (((sd->vcur.flip > 0) ^ sd->mirrorMask) > 0);
s32 freq = (sd->vcur.AC50Hz > 0);
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 c;
sd->nbIm = -1;
dat_freq2[2] = freq ? 0xc0 : 0x80;
dat_multi1[2] = 0x9d;
dat_multi3[2] = dat_multi1[2] + 1;
dat_multi4[2] = dat_multi2[2] = backlight;
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(3); /* 35 * */
common(gspca_dev);
ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x0000, 0, NULL);
msleep(70);
if (_MI2020b_)
ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000, 0, NULL);
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);
if (_MI2020c_)
fetch_idxdata(gspca_dev, tbl_init_post_alt_low_a,
ARRAY_SIZE(tbl_init_post_alt_low_a));
if (reso == IMAGE_800)
fetch_idxdata(gspca_dev, tbl_init_post_alt_low_b,
ARRAY_SIZE(tbl_init_post_alt_low_b));
fetch_idxdata(gspca_dev, tbl_init_post_alt_low_c,
ARRAY_SIZE(tbl_init_post_alt_low_c));
if (_MI2020b_) {
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(150);
} else if (_MI2020c_) {
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);
ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000, 0, NULL);
msleep(30);
} else if (_MI2020_) {
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(120);
ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000, 0, NULL);
msleep(30);
}
/* 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(20);
/* 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);
/* at init time but not after */
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa2\x0c");
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x17");
/* finish the backlight */
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi5);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi6);
msleep(5);/* " */
if (_MI2020c_) {
fetch_idxdata(gspca_dev, tbl_init_post_alt_low_d,
ARRAY_SIZE(tbl_init_post_alt_low_d));
} else {
ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, &c);
msleep(14); /* 0xd8 */
/* flip/mirror */
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(21);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033,
3, dat_dummy1);
msleep(5);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033,
3, dat_dummy1);
msleep(5);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033,
3, dat_dummy1);
msleep(5);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033,
3, dat_dummy1);
msleep(5);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033,
3, dat_dummy1);
msleep(5);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033,
3, dat_dummy1);
/* end of flip/mirror main part */
msleep(246); /* 146 */
sd->nbIm = 0;
}
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_a,
ARRAY_SIZE(tbl_init_post_alt_big_a));
if (reso == IMAGE_1600)
msleep(13); /* 1600 */
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\x27\x97");
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x01\x00");
msleep(53);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x20");
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x00");
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x03");
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x01");
if (reso == IMAGE_1600)
msleep(13); /* 1600 */
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa7\x02");
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x00");
msleep(53);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x20");
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x72");
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x03");
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x02");
if (reso == IMAGE_1600)
msleep(13); /* 1600 */
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa7\x02");
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x01");
msleep(53);
if (_MI2020b_) {
ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x0010, 0, NULL);
if (reso == IMAGE_1600)
msleep(500); /* 1600 */
ctrl_out(gspca_dev, 0x40, 1, 0x0000, 0x00c1, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x00c2, 0, NULL);
msleep(1850);
} else if (_MI2020c_ || _MI2020_) {
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(30);
}
/* 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(20);
/* 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);
/* at init time but not after */
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa2\x0c");
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x17");
/* finish the backlight */
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi5);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi6);
msleep(6); /* " */
ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, &c);
msleep(14);
if (_MI2020c_)
fetch_idxdata(gspca_dev, tbl_init_post_alt_big_b,
ARRAY_SIZE(tbl_init_post_alt_big_b));
/* flip/mirror */
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);
/* end of flip/mirror main part */
msleep(16);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x03");
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x01");
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x20");
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x00");
if (reso == IMAGE_1600)
msleep(25); /* 1600 */
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa7\x02");
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x00");
msleep(103);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x03");
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x02");
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x20");
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x72");
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa7\x02");
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x01");
sd->nbIm = 0;
if (_MI2020c_)
fetch_idxdata(gspca_dev, tbl_init_post_alt_big_c,
ARRAY_SIZE(tbl_init_post_alt_big_c));
}
sd->vold.mirror = mirror;
sd->vold.flip = flip;
sd->vold.AC50Hz = freq;
sd->vold.backlight = backlight;
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;
}
int mi2020_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 > 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);
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};
/* 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 (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);
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(130);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_dummy1);
msleep(6);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_dummy1);
msleep(6);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_dummy1);
msleep(6);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_dummy1);
msleep(6);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_dummy1);
msleep(6);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_dummy1);
msleep(6);
/* Sometimes present, sometimes not, useful? */
/* ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dummy2);
* ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dummy3);
* ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dummy2);
* ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dummy3);
* ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dummy2);
* ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dummy3);
* ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dummy2);
* ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dummy3);*/
}
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 (gam != sd->vold.gamma) {
sd->vold.gamma = gam;
if (gam < 0 || gam > sd->vmax.gamma)
gam = 0;
dat_multi1[2] = 0x6d;
dat_multi3[2] = dat_multi1[2] + 1;
dat_multi4[2] = dat_multi2[2] = 0x40 + 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 (cntr != sd->vold.contrast) {
sd->vold.contrast = cntr;
if (cntr < 0 || cntr > sd->vmax.contrast)
cntr = 0;
dat_multi1[2] = 0x6d;
dat_multi3[2] = dat_multi1[2] + 1;
dat_multi4[2] = dat_multi2[2] = 0x12 + 16 * cntr;
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 (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 (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(20);
if (_MI2020c_ || _MI2020_)
ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x0000, 0, NULL);
else
ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x0000, 0, NULL);
}

View File

@@ -0,0 +1,505 @@
/* @file gl860-ov2640.c
* @author Olivier LORIN, from Malmostoso's logs
* @date 2009-08-27
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* Sensor : OV2640 */
#include "gl860.h"
static u8 dat_init1[] = "\x00\x41\x07\x6a\x06\x61\x0d\x6a" "\x10\x10\xc1\x01";
static u8 dat_init2[] = {0x61}; /* expected */
static u8 dat_init3[] = {0x51}; /* 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 u8 c50[] = {0x50}; /* expected */
static u8 c28[] = {0x28}; /* expected */
static u8 ca8[] = {0xa8}; /* expected */
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_common_a[] = {
{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_common_b[] = {
{0x6001, 0x00ff}, {0x6038, 0x000c},
{10, 0xffff},
{0x6000, 0x0011},
/* backlight=31/64 */
{0x6001, 0x00ff}, {0x603e, 0x0024}, {0x6034, 0x0025},
/* bright=0/256 */
{0x6000, 0x00ff}, {0x6009, 0x007c}, {0x6000, 0x007d},
/* wbal=64/128 */
{0x6000, 0x00ff}, {0x6003, 0x007c}, {0x6040, 0x007d},
/* cntr=0/256 */
{0x6000, 0x00ff}, {0x6007, 0x007c}, {0x6000, 0x007d},
/* sat=128/256 */
{0x6000, 0x00ff}, {0x6001, 0x007c}, {0x6080, 0x007d},
/* sharpness=0/32 */
{0x6000, 0x00ff}, {0x6001, 0x0092}, {0x60c0, 0x0093},
/* hue=0/256 */
{0x6000, 0x00ff}, {0x6002, 0x007c}, {0x6000, 0x007d},
/* gam=32/64 */
{0x6000, 0x00ff}, {0x6008, 0x007c}, {0x6020, 0x007d},
/* image right up */
{0xffff, 0xffff},
{15, 0xffff},
{0x6001, 0x00ff}, {0x6000, 0x8004},
{0xffff, 0xffff},
{0x60a8, 0x0004},
{15, 0xffff},
{0x6001, 0x00ff}, {0x6000, 0x8004},
{0xffff, 0xffff},
{0x60f8, 0x0004},
/* image right up */
{0xffff, 0xffff},
/* backlight=31/64 */
{0x6001, 0x00ff}, {0x603e, 0x0024}, {0x6034, 0x0025},
};
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_big_a[] = {
{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_big_b[] = {
{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_big_c[] = {
{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->vmax.backlight = 64;
sd->vmax.brightness = 255;
sd->vmax.sharpness = 31;
sd->vmax.contrast = 255;
sd->vmax.gamma = 64;
sd->vmax.hue = 255 + 1;
sd->vmax.saturation = 255;
sd->vmax.whitebal = 128;
sd->vmax.mirror = 0;
sd->vmax.flip = 0;
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, dat_init2);
ctrl_out(gspca_dev, 0x40, 1, 0x00ef, 0x0006, 0, NULL);
ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, dat_init3);
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->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;
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 macros */
ctrl_out(gspca_dev, 0x40, 5, 0x0001, 0x0000, 0, NULL);
n = fetch_validx(gspca_dev, tbl_sensor_settings_common_a,
ARRAY_SIZE(tbl_sensor_settings_common_a));
ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, dat_post);
common(gspca_dev);
keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common_a,
ARRAY_SIZE(tbl_sensor_settings_common_a), 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_big_a, ARRAY_SIZE(tbl_big_a));
if (reso == IMAGE_1280) {
n = fetch_validx(gspca_dev, tbl_big_b,
ARRAY_SIZE(tbl_big_b));
} 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_big_c, ARRAY_SIZE(tbl_big_c));
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_common_b,
ARRAY_SIZE(tbl_sensor_settings_common_b));
ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, c50);
keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common_b,
ARRAY_SIZE(tbl_sensor_settings_common_b), n);
ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x8004, 1, c28);
keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common_b,
ARRAY_SIZE(tbl_sensor_settings_common_b), n);
ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x8004, 1, ca8);
keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common_b,
ARRAY_SIZE(tbl_sensor_settings_common_b), n);
ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, c50);
keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common_b,
ARRAY_SIZE(tbl_sensor_settings_common_b), n);
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;
if (backlight != sd->vold.backlight) {
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, 0x601f + backlight , 0x0024,
0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0x601f + backlight - 10, 0x0025,
0, NULL);
/* No sd->vold.backlight=backlight; (to be done again later) */
}
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 >= sd->vmax.hue)
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 (backlight != sd->vold.backlight) {
sd->vold.backlight = backlight;
ctrl_out(gspca_dev, 0x40, 1, 0x6001 , 0x00ff,
0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0x601f + backlight , 0x0024,
0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0x601f + 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));
}

View File

@@ -0,0 +1,337 @@
/* @file gl860-ov9655.c
* @author Olivier LORIN, from logs done by Simon (Sur3) and Almighurt
* on dsd's weblog
* @date 2009-08-27
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* Sensor : OV9655 */
#include "gl860.h"
static struct validx tbl_init_at_startup[] = {
{0x0000, 0x0000}, {0x0010, 0x0010}, {0x0008, 0x00c0}, {0x0001, 0x00c1},
{0x0001, 0x00c2}, {0x0020, 0x0006}, {0x006a, 0x000d},
{0x0040, 0x0000},
};
static struct validx tbl_commmon[] = {
{0x0041, 0x0000}, {0x006a, 0x0007}, {0x0063, 0x0006}, {0x006a, 0x000d},
{0x0000, 0x00c0}, {0x0010, 0x0010}, {0x0001, 0x00c1}, {0x0041, 0x00c2},
{0x0004, 0x00d8}, {0x0012, 0x0004}, {0x0000, 0x0058}, {0x0040, 0x0000},
{0x00f3, 0x0006}, {0x0058, 0x0000}, {0x0048, 0x0000}, {0x0061, 0x0000},
};
static s32 tbl_length[] = {12, 56, 52, 54, 56, 42, 32, 12};
static u8 *tbl_640[] = {
"\x00\x40\x07\x6a\x06\xf3\x0d\x6a" "\x10\x10\xc1\x01"
,
"\x12\x80\x00\x00\x01\x98\x02\x80" "\x03\x12\x04\x03\x0b\x57\x0e\x61"
"\x0f\x42\x11\x01\x12\x60\x13\x00" "\x14\x3a\x16\x24\x17\x14\x18\x00"
"\x19\x01\x1a\x3d\x1e\x04\x24\x3c" "\x25\x36\x26\x72\x27\x08\x28\x08"
"\x29\x15\x2a\x00\x2b\x00\x2c\x08"
,
"\x32\xff\x33\x00\x34\x3d\x35\x00" "\x36\xfa\x38\x72\x39\x57\x3a\x00"
"\x3b\x0c\x3d\x99\x3e\x0c\x3f\xc1" "\x40\xc0\x41\x00\x42\xc0\x43\x0a"
"\x44\xf0\x45\x46\x46\x62\x47\x2a" "\x48\x3c\x4a\xee\x4b\xe7\x4c\xe7"
"\x4d\xe7\x4e\xe7"
,
"\x4f\x98\x50\x98\x51\x00\x52\x28" "\x53\x70\x54\x98\x58\x1a\x59\x85"
"\x5a\xa9\x5b\x64\x5c\x84\x5d\x53" "\x5e\x0e\x5f\xf0\x60\xf0\x61\xf0"
"\x62\x00\x63\x00\x64\x02\x65\x20" "\x66\x00\x69\x0a\x6b\x5a\x6c\x04"
"\x6d\x55\x6e\x00\x6f\x9d"
,
"\x70\x15\x71\x78\x72\x00\x73\x00" "\x74\x3a\x75\x35\x76\x01\x77\x02"
"\x7a\x24\x7b\x04\x7c\x07\x7d\x10" "\x7e\x28\x7f\x36\x80\x44\x81\x52"
"\x82\x60\x83\x6c\x84\x78\x85\x8c" "\x86\x9e\x87\xbb\x88\xd2\x89\xe5"
"\x8a\x23\x8c\x8d\x90\x7c\x91\x7b"
,
"\x9d\x02\x9e\x02\x9f\x74\xa0\x73" "\xa1\x40\xa4\x50\xa5\x68\xa6\x70"
"\xa8\xc1\xa9\xef\xaa\x92\xab\x04" "\xac\x80\xad\x80\xae\x80\xaf\x80"
"\xb2\xf2\xb3\x20\xb4\x20\xb5\x00" "\xb6\xaf"
,
"\xbb\xae\xbc\x4f\xbd\x4e\xbe\x6a" "\xbf\x68\xc0\xaa\xc1\xc0\xc2\x01"
"\xc3\x4e\xc6\x85\xc7\x81\xc9\xe0" "\xca\xe8\xcb\xf0\xcc\xd8\xcd\x93"
,
"\xd0\x01\xd1\x08\xd2\xe0\xd3\x01" "\xd4\x10\xd5\x80"
};
static u8 *tbl_800[] = {
"\x00\x40\x07\x6a\x06\xf3\x0d\x6a" "\x10\x10\xc1\x01"
,
"\x12\x80\x00\x00\x01\x98\x02\x80" "\x03\x12\x04\x01\x0b\x57\x0e\x61"
"\x0f\x42\x11\x00\x12\x00\x13\x00" "\x14\x3a\x16\x24\x17\x1b\x18\xbb"
"\x19\x01\x1a\x81\x1e\x04\x24\x3c" "\x25\x36\x26\x72\x27\x08\x28\x08"
"\x29\x15\x2a\x00\x2b\x00\x2c\x08"
,
"\x32\xa4\x33\x00\x34\x3d\x35\x00" "\x36\xf8\x38\x72\x39\x57\x3a\x00"
"\x3b\x0c\x3d\x99\x3e\x0c\x3f\xc2" "\x40\xc0\x41\x00\x42\xc0\x43\x0a"
"\x44\xf0\x45\x46\x46\x62\x47\x2a" "\x48\x3c\x4a\xec\x4b\xe8\x4c\xe8"
"\x4d\xe8\x4e\xe8"
,
"\x4f\x98\x50\x98\x51\x00\x52\x28" "\x53\x70\x54\x98\x58\x1a\x59\x85"
"\x5a\xa9\x5b\x64\x5c\x84\x5d\x53" "\x5e\x0e\x5f\xf0\x60\xf0\x61\xf0"
"\x62\x00\x63\x00\x64\x02\x65\x20" "\x66\x00\x69\x02\x6b\x5a\x6c\x04"
"\x6d\x55\x6e\x00\x6f\x9d"
,
"\x70\x08\x71\x78\x72\x00\x73\x01" "\x74\x3a\x75\x35\x76\x01\x77\x02"
"\x7a\x24\x7b\x04\x7c\x07\x7d\x10" "\x7e\x28\x7f\x36\x80\x44\x81\x52"
"\x82\x60\x83\x6c\x84\x78\x85\x8c" "\x86\x9e\x87\xbb\x88\xd2\x89\xe5"
"\x8a\x23\x8c\x0d\x90\x90\x91\x90"
,
"\x9d\x02\x9e\x02\x9f\x94\xa0\x94" "\xa1\x01\xa4\x50\xa5\x68\xa6\x70"
"\xa8\xc1\xa9\xef\xaa\x92\xab\x04" "\xac\x80\xad\x80\xae\x80\xaf\x80"
"\xb2\xf2\xb3\x20\xb4\x20\xb5\x00" "\xb6\xaf"
,
"\xbb\xae\xbc\x38\xbd\x39\xbe\x01" "\xbf\x01\xc0\xe2\xc1\xc0\xc2\x01"
"\xc3\x4e\xc6\x85\xc7\x81\xc9\xe0" "\xca\xe8\xcb\xf0\xcc\xd8\xcd\x93"
,
"\xd0\x21\xd1\x18\xd2\xe0\xd3\x01" "\xd4\x28\xd5\x00"
};
static u8 c04[] = {0x04};
static u8 dat_post_1[] = "\x04\x00\x10\x20\xa1\x00\x00\x02";
static u8 dat_post_2[] = "\x10\x10\xc1\x02";
static u8 dat_post_3[] = "\x04\x00\x10\x7c\xa1\x00\x00\x04";
static u8 dat_post_4[] = "\x10\x02\xc1\x06";
static u8 dat_post_5[] = "\x04\x00\x10\x7b\xa1\x00\x00\x08";
static u8 dat_post_6[] = "\x10\x10\xc1\x05";
static u8 dat_post_7[] = "\x04\x00\x10\x7c\xa1\x00\x00\x08";
static u8 dat_post_8[] = "\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 macros */
s32 i;
u8 **tbl;
ctrl_out(gspca_dev, 0x40, 5, 0x0001, 0x0000, 0, NULL);
tbl = (reso == IMAGE_640) ? tbl_640 : tbl_800;
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_post_1);
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_post_1);
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_post_1);
ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post_2);
ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_3);
ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post_4);
ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_5);
ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post_6);
ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_7);
ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_8);
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);
}

View File

@@ -0,0 +1,785 @@
/* @file gl860.c
* @date 2009-08-27
*
* Genesys Logic webcam with gl860 subdrivers
*
* Driver by Olivier Lorin <o.lorin@laposte.net>
* GSPCA by Jean-Francois Moine <http://moinejf.free.fr>
* Thanks BUGabundo and Malmostoso for your amazing help!
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "gspca.h"
#include "gl860.h"
MODULE_AUTHOR("Olivier Lorin <lorin@laposte.net>");
MODULE_DESCRIPTION("GSPCA/Genesys Logic GL860 USB 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,
struct gspca_frame *frame, u8 *data, s32 len);
static void sd_callback(struct gspca_dev *gspca_dev);
static int gl860_guess_sensor(struct gspca_dev *gspca_dev,
s32 vendor_id, s32 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 =============================*/
/* Functions to get and set a control value */
#define SD_SETGET(thename) \
static int sd_set_##thename(struct gspca_dev *gspca_dev, s32 val)\
{\
struct sd *sd = (struct sd *) gspca_dev;\
\
sd->vcur.thename = val;\
if (gspca_dev->streaming)\
sd->dev_camera_settings(gspca_dev);\
return 0;\
} \
static int sd_get_##thename(struct gspca_dev *gspca_dev, s32 *val)\
{\
struct sd *sd = (struct sd *) gspca_dev;\
\
*val = sd->vcur.thename;\
return 0;\
}
SD_SETGET(mirror)
SD_SETGET(flip)
SD_SETGET(AC50Hz)
SD_SETGET(backlight)
SD_SETGET(brightness)
SD_SETGET(gamma)
SD_SETGET(hue)
SD_SETGET(saturation)
SD_SETGET(sharpness)
SD_SETGET(whitebal)
SD_SETGET(contrast)
#define GL860_NCTRLS 11
/* control table */
static struct ctrl sd_ctrls_mi1320[GL860_NCTRLS];
static struct ctrl sd_ctrls_mi2020[GL860_NCTRLS];
static struct ctrl sd_ctrls_mi2020b[GL860_NCTRLS];
static struct ctrl sd_ctrls_ov2640[GL860_NCTRLS];
static struct ctrl sd_ctrls_ov9655[GL860_NCTRLS];
#define SET_MY_CTRL(theid, \
thetype, thelabel, thename) \
if (sd->vmax.thename != 0) {\
sd_ctrls[nCtrls].qctrl.id = theid;\
sd_ctrls[nCtrls].qctrl.type = thetype;\
strcpy(sd_ctrls[nCtrls].qctrl.name, thelabel);\
sd_ctrls[nCtrls].qctrl.minimum = 0;\
sd_ctrls[nCtrls].qctrl.maximum = sd->vmax.thename;\
sd_ctrls[nCtrls].qctrl.default_value = sd->vcur.thename;\
sd_ctrls[nCtrls].qctrl.step = \
(sd->vmax.thename < 16) ? 1 : sd->vmax.thename/16;\
sd_ctrls[nCtrls].set = sd_set_##thename;\
sd_ctrls[nCtrls].get = sd_get_##thename;\
nCtrls++;\
}
static int gl860_build_control_table(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
struct ctrl *sd_ctrls;
int nCtrls = 0;
if (_MI1320_)
sd_ctrls = sd_ctrls_mi1320;
else if (_MI2020_)
sd_ctrls = sd_ctrls_mi2020;
else if (_MI2020b_)
sd_ctrls = sd_ctrls_mi2020b;
else if (_OV2640_)
sd_ctrls = sd_ctrls_ov2640;
else if (_OV9655_)
sd_ctrls = sd_ctrls_ov9655;
else
return 0;
memset(sd_ctrls, 0, GL860_NCTRLS * sizeof(struct ctrl));
SET_MY_CTRL(V4L2_CID_BRIGHTNESS,
V4L2_CTRL_TYPE_INTEGER, "Brightness", brightness)
SET_MY_CTRL(V4L2_CID_SHARPNESS,
V4L2_CTRL_TYPE_INTEGER, "Sharpness", sharpness)
SET_MY_CTRL(V4L2_CID_CONTRAST,
V4L2_CTRL_TYPE_INTEGER, "Contrast", contrast)
SET_MY_CTRL(V4L2_CID_GAMMA,
V4L2_CTRL_TYPE_INTEGER, "Gamma", gamma)
SET_MY_CTRL(V4L2_CID_HUE,
V4L2_CTRL_TYPE_INTEGER, "Palette", hue)
SET_MY_CTRL(V4L2_CID_SATURATION,
V4L2_CTRL_TYPE_INTEGER, "Saturation", saturation)
SET_MY_CTRL(V4L2_CID_WHITE_BALANCE_TEMPERATURE,
V4L2_CTRL_TYPE_INTEGER, "White Bal.", whitebal)
SET_MY_CTRL(V4L2_CID_BACKLIGHT_COMPENSATION,
V4L2_CTRL_TYPE_INTEGER, "Backlight" , backlight)
SET_MY_CTRL(V4L2_CID_HFLIP,
V4L2_CTRL_TYPE_BOOLEAN, "Mirror", mirror)
SET_MY_CTRL(V4L2_CID_VFLIP,
V4L2_CTRL_TYPE_BOOLEAN, "Flip", flip)
SET_MY_CTRL(V4L2_CID_POWER_LINE_FREQUENCY,
V4L2_CTRL_TYPE_BOOLEAN, "50Hz", AC50Hz)
return nCtrls;
}
/*==================== sud-driver structure initialisation =================*/
static struct sd_desc sd_desc_mi1320 = {
.name = MODULE_NAME,
.ctrls = sd_ctrls_mi1320,
.nctrls = GL860_NCTRLS,
.config = sd_config,
.init = sd_init,
.isoc_init = sd_isoc_init,
.start = sd_start,
.stop0 = sd_stop0,
.pkt_scan = sd_pkt_scan,
.dq_callback = sd_callback,
};
static struct sd_desc sd_desc_mi2020 = {
.name = MODULE_NAME,
.ctrls = sd_ctrls_mi2020,
.nctrls = GL860_NCTRLS,
.config = sd_config,
.init = sd_init,
.isoc_init = sd_isoc_init,
.start = sd_start,
.stop0 = sd_stop0,
.pkt_scan = sd_pkt_scan,
.dq_callback = sd_callback,
};
static struct sd_desc sd_desc_mi2020b = {
.name = MODULE_NAME,
.ctrls = sd_ctrls_mi2020b,
.nctrls = GL860_NCTRLS,
.config = sd_config,
.init = sd_init,
.isoc_init = sd_isoc_init,
.start = sd_start,
.stop0 = sd_stop0,
.pkt_scan = sd_pkt_scan,
.dq_callback = sd_callback,
};
static struct sd_desc sd_desc_ov2640 = {
.name = MODULE_NAME,
.ctrls = sd_ctrls_ov2640,
.nctrls = GL860_NCTRLS,
.config = sd_config,
.init = sd_init,
.isoc_init = sd_isoc_init,
.start = sd_start,
.stop0 = sd_stop0,
.pkt_scan = sd_pkt_scan,
.dq_callback = sd_callback,
};
static struct sd_desc sd_desc_ov9655 = {
.name = MODULE_NAME,
.ctrls = sd_ctrls_ov9655,
.nctrls = GL860_NCTRLS,
.config = sd_config,
.init = sd_init,
.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, 600, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
.bytesperline = 800,
.sizeimage = 800 * 600,
.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, 1200, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
.bytesperline = 1600,
.sizeimage = 1600 * 1200,
.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;
s32 vendor_id, product_id;
/* Get USB VendorID and ProductID */
vendor_id = le16_to_cpu(id->idVendor);
product_id = le16_to_cpu(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;
else if (strcmp(sensor, "MI2020b") == 0)
sd->sensor = ID_MI2020b;
/* 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;
gspca_dev->nbalt = 4;
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_MI2020b:
gspca_dev->sd_desc = &sd_desc_mi2020b;
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;
gl860_build_control_table(gspca_dev);
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;
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,
struct gspca_frame *frame, u8 *data, s32 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 endianess does not matter */
switch (*(s16 *) data) {
case 0x0202: /* End of frame, start a new one */
frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, data, 0);
nSkipped = 0;
if (sd->nbIm >= 0 && sd->nbIm < 10)
sd->nbIm++;
gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, 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, frame, 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 __devinitdata 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)
{
struct gspca_dev *gspca_dev;
s32 ret;
ret = gspca_dev_probe(intf, id,
&sd_desc_mi1320, sizeof(struct sd), THIS_MODULE);
if (ret >= 0) {
gspca_dev = usb_get_intfdata(intf);
PDEBUG(D_PROBE,
"Camera is now controlling video device /dev/video%d",
gspca_dev->vdev.minor);
}
return ret;
}
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,
#endif
};
/*====================== Init and Exit module functions ====================*/
static int __init sd_mod_init(void)
{
PDEBUG(D_PROBE, "driver startup - version %s", DRIVER_VERSION);
if (usb_register(&sd_driver) < 0)
return -1;
PDEBUG(D_PROBE, "driver registered");
return 0;
}
static void __exit sd_mod_exit(void)
{
usb_deregister(&sd_driver);
PDEBUG(D_PROBE, "driver deregistered");
}
module_init(sd_mod_init);
module_exit(sd_mod_exit);
/*==========================================================================*/
int gl860_RTx(struct gspca_dev *gspca_dev,
unsigned char pref, u32 req, u16 val, u16 index,
s32 len, void *pdata)
{
struct usb_device *udev = gspca_dev->dev;
s32 r = 0;
if (pref == 0x40) { /* Send */
if (len > 0) {
memcpy(gspca_dev->usb_buf, pdata, len);
r = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
req, pref, val, index,
gspca_dev->usb_buf,
len, 400 + 200 * (len > 1));
} else {
r = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
req, pref, val, index, NULL, len, 400);
}
} else { /* Receive */
if (len > 0) {
r = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
req, pref, val, index,
gspca_dev->usb_buf,
len, 400 + 200 * (len > 1));
memcpy(pdata, gspca_dev->usb_buf, len);
} else {
r = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
req, pref, val, index, NULL, len, 400);
}
}
if (r < 0)
PDEBUG(D_ERR,
"ctrl transfer failed %4d "
"[p%02x r%d v%04x i%04x len%d]",
r, pref, req, val, index, len);
else if (len > 1 && r < len)
PDEBUG(D_ERR, "short ctrl transfer %d/%d", r, len);
if ((_MI2020_ || _MI2020b_ || _MI2020c_) && (val || index))
msleep(1);
if (_OV2640_)
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,
s32 vendor_id, s32 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);
nOV = 0;
for (ntry = 0; ntry < 4; ntry++) {
ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000, 0, NULL);
msleep(3);
ctrl_out(gspca_dev, 0x40, 1, 0x0063, 0x0006, 0, NULL);
msleep(3);
ctrl_out(gspca_dev, 0x40, 1, 0x7a00, 0x8030, 0, NULL);
msleep(10);
ctrl_in(gspca_dev, 0xc0, 2, 0x7a00, 0x8030, 1, &probe);
PDEBUG(D_PROBE, "1st probe=%02x", probe);
if (probe == 0xff)
nOV++;
}
if (nOV) {
PDEBUG(D_PROBE, "0xff -> sensor OVXXXX");
PDEBUG(D_PROBE, "Probing for sensor OV2640 or OV9655");
nb26 = nb96 = 0;
for (ntry = 0; ntry < 4; ntry++) {
ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000,
0, NULL);
msleep(3);
ctrl_out(gspca_dev, 0x40, 1, 0x6000, 0x800a,
0, NULL);
msleep(10);
/* Wait for 26(OV2640) or 96(OV9655) */
ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x800a,
1, &probe);
PDEBUG(D_PROBE, "2nd probe=%02x", probe);
if (probe == 0x00)
nb26++;
if (probe == 0x26 || probe == 0x40) {
sd->sensor = ID_OV2640;
nb26 += 4;
break;
}
if (probe == 0x96 || probe == 0x55) {
sd->sensor = ID_OV9655;
nb96 += 4;
break;
}
if (probe == 0xff)
nb96++;
msleep(3);
}
if (nb26 < 4 && nb96 < 4) {
PDEBUG(D_PROBE, "No relevant answer ");
PDEBUG(D_PROBE, "* 1.3Mpixels -> use OV9655");
PDEBUG(D_PROBE, "* 2.0Mpixels -> use OV2640");
PDEBUG(D_PROBE,
"To force a sensor, add that line to "
"/etc/modprobe.d/options.conf:");
PDEBUG(D_PROBE, "options gspca_gl860 "
"sensor=\"OV2640\" or \"OV9655\"");
return -1;
}
} else { /* probe = 0 */
PDEBUG(D_PROBE, "No 0xff -> sensor MI2020");
sd->sensor = ID_MI2020;
}
}
if (_MI1320_) {
PDEBUG(D_PROBE, "05e3:f191 sensor MI1320 (1.3M)");
} else if (_MI2020_) {
PDEBUG(D_PROBE, "05e3:0503 sensor MI2020 (2.0M)");
} else if (_MI2020b_) {
PDEBUG(D_PROBE, "05e3:0503 sensor MI2020 alt. driver (2.0M)");
} else if (_OV9655_) {
PDEBUG(D_PROBE, "05e3:0503 sensor OV9655 (1.3M)");
} else if (_OV2640_) {
PDEBUG(D_PROBE, "05e3:0503 sensor OV2640 (2.0M)");
} else {
PDEBUG(D_PROBE, "***** Unknown sensor *****");
return -1;
}
return 0;
}

View File

@@ -0,0 +1,108 @@
/* @file gl860.h
* @author Olivier LORIN, tiré du pilote Syntek par Nicolas VIVIEN
* @date 2009-08-27
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef GL860_DEV_H
#define GL860_DEV_H
#include <linux/version.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 ID_MI2020b 16
#define _MI1320_ (((struct sd *) gspca_dev)->sensor == ID_MI1320)
#define _MI2020_ (((struct sd *) gspca_dev)->sensor == ID_MI2020)
#define _MI2020b_ (((struct sd *) gspca_dev)->sensor == ID_MI2020b)
#define _MI2020c_ 0
#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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,204 @@
#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 <linux/mutex.h>
/* compilation option */
#define GSPCA_DEBUG 1
#ifdef GSPCA_DEBUG
/* GSPCA our debug messages */
extern int gspca_debug;
#define PDEBUG(level, fmt, args...) \
do {\
if (gspca_debug & (level)) \
printk(KERN_INFO MODULE_NAME ": " fmt "\n", ## args); \
} while (0)
#define D_ERR 0x01
#define D_PROBE 0x02
#define D_CONF 0x04
#define D_STREAM 0x08
#define D_FRAM 0x10
#define D_PACK 0x20
#define D_USBI 0x40
#define D_USBO 0x80
#define D_V4L2 0x0100
#else
#define PDEBUG(level, fmt, args...)
#endif
#undef err
#define err(fmt, args...) \
printk(KERN_ERR MODULE_NAME ": " fmt "\n", ## args)
#undef info
#define info(fmt, args...) \
printk(KERN_INFO MODULE_NAME ": " fmt "\n", ## args)
#undef warn
#define warn(fmt, args...) \
printk(KERN_WARNING MODULE_NAME ": " fmt "\n", ## args)
#define GSPCA_MAX_FRAMES 16 /* maximum number of video frame buffers */
/* image transfers */
#define MAX_NURBS 4 /* max number of URBs */
/* device information - set at probe time */
struct cam {
int bulk_size; /* buffer size when image transfer by bulk */
const struct v4l2_pix_format *cam_mode; /* size nmodes */
char nmodes;
__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 */
u32 input_flags; /* value for ENUM_INPUT status flags */
};
struct gspca_dev;
struct gspca_frame;
/* subdriver operations */
typedef int (*cam_op) (struct gspca_dev *);
typedef void (*cam_v_op) (struct gspca_dev *);
typedef int (*cam_cf_op) (struct gspca_dev *, const struct usb_device_id *);
typedef int (*cam_jpg_op) (struct gspca_dev *,
struct v4l2_jpegcompression *);
typedef int (*cam_reg_op) (struct gspca_dev *,
struct v4l2_dbg_register *);
typedef int (*cam_ident_op) (struct gspca_dev *,
struct v4l2_dbg_chip_ident *);
typedef int (*cam_streamparm_op) (struct gspca_dev *,
struct v4l2_streamparm *);
typedef int (*cam_qmnu_op) (struct gspca_dev *,
struct v4l2_querymenu *);
typedef void (*cam_pkt_op) (struct gspca_dev *gspca_dev,
struct gspca_frame *frame,
__u8 *data,
int len);
struct ctrl {
struct v4l2_queryctrl qctrl;
int (*set)(struct gspca_dev *, __s32);
int (*get)(struct gspca_dev *, __s32 *);
};
/* subdriver description */
struct sd_desc {
/* information */
const char *name; /* sub-driver name */
/* controls */
const struct ctrl *ctrls;
int nctrls;
/* mandatory operations */
cam_cf_op config; /* called on probe */
cam_op init; /* called on probe and resume */
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_jpg_op get_jcomp;
cam_jpg_op set_jcomp;
cam_qmnu_op querymenu;
cam_streamparm_op get_streamparm;
cam_streamparm_op set_streamparm;
#ifdef CONFIG_VIDEO_ADV_DEBUG
cam_reg_op set_register;
cam_reg_op get_register;
#endif
cam_ident_op get_chip_ident;
};
/* packet types when moving from iso buf to frame buf */
enum gspca_packet_type {
DISCARD_PACKET,
FIRST_PACKET,
INTER_PACKET,
LAST_PACKET
};
struct gspca_frame {
__u8 *data; /* frame buffer */
__u8 *data_end; /* end of frame while filling */
int vma_use_count;
struct v4l2_buffer v4l2_buf;
};
struct gspca_dev {
struct video_device vdev; /* !! must be the first item */
struct module *module; /* subdriver handling the device */
struct usb_device *dev;
struct file *capt_file; /* file doing video capture */
struct cam cam; /* device information */
const struct sd_desc *sd_desc; /* subdriver description */
unsigned ctrl_dis; /* disabled controls (bit map) */
#define USB_BUF_SZ 64
__u8 *usb_buf; /* buffer for USB exchanges */
struct urb *urb[MAX_NURBS];
__u8 *frbuf; /* buffer for nframes */
struct gspca_frame frame[GSPCA_MAX_FRAMES];
__u32 frsz; /* frame size */
char nframes; /* number of frames */
char fr_i; /* frame being filled */
char fr_q; /* next frame to queue */
char fr_o; /* next frame to dequeue */
signed char fr_queue[GSPCA_MAX_FRAMES]; /* frame queue */
__u8 last_packet_type;
__s8 empty_packet; /* if (-1) don't check empty packets */
__u8 streaming;
__u8 curr_mode; /* current camera mode */
__u32 pixfmt; /* current mode parameters */
__u16 width;
__u16 height;
__u32 sequence; /* frame sequence number */
wait_queue_head_t wq; /* wait queue */
struct mutex usb_lock; /* usb exchange protection */
struct mutex read_lock; /* read protection */
struct mutex queue_lock; /* ISOC queue protection */
#ifdef CONFIG_PM
char frozen; /* suspend - resume */
#endif
char users; /* number of opens */
char present; /* device connected */
char nbufread; /* number of buffers for read() */
char nurbs; /* number of allocated URBs */
char memory; /* memory type (V4L2_MEMORY_xxx) */
__u8 iface; /* USB interface number */
__u8 alt; /* USB alternate setting */
__u8 nbalt; /* number of USB alternate settings */
u16 pkt_size; /* ISOC packet size */
};
int gspca_dev_probe(struct usb_interface *intf,
const struct usb_device_id *id,
const struct sd_desc *sd_desc,
int dev_size,
struct module *module);
void gspca_disconnect(struct usb_interface *intf);
struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev,
enum gspca_packet_type packet_type,
struct gspca_frame *frame,
const __u8 *data,
int len);
struct gspca_frame *gspca_get_i_frame(struct gspca_dev *gspca_dev);
#ifdef CONFIG_PM
int gspca_suspend(struct usb_interface *intf, pm_message_t message);
int gspca_resume(struct usb_interface *intf);
#endif
int gspca_auto_gain_n_exposure(struct gspca_dev *gspca_dev, int avg_lum,
int desired_avg_lum, int deadzone, int gain_knee, int exposure_knee);
#endif /* GSPCAV2_H */

View File

@@ -0,0 +1,390 @@
/*
* Jeilinj subdriver
*
* Supports some Jeilin dual-mode cameras which use bulk transport and
* download raw JPEG data.
*
* Copyright (C) 2009 Theodore Kilgore
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define MODULE_NAME "jeilinj"
#include <linux/workqueue.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_DATA_TIMEOUT 1000
/* Maximum transfer size to use. */
#define JEILINJ_MAX_TRANSFER 0x200
#define FRAME_HEADER_LEN 0x10
/* 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;
u8 quality; /* image quality */
u8 jpegqual; /* webcam quality */
u8 *jpeg_hdr;
};
struct jlj_command {
unsigned char instruction[2];
unsigned char ack_wanted;
};
/* 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}
};
/*
* cam uses endpoint 0x03 to send commands, 0x84 for read commands,
* and 0x82 for bulk transfer.
*/
/* All commands are two bytes only */
static int jlj_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)
PDEBUG(D_ERR, "command write [%02x] error %d",
gspca_dev->usb_buf[0], retval);
return retval;
}
/* Responses are one byte only */
static int jlj_read1(struct gspca_dev *gspca_dev, unsigned char response)
{
int retval;
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)
PDEBUG(D_ERR, "read command [%02x] error %d",
gspca_dev->usb_buf[0], retval);
return retval;
}
static int jlj_start(struct gspca_dev *gspca_dev)
{
int i;
int retval = -1;
u8 response = 0xff;
struct jlj_command start_commands[] = {
{{0x71, 0x81}, 0},
{{0x70, 0x05}, 0},
{{0x95, 0x70}, 1},
{{0x71, 0x81}, 0},
{{0x70, 0x04}, 0},
{{0x95, 0x70}, 1},
{{0x71, 0x00}, 0},
{{0x70, 0x08}, 0},
{{0x95, 0x70}, 1},
{{0x94, 0x02}, 0},
{{0xde, 0x24}, 0},
{{0x94, 0x02}, 0},
{{0xdd, 0xf0}, 0},
{{0x94, 0x02}, 0},
{{0xe3, 0x2c}, 0},
{{0x94, 0x02}, 0},
{{0xe4, 0x00}, 0},
{{0x94, 0x02}, 0},
{{0xe5, 0x00}, 0},
{{0x94, 0x02}, 0},
{{0xe6, 0x2c}, 0},
{{0x94, 0x03}, 0},
{{0xaa, 0x00}, 0},
{{0x71, 0x1e}, 0},
{{0x70, 0x06}, 0},
{{0x71, 0x80}, 0},
{{0x70, 0x07}, 0}
};
for (i = 0; i < ARRAY_SIZE(start_commands); i++) {
retval = jlj_write2(gspca_dev, start_commands[i].instruction);
if (retval < 0)
return retval;
if (start_commands[i].ack_wanted)
retval = jlj_read1(gspca_dev, response);
if (retval < 0)
return retval;
}
PDEBUG(D_ERR, "jlj_start retval is %d", retval);
return retval;
}
static int jlj_stop(struct gspca_dev *gspca_dev)
{
int i;
int retval;
struct jlj_command stop_commands[] = {
{{0x71, 0x00}, 0},
{{0x70, 0x09}, 0},
{{0x71, 0x80}, 0},
{{0x70, 0x05}, 0}
};
for (i = 0; i < ARRAY_SIZE(stop_commands); i++) {
retval = jlj_write2(gspca_dev, stop_commands[i].instruction);
if (retval < 0)
return retval;
}
return retval;
}
/* This function is called as a workqueue function and runs whenever the camera
* is streaming data. Because it is a workqueue function it is allowed to sleep
* so we can use synchronous USB calls. To avoid possible collisions with other
* threads attempting to use the camera's USB interface the gspca usb_lock is
* used when performing the one USB control operation inside the workqueue,
* which tells the camera to close the stream. In practice the only thing
* which needs to be protected against is the usb_set_interface call that
* gspca makes during stream_off. Otherwise the camera doesn't provide any
* controls that the user could try to change.
*/
static void jlj_dostream(struct work_struct *work)
{
struct sd *dev = container_of(work, struct sd, work_struct);
struct gspca_dev *gspca_dev = &dev->gspca_dev;
struct gspca_frame *frame;
int blocks_left; /* 0x200-sized blocks remaining in current frame. */
int size_in_blocks;
int act_len;
int discarding = 0; /* true if we failed to get space for frame. */
int packet_type;
int ret;
u8 *buffer;
buffer = kmalloc(JEILINJ_MAX_TRANSFER, GFP_KERNEL | GFP_DMA);
if (!buffer) {
PDEBUG(D_ERR, "Couldn't allocate USB buffer");
goto quit_stream;
}
while (gspca_dev->present && gspca_dev->streaming) {
if (!gspca_dev->present)
goto quit_stream;
/* Start a new frame, and add the JPEG header, first thing */
frame = gspca_get_i_frame(gspca_dev);
if (frame && !discarding)
gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
dev->jpeg_hdr, JPEG_HDR_SZ);
else
discarding = 1;
/*
* Now request data block 0. Line 0 reports the size
* to download, in blocks of size 0x200, and also tells the
* "actual" data size, in bytes, which seems best to ignore.
*/
ret = usb_bulk_msg(gspca_dev->dev,
usb_rcvbulkpipe(gspca_dev->dev, 0x82),
buffer, JEILINJ_MAX_TRANSFER, &act_len,
JEILINJ_DATA_TIMEOUT);
PDEBUG(D_STREAM,
"Got %d bytes out of %d for Block 0",
act_len, JEILINJ_MAX_TRANSFER);
if (ret < 0 || act_len < FRAME_HEADER_LEN)
goto quit_stream;
size_in_blocks = buffer[0x0a];
blocks_left = buffer[0x0a] - 1;
PDEBUG(D_STREAM, "blocks_left = 0x%x", blocks_left);
packet_type = INTER_PACKET;
if (frame && !discarding)
/* Toss line 0 of data block 0, keep the rest. */
gspca_frame_add(gspca_dev, packet_type,
frame, buffer + FRAME_HEADER_LEN,
JEILINJ_MAX_TRANSFER - FRAME_HEADER_LEN);
else
discarding = 1;
while (blocks_left > 0) {
if (!gspca_dev->present)
goto quit_stream;
ret = usb_bulk_msg(gspca_dev->dev,
usb_rcvbulkpipe(gspca_dev->dev, 0x82),
buffer, JEILINJ_MAX_TRANSFER, &act_len,
JEILINJ_DATA_TIMEOUT);
if (ret < 0 || act_len < JEILINJ_MAX_TRANSFER)
goto quit_stream;
PDEBUG(D_STREAM,
"%d blocks remaining for frame", blocks_left);
blocks_left -= 1;
if (blocks_left == 0)
packet_type = LAST_PACKET;
else
packet_type = INTER_PACKET;
if (frame && !discarding)
gspca_frame_add(gspca_dev, packet_type,
frame, buffer,
JEILINJ_MAX_TRANSFER);
else
discarding = 1;
}
}
quit_stream:
mutex_lock(&gspca_dev->usb_lock);
if (gspca_dev->present)
jlj_stop(gspca_dev);
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;
dev->quality = 85;
dev->jpegqual = 85;
PDEBUG(D_PROBE,
"JEILINJ camera detected"
" (vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct);
cam->cam_mode = jlj_mode;
cam->nmodes = 1;
cam->bulk = 1;
/* We don't use the buffer gspca allocates so make it small. */
cam->bulk_size = 32;
INIT_WORK(&dev->work_struct, jlj_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 jlj_dostream to finish */
destroy_workqueue(dev->work_thread);
dev->work_thread = NULL;
mutex_lock(&gspca_dev->usb_lock);
kfree(dev->jpeg_hdr);
}
/* this function is called at probe and resume time */
static int sd_init(struct gspca_dev *gspca_dev)
{
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;
/* create the JPEG header */
dev->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL);
if (dev->jpeg_hdr == NULL)
return -ENOMEM;
jpeg_define(dev->jpeg_hdr, gspca_dev->height, gspca_dev->width,
0x21); /* JPEG 422 */
jpeg_set_qual(dev->jpeg_hdr, dev->quality);
PDEBUG(D_STREAM, "Start streaming at 320x240");
ret = jlj_start(gspca_dev);
if (ret < 0) {
PDEBUG(D_ERR, "Start streaming command failed");
return ret;
}
/* Start the workqueue function to do the streaming */
dev->work_thread = create_singlethread_workqueue(MODULE_NAME);
queue_work(dev->work_thread, &dev->work_struct);
return 0;
}
/* Table of supported USB devices */
static const __devinitdata struct usb_device_id device_table[] = {
{USB_DEVICE(0x0979, 0x0280)},
{}
};
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,
#endif
};
/* -- module insert / remove -- */
static int __init sd_mod_init(void)
{
int ret;
ret = usb_register(&sd_driver);
if (ret < 0)
return ret;
PDEBUG(D_PROBE, "registered");
return 0;
}
static void __exit sd_mod_exit(void)
{
usb_deregister(&sd_driver);
PDEBUG(D_PROBE, "deregistered");
}
module_init(sd_mod_init);
module_exit(sd_mod_exit);

View File

@@ -0,0 +1,168 @@
#ifndef JPEG_H
#define JPEG_H 1
/*
* Insert a JPEG header at start of frame
*
* This module is used by the gspca subdrivers.
* A special case is done for Conexant webcams.
*
* Copyright (C) Jean-Francois Moine (http://moinejf.free.fr)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
/*
* generation options
* CONEX_CAM Conexant if present
*/
/* JPEG header */
static const u8 jpeg_head[] = {
0xff, 0xd8, /* jpeg */
/* quantization table quality 50% */
0xff, 0xdb, 0x00, 0x84, /* DQT */
0,
#define JPEG_QT0_OFFSET 7
0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e,
0x0d, 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28,
0x1a, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25,
0x1d, 0x28, 0x3a, 0x33, 0x3d, 0x3c, 0x39, 0x33,
0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, 0x44,
0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57,
0x5f, 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71,
0x79, 0x70, 0x64, 0x78, 0x5c, 0x65, 0x67, 0x63,
1,
#define JPEG_QT1_OFFSET 72
0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a,
0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63,
0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
/* huffman table */
0xff, 0xc4, 0x01, 0xa2,
0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
0x07, 0x08, 0x09, 0x0a, 0x0b, 0x01, 0x00, 0x03,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
0x0a, 0x0b, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03,
0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00,
0x00, 0x01, 0x7d, 0x01, 0x02, 0x03, 0x00, 0x04,
0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13,
0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81,
0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15,
0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72, 0x82,
0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25,
0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36,
0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46,
0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56,
0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66,
0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76,
0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86,
0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95,
0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4,
0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3,
0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2,
0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca,
0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9,
0xda, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5,
0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0x11, 0x00, 0x02,
0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05,
0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01,
0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06,
0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22,
0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1,
0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62,
0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25,
0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, 0x27, 0x28,
0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a,
0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a,
0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a,
0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a,
0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a,
0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2, 0xe3,
0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2,
0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa,
#ifdef CONEX_CAM
/* the Conexant frames start with SOF0 */
#define JPEG_HDR_SZ 556
#else
0xff, 0xc0, 0x00, 0x11, /* SOF0 (start of frame 0 */
0x08, /* data precision */
#define JPEG_HEIGHT_OFFSET 561
0x01, 0xe0, /* height */
0x02, 0x80, /* width */
0x03, /* component number */
0x01,
0x21, /* samples Y */
0x00, /* quant Y */
0x02, 0x11, 0x01, /* samples CbCr - quant CbCr */
0x03, 0x11, 0x01,
0xff, 0xda, 0x00, 0x0c, /* SOS (start of scan) */
0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00
#define JPEG_HDR_SZ 589
#endif
};
/* define the JPEG header */
static void jpeg_define(u8 *jpeg_hdr,
int height,
int width,
int samplesY)
{
memcpy(jpeg_hdr, jpeg_head, sizeof jpeg_head);
#ifndef CONEX_CAM
jpeg_hdr[JPEG_HEIGHT_OFFSET + 0] = height >> 8;
jpeg_hdr[JPEG_HEIGHT_OFFSET + 1] = height & 0xff;
jpeg_hdr[JPEG_HEIGHT_OFFSET + 2] = width >> 8;
jpeg_hdr[JPEG_HEIGHT_OFFSET + 3] = width & 0xff;
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 < 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

View File

@@ -0,0 +1,11 @@
config USB_M5602
tristate "ALi USB m5602 Camera Driver"
depends on VIDEO_V4L2 && USB_GSPCA
help
Say Y here if you want support for cameras based on the
ALi m5602 connected to various image sensors.
See <file:Documentation/video4linux/m5602.txt> for more info.
To compile this driver as a module, choose M here: the
module will be called gspca_m5602.

View File

@@ -0,0 +1,11 @@
obj-$(CONFIG_USB_M5602) += gspca_m5602.o
gspca_m5602-objs := m5602_core.o \
m5602_ov9650.o \
m5602_ov7660.o \
m5602_mt9m111.o \
m5602_po1030.o \
m5602_s5k83a.o \
m5602_s5k4aa.o
EXTRA_CFLAGS += -Idrivers/media/video/gspca

View File

@@ -0,0 +1,162 @@
/*
* USB Driver for ALi m5602 based webcams
*
* Copyright (C) 2008 Erik Andrén
* Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
* Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
*
* Portions of code to USB interface and ALi driver software,
* Copyright (c) 2006 Willem Duinker
* v4l2 interface modeled after the V4L2 driver
* for SN9C10x PC Camera Controllers
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, version 2.
*
*/
#ifndef M5602_BRIDGE_H_
#define M5602_BRIDGE_H_
#include "gspca.h"
#define MODULE_NAME "ALi m5602"
/*****************************************************************************/
#define M5602_XB_SENSOR_TYPE 0x00
#define M5602_XB_SENSOR_CTRL 0x01
#define M5602_XB_LINE_OF_FRAME_H 0x02
#define M5602_XB_LINE_OF_FRAME_L 0x03
#define M5602_XB_PIX_OF_LINE_H 0x04
#define M5602_XB_PIX_OF_LINE_L 0x05
#define M5602_XB_VSYNC_PARA 0x06
#define M5602_XB_HSYNC_PARA 0x07
#define M5602_XB_TEST_MODE_1 0x08
#define M5602_XB_TEST_MODE_2 0x09
#define M5602_XB_SIG_INI 0x0a
#define M5602_XB_DS_PARA 0x0e
#define M5602_XB_TRIG_PARA 0x0f
#define M5602_XB_CLK_PD 0x10
#define M5602_XB_MCU_CLK_CTRL 0x12
#define M5602_XB_MCU_CLK_DIV 0x13
#define M5602_XB_SEN_CLK_CTRL 0x14
#define M5602_XB_SEN_CLK_DIV 0x15
#define M5602_XB_AUD_CLK_CTRL 0x16
#define M5602_XB_AUD_CLK_DIV 0x17
#define M5602_OB_AC_LINK_STATE 0x22
#define M5602_OB_PCM_SLOT_INDEX 0x24
#define M5602_OB_GPIO_SLOT_INDEX 0x25
#define M5602_OB_ACRX_STATUS_ADDRESS_H 0x28
#define M5602_OB_ACRX_STATUS_DATA_L 0x29
#define M5602_OB_ACRX_STATUS_DATA_H 0x2a
#define M5602_OB_ACTX_COMMAND_ADDRESS 0x31
#define M5602_OB_ACRX_COMMAND_DATA_L 0x32
#define M5602_OB_ACTX_COMMAND_DATA_H 0X33
#define M5602_XB_DEVCTR1 0x41
#define M5602_XB_EPSETR0 0x42
#define M5602_XB_EPAFCTR 0x47
#define M5602_XB_EPBFCTR 0x49
#define M5602_XB_EPEFCTR 0x4f
#define M5602_XB_TEST_REG 0x53
#define M5602_XB_ALT2SIZE 0x54
#define M5602_XB_ALT3SIZE 0x55
#define M5602_XB_OBSFRAME 0x56
#define M5602_XB_PWR_CTL 0x59
#define M5602_XB_ADC_CTRL 0x60
#define M5602_XB_ADC_DATA 0x61
#define M5602_XB_MISC_CTRL 0x62
#define M5602_XB_SNAPSHOT 0x63
#define M5602_XB_SCRATCH_1 0x64
#define M5602_XB_SCRATCH_2 0x65
#define M5602_XB_SCRATCH_3 0x66
#define M5602_XB_SCRATCH_4 0x67
#define M5602_XB_I2C_CTRL 0x68
#define M5602_XB_I2C_CLK_DIV 0x69
#define M5602_XB_I2C_DEV_ADDR 0x6a
#define M5602_XB_I2C_REG_ADDR 0x6b
#define M5602_XB_I2C_DATA 0x6c
#define M5602_XB_I2C_STATUS 0x6d
#define M5602_XB_GPIO_DAT_H 0x70
#define M5602_XB_GPIO_DAT_L 0x71
#define M5602_XB_GPIO_DIR_H 0x72
#define M5602_XB_GPIO_DIR_L 0x73
#define M5602_XB_GPIO_EN_H 0x74
#define M5602_XB_GPIO_EN_L 0x75
#define M5602_XB_GPIO_DAT 0x76
#define M5602_XB_GPIO_DIR 0x77
#define M5602_XB_SEN_CLK_CONTROL 0x80
#define M5602_XB_SEN_CLK_DIVISION 0x81
#define M5602_XB_CPR_CLK_CONTROL 0x82
#define M5602_XB_CPR_CLK_DIVISION 0x83
#define M5602_XB_MCU_CLK_CONTROL 0x84
#define M5602_XB_MCU_CLK_DIVISION 0x85
#define M5602_XB_DCT_CLK_CONTROL 0x86
#define M5602_XB_DCT_CLK_DIVISION 0x87
#define M5602_XB_EC_CLK_CONTROL 0x88
#define M5602_XB_EC_CLK_DIVISION 0x89
#define M5602_XB_LBUF_CLK_CONTROL 0x8a
#define M5602_XB_LBUF_CLK_DIVISION 0x8b
#define I2C_BUSY 0x80
/*****************************************************************************/
/* Driver info */
#define DRIVER_AUTHOR "ALi m5602 Linux Driver Project"
#define DRIVER_DESC "ALi m5602 webcam driver"
#define M5602_ISOC_ENDPOINT_ADDR 0x81
#define M5602_INTR_ENDPOINT_ADDR 0x82
#define M5602_URB_MSG_TIMEOUT 5000
/*****************************************************************************/
/* A skeleton used for sending messages to the m5602 bridge */
static const unsigned char bridge_urb_skeleton[] = {
0x13, 0x00, 0x81, 0x00
};
/* A skeleton used for sending messages to the sensor */
static const unsigned char sensor_urb_skeleton[] = {
0x23, M5602_XB_GPIO_EN_H, 0x81, 0x06,
0x23, M5602_XB_MISC_CTRL, 0x81, 0x80,
0x13, M5602_XB_I2C_DEV_ADDR, 0x81, 0x00,
0x13, M5602_XB_I2C_REG_ADDR, 0x81, 0x00,
0x13, M5602_XB_I2C_DATA, 0x81, 0x00,
0x13, M5602_XB_I2C_CTRL, 0x81, 0x11
};
struct sd {
struct gspca_dev gspca_dev;
/* A pointer to the currently connected sensor */
const struct m5602_sensor *sensor;
struct sd_desc *desc;
/* Sensor private data */
void *sensor_priv;
/* The current frame's id, used to detect frame boundaries */
u8 frame_id;
/* The current frame count */
u32 frame_count;
};
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

View File

@@ -0,0 +1,440 @@
/*
* USB Driver for ALi m5602 based webcams
*
* Copyright (C) 2008 Erik Andrén
* Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
* Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
*
* Portions of code to USB interface and ALi driver software,
* Copyright (c) 2006 Willem Duinker
* v4l2 interface modeled after the V4L2 driver
* for SN9C10x PC Camera Controllers
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, version 2.
*
*/
#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 int dump_bridge;
int dump_sensor;
static const __devinitdata struct usb_device_id m5602_table[] = {
{USB_DEVICE(0x0402, 0x5602)},
{}
};
MODULE_DEVICE_TABLE(usb, m5602_table);
/* Reads a byte from the m5602 */
int m5602_read_bridge(struct sd *sd, const u8 address, u8 *i2c_data)
{
int err;
struct usb_device *udev = sd->gspca_dev.dev;
__u8 *buf = sd->gspca_dev.usb_buf;
err = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
0x04, 0xc0, 0x14,
0x8100 + address, buf,
1, M5602_URB_MSG_TIMEOUT);
*i2c_data = buf[0];
PDEBUG(D_CONF, "Reading bridge register 0x%x containing 0x%x",
address, *i2c_data);
/* usb_control_msg(...) returns the number of bytes sent upon success,
mask that and return zero instead*/
return (err < 0) ? err : 0;
}
/* Writes a byte to the m5602 */
int m5602_write_bridge(struct sd *sd, const u8 address, const u8 i2c_data)
{
int err;
struct usb_device *udev = sd->gspca_dev.dev;
__u8 *buf = sd->gspca_dev.usb_buf;
PDEBUG(D_CONF, "Writing bridge register 0x%x with 0x%x",
address, i2c_data);
memcpy(buf, bridge_urb_skeleton,
sizeof(bridge_urb_skeleton));
buf[1] = address;
buf[3] = i2c_data;
err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
0x04, 0x40, 0x19,
0x0000, buf,
4, M5602_URB_MSG_TIMEOUT);
/* usb_control_msg(...) returns the number of bytes sent upon success,
mask that and return zero instead */
return (err < 0) ? err : 0;
}
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;
if (!len || len > sd->sensor->i2c_regW)
return -EINVAL;
err = m5602_wait_for_i2c(sd);
if (err < 0)
return err;
err = m5602_write_bridge(sd, M5602_XB_I2C_DEV_ADDR,
sd->sensor->i2c_slave_id);
if (err < 0)
return err;
err = m5602_write_bridge(sd, M5602_XB_I2C_REG_ADDR, address);
if (err < 0)
return err;
/* Sensors with registers that are of only
one byte width are differently read */
/* FIXME: This works with the ov9650, but has issues with the po1030 */
if (sd->sensor->i2c_regW == 1) {
err = m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 1);
if (err < 0)
return err;
err = m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x08);
} else {
err = m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x18 + len);
}
for (i = 0; (i < len) && !err; i++) {
err = m5602_wait_for_i2c(sd);
if (err < 0)
return err;
err = m5602_read_bridge(sd, M5602_XB_I2C_DATA, &(i2c_data[i]));
PDEBUG(D_CONF, "Reading sensor register "
"0x%x containing 0x%x ", address, *i2c_data);
}
return err;
}
int m5602_write_sensor(struct sd *sd, const u8 address,
u8 *i2c_data, const u8 len)
{
int err, i;
u8 *p;
struct usb_device *udev = sd->gspca_dev.dev;
__u8 *buf = sd->gspca_dev.usb_buf;
/* No sensor with a data width larger than 16 bits has yet been seen */
if (len > sd->sensor->i2c_regW || !len)
return -EINVAL;
memcpy(buf, sensor_urb_skeleton,
sizeof(sensor_urb_skeleton));
buf[11] = sd->sensor->i2c_slave_id;
buf[15] = address;
/* Special case larger sensor writes */
p = buf + 16;
/* Copy a four byte write sequence for each byte to be written to */
for (i = 0; i < len; i++) {
memcpy(p, sensor_urb_skeleton + 16, 4);
p[3] = i2c_data[i];
p += 4;
PDEBUG(D_CONF, "Writing sensor register 0x%x with 0x%x",
address, i2c_data[i]);
}
/* Copy the tailer */
memcpy(p, sensor_urb_skeleton + 20, 4);
/* Set the total length */
p[3] = 0x10 + len;
err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
0x04, 0x40, 0x19,
0x0000, buf,
20 + len * 4, M5602_URB_MSG_TIMEOUT);
return (err < 0) ? err : 0;
}
/* Dump all the registers of the m5602 bridge,
unfortunately this breaks the camera until it's power cycled */
static void m5602_dump_bridge(struct sd *sd)
{
int i;
for (i = 0; i < 0x80; i++) {
unsigned char val = 0;
m5602_read_bridge(sd, i, &val);
info("ALi m5602 address 0x%x contains 0x%x", i, val);
}
info("Warning: The ALi m5602 webcam probably won't work "
"until it's power cycled");
}
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 */
info("Failed to find a sensor");
sd->sensor = NULL;
return -ENODEV;
}
static int m5602_configure(struct gspca_dev *gspca_dev,
const struct usb_device_id *id);
static int m5602_init(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
int err;
PDEBUG(D_CONF, "Initializing ALi m5602 webcam");
/* Run the init sequence */
err = sd->sensor->init(sd);
return err;
}
static int m5602_start_transfer(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
__u8 *buf = sd->gspca_dev.usb_buf;
int err;
/* Send start command to the camera */
const u8 buffer[4] = {0x13, 0xf9, 0x0f, 0x01};
if (sd->sensor->start)
sd->sensor->start(sd);
memcpy(buf, buffer, sizeof(buffer));
err = usb_control_msg(gspca_dev->dev,
usb_sndctrlpipe(gspca_dev->dev, 0),
0x04, 0x40, 0x19, 0x0000, buf,
sizeof(buffer), M5602_URB_MSG_TIMEOUT);
PDEBUG(D_STREAM, "Transfer started");
return (err < 0) ? err : 0;
}
static void m5602_urb_complete(struct gspca_dev *gspca_dev,
struct gspca_frame *frame,
__u8 *data, int len)
{
struct sd *sd = (struct sd *) gspca_dev;
if (len < 6) {
PDEBUG(D_PACK, "Packet is less than 6 bytes");
return;
}
/* Frame delimiter: ff xx xx xx ff ff */
if (data[0] == 0xff && data[4] == 0xff && data[5] == 0xff &&
data[2] != sd->frame_id) {
PDEBUG(D_FRAM, "Frame delimiter detected");
sd->frame_id = data[2];
/* Remove the extra fluff appended on each header */
data += 6;
len -= 6;
/* Complete the last frame (if any) */
frame = gspca_frame_add(gspca_dev, LAST_PACKET,
frame, data, 0);
sd->frame_count++;
/* Create a new frame */
gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, len);
PDEBUG(D_FRAM, "Starting new frame %d",
sd->frame_count);
} else {
int cur_frame_len = frame->data_end - frame->data;
/* Remove urb header */
data += 4;
len -= 4;
if (cur_frame_len + len <= frame->v4l2_buf.length) {
PDEBUG(D_FRAM, "Continuing frame %d copying %d bytes",
sd->frame_count, len);
gspca_frame_add(gspca_dev, INTER_PACKET, frame,
data, len);
} else if (frame->v4l2_buf.length - cur_frame_len > 0) {
/* Add the remaining data up to frame size */
gspca_frame_add(gspca_dev, INTER_PACKET, frame, data,
frame->v4l2_buf.length - 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, the ctrl and nctrl is filled at probe time */
static struct sd_desc sd_desc = {
.name = MODULE_NAME,
.config = m5602_configure,
.init = m5602_init,
.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;
sd->desc = &sd_desc;
if (dump_bridge)
m5602_dump_bridge(sd);
/* Probe sensor */
err = m5602_probe_sensor(sd);
if (err)
goto fail;
return 0;
fail:
PDEBUG(D_ERR, "ALi m5602 webcam failed");
cam->cam_mode = NULL;
cam->nmodes = 0;
return err;
}
static int m5602_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
THIS_MODULE);
}
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,
#endif
.disconnect = m5602_disconnect
};
/* -- module insert / remove -- */
static int __init mod_m5602_init(void)
{
int ret;
ret = usb_register(&sd_driver);
if (ret < 0)
return ret;
PDEBUG(D_PROBE, "registered");
return 0;
}
static void __exit mod_m5602_exit(void)
{
usb_deregister(&sd_driver);
PDEBUG(D_PROBE, "deregistered");
}
module_init(mod_m5602_init);
module_exit(mod_m5602_exit);
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");

View File

@@ -0,0 +1,645 @@
/*
* Driver for the mt9m111 sensor
*
* Copyright (C) 2008 Erik Andrén
* Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
* Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
*
* Portions of code to USB interface and ALi driver software,
* Copyright (c) 2006 Willem Duinker
* v4l2 interface modeled after the V4L2 driver
* for SN9C10x PC Camera Controllers
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, version 2.
*
*/
#include "m5602_mt9m111.h"
static int mt9m111_set_vflip(struct gspca_dev *gspca_dev, __s32 val);
static int mt9m111_get_vflip(struct gspca_dev *gspca_dev, __s32 *val);
static int mt9m111_get_hflip(struct gspca_dev *gspca_dev, __s32 *val);
static int mt9m111_set_hflip(struct gspca_dev *gspca_dev, __s32 val);
static int mt9m111_get_gain(struct gspca_dev *gspca_dev, __s32 *val);
static int mt9m111_set_gain(struct gspca_dev *gspca_dev, __s32 val);
static int mt9m111_set_auto_white_balance(struct gspca_dev *gspca_dev,
__s32 val);
static int mt9m111_get_auto_white_balance(struct gspca_dev *gspca_dev,
__s32 *val);
static int mt9m111_get_green_balance(struct gspca_dev *gspca_dev, __s32 *val);
static int mt9m111_set_green_balance(struct gspca_dev *gspca_dev, __s32 val);
static int mt9m111_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val);
static int mt9m111_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val);
static int mt9m111_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val);
static int mt9m111_set_red_balance(struct gspca_dev *gspca_dev, __s32 val);
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
}
};
const static struct ctrl mt9m111_ctrls[] = {
#define VFLIP_IDX 0
{
{
.id = V4L2_CID_VFLIP,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.name = "vertical flip",
.minimum = 0,
.maximum = 1,
.step = 1,
.default_value = 0
},
.set = mt9m111_set_vflip,
.get = mt9m111_get_vflip
},
#define HFLIP_IDX 1
{
{
.id = V4L2_CID_HFLIP,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.name = "horizontal flip",
.minimum = 0,
.maximum = 1,
.step = 1,
.default_value = 0
},
.set = mt9m111_set_hflip,
.get = mt9m111_get_hflip
},
#define GAIN_IDX 2
{
{
.id = V4L2_CID_GAIN,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "gain",
.minimum = 0,
.maximum = (INITIAL_MAX_GAIN - 1) * 2 * 2 * 2,
.step = 1,
.default_value = MT9M111_DEFAULT_GAIN,
.flags = V4L2_CTRL_FLAG_SLIDER
},
.set = mt9m111_set_gain,
.get = mt9m111_get_gain
},
#define AUTO_WHITE_BALANCE_IDX 3
{
{
.id = V4L2_CID_AUTO_WHITE_BALANCE,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.name = "auto white balance",
.minimum = 0,
.maximum = 1,
.step = 1,
.default_value = 0,
},
.set = mt9m111_set_auto_white_balance,
.get = mt9m111_get_auto_white_balance
},
#define GREEN_BALANCE_IDX 4
{
{
.id = M5602_V4L2_CID_GREEN_BALANCE,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "green balance",
.minimum = 0x00,
.maximum = 0x7ff,
.step = 0x1,
.default_value = MT9M111_GREEN_GAIN_DEFAULT,
.flags = V4L2_CTRL_FLAG_SLIDER
},
.set = mt9m111_set_green_balance,
.get = mt9m111_get_green_balance
},
#define BLUE_BALANCE_IDX 5
{
{
.id = V4L2_CID_BLUE_BALANCE,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "blue balance",
.minimum = 0x00,
.maximum = 0x7ff,
.step = 0x1,
.default_value = MT9M111_BLUE_GAIN_DEFAULT,
.flags = V4L2_CTRL_FLAG_SLIDER
},
.set = mt9m111_set_blue_balance,
.get = mt9m111_get_blue_balance
},
#define RED_BALANCE_IDX 5
{
{
.id = V4L2_CID_RED_BALANCE,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "red balance",
.minimum = 0x00,
.maximum = 0x7ff,
.step = 0x1,
.default_value = MT9M111_RED_GAIN_DEFAULT,
.flags = V4L2_CTRL_FLAG_SLIDER
},
.set = mt9m111_set_red_balance,
.get = mt9m111_get_red_balance
},
};
static void mt9m111_dump_registers(struct sd *sd);
int mt9m111_probe(struct sd *sd)
{
u8 data[2] = {0x00, 0x00};
int i;
s32 *sensor_settings;
if (force_sensor) {
if (force_sensor == MT9M111_SENSOR) {
info("Forcing a %s sensor", mt9m111.name);
goto sensor_found;
}
/* If we want to force another sensor, don't try to probe this
* one */
return -ENODEV;
}
info("Probing for a mt9m111 sensor");
/* Do the preinit */
for (i = 0; i < ARRAY_SIZE(preinit_mt9m111); i++) {
if (preinit_mt9m111[i][0] == BRIDGE) {
m5602_write_bridge(sd,
preinit_mt9m111[i][1],
preinit_mt9m111[i][2]);
} else {
data[0] = preinit_mt9m111[i][2];
data[1] = preinit_mt9m111[i][3];
m5602_write_sensor(sd,
preinit_mt9m111[i][1], data, 2);
}
}
if (m5602_read_sensor(sd, MT9M111_SC_CHIPVER, data, 2))
return -ENODEV;
if ((data[0] == 0x14) && (data[1] == 0x3a)) {
info("Detected a mt9m111 sensor");
goto sensor_found;
}
return -ENODEV;
sensor_found:
sensor_settings = kmalloc(ARRAY_SIZE(mt9m111_ctrls) * sizeof(s32),
GFP_KERNEL);
if (!sensor_settings)
return -ENOMEM;
sd->gspca_dev.cam.cam_mode = mt9m111_modes;
sd->gspca_dev.cam.nmodes = ARRAY_SIZE(mt9m111_modes);
sd->desc->ctrls = mt9m111_ctrls;
sd->desc->nctrls = ARRAY_SIZE(mt9m111_ctrls);
for (i = 0; i < ARRAY_SIZE(mt9m111_ctrls); i++)
sensor_settings[i] = mt9m111_ctrls[i].qctrl.default_value;
sd->sensor_priv = sensor_settings;
return 0;
}
int mt9m111_init(struct sd *sd)
{
int i, err = 0;
s32 *sensor_settings = sd->sensor_priv;
/* 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);
err = mt9m111_set_vflip(&sd->gspca_dev, sensor_settings[VFLIP_IDX]);
if (err < 0)
return err;
err = mt9m111_set_hflip(&sd->gspca_dev, sensor_settings[HFLIP_IDX]);
if (err < 0)
return err;
err = mt9m111_set_green_balance(&sd->gspca_dev,
sensor_settings[GREEN_BALANCE_IDX]);
if (err < 0)
return err;
err = mt9m111_set_blue_balance(&sd->gspca_dev,
sensor_settings[BLUE_BALANCE_IDX]);
if (err < 0)
return err;
err = mt9m111_set_red_balance(&sd->gspca_dev,
sensor_settings[RED_BALANCE_IDX]);
if (err < 0)
return err;
return mt9m111_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]);
}
int mt9m111_start(struct sd *sd)
{
int i, err = 0;
u8 data[2];
struct cam *cam = &sd->gspca_dev.cam;
s32 *sensor_settings = sd->sensor_priv;
int width = cam->cam_mode[sd->gspca_dev.curr_mode].width - 1;
int height = cam->cam_mode[sd->gspca_dev.curr_mode].height;
for (i = 0; i < ARRAY_SIZE(start_mt9m111) && !err; i++) {
if (start_mt9m111[i][0] == BRIDGE) {
err = m5602_write_bridge(sd,
start_mt9m111[i][1],
start_mt9m111[i][2]);
} else {
data[0] = start_mt9m111[i][2];
data[1] = start_mt9m111[i][3];
err = m5602_write_sensor(sd,
start_mt9m111[i][1], data, 2);
}
}
if (err < 0)
return err;
err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (height >> 8) & 0xff);
if (err < 0)
return err;
err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (height & 0xff));
if (err < 0)
return err;
for (i = 0; i < 2 && !err; i++)
err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, 0);
if (err < 0)
return err;
err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0);
if (err < 0)
return err;
err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 2);
if (err < 0)
return err;
for (i = 0; i < 2 && !err; i++)
err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, 0);
if (err < 0)
return err;
err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA,
(width >> 8) & 0xff);
if (err < 0)
return err;
err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, width & 0xff);
if (err < 0)
return err;
err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0);
if (err < 0)
return err;
switch (width) {
case 640:
PDEBUG(D_V4L2, "Configuring camera for VGA mode");
data[0] = MT9M111_RMB_OVER_SIZED;
data[1] = MT9M111_RMB_ROW_SKIP_2X |
MT9M111_RMB_COLUMN_SKIP_2X |
(sensor_settings[VFLIP_IDX] << 0) |
(sensor_settings[HFLIP_IDX] << 1);
err = m5602_write_sensor(sd,
MT9M111_SC_R_MODE_CONTEXT_B, data, 2);
break;
case 320:
PDEBUG(D_V4L2, "Configuring camera for QVGA mode");
data[0] = MT9M111_RMB_OVER_SIZED;
data[1] = MT9M111_RMB_ROW_SKIP_4X |
MT9M111_RMB_COLUMN_SKIP_4X |
(sensor_settings[VFLIP_IDX] << 0) |
(sensor_settings[HFLIP_IDX] << 1);
err = m5602_write_sensor(sd,
MT9M111_SC_R_MODE_CONTEXT_B, data, 2);
break;
}
return err;
}
void mt9m111_disconnect(struct sd *sd)
{
sd->sensor = NULL;
kfree(sd->sensor_priv);
}
static int mt9m111_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
*val = sensor_settings[VFLIP_IDX];
PDEBUG(D_V4L2, "Read vertical flip %d", *val);
return 0;
}
static int mt9m111_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
{
int err;
u8 data[2] = {0x00, 0x00};
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
PDEBUG(D_V4L2, "Set vertical flip to %d", val);
sensor_settings[VFLIP_IDX] = val;
/* The mt9m111 is flipped by default */
val = !val;
/* Set the correct page map */
err = m5602_write_sensor(sd, MT9M111_PAGE_MAP, data, 2);
if (err < 0)
return err;
err = m5602_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, data, 2);
if (err < 0)
return err;
data[1] = (data[1] & 0xfe) | val;
err = m5602_write_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B,
data, 2);
return err;
}
static int mt9m111_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
*val = sensor_settings[HFLIP_IDX];
PDEBUG(D_V4L2, "Read horizontal flip %d", *val);
return 0;
}
static int mt9m111_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
{
int err;
u8 data[2] = {0x00, 0x00};
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
PDEBUG(D_V4L2, "Set horizontal flip to %d", val);
sensor_settings[HFLIP_IDX] = val;
/* The mt9m111 is flipped by default */
val = !val;
/* Set the correct page map */
err = m5602_write_sensor(sd, MT9M111_PAGE_MAP, data, 2);
if (err < 0)
return err;
err = m5602_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, data, 2);
if (err < 0)
return err;
data[1] = (data[1] & 0xfd) | ((val << 1) & 0x02);
err = m5602_write_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B,
data, 2);
return err;
}
static int mt9m111_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
*val = sensor_settings[GAIN_IDX];
PDEBUG(D_V4L2, "Read gain %d", *val);
return 0;
}
static int mt9m111_set_auto_white_balance(struct gspca_dev *gspca_dev,
__s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
int err;
u8 data[2];
err = m5602_read_sensor(sd, MT9M111_CP_OPERATING_MODE_CTL, data, 2);
if (err < 0)
return err;
sensor_settings[AUTO_WHITE_BALANCE_IDX] = val & 0x01;
data[1] = ((data[1] & 0xfd) | ((val & 0x01) << 1));
err = m5602_write_sensor(sd, MT9M111_CP_OPERATING_MODE_CTL, data, 2);
PDEBUG(D_V4L2, "Set auto white balance %d", val);
return err;
}
static int mt9m111_get_auto_white_balance(struct gspca_dev *gspca_dev,
__s32 *val) {
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
*val = sensor_settings[AUTO_WHITE_BALANCE_IDX];
PDEBUG(D_V4L2, "Read auto white balance %d", *val);
return 0;
}
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;
s32 *sensor_settings = sd->sensor_priv;
sensor_settings[GAIN_IDX] = val;
/* Set the correct page map */
err = m5602_write_sensor(sd, MT9M111_PAGE_MAP, data, 2);
if (err < 0)
return err;
if (val >= INITIAL_MAX_GAIN * 2 * 2 * 2)
return -EINVAL;
if ((val >= INITIAL_MAX_GAIN * 2 * 2) &&
(val < (INITIAL_MAX_GAIN - 1) * 2 * 2 * 2))
tmp = (1 << 10) | (val << 9) |
(val << 8) | (val / 8);
else if ((val >= INITIAL_MAX_GAIN * 2) &&
(val < INITIAL_MAX_GAIN * 2 * 2))
tmp = (1 << 9) | (1 << 8) | (val / 4);
else if ((val >= INITIAL_MAX_GAIN) &&
(val < INITIAL_MAX_GAIN * 2))
tmp = (1 << 8) | (val / 2);
else
tmp = val;
data[1] = (tmp & 0xff);
data[0] = (tmp & 0xff00) >> 8;
PDEBUG(D_V4L2, "tmp=%d, data[1]=%d, data[0]=%d", tmp,
data[1], data[0]);
err = m5602_write_sensor(sd, MT9M111_SC_GLOBAL_GAIN,
data, 2);
return err;
}
static int mt9m111_set_green_balance(struct gspca_dev *gspca_dev, __s32 val)
{
int err;
u8 data[2];
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
sensor_settings[GREEN_BALANCE_IDX] = val;
data[1] = (val & 0xff);
data[0] = (val & 0xff00) >> 8;
PDEBUG(D_V4L2, "Set green balance %d", val);
err = m5602_write_sensor(sd, MT9M111_SC_GREEN_1_GAIN,
data, 2);
if (err < 0)
return err;
return m5602_write_sensor(sd, MT9M111_SC_GREEN_2_GAIN,
data, 2);
}
static int mt9m111_get_green_balance(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
*val = sensor_settings[GREEN_BALANCE_IDX];
PDEBUG(D_V4L2, "Read green balance %d", *val);
return 0;
}
static int mt9m111_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val)
{
u8 data[2];
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
sensor_settings[BLUE_BALANCE_IDX] = val;
data[1] = (val & 0xff);
data[0] = (val & 0xff00) >> 8;
PDEBUG(D_V4L2, "Set blue balance %d", val);
return m5602_write_sensor(sd, MT9M111_SC_BLUE_GAIN,
data, 2);
}
static int mt9m111_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
*val = sensor_settings[BLUE_BALANCE_IDX];
PDEBUG(D_V4L2, "Read blue balance %d", *val);
return 0;
}
static int mt9m111_set_red_balance(struct gspca_dev *gspca_dev, __s32 val)
{
u8 data[2];
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
sensor_settings[RED_BALANCE_IDX] = val;
data[1] = (val & 0xff);
data[0] = (val & 0xff00) >> 8;
PDEBUG(D_V4L2, "Set red balance %d", val);
return m5602_write_sensor(sd, MT9M111_SC_RED_GAIN,
data, 2);
}
static int mt9m111_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
*val = sensor_settings[RED_BALANCE_IDX];
PDEBUG(D_V4L2, "Read red balance %d", *val);
return 0;
}
static void mt9m111_dump_registers(struct sd *sd)
{
u8 address, value[2] = {0x00, 0x00};
info("Dumping the mt9m111 register state");
info("Dumping the mt9m111 sensor core registers");
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);
info("register 0x%x contains 0x%x%x",
address, value[0], value[1]);
}
info("Dumping the mt9m111 color pipeline registers");
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);
info("register 0x%x contains 0x%x%x",
address, value[0], value[1]);
}
info("Dumping the mt9m111 camera control registers");
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);
info("register 0x%x contains 0x%x%x",
address, value[0], value[1]);
}
info("mt9m111 register state dump complete");
}

View File

@@ -0,0 +1,275 @@
/*
* Driver for the mt9m111 sensor
*
* Copyright (C) 2008 Erik Andrén
* Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
* Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
*
* Portions of code to USB interface and ALi driver software,
* Copyright (c) 2006 Willem Duinker
* v4l2 interface modeled after the V4L2 driver
* for SN9C10x PC Camera Controllers
*
* Some defines taken from the mt9m111 sensor driver
* Copyright (C) 2008, Robert Jarzmik <robert.jarzmik@free.fr>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, version 2.
*
*/
#ifndef M5602_MT9M111_H_
#define M5602_MT9M111_H_
#include "m5602_sensor.h"
/*****************************************************************************/
#define MT9M111_SC_CHIPVER 0x00
#define MT9M111_SC_ROWSTART 0x01
#define MT9M111_SC_COLSTART 0x02
#define MT9M111_SC_WINDOW_HEIGHT 0x03
#define MT9M111_SC_WINDOW_WIDTH 0x04
#define MT9M111_SC_HBLANK_CONTEXT_B 0x05
#define MT9M111_SC_VBLANK_CONTEXT_B 0x06
#define MT9M111_SC_HBLANK_CONTEXT_A 0x07
#define MT9M111_SC_VBLANK_CONTEXT_A 0x08
#define MT9M111_SC_SHUTTER_WIDTH 0x09
#define MT9M111_SC_ROW_SPEED 0x0a
#define MT9M111_SC_EXTRA_DELAY 0x0b
#define MT9M111_SC_SHUTTER_DELAY 0x0c
#define MT9M111_SC_RESET 0x0d
#define MT9M111_SC_R_MODE_CONTEXT_B 0x20
#define MT9M111_SC_R_MODE_CONTEXT_A 0x21
#define MT9M111_SC_FLASH_CONTROL 0x23
#define MT9M111_SC_GREEN_1_GAIN 0x2b
#define MT9M111_SC_BLUE_GAIN 0x2c
#define MT9M111_SC_RED_GAIN 0x2d
#define MT9M111_SC_GREEN_2_GAIN 0x2e
#define MT9M111_SC_GLOBAL_GAIN 0x2f
#define MT9M111_CONTEXT_CONTROL 0xc8
#define MT9M111_PAGE_MAP 0xf0
#define MT9M111_BYTEWISE_ADDRESS 0xf1
#define MT9M111_CP_OPERATING_MODE_CTL 0x06
#define MT9M111_CP_LUMA_OFFSET 0x34
#define MT9M111_CP_LUMA_CLIP 0x35
#define MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_A 0x3a
#define MT9M111_CP_LENS_CORRECTION_1 0x3b
#define MT9M111_CP_DEFECT_CORR_CONTEXT_A 0x4c
#define MT9M111_CP_DEFECT_CORR_CONTEXT_B 0x4d
#define MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_B 0x9b
#define MT9M111_CP_GLOBAL_CLK_CONTROL 0xb3
#define MT9M111_CC_AUTO_EXPOSURE_PARAMETER_18 0x65
#define MT9M111_CC_AWB_PARAMETER_7 0x28
#define MT9M111_SENSOR_CORE 0x00
#define MT9M111_COLORPIPE 0x01
#define MT9M111_CAMERA_CONTROL 0x02
#define MT9M111_RESET (1 << 0)
#define MT9M111_RESTART (1 << 1)
#define MT9M111_ANALOG_STANDBY (1 << 2)
#define MT9M111_CHIP_ENABLE (1 << 3)
#define MT9M111_CHIP_DISABLE (0 << 3)
#define MT9M111_OUTPUT_DISABLE (1 << 4)
#define MT9M111_SHOW_BAD_FRAMES (1 << 0)
#define MT9M111_RESTART_BAD_FRAMES (1 << 1)
#define MT9M111_SYNCHRONIZE_CHANGES (1 << 7)
#define MT9M111_RMB_OVER_SIZED (1 << 0)
#define MT9M111_RMB_MIRROR_ROWS (1 << 0)
#define MT9M111_RMB_MIRROR_COLS (1 << 1)
#define MT9M111_RMB_ROW_SKIP_2X (1 << 2)
#define MT9M111_RMB_COLUMN_SKIP_2X (1 << 3)
#define MT9M111_RMB_ROW_SKIP_4X (1 << 4)
#define MT9M111_RMB_COLUMN_SKIP_4X (1 << 5)
#define MT9M111_COLOR_MATRIX_BYPASS (1 << 4)
#define MT9M111_SEL_CONTEXT_B (1 << 3)
#define MT9M111_TRISTATE_PIN_IN_STANDBY (1 << 1)
#define MT9M111_SOC_SOFT_STANDBY (1 << 0)
#define MT9M111_2D_DEFECT_CORRECTION_ENABLE (1 << 0)
#define INITIAL_MAX_GAIN 64
#define MT9M111_DEFAULT_GAIN 283
#define MT9M111_GREEN_GAIN_DEFAULT 0x20
#define MT9M111_BLUE_GAIN_DEFAULT 0x20
#define MT9M111_RED_GAIN_DEFAULT 0x20
/*****************************************************************************/
/* Kernel module parameters */
extern int force_sensor;
extern int dump_sensor;
int mt9m111_probe(struct sd *sd);
int mt9m111_init(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,
.disconnect = mt9m111_disconnect,
.start = mt9m111_start,
};
static const unsigned char preinit_mt9m111[][4] =
{
{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00},
{BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00},
{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
{BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
{SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
{SENSOR, MT9M111_SC_RESET,
MT9M111_RESET |
MT9M111_RESTART |
MT9M111_ANALOG_STANDBY |
MT9M111_CHIP_DISABLE,
MT9M111_SHOW_BAD_FRAMES |
MT9M111_RESTART_BAD_FRAMES |
MT9M111_SYNCHRONIZE_CHANGES},
{BRIDGE, M5602_XB_GPIO_DIR, 0x05, 0x00},
{BRIDGE, M5602_XB_GPIO_DAT, 0x04, 0x00},
{BRIDGE, M5602_XB_GPIO_EN_H, 0x3e, 0x00},
{BRIDGE, M5602_XB_GPIO_DIR_H, 0x3e, 0x00},
{BRIDGE, M5602_XB_GPIO_DAT_H, 0x02, 0x00},
{BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
{BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
{BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
{BRIDGE, M5602_XB_GPIO_DIR, 0x07, 0x00},
{BRIDGE, M5602_XB_GPIO_DAT, 0x0b, 0x00},
{BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
{BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
{BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a, 0x00}
};
static const unsigned char init_mt9m111[][4] =
{
{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
{BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
{BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
{BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
{BRIDGE, M5602_XB_GPIO_DAT, 0x04, 0x00},
{BRIDGE, M5602_XB_GPIO_DIR_H, 0x3e, 0x00},
{BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
{BRIDGE, M5602_XB_GPIO_DAT_H, 0x02, 0x00},
{BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
{BRIDGE, M5602_XB_GPIO_DIR, 0x07, 0x00},
{BRIDGE, M5602_XB_GPIO_DAT, 0x0b, 0x00},
{BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a, 0x00},
{SENSOR, MT9M111_SC_RESET, 0x00, 0x29},
{SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
{SENSOR, MT9M111_SC_RESET, 0x00, 0x08},
{SENSOR, MT9M111_PAGE_MAP, 0x00, 0x01},
{SENSOR, MT9M111_CP_OPERATING_MODE_CTL, 0x00,
MT9M111_CP_OPERATING_MODE_CTL},
{SENSOR, MT9M111_CP_LENS_CORRECTION_1, 0x04, 0x2a},
{SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_A, 0x00,
MT9M111_2D_DEFECT_CORRECTION_ENABLE},
{SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_B, 0x00,
MT9M111_2D_DEFECT_CORRECTION_ENABLE},
{SENSOR, MT9M111_CP_LUMA_OFFSET, 0x00, 0x00},
{SENSOR, MT9M111_CP_LUMA_CLIP, 0xff, 0x00},
{SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_A, 0x14, 0x00},
{SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_B, 0x14, 0x00},
{SENSOR, 0xcd, 0x00, 0x0e},
{SENSOR, 0xd0, 0x00, 0x40},
{SENSOR, MT9M111_PAGE_MAP, 0x00, 0x02},
{SENSOR, MT9M111_CC_AUTO_EXPOSURE_PARAMETER_18, 0x00, 0x00},
{SENSOR, MT9M111_CC_AWB_PARAMETER_7, 0xef, 0x03},
{SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
{SENSOR, 0x33, 0x03, 0x49},
{SENSOR, 0x34, 0xc0, 0x19},
{SENSOR, 0x3f, 0x20, 0x20},
{SENSOR, 0x40, 0x20, 0x20},
{SENSOR, 0x5a, 0xc0, 0x0a},
{SENSOR, 0x70, 0x7b, 0x0a},
{SENSOR, 0x71, 0xff, 0x00},
{SENSOR, 0x72, 0x19, 0x0e},
{SENSOR, 0x73, 0x18, 0x0f},
{SENSOR, 0x74, 0x57, 0x32},
{SENSOR, 0x75, 0x56, 0x34},
{SENSOR, 0x76, 0x73, 0x35},
{SENSOR, 0x77, 0x30, 0x12},
{SENSOR, 0x78, 0x79, 0x02},
{SENSOR, 0x79, 0x75, 0x06},
{SENSOR, 0x7a, 0x77, 0x0a},
{SENSOR, 0x7b, 0x78, 0x09},
{SENSOR, 0x7c, 0x7d, 0x06},
{SENSOR, 0x7d, 0x31, 0x10},
{SENSOR, 0x7e, 0x00, 0x7e},
{SENSOR, 0x80, 0x59, 0x04},
{SENSOR, 0x81, 0x59, 0x04},
{SENSOR, 0x82, 0x57, 0x0a},
{SENSOR, 0x83, 0x58, 0x0b},
{SENSOR, 0x84, 0x47, 0x0c},
{SENSOR, 0x85, 0x48, 0x0e},
{SENSOR, 0x86, 0x5b, 0x02},
{SENSOR, 0x87, 0x00, 0x5c},
{SENSOR, MT9M111_CONTEXT_CONTROL, 0x00, MT9M111_SEL_CONTEXT_B},
{SENSOR, 0x60, 0x00, 0x80},
{SENSOR, 0x61, 0x00, 0x00},
{SENSOR, 0x62, 0x00, 0x00},
{SENSOR, 0x63, 0x00, 0x00},
{SENSOR, 0x64, 0x00, 0x00},
{SENSOR, MT9M111_SC_ROWSTART, 0x00, 0x0d}, /* 13 */
{SENSOR, MT9M111_SC_COLSTART, 0x00, 0x12}, /* 18 */
{SENSOR, MT9M111_SC_WINDOW_HEIGHT, 0x04, 0x00}, /* 1024 */
{SENSOR, MT9M111_SC_WINDOW_WIDTH, 0x05, 0x10}, /* 1296 */
{SENSOR, MT9M111_SC_HBLANK_CONTEXT_B, 0x01, 0x60}, /* 352 */
{SENSOR, MT9M111_SC_VBLANK_CONTEXT_B, 0x00, 0x11}, /* 17 */
{SENSOR, MT9M111_SC_HBLANK_CONTEXT_A, 0x01, 0x60}, /* 352 */
{SENSOR, MT9M111_SC_VBLANK_CONTEXT_A, 0x00, 0x11}, /* 17 */
{SENSOR, MT9M111_SC_R_MODE_CONTEXT_A, 0x01, 0x0f}, /* 271 */
{SENSOR, 0x30, 0x04, 0x00},
/* Set number of blank rows chosen to 400 */
{SENSOR, MT9M111_SC_SHUTTER_WIDTH, 0x01, 0x90},
};
static const unsigned char start_mt9m111[][4] =
{
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
{BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
{BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
{BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
{BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
};
#endif

View File

@@ -0,0 +1,487 @@
/*
* Driver for the ov7660 sensor
*
* Copyright (C) 2009 Erik Andrén
* Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
* Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
*
* Portions of code to USB interface and ALi driver software,
* Copyright (c) 2006 Willem Duinker
* v4l2 interface modeled after the V4L2 driver
* for SN9C10x PC Camera Controllers
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, version 2.
*
*/
#include "m5602_ov7660.h"
static int ov7660_get_gain(struct gspca_dev *gspca_dev, __s32 *val);
static int ov7660_set_gain(struct gspca_dev *gspca_dev, __s32 val);
static int ov7660_get_auto_white_balance(struct gspca_dev *gspca_dev,
__s32 *val);
static int ov7660_set_auto_white_balance(struct gspca_dev *gspca_dev,
__s32 val);
static int ov7660_get_auto_gain(struct gspca_dev *gspca_dev, __s32 *val);
static int ov7660_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val);
static int ov7660_get_auto_exposure(struct gspca_dev *gspca_dev, __s32 *val);
static int ov7660_set_auto_exposure(struct gspca_dev *gspca_dev, __s32 val);
static int ov7660_get_hflip(struct gspca_dev *gspca_dev, __s32 *val);
static int ov7660_set_hflip(struct gspca_dev *gspca_dev, __s32 val);
static int ov7660_get_vflip(struct gspca_dev *gspca_dev, __s32 *val);
static int ov7660_set_vflip(struct gspca_dev *gspca_dev, __s32 val);
const static struct ctrl ov7660_ctrls[] = {
#define GAIN_IDX 1
{
{
.id = V4L2_CID_GAIN,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "gain",
.minimum = 0x00,
.maximum = 0xff,
.step = 0x1,
.default_value = OV7660_DEFAULT_GAIN,
.flags = V4L2_CTRL_FLAG_SLIDER
},
.set = ov7660_set_gain,
.get = ov7660_get_gain
},
#define BLUE_BALANCE_IDX 2
#define RED_BALANCE_IDX 3
#define AUTO_WHITE_BALANCE_IDX 4
{
{
.id = V4L2_CID_AUTO_WHITE_BALANCE,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.name = "auto white balance",
.minimum = 0,
.maximum = 1,
.step = 1,
.default_value = 1
},
.set = ov7660_set_auto_white_balance,
.get = ov7660_get_auto_white_balance
},
#define AUTO_GAIN_CTRL_IDX 5
{
{
.id = V4L2_CID_AUTOGAIN,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.name = "auto gain control",
.minimum = 0,
.maximum = 1,
.step = 1,
.default_value = 1
},
.set = ov7660_set_auto_gain,
.get = ov7660_get_auto_gain
},
#define AUTO_EXPOSURE_IDX 6
{
{
.id = V4L2_CID_EXPOSURE_AUTO,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.name = "auto exposure",
.minimum = 0,
.maximum = 1,
.step = 1,
.default_value = 1
},
.set = ov7660_set_auto_exposure,
.get = ov7660_get_auto_exposure
},
#define HFLIP_IDX 7
{
{
.id = V4L2_CID_HFLIP,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.name = "horizontal flip",
.minimum = 0,
.maximum = 1,
.step = 1,
.default_value = 0
},
.set = ov7660_set_hflip,
.get = ov7660_get_hflip
},
#define VFLIP_IDX 8
{
{
.id = V4L2_CID_VFLIP,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.name = "vertical flip",
.minimum = 0,
.maximum = 1,
.step = 1,
.default_value = 0
},
.set = ov7660_set_vflip,
.get = ov7660_get_vflip
},
};
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 void ov7660_dump_registers(struct sd *sd);
int ov7660_probe(struct sd *sd)
{
int err = 0, i;
u8 prod_id = 0, ver_id = 0;
s32 *sensor_settings;
if (force_sensor) {
if (force_sensor == OV7660_SENSOR) {
info("Forcing an %s sensor", 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;
info("Sensor reported 0x%x%x", prod_id, ver_id);
if ((prod_id == 0x76) && (ver_id == 0x60)) {
info("Detected a ov7660 sensor");
goto sensor_found;
}
return -ENODEV;
sensor_found:
sensor_settings = kmalloc(
ARRAY_SIZE(ov7660_ctrls) * sizeof(s32), GFP_KERNEL);
if (!sensor_settings)
return -ENOMEM;
sd->gspca_dev.cam.cam_mode = ov7660_modes;
sd->gspca_dev.cam.nmodes = ARRAY_SIZE(ov7660_modes);
sd->desc->ctrls = ov7660_ctrls;
sd->desc->nctrls = ARRAY_SIZE(ov7660_ctrls);
for (i = 0; i < ARRAY_SIZE(ov7660_ctrls); i++)
sensor_settings[i] = ov7660_ctrls[i].qctrl.default_value;
sd->sensor_priv = sensor_settings;
return 0;
}
int ov7660_init(struct sd *sd)
{
int i, err = 0;
s32 *sensor_settings = sd->sensor_priv;
/* 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 (dump_sensor)
ov7660_dump_registers(sd);
err = ov7660_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]);
if (err < 0)
return err;
err = ov7660_set_auto_white_balance(&sd->gspca_dev,
sensor_settings[AUTO_WHITE_BALANCE_IDX]);
if (err < 0)
return err;
err = ov7660_set_auto_gain(&sd->gspca_dev,
sensor_settings[AUTO_GAIN_CTRL_IDX]);
if (err < 0)
return err;
err = ov7660_set_auto_exposure(&sd->gspca_dev,
sensor_settings[AUTO_EXPOSURE_IDX]);
if (err < 0)
return err;
err = ov7660_set_hflip(&sd->gspca_dev,
sensor_settings[HFLIP_IDX]);
if (err < 0)
return err;
err = ov7660_set_vflip(&sd->gspca_dev,
sensor_settings[VFLIP_IDX]);
return err;
}
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;
kfree(sd->sensor_priv);
}
static int ov7660_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
*val = sensor_settings[GAIN_IDX];
PDEBUG(D_V4L2, "Read gain %d", *val);
return 0;
}
static int ov7660_set_gain(struct gspca_dev *gspca_dev, __s32 val)
{
int err;
u8 i2c_data;
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
PDEBUG(D_V4L2, "Setting gain to %d", val);
sensor_settings[GAIN_IDX] = val;
err = m5602_write_sensor(sd, OV7660_GAIN, &i2c_data, 1);
return err;
}
static int ov7660_get_auto_white_balance(struct gspca_dev *gspca_dev,
__s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
*val = sensor_settings[AUTO_WHITE_BALANCE_IDX];
return 0;
}
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;
s32 *sensor_settings = sd->sensor_priv;
PDEBUG(D_V4L2, "Set auto white balance to %d", val);
sensor_settings[AUTO_WHITE_BALANCE_IDX] = 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_get_auto_gain(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
*val = sensor_settings[AUTO_GAIN_CTRL_IDX];
PDEBUG(D_V4L2, "Read auto gain control %d", *val);
return 0;
}
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;
s32 *sensor_settings = sd->sensor_priv;
PDEBUG(D_V4L2, "Set auto gain control to %d", val);
sensor_settings[AUTO_GAIN_CTRL_IDX] = 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_get_auto_exposure(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
*val = sensor_settings[AUTO_EXPOSURE_IDX];
PDEBUG(D_V4L2, "Read auto exposure control %d", *val);
return 0;
}
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;
s32 *sensor_settings = sd->sensor_priv;
PDEBUG(D_V4L2, "Set auto exposure control to %d", val);
sensor_settings[AUTO_EXPOSURE_IDX] = val;
err = m5602_read_sensor(sd, OV7660_COM8, &i2c_data, 1);
if (err < 0)
return err;
i2c_data = ((i2c_data & 0xfe) | ((val & 0x01) << 0));
return m5602_write_sensor(sd, OV7660_COM8, &i2c_data, 1);
}
static int ov7660_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
*val = sensor_settings[HFLIP_IDX];
PDEBUG(D_V4L2, "Read horizontal flip %d", *val);
return 0;
}
static int ov7660_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
{
int err;
u8 i2c_data;
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
PDEBUG(D_V4L2, "Set horizontal flip to %d", val);
sensor_settings[HFLIP_IDX] = val;
i2c_data = ((val & 0x01) << 5) |
(sensor_settings[VFLIP_IDX] << 4);
err = m5602_write_sensor(sd, OV7660_MVFP, &i2c_data, 1);
return err;
}
static int ov7660_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
*val = sensor_settings[VFLIP_IDX];
PDEBUG(D_V4L2, "Read vertical flip %d", *val);
return 0;
}
static int ov7660_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
{
int err;
u8 i2c_data;
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
PDEBUG(D_V4L2, "Set vertical flip to %d", val);
sensor_settings[VFLIP_IDX] = val;
i2c_data = ((val & 0x01) << 4) | (sensor_settings[VFLIP_IDX] << 5);
err = m5602_write_sensor(sd, OV7660_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 = ov7660_start(sd);
return err;
}
static void ov7660_dump_registers(struct sd *sd)
{
int address;
info("Dumping the ov7660 register state");
for (address = 0; address < 0xa9; address++) {
u8 value;
m5602_read_sensor(sd, address, &value, 1);
info("register 0x%x contains 0x%x",
address, value);
}
info("ov7660 register state dump complete");
info("Probing for which registers that are read/write");
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])
info("register 0x%x is writeable", address);
else
info("register 0x%x is read only", address);
/* Restore original value */
m5602_write_sensor(sd, address, &old_value, 1);
}
}

View File

@@ -0,0 +1,263 @@
/*
* Driver for the ov7660 sensor
*
* Copyright (C) 2009 Erik Andrén
* Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
* Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
*
* Portions of code to USB interface and ALi driver software,
* Copyright (c) 2006 Willem Duinker
* v4l2 interface modeled after the V4L2 driver
* for SN9C10x PC Camera Controllers
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, version 2.
*
*/
#ifndef M5602_OV7660_H_
#define M5602_OV7660_H_
#include "m5602_sensor.h"
#define OV7660_GAIN 0x00
#define OV7660_BLUE_GAIN 0x01
#define OV7660_RED_GAIN 0x02
#define OV7660_VREF 0x03
#define OV7660_COM1 0x04
#define OV7660_BAVE 0x05
#define OV7660_GEAVE 0x06
#define OV7660_AECHH 0x07
#define OV7660_RAVE 0x08
#define OV7660_COM2 0x09
#define OV7660_PID 0x0a
#define OV7660_VER 0x0b
#define OV7660_COM3 0x0c
#define OV7660_COM4 0x0d
#define OV7660_COM5 0x0e
#define OV7660_COM6 0x0f
#define OV7660_AECH 0x10
#define OV7660_CLKRC 0x11
#define OV7660_COM7 0x12
#define OV7660_COM8 0x13
#define OV7660_COM9 0x14
#define OV7660_COM10 0x15
#define OV7660_RSVD16 0x16
#define OV7660_HSTART 0x17
#define OV7660_HSTOP 0x18
#define OV7660_VSTART 0x19
#define OV7660_VSTOP 0x1a
#define OV7660_PSHFT 0x1b
#define OV7660_MIDH 0x1c
#define OV7660_MIDL 0x1d
#define OV7660_MVFP 0x1e
#define OV7660_LAEC 0x1f
#define OV7660_BOS 0x20
#define OV7660_GBOS 0x21
#define OV7660_GROS 0x22
#define OV7660_ROS 0x23
#define OV7660_AEW 0x24
#define OV7660_AEB 0x25
#define OV7660_VPT 0x26
#define OV7660_BBIAS 0x27
#define OV7660_GbBIAS 0x28
#define OV7660_RSVD29 0x29
#define OV7660_RBIAS 0x2c
#define OV7660_HREF 0x32
#define OV7660_ADC 0x37
#define OV7660_OFON 0x39
#define OV7660_TSLB 0x3a
#define OV7660_COM12 0x3c
#define OV7660_COM13 0x3d
#define OV7660_LCC1 0x62
#define OV7660_LCC2 0x63
#define OV7660_LCC3 0x64
#define OV7660_LCC4 0x65
#define OV7660_LCC5 0x66
#define OV7660_HV 0x69
#define OV7660_RSVDA1 0xa1
#define OV7660_DEFAULT_GAIN 0x0e
#define OV7660_DEFAULT_RED_GAIN 0x80
#define OV7660_DEFAULT_BLUE_GAIN 0x80
#define OV7660_DEFAULT_SATURATION 0x00
#define OV7660_DEFAULT_EXPOSURE 0x20
/* Kernel module parameters */
extern int force_sensor;
extern int dump_sensor;
int ov7660_probe(struct sd *sd);
int ov7660_init(struct sd *sd);
int ov7660_start(struct sd *sd);
int ov7660_stop(struct sd *sd);
void ov7660_disconnect(struct sd *sd);
const static struct m5602_sensor ov7660 = {
.name = "ov7660",
.i2c_slave_id = 0x42,
.i2c_regW = 1,
.probe = ov7660_probe,
.init = ov7660_init,
.start = ov7660_start,
.stop = ov7660_stop,
.disconnect = ov7660_disconnect,
};
static const unsigned char preinit_ov7660[][4] =
{
{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02},
{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0},
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d},
{BRIDGE, M5602_XB_SENSOR_CTRL, 0x00},
{BRIDGE, M5602_XB_GPIO_DIR, 0x03},
{BRIDGE, M5602_XB_GPIO_DIR, 0x03},
{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
{SENSOR, OV7660_OFON, 0x0c},
{SENSOR, OV7660_COM2, 0x11},
{SENSOR, OV7660_COM7, 0x05},
{BRIDGE, M5602_XB_GPIO_DIR, 0x01},
{BRIDGE, M5602_XB_GPIO_DAT, 0x04},
{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
{BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x08},
{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
{BRIDGE, M5602_XB_GPIO_DIR, 0x05},
{BRIDGE, M5602_XB_GPIO_DAT, 0x00},
{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
{BRIDGE, M5602_XB_GPIO_EN_L, 0x00}
};
static const unsigned char init_ov7660[][4] =
{
{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02},
{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0},
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d},
{BRIDGE, M5602_XB_SENSOR_CTRL, 0x00},
{BRIDGE, M5602_XB_GPIO_DIR, 0x01},
{BRIDGE, M5602_XB_GPIO_DIR, 0x01},
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
{BRIDGE, M5602_XB_GPIO_DIR, 0x05},
{BRIDGE, M5602_XB_GPIO_DAT, 0x00},
{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
{BRIDGE, M5602_XB_GPIO_EN_L, 0x00},
{SENSOR, OV7660_COM7, 0x80},
{SENSOR, OV7660_CLKRC, 0x80},
{SENSOR, OV7660_COM9, 0x4c},
{SENSOR, OV7660_OFON, 0x43},
{SENSOR, OV7660_COM12, 0x28},
{SENSOR, OV7660_COM8, 0x00},
{SENSOR, OV7660_COM10, 0x40},
{SENSOR, OV7660_HSTART, 0x0c},
{SENSOR, OV7660_HSTOP, 0x61},
{SENSOR, OV7660_HREF, 0xa4},
{SENSOR, OV7660_PSHFT, 0x0b},
{SENSOR, OV7660_VSTART, 0x01},
{SENSOR, OV7660_VSTOP, 0x7a},
{SENSOR, OV7660_VSTOP, 0x00},
{SENSOR, OV7660_COM7, 0x05},
{SENSOR, OV7660_COM6, 0x42},
{SENSOR, OV7660_BBIAS, 0x94},
{SENSOR, OV7660_GbBIAS, 0x94},
{SENSOR, OV7660_RSVD29, 0x94},
{SENSOR, OV7660_RBIAS, 0x94},
{SENSOR, OV7660_COM1, 0x00},
{SENSOR, OV7660_AECH, 0x00},
{SENSOR, OV7660_AECHH, 0x00},
{SENSOR, OV7660_ADC, 0x05},
{SENSOR, OV7660_COM13, 0x00},
{SENSOR, OV7660_RSVDA1, 0x23},
{SENSOR, OV7660_TSLB, 0x0d},
{SENSOR, OV7660_HV, 0x80},
{SENSOR, OV7660_LCC1, 0x00},
{SENSOR, OV7660_LCC2, 0x00},
{SENSOR, OV7660_LCC3, 0x10},
{SENSOR, OV7660_LCC4, 0x40},
{SENSOR, OV7660_LCC5, 0x01},
{SENSOR, OV7660_AECH, 0x20},
{SENSOR, OV7660_COM1, 0x00},
{SENSOR, OV7660_OFON, 0x0c},
{SENSOR, OV7660_COM2, 0x11},
{SENSOR, OV7660_COM7, 0x05},
{BRIDGE, M5602_XB_GPIO_DIR, 0x01},
{BRIDGE, M5602_XB_GPIO_DAT, 0x04},
{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
{BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x08},
{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
{BRIDGE, M5602_XB_GPIO_DIR, 0x05},
{BRIDGE, M5602_XB_GPIO_DAT, 0x00},
{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
{BRIDGE, M5602_XB_GPIO_EN_L, 0x00},
{SENSOR, OV7660_AECH, 0x5f},
{SENSOR, OV7660_COM1, 0x03},
{SENSOR, OV7660_OFON, 0x0c},
{SENSOR, OV7660_COM2, 0x11},
{SENSOR, OV7660_COM7, 0x05},
{BRIDGE, M5602_XB_GPIO_DIR, 0x01},
{BRIDGE, M5602_XB_GPIO_DAT, 0x04},
{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
{BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x08},
{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
{BRIDGE, M5602_XB_GPIO_DIR, 0x05},
{BRIDGE, M5602_XB_GPIO_DAT, 0x00},
{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
{BRIDGE, M5602_XB_GPIO_EN_L, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06},
{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
{BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81},
{BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82},
{BRIDGE, M5602_XB_SIG_INI, 0x01},
{BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
{BRIDGE, M5602_XB_VSYNC_PARA, 0x08},
{BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
{BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
{BRIDGE, M5602_XB_VSYNC_PARA, 0x01},
{BRIDGE, M5602_XB_VSYNC_PARA, 0xec},
{BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
{BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
{BRIDGE, M5602_XB_SIG_INI, 0x00},
{BRIDGE, M5602_XB_SIG_INI, 0x02},
{BRIDGE, M5602_XB_HSYNC_PARA, 0x00},
{BRIDGE, M5602_XB_HSYNC_PARA, 0x27},
{BRIDGE, M5602_XB_HSYNC_PARA, 0x02},
{BRIDGE, M5602_XB_HSYNC_PARA, 0xa7},
{BRIDGE, M5602_XB_SIG_INI, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
};
#endif

View File

@@ -0,0 +1,880 @@
/*
* Driver for the ov9650 sensor
*
* Copyright (C) 2008 Erik Andrén
* Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
* Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
*
* Portions of code to USB interface and ALi driver software,
* Copyright (c) 2006 Willem Duinker
* v4l2 interface modeled after the V4L2 driver
* for SN9C10x PC Camera Controllers
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, version 2.
*
*/
#include "m5602_ov9650.h"
static int ov9650_set_exposure(struct gspca_dev *gspca_dev, __s32 val);
static int ov9650_get_exposure(struct gspca_dev *gspca_dev, __s32 *val);
static int ov9650_get_gain(struct gspca_dev *gspca_dev, __s32 *val);
static int ov9650_set_gain(struct gspca_dev *gspca_dev, __s32 val);
static int ov9650_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val);
static int ov9650_set_red_balance(struct gspca_dev *gspca_dev, __s32 val);
static int ov9650_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val);
static int ov9650_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val);
static int ov9650_get_hflip(struct gspca_dev *gspca_dev, __s32 *val);
static int ov9650_set_hflip(struct gspca_dev *gspca_dev, __s32 val);
static int ov9650_get_vflip(struct gspca_dev *gspca_dev, __s32 *val);
static int ov9650_set_vflip(struct gspca_dev *gspca_dev, __s32 val);
static int ov9650_get_auto_white_balance(struct gspca_dev *gspca_dev,
__s32 *val);
static int ov9650_set_auto_white_balance(struct gspca_dev *gspca_dev,
__s32 val);
static int ov9650_get_auto_gain(struct gspca_dev *gspca_dev, __s32 *val);
static int ov9650_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val);
static int ov9650_get_auto_exposure(struct gspca_dev *gspca_dev, __s32 *val);
static int ov9650_set_auto_exposure(struct gspca_dev *gspca_dev, __s32 val);
/* 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 const struct ctrl ov9650_ctrls[] = {
#define EXPOSURE_IDX 0
{
{
.id = V4L2_CID_EXPOSURE,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "exposure",
.minimum = 0x00,
.maximum = 0x1ff,
.step = 0x4,
.default_value = EXPOSURE_DEFAULT,
.flags = V4L2_CTRL_FLAG_SLIDER
},
.set = ov9650_set_exposure,
.get = ov9650_get_exposure
},
#define GAIN_IDX 1
{
{
.id = V4L2_CID_GAIN,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "gain",
.minimum = 0x00,
.maximum = 0x3ff,
.step = 0x1,
.default_value = GAIN_DEFAULT,
.flags = V4L2_CTRL_FLAG_SLIDER
},
.set = ov9650_set_gain,
.get = ov9650_get_gain
},
#define RED_BALANCE_IDX 2
{
{
.id = V4L2_CID_RED_BALANCE,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "red balance",
.minimum = 0x00,
.maximum = 0xff,
.step = 0x1,
.default_value = RED_GAIN_DEFAULT,
.flags = V4L2_CTRL_FLAG_SLIDER
},
.set = ov9650_set_red_balance,
.get = ov9650_get_red_balance
},
#define BLUE_BALANCE_IDX 3
{
{
.id = V4L2_CID_BLUE_BALANCE,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "blue balance",
.minimum = 0x00,
.maximum = 0xff,
.step = 0x1,
.default_value = BLUE_GAIN_DEFAULT,
.flags = V4L2_CTRL_FLAG_SLIDER
},
.set = ov9650_set_blue_balance,
.get = ov9650_get_blue_balance
},
#define HFLIP_IDX 4
{
{
.id = V4L2_CID_HFLIP,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.name = "horizontal flip",
.minimum = 0,
.maximum = 1,
.step = 1,
.default_value = 0
},
.set = ov9650_set_hflip,
.get = ov9650_get_hflip
},
#define VFLIP_IDX 5
{
{
.id = V4L2_CID_VFLIP,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.name = "vertical flip",
.minimum = 0,
.maximum = 1,
.step = 1,
.default_value = 0
},
.set = ov9650_set_vflip,
.get = ov9650_get_vflip
},
#define AUTO_WHITE_BALANCE_IDX 6
{
{
.id = V4L2_CID_AUTO_WHITE_BALANCE,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.name = "auto white balance",
.minimum = 0,
.maximum = 1,
.step = 1,
.default_value = 1
},
.set = ov9650_set_auto_white_balance,
.get = ov9650_get_auto_white_balance
},
#define AUTO_GAIN_CTRL_IDX 7
{
{
.id = V4L2_CID_AUTOGAIN,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.name = "auto gain control",
.minimum = 0,
.maximum = 1,
.step = 1,
.default_value = 1
},
.set = ov9650_set_auto_gain,
.get = ov9650_get_auto_gain
},
#define AUTO_EXPOSURE_IDX 8
{
{
.id = V4L2_CID_EXPOSURE_AUTO,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.name = "auto exposure",
.minimum = 0,
.maximum = 1,
.step = 1,
.default_value = 1
},
.set = ov9650_set_auto_exposure,
.get = ov9650_get_auto_exposure
}
};
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 void ov9650_dump_registers(struct sd *sd);
int ov9650_probe(struct sd *sd)
{
int err = 0;
u8 prod_id = 0, ver_id = 0, i;
s32 *sensor_settings;
if (force_sensor) {
if (force_sensor == OV9650_SENSOR) {
info("Forcing an %s sensor", ov9650.name);
goto sensor_found;
}
/* If we want to force another sensor,
don't try to probe this one */
return -ENODEV;
}
info("Probing for an ov9650 sensor");
/* Run the pre-init before probing the sensor */
for (i = 0; i < ARRAY_SIZE(preinit_ov9650) && !err; i++) {
u8 data = preinit_ov9650[i][2];
if (preinit_ov9650[i][0] == SENSOR)
err = m5602_write_sensor(sd,
preinit_ov9650[i][1], &data, 1);
else
err = m5602_write_bridge(sd,
preinit_ov9650[i][1], data);
}
if (err < 0)
return err;
if (m5602_read_sensor(sd, OV9650_PID, &prod_id, 1))
return -ENODEV;
if (m5602_read_sensor(sd, OV9650_VER, &ver_id, 1))
return -ENODEV;
if ((prod_id == 0x96) && (ver_id == 0x52)) {
info("Detected an ov9650 sensor");
goto sensor_found;
}
return -ENODEV;
sensor_found:
sensor_settings = kmalloc(
ARRAY_SIZE(ov9650_ctrls) * sizeof(s32), GFP_KERNEL);
if (!sensor_settings)
return -ENOMEM;
sd->gspca_dev.cam.cam_mode = ov9650_modes;
sd->gspca_dev.cam.nmodes = ARRAY_SIZE(ov9650_modes);
sd->desc->ctrls = ov9650_ctrls;
sd->desc->nctrls = ARRAY_SIZE(ov9650_ctrls);
for (i = 0; i < ARRAY_SIZE(ov9650_ctrls); i++)
sensor_settings[i] = ov9650_ctrls[i].qctrl.default_value;
sd->sensor_priv = sensor_settings;
return 0;
}
int ov9650_init(struct sd *sd)
{
int i, err = 0;
u8 data;
s32 *sensor_settings = sd->sensor_priv;
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);
}
err = ov9650_set_exposure(&sd->gspca_dev,
sensor_settings[EXPOSURE_IDX]);
if (err < 0)
return err;
err = ov9650_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]);
if (err < 0)
return err;
err = ov9650_set_red_balance(&sd->gspca_dev,
sensor_settings[RED_BALANCE_IDX]);
if (err < 0)
return err;
err = ov9650_set_blue_balance(&sd->gspca_dev,
sensor_settings[BLUE_BALANCE_IDX]);
if (err < 0)
return err;
err = ov9650_set_hflip(&sd->gspca_dev, sensor_settings[HFLIP_IDX]);
if (err < 0)
return err;
err = ov9650_set_vflip(&sd->gspca_dev, sensor_settings[VFLIP_IDX]);
if (err < 0)
return err;
err = ov9650_set_auto_exposure(&sd->gspca_dev,
sensor_settings[AUTO_EXPOSURE_IDX]);
if (err < 0)
return err;
err = ov9650_set_auto_white_balance(&sd->gspca_dev,
sensor_settings[AUTO_WHITE_BALANCE_IDX]);
if (err < 0)
return err;
err = ov9650_set_auto_gain(&sd->gspca_dev,
sensor_settings[AUTO_GAIN_CTRL_IDX]);
return err;
}
int ov9650_start(struct sd *sd)
{
u8 data;
int i, err = 0;
struct cam *cam = &sd->gspca_dev.cam;
s32 *sensor_settings = sd->sensor_priv;
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;
if ((!dmi_check_system(ov9650_flip_dmi_table) &&
sensor_settings[VFLIP_IDX]) ||
(dmi_check_system(ov9650_flip_dmi_table) &&
!sensor_settings[VFLIP_IDX]))
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) {
u8 data = res_init_ov9650[i][2];
err = m5602_write_sensor(sd,
res_init_ov9650[i][1], &data, 1);
}
}
if (err < 0)
return err;
err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA,
((ver_offs >> 8) & 0xff));
if (err < 0)
return err;
err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (ver_offs & 0xff));
if (err < 0)
return err;
err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, 0);
if (err < 0)
return err;
err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (height >> 8) & 0xff);
if (err < 0)
return err;
err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (height & 0xff));
if (err < 0)
return err;
for (i = 0; i < 2 && !err; i++)
err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, 0);
if (err < 0)
return err;
err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0);
if (err < 0)
return err;
err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 2);
if (err < 0)
return err;
err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA,
(hor_offs >> 8) & 0xff);
if (err < 0)
return err;
err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, hor_offs & 0xff);
if (err < 0)
return err;
err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA,
((width + hor_offs) >> 8) & 0xff);
if (err < 0)
return err;
err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA,
((width + hor_offs) & 0xff));
if (err < 0)
return err;
err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0);
if (err < 0)
return err;
switch (width) {
case 640:
PDEBUG(D_V4L2, "Configuring camera for VGA mode");
data = OV9650_VGA_SELECT | OV9650_RGB_SELECT |
OV9650_RAW_RGB_SELECT;
err = m5602_write_sensor(sd, OV9650_COM7, &data, 1);
break;
case 352:
PDEBUG(D_V4L2, "Configuring camera for CIF mode");
data = OV9650_CIF_SELECT | OV9650_RGB_SELECT |
OV9650_RAW_RGB_SELECT;
err = m5602_write_sensor(sd, OV9650_COM7, &data, 1);
break;
case 320:
PDEBUG(D_V4L2, "Configuring camera for QVGA mode");
data = OV9650_QVGA_SELECT | OV9650_RGB_SELECT |
OV9650_RAW_RGB_SELECT;
err = m5602_write_sensor(sd, OV9650_COM7, &data, 1);
break;
case 176:
PDEBUG(D_V4L2, "Configuring camera for QCIF mode");
data = OV9650_QCIF_SELECT | OV9650_RGB_SELECT |
OV9650_RAW_RGB_SELECT;
err = m5602_write_sensor(sd, OV9650_COM7, &data, 1);
break;
}
return err;
}
int ov9650_stop(struct sd *sd)
{
u8 data = OV9650_SOFT_SLEEP | OV9650_OUTPUT_DRIVE_2X;
return m5602_write_sensor(sd, OV9650_COM2, &data, 1);
}
void ov9650_disconnect(struct sd *sd)
{
ov9650_stop(sd);
sd->sensor = NULL;
kfree(sd->sensor_priv);
}
static int ov9650_get_exposure(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
*val = sensor_settings[EXPOSURE_IDX];
PDEBUG(D_V4L2, "Read exposure %d", *val);
return 0;
}
static int ov9650_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
u8 i2c_data;
int err;
PDEBUG(D_V4L2, "Set exposure to %d", val);
sensor_settings[EXPOSURE_IDX] = 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_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
*val = sensor_settings[GAIN_IDX];
PDEBUG(D_V4L2, "Read gain %d", *val);
return 0;
}
static int ov9650_set_gain(struct gspca_dev *gspca_dev, __s32 val)
{
int err;
u8 i2c_data;
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
PDEBUG(D_V4L2, "Setting gain to %d", val);
sensor_settings[GAIN_IDX] = 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_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
*val = sensor_settings[RED_BALANCE_IDX];
PDEBUG(D_V4L2, "Read red gain %d", *val);
return 0;
}
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;
s32 *sensor_settings = sd->sensor_priv;
PDEBUG(D_V4L2, "Set red gain to %d", val);
sensor_settings[RED_BALANCE_IDX] = val;
i2c_data = val & 0xff;
err = m5602_write_sensor(sd, OV9650_RED, &i2c_data, 1);
return err;
}
static int ov9650_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
*val = sensor_settings[BLUE_BALANCE_IDX];
PDEBUG(D_V4L2, "Read blue gain %d", *val);
return 0;
}
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;
s32 *sensor_settings = sd->sensor_priv;
PDEBUG(D_V4L2, "Set blue gain to %d", val);
sensor_settings[BLUE_BALANCE_IDX] = val;
i2c_data = val & 0xff;
err = m5602_write_sensor(sd, OV9650_BLUE, &i2c_data, 1);
return err;
}
static int ov9650_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
*val = sensor_settings[HFLIP_IDX];
PDEBUG(D_V4L2, "Read horizontal flip %d", *val);
return 0;
}
static int ov9650_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
{
int err;
u8 i2c_data;
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
PDEBUG(D_V4L2, "Set horizontal flip to %d", val);
sensor_settings[HFLIP_IDX] = val;
if (!dmi_check_system(ov9650_flip_dmi_table))
i2c_data = ((val & 0x01) << 5) |
(sensor_settings[VFLIP_IDX] << 4);
else
i2c_data = ((val & 0x01) << 5) |
(!sensor_settings[VFLIP_IDX] << 4);
err = m5602_write_sensor(sd, OV9650_MVFP, &i2c_data, 1);
return err;
}
static int ov9650_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
*val = sensor_settings[VFLIP_IDX];
PDEBUG(D_V4L2, "Read vertical flip %d", *val);
return 0;
}
static int ov9650_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
{
int err;
u8 i2c_data;
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
PDEBUG(D_V4L2, "Set vertical flip to %d", val);
sensor_settings[VFLIP_IDX] = val;
if (dmi_check_system(ov9650_flip_dmi_table))
val = !val;
i2c_data = ((val & 0x01) << 4) | (sensor_settings[VFLIP_IDX] << 5);
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_get_auto_exposure(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
*val = sensor_settings[AUTO_EXPOSURE_IDX];
PDEBUG(D_V4L2, "Read auto exposure control %d", *val);
return 0;
}
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;
s32 *sensor_settings = sd->sensor_priv;
PDEBUG(D_V4L2, "Set auto exposure control to %d", val);
sensor_settings[AUTO_EXPOSURE_IDX] = val;
err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1);
if (err < 0)
return err;
i2c_data = ((i2c_data & 0xfe) | ((val & 0x01) << 0));
return m5602_write_sensor(sd, OV9650_COM8, &i2c_data, 1);
}
static int ov9650_get_auto_white_balance(struct gspca_dev *gspca_dev,
__s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
*val = sensor_settings[AUTO_WHITE_BALANCE_IDX];
return 0;
}
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;
s32 *sensor_settings = sd->sensor_priv;
PDEBUG(D_V4L2, "Set auto white balance to %d", val);
sensor_settings[AUTO_WHITE_BALANCE_IDX] = 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_get_auto_gain(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
*val = sensor_settings[AUTO_GAIN_CTRL_IDX];
PDEBUG(D_V4L2, "Read auto gain control %d", *val);
return 0;
}
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;
s32 *sensor_settings = sd->sensor_priv;
PDEBUG(D_V4L2, "Set auto gain control to %d", val);
sensor_settings[AUTO_GAIN_CTRL_IDX] = 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 void ov9650_dump_registers(struct sd *sd)
{
int address;
info("Dumping the ov9650 register state");
for (address = 0; address < 0xa9; address++) {
u8 value;
m5602_read_sensor(sd, address, &value, 1);
info("register 0x%x contains 0x%x",
address, value);
}
info("ov9650 register state dump complete");
info("Probing for which registers that are read/write");
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])
info("register 0x%x is writeable", address);
else
info("register 0x%x is read only", address);
/* Restore original value */
m5602_write_sensor(sd, address, &old_value, 1);
}
}

View File

@@ -0,0 +1,311 @@
/*
* Driver for the ov9650 sensor
*
* Copyright (C) 2008 Erik Andrén
* Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
* Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
*
* Portions of code to USB interface and ALi driver software,
* Copyright (c) 2006 Willem Duinker
* v4l2 interface modeled after the V4L2 driver
* for SN9C10x PC Camera Controllers
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, version 2.
*
*/
#ifndef M5602_OV9650_H_
#define M5602_OV9650_H_
#include <linux/dmi.h>
#include "m5602_sensor.h"
/*****************************************************************************/
#define OV9650_GAIN 0x00
#define OV9650_BLUE 0x01
#define OV9650_RED 0x02
#define OV9650_VREF 0x03
#define OV9650_COM1 0x04
#define OV9650_BAVE 0x05
#define OV9650_GEAVE 0x06
#define OV9650_RSVD7 0x07
#define OV9650_COM2 0x09
#define OV9650_PID 0x0a
#define OV9650_VER 0x0b
#define OV9650_COM3 0x0c
#define OV9650_COM4 0x0d
#define OV9650_COM5 0x0e
#define OV9650_COM6 0x0f
#define OV9650_AECH 0x10
#define OV9650_CLKRC 0x11
#define OV9650_COM7 0x12
#define OV9650_COM8 0x13
#define OV9650_COM9 0x14
#define OV9650_COM10 0x15
#define OV9650_RSVD16 0x16
#define OV9650_HSTART 0x17
#define OV9650_HSTOP 0x18
#define OV9650_VSTRT 0x19
#define OV9650_VSTOP 0x1a
#define OV9650_PSHFT 0x1b
#define OV9650_MVFP 0x1e
#define OV9650_AEW 0x24
#define OV9650_AEB 0x25
#define OV9650_VPT 0x26
#define OV9650_BBIAS 0x27
#define OV9650_GbBIAS 0x28
#define OV9650_Gr_COM 0x29
#define OV9650_RBIAS 0x2c
#define OV9650_HREF 0x32
#define OV9650_CHLF 0x33
#define OV9650_ARBLM 0x34
#define OV9650_RSVD35 0x35
#define OV9650_RSVD36 0x36
#define OV9650_ADC 0x37
#define OV9650_ACOM38 0x38
#define OV9650_OFON 0x39
#define OV9650_TSLB 0x3a
#define OV9650_COM12 0x3c
#define OV9650_COM13 0x3d
#define OV9650_COM15 0x40
#define OV9650_COM16 0x41
#define OV9650_LCC1 0x62
#define OV9650_LCC2 0x63
#define OV9650_LCC3 0x64
#define OV9650_LCC4 0x65
#define OV9650_LCC5 0x66
#define OV9650_HV 0x69
#define OV9650_DBLV 0x6b
#define OV9650_COM21 0x8b
#define OV9650_COM22 0x8c
#define OV9650_COM24 0x8e
#define OV9650_DBLC1 0x8f
#define OV9650_RSVD94 0x94
#define OV9650_RSVD95 0x95
#define OV9650_RSVD96 0x96
#define OV9650_LCCFB 0x9d
#define OV9650_LCCFR 0x9e
#define OV9650_AECHM 0xa1
#define OV9650_COM26 0xa5
#define OV9650_ACOMA8 0xa8
#define OV9650_ACOMA9 0xa9
#define OV9650_REGISTER_RESET (1 << 7)
#define OV9650_VGA_SELECT (1 << 6)
#define OV9650_CIF_SELECT (1 << 5)
#define OV9650_QVGA_SELECT (1 << 4)
#define OV9650_QCIF_SELECT (1 << 3)
#define OV9650_RGB_SELECT (1 << 2)
#define OV9650_RAW_RGB_SELECT (1 << 0)
#define OV9650_FAST_AGC_AEC (1 << 7)
#define OV9650_AEC_UNLIM_STEP_SIZE (1 << 6)
#define OV9650_BANDING (1 << 5)
#define OV9650_AGC_EN (1 << 2)
#define OV9650_AWB_EN (1 << 1)
#define OV9650_AEC_EN (1 << 0)
#define OV9650_VARIOPIXEL (1 << 2)
#define OV9650_SYSTEM_CLK_SEL (1 << 7)
#define OV9650_SLAM_MODE (1 << 4)
#define OV9650_QVGA_VARIOPIXEL (1 << 7)
#define OV9650_VFLIP (1 << 4)
#define OV9650_HFLIP (1 << 5)
#define OV9650_SOFT_SLEEP (1 << 4)
#define OV9650_OUTPUT_DRIVE_2X (1 << 0)
#define OV9650_DENOISE_ENABLE (1 << 5)
#define OV9650_WHITE_PIXEL_ENABLE (1 << 1)
#define OV9650_WHITE_PIXEL_OPTION (1 << 0)
#define OV9650_LEFT_OFFSET 0x62
#define GAIN_DEFAULT 0x14
#define RED_GAIN_DEFAULT 0x70
#define BLUE_GAIN_DEFAULT 0x20
#define EXPOSURE_DEFAULT 0x1ff
/*****************************************************************************/
/* Kernel module parameters */
extern int force_sensor;
extern int dump_sensor;
int ov9650_probe(struct sd *sd);
int ov9650_init(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,
.start = ov9650_start,
.stop = ov9650_stop,
.disconnect = ov9650_disconnect,
};
static const unsigned char preinit_ov9650[][3] =
{
/* [INITCAM] */
{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02},
{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0},
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
{BRIDGE, M5602_XB_SENSOR_CTRL, 0x00},
{BRIDGE, M5602_XB_SENSOR_TYPE, 0x08},
{BRIDGE, M5602_XB_GPIO_DIR, 0x05},
{BRIDGE, M5602_XB_GPIO_DAT, 0x04},
{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
{BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00},
{BRIDGE, M5602_XB_GPIO_DAT, 0x00},
{BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a},
/* Reset chip */
{SENSOR, OV9650_COM7, OV9650_REGISTER_RESET},
/* Enable double clock */
{SENSOR, OV9650_CLKRC, 0x80},
/* Do something out of spec with the power */
{SENSOR, OV9650_OFON, 0x40}
};
static const unsigned char init_ov9650[][3] =
{
/* [INITCAM] */
{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02},
{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0},
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
{BRIDGE, M5602_XB_SENSOR_CTRL, 0x00},
{BRIDGE, M5602_XB_SENSOR_TYPE, 0x08},
{BRIDGE, M5602_XB_GPIO_DIR, 0x05},
{BRIDGE, M5602_XB_GPIO_DAT, 0x04},
{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
{BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00},
{BRIDGE, M5602_XB_GPIO_DAT, 0x00},
{BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a},
/* Reset chip */
{SENSOR, OV9650_COM7, OV9650_REGISTER_RESET},
/* One extra reset is needed in order to make the sensor behave
properly when resuming from ram, could be a timing issue */
{SENSOR, OV9650_COM7, OV9650_REGISTER_RESET},
/* Enable double clock */
{SENSOR, OV9650_CLKRC, 0x80},
/* Do something out of spec with the power */
{SENSOR, OV9650_OFON, 0x40},
/* Set fast AGC/AEC algorithm with unlimited step size */
{SENSOR, OV9650_COM8, OV9650_FAST_AGC_AEC |
OV9650_AEC_UNLIM_STEP_SIZE},
{SENSOR, OV9650_CHLF, 0x10},
{SENSOR, OV9650_ARBLM, 0xbf},
{SENSOR, OV9650_ACOM38, 0x81},
/* Turn off color matrix coefficient double option */
{SENSOR, OV9650_COM16, 0x00},
/* Enable color matrix for RGB/YUV, Delay Y channel,
set output Y/UV delay to 1 */
{SENSOR, OV9650_COM13, 0x19},
/* Enable digital BLC, Set output mode to U Y V Y */
{SENSOR, OV9650_TSLB, 0x0c},
/* Limit the AGC/AEC stable upper region */
{SENSOR, OV9650_COM24, 0x00},
/* Enable HREF and some out of spec things */
{SENSOR, OV9650_COM12, 0x73},
/* Set all DBLC offset signs to positive and
do some out of spec stuff */
{SENSOR, OV9650_DBLC1, 0xdf},
{SENSOR, OV9650_COM21, 0x06},
{SENSOR, OV9650_RSVD35, 0x91},
/* Necessary, no camera stream without it */
{SENSOR, OV9650_RSVD16, 0x06},
{SENSOR, OV9650_RSVD94, 0x99},
{SENSOR, OV9650_RSVD95, 0x99},
{SENSOR, OV9650_RSVD96, 0x04},
/* Enable full range output */
{SENSOR, OV9650_COM15, 0x0},
/* Enable HREF at optical black, enable ADBLC bias,
enable ADBLC, reset timings at format change */
{SENSOR, OV9650_COM6, 0x4b},
/* Subtract 32 from the B channel bias */
{SENSOR, OV9650_BBIAS, 0xa0},
/* Subtract 32 from the Gb channel bias */
{SENSOR, OV9650_GbBIAS, 0xa0},
/* Do not bypass the analog BLC and to some out of spec stuff */
{SENSOR, OV9650_Gr_COM, 0x00},
/* Subtract 32 from the R channel bias */
{SENSOR, OV9650_RBIAS, 0xa0},
/* Subtract 32 from the R channel bias */
{SENSOR, OV9650_RBIAS, 0x0},
{SENSOR, OV9650_COM26, 0x80},
{SENSOR, OV9650_ACOMA9, 0x98},
/* Set the AGC/AEC stable region upper limit */
{SENSOR, OV9650_AEW, 0x68},
/* Set the AGC/AEC stable region lower limit */
{SENSOR, OV9650_AEB, 0x5c},
/* Set the high and low limit nibbles to 3 */
{SENSOR, OV9650_VPT, 0xc3},
/* Set the Automatic Gain Ceiling (AGC) to 128x,
drop VSYNC at frame drop,
limit exposure timing,
drop frame when the AEC step is larger than the exposure gap */
{SENSOR, OV9650_COM9, 0x6e},
/* Set VSYNC negative, Set RESET to SLHS (slave mode horizontal sync)
and set PWDN to SLVS (slave mode vertical sync) */
{SENSOR, OV9650_COM10, 0x42},
/* Set horizontal column start high to default value */
{SENSOR, OV9650_HSTART, 0x1a}, /* 210 */
/* Set horizontal column end */
{SENSOR, OV9650_HSTOP, 0xbf}, /* 1534 */
/* Complementing register to the two writes above */
{SENSOR, OV9650_HREF, 0xb2},
/* Set vertical row start high bits */
{SENSOR, OV9650_VSTRT, 0x02},
/* Set vertical row end low bits */
{SENSOR, OV9650_VSTOP, 0x7e},
/* Set complementing vertical frame control */
{SENSOR, OV9650_VREF, 0x10},
{SENSOR, OV9650_ADC, 0x04},
{SENSOR, OV9650_HV, 0x40},
/* Enable denoise, and white-pixel erase */
{SENSOR, OV9650_COM22, OV9650_DENOISE_ENABLE |
OV9650_WHITE_PIXEL_ENABLE |
OV9650_WHITE_PIXEL_OPTION},
/* Enable VARIOPIXEL */
{SENSOR, OV9650_COM3, OV9650_VARIOPIXEL},
{SENSOR, OV9650_COM4, OV9650_QVGA_VARIOPIXEL},
/* Put the sensor in soft sleep mode */
{SENSOR, OV9650_COM2, OV9650_SOFT_SLEEP | OV9650_OUTPUT_DRIVE_2X},
};
static const unsigned char res_init_ov9650[][3] =
{
{SENSOR, OV9650_COM2, OV9650_OUTPUT_DRIVE_2X},
{BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x82},
{BRIDGE, M5602_XB_LINE_OF_FRAME_L, 0x00},
{BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82},
{BRIDGE, M5602_XB_PIX_OF_LINE_L, 0x00},
{BRIDGE, M5602_XB_SIG_INI, 0x01}
};
#endif

View File

@@ -0,0 +1,762 @@
/*
* Driver for the po1030 sensor
*
* Copyright (c) 2008 Erik Andrén
* Copyright (c) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
* Copyright (c) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
*
* Portions of code to USB interface and ALi driver software,
* Copyright (c) 2006 Willem Duinker
* v4l2 interface modeled after the V4L2 driver
* for SN9C10x PC Camera Controllers
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, version 2.
*
*/
#include "m5602_po1030.h"
static int po1030_get_exposure(struct gspca_dev *gspca_dev, __s32 *val);
static int po1030_set_exposure(struct gspca_dev *gspca_dev, __s32 val);
static int po1030_get_gain(struct gspca_dev *gspca_dev, __s32 *val);
static int po1030_set_gain(struct gspca_dev *gspca_dev, __s32 val);
static int po1030_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val);
static int po1030_set_red_balance(struct gspca_dev *gspca_dev, __s32 val);
static int po1030_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val);
static int po1030_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val);
static int po1030_get_green_balance(struct gspca_dev *gspca_dev, __s32 *val);
static int po1030_set_green_balance(struct gspca_dev *gspca_dev, __s32 val);
static int po1030_get_hflip(struct gspca_dev *gspca_dev, __s32 *val);
static int po1030_set_hflip(struct gspca_dev *gspca_dev, __s32 val);
static int po1030_get_vflip(struct gspca_dev *gspca_dev, __s32 *val);
static int po1030_set_vflip(struct gspca_dev *gspca_dev, __s32 val);
static int po1030_set_auto_white_balance(struct gspca_dev *gspca_dev,
__s32 val);
static int po1030_get_auto_white_balance(struct gspca_dev *gspca_dev,
__s32 *val);
static int po1030_set_auto_exposure(struct gspca_dev *gspca_dev,
__s32 val);
static int po1030_get_auto_exposure(struct gspca_dev *gspca_dev,
__s32 *val);
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 ctrl po1030_ctrls[] = {
#define GAIN_IDX 0
{
{
.id = V4L2_CID_GAIN,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "gain",
.minimum = 0x00,
.maximum = 0x4f,
.step = 0x1,
.default_value = PO1030_GLOBAL_GAIN_DEFAULT,
.flags = V4L2_CTRL_FLAG_SLIDER
},
.set = po1030_set_gain,
.get = po1030_get_gain
},
#define EXPOSURE_IDX 1
{
{
.id = V4L2_CID_EXPOSURE,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "exposure",
.minimum = 0x00,
.maximum = 0x02ff,
.step = 0x1,
.default_value = PO1030_EXPOSURE_DEFAULT,
.flags = V4L2_CTRL_FLAG_SLIDER
},
.set = po1030_set_exposure,
.get = po1030_get_exposure
},
#define RED_BALANCE_IDX 2
{
{
.id = V4L2_CID_RED_BALANCE,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "red balance",
.minimum = 0x00,
.maximum = 0xff,
.step = 0x1,
.default_value = PO1030_RED_GAIN_DEFAULT,
.flags = V4L2_CTRL_FLAG_SLIDER
},
.set = po1030_set_red_balance,
.get = po1030_get_red_balance
},
#define BLUE_BALANCE_IDX 3
{
{
.id = V4L2_CID_BLUE_BALANCE,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "blue balance",
.minimum = 0x00,
.maximum = 0xff,
.step = 0x1,
.default_value = PO1030_BLUE_GAIN_DEFAULT,
.flags = V4L2_CTRL_FLAG_SLIDER
},
.set = po1030_set_blue_balance,
.get = po1030_get_blue_balance
},
#define HFLIP_IDX 4
{
{
.id = V4L2_CID_HFLIP,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.name = "horizontal flip",
.minimum = 0,
.maximum = 1,
.step = 1,
.default_value = 0,
},
.set = po1030_set_hflip,
.get = po1030_get_hflip
},
#define VFLIP_IDX 5
{
{
.id = V4L2_CID_VFLIP,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.name = "vertical flip",
.minimum = 0,
.maximum = 1,
.step = 1,
.default_value = 0,
},
.set = po1030_set_vflip,
.get = po1030_get_vflip
},
#define AUTO_WHITE_BALANCE_IDX 6
{
{
.id = V4L2_CID_AUTO_WHITE_BALANCE,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.name = "auto white balance",
.minimum = 0,
.maximum = 1,
.step = 1,
.default_value = 0,
},
.set = po1030_set_auto_white_balance,
.get = po1030_get_auto_white_balance
},
#define AUTO_EXPOSURE_IDX 7
{
{
.id = V4L2_CID_EXPOSURE_AUTO,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.name = "auto exposure",
.minimum = 0,
.maximum = 1,
.step = 1,
.default_value = 0,
},
.set = po1030_set_auto_exposure,
.get = po1030_get_auto_exposure
},
#define GREEN_BALANCE_IDX 8
{
{
.id = M5602_V4L2_CID_GREEN_BALANCE,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "green balance",
.minimum = 0x00,
.maximum = 0xff,
.step = 0x1,
.default_value = PO1030_GREEN_GAIN_DEFAULT,
.flags = V4L2_CTRL_FLAG_SLIDER
},
.set = po1030_set_green_balance,
.get = po1030_get_green_balance
},
};
static void po1030_dump_registers(struct sd *sd);
int po1030_probe(struct sd *sd)
{
u8 dev_id_h = 0, i;
s32 *sensor_settings;
if (force_sensor) {
if (force_sensor == PO1030_SENSOR) {
info("Forcing a %s sensor", po1030.name);
goto sensor_found;
}
/* If we want to force another sensor, don't try to probe this
* one */
return -ENODEV;
}
info("Probing for a po1030 sensor");
/* Run the pre-init to actually probe the unit */
for (i = 0; i < ARRAY_SIZE(preinit_po1030); i++) {
u8 data = preinit_po1030[i][2];
if (preinit_po1030[i][0] == SENSOR)
m5602_write_sensor(sd,
preinit_po1030[i][1], &data, 1);
else
m5602_write_bridge(sd, preinit_po1030[i][1], data);
}
if (m5602_read_sensor(sd, PO1030_DEVID_H, &dev_id_h, 1))
return -ENODEV;
if (dev_id_h == 0x30) {
info("Detected a po1030 sensor");
goto sensor_found;
}
return -ENODEV;
sensor_found:
sensor_settings = kmalloc(
ARRAY_SIZE(po1030_ctrls) * sizeof(s32), GFP_KERNEL);
if (!sensor_settings)
return -ENOMEM;
sd->gspca_dev.cam.cam_mode = po1030_modes;
sd->gspca_dev.cam.nmodes = ARRAY_SIZE(po1030_modes);
sd->desc->ctrls = po1030_ctrls;
sd->desc->nctrls = ARRAY_SIZE(po1030_ctrls);
for (i = 0; i < ARRAY_SIZE(po1030_ctrls); i++)
sensor_settings[i] = po1030_ctrls[i].qctrl.default_value;
sd->sensor_priv = sensor_settings;
return 0;
}
int po1030_init(struct sd *sd)
{
s32 *sensor_settings = sd->sensor_priv;
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:
info("Invalid stream command, exiting init");
return -EINVAL;
}
}
if (err < 0)
return err;
if (dump_sensor)
po1030_dump_registers(sd);
err = po1030_set_exposure(&sd->gspca_dev,
sensor_settings[EXPOSURE_IDX]);
if (err < 0)
return err;
err = po1030_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]);
if (err < 0)
return err;
err = po1030_set_hflip(&sd->gspca_dev, sensor_settings[HFLIP_IDX]);
if (err < 0)
return err;
err = po1030_set_vflip(&sd->gspca_dev, sensor_settings[VFLIP_IDX]);
if (err < 0)
return err;
err = po1030_set_red_balance(&sd->gspca_dev,
sensor_settings[RED_BALANCE_IDX]);
if (err < 0)
return err;
err = po1030_set_blue_balance(&sd->gspca_dev,
sensor_settings[BLUE_BALANCE_IDX]);
if (err < 0)
return err;
err = po1030_set_green_balance(&sd->gspca_dev,
sensor_settings[GREEN_BALANCE_IDX]);
if (err < 0)
return err;
err = po1030_set_auto_white_balance(&sd->gspca_dev,
sensor_settings[AUTO_WHITE_BALANCE_IDX]);
if (err < 0)
return err;
err = po1030_set_auto_exposure(&sd->gspca_dev,
sensor_settings[AUTO_EXPOSURE_IDX]);
return err;
}
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_get_exposure(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
*val = sensor_settings[EXPOSURE_IDX];
PDEBUG(D_V4L2, "Exposure read as %d", *val);
return 0;
}
static int po1030_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
u8 i2c_data;
int err;
sensor_settings[EXPOSURE_IDX] = val;
PDEBUG(D_V4L2, "Set exposure to %d", val & 0xffff);
i2c_data = ((val & 0xff00) >> 8);
PDEBUG(D_V4L2, "Set exposure to high byte to 0x%x",
i2c_data);
err = m5602_write_sensor(sd, PO1030_INTEGLINES_H,
&i2c_data, 1);
if (err < 0)
return err;
i2c_data = (val & 0xff);
PDEBUG(D_V4L2, "Set exposure to low byte to 0x%x",
i2c_data);
err = m5602_write_sensor(sd, PO1030_INTEGLINES_M,
&i2c_data, 1);
return err;
}
static int po1030_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
*val = sensor_settings[GAIN_IDX];
PDEBUG(D_V4L2, "Read global gain %d", *val);
return 0;
}
static int po1030_set_gain(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
u8 i2c_data;
int err;
sensor_settings[GAIN_IDX] = val;
i2c_data = val & 0xff;
PDEBUG(D_V4L2, "Set global gain to %d", i2c_data);
err = m5602_write_sensor(sd, PO1030_GLOBALGAIN,
&i2c_data, 1);
return err;
}
static int po1030_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
*val = sensor_settings[HFLIP_IDX];
PDEBUG(D_V4L2, "Read hflip %d", *val);
return 0;
}
static int po1030_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
u8 i2c_data;
int err;
sensor_settings[HFLIP_IDX] = val;
PDEBUG(D_V4L2, "Set hflip %d", val);
err = m5602_read_sensor(sd, PO1030_CONTROL2, &i2c_data, 1);
if (err < 0)
return err;
i2c_data = (0x7f & i2c_data) | ((val & 0x01) << 7);
err = m5602_write_sensor(sd, PO1030_CONTROL2,
&i2c_data, 1);
return err;
}
static int po1030_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
*val = sensor_settings[VFLIP_IDX];
PDEBUG(D_V4L2, "Read vflip %d", *val);
return 0;
}
static int po1030_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
u8 i2c_data;
int err;
sensor_settings[VFLIP_IDX] = val;
PDEBUG(D_V4L2, "Set vflip %d", val);
err = m5602_read_sensor(sd, PO1030_CONTROL2, &i2c_data, 1);
if (err < 0)
return err;
i2c_data = (i2c_data & 0xbf) | ((val & 0x01) << 6);
err = m5602_write_sensor(sd, PO1030_CONTROL2,
&i2c_data, 1);
return err;
}
static int po1030_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
*val = sensor_settings[RED_BALANCE_IDX];
PDEBUG(D_V4L2, "Read red gain %d", *val);
return 0;
}
static int po1030_set_red_balance(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
u8 i2c_data;
int err;
sensor_settings[RED_BALANCE_IDX] = val;
i2c_data = val & 0xff;
PDEBUG(D_V4L2, "Set red gain to %d", i2c_data);
err = m5602_write_sensor(sd, PO1030_RED_GAIN,
&i2c_data, 1);
return err;
}
static int po1030_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
*val = sensor_settings[BLUE_BALANCE_IDX];
PDEBUG(D_V4L2, "Read blue gain %d", *val);
return 0;
}
static int po1030_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
u8 i2c_data;
int err;
sensor_settings[BLUE_BALANCE_IDX] = val;
i2c_data = val & 0xff;
PDEBUG(D_V4L2, "Set blue gain to %d", i2c_data);
err = m5602_write_sensor(sd, PO1030_BLUE_GAIN,
&i2c_data, 1);
return err;
}
static int po1030_get_green_balance(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
*val = sensor_settings[GREEN_BALANCE_IDX];
PDEBUG(D_V4L2, "Read green gain %d", *val);
return 0;
}
static int po1030_set_green_balance(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
u8 i2c_data;
int err;
sensor_settings[GREEN_BALANCE_IDX] = val;
i2c_data = val & 0xff;
PDEBUG(D_V4L2, "Set green gain to %d", i2c_data);
err = m5602_write_sensor(sd, PO1030_GREEN_1_GAIN,
&i2c_data, 1);
if (err < 0)
return err;
return m5602_write_sensor(sd, PO1030_GREEN_2_GAIN,
&i2c_data, 1);
}
static int po1030_get_auto_white_balance(struct gspca_dev *gspca_dev,
__s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
*val = sensor_settings[AUTO_WHITE_BALANCE_IDX];
PDEBUG(D_V4L2, "Auto white balancing is %d", *val);
return 0;
}
static int po1030_set_auto_white_balance(struct gspca_dev *gspca_dev,
__s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
u8 i2c_data;
int err;
sensor_settings[AUTO_WHITE_BALANCE_IDX] = val;
err = m5602_read_sensor(sd, PO1030_AUTOCTRL1, &i2c_data, 1);
if (err < 0)
return err;
PDEBUG(D_V4L2, "Set auto white balance to %d", val);
i2c_data = (i2c_data & 0xfe) | (val & 0x01);
err = m5602_write_sensor(sd, PO1030_AUTOCTRL1, &i2c_data, 1);
return err;
}
static int po1030_get_auto_exposure(struct gspca_dev *gspca_dev,
__s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
*val = sensor_settings[AUTO_EXPOSURE_IDX];
PDEBUG(D_V4L2, "Auto exposure is %d", *val);
return 0;
}
static int po1030_set_auto_exposure(struct gspca_dev *gspca_dev,
__s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
u8 i2c_data;
int err;
sensor_settings[AUTO_EXPOSURE_IDX] = val;
err = m5602_read_sensor(sd, PO1030_AUTOCTRL1, &i2c_data, 1);
if (err < 0)
return err;
PDEBUG(D_V4L2, "Set auto exposure to %d", val);
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;
kfree(sd->sensor_priv);
}
static void po1030_dump_registers(struct sd *sd)
{
int address;
u8 value = 0;
info("Dumping the po1030 sensor core registers");
for (address = 0; address < 0x7f; address++) {
m5602_read_sensor(sd, address, &value, 1);
info("register 0x%x contains 0x%x",
address, value);
}
info("po1030 register state dump complete");
info("Probing for which registers that are read/write");
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])
info("register 0x%x is writeable", address);
else
info("register 0x%x is read only", address);
/* Restore original value */
m5602_write_sensor(sd, address, &old_value, 1);
}
}

View File

@@ -0,0 +1,275 @@
/*
* Driver for the po1030 sensor.
*
* Copyright (c) 2008 Erik Andrén
* Copyright (c) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
* Copyright (c) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
*
* Portions of code to USB interface and ALi driver software,
* Copyright (c) 2006 Willem Duinker
* v4l2 interface modeled after the V4L2 driver
* for SN9C10x PC Camera Controllers
*
* Register defines taken from Pascal Stangs Procyon Armlib
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, version 2.
*
*/
#ifndef M5602_PO1030_H_
#define M5602_PO1030_H_
#include "m5602_sensor.h"
/*****************************************************************************/
#define PO1030_DEVID_H 0x00
#define PO1030_DEVID_L 0x01
#define PO1030_FRAMEWIDTH_H 0x04
#define PO1030_FRAMEWIDTH_L 0x05
#define PO1030_FRAMEHEIGHT_H 0x06
#define PO1030_FRAMEHEIGHT_L 0x07
#define PO1030_WINDOWX_H 0x08
#define PO1030_WINDOWX_L 0x09
#define PO1030_WINDOWY_H 0x0a
#define PO1030_WINDOWY_L 0x0b
#define PO1030_WINDOWWIDTH_H 0x0c
#define PO1030_WINDOWWIDTH_L 0x0d
#define PO1030_WINDOWHEIGHT_H 0x0e
#define PO1030_WINDOWHEIGHT_L 0x0f
#define PO1030_GLOBALIBIAS 0x12
#define PO1030_PIXELIBIAS 0x13
#define PO1030_GLOBALGAIN 0x15
#define PO1030_RED_GAIN 0x16
#define PO1030_GREEN_1_GAIN 0x17
#define PO1030_BLUE_GAIN 0x18
#define PO1030_GREEN_2_GAIN 0x19
#define PO1030_INTEGLINES_H 0x1a
#define PO1030_INTEGLINES_M 0x1b
#define PO1030_INTEGLINES_L 0x1c
#define PO1030_CONTROL1 0x1d
#define PO1030_CONTROL2 0x1e
#define PO1030_CONTROL3 0x1f
#define PO1030_CONTROL4 0x20
#define PO1030_PERIOD50_H 0x23
#define PO1030_PERIOD50_L 0x24
#define PO1030_PERIOD60_H 0x25
#define PO1030_PERIOD60_L 0x26
#define PO1030_REGCLK167 0x27
#define PO1030_FLICKER_DELTA50 0x28
#define PO1030_FLICKERDELTA60 0x29
#define PO1030_ADCOFFSET 0x2c
/* Gamma Correction Coeffs */
#define PO1030_GC0 0x2d
#define PO1030_GC1 0x2e
#define PO1030_GC2 0x2f
#define PO1030_GC3 0x30
#define PO1030_GC4 0x31
#define PO1030_GC5 0x32
#define PO1030_GC6 0x33
#define PO1030_GC7 0x34
/* Color Transform Matrix */
#define PO1030_CT0 0x35
#define PO1030_CT1 0x36
#define PO1030_CT2 0x37
#define PO1030_CT3 0x38
#define PO1030_CT4 0x39
#define PO1030_CT5 0x3a
#define PO1030_CT6 0x3b
#define PO1030_CT7 0x3c
#define PO1030_CT8 0x3d
#define PO1030_AUTOCTRL1 0x3e
#define PO1030_AUTOCTRL2 0x3f
#define PO1030_YTARGET 0x40
#define PO1030_GLOBALGAINMIN 0x41
#define PO1030_GLOBALGAINMAX 0x42
#define PO1030_AWB_RED_TUNING 0x47
#define PO1030_AWB_BLUE_TUNING 0x48
/* Output format control */
#define PO1030_OUTFORMCTRL1 0x5a
#define PO1030_OUTFORMCTRL2 0x5b
#define PO1030_OUTFORMCTRL3 0x5c
#define PO1030_OUTFORMCTRL4 0x5d
#define PO1030_OUTFORMCTRL5 0x5e
#define PO1030_EDGE_ENH_OFF 0x5f
#define PO1030_EGA 0x60
#define PO1030_Cb_U_GAIN 0x63
#define PO1030_Cr_V_GAIN 0x64
#define PO1030_YCONTRAST 0x74
#define PO1030_YSATURATION 0x75
#define PO1030_HFLIP (1 << 7)
#define PO1030_VFLIP (1 << 6)
#define PO1030_HREF_ENABLE (1 << 6)
#define PO1030_RAW_RGB_BAYER 0x4
#define PO1030_FRAME_EQUAL (1 << 3)
#define PO1030_AUTO_SUBSAMPLING (1 << 4)
#define PO1030_WEIGHT_WIN_2X (1 << 3)
#define PO1030_SHUTTER_MODE (1 << 6)
#define PO1030_AUTO_SUBSAMPLING (1 << 4)
#define PO1030_FRAME_EQUAL (1 << 3)
#define PO1030_SENSOR_RESET (1 << 5)
#define PO1030_SUBSAMPLING (1 << 6)
/*****************************************************************************/
#define PO1030_GLOBAL_GAIN_DEFAULT 0x12
#define PO1030_EXPOSURE_DEFAULT 0x0085
#define PO1030_BLUE_GAIN_DEFAULT 0x36
#define PO1030_RED_GAIN_DEFAULT 0x36
#define PO1030_GREEN_GAIN_DEFAULT 0x40
/*****************************************************************************/
/* Kernel module parameters */
extern int force_sensor;
extern int dump_sensor;
int po1030_probe(struct sd *sd);
int po1030_init(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,
.start = po1030_start,
.disconnect = po1030_disconnect,
};
static const unsigned char preinit_po1030[][3] =
{
{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02},
{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0},
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
{BRIDGE, M5602_XB_SENSOR_CTRL, 0x00},
{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
{BRIDGE, M5602_XB_GPIO_DIR, 0x05},
{BRIDGE, M5602_XB_GPIO_DAT, 0x04},
{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
{BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
{BRIDGE, M5602_XB_GPIO_DAT_H, 0x02},
{SENSOR, PO1030_AUTOCTRL2, PO1030_SENSOR_RESET | (1 << 2)},
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04},
{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
{BRIDGE, M5602_XB_GPIO_DIR, 0x05},
{BRIDGE, M5602_XB_GPIO_DAT, 0x00}
};
static const unsigned char init_po1030[][3] =
{
{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02},
{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0},
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
{BRIDGE, M5602_XB_SENSOR_CTRL, 0x00},
{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
{SENSOR, PO1030_AUTOCTRL2, PO1030_SENSOR_RESET | (1 << 2)},
{BRIDGE, M5602_XB_GPIO_DIR, 0x05},
{BRIDGE, M5602_XB_GPIO_DAT, 0x04},
{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
{BRIDGE, M5602_XB_GPIO_EN_L, 0x00},
{BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
{BRIDGE, M5602_XB_GPIO_DAT_H, 0x02},
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04},
{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
{BRIDGE, M5602_XB_GPIO_DIR, 0x05},
{BRIDGE, M5602_XB_GPIO_DAT, 0x00},
{SENSOR, PO1030_AUTOCTRL2, 0x04},
{SENSOR, PO1030_OUTFORMCTRL2, PO1030_RAW_RGB_BAYER},
{SENSOR, PO1030_AUTOCTRL1, PO1030_WEIGHT_WIN_2X},
{SENSOR, PO1030_CONTROL2, 0x03},
{SENSOR, 0x21, 0x90},
{SENSOR, PO1030_YTARGET, 0x60},
{SENSOR, 0x59, 0x13},
{SENSOR, PO1030_OUTFORMCTRL1, PO1030_HREF_ENABLE},
{SENSOR, PO1030_EDGE_ENH_OFF, 0x00},
{SENSOR, PO1030_EGA, 0x80},
{SENSOR, 0x78, 0x14},
{SENSOR, 0x6f, 0x01},
{SENSOR, PO1030_GLOBALGAINMAX, 0x14},
{SENSOR, PO1030_Cb_U_GAIN, 0x38},
{SENSOR, PO1030_Cr_V_GAIN, 0x38},
{SENSOR, PO1030_CONTROL1, PO1030_SHUTTER_MODE |
PO1030_AUTO_SUBSAMPLING |
PO1030_FRAME_EQUAL},
{SENSOR, PO1030_GC0, 0x10},
{SENSOR, PO1030_GC1, 0x20},
{SENSOR, PO1030_GC2, 0x40},
{SENSOR, PO1030_GC3, 0x60},
{SENSOR, PO1030_GC4, 0x80},
{SENSOR, PO1030_GC5, 0xa0},
{SENSOR, PO1030_GC6, 0xc0},
{SENSOR, PO1030_GC7, 0xff},
/* Set the width to 751 */
{SENSOR, PO1030_FRAMEWIDTH_H, 0x02},
{SENSOR, PO1030_FRAMEWIDTH_L, 0xef},
/* Set the height to 540 */
{SENSOR, PO1030_FRAMEHEIGHT_H, 0x02},
{SENSOR, PO1030_FRAMEHEIGHT_L, 0x1c},
/* Set the x window to 1 */
{SENSOR, PO1030_WINDOWX_H, 0x00},
{SENSOR, PO1030_WINDOWX_L, 0x01},
/* Set the y window to 1 */
{SENSOR, PO1030_WINDOWY_H, 0x00},
{SENSOR, PO1030_WINDOWY_L, 0x01},
/* with a very low lighted environment increase the exposure but
* decrease the FPS (Frame Per Second) */
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
{BRIDGE, M5602_XB_GPIO_DIR, 0x05},
{BRIDGE, M5602_XB_GPIO_DAT, 0x00},
{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
{BRIDGE, M5602_XB_GPIO_EN_L, 0x00},
};
#endif

View File

@@ -0,0 +1,709 @@
/*
* Driver for the s5k4aa sensor
*
* Copyright (C) 2008 Erik Andrén
* Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
* Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
*
* Portions of code to USB interface and ALi driver software,
* Copyright (c) 2006 Willem Duinker
* v4l2 interface modeled after the V4L2 driver
* for SN9C10x PC Camera Controllers
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, version 2.
*
*/
#include "m5602_s5k4aa.h"
static int s5k4aa_get_exposure(struct gspca_dev *gspca_dev, __s32 *val);
static int s5k4aa_set_exposure(struct gspca_dev *gspca_dev, __s32 val);
static int s5k4aa_get_vflip(struct gspca_dev *gspca_dev, __s32 *val);
static int s5k4aa_set_vflip(struct gspca_dev *gspca_dev, __s32 val);
static int s5k4aa_get_hflip(struct gspca_dev *gspca_dev, __s32 *val);
static int s5k4aa_set_hflip(struct gspca_dev *gspca_dev, __s32 val);
static int s5k4aa_get_gain(struct gspca_dev *gspca_dev, __s32 *val);
static int s5k4aa_set_gain(struct gspca_dev *gspca_dev, __s32 val);
static int s5k4aa_get_noise(struct gspca_dev *gspca_dev, __s32 *val);
static int s5k4aa_set_noise(struct gspca_dev *gspca_dev, __s32 val);
static int s5k4aa_get_brightness(struct gspca_dev *gspca_dev, __s32 *val);
static int s5k4aa_set_brightness(struct gspca_dev *gspca_dev, __s32 val);
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 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 = "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
}
};
static const struct ctrl s5k4aa_ctrls[] = {
#define VFLIP_IDX 0
{
{
.id = V4L2_CID_VFLIP,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.name = "vertical flip",
.minimum = 0,
.maximum = 1,
.step = 1,
.default_value = 0
},
.set = s5k4aa_set_vflip,
.get = s5k4aa_get_vflip
},
#define HFLIP_IDX 1
{
{
.id = V4L2_CID_HFLIP,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.name = "horizontal flip",
.minimum = 0,
.maximum = 1,
.step = 1,
.default_value = 0
},
.set = s5k4aa_set_hflip,
.get = s5k4aa_get_hflip
},
#define GAIN_IDX 2
{
{
.id = V4L2_CID_GAIN,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Gain",
.minimum = 0,
.maximum = 127,
.step = 1,
.default_value = S5K4AA_DEFAULT_GAIN,
.flags = V4L2_CTRL_FLAG_SLIDER
},
.set = s5k4aa_set_gain,
.get = s5k4aa_get_gain
},
#define EXPOSURE_IDX 3
{
{
.id = V4L2_CID_EXPOSURE,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Exposure",
.minimum = 13,
.maximum = 0xfff,
.step = 1,
.default_value = 0x100,
.flags = V4L2_CTRL_FLAG_SLIDER
},
.set = s5k4aa_set_exposure,
.get = s5k4aa_get_exposure
},
#define NOISE_SUPP_IDX 4
{
{
.id = V4L2_CID_PRIVATE_BASE,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.name = "Noise suppression (smoothing)",
.minimum = 0,
.maximum = 1,
.step = 1,
.default_value = 1,
},
.set = s5k4aa_set_noise,
.get = s5k4aa_get_noise
},
#define BRIGHTNESS_IDX 5
{
{
.id = V4L2_CID_BRIGHTNESS,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Brightness",
.minimum = 0,
.maximum = 0x1f,
.step = 1,
.default_value = S5K4AA_DEFAULT_BRIGHTNESS,
},
.set = s5k4aa_set_brightness,
.get = s5k4aa_get_brightness
},
};
static void s5k4aa_dump_registers(struct sd *sd);
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};
int i, err = 0;
s32 *sensor_settings;
if (force_sensor) {
if (force_sensor == S5K4AA_SENSOR) {
info("Forcing a %s sensor", s5k4aa.name);
goto sensor_found;
}
/* If we want to force another sensor, don't try to probe this
* one */
return -ENODEV;
}
info("Probing for a s5k4aa sensor");
/* Preinit the sensor */
for (i = 0; i < ARRAY_SIZE(preinit_s5k4aa) && !err; i++) {
u8 data[2] = {0x00, 0x00};
switch (preinit_s5k4aa[i][0]) {
case BRIDGE:
err = m5602_write_bridge(sd,
preinit_s5k4aa[i][1],
preinit_s5k4aa[i][2]);
break;
case SENSOR:
data[0] = preinit_s5k4aa[i][2];
err = m5602_write_sensor(sd,
preinit_s5k4aa[i][1],
data, 1);
break;
case SENSOR_LONG:
data[0] = preinit_s5k4aa[i][2];
data[1] = preinit_s5k4aa[i][3];
err = m5602_write_sensor(sd,
preinit_s5k4aa[i][1],
data, 2);
break;
default:
info("Invalid stream command, exiting init");
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
info("Detected a s5k4aa sensor");
sensor_found:
sensor_settings = kmalloc(
ARRAY_SIZE(s5k4aa_ctrls) * sizeof(s32), GFP_KERNEL);
if (!sensor_settings)
return -ENOMEM;
sd->gspca_dev.cam.cam_mode = s5k4aa_modes;
sd->gspca_dev.cam.nmodes = ARRAY_SIZE(s5k4aa_modes);
sd->desc->ctrls = s5k4aa_ctrls;
sd->desc->nctrls = ARRAY_SIZE(s5k4aa_ctrls);
for (i = 0; i < ARRAY_SIZE(s5k4aa_ctrls); i++)
sensor_settings[i] = s5k4aa_ctrls[i].qctrl.default_value;
sd->sensor_priv = sensor_settings;
return 0;
}
int s5k4aa_start(struct sd *sd)
{
int i, err = 0;
u8 data[2];
struct cam *cam = &sd->gspca_dev.cam;
s32 *sensor_settings = sd->sensor_priv;
switch (cam->cam_mode[sd->gspca_dev.curr_mode].width) {
case 1280:
PDEBUG(D_V4L2, "Configuring camera for SXGA mode");
for (i = 0; i < ARRAY_SIZE(SXGA_s5k4aa); i++) {
switch (SXGA_s5k4aa[i][0]) {
case BRIDGE:
err = m5602_write_bridge(sd,
SXGA_s5k4aa[i][1],
SXGA_s5k4aa[i][2]);
break;
case SENSOR:
data[0] = SXGA_s5k4aa[i][2];
err = m5602_write_sensor(sd,
SXGA_s5k4aa[i][1],
data, 1);
break;
case SENSOR_LONG:
data[0] = SXGA_s5k4aa[i][2];
data[1] = SXGA_s5k4aa[i][3];
err = m5602_write_sensor(sd,
SXGA_s5k4aa[i][1],
data, 2);
break;
default:
err("Invalid stream command, exiting init");
return -EINVAL;
}
}
err = s5k4aa_set_noise(&sd->gspca_dev, 0);
if (err < 0)
return err;
break;
case 640:
PDEBUG(D_V4L2, "Configuring camera for VGA mode");
for (i = 0; i < ARRAY_SIZE(VGA_s5k4aa); i++) {
switch (VGA_s5k4aa[i][0]) {
case BRIDGE:
err = m5602_write_bridge(sd,
VGA_s5k4aa[i][1],
VGA_s5k4aa[i][2]);
break;
case SENSOR:
data[0] = VGA_s5k4aa[i][2];
err = m5602_write_sensor(sd,
VGA_s5k4aa[i][1],
data, 1);
break;
case SENSOR_LONG:
data[0] = VGA_s5k4aa[i][2];
data[1] = VGA_s5k4aa[i][3];
err = m5602_write_sensor(sd,
VGA_s5k4aa[i][1],
data, 2);
break;
default:
err("Invalid stream command, exiting init");
return -EINVAL;
}
}
err = s5k4aa_set_noise(&sd->gspca_dev, 1);
if (err < 0)
return err;
break;
}
if (err < 0)
return err;
err = s5k4aa_set_exposure(&sd->gspca_dev,
sensor_settings[EXPOSURE_IDX]);
if (err < 0)
return err;
err = s5k4aa_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]);
if (err < 0)
return err;
err = s5k4aa_set_brightness(&sd->gspca_dev,
sensor_settings[BRIGHTNESS_IDX]);
if (err < 0)
return err;
err = s5k4aa_set_noise(&sd->gspca_dev, sensor_settings[NOISE_SUPP_IDX]);
if (err < 0)
return err;
err = s5k4aa_set_vflip(&sd->gspca_dev, sensor_settings[VFLIP_IDX]);
if (err < 0)
return err;
return s5k4aa_set_hflip(&sd->gspca_dev, sensor_settings[HFLIP_IDX]);
}
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:
info("Invalid stream command, exiting init");
return -EINVAL;
}
}
if (dump_sensor)
s5k4aa_dump_registers(sd);
return err;
}
static int s5k4aa_get_exposure(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
*val = sensor_settings[EXPOSURE_IDX];
PDEBUG(D_V4L2, "Read exposure %d", *val);
return 0;
}
static int s5k4aa_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
u8 data = S5K4AA_PAGE_MAP_2;
int err;
sensor_settings[EXPOSURE_IDX] = val;
PDEBUG(D_V4L2, "Set exposure to %d", val);
err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
if (err < 0)
return err;
data = (val >> 8) & 0xff;
err = m5602_write_sensor(sd, S5K4AA_EXPOSURE_HI, &data, 1);
if (err < 0)
return err;
data = val & 0xff;
err = m5602_write_sensor(sd, S5K4AA_EXPOSURE_LO, &data, 1);
return err;
}
static int s5k4aa_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
*val = sensor_settings[VFLIP_IDX];
PDEBUG(D_V4L2, "Read vertical flip %d", *val);
return 0;
}
static int s5k4aa_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
u8 data = S5K4AA_PAGE_MAP_2;
int err;
sensor_settings[VFLIP_IDX] = val;
PDEBUG(D_V4L2, "Set vertical flip to %d", val);
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))
val = !val;
data = ((data & ~S5K4AA_RM_V_FLIP) | ((val & 0x01) << 7));
err = m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1);
if (err < 0)
return err;
err = m5602_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
if (err < 0)
return err;
data = (data & 0xfe) | !val;
err = m5602_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
return err;
}
static int s5k4aa_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
*val = sensor_settings[HFLIP_IDX];
PDEBUG(D_V4L2, "Read horizontal flip %d", *val);
return 0;
}
static int s5k4aa_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
u8 data = S5K4AA_PAGE_MAP_2;
int err;
sensor_settings[HFLIP_IDX] = val;
PDEBUG(D_V4L2, "Set horizontal flip to %d", val);
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))
val = !val;
data = ((data & ~S5K4AA_RM_H_FLIP) | ((val & 0x01) << 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;
data = (data & 0xfe) | !val;
err = m5602_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
return err;
}
static int s5k4aa_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
*val = sensor_settings[GAIN_IDX];
PDEBUG(D_V4L2, "Read gain %d", *val);
return 0;
}
static int s5k4aa_set_gain(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
u8 data = S5K4AA_PAGE_MAP_2;
int err;
sensor_settings[GAIN_IDX] = val;
PDEBUG(D_V4L2, "Set gain to %d", val);
err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
if (err < 0)
return err;
data = val & 0xff;
err = m5602_write_sensor(sd, S5K4AA_GAIN, &data, 1);
return err;
}
static int s5k4aa_get_brightness(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
*val = sensor_settings[BRIGHTNESS_IDX];
PDEBUG(D_V4L2, "Read brightness %d", *val);
return 0;
}
static int s5k4aa_set_brightness(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
u8 data = S5K4AA_PAGE_MAP_2;
int err;
sensor_settings[BRIGHTNESS_IDX] = val;
PDEBUG(D_V4L2, "Set brightness to %d", val);
err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
if (err < 0)
return err;
data = val & 0xff;
return m5602_write_sensor(sd, S5K4AA_BRIGHTNESS, &data, 1);
}
static int s5k4aa_get_noise(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
*val = sensor_settings[NOISE_SUPP_IDX];
PDEBUG(D_V4L2, "Read noise %d", *val);
return 0;
}
static int s5k4aa_set_noise(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
u8 data = S5K4AA_PAGE_MAP_2;
int err;
sensor_settings[NOISE_SUPP_IDX] = val;
PDEBUG(D_V4L2, "Set noise to %d", val);
err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
if (err < 0)
return err;
data = val & 0x01;
return m5602_write_sensor(sd, S5K4AA_NOISE_SUPP, &data, 1);
}
void s5k4aa_disconnect(struct sd *sd)
{
sd->sensor = NULL;
kfree(sd->sensor_priv);
}
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);
info("Dumping the s5k4aa register state for page 0x%x", page);
for (address = 0; address <= 0xff; address++) {
u8 value = 0;
m5602_read_sensor(sd, address, &value, 1);
info("register 0x%x contains 0x%x",
address, value);
}
}
info("s5k4aa register state dump complete");
for (page = 0; page < 16; page++) {
m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &page, 1);
info("Probing for which registers that are "
"read/write for page 0x%x", 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)
info("register 0x%x is writeable", address);
else
info("register 0x%x is read only", address);
/* Restore original value */
m5602_write_sensor(sd, address, &old_value, 1);
}
}
info("Read/write register probing complete");
m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &old_page, 1);
}

View File

@@ -0,0 +1,289 @@
/*
* Driver for the s5k4aa sensor
*
* Copyright (C) 2008 Erik Andrén
* Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
* Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
*
* Portions of code to USB interface and ALi driver software,
* Copyright (c) 2006 Willem Duinker
* v4l2 interface modeled after the V4L2 driver
* for SN9C10x PC Camera Controllers
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, version 2.
*
*/
#ifndef M5602_S5K4AA_H_
#define M5602_S5K4AA_H_
#include <linux/dmi.h>
#include "m5602_sensor.h"
/*****************************************************************************/
#define S5K4AA_PAGE_MAP 0xec
#define S5K4AA_PAGE_MAP_0 0x00
#define S5K4AA_PAGE_MAP_1 0x01
#define S5K4AA_PAGE_MAP_2 0x02
/* Sensor register definitions for page 0x02 */
#define S5K4AA_READ_MODE 0x03
#define S5K4AA_ROWSTART_HI 0x04
#define S5K4AA_ROWSTART_LO 0x05
#define S5K4AA_COLSTART_HI 0x06
#define S5K4AA_COLSTART_LO 0x07
#define S5K4AA_WINDOW_HEIGHT_HI 0x08
#define S5K4AA_WINDOW_HEIGHT_LO 0x09
#define S5K4AA_WINDOW_WIDTH_HI 0x0a
#define S5K4AA_WINDOW_WIDTH_LO 0x0b
#define S5K4AA_GLOBAL_GAIN__ 0x0f
/* sync lost, if too low, reduces frame rate if too high */
#define S5K4AA_H_BLANK_HI__ 0x1d
#define S5K4AA_H_BLANK_LO__ 0x1e
#define S5K4AA_EXPOSURE_HI 0x17
#define S5K4AA_EXPOSURE_LO 0x18
#define S5K4AA_BRIGHTNESS 0x1f /* (digital?) gain : 5 bits */
#define S5K4AA_GAIN 0x20 /* (analogue?) gain : 7 bits */
#define S5K4AA_NOISE_SUPP 0x37
#define S5K4AA_RM_ROW_SKIP_4X 0x08
#define S5K4AA_RM_ROW_SKIP_2X 0x04
#define S5K4AA_RM_COL_SKIP_4X 0x02
#define S5K4AA_RM_COL_SKIP_2X 0x01
#define S5K4AA_RM_H_FLIP 0x40
#define S5K4AA_RM_V_FLIP 0x80
#define S5K4AA_DEFAULT_GAIN 0x5f
#define S5K4AA_DEFAULT_BRIGHTNESS 0x10
/*****************************************************************************/
/* Kernel module parameters */
extern int force_sensor;
extern int dump_sensor;
int s5k4aa_probe(struct sd *sd);
int s5k4aa_init(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,
.start = s5k4aa_start,
.disconnect = s5k4aa_disconnect,
};
static const unsigned char preinit_s5k4aa[][4] =
{
{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00},
{BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00},
{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
{BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00},
{BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00},
{BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00},
{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
{BRIDGE, M5602_XB_GPIO_DAT, 0x00, 0x00},
{BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
{BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
{BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
{BRIDGE, M5602_XB_SENSOR_TYPE, 0x08, 0x00},
{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
{BRIDGE, M5602_XB_GPIO_DAT, 0x14, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00},
{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
{BRIDGE, M5602_XB_GPIO_DAT, 0x1c, 0x00},
{BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
{BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00},
{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
{BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
{BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00},
{SENSOR, S5K4AA_PAGE_MAP, 0x00, 0x00}
};
static const unsigned char init_s5k4aa[][4] =
{
{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00},
{BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00},
{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
{BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00},
{BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00},
{BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00},
{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
{BRIDGE, M5602_XB_GPIO_DAT, 0x00, 0x00},
{BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
{BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
{BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
{BRIDGE, M5602_XB_SENSOR_TYPE, 0x08, 0x00},
{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
{BRIDGE, M5602_XB_GPIO_DAT, 0x14, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00},
{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
{BRIDGE, M5602_XB_GPIO_DAT, 0x1c, 0x00},
{BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
{BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00},
{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
{BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
{BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00},
{SENSOR, S5K4AA_PAGE_MAP, 0x07, 0x00},
{SENSOR, 0x36, 0x01, 0x00},
{SENSOR, S5K4AA_PAGE_MAP, 0x00, 0x00},
{SENSOR, 0x7b, 0xff, 0x00},
{SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00},
{SENSOR, 0x0c, 0x05, 0x00},
{SENSOR, 0x02, 0x0e, 0x00},
{SENSOR, S5K4AA_READ_MODE, 0xa0, 0x00},
{SENSOR, 0x37, 0x00, 0x00},
};
static const unsigned char VGA_s5k4aa[][4] =
{
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
{BRIDGE, M5602_XB_SENSOR_TYPE, 0x08, 0x00},
{BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
{BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
{BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
/* VSYNC_PARA, VSYNC_PARA : img height 480 = 0x01e0 */
{BRIDGE, M5602_XB_VSYNC_PARA, 0x01, 0x00},
{BRIDGE, M5602_XB_VSYNC_PARA, 0xe0, 0x00},
{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
{BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
{BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00},
{BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
{BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
/* HSYNC_PARA, HSYNC_PARA : img width 640 = 0x0280 */
{BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00},
{BRIDGE, M5602_XB_HSYNC_PARA, 0x80, 0x00},
{BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xa0, 0x00}, /* 48 MHz */
{SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00},
{SENSOR, S5K4AA_READ_MODE, S5K4AA_RM_H_FLIP | S5K4AA_RM_ROW_SKIP_2X
| S5K4AA_RM_COL_SKIP_2X, 0x00},
/* 0x37 : Fix image stability when light is too bright and improves
* image quality in 640x480, but worsens it in 1280x1024 */
{SENSOR, 0x37, 0x01, 0x00},
/* ROWSTART_HI, ROWSTART_LO : 10 + (1024-960)/2 = 42 = 0x002a */
{SENSOR, S5K4AA_ROWSTART_HI, 0x00, 0x00},
{SENSOR, S5K4AA_ROWSTART_LO, 0x29, 0x00},
{SENSOR, S5K4AA_COLSTART_HI, 0x00, 0x00},
{SENSOR, S5K4AA_COLSTART_LO, 0x0c, 0x00},
/* window_height_hi, window_height_lo : 960 = 0x03c0 */
{SENSOR, S5K4AA_WINDOW_HEIGHT_HI, 0x03, 0x00},
{SENSOR, S5K4AA_WINDOW_HEIGHT_LO, 0xc0, 0x00},
/* window_width_hi, window_width_lo : 1280 = 0x0500 */
{SENSOR, S5K4AA_WINDOW_WIDTH_HI, 0x05, 0x00},
{SENSOR, S5K4AA_WINDOW_WIDTH_LO, 0x00, 0x00},
{SENSOR, S5K4AA_H_BLANK_HI__, 0x00, 0x00},
{SENSOR, S5K4AA_H_BLANK_LO__, 0xa8, 0x00}, /* helps to sync... */
{SENSOR, S5K4AA_EXPOSURE_HI, 0x01, 0x00},
{SENSOR, S5K4AA_EXPOSURE_LO, 0x00, 0x00},
{SENSOR, 0x11, 0x04, 0x00},
{SENSOR, 0x12, 0xc3, 0x00},
{SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00},
{SENSOR, 0x02, 0x0e, 0x00},
};
static const unsigned char SXGA_s5k4aa[][4] =
{
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
{BRIDGE, M5602_XB_SENSOR_TYPE, 0x08, 0x00},
{BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
{BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
{BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
/* VSYNC_PARA, VSYNC_PARA : img height 1024 = 0x0400 */
{BRIDGE, M5602_XB_VSYNC_PARA, 0x04, 0x00},
{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
{BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
{BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00},
{BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
{BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
/* HSYNC_PARA, HSYNC_PARA : img width 1280 = 0x0500 */
{BRIDGE, M5602_XB_HSYNC_PARA, 0x05, 0x00},
{BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
{BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xa0, 0x00}, /* 48 MHz */
{SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00},
{SENSOR, S5K4AA_READ_MODE, S5K4AA_RM_H_FLIP, 0x00},
{SENSOR, 0x37, 0x01, 0x00},
{SENSOR, S5K4AA_ROWSTART_HI, 0x00, 0x00},
{SENSOR, S5K4AA_ROWSTART_LO, 0x09, 0x00},
{SENSOR, S5K4AA_COLSTART_HI, 0x00, 0x00},
{SENSOR, S5K4AA_COLSTART_LO, 0x0a, 0x00},
{SENSOR, S5K4AA_WINDOW_HEIGHT_HI, 0x04, 0x00},
{SENSOR, S5K4AA_WINDOW_HEIGHT_LO, 0x00, 0x00},
{SENSOR, S5K4AA_WINDOW_WIDTH_HI, 0x05, 0x00},
{SENSOR, S5K4AA_WINDOW_WIDTH_LO, 0x00, 0x00},
{SENSOR, S5K4AA_H_BLANK_HI__, 0x01, 0x00},
{SENSOR, S5K4AA_H_BLANK_LO__, 0xa8, 0x00},
{SENSOR, S5K4AA_EXPOSURE_HI, 0x01, 0x00},
{SENSOR, S5K4AA_EXPOSURE_LO, 0x00, 0x00},
{SENSOR, 0x11, 0x04, 0x00},
{SENSOR, 0x12, 0xc3, 0x00},
{SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00},
{SENSOR, 0x02, 0x0e, 0x00},
};
#endif

View File

@@ -0,0 +1,601 @@
/*
* Driver for the s5k83a sensor
*
* Copyright (C) 2008 Erik Andrén
* Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
* Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
*
* Portions of code to USB interface and ALi driver software,
* Copyright (c) 2006 Willem Duinker
* v4l2 interface modeled after the V4L2 driver
* for SN9C10x PC Camera Controllers
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, version 2.
*
*/
#include <linux/kthread.h>
#include "m5602_s5k83a.h"
static int s5k83a_set_gain(struct gspca_dev *gspca_dev, __s32 val);
static int s5k83a_get_gain(struct gspca_dev *gspca_dev, __s32 *val);
static int s5k83a_set_brightness(struct gspca_dev *gspca_dev, __s32 val);
static int s5k83a_get_brightness(struct gspca_dev *gspca_dev, __s32 *val);
static int s5k83a_set_exposure(struct gspca_dev *gspca_dev, __s32 val);
static int s5k83a_get_exposure(struct gspca_dev *gspca_dev, __s32 *val);
static int s5k83a_get_vflip(struct gspca_dev *gspca_dev, __s32 *val);
static int s5k83a_set_vflip(struct gspca_dev *gspca_dev, __s32 val);
static int s5k83a_get_hflip(struct gspca_dev *gspca_dev, __s32 *val);
static int s5k83a_set_hflip(struct gspca_dev *gspca_dev, __s32 val);
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 struct ctrl s5k83a_ctrls[] = {
#define GAIN_IDX 0
{
{
.id = V4L2_CID_GAIN,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "gain",
.minimum = 0x00,
.maximum = 0xff,
.step = 0x01,
.default_value = S5K83A_DEFAULT_GAIN,
.flags = V4L2_CTRL_FLAG_SLIDER
},
.set = s5k83a_set_gain,
.get = s5k83a_get_gain
},
#define BRIGHTNESS_IDX 1
{
{
.id = V4L2_CID_BRIGHTNESS,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "brightness",
.minimum = 0x00,
.maximum = 0xff,
.step = 0x01,
.default_value = S5K83A_DEFAULT_BRIGHTNESS,
.flags = V4L2_CTRL_FLAG_SLIDER
},
.set = s5k83a_set_brightness,
.get = s5k83a_get_brightness,
},
#define EXPOSURE_IDX 2
{
{
.id = V4L2_CID_EXPOSURE,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "exposure",
.minimum = 0x00,
.maximum = S5K83A_MAXIMUM_EXPOSURE,
.step = 0x01,
.default_value = S5K83A_DEFAULT_EXPOSURE,
.flags = V4L2_CTRL_FLAG_SLIDER
},
.set = s5k83a_set_exposure,
.get = s5k83a_get_exposure
},
#define HFLIP_IDX 3
{
{
.id = V4L2_CID_HFLIP,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.name = "horizontal flip",
.minimum = 0,
.maximum = 1,
.step = 1,
.default_value = 0
},
.set = s5k83a_set_hflip,
.get = s5k83a_get_hflip
},
#define VFLIP_IDX 4
{
{
.id = V4L2_CID_VFLIP,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.name = "vertical flip",
.minimum = 0,
.maximum = 1,
.step = 1,
.default_value = 0
},
.set = s5k83a_set_vflip,
.get = s5k83a_get_vflip
}
};
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)
{
struct s5k83a_priv *sens_priv;
u8 prod_id = 0, ver_id = 0;
int i, err = 0;
if (force_sensor) {
if (force_sensor == S5K83A_SENSOR) {
info("Forcing a %s sensor", s5k83a.name);
goto sensor_found;
}
/* If we want to force another sensor, don't try to probe this
* one */
return -ENODEV;
}
info("Probing for a s5k83a sensor");
/* Preinit the sensor */
for (i = 0; i < ARRAY_SIZE(preinit_s5k83a) && !err; i++) {
u8 data[2] = {preinit_s5k83a[i][2], preinit_s5k83a[i][3]};
if (preinit_s5k83a[i][0] == SENSOR)
err = m5602_write_sensor(sd, preinit_s5k83a[i][1],
data, 2);
else
err = m5602_write_bridge(sd, preinit_s5k83a[i][1],
data[0]);
}
/* We don't know what register (if any) that contain the product id
* Just pick the first addresses that seem to produce the same results
* on multiple machines */
if (m5602_read_sensor(sd, 0x00, &prod_id, 1))
return -ENODEV;
if (m5602_read_sensor(sd, 0x01, &ver_id, 1))
return -ENODEV;
if ((prod_id == 0xff) || (ver_id == 0xff))
return -ENODEV;
else
info("Detected a s5k83a sensor");
sensor_found:
sens_priv = kmalloc(
sizeof(struct s5k83a_priv), GFP_KERNEL);
if (!sens_priv)
return -ENOMEM;
sens_priv->settings =
kmalloc(sizeof(s32)*ARRAY_SIZE(s5k83a_ctrls), GFP_KERNEL);
if (!sens_priv->settings) {
kfree(sens_priv);
return -ENOMEM;
}
sd->gspca_dev.cam.cam_mode = s5k83a_modes;
sd->gspca_dev.cam.nmodes = ARRAY_SIZE(s5k83a_modes);
sd->desc->ctrls = s5k83a_ctrls;
sd->desc->nctrls = ARRAY_SIZE(s5k83a_ctrls);
/* null the pointer! thread is't running now */
sens_priv->rotation_thread = NULL;
for (i = 0; i < ARRAY_SIZE(s5k83a_ctrls); i++)
sens_priv->settings[i] = s5k83a_ctrls[i].qctrl.default_value;
sd->sensor_priv = sens_priv;
return 0;
}
int s5k83a_init(struct sd *sd)
{
int i, err = 0;
s32 *sensor_settings =
((struct s5k83a_priv *) sd->sensor_priv)->settings;
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:
info("Invalid stream command, exiting init");
return -EINVAL;
}
}
if (dump_sensor)
s5k83a_dump_registers(sd);
err = s5k83a_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]);
if (err < 0)
return err;
err = s5k83a_set_brightness(&sd->gspca_dev,
sensor_settings[BRIGHTNESS_IDX]);
if (err < 0)
return err;
err = s5k83a_set_exposure(&sd->gspca_dev,
sensor_settings[EXPOSURE_IDX]);
if (err < 0)
return err;
err = s5k83a_set_hflip(&sd->gspca_dev, sensor_settings[HFLIP_IDX]);
if (err < 0)
return err;
err = s5k83a_set_vflip(&sd->gspca_dev, sensor_settings[VFLIP_IDX]);
return err;
}
static int rotation_thread_function(void *data)
{
struct sd *sd = (struct sd *) data;
struct s5k83a_priv *sens_priv = sd->sensor_priv;
u8 reg, previous_rotation = 0;
__s32 vflip, hflip;
set_current_state(TASK_INTERRUPTIBLE);
while (!schedule_timeout(100)) {
if (mutex_lock_interruptible(&sd->gspca_dev.usb_lock))
break;
s5k83a_get_rotation(sd, &reg);
if (previous_rotation != reg) {
previous_rotation = reg;
info("Camera was flipped");
s5k83a_get_vflip((struct gspca_dev *) sd, &vflip);
s5k83a_get_hflip((struct gspca_dev *) sd, &hflip);
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) {
s5k83a_get_vflip((struct gspca_dev *) sd, &vflip);
s5k83a_get_hflip((struct gspca_dev *) sd, &hflip);
s5k83a_set_flip_real((struct gspca_dev *) sd, vflip, hflip);
}
sens_priv->rotation_thread = NULL;
return 0;
}
int s5k83a_start(struct sd *sd)
{
int i, err = 0;
struct s5k83a_priv *sens_priv = sd->sensor_priv;
/* 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 */
sens_priv->rotation_thread = kthread_create(rotation_thread_function,
sd, "rotation thread");
wake_up_process(sens_priv->rotation_thread);
/* Preinit the sensor */
for (i = 0; i < ARRAY_SIZE(start_s5k83a) && !err; i++) {
u8 data[2] = {start_s5k83a[i][2], start_s5k83a[i][3]};
if (start_s5k83a[i][0] == SENSOR)
err = m5602_write_sensor(sd, start_s5k83a[i][1],
data, 2);
else
err = m5602_write_bridge(sd, start_s5k83a[i][1],
data[0]);
}
if (err < 0)
return err;
return s5k83a_set_led_indication(sd, 1);
}
int s5k83a_stop(struct sd *sd)
{
struct s5k83a_priv *sens_priv = sd->sensor_priv;
if (sens_priv->rotation_thread)
kthread_stop(sens_priv->rotation_thread);
return s5k83a_set_led_indication(sd, 0);
}
void s5k83a_disconnect(struct sd *sd)
{
struct s5k83a_priv *sens_priv = sd->sensor_priv;
s5k83a_stop(sd);
sd->sensor = NULL;
kfree(sens_priv->settings);
kfree(sens_priv);
}
static int s5k83a_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
struct s5k83a_priv *sens_priv = sd->sensor_priv;
*val = sens_priv->settings[GAIN_IDX];
return 0;
}
static int s5k83a_set_gain(struct gspca_dev *gspca_dev, __s32 val)
{
int err;
u8 data[2];
struct sd *sd = (struct sd *) gspca_dev;
struct s5k83a_priv *sens_priv = sd->sensor_priv;
sens_priv->settings[GAIN_IDX] = val;
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_get_brightness(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
struct s5k83a_priv *sens_priv = sd->sensor_priv;
*val = sens_priv->settings[BRIGHTNESS_IDX];
return 0;
}
static int s5k83a_set_brightness(struct gspca_dev *gspca_dev, __s32 val)
{
int err;
u8 data[1];
struct sd *sd = (struct sd *) gspca_dev;
struct s5k83a_priv *sens_priv = sd->sensor_priv;
sens_priv->settings[BRIGHTNESS_IDX] = val;
data[0] = val;
err = m5602_write_sensor(sd, S5K83A_BRIGHTNESS, data, 1);
return err;
}
static int s5k83a_get_exposure(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
struct s5k83a_priv *sens_priv = sd->sensor_priv;
*val = sens_priv->settings[EXPOSURE_IDX];
return 0;
}
static int s5k83a_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
{
int err;
u8 data[2];
struct sd *sd = (struct sd *) gspca_dev;
struct s5k83a_priv *sens_priv = sd->sensor_priv;
sens_priv->settings[EXPOSURE_IDX] = val;
data[0] = 0;
data[1] = val;
err = m5602_write_sensor(sd, S5K83A_EXPOSURE, data, 2);
return err;
}
static int s5k83a_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
struct s5k83a_priv *sens_priv = sd->sensor_priv;
*val = sens_priv->settings[VFLIP_IDX];
return 0;
}
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_vflip(struct gspca_dev *gspca_dev, __s32 val)
{
int err;
u8 reg;
__s32 hflip;
struct sd *sd = (struct sd *) gspca_dev;
struct s5k83a_priv *sens_priv = sd->sensor_priv;
sens_priv->settings[VFLIP_IDX] = val;
s5k83a_get_hflip(gspca_dev, &hflip);
err = s5k83a_get_rotation(sd, &reg);
if (err < 0)
return err;
if (reg) {
val = !val;
hflip = !hflip;
}
err = s5k83a_set_flip_real(gspca_dev, val, hflip);
return err;
}
static int s5k83a_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
struct s5k83a_priv *sens_priv = sd->sensor_priv;
*val = sens_priv->settings[HFLIP_IDX];
return 0;
}
static int s5k83a_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
{
int err;
u8 reg;
__s32 vflip;
struct sd *sd = (struct sd *) gspca_dev;
struct s5k83a_priv *sens_priv = sd->sensor_priv;
sens_priv->settings[HFLIP_IDX] = val;
s5k83a_get_vflip(gspca_dev, &vflip);
err = s5k83a_get_rotation(sd, &reg);
if (err < 0)
return err;
if (reg) {
val = !val;
vflip = !vflip;
}
err = s5k83a_set_flip_real(gspca_dev, vflip, val);
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);
info("Dumping the s5k83a register state for page 0x%x", page);
for (address = 0; address <= 0xff; address++) {
u8 val = 0;
m5602_read_sensor(sd, address, &val, 1);
info("register 0x%x contains 0x%x",
address, val);
}
}
info("s5k83a register state dump complete");
for (page = 0; page < 16; page++) {
m5602_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1);
info("Probing for which registers that are read/write "
"for page 0x%x", 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)
info("register 0x%x is writeable", address);
else
info("register 0x%x is read only", address);
/* Restore original val */
m5602_write_sensor(sd, address, &old_val, 1);
}
}
info("Read/write register probing complete");
m5602_write_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1);
}

View File

@@ -0,0 +1,197 @@
/*
* Driver for the s5k83a sensor
*
* Copyright (C) 2008 Erik Andrén
* Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
* Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
*
* Portions of code to USB interface and ALi driver software,
* Copyright (c) 2006 Willem Duinker
* v4l2 interface modeled after the V4L2 driver
* for SN9C10x PC Camera Controllers
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, version 2.
*
*/
#ifndef M5602_S5K83A_H_
#define M5602_S5K83A_H_
#include "m5602_sensor.h"
#define S5K83A_FLIP 0x01
#define S5K83A_HFLIP_TUNE 0x03
#define S5K83A_VFLIP_TUNE 0x05
#define S5K83A_BRIGHTNESS 0x0a
#define S5K83A_EXPOSURE 0x18
#define S5K83A_GAIN 0x1b
#define S5K83A_PAGE_MAP 0xec
#define S5K83A_DEFAULT_GAIN 0x71
#define S5K83A_DEFAULT_BRIGHTNESS 0x7e
#define S5K83A_DEFAULT_EXPOSURE 0x00
#define S5K83A_MAXIMUM_EXPOSURE 0x3c
#define S5K83A_FLIP_MASK 0x10
#define S5K83A_GPIO_LED_MASK 0x10
#define S5K83A_GPIO_ROTATION_MASK 0x40
/*****************************************************************************/
/* Kernel module parameters */
extern int force_sensor;
extern int dump_sensor;
int s5k83a_probe(struct sd *sd);
int s5k83a_init(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,
.start = s5k83a_start,
.stop = s5k83a_stop,
.disconnect = s5k83a_disconnect,
.i2c_slave_id = 0x5a,
.i2c_regW = 2,
};
struct s5k83a_priv {
/* We use another thread periodically
probing the orientation of the camera */
struct task_struct *rotation_thread;
s32 *settings;
};
static const unsigned char preinit_s5k83a[][4] =
{
{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00},
{BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00},
{BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
{BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00},
{BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00},
{BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00},
{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
{BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
{BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
{BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
{BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00},
{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
{BRIDGE, M5602_XB_GPIO_DAT, 0x1c, 0x00},
{BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
{BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00},
{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
{BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
{BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00},
};
/* This could probably be considerably shortened.
I don't have the hardware to experiment with it, patches welcome
*/
static const unsigned char init_s5k83a[][4] =
{
/* The following sequence is useless after a clean boot
but is necessary after resume from suspend */
{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
{BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00},
{BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00},
{BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00},
{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
{BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
{BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
{BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
{BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00},
{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
{BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00},
{BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
{BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00},
{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
{BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
{BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00},
{SENSOR, S5K83A_PAGE_MAP, 0x04, 0x00},
{SENSOR, 0xaf, 0x01, 0x00},
{SENSOR, S5K83A_PAGE_MAP, 0x00, 0x00},
{SENSOR, 0x7b, 0xff, 0x00},
{SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00},
{SENSOR, 0x01, 0x50, 0x00},
{SENSOR, 0x12, 0x20, 0x00},
{SENSOR, 0x17, 0x40, 0x00},
{SENSOR, 0x1c, 0x00, 0x00},
{SENSOR, 0x02, 0x70, 0x00},
{SENSOR, 0x03, 0x0b, 0x00},
{SENSOR, 0x04, 0xf0, 0x00},
{SENSOR, 0x05, 0x0b, 0x00},
{SENSOR, 0x06, 0x71, 0x00},
{SENSOR, 0x07, 0xe8, 0x00}, /* 488 */
{SENSOR, 0x08, 0x02, 0x00},
{SENSOR, 0x09, 0x88, 0x00}, /* 648 */
{SENSOR, 0x14, 0x00, 0x00},
{SENSOR, 0x15, 0x20, 0x00}, /* 32 */
{SENSOR, 0x19, 0x00, 0x00},
{SENSOR, 0x1a, 0x98, 0x00}, /* 152 */
{SENSOR, 0x0f, 0x02, 0x00},
{SENSOR, 0x10, 0xe5, 0x00}, /* 741 */
/* normal colors
(this is value after boot, but after tries can be different) */
{SENSOR, 0x00, 0x06, 0x00},
};
static const unsigned char start_s5k83a[][4] =
{
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
{BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
{BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
{BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
{BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
{BRIDGE, M5602_XB_VSYNC_PARA, 0x01, 0x00},
{BRIDGE, M5602_XB_VSYNC_PARA, 0xe4, 0x00}, /* 484 */
{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
{BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
{BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00},
{BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
{BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
{BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00},
{BRIDGE, M5602_XB_HSYNC_PARA, 0x7f, 0x00}, /* 639 */
{BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
};
#endif

View File

@@ -0,0 +1,70 @@
/*
* USB Driver for ALi m5602 based webcams
*
* Copyright (C) 2008 Erik Andrén
* Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
* Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
*
* Portions of code to USB interface and ALi driver software,
* Copyright (c) 2006 Willem Duinker
* v4l2 interface modeled after the V4L2 driver
* for SN9C10x PC Camera Controllers
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, version 2.
*
*/
#ifndef M5602_SENSOR_H_
#define M5602_SENSOR_H_
#include "m5602_bridge.h"
#define M5602_V4L2_CID_GREEN_BALANCE (V4L2_CID_PRIVATE_BASE + 0)
#define M5602_V4L2_CID_NOISE_SUPPRESION (V4L2_CID_PRIVATE_BASE + 1)
/* Enumerates all supported sensors */
enum sensors {
OV9650_SENSOR = 1,
S5K83A_SENSOR = 2,
S5K4AA_SENSOR = 3,
MT9M111_SENSOR = 4,
PO1030_SENSOR = 5,
OV7660_SENSOR = 6,
};
/* Enumerates all possible instruction types */
enum instruction {
BRIDGE,
SENSOR,
SENSOR_LONG
};
struct m5602_sensor {
/* Defines the name of a sensor */
char name[32];
/* What i2c address the sensor is connected to */
u8 i2c_slave_id;
/* Width of each i2c register (in bytes) */
u8 i2c_regW;
/* Probes if the sensor is connected */
int (*probe)(struct sd *sd);
/* Performs a initialization sequence */
int (*init)(struct sd *sd);
/* 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

View File

@@ -0,0 +1,540 @@
/*
* Mars-Semi MR97311A library
* Copyright (C) 2005 <bradlch@hotmail.com>
*
* V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define MODULE_NAME "mars"
#include "gspca.h"
#include "jpeg.h"
MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
MODULE_DESCRIPTION("GSPCA/Mars USB Camera Driver");
MODULE_LICENSE("GPL");
/* specific webcam descriptor */
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
u8 brightness;
u8 colors;
u8 gamma;
u8 sharpness;
u8 quality;
#define QUALITY_MIN 40
#define QUALITY_MAX 70
#define QUALITY_DEF 50
u8 *jpeg_hdr;
};
/* 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_setcolors(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
static int sd_setgamma(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getgamma(struct gspca_dev *gspca_dev, __s32 *val);
static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val);
static struct ctrl sd_ctrls[] = {
{
{
.id = V4L2_CID_BRIGHTNESS,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Brightness",
.minimum = 0,
.maximum = 30,
.step = 1,
#define BRIGHTNESS_DEF 15
.default_value = BRIGHTNESS_DEF,
},
.set = sd_setbrightness,
.get = sd_getbrightness,
},
{
{
.id = V4L2_CID_SATURATION,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Color",
.minimum = 1,
.maximum = 255,
.step = 1,
#define COLOR_DEF 200
.default_value = COLOR_DEF,
},
.set = sd_setcolors,
.get = sd_getcolors,
},
{
{
.id = V4L2_CID_GAMMA,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Gamma",
.minimum = 0,
.maximum = 3,
.step = 1,
#define GAMMA_DEF 1
.default_value = GAMMA_DEF,
},
.set = sd_setgamma,
.get = sd_getgamma,
},
{
{
.id = V4L2_CID_SHARPNESS,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Sharpness",
.minimum = 0,
.maximum = 2,
.step = 1,
#define SHARPNESS_DEF 1
.default_value = SHARPNESS_DEF,
},
.set = sd_setsharpness,
.get = sd_getsharpness,
},
};
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 int reg_w(struct gspca_dev *gspca_dev,
int len)
{
int alen, ret;
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)
PDEBUG(D_ERR, "reg write [%02x] error %d",
gspca_dev->usb_buf[0], ret);
return 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);
}
/* 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->brightness = BRIGHTNESS_DEF;
sd->colors = COLOR_DEF;
sd->gamma = GAMMA_DEF;
sd->sharpness = SHARPNESS_DEF;
sd->quality = QUALITY_DEF;
gspca_dev->nbalt = 9; /* use the altsetting 08 */
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;
int err_code;
u8 *data;
int i;
/* create the JPEG header */
sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL);
if (!sd->jpeg_hdr)
return -ENOMEM;
jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
0x21); /* JPEG 422 */
jpeg_set_qual(sd->jpeg_hdr, sd->quality);
data = gspca_dev->usb_buf;
data[0] = 0x01; /* address */
data[1] = 0x01;
err_code = reg_w(gspca_dev, 2);
if (err_code < 0)
return err_code;
/*
Initialize the MR97113 chip register
*/
data[0] = 0x00; /* address */
data[1] = 0x0c | 0x01; /* reg 0 */
data[2] = 0x01; /* reg 1 */
data[3] = gspca_dev->width / 8; /* h_size , reg 2 */
data[4] = gspca_dev->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] = 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;
err_code = reg_w(gspca_dev, 11);
if (err_code < 0)
return err_code;
data[0] = 0x23; /* address */
data[1] = 0x09; /* reg 35, append frame header */
err_code = reg_w(gspca_dev, 2);
if (err_code < 0)
return err_code;
data[0] = 0x3c; /* address */
/* if (gspca_dev->width == 1280) */
/* data[1] = 200; * reg 60, pc-cam frame size
* (unit: 4KB) 800KB */
/* else */
data[1] = 50; /* 50 reg 60, pc-cam frame size
* (unit: 4KB) 200KB */
err_code = reg_w(gspca_dev, 2);
if (err_code < 0)
return err_code;
/* 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] = sd->colors << 3;
data[3] = ((sd->colors >> 2) & 0xf8) | 0x04;
data[4] = sd->brightness; /* reg 0x61 = brightness */
data[5] = 0x00;
err_code = reg_w(gspca_dev, 6);
if (err_code < 0)
return err_code;
data[0] = 0x67;
/*jfm: from win trace*/
data[1] = sd->sharpness * 4 + 3;
data[2] = 0x14;
err_code = reg_w(gspca_dev, 3);
if (err_code < 0)
return err_code;
data[0] = 0x69;
data[1] = 0x2f;
data[2] = 0x28;
data[3] = 0x42;
err_code = reg_w(gspca_dev, 4);
if (err_code < 0)
return err_code;
data[0] = 0x63;
data[1] = 0x07;
err_code = reg_w(gspca_dev, 2);
/*jfm: win trace - many writes here to reg 0x64*/
if (err_code < 0)
return err_code;
/* 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 transfering enable... */
reg_w(gspca_dev, 2);
return 0;
}
static void sd_stopN(struct gspca_dev *gspca_dev)
{
int result;
gspca_dev->usb_buf[0] = 1;
gspca_dev->usb_buf[1] = 0;
result = reg_w(gspca_dev, 2);
if (result < 0)
PDEBUG(D_ERR, "Camera Stop failed");
}
static void sd_stop0(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
kfree(sd->jpeg_hdr);
}
static void sd_pkt_scan(struct gspca_dev *gspca_dev,
struct gspca_frame *frame, /* target */
__u8 *data, /* isoc packet */
int len) /* iso packet length */
{
struct sd *sd = (struct sd *) gspca_dev;
int p;
if (len < 6) {
/* gspca_dev->last_packet_type = DISCARD_PACKET; */
return;
}
for (p = 0; p < len - 6; p++) {
if (data[0 + p] == 0xff
&& data[1 + p] == 0xff
&& data[2 + p] == 0x00
&& data[3 + p] == 0xff
&& data[4 + p] == 0x96) {
if (data[5 + p] == 0x64
|| data[5 + p] == 0x65
|| data[5 + p] == 0x66
|| data[5 + p] == 0x67) {
PDEBUG(D_PACK, "sof offset: %d len: %d",
p, len);
frame = gspca_frame_add(gspca_dev, LAST_PACKET,
frame, data, p);
/* put the JPEG header */
gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
sd->jpeg_hdr, JPEG_HDR_SZ);
data += p + 16;
len -= p + 16;
break;
}
}
}
gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
}
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
sd->brightness = val;
if (gspca_dev->streaming) {
gspca_dev->usb_buf[0] = 0x61;
gspca_dev->usb_buf[1] = val;
reg_w(gspca_dev, 2);
}
return 0;
}
static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
*val = sd->brightness;
return 0;
}
static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
sd->colors = val;
if (gspca_dev->streaming) {
/* see sd_start */
gspca_dev->usb_buf[0] = 0x5f;
gspca_dev->usb_buf[1] = sd->colors << 3;
gspca_dev->usb_buf[2] = ((sd->colors >> 2) & 0xf8) | 0x04;
reg_w(gspca_dev, 3);
}
return 0;
}
static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
*val = sd->colors;
return 0;
}
static int sd_setgamma(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
sd->gamma = val;
if (gspca_dev->streaming) {
gspca_dev->usb_buf[0] = 0x06;
gspca_dev->usb_buf[1] = val * 0x40;
reg_w(gspca_dev, 2);
}
return 0;
}
static int sd_getgamma(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
*val = sd->gamma;
return 0;
}
static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
sd->sharpness = val;
if (gspca_dev->streaming) {
gspca_dev->usb_buf[0] = 0x67;
gspca_dev->usb_buf[1] = val * 4 + 3;
reg_w(gspca_dev, 2);
}
return 0;
}
static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
*val = sd->sharpness;
return 0;
}
static int sd_set_jcomp(struct gspca_dev *gspca_dev,
struct v4l2_jpegcompression *jcomp)
{
struct sd *sd = (struct sd *) gspca_dev;
if (jcomp->quality < QUALITY_MIN)
sd->quality = QUALITY_MIN;
else if (jcomp->quality > QUALITY_MAX)
sd->quality = QUALITY_MAX;
else
sd->quality = jcomp->quality;
if (gspca_dev->streaming)
jpeg_set_qual(sd->jpeg_hdr, sd->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 = sd->quality;
jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
| V4L2_JPEG_MARKER_DQT;
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,
.init = sd_init,
.start = sd_start,
.stopN = sd_stopN,
.stop0 = sd_stop0,
.pkt_scan = sd_pkt_scan,
.get_jcomp = sd_get_jcomp,
.set_jcomp = sd_set_jcomp,
};
/* -- module initialisation -- */
static const __devinitdata 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,
#endif
};
/* -- module insert / remove -- */
static int __init sd_mod_init(void)
{
int ret;
ret = usb_register(&sd_driver);
if (ret < 0)
return ret;
PDEBUG(D_PROBE, "registered");
return 0;
}
static void __exit sd_mod_exit(void)
{
usb_deregister(&sd_driver);
PDEBUG(D_PROBE, "deregistered");
}
module_init(sd_mod_init);
module_exit(sd_mod_exit);

View File

@@ -0,0 +1,991 @@
/*
* Mars MR97310A library
*
* 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,
*
* Copyright (C) 2009 Theodore Kilgore <kilgota@auburn.edu>
*
* Acknowledgements:
*
* The MR97311A support in gspca/mars.c has been helpful in understanding some
* of the registers in these cameras.
*
* Hans de Goede <hdgoede@redhat.com> and
* Thomas Kaiser <thomas@kaiser-linux.li>
* have assisted with their experience. Each of them has also helped by
* testing a previously unsupported camera.
*
* 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 "mr97310a"
#include "gspca.h"
#define CAM_TYPE_CIF 0
#define CAM_TYPE_VGA 1
#define MR97310A_BRIGHTNESS_MIN -254
#define MR97310A_BRIGHTNESS_MAX 255
#define MR97310A_BRIGHTNESS_DEFAULT 0
#define MR97310A_EXPOSURE_MIN 300
#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
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 */
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 */
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;
int brightness;
u16 exposure;
u8 gain;
};
struct sensor_w_data {
u8 reg;
u8 flags;
u8 data[16];
int len;
};
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val);
static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val);
static void setbrightness(struct gspca_dev *gspca_dev);
static void setexposure(struct gspca_dev *gspca_dev);
static void setgain(struct gspca_dev *gspca_dev);
/* V4L2 controls supported by the driver */
static struct ctrl sd_ctrls[] = {
{
#define BRIGHTNESS_IDX 0
{
.id = V4L2_CID_BRIGHTNESS,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Brightness",
.minimum = MR97310A_BRIGHTNESS_MIN,
.maximum = MR97310A_BRIGHTNESS_MAX,
.step = 1,
.default_value = MR97310A_BRIGHTNESS_DEFAULT,
.flags = 0,
},
.set = sd_setbrightness,
.get = sd_getbrightness,
},
{
#define EXPOSURE_IDX 1
{
.id = V4L2_CID_EXPOSURE,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Exposure",
.minimum = MR97310A_EXPOSURE_MIN,
.maximum = MR97310A_EXPOSURE_MAX,
.step = 1,
.default_value = MR97310A_EXPOSURE_DEFAULT,
.flags = 0,
},
.set = sd_setexposure,
.get = sd_getexposure,
},
{
#define GAIN_IDX 2
{
.id = V4L2_CID_GAIN,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Gain",
.minimum = MR97310A_GAIN_MIN,
.maximum = MR97310A_GAIN_MAX,
.step = 1,
.default_value = MR97310A_GAIN_DEFAULT,
.flags = 0,
},
.set = sd_setgain,
.get = sd_getgain,
},
};
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)
PDEBUG(D_ERR, "reg write [%02x] error %d",
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)
PDEBUG(D_ERR, "reg read [%02x] error %d",
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;
rc = sensor_write_reg(gspca_dev, reg, 0x01, &buf, 1);
if (rc < 0)
return rc;
buf = 0x01;
confirm_reg = sd->sensor_type ? 0x13 : 0x11;
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 *data = gspca_dev->usb_buf;
int err_code;
data[0] = 0x21;
err_code = mr_write(gspca_dev, 1);
if (err_code < 0)
return err_code;
err_code = mr_read(gspca_dev, 16);
return err_code;
}
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);
if (err_code < 0)
return err_code;
err_code = mr_write(gspca_dev, 1);
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);
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);
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);
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);
status = data[0];
tries++;
if (err_code < 0)
return err_code;
}
if (status != 0x0a)
PDEBUG(D_ERR, "status is %02x", status);
tries = 0;
while (tries < 4) {
data[0] = 0x19;
data[1] = 0x00;
err_code = mr_write(gspca_dev, 2);
if (err_code < 0)
return err_code;
err_code = cam_get_response16(gspca_dev);
status = data[0];
tries++;
if (err_code < 0)
return err_code;
}
data[0] = 0x19;
err_code = mr_write(gspca_dev, 1);
if (err_code < 0)
return err_code;
err_code = mr_read(gspca_dev, 16);
if (err_code < 0)
return err_code;
return 0;
}
static u8 get_sensor_id(struct gspca_dev *gspca_dev)
{
int err_code;
gspca_dev->usb_buf[0] = 0x1e;
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;
PDEBUG(D_PROBE, "Byte zero reported is %01x", gspca_dev->usb_buf[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 sd *sd = (struct sd *) gspca_dev;
struct cam *cam;
__u8 *data = gspca_dev->usb_buf;
int err_code;
cam = &gspca_dev->cam;
cam->cam_mode = vga_mode;
cam->nmodes = ARRAY_SIZE(vga_mode);
if (id->idProduct == 0x010e) {
sd->cam_type = CAM_TYPE_CIF;
cam->nmodes--;
data[0] = 0x01;
data[1] = 0x01;
err_code = mr_write(gspca_dev, 2);
if (err_code < 0)
return err_code;
msleep(200);
data[0] = get_sensor_id(gspca_dev);
/*
* Known CIF cameras. If you have another to report, please do
*
* Name byte just read sd->sensor_type
* reported by
* Sakar Spy-shot 0x28 T. Kilgore 0
* Innovage 0xf5 (unstable) T. Kilgore 0
* Vivitar Mini 0x53 H. De Goede 0
* Vivitar Mini 0x04 / 0x24 E. Rodriguez 0
* Vivitar Mini 0x08 T. Kilgore 1
* Elta-Media 8212dc 0x23 T. Kaiser 1
* Philips dig. keych. 0x37 T. Kilgore 1
*/
if ((data[0] & 0x78) == 8 ||
((data[0] & 0x2) == 0x2 && data[0] != 0x53))
sd->sensor_type = 1;
else
sd->sensor_type = 0;
PDEBUG(D_PROBE, "MR97310A CIF camera detected, sensor: %d",
sd->sensor_type);
if (force_sensor_type != -1) {
sd->sensor_type = !! force_sensor_type;
PDEBUG(D_PROBE, "Forcing sensor type to: %d",
sd->sensor_type);
}
if (sd->sensor_type == 0)
gspca_dev->ctrl_dis = (1 << BRIGHTNESS_IDX);
} else {
sd->cam_type = CAM_TYPE_VGA;
PDEBUG(D_PROBE, "MR97310A VGA camera detected");
gspca_dev->ctrl_dis = (1 << BRIGHTNESS_IDX) |
(1 << EXPOSURE_IDX) | (1 << GAIN_IDX);
}
sd->brightness = MR97310A_BRIGHTNESS_DEFAULT;
sd->exposure = MR97310A_EXPOSURE_DEFAULT;
sd->gain = MR97310A_GAIN_DEFAULT;
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;
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 */
data[0] = 0x01;
data[1] = 0x01;
err_code = mr_write(gspca_dev, 2);
if (err_code < 0)
return err_code;
memcpy(data, startup_string, 11);
if (sd->sensor_type)
data[5] = 0xbb;
switch (gspca_dev->width) {
case 160:
data[9] |= 0x04; /* reg 8, 2:1 scale down from 320 */
/* fall thru */
case 320:
default:
data[3] = 0x28; /* reg 2, H size/8 */
data[4] = 0x3c; /* reg 3, V size/4 */
data[6] = 0x14; /* reg 5, H start */
data[8] = 0x1a + sd->sensor_type; /* reg 7, V start */
break;
case 176:
data[9] |= 0x04; /* reg 8, 2:1 scale down from 352 */
/* fall thru */
case 352:
data[3] = 0x2c; /* reg 2, H size/8 */
data[4] = 0x48; /* reg 3, V size/4 */
data[6] = 0x06; /* reg 5, H start */
data[8] = 0x06 - sd->sensor_type; /* reg 7, V start */
break;
}
err_code = mr_write(gspca_dev, 11);
if (err_code < 0)
return err_code;
if (!sd->sensor_type) {
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 */
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));
}
if (err_code < 0)
return err_code;
setbrightness(gspca_dev);
setexposure(gspca_dev);
setgain(gspca_dev);
msleep(200);
data[0] = 0x00;
data[1] = 0x4d; /* ISOC transfering enable... */
err_code = mr_write(gspca_dev, 2);
if (err_code < 0)
return err_code;
return 0;
}
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;
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 */
sd->sof_read = 0;
/*
* We have to know which camera we have, because the register writes
* depend upon the camera. This test, run before we actually enter
* the initialization routine, distinguishes most of the cameras, If
* needed, another routine is done later, too.
*/
memset(data, 0, 16);
data[0] = 0x20;
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;
PDEBUG(D_PROBE, "Byte reported is %02x", data[0]);
msleep(200);
/*
* Known VGA cameras. If you have another to report, please do
*
* Name byte just read sd->sensor_type
* sd->do_lcd_stop
* Aiptek Pencam VGA+ 0x31 0 1
* ION digital 0x31 0 1
* Argus DC-1620 0x30 1 0
* Argus QuickClix 0x30 1 1 (not caught here)
*/
sd->sensor_type = data[0] & 1;
sd->do_lcd_stop = (~data[0]) & 1;
/* Streaming setup begins here. */
data[0] = 0x01;
data[1] = 0x01;
err_code = mr_write(gspca_dev, 2);
if (err_code < 0)
return err_code;
/*
* A second test can now resolve any remaining ambiguity in the
* identification of the camera type,
*/
if (!sd->sensor_type) {
data[0] = get_sensor_id(gspca_dev);
if (data[0] == 0x7f) {
sd->sensor_type = 1;
PDEBUG(D_PROBE, "sensor_type corrected to 1");
}
msleep(200);
}
if (force_sensor_type != -1) {
sd->sensor_type = !! force_sensor_type;
PDEBUG(D_PROBE, "Forcing sensor type to: %d",
sd->sensor_type);
}
/*
* Known VGA cameras.
* This test is only run if the previous test returned 0x30, but
* here is the information for all others, too, just for reference.
*
* Name byte just read sd->sensor_type
*
* Aiptek Pencam VGA+ 0xfb (this test not run) 1
* ION digital 0xbd (this test not run) 1
* Argus DC-1620 0xe5 (no change) 0
* Argus QuickClix 0x7f (reclassified) 1
*/
memcpy(data, startup_string, 11);
if (!sd->sensor_type) {
data[5] = 0x00;
data[10] = 0x91;
}
switch (gspca_dev->width) {
case 160:
data[9] |= 0x0c; /* reg 8, 4:1 scale down */
/* fall thru */
case 320:
data[9] |= 0x04; /* reg 8, 2:1 scale down */
/* fall thru */
case 640:
default:
data[3] = 0x50; /* reg 2, H size/8 */
data[4] = 0x78; /* reg 3, V size/4 */
data[6] = 0x04; /* reg 5, H start */
data[8] = 0x03; /* reg 7, V start */
if (sd->do_lcd_stop)
data[8] = 0x04; /* Bayer tile shifted */
break;
case 176:
data[9] |= 0x04; /* reg 8, 2:1 scale down */
/* fall thru */
case 352:
data[3] = 0x2c; /* reg 2, H size */
data[4] = 0x48; /* reg 3, V size */
data[6] = 0x94; /* reg 5, H start */
data[8] = 0x63; /* reg 7, V start */
if (sd->do_lcd_stop)
data[8] = 0x64; /* Bayer tile shifted */
break;
}
err_code = mr_write(gspca_dev, 11);
if (err_code < 0)
return err_code;
if (!sd->sensor_type) {
/* The only known sensor_type 0 cam is the Argus DC-1620 */
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 { /* sd->sensor_type = 1 */
const struct sensor_w_data vga_sensor1_init_data[] = {
{0x02, 0x00, {0x06, 0x59, 0x0c, 0x16, 0x00,
0x07, 0x00, 0x01}, 8},
{0x11, 0x04, {0x01}, 1},
/*{0x0a, 0x00, {0x00, 0x01, 0x00, 0x00, 0x01, */
{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}
};
err_code = sensor_write_regs(gspca_dev, vga_sensor1_init_data,
ARRAY_SIZE(vga_sensor1_init_data));
}
if (err_code < 0)
return err_code;
msleep(200);
data[0] = 0x00;
data[1] = 0x4d; /* ISOC transfering enable... */
err_code = mr_write(gspca_dev, 2);
return err_code;
}
static int sd_start(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
int err_code;
struct cam *cam;
cam = &gspca_dev->cam;
sd->sof_read = 0;
/*
* Some of the supported cameras require the memory pointer to be
* set to 0, or else they will not stream.
*/
zero_the_pointer(gspca_dev);
msleep(200);
if (sd->cam_type == CAM_TYPE_CIF) {
err_code = start_cif_cam(gspca_dev);
} else {
err_code = start_vga_cam(gspca_dev);
}
return err_code;
}
static void sd_stopN(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
int result;
gspca_dev->usb_buf[0] = 1;
gspca_dev->usb_buf[1] = 0;
result = mr_write(gspca_dev, 2);
if (result < 0)
PDEBUG(D_ERR, "Camera Stop failed");
/* Not all the cams need this, but even if not, probably a good idea */
zero_the_pointer(gspca_dev);
if (sd->do_lcd_stop) {
gspca_dev->usb_buf[0] = 0x19;
gspca_dev->usb_buf[1] = 0x54;
result = mr_write(gspca_dev, 2);
if (result < 0)
PDEBUG(D_ERR, "Camera Stop failed");
}
}
static void setbrightness(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
u8 val;
if (gspca_dev->ctrl_dis & (1 << BRIGHTNESS_IDX))
return;
/* Note register 7 is also seen as 0x8x or 0xCx in dumps */
if (sd->brightness > 0) {
sensor_write1(gspca_dev, 7, 0x00);
val = sd->brightness;
} else {
sensor_write1(gspca_dev, 7, 0x01);
val = 257 - sd->brightness;
}
sensor_write1(gspca_dev, 8, val);
}
static void setexposure(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
u8 val;
if (gspca_dev->ctrl_dis & (1 << EXPOSURE_IDX))
return;
if (sd->sensor_type) {
val = sd->exposure >> 4;
sensor_write1(gspca_dev, 3, val);
val = sd->exposure & 0xf;
sensor_write1(gspca_dev, 4, val);
} else {
u8 clockdiv;
int exposure;
/* 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 calculayte the exposure
register setting (which goes from 0 - 511).
Note our 0 - 4095 exposure is mapped to 0 - 511
milliseconds exposure time */
clockdiv = (60 * sd->exposure + 7999) / 8000;
/* Limit framerate to not exceed usb bandwidth */
if (clockdiv < 3 && gspca_dev->width >= 320)
clockdiv = 3;
else if (clockdiv < 2)
clockdiv = 2;
/* Frame exposure time in ms = 1000 * clockdiv / 60 ->
exposure = (sd->exposure / 8) * 511 / (1000 * clockdiv / 60) */
exposure = (60 * 511 * sd->exposure) / (8000 * clockdiv);
if (exposure > 511)
exposure = 511;
/* exposure register value is reversed! */
exposure = 511 - exposure;
sensor_write1(gspca_dev, 0x02, clockdiv);
sensor_write1(gspca_dev, 0x0e, exposure & 0xff);
sensor_write1(gspca_dev, 0x0f, exposure >> 8);
}
}
static void setgain(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
if (gspca_dev->ctrl_dis & (1 << GAIN_IDX))
return;
if (sd->sensor_type) {
sensor_write1(gspca_dev, 0x0e, sd->gain);
} else {
sensor_write1(gspca_dev, 0x10, sd->gain);
}
}
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
sd->brightness = val;
if (gspca_dev->streaming)
setbrightness(gspca_dev);
return 0;
}
static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
*val = sd->brightness;
return 0;
}
static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
sd->exposure = val;
if (gspca_dev->streaming)
setexposure(gspca_dev);
return 0;
}
static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
*val = sd->exposure;
return 0;
}
static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
sd->gain = val;
if (gspca_dev->streaming)
setgain(gspca_dev);
return 0;
}
static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
*val = sd->gain;
return 0;
}
/* Include pac common sof detection functions */
#include "pac_common.h"
static void sd_pkt_scan(struct gspca_dev *gspca_dev,
struct gspca_frame *frame, /* target */
__u8 *data, /* isoc packet */
int len) /* iso packet length */
{
unsigned char *sof;
sof = pac_find_sof(gspca_dev, 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;
frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
data, n);
/* Start next frame. */
gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
pac_sof_marker, sizeof pac_sof_marker);
len -= sof - data;
data = sof;
}
gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
}
/* sub-driver description */
static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
.ctrls = sd_ctrls,
.nctrls = ARRAY_SIZE(sd_ctrls),
.config = sd_config,
.init = sd_init,
.start = sd_start,
.stopN = sd_stopN,
.pkt_scan = sd_pkt_scan,
};
/* -- module initialisation -- */
static const __devinitdata struct usb_device_id device_table[] = {
{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,
#endif
};
/* -- module insert / remove -- */
static int __init sd_mod_init(void)
{
int ret;
ret = usb_register(&sd_driver);
if (ret < 0)
return ret;
PDEBUG(D_PROBE, "registered");
return 0;
}
static void __exit sd_mod_exit(void)
{
usb_deregister(&sd_driver);
PDEBUG(D_PROBE, "deregistered");
}
module_init(sd_mod_init);
module_exit(sd_mod_exit);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,567 @@
/*
* Pixart PAC207BCA library
*
* Copyright (C) 2008 Hans de Goede <hdgoede@redhat.com>
* Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li
* Copyleft (C) 2005 Michel Xhaard mxhaard@magic.fr
*
* V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#define MODULE_NAME "pac207"
#include "gspca.h"
MODULE_AUTHOR("Hans de Goede <hdgoede@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_EXPOSURE_MIN 3
#define PAC207_EXPOSURE_MAX 26
#define PAC207_EXPOSURE_DEFAULT 5 /* power on default: 3 */
#define PAC207_EXPOSURE_KNEE 8 /* 4 = 30 fps, 11 = 8, 15 = 6 */
#define PAC207_GAIN_MIN 0
#define PAC207_GAIN_MAX 31
#define PAC207_GAIN_DEFAULT 9 /* power on default: 9 */
#define PAC207_GAIN_KNEE 31
#define PAC207_AUTOGAIN_DEADZONE 30
/* specific webcam descriptor */
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
u8 mode;
u8 brightness;
u8 exposure;
u8 autogain;
u8 gain;
u8 sof_read;
u8 header_read;
u8 autogain_ignore_frames;
atomic_t avg_lum;
};
/* V4L2 controls supported by the driver */
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val);
static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val);
static struct ctrl sd_ctrls[] = {
#define SD_BRIGHTNESS 0
{
{
.id = V4L2_CID_BRIGHTNESS,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Brightness",
.minimum = PAC207_BRIGHTNESS_MIN,
.maximum = PAC207_BRIGHTNESS_MAX,
.step = 1,
.default_value = PAC207_BRIGHTNESS_DEFAULT,
.flags = 0,
},
.set = sd_setbrightness,
.get = sd_getbrightness,
},
#define SD_EXPOSURE 1
{
{
.id = V4L2_CID_EXPOSURE,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "exposure",
.minimum = PAC207_EXPOSURE_MIN,
.maximum = PAC207_EXPOSURE_MAX,
.step = 1,
.default_value = PAC207_EXPOSURE_DEFAULT,
.flags = 0,
},
.set = sd_setexposure,
.get = sd_getexposure,
},
#define SD_AUTOGAIN 2
{
{
.id = V4L2_CID_AUTOGAIN,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.name = "Auto Gain",
.minimum = 0,
.maximum = 1,
.step = 1,
#define AUTOGAIN_DEF 1
.default_value = AUTOGAIN_DEF,
.flags = 0,
},
.set = sd_setautogain,
.get = sd_getautogain,
},
#define SD_GAIN 3
{
{
.id = V4L2_CID_GAIN,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "gain",
.minimum = PAC207_GAIN_MIN,
.maximum = PAC207_GAIN_MAX,
.step = 1,
.default_value = PAC207_GAIN_DEFAULT,
.flags = 0,
},
.set = sd_setgain,
.get = sd_getgain,
},
};
static 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 int pac207_write_regs(struct gspca_dev *gspca_dev, u16 index,
const u8 *buffer, u16 length)
{
struct usb_device *udev = gspca_dev->dev;
int err;
memcpy(gspca_dev->usb_buf, buffer, length);
err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x01,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
0x00, index,
gspca_dev->usb_buf, length, PAC207_CTRL_TIMEOUT);
if (err < 0)
PDEBUG(D_ERR,
"Failed to write registers to index 0x%04X, error %d)",
index, err);
return err;
}
static int pac207_write_reg(struct gspca_dev *gspca_dev, u16 index, u16 value)
{
struct usb_device *udev = gspca_dev->dev;
int err;
err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
value, index, NULL, 0, PAC207_CTRL_TIMEOUT);
if (err)
PDEBUG(D_ERR, "Failed to write a register (index 0x%04X,"
" value 0x%02X, error %d)", index, value, err);
return err;
}
static int pac207_read_reg(struct gspca_dev *gspca_dev, u16 index)
{
struct usb_device *udev = gspca_dev->dev;
int res;
res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
0x00, index,
gspca_dev->usb_buf, 1, PAC207_CTRL_TIMEOUT);
if (res < 0) {
PDEBUG(D_ERR,
"Failed to read a register (index 0x%04X, error %d)",
index, res);
return res;
}
return gspca_dev->usb_buf[0];
}
/* this function is called at probe time */
static int sd_config(struct gspca_dev *gspca_dev,
const struct usb_device_id *id)
{
struct sd *sd = (struct sd *) gspca_dev;
struct cam *cam;
u8 idreg[2];
idreg[0] = pac207_read_reg(gspca_dev, 0x0000);
idreg[1] = pac207_read_reg(gspca_dev, 0x0001);
idreg[0] = ((idreg[0] & 0x0F) << 4) | ((idreg[1] & 0xf0) >> 4);
idreg[1] = idreg[1] & 0x0f;
PDEBUG(D_PROBE, "Pixart Sensor ID 0x%02X Chips ID 0x%02X",
idreg[0], idreg[1]);
if (idreg[0] != 0x27) {
PDEBUG(D_PROBE, "Error invalid sensor ID!");
return -ENODEV;
}
PDEBUG(D_PROBE,
"Pixart PAC207BCA Image Processor and Control Chip detected"
" (vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct);
cam = &gspca_dev->cam;
cam->cam_mode = sif_mode;
cam->nmodes = ARRAY_SIZE(sif_mode);
sd->brightness = PAC207_BRIGHTNESS_DEFAULT;
sd->exposure = PAC207_EXPOSURE_DEFAULT;
sd->gain = PAC207_GAIN_DEFAULT;
sd->autogain = AUTOGAIN_DEF;
return 0;
}
/* this function is called at probe and resume time */
static int sd_init(struct gspca_dev *gspca_dev)
{
pac207_write_reg(gspca_dev, 0x41, 0x00);
/* Bit_0=Image Format,
* Bit_1=LED,
* Bit_2=Compression test mode enable */
pac207_write_reg(gspca_dev, 0x0f, 0x00); /* Power Control */
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->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, sd->brightness);
/* PGA global gain (Bit 4-0) */
pac207_write_reg(gspca_dev, 0x0e, sd->gain);
pac207_write_reg(gspca_dev, 0x02, sd->exposure); /* PXCK = 12MHz /n */
mode = 0x02; /* Image Format (Bit 0), LED (1), Compr. test mode (2) */
if (gspca_dev->width == 176) { /* 176x144 */
mode |= 0x01;
PDEBUG(D_STREAM, "pac207_start mode 176x144");
} else { /* 352x288 */
PDEBUG(D_STREAM, "pac207_start mode 352x288");
}
pac207_write_reg(gspca_dev, 0x41, mode);
pac207_write_reg(gspca_dev, 0x13, 0x01); /* Bit 0, auto clear */
pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */
msleep(10);
pac207_write_reg(gspca_dev, 0x40, 0x01); /* Start ISO pipe */
sd->sof_read = 0;
sd->autogain_ignore_frames = 0;
atomic_set(&sd->avg_lum, -1);
return 0;
}
static void sd_stopN(struct gspca_dev *gspca_dev)
{
pac207_write_reg(gspca_dev, 0x40, 0x00); /* Stop ISO pipe */
pac207_write_reg(gspca_dev, 0x41, 0x00); /* Turn of LED */
pac207_write_reg(gspca_dev, 0x0f, 0x00); /* Power Control */
}
/* Include pac common sof detection functions */
#include "pac_common.h"
static void pac207_do_auto_gain(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
int avg_lum = atomic_read(&sd->avg_lum);
if (avg_lum == -1)
return;
if (sd->autogain_ignore_frames > 0)
sd->autogain_ignore_frames--;
else if (gspca_auto_gain_n_exposure(gspca_dev, avg_lum,
100, PAC207_AUTOGAIN_DEADZONE,
PAC207_GAIN_KNEE, PAC207_EXPOSURE_KNEE))
sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES;
}
static void sd_pkt_scan(struct gspca_dev *gspca_dev,
struct gspca_frame *frame,
__u8 *data,
int len)
{
struct sd *sd = (struct sd *) gspca_dev;
unsigned char *sof;
sof = pac_find_sof(gspca_dev, 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;
frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
data, n);
sd->header_read = 0;
gspca_frame_add(gspca_dev, FIRST_PACKET, frame, NULL, 0);
len -= sof - data;
data = sof;
}
if (sd->header_read < 11) {
int needed;
/* get average lumination from frame header (byte 5) */
if (sd->header_read < 5) {
needed = 5 - sd->header_read;
if (len >= needed)
atomic_set(&sd->avg_lum, data[needed - 1]);
}
/* skip the rest of the header */
needed = 11 - sd->header_read;
if (len <= needed) {
sd->header_read += len;
return;
}
data += needed;
len -= needed;
sd->header_read = 11;
}
gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
}
static void setbrightness(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
pac207_write_reg(gspca_dev, 0x08, sd->brightness);
pac207_write_reg(gspca_dev, 0x13, 0x01); /* Bit 0, auto clear */
pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */
}
static void setexposure(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
pac207_write_reg(gspca_dev, 0x02, sd->exposure);
pac207_write_reg(gspca_dev, 0x13, 0x01); /* Bit 0, auto clear */
pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */
}
static void setgain(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
pac207_write_reg(gspca_dev, 0x0e, sd->gain);
pac207_write_reg(gspca_dev, 0x13, 0x01); /* Bit 0, auto clear */
pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */
}
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
sd->brightness = val;
if (gspca_dev->streaming)
setbrightness(gspca_dev);
return 0;
}
static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
*val = sd->brightness;
return 0;
}
static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
sd->exposure = val;
if (gspca_dev->streaming)
setexposure(gspca_dev);
return 0;
}
static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
*val = sd->exposure;
return 0;
}
static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
sd->gain = val;
if (gspca_dev->streaming)
setgain(gspca_dev);
return 0;
}
static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
*val = sd->gain;
return 0;
}
static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
sd->autogain = val;
/* when switching to autogain set defaults to make sure
we are on a valid point of the autogain gain /
exposure knee graph, and give this change time to
take effect before doing autogain. */
if (sd->autogain) {
sd->exposure = PAC207_EXPOSURE_DEFAULT;
sd->gain = PAC207_GAIN_DEFAULT;
if (gspca_dev->streaming) {
sd->autogain_ignore_frames =
PAC_AUTOGAIN_IGNORE_FRAMES;
setexposure(gspca_dev);
setgain(gspca_dev);
}
}
return 0;
}
static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
*val = sd->autogain;
return 0;
}
/* sub-driver description */
static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
.ctrls = sd_ctrls,
.nctrls = ARRAY_SIZE(sd_ctrls),
.config = sd_config,
.init = sd_init,
.start = sd_start,
.stopN = sd_stopN,
.dq_callback = pac207_do_auto_gain,
.pkt_scan = sd_pkt_scan,
};
/* -- module initialisation -- */
static const __devinitdata 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,
#endif
};
/* -- module insert / remove -- */
static int __init sd_mod_init(void)
{
int ret;
ret = usb_register(&sd_driver);
if (ret < 0)
return ret;
PDEBUG(D_PROBE, "registered");
return 0;
}
static void __exit sd_mod_exit(void)
{
usb_deregister(&sd_driver);
PDEBUG(D_PROBE, "deregistered");
}
module_init(sd_mod_init);
module_exit(sd_mod_exit);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,60 @@
/*
* Pixart PAC207BCA / PAC73xx common functions
*
* Copyright (C) 2008 Hans de Goede <j.w.r.degoede@hhs.nl>
* Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li
* Copyleft (C) 2005 Michel Xhaard mxhaard@magic.fr
*
* V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
/* We calculate the autogain at the end of the transfer of a frame, at this
moment a frame with the old settings is being transmitted, and a frame is
being captured with the old settings. So if we adjust the autogain we must
ignore atleast the 2 next frames for the new settings to come into effect
before doing any other adjustments */
#define PAC_AUTOGAIN_IGNORE_FRAMES 3
static const unsigned char pac_sof_marker[5] =
{ 0xff, 0xff, 0x00, 0xff, 0x96 };
static unsigned char *pac_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] == pac_sof_marker[sd->sof_read]) {
sd->sof_read++;
if (sd->sof_read == sizeof(pac_sof_marker)) {
PDEBUG(D_FRAM,
"SOF found, bytes to analyze: %u."
" Frame starts at byte #%u",
len, i + 1);
sd->sof_read = 0;
return m + i + 1;
}
} else {
sd->sof_read = 0;
}
}
return NULL;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,843 @@
/*
* SPCA505 chip based cameras initialization data
*
* V4L2 by Jean-Francis Moine <http://moinejf.free.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#define MODULE_NAME "spca505"
#include "gspca.h"
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 brightness;
u8 subtype;
#define IntelPCCameraPro 0
#define Nxultra 1
};
/* V4L2 controls supported by the driver */
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
static struct ctrl sd_ctrls[] = {
{
{
.id = V4L2_CID_BRIGHTNESS,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Brightness",
.minimum = 0,
.maximum = 255,
.step = 1,
#define BRIGHTNESS_DEF 127
.default_value = BRIGHTNESS_DEF,
},
.set = sd_setbrightness,
.get = sd_getbrightness,
},
};
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},
{0x00, 0x01, 0x00},
{0x00, 0x01, 0x01},
{0x00, 0x01, 0x34},
{0x00, 0x01, 0x35},
{0x06, 0x18, 0x08},
{0x06, 0xfc, 0x09},
{0x06, 0xfc, 0x0a},
{0x06, 0xfc, 0x0b},
{0x06, 0x18, 0x0c},
{0x06, 0xfc, 0x0d},
{0x06, 0xfc, 0x0e},
{0x06, 0xfc, 0x0f},
{0x06, 0x18, 0x10},
{0x06, 0xfe, 0x12},
{0x06, 0x00, 0x11},
{0x06, 0x00, 0x14},
{0x06, 0x00, 0x13},
{0x06, 0x28, 0x51},
{0x06, 0xff, 0x53},
{0x02, 0x00, 0x08},
{0x03, 0x00, 0x03},
{0x03, 0x10, 0x03},
{}
};
/*
* Data to initialize the camera using the internal CCD
*/
static const 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 usb_device *dev,
u16 req, u16 index, u16 value)
{
int ret;
ret = usb_control_msg(dev,
usb_sndctrlpipe(dev, 0),
req,
USB_TYPE_VENDOR | USB_RECIP_DEVICE,
value, index, NULL, 0, 500);
PDEBUG(D_USBO, "reg write: 0x%02x,0x%02x:0x%02x, %d",
req, index, value, ret);
if (ret < 0)
PDEBUG(D_ERR, "reg write: error %d", ret);
return ret;
}
/* returns: negative is error, pos or zero is data */
static int reg_read(struct gspca_dev *gspca_dev,
u16 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])
{
struct usb_device *dev = gspca_dev->dev;
int ret, i = 0;
while (data[i][0] != 0) {
ret = reg_write(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;
sd->brightness = BRIGHTNESS_DEF;
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)
{
struct sd *sd = (struct sd *) gspca_dev;
u8 brightness = sd->brightness;
reg_write(gspca_dev->dev, 0x05, 0x00, (255 - brightness) >> 6);
reg_write(gspca_dev->dev, 0x05, 0x01, (255 - brightness) << 2);
}
static int sd_start(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
struct usb_device *dev = gspca_dev->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) {
PDEBUG(D_ERR|D_CONF,
"register read failed err: %d",
ret);
return ret;
}
if (ret != 0x0101) {
PDEBUG(D_ERR|D_CONF,
"After vector read returns 0x%04x should be 0x0101",
ret);
}
ret = reg_write(gspca_dev->dev, 0x06, 0x16, 0x0a);
if (ret < 0)
return ret;
reg_write(gspca_dev->dev, 0x05, 0xc2, 0x12);
/* necessary because without it we can see stream
* only once after loading module */
/* stopping usb registers Tomasz change */
reg_write(dev, 0x02, 0x00, 0x00);
mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;
reg_write(dev, SPCA50X_REG_COMPRESS, 0x00, mode_tb[mode][0]);
reg_write(dev, SPCA50X_REG_COMPRESS, 0x06, mode_tb[mode][1]);
reg_write(dev, SPCA50X_REG_COMPRESS, 0x07, mode_tb[mode][2]);
ret = reg_write(dev, SPCA50X_REG_USB,
SPCA50X_USB_CTRL,
SPCA50X_CUSB_ENABLE);
setbrightness(gspca_dev);
return ret;
}
static void sd_stopN(struct gspca_dev *gspca_dev)
{
/* Disable ISO packet machine */
reg_write(gspca_dev->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->dev, 0x03, 0x03, 0x20);
reg_write(gspca_dev->dev, 0x03, 0x01, 0x00);
reg_write(gspca_dev->dev, 0x03, 0x00, 0x01);
reg_write(gspca_dev->dev, 0x05, 0x10, 0x01);
reg_write(gspca_dev->dev, 0x05, 0x11, 0x0f);
}
static void sd_pkt_scan(struct gspca_dev *gspca_dev,
struct gspca_frame *frame, /* target */
u8 *data, /* isoc packet */
int len) /* iso packet length */
{
switch (data[0]) {
case 0: /* start of frame */
frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
data, 0);
data += SPCA50X_OFFSET_DATA;
len -= SPCA50X_OFFSET_DATA;
gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
data, len);
break;
case 0xff: /* drop */
break;
default:
data += 1;
len -= 1;
gspca_frame_add(gspca_dev, INTER_PACKET, frame,
data, len);
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;
*val = sd->brightness;
return 0;
}
/* sub-driver description */
static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
.ctrls = sd_ctrls,
.nctrls = ARRAY_SIZE(sd_ctrls),
.config = sd_config,
.init = sd_init,
.start = sd_start,
.stopN = sd_stopN,
.stop0 = sd_stop0,
.pkt_scan = sd_pkt_scan,
};
/* -- module initialisation -- */
static const __devinitdata 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,
#endif
};
/* -- module insert / remove -- */
static int __init sd_mod_init(void)
{
int ret;
ret = usb_register(&sd_driver);
if (ret < 0)
return ret;
PDEBUG(D_PROBE, "registered");
return 0;
}
static void __exit sd_mod_exit(void)
{
usb_deregister(&sd_driver);
PDEBUG(D_PROBE, "deregistered");
}
module_init(sd_mod_init);
module_exit(sd_mod_exit);

View File

@@ -0,0 +1,738 @@
/*
* SPCA506 chip based cameras function
* M Xhaard 15/04/2004 based on different work Mark Taylor and others
* and my own snoopy file on a pv-321c donate by a german compagny
* "Firma Frank Gmbh" from Saarbruecken
*
* V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define MODULE_NAME "spca506"
#include "gspca.h"
MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
MODULE_DESCRIPTION("GSPCA/SPCA506 USB Camera Driver");
MODULE_LICENSE("GPL");
/* specific webcam descriptor */
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
unsigned char brightness;
unsigned char contrast;
unsigned char colors;
unsigned char hue;
char norme;
char channel;
};
/* V4L2 controls supported by the driver */
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
static int sd_sethue(struct gspca_dev *gspca_dev, __s32 val);
static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val);
static struct ctrl sd_ctrls[] = {
#define SD_BRIGHTNESS 0
{
{
.id = V4L2_CID_BRIGHTNESS,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Brightness",
.minimum = 0,
.maximum = 0xff,
.step = 1,
.default_value = 0x80,
},
.set = sd_setbrightness,
.get = sd_getbrightness,
},
#define SD_CONTRAST 1
{
{
.id = V4L2_CID_CONTRAST,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Contrast",
.minimum = 0,
.maximum = 0xff,
.step = 1,
.default_value = 0x47,
},
.set = sd_setcontrast,
.get = sd_getcontrast,
},
#define SD_COLOR 2
{
{
.id = V4L2_CID_SATURATION,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Saturation",
.minimum = 0,
.maximum = 0xff,
.step = 1,
.default_value = 0x40,
},
.set = sd_setcolors,
.get = sd_getcolors,
},
#define SD_HUE 3
{
{
.id = V4L2_CID_HUE,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Hue",
.minimum = 0,
.maximum = 0xff,
.step = 1,
.default_value = 0,
},
.set = sd_sethue,
.get = sd_gethue,
},
};
static const struct v4l2_pix_format vga_mode[] = {
{160, 120, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE,
.bytesperline = 160,
.sizeimage = 160 * 120 * 3 / 2,
.colorspace = V4L2_COLORSPACE_SRGB,
.priv = 5},
{176, 144, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE,
.bytesperline = 176,
.sizeimage = 176 * 144 * 3 / 2,
.colorspace = V4L2_COLORSPACE_SRGB,
.priv = 4},
{320, 240, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE,
.bytesperline = 320,
.sizeimage = 320 * 240 * 3 / 2,
.colorspace = V4L2_COLORSPACE_SRGB,
.priv = 2},
{352, 288, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE,
.bytesperline = 352,
.sizeimage = 352 * 288 * 3 / 2,
.colorspace = V4L2_COLORSPACE_SRGB,
.priv = 1},
{640, 480, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE,
.bytesperline = 640,
.sizeimage = 640 * 480 * 3 / 2,
.colorspace = V4L2_COLORSPACE_SRGB,
.priv = 0},
};
#define SPCA50X_OFFSET_DATA 10
#define SAA7113_bright 0x0a /* defaults 0x80 */
#define SAA7113_contrast 0x0b /* defaults 0x47 */
#define SAA7113_saturation 0x0c /* defaults 0x40 */
#define SAA7113_hue 0x0d /* defaults 0x00 */
#define SAA7113_I2C_BASE_WRITE 0x4a
/* read 'len' bytes to gspca_dev->usb_buf */
static void reg_r(struct gspca_dev *gspca_dev,
__u16 req,
__u16 index,
__u16 length)
{
usb_control_msg(gspca_dev->dev,
usb_rcvctrlpipe(gspca_dev->dev, 0),
req,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0, /* value */
index, gspca_dev->usb_buf, length,
500);
}
static void reg_w(struct usb_device *dev,
__u16 req,
__u16 value,
__u16 index)
{
usb_control_msg(dev,
usb_sndctrlpipe(dev, 0),
req,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
value, index,
NULL, 0, 500);
}
static void spca506_Initi2c(struct gspca_dev *gspca_dev)
{
reg_w(gspca_dev->dev, 0x07, SAA7113_I2C_BASE_WRITE, 0x0004);
}
static void spca506_WriteI2c(struct gspca_dev *gspca_dev, __u16 valeur,
__u16 reg)
{
int retry = 60;
reg_w(gspca_dev->dev, 0x07, reg, 0x0001);
reg_w(gspca_dev->dev, 0x07, valeur, 0x0000);
while (retry--) {
reg_r(gspca_dev, 0x07, 0x0003, 2);
if ((gspca_dev->usb_buf[0] | gspca_dev->usb_buf[1]) == 0x00)
break;
}
}
static void spca506_SetNormeInput(struct gspca_dev *gspca_dev,
__u16 norme,
__u16 channel)
{
struct sd *sd = (struct sd *) gspca_dev;
/* fixme: check if channel == 0..3 and 6..9 (8 values) */
__u8 setbit0 = 0x00;
__u8 setbit1 = 0x00;
__u8 videomask = 0x00;
PDEBUG(D_STREAM, "** Open Set Norme **");
spca506_Initi2c(gspca_dev);
/* NTSC bit0 -> 1(525 l) PAL SECAM bit0 -> 0 (625 l) */
/* Composite channel bit1 -> 1 S-video bit 1 -> 0 */
/* and exclude SAA7113 reserved channel set default 0 otherwise */
if (norme & V4L2_STD_NTSC)
setbit0 = 0x01;
if (channel == 4 || channel == 5 || channel > 9)
channel = 0;
if (channel < 4)
setbit1 = 0x02;
videomask = (0x48 | setbit0 | setbit1);
reg_w(gspca_dev->dev, 0x08, videomask, 0x0000);
spca506_WriteI2c(gspca_dev, (0xc0 | (channel & 0x0F)), 0x02);
if (norme & V4L2_STD_NTSC)
spca506_WriteI2c(gspca_dev, 0x33, 0x0e);
/* Chrominance Control NTSC N */
else if (norme & V4L2_STD_SECAM)
spca506_WriteI2c(gspca_dev, 0x53, 0x0e);
/* Chrominance Control SECAM */
else
spca506_WriteI2c(gspca_dev, 0x03, 0x0e);
/* Chrominance Control PAL BGHIV */
sd->norme = norme;
sd->channel = channel;
PDEBUG(D_STREAM, "Set Video Byte to 0x%2x", videomask);
PDEBUG(D_STREAM, "Set Norme: %08x Channel %d", norme, channel);
}
static void spca506_GetNormeInput(struct gspca_dev *gspca_dev,
__u16 *norme, __u16 *channel)
{
struct sd *sd = (struct sd *) gspca_dev;
/* Read the register is not so good value change so
we use your own copy in spca50x struct */
*norme = sd->norme;
*channel = sd->channel;
PDEBUG(D_STREAM, "Get Norme: %d Channel %d", *norme, *channel);
}
static void spca506_Setsize(struct gspca_dev *gspca_dev, __u16 code,
__u16 xmult, __u16 ymult)
{
struct usb_device *dev = gspca_dev->dev;
PDEBUG(D_STREAM, "** SetSize **");
reg_w(dev, 0x04, (0x18 | (code & 0x07)), 0x0000);
/* Soft snap 0x40 Hard 0x41 */
reg_w(dev, 0x04, 0x41, 0x0001);
reg_w(dev, 0x04, 0x00, 0x0002);
/* reserved */
reg_w(dev, 0x04, 0x00, 0x0003);
/* reserved */
reg_w(dev, 0x04, 0x00, 0x0004);
/* reserved */
reg_w(dev, 0x04, 0x01, 0x0005);
/* reserced */
reg_w(dev, 0x04, xmult, 0x0006);
/* reserved */
reg_w(dev, 0x04, ymult, 0x0007);
/* compression 1 */
reg_w(dev, 0x04, 0x00, 0x0008);
/* T=64 -> 2 */
reg_w(dev, 0x04, 0x00, 0x0009);
/* threshold2D */
reg_w(dev, 0x04, 0x21, 0x000a);
/* quantization */
reg_w(dev, 0x04, 0x00, 0x000b);
}
/* this function is called at probe time */
static int sd_config(struct gspca_dev *gspca_dev,
const struct usb_device_id *id)
{
struct 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->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value;
sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value;
sd->colors = sd_ctrls[SD_COLOR].qctrl.default_value;
sd->hue = sd_ctrls[SD_HUE].qctrl.default_value;
return 0;
}
/* this function is called at probe and resume time */
static int sd_init(struct gspca_dev *gspca_dev)
{
struct usb_device *dev = gspca_dev->dev;
reg_w(dev, 0x03, 0x00, 0x0004);
reg_w(dev, 0x03, 0xFF, 0x0003);
reg_w(dev, 0x03, 0x00, 0x0000);
reg_w(dev, 0x03, 0x1c, 0x0001);
reg_w(dev, 0x03, 0x18, 0x0001);
/* Init on PAL and composite input0 */
spca506_SetNormeInput(gspca_dev, 0, 0);
reg_w(dev, 0x03, 0x1c, 0x0001);
reg_w(dev, 0x03, 0x18, 0x0001);
reg_w(dev, 0x05, 0x00, 0x0000);
reg_w(dev, 0x05, 0xef, 0x0001);
reg_w(dev, 0x05, 0x00, 0x00c1);
reg_w(dev, 0x05, 0x00, 0x00c2);
reg_w(dev, 0x06, 0x18, 0x0002);
reg_w(dev, 0x06, 0xf5, 0x0011);
reg_w(dev, 0x06, 0x02, 0x0012);
reg_w(dev, 0x06, 0xfb, 0x0013);
reg_w(dev, 0x06, 0x00, 0x0014);
reg_w(dev, 0x06, 0xa4, 0x0051);
reg_w(dev, 0x06, 0x40, 0x0052);
reg_w(dev, 0x06, 0x71, 0x0053);
reg_w(dev, 0x06, 0x40, 0x0054);
/************************************************/
reg_w(dev, 0x03, 0x00, 0x0004);
reg_w(dev, 0x03, 0x00, 0x0003);
reg_w(dev, 0x03, 0x00, 0x0004);
reg_w(dev, 0x03, 0xFF, 0x0003);
reg_w(dev, 0x02, 0x00, 0x0000);
reg_w(dev, 0x03, 0x60, 0x0000);
reg_w(dev, 0x03, 0x18, 0x0001);
/* for a better reading mx :) */
/*sdca506_WriteI2c(value,register) */
spca506_Initi2c(gspca_dev);
spca506_WriteI2c(gspca_dev, 0x08, 0x01);
spca506_WriteI2c(gspca_dev, 0xc0, 0x02);
/* input composite video */
spca506_WriteI2c(gspca_dev, 0x33, 0x03);
spca506_WriteI2c(gspca_dev, 0x00, 0x04);
spca506_WriteI2c(gspca_dev, 0x00, 0x05);
spca506_WriteI2c(gspca_dev, 0x0d, 0x06);
spca506_WriteI2c(gspca_dev, 0xf0, 0x07);
spca506_WriteI2c(gspca_dev, 0x98, 0x08);
spca506_WriteI2c(gspca_dev, 0x03, 0x09);
spca506_WriteI2c(gspca_dev, 0x80, 0x0a);
spca506_WriteI2c(gspca_dev, 0x47, 0x0b);
spca506_WriteI2c(gspca_dev, 0x48, 0x0c);
spca506_WriteI2c(gspca_dev, 0x00, 0x0d);
spca506_WriteI2c(gspca_dev, 0x03, 0x0e); /* Chroma Pal adjust */
spca506_WriteI2c(gspca_dev, 0x2a, 0x0f);
spca506_WriteI2c(gspca_dev, 0x00, 0x10);
spca506_WriteI2c(gspca_dev, 0x0c, 0x11);
spca506_WriteI2c(gspca_dev, 0xb8, 0x12);
spca506_WriteI2c(gspca_dev, 0x01, 0x13);
spca506_WriteI2c(gspca_dev, 0x00, 0x14);
spca506_WriteI2c(gspca_dev, 0x00, 0x15);
spca506_WriteI2c(gspca_dev, 0x00, 0x16);
spca506_WriteI2c(gspca_dev, 0x00, 0x17);
spca506_WriteI2c(gspca_dev, 0x00, 0x18);
spca506_WriteI2c(gspca_dev, 0x00, 0x19);
spca506_WriteI2c(gspca_dev, 0x00, 0x1a);
spca506_WriteI2c(gspca_dev, 0x00, 0x1b);
spca506_WriteI2c(gspca_dev, 0x00, 0x1c);
spca506_WriteI2c(gspca_dev, 0x00, 0x1d);
spca506_WriteI2c(gspca_dev, 0x00, 0x1e);
spca506_WriteI2c(gspca_dev, 0xa1, 0x1f);
spca506_WriteI2c(gspca_dev, 0x02, 0x40);
spca506_WriteI2c(gspca_dev, 0xff, 0x41);
spca506_WriteI2c(gspca_dev, 0xff, 0x42);
spca506_WriteI2c(gspca_dev, 0xff, 0x43);
spca506_WriteI2c(gspca_dev, 0xff, 0x44);
spca506_WriteI2c(gspca_dev, 0xff, 0x45);
spca506_WriteI2c(gspca_dev, 0xff, 0x46);
spca506_WriteI2c(gspca_dev, 0xff, 0x47);
spca506_WriteI2c(gspca_dev, 0xff, 0x48);
spca506_WriteI2c(gspca_dev, 0xff, 0x49);
spca506_WriteI2c(gspca_dev, 0xff, 0x4a);
spca506_WriteI2c(gspca_dev, 0xff, 0x4b);
spca506_WriteI2c(gspca_dev, 0xff, 0x4c);
spca506_WriteI2c(gspca_dev, 0xff, 0x4d);
spca506_WriteI2c(gspca_dev, 0xff, 0x4e);
spca506_WriteI2c(gspca_dev, 0xff, 0x4f);
spca506_WriteI2c(gspca_dev, 0xff, 0x50);
spca506_WriteI2c(gspca_dev, 0xff, 0x51);
spca506_WriteI2c(gspca_dev, 0xff, 0x52);
spca506_WriteI2c(gspca_dev, 0xff, 0x53);
spca506_WriteI2c(gspca_dev, 0xff, 0x54);
spca506_WriteI2c(gspca_dev, 0xff, 0x55);
spca506_WriteI2c(gspca_dev, 0xff, 0x56);
spca506_WriteI2c(gspca_dev, 0xff, 0x57);
spca506_WriteI2c(gspca_dev, 0x00, 0x58);
spca506_WriteI2c(gspca_dev, 0x54, 0x59);
spca506_WriteI2c(gspca_dev, 0x07, 0x5a);
spca506_WriteI2c(gspca_dev, 0x83, 0x5b);
spca506_WriteI2c(gspca_dev, 0x00, 0x5c);
spca506_WriteI2c(gspca_dev, 0x00, 0x5d);
spca506_WriteI2c(gspca_dev, 0x00, 0x5e);
spca506_WriteI2c(gspca_dev, 0x00, 0x5f);
spca506_WriteI2c(gspca_dev, 0x00, 0x60);
spca506_WriteI2c(gspca_dev, 0x05, 0x61);
spca506_WriteI2c(gspca_dev, 0x9f, 0x62);
PDEBUG(D_STREAM, "** Close Init *");
return 0;
}
static int sd_start(struct gspca_dev *gspca_dev)
{
struct usb_device *dev = gspca_dev->dev;
__u16 norme;
__u16 channel;
/**************************************/
reg_w(dev, 0x03, 0x00, 0x0004);
reg_w(dev, 0x03, 0x00, 0x0003);
reg_w(dev, 0x03, 0x00, 0x0004);
reg_w(dev, 0x03, 0xFF, 0x0003);
reg_w(dev, 0x02, 0x00, 0x0000);
reg_w(dev, 0x03, 0x60, 0x0000);
reg_w(dev, 0x03, 0x18, 0x0001);
/*sdca506_WriteI2c(value,register) */
spca506_Initi2c(gspca_dev);
spca506_WriteI2c(gspca_dev, 0x08, 0x01); /* Increment Delay */
/* spca506_WriteI2c(gspca_dev, 0xc0, 0x02); * Analog Input Control 1 */
spca506_WriteI2c(gspca_dev, 0x33, 0x03);
/* Analog Input Control 2 */
spca506_WriteI2c(gspca_dev, 0x00, 0x04);
/* Analog Input Control 3 */
spca506_WriteI2c(gspca_dev, 0x00, 0x05);
/* Analog Input Control 4 */
spca506_WriteI2c(gspca_dev, 0x0d, 0x06);
/* Horizontal Sync Start 0xe9-0x0d */
spca506_WriteI2c(gspca_dev, 0xf0, 0x07);
/* Horizontal Sync Stop 0x0d-0xf0 */
spca506_WriteI2c(gspca_dev, 0x98, 0x08); /* Sync Control */
/* Defaults value */
spca506_WriteI2c(gspca_dev, 0x03, 0x09); /* Luminance Control */
spca506_WriteI2c(gspca_dev, 0x80, 0x0a);
/* Luminance Brightness */
spca506_WriteI2c(gspca_dev, 0x47, 0x0b); /* Luminance Contrast */
spca506_WriteI2c(gspca_dev, 0x48, 0x0c);
/* Chrominance Saturation */
spca506_WriteI2c(gspca_dev, 0x00, 0x0d);
/* Chrominance Hue Control */
spca506_WriteI2c(gspca_dev, 0x2a, 0x0f);
/* Chrominance Gain Control */
/**************************************/
spca506_WriteI2c(gspca_dev, 0x00, 0x10);
/* Format/Delay Control */
spca506_WriteI2c(gspca_dev, 0x0c, 0x11); /* Output Control 1 */
spca506_WriteI2c(gspca_dev, 0xb8, 0x12); /* Output Control 2 */
spca506_WriteI2c(gspca_dev, 0x01, 0x13); /* Output Control 3 */
spca506_WriteI2c(gspca_dev, 0x00, 0x14); /* reserved */
spca506_WriteI2c(gspca_dev, 0x00, 0x15); /* VGATE START */
spca506_WriteI2c(gspca_dev, 0x00, 0x16); /* VGATE STOP */
spca506_WriteI2c(gspca_dev, 0x00, 0x17); /* VGATE Control (MSB) */
spca506_WriteI2c(gspca_dev, 0x00, 0x18);
spca506_WriteI2c(gspca_dev, 0x00, 0x19);
spca506_WriteI2c(gspca_dev, 0x00, 0x1a);
spca506_WriteI2c(gspca_dev, 0x00, 0x1b);
spca506_WriteI2c(gspca_dev, 0x00, 0x1c);
spca506_WriteI2c(gspca_dev, 0x00, 0x1d);
spca506_WriteI2c(gspca_dev, 0x00, 0x1e);
spca506_WriteI2c(gspca_dev, 0xa1, 0x1f);
spca506_WriteI2c(gspca_dev, 0x02, 0x40);
spca506_WriteI2c(gspca_dev, 0xff, 0x41);
spca506_WriteI2c(gspca_dev, 0xff, 0x42);
spca506_WriteI2c(gspca_dev, 0xff, 0x43);
spca506_WriteI2c(gspca_dev, 0xff, 0x44);
spca506_WriteI2c(gspca_dev, 0xff, 0x45);
spca506_WriteI2c(gspca_dev, 0xff, 0x46);
spca506_WriteI2c(gspca_dev, 0xff, 0x47);
spca506_WriteI2c(gspca_dev, 0xff, 0x48);
spca506_WriteI2c(gspca_dev, 0xff, 0x49);
spca506_WriteI2c(gspca_dev, 0xff, 0x4a);
spca506_WriteI2c(gspca_dev, 0xff, 0x4b);
spca506_WriteI2c(gspca_dev, 0xff, 0x4c);
spca506_WriteI2c(gspca_dev, 0xff, 0x4d);
spca506_WriteI2c(gspca_dev, 0xff, 0x4e);
spca506_WriteI2c(gspca_dev, 0xff, 0x4f);
spca506_WriteI2c(gspca_dev, 0xff, 0x50);
spca506_WriteI2c(gspca_dev, 0xff, 0x51);
spca506_WriteI2c(gspca_dev, 0xff, 0x52);
spca506_WriteI2c(gspca_dev, 0xff, 0x53);
spca506_WriteI2c(gspca_dev, 0xff, 0x54);
spca506_WriteI2c(gspca_dev, 0xff, 0x55);
spca506_WriteI2c(gspca_dev, 0xff, 0x56);
spca506_WriteI2c(gspca_dev, 0xff, 0x57);
spca506_WriteI2c(gspca_dev, 0x00, 0x58);
spca506_WriteI2c(gspca_dev, 0x54, 0x59);
spca506_WriteI2c(gspca_dev, 0x07, 0x5a);
spca506_WriteI2c(gspca_dev, 0x83, 0x5b);
spca506_WriteI2c(gspca_dev, 0x00, 0x5c);
spca506_WriteI2c(gspca_dev, 0x00, 0x5d);
spca506_WriteI2c(gspca_dev, 0x00, 0x5e);
spca506_WriteI2c(gspca_dev, 0x00, 0x5f);
spca506_WriteI2c(gspca_dev, 0x00, 0x60);
spca506_WriteI2c(gspca_dev, 0x05, 0x61);
spca506_WriteI2c(gspca_dev, 0x9f, 0x62);
/**************************************/
reg_w(dev, 0x05, 0x00, 0x0003);
reg_w(dev, 0x05, 0x00, 0x0004);
reg_w(dev, 0x03, 0x10, 0x0001);
reg_w(dev, 0x03, 0x78, 0x0000);
switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) {
case 0:
spca506_Setsize(gspca_dev, 0, 0x10, 0x10);
break;
case 1:
spca506_Setsize(gspca_dev, 1, 0x1a, 0x1a);
break;
case 2:
spca506_Setsize(gspca_dev, 2, 0x1c, 0x1c);
break;
case 4:
spca506_Setsize(gspca_dev, 4, 0x34, 0x34);
break;
default:
/* case 5: */
spca506_Setsize(gspca_dev, 5, 0x40, 0x40);
break;
}
/* compress setting and size */
/* set i2c luma */
reg_w(dev, 0x02, 0x01, 0x0000);
reg_w(dev, 0x03, 0x12, 0x0000);
reg_r(gspca_dev, 0x04, 0x0001, 2);
PDEBUG(D_STREAM, "webcam started");
spca506_GetNormeInput(gspca_dev, &norme, &channel);
spca506_SetNormeInput(gspca_dev, norme, channel);
return 0;
}
static void sd_stopN(struct gspca_dev *gspca_dev)
{
struct usb_device *dev = gspca_dev->dev;
reg_w(dev, 0x02, 0x00, 0x0000);
reg_w(dev, 0x03, 0x00, 0x0004);
reg_w(dev, 0x03, 0x00, 0x0003);
}
static void sd_pkt_scan(struct gspca_dev *gspca_dev,
struct gspca_frame *frame, /* target */
__u8 *data, /* isoc packet */
int len) /* iso packet length */
{
switch (data[0]) {
case 0: /* start of frame */
frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
data, 0);
data += SPCA50X_OFFSET_DATA;
len -= SPCA50X_OFFSET_DATA;
gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
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, frame,
data, len);
break;
}
}
static void setbrightness(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
spca506_Initi2c(gspca_dev);
spca506_WriteI2c(gspca_dev, sd->brightness, SAA7113_bright);
spca506_WriteI2c(gspca_dev, 0x01, 0x09);
}
static void setcontrast(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
spca506_Initi2c(gspca_dev);
spca506_WriteI2c(gspca_dev, sd->contrast, SAA7113_contrast);
spca506_WriteI2c(gspca_dev, 0x01, 0x09);
}
static void setcolors(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
spca506_Initi2c(gspca_dev);
spca506_WriteI2c(gspca_dev, sd->colors, SAA7113_saturation);
spca506_WriteI2c(gspca_dev, 0x01, 0x09);
}
static void sethue(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
spca506_Initi2c(gspca_dev);
spca506_WriteI2c(gspca_dev, sd->hue, SAA7113_hue);
spca506_WriteI2c(gspca_dev, 0x01, 0x09);
}
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
sd->brightness = val;
if (gspca_dev->streaming)
setbrightness(gspca_dev);
return 0;
}
static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
*val = sd->brightness;
return 0;
}
static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
sd->contrast = val;
if (gspca_dev->streaming)
setcontrast(gspca_dev);
return 0;
}
static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
*val = sd->contrast;
return 0;
}
static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
sd->colors = val;
if (gspca_dev->streaming)
setcolors(gspca_dev);
return 0;
}
static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
*val = sd->colors;
return 0;
}
static int sd_sethue(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
sd->hue = val;
if (gspca_dev->streaming)
sethue(gspca_dev);
return 0;
}
static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
*val = sd->hue;
return 0;
}
/* sub-driver description */
static struct sd_desc sd_desc = {
.name = MODULE_NAME,
.ctrls = sd_ctrls,
.nctrls = ARRAY_SIZE(sd_ctrls),
.config = sd_config,
.init = sd_init,
.start = sd_start,
.stopN = sd_stopN,
.pkt_scan = sd_pkt_scan,
};
/* -- module initialisation -- */
static __devinitdata struct usb_device_id device_table[] = {
{USB_DEVICE(0x06e1, 0xa190)},
/*fixme: may be IntelPCCameraPro BRIDGE_SPCA505
{USB_DEVICE(0x0733, 0x0430)}, */
{USB_DEVICE(0x0734, 0x043b)},
{USB_DEVICE(0x99fa, 0x8988)},
{}
};
MODULE_DEVICE_TABLE(usb, device_table);
/* -- device connect -- */
static int sd_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
THIS_MODULE);
}
static struct usb_driver sd_driver = {
.name = MODULE_NAME,
.id_table = device_table,
.probe = sd_probe,
.disconnect = gspca_disconnect,
#ifdef CONFIG_PM
.suspend = gspca_suspend,
.resume = gspca_resume,
#endif
};
/* -- module insert / remove -- */
static int __init sd_mod_init(void)
{
int ret;
ret = usb_register(&sd_driver);
if (ret < 0)
return ret;
PDEBUG(D_PROBE, "registered");
return 0;
}
static void __exit sd_mod_exit(void)
{
usb_deregister(&sd_driver);
PDEBUG(D_PROBE, "deregistered");
}
module_init(sd_mod_init);
module_exit(sd_mod_exit);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,463 @@
/*
* SQ905 subdriver
*
* Copyright (C) 2008, 2009 Adam Baker and Theodore Kilgore
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* History and Acknowledgments
*
* The original Linux driver for SQ905 based cameras was written by
* Marcell Lengyel and furter developed by many other contributers
* 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 MODULE_NAME "sq905"
#include <linux/workqueue.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) {
PDEBUG(D_ERR, "%s: usb_control_msg failed (%d)",
__func__, ret);
return ret;
}
ret = usb_control_msg(gspca_dev->dev,
usb_sndctrlpipe(gspca_dev->dev, 0),
USB_REQ_SYNCH_FRAME, /* request */
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
SQ905_PING, 0, gspca_dev->usb_buf, 1,
SQ905_CMD_TIMEOUT);
if (ret < 0) {
PDEBUG(D_ERR, "%s: usb_control_msg failed 2 (%d)",
__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) {
PDEBUG(D_ERR, "%s: usb_control_msg failed (%d)", __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 ret;
int act_len;
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_BULK_READ, size, gspca_dev->usb_buf,
1, SQ905_CMD_TIMEOUT);
if (ret < 0) {
PDEBUG(D_ERR, "%s: usb_control_msg failed (%d)", __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) {
PDEBUG(D_ERR, "bulk read fail (%d) len %d/%d",
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 the camera's USB interface we take the gspca
* usb_lock when performing USB operations. In practice the only thing we need
* to protect against is the usb_set_interface call that gspca makes during
* stream_off as the camera doesn't provide any controls that the user could try
* to change.
*/
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;
struct gspca_frame *frame;
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 discarding; /* true if we failed to get space for frame. */
int packet_type;
int frame_sz;
int ret;
u8 *data;
u8 *buffer;
buffer = kmalloc(SQ905_MAX_TRANSFER, GFP_KERNEL | GFP_DMA);
mutex_lock(&gspca_dev->usb_lock);
if (!buffer) {
PDEBUG(D_ERR, "Couldn't allocate USB buffer");
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) {
/* Need a short delay to ensure streaming flag was set by
* gspca and to make sure gspca can grab the mutex. */
mutex_unlock(&gspca_dev->usb_lock);
msleep(1);
/* request some data and then read it until we have
* a complete frame. */
bytes_left = frame_sz;
header_read = 0;
discarding = 0;
while (bytes_left > 0) {
data_len = bytes_left > SQ905_MAX_TRANSFER ?
SQ905_MAX_TRANSFER : bytes_left;
mutex_lock(&gspca_dev->usb_lock);
if (!gspca_dev->present)
goto quit_stream;
ret = sq905_read_data(gspca_dev, buffer, data_len);
if (ret < 0)
goto quit_stream;
mutex_unlock(&gspca_dev->usb_lock);
PDEBUG(D_STREAM,
"Got %d bytes out of %d for frame",
data_len, bytes_left);
bytes_left -= data_len;
data = buffer;
if (!header_read) {
packet_type = FIRST_PACKET;
/* The first 64 bytes of each frame are
* a header full of FF 00 bytes */
data += FRAME_HEADER_LEN;
data_len -= FRAME_HEADER_LEN;
header_read = 1;
} else if (bytes_left == 0) {
packet_type = LAST_PACKET;
} else {
packet_type = INTER_PACKET;
}
frame = gspca_get_i_frame(gspca_dev);
if (frame && !discarding) {
frame = gspca_frame_add(gspca_dev, packet_type,
frame, 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)
frame = gspca_frame_add(gspca_dev,
LAST_PACKET,
frame, data, 0);
} else {
discarding = 1;
}
}
/* acknowledge the frame */
mutex_lock(&gspca_dev->usb_lock);
if (!gspca_dev->present)
goto quit_stream;
ret = sq905_ack_frame(gspca_dev);
if (ret < 0)
goto quit_stream;
}
quit_stream:
/* the usb_lock is already acquired */
if (gspca_dev->present)
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);
if (ret < 0)
return ret;
/* usb_buf is allocated with kmalloc so is aligned.
* Camera model number is the right way round if we assume this
* reverse engineered ID is supposed to be big endian. */
ident = be32_to_cpup((__be32 *)gspca_dev->usb_buf);
ret = sq905_command(gspca_dev, SQ905_CLEAR);
if (ret < 0)
return ret;
PDEBUG(D_CONF, "SQ905 camera ID %08x detected", ident);
gspca_dev->cam.cam_mode = sq905_mode;
gspca_dev->cam.nmodes = ARRAY_SIZE(sq905_mode);
if (!(ident & SQ905_HIRES_MASK))
gspca_dev->cam.nmodes--;
if (ident & SQ905_ORIENTATION_MASK)
gspca_dev->cam.input_flags = V4L2_IN_ST_VFLIP;
else
gspca_dev->cam.input_flags = V4L2_IN_ST_VFLIP |
V4L2_IN_ST_HFLIP;
return 0;
}
/* Set up for getting frames. */
static int sd_start(struct gspca_dev *gspca_dev)
{
struct sd *dev = (struct sd *) gspca_dev;
int ret;
/* "Open the shutter" and set size, to start capture */
switch (gspca_dev->curr_mode) {
default:
/* case 2: */
PDEBUG(D_STREAM, "Start streaming at high resolution");
ret = sq905_command(&dev->gspca_dev, SQ905_CAPTURE_HIGH);
break;
case 1:
PDEBUG(D_STREAM, "Start streaming at medium resolution");
ret = sq905_command(&dev->gspca_dev, SQ905_CAPTURE_MED);
break;
case 0:
PDEBUG(D_STREAM, "Start streaming at low resolution");
ret = sq905_command(&dev->gspca_dev, SQ905_CAPTURE_LOW);
}
if (ret < 0) {
PDEBUG(D_ERR, "Start streaming command failed");
return ret;
}
/* Start the workqueue function to do the streaming */
dev->work_thread = create_singlethread_workqueue(MODULE_NAME);
queue_work(dev->work_thread, &dev->work_struct);
return 0;
}
/* Table of supported USB devices */
static const __devinitdata 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,
#endif
};
/* -- module insert / remove -- */
static int __init sd_mod_init(void)
{
int ret;
ret = usb_register(&sd_driver);
if (ret < 0)
return ret;
PDEBUG(D_PROBE, "registered");
return 0;
}
static void __exit sd_mod_exit(void)
{
usb_deregister(&sd_driver);
PDEBUG(D_PROBE, "deregistered");
}
module_init(sd_mod_init);
module_exit(sd_mod_exit);

View File

@@ -0,0 +1,329 @@
/*
* SQ905C subdriver
*
* Copyright (C) 2009 Theodore Kilgore
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
*
* This driver uses work done in
* libgphoto2/camlibs/digigr8, Copyright (C) Theodore Kilgore.
*
* This driver has also used as a base the sq905c driver
* and may contain code fragments from it.
*/
#define MODULE_NAME "sq905c"
#include <linux/workqueue.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_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) {
PDEBUG(D_ERR, "%s: usb_control_msg failed (%d)",
__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 the camera's USB interface the gspca usb_lock is
* used when performing the one USB control operation inside the workqueue,
* which tells the camera to close the stream. In practice the only thing
* which needs to be protected against is the usb_set_interface call that
* gspca makes during stream_off. Otherwise the camera doesn't provide any
* controls that the user could try to change.
*/
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;
struct gspca_frame *frame;
int bytes_left; /* bytes remaining in current frame. */
int data_len; /* size to use for the next read. */
int act_len;
int discarding = 0; /* true if we failed to get space for frame. */
int packet_type;
int ret;
u8 *buffer;
buffer = kmalloc(SQ905C_MAX_TRANSFER, GFP_KERNEL | GFP_DMA);
if (!buffer) {
PDEBUG(D_ERR, "Couldn't allocate USB buffer");
goto quit_stream;
}
while (gspca_dev->present && gspca_dev->streaming) {
if (!gspca_dev->present)
goto quit_stream;
/* Request the header, which tells the size to download */
ret = usb_bulk_msg(gspca_dev->dev,
usb_rcvbulkpipe(gspca_dev->dev, 0x81),
buffer, FRAME_HEADER_LEN, &act_len,
SQ905C_DATA_TIMEOUT);
PDEBUG(D_STREAM,
"Got %d bytes out of %d for header",
act_len, FRAME_HEADER_LEN);
if (ret < 0 || act_len < FRAME_HEADER_LEN)
goto quit_stream;
/* size is read from 4 bytes starting 0x40, little endian */
bytes_left = buffer[0x40]|(buffer[0x41]<<8)|(buffer[0x42]<<16)
|(buffer[0x43]<<24);
PDEBUG(D_STREAM, "bytes_left = 0x%x", bytes_left);
/* We keep the header. It has other information, too. */
packet_type = FIRST_PACKET;
frame = gspca_get_i_frame(gspca_dev);
if (frame && !discarding) {
gspca_frame_add(gspca_dev, packet_type,
frame, buffer, FRAME_HEADER_LEN);
} else
discarding = 1;
while (bytes_left > 0) {
data_len = bytes_left > SQ905C_MAX_TRANSFER ?
SQ905C_MAX_TRANSFER : bytes_left;
if (!gspca_dev->present)
goto quit_stream;
ret = usb_bulk_msg(gspca_dev->dev,
usb_rcvbulkpipe(gspca_dev->dev, 0x81),
buffer, data_len, &act_len,
SQ905C_DATA_TIMEOUT);
if (ret < 0 || act_len < data_len)
goto quit_stream;
PDEBUG(D_STREAM,
"Got %d bytes out of %d for frame",
data_len, bytes_left);
bytes_left -= data_len;
if (bytes_left == 0)
packet_type = LAST_PACKET;
else
packet_type = INTER_PACKET;
frame = gspca_get_i_frame(gspca_dev);
if (frame && !discarding)
gspca_frame_add(gspca_dev, packet_type,
frame, buffer, data_len);
else
discarding = 1;
}
}
quit_stream:
mutex_lock(&gspca_dev->usb_lock);
if (gspca_dev->present)
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;
PDEBUG(D_PROBE,
"SQ9050 camera detected"
" (vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct);
cam->cam_mode = sq905c_mode;
cam->nmodes = 2;
if (id->idProduct == 0x9050)
cam->nmodes = 1;
/* We don't use the buffer gspca allocates so make it small. */
cam->bulk_size = 32;
cam->bulk = 1;
INIT_WORK(&dev->work_struct, sq905c_dostream);
return 0;
}
/* called on streamoff with alt==0 and on disconnect */
/* the usb_lock is held at entry - restore on exit */
static void sd_stop0(struct gspca_dev *gspca_dev)
{
struct sd *dev = (struct sd *) gspca_dev;
/* wait for the work queue to terminate */
mutex_unlock(&gspca_dev->usb_lock);
/* This waits for sq905c_dostream to finish */
destroy_workqueue(dev->work_thread);
dev->work_thread = NULL;
mutex_lock(&gspca_dev->usb_lock);
}
/* this function is called at probe and resume time */
static int sd_init(struct gspca_dev *gspca_dev)
{
int ret;
/* connect to the camera and reset it. */
ret = sq905c_command(gspca_dev, SQ905C_CLEAR, 0);
return ret;
}
/* Set up for getting frames. */
static int sd_start(struct gspca_dev *gspca_dev)
{
struct sd *dev = (struct sd *) gspca_dev;
int ret;
dev->cap_mode = gspca_dev->cam.cam_mode;
/* "Open the shutter" and set size, to start capture */
switch (gspca_dev->width) {
case 640:
PDEBUG(D_STREAM, "Start streaming at high resolution");
dev->cap_mode++;
ret = sq905c_command(gspca_dev, SQ905C_CAPTURE_HI,
SQ905C_CAPTURE_INDEX);
break;
default: /* 320 */
PDEBUG(D_STREAM, "Start streaming at medium resolution");
ret = sq905c_command(gspca_dev, SQ905C_CAPTURE_MED,
SQ905C_CAPTURE_INDEX);
}
if (ret < 0) {
PDEBUG(D_ERR, "Start streaming command failed");
return ret;
}
/* Start the workqueue function to do the streaming */
dev->work_thread = create_singlethread_workqueue(MODULE_NAME);
queue_work(dev->work_thread, &dev->work_struct);
return 0;
}
/* Table of supported USB devices */
static const __devinitdata struct usb_device_id device_table[] = {
{USB_DEVICE(0x2770, 0x905c)},
{USB_DEVICE(0x2770, 0x9050)},
{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,
#endif
};
/* -- module insert / remove -- */
static int __init sd_mod_init(void)
{
int ret;
ret = usb_register(&sd_driver);
if (ret < 0)
return ret;
PDEBUG(D_PROBE, "registered");
return 0;
}
static void __exit sd_mod_exit(void)
{
usb_deregister(&sd_driver);
PDEBUG(D_PROBE, "deregistered");
}
module_init(sd_mod_init);
module_exit(sd_mod_exit);

View File

@@ -0,0 +1,630 @@
/*
* Syntek DV4000 (STK014) subdriver
*
* Copyright (C) 2008 Jean-Francois Moine (http://moinejf.free.fr)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define MODULE_NAME "stk014"
#include "gspca.h"
#include "jpeg.h"
MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>");
MODULE_DESCRIPTION("Syntek DV4000 (STK014) USB Camera Driver");
MODULE_LICENSE("GPL");
/* specific webcam descriptor */
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
unsigned char brightness;
unsigned char contrast;
unsigned char colors;
unsigned char lightfreq;
u8 quality;
#define QUALITY_MIN 60
#define QUALITY_MAX 95
#define QUALITY_DEF 80
u8 *jpeg_hdr;
};
/* V4L2 controls supported by the driver */
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val);
static struct ctrl sd_ctrls[] = {
{
{
.id = V4L2_CID_BRIGHTNESS,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Brightness",
.minimum = 0,
.maximum = 255,
.step = 1,
#define BRIGHTNESS_DEF 127
.default_value = BRIGHTNESS_DEF,
},
.set = sd_setbrightness,
.get = sd_getbrightness,
},
{
{
.id = V4L2_CID_CONTRAST,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Contrast",
.minimum = 0,
.maximum = 255,
.step = 1,
#define CONTRAST_DEF 127
.default_value = CONTRAST_DEF,
},
.set = sd_setcontrast,
.get = sd_getcontrast,
},
{
{
.id = V4L2_CID_SATURATION,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Color",
.minimum = 0,
.maximum = 255,
.step = 1,
#define COLOR_DEF 127
.default_value = COLOR_DEF,
},
.set = sd_setcolors,
.get = sd_getcolors,
},
{
{
.id = V4L2_CID_POWER_LINE_FREQUENCY,
.type = V4L2_CTRL_TYPE_MENU,
.name = "Light frequency filter",
.minimum = 1,
.maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */
.step = 1,
#define FREQ_DEF 1
.default_value = FREQ_DEF,
},
.set = sd_setfreq,
.get = sd_getfreq,
},
};
static 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 int reg_r(struct gspca_dev *gspca_dev,
__u16 index)
{
struct usb_device *dev = gspca_dev->dev;
int ret;
ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
0x00,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0x00,
index,
gspca_dev->usb_buf, 1,
500);
if (ret < 0) {
PDEBUG(D_ERR, "reg_r err %d", ret);
return ret;
}
return gspca_dev->usb_buf[0];
}
/* -- write a register -- */
static int reg_w(struct gspca_dev *gspca_dev,
__u16 index, __u16 value)
{
struct usb_device *dev = gspca_dev->dev;
int ret;
ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
0x01,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
value,
index,
NULL,
0,
500);
if (ret < 0)
PDEBUG(D_ERR, "reg_w err %d", ret);
return ret;
}
/* -- get a bulk value (4 bytes) -- */
static int rcv_val(struct gspca_dev *gspca_dev,
int ads)
{
struct usb_device *dev = gspca_dev->dev;
int alen, ret;
reg_w(gspca_dev, 0x634, (ads >> 16) & 0xff);
reg_w(gspca_dev, 0x635, (ads >> 8) & 0xff);
reg_w(gspca_dev, 0x636, ads & 0xff);
reg_w(gspca_dev, 0x637, 0);
reg_w(gspca_dev, 0x638, 4); /* len & 0xff */
reg_w(gspca_dev, 0x639, 0); /* len >> 8 */
reg_w(gspca_dev, 0x63a, 0);
reg_w(gspca_dev, 0x63b, 0);
reg_w(gspca_dev, 0x630, 5);
ret = usb_bulk_msg(dev,
usb_rcvbulkpipe(dev, 0x05),
gspca_dev->usb_buf,
4, /* length */
&alen,
500); /* timeout in milliseconds */
return ret;
}
/* -- send a bulk value -- */
static int snd_val(struct gspca_dev *gspca_dev,
int ads,
unsigned int val)
{
struct usb_device *dev = gspca_dev->dev;
int alen, ret;
__u8 seq = 0;
if (ads == 0x003f08) {
ret = reg_r(gspca_dev, 0x0704);
if (ret < 0)
goto ko;
ret = reg_r(gspca_dev, 0x0705);
if (ret < 0)
goto ko;
seq = ret; /* keep the sequence number */
ret = reg_r(gspca_dev, 0x0650);
if (ret < 0)
goto ko;
reg_w(gspca_dev, 0x654, seq);
} else {
reg_w(gspca_dev, 0x654, (ads >> 16) & 0xff);
}
reg_w(gspca_dev, 0x655, (ads >> 8) & 0xff);
reg_w(gspca_dev, 0x656, ads & 0xff);
reg_w(gspca_dev, 0x657, 0);
reg_w(gspca_dev, 0x658, 0x04); /* size */
reg_w(gspca_dev, 0x659, 0);
reg_w(gspca_dev, 0x65a, 0);
reg_w(gspca_dev, 0x65b, 0);
reg_w(gspca_dev, 0x650, 5);
gspca_dev->usb_buf[0] = val >> 24;
gspca_dev->usb_buf[1] = val >> 16;
gspca_dev->usb_buf[2] = val >> 8;
gspca_dev->usb_buf[3] = val;
ret = usb_bulk_msg(dev,
usb_sndbulkpipe(dev, 6),
gspca_dev->usb_buf,
4,
&alen,
500); /* timeout in milliseconds */
if (ret < 0)
goto ko;
if (ads == 0x003f08) {
seq += 4;
seq &= 0x3f;
reg_w(gspca_dev, 0x705, seq);
}
return ret;
ko:
PDEBUG(D_ERR, "snd_val err %d", ret);
return ret;
}
/* set a camera parameter */
static int set_par(struct gspca_dev *gspca_dev,
int parval)
{
return snd_val(gspca_dev, 0x003f08, parval);
}
static void setbrightness(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
int parval;
parval = 0x06000000 /* whiteness */
+ (sd->brightness << 16);
set_par(gspca_dev, parval);
}
static void setcontrast(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
int parval;
parval = 0x07000000 /* contrast */
+ (sd->contrast << 16);
set_par(gspca_dev, parval);
}
static void setcolors(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
int parval;
parval = 0x08000000 /* saturation */
+ (sd->colors << 16);
set_par(gspca_dev, parval);
}
static void setfreq(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
set_par(gspca_dev, sd->lightfreq == 1
? 0x33640000 /* 50 Hz */
: 0x33780000); /* 60 Hz */
}
/* this function is called at probe time */
static int sd_config(struct gspca_dev *gspca_dev,
const struct usb_device_id *id)
{
struct sd *sd = (struct sd *) gspca_dev;
gspca_dev->cam.cam_mode = vga_mode;
gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode);
sd->brightness = BRIGHTNESS_DEF;
sd->contrast = CONTRAST_DEF;
sd->colors = COLOR_DEF;
sd->lightfreq = FREQ_DEF;
sd->quality = QUALITY_DEF;
return 0;
}
/* this function is called at probe and resume time */
static int sd_init(struct gspca_dev *gspca_dev)
{
int ret;
/* check if the device responds */
usb_set_interface(gspca_dev->dev, gspca_dev->iface, 1);
ret = reg_r(gspca_dev, 0x0740);
if (ret < 0)
return ret;
if (ret != 0xff) {
PDEBUG(D_ERR|D_STREAM, "init reg: 0x%02x", ret);
return -1;
}
return 0;
}
/* -- start the camera -- */
static int sd_start(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
int ret, value;
/* create the JPEG header */
sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL);
if (!sd->jpeg_hdr)
return -ENOMEM;
jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
0x22); /* JPEG 411 */
jpeg_set_qual(sd->jpeg_hdr, sd->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->width > 320)
value = 0x8002e001; /* 640x480 */
else
value = 0x4001f000; /* 320x240 */
set_par(gspca_dev, value);
ret = usb_set_interface(gspca_dev->dev,
gspca_dev->iface,
gspca_dev->alt);
if (ret < 0) {
PDEBUG(D_ERR|D_STREAM, "set intf %d %d failed",
gspca_dev->iface, gspca_dev->alt);
goto out;
}
ret = reg_r(gspca_dev, 0x0630);
if (ret < 0)
goto out;
rcv_val(gspca_dev, 0x000020); /* << (value ff ff ff ff) */
ret = reg_r(gspca_dev, 0x0650);
if (ret < 0)
goto out;
snd_val(gspca_dev, 0x000020, 0xffffffff);
reg_w(gspca_dev, 0x0620, 0);
reg_w(gspca_dev, 0x0630, 0);
reg_w(gspca_dev, 0x0640, 0);
reg_w(gspca_dev, 0x0650, 0);
reg_w(gspca_dev, 0x0660, 0);
setbrightness(gspca_dev); /* whiteness */
setcontrast(gspca_dev); /* contrast */
setcolors(gspca_dev); /* saturation */
set_par(gspca_dev, 0x09800000); /* Red ? */
set_par(gspca_dev, 0x0a800000); /* Green ? */
set_par(gspca_dev, 0x0b800000); /* Blue ? */
set_par(gspca_dev, 0x0d030000); /* Gamma ? */
setfreq(gspca_dev); /* light frequency */
/* start the video flow */
set_par(gspca_dev, 0x01000000);
set_par(gspca_dev, 0x01000000);
PDEBUG(D_STREAM, "camera started alt: 0x%02x", gspca_dev->alt);
return 0;
out:
PDEBUG(D_ERR|D_STREAM, "camera start err %d", ret);
return ret;
}
static void sd_stopN(struct gspca_dev *gspca_dev)
{
struct usb_device *dev = gspca_dev->dev;
set_par(gspca_dev, 0x02000000);
set_par(gspca_dev, 0x02000000);
usb_set_interface(dev, gspca_dev->iface, 1);
reg_r(gspca_dev, 0x0630);
rcv_val(gspca_dev, 0x000020); /* << (value ff ff ff ff) */
reg_r(gspca_dev, 0x0650);
snd_val(gspca_dev, 0x000020, 0xffffffff);
reg_w(gspca_dev, 0x0620, 0);
reg_w(gspca_dev, 0x0630, 0);
reg_w(gspca_dev, 0x0640, 0);
reg_w(gspca_dev, 0x0650, 0);
reg_w(gspca_dev, 0x0660, 0);
PDEBUG(D_STREAM, "camera stopped");
}
static void sd_stop0(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
kfree(sd->jpeg_hdr);
}
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;
static unsigned char ffd9[] = {0xff, 0xd9};
/* a frame starts with:
* - 0xff 0xfe
* - 0x08 0x00 - length (little endian ?!)
* - 4 bytes = size of whole frame (BE - including header)
* - 0x00 0x0c
* - 0xff 0xd8
* - .. JPEG image with escape sequences (ff 00)
* (without ending - ff d9)
*/
if (data[0] == 0xff && data[1] == 0xfe) {
frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
ffd9, 2);
/* put the JPEG 411 header */
gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
sd->jpeg_hdr, JPEG_HDR_SZ);
/* beginning of the frame */
#define STKHDRSZ 12
data += STKHDRSZ;
len -= STKHDRSZ;
}
gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
}
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
sd->brightness = val;
if (gspca_dev->streaming)
setbrightness(gspca_dev);
return 0;
}
static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
*val = sd->brightness;
return 0;
}
static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
sd->contrast = val;
if (gspca_dev->streaming)
setcontrast(gspca_dev);
return 0;
}
static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
*val = sd->contrast;
return 0;
}
static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
sd->colors = val;
if (gspca_dev->streaming)
setcolors(gspca_dev);
return 0;
}
static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
*val = sd->colors;
return 0;
}
static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
sd->lightfreq = val;
if (gspca_dev->streaming)
setfreq(gspca_dev);
return 0;
}
static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
*val = sd->lightfreq;
return 0;
}
static int sd_querymenu(struct gspca_dev *gspca_dev,
struct v4l2_querymenu *menu)
{
switch (menu->id) {
case V4L2_CID_POWER_LINE_FREQUENCY:
switch (menu->index) {
case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */
strcpy((char *) menu->name, "50 Hz");
return 0;
case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */
strcpy((char *) menu->name, "60 Hz");
return 0;
}
break;
}
return -EINVAL;
}
static int sd_set_jcomp(struct gspca_dev *gspca_dev,
struct v4l2_jpegcompression *jcomp)
{
struct sd *sd = (struct sd *) gspca_dev;
if (jcomp->quality < QUALITY_MIN)
sd->quality = QUALITY_MIN;
else if (jcomp->quality > QUALITY_MAX)
sd->quality = QUALITY_MAX;
else
sd->quality = jcomp->quality;
if (gspca_dev->streaming)
jpeg_set_qual(sd->jpeg_hdr, sd->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 = sd->quality;
jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
| V4L2_JPEG_MARKER_DQT;
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,
.init = sd_init,
.start = sd_start,
.stopN = sd_stopN,
.stop0 = sd_stop0,
.pkt_scan = sd_pkt_scan,
.querymenu = sd_querymenu,
.get_jcomp = sd_get_jcomp,
.set_jcomp = sd_set_jcomp,
};
/* -- module initialisation -- */
static const __devinitdata 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,
#endif
};
/* -- module insert / remove -- */
static int __init sd_mod_init(void)
{
int ret;
ret = usb_register(&sd_driver);
if (ret < 0)
return ret;
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);

View File

@@ -0,0 +1,9 @@
config USB_STV06XX
tristate "STV06XX USB Camera Driver"
depends on USB_GSPCA
help
Say Y here if you want support for cameras based on
the ST STV06XX chip.
To compile this driver as a module, choose M here: the
module will be called gspca_stv06xx.

View File

@@ -0,0 +1,10 @@
obj-$(CONFIG_USB_STV06XX) += gspca_stv06xx.o
gspca_stv06xx-objs := stv06xx.o \
stv06xx_vv6410.o \
stv06xx_hdcs.o \
stv06xx_pb0100.o \
stv06xx_st6422.o
EXTRA_CFLAGS += -Idrivers/media/video/gspca

View File

@@ -0,0 +1,558 @@
/*
* Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
* Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
* Copyright (c) 2002, 2003 Tuukka Toivonen
* Copyright (c) 2008 Erik Andrén
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* P/N 861037: Sensor HDCS1000 ASIC STV0600
* P/N 861050-0010: Sensor HDCS1000 ASIC STV0600
* P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express
* P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam
* P/N 861075-0040: Sensor HDCS1000 ASIC
* P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB
* P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web
*/
#include "stv06xx_sensor.h"
MODULE_AUTHOR("Erik Andrén");
MODULE_DESCRIPTION("STV06XX USB Camera Driver");
MODULE_LICENSE("GPL");
static int dump_bridge;
static int dump_sensor;
int stv06xx_write_bridge(struct sd *sd, u16 address, u16 i2c_data)
{
int err;
struct usb_device *udev = sd->gspca_dev.dev;
__u8 *buf = sd->gspca_dev.usb_buf;
u8 len = (i2c_data > 0xff) ? 2 : 1;
buf[0] = i2c_data & 0xff;
buf[1] = (i2c_data >> 8) & 0xff;
err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
0x04, 0x40, address, 0, buf, len,
STV06XX_URB_MSG_TIMEOUT);
PDEBUG(D_CONF, "Written 0x%x to address 0x%x, status: %d",
i2c_data, address, err);
return (err < 0) ? err : 0;
}
int stv06xx_read_bridge(struct sd *sd, u16 address, u8 *i2c_data)
{
int err;
struct usb_device *udev = sd->gspca_dev.dev;
__u8 *buf = sd->gspca_dev.usb_buf;
err = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
0x04, 0xc0, address, 0, buf, 1,
STV06XX_URB_MSG_TIMEOUT);
*i2c_data = buf[0];
PDEBUG(D_CONF, "Reading 0x%x from address 0x%x, status %d",
*i2c_data, address, err);
return (err < 0) ? err : 0;
}
/* Wraps the normal write sensor bytes / words functions for writing a
single value */
int stv06xx_write_sensor(struct sd *sd, u8 address, u16 value)
{
if (sd->sensor->i2c_len == 2) {
u16 data[2] = { address, value };
return stv06xx_write_sensor_words(sd, data, 1);
} else {
u8 data[2] = { address, value };
return stv06xx_write_sensor_bytes(sd, data, 1);
}
}
static int stv06xx_write_sensor_finish(struct sd *sd)
{
int err = 0;
if (sd->bridge == BRIDGE_STV610) {
struct usb_device *udev = sd->gspca_dev.dev;
__u8 *buf = sd->gspca_dev.usb_buf;
buf[0] = 0;
err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
0x04, 0x40, 0x1704, 0, buf, 1,
STV06XX_URB_MSG_TIMEOUT);
}
return (err < 0) ? err : 0;
}
int stv06xx_write_sensor_bytes(struct sd *sd, const u8 *data, u8 len)
{
int err, i, j;
struct usb_device *udev = sd->gspca_dev.dev;
__u8 *buf = sd->gspca_dev.usb_buf;
PDEBUG(D_CONF, "I2C: Command buffer contains %d entries", len);
for (i = 0; i < len;) {
/* Build the command buffer */
memset(buf, 0, I2C_BUFFER_LENGTH);
for (j = 0; j < I2C_MAX_BYTES && i < len; j++, i++) {
buf[j] = data[2*i];
buf[0x10 + j] = data[2*i+1];
PDEBUG(D_CONF, "I2C: Writing 0x%02x to reg 0x%02x",
data[2*i+1], data[2*i]);
}
buf[0x20] = sd->sensor->i2c_addr;
buf[0x21] = j - 1; /* Number of commands to send - 1 */
buf[0x22] = I2C_WRITE_CMD;
err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
0x04, 0x40, 0x0400, 0, buf,
I2C_BUFFER_LENGTH,
STV06XX_URB_MSG_TIMEOUT);
if (err < 0)
return err;
}
return stv06xx_write_sensor_finish(sd);
}
int stv06xx_write_sensor_words(struct sd *sd, const u16 *data, u8 len)
{
int err, i, j;
struct usb_device *udev = sd->gspca_dev.dev;
__u8 *buf = sd->gspca_dev.usb_buf;
PDEBUG(D_CONF, "I2C: Command buffer contains %d entries", len);
for (i = 0; i < len;) {
/* Build the command buffer */
memset(buf, 0, I2C_BUFFER_LENGTH);
for (j = 0; j < I2C_MAX_WORDS && i < len; j++, i++) {
buf[j] = data[2*i];
buf[0x10 + j * 2] = data[2*i+1];
buf[0x10 + j * 2 + 1] = data[2*i+1] >> 8;
PDEBUG(D_CONF, "I2C: Writing 0x%04x to reg 0x%02x",
data[2*i+1], data[2*i]);
}
buf[0x20] = sd->sensor->i2c_addr;
buf[0x21] = j - 1; /* Number of commands to send - 1 */
buf[0x22] = I2C_WRITE_CMD;
err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
0x04, 0x40, 0x0400, 0, buf,
I2C_BUFFER_LENGTH,
STV06XX_URB_MSG_TIMEOUT);
if (err < 0)
return err;
}
return stv06xx_write_sensor_finish(sd);
}
int stv06xx_read_sensor(struct sd *sd, const u8 address, u16 *value)
{
int err;
struct 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) {
PDEBUG(D_ERR, "I2C: Read error writing address: %d", err);
return err;
}
err = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
0x04, 0xc0, 0x1410, 0, buf, sd->sensor->i2c_len,
STV06XX_URB_MSG_TIMEOUT);
if (sd->sensor->i2c_len == 2)
*value = buf[0] | (buf[1] << 8);
else
*value = buf[0];
PDEBUG(D_CONF, "I2C: Read 0x%x from address 0x%x, status: %d",
*value, address, err);
return (err < 0) ? err : 0;
}
/* Dumps all bridge registers */
static void stv06xx_dump_bridge(struct sd *sd)
{
int i;
u8 data, buf;
info("Dumping all stv06xx bridge registers");
for (i = 0x1400; i < 0x160f; i++) {
stv06xx_read_bridge(sd, i, &data);
info("Read 0x%x from address 0x%x", data, i);
}
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)
info("Register 0x%x is read/write", i);
else if (data != buf)
info("Register 0x%x is read/write,"
"but only partially", i);
else
info("Register 0x%x is read-only", i);
stv06xx_write_bridge(sd, i, buf);
}
}
/* this function is called at probe and resume time */
static int stv06xx_init(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
int err;
PDEBUG(D_PROBE, "Initializing camera");
/* Let the usb init settle for a bit
before performing the initialization */
msleep(250);
err = sd->sensor->init(sd);
if (dump_sensor && sd->sensor->dump)
sd->sensor->dump(sd);
return (err < 0) ? err : 0;
}
/* Start the camera */
static int stv06xx_start(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
int err;
/* Prepare the sensor for start */
err = sd->sensor->start(sd);
if (err < 0)
goto out;
/* Start isochronous streaming */
err = stv06xx_write_bridge(sd, STV_ISO_ENABLE, 1);
out:
if (err < 0)
PDEBUG(D_STREAM, "Starting stream failed");
else
PDEBUG(D_STREAM, "Started streaming");
return (err < 0) ? err : 0;
}
static void stv06xx_stopN(struct gspca_dev *gspca_dev)
{
int err;
struct sd *sd = (struct sd *) gspca_dev;
/* stop ISO-streaming */
err = stv06xx_write_bridge(sd, STV_ISO_ENABLE, 0);
if (err < 0)
goto out;
err = sd->sensor->stop(sd);
out:
if (err < 0)
PDEBUG(D_STREAM, "Failed to stop stream");
else
PDEBUG(D_STREAM, "Stopped streaming");
}
/*
* Analyse an USB packet of the data stream and store it appropriately.
* Each packet contains an integral number of chunks. Each chunk has
* 2-bytes identification, followed by 2-bytes that describe the chunk
* length. Known/guessed chunk identifications are:
* 8001/8005/C001/C005 - Begin new frame
* 8002/8006/C002/C006 - End frame
* 0200/4200 - Contains actual image data, bayer or compressed
* 0005 - 11 bytes of unknown data
* 0100 - 2 bytes of unknown data
* The 0005 and 0100 chunks seem to appear only in compressed stream.
*/
static void stv06xx_pkt_scan(struct gspca_dev *gspca_dev,
struct gspca_frame *frame, /* target */
__u8 *data, /* isoc packet */
int len) /* iso packet length */
{
struct sd *sd = (struct sd *) gspca_dev;
PDEBUG(D_PACK, "Packet of length %d arrived", len);
/* A packet may contain several frames
loop until the whole packet is reached */
while (len) {
int id, chunk_len;
if (len < 4) {
PDEBUG(D_PACK, "Packet is smaller than 4 bytes");
return;
}
/* Capture the id */
id = (data[0] << 8) | data[1];
/* Capture the chunk length */
chunk_len = (data[2] << 8) | data[3];
PDEBUG(D_PACK, "Chunk id: %x, length: %d", id, chunk_len);
data += 4;
len -= 4;
if (len < chunk_len) {
PDEBUG(D_ERR, "URB packet length is smaller"
" than the specified chunk length");
gspca_dev->last_packet_type = DISCARD_PACKET;
return;
}
/* First byte seem to be 02=data 2nd byte is unknown??? */
if (sd->bridge == BRIDGE_ST6422 && (id & 0xFF00) == 0x0200)
goto frame_data;
switch (id) {
case 0x0200:
case 0x4200:
frame_data:
PDEBUG(D_PACK, "Frame data packet detected");
if (sd->to_skip) {
int skip = (sd->to_skip < chunk_len) ?
sd->to_skip : chunk_len;
data += skip;
len -= skip;
chunk_len -= skip;
sd->to_skip -= skip;
}
gspca_frame_add(gspca_dev, INTER_PACKET, frame,
data, chunk_len);
break;
case 0x8001:
case 0x8005:
case 0xc001:
case 0xc005:
PDEBUG(D_PACK, "Starting new frame");
/* Create a new frame, chunk length should be zero */
gspca_frame_add(gspca_dev, FIRST_PACKET,
frame, data, 0);
if (sd->bridge == BRIDGE_ST6422)
sd->to_skip = gspca_dev->width * 4;
if (chunk_len)
PDEBUG(D_ERR, "Chunk length is "
"non-zero on a SOF");
break;
case 0x8002:
case 0x8006:
case 0xc002:
PDEBUG(D_PACK, "End of frame detected");
/* Complete the last frame (if any) */
frame = gspca_frame_add(gspca_dev, LAST_PACKET,
frame, data, 0);
if (chunk_len)
PDEBUG(D_ERR, "Chunk length is "
"non-zero on a EOF");
break;
case 0x0005:
PDEBUG(D_PACK, "Chunk 0x005 detected");
/* Unknown chunk with 11 bytes of data,
occurs just before end of each frame
in compressed mode */
break;
case 0x0100:
PDEBUG(D_PACK, "Chunk 0x0100 detected");
/* Unknown chunk with 2 bytes of data,
occurs 2-3 times per USB interrupt */
break;
case 0x42ff:
PDEBUG(D_PACK, "Chunk 0x42ff detected");
/* Special chunk seen sometimes on the ST6422 */
break;
default:
PDEBUG(D_PACK, "Unknown chunk 0x%04x detected", id);
/* Unknown chunk */
}
data += chunk_len;
len -= chunk_len;
}
}
static int stv06xx_config(struct gspca_dev *gspca_dev,
const struct usb_device_id *id);
/* sub-driver description */
static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
.config = stv06xx_config,
.init = stv06xx_init,
.start = stv06xx_start,
.stopN = stv06xx_stopN,
.pkt_scan = stv06xx_pkt_scan
};
/* 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;
struct cam *cam;
PDEBUG(D_PROBE, "Configuring camera");
cam = &gspca_dev->cam;
sd->desc = sd_desc;
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 __devinitdata struct usb_device_id device_table[] = {
/* QuickCam Express */
{USB_DEVICE(0x046d, 0x0840), .driver_info = BRIDGE_STV600 },
/* LEGO cam / QuickCam Web */
{USB_DEVICE(0x046d, 0x0850), .driver_info = BRIDGE_STV610 },
/* Dexxa WebCam USB */
{USB_DEVICE(0x046d, 0x0870), .driver_info = BRIDGE_STV602 },
/* QuickCam Messenger */
{USB_DEVICE(0x046D, 0x08F0), .driver_info = BRIDGE_ST6422 },
/* QuickCam Communicate */
{USB_DEVICE(0x046D, 0x08F5), .driver_info = BRIDGE_ST6422 },
/* QuickCam Messenger (new) */
{USB_DEVICE(0x046D, 0x08F6), .driver_info = BRIDGE_ST6422 },
{}
};
MODULE_DEVICE_TABLE(usb, device_table);
/* -- device connect -- */
static int sd_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
PDEBUG(D_PROBE, "Probing for a stv06xx device");
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;
PDEBUG(D_PROBE, "Disconnecting the stv06xx device");
if (sd->sensor->disconnect)
sd->sensor->disconnect(sd);
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,
#endif
};
/* -- module insert / remove -- */
static int __init sd_mod_init(void)
{
int ret;
ret = usb_register(&sd_driver);
if (ret < 0)
return ret;
PDEBUG(D_PROBE, "registered");
return 0;
}
static void __exit sd_mod_exit(void)
{
usb_deregister(&sd_driver);
PDEBUG(D_PROBE, "deregistered");
}
module_init(sd_mod_init);
module_exit(sd_mod_exit);
module_param(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");

View File

@@ -0,0 +1,114 @@
/*
* Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
* Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
* Copyright (c) 2002, 2003 Tuukka Toivonen
* Copyright (c) 2008 Erik Andrén
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* P/N 861037: Sensor HDCS1000 ASIC STV0600
* P/N 861050-0010: Sensor HDCS1000 ASIC STV0600
* P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express
* P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam
* P/N 861075-0040: Sensor HDCS1000 ASIC
* P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB
* P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web
*/
#ifndef STV06XX_H_
#define STV06XX_H_
#include "gspca.h"
#define MODULE_NAME "STV06xx"
#define STV_ISOC_ENDPOINT_ADDR 0x81
#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
/* 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;
/* A pointer to the sd_desc struct */
struct sd_desc desc;
/* 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

View File

@@ -0,0 +1,610 @@
/*
* Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
* Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
* Copyright (c) 2002, 2003 Tuukka Toivonen
* Copyright (c) 2008 Erik Andrén
* Copyright (c) 2008 Chia-I Wu
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* P/N 861037: Sensor HDCS1000 ASIC STV0600
* P/N 861050-0010: Sensor HDCS1000 ASIC STV0600
* P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express
* P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam
* P/N 861075-0040: Sensor HDCS1000 ASIC
* P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB
* P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web
*/
#include "stv06xx_hdcs.h"
static const struct ctrl hdcs1x00_ctrl[] = {
{
{
.id = V4L2_CID_EXPOSURE,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "exposure",
.minimum = 0x00,
.maximum = 0xff,
.step = 0x1,
.default_value = HDCS_DEFAULT_EXPOSURE,
.flags = V4L2_CTRL_FLAG_SLIDER
},
.set = hdcs_set_exposure,
.get = hdcs_get_exposure
}, {
{
.id = V4L2_CID_GAIN,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "gain",
.minimum = 0x00,
.maximum = 0xff,
.step = 0x1,
.default_value = HDCS_DEFAULT_GAIN,
.flags = V4L2_CTRL_FLAG_SLIDER
},
.set = hdcs_set_gain,
.get = hdcs_get_gain
}
};
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 const struct ctrl hdcs1020_ctrl[] = {
{
{
.id = V4L2_CID_EXPOSURE,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "exposure",
.minimum = 0x00,
.maximum = 0xffff,
.step = 0x1,
.default_value = HDCS_DEFAULT_EXPOSURE,
.flags = V4L2_CTRL_FLAG_SLIDER
},
.set = hdcs_set_exposure,
.get = hdcs_get_exposure
}, {
{
.id = V4L2_CID_GAIN,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "gain",
.minimum = 0x00,
.maximum = 0xff,
.step = 0x1,
.default_value = HDCS_DEFAULT_GAIN,
.flags = V4L2_CTRL_FLAG_SLIDER
},
.set = hdcs_set_gain,
.get = hdcs_get_gain
}
};
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;
u8 exp_cache, gain_cache;
};
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_get_exposure(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
struct hdcs *hdcs = sd->sensor_priv;
*val = hdcs->exp_cache;
return 0;
}
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];
val &= 0xff;
hdcs->exp_cache = val;
cycles = val * HDCS_CLK_FREQ_MHZ * 257;
ct = hdcs->exp.cto + hdcs->psmp + (HDCS_ADC_START_SIG_DUR + 2);
cp = hdcs->exp.cto + (hdcs->w * ct / 2);
/* the cycles one row takes */
rp = hdcs->exp.rs + cp;
rowexp = cycles / rp;
/* the remaining cycles */
cycles -= rowexp * rp;
/* calculate sub-row exposure */
if (IS_1020(sd)) {
/* see HDCS-1020 datasheet 3.5.6.4, p. 63 */
srowexp = hdcs->w - (cycles + hdcs->exp.er + 13) / ct;
mnct = (hdcs->exp.er + 12 + ct - 1) / ct;
max_srowexp = hdcs->w - mnct;
} else {
/* see HDCS-1000 datasheet 3.4.5.5, p. 61 */
srowexp = cp - hdcs->exp.er - 6 - cycles;
mnct = (hdcs->exp.er + 5 + ct - 1) / ct;
max_srowexp = cp - mnct * ct - 1;
}
if (srowexp < 0)
srowexp = 0;
else if (srowexp > max_srowexp)
srowexp = max_srowexp;
if (IS_1020(sd)) {
exp[0] = HDCS20_CONTROL;
exp[1] = 0x00; /* Stop streaming */
exp[2] = HDCS_ROWEXPL;
exp[3] = rowexp & 0xff;
exp[4] = HDCS_ROWEXPH;
exp[5] = rowexp >> 8;
exp[6] = HDCS20_SROWEXP;
exp[7] = (srowexp >> 2) & 0xff;
exp[8] = HDCS20_ERROR;
exp[9] = 0x10; /* Clear exposure error flag*/
exp[10] = HDCS20_CONTROL;
exp[11] = 0x04; /* Restart streaming */
err = stv06xx_write_sensor_bytes(sd, exp, 6);
} else {
exp[0] = HDCS00_CONTROL;
exp[1] = 0x00; /* Stop streaming */
exp[2] = HDCS_ROWEXPL;
exp[3] = rowexp & 0xff;
exp[4] = HDCS_ROWEXPH;
exp[5] = rowexp >> 8;
exp[6] = HDCS00_SROWEXPL;
exp[7] = srowexp & 0xff;
exp[8] = HDCS00_SROWEXPH;
exp[9] = srowexp >> 8;
exp[10] = HDCS_STATUS;
exp[11] = 0x10; /* Clear exposure error flag*/
exp[12] = HDCS00_CONTROL;
exp[13] = 0x04; /* Restart streaming */
err = stv06xx_write_sensor_bytes(sd, exp, 7);
if (err < 0)
return err;
}
PDEBUG(D_V4L2, "Writing exposure %d, rowexp %d, srowexp %d",
val, rowexp, srowexp);
return err;
}
static int hdcs_set_gains(struct sd *sd, u8 g)
{
struct hdcs *hdcs = sd->sensor_priv;
int err;
u8 gains[4];
hdcs->gain_cache = g;
/* 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_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
struct hdcs *hdcs = sd->sensor_priv;
*val = hdcs->gain_cache;
return 0;
}
static int hdcs_set_gain(struct gspca_dev *gspca_dev, __s32 val)
{
PDEBUG(D_V4L2, "Writing gain %d", val);
return hdcs_set_gains((struct sd *) gspca_dev,
val & 0xff);
}
static int hdcs_set_size(struct sd *sd,
unsigned int width, unsigned int height)
{
struct hdcs *hdcs = sd->sensor_priv;
u8 win[4];
unsigned int x, y;
int err;
/* must be multiple of 4 */
width = (width + 3) & ~0x3;
height = (height + 3) & ~0x3;
if (width > hdcs->array.width)
width = hdcs->array.width;
if (IS_1020(sd)) {
/* the borders are also invalid */
if (height + 2 * hdcs->array.border + HDCS_1020_BOTTOM_Y_SKIP
> hdcs->array.height)
height = hdcs->array.height - 2 * hdcs->array.border -
HDCS_1020_BOTTOM_Y_SKIP;
y = (hdcs->array.height - HDCS_1020_BOTTOM_Y_SKIP - height) / 2
+ hdcs->array.top;
} else {
if (height > hdcs->array.height)
height = hdcs->array.height;
y = hdcs->array.top + (hdcs->array.height - height) / 2;
}
x = hdcs->array.left + (hdcs->array.width - width) / 2;
win[0] = y / 4;
win[1] = x / 4;
win[2] = (y + height) / 4 - 1;
win[3] = (x + width) / 4 - 1;
err = hdcs_reg_write_seq(sd, HDCS_FWROW, win, 4);
if (err < 0)
return err;
/* Update the current width and height */
hdcs->w = width;
hdcs->h = height;
return err;
}
static int hdcs_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;
info("HDCS-1000/1100 sensor detected");
sd->gspca_dev.cam.cam_mode = hdcs1x00_mode;
sd->gspca_dev.cam.nmodes = ARRAY_SIZE(hdcs1x00_mode);
sd->desc.ctrls = hdcs1x00_ctrl;
sd->desc.nctrls = ARRAY_SIZE(hdcs1x00_ctrl);
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;
info("HDCS-1020 sensor detected");
sd->gspca_dev.cam.cam_mode = hdcs1020_mode;
sd->gspca_dev.cam.nmodes = ARRAY_SIZE(hdcs1020_mode);
sd->desc.ctrls = hdcs1020_ctrl;
sd->desc.nctrls = ARRAY_SIZE(hdcs1020_ctrl);
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)
{
PDEBUG(D_STREAM, "Starting stream");
return hdcs_set_state(sd, HDCS_STATE_RUN);
}
static int hdcs_stop(struct sd *sd)
{
PDEBUG(D_STREAM, "Halting stream");
return hdcs_set_state(sd, HDCS_STATE_SLEEP);
}
static void hdcs_disconnect(struct sd *sd)
{
PDEBUG(D_PROBE, "Disconnecting the sensor");
kfree(sd->sensor_priv);
}
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 continous 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;
err = hdcs_set_gains(sd, HDCS_DEFAULT_GAIN);
if (err < 0)
return err;
err = hdcs_set_size(sd, hdcs->array.width, hdcs->array.height);
if (err < 0)
return err;
err = hdcs_set_exposure(&sd->gspca_dev, HDCS_DEFAULT_EXPOSURE);
return err;
}
static int hdcs_dump(struct sd *sd)
{
u16 reg, val;
info("Dumping sensor registers:");
for (reg = HDCS_IDENT; reg <= HDCS_ROWEXPH; reg++) {
stv06xx_read_sensor(sd, reg, &val);
info("reg 0x%02x = 0x%02x", reg, val);
}
return 0;
}

View File

@@ -0,0 +1,198 @@
/*
* Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
* Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
* Copyright (c) 2002, 2003 Tuukka Toivonen
* Copyright (c) 2008 Erik Andrén
* Copyright (c) 2008 Chia-I Wu
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* P/N 861037: Sensor HDCS1000 ASIC STV0600
* P/N 861050-0010: Sensor HDCS1000 ASIC STV0600
* P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express
* P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam
* P/N 861075-0040: Sensor HDCS1000 ASIC
* P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB
* P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web
*/
#ifndef STV06XX_HDCS_H_
#define STV06XX_HDCS_H_
#include "stv06xx_sensor.h"
#define HDCS_REG_CONFIG(sd) (IS_1020(sd) ? HDCS20_CONFIG : HDCS00_CONFIG)
#define HDCS_REG_CONTROL(sd) (IS_1020(sd) ? HDCS20_CONTROL : HDCS00_CONTROL)
#define HDCS_1X00_DEF_WIDTH 360
#define HDCS_1X00_DEF_HEIGHT 296
#define HDCS_1020_DEF_WIDTH 352
#define HDCS_1020_DEF_HEIGHT 292
#define HDCS_1020_BOTTOM_Y_SKIP 4
#define HDCS_CLK_FREQ_MHZ 25
#define HDCS_ADC_START_SIG_DUR 3
/* LSB bit of I2C or register address signifies write (0) or read (1) */
/* I2C Registers common for both HDCS-1000/1100 and HDCS-1020 */
/* Identifications Register */
#define HDCS_IDENT (0x00 << 1)
/* Status Register */
#define HDCS_STATUS (0x01 << 1)
/* Interrupt Mask Register */
#define HDCS_IMASK (0x02 << 1)
/* Pad Control Register */
#define HDCS_PCTRL (0x03 << 1)
/* Pad Drive Control Register */
#define HDCS_PDRV (0x04 << 1)
/* Interface Control Register */
#define HDCS_ICTRL (0x05 << 1)
/* Interface Timing Register */
#define HDCS_ITMG (0x06 << 1)
/* Baud Fraction Register */
#define HDCS_BFRAC (0x07 << 1)
/* Baud Rate Register */
#define HDCS_BRATE (0x08 << 1)
/* ADC Control Register */
#define HDCS_ADCCTRL (0x09 << 1)
/* First Window Row Register */
#define HDCS_FWROW (0x0a << 1)
/* First Window Column Register */
#define HDCS_FWCOL (0x0b << 1)
/* Last Window Row Register */
#define HDCS_LWROW (0x0c << 1)
/* Last Window Column Register */
#define HDCS_LWCOL (0x0d << 1)
/* Timing Control Register */
#define HDCS_TCTRL (0x0e << 1)
/* PGA Gain Register: Even Row, Even Column */
#define HDCS_ERECPGA (0x0f << 1)
/* PGA Gain Register: Even Row, Odd Column */
#define HDCS_EROCPGA (0x10 << 1)
/* PGA Gain Register: Odd Row, Even Column */
#define HDCS_ORECPGA (0x11 << 1)
/* PGA Gain Register: Odd Row, Odd Column */
#define HDCS_OROCPGA (0x12 << 1)
/* Row Exposure Low Register */
#define HDCS_ROWEXPL (0x13 << 1)
/* Row Exposure High Register */
#define HDCS_ROWEXPH (0x14 << 1)
/* I2C Registers only for HDCS-1000/1100 */
/* Sub-Row Exposure Low Register */
#define HDCS00_SROWEXPL (0x15 << 1)
/* Sub-Row Exposure High Register */
#define HDCS00_SROWEXPH (0x16 << 1)
/* Configuration Register */
#define HDCS00_CONFIG (0x17 << 1)
/* Control Register */
#define HDCS00_CONTROL (0x18 << 1)
/* I2C Registers only for HDCS-1020 */
/* Sub-Row Exposure Register */
#define HDCS20_SROWEXP (0x15 << 1)
/* Error Control Register */
#define HDCS20_ERROR (0x16 << 1)
/* Interface Timing 2 Register */
#define HDCS20_ITMG2 (0x17 << 1)
/* Interface Control 2 Register */
#define HDCS20_ICTRL2 (0x18 << 1)
/* Horizontal Blank Register */
#define HDCS20_HBLANK (0x19 << 1)
/* Vertical Blank Register */
#define HDCS20_VBLANK (0x1a << 1)
/* Configuration Register */
#define HDCS20_CONFIG (0x1b << 1)
/* Control Register */
#define HDCS20_CONTROL (0x1c << 1)
#define HDCS_RUN_ENABLE (1 << 2)
#define HDCS_SLEEP_MODE (1 << 1)
#define HDCS_DEFAULT_EXPOSURE 48
#define HDCS_DEFAULT_GAIN 128
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_stop(struct sd *sd);
static int hdcs_dump(struct sd *sd);
static void hdcs_disconnect(struct sd *sd);
static int hdcs_get_exposure(struct gspca_dev *gspca_dev, __s32 *val);
static int hdcs_set_exposure(struct gspca_dev *gspca_dev, __s32 val);
static int hdcs_set_gain(struct gspca_dev *gspca_dev, __s32 val);
static int hdcs_get_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,
.init = hdcs_init,
.probe = hdcs_probe_1x00,
.start = hdcs_start,
.stop = hdcs_stop,
.disconnect = hdcs_disconnect,
.dump = hdcs_dump,
};
const struct stv06xx_sensor stv06xx_sensor_hdcs1020 = {
.name = "HDCS-1020",
.i2c_flush = 0,
.i2c_addr = (0x55 << 1),
.i2c_len = 1,
.init = hdcs_init,
.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_ISO_SIZE_L, 847},
{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

View File

@@ -0,0 +1,561 @@
/*
* Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
* Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
* Copyright (c) 2002, 2003 Tuukka Toivonen
* Copyright (c) 2008 Erik Andrén
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* P/N 861037: Sensor HDCS1000 ASIC STV0600
* P/N 861050-0010: Sensor HDCS1000 ASIC STV0600
* P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express
* P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam
* P/N 861075-0040: Sensor HDCS1000 ASIC
* P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB
* P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web
*/
/*
* The spec file for the PB-0100 suggests the following for best quality
* images after the sensor has been reset :
*
* PB_ADCGAINL = R60 = 0x03 (3 dec) : sets low reference of ADC
to produce good black level
* PB_PREADCTRL = R32 = 0x1400 (5120 dec) : Enables global gain changes
through R53
* PB_ADCMINGAIN = R52 = 0x10 (16 dec) : Sets the minimum gain for
auto-exposure
* PB_ADCGLOBALGAIN = R53 = 0x10 (16 dec) : Sets the global gain
* PB_EXPGAIN = R14 = 0x11 (17 dec) : Sets the auto-exposure value
* PB_UPDATEINT = R23 = 0x02 (2 dec) : Sets the speed on
auto-exposure routine
* PB_CFILLIN = R5 = 0x0E (14 dec) : Sets the frame rate
*/
#include "stv06xx_pb0100.h"
static const struct ctrl pb0100_ctrl[] = {
#define GAIN_IDX 0
{
{
.id = V4L2_CID_GAIN,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Gain",
.minimum = 0,
.maximum = 255,
.step = 1,
.default_value = 128
},
.set = pb0100_set_gain,
.get = pb0100_get_gain
},
#define RED_BALANCE_IDX 1
{
{
.id = V4L2_CID_RED_BALANCE,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Red Balance",
.minimum = -255,
.maximum = 255,
.step = 1,
.default_value = 0
},
.set = pb0100_set_red_balance,
.get = pb0100_get_red_balance
},
#define BLUE_BALANCE_IDX 2
{
{
.id = V4L2_CID_BLUE_BALANCE,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Blue Balance",
.minimum = -255,
.maximum = 255,
.step = 1,
.default_value = 0
},
.set = pb0100_set_blue_balance,
.get = pb0100_get_blue_balance
},
#define EXPOSURE_IDX 3
{
{
.id = V4L2_CID_EXPOSURE,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Exposure",
.minimum = 0,
.maximum = 511,
.step = 1,
.default_value = 12
},
.set = pb0100_set_exposure,
.get = pb0100_get_exposure
},
#define AUTOGAIN_IDX 4
{
{
.id = V4L2_CID_AUTOGAIN,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.name = "Automatic Gain and Exposure",
.minimum = 0,
.maximum = 1,
.step = 1,
.default_value = 1
},
.set = pb0100_set_autogain,
.get = pb0100_get_autogain
},
#define AUTOGAIN_TARGET_IDX 5
{
{
.id = V4L2_CTRL_CLASS_USER + 0x1000,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Automatic Gain Target",
.minimum = 0,
.maximum = 255,
.step = 1,
.default_value = 128
},
.set = pb0100_set_autogain_target,
.get = pb0100_get_autogain_target
},
#define NATURAL_IDX 6
{
{
.id = V4L2_CTRL_CLASS_USER + 0x1001,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.name = "Natural Light Source",
.minimum = 0,
.maximum = 1,
.step = 1,
.default_value = 1
},
.set = pb0100_set_natural,
.get = pb0100_get_natural
}
};
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_probe(struct sd *sd)
{
u16 sensor;
int i, err;
s32 *sensor_settings;
err = stv06xx_read_sensor(sd, PB_IDENT, &sensor);
if (err < 0)
return -ENODEV;
if ((sensor >> 8) == 0x64) {
sensor_settings = kmalloc(
ARRAY_SIZE(pb0100_ctrl) * sizeof(s32),
GFP_KERNEL);
if (!sensor_settings)
return -ENOMEM;
info("Photobit pb0100 sensor detected");
sd->gspca_dev.cam.cam_mode = pb0100_mode;
sd->gspca_dev.cam.nmodes = ARRAY_SIZE(pb0100_mode);
sd->desc.ctrls = pb0100_ctrl;
sd->desc.nctrls = ARRAY_SIZE(pb0100_ctrl);
for (i = 0; i < sd->desc.nctrls; i++)
sensor_settings[i] = pb0100_ctrl[i].qctrl.default_value;
sd->sensor_priv = sensor_settings;
return 0;
}
return -ENODEV;
}
static int pb0100_start(struct sd *sd)
{
int err;
struct cam *cam = &sd->gspca_dev.cam;
s32 *sensor_settings = sd->sensor_priv;
u32 mode = cam->cam_mode[sd->gspca_dev.curr_mode].priv;
/* 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);
}
/* set_gain also sets red and blue balance */
pb0100_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]);
pb0100_set_exposure(&sd->gspca_dev, sensor_settings[EXPOSURE_IDX]);
pb0100_set_autogain_target(&sd->gspca_dev,
sensor_settings[AUTOGAIN_TARGET_IDX]);
pb0100_set_autogain(&sd->gspca_dev, sensor_settings[AUTOGAIN_IDX]);
err = stv06xx_write_sensor(sd, PB_CONTROL, BIT(5)|BIT(3)|BIT(1));
PDEBUG(D_STREAM, "Started stream, status: %d", err);
return (err < 0) ? err : 0;
}
static int pb0100_stop(struct sd *sd)
{
int err;
err = stv06xx_write_sensor(sd, PB_ABORTFRAME, 1);
if (err < 0)
goto out;
/* Set bit 1 to zero */
err = stv06xx_write_sensor(sd, PB_CONTROL, BIT(5)|BIT(3));
PDEBUG(D_STREAM, "Halting stream");
out:
return (err < 0) ? err : 0;
}
static void pb0100_disconnect(struct sd *sd)
{
sd->sensor = NULL;
kfree(sd->sensor_priv);
}
/* 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);
/* ISO-Size (0x27b: 635... why? - HDCS uses 847) */
stv06xx_write_bridge(sd, STV_ISO_SIZE_L, 847);
/* 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_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
*val = sensor_settings[GAIN_IDX];
return 0;
}
static int pb0100_set_gain(struct gspca_dev *gspca_dev, __s32 val)
{
int err;
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
if (sensor_settings[AUTOGAIN_IDX])
return -EBUSY;
sensor_settings[GAIN_IDX] = val;
err = stv06xx_write_sensor(sd, PB_G1GAIN, val);
if (!err)
err = stv06xx_write_sensor(sd, PB_G2GAIN, val);
PDEBUG(D_V4L2, "Set green gain to %d, status: %d", val, err);
if (!err)
err = pb0100_set_red_balance(gspca_dev,
sensor_settings[RED_BALANCE_IDX]);
if (!err)
err = pb0100_set_blue_balance(gspca_dev,
sensor_settings[BLUE_BALANCE_IDX]);
return err;
}
static int pb0100_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
*val = sensor_settings[RED_BALANCE_IDX];
return 0;
}
static int pb0100_set_red_balance(struct gspca_dev *gspca_dev, __s32 val)
{
int err;
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
if (sensor_settings[AUTOGAIN_IDX])
return -EBUSY;
sensor_settings[RED_BALANCE_IDX] = val;
val += sensor_settings[GAIN_IDX];
if (val < 0)
val = 0;
else if (val > 255)
val = 255;
err = stv06xx_write_sensor(sd, PB_RGAIN, val);
PDEBUG(D_V4L2, "Set red gain to %d, status: %d", val, err);
return err;
}
static int pb0100_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
*val = sensor_settings[BLUE_BALANCE_IDX];
return 0;
}
static int pb0100_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val)
{
int err;
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
if (sensor_settings[AUTOGAIN_IDX])
return -EBUSY;
sensor_settings[BLUE_BALANCE_IDX] = val;
val += sensor_settings[GAIN_IDX];
if (val < 0)
val = 0;
else if (val > 255)
val = 255;
err = stv06xx_write_sensor(sd, PB_BGAIN, val);
PDEBUG(D_V4L2, "Set blue gain to %d, status: %d", val, err);
return err;
}
static int pb0100_get_exposure(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
*val = sensor_settings[EXPOSURE_IDX];
return 0;
}
static int pb0100_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
{
int err;
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
if (sensor_settings[AUTOGAIN_IDX])
return -EBUSY;
sensor_settings[EXPOSURE_IDX] = val;
err = stv06xx_write_sensor(sd, PB_RINTTIME, val);
PDEBUG(D_V4L2, "Set exposure to %d, status: %d", val, err);
return err;
}
static int pb0100_get_autogain(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
*val = sensor_settings[AUTOGAIN_IDX];
return 0;
}
static int pb0100_set_autogain(struct gspca_dev *gspca_dev, __s32 val)
{
int err;
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
sensor_settings[AUTOGAIN_IDX] = val;
if (sensor_settings[AUTOGAIN_IDX]) {
if (sensor_settings[NATURAL_IDX])
val = BIT(6)|BIT(4)|BIT(0);
else
val = BIT(4)|BIT(0);
} else
val = 0;
err = stv06xx_write_sensor(sd, PB_EXPGAIN, val);
PDEBUG(D_V4L2, "Set autogain to %d (natural: %d), status: %d",
sensor_settings[AUTOGAIN_IDX], sensor_settings[NATURAL_IDX],
err);
return err;
}
static int pb0100_get_autogain_target(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
*val = sensor_settings[AUTOGAIN_TARGET_IDX];
return 0;
}
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;
s32 *sensor_settings = sd->sensor_priv;
sensor_settings[AUTOGAIN_TARGET_IDX] = val;
/* Number of pixels counted by the sensor when subsampling the pixels.
* Slightly larger than the real value to avoid oscillation */
totalpixels = gspca_dev->width * gspca_dev->height;
totalpixels = totalpixels/(8*8) + totalpixels/(64*64);
brightpixels = (totalpixels * val) >> 8;
darkpixels = totalpixels - brightpixels;
err = stv06xx_write_sensor(sd, PB_R21, brightpixels);
if (!err)
err = stv06xx_write_sensor(sd, PB_R22, darkpixels);
PDEBUG(D_V4L2, "Set autogain target to %d, status: %d", val, err);
return err;
}
static int pb0100_get_natural(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
*val = sensor_settings[NATURAL_IDX];
return 0;
}
static int pb0100_set_natural(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
sensor_settings[NATURAL_IDX] = val;
return pb0100_set_autogain(gspca_dev, sensor_settings[AUTOGAIN_IDX]);
}

View File

@@ -0,0 +1,149 @@
/*
* Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
* Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
* Copyright (c) 2002, 2003 Tuukka Toivonen
* Copyright (c) 2008 Erik Andrén
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* P/N 861037: Sensor HDCS1000 ASIC STV0600
* P/N 861050-0010: Sensor HDCS1000 ASIC STV0600
* P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express
* P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam
* P/N 861075-0040: Sensor HDCS1000 ASIC
* P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB
* P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web
*/
#ifndef STV06XX_PB0100_H_
#define STV06XX_PB0100_H_
#include "stv06xx_sensor.h"
/* mode priv field flags */
#define PB0100_CROP_TO_VGA 0x01
#define PB0100_SUBSAMPLE 0x02
/* I2C Registers */
#define PB_IDENT 0x00 /* Chip Version */
#define PB_RSTART 0x01 /* Row Window Start */
#define PB_CSTART 0x02 /* Column Window Start */
#define PB_RWSIZE 0x03 /* Row Window Size */
#define PB_CWSIZE 0x04 /* Column Window Size */
#define PB_CFILLIN 0x05 /* Column Fill-In */
#define PB_VBL 0x06 /* Vertical Blank Count */
#define PB_CONTROL 0x07 /* Control Mode */
#define PB_FINTTIME 0x08 /* Integration Time/Frame Unit Count */
#define PB_RINTTIME 0x09 /* Integration Time/Row Unit Count */
#define PB_ROWSPEED 0x0a /* Row Speed Control */
#define PB_ABORTFRAME 0x0b /* Abort Frame */
#define PB_R12 0x0c /* Reserved */
#define PB_RESET 0x0d /* Reset */
#define PB_EXPGAIN 0x0e /* Exposure Gain Command */
#define PB_R15 0x0f /* Expose0 */
#define PB_R16 0x10 /* Expose1 */
#define PB_R17 0x11 /* Expose2 */
#define PB_R18 0x12 /* Low0_DAC */
#define PB_R19 0x13 /* Low1_DAC */
#define PB_R20 0x14 /* Low2_DAC */
#define PB_R21 0x15 /* Threshold11 */
#define PB_R22 0x16 /* Threshold0x */
#define PB_UPDATEINT 0x17 /* Update Interval */
#define PB_R24 0x18 /* High_DAC */
#define PB_R25 0x19 /* Trans0H */
#define PB_R26 0x1a /* Trans1L */
#define PB_R27 0x1b /* Trans1H */
#define PB_R28 0x1c /* Trans2L */
#define PB_R29 0x1d /* Reserved */
#define PB_R30 0x1e /* Reserved */
#define PB_R31 0x1f /* Wait to Read */
#define PB_PREADCTRL 0x20 /* Pixel Read Control Mode */
#define PB_R33 0x21 /* IREF_VLN */
#define PB_R34 0x22 /* IREF_VLP */
#define PB_R35 0x23 /* IREF_VLN_INTEG */
#define PB_R36 0x24 /* IREF_MASTER */
#define PB_R37 0x25 /* IDACP */
#define PB_R38 0x26 /* IDACN */
#define PB_R39 0x27 /* DAC_Control_Reg */
#define PB_R40 0x28 /* VCL */
#define PB_R41 0x29 /* IREF_VLN_ADCIN */
#define PB_R42 0x2a /* Reserved */
#define PB_G1GAIN 0x2b /* Green 1 Gain */
#define PB_BGAIN 0x2c /* Blue Gain */
#define PB_RGAIN 0x2d /* Red Gain */
#define PB_G2GAIN 0x2e /* Green 2 Gain */
#define PB_R47 0x2f /* Dark Row Address */
#define PB_R48 0x30 /* Dark Row Options */
#define PB_R49 0x31 /* Reserved */
#define PB_R50 0x32 /* Image Test Data */
#define PB_ADCMAXGAIN 0x33 /* Maximum Gain */
#define PB_ADCMINGAIN 0x34 /* Minimum Gain */
#define PB_ADCGLOBALGAIN 0x35 /* Global Gain */
#define PB_R54 0x36 /* Maximum Frame */
#define PB_R55 0x37 /* Minimum Frame */
#define PB_R56 0x38 /* Reserved */
#define PB_VOFFSET 0x39 /* VOFFSET */
#define PB_R58 0x3a /* Snap-Shot Sequence Trigger */
#define PB_ADCGAINH 0x3b /* VREF_HI */
#define PB_ADCGAINL 0x3c /* VREF_LO */
#define PB_R61 0x3d /* Reserved */
#define PB_R62 0x3e /* Reserved */
#define PB_R63 0x3f /* Reserved */
#define PB_R64 0x40 /* Red/Blue Gain */
#define PB_R65 0x41 /* Green 2/Green 1 Gain */
#define PB_R66 0x42 /* VREF_HI/LO */
#define PB_R67 0x43 /* Integration Time/Row Unit Count */
#define PB_R240 0xf0 /* ADC Test */
#define PB_R241 0xf1 /* Chip Enable */
#define PB_R242 0xf2 /* Reserved */
static int pb0100_probe(struct sd *sd);
static int pb0100_start(struct sd *sd);
static int pb0100_init(struct sd *sd);
static int pb0100_stop(struct sd *sd);
static int pb0100_dump(struct sd *sd);
static void pb0100_disconnect(struct sd *sd);
/* V4L2 controls supported by the driver */
static int pb0100_get_gain(struct gspca_dev *gspca_dev, __s32 *val);
static int pb0100_set_gain(struct gspca_dev *gspca_dev, __s32 val);
static int pb0100_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val);
static int pb0100_set_red_balance(struct gspca_dev *gspca_dev, __s32 val);
static int pb0100_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val);
static int pb0100_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val);
static int pb0100_get_exposure(struct gspca_dev *gspca_dev, __s32 *val);
static int pb0100_set_exposure(struct gspca_dev *gspca_dev, __s32 val);
static int pb0100_get_autogain(struct gspca_dev *gspca_dev, __s32 *val);
static int pb0100_set_autogain(struct gspca_dev *gspca_dev, __s32 val);
static int pb0100_get_autogain_target(struct gspca_dev *gspca_dev, __s32 *val);
static int pb0100_set_autogain_target(struct gspca_dev *gspca_dev, __s32 val);
static int pb0100_get_natural(struct gspca_dev *gspca_dev, __s32 *val);
static int pb0100_set_natural(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,
.init = pb0100_init,
.probe = pb0100_probe,
.start = pb0100_start,
.stop = pb0100_stop,
.dump = pb0100_dump,
.disconnect = pb0100_disconnect,
};
#endif

View File

@@ -0,0 +1,83 @@
/*
* Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
* Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
* Copyright (c) 2002, 2003 Tuukka Toivonen
* Copyright (c) 2008 Erik Andrén
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* P/N 861037: Sensor HDCS1000 ASIC STV0600
* P/N 861050-0010: Sensor HDCS1000 ASIC STV0600
* P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express
* P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam
* P/N 861075-0040: Sensor HDCS1000 ASIC
* P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB
* P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web
*/
#ifndef STV06XX_SENSOR_H_
#define STV06XX_SENSOR_H_
#include "stv06xx.h"
#define IS_1020(sd) ((sd)->sensor == &stv06xx_sensor_hdcs1020)
extern const struct stv06xx_sensor stv06xx_sensor_vv6410;
extern const struct stv06xx_sensor stv06xx_sensor_hdcs1x00;
extern const struct stv06xx_sensor stv06xx_sensor_hdcs1020;
extern const struct stv06xx_sensor stv06xx_sensor_pb0100;
extern const struct stv06xx_sensor stv06xx_sensor_st6422;
struct stv06xx_sensor {
/* Defines the name of a sensor */
char name[32];
/* Sensor i2c address */
u8 i2c_addr;
/* Flush value*/
u8 i2c_flush;
/* length of an i2c word */
u8 i2c_len;
/* Probes if the sensor is connected */
int (*probe)(struct sd *sd);
/* Performs a initialization sequence */
int (*init)(struct sd *sd);
/* Executed at device disconnect */
void (*disconnect)(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

View File

@@ -0,0 +1,452 @@
/*
* Support for the sensor part which is integrated (I think) into the
* st6422 stv06xx alike bridge, as its integrated there are no i2c writes
* but instead direct bridge writes.
*
* Copyright (c) 2009 Hans de Goede <hdegoede@redhat.com>
*
* Strongly based on qc-usb-messenger, which is:
* Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
* Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
* Copyright (c) 2002, 2003 Tuukka Toivonen
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#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
},
};
static const struct ctrl st6422_ctrl[] = {
#define BRIGHTNESS_IDX 0
{
{
.id = V4L2_CID_BRIGHTNESS,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Brightness",
.minimum = 0,
.maximum = 31,
.step = 1,
.default_value = 3
},
.set = st6422_set_brightness,
.get = st6422_get_brightness
},
#define CONTRAST_IDX 1
{
{
.id = V4L2_CID_CONTRAST,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Contrast",
.minimum = 0,
.maximum = 15,
.step = 1,
.default_value = 11
},
.set = st6422_set_contrast,
.get = st6422_get_contrast
},
#define GAIN_IDX 2
{
{
.id = V4L2_CID_GAIN,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Gain",
.minimum = 0,
.maximum = 255,
.step = 1,
.default_value = 64
},
.set = st6422_set_gain,
.get = st6422_get_gain
},
#define EXPOSURE_IDX 3
{
{
.id = V4L2_CID_EXPOSURE,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Exposure",
.minimum = 0,
.maximum = 1023,
.step = 1,
.default_value = 256
},
.set = st6422_set_exposure,
.get = st6422_get_exposure
},
};
static int st6422_probe(struct sd *sd)
{
int i;
s32 *sensor_settings;
if (sd->bridge != BRIDGE_ST6422)
return -ENODEV;
info("st6422 sensor detected");
sensor_settings = kmalloc(ARRAY_SIZE(st6422_ctrl) * sizeof(s32),
GFP_KERNEL);
if (!sensor_settings)
return -ENOMEM;
sd->gspca_dev.cam.cam_mode = st6422_mode;
sd->gspca_dev.cam.nmodes = ARRAY_SIZE(st6422_mode);
sd->desc.ctrls = st6422_ctrl;
sd->desc.nctrls = ARRAY_SIZE(st6422_ctrl);
sd->sensor_priv = sensor_settings;
for (i = 0; i < sd->desc.nctrls; i++)
sensor_settings[i] = st6422_ctrl[i].qctrl.default_value;
return 0;
}
static int st6422_init(struct sd *sd)
{
int err = 0, i;
const u16 st6422_bridge_init[][2] = {
{ STV_ISO_ENABLE, 0x00 }, /* disable capture */
{ 0x1436, 0x00 },
{ 0x1432, 0x03 }, /* 0x00-0x1F brightness */
{ 0x143a, 0xF9 }, /* 0x00-0x0F contrast */
{ 0x0509, 0x38 }, /* R */
{ 0x050a, 0x38 }, /* G */
{ 0x050b, 0x38 }, /* B */
{ 0x050c, 0x2A },
{ 0x050d, 0x01 },
{ 0x1431, 0x00 }, /* 0x00-0x07 ??? */
{ 0x1433, 0x34 }, /* 160x120, 0x00-0x01 night filter */
{ 0x1438, 0x18 }, /* 640x480 */
/* 18 bayes */
/* 10 compressed? */
{ 0x1439, 0x00 },
/* anti-noise? 0xa2 gives a perfect image */
{ 0x143b, 0x05 },
{ 0x143c, 0x00 }, /* 0x00-0x01 - ??? */
/* shutter time 0x0000-0x03FF */
/* low value give good picures on moving objects (but requires much light) */
/* high value gives good picures in darkness (but tends to be overexposed) */
{ 0x143e, 0x01 },
{ 0x143d, 0x00 },
{ 0x1442, 0xe2 },
/* write: 1x1x xxxx */
/* read: 1x1x xxxx */
/* bit 5 == button pressed and hold if 0 */
/* write 0xe2,0xea */
/* 0x144a */
/* 0x00 init */
/* bit 7 == button has been pressed, but not handled */
/* interrupt */
/* if(urb->iso_frame_desc[i].status == 0x80) { */
/* if(urb->iso_frame_desc[i].status == 0x88) { */
{ 0x1500, 0xd0 },
{ 0x1500, 0xd0 },
{ 0x1500, 0x50 }, /* 0x00 - 0xFF 0x80 == compr ? */
{ 0x1501, 0xaf },
/* high val-> light area gets darker */
/* low val -> light area gets lighter */
{ 0x1502, 0xc2 },
/* high val-> light area gets darker */
/* low val -> light area gets lighter */
{ 0x1503, 0x45 },
/* high val-> light area gets darker */
/* low val -> light area gets lighter */
{ 0x1505, 0x02 },
/* 2 : 324x248 80352 bytes */
/* 7 : 248x162 40176 bytes */
/* c+f: 162*124 20088 bytes */
{ 0x150e, 0x8e },
{ 0x150f, 0x37 },
{ 0x15c0, 0x00 },
{ 0x15c1, 1023 }, /* 160x120, ISOC_PACKET_SIZE */
{ 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 void st6422_disconnect(struct sd *sd)
{
sd->sensor = NULL;
kfree(sd->sensor_priv);
}
static int st6422_start(struct sd *sd)
{
int err, packet_size;
struct cam *cam = &sd->gspca_dev.cam;
s32 *sensor_settings = sd->sensor_priv;
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) {
PDEBUG(D_ERR, "Couldn't get altsetting");
return -EIO;
}
packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize);
err = stv06xx_write_bridge(sd, 0x15c1, packet_size);
if (err < 0)
return err;
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;
err = st6422_set_brightness(&sd->gspca_dev,
sensor_settings[BRIGHTNESS_IDX]);
if (err < 0)
return err;
err = st6422_set_contrast(&sd->gspca_dev,
sensor_settings[CONTRAST_IDX]);
if (err < 0)
return err;
err = st6422_set_exposure(&sd->gspca_dev,
sensor_settings[EXPOSURE_IDX]);
if (err < 0)
return err;
err = st6422_set_gain(&sd->gspca_dev,
sensor_settings[GAIN_IDX]);
if (err < 0)
return err;
PDEBUG(D_STREAM, "Starting stream");
return 0;
}
static int st6422_stop(struct sd *sd)
{
PDEBUG(D_STREAM, "Halting stream");
return 0;
}
static int st6422_get_brightness(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
*val = sensor_settings[BRIGHTNESS_IDX];
PDEBUG(D_V4L2, "Read brightness %d", *val);
return 0;
}
static int st6422_set_brightness(struct gspca_dev *gspca_dev, __s32 val)
{
int err;
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
sensor_settings[BRIGHTNESS_IDX] = val;
if (!gspca_dev->streaming)
return 0;
/* val goes from 0 -> 31 */
PDEBUG(D_V4L2, "Set brightness to %d", val);
err = stv06xx_write_bridge(sd, 0x1432, val);
if (err < 0)
return err;
/* commit settings */
err = stv06xx_write_bridge(sd, 0x143f, 0x01);
return (err < 0) ? err : 0;
}
static int st6422_get_contrast(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
*val = sensor_settings[CONTRAST_IDX];
PDEBUG(D_V4L2, "Read contrast %d", *val);
return 0;
}
static int st6422_set_contrast(struct gspca_dev *gspca_dev, __s32 val)
{
int err;
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
sensor_settings[CONTRAST_IDX] = val;
if (!gspca_dev->streaming)
return 0;
/* Val goes from 0 -> 15 */
PDEBUG(D_V4L2, "Set contrast to %d\n", val);
err = stv06xx_write_bridge(sd, 0x143a, 0xf0 | val);
if (err < 0)
return err;
/* commit settings */
err = stv06xx_write_bridge(sd, 0x143f, 0x01);
return (err < 0) ? err : 0;
}
static int st6422_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
*val = sensor_settings[GAIN_IDX];
PDEBUG(D_V4L2, "Read gain %d", *val);
return 0;
}
static int st6422_set_gain(struct gspca_dev *gspca_dev, __s32 val)
{
int err;
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
sensor_settings[GAIN_IDX] = val;
if (!gspca_dev->streaming)
return 0;
PDEBUG(D_V4L2, "Set gain to %d", val);
/* Set red, green, blue, gain */
err = stv06xx_write_bridge(sd, 0x0509, val);
if (err < 0)
return err;
err = stv06xx_write_bridge(sd, 0x050a, val);
if (err < 0)
return err;
err = stv06xx_write_bridge(sd, 0x050b, val);
if (err < 0)
return err;
/* 2 mystery writes */
err = stv06xx_write_bridge(sd, 0x050c, 0x2a);
if (err < 0)
return err;
err = stv06xx_write_bridge(sd, 0x050d, 0x01);
if (err < 0)
return err;
/* commit settings */
err = stv06xx_write_bridge(sd, 0x143f, 0x01);
return (err < 0) ? err : 0;
}
static int st6422_get_exposure(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
*val = sensor_settings[EXPOSURE_IDX];
PDEBUG(D_V4L2, "Read exposure %d", *val);
return 0;
}
static int st6422_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
{
int err;
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
sensor_settings[EXPOSURE_IDX] = val;
if (!gspca_dev->streaming)
return 0;
PDEBUG(D_V4L2, "Set exposure to %d\n", val);
err = stv06xx_write_bridge(sd, 0x143d, val & 0xff);
if (err < 0)
return err;
err = stv06xx_write_bridge(sd, 0x143e, val >> 8);
if (err < 0)
return err;
/* commit settings */
err = stv06xx_write_bridge(sd, 0x143f, 0x01);
return (err < 0) ? err : 0;
}

View File

@@ -0,0 +1,59 @@
/*
* Support for the sensor part which is integrated (I think) into the
* st6422 stv06xx alike bridge, as its integrated there are no i2c writes
* but instead direct bridge writes.
*
* Copyright (c) 2009 Hans de Goede <hdegoede@redhat.com>
*
* Strongly based on qc-usb-messenger, which is:
* Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
* Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
* Copyright (c) 2002, 2003 Tuukka Toivonen
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef STV06XX_ST6422_H_
#define STV06XX_ST6422_H_
#include "stv06xx_sensor.h"
static int st6422_probe(struct sd *sd);
static int st6422_start(struct sd *sd);
static int st6422_init(struct sd *sd);
static int st6422_stop(struct sd *sd);
static void st6422_disconnect(struct sd *sd);
/* V4L2 controls supported by the driver */
static int st6422_get_brightness(struct gspca_dev *gspca_dev, __s32 *val);
static int st6422_set_brightness(struct gspca_dev *gspca_dev, __s32 val);
static int st6422_get_contrast(struct gspca_dev *gspca_dev, __s32 *val);
static int st6422_set_contrast(struct gspca_dev *gspca_dev, __s32 val);
static int st6422_get_gain(struct gspca_dev *gspca_dev, __s32 *val);
static int st6422_set_gain(struct gspca_dev *gspca_dev, __s32 val);
static int st6422_get_exposure(struct gspca_dev *gspca_dev, __s32 *val);
static int st6422_set_exposure(struct gspca_dev *gspca_dev, __s32 val);
const struct stv06xx_sensor stv06xx_sensor_st6422 = {
.name = "ST6422",
.init = st6422_init,
.probe = st6422_probe,
.start = st6422_start,
.stop = st6422_stop,
.disconnect = st6422_disconnect,
};
#endif

View File

@@ -0,0 +1,392 @@
/*
* Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
* Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
* Copyright (c) 2002, 2003 Tuukka Toivonen
* Copyright (c) 2008 Erik Andrén
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* P/N 861037: Sensor HDCS1000 ASIC STV0600
* P/N 861050-0010: Sensor HDCS1000 ASIC STV0600
* P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express
* P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam
* P/N 861075-0040: Sensor HDCS1000 ASIC
* P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB
* P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web
*/
#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 const struct ctrl vv6410_ctrl[] = {
#define HFLIP_IDX 0
{
{
.id = V4L2_CID_HFLIP,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.name = "horizontal flip",
.minimum = 0,
.maximum = 1,
.step = 1,
.default_value = 0
},
.set = vv6410_set_hflip,
.get = vv6410_get_hflip
},
#define VFLIP_IDX 1
{
{
.id = V4L2_CID_VFLIP,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.name = "vertical flip",
.minimum = 0,
.maximum = 1,
.step = 1,
.default_value = 0
},
.set = vv6410_set_vflip,
.get = vv6410_get_vflip
},
#define GAIN_IDX 2
{
{
.id = V4L2_CID_GAIN,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "analog gain",
.minimum = 0,
.maximum = 15,
.step = 1,
.default_value = 10
},
.set = vv6410_set_analog_gain,
.get = vv6410_get_analog_gain
},
#define EXPOSURE_IDX 3
{
{
.id = V4L2_CID_EXPOSURE,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "exposure",
.minimum = 0,
.maximum = 32768,
.step = 1,
.default_value = 20000
},
.set = vv6410_set_exposure,
.get = vv6410_get_exposure
}
};
static int vv6410_probe(struct sd *sd)
{
u16 data;
int err, i;
s32 *sensor_settings;
err = stv06xx_read_sensor(sd, VV6410_DEVICEH, &data);
if (err < 0)
return -ENODEV;
if (data == 0x19) {
info("vv6410 sensor detected");
sensor_settings = kmalloc(ARRAY_SIZE(vv6410_ctrl) * sizeof(s32),
GFP_KERNEL);
if (!sensor_settings)
return -ENOMEM;
sd->gspca_dev.cam.cam_mode = vv6410_mode;
sd->gspca_dev.cam.nmodes = ARRAY_SIZE(vv6410_mode);
sd->desc.ctrls = vv6410_ctrl;
sd->desc.nctrls = ARRAY_SIZE(vv6410_ctrl);
for (i = 0; i < sd->desc.nctrls; i++)
sensor_settings[i] = vv6410_ctrl[i].qctrl.default_value;
sd->sensor_priv = sensor_settings;
return 0;
}
return -ENODEV;
}
static int vv6410_init(struct sd *sd)
{
int err = 0, i;
s32 *sensor_settings = sd->sensor_priv;
for (i = 0; i < ARRAY_SIZE(stv_bridge_init); i++) {
/* if NULL then len contains single value */
if (stv_bridge_init[i].data == NULL) {
err = stv06xx_write_bridge(sd,
stv_bridge_init[i].start,
stv_bridge_init[i].len);
} else {
int j;
for (j = 0; j < stv_bridge_init[i].len; j++)
err = stv06xx_write_bridge(sd,
stv_bridge_init[i].start + j,
stv_bridge_init[i].data[j]);
}
}
if (err < 0)
return err;
err = stv06xx_write_sensor_bytes(sd, (u8 *) vv6410_sensor_init,
ARRAY_SIZE(vv6410_sensor_init));
if (err < 0)
return err;
err = vv6410_set_exposure(&sd->gspca_dev,
sensor_settings[EXPOSURE_IDX]);
if (err < 0)
return err;
err = vv6410_set_analog_gain(&sd->gspca_dev,
sensor_settings[GAIN_IDX]);
return (err < 0) ? err : 0;
}
static void vv6410_disconnect(struct sd *sd)
{
sd->sensor = NULL;
kfree(sd->sensor_priv);
}
static int vv6410_start(struct sd *sd)
{
int err;
struct cam *cam = &sd->gspca_dev.cam;
u32 priv = cam->cam_mode[sd->gspca_dev.curr_mode].priv;
if (priv & VV6410_CROP_TO_QVGA) {
PDEBUG(D_CONF, "Cropping to QVGA");
stv06xx_write_sensor(sd, VV6410_XENDH, 320 - 1);
stv06xx_write_sensor(sd, VV6410_YENDH, 240 - 1);
} else {
stv06xx_write_sensor(sd, VV6410_XENDH, 360 - 1);
stv06xx_write_sensor(sd, VV6410_YENDH, 294 - 1);
}
if (priv & VV6410_SUBSAMPLE) {
PDEBUG(D_CONF, "Enabling subsampling");
stv06xx_write_bridge(sd, STV_Y_CTRL, 0x02);
stv06xx_write_bridge(sd, STV_X_CTRL, 0x06);
stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x10);
} else {
stv06xx_write_bridge(sd, STV_Y_CTRL, 0x01);
stv06xx_write_bridge(sd, STV_X_CTRL, 0x0a);
stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x20);
}
/* Turn on LED */
err = stv06xx_write_bridge(sd, STV_LED_CTRL, LED_ON);
if (err < 0)
return err;
err = stv06xx_write_sensor(sd, VV6410_SETUP0, 0);
if (err < 0)
return err;
PDEBUG(D_STREAM, "Starting stream");
return 0;
}
static int vv6410_stop(struct sd *sd)
{
int err;
/* Turn off LED */
err = stv06xx_write_bridge(sd, STV_LED_CTRL, LED_OFF);
if (err < 0)
return err;
err = stv06xx_write_sensor(sd, VV6410_SETUP0, VV6410_LOW_POWER_MODE);
if (err < 0)
return err;
PDEBUG(D_STREAM, "Halting stream");
return (err < 0) ? err : 0;
}
static int vv6410_dump(struct sd *sd)
{
u8 i;
int err = 0;
info("Dumping all vv6410 sensor registers");
for (i = 0; i < 0xff && !err; i++) {
u16 data;
err = stv06xx_read_sensor(sd, i, &data);
info("Register 0x%x contained 0x%x", i, data);
}
return (err < 0) ? err : 0;
}
static int vv6410_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
*val = sensor_settings[HFLIP_IDX];
PDEBUG(D_V4L2, "Read horizontal flip %d", *val);
return 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;
s32 *sensor_settings = sd->sensor_priv;
sensor_settings[HFLIP_IDX] = val;
err = stv06xx_read_sensor(sd, VV6410_DATAFORMAT, &i2c_data);
if (err < 0)
return err;
if (val)
i2c_data |= VV6410_HFLIP;
else
i2c_data &= ~VV6410_HFLIP;
PDEBUG(D_V4L2, "Set horizontal flip to %d", val);
err = stv06xx_write_sensor(sd, VV6410_DATAFORMAT, i2c_data);
return (err < 0) ? err : 0;
}
static int vv6410_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
*val = sensor_settings[VFLIP_IDX];
PDEBUG(D_V4L2, "Read vertical flip %d", *val);
return 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;
s32 *sensor_settings = sd->sensor_priv;
sensor_settings[VFLIP_IDX] = val;
err = stv06xx_read_sensor(sd, VV6410_DATAFORMAT, &i2c_data);
if (err < 0)
return err;
if (val)
i2c_data |= VV6410_VFLIP;
else
i2c_data &= ~VV6410_VFLIP;
PDEBUG(D_V4L2, "Set vertical flip to %d", val);
err = stv06xx_write_sensor(sd, VV6410_DATAFORMAT, i2c_data);
return (err < 0) ? err : 0;
}
static int vv6410_get_analog_gain(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
*val = sensor_settings[GAIN_IDX];
PDEBUG(D_V4L2, "Read analog gain %d", *val);
return 0;
}
static int vv6410_set_analog_gain(struct gspca_dev *gspca_dev, __s32 val)
{
int err;
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
sensor_settings[GAIN_IDX] = val;
PDEBUG(D_V4L2, "Set analog gain to %d", val);
err = stv06xx_write_sensor(sd, VV6410_ANALOGGAIN, 0xf0 | (val & 0xf));
return (err < 0) ? err : 0;
}
static int vv6410_get_exposure(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
*val = sensor_settings[EXPOSURE_IDX];
PDEBUG(D_V4L2, "Read exposure %d", *val);
return 0;
}
static int vv6410_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
{
int err;
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
unsigned int fine, coarse;
sensor_settings[EXPOSURE_IDX] = val;
val = (val * val >> 14) + val / 4;
fine = val % VV6410_CIF_LINELENGTH;
coarse = min(512, val / VV6410_CIF_LINELENGTH);
PDEBUG(D_V4L2, "Set coarse exposure to %d, fine expsure to %d",
coarse, fine);
err = stv06xx_write_sensor(sd, VV6410_FINEH, fine >> 8);
if (err < 0)
goto out;
err = stv06xx_write_sensor(sd, VV6410_FINEL, fine & 0xff);
if (err < 0)
goto out;
err = stv06xx_write_sensor(sd, VV6410_COARSEH, coarse >> 8);
if (err < 0)
goto out;
err = stv06xx_write_sensor(sd, VV6410_COARSEL, coarse & 0xff);
out:
return err;
}

View File

@@ -0,0 +1,259 @@
/*
* Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
* Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
* Copyright (c) 2002, 2003 Tuukka Toivonen
* Copyright (c) 2008 Erik Andrén
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* P/N 861037: Sensor HDCS1000 ASIC STV0600
* P/N 861050-0010: Sensor HDCS1000 ASIC STV0600
* P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express
* P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam
* P/N 861075-0040: Sensor HDCS1000 ASIC
* P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB
* P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web
*/
#ifndef STV06XX_VV6410_H_
#define STV06XX_VV6410_H_
#include "stv06xx_sensor.h"
#define VV6410_COLS 416
#define VV6410_ROWS 320
/* Status registers */
/* Chip identification number including revision indicator */
#define VV6410_DEVICEH 0x00
#define VV6410_DEVICEL 0x01
/* User can determine whether timed I2C data
has been consumed by interrogating flag states */
#define VV6410_STATUS0 0x02
/* Current line counter value */
#define VV6410_LINECOUNTH 0x03
#define VV6410_LINECOUNTL 0x04
/* End x coordinate of image size */
#define VV6410_XENDH 0x05
#define VV6410_XENDL 0x06
/* End y coordinate of image size */
#define VV6410_YENDH 0x07
#define VV6410_YENDL 0x08
/* This is the average pixel value returned from the
dark line offset cancellation algorithm */
#define VV6410_DARKAVGH 0x09
#define VV6410_DARKAVGL 0x0a
/* This is the average pixel value returned from the
black line offset cancellation algorithm */
#define VV6410_BLACKAVGH 0x0b
#define VV6410_BLACKAVGL 0x0c
/* Flags to indicate whether the x or y image coordinates have been clipped */
#define VV6410_STATUS1 0x0d
/* Setup registers */
/* Low-power/sleep modes & video timing */
#define VV6410_SETUP0 0x10
/* Various parameters */
#define VV6410_SETUP1 0x11
/* Contains pixel counter reset value used by external sync */
#define VV6410_SYNCVALUE 0x12
/* Frame grabbing modes (FST, LST and QCK) */
#define VV6410_FGMODES 0x14
/* FST and QCK mapping modes. */
#define VV6410_PINMAPPING 0x15
/* Data resolution */
#define VV6410_DATAFORMAT 0x16
/* Output coding formats */
#define VV6410_OPFORMAT 0x17
/* Various mode select bits */
#define VV6410_MODESELECT 0x18
/* Exposure registers */
/* Fine exposure. */
#define VV6410_FINEH 0x20
#define VV6410_FINEL 0x21
/* Coarse exposure */
#define VV6410_COARSEH 0x22
#define VV6410_COARSEL 0x23
/* Analog gain setting */
#define VV6410_ANALOGGAIN 0x24
/* Clock division */
#define VV6410_CLKDIV 0x25
/* Dark line offset cancellation value */
#define VV6410_DARKOFFSETH 0x2c
#define VV6410_DARKOFFSETL 0x2d
/* Dark line offset cancellation enable */
#define VV6410_DARKOFFSETSETUP 0x2e
/* Video timing registers */
/* Line Length (Pixel Clocks) */
#define VV6410_LINELENGTHH 0x52
#define VV6410_LINELENGTHL 0x53
/* X-co-ordinate of top left corner of region of interest (x-offset) */
#define VV6410_XOFFSETH 0x57
#define VV6410_XOFFSETL 0x58
/* Y-coordinate of top left corner of region of interest (y-offset) */
#define VV6410_YOFFSETH 0x59
#define VV6410_YOFFSETL 0x5a
/* Field length (Lines) */
#define VV6410_FIELDLENGTHH 0x61
#define VV6410_FIELDLENGTHL 0x62
/* System registers */
/* Black offset cancellation default value */
#define VV6410_BLACKOFFSETH 0x70
#define VV6410_BLACKOFFSETL 0x71
/* Black offset cancellation setup */
#define VV6410_BLACKOFFSETSETUP 0x72
/* Analog Control Register 0 */
#define VV6410_CR0 0x75
/* Analog Control Register 1 */
#define VV6410_CR1 0x76
/* ADC Setup Register */
#define VV6410_AS0 0x77
/* Analog Test Register */
#define VV6410_AT0 0x78
/* Audio Amplifier Setup Register */
#define VV6410_AT1 0x79
#define VV6410_HFLIP (1 << 3)
#define VV6410_VFLIP (1 << 4)
#define VV6410_LOW_POWER_MODE (1 << 0)
#define VV6410_SOFT_RESET (1 << 2)
#define VV6410_PAL_25_FPS (0 << 3)
#define VV6410_CLK_DIV_2 (1 << 1)
#define VV6410_FINE_EXPOSURE 320
#define VV6410_COARSE_EXPOSURE 192
#define VV6410_DEFAULT_GAIN 5
#define VV6410_SUBSAMPLE 0x01
#define VV6410_CROP_TO_QVGA 0x02
#define VV6410_CIF_LINELENGTH 415
static int vv6410_probe(struct sd *sd);
static int vv6410_start(struct sd *sd);
static int vv6410_init(struct sd *sd);
static int vv6410_stop(struct sd *sd);
static int vv6410_dump(struct sd *sd);
static void vv6410_disconnect(struct sd *sd);
/* V4L2 controls supported by the driver */
static int vv6410_get_hflip(struct gspca_dev *gspca_dev, __s32 *val);
static int vv6410_set_hflip(struct gspca_dev *gspca_dev, __s32 val);
static int vv6410_get_vflip(struct gspca_dev *gspca_dev, __s32 *val);
static int vv6410_set_vflip(struct gspca_dev *gspca_dev, __s32 val);
static int vv6410_get_analog_gain(struct gspca_dev *gspca_dev, __s32 *val);
static int vv6410_set_analog_gain(struct gspca_dev *gspca_dev, __s32 val);
static int vv6410_get_exposure(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,
.init = vv6410_init,
.probe = vv6410_probe,
.start = vv6410_start,
.stop = vv6410_stop,
.dump = vv6410_dump,
.disconnect = vv6410_disconnect,
};
/* If NULL, only single value to write, stored in len */
struct stv_init {
const u8 *data;
u16 start;
u8 len;
};
static const u8 x1500[] = { /* 0x1500 - 0x150f */
0x0b, 0xa7, 0xb7, 0x00, 0x00
};
static const u8 x1536[] = { /* 0x1536 - 0x153b */
0x02, 0x00, 0x60, 0x01, 0x20, 0x01
};
static const u8 x15c1[] = { /* 0x15c1 - 0x15c2 */
0xff, 0x03 /* Output word 0x03ff = 1023 (ISO size) */
};
static const struct stv_init stv_bridge_init[] = {
/* This reg is written twice. Some kind of reset? */
{NULL, 0x1620, 0x80},
{NULL, 0x1620, 0x00},
{NULL, 0x1423, 0x04},
{x1500, 0x1500, ARRAY_SIZE(x1500)},
{x1536, 0x1536, ARRAY_SIZE(x1536)},
{x15c1, 0x15c1, ARRAY_SIZE(x15c1)}
};
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 */
{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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,511 @@
/*
* Quickcam cameras initialization data
*
* V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#define MODULE_NAME "tv8532"
#include "gspca.h"
MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
MODULE_DESCRIPTION("TV8532 USB Camera Driver");
MODULE_LICENSE("GPL");
/* specific webcam descriptor */
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
__u16 brightness;
__u8 packet;
};
/* V4L2 controls supported by the driver */
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
static struct ctrl sd_ctrls[] = {
{
{
.id = V4L2_CID_BRIGHTNESS,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Brightness",
.minimum = 1,
.maximum = 0x15f, /* = 352 - 1 */
.step = 1,
#define BRIGHTNESS_DEF 0x14c
.default_value = BRIGHTNESS_DEF,
},
.set = sd_setbrightness,
.get = sd_getbrightness,
},
};
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 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},
};
static int reg_r(struct gspca_dev *gspca_dev,
__u16 index)
{
usb_control_msg(gspca_dev->dev,
usb_rcvctrlpipe(gspca_dev->dev, 0),
0x03,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0, /* value */
index, gspca_dev->usb_buf, 1,
500);
return gspca_dev->usb_buf[0];
}
/* write 1 byte */
static void reg_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);
msleep(10);
}
/* this function is called at probe time */
static int sd_config(struct gspca_dev *gspca_dev,
const struct usb_device_id *id)
{
struct sd *sd = (struct sd *) gspca_dev;
struct cam *cam;
cam = &gspca_dev->cam;
cam->cam_mode = sif_mode;
cam->nmodes = ARRAY_SIZE(sif_mode);
sd->brightness = BRIGHTNESS_DEF;
return 0;
}
static void tv_8532ReadRegisters(struct gspca_dev *gspca_dev)
{
int i;
static u8 reg_tb[] = {
R0C_AD_WIDTHL,
R0D_AD_WIDTHH,
R28_QUANT,
R29_LINE,
R2C_POLARITY,
R2D_POINT,
R2E_POINTH,
R2F_POINTB,
R30_POINTBH,
R2A_HIGH_BUDGET,
R2B_LOW_BUDGET,
R34_VID,
R35_VIDH,
R36_PID,
R37_PIDH,
R83_AD_IDH,
R10_AD_COL_BEGINL,
R11_AD_COL_BEGINH,
R14_AD_ROW_BEGINL,
R15_AD_ROWBEGINH,
0
};
i = 0;
do {
reg_r(gspca_dev, reg_tb[i]);
i++;
} while (reg_tb[i] != 0);
}
static void tv_8532_setReg(struct gspca_dev *gspca_dev)
{
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, R00_PART_CONTROL, LATENT_CHANGE | EXPO_CHANGE);
/* = 0x84 */
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, R91_AD_SLOPEREG, 0x00);
reg_w1(gspca_dev, R94_AD_BITCONTROL, 0x02);
reg_w1(gspca_dev, R01_TIMING_CONTROL_LOW, CMD_EEprom_Close);
reg_w1(gspca_dev, R91_AD_SLOPEREG, 0x00);
reg_w1(gspca_dev, R00_PART_CONTROL, LATENT_CHANGE | EXPO_CHANGE);
/* = 0x84 */
}
static void tv_8532_PollReg(struct gspca_dev *gspca_dev)
{
int i;
/* strange polling from tgc */
for (i = 0; i < 10; i++) {
reg_w1(gspca_dev, R2C_POLARITY, 0x10);
reg_w1(gspca_dev, R00_PART_CONTROL,
LATENT_CHANGE | EXPO_CHANGE);
reg_w1(gspca_dev, R31_UPD, 0x01);
}
}
/* this function is called at probe and resume time */
static int sd_init(struct gspca_dev *gspca_dev)
{
tv_8532WriteEEprom(gspca_dev);
reg_w1(gspca_dev, R91_AD_SLOPEREG, 0x32); /* slope begin 1,7V,
* slope rate 2 */
reg_w1(gspca_dev, R94_AD_BITCONTROL, 0x00);
tv_8532ReadRegisters(gspca_dev);
reg_w1(gspca_dev, R3B_Test3, 0x0b);
reg_w2(gspca_dev, R0E_AD_HEIGHTL, 0x0190);
reg_w2(gspca_dev, R1C_AD_EXPOSE_TIMEL, 0x018f);
reg_w1(gspca_dev, R0C_AD_WIDTHL, 0xe8);
reg_w1(gspca_dev, R0D_AD_WIDTHH, 0x03);
/*******************************************************************/
reg_w1(gspca_dev, R28_QUANT, 0x90);
/* no compress - fixed Q - quant 0 */
reg_w1(gspca_dev, R29_LINE, 0x81);
/* 0x84; // CIF | 4 packet 0x29 */
/************************************************/
reg_w1(gspca_dev, R2C_POLARITY, 0x10);
/* 0x48; //0x08; 0x2c */
reg_w1(gspca_dev, R2D_POINT, 0x14);
/* 0x38; 0x2d */
reg_w1(gspca_dev, R2E_POINTH, 0x01);
/* 0x04; 0x2e */
reg_w1(gspca_dev, R2F_POINTB, 0x12);
/* 0x04; 0x2f */
reg_w1(gspca_dev, R30_POINTBH, 0x01);
/* 0x04; 0x30 */
reg_w1(gspca_dev, R00_PART_CONTROL, LATENT_CHANGE | EXPO_CHANGE);
/* 0x00<-0x84 */
/*************************************************/
reg_w1(gspca_dev, R31_UPD, 0x01); /* update registers */
msleep(200);
reg_w1(gspca_dev, R31_UPD, 0x00); /* end update */
/*************************************************/
tv_8532_setReg(gspca_dev);
/*************************************************/
reg_w1(gspca_dev, R3B_Test3, 0x0b); /* Test0Sel = 11 = GPIO */
/*************************************************/
tv_8532_setReg(gspca_dev);
/*************************************************/
tv_8532_PollReg(gspca_dev);
return 0;
}
static void setbrightness(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
reg_w2(gspca_dev, R1C_AD_EXPOSE_TIMEL, sd->brightness);
reg_w1(gspca_dev, R00_PART_CONTROL, LATENT_CHANGE | EXPO_CHANGE);
/* 0x84 */
}
/* -- start the camera -- */
static int sd_start(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
reg_w1(gspca_dev, R91_AD_SLOPEREG, 0x32); /* slope begin 1,7V,
* slope rate 2 */
reg_w1(gspca_dev, R94_AD_BITCONTROL, 0x00);
tv_8532ReadRegisters(gspca_dev);
reg_w1(gspca_dev, R3B_Test3, 0x0b);
reg_w2(gspca_dev, R0E_AD_HEIGHTL, 0x0190);
setbrightness(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);
reg_w1(gspca_dev, R00_PART_CONTROL, LATENT_CHANGE | EXPO_CHANGE);
/************************************************/
reg_w1(gspca_dev, R31_UPD, 0x01); /* update registers */
msleep(200);
reg_w1(gspca_dev, R31_UPD, 0x00); /* end update */
/************************************************/
tv_8532_setReg(gspca_dev);
/************************************************/
reg_w1(gspca_dev, R3B_Test3, 0x0b); /* Test0Sel = 11 = GPIO */
/************************************************/
tv_8532_setReg(gspca_dev);
/************************************************/
tv_8532_PollReg(gspca_dev);
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,
struct gspca_frame *frame, /* target */
__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->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,
frame, data + 2, gspca_dev->width);
gspca_frame_add(gspca_dev, packet_type1,
frame, data + gspca_dev->width + 5, gspca_dev->width);
}
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
sd->brightness = val;
if (gspca_dev->streaming)
setbrightness(gspca_dev);
return 0;
}
static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
*val = sd->brightness;
return 0;
}
/* sub-driver description */
static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
.ctrls = sd_ctrls,
.nctrls = ARRAY_SIZE(sd_ctrls),
.config = sd_config,
.init = sd_init,
.start = sd_start,
.stopN = sd_stopN,
.pkt_scan = sd_pkt_scan,
};
/* -- module initialisation -- */
static const __devinitdata 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,
#endif
};
/* -- module insert / remove -- */
static int __init sd_mod_init(void)
{
int ret;
ret = usb_register(&sd_driver);
if (ret < 0)
return ret;
PDEBUG(D_PROBE, "registered");
return 0;
}
static void __exit sd_mod_exit(void)
{
usb_deregister(&sd_driver);
PDEBUG(D_PROBE, "deregistered");
}
module_init(sd_mod_init);
module_exit(sd_mod_exit);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,253 @@
/*
* zc030x registers
*
* Copyright (c) 2008 Mauro Carvalho Chehab <mchehab@infradead.org>
*
* The register aliases used here came from this driver:
* http://zc0302.sourceforge.net/zc0302.php
*
* This code is placed under the terms of the GNU General Public License v2
*/
/* Define the register map */
#define ZC3XX_R000_SYSTEMCONTROL 0x0000
#define ZC3XX_R001_SYSTEMOPERATING 0x0001
/* Picture size */
#define ZC3XX_R002_CLOCKSELECT 0x0002
#define ZC3XX_R003_FRAMEWIDTHHIGH 0x0003
#define ZC3XX_R004_FRAMEWIDTHLOW 0x0004
#define ZC3XX_R005_FRAMEHEIGHTHIGH 0x0005
#define ZC3XX_R006_FRAMEHEIGHTLOW 0x0006
/* JPEG control */
#define ZC3XX_R008_CLOCKSETTING 0x0008
/* Test mode */
#define ZC3XX_R00B_TESTMODECONTROL 0x000b
/* Frame retreiving */
#define ZC3XX_R00C_LASTACQTIME 0x000c
#define ZC3XX_R00D_MONITORRES 0x000d
#define ZC3XX_R00E_TIMESTAMPHIGH 0x000e
#define ZC3XX_R00F_TIMESTAMPLOW 0x000f
#define ZC3XX_R018_FRAMELOST 0x0018
#define ZC3XX_R019_AUTOADJUSTFPS 0x0019
#define ZC3XX_R01A_LASTFRAMESTATE 0x001a
#define ZC3XX_R025_DATACOUNTER 0x0025
/* Stream and sensor specific */
#define ZC3XX_R010_CMOSSENSORSELECT 0x0010
#define ZC3XX_R011_VIDEOSTATUS 0x0011
#define ZC3XX_R012_VIDEOCONTROLFUNC 0x0012
/* Horizontal and vertical synchros */
#define ZC3XX_R01D_HSYNC_0 0x001d
#define ZC3XX_R01E_HSYNC_1 0x001e
#define ZC3XX_R01F_HSYNC_2 0x001f
#define ZC3XX_R020_HSYNC_3 0x0020
/* Target picture size in byte */
#define ZC3XX_R022_TARGETPICTSIZE_0 0x0022
#define ZC3XX_R023_TARGETPICTSIZE_1 0x0023
#define ZC3XX_R024_TARGETPICTSIZE_2 0x0024
/* Audio registers */
#define ZC3XX_R030_AUDIOADC 0x0030
#define ZC3XX_R031_AUDIOSTREAMSTATUS 0x0031
#define ZC3XX_R032_AUDIOSTATUS 0x0032
/* Sensor interface */
#define ZC3XX_R080_HBLANKHIGH 0x0080
#define ZC3XX_R081_HBLANKLOW 0x0081
#define ZC3XX_R082_RESETLEVELADDR 0x0082
#define ZC3XX_R083_RGAINADDR 0x0083
#define ZC3XX_R084_GGAINADDR 0x0084
#define ZC3XX_R085_BGAINADDR 0x0085
#define ZC3XX_R086_EXPTIMEHIGH 0x0086
#define ZC3XX_R087_EXPTIMEMID 0x0087
#define ZC3XX_R088_EXPTIMELOW 0x0088
#define ZC3XX_R089_RESETBLACKHIGH 0x0089
#define ZC3XX_R08A_RESETWHITEHIGH 0x008a
#define ZC3XX_R08B_I2CDEVICEADDR 0x008b
#define ZC3XX_R08C_I2CIDLEANDNACK 0x008c
#define ZC3XX_R08D_COMPABILITYMODE 0x008d
#define ZC3XX_R08E_COMPABILITYMODE2 0x008e
/* I2C control */
#define ZC3XX_R090_I2CCOMMAND 0x0090
#define ZC3XX_R091_I2CSTATUS 0x0091
#define ZC3XX_R092_I2CADDRESSSELECT 0x0092
#define ZC3XX_R093_I2CSETVALUE 0x0093
#define ZC3XX_R094_I2CWRITEACK 0x0094
#define ZC3XX_R095_I2CREAD 0x0095
#define ZC3XX_R096_I2CREADACK 0x0096
/* Window inside the sensor array */
#define ZC3XX_R097_WINYSTARTHIGH 0x0097
#define ZC3XX_R098_WINYSTARTLOW 0x0098
#define ZC3XX_R099_WINXSTARTHIGH 0x0099
#define ZC3XX_R09A_WINXSTARTLOW 0x009a
#define ZC3XX_R09B_WINHEIGHTHIGH 0x009b
#define ZC3XX_R09C_WINHEIGHTLOW 0x009c
#define ZC3XX_R09D_WINWIDTHHIGH 0x009d
#define ZC3XX_R09E_WINWIDTHLOW 0x009e
#define ZC3XX_R119_FIRSTYHIGH 0x0119
#define ZC3XX_R11A_FIRSTYLOW 0x011a
#define ZC3XX_R11B_FIRSTXHIGH 0x011b
#define ZC3XX_R11C_FIRSTXLOW 0x011c
/* Max sensor array size */
#define ZC3XX_R09F_MAXXHIGH 0x009f
#define ZC3XX_R0A0_MAXXLOW 0x00a0
#define ZC3XX_R0A1_MAXYHIGH 0x00a1
#define ZC3XX_R0A2_MAXYLOW 0x00a2
#define ZC3XX_R0A3_EXPOSURETIMEHIGH 0x00a3
#define ZC3XX_R0A4_EXPOSURETIMELOW 0x00a4
#define ZC3XX_R0A5_EXPOSUREGAIN 0x00a5
#define ZC3XX_R0A6_EXPOSUREBLACKLVL 0x00a6
/* Other registers */
#define ZC3XX_R100_OPERATIONMODE 0x0100
#define ZC3XX_R101_SENSORCORRECTION 0x0101
/* Gains */
#define ZC3XX_R116_RGAIN 0x0116
#define ZC3XX_R117_GGAIN 0x0117
#define ZC3XX_R118_BGAIN 0x0118
#define ZC3XX_R11D_GLOBALGAIN 0x011d
#define ZC3XX_R1A8_DIGITALGAIN 0x01a8
#define ZC3XX_R1A9_DIGITALLIMITDIFF 0x01a9
#define ZC3XX_R1AA_DIGITALGAINSTEP 0x01aa
/* Auto correction */
#define ZC3XX_R180_AUTOCORRECTENABLE 0x0180
#define ZC3XX_R181_WINXSTART 0x0181
#define ZC3XX_R182_WINXWIDTH 0x0182
#define ZC3XX_R183_WINXCENTER 0x0183
#define ZC3XX_R184_WINYSTART 0x0184
#define ZC3XX_R185_WINYWIDTH 0x0185
#define ZC3XX_R186_WINYCENTER 0x0186
/* Gain range */
#define ZC3XX_R187_MAXGAIN 0x0187
#define ZC3XX_R188_MINGAIN 0x0188
/* Auto exposure and white balance */
#define ZC3XX_R189_AWBSTATUS 0x0189
#define ZC3XX_R18A_AWBFREEZE 0x018a
#define ZC3XX_R18B_AESTATUS 0x018b
#define ZC3XX_R18C_AEFREEZE 0x018c
#define ZC3XX_R18F_AEUNFREEZE 0x018f
#define ZC3XX_R190_EXPOSURELIMITHIGH 0x0190
#define ZC3XX_R191_EXPOSURELIMITMID 0x0191
#define ZC3XX_R192_EXPOSURELIMITLOW 0x0192
#define ZC3XX_R195_ANTIFLICKERHIGH 0x0195
#define ZC3XX_R196_ANTIFLICKERMID 0x0196
#define ZC3XX_R197_ANTIFLICKERLOW 0x0197
/* What is this ? */
#define ZC3XX_R18D_YTARGET 0x018d
#define ZC3XX_R18E_RESETLVL 0x018e
/* Color */
#define ZC3XX_R1A0_REDMEANAFTERAGC 0x01a0
#define ZC3XX_R1A1_GREENMEANAFTERAGC 0x01a1
#define ZC3XX_R1A2_BLUEMEANAFTERAGC 0x01a2
#define ZC3XX_R1A3_REDMEANAFTERAWB 0x01a3
#define ZC3XX_R1A4_GREENMEANAFTERAWB 0x01a4
#define ZC3XX_R1A5_BLUEMEANAFTERAWB 0x01a5
#define ZC3XX_R1A6_YMEANAFTERAE 0x01a6
#define ZC3XX_R1A7_CALCGLOBALMEAN 0x01a7
#define ZC3XX_R1A2_BLUEMEANAFTERAGC 0x01a2
/* Matrixes */
/* Color matrix is like :
R' = R * RGB00 + G * RGB01 + B * RGB02 + RGB03
G' = R * RGB10 + G * RGB11 + B * RGB22 + RGB13
B' = R * RGB20 + G * RGB21 + B * RGB12 + RGB23
*/
#define ZC3XX_R10A_RGB00 0x010a
#define ZC3XX_R10B_RGB01 0x010b
#define ZC3XX_R10C_RGB02 0x010c
#define ZC3XX_R113_RGB03 0x0113
#define ZC3XX_R10D_RGB10 0x010d
#define ZC3XX_R10E_RGB11 0x010e
#define ZC3XX_R10F_RGB12 0x010f
#define ZC3XX_R114_RGB13 0x0114
#define ZC3XX_R110_RGB20 0x0110
#define ZC3XX_R111_RGB21 0x0111
#define ZC3XX_R112_RGB22 0x0112
#define ZC3XX_R115_RGB23 0x0115
/* Gamma matrix */
#define ZC3XX_R120_GAMMA00 0x0120
#define ZC3XX_R121_GAMMA01 0x0121
#define ZC3XX_R122_GAMMA02 0x0122
#define ZC3XX_R123_GAMMA03 0x0123
#define ZC3XX_R124_GAMMA04 0x0124
#define ZC3XX_R125_GAMMA05 0x0125
#define ZC3XX_R126_GAMMA06 0x0126
#define ZC3XX_R127_GAMMA07 0x0127
#define ZC3XX_R128_GAMMA08 0x0128
#define ZC3XX_R129_GAMMA09 0x0129
#define ZC3XX_R12A_GAMMA0A 0x012a
#define ZC3XX_R12B_GAMMA0B 0x012b
#define ZC3XX_R12C_GAMMA0C 0x012c
#define ZC3XX_R12D_GAMMA0D 0x012d
#define ZC3XX_R12E_GAMMA0E 0x012e
#define ZC3XX_R12F_GAMMA0F 0x012f
#define ZC3XX_R130_GAMMA10 0x0130
#define ZC3XX_R131_GAMMA11 0x0131
#define ZC3XX_R132_GAMMA12 0x0132
#define ZC3XX_R133_GAMMA13 0x0133
#define ZC3XX_R134_GAMMA14 0x0134
#define ZC3XX_R135_GAMMA15 0x0135
#define ZC3XX_R136_GAMMA16 0x0136
#define ZC3XX_R137_GAMMA17 0x0137
#define ZC3XX_R138_GAMMA18 0x0138
#define ZC3XX_R139_GAMMA19 0x0139
#define ZC3XX_R13A_GAMMA1A 0x013a
#define ZC3XX_R13B_GAMMA1B 0x013b
#define ZC3XX_R13C_GAMMA1C 0x013c
#define ZC3XX_R13D_GAMMA1D 0x013d
#define ZC3XX_R13E_GAMMA1E 0x013e
#define ZC3XX_R13F_GAMMA1F 0x013f
/* Luminance gamma */
#define ZC3XX_R140_YGAMMA00 0x0140
#define ZC3XX_R141_YGAMMA01 0x0141
#define ZC3XX_R142_YGAMMA02 0x0142
#define ZC3XX_R143_YGAMMA03 0x0143
#define ZC3XX_R144_YGAMMA04 0x0144
#define ZC3XX_R145_YGAMMA05 0x0145
#define ZC3XX_R146_YGAMMA06 0x0146
#define ZC3XX_R147_YGAMMA07 0x0147
#define ZC3XX_R148_YGAMMA08 0x0148
#define ZC3XX_R149_YGAMMA09 0x0149
#define ZC3XX_R14A_YGAMMA0A 0x014a
#define ZC3XX_R14B_YGAMMA0B 0x014b
#define ZC3XX_R14C_YGAMMA0C 0x014c
#define ZC3XX_R14D_YGAMMA0D 0x014d
#define ZC3XX_R14E_YGAMMA0E 0x014e
#define ZC3XX_R14F_YGAMMA0F 0x014f
#define ZC3XX_R150_YGAMMA10 0x0150
#define ZC3XX_R151_YGAMMA11 0x0151
#define ZC3XX_R1C5_SHARPNESSMODE 0x01c5
#define ZC3XX_R1C6_SHARPNESS00 0x01c6
#define ZC3XX_R1C7_SHARPNESS01 0x01c7
#define ZC3XX_R1C8_SHARPNESS02 0x01c8
#define ZC3XX_R1C9_SHARPNESS03 0x01c9
#define ZC3XX_R1CA_SHARPNESS04 0x01ca
#define ZC3XX_R1CB_SHARPNESS05 0x01cb
/* Dead pixels */
#define ZC3XX_R250_DEADPIXELSMODE 0x0250
/* EEPROM */
#define ZC3XX_R300_EEPROMCONFIG 0x0300
#define ZC3XX_R301_EEPROMACCESS 0x0301
#define ZC3XX_R302_EEPROMSTATUS 0x0302

File diff suppressed because it is too large Load Diff