📄 ov511.c
字号:
/* * OmniVision OV511 Camera-to-USB Bridge Driver * Copyright 1999 Mark W. McClelland * * Based on the Linux CPiA driver. * * Released under GPL v.2 license. * * Important keywords in comments: * CAMERA SPECIFIC - Camera specific code; may not work with other cameras. * DEBUG - Debugging code. * FIXME - Something that is broken or needs improvement. * * Version History: * Version 1.00 - Initial version *//* * 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. */#define __NO_VERSION__/* Handle mangled (versioned) external symbols */#include <linux/config.h> /* retrieve the CONFIG_* macros */#if defined(CONFIG_MODVERSIONS) && !defined(MODVERSIONS)# define MODVERSIONS /* force it on */#endif#ifdef MODVERSIONS#include <linux/modversions.h>#endif#include <linux/kernel.h>#include <linux/sched.h>#include <linux/list.h>#include <linux/malloc.h>#include <linux/mm.h>#include <linux/smp_lock.h>#include <linux/videodev.h>#include <linux/vmalloc.h>#include <linux/wrapper.h>#include <linux/module.h>#include <linux/spinlock.h>#include <asm/io.h>#include "usb.h"#include "ov511.h"#define OV511_I2C_RETRIES 3/* Video Size 640 x 480 x 3 bytes for RGB */#define MAX_FRAME_SIZE (640 * 480 * 3)// FIXME - Force CIF to make some apps happy for the moment. Should find a // better way to do this.#define DEFAULT_WIDTH 640#define DEFAULT_HEIGHT 480char kernel_version[] = UTS_RELEASE;/*******************************//* Memory management functions *//*******************************/#define MDEBUG(x) do { } while(0) /* Debug memory management */static struct usb_driver ov511_driver;/* 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 = page_address(pte_page(pte)) | (adr & (PAGE_SIZE-1)); } } MDEBUG(printk("uv2kva(%lx-->%lx)", adr, ret)); return ret;}static inline unsigned long uvirt_to_bus(unsigned long adr){ unsigned long kva, ret; kva = uvirt_to_kva(pgd_offset(current->mm, adr), adr); ret = virt_to_bus((void *)kva); MDEBUG(printk("uv2b(%lx-->%lx)", adr, ret)); return ret;}static inline unsigned long kvirt_to_bus(unsigned long adr){ unsigned long va, kva, ret; va = VMALLOC_VMADDR(adr); kva = uvirt_to_kva(pgd_offset_k(va), va); ret = virt_to_bus((void *)kva); MDEBUG(printk("kv2b(%lx-->%lx)", adr, ret)); 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); MDEBUG(printk("kv2pa(%lx-->%lx)", adr, ret)); 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(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(MAP_NR(__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(MAP_NR(__va(page))); adr += PAGE_SIZE; if (size > PAGE_SIZE) size -= PAGE_SIZE; else size = 0; } vfree(mem);}int ov511_reg_write(struct usb_device *dev, unsigned char reg, unsigned char value){ int rc; 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 0 PDEBUG("reg write: 0x%02X:0x%02X\n", reg, value);#endif return rc;}/* returns: negative is error, pos or zero is data */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); #if 0 PDEBUG("reg read: 0x%02X:0x%02X\n", reg, buffer[0]);#endif if(rc < 0) return rc; else return buffer[0]; }int ov511_i2c_write(struct usb_device *dev, unsigned char reg, unsigned char value){ int rc, retries;#if 0 PDEBUG("i2c write: 0x%02X:0x%02X\n", reg, value);#endif /* 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) return rc; /* Write "value" to I2C data port of OV511 */ rc = ov511_reg_write(dev, OV511_REG_I2C_DATA_PORT, value); if (rc < 0) return rc; /* Initiate 3-byte write cycle */ rc = ov511_reg_write(dev, OV511_REG_I2C_CONTROL, 0x01); if (rc < 0) return rc; do rc = ov511_reg_read(dev, OV511_REG_I2C_CONTROL); while(rc > 0 && ((rc&1) == 0)); /* Retry until idle */ if (rc < 0) return rc; if((rc&2) == 0) /* Ack? */ break; /* I2C abort */ ov511_reg_write(dev, OV511_REG_I2C_CONTROL, 0x10); if (--retries < 0) return -1; } return 0;}/* returns: negative is error, pos or zero is data */int ov511_i2c_read(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) return rc; /* Initiate 2-byte write cycle */ rc = ov511_reg_write(dev, OV511_REG_I2C_CONTROL, 0x03); if (rc < 0) return rc; do rc = ov511_reg_read(dev, OV511_REG_I2C_CONTROL); while(rc > 0 && ((rc&1) == 0)); /* Retry until idle */ if (rc < 0) return rc; if((rc&2) == 0) /* Ack? */ break; /* I2C abort */ ov511_reg_write(dev, OV511_REG_I2C_CONTROL, 0x10); if (--retries < 0) return -1; } /* 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) return rc; do rc = ov511_reg_read(dev, OV511_REG_I2C_CONTROL); while(rc > 0 && ((rc&1) == 0)); /* Retry until idle */ if (rc < 0) return rc; if((rc&2) == 0) /* Ack? */ break; /* I2C abort */ rc = ov511_reg_write(dev, OV511_REG_I2C_CONTROL, 0x10); if (rc < 0) return rc; if (--retries < 0) return -1; } value = ov511_reg_read(dev, OV511_REG_I2C_DATA_PORT);#if 0 PDEBUG("i2c read: 0x%02X:0x%02X\n", reg, value);#endif /* This is needed to make ov511_i2c_write() work */ rc = ov511_reg_write(dev, OV511_REG_I2C_CONTROL, 0x05); if (rc < 0) return rc; return (value);}#if 0static void ov511_dump_i2c_range( struct usb_device *dev, int reg1, int regn){ int i; int rc; for(i=reg1; i<=regn; i++) { rc = ov511_i2c_read(dev, i);#if 0 PDEBUG("OV7610[0x%X] = 0x%X\n", i, rc);#endif }}static void ov511_dump_i2c_regs( struct usb_device *dev){ PDEBUG("I2C REGS\n"); ov511_dump_i2c_range(dev, 0x00, 0x38);}static void ov511_dump_reg_range( struct usb_device *dev, int reg1, int regn){ int i; int rc; for(i=reg1; i<=regn; i++) { rc = ov511_reg_read(dev, i); PDEBUG("OV511[0x%X] = 0x%X\n", i, rc); }}static void ov511_dump_regs( struct usb_device *dev){ PDEBUG("CAMERA INTERFACE REGS\n"); ov511_dump_reg_range(dev, 0x10, 0x1f); PDEBUG("DRAM INTERFACE REGS\n"); ov511_dump_reg_range(dev, 0x20, 0x23); PDEBUG("ISO FIFO REGS\n"); ov511_dump_reg_range(dev, 0x30, 0x31); PDEBUG("PIO REGS\n"); ov511_dump_reg_range(dev, 0x38, 0x39); ov511_dump_reg_range(dev, 0x3e, 0x3e); PDEBUG("I2C REGS\n"); ov511_dump_reg_range(dev, 0x40, 0x49); PDEBUG("SYSTEM CONTROL REGS\n"); ov511_dump_reg_range(dev, 0x50, 0x53); ov511_dump_reg_range(dev, 0x5e, 0x5f); PDEBUG("OmniCE REGS\n"); ov511_dump_reg_range(dev, 0x70, 0x79); ov511_dump_reg_range(dev, 0x80, 0x9f); ov511_dump_reg_range(dev, 0xa0, 0xbf);}#endifint ov511_i2c_reset(struct usb_device *dev){ int rc; PDEBUG("Reset 7610\n"); rc = ov511_i2c_write(dev, 0x12, 0x80); if (rc < 0) printk(KERN_ERR "ov511: i2c reset: command failed\n"); return rc;}int ov511_reset(struct usb_device *dev, unsigned char reset_type){ int rc; PDEBUG("Reset: type=0x%X\n", reset_type); rc = ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, reset_type); if (rc < 0) printk(KERN_ERR "ov511: reset: command failed\n"); rc = ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, 0); if (rc < 0) printk(KERN_ERR "ov511: reset: command failed\n"); return rc;}int ov511_set_packet_size(struct usb_ov511 *ov511, int size){ int alt, multiplier, err; #if 0 PDEBUG("set packet size: %d\n", size);#endif switch (size) { case 992: alt = 0; multiplier = 31; break; case 993: alt = 1; multiplier = 31; break; case 768: alt = 2; multiplier = 24; break; case 769: alt = 3; multiplier = 24; break; case 512: alt = 4; multiplier = 16; break; case 513: alt = 5; multiplier = 16; break; case 257: alt = 6; multiplier = 8; break; case 0: alt = 7; multiplier = 1; // FIXME - is this correct? break; default: printk(KERN_ERR "ov511_set_packet_size: invalid size (%d)\n", size); return -EINVAL; } err = ov511_reg_write(ov511->dev, OV511_REG_FIFO_PACKET_SIZE, multiplier); if (err < 0) { printk(KERN_ERR "ov511: Set packet size: Set FIFO size ret %d\n", err); return -ENOMEM; } if (usb_set_interface(ov511->dev, ov511->iface, alt) < 0) { printk(KERN_ERR "ov511: Set packet size: set interface error\n"); return -EBUSY; } // FIXME - Should we only reset the FIFO? if (ov511_reset(ov511->dev, OV511_RESET_NOREGS) < 0) return -ENOMEM; return 0;}static int ov511_mode_init_regs(struct usb_ov511 *ov511, int width, int height, int mode){ int rc = 0; struct usb_device *dev = ov511->dev;#if 0 PDEBUG("ov511_mode_init_regs(ov511, %d, %d, %d)\n", width, height, mode);#endif ov511_set_packet_size(ov511, 0); /* Set mode consistent registers */ ov511_i2c_write(dev, 0x0f, 0x03); ov511_i2c_write(dev, 0x10, 0xff); ov511_i2c_write(dev, 0x13, 0x01); ov511_i2c_write(dev, 0x16, 0x06); ov511_i2c_write(dev, 0x20, 0x1c); ov511_i2c_write(dev, 0x24, 0x2e); /* 10 */ ov511_i2c_write(dev, 0x25, 0x7c); /* 8a */ ov511_i2c_write(dev, 0x26, 0x70); ov511_i2c_write(dev, 0x28, 0x24); /* 24 */ ov511_i2c_write(dev, 0x2b, 0xac); ov511_i2c_write(dev, 0x2c, 0xfe); ov511_i2c_write(dev, 0x2d, 0x93); ov511_i2c_write(dev, 0x34, 0x8b); if (width == 640 && height == 480) { ov511_reg_write(dev, 0x12, 0x4f); ov511_reg_write(dev, 0x13, 0x3d); ov511_reg_write(dev, 0x14, 0x00); ov511_reg_write(dev, 0x15, 0x00); ov511_reg_write(dev, 0x18, 0x03); ov511_i2c_write(dev, 0x11, 0x01); ov511_i2c_write(dev, 0x12, 0x24); ov511_i2c_write(dev, 0x14, 0x04); ov511_i2c_write(dev, 0x35, 0x9e); } else if (width == 320 && height == 240) { ov511_reg_write(dev, 0x12, 0x27); ov511_reg_write(dev, 0x13, 0x1f); ov511_reg_write(dev, 0x14, 0x00); ov511_reg_write(dev, 0x15, 0x00); ov511_reg_write(dev, 0x18, 0x03); ov511_i2c_write(dev, 0x11, 0x00); ov511_i2c_write(dev, 0x12, 0x04); ov511_i2c_write(dev, 0x14, 0x24); ov511_i2c_write(dev, 0x35, 0x1e); } else { PDEBUG("ov511: Unknown mode (%d, %d): %d\n", width, height, mode); rc = -EINVAL; } ov511_set_packet_size(ov511, 993); return rc;} /*************************************************************Turn a YUV4:2:0 block into an RGB block*************************************************************/#define LIMIT(x) ((((x)>0xffffff)?0xff0000:(((x)<=0xffff)?0:(x)&0xff0000))>>16)static inline void ov511_move_420_block(int y00, int y01, int y10, int y11, int u, int v, int w, unsigned char * pOut){ int r = 68911 * v; int g = -16915 * u + -35101 * v; int b = 87097 * u; y00 *= 49152; y01 *= 49152; y10 *= 49152; y11 *= 49152; *(pOut+w*3) = LIMIT(r + y10); *pOut++ = LIMIT(r + y00); *(pOut+w*3) = LIMIT(g + y10);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -