diff -Naur linux-2.4.3-orig/Documentation/usb/ov511.txt linux/Documentation/usb/ov511.txt --- linux-2.4.3-orig/Documentation/usb/ov511.txt Fri Mar 2 03:22:17 2001 +++ linux/Documentation/usb/ov511.txt Mon Apr 9 00:45:35 2001 @@ -8,11 +8,10 @@ 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, -384x288, 352x288, and 320x240. +API. Most V4L apps are compatible with it. The following resolutions are +supported: 640x480, 448x336, 384x288, 352x288, and 320x240. If you need more information, please visit the OV511 homepage at the above URL. @@ -29,14 +28,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 +59,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 +74,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 +94,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,21 +103,30 @@ 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, - including xawtv and vidcat. NOTE: See the section "TODO" for more info. + DESC: Set to 1 to enable snapshot mode. read()/VIDIOCSYNC will block until + the snapshot button is pressed. Note: enabling this mode disables + /proc/video/ov511//button NAME: sensor - TYPE: integer ([0, 1, 3]) - DEFAULT: [varies] + TYPE: integer (0 - 7) + DEFAULT: Initially zero, varies after detection DESC: If you know that your camera sensor is not detected correctly, set this parameter. This is a global option for all attached OV511 cameras. You will probably never need to set this, but if you do, valid values are: - 0 for OV7620 - 1 for OV7620AE - 3 for OV7610 + 0 for autodetect + 1 for OV7610 + 2 for OV7620 + 3 for OV7620AE + 4 for OV6620 + 5 for OV6630 + 6 for OV8600 (unsupported) + 7 for KS0127 (unsupported) + + Note that you cannot set an OV6xxx series sensor to detect as an OV7xxx + series one or vice versa, since they use different communication + addresses. NAME: i2c_detect_tries TYPE: integer (don't set it insanely high!) @@ -144,7 +145,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 +171,111 @@ 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: compress + TYPE: integer (Boolean) + DEFAULT: 0 + DESC: Set this to 1 to turn on the camera's compression engine. This can + potentially increase the frame rate at the expense of quality, if you + have a fast CPU. This does not work reliably yet (only stable with very + dark images.) + + NAME: testpat + TYPE: integer (Boolean) + DEFAULT: 0 + DESC: This configures the camera's sensor to transmit a colored test-pattern + instead of an image. This does not work correctly yet. + 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_bridge + TYPE: integer (Boolean) + DEFAULT: 0 + DESC: Dumps the bridge (OV511[+] or OV518[+]) register values to the system + log. Only useful for serious debugging/development purposes. + + NAME: dump_sensor + TYPE: integer (Boolean) + DEFAULT: 0 + DESC: Dumps the sensor register values to the system log. Only useful for + serious debugging/development purposes. + + NAME: printph + TYPE: integer (Boolean) + DEFAULT: 0 + DESC: Setting this to 1 will dump the first 12 bytes of each isoc frame. This + is only useful if you are trying to debug problems with the isoc data + stream (i.e.: camera initializes, but vidcat hangs until Ctrl-C). Be + warned that this dumps a large number of messages to your kernel log. + + NAME: phy, phuv, pvy, pvuv, qhy, qhuv, qvy, qvuv + TYPE: integer (0-63 for phy and phuv, 0-255 for rest) + DEFAULT: OV511 default values + DESC: These are registers 70h - 77h of the OV511, which control the + prediction ranges and quantization thresholds of the compressor, for + the Y and UV channels in the horizontal and vertical directions. See + the OV511 or OV511+ data sheet for more detailed descriptions. These + normally do not need to be changed. + + NAME: lightfreq + TYPE: integer (0, 50, or 60) + DEFAULT: 0 (use sensor default) + DESC: Sets the sensor to match your lighting frequency. This can reduce the + appearance of "banding", i.e. horizontal lines or waves of light and + dark that are often caused by artificial lighting. Valid values are: + 0 - Use default (depends on sensor, most likely 60 Hz) + 50 - For European and Asian 50 Hz power + 60 - For American 60 Hz power + + NAME: bandingfilter + TYPE: integer (Boolean) + DEFAULT: 0 (off) + DESC: Enables the sensorīs banding filter exposure algorithm. This reduces + or stabilizes the "banding" caused by some artificial light sources + (especially fluorescent). You might have to set lightfreq correctly for + this to work right. As an added bonus, this sometimes makes it + possible to capture your monitorīs output. + + NAME: ttpp + TYPE: integer (Boolean) + DEFAULT: 0 (off) + DESC: Task-Time Post-Processing. Set this to 1 to do post-processing + (decompression, format conversion, etc...) at task-time instead of + interrupt-time. This only supports RGB and YUV420/420P, and may + increase frame latency since all processing is deferred until the frame + is totally received. If the benefits of this (simpler code, less lost + interrupts) outweigh the costs, this will be default in the future. + WORKING FEATURES: o Color streaming/capture at 640x480, 448x336, 384x288, 352x288, and 320x240 o RGB24, RGB565, YUV420, YUV422, YUYV, and YUV422P color @@ -194,35 +287,35 @@ 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 176x144, 160x120 + o OV6630 sensor support + o Compression support + o Banding filter + o Task-time post-processing -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 YUV422 o Fix fixFrameRGBoffset(). It is not stable yet with streaming video. o V4L2 support (Probably not until it goes into the kernel) o Get rid of the memory management functions (put them in videodev.c??) - o Setting of contrast and brightness not working with 7620/7620AE + o Setting of color and hue not working with OV7620 + o Setting of contrast and hue not working with OV7620AE 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 OV511+ experiences frame corruption with moving objects 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: -You can email me at mwm@i.am . Please prefix the subject line +You can email me at mmcclell@bigfoot.com . Please prefix the subject line with "OV511: " so that I am certain to notice your message. CREDITS: @@ -232,3 +325,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.3-orig/drivers/usb/ov511.c linux/drivers/usb/ov511.c --- linux-2.4.3-orig/drivers/usb/ov511.c Mon Apr 9 00:13:42 2001 +++ linux/drivers/usb/ov511.c Mon Apr 9 00:45:23 2001 @@ -1,13 +1,14 @@ /* * OmniVision OV511 Camera-to-USB Bridge Driver * - * Copyright (c) 1999-2000 Mark W. McClelland + * Copyright (c) 1999-2001 Mark W. McClelland + * Original decompression code Copyright 1998-2000 OmniVision Technologies * Many improvements by Bret Wallach * Color fixes by by Orion Sky Lawlor (2/26/2000) * Snapshot code by Kevin Moore * OV7620 fixes by Charl P. Botha * Changes by Claudio Matsuoka - * + * * Based on the Linux CPiA driver written by Peter Pregler, * Scott J. Bertin and Johannes Erdfelt. * @@ -30,7 +31,7 @@ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -static const char version[] = "1.28"; +static const char version[] = "1.36"; #define __NO_VERSION__ @@ -52,11 +53,17 @@ #include "ov511.h" #define OV511_I2C_RETRIES 3 +#define ENABLE_OV511_COMPRESSION +#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: */ @@ -101,8 +108,7 @@ /* Prevent apps from timing out if frame is not done in time */ static int retry_sync = 0; -/* Enable compression. This is for experimentation only; compressed images - * still cannot be decoded yet. */ +/* Enable compression. Does not work reliably yet, and needs a fast CPU. */ static int compress = 0; /* Display test pattern - doesn't work yet either */ @@ -115,6 +121,46 @@ /* 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 register contents after initialization */ +static int dump_bridge = 0; + +/* Set this to 1 to dump the sensor register contents after initialization */ +static int dump_sensor = 0; + +/* Temporary option for debugging "works, but no image" problem. Prints the + * first 12 bytes of data (potentially a packet header) in each isochronous + * data frame. */ +static int printph = 0; + +/* Compression parameters - I'm not exactly sure what these do yet */ +static int phy = 0x1f; +static int phuv = 0x05; +static int pvy = 0x06; +static int pvuv = 0x06; +static int qhy = 0x14; +static int qhuv = 0x03; +static int qvy = 0x04; +static int qvuv = 0x04; + +/* Light frequency. Set to 50 or 60 (Hz), or zero for default settings */ +static int lightfreq = 0; + +/* Set this to 1 to enable banding filter by default. Compensates for alternating + * horizontal light/dark bands caused by (usually fluorescent) lights */ +static int bandingfilter = 0; + +/* Set this to 1 to do post-processing (decompression, format conversion, etc...) at + * task-time instead of interrupt-time. This only supports RGB and YUV420/420P, and may + * increase frame latency since all processing is deferred until the frame is totally + * received. If the benefits of this (simpler code, less lost interrupts) outweigh the + * costs, this will be default in the future. + */ +static int ttpp = 0; + MODULE_PARM(autoadjust, "i"); MODULE_PARM_DESC(autoadjust, "CCD dynamically changes exposure"); MODULE_PARM(debug, "i"); @@ -138,23 +184,55 @@ MODULE_PARM(retry_sync, "i"); MODULE_PARM_DESC(retry_sync, "Prevent apps from timing out"); MODULE_PARM(compress, "i"); -MODULE_PARM_DESC(compress, "Turn on compression (not functional yet)"); +MODULE_PARM_DESC(compress, "Turn on compression (not reliable yet)"); MODULE_PARM(testpat, "i"); MODULE_PARM_DESC(testpat, "Replace image with vertical bar testpattern (only partially working)"); MODULE_PARM(sensor_gbr, "i"); 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_bridge, "i"); +MODULE_PARM_DESC(dump_bridge, "Dump the bridge registers"); +MODULE_PARM(dump_sensor, "i"); +MODULE_PARM_DESC(dump_sensor, "Dump the sensor registers"); +MODULE_PARM(printph, "i"); +MODULE_PARM_DESC(printph, "Print the (potential) packet header of each isoc frame"); +MODULE_PARM(phy, "i"); +MODULE_PARM_DESC(phy, "Prediction range (horizontal Y)"); +MODULE_PARM(phuv, "i"); +MODULE_PARM_DESC(phuv, "Prediction range (horizontal UV)"); +MODULE_PARM(pvy, "i"); +MODULE_PARM_DESC(pvy, "Prediction range (vertical Y)"); +MODULE_PARM(pvuv, "i"); +MODULE_PARM_DESC(pvuv, "Prediction range (vertical UV)"); +MODULE_PARM(qhy, "i"); +MODULE_PARM_DESC(qhy, "Quantization threshold (horizontal Y)"); +MODULE_PARM(qhuv, "i"); +MODULE_PARM_DESC(qhuv, "Quantization threshold (horizontal UV)"); +MODULE_PARM(qvy, "i"); +MODULE_PARM_DESC(qvy, "Quantization threshold (vertical Y)"); +MODULE_PARM(qvuv, "i"); +MODULE_PARM_DESC(qvuv, "Quantization threshold (vertical UV)"); +MODULE_PARM(lightfreq, "i"); +MODULE_PARM_DESC(lightfreq, "Light frequency. Set to 50 or 60 Hz, or zero for default settings"); +MODULE_PARM(bandingfilter, "i"); +MODULE_PARM_DESC(bandingfilter, "Enable banding filter (to reduce effects of fluorescent lighting)"); +MODULE_PARM(ttpp, "i"); +MODULE_PARM_DESC(ttpp, "Do post-processing at task-time instead of interrupt-time (experimental)"); -MODULE_AUTHOR("Mark McClelland & Bret Wallach & Orion Sky Lawlor & Kevin Moore & Charl P. Botha & Claudio Matsuoka "); +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; +/* Function prototypes */ +static void ov51x_clear_snapshot(struct usb_ov511 *); +static int ov51x_check_snapshot(struct usb_ov511 *); +static inline int ov7610_get_picture(struct usb_ov511 *, struct video_picture *); +static int ov511_control_ioctl(struct inode *, struct file *, unsigned int, + unsigned long); /********************************************************************** * List of known OV511-based cameras @@ -162,12 +240,15 @@ 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! */ + { 41, "Samsung Anycam MPC-M10" }, + { 43, "Mtekvision Zeca MV402" }, { 100, "Lifeview RoboCam" }, { 102, "AverMedia InterCam Elite" }, { 112, "MediaForte MV300" }, /* or OV7110 evaluation kit */ @@ -177,6 +258,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 +287,9 @@ }; #endif +static unsigned char yQuanTable[] = YQUANTABLE; +static unsigned char uvQuanTable[] = UVQUANTABLE; + /********************************************************************** * * Memory management @@ -322,12 +407,19 @@ #define YES_NO(x) ((x) ? "yes" : "no") -static int ov511_read_proc(char *page, char **start, off_t off, - int count, int *eof, void *data) +/* /proc/video/ov511//info */ +static int ov511_read_proc_info(char *page, char **start, off_t off, + int count, int *eof, void *data) { char *out = page; int i, j, len; struct usb_ov511 *ov511 = data; + struct video_picture p; + + if (!ov511 || !ov511->dev) + return -ENODEV; + + ov7610_get_picture(ov511, &p); /* IMPORTANT: This output MUST be kept under PAGE_SIZE * or we need to get more sophisticated. */ @@ -343,9 +435,10 @@ out += sprintf (out, "sub_size : %d %d %d %d\n", ov511->subx, ov511->suby, ov511->subw, ov511->subh); out += sprintf (out, "data_format : %s\n", force_rgb ? "RGB" : "BGR"); - 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, "brightness : %d\n", p.brightness >> 8); + out += sprintf (out, "colour : %d\n", p.colour >> 8); + out += sprintf (out, "contrast : %d\n", p.contrast >> 8); + out += sprintf (out, "hue : %d\n", p.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 +464,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); @@ -394,45 +491,128 @@ return len; } -static int ov511_write_proc(struct file *file, const char *buffer, - unsigned long count, void *data) +/* /proc/video/ov511//button + * + * When the camera's button is pressed, the output of this will change from a + * 0 to a 1 (ASCII). It will retain this value until it is read, after which + * it will reset to zero. + * + * SECURITY NOTE: Since reading this file can change the state of the snapshot + * status, it is important for applications that open it to keep it locked + * against access by other processes, using flock() or a similar mechanism. No + * locking is provided by this driver. + */ +static int ov511_read_proc_button(char *page, char **start, off_t off, + int count, int *eof, void *data) { - return -EINVAL; + char *out = page; + int len, status; + struct usb_ov511 *ov511 = data; + + if (!ov511 || !ov511->dev) + return -ENODEV; + + status = ov51x_check_snapshot(ov511); + out += sprintf(out, "%d", status); + + if (status) + ov51x_clear_snapshot(ov511); + + len = out - page; + len -= off; + if (len < count) { + *eof = 1; + if (len <= 0) return 0; + } else + len = count; + + *start = page + off; + + return len; } -static void create_proc_ov511_cam (struct usb_ov511 *ov511) +static void create_proc_ov511_cam(struct usb_ov511 *ov511) { - char name[7]; - struct proc_dir_entry *ent; - + char dirname[4]; + if (!ov511_proc_entry || !ov511) return; - sprintf(name, "video%d", ov511->vdev.minor); - PDEBUG (4, "creating /proc/video/ov511/%s", name); - - ent = create_proc_entry(name, S_IFREG|S_IRUGO|S_IWUSR, ov511_proc_entry); + /* Create per-device directory */ + sprintf(dirname, "%d", ov511->vdev.minor); + PDEBUG(4, "creating /proc/video/ov511/%s/", dirname); + ov511->proc_devdir = create_proc_entry(dirname, S_IFDIR, + ov511_proc_entry); + if (!ov511->proc_devdir) + return; - if (!ent) + /* Create "info" entry (human readable device information) */ + PDEBUG(4, "creating /proc/video/ov511/%s/info", dirname); + ov511->proc_info = create_proc_read_entry("info", + S_IFREG|S_IRUGO|S_IWUSR, ov511->proc_devdir, + ov511_read_proc_info, ov511); + if (!ov511->proc_info) return; - ent->data = ov511; - ent->read_proc = ov511_read_proc; - ent->write_proc = ov511_write_proc; - ov511->proc_entry = ent; + /* Don't create it if old snapshot mode on (would cause race cond.) */ + if (!snapshot) { + /* Create "button" entry (snapshot button status) */ + PDEBUG(4, "creating /proc/video/ov511/%s/button", dirname); + ov511->proc_button = create_proc_read_entry("button", + S_IFREG|S_IRUGO|S_IWUSR, ov511->proc_devdir, + ov511_read_proc_button, ov511); + if (!ov511->proc_button) + return; + } + + /* Create "control" entry (ioctl() interface) */ + PDEBUG(4, "creating /proc/video/ov511/%s/control", dirname); + lock_kernel(); + ov511->proc_control = create_proc_entry("control", + S_IFREG|S_IRUGO|S_IWUSR, ov511->proc_devdir); + if (!ov511->proc_control) { + unlock_kernel(); + return; + } + ov511->proc_control->proc_fops->ioctl = ov511_control_ioctl; + ov511->proc_control->data = ov511; + unlock_kernel(); } -static void destroy_proc_ov511_cam (struct usb_ov511 *ov511) +static void destroy_proc_ov511_cam(struct usb_ov511 *ov511) { - char name[7]; + char dirname[4]; - if (!ov511 || !ov511->proc_entry) + if (!ov511 || !ov511->proc_devdir) return; - - sprintf(name, "video%d", ov511->vdev.minor); - PDEBUG (4, "destroying %s", name); - remove_proc_entry(name, ov511_proc_entry); - ov511->proc_entry = NULL; + + sprintf(dirname, "%d", ov511->vdev.minor); + + /* Destroy "control" entry */ + if (ov511->proc_control) { + PDEBUG(4, "destroying /proc/video/ov511/%s/control", dirname); + remove_proc_entry("control", ov511->proc_devdir); + ov511->proc_control = NULL; + } + + /* Destroy "button" entry */ + if (ov511->proc_button) { + PDEBUG(4, "destroying /proc/video/ov511/%s/button", dirname); + remove_proc_entry("button", ov511->proc_devdir); + ov511->proc_button = NULL; + } + + /* Destroy "info" entry */ + if (ov511->proc_info) { + PDEBUG(4, "destroying /proc/video/ov511/%s/info", dirname); + remove_proc_entry("info", ov511->proc_devdir); + ov511->proc_info = NULL; + } + + /* Destroy per-device directory */ + PDEBUG(4, "destroying /proc/video/ov511/%s/", dirname); + remove_proc_entry(dirname, ov511_proc_entry); + ov511->proc_devdir = NULL; } static void proc_ov511_create(void) @@ -442,7 +622,7 @@ * -claudio */ if (video_proc_entry == NULL) { - err("Unable to initialise /proc/video/ov511"); + err("Error: /proc/video/ does not exist"); return; } @@ -451,7 +631,7 @@ if (ov511_proc_entry) ov511_proc_entry->owner = THIS_MODULE; else - err("Unable to initialise /proc/ov511"); + err("Unable to create /proc/video/ov511"); } static void proc_ov511_destroy(void) @@ -477,14 +657,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 +683,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 +693,143 @@ } } +/* + * 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; + + PDEBUG(4, "Uploading quantization tables"); + + 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 < 0) + 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 < 0) + 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 +869,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 +962,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 +976,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 +1036,64 @@ 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); + /* NOTE: Quantization tables are not readable. You will get the value + * in reg. 0x79 for every table register */ 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 +1103,120 @@ /* 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)); +} + +/* Resets the hardware snapshot button */ +static void ov51x_clear_snapshot(struct usb_ov511 *ov511) +{ + if (ov511->bridge == BRG_OV511 || ov511->bridge == BRG_OV511PLUS) { + ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x01); + ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x03); + ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x01); + } else if (ov511->bridge == BRG_OV518PLUS) { + warn("snapshot reset not supported yet on OV518(+)"); + } else { + err("clear snap: invalid sensor type"); + } + +} + +/* Checks the status of the snapshot button. Returns 1 if it was pressed since + * it was last cleared, and zero in all other cases (including errors) */ +static int ov51x_check_snapshot(struct usb_ov511 *ov511) +{ + int ret, status = 0; + + if (ov511->bridge == BRG_OV511 || ov511->bridge == BRG_OV511PLUS) { + ret = ov511_reg_read(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT); + if (ret < 0) { + err("Error checking snspshot status (%d)", ret); + } else if (ret & 0x08) { + status = 1; + } + } else if (ov511->bridge == BRG_OV518PLUS) { + warn("snapshot check not supported yet on OV518(+)"); + } else { + err("check snap: invalid sensor type"); + } + + return status; +} + +/* 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 +1244,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,118 +1264,436 @@ 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; } - -static inline int -ov7610_set_picture(struct usb_ov511 *ov511, struct video_picture *p) +/* Upload compression params and quantization tables. Returns 0 for success. */ +static int +ov51x_init_compression(struct usb_ov511 *ov511) { - int ret; struct usb_device *dev = ov511->dev; + int rc = 0; - PDEBUG(4, "ov511_set_picture"); + if (ov511->bridge == BRG_OV518PLUS) { + err("compression not supported on OV518"); + rc = -EINVAL; + goto out; + } + + if (!ov511->compress_inited) { + ov511_reg_write(dev, 0x70, phy); + ov511_reg_write(dev, 0x71, phuv); + ov511_reg_write(dev, 0x72, pvy); + ov511_reg_write(dev, 0x73, pvuv); + ov511_reg_write(dev, 0x74, qhy); + ov511_reg_write(dev, 0x75, qhuv); + ov511_reg_write(dev, 0x76, qvy); + ov511_reg_write(dev, 0x77, qvuv); + if (ov511_upload_quan_tables(dev) < 0) { + err("Error uploading quantization tables"); + rc = -EIO; + goto out; + } + } - if (ov511_stop(dev) < 0) - return -EIO; + ov511->compress_inited = 1; +out: + return rc; +} - ov511->contrast = p->contrast; - ov511->brightness = p->brightness; - ov511->colour = p->colour; - ov511->hue = p->hue; - ov511->whiteness = p->whiteness; +/* -------------------------------------------------------------------------- */ - if ((ret = ov511_i2c_read(dev, OV7610_REG_COM_B)) < 0) - return -EIO; -#if 0 - /* disable auto adjust mode */ - if (ov511_i2c_write(dev, OV7610_REG_COM_B, ret & 0xfe) < 0) - return -EIO; -#endif - if (ov511->sensor == SEN_OV7610 || ov511->sensor == SEN_OV7620AE - || ov511->sensor == SEN_OV6620) - if (ov511_i2c_write(dev, OV7610_REG_SAT, p->colour >> 8) < 0) - return -EIO; +/* Sets sensor's contrast setting to "val" */ +static int +ov51x_set_contrast(struct usb_ov511 *ov511, unsigned short val) +{ + PDEBUG(4, "%d", val); - if (ov511->sensor == SEN_OV7610 || ov511->sensor == SEN_OV6620) { - if (ov511_i2c_write(dev, OV7610_REG_CNT, p->contrast >> 8) < 0) - return -EIO; + if (ov511_stop(ov511) < 0) + return -EIO; - if (ov511_i2c_write(dev, OV7610_REG_RED, 0xFF - (p->hue >> 8)) < 0) + switch (ov511->sensor) { + case SEN_OV7610: + case SEN_OV6620: + case SEN_OV6630: + { + if (ov511_i2c_write(ov511->dev, OV7610_REG_CNT, val >> 8) < 0) return -EIO; + break; + } + case SEN_OV7620: + { + unsigned char ctab[] = { + 0x01, 0x05, 0x09, 0x11, 0x15, 0x35, 0x37, 0x57, + 0x5b, 0xa5, 0xa7, 0xc7, 0xc9, 0xcf, 0xef, 0xff + }; - if (ov511_i2c_write(dev, OV7610_REG_BLUE, p->hue >> 8) < 0) + /* Use Y gamma control instead. Bit 0 enables it. */ + if (ov511_i2c_write(ov511->dev, 0x64, ctab[val>>12]) < 0) return -EIO; + break; + } + default: + { + PDEBUG(4, "Unsupported with this sensor"); + return -EPERM; + } + } - if (ov511_i2c_write(dev, OV7610_REG_BRT, p->brightness >> 8) < 0) - return -EIO; - } else if ((ov511->sensor == SEN_OV7620) - || (ov511->sensor == SEN_OV7620AE)) { -#if 0 - int cur_sat, new_sat, tmp; + if (ov511_restart(ov511) < 0) + return -EIO; - cur_sat = ov511_i2c_read(dev, OV7610_REG_BLUE); + return 0; +} - tmp = (p->hue >> 8) - cur_sat; - new_sat = (tmp < 0) ? (-tmp) | 0x80 : tmp; +/* Gets sensor's contrast setting */ +static int +ov51x_get_contrast(struct usb_ov511 *ov511, unsigned short *val) +{ + int ret; - PDEBUG(1, "cur=%d target=%d diff=%d", cur_sat, p->hue >> 8, tmp); + if (ov511_stop(ov511) < 0) + return -EIO; - if (ov511_i2c_write(dev, OV7610_REG_BLUE, new_sat) < 0) + switch (ov511->sensor) { + case SEN_OV7610: + case SEN_OV6620: + case SEN_OV6630: + ret = ov511_i2c_read(ov511->dev, OV7610_REG_CNT); + if (ret < 0) return -EIO; - - // DEBUG_CODE - PDEBUG(1, "hue=%d", ov511_i2c_read(dev, OV7610_REG_BLUE)); - -#endif + break; + case SEN_OV7620: + /* Use Y gamma reg instead. Bit 0 is the enable bit. */ + ret = ov511_i2c_read(ov511->dev, 0x64); + if (ret < 0) + return -EIO; + else + ret &= 0xfe; + break; + default: + PDEBUG(4, "Unsupported with this sensor"); + return -EPERM; } - if (ov511_restart(dev) < 0) + if (ov511_restart(ov511) < 0) return -EIO; + *val = ret << 8; + + PDEBUG(4, "%d", *val); + return 0; } -static inline int -ov7610_get_picture(struct usb_ov511 *ov511, struct video_picture *p) +/* -------------------------------------------------------------------------- */ + +/* Sets sensor's brightness setting to "val" */ +static int +ov51x_set_brightness(struct usb_ov511 *ov511, unsigned short val) +{ + PDEBUG(4, "%d", val); + + if (ov511_stop(ov511) < 0) + return -EIO; + + switch (ov511->sensor) { + case SEN_OV7610: + case SEN_OV7620AE: + case SEN_OV7620: + case SEN_OV6620: + case SEN_OV6630: + if (ov511_i2c_write(ov511->dev, OV7610_REG_BRT, val >> 8) < 0) + return -EIO; + break; + default: + PDEBUG(4, "Unsupported with this sensor"); + return -EPERM; + } + + if (ov511_restart(ov511) < 0) + return -EIO; + + return 0; +} + +/* Gets sensor's brightness setting */ +static int +ov51x_get_brightness(struct usb_ov511 *ov511, unsigned short *val) { int ret; - struct usb_device *dev = ov511->dev; + + if (ov511_stop(ov511) < 0) + return -EIO; + + switch (ov511->sensor) { + case SEN_OV7610: + case SEN_OV7620AE: + case SEN_OV7620: + case SEN_OV6620: + case SEN_OV6630: + ret = ov511_i2c_read(ov511->dev, OV7610_REG_BRT); + if (ret < 0) + return -EIO; + break; + default: + PDEBUG(4, "Unsupported with this sensor"); + return -EPERM; + } + + if (ov511_restart(ov511) < 0) + return -EIO; + + *val = ret << 8; + + PDEBUG(4, "%d", *val); + + return 0; +} + +/* -------------------------------------------------------------------------- */ + +/* Sets sensor's saturation (color intensity) setting to "val" */ +static int +ov51x_set_saturation(struct usb_ov511 *ov511, unsigned short val) +{ + PDEBUG(4, "%d", val); + + if (ov511_stop(ov511) < 0) + return -EIO; + + switch (ov511->sensor) { + case SEN_OV7610: + case SEN_OV7620AE: + case SEN_OV6620: + case SEN_OV6630: + if (ov511_i2c_write(ov511->dev, OV7610_REG_SAT, val >> 8) < 0) + return -EIO; + break; + case SEN_OV7620: +// /* Use UV gamma control instead. Bits 0 & 7 are reserved. */ +// if (ov511_i2c_write(ov511->dev, 0x62, (val >> 9) & 0x7e) < 0) +// return -EIO; + if (ov511_i2c_write(ov511->dev, OV7610_REG_SAT, val >> 8) < 0) + return -EIO; + break; + default: + PDEBUG(4, "Unsupported with this sensor"); + return -EPERM; + } + + if (ov511_restart(ov511) < 0) + return -EIO; + + return 0; +} + +/* Gets sensor's saturation (color intensity) setting */ +static int +ov51x_get_saturation(struct usb_ov511 *ov511, unsigned short *val) +{ + int ret; + + if (ov511_stop(ov511) < 0) + return -EIO; + + switch (ov511->sensor) { + case SEN_OV7610: + case SEN_OV7620AE: + case SEN_OV6620: + case SEN_OV6630: + ret = ov511_i2c_read(ov511->dev, OV7610_REG_SAT); + if (ret < 0) + return -EIO; + break; + case SEN_OV7620: + /* Use UV gamma reg instead. Bits 0 & 7 are reserved. */ + ret = ov511_i2c_read(ov511->dev, 0x62); + if (ret < 0) + return -EIO; + else + ret &= 0x7e; + break; + default: + PDEBUG(4, "Unsupported with this sensor"); + return -EPERM; + } + + if (ov511_restart(ov511) < 0) + return -EIO; + + *val = ret << 8; + + PDEBUG(4, "%d", *val); + + return 0; +} + +/* -------------------------------------------------------------------------- */ + +/* Sets sensor's hue (red/blue balance) setting to "val" */ +static int +ov51x_set_hue(struct usb_ov511 *ov511, unsigned short val) +{ + PDEBUG(4, "%d", val); + + if (ov511_stop(ov511) < 0) + return -EIO; + + switch (ov511->sensor) { + case SEN_OV7610: + case SEN_OV6620: + case SEN_OV6630: + if (ov511_i2c_write(ov511->dev, OV7610_REG_RED, 0xFF - (val >> 8)) < 0) + return -EIO; + + if (ov511_i2c_write(ov511->dev, OV7610_REG_BLUE, val >> 8) < 0) + return -EIO; + break; + case SEN_OV7620: + if (ov511_i2c_write(ov511->dev, 0x7a, (unsigned char)(val >> 8) + 0xb) < 0) + return -EIO; + + if (ov511_i2c_write(ov511->dev, 0x79, (unsigned char)(val >> 8) + 0xb) < 0) + return -EIO; + break; + default: + PDEBUG(4, "Unsupported with this sensor"); + return -EPERM; + } + + if (ov511_restart(ov511) < 0) + return -EIO; + + return 0; +} + +/* Gets sensor's hue (red/blue balance) setting */ +static int +ov51x_get_hue(struct usb_ov511 *ov511, unsigned short *val) +{ + int ret; + + if (ov511_stop(ov511) < 0) + return -EIO; + + switch (ov511->sensor) { + case SEN_OV7610: + case SEN_OV6620: + case SEN_OV6630: + ret = ov511_i2c_read(ov511->dev, OV7610_REG_BLUE); + if (ret < 0) + return -EIO; + break; + case SEN_OV7620: + ret = ov511_i2c_read(ov511->dev, 0x7a); + if (ret < 0) + return -EIO; + break; + default: + PDEBUG(4, "Unsupported with this sensor"); + return -EPERM; + } + + if (ov511_restart(ov511) < 0) + return -EIO; + + *val = ret << 8; + + PDEBUG(4, "%d", *val); + + return 0; +} + +/* -------------------------------------------------------------------------- */ + +static inline int +ov7610_set_picture(struct usb_ov511 *ov511, struct video_picture *p) +{ + int rc; + + PDEBUG(4, "ov511_set_picture"); + + ov511->contrast = p->contrast; + ov511->brightness = p->brightness; + ov511->colour = p->colour; + ov511->hue = p->hue; + ov511->whiteness = p->whiteness; + + /* Don't return error if a setting is unsupported, or rest of settings + * will not be performed */ + + rc = ov51x_set_contrast(ov511, p->contrast); + if (rc < 0 && rc != -EPERM) + return rc; + + rc = ov51x_set_brightness(ov511, p->brightness); + if (rc < 0 && rc != -EPERM) + return rc; + + rc = ov51x_set_saturation(ov511, p->colour); + if (rc < 0 && rc != -EPERM) + return rc; + + rc = ov51x_set_hue(ov511, p->hue); + if (rc < 0 && rc != -EPERM) + return rc; + + return 0; +} + +static inline int +ov7610_get_picture(struct usb_ov511 *ov511, struct video_picture *p) +{ + int rc; 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; - p->colour = ret << 8; + /* Don't return error if a setting is unsupported, or rest of settings + * will not be performed */ + + rc = ov51x_get_contrast(ov511, &(p->contrast)); + if (rc < 0 && rc != -EPERM) + return rc; - if ((ret = ov511_i2c_read(dev, OV7610_REG_CNT)) < 0) return -EIO; - p->contrast = ret << 8; + rc = ov51x_get_brightness(ov511, &(p->brightness)); + if (rc < 0 && rc != -EPERM) + return rc; - if ((ret = ov511_i2c_read(dev, OV7610_REG_BRT)) < 0) return -EIO; - p->brightness = ret << 8; + rc = ov51x_get_saturation(ov511, &(p->colour)); + if (rc < 0 && rc != -EPERM) + return rc; - /* This may not be the best way to do it */ - if ((ret = ov511_i2c_read(dev, OV7610_REG_BLUE)) < 0) return -EIO; - p->hue = ret << 8; + rc = ov51x_get_hue(ov511, &(p->hue)); + if (rc < 0 && rc != -EPERM) + return rc; p->whiteness = 105 << 8; @@ -899,9 +1701,127 @@ 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; +} + +/* Matches the sensor's internal frame rate to the lighting frequency. + * Valid frequencies are: + * 50 - 50Hz, for European and Asian lighting + * 60 - 60Hz, for American lighting + * + * Tested with: OV7610, OV7620, OV7620AE, OV6620 + * Returns: 0 for success + */ +static int +ov51x_set_light_freq(struct usb_ov511 *ov511, int freq) { + int sixty, rc = 0; + + PDEBUG(4, "%d Hz", freq); + + if (freq == 60) + sixty = 1; + else if (freq == 50) + sixty = 0; + else { + err("Invalid light freq (%d Hz)", freq); + return -EINVAL; + } + + if (ov511_stop(ov511) < 0) + return -EIO; + + switch (ov511->sensor) { + case SEN_OV7610: + ov511_i2c_write_mask(ov511->dev, 0x2a, sixty?0x00:0x80, 0x80); + ov511_i2c_write(ov511->dev, 0x2b, sixty?0x00:0xac); + ov511_i2c_write_mask(ov511->dev, 0x13, 0x10, 0x10); + ov511_i2c_write_mask(ov511->dev, 0x13, 0x00, 0x10); + break; + case SEN_OV7620: + case SEN_OV7620AE: + case SEN_OV8600: + ov511_i2c_write_mask(ov511->dev, 0x2a, sixty?0x00:0x80, 0x80); + ov511_i2c_write(ov511->dev, 0x2b, sixty?0x00:0xac); + ov511_i2c_write_mask(ov511->dev, 0x76, 0x01, 0x01); + break; + case SEN_OV6620: + case SEN_OV6630: + ov511_i2c_write(ov511->dev, 0x2b, sixty?0xa8:0x28); + ov511_i2c_write(ov511->dev, 0x2a, sixty?0x84:0xa4); + break; + default: + err("Sensor not supported for set_light_freq"); + rc = -EINVAL; + goto out_restart; + } + + ov511->lightfreq = freq; + +out_restart: + if (ov511_restart(ov511) < 0) return -EIO; + return rc; +} + +/* If enable is true, turn on the sensor's banding filter, otherwise turn it off. This + * filter tries to reduce the pattern of horizontal light/dark bands caused by some + * (usually fluorescent) lighting. The light frequency must be set either before or + * after enabling it with ov51x_set_light_freq(). + * + * Tested with: OV7610, OV7620, OV7620AE, OV6620. + * Returns: 0 for success + */ +static inline int +ov51x_set_banding_filter(struct usb_ov511 *ov511, int enable) +{ + int rc; + + PDEBUG(4, " (%s)", enable ? "turn on" : "turn off"); + + rc = ov511_i2c_write_mask(ov511->dev, 0x2d, enable?0x04:0x00, 0x04); + if (rc < 0) + return rc; + + ov511->bandfilt = enable; + + return 0; +} + +/* If enable is true, turn on the sensor's auto brightness control, otherwise + * turn it off. + * Returns: 0 for success + */ +static inline int +ov51x_set_auto_brightness(struct usb_ov511 *ov511, int enable) +{ + int rc; + + PDEBUG(4, " (%s)", enable ? "turn on" : "turn off"); + + rc = ov511_i2c_write_mask(ov511->dev, 0x2d, enable?0x10:0x00, 0x10); + if (rc < 0) + return rc; + + ov511->auto_brt = enable; + return 0; } @@ -918,12 +1838,13 @@ case VIDEO_PALETTE_YUYV: return 16; case VIDEO_PALETTE_YUV420: return 24; case VIDEO_PALETTE_YUV422P: return 24; /* Planar */ + case VIDEO_PALETTE_YUV420P: return 24; /* Planar */ default: return 0; /* Invalid format */ } } /* 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 }, @@ -935,26 +1856,76 @@ { 384, 288, 1, 0x2f, 0x25, 0x00, 0x00, 0x03, 0x04, 0x1e }, { 448, 336, 0, 0x37, 0x29, 0x00, 0x00, 0x03, 0x04, 0x1e }, { 448, 336, 1, 0x37, 0x29, 0x00, 0x00, 0x03, 0x04, 0x1e }, - { 176, 144, 0, 0x15, 0x12, 0x00, 0x00, 0x03, 0x04, 0x1e }, - { 176, 144, 1, 0x15, 0x12, 0x00, 0x00, 0x03, 0x04, 0x1e }, + { 176, 144, 0, 0x13, 0x13, 0x00, 0x00, 0x03, 0x04, 0x1e }, + { 176, 144, 1, 0x13, 0x13, 0x00, 0x00, 0x03, 0x04, 0x1e }, { 160, 120, 0, 0x13, 0x0e, 0x00, 0x00, 0x03, 0x04, 0x1e }, { 160, 120, 1, 0x13, 0x0e, 0x00, 0x00, 0x03, 0x04, 0x1e }, { 0, 0 } }; +// FIXME: Move rest of sensor init here, and add 6620/6630 support +static int +ov511_mode_init_sensor_regs(struct usb_ov511 *ov511, int qvga) +{ + struct usb_device *dev = ov511->dev; + + /* Set mode (VGA/QVGA) specific regs */ + switch (ov511->sensor) { + case SEN_OV7610: + ov511_i2c_write(dev, 0x14, qvga?0x24:0x04); +// FIXME: Does this improve the image quality or frame rate? +#if 0 + ov511_i2c_write_mask(dev, 0x28, qvga?0x00:0x20, 0x20); + ov511_i2c_write(dev, 0x24, 0x10); + ov511_i2c_write(dev, 0x25, qvga?0x40:0x8a); + ov511_i2c_write(dev, 0x2f, qvga?0x30:0xb0); + ov511_i2c_write(dev, 0x35, qvga?0x1c:0x9c); +#endif + break; + case SEN_OV7620: +// ov511_i2c_write(dev, 0x2b, 0x00); + ov511_i2c_write(dev, 0x14, qvga?0xa4:0x84); + ov511_i2c_write_mask(dev, 0x28, qvga?0x00:0x20, 0x20); + ov511_i2c_write(dev, 0x24, qvga?0x20:0x3a); + ov511_i2c_write(dev, 0x25, qvga?0x30:0x60); + ov511_i2c_write_mask(dev, 0x2d, qvga?0x40:0x00, 0x40); + ov511_i2c_write_mask(dev, 0x67, qvga?0xf0:0x90, 0xf0); + ov511_i2c_write_mask(dev, 0x74, qvga?0x20:0x00, 0x20); + break; + case SEN_OV7620AE: +// ov511_i2c_write(dev, 0x2b, 0x00); + ov511_i2c_write(dev, 0x14, qvga?0xa4:0x84); +// FIXME: Enable this once 7620AE uses 7620 initial settings +#if 0 + ov511_i2c_write_mask(dev, 0x28, qvga?0x00:0x20, 0x20); + ov511_i2c_write(dev, 0x24, qvga?0x20:0x3a); + ov511_i2c_write(dev, 0x25, qvga?0x30:0x60); + ov511_i2c_write_mask(dev, 0x2d, qvga?0x40:0x00, 0x40); + ov511_i2c_write_mask(dev, 0x67, qvga?0xb0:0x90, 0xf0); + ov511_i2c_write_mask(dev, 0x74, qvga?0x20:0x00, 0x20); +#endif + break; + default: + err("Invalid sensor"); + return -EINVAL; + } + + return 0; +} + static int ov511_mode_init_regs(struct usb_ov511 *ov511, int width, int height, int mode, int sub_flag) { - int i; + int i, ret; struct usb_device *dev = ov511->dev; int hwsbase, hwebase, vwsbase, vwebase, hwsize, vwsize; - int hwscale = 0, vwscale = 0; + int hoffset, voffset, hwscale = 0, vwscale = 0; 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 +1938,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,17 +1950,18 @@ 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); ov511_reg_write(dev, 0x1f, 0x03); } - /* The different sensor ICs handle setting up of window differently */ + /* The different sensor ICs handle setting up of window differently. + * IF YOU SET IT WRONG, YOU WILL GET ALL ZERO ISOC DATA FROM OV511!!! */ switch (ov511->sensor) { case SEN_OV7610: case SEN_OV7620AE: @@ -998,6 +1970,7 @@ vwsbase = vwebase = 0x05; break; case SEN_OV6620: + case SEN_OV6630: // FIXME: Is this right? hwsbase = 0x38; hwebase = 0x3a; vwsbase = 0x05; @@ -1029,19 +2002,27 @@ } else { if (width > 320 && height > 240) { /* VGA */ - ov511_i2c_write(dev, 0x14, 0x04); + ret = ov511_mode_init_sensor_regs(ov511, 0); + if (ret < 0) + return ret; hwscale = 2; vwscale = 1; hwsize = 640; vwsize = 480; } else { /* QVGA */ - ov511_i2c_write(dev, 0x14, 0x24); + ret = ov511_mode_init_sensor_regs(ov511, 1); + if (ret < 0) + return ret; hwscale = 1; hwsize = 320; vwsize = 240; - } + } } + /* Center the window */ + hoffset = ((hwsize - width) / 2) >> hwscale; + voffset = ((vwsize - height) / 2) >> vwscale; + /* FIXME! - This needs to be changed to support 160x120 and 6620!!! */ if (sub_flag) { ov511_i2c_write(dev, 0x17, hwsbase+(ov511->subx>>hwscale)); @@ -1049,82 +2030,439 @@ 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)); + ov511_i2c_write(dev, 0x17, hwsbase + hoffset); + ov511_i2c_write(dev, 0x18, hwebase + hoffset + (hwsize>>hwscale)); + ov511_i2c_write(dev, 0x19, vwsbase + voffset); + ov511_i2c_write(dev, 0x1a, vwebase + voffset + (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); - /* Calculate and set the clock divisor */ - clock = ((sub_flag ? ov511->subw * ov511->subh : width * height) - * (mlist[i].color ? 3 : 2) / 2) / 66000; + /* 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; + 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, mlist511[i].common_A | (testpat?0x0a:0x08)); else - ov511_i2c_write(dev, 0x12, mlist[i].common_A | (testpat?0x02:0x00)); + ov511_i2c_write(dev, 0x12, mlist511[i].common_A | (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, 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->lightfreq) + if (ov51x_set_light_freq(ov511, lightfreq)) + return -1; + + if (ov51x_set_banding_filter(ov511, bandingfilter)) + return -1; + + if (ov511->compress) { + ov511_reg_write(dev, 0x78, 0x07); // Turn on Y and UV compression + ov511_reg_write(dev, 0x79, 0x03); // Enable LUTs + ov511_reset(ov511, OV511_RESET_OMNICE); } - if (ov511_restart(ov511->dev) < 0) + if (ov511_restart(ov511) < 0) return -EIO; - if (mlist[i].width == 0) { + if (mlist511[i].width == 0) { err("Unknown mode (%d, %d): %d", width, height, mode); return -EINVAL; } #ifdef OV511_DEBUG - if (debug >= 5) + if (dump_sensor) 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 hoffset, voffset, 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_OV6630) { + 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; + } + } + + /* Center the window */ + hoffset = ((hwsize - width) / 2) >> hwscale; + voffset = ((vwsize - height) / 2) >> vwscale; + + /* 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 + hoffset); + ov511_i2c_write(dev, 0x18, hwebase + hoffset + (hwsize>>hwscale)); + ov511_i2c_write(dev, 0x19, vwsbase + voffset); + ov511_i2c_write(dev, 0x1a, vwebase + voffset + (vwsize>>vwscale)); + } + + 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; +#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, 0x04 | (testpat?0x0a:0x08)); + else + ov511_i2c_write(dev, 0x12, 0x04 | (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, 0x1e); + + break; + } + + if (ov511->lightfreq) + if (ov51x_set_light_freq(ov511, lightfreq)) + return -1; + + if (ov51x_set_banding_filter(ov511, bandingfilter)) + return -1; + + if (ov511->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 (mlist518[i].width == 0) { + err("Unknown mode (%d, %d): %d", width, height, mode); + return -EINVAL; + } + +#ifdef OV511_DEBUG + if (dump_sensor) + 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 +2711,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 +2749,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) */ @@ -1509,105 +2858,1766 @@ unsigned a = iWidth * iHeight; unsigned w = iWidth / 2; - pIn = pIn0; - pOut = pOut0 + iOutUV + a; - for (k = 0; k < 8; k++) { - pOut1 = pOut; - for (l = 0; l < 8; l++) { - *pOut1 = *(pOut1 + w) = *pIn++; - pOut1++; - } - pOut += iWidth; - } + pIn = pIn0; + pOut = pOut0 + iOutUV + a; + for (k = 0; k < 8; k++) { + pOut1 = pOut; + for (l = 0; l < 8; l++) { + *pOut1 = *(pOut1 + w) = *pIn++; + pOut1++; + } + pOut += iWidth; + } + + pIn = pIn0 + 64; + pOut = pOut0 + iOutUV + a + a/2; + for (k = 0; k < 8; k++) { + pOut1 = pOut; + for (l = 0; l < 8; l++) { + *pOut1 = *(pOut1 + w) = *pIn++; + pOut1++; + } + pOut += iWidth; + } + + pIn = pIn0 + 128; + pOut = pOut0 + iOutY; + for (k = 0; k < 4; k++) { + pOut1 = pOut; + for (l = 0; l < 8; l++) { + for (m = 0; m < 8; m++) + *pOut1++ =*pIn++; + pOut1 += iWidth - 8; + } + pOut += 8; + } +} + +/* + * For 640x480 RAW BW images, data shows up in 1200 256 byte segments. + * The segments represent 4 squares of 8x8 pixels as follows: + * + * 0 1 ... 7 64 65 ... 71 ... 192 193 ... 199 + * 8 9 ... 15 72 73 ... 79 200 201 ... 207 + * ... ... ... + * 56 57 ... 63 120 121 ... 127 248 249 ... 255 + * + */ +static void +ov511_parse_data_grey(unsigned char *pIn0, unsigned char *pOut0, + int iOutY, int iWidth) +{ + int k, l, m; + unsigned char *pIn; + unsigned char *pOut, *pOut1; + + pIn = pIn0; + pOut = pOut0 + iOutY; + for (k = 0; k < 4; k++) { + pOut1 = pOut; + for (l = 0; l < 8; l++) { + for (m = 0; m < 8; m++) { + *pOut1++ = *pIn++; + } + pOut1 += iWidth - 8; + } + pOut += 8; + } +} + +/* + * fixFrameRGBoffset-- + * My camera seems to return the red channel about 1 pixel + * low, and the blue channel about 1 pixel high. After YUV->RGB + * conversion, we can correct this easily. OSL 2/24/2000. + */ +static void fixFrameRGBoffset(struct ov511_frame *frame) +{ + int x, y; + int rowBytes = frame->width*3, w = frame->width; + unsigned char *rgb = frame->data; + const int shift = 1; /* Distance to shift pixels by, vertically */ + + /* Don't bother with little images */ + if (frame->width < 400) + return; + + /* Shift red channel up */ + for (y = shift; y < frame->height; y++) { + int lp = (y-shift)*rowBytes; /* Previous line offset */ + int lc = y*rowBytes; /* Current line offset */ + for (x = 0; x < w; x++) + rgb[lp+x*3+2] = rgb[lc+x*3+2]; /* Shift red up */ + } + + /* Shift blue channel down */ + for (y = frame->height-shift-1; y >= 0; y--) { + int ln = (y + shift) * rowBytes; /* Next line offset */ + int lc = y * rowBytes; /* Current line offset */ + for (x = 0; x < w; x++) + rgb[ln+x*3+0] = rgb[lc+x*3+0]; /* Shift blue down */ + } +} + +/********************************************************************** + * + * Decompression + * + **********************************************************************/ + +/* Large memory buffer allocation. */ +#define ALLOCATE(a, size, b) vmalloc(size) +#define FREE(addr) vfree(addr) + +#ifdef ENABLE_OV511_COMPRESSION + +/* Tables */ +static UCHAR YQuanTable[] = YQUANTABLE; +static UCHAR UVQuanTable[] = UVQUANTABLE; +static SHORT IDCTTable[] = IDCTTABLE; +static ULONG ZigZagTable[] = ZIGZAGTABLE; + +static PUCHAR GetYQuanTable(void) +{ + return YQuanTable; +} + +static PUCHAR GetUVQuanTable(void) +{ + return UVQuanTable; +} + +static PSHORT GetIDCTTable(void) +{ + return IDCTTable; +} + +static PULONG GetZigZagTable(void) +{ + return ZigZagTable; +} + +/************************************************************************/ +/* Avoid changing anything below this point, to make synchronizing with */ +/* newer code from OmniVision easier. */ +/************************************************************************/ + +static int DecompressYHINoMMX( LPBYTE lpDestBuf, + LPBYTE lpTMPBUF, + ULONG index, + DWORD IPTRR, + DWORD X_WIDE, + PUCHAR TableBufferYUV, + PSHORT IDCT_Coef, + PULONG ZZLN, + DWORDLONG UV_Scalor, + ULONG YUVFlag, + struct ov511_frame *frame) +{ + short ZigZag[64]; + int temp[64]; + int Zcnt_Flag=0; + int Num8_Flag=0; + int tmp, tmp1, tmp2, tmp3; + unsigned char header, ZTable[64]; + short tmpl, tmph, half_byte, idx, count; + DWORD ZigZag_length=0, ZT_length, i, j; +// DWORD Start, k; + short DeZigZag[64]; + + const short a = 11584; + const short b = 16068; + const short c = 15136; + const short d = 13624; + const short e = 9104; + const short f = 6270; + const short g = 3196; + + int IPTRR_YUV; + +#if 0 + int coef[64] = + { + 11584, 11584, 11584, 11584, 11584, 11584, 11584, 11584, + 16068, 13624, 9104, 3196, -3196, -9104, -13624, -16068, + 15136, 6270, -6270, -15136, -15136, -6270, 6270, 15136, + 13624, -3196, -16068, -9104, 9104, 16068, 3196, -13624, + 11584, -11584, -11584, 11584, 11584, -11584, -11584, 11584, + 9104, -16068, 3196, 13624, -13624, -3196, 16068, -9104, + 6270, -15136, 15136, -6270, -6270, 15136, -15136, 6270, + 3196, -9104, 13624, -16068, 16068, -13624, 9104, -3196 + }; + + int coeft[64] = + { + 11584, 16068, 15136, 13624, 11584, 9104, 6270, 3196, + 11584, 13624, 6270, -3196, -11584, -16068, -15136, -9104, + 11584, 9104, -6270, -16068, -11584, 3196, 15136, 13624, + 11584, 3196, -15136, -9104, 11584, 13624, -6270, -16068, + 11584, -3196, -15136, 9104, 11584, -13624, -6270, 16068, + 11584, -9104, -6270, 16068, -11584, -3196, 15136, -13624, + 11584, -13624, 6270, 3196, -11584, 16068, -15136, 9104, + 11584, -16068, 15136, -13624, 11584, -9104, 6270, -3196 + }; +#endif + + for(i=0; i<64; i++) + { + ZigZag[i] = 0; + } + + +// Read in the Y header byte + + + header = lpTMPBUF[index]; + index++; + + ZigZag_length = header & 0x3f; + ZigZag_length = ZigZag_length + 1; + + Num8_Flag = header & 0x40; + Zcnt_Flag = header & 0x80; + +// Read in the Y content +#if 1 + if(Zcnt_Flag==0) // Without Zero Table Read contents directly + { + + // Read in ZigZag[0] + ZigZag[0] = lpTMPBUF[index++]; + tmpl = lpTMPBUF[index++]; + tmph = tmpl<<8; + ZigZag[0] = ZigZag[0] | tmph; + ZigZag[0] = ZigZag[0]<<4; + ZigZag[0] = ZigZag[0]>>4; + + if(Num8_Flag) // 8 Bits + { + for(i=1; i>8; + + } + } + else // 12 bits and has no Zero table + { + + idx=1; + half_byte=0; + for(i=1; i>4; + half_byte=1; + } + else + { + ZigZag[i] = lpTMPBUF[index++]; + ZigZag[i] = ZigZag[i]<<8; + tmpl = tmpl & 0x00f0; + ZigZag[i] = ZigZag[i] | tmpl; + ZigZag[i] = ZigZag[i]>>4; + half_byte=0; + } + } + + } // end of Num8_Flag=0 + } // end of without Zero Table + else // Has Zero Table + { + + // calculate Z-Table length + ZT_length = ZigZag_length/8; + tmp = ZigZag_length%8; + if(tmp> 0) + { + ZT_length = ZT_length + 1; + } + + // read in Zero Table + for(j=0; j>4; + + // Decode ZiaZag + idx=0; + ZTable[idx]=ZTable[idx]<<1; + count=7; + + if(Num8_Flag) // 8 Bits and has zero table + { + + for( i=1; i>8; + } + + ZTable[idx]=ZTable[idx]<<1; + count--; + if(count==0) + { + count=8; + idx++; + + } + } + + }// end of 8 bits with zero table + else // 12bits has zero table + { + + + half_byte=0; + for( i=1; i>4; + half_byte=1; + } + else + { + ZigZag[i] = lpTMPBUF[index++]; + ZigZag[i] = ZigZag[i]<<8; + tmpl = tmpl & 0x00f0; + ZigZag[i] = ZigZag[i] | tmpl; + ZigZag[i] = ZigZag[i]>>4; + half_byte=0; + } + } + + ZTable[idx]=ZTable[idx]<<1; + count--; + if(count==0) + { + count=8; + idx++; + + } + } + + }// end of 12 bits with zero table + + } +#endif + + +#if 0 + //DeZigZag + idx=0; + for(i=0; i<8; i++) + { + for(j=0; j<8; j++) + { + + DeZigZag[idx] = ZigZag[ZZTB[idx]]; + idx++; + } + } +#endif +#if 1 + for(j=0; j<64; j++) + { + + DeZigZag[j] = 0; + } + + if(YUVFlag == 1) + { + DeZigZag[0] = ZigZag[0]; + DeZigZag[1] = ZigZag[1]<<1; + DeZigZag[2] = ZigZag[5]<<1; + DeZigZag[3] = ZigZag[6]<<2; + + DeZigZag[8] = ZigZag[2]<<1; + DeZigZag[9] = ZigZag[4]<<1; + DeZigZag[10] = ZigZag[7]<<1; + DeZigZag[11] = ZigZag[13]<<2; + + DeZigZag[16] = ZigZag[3]<<1; + DeZigZag[17] = ZigZag[8]<<1; + DeZigZag[18] = ZigZag[12]<<2; + DeZigZag[19] = ZigZag[17]<<2; + + DeZigZag[24] = ZigZag[9]<<2; + DeZigZag[25] = ZigZag[11]<<2; + DeZigZag[26] = ZigZag[18]<<2; + DeZigZag[27] = ZigZag[24]<<3; + } + else + { + DeZigZag[0] = ZigZag[0]; + DeZigZag[1] = ZigZag[1]<<2; + DeZigZag[2] = ZigZag[5]<<2; + DeZigZag[3] = ZigZag[6]<<3; + + DeZigZag[8] = ZigZag[2]<<2; + DeZigZag[9] = ZigZag[4]<<2; + DeZigZag[10] = ZigZag[7]<<2; + DeZigZag[11] = ZigZag[13]<<4; + + DeZigZag[16] = ZigZag[3]<<2; + DeZigZag[17] = ZigZag[8]<<2; + DeZigZag[18] = ZigZag[12]<<3; + DeZigZag[19] = ZigZag[17]<<4; + + DeZigZag[24] = ZigZag[9]<<3; + DeZigZag[25] = ZigZag[11]<<4; + DeZigZag[26] = ZigZag[18]<<4; + DeZigZag[27] = ZigZag[24]<<4; + } +#endif + +#if 0 + // Dequante + if(YUVFlag == 1) + { + // Dequante Y + for(i=0; i<64; i++) + { + ZigZag[i] = DeZigZag[i]<>15; + + tmp=tmp1-tmp2-tmp3; + temp[56] = tmp>>15; + + tmp1=a*DeZigZag[8]+c*DeZigZag[10]; + tmp2=b*DeZigZag[9]; + tmp3=d*DeZigZag[11]; + + tmp=tmp1+tmp2+tmp3; + temp[1] = tmp>>15; + + tmp=tmp1-tmp2-tmp3; + temp[57] = tmp>>15; + + tmp1=a*DeZigZag[16]+c*DeZigZag[18]; + tmp2=b*DeZigZag[17]; + tmp3=d*DeZigZag[19]; + + tmp=tmp1+tmp2+tmp3; + temp[2] = tmp>>15; + + tmp=tmp1-tmp2-tmp3; + temp[58] = tmp>>15; + + tmp1=a*DeZigZag[24]+c*DeZigZag[26]; + tmp2=b*DeZigZag[25]; + tmp3=d*DeZigZag[27]; + + tmp=tmp1+tmp2+tmp3; + temp[3] = tmp>>15; + + tmp=tmp1-tmp2-tmp3; + temp[59] = tmp>>15; + +// for(j=0; j<4; j++) +// { + tmp1=a*DeZigZag[0]+f*DeZigZag[2]; + tmp2=d*DeZigZag[1]; + tmp3=g*DeZigZag[3]; + + tmp=tmp1+tmp2-tmp3; + temp[8] = tmp>>15; + + tmp=tmp1-tmp2+tmp3; + temp[48] = tmp>>15; + + tmp1=a*DeZigZag[8]+f*DeZigZag[10]; + tmp2=d*DeZigZag[9]; + tmp3=g*DeZigZag[11]; + + tmp=tmp1+tmp2-tmp3; + temp[9] = tmp>>15; + + tmp=tmp1-tmp2+tmp3; + temp[49] = tmp>>15; + + tmp1=a*DeZigZag[16]+f*DeZigZag[18]; + tmp2=d*DeZigZag[17]; + tmp3=g*DeZigZag[19]; + + tmp=tmp1+tmp2-tmp3; + temp[10] = tmp>>15; + + tmp=tmp1-tmp2+tmp3; + temp[50] = tmp>>15; + + tmp1=a*DeZigZag[24]+f*DeZigZag[26]; + tmp2=d*DeZigZag[25]; + tmp3=g*DeZigZag[27]; + + tmp=tmp1+tmp2-tmp3; + temp[11] = tmp>>15; + + tmp=tmp1-tmp2+tmp3; + temp[51] = tmp>>15; +// } + +// for(j=0; j<4; j++) +// { + tmp1=a*DeZigZag[0]-f*DeZigZag[2]; + tmp2=e*DeZigZag[1]; + tmp3=b*DeZigZag[3]; + + tmp=tmp1+tmp2-tmp3; + temp[16] = tmp>>15; + + tmp=tmp1-tmp2+tmp3; + temp[40] = tmp>>15; + + tmp1=a*DeZigZag[8]-f*DeZigZag[10]; + tmp2=e*DeZigZag[9]; + tmp3=b*DeZigZag[11]; + + tmp=tmp1+tmp2-tmp3; + temp[17] = tmp>>15; + + tmp=tmp1-tmp2+tmp3; + temp[41] = tmp>>15; + + tmp1=a*DeZigZag[16]-f*DeZigZag[18]; + tmp2=e*DeZigZag[17]; + tmp3=b*DeZigZag[19]; + + tmp=tmp1+tmp2-tmp3; + temp[18] = tmp>>15; + + tmp=tmp1-tmp2+tmp3; + temp[42] = tmp>>15; + + tmp1=a*DeZigZag[24]-f*DeZigZag[26]; + tmp2=e*DeZigZag[25]; + tmp3=b*DeZigZag[27]; + + tmp=tmp1+tmp2-tmp3; + temp[19] = tmp>>15; + + tmp=tmp1-tmp2+tmp3; + temp[43] = tmp>>15; +// } + +// for(j=0; j<4; j++) +// { + tmp1=a*DeZigZag[0]-c*DeZigZag[2]; + tmp2=g*DeZigZag[1]; + tmp3=e*DeZigZag[3]; + + tmp=tmp1+tmp2-tmp3; + temp[24] = tmp>>15; + + tmp=tmp1-tmp2+tmp3; + temp[32] = tmp>>15; + + tmp1=a*DeZigZag[8]-c*DeZigZag[10]; + tmp2=g*DeZigZag[9]; + tmp3=e*DeZigZag[11]; + + tmp=tmp1+tmp2-tmp3; + temp[25] = tmp>>15; + + tmp=tmp1-tmp2+tmp3; + temp[33] = tmp>>15; + + tmp1=a*DeZigZag[16]-c*DeZigZag[18]; + tmp2=g*DeZigZag[17]; + tmp3=e*DeZigZag[19]; + + tmp=tmp1+tmp2-tmp3; + temp[26] = tmp>>15; + + tmp=tmp1-tmp2+tmp3; + temp[34] = tmp>>15; + + tmp1=a*DeZigZag[24]-c*DeZigZag[26]; + tmp2=g*DeZigZag[25]; + tmp3=e*DeZigZag[27]; + + tmp=tmp1+tmp2-tmp3; + temp[27] = tmp>>15; + + tmp=tmp1-tmp2+tmp3; + temp[35] = tmp>>15; +// } + +// for(j=0; j<4; j++) +// { +/* + tmp = 0; + for(k=0; k<4; k++) + { + tmp = tmp + coeft[32+k]*DeZigZag[8*j + k]; + } + temp[32+j] = tmp>>15; +*/ +// } + +// for(j=0; j<4; j++) +// { + +/* tmp=a*DeZigZag[0] - e*DeZigZag[1] - f*DeZigZag[2] + b*DeZigZag[3]; + temp[40] = tmp>>15; + + tmp=a*DeZigZag[8] - e*DeZigZag[9] - f*DeZigZag[10] + b*DeZigZag[11]; + temp[41] = tmp>>15; + + tmp=a*DeZigZag[16] - e*DeZigZag[17] - f*DeZigZag[18] + b*DeZigZag[19]; + temp[42] = tmp>>15; + + tmp=a*DeZigZag[24] - e*DeZigZag[25] - f*DeZigZag[26] + b*DeZigZag[27]; + temp[43] = tmp>>15; +*//* + tmp = 0; + for(k=0; k<4; k++) + { + tmp = tmp + coeft[40 + k]*DeZigZag[8*j + k]; + } + temp[40+j] = tmp>>15; +*/ +// } + +// for(j=0; j<4; j++) +// { + +/* tmp=a*DeZigZag[0] - d*DeZigZag[1]+ f*DeZigZag[2] + g*DeZigZag[3]; + temp[48] = tmp>>15; + + tmp=a*DeZigZag[8] - d*DeZigZag[9] + f*DeZigZag[10] + g*DeZigZag[11]; + temp[49] = tmp>>15; + + tmp=a*DeZigZag[16] - d*DeZigZag[17] + f*DeZigZag[18] + g*DeZigZag[19]; + temp[50] = tmp>>15; + + tmp=a*DeZigZag[24] - d*DeZigZag[25] + f*DeZigZag[26] + g*DeZigZag[27]; + temp[51] = tmp>>15; +*/ +// } + +// for(j=0; j<4; j++) +// { + +/* + tmp = a*DeZigZag[0] - b*DeZigZag[1]+ c*DeZigZag[2] - d*DeZigZag[3]; + temp[56] = tmp>>15; + + tmp = a*DeZigZag[8] - b*DeZigZag[9]+ c*DeZigZag[10] - d*DeZigZag[11]; + temp[57] = tmp>>15; + + tmp = a*DeZigZag[16] - b*DeZigZag[17]+ c*DeZigZag[18] - d*DeZigZag[19]; + temp[58] = tmp>>15; + + tmp = a*DeZigZag[24] - b*DeZigZag[25]+ c*DeZigZag[26] - d*DeZigZag[27]; + temp[59] = tmp>>15; +*/ + +// } + + +// } + +#endif + // ============= + // == IDCT 2D == + // ============= +#if 1 +// for(i=0; i<8; i++) +// { + IPTRR_YUV = IPTRR; + +// i=0 +// for(j=0; j<8; j++) +// { + // j=0 + tmp = temp[0]*a+temp[1]*b+temp[2]*c+temp[3]*d; + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[0+IPTRR] = (BYTE) tmp; + + // j=1 + tmp = temp[0]*a+temp[1]*d+temp[2]*f+temp[3]*(-g); + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[1+IPTRR] = (BYTE) tmp; + + // j=2 + tmp = temp[0]*a+temp[1]*e+temp[2]*(-f)+temp[3]*(-b); + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[2+IPTRR] = (BYTE) tmp; + + // j=3 + tmp = temp[0]*a+temp[1]*g+temp[2]*(-c)+temp[3]*(-e); + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[3+IPTRR] = (BYTE) tmp; + + // j=4 + tmp = temp[0]*a+temp[1]*(-g)+temp[2]*(-c)+temp[3]*e; + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[4+IPTRR] = (BYTE) tmp; + + // j=5 + tmp = temp[0]*a+temp[1]*(-e)+temp[2]*(-f)+temp[3]*b; + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[5+IPTRR] = (BYTE) tmp; + + // j=6 + tmp = temp[0]*a+temp[1]*(-d)+temp[2]*f+temp[3]*g; + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[6+IPTRR] = (BYTE) tmp; + + // j=7 + tmp = temp[0]*a+temp[1]*(-b)+temp[2]*c+temp[3]*(-d); + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[7+IPTRR] = (BYTE) tmp; +// } + +// i=1 +// for(j=0; j<8; j++) +// { + IPTRR_YUV = X_WIDE+IPTRR_YUV; + // j=0 + tmp = temp[8]*a+temp[9]*b+temp[10]*c+temp[11]*d; + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[IPTRR_YUV] = (BYTE) tmp; + + // j=1 + tmp = temp[8]*a+temp[9]*d+temp[10]*f+temp[11]*(-g); + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[IPTRR_YUV+1] = (BYTE) tmp; + + // j=2 + tmp = temp[8]*a+temp[9]*e+temp[10]*(-f)+temp[11]*(-b); + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[IPTRR_YUV+2] = (BYTE) tmp; + + // j=3 + tmp = temp[8]*a+temp[9]*g+temp[10]*(-c)+temp[11]*(-e); + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[IPTRR_YUV+3] = (BYTE) tmp; + + // j=4 + tmp = temp[8]*a+temp[9]*(-g)+temp[10]*(-c)+temp[11]*e; + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[IPTRR_YUV+4] = (BYTE) tmp; + + // j=5 + tmp = temp[8]*a+temp[9]*(-e)+temp[10]*(-f)+temp[11]*b; + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[IPTRR_YUV+5] = (BYTE) tmp; + + // j=6 + tmp = temp[8]*a+temp[9]*(-d)+temp[10]*f+temp[11]*g; + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[IPTRR_YUV+6] = (BYTE) tmp; + + // j=7 + tmp = temp[8]*a+temp[9]*(-b)+temp[10]*c+temp[11]*(-d); + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[IPTRR_YUV+7] = (BYTE) tmp; +// } + + IPTRR_YUV = X_WIDE+IPTRR_YUV; + +// i=2 +// for(j=0; j<8; j++) +// { + // j=0 + tmp = temp[16]*a+temp[17]*b+temp[18]*c+temp[19]*d; + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[IPTRR_YUV] = (BYTE) tmp; + + // j=1 + tmp = temp[16]*a+temp[17]*d+temp[18]*f+temp[19]*(-g); + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[IPTRR_YUV+1] = (BYTE) tmp; + + // j=2 + tmp = temp[16]*a+temp[17]*e+temp[18]*(-f)+temp[19]*(-b); + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[IPTRR_YUV+2] = (BYTE) tmp; + + // j=3 + tmp = temp[16]*a+temp[17]*g+temp[18]*(-c)+temp[19]*(-e); + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[IPTRR_YUV+3] = (BYTE) tmp; + + // j=4 + tmp = temp[16]*a+temp[17]*(-g)+temp[18]*(-c)+temp[19]*e; + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[IPTRR_YUV+4] = (BYTE) tmp; + + // j=5 + tmp = temp[16]*a+temp[17]*(-e)+temp[18]*(-f)+temp[19]*b; + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[IPTRR_YUV+5] = (BYTE) tmp; + + // j=6 + tmp = temp[16]*a+temp[17]*(-d)+temp[18]*f+temp[19]*g; + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[IPTRR_YUV+6] = (BYTE) tmp; + + // j=7 + tmp = temp[16]*a+temp[17]*(-b)+temp[18]*c+temp[19]*(-d); + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[IPTRR_YUV+7] = (BYTE) tmp; +// } + IPTRR_YUV = X_WIDE+IPTRR_YUV; +// i=3 +// for(j=0; j<8; j++) +// { + // j=0 + tmp = temp[24]*a+temp[25]*b+temp[26]*c+temp[27]*d; + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[IPTRR_YUV] = (BYTE) tmp; + + // j=1 + tmp = temp[24]*a+temp[25]*d+temp[26]*f+temp[27]*(-g); + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[IPTRR_YUV+1] = (BYTE) tmp; + + // j=2 + tmp = temp[24]*a+temp[25]*e+temp[26]*(-f)+temp[27]*(-b); + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[IPTRR_YUV+2] = (BYTE) tmp; + + // j=3 + tmp = temp[24]*a+temp[25]*g+temp[26]*(-c)+temp[27]*(-e); + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[IPTRR_YUV+3] = (BYTE) tmp; + + // j=4 + tmp = temp[24]*a+temp[25]*(-g)+temp[26]*(-c)+temp[27]*e; + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[IPTRR_YUV+4] = (BYTE) tmp; + + // j=5 + tmp = temp[24]*a+temp[25]*(-e)+temp[26]*(-f)+temp[27]*b; + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[IPTRR_YUV+5] = (BYTE) tmp; + + // j=6 + tmp = temp[24]*a+temp[25]*(-d)+temp[26]*f+temp[27]*g; + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[IPTRR_YUV+6] = (BYTE) tmp; + + // j=7 + tmp = temp[24]*a+temp[25]*(-b)+temp[26]*c+temp[27]*(-d); + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[IPTRR_YUV+7] = (BYTE) tmp; +// } + IPTRR_YUV = X_WIDE+IPTRR_YUV; +// i=4 +// for(j=0; j<8; j++) +// { + // j=0 + tmp = temp[32]*a+temp[33]*b+temp[34]*c+temp[35]*d; + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[IPTRR_YUV] = (BYTE) tmp; + + // j=1 + tmp = temp[32]*a+temp[33]*d+temp[34]*f+temp[35]*(-g); + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[IPTRR_YUV+1] = (BYTE) tmp; + + // j=2 + tmp = temp[32]*a+temp[33]*e+temp[34]*(-f)+temp[35]*(-b); + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[IPTRR_YUV+2] = (BYTE) tmp; + + // j=3 + tmp = temp[32]*a+temp[33]*g+temp[34]*(-c)+temp[35]*(-e); + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[IPTRR_YUV+3] = (BYTE) tmp; + + // j=4 + tmp = temp[32]*a+temp[33]*(-g)+temp[34]*(-c)+temp[35]*e; + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[IPTRR_YUV+4] = (BYTE) tmp; + + // j=5 + tmp = temp[32]*a+temp[33]*(-e)+temp[34]*(-f)+temp[35]*b; + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[IPTRR_YUV+5] = (BYTE) tmp; + + // j=6 + tmp = temp[32]*a+temp[33]*(-d)+temp[34]*f+temp[35]*g; + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[IPTRR_YUV+6] = (BYTE) tmp; + + // j=7 + tmp = temp[32]*a+temp[33]*(-b)+temp[34]*c+temp[35]*(-d); + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[IPTRR_YUV+7] = (BYTE) tmp; +// } + IPTRR_YUV = X_WIDE+IPTRR_YUV; +// i=5 +// for(j=0; j<8; j++) +// { + // j=0 + tmp = temp[40]*a+temp[41]*b+temp[42]*c+temp[43]*d; + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[IPTRR_YUV] = (BYTE) tmp; + + // j=1 + tmp = temp[40]*a+temp[41]*d+temp[42]*f+temp[43]*(-g); + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[IPTRR_YUV+1] = (BYTE) tmp; + + // j=2 + tmp = temp[40]*a+temp[41]*e+temp[42]*(-f)+temp[43]*(-b); + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[IPTRR_YUV+2] = (BYTE) tmp; + + // j=3 + tmp = temp[40]*a+temp[41]*g+temp[42]*(-c)+temp[43]*(-e); + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[IPTRR_YUV+3] = (BYTE) tmp; + + // j=4 + tmp = temp[40]*a+temp[41]*(-g)+temp[42]*(-c)+temp[43]*e; + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[IPTRR_YUV+4] = (BYTE) tmp; + + // j=5 + tmp = temp[40]*a+temp[41]*(-e)+temp[42]*(-f)+temp[43]*b; + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[IPTRR_YUV+5] = (BYTE) tmp; + + // j=6 + tmp = temp[40]*a+temp[41]*(-d)+temp[42]*f+temp[43]*g; + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[IPTRR_YUV+6] = (BYTE) tmp; + + // j=7 + tmp = temp[40]*a+temp[41]*(-b)+temp[42]*c+temp[43]*(-d); + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[IPTRR_YUV+7] = (BYTE) tmp; + +// } + IPTRR_YUV = X_WIDE+IPTRR_YUV; +// i=6 +// for(j=0; j<8; j++) +// { + tmp1=temp[48]*a+temp[50]*c; + tmp2=temp[49]*b; + tmp3=temp[51]*d; + // j=0 + tmp=tmp1+tmp2+tmp3; + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[IPTRR_YUV] = (BYTE) tmp; + + // j=7 + tmp=tmp1-tmp2-tmp3; + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[IPTRR_YUV+7] = (BYTE) tmp; + + tmp1=temp[48]*a+temp[50]*f; + tmp2=temp[49]*d; + tmp3=temp[51]*g; + // j=1 + tmp=tmp1+tmp2-tmp3; + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[IPTRR_YUV+1] = (BYTE) tmp; + + // j=6 + tmp=tmp1-tmp2+tmp3; + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[IPTRR_YUV+6] = (BYTE) tmp; + + tmp1=temp[48]*a-temp[50]*f; + tmp2=temp[49]*e; + tmp3=temp[51]*b; + // j=2 + tmp=tmp1+tmp2-tmp3; + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[IPTRR_YUV+2] = (BYTE) tmp; + + // j=5 + tmp=tmp1-tmp2+tmp3; + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[IPTRR_YUV+5] = (BYTE) tmp; + + tmp1=temp[48]*a-temp[50]*c; + tmp2=temp[49]*g; + tmp3=temp[51]*e; + // j=3 + tmp=tmp1+tmp2-tmp3; + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[IPTRR_YUV+3] = (BYTE) tmp; + + // j=4 + tmp=tmp1-tmp2+tmp3; + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[IPTRR_YUV+4] = (BYTE) tmp; +// } + IPTRR_YUV = X_WIDE+IPTRR_YUV; +// i=7 +// for(j=0; j<8; j++) +// { + tmp1=temp[56]*a+temp[58]*c; + tmp2=temp[57]*b; + tmp3=temp[59]*d; + + // j=0 + tmp=tmp1+tmp2+tmp3; + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[IPTRR_YUV] = (BYTE) tmp; + + // j=7 + tmp=tmp1-tmp2-tmp3; + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[IPTRR_YUV+7] = (BYTE) tmp; + + tmp1=temp[56]*a+temp[58]*f; + tmp2=temp[57]*d; + tmp3=temp[59]*g; + + // j=1 + tmp=tmp1+tmp2-tmp3; + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[IPTRR_YUV+1] = (BYTE) tmp; + + // j=6 + tmp=tmp1-tmp2+tmp3; + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[IPTRR_YUV+6] = (BYTE) tmp; + + tmp1=temp[56]*a-temp[58]*f; + tmp2=temp[57]*e; + tmp3=temp[59]*b; + + // j=2 + tmp=tmp1+tmp2-tmp3; + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[IPTRR_YUV+2] = (BYTE) tmp; + + // j=5 + tmp=tmp1-tmp2+tmp3; + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[IPTRR_YUV+5] = (BYTE) tmp; + + tmp1=temp[56]*a-temp[58]*c; + tmp2=temp[57]*g; + tmp3=temp[59]*e; + + // j=3 + tmp=tmp1+tmp2-tmp3; + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[IPTRR_YUV+3] = (BYTE) tmp; + + // j=4 + tmp=tmp1-tmp2+tmp3; + tmp = tmp>>15; + tmp=tmp+128; + if(tmp >255) tmp=255; + if(tmp < 0 ) tmp=0; + lpDestBuf[IPTRR_YUV+4] = (BYTE) tmp; + +// } + + +// } + +#endif + frame->TempInfo.index=index; + frame->TempInfo.IPTRR_U=IPTRR+8; + + return 0; +} + +static NTSTATUS +Decompress420HiNoMMX( + PUCHAR pY, + ULONG FrameLength, + ULONG X_SIZE, + ULONG Y_SIZE, + PUCHAR TableBuffer, + PUCHAR TableUVBuffer, + PSHORT CoefBuffer, + PULONG ZigZagTable, + PUCHAR RawFrameBuffer, + ULONG ProcessedDataLength, + PUCHAR TempBuffer, + struct ov511_frame *frame) + +{ + +// Begin of decoder definition +//POVTCAM_DEVICE_CONTEXT deviceContext = DeviceContext; +LPBYTE lpYUV, lpU, lpV, lpTMPBUF; +ULONG PTRX, PTRY; +// ULONG i; +ULONG IPTRR, IPTRR_U, IPTRR_V; +//ULONG ipm, tms, Tcnt=1600; +ULONG index; +ULONG SrcUVDataLength; +//ULONG SrcYDataLength; +//short a,b,c,d,e,mid; + +PUCHAR TempBuffU = NULL; +PUCHAR TempBuffV = NULL; +PUCHAR TempBuffY = NULL; + +DWORDLONG Y_Scalor=0x7fff7fff7fff7fff; + +// u=0.564 +DWORDLONG U_Scalor=0x4831483148314831; +// v=0.713 +DWORDLONG V_Scalor=0x5b435b435b435b43; + +//DWORDLONG U_Scalor=0x7fff7fff7fff7fff; +//DWORDLONG V_Scalor=0x7fff7fff7fff7fff; +//DWORDLONG CONST128=0x8080808080808080; +//DWORDLONG CONST128=0x0080008000800080; /* Previously disabled by OVT */ +//DWORDLONG Const4000=0x4000400040004000; + + +// TempBuffer = deviceContext->TempBuffer; +// TableBuffer = deviceContext->TableBuffer; //TempBuffer; +// CoefBuffer = deviceContext->CoefBuffer; //TempBuffer; + // ZigZagTable = deviceContext->ZigZagTable; + lpTMPBUF = RawFrameBuffer; // + 9; //TempBuffer; + +//ProcessedDataLength = deviceContext->ProcessedDataLength; +//ProcessedDataLength = ProcessedDataLength/8; +//ProcessedDataLength = ProcessedDataLength*8 + 8; + +SrcUVDataLength = X_SIZE * Y_SIZE/4; +//SrcYDataLength = X_SIZE * Y_SIZE; + +TempBuffU= TempBuffer; +TempBuffV= TempBuffU + SrcUVDataLength; +TempBuffY= TempBuffV + SrcUVDataLength; + + lpYUV = pY; + lpU = lpYUV+X_SIZE*Y_SIZE; + lpV = lpU + X_SIZE*Y_SIZE/4; + +index=0; +// ===== 320x240 images + for (PTRY=0; PTRY>2; +IPTRR_V=IPTRR_U; +if(index > ProcessedDataLength) +return STATUS_UNSUCCESSFUL; + for (PTRX=0; PTRXTempInfo.index; +IPTRR_U=frame->TempInfo.IPTRR_U; + +//DecompressYHINoMMX(lpV, lpTMPBUF, index, IPTRR_V, X_SIZE/2, TableBuffer+64, CoefBuffer, ZigZagTable, V_Scalor, 2, frame); +DecompressYHINoMMX(lpV, lpTMPBUF, index, IPTRR_V, X_SIZE/2, TableUVBuffer, CoefBuffer, ZigZagTable, V_Scalor, 2, frame); +index=frame->TempInfo.index; +IPTRR_V=frame->TempInfo.IPTRR_U; + +DecompressYHINoMMX(lpYUV, lpTMPBUF, index, IPTRR, X_SIZE, TableBuffer, CoefBuffer, ZigZagTable, Y_Scalor, 1, frame); +//DecompressYHINoMMX(TempBuffY, lpTMPBUF, index, IPTRR, X_SIZE, TableBuffer, CoefBuffer, ZigZagTable, Y_Scalor, 1, frame); +index=frame->TempInfo.index; +IPTRR=frame->TempInfo.IPTRR_U; + +DecompressYHINoMMX(lpYUV, lpTMPBUF, index, IPTRR, X_SIZE, TableBuffer, CoefBuffer, ZigZagTable, Y_Scalor, 1, frame); +//DecompressYHINoMMX(TempBuffY, lpTMPBUF, index, IPTRR, X_SIZE, TableBuffer, CoefBuffer, ZigZagTable, Y_Scalor, 1, frame); +index=frame->TempInfo.index; +IPTRR=frame->TempInfo.IPTRR_U; + +DecompressYHINoMMX(lpYUV, lpTMPBUF, index, IPTRR, X_SIZE, TableBuffer, CoefBuffer, ZigZagTable, Y_Scalor, 1, frame); +//DecompressYHINoMMX(TempBuffY, lpTMPBUF, index, IPTRR, X_SIZE, TableBuffer, CoefBuffer, ZigZagTable, Y_Scalor, 1, frame); +index=frame->TempInfo.index; +IPTRR=frame->TempInfo.IPTRR_U; + +DecompressYHINoMMX(lpYUV, lpTMPBUF, index, IPTRR, X_SIZE, TableBuffer, CoefBuffer, ZigZagTable, Y_Scalor, 1, frame); +//DecompressYHINoMMX(TempBuffY, lpTMPBUF, index, IPTRR, X_SIZE, TableBuffer, CoefBuffer, ZigZagTable, Y_Scalor, 1, frame); +index=frame->TempInfo.index; +IPTRR=frame->TempInfo.IPTRR_U; + } //Stop X + + +IPTRR=X_SIZE*(PTRY+8); +IPTRR_V=(X_SIZE*PTRY + X_SIZE); // /4; +IPTRR_V=IPTRR_V>>2; +IPTRR_U=IPTRR_V; + + for (PTRX=0; PTRXTempInfo.index; +IPTRR_U=frame->TempInfo.IPTRR_U; + +//DecompressYHINoMMX(lpV, lpTMPBUF, index, IPTRR_V, X_SIZE/2, TableBuffer+64, CoefBuffer, ZigZagTable, V_Scalor, 2, frame); +DecompressYHINoMMX(lpV, lpTMPBUF, index, IPTRR_V, X_SIZE/2, TableUVBuffer, CoefBuffer, ZigZagTable, V_Scalor, 2, frame); +index=frame->TempInfo.index; +IPTRR_V=frame->TempInfo.IPTRR_U; + +DecompressYHINoMMX(lpYUV, lpTMPBUF, index, IPTRR, X_SIZE, TableBuffer, CoefBuffer, ZigZagTable, Y_Scalor, 1, frame); +//DecompressYHINoMMX(TempBuffY, lpTMPBUF, index, IPTRR, X_SIZE, TableBuffer, CoefBuffer, ZigZagTable, Y_Scalor, 1, frame); +index=frame->TempInfo.index; +IPTRR=frame->TempInfo.IPTRR_U; + +DecompressYHINoMMX(lpYUV, lpTMPBUF, index, IPTRR, X_SIZE, TableBuffer, CoefBuffer, ZigZagTable, Y_Scalor, 1, frame); +//DecompressYHINoMMX(TempBuffY, lpTMPBUF, index, IPTRR, X_SIZE, TableBuffer, CoefBuffer, ZigZagTable, Y_Scalor, 1, frame); +index=frame->TempInfo.index; +IPTRR=frame->TempInfo.IPTRR_U; + +DecompressYHINoMMX(lpYUV, lpTMPBUF, index, IPTRR, X_SIZE, TableBuffer, CoefBuffer, ZigZagTable, Y_Scalor, 1, frame); +//DecompressYHINoMMX(TempBuffY, lpTMPBUF, index, IPTRR, X_SIZE, TableBuffer, CoefBuffer, ZigZagTable, Y_Scalor, 1, frame); +index=frame->TempInfo.index; +IPTRR=frame->TempInfo.IPTRR_U; + +DecompressYHINoMMX(lpYUV, lpTMPBUF, index, IPTRR, X_SIZE, TableBuffer, CoefBuffer, ZigZagTable, Y_Scalor, 1, frame); +//DecompressYHINoMMX(TempBuffY, lpTMPBUF, index, IPTRR, X_SIZE, TableBuffer, CoefBuffer, ZigZagTable, Y_Scalor, 1, frame); +index=frame->TempInfo.index; +IPTRR=frame->TempInfo.IPTRR_U; + } //Stop X +}// stop Y + + + return 0; +} + + + + +/* Input (RawFrameBuffer) format is raw isoc. data (with header and packet + * nunber stripped). + * Output (FrameBuffer) format is planar YUV420 + * Returns uncompressed data length if success, or zero if error + */ +static ULONG +Decompress420( ULONG SrcWidth, + ULONG SrcHeight, + PVOID FrameBuffer, + ULONG FrameLength, + PVOID RawFrameBuffer, + ULONG RawFrameLength, + ULONG ActualRawFrameLength, + struct ov511_frame *frame) +{ + ULONG dwLength = SrcWidth * SrcHeight; + PUCHAR pBuffer; +// PUCHAR pOutBuffer; + PUCHAR pYQuanTable; + PUCHAR pUVQuanTable; + PSHORT pIDCTTable; + PULONG pZigAzgTable; + NTSTATUS ntStatus = STATUS_SUCCESS; +// UCHAR YTable[QUANTABLESIZE], UVTable[QUANTABLESIZE]; +// UCHAR Temp; +// ULONG i; + + PDEBUG(4, "w=%lu h=%lu fb=%p fblen=%lu rawfb=%p rawlen=%lu, actlen=%lu", + SrcWidth, SrcHeight, FrameBuffer, FrameLength, RawFrameBuffer, + RawFrameLength, ActualRawFrameLength); + +//#ifdef OV518 +// pgFrameContext = m_pCurrentFrameContext; +//// OVTCAM_KdPrint(MIN_TRACE, ("OVTCAM: YUV=420 \n")); +//#endif + if(frame->m_pTempBuffer && frame->m_TempBufferLength < dwLength) + { + FREE(frame->m_pTempBuffer); + frame->m_pTempBuffer = NULL; + frame->m_TempBufferLength = 0; + } + if(frame->m_pTempBuffer == NULL) + { + frame->m_pTempBuffer = ALLOCATE(NonPagedPool, dwLength,0); + frame->m_TempBufferLength = dwLength ; + } + if(frame->m_pTempBuffer == NULL) + return 0; + + pBuffer = (PUCHAR)frame->m_pTempBuffer; +// pOutBuffer = (PUCHAR)FrameBuffer; + pYQuanTable = GetYQuanTable(); + pUVQuanTable = GetUVQuanTable(); + pIDCTTable = GetIDCTTable(); + pZigAzgTable = GetZigZagTable(); + +//#if 0 +// //def OV518 +// for(int i = 0; i < QUANTABLESIZE; i++) +// { +// Temp = *pYQuanTable++; +// if(Temp > 4) YTable[i] = 3; +// else YTable[i] = Temp - 2; +// +// Temp = *pUVQuanTable++; +// if(Temp > 4) UVTable[i] = 3; +// else UVTable[i] = Temp - 2; +// } +//#endif + +// if(m_bSupportMMX) +// { +//#ifdef OV518 +// +//#ifdef TABLE_DBG +// ntStatus = Decompress420OV518NoMMXTABLE((PUCHAR) FrameBuffer, FrameLength, SrcWidth, SrcHeight, (PUCHAR)pYQuanTable, (PUCHAR)pUVQuanTable,pIDCTTable,pZigAzgTable,(PUCHAR) RawFrameBuffer,ActualRawFrameLength,pBuffer); +//#else +// ntStatus = Decompress420OV518((PUCHAR) FrameBuffer, FrameLength, SrcWidth, SrcHeight, (PUCHAR)YTable, (PUCHAR)UVTable,pIDCTTable,pZigAzgTable,(PUCHAR) RawFrameBuffer,ActualRawFrameLength,pBuffer); +//#endif +// +//#else +// ntStatus = Decompress420Hi((PUCHAR) FrameBuffer, FrameLength, SrcWidth, SrcHeight, pYQuanTable, pUVQuanTable,pIDCTTable,pZigAzgTable,(PUCHAR) RawFrameBuffer,ActualRawFrameLength,pBuffer); +//#endif +// } +// else +// { + +//#ifdef OV518 +//#ifdef TABLE_DBG +// ntStatus = Decompress420OV518NoMMXTABLE((PUCHAR) FrameBuffer, FrameLength, SrcWidth, SrcHeight, (PUCHAR)pYQuanTable, (PUCHAR)pUVQuanTable,pIDCTTable,pZigAzgTable,(PUCHAR) RawFrameBuffer,ActualRawFrameLength,pBuffer); +//#else +// ntStatus = Decompress420OV518NoMMX((PUCHAR) FrameBuffer, FrameLength, SrcWidth, SrcHeight, (PUCHAR)YTable, (PUCHAR)UVTable,pIDCTTable,pZigAzgTable,(PUCHAR) RawFrameBuffer,ActualRawFrameLength,pBuffer); +//#endif +//#else + ntStatus = Decompress420HiNoMMX((PUCHAR) FrameBuffer, FrameLength, SrcWidth, SrcHeight, pYQuanTable, pUVQuanTable,pIDCTTable,pZigAzgTable,(PUCHAR) RawFrameBuffer,ActualRawFrameLength,pBuffer,frame); +//#endif + +// Disabled by Mark +// //for color saturation 8/22/00 +// { +// LONG u , v; +// PUCHAR pU = (PUCHAR) FrameBuffer + dwLength; +// PUCHAR pV = pU + dwLength/4; +// for(ULONG i = 0; i < dwLength/4; i++) +// { +// u = (LONG)*pU; +// v = (LONG)*pV; +// u -= 128; +// v -= 128; +// u = (u * 577) >> 10; +// v = (v * 730) >> 10; +// u += 128; +// v += 128; +// *pU++ = (UCHAR)u; +// *pV++ = (UCHAR)v; +//// u=0.564 +//// v=0.713 +// } +// } + + +// } + + // Added by Steven 11/06/2000 + if(ntStatus) return 0; + +// if(m_EnColorCorrect) +// { +// ColorCorrect420((PUCHAR) FrameBuffer, FrameLength, SrcWidth, SrcHeight); +// } + +// OVTCAM_CopyMemory(pOutBuffer, RawFrameBuffer, dwLength * 3 / 2); +// if(!NT_SUCCESS(ntStatus)) +// return 0; + return dwLength * 3 / 2; +} +#endif + +static void ov511_decompress(struct ov511_frame *frame) +{ + unsigned long rc; + +// err("Not implemented yet (but given %d bytes)", frame->bytes_recvd); + +#ifdef ENABLE_OV511_COMPRESSION + + PDEBUG(4, "Decompressing %d bytes", frame->bytes_recvd); + + rc = Decompress420( frame->width, + frame->height, + frame->data, /* Output buffer */ + MAX_DATA_SIZE, /* Output buffer len */ + frame->rawdata, /* Raw buffer */ + MAX_RAW_DATA_SIZE, /* Raw buffer len */ + frame->bytes_recvd, /* Actual raw len */ + frame); + + PDEBUG(4, "DEBUG: Decompress420 returned %ld", rc); + +#else + +// Enabling this screws up the preprocessor (cpp-2.96-69) +// warn("Compression was not compiled in!"); +// warn("You must load driver with compress=0 or you will get no image!); + +#endif + +} + +/* Converts frame->data from planar YUV420 to RGB24. This is currently only + * useful for postprocessing decompressed data, but will eventually replace + * ov511_parse_yuv420_to_rgb(). + * + * NOTE: Uses frame->rawdata as a scratch buffer + */ +static void ov511_yuv420p_to_rgb(struct ov511_frame *frame, int bits) +{ + const int numpix = frame->width * frame->height; + const int bytes = bits >> 3; + int i, j, y00, y01, y10, y11, u, v; + unsigned char *pY = frame->rawdata; + unsigned char *pU = pY + numpix; + unsigned char *pV = pU + numpix / 4; + unsigned char *pOut = frame->data; + + /* Move data to a safe place */ + memmove(frame->rawdata, frame->data, MAX_RAW_DATA_SIZE); + +// /* Clear buffer for testing purposes */ +// memset(frame->data, 0, MAX_DATA_SIZE); + + for (j = 0; j <= frame->height - 2; j += 2) { + for (i = 0; i <= frame->width - 2; i += 2) { + y00 = *pY; + y01 = *(pY + 1); + y10 = *(pY + frame->width); + y11 = *(pY + frame->width + 1); + u = (*pU++) - 128; + v = (*pV++) - 128; - pIn = pIn0 + 64; - pOut = pOut0 + iOutUV + a + a/2; - for (k = 0; k < 8; k++) { - pOut1 = pOut; - for (l = 0; l < 8; l++) { - *pOut1 = *(pOut1 + w) = *pIn++; - pOut1++; - } - pOut += iWidth; - } + ov511_move_420_block(y00, y01, y10, y11, u, v, + frame->width, pOut, bits); + + pY += 2; + pOut += 2 * bytes; - pIn = pIn0 + 128; - pOut = pOut0 + iOutY; - for (k = 0; k < 4; k++) { - pOut1 = pOut; - for (l = 0; l < 8; l++) { - for (m = 0; m < 8; m++) - *pOut1++ =*pIn++; - pOut1 += iWidth - 8; } - pOut += 8; + pY += frame->width; + pOut += frame->width * bytes; } } -/* - * For 640x480 RAW BW images, data shows up in 1200 256 byte segments. - * The segments represent 4 squares of 8x8 pixels as follows: - * - * 0 1 ... 7 64 65 ... 71 ... 192 193 ... 199 - * 8 9 ... 15 72 73 ... 79 200 201 ... 207 - * ... ... ... - * 56 57 ... 63 120 121 ... 127 248 249 ... 255 - * - */ +/* Converts frame->data from planar YUV420 to YUV422 (YUYV). + * NOTE: Uses frame->rawdata as a scratch buffer + */ static void -ov511_parse_data_grey(unsigned char *pIn0, unsigned char *pOut0, - int iOutY, int iWidth) +ov511_yuv420p_to_yuv422(struct ov511_frame *frame) { - int k, l, m; - unsigned char *pIn; - unsigned char *pOut, *pOut1; - - pIn = pIn0; - pOut = pOut0 + iOutY; - for (k = 0; k < 4; k++) { - pOut1 = pOut; - for (l = 0; l < 8; l++) { - for (m = 0; m < 8; m++) { - *pOut1++ = *pIn++; - } - pOut1 += iWidth - 8; + const int numpix = frame->width * frame->height; + int i, j; + unsigned char *pY = frame->rawdata; + unsigned char *pU = pY + numpix; + unsigned char *pV = pU + numpix / 4; + unsigned char *pOut = frame->data; + + /* Move data to a safe place */ + memmove(frame->rawdata, frame->data, MAX_RAW_DATA_SIZE); + + for (i = 0; i < numpix; i++) { + *pOut = *(pY + i); + pOut += 2; + } + + pOut = frame->data + 1; + for (j = 0; j <= frame->height - 2 ; j += 2) { + for (i = 0; i <= frame->width - 2; i += 2) { + int u = *pU++; + int v = *pV++; + + *pOut = u; + *(pOut+2) = v; + *(pOut+frame->width) = u; + *(pOut+frame->width+2) = v; + pOut += 4; } - pOut += 8; + pOut += (frame->width * 2); } } -/* - * fixFrameRGBoffset-- - * My camera seems to return the red channel about 1 pixel - * low, and the blue channel about 1 pixel high. After YUV->RGB - * conversion, we can correct this easily. OSL 2/24/2000. +/* Post-processes the specified frame. This consists of: + * 1. Decompress frame (from frame->rawfbuf into frame->data), if necessary + * 2. Convert frame->data from YUV420P to destination format, if necessary + * 3. Fix the RGB offset, if necessary */ -static void fixFrameRGBoffset(struct ov511_frame *frame) +static void ov511_postprocess(struct ov511_frame *frame) { - int x, y; - int rowBytes = frame->width*3, w = frame->width; - unsigned char *rgb = frame->data; - const int shift = 1; /* Distance to shift pixels by, vertically */ - - /* Don't bother with little images */ - if (frame->width < 400) - return; + if (frame->compressed) + ov511_decompress(frame); - /* Shift red channel up */ - for (y = shift; y < frame->height; y++) { - int lp = (y-shift)*rowBytes; /* Previous line offset */ - int lc = y*rowBytes; /* Current line offset */ - for (x = 0; x < w; x++) - rgb[lp+x*3+2] = rgb[lc+x*3+2]; /* Shift red up */ + switch (frame->format) { + case VIDEO_PALETTE_RGB565: + ov511_yuv420p_to_rgb(frame, 16); + break; + case VIDEO_PALETTE_RGB24: + ov511_yuv420p_to_rgb(frame, 24); + break; + case VIDEO_PALETTE_YUV422: + case VIDEO_PALETTE_YUYV: + ov511_yuv420p_to_yuv422(frame); + break; + case VIDEO_PALETTE_YUV420: + case VIDEO_PALETTE_YUV420P: + break; + default: + err("Cannot convert data to this format"); } - /* Shift blue channel down */ - for (y = frame->height-shift-1; y >= 0; y--) { - int ln = (y + shift) * rowBytes; /* Next line offset */ - int lc = y * rowBytes; /* Current line offset */ - for (x = 0; x < w; x++) - rgb[ln+x*3+0] = rgb[lc+x*3+0]; /* Shift blue down */ - } + if (fix_rgb_offset) + fixFrameRGBoffset(frame); } /********************************************************************** @@ -1623,10 +4633,20 @@ int aPackNum[10]; struct ov511_frame *frame; unsigned char *pData; - int iPix; + int iPix, data_size, pn, num, offset; + struct timeval *ts; 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 +4656,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,44 +4666,118 @@ 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 */ - /* Check for SOF/EOF packet */ - if ((cdata[0] | cdata[1] | cdata[2] | cdata[3] | - cdata[4] | cdata[5] | cdata[6] | cdata[7]) || - (~cdata[8] & 0x08)) - goto check_middle; +#if 0 + { + int d; + /* Print all data */ + for (d = data_size; d < data_size; d += 16) { + info("%4x: %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x", d, + cdata[d], cdata[d+1], cdata[d+2], cdata[d+3], + cdata[d+4], cdata[d+5], cdata[d+6], cdata[d+7], + cdata[d+8], cdata[d+9], cdata[d+10], cdata[d+11], + cdata[d+12], cdata[d+13], cdata[d+14], cdata[d+15]); + } + } +#endif + + if (printph) { + info("packet header: %x %x %x %x %x %x %x %x %x %x %x %x", + cdata[0], cdata[1], cdata[2], cdata[3], cdata[4], cdata[5], + cdata[6], cdata[7], cdata[8], cdata[9], cdata[10], cdata[11]); + } + + if (ov511->bridge == BRG_OV518PLUS) { + int trailer = data_size - 9; + + // Could a packet be both SOF and EOF? + + if (ov511->compress) + trailer -= 64; + + if (printph) { + info("trailer: %x %x %x %x %x %x %x %x", + cdata[trailer], cdata[trailer+1], cdata[trailer+2], cdata[trailer+3], + cdata[trailer+4], cdata[trailer+5], cdata[trailer+6], cdata[trailer+7]); + } + + /* A false positive here is likely, until OVT gives me + * the definitive SOF/EOF format */ + if (!(cdata[0] | cdata[1] | cdata[2] | cdata[3] | + cdata[5]) && cdata[6]) { + goto sof; + } else if (!(cdata[trailer] | cdata[trailer+1] | + cdata[trailer+2] | cdata[trailer+3] | + cdata[trailer+5]) && cdata[trailer+6]) { + goto eof; + } else { + goto check_middle; + } + } else { + /* Check for SOF/EOF packet */ + if ((cdata[0] | cdata[1] | cdata[2] | cdata[3] | + cdata[4] | cdata[5] | cdata[6] | cdata[7]) || + (~cdata[8] & 0x08)) + goto check_middle; + } /* Frame end */ if (cdata[8] & 0x80) { - struct timeval *ts; - +eof: 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; - if (fix_rgb_offset) - fixFrameRGBoffset(frame); - frame->grabstate = FRAME_DONE; + if (!ttpp) { + if (frame->compressed) { + ov511_decompress(frame); + + switch (frame->format) { + case VIDEO_PALETTE_RGB565: + ov511_yuv420p_to_rgb(frame, 16); + break; + case VIDEO_PALETTE_RGB24: + ov511_yuv420p_to_rgb(frame, 24); + break; + case VIDEO_PALETTE_YUV422: + case VIDEO_PALETTE_YUYV: + ov511_yuv420p_to_yuv422(frame); + break; + case VIDEO_PALETTE_YUV420: + case VIDEO_PALETTE_YUV420P: + memmove(frame->data, frame->rawdata, MAX_RAW_DATA_SIZE); + break; + default: + err("Cannot convert decompressed data to this format"); + } + } + + if (fix_rgb_offset) + fixFrameRGBoffset(frame); + } + + frame->grabstate = FRAME_DONE; // FIXME: Is this right? if (waitqueue_active(&frame->wq)) { frame->grabstate = FRAME_DONE; @@ -1711,6 +4806,7 @@ * fixed by carlosf@conectiva.com.br */ } else { +sof: /* Frame start */ PDEBUG(4, "Frame start, framenum = %d", ov511->curframe); @@ -1719,7 +4815,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 +4825,9 @@ frame->scanstate = STATE_LINES; frame->segment = 0; + frame->bytes_recvd = 0; + frame->output_offset = 0; + frame->compressed = cdata[8] & 0x40; } check_middle: @@ -1737,6 +4835,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 (frame->compressed) { + 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,15 +4861,16 @@ 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; + int format; unsigned char *pOut; iSegY = iSegUV = frame->segment; @@ -1767,7 +4885,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); @@ -1792,13 +4910,20 @@ iOutUVP = iUV*HDIV*2*frame->width + jUV*WDIV/2; iOutUV = iOutUVP * (frame->depth >> 3); - switch (frame->format) { + /* If ttpp is set just decode the frame to YUV420P, and + * post-process it at task-time. Else, do it the old way. */ + if (ttpp) + format = VIDEO_PALETTE_YUV420P; + else + format = frame->format; + + switch (format) { case VIDEO_PALETTE_GREY: ov511_parse_data_grey (pData, pOut, iOutY, frame->width); 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, @@ -1816,6 +4941,7 @@ ov511_parse_data_yuv422(pData, pOut, iOutY, iOutUV, frame->width); break; case VIDEO_PALETTE_YUV420: + case VIDEO_PALETTE_YUV420P: ov511_parse_data_yuv420 (pData, pOut, iOutYP, iUV*HDIV*frame->width/2 + jUV*WDIV/4, frame->width, frame->height); break; @@ -1831,8 +4957,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 +4966,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 +4997,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); @@ -1894,35 +5027,47 @@ PDEBUG(3, "*** Initializing capture ***"); - ov511->compress = 0; ov511->curframe = -1; 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 +5198,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 +5217,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 +5255,22 @@ 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; + + /* Allocated by decompressor */ + FREE(ov511->frame[i].m_pTempBuffer); + ov511->frame[i].m_pTempBuffer = NULL; } PDEBUG(4, "buffer memory deallocated"); @@ -2182,6 +5350,9 @@ } ov511->user++; + + if (ov511->led_policy == LED_AUTO) + ov511_led_control(ov511, 1); out: up(&ov511->lock); @@ -2203,6 +5374,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 +5448,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 +5575,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; @@ -2476,7 +5651,6 @@ if (ov511->frame[vm.frame].grabstate == FRAME_GRABBING) return -EBUSY; - /* Don't compress if the size changed */ if ((ov511->frame[vm.frame].width != vm.width) || (ov511->frame[vm.frame].height != vm.height) || (ov511->frame[vm.frame].format != vm.format) || @@ -2486,7 +5660,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) @@ -2572,10 +5746,12 @@ if ((ov511->snap_enabled) && (ov511->frame[frame].snapshot)) { ov511->frame[frame].snapshot = 0; - ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x01); - ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x03); - ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x01); + ov51x_clear_snapshot(ov511); } + + if (ttpp) + ov511_postprocess(&(ov511->frame[frame])); + break; } /* end switch */ @@ -2615,7 +5791,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; @@ -2690,11 +5866,12 @@ /* Clear the snapshot */ if (ov511->snap_enabled && frame->snapshot) { frame->snapshot = 0; - ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x01); - ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x03); - ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x01); + ov51x_clear_snapshot(ov511); } + if (ttpp) + ov511_postprocess((struct ov511_frame *) frame); + PDEBUG(4, "frmx=%d, bytes_read=%ld, scanlength=%ld", frmx, frame->bytes_read, frame->scanlength); @@ -2773,13 +5950,196 @@ initialize: ov511_init_done, }; +#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS) +static int +ov511_control_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long ularg) +{ + struct proc_dir_entry *pde; + struct usb_ov511 *ov511; + void *arg = (void *) ularg; + int rc; + + pde = (struct proc_dir_entry *) inode->u.generic_ip; + if (!pde) + return -ENOENT; + + ov511 = (struct usb_ov511 *) pde->data; + if (!ov511) + return -ENODEV; + + if (!ov511->dev) + return -EIO; + + /* Should we pass through standard V4L IOCTLs? */ + + switch (cmd) { + case OV511IOC_GINTVER: + { + PDEBUG(4, "Get interface version: %d", OV511_INTERFACE_VER); + return 0; + } + case OV511IOC_GUSHORT: + { + struct ov511_ushort_opt opt; + + if (copy_from_user(&opt, arg, sizeof(opt))) + return -EFAULT; + + switch (opt.optnum) { + case OV511_USOPT_BRIGHT: + rc = ov51x_get_brightness(ov511, &(opt.val)); + if (rc) return rc; + break; + case OV511_USOPT_SAT: + rc = ov51x_get_saturation(ov511, &(opt.val)); + if (rc) return rc; + break; + case OV511_USOPT_HUE: + rc = ov51x_get_hue(ov511, &(opt.val)); + if (rc) return rc; + break; + case OV511_USOPT_CONTRAST: + rc = ov51x_get_contrast(ov511, &(opt.val)); + if (rc) return rc; + break; + default: + err("Invalid get short option number"); + return -EINVAL; + } + + if (copy_to_user(arg, &opt, sizeof(opt))) + return -EFAULT; + + return 0; + } + case OV511IOC_SUSHORT: + { + struct ov511_ushort_opt opt; + + if (copy_from_user(&opt, arg, sizeof(opt))) + return -EFAULT; + + switch (opt.optnum) { + case OV511_USOPT_BRIGHT: + rc = ov51x_set_brightness(ov511, opt.val); + if (rc) return rc; + break; + case OV511_USOPT_SAT: + rc = ov51x_set_saturation(ov511, opt.val); + if (rc) return rc; + break; + case OV511_USOPT_HUE: + rc = ov51x_set_hue(ov511, opt.val); + if (rc) return rc; + break; + case OV511_USOPT_CONTRAST: + rc = ov51x_set_contrast(ov511, opt.val); + if (rc) return rc; + break; + default: + err("Invalid set short option number"); + return -EINVAL; + } + + return 0; + } + case OV511IOC_GUINT: + { + struct ov511_uint_opt opt; + + if (copy_from_user(&opt, arg, sizeof(opt))) + return -EFAULT; + + switch (opt.optnum) { + case OV511_UIOPT_POWER_FREQ: + opt.val = ov511->lightfreq; + break; + case OV511_UIOPT_BFILTER: + opt.val = ov511->bandfilt; + break; + case OV511_UIOPT_LED: + opt.val = ov511->led_policy; + break; + case OV511_UIOPT_DEBUG: + opt.val = debug; + break; + case OV511_UIOPT_COMPRESS: + opt.val = ov511->compress; + break; + default: + err("Invalid get int option number"); + return -EINVAL; + } + + if (copy_to_user(arg, &opt, sizeof(opt))) + return -EFAULT; + + return 0; + } + case OV511IOC_SUINT: + { + struct ov511_uint_opt opt; + + if (copy_from_user(&opt, arg, sizeof(opt))) + return -EFAULT; + + switch (opt.optnum) { + case OV511_UIOPT_POWER_FREQ: + rc = ov51x_set_light_freq(ov511, opt.val); + if (rc) return rc; + break; + case OV511_UIOPT_BFILTER: + rc = ov51x_set_banding_filter(ov511, opt.val); + if (rc) return rc; + break; + case OV511_UIOPT_LED: + if (opt.val <= 2) { + ov511->led_policy = opt.val; + if (ov511->led_policy == LED_OFF) + ov511_led_control(ov511, 0); + else if (ov511->led_policy == LED_ON) + ov511_led_control(ov511, 1); + } else { + return -EINVAL; + } + break; + case OV511_UIOPT_DEBUG: + if (opt.val <= 5) + debug = opt.val; + else + return -EINVAL; + break; + case OV511_UIOPT_COMPRESS: + ov511->compress = opt.val; + if (ov511->compress) + ov51x_init_compression(ov511); + break; + default: + err("Invalid get int option number"); + return -EINVAL; + } + + return 0; + } + default: + return -ENOIOCTLCMD; + } /* end switch */ + + return 0; +} +#endif + /**************************************************************************** * - * 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; @@ -2825,6 +6185,7 @@ { OV511_DONE_BUS, 0x0, 0x00 }, }; +#if 0 static struct ov511_regvals aRegvalsNorm7620[] = { { OV511_I2C_BUS, 0x10, 0xff }, { OV511_I2C_BUS, 0x16, 0x06 }, @@ -2851,44 +6212,84 @@ { OV511_I2C_BUS, 0x0d, 0x24 }, { OV511_DONE_BUS, 0x0, 0x00 }, }; +#else + static struct ov511_regvals aRegvalsNorm7620[] = { + { OV511_I2C_BUS, 0x00, 0x00 }, + { OV511_I2C_BUS, 0x01, 0x80 }, + { OV511_I2C_BUS, 0x02, 0x80 }, + { OV511_I2C_BUS, 0x03, 0xc0 }, + { OV511_I2C_BUS, 0x06, 0x60 }, + { OV511_I2C_BUS, 0x07, 0x00 }, + { OV511_I2C_BUS, 0x0c, 0x24 }, + { OV511_I2C_BUS, 0x0c, 0x24 }, + { OV511_I2C_BUS, 0x0d, 0x24 }, + { OV511_I2C_BUS, 0x11, 0x01 }, + { OV511_I2C_BUS, 0x12, 0x24 }, + { OV511_I2C_BUS, 0x13, 0x01 }, + { OV511_I2C_BUS, 0x14, 0x84 }, + { OV511_I2C_BUS, 0x15, 0x01 }, + { OV511_I2C_BUS, 0x16, 0x03 }, + { OV511_I2C_BUS, 0x17, 0x2f }, + { OV511_I2C_BUS, 0x18, 0xcf }, + { OV511_I2C_BUS, 0x19, 0x06 }, + { OV511_I2C_BUS, 0x1a, 0xf5 }, + { OV511_I2C_BUS, 0x1b, 0x00 }, + { OV511_I2C_BUS, 0x20, 0x18 }, + { OV511_I2C_BUS, 0x21, 0x80 }, + { OV511_I2C_BUS, 0x22, 0x80 }, + { OV511_I2C_BUS, 0x23, 0x00 }, + { OV511_I2C_BUS, 0x26, 0xa2 }, + { OV511_I2C_BUS, 0x27, 0xea }, + { OV511_I2C_BUS, 0x28, 0x20 }, + { OV511_I2C_BUS, 0x29, 0x00 }, + { OV511_I2C_BUS, 0x2a, 0x10 }, + { OV511_I2C_BUS, 0x2b, 0x00 }, + { OV511_I2C_BUS, 0x2c, 0x88 }, + { OV511_I2C_BUS, 0x2d, 0x95 }, + { OV511_I2C_BUS, 0x2e, 0x80 }, + { OV511_I2C_BUS, 0x2f, 0x44 }, + { OV511_I2C_BUS, 0x60, 0x27 }, + { OV511_I2C_BUS, 0x61, 0x02 }, + { OV511_I2C_BUS, 0x62, 0x5f }, + { OV511_I2C_BUS, 0x63, 0xd5 }, + { OV511_I2C_BUS, 0x64, 0x57 }, + { OV511_I2C_BUS, 0x65, 0x83 }, + { OV511_I2C_BUS, 0x66, 0x55 }, + { OV511_I2C_BUS, 0x67, 0x92 }, + { OV511_I2C_BUS, 0x68, 0xcf }, + { OV511_I2C_BUS, 0x69, 0x76 }, + { OV511_I2C_BUS, 0x6a, 0x22 }, + { OV511_I2C_BUS, 0x6b, 0x00 }, + { OV511_I2C_BUS, 0x6c, 0x02 }, + { OV511_I2C_BUS, 0x6d, 0x44 }, + { OV511_I2C_BUS, 0x6e, 0x80 }, + { OV511_I2C_BUS, 0x6f, 0x1d }, + { OV511_I2C_BUS, 0x70, 0x8b }, + { OV511_I2C_BUS, 0x71, 0x00 }, + { OV511_I2C_BUS, 0x72, 0x14 }, + { OV511_I2C_BUS, 0x73, 0x54 }, + { OV511_I2C_BUS, 0x74, 0x00 }, + { OV511_I2C_BUS, 0x75, 0x8e }, + { OV511_I2C_BUS, 0x76, 0x00 }, + { OV511_I2C_BUS, 0x77, 0xff }, + { OV511_I2C_BUS, 0x78, 0x80 }, + { OV511_I2C_BUS, 0x79, 0x80 }, + { OV511_I2C_BUS, 0x7a, 0x80 }, + { OV511_I2C_BUS, 0x7b, 0xe2 }, + { OV511_I2C_BUS, 0x7c, 0x00 }, + { OV511_DONE_BUS, 0x0, 0x00 }, + }; +#endif 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 +6309,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 mmcclell@bigfoot.com"); + 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); } } @@ -2929,8 +6333,22 @@ info("Sensor is an OV7610"); ov511->sensor = SEN_OV7610; } else if((rc & 3) == 1) { - info("Sensor is an OV7620AE"); - ov511->sensor = SEN_OV7620AE; + + /* I don't know what's different about the 76BE yet */ + if (ov511_i2c_read(dev, 0x15) & 1) + info("Sensor is an OV7620AE"); + else + info("Sensor is an OV76BE"); + + /* OV511+ will return all zero isoc data unless we + * configure the sensor as a 7620. Someone needs to + * find the exact reg. setting that causes this. */ + if (ov511->bridge == BRG_OV511PLUS) { + info("Enabling 511+/7620AE workaround"); + ov511->sensor = SEN_OV7620; + } else { + ov511->sensor = SEN_OV7620AE; + } } else if((rc & 3) == 0) { info("Sensor is an OV7620"); ov511->sensor = SEN_OV7620; @@ -2981,74 +6399,138 @@ 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 }, + /* 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 */ +// { OV511_I2C_BUS, 0x12, 0x24 }, /* 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 mmcclell@bigfoot.com"); return -1; + } else { + PDEBUG(1, "OV6xx0 sensor detected"); } /* Detect sensor if user didn't use override param */ @@ -3058,10 +6540,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 +6562,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 +6583,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 }, @@ -3118,107 +6613,241 @@ static struct ov511_regvals aRegvalsNorm511[] = { { OV511_REG_BUS, OV511_REG_DRAM_ENABLE_FLOW_CONTROL, 0x01 }, - { OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x02 }, - { OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x00 }, + { OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x01 }, + { OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x03 }, + { OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x01 }, { OV511_REG_BUS, OV511_REG_FIFO_BITMASK, 0x1f }, - { OV511_REG_BUS, OV511_OMNICE_PREDICTION_HORIZ_Y, 0x08 }, - { 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_UV, 0x01 }, - { OV511_REG_BUS, OV511_OMNICE_QUANTIZATION_VERT_Y, 0x01 }, - { OV511_REG_BUS, OV511_OMNICE_QUANTIZATION_VERT_UV, 0x01 }, - { OV511_REG_BUS, OV511_OMNICE_ENABLE, 0x06 }, + { OV511_REG_BUS, OV511_OMNICE_ENABLE, 0x00 }, { OV511_REG_BUS, OV511_OMNICE_LUT_ENABLE, 0x03 }, { OV511_DONE_BUS, 0x0, 0x00 }, }; - memcpy(&ov511->vdev, &ov511_template, sizeof(ov511_template)); + PDEBUG(4, ""); + + 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 notify mmcclell@bigfoot.com of the name, manufacturer, model,"); + err("and camera type number of your camera. Also include the"); + err("complete output of the detection process."); + } 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, aRegvalsInit511)) goto error; + + if (ov511->led_policy == LED_OFF || ov511->led_policy == LED_AUTO) + ov511_led_control(ov511, 0); - if (ov511_write_regvals(dev, aRegvalsInit)) goto error; if (ov511_write_regvals(dev, aRegvalsNorm511)) goto error; + ov51x_init_compression(ov511); + 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) + /* Test for 7xx0 */ + if (ov51x_set_slave_ids(ov511, OV7xx0_I2C_WRITE_ID, + OV7xx0_I2C_READ_ID) < 0) goto error; - if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ, - OV7610_I2C_READ_ID) < 0) + 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) + goto error; + + 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)) + goto error; + + 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 (ov51x_set_default_params(ov511) < 0) goto error; - if (ov511_reset(dev, OV511_RESET_NOREGS) < 0) + return 0; + +error: + err("OV511 Config failed"); + + 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; + + 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}, + }; + +#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)); + + for (i = 0; i < OV511_NUMFRAMES; i++) + init_waitqueue_head(&ov511->frame[i].wq); + + init_waitqueue_head(&ov511->wq); + + 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 (ov511->compress) { + ov511->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) 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; + /* 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 (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ, - OV6xx0_I2C_READ_ID) < 0) + 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) goto error; - if (ov511_reset(dev, OV511_RESET_NOREGS) < 0) - goto error; + 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) + goto error; - 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; + 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: - video_unregister_device(&ov511->vdev); - usb_driver_release_interface(&ov511_driver, - &dev->actconfig->interface[ov511->iface]); + err("OV518 Config failed"); - return -EBUSY; + return -EBUSY; } @@ -3234,7 +6863,6 @@ { struct usb_interface_descriptor *interface; struct usb_ov511 *ov511; - int i; PDEBUG(1, "probing for device..."); @@ -3262,6 +6890,13 @@ ov511->dev = dev; ov511->iface = interface->bInterfaceNumber; + ov511->led_policy = led; + ov511->compress = compress; + ov511->lightfreq = lightfreq; + ov511->auto_brt = 1; /* Settings enable this by default */ + + ov511->auto_gain = autoadjust; /* These will be decoupled later */ + ov511->auto_exp = autoadjust; switch (dev->descriptor.idProduct) { case 0x0511: @@ -3272,6 +6907,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; @@ -3279,57 +6918,58 @@ ov511->bridge = BRG_OV511PLUS; break; default: - err("Unknown product ID"); + err("Unknown product ID 0x%x", dev->descriptor.idProduct); 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; - } + /* Workaround for some applications that want data in RGB + * instead of BGR. */ + if (force_rgb) + info("data format set to RGB"); - 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; - } + if (ov511->bridge == BRG_OV518PLUS) { + if (ov518_configure(ov511) < 0) + goto error; + } else { + if (ov511_configure(ov511) < 0) + goto error; } - /* Lifeview USB Life TV not supported */ - if (clist[i].id == 38) { - err("This device is not supported yet."); - 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; - 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."); - } +#ifdef OV511_DEBUG + if (dump_bridge) + ov511_dump_regs(dev); +#endif - /* Workaround for some applications that want data in RGB - * instead of BGR */ - if (force_rgb) - info("data format set to RGB"); + memcpy(&ov511->vdev, &ov511_template, sizeof(ov511_template)); - 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; - } else { - err("Failed to configure camera"); + if (video_register_device(&ov511->vdev, VFL_TYPE_GRABBER) < 0) { + err("video_register_device failed"); goto error; } + PDEBUG(1, "Camera initialization successful"); + MOD_DEC_USE_COUNT; return ov511; error: + err("Camera initialization failed"); + + video_unregister_device(&ov511->vdev); + + #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 + + usb_driver_release_interface(&ov511_driver, + &dev->actconfig->interface[ov511->iface]); + if (ov511) { kfree(ov511); ov511 = NULL; @@ -3433,7 +7073,7 @@ #if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS) proc_ov511_destroy(); -#endif +#endif } module_init(usb_ov511_init); diff -Naur linux-2.4.3-orig/drivers/usb/ov511.h linux/drivers/usb/ov511.h --- linux-2.4.3-orig/drivers/usb/ov511.h Fri Mar 2 03:21:10 2001 +++ linux/drivers/usb/ov511.h Mon Apr 9 00:45:23 2001 @@ -10,11 +10,45 @@ #ifdef OV511_DEBUG # define PDEBUG(level, fmt, args...) \ -if (debug >= level) info("[" __PRETTY_FUNCTION__ ":%d] " fmt, __LINE__ , ## args) +if (debug >= (level)) info("[" __PRETTY_FUNCTION__ ":%d] " fmt, __LINE__ , ## args) #else # define PDEBUG(level, fmt, args...) do {} while(0) #endif +/* Standard Windows data types, taken from winnt.h at + * http://www.winehq.com/wine/include/ */ + +#ifndef VOID +#define VOID void +#endif + +typedef const void *PCVOID, *LPCVOID; +typedef int BOOL, *PBOOL, *LPBOOL; +typedef unsigned char BYTE, *PBYTE, *LPBYTE; +typedef unsigned char UCHAR, *PUCHAR; +typedef unsigned short USHORT, *PUSHORT, *LPUSHORT; +typedef unsigned short WORD, *PWORD, *LPWORD; +typedef int INT, *PINT, *LPINT; +typedef unsigned int UINT, *PUINT, *LPUINT; +typedef unsigned long DWORD, *PDWORD, *LPDWORD; +typedef unsigned long ULONG, *PULONG, *LPULONG; +typedef VOID *PVOID, *LPVOID; +typedef BYTE BOOLEAN, *PBOOLEAN; +typedef char CHAR, *PCHAR; +typedef short SHORT, *PSHORT; +typedef long LONG, *PLONG, *LPLONG; +typedef __s64 LONGLONG, *PLONGLONG; +typedef __u64 ULONGLONG, *PULONGLONG; +typedef ULONGLONG DWORDLONG, *PDWORDLONG; + +typedef ULONG NTSTATUS; +const NTSTATUS STATUS_SUCCESS = 0x00000000; +const NTSTATUS STATUS_UNSUCCESSFUL = 0xC0000001; + +/* --------------------------------- */ +/* DEFINES FOR OV511 AND OTHER CHIPS */ +/* --------------------------------- */ + /* Camera interface register numbers */ #define OV511_REG_CAMERA_DELAY_MODE 0x10 #define OV511_REG_CAMERA_EDGE_MODE 0x11 @@ -52,6 +86,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 +113,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 +162,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 +229,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,17 +251,22 @@ /* Bridge types */ enum { + BRG_UNKNOWN, BRG_OV511, BRG_OV511PLUS, + BRG_OV518PLUS, }; -/* Sensor types */ +/* Sensor types - Update ov511.txt (sensor param docs) if you change this! */ enum { SEN_UNKNOWN, SEN_OV7610, SEN_OV7620, SEN_OV7620AE, SEN_OV6620, + SEN_OV6630, + SEN_OV8600, + SEN_KS0127, }; enum { @@ -222,6 +282,61 @@ BUF_PEND_DEALLOC, /* ov511->buf_timer is set */ }; +/* --------- Definition of ioctl interface --------- */ + +#define OV511_INTERFACE_VER 100 + +/* LED options */ +enum { + LED_OFF, + LED_ON, + LED_AUTO, +}; + +/* Unsigned short option numbers */ +enum { + OV511_USOPT_INVALID, + OV511_USOPT_BRIGHT, + OV511_USOPT_SAT, + OV511_USOPT_HUE, + OV511_USOPT_CONTRAST, +}; + +/* Unsigned int option numbers */ +enum { + OV511_UIOPT_INVALID, + OV511_UIOPT_POWER_FREQ, + OV511_UIOPT_BFILTER, + OV511_UIOPT_LED, + OV511_UIOPT_DEBUG, + OV511_UIOPT_COMPRESS, +}; + +struct ov511_ushort_opt { + int optnum; /* Specific option number */ + unsigned short val; +}; + +struct ov511_uint_opt { + int optnum; /* Specific option number */ + unsigned int val; +}; + +/* ioctls */ +#define OV511IOC_GINTVER _IOR('v', BASE_VIDIOCPRIVATE + 0, int) +#define OV511IOC_GUSHORT _IOWR('v', BASE_VIDIOCPRIVATE + 1, struct ov511_ushort_opt) +#define OV511IOC_SUSHORT _IOW('v', BASE_VIDIOCPRIVATE + 2, struct ov511_ushort_opt) +#define OV511IOC_GUINT _IOWR('v', BASE_VIDIOCPRIVATE + 3, struct ov511_uint_opt) +#define OV511IOC_SUINT _IOW('v', BASE_VIDIOCPRIVATE + 4, struct ov511_uint_opt) + +/* ------------------------------------------------ */ + +/* Keeps track of where we are in decompressing a frame */ +typedef struct CompInfo { + LONG index; + DWORD IPTRR_U; +} CompInfo; + struct usb_device; struct ov511_sbuf { @@ -249,6 +364,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 +375,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 +384,15 @@ 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 */ + + CompInfo TempInfo; /* Keeps track of decompression state */ + PUCHAR m_pTempBuffer; /* Temporary buffer, used by decompressor */ + ULONG m_TempBufferLength; wait_queue_head_t wq; /* Processes waiting */ @@ -298,6 +421,11 @@ int contrast; int hue; int whiteness; + int auto_brt; /* Auto brightness enabled flag */ + int auto_gain; /* Auto gain control enabled flag */ + int auto_exp; /* Auto exposure enabled flag */ + + int led_policy; /* LED: off|on|auto; OV511+ only */ struct semaphore lock; int user; /* user count for exclusive use */ @@ -306,8 +434,13 @@ int grabbing; /* Are we grabbing? */ int compress; /* Should the next frame be compressed? */ + int compress_inited; /* Are compression params uploaded? */ + + int lightfreq; /* Power (lighting) frequency */ + int bandfilt; /* Banding filter enabled flag */ 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 */ @@ -334,10 +467,14 @@ int packet_size; /* Frame size per isoc desc */ - /* proc interface */ struct semaphore param_lock; /* params lock for this camera */ - struct proc_dir_entry *proc_entry; /* /proc/ov511/videoX */ - + + /* /proc entries, relative to /proc/video/ov511/ */ + struct proc_dir_entry *proc_devdir; /* Per-device proc directory */ + struct proc_dir_entry *proc_info; /* /info entry */ + struct proc_dir_entry *proc_button; /* /button entry */ + struct proc_dir_entry *proc_control; /* /control entry */ + /* Framebuffer/sbuf management */ int buf_state; struct semaphore buf_lock; @@ -354,7 +491,7 @@ char *name; }; -struct mode_list { +struct mode_list_511 { int width; int height; int color; /* 0=grayscale, 1=color */ @@ -366,6 +503,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