Linux_S3C2440_CAMIF接口驱动程序(二)OV9650驱动完全解析
2014-04-09 17:31
399 查看
**************************************
OV9650.h
#ifndef __S3C2440CAMIF_H__
#define __S3C2440CAMIF_H__
//这是为了防止c头文件多次包含造成重复定义错误。
#define OV9650_SCCB_ADDR 0x60//定义设备地址
#define OV9650_MANUFACT_ID 0x7FA2
#define OV9650_PRODUCT_ID 0x9650
static struct ov9650_reg//ov9650的寄存器配置表
{
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, 0x06}, {0x35, 0x91}, {0x94, 0x88}, {0x95, 0x88}, {0x40, 0xc1},
{0x29, 0x3f}, {0x0f, 0x42}, {0x13, 0xe5}, {0x3d, 0x92}, {0x69, 0x80},
{0x5c, 0x96}, {0x5d, 0x96}, {0x5e, 0x10}, {0x59, 0xeb}, {0x5a, 0x9c},
{0x5b, 0x55}, {0x43, 0xf0}, {0x44, 0x10}, {0x45, 0x55}, {0x46, 0x86},
{0x47, 0x64}, {0x48, 0x86}, {0x5f, 0xe0}, {0x60, 0x8c}, {0x61, 0x20},
{0xa5, 0xd9}, {0xa4, 0x74}, {0x8d, 0x02}, {0x13, 0xe7}, {0x4f, 0x3a},
{0x50, 0x3d}, {0x51, 0x03}, {0x52, 0x12}, {0x53, 0x26}, {0x54, 0x38},
{0x55, 0x40}, {0x56, 0x40}, {0x57, 0x40}, {0x58, 0x0d}, {0x8c, 0x23},
{0x3e, 0x02}, {0xa9, 0xb8}, {0xaa, 0x92}, {0xab, 0x0a}, {0x8f, 0xdf},
{0x90, 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, 0x60}, {0x75, 0x60}, {0x76, 0x50}, {0x77, 0x48},
{0x78, 0x3a}, {0x79, 0x2e}, {0x7a, 0x28}, {0x7b, 0x22},
{0x7c, 0x04}, {0x7d, 0x07}, {0x7e, 0x10}, {0x7f, 0x28},
{0x80, 0x36}, {0x81, 0x44}, {0x82, 0x52}, {0x83, 0x60},
{0x84, 0x6c}, {0x85, 0x78}, {0x86, 0x8c}, {0x87, 0x9e},
{0x88, 0xbb}, {0x89, 0xd2}, {0x8a, 0xe6},
{0x6a, 0x41}, {0x66, 0x00},
{0x3e, 0x00}, {0x3f, 0xa4}
};
DECLARE_MUTEX(regs_mutex); //声明并初始化互斥信号量为0。(信号量控制多线程编程)
static DECLARE_MUTEX(bus_lock);
#define SIO_C S3C2410_GPE14
#define SIO_D S3C2410_GPE15
/*#define smp_mb barrrier();设置内存屏障,变量使用最原始的值*/
#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)
//CFG_WRITE(x)把GPIO设置为输出
//CFG_READ(x)把GPIO设置为输入
//High(x) 把GPIO设置为高电平
//Low(x) 把GPIO设置为低电平
#define MIN_C_WIDTH 32
#define MIN_C_HEIGHT 48
#define MAX_C_WIDTH 1280
#define MAX_C_HEIGHT 1024
#define MIN_P_WIDTH 32
#define MIN_P_HEIGHT 48
#define MAX_P_WIDTH 1280
#define MAX_P_HEIGHT 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= 1<<3,
// target image format changed.
CAMIF_CMD_P2C
= 1<<4, // need camif switches from p-path to c-path
CAMIF_CMD_C2P
= 1<<5, // neet camif switches from c-path to p-path
CAMIF_CMD_STOP= 1<<16
// stop capture
};
/* main s3c2440 camif structure. */
struct tq2440_camif_dev
{
/* for sub-devices 定义设备链表*/
struct list_head devlist;
/* minor device ,V4L2的核心结构体*/
struct video_device * vfd;
/* hardware clock. 时钟源,用于时钟管理,详细看这篇博客
http://blog.csdn.net/smmei/article/details/8073470*/
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. 接口状态 空闲 预览 拍摄*/
1fe38
int state;
// CMAIF_STATE_FREE, CAMIF_STATE_PREVIEWING, CAMIF_STATE_CAPTURING.
/* for executing camif commands. camera控制指令*/
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) ((x) + camif_base_addr)
//根据datasheet,那个base adress应是0x4F000000.但我对这个地址S3C2440_PA_CAMIF进行了remap,所以要用camif_base_adr.
/* CAMIF control registers */
#define S3C244X_CISRCFMT S3C244X_CAMIFREG(0x00)
#define S3C244X_CIWDOFST S3C244X_CAMIFREG(0x04)
#define S3C244X_CIGCTRL S3C244X_CAMIFREG(0x08)
#define S3C244X_CICOYSA1 S3C244X_CAMIFREG(0x18)
#define S3C244X_CICOYSA2 S3C244X_CAMIFREG(0x1C)
#define S3C244X_CICOYSA3 S3C244X_CAMIFREG(0x20)
#define S3C244X_CICOYSA4 S3C244X_CAMIFREG(0x24)
#define S3C244X_CICOCBSA1 S3C244X_CAMIFREG(0x28)
#define S3C244X_CICOCBSA2 S3C244X_CAMIFREG(0x2C)
#define S3C244X_CICOCBSA3 S3C244X_CAMIFREG(0x30)
#define S3C244X_CICOCBSA4 S3C244X_CAMIFREG(0x34)
#define S3C244X_CICOCRSA1 S3C244X_CAMIFREG(0x38)
#define S3C244X_CICOCRSA2 S3C244X_CAMIFREG(0x3C)
#define S3C244X_CICOCRSA3 S3C244X_CAMIFREG(0x40)
#define S3C244X_CICOCRSA4 S3C244X_CAMIFREG(0x44)
#define S3C244X_CICOTRGFMT S3C244X_CAMIFREG(0x48)
#define S3C244X_CICOCTRL S3C244X_CAMIFREG(0x4C)
#define S3C244X_CICOSCPRERATIO S3C244X_CAMIFREG(0x50)
#define S3C244X_CICOSCPREDST S3C244X_CAMIFREG(0x54)
#define S3C244X_CICOSCCTRL S3C244X_CAMIFREG(0x58)
#define S3C244X_CICOTAREA S3C244X_CAMIFREG(0x5C)
#define S3C244X_CICOSTATUS S3C244X_CAMIFREG(0x64)
#define S3C244X_CIPRCLRSA1 S3C244X_CAMIFREG(0x6C)
#define S3C244X_CIPRCLRSA2 S3C244X_CAMIFREG(0x70)
#define S3C244X_CIPRCLRSA3 S3C244X_CAMIFREG(0x74)
#define S3C244X_CIPRCLRSA4 S3C244X_CAMIFREG(0x78)
#define S3C244X_CIPRTRGFMT S3C244X_CAMIFREG(0x7C)
#define S3C244X_CIPRCTRL S3C244X_CAMIFREG(0x80)
#define S3C244X_CIPRSCPRERATIO S3C244X_CAMIFREG(0x84)
#define S3C244X_CIPRSCPREDST S3C244X_CAMIFREG(0x88)
#define S3C244X_CIPRSCCTRL S3C244X_CAMIFREG(0x8C)
#define S3C244X_CIPRTAREA S3C244X_CAMIFREG(0x90)
#define S3C244X_CIPRSTATUS S3C244X_CAMIFREG(0x98)
#define S3C244X_CIIMGCPT S3C244X_CAMIFREG(0xA0)
#endif
//代码详细解读////////////////////////////////////////////////////////
/*以上看出,头文件的几个主要定义是
#define OV9650_SCCB_ADDR 0x60//定义设备地址
/* image buffer for s3c2440 camif. */
struct tq2440_camif_buffer
/* main s3c2440 camif structure. */
struct tq2440_camif_dev
/* opened file handle.打开文件句柄*/
/*struct tq2440_camif_fh
#define S3C244X_CAMIFREG(x) ((x) + camif_base_addr)*/
//寄存器地址定义
***********************************
OV9650.c
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <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 <asm/io.h>
#include <asm/memory.h>
#include <mach/regs-gpio.h>
#include <mach/regs-gpioj.h>
#include <mach/regs-clock.h>
#include <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.软件复位,对CIGCTRL寄存器配置软件复位*/
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)//从codec转到preview
{
/* 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. 配置CISRCFMT寄存器*/
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
*/
/*
下面这个函数涉及三个寄存器
CIPRCLRSA1234,这四个寄存器放p-path的frame DMA地址,每个存放一个frame,一共四个;
CIPRTRGFMT寄存器,通过配置这个寄存器可实现图片的旋转和镜像。它还存放着target image 的大小;
接下来是CIPRCTRL,这个burst size和remain burst size的计算函数如下
static void __inline__ calc_burst_size()。
!!这里特别提一下,这里的LastIRQEN最好是enable,它能保证输出图片的稳定。
*/
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.
这个寄存器其实就是对sourimage进行裁剪,截出来的就是Target image。所以target image的大小与window
image的大小相同。
*/
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<<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.
不知为什么要等待VSYNC为L?然后对寄存器进行配置。
参考CAMIF这一段:
控制器会在每个VSYNC下降沿判断ImgCptEn信号等命令。如果在下降沿发现ImgCptEn信号有效,则产生IRQ中断。然后才开始一帧
图像的真正采集。而如果在VSYNC下降沿判断到ImgCptEn为低电平且之前LastIRQEn没有使能,则不会产生任何中断,且不会再进行下一
帧的采集。如果你想在ImgCptEn关闭后,一帧采集完后产生一个中断通知你,那么就需要在最后一次中断产生前(stop capturing后的
vysnc下将沿)使能lastirq就可以了。
我在移植linux驱动时就遇到了一个Last IRQ的问题。现象是输出图像上面总是有一条比其它部分反应慢。采集运动图像,就能看出现
象。查看代码是因为没有设立lastirq,因为每次如果不在lastirq产生的情况下读取,图像缓冲中的数据是不稳定的,可能照成图像不完整。
修改代码支持lastirq后,问题解决
*/
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.
开始捕获函数,设置window offset
*/
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)
// 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);
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)// 这里是拍照,steam为0
{
pdev->cmdcode = CAMIF_CMD_STOP;
//如果是捕获图片的话,那么只会中断一次,然后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.由于设备状态是CAMIF_STATE_READY所以,直接更新寄存器。 */
static void update_camif_config (struct tq2440_camif_fh * fh, u32 cmdcode)
{
struct tq2440_camif_dev* pdev;
pdev = fh->dev;
//根据camif的不同模式,执行不同的动作
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.
主要是设置了source,window,target,previe的图片大小。
*/
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的state
{
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). 为DMA分配内存,按页分配*/
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的阶数,内存按页分配,
//把size的转为2的n次幂,order最大值为10或11(对应1024或2048
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);
//函数free_pgaes就ok了.这函数就是整页整页申请内存的函数,kmalloc什么的都太小,不适合用在这里
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);
/*假如是Image capture enable of codec path */
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;//DMA的frame数
if (pdev->cmdcode & CAMIF_CMD_STOP)
{
stop_capture(pdev);//与start_capture对应
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.摄像头接口中断服务P path函数:
*/
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)//查看有没有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;
wake_up(&pdev->cmdqueue);//最后将CMD置为NONE,然后唤醒Read中的queue
return IRQ_RETVAL(IRQ_HANDLED);
}
/*一切开始于camif_open()函数.关系如下
camif_open()——>init_camif_config(fh)——>update_camif_config(fh,CAMIF_CM_STOP)—— >
update_camif_regs(fd-dev)——>{
update_source_fmt_regs(pdev);
update_target_wnd_regs(pdev);
update_target_fmt_regs(pdev);
update_target_zoom_regs(pdev);
}
*/
/*
* camif_open()摄像头打开
*/
static int camif_open(struct inode *inode, struct file *file)
{
struct tq2440_camif_dev *pdev;
struct tq2440_camif_fh *fh;
//首先判断是否存在OV9650 camera是文件开始的一个static变量。
int ret;
if (!has_ov9650) {
return -ENODEV;
}
pdev = &camera;
fh = kzalloc(sizeof(*fh),GFP_KERNEL); // alloc memory for filehandle,为file handle分配内存
if (NULL == fh)
{
return -ENOMEM;
}
fh->dev = pdev;
//设置设备状态为open and standby(ready),初始化CAMIF的配置。
pdev->state = CAMIF_STATE_READY;
init_camif_config(fh);
//对CAMIF进行配置,最后更新一下配置
ret = init_image_buffer();// init image buffer.
if (ret < 0)
{
goto error1;
}
/*
申请中断,分别为Codec的中断和Preview的中断
这两个是中断申请,为这两中断:IRQ_S3C2440_CAM_C,IRQ_S3C2440_CAM_P.对于中断申请函数,我应该关
心的就是中断服务函数了
*/
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;
}
//使能时钟,软件复位,更新CAMIF的配置。
clk_enable(pdev->clk);// and enable camif clock.开启CAMIF时钟
soft_reset_camif();
//这里的soft_reset_camif()跟前面的hw_reset_camif()其实是一样的:
file->private_data = fh;
fh->dev = pdev;
update_camif_config(fh, 0);//更新CAMIF配置
return 0;
error3:
free_irq(IRQ_S3C2440_CAM_C, pdev);
error2:
free_image_buffer();
error1:
kfree(fh);
return ret;
}
/*
ov9650驱动的两个函数,camif_init和camif_open。这两个函数主要都是做一些初始化和内存分
配的工作:camera接口的I/O内存申请,camera寄存器的配置,摄像的寄存器的初始化,图像缓存通道的DMA内存的申请.接下我么来看看如何开启摄
像头,进行拍摄预览。camif_read()
*/
/*
* 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;
}
//中断已经发生,数据已经更新。把两个通道的irq解除。
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);// 把数据copy到用户层
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)//CLK高电平时,DATA拉低,即为START,开始工作,具体可以参见《大海边.pdf》
{
CFG_WRITE(SIO_D);
Low(SIO_D);
WAIT_STABLE();
}
//SCCB读一个字节
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();
}
}
//SCCB写一个字节
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);
}
//sccb写,需要三个参数,主线地址idaddr,寄存器地址subaddr,数据data,是单开始信号,
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);
}
//sccb读,主线地址idaddr,寄存器地址subaddr,双开始信号
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;
}
/*在这里S3C2410_GPG11就是ENIT19了,目的就是用通断cmos摄像头的电。至于是高还是低电平,那要看电路设计。
OV9650上电,这里对GPG11设置为输出,这里使用的虽然是中断引脚,但似乎没有用中断,这是一个电源控制引脚,PWDN,0:power
on,1:power down
*/
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);
}
//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("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)//ov9650的初始化
{
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;
}
/*
这驱动注册框架是misc字符设备,所以,有__init __exit,struct file_opreations 结构体,我们先从最简单的
__init和__exit开始好了,分别是static int __init camif_init(void) 和static int __exit camif_cleanup
*/
/*
* 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");
//这里camera在前面有定义
// camera device(s) static struct tq2440_camif_dev camera;
pdev = &camera;
/* set gpio-j to camera mode. 配置CAMIF的GPIO口的功能,将GPIO-J设为camif模式,摄像会连接在这个接口上*/
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);
/* init camera's virtual memory. 初始化摄像头的虚拟内存,申请CAMIF的寄存器区域,由于OS的系统统一管理,所以,需要确定,该寄存器
区域没有被别的进程获取,所以要申请。
*/
/*
在arch/arm/mach-s3c2410/include/mach/map.h
#define S3C2440_PA_CAMIF (0x4F000000)
#define CARD_NAME "camera"
*/
if (!request_mem_region((unsigned long)S3C2440_PA_CAMIF, S3C2440_SZ_CAMIF, CARD_NAME))
{
ret = -EBUSY;
goto error1;
}
/* remap the virtual memory. 申请到了寄存器区域,相当于物理地址可用,然后,再将该物理地址映射到虚拟
地址。
这里用到linux的内存管理方面的知识,建议找本32位的linux内核书看看。
这里进行的目的是为了访问CAMIF接口内存,通过申请内存,其实设备就不会访问这块内存了哦!
访问I/O内存的流程是:request_mem_region() -> ioremap() -> ioread8()/iowrite8() -> iounmap() ->release_mem_region() 。
先看申请内存的函数定义:
#define request_mem_region(start,n,name) __request_region(&iomem_resource, (start), (n), (name))
start:设备起始物理地址
n:请求的内存空间
name:名字
接着看内存映射函数:
#define ioremap_nocache(cookie,size) __arm_ioremap(cookie, size, MT_DEVICE)
#define ioremap_cached(cookie,size) __arm_ioremap(cookie, size, MT_DEVICE_CACHED)
extern void __iomem * __arm_ioremap(unsigned long, size_t, unsigned int);
extern void __iounmap(volatile void __iomem *addr);
void __iomem * ioremap_nocache (unsigned longphys_addr, unsigned longsize);
phys_addr
要映射的物理地址
size
要映射资源的大小
调用ioremap_nocache()函数之后,返回一个线性地址,此时CPU 可以访问设备的内存(已经将其映射到了线性地
址空间中了),此时CPU可以使用访问内存的指令访问设备的内存空间(host bridge 判断访问物理内存还是设备中
的内存),此时我们就可以像访问内存一样来访问设备的内存(寄存器)。
camif_base_addr 这个地址我们后还会见到喔,到时候看看他是怎么被使用的吧!
*/
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. 获取CAMIF的时钟,使用24M*/
/*接下来看下linux时钟管理相关的资料
在此之前我们应该了解一下struct clk:
struct clk {
struct list_head list; //时钟链表节点
struct module *owner;
struct clk *parent; //父对象,就是FCLK,HCLK,PCLK
const char *name;
int id; //单设备为-1,如UART,则为0,1,2
atomic_t used;
unsigned long rate;
unsigned long ctrlbit;// S3C2410_CLKCON指定位
int (*enable)(struct clk *, int enable) // 时钟的唯一方法,使能函数
};
获取时钟:
struct clk *clkp;
clkp = clk_get(dev_id,clk_name);
if(!clkp)
pr_err("clk_get failed\n");
其对应的接口函数:
释放时钟:
clk_put(clkp);
使能时钟:
clk_enable(clkp);
关闭时钟:
clk_disable(clkp);
获得时钟频率:
u32 clk_rate;
clk_rate = clk_get_rate(clkp);
改变时钟频率:
rounded_rate = clk_round_rate(clkp, target_rate);
/*Now call set_rate, remember set_rate takes clock frequency in '''Hz''' only;*/
/*ret = clk_set_rate(clkp, rounded_rate);
首先调用clk_get(...)通过比较名称”camif”找到时钟结构:
然后调用clk_enable(pdev->clk);即调用:
下面的几句设置camif-pull时钟,这里频率24000000。
camif_upll_clk = clk_get(NULL, "camif-upll");
clk_set_rate(camif_upll_clk, 24000000);
mdelay(100);
通过写时钟控制寄存器将camif时钟使能.同样也可以通过这个函数将时钟关闭.
同样在电源管理的suspend函数中将时钟关闭,在resume中将时钟打开.
*/
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;//enum定义在ov9650.h中
/* 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();//初始化SCCB总线,直接实现
CFG_WRITE(SIO_C);
CFG_WRITE(SIO_D);
High(SIO_C);
High(SIO_D);
WAIT_STABLE();
hw_reset_camif();//see line93,硬件复位CAMIF,其实是软件复位,对CIGCTRL寄存器配置软件复位
has_ov9650 = s3c2440_ov9650_init() >= 0;//该函数若初始化成功则返回的是0,否则return -ENODEV
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)//跟__init配套的
{
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);
OV9650.h
#ifndef __S3C2440CAMIF_H__
#define __S3C2440CAMIF_H__
//这是为了防止c头文件多次包含造成重复定义错误。
#define OV9650_SCCB_ADDR 0x60//定义设备地址
#define OV9650_MANUFACT_ID 0x7FA2
#define OV9650_PRODUCT_ID 0x9650
static struct ov9650_reg//ov9650的寄存器配置表
{
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, 0x06}, {0x35, 0x91}, {0x94, 0x88}, {0x95, 0x88}, {0x40, 0xc1},
{0x29, 0x3f}, {0x0f, 0x42}, {0x13, 0xe5}, {0x3d, 0x92}, {0x69, 0x80},
{0x5c, 0x96}, {0x5d, 0x96}, {0x5e, 0x10}, {0x59, 0xeb}, {0x5a, 0x9c},
{0x5b, 0x55}, {0x43, 0xf0}, {0x44, 0x10}, {0x45, 0x55}, {0x46, 0x86},
{0x47, 0x64}, {0x48, 0x86}, {0x5f, 0xe0}, {0x60, 0x8c}, {0x61, 0x20},
{0xa5, 0xd9}, {0xa4, 0x74}, {0x8d, 0x02}, {0x13, 0xe7}, {0x4f, 0x3a},
{0x50, 0x3d}, {0x51, 0x03}, {0x52, 0x12}, {0x53, 0x26}, {0x54, 0x38},
{0x55, 0x40}, {0x56, 0x40}, {0x57, 0x40}, {0x58, 0x0d}, {0x8c, 0x23},
{0x3e, 0x02}, {0xa9, 0xb8}, {0xaa, 0x92}, {0xab, 0x0a}, {0x8f, 0xdf},
{0x90, 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, 0x60}, {0x75, 0x60}, {0x76, 0x50}, {0x77, 0x48},
{0x78, 0x3a}, {0x79, 0x2e}, {0x7a, 0x28}, {0x7b, 0x22},
{0x7c, 0x04}, {0x7d, 0x07}, {0x7e, 0x10}, {0x7f, 0x28},
{0x80, 0x36}, {0x81, 0x44}, {0x82, 0x52}, {0x83, 0x60},
{0x84, 0x6c}, {0x85, 0x78}, {0x86, 0x8c}, {0x87, 0x9e},
{0x88, 0xbb}, {0x89, 0xd2}, {0x8a, 0xe6},
{0x6a, 0x41}, {0x66, 0x00},
{0x3e, 0x00}, {0x3f, 0xa4}
};
DECLARE_MUTEX(regs_mutex); //声明并初始化互斥信号量为0。(信号量控制多线程编程)
static DECLARE_MUTEX(bus_lock);
#define SIO_C S3C2410_GPE14
#define SIO_D S3C2410_GPE15
/*#define smp_mb barrrier();设置内存屏障,变量使用最原始的值*/
#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)
//CFG_WRITE(x)把GPIO设置为输出
//CFG_READ(x)把GPIO设置为输入
//High(x) 把GPIO设置为高电平
//Low(x) 把GPIO设置为低电平
#define MIN_C_WIDTH 32
#define MIN_C_HEIGHT 48
#define MAX_C_WIDTH 1280
#define MAX_C_HEIGHT 1024
#define MIN_P_WIDTH 32
#define MIN_P_HEIGHT 48
#define MAX_P_WIDTH 1280
#define MAX_P_HEIGHT 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= 1<<3,
// target image format changed.
CAMIF_CMD_P2C
= 1<<4, // need camif switches from p-path to c-path
CAMIF_CMD_C2P
= 1<<5, // neet camif switches from c-path to p-path
CAMIF_CMD_STOP= 1<<16
// stop capture
};
/* main s3c2440 camif structure. */
struct tq2440_camif_dev
{
/* for sub-devices 定义设备链表*/
struct list_head devlist;
/* minor device ,V4L2的核心结构体*/
struct video_device * vfd;
/* hardware clock. 时钟源,用于时钟管理,详细看这篇博客
http://blog.csdn.net/smmei/article/details/8073470*/
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. 接口状态 空闲 预览 拍摄*/
1fe38
int state;
// CMAIF_STATE_FREE, CAMIF_STATE_PREVIEWING, CAMIF_STATE_CAPTURING.
/* for executing camif commands. camera控制指令*/
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) ((x) + camif_base_addr)
//根据datasheet,那个base adress应是0x4F000000.但我对这个地址S3C2440_PA_CAMIF进行了remap,所以要用camif_base_adr.
/* CAMIF control registers */
#define S3C244X_CISRCFMT S3C244X_CAMIFREG(0x00)
#define S3C244X_CIWDOFST S3C244X_CAMIFREG(0x04)
#define S3C244X_CIGCTRL S3C244X_CAMIFREG(0x08)
#define S3C244X_CICOYSA1 S3C244X_CAMIFREG(0x18)
#define S3C244X_CICOYSA2 S3C244X_CAMIFREG(0x1C)
#define S3C244X_CICOYSA3 S3C244X_CAMIFREG(0x20)
#define S3C244X_CICOYSA4 S3C244X_CAMIFREG(0x24)
#define S3C244X_CICOCBSA1 S3C244X_CAMIFREG(0x28)
#define S3C244X_CICOCBSA2 S3C244X_CAMIFREG(0x2C)
#define S3C244X_CICOCBSA3 S3C244X_CAMIFREG(0x30)
#define S3C244X_CICOCBSA4 S3C244X_CAMIFREG(0x34)
#define S3C244X_CICOCRSA1 S3C244X_CAMIFREG(0x38)
#define S3C244X_CICOCRSA2 S3C244X_CAMIFREG(0x3C)
#define S3C244X_CICOCRSA3 S3C244X_CAMIFREG(0x40)
#define S3C244X_CICOCRSA4 S3C244X_CAMIFREG(0x44)
#define S3C244X_CICOTRGFMT S3C244X_CAMIFREG(0x48)
#define S3C244X_CICOCTRL S3C244X_CAMIFREG(0x4C)
#define S3C244X_CICOSCPRERATIO S3C244X_CAMIFREG(0x50)
#define S3C244X_CICOSCPREDST S3C244X_CAMIFREG(0x54)
#define S3C244X_CICOSCCTRL S3C244X_CAMIFREG(0x58)
#define S3C244X_CICOTAREA S3C244X_CAMIFREG(0x5C)
#define S3C244X_CICOSTATUS S3C244X_CAMIFREG(0x64)
#define S3C244X_CIPRCLRSA1 S3C244X_CAMIFREG(0x6C)
#define S3C244X_CIPRCLRSA2 S3C244X_CAMIFREG(0x70)
#define S3C244X_CIPRCLRSA3 S3C244X_CAMIFREG(0x74)
#define S3C244X_CIPRCLRSA4 S3C244X_CAMIFREG(0x78)
#define S3C244X_CIPRTRGFMT S3C244X_CAMIFREG(0x7C)
#define S3C244X_CIPRCTRL S3C244X_CAMIFREG(0x80)
#define S3C244X_CIPRSCPRERATIO S3C244X_CAMIFREG(0x84)
#define S3C244X_CIPRSCPREDST S3C244X_CAMIFREG(0x88)
#define S3C244X_CIPRSCCTRL S3C244X_CAMIFREG(0x8C)
#define S3C244X_CIPRTAREA S3C244X_CAMIFREG(0x90)
#define S3C244X_CIPRSTATUS S3C244X_CAMIFREG(0x98)
#define S3C244X_CIIMGCPT S3C244X_CAMIFREG(0xA0)
#endif
//代码详细解读////////////////////////////////////////////////////////
/*以上看出,头文件的几个主要定义是
#define OV9650_SCCB_ADDR 0x60//定义设备地址
/* image buffer for s3c2440 camif. */
struct tq2440_camif_buffer
/* main s3c2440 camif structure. */
struct tq2440_camif_dev
/* opened file handle.打开文件句柄*/
/*struct tq2440_camif_fh
#define S3C244X_CAMIFREG(x) ((x) + camif_base_addr)*/
//寄存器地址定义
***********************************
OV9650.c
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <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 <asm/io.h>
#include <asm/memory.h>
#include <mach/regs-gpio.h>
#include <mach/regs-gpioj.h>
#include <mach/regs-clock.h>
#include <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.软件复位,对CIGCTRL寄存器配置软件复位*/
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)//从codec转到preview
{
/* 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. 配置CISRCFMT寄存器*/
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
*/
/*
下面这个函数涉及三个寄存器
CIPRCLRSA1234,这四个寄存器放p-path的frame DMA地址,每个存放一个frame,一共四个;
CIPRTRGFMT寄存器,通过配置这个寄存器可实现图片的旋转和镜像。它还存放着target image 的大小;
接下来是CIPRCTRL,这个burst size和remain burst size的计算函数如下
static void __inline__ calc_burst_size()。
!!这里特别提一下,这里的LastIRQEN最好是enable,它能保证输出图片的稳定。
*/
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.
这个寄存器其实就是对sourimage进行裁剪,截出来的就是Target image。所以target image的大小与window
image的大小相同。
*/
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<<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.
不知为什么要等待VSYNC为L?然后对寄存器进行配置。
参考CAMIF这一段:
控制器会在每个VSYNC下降沿判断ImgCptEn信号等命令。如果在下降沿发现ImgCptEn信号有效,则产生IRQ中断。然后才开始一帧
图像的真正采集。而如果在VSYNC下降沿判断到ImgCptEn为低电平且之前LastIRQEn没有使能,则不会产生任何中断,且不会再进行下一
帧的采集。如果你想在ImgCptEn关闭后,一帧采集完后产生一个中断通知你,那么就需要在最后一次中断产生前(stop capturing后的
vysnc下将沿)使能lastirq就可以了。
我在移植linux驱动时就遇到了一个Last IRQ的问题。现象是输出图像上面总是有一条比其它部分反应慢。采集运动图像,就能看出现
象。查看代码是因为没有设立lastirq,因为每次如果不在lastirq产生的情况下读取,图像缓冲中的数据是不稳定的,可能照成图像不完整。
修改代码支持lastirq后,问题解决
*/
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.
开始捕获函数,设置window offset
*/
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)
// 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);
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)// 这里是拍照,steam为0
{
pdev->cmdcode = CAMIF_CMD_STOP;
//如果是捕获图片的话,那么只会中断一次,然后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.由于设备状态是CAMIF_STATE_READY所以,直接更新寄存器。 */
static void update_camif_config (struct tq2440_camif_fh * fh, u32 cmdcode)
{
struct tq2440_camif_dev* pdev;
pdev = fh->dev;
//根据camif的不同模式,执行不同的动作
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.
主要是设置了source,window,target,previe的图片大小。
*/
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的state
{
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). 为DMA分配内存,按页分配*/
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的阶数,内存按页分配,
//把size的转为2的n次幂,order最大值为10或11(对应1024或2048
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);
//函数free_pgaes就ok了.这函数就是整页整页申请内存的函数,kmalloc什么的都太小,不适合用在这里
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);
/*假如是Image capture enable of codec path */
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;//DMA的frame数
if (pdev->cmdcode & CAMIF_CMD_STOP)
{
stop_capture(pdev);//与start_capture对应
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.摄像头接口中断服务P path函数:
*/
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)//查看有没有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;
wake_up(&pdev->cmdqueue);//最后将CMD置为NONE,然后唤醒Read中的queue
return IRQ_RETVAL(IRQ_HANDLED);
}
/*一切开始于camif_open()函数.关系如下
camif_open()——>init_camif_config(fh)——>update_camif_config(fh,CAMIF_CM_STOP)—— >
update_camif_regs(fd-dev)——>{
update_source_fmt_regs(pdev);
update_target_wnd_regs(pdev);
update_target_fmt_regs(pdev);
update_target_zoom_regs(pdev);
}
*/
/*
* camif_open()摄像头打开
*/
static int camif_open(struct inode *inode, struct file *file)
{
struct tq2440_camif_dev *pdev;
struct tq2440_camif_fh *fh;
//首先判断是否存在OV9650 camera是文件开始的一个static变量。
int ret;
if (!has_ov9650) {
return -ENODEV;
}
pdev = &camera;
fh = kzalloc(sizeof(*fh),GFP_KERNEL); // alloc memory for filehandle,为file handle分配内存
if (NULL == fh)
{
return -ENOMEM;
}
fh->dev = pdev;
//设置设备状态为open and standby(ready),初始化CAMIF的配置。
pdev->state = CAMIF_STATE_READY;
init_camif_config(fh);
//对CAMIF进行配置,最后更新一下配置
ret = init_image_buffer();// init image buffer.
if (ret < 0)
{
goto error1;
}
/*
申请中断,分别为Codec的中断和Preview的中断
这两个是中断申请,为这两中断:IRQ_S3C2440_CAM_C,IRQ_S3C2440_CAM_P.对于中断申请函数,我应该关
心的就是中断服务函数了
*/
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;
}
//使能时钟,软件复位,更新CAMIF的配置。
clk_enable(pdev->clk);// and enable camif clock.开启CAMIF时钟
soft_reset_camif();
//这里的soft_reset_camif()跟前面的hw_reset_camif()其实是一样的:
file->private_data = fh;
fh->dev = pdev;
update_camif_config(fh, 0);//更新CAMIF配置
return 0;
error3:
free_irq(IRQ_S3C2440_CAM_C, pdev);
error2:
free_image_buffer();
error1:
kfree(fh);
return ret;
}
/*
ov9650驱动的两个函数,camif_init和camif_open。这两个函数主要都是做一些初始化和内存分
配的工作:camera接口的I/O内存申请,camera寄存器的配置,摄像的寄存器的初始化,图像缓存通道的DMA内存的申请.接下我么来看看如何开启摄
像头,进行拍摄预览。camif_read()
*/
/*
* 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;
}
//中断已经发生,数据已经更新。把两个通道的irq解除。
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);// 把数据copy到用户层
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)//CLK高电平时,DATA拉低,即为START,开始工作,具体可以参见《大海边.pdf》
{
CFG_WRITE(SIO_D);
Low(SIO_D);
WAIT_STABLE();
}
//SCCB读一个字节
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();
}
}
//SCCB写一个字节
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);
}
//sccb写,需要三个参数,主线地址idaddr,寄存器地址subaddr,数据data,是单开始信号,
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);
}
//sccb读,主线地址idaddr,寄存器地址subaddr,双开始信号
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;
}
/*在这里S3C2410_GPG11就是ENIT19了,目的就是用通断cmos摄像头的电。至于是高还是低电平,那要看电路设计。
OV9650上电,这里对GPG11设置为输出,这里使用的虽然是中断引脚,但似乎没有用中断,这是一个电源控制引脚,PWDN,0:power
on,1:power down
*/
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);
}
//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("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)//ov9650的初始化
{
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;
}
/*
这驱动注册框架是misc字符设备,所以,有__init __exit,struct file_opreations 结构体,我们先从最简单的
__init和__exit开始好了,分别是static int __init camif_init(void) 和static int __exit camif_cleanup
*/
/*
* 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");
//这里camera在前面有定义
// camera device(s) static struct tq2440_camif_dev camera;
pdev = &camera;
/* set gpio-j to camera mode. 配置CAMIF的GPIO口的功能,将GPIO-J设为camif模式,摄像会连接在这个接口上*/
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);
/* init camera's virtual memory. 初始化摄像头的虚拟内存,申请CAMIF的寄存器区域,由于OS的系统统一管理,所以,需要确定,该寄存器
区域没有被别的进程获取,所以要申请。
*/
/*
在arch/arm/mach-s3c2410/include/mach/map.h
#define S3C2440_PA_CAMIF (0x4F000000)
#define CARD_NAME "camera"
*/
if (!request_mem_region((unsigned long)S3C2440_PA_CAMIF, S3C2440_SZ_CAMIF, CARD_NAME))
{
ret = -EBUSY;
goto error1;
}
/* remap the virtual memory. 申请到了寄存器区域,相当于物理地址可用,然后,再将该物理地址映射到虚拟
地址。
这里用到linux的内存管理方面的知识,建议找本32位的linux内核书看看。
这里进行的目的是为了访问CAMIF接口内存,通过申请内存,其实设备就不会访问这块内存了哦!
访问I/O内存的流程是:request_mem_region() -> ioremap() -> ioread8()/iowrite8() -> iounmap() ->release_mem_region() 。
先看申请内存的函数定义:
#define request_mem_region(start,n,name) __request_region(&iomem_resource, (start), (n), (name))
start:设备起始物理地址
n:请求的内存空间
name:名字
接着看内存映射函数:
#define ioremap_nocache(cookie,size) __arm_ioremap(cookie, size, MT_DEVICE)
#define ioremap_cached(cookie,size) __arm_ioremap(cookie, size, MT_DEVICE_CACHED)
extern void __iomem * __arm_ioremap(unsigned long, size_t, unsigned int);
extern void __iounmap(volatile void __iomem *addr);
void __iomem * ioremap_nocache (unsigned longphys_addr, unsigned longsize);
phys_addr
要映射的物理地址
size
要映射资源的大小
调用ioremap_nocache()函数之后,返回一个线性地址,此时CPU 可以访问设备的内存(已经将其映射到了线性地
址空间中了),此时CPU可以使用访问内存的指令访问设备的内存空间(host bridge 判断访问物理内存还是设备中
的内存),此时我们就可以像访问内存一样来访问设备的内存(寄存器)。
camif_base_addr 这个地址我们后还会见到喔,到时候看看他是怎么被使用的吧!
*/
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. 获取CAMIF的时钟,使用24M*/
/*接下来看下linux时钟管理相关的资料
在此之前我们应该了解一下struct clk:
struct clk {
struct list_head list; //时钟链表节点
struct module *owner;
struct clk *parent; //父对象,就是FCLK,HCLK,PCLK
const char *name;
int id; //单设备为-1,如UART,则为0,1,2
atomic_t used;
unsigned long rate;
unsigned long ctrlbit;// S3C2410_CLKCON指定位
int (*enable)(struct clk *, int enable) // 时钟的唯一方法,使能函数
};
获取时钟:
struct clk *clkp;
clkp = clk_get(dev_id,clk_name);
if(!clkp)
pr_err("clk_get failed\n");
其对应的接口函数:
释放时钟:
clk_put(clkp);
使能时钟:
clk_enable(clkp);
关闭时钟:
clk_disable(clkp);
获得时钟频率:
u32 clk_rate;
clk_rate = clk_get_rate(clkp);
改变时钟频率:
rounded_rate = clk_round_rate(clkp, target_rate);
/*Now call set_rate, remember set_rate takes clock frequency in '''Hz''' only;*/
/*ret = clk_set_rate(clkp, rounded_rate);
首先调用clk_get(...)通过比较名称”camif”找到时钟结构:
然后调用clk_enable(pdev->clk);即调用:
下面的几句设置camif-pull时钟,这里频率24000000。
camif_upll_clk = clk_get(NULL, "camif-upll");
clk_set_rate(camif_upll_clk, 24000000);
mdelay(100);
通过写时钟控制寄存器将camif时钟使能.同样也可以通过这个函数将时钟关闭.
同样在电源管理的suspend函数中将时钟关闭,在resume中将时钟打开.
*/
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;//enum定义在ov9650.h中
/* 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();//初始化SCCB总线,直接实现
CFG_WRITE(SIO_C);
CFG_WRITE(SIO_D);
High(SIO_C);
High(SIO_D);
WAIT_STABLE();
hw_reset_camif();//see line93,硬件复位CAMIF,其实是软件复位,对CIGCTRL寄存器配置软件复位
has_ov9650 = s3c2440_ov9650_init() >= 0;//该函数若初始化成功则返回的是0,否则return -ENODEV
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)//跟__init配套的
{
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);
相关文章推荐
- Linux_S3C2440_CAMIF接口驱动程序(一)驱动和系统相关的学习
- S5pv210 HDMI 接口在 Linux 3.0.8 驱动框架解析 (By liukun321 咕唧咕唧)
- 基于S3C2440的嵌入式Linux驱动——AT24C02(EEPROM I2C接口)驱动解读
- S5pv210 HDMI 接口在 Linux 3.0.8 驱动框架解析 (By liukun321 咕唧咕唧)
- 基于V4L2的视频驱动开发(以s3c2440和ov9650举例)
- (转)S5pv210 HDMI 接口在 Linux 3.0.8 驱动框架解析 (By liukun321 咕唧咕唧)
- S5pv210下 HDMI 接口在 Linux 3.0.8 内核下的驱动解析
- S5pv210 HDMI 接口在 Linux 3.0.8 驱动框架解析
- S5pv210 HDMI 接口在 Linux 3.0.8 驱动框架解析 (By liukun321 咕唧咕唧)
- 基于S3C2440的嵌入式Linux驱动——AT24C02(EEPROM I2C接口)驱动解读
- S5pv210 HDMI 接口在 Linux 3.0.8 驱动框架解析
- S5pv210 HDMI 接口在 Linux 3.0.8 驱动框架解析
- s3c2440 camif接口摄像头驱动分析——基于tq2440的ov9650.c
- 基于S3C2440的嵌入式Linux驱动——AT24C02(EEPROM I2C接口)驱动解读
- S5pv210 HDMI 接口在 Linux 3.0.8 驱动框架解析 (By liukun321 咕唧咕唧)
- Linux驱动之I2C设备驱动完全解析
- linux设备驱动中断程序深度完全解析
- 嵌入式Linux之我行——S3C2440上MMC/SD卡驱动实例开发讲解(二)
- 【Linux 驱动】设备驱动程序再理解
- 基于事件驱动的解析接口 SAX (实例二)