\drivers\media\video\s3c2440camif.c \drivers\media\video\s3c2440_ov9650.c \drivers\media\video\sccb.c 硬件连接 I2C总线连接
I2CSCL ——GPE14 I2CSDA——GPE15 Sccb.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设置为低电平
+ [5 d2 s0 q5 U* G, rSccb.c
把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拉低,即为START static 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 openned CAMIF_STATE_READY = 1, // openned, but standby CAMIF_STATE_PREVIEWING = 2, // in previewing CAMIF_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(); 硬件复位CAMIF hw_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); }
检测是否存在OV9650,GPG4连的是LCD_PWR,不知道为什么? has_ov9650 = s3c2440_ov9650_init() >= 0; s3c2410_gpio_setpin(S3C2410_GPG4, 1);
OV9650的初始化: 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 down static void __inline__ ov9650_poweron(void) { s3c2410_gpio_cfgpin(S3C2410_GPG12, S3C2410_GPG12_OUTP); s3c2410_gpio_setpin(S3C2410_GPG12, 0); mdelay(20); }
OV9650检测,读取OV9650的Manu ID static 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[0].order = order; img_buff[0].virt_base = __get_free_pages(GFP_KERNEL|GFP_DMA, img_buff[0].order); if (img_buff[0].virt_base == (unsigned long)NULL) { goto error0; } img_buff[0].phy_base = img_buff[0].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 offset ciwdofst |= (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 Cr iowrite32(ciwdofst, S3C244X_CIWDOFST); ciprscctrl = ioread32(S3C244X_CIPRSCCTRL); //Preview main scale control开始prev ciprscctrl |= 1<<15; // preview scaler start iowrite32(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[x] just DMAed. */ frame = (ciprstatus&(3<<26))>>26; 计算帧数? frame = (frame+4-1)%4; img_buff[frame].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中的queue wake_up(&pdev->cmdqueue); return IRQ_RETVAL(IRQ_HANDLED); }
配置目标格式寄存器 /* 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[0].phy_base, S3C244X_CIPRCLRSA1); DMA的地址 iowrite32(img_buff[1].phy_base, S3C244X_CIPRCLRSA2); iowrite32(img_buff[2].phy_base, S3C244X_CIPRCLRSA3); iowrite32(img_buff[3].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 image iowrite32(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 RGB iowrite32(ciprscctrl, S3C244X_CIPRSCCTRL); // 16-bit RGB /* CIPRTAREA. */ iowrite32(pdev->preTargetHsize * pdev->preTargetVsize, S3C244X_CIPRTAREA); } % X6 K9 R. X5 M6 _! T/ H$ B
|