本文均属自己阅读源码的点滴总结,转账请注明出处谢谢。http://blog.csdn.net/gzzaigcn/article/details/7750079
欢迎和大家交流。qq:1037701636 email:200803090209@zjut.com,gzzaigcn2012@gmail.com
dm6446是基于Davinci架构设计的多媒体处理器。在这里我们分析的Linux源码是montavista的2.6.10的版本,该源码中使用的视频驱动架构为V4L2的框架。对这个框架而言,网上已经存在大量的分析,但涉及的内容主要都是框架层的封装以及相关应用层的解析,底层核心的内容很少会涉及。在这里,我将结合DM6446的视频处理前端VPFE中的CCDC模块,完成视频采集的驱动核心内容的解析,核心源码位于kernel/driver/media/video下面。
依旧驱动模块的注册过程,视频采集的驱动模块主要分为:videodev_init(位于videodev.c),vpfe_init(位于davinci_vpfe.c),tvp5146_i2c_init(该模块在这里不做解析,是实现一款解码的芯片)。
videodev_init(位于videodev_init.c)模块解析
该驱动模块下,主要是完成V4L2核心的驱动的注册。源码如下:
[html] view
plaincopy
-
static int __init videodev_init(void)
-
{
-
int ret;
-
-
printk(KERN_INFO "Linux video capture interface: v1.00
");
-
if (register_chrdev(VIDEO_MAJOR, VIDEO_NAME, &video_fops)) { //注册一个视频驱动,主设备号为81
-
printk(KERN_WARNING "video_dev: unable to get major %d
", VIDEO_MAJOR);
-
return -EIO;
-
}
-
-
ret = class_register(&video_class);
-
if (ret < 0) {
-
unregister_chrdev(VIDEO_MAJOR, VIDEO_NAME);
-
printk(KERN_WARNING "video_dev: class_register failed
");
-
return -EIO;
-
}
-
-
return 0;
-
}
从代码的分析中我们看到,V4L2的架构中,先是注册一个主设备号为81的字符型设备,文件操作指针定义为video_fops。的确在上述的模块中完成的内容不多,这只是V4L2驱动的基本框架,可以再多平台之间移植。因此在这个框架下针对自己的处理器平台需要完成video_device的抽象设备和驱动的注册。也就是说V4L2下面可以注册进入很多的视频抽象设备。
vpfe_init(位于davinci_vpfe.c)驱动模块中主要完成的内容就是完成video_device的注册,当然因为结合了专用的处理器平台在完成设备和驱动和注册的同时还需要完成相关硬件设备需求的初始化。
[plain] view
plaincopy
-
static int vpfe_init(void)
-
{
-
int i = 0;
-
int fbuf_size;
-
ccdc_frmfmt frame_format;
-
void *mem;
-
int ret = 0;
-
-
if (device_type == TVP5146) {
-
fbuf_size = VPFE_TVP5146_MAX_FBUF_SIZE; //768*576*2
-
vpfe_device = vpfe_device_ycbcr;
-
frame_format = vpfe_device.ccdc_params_ycbcr.frm_fmt;
-
}
-
-
else if (device_type == MT9T001) {
-
fbuf_size = VPFE_MT9T001_MAX_FBUF_SIZE;
-
vpfe_device = vpfe_device_raw;
-
frame_format = vpfe_device.ccdc_params_raw.frm_fmt;
-
} else {
-
return -1;
-
}
-
/* allocate memory at initialization time to guarentee availability *///对默认的3路缓冲区进行内存的申请
-
for (i = 0; i < VPFE_DEFNUM_FBUFS; i++) { //VPFE_DEFNUM_FBUFS=3
-
mem =
-
(void *)__get_free_pages(GFP_KERNEL | GFP_DMA,
-
get_order(fbuf_size)); //需要的一帧内存大小为768*576*2
-
if (mem) {
-
unsigned long adr = (unsigned long)mem; //获取32位的内存地址
-
u32 size = PAGE_SIZE << (get_order(fbuf_size));//get_order(fbuf_size)=8,PAGE_SIZE=4K,
-
while (size > 0) {
-
/* make sure the frame buffers
-
are never swapped out of memory */
-
SetPageReserved(virt_to_page(adr));//对256页每页内存申请被保留,不被他人使用
-
adr += PAGE_SIZE;
-
size -= PAGE_SIZE;
-
}//获取每一个物理缓存区的首地址,地址存入fbuffers数组
-
vpfe_device.fbuffers[i] = (u8 *) mem;//3
-
} else {
-
while (--i >= 0) {
-
free_reserved_pages((unsigned long)
-
vpfe_device.
-
fbuffers[i], fbuf_size);
-
}
-
printk(KERN_INFO
-
"frame buffer memory allocation failed.
");
-
return -ENOMEM;
-
}
-
}
-
if (driver_register(&vpfe_driver) != 0) {
-
printk(KERN_INFO "driver registration failed
");
-
return -1;
-
}
-
if (platform_device_register(&_vpfe_device) != 0) {
-
driver_unregister(&vpfe_driver);
-
printk(KERN_INFO "device registration failed
");
-
return -1;
-
}
-
-
ccdc_reset(); //CCDC复位不使能
-
-
if (device_type == TVP5146) {
-
ret = tvp5146_ctrl(TVP5146_INIT, NULL); //decoder I2C驱动的初始化
-
if (ret >= 0) {
-
ret = tvp5146_ctrl(TVP5146_RESET, NULL);
-
/* configure the tvp5146 to default parameters */
-
ret |=
-
tvp5146_ctrl(TVP5146_CONFIG,
-
&vpfe_device.tvp5146_params); //vpfe_device=vpfe_device_ycbcr
-
}
-
if (ret < 0) {
-
tvp5146_ctrl(TVP5146_CLEANUP, NULL);
-
}
-
} else if (device_type == MT9T001) {
-
/* enable video port in case of raw capture */
-
ccdc_enable_vport();
-
vpfe_device.config_dev_fxn = mt9t001_ctrl;
-
ret =
-
vpfe_device.config_dev_fxn(MT9T001_INIT,
-
&vpfe_device.std,
-
&vpfe_device.device_params);
-
}
-
-
if (ret < 0) {
-
platform_device_unregister(&_vpfe_device);
-
driver_unregister(&vpfe_driver);
-
/* Free memory for all image buffers */
-
for (i = 0; i < VPFE_DEFNUM_FBUFS; i++) {
-
free_reserved_pages((unsigned long)
-
vpfe_device.fbuffers[i], fbuf_size);
-
}
-
return -1;
-
}
-
-
/* setup interrupt handling */
-
/* request VDINT1 if progressive format */
-
if (frame_format == CCDC_FRMFMT_PROGRESSIVE) { //frame_format=CCDC_FRMFMT_INTERLACED=1
-
ret = request_irq(IRQ_VDINT1, vdint1_isr, SA_INTERRUPT,
-
"dm644xv4l2", (void *)&vpfe_device); //逐行格式,申请VDINT1中断
-
if (ret < 0) {
-
platform_device_unregister(&_vpfe_device);
-
driver_unregister(&vpfe_driver);
-
/* Free memory for all image buffers */
-
for (i = 0; i < VPFE_DEFNUM_FBUFS; i++) {
-
free_reserved_pages((unsigned long)
-
vpfe_device.
-
fbuffers[i], fbuf_size);
-
}
-
return -1;
-
}
-
}
-
ret = request_irq(IRQ_VDINT0, vpfe_isr, SA_INTERRUPT,
-
"dm644xv4l2", (void *)&vpfe_device);//申请VDINT0中断
-
if (ret < 0) {
-
platform_device_unregister(&_vpfe_device);
-
driver_unregister(&vpfe_driver);
-
/* Free memory for all image buffers */
-
for (i = 0; i < VPFE_DEFNUM_FBUFS; i++) {
-
free_reserved_pages((unsigned long)
-
vpfe_device.fbuffers[i], fbuf_size);
-
}
-
free_irq(IRQ_VDINT1, &vpfe_device);
-
return -1;
-
}
-
-
printk(KERN_INFO "DaVinci v4l2 capture driver V1.0 loaded
");
-
return 0;
-
}
-
-
/
在这个初始化的内容中,我们看到做了很多的内容,这里的vpfe_device = vpfe_device_ycbcr;就是上面提到的video_device的实例,也就是DM6446的视频前端VPFE这个视频设备。同时可以发现,在这里进行了连续的内存页式缓存区的申请(主要用于后续的视频采集内存缓存区队列设置的相关内容),完成vpfe_device的初始化。随后通过基于平台设备进行了驱动和设备的注册。其实这个设备和驱动的注册,完全就是为了调用匹配以后的probe函数,进一步完成相关视频设备文件(主设备号为81,次设备号不一)节点的创建(相当于手动在终端完成mknod操作),只有这样才可以在应用层打开设备之后,才能经过系统调来调用主设备号81的文件操作指针video_fops。同时也完成了VPFE前端CCDC的中断申请。下面来看porbe的指针函数。
[plain] view
plaincopy
-
static int __init vpfe_probe(struct device *device) //传入参数_vpfe_device->dev
-
{
-
struct video_device *vfd;
-
vpfe_obj *vpfe = &vpfe_device; //vpfe_device=vpfe_device_ycbcr
-
vpfe_dev = device;
-
dev_dbg(vpfe_dev, "
Starting of vpfe_probe...");
-
/* alloc video device */
-
if ((vfd = video_device_alloc()) == NULL) {
-
return -ENOMEM;
-
}
-
*vfd = vpfe_video_template; //video_device的类型
-
vfd->dev = device;
-
vfd->release = video_device_release;
-
snprintf(vfd->name, sizeof(vfd->name),
-
"DM644X_VPFE_DRIVER_V%d.%d.%d",
-
(VPFE_VERSION_CODE >> 16) & 0xff,
-
(VPFE_VERSION_CODE >> 8) & 0xff, (VPFE_VERSION_CODE) & 0xff);
-
-
vpfe->video_dev = vfd; //vpfe_obj的实例vpfe_device获取video_device实例
-
vpfe->usrs = 0;
-
vpfe->io_usrs = 0;
-
vpfe->started = FALSE;
-
vpfe->latest_only = TRUE;
-
-
v4l2_prio_init(&vpfe->prio);
-
init_MUTEX(&vpfe->lock);
-
-
/* register video device */
-
dev_dbg(vpfe_dev, "trying to register vpfe device.
");
-
dev_dbg(vpfe_dev, "vpfe=%x,vpfe->video_dev=%x
", (int)vpfe,
-
(int)&vpfe->video_dev);
-
if (video_register_device(vpfe->video_dev, VFL_TYPE_GRABBER, -1) < 0) { //完成视频设备vfd的注册
-
video_device_release(vpfe->video_dev);
-
vpfe->video_dev = NULL;
-
return -1;
-
}
-
-
dev_dbg(vpfe_dev, "DM644X vpfe: driver version V%d.%d.%d loaded
",
-
(VPFE_VERSION_CODE >> 16) & 0xff,
-
(VPFE_VERSION_CODE >> 8) & 0xff, (VPFE_VERSION_CODE) & 0xff);
-
-
dev_dbg(vpfe_dev, "vpfe: registered device video%d
",
-
vpfe->video_dev->minor & 0x1f);
-
-
/* all done */
-
return 0;
-
}
在这里我们可以看到调用video_register_device完成
[plain] view
plaincopy
-
int video_register_device(struct video_device *vfd, int type, int nr)
-
{
-
int i=0;
-
int base;
-
int end;
-
char *name_base;
-
-
switch(type)
-
{
-
case VFL_TYPE_GRABBER: //明是一个图像采集设备?包括摄像头、调谐器
-
base=0;
-
end=64;
-
name_base = "video";
-
break;
-
case VFL_TYPE_VTX: //代表视传设备
-
base=192;
-
end=224;
-
name_base = "vtx";
-
break;
-
case VFL_TYPE_VBI: //代表的设备是从视频消隐的时间段取得信息的设备。
-
base=224;
-
end=240;
-
name_base = "vbi";
-
break;
-
case VFL_TYPE_RADIO: //代表无线电设备
-
base=64;
-
end=128;
-
name_base = "radio";
-
break;
-
default:
-
return -1;
-
}
-
-
/* pick a minor number */
-
down(&videodev_lock);
-
if (nr >= 0 && nr < end-base) {
-
/