Linux v4l2子系统(1):RK3588 VI概述
本文从硬件框架、软件框架、Buildroot配置、相关源码、创建Video设备及其关系图,来对整个RK3588 VI有所了解。
基于对硬件架构的了解,然后通过
对v4l2和Media有个基本的了解,然后分解介绍不同硬件的驱动:
- 《Linux v4l2子系统(4):I2C&OV13850》。
- 《Linux v4l2子系统(5):MIPI DPHY》。
- 《Linux v4l2子系统(6):MIPI CSI2》。
- 《Linux v4l2子系统(7):CIF(VICAP)》。
- 《Linux v4l2子系统(8):Rockchip ISP》。
最后介绍v4l2的库文件以及相关应用:
1 RK3588 VI硬件框架
RK3588的VI(Video Input)硬件框架包括如下部分:
- 外部MIPI/LVDS/DVP Camera采集数据,最对支持7路输入(6 MIPI + 1 DVP)。
- MIPI接口:
- CSI-2是MIPI针对摄像头芯片接口协议。
- D/C-PHY同时支持D-PHY和C-PHY。
- D-PHY支持1路4Lane,或者2路2Lane组合。
- DVP接口或称为Camera并口,一般支持BT601/BT656/BT1120数据的传输。
- VICAP负责将从MIPI获取的数据搬运到DDR。
- ISP对Camera Sensor输出的图像信号进行后处理,最多支持4路数据源输入。VICAP数据到ISP支持直通和回读两种模式:
- 直通:指数据经过VICAP采集,直接发送给ISP处理,不存储到DDR。需要注意的是hdr直通时,只有短帧是真正的直通,长帧需要存在DDR,ISP再从DDR读取。
- 回读:指数据经过VICAP采集到DDR,应用获取到数据后,将buffer地址推送给ISP,ISP再从DDR获取图像数据。
- FEC(Fish Eye Correction)负责鱼眼校正。
参考文档《Rockchip_Development_Guide_ISP30_CN_v1.2.3.pdf》。
2 RK3588 MIPI VI软件架构
如下是RK3588 VI链路框图:
- DPHY0/1可以两种模式运行:
- full mode:以节点csi2_dphy0/csi2_dphy3提供4 Lane MIPI接口。
- split mode:以节点csi2_dphy1/csi2_dphy2/csi2_dphy4/csi2_dphy5提供2 Lane MIPI接口。
- VICAP支持同时输入6路MIPI及1路DVP数据输入。
- ISP支持同时4路输入,虚拟出4个设备节点。
以OV13850为例,详细解释数据链路:
- OV13850由IC接口进行配置,需要MIPI 4 Lane将数据送到MIPI DPHY。
- DPHY0 csi2_dphy0作为输入端,将数据送到CSI HOST2。
- VICAP虚拟出多个设备,通过rkcif_mipi_lvds2设备接收数据,通过rkcif_mipi_lvds2_sditf输出到ISP0。
- ISOP0虚拟出4个设备,通过rkisp0_vir0接收数据进行处理。
以OV13850为例,整个软硬件框架分为:
- 硬件层:包含I2C Master、OV13850、DPHY、CSI2、VICAP、ISP等。
- 内核驱动层:I2C Master Driver、OV13850 Driver、DPHY Driver、CSI2 Host Driver、RKCIF Driver、ISP Driver、v4l2 Subsystem、I2C Subsystem、Media Subsystem等。
- 用户层:基于/dev/videoX设备的用户程序以及测试程序等。
3 RK3588 MIPI Camera配置
RK3588 MIPI Camera以OV13850为例,配置如下:
- 使能Media Support,以及v4l2。
- 打开OV13850 Camera。
- 打开RK I2C功能。
Device Drivers Multimedia support
Media core support
Video4Linux core
Media Controller API
Video4Linux options
V4L2 sub-device userspace API
Media ancillary drivers
Camera sensor devices
OmniVision OV13850 sensor support
4 RK3588 MIPI Camera文件
Camera通过I2C接口配置:
drivers/i2c/ ├── algos │ ├── i2c-algo-bit.c ├── busses │ ├── i2c-rk3x.c ├── i2c-boardinfo.c ├── i2c-core-base.c ├── i2c-core-of.c ├── i2c-core-smbus.c ├── i2c-dev.c ├── i2c-mux.c
v4l2 Core以及Camera文件:
drivers/media/ ├── cec │ ├── core │ │ ├── cec-adap.c │ │ ├── cec-api.c │ │ ├── cec-core.c │ │ ├── cec-notifier.c ├── common │ └── videobuf2--v4l2内存管理。 │ ├── vb2-trace.c │ ├── videobuf2-cma-sg.c │ ├── videobuf2-core.c │ ├── videobuf2-dma-contig.c │ ├── videobuf2-dma-sg.c │ ├── videobuf2-dvb.c │ ├── videobuf2-memops.c │ ├── videobuf2-v4l2.c │ ├── videobuf2-vmalloc.c ├── i2c │ ├── ov13850.c--OV13850驱动。 ├── mc │ ├── mc-dev-allocator.c │ ├── mc-device.c │ ├── mc-devnode.c │ ├── mc-entity.c │ ├── mc-request.c ├── platform │ ├── rockchip │ │ ├── cif--RKCIF驱动。 │ │ │ ├── capture.c │ │ │ ├── cif-luma.c │ │ │ ├── cif-scale.c │ │ │ ├── cif-tools.c │ │ │ ├── common.c │ │ │ ├── dev.c │ │ │ ├── hw.c │ │ │ ├── mipi-csi2.c--RK3588 CSI2 Host驱动。 │ │ │ ├── procfs.c │ │ │ ├── subdev-itf.c │ │ ├── isp--RKISP驱动。 │ │ │ ├── bridge.c │ │ │ ├── bridge_v30.c │ │ │ ├── capture.c │ │ │ ├── capture_v1x.c │ │ │ ├── capture_v21.c │ │ │ ├── capture_v30.c │ │ │ ├── common.c │ │ │ ├── csi.c │ │ │ ├── dev.c │ │ │ ├── dmarx.c │ │ │ ├── hw.c │ │ │ ├── isp_dvbm.c │ │ │ ├── isp_params.c │ │ │ ├── isp_params_v1x.c │ │ │ ├── isp_params_v21.c │ │ │ ├── isp_params_v32.c │ │ │ ├── isp_params_v3x.c │ │ │ ├── isp_rockit.c │ │ │ ├── isp_stats.c │ │ │ ├── isp_stats_v1x.c │ │ │ ├── isp_stats_v21.c │ │ │ ├── isp_stats_v2x.c │ │ │ ├── isp_stats_v2x.h │ │ │ ├── isp_stats_v32.c │ │ │ ├── isp_stats_v32.h │ │ │ ├── isp_stats_v3x.c │ │ │ ├── procfs.c │ │ │ ├── regs.c │ │ │ ├── rkisp.c │ │ └── rga │ │ ├── rga-buf.c │ │ ├── rga.c │ │ ├── rga-hw.c └── v4l2-core--v4l2核心。 ├── tuner-core.c ├── v4l2-async.c ├── v4l2-clk.c ├── v4l2-common.c ├── v4l2-compat-ioctl32.c ├── v4l2-ctrls.c ├── v4l2-dev.c ├── v4l2-device.c ├── v4l2-dv-timings.c ├── v4l2-event.c ├── v4l2-fh.c ├── v4l2-flash-led-class.c ├── v4l2-fwnode.c ├── v4l2-h264.c ├── v4l2-i2c.c ├── v4l2-ioctl.c ├── v4l2-mc.c ├── v4l2-mem2mem.c ├── v4l2-spi.c ├── v4l2-subdev.c ├── v4l2-trace.c
使用到的MIPI DPHY驱动:
drivers/phy/rockchip/
├── phy-rockchip-csi2-dphy.c--注册DPHY v4l2设备驱动。 ├── phy-rockchip-csi2-dphy-hw.c--获取DPHY硬件参数驱动。
5 用户空间设备节点
v4l2设备:
v4l-subdev0 -> ../../devices/platform/fdd30000.mipi2-csi2/video4linux/v4l-subdev0 rockchip-mipi-csi2 v4l-subdev1 -> ../../devices/platform/csi2-dphy0/video4linux/v4l-subdev1 rockchip-csi2-dphy0 v4l-subdev2 -> ../../devices/platform/feab0000.i2c/i2c-3/3-0010/video4linux/v4l-subdev2 m02_b_ov13850 3-0010 v4l-subdev3 -> ../../devices/platform/rkisp0-vir0/video4linux/v4l-subdev3 rkisp-isp-subdev v4l-subdev4 -> ../../devices/platform/rkcif-mipi-lvds2-sditf/video4linux/v4l-subdev4 rkcif-mipi-lvds2 video0 -> ../../devices/platform/rkcif-mipi-lvds2/video4linux/video0 stream_cif_mipi_id0 video1 -> ../../devices/platform/rkcif-mipi-lvds2/video4linux/video1 stream_cif_mipi_id1 video2 -> ../../devices/platform/rkcif-mipi-lvds2/video4linux/video2 stream_cif_mipi_id2 video3 -> ../../devices/platform/rkcif-mipi-lvds2/video4linux/video3 stream_cif_mipi_id3 video4 -> ../../devices/platform/rkcif-mipi-lvds2/video4linux/video4 rkcif_scale_ch0 video5 -> ../../devices/platform/rkcif-mipi-lvds2/video4linux/video5 rkcif_scale_ch1 video6 -> ../../devices/platform/rkcif-mipi-lvds2/video4linux/video6 rkcif_scale_ch2 video7 -> ../../devices/platform/rkcif-mipi-lvds2/video4linux/video7 rkcif_scale_ch3 video8 -> ../../devices/platform/rkcif-mipi-lvds2/video4linux/video8 rkcif_tools_id0 video9 -> ../../devices/platform/rkcif-mipi-lvds2/video4linux/video9 rkcif_tools_id1 video10 -> ../../devices/platform/rkcif-mipi-lvds2/video4linux/video10 rkcif_tools_id2 video11 -> ../../devices/platform/rkisp0-vir0/video4linux/video11 rkisp_mainpath video12 -> ../../devices/platform/rkisp0-vir0/video4linux/video12 rkisp_selfpath video13 -> ../../devices/platform/rkisp0-vir0/video4linux/video13 rkisp_fbcpath video14 -> ../../devices/platform/rkisp0-vir0/video4linux/video14 rkisp_iqtool video15 -> ../../devices/platform/rkisp0-vir0/video4linux/video15 rkisp_rawrd0_m video16 -> ../../devices/platform/rkisp0-vir0/video4linux/video16 rkisp_rawrd2_s video17 -> ../../devices/platform/rkisp0-vir0/video4linux/video17 rkisp_rawrd1_l video18 -> ../../devices/platform/rkisp0-vir0/video4linux/video18 rkisp-statistics video19 -> ../../devices/platform/rkisp0-vir0/video4linux/video19 rkisp-input-params video20 -> ../../devices/platform/fdee0000.hdmirx-controller/video4linux/video20 stream_hdmirx
5.1 通过media_gobj_create函数创建Media Graph
根据media_gobj_create()输出的log,使用如下脚本生成dot文件:
import re dmesg_name = "rk3588-v4l2-boot.txt" #interested_device_name = "rkcif-mipi-lvds2" interested_device_name = "rkisp0-vir0" entity_dict = {} media_graph = {} if __name__ == '__main__': print("strict digraph \"%s\" {"%(interested_device_name)) print("\trankdir=LR") print("\tnodesep=1") print("\tranksep=1") with open(dmesg_name, 'r') as dmesg: for line in dmesg: if "media_gobj_create" in line: #[ 5.170297] rkisp rkisp0-vir0: media_gobj_create id 1: entity 'rkisp-isp-subdev' #print(line) #m = re.match('\[ *(?P<time>.*)\] (?P<module>.*) (?P<device>.*): media_gobj_create id (?P<id>.[0-9]): (?P<t>.*)', line) #m = re.match('\[ *(?P<time>.*)\] (?P<module>[a-zA-Z0-9]*) (?P<device>.*): media_gobj_create id (?P<else>.*)', line) m = re.match('\[ *(?P<time>.*)\] (?P<module>[a-zA-Z0-9]*) (?P<device>.*): media_gobj_create id (?P<id>[0-9]*): (?P<else>.*)',line) if(m): #device, id, t = m.group('device', 'id', 't') #print("Device=%s, ID=%s, String=%s"%(device, id, t)) module_name = m.group("module") device_name = m.group("device") if device_name != interested_device_name: continue id_num = m.group("id") media_graph_item = m.group("else") #print("%s %s %s: %s"%(module_name, device_name, id_num, media_graph_item)) if "entity" in media_graph_item: m = re.match("entity \'(?P<entity>.*)\'", media_graph_item) if(m): entity_name = m.group("entity") print("\t%s[label=\"%s\", shape=box, fontcolor=Red, group=g%s];"%(id_num, entity_name, id_num)) entity_dict[entity_name]={"entity":id_num, "sink":[], "source":[]} else: print("Error in Entity: %s"%(media_graph_item)) sys.exit(1) elif "intf_devnode" in media_graph_item: m = re.match("intf_devnode (?P<video>.*) - major: (?P<major>[0-9]*), minor: (?P<minor>[0-9]*)", media_graph_item) if(m): video_name = m.group("video") major_name = m.group("major") minor_name = m.group("minor") if video_name == "v4l-video": print("\t%s[label=\"IntfDevnode:%s%s\", fontcolor=Blue];"%(id_num, "/dev/video", minor_name)) elif video_name == "v4l-subdev": print("\t%s[label=\"IntfDevnode:%s%s\", fontcolor=Blue];"%(id_num, "/dev/v4l-subdev", minor_name)) else: sys.exit(1) else: print("Error in IntfDevnode: %s"%(media_graph_item)) sys.exit(1) elif "sink pad" in media_graph_item: m = re.match("sink pad \'(?P<entity>.*)\':(?P<id>[0-9]*)", media_graph_item) if(m): entity_name = m.group("entity") sink_id = m.group("id") print("\t%s[label=\"SinkPad:%s-%s\", shape=cds, group=g%s];"%(id_num, entity_name, sink_id, entity_dict[entity_name]["entity"])) print("\t%s->%s;"%(id_num, entity_dict[entity_name]["entity"])) entity_dict[entity_name]["sink"].append(id_num) else: print("Error in SinkPad: %s"%(media_graph_item)) sys.exit(1) elif "source pad" in media_graph_item: m = re.match("source pad \'(?P<entity>.*)\':(?P<id>[0-9]*)", media_graph_item) if(m): entity_name = m.group("entity") sink_id = m.group("id") print("\t%s[label=\"SourcePad:%s-%s\", shape=cds, group=g%s];"%(id_num, entity_name, sink_id, entity_dict[entity_name]["entity"])) print("\t%s->%s;"%(entity_dict[entity_name]["entity"], id_num)) entity_dict[entity_name]["source"].append(id_num) else: print("Error in SourcePad: %s"%(media_graph_item)) sys.exit(1) elif "interface link" in media_graph_item: m = re.match("interface link id (?P<src>[0-9]*) ==> id (?P<dst>[0-9]*)", media_graph_item) if(m): src_id=m.group("src") dst_id=m.group("dst") print("\t%s->%s;"%(src_id, dst_id)) else: print("Error in InterfaceLink: %s"%(media_graph_item)) sys.exit(1) elif "data link" in media_graph_item: m = re.match("data link id (?P<src>[0-9]*) ==> id (?P<dst>[0-9]*)", media_graph_item) if (m): src_id = m.group("src") dst_id = m.group("dst") print("\t%s->%s;" % (src_id, dst_id)) else: print("Error in DataLink: %s"%(media_graph_item)) sys.exit(1) else: print("Error in: "%(media_graph_item)) sys.exit(1) else: sys.exit(1) print("Error:", line) print("}")
得到的Media Graph如下:
5.2 通过media-ctl生成dot创建Media Graph图
后来发现media-ctl生成更好的Media Graph图:
- Entity用绿色框表示,输入Port为Entity Sink,输出Port为Entity Source。
- 虚线表示可能的链路,实线表示当前激活链路。
- IntfDevnode用黄色框表示。
通过如下命令生成dot,然后转成png:
media-ctl --d /dev/media0 --print-dot
dot -Tpng media0.dot -o media0.png
参考资料:
《V4L2框架概述》、《V4L2框架-media device》、《V4L2框架-v4l2 device》、《V4L2框架-control_v4l2_control》、《V4L2框架-control的数据结构》、《V4L2框架-videobuf2》。
《RK3588-Camera:MIPI-CSI调试之通路解析》、《camera调试:RK3588 MIPI/DVP camera关键配置》。
《RK3588s imx415相机适配及ISP调优系列(一)_rk_evb_imx415连接器》、《RK3588s imx415相机适配及ISP调优系列(二)--- mipi相机适配_rk3588 imx415》、《RK3588s imx415相机适配及ISP调优系列(三)--- RKISP调试环境配置》。