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 <shaskumar@nvidia.com>
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 <arai@nvidia.com>
Reviewed-by: Bibek Basu <bbasu@nvidia.com>
Tested-by: Anubhav Rai <arai@nvidia.com>
GVS: Gerrit_Virtual_Submit <buildbot_gerritrpt@nvidia.com>
This commit is contained in:
Shashank Kumar
2023-04-02 16:07:56 +00:00
committed by mobile promotions
parent 2bd1339e63
commit c56189df5d
6 changed files with 386 additions and 51 deletions

View File

@@ -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,
};

View File

@@ -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,
};

View File

@@ -12,6 +12,8 @@
#include <media/tegracam_utils.h>
#include <linux/arm64-barrier.h>
#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,

View File

@@ -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");

View File

@@ -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);

View File

@@ -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__ */