Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3167602
  • 博文数量: 685
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 5303
  • 用 户 组: 普通用户
  • 注册时间: 2014-04-19 14:17
个人简介

文章分类

全部博文(685)

文章存档

2015年(116)

2014年(569)

分类: Android平台

2014-09-20 14:29:13

原文地址:http://blog.chinaunix.net/uid-26009923-id-4050452.html

一. V4L2的模块的初始化过程
module_init(videodev_init)
    --> videodev_init
在drivers/media/video/v4l2-dev.c中
  1. static int __init videodev_init(void)
  2. {
  3.     dev_t dev = MKDEV(VIDEO_MAJOR, 0);  //请求设备号VIDEO_MAJOR=81
  4.     register_chrdev_region(dev, VIDEO_NUM_DEVICES, VIDEO_NAME);
  5.     class_register(&video_class);      //注册一个class
  6. }
v4l2这个模块好像没有干什么事.
二. s3c_fimc_driver的初始化过
1.  
module_init(s3c_fimc_register);
    -->s3c_fimc_register
在drivers/media/video/samsung/fimc/s3c_fimc_core.c中
  1. static int s3c_fimc_register(void)
  2. {
  3.     platform_driver_register(&s3c_fimc_driver);
  4. }
platform_driver的结构如下:
  1. static struct platform_driver s3c_fimc_driver = {
  2.     .probe     = s3c_fimc_probe,
  3.     .remove    = s3c_fimc_remove,
  4.     .suspend   = s3c_fimc_suspend,
  5.     .resume    = s3c_fimc_resume,
  6.     .driver    = {
  7.         .name  = "s3c-fimc",
  8.         .owner = THIS_MODULE,
  9.     },
  10. };
先看一下s3cfimc的设备
在arch/arm/plat-samsun/dev-fimc0.c中
  1. static struct s3c_platform_fimc default_fimc0_data __initdata = {
  2.     .srclk_name    = "hclk",
  3.     .clk_name    = "fimc",
  4.     .clockrate    = 133000000,
  5.     .line_length    = 720,
  6.     .nr_frames    = 4,
  7.     .shared_io    = 0,
  8. };
下一步进入probe过程
  1. static int s3c_fimc_probe(struct platform_device *pdev)
  2. {
  3.     struct s3c_platform_fimc *pdata;
  4.     struct s3c_fimc_control *ctrl;
  5.     struct clk *srclk;
  6.     int ret;

  7.     ctrl = s3c_fimc_register_controller(pdev);     //
  8.     
  9.     pdata = to_fimc_plat(&pdev->dev);
  10.     if (pdata->cfg_gpio)
  11.         pdata->cfg_gpio(pdev);                     //gpio的初始化调用s3c_fimc0_cfg_gpio

  12.     srclk = clk_get(&pdev->dev, pdata->srclk_name);      //获取srclk_name
  13.     ctrl->clock = clk_get(&pdev->dev, pdata->clk_name);  //获取fimc_clk并enable
  14.     clk_enable(ctrl->clock);  
  15.     //向v4l2注册TYPE_GRABBER即图像采集设备  
  16.     ret = video_register_device(ctrl->vd, VFL_TYPE_GRABBER, ctrl->id);    //VFL_TYPE_GRABBER 图像采集设备
  17.     return 0;
  18. }
简要说明一下
module_init(s3c_fimc_register);
    -->s3c_fimc_register
        --> s3c_fimc_probe
            --> s3c_fimc_register_controller
  1. struct s3c_fimc_control *s3c_fimc_register_controller(struct platform_device *pdev)
  2. {
  3.     struct s3c_platform_fimc *pdata;
  4.     struct s3c_fimc_control *ctrl;
  5.     int id = pdev->id;
  6.     pdata = to_fimc_plat(&pdev->dev);
  7.     ctrl = &s3c_fimc.ctrl[id];
  8.     ctrl->id = id;
  9.     ctrl->dev = &pdev->dev;
  10.     ctrl->vd = &s3c_fimc_video_device[id]; //这句代码很关键 

  11.     return ctrl;
  12. }
看一下s3c_fimc_video_deivce的定义就知道了:
  1. struct video_device s3c_fimc_video_device[S3C_FIMC_MAX_CTRLS] = {
  2.     [0] = {
  3.         .vfl_type = VID_TYPE_OVERLAY | VID_TYPE_CAPTURE | VID_TYPE_CLIPPING | VID_TYPE_SCALES,
  4.         .fops = &s3c_fimc_fops,
  5.         .ioctl_ops = &s3c_fimc_v4l2_ops,
  6.         .release = s3c_fimc_vdev_release,
  7.         .name = "sc3_video0",
  8.     },
  9. };
这实际上是注册了一两套函数指针,s3c_fimc_fops与s3c_fimc_v4l2_ops

以后就可以通过vl42_fops来操作fimc设备了
  1. static const struct file_operations v4l2_fops = {
  2.     .owner = THIS_MODULE,
  3.     .read = v4l2_read,
  4.     .write = v4l2_write,
  5.     .open = v4l2_open,
  6.     .get_unmapped_area = v4l2_get_unmapped_area,
  7.     .mmap = v4l2_mmap,
  8.     .unlocked_ioctl = v4l2_ioctl,
  9. #ifdef CONFIG_COMPAT
  10.     .compat_ioctl = v4l2_compat_ioctl32,
  11. #endif
  12.     .release = v4l2_release,
  13.     .poll = v4l2_poll,
  14.     .llseek = no_llseek,
  15. };


三. ov965x_i2c_driver的初始化过程

1. 硬件的定义
ov9650的硬件定义在arch/arm/mach-s3c64xx/mach-smdk6410.c中
  1. 在arch/arm/mach-s3c64xx/mach-smdk6410.c中
  2. static struct i2c_board_info i2c_devs0[] __initdata = {
  3.     { I2C_BOARD_INFO("ov965x", 0x30), }, // gjl
  4. };
  5. //这个定义会被smdk6410_machine_init调用,添加到I2C的链表中
  6. 在arch/arm/mach-s3c64xx/mach-smdk6410.c中
  7. static void __init smdk6410_machine_init(void)
  8. {
  9.     i2c_register_board_info(0, i2c_devs0, ARRAY_SIZE(i2c_devs0));
  10. }
2. ov9650驱动初始化
ov9650的驱动在drivers/media/video/samsung/fimc/ov965x.c中
module_init(ov965x_init)
    --> ov965x_init   >> i2c_add_driver
  1. static __init int ov965x_init(void)
  2. {
  3.     return i2c_add_driver(&ov965x_i2c_driver);
  4. }
这个i2c_add_driver也是调用了driver_register,最终会调用ov965x的probe函数
  1. 在include/linux/i2c.h中
  2. static inline int i2c_add_driver(struct i2c_driver *driver)
  3. {
  4.     return i2c_register_driver(THIS_MODULE, driver);
  5. }
  6. //driver_register中会调用具体函数的probe
  7. 在drivers/i2c/i2c-core.c中
  8. int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
  9. {
  10.     res = driver_register(&driver->driver);
  11.     if (res)
  12.         return res;
  13. }
3. ov9650的probe函数
module_init(ov965x_init)
    --> ov965x_init   >> i2c_add_driver
        --> ov965x_probe
drivers/media/video/samsung/fimc/ov965x.c
  1. static int ov965x_probe(struct i2c_client *c, const struct i2c_device_id *id)
  2. {
  3.     int i;
  4.     int ret;
  5.     ov965x_data.client = c;
  6.     s3c_fimc_register_camera(&ov965x_data);     //3.1向s3c-fimc注册camera
  7.     for (= 0; i < OV965X_INIT_REGS; i++) {     //通过I2C向ov965x中的寄存器写入配置信息
  8.         ret = i2c_smbus_write_byte_data(c, OV965X_init_reg[i].subaddr, OV965X_init_reg[i].value);
  9.     } //在.config 中定义了CONFIG_OV965X_QVGA=y,所以这儿是QVGA(320*240)模式
  10.     return 0;
  11. }

3.1 s3c_fimc_driver注册camera
先看一下s3c_fimc_camera是个什么东东
  1. static struct s3c_fimc_camera ov965x_data = {
  2.     .id         = CONFIG_VIDEO_FIMC_CAM_CH,
  3.     .type        = CAM_TYPE_ITU,
  4.     .mode        = ITU_601_YCBCR422_8BIT,
  5.     .order422    = CAM_ORDER422_8BIT_YCBYCR,//YCRYCB,
  6.     .clockrate    = 24000000,
  7.     .width        = 640,//800,
  8.     .height        = 480,//600,
  9.     .offset        = {
  10.         .h1 = 0,
  11.         .h2 = 0,
  12.         .v1 = 0,
  13.         .v2 = 0,
  14.     },
  15.     .polarity    = {
  16.         .pclk    = 0,
  17.         .vsync    = 1,
  18.         .href    = 0,
  19.         .hsync    = 0,
  20.     },
  21.     .initialized    = 0,
  22. };

module_init(ov965x_init)
    --> ov965x_init   >> i2c_add_driver
        --> ov965x_probe
                --> s3c_fimc_register_camera >>>s3c_fimc_register_camera(&ov965x_data);
在drivers/media/video/samsung/fimc/s3c_fimc_core.c中
  1. void s3c_fimc_register_camera(struct s3c_fimc_camera *cam)
  2. {
  3.     int i;
  4.     static int once=0;
  5.     //通过索引全局数组s3c_fimc_camer就可以找到ov9650的控制指针
  6.     s3c_fimc.camera[cam->id] = cam;  

  7.     for (= 0; i < S3C_FIMC_MAX_CTRLS; i++) {
  8.         s3c_fimc.ctrl[i].in_cam = s3c_fimc.camera[cam->id];
  9.     }
  10.     
  11.     //设置摄像头的clock: disable, set, enable
  12.     clk_disable(s3c_fimc.cam_clock);
  13.     clk_set_rate(s3c_fimc.cam_clock, cam->clockrate);
  14.     clk_enable(s3c_fimc.cam_clock);
  1.     s3c_fimc_reset_camera();                     //3.1.1复位后,设置的clock就起作用了
  2.     if((once==0)&&(cam->client!=0))
  3.     {
  4.         for (= 0; i < S3C_FIMC_MAX_CTRLS; i++) 
  5.             s3c_fimc_init_camera(&s3c_fimc.ctrl[i]);  //3.1.2复位后,设置的clock就起作用了       
  6.         once = 1;
  7.     }
  8. }
3.1.1 复位camera
module_init(ov965x_init)
    --> ov965x_init   >> i2c_add_driver
        --> ov965x_probe
                --> s3c_fimc_register_camera >>>s3c_fimc_register_camera(&ov965x_data);
                    --> s3c_fimc_reset_camera
  1. void s3c_fimc_reset_camera(void)
  2. {
  3.     //对寄存器一个ioremap然后直接使用
  4.     void __iomem *regs = ioremap(S3C64XX_PA_FIMC, SZ_4K);
  5.     u32 cfg = readl(regs + S3C_CIGCTRL);
  6.     cfg |= S3C_CIGCTRL_CAMRST;
  7.     writel(cfg, regs + S3C_CIGCTRL);
  8.     mdelay(200);

  9.     cfg = readl(regs + S3C_CIGCTRL);
  10.     cfg &= ~S3C_CIGCTRL_CAMRST;
  11.     writel(cfg, regs + S3C_CIGCTRL);
  12.     udelay(2000);
  13.     //使用完后iounmap
  14.     iounmap(regs);
  15. }

3.1.2 将初始化标志设为1
module_init(ov965x_init)
    --> ov965x_init   >> i2c_add_driver
        --> ov965x_probe
                --> s3c_fimc_register_camera >>>s3c_fimc_register_camera(&ov965x_data);
                    --> s3c_fimc_init_camera   
  1. void s3c_fimc_init_camera(struct s3c_fimc_control *ctrl)
  2. {
  3.     struct s3c_fimc_camera *cam = ctrl->in_cam;
  4.     if (cam && cam->id != S3C_FIMC_TPID && !cam->initialized) {
  5.         cam->initialized = 1;
  6.     }
  7. }
4. 总结
初始化完成了,总结一下
    ov9650 --> s3c_fimc_core注册
           s3c_fimc_core --> v4l2注册
这样上层就可以调用v4l2-->s3c_fimc_core来间接的控制ov9650了.

附录
一. camera 的clk设置
1. 获取clk的结构体
在drivers/media/video/samsung/fimc/s3c_fimc_core.c中
s3c_fimc_probe
--> s3c_fimc_init_global
  1. static int s3c_fimc_init_global(struct platform_device *pdev)
  2. {
  3.     s3c_fimc.cam_clock = clk_get(&pdev->dev, "sclk_cam");
  4. }
其中sclk_cam是定义在arch/arm/mach-s3c64xx/clock.c中
  1. static struct clk init_clocks[] =
  2. {
  3.     .name = "sclk_cam",
  4.     .id = -1,
  5.     .parent = &clk_h2,
  6.     .enable = s3c64xx_sclk_ctrl,
  7.     .ctrlbit = S3C_CLKCON_SCLK_CAM,
  8.     .ops = &(struct clk_ops) {
  9.         .set_rate = s3c64xx_setrate_sclk_cam,
  10.     }
  11. }
2.clk的初始化
module_init(ov965x_init)
    --> ov965x_init   >> i2c_add_driver
        --> ov965x_probe
                --> s3c_fimc_register_camera
在drivers/media/video/samsung/fimc/s3c_fimc_core.c中
  1. void s3c_fimc_register_camera(struct s3c_fimc_camera *cam)
  2. {
  3.     //设置摄像头的clock频率为24M
  4.     clk_disable(s3c_fimc.cam_clock);
  5.     //cam->clockrate是ov965x_data中设定的数值
  6.     clk_set_rate(s3c_fimc.cam_clock, cam->clockrate);   //cam->clockrate=24000000=24M
  7.     clk_enable(s3c_fimc.cam_clock);
  8. }
2.1 clk_disable
module_init(ov965x_init)
    --> ov965x_init   >> i2c_add_driver
        --> ov965x_probe
                --> s3c_fimc_register_camera
                        --> clock_disable
在arch/arm/plat-samsung/clock.c中
  1. void clk_disable(struct clk *clk)
  2. {
  3.     spin_lock(&clocks_lock);
  4.     (clk->enable)(clk, 0);   //这个enabel也就是调用"sclk_cam"的enable
  5.     spin_unlock(&clocks_lock);
  6.     clk_disable(clk->parent);
  7. }
module_init(ov965x_init)
    --> ov965x_init   >> i2c_add_driver
        --> ov965x_probe
                --> s3c_fimc_register_camera
                        --> clock_disable
  1. int s3c64xx_sclk_ctrl(struct clk *clk, int enable)
  2. {
  3.     //enable=0或1控制打开或关闭
  4.     return s3c64xx_gate(S3C_SCLK_GATE, clk, enable);   
  5. }
2.2 clk_set_rate
module_init(ov965x_init)
    --> ov965x_init   >> i2c_add_driver
        --> ov965x_probe
                --> s3c_fimc_register_camera
                        -->clk_set_rate
在arch/arm/plat-samsung/clock.c中
  1. int clk_set_rate(struct clk *clk, unsigned long rate)
  2. {
  3.     spin_lock(&clocks_lock);
  4.     //这个clk_set_rate也就是调用"sclk_cam"clk_set_rate
  5.     ret = (clk->ops->set_rate)(clk, rate); 
  6.     spin_unlock(&clocks_lock);
  7. }
2.2.1设置camra的clk是24M
module_init(ov965x_init)
    --> ov965x_init   >> i2c_add_driver
        --> ov965x_probe
                --> s3c_fimc_register_camera
                        -->clk_set_rate
                            --> s3c64xx_setrate_sclk_cam
在arch/arm/mach-s3c64xx/clock.c中
  1. static int s3c64xx_setrate_sclk_cam(struct clk *clk, unsigned long rate)
  2. {
  3.     u32 shift = 20;
  4.     u32 cam_div, cfg;
  5.     unsigned long src_clk = clk_get_rate(clk->parent); //parent=266M

  6.     cam_div = src_clk / rate;   //cam_div=266M/24M=11
  7.     // 手册P147 CLK_DIV0  0x7E00_F020 
        // CAM_RATIO [23:20] --> CAM clock divider ratio 
        // CLKCAM = HCLKX2 / (CAM_RATIO + 1)   
  8.     cfg = __raw_readl(S3C_CLK_DIV0);
  9.     cfg &= ~(0xf << shift);
  10.     cfg |= ((cam_div - 1) << shift);
  11.     __raw_writel(cfg, S3C_CLK_DIV0);
  12.     return 0;
  13. }

阅读(1044) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~