Thank you for the clarification.
I understand and acknowledge the distinction between firmware-applied overlays via config.txt and dynamically loaded overlays, and that dtoverlay -l will not list firmware-applied overlays. That point is now clear.
To address your request for concrete information, below is the exact technical state and source code currently in use.
Platform & Kernel
Hardware: Raspberry Pi CM4 (BCM2711)
OS: Debian / Raspberry Pi OS (Bookworm / Trixie)
Kernel: 6.12.47+rpt-rpi-v8 (stock Raspberry Pi kernel, unmodified)
Firmware: Standard Raspberry Pi firmware (no custom patches)
No kernel replacement, no rpi-source, and no custom kernel builds were installed at the time of testing.
Device Tree Overlay (Firmware-applied)
The overlay is applied via config.txt and therefore not visible to dtoverlay -l, as expected.
config.txt excerpt:
arm_64bit=1
camera_auto_detect=0
disable_camera_led=1
dtoverlay=vc4-kms-v3d,noisp
dtparam=i2c_vc=on
dtparam=csi1=on
dtoverlay=pr2000k-final
Overlay source (pr2000k-final.dts):
/dts-v1/;
/plugin/;
/ {
compatible = "brcm,bcm2711";
fragment@0 {
target-path = "/soc/i2c@7e804000";
__overlay__ {
status = "okay";
#address-cells = <1>;
#size-cells = <0>;
pr2000k: pr2000k@5c {
compatible = "pixelplus,pr2000k";
reg = <0x5c>;
status = "okay";
port {
pr2000k_out: endpoint {
remote-endpoint = <&unicam_in>;
bus-type = <4>; /* CSI-2 */
clock-lanes = <0>;
data-lanes = <1 2>;
};
};
};
};
};
fragment@1 {
target-path = "/soc/csi@7e801000";
__overlay__ {
status = "okay";
brcm,media-controller;
port {
unicam_in: endpoint {
remote-endpoint = <&pr2000k_out>;
bus-type = <4>;
clock-lanes = <0>;
data-lanes = <1 2>;
};
};
};
};
};
This overlay does appear in /proc/device-tree and the I²C device is instantiated correctly.
PR2000K Driver Source (GPLv2)
The PR2000K driver is GPLv2 and provided below in full.
Location:
/home/pel/pr2000k-drive/pr2000k.c
Source:
// SPDX-License-Identifier: GPL-2.0
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/of.h>
#include <media/v4l2-device.h>
#include <media/v4l2-subdev.h>
#include <media/v4l2-async.h>
#include <media/media-entity.h>
#define PR2000K_NAME "pr2000k"
struct pr2000k {
struct v4l2_subdev sd;
struct media_pad pad;
struct i2c_client *client;
};
static int pr2000k_s_stream(struct v4l2_subdev *sd, int enable)
{
pr_info("pr2000k: s_stream %d\n", enable);
return 0;
}
static const struct v4l2_subdev_video_ops pr2000k_video_ops = {
.s_stream = pr2000k_s_stream,
};
static const struct v4l2_subdev_ops pr2000k_subdev_ops = {
.video = &pr2000k_video_ops,
};
static int pr2000k_probe(struct i2c_client *client)
{
struct pr2000k *priv;
int ret;
priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->client = client;
v4l2_i2c_subdev_init(&priv->sd, client, &pr2000k_subdev_ops);
priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
priv->sd.entity.function = MEDIA_ENT_F_ATV_DECODER;
priv->pad.flags = MEDIA_PAD_FL_SOURCE;
ret = media_entity_pads_init(&priv->sd.entity, 1, &priv->pad);
if (ret)
return ret;
ret = v4l2_async_register_subdev(&priv->sd);
if (ret)
return ret;
pr_info("pr2000k: probe success (DT-driven CSI2)\n");
return 0;
}
static void pr2000k_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
v4l2_async_unregister_subdev(sd);
media_entity_cleanup(&sd->entity);
}
static const struct of_device_id pr2000k_of_match[] = {
{ .compatible = "pixelplus,pr2000k" },
{ }
};
MODULE_DEVICE_TABLE(of, pr2000k_of_match);
static struct i2c_driver pr2000k_driver = {
.driver = {
.name = PR2000K_NAME,
.of_match_table = pr2000k_of_match,
},
.probe = pr2000k_probe,
.remove = pr2000k_remove,
};
module_i2c_driver(pr2000k_driver);
MODULE_DESCRIPTION("PixelPlus PR2000K V4L2 Subdevice");
MODULE_AUTHOR("Zohaib");
MODULE_LICENSE("GPL");
Observed Runtime Behaviour
pr2000k probes successfully (probe success logged)
I²C communication is confirmed
Overlay is present in the live device tree
No /dev/v4l-subdev* nodes
No Unicam media entity
No async bound message from Unicam
The failure occurs after v4l2_async_register_subdev() and before any call to
bcm2835_unicam_async_bound().
This suggests the async notifier match is silently rejected inside the Unicam path, likely during DT/fwnode validation.
Request for Guidance
Given that ADV728x and TC358743 work on the same platform, could you please advise:
Which DT properties or endpoint characteristics bcm2835-unicam validates during async matching?
Are there Unicam-specific expectations (clocking, port numbering, endpoint ordering, or function type) that differ from i.MX / Rockchip?
Is there a recommended way to instrument or trace why v4l2_async_match_fwnode() fails in this case?
I am happy to add targeted debug logging if you can indicate which checks are relevant.
Thank you for your time and guidance.
As a next step, I am preparing a side-by-side comparison of this overlay against the existing ADV728x overlay to identify any Unicam-specific expectations.”
I understand and acknowledge the distinction between firmware-applied overlays via config.txt and dynamically loaded overlays, and that dtoverlay -l will not list firmware-applied overlays. That point is now clear.
To address your request for concrete information, below is the exact technical state and source code currently in use.
Platform & Kernel
Hardware: Raspberry Pi CM4 (BCM2711)
OS: Debian / Raspberry Pi OS (Bookworm / Trixie)
Kernel: 6.12.47+rpt-rpi-v8 (stock Raspberry Pi kernel, unmodified)
Firmware: Standard Raspberry Pi firmware (no custom patches)
No kernel replacement, no rpi-source, and no custom kernel builds were installed at the time of testing.
Device Tree Overlay (Firmware-applied)
The overlay is applied via config.txt and therefore not visible to dtoverlay -l, as expected.
config.txt excerpt:
arm_64bit=1
camera_auto_detect=0
disable_camera_led=1
dtoverlay=vc4-kms-v3d,noisp
dtparam=i2c_vc=on
dtparam=csi1=on
dtoverlay=pr2000k-final
Overlay source (pr2000k-final.dts):
/dts-v1/;
/plugin/;
/ {
compatible = "brcm,bcm2711";
fragment@0 {
target-path = "/soc/i2c@7e804000";
__overlay__ {
status = "okay";
#address-cells = <1>;
#size-cells = <0>;
pr2000k: pr2000k@5c {
compatible = "pixelplus,pr2000k";
reg = <0x5c>;
status = "okay";
port {
pr2000k_out: endpoint {
remote-endpoint = <&unicam_in>;
bus-type = <4>; /* CSI-2 */
clock-lanes = <0>;
data-lanes = <1 2>;
};
};
};
};
};
fragment@1 {
target-path = "/soc/csi@7e801000";
__overlay__ {
status = "okay";
brcm,media-controller;
port {
unicam_in: endpoint {
remote-endpoint = <&pr2000k_out>;
bus-type = <4>;
clock-lanes = <0>;
data-lanes = <1 2>;
};
};
};
};
};
This overlay does appear in /proc/device-tree and the I²C device is instantiated correctly.
PR2000K Driver Source (GPLv2)
The PR2000K driver is GPLv2 and provided below in full.
Location:
/home/pel/pr2000k-drive/pr2000k.c
Source:
// SPDX-License-Identifier: GPL-2.0
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/of.h>
#include <media/v4l2-device.h>
#include <media/v4l2-subdev.h>
#include <media/v4l2-async.h>
#include <media/media-entity.h>
#define PR2000K_NAME "pr2000k"
struct pr2000k {
struct v4l2_subdev sd;
struct media_pad pad;
struct i2c_client *client;
};
static int pr2000k_s_stream(struct v4l2_subdev *sd, int enable)
{
pr_info("pr2000k: s_stream %d\n", enable);
return 0;
}
static const struct v4l2_subdev_video_ops pr2000k_video_ops = {
.s_stream = pr2000k_s_stream,
};
static const struct v4l2_subdev_ops pr2000k_subdev_ops = {
.video = &pr2000k_video_ops,
};
static int pr2000k_probe(struct i2c_client *client)
{
struct pr2000k *priv;
int ret;
priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->client = client;
v4l2_i2c_subdev_init(&priv->sd, client, &pr2000k_subdev_ops);
priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
priv->sd.entity.function = MEDIA_ENT_F_ATV_DECODER;
priv->pad.flags = MEDIA_PAD_FL_SOURCE;
ret = media_entity_pads_init(&priv->sd.entity, 1, &priv->pad);
if (ret)
return ret;
ret = v4l2_async_register_subdev(&priv->sd);
if (ret)
return ret;
pr_info("pr2000k: probe success (DT-driven CSI2)\n");
return 0;
}
static void pr2000k_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
v4l2_async_unregister_subdev(sd);
media_entity_cleanup(&sd->entity);
}
static const struct of_device_id pr2000k_of_match[] = {
{ .compatible = "pixelplus,pr2000k" },
{ }
};
MODULE_DEVICE_TABLE(of, pr2000k_of_match);
static struct i2c_driver pr2000k_driver = {
.driver = {
.name = PR2000K_NAME,
.of_match_table = pr2000k_of_match,
},
.probe = pr2000k_probe,
.remove = pr2000k_remove,
};
module_i2c_driver(pr2000k_driver);
MODULE_DESCRIPTION("PixelPlus PR2000K V4L2 Subdevice");
MODULE_AUTHOR("Zohaib");
MODULE_LICENSE("GPL");
Observed Runtime Behaviour
pr2000k probes successfully (probe success logged)
I²C communication is confirmed
Overlay is present in the live device tree
No /dev/v4l-subdev* nodes
No Unicam media entity
No async bound message from Unicam
The failure occurs after v4l2_async_register_subdev() and before any call to
bcm2835_unicam_async_bound().
This suggests the async notifier match is silently rejected inside the Unicam path, likely during DT/fwnode validation.
Request for Guidance
Given that ADV728x and TC358743 work on the same platform, could you please advise:
Which DT properties or endpoint characteristics bcm2835-unicam validates during async matching?
Are there Unicam-specific expectations (clocking, port numbering, endpoint ordering, or function type) that differ from i.MX / Rockchip?
Is there a recommended way to instrument or trace why v4l2_async_match_fwnode() fails in this case?
I am happy to add targeted debug logging if you can indicate which checks are relevant.
Thank you for your time and guidance.
As a next step, I am preparing a side-by-side comparison of this overlay against the existing ADV728x overlay to identify any Unicam-specific expectations.”
Statistics: Posted by mukam — Mon Jan 05, 2026 8:03 pm