作者:咕唧咕唧liukun321

来自:http://blog.csdn.net/liukun321

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.c  Fimc硬件相关操作,基本寄存器配置等。这个文件提供函数接口供fimc_capture.c、fimc_output.c、fimc_overlay.c调用。

通过刚才的分析。能够总结出以下的源代码结构图:

好了,框架有了。再来看源代码就轻松多了

接下来,先来看看FIMC设备的注冊过程。

以FIMC-0为例。说说/dev/video0 这个设备文件是怎么出来的。

先看几个关键结构:

首先是 s3c_platform_fimcfimc_plat_lsi;也就是抽象fimc模块的数据结构,fimc_plat_lsi还包括了一个.camera成员。该结构初始化例如以下

static struct s3c_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

static struct s3c_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(struct s3c_platform_fimc *pd)
{
struct s3c_platform_fimc *npd; if (!pd)
pd = &default_fimc0_data; npd = kmemdup(pd, sizeof(struct s3c_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定义:

struct platform_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 的注冊:

static struct platform_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) 函数上场了。

static int __devinit fimc_probe(struct platform_device *pdev)
{
struct s3c_platform_fimc *pdata;
struct fimc_control *ctrl;
struct clk *srclk;
int ret;
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__);
goto err_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");
return PTR_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__);
goto err_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__);
goto err_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__);
goto err_fimc;
} /* things to initialize once */
if (!fimc_dev->initialized) {
ret = fimc_init_global(pdev);
if (ret)
goto err_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__);
goto err_v4l2;
} video_set_drvdata(ctrl->vd, ctrl); ret = device_create_file(&(pdev->dev), &dev_attr_log_level);
if (ret < 0) {
fimc_err("failed to add sysfs entries\n");
goto err_global;
}
printk(KERN_INFO "FIMC%d registered successfully\n", ctrl->id); return 0; 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)
goto err_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__);
goto err_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__);
goto err_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 3.0.8 Samsung FIMC(S5PV210) 的摄像头驱动框架解读(一)的更多相关文章

  1. nodejs实战:使用原生nodeJs模块实现静态文件及REST请求解析及响应(基于nodejs6.2.0版本,不使用express等webMVC框架 )

    一.准备工作 1.安装nodejs 首先你需要安装nodeJs 那么nodejs官网:http://nodejs.cn/,下载相应版本,一步一步安装. 二.使用nodejs开发服务器后台应用 1.创建 ...

  2. Linux设备驱动框架设计

    引子 Linux操作系统的一大优势就是支持数以万计的芯片设备,大大小小的芯片厂商工程师都在积极地向Linux kernel提交设备驱动代码.能让这个目标得以实现,这背后隐藏着一个看不见的技术优势:Li ...

  3. (转)S5pv210 HDMI 接口在 Linux 3.0.8 驱动框架解析 (By liukun321 咕唧咕唧)

    作者:liukun321 咕唧咕唧 日期:2014.1.18 转载请标明作者.出处:http://blog.csdn.net/liukun321/article/details/18452663 本文 ...

  4. S5pv210 HDMI 接口在 Linux 3.0.8 驱动框架解析

    作者:liukun321 咕唧咕唧 日期:2014.1.18 转载请标明作者.出处:http://blog.csdn.net/liukun321/article/details/18452663 本文 ...

  5. 《Linux设备驱动开发具体解释(第3版)》(即《Linux设备驱动开发具体解释:基于最新的Linux 4.0内核》)网购链接

    <Linux设备驱动开发具体解释:基于最新的Linux 4.0内核> china-pub   spm=a1z10.3-b.w4011-10017777404.30.kvceXB&i ...

  6. Kali Linux 2.0配置并安装常用的工具

    Kali Linux 前身是著名渗透测试系统BackTrack ,是一个基于 Debian 的 Linux 发行版,包含很多安全和取证方面的相关工具. Kali Linux 2.0是基于Debian ...

  7. 基于Linux的智能家居的设计(3)

    2  硬件设计 本课题的硬件设计包含主控制器.传输数据设计.数据採集设计.控制驱动设计.显示设计.门禁设计. 2.1  主控制器 依据方案三选择S3C6410主控芯片,S3C6410是由Samsung ...

  8. 基于Linux平台的libpcap源码分析和优化

    目录 1..... libpcap简介... 1 2..... libpcap捕包过程... 2 2.1        数据包基本捕包流程... 2 2.2        libpcap捕包过程... ...

  9. 轻易实现基于linux或win运行的聊天服务端程序

    对于不了解网络编程的开发人员来说,编写一个良好的服务端通讯程序是一件比较麻烦的事情.然而通过EC这个免费组件你可以非常简单地构建一个基于linux或win部署运行的网络服务程序.这种便利性完全得益于m ...

随机推荐

  1. eclipse启动tomcat 访问http://localhost:8080 报404错误

    eclipse启动tomcat 访问http://localhost:8080 报404错误 Server Locations修改后会变灰,如果需要更改设置,则需要移除与Tomcat服务器关联的项目, ...

  2. 让C/C++程序一次编译可以发布到多版本Linux之上

    最近页游开放平台比较多, 每个平台要求的Linux版本各不相同, 这给开发人员部署服务器带来了很大的困难. 在本机Linux编译的程序,发布时即便将依赖的so附带到目标Linux环境,仍然会碰到依赖及 ...

  3. SQL SERVER 2005 获取表的所有索引信息以及删除和新建语句

    BEGIN        WITH tx AS        (                SELECT a.object_id                      ,b.name AS s ...

  4. 9款超酷的jQuery/CSS3插件

    Article From here: http://js.itivy.com/?p=1883 1.jQuery向前滑动切换焦点图 这款jQuery焦点图非常绚丽,切换图片的时候每张图片是向前滑动的,很 ...

  5. Java实现二叉搜索树的添加,前序、后序、中序及层序遍历,求树的节点数,求树的最大值、最小值,查找等操作

    什么也不说了,直接上代码. 首先是节点类,大家都懂得 /** * 二叉树的节点类 * * @author HeYufan * * @param <T> */ class Node<T ...

  6. [Leetcode][Python]51: N-Queens

    # -*- coding: utf8 -*-'''__author__ = 'dabay.wang@gmail.com' 51: N-Queenshttps://oj.leetcode.com/pro ...

  7. linux之SQL语句简明教程

    本教程参考http://www.1keydata.com/cn/sql/ 目的是让初学者了解linux下Mysql的操作,但是我仍想侧重于SQL语句的讲解 sql语句的学习将按照下图的流程: 当然在这 ...

  8. oracle记录各登陆主机用户名,登陆ip,所执行的命令

    oracle记录各登陆主机用户名,登陆ip,所执行的命令 /etc/profile #history USER_IP=`>/dev/null| awk '{print $NF}'|sed -e ...

  9. JS属性

    1.类型分析: js中的数据类型有undefined,boolean,number,string,object等5种,前4种为原始类型,第5种为引用类型. 代码: var a1;var a2 = tr ...

  10. Android EditText限制输入一些固定字符的属性

    android:digits="abcdefghijklmnopqrstuvwxyz1234567890" 仅仅能输入这些