// 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 #define DEBUG 1 #include #include #include #include #include #include #include #include #include #include #include #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, ®_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