RK3588-Camera:MIPI-CSI调试之通路解析

一、简介

  • 本文介绍RK3588平台的Camera:MIPI-CSI调试之通路解析

  • MIPI联盟,即移动产业处理器接口(Mobile Industry Processor Interface 简称MIPI)联盟。MIPI(移动产业处理器接口)是 MIPI联盟发起的为移动应用处理器制定的开放标准和一个规范。

    目的是把手机内部的接口如摄像头、显示屏接口、射频/基带接口等标准化,从而减少手机设计的复杂程度和增加设计灵活性。

  • CSI & DSI

    CSI ( Camera Serial Interface ):摄像头接口

    DSI ( Display Serial Interface ):显示接口

二、 名词解释:

  • ISP ( Image Signal Processor ): 即图像信号处理模块, 主要作用是对前端图像传感器输出的信号做后期处理,依赖于 ISP 才能在不同的光学条件下都能较好的还原现场细节。

  • VICAP( Video capture ):视频捕获单元

三、RK3588 的camera通路:

多sensor支持:

  • 单路硬件isp最多支持4路复用,isp复用情况支持分辨率如下:
  • 2路复用:最大分辨率3840x2160,dts对应配置2路rkisp_vir设备。
  • 3路或4路复用:最大分辨率2560x1536,dts对应配置3或4路rkisp_vir设备。
  • 硬件支持最多采集7路sensor:6mipi + 1dvp,多sensor软件通路如下:

下图是RK3588 camera连接链路示意图,可以支持7路camera。

在这里插入图片描述

四、 链路解析:

在这里插入图片描述

  • 图中:mipi camera2—> csi2_dphy1 —> mipi2_csi2 —> rkcif_mipi_lvds2—>rkcif_mipi_lvds2_sditf —>rkisp0_vir2

  • 对应节点:imx415 —> csi2_dphy0 —> mipi2_csi2 —> rkcif_mipi_lvds2—>rkcif_mipi_lvds2_sditf —>rkisp0_vir2

  • 链接关系:sensor—> csi2 dphy---->mipi csi host—>vicap

  • 实线链路解析: Camera sensor —> dphy —> 通过mipi_csi2模块解析mipi协议—> vicap ( rkcif节点代表vicap )

  • 虚线链路解析:vicap —> rkcif_mipi_lvds2_sditf —> isp

    每个vicap节点与isp的链接关系,通过对应虚拟出的XXX_sditf来指明链接关系。

五、RK3588硬件通路框图

在这里插入图片描述

  • rk3588支持2个isp硬件,每个isp设备可虚拟出多个虚拟节点,软件上通过回读的方式,依次从ddr读取每一路的图像数据进isp处理。对于多摄方案,建议将数据流平均分配到两个isp上。

  • 回读:指数据经过vicap采集到ddr,应用获取到数据后,将buffer地址推送给isp,isp再从ddr获取图像数据。

六、详细解析:

  1. imx415 : Camera sensor

  2. csi2_dphy0 : rk3588支持2个dphy硬件,这里我们称之为dphy0_hw/dphy1_hw ,,两个dphy硬件都可以工作在full mode 和split mode两种模式下。

当使用dphy0_hw:

  • full mode:节点名称使用csi2_dphy0,最多支持4 lane。
    当dphy0_hw使用full mode时,链路需要按照csi2_dphy1这条链路来配置,但是节点名称csi2_dphy1需要修改为csi2_dphy0,软件上是通过phy的序号来区分phy使用的模式。

  • split mode:拆分成2个phy使用,分别为csi2_dphy1(使用0/1 lane)、csi2_dphy2(使用2/3 lane),每个phy最多支持2 lane。

当使用dphy1_hw:

  • full mode:节点名称使用csi2_dphy3,最多支持4 lane。
    当dphy1_hw使用full mode时,链路需要按照csi2_dphy4这条链路来配置,但是节点名称csi2_dphy4需要修改为csi2_dphy3,软件上是通过phy的序号来区分phy使用的模式。

  • split mode:拆分成2个phy使用,分别为csi2_dphy4(使用0/1 lane)、csi2_dphy5(使用2/3 lane),每个phy最多支持2 lane。

  1. dcphy:
    rk3588支持两个dcphy,节点名称分别为csi2_dcphy0/csi2_dcphy1。每个dcphy硬件支持RX/TX同时使用,对于camera输入使用的是RX。支持DPHY/CPHY协议复用;需要注意的是同一个dcphy的TX/RX只能同时使用DPHY或同时使用CPHY。其他dcphy参数请查阅rk3588数据手册。

  2. 使用上述mipi phy节点,需要把对应的物理节点配置上。
    (csi2_dcphy0_hw/csi2_dcphy1_hw/csi2_dphy0_hw/csi2_dphy1_hw)

  3. 每个mipi phy都需要一个csi2模块来解析mipi协议,节点名称分别为mipi0_csi2~mipi5_csi2。

  4. rk3588所有camera数据都需要通过vicap,再链接到isp。rk3588仅支持一个vicap硬件,这个vicap支持同时输入6路mipi phy,及一路dvp数据,所以我们将vicap分化成rkcif_mipi_lvds~rkcif_mipi_lvds5、rkcif_dvp等7个节点,各个节点的绑定关系需要严格按照框图的节点序号配置。

  5. 每个vicap节点与isp的链接关系,通过对应虚拟出的XXX_sditf来指明链接关系。

  6. rk3588支持2个isp硬件,每个isp设备可虚拟出多个虚拟节点,软件上通过回读的方式,依次从ddr读取每一路的图像数据进isp处理。对于多摄方案,建议将数据流平均分配到两个isp上。

  7. 直通与回读模式:
    •直通:指数据经过vicap采集,直接发送给isp处理,不存储到ddr。需要注意的是hdr直通时,只有短帧是真正的直通,长帧需要存在ddr,isp再从ddr读取。

    •回读:指数据经过vicap采集到ddr,应用获取到数据后,将buffer地址推送给isp,isp再从ddr获取图像数据。

    •在dts配置时,一个isp硬件,如果只配置一个虚拟节点,默认使用直通模式,如果配置了多个虚拟节点默认使用回读模式。

七、单路Camera的dts配置说明:( 以imx415摄像头为例 )

  • 案例场景:这里使用的是csi2_dphy0的单路camera配置:
  • 链路配置: imx415 —> csi2_dphy0 —> mipi2_csi2 —> rkcif_mipi_lvds2—>rkcif_mipi_lvds2_sditf —>rkisp0_vir2
&i2c3 {
	status = "okay";

	imx415: imx415@1a {
		status = "okay";
		compatible = "sony,imx415";
		reg = <0x1a>;
		clocks = <&cru CLK_MIPI_CAMARAOUT_M3>;
		clock-names = "xvclk";
		pinctrl-names = "default";
		pinctrl-0 = <&mipim0_camera3_clk>;
		power-domains = <&power RK3588_PD_VI>;
		pwdn-gpios = <&gpio1 RK_PB0 GPIO_ACTIVE_HIGH>;
		reset-gpios = <&gpio4 RK_PA0 GPIO_ACTIVE_LOW>;
		rockchip,camera-module-index = <0>;
		rockchip,camera-module-facing = "back";
		rockchip,camera-module-name = "CMK-OT2022-PX1";
		rockchip,camera-module-lens-name = "IR0147-50IRC-8M-F20";
		port {
			imx415_out0: endpoint {
				remote-endpoint = <&mipidphy0_in_ucam0>;
				data-lanes = <1 2 3 4>;
			};
		};
	};

	camera_imx219: camera-imx219@10 {
		status = "disabled";
		compatible = "sony,imx219";
		reg = <0x10>;

		clocks = <&clk_cam_24m>;
		clock-names = "xvclk";

		rockchip,camera-module-index = <0>;
		rockchip,camera-module-facing = "back";
		rockchip,camera-module-name = "rpi-camera-v2";
		rockchip,camera-module-lens-name = "default";

		port {
			imx219_out0: endpoint {
				remote-endpoint = <&mipidphy0_in_ucam1>;
				data-lanes = <1 2>;
			};
		};
	};
};

&csi2_dphy0_hw {
	status = "okay";
};

&csi2_dphy0 {
	status = "okay";

	ports {
		#address-cells = <1>;
		#size-cells = <0>;

		port@0 {
			reg = <0>;
			#address-cells = <1>;
			#size-cells = <0>;

			mipidphy0_in_ucam0: endpoint@1 {
				reg = <1>;
				remote-endpoint = <&imx415_out0>;
				data-lanes = <1 2 3 4>;
			};

			mipidphy0_in_ucam1: endpoint@2 {
				reg = <2>;
				remote-endpoint = <&imx219_out0>;
				data-lanes = <1 2>;
			};
		};

		port@1 {
			reg = <1>;
			#address-cells = <1>;
			#size-cells = <0>;

			csidphy0_out: endpoint@0 {
				reg = <0>;
				remote-endpoint = <&mipi2_csi2_input>;
			};
		};
	};
};

&mipi2_csi2 {
	status = "okay";

	ports {
		#address-cells = <1>;
		#size-cells = <0>;

		port@0 {
			reg = <0>;
			#address-cells = <1>;
			#size-cells = <0>;

			mipi2_csi2_input: endpoint@1 {
				reg = <1>;
				remote-endpoint = <&csidphy0_out>;
			};
		};

		port@1 {
			reg = <1>;
			#address-cells = <1>;
			#size-cells = <0>;

			mipi2_csi2_output: endpoint@0 {
				reg = <0>;
				remote-endpoint = <&cif_mipi2_in0>;
			};
		};
	};
};

&rkcif {
	status = "okay";
};

&rkcif_mipi_lvds2 {
	status = "okay";

	port {
		cif_mipi2_in0: endpoint {
			remote-endpoint = <&mipi2_csi2_output>;
		};
	};
};

&rkcif_mipi_lvds2_sditf {
	status = "okay";

	port {
		mipi_lvds2_sditf: endpoint {
			remote-endpoint = <&isp0_vir0>;
		};
	};
};

&rkcif_mmu {
	status = "okay";
};

&rkisp0 {
	status = "okay";
};

&isp0_mmu {
	status = "okay";
};

&rkisp0_vir0 {
	status = "okay";

	port {
		#address-cells = <1>;
		#size-cells = <0>;

		isp0_vir0: endpoint@0 {
			reg = <0>;
			remote-endpoint = <&mipi_lvds2_sditf>;
		};
	};
};

&pinctrl {
	camera {
		cam_pwdn_gpio: cam-pwdn-gpio {
			rockchip,pins = <1 RK_PB0 RK_FUNC_GPIO &pcfg_pull_up>;
		};
	};
};

八、 调试技巧

8.1 i2c设备的通用调试命令:查看设备是否挂载到i2c总线下:

i2cdetect -y 3

8.2 摄像命令

  • Linux系统摄像命令:
gst-launch-1.0 v4l2src device=/dev/video11 ! video/x-raw,format=NV12,width=3840,height=2160, framerate=30/1 ! xvimagesink
  • Android系统:

    Android系统自带相机APP。点击APP,看摄像画面是否正常显示。

8.3 imx415 相关的log信息

dmesg | grep imx415

8.4 查看拓扑结构

 media-ctl -d /dev/media0 -p

8.5 查看节点采集格式

查看 video11 支持采集的格式,以 video11 为例。
v4l2-ctl -d /dev/video11 --list-formats
在这里插入图片描述

8.6 查看节点支持的分辨率

下图为video11节点: imx415 摄像头支持4K(3840x2160)的查询结果。
在这里插入图片描述

完善 RK3588 驱动 #include <linux/module.h> #include <linux/platform_device.h> #include <media/v4l2-subdev.h> #include <media/media-entity.h> #define DRV_NAME "fpga_mipi" struct fpga_device { struct v4l2_subdev sd; struct media_pad pad; struct gpio_desc *reset_gpio; struct gpio_desc *power_en_gpio; }; static struct fpga_device *fpga_dev; // V4L2子设备操作函数 static const struct v4l2_subdev_ops fpga_subdev_ops = { }; // 媒体实体初始化 static int fpga_init_media_entity(struct fpga_device *dev) { dev->pad.flags = MEDIA_PAD_FL_SOURCE; dev->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; return media_entity_pads_init(&dev->sd.entity, 1, &dev->pad); } // 格式配置回调 static int fpga_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, struct v4l2_subdev_format *fmt) { // 设置FPGA输出格式(需与硬件匹配) fmt->format.width = 1920; fmt->format.height = 1080; fmt->format.code = MEDIA_BUS_FMT_UYVY8_2X8; // YUV422格式 return 0; } // 探测函数 static int fpga_probe(struct platform_device *pdev) { struct fpga_device *dev; int ret; dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); if (!dev) return -ENOMEM; // 初始化V4L2子设备 v4l2_subdev_init(&dev->sd, &fpga_subdev_ops); dev->sd.owner = THIS_MODULE; snprintf(dev->sd.name, sizeof(dev->sd.name), DRV_NAME); // 配置GPIO dev->reset_gpio = devm_gpiod_get(&pdev->dev, "reset", GPIOD_OUT_LOW); dev->power_en_gpio = devm_gpiod_get(&pdev->dev, "power-en", GPIOD_OUT_HIGH); // 初始化媒体实体 ret = fpga_init_media_entity(dev); if (ret) return ret; // 注册子设备 ret = v4l2_async_register_subdev(&dev->sd); if (ret) { media_entity_cleanup(&dev->sd.entity); return ret; } platform_set_drvdata(pdev, dev); fpga_dev = dev; return 0; } // 移除函数 static int fpga_remove(struct platform_device *pdev) { struct fpga_device *dev = platform_get_drvdata(pdev); v4l2_async_unregister_sub
最新发布
03-14
<think>好的,我现在需要帮助用户解决关于RK3588 V4L2子设备驱动开发的问题,特别是涉及媒体实体和GPIO配置的示例。首先,我需要回忆一下相关的知识,确保信息准确。 用户提到了V4L2子设备和媒体实体,这涉及到Linux内核中的视频设备驱动开发。V4L2是Video for Linux 2的缩写,用于处理视频设备的框架。子设备(subdev)通常指传感器或其他独立模块,通过媒体控制器框架进行连接和管理。媒体实体(media entity)是媒体控制器中的基本单元,代表硬件中的功能块,比如传感器、CSI接口等。 接下来,GPIO配置是硬件相关的部分,可能需要通过设备树(Device Tree)来配置引脚功能,比如控制传感器的电源或复位引脚。RK3588是Rockchip的处理器,需要查阅其文档,了解GPIO子系统的配置方法。 用户需要一个示例,所以需要构建一个简单的驱动代码框架,展示如何注册V4L2子设备,创建媒体实体,并配置GPIO。需要注意设备树中的节点定义,以及驱动中如何解析这些配置。 可能的问题点包括媒体控制器的链路建立是否正确,GPIO的请求和释放是否正确处理,以及子设备与主设备(如CSI接口)的连接。此外,需要确保遵循Rockchip特定的配置要求,比如时钟、电源管理等。 然后,用户可能需要知道如何测试这个驱动,比如使用media-ctl工具检查拓扑结构,或者使用v4l2-ctl进行视频流的捕获测试。还需要考虑错误处理,比如GPIO申请失败时的回滚操作,媒体实体注册失败的处理等。 最后,生成的相关问题需要围绕RK3588驱动开发的常见挑战、调试方法、性能优化等,帮助用户进一步深入学习和解决问题。</think>### RK3588 V4L2子设备驱动开发指南 #### 一、媒体实体配置 1. 需在设备树中定义子设备节点: ```dts &i2c3 { camera_sensor: camera@10 { compatible = "ovti,ov5648"; reg = <0x10>; clocks = <&cru CLK_CIF_OUT>; pinctrl-names = "default"; pinctrl-0 = <&cif_clkout>; reset-gpios = <&gpio1 12 GPIO_ACTIVE_LOW>; port { sensor_out: endpoint { remote-endpoint = <&csi_in>; }; }; }; }; ``` 2. 驱动中需实现媒体控制器接口: ```c static int sensor_s_stream(struct v4l2_subdev *sd, int enable) { struct sensor_device *sensor = to_sensor(sd); if (enable) { // 启动传感器数据流 media_entity_pipeline_start(&sensor->subdev.entity, &sensor->pipe); } else { // 停止数据流 media_entity_pipeline_stop(&sensor->subdev.entity); } return 0; } ``` #### 二、GPIO配置实践 1. 设备树GPIO定义: ```dts reset-gpios = <&gpio3 14 GPIO_ACTIVE_HIGH>; pwdn-gpios = <&gpio4 21 GPIO_ACTIVE_LOW>; ``` 2. 驱动中GPIO控制: ```c struct sensor_device { struct gpio_desc *reset_gpio; struct gpio_desc *pwdn_gpio; }; static int sensor_power_on(struct device *dev) { gpiod_direction_output(sensor->reset_gpio, 1); msleep(10); gpiod_set_value(sensor->pwdn_gpio, 0); msleep(20); return 0; } ``` #### 三、媒体链路建立 使用media_create_pad_link()建立传感器与CSI接口的连接: ```c media_create_pad_link( &sensor->subdev.entity, SENSOR_PAD_SOURCE, &csi->subdev.entity, CSI_PAD_SINK, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE ); ``` #### 四、验证流程 1. 加载驱动后使用media-ctl检查拓扑: ```bash media-ctl -p -d /dev/media0 ``` 2. 验证GPIO状态: ```bash cat /sys/kernel/debug/gpio ``` 3. 视频流测试: ```bash v4l2-ctl --stream-mmap --stream-count=10 --stream-to=test.raw ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嵌入式Linux开发

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值