Camera | 6.v4l2拓撲架構

一口linux 發佈 2024-03-29T02:01:20.234515+00:00

一、 設備節點、模塊、拓撲結構關係拓撲結構是我們了解MIPI-CSI內部模塊以及與攝像頭連接關係的最直觀最便捷的方法。1. 如何表示拓撲結構?file視角v4l2視角來自: 參考文檔《RKISP_Driver_User_Manual_v1.3.

一、 設備節點、模塊、拓撲結構關係

拓撲結構是我們了解MIPI-CSI內部模塊以及與攝像頭連接關係的最直觀最便捷的方法。

1. 如何表示拓撲結構?

  • file視角
  • v4l2視角

來自: 參考文檔《RKISP_Driver_User_Manual_v1.3.pdf》

  • 模塊之間相互獨立,通過struct media_entity來進行抽象,通常會將struct media_entity嵌入到其他結構中,以支持media framework功能;
  • entity模塊包含struct media_pad,pad可以認為是埠,與其他模塊進行聯繫的媒介,針對特定模塊來說它是確定的;
  • pad通過struct media_link來建立連接,指定source和sink,即可將通路建立起來;
  • 各個模塊之間最終建立一條數據流,便是一條pipeline了,同一條pipeline中的模塊,可以根據前一個模塊查找到下一個模塊,因此也可以很方便進行遍歷,並做進一步的設置操作;

2. 設備節點-------少media的

在/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  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文件系統會為我們在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/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.拓撲結構圖

命令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

字符設備類型主要有兩種(只考慮攝像頭):

  • /dev/videox (x取值0~8) (所有設備共用主設備號81,次設備號區分)
  • /dev/v4l-subdevx (x取值0~3)

video設備主要用於圖像操作,必須創建結構體struct video_device變量, v4l-subdev設備主要對應sensor等具體從設備,必須創建struct v4l2_subdev變量, 內部的isp和csi、csi-dphy也都需要註冊為subdev

這些entity由media_entity模塊負責維護,將他們連接起來。

4. 模塊功能

這些entity瑞芯微已經設定了他們各自的功能:




這些entity我們可以理解為一個個功能模塊。

這些功能模塊有的用於驅動csi、有的驅動isp、有的用於預覽圖像、有的用於統計視頻信息、有的用於配置參數。

這些功能模塊,並不是都一定每個camera控制器都有的,有一些是通用的,比如,mainpath、selfpath,有一些要完全看SoC設計,即使瑞芯微的SoC,不同型號,差別也不小。所以具體問題要具體分析,不可教條。

v4l2隻定義了基本架構,定義好了回調函數接口,要實現模塊具體功能只需要填充好對應的回調函數即可,應用層通過這些字符設備文件和對應的ioctrl命令,就可以實現相應的功能。

二、 如何描述拓撲?

1. struct rkisp_device

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 設備名



2. 舉例1:rkisp-csi-subdev註冊到拓撲結構中

要添加到拓撲結構中,表示該模塊的結構體中包含成員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);   
   }

大家也可以試著去分析其他的模塊。

三、設備樹如何描述攝像頭拓撲結構?

1. 設備樹說明文檔

瑞芯微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等節點的詳細說明,如果不是廠家,我們只需要搞懂攝像頭拓撲結構即可。

2. ov13850

我們移植的攝像頭為ov13850,他的連接關係如下:

  • 數據通道通過mipi接口連接到rk3568的mipi通道0
  • Camera控制器的csi2-dphy0模塊負責從mipi通道中接收數據幀

外設攝像頭拓撲關係由設備樹來描述,內核會自動解析並幫我們自動註冊。

千言萬語,不如一圖:



由上圖可得:

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