After finish a camera .dts file and compiled it to .dtb file.
How can I check if the device is compatible with the device tree?
Hello @leo857366,
You need to have a driver that shared the same compatible in as your camera node in the dtb.
Then, the best test is to see if your driver actually loads and interacts with the camera sensor.
At what stage of development are you in right now?
regards,
Andrew
Embedded Software Engineer
I finished a driver and added the device to device tree.
But, after I load the driver to the kernel. I still can’t see the device at “/dev”.
I’m not sure if I made a mistake in the driver, the device tree, or both.
This why I want to check does I make some mistake at device tree first.
How should i do now for finding out the mistake?
If you want to check the content of the device tree running on the board, please check /proc/device-tree
I am assuming the driver is in the form of a module.
I will add that if the driver is for hardware, and if the driver can load, then it becomes possible for the “/dev
” to show up. If the driver cannot load, then either (A) the driver is not working with your kernel, or (B) the driver is not finding the hardware. Use lsmod
to see what drivers are loaded. You can use modprobe
or insmod
to insert the driver manually. Also, if you’ve updated a kernel module, then you are advised to run “sudo depmod -a
” once.
Hello @leo857366,
Thanks for getting back with more details.
If the device is not being exposed on /dev, there are multiple possible issues you could be facing.
But, no worries, we can try to get through this together.
Lets start with thw most basic question.
Is the driver loading ?
Please run the following command in order to see if your driver is actually trying to load:
dmesg | grep your device or driver name
For instnace, if we wew sebugging the imx415 driver, you could run
dmesg | grep imx415
And see what happens.
Please share the output with us.
And, is there is no output, just share whatever dmesg throws with no grep.
regards,
Andrew
Embedded Software Engineer
Hi @leo857366
There are many causes that produces that you can can’t see the device at “/dev”.
For example
- The node added in the device tree is not included correctly, or it is overwritten or it is disabled.
- The compatible of the node added doesn’t match with your driver.
- The driver is a sub-device and it is not “connected” correctly with other subdevices.
- …
Also there are other causes from the driver side.
I suggest to add a print message in the start and the end of the probe function to make sure the kernel is trying to load the driver, For example:
static struct i2c_driver cam_i2c_driver = {
. . .
.probe = cam_probe,
};
static int cam_probe(struct i2c_client *client)
{
dev_info(dev, "probing v4l2 sensor at addr 0x%0x\n", client->addr);
...
dev_dbg(dev, "detected imx219 sensor\n");
return 0;
}
Please share the kernel log. (dmesg
) if you can share your device tree it would be great.
Manuel Leiva
Embedded SW Engineer at RidgeRun
Contact us: support@ridgerun.com
Developers wiki: https://developer.ridgerun.com
Website: www.ridgerun.com
The device is an camera module that use FPGA combine a visible sensor and a thermal sensor.
$ sudo dmesg | grep gct
[ 8.319683] i2c i2c-9: Failed to register i2c client gct at 0x1a (-16)
[ 8.326414] i2c i2c-9: of_i2c: Failure registering /cam_i2cmux/i2c@0/nv_gct_a@1a
[ 8.334024] i2c i2c-9: Failed to create I2C device for /cam_i2cmux/i2c@0/nv_gct_a@1a
[ 8.347744] i2c i2c-10: Failed to register i2c client gct at 0x1a (-16)
[ 8.354564] i2c i2c-10: of_i2c: Failure registering /cam_i2cmux/i2c@1/nv_gct_c@1a
[ 8.362259] i2c i2c-10: Failed to create I2C device for /cam_i2cmux/i2c@1/nv_gct_c@1a
I haven’t load the driver yet. So, I think there are some problem at the device tree part.
I ask the manufacturer of the camera module to provide me a “.dtsi” file, but they seem like not familiar with device tree. They only told me some parameter like pix_clk_hz, active_w, active_h, etc.
This is the device tree.
tegra234-camera-gct.txt (6.3 KB)
/*
* Copyright (c) 2022-2023, NVIDIA CORPORATION. All rights reserved.
* Copyright (c) 2023, RidgeRun <support@ridgerun.com>. All rights reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/ {
tegra-capture-vi {
num-channels = <2>;
ports {
#address-cells = <1>;
#size-cells = <0>;
vi_port0: port@0 {
reg = <0>;
gct_vi_in0: endpoint {
status="okay";
port-index = <0>;
bus-width = <2>;
remote-endpoint = <&gct_csi_out0>;
};
};
vi_port1: port@1 {
reg = <1>;
gct_vi_in1: endpoint {
status="okay";
port-index = <2>;
bus-width = <2>;
remote-endpoint = <&gct_csi_out1>;
};
};
};
};
host1x@13e00000 {
nvcsi@15a00000 {
num-channels = <2>;
#address-cells = <1>;
#size-cells = <0>;
csi_chan0: channel@0 {
reg = <0>;
ports {
#address-cells = <1>;
#size-cells = <0>;
csi_chan0_port0: port@0 {
reg = <0>;
gct_csi_in0: endpoint@0 {
status = "okay";
port-index = <1>;
bus-width = <2>;
remote-endpoint = <&gct_out0>;
};
};
csi_chan0_port1: port@1 {
reg = <1>;
gct_csi_out0: endpoint@1 {
status = "okay";
remote-endpoint = <&gct_vi_in0>;
};
};
};
};
csi_chan1: channel@1 {
reg = <1>;
ports {
#address-cells = <1>;
#size-cells = <0>;
csi_chan1_port0: port@0 {
reg = <0>;
gct_csi_in1: endpoint@2 {
status = "okay";
port-index = <2>;
bus-width = <2>;
remote-endpoint = <&gct_out1>;
};
};
csi_chan1_port1: port@1 {
reg = <1>;
gct_csi_out1: endpoint@3 {
status = "okay";
remote-endpoint = <&gct_vi_in1>;
};
};
};
};
};
};
cam_i2cmux {
i2c_0:i2c@0 {
gct_cam0: nv_gct_a@1a {
status = "okay";
compatible = "nvidia,gct";
reg = <0x1a>;
devnode = "video0";
physical_w = "3.680";
physical_h = "2.760";
sensor_model = "fpga_gct";
use_sensor_mode_id = "true";
mode0 {
mclk_khz = "24000";
num_lanes = "2";
tegra_sinterface = "serial_a";
phy_mode = "DPHY";
discontinuous_clk = "no";
dpcm_enable = "false";
cil_settletime = "0";
lane_polarity = "6";
active_w = "1920";
active_h = "1080";
mode_type = "yuv";
pixel_phase = "uyvy";
csi_pixel_bit_depth = "16";
readout_orientation = "0";
line_length = "2200";
inherent_gain = "1";
pix_clk_hz = "74250000";
gain_factor = "16";
framerate_factor = "1000000";
exposure_factor = "1000000";
min_gain_val = "16";
max_gain_val = "256";
step_gain_val = "1";
default_gain = "16";
min_hdr_ratio = "1";
max_hdr_ratio = "1";
min_framerate = "30";
max_framerate = "30";
step_framerate = "1";
default_framerate = "14000000";
min_exp_time = "500";
max_exp_time = "65487";
step_exp_time = "1";
default_exp_time = "1600";
embedded_metadata_height = "4";
};
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
gct_out0: endpoint {
port-index = <0>;
bus-width = <2>;
remote-endpoint = <&gct_csi_in0>;
};
};
};
};
};
i2c_1: i2c@1 {
gct_cam1: nv_gct_c@1a {
status = "okay";
compatible = "nvidia,gct";
reg = <0x1a>;
devnode = "video1";
physical_w = "3.680";
physical_h = "2.760";
sensor_model = "fpga_gct";
use_sensor_mode_id = "true";
mode0 {
mclk_khz = "24000";
num_lanes = "2";
tegra_sinterface = "serial_a";
phy_mode = "DPHY";
discontinuous_clk = "no";
dpcm_enable = "false";
cil_settletime = "0";
lane_polarity = "6";
active_w = "1920";
active_h = "1080";
mode_type = "yuv";
pixel_phase = "uyvy";
csi_pixel_bit_depth = "16";
readout_orientation = "0";
line_length = "2200";
inherent_gain = "1";
pix_clk_hz = "74250000";
gain_factor = "16";
framerate_factor = "1000000";
exposure_factor = "1000000";
min_gain_val = "16";
max_gain_val = "256";
step_gain_val = "1";
default_gain = "16";
min_hdr_ratio = "1";
max_hdr_ratio = "1";
min_framerate = "30";
max_framerate = "30";
step_framerate = "1";
default_framerate = "14000000";
min_exp_time = "500";
max_exp_time = "65487";
step_exp_time = "1";
default_exp_time = "1600";
embedded_metadata_height = "4";
};
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
gct_out1: endpoint {
status = "okay";
port-index = <2>;
bus-width = <2>;
remote-endpoint = <&gct_csi_in1>;
};
};
};
};
};
};
};
/ {
tegra-camera-platform {
compatible = "nvidia, tegra-camera-platform";
num_csi_lanes = <4>;
max_lane_speed = <2500000>;
min_bits_per_pixel = <10>;
vi_peak_byte_per_pixel = <2>;
vi_bw_margin_pct = <25>;
max_pixel_rate = <7500000>;
isp_peak_byte_per_pixel = <5>;
isp_bw_margin_pct = <25>;
modules {
cam_module0: module0 {
status = "okay";
badge = "camera_front_GCT";
position = "front";
orientation = "1";
cam_module0_drivernode0: drivernode0 {
status = "okay";
pcl_id = "v4l2_sensor";
devname = "gct 9-001a";
proc-device-tree = "/proc/device-tree/cam_i2cmux/i2c@0/nv_gct_a@1a";
};
};
cam_module1: module1 {
badge = "camera_rear_GCT";
position = "rear";
orientation = "1";
cam_module1_drivernode0: drivernode0 {
status = "okay";
pcl_id = "v4l2_sensor";
devname = "gct 10-001a";
proc-device-tree = "/proc/device-tree/cam_i2cmux/i2c@1/nv_gct_c@1a";
};
};
};
};
};
Hello @leo857366,
Thanks for sharing the extra details.
Seems to be an issue while registering the i2c device, therefore, the driver is not even able to communicate with the sensor.
What do you see if you run:
i2cdetect -y -r 9
Do you see your device connected to the bus at address 1a ?
You could also check other buses to see if the device shows.
It might also be that the original driver was developed by the manufacturer for a different Jetson board or Jetpack version, and it might need porting to support your system.
Could you provide information on what board and Jetpack version are you using ?
Also, if you could share the driver .c file would be also helpful.
regards,
Andrew
Embedded Software Engineer
Jetson Orin Nano, with Jetpack 5.1.1
$ sudo i2cdetect -y -r 9
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
$ sudo i2cdetect -y -r 10
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
I refer this topic, and fix the code. And I ignore gain, exposure, etc. first.
This is the driver .c code.
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/gpio.h>
#include <linux/module.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <media/tegra-v4l2-camera.h>
#include <media/tegracam_core.h>
//#include <media/gct.h>
#include "gct_mode_tbls.h"
#define CREATE_TRACE_POINTS
#define GCT_MAX_COARSE_DIFF 6
#define GCT_MAX_FRAME_LENGTH (0x7fff)
#define GCT_MIN_EXPOSURE_COARSE (0x0002)
#define GCT_MAX_EXPOSURE_COARSE \
(GCT_MAX_FRAME_LENGTH-GCT_MAX_COARSE_DIFF)
#define GCT_DEFAULT_LINE_LENGTH (0xA80)
#define GCT_DEFAULT_PIXEL_CLOCK (160)
#define GCT_DEFAULT_FRAME_LENGTH (0x07C0)
#define GCT_DEFAULT_EXPOSURE_COARSE \
(GCT_DEFAULT_FRAME_LENGTH-GCT_MAX_COARSE_DIFF)
//NEW DEFINE
#define GCT_FRAME_LENGTH_ADDR_MSB 0x0000
#define GCT_FRAME_LENGTH_ADDR_LSB 0x0000
#define GCT_COARSE_TIME_ADDR_1 0x0000
#define GCT_COARSE_TIME_ADDR_2 0x0000
#define GCT_COARSE_TIME_ADDR_3 0x0000
#define GCT_COARSE_TIME_SHORT_ADDR_1 0x0000
#define GCT_COARSE_TIME_SHORT_ADDR_2 0x0000
#define GCT_COARSE_TIME_SHORT_ADDR_3 0x0000
#define GCT_GAIN_ADDR_MSB 0x0000
#define GCT_GAIN_ADDR_LSB 0x0000
#define GCT_GROUP_HOLD_ADDR 0x0000
static const u32 ctrl_cid_list[] = {
TEGRA_CAMERA_CID_GAIN,
TEGRA_CAMERA_CID_EXPOSURE,
TEGRA_CAMERA_CID_EXPOSURE_SHORT,
TEGRA_CAMERA_CID_FRAME_RATE,
TEGRA_CAMERA_CID_GROUP_HOLD,
TEGRA_CAMERA_CID_HDR_EN,
};
struct gct {
struct i2c_client *i2c_client;
struct v4l2_subdev *subdev;
const char *devname;
struct mutex streaming_lock;
bool streaming;
s32 group_hold_prev;
u32 frame_length;
bool group_hold_en;
struct camera_common_i2c i2c_dev;
struct camera_common_data *s_data;
struct tegracam_device *tc_dev;
};
static struct regmap_config gct_regmap_config = {
.reg_bits = 16,
.val_bits = 8,
};
static inline void gct_get_frame_length_regs(gct_reg *regs,
u32 frame_length)
{
regs->addr = GCT_FRAME_LENGTH_ADDR_MSB;
regs->val = (frame_length >> 8) & 0xff;
(regs + 1)->addr = GCT_FRAME_LENGTH_ADDR_LSB;
(regs + 1)->val = (frame_length) & 0xff;
}
static inline void gct_get_coarse_time_regs(gct_reg *regs,
u32 coarse_time)
{
regs->addr = GCT_COARSE_TIME_ADDR_1;
regs->val = (coarse_time >> 12) & 0xff;
(regs + 1)->addr = GCT_COARSE_TIME_ADDR_2;
(regs + 1)->val = (coarse_time >> 4) & 0xff;
(regs + 2)->addr = GCT_COARSE_TIME_ADDR_3;
(regs + 2)->val = (coarse_time & 0xf) << 4;
}
static inline void gct_get_coarse_time_short_regs(gct_reg *regs,
u32 coarse_time)
{
regs->addr = GCT_COARSE_TIME_SHORT_ADDR_1;
regs->val = (coarse_time >> 12) & 0xff;
(regs + 1)->addr = GCT_COARSE_TIME_SHORT_ADDR_2;
(regs + 1)->val = (coarse_time >> 4) & 0xff;
(regs + 2)->addr = GCT_COARSE_TIME_SHORT_ADDR_3;
(regs + 2)->val = (coarse_time & 0xf) << 4;
}
static inline void gct_get_gain_regs(gct_reg *regs,
u16 gain)
{
regs->addr = GCT_GAIN_ADDR_MSB;
regs->val = (gain >> 8) & 0xff;
(regs + 1)->addr = GCT_GAIN_ADDR_LSB;
(regs + 1)->val = (gain) & 0xff;
}
static int test_mode;
module_param(test_mode, int, 0644);
static int gct_write_reg(struct camera_common_data *s_data, u16 addr, u8 val)
{
return 0;
}
static int gct_write_table(struct gct *priv,
const gct_reg table[])
{
return 0;
}
static int gct_power_on(struct camera_common_data *s_data)
{
struct camera_common_power_rail *pw = s_data->power;
pw->state = SWITCH_ON;
return 0;
}
static int gct_power_off(struct camera_common_data *s_data)
{
struct camera_common_power_rail *pw = s_data->power;
pw->state = SWITCH_OFF;
return 0;
}
static int gct_power_put(struct tegracam_device *tc_dev)
{
return 0;
}
static int gct_power_get(struct tegracam_device *tc_dev)
{
struct camera_common_data *s_data = tc_dev->s_data;
struct camera_common_power_rail *pw = s_data->power;
pw->state = SWITCH_OFF;
return 0;
}
static int gct_set_gain(struct tegracam_device *tc_dev, s64 val);
static int gct_set_frame_rate(struct tegracam_device *tc_dev, s64 val);
static int gct_set_exposure(struct tegracam_device *tc_dev, s64 val);
static int gct_set_exposure_short(struct tegracam_device *tc_dev, s64 val);
static const struct of_device_id gct_of_match[] = {
{
.compatible = "nvidia,gct",
},
{ },
};
static int gct_set_group_hold(struct tegracam_device *tc_dev, bool val)
{
int err;
struct gct *priv = tc_dev->priv;
int gh_prev = switch_ctrl_qmenu[priv->group_hold_prev];
struct device *dev = tc_dev->dev;
if (priv->group_hold_en == true && gh_prev == SWITCH_OFF) {
camera_common_i2c_aggregate(&priv->i2c_dev, true);
/* enter group hold */
err = gct_write_reg(priv->s_data,
GCT_GROUP_HOLD_ADDR, val);
if (err)
goto fail;
priv->group_hold_prev = 1;
dev_info(dev, "%s: enter group hold\n", __func__);
} else if (priv->group_hold_en == false && gh_prev == SWITCH_ON) {
/* leave group hold */
err = gct_write_reg(priv->s_data,
GCT_GROUP_HOLD_ADDR, 0x11);
if (err)
goto fail;
err = gct_write_reg(priv->s_data,
GCT_GROUP_HOLD_ADDR, 0x61);
if (err)
goto fail;
priv->group_hold_prev = 0;
dev_info(dev, "%s: leave group hold\n", __func__);
}
return 0;
fail:
dev_info(dev, "%s: Group hold control error\n", __func__);
return err;
}
static int gct_set_gain(struct tegracam_device *tc_dev, s64 val)
{
struct camera_common_data *s_data = tc_dev->s_data;
struct gct *priv = (struct gct *)tc_dev->priv;
struct device *dev = tc_dev->dev;
const struct sensor_mode_properties *mode =
&s_data->sensor_props.sensor_modes[s_data->mode_prop_idx];
gct_reg reg_list[2];
int err;
u16 gain;
int i;
if (!priv->group_hold_prev)
gct_set_group_hold(tc_dev, 1);
/* translate value */
gain = (u16) (((val * 16) +
(mode->control_properties.gain_factor / 2)) /
mode->control_properties.gain_factor);
gct_get_gain_regs(reg_list, gain);
dev_info(dev, "%s: gain %d val: %lld\n", __func__, gain, val);
for (i = 0; i < 2; i++) {
err = gct_write_reg(s_data, reg_list[i].addr,
reg_list[i].val);
if (err)
goto fail;
}
return 0;
fail:
dev_info(dev, "%s: GAIN control error\n", __func__);
return err;
}
static int gct_set_frame_rate(struct tegracam_device *tc_dev, s64 val)
{
struct camera_common_data *s_data = tc_dev->s_data;
struct device *dev = tc_dev->dev;
struct gct *priv = tc_dev->priv;
const struct sensor_mode_properties *mode =
&s_data->sensor_props.sensor_modes[s_data->mode_prop_idx];
gct_reg reg_list[2];
int err;
u32 frame_length;
int i;
if (!priv->group_hold_prev)
gct_set_group_hold(tc_dev, 1);
frame_length = mode->signal_properties.pixel_clock.val *
mode->control_properties.framerate_factor /
mode->image_properties.line_length / val;
gct_get_frame_length_regs(reg_list, frame_length);
dev_info(dev, "%s: val: %d\n", __func__, frame_length);
for (i = 0; i < 2; i++) {
err = gct_write_reg(s_data, reg_list[i].addr,
reg_list[i].val);
if (err)
goto fail;
}
priv->frame_length = frame_length;
return 0;
fail:
dev_info(dev, "%s: FRAME_LENGTH control error\n", __func__);
return err;
}
static int gct_set_exposure(struct tegracam_device *tc_dev, s64 val)
{
struct camera_common_data *s_data = tc_dev->s_data;
struct device *dev = tc_dev->dev;
struct gct *priv = tc_dev->priv;
const s32 max_coarse_time = priv->frame_length - GCT_MAX_COARSE_DIFF;
const struct sensor_mode_properties *mode =
&s_data->sensor_props.sensor_modes[s_data->mode_prop_idx];
gct_reg reg_list[3];
int err;
u32 coarse_time;
int i;
if (!priv->group_hold_prev)
gct_set_group_hold(tc_dev, 1);
coarse_time = (u32)(((mode->signal_properties.pixel_clock.val*val)
/mode->image_properties.line_length)/
mode->control_properties.exposure_factor);
if (coarse_time < GCT_MIN_EXPOSURE_COARSE)
coarse_time = GCT_MIN_EXPOSURE_COARSE;
else if (coarse_time > max_coarse_time)
coarse_time = max_coarse_time;
gct_get_coarse_time_regs(reg_list, coarse_time);
dev_info(dev, "%s: val: %d\n", __func__, coarse_time);
for (i = 0; i < 3; i++) {
err = gct_write_reg(s_data, reg_list[i].addr,
reg_list[i].val);
if (err)
goto fail;
}
return 0;
fail:
dev_info(dev, "%s: COARSE_TIME control error\n", __func__);
return err;
}
static int gct_set_exposure_short(struct tegracam_device *tc_dev, s64 val)
{
struct camera_common_data *s_data = tc_dev->s_data;
struct device *dev = tc_dev->dev;
struct gct *priv = tc_dev->priv;
const struct sensor_mode_properties *mode =
&s_data->sensor_props.sensor_modes[s_data->mode_prop_idx];
gct_reg reg_list[3];
int err;
struct v4l2_control hdr_control;
int hdr_en;
u32 coarse_time_short;
int i;
if (!priv->group_hold_prev)
gct_set_group_hold(tc_dev, 1);
/* check hdr enable ctrl */
hdr_control.id = TEGRA_CAMERA_CID_HDR_EN;
err = camera_common_g_ctrl(s_data, &hdr_control);
if (err < 0) {
dev_err(dev, "could not find device ctrl.\n");
return err;
}
hdr_en = switch_ctrl_qmenu[hdr_control.value];
if (hdr_en == SWITCH_OFF)
return 0;
coarse_time_short = (u32)(((mode->signal_properties.pixel_clock.val*val)
/mode->image_properties.line_length)
/mode->control_properties.exposure_factor);
gct_get_coarse_time_short_regs(reg_list, coarse_time_short);
dev_info(dev, "%s: val: %d\n", __func__, coarse_time_short);
for (i = 0; i < 3; i++) {
err = gct_write_reg(s_data, reg_list[i].addr,
reg_list[i].val);
if (err)
goto fail;
}
return 0;
fail:
dev_info(dev, "%s: COARSE_TIME_SHORT control error\n", __func__);
return err;
}
MODULE_DEVICE_TABLE(of, gct_of_match);
static struct camera_common_pdata *gct_parse_dt(struct tegracam_device
*tc_dev)
{
struct device *dev = tc_dev->dev;
struct device_node *node = dev->of_node;
struct camera_common_pdata *board_priv_pdata;
const struct of_device_id *match;
int err;
if (!node)
return NULL;
match = of_match_device(gct_of_match, dev);
if (!match) {
dev_err(dev, "Failed to find matching dt id\n");
return NULL;
}
board_priv_pdata = devm_kzalloc(dev,
sizeof(*board_priv_pdata), GFP_KERNEL);
if (!board_priv_pdata)
return NULL;
err = camera_common_parse_clocks(dev,
board_priv_pdata);
if (err) {
dev_err(dev, "Failed to find clocks\n");
}
return board_priv_pdata;
}
static int gct_set_mode(struct tegracam_device *tc_dev)
{
struct gct *priv = (struct gct *)tegracam_get_privdata(tc_dev);
struct camera_common_data *s_data = tc_dev->s_data;
int err;
err = gct_write_table(priv, mode_table[s_data->mode_prop_idx]);
if (err)
return err;
return 0;
}
static int gct_start_streaming(struct tegracam_device *tc_dev)
{
struct gct *priv = (struct gct *)tegracam_get_privdata(tc_dev);
mutex_lock(&priv->streaming_lock);
priv->streaming = true;
mutex_unlock(&priv->streaming_lock);
return 0;
}
static int gct_stop_streaming(struct tegracam_device *tc_dev)
{
struct gct *priv = (struct gct *)tegracam_get_privdata(tc_dev);
u32 frame_time;
mutex_lock(&priv->streaming_lock);
priv->streaming = false;
mutex_unlock(&priv->streaming_lock);
/*
* Wait for one frame to make sure sensor is set to
* software standby in V-blank
*
* frame_time = frame length rows * Tline
* Tline = line length / pixel clock (in MHz)
*/
frame_time = priv->frame_length *
GCT_DEFAULT_LINE_LENGTH / GCT_DEFAULT_PIXEL_CLOCK;
usleep_range(frame_time, frame_time + 1000);
return 0;
}
static struct camera_common_sensor_ops gct_common_ops = {
.numfrmfmts = ARRAY_SIZE(gct_frmfmt),
.frmfmt_table = gct_frmfmt,
.power_on = gct_power_on,
.power_off = gct_power_off,
.write_reg = gct_write_reg,
.parse_dt = gct_parse_dt,
.power_get = gct_power_get,
.power_put = gct_power_put,
.set_mode = gct_set_mode,
.start_streaming = gct_start_streaming,
.stop_streaming = gct_stop_streaming,
};
static struct tegracam_ctrl_ops gct_ctrl_ops = {
.numctrls = ARRAY_SIZE(ctrl_cid_list),
.ctrl_cid_list = ctrl_cid_list,
.set_gain = gct_set_gain,
.set_exposure = gct_set_exposure,
.set_exposure_short = gct_set_exposure_short,
.set_frame_rate = gct_set_frame_rate,
.set_group_hold = gct_set_group_hold,
};
static int gct_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
dev_info(&client->dev, "%s:\n", __func__);
return 0;
}
static const struct v4l2_subdev_internal_ops gct_subdev_internal_ops = {
.open = gct_open,
};
static int gct_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
struct device_node *node = client->dev.of_node;
struct tegracam_device *tc_dev;
struct gct *priv;
int err;
const struct of_device_id *match;
dev_info(dev, "probing v4l2 sensor no i2c.\n");
match = of_match_device(gct_of_match, dev);
if (!match) {
dev_err(dev, "No device match found\n");
return -ENODEV;
}
if (!IS_ENABLED(CONFIG_OF) || !node)
return -EINVAL;
priv = devm_kzalloc(dev,
sizeof(struct gct), GFP_KERNEL);
if (!priv)
return -ENOMEM;
tc_dev = devm_kzalloc(dev,
sizeof(struct tegracam_device), GFP_KERNEL);
if (!tc_dev)
return -ENOMEM;
priv->i2c_client = tc_dev->client = client;
tc_dev->dev = dev;
strncpy(tc_dev->name, "gct", sizeof(tc_dev->name));
tc_dev->dev_regmap_config = &gct_regmap_config;
tc_dev->sensor_ops = &gct_common_ops;
tc_dev->v4l2sd_internal_ops = &gct_subdev_internal_ops;
tc_dev->tcctrl_ops = &gct_ctrl_ops;
err = tegracam_device_register(tc_dev);
if (err) {
dev_err(dev, "tegra camera driver registration failed\n");
return err;
}
priv->tc_dev = tc_dev;
priv->s_data = tc_dev->s_data;
priv->subdev = &tc_dev->s_data->subdev;
tegracam_set_privdata(tc_dev, (void *)priv);
mutex_init(&priv->streaming_lock);
err = tegracam_v4l2subdev_register(tc_dev, true);
if (err) {
dev_err(dev, "tegra camera subdev registration failed\n");
return err;
}
dev_info(dev, "Detected GCT sensor\n");
return 0;
}
static int
gct_remove(struct i2c_client *client)
{
struct camera_common_data *s_data = to_camera_common_data(&client->dev);
struct gct *priv = (struct gct *)s_data->priv;
tegracam_v4l2subdev_unregister(priv->tc_dev);
gct_power_put(priv->tc_dev);
tegracam_device_unregister(priv->tc_dev);
mutex_destroy(&priv->streaming_lock);
return 0;
}
static const struct i2c_device_id gct_id[] = {
{ "gct", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, gct_id);
static struct i2c_driver gct_i2c_driver = {
.driver = {
.name = "gct",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(gct_of_match),
},
.probe = gct_probe,
.remove = gct_remove,
.id_table = gct_id,
};
module_i2c_driver(gct_i2c_driver);
MODULE_DESCRIPTION("Media Controller driver for OmniVision GCT");
MODULE_AUTHOR("NVIDIA Corporation");
MODULE_LICENSE("GPL v2");
By the way, this is dmesg about after I load .ko into kernel.
[ 7254.938743] nv_gct: module verification failed: signature and/or required key missing - tainting kernel
Hello @leo857366,
Thanks for sharing the outputs of the commands.
Lest go step by step.
- Going back to the i2c i2c-9: Failed to register i2c client gct at 0x1a (-16) error you reported on your dmesg output a while ago is interesting because it seems to report that the i2c devices for each one of the nodes in the DTB are not being registered due to the device or resource being busy according to the error number
#define EBUSY 16 /* Device or resource busy */
. - If you run the i2cdetect command you are not seeing anything on those busses, not a device nor a driver making use of the i2c addresses. The sensor might not be showing just because it might powered off, given that usually sensors are powered on demand by the driver. However, it seems like the driver is not even loading, given that we would expect the addresses owned by the driver be marked with UU in the i2cdected output.
- This all leads us to the newest error message you are seeing in dmesg about the module verification failed. This seems to be shown when the board is configured to use secure boot and the module is not compiled with the proper signature so that it is deemed secure for the system. In some cases, this message is shown and the driver still loads, but in your case, the driver seems to be completely flailing to load.
Now, a couple of questions:
- Are you aware if you are running secure boot or no ?
- Could you share the full output of dmesg.
- You mentioned that the camera manufacturer did not provide a dtsi file, if that is the case, how did you manage to come up with the one you shared before? Did you create it yourself ?
regards,
Andrew
Embedded Software Engineer
The first thing you should try is be able to detect your camera address on bus 9&10
We need to know we can’t see the camera address.
Do you know if the camera required something to be enabled, for example, clock, Power/reset GPIO, voltage regulator, etc
I have seen the driver doesn’t enable any clock, regulator or GPIO, so if some of them are required, they need to be enabled manually or need to be added to the driver.
Make sure that your i2c mux is configured correctly, In some part of your DT you need to have the cam_i2cmux node configuration, Similar to this example:
cam_i2cmux{
status = "okay";
compatible = "i2c-mux-gpio";
#address-cells = <1>;
#size-cells = <0>;
i2c-parent = <&cam_i2c>;
mux-gpios = <&gpio_aon CAM_I2C_MUX GPIO_ACTIVE_HIGH>;
i2c@0 {
status = "okay";
reg = <0>;
#address-cells = <1>;
#size-cells = <0>;
rbpcv2_imx219_a@10 {
reset-gpios = <&gpio CAM0_PWDN GPIO_ACTIVE_HIGH>;
};
};
i2c@1 {
status = "okay";
reg = <1>;
#address-cells = <1>;
#size-cells = <0>;
rbpcv2_imx219_c@10 {
reset-gpios = <&gpio CAM1_PWDN GPIO_ACTIVE_HIGH>;
};
};
};
Manuel Leiva
Embedded SW Engineer at RidgeRun
Contact us: support@ridgerun.com
Developers wiki: https://developer.ridgerun.com
Website: www.ridgerun.com
Is the FPGA on the PCI bus? If so, then you have to fully boot the FPGA prior to starting the Jetson. The hot plug and late detection on the PCI is normally disabled to conserve power on Jetsons. If for any reason the Jetson boots up past PCI detection, and if the FPGA isn’t quite finished booting, then it will not be detected. Any USB serial UART would be ok because it is hot plug. The target to the serial UART itself may not be there fast enough.