因為有拍照、錄制視頻、直播等剛需,現在手機的攝像頭基本都是高清,支持高清攝像頭的SoC都支持MIPI-CSI。
不同SoC的MIPI-CSI在實現上有一定差別,即使同一廠家設計生產的芯片也都不盡相同。
本文基于瑞芯微rk3568平臺evb1公板為例來詳細講解MIPI-CSI/DPHY驅動。
閱讀本文之前,建議大家先仔細學習前面幾篇文章。
一、rk3568硬件模塊部分
驅動的研究往往要先從硬件著手,下面我首先看下rk3568公板電路。
1)電路圖
由電路圖可得攝像頭與SoC的MIPI-CSI接口,可以是x4lane,也可以是x2lane,data和clk都是差分信號。
如果不了解,建議問下硬件工程師。
控制攝像頭接口是I2C接口,并且位于I2C通道4下。
2)rk3568內部MIPI相關模塊圖
電路圖只能查看SoC的MIPI控制器與攝像頭的接口關系,下面我們來看下rk3568內部與mipi相關的模塊。
吐槽一下瑞芯微的文檔,一言難盡,我嚴重懷疑廠家壓根就不想讓其他人真正搞懂他們的SDK,這樣好收每年的技術支持費用,高通這損招是讓丫徹底學明白了。
由于官方給的手冊要么有錯誤,要么就是有些圖片錯誤,就不截圖了。
下圖是根據官方手冊說明,整理的內部模塊圖。
- Sensor輸出數據流通過MIPI的lanes傳輸給rk3568的DPHY控制器
- CSI控制器從硬件中提取出圖像數據
- VICAP從MIPI接口讀取數據
- 然后將數據傳遞給給ISP ,ISP 再輸出經過一系列圖像處理算法后得到圖像。
- MP用于預覽圖像
- SP用于縮放
- VICAP Video Capture通過DVP/MIPI接口從攝像頭讀取數據,并通過AXI總線將數據傳輸到主存中。
VICAP特性:
支持 BT601 YCbCr 422 8bit 、 RAW 8/10/12bit 輸入支持 BT656 YCbCr 422 8bit 輸入支持 BT1120 YCbCr 422 8bit 輸入 , 單/雙邊 取樣支持 2/4 mixed BT656/BT1120 YCbCr 422 8bit input支持 YUYV 序列的配置支持 the polarity of pixel_clk, hsync and vsync configurable支持接收 CSI2 協議的數據(最多4個IDs)支持接收 DSI 協議的數據(Video mode/Command mode)支持窗口裁剪支持virtual stride when write to DDR支持輸出NV16/NV12格式的YUV數據支持compact/non-compact output for RAW data支持MMU
- ISP(圖像信號處理)
ISP是一個完整的視頻和靜止圖像輸入設備。 這個模塊支持集成YCbCr處理圖像傳感器和簡單CMOS傳感器 ,提交沒有任何綜合圖像處理Bayer RGB模式圖像。
rk3568采用的是ISP21版本。
ISP21 包含了一系列的圖像處理算法模塊,主要包括:暗電流矯正、壞點矯正、3A、HDR、鏡頭陰影矯 正、鏡頭畸變矯正、3DLUT、去噪(包括RAW域去噪,多幀降噪,顏色去噪等)、銳化等。
ISP21包括硬件算法實現及軟件邏輯控制部分,RkAiq即為軟件邏輯控制部分的實現。
RkAiq不斷從ISP HW獲取統計數據,并經過3A等算法生成新的參數反饋給各硬件模塊。
RkAiq軟件模塊主要實現的功能為:從ISP驅動獲取圖像統計,結合IQ Tuning參數,使用一系列算法計 算出新的ISP、Sensor等硬件參數,不斷迭代該過程,最終達到最優的圖像效果。
3)CSI_RX、VICAP、ISP寄存器基地址
《Rockchip RK3568 TRM Part1 V1.1-20210301.pdf》
二、 瑞芯微MIPI-CSI設備樹分析
在rk3568中主要包含4個設備:
- isp-subdev: 圖像處理控制器,如3a處理,并將處理后的所得的參數反饋給sensor。
- csi-subdev: mipi數據解析控制器。
- cis2-dphy: mipi數據硬件接收控制器。
- sensor: 外接的sensor,支持mipi輸出。
下面我看下瑞芯微MIPI-CSI是如何用設備樹描述的。
1. 內核中相關MIPI設備樹說明文檔
瑞芯微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系列的攝像設備樹說明
2. 設備樹節點說明
rk3568的MIPI-CSI用到的所有的設備樹節點:
a) rockchip,rkisp-vir
rkisp_vir0: rkisp-vir0 { compatible = "rockchip,rkisp-vir"; rockchip,hw = <&rkisp>; status = "disabled";};
該設備樹信息對應的初始化函數
[kernel\drivers\media\platform\rockchip\isp\dev.c]struct platform_driver rkisp_plat_drv = { .driver = { .name = DRIVER_NAME, .of_match_table = of_match_ptr(rkisp_plat_of_match), .pm = &rkisp_plat_pm_ops, }, .probe = rkisp_plat_probe, .remove = rkisp_plat_remove,};
該節點用于初始化isp相關的組件,
驅動程序會創建拓撲圖中的 rkisp-isp-subdev、rkisp-csi-subdev、rkisp_mainpath、rkisp_selfpath、rkisp_rawwr0、rkisp_rawwr2、rkisp_rawwr3、rkisp_rawrd0_m、rkisp_rawrd2_s、rkisp-statistics、、rkisp-input-params 組件
isp硬件相關的信息在父節點**rockchip,hw = <&rkisp>;**中描述。
b) rkisp
rkisp: rkisp@fdff0000 { compatible = "rockchip,rk3568-rkisp"; reg = <0x0 0xfdff0000 0x0 0x10000>; interrupts = <GIC_SPI 57 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 58 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 60 IRQ_TYPE_LEVEL_HIGH>; //中斷使用的gpio,觸發方式高電平觸發 interrupt-names = "mipi_irq", "mi_irq", "isp_irq"; //中斷名稱 clocks = <&cru ACLK_ISP>, <&cru HCLK_ISP>, <&cru CLK_ISP>; //時鐘 clock-names = "aclk_isp", "hclk_isp", "clk_isp"; //時鐘名稱 resets = <&cru SRST_ISP>, <&cru SRST_H_ISP>; reset-names = "isp", "isp-h"; rockchip,grf = <&grf>; power-domains = <&power RK3568_PD_VI>; //isp vicap電源和時鐘 iommus = <&rkisp_mmu>; //mmu屬性 rockchip,iq-feature = /bits/ 64 <0x3FBFFFE67FF>; status = "disabled";}; rkisp_mmu: iommu@fdff1a00 { compatible = "rockchip,iommu-v2"; reg = <0x0 0xfdff1a00 0x0 0x100>; interrupts = <GIC_SPI 59 IRQ_TYPE_LEVEL_HIGH>; interrupt-names = "isp_mmu"; clocks = <&cru ACLK_ISP>, <&cru HCLK_ISP>; clock-names = "aclk", "iface"; power-domains = <&power RK3568_PD_VI>; #iommu-cells = <0>; rockchip,disable-mmu-reset; status = "disabled"; }; pmu: power-management@fdd90000 { pd_vi@RK3568_PD_VI { reg = <RK3568_PD_VI>; clocks = <&cru HCLK_VI>, <&cru PCLK_VI>; pm_qos = <&qos_isp>, <&qos_vicap0>, <&qos_vicap1>; };};
該設備樹節點用于描述ISP硬件信息: 基地址0xfdff0000 、中斷源、時鐘、reset引腳、iommus等。
驅動提取對應的硬件信息,填充到struct rkisp_hw_dev結構體變量中。
對應驅動入口:
[kernel\drivers\media\platform\rockchip\isp\hw.c]static struct platform_driver rkisp_hw_drv = { .driver = { .name = "rkisp_hw", .of_match_table = of_match_ptr(rkisp_hw_of_match), .pm = &rkisp_hw_pm_ops, }, .probe = rkisp_hw_probe, .remove = rkisp_hw_remove, .shutdown = rkisp_hw_shutdown,};
c) CSI2協議相關設備樹
- csi2_dphy0 拓撲結構相關信息
- csi2_dphy_hw csi2驅動相關硬件信息
以下是描述csi2_dphy0拓撲信息,實際攝像頭信息需要用戶自己填寫:
[rk3568-evb1-ddr4-v10.dtsi]&csi2_dphy0 { status = "okay"; ports { #address-cells = <1>; #size-cells = <0>; port@0 { reg = <0>; #address-cells = <1>; #size-cells = <0>; mipi_in_ucam0: endpoint@1 { reg = <1>; remote-endpoint = <&0v13850_out>; data-lanes = <1 2 3 4>; }; }; port@1 { reg = <1>; #address-cells = <1>; #size-cells = <0>; csidphy_out: endpoint@0 { reg = <0>; remote-endpoint = <&isp0_in>; }; }; };};
該節點描述內容:
- 父節點csi2_dphy0 - port@n : 表示pad號為n- mipi_in_ucam0 : Sink Pad(in表示進入該entity,上游連接的設備由remote-endpoint給出,即攝像頭0v13850_out)- data-lanes : mipi通道數量:4- csidphy_out : Source Pad,下游連接的設備由remote-endpoint給出,即isp0_in
以下是csi2_dphy控制器相關硬件信息,位于瑞芯微3568平臺設備樹文件rk3568.dtsi中
[rk3568.dtsi]aliases { csi2dphy0 = &csi2_dphy0; ……}csi2_dphy0: csi2-dphy0 { compatible = "rockchip,rk3568-csi2-dphy"; rockchip,hw = <&csi2_dphy_hw>; status = "disabled";};csi2_dphy_hw: csi2-dphy-hw@fe870000 { compatible = "rockchip,rk3568-csi2-dphy-hw"; reg = <0x0 0xfe870000 0x0 0x1000>; clocks = <&cru PCLK_MIPICSIPHY>; clock-names = "pclk"; rockchip,grf = <&grf>; status = "disabled";};
csi2dphy0 對應的驅動入口為:
[kernel\drivers\phy\rockchip\phy-rockchip-csi2-dphy-hw.c]static struct platform_driver rockchip_csi2_dphy_hw_driver = { .probe = rockchip_csi2_dphy_hw_probe, .remove = rockchip_csi2_dphy_hw_remove, .driver = { .name = "rockchip-csi2-dphy-hw", .of_match_table = rockchip_csi2_dphy_hw_match_id, },};
在函數rockchip_csi2_dphy_hw_probe()中還會注冊結構體變量 rockchip_csi2_dphy_driver
630 platform_driver_register(&rockchip_csi2_dphy_driver);
rockchip_csi2_dphy_driver定義如下:
[kernel\drivers\phy\rockchip\phy-rockchip-csi2-dphy-hw.c]struct platform_driver rockchip_csi2_dphy_driver = { .probe = rockchip_csi2_dphy_probe, .remove = rockchip_csi2_dphy_remove, .driver = { .name = "rockchip-csi2-dphy", .pm = &rockchip_csi2_dphy_pm_ops, .of_match_table = rockchip_csi2_dphy_match_id, },};
分析驅動就要從這些入口函數probe開始分析。
三、驅動初始化
1. 驅動文件目錄
kernel ├── arch/arm64/boot/dts/rockchip DTS 配置文件
├── drivers/phy/rockchip/
├── phy-rockchip-csi2-dphy.c
└── phy-rockchip-csi2-dphy-hw.c mipi dphy 驅動
├── drivers/media|
├── platform/rockchip/isp rkisp isp 驅動
│
├── capture_v21.c 包含 mp/sp 的配置及 vb2,幀中斷處理
│
├── dev.c 包含 probe、異步注冊、 clock、 pipeline、iommu 及 media/v4l2 framework
│
├── isp_params_v21.c 3A 相關參數設置
│
├── isp_stats_v21.c 3A 相關統計
│
├── regs.c 寄存器相關的讀寫操作
│ └── rkisp.c 對應 isp_sd entity 節點,
│ 包含從 mipi 接收數據,并有 crop 功能
├── v4l2-core v4l2核心代碼
└── i2c/
└── ov13850.c CIS(cmos image sensor)驅動
注: 3568的isp版本是v21,只需要看v21結尾的文件
1. 字符設備號申請:videodev_init()
該函數主要用于申請設備號:
主設備號 :81 設備名 :video4linux 申請class:video4linux
#define VIDEO_MAJOR 81#define VIDEO_NUM_DEVICES 256#define VIDEO_NAME "video4linux"static struct class video_class = { .name = VIDEO_NAME, .dev_groups = video_device_groups,};static int __init videodev_init(void){ dev_t dev = MKDEV(VIDEO_MAJOR, 0); ret = register_chrdev_region(dev, VIDEO_NUM_DEVICES, VIDEO_NAME); ret = class_register(&video_class);}static void __exit videodev_exit(void){ dev_t dev = MKDEV(VIDEO_MAJOR, 0); class_unregister(&video_class); unregister_chrdev_region(dev, VIDEO_NUM_DEVICES);}
注意 為簡化起見,所有代碼只把最重要的部分列舉出來,后同。
2. isp架構初始化:rkisp_plat_probe()
該函數是最重要的一個初始化函數,除了rkisp_csi2_dphy(entity67)外,其他的功能部件都在該函數中初始化。
注冊rkisp-vir0父設備、isp-dubdev子設備、csi2-dev子設備等,由于rk3568支持多路sensor輸入,即isp支持多路處理,因此會虛擬多通道isp-virx。
該函數主要工作:
- 給isp_dev申請內存并初始化,該結構體用于camera控制器所有的信息
- 注冊v4l2_device結構體
- 初始化media相關資源
- 函數rkisp_register_platform_subdevs(),用于注冊拓撲結構中的各個模塊,對應的entity詳見圖
- entity1、7注冊為sub_device 初始化struct v4l2_subdev_ops、初始化media子模塊需要相關信息 entity1還會設置默認的圖像格式
- entity13、19、25、31、37、43、49、55、61注冊為video_device 填充struct v4l2_file_operations(struct video_device->fops)、struct v4l2_ioctl_ops(struct video_device->ioctl_ops),struct vb2_ops(struct video_device->vb2_queue->ops)
中注冊。
3. isp驅動初始化:rkisp_hw_probe()
該函數主要初始化isp驅動
static const struct of_device_id rkisp_hw_of_match[] = { …… { .compatible = "rockchip,rk3568-rkisp", .data = &rk3568_isp_match_data, }, {},};640 static int rkisp_hw_probe(struct platform_device *pdev)641 { 646 struct rkisp_hw_dev *hw_dev;