#include <linux/module.h> #include <linux/delay.h> #include <linux/errno.h>

advertisement
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
<linux/module.h>
<linux/delay.h>
<linux/errno.h>
<linux/fs.h>
<linux/kernel.h>
<linux/slab.h>
<linux/mm.h>
<linux/ioport.h>
<linux/init.h>
<linux/sched.h>
#include <linux/clk.h>
#include <linux/random.h>
#include <linux/version.h>
#include <linux/videodev2.h>
#include <linux/dma-mapping.h>
#ifdef CONFIG_VIDEO_V4L1_COMPAT
#include <linux/videodev.h>
#endif
#include <linux/interrupt.h>
#include <media/v4l2-common.h>
#include <linux/highmem.h>
#include <linux/miscdevice.h>
#include
#include
#include
#include
#include
#include
<asm/io.h>
<asm/memory.h>
<mach/regs-gpio.h>
<mach/regs-gpioj.h>
<mach/regs-clock.h>
<mach/map.h>
#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)
target image
// horizontal pixel number of
|(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)
|(1<<30)
//
|(winHorOfst<<16)
|(1<<15)
//
|(1<<14)
//
|(1<<13)
//
// window offset enable
clear the overflow ind flag of input CODEC FIFO Y
// windows horizontal offset
clear the overflow ind flag of input CODEC FIFO Cb
clear the overflow ind flag of input CODEC FIFO Cr
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
u32
u32
u32
u32
Hshift, Vshift;
shfactor;
preDstWidth, preDstHeight;
Hscale, Vscale;
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<<Hshift);
mainVratio = (pdev->wndVsize<<8)/(pdev->preTargetVsize<<Vshift);
ciprscctrl = ioread32(S3C244X_CIPRSCCTRL);
ciprscctrl &= (1<<30)|(1<<15);
// keep preview image format (RGB565 or
RGB24), and preview scaler start state.
ciprscctrl |= (1<<31) // this bit should be always 1.
|(Hscale<<29) // horizontal scale up/down.
|(Vscale<<28) // vertical scale up/down.
|(mainHratio<<16) // horizontal scale ratio for preview main-scaler
|(mainVratio<<0); // vertical scale ratio for preview main-scaler
iowrite32(ciprscctrl, S3C244X_CIPRSCCTRL);
}
/* update camif registers, called only when camif ready, or ISR. */
static void __inline__ update_camif_regs(struct tq2440_camif_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);
}
/* start image capture.
*
* param 'stream' means capture pictures streamly or capture only one picture.
*/
static int start_capture(struct tq2440_camif_dev * pdev, int stream)
{
int ret;
u32 ciwdofst;
u32 ciprscctrl;
u32 ciimgcpt;
ciwdofst = ioread32(S3C244X_CIWDOFST);
ciwdofst |= (1<<30) // Clear the overflow indication flag of input CODEC FIFO
Y
|(1<<15)
|(1<<14)
|(1<<13)
// Clear the overflow indication flag of input CODEC FIFO Cb
// Clear the overflow indication flag of input CODEC FIFO Cr
// Clear the overflow indication flag of input PREVIEW FIFO
|(1<<12);
// Clear the overflow indication flag of input PREVIEW FIFO
Cb
Cr
iowrite32(ciwdofst, S3C244X_CIWDOFST);
ciprscctrl = ioread32(S3C244X_CIPRSCCTRL);
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);
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);
break;
// config the regs directly.
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;
detail.
// FIXME, the default input image format, see inputs[] for
/* 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; i<ARRAY_SIZE(regs); i++)
{
if (regs[i].subaddr == 0xff)
{
mdelay(regs[i].value);
continue;
}
sccb_write(OV9650_SCCB_ADDR, regs[i].subaddr, regs[i].value);
}
up(®s_mutex);
}
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;
}
/*
* camif_init()
*/
static int __init camif_init(void)
{
int ret;
struct tq2440_camif_dev * pdev;
struct clk * camif_upll_clk;
printk(KERN_ALERT"initializing s3c2440 camera interface......\n");
pdev = &camera;
/* set gpio-j to camera mode. */
s3c2410_gpio_cfgpin(S3C2440_GPJ0,
s3c2410_gpio_cfgpin(S3C2440_GPJ1,
s3c2410_gpio_cfgpin(S3C2440_GPJ2,
s3c2410_gpio_cfgpin(S3C2440_GPJ3,
s3c2410_gpio_cfgpin(S3C2440_GPJ4,
s3c2410_gpio_cfgpin(S3C2440_GPJ5,
s3c2410_gpio_cfgpin(S3C2440_GPJ6,
S3C2440_GPJ0_CAMDATA0);
S3C2440_GPJ1_CAMDATA1);
S3C2440_GPJ2_CAMDATA2);
S3C2440_GPJ3_CAMDATA3);
S3C2440_GPJ4_CAMDATA4);
S3C2440_GPJ5_CAMDATA5);
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);
/* 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;
}
/* 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);
/* 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);
#ifndef __S3C2440CAMIF_H__
#define __S3C2440CAMIF_H__
#define OV9650_SCCB_ADDR 0x60
#define OV9650_MANUFACT_ID 0x7FA2
#define OV9650_PRODUCT_ID
0x9650
static struct ov9650_reg
{
u8 subaddr;
u8 value;
}regs[] = {
/* OV9650 intialization parameter table for SXGA application */
{0x12, 0x80}, {0x39, 0x43}, {0x38, 0x12}, {0x37, 0x00}, {0x0e, 0x20},
{0x1e, 0x0c}, {0x01, 0x80}, {0x02, 0x80}, {0x00, 0x00}, {0x10, 0xf0},
{0x04, 0x00}, {0x0c, 0x00}, {0x0d, 0x00}, {0x11, 0x80}, {0x12, 0x00},
{0x14, 0x2e}, {0x15, 0x00}, {0x18, 0xbd}, {0x17, 0x1d}, {0x32, 0xbf},
{0x03, 0x12}, {0x1a, 0x81}, {0x19, 0x01}, {0x3f, 0xa6}, {0x41, 0x02},
{0x42, 0x08}, {0x1b, 0x00}, {0x16, 0x06}, {0x33, 0xc0}, {0x34, 0xbf},
{0xa8, 0x80}, {0x96, 0x04}, {0x3a, 0x00}, {0x8e, 0x00}, {0x3c, 0x77},
{0x8b,
{0x29,
{0x5c,
{0x5b,
{0x47,
{0xa5,
{0x50,
{0x55,
{0x3e,
{0x90,
0x06}, {0x35, 0x91}, {0x94, 0x88}, {0x95, 0x88}, {0x40, 0xc1},
0x3f}, {0x0f, 0x42}, {0x13, 0xe5}, {0x3d, 0x92}, {0x69, 0x80},
0x96}, {0x5d, 0x96}, {0x5e, 0x10}, {0x59, 0xeb}, {0x5a, 0x9c},
0x55}, {0x43, 0xf0}, {0x44, 0x10}, {0x45, 0x55}, {0x46, 0x86},
0x64}, {0x48, 0x86}, {0x5f, 0xe0}, {0x60, 0x8c}, {0x61, 0x20},
0xd9}, {0xa4, 0x74}, {0x8d, 0x02}, {0x13, 0xe7}, {0x4f, 0x3a},
0x3d}, {0x51, 0x03}, {0x52, 0x12}, {0x53, 0x26}, {0x54, 0x38},
0x40}, {0x56, 0x40}, {0x57, 0x40}, {0x58, 0x0d}, {0x8c, 0x23},
0x02}, {0xa9, 0xb8}, {0xaa, 0x92}, {0xab, 0x0a}, {0x8f, 0xdf},
0x00}, {0x91, 0x00}, {0x9f, 0x00}, {0x3a, 0x0c}, {0x24, 0x70},
{0x25, 0x64}, {0x26, 0xc3}, {0x2a, 0x12}, {0x2b, 0x46}, {0x3b, 0x19},
{0x6c, 0x40}, {0x6d, 0x30}, {0x6e, 0x4b}, {0x6f, 0x60},
{0x70, 0x70}, {0x71, 0x70}, {0x72, 0x70}, {0x73, 0x70},
{0x74,
{0x78,
{0x7c,
{0x80,
{0x84,
{0x88,
0x60},
0x3a},
0x04},
0x36},
0x6c},
0xbb},
{0x75,
{0x79,
{0x7d,
{0x81,
{0x85,
{0x89,
0x60},
0x2e},
0x07},
0x44},
0x78},
0xd2},
{0x76,
{0x7a,
{0x7e,
{0x82,
{0x86,
{0x8a,
0x50},
0x28},
0x10},
0x52},
0x8c},
0xe6},
{0x77, 0x48},
{0x7b, 0x22},
{0x7f, 0x28},
{0x83, 0x60},
{0x87, 0x9e},
{0x6a, 0x41}, {0x66, 0x00},
{0x3e, 0x00}, {0x3f, 0xa4}
};
DECLARE_MUTEX(regs_mutex);
static DECLARE_MUTEX(bus_lock);
#define SIO_C
S3C2410_GPE14
#define SIO_D
S3C2410_GPE15
#define State(x)
#define High(x)
s3c2410_gpio_getpin(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()
#define WAIT_CYCLE()
do{udelay(10);}while(0)
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)
#define
#define
#define
#define
MIN_C_WIDTH
MIN_C_HEIGHT
MAX_C_WIDTH
MAX_C_HEIGHT
#define MIN_P_WIDTH
#define MIN_P_HEIGHT
#define MAX_P_WIDTH
32
48
1280
1024
32
#define MAX_P_HEIGHT
48
1280
1024
enum
{
CAMIF_BUFF_INVALID = 0,
CAMIF_BUFF_RGB565 = 1,
CAMIF_BUFF_RGB24 = 2,
CAMIF_BUFF_YCbCr420 = 3,
CAMIF_BUFF_YCbCr422 = 4
};
/* image buffer for s3c2440 camif. */
struct tq2440_camif_buffer
{
int state;
ssize_t img_size;
unsigned int order;
unsigned long virt_base;
unsigned long phy_base;
};
/* for tq2440_camif_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
};
/* for tq2440_camif_dev->cmdcode field. */
enum
{
CAMIF_CMD_NONE
= 0,
CAMIF_CMD_SFMT
= 1<<0,
// source image format changed.
CAMIF_CMD_WND = 1<<1,
// window offset changed.
CAMIF_CMD_ZOOM
= 1<<2,
// zoom picture in/out
CAMIF_CMD_TFMT
CAMIF_CMD_P2C
CAMIF_CMD_C2P
= 1<<3,
= 1<<4,
= 1<<5,
CAMIF_CMD_STOP
= 1<<16
// target image format changed.
// need camif switches from p-path to c-path
// neet camif switches from c-path to p-path
// stop capture
};
/* main s3c2440 camif structure. */
struct tq2440_camif_dev
{
/* for sub-devices */
struct list_head devlist;
/* minor device */
struct video_device * vfd;
/* hardware clock. */
struct clk * clk;
/* reference count. */
struct mutex rcmutex;
int rc;
/* the input images's format select. */
int input;
/* source(input) image size. */
int srcHsize; int srcVsize;
/* windowed image size. */
int wndHsize; int wndVsize;
/* codec-path target(output) image size. */
int coTargetHsize;
int coTargetVsize;
/* preview-path target(preview) image size. */
int preTargetHsize;
int preTargetVsize;
/* the camera interface state. */
int state; // CMAIF_STATE_FREE, CAMIF_STATE_PREVIEWING,
CAMIF_STATE_CAPTURING.
/* for executing camif commands. */
int cmdcode;
// command code, CAMIF_CMD_START,
CAMIF_CMD_CFG, etc.
wait_queue_head_t cmdqueue; // wait queue for waiting untile command
completed (if in preview or in capturing).
};
/* opened file handle.*/
struct tq2440_camif_fh
{
/* the camif */
struct tq2440_camif_dev * dev;
/* master flag, only master openner could execute 'set' ioctls. */
int master;
};
#define S3C244X_CAMIFREG(x)
/* CAMIF control registers */
#define S3C244X_CISRCFMT
#define S3C244X_CIWDOFST
#define S3C244X_CIGCTRL
#define S3C244X_CICOYSA1
#define S3C244X_CICOYSA2
#define S3C244X_CICOYSA3
#define S3C244X_CICOYSA4
#define
#define
#define
#define
#define
#define
#define
#define
S3C244X_CICOCBSA1
S3C244X_CICOCBSA2
S3C244X_CICOCBSA3
S3C244X_CICOCBSA4
S3C244X_CICOCRSA1
S3C244X_CICOCRSA2
S3C244X_CICOCRSA3
S3C244X_CICOCRSA4
((x) + camif_base_addr)
S3C244X_CAMIFREG(0x00)
S3C244X_CAMIFREG(0x04)
S3C244X_CAMIFREG(0x08)
S3C244X_CAMIFREG(0x18)
S3C244X_CAMIFREG(0x1C)
S3C244X_CAMIFREG(0x20)
S3C244X_CAMIFREG(0x24)
S3C244X_CAMIFREG(0x28)
S3C244X_CAMIFREG(0x2C)
S3C244X_CAMIFREG(0x30)
S3C244X_CAMIFREG(0x34)
S3C244X_CAMIFREG(0x38)
S3C244X_CAMIFREG(0x3C)
S3C244X_CAMIFREG(0x40)
S3C244X_CAMIFREG(0x44)
#define S3C244X_CICOTRGFMT
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
S3C244X_CAMIFREG(0x48)
S3C244X_CICOCTRL
S3C244X_CAMIFREG(0x4C)
S3C244X_CICOSCPRERATIO
S3C244X_CAMIFREG(0x50)
S3C244X_CICOSCPREDST
S3C244X_CAMIFREG(0x54)
S3C244X_CICOSCCTRL
S3C244X_CAMIFREG(0x58)
S3C244X_CICOTAREA
S3C244X_CAMIFREG(0x5C)
S3C244X_CICOSTATUS
S3C244X_CAMIFREG(0x64)
S3C244X_CIPRCLRSA1
S3C244X_CAMIFREG(0x6C)
S3C244X_CIPRCLRSA2
S3C244X_CAMIFREG(0x70)
S3C244X_CIPRCLRSA3
S3C244X_CAMIFREG(0x74)
S3C244X_CIPRCLRSA4
S3C244X_CAMIFREG(0x78)
#define S3C244X_CIPRTRGFMT
S3C244X_CAMIFREG(0x7C)
#define S3C244X_CIPRCTRL
S3C244X_CAMIFREG(0x80)
#define S3C244X_CIPRSCPRERATIO
S3C244X_CAMIFREG(0x84)
#define
#define
#define
#define
#define
#endif
S3C244X_CIPRSCPREDST
S3C244X_CAMIFREG(0x88)
S3C244X_CIPRSCCTRL
S3C244X_CAMIFREG(0x8C)
S3C244X_CIPRTAREA
S3C244X_CAMIFREG(0x90)
S3C244X_CIPRSTATUS
S3C244X_CAMIFREG(0x98)
S3C244X_CIIMGCPT
S3C244X_CAMIFREG(0xA0)
Download