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

 

通过刚才的分析,可以总结出下面的源码结构图:


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

接下来,先来看看FIMC设备的注册过程。以FIMC-0为例,说说/dev/video0 这个设备文件是怎么出来的。

先看几个关键结构:

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

  1. static struct s3c_platform_fimc  fimc_plat_lsi = {  
  2.     .srclk_name = "mout_mpll",  
  3.     .clk_name   = "sclk_fimc",  
  4.     .lclk_name  = "fimc",  
  5.     .clk_rate   = 166750000,  
  6. #if defined(CONFIG_VIDEO_S5K4EA)  
  7.     .default_cam    = CAMERA_CSI_C,  
  8. #else  
  9. #ifdef CAM_ITU_CH_A  
  10.     .default_cam    = CAMERA_PAR_A,  
  11. #else  
  12.     .default_cam    = CAMERA_PAR_B,  
  13. #endif  
  14. #endif  
  15.     .camera     = {  
  16. #ifdef CONFIG_VIDEO_S5K4ECGX  
  17.             &s5k4ecgx,  
  18. #endif  
  19. #ifdef CONFIG_VIDEO_S5KA3DFX  
  20.             &s5ka3dfx,  
  21. #endif  
  22. #ifdef CONFIG_VIDEO_S5K4BA  
  23.             &s5k4ba,  
  24. #endif  
  25. #ifdef CONFIG_VIDEO_S5K4EA  
  26.             &s5k4ea,  
  27. #endif  
  28. #ifdef CONFIG_VIDEO_TVP5150  
  29.             &tvp5150,  
  30. #endif  
  31. #ifdef CONFIG_VIDEO_OV9650  
  32.             &ov9650,  
  33. #endif  
  34.     },  
  35.     .hw_ver     = 0x43,  
  36. };  

可以看到在s3c_platform_fimc中有一个camera成员。这里重点看一下ov9650.展开ov9650


  1. static struct s3c_platform_camera ov9650 = {  
  2.     #ifdef CAM_ITU_CH_A  
  3.     .id     = CAMERA_PAR_A,  
  4.     #else  
  5.     .id     = CAMERA_PAR_B,  
  6.     #endif  
  7.     .type       = CAM_TYPE_ITU,  
  8.     .fmt        = ITU_601_YCBCR422_8BIT,  
  9.     .order422   = CAM_ORDER422_8BIT_YCBYCR,  
  10.     .i2c_busnum = 0,  
  11.     .info       = &ov9650_i2c_info,  
  12.     .pixelformat    = V4L2_PIX_FMT_YUYV,  
  13.     .srclk_name = "mout_mpll",  
  14.     /* .srclk_name  = "xusbxti", */  
  15.     .clk_name   = "sclk_cam1",  
  16.     .clk_rate   = 40000000,  
  17.     .line_length    = 1920,  
  18.     .width      = 1280,  
  19.     .height     = 1024,  
  20.     .window     = {  
  21.         .left   = 0,  
  22.         .top    = 0,  
  23.         .width  = 1280,  
  24.         .height = 1024,  
  25.     },  
  26.   
  27.     /* Polarity */  
  28.     .inv_pclk   = 1,  
  29.     .inv_vsync  = 1,  
  30.     .inv_href   = 0,  
  31.     .inv_hsync  = 0,  
  32.   
  33.     .initialized    = 0,  
  34.     .cam_power  = ov9650_power_en,  
  35. };  

这个结构体,实现了对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


  1. s3c_fimc0_set_platdata(&fimc_plat_lsi);  
  2. s3c_fimc1_set_platdata(&fimc_plat_lsi);  
  3. s3c_fimc2_set_platdata(&fimc_plat_lsi);  

以s3c_fimc0_set_platdata为例展开


  1. void __init s3c_fimc0_set_platdata(struct s3c_platform_fimc *pd)  
  2. {  
  3.     struct s3c_platform_fimc *npd;  
  4.   
  5.     if (!pd)  
  6.         pd = &default_fimc0_data;  
  7.   
  8.     npd = kmemdup(pd, sizeof(struct s3c_platform_fimc), GFP_KERNEL);  
  9.     if (!npd)  
  10.         printk(KERN_ERR "%s: no memory for platform data\n", __func__);  
  11.     else {  
  12.         if (!npd->cfg_gpio)  
  13.             npd->cfg_gpio = s3c_fimc0_cfg_gpio;  
  14.   
  15.         if (!npd->clk_on)  
  16.             npd->clk_on = s3c_fimc_clk_on;  
  17.   
  18.         if (!npd->clk_off)  
  19.             npd->clk_off = s3c_fimc_clk_off;  
  20.   
  21.         npd->hw_ver = 0x45;  
  22.   
  23.         /* starting physical address of memory region */  
  24.         npd->pmem_start = s5p_get_media_memory_bank(S5P_MDEV_FIMC0, 1);  
  25.         /* size of memory region */  
  26.         npd->pmem_size = s5p_get_media_memsize_bank(S5P_MDEV_FIMC0, 1);  
  27.   
  28.         s3c_device_fimc0.dev.platform_data = npd;  
  29.     }  
  30. }  

最后一句是关键 s3c_device_fimc0.dev.platform_data = npd; 

看一下s3c_device_fimc0定义:


  1. struct platform_device s3c_device_fimc0 = {  
  2.     .name       = "s3c-fimc",  
  3.     .id     = 0,  
  4.     .num_resources  = ARRAY_SIZE(s3c_fimc0_resource),  
  5.     .resource   = s3c_fimc0_resource,  
  6. };  

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 的注册:


  1. static struct platform_device *mini210_devices[] __initdata = {  
  2.     &s3c_device_adc,  
  3.     &s3c_device_cfcon,  
  4.     &s3c_device_nand,  
  5.     。。。  
  6.     &s3c_device_fb,  
  7.     &mini210_lcd_dev,  
  8. #ifdef CONFIG_VIDEO_FIMC  
  9.     &s3c_device_fimc0,  
  10.     &s3c_device_fimc1,  
  11.     &s3c_device_fimc2,  
  12. }  

[html]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. 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)  函数上场了。


  1. static int __devinit fimc_probe(struct platform_device *pdev)  
  2. {  
  3.     struct s3c_platform_fimc *pdata;  
  4.     struct fimc_control *ctrl;  
  5.     struct clk *srclk;  
  6.     int ret;  
  7.     if (!fimc_dev) {  
  8.         fimc_dev = kzalloc(sizeof(*fimc_dev), GFP_KERNEL);  
  9.         if (!fimc_dev) {  
  10.             dev_err(&pdev->dev, "%s: not enough memory\n",  
  11.                 __func__);  
  12.             return -ENOMEM;  
  13.         }  
  14.     }  
  15.   
  16.     ctrl = fimc_register_controller(pdev);  
  17.     if (!ctrl) {  
  18.         printk(KERN_ERR "%s: cannot register fimc\n", __func__);  
  19.         goto err_alloc;  
  20.     }  
  21.   
  22.     pdata = to_fimc_plat(&pdev->dev);  
  23.     if (pdata->cfg_gpio)  
  24.         pdata->cfg_gpio(pdev);  
  25.   
  26. #ifdef REGULATOR_FIMC  
  27.     /* Get fimc power domain regulator */  
  28.     ctrl->regulator = regulator_get(&pdev->dev, "pd");  
  29.     if (IS_ERR(ctrl->regulator)) {  
  30.         fimc_err("%s: failed to get resource %s\n",  
  31.                 __func__, "s3c-fimc");  
  32.         return PTR_ERR(ctrl->regulator);  
  33.     }  
  34. #endif //REGULATOR_FIMC  
  35.     /* fimc source clock */  
  36.     srclk = clk_get(&pdev->dev, pdata->srclk_name);  
  37.     if (IS_ERR(srclk)) {  
  38.         fimc_err("%s: failed to get source clock of fimc\n",  
  39.                 __func__);  
  40.         goto err_v4l2;  
  41.     }  
  42.   
  43.     /* fimc clock */  
  44.     ctrl->clk = clk_get(&pdev->dev, pdata->clk_name);  
  45.     if (IS_ERR(ctrl->clk)) {  
  46.         fimc_err("%s: failed to get fimc clock source\n",  
  47.             __func__);  
  48.         goto err_v4l2;  
  49.     }  
  50.   
  51.     /* set parent for mclk */  
  52.     clk_set_parent(ctrl->clk, srclk);  
  53.   
  54.     /* set rate for mclk */  
  55.     clk_set_rate(ctrl->clk, pdata->clk_rate);  
  56.   
  57.     /* V4L2 device-subdev registration */  
  58.     ret = v4l2_device_register(&pdev->dev, &ctrl->v4l2_dev);  
  59.     if (ret) {  
  60.         fimc_err("%s: v4l2 device register failed\n", __func__);  
  61.         goto err_fimc;  
  62.     }  
  63.   
  64.     /* things to initialize once */  
  65.     if (!fimc_dev->initialized) {  
  66.         ret = fimc_init_global(pdev);  
  67.         if (ret)  
  68.             goto err_v4l2;  
  69.     }  
  70.   
  71.     /* video device register */  
  72.     ret = video_register_device(ctrl->vd, VFL_TYPE_GRABBER, ctrl->id);  
  73.     if (ret) {  
  74.         fimc_err("%s: cannot register video driver\n", __func__);  
  75.         goto err_v4l2;  
  76.     }  
  77.   
  78.     video_set_drvdata(ctrl->vd, ctrl);  
  79.   
  80.     ret = device_create_file(&(pdev->dev), &dev_attr_log_level);  
  81.     if (ret < 0) {  
  82.         fimc_err("failed to add sysfs entries\n");  
  83.         goto err_global;  
  84.     }  
  85.     printk(KERN_INFO "FIMC%d registered successfully\n", ctrl->id);  
  86.   
  87.     return 0;  
  88.   
  89. err_global:  
  90.     video_unregister_device(ctrl->vd);  
  91.   
  92. err_v4l2:  
  93.     v4l2_device_unregister(&ctrl->v4l2_dev);  
  94.   
  95. err_fimc:  
  96.     fimc_unregister_controller(pdev);  
  97.   
  98. err_alloc:  
  99.     kfree(fimc_dev);  
  100.     return -EINVAL;  
  101.   
  102. }  

在fimc_probe函数中有这么一段

  1. if(!fimc_dev->initialized) {  
  2.                    ret = fimc_init_global(pdev);  
  3.                    if (ret)  
  4.                             goto err_v4l2;  
  5.          }  


这段代码执行过程:首先判断fimc是否已经被初始化完成(此时FIMC是忙状态的),如果没有被初始化,则执行fimc_init_global(pdev);函数,它的作用是先判断平台数据中是否初始化了摄像头结构(即前面提到的.camera成员),从平台数据中获得摄像头的时钟频率并将平台数据中内嵌的s3c_platform_camera结构数据保存到该驱动模块全局的fimc_dev中,感兴趣的朋友可以展开这个函数看一下,这里就不再贴出来了。

 

紧接着这段代码还执行了两个非常关键的过程:

  1. ret= v4l2_device_register(&pdev->dev, &ctrl->v4l2_dev);  
  2.          if (ret) {  
  3.                    fimc_err("%s: v4l2device register failed\n", __func__);  
  4.                    goto err_fimc;  
  5.          }  


这个函数里的核心完成了对v4l2_dev->subdev链表头的初始化,并将ctrl->v4l2_dev关联到pdev->dev结构的私有数据的driver_data成员中(即完成了pdev->dev->p->driver_data= ctrl->v4l2_dev; ),也就是实现了v4l2_dev向内核结构注册的过程。

 

  1. ret= video_register_device(ctrl->vd, VFL_TYPE_GRABBER, ctrl->id);  
  2.          if (ret) {  
  3.                    fimc_err("%s: cannotregister video driver\n", __func__);  
  4.                    goto err_v4l2;  
  5.          }  
  6.    
  7.          video_set_drvdata(ctrl->vd, ctrl);  
  8.    
  9.          ret = device_create_file(&(pdev->dev),&dev_attr_log_level);  


上面的过程完成了对video_device 设备的注册,并且在sys 目录下生成了对应的属性文件。如果系统中移植有mdev,将会生成对应设备节点/dev/videoX。

 

其实到目前为止,只完成了fimc设备主要数据结构的初始化和注册,几乎没有操作fimc或摄像头的硬件寄存器。也没有完成FIMC驱动和摄像头的驱动模块的软件关联。我们是如何做到仅操作fimc的设备节点/dev/videoX就能控制摄像头设备的效果呢?下回分解吧。。。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值