// SPDX-License-Identifier: GPL-2.0-only // SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. /* * nv_hawk_owl.c.c - ar0234 sensor driver */ #include #define DEBUG 0 #include #include #include #include #include #include #include #include #include #include #include #include #include "hawk_owl_mode_tbls.h" #include #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 #define OWL_CHANNEL 1 #define HAWK_CHANNEL 0 #define EXTERNAL_FSYNC 1 #define INTERNAL_FSYNC 2 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 int owl_links; int hawk_links; static const struct of_device_id ar0234_of_match[] = { {.compatible = "nvidia,ar0234_hawk_owl",}, { }, }; 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 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; const char *sensor_name; 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 = 0; 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_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; int retry_seraddr = 0x84; 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_err(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 { retry = 5; if (priv->channel == CHANNEL_N) { i++; continue; } retry_serdes: /* To handle sync sensor i2c trans */ if (2 == priv->sync_sensor_index) ret = max96712_write_reg_Dser(table[i].source, 0/*channel-num*/, table[i].addr, (u8)table[i].val); else ret = max96712_write_reg_Dser(table[i].source, priv->channel, table[i].addr, (u8)table[i].val); /* To handle ser address change from 0x80 to 0x84 * after link enable at deser*/ if (ret && (0x80 == table[i].source)) { ret = max96712_write_reg_Dser(retry_seraddr, 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_hawk_link_check(struct ar0234 *priv) { unsigned int linkA = 0; unsigned int linkB = 0; unsigned int linkC = 0; unsigned int linkD = 0; if ((1 == priv->channel)) { max96712_read_reg_Dser(0x52, OWL_CHANNEL, 0x1A, &linkA); max96712_read_reg_Dser(0x52, OWL_CHANNEL, 0x0A, &linkB); max96712_read_reg_Dser(0x52, OWL_CHANNEL, 0x0B, &linkC); max96712_read_reg_Dser(0x52, OWL_CHANNEL, 0x0C, &linkD); } else if ((!priv->channel) || (2 == priv->sync_sensor_index)) { max96712_read_reg_Dser(0x52, HAWK_CHANNEL, 0x1A, &linkA); max96712_read_reg_Dser(0x52, HAWK_CHANNEL, 0x0A, &linkB); max96712_read_reg_Dser(0x52, HAWK_CHANNEL, 0x0B, &linkC); max96712_read_reg_Dser(0x52, HAWK_CHANNEL, 0x0C, &linkD); } pr_info("%s: linA=%x, linB=%x, linC=%x, linD=%x\n",__func__, linkA, linkB, linkC, linkD); if((linkB & 0x8) && (linkA & 0x8) && (linkC & 0x8) && (linkD & 0x8)) { return 4; } else if((linkB & 0x8) && (linkA & 0x8) && (linkC & 0x8) ){ return 3; } else if((linkB & 0x8) && (linkA & 0x8)){ return 2; } else if(linkA & 0x8) { return 1; } else { return 0; } } static int sensor_name_to_enum(struct ar0234 *priv) { if (!strcmp(priv->sensor_name, "OWL1")) return OWL1; else if (!strcmp(priv->sensor_name, "OWL2")) return OWL2; else if (!strcmp(priv->sensor_name, "OWL3")) return OWL3; else if (!strcmp(priv->sensor_name, "OWL4")) return OWL4; else if (!strcmp(priv->sensor_name, "HAWK1")) return HAWK1; else if (!strcmp(priv->sensor_name, "HAWK2")) return HAWK2; else if (!strcmp(priv->sensor_name, "HAWK3")) return HAWK3; else if (!strcmp(priv->sensor_name, "HAWK4")) return HAWK4; else pr_err("%s: Sensor name %s is not matching!!\n",__func__,priv->sensor_name); return -1; } static int ar0234_hawk_owl_i2ctrans(struct ar0234 *priv) { int err = 0; if(ar0234_hawk_link_check(priv) == 4) { err = ar0234_write_table(priv, hawk_owl_i2ctrans_table[sensor_name_to_enum(priv)]); } else if(ar0234_hawk_link_check(priv) == 3) { err = ar0234_write_table(priv, hawk_owl_i2ctrans_table[sensor_name_to_enum(priv)]); } else if(ar0234_hawk_link_check(priv) == 2) { err = ar0234_write_table(priv, hawk_owl_i2ctrans_table[sensor_name_to_enum(priv)]); } else if(ar0234_hawk_link_check(priv) == 1) { err = ar0234_write_table(priv, hawk_owl_i2ctrans_table[sensor_name_to_enum(priv)]); } return err; } static int hawk_owl_ser_i2c_trans(struct camera_common_data *s_data) { int err = 0; struct ar0234 *priv = (struct ar0234 *) s_data->priv; struct device *dev = s_data->dev; static int haw_flag = 0, owl_flag = 0; /* Serializer i2c address trans */ if(ar0234_hawk_link_check(priv) == 4) { if ((1 == priv->channel) && !owl_flag) { err = ar0234_write_table(priv, i2c_address_trans_owl_Quad_ser); if (err) { dev_err(dev,"%s: Failed to do i2c address trans..\n",__func__); } else { dev_info(dev,"%s: Successfully done I2c address trans..\n",__func__); } owl_flag++; } else if ((!priv->channel) && !haw_flag) { err = ar0234_write_table(priv, i2c_address_trans_hawk_Quad_ser); if (err) { dev_err(dev,"%s: Failed to do i2c address trans..\n",__func__); } else { dev_info(dev,"%s: Successfully done I2c address trans..\n",__func__); } haw_flag++; } } else if(ar0234_hawk_link_check(priv) == 3) { if ((1 == priv->channel) && !owl_flag) { err = ar0234_write_table(priv, i2c_address_trans_owl_Triple_ser); if (err) { dev_err(dev,"%s: Failed to do i2c address trans..\n",__func__); } else { dev_info(dev,"%s: Successfully done I2c address trans..\n",__func__); } owl_flag++; } else if ((!priv->channel) && !haw_flag) { err = ar0234_write_table(priv, i2c_address_trans_hawk_Triple_ser); if (err) { dev_err(dev,"%s: Failed to do i2c address trans..\n",__func__); } else { dev_info(dev,"%s: Successfully done I2c address trans..\n",__func__); } haw_flag++; } } else if(ar0234_hawk_link_check(priv) == 2) { if ((1 == priv->channel) && !owl_flag) { err = ar0234_write_table(priv, i2c_address_trans_owl_Dual_ser); if (err) { dev_err(dev,"%s: Failed to do i2c address trans..\n",__func__); } else { dev_info(dev,"%s: Successfully done I2c address trans..\n",__func__); } owl_flag++; } else if ((!priv->channel) && !haw_flag) { err = ar0234_write_table(priv, i2c_address_trans_hawk_Dual_ser); if (err) { dev_err(dev,"%s: Failed to do i2c address trans..\n",__func__); } else { dev_info(dev,"%s: Successfully done I2c address trans..\n",__func__); } haw_flag++; } } else if(ar0234_hawk_link_check(priv) == 1) { if ((1 == priv->channel) && !owl_flag) { err = ar0234_write_table(priv, i2c_address_trans_owl_Single_ser); if (err) { dev_err(dev,"%s: Failed to do i2c address trans..\n",__func__); } else { dev_info(dev,"%s: Successfully done I2c address trans..\n",__func__); } owl_flag++; } else if ((!priv->channel) && !haw_flag) { err = ar0234_write_table(priv, i2c_address_trans_hawk_Single_ser); if (err) { dev_err(dev,"%s: Failed to do i2c address trans..\n",__func__); } else { dev_info(dev,"%s: Successfully done I2c address trans..\n",__func__); } haw_flag++; } } return err; } 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; struct ar0234 *priv = (struct ar0234 *) s_data->priv; 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); pw->state = SWITCH_ON; /*i2c address trans for Hawk & Owl*/ err = ar0234_hawk_owl_i2ctrans(priv); return err; } 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_err(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_err(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_err(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; 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_err(&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: tmp = kmalloc(sizeof(LiEeprom_Content_Struct), GFP_KERNEL); if (tmp == NULL) return -ENOMEM; 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)); 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(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 pwr_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); if (board_priv_pdata->reset_gpio > 0) gpio_direction_output(board_priv_pdata->reset_gpio, 1); else dev_dbg(dev, "failed to read reset_gpio\n"); board_priv_pdata->pwdn_gpio = of_get_named_gpio(node, "pwdn-gpios", 0); if (board_priv_pdata->pwdn_gpio > 0) gpio_direction_output(board_priv_pdata->pwdn_gpio, 1); else dev_dbg(dev, "failed to read pwdn_gpio\n"); pwr_gpio = of_get_named_gpio(node, "pwr-gpios", 0); if (pwr_gpio > 0) gpio_direction_output(pwr_gpio, 1); else dev_dbg(dev, "failed to read pwr_gpio\n"); board_priv_pdata->has_eeprom = of_property_read_bool(node, "has-eeprom"); if (board_priv_pdata->has_eeprom) { err = of_property_read_u32(node, "eeprom-addr", &board_priv_pdata->eeprom_id_addr); if (err) dev_err(dev, "Failed to read eeprom addr\n"); } 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 = 0; 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_err(dev, "%s: mode index:%d\n", __func__,s_data->mode_prop_idx); /* Moved the sensor mode table write during probe time, to reduce Stream on time */ 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_err(&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)); /* assign the EEPROM addrs which is read from DT */ priv->eeprom[i].brd.addr = pdata->eeprom_id_addr + i; #if defined(NV_I2C_NEW_CLIENT_DEVICE_PRESENT) /* Linux 5.10 */ priv->eeprom[i].i2c_client = i2c_new_client_device( priv->eeprom[i].adap, &priv->eeprom[i].brd); #else priv->eeprom[i].i2c_client = i2c_new_device( priv->eeprom[i].adap, &priv->eeprom[i].brd); #endif if (!priv->eeprom[i].i2c_client) { pr_err("%s: Failed to probe EEPORM at addr = 0x%x \n", __func__, priv->eeprom[i].brd.addr); return 0; } 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; } } #if DEBUG /* Print added, Remove later */ for (i = 0; i < AR0234_EEPROM_BLOCK_SIZE; i+=8) { pr_err("%02x %02x %02x %02x %02x %02x %02x %02x",priv->eeprom_buf[i+0],\ priv->eeprom_buf[i+1],priv->eeprom_buf[i+2],priv->eeprom_buf[i+3],\ priv->eeprom_buf[i+4],priv->eeprom_buf[i+5],priv->eeprom_buf[i+6], priv->eeprom_buf[i+7]); pr_err("\n"); } #endif 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_err(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; } 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_hawk_owl_EEPROM_address_trans(struct ar0234 *priv) { int err = 0; if(ar0234_hawk_link_check(priv) == 4) { err = ar0234_write_table(priv, hawk_owl_eeprom_i2ctrans_table[sensor_name_to_enum(priv)]); } else if(ar0234_hawk_link_check(priv) == 3) { err = ar0234_write_table(priv, hawk_owl_eeprom_i2ctrans_table[sensor_name_to_enum(priv)]); } else if(ar0234_hawk_link_check(priv) == 2) { err = ar0234_write_table(priv, hawk_owl_eeprom_i2ctrans_table[sensor_name_to_enum(priv)]); } else if(ar0234_hawk_link_check(priv) == 1) { err = ar0234_write_table(priv, hawk_owl_eeprom_i2ctrans_table[sensor_name_to_enum(priv)]); } return err; } static int max96712_fsync_program(const struct index_reg_8 table[], int channel) { int ret = 0; int i = 0; int retry = 5; while (table[i].source != 0x00) { retry_serdes: ret = max96712_write_reg_Dser(table[i].source, channel, table[i].addr, (u8)table[i].val); if (ret && (table[i].addr != 0x0000)) { retry--; if (retry > 0) { pr_err("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 ret; } static int owl_internal_fsync(void) { int err = 0; switch(owl_links) { case QUAD_LINK: err = max96712_fsync_program(Owl_QuadLink_Dser_InFsync, OWL_CHANNEL); if (err) pr_err("%s: Failed to set internal fsync for OWL\n",__func__); else pr_info("%s: Successfully set internal fsync for OWL\n",__func__); break; case TRIPLE_LINK: err = max96712_fsync_program(Owl_TripleLink_Dser_InFsync, OWL_CHANNEL); if (err) pr_err("%s: Failed to set internal fsync for OWL\n",__func__); else pr_info("%s: Successfully set internal fsync for OWL\n",__func__); break; case DUAL_LINK: err = max96712_fsync_program(Owl_DualLink_Dser_InFsync, OWL_CHANNEL); if (err) pr_err("%s: Failed to set internal fsync for OWL\n",__func__); else pr_info("%s: Successfully set internal fsync for OWL\n",__func__); break; case SINGLE_LINK: err = max96712_fsync_program(Owl_SingleLink_Dser_InFsync, OWL_CHANNEL); if (err) pr_err("%s: Failed to set internal fsync for OWL\n",__func__); else pr_info("%s: Successfully set internal fsync for OWL\n",__func__); break; default: pr_err("%s: not matching to any link type %d\n", __func__, owl_links); break; } return err; } static int owl_external_fsync(void) { int err = 0; switch(owl_links) { case QUAD_LINK: err = max96712_fsync_program(Owl_QuadLink_Dser_ExFsync, OWL_CHANNEL); if (err) pr_err("%s: Failed to set external fsync for OWL\n",__func__); else pr_info("%s: Successfully set external fsync for OWL\n",__func__); break; case TRIPLE_LINK: err = max96712_fsync_program(Owl_TripleLink_Dser_ExFsync, OWL_CHANNEL); if (err) pr_err("%s: Failed to set external fsync for OWL\n",__func__); else pr_info("%s: Successfully set external fsync for OWL\n",__func__); break; case DUAL_LINK: err = max96712_fsync_program(Owl_DualLink_Dser_ExFsync, OWL_CHANNEL); if (err) pr_err("%s: Failed to set external fsync for OWL\n",__func__); else pr_info("%s: Successfully set external fsync for OWL\n",__func__); break; case SINGLE_LINK: err = max96712_fsync_program(Owl_SingleLink_Dser_ExFsync, OWL_CHANNEL); if (err) pr_err("%s: Failed to set external fsync for OWL\n",__func__); else pr_info("%s: Successfully set external fsync for OWL\n",__func__); break; default: pr_err("%s: not matching to any link type %d\n", __func__, owl_links); break; } return err; } static int hawk_internal_fsync(void) { int err = 0; switch(hawk_links) { case QUAD_LINK: err = max96712_fsync_program(Hawk_QuadLink_Dser_InFsync, HAWK_CHANNEL); if (err) pr_err("%s: Failed to set internal fsync for HAWK\n",__func__); else pr_info("%s: Successfully set internal fsync for HAWK\n",__func__); break; case TRIPLE_LINK: err = max96712_fsync_program(Hawk_TripleLink_Dser_InFsync, HAWK_CHANNEL); if (err) pr_err("%s: Failed to set internal fsync for HAWK\n",__func__); else pr_info("%s: Successfully set internal fsync for HAWK\n",__func__); break; case DUAL_LINK: err = max96712_fsync_program(Hawk_DualLink_Dser_InFsync, HAWK_CHANNEL); if (err) pr_err("%s: Failed to set internal fsync for HAWK\n",__func__); else pr_info("%s: Successfully set internal fsync for HAWK\n",__func__); break; case SINGLE_LINK: err = max96712_fsync_program(Hawk_SingleLink_Dser_InFsync, HAWK_CHANNEL); if (err) pr_err("%s: Failed to set internal fsync for HAWK\n",__func__); else pr_info("%s: Successfully set internal fsync for HAWK\n",__func__); break; default: pr_err("%s: not matching to any link type %d\n", __func__, hawk_links); break; } return err; } static int hawk_external_fsync(void) { int err = 0; switch (hawk_links) { case QUAD_LINK: err = max96712_fsync_program(Hawk_QuadLink_Dser_ExFsync, HAWK_CHANNEL); if (err) pr_err("%s: Failed to set external fsync for HAWK\n",__func__); else pr_info("%s: Successfully set external fsync for HAWK\n",__func__); break; case TRIPLE_LINK: err = max96712_fsync_program(Hawk_TripleLink_Dser_ExFsync, HAWK_CHANNEL); if (err) pr_err("%s: Failed to set external fsync for HAWK\n",__func__); else pr_info("%s: Successfully set external fsync for HAWK\n",__func__); break; case DUAL_LINK: err = max96712_fsync_program(Hawk_DualLink_Dser_ExFsync, HAWK_CHANNEL); if (err) pr_err("%s: Failed to set external fsync for HAWK\n",__func__); else pr_info("%s: Successfully set external fsync for HAWK\n",__func__); break; case SINGLE_LINK: err = max96712_fsync_program(Hawk_SingleLink_Dser_ExFsync, HAWK_CHANNEL); if (err) pr_err("%s: Failed to set external fsync for HAWK\n",__func__); else pr_info("%s: Successfully set external fsync for HAWK\n",__func__); break; default: pr_err("%s: not matching to any link type %d\n", __func__, hawk_links); break; } return err; } int Hawk_Owl_Fsync_program(int fsync_type) { int err = 0; switch (fsync_type) { case EXTERNAL_FSYNC: err = owl_external_fsync(); if (err) pr_err("%s: Failed to set external fsync for OWLs!! err = %d\n", __func__,err); err = hawk_external_fsync(); if (err) pr_err("%s: Failed to set external fsync for HAWKS!! err = %d\n", __func__,err); break; case INTERNAL_FSYNC: err = owl_internal_fsync(); if (err) pr_err("%s: Failed to set internal fsync for OWLs!! err = %d\n", __func__,err); err = hawk_internal_fsync(); if (err) pr_err("%s: Failed to set internal fsync for HAWKS!! err = %d\n", __func__,err); break; default: pr_err("%s: not supported fsync_type = %d\n", __func__, fsync_type); break; } return err; } EXPORT_SYMBOL(Hawk_Owl_Fsync_program); static int ar0234_hawk_owl_deser_ser_program(struct ar0234 *priv) { int err = 0; static int hawk_flag = 0, owl_flag = 0; struct camera_common_data *s_data = priv->s_data; struct device *dev = s_data->dev; if(ar0234_hawk_link_check(priv) == 4) { if ((1 == priv->channel) && (!owl_flag)) { err = ar0234_write_table(priv, mode_table[AR0234_MODE_OWL_QUADLINK_DSER_SER]); if (err) dev_err(dev,"%s: Failed to do OWL mode table..\n",__func__); else dev_info(dev,"%s: Successfully done OWL mode table ..\n",__func__); owl_flag++; owl_links = QUAD_LINK; } if ((!priv->channel) && !hawk_flag) { err = ar0234_write_table(priv, mode_table[AR0234_MODE_HAWK_QUADLINK_DSER_SER]); if (err) dev_err(dev,"%s: Failed to do Hawk mode table..\n",__func__); else dev_info(dev,"%s: Successfully done Hawk mode table ..\n",__func__); hawk_flag++; hawk_links = QUAD_LINK; } } else if(ar0234_hawk_link_check(priv) == 3) { if ((1 == priv->channel) && (!owl_flag)) { err = ar0234_write_table(priv, mode_table[AR0234_MODE_OWL_TRIPLELINK_DSER_SER]); if (err) dev_err(dev,"%s: Failed to do OWL mode table..\n",__func__); else dev_info(dev,"%s: Successfully done OWL mode table ..\n",__func__); owl_flag++; owl_links = TRIPLE_LINK; } if ((!priv->channel) && !hawk_flag) { err = ar0234_write_table(priv, mode_table[AR0234_MODE_HAWK_TRIPLELINK_DSER_SER]); if (err) dev_err(dev,"%s: Failed to do Hawk mode table..\n",__func__); else dev_info(dev,"%s: Successfully done Hawk mode table ..\n",__func__); hawk_flag++; hawk_links = TRIPLE_LINK; } } else if(ar0234_hawk_link_check(priv) == 2) { if ((1 == priv->channel) && (!owl_flag)) { err = ar0234_write_table(priv, mode_table[AR0234_MODE_OWL_DUALLINK_DSER_SER]); if (err) dev_err(dev,"%s: Failed to do OWL mode table..\n",__func__); else dev_info(dev,"%s: Successfully done OWL mode table ..\n",__func__); owl_flag++; owl_links = DUAL_LINK; } if ((!priv->channel) && !hawk_flag) { err = ar0234_write_table(priv, mode_table[AR0234_MODE_HAWK_DUALLINK_DSER_SER]); if (err) dev_err(dev,"%s: Failed to do Hawk mode table..\n",__func__); else dev_info(dev,"%s: Successfully done Hawk mode table ..\n",__func__); hawk_flag++; hawk_links = DUAL_LINK; } } else if(ar0234_hawk_link_check(priv) == 1) { if ((1 == priv->channel) && (!owl_flag)) { err = ar0234_write_table(priv, mode_table[AR0234_MODE_OWL_SINGLELINK_DSER_SER]); if (err) dev_err(dev,"%s: Failed to do OWL mode table..\n",__func__); else dev_info(dev,"%s: Successfully done OWL mode table ..\n",__func__); owl_flag++; owl_links = SINGLE_LINK; } if ((!priv->channel) && !hawk_flag) { err = ar0234_write_table(priv, mode_table[AR0234_MODE_HAWK_SINGLELINK_DSER_SER]); if (err) dev_err(dev,"%s: Failed to do Hawk mode table..\n",__func__); else dev_info(dev,"%s: Successfully done Hawk mode table ..\n",__func__); hawk_flag++; hawk_links = SINGLE_LINK; } } 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) { 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_info(dev, "%s: channel %d\n", __func__, priv->channel); err = of_property_read_string(node, "sync_sensor", &priv->sensor_name); if (err) dev_err(dev, "sync_sensor not found\n"); dev_info(dev,"%s: sync_sensor = %s \n", __func__, priv->sensor_name); 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); /* Serializer i2c address trans */ err = hawk_owl_ser_i2c_trans(tc_dev->s_data); if (err) { dev_err(&client->dev,"Failed to enable gpio/ to do serializer i2c address trans\n"); goto un_register; } err = ar0234_power_on(tc_dev->s_data); if (err) { dev_err(&client->dev,"Failed to power on\n"); goto un_register; } msleep(100); err = ar0234_write_table(priv, mode_table[AR0234_MODE_STOP_STREAM]); if (err) { dev_err(&client->dev,"ar0234 detect error\n"); goto un_register; } msleep(100); /* Deser/ser programming */ err = ar0234_hawk_owl_deser_ser_program(priv); if (err) { dev_err(&client->dev,"Failed to program deser/ser\n"); goto un_register; } /* i2c address trans for EEPROM access */ err = ar0234_hawk_owl_EEPROM_address_trans(priv); if (err) { dev_err(&client->dev,"Failed to do EEPROM i2c address trans\n"); goto un_register; } err = ar0234_board_setup(priv); if (err) { dev_err(dev, "board setup failed\n"); goto release_eeprom; } /* Un-register i2c client for EEPROM, * so we can re-use i2c address for 2nd sensor of HAWK module */ ar0234_eeprom_device_release(priv); /* re-i2c address trans for Hawk & Owl sensors */ err = ar0234_hawk_owl_i2ctrans(priv); if (err) { dev_err(dev, "failed to do re-i2c address translation\n"); goto un_register; } /* Set Internal fsync as default mode */ err = Hawk_Owl_Fsync_program(INTERNAL_FSYNC); if (err) { dev_err(dev, "Failed to set Internal Fsync !!\n"); } /* Fix-me: This change is temp fix.*/ /* Once other modes are added will move back to set_mode call */ /* Write sensor mode table */ err = ar0234_write_table(priv, mode_table[0]); if (err) dev_err(dev, "Failed to write mode table\n"); err = tegracam_v4l2subdev_register(tc_dev, true); if (err) { dev_err(dev, "tegra camera subdev registration failed\n"); goto un_register; } dev_err(&client->dev, "Detected AR0234 sensor\n"); return 0; release_eeprom: ar0234_eeprom_device_release(priv); un_register: tegracam_device_unregister(tc_dev); return err; } #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 = (struct ar0234 *)s_data->priv; if (!s_data) #if defined(NV_I2C_DRIVER_STRUCT_REMOVE_RETURN_TYPE_INT) /* Linux 6.1 */ return -EINVAL; #else return; #endif 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("Praveen AC