高通mm-camera_kernel

1. Camera Kernel 驱动

在这里插入图片描述

  • msm.c: 所有qcom camera相关设备的管理,提供session、stream、event、vb2_queue等接口的创建和删除
  • camera/:用于控制sensor,提供了ioctl_ops,如启动流,qbuf和dqbuf,subscribe_event等
  • sensor/:具体的硬件驱动,例如sensor、eeprom、actuator、ois等

从代码结构上看 分为camera 和sensor . 从框架把Camera系统分为 msm-config 和 msm-sensor 两部分:

msm-config:用于创建v4l2_device,用于管理所有的子设备,同时生成/dev/video0设备节点,暴露控制接口给用户空间使用,主要包括了session/stream的创建与销毁,event事件的分发,另外,还创建了media_device,用于暴露枚举接口给用户来轮询查找整个内核的子设备。
节点:/sys/class/video4linux/video1
相关源码:kernel/msm-4.9/drivers/media/platform/msm/camera_v2/msm.c

创建的节点  :/vidoe*/msm-config
strlcpy(pvdev->vdev->name, "msm-config", sizeof(pvdev->vdev->name));
pvdev->vdev->release  = video_device_release;
pvdev->vdev->fops     = &msm_fops;
pvdev->vdev->ioctl_ops = &g_msm_ioctl_ops;
pvdev->vdev->minor     = -1;
pvdev->vdev->vfl_type  = VFL_TYPE_GRABBER;
rc = video_register_device(pvdev->vdev,
    VFL_TYPE_GRABBER, -1);


创建的节点 :/media*/msm_config 
media_device_init(msm_v4l2_dev->mdev);
strlcpy(msm_v4l2_dev->mdev->model, MSM_CONFIGURATION_NAME,  //MSM_CONFIGURATION_NAME=msm_config
        sizeof(msm_v4l2_dev->mdev->model));
msm_v4l2_dev->mdev->dev = &(pdev->dev);

rc = media_device_register(msm_v4l2_dev->mdev);

"kernel/msm-4.19/drivers/media/platform/msm/camera_v2/camera/camera.c"
/vidoe*/msm-camera
strlcpy(pvdev->vdev->name, "msm-sensor", sizeof(pvdev->vdev->name));
pvdev->vdev->release  = video_device_release;
pvdev->vdev->fops     = &camera_v4l2_fops;
pvdev->vdev->ioctl_ops = &camera_v4l2_ioctl_ops;
pvdev->vdev->minor     = -1;
pvdev->vdev->vfl_type  = VFL_TYPE_GRABBER;
pvdev->vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
rc = video_register_device(pvdev->vdev,
    VFL_TYPE_GRABBER, -1);

msm-sensor:创建一个v412_device,用于管理sensor,比如sensor 的初始化,启动流,关闭流,图像buf的管理等,同时创建video3/video4设备节点暴露接口给用户空间进行访问。
节点:/sys/class/video4linux/video3 /sys/class/video4linux/video4
相关源码:kernel/msm-4.19/drivers/media/platform/msm/camera_v2/camera/camera.c

/vidoe*/msm-camera
strlcpy(pvdev->vdev->name, "msm-sensor", sizeof(pvdev->vdev->name));
pvdev->vdev->release  = video_device_release;
pvdev->vdev->fops     = &camera_v4l2_fops;
pvdev->vdev->ioctl_ops = &camera_v4l2_ioctl_ops;
pvdev->vdev->minor     = -1;
pvdev->vdev->vfl_type  = VFL_TYPE_GRABBER;
pvdev->vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
rc = video_register_device(pvdev->vdev,
    VFL_TYPE_GRABBER, -1);


/media*/msm_camera 下的节点
strlcpy(v4l2_dev->mdev->model, MSM_CAMERA_NAME,     //MSM_CAMERA_NAME=msm_camera
        sizeof(v4l2_dev->mdev->model));

v4l2_dev->mdev->dev = dev;

rc = media_device_register(v4l2_dev->mdev);
if (WARN_ON(rc < 0))
    goto media_fail;

本文分析camera 部分, 和 sensor dts 解析部分.

  1. camera
    通过解析compatible = “qcom,msm-cam”;来初始化并注册好media_device 、v4l2_device、video_device 设备,同时生成/dev/media0节点。

  2. Sensor 部分
    通过解析compatible = “qcom,camera”;来初始化调用probe解析camera sensor的dts节点信息,保存在全局g_sctrl 数组中。
    然后,上层在初始化时,依次对每个sensor下发 ioctl 参数,触发其作初始化probe ,上电check_sensor_id 及 创建对应的 /dev/videoX 节点 及 /dev/mediaX 的节点

2. msm-cam驱动

msm-cam是在dts 中定义的,
项目 xxx.dts 包含->
kernel/msm-4.19/arch/arm64/boot/dts/vendor/qcom/vendor/qcom/sdm660.dtsi ->
kernel/msm-4.19/arch/arm64/boot/dts/vendor/qcom/sdm660-camera.dtsi

qcom,msm-cam@ca00000 {
    compatible = "qcom,msm-cam";
    reg = <0xca00000 0x4000>;
    reg-names = "msm-cam";
    status = "ok";
    bus-vectors = "suspend", "svs", "nominal", "turbo";
    qcom,bus-votes = <0 150000000 320000000 320000000>;
    qcom,gpu-limit = <700000000>;
};

kernel/msm-4.19/drivers/media/platform/msm/camera_v2/msm.c
static int msm_probe(struct platform_device *pdev)
{
    struct msm_video_device *pvdev = NULL;
    static struct dentry *cam_debugfs_root;
    int rc = 0;
    // 1. 初始化一个 v4l2_device 类型的结构体,并分配好结构体内存
    msm_v4l2_dev = kzalloc(sizeof(*msm_v4l2_dev),
        GFP_KERNEL);
    if (WARN_ON(!msm_v4l2_dev)) {
        rc = -ENOMEM;
        goto probe_end;
    }

    pvdev = kzalloc(sizeof(struct msm_video_device),
        GFP_KERNEL);
    if (WARN_ON(!pvdev)) {
        rc = -ENOMEM;
        goto pvdev_fail;
    }
    // 2. 分配 video_device 结构体内存
    pvdev->vdev = video_device_alloc();
    if (WARN_ON(!pvdev->vdev)) {
        rc = -ENOMEM;
        goto video_fail;
    }

#if defined(CONFIG_MEDIA_CONTROLLER)
    // 3. 分配 media_device 结构体内存
    msm_v4l2_dev->mdev = kzalloc(sizeof(struct media_device),
        GFP_KERNEL);
    if (!msm_v4l2_dev->mdev) {
        rc = -ENOMEM;
        goto mdev_fail;
    }
    // 4.初始化media_device
    media_device_init(msm_v4l2_dev->mdev);
    strlcpy(msm_v4l2_dev->mdev->model, MSM_CONFIGURATION_NAME,sizeof(msm_v4l2_dev->mdev->model));  //MSM_CONFIGURATION_NAME = "msm_config" 

    msm_v4l2_dev->mdev->dev = &(pdev->dev);
    // 5. 注册 media_device , 使用的 v4l2 
    rc = media_device_register(msm_v4l2_dev->mdev);
/**
media_device_register()
    media_devnode_register () 
        device_initialize()  //初始化media 创建mediaX
        cdev_init()      //初始化字符设备
        cdev_device_add() //
    device_create_file(&devnode->dev, &dev_attr_model)  // 创建的节点 sys/devices/platform/soc/ca00000.qcom,msm-cam/media0/model
节点:/sys/bus/media/devices/media0 # cat model
msm_config
上层可以通过IOCTL指令:MEDIA_IOC_ENUM_ENTITIES,枚举所有的media设备,然后根据设备名称msm_config找到的/dev/video0节点。
*/
    if (WARN_ON(rc < 0))
        goto media_fail;

    if (WARN_ON((rc == media_entity_pads_init(&pvdev->vdev->entity,
            0, NULL)) < 0))
        goto entity_fail;

    pvdev->vdev->entity.function = QCAMERA_VNODE_GROUP_ID;
#endif

    msm_v4l2_dev->notify = msm_sd_notify;

    pvdev->vdev->v4l2_dev = msm_v4l2_dev;
    // 6. 设置父设备为 pdev->dev (也就是 qcom,msm-cam 的设备信息)
    rc = v4l2_device_register(&(pdev->dev), pvdev->vdev->v4l2_dev);
/**
int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev)
{
    if (v4l2_dev == NULL)
        return -EINVAL;

    INIT_LIST_HEAD(&v4l2_dev->subdevs);
    spin_lock_init(&v4l2_dev->lock);
    v4l2_prio_init(&v4l2_dev->prio);
    kref_init(&v4l2_dev->ref);
    get_device(dev);
    v4l2_dev->dev = dev;
    if (dev == NULL) {
        /* If dev == NULL, then name must be filled in by the caller */
        if (WARN_ON(!v4l2_dev->name[0]))
            return -EINVAL;
        return 0;
    }

    /* Set name to driver name + device name if it is empty. */
    if (!v4l2_dev->name[0])
       snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "%s %s",
            dev->driver->name, dev_name(dev));
    printk("v4l2_dev->name =%s \n",v4l2_dev->name); //v4l2_dev->name =msm ca00000.qcom,msm-cam
    if (!dev_get_drvdata(dev))
        dev_set_drvdata(dev, v4l2_dev);
    return 0;
}

*/
    if (WARN_ON(rc < 0))
        goto register_fail;
    // 7. 注册 video_device设备 
    strlcpy(pvdev->vdev->name, "msm-config", sizeof(pvdev->vdev->name));
    pvdev->vdev->release  = video_device_release;
    pvdev->vdev->fops     = &msm_fops;   //绑定fops
    pvdev->vdev->ioctl_ops = &g_msm_ioctl_ops;
    pvdev->vdev->minor     = -1;
    pvdev->vdev->vfl_type  = VFL_TYPE_GRABBER;
    rc = video_register_device(pvdev->vdev,
        VFL_TYPE_GRABBER, -1);   // 节点 /dev/vdieoX
/**
video_register_device()
    device_register(&vdev->dev);  //注册video 的节点
/sys/class # cat video4linux/video1/name
msm-config
    
*/
    if (WARN_ON(rc < 0))
        goto v4l2_fail;

#if defined(CONFIG_MEDIA_CONTROLLER)
    /* FIXME: How to get rid of this messy? */
    pvdev->vdev->entity.name = video_device_node_name(pvdev->vdev);
#endif

    atomic_set(&pvdev->opened, 0);
    // 8. 将当前 msm_video_device 结构体设为私有数据     
    video_set_drvdata(pvdev->vdev, pvdev);

    msm_session_q = kzalloc(sizeof(*msm_session_q), GFP_KERNEL);
    if (WARN_ON(!msm_session_q))
        goto v4l2_fail;
    // 9. 分配  msm_queue_head 结构体内存
    msm_init_queue(msm_session_q);
    spin_lock_init(&msm_eventq_lock);
    spin_lock_init(&msm_pid_lock);
    mutex_init(&ordered_sd_mtx);
    mutex_init(&v4l2_event_mtx);
    INIT_LIST_HEAD(&ordered_sd_list);
    // 10. 创建 camera 调试目录
    cam_debugfs_root = debugfs_create_dir(MSM_CAM_LOGSYNC_FILE_BASEDIR,
                        NULL);
    if (!cam_debugfs_root) {
        pr_warn("NON-FATAL: failed to create logsync base directory\n");
    } else {
        if (!debugfs_create_file(MSM_CAM_LOGSYNC_FILE_NAME,
                     0660,
                     cam_debugfs_root,
                     NULL,
                     &logsync_fops))
            pr_warn("NON-FATAL: failed to create logsync debugfs file\n");
    }

    rc = cam_ahb_clk_init(pdev);
    if (rc < 0) {
        pr_err("%s: failed to register ahb clocks\n", __func__);
        goto v4l2_fail;
    }

    of_property_read_u32(pdev->dev.of_node,
        "qcom,gpu-limit", &gpu_limit);

    goto probe_end;

v4l2_fail:
    v4l2_device_unregister(pvdev->vdev->v4l2_dev);
register_fail:
#if defined(CONFIG_MEDIA_CONTROLLER)
    media_entity_cleanup(&pvdev->vdev->entity);
entity_fail:
    media_device_unregister(msm_v4l2_dev->mdev);
media_fail:
    kzfree(msm_v4l2_dev->mdev);
mdev_fail:
#endif
    video_device_release(pvdev->vdev);
video_fail:
    kfree(pvdev);
pvdev_fail:
    kzfree(msm_v4l2_dev);
probe_end:
    return rc;
}
static struct v4l2_file_operations msm_fops = {
    .owner  = THIS_MODULE,
    .open   = msm_open,
    .poll   = msm_poll,
    .release = msm_close,
    .unlocked_ioctl   = video_ioctl2,
#ifdef CONFIG_COMPAT
    .compat_ioctl32 = video_ioctl2,
#endif
};
 
static const struct v4l2_ioctl_ops g_msm_ioctl_ops = {
    .vidioc_subscribe_event = msm_subscribe_event,
    .vidioc_unsubscribe_event = msm_unsubscribe_event,
    .vidioc_default = msm_private_ioctl,
};


v4l2_file_operations:
* msm_open:创建event队列
* msm_poll:轮询event队列
* msm_close:释放相关资源

v4l2_ioctl_ops:
* msm_subscribe_event:订阅事件
* msm_unsubscribe_event:注销事件

3. sensor驱动

kernel/msm-4.19/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c

msm_sensor_driver_platform_probe()
	struct msm_sensor_ctrl_t *s_ctrl;
	msm_sensor_driver_parse() //解析dts 内容
		msm_sensor_driver_get_dt_data(s_ctrl); //
			msm_sensor_driver_get_dt_data(s_ctrl); //
				struct msm_camera_sensor_board_info *sensordata // 结构体msm_camera_sensor_board_info
				of_property_read_u32(of_node, "cell-index", &cell_id); // cell_id 即为camera id
				msm_sensor_get_sub_module_index() //获取马达 其他子设备的信息,包括 "qcom,actuator-src"、"qcom,ois-src"、"qcom,eeprom-src"、
				msm_camera_get_dt_vreg_data() //获取供电信息
				msm_sensor_driver_get_gpio_data() //读取dts 配置的gpio信息
				msm_sensor_init_default_params(s_ctrl) //  // 初始化默认参数
					struct msm_sensor_ctrl_t *s_ctrl;
					struct msm_camera_cci_client *cci_client;
					s_ctrl->sensor_i2c_client->i2c_func_tbl = &msm_sensor_cci_func_tbl; //更新i2c function table
						/**
						static struct msm_camera_i2c_fn_t msm_sensor_cci_func_tbl = {
							.i2c_read = msm_camera_cci_i2c_read,
							.i2c_read_seq = msm_camera_cci_i2c_read_seq,
							.i2c_write = msm_camera_cci_i2c_write,
							.i2c_write_table = msm_camera_cci_i2c_write_table,
							.i2c_write_seq_table = msm_camera_cci_i2c_write_seq_table,
							.i2c_write_table_w_microdelay =
								msm_camera_cci_i2c_write_table_w_microdelay,
							.i2c_util = msm_sensor_cci_i2c_util,
							.i2c_write_conf_tbl = msm_camera_cci_i2c_write_conf_tbl,
							.i2c_write_table_async = msm_camera_cci_i2c_write_table_async,
							.i2c_write_table_sync = msm_camera_cci_i2c_write_table_sync,
							.i2c_write_table_sync_block = msm_camera_cci_i2c_write_table_sync_block,
						
						};
						*/					
					s_ctrl->func_tbl = &msm_sensor_func_tbl; //更新ioctrl 的function table
						/**
						static struct msm_sensor_fn_t msm_sensor_func_tbl = {
							.sensor_config = msm_sensor_config,
						#ifdef CONFIG_COMPAT
							.sensor_config32 = msm_sensor_config32,
						#endif
							.sensor_power_up = msm_sensor_power_up,
							.sensor_power_down = msm_sensor_power_down,
							.sensor_match_id = msm_sensor_match_id,
						};
						*/
					s_ctrl->sensor_v4l2_subdev_ops = &msm_sensor_subdev_ops;  //更新v4l2 ops table
						/**
						static struct v4l2_subdev_core_ops msm_sensor_subdev_core_ops = {
							.ioctl = msm_sensor_subdev_ioctl,
							.s_power = msm_sensor_power,
						};
						static struct v4l2_subdev_ops msm_sensor_subdev_ops = {
							.core = &msm_sensor_subdev_core_ops,
						};
						*/
					g_sctrl[s_ctrl->id] = s_ctrl;  // s_ctrl 初始化的值给全局变量 static struct msm_sensor_ctrl_t *g_sctrl[MAX_CAMERAS];
				msm_camera_get_clk_info() // 获取clock 信息 dts 中配置的camera clk 信息 clock-names = "cam_src_clk", "cam_clk";
				
				
g_sctrl[s_ctrl->id] = s_ctrl;  // s_ctrl 初始化的值给全局变量 static struct msm_sensor_ctrl_t *g_sctrl[MAX_CAMERAS];
全局CameraSensorCtrol数组g_sctrl
g_sctrl 是静态全局的一个msm_sensor_ctrl_t * 结构体指针数组,其定义如下:
static struct msm_sensor_ctrl_t *g_sctrl[MAX_CAMERAS];

前面我们在配置dts 时也发现了,通常我们要配置的camera数量是大于1的,前面代码中,我们配置了3个Camera,两个后摄,一个前摄。而这三个camera dts 中的节点都是一样的"qcom,camera"。
可以看出,驱动和设备会匹配三次,换句话说,也就是 msm_sensor_driver_platform_probe()函数会走三次,每次传递的dts节点内容是不一样的,三个camera都会依次probe 一次,
从而,当probe 完毕后,会保存三个struct msm_sensor_ctrl_t *结构体的数据保存在全局 g_sctrl 中。

至此,我们代码中,就把dts 的内容成功的转化为了msm_sensor_ctrl_t结构体保存在 全局 g_sctrl 中。

4. 总结

msm_sensor_driver_platform_probe(),这个函数主要作用还是解析DTS,但并不会真正probe camera sensor。

那问题来了,camera sensor probe 是在什么时候呢?
其实,camera probe 并不是和其他kernerl 驱动一样,在初始化时就probe,而是通过hal 层下发 probe 指令来控制probe 的。详细的看下高通mm-camera_vendor . hal 层下发指令到vendor , vednor 调用到kenel .
VIDIOC_MSM_SENSOR_INIT_CFG -> msm_sensor_driver_cmd() //“kernel/msm-4.19/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_init.c”

你可能感兴趣的:(Camera_driver,驱动开发)