media: video: tegra: sh532u: Focuser range tuning support
Naren Bhat [Thu, 21 Jun 2012 23:48:51 +0000 (16:48 -0700)]
Support for get/set capabilities added. Focuser code has a way to
calibrate itself that results in determination of optimal working
range. This along with actual range from device ROM are returned
to the caller. Focuser has the macro at lower end and infinity at
farther end of the range, which are reversed to the user level to
keep the inf/macro positions to be consistent. Focuser range
translation is taken out.

Bug 1004816

Change-Id: I1a086ff10e99940f9ad861397bf7e71e9996c68a
Signed-off-by: Naren Bhat <nbhat@nvidia.com>
Reviewed-on: http://git-master/r/110443
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Charlie Huang <chahuang@nvidia.com>
Reviewed-by: Jon Mayo <jmayo@nvidia.com>
Reviewed-by: Patrick Shehane <pshehane@nvidia.com>
Tested-by: Charlie Huang <chahuang@nvidia.com>
Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com>

drivers/media/video/tegra/sh532u.c
include/media/nvc_focus.h

index 4c7ef5a..84692d9 100644 (file)
 #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__);
index c6ff1fa..e37d897 100644 (file)
@@ -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__ */