Hi Forum,
I am bringing up a custom AR0234 camera module (without any oscillator) making use the <jetpack5.1.2-r35.4.1-bsp>/kernel/nvidia/drivers/media/i2c/nv_ar0234.c. Below is the dts :
/dts-v1/;
#include "tegra234-orin-nx-toto-VIO-WASN002.dts"
#include "../toto_camera/tegra234-orin-nx-toto-VIO-WASN002-AR0234-2LANES.dtsi"
/ {
nvidia,dtsfilename = "tegra234-orin-nx-toto-VIO-WASN002-AR0234-2LANES.dts";
nvidia,dtbbuildtime = __DATE__, __TIME__;
};
#include "dt-bindings/gpio/tegra234-gpio.h"
#include "dt-bindings/clock/tegra234-clock.h"
#define CAM0_RST_L TEGRA234_MAIN_GPIO(H, 3)
#define CAM0_PWDN TEGRA234_MAIN_GPIO(H, 6)
#define CAM1_RST_L TEGRA234_MAIN_GPIO(AC, 1)
#define CAM1_PWDN TEGRA234_MAIN_GPIO(AC, 0)
#define PWR_EN TEGRA234_MAIN_GPIO(AC, 7)
#define CAM_I2C_MUX TEGRA234_AON_GPIO(CC, 3)
/ {
cam_i2cmux{
status = "okay";
compatible = "i2c-mux-gpio";
#address-cells = <1>;
#size-cells = <0>;
i2c-parent = <&cam_i2c>;
mux-gpios = <&tegra_aon_gpio CAM_I2C_MUX GPIO_ACTIVE_HIGH>;
i2c@0 {
status = "okay";
reg = <0>;
#address-cells = <1>;
#size-cells = <0>;
ar0234_a: ar0234_a@10 {
status = "okay";
/* Define any required hw resources needed by driver */
/* ie. clocks, io pins, power sources */
clocks = <&bpmp_clks TEGRA234_CLK_EXTPERIPH1>,
<&bpmp_clks TEGRA234_CLK_EXTPERIPH1>;
clock-names = "extperiph1", "pllp_grtba";
clock-frequency = <24000000>;
mclk = "extperiph1";
channel = "a";
reset-gpios = <&tegra_main_gpio CAM0_RST_L GPIO_ACTIVE_HIGH>;
pwdn-gpios = <&tegra_main_gpio CAM0_PWDN GPIO_ACTIVE_HIGH>;
pwr-gpios = <&tegra_main_gpio PWR_EN GPIO_ACTIVE_HIGH>;
};
};
i2c@1 {
status = "okay";
reg = <1>;
#address-cells = <1>;
#size-cells = <0>;
ar0234_c: ar0234_c@10 {
status = "okay";
/* Define any required hw resources needed by driver */
/* ie. clocks, io pins, power sources */
clocks = <&bpmp_clks TEGRA234_CLK_EXTPERIPH1>,
<&bpmp_clks TEGRA234_CLK_EXTPERIPH1>;
clock-names = "extperiph1", "pllp_grtba";
clock-frequency = <24000000>;
mclk = "extperiph1";
channel = "a";
reset-gpios = <&tegra_main_gpio CAM1_RST_L GPIO_ACTIVE_HIGH>;
pwdn-gpios = <&tegra_main_gpio CAM1_PWDN GPIO_ACTIVE_HIGH>;
pwr-gpios = <&tegra_main_gpio PWR_EN GPIO_ACTIVE_HIGH>;
};
};
};
gpio@2200000 {
camera-control-output-low {
gpio-hog;
output-low;
gpios = <CAM0_PWDN 0 CAM1_PWDN 0>;
label = "cam0-pwdn","cam1-pwdn";
};
};
};
And the .dtsi :
#include <dt-bindings/media/camera.h>
/ {
tegra-capture-vi {
num-channels = <2>;
ports {
#address-cells = <1>;
#size-cells = <0>;
vi_port0: port@0 {
reg = <0>;
vio_vi_in0: endpoint {
port-index = <0>;
bus-width = <2>;
remote-endpoint = <&vio_csi_out0>;
};
};
vi_port1: port@1 {
reg = <1>;
vio_vi_in1: endpoint {
port-index = <2>;
bus-width = <2>;
remote-endpoint = <&vio_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>;
vio_csi_in0: endpoint@0 {
port-index = <0>;
bus-width = <2>;
remote-endpoint = <&vio_cam_out0>;
};
};
csi_chan0_port1: port@1 {
reg = <1>;
vio_csi_out0: endpoint@1 {
remote-endpoint = <&vio_vi_in0>;
};
};
};
};
csi_chan1: channel@1 {
reg = <1>;
ports {
#address-cells = <1>;
#size-cells = <0>;
csi_chan1_port0: port@0 {
reg = <0>;
vio_csi_in1: endpoint@2 {
port-index = <2>;
bus-width = <2>;
remote-endpoint = <&vio_cam_out1>;
};
};
csi_chan1_port1: port@1 {
reg = <1>;
vio_csi_out1: endpoint@3 {
remote-endpoint = <&vio_vi_in1>;
};
};
};
};
};
};
cam_i2cmux {
i2c_0:i2c@0 {
ar0234_a@10 {
compatible = "onsemi,ar0234";
reg = <0x10>;
devnode = "video0";
/* Physical dimensions of sensor */
physical_w = "15.0";
physical_h = "12.5";
sensor_model ="ar0234";
supports-alt-exp = "true";
post_crop_frame_drop = "0";
/* Convert Gain to unit of dB (decibel) befor passing to kernel driver */
//use_decibel_gain = "true";
/* enable CID_SENSOR_MODE_ID for sensor modes selection */
use_sensor_mode_id = "true";
mode0 {/*mode IMX424_MODE_3840X1080_CROP_30FPS*/
mclk_khz = "24000";
num_lanes = "2";
tegra_sinterface = "serial_a";
vc_id = "1";
discontinuous_clk = "no";
dpcm_enable = "false";
cil_settletime = "0";
dynamic_pixel_bit_depth = "10";
csi_pixel_bit_depth = "10";
mode_type = "bayer";
pixel_phase = "grbg";
active_w = "1920";
active_h = "1200";
readout_orientation = "0";
line_length = "2448";
inherent_gain = "1";
mclk_multiplier = "3.01";
pix_clk_hz = "134400000";
serdes_pix_clk_hz = "299000000";
gain_factor = "100";
min_gain_val = "100"; /* dB */
max_gain_val = "1600"; /* dB */
step_gain_val = "1"; /* 0.1 */
default_gain = "100";
min_hdr_ratio = "1";
max_hdr_ratio = "1";
framerate_factor = "1000000";
min_framerate = "30000000";
max_framerate = "30000000";
step_framerate = "30000000";
default_framerate = "30000000";
exposure_factor = "1000000";
min_exp_time = "28"; /*us, 2 lines*/
max_exp_time = "22000";
step_exp_time = "1";
default_exp_time = "22000";/* us */
embedded_metadata_height = "0";
};
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
vio_cam_out0: endpoint {
port-index = <0>; /* CSI A */
bus-width = <2>;
remote-endpoint = <&vio_csi_in0>;
};
};
};
};
};
i2c_1: i2c@1 {
ar0234_c@10 {
compatible = "onsemi,ar0234";
reg = <0x10>;
devnode = "video1";
/* Physical dimensions of sensor */
physical_w = "15.0";
physical_h = "12.5";
sensor_model ="ar0234";
supports-alt-exp = "true";
post_crop_frame_drop = "0";
/* Convert Gain to unit of dB (decibel) befor passing to kernel driver */
//use_decibel_gain = "true";
/* enable CID_SENSOR_MODE_ID for sensor modes selection */
use_sensor_mode_id = "true";
mode0 {/*mode IMX424_MODE_3840X1080_CROP_30FPS*/
mclk_khz = "24000";
num_lanes = "2";
tegra_sinterface = "serial_c";
vc_id = "1";
discontinuous_clk = "no";
dpcm_enable = "false";
cil_settletime = "0";
dynamic_pixel_bit_depth = "10";
csi_pixel_bit_depth = "10";
mode_type = "bayer";
pixel_phase = "grbg";
active_w = "1920";
active_h = "1200";
readout_orientation = "0";
line_length = "2448";
inherent_gain = "1";
mclk_multiplier = "3.01";
pix_clk_hz = "134400000";
serdes_pix_clk_hz = "299000000";
gain_factor = "100";
min_gain_val = "100"; /* dB */
max_gain_val = "1600"; /* dB */
step_gain_val = "1"; /* 0.1 */
default_gain = "100";
min_hdr_ratio = "1";
max_hdr_ratio = "1";
framerate_factor = "1000000";
min_framerate = "30000000";
max_framerate = "30000000";
step_framerate = "30000000";
default_framerate = "30000000";
exposure_factor = "1000000";
min_exp_time = "28"; /*us, 2 lines*/
max_exp_time = "22000";
step_exp_time = "1";
default_exp_time = "22000";/* us */
embedded_metadata_height = "0";
};
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
vio_cam_out1: endpoint {
status = "okay";
port-index = <2>;
bus-width = <2>;
remote-endpoint = <&vio_csi_in1>;
};
};
};
};
};
};
};
/ {
tcp: 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 = <10>;
vi_peak_byte_per_pixel = <2>;
vi_bw_margin_pct = <25>;
max_pixel_rate = <240000>;
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 vendor.
*/
modules {
cam_module0: module0 {
badge = "li_ar0234_front";
position = "front";
orientation = "1";
cam_module0_drivernode0: drivernode0 {
pcl_id = "v4l2_sensor";
devname = "ar0234 9-0010";
proc-device-tree = "/proc/device-tree/cam_i2cmux/i2c@0/ar0234_a@10";
};
};
cam_module1: module1 {
badge = "li_ar0234_rear";
position = "rear";
orientation = "1";
cam_module1_drivernode0: drivernode0 {
pcl_id = "v4l2_sensor";
devname = "ar0234 10-0010";
proc-device-tree = "/proc/device-tree/cam_i2cmux/i2c@1/ar0234_c@10";
};
};
};
};
};
The modified source file in which I dropped gmsl, used i2c only. I also moved the ar0234_board_setup() function on the top of any writting / reading to the I2C register(s) to make sure that the clocks are enabled (via camera_common_mclk_enable() as suggested in How to force mclk enabled for cmos sensor driver - #3 by yawei.yang ) :
/*
* ar0234.c - ar0234 sensor driver
*
* Copyright (c) 2018-2023, 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/>.
*/
#define DEBUG 1
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/gpio.h>
#include <linux/module.h>
#include <linux/seq_file.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#if 0
#include <media/max9295.h>
#include <media/max9296.h>
#endif
#include <media/tegracam_core.h>
#include "ar0234_mode_tbls.h"
#define CHANNEL_N 13
#define MAX_RADIAL_COEFFICIENTS 6
#define MAX_TANGENTIAL_COEFFICIENTS 2
#define MAX_FISHEYE_COEFFICIENTS 6
#define CAMERA_MAX_SN_LENGTH 32
#define LEOP_CAMERA_MAX_SN_LENGTH 10
#define MAX_RLS_COLOR_CHANNELS 4
#define MAX_RLS_BREAKPOINTS 6
#if 0
extern int max96712_write_reg_Dser(int slaveAddr,int channel,
u16 addr, u8 val);
extern int max96712_read_reg_Dser(int slaveAddr,int channel,
u16 addr, unsigned int *val);
#endif
#define AR0234_MIN_GAIN (1)
#define AR0234_MAX_GAIN (8)
#define AR0234_MAX_GAIN_REG (0x40)
#define AR0234_DEFAULT_FRAME_LENGTH (1224)
#define AR0234_COARSE_TIME_SHS1_ADDR 0x3012
#define AR0234_CTX_CONTROL_REG_ADDR 0x3034
#define AR0234_CTX_WR_DATA_REG_ADDR 0x3066
#define AR0234_ANALOG_GAIN 0x3060
const struct of_device_id ar0234_of_match[] = {
{.compatible = "onsemi,ar0234",},
{ },
};
MODULE_DEVICE_TABLE(of, ar0234_of_match);
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_EEPROM_DATA,
TEGRA_CAMERA_CID_HDR_EN,
TEGRA_CAMERA_CID_SENSOR_MODE_ID,
TEGRA_CAMERA_CID_STEREO_EEPROM,
TEGRA_CAMERA_CID_ALTERNATING_EXPOSURE,
};
const u16 alternating_exposure_cfg_size = sizeof(struct alternating_exposure_cfg);
// Coefficients as per distortion model (wide FOV) being used
typedef struct
{
// Radial coefficients count
u32 coeff_count;
// Radial coefficients
float k[MAX_FISHEYE_COEFFICIENTS];
// 0 -> equidistant, 1 -> equisolid, 2 -> orthographic, 3 -> stereographic
u32 mapping_type;
} fisheye_lens_distortion_coeff;
// Coefficients as per distortion model being used
typedef struct
{
// Radial coefficients count
u32 radial_coeff_count;
// Radial coefficients
float k[MAX_RADIAL_COEFFICIENTS];
// Tangential coefficients count
u32 tangential_coeff_count;
// Tangential coefficients
float p[MAX_TANGENTIAL_COEFFICIENTS];
} polynomial_lens_distortion_coeff;
/*
* Stereo Eeprom Data
*/
typedef struct
{
// Width and height of image in pixels
u32 width, height;
// Focal length in pixels
float fx, fy;
float skew;
// Principal point (optical center) in pixels
float cx, cy;
/*
* Structure for distortion coefficients as per the model being used
* 0: pinhole, assuming polynomial distortion
* 1: fisheye, assuming fisheye distortion)
* 2: ocam (omini-directional)
*/
u32 distortion_type;
union distortion_coefficients {
polynomial_lens_distortion_coeff poly;
fisheye_lens_distortion_coeff fisheye;
} dist_coeff;
} camera_intrinsics;
/*
* Extrinsic parameters shared by camera and IMU.
* All rotation + translation with respect to the same reference point
*/
typedef struct
{
/*
* Rotation parameter expressed in Rodrigues notation
* angle = sqrt(rx^2+ry^2+rz^2)
* unit axis = [rx,ry,rz]/angle
*/
float rx, ry, rz;
// Translation parameter from one camera to another parameter
float tx, ty, tz;
} camera_extrinsics;
/*
* IMU parameters used by HAWK 1.0. HAWK 1.0 did not have IMU noise model parameters
* in EEPROM. To preserve backward compatibility with HAWK 1.0, the EEPROM data is arranged
* in a certain way which requires tracking the imu noise model parameters in a
* separate structure.
*/
typedef struct
{
// 3D vector to add to accelerometer readings
float linear_acceleration_bias[3];
// 3D vector to add to gyroscope readings
float angular_velocity_bias[3];
// gravity acceleration
float gravity_acceleration[3];
// Extrinsic structure for IMU device
camera_extrinsics extr;
} imu_params_v1;
typedef struct {
/*
*Noise model parameters
*/
float update_rate;
float linear_acceleration_noise_density;
float linear_acceleration_random_walk;
float angular_velocity_noise_density;
float angular_velocity_random_walk;
} imu_params_noise_m;
/*
* Combined IMU calibration data structure
*/
typedef struct {
imu_params_v1 imu_data_v1;
imu_params_noise_m nm;
} imu_params_v2;
typedef struct {
// Image height
u16 image_height;
// Image width
u16 image_width;
// Number of color channels
u8 n_channels;
// Coordinate x of center point
float rls_x0[MAX_RLS_COLOR_CHANNELS];
// Coordinate y of center point
float rls_y0[MAX_RLS_COLOR_CHANNELS];
// Ellipse xx parameter
double ekxx[MAX_RLS_COLOR_CHANNELS];
// Ellipse xy parameter
double ekxy[MAX_RLS_COLOR_CHANNELS];
// Ellipse yx parameter
double ekyx[MAX_RLS_COLOR_CHANNELS];
// Ellipse yy parameter
double ekyy[MAX_RLS_COLOR_CHANNELS];
// Number of breakpoints in LSC radial transfer function
u8 rls_n_points;
// LSC radial transfer function X
float rls_rad_tf_x[MAX_RLS_COLOR_CHANNELS][MAX_RLS_BREAKPOINTS];
// LSC radial transfer function Y
float rls_rad_tf_y[MAX_RLS_COLOR_CHANNELS][MAX_RLS_BREAKPOINTS];
// LSC radial transfer function slope
float rls_rad_tf_slope[MAX_RLS_COLOR_CHANNELS][MAX_RLS_BREAKPOINTS];
// rScale parameter
u8 r_scale;
} radial_lsc_params;
typedef struct
{
// Intrinsic structure for camera device
camera_intrinsics cam_intr;
// Extrinsic structure for camera device
camera_extrinsics cam_extr;
// Flag for IMU availability
u8 imu_present;
// Intrinsic structure for IMU
imu_params_v2 imu;
// HAWK module serial number
u8 serial_number[CAMERA_MAX_SN_LENGTH];
// Radial Lens Shading Correction parameters
radial_lsc_params rls;
} NvCamSyncSensorCalibData;
typedef struct
{
/**
* EEPROM layout version
*/
u32 version;
/**
* Factory Blob Flag, to set when factory flashed and reset to 0
* when user modified
*/
u32 factory_data;
/**
* Intrinsic structure for camera device
*/
camera_intrinsics left_cam_intr;
camera_intrinsics right_cam_intr;
/**
* Extrinsic structure for camera device
*/
camera_extrinsics cam_extr;
/**
* Flag for IMU 0-absent, 1-present
*/
u8 imu_present;
/**
* Intrinsic structure for IMU
*/
imu_params_v1 imu;
u8 tmp[16];
// HAWK module serial number
u8 serial_number[LEOP_CAMERA_MAX_SN_LENGTH];
imu_params_noise_m nm;
// Radial Lens Shading Correction parameters
radial_lsc_params left_rls;
radial_lsc_params right_rls;
} LiEeprom_Content_Struct;
struct ar0234 {
struct camera_common_eeprom_data eeprom[AR0234_EEPROM_NUM_BLOCKS];
u8 eeprom_buf[AR0234_EEPROM_SIZE];
struct i2c_client *i2c_client;
const struct i2c_device_id *id;
struct v4l2_subdev *subdev;
u32 frame_length;
struct camera_common_data *s_data;
struct tegracam_device *tc_dev;
u32 channel;
u32 sync_sensor_index;
NvCamSyncSensorCalibData EepromCalib;
};
static const struct regmap_config sensor_regmap_config = {
.reg_bits = 16,
.val_bits = 16,
.cache_type = REGCACHE_RBTREE,
};
static inline void ar0234_get_coarse_time_regs_shs1(ar0234_reg *regs,
u16 coarse_time)
{
regs->addr = AR0234_COARSE_TIME_SHS1_ADDR;
regs->val = (coarse_time) & 0xffff;
}
static inline void ar0234_get_gain_reg(ar0234_reg *regs,
u16 gain)
{
regs->addr = AR0234_ANALOG_GAIN;
regs->val = (gain) & 0xffff;
}
static inline void ar0234_compute_gain_reg(u16 *gain_reg,
u16 *gain, s64 val)
{
if (val < 200) {
*gain_reg = (32 * (1000 - (100000 / *gain)))/1000;
} else if (val < 400 && val >= 200) {
*gain = *gain / 2;
*gain_reg = (16 * (1000 - (100000 / *gain)))/1000 * 2;
*gain_reg = *gain_reg + 0x10;
} else if (val < 800 && val >= 400) {
*gain = *gain / 4;
*gain_reg = (32 * (1000 - (100000 / *gain)))/1000;
*gain_reg = *gain_reg + 0x20;
} else if (val < 1600 && val >= 800) {
*gain = *gain / 8;
*gain_reg = (16 * (1000 - (100000 / *gain)))/1000 * 2;
*gain_reg = *gain_reg + 0x30;
} else if (val >= 1600) {
*gain_reg = 0x40;
}
if (*gain_reg > AR0234_MAX_GAIN_REG)
*gain_reg = AR0234_MAX_GAIN_REG;
}
static int test_mode;
module_param(test_mode, int, 0644);
static inline int ar0234_read_reg(struct camera_common_data *s_data,
u16 addr, u16 *val)
{
int err = 0;
u32 reg_val = 0;
err = regmap_read(s_data->regmap, addr, ®_val);
*val = reg_val & 0xFFFF;
return err;
}
static int ar0234_write_reg(struct camera_common_data *s_data,
u16 addr, u16 val)
{
int err;
struct device *dev = s_data->dev;
err = regmap_write(s_data->regmap, addr, val);
if (err)
dev_err(dev, "%s:i2c write failed, 0x%x = %x\n",
__func__, addr, val);
return err;
}
#if 0
static int ar0234_hawk_link_check(struct ar0234 *priv)
{
struct tegracam_device *tc_dev = priv->tc_dev;
struct device *dev = tc_dev->dev;
unsigned int linkA = 0;
unsigned int linkB = 0;
dev_dbg(dev, "%s: channel %d, ", __func__, priv->channel);
if (priv->channel == CHANNEL_N)
return 3;
max96712_read_reg_Dser(0x52, priv->channel, 0x1A, &linkA);
max96712_read_reg_Dser(0x52, priv->channel, 0x0A, &linkB);
dev_dbg(dev, "linA=%x, linB=%x\n", linkA, linkB);
if ((linkB & 0x8) && (linkA & 0x8)) {
return 2;
} else if (linkA & 0x8) {
return 1;
} else {
return 0;
}
}
#endif
static int ar0234_write_table(struct ar0234 *priv,
const struct index_reg_8 table[])
{
struct tegracam_device *tc_dev = priv->tc_dev;
struct device *dev = tc_dev->dev;
int i = 0;
int ret = 0;
int retry = 5;
dev_dbg(dev, "%s: channel %d, ", __func__, priv->channel);
while (table[i].source != 0x00) {
if (table[i].source == 0x06) {
retry = 1;
if (table[i].addr == AR0234_TABLE_WAIT_MS)
{
dev_dbg(dev, "%s: sleep 500\n", __func__);
msleep(table[i].val);
i++;
continue;
}
retry_sensor:
ret = ar0234_write_reg(priv->s_data, table[i].addr, table[i].val);
if (ret) {
retry--;
if (retry > 0) {
dev_warn(dev, "ar0234_write_reg: try %d\n", retry);
msleep(4);
goto retry_sensor;
}
return -1;
} else {
if (0x301a == table[i].addr || 0x3060 == table[i].addr)
msleep(100);
}
} else {
#if 0
retry = 5;
if (priv->channel == CHANNEL_N)
{
i++;
continue;
}
retry_serdes:
ret = max96712_write_reg_Dser(table[i].source, priv->channel, table[i].addr, (u8)table[i].val);
if (ret && (table[i].addr != 0x0000))
{
retry--;
if (retry > 0) {
dev_warn(dev, "max96712_write_reg_Dser: try %d\n", retry);
msleep(4);
goto retry_serdes;
}
return -1;
}
if (0x0010 == table[i].addr || 0x0000 == table[i].addr || 0x0006 == table[i].addr || 0x0018 == table[i].addr)
msleep(300);
else
msleep(10);
#endif
}
i++;
}
return 0;
}
static int ar0234_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;
}
if (gpio_is_valid(pw->reset_gpio > 0))
gpio_set_value(pw->reset_gpio, 1);
usleep_range(1000, 2000);
if (gpio_is_valid(pw->reset_gpio > 0))
gpio_set_value(pw->reset_gpio, 1);
usleep_range(10000, 20000);
pw->state = SWITCH_ON;
return 0;
}
static int ar0234_power_off(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:\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;
}
power_off_done:
pw->state = SWITCH_OFF;
return 0;
}
static int ar0234_power_get(struct tegracam_device *tc_dev)
{
struct device *dev = tc_dev->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;
const char *mclk_name;
const char *parentclk_name;
struct clk *parent;
int err = 0;
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);
}
if (!err) {
pw->reset_gpio = pdata->reset_gpio;
pw->af_gpio = pdata->af_gpio;
pw->pwdn_gpio = pdata->pwdn_gpio;
}
pw->state = SWITCH_OFF;
return err;
}
static int ar0234_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;
if (unlikely(!pw))
return -EFAULT;
return 0;
}
static int ar0234_set_group_hold(struct tegracam_device *tc_dev, bool val)
{
struct device *dev = tc_dev->dev;
int err = 0;
if (err) {
dev_dbg(dev,
"%s: Group hold control error\n", __func__);
return err;
}
return 0;
}
static int ar0234_set_gain(struct tegracam_device *tc_dev, s64 val)
{
struct camera_common_data *s_data = tc_dev->s_data;
struct device *dev = tc_dev->dev;
ar0234_reg reg_list[1];
int err;
u16 gain = (u16)val;
u16 gain_reg = 0;
ar0234_compute_gain_reg(&gain_reg, &gain, val);
ar0234_get_gain_reg(reg_list, gain_reg);
err = ar0234_write_reg(s_data, reg_list[0].addr,
reg_list[0].val);
if (err)
goto fail;
return 0;
fail:
dev_info(dev, "%s: GAIN control error\n", __func__);
return err;
}
static int ar0234_set_frame_rate(struct tegracam_device *tc_dev, s64 val)
{
struct ar0234 *priv = (struct ar0234 *)tegracam_get_privdata(tc_dev);
if (val == 30000000) { //30fps full resolution
#if 0
max96712_write_reg_Dser(0x52, priv->channel, 0x04A5, 0x35);
max96712_write_reg_Dser(0x52, priv->channel, 0x04A6, 0xB7);
max96712_write_reg_Dser(0x52, priv->channel, 0x04A7, 0x0C);
#endif
priv->frame_length = 0xC20;
} else if (val == 60000000) {//60fps full resolution
#if 0
max96712_write_reg_Dser(0x52, priv->channel, 0x04A5, 0x9A);
max96712_write_reg_Dser(0x52, priv->channel, 0x04A6, 0x5B);
max96712_write_reg_Dser(0x52, priv->channel, 0x04A7, 0x06);
#endif
priv->frame_length = 0x610;
} else if (val == 120000000) {//120fps binning resolution
#if 0
max96712_write_reg_Dser(0x52, priv->channel, 0x04A5, 0xCD);
max96712_write_reg_Dser(0x52, priv->channel, 0x04A6, 0x2D);
max96712_write_reg_Dser(0x52, priv->channel, 0x04A7, 0x03);
#endif
priv->frame_length = 0x308;
}
return 0;
}
static int ar0234_set_exposure(struct tegracam_device *tc_dev, s64 val)
{
struct ar0234 *priv = (struct ar0234 *)tegracam_get_privdata(tc_dev);
struct camera_common_data *s_data = tc_dev->s_data;
const struct sensor_mode_properties *mode =
&s_data->sensor_props.sensor_modes[s_data->mode];
ar0234_reg reg_list[1];
int err;
u32 coarse_time;
u32 shs1;
if (priv->frame_length == 0)
priv->frame_length = AR0234_DEFAULT_FRAME_LENGTH;
coarse_time = mode->signal_properties.pixel_clock.val *
val / mode->image_properties.line_length /
mode->control_properties.exposure_factor;
if (coarse_time > priv->frame_length)
coarse_time = priv->frame_length;
shs1 = coarse_time;
/* 0 and 1 are prohibited */
if (shs1 < 2)
shs1 = 2;
ar0234_get_coarse_time_regs_shs1(reg_list, shs1);
err = ar0234_write_reg(priv->s_data, reg_list[0].addr,
reg_list[0].val);
if (err)
goto fail;
return 0;
fail:
dev_dbg(&priv->i2c_client->dev,
"%s: set coarse time error\n", __func__);
return err;
}
static int ar0234_fill_string_ctrl(struct tegracam_device *tc_dev,
struct v4l2_ctrl *ctrl)
{
struct ar0234 *priv = tc_dev->priv;
int i, ret;
switch (ctrl->id) {
case TEGRA_CAMERA_CID_EEPROM_DATA:
for (i = 0; i < AR0234_EEPROM_SIZE; i++) {
ret = sprintf(&ctrl->p_new.p_char[i*2], "%02x",
priv->eeprom_buf[i]);
if (ret < 0)
return -EINVAL;
}
break;
default:
return -EINVAL;
}
ctrl->p_cur.p_char = ctrl->p_new.p_char;
return 0;
}
static int ar0234_fill_eeprom(struct tegracam_device *tc_dev,
struct v4l2_ctrl *ctrl)
{
struct ar0234 *priv = tc_dev->priv;
LiEeprom_Content_Struct tmp;
u32 test = 0;
switch (ctrl->id) {
case TEGRA_CAMERA_CID_STEREO_EEPROM:
memset(&(priv->EepromCalib), 0, sizeof(NvCamSyncSensorCalibData));
memset(ctrl->p_new.p, 0, sizeof(NvCamSyncSensorCalibData));
memcpy(&tmp, priv->eeprom_buf, sizeof(LiEeprom_Content_Struct));
if (priv->sync_sensor_index == 1) {
priv->EepromCalib.cam_intr = tmp.left_cam_intr;
} else if (priv->sync_sensor_index == 2) {
priv->EepromCalib.cam_intr = tmp.right_cam_intr;
} else {
priv->EepromCalib.cam_intr = tmp.left_cam_intr;
}
priv->EepromCalib.cam_extr = tmp.cam_extr;
priv->EepromCalib.imu_present = tmp.imu_present;
priv->EepromCalib.imu.imu_data_v1 = tmp.imu;
priv->EepromCalib.imu.nm = tmp.nm;
memcpy(priv->EepromCalib.serial_number, tmp.serial_number, 8);
if (priv->sync_sensor_index == 1)
priv->EepromCalib.rls = tmp.left_rls;
else if (priv->sync_sensor_index == 2)
priv->EepromCalib.rls = tmp.right_rls;
else
priv->EepromCalib.rls = tmp.left_rls;
memcpy(ctrl->p_new.p, (u8 *)&(priv->EepromCalib), sizeof(NvCamSyncSensorCalibData));
break;
default:
return -EINVAL;
}
memcpy(&test, &(priv->EepromCalib.cam_intr.fx), 4);
ctrl->p_cur.p = ctrl->p_new.p;
return 0;
}
static int ar0234_set_alternating_exposure(struct tegracam_device *tc_dev,
struct alternating_exposure_cfg *cfg)
{
struct ar0234 *priv = (struct ar0234 *)tegracam_get_privdata(tc_dev);
struct camera_common_data *s_data = tc_dev->s_data;
const struct sensor_mode_properties *mode =
&s_data->sensor_props.sensor_modes[s_data->mode];
int err, ii;
const int num_contexts = 2;
u32 coarse_time_values[2];
u16 gain_reg_values[2];
u16 gain_values[2];
struct index_reg_8 table_alt_exp_enable[] = {
{0x06, AR0234_CTX_CONTROL_REG_ADDR, 0x0000}, // reset address pointer
// set 1 (1+1) contexts in bits 7-4, set MSB 0x3 of CIT register in bits 3-0
{0x06, AR0234_CTX_WR_DATA_REG_ADDR, 0xF813},
{0x06, AR0234_CTX_WR_DATA_REG_ADDR, 0x0809}, // set address x3012, CIT
{0x06, AR0234_CTX_WR_DATA_REG_ADDR, 0x0100}, // context 0 exposure time
{0x06, AR0234_CTX_WR_DATA_REG_ADDR, 0x0200}, // context 1 exposure time
// set 1 (1+1) contexts in bits 7-4, set MSB 0x3 of analog gain register in bits 3-0
{0x06, AR0234_CTX_WR_DATA_REG_ADDR, 0xF813},
{0x06, AR0234_CTX_WR_DATA_REG_ADDR, 0x0830}, // set address x3060, analog gain
{0x06, AR0234_CTX_WR_DATA_REG_ADDR, 0x0010}, // context 0 gain
{0x06, AR0234_CTX_WR_DATA_REG_ADDR, 0x0010}, // context 1 gain
{0x06, AR0234_CTX_WR_DATA_REG_ADDR, 0x0000}, // end of code
{0x06, AR0234_CTX_CONTROL_REG_ADDR, 0x0220}, // stop auto cycling
{0x06, AR0234_TABLE_WAIT_MS, 100},
{0x06, AR0234_CTX_CONTROL_REG_ADDR, 0x02A0}, // start auto cycling two contexts
{0x00, AR0234_TABLE_END, 0x00},
};
struct index_reg_8 table_alt_exp_disable[] = {
{0x06, AR0234_CTX_CONTROL_REG_ADDR, 0x0000}, // reset address pointer
{0x06, AR0234_CTX_CONTROL_REG_ADDR, 0x0220}, // stop auto cycling
{0x06, AR0234_TABLE_WAIT_MS, 100},
{0x00, AR0234_TABLE_END, 0x00},
};
if (priv->frame_length == 0)
priv->frame_length = AR0234_DEFAULT_FRAME_LENGTH;
coarse_time_values[0] = mode->signal_properties.pixel_clock.val *
cfg->exp_time_0 / mode->image_properties.line_length /
mode->control_properties.exposure_factor;
coarse_time_values[1] = mode->signal_properties.pixel_clock.val *
cfg->exp_time_1 / mode->image_properties.line_length /
mode->control_properties.exposure_factor;
/* 0 and 1 are prohibited */
for (ii = 0; ii < num_contexts; ii++)
if (coarse_time_values[ii] < 2)
coarse_time_values[ii] = 2;
gain_values[0] = (u16)(cfg->analog_gain_0);
gain_values[1] = (u16)(cfg->analog_gain_1);
for (ii = 0; ii < 2; ii++)
gain_reg_values[ii] = 0;
ar0234_compute_gain_reg(&gain_reg_values[0], &gain_values[0], cfg->analog_gain_0);
ar0234_compute_gain_reg(&gain_reg_values[1], &gain_values[1], cfg->analog_gain_1);
table_alt_exp_enable[3].val = coarse_time_values[0] & 0xffff;
table_alt_exp_enable[4].val = coarse_time_values[1] & 0xffff;
table_alt_exp_enable[7].val = gain_reg_values[0] & 0xffff;
table_alt_exp_enable[8].val = gain_reg_values[1] & 0xffff;
if (cfg->enable == false)
err = ar0234_write_table(priv, table_alt_exp_disable);
else
err = ar0234_write_table(priv, table_alt_exp_enable);
if (err)
goto fail;
return 0;
fail:
dev_dbg(&priv->i2c_client->dev,
"%s: set alternating exposure error\n", __func__);
return err;
}
static struct tegracam_ctrl_ops ar0234_ctrl_ops = {
.numctrls = ARRAY_SIZE(ctrl_cid_list),
.ctrl_cid_list = ctrl_cid_list,
.string_ctrl_size = {AR0234_EEPROM_STR_SIZE},
.compound_ctrl_size = {sizeof(NvCamSyncSensorCalibData), alternating_exposure_cfg_size},
.set_gain = ar0234_set_gain,
.set_exposure = ar0234_set_exposure,
.set_exposure_short = ar0234_set_exposure,
.set_frame_rate = ar0234_set_frame_rate,
.set_group_hold = ar0234_set_group_hold,
.set_alternating_exposure = ar0234_set_alternating_exposure,
.fill_string_ctrl = ar0234_fill_string_ctrl,
.fill_compound_ctrl = ar0234_fill_eeprom,
};
static struct camera_common_pdata *ar0234_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;
int gpio = 0;
if (!node)
return NULL;
match = of_match_device(ar0234_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);
err = of_property_read_string(node, "mclk",
&board_priv_pdata->mclk_name);
if (err)
dev_err(dev, "mclk not in DT\n");
board_priv_pdata->reset_gpio = of_get_named_gpio(node,
"reset-gpios", 0);
gpio_direction_output(board_priv_pdata->reset_gpio, 1);
gpio = of_get_named_gpio(node,
"pwdn-gpios", 0);
gpio_direction_output(gpio, 1);
gpio = of_get_named_gpio(node,
"pwr-gpios", 0);
gpio_direction_output(gpio, 1);
board_priv_pdata->has_eeprom =
of_property_read_bool(node, "has-eeprom");
return board_priv_pdata;
}
static int ar0234_set_mode(struct tegracam_device *tc_dev)
{
struct ar0234 *priv = (struct ar0234 *)tegracam_get_privdata(tc_dev);
struct camera_common_data *s_data = tc_dev->s_data;
struct device *dev = tc_dev->dev;
const struct of_device_id *match;
int err;
match = of_match_device(ar0234_of_match, dev);
if (!match) {
dev_err(dev, "Failed to find matching dt id\n");
return -EINVAL;
}
err = ar0234_write_table(priv, mode_table[AR0234_MODE_STOP_STREAM]);
if (err)
return err;
if (s_data->mode_prop_idx < 0)
return -EINVAL;
dev_dbg(dev, "%s: mode index:%d\n", __func__,s_data->mode_prop_idx);
err = ar0234_write_table(priv, mode_table[s_data->mode_prop_idx]);
if (err)
return err;
return 0;
}
static int ar0234_start_streaming(struct tegracam_device *tc_dev)
{
struct ar0234 *priv = (struct ar0234 *)tegracam_get_privdata(tc_dev);
int err;
if (test_mode) {
err = ar0234_write_table(priv,
mode_table[AR0234_MODE_TEST_PATTERN]);
if (err)
return err;
}
err = ar0234_write_table(priv,
mode_table[AR0234_MODE_START_STREAM]);
if (err)
return err;
return 0;
}
static int ar0234_stop_streaming(struct tegracam_device *tc_dev)
{
struct ar0234 *priv = (struct ar0234 *)tegracam_get_privdata(tc_dev);
int err;
err = ar0234_write_table(priv, mode_table[AR0234_MODE_STOP_STREAM]);
if (err)
return err;
return 0;
}
static struct camera_common_sensor_ops ar0234_common_ops = {
.numfrmfmts = ARRAY_SIZE(ar0234_frmfmt),
.frmfmt_table = ar0234_frmfmt,
.power_on = ar0234_power_on,
.power_off = ar0234_power_off,
.parse_dt = ar0234_parse_dt,
.power_get = ar0234_power_get,
.power_put = ar0234_power_put,
.set_mode = ar0234_set_mode,
.start_streaming = ar0234_start_streaming,
.stop_streaming = ar0234_stop_streaming,
};
static int ar0234_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 int ar0234_eeprom_device_release(struct ar0234 *priv)
{
int i;
for (i = 0; i < AR0234_EEPROM_NUM_BLOCKS; i++) {
if (priv->eeprom[i].i2c_client != NULL) {
i2c_unregister_device(priv->eeprom[i].i2c_client);
priv->eeprom[i].i2c_client = NULL;
}
}
return 0;
}
static const struct v4l2_subdev_internal_ops ar0234_subdev_internal_ops = {
.open = ar0234_open,
};
static int ar0234_eeprom_device_init(struct ar0234 *priv)
{
struct camera_common_pdata *pdata = priv->s_data->pdata;
char *dev_name = "eeprom_ar0234";
static struct regmap_config eeprom_regmap_config = {
.reg_bits = 8,
.val_bits = 8
};
int i;
int err;
if (!pdata->has_eeprom)
return -EINVAL;
for (i = 0; i < AR0234_EEPROM_NUM_BLOCKS; i++) {
priv->eeprom[i].adap = i2c_get_adapter(
priv->i2c_client->adapter->nr);
memset(&priv->eeprom[i].brd, 0, sizeof(priv->eeprom[i].brd));
strncpy(priv->eeprom[i].brd.type, dev_name,
sizeof(priv->eeprom[i].brd.type));
if (priv->sync_sensor_index == 1)
priv->eeprom[i].brd.addr = AR0234_EEPROM_ADDRESS + i;
else if (priv->sync_sensor_index == 2)
priv->eeprom[i].brd.addr = AR0234_EEPROM_ADDRESS_R + i;
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)
priv->eeprom[i].i2c_client = i2c_new_device(
priv->eeprom[i].adap, &priv->eeprom[i].brd);
#else
priv->eeprom[i].i2c_client = i2c_new_client_device(
priv->eeprom[i].adap, &priv->eeprom[i].brd);
#endif
priv->eeprom[i].regmap = devm_regmap_init_i2c(
priv->eeprom[i].i2c_client, &eeprom_regmap_config);
if (IS_ERR(priv->eeprom[i].regmap)) {
err = PTR_ERR(priv->eeprom[i].regmap);
ar0234_eeprom_device_release(priv);
return err;
}
}
return 0;
}
static int ar0234_read_eeprom(struct ar0234 *priv)
{
int err, i;
for (i = 0; i < AR0234_EEPROM_NUM_BLOCKS; i++) {
err = regmap_bulk_read(priv->eeprom[i].regmap, 0,
&priv->eeprom_buf[i * AR0234_EEPROM_BLOCK_SIZE],
AR0234_EEPROM_BLOCK_SIZE);
if (err)
return err;
}
return 0;
}
static int ar0234_board_setup(struct ar0234 *priv)
{
struct camera_common_data *s_data = priv->s_data;
struct device *dev = s_data->dev;
bool eeprom_ctrl = 0;
int err = 0;
dev_dbg(dev, "%s++\n", __func__);
/* eeprom interface */
err = ar0234_eeprom_device_init(priv);
if (err && s_data->pdata->has_eeprom)
dev_err(dev,
"Failed to allocate eeprom reg map: %d\n", err);
eeprom_ctrl = !err;
err = camera_common_mclk_enable(s_data);
if (err) {
dev_err(dev,
"Error %d turning on mclk\n", err);
return err;
}
err = ar0234_power_on(s_data);
if (err) {
dev_err(dev,
"Error %d during power on sensor\n", err);
return err;
}
if (eeprom_ctrl) {
err = ar0234_read_eeprom(priv);
if (err) {
dev_err(dev,
"Error %d reading eeprom\n", err);
goto error;
}
}
error:
ar0234_power_off(s_data);
camera_common_mclk_disable(s_data);
return err;
}
static int ar0234_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
struct device_node *node = dev->of_node;
struct tegracam_device *tc_dev;
struct ar0234 *priv;
const char *str;
int err;
dev_info(dev, "probing v4l2 sensor.\n");
if (!IS_ENABLED(CONFIG_OF) || !node)
return -EINVAL;
priv = devm_kzalloc(dev, sizeof(struct ar0234), GFP_KERNEL);
if (!priv) {
dev_err(dev, "unable to allocate memory!\n");
return -ENOMEM;
}
tc_dev = devm_kzalloc(dev,
sizeof(struct tegracam_device), GFP_KERNEL);
if (!tc_dev)
return -ENOMEM;
err = of_property_read_string(node, "channel", &str);
if (err)
dev_err(dev, "channel not found\n");
priv->channel = str[0] - 'a';
dev_dbg(dev, "%s: channel %d\n", __func__, priv->channel);
err = of_property_read_u32(node, "sync_sensor_index",
&priv->sync_sensor_index);
if (err)
dev_err(dev, "sync name index not in DT\n");
priv->i2c_client = tc_dev->client = client;
tc_dev->dev = dev;
strncpy(tc_dev->name, "ar0234", sizeof(tc_dev->name));
tc_dev->dev_regmap_config = &sensor_regmap_config;
tc_dev->sensor_ops = &ar0234_common_ops;
tc_dev->v4l2sd_internal_ops = &ar0234_subdev_internal_ops;
tc_dev->tcctrl_ops = &ar0234_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);
ar0234_power_on(tc_dev->s_data);
msleep(100);
#if 0
if (ar0234_hawk_link_check(priv) == 2) {
err = ar0234_write_table(priv, mode_table[AR0234_MODE_Dser_Ser]);
if (err) {
dev_info(&client->dev,"dual camera detect error\n");
return err;
} else {
dev_info(&client->dev,"dual camera detect success\n");
}
} else if (ar0234_hawk_link_check(priv) == 1) {
err = ar0234_write_table(priv, mode_table[AR0234_MODE_Single_Dser_Ser]);
if (err) {
dev_info(&client->dev,"single detect error\n");
return err;
} else {
dev_info(&client->dev,"single detect success\n");
}
} else if (ar0234_hawk_link_check(priv) == 3) {
;
} else {
return -1;
}
#endif
msleep(100);
err = ar0234_board_setup(priv);
if (err) {
dev_err(dev, "board setup failed\n");
return err;
}
err = ar0234_write_table(priv, mode_table[AR0234_MODE_STOP_STREAM]);
if (err) {
dev_info(&client->dev,"ar0234 detect error\n");
return err;
}
err = tegracam_v4l2subdev_register(tc_dev, true);
if (err) {
dev_err(dev, "tegra camera subdev registration failed\n");
return err;
}
dev_info(&client->dev, "Detected AR0234 sensor\n");
return 0;
}
static int ar0234_remove(struct i2c_client *client)
{
struct camera_common_data *s_data = to_camera_common_data(&client->dev);
struct ar0234 *priv = (struct ar0234 *)s_data->priv;
tegracam_v4l2subdev_unregister(priv->tc_dev);
tegracam_device_unregister(priv->tc_dev);
ar0234_eeprom_device_release(priv);
return 0;
}
static const struct i2c_device_id ar0234_id[] = {
{ "ar0234", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ar0234_id);
static struct i2c_driver ar0234_i2c_driver = {
.driver = {
.name = "ar0234",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(ar0234_of_match),
},
.probe = ar0234_probe,
.remove = ar0234_remove,
.id_table = ar0234_id,
};
module_i2c_driver(ar0234_i2c_driver);
MODULE_DESCRIPTION("Media Controller driver for Sony AR0234");
MODULE_AUTHOR("NVIDIA Corporation");
MODULE_AUTHOR("Sudhir Vyas <svyas@nvidia.com");
MODULE_LICENSE("GPL v2");
However, I cannot probe any waveform from the MCLK pin.
Most of the available examples have the similar clock configuration but are for camera modules with on-board oscillator such as the Raspberry Pi IMX219, IMX477, …
I also found another discussion about similar issue : Enabling extperiph1/2 output - #5 by maxe777
Could you tell what was the missing configuration for generating 24MHz MCLK so that the camera sensor can work and communicate via I2C channel, please?
Thanks in advance and best regards,
Khang