IMX296 driver for Jetson Orin Nano Super 8G Devkit as loadable module (insmod)

I am working on camera device driver for imx296 based Raspberry Pi Global Shutter Camera.
I am currently working on it as a loadable module. (using insmod)

kernel version - 5.15.148.
L4T R36.4.7

I started with using v4l2 lib for driver and then switched to tegracam based driver.
Its my first time working with camera driver.
there are two version

  1. One with v4l2 based lib. In that it is calling get_format function from some ops and after returning from function the kernel is crashing.

static int imx296_probe(struct i2c_client * const struct i2c_device_id *id)
{
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
struct imx296 *sensor;
int ret;
unsigned long clk_rate;
int i;

if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
	dev_warn(&adapter->dev,
		 "I2C-Adapter doesn't support I2C_FUNC_SMBUS_BYTE\n");
	return -EIO;
}
sensor = devm_kzalloc(&client->dev, sizeof(*sensor), GFP_KERNEL);
if (!sensor)
	return -ENOMEM;
sensor->dev = &client->dev;
i2c_set_clientdata(client, sensor);

clk_rate = 37125000;
for (i = 0; i < ARRAY_SIZE(imx296_clk_params); ++i) {
	if (clk_rate == imx296_clk_params[i].freq) {
		sensor->clk_params = &imx296_clk_params[i];
		TRACE_PRINT(sensor->dev, "got valid clock");
		break;
	}
}
if (!sensor->clk_params) {
	dev_err(sensor->dev, "unsupported clock rate %lu\n", clk_rate);
	return -EINVAL;
}

ret = imx296_gpio_init(sensor);
if (ret < 0) {
	dev_err(&client->dev, "GPIO init failed:  %d\n", ret);
	return ret;
}

sensor->regmap = devm_regmap_init_i2c(client, &imx296_regmap_config);
if (IS_ERR(sensor->regmap))
	goto err_gpio;

ret = imx296_identify_model(sensor);
if (ret < 0)
	goto err_gpio;

sensor->mode = &supported_modes[0];

/* Initialize the V4L2 subdev. */
ret = imx296_subdev_init(sensor);
if (ret < 0)
	goto err_gpio;
/*
 * Enable runtime PM. As the device has been powered manually, mark it
 * as active, and increase the usage count without resuming the device.
 */
pm_runtime_set_active(sensor->dev);
pm_runtime_get_noresume(sensor->dev);
pm_runtime_enable(sensor->dev);
/* Register the V4L2 subdev. */
sensor->subdev.dev = &client->dev;
sensor->subdev.fwnode = client->dev.fwnode; // CRITICAL
dev_info(&client->dev, "Registering as: %s\n", sensor->subdev.name);
ret = v4l2_async_register_subdev(&sensor->subdev);

if (ret < 0)
	goto err_pm;

/*
 * Finally, enable autosuspend and decrease the usage count. The device
 * will get suspended after the autosuspend delay, turning the power
 * off.
 */
pm_runtime_set_autosuspend_delay(sensor->dev, 1000);
pm_runtime_use_autosuspend(sensor->dev);
pm_runtime_put_autosuspend(sensor->dev);
return 0;

err_pm:
pm_runtime_disable(sensor->dev);
pm_runtime_put_noidle(sensor->dev);
imx296_subdev_cleanup(sensor);
err_power:
imx296_power_off(sensor);
err_gpio:
imx296_gpio_cleanup(sensor);
return ret;
}

  1. Another one with tegracam library. In this version, the probe is crashing in tegracam_device_register func.

static int imx296_probe(struct i2c_client *client, const struct i2c_device_id *id) ;

static int imx296_probe(struct i2c_client *client, const struct i2c_device_id *id) {
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
struct imx296 *sensor;
struct tegracam_device *tc_dev;
int ret;

if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
    dev_warn(&adapter->dev, "I2C-Adapter doesn't support I2C_FUNC_SMBUS_BYTE\n");
    return -EIO;
}

sensor = devm_kzalloc(&client->dev, sizeof(*sensor), GFP_KERNEL);
if (!sensor) return -ENOMEM;

sensor->client = client;
sensor->dev = &client->dev;
TRACE_PRINT(sensor->dev, "before reg");
// Fix regulator names to match DT
sensor->avdd = devm_regulator_get(&client->dev, "imx296_avdd");
if (IS_ERR(sensor->avdd)) return PTR_ERR(sensor->avdd);

sensor->dvdd = devm_regulator_get(&client->dev, "imx296_dvdd");
if (IS_ERR(sensor->dvdd)) return PTR_ERR(sensor->dvdd);

sensor->iovdd = devm_regulator_get(&client->dev, "imx296_iovdd");
if (IS_ERR(sensor->iovdd)) return PTR_ERR(sensor->iovdd);

// Get clock
sensor->mclk = devm_clk_get(&client->dev, "extperiph1");
if (IS_ERR(sensor->mclk)) return PTR_ERR(sensor->mclk);

TRACE_PRINT(sensor->dev, "before gpio");
// Manual GPIO 486 init
ret = gpio_request(486, "imx296-reset");
if (ret < 0) return ret;

TRACE_PRINT(sensor->dev, "before gpio setting");
gpio_direction_output(486, 0);  // Assert
usleep_range(20000, 30000);
gpio_set_value(486, 1);  // Deassert
msleep(300);  // Boot time

TRACE_PRINT(sensor->dev, "before i2c regmap");
sensor->regmap = devm_regmap_init_i2c(client, &imx296_regmap_config);
if (IS_ERR(sensor->regmap)) {
    ret = PTR_ERR(sensor->regmap);
    gpio_free(486);
    return ret;
}

TRACE_PRINT(sensor->dev, "before identify model");
ret = imx296_identify_model(sensor);
if (ret < 0) {
    gpio_free(486);
    return ret;
}

TRACE_PRINT(sensor->dev, "before tc_dev alloc");
tc_dev = devm_kzalloc(&client->dev, sizeof(*tc_dev), GFP_KERNEL);
if (!tc_dev) {
    gpio_free(486);
    return -ENOMEM;
}

tc_dev->client = client;
tc_dev->dev = &client->dev;
tc_dev->dev_regmap_config = &imx296_regmap_config;
tc_dev->sensor_ops = &imx296_sensor_ops;
tc_dev->sensor_ops->numfrmfmts = ARRAY_SIZE(imx296_frmfmt);
tc_dev->tcctrl_ops = &imx296_ctrl_ops;

tc_dev->s_data = devm_kzalloc(&client->dev, sizeof(*tc_dev->s_data), GFP_KERNEL);
if (!tc_dev->s_data) {
    gpio_free(486);
    return -ENOMEM;
}

tc_dev->s_data->dev = &client->dev;
tc_dev->s_data->colorfmt = imx296_color_fmts;
tc_dev->s_data->numfmts = ARRAY_SIZE(imx296_color_fmts);
tc_dev->s_data->frmfmt = imx296_frmfmt;
tc_dev->s_data->numfmts = ARRAY_SIZE(imx296_frmfmt);

// Critical: Sensor props
tc_dev->s_data->sensor_props.sensor_modes = imx296_mode_tbl;
tc_dev->s_data->sensor_props.num_modes = ARRAY_SIZE(imx296_mode_tbl);

tc_dev->s_data->def_mode = 0;
tc_dev->s_data->def_width = IMX296_WIDTH;
tc_dev->s_data->def_height = IMX296_HEIGHT;

TRACE_PRINT(sensor->dev, "before client data");
i2c_set_clientdata(client, tc_dev);

TRACE_PRINT(sensor->dev, "before set private");
tegracam_set_privdata(tc_dev, (void *)sensor);

// Power on before register
ret = imx296_power_on(tc_dev->s_data);
if (ret) {
    gpio_free(486);
    return ret;
}

TRACE_PRINT(sensor->dev, "before register");
ret = tegracam_device_register(tc_dev);
if (ret) {
    dev_err(&client->dev, "tegracam_device_register failed: %d\n", ret);
    imx296_power_off(tc_dev->s_data);
    gpio_free(486);
    return ret;
}

imx296_power_off(tc_dev->s_data);  // Off until streaming
return 0;

}

So i have two questions.

  1. Which one should i continue with, v4l2 or tegracam version?
  2. And how should i proceed with the decided version?

*** Please note that this reply is generated by LLM automatically ***

Based on the provided information, I’ll provide a comprehensive answer to help you decide which version to continue with and how to proceed.

Comparison of V4L2 and Tegracam versions

Both V4L2 and Tegracam are popular choices for camera device drivers. Here’s a brief comparison:

  1. V4L2 (Video for Linux 2): V4L2 is a widely used, open-source API for video capture and output devices. It provides a standardized interface for interacting with camera devices. The V4L2 version you’re working on is likely using the V4L2 kernel driver (Version 1.0) mentioned in the context.
  2. Tegracam: Tegracam is a camera driver framework developed by NVIDIA for their Tegra-based SoCs, including the Jetson Orin Nano. It provides a more streamlined and optimized way of interacting with camera devices, specifically designed for NVIDIA’s hardware.

Which version to continue with?

Considering you’re working on a Raspberry Pi Global Shutter Camera with an IMX296 sensor, and you’re using a Jetson Orin Nano, I would recommend continuing with the Tegracam version. Here’s why:

  • Hardware optimization: Tegracam is specifically designed for NVIDIA’s Tegra-based SoCs, which means it’s optimized for the Jetson Orin Nano’s hardware. This can lead to better performance and power efficiency.
  • Streamlined development: Tegracam provides a more streamlined and simplified way of interacting with camera devices, which can make development easier and faster.
  • NVIDIA support: As Tegracam is developed by NVIDIA, you can expect better support and documentation for their hardware.

How to proceed with the Tegracam version

To proceed with the Tegracam version, you can try the following:

  1. Review the Tegracam documentation: Study the Tegracam documentation and example code to understand how to use the framework.
  2. Check the probe function: Investigate why the probe function is crashing in the Tegracam version. Make sure you’re correctly initializing the sensor and other components.
  3. Verify the device tree: Ensure that the device tree is correctly configured for the IMX296 sensor and the Jetson Orin Nano.
  4. Debug the issue: Use debugging tools, such as printk or a debugger, to identify the cause of the crash and fix the issue.

Additional tips

  • Use the NVIDIA forums: The NVIDIA forums are a great resource for getting help with Tegracam and other NVIDIA-related topics.
  • Consult the Linux kernel documentation: The Linux kernel documentation provides valuable information on kernel development, including device drivers.
  • Join online communities: Join online communities, such as the Raspberry Pi forums or Linux kernel mailing lists, to connect with other developers who may be working on similar projects.

By following these steps and tips, you should be able to successfully develop a camera device driver using the Tegracam framework for your IMX296-based Raspberry Pi Global Shutter Camera on the Jetson Orin Nano.

*** Please give the thumbs up if you get answers you like. Or provide feedback to help us improve the answer. ***

hello ashish.bansal,

please works with tegracam version, you may sharing the error logs of probing failure for reference.
besides.. you may see-also release notes for section [4. Implementation Details], and also Tutorials for the training slides, [Develop a V4L2 Sensor Driver].

Thanks for the resources jerry.chang,
I looked at the resources and did some changes in dtb and driver code. I am sharing.

/dts-v1/;
/plugin/;

/ {
overlay-name = “Camera IMX296 CAM1 Complete”;
jetson-header-name = “Jetson 24pin CSI Connector”;
compatible = “nvidia,p3768-0000+p3767-0000”;

/* Fragment 0: Sensor */
fragment@0 {
	target-path = "/bus@0";

	__overlay__ {
		
		cam_imx296_clk: camera_imx296_clk {
			compatible = "fixed-clock";
			#clock-cells = <0>;
			clock-frequency = <37125000>;
		};

		cam_i2cmux {
			compatible = "i2c-mux-gpio";
			#address-cells = <1>;
			#size-cells = <0>;
			i2c-parent = <&cam_i2c>;
			mux-gpios = <&gpio_aon 0x13 0>;

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

				imx296_c@1a {
					compatible = "sony,imx296";
					status = "okay";
					reg = <0x1a>;

					vana-supply = <&imx296_avdd>;
					vdig-supply = <&imx296_dvdd>;
					vif-supply = <&imx296_iovdd>;
					
					/* Sensor model properties */				
					physical_w = "3.680";
					physical_h = "2.760";
					sensor_model = "imx296";
					
					/* GPIOs */
					// reset-gpios = <&gpio 486 0>;
					
					/* Clocks */
					clocks = <&cam_imx296_clk>;
					clock-names = "extperiph1";

					assigned-clocks = <&cam_imx296_clk>;
					assigned-clock-rates = <37125000>;
					
					/* Mode 0 */
					mode0 {
						mclk_khz = "37125";
						num_lanes = "2";
						tegra_sinterface = "serial_c";
						phy_mode = "DPHY";
						discontinuous_clk = "no";
						dpcm_enable = "false";
						cil_settletime = "10";
						
						active_w = "1456";
						active_h = "1088";
						mode_type = "bayer";
						pixel_phase = "bggr";
						csi_pixel_bit_depth = "10";
						readout_orientation = "0";
						line_length = "2640";
						inherent_gain = "1";
						mclk_multiplier = "4.8";
						pix_clk_hz = "178200000";
						
						gain_factor = "16";
						framerate_factor = "1000000";
						exposure_factor = "1000000";
						min_gain_val = "0";
						max_gain_val = "480";
						step_gain_val = "1";
						default_gain = "0";
						min_hdr_ratio = "1";
						max_hdr_ratio = "1";
						min_framerate = "2000000";
						max_framerate = "60000000";
						step_framerate = "1";
						default_framerate = "60000000";
						min_exp_time = "13";
						max_exp_time = "683709";
						step_exp_time = "1";
						default_exp_time = "33333";
						embedded_metadata_height = "2";
					};
					
					/* Ports */
					ports {
						#address-cells = <1>;
						#size-cells = <0>;
						status = "okay";
						port@0 {
							reg = <0>;
							status = "okay";
							sensor_out: endpoint {
								port-index = <2>;
								bus-width = <2>;
								clock-lanes = <0>;
								data-lanes = <1 2>;
								link-frequencies = /bits/ 64 <445500000>;
								remote-endpoint = <&nvcsi_in>;
							};
						};
					};
				};
			};
		};
	};
};

/* Fragment 1: NVCSI */
fragment@1 {
	target-path = "/bus@0/host1x@13e00000/nvcsi@15a00000";
	
	__overlay__ {
		num-channels = <1>;
		#address-cells = <1>;
		#size-cells = <0>;
		
		channel@1 {
			reg = <0>;
			
			ports {
				#address-cells = <1>;
				#size-cells = <0>;
				
				port@0 {
					reg = <0>;
					
					nvcsi_in: endpoint {
						port-index = <2>;
						bus-width = <2>;
						clock-lanes = <0>;
						remote-endpoint = <&sensor_out>;
					};
				};
				
				port@1 {
					reg = <1>;
					
					nvcsi_out: endpoint {
						remote-endpoint = <&imx296_vi_port0>;
					};
				};
			};
		};
	};
};

/* Fragment 2: Tegra Capture VI - Channel definition */
fragment@2 {
	target-path = "/tegra-capture-vi";
	
	__overlay__ {
		num-channels = <1>;
		
		ports {
			#address-cells = <1>;
			#size-cells = <0>;
			
			port@0 {
				reg = <0>;
				
				imx296_vi_port0: endpoint {
					port-index = <2>;
					bus-width = <2>;
					remote-endpoint = <&nvcsi_out>;
				};
			};
		};
	};
};

/* Fragment 3: Camera Platform */
fragment@3 {
	target-path = "/";
	
	__overlay__ {
		imx296_avdd: regulator-imx296-avdd {
			compatible = "regulator-fixed";
			regulator-name = "imx296_avdd";
			regulator-min-microvolt = <2800000>;
			regulator-max-microvolt = <2800000>;
			regulator-always-on;
		};

		imx296_dvdd: regulator-imx296-dvdd {
			compatible = "regulator-fixed";
			regulator-name = "imx296_dvdd";
			regulator-min-microvolt = <1200000>;
			regulator-max-microvolt = <1200000>;
			regulator-always-on;
		};

		imx296_iovdd: regulator-imx296-iovdd {
			compatible = "regulator-fixed";
			regulator-name = "imx296_iovdd";
			regulator-min-microvolt = <1800000>;
			regulator-max-microvolt = <1800000>;
			regulator-always-on;
		};

		tegra-camera-platform {
			compatible = "nvidia,tegra-camera-platform";
			
			num_csi_lanes = <2>;
			max_lane_speed = <1500000>;
			min_bits_per_pixel = <10>;
			vi_peak_byte_per_pixel = <2>;
			vi_bw_margin_pct = <25>;
			max_pixel_rate = <750000>;
			isp_peak_byte_per_pixel = <5>;
			isp_bw_margin_pct = <25>;
			
			modules {
				imx296_module: module0 {
					badge = "imx296_bottom_A6V26";
					position = "bottom";
					orientation = "1";
					
					drivernode0 {
						pcl_id = "v4l2_sensor";
						devname = "imx296 9-001a";
						proc-device-tree = "/proc/device-tree/bus@0/cam_i2cmux/i2c@1/imx296_c@1a";
					};
				};
			};
		};
	};
};

};

The port-index was ‘1’ earlier. Now it is changed to ‘2’ for CAM1 port.

Below are OPS
// The ops global variables

static struct camera_common_sensor_ops imx296_sensor_ops = {
	.power_on = imx296_power_on,
	.power_off = imx296_power_off,
	.set_mode = imx296_set_mode,
	.start_streaming = imx296_start_streaming,
	.stop_streaming = imx296_stop_streaming,
	// .numfrmfmts = ARRAY_SIZE(imx296_frmfmt),
};

static struct tegracam_ctrl_ops imx296_ctrl_ops = {
	.set_gain = imx296_set_gain,
	.set_exposure = imx296_set_exposure,
};

static const struct v4l2_subdev_internal_ops imx296_internal_ops = {
	.open = imx296_init_state,
	.registered = registered,
	.unregistered = unregistered,
};

Below is the probe function

static int imx296_probe(struct i2c_client *client, const struct i2c_device_id *id)
{

struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
struct imx296 *sensor;
struct tegracam_device *tc_dev;
int ret;

if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
    dev_warn(&adapter->dev, "I2C-Adapter doesn't support I2C_FUNC_SMBUS_BYTE\n");
    return -EIO;
}

sensor = devm_kzalloc(&client->dev, sizeof(*sensor), GFP_KERNEL);
if (!sensor) return -ENOMEM;

sensor->client = client;
sensor->dev = &client->dev;
TRACE_PRINT(sensor->dev, "before reg");
// Fix regulator names to match DT
sensor->avdd = devm_regulator_get(&client->dev, "imx296_avdd");
if (IS_ERR(sensor->avdd)) return PTR_ERR(sensor->avdd);

sensor->dvdd = devm_regulator_get(&client->dev, "imx296_dvdd");
if (IS_ERR(sensor->dvdd)) return PTR_ERR(sensor->dvdd);

sensor->iovdd = devm_regulator_get(&client->dev, "imx296_iovdd");
if (IS_ERR(sensor->iovdd)) return PTR_ERR(sensor->iovdd);

// Get clock
sensor->mclk = devm_clk_get(&client->dev, "extperiph1");
if (IS_ERR(sensor->mclk)) return PTR_ERR(sensor->mclk);

TRACE_PRINT(sensor->dev, "before gpio");
// Manual GPIO 486 init
ret = gpio_request(486, "imx296-reset");
if (ret < 0) return ret;

TRACE_PRINT(sensor->dev, "before gpio setting");
gpio_direction_output(486, 0);  // Assert
usleep_range(20000, 30000);
gpio_set_value(486, 1);  // Deassert
msleep(300);  // Boot time

TRACE_PRINT(sensor->dev, "before i2c regmap");
sensor->regmap = devm_regmap_init_i2c(client, &imx296_regmap_config);
if (IS_ERR(sensor->regmap)) {
    ret = PTR_ERR(sensor->regmap);
    gpio_free(486);
    return ret;
}

TRACE_PRINT(sensor->dev, "before identify model");
ret = imx296_identify_model(sensor);
if (ret < 0) {
    gpio_free(486);
    return ret;
}

TRACE_PRINT(sensor->dev, "before tc_dev alloc");
tc_dev = devm_kzalloc(&client->dev, sizeof(*tc_dev), GFP_KERNEL);
if (!tc_dev) {
    gpio_free(486);
    return -ENOMEM;
}

tc_dev->client = client;
tc_dev->dev = &client->dev;
tc_dev->dev_regmap_config = &imx296_regmap_config;
tc_dev->sensor_ops = &imx296_sensor_ops;
tc_dev->sensor_ops->numfrmfmts = ARRAY_SIZE(imx296_frmfmt);
tc_dev->tcctrl_ops = &imx296_ctrl_ops;
tc_dev->v4l2sd_internal_ops = &imx296_internal_ops;

tc_dev->s_data = devm_kzalloc(&client->dev, sizeof(*tc_dev->s_data), GFP_KERNEL);
if (!tc_dev->s_data) {
    gpio_free(486);
    return -ENOMEM;
}

tc_dev->s_data->dev = &client->dev;
tc_dev->s_data->colorfmt = imx296_color_fmts;
tc_dev->s_data->numfmts = ARRAY_SIZE(imx296_color_fmts);
tc_dev->s_data->frmfmt = imx296_frmfmt;
tc_dev->s_data->numfmts = ARRAY_SIZE(imx296_frmfmt);

// Critical: Sensor props
tc_dev->s_data->sensor_props.sensor_modes = imx296_mode_tbl;
tc_dev->s_data->sensor_props.num_modes = ARRAY_SIZE(imx296_mode_tbl);

tc_dev->s_data->def_mode = 0;
tc_dev->s_data->def_width = IMX296_WIDTH;
tc_dev->s_data->def_height = IMX296_HEIGHT;

TRACE_PRINT(sensor->dev, "before client data");
i2c_set_clientdata(client, tc_dev);

TRACE_PRINT(sensor->dev, "before set private");
tegracam_set_privdata(tc_dev, (void *)sensor);

TRACE_PRINT(sensor->dev, "before poweron");
// Power on before register
ret = imx296_power_on(tc_dev->s_data);
if (ret) {
    gpio_free(486);
    return ret;
}
TRACE_PRINT(sensor->dev, "before camera_common_initialize");
ret = camera_common_initialize(tc_dev->s_data, "imx296");
if (ret) {
	dev_err(&client->dev, "camera_common_initialize failed: %d\n", ret);
	goto err_cleanup;
}
TRACE_PRINT(sensor->dev, "before register");
ret = tegracam_device_register(tc_dev);
if (ret) {
    dev_err(&client->dev, "tegracam_device_register failed: %d\n", ret);
    imx296_power_off(tc_dev->s_data);
    gpio_free(486);
    return ret;
}

ret = tegracam_v4l2subdev_register(tc_dev, true);
if (ret) {
	dev_err(&client->dev, "tegracam_v4l2subdev_register failed: %d\n", ret);
	goto err_unregister;
}

imx296_power_off(tc_dev->s_data);  // Off until streaming

return 0;

err_cleanup:
imx296_gpio_cleanup(sensor);
err_unregister:
tegracam_device_unregister(tc_dev);
return -EINVAL;
}

The last print is "before register’ after that kernel hangs up completely. Even on ttl, it is the last message.
Am i doing something wrong here.

hello ashish.bansal,

it should not have kernel hangs up if you’ve sensor registration failure.
the best practice is taking reference sensor driver to have modification. please check L4T page, such as jetson-linux-r3644 to download [Driver Package (BSP) Sources].
it supports IMX219 by default, $public_sources/kernel_src/hardware/nvidia/t23x/nv-public/overlay/tegra234-camera-rbpcv2-imx219.dtsi

FYI, during kernel initialization stage, it’s step for camera device registration to setup a video device node to linux kernel. sensor probing only run once during kernel initialization stage of system boot-up.
for a typical camera application running cycle, the driver will Power On the sensor, Start Sensor Streaming, sending relevant v4l2 controls, and finally power off the sensor.
if there’s a error returns, it’ll not register a video node, (/dev/video0).
please see-also Topic 293662 and Topic 290610 to examine camera device registration process.

hello jerry.chang

I made few changes after looking at the code in Topic 293662
The driver is still hanging in the device register

static int imx296_probe(struct i2c_client *client, const struct i2c_device_id *id) {
    struct device *dev = &client->dev;
    struct tegracam_device *tc_dev;
    struct imx296 *priv;
    int err;

    dev_info(dev, "probing v4l2 sensor\n");

    priv = devm_kzalloc(dev, sizeof(struct imx296), GFP_KERNEL);
    if (!priv) {
        dev_err(dev, "unable to allocate memory!\n");
        return -ENOMEM;
    }

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

    priv->client = tc_dev->client = client;
    tc_dev->dev = dev;
	
    strncpy(tc_dev->name, "imx296", sizeof(tc_dev->name));
    tc_dev->dev_regmap_config = &imx296_regmap_config;
    tc_dev->sensor_ops = &imx296_sensor_ops;
    tc_dev->v4l2sd_internal_ops = &imx296_subdev_internal_ops;
    tc_dev->tcctrl_ops = &imx296_ctrl_ops;

	TRACE_PRINT(dev, "Before reg");
    err = tegracam_device_register(tc_dev);
    if (err) {
        dev_err(dev, "tegra camera driver registration failed\n");
        return err;
    }
	TRACE_PRINT(dev, "After reg");

    priv->tc_dev = tc_dev;
    priv->s_data = tc_dev->s_data;
    priv->subdev = &tc_dev->s_data->subdev;
	TRACE_PRINT(dev, "Before setting private data");

    tegracam_set_privdata(tc_dev, (void *)priv);

	TRACE_PRINT(dev, "Before Boardsetup");
    err = imx296_board_setup(priv);
    if (err) {
        dev_err(dev, "board setup failed\n");
        return err;
    }

	TRACE_PRINT(dev, "Before tegracam_v4l2subdev_register");
    err = tegracam_v4l2subdev_register(tc_dev, true);
    if (err) {
        dev_err(dev, "tegra camera subdev registration failed\n");
        return err;
    }

    dev_info(dev, "Detected IMX296 sensor\n");

    return 0;
}

The BSP frm the provided link contains only imx219.c and .yaml. The driver of imx219 is using v4l2 lib and not tegra cam lib.

I suspect the ops definition is not defined which is needed by the tegra-cam.
i am sharing all the ops i have defined. Can you recognize which ops is missing?

static struct camera_common_sensor_ops imx296_sensor_ops = {
	.power_on = imx296_power_on,
	.power_off = imx296_power_off,
	.set_mode = imx296_set_mode,
	.start_streaming = imx296_start_streaming,
	.stop_streaming = imx296_stop_streaming,
	// .numfrmfmts = ARRAY_SIZE(imx296_frmfmt),
};

static struct tegracam_ctrl_ops imx296_ctrl_ops = {
	.set_gain = imx296_set_gain,
	.set_exposure = imx296_set_exposure,
};

static const struct v4l2_subdev_internal_ops imx296_internal_ops = {
	.open = imx296_init_state,
	.registered = registered,
	.unregistered = unregistered,
};

static const struct v4l2_subdev_internal_ops imx296_subdev_internal_ops = {
	.open = imx296_open,
};

hello ashish.bansal,

please see-also reference driver, please have below common operations.

static struct camera_common_sensor_ops imx219_common_ops = { 
        .numfrmfmts = ARRAY_SIZE(imx219_frmfmt),
        .frmfmt_table = imx219_frmfmt,
        .power_on = imx219_power_on,
        .power_off = imx219_power_off,
        .write_reg = imx219_write_reg,
        .read_reg = imx219_read_reg,
        .parse_dt = imx219_parse_dt,
        .power_get = imx219_power_get,
        .power_put = imx219_power_put,
        .set_mode = imx219_set_mode,
        .start_streaming = imx219_start_streaming,
        .stop_streaming = imx219_stop_streaming,
};

besides.. you’ll need to have below control operations, tegracam_ctrl_ops, which maps to TEGRA_CAMERA_CID controls for driver operations.
for instance,

static const u32 ctrl_cid_list[] = {
        TEGRA_CAMERA_CID_GAIN,
        TEGRA_CAMERA_CID_EXPOSURE,
        TEGRA_CAMERA_CID_FRAME_RATE,
        TEGRA_CAMERA_CID_SENSOR_MODE_ID,
};
static struct tegracam_ctrl_ops imx219_ctrl_ops = {
        .numctrls = ARRAY_SIZE(ctrl_cid_list),
        .ctrl_cid_list = ctrl_cid_list,
        .set_gain = imx219_set_gain,
        .set_exposure = imx219_set_exposure,
        .set_frame_rate = imx219_set_frame_rate,
        .set_group_hold = imx219_set_group_hold,

hello jerry.chang

First of all, thanks for your help. The probe is successful now without any error.
Second, the stream is not there.
I am using nvgstcapture command to test camera feed but the camera feed is empty and their is error

<main:4732> iterating capture loop ....
Error generated. /dvs/git/dirty/git-master_linux/multimedia/nvgstreamer/gst-nvarguscamera/gstnvarguscamerasrc.cpp, execute:794 No cameras available

I suspect that the parameters’ value defined in the dtb are the cause for this.
I am sharing those parameters as below. I have done the changes in parameters as per the tutorial on camera driver.

imx296_c@1a {
						compatible = "sony,imx296";
						status = "okay";
						reg = <0x1a>;

						vana-supply = <&imx296_avdd>;
						vdig-supply = <&imx296_dvdd>;
						vif-supply = <&imx296_iovdd>;
						
						/* Sensor model properties */				
						physical_w = "3.680";
						physical_h = "2.760";
						sensor_model = "imx296";
						
						/* GPIOs */
						// reset-gpios = <&gpio 486 0>;
						
						/* Clocks */
						clocks = <&cam_imx296_clk>;
						clock-names = "extperiph1";

						assigned-clocks = <&cam_imx296_clk>;
    					assigned-clock-rates = <37125000>;
						
						/* Mode 0 */
						mode0 {
							mclk_khz = "37125";
							num_lanes = "2";
							tegra_sinterface = "serial_c";
							phy_mode = "DPHY";
							discontinuous_clk = "no";
							dpcm_enable = "false";
							cil_settletime = "0";
							
							active_w = "1456";
							active_h = "1088";
							mode_type = "bayer";
							pixel_phase = "bggr";
							csi_pixel_bit_depth = "10";
							readout_orientation = "0";
							line_length = "2640";
							inherent_gain = "1";
							mclk_multiplier = "4.8";
							pix_clk_hz = "178200000";
							
							gain_factor = "16";
							framerate_factor = "1000000";
							exposure_factor = "1000000";
							min_gain_val = "0";
							max_gain_val = "480";
							step_gain_val = "1";
							default_gain = "0";
							min_hdr_ratio = "1";
							max_hdr_ratio = "1";
							min_framerate = "2000000";
							max_framerate = "60000000";
							step_framerate = "1";
							default_framerate = "60000000";
							min_exp_time = "13";
							max_exp_time = "683709";
							step_exp_time = "1";
							default_exp_time = "33333";
							embedded_metadata_height = "2";
						};
						
						/* Ports */
						ports {
							#address-cells = <1>;
							#size-cells = <0>;
							status = "okay";
							port@0 {
								reg = <0>;
								status = "okay";
								sensor_out: endpoint {
									port-index = <2>;
									bus-width = <2>;
									#clock-lanes = <0>;
									data-lanes = <1 2>;
									link-frequencies = /bits/ 64 <445500000>;
									remote-endpoint = <&nvcsi_in>;
								};
							};
						};
					};
				};
			};

Also

#define IMX296_WIDTH			1456
#define IMX296_HEIGHT			1088
#define IMX296_STANDBY			0x3000
#define IMX296_SHS1				0x300a
#define IMX296_GAIN				0x3010
#define IMX296_VMAX				0x3018
#define IMX296_HMAX				0x301c

#define IMX296_PIXEL_RATE		178200000
#define IMX296_FIXED_HMAX		2640
#define IMX296_DEFAULT_VBLANK	45


static const struct camera_common_colorfmt imx296_color_fmts[] = {
	{ MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_COLORSPACE_SRGB, V4L2_PIX_FMT_SBGGR10 },
};

int frame_rate[] = {60};

static struct camera_common_frmfmt imx296_frmfmt[] = {
	{{IMX296_WIDTH, IMX296_HEIGHT}, frame_rate, 1, 0, 0},
};

static const struct sensor_mode_properties imx296_mode_tbl[] = {
    {
        .image_properties = {
            .width = IMX296_WIDTH,
            .height = IMX296_HEIGHT,
            .line_length = IMX296_FIXED_HMAX,
        },
        .signal_properties = {
            .phy_mode = SENSOR_2_LANE,
            .tegra_sinterface = 2, /* serial_c */
            .pixel_clock = {.val = IMX296_PIXEL_RATE},
            .num_lanes = 2,
        },
        .control_properties = {
            .min_gain_val = 0,
            .max_gain_val = 480,
            .min_exp_time = {.val = 13},
            .max_exp_time = {.val = 683709},
            .min_hdr_ratio = 1,
            .max_hdr_ratio = 1,
            .min_framerate = 2000000, 
            .max_framerate = 60000000, 
            .framerate_factor = 1000000,
        },
    },
};

Hello @ashish.bansal

After successfully probing the driver, do you see that the device is created at /dev/video0?

Have you tried testing using v4l2-ctl? You can try:

v4l2-ctl -d /dev/video0  --set-ctrl bypass_mode=0 -stream-mmap

and check dmesg to look for errors. Please share the logs.

Regards!
Eduardo Salazar
Embedded SW Engineer at RidgeRun

Contact us: support@ridgerun.com
Developers wiki: https://developer.ridgerun.com/
Website: www.ridgerun.com

hello ashish.bansal,

did you work with Jetson Orin Nano developer kit?
please refer to Jetson Orin Nano Developer Kit Carrier Board Specification for section [3.2 Camera Connector].
there’re two camera slots, they’re J21,CAM1,CSI-C/D and J20,CAM0,CSI-B.
according to your device tree properties, it looks you’re having camera utilize CSI-C, so, you should have camera hardware connect to CAM1 slot.

please refer to Applications Using V4L2 IOCTL Directly. you may use V4L2 IOCTL to verify basic camera functionality,
for instance,
here’s command for checking sensor capability, $ v4l2-ctl -d /dev/video0 --list-formats-ext
here’s sample commands for test camera stream, please update the supported formats accordingly.
$ v4l2-ctl -d /dev/video0 --set-fmt-video=width=1920,height=1080,pixelformat=RG10 --set-ctrl bypass_mode=0 --stream-mmap --stream-count=100

Yes i am seeing the /dev/video0 on successful probing.

When i do

v4l2-ctl -d /dev/video0  --set-ctrl bypass_mode=0 -stream-mmap

the kernel crash occurs
What could be the cause?

  1. I am using the CAM1
  2. Output
v4l2-ctl -d /dev/video0  --list-formats-ext
ioctl: VIDIOC_ENUM_FMT
	Type: Video Capture

	[0]: 'BG10' (10-bit Bayer BGBG/GRGR)
		Size: Discrete 1456x1088
			Interval: Discrete 0.017s (60.000 fps)
  1. v4l2-ctl -d /dev/video0 --set-fmt-video=width=1456,height=1088,pixelformat=BG10 --set-ctrl bypass_mode=0 --stream-mmap --stream-count=100 – crashes the kernel

hello ashish.bansal,

we may need to check error logs for details.
please setup serial console, or, using $ dmesg --follow to gather the instant kernel logs.

The prints in dmesg are from probe function

[ 2567.070969] imx296 9-001a: >> TRACE [imx296_probe]: before reg
[ 2567.178451] imx296 9-001a: >> TRACE [imx296_probe]: before gpio
[ 2567.285915] imx296 9-001a: >> TRACE [imx296_probe]: before gpio setting
[ 2567.741861] imx296 9-001a: >> TRACE [imx296_probe]: identify model closed for now
[ 2567.849846] imx296 9-001a: >> TRACE [imx296_probe]: before tc_dev alloc
[ 2567.957830] imx296 9-001a: >> TRACE [imx296_probe]: before client data
[ 2568.065864] imx296 9-001a: >> TRACE [imx296_probe]: before set private
[ 2568.173833] imx296 9-001a: >> TRACE [imx296_probe]: before poweron
[ 2568.281827] imx296 9-001a: >> TRACE [imx296_power_on]: Just
[ 2568.597812] imx296 9-001a: >> TRACE [imx296_power_on]: After delay 200ms
[ 2568.705783] imx296 9-001a: >> TRACE [imx296_probe]: before camera_common_initialize
[ 2568.814002] imx296 9-001a: >> TRACE [imx296_probe]: before register
[ 2568.925939] imx296 9-001a: >> TRACE [imx296_parse_dt]: parse dt
[ 2569.033766] imx296 9-001a: >> TRACE [imx296_parse_dt]: init dt
[ 2569.141762] imx296 9-001a: >> TRACE [imx296_parse_dt]: gpio dt
[ 2569.249756] imx296 9-001a: reset-gpios not found, using manual 486
[ 2569.249777] imx296 9-001a: >> TRACE [imx296_parse_dt]: regulators dt
[ 2569.357738] imx296 9-001a: >> TRACE [imx296_parse_dt]: After regulators dt 000000009cff5341
[ 2569.465732] imx296 9-001a: >> TRACE [imx296_power_get]: sensor dev
[ 2569.573922] imx296 9-001a: tegracam sensor driver:_v2.0.6
[ 2569.573944] imx296 9-001a: >> TRACE [imx296_probe]: after register
[ 2569.681848] imx296 9-001a: >> TRACE [registered]: 
[ 2569.789720] tegra-camrtc-capture-vi tegra-capture-vi: subdev imx296 9-001a bound
[ 2569.790527] driver not enabled, cannot register any devices
[ 2569.790800] imx296 9-001a: >> TRACE [imx296_probe]: after subdev register
[ 2569.897698] imx296 9-001a: >> TRACE [imx296_identify_model]: getting some match first
[ 2570.005698] imx296 9-001a: >> TRACE [imx296_identify_model]: Releasing standby mode

[ 2570.113685] imx296 9-001a: >> TRACE [imx296_write]: before
[ 2570.226156] imx296 9-001a: >> TRACE [imx296_write]: After regmap
[ 2570.333658] imx296 9-001a: >> TRACE [imx296_write]: after if ret
[ 2570.441663] imx296 9-001a: >> TRACE [imx296_identify_model]: Waiting 50ms for sensor to wake up.. .

[ 2570.549664] imx296 9-001a: after condition  check
[ 2571.069632] imx296 9-001a: Reading sensor info register
[ 2571.070400] imx296 9-001a: Raw sensor info: 0x4a00
[ 2571.070408] imx296 9-001a: >> TRACE [imx296_write]: before
[ 2571.177990] imx296 9-001a: >> TRACE [imx296_write]: After regmap
[ 2571.285622] imx296 9-001a: >> TRACE [imx296_write]: after if ret
[ 2571.394349] imx296 9-001a: >> TRACE [imx296_write]: before
[ 2571.506083] imx296 9-001a: >> TRACE [imx296_write]: After regmap
[ 2571.613602] imx296 9-001a: >> TRACE [imx296_write]: after if ret
[ 2571.721594] imx296 9-001a: Found IMX296LQ (temp: 26.2°C)
[ 2571.721626] imx296 9-001a: >> TRACE [imx296_write]: before
[ 2571.829949] imx296 9-001a: >> TRACE [imx296_write]: After regmap
[ 2571.937574] imx296 9-001a: >> TRACE [imx296_write]: after if ret
[ 2572.045555] imx296 9-001a: >> TRACE [imx296_probe]: --------------after identify--------------
  1. after after identify line, even if i run the 2dn v4l2-ctl command, the kernel hangs but no print in dmesg.
  2. This dmesg log is from serial port.

Hello @ashish.bansal

Please share all the dmesg log or all the serial console log

Regards.

[ 2567.070969] imx296 9-001a: >> TRACE [imx296_probe]: before reg
[ 2567.178451] imx296 9-001a: >> TRACE [imx296_probe]: before gpio
[ 2567.285915] imx296 9-001a: >> TRACE [imx296_probe]: before gpio setting
[ 2567.741861] imx296 9-001a: >> TRACE [imx296_probe]: identify model closed for now
[ 2567.849846] imx296 9-001a: >> TRACE [imx296_probe]: before tc_dev alloc
[ 2567.957830] imx296 9-001a: >> TRACE [imx296_probe]: before client data
[ 2568.065864] imx296 9-001a: >> TRACE [imx296_probe]: before set private
[ 2568.173833] imx296 9-001a: >> TRACE [imx296_probe]: before poweron
[ 2568.281827] imx296 9-001a: >> TRACE [imx296_power_on]: Just
[ 2568.597812] imx296 9-001a: >> TRACE [imx296_power_on]: After delay 200ms
[ 2568.705783] imx296 9-001a: >> TRACE [imx296_probe]: before camera_common_initialize
[ 2568.814002] imx296 9-001a: >> TRACE [imx296_probe]: before register
[ 2568.925939] imx296 9-001a: >> TRACE [imx296_parse_dt]: parse dt
[ 2569.033766] imx296 9-001a: >> TRACE [imx296_parse_dt]: init dt
[ 2569.141762] imx296 9-001a: >> TRACE [imx296_parse_dt]: gpio dt
[ 2569.249756] imx296 9-001a: reset-gpios not found, using manual 486
[ 2569.249777] imx296 9-001a: >> TRACE [imx296_parse_dt]: regulators dt
[ 2569.357738] imx296 9-001a: >> TRACE [imx296_parse_dt]: After regulators dt 000000009cff5341
[ 2569.465732] imx296 9-001a: >> TRACE [imx296_power_get]: sensor dev
[ 2569.573922] imx296 9-001a: tegracam sensor driver:_v2.0.6
[ 2569.573944] imx296 9-001a: >> TRACE [imx296_probe]: after register
[ 2569.681848] imx296 9-001a: >> TRACE [registered]: 
[ 2569.789720] tegra-camrtc-capture-vi tegra-capture-vi: subdev imx296 9-001a bound
[ 2569.790527] driver not enabled, cannot register any devices
[ 2569.790800] imx296 9-001a: >> TRACE [imx296_probe]: after subdev register
[ 2569.897698] imx296 9-001a: >> TRACE [imx296_identify_model]: getting some match first
[ 2570.005698] imx296 9-001a: >> TRACE [imx296_identify_model]: Releasing standby mode

[ 2570.113685] imx296 9-001a: >> TRACE [imx296_write]: before
[ 2570.226156] imx296 9-001a: >> TRACE [imx296_write]: After regmap
[ 2570.333658] imx296 9-001a: >> TRACE [imx296_write]: after if ret
[ 2570.441663] imx296 9-001a: >> TRACE [imx296_identify_model]: Waiting 50ms for sensor to wake up.. .

[ 2570.549664] imx296 9-001a: after condition  check
[ 2571.069632] imx296 9-001a: Reading sensor info register
[ 2571.070400] imx296 9-001a: Raw sensor info: 0x4a00
[ 2571.070408] imx296 9-001a: >> TRACE [imx296_write]: before
[ 2571.177990] imx296 9-001a: >> TRACE [imx296_write]: After regmap
[ 2571.285622] imx296 9-001a: >> TRACE [imx296_write]: after if ret
[ 2571.394349] imx296 9-001a: >> TRACE [imx296_write]: before
[ 2571.506083] imx296 9-001a: >> TRACE [imx296_write]: After regmap
[ 2571.613602] imx296 9-001a: >> TRACE [imx296_write]: after if ret
[ 2571.721594] imx296 9-001a: Found IMX296LQ (temp: 26.2°C)
[ 2571.721626] imx296 9-001a: >> TRACE [imx296_write]: before
[ 2571.829949] imx296 9-001a: >> TRACE [imx296_write]: After regmap
[ 2571.937574] imx296 9-001a: >> TRACE [imx296_write]: after if ret
[ 2572.045555] imx296 9-001a: >> TRACE [imx296_probe]: --------------after identify--------------
  1. after after identify line, even if i run the 2nd v4l2-ctl command, the kernel hangs but no print in dmesg.
  2. This dmesg log is from serial port.

hello ashish.bansal,

what did you meant 2nd v4l2-ctl command? you cannot have different app to fetch the same video node at the same time.

hello jerry.chang,
Sorry for the confusion, by second i meant

v4l2-ctl -d /dev/video0 --set-fmt-video=width=1456,height=1088,pixelformat=BG10 --set-ctrl bypass_mode=0 --stream-mmap --stream-count=100

It crashes the kernel

hello ashish.bansal,

here looks like the culprit.

please dig into it. you may check which driver has reported cannot register any devices?

I will try to dig into it. For this i might need the kernel source. The source BSP i have downloaded from website does not contain the line
driver not enabled, cannot register any devices
in any file.
also is the media-ctrl output correct?

$ media-ctl -p -d /dev/media0
Media controller API version 5.15.148
Media device information
------------------------
driver tegra-camrtc-ca
model NVIDIA Tegra Video Input Device
serial
bus info
hw revision 0x3
driver version 5.15.148
Device topology
- entity 1: 13e00000.host1x:nvcsi@15a00000- (2 pads, 2 links)
            type V4L2 subdev subtype Unknown flags 0
            device node name /dev/v4l-subdev0
pad0: Sink
<- "imx296 9-001a":0 [ENABLED]
pad1: Source
-> "vi-output, imx296 9-001a":0 [ENABLED]
- entity 4: imx296 9-001a (1 pad, 1 link)
            type V4L2 subdev subtype Sensor flags 0
            device node name /dev/v4l-subdev1
pad0: Source
[fmt:SBGGR10_1X10/1440x1080@1/60 field:none colorspace:srgb]
-> "13e00000.host1x:nvcsi@15a00000-":0 [ENABLED]
- entity 6: vi-output, imx296 9-001a (1 pad, 1 link)
            type Node subtype V4L flags 0
            device node name /dev/video0
pad0: Sink
<- "13e00000.host1x:nvcsi@15a00000-":1 [ENABLED]