页面加载中,请稍候
来源:一口Linux发布时间:2023-03-13294浏览
询问 AICamera | 5.Linux v4l2架构(基于rk3568)
拓扑结构是我们了解MIPI-CSI内部模块以及与摄像头连接关系的最直观最便捷的方法。
file视角
v4l2视角
来自:参考文档《RKISP_Driver_User_Manual_v1.3.pdf》
在/sys/class/video4linux/下可以找到v4l2相关的设备节点:
rk3568_r:/#lssys/class/video4linux
lssys/class/video4linux
v4l-subdev0v4l-subdev2video1video3video5video7
v4l-subdev1video0video2video4video6video8
rk3568_r:/#catsys/class/video4linux/video0/dev
catsys/class/video4linux/video0/dev
81:0
rk3568_r:/#catsys/class/video4linux/video0/name
catsys/class/video4linux/video0/name
rkisp_mainpath
udev文件系统会为我们在dev/目录下创建一个video0节点,即dev/video0
用户可以打开dev/video0节点,通过IOCTL命令和内核空间进行通信。
rk3568_r:/#ls/dev/video*-l
ls/dev/video*-l
crw-rw----1mediacamera81,02022-11-0917:06/dev/video0
crw-rw----1mediacamera81,12022-11-0917:06/dev/video1
crw-rw----1mediacamera81,22022-11-0917:06/dev/video2
crw-rw----1mediacamera81,32022-11-0917:06/dev/video3
crw-rw----1mediacamera81,42022-11-0917:06/dev/video4
crw-rw----1mediacamera81,52022-11-0917:06/dev/video5
crw-rw----1mediacamera81,62022-11-0917:06/dev/video6
crw-rw----1mediacamera81,72022-11-0917:06/dev/video7
crw-rw----1mediacamera81,82022-11-0917:06/dev/video8
rk3568_r:/#ls/dev/v4l-sub*-l
ls/dev/v4l-sub*-l
crw-rw-rw-1mediacamera81,92022-11-0917:06/dev/v4l-subdev0
crw-rw-rw-1mediacamera81,102022-11-0917:06/dev/v4l-subdev1
crw-rw-rw-1mediacamera81,112022-11-0917:06/dev/v4l-subdev2
命令media-ctl可以查看拓扑结构图
rk3568_r:/#media-ctl-d/dev/media0-p
media-ctl-d/dev/media0-p
Openingmediadevice/dev/media0
Enumeratingentities
Found13entities
Enumeratingpadsandlinks
MediacontrollerAPIversion0.0.255
Mediadeviceinformation
------------------------
driverrkisp-vir0
modelrkisp0
serial
businfo
hwrevision0x0
driverversion0.0.255
Devicetopology
-entity1:rkisp-isp-subdev(4pads,7links)
typeV4L2subdevsubtypeUnknown
devicenodename/dev/v4l-subdev0
pad0:Sink
[fmt:SBGGR10/4224x3136
crop.bounds:(0,0)/4096x3072
crop:(0,0)/4096x3072]
<-"rkisp-csi-subdev":1[]
<-"rkisp_rawrd0_m":0[]
<-"rkisp_rawrd2_s":0[]
pad1:Sink
<-"rkisp-input-params":0[]
pad2:Source
[fmt:YUYV2X8/4096x3072
crop.bounds:(0,0)/4096x3072
crop:(0,0)/4096x3072]
->"rkisp_mainpath":0[]
->"rkisp_selfpath":0[]
pad3:Source
->"rkisp-statistics":0[]
-entity6:rkisp-csi-subdev(6pads,5links)
typeV4L2subdevsubtypeUnknown
devicenodename/dev/v4l-subdev1
pad0:Sink
<-"rockchip-csi2-dphy0":1[]
pad1:Source
->"rkisp-isp-subdev":0[]
pad2:Source
->"rkisp_rawwr0":0[]
pad3:Source
pad4:Source
->"rkisp_rawwr2":0[]
pad5:Source
->"rkisp_rawwr3":0[]
-entity13:rkisp_mainpath(1pad,1link)
typeNodesubtypeV4L
devicenodename/dev/video0
pad0:Sink
<-"rkisp-isp-subdev":2[]
-entity19:rkisp_selfpath(1pad,1link)
typeNodesubtypeV4L
devicenodename/dev/video1
pad0:Sink
<-"rkisp-isp-subdev":2[]
-entity25:rkisp_rawwr0(1pad,1link)
typeNodesubtypeV4L
devicenodename/dev/video2
pad0:Sink
<-"rkisp-csi-subdev":2[]
-entity31:rkisp_rawwr2(1pad,1link)
typeNodesubtypeV4L
devicenodename/dev/video3
pad0:Sink
<-"rkisp-csi-subdev":4[]
-entity37:rkisp_rawwr3(1pad,1link)
typeNodesubtypeV4L
devicenodename/dev/video4
pad0:Sink
<-"rkisp-csi-subdev":5[]
-entity43:rkisp_rawrd0_m(1pad,1link)
typeNodesubtypeV4L
devicenodename/dev/video5
pad0:Source
->"rkisp-isp-subdev":0[]
-entity49:rkisp_rawrd2_s(1pad,1link)
typeNodesubtypeV4L
devicenodename/dev/video6
pad0:Source
->"rkisp-isp-subdev":0[]
-entity55:rkisp-statistics(1pad,1link)
typeNodesubtypeV4L
devicenodename/dev/video7
pad0:Sink
<-"rkisp-isp-subdev":3[]
-entity61:rkisp-input-params(1pad,1link)
typeNodesubtypeV4L
devicenodename/dev/video8
pad0:Source
->"rkisp-isp-subdev":1[]
-entity67:rockchip-csi2-dphy0(2pads,2links)
typeV4L2subdevsubtypeUnknown
devicenodename/dev/v4l-subdev2
pad0:Sink
<-"m00_b_ov138504-0010":0[]
pad1:Source
->"rkisp-csi-subdev":0[]
-entity70:m00_b_ov138504-0010(1pad,1link)
typeV4L2subdevsubtypeSensor
devicenodename/dev/v4l-subdev3
pad0:Source
[fmt:SBGGR10/4224x3136]
->"rockchip-csi2-dphy0":0[]
下面是根据显示内容绘制的拓扑图:
拓扑结构该图中各个entity对应的设备节点名称已经标注。模块的上方的黄色pad默认是source pad,下方的黄色pad是sink pad
字符设备类型主要有两种(只考虑摄像头):
video设备主要用于图像操作,必须创建结构体struct video_device变量,v4l-subdev设备主要对应sensor等具体从设备,必须创建struct v4l2_subdev变量,内部的isp和csi、csi-dphy也都需要注册为subdev
这些entity由media_entity模块负责维护,将他们连接起来。
这些entity瑞芯微已经设定了他们各自的功能:


这些entity我们可以理解为一个个功能模块。
这些功能模块有的用于驱动csi、有的驱动isp、有的用于预览图像、有的用于统计视频信息、有的用于配置参数。
这些功能模块,并不是都一定每个camera控制器都有的,有一些是通用的,比如,mainpath、selfpath,有一些要完全看SoC设计,即使瑞芯微的SoC,不同型号,差别也不小。所以具体问题要具体分析,不可教条。
v4l2只定义了基本架构,定义好了回调函数接口,要实现模块具体功能只需要填充好对应的回调函数即可,应用层通过这些字符设备文件和对应的ioctrl命令,就可以实现相应的功能。
rk3568的camera控制器使用结构体struct rkisp_device管理所有的资源。
/*
*structrkisp_device-ISPplatformdevice
*@base_addr:baseregisteraddress
*@active_sensor:sensorin-use,setwhenstreamingon
*@isp_sdev:ISPsub-device
*@cap_dev:imagecapturedevice
*@stats_vdev:ISPstatisticsoutputdevice
*@params_vdev:ISPinputparametersdevice
*@dmarx_dev:imageinputdevice
*@csi_dev:mipicsidevice
*@br_dev:bridgeofispandisppdevice
*/
structrkisp_device{
structlist_headlist;
void__iomem*base_addr;
structdevice*dev;
charname[128];
void*sw_base_addr;
structrkisp_hw_dev*hw_dev;
structv4l2_devicev4l2_dev;
structv4l2_ctrl_handlerctrl_handler;
structmedia_devicemedia_dev;
structv4l2_async_notifiernotifier;
structv4l2_subdev*subdevs[RKISP_SD_MAX];
structrkisp_sensor_info*active_sensor;
structrkisp_sensor_infosensors[RKISP_MAX_SENSOR];
intnum_sensors;
structrkisp_isp_subdevisp_sdev;
structrkisp_capture_devicecap_dev;
structrkisp_isp_stats_vdevstats_vdev;
structrkisp_isp_params_vdevparams_vdev;
structrkisp_dmarx_devicedmarx_dev;
structrkisp_csi_devicecsi_dev;
structrkisp_bridge_devicebr_dev;
structrkisp_luma_vdevluma_vdev;
structproc_dir_entry*procfs;
structrkisp_pipelinepipe;
enumrkisp_isp_verisp_ver;
structrkisp_emd_dataemd_data_fifo[RKISP_EMDDATA_FIFO_MAX];
unsignedintemd_data_idx;
unsignedintemd_vc;
unsignedintemd_dt;
intvs_irq;
structgpio_desc*vs_irq_gpio;
structrkisp_hdrhdr;
unsignedintisp_state;
unsignedintisp_err_cnt;
unsignedintisp_isr_cnt;
unsignedintisp_inp;
structmutexapilock;/*mutextoserializethecallsofstream*/
structmutexiqlock;/*mutextoserializethecallsofiq*/
wait_queue_head_tsync_onoff;
dma_addr_tresmem_addr;
phys_addr_tresmem_pa;
size_tresmem_size;
intdev_id;
unsignedintskip_frame;
unsignedintirq_ends;
unsignedintirq_ends_mask;
boolsend_fbcgain;
structrkisp_ispp_buf*cur_fbcgain;
structrkisp_buffer*cur_spbuf;
boolis_thunderboot;
structkfifordbk_kfifo;
spinlock_trdbk_lock;
intrdbk_cnt;
intrdbk_cnt_x1;
intrdbk_cnt_x2;
intrdbk_cnt_x3;
u32rd_mode;
u8filt_state[RDBK_F_MAX];
};
其中与isp2.1拓扑结构相关的的几个结构体成员以及他们之间的关系:
成员含义拓扑图中的entity设备名void __iomem *base_addr基地址--struct rkisp_sensor_info *active_sensor;正在使用的sensor--struct rkisp_isp_subdev isp_sdev;isp模块rkisp-isp-subdevv4l-subdev0struct rkisp_capture_device cap_dev;capture模块, 维护struct vb2_v4l2_buffer对应拓扑图中的rkisp_mainpath、rkisp_selfpath、rkisp_rawwr0、rkisp_rawwr2、rkisp_rawwr3video0~video4struct rkisp_isp_stats_vdev stats_vdev;数据统计模块rkisp-statisticsvideo7struct rkisp_isp_params_vdev params_vdev;参数配置模块rkisp-input-paramsvideo8struct rkisp_dmarx_device dmarx_dev;dma数据接收模块rkisp_rawrd0_m、rkisp_rawrd2_svideo5、video6struct rkisp_csi_device csi_dev;csi的sub device从设备rkisp-csi-subdevv4l-subdev1struct rkisp_bridge_device br_dev;桥接模块备,isp2.0中有--enum rkisp_isp_ver isp_ver;isp版本号,rk3568是2.1--要添加到拓扑结构中,表示该模块的结构体中包含成员struct media_pad ,它和struct v4l2_subdev中的 struct media_entity entity;共同生成拓扑结构。
rkisp-csi-subdev设备结构体定义如下:
structrkisp_csi_device{
structrkisp_device*ispdev;
structv4l2_subdevsd;
structmedia_padpads[CSI_PAD_MAX];
structsink_infosink[CSI_PAD_MAX-1];
intmax_pad;
u32err_cnt;
u32irq_cnt;
u8mipi_di[CSI_PAD_MAX-1];
u8tx_first[HDR_DMA_MAX];
};
参考第二节的拓扑图中entity6:
由上图可知,该模块有6个pad,pad属性定义如下
#defineMEDIA_PAD_FL_SINK(1<<0)
#defineMEDIA_PAD_FL_SOURCE(1<<1)
#defineMEDIA_PAD_FL_MUST_CONNECT(1<<2)
pad的名称定义如下:
enumrkisp_csi_pad{
CSI_SINK=0,
CSI_SRC_CH0,
CSI_SRC_CH1,
CSI_SRC_CH2,
CSI_SRC_CH3,
CSI_SRC_CH4,
CSI_PAD_MAX
};
isp的in pad
//isp的inpad
enumrkisp_isp_inp{
INP_INVAL=0,
INP_RAWRD0=BIT(0),
INP_RAWRD1=BIT(1),
INP_RAWRD2=BIT(2),
INP_CSI=BIT(4),
INP_DVP=BIT(5),
INP_DMARX_ISP=BIT(6),
INP_LVDS=BIT(7),
INP_CIF=BIT(8),
};
根据该拓扑图,pads[0]为sink,pads[1~5]均为source
以下是驱动中pad初始化代码:
rkisp_register_csi_subdev()
{
……
v4l2_subdev_init(sd,rkisp_csi_ops);
sd->flags|=V4L2_SUBDEV_FL_HAS_DEVNODE;//是否需要子节点
sd->entity.ops=rkisp_csi_media_ops;
sd->entity.function=MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN;
snprintf(sd->name,sizeof(sd->name),CSI_DEV_NAME);//名字前缀,#defineCSI_DEV_NAMEDRIVER_NAME"-csi-subdev"
csi_dev->pads[CSI_SINK].flags=
MEDIA_PAD_FL_SINK|MEDIA_PAD_FL_MUST_CONNECT;//pad0属性
csi_dev->pads[CSI_SRC_CH0].flags=
MEDIA_PAD_FL_SOURCE|MEDIA_PAD_FL_MUST_CONNECT;//pad1属性
csi_dev->max_pad=CSI_SRC_CH0+1;
if(dev->isp_ver==ISP_V20||dev->isp_ver==ISP_V21){
csi_dev->max_pad=CSI_PAD_MAX;
csi_dev->pads[CSI_SRC_CH1].flags=MEDIA_PAD_FL_SOURCE;//pad2属性
csi_dev->pads[CSI_SRC_CH2].flags=MEDIA_PAD_FL_SOURCE;//pad3属性
csi_dev->pads[CSI_SRC_CH3].flags=MEDIA_PAD_FL_SOURCE;//pad4属性
csi_dev->pads[CSI_SRC_CH4].flags=MEDIA_PAD_FL_SOURCE;//pad5属性
}
ret=media_entity_pads_init(sd->entity,csi_dev->max_pad,
csi_dev->pads);
……
}
一些关键的宏汇总:
//各个模块对应的名字
【kernel\drivers\media\platform\rockchip\isp\dev.h】
#defineDRIVER_NAME"rkisp"
#defineISP_VDEV_NAMEDRIVER_NAME"_ispdev"
#defineSP_VDEV_NAMEDRIVER_NAME"_selfpath"
#defineMP_VDEV_NAMEDRIVER_NAME"_mainpath"
#defineDMA_VDEV_NAMEDRIVER_NAME"_dmapath"
#defineRAW_VDEV_NAMEDRIVER_NAME"_rawpath"
#defineDMATX0_VDEV_NAMEDRIVER_NAME"_rawwr0"
#defineDMATX1_VDEV_NAMEDRIVER_NAME"_rawwr1"
#defineDMATX2_VDEV_NAMEDRIVER_NAME"_rawwr2"
#defineDMATX3_VDEV_NAMEDRIVER_NAME"_rawwr3"
#defineDMARX0_VDEV_NAMEDRIVER_NAME"_rawrd0_m"
#defineDMARX1_VDEV_NAMEDRIVER_NAME"_rawrd1_l"
#defineDMARX2_VDEV_NAMEDRIVER_NAME"_rawrd2_s"
#defineGRP_ID_SENSORBIT(0)
#defineGRP_ID_MIPIPHYBIT(1)
#defineGRP_ID_ISPBIT(2)
#defineGRP_ID_ISP_MPBIT(3)
#defineGRP_ID_ISP_SPBIT(4)
#defineGRP_ID_ISP_DMARXBIT(5)
#defineGRP_ID_ISP_BRIDGEBIT(6)
#defineGRP_ID_CSIBIT(7)
//pad的属性
[kernel\include\uapi\linux\media.h]
#defineMEDIA_PAD_FL_SINK(1<<0)
#defineMEDIA_PAD_FL_SOURCE(1<<1)
#defineMEDIA_PAD_FL_MUST_CONNECT(1<<2)
由代码可得,拓扑关系由csi_dev->pads描述。
最终调用函数media_entity_pads_init()注册。
rkisp_register_platform_subdevs()
isp_subdev_notifier()
v4l2_async_notifier_parse_fwnode_endpoints()
__v4l2_async_notifier_parse_fwnode_endpoints()
{
for(fwnode=fwnode_graph_get_next_endpoint())
{
dev_fwnode=fwnode_graph_get_port_parent(fwnode);
is_available=fwnode_device_is_available(dev_fwnode);
fwnode_handle_put(dev_fwnode);
fwnode_graph_parse_endpoint(fwnode,ep);
}
for(fwnode=fwnode_graph_get_next_endpoint())
{
dev_fwnode=fwnode_graph_get_port_parent(fwnode);
is_available=fwnode_device_is_available(dev_fwnode);
fwnode_handle_put(dev_fwnode);
fwnode_graph_parse_endpoint(fwnode,ep);
v4l2_async_notifier_fwnode_parse_endpoint();
}
fwnode_handle_put(fwnode);
}
大家也可以试着去分析其他的模块。
瑞芯微MIPI-CSI设备树节点属性说明参考内核说明文档:
[kernel\Documentation\devicetree\bindings\media\]
video-interfaces.txt关于sensor节点属性的说明,接口类型,
rockchip-isp1.txtisp模块属性说明
rockchip-mipi-dphy.txtdphy模块的说明
kernel\Documentation\devicetree\bindings\media\i2c\ovxxxxxx.txtov系列的摄像设备树说明
这些文档中有关于port、remote-endpoint等节点的详细说明,如果不是厂家,我们只需要搞懂摄像头拓扑结构即可。
我们移植的摄像头为ov13850,他的连接关系如下:
外设摄像头拓扑关系由设备树来描述,内核会自动解析并帮我们自动注册。
千言万语,不如一图:
由上图可得:
文中各种mipi技术文档,后台回复关键字:mipi
后面还会继续更新几篇Camera文章,
建议大家订阅本专题!
也可以后台留言,加一口君好友yikoupeng,
拉你进高质量技术交流群。
一口Linux
关注,回复【1024】海量Linux资料赠送
新闻来源:一口Linux,文中所述为作者独立观点,不代表icspec立场。更多精彩资讯请下载icspec App。如对本稿件有异议,请联系微信客服specltkj。
暂无评论哦,快来评论一下吧!

2026-07-23

2026-06-10