📄 csi2c.c
字号:
/* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Copyright (C) 2002, 2003 Motorola Semiconductors HK Ltd * *//*Special notes for CMOS sensor data capture.There are two capture modes: continuous mode (also called rolling-shutter) andsingle frame mode.The single frame mode is to be used only when there is flash.So we pick the continuous mode. And under such mode there are two different streaming modes: continuous streaming and single-frame streaming. While single-frame streaming mode functions more-or-less the same way as the singel-framecapture mode (which is not suitable for us), we have to choose the continuousstreaming mode.When continuous capture mode + continuous streaming mode is used, the CMOS sensor keeps dumping out data through the CSI interface. Every frame of datais marked by a Start Of Frame (hereafter called "SOF") signal. The simple way is to use the SOF interrupt to setup the DMA transfer for a particularframe and keep on for the following frames.However, the first bit of image data will be sent out from the CMOS sensor 32CMOS sensor clocks (which may be several times slower than MX1's systemclock) after the rising edge of the SOF signal. So for a non-realtime OS suchas Linux, it is possible that the setup of the DMA channel will be later thanthe first bit of image data (which keep on rolling out). In such case, dataloss will occur, which will result in lost-sync of image data (shifting of image data on the display).To solve this problem, we have chosen the following scheme:- use SOF interrupt to setup the DMA channel for the first time- number every SOF interrupt and DMA complete interrupt with consecutive number- in every SOF interrupt, check whether there is missing DMA complete interrupt by comparing the SOF sequence number with the DMA complete sequence number. - if there is missing DMA complete interrupt, stop the current DMA transfer and start a new one- in every DMA complete interrupt, check whether there is still data in the CSI receive FIFO (this is to ensure that the DMA complete interrupt is not a "shifted one"*) - if the receive FIFO is not empty, discard the interrupt and treat it as lost - if there is no data in the receive FIFO, it is counted as a normal DMA complete interrupt. It will then setup the next DMA transfer *REMARK:When a SOF interrupt find a lost DMA complete interrupt, it will stop the currenttransfer. However, it is possible that the DMA transfer is completed just before it is terminated in the SOF interrupt. So there will be a DMA complete interruptjust right after completion of the SOF interrupt. We have to identify such "shifted" DMA interrupt, instead of treating it as a normal DMA complete interrupt. Figure 1: two kinds of normal DMA complete interrupt SOF SOF <---------- image data ----------> ^-- DMA transfer started here ^-- DMA transfer ended here ^-- DMA channel setup here SOF SOF <---------- image data ----------> <---------- image data ----------> DMA transfer ended here --^ and setup next DMA transfer ^-- DMA channel setup here DMA transfer ended here --^Figure 2: a shifted DMA complete interrupt SOF SOF <---------- image data ----------> <---------- image data ----------> ^-- DMA transfer started here ^-- DMA transfer ended here ^-- DMA channel setup here (not terminated by the SOF interrupt) */#include <linux/kernel.h>#include <linux/module.h>#include <linux/compatmac.h>#include <linux/hdreg.h>#include <linux/vmalloc.h>#include <linux/fs.h>#include <linux/module.h>#include <linux/blkpg.h>//#include <asm/arch/hardware.h>#include <linux/i2c.h>#include <linux/i2c-algo-bit.h>#include <linux/i2c-id.h>#include <linux/slab.h>#include <asm/io.h>//#include <asm/proc-armv/cache.h>#include <linux/mm.h>#include <linux/wrapper.h>#include <asm/dma.h>#include <linux/miscdevice.h>#include "type.h"#include "mx1hw.h"//#define DEBUG_CSI2C#ifdef DEBUG_CSI2C#define dprintcsi2c(str...) printk("<"__FUNCTION__"> "str)#else#define dprintcsi2c(str...) // nothing#endif#define IOCTL_CSI_INIT 1#define IOCTL_I2C_WRITE 2#define IOCTL_I2C_READ 3#define IOCTL_SUBSAMPLE 4#define IOCTL_SET_GAIN 5#define IOCTL_SET_VF_WIDTH 6#define IOCTL_SET_INT_TIME 7#define IOCTL_DMA_CAPTURE 8//#define IOCTL_RESET_ASSERT 11//#define IOCTL_RESET_RELEASE 12#define IOCTL_STOP_CAPTURE 13#define IOCTL_INC_FRM 20 // increment frame rate meter#define CSI_IRQ 6#define I2C_DRIVERID_I2CCSI 0x1001/* Define of Massage Flag */#define EMBEDDED_REGISTER 0x01#define I2C_M_READ 0x02#define I2C_M_WT 0x04// I2C clock divider//#define I2C_CLKDIV 0x17 // 0x17 => /960 => 100k I2C clock for 96 MHz system clock#define I2C_CLKDIV 0x13 // 0x13 => /480 => 100k I2C clock for 48 MHz system clock//// SENSOR read/write command//#define SCM20014_ADDR_W (U32) 0x66 //01100110b, last bit 0 => WRITE#define SCM20014_ADDR_R (U32) 0x67 //01100111b, last bit 1 => READ// functions and interfacestatic int csi2c_open(struct inode *inode, struct file *filp);static int csi2c_release(struct inode *inode, struct file *filp);static ssize_t csi2c_read(struct file *filp, char *buf, size_t size, loff_t *l);static ssize_t csi2c_write(struct file *filp, const char *buf, size_t size, loff_t *l);static int csi2c_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);static void I2C_init(void);static U32 CSI_init(U32 systemClock, U32 sensorClock);static void I2C_read(U32 reg, U32 * _data);static void I2C_write(U32 reg, U32 data);static void port_init_SENSOR(void);static U32 SFCM_capture_DMA(U32 nPixel);static int i2c_csi_attach_adapter (struct i2c_adapter * adap);static void dma_complete_handler(void);static void dma_error_handler(int error_type);static int gMajor=0;static U32 *csi_data_buf=0;static U32 gSensorClock;int sensorFrameCount=0;static dmach_t dma_channel;static U32 readSeqNum;static U32 capSeqNum;static U32 SOFseqNum;static U8 stopCapture;static U8 badFrame;static U32 dma_buf_phy_addr;static U32 dma_data_size;static U8 dmaStopped;static U8 SOFintrStopped;static DECLARE_WAIT_QUEUE_HEAD(dma_wait);static DECLARE_WAIT_QUEUE_HEAD(stop_capture_wait);static struct i2c_driver i2c_csi_driver = { name: "i2c-csi client driver", id: I2C_DRIVERID_I2CCSI, flags: I2C_DF_DUMMY | I2C_DF_NOTIFY, attach_adapter: i2c_csi_attach_adapter, detach_client: NULL, /*i2c_csi_detach_client,*/ command: NULL};static struct i2c_client i2c_csi_client = { name: "i2c-csi client", id: 1, flags: 0, addr: -1, adapter: NULL, driver: &i2c_csi_driver, data: NULL};struct file_operations csi2c_fops = { open: csi2c_open, release: csi2c_release, read: csi2c_read, write: csi2c_write, ioctl: csi2c_ioctl,};static int csi2c_open(struct inode *inode, struct file *filp){ dprintcsi2c("*** open ***\n"); MOD_INC_USE_COUNT; /* request DMA channel for RxFIFO data */ for(dma_channel = 0; dma_channel < 11; dma_channel++) { if ( (request_dma(dma_channel, "CMOS Sensor")) == 0) { printk("dma channel %d granted\n", dma_channel); *((U32 *)(DMA_SAR0+dma_channel*0x40)) = 0x00224010; // CSI RxFIFO register *((U32 *)(DMA_RSSR0+dma_channel*0x40)) = 7; // CSI data *((U32 *)(DMA_BLR0+dma_channel*0x40)) = 64; // burst length : 16 words = 64 bytes *((U32 *)(DMA_RTOR0+dma_channel*0x40)) = 0; // burst timeout, not used *((U32 *)(DMA_BUCR0+dma_channel*0x40)) = 0; // bus utilization, not used request_dma_intr( dma_channel, (callback_t)dma_complete_handler, (err_callback_t)dma_error_handler ); break; } } if (dma_channel > 11) printk("*** ERROR: no dma channel is available ! ***\n"); return 0;}static int csi2c_release(struct inode *inode, struct file *filp){ dprintcsi2c("*** close ***\n"); MOD_DEC_USE_COUNT; free_dma_intr(dma_channel); free_dma(dma_channel); return 0;}static ssize_t csi2c_read(struct file *filp, char *buf, size_t size, loff_t *l){ U32 flags; U32 captureDone; dprintcsi2c("*** read ***\n"); do { captureDone = 0; save_flags(flags); cli(); if (capSeqNum > readSeqNum) captureDone = 1; restore_flags(flags); if (!captureDone) interruptible_sleep_on(&dma_wait); if (badFrame) captureDone = 1; } while (!captureDone); if (badFrame) {// printk("--- bad frame ---\n"); return 0; // a zero size frame implies bad frame } else { readSeqNum++; copy_to_user(buf, csi_data_buf, size); return size; } }static ssize_t csi2c_write(struct file *filp, const char *buf, size_t size, loff_t *l){ dprintcsi2c("*** write ***\n"); return 0;}static int malloc_buffer() { if ((csi_data_buf = (U32 *)__get_free_pages(GFP_KERNEL, 7))) // 128 pages = 128x4K = 512K { dma_buf_phy_addr = virt_to_phys((void *)csi_data_buf); dprintcsi2c("Buffer start: 0x%08x, DMA addr: 0x%08x\n", (int)csi_data_buf, (int)dma_buf_phy_addr); } else { printk("*** ERROR: cannot allocate buffer memory for driver ! ***\n"); return -1; } return 0;}static void free_buffer(){ dprintcsi2c("free buffer\n"); if (csi_data_buf) __free_pages((void *)csi_data_buf,7); // 512K}static void setSubsample(int subsample){ U32 value; switch (subsample) { case 2: // divide by 2 value = 0x25; break; case 4: // divide by 4 value = 0x2A; break; case 8: // divide by 8 value = 0x2F; break; default: // i.e. full subsampling value = 0; // REMARK: "cm" bit must be cleared for full-sampling } I2C_write(0x41, value);} static void setGlobalGain(int gain){ I2C_write(0x10, gain);}static void setVFwidth(int width){ I2C_write(0x52, width >> 8); I2C_write(0x53, width & 0xFF);} static void setIntegrationTime(int value){ I2C_write(0x4E, value >> 8); I2C_write(0x4F, value & 0xFF);}static void stopCMOScapture(){ U32 flags; U8 allStopped; save_flags(flags); cli(); stopCapture = 1; dmaStopped = 0; SOFintrStopped = 0; allStopped = 0; restore_flags(flags); do { save_flags(flags); cli(); if ((dmaStopped) && (SOFintrStopped)) { allStopped = 1; restore_flags(flags); } else { interruptible_sleep_on(&stop_capture_wait); restore_flags(flags); } } while (!allStopped);}static int csi2c_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ dprintcsi2c("cmd: 0x%08x, arg: 0x%08x\n", cmd, (int)arg); switch(cmd) { case IOCTL_CSI_INIT: dprintcsi2c("Init CSI, bus clock: %d MHz, sensor clock: %d MHz\n", (int)(arg >> 8), (int)(arg & 0xFF)); { return (CSI_init(arg >> 8, arg & 0xFF)); } break; case IOCTL_I2C_WRITE: dprintcsi2c("I2C write - reg: 0x%02x, data: 0x%02x\n", arg >> 8, arg&0xFF); I2C_write(arg>>8, arg&0xFF); break; case IOCTL_I2C_READ: { int rv; dprintcsi2c("I2C read\n"); I2C_read(arg, &rv); return(rv); } break; case IOCTL_SUBSAMPLE: dprintcsi2c("Set subsample: %d\n", (int)arg); setSubsample(arg); break; case IOCTL_SET_GAIN: dprintcsi2c("Set global gain to %d\n", (int)arg); setGlobalGain(arg); break; case IOCTL_SET_VF_WIDTH: dprintcsi2c("Set virtual frame width to 0x%04x\n", (int)arg); setVFwidth(arg); break; case IOCTL_SET_INT_TIME: dprintcsi2c("Set integration time to 0x%04x\n", (int)arg); setIntegrationTime(arg); break; case IOCTL_DMA_CAPTURE: dprintcsi2c("DMA capture for %d bytes\n", (int)arg); return SFCM_capture_DMA((U32)arg); break;/* case IOCTL_RESET_ASSERT: * (U32 *)PTB_DR |= (0x1 << 18); // RESET asserted break; case IOCTL_RESET_RELEASE: * (U32 *)PTB_DR &= ~(0x1 << 18); // RESET released break;*/ case IOCTL_STOP_CAPTURE: stopCMOScapture(); break; case IOCTL_INC_FRM: { U32 flags = 0; save_flags(flags); cli(); sensorFrameCount++; restore_flags(flags); } break; } return 0;}static int i2c_csi_attach_adapter (struct i2c_adapter * adap){ /* find out the adapter for the I2C module in the DBMX1*/ if (memcmp(adap->name, "DBMX1 I2C adapter", 17) != 0 ) return -ENODEV; /* store the adapter to the client driver */ i2c_csi_client.adapter = adap; return 0;}static void I2C_init(void){ // init port // PA15 : I2C_DATA // PA16 : I2C_CLK
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -