linux驱动学习笔记Camif.docx
《linux驱动学习笔记Camif.docx》由会员分享,可在线阅读,更多相关《linux驱动学习笔记Camif.docx(16页珍藏版)》请在冰豆网上搜索。
![linux驱动学习笔记Camif.docx](https://file1.bdocx.com/fileroot1/2023-1/30/588c2775-c400-43ee-9839-2a9670130b43/588c2775-c400-43ee-9839-2a9670130b431.gif)
linux驱动学习笔记Camif
触摸屏驱动学习:
\drivers\media\video\s3c2440camif.c
\drivers\media\video\s3c2440_ov9650.c
\drivers\media\video\sccb.c
硬件连接
I2C总线连接
I2CSCL——GPE14
I2CSDA——GPE15
Sccb.h
#defineSIO_CS3C2410_GPE14
#defineSIO_DS3C2410_GPE15
#defineState(x)s3c2410_gpio_getpin(x)
#defineHigh(x)do{s3c2410_gpio_setpin(x,1);smp_mb();}while(0)
#defineLow(x)do{s3c2410_gpio_setpin(x,0);smp_mb();}while(0)
#defineWAIT_STABLE()do{udelay(10);}while(0)
#defineWAIT_CYCLE()do{udelay(90);}while(0)
#defineCFG_READ(x)do{s3c2410_gpio_cfgpin(x,S3C2410_GPIO_INPUT);smp_mb();}while(0)
#defineCFG_WRITE(x)do{s3c2410_gpio_cfgpin(x,S3C2410_GPIO_OUTPUT);smp_mb();}while(0)
voidsccb_write(u8IdAddr,u8SubAddr,u8data);
u8sccb_read(u8IdAddr,u8SubAddr);
CFG_WRITE(x)把GPIO设置为输出
CFG_READ(x)把GPIO设置为输入
High(x)把GPIO设置为高电平
Low(x)把GPIO设置为低电平
Sccb.c
把SCL和Data都拉高,即为默认初始化状态电平
intsccb_init(void)
{
CFG_WRITE(SIO_C);
CFG_WRITE(SIO_D);
High(SIO_C);
High(SIO_D);
WAIT_STABLE();
return0;
}
由上面的图可以看出,CLK高电平时,DATA拉低,即为START
staticvoid__inline__sccb_start(void)
{
CFG_WRITE(SIO_D);
Low(SIO_D);
WAIT_STABLE();
}
使用到一个信号量
staticDECLARE_MUTEX(bus_lock);
voidsccb_write(u8IdAddr,u8SubAddr,u8data)
{
down(&bus_lock);
sccb_start();
sccb_write_byte(IdAddr);
sccb_write_byte(SubAddr);
sccb_write_byte(data);
sccb_stop();
up(&bus_lock);
}
首先把信号量数值降低,表示自己使用,如果此时信号量不大于0,表示bus正在使用,驱动会在此等待,知道信号量大于0,然后,将其减1,此时bus对外界不可用。
最后再把信号量加1,表示bus可用。
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
摄像头接口初始化:
/*
*camif_init()
*/
staticint__initcamif_init(void)
{
配置CAMIF的GPIO接口功能
/*setgpio-jtocameramode.*/
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);
申请CAMIF的寄存器区域,由于OS的系统统一管理,所以,需要确定,该寄存器区域没有被别的进程获取,所以要申请。
/*initcamera'svirtualmemory.*/
if(!
request_mem_region((unsignedlong)S3C2440_PA_CAMIF,S3C2440_SZ_CAMIF,CARD_NAME))
{
ret=-EBUSY;
gotoerror1;
}
申请到了寄存器区域,相当于物理地址可用,然后,再将该物理地址映射到虚拟地址。
/*remapthevirtualmemory.*/
camif_base_addr=(unsignedlong)ioremap_nocache((unsignedlong)S3C2440_PA_CAMIF,S3C2440_SZ_CAMIF);
if(camif_base_addr==(unsignedlong)NULL)
{
ret=-EBUSY;
gotoerror2;
}
获取CAMIF的时钟,使用24M
/*initcameraclock.*/
pdev->clk=clk_get(NULL,"camif");
if(IS_ERR(pdev->clk))
{
ret=-ENOENT;
gotoerror3;
}
clk_enable(pdev->clk);
camif_upll_clk=clk_get(NULL,"camif-upll");
clk_set_rate(camif_upll_clk,);
mdelay(100);
初始化CAMIF的状态
/*initcamifstateanditslock.*/
pdev->state=CAMIF_STATE_FREE;
CAMIFDEV的状态:
/*fors3c2440camif_dev->statefield.*/
enum
{
CAMIF_STATE_FREE=0,//notopenned
CAMIF_STATE_READY=1,//openned,butstandby
CAMIF_STATE_PREVIEWING=2,//inpreviewing
CAMIF_STATE_CODECING=3//incapturing
};
注册杂项设备:
/*registertovideodevlayer.*/
if(misc_register(&misc)<0)
{
ret=-EBUSY;
gotoerror4;
}
printk(KERN_ALERT"s3c2440camifinitdone\n");
初始化SCCB接口,SerialCameraControlBus,其实是I2C接口
sccb_init();
硬件复位CAMIF
hw_reset_camif();
其实是软件复位,对CIGCTRL寄存器配置软件复位
/*softwareresetcamerainterface.*/
staticvoid__inline__hw_reset_camif(void)
{
u32cigctrl;
cigctrl=(1<<30)|(1<<29);
iowrite32(cigctrl,S3C244X_CIGCTRL);
mdelay(10);
cigctrl=(1<<29);
iowrite32(cigctrl,S3C244X_CIGCTRL);
mdelay(10);
}
检测是否存在OV9650,GPG4连的是LCD_PWR,不知道为什么?
has_ov9650=s3c2440_ov9650_init()>=0;
s3c2410_gpio_setpin(S3C2410_GPG4,1);
OV9650的初始化:
ints3c2440_ov9650_init(void)
{
printk(KERN_ALERT"LoadingOV9650driver.........\n");
/*poweron.*/
ov9650_poweron();
mdelay(100);
/*checkdevice.*/
if(ov9650_check()==0&&ov9650_check()==0)
{
printk(KERN_ERR"NoOV9650found!
!
!
\n");
return-ENODEV;
}
show_ov9650_product_id();
ov9650_init_regs();
printk("ov9650initdone!
\n");
return0;
}
OV9650上电,这里对GPG12设置为输出,这里使用的虽然是中断引脚,但似乎没有用中断,这是一个电源控制引脚,PWDN,0:
poweron,1:
powerdown
staticvoid__inline__ov9650_poweron(void)
{
s3c2410_gpio_cfgpin(S3C2410_GPG12,S3C2410_GPG12_OUTP);
s3c2410_gpio_setpin(S3C2410_GPG12,0);
mdelay(20);
}
OV9650检测,读取OV9650的ManuID
staticint__inline__ov9650_check(void)
{
u32mid;
mid=sccb_read(OV9650_SCCB_ADDR,0x1c)<<8;
mid|=sccb_read(OV9650_SCCB_ADDR,0x1d);
printk("SCCBaddress0x%02X,manufactureID0x%04X,expect0x%04X\n",OV9650_SCCB_ADDR,mid,OV9650_MANUFACT_ID);
return(mid==OV9650_MANUFACT_ID)?
1:
0;
}
#defineOV9650_SCCB_ADDR0x60
#defineOV9650_MANUFACT_ID0x7FA2
#defineOV9650_PRODUCT_ID0x9650
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
摄像头接口打开:
/*
*camif_open()
*/
staticintcamif_open(structinode*inode,structfile*file)
{
首先判断是否存在OV9650camera是文件开始的一个static变量。
structs3c2440camif_dev*pdev;
structs3c2440camif_fh*fh;
intret;
if(!
has_ov9650){
return-ENODEV;
}
pdev=&camera;
为filehandle分配内存
fh=kzalloc(sizeof(*fh),GFP_KERNEL);//allocmemoryforfilehandle
设置设备状态为openandstandby,初始化CAMIF的配置。
pdev->state=CAMIF_STATE_READY;
init_camif_config(fh);
对CAMIF进行配置,最后更新一下配置
/*configcamifwhenmaster-opencamera.*/
staticvoidinit_camif_config(structs3c2440camif_fh*fh)
{
structs3c2440camif_dev*pdev;
pdev=fh->dev;
pdev->input=0;//FIXME,thedefaultinputimageformat,seeinputs[]fordetail.
/*thesourceimagesize(inputfromexternalcamera).*/
pdev->srcHsize=1280;//FIXME,theOV9650'shorizontaloutputpixels.
pdev->srcVsize=1024;//FIXME,theOV9650'svericaloutputpixels.
/*thewindowedimagesize.*/
pdev->wndHsize=1280;
pdev->wndVsize=1024;
/*codec-pathtarget(output)imagesize.*/
pdev->coTargetHsize=pdev->wndHsize;
pdev->coTargetVsize=pdev->wndVsize;
/*preview-pathtarget(preview)imagesize.*/
pdev->preTargetHsize=640;
pdev->preTargetVsize=512;
update_camif_config(fh,CAMIF_CMD_STOP);
}
由于设备状态是CAMIF_STATE_READY所以,直接更新寄存器。
/*updatecamerainterfacewiththenewconfig.*/
staticvoidupdate_camif_config(structs3c2440camif_fh*fh,u32cmdcode)
{
structs3c2440camif_dev*pdev;
pdev=fh->dev;
switch(pdev->state)
{
caseCAMIF_STATE_READY:
update_camif_regs(fh->dev);//configtheregsdirectly.
break;
caseCAMIF_STATE_PREVIEWING:
/*camifispreviewingimage.*/
disable_irq(IRQ_S3C2440_CAM_P);//disablecam-previewirq.
/*sourceimageformat.*/
if(cmdcode&CAMIF_CMD_SFMT)
{
//ignoreit,nothingtodonow.
}
/*targetimageformat.*/
if(cmdcode&CAMIF_CMD_TFMT)
{
/*changetargetimageformatonly.*/
pdev->cmdcode|=CAMIF_CMD_TFMT;
}
不知为什么要等待VSYNC为L?
然后对寄存器进行配置。
/*updatecamifregisters,calledonlywhencamifready,orISR.*/
staticvoid__inline__update_camif_regs(structs3c2440camif_dev*pdev)
{
if(!
in_irq())
{
while
(1)//waituntilVSYNCis'L'
{
barrier();
if((ioread32(S3C244X_CICOSTATUS)&(1<<28))==0)
break;
}
}
/*WARNING:
don'tchangethestatementsortbelow!
!
!
*/
update_source_fmt_regs(pdev);
update_target_wnd_regs(pdev);
update_target_fmt_regs(pdev);
update_target_zoom_regs(pdev);
}
初始化CAMIF的DMA内存。
ret=init_image_buffer();//initimagebuffer.
这里为DMA分配内存,按页分配,内存管理的知识需要学习?
/*initimagebuffer(onlywhenthecamifisfirstopen).*/
staticint__inline__init_image_buffer(void)
{
intsize1,size2;
unsignedlongsize;
unsignedintorder;
/*size1isthemaximagesizeofcodecpath.*/
size1=MAX_C_WIDTH*MAX_C_HEIGHT*16/8;
/*size2isthemaximagesizeofpreviewpath.*/
size2=MAX_P_WIDTH*MAX_P_HEIGHT*16/8;
size=(size1>size2)?
size1:
size2;
order=get_order(size);//获取需要分配字节的2的阶数,内存按页分配
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==(unsignedlong)NULL)
{
gotoerror0;
}
img_buff[0].phy_base=img_buff[0].virt_base-PAGE_OFFSET+PHYS_OFFSET;//theDMAaddress.
申请中断,分别为Codec的中断和Preview的中断
request_irq(IRQ_S3C2440_CAM_C,on_camif_irq_c,IRQF_DISABLED,"CAM_C",pdev);
request_irq(IRQ_S3C2440_CAM_P,on_camif_irq_p,IRQF_DISABLED,"CAM_P",pdev);
使能时钟,软件复位,更新CAMIF的配置。
clk_enable(pdev->clk);//andenablecamifclock.
soft_reset_camif();
file->private_data=fh;
fh->dev=pdev;
update_camif_config(fh,0);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
摄像头接口读取函数:
staticssize_tcamif_read(structfile*file,char__user*data,size_tcount,loff_t*ppos)
{
inti;
structs3c2440camif_fh*fh;
structs3c2440camif_dev*pdev;
fh=file->private_data;
pdev=fh->dev;
if(start_capture(pdev,0)!
=0)//此处是捕获一张图片,所以会阻塞在此,直至中断发生。
{
return-ERESTARTSYS;
}
//中断已经发生,数据已经更新。
disable_irq(IRQ_S3C2440_CAM_C);
disable_irq(IRQ_S3C2440_CAM_P);
for(i=0;i<4;i++)
{
if(img_buff[i].state!
=CAMIF_BUFF_INVALID)//如果数据已经更新,移动数据
{
copy_to_user(data,(void*)img_buff[i].virt_base,count);
img_buff[i].state=CAMIF_BUFF_INVALID;//设置数据无效
}
}
enable_irq(IRQ_S3C2440_CAM_P);//重新使能中断
enable_irq(IRQ_S3C2440_CAM_C);
returncount;
}
开始捕获函数,设置windowoffset
/*startimagecapture.
*
*param'stream'meanscapturepicturesstreamly