|
地板

楼主 |
发表于 2018-5-6 21:23:30
|
只看该作者
platform驱动:nxp-v4l2
本帖最后由 ancientcc 于 2018-5-8 11:43 编辑
nxp-v4l2是个platform驱动,用于封装v4l2相关的多个设备,像相机有关的sensor,显示有关的hdmi。
系统一启动就会调用nxp_board_devices_register把“nxp-v4l2”这个设备注册向内核。运行到加载“nxp-v4l2”驱动后,platformat总线匹配到对应设备,于是调用“nxp-v4l2”驱动的prob方法,即nxp_v4l2_probe。正是它完成了nxp-v4l2内部各设备的就绪功能。
nxp-v4l2是个和media密切相关的驱动。以下entity就是media中的entity。
- nxp_v4l2_probe
- v4l2_device_register。 --v4l2_device设备名称是“nxp-v4l2.0”
- media_device_register。 --media_dev设备名称是“nxp-v4l2.0”
- register_nxp_capture
- nxp_vin_clipper_register。 ——添加的entity: 1)NXP VIN CLIPPER0,2)VIDEO CLIPPER0。
- nxp_decimator_register。 ——添加的entity: 1)NXP DECIMATOR0,2)VIDEO DECIMATOR0。
- _register_sensor。 ——添加的entity: 1)ov5640 0-003c。
- register_nxp_scaler。 ——添加的entity: 1)NXP SCALER,2)VIDEO SCALER。
- register_nxp_out。 ——添加的entity:1)NXP MLC0,2)VIDEO MLC RGB0。3)VIDEO MLC VID0。4)NXP MLC1。5)VIDEO MLC RGB1。6)VIDEO MLC VID1。7)NXP RESC0。8)NXP HDMI。
- v4l2_device_register_subdev_nodes。 --v4l2_device设备名称是nxp-v4l2.0
- platform_set_drvdata
复制代码 运行nxp_v4l2_probe后,media将有16个entity,各entity的id依次从1到16。接下具体分析“ov5640 0-003c”这个entity。
“ov5640 0-003c”对应相机设备,它在_register_sensor被注册,并创建相应的entity。
- _register_sensor
- i2c_get_adapter。——返回名称是“nxp-i2c”的i2c_adapter。
- v4l2_i2c_new_subdev_board
- request_module。——请求的module叫“ov5640”。
- i2c_new_device。——注册设备,由于之前已加载叫“ov5640”驱动,将调用ov5640_probe。
- try_module_get
- v4l2_device_register_subdev。——添加一个叫“ov5640 0-003c”的entity。
- module_put。
- media_entity_create_link
- _set_sensor_entity_name
复制代码 经过_register_sensor,相机驱动就绪了,同时添加了一个叫“ov5640 0-003c”的entity。
Nanopc-t2相机用的是ov5640,很自然会问一个问题,ov5640驱动是如何被Android的camera这个HAL调用的。
- android_nxp_v4l2_init
- V4l2UsageScheme* scheme;
- v4l2_init(scheme);
- _priv = new V4l2NexellPrivate();
- int ret = _priv->init(scheme);
- MediaFD = open("/dev/media0", O_RDWR);
- enumEntities();——填充pDevice中的id、pads、links字段。当前16个。
- enumDevices();——填充pDevice中的Devnode字段。
- createDevices();——创建pDevice中的Device字段。
- linkDefault();
- activateDevices();
复制代码 首先要清楚两个id,EntityID和DeviceID。
- EntityID指的是Entity结构中的id。用MEDIA_IOC_ENUM_ENTITIES去枚举时,返回的该Entity在media驱动的Entity数组中的索引。在值上,可认为就是该Entity所在驱动挂载到内核的次序+1。该值会随着加载次序变化而变化,因而不宜作为预设值。
- DeviceID。也叫InternalID、nxp_v4l2_id。V4l2NexellPrivate有个数组变量:DeviceInfo Devices[INTERNAL_ID_MAX]。DeviceID值是这个数组的索引。getDevice(int id)中的id就是DeviceID。bsp和android之间的固定值用的也是DeviceID。
到现在也不理解ov5640驱动是如何被Android的camera这个HAL调用的,先放几个结论。
- ov5640对应的Entity名称是“ov5640 0-003c”,DeviceID应该是0(InternalID是Sensor0)。再用getDevice(char *name)比较前会调用getCameraInfo,从/sys/devices/platform/camera sensor/sensor.0读出名称填充CameraInfo[0].SensorEntityName字段。
- 猜测PreviewThread要从ov5640驱动读视频帧,然后看该线程的线程片断函数threadLoop。
- PreviewThread::threadLoop()
- v4l2_dqbuf(Id, PlaneNum, &dqIdx, NULL);——Id是DeviceID,值是nxp_v4l2_decimator0(11),不是0!
- V4l2NexellPrivate::dqBuf(Id, plane_num, index0, index1);
- info->Device->dqBuf(planeNum, index0);——Device的类型是V4l2Device,它是DeviceInfo内的字段,而info是根据Id这个索引在DeviceInfo Devices[INTERNAL_ID_MAX]这个数组对应的单元。
- ioctl(FD, VIDIOC_DQBUF, &v4l2_buf);——FD是打开设备驱动返回的句柄,DeviceInfo中的Devnode字段就是open的第一个参数。
- NXStream *stream = getActiveStream();
- stream->enqueueBuffer(timestamp);
- stream->dequeueBuffer(&buf);
- v4l2_qbuf(Id, PlaneNum, dqIdx, ZoomController->getBuffer(dqIdx), -1, NULL);
复制代码
PreviewThread是如何得到用于v4l2的Id?——get_board_preview_v4l2_id(),它返回nxp_v4l2_decimator0,后者是一个Composite设备(V4l2Composite),让看它的dqBuf。
- virtual int V4l2Composite::dqBuf(int planeNum, int *index0, int *index1 = NULL) {
- return VideoDev->dqBuf(planeNum, index0, index1);
- }
- virtual int V4l2Video::dqBuf(int planeNum, int *index0, int *index1)
- {
- ...
- ret = ioctl(FD, VIDIOC_DQBUF, &v4l2_buf);
- ...
- }
复制代码 V4l2Composite是通过调用V4l2Video的dqBuf。而nxp_v4l2_decimator0中的VideoDev正是nxp_v4l2_sensor0指向的“ov5640 0-003c”,于是发向nxp_v4l2_decimator0的dqBuf实际导向了“ov5640 0-003c”的dqBuf。
接下要如何理解?得深入v4l2如何处理VIDIOC_QBUF、VIDIOC_DQBUF,对nxp-v4l各个驱动,处理这两个ioctl对应的是nxp_video_dqbuf、nxp_video_qbuf。
- struct v4l2_subdev {
- struct media_entity entity;
- ......
- };
- struct nxp_csi {
- ......
- struct v4l2_subdev subdev;
- ......
- };
复制代码
|
|