s3c2440 camif接口摄像头驱动分析——基于tq2440的ov9650.c

news/2024/10/18 16:46:16/

前言

            最近想做摄像头驱动,看了一些文章,对摄像头驱动的结构还是很晕。于是决定分析内核自带的驱动程序。内核的cmos摄像头

有用v4l2的,也有用arm的camif codes通道结构的。本文是针对s3c2440 camif接口而写的驱动的代码导读。写得不好请多多指教。

一、开发环境

1.开发板:tq2440开发板

2.arm cpu:s3c2440

3.摄像头:ov9650

4.linux 内核:linux 2.6.30

5.源码:driver/meida/video/ov9650.c


二、需要了解的概念和准备的东西

1.SCCB协议  点击打开链接

2.ov9650 datasheet 点击打开链接

3.s3c2440 datasheet

4.linux 内存管理、映射(推荐《LDD3》,《linux内核情景分析jkk》)

5.linux misc字符设备

6.自旋锁、原子量、中断等

7.linux 的时钟管理,struct clk。


三、SCCB协议的实现

       sccb是一种与i2c类似的协议,在写的时候跟i2c协议完全相同。但在读的时候与i2c有一点差别。

这里的ov9650驱动没有使用linux的i2c系统,而是用I/O模拟重新写过。

1、需要知道的宏定义

/*ov9650.h*/
#define OV9650_SCCB_ADDR   0x66
#define OV9650_MANUFACT_ID	0x7FA2
#define OV9650_PRODUCT_ID	0x9650
#define SIO_C		S3C2410_GPE14   //i2cscl
#define SIO_D		S3C2410_GPE15   //i2csda#define State(x)		s3c2410_gpio_getpin(x)/*#define smp_mb barrrier();设置内存屏障,变量使用最原始的值*/
#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)

2、start、write|read、stop

        回到ov9650.c中,先看到第一个与sccb有关函数 sccb_start(void):


static void __inline__ sccb_start(void)
{CFG_WRITE(SIO_D); //设置i2csda为输出Low(SIO_D);      //将i2csda设置为0WAIT_STABLE();   //延时10us
}

       接下是 sccb写一个字节,sccb_write_byte(u8 data):

static void __inline__ sccb_write_byte(u8 data)
{int i;CFG_WRITE(SIO_D);  //设置i2csda为输出WAIT_STABLE();     //延时10us/* write 8-bits octet. */for (i=0;i<8;i++){Low(SIO_C);      //将i2cscl拉低WAIT_STABLE();if (data & 0x80)  //从高位开始写{High(SIO_D);}else{Low(SIO_D);}data = data<<1;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;
}
接着是stop:

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;
}

四、__init 和 __exit

        这驱动注册框架是misc字符设备,所以,有__init  __exit,struct file_opreations 结构体,我们先从最简单的

__init和__exit开始好了,分别是static int __init camif_init(void) 和static int __exit camif_cleanup

1.static int __init camif_init(void)

/** camif_init()*/
static int __init camif_init(void)
{int ret;struct tq2440_camif_dev * pdev; //声明一个tq2440_camif_dev结构体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. *//*把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. *//*初始化摄像头的虚拟内存*//*在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. *//*重新映射虚拟地址*/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. *//*初始化camera的时钟*/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;
}

看起来好像不是那么简单,我们慢慢来吧。一开始就看到个不常见的结构体struct tq2440_camif_dev 我们跟进去看吧:

/* main s3c2440 camif structure. */
struct tq2440_camif_dev
{/* for sub-devices *//*定义了一个设备链表*/struct list_head devlist;/* minor device *//*video_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. *//*s3c2440 camera接口的状态:空闲,预览,拍摄*/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).
};

接着是,gpio-j的配置,没什么可讲,看datasheet吧,

/* set gpio-j to camera mode. *//*把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. *//*初始化摄像头的虚拟内存*//*在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. *//*重新映射虚拟地址*/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;}

这里用到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 这个地址我们后还会见到喔,到时候看看他是怎么被使用的吧!

接下来这段:

/* init camera clock. *//*初始化camera的时钟*/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);
这段跟linux 的时钟clk管理有关,先看看s3c2440 linux的clk吧!


 显然这一段跟设置camrea接口的时钟有关。在此之前我们应该了解一下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中将时钟打开.


接下来的没什么可讲,我来看最后几句函数吧:

	hw_reset_camif();     //对hw_reset_camif()对S3C244X_CIGCTRL进行初始化has_ov9650 = s3c2440_ov9650_init() >= 0;  //ov9650的初始化s3c2410_gpio_setpin(S3C2410_GPG4, 1);      return 0;
这里的几个函数定义如下:

/* 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);
}/*接下来是s3c2440_ov9650_init(),*/int s3c2440_ov9650_init(void)
{printk(KERN_ALERT"Loading OV9650 driver.........\n");/* power on. */ov9650_poweron();   //给电源管脚上电mdelay(100);/* check device. *//*检测设备,通过读取ov9650的adreeID*/if (ov9650_check() == 0 && ov9650_check() == 0){printk(KERN_ERR"No OV9650 found!!!\n");return -ENODEV;}show_ov9650_product_id(); //打印设备idov9650_init_regs();       //初始化ov9650寄存器printk("OV9650 init done!\n");return 0;
}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);
}
上面的东西,自己对这s3c2440 datasheet慢慢看吧。

对了,这里提到了S3C244X_CIGCTRL这个寄存器,那就随便讲讲前面留下的那个camif_base_addr

S3C244X_CIGCTRL这个寄存器的定义在ov9650.h,我们一起去看看:

#define S3C244X_CAMIFREG(x) ((x) + camif_base_addr)
//经过remap后,camif_base_addr有个值
/* 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) 


 #define S3C2440_PA_CAMIF   (0x4F000000)

根据datasheet,那个base adress应是0x4F000000.但我对这个地址S3C2440_PA_CAMIF进行了remap,所以要用camif_base_adr.

2.__exit

这个没什么好讲,都是跟__init配套的。

/** 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");
}



五、熟悉的struct file_operations

下面开始分析关键部分,从struct file_operations cammif_fops开始。写过字符设备的少年应该都懂的。

1.open = camif_open


/** camif_open()*/
static int camif_open(struct inode *inode, struct file *file)
{struct tq2440_camif_dev *pdev; //定义一个tq2440_camif_dev结构体struct tq2440_camif_fh *fh;    //定义一个tq2440_camif_fh结构体int ret;if (!has_ov9650) {             //在__init处赋的值判断是有ov9650加载了return -ENODEV;}pdev = &camera;fh = kzalloc(sizeof(*fh),GFP_KERNEL); // alloc memory for filehandleif (NULL == fh){return -ENOMEM;}fh->dev = pdev;pdev->state = CAMIF_STATE_READY;  //设置pdev的状态为readyinit_camif_config(fh);     //初始化camifret = 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 ISRsif (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;
}

一开始就看到一个结构体,struct tq2440_camif_fh,遇到不懂的结构体就要查到底,定义如下:

/* 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;    //这个标志不太懂其目的。
};

往下看,有个函数

init_camif_config(fh);     //初始化camif
我们看看这个函数做了什么:

/* config camif when master-open camera.*/
static void init_camif_config(struct tq2440_camif_fh * fh)
{struct tq2440_camif_dev	* pdev;pdev = fh->dev;/*选择输入图片的格式,这是0*/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);  //更新设置
}

这里主要是设置了source,window,target,previe的图片大小。

最后接下调用了这函数 update_camif_config(),这个函数定义如下:


/* 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;/*根据camif的不同模式,执行不同的动作*/switch (pdev->state){case CAMIF_STATE_READY:update_camif_regs(fh->dev);  //配置寄存器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;}
}

这里的英文注释也相当详细。

case CAMIF_STATE_READY:update_camif_regs(fh->dev);  //配置寄存器break;


这里有个updata_camif_regs(fh-.dev)函数,需要看看他都做了什么:

/* 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 'Low'{barrier();/*#define S3C244X_CICOSTATUS		S3C244X_CAMIFREG(0x64)*//*datasheet [28]位的解释*//*   Camera VSYNC (This bit can be referred by CPU for first SFR setting. And, it can be seen in the ITU-R BT 656 mode, too) */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);
}

下面的配置自己对着datasheet慢慢研究吧!

/* 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 heightiowrite32(cisrcfmt, S3C244X_CISRCFMT);
}/* update registers:*	PREVIEW path:*		CIPRCLRSA1 ~ CIPRCLRSA4*		CIPRTRGFMT*		CIPRCTRL*		CIPRSCCTRL*		CIPRTAREA*	CODEC path:*		CICOYSA1 ~ CICOYSA4*		CICOCBSA1 ~ CICOCBSA4*		CICOCRSA1 ~ CICOCRSA4*		CICOTRGFMT*		CICOCTRL*		CICOTAREA*/
static void __inline__ update_target_fmt_regs(struct tq2440_camif_dev * pdev)
{u32 ciprtrgfmt;u32 ciprctrl;u32 ciprscctrl;u32 mainBurstSize, remainedBurstSize;/* CIPRCLRSA1 ~ CIPRCLRSA4. */iowrite32(img_buff[0].phy_base, S3C244X_CIPRCLRSA1);iowrite32(img_buff[1].phy_base, S3C244X_CIPRCLRSA2);iowrite32(img_buff[2].phy_base, S3C244X_CIPRCLRSA3);iowrite32(img_buff[3].phy_base, S3C244X_CIPRCLRSA4);/* CIPRTRGFMT. */ciprtrgfmt =	(pdev->preTargetHsize<<16)		// horizontal pixel number of target image|(0<<14)						// don't mirror or rotation.|(pdev->preTargetVsize<<0);	// vertical pixel number of target imageiowrite32(ciprtrgfmt, S3C244X_CIPRTRGFMT);/* CIPRCTRL. */calc_burst_size(2, pdev->preTargetHsize, &mainBurstSize, &remainedBurstSize);ciprctrl = (mainBurstSize<<19)|(remainedBurstSize<<14);iowrite32(ciprctrl, S3C244X_CIPRCTRL);/* CIPRSCCTRL. */ciprscctrl = ioread32(S3C244X_CIPRSCCTRL);ciprscctrl &= 1<<15;	// clear all other info except 'preview scaler start'.ciprscctrl |= 0<<30;	// 16-bits RGBiowrite32(ciprscctrl, S3C244X_CIPRSCCTRL);	// 16-bit RGB/* CIPRTAREA. */iowrite32(pdev->preTargetHsize * pdev->preTargetVsize, S3C244X_CIPRTAREA);
}/* update CIWDOFST only. */
static void __inline__ update_target_wnd_regs(struct tq2440_camif_dev * pdev)
{u32 ciwdofst;u32 winHorOfst, winVerOfst;winHorOfst = (pdev->srcHsize - pdev->wndHsize)>>1;winVerOfst = (pdev->srcVsize - pdev->wndVsize)>>1;winHorOfst &= 0xFFFFFFF8;winVerOfst &= 0xFFFFFFF8;if ((winHorOfst == 0)&&(winVerOfst == 0)){ciwdofst = 0;	// disable windows offset.}else{ciwdofst = (1<<31)			// window offset enable|(1<<30)			// clear the overflow ind flag of input CODEC FIFO Y|(winHorOfst<<16)		// windows horizontal offset|(1<<15)			// clear the overflow ind flag of input CODEC FIFO Cb|(1<<14)			// clear the overflow ind flag of input CODEC FIFO Cr|(1<<13)			// clear the overflow ind flag of input PREVIEW FIFO Cb|(1<<12)			// clear the overflow ind flag of input PREVIEW FIFO Cr|(winVerOfst<<0);		// window vertical offset}iowrite32(ciwdofst, S3C244X_CIWDOFST);
}/* update registers:*	PREVIEW path:*		CIPRSCPRERATIO*		CIPRSCPREDST*		CIPRSCCTRL*	CODEC path:*		CICOSCPRERATIO*		CICOSCPREDST*		CICOSCCTRL*/
static void __inline__ update_target_zoom_regs(struct tq2440_camif_dev * pdev)
{u32 preHratio, preVratio;u32 Hshift, Vshift;u32 shfactor;u32 preDstWidth, preDstHeight;u32 Hscale, Vscale;u32 mainHratio, mainVratio;u32 ciprscpreratio;u32 ciprscpredst;u32 ciprscctrl;/* CIPRSCPRERATIO. */calc_prescaler_ratio_shift(pdev->wndHsize, pdev->preTargetHsize, &preHratio, &Hshift);calc_prescaler_ratio_shift(pdev->wndVsize, pdev->preTargetVsize, &preVratio, &Vshift);shfactor = 10 - (Hshift + Vshift);ciprscpreratio =	(shfactor<<28)		// shift factor for preview pre-scaler|(preHratio<<16)		// horizontal ratio of preview pre-scaler|(preVratio<<0);		// vertical ratio of preview pre-scaleriowrite32(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-scaleriowrite32(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-scaleriowrite32(ciprscctrl, S3C244X_CIPRSCCTRL);
}


好吧,让我们回到可爱的camif_open函数,init_image_buffer();我们看看他的定义吧:

/* 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_pgaes就ok了.这函数就是整页整页申请内存的函数,kmalloc什么的都太小,不适合用在这里.
接下来,我们在细细看吧:

	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;  //谁的大,就用谁的sizeorder = get_order(size);  //把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);   //GFP_DMA,申请dmaif (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.// 把虚拟地址转为物理地址,为 DMA address. 

这里来看看img_buff[ ]是什么结构体:

/* image buffer for previewing. */
struct tq2440_camif_buffer img_buff[] =
{{.state = CAMIF_BUFF_INVALID,   //状态.img_size = 0,                 //大小.order = 0,                     //2的order次幂的幂数.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}
};

接下来用同样的方法为img_buff[1],img_buff[2],img_buff[3]申请了内存。

最后一句:invalid_image_buffer(),设置img_buff的state。

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;
}
让我们再次回到camif_open函数中吧,看看接下来的就个函数:

	request_irq(IRQ_S3C2440_CAM_C, on_camif_irq_c, IRQF_DISABLED, "CAM_C", pdev);	// setup ISRsif (ret < 0){goto error2;}request_irq(IRQ_S3C2440_CAM_P, on_camif_irq_p, IRQF_DISABLED, "CAM_P", pdev);if (ret < 0){goto error3;}

这两个是中断申请,为这两中断:IRQ_S3C2440_CAM_C,IRQ_S3C2440_CAM_P.对于中断申请函数,我应该关心的就是中断服务函数了

,让我们来看看他们都做了些什么:

/** 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 count of codec DMA (This counter value indicates the
next frame number)这里算出这个值在后面根本没用到,是用来debug的吧*/*/frame = (cicostatus&(3<<26))>>26;frame = (frame+4-1)%4;  //DMA的frame数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);
}这个是camif p通道的中断响应函数:
/** 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);
}

看这里:

if (pdev->cmdcode & CAMIF_CMD_STOP){stop_capture(pdev);pdev->state = CAMIF_STATE_READY;}

上面有个stop_capture函数,将会与后面的start_capture相对应

/* 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;}
}
这里有个camif_c2p函数:
camif_c2p(pdev);   //从codes转到preview
我们看看其定义吧:

/* 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);}
}


回到open函数中接着往下看:

	clk_enable(pdev->clk);		// 开启camif时钟soft_reset_camif();file->private_data = fh;fh->dev  = pdev;update_camif_config(fh, 0);return 0;
这里的soft_reset_camif()跟前面的hw_reset_camif()其实是一样的:

/* software reset camera interface.
写入的值的功能具体看s3c2440 camera接口的寄存器*/
*/
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);
}

呵呵,已经看完ov9650驱动的两个函数,camif_init和camif_open。这连个函数主要都是做一些初始化和内存分配的工作:camera接口的I/O内存申请
,camera寄存器的配置,摄像的寄存器的初始化,图像缓存通道的DMA内存的申请.接下我么来看看如何开启摄像头,进行拍摄预览。

2.read = 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到用户层*/                  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;
}

这个函数比想象的要短,这里只要看看start_capture(pdev,0)函数就好了:


/* 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)		// Clear the overflow indication flag of input CODEC FIFO Cb|(1<<14)		// Clear the overflow indication flag of input CODEC FIFO Cr|(1<<13)		// Clear the overflow indication flag of input PREVIEW FIFO Cb|(1<<12);		// Clear the overflow indication flag of input PREVIEW FIFO Criowrite32(ciwdofst, S3C244X_CIWDOFST);ciprscctrl = ioread32(S3C244X_CIPRSCCTRL);ciprscctrl |= 1<<15;	// preview scaler startiowrite32(ciprscctrl, S3C244X_CIPRSCCTRL);/*设置为预览状态*/pdev->state = CAMIF_STATE_PREVIEWING;ciimgcpt = (1<<31)		// camera interface global capture enable|(1<<29);		// capture enable for preview scaler.iowrite32(ciimgcpt, S3C244X_CIIMGCPT); //开启拍摄和预览ret = 0;/*这里是拍照,steam为0*/if (stream == 0){pdev->cmdcode = CAMIF_CMD_STOP;ret = wait_event_interruptible(pdev->cmdqueue, pdev->cmdcode == CAMIF_CMD_NONE);}return ret;
}

  后面还会再写一篇关于camif的寄存器配置和这个驱动对应的用户层app的文章。See You~






http://www.ppmy.cn/news/152482.html

相关文章

ov9650学习(3)

参考了飞凌公司提供的测试源码&#xff0c;自己稍加修改&#xff0c;可以实现摄像头的在LCD上显示 由于飞凌公司提供的测试源码功能很多所以一下子可能不能理解&#xff0c;所以我就想一步步拆开来做&#xff0c;先实现在LCD屏上的显示 ——————————————————…

linux 0v9650驱动分析

学习了裸机OV9650的P通道LCD直接显示程序&#xff0c;作为这点基础开始分析OV9650在linux设备驱动程序。昨天看了点这个驱动程序&#xff0c;让我很郁闷的是写这个程序的人是有毛病还是怎么回事&#xff0c;简简单单的IO口功能引脚的定义&#xff0c;整出了一个套一个的定义&am…

OV9650----摄像头调试笔记

http://blog.chinaunix.net/uid-24486720-id-1664850.html 过4天的调试,摄像头终于可以拍照片保存到电脑上来了&#xff0c;ov9650的调试走了不少弯路&#xff0c;一些教训总结如下&#xff1a; 1:OV9650是OmniVision公司的COMS摄像头&#xff0c;号称有130万像素&#xff0c;…

OV9650摄像头驱动略析

首先要明确一下摄像头工作方式&#xff1a; 一、摄像头是怎么把数据传送给mini2440的呢&#xff1f; 这个摄像头有10个数据口&#xff0c;mini2440通过这些数据口采集摄像头的数据。 二、硬件以什么样的方式交换采集数据呢&#xff1f; 摄像头将采集到的图像数据以一些标准…

ov9650 实时显示app 平台:i.mx6ul linux 3.14.38

本文先简单记录&#xff1a; 1.ov9650采集 摄像头输出yuyv格式 2.写入到framebuffer显示 3.贴yuv2rgb.c的代码如下&#xff1a; tips: 查表法/bpp32 table略 void yuv2rgb(unsigned char *pyuv,unsigned char *prgb,int width, int height){ unsigned char *py p…

ov9650摄像头驱动之——linux内核v4l2架构分析3

NO.3 V4L2的API和数据结构 V4L2是V4L的升级版本&#xff0c;为linux下视频设备程序提供了一套接口规范。包括一套数据结构和底层V4L2驱动接口。 1、常用的结构体在内核目录include/linux/videodev2.h中定义 struct v4l2_requestbuffers //申请帧缓冲&#xff0c;对应命令VIDIOC…

ov9650 的配置

经过4天的调试摄像头终于可以拍照片保存到电脑上来&#xff0c;ov9650的调试走了不少弯路&#xff0c;一些教训总结如下&#xff1a; 1:OV9650是OmniVision公司的COMS摄像头&#xff0c;号称有130万像素&#xff0c;但是实际效果感觉不如CCD的&#xff0c;特别是远处的背景更糟…

基于mini2440的ov9650摄像头裸机测试

mini2440提供了一个摄像头接口&#xff0c;可以输出RGB24,RGB16的原始图像还可以输出编码的如YUV格式的图像&#xff0c;并提供了偏移翻转&#xff0c;放大缩小的功能。与开发板配套的摄像头模块为CAM130,采用ov9650的芯片。操作摄像头接口实现视频的采集与显示&#xff0c;主要…