diff -Naur linux-2.4.0-orig/Documentation/usb/ov511.txt linux/Documentation/usb/ov511.txt --- linux-2.4.0-orig/Documentation/usb/ov511.txt Tue Jan 30 03:52:45 2001 +++ linux/Documentation/usb/ov511.txt Tue Jan 30 03:55:38 2001 @@ -8,7 +8,7 @@ INTRODUCTION: This is a driver for the OV511, a USB-only chip used in many "webcam" devices. -Any camera using the OV511/OV511+ and the OV7610/20/20AE CCD should work. It +Any camera using the OV511/OV511+ and the OV6620/OV7610/20/20AE CCD should work. It supports streaming and capture of color or monochrome video via the Video4Linux API. Most V4L apps are compatible with it, but a few video-conferencing programs do not work yet. The following resolutions are supported: 640x480, 448x336, @@ -29,14 +29,14 @@ You must have first compiled USB support, support for your specific USB host controller (UHCI or OHCI), and Video4Linux support for your kernel (I recommend -making them modules.) +making them modules.) Make sure "Enforce bandwidth allocation" is NOT enabled. -Next, (as root) from your appropriate modules directory (lib/modules/2.3.XX): +Next, (as root): - insmod usb/usbcore.o - insmod usb/usb-uhci.o insmod usb/ohci-hcd.o - insmod misc/videodev.o - insmod usb/ov511.o + modprobe usbcore + modprobe usb-uhci modprobe usb-ohci + modprobe videodev + modprobe ov511 If it is not already there (it usually is), create the video device: @@ -60,21 +60,14 @@ [Using xawtv:] -You must make some modifications to the source and compile it before you use it. -(Note: this may not be applicable to versions other than 3.06) - -In src/Xawtv.ad, change xawtv.tv.width to 640 and xawtv.tv.height to 480. Next, -in src/grab-v4l.c, change SYNC_TIMEOUT from 1 to 2. Then, from the main xawtv -directory: +From the main xawtv directory: make clean ./configure make make install -Now you should be able to run xawtv. Right click for the options dialog. If -you get a scrambled image it is likely that you made a mistake in Xawtv.ad. -Try setting the size to 320x240 if all else fails. +Now you should be able to run xawtv. Right click for the options dialog. MODULE PARAMETERS: @@ -82,7 +75,7 @@ There is currently no way to set these on a per-camera basis. NAME: autoadjust - TYPE: integer (boolean) + TYPE: integer (Boolean) DEFAULT: 1 DESC: The camera normally adjusts exposure, gain, and hue automatically. This can be set to 0 to disable this automatic adjustment. Note that there is @@ -102,7 +95,7 @@ 5=highly repetitive mesgs NAME: fix_rgb_offset - TYPE: integer (boolean) + TYPE: integer (Boolean) DEFAULT: 0 DESC: Some people have reported that the blue component of the image is one or so lines higher than the red component. This is only apparent in @@ -111,7 +104,7 @@ experimental and very buggy. You will likely need a fast (500 MHz) CPU. NAME: snapshot - TYPE: integer (boolean) + TYPE: integer (Boolean) DEFAULT: 0 DESC: Set to 1 to enable snapshot mode. read() will block until the snapshot button is pressed. Note that this does not yet work with most apps, @@ -144,7 +137,7 @@ for if you want to play with the camera's pixel saturation. NAME: force_rgb - TYPE: integer (boolean) + TYPE: integer (Boolean) DEFAULT: 0 DESC: Force image to be read in RGB instead of BGR. This option allow programs that expect RGB data (e.g. gqcam) to work with this driver. If @@ -170,19 +163,45 @@ finding the optimum setting. NAME: retry_sync - TYPE: boolean + TYPE: integer (Boolean) DEFAULT: 0 DESC: Prevent apps from timing out if frame is not done in time. This is useful if you are having problems with Xawtv getting "stuck" on a frame when your system is under heavy load. NAME: sensor_gbr - TYPE: boolean + TYPE: integer (Boolean) DEFAULT: 0 DESC: This makes the sensor output GBR422 instead of YUV420. This saves the driver the trouble of converting YUV to RGB, but it currently does not work very well (the colors are not quite right) + NAME: dumppix + TYPE: integer (0-3) + DEFAULT: 0 + DESC: Dumps raw pixel data, in one of three formats. Only works with RGB24 + format, and is for debugging purposes only. Options are: + 0: Disable (default) + 1: Dump formatted YUV data. One RGB24 pixel per data byte. + 2: Convert Y data to formatted "monochrome" RGB data + 3: Dumps the Y channel as-is. One RGB24 pixel per data byte. + 4: Dumps unformatted YUV data as-is. + + NAME: led + TYPE: integer (0-2) + DEFAULT: 1 (Always on) + DESC: Controls whether the LED (the little light) on the front of the camera + is always off (0), always on (1), or only on when driver is open (2). + This is only supported with the OV511+ chipset, and even then only on + some cameras (ones that actually have the LED wired to the control pin, + and not just hardwired to be on all the time). + + NAME: dump_regs + TYPE: integer (Boolean) + DEFAULT: 0 + DESC: Dumps the camera (OV511) and i2c (sensor) register values to the system + log. Only useful for serious debugging/development purposes. + WORKING FEATURES: o Color streaming/capture at 640x480, 448x336, 384x288, 352x288, and 320x240 o RGB24, RGB565, YUV420, YUV422, YUYV, and YUV422P color @@ -190,21 +209,19 @@ o Setting/getting of saturation, contrast, brightness, and hue (only some of them work the OV7620 and OV7620AE) o /proc status reporting + o OV6620 sensor support EXPERIMENTAL FEATURES: o fix_rgb_offset: Sometimes works, but other times causes errors with xawtv and corrupted frames. If you have a very fast CPU, you can try it. - o Snapshot mode (only works with some read() based apps; see below for more) - o OV6620 sensor support + o Snapshot mode o GBR422 parsing o 160x120 + o OV6630 sensor support -TODO: +TO-DO: o Fix the noise / grainy image problem. - o Get compression working. It would be a nice addition as it improves - frame rate quite a bit. OmniVision wouldn't tell me how the algorithm works, - so we can't really work on that yet. Please kindly inform OmniVision that you - would like them to release their specifications to the Linux community. + o Get compression working. o YUV422 o Fix fixFrameRGBoffset(). It is not stable yet with streaming video. o V4L2 support (Probably not until it goes into the kernel) @@ -213,12 +230,14 @@ o Driver/camera state save/restore for when USB supports suspend/resume o Unstable on SMP systems o OV7620/OV6620 experience frame corruption with moving objects - o OV6620 is too dark o 176x144 support o Driver sometimes hangs upon close() with OHCI o The image should always be written properly to the mmap'ed buffer as long as the requested image size is at least the minimum size. This will likely require a rewrite of all the parsing code. + o OV8600 sensor support (Not used in anything yet) + o OV518+ support + o cams >= 3 not working HOW TO CONTACT ME: @@ -232,3 +251,4 @@ and the USB stack. Thanks to Bret Wallach for getting camera reg IO, ISOC, and image capture working. Thanks to Orion Sky Lawlor, Kevin Moore, and Claudio Matsuoka for their work as well. + diff -Naur linux-2.4.0-orig/drivers/usb/ov511.c linux/drivers/usb/ov511.c --- linux-2.4.0-orig/drivers/usb/ov511.c Tue Jan 30 03:52:30 2001 +++ linux/drivers/usb/ov511.c Tue Jan 30 03:55:32 2001 @@ -30,7 +30,7 @@ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -static const char version[] = "1.28"; +static const char version[] = "1.31"; #define __NO_VERSION__ @@ -52,11 +52,16 @@ #include "ov511.h" #define OV511_I2C_RETRIES 3 +#define ENABLE_Y_QUANTABLE 1 +#define ENABLE_UV_QUANTABLE 1 /* Video Size 640 x 480 x 3 bytes for RGB */ #define MAX_FRAME_SIZE (640 * 480 * 3) #define MAX_DATA_SIZE (MAX_FRAME_SIZE + sizeof(struct timeval)) +/* Max size * bytes per YUV420 pixel (1.5) + one extra isoc frame for safety */ +#define MAX_RAW_DATA_SIZE (((640 * 480 * 3) / 2) + 1024) + #define GET_SEGSIZE(p) ((p) == VIDEO_PALETTE_GREY ? 256 : 384) /* PARAMETER VARIABLES: */ @@ -115,6 +120,14 @@ /* Dump raw pixel data, in one of 3 formats. See ov511_dumppix() for details. */ static int dumppix = 0; +/* LED policy. Only works on some OV511+ cameras. 0=off, 1=on (default), 2=auto + * (on when open) */ +static int led = 1; + +/* Set this to 1 to dump the bridge and sensor register contents after + * initialization */ +static int dump_regs = 0; + MODULE_PARM(autoadjust, "i"); MODULE_PARM_DESC(autoadjust, "CCD dynamically changes exposure"); MODULE_PARM(debug, "i"); @@ -145,29 +158,30 @@ MODULE_PARM_DESC(sensor_gbr, "Make sensor output GBR422 rather than YUV420"); MODULE_PARM(dumppix, "i"); MODULE_PARM_DESC(dumppix, "Dump raw pixel data, in one of 3 formats. See ov511_dumppix() for details"); +MODULE_PARM(led, "i"); +MODULE_PARM_DESC(led, "Sets LED policy. Only works on some OV511+ cameras. 0=off, 1=on (default), 2=auto (on when open)"); +MODULE_PARM(dump_regs, "i"); +MODULE_PARM_DESC(dump_regs, "Dump the bridge and sensor registers"); MODULE_AUTHOR("Mark McClelland & Bret Wallach & Orion Sky Lawlor & Kevin Moore & Charl P. Botha & Claudio Matsuoka "); MODULE_DESCRIPTION("OV511 USB Camera Driver"); -char kernel_version[] = UTS_RELEASE; - static struct usb_driver ov511_driver; -/* I know, I know, global variables suck. This is only a temporary hack */ -int output_offset; - /********************************************************************** * List of known OV511-based cameras **********************************************************************/ static struct cam_list clist[] = { { 0, "generic model (no ID)" }, + { 1, "Mustek WCam 3X" }, { 3, "D-Link DSB-C300" }, { 4, "generic OV511/OV7610" }, { 5, "Puretek PT-6007" }, { 21, "Creative Labs WebCam 3" }, { 36, "Koala-Cam" }, { 38, "Lifeview USB Life TV" }, /* No support yet! */ + { 43, "Mtekvision Zeca MV402" }, { 100, "Lifeview RoboCam" }, { 102, "AverMedia InterCam Elite" }, { 112, "MediaForte MV300" }, /* or OV7110 evaluation kit */ @@ -177,6 +191,7 @@ static __devinitdata struct usb_device_id device_table [] = { { USB_DEVICE(0x05a9, 0x0511) }, /* OV511 */ { USB_DEVICE(0x05a9, 0xA511) }, /* OV511+ */ + { USB_DEVICE(0x05a9, 0x0518) }, /* OV518+ */ { USB_DEVICE(0x0813, 0x0002) }, /* Intel Play Me2Cam OV511+ */ { } /* Terminating entry */ }; @@ -205,6 +220,9 @@ }; #endif +static unsigned char yQuanTable[] = YQUANTABLE; +static unsigned char uvQuanTable[] = UVQUANTABLE; + /********************************************************************** * * Memory management @@ -346,6 +364,7 @@ out += sprintf (out, "brightness : %d\n", ov511->brightness >> 8); out += sprintf (out, "colour : %d\n", ov511->colour >> 8); out += sprintf (out, "contrast : %d\n", ov511->contrast >> 8); + out += sprintf (out, "hue : %d\n", ov511->hue >> 8); out += sprintf (out, "num_frames : %d\n", OV511_NUMFRAMES); for (i = 0; i < OV511_NUMFRAMES; i++) { out += sprintf (out, "frame : %d\n", i); @@ -371,12 +390,16 @@ out += sprintf (out, "bridge : %s\n", ov511->bridge == BRG_OV511 ? "OV511" : ov511->bridge == BRG_OV511PLUS ? "OV511+" : + ov511->bridge == BRG_OV518PLUS ? "OV518+" : "unknown"); out += sprintf (out, "sensor : %s\n", ov511->sensor == SEN_OV6620 ? "OV6620" : + ov511->sensor == SEN_OV6630 ? "OV6630" : ov511->sensor == SEN_OV7610 ? "OV7610" : ov511->sensor == SEN_OV7620 ? "OV7620" : ov511->sensor == SEN_OV7620AE ? "OV7620AE" : + ov511->sensor == SEN_OV8600 ? "OV8600" : + ov511->sensor == SEN_KS0127 ? "KS0127" : "unknown"); out += sprintf (out, "packet_size : %d\n", ov511->packet_size); out += sprintf (out, "framebuffer : 0x%p\n", ov511->fbuf); @@ -477,14 +500,14 @@ { int rc; + PDEBUG(5, "0x%02X:0x%02X", reg, value); + rc = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 2 /* REG_IO */, USB_TYPE_CLASS | USB_RECIP_DEVICE, 0, (__u16)reg, &value, 1, HZ); - PDEBUG(5, "reg write: 0x%02X:0x%02X, 0x%x", reg, value, rc); - if (rc < 0) err("reg write: error %d", rc); @@ -503,7 +526,7 @@ USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_DEVICE, 0, (__u16)reg, buffer, 1, HZ); - PDEBUG(5, "reg read: 0x%02X:0x%02X", reg, buffer[0]); + PDEBUG(5, "0x%02X:0x%02X", reg, buffer[0]); if (rc < 0) { err("reg read: error %d", rc); @@ -513,13 +536,141 @@ } } +/* + * Writes bits at positions specified by mask to a reg. Bits that are in + * the same position as 1's in "mask" are cleared and set to "value". Bits + * that are in the same position as 0's in "mask" are preserved, regardless + * of their respective state in "value". + */ +static int ov511_reg_write_mask(struct usb_device *dev, + unsigned char reg, + unsigned char value, + unsigned char mask) +{ + int ret; + unsigned char oldval, newval; + + ret = ov511_reg_read(dev, reg); + if (ret < 0) + return ret; + + oldval = (unsigned char) ret; + oldval &= (~mask); /* Clear the masked bits */ + value &= mask; /* Enforce mask on value */ + newval = oldval | value; /* Set the desired bits */ + + return (ov511_reg_write(dev, reg, newval)); +} + +/* Writes multiple (n) values to a single register. Only valid with certain + * registers (0x30 and 0xc4 - 0xce). I don't know yet what this really does */ +static int ov518_reg_write_multi(struct usb_device *dev, + unsigned char reg, + unsigned char *values, + int n) +{ + int rc; + + PDEBUG(5, "0x%02X:[multiple], n=%d", reg, n); // FIXME + + if (values == NULL) { + err("reg write multiple: NULL buffer"); + return -EINVAL; + } + + rc = usb_control_msg(dev, + usb_sndctrlpipe(dev, 0), + 2 /* REG_IO */, + USB_TYPE_CLASS | USB_RECIP_DEVICE, + 0, (__u16)reg, values, n, HZ); + + if (rc < 0) + err("reg write multiple: error %d", rc); + + return rc; +} + +static int ov511_upload_quan_tables(struct usb_device *dev) +{ + unsigned char *pYTable = yQuanTable; + unsigned char *pUVTable = uvQuanTable; + unsigned char val0, val1; + int i, rc, reg = OV511_OMNICE_Y_LUT_BEGIN; + + for(i = 0; i < QUANTABLESIZE / 2; i++) + { + if (ENABLE_Y_QUANTABLE) + { + val0 = *pYTable++; + val1 = *pYTable++; + val0 &= 0x0f; + val1 &= 0x0f; + val0 |= val1 << 4; + rc = ov511_reg_write(dev, reg, val0); + if (rc) + return rc; + } + + if (ENABLE_UV_QUANTABLE) + { + val0 = *pUVTable++; + val1 = *pUVTable++; + val0 &= 0x0f; + val1 &= 0x0f; + val0 |= val1 << 4; + rc = ov511_reg_write(dev, reg + QUANTABLESIZE / 2, val0); + if (rc) + return rc; + } + + reg++; + } + + return 0; +} + +/* The OV518 I2C I/O procedure is different, hence, this function. + * This is normally only called from ov511_i2c_write(). Note that this function + * always succeeds regardless of whether the sensor is present and working. + */ +static int ov518_i2c_write(struct usb_device *dev, + unsigned char reg, + unsigned char value) +{ + int rc; + + PDEBUG(5, "0x%02X:0x%02X", reg, value); + + /* Select camera register */ + rc = ov511_reg_write(dev, OV511_REG_I2C_SUB_ADDRESS_3_BYTE, reg); + if (rc < 0) goto error; + + /* Write "value" to I2C data port of OV511 */ + rc = ov511_reg_write(dev, OV511_REG_I2C_DATA_PORT, value); + if (rc < 0) goto error; + + /* Initiate 3-byte write cycle */ + rc = ov511_reg_write(dev, OV518_REG_I2C_CONTROL, 0x01); + if (rc < 0) goto error; + + return 0; + +error: + err("ov518 i2c write: error %d", rc); + return rc; +} + static int ov511_i2c_write(struct usb_device *dev, unsigned char reg, unsigned char value) { int rc, retries; - PDEBUG(5, "i2c write: 0x%02X:0x%02X", reg, value); + /* OV518 needs a different write algorithm */ + if (dev->descriptor.idProduct == 0x0518) + return (ov518_i2c_write(dev, reg, value)); + + PDEBUG(5, "0x%02X:0x%02X", reg, value); /* Three byte write cycle */ for (retries = OV511_I2C_RETRIES; ; ) { @@ -559,11 +710,46 @@ return rc; } +/* The OV518 I2C I/O procedure is different, hence, this function. + * This is normally only called from ov511_i2c_read(). Note that this function + * always succeeds regardless of whether the sensor is present and working. + */ +static int ov518_i2c_read(struct usb_device *dev, unsigned char reg) +{ + int rc, value; + + /* Select camera register */ + rc = ov511_reg_write(dev, OV511_REG_I2C_SUB_ADDRESS_2_BYTE, reg); + if (rc < 0) goto error; + + /* Initiate 2-byte write cycle */ + rc = ov511_reg_write(dev, OV518_REG_I2C_CONTROL, 0x03); + if (rc < 0) goto error; + + /* Initiate 2-byte read cycle */ + rc = ov511_reg_write(dev, OV518_REG_I2C_CONTROL, 0x05); + if (rc < 0) goto error; + + value = ov511_reg_read(dev, OV511_REG_I2C_DATA_PORT); + + PDEBUG(5, "0x%02X:0x%02X", reg, value); + + return value; + +error: + err("ov518 i2c read: error %d", rc); + return rc; +} + /* returns: negative is error, pos or zero is data */ static int ov511_i2c_read(struct usb_device *dev, unsigned char reg) { int rc, value, retries; + /* OV518 needs a different read algorithm */ + if (dev->descriptor.idProduct == 0x0518) + return (ov518_i2c_read(dev, reg)); + /* Two byte write cycle */ for (retries = OV511_I2C_RETRIES; ; ) { /* Select camera register */ @@ -617,7 +803,7 @@ value = ov511_reg_read(dev, OV511_REG_I2C_DATA_PORT); - PDEBUG(5, "i2c read: 0x%02X:0x%02X", reg, value); + PDEBUG(5, "0x%02X:0x%02X", reg, value); /* This is needed to make ov511_i2c_write() work */ rc = ov511_reg_write(dev, OV511_REG_I2C_CONTROL, 0x05); @@ -631,6 +817,31 @@ return rc; } +/* Writes bits at positions specified by mask to an I2C reg. Bits that are in + * the same position as 1's in "mask" are cleared and set to "value". Bits + * that are in the same position as 0's in "mask" are preserved, regardless + * of their respective state in "value". + */ +static int ov511_i2c_write_mask(struct usb_device *dev, + unsigned char reg, + unsigned char value, + unsigned char mask) +{ + int ret; + unsigned char oldval, newval; + + ret = ov511_i2c_read(dev, reg); + if (ret < 0) + return ret; + + oldval = (unsigned char) ret; + oldval &= (~mask); /* Clear the masked bits */ + value &= mask; /* Enforce mask on value */ + newval = oldval | value; /* Set the desired bits */ + + return (ov511_i2c_write(dev, reg, newval)); +} + static int ov511_write_regvals(struct usb_device *dev, struct ov511_regvals * pRegvals) { @@ -666,59 +877,62 @@ int rc; for(i = reg1; i <= regn; i++) { rc = ov511_i2c_read(dev, i); - PDEBUG(1, "OV7610[0x%X] = 0x%X", i, rc); + info("OV7610[0x%X] = 0x%X", i, rc); } } static void ov511_dump_i2c_regs(struct usb_device *dev) { - PDEBUG(3, "I2C REGS"); + info("I2C REGS"); ov511_dump_i2c_range(dev, 0x00, 0x7C); } -#if 0 static void ov511_dump_reg_range(struct usb_device *dev, int reg1, int regn) { int i; int rc; for(i = reg1; i <= regn; i++) { rc = ov511_reg_read(dev, i); - PDEBUG(1, "OV511[0x%X] = 0x%X", i, rc); + info("OV511[0x%X] = 0x%X", i, rc); } } static void ov511_dump_regs(struct usb_device *dev) { - PDEBUG(1, "CAMERA INTERFACE REGS"); + info("CAMERA INTERFACE REGS"); ov511_dump_reg_range(dev, 0x10, 0x1f); - PDEBUG(1, "DRAM INTERFACE REGS"); + info("DRAM INTERFACE REGS"); ov511_dump_reg_range(dev, 0x20, 0x23); - PDEBUG(1, "ISO FIFO REGS"); + info("ISO FIFO REGS"); ov511_dump_reg_range(dev, 0x30, 0x31); - PDEBUG(1, "PIO REGS"); + info("PIO REGS"); ov511_dump_reg_range(dev, 0x38, 0x39); ov511_dump_reg_range(dev, 0x3e, 0x3e); - PDEBUG(1, "I2C REGS"); + info("I2C REGS"); ov511_dump_reg_range(dev, 0x40, 0x49); - PDEBUG(1, "SYSTEM CONTROL REGS"); + info("SYSTEM CONTROL REGS"); ov511_dump_reg_range(dev, 0x50, 0x55); ov511_dump_reg_range(dev, 0x5e, 0x5f); - PDEBUG(1, "OmniCE REGS"); + info("OmniCE REGS"); ov511_dump_reg_range(dev, 0x70, 0x79); ov511_dump_reg_range(dev, 0x80, 0x9f); ov511_dump_reg_range(dev, 0xa0, 0xbf); } #endif -#endif -static int ov511_reset(struct usb_device *dev, unsigned char reset_type) +static int ov511_reset(struct usb_ov511 *ov511, unsigned char reset_type) { int rc; - + + /* Setting bit 0 not allowed on 518/518Plus */ + if (ov511->bridge == BRG_OV518PLUS) + reset_type &= 0xfe; + PDEBUG(4, "Reset: type=0x%X", reset_type); - rc = ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, reset_type); - rc = ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, 0); + + rc = ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_RESET, reset_type); + rc = ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_RESET, 0); if (rc < 0) err("reset: command failed"); @@ -728,24 +942,83 @@ /* Temporarily stops OV511 from functioning. Must do this before changing * registers while the camera is streaming */ -static inline int ov511_stop(struct usb_device *dev) +static inline int ov511_stop(struct usb_ov511 *ov511) { PDEBUG(4, "stopping"); - return (ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, 0x3d)); + if (ov511->bridge == BRG_OV518PLUS) + return (ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_RESET, 0x3c)); + else + return (ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_RESET, 0x3d)); } /* Restarts OV511 after ov511_stop() is called */ -static inline int ov511_restart(struct usb_device *dev) +static inline int ov511_restart(struct usb_ov511 *ov511) { PDEBUG(4, "restarting"); - return (ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, 0x00)); + return (ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_RESET, 0x00)); +} + +/* Sets I2C read and write slave IDs. Returns <0 for error */ +static int ov51x_set_slave_ids(struct usb_ov511 *ov511, + unsigned char write_id, + unsigned char read_id) +{ + struct usb_device *dev = ov511->dev; + + if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE, write_id) < 0) + return -EIO; + + if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ, read_id) < 0) + return -EIO; + + if (ov511_reset(ov511, OV511_RESET_NOREGS) < 0) + return -EIO; + + return 0; +} + +/* This does an initial reset of an OmniVision sensor and ensures that I2C + * is synchronized. Returns <0 for failure. + */ +static int ov51x_init_ov_sensor(struct usb_ov511 *ov511) +{ + struct usb_device *dev = ov511->dev; + int i, success; + + /* Reset the sensor */ + if (ov511_i2c_write(dev, 0x12, 0x80) < 0) return -EIO; + + /* Wait for it to initialize */ + schedule_timeout (1 + 150 * HZ / 1000); + + for (i = 0, success = 0; i < i2c_detect_tries && !success; i++) { + if ((ov511_i2c_read(dev, OV7610_REG_ID_HIGH) == 0x7F) && + (ov511_i2c_read(dev, OV7610_REG_ID_LOW) == 0xA2)) { + success = 1; + continue; + } + + /* Reset the sensor */ + if (ov511_i2c_write(dev, 0x12, 0x80) < 0) return -EIO; + /* Wait for it to initialize */ + schedule_timeout (1 + 150 * HZ / 1000); + /* Dummy read to sync I2C */ + if (ov511_i2c_read(dev, 0x00) < 0) return -EIO; + } + + if (!success) + return -EIO; + + PDEBUG(1, "I2C synced in %d attempt(s)", i); + + return 0; } static int ov511_set_packet_size(struct usb_ov511 *ov511, int size) { int alt, mult; - if (ov511_stop(ov511->dev) < 0) + if (ov511_stop(ov511) < 0) return -EIO; mult = size >> 5; @@ -773,6 +1046,19 @@ err("Set packet size: invalid size (%d)", size); return -EINVAL; } + } else if (ov511->bridge == BRG_OV518PLUS) { + if (size == 0) alt = OV518PLUS_ALT_SIZE_0; + else if (size == 128) alt = OV518PLUS_ALT_SIZE_128; + else if (size == 256) alt = OV518PLUS_ALT_SIZE_256; + else if (size == 384) alt = OV518PLUS_ALT_SIZE_384; + else if (size == 512) alt = OV518PLUS_ALT_SIZE_512; + else if (size == 640) alt = OV518PLUS_ALT_SIZE_640; + else if (size == 768) alt = OV518PLUS_ALT_SIZE_768; + else if (size == 896) alt = OV518PLUS_ALT_SIZE_896; + else { + err("Set packet size: invalid size (%d)", size); + return -EINVAL; + } } else { err("Set packet size: Invalid bridge type"); return -EINVAL; @@ -780,21 +1066,28 @@ PDEBUG(3, "set packet size: %d, mult=%d, alt=%d", size, mult, alt); - if (ov511_reg_write(ov511->dev, OV511_REG_FIFO_PACKET_SIZE, mult) < 0) - return -ENOMEM; + // FIXME: Don't know how to do this on OV518 yet + if (ov511->bridge != BRG_OV518PLUS) + if (ov511_reg_write(ov511->dev, OV511_REG_FIFO_PACKET_SIZE, mult) < 0) + return -EIO; if (usb_set_interface(ov511->dev, ov511->iface, alt) < 0) { err("Set packet size: set interface error"); return -EBUSY; } + /* I don't know what this does */ + if (ov511->bridge == BRG_OV518PLUS) + if (ov511_reg_write(ov511->dev, 0x2f, 0x80) < 0) + return -EIO; + // FIXME - Should we only reset the FIFO? - if (ov511_reset(ov511->dev, OV511_RESET_NOREGS) < 0) - return -ENOMEM; + if (ov511_reset(ov511, OV511_RESET_NOREGS) < 0) + return -EIO; ov511->packet_size = size; - if (ov511_restart(ov511->dev) < 0) + if (ov511_restart(ov511) < 0) return -EIO; return 0; @@ -809,7 +1102,7 @@ PDEBUG(4, "ov511_set_picture"); - if (ov511_stop(dev) < 0) + if (ov511_stop(ov511) < 0) return -EIO; ov511->contrast = p->contrast; @@ -845,6 +1138,19 @@ } else if ((ov511->sensor == SEN_OV7620) || (ov511->sensor == SEN_OV7620AE)) { #if 0 + // These are 7620 only + + /* Use gamma control instead */ + if (ov511_i2c_write(dev, 0x64, (p->contrast >> 8) | 1) < 0) + return -EIO; + + /* These regs are undocumented. They may be backwards */ + if (ov511_i2c_write(dev, 0x7a, 0xFF - (p->hue >> 8)) < 0) + return -EIO; + + if (ov511_i2c_write(dev, 0x79, p->hue >> 8) < 0) + return -EIO; + int cur_sat, new_sat, tmp; cur_sat = ov511_i2c_read(dev, OV7610_REG_BLUE); @@ -863,7 +1169,7 @@ #endif } - if (ov511_restart(dev) < 0) + if (ov511_restart(ov511) < 0) return -EIO; return 0; @@ -877,7 +1183,7 @@ PDEBUG(4, "ov511_get_picture"); - if (ov511_stop(dev) < 0) + if (ov511_stop(ov511) < 0) return -EIO; if ((ret = ov511_i2c_read(dev, OV7610_REG_SAT)) < 0) return -EIO; @@ -899,12 +1205,26 @@ p->depth = ov511->frame[0].depth; p->palette = ov511->frame[0].format; - if (ov511_restart(dev) < 0) + if (ov511_restart(ov511) < 0) return -EIO; return 0; } +/* Turns on or off the LED. Only has an effect with OV511+/OV518+ */ +static inline void +ov511_led_control(struct usb_ov511 *ov511, int enable) { + PDEBUG(4, " (%s)", enable ? "turn on" : "turn off"); + + if (ov511->bridge == BRG_OV511PLUS) + ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_LED_CTL, + enable ? 1 : 0); + else if (ov511->bridge == BRG_OV518PLUS) + ov511_reg_write_mask(ov511->dev, OV518_REG_GPIO_OUT, + enable ? 0x02 : 0x00, 0x02); + return; +} + /* Returns number of bits per pixel (regardless of where they are located; planar or * not), or zero for unsupported format. */ @@ -923,7 +1243,7 @@ } /* LNCNT values fixed by Lawrence Glaister */ -static struct mode_list mlist[] = { +static struct mode_list_511 mlist511[] = { /* W H C PXCNT LNCNT PXDIV LNDIV M420 COMA COML */ { 640, 480, 0, 0x4f, 0x3b, 0x00, 0x00, 0x03, 0x24, 0x9e }, { 640, 480, 1, 0x4f, 0x3b, 0x00, 0x00, 0x03, 0x24, 0x9e }, @@ -954,7 +1274,7 @@ PDEBUG(3, "width:%d, height:%d, mode:%d, sub:%d", width, height, mode, sub_flag); - if (ov511_stop(ov511->dev) < 0) + if (ov511_stop(ov511) < 0) return -EIO; /* Dumppix only works with RGB24 */ @@ -967,10 +1287,10 @@ ov511_reg_write(dev, 0x16, 0x00); if (ov511->sensor == SEN_OV7610 || ov511->sensor == SEN_OV7620AE) { - /* these aren't valid on the OV6620/OV7620 */ + /* these aren't valid on the OV6620/OV7620/6630? */ ov511_i2c_write(dev, 0x0e, 0x44); } - ov511_i2c_write(dev, 0x13, autoadjust ? 0x21 : 0x20); + ov511_i2c_write_mask(dev, 0x13, autoadjust ? 0x21 : 0x20, 0x21); /* For snapshot */ ov511_reg_write(dev, 0x1e, 0x00); @@ -979,10 +1299,10 @@ ov511_reg_write(dev, 0x16, 0x01); if (ov511->sensor == SEN_OV7610 || ov511->sensor == SEN_OV7620AE) { - /* not valid on the OV6620/OV7620 */ + /* not valid on the OV6620/OV7620/6630? */ ov511_i2c_write(dev, 0x0e, 0x04); } - ov511_i2c_write(dev, 0x13, autoadjust ? 0x01 : 0x00); + ov511_i2c_write_mask(dev, 0x13, autoadjust ? 0x01 : 0x00, 0x21); /* For snapshot */ ov511_reg_write(dev, 0x1e, 0x01); @@ -998,6 +1318,7 @@ vwsbase = vwebase = 0x05; break; case SEN_OV6620: + case SEN_OV6630: // FIXME: Is this right? hwsbase = 0x38; hwebase = 0x3a; vwsbase = 0x05; @@ -1055,51 +1376,351 @@ ov511_i2c_write(dev, 0x1a, vwebase + (vwsize>>vwscale)); } - for (i = 0; mlist[i].width; i++) { + for (i = 0; mlist511[i].width; i++) { int lncnt, pxcnt, clock; - if (width != mlist[i].width || height != mlist[i].height) + if (width != mlist511[i].width || height != mlist511[i].height) continue; - if (!mlist[i].color && mode != VIDEO_PALETTE_GREY) + if (!mlist511[i].color && mode != VIDEO_PALETTE_GREY) continue; /* Here I'm assuming that snapshot size == image size. * I hope that's always true. --claudio */ - pxcnt = sub_flag ? (ov511->subw >> 3) - 1 : mlist[i].pxcnt; - lncnt = sub_flag ? (ov511->subh >> 3) - 1 : mlist[i].lncnt; + pxcnt = sub_flag ? (ov511->subw >> 3) - 1 : mlist511[i].pxcnt; + lncnt = sub_flag ? (ov511->subh >> 3) - 1 : mlist511[i].lncnt; ov511_reg_write(dev, 0x12, pxcnt); ov511_reg_write(dev, 0x13, lncnt); - ov511_reg_write(dev, 0x14, mlist[i].pxdv); - ov511_reg_write(dev, 0x15, mlist[i].lndv); - ov511_reg_write(dev, 0x18, mlist[i].m420); + ov511_reg_write(dev, 0x14, mlist511[i].pxdv); + ov511_reg_write(dev, 0x15, mlist511[i].lndv); + ov511_reg_write(dev, 0x18, mlist511[i].m420); /* Snapshot additions */ ov511_reg_write(dev, 0x1a, pxcnt); ov511_reg_write(dev, 0x1b, lncnt); - ov511_reg_write(dev, 0x1c, mlist[i].pxdv); - ov511_reg_write(dev, 0x1d, mlist[i].lndv); + ov511_reg_write(dev, 0x1c, mlist511[i].pxdv); + ov511_reg_write(dev, 0x1d, mlist511[i].lndv); + + /* The OV6620 needs special handling. This prevents the + * severe banding that normally occurs */ + if (ov511->sensor == SEN_OV6620) + { + /* Clock down */ + ov511_i2c_write(dev, 0x2a, 0x04); + ov511_i2c_write(dev, 0x11, 0x03); + ov511_i2c_write(dev, 0x2a, 0x84); + /* This next setting is critical. It seems to improve + * the gain or the contrast. The "reserved" bits seem + * to have some effect in this case. */ + ov511_i2c_write(dev, 0x2d, 0x85); + + } + else + { + /* Calculate and set the clock divisor */ + clock = ((sub_flag ? ov511->subw * ov511->subh : width * height) + * (mlist511[i].color ? 3 : 2) / 2) / 66000; +#if 0 + clock *= cams; +#endif + ov511_i2c_write(dev, 0x11, clock); + } + + /* We only have code to convert GBR -> RGB24 */ + if ((mode == VIDEO_PALETTE_RGB24) && sensor_gbr) + ov511_i2c_write(dev, 0x12, mlist511[i].common_A | (testpat?0x0a:0x08)); + else + ov511_i2c_write(dev, 0x12, mlist511[i].common_A | (testpat?0x02:0x00)); + + /* Enable/Disable AGC */ + if (ov511_i2c_write_mask(dev, 0x12, autoadjust ? 0x20 : 0x00, 0x20) < 0) + return -1; + + /* 7620/6620/6630? don't have register 0x35, so play it safe */ + if (ov511->sensor == SEN_OV7610 || + ov511->sensor == SEN_OV7620AE) + ov511_i2c_write(dev, 0x35, mlist511[i].common_L); + + break; + } + + if (compress) { + ov511_reg_write(dev, 0x78, 0x03); // Turn on Y compression + ov511_reg_write(dev, 0x79, 0x00); // Disable LUTs + } + + if (ov511_restart(ov511) < 0) + return -EIO; + + if (mlist511[i].width == 0) { + err("Unknown mode (%d, %d): %d", width, height, mode); + return -EINVAL; + } + +#ifdef OV511_DEBUG + if (dump_regs) + ov511_dump_i2c_regs(dev); +#endif + + return 0; +} + +static struct mode_list_518 mlist518[] = { + /* W H reg28 reg29 reg2a reg2c reg2e reg24 reg25 */ + { 352, 288, 0x80, 0x16, 0x48, 0x00, 0x00, 0x9f, 0x90 }, + { 320, 240, 0x80, 0x14, 0x3c, 0x10, 0x18, 0x9f, 0x90 }, + { 176, 144, 0x85, 0x0b, 0x24, 0x00, 0x00, 0xff, 0xf0 }, + { 160, 120, 0x85, 0x0a, 0x1e, 0x08, 0x0c, 0xff, 0xf0 }, + { 0, 0 } +}; + +/* OV518 needs a completely different approach, until we can figure out what + * the individual registers do. Many register ops are commented out until we + * can find out if they are still valid. Also, only 15 FPS is supported now. + */ +static int +ov518_mode_init_regs(struct usb_ov511 *ov511, + int width, int height, int mode, int sub_flag) +{ + int i; + struct usb_device *dev = ov511->dev; + int hwsbase, hwebase, vwsbase, vwebase, hwsize, vwsize; + int hwscale = 0, vwscale = 0; + unsigned char b[3]; /* Multiple-value reg buffer */ + + PDEBUG(3, "width:%d, height:%d, mode:%d, sub:%d", + width, height, mode, sub_flag); + + if (ov511_stop(ov511) < 0) + return -EIO; + + /* Dumppix only works with RGB24 */ + if (dumppix && (mode != VIDEO_PALETTE_RGB24)) { + err("dumppix only supported with RGB 24"); + return -EINVAL; + } + + /* I don't know how to set YUV400 mode on OV518 yet */ + if (mode == VIDEO_PALETTE_GREY) { +// ov511_reg_write(dev, 0x16, 0x00); + if (ov511->sensor == SEN_OV7610 + || ov511->sensor == SEN_OV7620AE) { + /* these aren't valid on the OV6620/OV7620/6630? */ + ov511_i2c_write(dev, 0x0e, 0x44); + } + ov511_i2c_write_mask(dev, 0x13, autoadjust ? 0x21 : 0x20, 0x21); + } else { +// ov511_reg_write(dev, 0x16, 0x01); + if (ov511->sensor == SEN_OV7610 + || ov511->sensor == SEN_OV7620AE) { + /* not valid on the OV6620/OV7620/6630? */ + ov511_i2c_write(dev, 0x0e, 0x04); + } + ov511_i2c_write_mask(dev, 0x13, autoadjust ? 0x01 : 0x00, 0x21); + } + + /* The different sensor ICs handle setting up of window differently */ + switch (ov511->sensor) { + case SEN_OV7610: + case SEN_OV7620AE: + hwsbase = 0x38; + hwebase = 0x3a; + vwsbase = vwebase = 0x05; + break; + case SEN_OV6620: + case SEN_OV6630: // FIXME: Is this right? + hwsbase = 0x38; + hwebase = 0x3a; + vwsbase = 0x05; + vwebase = 0x06; + break; + case SEN_OV7620: + hwsbase = 0x2c; + hwebase = 0x2d; + vwsbase = vwebase = 0x05; + break; + default: + err("Invalid sensor"); + return -EINVAL; + } + + /* Bit 5 of COM C register varies with sensor */ + if (ov511->sensor == SEN_OV6620) { + if (width > 176 && height > 144) { /* CIF */ + ov511_i2c_write(dev, 0x14, 0x04); + hwscale = 1; + vwscale = 1; /* The datasheet says 0; it's wrong */ + hwsize = 352; + vwsize = 288; + } else { /* QCIF */ + ov511_i2c_write(dev, 0x14, 0x24); + hwsize = 176; + vwsize = 144; + } + } + else { + if (width > 320 && height > 240) { /* VGA */ + ov511_i2c_write(dev, 0x14, 0x04); + hwscale = 2; + vwscale = 1; + hwsize = 640; + vwsize = 480; + } else { /* QVGA */ + ov511_i2c_write(dev, 0x14, 0x24); + hwscale = 1; + hwsize = 320; + vwsize = 240; + } + } + + /* FIXME! - This needs to be changed to support 160x120 and 6620!!! */ + if (sub_flag) { + ov511_i2c_write(dev, 0x17, hwsbase+(ov511->subx>>hwscale)); + ov511_i2c_write(dev, 0x18, hwebase+((ov511->subx+ov511->subw)>>hwscale)); + ov511_i2c_write(dev, 0x19, vwsbase+(ov511->suby>>vwscale)); + ov511_i2c_write(dev, 0x1a, vwebase+((ov511->suby+ov511->subh)>>vwscale)); + } else { + ov511_i2c_write(dev, 0x17, hwsbase); + ov511_i2c_write(dev, 0x18, hwebase + (hwsize>>hwscale)); + ov511_i2c_write(dev, 0x19, vwsbase); + ov511_i2c_write(dev, 0x1a, vwebase + (vwsize>>vwscale)); + } - /* Calculate and set the clock divisor */ - clock = ((sub_flag ? ov511->subw * ov511->subh : width * height) - * (mlist[i].color ? 3 : 2) / 2) / 66000; + for (i = 0; mlist518[i].width; i++) { +// int lncnt, pxcnt; + int clock; + + if (width != mlist518[i].width || height != mlist518[i].height) + continue; + +// FIXME: Subcapture won't be possible until we know what the registers do +// FIXME: We can't handle anything but YUV420 so far + +// /* Here I'm assuming that snapshot size == image size. +// * I hope that's always true. --claudio +// */ +// pxcnt = sub_flag ? (ov511->subw >> 3) - 1 : mlist[i].pxcnt; +// lncnt = sub_flag ? (ov511->subh >> 3) - 1 : mlist[i].lncnt; +// +// ov511_reg_write(dev, 0x12, pxcnt); +// ov511_reg_write(dev, 0x13, lncnt); + + /******** Set the mode ********/ + + /* Mode independent regs */ + ov511_reg_write(dev, 0x2b, 0x00); + ov511_reg_write(dev, 0x2d, 0x00); + ov511_reg_write(dev, 0x3b, 0x00); + ov511_reg_write(dev, 0x3d, 0x00); + + /* Mode dependent regs. Regs 38 - 3e are always the same as + * regs 28 - 2e */ + ov511_reg_write_mask(dev, 0x28, mlist518[i].reg28, 0x0f); + ov511_reg_write(dev, 0x29, mlist518[i].reg29); + ov511_reg_write(dev, 0x2a, mlist518[i].reg2a); + ov511_reg_write(dev, 0x2c, mlist518[i].reg2c); + ov511_reg_write(dev, 0x2e, mlist518[i].reg2e); + ov511_reg_write_mask(dev, 0x38, mlist518[i].reg28, 0x0f); + ov511_reg_write(dev, 0x39, mlist518[i].reg29); + ov511_reg_write(dev, 0x3a, mlist518[i].reg2a); + ov511_reg_write(dev, 0x3c, mlist518[i].reg2c); + ov511_reg_write(dev, 0x3e, mlist518[i].reg2e); + ov511_reg_write(dev, 0x24, mlist518[i].reg24); + ov511_reg_write(dev, 0x25, mlist518[i].reg25); + + /* Windows driver does this here; who knows why */ + ov511_reg_write(dev, 0x2f, 0x80); + + /******** Set the framerate (to 15 FPS) ********/ + + /* Mode independent, but framerate dependent, regs */ + /* These are for 15 FPS only */ + ov511_reg_write(dev, 0x51, 0x08); + ov511_reg_write(dev, 0x22, 0x18); + ov511_reg_write(dev, 0x23, 0xff); + ov511_reg_write(dev, 0x71, 0x19); /* Compression-related? */ + /* Bit 5 is what matters here. Of course, it is "reserved" */ + ov511_i2c_write(dev, 0x54, 0x23); + + ov511_reg_write(dev, 0x2f, 0x80); + + /* Mode dependent regs */ + if ((width == 352 && height == 288) || + (width == 320 && height == 240)) { + b[0]=0x80; b[1]=0x02; ov518_reg_write_multi(dev, 0x30, b, 2); + b[0]=0x90; b[1]=0x01; ov518_reg_write_multi(dev, 0xc4, b, 2); + b[0]=0xf4; b[1]=0x01; ov518_reg_write_multi(dev, 0xc6, b, 2); + b[0]=0xf4; b[1]=0x01; ov518_reg_write_multi(dev, 0xc7, b, 2); + b[0]=0x8e; b[1]=0x00; ov518_reg_write_multi(dev, 0xc8, b, 2); + b[0]=0x1a; b[1]=0x00; b[2]=0x02; ov518_reg_write_multi(dev, 0xca, b, 3); + b[0]=0x14; b[1]=0x02; ov518_reg_write_multi(dev, 0xcb, b, 2); + b[0]=0xd0; b[1]=0x07; ov518_reg_write_multi(dev, 0xcc, b, 2); + b[0]=0x20; b[1]=0x00; ov518_reg_write_multi(dev, 0xcd, b, 2); + b[0]=0x60; b[1]=0x02; ov518_reg_write_multi(dev, 0xce, b, 2); + + } else if ((width == 176 && height == 144) || + (width == 160 && height == 120)) { + b[0]=0x80; b[1]=0x01; ov518_reg_write_multi(dev, 0x30, b, 2); + b[0]=0xc8; b[1]=0x00; ov518_reg_write_multi(dev, 0xc4, b, 2); + b[0]=0x40; b[1]=0x01; ov518_reg_write_multi(dev, 0xc6, b, 2); + b[0]=0x40; b[1]=0x01; ov518_reg_write_multi(dev, 0xc7, b, 2); + b[0]=0x60; b[1]=0x00; ov518_reg_write_multi(dev, 0xc8, b, 2); + b[0]=0x0f; b[1]=0x33; b[2]=0x01; ov518_reg_write_multi(dev, 0xca, b, 3); + b[0]=0x40; b[1]=0x01; ov518_reg_write_multi(dev, 0xcb, b, 2); + b[0]=0xec; b[1]=0x04; ov518_reg_write_multi(dev, 0xcc, b, 2); + b[0]=0x13; b[1]=0x00; ov518_reg_write_multi(dev, 0xcd, b, 2); + b[0]=0x6d; b[1]=0x01; ov518_reg_write_multi(dev, 0xce, b, 2); + } else { + /* Can't happen, since we already handled this case */ + err("ov518_mode_init_regs(): **** logic error ****"); + } + + ov511_reg_write(dev, 0x2f, 0x80); + + /* The OV6620 needs special handling. This prevents the + * severe banding that normally occurs */ + if (ov511->sensor == SEN_OV6620) + { + /* Clock down */ + ov511_i2c_write(dev, 0x2a, 0x04); + ov511_i2c_write(dev, 0x11, 0x03); + ov511_i2c_write(dev, 0x2a, 0x84); + /* This next setting is critical. It seems to improve + * the gain or the contrast. The "reserved" bits seem + * to have some effect in this case. */ + ov511_i2c_write(dev, 0x2d, 0x85); + + } + else + { + /* Calculate and set the clock divisor */ +// Color is always on +// clock = ((sub_flag ? ov511->subw * ov511->subh : width * height) +// * (mlist518[i].color ? 3 : 2) / 2) / 66000; + clock = ((sub_flag ? ov511->subw * ov511->subh : width * height) + * (3 / 2)) / 66000; #if 0 - clock *= cams; + clock *= cams; #endif - ov511_i2c_write(dev, 0x11, clock); + ov511_i2c_write(dev, 0x11, clock); + } /* We only have code to convert GBR -> RGB24 */ if ((mode == VIDEO_PALETTE_RGB24) && sensor_gbr) - ov511_i2c_write(dev, 0x12, mlist[i].common_A | (testpat?0x0a:0x08)); + ov511_i2c_write(dev, 0x12, 0x04 | (testpat?0x0a:0x08)); else - ov511_i2c_write(dev, 0x12, mlist[i].common_A | (testpat?0x02:0x00)); + ov511_i2c_write(dev, 0x12, 0x04 | (testpat?0x02:0x00)); - /* 7620/6620 don't have register 0x35, so play it safe */ + /* Enable/Disable AGC */ + if (ov511_i2c_write_mask(dev, 0x12, autoadjust ? 0x20 : 0x00, 0x20) < 0) + return -1; + + /* 7620/6620/6630? don't have register 0x35, so play it safe */ if (ov511->sensor == SEN_OV7610 || ov511->sensor == SEN_OV7620AE) - ov511_i2c_write(dev, 0x35, mlist[i].common_L); + ov511_i2c_write(dev, 0x35, 0x1e); break; } @@ -1109,22 +1730,60 @@ ov511_reg_write(dev, 0x79, 0x00); // Disable LUTs } - if (ov511_restart(ov511->dev) < 0) + if (ov511_restart(ov511) < 0) return -EIO; - if (mlist[i].width == 0) { + if (mlist518[i].width == 0) { err("Unknown mode (%d, %d): %d", width, height, mode); return -EINVAL; } #ifdef OV511_DEBUG - if (debug >= 5) + if (dump_regs) ov511_dump_i2c_regs(dev); #endif return 0; } +/* This is a wrapper around the OV511, OV518, and sensor specific functions */ +static int +mode_init_regs(struct usb_ov511 *ov511, + int width, int height, int mode, int sub_flag) +{ + if (ov511->bridge == BRG_OV518PLUS) + return ov518_mode_init_regs(ov511, width, height, mode, sub_flag); + else + return ov511_mode_init_regs(ov511, width, height, mode, sub_flag); +} + +/* This sets the default image parameters (Size = max, RGB24). This is + * useful for apps that use read() and do not set these. + */ +static int ov51x_set_default_params(struct usb_ov511 *ov511) +{ + int i; + + /* Set default sizes in case IOCTL (VIDIOCMCAPTURE) is not used + * (using read() instead). */ + for (i = 0; i < OV511_NUMFRAMES; i++) { + ov511->frame[i].width = ov511->maxwidth; + ov511->frame[i].height = ov511->maxheight; + ov511->frame[i].depth = 24; + ov511->frame[i].bytes_read = 0; + ov511->frame[i].segment = 0; + ov511->frame[i].format = VIDEO_PALETTE_RGB24; + ov511->frame[i].segsize = GET_SEGSIZE(ov511->frame[i].format); + } + + /* Initialize to max width/height, RGB24 */ + if (mode_init_regs(ov511, ov511->maxwidth, ov511->maxheight, + VIDEO_PALETTE_RGB24, 0) < 0) + return -EINVAL; + + return 0; +} + /********************************************************************** * * Color correction functions @@ -1373,7 +2032,8 @@ } static void -ov511_dumppix(unsigned char *pIn0, unsigned char *pOut0, +ov511_dumppix(struct ov511_frame *frame, + unsigned char *pIn0, unsigned char *pOut0, int iOutY, int iOutUV, int iHalf, int iWidth) { int i, j, k; @@ -1410,12 +2070,22 @@ break; case 3: /* This will dump only the Y channel data stream as-is */ pIn = pIn0 + 128; - pOut = pOut0 + output_offset; + pOut = pOut0 + frame->output_offset; for (i = 0; i < 256; i++) { *pOut++ = *pIn; *pOut++ = *pIn; *pOut++ = *pIn++; - output_offset += 3; + frame->output_offset += 3; + } + break; + case 4: /* This will dump the whole unformatted data stream as-is */ + pIn = pIn0; + pOut = pOut0 + frame->output_offset; + for (i = 0; i < 384; i++) { + *pOut++ = *pIn; + *pOut++ = *pIn; + *pOut++ = *pIn++; + frame->output_offset += 3; } break; } /* End switch (dumppix) */ @@ -1612,6 +2282,16 @@ /********************************************************************** * + * Decompression + * + **********************************************************************/ +static void ov511_decompress(struct ov511_frame *frame) +{ + err("Not implemented yet (but given %d bytes)", frame->bytes_recvd); +} + +/********************************************************************** + * * OV511 data transfer, IRQ handler * **********************************************************************/ @@ -1623,10 +2303,19 @@ int aPackNum[10]; struct ov511_frame *frame; unsigned char *pData; - int iPix; + int iPix, data_size, pn, num, offset; PDEBUG (4, "Moving %d packets", urb->number_of_packets); + /* OV518+ has no packet numbering */ + if (ov511->bridge == BRG_OV518PLUS) { + pn = 0; + data_size = ov511->packet_size; + } else { + pn = 1; + data_size = ov511->packet_size - 1; + } + for (i = 0; i < urb->number_of_packets; i++) { int n = urb->iso_frame_desc[i].actual_length; int st = urb->iso_frame_desc[i].status; @@ -1636,7 +2325,8 @@ cdata = urb->transfer_buffer + urb->iso_frame_desc[i].offset; - aPackNum[i] = n ? cdata[ov511->packet_size - 1] : -1; + if (pn) + aPackNum[i] = n ? cdata[ov511->packet_size - 1] : -1; if (!n || ov511->curframe == -1) continue; @@ -1645,21 +2335,25 @@ PDEBUG(2, "data error: [%d] len=%d, status=%d", i, n, st); frame = &ov511->frame[ov511->curframe]; - + /* SOF/EOF packets have 1st to 8th bytes zeroed and the 9th * byte non-zero. The EOF packet has image width/height in the - * 10th and 11th packets. The 9th bit is given as follows: + * 10th and 11th bytes. The 9th byte is given as follows: * * bit 7: EOF * 6: compression enabled * 5: 422/420/400 modes * 4: 422/420/400 modes * 3: 1 - * 2: snapshot bottom on + * 2: snapshot button on * 1: snapshot frame * 0: even/odd field */ - +#if 0 + PDEBUG(5, "packet header: %d %d %d %d %d %d %d %d %d %d %d %d", + cdata[0], cdata[1], cdata[2], cdata[3], cdata[4], cdata[5], + cdata[6], cdata[7], cdata[8], cdata[9], cdata[10], cdata[11]); +#endif /* Check for SOF/EOF packet */ if ((cdata[0] | cdata[1] | cdata[2] | cdata[3] | cdata[4] | cdata[5] | cdata[6] | cdata[7]) || @@ -1673,16 +2367,21 @@ ts = (struct timeval *)(frame->data + MAX_FRAME_SIZE); do_gettimeofday (ts); - PDEBUG(4, "Frame end, curframe = %d, packnum=%d, hw=%d, vw=%d", - ov511->curframe, (int)(cdata[ov511->packet_size - 1]), - (int)(cdata[9]), (int)(cdata[10])); + PDEBUG(4, "Frame end, curframe = %d, packnum=%d, hw=%d, vw=%d, recvd=%d", + ov511->curframe, + (int)(pn ? cdata[ov511->packet_size - 1] : -1), + (int)(cdata[9]), (int)(cdata[10]), frame->bytes_recvd); if (frame->scanstate == STATE_LINES) { int iFrameNext; + /* Would ideally be done on user's time */ + if (frame->compressed) + ov511_decompress(frame); + if (fix_rgb_offset) fixFrameRGBoffset(frame); - frame->grabstate = FRAME_DONE; + frame->grabstate = FRAME_DONE; // FIXME: Is this right? if (waitqueue_active(&frame->wq)) { frame->grabstate = FRAME_DONE; @@ -1719,7 +2418,6 @@ * for compression experimentation */ memset(frame->data, 0, MAX_DATA_SIZE); #endif - output_offset = 0; /* Check to see if it's a snapshot frame */ /* FIXME?? Should the snapshot reset go here? Performance? */ @@ -1730,6 +2428,8 @@ frame->scanstate = STATE_LINES; frame->segment = 0; + frame->bytes_recvd = 0; + frame->output_offset = 0; } check_middle: @@ -1737,6 +2437,25 @@ if (frame->scanstate != STATE_LINES) continue; + /* If frame start, skip header */ + if (frame->bytes_recvd == 0) + offset = 9; + else + offset = 0; + + num = n - offset - (pn ? 1 : 0); + frame->bytes_recvd += num; + + if (compress) { + if (frame->bytes_recvd <= MAX_RAW_DATA_SIZE) + memmove(frame->rawdata + frame->bytes_recvd - num, + &cdata[offset], num); + else + err("Raw data buffer overrun!!"); + + continue; /* Skip all other processing */ + } + /* Deal with leftover from last segment, if any */ if (frame->segment) { pData = ov511->scratch; @@ -1744,12 +2463,12 @@ memmove(pData + ov511->scratchlen, cdata, iPix+frame->segsize); } else { - pData = &cdata[iPix = 9]; + pData = &cdata[iPix = 9]; /* Start of frame */ } /* Parse the segments */ - while (iPix <= (ov511->packet_size - 1) - frame->segsize && - frame->segment < frame->width * frame->height / 256) { + while (iPix <= data_size - frame->segsize && + frame->segment < frame->width * frame->height / PIXELS_PER_SEG) { int iSegY, iSegUV; int iY, jY, iUV, jUV; int iOutY, iOutYP, iOutUV, iOutUVP; @@ -1767,7 +2486,7 @@ iSeg1 = iSegY / (ov511->subw / 32); iSeg1 *= frame->width / 32; iSegY = iSeg1 + (iSegY % (ov511->subw / 32)); - if (iSegY >= frame->width * ov511->subh / 256) + if (iSegY >= frame->width * ov511->subh / PIXELS_PER_SEG) break; iSeg1 = iSegUV / (ov511->subw / 16); @@ -1798,7 +2517,7 @@ break; case VIDEO_PALETTE_RGB24: if (dumppix) - ov511_dumppix(pData, pOut, iOutY, iOutUV, + ov511_dumppix(frame, pData, pOut, iOutY, iOutUV, iY & 1, frame->width); else if (sensor_gbr) ov511_parse_gbr422_to_rgb24(pData, pOut, iOutY, iOutUV, @@ -1831,8 +2550,8 @@ } /* Save extra data for next time */ - if (frame->segment < frame->width * frame->height / 256) { - ov511->scratchlen = (ov511->packet_size - 1) - iPix; + if (frame->segment < frame->width * frame->height / PIXELS_PER_SEG) { + ov511->scratchlen = data_size - iPix; if (ov511->scratchlen < frame->segsize) memmove(ov511->scratch, pData, ov511->scratchlen); else @@ -1840,9 +2559,11 @@ } } - PDEBUG(5, "pn: %d %d %d %d %d %d %d %d %d %d", - aPackNum[0], aPackNum[1], aPackNum[2], aPackNum[3], aPackNum[4], - aPackNum[5],aPackNum[6], aPackNum[7], aPackNum[8], aPackNum[9]); + if (pn) { + PDEBUG(5, "pn: %d %d %d %d %d %d %d %d %d %d", + aPackNum[0], aPackNum[1], aPackNum[2], aPackNum[3], aPackNum[4], + aPackNum[5],aPackNum[6], aPackNum[7], aPackNum[8], aPackNum[9]); + } return totlen; } @@ -1869,9 +2590,14 @@ PDEBUG(4, "hmmm... not streaming, but got interrupt"); return; } - + sbuf = &ov511->sbuf[ov511->cursbuf]; + if (sbuf == NULL) { + err("null scrath buffer pointer!"); + return; + } + /* Copy the data received into our scratch buffer */ if (ov511->curframe >= 0) { len = ov511_move_data(ov511, urb); @@ -1899,30 +2625,43 @@ ov511->cursbuf = 0; ov511->scratchlen = 0; - if (ov511->bridge == BRG_OV511) - if (cams == 1) size = 993; - else if (cams == 2) size = 513; - else if (cams == 3 || cams == 4) size = 257; + if (ov511->bridge == BRG_OV511) { + if (cams == 1) size = 993; + else if (cams == 2) size = 513; + else if (cams == 3 || cams == 4) size = 257; else { err("\"cams\" parameter too high!"); return -1; } - else if (ov511->bridge == BRG_OV511PLUS) - if (cams == 1) size = 961; - else if (cams == 2) size = 513; - else if (cams == 3 || cams == 4) size = 257; - else if (cams >= 5 && cams <= 8) size = 129; - else if (cams >= 9 && cams <= 31) size = 33; + } else if (ov511->bridge == BRG_OV511PLUS) { + if (cams == 1) size = 961; + else if (cams == 2) size = 513; + else if (cams == 3 || cams == 4) size = 257; + else if (cams >= 5 && cams <= 8) size = 129; + else if (cams >= 9 && cams <= 31) size = 33; else { err("\"cams\" parameter too high!"); return -1; } - else { + } else if (ov511->bridge == BRG_OV518PLUS) { + if (cams == 1) size = 896; + else if (cams == 2) size = 512; + else if (cams == 3 || cams == 4) size = 256; + else if (cams >= 5 && cams <= 8) size = 128; + else { + err("\"cams\" parameter too high!"); + return -1; + } + } else { err("invalid bridge type"); return -1; } - ov511_set_packet_size(ov511, size); + // FIXME: OV518 is hardcoded to 15 FPS (alternate 5) for now + if (ov511->bridge == BRG_OV518PLUS) + ov511_set_packet_size(ov511, 640); + else + ov511_set_packet_size(ov511, size); for (n = 0; n < OV511_NUMSBUF; n++) { urb = usb_alloc_urb(FRAMES_PER_DESC); @@ -2053,9 +2792,17 @@ if (!ov511->fbuf) goto error; + ov511->rawfbuf = vmalloc(OV511_NUMFRAMES * MAX_RAW_DATA_SIZE); + if (!ov511->rawfbuf) { + rvfree(ov511->fbuf, OV511_NUMFRAMES * MAX_DATA_SIZE); + ov511->fbuf = NULL; + goto error; + } + for (i = 0; i < OV511_NUMFRAMES; i++) { ov511->frame[i].grabstate = FRAME_UNUSED; ov511->frame[i].data = ov511->fbuf + i * MAX_DATA_SIZE; + ov511->frame[i].rawdata = ov511->rawfbuf + i * MAX_RAW_DATA_SIZE; PDEBUG(4, "frame[%d] @ %p", i, ov511->frame[i].data); ov511->sbuf[i].data = kmalloc(FRAMES_PER_DESC * @@ -2064,7 +2811,11 @@ while (--i) { kfree(ov511->sbuf[i].data); ov511->sbuf[i].data = NULL; + ov511->frame[i].data = NULL; + ov511->frame[i].rawdata = NULL; } + vfree(ov511->rawfbuf); + ov511->rawfbuf = NULL; rvfree(ov511->fbuf, OV511_NUMFRAMES * MAX_DATA_SIZE); ov511->fbuf = NULL; goto error; @@ -2098,11 +2849,18 @@ ov511->fbuf = NULL; } + if (ov511->rawfbuf) { + vfree(ov511->rawfbuf); + ov511->rawfbuf = NULL; + } + for (i = 0; i < OV511_NUMFRAMES; i++) { if (ov511->sbuf[i].data) { kfree(ov511->sbuf[i].data); ov511->sbuf[i].data = NULL; } + ov511->frame[i].data = NULL; + ov511->frame[i].rawdata = NULL; } PDEBUG(4, "buffer memory deallocated"); @@ -2182,6 +2940,9 @@ } ov511->user++; + + if (ov511->led_policy == LED_AUTO) + ov511_led_control(ov511, 1); out: up(&ov511->lock); @@ -2203,6 +2964,9 @@ ov511->user--; ov511_stop_isoc(ov511); + if (ov511->led_policy == LED_AUTO) + ov511_led_control(ov511, 0); + if (ov511->dev) ov511_dealloc(ov511, 0); @@ -2274,6 +3038,7 @@ v.flags = 0; v.tuners = 0; v.type = VIDEO_TYPE_CAMERA; + v.norm = 0; strcpy(v.name, "Camera"); if (copy_to_user(arg, &v, sizeof(v))) @@ -2400,7 +3165,7 @@ interruptible_sleep_on(&ov511->wq); if (signal_pending(current)) return -EINTR; - result = ov511_mode_init_regs(ov511, vw.width, vw.height, + result = mode_init_regs(ov511, vw.width, vw.height, ov511->frame[0].format, ov511->sub_flag); if (result < 0) return result; @@ -2486,7 +3251,7 @@ before changing modes */ interruptible_sleep_on(&ov511->wq); if (signal_pending(current)) return -EINTR; - ret = ov511_mode_init_regs(ov511, vm.width, vm.height, + ret = mode_init_regs(ov511, vm.width, vm.height, vm.format, ov511->sub_flag); #if 0 if (ret < 0) @@ -2615,7 +3380,7 @@ return 0; } -static long ov511_read(struct video_device *dev, char *buf, unsigned long count, int noblock) +static inline long ov511_read(struct video_device *dev, char *buf, unsigned long count, int noblock) { struct usb_ov511 *ov511 = (struct usb_ov511 *)dev; int i; @@ -2775,11 +3540,14 @@ /**************************************************************************** * - * OV511/OV7610 configuration + * OV511 and sensor configuration * ***************************************************************************/ -static int ov76xx_configure(struct usb_ov511 *ov511) +/* This initializes the OV7610, OV7620, or OV7620AE sensor. The OV7620AE uses + * the same register settings as the OV7610, since they are very similar. + */ +static int ov7xx0_configure(struct usb_ov511 *ov511) { struct usb_device *dev = ov511->dev; int i, success; @@ -2855,40 +3623,12 @@ PDEBUG (4, "starting configuration"); /* This looks redundant, but is necessary for WebCam 3 */ - if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE, - OV7610_I2C_WRITE_ID) < 0) - return -1; - - if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ, - OV7610_I2C_READ_ID) < 0) - return -1; - - if (ov511_reset(dev, OV511_RESET_NOREGS) < 0) + if (ov51x_set_slave_ids(ov511, OV7xx0_I2C_WRITE_ID, + OV7xx0_I2C_READ_ID) < 0) return -1; - /* Reset the 76xx */ - if (ov511_i2c_write(dev, 0x12, 0x80) < 0) return -1; - - /* Wait for it to initialize */ - schedule_timeout (1 + 150 * HZ / 1000); - - for (i = 0, success = 0; i < i2c_detect_tries && !success; i++) { - if ((ov511_i2c_read(dev, OV7610_REG_ID_HIGH) == 0x7F) && - (ov511_i2c_read(dev, OV7610_REG_ID_LOW) == 0xA2)) { - success = 1; - continue; - } - - /* Reset the 76xx */ - if (ov511_i2c_write(dev, 0x12, 0x80) < 0) return -1; - /* Wait for it to initialize */ - schedule_timeout (1 + 150 * HZ / 1000); - /* Dummy read to sync I2C */ - if (ov511_i2c_read(dev, 0x00) < 0) return -1; - } - - if (success) { - PDEBUG(1, "I2C synced in %d attempt(s) (method 1)", i); + if (ov51x_init_ov_sensor(ov511) >= 0) { + PDEBUG(1, "OV7xx0 sensor initalized (method 1)"); } else { /* Reset the 76xx */ if (ov511_i2c_write(dev, 0x12, 0x80) < 0) return -1; @@ -2908,13 +3648,16 @@ } } - if ((i == i2c_detect_tries) && (success == 0)) { +// Was (i == i2c_detect_tries) previously. This obviously used to always report +// success. Whether anyone actually depended on that bug is unknown + if ((i >= i2c_detect_tries) && (success == 0)) { err("Failed to read sensor ID. You might not have an OV7610/20,"); - err("or it may be not responding. Report this to"); - err("mwm@i.am"); - return -1; + err("or it may be not responding. Report this to mwm@i.am"); + err("This is only a warning. You can attempt to use your camera anyway"); +// Only issue a warning for now +// return -1; } else { - PDEBUG(1, "I2C synced in %d attempt(s) (method 2)", i+1); + PDEBUG(1, "OV7xx0 initialized in %d attempt(s) (method 2)", i+1); } } @@ -2981,74 +3724,137 @@ return 0; } +/* This initializes the OV6620, OV6630, OV6630AE, or OV6630AF sensor. */ static int ov6xx0_configure(struct usb_ov511 *ov511) { struct usb_device *dev = ov511->dev; - int i, success, rc; + int rc; static struct ov511_regvals aRegvalsNorm6x20[] = { - { OV511_I2C_BUS, 0x12, 0x80 }, /* reset */ + { OV511_I2C_BUS, 0x12, 0x80 }, /* reset */ { OV511_I2C_BUS, 0x11, 0x01 }, - { OV511_I2C_BUS, 0x03, 0xd0 }, - { OV511_I2C_BUS, 0x05, 0x7f }, + { OV511_I2C_BUS, 0x03, 0x60 }, + { OV511_I2C_BUS, 0x05, 0x7f }, /* For when autoadjust is off */ { OV511_I2C_BUS, 0x07, 0xa8 }, + /* The ratio of 0x0c and 0x0d controls the white point */ { OV511_I2C_BUS, 0x0c, 0x24 }, { OV511_I2C_BUS, 0x0d, 0x24 }, - { OV511_I2C_BUS, 0x10, 0xff }, /* ? */ + { OV511_I2C_BUS, 0x12, 0x28 }, /* Enable AGC */ { OV511_I2C_BUS, 0x14, 0x04 }, - { OV511_I2C_BUS, 0x16, 0x06 }, /* ? */ - { OV511_I2C_BUS, 0x19, 0x04 }, - { OV511_I2C_BUS, 0x1a, 0x93 }, - { OV511_I2C_BUS, 0x20, 0x28 }, - { OV511_I2C_BUS, 0x27, 0xa2 }, - { OV511_I2C_BUS, 0x28, 0x24 }, - { OV511_I2C_BUS, 0x2a, 0x04 }, /* 84? */ - { OV511_I2C_BUS, 0x2b, 0xac }, /* a8? */ - { OV511_I2C_BUS, 0x2d, 0x95 }, - { OV511_I2C_BUS, 0x33, 0x28 }, - { OV511_I2C_BUS, 0x34, 0xc7 }, + /* 0x16: 0x06 helps frame stability with moving objects */ + { OV511_I2C_BUS, 0x16, 0x06 }, +// { OV511_I2C_BUS, 0x20, 0x30 }, /* Aperture correction enable */ + { OV511_I2C_BUS, 0x26, 0xb2 }, /* BLC enable */ + { OV511_I2C_BUS, 0x28, 0x05 }, /* Selects RGB format if RGB on */ + { OV511_I2C_BUS, 0x2a, 0x04 }, /* Disable framerate adjust */ +// { OV511_I2C_BUS, 0x2b, 0xac }, /* Framerate; Set 2a[7] first */ + { OV511_I2C_BUS, 0x34, 0xd2 }, /* Max A/D range */ { OV511_I2C_BUS, 0x38, 0x8b }, - { OV511_I2C_BUS, 0x3c, 0x5c }, + { OV511_I2C_BUS, 0x39, 0x40 }, + + { OV511_I2C_BUS, 0x3c, 0x39 }, /* Enable AEC mode changing */ + { OV511_I2C_BUS, 0x3c, 0x3c }, /* Change AEC mode */ + { OV511_I2C_BUS, 0x3c, 0x24 }, /* Disable AEC mode changing */ + { OV511_I2C_BUS, 0x3d, 0x80 }, - { OV511_I2C_BUS, 0x3f, 0x00 }, - { OV511_I2C_BUS, 0x4a, 0x80 }, /* undocumented */ - { OV511_I2C_BUS, 0x4b, 0x80 }, /* undocumented */ - { OV511_I2C_BUS, 0x4d, 0xd2 }, + /* These next two registers (0x4a, 0x4b) are undocumented. They + * control the color balance */ + { OV511_I2C_BUS, 0x4a, 0x80 }, + { OV511_I2C_BUS, 0x4b, 0x80 }, + { OV511_I2C_BUS, 0x4d, 0xd2 }, /* This reduces noise a bit */ { OV511_I2C_BUS, 0x4e, 0xc1 }, { OV511_I2C_BUS, 0x4f, 0x04 }, +// Do 50-53 have any effect? +// Toggle 0x12[2] off and on here? + { OV511_DONE_BUS, 0x0, 0x00 }, + }; + + /* This chip is undocumented so many of these are guesses. OK=verified, + * A=Added since 6620, U=unknown function (not a 6620 reg) */ + static struct ov511_regvals aRegvalsNorm6x30[] = { + /*OK*/ { OV511_I2C_BUS, 0x12, 0x80 }, /* reset */ + /*00?*/ { OV511_I2C_BUS, 0x11, 0x01 }, + /*OK*/ { OV511_I2C_BUS, 0x03, 0x60 }, + /*0A?*/ { OV511_I2C_BUS, 0x05, 0x7f }, /* For when autoadjust is off */ +// { OV511_I2C_BUS, 0x07, 0xa8 }, /* Does 6630 have this? */ + /* The ratio of 0x0c and 0x0d controls the white point */ + /*OK*/ { OV511_I2C_BUS, 0x0c, 0x24 }, + /*OK*/ { OV511_I2C_BUS, 0x0d, 0x24 }, + /*A*/ { OV511_I2C_BUS, 0x0e, 0x20 }, + /*24?*/ { OV511_I2C_BUS, 0x12, 0x28 }, /* Enable AGC */ + /*A*/ { OV511_I2C_BUS, 0x13, 0x21 }, + /*04?*/ { OV511_I2C_BUS, 0x14, 0x80 }, + /* 0x16: 0x06 helps frame stability with moving objects */ + /*03?*/ { OV511_I2C_BUS, 0x16, 0x06 }, +// /*OK*/ { OV511_I2C_BUS, 0x20, 0x30 }, /* Aperture correction enable */ + // 21 & 22? The suggested values look wrong. Go with default + /*A*/ { OV511_I2C_BUS, 0x23, 0xc0 }, + /*A*/ { OV511_I2C_BUS, 0x25, 0x9a }, // Check this against default + /*OK*/ { OV511_I2C_BUS, 0x26, 0xb2 }, /* BLC enable */ + /*04?*/ { OV511_I2C_BUS, 0x28, 0x05 }, /* Selects RGB format if RGB on */ + /*OK*/ { OV511_I2C_BUS, 0x2a, 0x04 }, /* Disable framerate adjust */ +// /*OK*/ { OV511_I2C_BUS, 0x2b, 0xac }, /* Framerate; Set 2a[7] first */ + /*U*/ { OV511_I2C_BUS, 0x2c, 0xa0 }, + /*A*/ { OV511_I2C_BUS, 0x33, 0x26 }, // Reserved bits on 6620 + // Reg 0x34: More 6620 reserved junk + /*d2?*/ { OV511_I2C_BUS, 0x34, 0x03 }, /* Max A/D range */ + /*U*/ { OV511_I2C_BUS, 0x36, 0x8f }, // May not be necessary + /*U*/ { OV511_I2C_BUS, 0x37, 0x80 }, // May not be necessary + /*8b?*/ { OV511_I2C_BUS, 0x38, 0x83 }, + /*40?*/ { OV511_I2C_BUS, 0x39, 0xc0 }, // 6630 adds bit 7 + //Check these again + { OV511_I2C_BUS, 0x3c, 0x39 }, /* Enable AEC mode changing */ + { OV511_I2C_BUS, 0x3c, 0x3c }, /* Change AEC mode */ + { OV511_I2C_BUS, 0x3c, 0x24 }, /* Disable AEC mode changing */ + + /*OK*/ { OV511_I2C_BUS, 0x3d, 0x80 }, + /*A*/ { OV511_I2C_BUS, 0x3f, 0x0e }, // Reserved bits on 6620. Default may be safer. + + /*U*/ { OV511_I2C_BUS, 0x40, 0x00 }, + /*U*/ { OV511_I2C_BUS, 0x41, 0x00 }, + /*U*/ { OV511_I2C_BUS, 0x42, 0x80 }, + /*U*/ { OV511_I2C_BUS, 0x43, 0x3f }, + /*U*/ { OV511_I2C_BUS, 0x44, 0x80 }, + /*U*/ { OV511_I2C_BUS, 0x45, 0x20 }, + /*U*/ { OV511_I2C_BUS, 0x46, 0x20 }, + /*U*/ { OV511_I2C_BUS, 0x47, 0x80 }, + /*U*/ { OV511_I2C_BUS, 0x48, 0x7f }, + /*U*/ { OV511_I2C_BUS, 0x49, 0x00 }, + + /* These next two registers (0x4a, 0x4b) are undocumented. They + * control the color balance */ + /*OK?*/ { OV511_I2C_BUS, 0x4a, 0x80 }, // Check these + /*OK?*/ { OV511_I2C_BUS, 0x4b, 0x80 }, + /*U*/ { OV511_I2C_BUS, 0x4c, 0xd0 }, // These high registers are always suspect + /*d2?*/ { OV511_I2C_BUS, 0x4d, 0x10 }, /* This reduces noise a bit */ + /*c1?*/ { OV511_I2C_BUS, 0x4e, 0x40 }, + /*04?*/ { OV511_I2C_BUS, 0x4f, 0x07 }, + + /*U*/ { OV511_I2C_BUS, 0x50, 0xff }, // These are all undocumented (by 6620 docs) + /*U*/ { OV511_I2C_BUS, 0x54, 0x23 }, + /*U*/ { OV511_I2C_BUS, 0x55, 0xff }, + /*U*/ { OV511_I2C_BUS, 0x56, 0x12 }, + /*U*/ { OV511_I2C_BUS, 0x57, 0x81 }, + /*U*/ { OV511_I2C_BUS, 0x58, 0x75 }, + /*U*/ { OV511_I2C_BUS, 0x59, 0x01 }, + /*U*/ { OV511_I2C_BUS, 0x5a, 0x2c }, + /*U*/ { OV511_I2C_BUS, 0x5b, 0x0f }, + /*U*/ { OV511_I2C_BUS, 0x5c, 0x10 }, + +// Set 0x3d to 0x80 here? +// Set 0x27 to 0xa6 here? +// Toggle 0x12[2] off and on here? { OV511_DONE_BUS, 0x0, 0x00 }, }; PDEBUG (4, "starting sensor configuration"); - /* Reset the 6xx0 */ - if (ov511_i2c_write(dev, 0x12, 0x80) < 0) return -1; - - /* Wait for it to initialize */ - schedule_timeout (1 + 150 * HZ / 1000); - - for (i = 0, success = 0; i < i2c_detect_tries && !success; i++) { - if ((ov511_i2c_read(dev, OV7610_REG_ID_HIGH) == 0x7F) && - (ov511_i2c_read(dev, OV7610_REG_ID_LOW) == 0xA2)) { - success = 1; - continue; - } - - /* Reset the 6xx0 */ - if (ov511_i2c_write(dev, 0x12, 0x80) < 0) return -1; - /* Wait for it to initialize */ - schedule_timeout (1 + 150 * HZ / 1000); - /* Dummy read to sync I2C */ - if (ov511_i2c_read(dev, 0x00) < 0) return -1; - } - - if (success) { - PDEBUG(1, "I2C synced in %d attempt(s)", i); - } else { + if (ov51x_init_ov_sensor(ov511) < 0) { err("Failed to read sensor ID. You might not have an OV6xx0,"); - err("or it may be not responding. Report this to"); - err("mwm@i.am"); + err("or it may be not responding. Report this to mwm@i.am"); return -1; + } else { + PDEBUG(1, "OV6xx0 sensor detected"); } /* Detect sensor if user didn't use override param */ @@ -3058,10 +3864,19 @@ if (rc < 0) { err("Error detecting sensor type"); return -1; - } else { - info("Sensor is an OV6xx0 (version %d)", rc & 3); + } else if((rc & 3) == 0) { + info("Sensor is an OV6630"); + ov511->sensor = SEN_OV6630; + } else if((rc & 3) == 1) { + info("Sensor is an OV6620"); ov511->sensor = SEN_OV6620; - } + } else if((rc & 3) == 2) { + info("Sensor is an OV6630AE"); + ov511->sensor = SEN_OV6630; + } else if((rc & 3) == 3) { + info("Sensor is an OV6630AF"); + ov511->sensor = SEN_OV6630; + } } else { /* sensor != 0; user overrode detection */ ov511->sensor = sensor; info("Sensor set to type %d", ov511->sensor); @@ -3071,10 +3886,16 @@ ov511->maxwidth = 352; ov511->maxheight = 288; - PDEBUG(4, "Writing 6x20 registers"); - if (ov511_write_regvals(dev, aRegvalsNorm6x20)) - return -1; - + if (ov511->sensor == SEN_OV6620) { + PDEBUG(4, "Writing 6x20 registers"); + if (ov511_write_regvals(dev, aRegvalsNorm6x20)) + return -1; + } else { + PDEBUG(4, "Writing 6x30 registers"); + if (ov511_write_regvals(dev, aRegvalsNorm6x30)) + return -1; + } + if (aperture < 0) { /* go with the default */ if (ov511_i2c_write(dev, 0x26, 0xa2) < 0) return -1; } else if (aperture <= 0xf) { /* user overrode default */ @@ -3086,26 +3907,24 @@ } if (autoadjust) { - if (ov511_i2c_write(dev, 0x13, 0x01) < 0) return -1; - if (ov511_i2c_write(dev, 0x2d, - ov511->sensor==SEN_OV7620?0x91:0x93) < 0) return -1; + if (ov511_i2c_write_mask(dev, 0x13, 0x01, 0x01) < 0) return -1; + if (ov511_i2c_write(dev, 0x2d, 0x99) < 0) return -1; } else { - if (ov511_i2c_write(dev, 0x13, 0x00) < 0) return -1; - if (ov511_i2c_write(dev, 0x2d, - ov511->sensor==SEN_OV7620?0x81:0x83) < 0) return -1; - ov511_i2c_write(dev, 0x28, ov511_i2c_read(dev, 0x28) | 8); + if (ov511_i2c_write_mask(dev, 0x13, 0x00, 0x01) < 0) return -1; + if (ov511_i2c_write(dev, 0x2d, 0x89) < 0) return -1; + if (ov511_i2c_write_mask(dev, 0x28, 0x08, 0x08) < 0) return -1; } return 0; } - +/* This initializes the OV511/OV511+ and the sensor */ static int ov511_configure(struct usb_ov511 *ov511) { struct usb_device *dev = ov511->dev; int i; - static struct ov511_regvals aRegvalsInit[] = { + static struct ov511_regvals aRegvalsInit511[] = { { OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x7f }, { OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0x01 }, { OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x7f }, @@ -3121,19 +3940,50 @@ { OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x02 }, { OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x00 }, { OV511_REG_BUS, OV511_REG_FIFO_BITMASK, 0x1f }, - { OV511_REG_BUS, OV511_OMNICE_PREDICTION_HORIZ_Y, 0x08 }, + { OV511_REG_BUS, OV511_OMNICE_PREDICTION_HORIZ_Y, 0x1f }, { OV511_REG_BUS, OV511_OMNICE_PREDICTION_HORIZ_UV, 0x01 }, { OV511_REG_BUS, OV511_OMNICE_PREDICTION_VERT_Y, 0x08 }, { OV511_REG_BUS, OV511_OMNICE_PREDICTION_VERT_UV, 0x01 }, - { OV511_REG_BUS, OV511_OMNICE_QUANTIZATION_HORIZ_Y, 0x01 }, + { OV511_REG_BUS, OV511_OMNICE_QUANTIZATION_HORIZ_Y, 0xff }, { OV511_REG_BUS, OV511_OMNICE_QUANTIZATION_HORIZ_UV, 0x01 }, - { OV511_REG_BUS, OV511_OMNICE_QUANTIZATION_VERT_Y, 0x01 }, + { OV511_REG_BUS, OV511_OMNICE_QUANTIZATION_VERT_Y, 0xff }, { OV511_REG_BUS, OV511_OMNICE_QUANTIZATION_VERT_UV, 0x01 }, - { OV511_REG_BUS, OV511_OMNICE_ENABLE, 0x06 }, - { OV511_REG_BUS, OV511_OMNICE_LUT_ENABLE, 0x03 }, + { OV511_REG_BUS, OV511_OMNICE_ENABLE, 0x00 }, + { OV511_REG_BUS, OV511_OMNICE_LUT_ENABLE, 0x00 }, { OV511_DONE_BUS, 0x0, 0x00 }, }; + PDEBUG(4, ""); + + ov511->customid = ov511_reg_read(dev, OV511_REG_SYSTEM_CUSTOM_ID); + if (ov511->customid < 0) { + err("Unable to read camera bridge registers"); + return -EIO; + } + + ov511->desc = -1; + PDEBUG (4, "CustomID = %d", ov511->customid); + for (i = 0; clist[i].id >= 0; i++) { + if (ov511->customid == clist[i].id) { + info("camera: %s", clist[i].description); + ov511->desc = i; + break; + } + } + + /* Lifeview USB Life TV not supported */ + if (clist[i].id == 38) { + err("This device is not supported yet."); + return -EIO; + } + + if (clist[i].id == -1) { + err("Camera type (%d) not recognized", ov511->customid); + err("Please notify mwm@i.am of the name, manufacturer, model,"); + err("and camera type number of your camera. Also include the"); + err("complete output of the detection process."); + } + memcpy(&ov511->vdev, &ov511_template, sizeof(ov511_template)); for (i = 0; i < OV511_NUMFRAMES; i++) @@ -3146,79 +3996,236 @@ return -EBUSY; } - if (ov511_write_regvals(dev, aRegvalsInit)) goto error; + if (ov511_write_regvals(dev, aRegvalsInit511)) goto error; + + if (ov511->led_policy == LED_OFF || ov511->led_policy == LED_AUTO) + ov511_led_control(ov511, 0); + if (ov511_write_regvals(dev, aRegvalsNorm511)) goto error; + if (compress) + ov511_upload_quan_tables(dev); ov511_set_packet_size(ov511, 0); ov511->snap_enabled = snapshot; - /* Test for 76xx */ - if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE, - OV7610_I2C_WRITE_ID) < 0) - goto error; + /* Test for 7xx0 */ + if (ov51x_set_slave_ids(ov511, OV7xx0_I2C_WRITE_ID, + OV7xx0_I2C_READ_ID) < 0) + return -1; - if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ, - OV7610_I2C_READ_ID) < 0) - goto error; + if (ov511_i2c_write(dev, 0x12, 0x80) < 0) { + /* Test for 6xx0 */ + if (ov51x_set_slave_ids(ov511, OV6xx0_I2C_WRITE_ID, + OV6xx0_I2C_READ_ID) < 0) + return -1; + + if (ov511_i2c_write(dev, 0x12, 0x80) < 0) { + /* Test for 8xx0 */ + if (ov51x_set_slave_ids(ov511, OV8xx0_I2C_WRITE_ID, + OV8xx0_I2C_READ_ID)) + return -1; + + if (ov511_i2c_write(dev, 0x12, 0x80) < 0) { + err("Can't determine sensor slave IDs"); + goto error; + } else { + err("Detected unsupported OV8xx0 sensor"); + goto error; + } + } else { + if(ov6xx0_configure(ov511) < 0) { + err("failed to configure OV6xx0"); + goto error; + } + } + } else { + if(ov7xx0_configure(ov511) < 0) { + err("failed to configure OV7xx0"); + goto error; + } + } - if (ov511_reset(dev, OV511_RESET_NOREGS) < 0) + if (ov51x_set_default_params(ov511) < 0) goto error; - if (ov511_i2c_write(dev, 0x12, 0x80) < 0) { - /* Test for 6xx0 */ - if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE, - OV6xx0_I2C_WRITE_ID) < 0) - goto error; + return 0; - if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ, - OV6xx0_I2C_READ_ID) < 0) - goto error; +error: + err("OV511 Config failed"); - if (ov511_reset(dev, OV511_RESET_NOREGS) < 0) - goto error; + #if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS) + /* Safe to call even if entry doesn't exist */ + destroy_proc_ov511_cam((struct usb_ov511 *)dev); + #endif - if (ov511_i2c_write(dev, 0x12, 0x80) < 0) { - err("Can't determine sensor slave IDs"); - goto error; - } - - if(ov6xx0_configure(ov511) < 0) { - err("failed to configure OV6xx0"); - goto error; + video_unregister_device(&ov511->vdev); + usb_driver_release_interface(&ov511_driver, + &dev->actconfig->interface[ov511->iface]); + + return -EBUSY; +} + +/* This initializes the OV518/OV518+ and the sensor */ +static int ov518_configure(struct usb_ov511 *ov511) +{ + struct usb_device *dev = ov511->dev; + int i; + +#if 0 + /* Conservative sequence, based on OV511 */ + static struct ov511_regvals aRegvalsInit518[] = { + { OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x7e }, + { OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0x01 }, + { OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x7e }, + { OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0x01 }, + { OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x3e }, + { OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0x01 }, + { OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x3c }, + { OV511_DONE_BUS, 0x0, 0x00}, + }; +#else + /* Experimental sequence, based on OV518 Windows settings */ + static struct ov511_regvals aRegvalsInit518[] = { + { OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x40 }, + { OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0xe1 }, + { OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x3e }, + { OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0xe1 }, + { OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x00 }, + { OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0xe1 }, + { OV511_REG_BUS, 0x5d, 0x03 }, + { OV511_DONE_BUS, 0x0, 0x00}, + }; +#endif + +#if 0 + /* Old sequence - Note: the mnemonics for these registers may not be + * right with the OV518, but their values are right. */ + static struct ov511_regvals aRegvalsNorm518[] = { + { OV511_REG_BUS, OV511_REG_DRAM_ENABLE_FLOW_CONTROL, 0x09 }, + { OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x02 }, + { OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x00 }, + { OV511_REG_BUS, OV511_REG_FIFO_BITMASK, 0x0f }, + { OV511_REG_BUS, 0x24, 0x9f }, + { OV511_REG_BUS, 0x25, 0x90 }, + { OV511_REG_BUS, OV511_REG_SYSTEM_CLOCK_DIVISOR, 0x04 }, /* ? */ + { OV511_REG_BUS, OV511_OMNICE_ENABLE, 0x00 }, + { OV511_REG_BUS, OV511_OMNICE_LUT_ENABLE, 0x00 }, + { OV511_DONE_BUS, 0x0, 0x00 }, + }; +#else + /* New values, based on Windows driver. Since what they do is not + * known yet, this may be incorrect. */ + static struct ov511_regvals aRegvalsNorm518[] = { + { OV511_REG_BUS, 0x52, 0x02 }, + { OV511_REG_BUS, 0x52, 0x01 }, + { OV511_REG_BUS, 0x31, 0x0f }, + { OV511_REG_BUS, 0x5d, 0x03 }, + { OV511_REG_BUS, 0x24, 0x9f }, + { OV511_REG_BUS, 0x25, 0x90 }, + { OV511_REG_BUS, 0x20, 0x08 }, + { OV511_REG_BUS, 0x51, 0x04 }, + { OV511_REG_BUS, 0x71, 0x19 }, + { OV511_DONE_BUS, 0x0, 0x00 }, + }; +#endif + PDEBUG(4, ""); + + /* First 5 bits of custom ID reg are a revision ID on OV518 */ + info("Camera revision %d", 0x1F & ov511_reg_read(dev, OV511_REG_SYSTEM_CUSTOM_ID)); + + memcpy(&ov511->vdev, &ov511_template, sizeof(ov511_template)); + + for (i = 0; i < OV511_NUMFRAMES; i++) + init_waitqueue_head(&ov511->frame[i].wq); + + init_waitqueue_head(&ov511->wq); + + if (video_register_device(&ov511->vdev, VFL_TYPE_GRABBER) < 0) { + err("video_register_device failed"); + return -EBUSY; + } + + if (ov511_write_regvals(dev, aRegvalsInit518)) goto error; + + /* LED is off by default with OV518; have to explicitly turn it on */ + if (ov511->led_policy == LED_OFF || ov511->led_policy == LED_AUTO) + ov511_led_control(ov511, 0); + else + ov511_led_control(ov511, 1); + + if (compress) { + compress = 0; + warn("Compression disabled on OV518+"); + } + + if (ov511_write_regvals(dev, aRegvalsNorm518)) goto error; + /* The next setting may not be necessary */ + if (ov511_reg_write_mask(dev, 0x57,0x00, 0x02) < 0) goto error; + + ov511_set_packet_size(ov511, 0); + + ov511->snap_enabled = snapshot; + + /* Test for 76xx */ + if (ov51x_set_slave_ids(ov511, OV7xx0_I2C_WRITE_ID, + OV7xx0_I2C_READ_ID) < 0) + return -1; + + /* The OV518 must be more aggressive about sensor detection since + * I2C write will never fail if the sensor is not present. We have + * to try to initialize the sensor to detect its presence */ + + if (ov51x_init_ov_sensor(ov511) < 0) { + /* Test for 6xx0 */ + if (ov51x_set_slave_ids(ov511, OV6xx0_I2C_WRITE_ID, + OV6xx0_I2C_READ_ID) < 0) + return -1; + + if (ov51x_init_ov_sensor(ov511) < 0) { + /* Test for 8xx0 */ + if (ov51x_set_slave_ids(ov511, OV6xx0_I2C_WRITE_ID, + OV6xx0_I2C_READ_ID) < 0) + return -1; + + if (ov51x_init_ov_sensor(ov511) < 0) { + err("Can't determine sensor slave IDs"); + goto error; + } else { + err("Detected unsupported OV8xx0 sensor"); + goto error; + } + } else { + if(ov6xx0_configure(ov511) < 0) { + err("failed to configure OV6xx0"); + goto error; + } } } else { - if(ov76xx_configure(ov511) < 0) { - err("failed to configure OV76xx"); + if(ov7xx0_configure(ov511) < 0) { + err("failed to configure OV7xx0"); goto error; } } - - /* Set default sizes in case IOCTL (VIDIOCMCAPTURE) is not used - * (using read() instead). */ - for (i = 0; i < OV511_NUMFRAMES; i++) { - ov511->frame[i].width = ov511->maxwidth; - ov511->frame[i].height = ov511->maxheight; - ov511->frame[i].depth = 24; - ov511->frame[i].bytes_read = 0; - ov511->frame[i].segment = 0; - ov511->frame[i].format = VIDEO_PALETTE_RGB24; - ov511->frame[i].segsize = GET_SEGSIZE(ov511->frame[i].format); - } - /* Initialize to max width/height, RGB24 */ - if (ov511_mode_init_regs(ov511, ov511->maxwidth, ov511->maxheight, - VIDEO_PALETTE_RGB24, 0) < 0) + if (ov51x_set_default_params(ov511) < 0) goto error; return 0; - + error: + err("OV518 Config failed"); + + #if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS) + /* Safe to call even if entry doesn't exist */ + destroy_proc_ov511_cam((struct usb_ov511 *)dev); + #endif + video_unregister_device(&ov511->vdev); usb_driver_release_interface(&ov511_driver, &dev->actconfig->interface[ov511->iface]); - return -EBUSY; + return -EBUSY; } @@ -3234,7 +4241,6 @@ { struct usb_interface_descriptor *interface; struct usb_ov511 *ov511; - int i; PDEBUG(1, "probing for device..."); @@ -3262,6 +4268,7 @@ ov511->dev = dev; ov511->iface = interface->bInterfaceNumber; + ov511->led_policy = led; switch (dev->descriptor.idProduct) { case 0x0511: @@ -3272,6 +4279,10 @@ info("USB OV511+ camera found"); ov511->bridge = BRG_OV511PLUS; break; + case 0x0518: + info("USB OV518+ camera found"); + ov511->bridge = BRG_OV518PLUS; + break; case 0x0002: if (dev->descriptor.idVendor != 0x0813) goto error; @@ -3283,53 +4294,36 @@ goto error; } - ov511->customid = ov511_reg_read(dev, OV511_REG_SYSTEM_CUSTOM_ID); - if (ov511->customid < 0) { - err("Unable to read camera bridge registers"); - goto error; - } - - ov511->desc = -1; - PDEBUG (4, "CustomID = %d", ov511->customid); - for (i = 0; clist[i].id >= 0; i++) { - if (ov511->customid == clist[i].id) { - info("camera: %s", clist[i].description); - ov511->desc = i; - break; - } - } - - /* Lifeview USB Life TV not supported */ - if (clist[i].id == 38) { - err("This device is not supported yet."); - goto error; - } - - if (clist[i].id == -1) { - err("Camera type (%d) not recognized", ov511->customid); - err("Please contact mwm@i.am to request"); - err("support for your camera."); - } - /* Workaround for some applications that want data in RGB - * instead of BGR */ + * instead of BGR. */ if (force_rgb) info("data format set to RGB"); - if (!ov511_configure(ov511)) { - ov511->user = 0; - init_MUTEX(&ov511->lock); /* to 1 == available */ - init_MUTEX(&ov511->buf_lock); - ov511->buf_state = BUF_NOT_ALLOCATED; + if (ov511->bridge == BRG_OV518PLUS) { + if (ov518_configure(ov511) < 0) + goto error; } else { - err("Failed to configure camera"); - goto error; + if (ov511_configure(ov511) < 0) + goto error; } + init_MUTEX(&ov511->lock); /* to 1 == available */ + init_MUTEX(&ov511->buf_lock); + init_MUTEX(&ov511->param_lock); + ov511->buf_state = BUF_NOT_ALLOCATED; + +#ifdef OV511_DEBUG + if (dump_regs) + ov511_dump_regs(dev); +#endif + + PDEBUG(1, "Camera initialization successful"); + MOD_DEC_USE_COUNT; return ov511; error: + err("Camera initialization failed"); if (ov511) { kfree(ov511); ov511 = NULL; diff -Naur linux-2.4.0-orig/drivers/usb/ov511.h linux/drivers/usb/ov511.h --- linux-2.4.0-orig/drivers/usb/ov511.h Tue Jan 30 03:52:30 2001 +++ linux/drivers/usb/ov511.h Tue Jan 30 03:55:32 2001 @@ -52,6 +52,7 @@ /* I2C register numbers */ #define OV511_REG_I2C_CONTROL 0x40 +#define OV518_REG_I2C_CONTROL 0x47 /* OV518+ only */ #define OV511_REG_I2C_SLAVE_ID_WRITE 0x41 #define OV511_REG_I2C_SUB_ADDRESS_3_BYTE 0x42 #define OV511_REG_I2C_SUB_ADDRESS_2_BYTE 0x43 @@ -78,8 +79,16 @@ #define OV511_REG_SYSTEM_CLOCK_DIVISOR 0x51 #define OV511_REG_SYSTEM_SNAPSHOT 0x52 #define OV511_REG_SYSTEM_INIT 0x53 -#define OV511_REG_SYSTEM_PWR_CLK 0x54 /* OV511+ only */ +#define OV511_REG_SYSTEM_PWR_CLK 0x54 /* OV511+/OV518+ only */ #define OV511_REG_SYSTEM_LED_CTL 0x55 /* OV511+ only */ +#define OV518_REG_GPIO_IN 0x55 /* OV518+ only */ +#define OV518_REG_GPIO_OUT 0x56 /* OV518+ only */ +#define OV518_REG_GPIO_CTL 0x57 /* OV518+ only */ +#define OV518_REG_GPIO_PULSE_IN 0x58 /* OV518+ only */ +#define OV518_REG_GPIO_PULSE_CLEAR 0x59 /* OV518+ only */ +#define OV518_REG_GPIO_PULSE_POLARITY 0x5a /* OV518+ only */ +#define OV518_REG_GPIO_PULSE_EN 0x5b /* OV518+ only */ +#define OV518_REG_GPIO_RESET 0x5c /* OV518+ only */ #define OV511_REG_SYSTEM_USER_DEFINED 0x5E #define OV511_REG_SYSTEM_CUSTOM_ID 0x5F @@ -119,6 +128,16 @@ #define OV511PLUS_ALT_SIZE_769 6 #define OV511PLUS_ALT_SIZE_961 7 +/* Alternate numbers for various max packet sizes (OV518+ only) */ +#define OV518PLUS_ALT_SIZE_0 0 +#define OV518PLUS_ALT_SIZE_128 1 +#define OV518PLUS_ALT_SIZE_256 2 +#define OV518PLUS_ALT_SIZE_384 3 +#define OV518PLUS_ALT_SIZE_512 4 +#define OV518PLUS_ALT_SIZE_640 5 +#define OV518PLUS_ALT_SIZE_768 6 +#define OV518PLUS_ALT_SIZE_896 7 + /* OV7610 registers */ #define OV7610_REG_GAIN 0x00 /* gain setting (5:0) */ #define OV7610_REG_BLUE 0x01 /* blue channel balance */ @@ -176,15 +195,17 @@ #define FRAMES_PER_DESC 10 /* FIXME - What should this be? */ #define FRAME_SIZE_PER_DESC 993 /* FIXME - Deprecated */ #define MAX_FRAME_SIZE_PER_DESC 993 /* For statically allocated stuff */ +#define PIXELS_PER_SEG 256 /* Pixels per segment */ #define OV511_ENDPOINT_ADDRESS 1 /* Isoc endpoint number */ -// CAMERA SPECIFIC -// FIXME - these can vary between specific models -#define OV7610_I2C_WRITE_ID 0x42 -#define OV7610_I2C_READ_ID 0x43 +/* CAMERA SPECIFIC */ +#define OV7xx0_I2C_WRITE_ID 0x42 +#define OV7xx0_I2C_READ_ID 0x43 #define OV6xx0_I2C_WRITE_ID 0xC0 #define OV6xx0_I2C_READ_ID 0xC1 +#define OV8xx0_I2C_WRITE_ID 0xA0 +#define OV8xx0_I2C_READ_ID 0xA1 #define OV511_I2C_CLOCK_PRESCALER 0x03 @@ -196,8 +217,10 @@ /* Bridge types */ enum { + BRG_UNKNOWN, BRG_OV511, BRG_OV511PLUS, + BRG_OV518PLUS, }; /* Sensor types */ @@ -207,6 +230,9 @@ SEN_OV7620, SEN_OV7620AE, SEN_OV6620, + SEN_OV6630, + SEN_OV8600, + SEN_KS0127, }; enum { @@ -222,6 +248,13 @@ BUF_PEND_DEALLOC, /* ov511->buf_timer is set */ }; +/* LED options */ +enum { + LED_OFF, + LED_ON, + LED_AUTO, +}; + struct usb_device; struct ov511_sbuf { @@ -249,6 +282,7 @@ struct ov511_frame { char *data; /* Frame buffer */ + char *rawdata; /* Raw camera data buffer */ int depth; /* Bytes per pixel */ int width; /* Width application is expecting */ @@ -259,6 +293,7 @@ int sub_flag; /* Sub-capture mode for this frame? */ unsigned int format; /* Format for this frame */ + int compressed; /* Is frame compressed? */ int segsize; /* How big is each segment from the camera? */ volatile int grabstate; /* State of grabbing */ @@ -267,9 +302,11 @@ int curline; /* Line of frame we're working on */ int curpix; int segment; /* Segment from the incoming data */ + int bytes_recvd; /* Number of image bytes received from camera */ long scanlength; /* uncompressed, raw data length of frame */ long bytes_read; /* amount of scanlength that has been read from *data */ + int output_offset; /* Current offset where we are dumping raw data */ wait_queue_head_t wq; /* Processes waiting */ @@ -299,6 +336,8 @@ int hue; int whiteness; + int led_policy; /* LED: off|on|auto; OV511+ only */ + struct semaphore lock; int user; /* user count for exclusive use */ @@ -308,6 +347,7 @@ int compress; /* Should the next frame be compressed? */ char *fbuf; /* Videodev buffer area */ + char *rawfbuf; /* Raw camera data buffer area */ int sub_flag; /* Pix Array subcapture on flag */ int subx; /* Pix Array subcapture x offset */ @@ -354,7 +394,7 @@ char *name; }; -struct mode_list { +struct mode_list_511 { int width; int height; int color; /* 0=grayscale, 1=color */ @@ -366,6 +406,92 @@ u8 common_A; u8 common_L; }; + +struct mode_list_518 { + int width; + int height; + u8 reg28; + u8 reg29; + u8 reg2a; + u8 reg2c; + u8 reg2e; + u8 reg24; + u8 reg25; +}; + +/* Compression stuff */ + +#define QUANTABLESIZE 64 +#define IDCTTABLESIZE 256 +#define ZIGZAGTABLESIZE 64 + +#define YQUANTABLE { \ + 0, 1, 1, 2, 2, 3, 3, 4, \ + 1, 1, 1, 2, 2, 3, 4, 4, \ + 1, 1, 2, 2, 3, 4, 4, 4, \ + 2, 2, 2, 3, 4, 4, 4, 4, \ + 2, 2, 3, 4, 4, 5, 5, 5, \ + 3, 3, 4, 4, 5, 5, 5, 5, \ + 3, 4, 4, 4, 5, 5, 5, 5, \ + 4, 4, 4, 4, 5, 5, 5, 5 \ +} + +#define UVQUANTABLE { \ + 0, 2, 2, 3, 4, 4, 4, 4, \ + 2, 2, 2, 4, 4, 4, 4, 4, \ + 2, 2, 3, 4, 4, 4, 4, 4, \ + 3, 4, 4, 4, 4, 4, 4, 4, \ + 4, 4, 4, 4, 4, 4, 4, 4, \ + 4, 4, 4, 4, 4, 4, 4, 4, \ + 4, 4, 4, 4, 4, 4, 4, 4, \ + 4, 4, 4, 4, 4, 4, 4, 4 \ +} + +#define IDCTTABLE { \ + 11584, 11584, 11584, 11584, 16708, 16708, 16708, 16708, \ + 17696, 17696, 17696, 17696, 13640, 13640, 13640, 13640, \ + 11584, 11584, 11584, 11584, 9360, 9360, 9360, 9360, \ + 6274, 6274, 6274, 6274, 3204, 3204, 3204, 3204, \ + 11584, 11584, 11584, 11584, 13640, 13640, 13640, 13640, \ + 6274, 6274, 6274, 6274, -3204, -3204, -3204, -3204, \ + -11584, -11584, -11584, -11584, -16708, -16708, -16708, -16708, \ + -17696, -17696, -17696, -17696, -9360, -9360, -9360, -9360, \ + 11584, 11584, 11584, 11584, 9360, 9360, 9360, 9360, \ + -6274, -6274, -6274, -6274, -16708, -16708, -16708, -16708, \ + -11584, -11584, -11584, -11584, 3204, 3204, 3204, 3204, \ + 17696, 17696, 17696, 17696, 13640, 13640, 13640, 13640, \ + 11584, 11584, 11584, 11584, 3204, 3204, 3204, 3204, \ + -17696, -17696, -17696, -17696, -9360, -9360, -9360, -9360, \ + 11584, 11584, 11584, 11584, 13640, 13640, 13640, 13640, \ + -6274, -6274, -6274, -6274, -16708, -16708, -16708, -16708, \ + 11584, 11584, 11584, 11584, -3204, -3204, -3204, -3204, \ + -17696, -17696, -17696, -17696, 9360, 9360, 9360, 9360, \ + 11584, 11584, 11584, 11584, -13640, -13640, -13640, -13640, \ + -6274, -6274, -6274, -6274, 16708, 16708, 16708, 16708, \ + 11584, 11584, 11584, 11584, -9360, -9360, -9360, -9360, \ + -6274, -6274, -6274, -6274, 16708, 16708, 16708, 16708, \ + -11584, -11584, -11584, -11584, -3204, -3204, -3204, -3204, \ + 17696, 17696, 17696, 17696, -13640, -13640, -13640, -13640, \ + 11584, 11584, 11584, 11584, -13640, -13640, -13640, -13640, \ + 6274, 6274, 6274, 6274, 3204, 3204, 3204, 3204, \ + -11584, -11584, -11584, -11584, 16708, 16708, 16708, 16708, \ + -17696, -17696, -17696, -17696, 9360, 9360, 9360, 9360, \ + 11584, 11584, 11584, 11584, -16708, -16708, -16708, -16708, \ + 17696, 17696, 17696, 17696, -13640, -13640, -13640, -13640, \ + 11584, 11584, 11584, 11584, -9360, -9360, -9360, -9360, \ + 6274, 6274, 6274, 6274, -3204, -3204, -3204, -3204 \ +} + +#define ZIGZAGTABLE { \ + 0, 2, 16, 32, 18, 4, 6, 20, \ + 34, 48, 64, 50, 36, 22, 8, 10, \ + 24, 38, 52, 66, 80, 96, 82, 68, \ + 54, 40, 26, 12, 14, 28, 42, 56, \ + 70, 84, 98, 112, 114, 100, 86, 72, \ + 58, 44, 30, 46, 60, 74, 88, 102, \ + 116, 118, 104, 90, 76, 62, 78, 92, \ + 106, 120, 122, 108, 94, 110, 124, 126 \ +} #endif