summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/media/video/tegra/sh532u.c240
-rw-r--r--include/media/nvc_focus.h25
2 files changed, 188 insertions, 77 deletions
diff --git a/drivers/media/video/tegra/sh532u.c b/drivers/media/video/tegra/sh532u.c
index 4c7ef5aeaeab..84692d992154 100644
--- a/drivers/media/video/tegra/sh532u.c
+++ b/drivers/media/video/tegra/sh532u.c
@@ -113,7 +113,9 @@
#define SH532U_TIMEOUT_MS 200
#define SH532U_POS_LOW_DEFAULT 0xA000
#define SH532U_POS_HIGH_DEFAULT 0x6000
-
+#define SH532U_SLEW_RATE 1
+#define SH532U_POS_TRANSLATE 0
+#define SH532U_POS_SIGN_CHANGER (-1)
static u8 sh532u_ids[] = {
0xF0,
@@ -148,6 +150,7 @@ struct sh532u_info {
unsigned i2c_addr_rom;
struct nvc_focus_nvc nvc;
struct nvc_focus_cap cap;
+ struct nv_focuser_config config;
enum nvc_focus_sts sts;
struct sh532u_pdata_info cfg;
bool reset_flag;
@@ -172,6 +175,8 @@ static struct nvc_focus_cap sh532u_default_cap = {
.focus_macro = SH532U_FOCUS_MACRO,
.focus_hyper = SH532U_FOCUS_HYPER,
.focus_infinity = SH532U_FOCUS_INFINITY,
+ .slew_rate = SH532U_SLEW_RATE,
+ .position_translate = SH532U_POS_TRANSLATE,
};
static struct nvc_focus_nvc sh532u_default_nvc = {
@@ -736,22 +741,31 @@ static void sh532u_sts_rd(struct sh532u_info *info)
}
}
-static s16 sh532u_rel2abs(struct sh532u_info *info, u32 rel_position)
+static s16 sh532u_rel2abs(struct sh532u_info *info, s32 rel_position)
{
s16 abs_pos;
if (rel_position > info->cap.actuator_range)
rel_position = info->cap.actuator_range;
- rel_position = info->cap.actuator_range - rel_position;
- if (rel_position) {
- rel_position *= info->abs_range;
- rel_position /= info->cap.actuator_range;
+ if (info->cap.position_translate) {
+ rel_position = info->cap.actuator_range - rel_position;
+ if (rel_position) {
+ rel_position *= info->abs_range;
+ rel_position /= info->cap.actuator_range;
+ }
+ abs_pos = (s16)(info->abs_base + rel_position);
+ } else {
+ abs_pos = rel_position * SH532U_POS_SIGN_CHANGER;
}
- abs_pos = (s16)(info->abs_base + rel_position);
+
if (abs_pos < info->cfg.limit_low)
abs_pos = info->cfg.limit_low;
if (abs_pos > info->cfg.limit_high)
abs_pos = info->cfg.limit_high;
+
+ dev_dbg(&info->i2c_client->dev, "%s: rel_position %d returning abs_pos %d\n",
+ __func__, rel_position, abs_pos);
+
return abs_pos;
}
@@ -763,12 +777,21 @@ static u32 sh532u_abs2rel(struct sh532u_info *info, s16 abs_position)
abs_position = info->cfg.limit_high;
if (abs_position < info->abs_base)
abs_position = info->abs_base;
- rel_pos = (u32)(abs_position - info->abs_base);
- rel_pos *= info->cap.actuator_range;
- rel_pos /= info->abs_range;
- if (rel_pos > info->cap.actuator_range)
- rel_pos = info->cap.actuator_range;
- rel_pos = info->cap.actuator_range - rel_pos;
+
+ if (info->cap.position_translate) {
+ rel_pos = (u32)(abs_position - info->abs_base);
+ rel_pos *= info->cap.actuator_range;
+ rel_pos /= info->abs_range;
+
+ if (rel_pos > info->cap.actuator_range)
+ rel_pos = info->cap.actuator_range;
+ rel_pos = info->cap.actuator_range - rel_pos;
+ } else {
+ rel_pos = abs_position * SH532U_POS_SIGN_CHANGER;
+ }
+ dev_dbg(&info->i2c_client->dev, "%s: abs_position %d returning rel_pos %d",
+ __func__, abs_position, rel_pos);
+
return rel_pos;
}
@@ -782,7 +805,7 @@ static int sh532u_abs_pos_rd(struct sh532u_info *info, s16 *position)
return err;
}
-static int sh532u_rel_pos_rd(struct sh532u_info *info, u32 *position)
+static int sh532u_rel_pos_rd(struct sh532u_info *info, s32 *position)
{
s16 abs_pos;
long msec;
@@ -811,9 +834,11 @@ static int sh532u_rel_pos_rd(struct sh532u_info *info, u32 *position)
pos = (int)info->pos_rel;
}
}
- if (pos < 0)
- pos = 0;
- *position = (u32)pos;
+ if (info->cap.position_translate) {
+ if (pos < 0)
+ pos = 0;
+ }
+ *position = pos;
return 0;
}
@@ -851,6 +876,8 @@ static void sh532u_calibration_caps(struct sh532u_info *info)
rel_hi = info->cap.focus_infinity;
info->abs_range = (u32)(info->cfg.pos_high - info->cfg.pos_low);
loop_limit = (rel_lo > rel_hi) ? rel_lo : rel_hi;
+ dev_dbg(&info->i2c_client->dev, "%s: rel_lo %d rel_hi %d loop_limit %d\n",
+ __func__, rel_lo, rel_hi, loop_limit);
for (i = 0; i <= loop_limit; i++) {
rel_range = info->cap.actuator_range - (rel_lo + rel_hi);
step = info->abs_range / rel_range;
@@ -868,22 +895,36 @@ static void sh532u_calibration_caps(struct sh532u_info *info)
abs_top <= info->cfg.limit_high)
break;
}
+ dev_dbg(&info->i2c_client->dev, "%s: info->abs_range %d abs_base %d abs_top %d\n",
+ __func__, info->abs_range, info->abs_base, abs_top);
+
+ if (!info->cap.position_translate && info->abs_range)
+ info->cap.actuator_range = info->abs_range;
+
info->cap.focus_hyper = info->abs_range;
info->abs_range = (u32)(abs_top - info->abs_base);
/* calculate absolute hyperfocus position */
info->cap.focus_hyper *= info->cfg.focus_hyper_ratio;
info->cap.focus_hyper /= info->cfg.focus_hyper_div;
abs_top = (s16)(info->cfg.pos_high - info->cap.focus_hyper);
+
/* update actual relative positions */
info->cap.focus_hyper = sh532u_abs2rel(info, abs_top);
+ dev_dbg(&info->i2c_client->dev, "%s: focus_hyper abs %d rel %d\n",
+ __func__, abs_top, info->cap.focus_hyper);
+
info->cap.focus_infinity = sh532u_abs2rel(info, info->cfg.pos_high);
+ dev_dbg(&info->i2c_client->dev, "%s: focus_infinity abs %d rel %d\n",
+ __func__, info->cfg.pos_high, info->cap.focus_infinity);
+
info->cap.focus_macro = sh532u_abs2rel(info, info->cfg.pos_low);
- dev_dbg(&info->i2c_client->dev, "%s focus_macro=%u\n",
- __func__, info->cap.focus_macro);
- dev_dbg(&info->i2c_client->dev, "%s focus_infinity=%u\n",
- __func__, info->cap.focus_infinity);
- dev_dbg(&info->i2c_client->dev, "%s focus_hyper=%u\n",
- __func__, info->cap.focus_hyper);
+ dev_dbg(&info->i2c_client->dev, "%s: focus_macro abs %d rel %d\n",
+ __func__, info->cfg.pos_low, info->cap.focus_macro);
+
+ dev_dbg(&info->i2c_client->dev, "%s: Version %d actuator_range %d "
+ "settle_time %d position_traslate %d\n",
+ __func__, info->cap.version, info->cap.actuator_range,
+ info->cap.settle_time, info->cap.position_translate);
}
static int sh532u_calibration(struct sh532u_info *info, bool use_defaults)
@@ -892,8 +933,11 @@ static int sh532u_calibration(struct sh532u_info *info, bool use_defaults)
int err;
int ret = 0;
- if (info->init_cal_flag)
+ if (info->init_cal_flag) {
+ dev_dbg(&info->i2c_client->dev, "%s: Already initialized"
+ "Returning\n", __func__);
return 0;
+ }
/*
* Get Inf1, Mac1
@@ -965,8 +1009,9 @@ static int sh532u_calibration(struct sh532u_info *info, bool use_defaults)
* 1 PASS PASS Continue to calculations
*/
/* err = DATA where FAIL = 1 */
- if (!info->cfg.pos_low || !info->cfg.pos_high ||
- !info->cfg.limit_low || !info->cfg.limit_high)
+ if (!info->cfg.pos_low || info->cfg.pos_high <= info->cfg.pos_low ||
+ !info->cfg.limit_low ||
+ info->cfg.limit_high <= info->cfg.limit_low)
err = 1;
else
err = 0;
@@ -998,6 +1043,7 @@ static int sh532u_calibration(struct sh532u_info *info, bool use_defaults)
__func__, (int)info->cfg.limit_low);
dev_dbg(&info->i2c_client->dev, "%s limit_high=%d\n",
__func__, (int)info->cfg.limit_high);
+
sh532u_calibration_caps(info);
info->init_cal_flag = 1;
dev_dbg(&info->i2c_client->dev, "%s complete\n", __func__);
@@ -1215,6 +1261,7 @@ static int sh532u_pos_abs_wr(struct sh532u_info *info, s16 tar_pos)
STMLFF_OFF |
STMVEN_ON));
}
+ dev_dbg(&info->i2c_client->dev, "%s: position %d\n", __func__, tar_pos);
return err;
}
@@ -1289,30 +1336,101 @@ static int sh532u_hvca_pos_init(struct sh532u_info *info)
return err;
}
-static int sh532u_pos_rel_wr(struct sh532u_info *info, u32 position)
+static int sh532u_pos_rel_wr(struct sh532u_info *info, s32 position)
{
s16 abs_pos;
- if (position > info->cap.actuator_range) {
- dev_err(&info->i2c_client->dev, "%s invalid position %u\n",
- __func__, position);
- return -EINVAL;
+ if (info->cap.position_translate) {
+ if (position > info->cap.actuator_range) {
+ dev_err(&info->i2c_client->dev, "%s invalid position %d\n",
+ __func__, position);
+ return -EINVAL;
+ }
}
-
abs_pos = sh532u_rel2abs(info, position);
+
info->pos_rel = position;
info->pos_abs = abs_pos;
info->pos_time_wr = jiffies;
return sh532u_pos_abs_wr(info, abs_pos);
}
+static void sh532u_get_focuser_capabilities(struct sh532u_info *info)
+{
+ memset(&info->config, 0, sizeof(info->config));
+
+ info->config.focal_length = info->nvc.focal_length;
+ info->config.fnumber = info->nvc.fnumber;
+ info->config.max_aperture = info->nvc.fnumber;
+ info->config.range_ends_reversed = (SH532U_POS_SIGN_CHANGER == -1)
+ ? 1 : 0;
+
+ info->config.settle_time = info->cap.settle_time;
+
+ /*
+ * We do not use pos_working_low and pos_working_high
+ * in the kernel driver.
+ */
+ info->config.pos_working_low = AF_POS_INVALID_VALUE;
+ info->config.pos_working_high = AF_POS_INVALID_VALUE;
+
+ info->config.pos_actual_low = info->cfg.limit_high *
+ SH532U_POS_SIGN_CHANGER;
+ info->config.pos_actual_high = info->cfg.limit_low *
+ SH532U_POS_SIGN_CHANGER;
+ info->config.slew_rate = info->cap.slew_rate;
+ info->config.circle_of_confusion = -1;
+
+ /*
+ * These need to be passed up once we have the EEPROM/OTP read
+ * routines in teh kernel. These need to be passed up much earlier on.
+ * Till we have these routines, we pass them up as part of the get call.
+ */
+ info->config.num_focuser_sets = 1;
+ info->config.focuser_set[0].posture = 'S';
+ info->config.focuser_set[0].macro = info->cap.focus_macro;
+ info->config.focuser_set[0].hyper = info->cap.focus_hyper;
+ info->config.focuser_set[0].inf = info->cap.focus_infinity;
+ info->config.focuser_set[0].hysteresis = 0;
+ info->config.focuser_set[0].settle_time = info->cap.settle_time;
+ info->config.focuser_set[0].num_dist_pairs = 0;
+
+ dev_dbg(&info->i2c_client->dev, "%s: pos_actual_low %d pos_actual_high %d "
+ " settle_time %d\n", __func__, info->config.pos_actual_low,
+ info->config.pos_actual_high, info->cap.settle_time);
+
+}
+
+
+static int sh532u_set_focuser_capabilities(struct sh532u_info *info,
+ struct nvc_param *params)
+{
+ if (copy_from_user(&info->config, (const void __user *)params->p_value,
+ params->sizeofvalue)) {
+ dev_err(&info->i2c_client->dev, "%s Error: copy_from_user bytes %d\n",
+ __func__, params->sizeofvalue);
+ return -EFAULT;
+ }
+
+ /* info.config.focuser_set[0].posture, macro, hyper, infinity and
+ * hysterisis can remain there only. We need only settle_time &
+ * slew_rate for use here.
+ */
+ info->cap.settle_time = info->config.focuser_set[0].settle_time;
+ info->config.slew_rate = info->config.slew_rate;
+
+ dev_dbg(&info->i2c_client->dev, "%s: copy_from_user bytes %d\n",
+ __func__, params->sizeofvalue);
+ return 0;
+}
+
static int sh532u_param_rd(struct sh532u_info *info, unsigned long arg)
{
struct nvc_param params;
const void *data_ptr;
u32 data_size = 0;
- u32 position;
+ s32 position;
int err;
if (copy_from_user(&params,
@@ -1366,16 +1484,14 @@ static int sh532u_param_rd(struct sh532u_info *info, unsigned long arg)
sh532u_pm_dev_wr(info, NVC_PWR_STDBY);
if (err)
return -EIO;
+ dev_dbg(&info->i2c_client->dev, "%s: NVC_PARAM_CAPS: params.param %d "
+ "params.sizeofvalue %d\n",
+ __func__, params.param, params.sizeofvalue);
- data_ptr = &info->cap;
- /* there are different sizes depending on the version */
- /* send back just what's requested or our max size */
- if (params.sizeofvalue < sizeof(info->cap))
- data_size = params.sizeofvalue;
- else
- data_size = sizeof(info->cap);
- dev_dbg(&info->i2c_client->dev, "%s CAPS\n",
- __func__);
+ sh532u_get_focuser_capabilities(info);
+
+ data_ptr = &info->config;
+ data_size = params.sizeofvalue;
break;
case NVC_PARAM_STS:
@@ -1421,16 +1537,15 @@ static int sh532u_param_wr_s(struct sh532u_info *info,
struct nvc_param *params,
u32 u32val)
{
- struct nvc_focus_cap cap;
u8 u8val;
int err;
u8val = (u8)u32val;
switch (params->param) {
case NVC_PARAM_LOCUS:
- dev_dbg(&info->i2c_client->dev, "%s LOCUS: %u\n",
- __func__, u32val);
- err = sh532u_pos_rel_wr(info, u32val);
+ dev_dbg(&info->i2c_client->dev, "%s LOCUS: %d\n",
+ __func__, (s32) u32val);
+ err = sh532u_pos_rel_wr(info, (s32) u32val);
return err;
case NVC_PARAM_RESET:
@@ -1446,27 +1561,9 @@ static int sh532u_param_wr_s(struct sh532u_info *info,
return err;
case NVC_PARAM_CAPS:
- dev_dbg(&info->i2c_client->dev, "%s CAPS\n",
- __func__);
- if (copy_from_user(&cap, (const void __user *)params->p_value,
- sizeof(params->sizeofvalue))) {
- dev_err(&info->i2c_client->dev, "%s %d copy_from_user err\n",
- __func__, __LINE__);
- return -EFAULT;
- }
-
- if (!cap.version)
- return -EINVAL;
-
- if (cap.version >= NVC_FOCUS_CAP_VER1)
- info->cap.actuator_range = cap.actuator_range;
- if (cap.version >= NVC_FOCUS_CAP_VER2) {
- info->cap.focus_macro = cap.focus_macro;
- info->cap.focus_hyper = cap.focus_hyper;
- info->cap.focus_infinity = cap.focus_infinity;
- }
- sh532u_calibration_caps(info);
- return 0;
+ dev_dbg(&info->i2c_client->dev, "%s CAPS. Error. sh532u_param_wr "
+ "should be called instead\n", __func__);
+ return -EFAULT;
default:
dev_dbg(&info->i2c_client->dev,
@@ -1575,6 +1672,14 @@ static int sh532u_param_wr(struct sh532u_info *info, unsigned long arg)
return err;
+ case NVC_PARAM_CAPS:
+ if (sh532u_set_focuser_capabilities(info, &params)) {
+ dev_err(&info->i2c_client->dev, "%s: Error: copy_from_user bytes %d\n",
+ __func__, params.sizeofvalue);
+ return -EFAULT;
+ }
+ return 0;
+
default:
/* parameters dependent on sync mode */
switch (info->s_mode) {
@@ -1829,7 +1934,6 @@ static int sh532u_probe(
char dname[16];
int err;
- dev_dbg(&client->dev, "%s\n", __func__);
info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL);
if (info == NULL) {
dev_err(&client->dev, "%s: kzalloc error\n", __func__);
diff --git a/include/media/nvc_focus.h b/include/media/nvc_focus.h
index c6ff1fa792c1..e37d897d8323 100644
--- a/include/media/nvc_focus.h
+++ b/include/media/nvc_focus.h
@@ -33,6 +33,8 @@
#define NVC_FOCUS_CAP_VER2 2
#define NVC_FOCUS_CAP_VER 2 /* latest version */
+#define AF_POS_INVALID_VALUE INT_MAX
+
enum nvc_focus_sts {
NVC_FOCUS_STS_UNKNOWN = 1,
NVC_FOCUS_STS_NO_DEVICE,
@@ -52,11 +54,13 @@ struct nvc_focus_nvc {
struct nvc_focus_cap {
__u32 version;
- __u32 actuator_range;
+ __s32 actuator_range;
__u32 settle_time;
- __u32 focus_macro;
- __u32 focus_hyper;
- __u32 focus_infinity;
+ __s32 focus_macro;
+ __s32 focus_hyper;
+ __s32 focus_infinity;
+ __u32 slew_rate;
+ __u32 position_translate;
} __packed;
@@ -66,7 +70,7 @@ struct nvc_focus_cap {
struct nv_focuser_set_dist_pairs {
__s32 fdn;
__s32 distance;
-};
+} __packed;
struct nv_focuser_set {
__s32 posture;
@@ -78,23 +82,26 @@ struct nv_focuser_set {
__s32 macro_offset;
__s32 inf_offset;
__u32 num_dist_pairs;
- struct nv_focuser_set_dist_pairs dist_pair[NV_FOCUSER_SET_DISTANCE_PAIR];
-};
+ struct nv_focuser_set_dist_pairs
+ dist_pair[NV_FOCUSER_SET_DISTANCE_PAIR];
+} __packed;
struct nv_focuser_config {
__u32 focal_length;
__u32 fnumber;
__u32 max_aperture;
- __u32 actuator_range;
+ __s32 actuator_range;
__u32 settle_time;
+ __u32 range_ends_reversed;
__s32 pos_working_low;
__s32 pos_working_high;
__s32 pos_actual_low;
__s32 pos_actual_high;
__u32 slew_rate;
__u32 circle_of_confusion;
+ __u32 num_focuser_sets;
struct nv_focuser_set focuser_set[NV_FOCUSER_SET_MAX];
-};
+} __packed;
#endif /* __NVC_FOCUS_H__ */