Files
linux-nv-oot/drivers/media/i2c/nv_ar0234.c
Praveen AC 8ba7c4a320 media: i2c: update ar0234 EEPROM data structure
- Integrate calibration data structure from
  Leopard Imaging hawk driver into NVIDIA
  hawk driver
- Rearrange EEPROM calibration data structure to
  maintain backward compatibility with HAWK, OWL
  modules with EEPROMs programmed using earlier
  calibration data structure
- Use separate structures for tracking imu noise
  parameters and place it after the module
  serial number.
- Change module serial number length used for
  reading the serial number from EEPROM data

L4T-4649

Change-Id: I17613905e63fef3fc690dca42a0a10bd1f4fc24d
Signed-off-by: Praveen AC <pac@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3072846
(cherry picked from commit 518ae94bb4)
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3073820
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
Reviewed-by: Bibek Basu <bbasu@nvidia.com>
2024-07-19 03:05:51 -07:00

1227 lines
31 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
// SPDX-FileCopyrightText: Copyright (c) 2018-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
/*
* ar0234.c - ar0234 sensor driver
*/
#include <nvidia/conftest.h>
#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>
#include <media/max9295.h>
#include <media/max9296.h>
#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
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);
#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,
};
// Coefficients as per distortion model (wide FOV) being used
struct fisheye_lens_distortion_coeff {
// Radial coefficients count
u32 coeff_count;
// Radial coefficients
float k[MAX_FISHEYE_COEFFICIENTS];
// 0 -> equidistant, 1 -> equisolid, 2 -> orthographic, 3 -> stereographic
u32 mapping_type;
};
// Coefficients as per distortion model being used
struct polynomial_lens_distortion_coeff {
// 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];
};
/*
* Stereo Eeprom Data
*/
struct camera_intrinsics {
// 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 {
struct polynomial_lens_distortion_coeff poly;
struct fisheye_lens_distortion_coeff fisheye;
} dist_coeff;
};
/*
* Extrinsic parameters shared by camera and IMU.
* All rotation + translation with respect to the same reference point
*/
struct camera_extrinsics {
/*
* 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;
};
/*
* 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.
*/
struct imu_params_v1 {
// 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
struct camera_extrinsics extr;
};
struct imu_params_noise_m {
/*
* 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;
};
/*
* Combined IMU calibration data structure
*/
struct imu_params_v2 {
struct imu_params_v1 imu_data_v1;
struct imu_params_noise_m nm;
};
struct radial_lsc_params {
// 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;
};
struct NvCamSyncSensorCalibData {
// Intrinsic structure for camera device
struct camera_intrinsics cam_intr;
// Extrinsic structure for camera device
struct camera_extrinsics cam_extr;
// Flag for IMU availability
u8 imu_present;
// Intrinsic structure for IMU
struct imu_params_v2 imu;
// HAWK module serial number
u8 serial_number[CAMERA_MAX_SN_LENGTH];
// Radial Lens Shading Correction parameters
struct radial_lsc_params rls;
};
struct LiEeprom_Content_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
*/
struct camera_intrinsics left_cam_intr;
struct camera_intrinsics right_cam_intr;
/**
* Extrinsic structure for camera device
*/
struct camera_extrinsics cam_extr;
/**
* Flag for IMU 0-absent, 1-present
*/
u8 imu_present;
/**
* Intrinsic structure for IMU
*/
struct imu_params_v1 imu;
u8 tmp[16];
// HAWK module serial number
u8 serial_number[LEOP_CAMERA_MAX_SN_LENGTH];
struct imu_params_noise_m nm;
// Radial Lens Shading Correction parameters
struct radial_lsc_params left_rls;
struct radial_lsc_params right_rls;
};
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;
struct 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, &reg_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;
}
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;
}
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;
}
if (0x301a == table[i].addr || 0x3060 == table[i].addr)
msleep(100);
} else {
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);
}
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 (pw->reset_gpio > 0)
gpio_set_value(pw->reset_gpio, 1);
usleep_range(1000, 2000);
if (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 {
err = clk_set_parent(pw->mclk, parent);
if (err < 0)
dev_dbg(dev,
"%s failed to set parent clock %d\n",
__func__, err);
}
}
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
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);
priv->frame_length = 0xC20;
} else if (val == 60000000) {//60fps full resolution
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);
priv->frame_length = 0x610;
} else if (val == 120000000) {//120fps binning resolution
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);
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;
if (mode->image_properties.line_length == 0 ||
mode->control_properties.exposure_factor == 0)
return -EINVAL;
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;
struct LiEeprom_Content_Struct *tmp;
u32 test = 0;
switch (ctrl->id) {
case TEGRA_CAMERA_CID_STEREO_EEPROM:
if (AR0234_EEPROM_SIZE >
sizeof(struct LiEeprom_Content_Struct))
return -EINVAL;
tmp = kmalloc(sizeof(struct LiEeprom_Content_Struct),
GFP_KERNEL);
if (tmp == NULL)
return -ENOMEM;
memset(&(priv->EepromCalib), 0,
sizeof(struct NvCamSyncSensorCalibData));
memset(ctrl->p_new.p, 0,
sizeof(struct NvCamSyncSensorCalibData));
memcpy(tmp, priv->eeprom_buf, AR0234_EEPROM_SIZE);
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(struct NvCamSyncSensorCalibData));
kfree(tmp);
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(struct NvCamSyncSensorCalibData), sizeof(struct alternating_exposure_cfg)},
.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;
priv->eeprom[i].i2c_client = i2c_new_client_device(
priv->eeprom[i].adap, &priv->eeprom[i].brd);
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);
}
ar0234_power_off(s_data);
camera_common_mclk_disable(s_data);
return err;
}
#if defined(NV_I2C_DRIVER_STRUCT_PROBE_WITHOUT_I2C_DEVICE_ID_ARG) /* Linux 6.3 */
static int ar0234_probe(struct i2c_client *client)
#else
static int ar0234_probe(struct i2c_client *client,
const struct i2c_device_id *id)
#endif
{
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)
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 (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;
}
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;
}
dev_info(&client->dev, "single detect success\n");
} else if (ar0234_hawk_link_check(priv) == 3) {
;
} else {
return -1;
}
err = ar0234_write_table(priv, mode_table[AR0234_MODE_STOP_STREAM]);
if (err) {
dev_info(&client->dev, "ar0234 detect error\n");
return err;
}
msleep(100);
err = ar0234_board_setup(priv);
if (err) {
dev_err(dev, "board setup failed\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;
}
#if defined(NV_I2C_DRIVER_STRUCT_REMOVE_RETURN_TYPE_INT) /* Linux 6.1 */
static int ar0234_remove(struct i2c_client *client)
#else
static void ar0234_remove(struct i2c_client *client)
#endif
{
struct camera_common_data *s_data = to_camera_common_data(&client->dev);
struct ar0234 *priv;
if (!s_data)
#if defined(NV_I2C_DRIVER_STRUCT_REMOVE_RETURN_TYPE_INT) /* Linux 6.1 */
return -EINVAL;
#else
return;
#endif
priv = (struct ar0234 *)s_data->priv;
tegracam_v4l2subdev_unregister(priv->tc_dev);
tegracam_device_unregister(priv->tc_dev);
ar0234_eeprom_device_release(priv);
#if defined(NV_I2C_DRIVER_STRUCT_REMOVE_RETURN_TYPE_INT) /* Linux 6.1 */
return 0;
#endif
}
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");