Jetson Camera Drivers - Fixed V4L2 Video Devices
Overview
The following changes allow to define a fixed name for the V4L2 video device created when loading a camera driver. These changes were tested on JP-4.5.1, JP-4.6 and JP-5.1.5. After applying them, recompile both the kernel and device tree and update them into your Jetson board.
Device-tree Changes
Define devnode field on the specific VI port node:
/ {
....
host1x {
/* VI Setup */
vi@15700000 {
ports {
port@0 {
reg = <0>;
camera_vi_in0: endpoint {
devnode = "video1"; /* V4L2 Device name as preferred by the user */
port-index = <4>;
vc-id = <0>;
bus-width = <4>;
remote-endpoint = <&camera_csi_out0>;
};
};
....
Kernel Changes
Apply the following changes to fix video device names.
* kernel/kernel-4.9/drivers/media/v4l2-core/v4l2-dev.c
For jetpack 4 use:
@@ -825,6 +825,8 @@ int __video_register_device(struct video_device *vdev, int type, int nr,
int minor_offset = 0;
int minor_cnt = VIDEO_NUM_DEVICES;
const char *name_base;
+ int n = 0;
+ int valid_name = 1;
/* A minor value of -1 marks this video device as never
having been registered */
@@ -964,7 +966,42 @@ int __video_register_device(struct video_device *vdev, int type, int nr,
vdev->dev.class = &video_class;
vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor);
vdev->dev.parent = vdev->dev_parent;
- dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num);
+ if (VFL_TYPE_GRABBER == type) {
+
+ /* If no video name was assigned in the device tree then assign one */
+ if (strcmp(vdev->name, "") == 0) {
+ snprintf(vdev->name, sizeof(vdev->name), "%s%d", name_base, vdev->num);
+ }
+
+ /* Compare the name we just set with every other device to check that there
+ isn't other with an equal name */
+ do {
+ /* Assume that the name is valid, otherwise it will be set to 0 and the
+ loop will continue */
+ valid_name = 1;
+ for (i = 0; i < VIDEO_NUM_DEVICES; i++) {
+ if (video_device[i] != NULL) {
+ if (video_device[i]->dev.kobj.name != NULL) {
+ /* If we find another device with the same name then start the
+ check again with the next videoX name, where X is the n
+ counter */
+ if (strcmp(video_device[i]->dev.kobj.name, vdev->name) == 0) {
+ snprintf(vdev->name, sizeof(vdev->name), "%s%d", name_base, n);
+ n++;
+ valid_name = 0;
+ break;
+ }
+ }
+ }
+ }
+ /* If a valid name was found we will exit with the final name set,
+ otherwise repeat the loop to check the new proposed name */
+ } while (!valid_name);
+
+ dev_set_name(&vdev->dev, "%s", vdev->name);
+ } else {
+ dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num);
+ }
ret = device_register(&vdev->dev);
if (ret < 0) {
printk(KERN_ERR "%s: device_register failed\n", __func__);
For jetpack 5 use:
@@ -825,6 +825,8 @@ int __video_register_device(struct video_device *vdev, int type, int nr,
int minor_offset = 0;
int minor_cnt = VIDEO_NUM_DEVICES;
const char *name_base;
+ int n = 0;
+ int valid_name = 1;
/* A minor value of -1 marks this video device as never
having been registered */
@@ -964,7 +966,42 @@ int __video_register_device(struct video_device *vdev, int type, int nr,
vdev->dev.class = &video_class;
vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor);
vdev->dev.parent = vdev->dev_parent;
- dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num);
+ if (VFL_TYPE_VIDEO == type) {
+
+ /* If no video name was assigned in the device tree then assign one */
+ if (strcmp(vdev->name, "") == 0) {
+ snprintf(vdev->name, sizeof(vdev->name), "%s%d", name_base, vdev->num);
+ }
+
+ /* Compare the name we just set with every other device to check that there
+ isn't other with an equal name */
+ do {
+ /* Assume that the name is valid, otherwise it will be set to 0 and the
+ loop will continue */
+ valid_name = 1;
+ for (i = 0; i < VIDEO_NUM_DEVICES; i++) {
+ if (video_devices[i] != NULL) {
+ if (video_devices[i]->dev.kobj.name != NULL) {
+ /* If we find another device with the same name then start the
+ check again with the next videoX name, where X is the n
+ counter */
+ if (strcmp(video_devices[i]->dev.kobj.name, vdev->name) == 0) {
+ snprintf(vdev->name, sizeof(vdev->name), "%s%d", name_base, n);
+ n++;
+ valid_name = 0;
+ break;
+ }
+ }
+ }
+ }
+ /* If a valid name was found we will exit with the final name set,
+ otherwise repeat the loop to check the new proposed name */
+ } while (!valid_name);
+
+ dev_set_name(&vdev->dev, "%s", vdev->name);
+ } else {
+ dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num);
+ }
ret = device_register(&vdev->dev);
if (ret < 0) {
printk(KERN_ERR "%s: device_register failed\n", __func__);
- kernel/nvidia/drivers/media/platform/tegra/camera/vi/channel.c
@@ -2290,6 +2290,16 @@ int tegra_channel_init_video(struct tegra_channel *chan)
chan->video->ctrl_handler = &chan->ctrl_handler;
chan->video->lock = &chan->video_lock;
+ /* Validate that the name fits */
+ if (ARRAY_SIZE(chan->video->name) >= ARRAY_SIZE(chan->devnode_name)) {
+ strncpy(chan->video->name, chan->devnode_name, ARRAY_SIZE(
+ chan->devnode_name));
+ } else {
+ ret = -ENOMEM;
+ dev_err(&chan->video->dev, "Not enough space for channel video name");
+ goto ctrl_init_error;
+ }
+
video_set_drvdata(chan->video, chan);
return ret;
- kernel/nvidia/drivers/media/platform/tegra/camera/vi/graph.c
@@ -518,6 +518,7 @@ int tegra_vi_get_port_info(struct tegra_channel *chan,
struct device_node *port;
int value = 0xFFFF;
int ret = 0, i;
+ const char *temp_str = NULL;
ports = of_get_child_by_name(node, "ports");
if (ports == NULL)
@@ -550,6 +551,20 @@ int tegra_vi_get_port_info(struct tegra_channel *chan,
return -EINVAL;
}
+ /* Get video device name */
+ ret = of_property_read_string(ep, "devnode", &temp_str);
+ if (ret < 0) {
+ strcpy(chan->devnode_name, "");
+ dev_err(chan->vi->dev, "devnode name not defined\n");
+ } else {
+ /* Validate that the name plus the null character fits */
+ if (ARRAY_SIZE(chan->devnode_name) >= strlen(temp_str) + 1) {
+ strncpy(chan->devnode_name, temp_str, strlen(temp_str) + 1);
+ } else {
+ dev_err(chan->vi->dev, "Not enough space for devnode name");
+ return -ENOMEM;
+ }
+ }
/* Get CSI port */
ret = of_property_read_u32(ep, "port-index", &value);
if (ret < 0)
- kernel/nvidia/include/media/mc_common.h
@@ -48,6 +48,8 @@
#define TEGRA_MEM_FORMAT 0
#define TEGRA_ISP_FORMAT 1
+#define V4L2_VIDEO_DEVICE_NAME_SIZE 32
+
enum channel_capture_state {
CAPTURE_IDLE = 0,
CAPTURE_GOOD,
@@ -263,6 +265,7 @@ struct tegra_channel {
atomic_t syncpt_depth;
struct rw_semaphore reset_lock;
+ char devnode_name[V4L2_VIDEO_DEVICE_NAME_SIZE];
};
#define to_tegra_channel(vdev) \
Jetpack 6
For Jetpack 6+ these are the changes needed (tested on Jetpack 6.2)
diff --git a/nvidia-oot/drivers/media/platform/tegra/camera/vi/channel.c b/nvidia-oot/drivers/media/platform/tegra/camera/vi/channel.c
index e6826f838..5a9ce1d6a 100644
--- a/nvidia-oot/drivers/media/platform/tegra/camera/vi/channel.c
+++ b/nvidia-oot/drivers/media/platform/tegra/camera/vi/channel.c
@@ -2660,6 +2660,7 @@ int tegra_channel_init(struct tegra_channel *chan)
init_waitqueue_head(&chan->release_wait);
atomic_set(&chan->restart_version, 1);
chan->capture_version = 0;
+ chan->video_nr = -1;
spin_lock_init(&chan->start_lock);
spin_lock_init(&chan->release_lock);
INIT_LIST_HEAD(&chan->dequeue);
diff --git a/nvidia-oot/drivers/media/platform/tegra/camera/vi/graph.c b/nvidia-oot/drivers/media/platform/tegra/camera/vi/graph.c
index 7c2d05e74..a2e1d270d 100644
--- a/nvidia-oot/drivers/media/platform/tegra/camera/vi/graph.c
+++ b/nvidia-oot/drivers/media/platform/tegra/camera/vi/graph.c
@@ -45,6 +45,36 @@ tegra_vi_graph_find_entity(struct tegra_channel *chan,
return NULL;
}
+static void tegra_vi_graph_update_video_nr_from_devnode(struct tegra_channel *chan,
+ struct device_node *node)
+{
+ const char *devnode = NULL;
+ unsigned int video_nr = 0;
+ int ret = 0;
+
+ if (node == NULL)
+ return;
+
+ ret = of_property_read_string(node, "devnode", &devnode);
+ if (ret != 0)
+ return;
+
+ if (strncmp(devnode, "video", 5) != 0 || devnode[5] == '\0') {
+ dev_warn(chan->vi->dev, "ignoring invalid devnode '%s' on %pOF\n",
+ devnode, node);
+ return;
+ }
+
+ ret = kstrtouint(devnode + 5, 10, &video_nr);
+ if (ret != 0) {
+ dev_warn(chan->vi->dev, "ignoring invalid devnode '%s' on %pOF\n",
+ devnode, node);
+ return;
+ }
+
+ chan->video_nr = (int)video_nr;
+}
+
static int tegra_vi_graph_build_one(struct tegra_channel *chan,
struct tegra_vi_graph_entity *entity)
{
@@ -304,10 +334,12 @@ static int tegra_vi_graph_notify_complete(struct v4l2_async_notifier *notifier)
return ret;
}
- ret = video_register_device(chan->video, VFL_TYPE_VIDEO, -1);
+ ret = video_register_device(chan->video, VFL_TYPE_VIDEO,
+ chan->video_nr);
if (ret < 0) {
- dev_err(chan->vi->dev, "failed to register %s\n",
- chan->video->name);
+ dev_err(chan->vi->dev,
+ "failed to register %s as /dev/video%d\n",
+ chan->video->name, chan->video_nr);
goto register_device_error;
}
@@ -374,6 +406,9 @@ static int tegra_vi_graph_notify_bound(struct v4l2_async_notifier *notifier,
dev_info(chan->vi->dev, "subdev %s bound\n", subdev->name);
entity->entity = &subdev->entity;
entity->subdev = subdev;
+ if (subdev->dev != NULL)
+ tegra_vi_graph_update_video_nr_from_devnode(chan,
+ subdev->dev->of_node);
chan->subdevs_bound++;
return 0;
}
diff --git a/nvidia-oot/include/media/mc_common.h b/nvidia-oot/include/media/mc_common.h
index 607f1f5fe..3a4b09438 100644
--- a/nvidia-oot/include/media/mc_common.h
+++ b/nvidia-oot/include/media/mc_common.h
@@ -227,6 +227,7 @@ struct tegra_channel {
unsigned int numlanes;
unsigned int io_id;
unsigned int num_subdevs;
+ int video_nr;
unsigned int sequence;
unsigned int saved_ctx_bypass;
unsigned int saved_ctx_pgmode;
Contact Us
For direct inquiries, please refer to the contact information available on our Contact page. Alternatively, you may complete and submit the form provided at the same link. We will respond to your request at our earliest opportunity.
Links to RidgeRun Resources and RidgeRun Artificial Intelligence Solutions can be found in the footer below.