From c56189df5dc3e29d99937cc7bca411ca7eb9e5ce Mon Sep 17 00:00:00 2001 From: Shashank Kumar Date: Sun, 2 Apr 2023 16:07:56 +0000 Subject: [PATCH] drivers: media: add alternating exposure support - update ar0234 driver to support ar0234 sensor hardware provided support for capturing frames using alternating exposure time and analog gain settings through automatic context switching - added new v4l2 control having control id TEGRA_CAMERA_CID_ALTERNATING_EXPOSURE for configuring alternating exposure settings - other changes to support aforementioned changes Bug 4064517 Jira L4T-4501 Change-Id: Id1d3d745345836436b16da1fd00cc97f2e3b5553 Signed-off-by: Shashank Kumar Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2881141 (cherry picked from commit e973475525c9a7a99552282316e06126b8fff625) Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/2987067 Reviewed-by: Anubhav Rai Reviewed-by: Bibek Basu Tested-by: Anubhav Rai GVS: Gerrit_Virtual_Submit --- drivers/media/i2c/nv_ar0234.c | 138 ++++++++++++++--- drivers/media/i2c/nv_hawk_owl.c | 142 +++++++++++++++--- .../platform/tegra/camera/tegracam_ctrls.c | 135 +++++++++++++++-- .../platform/tegra/camera/tegracam_v4l2.c | 6 +- include/media/camera_common.h | 3 + include/media/tegra-v4l2-camera.h | 13 ++ 6 files changed, 386 insertions(+), 51 deletions(-) diff --git a/drivers/media/i2c/nv_ar0234.c b/drivers/media/i2c/nv_ar0234.c index 569c1b5f..13903bf3 100644 --- a/drivers/media/i2c/nv_ar0234.c +++ b/drivers/media/i2c/nv_ar0234.c @@ -40,6 +40,8 @@ extern int max96712_read_reg_Dser(int slaveAddr, int channel, #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[] = { @@ -57,8 +59,11 @@ static const u32 ctrl_cid_list[] = { TEGRA_CAMERA_CID_HDR_EN, TEGRA_CAMERA_CID_SENSOR_MODE_ID, TEGRA_CAMERA_CID_STEREO_EEPROM, + TEGRA_CAMERA_CID_ALTERNATING_EXPOSURE, }; +const u16 alternating_exposure_cfg_size = sizeof(struct alternating_exposure_cfg); + // Coefficients as per distortion model (wide FOV) being used struct fisheye_lens_distortion_coeff { // Radial coefficients count @@ -260,6 +265,31 @@ static inline void ar0234_get_gain_reg(ar0234_reg *regs, } +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); @@ -509,26 +539,8 @@ static int ar0234_set_gain(struct tegracam_device *tc_dev, s64 val) u16 gain = (u16)val; u16 gain_reg = 0; - 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; - } + ar0234_compute_gain_reg(&gain_reg, &gain, val); - if (gain > AR0234_MAX_GAIN_REG) - gain = AR0234_MAX_GAIN_REG; ar0234_get_gain_reg(reg_list, gain_reg); err = ar0234_write_reg(s_data, reg_list[0].addr, reg_list[0].val); @@ -691,16 +703,102 @@ static int ar0234_fill_eeprom(struct tegracam_device *tc_dev, 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)}, + .compound_ctrl_size = {sizeof(struct NvCamSyncSensorCalibData), alternating_exposure_cfg_size}, .set_gain = ar0234_set_gain, .set_exposure = ar0234_set_exposure, .set_exposure_short = ar0234_set_exposure, .set_frame_rate = ar0234_set_frame_rate, .set_group_hold = ar0234_set_group_hold, + .set_alternating_exposure = ar0234_set_alternating_exposure, .fill_string_ctrl = ar0234_fill_string_ctrl, .fill_compound_ctrl = ar0234_fill_eeprom, }; diff --git a/drivers/media/i2c/nv_hawk_owl.c b/drivers/media/i2c/nv_hawk_owl.c index 53bac583..fc64dfaf 100644 --- a/drivers/media/i2c/nv_hawk_owl.c +++ b/drivers/media/i2c/nv_hawk_owl.c @@ -40,6 +40,8 @@ extern int max96712_read_reg_Dser(int slaveAddr,int channel, #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; @@ -59,7 +61,11 @@ static const u32 ctrl_cid_list[] = { TEGRA_CAMERA_CID_HDR_EN, TEGRA_CAMERA_CID_SENSOR_MODE_ID, TEGRA_CAMERA_CID_STEREO_EEPROM, + TEGRA_CAMERA_CID_ALTERNATING_EXPOSURE, }; + +const u16 alternating_exposure_cfg_size = sizeof(struct alternating_exposure_cfg); + // Coefficients as per distortion model (wide FOV) being used typedef struct { @@ -256,6 +262,32 @@ static inline void ar0234_get_gain_reg(ar0234_reg *regs, 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, @@ -619,25 +651,9 @@ static int ar0234_set_gain(struct tegracam_device *tc_dev, s64 val) int err; u16 gain = (u16)val; u16 gain_reg = 0; - 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 > AR0234_MAX_GAIN_REG) - gain = AR0234_MAX_GAIN_REG; + + 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); @@ -772,16 +788,102 @@ static int ar0234_fill_eeprom(struct tegracam_device *tc_dev, 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)}, + .compound_ctrl_size = {sizeof(NvCamSyncSensorCalibData), alternating_exposure_cfg_size}, .set_gain = ar0234_set_gain, .set_exposure = ar0234_set_exposure, .set_exposure_short = ar0234_set_exposure, .set_frame_rate = ar0234_set_frame_rate, .set_group_hold = ar0234_set_group_hold, + .set_alternating_exposure = ar0234_set_alternating_exposure, .fill_string_ctrl = ar0234_fill_string_ctrl, .fill_compound_ctrl = ar0234_fill_eeprom, }; diff --git a/drivers/media/platform/tegra/camera/tegracam_ctrls.c b/drivers/media/platform/tegra/camera/tegracam_ctrls.c index a1b8ed7a..98bddf27 100644 --- a/drivers/media/platform/tegra/camera/tegracam_ctrls.c +++ b/drivers/media/platform/tegra/camera/tegracam_ctrls.c @@ -12,6 +12,8 @@ #include #include +#define CTRL_U8_MIN 0 +#define CTRL_U8_MAX 0xFF #define CTRL_U32_MIN 0 #define CTRL_U32_MAX 0x7FFFFFFF #define CTRL_U64_MIN 0 @@ -46,6 +48,11 @@ static const u32 tegracam_override_cids[] = { }; #define NUM_OVERRIDE_CTRLS ARRAY_SIZE(tegracam_override_cids) +static const u32 tegracam_sync_cids[] = { + TEGRA_CAMERA_CID_ALTERNATING_EXPOSURE, +}; +#define NUM_SYNC_CTRLS ARRAY_SIZE(tegracam_sync_cids) + static struct v4l2_ctrl_config ctrl_cfg_list[] = { /* Do not change the name field for the controls! */ { @@ -165,6 +172,16 @@ static struct v4l2_ctrl_config ctrl_cfg_list[] = { .max = STEREO_EEPROM_SIZE, .step = 2, }, + { + .ops = &tegracam_ctrl_ops, + .id = TEGRA_CAMERA_CID_ALTERNATING_EXPOSURE, + .name = "Alternating Exposure", + .type = V4L2_CTRL_TYPE_U8, + .flags = V4L2_CTRL_FLAG_HAS_PAYLOAD, + .min = CTRL_U8_MIN, + .max = CTRL_U8_MAX, + .step = 1, + }, }; static int tegracam_get_ctrl_index(u32 cid) @@ -209,6 +226,9 @@ static int tegracam_get_compound_ctrl_size(u32 cid, case TEGRA_CAMERA_CID_STEREO_EEPROM: index = TEGRA_CAM_COMPOUND_CTRL_EEPROM_INDEX; break; + case TEGRA_CAMERA_CID_ALTERNATING_EXPOSURE: + index = TEGRA_CAM_COMPOUND_CTRL_ALT_EXP_INDEX; + break; default: return -EINVAL; } @@ -258,7 +278,8 @@ static int tegracam_setup_compound_ctrls(struct tegracam_device *tc_dev, for (i = 0; i < numctrls; i++) { struct v4l2_ctrl *ctrl = handler->ctrls[i]; - if (ctrl->type == V4L2_CTRL_COMPOUND_TYPES) { + if (ctrl->type == V4L2_CTRL_COMPOUND_TYPES && + ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY) { err = ops->fill_compound_ctrl(tc_dev, ctrl); if (err) return err; @@ -276,6 +297,7 @@ static int tegracam_set_ctrls(struct tegracam_ctrl_handler *handler, const struct tegracam_ctrl_ops *ops = handler->ctrl_ops; struct tegracam_device *tc_dev = handler->tc_dev; struct camera_common_data *s_data = tc_dev->s_data; + struct sensor_control_properties *ctrlprops = NULL; int err = 0; u32 status = 0; @@ -298,15 +320,22 @@ static int tegracam_set_ctrls(struct tegracam_ctrl_handler *handler, if (!status) return 0; + ctrlprops = + &s_data->sensor_props.sensor_modes[0].control_properties; + /* For controls that require sensor to be on */ switch (ctrl->id) { case TEGRA_CAMERA_CID_GAIN: + if (*ctrl->p_new.p_s64 == ctrlprops->min_gain_val - 1) + return 0; err = ops->set_gain(tc_dev, *ctrl->p_new.p_s64); break; case TEGRA_CAMERA_CID_FRAME_RATE: err = ops->set_frame_rate(tc_dev, *ctrl->p_new.p_s64); break; case TEGRA_CAMERA_CID_EXPOSURE: + if (*ctrl->p_new.p_s64 == ctrlprops->min_exp_time.val - 1) + return 0; err = ops->set_exposure(tc_dev, *ctrl->p_new.p_s64); break; case TEGRA_CAMERA_CID_EXPOSURE_SHORT: @@ -315,6 +344,10 @@ static int tegracam_set_ctrls(struct tegracam_ctrl_handler *handler, case TEGRA_CAMERA_CID_GROUP_HOLD: err = ops->set_group_hold(tc_dev, ctrl->val); break; + case TEGRA_CAMERA_CID_ALTERNATING_EXPOSURE: + err = ops->set_alternating_exposure(tc_dev, + (struct alternating_exposure_cfg *)ctrl->p_new.p); + break; default: pr_err("%s: unknown ctrl id.\n", __func__); return -EINVAL; @@ -411,6 +444,58 @@ static int tegracam_s_ctrl(struct v4l2_ctrl *ctrl) return 0; } +/* + * It is needed to synchronize the alternating exposure settings in sensor with the v4l2 control + * value every time sensor starts streaming. If we don't do this, then since the v4l2 control would + * retain the alternating exposure settings from the previous stream as its current value after + * the streaming stops, there would be a mismatch between the settings stored in v4l2 control's + * current value and the actual settings stored in sensor registers, which can result in the sensor + * driver missing a sensor programming for alternating exposure because of condition checks at the + * v4l2 layer which propagate the new settings to sensor driver only if the settings differ from + * the control's current value. + */ +int tegracam_ctrl_synchronize_ctrls(struct tegracam_ctrl_handler *hdl) +{ + struct tegracam_device *tc_dev = hdl->tc_dev; + struct device *dev = tc_dev->dev; + const struct tegracam_ctrl_ops *ops = hdl->ctrl_ops; + struct v4l2_ctrl_handler *ctrl_hdl = &hdl->ctrl_handler; + struct v4l2_ctrl *ctrl = NULL; + void *ptr = NULL; + int err = 0; + u32 id; + int i; + + if (ops == NULL) + return 0; + + for (i = 0; i < NUM_SYNC_CTRLS; i++) { + id = tegracam_sync_cids[i]; + ctrl = v4l2_ctrl_find(ctrl_hdl, tegracam_sync_cids[i]); + if (ctrl) { + switch (id) { + case TEGRA_CAMERA_CID_ALTERNATING_EXPOSURE: + ptr = ctrl->p_cur.p; + if (ops->set_alternating_exposure != NULL) + err = ops->set_alternating_exposure(tc_dev, + (struct alternating_exposure_cfg *)ptr); + break; + default: + dev_err(dev, "%s: synchronization of control with cid %x not implemented\n", + __func__, id); + return -EINVAL; + } + if (err) { + dev_err(dev, "%s: error while synchronizing control with cid %x\n", + __func__, id); + return err; + } + } + } + + return 0; +} + int tegracam_ctrl_set_overrides(struct tegracam_ctrl_handler *hdl) { struct v4l2_ext_controls ctrls; @@ -507,6 +592,14 @@ int tegracam_init_ctrl_ranges_by_mode( ctrlprops = &s_data->sensor_props.sensor_modes[modeidx].control_properties; +/* + * For gain and exposure we are allowing for the control to have a value one less than + * the minimum supported by sensor range. This value would represent the exposure/gain + * being left in an indeterminate state due to the sensor capturing using alternating + * exposures/gain settings immediately before this. This happens because once alternating + * exposure stops, the sensor is left with analog gain and exposure time settings + * corresponding to one of the two contexts, however we do not know which one. + */ for (i = 0; i < handler->numctrls; i++) { struct v4l2_ctrl *ctrl = handler->ctrls[i]; int err = 0; @@ -514,7 +607,7 @@ int tegracam_init_ctrl_ranges_by_mode( switch (ctrl->id) { case TEGRA_CAMERA_CID_GAIN: err = v4l2_ctrl_modify_range(ctrl, - ctrlprops->min_gain_val, + ctrlprops->min_gain_val - 1, ctrlprops->max_gain_val, ctrlprops->step_gain_val, ctrlprops->default_gain); @@ -528,7 +621,7 @@ int tegracam_init_ctrl_ranges_by_mode( break; case TEGRA_CAMERA_CID_EXPOSURE: err = v4l2_ctrl_modify_range(ctrl, - ctrlprops->min_exp_time.val, + ctrlprops->min_exp_time.val - 1, ctrlprops->max_exp_time.val, ctrlprops->step_exp_time.val, ctrlprops->default_exp_time.val); @@ -706,6 +799,19 @@ static int tegracam_check_ctrl_ops( TEGRA_CAMERA_CID_STEREO_EEPROM, ops) == 0) dev_err(dev, "Stereo EEPROM size not \ specified\n"); + else if (ops->fill_compound_ctrl == NULL) + dev_err(dev, + "Missing compound control implementation for TEGRA_CAMERA_CID_STEREO_EEPROM"); + else + compound_ops++; + break; + case TEGRA_CAMERA_CID_ALTERNATING_EXPOSURE: + if (tegracam_get_compound_ctrl_size( + TEGRA_CAMERA_CID_ALTERNATING_EXPOSURE, ops) == 0) + dev_err(dev, "TEGRA_CAMERA_CID_ALTERNATING_EXPOSURE control size unspecified\n"); + else if (ops->set_alternating_exposure == NULL) + dev_err(dev, + "Missing control implementation for TEGRA_CAMERA_CID_ALTERNATING_EXPOSURE"); else compound_ops++; break; @@ -728,13 +834,6 @@ static int tegracam_check_ctrl_ops( } } - if (compound_ops > 0) { - if (ops->fill_compound_ctrl == NULL) { - dev_err(dev, "Missing compound control implementation\n"); - compound_ops = 0; - } - } - /* Find missing default controls */ for (i = 0; i < TEGRACAM_DEF_CTRLS; i++) { switch (tegracam_def_cids[i]) { @@ -896,6 +995,19 @@ static int tegracam_check_ctrl_cids(struct tegracam_ctrl_handler *handler) } } + if (ops->set_alternating_exposure != NULL) { + if (tegracam_get_compound_ctrl_size( + TEGRA_CAMERA_CID_ALTERNATING_EXPOSURE, ops) > 0) { + if (!find_matching_cid(ops->ctrl_cid_list, + ops->numctrls, + TEGRA_CAMERA_CID_ALTERNATING_EXPOSURE)) { + dev_err(dev, + "Missing TEGRA_CAMERA_CID_ALTERNATING_EXPOSURE registration\n"); + errors_found++; + } + } + } + if (errors_found > 0) { dev_err(dev, "ERROR: %d controls implemented but not registered with framework\n", errors_found); @@ -914,6 +1026,7 @@ int tegracam_ctrl_handler_init(struct tegracam_ctrl_handler *handler) const struct tegracam_ctrl_ops *ops = handler->ctrl_ops; const u32 *cids = NULL; u32 numctrls = 0; + u8 compound_control_default_byte_value = 0xff; int i, j; int err = 0; @@ -961,6 +1074,8 @@ int tegracam_ctrl_handler_init(struct tegracam_ctrl_handler *handler) return -EINVAL; } ctrl_cfg->dims[0] = size; + ctrl_cfg->p_def = v4l2_ctrl_ptr_create( + (void *)&compound_control_default_byte_value); } ctrl = v4l2_ctrl_new_custom(&handler->ctrl_handler, diff --git a/drivers/media/platform/tegra/camera/tegracam_v4l2.c b/drivers/media/platform/tegra/camera/tegracam_v4l2.c index 02e08610..7441a401 100644 --- a/drivers/media/platform/tegra/camera/tegracam_v4l2.c +++ b/drivers/media/platform/tegra/camera/tegracam_v4l2.c @@ -62,7 +62,11 @@ static int v4l2sd_stream(struct v4l2_subdev *sd, int enable) goto error; } } - + err = tegracam_ctrl_synchronize_ctrls(s_data->tegracam_ctrl_hdl); + if (err) { + dev_err(&client->dev, "Error synchronizing controls during stream start\n"); + goto error; + } err = sensor_ops->start_streaming(tc_dev); if (err) { dev_err(&client->dev, "Error turning on streaming\n"); diff --git a/include/media/camera_common.h b/include/media/camera_common.h index fee3b31c..6b5ae91d 100644 --- a/include/media/camera_common.h +++ b/include/media/camera_common.h @@ -186,6 +186,8 @@ struct tegracam_ctrl_ops { int (*set_exposure_short)(struct tegracam_device *tc_dev, s64 val); int (*set_frame_rate)(struct tegracam_device *tc_dev, s64 val); int (*set_group_hold)(struct tegracam_device *tc_dev, bool val); + int (*set_alternating_exposure)(struct tegracam_device *tc_dev, + struct alternating_exposure_cfg *val); int (*fill_string_ctrl)(struct tegracam_device *tc_dev, struct v4l2_ctrl *ctrl); int (*fill_compound_ctrl)(struct tegracam_device *tc_dev, @@ -353,6 +355,7 @@ const struct camera_common_colorfmt *camera_common_find_pixelfmt( unsigned int pix_fmt); /* common control layer init */ +int tegracam_ctrl_synchronize_ctrls(struct tegracam_ctrl_handler *handler); int tegracam_ctrl_set_overrides(struct tegracam_ctrl_handler *handler); int tegracam_ctrl_handler_init(struct tegracam_ctrl_handler *handler); int tegracam_init_ctrl_ranges(struct tegracam_ctrl_handler *handler); diff --git a/include/media/tegra-v4l2-camera.h b/include/media/tegra-v4l2-camera.h index 936b4e13..3521ed37 100644 --- a/include/media/tegra-v4l2-camera.h +++ b/include/media/tegra-v4l2-camera.h @@ -27,6 +27,7 @@ #define TEGRA_CAMERA_CID_FRAME_RATE (TEGRA_CAMERA_CID_BASE+11) #define TEGRA_CAMERA_CID_EXPOSURE_SHORT (TEGRA_CAMERA_CID_BASE+12) #define TEGRA_CAMERA_CID_STEREO_EEPROM (TEGRA_CAMERA_CID_BASE+13) +#define TEGRA_CAMERA_CID_ALTERNATING_EXPOSURE (TEGRA_CAMERA_CID_BASE+14) #define TEGRA_CAMERA_CID_SENSOR_CONFIG (TEGRA_CAMERA_CID_BASE+50) #define TEGRA_CAMERA_CID_SENSOR_MODE_BLOB (TEGRA_CAMERA_CID_BASE+51) @@ -74,6 +75,7 @@ #define TEGRA_CAM_MAX_COMPOUND_CONTROLS 4 #define TEGRA_CAM_COMPOUND_CTRL_EEPROM_INDEX 0 +#define TEGRA_CAM_COMPOUND_CTRL_ALT_EXP_INDEX 1 #define CSI_PHY_MODE_DPHY 0 #define CSI_PHY_MODE_CPHY 1 @@ -172,4 +174,15 @@ struct sensor_mode_properties { (sizeof(struct sensor_blob) / sizeof(__u32)) #define SENSOR_CTRL_BLOB_SIZE \ (sizeof(struct sensor_blob) / sizeof(__u32)) + +struct alternating_exposure_cfg { + __u8 enable; + __s64 exp_time_0; + __s64 exp_time_1; + __s64 analog_gain_0; + __s64 analog_gain_1; +}; + +#define ALTERNATING_EXPOSURE_CID_SIZE \ + sizeof(struct alternating_exposure_cfg) #endif /* __TEGRA_V4L2_CAMERA__ */