Hello NVIDIA Developers,
I would like to be able to use a Jetson AGX Xavier running L4T 32.4.4 to read a 4-lane MIPI CSI-2 without I2C from a Xilinx FGPA. The video is 1920 x 1080, 30fps, YUV444. The MIPI signals are sent from the FMC block of the FPGA to a Leopard Imaging LI-JXAV-MIPI-ADPT-4CAM adapter (https://www.leopardimaging.com/product/accessories/adapters-carrier-boards/li-jxav-mipi-adpt-4cam/).
Only one of the connectors on the Leopard board (J1) is being utilized during this phase of development. Multiple “cameras” will later be connected to the Leopard board, requiring enhancement to the dtsi files (e.g., similar to e3333 configuration). The electrical signals have been probed at the MIPI connector on the bottom of the Leopard board, and have been found to be good to the point where it makes physical contact with the Xavier. When the Leopard board is connected to the Xavier, i2cdetect -y -a -r 2
shows responses on addresses 0x54 and 0x70, but i2cdump
on those addresses doesn’t seem to output anything meaningful. The i2cdetect and i2cdump commands yield the same respective results when the Leopard board while it is still connected to the Xavier, but disconnected from the FPGA.
The primary problem at this point is that the Xavier cannot detect /dev/video0.
I have gone through the NVIDIA development documentation (Sensor Software Driver Programming Guide, @JerryChang tutorial videos, etc.), and browsed through similar forum threads without success. Forum thread Xavier - using raw CSI without i2c appears to be the most similar problem, as my driver development was also initially based on the imx185 sensor (without success), but have since abandoned it in favor of using the ov5693 sensor files as my template after reading suggestions from @ShaneCCC.
As an aside, I pulled an ov5693 camera from a TX2 and captured streams successfully on the Xavier, so there shouldn’t be anything wrong with the pins on the Xavier MIPI port, or any fundamental problems with the Xavier itself.
Here is the recipe I last used to try to get this to work.
1. Edit tegra194-camera-e3326-a00.dtsi.
Only one mode is being used, which is set to 1920 x 1080 resolution.
/*
* Copyright (c) 2015-2019, NVIDIA CORPORATION. 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/>.
*/
/ {
host1x {
vi@15c10000 {
num-channels = <1>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
e3326_vi_in0: endpoint {
port-index = <2>;
bus-width = <4>;
remote-endpoint = <&e3326_csi_out0>;
};
};
};
};
nvcsi@15a00000 {
num-channels = <1>;
#address-cells = <1>;
#size-cells = <0>;
status = "okay";
channel@0 {
reg = <0>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
e3326_csi_in0: endpoint@0 {
port-index = <2>;
bus-width = <4>;
remote-endpoint = <&e3326_ov5693_out0>;
};
};
port@1 {
reg = <1>;
e3326_csi_out0: endpoint@1 {
remote-endpoint = <&e3326_vi_in0>;
};
};
};
};
};
};
i2c@3180000 {
ov5693_c@36 {
compatible = "nvidia,ov5693";
/* I2C device address */
reg = <0x36>;
/* V4L2 device node location */
devnode = "video0";
/* Physical dimensions of sensor */
physical_w = "3.674";
physical_h = "2.738";
/* Define any required hw resources needed by driver */
/* ie. clocks, io pins, power sources */
avdd-reg = "vana";
iovdd-reg = "vif";
/* Sensor output flip settings */
vertical-flip = "true";
/**
* A modeX node is required to support v4l2 driver
* implementation with NVIDIA camera software stack
*
* mclk_khz = "";
* Standard MIPI driving clock, typically 24MHz
*
* num_lanes = "";
* Number of lane channels sensor is programmed to output
*
* tegra_sinterface = "";
* The base tegra serial interface lanes are connected to
* Incase of virtual HW devices, use virtual
* For SW emulated devices, use host
*
* phy_mode = "";
* PHY mode used by the MIPI lanes for this device
*
* discontinuous_clk = "";
* The sensor is programmed to use a discontinuous clock on MIPI lanes
*
* dpcm_enable = "true";
* The sensor is programmed to use a DPCM modes
*
* cil_settletime = "";
* MIPI lane settle time value.
* A "0" value attempts to autocalibrate based on mclk_multiplier
*
*
*
*
* active_w = "";
* Pixel active region width
*
* active_h = "";
* Pixel active region height
*
* pixel_t = "";
* The sensor readout pixel pattern
*
* readout_orientation = "0";
* Based on camera module orientation.
* Only change readout_orientation if you specifically
* Program a different readout order for this mode
*
* line_length = "";
* Pixel line length (width) for sensor mode.
* This is used to calibrate features in our camera stack.
*
* mclk_multiplier = "";
* Multiplier to MCLK to help time hardware capture sequence
* TODO: Assign to PLL_Multiplier as well until fixed in core
*
* pix_clk_hz = "";
* Sensor pixel clock used for calculations like exposure and framerate
*
*
*
*
* inherent_gain = "";
* Gain obtained inherently from mode (ie. pixel binning)
*
* == Source Control Settings ==
*
* Gain factor used to convert fixed point integer to float
* Gain range [min_gain/gain_factor, max_gain/gain_factor]
* Gain step [step_gain/gain_factor is the smallest step that can be configured]
* Default gain [Default gain to be initialized for the control.
* use min_gain_val as default for optimal results]
* Framerate factor used to convert fixed point integer to float
* Framerate range [min_framerate/framerate_factor, max_framerate/framerate_factor]
* Framerate step [step_framerate/framerate_factor is the smallest step that can be configured]
* Default Framerate [Default framerate to be initialized for the control.
* use max_framerate to get required performance]
* Exposure factor used to convert fixed point integer to float
* For convenience use 1 sec = 1000000us as conversion factor
* Exposure range [min_exp_time/exposure_factor, max_exp_time/exposure_factor]
* Exposure step [step_exp_time/exposure_factor is the smallest step that can be configured]
* Default Exposure Time [Default exposure to be initialized for the control.
* Set default exposure based on the default_framerate for optimal exposure settings]
*
* gain_factor = ""; (integer factor used for floating to fixed point conversion)
* min_gain_val = ""; (ceil to integer)
* max_gain_val = ""; (ceil to integer)
* step_gain_val = ""; (ceil to integer)
* default_gain = ""; (ceil to integer)
* Gain limits for mode
*
* exposure_factor = ""; (integer factor used for floating to fixed point conversion)
* min_exp_time = ""; (ceil to integer)
* max_exp_time = ""; (ceil to integer)
* step_exp_time = ""; (ceil to integer)
* default_exp_time = ""; (ceil to integer)
* Exposure Time limits for mode (us)
*
*
* min_hdr_ratio = "";
* max_hdr_ratio = "";
* HDR Ratio limits for mode
*
* framerate_factor = ""; (integer factor used for floating to fixed point conversion)
* min_framerate = "";
* max_framerate = "";
* step_framerate = ""; (ceil to integer)
* default_framerate = ""; (ceil to integer)
* Framerate limits for mode (fps)
*/
mode0 { // OV5693_MODE_2592X1944
mclk_khz = "24000";
num_lanes = "4";
tegra_sinterface = "serial_c";
phy_mode = "DPHY";
discontinuous_clk = "yes";
dpcm_enable = "false";
cil_settletime = "0";
active_w = "1920";
active_h = "1080";
mode_type = "bayer";
pixel_phase = "rggb";
csi_pixel_bit_depth = "8";
readout_orientation = "0";
line_length = "2688";
inherent_gain = "1";
mclk_multiplier = "6.67";
pix_clk_hz = "160000000";
gain_factor = "10";
min_gain_val = "10";/* 1DB*/
max_gain_val = "160";/* 16DB*/
step_gain_val = "1";
default_gain = "10";
min_hdr_ratio = "1";
max_hdr_ratio = "1";
framerate_factor = "1000000";
min_framerate = "1816577";/*1.816577 */
max_framerate = "30000000";/*30*/
step_framerate = "1";
default_framerate = "30000000";
exposure_factor = "1000000";
min_exp_time = "34";/* us */
max_exp_time = "550385";/* us */
step_exp_time = "1";
default_exp_time = "33334";/* us */
embedded_metadata_height = "0";
};
// mode1 { //OV5693_MODE_2592X1458
// mclk_khz = "24000";
// num_lanes = "2";
// tegra_sinterface = "serial_c";
// phy_mode = "DPHY";
// discontinuous_clk = "yes";
// dpcm_enable = "false";
// cil_settletime = "0";
//
// active_w = "2592";
// active_h = "1458";
// mode_type = "bayer";
// pixel_phase = "bggr";
// csi_pixel_bit_depth = "10";
// readout_orientation = "0";
// line_length = "2688";
// inherent_gain = "1";
// mclk_multiplier = "6.67";
// pix_clk_hz = "160000000";
//
// gain_factor = "10";
// min_gain_val = "10";/* 1DB*/
// max_gain_val = "160";/* 16DB*/
// step_gain_val = "1";
// default_gain = "10";
// min_hdr_ratio = "1";
// max_hdr_ratio = "1";
// framerate_factor = "1000000";
// min_framerate = "1816577";/*1.816577 */
// max_framerate = "30000000";/*30*/
// step_framerate = "1";
// default_framerate = "30000000";
// exposure_factor = "1000000";
// min_exp_time = "34";/* us */
// max_exp_time = "550385";/* us */
// step_exp_time = "1";
// default_exp_time = "33334";/* us */
// embedded_metadata_height = "0";
// };
//
// mode2 { //OV5693_MODE_1280X720
// mclk_khz = "24000";
// num_lanes = "2";
// tegra_sinterface = "serial_c";
// phy_mode = "DPHY";
// discontinuous_clk = "yes";
// dpcm_enable = "false";
// cil_settletime = "0";
//
// active_w = "1280";
// active_h = "720";
// mode_type = "bayer";
// pixel_phase = "bggr";
// csi_pixel_bit_depth = "10";
// readout_orientation = "0";
// line_length = "1752";
// inherent_gain = "1";
// mclk_multiplier = "6.67";
// pix_clk_hz = "160000000";
//
// gain_factor = "10";
// min_gain_val = "10";/* 1DB*/
// max_gain_val = "160";/* 16DB*/
// step_gain_val = "1";
// default_gain = "10";
// min_hdr_ratio = "1";
// max_hdr_ratio = "1";
// framerate_factor = "1000000";
// min_framerate = "2787078";/* 2.787078 */
// max_framerate = "120000000";/* 120*/
// step_framerate = "1";
// default_framerate = "120000000";
// exposure_factor = "1000000";
// min_exp_time = "22";/* us */
// max_exp_time = "358733";/* us */
// step_exp_time = "1";
// default_exp_time = "8334";/* us */
// embedded_metadata_height = "0";
// };
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
e3326_ov5693_out0: endpoint {
port-index = <2>;
bus-width = <4>;
remote-endpoint = <&e3326_csi_in0>;
};
};
};
};
};
e3326_lens_ov5693@P5V27C {
min_focus_distance = "0.0";
hyper_focal = "0.0";
focal_length = "2.67";
f_number = "2.0";
aperture = "2.0";
};
tegra-camera-platform {
compatible = "nvidia, tegra-camera-platform";
/**
* Physical settings to calculate max ISO BW
*
* num_csi_lanes = <>;
* Total number of CSI lanes when all cameras are active
*
* max_lane_speed = <>;
* Max lane speed in Kbit/s
*
* min_bits_per_pixel = <>;
* Min bits per pixel
*
* vi_peak_byte_per_pixel = <>;
* Max byte per pixel for the VI ISO case
*
* vi_bw_margin_pct = <>;
* Vi bandwidth margin in percentage
*
* max_pixel_rate = <>;
* Max pixel rate in Kpixel/s for the ISP ISO case
*
* isp_peak_byte_per_pixel = <>;
* Max byte per pixel for the ISP ISO case
*
* isp_bw_margin_pct = <>;
* Isp bandwidth margin in percentage
*/
num_csi_lanes = <4>;
max_lane_speed = <1500000>;
min_bits_per_pixel = <8>;
vi_peak_byte_per_pixel = <2>;
vi_bw_margin_pct = <25>;
max_pixel_rate = <160000>;
isp_peak_byte_per_pixel = <5>;
isp_bw_margin_pct = <25>;
/**
* The general guideline for naming badge_info contains 3 parts, and is as follows,
* The first part is the camera_board_id for the module; if the module is in a FFD
* platform, then use the platform name for this part.
* The second part contains the position of the module, ex. “rear” or “front”.
* The third part contains the last 6 characters of a part number which is found
* in the module's specsheet from the vender.
*/
modules {
module0 {
badge = "e3326_front_P5V27C";
position = "rear";
orientation = "1";
drivernode0 {
/* Declare PCL support driver (classically known as guid) */
pcl_id = "v4l2_sensor";
/* Driver v4l2 device name */
devname = "ov5693 2-0036";
/* Declare the device-tree hierarchy to driver instance */
proc-device-tree = "/proc/device-tree/i2c@3180000/ov5693_c@36";
};
drivernode1 {
/* Declare PCL support driver (classically known as guid) */
pcl_id = "v4l2_lens";
proc-device-tree = "/proc/device-tree/e3326_lens_ov5693@P5V27C/";
};
};
};
};
};
2. Edit tegra194-p2822-0000-camera-e3326-a00.dtsi
Edits to this file were very minimal, adding just one line of status = “okay”. This file is structured differently than the camera modules file that was originally used from the imx185 template, which leads into step #3.
/*
* Copyright (c) 2015-2018, NVIDIA CORPORATION. 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/>.
*/
#include <t19x-common-modules/tegra194-camera-e3326-a00.dtsi>
#include "dt-bindings/clock/tegra194-clock.h"
#define CAM0_RST_L TEGRA194_MAIN_GPIO(H, 3)
#define CAM0_PWDN TEGRA194_MAIN_GPIO(H, 6)
/* camera control gpio definitions */
/ {
i2c@3180000 {
ov5693_c@36 {
/* Define any required hw resources needed by driver */
/* ie. clocks, io pins, power sources */
/* mclk-index indicates the index of the */
/* mclk-name with in the clock-names array */
clocks = <&bpmp_clks TEGRA194_CLK_EXTPERIPH1>,
<&bpmp_clks TEGRA194_CLK_PLLP_OUT0>;
clock-names = "extperiph1", "pllp_grtba";
mclk = "extperiph1";
clock-frequency = <24000000>;
reset-gpios = <&tegra_main_gpio CAM0_RST_L GPIO_ACTIVE_HIGH>;
pwdn-gpios = <&tegra_main_gpio CAM0_PWDN GPIO_ACTIVE_HIGH>;
vana-supply = <&p2822_avdd_cam_2v8>;
vif-supply = <&p2822_vdd_1v8_cvb>;
status = "okay";
};
};
gpio@2200000 {
camera-control-output-low {
gpio-hog;
output-low;
gpios = <CAM0_RST_L 0 CAM0_PWDN 0>;
label = "cam0-rst", "cam0-pwdn";
};
};
};
3. Disable plugin manager.
Edited tegra194-p2888-0001-p2822-0000.dts. It was unclear to me if comments from @ShaneCCC regarding #include tegra194-p2822-0000-camera-e3326-a00.dtsi;
statement refers to the file in its default state, or if the file requires additional edits.
/*
* Top level DTS file for CVM:P2888-0001 and CVB:P2822-0000.
*
* Copyright (c) 2017-2019, NVIDIA CORPORATION. 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; version 2 of the License.
*
* 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.
*/
#include "common/tegra194-p2888-0001-p2822-0000-common.dtsi"
//#include "common/tegra194-p2822-camera-modules.dtsi"
//#include "t19x-common-modules/tegra194-camera-plugin-manager.dtsi"
#include "common/tegra194-p2822-0000-camera-e3326-a00.dtsi"
4. Edit ov5693.c.
I did not actually make any edits to this particular file. I just copied it from @ShaneCCC in forum thread Xavier - using raw CSI without i2c.
/*
* ov5693_v4l2.c - ov5693 sensor driver
*
* Copyright (c) 2013-2019, NVIDIA CORPORATION. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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/>.
*/
#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/ov5693.h>
#include "../platform/tegra/camera/camera_gpio.h"
#include "ov5693_mode_tbls.h"
#define CREATE_TRACE_POINTS
#include <trace/events/ov5693.h>
#define OV5693_MAX_COARSE_DIFF 6
#define OV5693_MAX_FRAME_LENGTH (0x7fff)
#define OV5693_MIN_EXPOSURE_COARSE (0x0002)
#define OV5693_MAX_EXPOSURE_COARSE \
(OV5693_MAX_FRAME_LENGTH-OV5693_MAX_COARSE_DIFF)
#define OV5693_DEFAULT_LINE_LENGTH (0xA80)
#define OV5693_DEFAULT_PIXEL_CLOCK (160)
#define OV5693_DEFAULT_FRAME_LENGTH (0x07C0)
#define OV5693_DEFAULT_EXPOSURE_COARSE \
(OV5693_DEFAULT_FRAME_LENGTH-OV5693_MAX_COARSE_DIFF)
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 ov5693 {
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 ov5693_regmap_config = {
.reg_bits = 16,
.val_bits = 8,
};
static inline void ov5693_get_frame_length_regs(ov5693_reg *regs,
u32 frame_length)
{
regs->addr = OV5693_FRAME_LENGTH_ADDR_MSB;
regs->val = (frame_length >> 8) & 0xff;
(regs + 1)->addr = OV5693_FRAME_LENGTH_ADDR_LSB;
(regs + 1)->val = (frame_length) & 0xff;
}
static inline void ov5693_get_coarse_time_regs(ov5693_reg *regs,
u32 coarse_time)
{
regs->addr = OV5693_COARSE_TIME_ADDR_1;
regs->val = (coarse_time >> 12) & 0xff;
(regs + 1)->addr = OV5693_COARSE_TIME_ADDR_2;
(regs + 1)->val = (coarse_time >> 4) & 0xff;
(regs + 2)->addr = OV5693_COARSE_TIME_ADDR_3;
(regs + 2)->val = (coarse_time & 0xf) << 4;
}
static inline void ov5693_get_coarse_time_short_regs(ov5693_reg *regs,
u32 coarse_time)
{
regs->addr = OV5693_COARSE_TIME_SHORT_ADDR_1;
regs->val = (coarse_time >> 12) & 0xff;
(regs + 1)->addr = OV5693_COARSE_TIME_SHORT_ADDR_2;
(regs + 1)->val = (coarse_time >> 4) & 0xff;
(regs + 2)->addr = OV5693_COARSE_TIME_SHORT_ADDR_3;
(regs + 2)->val = (coarse_time & 0xf) << 4;
}
static inline void ov5693_get_gain_regs(ov5693_reg *regs,
u16 gain)
{
regs->addr = OV5693_GAIN_ADDR_MSB;
regs->val = (gain >> 8) & 0xff;
(regs + 1)->addr = OV5693_GAIN_ADDR_LSB;
(regs + 1)->val = (gain) & 0xff;
}
static int test_mode;
module_param(test_mode, int, 0644);
static int ov5693_write_reg(struct camera_common_data *s_data, u16 addr, u8 val)
{
return 0;
}
static int ov5693_write_table(struct ov5693 *priv,
const ov5693_reg table[])
{
return 0;
}
static void ov5693_gpio_set(struct camera_common_data *s_data,
unsigned int gpio, int val)
{
struct camera_common_pdata *pdata = s_data->pdata;
if (pdata && pdata->use_cam_gpio)
cam_gpio_ctrl(s_data->dev, gpio, val, 1);
else {
if (gpio_cansleep(gpio))
gpio_set_value_cansleep(gpio, val);
else
gpio_set_value(gpio, val);
}
}
static int ov5693_power_on(struct camera_common_data *s_data)
{
int err = 0;
struct camera_common_power_rail *pw = s_data->power;
struct camera_common_pdata *pdata = s_data->pdata;
struct device *dev = s_data->dev;
dev_dbg(dev, "%s: power on\n", __func__);
if (pdata && pdata->power_on) {
err = pdata->power_on(pw);
if (err)
dev_err(dev, "%s failed.\n", __func__);
else
pw->state = SWITCH_ON;
return err;
}
/* sleeps calls in the sequence below are for internal device
* signal propagation as specified by sensor vendor
*/
if (pw->avdd)
err = regulator_enable(pw->avdd);
if (err)
goto ov5693_avdd_fail;
if (pw->iovdd)
err = regulator_enable(pw->iovdd);
if (err)
goto ov5693_iovdd_fail;
usleep_range(1, 2);
if (gpio_is_valid(pw->pwdn_gpio))
ov5693_gpio_set(s_data, pw->pwdn_gpio, 1);
/*
* datasheet 2.9: reset requires ~2ms settling time
* a power on reset is generated after core power becomes stable
*/
usleep_range(2000, 2010);
if (gpio_is_valid(pw->reset_gpio))
ov5693_gpio_set(s_data, pw->reset_gpio, 1);
/* datasheet fig 2-9: t3 */
usleep_range(2000, 2010);
pw->state = SWITCH_ON;
return 0;
ov5693_iovdd_fail:
regulator_disable(pw->avdd);
ov5693_avdd_fail:
dev_err(dev, "%s failed.\n", __func__);
return -ENODEV;
}
static int ov5693_power_off(struct camera_common_data *s_data)
{
int err = 0;
struct camera_common_power_rail *pw = s_data->power;
struct device *dev = s_data->dev;
struct camera_common_pdata *pdata = s_data->pdata;
dev_dbg(dev, "%s: power off\n", __func__);
if (pdata && pdata->power_off) {
err = pdata->power_off(pw);
if (!err) {
goto power_off_done;
} else {
dev_err(dev, "%s failed.\n", __func__);
return err;
}
}
/* sleeps calls in the sequence below are for internal device
* signal propagation as specified by sensor vendor
*/
usleep_range(21, 25);
if (gpio_is_valid(pw->pwdn_gpio))
ov5693_gpio_set(s_data, pw->pwdn_gpio, 0);
usleep_range(1, 2);
if (gpio_is_valid(pw->reset_gpio))
ov5693_gpio_set(s_data, pw->reset_gpio, 0);
/* datasheet 2.9: reset requires ~2ms settling time*/
usleep_range(2000, 2010);
if (pw->iovdd)
regulator_disable(pw->iovdd);
if (pw->avdd)
regulator_disable(pw->avdd);
power_off_done:
pw->state = SWITCH_OFF;
return 0;
}
static int ov5693_power_put(struct tegracam_device *tc_dev)
{
struct camera_common_data *s_data = tc_dev->s_data;
struct camera_common_power_rail *pw = s_data->power;
struct camera_common_pdata *pdata = s_data->pdata;
struct device *dev = tc_dev->dev;
if (unlikely(!pw))
return -EFAULT;
if (pdata && pdata->use_cam_gpio)
cam_gpio_deregister(dev, pw->pwdn_gpio);
else {
if (gpio_is_valid(pw->pwdn_gpio))
gpio_free(pw->pwdn_gpio);
if (gpio_is_valid(pw->reset_gpio))
gpio_free(pw->reset_gpio);
}
return 0;
}
static int ov5693_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;
struct camera_common_pdata *pdata = s_data->pdata;
struct device *dev = tc_dev->dev;
const char *mclk_name;
const char *parentclk_name;
struct clk *parent;
int err = 0, ret = 0;
if (!pdata) {
dev_err(dev, "pdata missing\n");
return -EFAULT;
}
mclk_name = pdata->mclk_name ?
pdata->mclk_name : "cam_mclk1";
pw->mclk = devm_clk_get(dev, mclk_name);
if (IS_ERR(pw->mclk)) {
dev_err(dev, "unable to get clock %s\n", mclk_name);
return PTR_ERR(pw->mclk);
}
parentclk_name = pdata->parentclk_name;
if (parentclk_name) {
parent = devm_clk_get(dev, parentclk_name);
if (IS_ERR(parent)) {
dev_err(dev, "unable to get parent clcok %s",
parentclk_name);
} else
clk_set_parent(pw->mclk, parent);
}
/* analog 2.8v */
err |= camera_common_regulator_get(dev,
&pw->avdd, pdata->regulators.avdd);
/* IO 1.8v */
err |= camera_common_regulator_get(dev,
&pw->iovdd, pdata->regulators.iovdd);
if (!err) {
pw->reset_gpio = pdata->reset_gpio;
pw->pwdn_gpio = pdata->pwdn_gpio;
}
if (pdata->use_cam_gpio) {
err = cam_gpio_register(dev, pw->pwdn_gpio);
if (err)
dev_err(dev, "%s ERR can't register cam gpio %u!\n",
__func__, pw->pwdn_gpio);
} else {
if (gpio_is_valid(pw->pwdn_gpio)) {
ret = gpio_request(pw->pwdn_gpio, "cam_pwdn_gpio");
if (ret < 0) {
dev_dbg(dev, "%s can't request pwdn_gpio %d\n",
__func__, ret);
}
gpio_direction_output(pw->pwdn_gpio, 1);
}
if (gpio_is_valid(pw->reset_gpio)) {
ret = gpio_request(pw->reset_gpio, "cam_reset_gpio");
if (ret < 0) {
dev_dbg(dev, "%s can't request reset_gpio %d\n",
__func__, ret);
}
gpio_direction_output(pw->reset_gpio, 1);
}
}
pw->state = SWITCH_OFF;
return err;
}
static int ov5693_set_gain(struct tegracam_device *tc_dev, s64 val);
static int ov5693_set_frame_rate(struct tegracam_device *tc_dev, s64 val);
static int ov5693_set_exposure(struct tegracam_device *tc_dev, s64 val);
static int ov5693_set_exposure_short(struct tegracam_device *tc_dev, s64 val);
static const struct of_device_id ov5693_of_match[] = {
{
.compatible = "nvidia,ov5693",
},
{ },
};
static int ov5693_set_group_hold(struct tegracam_device *tc_dev, bool val)
{
int err;
struct ov5693 *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 = ov5693_write_reg(priv->s_data,
OV5693_GROUP_HOLD_ADDR, val);
if (err)
goto fail;
priv->group_hold_prev = 1;
dev_dbg(dev, "%s: enter group hold\n", __func__);
} else if (priv->group_hold_en == false && gh_prev == SWITCH_ON) {
/* leave group hold */
err = ov5693_write_reg(priv->s_data,
OV5693_GROUP_HOLD_ADDR, 0x11);
if (err)
goto fail;
err = ov5693_write_reg(priv->s_data,
OV5693_GROUP_HOLD_ADDR, 0x61);
if (err)
goto fail;
priv->group_hold_prev = 0;
dev_dbg(dev, "%s: leave group hold\n", __func__);
}
return 0;
fail:
dev_dbg(dev, "%s: Group hold control error\n", __func__);
return err;
}
static int ov5693_set_gain(struct tegracam_device *tc_dev, s64 val)
{
struct camera_common_data *s_data = tc_dev->s_data;
struct ov5693 *priv = (struct ov5693 *)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];
ov5693_reg reg_list[2];
int err;
u16 gain;
int i;
if (!priv->group_hold_prev)
ov5693_set_group_hold(tc_dev, 1);
/* translate value */
gain = (u16) (((val * 16) +
(mode->control_properties.gain_factor / 2)) /
mode->control_properties.gain_factor);
ov5693_get_gain_regs(reg_list, gain);
dev_dbg(dev, "%s: gain %d val: %lld\n", __func__, gain, val);
for (i = 0; i < 2; i++) {
err = ov5693_write_reg(s_data, reg_list[i].addr,
reg_list[i].val);
if (err)
goto fail;
}
return 0;
fail:
dev_dbg(dev, "%s: GAIN control error\n", __func__);
return err;
}
static int ov5693_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 ov5693 *priv = tc_dev->priv;
const struct sensor_mode_properties *mode =
&s_data->sensor_props.sensor_modes[s_data->mode_prop_idx];
ov5693_reg reg_list[2];
int err;
u32 frame_length;
int i;
if (!priv->group_hold_prev)
ov5693_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;
ov5693_get_frame_length_regs(reg_list, frame_length);
dev_dbg(dev, "%s: val: %d\n", __func__, frame_length);
for (i = 0; i < 2; i++) {
err = ov5693_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_dbg(dev, "%s: FRAME_LENGTH control error\n", __func__);
return err;
}
static int ov5693_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 ov5693 *priv = tc_dev->priv;
const s32 max_coarse_time = priv->frame_length - OV5693_MAX_COARSE_DIFF;
const struct sensor_mode_properties *mode =
&s_data->sensor_props.sensor_modes[s_data->mode_prop_idx];
ov5693_reg reg_list[3];
int err;
u32 coarse_time;
int i;
if (!priv->group_hold_prev)
ov5693_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 < OV5693_MIN_EXPOSURE_COARSE)
coarse_time = OV5693_MIN_EXPOSURE_COARSE;
else if (coarse_time > max_coarse_time)
coarse_time = max_coarse_time;
ov5693_get_coarse_time_regs(reg_list, coarse_time);
dev_dbg(dev, "%s: val: %d\n", __func__, coarse_time);
for (i = 0; i < 3; i++) {
err = ov5693_write_reg(s_data, reg_list[i].addr,
reg_list[i].val);
if (err)
goto fail;
}
return 0;
fail:
dev_dbg(dev, "%s: COARSE_TIME control error\n", __func__);
return err;
}
static int ov5693_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 ov5693 *priv = tc_dev->priv;
const struct sensor_mode_properties *mode =
&s_data->sensor_props.sensor_modes[s_data->mode_prop_idx];
ov5693_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)
ov5693_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);
ov5693_get_coarse_time_short_regs(reg_list, coarse_time_short);
dev_dbg(dev, "%s: val: %d\n", __func__, coarse_time_short);
for (i = 0; i < 3; i++) {
err = ov5693_write_reg(s_data, reg_list[i].addr,
reg_list[i].val);
if (err)
goto fail;
}
return 0;
fail:
dev_dbg(dev, "%s: COARSE_TIME_SHORT control error\n", __func__);
return err;
}
MODULE_DEVICE_TABLE(of, ov5693_of_match);
static struct camera_common_pdata *ov5693_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 gpio;
int err;
struct camera_common_pdata *ret = NULL;
if (!node)
return NULL;
match = of_match_device(ov5693_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");
goto error;
}
gpio = of_get_named_gpio(node, "pwdn-gpios", 0);
if (gpio < 0) {
if (gpio == -EPROBE_DEFER) {
ret = ERR_PTR(-EPROBE_DEFER);
goto error;
}
gpio = 0;
}
board_priv_pdata->pwdn_gpio = (unsigned int)gpio;
gpio = of_get_named_gpio(node, "reset-gpios", 0);
if (gpio < 0) {
/* reset-gpio is not absolutely needed */
if (gpio == -EPROBE_DEFER) {
ret = ERR_PTR(-EPROBE_DEFER);
goto error;
}
dev_dbg(dev, "reset gpios not in DT\n");
gpio = 0;
}
board_priv_pdata->reset_gpio = (unsigned int)gpio;
board_priv_pdata->use_cam_gpio =
of_property_read_bool(node, "cam, use-cam-gpio");
err = of_property_read_string(node, "avdd-reg",
&board_priv_pdata->regulators.avdd);
if (err) {
dev_err(dev, "avdd-reg not in DT\n");
goto error;
}
err = of_property_read_string(node, "iovdd-reg",
&board_priv_pdata->regulators.iovdd);
if (err) {
dev_err(dev, "iovdd-reg not in DT\n");
goto error;
}
board_priv_pdata->v_flip = of_property_read_bool(node, "vertical-flip");
board_priv_pdata->h_mirror = of_property_read_bool(node,
"horizontal-mirror");
return board_priv_pdata;
error:
devm_kfree(dev, board_priv_pdata);
return ret;
}
static int ov5693_set_mode(struct tegracam_device *tc_dev)
{
struct ov5693 *priv = (struct ov5693 *)tegracam_get_privdata(tc_dev);
struct camera_common_data *s_data = tc_dev->s_data;
int err;
err = ov5693_write_table(priv, mode_table[s_data->mode_prop_idx]);
if (err)
return err;
return 0;
}
static int ov5693_start_streaming(struct tegracam_device *tc_dev)
{
struct ov5693 *priv = (struct ov5693 *)tegracam_get_privdata(tc_dev);
mutex_lock(&priv->streaming_lock);
priv->streaming = true;
mutex_unlock(&priv->streaming_lock);
return 0;
}
static int ov5693_stop_streaming(struct tegracam_device *tc_dev)
{
struct ov5693 *priv = (struct ov5693 *)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 *
OV5693_DEFAULT_LINE_LENGTH / OV5693_DEFAULT_PIXEL_CLOCK;
usleep_range(frame_time, frame_time + 1000);
return 0;
}
static struct camera_common_sensor_ops ov5693_common_ops = {
.numfrmfmts = ARRAY_SIZE(ov5693_frmfmt),
.frmfmt_table = ov5693_frmfmt,
.power_on = ov5693_power_on,
.power_off = ov5693_power_off,
.write_reg = ov5693_write_reg,
.parse_dt = ov5693_parse_dt,
.power_get = ov5693_power_get,
.power_put = ov5693_power_put,
.set_mode = ov5693_set_mode,
.start_streaming = ov5693_start_streaming,
.stop_streaming = ov5693_stop_streaming,
};
static struct tegracam_ctrl_ops ov5693_ctrl_ops = {
.numctrls = ARRAY_SIZE(ctrl_cid_list),
.ctrl_cid_list = ctrl_cid_list,
.set_gain = ov5693_set_gain,
.set_exposure = ov5693_set_exposure,
.set_exposure_short = ov5693_set_exposure_short,
.set_frame_rate = ov5693_set_frame_rate,
.set_group_hold = ov5693_set_group_hold,
};
static int ov5693_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
dev_dbg(&client->dev, "%s:\n", __func__);
return 0;
}
static const struct v4l2_subdev_internal_ops ov5693_subdev_internal_ops = {
.open = ov5693_open,
};
static int ov5693_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 ov5693 *priv;
int err;
const struct of_device_id *match;
dev_info(dev, "probing v4l2 sensor no i2c.\n");
match = of_match_device(ov5693_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 ov5693), 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, "ov5693", sizeof(tc_dev->name));
tc_dev->dev_regmap_config = &ov5693_regmap_config;
tc_dev->sensor_ops = &ov5693_common_ops;
tc_dev->v4l2sd_internal_ops = &ov5693_subdev_internal_ops;
tc_dev->tcctrl_ops = &ov5693_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_dbg(dev, "Detected OV5693 sensor\n");
return 0;
}
static int
ov5693_remove(struct i2c_client *client)
{
struct camera_common_data *s_data = to_camera_common_data(&client->dev);
struct ov5693 *priv = (struct ov5693 *)s_data->priv;
tegracam_v4l2subdev_unregister(priv->tc_dev);
ov5693_power_put(priv->tc_dev);
tegracam_device_unregister(priv->tc_dev);
mutex_destroy(&priv->streaming_lock);
return 0;
}
static const struct i2c_device_id ov5693_id[] = {
{ "ov5693", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ov5693_id);
static struct i2c_driver ov5693_i2c_driver = {
.driver = {
.name = "ov5693",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(ov5693_of_match),
},
.probe = ov5693_probe,
.remove = ov5693_remove,
.id_table = ov5693_id,
};
module_i2c_driver(ov5693_i2c_driver);
MODULE_DESCRIPTION("Media Controller driver for OmniVision OV5693");
MODULE_AUTHOR("NVIDIA Corporation");
MODULE_LICENSE("GPL v2");
5. Edit ov5693_mode_tbls.h.
Like the file above, I just picked this up from @ShaneCCC in forum thread Xavier - using raw CSI without i2c. The only edit I made was to the mode table for 1920 x 1080 resolution.
/*
* ov5693_mode_tbls.h - ov5693 sensor mode tables
*
* Copyright (c) 2015-2019, NVIDIA CORPORATION, All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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/>.
*/
#ifndef __OV5693_TABLES__
#define __OV5693_TABLES__
#include <media/camera_common.h>
#define OV5693_TABLE_WAIT_MS 0
#define OV5693_TABLE_END 1
#define OV5693_MAX_RETRIES 3
#define OV5693_WAIT_MS 10
#define ov5693_reg struct reg_8
static const ov5693_reg ov5693_start[] = {
{0x0100, 0x01}, /* mode select streaming on */
{OV5693_TABLE_END, 0x00}
};
static const ov5693_reg ov5693_stop[] = {
{0x0100, 0x00}, /* mode select streaming on */
{OV5693_TABLE_END, 0x00}
};
static const ov5693_reg tp_colorbars[] = {
{0x0600, 0x00},
{0x0601, 0x02},
{OV5693_TABLE_WAIT_MS, OV5693_WAIT_MS},
{OV5693_TABLE_END, 0x00}
};
static const ov5693_reg mode_2592x1944[] = {
{OV5693_TABLE_WAIT_MS, OV5693_WAIT_MS},
{OV5693_TABLE_END, 0x0000}
};
enum {
OV5693_MODE_2592X1944,
OV5693_MODE_START_STREAM,
OV5693_MODE_STOP_STREAM,
OV5693_MODE_TEST_PATTERN
};
static const ov5693_reg *mode_table[] = {
[OV5693_MODE_2592X1944] = mode_2592x1944,
[OV5693_MODE_START_STREAM] = ov5693_start,
[OV5693_MODE_STOP_STREAM] = ov5693_stop,
[OV5693_MODE_TEST_PATTERN] = tp_colorbars,
};
static const int ov5693_30fps[] = {
30,
};
static const struct camera_common_frmfmt ov5693_frmfmt[] = {
{{1920, 1080}, ov5693_30fps, 1, 0, OV5693_MODE_2592X1944},
};
#endif /* __OV5693_TABLES__ */
6. Kernel configuration.
Edited [src]/kernel/kernel-4.9/arch/arm64/configs/tegra_defconfig.
CONFIG_VIDEO_OV5693=y
Note: In previous iterations, this step was also tried as a loadable kernel module by changing the line to CONFIG_VIDEO_OV5693=m
, copying the new LKM file to the jetson, then running $ sudo rmmod [path_to_original_ov5693.ko]
, and finally $ sudo insmod [path_to_new_ov5693.ko]
. This did not change anything in regard to the detection of /dev/video0.
7. Build the NVIDIA Kernel.
See “Building the NVIDIA Kernel” Welcome — Jetson Linux<br/>Developer Guide 34.1 documentation
Note: The sample root file system was integrated into the L4T rootfs folder prior to transferring kernel output files to L4T and installing the kernel.
8. Apply binaries.
$ sudo ./apply_binaries.sh
This was initially performed on an Ubuntu 16.04 host computer, which resulted in errors. Running on Ubuntu 18.04 resulted in “Success!”
9. Connect to Xavier USB-C port on back side (where the GPIO pins live).
10. Flash kernel.
$ sudo ./flash.sh -k kernel-dtb jetson-xavier mmcblk0p1
Incoporating the -r
switch in the ./flash.sh
command failed because it looks for system.img, which was not built.
I understand that my driver could be so far out of tune that it wouldn’t pick up a single valid video frame from the FPGA. That will require adjusting, and possibly an additional post to the forums. However, I do not understand what needs to be corrected to identify /dev/video0.
Any help or feedback would be greatly apprecited.
Thanks!
Ian