拓扑结构是我们了解MIPI-CSI内部模块以及与摄像头连接关系的最直观最便捷的方法。
file视角
v4l2视角
来自:
参考文档《RKISP_Driver_User_Manual_v1.3.pdf》
在/sys/class/video4linux/下可以找到v4l2相关的设备节点:
rk3568_r:/ # ls sys/class/video4linux
ls sys/class/video4linux
v4l-subdev0 v4l-subdev2 video1 video3 video5 video7
v4l-subdev1 video0 video2 video4 video6 video8rk3568_r:/ # cat sys/class/video4linux/video0/dev
cat sys/class/video4linux/video0/dev
81:0
rk3568_r:/ # cat sys/class/video4linux/video0/name
cat sys/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---- 1 media camera 81, 0 2022-11-09 17:06 /dev/video0
crw-rw---- 1 media camera 81, 1 2022-11-09 17:06 /dev/video1
crw-rw---- 1 media camera 81, 2 2022-11-09 17:06 /dev/video2
crw-rw---- 1 media camera 81, 3 2022-11-09 17:06 /dev/video3
crw-rw---- 1 media camera 81, 4 2022-11-09 17:06 /dev/video4
crw-rw---- 1 media camera 81, 5 2022-11-09 17:06 /dev/video5
crw-rw---- 1 media camera 81, 6 2022-11-09 17:06 /dev/video6
crw-rw---- 1 media camera 81, 7 2022-11-09 17:06 /dev/video7
crw-rw---- 1 media camera 81, 8 2022-11-09 17:06 /dev/video8rk3568_r:/ # ls /dev/v4l-sub* -l
ls /dev/v4l-sub* -l
crw-rw-rw- 1 media camera 81, 9 2022-11-09 17:06 /dev/v4l-subdev0
crw-rw-rw- 1 media camera 81, 10 2022-11-09 17:06 /dev/v4l-subdev1
crw-rw-rw- 1 media camera 81, 11 2022-11-09 17:06 /dev/v4l-subdev2
命令media-ctl可以查看拓扑结构图
rk3568_r:/ # media-ctl -d /dev/media0 -p
media-ctl -d /dev/media0 -p
Opening media device /dev/media0
Enumerating entities
Found 13 entities
Enumerating pads and links
Media controller API version 0.0.255 Media device information
------------------------
driver rkisp-vir0
model rkisp0
serial
bus info
hw revision 0x0
driver version 0.0.255 Device topology
- entity 1: rkisp-isp-subdev (4 pads, 7 links) type V4L2 subdev subtype Unknown device node name /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 [] - entity 6: rkisp-csi-subdev (6 pads, 5 links) type V4L2 subdev subtype Unknown device node name /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 [] - entity 13: rkisp_mainpath (1 pad, 1 link) type Node subtype V4L device node name /dev/video0 pad0: Sink <- "rkisp-isp-subdev":2 [] - entity 19: rkisp_selfpath (1 pad, 1 link) type Node subtype V4L device node name /dev/video1 pad0: Sink <- "rkisp-isp-subdev":2 [] - entity 25: rkisp_rawwr0 (1 pad, 1 link) type Node subtype V4L device node name /dev/video2 pad0: Sink <- "rkisp-csi-subdev":2 [] - entity 31: rkisp_rawwr2 (1 pad, 1 link) type Node subtype V4L device node name /dev/video3 pad0: Sink <- "rkisp-csi-subdev":4 [] - entity 37: rkisp_rawwr3 (1 pad, 1 link) type Node subtype V4L device node name /dev/video4 pad0: Sink <- "rkisp-csi-subdev":5 [] - entity 43: rkisp_rawrd0_m (1 pad, 1 link) type Node subtype V4L device node name /dev/video5 pad0: Source -> "rkisp-isp-subdev":0 [] - entity 49: rkisp_rawrd2_s (1 pad, 1 link) type Node subtype V4L device node name /dev/video6 pad0: Source -> "rkisp-isp-subdev":0 [] - entity 55: rkisp-statistics (1 pad, 1 link) type Node subtype V4L device node name /dev/video7 pad0: Sink <- "rkisp-isp-subdev":3 [] - entity 61: rkisp-input-params (1 pad, 1 link) type Node subtype V4L device node name /dev/video8 pad0: Source -> "rkisp-isp-subdev":1 [] - entity 67: rockchip-csi2-dphy0 (2 pads, 2 links) type V4L2 subdev subtype Unknown device node name /dev/v4l-subdev2 pad0: Sink <- "m00_b_ov13850 4-0010":0 [] pad1: Source -> "rkisp-csi-subdev":0 [] - entity 70: m00_b_ov13850 4-0010 (1 pad, 1 link) type V4L2 subdev subtype Sensor device node name /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管理所有的资源。
/** struct rkisp_device - ISP platform device* @base_addr: base register address* @active_sensor: sensor in-use, set when streaming on* @isp_sdev: ISP sub-device* @cap_dev: image capture device* @stats_vdev: ISP statistics output device* @params_vdev: ISP input parameters device* @dmarx_dev: image input device* @csi_dev: mipi csi device* @br_dev: bridge of isp and ispp device*/
struct rkisp_device {struct list_head list;void __iomem *base_addr;struct device *dev;char name[128];void *sw_base_addr;struct rkisp_hw_dev *hw_dev; struct v4l2_device v4l2_dev;struct v4l2_ctrl_handler ctrl_handler;struct media_device media_dev;struct v4l2_async_notifier notifier;struct v4l2_subdev *subdevs[RKISP_SD_MAX];struct rkisp_sensor_info *active_sensor;struct rkisp_sensor_info sensors[RKISP_MAX_SENSOR];int num_sensors;struct rkisp_isp_subdev isp_sdev;struct rkisp_capture_device cap_dev;struct rkisp_isp_stats_vdev stats_vdev;struct rkisp_isp_params_vdev params_vdev;struct rkisp_dmarx_device dmarx_dev;struct rkisp_csi_device csi_dev;struct rkisp_bridge_device br_dev;struct rkisp_luma_vdev luma_vdev;struct proc_dir_entry *procfs;struct rkisp_pipeline pipe;enum rkisp_isp_ver isp_ver;struct rkisp_emd_data emd_data_fifo[RKISP_EMDDATA_FIFO_MAX];unsigned int emd_data_idx;unsigned int emd_vc;unsigned int emd_dt;int vs_irq;struct gpio_desc *vs_irq_gpio;struct rkisp_hdr hdr;unsigned int isp_state;unsigned int isp_err_cnt;unsigned int isp_isr_cnt;unsigned int isp_inp;struct mutex apilock; /* mutex to serialize the calls of stream */struct mutex iqlock; /* mutex to serialize the calls of iq */wait_queue_head_t sync_onoff;dma_addr_t resmem_addr;phys_addr_t resmem_pa;size_t resmem_size;int dev_id;unsigned int skip_frame;unsigned int irq_ends;unsigned int irq_ends_mask;bool send_fbcgain;struct rkisp_ispp_buf *cur_fbcgain;struct rkisp_buffer *cur_spbuf;bool is_thunderboot;struct kfifo rdbk_kfifo;spinlock_t rdbk_lock;int rdbk_cnt;int rdbk_cnt_x1;int rdbk_cnt_x2;int rdbk_cnt_x3;u32 rd_mode;u8 filt_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-subdev | v4l-subdev0 |
struct rkisp_capture_device cap_dev; | capture模块, 维护struct vb2_v4l2_buffer | 对应拓扑图中的rkisp_mainpath、rkisp_selfpath、rkisp_rawwr0、rkisp_rawwr2、rkisp_rawwr3 | video0~video4 |
struct rkisp_isp_stats_vdev stats_vdev; | 数据统计模块 | rkisp-statistics | video7 |
struct rkisp_isp_params_vdev params_vdev; | 参数配置模块 | rkisp-input-params | video8 |
struct rkisp_dmarx_device dmarx_dev; | dma数据接收模块 | rkisp_rawrd0_m、rkisp_rawrd2_s | video5、video6 |
struct rkisp_csi_device csi_dev; | csi的sub device从设备 | rkisp-csi-subdev | v4l-subdev1 |
struct 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设备结构体定义如下:
struct rkisp_csi_device {struct rkisp_device *ispdev;struct v4l2_subdev sd;struct media_pad pads[CSI_PAD_MAX];struct sink_info sink[CSI_PAD_MAX - 1];int max_pad;u32 err_cnt;u32 irq_cnt;u8 mipi_di[CSI_PAD_MAX - 1];u8 tx_first[HDR_DMA_MAX];
};
参考第二节的拓扑图中 entity6 :
由上图可知,该模块有6个pad,pad属性定义如下
#define MEDIA_PAD_FL_SINK (1 << 0)
#define MEDIA_PAD_FL_SOURCE (1 << 1)
#define MEDIA_PAD_FL_MUST_CONNECT (1 << 2)
pad的名称定义如下:
enum rkisp_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的in pad
enum rkisp_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);//名字前缀,#define CSI_DEV_NAME DRIVER_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】
#define DRIVER_NAME "rkisp"
#define ISP_VDEV_NAME DRIVER_NAME "_ispdev"
#define SP_VDEV_NAME DRIVER_NAME "_selfpath"
#define MP_VDEV_NAME DRIVER_NAME "_mainpath"
#define DMA_VDEV_NAME DRIVER_NAME "_dmapath"
#define RAW_VDEV_NAME DRIVER_NAME "_rawpath"
#define DMATX0_VDEV_NAME DRIVER_NAME "_rawwr0"
#define DMATX1_VDEV_NAME DRIVER_NAME "_rawwr1"
#define DMATX2_VDEV_NAME DRIVER_NAME "_rawwr2"
#define DMATX3_VDEV_NAME DRIVER_NAME "_rawwr3"
#define DMARX0_VDEV_NAME DRIVER_NAME "_rawrd0_m"
#define DMARX1_VDEV_NAME DRIVER_NAME "_rawrd1_l"
#define DMARX2_VDEV_NAME DRIVER_NAME "_rawrd2_s"#define GRP_ID_SENSOR BIT(0)
#define GRP_ID_MIPIPHY BIT(1)
#define GRP_ID_ISP BIT(2)
#define GRP_ID_ISP_MP BIT(3)
#define GRP_ID_ISP_SP BIT(4)
#define GRP_ID_ISP_DMARX BIT(5)
#define GRP_ID_ISP_BRIDGE BIT(6)
#define GRP_ID_CSI BIT(7)//pad的属性
[kernel\include\uapi\linux\media.h]
#define MEDIA_PAD_FL_SINK (1 << 0)
#define MEDIA_PAD_FL_SOURCE (1 << 1)
#define MEDIA_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.txt isp模块属性说明
rockchip-mipi-dphy.txt dphy模块的说明
kernel\Documentation\devicetree\bindings\media\i2c\ovxxxxxx.txt ov系列的摄像设备树说明
这些文档中有关于port、remote-endpoint等节点的详细说明,如果不是厂家,我们只需要搞懂摄像头拓扑结构即可。
我们移植的摄像头为ov13850,他的连接关系如下:
外设摄像头拓扑关系由设备树来描述,内核会自动解析并帮我们自动注册。
千言万语,不如一图:
由上图可得:
文中各种mipi技术文档,后台回复关键字:mipi
后面还会继续更新几篇Camera文章,
建议大家订阅本专题!
也可以后台留言,加一口君好友yikoupeng,
拉你进高质量技术交流群。
上一篇:CSP202209-3防疫大数据
下一篇:NumPy入门介绍