Verdin IMX8MP + ADV7281A-M

Verdin imx8mp v1.1a
Toradex Linux 5.4

Intro

I need to get an ADV7281A-M working with the Verdin IMX8MP. This is all connected up on a custom carrier board.

ADV7281A-M is a 6 channel Analog MIPI .

From the research I’ve done (not expert here), there are two ADV72xx drivers in the kernel.

  • drivers/media/i2c/adv7180.c (i’ll refer to this as i2c-adv driver)
    and
  • drivers/media/platform/mxc/capture/adv7280.c (i’ll refer to this as mxc-adv driver)

The i2c-adv driver supports the “adv7281-m” chip, which is exactly the same apart from the IDENT according to AD’s website.

The mxc-adv driver only supports adv7280.c which isn’t MIPI out (it is parallel).

Looking at the mipi examples in the Toradex overlays, the example used is a driver in the mxc directory.

The ov5640 (overlay example) driver is making calls like mipi_csi2_set_lanes which doesn’t appear to happen in the i2c folder.

Goal

The end goal is to have an attached camera displayed on the screen in real time (and recorded to disk).

The product is using SDL2 on the screen, and the approach I was looking at (considering) was using v4l2 and copying the pixel data from the camera onto a texture etc. No approach was decided/researched for recording the video.

Questions

  • Is it possible to used the older i2c drivers with the imx8mp?
  • Do I need to make a driver that uses the mxc system?
  • Are there any other halfway solutions? I’m okay with configuring the chip from userspace if there is some generic MIPI driver I can use.

Additional
I have tried, and the closest I have come resulted in a BUG() from the kernel:

I couldn’t find any examples of drive tree’s using “adi,advXXXXX-m” (m for mipi) in the kernel. I suspect there might be a way.

[    8.781304] kernel BUG at drivers/media/mc/mc-entity.c:665!
[    8.789306] Internal error: Oops - BUG: 0 [#1] PREEMPT SMP
[    8.797256] Modules linked in: rfkill snd_soc_nau8822 crct10dif_ce ina2xx flexcan adv7180(+) lm75 can_dev libcomposite configfs imx_sdma galcore(O)
[    8.813158] CPU: 3 PID: 287 Comm: systemd-udevd Tainted: G           O      5.4.161-5.6.0-devel+git.49115d35cacd #1
[    8.813163] Hardware name: Toradex Verdin iMX8M Plus WB on xxxx
[    8.836119] pstate: 40000005 (nZcv daif -PAN -UAO)
[    8.836130] pc : media_create_pad_link+0x12c/0x158
[    8.836141] lr : subdev_notifier_complete+0xbc/0x6f0
[    8.858836] sp : ffff800011e9b830
[    8.858839] x29: ffff800011e9b830 x28: 0000000000000000
[    8.858843] x27: 000000000000000c x26: ffff80001104e8c8
[    8.858846] x25: ffff0000f943b440 x24: ffff0000f943b6a0
[    8.858849] x23: ffff0000f943b0c4 x22: 0000000000000000
[    8.858853] x21: ffff80001104e8a8 x20: ffff0000f943b080
[    8.904824] x19: ffff0000f943b8a0 x18: 0000000000000000
[    8.904828] x17: 0000000000000000 x16: 0000000000000000
[    8.904832] x15: 0000000000000000 x14: 0000000000000000
[    8.928466] x13: 0000000000000001 x12: 0000000000000000
[    8.928469] x11: 0000000000000007 x10: 0101010101010101
[    8.928472] x9 : 0000000000000004 x8 : 0000000000000004
[    8.928475] x7 : 0000000000000008 x6 : dead000000000100
[    8.928478] x5 : ffff0000f4650258 x4 : 0000000000000001
[    8.928481] x3 : 0000000000000000 x2 : 0000000000000000
[    8.928484] x1 : 000000000000000c x0 : 0000000000000000
[    8.928487] Call trace:
[    8.928496]  media_create_pad_link+0x12c/0x158
[    8.928501]  v4l2_async_notifier_try_complete.part.0+0x44/0x60
[    8.928504]  v4l2_async_register_subdev+0xf4/0x1b0
[    8.928512]  adv7180_probe+0x328/0x360 [adv7180]
[    8.928517]  i2c_device_probe+0x308/0x338
[    8.928522]  really_probe+0xd8/0x438
[    8.928525]  driver_probe_device+0xdc/0x130
[    8.928528]  device_driver_attach+0x6c/0x78
[    8.928531]  __driver_attach+0x9c/0x168
[    8.928534]  bus_for_each_dev+0x6c/0xc0
[    8.928537]  driver_attach+0x20/0x28
[    8.928540]  bus_add_driver+0x138/0x218
[    8.928545]  driver_register+0x60/0x110
[    8.928548]  i2c_register_driver+0x44/0xa8
[    8.928554]  adv7180_driver_init+0x20/0x1000 [adv7180]
[    8.928558]  do_one_initcall+0x50/0x1a8
[    8.928562]  do_init_module+0x50/0x210
[    8.928565]  load_module+0x2098/0x2628
[    8.928568]  __do_sys_finit_module+0xac/0x110
[    8.928573]  __arm64_sys_finit_module+0x1c/0x28
[    8.928577]  el0_svc_common.constprop.0+0x68/0x178
[    8.928581]  el0_svc_handler+0x20/0x80
[    8.928583]  el0_svc+0x8/0x208
[    8.928589] Code: a94363f7 f94023f9 a8c57bfd d65f03c0 (d4210000)
[    8.928593] ---[ end trace e8cd56b4aff223a8 ]---
[    8.928596] note: systemd-udevd[287] exited with preempt_count 1
[    8.931790] ------------[ cut here ]------------

Any help is much appreciated.

Hi @phil , sorry for the late reply.

I’m not a 100% sure but it seems that ADV7281 and ADV7280 have the same registers which explains why one of the drivers is compatible with several models.

I would go the route of using what you call mxc-adv as this is specifically done for iMX platform, but you may have to make some changes to make it compatible for the ADV7281.

Here you can find an overlay for the Verdin iMX8MP and the ADV7280 that you can use as reference:
verdin-imx8mp_adv7280_20_overlay.dts (1.6 KB)

Hope this helps.

Kind regards,
Alvaro.

Thanks for that. It kinda looks like your existing camera overlay with a few renames.

Currently the “mxc-adv” doesn’t support MIPI at all, so there will be some significant changes needed.

I have made some progress since first posting. I looked at the “i2c-adv” driver in the latest 5.xxx freescale kernel and there were some enhancements made. One in particular was the ability for the driver to control the reset pin. I back ported this functionality and for the first time the /dev/video0 was registered and v4l2-ctrl --all hasn’t crashed. Before it would register then immediately unregister as the link status was 0. Given I only changed the reset pin control it must now be receiving MIPI data.

On monday i’ll try and get some pixel data out of v4l2 and verify it is working.

If I get a working solution I will post up a patch to the driver for anyone else having the same problem.

1 Like

Hello Phil,

I would be interested in progress on this as well.

I am currently designing a custom PCB to interface with the ADV7281A-M. I figured the ADV7280-M drivers would work all the same on Linux without issue.
Unless I am misunderstanding, I’m assuming the ADV7280-M drivers are broken if I were to use the ADV7281A-M in my project at this point?

I am simply looking to do the same, though I am not using an IMX8MP in my testing as of current.

Thanks.

Hi Pawkow,

I now have it working. If you are using the 5.4 kernel, you will need to backport the reset pin control from the 5.14 kernel.

Relevant parts of my device tree:


&i2c3 {
	status = "okay";

	clock-frequency = <10000>;

	adv7281a-m@21 {
		status = "okay";
		compatible = "adi,adv7281-m";
		reg = <0x21>;
		reset-gpios = <&gpio1 7 GPIO_ACTIVE_LOW>;
		powerdown-gpios = <&gpio1 8 GPIO_ACTIVE_LOW>;
		interrupt-parent = <&gpio4>;
		interrupts = <3 IRQ_TYPE_LEVEL_LOW>;

		port {
			adv7281a_mipi_0_ep: endpoint {
				remote-endpoint = <&mipi_csi0_ep>;
			};
		};
	};
};

and


&mipi_csi_0 {
	#address-cells = <1>;
	#size-cells = <0>;
	status = "okay";
	bus-width = <1>;

	clock-frequency = <108000000>;

	port@0 {
		reg = <0>;

		mipi_csi0_ep: endpoint {
			csis-hs-settle = <13>;
			csis-clk-settle = <2>;
			csis-wclk;
			data-lanes = <1>;
			remote-endpoint = <&adv7281a_mipi_0_ep>;
		};
	};
};

Notes:

  • The IC doesn’t seem to send MIPI data if powered on with a video input signal present. The stream hangs waiting for data. If you switch the input mux to an empty channel then switch back again it comes right.
  • Your GPIOs will probably be different.

Many thanks for posting your DT @phil ! By the way what driver did you end up using? Did you have to make many changes to it?

I diffed the driver for the latest 5.x kernel vs my 5.4 kernel and then copied in all changes related to the reset pin.

I don’t have a clean patch file with just those changes at the moment otherwise I would post it. It is very easy to do manually.

As an aside, do you know how to call the s_routing callback that is part of the v4l2_subdev_video_ops struct. I want to change the input channel and couldn’t work out how to do it. I ended up manually injecting i2c commands.

Hi Phil. Could you elaborate on this comment? My setup is pretty much exactly the same as yours and I’m struggling to get video data from the ADV chip. How do you switch the input mux to an empty channel?

  • The IC doesn’t seem to send MIPI data if powered on with a video input signal present. The stream hangs waiting for data. If you switch the input mux to an empty channel then switch back again it comes right.

Hi David,

There is a race condition between the imx8 CSI receive hardware becoming ready and the ADV driver enabling the chip’s CSI output. The fix was to delay enabling of the CSI output of the ADV chip.

I had to hack up adv7180.c to get it working. I also added some code to allow switching of the video inputs via IOCTL.

I can share what I did, but use at your own risk. This is based on Linux 5.15.77 from Toradex.

I’ve attached my modified driver file; you will want diff with your version and take the parts related to csi_start_delayed (Maybe also the input mux switching). I changed some other register values from memory which you probably don’t want.

adv7180.c (42.7 KB)

Here is how you can then select the input from user space.

struct v4l2_control control;
control.id = V4L2_CID_USER_ADV7180_BASE + 1; // Input mux
control.value = input;
if (-1 == xioctl(fdSubdev, VIDIOC_S_CTRL, &control)) {
            fprintf(stderr, "Failed to switch input channel");
}

Also, device tree:


&i2c3 {
	status = "okay";

	clock-frequency = <10000>;

	adv7281a-m@21 {
		status = "okay";
		compatible = "adi,adv7281-m";
		reg = <0x21>;

		reset-gpios = <&gpio1 7 GPIO_ACTIVE_LOW>;
		powerdown-gpios = <&gpio1 8 GPIO_ACTIVE_LOW>;
		interrupt-parent = <&gpio4>;
		interrupts = <3 IRQ_TYPE_LEVEL_LOW>;

		pinctrl-names = "default";
		pinctrl-0 = <&pinctrl_gpio5>, <&pinctrl_gpio6>, <&pinctrl_gpio7>;

		port {
			adv7281a_mipi_0_ep: endpoint {
				remote-endpoint = <&mipi_csi0_ep>;
			};
		};
	};
};

&isi_0 {
	status = "okay";

	cap_device {
		status = "okay";
	};

	m2m_device {
		status = "disabled";
	};
};

&mipi_csi_0 {
	#address-cells = <1>;
	#size-cells = <0>;
	status = "okay";
	bus-width = <1>;

	clock-frequency = <108000000>;

	port@0 {
		reg = <0>;

		mipi_csi0_ep: endpoint {
			csis-hs-settle = <13>;
			csis-clk-settle = <2>;
			csis-wclk;
			data-lanes = <1>;
			remote-endpoint = <&adv7281a_mipi_0_ep>;
		};
	};
};

&cameradev {
	status = "okay";
};

And then there is making sure the drivers are enabled in the kernel config.

If I’ve missed anything or you have any other questions don’t hesitate to ask. This took ages to work out and I’m happy to save someone else the pain.

Cheers,
Phil