📄 s3c2440a_camif.c
字号:
/* * Camera interface on S3C2440A * * $Revision: 1.0 $ * * april 2008 PHT * * 飞 凌 嵌 入 式 * www.witech.com.cn */#include <linux/config.h>#include <linux/module.h>#include <linux/init.h>#include <linux/kernel.h>#include <linux/version.h>#include <linux/wait.h>#include <linux/delay.h>#include <linux/interrupt.h>#include <linux/mm.h>#include <linux/slab.h>#include <linux/completion.h>#include <linux/videodev.h>#include <linux/dma-mapping.h>#include <asm/io.h>#include <asm/hardware.h>#include <asm/semaphore.h>#include <asm/uaccess.h>#include <asm/arch/regs-gpio.h>#include <asm/arch/regs-gpioj.h>#include <asm/arch/regs-clock.h>#include <asm/arch/regs-irq.h>#include "s3c2440a_camif.h"#define DEBUG 0#if DEBUG#define DPRINTK(x...) printk(x)#else#define DPRINTK(x...) /* !!!! */#endif#ifndef __user#define __user#endif#define VIDIOCSYCbCr _IOW('v',200, int) #define CAMERA_STOP_QUICKLY#define SKIP_FIRST_FRAME#define RGB_YUV_MUTEXstatic struct img_buf_t yuv_buf[4];static struct img_buf_t rgb_buf[4];#define YUV_TUPLE_SIZE (640*480*2)//(1024*768*2) // maximum#define YUV_IMG_BUF_SIZE PAGE_ALIGN(YUV_TUPLE_SIZE + PAGE_SIZE - 1)static unsigned char *camif_yuv_buf = NULL;static dma_addr_t camif_yuv_buf_dma = 0;#define RGB_TUPLE_SIZE (640*480*2) // maximum RGB24#define RGB_IMG_BUF_SIZE PAGE_ALIGN(RGB_TUPLE_SIZE + PAGE_SIZE - 1)static unsigned char *camif_rgb_buf = NULL;static dma_addr_t camif_rgb_buf_dma = 0;static struct s3c2440_camif camif; // v4l devstatic struct s3c2440_camif_cfg_t s3c2440_camif_cfg;static int v4l_cam_open(struct inode *inode, struct file *file);static void v4l_cam_release (struct inode *, struct file *);static int v4l_cam_ioctl(struct inode *, struct file *, unsigned int, unsigned long);static int v4l_cam_mmap(struct file *v, struct vm_area_struct *vma);static ssize_t v4l_cam_read(struct file *, char __user *, size_t, loff_t *);static unsigned int v4l_cam_poll(struct file *, struct poll_table_struct *);static __u16 power_lvl = 0;static __u16 reset_lvl = 1;static struct file_operations cam_ops =
{
.open = v4l_cam_open, .release = v4l_cam_release, .read = v4l_cam_read, .ioctl = v4l_cam_ioctl, .poll = v4l_cam_poll, .mmap = v4l_cam_mmap,};//power on/offstatic __inline void camera_power(int mode){ if(mode) { //power on if(power_lvl) s3c2410_gpio_setpin(S3C2410_GPG12,1); else s3c2410_gpio_setpin(S3C2410_GPG12,0); } else { if(power_lvl) s3c2410_gpio_setpin(S3C2410_GPG12,0); else s3c2410_gpio_setpin(S3C2410_GPG12,1); }}//external resetstatic __inline void camera_reset(int mode){ if(mode) { //do reset if(reset_lvl) __raw_writel(__raw_readl(rCIGCTRL)|(1<<30),rCIGCTRL); else __raw_writel(__raw_readl(rCIGCTRL) & ~(1<<30),rCIGCTRL); } else { if(reset_lvl) __raw_writel(__raw_readl(rCIGCTRL) & ~(1<<30),rCIGCTRL); else __raw_writel(__raw_readl(rCIGCTRL) | (1<<30),rCIGCTRL); } }//software reset#define CAMERA_RESET() \ {__raw_writel(__raw_readl(rCIGCTRL) | (1<<31),rCIGCTRL); \ camera_reset(1); \ mdelay(50); \ __raw_writel(__raw_readl(rCIGCTRL) & ~(1<<31),rCIGCTRL); \ camera_reset(0); \ mdelay(50);}/* * CAMCLK_DIV = UPLL / ( CAMCLK * 2) - 1 ; */static void cam_gpio_init(void){ DPRINTK("%s():\n", __FUNCTION__); /* s3c2410_gpio_cfgpin(S3C2440_GPJ0 , 2); s3c2410_gpio_cfgpin(S3C2440_GPJ1 , 2); s3c2410_gpio_cfgpin(S3C2440_GPJ2 , 2); s3c2410_gpio_cfgpin(S3C2440_GPJ3 , 2); s3c2410_gpio_cfgpin(S3C2440_GPJ4 , 2); s3c2410_gpio_cfgpin(S3C2440_GPJ5 , 2); s3c2410_gpio_cfgpin(S3C2440_GPJ6 , 2); s3c2410_gpio_cfgpin(S3C2440_GPJ7 , 2); s3c2410_gpio_cfgpin(S3C2440_GPJ8 , 2); s3c2410_gpio_cfgpin(S3C2440_GPJ9 , 2); s3c2410_gpio_cfgpin(S3C2440_GPJ10 , 2); s3c2410_gpio_cfgpin(S3C2440_GPJ11 , 2); s3c2410_gpio_cfgpin(S3C2440_GPJ12 , 2);*/ __raw_writel(0x2aaaaaa , S3C2440_GPJCON); __raw_writel(0x0 , S3C2440_GPJDAT); __raw_writel(0x0 , S3C2440_GPJUP);}static void inline s3c2440_camif_init(void){ DPRINTK("%s():\n", __FUNCTION__); cam_gpio_init(); // Camera clock enable __raw_writel(__raw_readl(S3C2410_CLKCON) | S3C2440_CLKCON_CAMERA,S3C2410_CLKCON); //set CAMDIVN; CAMCLK_DIV=upll /( camclk * 2) -1; 0:24M, 1:12M, 2:8M, 3:6M, 5:4M, 11:2M __raw_writel(S3C2440_CAMDIVN_CAMCLK_SEL|(CAM_CLK_DIV & S3C2440_CAMDIVN_CAMCLK_MASK),S3C2440_CAMDIVN); printk("camera power level %d, reset level %d\n", power_lvl, reset_lvl); __raw_writel((1<<29),rCIGCTRL); //bit 29 must be 1 CAMERA_RESET(); }static void s3c2440_camif_deinit(void){ DPRINTK("%s():\n", __FUNCTION__); // Camera clock disable __raw_writel(__raw_readl(S3C2410_CLKCON) & ~S3C2440_CLKCON_CAMERA,S3C2410_CLKCON); __raw_writel(0,rCIIMGCPT); mdelay(5);}/******************************************************** CalculateBurstSize - Calculate the busrt lengths Description: - dstHSize: the number of the byte of H Size. */void __inline CalculateBurstSize(u32 hSize, u32 *mainBurstSize, u32 *remainedBurstSize){ __u32 tmp = (hSize/4)%16; switch(tmp) { case 0: *mainBurstSize = 16; *remainedBurstSize = 16; break; case 4: *mainBurstSize = 16; *remainedBurstSize = 4; break; case 8: *mainBurstSize=16; *remainedBurstSize = 8; break; default: tmp=(hSize/4)%8; switch(tmp) { case 0: *mainBurstSize = 8; *remainedBurstSize = 8; break; case 4: *mainBurstSize = 8; *remainedBurstSize = 4; default: *mainBurstSize = 4; tmp = (hSize/4)%4; *remainedBurstSize = (tmp)?tmp:4; break; } break; }}/******************************************************** CalculatePrescalerRatioShift - none Description: - none */void __inline CalculatePrescalerRatioShift(u32 SrcSize, u32 DstSize, u32 *ratio, u32 *shift){ if(SrcSize>=64*DstSize) { printk(KERN_ERR "Error: out of the prescaler range: SrcSize/DstSize = %d(< 64)\n", SrcSize/DstSize); panic("camera set error!\n"); } else if(SrcSize>=32*DstSize) { *ratio=32; *shift=5; } else if(SrcSize>=16*DstSize) { *ratio=16; *shift=4; } else if(SrcSize>=8*DstSize) { *ratio=8; *shift=3; } else if(SrcSize>=4*DstSize) { *ratio=4; *shift=2; } else if(SrcSize>=2*DstSize) { *ratio=2; *shift=1; } else { *ratio=1; *shift=0; } }static void s3c2440_camif_configure(struct s3c2440_camif_cfg_t *cfg){ int i; unsigned int ysize, csize; unsigned int burst_y, burst_c, remain_burst_y, remain_burst_c; unsigned int hsft, vsft, hratio, vratio, mhratio, mvratio; unsigned int hscale, vscale; DPRINTK("%s():\n", __FUNCTION__); cfg->pre_x = cfg->dst_x; cfg->pre_y = cfg->dst_y; switch(cfg->dev->mode) { case 0: cfg->dev->size = cfg->dst_x*cfg->dst_y*2; cfg->pre_fmt = 0; //RGB16 break; case 1: cfg->dev->size = cfg->dst_x*cfg->dst_y*3; cfg->pre_fmt = 1; //RGB24 break; case 2: cfg->dev->size = (cfg->dst_x*cfg->dst_y*3)/2; cfg->dst_fmt = 0; //YCbCr4:2:0 break; case 3: cfg->dev->size = cfg->dst_x*cfg->dst_y*2; cfg->dst_fmt = 1; //YCbCr4:2:2 break; } DPRINTK("CAMIF: src = (%dx%d), co dst = (%dx%d), pre dst = (%dx%d)\n", cfg->src_x, cfg->src_y, cfg->dst_x, cfg->dst_y, cfg->pre_x, cfg->pre_y); ////////////////// common control setting // ITU601,UVOffset=0,YCbYCr __raw_writel((1<<31)|(0<<30)|(0<<29)|(cfg->src_x<<16)|(cfg->ycbcr<<14)|(cfg->src_y),rCISRCFMT); // clear overflow, disable window offset __raw_writel((1<<30)|(0xf<<12),rCIWDOFST); __raw_writel(0,rCIWDOFST); // clear test pattern, normal PCLK, VSYNC, HREF __raw_writel(__raw_readl(rCIGCTRL) & ~((3<<27)|(7<<24)),rCIGCTRL); ysize = cfg->dst_x * cfg->dst_y; if(cfg->dst_fmt) //YCbCr4:2:2 csize = ysize / 2; else //YCbCr4:2:0 csize = ysize / 4; for (i = 0; i < 4; i++) { yuv_buf[i].buf = camif_yuv_buf; yuv_buf[i].phys_addr = (unsigned int)camif_yuv_buf_dma; rgb_buf[i].buf = camif_rgb_buf; rgb_buf[i].phys_addr = (unsigned int)camif_rgb_buf_dma; DPRINTK("yuv_buf[%d] : virt=0x%08lx, phys = 0x%08lx\n", i, (unsigned long)yuv_buf[i].buf, (unsigned long)yuv_buf[i].phys_addr); DPRINTK("rgb_buf[%d] : virt=0x%08lx, phys = 0x%08lx\n", i, (unsigned long)rgb_buf[i].buf, (unsigned long)rgb_buf[i].phys_addr); __raw_writel(yuv_buf[i].phys_addr,CICOSTAY(i)); __raw_writel(yuv_buf[i].phys_addr + ysize,CICOSTACb(i)); __raw_writel(yuv_buf[i].phys_addr + ysize + csize,CICOSTACr(i)); __raw_writel(rgb_buf[i].phys_addr,CIPRSTARGB(i)); DPRINTK("STAY%d = 0x%08x\n", i, yuv_buf[i].phys_addr); DPRINTK("STACB%d = 0x%08x\n", i, yuv_buf[i].phys_addr + ysize); DPRINTK("STACR%d = 0x%08x\n", i, yuv_buf[i].phys_addr + ysize + csize); } ////////////////// codec port setting // YCbCr4:2:2 input, YCbCr4:2:2/YCbCr4:2:0 output, normal __raw_writel((1<<31)|((cfg->dst_fmt?1:0)<<30)|(cfg->dst_x<<16)|(0<<14)|(cfg->dst_y),rCICOTRGFMT); CalculateBurstSize(cfg->dst_x, &burst_y, &remain_burst_y); CalculateBurstSize(cfg->dst_x/2, &burst_c, &remain_burst_c); __raw_writel((burst_y<<19)|(remain_burst_y<<14)|(burst_c<<9)|(remain_burst_c<<4),rCICOCTRL); CalculatePrescalerRatioShift(cfg->src_x, cfg->dst_x, &hratio, &hsft); CalculatePrescalerRatioShift(cfg->src_y, cfg->dst_y, &vratio, &vsft); mhratio = (cfg->src_x<<8)/(cfg->dst_x<<hsft); mvratio = (cfg->src_y<<8)/(cfg->dst_y<<vsft); __raw_writel(((10-hsft-vsft)<<28)|(hratio<<16)|(vratio),rCICOSCPRERATIO); __raw_writel(((cfg->src_x/hratio)<<16)|(cfg->src_y/vratio),rCICOSCPREDST); if(cfg->dev->mode==2||cfg->dev->mode==3) cfg->bypass = (cfg->src_x==cfg->dst_x&&cfg->src_y==cfg->dst_y)?1:0; hscale = (cfg->src_x>cfg->dst_x)?0:1; vscale = (cfg->src_y>cfg->dst_y)?0:1; __raw_writel((0<<31)|(hscale<<30)|(vscale<<29)|(mhratio<<16)|(mvratio),rCICOSCCTRL); __raw_writel(cfg->dst_x*cfg->dst_y,rCICOTAREA); ///////////////// preview port setting __raw_writel((cfg->pre_x<<16)|(0<<14)|(cfg->pre_y),rCIPRTRGFMT); if (cfg->pre_fmt) //RGB24B CalculateBurstSize(cfg->pre_x*4, &burst_y, &remain_burst_y); else // RGB16B CalculateBurstSize(cfg->pre_x*2, &burst_y, &remain_burst_y); __raw_writel((burst_y<<19)|(remain_burst_y<<14),rCIPRCTRL); CalculatePrescalerRatioShift(cfg->src_x, cfg->pre_x, &hratio, &hsft); CalculatePrescalerRatioShift(cfg->src_y, cfg->pre_y, &vratio, &vsft); mhratio = (cfg->src_x<<8)/(cfg->pre_x<<hsft); mvratio = (cfg->src_y<<8)/(cfg->pre_y<<vsft); __raw_writel(((10-hsft-vsft)<<28)|(hratio<<16)|(vratio),rCIPRSCPRERATIO); __raw_writel(((cfg->src_x/hratio)<<16)|(cfg->src_y/vratio),rCIPRSCPREDST); hscale = (cfg->src_x>cfg->pre_x)?0:1; vscale = (cfg->src_y>cfg->pre_y)?0:1; __raw_writel((1<<31)|((cfg->pre_fmt?1:0)<<30)|(hscale<<29)|(vscale<<28)|(mhratio<<16)|(mvratio),rCIPRSCCTRL); __raw_writel(cfg->pre_x*cfg->pre_y,rCIPRTAREA); }static __inline void start_capture(struct s3c2440_camif *dev){ switch(dev->mode) { case 0: case 1: __raw_writel(__raw_readl(rCIPRSCCTRL) | (1<<15),rCIPRSCCTRL); __raw_writel(__raw_readl(rCIIMGCPT) | (5<<29),rCIIMGCPT); break; case 2: case 3: __raw_writel(__raw_readl(rCICOSCCTRL) | (1<<15)|(s3c2440_camif_cfg.bypass?(1<<31):0),rCICOSCCTRL); __raw_writel(__raw_readl(rCIIMGCPT) | (6<<29),rCIIMGCPT); break; }}static __inline void stop_capture(struct s3c2440_camif *dev){ switch(dev->mode) { case 0: case 1: __raw_writel(__raw_readl(rCIPRSCCTRL) & ~(1<<15),rCIPRSCCTRL); __raw_writel(__raw_readl(rCIIMGCPT) & ~(7<<29),rCIIMGCPT);#ifndef CAMERA_STOP_QUICKLY __raw_writel(__raw_readl(rCIPRCTRL) | (1<<2),rCIPRCTRL); //NOTE:LastIrqEn bit should be set after clearing CAPTURE_ENABLE_BIT & SCALER_START_BIT dev->flag |= 0x200;#endif break; case 2: case 3: __raw_writel(__raw_readl(rCICOSCCTRL) & ~(1<<15),rCICOSCCTRL); __raw_writel(__raw_readl(rCIIMGCPT) & ~(7<<29),rCIIMGCPT);#ifndef CAMERA_STOP_QUICKLY __raw_writel(__raw_readl(rCICOCTRL) | (1<<2),rCICOCTRL); //NOTE:LastIrqEn bit should be set after clearing CAPTURE_ENABLE_BIT & SCALER_START_BIT dev->flag |= 0x200;#endif break; }}// run in irq disabled statestatic irqreturn_t s3c2440_camif_isr_c(int irq, void *dev_id, struct pt_regs *regs){ struct s3c2440_camif *dev = (struct s3c2440_camif*)dev_id; DPRINTK("%s():\n", __FUNCTION__); if(__raw_readl(S3C2410_SUBSRCPND) & (0x1<<11))
{ __raw_writel(__raw_readl(S3C2410_SUBSRCPND) | (1<<11),S3C2410_SUBSRCPND); __raw_writel(__raw_readl(S3C2410_SRCPND) | (1<<6),S3C2410_SRCPND); __raw_writel(__raw_readl(S3C2410_INTPND) | (1<<6),S3C2410_INTPND);
}
else if(__raw_readl(S3C2410_SUBSRCPND) & (0x1<<12))
{ __raw_writel(__raw_readl(S3C2410_SUBSRCPND) | (2<<11),S3C2410_SUBSRCPND); __raw_writel(__raw_readl(S3C2410_SRCPND) | (1<<6),S3C2410_SRCPND); __raw_writel(__raw_readl(S3C2410_INTPND) | (1<<6),S3C2410_INTPND);
} //GPFDAT ^= 1<<4; //LED0 if(dev->flag&1) { if(dev->flag&0x200) { dev->flag = 0; complete(&dev->stop); } else if(dev->flag&0x100) { stop_capture(dev);#ifdef CAMERA_STOP_QUICKLY dev->flag = 0; complete(&dev->stop);#endif } else#ifdef SKIP_FIRST_FRAME if(dev->flag&4)#endif { if(!(dev->flag&2)) { stop_capture(dev);#ifdef CAMERA_STOP_QUICKLY dev->flag = 0;#endif } dev->rdy = 1; wake_up_interruptible(&dev->wait); } dev->flag |= 4; } else { printk(KERN_ERR "flag is 0x%08x\n", dev->flag); dev->flag = 1; //error state, must not happen stop_capture(dev); printk(KERN_ERR "%s.%d: camera BUG!\n", __FILE__, __LINE__); } return IRQ_HANDLED;}#define s3c2440_camif_isr_p s3c2440_camif_isr_cstatic int v4l_cam_open(struct inode *inode, struct file *file){ unsigned long flags; struct s3c2440_camif *dev =&camif; //dev = container_of(camif.dev,struct s3c2440_camif, video_device); file->private_data = dev; DPRINTK("%s():\n", __FUNCTION__); spin_lock_irqsave(&dev->lock, flags); if(dev->open_count) { spin_unlock_irqrestore(&dev->lock, flags); return -EBUSY; } dev->open_count++; spin_unlock_irqrestore(&dev->lock, flags); DPRINTK("open(): dev->opencount = %d\n", dev->open_count); camera_power(1); //s3c2440_camif_init(); dev->set_chg = 1; dev->mode = 0; //RGB image dev->flag = 0; //not run, not ready s3c2440_camif_cfg.dev = dev; s3c2440_camif_cfg.src_x = 640; s3c2440_camif_cfg.src_y = 480; s3c2440_camif_cfg.dst_x = 240; s3c2440_camif_cfg.dst_y = 180; s3c2440_camif_cfg.pre_x = 240; s3c2440_camif_cfg.pre_y = 180; s3c2440_camif_cfg.dst_fmt = 1; s3c2440_camif_cfg.pre_fmt = 0; s3c2440_camif_cfg.ycbcr = 0; //YCbCr
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -