]> nv-tegra.nvidia Code Review - linux-2.6.git/commitdiff
media: video: tegra: sh532u driver
authorErik Lilliebjerg <elilliebjerg@nvidia.com>
Tue, 22 Nov 2011 10:53:54 +0000 (03:53 -0700)
committerDan Willemsen <dwillemsen@nvidia.com>
Fri, 23 Mar 2012 07:41:18 +0000 (00:41 -0700)
- Added multi-instance support with sync capability
- Added GLOS power scheme
- Standardized the IOCTL API

Bug 865305

Change-Id: I37bcf306477d30589f3985d9370c59450842d340
Signed-off-by: Erik Lilliebjerg <elilliebjerg@nvidia.com>
Reviewed-on: http://git-master/r/66116
Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com>
Tested-by: Gerrit_Virtual_Submit
Rebase-Id: Rb4fb40adf45cfd5359ebc4da6efc0ff2b0beeb9e

drivers/media/video/tegra/sh532u.c
include/media/nvc_focus.h [new file with mode: 0644]
include/media/sh532u.h

index ec760e2ec6dc621cf8df923f4321426355e8c886..5781621e6875a6b210012c91d7af59ac98deaa8d 100644 (file)
  * 02111-1307, USA
  */
 
-#include <linux/delay.h>
+/* Implementation
+ * --------------
+ * The board level details about the device need to be provided in the board
+ * file with the sh532u_platform_data structure.
+ * Standard among NVC kernel drivers in this structure is:
+ * .cfg = Use the NVC_CFG_ defines that are in nvc.h.
+ *        Descriptions of the configuration options are with the defines.
+ *        This value is typically 0.
+ * .num = The number of the instance of the device.  This should start at 1 and
+ *        and increment for each device on the board.  This number will be
+ *        appended to the MISC driver name, Example: /dev/focuser.1
+ *        If not used or 0, then nothing is appended to the name.
+ * .sync = If there is a need to synchronize two devices, then this value is
+ *         the number of the device instance (.num above) this device is to
+ *         sync to.  For example:
+ *         Device 1 platform entries =
+ *         .num = 1,
+ *         .sync = 2,
+ *         Device 2 platfrom entries =
+ *         .num = 2,
+ *         .sync = 1,
+ *         The above example sync's device 1 and 2.
+ *         This is typically used for stereo applications.
+ * .dev_name = The MISC driver name the device registers as.  If not used,
+ *             then the part number of the device is used for the driver name.
+ *             If using the NVC user driver then use the name found in this
+ *             driver under _default_pdata.
+ *
+ * The following is specific to NVC kernel focus drivers:
+ * .nvc = Pointer to the nvc_focus_nvc structure.  This structure needs to
+ *        be defined and populated if overriding the driver defaults.
+ * .cap = Pointer to the nvc_focus_cap structure.  This structure needs to
+ *        be defined and populated if overriding the driver defaults.
+ *
+ * The following is specific to only this NVC kernel focus driver:
+ * .info = Pointer to the sh532u_pdata_info structure.  This structure does
+ *         not need to be defined and populated unless overriding ROM data.
+.* .i2c_addr_rom = The I2C address of the onboard ROM.
+ * .gpio_reset = The GPIO connected to the devices reset.  If not used then
+ *               leave blank.
+ * .gpio_en = Due to a Linux limitation, a GPIO is defined to "enable" the
+ *            device.  This workaround is for when the device's power GPIO's
+ *            are behind an I2C expander.  The Linux limitation doesn't allow
+ *            the I2C GPIO expander to be ready for use when this device is
+ *            probed.  When this problem is solved, this driver needs to
+ *            remove the gpio_en WAR.
+ *
+ * Power Requirements
+ * The board power file must contain the following labels for the power
+ * regulator(s) of this device:
+ * "vdd" = the power regulator for the device's power.
+ * "vdd_i2c" = the power regulator for the I2C power.
+ *
+ * The above values should be all that is needed to use the device with this
+ * driver.  Modifications of this driver should not be needed.
+ */
+
+
 #include <linux/fs.h>
 #include <linux/i2c.h>
 #include <linux/miscdevice.h>
 #include <linux/slab.h>
+#include <linux/delay.h>
 #include <linux/uaccess.h>
-#include <media/ov5650.h>
+#include <linux/list.h>
+#include <linux/jiffies.h>
+#include <linux/gpio.h>
+#include <media/nvc.h>
 #include <media/sh532u.h>
-#include <media/tegra_camera.h>
 
-#include <asm/traps.h>
+#define SH532U_ID              0xF0
+/* defaults if no ROM data */
+#define SH532U_HYPERFOCAL_RATIO 1836 /* 41.2f/224.4f Ratio source: SEMCO */
+/* _HYPERFOCAL_RATIO is multiplied and _HYPERFOCAL_DIV divides for float */
+#define SH532U_HYPERFOCAL_DIV  10000
+#define SH532U_FOCAL_LENGTH    0x408D70A4
+#define SH532U_FNUMBER         0x40333333
+#define SH532U_MAX_APERATURE   0x3FCA0EA1
+/* SH532U_CAPS_VER = 0: invalid value */
+/* SH532U_CAPS_VER = 1: added NVC_PARAM_STS */
+/* SH532U_CAPS_VER = 2: expanded nvc_focus_cap */
+#define SH532U_CAPS_VER                2
+#define SH532U_ACTUATOR_RANGE  1000
+#define SH532U_SETTLETIME      30
+#define SH532U_FOCUS_MACRO     900
+#define SH532U_FOCUS_HYPER     250
+#define SH532U_FOCUS_INFINITY  100
+#define SH532U_TIMEOUT_MS      200
 
-#define POS_LOW (0xA000)
-#define POS_HIGH (0x6000)
-#define SETTLETIME_MS (7)
-#define FOCAL_LENGTH 0x408d70a4 /* (4.42f) */
-#define FNUMBER 0x40333333 /* (2.8f) */
 
-struct sh532u_sensor {
+struct sh532u_info {
+       atomic_t in_use;
        struct i2c_client *i2c_client;
-       struct sh532u_config config;
-       struct sh532u_platform_data pdata;
+       struct sh532u_platform_data *pdata;
+       struct miscdevice miscdev;
+       struct list_head list;
+       int pwr_api;
+       int pwr_dev;
+       struct nvc_regulator vreg_vdd;
+       struct nvc_regulator vreg_i2c;
+       u8 s_mode;
+       struct sh532u_info *s_info;
+       unsigned i2c_addr_rom;
+       struct nvc_focus_nvc nvc;
+       struct nvc_focus_cap cap;
+       enum nvc_focus_sts sts;
+       struct sh532u_pdata_info cfg;
+       bool gpio_flag_reset;
+       bool init_cal_flag;
+       u32 pos_rel;
+       s16 pos_abs;
+       long pos_time_wr;
 };
 
-struct sh532u_info {
-       enum StereoCameraMode camera_mode;
-       struct sh532u_sensor *left;
-       struct sh532u_sensor *right;
+static struct sh532u_pdata_info sh532u_default_info = {
+       .move_timeoutms = SH532U_TIMEOUT_MS,
+       .focus_hyper_ratio = SH532U_HYPERFOCAL_RATIO,
+       .focus_hyper_div = SH532U_HYPERFOCAL_DIV,
+};
+
+static struct nvc_focus_cap sh532u_default_cap = {
+       .version        = SH532U_CAPS_VER,
+       .actuator_range = SH532U_ACTUATOR_RANGE,
+       .settle_time    = SH532U_SETTLETIME,
+       .focus_macro    = SH532U_FOCUS_MACRO,
+       .focus_hyper    = SH532U_FOCUS_HYPER,
+       .focus_infinity = SH532U_FOCUS_INFINITY,
+};
+
+static struct nvc_focus_nvc sh532u_default_nvc = {
+       .focal_length   = SH532U_FOCAL_LENGTH,
+       .fnumber        = SH532U_FNUMBER,
+       .max_aperature  = SH532U_MAX_APERATURE,
+};
+
+static struct sh532u_platform_data sh532u_default_pdata = {
+       .cfg            = 0,
+       .num            = 0,
+       .sync           = 0,
+       .dev_name       = "focuser",
+       .i2c_addr_rom   = 0x50,
+};
+
+static u32 sh532u_a2buf[] = {
+       0x0018019c,
+       0x0018019d,
+       0x0000019e,
+       0x007f0192,
+       0x00000194,
+       0x00f00184,
+       0x00850187,
+       0x0000018a,
+       0x00fd7187,
+       0x007f7183,
+       0x0008025a,
+       0x05042218,
+       0x80010216,
+       0x000601a0,
+       0x00808183,
+       0xffffffff
 };
 
-static struct sh532u_info *stereo_sh532u_info; /* set to NULL by compiler */
+static LIST_HEAD(sh532u_info_list);
+static DEFINE_SPINLOCK(sh532u_spinlock);
 
-static int sh532u_read_u8(struct i2c_client *client, u8 dev, u8 addr, u8 *val)
+
+static int sh532u_i2c_rd8(struct sh532u_info *info, u8 addr, u8 reg, u8 *val)
 {
        struct i2c_msg msg[2];
-       unsigned char data[3];
+       u8 buf[2];
 
-       if (dev)
-               msg[0].addr = dev;
-       else
-               msg[0].addr = client->addr;
+       buf[0] = reg;
+       if (addr) {
+               msg[0].addr = addr;
+               msg[1].addr = addr;
+       } else {
+               msg[0].addr = info->i2c_client->addr;
+               msg[1].addr = info->i2c_client->addr;
+       }
        msg[0].flags = 0;
        msg[0].len = 1;
-       msg[0].buf = data;
-
-       data[0] = (u8)addr;
-
-       if (dev)
-               msg[1].addr = dev;
-       else
-               msg[1].addr = client->addr;
+       msg[0].buf = &buf[0];
        msg[1].flags = I2C_M_RD;
        msg[1].len = 1;
-       msg[1].buf = data + 2;
+       msg[1].buf = &buf[1];
+       *val = 0;
+       if (i2c_transfer(info->i2c_client->adapter, msg, 2) != 2)
+               return -EIO;
 
-       if (i2c_transfer(client->adapter, msg, 2) != 2)
-               return -1;
-       *val = data[2];
+       *val = buf[1];
        return 0;
 }
 
-static int sh532u_read_u16(struct i2c_client *client, u8 addr, u16 *val)
+static int sh532u_i2c_wr8(struct sh532u_info *info, u8 reg, u8 val)
+{
+       struct i2c_msg msg;
+       u8 buf[2];
+
+       buf[0] = reg;
+       buf[1] = val;
+       msg.addr = info->i2c_client->addr;
+       msg.flags = 0;
+       msg.len = 2;
+       msg.buf = &buf[0];
+       if (i2c_transfer(info->i2c_client->adapter, &msg, 1) != 1)
+               return -EIO;
+
+       return 0;
+}
+
+static int sh532u_i2c_rd16(struct sh532u_info *info, u8 reg, u16 *val)
 {
        struct i2c_msg msg[2];
-       u8 buf[4];
+       u8 buf[3];
 
-       msg[0].addr = client->addr;
+       buf[0] = reg;
+       msg[0].addr = info->i2c_client->addr;
        msg[0].flags = 0;
        msg[0].len = 1;
        msg[0].buf = &buf[0];
-
-       /* high byte goes out first */
-       buf[0] = (u8) (addr);
-
-       msg[1].addr = client->addr;
+       msg[1].addr = info->i2c_client->addr;
        msg[1].flags = I2C_M_RD;
        msg[1].len = 2;
        msg[1].buf = &buf[1];
+       if (i2c_transfer(info->i2c_client->adapter, msg, 2) != 2)
+               return -EIO;
 
-       if (i2c_transfer(client->adapter, msg, 2) != 2)
-               return -1;
        *val = (((u16)buf[1] << 8) | (u16)buf[2]);
        return 0;
 }
 
-static int eeprom_read_u32(struct i2c_client *client, u8 addr, u32 *val)
+
+static int sh532u_i2c_wr16(struct sh532u_info *info, u8 reg, u16 val)
+{
+       struct i2c_msg msg;
+       u8 buf[3];
+
+       buf[0] = reg;
+       buf[1] = (u8)(val >> 8);
+       buf[2] = (u8)(val & 0xff);
+       msg.addr = info->i2c_client->addr;
+       msg.flags = 0;
+       msg.len = 3;
+       msg.buf = &buf[0];
+       if (i2c_transfer(info->i2c_client->adapter, &msg, 1) != 1)
+               return -EIO;
+
+       return 0;
+}
+
+static int sh532u_i2c_rd32(struct sh532u_info *info, u8 addr, u8 reg, u32 *val)
 {
        struct i2c_msg msg[2];
-       union {
-               u8   dataU8[8];
-               u32  dataU32[2];
-       } buffer;
+       u8 buf[5];
 
-       msg[0].addr = 0x50;
+       buf[0] = reg;
+       if (addr) {
+               msg[0].addr = addr;
+               msg[1].addr = addr;
+       } else {
+               msg[0].addr = info->i2c_client->addr;
+               msg[1].addr = info->i2c_client->addr;
+       }
        msg[0].flags = 0;
        msg[0].len = 1;
-       msg[0].buf = &(buffer.dataU8[0]);
-
-       /* high byte goes out first */
-       buffer.dataU8[0] = (u8) (addr);
-       buffer.dataU8[1] = (u8) (0);
-
-       msg[1].addr = 0x50;
+       msg[0].buf = &buf[0];
        msg[1].flags = I2C_M_RD;
        msg[1].len = 4;
-       msg[1].buf = (u8 *)&(buffer.dataU32[1]);
+       msg[1].buf = &buf[1];
+       if (i2c_transfer(info->i2c_client->adapter, msg, 2) != 2)
+               return -EIO;
 
-       if (i2c_transfer(client->adapter, msg, 2) != 2)
-               return -1;
-       *val = buffer.dataU32[1];
+       *val = (((u32)buf[4] << 24) | ((u32)buf[3] << 16) |
+                       ((u32)buf[2] << 8) | ((u32)buf[1]));
        return 0;
 }
 
-static int sh532u_write_u8(struct i2c_client *client, u16 addr, u8 val)
+static void sh532u_gpio_en(struct sh532u_info *info, int val)
 {
-       struct i2c_msg msg;
-       unsigned char data[2];
+       if (info->pdata->gpio_en)
+               gpio_set_value(info->pdata->gpio_en, val);
+}
 
-       data[0] = (u8) (addr & 0xff);
-       data[1] = (u8) (val & 0xff);
+static void sh532u_gpio_reset(struct sh532u_info *info, int val)
+{
+       if (val) {
+               if (!info->gpio_flag_reset && info->pdata->gpio_reset) {
+                       gpio_set_value(info->pdata->gpio_reset, 0);
+                       mdelay(1);
+                       gpio_set_value(info->pdata->gpio_reset, 1);
+                       mdelay(10); /* delay for device startup */
+                       info->gpio_flag_reset = 1;
+               }
+       } else {
+               info->gpio_flag_reset = 0;
+       }
+}
 
-       msg.addr = client->addr;
-       msg.flags = 0;
-       msg.len = 2;
-       msg.buf = data;
+static void sh532u_pm_regulator_put(struct nvc_regulator *sreg)
+{
+       regulator_put(sreg->vreg);
+       sreg->vreg = NULL;
+}
+
+static int sh532u_pm_regulator_get(struct sh532u_info *info,
+                                  struct nvc_regulator *sreg,
+                                  char vreg_name[])
+{
+       int err = 0;
+
+       sreg->vreg_flag = 0;
+       sreg->vreg = regulator_get(&info->i2c_client->dev, vreg_name);
+       if (IS_ERR_OR_NULL(sreg->vreg)) {
+               dev_err(&info->i2c_client->dev,
+                               "%s err for regulator: %s err: %d\n",
+                               __func__, vreg_name, (int)sreg->vreg);
+               err = PTR_ERR(sreg->vreg);
+               sreg->vreg = NULL;
+       } else {
+               sreg->vreg_name = vreg_name;
+               dev_dbg(&info->i2c_client->dev,
+                               "%s vreg_name: %s\n",
+                               __func__, sreg->vreg_name);
+       }
+       return err;
+}
+
+static int sh532u_pm_regulator_en(struct sh532u_info *info,
+                                 struct nvc_regulator *sreg)
+{
+       int err = 0;
+
+       if (!sreg->vreg_flag && (sreg->vreg != NULL)) {
+               err = regulator_enable(sreg->vreg);
+               if (!err) {
+                       dev_dbg(&info->i2c_client->dev,
+                                       "%s vreg_name: %s\n",
+                                       __func__, sreg->vreg_name);
+                       sreg->vreg_flag = 1;
+                       err = 1; /* flag regulator state change */
+               } else {
+                       dev_err(&info->i2c_client->dev,
+                                       "%s err, regulator: %s\n",
+                                       __func__, sreg->vreg_name);
+               }
+       }
+       return err;
+}
+
+static int sh532u_pm_regulator_dis(struct sh532u_info *info,
+                                  struct nvc_regulator *sreg)
+{
+       int err = 0;
+
+       if (sreg->vreg_flag && (sreg->vreg != NULL)) {
+               err = regulator_disable(sreg->vreg);
+               if (err)
+                       dev_err(&info->i2c_client->dev,
+                                       "%s err, regulator: %s\n",
+                                       __func__, sreg->vreg_name);
+       }
+       sreg->vreg_flag = 0;
+       return err;
+}
 
-       if (i2c_transfer(client->adapter, &msg, 1) != 1)
-               return -1;
+static int sh532u_pm_wr(struct sh532u_info *info, int pwr)
+{
+       int err = 0;
+
+       if (pwr == info->pwr_dev)
+               return 0;
+
+       switch (pwr) {
+       case NVC_PWR_OFF_DELAYED:
+       case NVC_PWR_OFF:
+               if ((info->pdata->cfg & NVC_CFG_OFF2STDBY) ||
+                            (info->pdata->cfg & NVC_CFG_BOOT_INIT)) {
+                       pwr = NVC_PWR_STDBY;
+               } else {
+                       sh532u_gpio_en(info, 0);
+                       err = sh532u_pm_regulator_dis(info, &info->vreg_vdd);
+                       err |= sh532u_pm_regulator_dis(info, &info->vreg_i2c);
+                       sh532u_gpio_reset(info, 0);
+                       break;
+               }
+       case NVC_PWR_STDBY_OFF:
+               if ((info->pdata->cfg & NVC_CFG_OFF2STDBY) ||
+                            (info->pdata->cfg & NVC_CFG_BOOT_INIT)) {
+                       pwr = NVC_PWR_STDBY;
+               } else {
+                       err = sh532u_pm_regulator_en(info, &info->vreg_vdd);
+                       err |= sh532u_pm_regulator_en(info, &info->vreg_i2c);
+                       sh532u_gpio_en(info, 1);
+                       sh532u_gpio_reset(info, 0);
+                       break;
+               }
+       case NVC_PWR_STDBY:
+               err = sh532u_pm_regulator_en(info, &info->vreg_vdd);
+               err |= sh532u_pm_regulator_en(info, &info->vreg_i2c);
+               sh532u_gpio_en(info, 1);
+               sh532u_gpio_reset(info, 1);
+               err |= sh532u_i2c_wr8(info, STBY_211, 0x80);
+               err |= sh532u_i2c_wr8(info, CLKSEL_211, 0x38);
+               err |= sh532u_i2c_wr8(info, CLKSEL_211, 0x39);
+               break;
+
+       case NVC_PWR_COMM:
+       case NVC_PWR_ON:
+               err = sh532u_pm_regulator_en(info, &info->vreg_vdd);
+               err |= sh532u_pm_regulator_en(info, &info->vreg_i2c);
+               sh532u_gpio_en(info, 1);
+               sh532u_gpio_reset(info, 1);
+               err |= sh532u_i2c_wr8(info, CLKSEL_211, 0x38);
+               err |= sh532u_i2c_wr8(info, CLKSEL_211, 0x34);
+               err |= sh532u_i2c_wr8(info, STBY_211, 0xF0);
+               break;
+
+       default:
+               err = -EINVAL;
+               break;
+       }
+
+       if (err < 0) {
+               dev_err(&info->i2c_client->dev, "%s pwr err: %d\n",
+                               __func__, pwr);
+               pwr = NVC_PWR_ERR;
+       }
+       info->pwr_dev = pwr;
+       if (err > 0)
+               return 0;
+
+       return err;
+}
+
+static int sh532u_pm_wr_s(struct sh532u_info *info, int pwr)
+{
+       int err1 = 0;
+       int err2 = 0;
+
+       if ((info->s_mode == NVC_SYNC_OFF) ||
+                       (info->s_mode == NVC_SYNC_MASTER) ||
+                       (info->s_mode == NVC_SYNC_STEREO))
+               err1 = sh532u_pm_wr(info, pwr);
+       if ((info->s_mode == NVC_SYNC_SLAVE) ||
+                       (info->s_mode == NVC_SYNC_STEREO))
+               err2 = sh532u_pm_wr(info->s_info, pwr);
+       return err1 | err2;
+}
+
+static int sh532u_pm_api_wr(struct sh532u_info *info, int pwr)
+{
+       int err = 0;
+
+       if (!pwr || (pwr > NVC_PWR_ON))
+               return 0;
+
+       if (pwr > info->pwr_dev)
+               err = sh532u_pm_wr_s(info, pwr);
+       if (!err)
+               info->pwr_api = pwr;
        else
+               info->pwr_api = NVC_PWR_ERR;
+       if (info->pdata->cfg & NVC_CFG_NOERR)
                return 0;
+
+       return err;
 }
 
-static int sh532u_write_u16(struct i2c_client *client, u16 addr, u16 val)
+static int sh532u_pm_dev_wr(struct sh532u_info *info, int pwr)
 {
-       struct i2c_msg msg;
-       unsigned char data[3];
+       if (pwr < info->pwr_api)
+               pwr = info->pwr_api;
+       if (info->sts == NVC_FOCUS_STS_WAIT_FOR_MOVE_END)
+               pwr = NVC_PWR_ON;
+       return sh532u_pm_wr(info, pwr);
+}
 
-       data[0] = (u8) (addr & 0xff);
-       data[1] = (u8) (val >> 8);
-       data[2] = (u8) (val & 0xff);
+static void sh532u_pm_exit(struct sh532u_info *info)
+{
+       sh532u_pm_wr_s(info, NVC_PWR_OFF);
+       sh532u_pm_regulator_put(&info->vreg_vdd);
+       sh532u_pm_regulator_put(&info->vreg_i2c);
+}
 
-       msg.addr = client->addr;
-       msg.flags = 0;
-       msg.len = 3;
-       msg.buf = data;
+static void sh532u_pm_init(struct sh532u_info *info)
+{
+       sh532u_pm_regulator_get(info, &info->vreg_vdd, "vdd");
+       sh532u_pm_regulator_get(info, &info->vreg_i2c, "vdd_i2c");
+}
 
-       if (i2c_transfer(client->adapter, &msg, 1) != 1)
-               return -1;
-       return 0;
+static int sh532u_dev_id(struct sh532u_info *info)
+{
+       u8 val;
+       int err;
+
+       err = sh532u_i2c_rd8(info, 0, HVCA_DEVICE_ID, &val);
+       if (!err && (val == SH532U_ID))
+               return 0;
+
+       return -ENODEV;
 }
 
-static void move_driver(struct i2c_client *client, s16 tarPos)
+static void sh532u_sts_rd(struct sh532u_info *info)
 {
-       s16 curPos, moveStep;
-       u16 moveDistance;
+       u8 us_tmp;
+       u16 us_smv_fin;
        int err;
 
-       /* Read Current Position */
-       err = sh532u_read_u16(client, RZ_211H, &curPos);
+       if (info->sts == NVC_FOCUS_STS_INITIALIZING)
+               return;
+
+       info->sts = NVC_FOCUS_STS_NO_DEVICE; /* assume I2C err */
+       err = sh532u_i2c_rd8(info, 0, STMVEN_211, &us_tmp);
+       err |= sh532u_i2c_rd16(info, RZ_211H, &us_smv_fin);
        if (err)
-               goto move_driver_error;
-       /* Check move distance to Target Position */
-       moveDistance = abs((int)curPos - (int)tarPos);
+               return;
 
-       /* if move distance is shorter than MS1Z12(=Step width) */
-       if (moveDistance <= STMV_SIZE) {
-               err = sh532u_write_u8(client, MSSET_211,
-                                                       (INI_MSSET_211 | 0x01));
-               err = err | sh532u_write_u16(client, MS1Z22_211H, tarPos);
-               if (err)
-                       goto move_driver_error;
+       /* StepMove Error Handling, Unexpected Position */
+       if ((us_smv_fin == 0x7FFF) || (us_smv_fin == 0x8001))
+               /* Stop StepMove Operation */
+               sh532u_i2c_wr8(info, STMVEN_211, us_tmp & 0xFE);
+       if (us_tmp & STMVEN_ON) {
+               err = sh532u_i2c_rd8(info, 0, MSSET_211, &us_tmp);
+               if (!err) {
+                       if (us_tmp & CHTGST_ON)
+                               info->sts = NVC_FOCUS_STS_WAIT_FOR_SETTLE;
+                       else
+                               info->sts = NVC_FOCUS_STS_LENS_SETTLED;
+               }
        } else {
-               if (curPos < tarPos)
-                       moveStep = STMV_SIZE;
-               else
-                       moveStep = -STMV_SIZE;
-
-               /* Set StepMove Target Positon */
-               err = sh532u_write_u16(client, MS1Z12_211H, moveStep);
-               err = err | sh532u_write_u16(client, STMVENDH_211, tarPos);
-               /* Start StepMove */
-               err = err |
-                     sh532u_write_u8(client, STMVEN_211,
-                       (STMCHTG_ON | STMSV_ON | STMLFF_OFF | STMVEN_ON));
-               if (err)
-                       goto move_driver_error;
+               info->sts = NVC_FOCUS_STS_WAIT_FOR_MOVE_END;
        }
-
-       return;
-move_driver_error:
-       pr_err("sh532u: %s failed!\n", __func__);
 }
 
-static void wait_for_move(struct i2c_client *client)
+static s16 sh532u_rel2abs(struct sh532u_info *info, u32 rel_position)
 {
-       u16 usSmvFin;
-       u8 moveTime, ucParMod, tmp;
-       int err;
+       s16 abs_pos;
 
-       moveTime = 0;
-       do {
-               mdelay(1);
-               err = sh532u_read_u8(client, 0, STMVEN_211, &ucParMod);
-               err = err | sh532u_read_u16(client, RZ_211H, &usSmvFin);
-               if (err)
-                       goto wait_for_move_error;
-               /* StepMove Error Handling, Unexpected Position */
-               if ((usSmvFin == 0x7FFF) || (usSmvFin == 0x8001)) {
-                       /* Stop StepMove Operation */
-                       err = sh532u_write_u8(client, STMVEN_211,
-                                                       ucParMod & 0xFE);
-                       if (err)
-                               goto wait_for_move_error;
-               }
-               moveTime++;
-               /* Wait StepMove operation end */
-       } while ((ucParMod & STMVEN_ON) && (moveTime < 50));
+       if (info->cap.actuator_range > 0) {
+               abs_pos = info->cfg.pos_high -
+                               (((info->cfg.pos_high - info->cfg.pos_low) *
+                               rel_position) / info->cap.actuator_range);
+               return abs_pos;
+       } else {
+               return 0;
+       }
+}
 
-       moveTime = 0;
-       if ((ucParMod & 0x08) == STMCHTG_ON) {
-               mdelay(5);
-               do {
-                       mdelay(1);
-                       moveTime++;
-                       err = sh532u_read_u8(client, 0, MSSET_211, &tmp);
-                       if (err)
-                               goto wait_for_move_error;
-               } while ((tmp & CHTGST_ON) && (moveTime < 15));
+static u32 sh532u_abs2rel(struct sh532u_info *info, s16 abs_position)
+{
+       u32 rel_pos;
+
+       if (abs_position > info->cfg.pos_high)
+               abs_position = info->cfg.pos_high;
+       if ((info->cfg.pos_high - info->cfg.pos_low) > 0) {
+               rel_pos = (((info->cfg.pos_high - abs_position) *
+                               info->cap.actuator_range) /
+                               (info->cfg.pos_high - info->cfg.pos_low));
+               return rel_pos;
+       } else {
+               return 0;
        }
+}
 
-       return;
-wait_for_move_error:
-       pr_err("sh532u: %s failed!\n", __func__);
+static int sh532u_abs_pos_rd(struct sh532u_info *info, s16 *position)
+{
+       int err;
+       u16 abs_pos = 0;
+
+       err = sh532u_i2c_rd16(info, RZ_211H, &abs_pos);
+       *position = (s16)abs_pos;
+       return err;
 }
 
-static void lens_move_pulse(struct i2c_client *client, s16 position)
+static int sh532u_rel_pos_rd(struct sh532u_info *info, u32 *position)
 {
-       move_driver(client, position);
-       wait_for_move(client);
+       s16 abs_pos;
+       long msec;
+       int pos;
+       int err;
+
+       err = sh532u_abs_pos_rd(info, &abs_pos);
+       if (err)
+               return -EINVAL;
+
+       if ((abs_pos >= (info->pos_abs - STMV_SIZE)) &&
+                       (abs_pos <= (info->pos_abs + STMV_SIZE))) {
+               pos = (int)info->pos_rel;
+       } else {
+               msec = jiffies;
+               msec -= info->pos_time_wr;
+               msec = msec * 1000 / HZ;
+               sh532u_sts_rd(info);
+               if ((info->sts == NVC_FOCUS_STS_LENS_SETTLED) ||
+                               (msec > info->cfg.move_timeoutms)) {
+                       pos = (int)info->pos_rel;
+               } else {
+                       pos = (int)sh532u_abs2rel(info, abs_pos);
+                       if ((pos == (info->pos_rel - 1)) ||
+                                       (pos == (info->pos_rel + 1)))
+                               pos = (int)info->pos_rel;
+               }
+       }
+       if (pos < 0)
+               pos = 0;
+       *position = (u32)pos;
+       return 0;
 }
 
-static void get_rom_info(struct sh532u_sensor *info)
+static int sh532u_calibration(struct sh532u_info *info)
 {
-       struct i2c_client *client = info->i2c_client;
-       u8 tmp;
+       u8 reg;
+       s16 abs_focus_macro;
+       s16 abs_focus_infinity;
+       u32 abs_focus_hyper;
+       u32 abs_range;
+       u32 rel_range;
+       u32 step;
        int err;
+       int ret = 0;
+
+       if (info->init_cal_flag)
+               return 0;
 
+       /* set defaults */
+       memcpy(&info->cfg, &sh532u_default_info, sizeof(info->cfg));
+       memcpy(&info->nvc, &sh532u_default_nvc, sizeof(info->nvc));
+       memcpy(&info->cap, &sh532u_default_cap, sizeof(info->cap));
+       if (info->pdata->i2c_addr_rom)
+               info->i2c_addr_rom = info->pdata->i2c_addr_rom;
+       else
+               info->i2c_addr_rom = sh532u_default_pdata.i2c_addr_rom;
+       /* set overrides if any */
+       if (info->pdata->nvc) {
+               if (info->pdata->nvc->fnumber)
+                       info->nvc.fnumber = info->pdata->nvc->fnumber;
+               if (info->pdata->nvc->focal_length)
+                       info->nvc.focal_length =
+                                       info->pdata->nvc->focal_length;
+               if (info->pdata->nvc->max_aperature)
+                       info->nvc.max_aperature =
+                                       info->pdata->nvc->max_aperature;
+       }
+       if (info->pdata->cap) {
+               if (info->pdata->cap->actuator_range)
+                       info->cap.actuator_range =
+                                       info->pdata->cap->actuator_range;
+               if (info->pdata->cap->settle_time)
+                       info->cap.settle_time = info->pdata->cap->settle_time;
+               if (info->pdata->cap->focus_macro)
+                       info->cap.focus_macro = info->pdata->cap->focus_macro;
+               if (info->pdata->cap->focus_hyper)
+                       info->cap.focus_hyper = info->pdata->cap->focus_hyper;
+               if (info->pdata->cap->focus_infinity)
+                       info->cap.focus_infinity =
+                                       info->pdata->cap->focus_infinity;
+       }
        /* Get Inf1, Mac1
        Inf1 and Mac1 are the mechanical limit position.
        Inf1     : Bottom limit.
        Mac1 : Top limit. */
-       err = sh532u_read_u8(client, 0x50, addrMac1, &tmp);
-       if (err)
-               goto get_rom_info_error;
-       info->config.limit_low = (tmp<<8) & 0xff00;
-       err = sh532u_read_u8(client, 0x50, addrInf1, &tmp);
-       if (err)
-               goto get_rom_info_error;
-       info->config.limit_high = (tmp<<8) & 0xff00;
-
+       err = sh532u_i2c_rd8(info, info->i2c_addr_rom, addrMac1, &reg);
+       if (!err && (reg != 0) && (reg != 0xFF))
+               info->cfg.limit_low = (reg<<8) & 0xff00;
+       ret = err;
+       err = sh532u_i2c_rd8(info, info->i2c_addr_rom, addrInf1, &reg);
+       if (!err && (reg != 0) && (reg != 0xFF))
+               info->cfg.limit_high = (reg<<8) & 0xff00;
+       ret |= err;
        /* Get Inf2, Mac2
        Inf2 and Mac2 are the calibration data for SEMCO AF lens.
        Inf2: Best focus (lens position) when object distance is 1.2M.
        Mac2: Best focus (lens position) when object distance is 10cm. */
-       err = sh532u_read_u8(client, 0x50, addrMac2, &tmp);
-       if (err)
-               goto get_rom_info_error;
-       info->config.pos_low = (tmp << 8) & 0xff00;
-       err = sh532u_read_u8(client, 0x50, addrInf2, &tmp);
-       if (err)
-               goto get_rom_info_error;
-       info->config.pos_high = (tmp << 8) & 0xff00;
-
-       return;
-get_rom_info_error:
-       pr_err("sh532u: %s failed!\n", __func__);
-       info->config.limit_high = POS_HIGH;
-       info->config.limit_low = POS_LOW;
-       info->config.pos_high = POS_HIGH;
-       info->config.pos_low = POS_LOW;
+       err = sh532u_i2c_rd8(info, info->i2c_addr_rom, addrMac2, &reg);
+       if (!err && (reg != 0) && (reg != 0xFF))
+               info->cfg.pos_low = (reg << 8) & 0xff00;
+       ret |= err;
+       err = sh532u_i2c_rd8(info, info->i2c_addr_rom, addrInf2, &reg);
+       if (!err && (reg != 0) && (reg != 0xFF))
+               info->cfg.pos_high = (reg << 8) & 0xff00;
+       ret |= err;
+       /* set overrides */
+       if (info->pdata->info) {
+               if (info->pdata->info->pos_low)
+                       info->cfg.pos_low = info->pdata->info->pos_low;
+               if (info->pdata->info->pos_high)
+                       info->cfg.pos_high = info->pdata->info->pos_high;
+               if (info->pdata->info->limit_low)
+                       info->cfg.limit_low = info->pdata->info->limit_low;
+               if (info->pdata->info->limit_high)
+                       info->cfg.limit_high = info->pdata->info->limit_high;
+               if (info->pdata->info->move_timeoutms)
+                       info->cfg.move_timeoutms =
+                                       info->pdata->info->move_timeoutms;
+               if (info->pdata->info->focus_hyper_ratio)
+                       info->cfg.focus_hyper_ratio =
+                                       info->pdata->info->focus_hyper_ratio;
+               if (info->pdata->info->focus_hyper_div)
+                       info->cfg.focus_hyper_div =
+                                       info->pdata->info->focus_hyper_div;
+       }
+       if (ret || !info->cfg.pos_low || !info->cfg.pos_high ||
+                       !info->cfg.limit_low || !info->cfg.limit_high) {
+               dev_err(&info->i2c_client->dev, "%s ERR\n", __func__);
+               return -EIO;
+       }
+       /* calculate relative and absolute positions */
+       abs_focus_macro = info->cfg.pos_low;
+       abs_focus_infinity = info->cfg.pos_high;
+       abs_range = (u32)(abs_focus_infinity - abs_focus_macro);
+       rel_range = (info->cap.focus_macro - info->cap.focus_infinity);
+               /* calculate absolute hyperfocus position */
+       abs_focus_hyper = ((abs_range * info->cfg.focus_hyper_ratio) /
+                          info->cfg.focus_hyper_div);
+       abs_focus_hyper = abs_focus_infinity - abs_focus_hyper;
+               /* calculate absolute high end */
+       step = (abs_range * info->cap.focus_infinity) / rel_range;
+       info->cfg.pos_high += step;
+       if (abs_focus_infinity < info->cfg.limit_high) {
+               if (info->cfg.pos_high > info->cfg.limit_high)
+                       info->cfg.pos_high = info->cfg.limit_high;
+       }
+               /* calculate absolute low end */
+       step = (abs_range * (info->cap.actuator_range -
+                            info->cap.focus_macro)) / rel_range;
+       info->cfg.pos_low -= step;
+       if (abs_focus_macro > info->cfg.limit_low) {
+               if (info->cfg.pos_low < info->cfg.limit_low)
+                       info->cfg.pos_low = info->cfg.limit_low;
+       }
+               /* update actual relative positions */
+       info->cap.focus_macro = sh532u_abs2rel(info, abs_focus_macro);
+       info->cap.focus_infinity = sh532u_abs2rel(info, abs_focus_infinity);
+       info->cap.focus_hyper = sh532u_abs2rel(info, (s16)abs_focus_hyper);
+       info->init_cal_flag = 1;
+       dev_dbg(&info->i2c_client->dev, "%s complete\n", __func__);
+       return 0;
 }
 
-static unsigned int a2buf[] = {
-       0x0018019c,
-       0x0018019d,
-       0x0000019e,
-       0x007f0192,
-       0x00000194,
-       0x00f00184,
-       0x00850187,
-       0x0000018a,
-       0x00fd7187,
-       0x007f7183,
-       0x0008025a,
-       0x05042218,
-       0x80010216,
-       0x000601a0,
-       0x00808183,
-       0xffffffff
-};
-
-/* Write 1 byte data to the HVCA Drive IC by data type */
-static void sh532u_hvca_wr1(struct sh532u_sensor *info,
-                                       u8 ep_type, u8 ep_data1, u8 ep_addr)
+       /* Write 1 byte data to the HVCA Drive IC by data type */
+static int sh532u_hvca_wr1(struct sh532u_info *info,
+                          u8 ep_type, u8 ep_data1, u8 ep_addr)
 {
-       struct i2c_client *client = info->i2c_client;
-       int err = 0;
        u8 us_data;
+       int err = 0;
 
        switch (ep_type & 0xF0) {
        case DIRECT_MODE:
@@ -333,45 +770,42 @@ static void sh532u_hvca_wr1(struct sh532u_sensor *info,
                break;
 
        case INDIRECT_EEPROM:
-               err = sh532u_read_u8(client, 0x50, ep_data1, &us_data);
+               err = sh532u_i2c_rd8(info,
+                                    info->i2c_addr_rom,
+                                    ep_data1,
+                                    &us_data);
                break;
 
        case INDIRECT_HVCA:
-               err = sh532u_read_u8(client, 0, (u16)ep_data1, &us_data);
+               err = sh532u_i2c_rd8(info, 0, ep_data1, &us_data);
                break;
 
        case MASK_AND:
-               err = sh532u_read_u8(client, 0, (u16)ep_addr, &us_data);
-               us_data = us_data & ep_data1;
+               err = sh532u_i2c_rd8(info, 0, ep_addr, &us_data);
+               us_data &= ep_data1;
                break;
 
        case MASK_OR:
-               err = sh532u_read_u8(client, 0, (u16)ep_addr, &us_data);
-               us_data = us_data | ep_data1;
+               err = sh532u_i2c_rd8(info, 0, ep_addr, &us_data);
+               us_data |= ep_data1;
                break;
 
        default:
-               err = 1;
+               err = -EINVAL;
        }
        if (!err)
-               err = sh532u_write_u8(client, (u16)ep_addr, us_data);
-
-       /* we output error message when there is I2C error, but we can't do
-        * anything about it nor recover from it. */
-       if (err)
-               pr_err("sh532u: %s: Failed to init!: client=0x%x, ep_addr=0x%2x, us_data=0x%x, ret=%d\n",
-                       __func__, (u32)client, (u32)ep_addr, (u32)us_data, err);
+               err = sh532u_i2c_wr8(info, ep_addr, us_data);
+       return err;
 }
 
-/* Write 2 byte data to the HVCA Drive IC by data type */
-static void sh532u_hvca_wr2(struct sh532u_sensor *info, u8 ep_type,
+       /* Write 2 byte data to the HVCA Drive IC by data type */
+static int sh532u_hvca_wr2(struct sh532u_info *info, u8 ep_type,
                                u8 ep_data1, u8 ep_data2, u8 ep_addr)
 {
-       struct i2c_client *client = info->i2c_client;
-       int err = 0;
        u8 uc_data1;
        u8 uc_data2;
        u16 us_data;
+       int err = 0;
 
        switch (ep_type & 0xF0) {
        case DIRECT_MODE:
@@ -380,63 +814,77 @@ static void sh532u_hvca_wr2(struct sh532u_sensor *info, u8 ep_type,
                break;
 
        case INDIRECT_EEPROM:
-               err = sh532u_read_u8(client, 0x50, (u16)ep_data1,
-                                                       &uc_data1);
-               err = err | sh532u_read_u8(client, 0x50, (u16)ep_data2,
-                       &uc_data2);
+               err = sh532u_i2c_rd8(info,
+                                    info->i2c_addr_rom,
+                                    ep_data1,
+                                    &uc_data1);
+               err |= sh532u_i2c_rd8(info,
+                                     info->i2c_addr_rom,
+                                     ep_data2,
+                                     &uc_data2);
                us_data = (((u16)uc_data1 << 8) & 0xFF00) |
-                       ((u16)uc_data2 & 0x00FF);
+                               ((u16)uc_data2 & 0x00FF);
                break;
 
        case INDIRECT_HVCA:
-               err = sh532u_read_u8(client, 0, (u16)ep_data1, &uc_data1);
-               err = err | sh532u_read_u8(client, 0, (u16)ep_data2, &uc_data2);
+               err = sh532u_i2c_rd8(info, 0, ep_data1, &uc_data1);
+               err |= sh532u_i2c_rd8(info, 0, ep_data2, &uc_data2);
                us_data = (((u16)uc_data1 << 8) & 0xFF00) |
-                       ((u16)uc_data2 & 0x00FF);
+                               ((u16)uc_data2 & 0x00FF);
                break;
 
        case MASK_AND:
-               err = sh532u_read_u16(client, (u16)ep_addr, &us_data);
-               us_data = us_data & ((((u16)ep_data1 << 8) & 0xFF00) |
-                       ((u16)ep_data2 & 0x00FF));
+               err = sh532u_i2c_rd16(info, ep_addr, &us_data);
+               us_data &= ((((u16)ep_data1 << 8) & 0xFF00) |
+                           ((u16)ep_data2 & 0x00FF));
                break;
 
        case MASK_OR:
-               err = sh532u_read_u16(client, (u16)ep_addr, &us_data);
-               us_data = us_data | ((((u16)ep_data1 << 8) & 0xFF00) |
-                       ((u16)ep_data2 & 0x00FF));
+               err = sh532u_i2c_rd16(info, ep_addr, &us_data);
+               us_data |= ((((u16)ep_data1 << 8) & 0xFF00) |
+                           ((u16)ep_data2 & 0x00FF));
                break;
 
        default:
-               err = 1;
+               err = -EINVAL;
        }
        if (!err)
-               err = sh532u_write_u16(client, (u16)ep_addr, us_data);
-
-       /* we output error message when there is I2C error, but we can't do
-        * anything about it nor recover from it. */
-       if (err)
-               pr_err("sh532u: %s: Failed to init!: client=0x%x, ep_addr=0x%2x, us_data=0x%x, ret=%d\n",
-                       __func__, (u32)client, (u32)ep_addr, (u32)us_data, err);
+               err = sh532u_i2c_wr16(info, ep_addr, us_data);
+       return err;
 }
 
-static void init_driver(struct sh532u_sensor *info)
+static int sh532u_dev_init(struct sh532u_info *info)
 {
-       int eeprom_addr;
-       unsigned int eeprom_data = 0;
-       u8 ep_addr, ep_type, ep_data1, ep_data2;
-
-       pr_info("sh532u: init_driver: i2c_client = 0x%x\n",
-                       (u32)info->i2c_client);
-
-       for (eeprom_addr = 0x30; eeprom_addr <= 0x013C; eeprom_addr += 4) {
-               if (eeprom_addr > 0xff) {
+       int eeprom_reg;
+       unsigned eeprom_data = 0;
+       u8 ep_addr;
+       u8 ep_type;
+       u8 ep_data1;
+       u8 ep_data2;
+       int err;
+       int ret = 0;
+
+       err = sh532u_i2c_rd8(info, 0, SWTCH_211, &ep_data1);
+       ep_data2 = ep_data1;
+       err |= sh532u_i2c_rd8(info, 0, ANA1_211, &ep_data1);
+       ep_data2 |= ep_data1;
+       if (!err && ep_data2)
+               return 0; /* Already initialized */
+
+       info->sts = NVC_FOCUS_STS_INITIALIZING;
+       for (eeprom_reg = 0x30; eeprom_reg <= 0x013C; eeprom_reg += 4) {
+               if (eeprom_reg > 0xFF) {
                        /* use hardcoded data instead */
-                       eeprom_data = a2buf[(eeprom_addr & 0xFF) / 4];
+                       eeprom_data = sh532u_a2buf[(eeprom_reg & 0xFF) / 4];
                } else {
-                       if (eeprom_read_u32(info->i2c_client,
-                               eeprom_addr & 0xFF, &eeprom_data))
-                               pr_info("sh532u: cannot read eeprom\n");
+                       err = (sh532u_i2c_rd32(info,
+                                           info->i2c_addr_rom,
+                                           eeprom_reg & 0xFF,
+                                           &eeprom_data));
+                       if (err) {
+                               ret |= err;
+                               continue;
+                       }
                }
 
                /* HVCA Address to write eeprom Data1,Data2 by the Data type */
@@ -444,7 +892,6 @@ static void init_driver(struct sh532u_sensor *info)
                ep_type = (u8)((eeprom_data & 0x0000ff00) >> 8);
                ep_data1 = (u8)((eeprom_data & 0x00ff0000) >> 16);
                ep_data2 = (u8)((eeprom_data & 0xff000000) >> 24);
-
                if (ep_addr == 0xFF)
                        break;
 
@@ -452,377 +899,698 @@ static void init_driver(struct sh532u_sensor *info)
                        mdelay((unsigned int)((ep_data1 << 8) | ep_data2));
                } else {
                        if ((ep_type & 0x0F) == DATA_1BYTE) {
-                               sh532u_hvca_wr1(info, ep_type, ep_data1,
-                                                               ep_addr);
+                               err = sh532u_hvca_wr1(info,
+                                                     ep_type,
+                                                     ep_data1,
+                                                     ep_addr);
                        } else {
-                               sh532u_hvca_wr2(info,
-                                               ep_type,
-                                               ep_data1,
-                                               ep_data2,
-                                               ep_addr);
+                               err = sh532u_hvca_wr2(info,
+                                                     ep_type,
+                                                     ep_data1,
+                                                     ep_data2,
+                                                     ep_addr);
                        }
                }
+               ret |= err;
        }
 
-       get_rom_info(info);
+       err = ret;
+       if (err)
+               dev_err(&info->i2c_client->dev, "%s programming err=%d\n",
+                               __func__, err);
+       err |= sh532u_calibration(info);
+       info->sts = NVC_FOCUS_STS_LENS_SETTLED;
+       return err;
 }
 
-
-static int sh532u_set_position(struct sh532u_sensor *info, s16 position)
+static int sh532u_move_lens(struct sh532u_info *info, s16 tar_pos)
 {
-       if (position > info->config.limit_high)
-               return -1;
-       /* Caller's responsibility to check motor status. */
-       move_driver(info->i2c_client, position);
-       return 0;
-}
+       s16 cur_pos;
+       s16 move_step;
+       u16 move_distance;
+       int err;
 
-static int sh532u_get_move_status(struct sh532u_sensor *info, unsigned long arg)
-{
-       struct i2c_client *client = info->i2c_client;
-       enum sh532u_move_status status = SH532U_Forced32;
-       u8 ucTmp;
-       u16 usSmvFin;
-       int err = sh532u_read_u8(client, 0, STMVEN_211, &ucTmp) |
-               sh532u_read_u16(client, RZ_211H, &usSmvFin);
+       sh532u_pm_dev_wr(info, NVC_PWR_ON);
+       err = sh532u_dev_init(info);
        if (err)
                return err;
 
-       /* StepMove Error Handling, Unexpected Position */
-       if ((usSmvFin == 0x7FFF) || (usSmvFin == 0x8001)) {
-               /* Stop StepMove Operation */
-               err = sh532u_write_u8(client, STMVEN_211, ucTmp & 0xFE);
-               if (err)
-                       return err;
-       }
+       /* Read Current Position */
+       err = sh532u_abs_pos_rd(info, &cur_pos);
+       if (err)
+               return err;
 
-       if (ucTmp & STMVEN_ON) {
-               err = sh532u_read_u8(client, 0, MSSET_211, &ucTmp);
-               if (err)
-                       return err;
-               if  (ucTmp & CHTGST_ON)
-                       status = SH532U_WAIT_FOR_SETTLE;
+       info->sts = NVC_FOCUS_STS_WAIT_FOR_MOVE_END;
+       /* Check move distance to Target Position */
+       move_distance = abs((int)cur_pos - (int)tar_pos);
+       /* if move distance is shorter than MS1Z12(=Step width) */
+       if (move_distance <= STMV_SIZE) {
+               err = sh532u_i2c_wr8(info, MSSET_211,
+                                    (INI_MSSET_211 | 0x01));
+               err |= sh532u_i2c_wr16(info, MS1Z22_211H, tar_pos);
+       } else {
+               if (cur_pos < tar_pos)
+                       move_step = STMV_SIZE;
                else
-                       status = SH532U_LENS_SETTLED;
-       } else
-               status = SH532U_WAIT_FOR_MOVE_END;
-
-       if (copy_to_user((void __user *) arg, &status,
-                        sizeof(enum sh532u_move_status))) {
-               pr_info("Error in copying move status: %s: %d\n",
-                               __func__, __LINE__);
-               return -EFAULT;
+                       move_step = -STMV_SIZE;
+               /* Set StepMove Target Positon */
+               err = sh532u_i2c_wr16(info, MS1Z12_211H, move_step);
+               err |= sh532u_i2c_wr16(info, STMVENDH_211, tar_pos);
+               /* Start StepMove */
+               err |= sh532u_i2c_wr8(info, STMVEN_211,
+                                     (STMCHTG_ON |
+                                      STMSV_ON |
+                                      STMLFF_OFF |
+                                      STMVEN_ON));
        }
-       return 0;
+       return err;
 }
 
-static long sh532u_ioctl_helper(
-       struct sh532u_sensor *info,
-       unsigned int cmd,
-       unsigned long arg)
+static int sh532u_move_wait(struct sh532u_info *info)
 {
-       switch (cmd) {
-       case SH532U_IOCTL_GET_CONFIG:
-               if (copy_to_user((void __user *) arg, &info->config,
-                                sizeof(info->config))) {
-                       pr_err("Error in copying config: %s: %d\n",
-                                       __func__, __LINE__);
-                       return -EFAULT;
+       u16 us_smv_fin;
+       u8 moveTime;
+       u8 ucParMod;
+       u8 tmp;
+       int err;
+
+       moveTime = 0;
+       do {
+               mdelay(1);
+               err = sh532u_i2c_rd8(info, 0, STMVEN_211, &ucParMod);
+               err |= sh532u_i2c_rd16(info, RZ_211H, &us_smv_fin);
+               if (err)
+                       return err;
+
+               /* StepMove Error Handling, Unexpected Position */
+               if ((us_smv_fin == 0x7FFF) || (us_smv_fin == 0x8001)) {
+                       /* Stop StepMove Operation */
+                       err = sh532u_i2c_wr8(info, STMVEN_211,
+                                            ucParMod & 0xFE);
+                       if (err)
+                               return err;
                }
-               return 0;
 
-       case SH532U_IOCTL_SET_POSITION:
-               return sh532u_set_position(info, (s16)(arg & 0xffff));
+               moveTime++;
+               /* Wait StepMove operation end */
+       } while ((ucParMod & STMVEN_ON) && (moveTime < 50));
 
-       case SH532U_IOCTL_GET_MOVE_STATUS:
-               return sh532u_get_move_status(info, arg);
+       moveTime = 0;
+       if ((ucParMod & 0x08) == STMCHTG_ON) {
+               mdelay(5);
+               do {
+                       mdelay(1);
+                       moveTime++;
+                       err = sh532u_i2c_rd8(info, 0, MSSET_211, &tmp);
+                       if (err)
+                               return err;
 
-       default:
-               return -EINVAL;
+               } while ((tmp & CHTGST_ON) && (moveTime < 15));
        }
+       return err;
 }
 
-static long sh532u_ioctl(
-       struct file *file,
-       unsigned int cmd,
-       unsigned long arg)
+static int sh532u_move_pulse(struct sh532u_info *info, s16 position)
 {
-       struct sh532u_info *stereo_info = file->private_data;
-       int ret;
-
-       /* select a camera */
-       if (cmd == SH532U_IOCTL_SET_CAMERA_MODE) {
-               stereo_info->camera_mode = arg;
-               return 0;
-       }
-
-       if (StereoCameraMode_Left & stereo_info->camera_mode) {
-               ret = sh532u_ioctl_helper(stereo_info->left, cmd, arg);
-               if (ret)
-                       return ret;
-
-               if (stereo_info->camera_mode == StereoCameraMode_Stereo) {
-                       /* To be finalized for stereo */
-                       if (cmd != SH532U_IOCTL_GET_CONFIG)
-                               ret = sh532u_ioctl_helper(stereo_info->right,
-                                                               cmd, arg);
-               }
-               return ret;
-       }
-
-       if (StereoCameraMode_Right & stereo_info->camera_mode)
-               return sh532u_ioctl_helper(stereo_info->right, cmd, arg);
+       int err;
 
-       return 0;
+       err = sh532u_move_lens(info, position);
+       err |= sh532u_move_wait(info);
+       return err;
 }
 
-static void sh532u_open_helper(struct sh532u_sensor *info)
+static int sh532u_hvca_pos_init(struct sh532u_info *info)
 {
-       if (info->pdata.board_init)
-               info->pdata.board_init(info->pdata.context_data);
-       init_driver(info);
+       s16 limit_bottom;
+       s16 limit_top;
+       int err;
+
+       limit_bottom = (((int)info->cfg.limit_low * 5) >> 3) & 0xFFC0;
+       if (limit_bottom < info->cfg.limit_low)
+               limit_bottom = info->cfg.limit_low;
+       limit_top = (((int)info->cfg.limit_high * 5) >> 3) & 0xFFC0;
+       if (limit_top > info->cfg.limit_high)
+               limit_top = info->cfg.limit_high;
+       err = sh532u_move_pulse(info, limit_bottom);
+       err |= sh532u_move_pulse(info, limit_top);
+       err |= sh532u_move_pulse(info, info->cfg.pos_high);
+       return err;
 }
 
-static void sh532u_release_helper(struct sh532u_sensor *info)
+static int sh532u_pos_abs_wr(struct sh532u_info *info, s16 position)
 {
-       if (info->pdata.board_deinit)
-               info->pdata.board_deinit(info->pdata.context_data);
+       if (position > info->cfg.limit_high || position < info->cfg.limit_low)
+               return -EINVAL;
+
+       return sh532u_move_lens(info, position);
 }
 
-static int sh532u_open(struct inode *inode, struct file *file)
+static int sh532u_pos_rel_wr(struct sh532u_info *info, u32 position)
 {
-       pr_info("sh532u open: camera_mode: %2d\n",
-                       stereo_sh532u_info->camera_mode);
+       s16 abs_pos;
 
-       file->private_data = stereo_sh532u_info;
+       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);
+}
 
-       if (StereoCameraMode_Left & stereo_sh532u_info->camera_mode)
-               sh532u_open_helper(stereo_sh532u_info->left);
 
-       if (StereoCameraMode_Right & stereo_sh532u_info->camera_mode)
-               sh532u_open_helper(stereo_sh532u_info->right);
+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;
+       int err;
 
-       return 0;
-}
+       if (copy_from_user(&params,
+                       (const void __user *)arg,
+                       sizeof(struct nvc_param))) {
+               dev_err(&info->i2c_client->dev, "%s %d copy_from_user err\n",
+                               __func__, __LINE__);
+               return -EFAULT;
+       }
 
-int sh532u_release(struct inode *inode, struct file *file)
-{
-       struct sh532u_info *info = file->private_data;
+       if (info->s_mode == NVC_SYNC_SLAVE)
+               info = info->s_info;
+       switch (params.param) {
+       case NVC_PARAM_LOCUS:
+               sh532u_pm_dev_wr(info, NVC_PWR_COMM);
+               err = sh532u_rel_pos_rd(info, &position);
+               if (err && !(info->pdata->cfg & NVC_CFG_NOERR))
+                       return -EINVAL;
+
+               data_ptr = &position;
+               data_size = sizeof(position);
+               sh532u_pm_dev_wr(info, NVC_PWR_STDBY);
+               dev_dbg(&info->i2c_client->dev, "%s LOCUS: %d\n",
+                               __func__, position);
+               break;
 
-       pr_info("sh532u release: camera_mode: %2d\n",
-                       info->camera_mode);
+       case NVC_PARAM_FOCAL_LEN:
+               data_ptr = &info->nvc.focal_length;
+               data_size = sizeof(info->nvc.focal_length);
+               dev_dbg(&info->i2c_client->dev, "%s FOCAL_LEN: %x\n",
+                               __func__, info->nvc.focal_length);
+               break;
 
-       if (StereoCameraMode_Left & info->camera_mode)
-               sh532u_release_helper(info->left);
+       case NVC_PARAM_MAX_APERTURE:
+               data_ptr = &info->nvc.max_aperature;
+               data_size = sizeof(info->nvc.max_aperature);
+               dev_dbg(&info->i2c_client->dev, "%s MAX_APERTURE: %x\n",
+                               __func__, info->nvc.max_aperature);
+               break;
 
-       if (StereoCameraMode_Right & info->camera_mode)
-               sh532u_release_helper(info->right);
+       case NVC_PARAM_FNUMBER:
+               data_ptr = &info->nvc.fnumber;
+               data_size = sizeof(info->nvc.fnumber);
+               dev_dbg(&info->i2c_client->dev, "%s FNUMBER: %x\n",
+                               __func__, info->nvc.fnumber);
+               break;
 
-       file->private_data = NULL;
-       return 0;
-}
+       case NVC_PARAM_CAPS:
+               sh532u_pm_dev_wr(info, NVC_PWR_COMM);
+               err = sh532u_calibration(info);
+               sh532u_pm_dev_wr(info, NVC_PWR_STDBY);
+               if (err)
+                       return -EIO;
 
+               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__);
+               break;
 
-static const struct file_operations sh532u_fileops = {
-       .owner = THIS_MODULE,
-       .open = sh532u_open,
-       .unlocked_ioctl = sh532u_ioctl,
-       .release = sh532u_release,
-};
+       case NVC_PARAM_STS:
+               data_ptr = &info->sts;
+               data_size = sizeof(info->sts);
+               dev_dbg(&info->i2c_client->dev, "%s STS: %d\n",
+                               __func__, info->sts);
+               break;
 
-static struct miscdevice sh532u_device = {
-       .minor = MISC_DYNAMIC_MINOR,
-       .name = "sh532u",
-       .fops = &sh532u_fileops,
-};
+       case NVC_PARAM_STEREO:
+               data_ptr = &info->s_mode;
+               data_size = sizeof(info->s_mode);
+               dev_dbg(&info->i2c_client->dev, "%s STEREO: %d\n",
+                               __func__, info->s_mode);
+               break;
 
-static int sh532u_probe_init(struct i2c_client *client,
-                               struct sh532u_sensor **info)
-{
-       struct sh532u_platform_data *pdata = client->dev.platform_data;
-       struct sh532u_sensor *p_info =
-               kzalloc(sizeof(struct sh532u_sensor), GFP_KERNEL);
-       if (!p_info) {
-               pr_err("%s\n", "sh532u_sensor: Unable to allocate memory!\n");
-               return -ENOMEM;
+       default:
+               dev_err(&info->i2c_client->dev,
+                               "%s unsupported parameter: %d\n",
+                               __func__, params.param);
+               return -EINVAL;
        }
 
-       p_info->i2c_client = client;
-       p_info->config.settle_time = SETTLETIME_MS;
-       p_info->config.focal_length = FOCAL_LENGTH;
-       p_info->config.fnumber = FNUMBER;
-       p_info->config.pos_low = POS_LOW;
-       p_info->config.pos_high = POS_HIGH;
-       i2c_set_clientdata(client, p_info);
+       if (params.sizeofvalue < data_size) {
+               dev_err(&info->i2c_client->dev, "%s %d data size err\n",
+                               __func__, __LINE__);
+               return -EINVAL;
+       }
 
-       if (pdata) {
-               p_info->pdata.context_data = pdata->context_data;
-               p_info->pdata.board_init = pdata->board_init;
-               p_info->pdata.board_deinit = pdata->board_deinit;
+       if (copy_to_user((void __user *)params.p_value,
+                        data_ptr,
+                        data_size)) {
+               dev_err(&info->i2c_client->dev, "%s %d copy_to_user err\n",
+                               __func__, __LINE__);
+               return -EFAULT;
        }
 
-       *info = p_info;
        return 0;
 }
 
-static int sh532u_probe_helper(struct i2c_client *client)
+static int sh532u_param_wr_s(struct sh532u_info *info,
+                            struct nvc_param *params,
+                            u32 u32_val)
 {
        int err;
 
-       if (!stereo_sh532u_info) {
-               stereo_sh532u_info = kzalloc(sizeof(struct sh532u_info),
-                                                               GFP_KERNEL);
-               if (!stereo_sh532u_info) {
-                       pr_err("%s\n",
-                               "sh532u_info: Unable to allocate memory!\n");
-                       return -ENOMEM;
-               }
+       switch (params->param) {
+       case NVC_PARAM_LOCUS:
+               dev_dbg(&info->i2c_client->dev, "%s LOCUS: %u\n",
+                               __func__, u32_val);
+               err = sh532u_pos_rel_wr(info, u32_val);
+               return err;
 
-               err = misc_register(&sh532u_device);
-               if (err) {
-                       pr_err("sh532u: Unable to register sh532u device!\n");
-                       kfree(stereo_sh532u_info);
-                       stereo_sh532u_info = NULL;
-                       return err;
-               }
+       case NVC_PARAM_RESET:
+               err = sh532u_pm_wr(info, NVC_PWR_OFF);
+               err |= sh532u_pm_wr(info, NVC_PWR_ON);
+               err |= sh532u_pm_wr(info, info->pwr_api);
+               dev_dbg(&info->i2c_client->dev, "%s RESET: %d\n",
+                               __func__, err);
+               return err;
+
+       case NVC_PARAM_SELF_TEST:
+               err = sh532u_hvca_pos_init(info);
+               dev_dbg(&info->i2c_client->dev, "%s SELF_TEST: %d\n",
+                               __func__, err);
+               return err;
+
+       default:
+               dev_err(&info->i2c_client->dev,
+                               "%s unsupported parameter: %d\n",
+                               __func__, params->param);
+               return -EINVAL;
+       }
+}
+
+static int sh532u_param_wr(struct sh532u_info *info, unsigned long arg)
+{
+       struct nvc_param params;
+       u8 val;
+       u32 u32_val;
+       int err = 0;
+
+       if (copy_from_user(&params,
+                               (const void __user *)arg,
+                               sizeof(struct nvc_param))) {
+               dev_err(&info->i2c_client->dev, "%s %d copy_from_user err\n",
+                               __func__, __LINE__);
+               return -EFAULT;
+       }
+
+       if (copy_from_user(&u32_val, (const void __user *)params.p_value,
+                          sizeof(u32_val))) {
+               dev_err(&info->i2c_client->dev, "%s %d copy_from_user err\n",
+                               __func__, __LINE__);
+               return -EFAULT;
+       }
+
+       /* parameters independent of sync mode */
+       switch (params.param) {
+       case NVC_PARAM_STEREO:
+               dev_dbg(&info->i2c_client->dev, "%s STEREO: %u\n",
+                               __func__, u32_val);
+               val = (u8)u32_val;
+               if (val == info->s_mode)
+                       return 0;
+
+               switch (val) {
+               case NVC_SYNC_OFF:
+                       info->s_mode = val;
+                       if (info->s_info != NULL) {
+                               info->s_info->s_mode = val;
+                               sh532u_pm_wr(info->s_info, NVC_PWR_OFF);
+                       }
+                       break;
+
+               case NVC_SYNC_MASTER:
+                       info->s_mode = val;
+                       if (info->s_info != NULL)
+                               info->s_info->s_mode = val;
+                       break;
+
+               case NVC_SYNC_SLAVE:
+                       if (info->s_info != NULL) {
+                               /* default slave lens position */
+                               err = sh532u_pos_rel_wr(info->s_info,
+                                            info->s_info->cap.focus_infinity);
+                               if (!err) {
+                                       info->s_mode = val;
+                                       info->s_info->s_mode = val;
+                               } else {
+                                       if (info->s_mode != NVC_SYNC_STEREO)
+                                               sh532u_pm_wr(info->s_info,
+                                                            NVC_PWR_OFF);
+                                       err = -EIO;
+                               }
+                       } else {
+                               err = -EINVAL;
+                       }
+                       break;
+
+               case NVC_SYNC_STEREO:
+                       if (info->s_info != NULL) {
+                               /* sync power */
+                               info->s_info->pwr_api = info->pwr_api;
+                               /* move slave lens to master position */
+                               err = sh532u_pos_rel_wr(info->s_info,
+                                                        info->pos_rel);
+                               if (!err) {
+                                       info->s_mode = val;
+                                       info->s_info->s_mode = val;
+                               } else {
+                                       if (info->s_mode != NVC_SYNC_SLAVE)
+                                               sh532u_pm_wr(info->s_info,
+                                                            NVC_PWR_OFF);
+                                       err = -EIO;
+                               }
+                       } else {
+                               err = -EINVAL;
+                       }
+                       break;
 
-               err = sh532u_probe_init(client, &stereo_sh532u_info->left);
-               if (err) {
-                       kfree(stereo_sh532u_info);
-                       stereo_sh532u_info = NULL;
-                       return -ENOMEM;
+               default:
+                       err = -EINVAL;
                }
+               if (info->pdata->cfg & NVC_CFG_NOERR)
+                       return 0;
+
+               return err;
+
+       default:
+       /* parameters dependent on sync mode */
+               switch (info->s_mode) {
+               case NVC_SYNC_OFF:
+               case NVC_SYNC_MASTER:
+                       return sh532u_param_wr_s(info, &params, u32_val);
+
+               case NVC_SYNC_SLAVE:
+                       return sh532u_param_wr_s(info->s_info,
+                                                &params,
+                                                u32_val);
+
+               case NVC_SYNC_STEREO:
+                       err = sh532u_param_wr_s(info, &params, u32_val);
+                       if (!(info->pdata->cfg & NVC_CFG_SYNC_I2C_MUX))
+                               err |= sh532u_param_wr_s(info->s_info,
+                                                        &params,
+                                                        u32_val);
+                       return err;
 
-               err = sh532u_probe_init(client, &stereo_sh532u_info->right);
-               if (err) {
-                       kfree(stereo_sh532u_info);
-                       stereo_sh532u_info = NULL;
-                       kfree(stereo_sh532u_info->left);
-                       return -ENOMEM;
+               default:
+                       dev_err(&info->i2c_client->dev, "%s %d internal err\n",
+                                       __func__, __LINE__);
+                       return -EINVAL;
                }
-               stereo_sh532u_info->camera_mode = StereoCameraMode_Left;
        }
-
-       return 0;
 }
 
-static int left_sh532u_probe(
-       struct i2c_client *client,
-       const struct i2c_device_id *id)
+static long sh532u_ioctl(struct file *file,
+               unsigned int cmd,
+               unsigned long arg)
 {
-       int err ;
+       struct sh532u_info *info = file->private_data;
+       int pwr;
 
-       pr_info("left_sh532u: probing sensor: i2c_client=0x%x\n", (u32)client);
+       switch (cmd) {
+       case NVC_IOCTL_PARAM_WR:
+               return sh532u_param_wr(info, arg);
+
+       case NVC_IOCTL_PARAM_RD:
+               return sh532u_param_rd(info, arg);
+
+       case NVC_IOCTL_PWR_WR:
+               /* This is a Guaranteed Level of Service (GLOS) call */
+               pwr = (int)arg * 2;
+               dev_dbg(&info->i2c_client->dev, "%s PWR: %d\n",
+                               __func__, pwr);
+               return sh532u_pm_api_wr(info, pwr);
+
+       case NVC_IOCTL_PWR_RD:
+               if (info->s_mode == NVC_SYNC_SLAVE)
+                       pwr = info->s_info->pwr_api / 2;
+               else
+                       pwr = info->pwr_api / 2;
+               dev_dbg(&info->i2c_client->dev, "%s PWR_RD: %d\n",
+                               __func__, pwr);
+               if (copy_to_user((void __user *)arg, (const void *)&pwr,
+                                sizeof(pwr))) {
+                       dev_err(&info->i2c_client->dev,
+                                       "%s copy_to_user err line %d\n",
+                                       __func__, __LINE__);
+                       return -EFAULT;
+               }
 
-       err = sh532u_probe_helper(client);
+               return 0;
 
-       return err;
+       default:
+               dev_err(&info->i2c_client->dev, "%s unsupported ioctl: %x\n",
+                               __func__, cmd);
+               return -EINVAL;
+       }
+}
+
+static int sh532u_sync_en(int dev1, int dev2)
+{
+       struct sh532u_info *sync1 = NULL;
+       struct sh532u_info *sync2 = NULL;
+       struct sh532u_info *pos = NULL;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(pos, &sh532u_info_list, list) {
+               if (pos->pdata->num == dev1) {
+                       sync1 = pos;
+                       break;
+               }
+       }
+       pos = NULL;
+       list_for_each_entry_rcu(pos, &sh532u_info_list, list) {
+               if (pos->pdata->num == dev2) {
+                       sync2 = pos;
+                       break;
+               }
+       }
+       rcu_read_unlock();
+       if (sync1 != NULL)
+               sync1->s_info = NULL;
+       if (sync2 != NULL)
+               sync2->s_info = NULL;
+       if (!dev1 && !dev2)
+               return 0; /* no err if default instance 0's used */
+
+       if (dev1 == dev2)
+               return -EINVAL; /* err if sync instance is itself */
+
+       if ((sync1 != NULL) && (sync2 != NULL)) {
+               sync1->s_info = sync2;
+               sync2->s_info = sync1;
+       }
+       return 0;
 }
 
-static int left_sh532u_remove(struct i2c_client *client)
+static int sh532u_sync_dis(struct sh532u_info *info)
 {
-       pr_info("left_sh532u to be removed\n");
-       if (!stereo_sh532u_info || !client) {
-               pr_info("left_sh532u_remove(): NULL pointers\n");
+       if (info->s_info != NULL) {
+               info->s_info->s_mode = 0;
+               info->s_info->s_info = NULL;
+               info->s_mode = 0;
+               info->s_info = NULL;
                return 0;
        }
 
-       kfree(stereo_sh532u_info->left);
-       stereo_sh532u_info->left = NULL;
+       return -EINVAL;
+}
 
-       if (!stereo_sh532u_info->right) {
-               misc_deregister(&sh532u_device);
-               kfree(stereo_sh532u_info);
-               stereo_sh532u_info = NULL;
+static int sh532u_open(struct inode *inode, struct file *file)
+{
+       struct sh532u_info *info = NULL;
+       struct sh532u_info *pos = NULL;
+       int err;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(pos, &sh532u_info_list, list) {
+               if (pos->miscdev.minor == iminor(inode)) {
+                       info = pos;
+                       break;
+               }
+       }
+       rcu_read_unlock();
+       if (!info)
+               return -ENODEV;
+
+       err = sh532u_sync_en(info->pdata->num, info->pdata->sync);
+       if (err == -EINVAL)
+               dev_err(&info->i2c_client->dev,
+                        "%s err: invalid num (%u) and sync (%u) instance\n",
+                        __func__, info->pdata->num, info->pdata->sync);
+       if (atomic_xchg(&info->in_use, 1))
+               return -EBUSY;
+
+       if (info->s_info != NULL) {
+               if (atomic_xchg(&info->s_info->in_use, 1))
+                       return -EBUSY;
        }
 
+       file->private_data = info;
+       dev_dbg(&info->i2c_client->dev, "%s\n", __func__);
+       sh532u_pos_rel_wr(info, info->cap.focus_infinity);
        return 0;
 }
 
-static const struct i2c_device_id left_sh532u_id[] = {
-       { "sh532u", 0 },
-       { "sh532uL", 0 },
-       { },
-};
+int sh532u_release(struct inode *inode, struct file *file)
+{
+       struct sh532u_info *info = file->private_data;
 
-MODULE_DEVICE_TABLE(i2c, left_sh532u_id);
+       dev_dbg(&info->i2c_client->dev, "%s\n", __func__);
+       sh532u_pm_wr_s(info, NVC_PWR_OFF);
+       file->private_data = NULL;
+       WARN_ON(!atomic_xchg(&info->in_use, 0));
+       if (info->s_info != NULL)
+               WARN_ON(!atomic_xchg(&info->s_info->in_use, 0));
+       sh532u_sync_dis(info);
+       return 0;
+}
 
-static struct i2c_driver left_sh532u_i2c_driver = {
-       .driver = {
-               .name = "sh532uL",
-               .owner = THIS_MODULE,
-       },
-       .probe = left_sh532u_probe,
-       .remove = left_sh532u_remove,
-       .id_table = left_sh532u_id,
+static const struct file_operations sh532u_fileops = {
+       .owner = THIS_MODULE,
+       .open = sh532u_open,
+       .unlocked_ioctl = sh532u_ioctl,
+       .release = sh532u_release,
 };
 
-static int right_sh532u_probe(
-       struct i2c_client *client,
-       const struct i2c_device_id *id)
+static void sh532u_del(struct sh532u_info *info)
 {
-       int err ;
-
-       pr_info("right_sh532u: probing sensor: i2c_client=0x%x\n", (u32)client);
+       sh532u_pm_exit(info);
+       sh532u_sync_dis(info);
+       spin_lock(&sh532u_spinlock);
+       list_del_rcu(&info->list);
+       spin_unlock(&sh532u_spinlock);
+       synchronize_rcu();
+}
 
-       err = sh532u_probe_helper(client);
+static int sh532u_remove(struct i2c_client *client)
+{
+       struct sh532u_info *info = i2c_get_clientdata(client);
 
-       return err;
+       dev_dbg(&info->i2c_client->dev, "%s\n", __func__);
+       misc_deregister(&info->miscdev);
+       sh532u_del(info);
+       return 0;
 }
 
-static int right_sh532u_remove(struct i2c_client *client)
+static int sh532u_probe(
+       struct i2c_client *client,
+       const struct i2c_device_id *id)
 {
-       if (!stereo_sh532u_info || !client) {
-               pr_info("right_sh532u_remove(): NULL pointers\n");
-               return 0;
+       struct sh532u_info *info = NULL;
+       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__);
+               return -ENOMEM;
        }
 
-       kfree(stereo_sh532u_info->right);
-       stereo_sh532u_info->right = NULL;
+       info->i2c_client = client;
+       if (client->dev.platform_data) {
+               info->pdata = client->dev.platform_data;
+       } else {
+               info->pdata = &sh532u_default_pdata;
+               dev_dbg(&client->dev,
+                               "%s No platform data.  Using defaults.\n",
+                               __func__);
+       }
+       i2c_set_clientdata(client, info);
+       INIT_LIST_HEAD(&info->list);
+       spin_lock(&sh532u_spinlock);
+       list_add_rcu(&info->list, &sh532u_info_list);
+       spin_unlock(&sh532u_spinlock);
+       sh532u_pm_init(info);
+       sh532u_pm_dev_wr(info, NVC_PWR_COMM);
+       err = sh532u_dev_id(info);
+       if (err < 0) {
+               dev_err(&client->dev, "%s device not found\n", __func__);
+               sh532u_pm_wr(info, NVC_PWR_OFF);
+               if (info->pdata->cfg & NVC_CFG_NODEV) {
+                       sh532u_del(info);
+                       return -ENODEV;
+               }
+       } else {
+               dev_dbg(&client->dev, "%s device found\n", __func__);
+               sh532u_calibration(info);
+               if (info->pdata->cfg & NVC_CFG_BOOT_INIT) {
+                       /* initial move causes full initialization */
+                       sh532u_pos_rel_wr(info, info->cap.focus_infinity);
+               } else {
+                       sh532u_pm_wr(info, NVC_PWR_OFF);
+               }
+       }
 
-       if (!stereo_sh532u_info->left) {
-               misc_deregister(&sh532u_device);
-               kfree(stereo_sh532u_info);
-               stereo_sh532u_info = NULL;
+       if (info->pdata->dev_name != 0)
+               strcpy(dname, info->pdata->dev_name);
+       else
+               strcpy(dname, "sh532u");
+       if (info->pdata->num)
+               snprintf(dname, sizeof(dname), "%s.%u",
+                        dname, info->pdata->num);
+       info->miscdev.name = dname;
+       info->miscdev.fops = &sh532u_fileops;
+       info->miscdev.minor = MISC_DYNAMIC_MINOR;
+       if (misc_register(&info->miscdev)) {
+               dev_err(&client->dev, "%s unable to register misc device %s\n",
+                               __func__, dname);
+               sh532u_del(info);
+               return -ENODEV;
        }
 
        return 0;
 }
 
-static const struct i2c_device_id right_sh532u_id[] = {
-       { "sh532uR", 0 },
+static const struct i2c_device_id sh532u_id[] = {
+       { "sh532u", 0 },
        { },
 };
 
-MODULE_DEVICE_TABLE(i2c, right_sh532u_id);
+MODULE_DEVICE_TABLE(i2c, sh532u_id);
 
-static struct i2c_driver right_sh532u_i2c_driver = {
+static struct i2c_driver sh532u_i2c_driver = {
        .driver = {
-               .name = "sh532uR",
+               .name = "sh532u",
                .owner = THIS_MODULE,
        },
-       .probe = right_sh532u_probe,
-       .remove = right_sh532u_remove,
-       .id_table = right_sh532u_id,
+       .id_table = sh532u_id,
+       .probe = sh532u_probe,
+       .remove = sh532u_remove,
 };
 
 static int __init sh532u_init(void)
 {
-       int ret;
-       pr_info("sh532u focuser driver loading\n");
-       ret = i2c_add_driver(&left_sh532u_i2c_driver);
-       if (ret)
-               return ret;
-
-       ret = i2c_add_driver(&right_sh532u_i2c_driver);
-
-       return ret;
+       return i2c_add_driver(&sh532u_i2c_driver);
 }
 
 static void __exit sh532u_exit(void)
 {
-       i2c_del_driver(&left_sh532u_i2c_driver);
-       i2c_del_driver(&right_sh532u_i2c_driver);
+       i2c_del_driver(&sh532u_i2c_driver);
 }
 
 module_init(sh532u_init);
diff --git a/include/media/nvc_focus.h b/include/media/nvc_focus.h
new file mode 100644 (file)
index 0000000..fd83258
--- /dev/null
@@ -0,0 +1,48 @@
+/* Copyright (C) 2011 NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#ifndef __NVC_FOCUS_H__
+#define __NVC_FOCUS_H__
+
+enum nvc_focus_sts {
+       NVC_FOCUS_STS_UNKNOWN           = 1,
+       NVC_FOCUS_STS_NO_DEVICE,
+       NVC_FOCUS_STS_INITIALIZING,
+       NVC_FOCUS_STS_INIT_ERR,
+       NVC_FOCUS_STS_WAIT_FOR_MOVE_END,
+       NVC_FOCUS_STS_WAIT_FOR_SETTLE,
+       NVC_FOCUS_STS_LENS_SETTLED,
+       NVC_FOCUS_STS_FORCE32           = 0x7FFFFFFF
+};
+
+struct nvc_focus_nvc {
+       __u32 focal_length;
+       __u32 fnumber;
+       __u32 max_aperature;
+} __packed;
+
+struct nvc_focus_cap {
+       __u32 version;
+       __u32 actuator_range;
+       __u32 settle_time;
+       __u32 focus_macro;
+       __u32 focus_hyper;
+       __u32 focus_infinity;
+} __packed;
+
+#endif /* __NVC_FOCUS_H__ */
+
index 166caad65f88a2a5fe8b2a7bea32f546b70b19e1..19da2070b70f71a2bbf573690e2fa06f2e6a1b79 100644 (file)
 #ifndef __SH532U_H__
 #define __SH532U_H__
 
-#include <linux/ioctl.h> /* For IOCTL macros */
+#include <media/nvc_focus.h>
 
-#define SH532U_IOCTL_GET_CONFIG                _IOR('o', 1, struct sh532u_config)
-#define SH532U_IOCTL_SET_POSITION      _IOW('o', 2, u32)
-#define SH532U_IOCTL_GET_MOVE_STATUS   _IOW('o', 3, unsigned char)
-#define SH532U_IOCTL_SET_CAMERA_MODE   _IOW('o', 4, unsigned char)
 
-enum sh532u_move_status {
-       SH532U_STATE_UNKNOWN = 1,
-       SH532U_WAIT_FOR_MOVE_END,
-       SH532U_WAIT_FOR_SETTLE,
-       SH532U_LENS_SETTLED,
-       SH532U_Forced32 = 0x7FFFFFFF
+struct sh532u_platform_data {
+       int cfg;
+       int num;
+       int sync;
+       const char *dev_name;
+       struct nvc_focus_nvc (*nvc);
+       struct nvc_focus_cap (*cap);
+       struct sh532u_pdata_info (*info);
+       __u8 i2c_addr_rom;
+       unsigned gpio_reset;
+/* Due to a Linux limitation, a GPIO is defined to "enable" the device.  This
+ * workaround is for when the device's power GPIO's are behind an I2C expander.
+ * The Linux limitation doesn't allow the I2C GPIO expander to be ready for
+ * use when this device is probed.
+ */
+       unsigned gpio_en;
 };
 
-struct sh532u_config {
-       __u32 settle_time;
-       __u32 focal_length;
-       __u32 fnumber;
-       s16 pos_low;
-       s16 pos_high;
-       s16 limit_low;
-       s16 limit_high;
+struct sh532u_pdata_info {
+       __s16 pos_low;
+       __s16 pos_high;
+       __s16 limit_low;
+       __s16 limit_high;
+       int move_timeoutms;
+       __u32 focus_hyper_ratio;
+       __u32 focus_hyper_div;
 };
 
-struct sh532u_platform_data {
-       void *context_data;
-       int (*board_init)(void *context_data);
-       int (*board_deinit)(void *context_data);
-};
+
 /* Register Definition  : Sany Driver IC */
 /* EEPROM addresses */
 #define addrHallOffset         0x10
@@ -104,8 +106,8 @@ struct sh532u_platform_data {
 #define ADHXI_211L             0x01
 #define PIDZO_211H             0x02
 #define PIDZO_211L             0x03
-#define RZ_211H                0x04
-#define RZ_211L                0x05
+#define RZ_211H                        0x04
+#define RZ_211L                        0x05
 #define DZ1_211H               0x06
 #define DZ1_211L               0x07
 #define DZ2_211H               0x08
@@ -148,8 +150,8 @@ struct sh532u_platform_data {
 #define OZ4_211L               0x2D
 #define OZ5_211H               0x2E
 #define OZ5_211L               0x2F
-#define oe_211H                0x30
-#define oe_211L                0x31
+#define oe_211H                        0x30
+#define oe_211L                        0x31
 #define MSR1CMAX_211H          0x32
 #define MSR1CMAX_211L          0x33
 #define MSR1CMIN_211H          0x34
@@ -162,34 +164,34 @@ struct sh532u_platform_data {
 #define OFFSET_211L            0x3B
 #define ADOFFSET_211H          0x3C
 #define ADOFFSET_211L          0x3D
-#define EZ_211H                0x3E
-#define EZ_211L                0x3F
+#define EZ_211H                        0x3E
+#define EZ_211L                        0x3F
 
 /* Coefficient RAM 40h ~ 7Fh */
-#define ag_211H                0x40
-#define ag_211L                0x41
-#define da_211H                0x42
-#define da_211L                0x43
-#define db_211H                0x44
-#define db_211L                0x45
-#define dc_211H                0x46
-#define dc_211L                0x47
-#define dg_211H                0x48
-#define dg_211L                0x49
-#define pg_211H                0x4A
-#define pg_211L                0x4B
+#define ag_211H                        0x40
+#define ag_211L                        0x41
+#define da_211H                        0x42
+#define da_211L                        0x43
+#define db_211H                        0x44
+#define db_211L                        0x45
+#define dc_211H                        0x46
+#define dc_211L                        0x47
+#define dg_211H                        0x48
+#define dg_211L                        0x49
+#define pg_211H                        0x4A
+#define pg_211L                        0x4B
 #define gain1_211H             0x4C
 #define gain1_211L             0x4D
 #define gain2_211H             0x4E
 #define gain2_211L             0x4F
-#define ua_211H                0x50
-#define ua_211L                0x51
-#define uc_211H                0x52
-#define uc_211L                0x53
-#define ia_211H                0x54
-#define ia_211L                0x55
-#define ib_211H                0x56
-#define ib_211L                0x57
+#define ua_211H                        0x50
+#define ua_211L                        0x51
+#define uc_211H                        0x52
+#define uc_211L                        0x53
+#define ia_211H                        0x54
+#define ia_211L                        0x55
+#define ib_211H                        0x56
+#define ib_211L                        0x57
 #define i_c_211H               0x58
 #define i_c_211L               0x59
 #define ms11a_211H             0x5A
@@ -216,10 +218,10 @@ struct sh532u_platform_data {
 #define ms22e_211L             0x6F
 #define ms23p_211H             0x70
 #define ms23p_211L             0x71
-#define oa_211H                0x72
-#define oa_211L                0x73
-#define oc_211H                0x74
-#define oc_211L                0x75
+#define oa_211H                        0x72
+#define oa_211L                        0x73
+#define oc_211H                        0x74
+#define oc_211L                        0x75
 #define PX12_211H              0x76
 #define PX12_211L              0x77
 #define PX3_211H               0x78
@@ -237,7 +239,7 @@ struct sh532u_platform_data {
 #define PWMSEL_211             0x82
 #define SWTCH_211              0x83
 #define STBY_211               0x84
-#define CLR_211                0x85
+#define CLR_211                        0x85
 #define DSSEL_211              0x86
 #define ENBL_211               0x87
 #define ANA1_211               0x88
@@ -296,10 +298,10 @@ P0  P1
 
 /* E2P data type define of HVCA Initial Value Section */
 #define DIRECT_MODE            0x00
-#define INDIRECT_EEPROM        0x10
+#define INDIRECT_EEPROM                0x10
 #define INDIRECT_HVCA          0x20
 #define MASK_AND               0x70
-#define MASK_OR                0x80
+#define MASK_OR                        0x80
 
 #define DATA_1BYTE             0x01
 #define DATA_2BYTE             0x02