前言
最近想做摄像头驱动,看了一些文章,对摄像头驱动的结构还是很晕。于是决定分析内核自带的驱动程序。内核的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设置为0
WAIT_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 cmdq