赞
踩
因为有拍照、录制视频、直播等刚需,现在手机的摄像头基本都是高清,支持高清摄像头的SoC都支持MIPI-CSI。
不同SoC的MIPI-CSI在实现上有一定差别,即使同一厂家设计生产的芯片也都不尽相同。
本文基于瑞芯微rk3568平台evb1公板为例来详细讲解MIPI-CSI/DPHY驱动。
阅读本文之前,建议大家先仔细学习前面几篇文章。
驱动的研究往往要先从硬件着手,下面我首先看下rk3568公板电路。
由电路图可得摄像头与SoC的MIPI-CSI接口,可以是x4lane,也可以是x2lane,data和clk都是差分信号。
如果不了解,建议问下硬件工程师。
控制摄像头接口是I2C接口,并且位于I2C通道4下。
电路图只能查看SoC的MIPI控制器与摄像头的接口关系,下面我们来看下rk3568内部与mipi相关的模块。
吐槽一下瑞芯微的文档,一言难尽,我严重怀疑厂家压根就不想让其他人真正搞懂他们的SDK,这样好收每年的技术支持费用,高通这损招是让丫彻底学明白了。
由于官方给的手册要么有错误,要么就是有些图片错误,就不截图了。
下图是根据官方手册说明,整理的内部模块图。
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是一个完整的视频和静止图像输入设备。
这个模块支持集成YCbCr处理图像传感器和简单CMOS传感器 ,提交没有任何综合图像处理Bayer RGB模式图像。
rk3568采用的是ISP21版本。
ISP21 包含了一系列的图像处理算法模块,主要包括:暗电流矫正、坏点矫正、3A、HDR、镜头阴影矫
正、镜头畸变矫正、3DLUT、去噪(包括RAW域去噪,多帧降噪,颜色去噪等)、锐化等。
ISP21包括硬件算法实现及软件逻辑控制部分,RkAiq即为软件逻辑控制部分的实现。
RkAiq不断从ISP HW获取统计数据,并经过3A等算法生成新的参数反馈给各硬件模块。
RkAiq软件模块主要实现的功能为:从ISP驱动获取图像统计,结合IQ Tuning参数,使用一系列算法计
算出新的ISP、Sensor等硬件参数,不断迭代该过程,最终达到最优的图像效果。
《Rockchip RK3568 TRM Part1 V1.1-20210301.pdf》
在rk3568中主要包含4个设备:
下面我看下瑞芯微MIPI-CSI是如何用设备树描述的。
瑞芯微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系列的摄像设备树说明
rk3568的MIPI-CSI用到的所有的设备树节点:
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>;**中描述。
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,
};
以下是描述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开始分析。
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结尾的文件
该函数主要用于申请设备号:
主设备号 :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); }
注意
为简化起见,所有代码只把最重要的部分列举出来,后同。
该函数是最重要的一个初始化函数,除了rkisp_csi2_dphy(entity67)外,其他的功能部件都在该函数中初始化。
注册rkisp-vir0父设备、isp-dubdev子设备、csi2-dev子设备等,由于rk3568支持多路sensor输入,即isp支持多路处理,因此会虚拟多通道isp-virx。
该函数主要工作:
![ ](https://img-blog.csdnimg.cn/796972bedb2046e89d3e3772fec31a9b.png
其中对于研发人员最重要的就是这些回调函数,应用层下发的各个命令最终都会通过架构调用到这些函数。
下面把重要的几个模块所注册的回调函数做了个总结:
这些回调函数在架构中关系参考下图:
其中entity67相关资源是在函数rockchip_csi2_dphy_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; …… /*匹配设备树compatible属性*/ 651 match = of_match_node(rkisp_hw_of_match, node); 654 /*为hw_dev 分配内存*/ 655 hw_dev = devm_kzalloc(dev, sizeof(*hw_dev), GFP_KERNEL); 659 dev_set_drvdata(dev, hw_dev);//dev->driver_data 660 hw_dev->dev = dev; 661 hw_dev->is_thunderboot = IS_ENABLED(CONFIG_VIDEO_ROCKCHIP_THUNDER_BOOT_ISP); 662 dev_info(dev, "is_thunderboot: %d\n", hw_dev->is_thunderboot); 663 hw_dev->max_in.w = 0; 664 hw_dev->max_in.h = 0; 665 hw_dev->max_in.fps = 0; //获得grf句柄*/ 669 hw_dev->grf = syscon_regmap_lookup_by_phandle(node, "rockchip,grf"); 672 /*获取控制器物理地址 673 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); /*将物理地址映射为基地址*/ 679 hw_dev->base_addr = devm_ioremap_resource(dev, res); 694 match_data = match->data; 695 hw_dev->mipi_irq = -1; 696 697 hw_dev->pdev = pdev; 698 hw_dev->match_data = match_data; 699 if (!hw_dev->is_thunderboot) 700 rkisp_register_irq(hw_dev); //注册中断 701 /*从设备树中提取时钟*/ 702 for (i = 0; i < match_data->num_clks; i++) { 703 struct clk *clk = devm_clk_get(dev, match_data->clks[i]); 704 707 hw_dev->clks[i] = clk; 708 } 709 hw_dev->num_clks = match_data->num_clks; 710 hw_dev->clk_rate_tbl = match_data->clk_rate_tbl; 711 hw_dev->num_clk_rate_tbl = match_data->num_clk_rate_tbl; 712 /*提取reset属性*/ 713 hw_dev->reset = devm_reset_control_array_get(dev, false, false); 718 719 ret = of_property_read_u64(node, "rockchip,iq-feature", &hw_dev->iq_feature); 720 if (!ret) 721 hw_dev->is_feature_on = true; 722 else 723 hw_dev->is_feature_on = false; 724 /*初始化其他的一些变量*/ 725 hw_dev->dev_num = 0; ………… 743 hw_dev->is_shutdown = false; 744 hw_dev->is_mmu = is_iommu_enable(dev); 745 ret = of_reserved_mem_device_init(dev); ………… 770 }
学习更多嵌入式、Linux相关知识,后台留言加我好友!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。