#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_VIDEO_V4L1_COMPAT #include #endif #include #include #include #include #include #include #include #include #include #include #include "ov9650.h" /* debug print macro. */ /* hardware & driver name, version etc. */ #define CARD_NAME "camera" static unsigned has_ov9650; unsigned long camif_base_addr; /* camera device(s) */ static struct tq2440_camif_dev camera; /* image buffer for previewing. */ struct tq2440_camif_buffer img_buff[] = { { .state = CAMIF_BUFF_INVALID, .img_size = 0, .order = 0, .virt_base = (unsigned long)NULL, .phy_base = (unsigned long)NULL }, { .state = CAMIF_BUFF_INVALID, .img_size = 0, .order = 0, .virt_base = (unsigned long)NULL, .phy_base = (unsigned long)NULL }, { .state = CAMIF_BUFF_INVALID, .img_size = 0, .order = 0, .virt_base = (unsigned long)NULL, .phy_base = (unsigned long)NULL }, { .state = CAMIF_BUFF_INVALID, .img_size = 0, .order = 0, .virt_base = (unsigned long)NULL, .phy_base = (unsigned long)NULL } }; /* software reset camera interface. */ static void __inline__ soft_reset_camif(void) { u32 cigctrl; cigctrl = (1<<31)|(1<<29); iowrite32(cigctrl, S3C244X_CIGCTRL); mdelay(10); cigctrl = (1<<29); iowrite32(cigctrl, S3C244X_CIGCTRL); mdelay(10); } /* 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); } /* switch camif from codec path to preview path. */ static void __inline__ camif_c2p(struct tq2440_camif_dev * pdev) { /* 1. stop codec. */ { u32 cicoscctrl; cicoscctrl = ioread32(S3C244X_CICOSCCTRL); cicoscctrl &= ~(1<<15); // stop preview scaler. iowrite32(cicoscctrl, S3C244X_CICOSCCTRL); } /* 2. soft-reset camif. */ soft_reset_camif(); /* 3. clear all overflow. */ { u32 ciwdofst; ciwdofst = ioread32(S3C244X_CIWDOFST); ciwdofst |= (1<<30)|(1<<15)|(1<<14)|(1<<13)|(1<<12); iowrite32(ciwdofst, S3C244X_CIWDOFST); ciwdofst &= ~((1<<30)|(1<<15)|(1<<14)|(1<<13)|(1<<12)); iowrite32(ciwdofst, S3C244X_CIWDOFST); } } /* calculate main burst size and remained burst size. */ static void __inline__ calc_burst_size(u32 pixperword,u32 hSize, u32 *mainBurstSize, u32 *remainedBurstSize) { u32 tmp; tmp = (hSize/pixperword)%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/pixperword)%8; switch(tmp) { case 0: *mainBurstSize = 8; *remainedBurstSize = 8; break; case 4: *mainBurstSize = 8; *remainedBurstSize = 4; default: *mainBurstSize = 4; tmp = (hSize/pixperword)%4; *remainedBurstSize = (tmp)?tmp:4; break; } break; } } /* calculate prescaler ratio and shift. */ static void __inline__ calc_prescaler_ratio_shift(u32 SrcSize, u32 DstSize, u32 *ratio, u32 *shift) { 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; } } /* update CISRCFMT only. */ static void __inline__ update_source_fmt_regs(struct tq2440_camif_dev * pdev) { u32 cisrcfmt; cisrcfmt = (1<<31) // ITU-R BT.601 YCbCr 8-bit mode |(0<<30) // CB,Cr value offset cntrol for YCbCr |(pdev->srcHsize<<16) // source image width |(2<<14) // input order is CbYCrY |(pdev->srcVsize<<0); // source image height iowrite32(cisrcfmt, S3C244X_CISRCFMT); } /* 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 tq2440_camif_dev * pdev) { u32 ciprtrgfmt; u32 ciprctrl; u32 ciprscctrl; u32 mainBurstSize, remainedBurstSize; /* CIPRCLRSA1 ~ CIPRCLRSA4. */ iowrite32(img_buff[0].phy_base, S3C244X_CIPRCLRSA1); 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); } /* update CIWDOFST only. */ static void __inline__ update_target_wnd_regs(struct tq2440_camif_dev * pdev) { u32 ciwdofst; u32 winHorOfst, winVerOfst; winHorOfst = (pdev->srcHsize - pdev->wndHsize)>>1; winVerOfst = (pdev->srcVsize - pdev->wndVsize)>>1; winHorOfst &= 0xFFFFFFF8; winVerOfst &= 0xFFFFFFF8; if ((winHorOfst == 0)&&(winVerOfst == 0)) { ciwdofst = 0; // disable windows offset. } else { ciwdofst = (1<<31) // window offset enable |(1<<30) // clear the overflow ind flag of input CODEC FIFO Y |(winHorOfst<<16) // windows horizontal offset |(1<<15) // clear the overflow ind flag of input CODEC FIFO Cb |(1<<14) // clear the overflow ind flag of input CODEC FIFO Cr |(1<<13) // clear the overflow ind flag of input PREVIEW FIFO Cb |(1<<12) // clear the overflow ind flag of input PREVIEW FIFO Cr |(winVerOfst<<0); // window vertical offset } iowrite32(ciwdofst, S3C244X_CIWDOFST); } /* update registers: * PREVIEW path: * CIPRSCPRERATIO * CIPRSCPREDST * CIPRSCCTRL * CODEC path: * CICOSCPRERATIO * CICOSCPREDST * CICOSCCTRL */ static void __inline__ update_target_zoom_regs(struct tq2440_camif_dev * pdev) { u32 preHratio, preVratio; u32 Hshift, Vshift; u32 shfactor; u32 preDstWidth, preDstHeight; u32 Hscale, Vscale; u32 mainHratio, mainVratio; u32 ciprscpreratio; u32 ciprscpredst; u32 ciprscctrl; /* CIPRSCPRERATIO. */ calc_prescaler_ratio_shift(pdev->wndHsize, pdev->preTargetHsize, &preHratio, &Hshift); calc_prescaler_ratio_shift(pdev->wndVsize, pdev->preTargetVsize, &preVratio, &Vshift); shfactor = 10 - (Hshift + Vshift); ciprscpreratio = (shfactor<<28) // shift factor for preview pre-scaler |(preHratio<<16) // horizontal ratio of preview pre-scaler |(preVratio<<0); // vertical ratio of preview pre-scaler iowrite32(ciprscpreratio, S3C244X_CIPRSCPRERATIO); /* CIPRSCPREDST. */ preDstWidth = pdev->wndHsize / preHratio; preDstHeight = pdev->wndVsize / preVratio; ciprscpredst = (preDstWidth<<16) // destination width for preview pre-scaler |(preDstHeight<<0); // destination height for preview pre-scaler iowrite32(ciprscpredst, S3C244X_CIPRSCPREDST); /* CIPRSCCTRL. */ Hscale = (pdev->wndHsize >= pdev->preTargetHsize)?0:1; Vscale = (pdev->wndVsize >= pdev->preTargetVsize)?0:1; mainHratio = (pdev->wndHsize<<8)/(pdev->preTargetHsize<wndVsize<<8)/(pdev->preTargetVsize<state = CAMIF_STATE_PREVIEWING; ciimgcpt = (1<<31) // camera interface global capture enable |(1<<29); // capture enable for preview scaler. iowrite32(ciimgcpt, S3C244X_CIIMGCPT); ret = 0; if (stream == 0) { pdev->cmdcode = CAMIF_CMD_STOP; ret = wait_event_interruptible(pdev->cmdqueue, pdev->cmdcode == CAMIF_CMD_NONE); } return ret; } /* stop image capture, always called in ISR. * P-path regs: * CIPRSCCTRL * CIPRCTRL * C-path regs: * CICOSCCTRL. * CICOCTRL * Global regs: * CIIMGCPT */ static void stop_capture(struct tq2440_camif_dev * pdev) { u32 ciprscctrl; u32 ciprctrl; u32 cicoscctrl; u32 cicoctrl; switch(pdev->state) { case CAMIF_STATE_PREVIEWING: /* CIPRCTRL. */ ciprctrl = ioread32(S3C244X_CIPRCTRL); ciprctrl |= 1<<2; // enable last IRQ at the end of frame capture. iowrite32(ciprctrl, S3C244X_CIPRCTRL); /* CIPRSCCTRL. */ ciprscctrl = ioread32(S3C244X_CIPRSCCTRL); ciprscctrl &= ~(1<<15); // clear preview scaler start bit. iowrite32(ciprscctrl, S3C244X_CIPRSCCTRL); /* CIIMGCPT. */ iowrite32(0, S3C244X_CIIMGCPT); pdev->state = CAMIF_STATE_READY; break; case CAMIF_STATE_CODECING: /* CICOCTRL. */ cicoctrl = ioread32(S3C244X_CICOCTRL); cicoctrl |= 1<<2; // enable last IRQ at the end of frame capture. iowrite32(cicoctrl, S3C244X_CICOCTRL); /* CICOSCCTRL. */ cicoscctrl = ioread32(S3C244X_CICOSCCTRL); cicoscctrl &= ~(1<<15); // clear codec scaler start bit. iowrite32(cicoscctrl, S3C244X_CICOSCCTRL); /* CIIMGCPT. */ iowrite32(0, S3C244X_CIIMGCPT); pdev->state = CAMIF_STATE_READY; break; } } /* update camera interface with the new config. */ static void update_camif_config (struct tq2440_camif_fh * fh, u32 cmdcode) { struct tq2440_camif_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; } /* target image window offset. */ if (cmdcode & CAMIF_CMD_WND) { pdev->cmdcode |= CAMIF_CMD_WND; } /* target image zoomi & zoomout. */ if (cmdcode & CAMIF_CMD_ZOOM) { pdev->cmdcode |= CAMIF_CMD_ZOOM; } /* stop previewing. */ if (cmdcode & CAMIF_CMD_STOP) { pdev->cmdcode |= CAMIF_CMD_STOP; } enable_irq(IRQ_S3C2440_CAM_P); // enable cam-preview irq. wait_event(pdev->cmdqueue, (pdev->cmdcode==CAMIF_CMD_NONE)); // wait until the ISR completes command. break; case CAMIF_STATE_CODECING: /* camif is previewing image. */ disable_irq(IRQ_S3C2440_CAM_C); // disable cam-codec 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; } /* target image window offset. */ if (cmdcode & CAMIF_CMD_WND) { pdev->cmdcode |= CAMIF_CMD_WND; } /* target image zoomi & zoomout. */ if (cmdcode & CAMIF_CMD_ZOOM) { pdev->cmdcode |= CAMIF_CMD_ZOOM; } /* stop previewing. */ if (cmdcode & CAMIF_CMD_STOP) { pdev->cmdcode |= CAMIF_CMD_STOP; } enable_irq(IRQ_S3C2440_CAM_C); // enable cam-codec irq. wait_event(pdev->cmdqueue, (pdev->cmdcode==CAMIF_CMD_NONE)); // wait until the ISR completes command. break; default: break; } } /* config camif when master-open camera.*/ static void init_camif_config(struct tq2440_camif_fh * fh) { struct tq2440_camif_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 = 320; pdev->preTargetVsize = 240; update_camif_config(fh, CAMIF_CMD_STOP); } static void __inline__ invalid_image_buffer(void) { img_buff[0].state = CAMIF_BUFF_INVALID; img_buff[1].state = CAMIF_BUFF_INVALID; img_buff[2].state = CAMIF_BUFF_INVALID; img_buff[3].state = CAMIF_BUFF_INVALID; } /* 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); 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. img_buff[1].order = order; img_buff[1].virt_base = __get_free_pages(GFP_KERNEL|GFP_DMA, img_buff[1].order); if (img_buff[1].virt_base == (unsigned long)NULL) { goto error1; } img_buff[1].phy_base = img_buff[1].virt_base - PAGE_OFFSET + PHYS_OFFSET; // the DMA address. img_buff[2].order = order; img_buff[2].virt_base = __get_free_pages(GFP_KERNEL|GFP_DMA, img_buff[2].order); if (img_buff[2].virt_base == (unsigned long)NULL) { goto error2; } img_buff[2].phy_base = img_buff[2].virt_base - PAGE_OFFSET + PHYS_OFFSET; // the DMA address. img_buff[3].order = order; img_buff[3].virt_base = __get_free_pages(GFP_KERNEL|GFP_DMA, img_buff[3].order); if (img_buff[3].virt_base == (unsigned long)NULL) { goto error3; } img_buff[3].phy_base = img_buff[3].virt_base - PAGE_OFFSET + PHYS_OFFSET; // the DMA address. invalid_image_buffer(); return 0; error3: free_pages(img_buff[2].virt_base, order); img_buff[2].phy_base = (unsigned long)NULL; error2: free_pages(img_buff[1].virt_base, order); img_buff[1].phy_base = (unsigned long)NULL; error1: free_pages(img_buff[0].virt_base, order); img_buff[0].phy_base = (unsigned long)NULL; error0: return -ENOMEM; } /* free image buffers (only when the camif is latest close). */ static void __inline__ free_image_buffer(void) { free_pages(img_buff[0].virt_base, img_buff[0].order); free_pages(img_buff[1].virt_base, img_buff[1].order); free_pages(img_buff[2].virt_base, img_buff[2].order); free_pages(img_buff[3].virt_base, img_buff[3].order); img_buff[0].order = 0; img_buff[0].virt_base = (unsigned long)NULL; img_buff[0].phy_base = (unsigned long)NULL; img_buff[1].order = 0; img_buff[1].virt_base = (unsigned long)NULL; img_buff[1].phy_base = (unsigned long)NULL; img_buff[2].order = 0; img_buff[2].virt_base = (unsigned long)NULL; img_buff[2].phy_base = (unsigned long)NULL; img_buff[3].order = 0; img_buff[3].virt_base = (unsigned long)NULL; img_buff[3].phy_base = (unsigned long)NULL; } /* * ISR: service for C-path interrupt. */ static irqreturn_t on_camif_irq_c(int irq, void * dev) { u32 cicostatus; u32 frame; struct tq2440_camif_dev * pdev; cicostatus = ioread32(S3C244X_CICOSTATUS); if ((cicostatus & (1<<21))== 0) { return IRQ_RETVAL(IRQ_NONE); } pdev = (struct tq2440_camif_dev *)dev; /* valid img_buff[x] just DMAed. */ frame = (cicostatus&(3<<26))>>26; frame = (frame+4-1)%4; if (pdev->cmdcode & CAMIF_CMD_STOP) { stop_capture(pdev); pdev->state = CAMIF_STATE_READY; } else { if (pdev->cmdcode & CAMIF_CMD_C2P) { 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; wake_up(&pdev->cmdqueue); return IRQ_RETVAL(IRQ_HANDLED); } /* * ISR: service for P-path interrupt. */ static irqreturn_t on_camif_irq_p(int irq, void * dev) { u32 ciprstatus; u32 frame; struct tq2440_camif_dev * pdev; ciprstatus = ioread32(S3C244X_CIPRSTATUS); if ((ciprstatus & (1<<21))== 0) { return IRQ_RETVAL(IRQ_NONE); } pdev = (struct tq2440_camif_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) { 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; wake_up(&pdev->cmdqueue); return IRQ_RETVAL(IRQ_HANDLED); } /* * camif_open() */ static int camif_open(struct inode *inode, struct file *file) { struct tq2440_camif_dev *pdev; struct tq2440_camif_fh *fh; int ret; if (!has_ov9650) { return -ENODEV; } pdev = &camera; fh = kzalloc(sizeof(*fh),GFP_KERNEL); // alloc memory for filehandle if (NULL == fh) { return -ENOMEM; } fh->dev = pdev; pdev->state = CAMIF_STATE_READY; init_camif_config(fh); ret = init_image_buffer(); // init image buffer. if (ret < 0) { goto error1; } request_irq(IRQ_S3C2440_CAM_C, on_camif_irq_c, IRQF_DISABLED, "CAM_C", pdev); // setup ISRs if (ret < 0) { goto error2; } request_irq(IRQ_S3C2440_CAM_P, on_camif_irq_p, IRQF_DISABLED, "CAM_P", pdev); if (ret < 0) { goto error3; } clk_enable(pdev->clk); // and enable camif clock. soft_reset_camif(); file->private_data = fh; fh->dev = pdev; update_camif_config(fh, 0); return 0; error3: free_irq(IRQ_S3C2440_CAM_C, pdev); error2: free_image_buffer(); error1: kfree(fh); return ret; } /* * camif_read() */ static ssize_t camif_read(struct file *file, char __user *data, size_t count, loff_t *ppos) { int i; struct tq2440_camif_fh * fh; struct tq2440_camif_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[i].state != CAMIF_BUFF_INVALID) { copy_to_user(data, (void *)img_buff[i].virt_base, count); img_buff[i].state = CAMIF_BUFF_INVALID; } } enable_irq(IRQ_S3C2440_CAM_P); enable_irq(IRQ_S3C2440_CAM_C); return count; } /* * camif_release() */ static int camif_release(struct inode *inode, struct file *file) { struct tq2440_camif_fh * fh; struct tq2440_camif_dev * pdev; fh = file->private_data; pdev = fh->dev; clk_disable(pdev->clk); // stop camif clock free_irq(IRQ_S3C2440_CAM_P, pdev); // free camif IRQs free_irq(IRQ_S3C2440_CAM_C, pdev); free_image_buffer(); // and free image buffer return 0; } /* * the key methods/attributes for this video device. */ static struct file_operations camif_fops = { .owner = THIS_MODULE, .open = camif_open, .release = camif_release, .read = camif_read, }; static struct miscdevice misc = { .minor = MISC_DYNAMIC_MINOR, .name = CARD_NAME, .fops = &camif_fops, }; static void __inline__ sccb_start(void) { CFG_WRITE(SIO_D); Low(SIO_D); WAIT_STABLE(); } static void __inline__ sccb_write_byte(u8 data) { int i; CFG_WRITE(SIO_D); WAIT_STABLE(); /* write 8-bits octet. */ for (i=0;i<8;i++) { Low(SIO_C); WAIT_STABLE(); if (data & 0x80) { High(SIO_D); } else { Low(SIO_D); } data = data<<1; WAIT_CYCLE(); High(SIO_C); WAIT_CYCLE(); } /* write byte done, wait the Don't care bit now. */ { Low(SIO_C); High(SIO_D); CFG_READ(SIO_D); WAIT_CYCLE(); High(SIO_C); WAIT_CYCLE(); } } static u8 __inline__ sccb_read_byte(void) { int i; u8 data; CFG_READ(SIO_D); WAIT_STABLE(); Low(SIO_C); WAIT_CYCLE(); data = 0; for (i=0;i<8;i++) { High(SIO_C); WAIT_STABLE(); data = data<<1; data |= State(SIO_D)?1:0; WAIT_CYCLE(); Low(SIO_C); WAIT_CYCLE(); } /* read byte down, write the NA bit now.*/ { CFG_WRITE(SIO_D); High(SIO_D); WAIT_CYCLE(); High(SIO_C); WAIT_CYCLE(); } return data; } static void __inline__ sccb_stop(void) { Low(SIO_C); WAIT_STABLE(); CFG_WRITE(SIO_D); Low(SIO_D); WAIT_CYCLE(); High(SIO_C); WAIT_STABLE(); High(SIO_D); WAIT_CYCLE(); CFG_READ(SIO_D); } 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); } u8 sccb_read(u8 IdAddr, u8 SubAddr) { u8 data; down(&bus_lock); sccb_start(); sccb_write_byte(IdAddr); sccb_write_byte(SubAddr); sccb_stop(); sccb_start(); sccb_write_byte(IdAddr|0x01); data = sccb_read_byte(); sccb_stop(); up(&bus_lock); return data; } static void __inline__ ov9650_poweron(void) { s3c2410_gpio_cfgpin(S3C2410_GPG11, S3C2410_GPG11_OUTP); s3c2410_gpio_setpin(S3C2410_GPG11, 0); mdelay(20); } static void __inline__ ov9650_poweroff(void) { s3c2410_gpio_cfgpin(S3C2410_GPG11, S3C2410_GPG11_OUTP); s3c2410_gpio_setpin(S3C2410_GPG11, 1); mdelay(20); } static int __inline__ ov9650_check(void) { u32 mid; mid = sccb_read(OV9650_SCCB_ADDR, 0x1c)<<8; mid |= sccb_read(OV9650_SCCB_ADDR, 0x1d); printk("OV9650 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; } static u32 __inline__ show_ov9650_product_id(void) { u32 pid; pid = sccb_read(OV9650_SCCB_ADDR, 0x0a)<<8; pid |= sccb_read(OV9650_SCCB_ADDR, 0x0b); printk("OV9650 address 0x%02X, product ID 0x%04X, expect 0x%04X\n", OV9650_SCCB_ADDR, pid, OV9650_PRODUCT_ID); return pid; } static void ov9650_init_regs(void) { int i; down(®s_mutex); for (i=0; iclk = 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); /* init reference counter and its mutex. */ mutex_init(&pdev->rcmutex); pdev->rc = 0; /* init image input source. */ pdev->input = 0; /* init camif state and its lock. */ pdev->state = CAMIF_STATE_FREE; /* init command code, command lock and the command wait queue. */ pdev->cmdcode = CAMIF_CMD_NONE; init_waitqueue_head(&pdev->cmdqueue); /* register to videodev layer. */ if (misc_register(&misc) < 0) { ret = -EBUSY; goto error4; } printk(KERN_ALERT"s3c2440 camif init done\n"); // sccb_init(); CFG_WRITE(SIO_C); CFG_WRITE(SIO_D); High(SIO_C); High(SIO_D); WAIT_STABLE(); hw_reset_camif(); has_ov9650 = s3c2440_ov9650_init() >= 0; s3c2410_gpio_setpin(S3C2410_GPG4, 1); return 0; error4: clk_put(pdev->clk); error3: iounmap((void *)camif_base_addr); error2: release_mem_region((unsigned long)S3C2440_PA_CAMIF, S3C2440_SZ_CAMIF); error1: return ret; } /* * camif_cleanup() */ static void __exit camif_cleanup(void) { struct tq2440_camif_dev *pdev; // sccb_cleanup(); CFG_READ(SIO_C); CFG_READ(SIO_D); pdev = &camera; misc_deregister(&misc); clk_put(pdev->clk); iounmap((void *)camif_base_addr); release_mem_region((unsigned long)S3C2440_PA_CAMIF, S3C2440_SZ_CAMIF); printk(KERN_ALERT"tq2440_camif: module removed\n"); } MODULE_LICENSE("GPL"); module_init(camif_init); module_exit(camif_cleanup);