📄 h3600_backpaq_core.c
字号:
/* * Driver for the Compaq iPAQ Mercury Backpaq camera * Video4Linux interface * * Copyright 2001 Compaq Computer Corporation. * * Use consistent with the GNU GPL is permitted, * provided that this copyright notice is * preserved in its entirety in all copies and derived works. * * COMPAQ COMPUTER CORPORATION MAKES NO WARRANTIES, EXPRESSED OR IMPLIED, * AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS * FITNESS FOR ANY PARTICULAR PURPOSE. * * Author: Andrew Christian * <andyc@handhelds.org> * 4 May 2001 * * Driver for Mercury BackPAQ camera * * Support for Philips imagers added February, 2002 * * Issues to be addressed: * 1. Writing to the FPGA when we need to do a functionality change * 2. Sampling the pixels correctly and building a pixel array * 3. Handling different pixel formats correctly * 4. Changing the contrast, brightness, white balance, and so forth. * 5. Specifying a subregion (i.e., setting "top, left" and SUBCAPTURE) */#include <linux/module.h>#include <linux/version.h>#include <linux/init.h>#include <asm/uaccess.h> /* get_user,copy_to_user*/#include <linux/vmalloc.h>#include <linux/proc_fs.h>#include <linux/pm.h>#include <asm/io.h>#include <linux/wrapper.h>/* #include <asm/arch/hardware.h>*/ /* Included in <asm/io.h> */#include <asm/irq.h>#include <linux/kmod.h>#include <asm/arch/backpaq.h>#include "h3600_backpaq_common.h"#include "philips_image_sensor.h"/* Actual chip specifications */#define HC_TRUE_WIDTH 644 /* Last two columns are black */#define HC_TRUE_HEIGHT 482#define HC_RAW_BUFFER_SIZE ((u32) HC_TRUE_WIDTH * (u32) HC_TRUE_HEIGHT )#define HC_DECIMATED_WIDTH 320#define HC_DECIMATED_HEIGHT 240#define HC_DECIMATED_BUFFER_SIZE ((u32) HC_DECIMATED_WIDTH * (u32) HC_DECIMATED_HEIGHT )/* Largest frame buffer for 24 bit color (we don't do 32 bit color */#define HC_MAX_ALLOWED_WIDTH 640#define HC_MAX_ALLOWED_HEIGHT 480#define HC_MIN_ALLOWED_WIDTH 160#define HC_MIN_ALLOWED_HEIGHT 120/* Memory-mapped frame size */#define HC_MAX_PROCESSED_FRAME_UNALIGNED (HC_MAX_ALLOWED_WIDTH * HC_MAX_ALLOWED_HEIGHT * 3)#define HC_MAX_PROCESSED_FRAME_SIZE ((HC_MAX_PROCESSED_FRAME_UNALIGNED + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))/* Control locations for setting camera parameters */#define IMAGER_CTL_SPECIAL_MODES 0x10#define IMAGER_CTL_AUTOBRIGHT 0x60#define IMAGER_CTL_GAIN_FORMAT 0x70#define IMAGER_CTL_POWER_SETTING 0x80#define IMAGER_CTL_POWER_MANAGEMENT 0x90 /* Shouldn't need to set this *//* Mask definition so we only change modified parameters *//* Definitions for Smallcam image sensor */#define PARAM_IMAGER_SPECIAL_MODES (1 << 0)#define PARAM_IMAGER_AUTOBRIGHT (1 << 1)#define PARAM_IMAGER_GAIN_FORMAT (1 << 2)#define PARAM_IMAGER_POWER_SETTING (1 << 3)#define PARAM_IMAGER_POWER_MGMT (1 << 4)#define PARAM_FPGA_INTEGRATION_TIME (1 << 8) /* Actually used for Smallcam */#define PARAM_FPGA_CLOCK_DIVISOR (1 << 9)#define PARAM_FPGA_INTERRUPT_FIFO (1 << 10)#define PARAM_FPGA_LIVE_MODE (1 << 11)#define PARAM_FPGA_DECIMATION_MODE (1 << 12) /* Not a true parameter *//* Allow exterior control of decimation */static int decimate_mode = 1;MODULE_PARM(decimate_mode, "i");MODULE_PARM_DESC(decimate_mode, "Use FPGA-based decimation");/* Camera modes */enum hc_camera_mode { HC_MODE_RAW = 0, HC_MODE_RAW_DECIMATED, HC_MODE_DECIMATION_1, HC_MODE_DECIMATION_2, HC_MODE_DECIMATION_4, HC_MODE_CIF, HC_MODE_QCIF};static int fpga_mode_to_decimation[] = { 0, 1, 0, 1, 1, 0, 1 };struct h3600_mode_to_rawsize { int width; int height; int bytes;};/* Use this table to convert a mode into a size */static struct h3600_mode_to_rawsize hc_raw[] = { { HC_TRUE_WIDTH, HC_TRUE_HEIGHT, HC_RAW_BUFFER_SIZE }, { HC_DECIMATED_WIDTH, HC_DECIMATED_HEIGHT, HC_DECIMATED_BUFFER_SIZE }};/* We scan this table when setting a requested resolution */struct h3600_size_mode { int width; int height; int mode;};static struct h3600_size_mode hc_default_resolutions[] = { { 160, 120, HC_MODE_DECIMATION_4 }, { 176, 144, HC_MODE_QCIF }, { 320, 240, HC_MODE_DECIMATION_2 }, { 352, 288, HC_MODE_CIF }, { 640, 480, HC_MODE_DECIMATION_1 }, { 0, 0, 0 }};static struct h3600_size_mode hc_raw_resolutions[] = { { HC_DECIMATED_WIDTH, HC_DECIMATED_HEIGHT, HC_MODE_RAW_DECIMATED }, { HC_TRUE_WIDTH, HC_TRUE_HEIGHT, HC_MODE_RAW }, { 0, 0, 0 }};#define NUMBER_OF_FRAMES 2/* Core states */enum hc_v4l_state { HC_V4L_IDLE = 0, HC_V4L_GRABBING, /* A "read" command has been issued */ HC_V4L_STREAMING /* VIDIOCMCAPTURE active and there is an available buffer */};/* Capture states */ enum capture_state { CAPTURE_OFF = 0, CAPTURE_WAIT_FOR_REFRESH, /* Waiting for a VBLANK */ CAPTURE_WAIT_FOR_FIFO, /* Waiting for our first FIFO request */ CAPTURE_ACTIVE /* Actively receiving FIFO interrupts */};struct capture_stats { int fifo_high; int fifo_low; int vblank_count; int fifo_count; int complete_frames; /* Successfully captured frames */ int ef_extra_vblank; /* VBLANK while waiting for FIFO interrupt */ int ef_fifo_overrun; /* Miss because of fifo overrun */ int ef_incomplete_frames; /* Miss because we didn't read all of the data */ int ef_no_capture_buffer; /* Miss because we didn't have an available capture buffer */ int camera_writethru_wait; /* Count how many "spins" of the writethru we blocked on */};static char * camera_type_names[] = { "smal", "philips"};struct h3600_camera_struct { struct video_device vdev; /* This must be first */ struct video_picture vpic; /* v4l camera settings */ struct video_window vwin; /* v4l capture area */ enum h3600_camera_type ctype; /* Type of camera */ /* General parameters common to all camera types */ unsigned short clock_divisor; /* 0x100 = 5 fps */ unsigned short interrupt_fifo; unsigned char read_polling_mode; /* Force "read" to use polling mode */ unsigned char flip; /* Set to TRUE to invert image */ /* Parameters specific to SMaL camera */ unsigned char power_setting; unsigned char gain_format; unsigned char power_mgmt; unsigned char special_modes; unsigned char autobright; /* Parameters specific to the Philips camera */ enum Philips_sub_type cam_sub_type; /* Sub type for philips cameras */ unsigned short electronic_shutter; unsigned char subrow; /* negative subrow means switch it off */ /* Which parameters need to be updated */ int param_mask; /* I2C data for Philips sensors */ unsigned char i2c_status; /* interrupt status */ wait_queue_head_t i2cq; /* Wait queue for i2c transaction */ volatile enum hc_v4l_state state; /* v4l current state = IDLE, GRABBING, STREAMING */ /* Capture data */ struct frame *active; /* List of capture frames that need filling */ volatile enum capture_state capture_state; /* Active capture mode */ struct frame frame[NUMBER_OF_FRAMES]; struct capture_stats capture_stats; wait_queue_head_t capq; /* Processing data */ enum hc_camera_mode mode; /* Mode index selection (RAW, DECIMATE_1, etc...) */ struct semaphore lock; /* Force single access */ int usage_count; /* How many are open */ /* mmap interface */ unsigned char * frame_buf; /* frame buffer data (raw bits stored here) */};//#define BACKPAQ_CAMERA_DEBUG#undef BACKPAQ_CAMERA_DEBUG#ifdef BACKPAQ_CAMERA_DEBUG#define CAMDEBUG(format,args...) printk(KERN_ALERT __FILE__ format, ## args)#define PRINTDEBUG(format,args...) printk(__FUNCTION__ format, ## args)#else#define CAMDEBUG(format,args...)#define PRINTDEBUG(format,args...)#endif#define CAMERROR(format,args...) printk(KERN_ERR __FILE__ format, ## args)/* Global variables */static struct h3600_camera_struct hc_camera; /* We only have a single camera */static struct proc_dir_entry *proc_backpaq_camera = NULL;/* Initial register settings for UPA1021 sensor */static unsigned char upa1021_i2c_regs[] = {0xA8,0x19,0x07,0x06,0xF5,0x18,0xe4,0x3C,0x02,0x80,0x80,0x40,0x80,0x00,0x67,0x01, 0x00,0x00,0x14,0x20,0x48,0x58,0x07,0x27,0x27,0x37,0x05,0x39,0x1F,0xF3,0x31,0x4F, 0xD3,0x20,0x01,0x01,0x00,0xEa,0x08,0x01,0x00,0x00,0x00,0x00};/* Initial register settings for UPA1022 sensor */static unsigned char upa1022_i2c_regs[] = {/*00*/0x68, /*01*/0x19, /*02*/0x07, /*03*/0x0e, /*04*/0xf2, /*05*/0x18, /*06*/0xff, /*07*/0x6b, /*08*/0x42, /*09*/0x80, /*0a*/0x80, /*0b*/0x20, /*0c*/0x80, /*0d*/0x00, /*0e*/0xe6, /*0f*/0x00,/*10*/0x00, /*11*/0x00, /*12*/0x14, /*13*/0x10, /*14*/0x4d, /*15*/0x5d, /*16*/0x08, /*17*/0x28,/*18*/0x28, /*19*/0x38, /*1a*/0x06, /*1b*/0x3a, /*1c*/0x1f, /*1d*/0xf3, /*1e*/0x31, /*1f*/0x6e,/*20*/0xf2, /*21*/0x20, /*22*/0x01, /*23*/0x01, /*24*/0x00, /*25*/0xe4, /*26*/0x02, /*27*/0x01,/*28*/0x1d, /*29*/0x4c, /*2a*/0xae, /*2b*/0x13, /*2c*/0x00, /*2d*/0x00};unsigned int h3600_camera_debug = 0;/* Useful externals */int h3600_backpaq_fpga_status(void);/* insmod options */MODULE_PARM(h3600_camera_debug,"i");MODULE_PARM_DESC(h3600_camera_debug,"debug messages, default is 0 (no)");#define BANNER "Compaq iPAQ H3600 Mercury BackPAQ Camera for Video4Linux"MODULE_DESCRIPTION(BANNER);MODULE_AUTHOR("Andrew Christian <andyc@handhelds.org>");/* Functions defined in h3600_backpaq_camera__algo.c */void decimate( struct frame *frame );unsigned long write_vga( struct frame *frame, unsigned char *buf, int streaming, int flipped, int palette );unsigned long write_qvga( struct frame *frame, unsigned char *buf, int streaming, int flipped, int palette );unsigned long write_cif( struct frame *frame, unsigned char *buf, int streaming, int flipped, int palette );unsigned long write_qcif( struct frame *frame, unsigned char *buf, int streaming, int flipped, int palette );/******************************* * Utility routines *******************************/static void set_camera_resolution( struct h3600_camera_struct *cam, int palette, int width, int height ){ struct h3600_size_mode *m = hc_default_resolutions; enum hc_camera_mode old_mode = cam->mode; cam->vpic.palette = palette; switch (palette) { case VIDEO_PALETTE_RAW: cam->vpic.depth = 8; m = hc_raw_resolutions; break; case VIDEO_PALETTE_RGB24: cam->vpic.depth = 24; break; case VIDEO_PALETTE_GREY: cam->vpic.depth = 8; break; case VIDEO_PALETTE_YUV422: cam->vpic.depth = 16; break; } do { cam->vwin.width = m->width; cam->vwin.height = m->height; cam->mode = m->mode; m++; } while ( m->width > 0 && width >= m->width && height >= m->height );// printk(KERN_ALERT __FILE__ ": " __FUNCTION__ ": Setting to %d x %d, mode %d (%d)\n", // cam->vwin.width, cam->vwin.height, cam->mode,// fpga_mode_to_decimation[cam->mode]); // Set the param_mask field if we need to change decimation modes */ if ( fpga_mode_to_decimation[cam->mode] != fpga_mode_to_decimation[old_mode] ) cam->param_mask |= PARAM_FPGA_DECIMATION_MODE;}static unsigned long required_buf_size( struct h3600_camera_struct *cam ){ unsigned long pixel_count = cam->vwin.width * cam->vwin.height; switch (cam->vpic.palette) { case VIDEO_PALETTE_RGB24: pixel_count *= 3; break; case VIDEO_PALETTE_YUV422: pixel_count *= 2; break; } return pixel_count;}/********************************************************************** * * Memory management * * This is a shameless copy from the 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. * **********************************************************************//* 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);}static int allocate_frame_buf(struct h3600_camera_struct *cam){ int i; cam->frame_buf = rvmalloc(NUMBER_OF_FRAMES * HC_MAX_PROCESSED_FRAME_SIZE); if (!cam->frame_buf)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -