media: video: tegra: dw9714l: Make focuser driver more generic
authorAndrei Warkentin <andreiw@motorola.com>
Mon, 20 Sep 2010 20:32:56 +0000 (15:32 -0500)
committerColin Cross <ccross@android.com>
Thu, 7 Oct 2010 00:01:22 +0000 (17:01 -0700)
Exposes more parameters and settings to focuser HAL,
putting all specifics in the driver, queryable from the HAL.

Change-Id: Id3d8764b7f9f1853982ca9c0d403fb0e455c7185
Signed-off-by: Andrei Warkentin <andreiw@motorola.com>
drivers/media/video/tegra/dw9714l.c
include/media/dw9714l.h

index 7ac4f3c13fa8035a79d15bb3600d5d149b01c973..26c36c3a0803385e10f8ce0483f4f05c707a3c1a 100644 (file)
 #include <linux/miscdevice.h>
 #include <linux/regulator/consumer.h>
 #include <linux/slab.h>
+#include <linux/uaccess.h>
 #include <media/dw9714l.h>
 
-#define DW9714L_MAX_RETRIES (3)
+#define POS_LOW (144)
+#define POS_HIGH (520)
+#define SETTLETIME_MS (50)
+#define FOCAL_LENGTH (4.42f)
+#define FNUMBER (2.8f)
+#define DEFAULT_MODE (MODE_LSC);
+
+#define PROT_OFF (0xECA3)
+#define PROT_ON (0xDC51)
+#define DLC_MCLK (0x2)
+#define DLC_TSRC (0x17)
+#define LSC_MCLK (0x1)
+#define LSC_S10 (0x3)
+#define LSC_S32 (0x3)
+#define LSC_TSRC (0x3)
+#define LSC_GOAL(pos) ((pos << 4) | (LSC_S32 << 2) | LSC_S10)
+#define GOAL(pos) (pos << 4)
 
-#define POS_PER_STEP (3)
+#define DW9714L_MAX_RETRIES (3)
 
 struct dw9714l_info {
        struct i2c_client *i2c_client;
        struct regulator *regulator;
+       struct dw9714l_config config;
+};
+
+static u16 dlc_pre_set_pos[] =
+{
+       PROT_OFF,
+       0xA10C | DLC_MCLK,
+       0xF200 | (DLC_TSRC << 3),
+       PROT_ON
+};
+
+static u16 lsc_pre_set_pos[] =
+{
+       PROT_OFF,
+       0xA104 | LSC_MCLK,
+       0xF200 | (LSC_TSRC << 3),
+       PROT_ON
 };
 
 static int dw9714l_write(struct i2c_client *client, u16 value)
@@ -60,31 +94,66 @@ static int dw9714l_write(struct i2c_client *client, u16 value)
        return -EIO;
 }
 
-static int dw9714l_set_position(struct dw9714l_info *info, u32 position)
+static int dw9714l_write_many(struct i2c_client *client,
+                             u16 *values,
+                             size_t count)
 {
-       int ret;
+       int ix = 0;
+       int ret = 0;
+       while (ix < count && ret == 0)
+               ret = dw9714l_write(client, values[ix++]);
 
-       /* Protection off. */
-       ret = dw9714l_write(info->i2c_client, 0xECA3);
-       if (ret)
-               return ret;
+       return ret;
+}
 
-       ret = dw9714l_write(info->i2c_client, 0xF200 | (0x0F << 3));
-       if (ret)
-               return ret;
+static int dw9714l_set_position(struct dw9714l_info *info, u32 position)
+{
+       int ret;
 
-       /* Protection on. */
-       ret = dw9714l_write(info->i2c_client, 0xDC51);
-       if (ret)
-               return ret;
+       if (position < info->config.pos_low ||
+           position > info->config.pos_high)
+               return -EINVAL;
 
-       ret = dw9714l_write(info->i2c_client,
-                           (position << 4) |
-                           (POS_PER_STEP << 2));
-       if (ret)
-               return ret;
+       /*
+         As we calibrate the focuser, we might go back and forth on
+         the actual mode of setting the position. To
+         make this least painful, we'll make the mode a settable
+         parameter exposed to the focuser HAL.
+        */
+
+       switch(info->config.mode)
+       {
+       case MODE_LSC:
+               ret = dw9714l_write_many(info->i2c_client,
+                                        lsc_pre_set_pos,
+                                        ARRAY_SIZE(lsc_pre_set_pos));
+
+               if (ret)
+                       return ret;
+
+               ret = dw9714l_write(info->i2c_client,
+                                   LSC_GOAL(position));
+
+               break;
+       case MODE_DLC:
+               ret = dw9714l_write_many(info->i2c_client,
+                                        dlc_pre_set_pos,
+                                        ARRAY_SIZE(dlc_pre_set_pos));
+
+               if (ret)
+                       return ret;
+
+               /* Fall through */
+       case MODE_DIRECT:
+               ret = dw9714l_write(info->i2c_client,
+                                   GOAL(position));
+               break;
+       case MODE_INVALID:
+       default:
+               WARN_ON(info->config.mode);
+       }
 
-       return 0;
+       return ret;
 }
 
 static int dw9714l_ioctl(struct inode *inode, struct file *file,
@@ -93,11 +162,39 @@ static int dw9714l_ioctl(struct inode *inode, struct file *file,
        struct dw9714l_info *info = file->private_data;
 
        switch (cmd) {
+       case DW9714L_IOCTL_GET_CONFIG:
+       {
+               if (copy_to_user((void __user *) arg,
+                                &info->config,
+                                sizeof(info->config))) {
+                       pr_err("%s: 0x%x\n", __func__, __LINE__);
+                       return -EFAULT;
+               }
+
+               break;
+       }
+       case DW9714L_IOCTL_SET_CAL:
+       {
+               struct dw9714l_cal cal;
+               if (copy_from_user(&cal,
+                                  (const void __user *) arg,
+                                  sizeof(cal))) {
+                       pr_err("%s: 0x%x\n", __func__, __LINE__);
+                       return -EFAULT;
+               }
+
+               if (cal.mode >= MODE_INVALID)
+                       return -EINVAL;
+
+               info->config.mode = cal.mode;
+               break;
+       }
        case DW9714L_IOCTL_SET_POSITION:
                return dw9714l_set_position(info, (u32) arg);
        default:
                return -EINVAL;
        }
+
        return 0;
 }
 
@@ -105,9 +202,7 @@ struct dw9714l_info *info = NULL;
 
 static int dw9714l_open(struct inode *inode, struct file *file)
 {
-       u8 status;
 
-       pr_info("%s\n", __func__);
        file->private_data = info;
        if (info->regulator)
                regulator_enable(info->regulator);
@@ -116,7 +211,6 @@ static int dw9714l_open(struct inode *inode, struct file *file)
 
 int dw9714l_release(struct inode *inode, struct file *file)
 {
-       pr_info("%s\n", __func__);
        if (info->regulator)
                regulator_disable(info->regulator);
        file->private_data = NULL;
@@ -167,6 +261,12 @@ static int dw9714l_probe(struct i2c_client *client,
        }
 
        info->i2c_client = client;
+       info->config.settle_time = SETTLETIME_MS;
+       info->config.focal_length = FOCAL_LENGTH;
+       info->config.fnumber = FNUMBER;
+       info->config.pos_low = POS_LOW;
+       info->config.pos_high = POS_HIGH;
+       info->config.mode = DEFAULT_MODE;
        i2c_set_clientdata(client, info);
        return 0;
 }
index 3d91140dd37f088a15ed6cbffbf69f0317e1dec2..cac2abb9537b28223cca2becdd9cddf500cffe8f 100644 (file)
 
 #include <linux/ioctl.h>  /* For IOCTL macros */
 
-#define DW9714L_IOCTL_SET_POSITION             _IOW('o', 1, u32)
+#define DW9714L_IOCTL_GET_CONFIG   _IOR('o', 1, struct dw9714l_config)
+#define DW9714L_IOCTL_SET_CAL      _IOW('o', 2, struct dw9714l_cal)
+#define DW9714L_IOCTL_SET_POSITION _IOW('o', 3, u32)
+
+enum dw9714l_mode {
+       MODE_DIRECT,
+       MODE_LSC,
+       MODE_DLC,
+       MODE_INVALID
+};
+
+struct dw9714l_config
+{
+       __u32 settle_time;
+       float focal_length;
+       float fnumber;
+       __u32 pos_low;
+       __u32 pos_high;
+       enum dw9714l_mode mode;
+};
+
+struct dw9714l_cal
+{
+       enum dw9714l_mode mode;
+};
 
 #endif  /* __DW9714L_H__ */