V4L2驱动简单分析二

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/GCE7212201/article/details/53590354

上一篇提到了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驱动的关联流程搞通了。

展开阅读全文

没有更多推荐了,返回首页