ov511.c
上传用户:lgb322
上传日期:2013-02-24
资源大小:30529k
文件大小:177k
- /*
- * OmniVision OV511 Camera-to-USB Bridge Driver
- *
- * Copyright (c) 1999-2001 Mark W. McClelland
- * Original decompression code Copyright 1998-2000 OmniVision Technologies
- * Many improvements by Bret Wallach <bwallac1@san.rr.com>
- * Color fixes by by Orion Sky Lawlor <olawlor@acm.org> (2/26/2000)
- * Snapshot code by Kevin Moore
- * OV7620 fixes by Charl P. Botha <cpbotha@ieee.org>
- * Changes by Claudio Matsuoka <claudio@conectiva.com>
- * Original SAA7111A code by Dave Perks <dperks@ibm.net>
- * Kernel I2C interface adapted from nt1003 driver
- *
- * Based on the Linux CPiA driver written by Peter Pregler,
- * Scott J. Bertin and Johannes Erdfelt.
- *
- * Please see the file: linux/Documentation/usb/ov511.txt
- * and the website at: http://alpha.dyndns.org/ov511
- * for more info.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
- #include <linux/config.h>
- #include <linux/version.h>
- #include <linux/module.h>
- #include <linux/init.h>
- #include <linux/fs.h>
- #include <linux/vmalloc.h>
- #include <linux/slab.h>
- #include <linux/proc_fs.h>
- #include <linux/ctype.h>
- #include <linux/pagemap.h>
- #include <asm/io.h>
- #include <asm/semaphore.h>
- #include <asm/processor.h>
- #include <linux/wrapper.h>
- #if defined (__i386__)
- #include <asm/cpufeature.h>
- #endif
- #include "ov511.h"
- /*
- * Version Information
- */
- #define DRIVER_VERSION "v1.48a for Linux 2.4"
- #define EMAIL "mmcclell@bigfoot.com"
- #define DRIVER_AUTHOR "Mark McClelland <mmcclell@bigfoot.com> & Bret Wallach
- & Orion Sky Lawlor <olawlor@acm.org> & Kevin Moore & Charl P. Botha
- <cpbotha@ieee.org> & Claudio Matsuoka <claudio@conectiva.com>"
- #define DRIVER_DESC "OV511 USB Camera Driver"
- #define OV511_I2C_RETRIES 3
- #define ENABLE_Y_QUANTABLE 1
- #define ENABLE_UV_QUANTABLE 1
- /* Pixel count * 3 bytes for RGB */
- #define MAX_FRAME_SIZE(w, h) ((w) * (h) * 3)
- #define MAX_DATA_SIZE(w, h) (MAX_FRAME_SIZE(w, h) + sizeof(struct timeval))
- /* Max size * bytes per YUV420 pixel (1.5) + one extra isoc frame for safety */
- #define MAX_RAW_DATA_SIZE(w, h) ((w) * (h) * 3 / 2 + 1024)
- #define FATAL_ERROR(rc) ((rc) < 0 && (rc) != -EPERM)
- /* PARAMETER VARIABLES: */
- /* (See ov511.txt for detailed descriptions of these.) */
- /* Sensor automatically changes brightness */
- static int autobright = 1;
- /* Sensor automatically changes gain */
- static int autogain = 1;
- /* Sensor automatically changes exposure */
- static int autoexp = 1;
- /* 0=no debug messages
- * 1=init/detection/unload and other significant messages,
- * 2=some warning messages
- * 3=config/control function calls
- * 4=most function calls and data parsing messages
- * 5=highly repetitive mesgs
- * NOTE: This should be changed to 0, 1, or 2 for production kernels
- */
- static int debug; /* = 0 */
- /* Fix vertical misalignment of red and blue at 640x480 */
- static int fix_rgb_offset; /* = 0 */
- /* Snapshot mode enabled flag */
- static int snapshot; /* = 0 */
- /* 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. */
- static int force_rgb; /* = 0 */
- /* Number of seconds before inactive buffers are deallocated */
- static int buf_timeout = 5;
- /* Number of cameras to stream from simultaneously */
- static int cams = 1;
- /* Enable compression. Needs a fast (>300 MHz) CPU. */
- static int compress; /* = 0 */
- /* Display test pattern - doesn't work yet either */
- static int testpat; /* = 0 */
- /* Setting this to 1 will make the sensor output GBR422 instead of YUV420. Only
- * affects RGB24 mode. */
- static int sensor_gbr; /* = 0 */
- /* Dump raw pixel data. */
- 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 */
- /* Pixel clock divisor */
- static int clockdiv = -1;
- /* Isoc packet size */
- static int packetsize = -1;
- /* Frame drop register (16h) */
- static int framedrop = -1;
- /* Allows picture settings (brightness, hue, etc...) to take effect immediately,
- * even in the middle of a frame. This reduces the time to change settings, but
- * can ruin frames during the change. Only affects OmniVision sensors. */
- static int fastset; /* = 0 */
- /* Forces the palette to a specific value. If an application requests a
- * different palette, it will be rejected. */
- static int force_palette; /* = 0 */
- /* Set tuner type, if not autodetected */
- static int tuner = -1;
- /* Allows proper exposure of objects that are illuminated from behind. Only
- * affects OmniVision sensors. */
- static int backlight; /* = 0 */
- /* If you change this, you must also change the MODULE_PARM definition */
- #define OV511_MAX_UNIT_VIDEO 16
- /* Allows specified minor numbers to be forced. They will be assigned in the
- * order that devices are detected. Note that you cannot specify 0 as a minor
- * number. If you do not specify any, the next available one will be used. This
- * requires kernel 2.4.5 or later. */
- static int unit_video[OV511_MAX_UNIT_VIDEO];
- /* Remove zero-padding from uncompressed incoming data. This will compensate for
- * the blocks of corruption that appear when the camera cannot keep up with the
- * speed of the USB bus (eg. at low frame resolutions) */
- static int remove_zeros; /* = 0 */
- MODULE_PARM(autobright, "i");
- MODULE_PARM_DESC(autobright, "Sensor automatically changes brightness");
- MODULE_PARM(autogain, "i");
- MODULE_PARM_DESC(autogain, "Sensor automatically changes gain");
- MODULE_PARM(autoexp, "i");
- MODULE_PARM_DESC(autoexp, "Sensor automatically changes exposure");
- MODULE_PARM(debug, "i");
- MODULE_PARM_DESC(debug,
- "Debug level: 0=none, 1=inits, 2=warning, 3=config, 4=functions, 5=max");
- MODULE_PARM(fix_rgb_offset, "i");
- MODULE_PARM_DESC(fix_rgb_offset,
- "Fix vertical misalignment of red and blue at 640x480");
- MODULE_PARM(snapshot, "i");
- MODULE_PARM_DESC(snapshot, "Enable snapshot mode");
- MODULE_PARM(force_rgb, "i");
- MODULE_PARM_DESC(force_rgb, "Read RGB instead of BGR");
- MODULE_PARM(buf_timeout, "i");
- MODULE_PARM_DESC(buf_timeout, "Number of seconds before buffer deallocation");
- MODULE_PARM(cams, "i");
- MODULE_PARM_DESC(cams, "Number of simultaneous cameras");
- MODULE_PARM(compress, "i");
- 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)");
- // Temporarily removed (needs to be rewritten for new format conversion code)
- // 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");
- MODULE_PARM(led, "i");
- MODULE_PARM_DESC(led,
- "LED policy (OV511+ or later). 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 frame start/end headers");
- MODULE_PARM(phy, "i");
- MODULE_PARM_DESC(phy, "Prediction range (horiz. Y)");
- MODULE_PARM(phuv, "i");
- MODULE_PARM_DESC(phuv, "Prediction range (horiz. UV)");
- MODULE_PARM(pvy, "i");
- MODULE_PARM_DESC(pvy, "Prediction range (vert. Y)");
- MODULE_PARM(pvuv, "i");
- MODULE_PARM_DESC(pvuv, "Prediction range (vert. UV)");
- MODULE_PARM(qhy, "i");
- MODULE_PARM_DESC(qhy, "Quantization threshold (horiz. Y)");
- MODULE_PARM(qhuv, "i");
- MODULE_PARM_DESC(qhuv, "Quantization threshold (horiz. UV)");
- MODULE_PARM(qvy, "i");
- MODULE_PARM_DESC(qvy, "Quantization threshold (vert. Y)");
- MODULE_PARM(qvuv, "i");
- MODULE_PARM_DESC(qvuv, "Quantization threshold (vert. 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(clockdiv, "i");
- MODULE_PARM_DESC(clockdiv, "Force pixel clock divisor to a specific value");
- MODULE_PARM(packetsize, "i");
- MODULE_PARM_DESC(packetsize, "Force a specific isoc packet size");
- MODULE_PARM(framedrop, "i");
- MODULE_PARM_DESC(framedrop, "Force a specific frame drop register setting");
- MODULE_PARM(fastset, "i");
- MODULE_PARM_DESC(fastset, "Allows picture settings to take effect immediately");
- MODULE_PARM(force_palette, "i");
- MODULE_PARM_DESC(force_palette, "Force the palette to a specific value");
- MODULE_PARM(tuner, "i");
- MODULE_PARM_DESC(tuner, "Set tuner type, if not autodetected");
- MODULE_PARM(backlight, "i");
- MODULE_PARM_DESC(backlight, "For objects that are lit from behind");
- MODULE_PARM(unit_video, "0-16i");
- MODULE_PARM_DESC(unit_video,
- "Force use of specific minor number(s). 0 is not allowed.");
- MODULE_PARM(remove_zeros, "i");
- MODULE_PARM_DESC(remove_zeros,
- "Remove zero-padding from uncompressed incoming data");
- MODULE_AUTHOR(DRIVER_AUTHOR);
- MODULE_DESCRIPTION(DRIVER_DESC);
- MODULE_LICENSE("GPL");
- static struct usb_driver ov511_driver;
- static struct ov51x_decomp_ops *ov511_decomp_ops;
- static struct ov51x_decomp_ops *ov511_mmx_decomp_ops;
- static struct ov51x_decomp_ops *ov518_decomp_ops;
- static struct ov51x_decomp_ops *ov518_mmx_decomp_ops;
- /* Number of times to retry a failed I2C transaction. Increase this if you
- * are getting "Failed to read sensor ID..." */
- static int i2c_detect_tries = 5;
- /* MMX support is present in kernel and CPU. Checked upon decomp module load. */
- static int ov51x_mmx_available;
- /* Function prototypes */
- static void ov51x_clear_snapshot(struct usb_ov511 *);
- static int ov51x_check_snapshot(struct usb_ov511 *);
- static inline int sensor_get_picture(struct usb_ov511 *,
- struct video_picture *);
- static int sensor_get_exposure(struct usb_ov511 *, unsigned char *);
- static int ov511_control_ioctl(struct inode *, struct file *, unsigned int,
- unsigned long);
- /**********************************************************************
- * List of known OV511-based cameras
- **********************************************************************/
- static struct cam_list clist[] = {
- { 0, "Generic Camera (no ID)" },
- { 1, "Mustek WCam 3X" },
- { 3, "D-Link DSB-C300" },
- { 4, "Generic OV511/OV7610" },
- { 5, "Puretek PT-6007" },
- { 6, "Lifeview USB Life TV (NTSC)" },
- { 21, "Creative Labs WebCam 3" },
- { 36, "Koala-Cam" },
- { 38, "Lifeview USB Life TV" },
- { 41, "Samsung Anycam MPC-M10" },
- { 43, "Mtekvision Zeca MV402" },
- { 46, "Suma eON" },
- { 100, "Lifeview RoboCam" },
- { 102, "AverMedia InterCam Elite" },
- { 112, "MediaForte MV300" }, /* or OV7110 evaluation kit */
- { -1, NULL }
- };
- static __devinitdata struct usb_device_id device_table [] = {
- { USB_DEVICE(VEND_OMNIVISION, PROD_OV511) },
- { USB_DEVICE(VEND_OMNIVISION, PROD_OV511PLUS) },
- { USB_DEVICE(VEND_OMNIVISION, PROD_OV518) },
- { USB_DEVICE(VEND_OMNIVISION, PROD_OV518PLUS) },
- { USB_DEVICE(VEND_MATTEL, PROD_ME2CAM) },
- { } /* Terminating entry */
- };
- MODULE_DEVICE_TABLE (usb, device_table);
- #if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
- static struct palette_list plist[] = {
- { VIDEO_PALETTE_GREY, "GREY" },
- { VIDEO_PALETTE_HI240, "HI240" },
- { VIDEO_PALETTE_RGB565, "RGB565" },
- { VIDEO_PALETTE_RGB24, "RGB24" },
- { VIDEO_PALETTE_RGB32, "RGB32" },
- { VIDEO_PALETTE_RGB555, "RGB555" },
- { VIDEO_PALETTE_YUV422, "YUV422" },
- { VIDEO_PALETTE_YUYV, "YUYV" },
- { VIDEO_PALETTE_UYVY, "UYVY" },
- { VIDEO_PALETTE_YUV420, "YUV420" },
- { VIDEO_PALETTE_YUV411, "YUV411" },
- { VIDEO_PALETTE_RAW, "RAW" },
- { VIDEO_PALETTE_YUV422P,"YUV422P" },
- { VIDEO_PALETTE_YUV411P,"YUV411P" },
- { VIDEO_PALETTE_YUV420P,"YUV420P" },
- { VIDEO_PALETTE_YUV410P,"YUV410P" },
- { -1, NULL }
- };
- #endif
- static unsigned char yQuanTable511[] = OV511_YQUANTABLE;
- static unsigned char uvQuanTable511[] = OV511_UVQUANTABLE;
- static unsigned char yQuanTable518[] = OV518_YQUANTABLE;
- static unsigned char uvQuanTable518[] = OV518_UVQUANTABLE;
- /**********************************************************************
- *
- * Memory management
- *
- * This is a shameless copy from the USB-cpia driver (linux kernel
- * version 2.3.29 or so, I have no idea what this code actually does ;).
- * Actually it seems to be a copy of a shameless copy of the bttv-driver.
- * Or that is a copy of a shameless copy of ... (To the powers: is there
- * no generic kernel-function to do this sort of stuff?)
- *
- * Yes, it was a shameless copy from the bttv-driver. IIRC, Alan says
- * there will be one, but apparentely not yet -jerdfelt
- *
- * So I copied it again for the OV511 driver -claudio
- **********************************************************************/
- /* Given PGD from the address space's page table, return the kernel
- * virtual mapping of the physical memory mapped at ADR.
- */
- static inline unsigned long
- uvirt_to_kva(pgd_t *pgd, unsigned long adr)
- {
- unsigned long ret = 0UL;
- pmd_t *pmd;
- pte_t *ptep, pte;
- if (!pgd_none(*pgd)) {
- pmd = pmd_offset(pgd, adr);
- if (!pmd_none(*pmd)) {
- ptep = pte_offset(pmd, adr);
- pte = *ptep;
- if (pte_present(pte)) {
- ret = (unsigned long)
- page_address(pte_page(pte));
- ret |= (adr & (PAGE_SIZE - 1));
- }
- }
- }
- return ret;
- }
- /* Here we want the physical address of the memory.
- * This is used when initializing the contents of the
- * area and marking the pages as reserved.
- */
- static inline unsigned long
- kvirt_to_pa(unsigned long adr)
- {
- unsigned long va, kva, ret;
- va = VMALLOC_VMADDR(adr);
- kva = uvirt_to_kva(pgd_offset_k(va), va);
- ret = __pa(kva);
- return ret;
- }
- static void *
- rvmalloc(unsigned long size)
- {
- void *mem;
- unsigned long adr, page;
- /* Round it off to PAGE_SIZE */
- size += (PAGE_SIZE - 1);
- size &= ~(PAGE_SIZE - 1);
- mem = vmalloc_32(size);
- if (!mem)
- return NULL;
- memset(mem, 0, size); /* Clear the ram out, no junk to the user */
- adr = (unsigned long) mem;
- while (size > 0) {
- page = kvirt_to_pa(adr);
- mem_map_reserve(virt_to_page(__va(page)));
- adr += PAGE_SIZE;
- if (size > PAGE_SIZE)
- size -= PAGE_SIZE;
- else
- size = 0;
- }
- return mem;
- }
- static void
- rvfree(void *mem, unsigned long size)
- {
- unsigned long adr, page;
- if (!mem)
- return;
- size += (PAGE_SIZE - 1);
- size &= ~(PAGE_SIZE - 1);
- adr=(unsigned long) mem;
- while (size > 0) {
- page = kvirt_to_pa(adr);
- mem_map_unreserve(virt_to_page(__va(page)));
- adr += PAGE_SIZE;
- if (size > PAGE_SIZE)
- size -= PAGE_SIZE;
- else
- size = 0;
- }
- vfree(mem);
- }
- /**********************************************************************
- * /proc interface
- * Based on the CPiA driver version 0.7.4 -claudio
- **********************************************************************/
- #if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
- static struct proc_dir_entry *ov511_proc_entry = NULL;
- extern struct proc_dir_entry *video_proc_entry;
- static struct file_operations ov511_control_fops = {
- ioctl: ov511_control_ioctl,
- };
- #define YES_NO(x) ((x) ? "yes" : "no")
- /* /proc/video/ov511/<minor#>/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;
- unsigned char exp;
- if (!ov511 || !ov511->dev)
- return -ENODEV;
- sensor_get_picture(ov511, &p);
- sensor_get_exposure(ov511, &exp);
- /* IMPORTANT: This output MUST be kept under PAGE_SIZE
- * or we need to get more sophisticated. */
- out += sprintf(out, "driver_version : %sn", DRIVER_VERSION);
- out += sprintf(out, "custom_id : %dn", ov511->customid);
- out += sprintf(out, "model : %sn", ov511->desc ?
- clist[ov511->desc].description : "unknown");
- out += sprintf(out, "streaming : %sn", YES_NO(ov511->streaming));
- out += sprintf(out, "grabbing : %sn", YES_NO(ov511->grabbing));
- out += sprintf(out, "compress : %sn", YES_NO(ov511->compress));
- out += sprintf(out, "subcapture : %sn", YES_NO(ov511->sub_flag));
- out += sprintf(out, "sub_size : %d %d %d %dn",
- ov511->subx, ov511->suby, ov511->subw, ov511->subh);
- out += sprintf(out, "data_format : %sn",
- force_rgb ? "RGB" : "BGR");
- out += sprintf(out, "brightness : %dn", p.brightness >> 8);
- out += sprintf(out, "colour : %dn", p.colour >> 8);
- out += sprintf(out, "contrast : %dn", p.contrast >> 8);
- out += sprintf(out, "hue : %dn", p.hue >> 8);
- out += sprintf(out, "exposure : %dn", exp);
- out += sprintf(out, "num_frames : %dn", OV511_NUMFRAMES);
- for (i = 0; i < OV511_NUMFRAMES; i++) {
- out += sprintf(out, "frame : %dn", i);
- out += sprintf(out, " depth : %dn",
- ov511->frame[i].depth);
- out += sprintf(out, " size : %d %dn",
- ov511->frame[i].width, ov511->frame[i].height);
- out += sprintf(out, " format : ");
- for (j = 0; plist[j].num >= 0; j++) {
- if (plist[j].num == ov511->frame[i].format) {
- out += sprintf(out, "%sn", plist[j].name);
- break;
- }
- }
- if (plist[j].num < 0)
- out += sprintf(out, "unknownn");
- out += sprintf(out, " data_buffer : 0x%pn",
- ov511->frame[i].data);
- }
- out += sprintf(out, "snap_enabled : %sn",
- YES_NO(ov511->snap_enabled));
- out += sprintf(out, "bridge : %sn",
- ov511->bridge == BRG_OV511 ? "OV511" :
- ov511->bridge == BRG_OV511PLUS ? "OV511+" :
- ov511->bridge == BRG_OV518 ? "OV518" :
- ov511->bridge == BRG_OV518PLUS ? "OV518+" :
- "unknown");
- out += sprintf(out, "sensor : %sn",
- 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" :
- ov511->sensor == SEN_KS0127B ? "KS0127B" :
- ov511->sensor == SEN_SAA7111A ? "SAA7111A" :
- "unknown");
- out += sprintf(out, "packet_size : %dn", ov511->packet_size);
- out += sprintf(out, "framebuffer : 0x%pn", ov511->fbuf);
- len = out - page;
- len -= off;
- if (len < count) {
- *eof = 1;
- if (len <= 0)
- return 0;
- } else
- len = count;
- *start = page + off;
- return len;
- }
- /* /proc/video/ov511/<minor#>/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)
- {
- 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)
- {
- char dirname[4];
- if (!ov511_proc_entry || !ov511)
- return;
- /* 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;
- /* 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;
- /* 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->data = ov511;
- ov511->proc_control->proc_fops = &ov511_control_fops;
- unlock_kernel();
- }
- static void
- destroy_proc_ov511_cam(struct usb_ov511 *ov511)
- {
- char dirname[4];
-
- if (!ov511 || !ov511->proc_devdir)
- return;
- 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)
- {
- /* No current standard here. Alan prefers /proc/video/ as it keeps
- * /proc "less cluttered than /proc/randomcardifoundintheshed/"
- * -claudio
- */
- if (video_proc_entry == NULL) {
- err("Error: /proc/video/ does not exist");
- return;
- }
- ov511_proc_entry = create_proc_entry("ov511", S_IFDIR,
- video_proc_entry);
- if (ov511_proc_entry)
- ov511_proc_entry->owner = THIS_MODULE;
- else
- err("Unable to create /proc/video/ov511");
- }
- static void
- proc_ov511_destroy(void)
- {
- PDEBUG(3, "removing /proc/video/ov511");
- if (ov511_proc_entry == NULL)
- return;
- remove_proc_entry("ov511", video_proc_entry);
- }
- #endif /* CONFIG_PROC_FS && CONFIG_VIDEO_PROC_FS */
- /**********************************************************************
- *
- * Register I/O
- *
- **********************************************************************/
- static int
- ov511_reg_write(struct usb_device *dev, unsigned char reg, unsigned char value)
- {
- 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);
- if (rc < 0)
- err("reg write: error %d", rc);
- return rc;
- }
- /* returns: negative is error, pos or zero is data */
- static int
- ov511_reg_read(struct usb_device *dev, unsigned char reg)
- {
- int rc;
- unsigned char buffer[1];
- rc = usb_control_msg(dev,
- usb_rcvctrlpipe(dev, 0),
- 2 /* REG_IO */,
- USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_DEVICE,
- 0, (__u16)reg, buffer, 1, HZ);
-
- PDEBUG(5, "0x%02X:0x%02X", reg, buffer[0]);
-
- if (rc < 0) {
- err("reg read: error %d", rc);
- return rc;
- } else {
- return buffer[0];
- }
- }
- /*
- * 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). Used for writing 16 and 24-bit values. */
- 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 = yQuanTable511;
- unsigned char *pUVTable = uvQuanTable511;
- unsigned char val0, val1;
- int i, rc, reg = OV511_OMNICE_Y_LUT_BEGIN;
- PDEBUG(4, "Uploading quantization tables");
- for (i = 0; i < OV511_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 + OV511_QUANTABLESIZE / 2,
- val0);
- if (rc < 0)
- return rc;
- }
- reg++;
- }
- return 0;
- }
- /* OV518 quantization tables are 8x4 (instead of 8x8) */
- static int
- ov518_upload_quan_tables(struct usb_device *dev)
- {
- unsigned char *pYTable = yQuanTable518;
- unsigned char *pUVTable = uvQuanTable518;
- unsigned char val0, val1;
- int i, rc, reg = OV511_OMNICE_Y_LUT_BEGIN;
- PDEBUG(4, "Uploading quantization tables");
- for (i = 0; i < OV518_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 + OV518_QUANTABLESIZE / 2,
- val0);
- if (rc < 0)
- return rc;
- }
- reg++;
- }
- return 0;
- }
- /* NOTE: Do not call this function directly!
- * The OV518 I2C I/O procedure is different, hence, this function.
- * This is normally only called from ov51x_i2c_write(). Note that this function
- * always succeeds regardless of whether the sensor is present and working.
- */
- static int
- ov518_i2c_write_internal(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;
- }
- /* NOTE: Do not call this function directly! */
- static int
- ov511_i2c_write_internal(struct usb_device *dev,
- unsigned char reg,
- unsigned char value)
- {
- int rc, retries;
- PDEBUG(5, "0x%02X:0x%02X", reg, value);
- /* Three byte write cycle */
- for (retries = OV511_I2C_RETRIES; ; ) {
- /* 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, OV511_REG_I2C_CONTROL, 0x01);
- if (rc < 0) goto error;
- do rc = ov511_reg_read(dev, OV511_REG_I2C_CONTROL);
- while (rc > 0 && ((rc&1) == 0)); /* Retry until idle */
- if (rc < 0) goto error;
- if ((rc&2) == 0) /* Ack? */
- break;
- #if 0
- /* I2C abort */
- ov511_reg_write(dev, OV511_REG_I2C_CONTROL, 0x10);
- #endif
- if (--retries < 0) {
- err("i2c write retries exhausted");
- rc = -1;
- goto error;
- }
- }
- return 0;
- error:
- err("i2c write: error %d", rc);
- return rc;
- }
- /* NOTE: Do not call this function directly!
- * The OV518 I2C I/O procedure is different, hence, this function.
- * This is normally only called from ov51x_i2c_read(). Note that this function
- * always succeeds regardless of whether the sensor is present and working.
- */
- static int
- ov518_i2c_read_internal(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;
- }
- /* NOTE: Do not call this function directly!
- * returns: negative is error, pos or zero is data */
- static int
- ov511_i2c_read_internal(struct usb_device *dev, unsigned char reg)
- {
- int rc, value, retries;
- /* Two byte write cycle */
- for (retries = OV511_I2C_RETRIES; ; ) {
- /* 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, OV511_REG_I2C_CONTROL, 0x03);
- if (rc < 0) goto error;
- do rc = ov511_reg_read(dev, OV511_REG_I2C_CONTROL);
- while (rc > 0 && ((rc&1) == 0)); /* Retry until idle */
- if (rc < 0) goto error;
- if ((rc&2) == 0) /* Ack? */
- break;
- /* I2C abort */
- ov511_reg_write(dev, OV511_REG_I2C_CONTROL, 0x10);
- if (--retries < 0) {
- err("i2c write retries exhausted");
- rc = -1;
- goto error;
- }
- }
- /* Two byte read cycle */
- for (retries = OV511_I2C_RETRIES; ; ) {
- /* Initiate 2-byte read cycle */
- rc = ov511_reg_write(dev, OV511_REG_I2C_CONTROL, 0x05);
- if (rc < 0) goto error;
- do rc = ov511_reg_read(dev, OV511_REG_I2C_CONTROL);
- while (rc > 0 && ((rc&1) == 0)); /* Retry until idle */
- if (rc < 0) goto error;
- if ((rc&2) == 0) /* Ack? */
- break;
- /* I2C abort */
- rc = ov511_reg_write(dev, OV511_REG_I2C_CONTROL, 0x10);
- if (rc < 0) goto error;
- if (--retries < 0) {
- err("i2c read retries exhausted");
- rc = -1;
- goto error;
- }
- }
- value = ov511_reg_read(dev, OV511_REG_I2C_DATA_PORT);
- PDEBUG(5, "0x%02X:0x%02X", reg, value);
-
- /* This is needed to make ov51x_i2c_write() work */
- rc = ov511_reg_write(dev, OV511_REG_I2C_CONTROL, 0x05);
- if (rc < 0)
- goto error;
-
- return value;
- error:
- err("i2c read: error %d", rc);
- return rc;
- }
- /* returns: negative is error, pos or zero is data */
- static int
- ov51x_i2c_read(struct usb_ov511 *ov511, unsigned char reg)
- {
- int rc;
- struct usb_device *dev = ov511->dev;
- down(&ov511->i2c_lock);
- if (dev->descriptor.idProduct == PROD_OV518 ||
- dev->descriptor.idProduct == PROD_OV518PLUS)
- rc = ov518_i2c_read_internal(dev, reg);
- else
- rc = ov511_i2c_read_internal(dev, reg);
- up(&ov511->i2c_lock);
- return rc;
- }
- static int
- ov51x_i2c_write(struct usb_ov511 *ov511,
- unsigned char reg,
- unsigned char value)
- {
- int rc;
- struct usb_device *dev = ov511->dev;
- down(&ov511->i2c_lock);
- if (dev->descriptor.idProduct == PROD_OV518 ||
- dev->descriptor.idProduct == PROD_OV518PLUS)
- rc = ov518_i2c_write_internal(dev, reg, value);
- else
- rc = ov511_i2c_write_internal(dev, reg, value);
- up(&ov511->i2c_lock);
- return rc;
- }
- /* Do not call this function directly! */
- static int
- ov51x_i2c_write_mask_internal(struct usb_device *dev,
- unsigned char reg,
- unsigned char value,
- unsigned char mask)
- {
- int rc;
- unsigned char oldval, newval;
- if (mask == 0xff) {
- newval = value;
- } else {
- if (dev->descriptor.idProduct == PROD_OV518 ||
- dev->descriptor.idProduct == PROD_OV518PLUS)
- rc = ov518_i2c_read_internal(dev, reg);
- else
- rc = ov511_i2c_read_internal(dev, reg);
- if (rc < 0)
- return rc;
- oldval = (unsigned char) rc;
- oldval &= (~mask); /* Clear the masked bits */
- value &= mask; /* Enforce mask on value */
- newval = oldval | value; /* Set the desired bits */
- }
- if (dev->descriptor.idProduct == PROD_OV518 ||
- dev->descriptor.idProduct == PROD_OV518PLUS)
- return (ov518_i2c_write_internal(dev, reg, newval));
- else
- return (ov511_i2c_write_internal(dev, reg, newval));
- }
- /* 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
- ov51x_i2c_write_mask(struct usb_ov511 *ov511,
- unsigned char reg,
- unsigned char value,
- unsigned char mask)
- {
- int rc;
- struct usb_device *dev = ov511->dev;
- down(&ov511->i2c_lock);
- rc = ov51x_i2c_write_mask_internal(dev, reg, value, mask);
- up(&ov511->i2c_lock);
- return rc;
- }
- /* Write to a specific I2C slave ID and register, using the specified mask */
- static int
- ov51x_i2c_write_slave(struct usb_ov511 *ov511,
- unsigned char slave,
- unsigned char reg,
- unsigned char value,
- unsigned char mask)
- {
- int rc = 0;
- struct usb_device *dev = ov511->dev;
- down(&ov511->i2c_lock);
- /* Set new slave IDs */
- if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE, slave) < 0) {
- rc = -EIO;
- goto out;
- }
- if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ, slave + 1) < 0) {
- rc = -EIO;
- goto out;
- }
- rc = ov51x_i2c_write_mask_internal(dev, reg, value, mask);
- /* Don't bail out yet if error; IDs must be restored */
- /* Restore primary IDs */
- slave = ov511->primary_i2c_slave;
- if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE, slave) < 0) {
- rc = -EIO;
- goto out;
- }
- if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ, slave + 1) < 0) {
- rc = -EIO;
- goto out;
- }
- out:
- up(&ov511->i2c_lock);
- return rc;
- }
- /* Read from a specific I2C slave ID and register */
- static int
- ov51x_i2c_read_slave(struct usb_ov511 *ov511,
- unsigned char slave,
- unsigned char reg)
- {
- int rc;
- struct usb_device *dev = ov511->dev;
- down(&ov511->i2c_lock);
- /* Set new slave IDs */
- if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE, slave) < 0) {
- rc = -EIO;
- goto out;
- }
- if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ, slave + 1) < 0) {
- rc = -EIO;
- goto out;
- }
- if (dev->descriptor.idProduct == PROD_OV518 ||
- dev->descriptor.idProduct == PROD_OV518PLUS)
- rc = ov518_i2c_read_internal(dev, reg);
- else
- rc = ov511_i2c_read_internal(dev, reg);
- /* Don't bail out yet if error; IDs must be restored */
- /* Restore primary IDs */
- slave = ov511->primary_i2c_slave;
- if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE, slave) < 0) {
- rc = -EIO;
- goto out;
- }
- if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ, slave + 1) < 0) {
- rc = -EIO;
- goto out;
- }
- out:
- up(&ov511->i2c_lock);
- return rc;
- }
- static int
- ov511_write_regvals(struct usb_ov511 *ov511,
- struct ov511_regvals * pRegvals)
- {
- int rc;
- struct usb_device *dev = ov511->dev;
- while (pRegvals->bus != OV511_DONE_BUS) {
- if (pRegvals->bus == OV511_REG_BUS) {
- if ((rc = ov511_reg_write(dev, pRegvals->reg,
- pRegvals->val)) < 0)
- goto error;
- } else if (pRegvals->bus == OV511_I2C_BUS) {
- if ((rc = ov51x_i2c_write(ov511, pRegvals->reg,
- pRegvals->val)) < 0)
- goto error;
- } else {
- err("Bad regval array");
- rc = -1;
- goto error;
- }
- pRegvals++;
- }
- return 0;
- error:
- err("write regvals: error %d", rc);
- return rc;
- }
- #ifdef OV511_DEBUG
- static void
- ov511_dump_i2c_range(struct usb_ov511 *ov511, int reg1, int regn)
- {
- int i;
- int rc;
- for (i = reg1; i <= regn; i++) {
- rc = ov51x_i2c_read(ov511, i);
- info("OV7610[0x%X] = 0x%X", i, rc);
- }
- }
- static void
- ov51x_dump_i2c_regs(struct usb_ov511 *ov511)
- {
- info("I2C REGS");
- ov511_dump_i2c_range(ov511, 0x00, 0x7C);
- }
- 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);
- info("OV511[0x%X] = 0x%X", i, rc);
- }
- }
- static void
- ov511_dump_regs(struct usb_device *dev)
- {
- info("CAMERA INTERFACE REGS");
- ov511_dump_reg_range(dev, 0x10, 0x1f);
- info("DRAM INTERFACE REGS");
- ov511_dump_reg_range(dev, 0x20, 0x23);
- info("ISO FIFO REGS");
- ov511_dump_reg_range(dev, 0x30, 0x31);
- info("PIO REGS");
- ov511_dump_reg_range(dev, 0x38, 0x39);
- ov511_dump_reg_range(dev, 0x3e, 0x3e);
- info("I2C REGS");
- ov511_dump_reg_range(dev, 0x40, 0x49);
- info("SYSTEM CONTROL REGS");
- ov511_dump_reg_range(dev, 0x50, 0x55);
- ov511_dump_reg_range(dev, 0x5e, 0x5f);
- 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
- /**********************************************************************
- *
- * Kernel I2C Interface
- *
- **********************************************************************/
- /* For as-yet unimplemented I2C interface */
- static void
- call_i2c_clients(struct usb_ov511 *ov511, unsigned int cmd,
- void *arg)
- {
- /* Do nothing */
- }
- /*****************************************************************************/
- 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_OV518 ||
- ov511->bridge == BRG_OV518PLUS)
- reset_type &= 0xfe;
- PDEBUG(4, "Reset: type=0x%X", reset_type);
- 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");
- return rc;
- }
- /* Temporarily stops OV511 from functioning. Must do this before changing
- * registers while the camera is streaming */
- static inline int
- ov511_stop(struct usb_ov511 *ov511)
- {
- PDEBUG(4, "stopping");
- ov511->stopped = 1;
- if (ov511->bridge == BRG_OV518 ||
- ov511->bridge == BRG_OV518PLUS)
- return (ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_RESET,
- 0x3a));
- else
- return (ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_RESET,
- 0x3d));
- }
- /* Restarts OV511 after ov511_stop() is called. Has no effect if it is not
- * actually stopped (for performance). */
- static inline int
- ov511_restart(struct usb_ov511 *ov511)
- {
- if (ov511->stopped) {
- PDEBUG(4, "restarting");
- ov511->stopped = 0;
- /* Reinitialize the stream */
- if (ov511->bridge == BRG_OV518 ||
- ov511->bridge == BRG_OV518PLUS)
- ov511_reg_write(ov511->dev, 0x2f, 0x80);
- return (ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_RESET,
- 0x00));
- }
- return 0;
- }
- /* 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_OV518 ||
- ov511->bridge == BRG_OV518PLUS) {
- warn("snapshot reset not supported yet on OV518(+)");
- } else {
- err("clear snap: invalid bridge 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_OV518 ||
- ov511->bridge == BRG_OV518PLUS) {
- warn("snapshot check not supported yet on OV518(+)");
- } else {
- err("check snap: invalid bridge 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)
- {
- int i, success;
- /* Reset the sensor */
- if (ov51x_i2c_write(ov511, 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 ((ov51x_i2c_read(ov511, OV7610_REG_ID_HIGH) == 0x7F) &&
- (ov51x_i2c_read(ov511, OV7610_REG_ID_LOW) == 0xA2)) {
- success = 1;
- continue;
- }
- /* Reset the sensor */
- if (ov51x_i2c_write(ov511, 0x12, 0x80) < 0) return -EIO;
- /* Wait for it to initialize */
- schedule_timeout(1 + 150 * HZ / 1000);
- /* Dummy read to sync I2C */
- if (ov51x_i2c_read(ov511, 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) < 0)
- return -EIO;
- mult = size >> 5;
- if (ov511->bridge == BRG_OV511) {
- if (size == 0) alt = OV511_ALT_SIZE_0;
- else if (size == 257) alt = OV511_ALT_SIZE_257;
- else if (size == 513) alt = OV511_ALT_SIZE_513;
- else if (size == 769) alt = OV511_ALT_SIZE_769;
- else if (size == 993) alt = OV511_ALT_SIZE_993;
- else {
- err("Set packet size: invalid size (%d)", size);
- return -EINVAL;
- }
- } else if (ov511->bridge == BRG_OV511PLUS) {
- if (size == 0) alt = OV511PLUS_ALT_SIZE_0;
- else if (size == 33) alt = OV511PLUS_ALT_SIZE_33;
- else if (size == 129) alt = OV511PLUS_ALT_SIZE_129;
- else if (size == 257) alt = OV511PLUS_ALT_SIZE_257;
- else if (size == 385) alt = OV511PLUS_ALT_SIZE_385;
- else if (size == 513) alt = OV511PLUS_ALT_SIZE_513;
- else if (size == 769) alt = OV511PLUS_ALT_SIZE_769;
- else if (size == 961) alt = OV511PLUS_ALT_SIZE_961;
- else {
- err("Set packet size: invalid size (%d)", size);
- return -EINVAL;
- }
- } else if (ov511->bridge == BRG_OV518 ||
- ov511->bridge == BRG_OV518PLUS) {
- if (size == 0) alt = OV518_ALT_SIZE_0;
- else if (size == 128) alt = OV518_ALT_SIZE_128;
- else if (size == 256) alt = OV518_ALT_SIZE_256;
- else if (size == 384) alt = OV518_ALT_SIZE_384;
- else if (size == 512) alt = OV518_ALT_SIZE_512;
- else if (size == 640) alt = OV518_ALT_SIZE_640;
- else if (size == 768) alt = OV518_ALT_SIZE_768;
- else if (size == 896) alt = OV518_ALT_SIZE_896;
- else {
- err("Set packet size: invalid size (%d)", size);
- return -EINVAL;
- }
- } else {
- err("Set packet size: Invalid bridge type");
- return -EINVAL;
- }
- PDEBUG(3, "set packet size: %d, mult=%d, alt=%d", size, mult, alt);
- // FIXME: Don't know how to do this on OV518 yet
- if (ov511->bridge != BRG_OV518 &&
- 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;
- }
- /* Initialize the stream */
- if (ov511->bridge == BRG_OV518 ||
- 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, OV511_RESET_NOREGS) < 0)
- return -EIO;
- ov511->packet_size = size;
- if (ov511_restart(ov511) < 0)
- return -EIO;
- return 0;
- }
- /* Upload compression params and quantization tables. Returns 0 for success. */
- static int
- ov511_init_compression(struct usb_ov511 *ov511)
- {
- struct usb_device *dev = ov511->dev;
- int rc = 0;
- 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;
- }
- }
- ov511->compress_inited = 1;
- out:
- return rc;
- }
- /* Upload compression params and quantization tables. Returns 0 for success. */
- static int
- ov518_init_compression(struct usb_ov511 *ov511)
- {
- struct usb_device *dev = ov511->dev;
- int rc = 0;
- if (!ov511->compress_inited) {
- if (ov518_upload_quan_tables(dev) < 0) {
- err("Error uploading quantization tables");
- rc = -EIO;
- goto out;
- }
- }
- ov511->compress_inited = 1;
- out:
- return rc;
- }
- /* -------------------------------------------------------------------------- */
- /* Sets sensor's contrast setting to "val" */
- static int
- sensor_set_contrast(struct usb_ov511 *ov511, unsigned short val)
- {
- int rc;
- PDEBUG(3, "%d", val);
- if (ov511->stop_during_set)
- if (ov511_stop(ov511) < 0)
- return -EIO;
- switch (ov511->sensor) {
- case SEN_OV7610:
- case SEN_OV6620:
- case SEN_OV6630:
- {
- rc = ov51x_i2c_write(ov511, OV7610_REG_CNT, val >> 8);
- if (rc < 0)
- goto out;
- break;
- }
- case SEN_OV7620:
- {
- unsigned char ctab[] = {
- 0x01, 0x05, 0x09, 0x11, 0x15, 0x35, 0x37, 0x57,
- 0x5b, 0xa5, 0xa7, 0xc7, 0xc9, 0xcf, 0xef, 0xff
- };
- /* Use Y gamma control instead. Bit 0 enables it. */
- rc = ov51x_i2c_write(ov511, 0x64, ctab[val>>12]);
- if (rc < 0)
- goto out;
- break;
- }
- case SEN_SAA7111A:
- {
- rc = ov51x_i2c_write(ov511, 0x0b, val >> 9);
- if (rc < 0)
- goto out;
- break;
- }
- default:
- {
- PDEBUG(3, "Unsupported with this sensor");
- rc = -EPERM;
- goto out;
- }
- }
- rc = 0; /* Success */
- ov511->contrast = val;
- out:
- if (ov511_restart(ov511) < 0)
- return -EIO;
- return rc;
- }
- /* Gets sensor's contrast setting */
- static int
- sensor_get_contrast(struct usb_ov511 *ov511, unsigned short *val)
- {
- int rc;
- switch (ov511->sensor) {
- case SEN_OV7610:
- case SEN_OV6620:
- case SEN_OV6630:
- rc = ov51x_i2c_read(ov511, OV7610_REG_CNT);
- if (rc < 0)
- return rc;
- else
- *val = rc << 8;
- break;
- case SEN_OV7620:
- /* Use Y gamma reg instead. Bit 0 is the enable bit. */
- rc = ov51x_i2c_read(ov511, 0x64);
- if (rc < 0)
- return rc;
- else
- *val = (rc & 0xfe) << 8;
- break;
- case SEN_SAA7111A:
- *val = ov511->contrast;
- break;
- default:
- PDEBUG(3, "Unsupported with this sensor");
- return -EPERM;
- }
- PDEBUG(3, "%d", *val);
- ov511->contrast = *val;
- return 0;
- }
- /* -------------------------------------------------------------------------- */
- /* Sets sensor's brightness setting to "val" */
- static int
- sensor_set_brightness(struct usb_ov511 *ov511, unsigned short val)
- {
- int rc;
- PDEBUG(4, "%d", val);
- if (ov511->stop_during_set)
- if (ov511_stop(ov511) < 0)
- return -EIO;
- switch (ov511->sensor) {
- case SEN_OV7610:
- case SEN_OV7620AE:
- case SEN_OV6620:
- case SEN_OV6630:
- rc = ov51x_i2c_write(ov511, OV7610_REG_BRT, val >> 8);
- if (rc < 0)
- goto out;
- break;
- case SEN_OV7620:
- /* 7620 doesn't like manual changes when in auto mode */
- if (!ov511->auto_brt) {
- rc = ov51x_i2c_write(ov511, OV7610_REG_BRT, val >> 8);
- if (rc < 0)
- goto out;
- }
- break;
- case SEN_SAA7111A:
- rc = ov51x_i2c_write(ov511, 0x0a, val >> 8);
- if (rc < 0)
- goto out;
- break;
- default:
- PDEBUG(3, "Unsupported with this sensor");
- rc = -EPERM;
- goto out;
- }
- rc = 0; /* Success */
- ov511->brightness = val;
- out:
- if (ov511_restart(ov511) < 0)
- return -EIO;
- return rc;
- }
- /* Gets sensor's brightness setting */
- static int
- sensor_get_brightness(struct usb_ov511 *ov511, unsigned short *val)
- {
- int rc;
- switch (ov511->sensor) {
- case SEN_OV7610:
- case SEN_OV7620AE:
- case SEN_OV7620:
- case SEN_OV6620:
- case SEN_OV6630:
- rc = ov51x_i2c_read(ov511, OV7610_REG_BRT);
- if (rc < 0)
- return rc;
- else
- *val = rc << 8;
- break;
- case SEN_SAA7111A:
- *val = ov511->brightness;
- break;
- default:
- PDEBUG(3, "Unsupported with this sensor");
- return -EPERM;
- }
- PDEBUG(3, "%d", *val);
- ov511->brightness = *val;
- return 0;
- }
- /* -------------------------------------------------------------------------- */
- /* Sets sensor's saturation (color intensity) setting to "val" */
- static int
- sensor_set_saturation(struct usb_ov511 *ov511, unsigned short val)
- {
- int rc;
- PDEBUG(3, "%d", val);
- if (ov511->stop_during_set)
- if (ov511_stop(ov511) < 0)
- return -EIO;
- switch (ov511->sensor) {
- case SEN_OV7610:
- case SEN_OV7620AE:
- case SEN_OV6620:
- case SEN_OV6630:
- rc = ov51x_i2c_write(ov511, OV7610_REG_SAT, val >> 8);
- if (rc < 0)
- goto out;
- break;
- case SEN_OV7620:
- // /* Use UV gamma control instead. Bits 0 & 7 are reserved. */
- // rc = ov511_i2c_write(ov511->dev, 0x62, (val >> 9) & 0x7e);
- // if (rc < 0)
- // goto out;
- rc = ov51x_i2c_write(ov511, OV7610_REG_SAT, val >> 8);
- if (rc < 0)
- goto out;
- break;
- case SEN_SAA7111A:
- rc = ov51x_i2c_write(ov511, 0x0c, val >> 9);
- if (rc < 0)
- goto out;
- break;
- default:
- PDEBUG(3, "Unsupported with this sensor");
- rc = -EPERM;
- goto out;
- }
- rc = 0; /* Success */
- ov511->colour = val;
- out:
- if (ov511_restart(ov511) < 0)
- return -EIO;
- return rc;
- }
- /* Gets sensor's saturation (color intensity) setting */
- static int
- sensor_get_saturation(struct usb_ov511 *ov511, unsigned short *val)
- {
- int rc;
- switch (ov511->sensor) {
- case SEN_OV7610:
- case SEN_OV7620AE:
- case SEN_OV6620:
- case SEN_OV6630:
- rc = ov51x_i2c_read(ov511, OV7610_REG_SAT);
- if (rc < 0)
- return rc;
- else
- *val = rc << 8;
- break;
- case SEN_OV7620:
- // /* Use UV gamma reg instead. Bits 0 & 7 are reserved. */
- // rc = ov51x_i2c_read(ov511, 0x62);
- // if (rc < 0)
- // return rc;
- // else
- // *val = (rc & 0x7e) << 9;
- rc = ov51x_i2c_read(ov511, OV7610_REG_SAT);
- if (rc < 0)
- return rc;
- else
- *val = rc << 8;
- break;
- case SEN_SAA7111A:
- *val = ov511->colour;
- break;
- default:
- PDEBUG(3, "Unsupported with this sensor");
- return -EPERM;
- }
- PDEBUG(3, "%d", *val);
- ov511->colour = *val;
- return 0;
- }
- /* -------------------------------------------------------------------------- */
- /* Sets sensor's hue (red/blue balance) setting to "val" */
- static int
- sensor_set_hue(struct usb_ov511 *ov511, unsigned short val)
- {
- int rc;
- PDEBUG(3, "%d", val);
- if (ov511->stop_during_set)
- if (ov511_stop(ov511) < 0)
- return -EIO;
- switch (ov511->sensor) {
- case SEN_OV7610:
- case SEN_OV6620:
- case SEN_OV6630:
- rc = ov51x_i2c_write(ov511, OV7610_REG_RED, 0xFF - (val >> 8));
- if (rc < 0)
- goto out;
- rc = ov51x_i2c_write(ov511, OV7610_REG_BLUE, val >> 8);
- if (rc < 0)
- goto out;
- break;
- case SEN_OV7620:
- // Hue control is causing problems. I will enable it once it's fixed.
- #if 0
- rc = ov51x_i2c_write(ov511, 0x7a,
- (unsigned char)(val >> 8) + 0xb);
- if (rc < 0)
- goto out;
- rc = ov51x_i2c_write(ov511, 0x79,
- (unsigned char)(val >> 8) + 0xb);
- if (rc < 0)
- goto out;
- #endif
- break;
- case SEN_SAA7111A:
- rc = ov51x_i2c_write(ov511, 0x0d, (val + 32768) >> 8);
- if (rc < 0)
- goto out;
- break;
- default:
- PDEBUG(3, "Unsupported with this sensor");
- rc = -EPERM;
- goto out;
- }
- rc = 0; /* Success */
- ov511->hue = val;
- out:
- if (ov511_restart(ov511) < 0)
- return -EIO;
- return rc;
- }
- /* Gets sensor's hue (red/blue balance) setting */
- static int
- sensor_get_hue(struct usb_ov511 *ov511, unsigned short *val)
- {
- int rc;
- switch (ov511->sensor) {
- case SEN_OV7610:
- case SEN_OV6620:
- case SEN_OV6630:
- rc = ov51x_i2c_read(ov511, OV7610_REG_BLUE);
- if (rc < 0)
- return rc;
- else
- *val = rc << 8;
- break;
- case SEN_OV7620:
- rc = ov51x_i2c_read(ov511, 0x7a);
- if (rc < 0)
- return rc;
- else
- *val = rc << 8;
- break;
- case SEN_SAA7111A:
- *val = ov511->hue;
- break;
- default:
- PDEBUG(3, "Unsupported with this sensor");
- return -EPERM;
- }
- PDEBUG(3, "%d", *val);
- ov511->hue = *val;
- return 0;
- }
- /* -------------------------------------------------------------------------- */
- static inline int
- sensor_set_picture(struct usb_ov511 *ov511, struct video_picture *p)
- {
- int rc;
- PDEBUG(4, "sensor_set_picture");
- ov511->whiteness = p->whiteness;
- /* Don't return error if a setting is unsupported, or rest of settings
- * will not be performed */
- rc = sensor_set_contrast(ov511, p->contrast);
- if (FATAL_ERROR(rc))
- return rc;
- rc = sensor_set_brightness(ov511, p->brightness);
- if (FATAL_ERROR(rc))
- return rc;
- rc = sensor_set_saturation(ov511, p->colour);
- if (FATAL_ERROR(rc))
- return rc;
- rc = sensor_set_hue(ov511, p->hue);
- if (FATAL_ERROR(rc))
- return rc;
- return 0;
- }
- static inline int
- sensor_get_picture(struct usb_ov511 *ov511, struct video_picture *p)
- {
- int rc;
- PDEBUG(4, "sensor_get_picture");
- /* Don't return error if a setting is unsupported, or rest of settings
- * will not be performed */
- rc = sensor_get_contrast(ov511, &(p->contrast));
- if (FATAL_ERROR(rc))
- return rc;
- rc = sensor_get_brightness(ov511, &(p->brightness));
- if (FATAL_ERROR(rc))
- return rc;
- rc = sensor_get_saturation(ov511, &(p->colour));
- if (FATAL_ERROR(rc))
- return rc;
- rc = sensor_get_hue(ov511, &(p->hue));
- if (FATAL_ERROR(rc))
- return rc;
- p->whiteness = 105 << 8;
- /* Can we get these from frame[0]? -claudio? */
- p->depth = ov511->frame[0].depth;
- p->palette = ov511->frame[0].format;
- return 0;
- }
- // FIXME: Exposure range is only 0x00-0x7f in interlace mode
- /* Sets current exposure for sensor. This only has an effect if auto-exposure
- * is off */
- static inline int
- sensor_set_exposure(struct usb_ov511 *ov511, unsigned char val)
- {
- int rc;
- PDEBUG(3, "%d", val);
- if (ov511->stop_during_set)
- if (ov511_stop(ov511) < 0)
- return -EIO;
- switch (ov511->sensor) {
- case SEN_OV6620:
- case SEN_OV6630:
- case SEN_OV7610:
- case SEN_OV7620:
- case SEN_OV7620AE:
- case SEN_OV8600:
- rc = ov51x_i2c_write(ov511, 0x10, val);
- if (rc < 0)
- goto out;
- break;
- case SEN_KS0127:
- case SEN_KS0127B:
- case SEN_SAA7111A:
- PDEBUG(3, "Unsupported with this sensor");
- return -EPERM;
- default:
- err("Sensor not supported for set_exposure");
- return -EINVAL;
- }
- rc = 0; /* Success */
- ov511->exposure = val;
- out:
- if (ov511_restart(ov511) < 0)
- return -EIO;
- return rc;
- }
- /* Gets current exposure level from sensor, regardless of whether it is under
- * manual control. */
- static int
- sensor_get_exposure(struct usb_ov511 *ov511, unsigned char *val)
- {
- int rc;
- switch (ov511->sensor) {
- case SEN_OV7610:
- case SEN_OV6620:
- case SEN_OV6630:
- case SEN_OV7620:
- case SEN_OV7620AE:
- case SEN_OV8600:
- rc = ov51x_i2c_read(ov511, 0x10);
- if (rc < 0)
- return rc;
- else
- *val = rc;
- break;
- case SEN_KS0127:
- case SEN_KS0127B:
- case SEN_SAA7111A:
- val = 0;
- PDEBUG(3, "Unsupported with this sensor");
- return -EPERM;
- default:
- err("Sensor not supported for get_exposure");
- return -EINVAL;
- }
- PDEBUG(3, "%d", *val);
- ov511->exposure = *val;
- return 0;
- }
- /* Turns on or off the LED. Only has an effect with OV511+/OV518(+) */
- static inline void
- ov51x_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_OV518 ||
- 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
- * Unsupported: KS0127, KS0127B, SAA7111A
- * Returns: 0 for success
- */
- static int
- sensor_set_light_freq(struct usb_ov511 *ov511, int freq)
- {
- int sixty;
- 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;
- }
- switch (ov511->sensor) {
- case SEN_OV7610:
- ov51x_i2c_write_mask(ov511, 0x2a, sixty?0x00:0x80, 0x80);
- ov51x_i2c_write(ov511, 0x2b, sixty?0x00:0xac);
- ov51x_i2c_write_mask(ov511, 0x13, 0x10, 0x10);
- ov51x_i2c_write_mask(ov511, 0x13, 0x00, 0x10);
- break;
- case SEN_OV7620:
- case SEN_OV7620AE:
- case SEN_OV8600:
- ov51x_i2c_write_mask(ov511, 0x2a, sixty?0x00:0x80, 0x80);
- ov51x_i2c_write(ov511, 0x2b, sixty?0x00:0xac);
- ov51x_i2c_write_mask(ov511, 0x76, 0x01, 0x01);
- break;
- case SEN_OV6620:
- case SEN_OV6630:
- ov51x_i2c_write(ov511, 0x2b, sixty?0xa8:0x28);
- ov51x_i2c_write(ov511, 0x2a, sixty?0x84:0xa4);
- break;
- case SEN_KS0127:
- case SEN_KS0127B:
- case SEN_SAA7111A:
- PDEBUG(5, "Unsupported with this sensor");
- return -EPERM;
- default:
- err("Sensor not supported for set_light_freq");
- return -EINVAL;
- }
- ov511->lightfreq = freq;
- return 0;
- }
- /* 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.
- * Unsupported: KS0127, KS0127B, SAA7111A
- * Returns: 0 for success
- */
- static inline int
- sensor_set_banding_filter(struct usb_ov511 *ov511, int enable)
- {
- int rc;
- PDEBUG(4, " (%s)", enable ? "turn on" : "turn off");
- if (ov511->sensor == SEN_KS0127 || ov511->sensor == SEN_KS0127B
- || ov511->sensor == SEN_SAA7111A) {
- PDEBUG(5, "Unsupported with this sensor");
- return -EPERM;
- }
- rc = ov51x_i2c_write_mask(ov511, 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.
- *
- * Unsupported: KS0127, KS0127B, SAA7111A
- * Returns: 0 for success
- */
- static inline int
- sensor_set_auto_brightness(struct usb_ov511 *ov511, int enable)
- {
- int rc;
- PDEBUG(4, " (%s)", enable ? "turn on" : "turn off");
- if (ov511->sensor == SEN_KS0127 || ov511->sensor == SEN_KS0127B
- || ov511->sensor == SEN_SAA7111A) {
- PDEBUG(5, "Unsupported with this sensor");
- return -EPERM;
- }
- rc = ov51x_i2c_write_mask(ov511, 0x2d, enable?0x10:0x00, 0x10);
- if (rc < 0)
- return rc;
- ov511->auto_brt = enable;
- return 0;
- }
- /* If enable is true, turn on the sensor's auto exposure control, otherwise
- * turn it off.
- *
- * Unsupported: KS0127, KS0127B, SAA7111A
- * Returns: 0 for success
- */
- static inline int
- sensor_set_auto_exposure(struct usb_ov511 *ov511, int enable)
- {
- PDEBUG(4, " (%s)", enable ? "turn on" : "turn off");
- switch (ov511->sensor) {
- case SEN_OV7610:
- ov51x_i2c_write_mask(ov511, 0x29, enable?0x00:0x80, 0x80);
- break;
- case SEN_OV6620:
- case SEN_OV7620:
- case SEN_OV7620AE:
- case SEN_OV8600:
- ov51x_i2c_write_mask(ov511, 0x13, enable?0x01:0x00, 0x01);
- break;
- case SEN_OV6630:
- ov51x_i2c_write_mask(ov511, 0x28, enable?0x00:0x10, 0x10);
- break;
- case SEN_KS0127:
- case SEN_KS0127B:
- case SEN_SAA7111A:
- PDEBUG(5, "Unsupported with this sensor");
- return -EPERM;
- default:
- err("Sensor not supported for set_auto_exposure");
- return -EINVAL;
- }
- ov511->auto_exp = enable;
- return 0;
- }
- /* Modifies the sensor's exposure algorithm to allow proper exposure of objects
- * that are illuminated from behind.
- *
- * Tested with: OV6620, OV7620
- * Unsupported: OV7610, OV7620AE, KS0127, KS0127B, SAA7111A
- * Returns: 0 for success
- */
- static int
- sensor_set_backlight(struct usb_ov511 *ov511, int enable)
- {
- PDEBUG(4, " (%s)", enable ? "turn on" : "turn off");
- switch (ov511->sensor) {
- case SEN_OV7620:
- case SEN_OV8600:
- ov51x_i2c_write_mask(ov511, 0x68, enable?0xe0:0xc0, 0xe0);
- ov51x_i2c_write_mask(ov511, 0x29, enable?0x08:0x00, 0x08);
- ov51x_i2c_write_mask(ov511, 0x28, enable?0x02:0x00, 0x02);
- break;
- case SEN_OV6620:
- ov51x_i2c_write_mask(ov511, 0x4e, enable?0xe0:0xc0, 0xe0);
- ov51x_i2c_write_mask(ov511, 0x29, enable?0x08:0x00, 0x08);
- ov51x_i2c_write_mask(ov511, 0x0e, enable?0x80:0x00, 0x80);
- break;
- case SEN_OV6630:
- ov51x_i2c_write_mask(ov511, 0x4e, enable?0x80:0x60, 0xe0);
- ov51x_i2c_write_mask(ov511, 0x29, enable?0x08:0x00, 0x08);
- ov51x_i2c_write_mask(ov511, 0x28, enable?0x02:0x00, 0x02);
- break;
- case SEN_OV7610:
- case SEN_OV7620AE:
- case SEN_KS0127:
- case SEN_KS0127B:
- case SEN_SAA7111A:
- PDEBUG(5, "Unsupported with this sensor");
- return -EPERM;
- default:
- err("Sensor not supported for set_backlight");
- return -EINVAL;
- }
- ov511->backlight = enable;
- return 0;
- }
- /* Returns number of bits per pixel (regardless of where they are located;
- * planar or not), or zero for unsupported format.
- */
- static inline int
- ov511_get_depth(int palette)
- {
- switch (palette) {
- case VIDEO_PALETTE_GREY: return 8;
- case VIDEO_PALETTE_RGB565: return 16;
- case VIDEO_PALETTE_RGB24: return 24;
- case VIDEO_PALETTE_YUV422: return 16;
- case VIDEO_PALETTE_YUYV: return 16;
- case VIDEO_PALETTE_YUV420: return 12;
- case VIDEO_PALETTE_YUV422P: return 16; /* Planar */
- case VIDEO_PALETTE_YUV420P: return 12; /* Planar */
- default: return 0; /* Invalid format */
- }
- }
- /* Bytes per frame. Used by read(). Return of 0 indicates error */
- static inline long int
- get_frame_length(struct ov511_frame *frame)
- {
- if (!frame)
- return 0;
- else
- return ((frame->width * frame->height
- * ov511_get_depth(frame->format)) >> 3);
- }
- static int
- mode_init_ov_sensor_regs(struct usb_ov511 *ov511, int width, int height,
- int mode, int sub_flag, int qvga)
- {
- int clock;
- /******** Mode (VGA/QVGA) and sensor specific regs ********/
- switch (ov511->sensor) {
- case SEN_OV7610:
- ov51x_i2c_write(ov511, 0x14, qvga?0x24:0x04);
- // FIXME: Does this improve the image quality or frame rate?
- #if 0
- ov51x_i2c_write_mask(ov511, 0x28, qvga?0x00:0x20, 0x20);
- ov51x_i2c_write(ov511, 0x24, 0x10);
- ov51x_i2c_write(ov511, 0x25, qvga?0x40:0x8a);
- ov51x_i2c_write(ov511, 0x2f, qvga?0x30:0xb0);
- ov51x_i2c_write(ov511, 0x35, qvga?0x1c:0x9c);
- #endif
- break;
- case SEN_OV7620:
- // ov51x_i2c_write(ov511, 0x2b, 0x00);
- ov51x_i2c_write(ov511, 0x14, qvga?0xa4:0x84);
- ov51x_i2c_write_mask(ov511, 0x28, qvga?0x00:0x20, 0x20);
- ov51x_i2c_write(ov511, 0x24, qvga?0x20:0x3a);
- ov51x_i2c_write(ov511, 0x25, qvga?0x30:0x60);
- ov51x_i2c_write_mask(ov511, 0x2d, qvga?0x40:0x00, 0x40);
- ov51x_i2c_write_mask(ov511, 0x67, qvga?0xf0:0x90, 0xf0);
- ov51x_i2c_write_mask(ov511, 0x74, qvga?0x20:0x00, 0x20);
- break;
- case SEN_OV7620AE:
- // ov51x_i2c_write(ov511, 0x2b, 0x00);
- ov51x_i2c_write(ov511, 0x14, qvga?0xa4:0x84);
- // FIXME: Enable this once 7620AE uses 7620 initial settings
- #if 0
- ov51x_i2c_write_mask(ov511, 0x28, qvga?0x00:0x20, 0x20);
- ov51x_i2c_write(ov511, 0x24, qvga?0x20:0x3a);
- ov51x_i2c_write(ov511, 0x25, qvga?0x30:0x60);
- ov51x_i2c_write_mask(ov511, 0x2d, qvga?0x40:0x00, 0x40);
- ov51x_i2c_write_mask(ov511, 0x67, qvga?0xb0:0x90, 0xf0);
- ov51x_i2c_write_mask(ov511, 0x74, qvga?0x20:0x00, 0x20);
- #endif
- break;
- case SEN_OV6620:
- case SEN_OV6630:
- ov51x_i2c_write(ov511, 0x14, qvga?0x24:0x04);
- /* No special settings yet */
- break;
- default:
- err("Invalid sensor");
- return -EINVAL;
- }
- /******** Palette-specific regs ********/
- if (mode == VIDEO_PALETTE_GREY) {
- if (ov511->sensor == SEN_OV7610
- || ov511->sensor == SEN_OV7620AE) {
- /* these aren't valid on the OV6620/OV7620/6630? */
- ov51x_i2c_write_mask(ov511, 0x0e, 0x40, 0x40);
- }
- ov51x_i2c_write_mask(ov511, 0x13, 0x20, 0x20);
- } else {
- if (ov511->sensor == SEN_OV7610
- || ov511->sensor == SEN_OV7620AE) {
- /* not valid on the OV6620/OV7620/6630? */
- ov51x_i2c_write_mask(ov511, 0x0e, 0x00, 0x40);
- }
- ov51x_i2c_write_mask(ov511, 0x13, 0x00, 0x20);
- }
- /******** Clock programming ********/
- // FIXME: Test this with OV6630
- /* The OV6620 needs special handling. This prevents the
- * severe banding that normally occurs */
- if (ov511->sensor == SEN_OV6620 || ov511->sensor == SEN_OV6630)
- {
- /* Clock down */
- ov51x_i2c_write(ov511, 0x2a, 0x04);
- if (ov511->compress) {
- // clock = 0; /* This ensures the highest frame rate */
- clock = 3;
- } else if (clockdiv == -1) { /* If user didn't override it */
- clock = 3; /* Gives better exposure time */
- } else {
- clock = clockdiv;
- }
- PDEBUG(4, "Setting clock divisor to %d", clock);
- ov51x_i2c_write(ov511, 0x11, clock);
- ov51x_i2c_write(ov511, 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. */
- ov51x_i2c_write(ov511, 0x2d, 0x85);
- }
- else
- {
- if (ov511->compress) {
- clock = 1; /* This ensures the highest frame rate */
- } else if (clockdiv == -1) { /* If user didn't override it */
- /* Calculate and set the clock divisor */
- clock = ((sub_flag ? ov511->subw * ov511->subh
- : width * height)
- * (mode == VIDEO_PALETTE_GREY ? 2 : 3) / 2)
- / 66000;
- } else {
- clock = clockdiv;
- }
- PDEBUG(4, "Setting clock divisor to %d", clock);
- ov51x_i2c_write(ov511, 0x11, clock);
- }
- /******** Special Features ********/
- if (framedrop >= 0)
- ov51x_i2c_write(ov511, 0x16, framedrop);
- /* We only have code to convert GBR -> RGB24 */
- if ((mode == VIDEO_PALETTE_RGB24) && sensor_gbr)
- ov51x_i2c_write_mask(ov511, 0x12, 0x08, 0x08);
- else
- ov51x_i2c_write_mask(ov511, 0x12, 0x00, 0x08);
- /* Test Pattern */
- ov51x_i2c_write_mask(ov511, 0x12, (testpat?0x02:0x00), 0x02);
- /* Auto white balance */
- // if (awb)
- ov51x_i2c_write_mask(ov511, 0x12, 0x04, 0x04);
- // else
- // ov51x_i2c_write_mask(ov511, 0x12, 0x00, 0x04);
- // This will go away as soon as ov511_mode_init_sensor_regs()
- // is fully tested.
- /* 7620/6620/6630? don't have register 0x35, so play it safe */
- if (ov511->sensor == SEN_OV7610 ||
- ov511->sensor == SEN_OV7620AE) {
- if (width == 640 && height == 480)
- ov51x_i2c_write(ov511, 0x35, 0x9e);
- else
- ov51x_i2c_write(ov511, 0x35, 0x1e);
- }
- return 0;
- }
- static int
- set_ov_sensor_window(struct usb_ov511 *ov511, int width, int height, int mode,
- int sub_flag)
- {
- int ret;
- int hwsbase, hwebase, vwsbase, vwebase, hwsize, vwsize;
- int hoffset, voffset, hwscale = 0, vwscale = 0;
- /* The different sensor ICs handle setting up of window differently.
- * IF YOU SET IT WRONG, YOU WILL GET ALL ZERO ISOC DATA FROM OV51x!!! */
- 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 = 0x2f; /* From 7620.SET (spec is wrong) */
- hwebase = 0x2f;
- vwsbase = vwebase = 0x05;
- break;
- default:
- err("Invalid sensor");
- return -EINVAL;
- }
- if (ov511->sensor == SEN_OV6620 || ov511->sensor == SEN_OV6630) {
- if (width > 176 && height > 144) { /* CIF */
- ret = mode_init_ov_sensor_regs(ov511, width, height,
- mode, sub_flag, 0);
- if (ret < 0)
- return ret;
- hwscale = 1;
- vwscale = 1; /* The datasheet says 0; it's wrong */
- hwsize = 352;
- vwsize = 288;
- } else if (width > 176 || height > 144) {
- err("Illegal dimensions");
- return -EINVAL;
- } else { /* QCIF */
- ret = mode_init_ov_sensor_regs(ov511, width, height,
- mode, sub_flag, 1);
- if (ret < 0)
- return ret;
- hwsize = 176;
- vwsize = 144;
- }
- } else {
- if (width > 320 && height > 240) { /* VGA */
- ret = mode_init_ov_sensor_regs(ov511, width, height,
- mode, sub_flag, 0);
- if (ret < 0)
- return ret;
- hwscale = 2;
- vwscale = 1;
- hwsize = 640;
- vwsize = 480;
- } else if (width > 320 || height > 240) {
- err("Illegal dimensions");
- return -EINVAL;
- } else { /* QVGA */
- ret = mode_init_ov_sensor_regs(ov511, width, height,
- mode, sub_flag, 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) {
- ov51x_i2c_write(ov511, 0x17, hwsbase+(ov511->subx>>hwscale));
- ov51x_i2c_write(ov511, 0x18,
- hwebase+((ov511->subx+ov511->subw)>>hwscale));
- ov51x_i2c_write(ov511, 0x19, vwsbase+(ov511->suby>>vwscale));
- ov51x_i2c_write(ov511, 0x1a,
- vwebase+((ov511->suby+ov511->subh)>>vwscale));
- } else {
- ov51x_i2c_write(ov511, 0x17, hwsbase + hoffset);
- ov51x_i2c_write(ov511, 0x18,
- hwebase + hoffset + (hwsize>>hwscale));
- ov51x_i2c_write(ov511, 0x19, vwsbase + voffset);
- ov51x_i2c_write(ov511, 0x1a,
- vwebase + voffset + (vwsize>>vwscale));
- }
- #ifdef OV511_DEBUG
- if (dump_sensor)
- ov51x_dump_i2c_regs(ov511);
- #endif
- return 0;
- }
- /* Set up the OV511/OV511+ with the given image parameters.
- *
- * Do not put any sensor-specific code in here (including I2C I/O functions)
- */
- static int
- ov511_mode_init_regs(struct usb_ov511 *ov511,
- int width, int height, int mode, int sub_flag)
- {
- int lncnt, pxcnt, rc = 0;
- struct usb_device *dev = ov511->dev;
- if (!ov511 || !dev)
- return -EFAULT;
- if (sub_flag) {
- width = ov511->subw;
- height = ov511->subh;
- }
- PDEBUG(3, "width:%d, height:%d, mode:%d, sub:%d",
- width, height, mode, sub_flag);
- // FIXME: This should be moved to a 7111a-specific function once
- // subcapture is dealt with properly
- if (ov511->sensor == SEN_SAA7111A) {
- if (width == 320 && height == 240) {
- /* No need to do anything special */
- } else if (width == 640 && height == 480) {
- /* Set the OV511 up as 320x480, but keep the V4L
- * resolution as 640x480 */
- width = 320;
- } else {
- err("SAA7111A only supports 320x240 or 640x480");
- return -EINVAL;
- }
- }
- /* Make sure width and height are a multiple of 8 */
- if (width % 8 || height % 8) {
- err("Invalid size (%d, %d) (mode = %d)", width, height, mode);
- return -EINVAL;
- }
- if (width < ov511->minwidth || height < ov511->minheight) {
- err("Requested dimensions are too small");
- return -EINVAL;
- }
- if (ov511_stop(ov511) < 0)
- return -EIO;
- if (mode == VIDEO_PALETTE_GREY) {
- ov511_reg_write(dev, 0x16, 0x00);
- /* For snapshot */
- ov511_reg_write(dev, 0x1e, 0x00);
- ov511_reg_write(dev, 0x1f, 0x01);
- } else {
- ov511_reg_write(dev, 0x16, 0x01);
- /* For snapshot */
- ov511_reg_write(dev, 0x1e, 0x01);
- ov511_reg_write(dev, 0x1f, 0x03);
- }
- /* Here I'm assuming that snapshot size == image size.
- * I hope that's always true. --claudio
- */
- pxcnt = (width >> 3) - 1;
- lncnt = (height >> 3) - 1;
- ov511_reg_write(dev, 0x12, pxcnt);
- ov511_reg_write(dev, 0x13, lncnt);
- ov511_reg_write(dev, 0x14, 0x00);
- ov511_reg_write(dev, 0x15, 0x00);
- ov511_reg_write(dev, 0x18, 0x03); /* YUV420, low pass filer on */
- /* Snapshot additions */
- ov511_reg_write(dev, 0x1a, pxcnt);
- ov511_reg_write(dev, 0x1b, lncnt);
- ov511_reg_write(dev, 0x1c, 0x00);
- ov511_reg_write(dev, 0x1d, 0x00);
- if (ov511->compress) {
- ov511_reg_write(dev, 0x78, 0x07); // Turn on Y & UV compression
- ov511_reg_write(dev, 0x79, 0x03); // Enable LUTs
- ov511_reset(ov511, OV511_RESET_OMNICE);
- }
- //out:
- if (ov511_restart(ov511) < 0)
- return -EIO;
- return rc;
- }
- static struct mode_list_518 mlist518[] = {
- /* W H reg28 reg29 reg2a reg2c reg2e reg24 reg25 */
- { 352, 288, 0x00, 0x16, 0x48, 0x00, 0x00, 0x9f, 0x90 },
- { 320, 240, 0x00, 0x14, 0x3c, 0x10, 0x18, 0x9f, 0x90 },
- { 176, 144, 0x05, 0x0b, 0x24, 0x00, 0x00, 0xff, 0xf0 },
- { 160, 120, 0x05, 0x0a, 0x1e, 0x08, 0x0c, 0xff, 0xf0 },
- { 0, 0 }
- };
- /* Sets up the OV518/OV518+ with the given image parameters
- *
- * 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.
- *
- * Do not put any sensor-specific code in here (including I2C I/O functions)
- */
- 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;
- 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;
- for (i = 0; mlist518[i].width; i++) {
- // int lncnt, pxcnt;
- 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
- | (mode == VIDEO_PALETTE_GREY) ? 0x80:0x00, 0x8f);
- 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
- | (mode == VIDEO_PALETTE_GREY) ? 0x80:0x00, 0x8f);
- 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? */
- // FIXME: Sensor-specific
- /* Bit 5 is what matters here. Of course, it is "reserved" */
- ov51x_i2c_write(ov511, 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);
- break;
- }
- if (ov511_restart(ov511) < 0)
- return -EIO;
- /* Reset it just for good measure */
- if (ov511_reset(ov511, OV511_RESET_NOREGS) < 0)
- return -EIO;
- if (mlist518[i].width == 0) {
- err("Unknown mode (%d, %d): %d", width, height, mode);
- return -EINVAL;
- }
- 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)
- {
- int rc = 0;
- if (ov511->bridge == BRG_OV518 ||
- ov511->bridge == BRG_OV518PLUS) {
- rc = ov518_mode_init_regs(ov511, width, height, mode, sub_flag);
- } else {
- rc = ov511_mode_init_regs(ov511, width, height, mode, sub_flag);
- }
- if (FATAL_ERROR(rc))
- return rc;
- switch (ov511->sensor) {
- case SEN_OV7610:
- case SEN_OV7620:
- case SEN_OV7620AE:
- case SEN_OV8600:
- case SEN_OV6620:
- case SEN_OV6630:
- rc = set_ov_sensor_window(ov511, width, height, mode, sub_flag);
- break;
- case SEN_KS0127:
- case SEN_KS0127B:
- err("KS0127-series decoders not supported yet");
- rc = -EINVAL;
- break;
- case SEN_SAA7111A:
- // rc = mode_init_saa_sensor_regs(ov511, width, height, mode,
- // sub_flag);
- PDEBUG(1, "SAA status = 0X%x", ov51x_i2c_read(ov511, 0x1f));
- break;
- default:
- err("Unknown sensor");
- rc = -EINVAL;
- }
- if (FATAL_ERROR(rc))
- return rc;
- /* Sensor-independent settings */
- rc = sensor_set_auto_brightness(ov511, ov511->auto_brt);
- if (FATAL_ERROR(rc))
- return rc;
- rc = sensor_set_auto_exposure(ov511, ov511->auto_exp);
- if (FATAL_ERROR(rc))
- return rc;
- rc = sensor_set_banding_filter(ov511, bandingfilter);
- if (FATAL_ERROR(rc))
- return rc;
- if (ov511->lightfreq) {
- rc = sensor_set_light_freq(ov511, lightfreq);
- if (FATAL_ERROR(rc))
- return rc;
- }
- rc = sensor_set_backlight(ov511, ov511->backlight);
- if (FATAL_ERROR(rc))
- return rc;
- return 0;
- }
- /* 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;
- PDEBUG(3, "%dx%d, RGB24", ov511->maxwidth, ov511->maxheight);
- /* 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].bytes_read = 0;
- if (force_palette)
- ov511->frame[i].format = force_palette;
- else
- ov511->frame[i].format = VIDEO_PALETTE_RGB24;
- ov511->frame[i].depth = ov511_get_depth(ov511->frame[i].format);
- }
- /* Initialize to max width/height, RGB24 */
- if (mode_init_regs(ov511, ov511->maxwidth, ov511->maxheight,
- ov511->frame[0].format, 0) < 0)
- return -EINVAL;
- return 0;
- }
- /**********************************************************************
- *
- * Video decoder stuff
- *
- **********************************************************************/
- /* Set analog input port of decoder */
- static int
- decoder_set_input(struct usb_ov511 *ov511, int input)
- {
- PDEBUG(4, "port %d", input);
- switch (ov511->sensor) {
- case SEN_SAA7111A:
- {
- /* Select mode */
- ov51x_i2c_write_mask(ov511, 0x02, input, 0x07);
- /* Bypass chrominance trap for modes 4..7 */
- ov51x_i2c_write_mask(ov511, 0x09,
- (input > 3) ? 0x80:0x00, 0x80);
- break;
- }
- default:
- return -EINVAL;
- }
- return 0;
- }
- /* Get ASCII name of video input */
- static int
- decoder_get_input_name(struct usb_ov511 *ov511, int input, char *name)
- {
- switch (ov511->sensor) {
- case SEN_SAA7111A:
- {
- if (input < 0 || input > 7)
- return -EINVAL;
- else if (input < 4)
- sprintf(name, "CVBS-%d", input);
- else // if (input < 8)
- sprintf(name, "S-Video-%d", input - 4);
- break;
- }
- default:
- sprintf(name, "%s", "Camera");
- }
- return 0;
- }
- /* Set norm (NTSC, PAL, SECAM, AUTO) */
- static int
- decoder_set_norm(struct usb_ov511 *ov511, int norm)
- {
- PDEBUG(4, "%d", norm);
- switch (ov511->sensor) {
- case SEN_SAA7111A:
- {
- int reg_8, reg_e;
- if (norm == VIDEO_MODE_NTSC) {
- reg_8 = 0x40; /* 60 Hz */
- reg_e = 0x00; /* NTSC M / PAL BGHI */
- } else if (norm == VIDEO_MODE_PAL) {
- reg_8 = 0x00; /* 50 Hz */
- reg_e = 0x00; /* NTSC M / PAL BGHI */
- } else if (norm == VIDEO_MODE_AUTO) {
- reg_8 = 0x80; /* Auto field detect */
- reg_e = 0x00; /* NTSC M / PAL BGHI */
- } else if (norm == VIDEO_MODE_SECAM) {
- reg_8 = 0x00; /* 50 Hz */
- reg_e = 0x50; /* SECAM / PAL 4.43 */
- } else {
- return -EINVAL;
- }
- ov51x_i2c_write_mask(ov511, 0x08, reg_8, 0xc0);
- ov51x_i2c_write_mask(ov511, 0x0e, reg_e, 0x70);
- break;
- }
- default:
- return -EINVAL;
- }
- return 0;
- }
- /**********************************************************************
- *
- * Color correction functions
- *
- **********************************************************************/
- /*
- * Turn a YUV4:2:0 block into an RGB block
- *
- * Video4Linux seems to use the blue, green, red channel
- * order convention-- rgb[0] is blue, rgb[1] is green, rgb[2] is red.
- *
- * Color space conversion coefficients taken from the excellent
- * http://www.inforamp.net/~poynton/ColorFAQ.html
- * In his terminology, this is a CCIR 601.1 YCbCr -> RGB.
- * Y values are given for all 4 pixels, but the U (Pb)
- * and V (Pr) are assumed constant over the 2x2 block.
- *
- * To avoid floating point arithmetic, the color conversion
- * coefficients are scaled into 16.16 fixed-point integers.
- * They were determined as follows:
- *
- * double brightness = 1.0; (0->black; 1->full scale)
- * double saturation = 1.0; (0->greyscale; 1->full color)
- * double fixScale = brightness * 256 * 256;
- * int rvScale = (int)(1.402 * saturation * fixScale);
- * int guScale = (int)(-0.344136 * saturation * fixScale);
- * int gvScale = (int)(-0.714136 * saturation * fixScale);
- * int buScale = (int)(1.772 * saturation * fixScale);
- * int yScale = (int)(fixScale);
- */
- /* LIMIT: convert a 16.16 fixed-point value to a byte, with clipping. */
- #define LIMIT(x) ((x)>0xffffff?0xff: ((x)<=0xffff?0:((x)>>16)))
- static inline void
- ov511_move_420_block(int yTL, int yTR, int yBL, int yBR, int u, int v,
- int rowPixels, unsigned char * rgb, int bits)
- {
- const int rvScale = 91881;
- const int guScale = -22553;
- const int gvScale = -46801;
- const int buScale = 116129;
- const int yScale = 65536;
- int r, g, b;
- g = guScale * u + gvScale * v;
- if (force_rgb) {
- r = buScale * u;
- b = rvScale * v;
- } else {
- r = rvScale * v;
- b = buScale * u;
- }
- yTL *= yScale; yTR *= yScale;
- yBL *= yScale; yBR *= yScale;
- if (bits == 24) {
- /* Write out top two pixels */
- rgb[0] = LIMIT(b+yTL); rgb[1] = LIMIT(g+yTL);
- rgb[2] = LIMIT(r+yTL);
- rgb[3] = LIMIT(b+yTR); rgb[4] = LIMIT(g+yTR);
- rgb[5] = LIMIT(r+yTR);
- /* Skip down to next line to write out bottom two pixels */
- rgb += 3 * rowPixels;
- rgb[0] = LIMIT(b+yBL); rgb[1] = LIMIT(g+yBL);
- rgb[2] = LIMIT(r+yBL);
- rgb[3] = LIMIT(b+yBR); rgb[4] = LIMIT(g+yBR);
- rgb[5] = LIMIT(r+yBR);
- } else if (bits == 16) {
- /* Write out top two pixels */
- rgb[0] = ((LIMIT(b+yTL) >> 3) & 0x1F)
- | ((LIMIT(g+yTL) << 3) & 0xE0);
- rgb[1] = ((LIMIT(g+yTL) >> 5) & 0x07)
- | (LIMIT(r+yTL) & 0xF8);
- rgb[2] = ((LIMIT(b+yTR) >> 3) & 0x1F)
- | ((LIMIT(g+yTR) << 3) & 0xE0);
- rgb[3] = ((LIMIT(g+yTR) >> 5) & 0x07)
- | (LIMIT(r+yTR) & 0xF8);
- /* Skip down to next line to write out bottom two pixels */
- rgb += 2 * rowPixels;
- rgb[0] = ((LIMIT(b+yBL) >> 3) & 0x1F)
- | ((LIMIT(g+yBL) << 3) & 0xE0);
- rgb[1] = ((LIMIT(g+yBL) >> 5) & 0x07)
- | (LIMIT(r+yBL) & 0xF8);
- rgb[2] = ((LIMIT(b+yBR) >> 3) & 0x1F)
- | ((LIMIT(g+yBR) << 3) & 0xE0);
- rgb[3] = ((LIMIT(g+yBR) >> 5) & 0x07)
- | (LIMIT(r+yBR) & 0xF8);
- }
- }
- /**********************************************************************
- *
- * Raw data parsing
- *
- **********************************************************************/
- /* Copies a 64-byte segment at pIn to an 8x8 block at pOut. The width of the
- * array at pOut is specified by w.
- */
- static inline void
- ov511_make_8x8(unsigned char *pIn, unsigned char *pOut, int w)
- {
- unsigned char *pOut1 = pOut;
- int x, y;
- for (y = 0; y < 8; y++) {
- pOut1 = pOut;
- for (x = 0; x < 8; x++) {
- *pOut1++ = *pIn++;
- }
- pOut += w;
- }
-
- }
- /*
- * For RAW BW (YUV400) images, data shows up in 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
- yuv400raw_to_yuv400p(struct ov511_frame *frame,
- unsigned char *pIn0, unsigned char *pOut0)
- {
- int x, y;
- unsigned char *pIn, *pOut, *pOutLine;
- /* Copy Y */
- pIn = pIn0;
- pOutLine = pOut0;
- for (y = 0; y < frame->rawheight - 1; y += 8) {
- pOut = pOutLine;
- for (x = 0; x < frame->rawwidth - 1; x += 8) {
- ov511_make_8x8(pIn, pOut, frame->rawwidth);
- pIn += 64;
- pOut += 8;
- }
- pOutLine += 8 * frame->rawwidth;
- }
- }
- /*
- * For YUV4:2:0 images, the data shows up in 384 byte segments.
- * The first 64 bytes of each segment are U, the next 64 are V. The U and
- * V are arranged as follows:
- *
- * 0 1 ... 7
- * 8 9 ... 15
- * ...
- * 56 57 ... 63
- *
- * U and V are shipped at half resolution (1 U,V sample -> one 2x2 block).
- *
- * The next 256 bytes are full resolution Y data and 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
- *
- * Note that the U and V data in one segment represents a 16 x 16 pixel
- * area, but the Y data represents a 32 x 8 pixel area. If the width is not an
- * even multiple of 32, the extra 8x8 blocks within a 32x8 block belong to the
- * next horizontal stripe.
- *
- * If dumppix module param is set, _parse_data just dumps the incoming segments,
- * verbatim, in order, into the frame. When used with vidcat -f ppm -s 640x480
- * this puts the data on the standard output and can be analyzed with the
- * parseppm.c utility I wrote. That's a much faster way for figuring out how
- * this data is scrambled.
- */
- /* Converts from raw, uncompressed segments at pIn0 to a YUV420P frame at pOut0.
- *
- * FIXME: Currently only handles width and height that are multiples of 16
- */
- static void
- yuv420raw_to_yuv420p(struct ov511_frame *frame,
- unsigned char *pIn0, unsigned char *pOut0)
- {
- int k, x, y;
- unsigned char *pIn, *pOut, *pOutLine;
- const unsigned int a = frame->rawwidth * frame->rawheight;
- const unsigned int w = frame->rawwidth / 2;
- /* Copy U and V */
- pIn = pIn0;
- pOutLine = pOut0 + a;
- for (y = 0; y < frame->rawheight - 1; y += 16) {
- pOut = pOutLine;
- for (x = 0; x < frame->rawwidth - 1; x += 16) {
- ov511_make_8x8(pIn, pOut, w);
- ov511_make_8x8(pIn + 64, pOut + a/4, w);
- pIn += 384;
- pOut += 8;
- }
- pOutLine += 8 * w;
- }
- /* Copy Y */
- pIn = pIn0 + 128;
- pOutLine = pOut0;
- k = 0;
- for (y = 0; y < frame->rawheight - 1; y += 8) {
- pOut = pOutLine;
- for (x = 0; x < frame->rawwidth - 1; x += 8) {
- ov511_make_8x8(pIn, pOut, frame->rawwidth);
- pIn += 64;
- pOut += 8;
- if ((++k) > 3) {
- k = 0;
- pIn += 128;
- }
- }
- pOutLine += 8 * frame->rawwidth;
- }
- }
- /*
- * 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;
- /* This only works with RGB24 */
- if (frame->format != VIDEO_PALETTE_RGB24)
- 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
- *
- **********************************************************************/
- /* Chooses a decompression module, locks it, and sets ov511->decomp_ops
- * accordingly. Returns -ENXIO if decompressor is not available, otherwise
- * returns 0 if no other error.
- */
- static int
- ov51x_request_decompressor(struct usb_ov511 *ov511)
- {
- if (!ov511)
- return -ENODEV;
- if (ov511->decomp_ops) {
- err("ERROR: Decompressor already requested!");
- return -EINVAL;
- }
- lock_kernel();
- /* Try to get MMX, and fall back on no-MMX if necessary */
- if (ov511->bridge == BRG_OV511 || ov511->bridge == BRG_OV511PLUS) {
- if (ov511_mmx_decomp_ops) {
- PDEBUG(3, "Using OV511 MMX decompressor");
- ov511->decomp_ops = ov511_mmx_decomp_ops;
- } else if (ov511_decomp_ops) {
- PDEBUG(3, "Using OV511 decompressor");
- ov511->decomp_ops = ov511_decomp_ops;
- } else {
- err("No decompressor available");
- }
- } else if (ov511->bridge == BRG_OV518 ||
- ov511->bridge == BRG_OV518PLUS) {
- if (ov518_mmx_decomp_ops) {
- PDEBUG(3, "Using OV518 MMX decompressor");
- ov511->decomp_ops = ov518_mmx_decomp_ops;
- } else if (ov518_decomp_ops) {
- PDEBUG(3, "Using OV518 decompressor");
- ov511->decomp_ops = ov518_decomp_ops;
- } else {
- err("No decompressor available");
- }
- } else {
- err("Unknown bridge");