上一篇提到了radio-tea5764.c实例来分析了V4L2的驱动注册流程,下面再以x210摄像头驱动ov2655来继续分析一下V4L2摄像头驱动注册流程
首先我们带着疑问来进入分析流程,当然这些疑问是与上一篇v4l2 radio驱动比对发现的。
先罗列一下ov2655驱动关键结构体
static const struct v4l2_subdev_core_ops ov2655_core_ops =
{
.init = ov2655_init, /* initializing API */
.s_config = ov2655_s_config, /* Fetch platform data */
.queryctrl = ov2655_queryctrl,
.querymenu = ov2655_querymenu,
.g_ctrl = ov2655_g_ctrl,
.s_ctrl = ov2655_s_ctrl,
};
static const struct v4l2_subdev_video_ops ov2655_video_ops =
{
.g_fmt = ov2655_g_fmt,//no
.s_fmt = ov2655_s_fmt,//no
.enum_framesizes = ov2655_enum_framesizes,
.enum_frameintervals = ov2655_enum_frameintervals,//no
.enum_fmt = ov2655_enum_fmt,//no
.try_fmt = ov2655_try_fmt,//no
.g_parm = ov2655_g_parm,//no
.s_parm = ov2655_s_parm,//no
};
static const struct v4l2_subdev_ops ov2655_ops =
{
.core = &ov2655_core_ops,
.video = &ov2655_video_ops,
};
我们会在摄像头驱动文件里面看到以下三个结构体,然后probe函
数如下
static int ov2655_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct ov2655_state *state;
struct v4l2_subdev *sd;
state = kzalloc(sizeof(struct ov2655_state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
sd = &state->sd;
strcpy(sd->name, S5K4BA_DRIVER_NAME);
/* Registering subdev */
v4l2_i2c_subdev_init(sd, client, &ov2655_ops);
dev_info(&client->dev, "ov2655 has been probed\n");
return 0;
}
经过分析发现,找了整个驱动文件,都找不到与上一篇提到的video_device、v4l2_ioctl_ops、v4l2_file_operations、video_register_device等结构体或者函数,那是为什么呢?对于摄像头设备,以上的东西都去哪里了呢?
下面我们一步一步来分析一下其中的原因
首先, 在s5pv210中,摄像头接口是连接在FIMC控制器里面的,而Samsung把摄像头设备与fimc设备关联了在一起。所以,分析摄像头驱动,要先从fimc设备驱动来作分析,然后再去分析具体摄像头的驱动流程。
1)在FIMC上注册摄像头设备信息
arch/arm/mach-s5pv210/mach-x210.c
smdkc110_machine_init
//把摄像头信息注册在fimc0-2设备上
s3c_fimc0_set_platdata(&fimc_plat_lsi);
s3c_fimc1_set_platdata(&fimc_plat_lsi);
s3c_fimc2_set_platdata(&fimc_plat_lsi);
static struct s3c_platform_fimc fimc_plat_lsi = {
...
.camera = {
#ifdef CONFIG_VIDEO_OV2655
&ov2655,
#endif
},
...
};
2)fimc设备驱动注册流程
三个关键结构体
//drivers/media/video/samsung/fimc/fimc_dev.c
//v4l2_file_operations操作结构体
//应用层控制摄像头时调用的接口
static const struct v4l2_file_operations fimc_fops = {
.owner = THIS_MODULE,
.open = fimc_open,
.release = fimc_release,
.ioctl = video_ioctl2,//通过这个来调用fimc_v4l2_ops
.read = fimc_read,
.write = fimc_write,
.mmap = fimc_mmap,
.poll = fimc_poll,
};
//drivers/media/video/samsung/fimc/fimc_dev.c
//对应的v4l2专用接口
const struct v4l2_ioctl_ops fimc_v4l2_ops = {
....
}
//drivers/media/video/samsung/fimc/fimc_dev.c
//video_device s5pv210有三个fimc接口,所以有三个结构体成员
struct video_device fimc_video_device[FIMC_DEVICES] = {
[0] = {
.fops = &fimc_fops,//v4l2_file_operations 直接供应用层调用的接口
.ioctl_ops = &fimc_v4l2_ops,//v4l2_ioctl_ops
.release = fimc_vdev_release,
},
[1] = {
.fops = &fimc_fops,
.ioctl_ops = &fimc_v4l2_ops,
.release = fimc_vdev_release,
},
[2] = {
.fops = &fimc_fops,
.ioctl_ops = &fimc_v4l2_ops,
.release = fimc_vdev_release,
},
};
有了解platform设备probe的执行流程的都知道,一般在注册platform driver之前,都要先注册platform device。所以在arch/arm/mach-s5pv210/mach-x210.c里面有如下定义
static struct platform_device *smdkc110_devices[] __initdata = {
//注册三个fimc控制器设备
#ifdef CONFIG_VIDEO_FIMC
&s3c_device_fimc0,
&s3c_device_fimc1,
&s3c_device_fimc2,
#endif
}
//其它类似
struct platform_device s3c_device_fimc0 = {
.name = "s3c-fimc",
.id = 0,
.num_resources = ARRAY_SIZE(s3c_fimc0_resource),
.resource = s3c_fimc0_resource,
};
//再回到drivers/media/video/samsung/fimc/fimc_dev.c里面
#define FIMC_NAME "s3c-fimc"
static struct platform_driver fimc_driver = {
.probe = fimc_probe,
.remove = fimc_remove,
.suspend = fimc_suspend,
.resume = fimc_resume,
.driver = {
.name = FIMC_NAME,
.owner = THIS_MODULE,
},
};
所以在fimc_register执行时,就会执行fimc_probe函数
3)具体摄像头的注册流程
摄像头设备挂载在I2C总线上,通过probe函数把摄像头注册为v4l2子设备
ov2655_probe
->v4l2_i2c_subdev_init
4)问题来了,把摄像头注册程v4l2子设备后,fimc是怎么控制具体摄像头关联的呢
当然,首先第一反应肯定是要从fimc_fops的fimc_open和fimc_probe那里查找。
static int fimc_open(struct file *filp)
{
struct fimc_control *ctrl;
struct s3c_platform_fimc *pdata;
struct fimc_prv_data *prv_data;
int in_use;
int ret;
//通过设备号索引对应的video设备(即具体摄像头)
//通过video_get_drvdata找到video设备所对应的fimc_control结构体
ctrl = video_get_drvdata(video_devdata(filp));
pdata = to_fimc_plat(ctrl->dev);
mutex_lock(&ctrl->lock);
in_use = atomic_read(&ctrl->in_use);
if (in_use >= FIMC_MAX_CTXS || (in_use && 1 != ctrl->id)) {
fimc_err("%s: Device busy.\n", __func__);
ret = -EBUSY;
goto resource_busy;
} else {
atomic_inc(&ctrl->in_use);
}
in_use = atomic_read(&ctrl->in_use);
prv_data = kzalloc(sizeof(struct fimc_prv_data), GFP_KERNEL);
if (!prv_data) {
fimc_err("%s: not enough memory\n", __func__);
ret = -ENOMEM;
goto kzalloc_err;
}
......;//省略
prv_data->ctrl = ctrl;
//保存备份ctrl的私有数据
filp->private_data = prv_data;
......;//省略
}
看了fimc_open函数,显然没有相关的关联函数,只是直接根据摄像头的索引号,查找到对应的fimc_control,然后保存在文件的私有数据中。
下面继续分析probe函数,从2)中知道,probe里面传入的pdev指针应该是s3c_device_fimc0-2
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设备,用以存放fimc控制器和s3c_platform_camera
fimc_dev = kzalloc(sizeof(*fimc_dev), GFP_KERNEL);
if (!fimc_dev) {
dev_err(&pdev->dev, "%s: not enough memory\n",
__func__);
return -ENOMEM;
}
}
//把s3c_device_fimc0-2 填充到一个新的fimc_control结构体中
//就是为每一个fimc控制器分配一个fimc_control结构体
//并且实现ctrl与fimc_dev进行关联
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);
/* 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);
}
/* 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 */
//把s3c_device_fimc0-2 指向的dev与fimc_control结构体的v4l2_dev关联
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) {
//找出可用的摄像头
//并把它们放到fimc_dev结构体中
//pdev = &s3c_device_fimc0 s3c_device_fimc0->camera[i]
//具体看一下下面fimc_init_global的分析
ret = fimc_init_global(pdev);
if (ret)
goto err_v4l2;
}
/* video device register */
//ctrl->vd 在fimc_register_controller里面进行赋值
//把控制器的V4L2设备注册为video设备即出现
//的设备名为/dev/videox
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_init_global的分析
static int fimc_init_global(struct platform_device *pdev)
{
struct s3c_platform_fimc *pdata;
//这个结构体就是用来描述一个摄像头的
struct s3c_platform_camera *cam;
int i;
//获得平台信息
pdata = to_fimc_plat(&pdev->dev);
/* Registering external camera modules. re-arrange order to be sure */
for (i = 0; i < FIMC_MAXCAMS; i++) {
//找出从fimc_plat_lsi注册过的camera
cam = pdata->camera[i];
if (!cam)
break;
cam->srclk = clk_get(&pdev->dev, cam->srclk_name);
if (IS_ERR(cam->srclk)) {
dev_err(&pdev->dev,
"%s: failed to get mclk src(srclk_name:%s)\n",
__func__, cam->srclk_name);
return -EINVAL;
}
/* mclk */
cam->clk = clk_get(&pdev->dev, cam->clk_name);
if (IS_ERR(cam->clk)) {
dev_err(&pdev->dev,
"%s: failed to get mclk src(clk_name:%s)\n",
__func__, cam->clk_name);
clk_put(cam->srclk);
return -EINVAL;
}
clk_put(cam->clk);
clk_put(cam->srclk);
/* Assign camera device to fimc */
//把摄像头填充到fimc_dev结构体中
//通过fimc_s_input实现关联ctrl的cam摄像头
memcpy(&fimc_dev->camera[i], cam, sizeof(*cam));
//表示相应的有效
fimc_dev->camera_isvalid[i] = 1;
fimc_dev->camera[i].initialized = 0;
}
fimc_dev->active_camera = -1;
fimc_dev->initialized = 1;
return 0;
}
分析完了fimc_probe后,fimc_dev创建了,也填充相应的fimc_control和camera结构体了,应该是与实际的驱动关联起来了吧,真的吗,我们就看看实际的驱动是怎么用的?
下面以fimc_g_ctrl为例
fimc_g_ctrl
->fimc_g_ctrl_capture
->subdev_call
->v4l2_subdev_call(ctrl->cam->sd, o, f, ##args)
看到这里,我们会注意到,应用直接去调用摄像头驱动用的是fimc_control里面的cam指针,但是我们查看了fimc_probe里面所有相关的函数,都没有提到有关ctrl->cam指针的初始化啊,只有在fimc_init_global里面对memcpy(&fimc_dev->camera[i], cam, sizeof(*cam));进行了初始化。估计有些朋友不能理解我说的没有初始化ctrl->cam指针是什么意思,下面列出几个机构体就清楚了。
struct fimc_global *fimc_dev;
struct fimc_global {
struct fimc_control ctrl[FIMC_DEVICES];
struct s3c_platform_camera camera[FIMC_MAXCAMS];/*fimc_init_global里面初始化的camera结构体*/
int camera_isvalid[FIMC_MAXCAMS];
int active_camera;
int initialized;
};
/* fimc controller abstration */
struct fimc_control {
...;
struct s3c_platform_camera *cam; /*对应的所说的ctrl->cam指针*/
};
那么,到底这个ctrl->cam的指针是在什么时候被初始化并与实际的驱动关联的呢?
我们知道应用程序在open后使用VIDIOC_S_INPUT和ioctl来选择不同输入,所以我们试着来分析fimc_s_input。
int fimc_s_input(struct file *file, void *fh, unsigned int i)
{
struct fimc_global *fimc = get_fimc_dev();
struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
int ret = 0;
fimc_dbg("%s: index %d\n", __func__, i);
if (i < 0 || i >= FIMC_MAXCAMS) {
fimc_err("%s: invalid input index\n", __func__);
return -EINVAL;
}
if (!fimc->camera_isvalid[i])
return -EINVAL;
if (fimc->camera[i].sd && ctrl->id != 2) {
fimc_err("%s: Camera already in use.\n", __func__);
return -EBUSY;
}
mutex_lock(&ctrl->v4l2_lock);
/* If ctrl->cam is not NULL, there is one subdev already registered.
* We need to unregister that subdev first.
*/
if (i != fimc->active_camera) {
fimc_release_subdev(ctrl);
//下面两个函数很关键
//出现了ctrl->cam初始化了
//就是fimc_global结构体里面的camera复制到fimc_control的cam指针里面
ctrl->cam = &fimc->camera[i];
//通过i2c适配器实现了与实际的摄像头驱动关联
ret = fimc_configure_subdev(ctrl);
if (ret < 0) {
mutex_unlock(&ctrl->v4l2_lock);
fimc_err("%s: Could not register camera sensor "
"with V4L2.\n", __func__);
return -ENODEV;
}
fimc->active_camera = i;
}
if (ctrl->id == 2) {
if (i == fimc->active_camera) {
ctrl->cam = &fimc->camera[i];
} else {
mutex_unlock(&ctrl->v4l2_lock);
return -EINVAL;
}
}
mutex_unlock(&ctrl->v4l2_lock);
return 0;
}
//通过i2c适配器实现了与实际的摄像头驱动关联
static int fimc_configure_subdev(struct fimc_control *ctrl)
{
struct i2c_adapter *i2c_adap;
struct i2c_board_info *i2c_info;
struct v4l2_subdev *sd;
unsigned short addr;
char *name;
/* set parent for mclk */
if (clk_get_parent(ctrl->cam->clk->parent))
clk_set_parent(ctrl->cam->clk->parent, ctrl->cam->srclk);
/* set rate for mclk */
if (clk_get_rate(ctrl->cam->clk))
clk_set_rate(ctrl->cam->clk, ctrl->cam->clk_rate);
//根据cam的配置的i2c值(这里面配置s3c_platform_camera ov2655)
//找到适配器
i2c_adap = i2c_get_adapter(ctrl->cam->i2c_busnum);
if (!i2c_adap)
fimc_err("subdev i2c_adapter missing-skip registration\n");
//s3c_platform_camera ov2655 里面的info
i2c_info = ctrl->cam->info;
if (!i2c_info) {
fimc_err("%s: subdev i2c board info missing\n", __func__);
return -ENODEV;
}
name = i2c_info->type;
if (!name) {
fimc_err("subdev i2c driver name missing-skip registration\n");
return -ENODEV;
}
addr = i2c_info->addr;
if (!addr) {
fimc_err("subdev i2c address missing-skip registration\n");
return -ENODEV;
}
/*
* NOTE: first time subdev being registered,
* s_config is called and try to initialize subdev device
* but in this point, we are not giving MCLK and power to subdev
* so nothing happens but pass platform data through
*/
//将camera同时注册为i2c和v4l2设备
sd = v4l2_i2c_new_subdev_board(&ctrl->v4l2_dev, i2c_adap,
name, i2c_info, &addr);
if (!sd) {
fimc_err("%s: v4l2 subdev board registering failed\n",
__func__);
}
/* Assign subdev to proper camera device pointer */
ctrl->cam->sd = sd;/*把摄像头与fimc_ctrl结构体关联起来*/
return 0;
}
到这里,终于把fimc如何跟camera驱动的关联流程搞通了。