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串起来了。写的比较潦草,仅作记录,如果有幸能为后来的人提供个参考。