Anonymous 发表于 2015-8-12 14:59:10

OV9650摄像头驱动学习

\drivers\media\video\s3c2440camif.c\drivers\media\video\s3c2440_ov9650.c\drivers\media\video\sccb.c硬件连接I2C总线连接http://images.cnblogs.com/cnblogs_com/yanhc/201109/201109132303556870.jpghttp://images.cnblogs.com/cnblogs_com/yanhc/201109/20110913230355524.jpgI2CSCL ——GPE14I2CSDA——GPE15Sccb.h#define SIO_C S3C2410_GPE14#define SIO_D S3C2410_GPE15#define State(x) s3c2410_gpio_getpin(x)#define High(x) do{s3c2410_gpio_setpin(x,1); smp_mb();}while(0)#define Low(x) do{s3c2410_gpio_setpin(x,0); smp_mb();}while(0)#define WAIT_STABLE() do{udelay(10);}while(0)#define WAIT_CYCLE() do{udelay(90);}while(0)#define CFG_READ(x) do{s3c2410_gpio_cfgpin(x,S3C2410_GPIO_INPUT);smp_mb();}while(0)#define CFG_WRITE(x) do{s3c2410_gpio_cfgpin(x,S3C2410_GPIO_OUTPUT);smp_mb();}while(0)void sccb_write(u8 IdAddr, u8 SubAddr, u8 data);u8 sccb_read(u8 IdAddr, u8 SubAddr);CFG_WRITE(x)把GPIO设置为输出CFG_READ(x)把GPIO设置为输入High(x) 把GPIO设置为高电平Low(x) 把GPIO设置为低电平
Sccb.chttp://images.cnblogs.com/cnblogs_com/yanhc/201109/201109132303566445.jpg把SCL和Data都拉高,即为默认初始化状态电平int sccb_init(void){CFG_WRITE(SIO_C);CFG_WRITE(SIO_D);High(SIO_C);High(SIO_D);WAIT_STABLE();return 0;}由上面的图可以看出,CLK高电平时,DATA拉低,即为STARTstatic void __inline__ sccb_start(void){CFG_WRITE(SIO_D);Low(SIO_D);WAIT_STABLE();}使用到一个信号量static DECLARE_MUTEX(bus_lock);void sccb_write(u8 IdAddr, u8 SubAddr, u8 data){down(&bus_lock);sccb_start();sccb_write_byte(IdAddr);sccb_write_byte(SubAddr);sccb_write_byte(data);sccb_stop();up (&bus_lock);}首先把信号量数值降低,表示自己使用,如果此时信号量不大于0,表示bus正在使用,驱动会在此等待,知道信号量大于0,然后,将其减1,此时bus对外界不可用。最后再把信号量加1,表示bus可用。%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%摄像头接口初始化:/** camif_init()*/static int __init camif_init(void){配置CAMIF的GPIO接口功能/* set gpio-j to camera mode. */s3c2410_gpio_cfgpin(S3C2440_GPJ0, S3C2440_GPJ0_CAMDATA0);s3c2410_gpio_cfgpin(S3C2440_GPJ1, S3C2440_GPJ1_CAMDATA1);s3c2410_gpio_cfgpin(S3C2440_GPJ2, S3C2440_GPJ2_CAMDATA2);s3c2410_gpio_cfgpin(S3C2440_GPJ3, S3C2440_GPJ3_CAMDATA3);s3c2410_gpio_cfgpin(S3C2440_GPJ4, S3C2440_GPJ4_CAMDATA4);s3c2410_gpio_cfgpin(S3C2440_GPJ5, S3C2440_GPJ5_CAMDATA5);s3c2410_gpio_cfgpin(S3C2440_GPJ6, S3C2440_GPJ6_CAMDATA6);s3c2410_gpio_cfgpin(S3C2440_GPJ7, S3C2440_GPJ7_CAMDATA7);s3c2410_gpio_cfgpin(S3C2440_GPJ8, S3C2440_GPJ8_CAMPCLK);s3c2410_gpio_cfgpin(S3C2440_GPJ9, S3C2440_GPJ9_CAMVSYNC);s3c2410_gpio_cfgpin(S3C2440_GPJ10, S3C2440_GPJ10_CAMHREF);s3c2410_gpio_cfgpin(S3C2440_GPJ11, S3C2440_GPJ11_CAMCLKOUT);s3c2410_gpio_cfgpin(S3C2440_GPJ12, S3C2440_GPJ12_CAMRESET);申请CAMIF的寄存器区域,由于OS的系统统一管理,所以,需要确定,该寄存器区域没有被别的进程获取,所以要申请。/* init camera's virtual memory. */if (!request_mem_region((unsigned long)S3C2440_PA_CAMIF, S3C2440_SZ_CAMIF, CARD_NAME)){ret = -EBUSY;goto error1;}申请到了寄存器区域,相当于物理地址可用,然后,再将该物理地址映射到虚拟地址。/* remap the virtual memory. */camif_base_addr = (unsigned long)ioremap_nocache((unsigned long)S3C2440_PA_CAMIF, S3C2440_SZ_CAMIF);if (camif_base_addr == (unsigned long)NULL){ret = -EBUSY;goto error2;}获取CAMIF的时钟,使用24M/* init camera clock. */pdev->clk = clk_get(NULL, "camif");if (IS_ERR(pdev->clk)){ret = -ENOENT;goto error3;}clk_enable(pdev->clk);camif_upll_clk = clk_get(NULL, "camif-upll");clk_set_rate(camif_upll_clk, 24000000);mdelay(100);初始化CAMIF的状态/* init camif state and its lock. */pdev->state = CAMIF_STATE_FREE;CAMIF DEV的状态:/* for s3c2440camif_dev->state field. */enum{CAMIF_STATE_FREE = 0, // not opennedCAMIF_STATE_READY = 1, // openned, but standbyCAMIF_STATE_PREVIEWING = 2, // in previewingCAMIF_STATE_CODECING = 3 // in capturing};注册杂项设备:/* register to videodev layer. */if (misc_register(&misc) < 0){ret = -EBUSY;goto error4;}printk(KERN_ALERT"s3c2440 camif init done\n");初始化SCCB接口,Serial Camera Control Bus,其实是I2C接口sccb_init();硬件复位CAMIFhw_reset_camif();其实是软件复位,对CIGCTRL寄存器配置软件复位/* software reset camera interface. */static void __inline__ hw_reset_camif(void){u32 cigctrl;cigctrl = (1<<30)|(1<<29);iowrite32(cigctrl, S3C244X_CIGCTRL);mdelay(10);cigctrl = (1<<29);iowrite32(cigctrl, S3C244X_CIGCTRL);mdelay(10);}http://images.cnblogs.com/cnblogs_com/yanhc/201109/201109132303575639.jpg检测是否存在OV9650,GPG4连的是LCD_PWR,不知道为什么?has_ov9650 = s3c2440_ov9650_init() >= 0;s3c2410_gpio_setpin(S3C2410_GPG4, 1);http://images.cnblogs.com/cnblogs_com/yanhc/201109/201109132303574592.jpgOV9650的初始化:int s3c2440_ov9650_init(void){printk(KERN_ALERT"Loading OV9650 driver.........\n");/* power on. */ov9650_poweron();mdelay(100);/* check device. */if (ov9650_check() == 0 && ov9650_check() == 0){printk(KERN_ERR"No OV9650 found!!!\n");return -ENODEV;}show_ov9650_product_id();ov9650_init_regs();printk("ov9650 init done!\n");return 0;}OV9650上电,这里对GPG12设置为输出,这里使用的虽然是中断引脚,但似乎没有用中断,这是一个电源控制引脚,PWDN,0:power on,1:power downstatic void __inline__ ov9650_poweron(void){s3c2410_gpio_cfgpin(S3C2410_GPG12, S3C2410_GPG12_OUTP);s3c2410_gpio_setpin(S3C2410_GPG12, 0);mdelay(20);}http://images.cnblogs.com/cnblogs_com/yanhc/201109/20110913230357722.jpghttp://images.cnblogs.com/cnblogs_com/yanhc/201109/201109132303585771.jpgOV9650检测,读取OV9650的Manu IDstatic int __inline__ ov9650_check(void){u32 mid;mid = sccb_read(OV9650_SCCB_ADDR, 0x1c)<<8;mid |= sccb_read(OV9650_SCCB_ADDR, 0x1d);printk("SCCB address 0x%02X, manufacture ID 0x%04X, expect 0x%04X\n", OV9650_SCCB_ADDR, mid, OV9650_MANUFACT_ID);return (mid==OV9650_MANUFACT_ID)?1:0;}#define OV9650_SCCB_ADDR 0x60#define OV9650_MANUFACT_ID 0x7FA2#define OV9650_PRODUCT_ID 0x9650%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%摄像头接口打开:/** camif_open()*/static int camif_open(struct inode *inode, struct file *file){首先判断是否存在OV9650 camera是文件开始的一个static变量。struct s3c2440camif_dev *pdev;struct s3c2440camif_fh *fh;int ret;if (!has_ov9650) {return -ENODEV;}pdev = &camera;为file handle分配内存fh = kzalloc(sizeof(*fh),GFP_KERNEL); // alloc memory for filehandle设置设备状态为open and standby,初始化CAMIF的配置。pdev->state = CAMIF_STATE_READY;init_camif_config(fh);对CAMIF进行配置,最后更新一下配置/* config camif when master-open camera.*/static void init_camif_config(struct s3c2440camif_fh * fh){struct s3c2440camif_dev * pdev;pdev = fh->dev;pdev->input = 0; // FIXME, the default input image format, see inputs[] for detail./* the source image size (input from external camera). */pdev->srcHsize = 1280; // FIXME, the OV9650's horizontal output pixels.pdev->srcVsize = 1024; // FIXME, the OV9650's verical output pixels./* the windowed image size. */pdev->wndHsize = 1280;pdev->wndVsize = 1024;/* codec-path target(output) image size. */pdev->coTargetHsize = pdev->wndHsize;pdev->coTargetVsize = pdev->wndVsize;/* preview-path target(preview) image size. */pdev->preTargetHsize = 640;pdev->preTargetVsize = 512;update_camif_config(fh, CAMIF_CMD_STOP);}由于设备状态是CAMIF_STATE_READY所以,直接更新寄存器。/* update camera interface with the new config. */static void update_camif_config (struct s3c2440camif_fh * fh, u32 cmdcode){struct s3c2440camif_dev * pdev;pdev = fh->dev;switch (pdev->state){case CAMIF_STATE_READY:update_camif_regs(fh->dev); // config the regs directly.break;case CAMIF_STATE_PREVIEWING:/* camif is previewing image. */disable_irq(IRQ_S3C2440_CAM_P); // disable cam-preview irq./* source image format. */if (cmdcode & CAMIF_CMD_SFMT){// ignore it, nothing to do now.}/* target image format. */if (cmdcode & CAMIF_CMD_TFMT){/* change target image format only. */pdev->cmdcode |= CAMIF_CMD_TFMT;}不知为什么要等待VSYNC为L?然后对寄存器进行配置。/* update camif registers, called only when camif ready, or ISR. */static void __inline__ update_camif_regs(struct s3c2440camif_dev * pdev){if (!in_irq()){while(1) // wait until VSYNC is 'L'{barrier();if ((ioread32(S3C244X_CICOSTATUS)&(1<<28)) == 0)break;}}/* WARNING: don't change the statement sort below!!! */update_source_fmt_regs(pdev);update_target_wnd_regs(pdev);update_target_fmt_regs(pdev);update_target_zoom_regs(pdev);}初始化CAMIF的DMA内存。ret = init_image_buffer(); // init image buffer.这里为DMA分配内存,按页分配,内存管理的知识需要学习?/* init image buffer (only when the camif is first open). */static int __inline__ init_image_buffer(void){int size1, size2;unsigned long size;unsigned int order;/* size1 is the max image size of codec path. */size1 = MAX_C_WIDTH * MAX_C_HEIGHT * 16 / 8;/* size2 is the max image size of preview path. */size2 = MAX_P_WIDTH * MAX_P_HEIGHT * 16 / 8;size = (size1 > size2)?size1:size2;order = get_order(size); //获取需要分配字节的2的阶数,内存按页分配img_buff.order = order;img_buff.virt_base = __get_free_pages(GFP_KERNEL|GFP_DMA, img_buff.order);if (img_buff.virt_base == (unsigned long)NULL){goto error0;}img_buff.phy_base = img_buff.virt_base - PAGE_OFFSET + PHYS_OFFSET; // the DMA address.申请中断,分别为Codec的中断和Preview的中断request_irq(IRQ_S3C2440_CAM_C, on_camif_irq_c, IRQF_DISABLED, "CAM_C", pdev);request_irq(IRQ_S3C2440_CAM_P, on_camif_irq_p, IRQF_DISABLED, "CAM_P", pdev);使能时钟,软件复位,更新CAMIF的配置。clk_enable(pdev->clk); // and enable camif clock.soft_reset_camif();file->private_data = fh;fh->dev = pdev;update_camif_config(fh, 0);%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%摄像头接口读取函数:static ssize_t camif_read(struct file *file, char __user *data, size_t count, loff_t *ppos){int i;struct s3c2440camif_fh * fh;struct s3c2440camif_dev * pdev;fh = file->private_data;pdev = fh->dev;if (start_capture(pdev, 0) != 0) //此处是捕获一张图片,所以会阻塞在此,直至中断发生。{return -ERESTARTSYS;}//中断已经发生,数据已经更新。disable_irq(IRQ_S3C2440_CAM_C);disable_irq(IRQ_S3C2440_CAM_P);for (i = 0; i < 4; i++){if (img_buff.state != CAMIF_BUFF_INVALID) //如果数据已经更新,移动数据{copy_to_user(data, (void *)img_buff.virt_base, count);img_buff.state = CAMIF_BUFF_INVALID; //设置数据无效}}enable_irq(IRQ_S3C2440_CAM_P); //重新使能中断enable_irq(IRQ_S3C2440_CAM_C);return count;}开始捕获函数,设置window offset/* start image capture.** param 'stream' means capture pictures streamly or capture only one picture.*/static int start_capture(struct s3c2440camif_dev * pdev, int stream){int ret;u32 ciwdofst;u32 ciprscctrl;u32 ciimgcpt;ciwdofst = ioread32(S3C244X_CIWDOFST); //window offsetciwdofst |= (1<<30) // Clear the overflow indication flag of input CODEC FIFO Y|(1<<15) // Clear the overflow indication flag of input CODEC FIFO Cb|(1<<14) // Clear the overflow indication flag of input CODEC FIFO Cr|(1<<13) // Clear the overflow indication flag of input PREVIEW FIFO Cb|(1<<12); // Clear the overflow indication flag of input PREVIEW FIFO Criowrite32(ciwdofst, S3C244X_CIWDOFST);ciprscctrl = ioread32(S3C244X_CIPRSCCTRL); //Preview main scale control开始prevciprscctrl |= 1<<15; // preview scaler startiowrite32(ciprscctrl, S3C244X_CIPRSCCTRL);pdev->state = CAMIF_STATE_PREVIEWING;ciimgcpt = (1<<31) // camera interface global capture enable|(1<<29); // capture enable for preview scaler.iowrite32(ciimgcpt, S3C244X_CIIMGCPT); //Image capture enable开始捕获//如果是捕获视频的话,会一直循环这样,只是数据到什么地方了呢?ret = 0;if (stream == 0){pdev->cmdcode = CAMIF_CMD_STOP;//如果是捕获图片的话,那么只会中断一次,然后STOP,在此阻塞,等待中断发生ret = wait_event_interruptible(pdev->cmdqueue, pdev->cmdcode == CAMIF_CMD_NONE);}return ret;}%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%摄像头接口中断服务P path函数:/** ISR: service for P-path interrupt.*/static irqreturn_t on_camif_irq_p(int irq, void * dev){u32 ciprstatus;u32 frame;struct s3c2440camif_dev * pdev;ciprstatus = ioread32(S3C244X_CIPRSTATUS); 检查状态if ((ciprstatus & (1<<21))== 0){return IRQ_RETVAL(IRQ_NONE);}pdev = (struct s3c2440camif_dev *)dev;/* valid img_buff just DMAed. */frame = (ciprstatus&(3<<26))>>26; 计算帧数?frame = (frame+4-1)%4;img_buff.state = CAMIF_BUFF_RGB565;if (pdev->cmdcode & CAMIF_CMD_STOP) 查看有没有CMD{stop_capture(pdev);pdev->state = CAMIF_STATE_READY;}else{if (pdev->cmdcode & CAMIF_CMD_P2C){camif_c2p(pdev);}if (pdev->cmdcode & CAMIF_CMD_WND){update_target_wnd_regs(pdev);}if (pdev->cmdcode & CAMIF_CMD_TFMT){update_target_fmt_regs(pdev);}if (pdev->cmdcode & CAMIF_CMD_ZOOM){update_target_zoom_regs(pdev);}invalid_image_buffer();}pdev->cmdcode = CAMIF_CMD_NONE; 最后将CMD置为NONE,然后唤醒Read中的queuewake_up(&pdev->cmdqueue);return IRQ_RETVAL(IRQ_HANDLED);}http://images.cnblogs.com/cnblogs_com/yanhc/201109/201109132303597266.jpg配置目标格式寄存器/* update registers:* PREVIEW path:* CIPRCLRSA1 ~ CIPRCLRSA4* CIPRTRGFMT* CIPRCTRL* CIPRSCCTRL* CIPRTAREA* CODEC path:* CICOYSA1 ~ CICOYSA4* CICOCBSA1 ~ CICOCBSA4* CICOCRSA1 ~ CICOCRSA4* CICOTRGFMT* CICOCTRL* CICOTAREA*/static void __inline__ update_target_fmt_regs(struct s3c2440camif_dev * pdev){u32 ciprtrgfmt;u32 ciprctrl;u32 ciprscctrl;u32 mainBurstSize, remainedBurstSize;/* CIPRCLRSA1 ~ CIPRCLRSA4. */iowrite32(img_buff.phy_base, S3C244X_CIPRCLRSA1); DMA的地址iowrite32(img_buff.phy_base, S3C244X_CIPRCLRSA2);iowrite32(img_buff.phy_base, S3C244X_CIPRCLRSA3);iowrite32(img_buff.phy_base, S3C244X_CIPRCLRSA4);/* CIPRTRGFMT. */ciprtrgfmt = (pdev->preTargetHsize<<16) // horizontal pixel number of target image|(0<<14) // don't mirror or rotation.|(pdev->preTargetVsize<<0); // vertical pixel number of target imageiowrite32(ciprtrgfmt, S3C244X_CIPRTRGFMT);/* CIPRCTRL. */calc_burst_size(2, pdev->preTargetHsize, &mainBurstSize, &remainedBurstSize);ciprctrl = (mainBurstSize<<19)|(remainedBurstSize<<14);iowrite32(ciprctrl, S3C244X_CIPRCTRL);/* CIPRSCCTRL. */ciprscctrl = ioread32(S3C244X_CIPRSCCTRL);ciprscctrl &= 1<<15; // clear all other info except 'preview scaler start'.ciprscctrl |= 0<<30; // 16-bits RGBiowrite32(ciprscctrl, S3C244X_CIPRSCCTRL); // 16-bit RGB/* CIPRTAREA. */iowrite32(pdev->preTargetHsize * pdev->preTargetVsize, S3C244X_CIPRTAREA);}
页: [1]
查看完整版本: OV9650摄像头驱动学习