Files
linux-nv-oot/drivers/media/platform/tegra/camera/tegracam_ctrls.c
Praveen AC 4e63d195c6 drivers:media: Fix imx390 & imx185 probe issue.
Due to "mingain - 1" & "minexp - 1" during probe time observing
out of range whenever gain or exp is set "0" as min in DT.
Instead doing "maxgain + 1" & "maxexp + 1" to fix the probe.

Bug 4142996
Bug 4189361
Bug 4386912

Change-Id: I103e87b293079dadcd16b91f8e329ec9f938208c
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3020349
Tested-by: Praveen AC <pac@nvidia.com>
Reviewed-by: Praveen AC <pac@nvidia.com>
Reviewed-by: Narendra Kondapalli <nkondapalli@nvidia.com>
Reviewed-by: Ankur Pawar <ankurp@nvidia.com>
Reviewed-by: Anubhav Rai <arai@nvidia.com>
Reviewed-by: Amulya Yarlagadda <ayarlagadda@nvidia.com>
GVS: Gerrit_Virtual_Submit <buildbot_gerritrpt@nvidia.com>
2023-11-22 10:18:55 -08:00

1142 lines
29 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* tegracam_ctrls - control framework for tegra camera drivers
*
* Copyright (c) 2017-2022, NVIDIA CORPORATION. All rights reserved.
*/
#include <linux/nospec.h>
#include <linux/types.h>
#include <media/tegra-v4l2-camera.h>
#include <media/camera_common.h>
#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
#define CTRL_U64_MAX 0x7FFFFFFFFFFFFFFFLL
#define CTRL_S32_MIN 0x80000000
#define CTRL_S32_MAX 0x7FFFFFFF
#define CTRL_S64_MIN 0x8000000000000000LL
#define CTRL_S64_MAX 0x7FFFFFFFFFFFFFFFLL
#define CTRL_MAX_STR_SIZE 4096
#define STEREO_EEPROM_SIZE 4096
#define TEGRACAM_DEF_CTRLS 1
static int tegracam_s_ctrl(struct v4l2_ctrl *ctrl);
static const struct v4l2_ctrl_ops tegracam_ctrl_ops = {
.s_ctrl = tegracam_s_ctrl,
};
static const u32 tegracam_def_cids[] = {
TEGRA_CAMERA_CID_GROUP_HOLD,
};
/*
* For auto control, the states of the previous controls must
* be applied to get optimal quality faster. List all the controls
* which must be overriden
*/
static const u32 tegracam_override_cids[] = {
TEGRA_CAMERA_CID_GAIN,
TEGRA_CAMERA_CID_EXPOSURE,
TEGRA_CAMERA_CID_FRAME_RATE,
};
#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! */
{
.ops = &tegracam_ctrl_ops,
.id = TEGRA_CAMERA_CID_GAIN,
.name = "Gain",
.type = V4L2_CTRL_TYPE_INTEGER64,
.flags = V4L2_CTRL_FLAG_SLIDER,
.min = CTRL_U64_MIN,
.max = CTRL_U64_MAX,
.def = CTRL_U64_MIN,
.step = 1,
},
{
.ops = &tegracam_ctrl_ops,
.id = TEGRA_CAMERA_CID_EXPOSURE,
.name = "Exposure",
.type = V4L2_CTRL_TYPE_INTEGER64,
.flags = V4L2_CTRL_FLAG_SLIDER,
.min = CTRL_U64_MIN,
.max = CTRL_U64_MAX,
.def = CTRL_U64_MIN,
.step = 1,
},
{
.ops = &tegracam_ctrl_ops,
.id = TEGRA_CAMERA_CID_EXPOSURE_SHORT,
.name = "Exposure Short",
.type = V4L2_CTRL_TYPE_INTEGER64,
.flags = V4L2_CTRL_FLAG_SLIDER,
.min = CTRL_U64_MIN,
.max = CTRL_U64_MAX,
.def = CTRL_U64_MIN,
.step = 1,
},
{
.ops = &tegracam_ctrl_ops,
.id = TEGRA_CAMERA_CID_FRAME_RATE,
.name = "Frame Rate",
.type = V4L2_CTRL_TYPE_INTEGER64,
.flags = V4L2_CTRL_FLAG_SLIDER,
.min = CTRL_U64_MIN,
.max = CTRL_U64_MAX,
.def = CTRL_U64_MIN,
.step = 1,
},
{
.ops = &tegracam_ctrl_ops,
.id = TEGRA_CAMERA_CID_GROUP_HOLD,
.name = "Group Hold",
.type = V4L2_CTRL_TYPE_BOOLEAN,
.flags = V4L2_CTRL_FLAG_EXECUTE_ON_WRITE,
.min = 0,
.max = 1,
.def = 0,
.step = 1,
},
{
.ops = &tegracam_ctrl_ops,
.id = TEGRA_CAMERA_CID_EEPROM_DATA,
.name = "EEPROM Data",
.type = V4L2_CTRL_TYPE_STRING,
.flags = V4L2_CTRL_FLAG_READ_ONLY,
.min = 0,
.max = CTRL_MAX_STR_SIZE,
.step = 2,
},
{
.ops = &tegracam_ctrl_ops,
.id = TEGRA_CAMERA_CID_FUSE_ID,
.name = "Fuse ID",
.type = V4L2_CTRL_TYPE_STRING,
.flags = V4L2_CTRL_FLAG_READ_ONLY,
.min = 0,
.max = CTRL_MAX_STR_SIZE,
.step = 2,
},
{
.ops = &tegracam_ctrl_ops,
.id = TEGRA_CAMERA_CID_SENSOR_MODE_ID,
.name = "Sensor Mode",
.type = V4L2_CTRL_TYPE_INTEGER64,
.flags = V4L2_CTRL_FLAG_SLIDER,
.min = CTRL_U32_MIN,
.max = CTRL_U32_MAX,
.def = CTRL_U32_MIN,
.step = 1,
},
{
.ops = &tegracam_ctrl_ops,
.id = TEGRA_CAMERA_CID_HDR_EN,
.name = "HDR enable",
.type = V4L2_CTRL_TYPE_INTEGER_MENU,
.min = 0,
.max = ARRAY_SIZE(switch_ctrl_qmenu) - 1,
.menu_skip_mask = 0,
.def = 0,
.qmenu_int = switch_ctrl_qmenu,
},
{
.ops = &tegracam_ctrl_ops,
.id = TEGRA_CAMERA_CID_OTP_DATA,
.name = "OTP Data",
.type = V4L2_CTRL_TYPE_STRING,
.flags = V4L2_CTRL_FLAG_READ_ONLY,
.min = 0,
.max = CTRL_MAX_STR_SIZE,
.step = 2,
},
{
.ops = &tegracam_ctrl_ops,
.id = TEGRA_CAMERA_CID_STEREO_EEPROM,
.name = "Stereo EEPROM",
.type = V4L2_CTRL_COMPOUND_TYPES,
.flags = V4L2_CTRL_FLAG_READ_ONLY,
.min = 0,
.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)
{
int i;
for (i = 0; i < ARRAY_SIZE(ctrl_cfg_list); i++) {
if (ctrl_cfg_list[i].id == cid)
return i;
}
return -EINVAL;
}
static int tegracam_get_string_ctrl_size(u32 cid,
const struct tegracam_ctrl_ops *ops)
{
u32 index = 0;
switch (cid) {
case TEGRA_CAMERA_CID_EEPROM_DATA:
index = TEGRA_CAM_STRING_CTRL_EEPROM_INDEX;
break;
case TEGRA_CAMERA_CID_FUSE_ID:
index = TEGRA_CAM_STRING_CTRL_FUSEID_INDEX;
break;
case TEGRA_CAMERA_CID_OTP_DATA:
index = TEGRA_CAM_STRING_CTRL_OTP_INDEX;
break;
default:
return -EINVAL;
}
return ops->string_ctrl_size[index];
}
static int tegracam_get_compound_ctrl_size(u32 cid,
const struct tegracam_ctrl_ops *ops)
{
u32 index = 0;
switch (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;
}
return ops->compound_ctrl_size[index];
}
static int tegracam_setup_string_ctrls(struct tegracam_device *tc_dev,
struct tegracam_ctrl_handler *handler)
{
const struct tegracam_ctrl_ops *ops = handler->ctrl_ops;
u32 numctrls = 0;
int i;
int err = 0;
if (ops == NULL)
return 0;
numctrls = ops->numctrls;
for (i = 0; i < numctrls; i++) {
struct v4l2_ctrl *ctrl = handler->ctrls[i];
if (ctrl->type == V4L2_CTRL_TYPE_STRING) {
err = ops->fill_string_ctrl(tc_dev, ctrl);
if (err)
return err;
}
}
spec_bar();
return 0;
}
static int tegracam_setup_compound_ctrls(struct tegracam_device *tc_dev,
struct tegracam_ctrl_handler *handler)
{
const struct tegracam_ctrl_ops *ops = handler->ctrl_ops;
u32 numctrls = 0;
int i;
int err = 0;
if (ops == NULL)
return 0;
numctrls = ops->numctrls;
for (i = 0; i < numctrls; i++) {
struct v4l2_ctrl *ctrl = handler->ctrls[i];
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;
}
}
spec_bar(); /* break_spec_#5_1 */
return 0;
}
static int tegracam_set_ctrls(struct tegracam_ctrl_handler *handler,
struct v4l2_ctrl *ctrl)
{
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;
/* For controls that are independent of power state */
switch (ctrl->id) {
case TEGRA_CAMERA_CID_SENSOR_MODE_ID:
s_data->sensor_mode_id = (int) (*ctrl->p_new.p_s64);
return 0;
case TEGRA_CAMERA_CID_HDR_EN:
return 0;
}
if (v4l2_subdev_call(&s_data->subdev, video,
g_input_status, &status)) {
dev_err(s_data->dev, "power status query unsupported\n");
return -ENOTTY;
}
/* power state is turned off, do not program sensor now */
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->max_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->max_exp_time.val + 1)
return 0;
err = ops->set_exposure(tc_dev, *ctrl->p_new.p_s64);
break;
case TEGRA_CAMERA_CID_EXPOSURE_SHORT:
err = ops->set_exposure_short(tc_dev, *ctrl->p_new.p_s64);
break;
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;
}
return err;
}
static int tegracam_set_grouphold_ex(struct tegracam_device *tc_dev,
struct sensor_blob *blob,
bool status)
{
const struct tegracam_ctrl_ops *ops = tc_dev->tcctrl_ops;
struct camera_common_data *s_data = tc_dev->s_data;
int err = 0;
/*
* when grouphold is set, reset control blob
* set grouphold register using set API
* start packetize commands for delivering the blob
* when grouphold is unset, unset grouphold register
* and write the blob only if sensor is streaming.
*/
if (status) {
memset(blob, 0, sizeof(struct sensor_blob));
err = ops->set_group_hold_ex(tc_dev, blob, status);
if (err)
return err;
} else {
err = ops->set_group_hold_ex(tc_dev, blob, status);
if (err)
return err;
/* TODO: block this write selectively from VI5 */
if (tc_dev->is_streaming) {
err = write_sensor_blob(s_data->regmap, blob);
if (err)
return err;
}
}
return 0;
}
static int tegracam_set_ctrls_ex(struct tegracam_ctrl_handler *handler,
struct v4l2_ctrl *ctrl)
{
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 tegracam_sensor_data *sensor_data = &handler->sensor_data;
struct sensor_blob *blob = &sensor_data->ctrls_blob;
int err = 0;
switch (ctrl->id) {
case TEGRA_CAMERA_CID_GAIN:
err = ops->set_gain_ex(tc_dev, blob, *ctrl->p_new.p_s64);
break;
case TEGRA_CAMERA_CID_FRAME_RATE:
err = ops->set_frame_rate_ex(tc_dev, blob, *ctrl->p_new.p_s64);
break;
case TEGRA_CAMERA_CID_EXPOSURE:
err = ops->set_exposure_ex(tc_dev, blob, *ctrl->p_new.p_s64);
break;
case TEGRA_CAMERA_CID_GROUP_HOLD:
err = tegracam_set_grouphold_ex(tc_dev, blob, ctrl->val);
break;
case TEGRA_CAMERA_CID_SENSOR_MODE_ID:
s_data->sensor_mode_id = (int) (*ctrl->p_new.p_s64);
break;
case TEGRA_CAMERA_CID_HDR_EN:
break;
default:
pr_err("%s: unknown ctrl id.\n", __func__);
return -EINVAL;
}
return err;
}
static int tegracam_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct tegracam_ctrl_handler *handler =
container_of(ctrl->handler,
struct tegracam_ctrl_handler, ctrl_handler);
const struct tegracam_ctrl_ops *ops = handler->ctrl_ops;
if (ops->is_blob_supported)
return tegracam_set_ctrls_ex(handler, ctrl);
else
return tegracam_set_ctrls(handler, 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;
struct v4l2_ext_control control;
struct tegracam_device *tc_dev = hdl->tc_dev;
struct device *dev = tc_dev->dev;
const struct tegracam_ctrl_ops *ops = hdl->ctrl_ops;
struct tegracam_sensor_data *sensor_data = &hdl->sensor_data;
struct sensor_blob *blob = &sensor_data->ctrls_blob;
bool is_blob_supported = false;
int err, result = 0;
int i;
if (ops == NULL)
return 0;
is_blob_supported = ops->is_blob_supported;
/*
* write list of override regs for the asking frame length,
* coarse integration time, and gain. Failures to write
* overrides are non-fatal
*/
memset(&ctrls, 0, sizeof(ctrls));
ctrls.which = V4L2_CTRL_ID2WHICH(TEGRA_CAMERA_CID_BASE);
ctrls.count = 1;
ctrls.controls = &control;
for (i = 0; i < NUM_OVERRIDE_CTRLS; i++) {
s64 val = 0;
control.id = tegracam_override_cids[i];
result = v4l2_g_ext_ctrls(&hdl->ctrl_handler, tc_dev->s_data->subdev.devnode, NULL, &ctrls);
if (result == 0) {
val = control.value64;
switch (control.id) {
case TEGRA_CAMERA_CID_GAIN:
if (is_blob_supported)
err = ops->set_gain_ex(tc_dev,
blob, val);
else
err = ops->set_gain(tc_dev, val);
break;
case TEGRA_CAMERA_CID_EXPOSURE:
if (is_blob_supported)
err = ops->set_exposure_ex(tc_dev,
blob, val);
else
err = ops->set_exposure(tc_dev, val);
break;
case TEGRA_CAMERA_CID_FRAME_RATE:
if (is_blob_supported)
err = ops->set_frame_rate_ex(tc_dev,
blob, val);
else
err = ops->set_frame_rate(tc_dev, val);
break;
default:
dev_err(dev, "%s: unsupported override %x\n",
__func__, control.id);
return -EINVAL;
}
if (err) {
dev_err(dev, "%s: error to set %d override\n",
__func__, control.id);
return err;
}
}
}
return 0;
}
int tegracam_init_ctrl_ranges_by_mode(
struct tegracam_ctrl_handler *handler,
u32 modeidx)
{
struct tegracam_device *tc_dev = handler->tc_dev;
struct camera_common_data *s_data = tc_dev->s_data;
struct sensor_control_properties *ctrlprops = NULL;
s64 min_short_exp_time = 0;
s64 max_short_exp_time = 0;
s64 default_short_exp_time = 0;
int i;
if (handler->numctrls == 0)
return 0;
if (modeidx >= s_data->sensor_props.num_modes)
return -EINVAL;
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;
switch (ctrl->id) {
case TEGRA_CAMERA_CID_GAIN:
err = v4l2_ctrl_modify_range(ctrl,
ctrlprops->min_gain_val,
ctrlprops->max_gain_val + 1,
ctrlprops->step_gain_val,
ctrlprops->default_gain);
break;
case TEGRA_CAMERA_CID_FRAME_RATE:
err = v4l2_ctrl_modify_range(ctrl,
ctrlprops->min_framerate,
ctrlprops->max_framerate,
ctrlprops->step_framerate,
ctrlprops->default_framerate);
break;
case TEGRA_CAMERA_CID_EXPOSURE:
err = v4l2_ctrl_modify_range(ctrl,
ctrlprops->min_exp_time.val,
ctrlprops->max_exp_time.val + 1,
ctrlprops->step_exp_time.val,
ctrlprops->default_exp_time.val);
break;
case TEGRA_CAMERA_CID_EXPOSURE_SHORT:
/*
* min_hdr_ratio should be equal to max_hdr_ratio.
* This will ensure consistent short exposure
* limit calculations.
*/
min_short_exp_time =
ctrlprops->min_exp_time.val /
ctrlprops->min_hdr_ratio;
max_short_exp_time =
ctrlprops->max_exp_time.val /
ctrlprops->min_hdr_ratio;
default_short_exp_time =
ctrlprops->default_exp_time.val /
ctrlprops->min_hdr_ratio;
err = v4l2_ctrl_modify_range(ctrl,
min_short_exp_time,
max_short_exp_time,
ctrlprops->step_exp_time.val,
default_short_exp_time);
dev_dbg(s_data->dev,
"%s:short_exp_limits[%lld,%lld], default_short_exp_time=%lld\n",
__func__,
min_short_exp_time,
max_short_exp_time,
default_short_exp_time);
break;
default:
/* Not required to modify these control ranges */
break;
}
if (err) {
dev_err(s_data->dev,
"ctrl %s range update failed\n", ctrl->name);
return err;
}
}
spec_bar();
return 0;
}
EXPORT_SYMBOL_GPL(tegracam_init_ctrl_ranges_by_mode);
int tegracam_init_ctrl_ranges(struct tegracam_ctrl_handler *handler)
{
struct tegracam_device *tc_dev = handler->tc_dev;
struct camera_common_data *s_data = tc_dev->s_data;
struct device *dev = tc_dev->dev;
int i, err = 0;
/* Updating static control ranges */
for (i = 0; i < handler->numctrls; i++) {
struct v4l2_ctrl *ctrl = handler->ctrls[i];
switch (ctrl->id) {
case TEGRA_CAMERA_CID_SENSOR_MODE_ID:
err = v4l2_ctrl_modify_range(ctrl,
CTRL_U32_MIN,
(s64) s_data->sensor_props.num_modes,
1,
CTRL_U32_MIN);
break;
default:
/* Not required to modify these control ranges */
break;
}
if (err) {
dev_err(s_data->dev,
"ctrl %s range update failed\n", ctrl->name);
return err;
}
}
spec_bar();
/* Use mode 0 control ranges as default */
if (s_data->sensor_props.num_modes > 0)
{
err = tegracam_init_ctrl_ranges_by_mode(handler, 0);
if (err) {
dev_err(dev, "Error %d updating mode specific control ranges \n", err);
return err;
}
}
return 0;
}
EXPORT_SYMBOL_GPL(tegracam_init_ctrl_ranges);
static int tegracam_check_ctrl_ops(
struct tegracam_ctrl_handler *handler, int *numctrls)
{
struct tegracam_device *tc_dev = handler->tc_dev;
struct device *dev = tc_dev->dev;
const struct tegracam_ctrl_ops *ops = handler->ctrl_ops;
const u32 *cids = ops->ctrl_cid_list;
int sensor_ops = 0, sensor_ex_ops = 0, mode_ops = 0, string_ops = 0;
int compound_ops = 0;
int default_ops = 0, default_ex_ops = 0, total_ops = 0;
int i;
/* Find missing sensor controls */
for (i = 0; i < ops->numctrls; i++) {
switch (cids[i]) {
case TEGRA_CAMERA_CID_GAIN:
if (ops->set_gain == NULL && ops->set_gain_ex == NULL)
dev_err(dev,
"Missing TEGRA_CAMERA_CID_GAIN implementation\n");
if (ops->set_gain != NULL)
sensor_ops++;
if (ops->set_gain_ex != NULL)
sensor_ex_ops++;
break;
case TEGRA_CAMERA_CID_EXPOSURE:
if (ops->set_exposure == NULL &&
ops->set_exposure_ex == NULL)
dev_err(dev,
"Missing TEGRA_CAMERA_CID_EXPOSURE implementation\n");
if (ops->set_exposure != NULL)
sensor_ops++;
if (ops->set_exposure_ex != NULL)
sensor_ex_ops++;
break;
case TEGRA_CAMERA_CID_EXPOSURE_SHORT:
if (ops->set_exposure_short == NULL)
dev_err(dev,
"Missing TEGRA_CAMERA_CID_EXPOSURE_SHORT implementation\n");
else
sensor_ops++;
break;
case TEGRA_CAMERA_CID_FRAME_RATE:
if (ops->set_frame_rate == NULL &&
ops->set_frame_rate_ex == NULL)
dev_err(dev,
"Missing TEGRA_CAMERA_CID_FRAME_RATE implementation\n");
if (ops->set_frame_rate != NULL)
sensor_ops++;
if (ops->set_frame_rate_ex != NULL)
sensor_ex_ops++;
break;
case TEGRA_CAMERA_CID_GROUP_HOLD:
dev_err(dev,
"TEGRA_CAMERA_CID_GROUP_HOLD contorl is enabled in framework by default, no need to add it in driver\n");
return -EINVAL;
case TEGRA_CAMERA_CID_EEPROM_DATA:
if (tegracam_get_string_ctrl_size(
TEGRA_CAMERA_CID_EEPROM_DATA, ops) == 0)
dev_err(dev, "EEPROM size not specified\n");
else
string_ops++;
break;
case TEGRA_CAMERA_CID_FUSE_ID:
if (tegracam_get_string_ctrl_size(
TEGRA_CAMERA_CID_FUSE_ID, ops) == 0)
dev_err(dev, "Fuse ID size not specified\n");
else
string_ops++;
break;
case TEGRA_CAMERA_CID_OTP_DATA:
if (tegracam_get_string_ctrl_size(
TEGRA_CAMERA_CID_OTP_DATA, ops) == 0)
dev_err(dev, "OTP size not specified\n");
else
string_ops++;
break;
case TEGRA_CAMERA_CID_STEREO_EEPROM:
if (tegracam_get_compound_ctrl_size(
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;
/* The below controls are handled by framework */
case TEGRA_CAMERA_CID_SENSOR_MODE_ID:
case TEGRA_CAMERA_CID_HDR_EN:
mode_ops++;
break;
default:
break;
}
}
/* Find missing string controls */
if (string_ops > 0) {
if (ops->fill_string_ctrl == NULL) {
dev_err(dev, "Missing string control implementation\n");
string_ops = 0;
}
}
/* Find missing default controls */
for (i = 0; i < TEGRACAM_DEF_CTRLS; i++) {
switch (tegracam_def_cids[i]) {
case TEGRA_CAMERA_CID_GROUP_HOLD:
if ((sensor_ops > 0 &&
ops->set_group_hold == NULL) ||
(sensor_ex_ops > 0 &&
ops->set_group_hold_ex == NULL))
dev_err(dev,
"Missing TEGRA_CAMERA_CID_GROUP_HOLD implementation\n");
if (ops->set_group_hold != NULL)
default_ops++;
if (ops->set_group_hold_ex != NULL)
default_ex_ops++;
break;
default:
break;
}
}
/* Don't use extended control when blob support is not enabled */
if (sensor_ex_ops > 0 && ops->is_blob_supported == false) {
dev_err(dev,
"ERROR: Extended controls only work when blob support is enabled\n");
return -EINVAL;
}
/* Should not mix normal and extended controls */
if ((sensor_ops + default_ops) > 0 &&
(sensor_ex_ops + default_ex_ops) > 0) {
dev_err(dev,
"ERROR: Can not mix normal and extended sensor controls\n");
return -EINVAL;
}
total_ops = sensor_ops + mode_ops + string_ops + default_ops + compound_ops;
total_ops += sensor_ex_ops + default_ex_ops;
if (total_ops != (ops->numctrls + TEGRACAM_DEF_CTRLS)) {
dev_err(dev,
"ERROR: %d controls registered with framework but missing implementation\n",
(ops->numctrls + TEGRACAM_DEF_CTRLS) - total_ops);
return -EINVAL;
}
*numctrls = sensor_ops + sensor_ex_ops + mode_ops + string_ops + compound_ops;
/* default controls are only needed if sensor controls are registered */
if (sensor_ops > 0)
*numctrls += default_ops;
if (sensor_ex_ops > 0)
*numctrls += default_ex_ops;
return 0;
}
static bool find_matching_cid(const u32 *ctrl_cid_list, u32 numctrls, u32 cid)
{
int i;
for (i = 0; i < numctrls; i++) {
if (ctrl_cid_list[i] == cid)
return true;
}
return false;
}
static int tegracam_check_ctrl_cids(struct tegracam_ctrl_handler *handler)
{
struct tegracam_device *tc_dev = handler->tc_dev;
struct device *dev = tc_dev->dev;
const struct tegracam_ctrl_ops *ops = handler->ctrl_ops;
int errors_found = 0;
/* Find missing sensor control IDs */
if (ops->set_gain != NULL || ops->set_gain_ex != NULL) {
if (!find_matching_cid(ops->ctrl_cid_list,
ops->numctrls,
TEGRA_CAMERA_CID_GAIN)) {
dev_err(dev, "Missing TEGRA_CAMERA_CID_GAIN registration\n");
errors_found++;
}
}
if (ops->set_exposure != NULL || ops->set_exposure_ex != NULL) {
if (!find_matching_cid(ops->ctrl_cid_list,
ops->numctrls,
TEGRA_CAMERA_CID_EXPOSURE)) {
dev_err(dev, "Missing TEGRA_CAMERA_CID_EXPOSURE registration\n");
errors_found++;
}
}
if (ops->set_exposure_short != NULL) {
if (!find_matching_cid(ops->ctrl_cid_list,
ops->numctrls,
TEGRA_CAMERA_CID_EXPOSURE_SHORT)) {
dev_err(dev,
"Missing TEGRA_CAMERA_CID_EXPOSURE_SHORT registration\n");
errors_found++;
}
}
if (ops->set_frame_rate != NULL || ops->set_frame_rate_ex != NULL) {
if (!find_matching_cid(ops->ctrl_cid_list,
ops->numctrls,
TEGRA_CAMERA_CID_FRAME_RATE)) {
dev_err(dev, "Missing TEGRA_CAMERA_CID_FRAME_RATE registration\n");
errors_found++;
}
}
/* Find missing string control IDs */
if (ops->fill_string_ctrl != NULL) {
if (tegracam_get_string_ctrl_size(
TEGRA_CAMERA_CID_EEPROM_DATA, ops) > 0) {
if (!find_matching_cid(ops->ctrl_cid_list,
ops->numctrls,
TEGRA_CAMERA_CID_EEPROM_DATA)) {
dev_err(dev,
"Missing TEGRA_CAMERA_CID_EEPROM_DATA registration\n");
errors_found++;
}
}
if (tegracam_get_string_ctrl_size(
TEGRA_CAMERA_CID_FUSE_ID, ops) > 0) {
if (!find_matching_cid(ops->ctrl_cid_list,
ops->numctrls,
TEGRA_CAMERA_CID_FUSE_ID)) {
dev_err(dev,
"Missing TEGRA_CAMERA_CID_FUSE_ID registration\n");
errors_found++;
}
}
if (tegracam_get_string_ctrl_size(
TEGRA_CAMERA_CID_OTP_DATA, ops) > 0) {
if (!find_matching_cid(ops->ctrl_cid_list,
ops->numctrls,
TEGRA_CAMERA_CID_OTP_DATA)) {
dev_err(dev,
"Missing TEGRA_CAMERA_CID_OTP_DATA registration\n");
errors_found++;
}
}
}
if (ops->fill_compound_ctrl != NULL) {
if (tegracam_get_compound_ctrl_size(
TEGRA_CAMERA_CID_STEREO_EEPROM, ops) > 0) {
if (!find_matching_cid(ops->ctrl_cid_list,
ops->numctrls,
TEGRA_CAMERA_CID_STEREO_EEPROM)) {
dev_err(dev,
"Missing TEGRA_CAMERA_CID_STEREO_EEPROM registration\n");
errors_found++;
}
}
}
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);
return -EINVAL;
}
return 0;
}
int tegracam_ctrl_handler_init(struct tegracam_ctrl_handler *handler)
{
struct tegracam_device *tc_dev = handler->tc_dev;
struct v4l2_ctrl *ctrl;
struct v4l2_ctrl_config *ctrl_cfg;
struct device *dev = tc_dev->dev;
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;
if (ops != NULL) {
cids = ops->ctrl_cid_list;
err = tegracam_check_ctrl_ops(handler, &numctrls);
if (err) {
dev_err(dev, "Error %d in control ops setup\n", err);
goto ctrl_error;
}
err = tegracam_check_ctrl_cids(handler);
if (err) {
dev_err(dev, "Error %d in control cids setup\n", err);
goto ctrl_error;
}
}
err = v4l2_ctrl_handler_init(&handler->ctrl_handler, numctrls);
for (i = 0, j = 0; i < numctrls; i++) {
u32 cid = i < ops->numctrls ? cids[i] : tegracam_def_cids[j++];
int index = tegracam_get_ctrl_index(cid);
int size = 0;
if (index >= ARRAY_SIZE(ctrl_cfg_list)) {
dev_err(dev, "unsupported control in the list\n");
return -ENOTTY;
}
ctrl_cfg = &ctrl_cfg_list[index];
if (ctrl_cfg->type == V4L2_CTRL_TYPE_STRING) {
size = tegracam_get_string_ctrl_size(ctrl_cfg->id, ops);
if (size < 0) {
dev_err(dev, "Invalid string ctrl size\n");
return -EINVAL;
}
ctrl_cfg->max = size;
}
if (ctrl_cfg->type == V4L2_CTRL_COMPOUND_TYPES) {
size = tegracam_get_compound_ctrl_size(ctrl_cfg->id,
ops);
if (size < 0) {
dev_err(dev, "Invalid compound ctrl size\n");
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,
ctrl_cfg, NULL);
if (ctrl == NULL) {
dev_err(dev, "Failed to init %s ctrl\n",
ctrl_cfg->name);
return -EINVAL;
}
if (ctrl_cfg->type == V4L2_CTRL_TYPE_STRING &&
ctrl_cfg->flags & V4L2_CTRL_FLAG_READ_ONLY) {
ctrl->p_new.p_char = devm_kzalloc(tc_dev->dev,
size + 1, GFP_KERNEL);
}
if ((ctrl_cfg->type == V4L2_CTRL_COMPOUND_TYPES) &&
ctrl_cfg->flags & V4L2_CTRL_FLAG_READ_ONLY)
ctrl->p_new.p = devm_kzalloc(tc_dev->dev,
ctrl_cfg->max, GFP_KERNEL);
handler->ctrls[i] = ctrl;
};
spec_bar();
handler->numctrls = numctrls;
err = v4l2_ctrl_handler_setup(&handler->ctrl_handler);
if (err) {
dev_err(dev, "Error %d in control hdl setup\n", err);
goto error;
}
err = handler->ctrl_handler.error;
if (err) {
dev_err(dev, "Error %d adding controls\n", err);
goto error;
}
err = tegracam_setup_string_ctrls(tc_dev, handler);
if (err) {
dev_err(dev, "setup string controls failed\n");
goto error;
}
err = tegracam_setup_compound_ctrls(tc_dev, handler);
if (err) {
dev_err(dev, "setup compound controls failed\n");
goto error;
}
err = tegracam_init_ctrl_ranges(handler);
if (err) {
dev_err(dev, "Error %d updating control ranges\n", err);
goto error;
}
return 0;
error:
v4l2_ctrl_handler_free(&handler->ctrl_handler);
ctrl_error:
return err;
}
EXPORT_SYMBOL_GPL(tegracam_ctrl_handler_init);