读Linux源码分析ov5640在三星exynos4412平台上的使用
来源:互联网 发布:香港代购mac口红多少钱 编辑:程序博客网 时间:2024/04/30 22:23
ov5640设备的抽象结构体在mach-itop4412.c文件中
static struct s3c_platform_camera ov5640 = { .id = CAMERA_PAR_A, .clk_name = "sclk_cam0", .i2c_busnum = 7, .cam_power = smdk4x12_cam1_reset, .type = CAM_TYPE_ITU, .fmt = ITU_601_YCBCR422_8BIT, .order422 = CAM_ORDER422_8BIT_CBYCRY, .info = &ov5640_i2c_info, .pixelformat = V4L2_PIX_FMT_UYVY, //modify by cym V4L2_PIX_FMT_UYVY, .srclk_name = "xusbxti", .clk_rate = 24000000, .line_length = 1920, .width = 640, .height = 480, .window = { .left = 0, .top = 0, .width = 640, .height = 480, }, /* Polarity */ .inv_pclk = 0, .inv_vsync = 1, .inv_href = 0, .inv_hsync = 0, .reset_camera = 1, .initialized = 0, .layout_rotate = 0 //for shuping, //180, };
以上结构体注册在fimc_plat
结构体中
static struct s3c_platform_fimc fimc_plat = {#ifdef CONFIG_ITU_A .default_cam = CAMERA_PAR_A,#endif#ifdef CONFIG_ITU_B .default_cam = CAMERA_PAR_B,#endif#ifdef CONFIG_CSI_C .default_cam = CAMERA_CSI_C,#endif#ifdef CONFIG_CSI_D .default_cam = CAMERA_CSI_D,#endif#ifdef WRITEBACK_ENABLED .default_cam = CAMERA_WB,#endif .camera = {#if 0 //added yqf, adjust for middleware request#ifdef CONFIG_VIDEO_S5K4ECGX &s5k4ecgx,#endif#else//for S5K4EC back#ifdef CONFIG_VIDEO_S5K4ECGX &s5k4ecgx,#endif#ifdef CONFIG_VIDEO_OV5640 &ov5640,#endif/* add by cym 20140915 */#ifdef CONFIG_VIDEO_TVP5150 &tvp5150,#endif/* end add *///front/* add by cym 20121114 */#ifdef CONFIG_VIDEO_SR200PC20 &sr200pc20,#endif/* end add */#endif#ifdef WRITEBACK_ENABLED &writeback,#endif }, .hw_ver = 0x51,//硬件版本号};
可以看到ov5640是作为fimc_plat结构体的camera成员的一个成员。fimc_plat对应的结构体为s3c_platform_fimc如下:
struct s3c_platform_fimc { enum fimc_cam_index default_cam; /* index of default cam */#ifdef CONFIG_ARCH_EXYNOS4 struct s3c_platform_camera *camera[7]; /* FIXME */#else struct s3c_platform_camera *camera[5]; /* FIXME */#endif int hw_ver;//硬件版本号 bool use_cam;//这个没看懂是干什么的,在s3c_fimc0_set_platdata函数里面初始化为true void (*cfg_gpio)(struct platform_device *pdev);//相机GPIO的配置函数,在s3c_fimc0_set_platdata函数里面被赋值为s3c_fimc0_cfg_gpio int (*clk_on)(struct platform_device *pdev, struct clk **clk);//时钟开启,在s3c_fimc0_set_platdata函数里面被初始化为s3c_fimc_clk_on int (*clk_off)(struct platform_device *pdev, struct clk **clk);//时钟开启,在s3c_fimc0_set_platdata函数里面被初始化为s3c_fimc_clk_off};
在s3c_fimc0_set_platdata
函数中有个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,};
看到platform_device这个结构体,应该就知道这是平台设备,是最后和设备驱动进行匹配用的。它的name
字段被初始化为”s3c-fimc”,之后会和设备驱动的名字进行匹配。在这个结构体中还有个s3c_fimc0_resource结构:
static struct resource s3c_fimc0_resource[] = { [0] = { .start = S5P_PA_FIMC0, .end = S5P_PA_FIMC0 + SZ_4K - 1, .flags = IORESOURCE_MEM, }, [1] = { .start = IRQ_FIMC0, .end = IRQ_FIMC0, .flags = IORESOURCE_IRQ, },};
这个结构体描述了fimc0这个控制器的物理起始地址,占用范围和对应的IRQ资源。
在s3c_fimc0_set_platdata
函数中最后把fimc_plat的副本npd放在s3c_device_fimc0的dev.platform_data中。npd对应的s3c_platform_fimc结构,中的函数指针所指向的原形函数在setup_fimc0.c文件中定义。
驱动程序是在fimc_dev.c文件中,直接看平台驱动结构的初始化:
static struct platform_driver fimc_driver = { .probe = fimc_probe, .remove = fimc_remove,#if (!defined(CONFIG_EXYNOS_DEV_PD) || !defined(CONFIG_PM_RUNTIME)) .suspend = fimc_suspend, .resume = fimc_resume,#endif .driver = { .name = FIMC_NAME, .owner = THIS_MODULE,#if (defined(CONFIG_EXYNOS_DEV_PD) && defined(CONFIG_PM_RUNTIME)) .pm = &fimc_pm_ops,#else .pm = NULL,#endif },};
里面的name
成员FIMC_NAME实在fimc.h中定义的:
#define FIMC_NAME "s3c-fimc"
这样,驱动就可以和设备进行match了。
接下来分析驱动。
static int __devinit fimc_probe(struct platform_device *pdev){ struct s3c_platform_fimc *pdata; struct fimc_control *ctrl; 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 ((ctrl->id == FIMC0) && (pdata->cfg_gpio)) pdata->cfg_gpio(pdev); /* 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);#ifdef CONFIG_VIDEO_FIMC_RANGE_WIDE ctrl->range = FIMC_RANGE_WIDE;#else ctrl->range = FIMC_RANGE_NARROW;#endif ret = device_create_file(&(pdev->dev), &dev_attr_log_level); if (ret < 0) { fimc_err("failed to add sysfs entries for log level\n"); goto err_global; } ret = device_create_file(&(pdev->dev), &dev_attr_range_mode); if (ret < 0) { fimc_err("failed to add sysfs entries for range mode\n"); goto err_global; } printk(KERN_INFO "FIMC%d registered successfully\n", ctrl->id);#if (defined(CONFIG_EXYNOS_DEV_PD) && defined(CONFIG_PM_RUNTIME)) sprintf(buf, "fimc%d_iqr_wq_name", ctrl->id); ctrl->fimc_irq_wq = create_workqueue(buf); if (ctrl->fimc_irq_wq == NULL) { fimc_err("failed to create_workqueue\n"); goto err_global; } INIT_WORK(&ctrl->work_struct, s3c_fimc_irq_work); ctrl->irq_cnt.counter = 0; ctrl->power_status = FIMC_POWER_OFF; pm_runtime_enable(&pdev->dev);#endif#ifdef CONFIG_BUSFREQ_OPP /* To lock bus frequency in OPP mode */ ctrl->bus_dev = dev_get(EXYNOS_BUSFREQ_NAME);#endif 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;}
在驱动程序的probe
中,fimc_dev是全局fimc_global结构体变量,在probe
函数开始时分配的内存。
static struct fimc_control *fimc_register_controller(struct platform_device *pdev){ struct s3c_platform_fimc *pdata; struct fimc_control *ctrl; struct resource *res; int id, err; struct cma_info mem_info; struct clk *sclk_fimc_lclk = NULL; struct clk *fimc_src_clk = NULL; id = pdev->id; pdata = to_fimc_plat(&pdev->dev); ctrl = get_fimc_ctrl(id); ctrl->id = id; ctrl->dev = &pdev->dev; ctrl->vd = &fimc_video_device[id]; ctrl->vd->minor = id; ctrl->log = FIMC_LOG_DEFAULT; ctrl->power_status = FIMC_POWER_OFF; /* CMA */ sprintf(ctrl->cma_name, "%s%d", FIMC_CMA_NAME, ctrl->id); err = cma_info(&mem_info, ctrl->dev, 0); fimc_info1("%s : [cma_info] start_addr : 0x%x, end_addr : 0x%x, " "total_size : 0x%x, free_size : 0x%x\n", __func__, mem_info.lower_bound, mem_info.upper_bound, mem_info.total_size, mem_info.free_size); if (err) { fimc_err("%s: get cma info failed\n", __func__); ctrl->mem.size = 0; ctrl->mem.base = 0; } else { ctrl->mem.size = mem_info.total_size; ctrl->mem.base = (dma_addr_t)cma_alloc (ctrl->dev, ctrl->cma_name, (size_t)ctrl->mem.size, 0); } printk(KERN_DEBUG "ctrl->mem.size = 0x%x\n", ctrl->mem.size); printk(KERN_DEBUG "ctrl->mem.base = 0x%x\n", ctrl->mem.base); ctrl->mem.curr = ctrl->mem.base; ctrl->status = FIMC_STREAMOFF; switch (pdata->hw_ver) { case 0x40: ctrl->limit = &fimc40_limits[id]; break; case 0x43: case 0x45: ctrl->limit = &fimc43_limits[id]; break; case 0x50: ctrl->limit = &fimc50_limits[id]; break; case 0x51: ctrl->limit = &fimc51_limits[id]; break; default: ctrl->limit = &fimc51_limits[id]; fimc_err("%s: failed to get HW version\n", __func__); break; } sprintf(ctrl->name, "%s%d", FIMC_NAME, id); strcpy(ctrl->vd->name, ctrl->name); atomic_set(&ctrl->in_use, 0); mutex_init(&ctrl->lock); mutex_init(&ctrl->v4l2_lock); spin_lock_init(&ctrl->outq_lock); init_waitqueue_head(&ctrl->wq); /* get resource for io memory */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { fimc_err("%s: failed to get io memory region\n", __func__); return NULL; } /* request mem region */ res = request_mem_region(res->start, res->end - res->start + 1, pdev->name); if (!res) { fimc_err("%s: failed to request io memory region\n", __func__); return NULL; } /* ioremap for register block */ ctrl->regs = ioremap(res->start, res->end - res->start + 1); if (!ctrl->regs) { fimc_err("%s: failed to remap io region\n", __func__); return NULL; } /* irq */ ctrl->irq = platform_get_irq(pdev, 0); if (request_irq(ctrl->irq, fimc_irq, IRQF_DISABLED, ctrl->name, ctrl)) fimc_err("%s: request_irq failed\n", __func__); if (soc_is_exynos4210()) fimc_src_clk = clk_get(&pdev->dev, "mout_mpll"); else fimc_src_clk = clk_get(&pdev->dev, "mout_mpll_user"); if (IS_ERR(fimc_src_clk)) { dev_err(&pdev->dev, "failed to get parent clock\n"); iounmap(ctrl->regs); return NULL; } sclk_fimc_lclk = clk_get(&pdev->dev, FIMC_CORE_CLK); if (IS_ERR(sclk_fimc_lclk)) { dev_err(&pdev->dev, "failed to get sclk_fimc_lclk\n"); iounmap(ctrl->regs); clk_put(fimc_src_clk); return NULL; } if (clk_set_parent(sclk_fimc_lclk, fimc_src_clk)) { dev_err(&pdev->dev, "unable to set parent %s of clock %s.\n", fimc_src_clk->name, sclk_fimc_lclk->name); iounmap(ctrl->regs); clk_put(sclk_fimc_lclk); clk_put(fimc_src_clk); return NULL; } clk_set_rate(sclk_fimc_lclk, FIMC_CLK_RATE); clk_put(sclk_fimc_lclk); clk_put(fimc_src_clk);#if (!defined(CONFIG_EXYNOS_DEV_PD) || !defined(CONFIG_PM_RUNTIME)) fimc_hwset_reset(ctrl);#endif return ctrl;}
fimc_register_controller
函数用ctrl这个fimc_control结构体,指向全局变量fimc_dev的ctrl。在这个函数中,只对这个指针进行操作,但因为它是个指针,所以操作之后的效果, 和直接操作fimc_dev.ctrl一样,而且返回到probe中后,在fimc_init_global
中,同样再直接对fimc_dev进行操作,也可以看作是对ctrl的操作,(在这,指针的强大之处就体现出来了,)在fimc_reister_controller
中设置一部分,在fimc_init_global
又设置了另外一部分。在fimc_register_controller
中,对结构体ctrl的vd成员的操作,完成了V4L2设备操作,和Fime设备操作的关联,fimc_video_device[id]是全局变量,是个video_device结构体,对应于camera的fimc_init_camera的调用,或者说注册是在这里完成的,竖着video_device结构体的ioctl_ops成员继续查找下去,ioctl_ops成员本身是个V4L2_ioctl_ops的结构体指针,指向了fimc_v4l2_ops,而fimc_v4l2_ioctl_ops的定义在Fimc_v4l2.c文件中,是个全局变量,这个结构体很大,没仔细看,(v4l2是很重要的东西,网上介绍的资料很多,之后会成为主要的学习方向,结构体的成员实在太多太杂,看着有点头晕)不过有个vidioc_s_input
被赋值为fimc_s_input
,fimc_s_input
这个函数调用了fimc_configure_subdev
函数,而fimc_configure_subdev
最终调用了fimc_init_camera
。 fimc_init_global
函数建立起了platform_device和驱动程序的关系。此时将设备层面的配置,关联到了驱动当中。
在fimc_v4l2.c文件中的全局变量fimc_v4l2_ops中有个vidioc_streamon函数指针被赋值为fimc_streamon
,在这个函数中调用了fimc_stream_capture
,在fimc_stream_capture
这个函数中有个s3c_platform_camera *cam
结构体指针,通过查找可以发现,在mach-itop4412.c文件中定义的结构体也是s3c_platform_camera
,继续读代码,在fimc_stream_capture
函数中有个fimc_control结构体指针ctrl,cam的值就是ctrl->cam,而这个ctrl实际就是fimc_dev.c文件中的全局变量fimc_dev,前面分析过,在fimc_probe函数中,有对fimc_dev的初始化,在fimc_probe中,调用的finc_init_global
函数中,把fimc_dev->camera[i]赋值为pdata->camera[i], 这个东西实际就是在mach-itop4412.c文件中的fimc_plat这个sc3_platform_fimc结构体,这个结构体中的camera[]数组成员中就有ov5640这个具体设备。所以到此就把这个设备到驱动到v4l2串起来了。写的比较潦草,仅作记录,如果有幸能为后来的人提供个参考。
- 读Linux源码分析ov5640在三星exynos4412平台上的使用
- 分析:Python在Linux平台上的发展前景
- jstat java工具在linux上的源码分析
- AWStats: Apache/IIS的日志分析工具——在GNU/Linux和Windows平台上的使用简介
- 三星平台上的测试心得
- 在全志R16平台的androidM系统下调通OV5640(分色排版)
- 自娱自乐9之Linux DMA使用1(三星平台DMA分析)
- 在Linux平台上使用Cmake进行交叉编译替代嵌入式平台所使用的Makefile
- 在linux下烧写exynos4412 SD卡启动的Supperboot
- 在linux下烧写exynos4412 SD卡启动的Supperboot(1)
- 在linux下烧写exynos4412 SD卡启动的Supperboot(2)
- dts文件分析---以ov5640为例,修改dts文件使ov5640使用第二个IPU
- 分析Linux中Spinlock在ARM及X86平台上的实现
- 分析Linux中Spinlock在ARM及X86平台上的实现
- 分析Linux中Spinlock在ARM及X86平台上的实现
- 分析Linux中Spinlock在ARM及X86平台上的实现
- 分析Linux中Spinlock在ARM及X86平台上的实现
- AWStats: Apache/IIS的日志分析工具 ——在Windows平台上的使用简介
- 文件编程示例
- c#中decimal ,double,float的区别
- 分解和合并:Java 也擅长轻松的并行编程!
- Struts2学习之3(文件的上传下载,OGNL表达式,常用标签,表单重复提交)
- HDU-2273 The buses
- 读Linux源码分析ov5640在三星exynos4412平台上的使用
- 机器学习数据集
- 2017
- java之线程池简单实现
- 开博客,庆祝!(蜂鸣器马里奥)(测试)
- github安装失败
- Firebug: 已拦截跨源请求:同源策略禁止读取位于XXX的远程资源。(原因:CORS 头缺少 'Access-Control-Allow-
- 素数判定
- 17 - 04 - 18 Web安全(14)