📄 h3600_backpaq_camera.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 * * 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/arch/backpaq.h>#include <asm/arch/h3600_backpaq_camera.h>#include <asm/irq.h>#include <linux/kmod.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 */#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)#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 }};/* A single frame of raw data from the camera *//* We can always tell the active mode from the size */enum frame_state { FRAME_DONE = 0, FRAME_PENDING, FRAME_ACTIVE, FRAME_FULL};struct frame { unsigned char *data; /* May point to true_data OR to shared memory segment */ unsigned char *local_data; /* Our local storage area for raw frames */ unsigned char *shared_data; /* Points into shared memory "frame_buf" */ int bytes_read; /* How full it is */ int bytes_to_read; /* Normally width * height */ int width; int height; volatile enum frame_state state; struct frame *next; /* Linked list */};#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 */};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 */ struct h3600_backpaq_camera_params params; /* Our special settings */ int param_mask; /* Which parameters need to be updated */ 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)#else#define CAMDEBUG(x) do { } while(0)#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;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>");/******************************* * 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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -