Porting an R24 camera driver to R28.1

I’m trying to port a camera driver for R24 (kernel 3.x) to R28.1 (kernel 4.x), and am having trouble getting the driver to complete initialization at boot time. It looks like the camera driver itself is loading, but the I2C interface isn’t. This is for an IMX172 camera sensor hooked up through a Leopard Imaging carrier card. It was working on the same hardware using R24, but I haven’t had any luck getting it to run on the newer OS.

Here are the relevant lines getting printed to dmesg at boot time:

[    3.356309] tegra_rtc 7000e000.rtc: Tegra internal Real Time Clock
[    3.357164] i2c /dev entries driver
[    3.358904] [IMX172]: probing v4l2 sensor.
[    3.377878] imx172 30-0052: imx172_probe: name imx172_a
[    3.377920] imx172 30-0052: imx172_ctrls_init++
[    3.377971] MPJ initing 4 ctrls.
[    3.377972] imx172 30-0052: Detected IMX172 sensor
[    3.378735] pca9570 30-0024: pca9570_icr_move, forward val=5
[    3.401101] tegra-vii2c 546c0000.i2c: no acknowledge from address 0x24
[    3.412814] pca9570 30-0024: pca9570_write_reg:i2c write failed, 0x24 = 2
[    3.412819] pca9570 30-0024: pca9570_icr_move:i2c write failed
[    3.412926] pca9570: probe of 30-0024 failed with error -121
[    3.413635] IR NEC protocol handler initialized

I’m wondering if my device tree is incorrect for the PCA9570. The camera driver is for an IMX172, so I’ve been using the IMX185 driver as a guide when porting. Here are the two device tree files I created.

tegra210-camera-imx172-a00.dtsi:

#include <dt-bindings/media/camera.h>
#include <dt-bindings/platform/t210/t210.h>

/ {
	host1x {
		vi {
			num-channels = <2>;
			ports {
				#address-cells = <1>;
				#size-cells = <0>;
				port@0 {
					reg = <0>;
					liimx172_vi_in0: endpoint {
						csi-port = <0>;
						bus-width = <4>;
						remote-endpoint = <&liimx172_out0>;
					};
				};
			};
		};

		i2c@546c0000 {
            avdd_dsi_csi-supply = <&max77620_gpio7>;
            status = "okay";

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

			tca9546@70 {
				i2c@0 {
                    imx172_a@52 {
                        compatible = "nvidia,imx172";
                        reg = <0x52>;

                        //devnode = "video0";

                        /* Physical dimensions of sensor */
                        physical_w = "4.000";
                        physical_h = "3.000";

                        sensor_model ="imx172";

                        avdd-reg = "vana";
                        iovdd-reg = "vif";

                        /* Define any required hw resources needed by driver */
                        /* ie. clocks, io pins, power sources */

                        /* Defines number of frames to be dropped by driver internally after applying */
                        /* sensor crop settings. Some sensors send corrupt frames after applying */
                        /* crop co-ordinates */
                        post_crop_frame_drop = "0";

                        /* Convert Gain to unit of dB (decibel) befor passing to kernel driver */
                        use_decibel_gain = "true";

                        /* if true, delay gain setting by one frame to be in sync with exposure */
                        /* delayed_gain = "true"; */

                        /* enable CID_SENSOR_MODE_ID for sensor modes selection */
                        use_sensor_mode_id = "true";

                        mode0 {   // mode IMX172_MODE_4000X3000
                            mclk_khz = "24000";
                            num_lanes = "4";
                            tegra_sinterface = "serial_a";
                            discontinuous_clk = "no";
                            dpcm_enable = "false";
                            cil_settletime = "0xE";
                            dynamic_pixel_bit_depth = "12";
                            csi_pixel_bit_depth = "12";
                            mode_type = "bayer";
                            pixel_phase = "rggb";
                            pixel_t = "bayer_rggb";

                            active_w = "4000";
                            active_h = "3000";
                            readout_orientation = "0";
                            line_length = "6500";		// = hmax(540) * 8, one line time = line_length / pix_clk_hz (seconds)
                            inherent_gain = "1";
                            mclk_multiplier = "25";
                            pix_clk_hz = "400000000";	// imx172 : 400000000 = 50Mhz * 8

                            min_gain_val = "1.0"; /* dB */		// Defined in imx172.c
                            max_gain_val = "100"; /* dB */		// Defined in imx172.c
                            min_hdr_ratio = "1";
                            max_hdr_ratio = "64";
                            min_framerate = "2.2290";			// = 1 / max_exp_time
                            max_framerate = "10";
                            min_exp_time = "44";				// 4 line time, in micro-second
                            max_exp_time = "33000";				// max exposure time = 33ms, in micro-second
                            embedded_metadata_height = "0";
                        };
                        ports {
                            #address-cells = <1>;
                            #size-cells = <0>;
                            port@0 {
                                reg = <0>;
                                liimx172_out0: endpoint {
                                    csi-port = <0>;
                                    bus-width = <4>;
                                    remote-endpoint = <&liimx172_vi_in0>;
                                };
							};
						};
					};
				};
			};
		};
	};
};

/ {

	tegra-camera-platform {
		compatible = "nvidia, tegra-camera-platform";

		num_csi_lanes = <4>;
		max_lane_speed = <1500000>;
		min_bits_per_pixel = <10>;
		vi_peak_byte_per_pixel = <2>;
		vi_bw_margin_pct = <25>;
		isp_peak_byte_per_pixel = <5>;
		isp_bw_margin_pct = <25>;


		modules {
			module0 {
				badge = "imx172_bottomleft_A6V24";
				position = "bottomleft";
				orientation = "1";
                status = "okay";
				drivernode0 {
					/* Declare PCL support driver (classically known as guid)  */
					pcl_id = "v4l2_sensor";
					/* Driver v4l2 device name */
					devname = "imx172 7-0052";
					/* Declare the device-tree hierarchy to driver instance */
					proc-device-tree = "/proc/device-tree/host1x/i2c@546c0000/tca9546@70/i2c@0/imx172_a@52";
                    status = "okay";
				};
			};
		};
	};
};

tegra210-jetson-cv-camera-imx172-a00.dtsi:

#include "tegra210-camera-imx172-a00.dtsi"

//#define CAM0_RST_L	TEGRA_GPIO(S, 4)

#define CAM0_RST		TEGRA_GPIO(S, 4)
#define CAM0_PWDN		TEGRA_GPIO(S, 7)
#define CAM1_PWDN		TEGRA_GPIO(T, 0)
#define CAM0_SID		TEGRA_GPIO(E, 8)
#define CAM1_RST_L		TEGRA_GPIO(S, 5)

//#define CAMERA_I2C_MUX_BUS(x) (0x1E + x)

/* camera control gpio definitions */

/ {
    tegra-camera-platform {
        compatible = "nvidia, tegra-camera-platform";

        num_csi_lanes = <4>;
        max_lane_speed = <1500000>;
        min_bits_per_pixel = <10>;
        vi_peak_byte_per_pixel = <2>;
        vi_bw_margin_pct = <25>;
        isp_peak_byte_per_pixel = <5>;
        isp_bw_margin_pct = <25>;
    };

    host1x {
        i2c@546c0000 {
            tca9546@70 {
                compatible = "nxp,pca9546";
                reg = <0x70>;
                #address-cells = <1>;
                #size-cells = <0>;
                vif-supply = <&en_vdd_cam>;
                vcc_lp = "vif";
                i2c@0 {
                    reg = <0>;
                    i2c-mux,deselect-on-exit;
                    #address-cells = <1>;
                    #size-cells = <0>;
                    pca9570_a@24 {
                        compatible = "nvidia,pca9570";
                        reg = <0x24>;
                        channel = "a";
                    };
                };
            };
        };
    };
};


/ {

	host1x {
		i2c@546c0000 {
			tca9546@70 {
				/* compatible = "nxp,pca9546";
				 * reg = <0x70>;
				 * #address-cells = <1>;
				 * #size-cells = <0>;
				 * vcc-supply = <&en_vdd_cam>;
				 * force_bus_start = <CAMERA_I2C_MUX_BUS(0)>;
				 */
				vcc_lp = "vif";

				i2c@0 {
					imx172_a@52 {
						/* Define any required hw resources needed by driver */
						/* ie. clocks, io pins, power sources */
						clocks = <&tegra_car TEGRA210_CLK_CLK_OUT_3>;
						clock-names = "clk_out_3";
						clock-frequency = <24000000>;
						mclk = "clk_out_3";
						reset-gpios = <&gpio CAM0_RST GPIO_ACTIVE_HIGH>;
						pwdn-gpios = <&gpio CAM0_RST GPIO_ACTIVE_HIGH>;
                        sid-gpios = <&tca9539_77 1 GPIO_ACTIVE_HIGH>;
						vana_supply = <&en_vdd_cam_hv_2v8>;
						vif-supply = <&en_vdd_cam>;
					};
				};
			};
		};
	};

    gpio: gpio@6000d000 {
        camera-control {
            gpio-output-low = <
            CAM0_RST
            CAM0_PWDN
            >;
        };
    };


};

Does anyone have any ideas of what might not be configured correctly above?

Is there a guide for the upgrade process in the device tree between the two kernel versions? After this I’m going to be working on porting the driver to the TX2, so I’m guessing the device tree is going to need to change there as well.

Thanks.

I forgot to mention, as shown above the driver appears to detect the camera sensor fine, but /dev/video* isn’t created.

Would love to hear some suggestions of the best way to debug this too. I feel like I’m working with a black box right now.

I have my own camera driver that I was attempting to port to 28.1 before getting distracted with higher priority projects.

I managed to get everything to compile and probe, but ran into some issues with a null dereference deep in the Nvidia stack if I tried to use it. That said, the changes to the device tree and the driver code itself may be helpful to you.

This commit got the driver to compile successfully in the r28.1 kernel:

And this commit was a device tree change to the clocks to get it to probe on r28.1:

By the way, one of the more annoying changes in r28.1 is that the dtb can’t be just copied over to the boot directory anymore. It has to be re-flashed.

Command is here:

Thanks for linking your diff for the driver and device tree. I’m going to spend some time going through this and seeing if I can make similar changes to get my driver to load.

I saw something about the dtb needing to be flashed and not just copied over to /boot anymore, but isn’t that specific to the TX2? I was under the impression that on the TX1 you can still copy to /boot and it will work. When I copy to /boot, it appears to load my modified device tree at least. Maybe that’s causing my problem though. I might have to give it a shot.

Pretty much everything changed with the new kernel including how u-boot is set up and which partition it’s on. I expect they had to do that to get the TX2 and TX1 on the same page.

Because of the uboot change you have to flash the dtb. Took me awhile to figure out why my changes weren’t going through.

Any luck with this?

I got my driver (GitHub - Daxbot/daxc02: Nvidia Jetson TX1/TX2 Kernel Driver for Leopard Imaging LI-M021C-MIPI) to probe on my TX2 as well as the TX1, but when I try to take an image it doesn’t even call s_stream. Just sits there for a bit, and then times out:

[  141.166802] daxc02 1-0010: daxc02_power_on
[  141.410579] daxc02 1-0010: daxc02_power_off
[  141.420368] daxc02 1-0010: daxc02_power_on
[  141.662630] daxc02 1-0010: daxc02_power_off
[  141.699909] daxc02 1-0010: daxc02_power_on
[  141.942308] daxc02 1-0010: daxc02_power_off
[  141.948696] daxc02 1-0010: daxc02_power_on
[  142.190457] daxc02 1-0010: daxc02_power_off
[  142.196624] daxc02 1-0010: daxc02_power_on
[  142.438360] daxc02 1-0010: daxc02_g_input_status
[  142.443084] daxc02 1-0010: daxc02_power_off
[  142.479948] daxc02 1-0010: daxc02_power_on
[  142.722356] daxc02 1-0010: daxc02_power_off
[  142.804515] daxc02 1-0010: daxc02_power_on
[  143.046321] daxc02 1-0010: could not find device ctrl.
[  144.590138] fence timeout on [ffffffc1ac1bf400] after 1500ms

It looks like there are a few other posts about porting drivers, so I’m going to look into it more next week.

Unfortunately I wasn’t able to get it working and had to move on to another project. From your logs, it looks like it’s somehow not sending messages correctly on the I2C bus.