Sequence of actions for adding the built-in driver to the kernel

Hello

I want to develop the driver for my camera. First, I decided to copy the ov5693 driver with a different name. I copy and rename ov5693.c, ov5693_mode_tbls.h, ov5693.h. In the new files, I change all “ov5693” to a new name.
In the / drivers / media / makefile, I added:
obj - $ (CONFIG_VIDEO_NEWNAME) + = newname.o

In / drivers / media / kconfig, I added:

config VIDEO_NEWNAME
tristate "support for the NEWNAME camera sensor"
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
---Help---
This is a Video4Linux2 level sensor driver for OmniVision
NEWNAME camera.

To compile this driver as a module, select M here: module
will be called NEWNAME.

In kernel-4.4 / arch / arm64 / configs / tegra21_defconfig, I added the line:
CONFIG_VIDEO_NEWNAME = y

Then I used the “Building an NVIDIA Kernel” from nvl4t_docs / index.html for L4T 28.1

After “make O = $ TEGRA_KERNEL_OUT zImage”, I can see the newname.o file in the drivers / media / i2c folder

In the generated dtb-file (I converted it to dts, and then returned to dtb). I manually change “ov5693” to newname. Before it i checked that this dtb works correctly with ov5693.

.....
        i2c@546c0000 {
            #address-cells = <0x1>;
            #size-cells = <0x0>;
            compatible = "nvidia,tegra210-vii2c";
            reg = <0x0 0x546c0000 0x0 0x34000>;
            iommus = <0x52 0x12>;
            interrupts = <0x0 0x11 0x4>;
            scl-gpio = <0x7b 0x92 0x0>;
            sda-gpio = <0x7b 0x93 0x0>;
            status = "okay";
            clocks = <0x41 0xd0 0x41 0x51 0x41 0x1c>;
            clock-names = "vii2c", "i2cslow", "host1x";
            resets = <0x41 0xd0>;
            reset-names = "vii2c";
            clock-frequency = <0x61a80>;
            bus-pullup-supply = <0x69>;
            avdd_dsi_csi-supply = <0x67>;
            linux,phandle = <0xe0>;
            phandle = <0xe0>;

            newname_c@20 {
                compatible = "nvidia,newname";
                reg = <0x36>;
                devnode = "video0";
                physical_w = "3.674";
                physical_h = "2.738";
                vertical-flip = "true";
                avdd-reg = "vana";
                iovdd-reg = "vif";
                clocks = <0x41 0x117>;
                clock-names = "clk_out_3";
                clock-frequency = <0x16e3600>;
                mclk = "clk_out_3";
                reset-gpios = <0x7b 0x94 0x0>;
                pwdn-gpios = <0x7b 0x97 0x0>;
                vana-supply = <0x94>;
                vif-supply = <0x93>;
                status = "disabled";
                linux,phandle = <0xe1>;
                phandle = <0xe1>;

                mode0 {
                    mclk_khz = "24000";
                    num_lanes = [32 00];
                    tegra_sinterface = "serial_c";
                    discontinuous_clk = "no";
                    dpcm_enable = "false";
                    cil_settletime = [30 00];
                    active_w = "2592";
                    active_h = "1944";
                    pixel_t = "bayer_bggr";
                    readout_orientation = "90";
                    line_length = "2688";
                    inherent_gain = [31 00];
                    mclk_multiplier = "6.67";
                    pix_clk_hz = "160000000";
                    min_gain_val = "1.0";
                    max_gain_val = "16";
                    min_hdr_ratio = [31 00];
                    max_hdr_ratio = "64";
                    min_framerate = "1.816577";
                    max_framerate = "30";
                    min_exp_time = "34";
                    max_exp_time = "550385";
                };

                mode1 {
                    mclk_khz = "24000";
                    num_lanes = [32 00];
                    tegra_sinterface = "serial_c";
                    discontinuous_clk = "no";
                    dpcm_enable = "false";
                    cil_settletime = [30 00];
                    active_w = "2592";
                    active_h = "1458";
                    pixel_t = "bayer_bggr";
                    readout_orientation = "90";
                    line_length = "2688";
                    inherent_gain = [31 00];
                    mclk_multiplier = "6.67";
                    pix_clk_hz = "160000000";
                    min_gain_val = "1.0";
                    max_gain_val = "16";
                    min_hdr_ratio = [31 00];
                    max_hdr_ratio = "64";
                    min_framerate = "1.816577";
                    max_framerate = "30";
                    min_exp_time = "34";
                    max_exp_time = "550385";
                };

                mode2 {
                    mclk_khz = "24000";
                    num_lanes = [32 00];
                    tegra_sinterface = "serial_c";
                    discontinuous_clk = "no";
                    dpcm_enable = "false";
                    cil_settletime = [30 00];
                    active_w = "1280";
                    active_h = "720";
                    pixel_t = "bayer_bggr";
                    readout_orientation = "90";
                    line_length = "1752";
                    inherent_gain = [31 00];
                    mclk_multiplier = "6.67";
                    pix_clk_hz = "160000000";
                    min_gain_val = "1.0";
                    max_gain_val = "16";
                    min_hdr_ratio = [31 00];
                    max_hdr_ratio = "64";
                    min_framerate = "2.787078";
                    max_framerate = "120";
                    min_exp_time = "22";
                    max_exp_time = "358733";
                };

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

                    port@0 {
                        reg = <0x0>;

                        endpoint {
                            csi-port = <0x2>;
                            bus-width = <0x2>;
                            remote-endpoint = <0x96>;
                            linux,phandle = <0xea>;
                            phandle = <0xea>;
                        };
                    };
                };
            };

        };



    e3333_lens_newname@P5V27C {
        min_focus_distance = "0.0";
        hyper_focal = "0.0";
        focal_length = "2.67";
        f_number = "2.0";
        aperture = "2.0";
    };

    e3326_lens_newname@P5V27C {
        min_focus_distance = "0.0";
        hyper_focal = "0.0";
        focal_length = "2.67";
        f_number = "2.0";
        aperture = "2.0";
    };

.......

    plugin-manager {

        fragment-e3326@0 {
            ids = "3326-*";

            override@0 {
                target = <0xe1>;

                _overlay_ {
                    status = "okay";
                };
            };

            override@1 {
                target = <0xe2>;

                _overlay_ {
                    status = "okay";
                    badge = "e3326_front_P5V27C";
                    position = "rear";
                    orientation = [31 00];
                };
            };

            override@2 {
                target = <0xe3>;

                _overlay_ {
                    status = "okay";
                    pcl_id = "v4l2_sensor";
                    devname = "newname 6-0036";
                    proc-device-tree = "/proc/device-tree/host1x/i2c@546c0000/newname_c@20";
                };
            };
.....

Then I run the script flash.sh and after starting the board I check the file /proc/config.gz - it does not have the line “CONFIG_VIDEO_NEWNAME = y”, and the driver also does not work.
In / dev / folder I do not have / video0 device. I think it means, that driver “newname” is missing in kernel.

I think I’m making some mistakes in the kernel configuration before building it. Could you help me?

Hi,

  1. If you are booting from SD card, you need to copy Image to /boot/ folder of SD card and dtb needs to be flashed to the EMMC.

  2. Did you change the compatible string in the driver to “nvidia,newname”?

Regards,
Rejeesh

Thanks for your answer

1. If you are booting from SD card, you need to copy Image to /boot/ folder of SD card and dtb needs to be flashed to the EMMC.

I did as it says in the manual - “Building the NVIDIA Kernel”:
6. Copy both the uncompressed (Image) and compressed (zImage) kernel images over the ones present in the following directory:
Linux_for_Tegra/kernel
7. Replace the contents of Linux_for_Tegra/kernel/dtb/ with the contents of:
$TEGRA__KERNEL_OUT/arch/arm64/boot/dts/

Then i used “sudo ./flash.sh -r jetson-tx2 mmcblk0p1” from “Flashing and Booting the Target Device” for download to the board.

2. Did you change the compatible string in the driver to “nvidia,newname”?
Yes, it looks like:
static struct of_device_id newname_of_match = {
{ .compatible = “nvidia,newname”, },
{ },
};

zImage won’t be used, only “Image”. Then extlinux.conf names the kernel Image file (I always suggest a different file name matching the future “uname -r”, e.g., “Image-4.4.38-test1+” after setting CONFIG_LOCALVERSION to “-test1”…the “+” is auto added if you use the source_sync.sh kernel source…and leave the original extlinux.conf entry and test with an alternate entry and serial console to pick the alternate). Don’t forget that as soon as you have a new “uname -r” you need to install modules as well. Modules are searched for in “/lib/modules/$(uname -r)/”.

Once you boot don’t start by looking at config.gz, start by seeing if you have the expected “uname -r”.

Note that device tree updates are different than kernel updates. You don’t need to flash for a kernel. If you check the docs you will find there is a flash.sh line for updating only device tree if you put the dtb file in the correct location. You can also use dd to put this in place, but I wouldn’t recommend it unless you know what you are doing.

linuxdev, thanks a lot

As you said i use “uname -r” to check kernel into the board. My mistake was “-r” when i started flash.sh.
Now newname driver works fine with on-board camera:

[    3.055678] i2c /dev entries driver
[    3.057106] [NEWNAME]: probing v4l2 sensor.
... and other my debug messages

But when i disconnect on-board camera and connect to tx1 my own pcb, i see only

[    3.029131] i2c /dev entries driver
[    3.031158] IR NEC protocol handler initialized

I already checked from user-space, that i2c on my own pcb works fine. I can read and write registers, but on startup I dont see “probing v4l2 sensor” and other messages. I think, that i must see this messages if i2c on my own pcb works in user-space.

uname” is actually a command line program name. The “$(uname -r)” will substitute during a command, e.g.:

cd "/lib/modules/$(uname -r)/"

…will cd to the module directory of the current kernel.

I do not have i2c experience, but what it comes down to is that if the hardware isn’t detected, it probably won’t load the driver (sometimes a driver can be told to load blindly for something you know is there). Device tree entries can influence this since a device may not be active unless configured, or might need setup prior to becoming useful. If a device tree is set up correctly, then it is possible for the driver to function. You might consider that in many cases an incorrect device tree may result in the effect of not having the hardware at all.

A message about probing probably means the driver thinks it should check, but it isn’t necessarily an indication that the hardware has been set up or will respond. The part which has “i2c@546c0000” tells the i2c driver the settings using the names in that block correspond to a controller located at address “0x546c0000”…the driver will probably set up that address based on the values in that device tree block…if there is no hardware there, then either nothing will happen or something bizarre and bad will happen.

The driver is part of the kernel Image or a kernel module. The firmware setup is part of the device tree…the two work together.

I do not have i2c experience, but what it comes down to is that if the hardware isn’t detected, it probably won’t load the driver

I can’t understand how kernel detect hardware:

  1. no connect on-board camera pcb - kernel don’t touch _probe function
  2. connect on-board camera pcb - kernel use _probe function
  3. connect my own camera pcb - kernel don’t touch _probe function

All looks fine: no correct pcb - no loading driver. But how work with my own pcb?
I try to change i2c-address in .dtb/.dts from on-board camera to my own i2c address:

newname_c@30 {
				compatible = "nvidia,newname";
				reg = <0x30>;
				devnode = "video0";
                                ...

If I connect on-board camera pcb with changed address - kernel touch _probe function. If I connect my own camera pcb - kernel don’t use _probe function, but “sudo i2cdetect -r 6” show me, that on i2c-6 there is device with address 0x30.

If i2c-address don’t used by kernel to detect hardware I don’t have any idea how it works. May be somewhere checked values of some gpios? Where can i read about it?

In some cases detect is via a hotplug mechanism or by hardware probing (this is just generally speaking, I lack i2c knowledge). In some cases a device tree has to be set up for a driver to know it is supposed to look for a controller or hardware. I do not know what i2c does for any specific case, but I suspect it is a device tree setup (or timing of some setup prior to the camera driver load).

The specific example I know of is the i2c query of an HDMI monitor’s DDC wire (the EDID data which the monitor replies with to say what its capabilities are is i2c). In this case something has set up a hotplug detect to know when the cable is plugged in or unplugged, and at plugin an i2c query is triggered…but the i2c controller and device tree are set up prior to this. During cold boot, assuming device tree and controller are already set up, it is possible for the driver load to cause probing regardless of hotplug being set up or not. Case 1, everything is set up prior to driver loading and probe occurs; case 2, everything is set up and a hotplug event is detected and the code for probing occurs. The former case works even if hotplug is not supported; the latter case is an extension to trigger hardware probing equivalent to that from the moment the driver loaded.

As I mentioned before, I do not know much about i2c. However, it seems likely you need the device tree setup to be confirmed as correct, and if this is confirmed (I can’t tell you if this is correct or not, I don’t know), and if the camera is connected and powered at the moment the driver loads, then probably the driver should probe at that time…perhaps this driver needs to add something at load to tell it to probe, or perhaps when it probes device tree was not set up so it didn’t know to probe that instance.

You might ask yourself these questions: Does driver load trigger any probing, and if not, should it? Does your camera connect mechanism provide a hotplug event which can be used to trigger the same code as run upon driver load? If driver load does not probe known addresses, then you must have a hotplug detect. In both cases of hardware probe, did the probe actually go out? If not, then device tree may be to blame since probe should have resulted in setup.

Thanks a lot
You might ask yourself these questions:
I wrote new driver and cancel experiments with copying of ov5693 driver.
Now _probe works as I expect. I can config camera by I2C. But in _probe function I have:

struct ap0100_platform_data *pdata = client->dev.platform_data;

Now pdata is equal NULL. May be my device-tree not fully correct?

i2c@546c0000 {
			#address-cells = <0x1>;
			#size-cells = <0x0>;
			compatible = "nvidia,tegra210-vii2c";
			reg = <0x0 0x546c0000 0x0 0x34000>;
			iommus = <0x52 0x12>;
			interrupts = <0x0 0x11 0x4>;
			scl-gpio = <0x7b 0x92 0x0>;
			sda-gpio = <0x7b 0x93 0x0>;
			status = "okay";
			clocks = <0x41 0xd0 0x41 0x51 0x41 0x1c>;
			clock-names = "vii2c", "i2cslow", "host1x";
			resets = <0x41 0xd0>;
			reset-names = "vii2c";
			clock-frequency = <0x186a0>;
			bus-pullup-supply = <0x69>;
			avdd_dsi_csi-supply = <0x67>;
			linux,phandle = <0xe0>;
			phandle = <0xe0>;

			ap0100_c@30 {
				compatible = "nvidia,ap0100";
				reg = <0x30>;
				status = "okay";
				devnode = "video0";
				physical_w = "3.674";
				physical_h = "2.738";
				vertical-flip = "true";
				avdd-reg = "vana";
				iovdd-reg = "vif";
				clocks = <0x41 0x117>;
				clock-names = "clk_out_3";
				clock-frequency = <0x16e3600>;
				mclk = "clk_out_3";
				vana-supply = <0x94>;
				vif-supply = <0x93>;

				mode0 {
					mclk_khz = "24000";
					num_lanes = [32 00];
					tegra_sinterface = "serial_c";
					discontinuous_clk = "no";
					dpcm_enable = "false";
					cil_settletime = [30 00];
					active_w = "2592";
					active_h = "1944";
					pixel_t = "bayer_bggr";
					readout_orientation = "90";
					line_length = "2688";
					inherent_gain = [31 00];
					mclk_multiplier = "6.67";
					pix_clk_hz = "160000000";
					min_gain_val = "1.0";
					max_gain_val = "16";
					min_hdr_ratio = [31 00];
					max_hdr_ratio = "64";
					min_framerate = "1.816577";
					max_framerate = "30";
					min_exp_time = "34";
					max_exp_time = "550385";
				};

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

					port@0 {
						reg = <0x0>;

						endpoint {
							csi-port = <0x2>;
							bus-width = <0x2>;
							remote-endpoint = <0x96>;
							linux,phandle = <0xea>;
							phandle = <0xea>;
						};
					};
				};
			};
		};

I suspect you are right about device tree as the issue, but I don’t know enough about it to be able to confirm.

hello folko12,

there’s probing function to acquire platform data from device tree.
did you got any kernel failure message?
please try to narrow down the issue and share the logs for reference.
thanks