一、 設(shè)備節(jié)點、模塊、拓撲結(jié)構(gòu)關(guān)系
拓撲結(jié)構(gòu)是我們了解MIPI-CSI內(nèi)部模塊以及與攝像頭連接關(guān)系的最直觀最便捷的方法。
- 如何表示拓撲結(jié)構(gòu)?


來自: 參考文檔《RKISP_Driver_User_Manual_v1.3.pdf》
- 模塊之間相互獨立,通過struct media_entity來進行抽象,通常會將struct media_entity嵌入到其他結(jié)構(gòu)中,以支持media framework功能;
- entity模塊包含struct media_pad,pad可以認為是端口,與其他模塊進行聯(lián)系的媒介,針對特定模塊來說它是確定的;
- pad通過struct media_link來建立連接,指定source和sink,即可將通路建立起來;
- 各個模塊之間最終建立一條數(shù)據(jù)流,便是一條pipeline了,同一條pipeline中的模塊,可以根據(jù)前一個模塊查找到下一個模塊,因此也可以很方便進行遍歷,并做進一步的設(shè)置操作;
2. 設(shè)備節(jié)點-------少media的
在/sys/class/video4linux/下可以找到v4l2相關(guān)的設(shè)備節(jié)點:
rk3568_r:/ # ls sys/class/video4linux
ls sys/class/video4linux
v4l-subdev0 v4l-subdev2 video1 video3 video5 video7
v4l-subdev1 video0 video2 video4 video6 video8
rk3568_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文件系統(tǒng)會為我們在dev/目錄下創(chuàng)建一個video0節(jié)點,即dev/video0
用戶可以打開dev/video0節(jié)點,通過IOCTL命令和內(nèi)核空間進行通信。
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/video8
rk3568_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
3. 拓撲結(jié)構(gòu)圖
命令media-ctl可以查看拓撲結(jié)構(gòu)圖
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 []
下面是根據(jù)顯示內(nèi)容繪制的拓撲圖:

拓撲結(jié)構(gòu)
該圖中各個entity對應(yīng)的設(shè)備節(jié)點名稱已經(jīng)標注。模塊的上方的黃色pad默認是source pad,下方的黃色pad是sink pad
字符設(shè)備類型主要有兩種(只考慮攝像頭):
- /dev/videox (x取值0~8) (所有設(shè)備共用主設(shè)備號81,次設(shè)備號區(qū)分)
- /dev/v4l-subdevx (x取值0~3)
video設(shè)備主要用于圖像操作,必須創(chuàng)建結(jié)構(gòu)體struct video_device變量, v4l-subdev設(shè)備主要對應(yīng)sensor等具體從設(shè)備,必須創(chuàng)建struct v4l2_subdev變量, 內(nèi)部的isp和csi、csi-dphy也都需要注冊為subdev
這些entity由media_entity模塊負責維護,將他們連接起來。
4. 模塊功能
這些entity瑞芯微已經(jīng)設(shè)定了他們各自的功能:


這些entity我們可以理解為一個個功能模塊。
這些功能模塊有的用于驅(qū)動csi、有的驅(qū)動isp、有的用于預覽圖像、有的用于統(tǒng)計視頻信息、有的用于配置參數(shù)。
這些功能模塊,并不是都一定每個camera控制器都有的,有一些是通用的,比如,mainpath、selfpath,有一些要完全看SoC設(shè)計,即使瑞芯微的SoC,不同型號,差別也不小。所以具體問題要具體分析,不可教條。
v4l2只定義了基本架構(gòu),定義好了回調(diào)函數(shù)接口,要實現(xiàn)模塊具體功能只需要填充好對應(yīng)的回調(diào)函數(shù)即可,應(yīng)用層通過這些字符設(shè)備文件和對應(yīng)的ioctrl命令,就可以實現(xiàn)相應(yīng)的功能。
二、 如何描述拓撲?
1. struct rkisp_device
rk3568的camera控制器使用結(jié)構(gòu)體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拓撲結(jié)構(gòu)相關(guān)的的幾個結(jié)構(gòu)體成員以及他們之間的關(guān)系:
成員 | 含義 | 拓撲圖中的entity | 設(shè)備名 |
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 | 對應(yīng)拓撲圖中的rkisp_mainpath、rkisp_selfpath、rkisp_rawwr0、rkisp_rawwr2、rkisp_rawwr3 | video0~video4 |
struct rkisp_isp_stats_vdev stats_vdev; | 數(shù)據(jù)統(tǒng)計模塊 | rkisp-statistics | video7 |
struct rkisp_isp_params_vdev params_vdev; | 參數(shù)配置模塊 | rkisp-input-params | video8 |
struct rkisp_dmarx_device dmarx_dev; | dma數(shù)據(jù)接收模塊 | rkisp_rawrd0_m、rkisp_rawrd2_s | video5、video6 |
struct rkisp_csi_device csi_dev; | csi的sub device從設(shè)備 | rkisp-csi-subdev | v4l-subdev1 |
struct rkisp_bridge_device br_dev; | 橋接模塊備,isp2.0中有 | - | - |
enum rkisp_isp_ver isp_ver; | isp版本號,rk3568是2.1 | - | - |
2. 舉例1:rkisp-csi-subdev注冊到拓撲結(jié)構(gòu)中
要添加到拓撲結(jié)構(gòu)中,表示該模塊的結(jié)構(gòu)體中包含成員struct media_pad ,它和struct v4l2_subdev中的 struct media_entity entity;共同生成拓撲結(jié)構(gòu)。
rkisp-csi-subdev設(shè)備結(jié)構(gòu)體定義如下:
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];
};
參考第二節(jié)的拓撲圖中 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),
};
根據(jù)該拓撲圖,pads[0] 為sink ,pads[1~5] 均為source
以下是驅(qū)動中pad初始化代碼:
rkisp_register_csi_subdev()
{
……
v4l2_subdev_init(sd, &rkisp_csi_ops);
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; //是否需要子節(jié)點
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);
……
}
一些關(guān)鍵的宏匯總:
//各個模塊對應(yīng)的名字
【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)
由代碼可得,拓撲關(guān)系由csi_dev->pads描述。
最終調(diào)用函數(shù)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);
}
大家也可以試著去分析其他的模塊。
三、設(shè)備樹如何描述攝像頭拓撲結(jié)構(gòu)?
1. 設(shè)備樹說明文檔
瑞芯微MIPI-CSI設(shè)備樹節(jié)點屬性說明參考內(nèi)核說明文檔:
[kernel\Documentation\devicetree\bindings\media\]
video-interfaces.txt 關(guān)于sensor節(jié)點屬性的說明,接口類型,
rockchip-isp1.txt isp模塊屬性說明
rockchip-mipi-dphy.txt dphy模塊的說明
kernel\Documentation\devicetree\bindings\media\i2c\ovxxxxxx.txt ov系列的攝像設(shè)備樹說明
這些文檔中有關(guān)于port、remote-endpoint等節(jié)點的詳細說明,如果不是廠家,我們只需要搞懂攝像頭拓撲結(jié)構(gòu)即可。
2. ov13850
我們移植的攝像頭為ov13850,他的連接關(guān)系如下:
- 數(shù)據(jù)通道通過mipi接口連接到rk3568的mipi通道0
- Camera控制器的csi2-dphy0模塊負責從mipi通道中接收數(shù)據(jù)幀
外設(shè)攝像頭拓撲關(guān)系由設(shè)備樹來描述,內(nèi)核會自動解析并幫我們自動注冊。
千言萬語,不如一圖:

由上圖可得:
- 攝像頭ov13850設(shè)備樹只有一個port子節(jié)點,所以pad為0,out表示該pad是source pad
- remote-endpoint屬性表示該pad連接的上游模塊的pad名字:mipi_in_ucam0,而模塊csi2_dphy0中包含pad:mipi_in_ucam0,所以ov13850連接到該模塊
- csi2_dphy0 port0節(jié)點的mipi_in_ucam0設(shè)備,通過remote-endpoint即可知道該pad所連接的是設(shè)備ov13850的pad
- 綜上可得csi2_dphy0的pad0(sink pad)連接的ov13850的pad0(source pad)
