Jetson Orin Nano – SONY imx415 camera驱动开发

目录

前言:

调试准备工作:

修改内核默认打印等级

一、imx415驱动开发

1、硬件接线

2、设备树修改

2.1 创建 tegra234-p3767-camera-p3768-imx415-C-4lane.dtsi 文件

2.2 tegra234-p3767-camera-p3768-imx415-C-4lane.dtsi 添加到设备树

2.3 编译设备树

3、imx415驱动开发

3.1 imx415寄存器

1)mclk 时钟

2)lane数量

3)lane速率

4)分辨率

5)像素深度

6)Master/Slave模式

7)启流、停流相关

8)增益

​编辑

9)曝光

​编辑

10)All pixel模式 4lane 配置表

3.2 imx415驱动

1)Linux_for_Tegra/source/nvidia-oot/drivers/media/i2c/imx415_mode_tbls.h 源码

2)Linux_for_Tegra/source/nvidia-oot/include/media/imx415.h 源码

3)Linux_for_Tegra/source/nvidia-oot/drivers/media/i2c/nv_imx415.c 驱动文件

4)将 nv_imx415 添加到Makefile

二、编译调试

1、编译imx415驱动

2、安装 v4l-utils 并查看 /dev/video0信息

2.1 安装 v4l-utils

2.2 查看 /dev/video0 信息

3、抓拍

3.1 v4l2-ctl 命令验证vi抓拍

3.2 gstreamer英伟达命令抓拍

1)gst-launch-1.0 自动曝光、自动增益抓拍

2)gst-launch-1.0 设置曝光、增益抓拍

三、imx415驱动开发遇到的问题及解决方法

1、Jetson orin nano开发板的CAM0不支持4lane模式

2、使用 CSI0/1 时,需要将lane_polarity改为 6

3、v4l2-ctl 抓图命令报错

4、v4l2-ctl 抓图不成功,vi能收到数据,但数据不对

5、gstreamer抓图偏红色

四、曝光、增益、格式、焦距对拍照效果的影响


前言:

nvidia相机开发参考链接:相机软件开发解决方案 — NVIDIA Jetson Linux 开发者指南

可加载内核模块 (LKM)其实就是就是编译成ko模块。nvidia将所有模块都放在 nvidia-oot 目录下。参考链接:传感器软件驱动程序编程 — NVIDIA Jetson Linux 开发者指南

说明:博主使用野火的imx415摄像头模块,在 Jetson Orin Nano开发板上开发imx415驱动

调试准备工作:


修改内核默认打印等级

$ vi /etc/sysctl.conf

#
# /etc/sysctl.conf - Configuration file for setting system variables
# See /etc/sysctl.d/ for additional system variables.
# See sysctl.conf (5) for information.
#

#kernel.domainname = example.com

# Uncomment the following to stop low-level messages on console
kernel.printk = 8 8 8 8                #打印等级

###################################################################
# Functions previously found in netbase
#

# Uncomment the next two lines to enable Spoof protection (reverse-path filter)
# Turn on Source Address Verification in all interfaces to
# prevent some spoofing attacks
#net.ipv4.conf.default.rp_filter=1
#net.ipv4.conf.all.rp_filter=1

# Uncomment the next line to enable TCP/IP SYN cookies
# See http://lwn.net/Articles/277146/

一、imx415驱动开发


1、硬件接线

imx415模块和 Jetson orin nano开发板的csi接口中间有转接板。2 lane模式下,仅 CSI_D0 P/N 和 CSI_D1 P/N 有数据。我使用的是 4 lane 模式。

硬件接上后,用 i2cdetect 工具探测 I2C通不通。cam_i2cmux挂在i2c3下,因此命令如下:

$ i2cdetect -r -y 2

可以看到 imx415 的设备地址为 0x1a

问题:如何知道 CAM1_I2C 挂在 i2c3下?

查看 Linux_for_Tegra/source/hardware/nvidia/t23x/nv-public/tegra234.dtsi

cam_i2c 为I2C3节点, 因此cam_i2cmux在I2C3节点下

2、设备树修改

设备树增加sensor参考文档:Sensor Software Driver Programming — NVIDIA Jetson Linux Developer Guide

2.1 创建 tegra234-p3767-camera-p3768-imx415-C-4lane.dtsi 文件

1)将 Linux_for_Tegra/source/hardware/nvidia/t23x/nv-public/overlay 目录下的 tegra234-p3767-camera-p3768-imx477-C.dts 作为模板拷贝一份 tegra234-p3767-camera-p3768-imx415-C-4lane.dtsi

imx415 模块 4 lane差分线接在CSI2上,CSI2对应”serial_c”(CSI0对应serial_a,依此类推),因此命名 “tegra234-p3767-camera-p3768-imx415-C-4lane.dtsi”

2)修改 tegra234-p3767-camera-p3768-imx415-C-4lane.dtsi

关键参数计算过程:

compatible = “ridgerun,imx415” 要和imx415驱动对应上

mclk_khz:imx415模块外接 37.125M晶振,因此 mclk_khz = “37125”

4 lane模式:因此 num_lanes = “4”, bus-width = <4>;

CSI2:因此 tegra_sinterface = “serial_c”,port-index = <2>

分辨率:根据 imx415分辨率设置 “active_w = 3864″,active_h = “2192”

pixel_phase:像素格式,pixel_phase = “gbrg”,调试时设置为 “rggb” 导致图像偏红色

csi_pixel_bit_depth:像素深度,imx415使用RAW12模式,因此 csi_pixel_bit_depth = “12”

pix_clk_hz:imx415 配置 lane频率为 891 Mbps,4lane,像素深度为12bit,pix_clk_hz = 891 Mbps × 4 / 12 = 297000000,我理解只要不小于此值即可。

mclk_multiplier :mclk_multiplier  ≥ pix_clk_hz / mclk = 297000000 / 37125000 = 8,因此mclk_multiplier = “8”。从nvidia官方手册可知,mclk_multiplier 必须大于等于pix_clk_hz / mclk,以确保ISP运行得足够快,能够处理来自传感器的数据。

增益,查看imx415手册

 GAIN_PCG_0计算公式:GAIN_PCG_0 = Gain[dB] x 10 /3

GAIN_PCG_0 范围为 0 – 240d,因此 Gain范围为  0 – 72 [dB],我这里增益因子配置为 gain_factor = “10” ,Gain范围 0 – 72 [dB] 乘上增益因子,得到 min_gain_val = “0”, max_gain_val = “720”,step为 0.3dB 乘增益因子,因此 step_gain_val = “3”

曝光:

曝光参数单位为us,需要根据帧率(30fps)、VMAX(2250)、 SHR0 值范围(8 到 Number oflines per frame – 4),按照下面公式倒推 exposure 最小值、最大值、step,我算得结果图下:

min_exp_time = "15";        /* 单位:us */
max_exp_time = "33214";     /* 单位:us */
step_exp_time = "15";       /* 单位:us */

embedded_metadata_height:根据imx415手册来配置为 “1”,如下图

最终 tegra234-p3767-camera-p3768-imx415-C-4lane.dtsi 文件内容如下:

设备树描述了 VI(视频输入)、NvCSI 、传感器模块 的端口绑定关系

// SPDX-License-Identifier: GPL-2.0-only
// SPDX-FileCopyrightText: Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved.

///dts-v1/;
///plugin/;

#define CAM0_RST	TEGRA234_MAIN_GPIO(Q, 6)	/* add */
#define CAM0_PWDN	TEGRA234_MAIN_GPIO(H, 6)
#define CAM1_PWDN	TEGRA234_MAIN_GPIO(AC, 0)
#define CAM_I2C_MUX 	TEGRA234_AON_GPIO(CC, 3)

#include <dt-bindings/tegra234-p3767-0000-common.h>

/ {
	overlay-name = "Camera IMX415-C 4 lane";
	jetson-header-name = "Jetson 24pin CSI Connector";
	compatible = JETSON_COMPATIBLE_P3768;
	/*IMX415 connected on cam0 port */

	fragment@0 {
		target-path = "/";
		__overlay__ {
			tegra-capture-vi  {
				num-channels = <1>;
				ports {
					#address-cells = <1>;
					#size-cells = <0>;
					port@0 {
						reg = <0>;
						rbpcv3_imx415_vi_in1: endpoint {
							port-index = <2>;
							bus-width = <4>;
							remote-endpoint = <&rbpcv3_imx415_csi_out1>;
						};
					};
				};
			};
			tegra-camera-platform {
				compatible = "nvidia, tegra-camera-platform";
				/**
				* Physical settings to calculate max ISO BW
				*
				* num_csi_lanes = <>;
				* Total number of CSI lanes when all cameras are active
				*
				* max_lane_speed = <>;
				* Max lane speed in Kbit/s
				*
				* min_bits_per_pixel = <>;
				* Min bits per pixel
				*
				* vi_peak_byte_per_pixel = <>;
				* Max byte per pixel for the VI ISO case
				*
				* vi_bw_margin_pct = <>;
				* Vi bandwidth margin in percentage
				*
				* max_pixel_rate = <>;
				* Max pixel rate in Kpixel/s for the ISP ISO case
				*
				* isp_peak_byte_per_pixel = <>;
				* Max byte per pixel for the ISP ISO case
				*
				* isp_bw_margin_pct = <>;
				* Isp bandwidth margin in percentage
				*/
				num_csi_lanes = <4>;
				max_lane_speed = <1500000>;
				min_bits_per_pixel = <10>;
				vi_peak_byte_per_pixel = <2>;
				vi_bw_margin_pct = <25>;
				max_pixel_rate = <7500000>;
				isp_peak_byte_per_pixel = <5>;
				isp_bw_margin_pct = <25>;
				/**
				 * The general guideline for naming badge_info contains 3 parts, and is as follows,
				 * The first part is the camera_board_id for the module; if the module is in a FFD
				 * platform, then use the platform name for this part.
				 * The second part contains the position of the module, ex. "rear" or "front".
				 * The third part contains the last 6 characters of a part number which is found
				 * in the module's specsheet from the vendor.
				 */
				modules {
					module1 {
						badge = "jakku_rear_RBPCV3";
						position = "rear";
						orientation = "1";
						drivernode0 {
							pcl_id = "v4l2_sensor";
							sysfs-device-tree = "/sys/firmware/devicetree/base/bus@0/cam_i2cmux/i2c@1/rbpcv3_imx415_c@1a";
						};
					};
				};
			};
			bus@0 {
				host1x@13e00000 {
					nvcsi@15a00000 {
						num-channels = <1>;
						#address-cells = <1>;
						#size-cells = <0>;
						channel@0 {
							reg = <0>;
							ports {
								#address-cells = <1>;
								#size-cells = <0>;
								port@0 {
									reg = <0>;
									rbpcv3_imx415_csi_in1: endpoint@0 {
										port-index = <2>;
										bus-width = <4>;
										remote-endpoint = <&rbpcv3_imx415_out1>;
									};
								};
								port@1 {
									reg = <1>;
									rbpcv3_imx415_csi_out1: endpoint@1 {
										remote-endpoint = <&rbpcv3_imx415_vi_in1>;
									};
								};
							};
						};
					};
				};
				cam_i2cmux {
					status = "okay";
					compatible = "i2c-mux-gpio";
					#address-cells = <1>;
					#size-cells = <0>;
					mux-gpios = <&gpio_aon CAM_I2C_MUX GPIO_ACTIVE_HIGH>;
					i2c-parent = <&cam_i2c>;
					i2c@0 {
						rbpcv2_imx219_a@10 {
							status = "disabled";
						};
					};
					i2c@1 {
						status = "okay";
						reg = <1>;
						#address-cells = <1>;
						#size-cells = <0>;
						rbpcv2_imx219_c@10 {
							status = "disabled";
						};
						rbpcv3_imx415_c@1a {
							reset-gpios = <&gpio CAM1_PWDN GPIO_ACTIVE_HIGH>;
							compatible = "ridgerun,imx415";
							/* I2C device address */
							reg = <0x1a>;
							/* V4L2 device node location */
							devnode = "video0";
							/* Physical dimensions of sensor */
							physical_w = "9.3";
							physical_h = "4.65";
							sensor_model = "imx415";
							use_sensor_mode_id = "true";
							mode0 { /* IMX415_MODE_3864x2192 */
								mclk_khz = "37125";	//"24000";
								num_lanes = "4";
								tegra_sinterface = "serial_c";
								phy_mode = "DPHY";
								discontinuous_clk = "no";
								dpcm_enable = "false";
								cil_settletime = "0";		// 0表示自动校准 
								lane_polarity = "0";
								active_w = "3864";
								active_h = "2192";
								mode_type = "bayer";
								pixel_phase = "gbrg";	//"rggb";
								csi_pixel_bit_depth = "12";	//12
								readout_orientation = "0";	//90
								line_length = "4400";	//0x08CA "11200";
								inherent_gain = "1";
								mclk_multiplier = "8";		//pix_clk_hz / mclk_khz = 297000000 / 37125000 = 8
								pix_clk_hz = "297000000";	//891 Mbps × 4 / 12
								gain_factor = "10";
								framerate_factor = "1000000";
								exposure_factor = "1000000";
								min_gain_val = "0";	/* 0dB */
								max_gain_val = "720";	/* 72dB */
								step_gain_val = "3";	/* 0.3dB step */
								default_gain = "10";	/* 1dB */
								min_hdr_ratio = "1";
								max_hdr_ratio = "1";
								min_framerate = "2000000"; /* 2.0 fps */
								max_framerate = "60000000";	//"30000000"; /* 30.0 fps */
								step_framerate = "1";
								default_framerate = "30000000"; /* 30.0 fps */
								min_exp_time = "15";		/* us, 1H时间 = line_length / pix_clk_hz */
								max_exp_time = "33214";	//"33334";		/* us */
								step_exp_time = "15";
								default_exp_time = "5000";	/* us */
								embedded_metadata_height = "1";	
								set_mode_delay_ms = "500";                      //dongao 等待8帧
							};
							mode1 { /* IMX415_MODE_3864x2192 */
								mclk_khz = "37125";	//"24000";
								num_lanes = "4";
								tegra_sinterface = "serial_c";
								phy_mode = "DPHY";
								discontinuous_clk = "no";
								dpcm_enable = "false";
								cil_settletime = "0";		// 0表示自动校准 
								lane_polarity = "0";
								active_w = "3864";
								active_h = "2192";
								mode_type = "bayer";
								pixel_phase = "gbrg";	//"rggb";
								csi_pixel_bit_depth = "12";	//10
								readout_orientation = "0";	//90
								line_length = "4400";	//0x08CA "11200";
								inherent_gain = "1";
								mclk_multiplier = "8";
								pix_clk_hz = "297000000";		//"356400000";
								gain_factor = "10";
								framerate_factor = "1000000";
								exposure_factor = "1000000";
								min_gain_val = "0";	/* 0dB */
								max_gain_val = "720";	/* 72dB */
								step_gain_val = "3";	/* 0.3dB step */
								default_gain = "10";	/* 1dB */
								min_hdr_ratio = "1";
								max_hdr_ratio = "1";
								min_framerate = "2000000"; /* 2.0 fps */
								max_framerate = "60000000"; /* 60.0 fps */
								step_framerate = "1";
								default_framerate = "30000000"; /* 60.0 fps */
								min_exp_time = "15"; /* us */
								max_exp_time = "33214"; /* us */
								step_exp_time = "15";
								default_exp_time = "5000"; /* us */
								embedded_metadata_height = "1";
							};
							ports {
								#address-cells = <1>;
								#size-cells = <0>;
								port@0 {
									reg = <0>;
									rbpcv3_imx415_out1: endpoint {
										port-index = <2>;
										bus-width = <4>;
										remote-endpoint = <&rbpcv3_imx415_csi_in1>;
									};
								};
							};
						};
					};
				};

				gpio@2200000 {                  //add
                                      camera-control-output-low {
                                              gpio-hog;
                                              output-low;
                                              gpios = <CAM0_RST 0>;
                                              label = "cam0-rst";
                                      };
	                       };

				gpio@6000d000 {
					camera-control-output-low {
						gpio-hog;
						output-low;
						gpios = <CAM1_PWDN 0  CAM0_PWDN 0>;
						label = "cam1-pwdn", "cam0-pwdn";
					};
				};
			};
		};
	};
};

2.2 tegra234-p3767-camera-p3768-imx415-C-4lane.dtsi 添加到设备树

在 tegra234-p3768-0000+p3767-0000-dynamic.dts 中新增 “#include “tegra234-p3767-camera-p3768-imx415-C-4lane.dtsi””,如下:

// SPDX-License-Identifier: GPL-2.0-only
// SPDX-FileCopyrightText: Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES.  All rights reserved.

/dts-v1/;
/plugin/;

/ {
        overlay-name = "Tegra234 p3768-0000+p3767-xxxx Dynamic Overlay";
};

/*
 * Include this file last in the device tree.  It manages run-time
 * pruning of peripherals that are not available across the various
 * SKUs of p3767.  For example PVA can be enabled in the device tree
 * and it will automatically be disabled for SKUs without PVA support.
 */
#include "tegra234-p3767-sku-handling.dtsi"
#include "tegra234-p3767-camera-p3768-imx415-C-4lane.dtsi"    //设备树新增imx415内容

问题:为什么在 tegra234-p3768-0000+p3767-0000-dynamic.dts 中添加?

我们刷机命令的配置文件为 jetson-orin-nano-devkit-nvme.conf

$ sudo ./flash.sh jetson-orin-nano-devkit-nvme internal

看下 jetson-orin-nano-devkit-nvme.conf 文件相关内容,设置了 OVERLAY_DTB_FILE 的缺省值

2.3 编译设备树

1)编译

Linux_for_Tegra/source 目录下执行:

设置交叉编译工具 CROSS_COMPILE 环境变量

$ export CROSS_COMPILE=$HOME/l4t-gcc/aarch64–glibc–stable-2022.08-1/bin/aarch64-buildroot-linux-gnu-

设置环境变量

$ export KERNEL_HEADERS=$PWD/kernel/kernel-jammy-src

编译dtbs

$ make dtbs

编译报错:

解决方法:

编译成功打印结果:

2)编译完成后,fdtdump命令查看是否生效

$ fdtdump kernel-devicetree/generic-dts/dtbs/tegra234-p3768-0000+p3767-0000-dynamic.dtbo

3)拷贝编译结果

$ cp kernel-devicetree/generic-dts/dtbs/* /Linux_for_Tegra/kernel/dtb/

4)刷机

$ sudo ./flash.sh jetson-orin-nano-devkit-nvme internal

设备树查看路径:

/sys/firmware/devicetree/base/bus@0/cam_i2cmux/i2c@1/rbpcv3_imx415_c@1a/mode0

3、imx415驱动开发


3.1 imx415寄存器

重点已关注下列几个相关寄存器,其他寄存器看imx415手册

mclk时钟

lane数量

lane速率

分辨率

像素深度

Master/Slave模式

启流、停流

增益

曝光

1)mclk 时钟

野火imx415相机模块接了37.125M的晶振,以 891Mbps/lane 为例:

将INCK相关寄存器设置成对应值

2)lane数量

默认值是 3h:表示 CSI-2 4lane

3)lane速率

我用的 891 Mbps / lane,因此需将 3033h 寄存器配置成 0x05

4)分辨率

imx415寄存器只有设置 VMAX(垂直方向时钟数,包括blanking等区域)、HMAX(水平方向时钟数,包括blanking等区域)。我理解imx415缺省输出 3864×2192 分辨率,如下图:

寄存器如下:

VMAX表示每帧的行数
HMAX表示每行的时钟数,单位:像素时钟周期(pixel clocks)

帧率 = 像素时钟频率 / (VMAX * HMAX)

5)像素深度

我配置的像素深度为12,因此 3032h 寄存器设置成 1h

6)Master/Slave模式

默认是 0 : Master模式

7)启流、停流相关

8)增益

GAIN_PCG_0计算公式:GAIN_PCG_0 = Gain[dB] x 10 /3

GAIN_PCG_0 范围为 0 – 240d,因此 Gain范围为  0 – 72 [dB]

9)曝光

imx415的曝光与SHR0寄存器相关。具体计算方式查看手册,也可以查看imx415驱动设置曝光函数

曝光时间计算公式:

10)All pixel模式 4lane 配置表

imx415手册提供了寄存器配置表,我们只需根据需求配置即可,我这里用的 4lane 30fps 891Mbps/lane

3.2 imx415驱动

驱动源码下载链接:https://download.csdn.net/download/hinewcc/90933767

思路:

Jetson orin nano平台内核没有 imx415 驱动,我用 imx477 驱动作为模板去修改(imx477 和 imx415 寄存器区别很大)

瑞芯微 rv1126 或 rv3588 自带 imx415 驱动,下载一份作为参考,kernel链接:GitHub – LubanCat/kernel: LubanCat-RK BSP kernel source

        阅读驱动代码发现,由于nvidia平台v4l2驱动封装了一层,因此 rv3588 自带 imx415 驱动无法直接使用。驱动修改主要参考rv3588 自带 imx415 驱动的寄存器部分。

需要修改的文件

Linux_for_Tegra/source/nvidia-oot/include/media/imx415.h

Linux_for_Tegra/source/nvidia-oot/drivers/media/i2c/nv_imx415.c

Linux_for_Tegra/source/nvidia-oot/drivers/media/i2c/imx415_mode_tbls.h

imx415 Master 模式流程:

1)Linux_for_Tegra/source/nvidia-oot/drivers/media/i2c/imx415_mode_tbls.h 源码
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Copyright (c) 2020-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
 *
 */

#ifndef __IMX415_I2C_TABLES__
#define __IMX415_I2C_TABLES__

#include <media/camera_common.h>

#define IMX415_TABLE_WAIT_MS 0
#define IMX415_TABLE_END 1
#define IMX415_WAIT_MS 24
#define IMX415_STANDBY_REG 0x3000
#define IMX415_XMSTA_REG	0x3002

#define imx415_reg struct reg_8

static const imx415_reg imx415_start[] = {
	{IMX415_STANDBY_REG, 0x0},
	{IMX415_TABLE_WAIT_MS, IMX415_WAIT_MS*3},
	{IMX415_TABLE_END, 0x00}
};

static const imx415_reg imx415_stop[] = {
	{IMX415_STANDBY_REG, 0x1},
	{IMX415_TABLE_END, 0x00}
};

static const imx415_reg imx415_global_10bit_3864x2192_regs[] = {
//	{0x3002, 0x00},
	{0x3008, 0x7F},
	{0x300A, 0x5B},
	{0x3031, 0x00},
	{0x3032, 0x01},
	{0x30C1, 0x00},
	{0x30D9, 0x06},
	{0x3116, 0x24},
	{0x311E, 0x24},
	{0x32D4, 0x21},
	{0x32EC, 0xA1},
	{0x3452, 0x7F},
	{0x3453, 0x03},
	{0x358A, 0x04},
	{0x35A1, 0x02},
	{0x36BC, 0x0C},
	{0x36CC, 0x53},
	{0x36CD, 0x00},
	{0x36CE, 0x3C},
	{0x36D0, 0x8C},
	{0x36D1, 0x00},
	{0x36D2, 0x71},
	{0x36D4, 0x3C},
	{0x36D6, 0x53},
	{0x36D7, 0x00},
	{0x36D8, 0x71},
	{0x36DA, 0x8C},
	{0x36DB, 0x00},
	{0x3701, 0x00},
	{0x3724, 0x02},
	{0x3726, 0x02},
	{0x3732, 0x02},
	{0x3734, 0x03},
	{0x3736, 0x03},
	{0x3742, 0x03},
	{0x3862, 0xE0},
	{0x38CC, 0x30},
	{0x38CD, 0x2F},
	{0x395C, 0x0C},
	{0x3A42, 0xD1},
	{0x3A4C, 0x77},
	{0x3AE0, 0x02},
	{0x3AEC, 0x0C},
	{0x3B00, 0x2E},
	{0x3B06, 0x29},
	{0x3B98, 0x25},
	{0x3B99, 0x21},
	{0x3B9B, 0x13},
	{0x3B9C, 0x13},
	{0x3B9D, 0x13},
	{0x3B9E, 0x13},
	{0x3BA1, 0x00},
	{0x3BA2, 0x06},
	{0x3BA3, 0x0B},
	{0x3BA4, 0x10},
	{0x3BA5, 0x14},
	{0x3BA6, 0x18},
	{0x3BA7, 0x1A},
	{0x3BA8, 0x1A},
	{0x3BA9, 0x1A},
	{0x3BAC, 0xED},
	{0x3BAD, 0x01},
	{0x3BAE, 0xF6},
	{0x3BAF, 0x02},
	{0x3BB0, 0xA2},
	{0x3BB1, 0x03},
	{0x3BB2, 0xE0},
	{0x3BB3, 0x03},
	{0x3BB4, 0xE0},
	{0x3BB5, 0x03},
	{0x3BB6, 0xE0},
	{0x3BB7, 0x03},
	{0x3BB8, 0xE0},
	{0x3BBA, 0xE0},
	{0x3BBC, 0xDA},
	{0x3BBE, 0x88},
	{0x3BC0, 0x44},
	{0x3BC2, 0x7B},
	{0x3BC4, 0xA2},
	{0x3BC8, 0xBD},
	{0x3BCA, 0xBD},
	{0x4004, 0x48},
	{0x4005, 0x09},
	{IMX415_TABLE_WAIT_MS, IMX415_WAIT_MS},
	{IMX415_TABLE_END, 0x0000}
};

static const imx415_reg imx415_linear_12bit_3864x2192_891M_regs[] = {
	{0x3020, 0x00},
	{0x3021, 0x00},
	{0x3022, 0x00},
	{0x3024, 0xCA},
	{0x3025, 0x08},
	{0x3028, 0x4C},	//每行指定的时钟数(水平视场范围) 0x0044C - 1100  (* 4lane) *2
	{0x3029, 0x04},
	{0x302C, 0x00},
	{0x302D, 0x00},
	{0x3033, 0x05},
	{0x3050, 0x08},
	{0x3051, 0x00},
	{0x3054, 0x19},
	{0x3058, 0x3E},
	{0x3060, 0x25},
	{0x3064, 0x4a},
	{0x30CF, 0x00},
	{0x3118, 0xC0},
	{0x3260, 0x01},
	{0x400C, 0x00},
	{0x4018, 0x7F},
	{0x401A, 0x37},
	{0x401C, 0x37},
	{0x401E, 0xF7},
	{0x401F, 0x00},
	{0x4020, 0x3F},
	{0x4022, 0x6F},
	{0x4024, 0x3F},
	{0x4026, 0x5F},
	{0x4028, 0x2F},
	{0x4074, 0x01},
	{IMX415_TABLE_WAIT_MS, IMX415_WAIT_MS},
	{IMX415_TABLE_END, 0x0000}
};


enum {
	IMX415_MODE_3864x2192_30FPS_2LANE,
//	IMX415_MODE_1920x1080_60FPS,
	IMX415_MODE_3864x2192_30FPS_4LANE,
//	IMX415_MODE_1920x1080_60FPS_4LANE,
	IMX415_MODE_COMMON,
	IMX415_START_STREAM,
	IMX415_STOP_STREAM,
};

static const imx415_reg *mode_table[] = {
	[IMX415_MODE_3864x2192_30FPS_2LANE] = imx415_linear_12bit_3864x2192_891M_regs,
//	[IMX415_MODE_1920x1080_60FPS] = imx415_linear_10bit_3864x2192_891M_regs,
	[IMX415_MODE_3864x2192_30FPS_4LANE] = imx415_linear_12bit_3864x2192_891M_regs,
//	[IMX415_MODE_1920x1080_60FPS_4LANE] = imx415_linear_10bit_3864x2192_891M_regs,
	[IMX415_MODE_COMMON] = imx415_global_10bit_3864x2192_regs,
	[IMX415_START_STREAM] = imx415_start,
	[IMX415_STOP_STREAM] = imx415_stop,
};

static const int imx415_15_fr[] = {
	15,
};

static const int imx415_30_fr[] = {
	30,
};


#if 0
static const int imx415_60_fr[] = {
	60,
};
#endif

static const struct camera_common_frmfmt imx415_frmfmt[] = {
	{
           {3864, 2192}, imx415_30_fr, 1, 0, IMX415_MODE_3864x2192_30FPS_2LANE},
//	{
           {1920, 1080}, imx415_60_fr, 1, 0, IMX415_MODE_1920x1080_60FPS},
};
#endif /* __IMX415_I2C_TABLES__ */
2)Linux_for_Tegra/source/nvidia-oot/include/media/imx415.h 源码
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * IMX415_.h - IMX415 sensor header
 *
 * Copyright (c) 2020, RidgeRun. All rights reserved.
 *
 * Contact us: support@ridgerun.com
 *
 */

 #ifndef __IMX415_H__
 #define __IMX415_H__
 
 /* IMX415 - sensor parameters */
 #define IMX415_GAIN_PCG_0_MIN		                (0)
 #define IMX415_GAIN_PCG_0_MAX		                (240)
 
 #define IMX415_SHIFT_8_BITS			(8)

 #define IMX415_MASK_LSB_1_BITS			0x0001
 #define IMX415_MASK_LSB_8_BITS			0x00ff
 
 /* IMX415 sensor register address */
 #define IMX415_MODEL_ID_ADDR_MSB		0x311B
 #define IMX415_MODEL_ID_ADDR_LSB		0x311A
 
 #define IMX415_ANALOG_GAIN_ADDR_MSB		0x3091
 #define IMX415_ANALOG_GAIN_ADDR_LSB		0x3090
 
 #define IMX415_GROUP_HOLD_ADDR		        0x3001

 #define IMX415_LF_EXPO_REG_H		0x3052
 #define IMX415_LF_EXPO_REG_M		0x3051
 #define IMX415_LF_EXPO_REG_L		0x3050
 
 #define IMX415_VTS_REG_L		0x3024          //VMAX
 #define IMX415_VTS_REG_M		0x3025
 #define IMX415_VTS_REG_H		0x3026
 

 #define IMX415_ADBIT_ADDR      0x3031
 #define IMX415_ADBIT_10BIT     0x00
 #define IMX415_ADBIT_12BIT     0x01
 
 #endif /* __IMX415_H__ */
 

3)Linux_for_Tegra/source/nvidia-oot/drivers/media/i2c/nv_imx415.c 驱动文件

修改 compatible 与 设备树匹配

static const struct of_device_id imx415_of_match[] = {
	{.compatible = "ridgerun,imx415",},
	{},
};

static struct i2c_driver imx415_i2c_driver = {
	.driver = {
		   .name = "imx415",
		   .owner = THIS_MODULE,
		   .of_match_table = of_match_ptr(imx415_of_match),
		   },
	.probe = imx415_probe,
	.remove = imx415_remove,
	.id_table = imx415_id,
};

主要修改 ops 结构体中的函数,主要有:

imx415_set_mode – 设置模式(配置寄存器)

imx415_start_streaming – 启流

imx415_stop_streaming – 停流

imx415_set_gain – 设置增益

imx415_set_exposure – 设置曝光

nv_imx415.c 驱动源码:

// SPDX-License-Identifier: GPL-2.0-only
// SPDX-FileCopyrightText: Copyright (c) 2021-2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
/*
 * Copyright (c) 2020, RidgeRun. All rights reserved.
 *
 * Contact us: support@ridgerun.com
 *
 * nv_imx415.c - imx415 sensor driver
 */

#include <nvidia/conftest.h>

#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/gpio.h>
#include <linux/module.h>
#include <linux/seq_file.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>

#include <media/tegra_v4l2_camera.h>
#include <media/tegracam_core.h>
#include <media/imx415.h>

#include "../platform/tegra/camera/camera_gpio.h"
#include "imx415_mode_tbls.h"

//#define IMX415_SENSOR_INTERNAL_CLK_FREQ   840000000

static const struct of_device_id imx415_of_match[] = {
	{.compatible = "ridgerun,imx415",},
	{},
};

MODULE_DEVICE_TABLE(of, imx415_of_match);

static const u32 ctrl_cid_list[] = {
	TEGRA_CAMERA_CID_GAIN,
	TEGRA_CAMERA_CID_EXPOSURE,
	TEGRA_CAMERA_CID_FRAME_RATE,  //imx415帧率跟很多寄存器相关
	TEGRA_CAMERA_CID_SENSOR_MODE_ID,
};

enum imx415_Config {
	TWO_LANE_CONFIG,
	FOUR_LANE_CONFIG,
};

struct imx415 {
	struct i2c_client *i2c_client;
	struct v4l2_subdev *subdev;
	u16 fine_integ_time;
	u32 frame_length;
	struct camera_common_data *s_data;
	struct tegracam_device *tc_dev;
	enum imx415_Config config;
};

static const struct regmap_config sensor_regmap_config = {
	.reg_bits = 16,
	.val_bits = 8,
	.cache_type = REGCACHE_RBTREE,
	.use_single_read = true,
	.use_single_write = true,
};

static inline void imx415_get_shr0_regs(imx415_reg *regs, u32 val)
{
	regs->addr = IMX415_LF_EXPO_REG_H;
	regs->val = (val >> 16) & 0x0f;
	(regs + 1)->addr = IMX415_LF_EXPO_REG_M;
	(regs + 1)->val = (val >> 8) & 0xff;
	(regs + 2)->addr = IMX415_LF_EXPO_REG_L;
	(regs + 2)->val = val & 0xff;
}

static inline void imx415_get_gain_reg(imx415_reg *reg, u16 gain)
{
	reg->addr = IMX415_ANALOG_GAIN_ADDR_MSB;
	reg->val = (gain >> IMX415_SHIFT_8_BITS) & IMX415_MASK_LSB_1_BITS;

	(reg + 1)->addr = IMX415_ANALOG_GAIN_ADDR_LSB;
	(reg + 1)->val = (gain) & IMX415_MASK_LSB_8_BITS;
}

static inline int imx415_read_reg(struct camera_common_data *s_data,
				  u16 addr, u8 *val)
{
	int err = 0;
	u32 reg_val = 0;

	err = regmap_read(s_data->regmap, addr, &reg_val);
	*val = reg_val & 0xff;

	return err;
}

static inline int imx415_write_reg(struct camera_common_data *s_data,
				   u16 addr, u8 val)
{
	int err = 0;

	err = regmap_write(s_data->regmap, addr, val);
	if (err)
		dev_err(s_data->dev, "%s: i2c write failed, 0x%x = %x",
			__func__, addr, val);

	return err;
}

static int imx415_write_table(struct imx415 *priv, const imx415_reg table[])
{
	return regmap_util_write_table_8(priv->s_data->regmap, table, NULL, 0,
					 IMX415_TABLE_WAIT_MS,
					 IMX415_TABLE_END);
}

static int imx415_set_group_hold(struct tegracam_device *tc_dev, bool val)
{
	struct camera_common_data *s_data = tc_dev->s_data;
	struct device *dev = tc_dev->dev;
	int err;

	dev_dbg(dev, "%s: Setting group hold control to: %u
", __func__, val);

	err = imx415_write_reg(s_data, IMX415_GROUP_HOLD_ADDR, val);
	if (err) {
		dev_err(dev, "%s: Group hold control error
", __func__);
		return err;
	}

	return 0;
}

//val = gain[dB] * gain_factor
static int imx415_set_gain(struct tegracam_device *tc_dev, s64 val)
{
	struct camera_common_data *s_data = tc_dev->s_data;
	struct device *dev = s_data->dev;
	const struct sensor_mode_properties *mode =
	    &s_data->sensor_props.sensor_modes[s_data->mode_prop_idx];
	int err = 0, i = 0;
	imx415_reg gain_reg[2];
//	s16 gain;
	s16 gain_pcg_0 = 0;

	dev_info(dev, "%s: mode index: %d
", __func__, s_data->mode_prop_idx);

	dev_info(dev, "%s: Setting gain control to: %lld
", __func__, val);
	dev_info(dev, "%s: min_gain_val: %d
", __func__, mode->control_properties.min_gain_val);
	dev_info(dev, "%s: max_gain_val: %d
", __func__, mode->control_properties.max_gain_val);
	dev_info(dev, "%s: gain_factor: %d
", __func__, mode->control_properties.gain_factor);

	//增益
	if (val < mode->control_properties.min_gain_val)
		val = mode->control_properties.min_gain_val;
	else if (val > mode->control_properties.max_gain_val)
		val = mode->control_properties.max_gain_val;

	if (val == 0)
		return -EINVAL;

	/* gain_pcg_0 = gain[dB] * 10 / 3 */
	gain_pcg_0 = val * 10 / mode->control_properties.gain_factor / 3;
	dev_info(dev, "%s: gain_pcg_0: %d
", __func__, gain_pcg_0);

	if (gain_pcg_0 < IMX415_GAIN_PCG_0_MIN)
		gain_pcg_0 = IMX415_GAIN_PCG_0_MIN;
	else if (gain_pcg_0 > IMX415_GAIN_PCG_0_MAX)
		gain_pcg_0 = IMX415_GAIN_PCG_0_MAX;

	dev_info(dev, "%s: gain_pcg_0 result: %d
", __func__, gain_pcg_0);

	imx415_get_gain_reg(gain_reg, (u16) gain_pcg_0);

	for (i = 0; i < ARRAY_SIZE(gain_reg); i++) {
		dev_info(dev, "%s: set gain:  Addr: 0x%x  Value: %d
", __func__, gain_reg[i].addr, gain_reg[i].val);
		err = imx415_write_reg(s_data, gain_reg[i].addr,
				       gain_reg[i].val);
		if (err) {
			dev_err(dev, "%s: gain control error
", __func__);
			break;
		}
	}

	return err;
}

static int imx415_set_frame_rate(struct tegracam_device *tc_dev, s64 val)
{
	return 0;
}

//val: 曝光时间,单位:us
static int imx415_set_exposure(struct tegracam_device *tc_dev, s64 val)
{
	struct camera_common_data *s_data = tc_dev->s_data;
//	struct imx415 *priv = (struct imx415 *)tc_dev->priv;
	struct device *dev = tc_dev->dev;
	const struct sensor_mode_properties *mode =
	    &s_data->sensor_props.sensor_modes[s_data->mode_prop_idx];

	int err = 0;
	imx415_reg shr0_regs[3] = {0};
	u8 vmax_array[3] = {0};
	int vmax_value = 0;
	int shr0 = 0;
	u8 reg_adbit = 0;
	int Toffset = 0;
	int Toffset_LineNum = 0, Val_LineNum = 0;
	int line_length = 0;
	int i;

	dev_info(dev, "%s: mode index: %d
", __func__, s_data->mode_prop_idx);

	dev_info(dev, "%s: pixel_clock: %lld
", __func__, mode->signal_properties.pixel_clock.val);
	dev_info(dev, "%s: exposure_factor: %d
", __func__, mode->control_properties.exposure_factor);
	dev_info(dev, "%s: line_length: %d
", __func__, mode->image_properties.line_length);

	if (mode->signal_properties.pixel_clock.val == 0 ||
		 mode->control_properties.exposure_factor == 0 ||
		 mode->image_properties.line_length == 0)
		return -EINVAL;

	err = imx415_read_reg(s_data, IMX415_VTS_REG_L, &vmax_array[0]);
	if (err) {
		dev_info(dev, "%s: imx415_read_reg IMX415_VTS_REG_L failed
", __func__);
		return err;
	}
	err = imx415_read_reg(s_data, IMX415_VTS_REG_M, &vmax_array[1]);
	if (err) {
		dev_info(dev, "%s: imx415_read_reg IMX415_VTS_REG_M failed
", __func__);
		return err;
	}
	err = imx415_read_reg(s_data, IMX415_VTS_REG_H, &vmax_array[2]);
	if (err) {
		dev_info(dev, "%s: imx415_read_reg IMX415_VTS_REG_M failed
", __func__);
		return err;
	}

	vmax_value = ((vmax_array[2] << 16) & 0x0f0000) + ((vmax_array[1] << 8) & 0xff00) + vmax_array[0];
	dev_info(dev, "%s: vmax_value: %d, val: %lld
", __func__, vmax_value, val);


	/*
		Integration time(曝光时间) = 1 frame period - SHR0 x (1H period) + Toffset
		AD 10bit: Toffset = 1.79 us
		AD 12bit: Toffset = 2.68us
	*/
	/* 
		val / mode->control_properties.exposure_factor = 曝光时间(us)
		曝光时间 * mode->signal_properties.pixel_clock.val = 像素数量
		像素数量 / mode->image_properties.line_length = 行数,即:shr0
	*/
	err = imx415_read_reg(s_data, IMX415_ADBIT_ADDR, &reg_adbit);
	if (err) {
		dev_info(dev, "%s: imx415_read_reg IMX415_ADBIT_ADDR failed
", __func__);
		return err;
	}
	if (reg_adbit == IMX415_ADBIT_10BIT) {
		Toffset = 1790;		//单位:ns
	} else {
		Toffset = 2680;		//单位:ns
	}

	/*	
		Integration time(曝光时间) = 1 frame period - SHR0 x (1H period) + Toffset
		SHR0 = height + Toffset_LineNum - Val_LineNum;
	
		Toffset_LineNum(Toffset的行数) = Toffset * mode->signal_properties.pixel_clock.val / 1000  / line_length / mode->control_properties.exposure_factor;
		Val_LineNum(val的行数) = val * mode->signal_properties.pixel_clock.val / line_length / mode->control_properties.exposure_factor;
	*/
	line_length = mode->image_properties.line_length;
	Toffset_LineNum = Toffset * mode->signal_properties.pixel_clock.val / 1000  / line_length / mode->control_properties.exposure_factor;
	Val_LineNum = val * mode->signal_properties.pixel_clock.val / line_length / mode->control_properties.exposure_factor;

	dev_info(dev, "%s: line_length: %d, vmax_value : %d
", __func__, line_length, vmax_value);
	dev_info(dev, "%s: Toffset_LineNum: %d, Val_LineNum : %d
", __func__, Toffset_LineNum, Val_LineNum);
	shr0 = vmax_value + Toffset_LineNum - Val_LineNum;

	/* shr0值:8 - (VMAX - 4)之间 */
	if (shr0 < 8) {
		shr0 = 8;
	} else if (shr0 >= vmax_value - 4) {
		shr0 = vmax_value - 4;
	}

	//shr0 = vmax_value - 500;
	dev_info(dev, "%s: shr0_val: %d
", __func__, shr0);
	imx415_get_shr0_regs(shr0_regs, shr0);

	for (i = 0; i < ARRAY_SIZE(shr0_regs); i++) {
		dev_info(dev, "%s: set shr0  Addr: 0x%x  Value: %d
", __func__, shr0_regs[i].addr, shr0_regs[i].val);
		err = imx415_write_reg(s_data, shr0_regs[i].addr, shr0_regs[i].val);
		if (err) {
			dev_dbg(dev,
				"%s: shr0 control error
", __func__);
			return err;
		}
	}

	return err;
}

//用于更新传感器参数
static struct tegracam_ctrl_ops imx415_ctrl_ops = {
	.numctrls = ARRAY_SIZE(ctrl_cid_list),
	.ctrl_cid_list = ctrl_cid_list,
	.set_gain = imx415_set_gain,
	.set_exposure = imx415_set_exposure,
	.set_frame_rate = imx415_set_frame_rate,		//返回0
	.set_group_hold = imx415_set_group_hold,
};

static int imx415_power_on(struct camera_common_data *s_data)
{
	int err = 0;
	struct camera_common_power_rail *pw = s_data->power;
	struct camera_common_pdata *pdata = s_data->pdata;
	struct device *dev = s_data->dev;

	dev_info(dev, "%s: power on
", __func__);
	if (pdata && pdata->power_on) {
		err = pdata->power_on(pw);
		if (err)
			dev_err(dev, "%s failed.
", __func__);
		else
			pw->state = SWITCH_ON;
		return err;
	}

#if 0	//dongao
	if (pw->reset_gpio) {
		if (gpiod_cansleep(gpio_to_desc(pw->reset_gpio)))
			gpio_set_value_cansleep(pw->reset_gpio, 0);
		else
			gpio_set_value(pw->reset_gpio, 0);
	}
#else
	if (pw->reset_gpio) {
		dev_info(dev, "dongao: reset_gpio  hi
");
		gpiod_direction_output(gpio_to_desc(pw->reset_gpio), 0);		//复位无效
	}
#endif

	if (unlikely(!(pw->avdd || pw->iovdd || pw->dvdd)))
		goto skip_power_seqn;

	usleep_range(10, 20);

	if (pw->avdd) {
		err = regulator_enable(pw->avdd);
		if (err)
			goto imx415_avdd_fail;
	}

	if (pw->iovdd) {
		err = regulator_enable(pw->iovdd);
		if (err)
			goto imx415_iovdd_fail;
	}

	if (pw->dvdd) {
		err = regulator_enable(pw->dvdd);
		if (err)
			goto imx415_dvdd_fail;
	}

	usleep_range(10, 20);

skip_power_seqn:
#if 0	//dongao
	if (pw->reset_gpio) {
		if (gpiod_cansleep(gpio_to_desc(pw->reset_gpio)))
			gpio_set_value_cansleep(pw->reset_gpio, 1);
		else
			gpio_set_value(pw->reset_gpio, 1);
	}
#endif

	/* Need to wait for t4 + t5 + t9 + t10 time as per the data sheet */
	/* t4 - 200us, t5 - 21.2ms, t9 - 1.2ms t10 - 270 ms */
	usleep_range(300000, 300100);

	pw->state = SWITCH_ON;

	return 0;

imx415_dvdd_fail:
	regulator_disable(pw->iovdd);

imx415_iovdd_fail:
	regulator_disable(pw->avdd);

imx415_avdd_fail:
	dev_err(dev, "%s failed.
", __func__);

	return -ENODEV;
}

static int imx415_power_off(struct camera_common_data *s_data)
{
	int err = 0;
	struct camera_common_power_rail *pw = s_data->power;
	struct camera_common_pdata *pdata = s_data->pdata;
	struct device *dev = s_data->dev;

	dev_info(dev, "%s: power off
", __func__);

	if (pdata && pdata->power_off) {
		err = pdata->power_off(pw);
		if (err) {
			dev_err(dev, "%s failed.
", __func__);
			return err;
		}
	} else {
#if 0	//dongao
		if (pw->reset_gpio) {
			if (gpiod_cansleep(gpio_to_desc(pw->reset_gpio)))
				gpio_set_value_cansleep(pw->reset_gpio, 0);
			else
				gpio_set_value(pw->reset_gpio, 0);
		}
#else
		if (pw->reset_gpio) {
			dev_info(dev, "dongao: reset_gpio  low
");
			gpiod_direction_output(gpio_to_desc(pw->reset_gpio), 1);		//复位有效
		}
#endif

		usleep_range(10, 10);

		if (pw->dvdd)
			regulator_disable(pw->dvdd);
		if (pw->iovdd)
			regulator_disable(pw->iovdd);
		if (pw->avdd)
			regulator_disable(pw->avdd);
	}

	pw->state = SWITCH_OFF;

	return 0;
}

static int imx415_power_put(struct tegracam_device *tc_dev)
{
	struct camera_common_data *s_data = tc_dev->s_data;
	struct camera_common_power_rail *pw = s_data->power;

	if (unlikely(!pw))
		return -EFAULT;

	if (likely(pw->dvdd))
		devm_regulator_put(pw->dvdd);

	if (likely(pw->avdd))
		devm_regulator_put(pw->avdd);

	if (likely(pw->iovdd))
		devm_regulator_put(pw->iovdd);

	pw->dvdd = NULL;
	pw->avdd = NULL;
	pw->iovdd = NULL;

	if (likely(pw->reset_gpio))
		gpio_free(pw->reset_gpio);

	return 0;
}

static int imx415_power_get(struct tegracam_device *tc_dev)
{
	struct device *dev = tc_dev->dev;
	struct camera_common_data *s_data = tc_dev->s_data;
	struct camera_common_power_rail *pw = s_data->power;
	struct camera_common_pdata *pdata = s_data->pdata;
	struct clk *parent;
	int err = 0;

	if (!pdata) {
		dev_err(dev, "pdata missing
");
		return -EFAULT;
	}

	dev_info(tc_dev->dev, "dongao:%s, mclk_name:%s
", __func__, pdata->mclk_name);

	/* Sensor MCLK (aka. INCK) */
	if (pdata->mclk_name) {
		pw->mclk = devm_clk_get(dev, pdata->mclk_name);
		if (IS_ERR(pw->mclk)) {
			dev_err(dev, "unable to get clock %s
",
				pdata->mclk_name);
			return PTR_ERR(pw->mclk);
		}

		if (pdata->parentclk_name) {
			parent = devm_clk_get(dev, pdata->parentclk_name);
			if (IS_ERR(parent)) {
				dev_err(dev, "unable to get parent clock %s",
					pdata->parentclk_name);
			} else
				clk_set_parent(pw->mclk, parent);
		}
	}


	/* analog 2.8v */
	if (pdata->regulators.avdd)
		err |= camera_common_regulator_get(dev,
						   &pw->avdd,
						   pdata->regulators.avdd);
	/* IO 1.8v */
	if (pdata->regulators.iovdd)
		err |= camera_common_regulator_get(dev,
						   &pw->iovdd,
						   pdata->regulators.iovdd);
	/* dig 1.2v */
	if (pdata->regulators.dvdd)
		err |= camera_common_regulator_get(dev,
						   &pw->dvdd,
						   pdata->regulators.dvdd);
	if (err) {
		dev_err(dev, "%s: unable to get regulator(s)
", __func__);
		goto done;
	}

	/* Reset or ENABLE GPIO */
	pw->reset_gpio = pdata->reset_gpio;
	err = gpio_request(pw->reset_gpio, "cam_reset_gpio");
	if (err < 0) {
		dev_err(dev, "%s: unable to request reset_gpio (%d)
",
			__func__, err);
		goto done;
	}

done:
	pw->state = SWITCH_OFF;

	return err;
}

static struct camera_common_pdata *imx415_parse_dt(struct tegracam_device
						   *tc_dev)
{
	struct device *dev = tc_dev->dev;
	struct device_node *np = dev->of_node;
	struct camera_common_pdata *board_priv_pdata;
	const struct of_device_id *match;
	struct camera_common_pdata *ret = NULL;
	int err = 0;
	int gpio;

	if (!np)
		return NULL;

	match = of_match_device(imx415_of_match, dev);
	if (!match) {
		dev_err(dev, "Failed to find matching dt id
");
		return NULL;
	}

	board_priv_pdata = devm_kzalloc(dev,
					sizeof(*board_priv_pdata), GFP_KERNEL);
	if (!board_priv_pdata)
		return NULL;

	gpio = of_get_named_gpio(np, "reset-gpios", 0);
	if (gpio < 0) {
		if (gpio == -EPROBE_DEFER)
			ret = ERR_PTR(-EPROBE_DEFER);
		dev_err(dev, "reset-gpios not found
");
		goto error;
	}
	board_priv_pdata->reset_gpio = (unsigned int)gpio;

	err = of_property_read_string(np, "mclk", &board_priv_pdata->mclk_name);
	if (err)
		dev_dbg(dev,
			"mclk name not present, assume sensor driven externally
");

	err = of_property_read_string(np, "avdd-reg",
				      &board_priv_pdata->regulators.avdd);
	err |= of_property_read_string(np, "iovdd-reg",
				       &board_priv_pdata->regulators.iovdd);
	err |= of_property_read_string(np, "dvdd-reg",
				       &board_priv_pdata->regulators.dvdd);
	if (err)
		dev_dbg(dev,
		"avdd, iovdd and/or dvdd reglrs. not present, assume sensor powered independently
");

	board_priv_pdata->has_eeprom = of_property_read_bool(np, "has-eeprom");

	return board_priv_pdata;

error:
	devm_kfree(dev, board_priv_pdata);

	return ret;
}

static int imx415_set_mode(struct tegracam_device *tc_dev)
{
	struct imx415 *priv = (struct imx415 *)tegracam_get_privdata(tc_dev);
	struct camera_common_data *s_data = tc_dev->s_data;
	unsigned int mode_index = 0;
	int err = 0;
	const char *config;
	struct device_node *mode;
	uint offset = ARRAY_SIZE(imx415_frmfmt);

	usleep_range(1000000, 1000100);


	dev_dbg(tc_dev->dev, "%s:
", __func__);
	mode = of_get_child_by_name(tc_dev->dev->of_node, "mode0");
	err = of_property_read_string(mode, "num_lanes", &config);

	if (config[0] == '4')
		priv->config = FOUR_LANE_CONFIG;
	else if (config[0] == '2')
		priv->config = TWO_LANE_CONFIG;
	else
		dev_err(tc_dev->dev, "Unsupported config
");

	dev_info(tc_dev->dev, "dongao: %s:
", __func__);
	
	if (mode_table[IMX415_MODE_COMMON] != NULL) {
		err = imx415_write_table(priv, mode_table[IMX415_MODE_COMMON]);
		if (err)
			return err;
	}

#if 0	//dongao
	err = imx415_write_table(priv, mode_table[IMX415_MODE_3864x2192_30FPS_4LANE]);
#else
	mode_index = s_data->mode;
	dev_info(tc_dev->dev, "%s, mode_index = %d, offset = %d
", __func__, mode_index, offset);
	if (priv->config == FOUR_LANE_CONFIG)
		err = imx415_write_table(priv, mode_table[mode_index + offset]);
	else
		err = imx415_write_table(priv, mode_table[mode_index]);
#endif

	if (err)
		return err;

	return 0;
}

static int imx415_start_streaming(struct tegracam_device *tc_dev)
{
	struct imx415 *priv = (struct imx415 *)tegracam_get_privdata(tc_dev);
	struct camera_common_data *s_data = priv->s_data;
	u8 regdata = 0;
	u16 regaddr = 0;
	int i = 0;
	int err = -1;

	/* dongao 读寄存器并打印 */
	for (i = 0; i < ARRAY_SIZE(imx415_linear_12bit_3864x2192_891M_regs); i++) {
		regaddr = imx415_linear_12bit_3864x2192_891M_regs[i].addr;

		if (regaddr == IMX415_TABLE_WAIT_MS || regaddr == IMX415_TABLE_END) {
			continue;
		}

		err = imx415_read_reg(s_data, regaddr, &regdata);
		if (err) {
			dev_info(tc_dev->dev, "%s:  read 0x%04x failed
", __func__, regaddr);
		}else {
			dev_info(tc_dev->dev, "%s dongao read: 0x%04x - 0x%x
", __func__, regaddr, regdata);
		}

		if (regdata != imx415_linear_12bit_3864x2192_891M_regs[i].val) {
			dev_info(tc_dev->dev, "%s: !!!!!!! read 0x%x != 0x%x 
", __func__, regdata, imx415_linear_12bit_3864x2192_891M_regs[i].val);
		}
	}

	dev_dbg(tc_dev->dev, "%s:
", __func__);
	err = imx415_write_table(priv, mode_table[IMX415_START_STREAM]);

	usleep_range(24000, 30000);

	err = imx415_write_reg(priv->s_data, IMX415_XMSTA_REG, 0);		//XMSTA start
	
	usleep_range(500000, 500100);
	return 0;
}

static int imx415_stop_streaming(struct tegracam_device *tc_dev)
{
	int err;
	struct imx415 *priv = (struct imx415 *)tegracam_get_privdata(tc_dev);

	dev_info(tc_dev->dev, "dongao:%s
", __func__);

	dev_dbg(tc_dev->dev, "%s:
", __func__);
	err = imx415_write_table(priv, mode_table[IMX415_STOP_STREAM]);

	imx415_write_reg(priv->s_data, IMX415_XMSTA_REG, 1);		//XMSTA stop

	return err;
}

//sensor驱动程序使用的基本功能
static struct camera_common_sensor_ops imx415_common_ops = {
	.numfrmfmts = ARRAY_SIZE(imx415_frmfmt),
	.frmfmt_table = imx415_frmfmt,
	.power_on = imx415_power_on,
	.power_off = imx415_power_off,
	.write_reg = imx415_write_reg,
	.read_reg = imx415_read_reg,
	.parse_dt = imx415_parse_dt,
	.power_get = imx415_power_get,
	.power_put = imx415_power_put,
	.set_mode = imx415_set_mode,
	.start_streaming = imx415_start_streaming,
	.stop_streaming = imx415_stop_streaming,
};

static int imx415_board_setup(struct imx415 *priv)
{
	struct camera_common_data *s_data = priv->s_data;
	struct device *dev = s_data->dev;
	u8 reg_val[2];
	int err = 0;

	/* Skip mclk enable as this camera has an internal oscillator */

	err = imx415_power_on(s_data);
	if (err) {
		dev_err(dev, "error during power on sensor (%d)
", err);
		goto done;
	}

	/* Probe sensor model id registers */
	err = imx415_read_reg(s_data, IMX415_MODEL_ID_ADDR_MSB, &reg_val[0]);
	if (err) {
		dev_err(dev, "%s: error during i2c read probe (%d)
",
			__func__, err);
		goto err_reg_probe;
	}
	err = imx415_read_reg(s_data, IMX415_MODEL_ID_ADDR_LSB, &reg_val[1]);
	if (err) {
		dev_err(dev, "%s: error during i2c read probe (%d)
",
			__func__, err);
		goto err_reg_probe;
	}

	//dongao
	dev_info(dev, "dongao: sensor model id: %x-%x
", IMX415_MODEL_ID_ADDR_MSB, reg_val[0]);
	dev_info(dev, "dongao: sensor model id: %x-%x
", IMX415_MODEL_ID_ADDR_LSB, reg_val[1]);

#if 0	//dongao
	/* Sensor fine integration time */
	err = imx415_get_fine_integ_time(priv, &priv->fine_integ_time);
	if (err)
		dev_err(dev, "%s: error querying sensor fine integ. time
",
			__func__);
#endif

err_reg_probe:
	imx415_power_off(s_data);

done:
	return err;
}

static int imx415_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
{
	struct i2c_client *client = v4l2_get_subdevdata(sd);

	dev_info(&client->dev, "%s:
", __func__);

	return 0;
}

//V4L2 框架使用 V4L2 子设备 op 来启动驱动程序
static const struct v4l2_subdev_internal_ops imx415_subdev_internal_ops = {
	.open = imx415_open,
};

#if defined(NV_I2C_DRIVER_STRUCT_PROBE_WITHOUT_I2C_DEVICE_ID_ARG) /* Linux 6.3 */
static int imx415_probe(struct i2c_client *client)
#else
static int imx415_probe(struct i2c_client *client,
			const struct i2c_device_id *id)
#endif
{
	struct device *dev = &client->dev;
	struct tegracam_device *tc_dev;
	struct imx415 *priv;
	int err;

	dev_info(dev, "dongao: imx415_probe_V1.0
");

	dev_dbg(dev, "probing v4l2 sensor at addr 0x%0x
", client->addr);

	if (!IS_ENABLED(CONFIG_OF) || !client->dev.of_node)
		return -EINVAL;

	priv = devm_kzalloc(dev, sizeof(struct imx415), GFP_KERNEL);
	if (!priv)
		return -ENOMEM;

	tc_dev = devm_kzalloc(dev, sizeof(struct tegracam_device), GFP_KERNEL);
	if (!tc_dev)
		return -ENOMEM;

	priv->i2c_client = tc_dev->client = client;
	tc_dev->dev = dev;
	strncpy(tc_dev->name, "imx415", sizeof(tc_dev->name));
	tc_dev->dev_regmap_config = &sensor_regmap_config;
	tc_dev->sensor_ops = &imx415_common_ops;						//通用操作集
	tc_dev->v4l2sd_internal_ops = &imx415_subdev_internal_ops;		//提供 open
	tc_dev->tcctrl_ops = &imx415_ctrl_ops;

	err = tegracam_device_register(tc_dev);			//解析设备树,参数保存到 tegracam_device
	if (err) {
		dev_err(dev, "tegra camera driver registration failed
");
		return err;
	}
	priv->tc_dev = tc_dev;
	priv->s_data = tc_dev->s_data;
	priv->subdev = &tc_dev->s_data->subdev;
	tegracam_set_privdata(tc_dev, (void *)priv);

	err = imx415_board_setup(priv);
	if (err) {
		dev_err(dev, "board setup failed
");
		return err;
	}

	err = tegracam_v4l2subdev_register(tc_dev, true);		//v4l2_i2c_subdev_init 等
	if (err) {
		tegracam_device_unregister(tc_dev);
		dev_err(dev, "tegra camera subdev registration failed
");
		return err;
	}

	dev_info(dev, "detected imx415 sensor
");

	return 0;
}

#if defined(NV_I2C_DRIVER_STRUCT_REMOVE_RETURN_TYPE_INT) /* Linux 6.1 */
static int imx415_remove(struct i2c_client *client)
#else
static void imx415_remove(struct i2c_client *client)
#endif
{
	struct camera_common_data *s_data = to_camera_common_data(&client->dev);
	struct imx415 *priv;

	if (!s_data) {
		dev_err(&client->dev, "camera common data is NULL
");
#if defined(NV_I2C_DRIVER_STRUCT_REMOVE_RETURN_TYPE_INT) /* Linux 6.1 */
		return -EINVAL;
#else
		return;
#endif
	}
	priv = (struct imx415 *)s_data->priv;

	tegracam_v4l2subdev_unregister(priv->tc_dev);
	tegracam_device_unregister(priv->tc_dev);
#if defined(NV_I2C_DRIVER_STRUCT_REMOVE_RETURN_TYPE_INT) /* Linux 6.1 */
	return 0;
#endif
}

static const struct i2c_device_id imx415_id[] = {
	{"imx415", 0},
	{}
};

MODULE_DEVICE_TABLE(i2c, imx415_id);

static struct i2c_driver imx415_i2c_driver = {
	.driver = {
		   .name = "imx415",
		   .owner = THIS_MODULE,
		   .of_match_table = of_match_ptr(imx415_of_match),
		   },
	.probe = imx415_probe,
	.remove = imx415_remove,
	.id_table = imx415_id,
};

module_i2c_driver(imx415_i2c_driver);

MODULE_DESCRIPTION("Media Controller driver for Sony IMX415");
MODULE_AUTHOR("DONGAO");
MODULE_LICENSE("GPL v2");

4)将 nv_imx415 添加到Makefile

Linux_for_Tegra/source/nvidia-oot/drivers/media/i2c/Makefile

源码链接:基于英伟达Jetsonorinnano平台开发的imx415驱动资源-CSDN文库

二、编译调试


1、编译imx415驱动

我们修改的 nvidia-oot/ 目录下的驱动 需编译可加载内核模块 (LKM):

Linux_for_Tegra/source 目录下执行:

$ export CROSS_COMPILE=$HOME/l4t-gcc/aarch64--glibc--stable-2022.08-1/bin/aarch64-buildroot-linux-gnu-
$ export KERNEL_HEADERS=$PWD/kernel/kernel-jammy-src		
$ make modules	

方法一:安装后,整体刷机

$ export INSTALL_MOD_PATH=<install-path>/Linux_for_Tegra/rootfs/
$ sudo -E make modules_install

 安装完成后,刷机即可

方法二:使用scp 将 .ko 驱动直接拷贝到开发板

编译生成 nv_imx415.ko 传入开发板:

$ scp nvidia-oot/drivers/media/i2c/nv_imx415.ko nvidia@192.168.7.75:/tmp

开发板上重新加载 nv_imx415.ko

$ sudo insmod nv_imx415.ko

驱动加载成功后,生成 /dev/video0 节点

2、安装 v4l-utils 并查看 /dev/video0信息

2.1 安装 v4l-utils

先连上网线,确保板子能访问外网

$ sudo apt-get update
$ sudo apt-get install v4l-utils

运行过程:

2.2 查看 /dev/video0 信息

1)查看摄像头支持的图像格式和分辨率

$ v4l2-ctl -d /dev/video0 –list-formats-ext

2) 列出设备信息

$ v4l2-ctl –list-devices

/dev/video0:视频捕获设备节点

/dev/media0:media子系统设备节点

3)获取video0格式

$ v4l2-ctl -d /dev/video0 –get-fmt-video

4)查看 vi、csi、imx415 端口绑定结果

$ sudo media-ctl -p -d /dev/media0

$ v4l2-ctl -d /dev/video0 –list-ctrls 查看各种参数

root@localhost:/tmp# v4l2-ctl -d /dev/video0 --list-ctrls

Camera Controls

                     group_hold 0x009a2003 (bool)   : default=0 value=0 flags=execute-on-write
                    sensor_mode 0x009a2008 (int64)  : min=0 max=2 step=1 default=0 value=0 flags=slider
                           gain 0x009a2009 (int64)  : min=0 max=241 step=1 default=0 value=100 flags=slider
                       exposure 0x009a200a (int64)  : min=13 max=683710 step=1 default=3000 value=13 flags=slider
                     frame_rate 0x009a200b (int64)  : min=2000000 max=30000000 step=1 default=15000000 value=2000000 flags=slider
           sensor_configuration 0x009a2032 (u32)    : min=0 max=4294967295 step=1 default=0 dims=[22] flags=read-only, volatile, has-payload
         sensor_mode_i2c_packet 0x009a2033 (u32)    : min=0 max=4294967295 step=1 default=0 dims=[1026] flags=read-only, volatile, has-payload
      sensor_control_i2c_packet 0x009a2034 (u32)    : min=0 max=4294967295 step=1 default=0 dims=[1026] flags=read-only, volatile, has-payload
                    bypass_mode 0x009a2064 (intmenu): min=0 max=1 default=0 value=0 (0 0x0)
                override_enable 0x009a2065 (intmenu): min=0 max=1 default=0 value=0 (0 0x0)
                   height_align 0x009a2066 (int)    : min=1 max=16 step=1 default=1 value=1
                     size_align 0x009a2067 (intmenu): min=0 max=2 default=0 value=0 (1 0x1)
               write_isp_format 0x009a2068 (int)    : min=1 max=1 step=1 default=1 value=1
       sensor_signal_properties 0x009a2069 (u32)    : min=0 max=4294967295 step=1 default=0 dims=[30][18] flags=read-only, has-payload
        sensor_image_properties 0x009a206a (u32)    : min=0 max=4294967295 step=1 default=0 dims=[30][16] flags=read-only, has-payload
      sensor_control_properties 0x009a206b (u32)    : min=0 max=4294967295 step=1 default=0 dims=[30][36] flags=read-only, has-payload
              sensor_dv_timings 0x009a206c (u32)    : min=0 max=4294967295 step=1 default=0 dims=[30][16] flags=read-only, has-payload
               low_latency_mode 0x009a206d (bool)   : default=0 value=0
               preferred_stride 0x009a206e (int)    : min=0 max=65535 step=1 default=0 value=0
    override_capture_timeout_ms 0x009a206f (int)    : min=-1 max=2147483647 step=1 default=15000 value=15000
                   sensor_modes 0x009a2082 (int)    : min=0 max=30 step=1 default=30 value=2 flags=read-only


//获取单个参数
nvidia@localhost:~$ v4l2-ctl -d /dev/video0 --get-ctrl=exposure
exposure: 13

3、抓拍

3.1 v4l2-ctl 命令验证vi抓拍

$ sudo v4l2-ctl –set-fmt-video=width=3864,height=2192,pixelformat=GB12 –stream-skip=8 –stream-mmap –stream-count=100 -d /dev/video0 –stream-to=imx415.raw

命令执行结果:

问题:明明配置的帧率为30帧/s,为什么这里只 15fps左右???

原因:采集数据写到 imx415.raw 文件花费了时间

不写文件验证,确实为30fps

$ v4l2-ctl –stream-mmap –stream-count=500 -d /dev/video0  

博主遇到v4l2-ctl抓拍的几个问题(没有深究):(Bayer/Packed)

v4l2-ctl命令抓 RAW10/RAW12 (Bayer/Packed)图片的大小有问题。1帧图片 packed RAW12 大小理论上为 3864 * 2192 * 1.5,但实际为 3864 * 2192 * 2

v4l2-ctl -d /dev/video0 –get-fmt-video 命令查看 Bytes per Line 为什么为 7728(3864 * 2)?

抓到的raw12图片放大后,就是这种gbrg的小方格

3.2 gstreamer英伟达命令抓拍

先安装SDK组件,然后再执行英伟达抓拍命令,否则会报错

$ sudo apt-get update -y
$ sudo apt-get install nvidia-jetpack -y //安装SDK
1)gst-launch-1.0 自动曝光、自动增益抓拍

$ gst-launch-1.0 nvarguscamerasrc sensor-id=0 num-buffers=15 ! 'video/x-raw(memory:NVMM), width=(int)3864,height=(int)2192, framerate=15/1' ! nvvidconv flip-method=2 ! 'video/x-raw, format=(string)I420' ! jpegenc ! multifilesink location=camera0.jpg -e

正常打印(帧率要比实际帧率小,否则命令可能报错):

正常照片:

2)gst-launch-1.0 设置曝光、增益抓拍

$ gst-launch-1.0 nvarguscamerasrc exposuretimerange=”200000 200000″ gainrange=”8 8″ sensor-id=0 num-buffers=15 ! 'video/x-raw(memory:NVMM), width=(int)3864,height=(int)2192, framerate=15/1' ! nvvidconv flip-method=2 ! 'video/x-raw, format=(string)I420' ! jpegenc ! multifilesink location=camera5.jpg -e

exposuretimerange=”200000 200000″ 表示固定曝光200us

gainrange=”8 8″ 表示固定增益8x

当曝光设为200us、增益8x 拍照结果:

三、imx415驱动开发遇到的问题及解决方法


Nvidia平台trace查看方法:

$ sudo su
$ modprobe rtcpu_debug
$ echo 1 > /sys/kernel/debug/tracing/tracing_on
$ echo 30720 > /sys/kernel/debug/tracing/buffer_size_kb
$ echo 1 > /sys/kernel/debug/tracing/events/tegra_rtcpu/enable
$ echo 1 > /sys/kernel/debug/tracing/events/freertos/enable
$ echo 3 > /sys/kernel/debug/camrtc/log-level
$ echo 1 > /sys/kernel/debug/tracing/events/camera_common/enable
$ echo  > /sys/kernel/debug/tracing/trace
$ cat /sys/kernel/debug/tracing/trace		//运行 v4l2-ctl 抓图后,调用此命令可以看到 nvidia的 trace日志

1、Jetson orin nano开发板的CAM0不支持4lane模式

原因:CS1_CLK 仅支持2lane

分析过程:

Orin Nano Devkit 的摄像头 #0 连接器 (J20) 上的 CSI0_D0_N/P 和 CSI0_D1_N/P 的设计目的是什么?- Jetson & Embedded Systems / Jetson Orin Nano – NVIDIA开发者论坛

2、使用 CSI0/1 时,需要将lane_polarity改为 6

说明:因为我用的 CSI2,因此lane_polarity为0

设备树中提到 lane_polarity

3、v4l2-ctl 抓图命令报错

$ sudo v4l2-ctl –set-fmt-video=width=3864,height=2192,pixelformat=GB12 –stream-mmap –stream-count=1 -d /dev/video0 –stream-to=imx415.raw

报错 2500ms超时:

 tegra-camrtc-capture-vi tegra-capture-vi: uncorr_err: request timed out after 2500 ms

trace日志:

    kworker/3:2-160     [003] ....    72.586960: rtcpu_string: tstamp:3691891507 id:0x04010000 str:"VM0 activating."
     kworker/3:2-160     [003] ....    72.642962: rtcpu_vinotify_event: tstamp:3692561650 cch:0 vi:0 tag:VIFALC_TDSTATE channel:0x23 frame:0 vi_tstamp:118146060768 data:0x759d580010000000
     kworker/3:2-160     [003] ....    72.642964: rtcpu_vinotify_event: tstamp:3692561788 cch:0 vi:0 tag:VIFALC_TDSTATE channel:0x23 frame:0 vi_tstamp:118146067232 data:0x0000000031000001
     kworker/3:2-160     [003] ....    72.642965: rtcpu_vinotify_event: tstamp:3692561941 cch:0 vi:0 tag:VIFALC_TDSTATE channel:0x23 frame:0 vi_tstamp:118146127648 data:0x759d550010000000
     kworker/3:2-160     [003] ....    72.642965: rtcpu_vinotify_event: tstamp:3692562072 cch:0 vi:0 tag:VIFALC_TDSTATE channel:0x23 frame:0 vi_tstamp:118146134176 data:0x0000000031000002
 vi-output, ov42-2565    [002] ....    75.279729: tegra_channel_capture_setup: vnc_id 0 W 1500 H 1500 fmt c4
     kworker/3:2-160     [003] ....    75.338965: rtcpu_vinotify_event: tstamp:3777198043 cch:0 vi:0 tag:VIFALC_TDSTATE channel:0x23 frame:0 vi_tstamp:120853141696 data:0x759d580010000000
     kworker/3:2-160     [003] ....    75.338967: rtcpu_vinotify_event: tstamp:3777198180 cch:0 vi:0 tag:VIFALC_TDSTATE channel:0x23 frame:0 vi_tstamp:120853184384 data:0x0000000031000001
     kworker/3:2-160     [003] ....    75.338968: rtcpu_vinotify_event: tstamp:3777198333 cch:0 vi:0 tag:VIFALC_TDSTATE channel:0x23 frame:0 vi_tstamp:120853202016 data:0x759d550010000000
     kworker/3:2-160     [003] ....    75.338969: rtcpu_vinotify_event: tstamp:3777198464 cch:0 vi:0 tag:VIFALC_TDSTATE channel:0x23 frame:0 vi_tstamp:120853262400 data:0x0000000031000002
 vi-output, ov42-2565    [001] ....    78.867978: tegra_channel_capture_setup: vnc_id 0 W 1500 H 1500 fmt c4
     kworker/3:2-160     [003] ....    86.154100: rtcpu_string: tstamp:4114987402 id:0x04010000 str:"VM0 deactivating."

原因:1)csi引脚没收到 mipi 信号,可能是sensor驱动问题(没有mipi信号输出),也有可能是硬件问题; 2)可能设备树pix_clk_hz值有问题

解决方法:1)可以用示波器看mipi引脚是否有输出; 2)mipi输出是否连到对应的CSI上

相似问题:Ov428 drives tegra-capture-vi timeout (jetpack 5.1.3-b29) – Jetson & Embedded Systems / Jetson Orin Nano – NVIDIA Developer Forums

4、v4l2-ctl 抓图不成功,vi能收到数据,但数据不对

trach信息

 vi-output, imx4-3067    [001] .......   904.941756: tegra_channel_capture_frame: sof:926.921297600
 vi-output, imx4-3067    [001] .......   904.941758: tegra_channel_capture_frame: eof:926.921328256
 vi-output, imx4-3066    [001] .......   904.942394: vi_task_submit: class_id:48 ch:0 syncpt_id:26 syncpt_thresh:964 pid:3066 tid:3066
     kworker/5:1-49      [005] .......   904.945896: rtcpu_vinotify_event: tstamp:28966226634 cch:0 vi:1 tag:CHANSEL_FAULT channel:0x23 frame:53 vi_tstamp:926918622816 data:0x00000000086f0182
     kworker/5:1-49      [005] .......   904.945897: rtcpu_vinotify_event: tstamp:28966226927 cch:0 vi:1 tag:ATOMP_FRAME_DONE channel:0x23 frame:53 vi_tstamp:926918623776 data:0x0000000000000000
     kworker/5:1-49      [005] .......   904.945897: rtcpu_vinotify_event: tstamp:28966227183 cch:0 vi:1 tag:CHANSEL_FAULT channel:0x23 frame:53 vi_tstamp:926918631008 data:0x00000000087001c0
     kworker/5:1-49      [005] .......   904.945898: rtcpu_vinotify_event: tstamp:28966297792 cch:0 vi:1 tag:FE channel:0x00 frame:53 vi_tstamp:926919571552 data:0x0000003500000022
     kworker/5:1-49      [005] .......   904.945898: rtcpu_vinotify_event: tstamp:28966298051 cch:0 vi:1 tag:CHANSEL_SHORT_FRAME channel:0x04 frame:53 vi_tstamp:926919571552 data:0x0001200000000000
     kworker/5:1-49      [005] .......   904.945898: rtcpu_vinotify_event: tstamp:28966298346 cch:0 vi:1 tag:ATOMP_FE channel:0x00 frame:53 vi_tstamp:926919571584 data:0x0000000800000000
     kworker/5:1-49      [005] .......   904.945899: rtcpu_vinotify_event: tstamp:28966298600 cch:0 vi:1 tag:VIFALC_ACTIONLST channel:0x23 frame:0 vi_tstamp:926919590752 data:0x0000000007020036
     kworker/5:1-49      [005] .......   904.945899: rtcpu_vinotify_event: tstamp:28966298885 cch:0 vi:1 tag:VIFALC_TDSTATE channel:0x23 frame:0 vi_tstamp:926919605216 data:0x799e300010000000
     kworker/5:1-49      [005] .......   904.945899: rtcpu_vinotify_event: tstamp:28966299137 cch:0 vi:1 tag:VIFALC_TDSTATE channel:0x23 frame:0 vi_tstamp:926919615040 data:0x0000000031000037
     kworker/5:1-49      [005] .......   904.945900: rtcpu_vinotify_event: tstamp:28966299418 cch:0 vi:1 tag:FS channel:0x00 frame:54 vi_tstamp:926920171200 data:0x0000003600000012
     kworker/5:1-49      [005] .......   904.945900: rtcpu_vinotify_event: tstamp:28966299672 cch:0 vi:1 tag:ATOMP_FS channel:0x00 frame:54 vi_tstamp:926920171200 data:0x0000000800000000
     kworker/5:1-49      [005] .......   904.945901: rtcpu_vinotify_event: tstamp:28966299954 cch:0 vi:1 tag:CHANSEL_EMBED_SOF channel:0x23 frame:54 vi_tstamp:926920201312 data:0x0000000000000004
     kworker/5:1-49      [005] .......   904.945901: rtcpu_vinotify_event: tstamp:28966300206 cch:0 vi:1 tag:CHANSEL_NOMATCH channel:0x04 frame:54 vi_tstamp:926920230912 data:0x00000000000006e9
     kworker/5:1-49      [005] .......   904.945901: rtcpu_vinotify_event: tstamp:28966300485 cch:0 vi:1 tag:CHANSEL_PXL_SOF channel:0x23 frame:54 vi_tstamp:926921297600 data:0x0000000000000001
     kworker/5:1-49      [005] .......   904.945902: rtcpu_vinotify_event: tstamp:28966300734 cch:0 vi:1 tag:VIFALC_ACTIONLST channel:0x23 frame:54 vi_tstamp:926921299648 data:0x0000000008020036
     kworker/5:1-49      [005] .......   905.001899: rtcpu_vinotify_event: tstamp:28966601541 cch:0 vi:1 tag:CHANSEL_FAULT channel:0x23 frame:54 vi_tstamp:926921319072 data:0x0000000000000100
     kworker/5:1-49      [005] .......   905.001900: rtcpu_vinotify_event: tstamp:28966601808 cch:0 vi:1 tag:VIFALC_ACTIONLST channel:0x23 frame:54 vi_tstamp:926921328640 data:0x0000000001020036
 vi-output, imx4-3067    [001] .......   905.008662: tegra_channel_capture_frame: sof:926.987963488
 vi-output, imx4-3067    [001] .......   905.008664: tegra_channel_capture_frame: eof:926.987994144

问题原因:imx415分辨率配置成了 3840×2160

分析过程:

论坛帖子:IMX-678 on Jetson Orin Nano Devkit – Jetson & Embedded Systems / Jetson Orin Nano – NVIDIA Developer Forums

解决方法:

1)将驱动文件中的分辨率改为 3864×2192

2)设备树中的分辨率改为3864×2192

5、gstreamer抓图偏红色

英伟达抓图命令:

$ gst-launch-1.0 nvarguscamerasrc sensor-id=0 num-buffers=15 ! 'video/x-raw(memory:NVMM), width=(int)3864,height=(int)2192, framerate=15/1' ! nvvidconv flip-method=2 ! 'video/x-raw, format=(string)I420' ! jpegenc ! multifilesink location=camera0.jpg -e

抓拍是红色的

问题原因:修改设备树 “rggb” 改为 “gbrg”,正常出图,修改方法如下图

最终正常抓拍结果:

四、曝光、增益、格式、焦距对拍照效果的影响


帧率:30fps

正常拍照:exposure(曝光):33210 us、gain(增益):8.1dB

1、曝光时间设很低:exposure(曝光):300 us、gain(增益):8.1dB

2、曝光很低、自动增益:exposure(曝光):300 us、gain(自动增益):34.2dB

可以看到很多噪点

3、格式不对:”gbrg” 改为 “rggb”格式,图像变红

4、调整焦距后


© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容