三星 摄像头 linux,基于Linux 3.0.8 Samsung FIMC(S5PV210) 的摄像头驱动框架解读(一)...
FIMC这个名字应该是从S5PC1x0开始出现的,在s5pv210里面的定义是摄像头接口,但是它同样具有图像数据颜色空间转换的作用。而exynos4412对它的定义看起来更清晰些,摄像头接口被定义为FIMC-LITE 。颜色空间转换的硬件结构被定义为FIMC-IS。不多说了,我们先来看看Linux3.0.8 三星的BSP包中与fimc驱动相关的文件。
上面的源码文件组成了整个fimc的驱动框架。通过.c文件的命名也大致可以猜测到FIMC的几个用途:
1、Capture,Camera Interface 用于控制Camera,及m2m操作
2、Output,这个用途可以简单看成:只使用了FIMC的m2m功能,这里fimc实际上就成了一个带有颜色空间转换功能的高速DMA。
3、Overlay,比如Android 的Overlay就依赖了FIMC的这个功能,可以简单把它看作是个m2fb,当然实质上还是m2m。
清楚FIMC的大致用途了。再来说说,每个C文件在FIMC驱动框架中扮演了何种角色:
csis.c文件,用于MIPI 接口的摄像头设备,这里不多说什么了。
fimc_dev.c是驱动中对FIMC硬件设备最高层的抽象,这在后面会详细介绍。
fimc_v4l2.c linux驱动中 ,将fimc 设备的功能操作接口(Capture,output,Overlay),用v4l2框架封装。在应用层用过摄像头设备,或在应用层使用FMIC设备完成过m2m操作的朋友应该都清楚,fimc经层层封装后最终暴露给用户空间的是v4l2 标准接口的设备文件 videoX。 这里面也引出了一个我们应该关注的问题:Fimc设备在软件层上是如何同摄像头设备关联的。
fimc_capture.c实现对camera Interface 的控制操作,它实现的基础依赖硬件相关的摄像头驱动(eg.ov965X.c / ov5642.c 等)。 并且提供以下函数接口,由fimc_v4l2.c文件进一步封装
int fimc_g_parm(struct file *file, void*fh, struct v4l2_streamparm *a)
int fimc_s_parm(struct file *file, void*fh, struct v4l2_streamparm *a)
intfimc_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qc)
intfimc_querymenu(struct file *file, void *fh, struct v4l2_querymenu *qm)
intfimc_enum_input(struct file *file, void *fh, struct v4l2_input *inp)
intfimc_g_input(struct file *file, void *fh, unsigned int *i)
intfimc_release_subdev(struct fimc_control *ctrl)
intfimc_s_input(struct file *file, void *fh, unsigned int i)
intfimc_enum_fmt_vid_capture(struct file *file, void *fh,struct v4l2_fmtdesc *f)
intfimc_g_fmt_vid_capture(struct file *file, void *fh, struct v4l2_format *f)
intfimc_s_fmt_vid_capture(struct file *file, void *fh, struct v4l2_format *f)
intfimc_try_fmt_vid_capture(struct file *file, void *fh, struct v4l2_format *f)
intfimc_reqbufs_capture(void *fh, struct v4l2_requestbuffers *b)
intfimc_querybuf_capture(void *fh, struct v4l2_buffer *b)
intfimc_g_ctrl_capture(void *fh, struct v4l2_control *c)
intfimc_s_ctrl_capture(void *fh, struct v4l2_control *c)
intfimc_s_ext_ctrls_capture(void *fh, struct v4l2_ext_controls *c)
intfimc_cropcap_capture(void *fh, struct v4l2_cropcap *a)
intfimc_g_crop_capture(void *fh, struct v4l2_crop *a)
intfimc_s_crop_capture(void *fh, struct v4l2_crop *a)
intfimc_start_capture(struct fimc_control *ctrl)
intfimc_stop_capture(struct fimc_control *ctrl)
intfimc_streamon_capture(void *fh)
intfimc_streamoff_capture(void *fh)
intfimc_qbuf_capture(void *fh, struct v4l2_buffer *b)
intfimc_dqbuf_capture(void *fh, struct v4l2_buffer *b)
fimc_output.c实现fimc m2m操作,需要用FIMC实现硬件颜色空间转换的时候,这个文件里的函数就派上作用了,另外在fimc 用于Capture 和 overlay 过程本质上也包含m2m操作。因此除了提供功能函数接口,由fimc_v4l2.c文件进一步封装。另外还提供了一些功能函数供fimc_dev.c调用,比如用于设置一个m2m过程的srcAddr(源地址) 和 dstAddr(目的地址)。这部分接口太多就不贴出来了。
fimc_overlay.c实现fimc overlay操作。同样提供函数接口,由fimc_v4l2.c文件进一步封装。
fimc_regs.cFimc硬件相关操作,基本寄存器配置等。这个文件提供函数接口供fimc_capture.c、fimc_output.c、fimc_overlay.c调用。
通过刚才的分析,可以总结出下面的源码结构图:
好了,框架有了,再来看源码就轻松多了
接下来,先来看看FIMC设备的注册过程。以FIMC-0为例,说说/dev/video0 这个设备文件是怎么出来的。
先看几个关键结构:
首先是 s3c_platform_fimcfimc_plat_lsi;也就是抽象fimc模块的数据结构,fimc_plat_lsi还包含了一个.camera成员。该结构初始化如下
staticstructs3c_platform_fimc fimc_plat_lsi = {
.srclk_name = "mout_mpll",
.clk_name = "sclk_fimc",
.lclk_name = "fimc",
.clk_rate = 166750000,
#if defined(CONFIG_VIDEO_S5K4EA)
.default_cam = CAMERA_CSI_C,
#else
#ifdef CAM_ITU_CH_A
.default_cam = CAMERA_PAR_A,
#else
.default_cam = CAMERA_PAR_B,
#endif
#endif
.camera = {
#ifdef CONFIG_VIDEO_S5K4ECGX
&s5k4ecgx,
#endif
#ifdef CONFIG_VIDEO_S5KA3DFX
&s5ka3dfx,
#endif
#ifdef CONFIG_VIDEO_S5K4BA
&s5k4ba,
#endif
#ifdef CONFIG_VIDEO_S5K4EA
&s5k4ea,
#endif
#ifdef CONFIG_VIDEO_TVP5150
&tvp5150,
#endif
#ifdef CONFIG_VIDEO_OV9650
&ov9650,
#endif
},
.hw_ver = 0x43,
};
可以看到在s3c_platform_fimc中有一个camera成员。这里重点看一下ov9650.展开ov9650
staticstructs3c_platform_camera ov9650 = {
#ifdef CAM_ITU_CH_A
.id = CAMERA_PAR_A,
#else
.id = CAMERA_PAR_B,
#endif
.type = CAM_TYPE_ITU,
.fmt = ITU_601_YCBCR422_8BIT,
.order422 = CAM_ORDER422_8BIT_YCBYCR,
.i2c_busnum = 0,
.info = &ov9650_i2c_info,
.pixelformat = V4L2_PIX_FMT_YUYV,
.srclk_name = "mout_mpll",
/* .srclk_name = "xusbxti", */
.clk_name = "sclk_cam1",
.clk_rate = 40000000,
.line_length = 1920,
.width = 1280,
.height = 1024,
.window = {
.left = 0,
.top = 0,
.width = 1280,
.height = 1024,
},
/* Polarity */
.inv_pclk = 1,
.inv_vsync = 1,
.inv_href = 0,
.inv_hsync = 0,
.initialized = 0,
.cam_power = ov9650_power_en,
};
这个结构体,实现了对ov9650摄像头硬件结构的抽象。定义了摄像头的关键参数和基本特性。因为fimc设备在linux3.0.8内核中作为一个平台设备加载,而上面提到的s3c_platform_fimcfimc_plat_lsi仅是fimc的抽象数据而非设备。这就需要将抽象fimc的结构体作为fimc platform_device 的一个私有数据。所以就有了下面的过程。s3c_platform_fimcfimc_plat_lsi 结构在板级设备初始化XXX_machine_init(void) 过程作为s3c_fimc0_set_platdata 的实参传入。之后fimc_plat_lsi就成为了fimc设备的platform_data。
s3c_fimc0_set_platdata(&fimc_plat_lsi);
s3c_fimc1_set_platdata(&fimc_plat_lsi);
s3c_fimc2_set_platdata(&fimc_plat_lsi);
以s3c_fimc0_set_platdata为例展开
void__init s3c_fimc0_set_platdata(structs3c_platform_fimc *pd)
{
structs3c_platform_fimc *npd;
if(!pd)
pd = &default_fimc0_data;
npd = kmemdup(pd, sizeof(structs3c_platform_fimc), GFP_KERNEL);
if(!npd)
printk(KERN_ERR "%s: no memory for platform data\n", __func__);
else{
if(!npd->cfg_gpio)
npd->cfg_gpio = s3c_fimc0_cfg_gpio;
if(!npd->clk_on)
npd->clk_on = s3c_fimc_clk_on;
if(!npd->clk_off)
npd->clk_off = s3c_fimc_clk_off;
npd->hw_ver = 0x45;
/* starting physical address of memory region */
npd->pmem_start = s5p_get_media_memory_bank(S5P_MDEV_FIMC0, 1);
/* size of memory region */
npd->pmem_size = s5p_get_media_memsize_bank(S5P_MDEV_FIMC0, 1);
s3c_device_fimc0.dev.platform_data = npd;
}
}
最后一句是关键 s3c_device_fimc0.dev.platform_data = npd;看一下s3c_device_fimc0定义:
structplatform_device s3c_device_fimc0 = {
.name = "s3c-fimc",
.id = 0,
.num_resources = ARRAY_SIZE(s3c_fimc0_resource),
.resource = s3c_fimc0_resource,
};
fimc的抽象数据,则作为它的私有数据被包含进了s3c_device_fimc0这个结构中。到这里才完成了FIMC平台设备的最终定义。这个平台设备的定义s3c_device_fimc0又被添加到了整个硬件平台的 platform_device 列表中,最终在XXX_machine_init(void) 函数中调用platform_add_devices(mini210_devices, ARRAY_SIZE(mini210_devices)); 完成所有platform_device 的注册:
staticstructplatform_device *mini210_devices[] __initdata = {
&s3c_device_adc,
&s3c_device_cfcon,
&s3c_device_nand,
。。。
&s3c_device_fb,
&mini210_lcd_dev,
#ifdef CONFIG_VIDEO_FIMC
&s3c_device_fimc0,
&s3c_device_fimc1,
&s3c_device_fimc2,
}
platform_add_devices(mini210_devices, ARRAY_SIZE(mini210_devices));
platform_device被加载后,等待与之匹配的platform_driver。若此时fimc driver的驱动模块被加载。这个时候,fimc_dev.c文件里的static int __devinit fimc_probe(structplatform_device *pdev)函数上场了。
staticint__devinit fimc_probe(structplatform_device *pdev)
{
structs3c_platform_fimc *pdata;
structfimc_control *ctrl;
structclk *srclk;
intret;
if(!fimc_dev) {
fimc_dev = kzalloc(sizeof(*fimc_dev), GFP_KERNEL);
if(!fimc_dev) {
dev_err(&pdev->dev, "%s: not enough memory\n",
__func__);
return-ENOMEM;
}
}
ctrl = fimc_register_controller(pdev);
if(!ctrl) {
printk(KERN_ERR "%s: cannot register fimc\n", __func__);
gotoerr_alloc;
}
pdata = to_fimc_plat(&pdev->dev);
if(pdata->cfg_gpio)
pdata->cfg_gpio(pdev);
#ifdef REGULATOR_FIMC
/* Get fimc power domain regulator */
ctrl->regulator = regulator_get(&pdev->dev, "pd");
if(IS_ERR(ctrl->regulator)) {
fimc_err("%s: failed to get resource %s\n",
__func__, "s3c-fimc");
returnPTR_ERR(ctrl->regulator);
}
#endif //REGULATOR_FIMC
/* fimc source clock */
srclk = clk_get(&pdev->dev, pdata->srclk_name);
if(IS_ERR(srclk)) {
fimc_err("%s: failed to get source clock of fimc\n",
__func__);
gotoerr_v4l2;
}
/* fimc clock */
ctrl->clk = clk_get(&pdev->dev, pdata->clk_name);
if(IS_ERR(ctrl->clk)) {
fimc_err("%s: failed to get fimc clock source\n",
__func__);
gotoerr_v4l2;
}
/* set parent for mclk */
clk_set_parent(ctrl->clk, srclk);
/* set rate for mclk */
clk_set_rate(ctrl->clk, pdata->clk_rate);
/* V4L2 device-subdev registration */
ret = v4l2_device_register(&pdev->dev, &ctrl->v4l2_dev);
if(ret) {
fimc_err("%s: v4l2 device register failed\n", __func__);
gotoerr_fimc;
}
/* things to initialize once */
if(!fimc_dev->initialized) {
ret = fimc_init_global(pdev);
if(ret)
gotoerr_v4l2;
}
/* video device register */
ret = video_register_device(ctrl->vd, VFL_TYPE_GRABBER, ctrl->id);
if(ret) {
fimc_err("%s: cannot register video driver\n", __func__);
gotoerr_v4l2;
}
video_set_drvdata(ctrl->vd, ctrl);
ret = device_create_file(&(pdev->dev), &dev_attr_log_level);
if(ret
fimc_err("failed to add sysfs entries\n");
gotoerr_global;
}
printk(KERN_INFO "FIMC%d registered successfully\n", ctrl->id);
return0;
err_global:
video_unregister_device(ctrl->vd);
err_v4l2:
v4l2_device_unregister(&ctrl->v4l2_dev);
err_fimc:
fimc_unregister_controller(pdev);
err_alloc:
kfree(fimc_dev);
return-EINVAL;
}
在fimc_probe函数中有这么一段
if(!fimc_dev->initialized) {
ret = fimc_init_global(pdev);
if(ret)
gotoerr_v4l2;
}
这段代码执行过程:首先判断fimc是否已经被初始化完成(此时FIMC是忙状态的),如果没有被初始化,则执行fimc_init_global(pdev);函数,它的作用是先判断平台数据中是否初始化了摄像头结构(即前面提到的.camera成员),从平台数据中获得摄像头的时钟频率并将平台数据中内嵌的s3c_platform_camera结构数据保存到该驱动模块全局的fimc_dev中,感兴趣的朋友可以展开这个函数看一下,这里就不再贴出来了。
紧接着这段代码还执行了两个非常关键的过程:
ret= v4l2_device_register(&pdev->dev, &ctrl->v4l2_dev);
if(ret) {
fimc_err("%s: v4l2device register failed\n", __func__);
gotoerr_fimc;
}
这个函数里的核心完成了对v4l2_dev->subdev链表头的初始化,并将ctrl->v4l2_dev关联到pdev->dev结构的私有数据的driver_data成员中(即完成了pdev->dev->p->driver_data= ctrl->v4l2_dev; ),也就是实现了v4l2_dev向内核结构注册的过程。
ret= video_register_device(ctrl->vd, VFL_TYPE_GRABBER, ctrl->id);
if(ret) {
fimc_err("%s: cannotregister video driver\n", __func__);
gotoerr_v4l2;
}
video_set_drvdata(ctrl->vd, ctrl);
ret = device_create_file(&(pdev->dev),&dev_attr_log_level);
上面的过程完成了对video_device 设备的注册,并且在sys 目录下生成了对应的属性文件。如果系统中移植有mdev,将会生成对应设备节点/dev/videoX。
其实到目前为止,只完成了fimc设备主要数据结构的初始化和注册,几乎没有操作fimc或摄像头的硬件寄存器。也没有完成FIMC驱动和摄像头的驱动模块的软件关联。我们是如何做到仅操作fimc的设备节点/dev/videoX就能控制摄像头设备的效果呢?下回分解吧。。。
三星 摄像头 linux,基于Linux 3.0.8 Samsung FIMC(S5PV210) 的摄像头驱动框架解读(一)...相关推荐
- ds18b20驱动程序Linux,基于linux下的ds18b20驱动程序的编写
基于Linux操作系统下面的驱动程序的编写 一.所用的平台: 硬件平台:Mini2440 Size of NAND:256M linux kernel:linux-2.6.32.2 二.编写ds18b ...
- 单机版游戏 linux,[基于linux系统图形单机版农场游戏.ppt
[基于linux系统图形单机版农场游戏 07届毕业设计论文答辩 基于linux系统图形单机版农场游戏 通信xxxx xxxx 指导老师:XX 专业:通信工程 院系:计算机与通信工程 姓名:XXX 学号 ...
- 通信核心网linux,基于linux的双模智能手机实现方案
双模智能手机是能利用CS域和IMS域进行通讯的移动终端设备.在分析采用的三种技术后,再从系统架构.硬件平台.软件平台,再到DMS进行设计,从而完成整个系统的设计,并结合实际使用的实物器件进行具体实现. ...
- ansole终端链接linux,基于Linux系统的智能家居远程控制系统设计论文.doc
基于Linux系统的智能家居远程控制系统设计论文 学科分类号 0801 北京邮电大学毕业论文 题目 (中文):基于Linux系统的智能家居远程控制系统设计 (英文):The smart home re ...
- linux红外遥控进程,46.Linux-分析rc红外遥控平台驱动框架,修改内核的NEC解码函数BUG(1)...
内核版本 : Linux 3.10.14 rc红外接收类型: GPIO 类型的NEC红外编码 本章内容 1)rc体系结构分析 2) 分析红外platform_driver平台驱动框 ...
- linux 远程挂载摄像头_基于Linux的嵌入式网络摄像机设计
本嵌入式网络摄像机采用高性能ARM9芯片微处理器,内置嵌入式Web服务器. 通过嵌入式多任务操作系统采集摄像机视频数据:采集的视频信号数字化后经MJPEG算法压缩,再通过内部总线送到内置的Web服务器 ...
- 串口服务器 linux,基于Linux的串口服务器设计与实现
随着互联网的迅猛发展,在使用计算机进行网络互联的同时,各种家电设备.仪器仪表以及工业生产中的数据采集和控制设备也在逐步地走向网络化,以便共享网络资源.所以,在电子设备日趋网络化的今天,利用串口服务器来 ...
- 文件系统加密 嵌入式Linux,基于Linux的NAND Flash加密文件系统的设计与实现
摘要: NAND Flash以其大容量,低成本,低功耗,抗震荡在非易失存储介质中占据重要地位,已经被广泛应用于消费型电子,航空设备等领域.但是由于NANDFlash的物理特性不同于磁盘存储设备,需要为 ...
- Linux驱动框架之v4l2视频驱动框架解析
1.简介 v4l2是专门为linux设备设计的一套视频框架,其主体框架在linux内核,可以理解为是整个 linux 系统上面的视频源捕获驱动框架.其广泛应用在嵌入式设备以及移动端.个人电脑设备上面, ...
最新文章
- 【亲测可用→防止入坑Routes】设置angular10项目异步加载、惰性加载、懒加载路由
- javascript publish/subscribe or observer pattern
- 小程序登录、用户信息相关接口调整说明
- 春节充电 | 文科生都能看懂的机器学习教程:梯度下降、线性回归、逻辑回归(附动图解释)...
- kafka系列九、kafka事务原理、事务API和使用场景
- 敲代码4年,发现C语言里几个有意思的问题
- 深度强化学习在时序数据压缩中的应用--ICDE 2020收录论文
- 解决编写C#在vscode上面不能输入问题
- Bat脚本处理ftp超强案例解说
- Android平台i2c-tools及16位地址读写,以及not executable: 64-bit ELF file解决办法
- 雪花飘落代码java_个人网站html5雪花飘落代码JS特效下载
- 矩阵关于26的模逆matlab,给定加密矩阵在模运算下的逆矩阵.ppt
- 关于“档案大数据”的非主流看法
- ios, android平台手机游戏,《王者荣耀》ios和安卓怎么转平台 ios和安卓转平台攻略...
- Cadence学习三:如何快速的建立artwork层?
- 东莞爱维EVER总线步进电机驱动器profinet连接实例
- [转]竞价实例与AWS SPOT逆向解析
- 天籁obd接口针脚定义_汽车OBD接口定义
- Python利用tkinter制作桌面翻译小工具
- Windows的故障恢复控制台应用实例详解