summaryrefslogtreecommitdiff
path: root/drivers/media/video/tegra/ar0832_focuser.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/video/tegra/ar0832_focuser.c')
-rw-r--r--drivers/media/video/tegra/ar0832_focuser.c230
1 files changed, 230 insertions, 0 deletions
diff --git a/drivers/media/video/tegra/ar0832_focuser.c b/drivers/media/video/tegra/ar0832_focuser.c
new file mode 100644
index 000000000000..26c03c981e3f
--- /dev/null
+++ b/drivers/media/video/tegra/ar0832_focuser.c
@@ -0,0 +1,230 @@
+/*
+* ar0832_focuser.c - focuser driver
+*
+* Copyright (c) 2011, NVIDIA, All Rights Reserved.
+*
+* This file is licensed under the terms of the GNU General Public License
+* version 2. This program is licensed "as is" without any warranty of any
+* kind, whether express or implied.
+*/
+
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/i2c.h>
+#include <linux/miscdevice.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <media/ar0832_focuser.h>
+
+
+#define POS_LOW 50
+#define POS_HIGH 1000
+#define SETTLETIME_MS 100
+#define FOCAL_LENGTH (3.5f)
+#define FNUMBER (2.8f)
+#define FPOS_COUNT 1024
+DEFINE_MUTEX(star_focuser_lock);
+#define DW9716_MAX_RETRIES (3)
+
+static int ar0832_focuser_write(struct i2c_client *client, u16 value)
+{
+ int count;
+ struct i2c_msg msg[1];
+ unsigned char data[2];
+ int retry = 0;
+
+ if (!client->adapter)
+ return -ENODEV;
+
+ data[0] = (u8) ((value >> 4) & 0x3F);
+ data[1] = (u8) ((value & 0xF) << 4);
+ /* Slew rate control (8 steps, 50us) */
+ data[1] = (data[1] & 0xF0) | 0x05;
+
+ msg[0].addr = client->addr;
+ msg[0].flags = 0;
+ msg[0].len = ARRAY_SIZE(data);
+ msg[0].buf = data;
+
+ do {
+ count = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
+ if (count == ARRAY_SIZE(msg))
+ return 0;
+ retry++;
+ pr_err("ar0832_focuser: i2c transfer failed, retrying %x\n",
+ value);
+ usleep_range(3000, 3500);
+ } while (retry <= DW9716_MAX_RETRIES);
+ return -EIO;
+}
+
+static int ar0832_focuser_write_helper(
+ struct ar0832_focuser_info *info, u16 value)
+{
+ int ret;
+ switch (info->camera_mode) {
+ case MAIN:
+ case LEFT_ONLY:
+ ret = ar0832_focuser_write(info->i2c_client, value);
+ break;
+ case STEREO:
+ ret = ar0832_focuser_write(info->i2c_client, value);
+ ret = ar0832_focuser_write(info->i2c_client_right, value);
+ break;
+ case RIGHT_ONLY:
+ ret = ar0832_focuser_write(info->i2c_client_right, value);
+ break;
+ default:
+ return -1;
+ }
+ return ret;
+}
+static int ar0832_focuser_set_position(
+ struct ar0832_focuser_info *info, u32 position)
+{
+ if (position < info->config.pos_low ||
+ position > info->config.pos_high)
+ return -EINVAL;
+
+ return ar0832_focuser_write(info->i2c_client, position);
+}
+
+static long ar0832_focuser_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct ar0832_focuser_info *info = file->private_data;
+ int ret;
+ switch (cmd) {
+ case AR0832_FOCUSER_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 AR0832_FOCUSER_IOCTL_SET_POSITION:
+ mutex_lock(&star_focuser_lock);
+ ret = ar0832_focuser_set_position(info, (u32) arg);
+ mutex_unlock(&star_focuser_lock);
+ return ret;
+ case AR0832_FOCUSER_IOCTL_SET_MODE:
+ info->camera_mode = (enum StereoCameraMode)arg;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static struct ar0832_focuser_info *info;
+
+static int ar0832_focuser_open(struct inode *inode, struct file *file)
+{
+ pr_info("ar0832_focuser: open!\n");
+ file->private_data = info;
+ return 0;
+}
+
+int ar0832_focuser_release(struct inode *inode, struct file *file)
+{
+ pr_info("ar0832_focuser: release!\n");
+ file->private_data = NULL;
+ return 0;
+}
+
+
+static const struct file_operations ar0832_focuser_fileops = {
+ .owner = THIS_MODULE,
+ .open = ar0832_focuser_open,
+ .unlocked_ioctl = ar0832_focuser_ioctl,
+ .release = ar0832_focuser_release,
+};
+
+static struct miscdevice ar0832_focuser_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "ar0832_focuser",
+ .fops = &ar0832_focuser_fileops,
+};
+
+static int ar0832_focuser_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int err;
+
+ pr_info("ar0832_focuser: probing sensor.\n");
+
+ info = kzalloc(sizeof(struct ar0832_focuser_info), GFP_KERNEL);
+ if (!info) {
+ pr_err("ar0832_focuser: Unable to allocate memory!\n");
+ return -ENOMEM;
+ }
+
+ err = misc_register(&ar0832_focuser_device);
+ if (err) {
+ pr_err("ar0832_focuser: Unable to register misc device!\n");
+ kfree(info);
+ return err;
+ }
+
+ info->regulator = 0;
+ info->i2c_client = client;
+ info->config.settle_time = SETTLETIME_MS;
+ /* FIX-ME */
+ /*
+ focuser_info->config.focal_length = FOCAL_LENGTH;
+ focuser_info->config.fnumber = FNUMBER;
+ */
+ info->config.pos_low = POS_LOW;
+ info->config.pos_high = POS_HIGH;
+ i2c_set_clientdata(client, info);
+
+ return 0;
+}
+
+static int ar0832_focuser_remove(struct i2c_client *client)
+{
+ struct ar0832_focuser_info *info;
+ info = i2c_get_clientdata(client);
+ misc_deregister(&ar0832_focuser_device);
+ kfree(info);
+ return 0;
+}
+
+static const struct i2c_device_id ar0832_focuser_id[] = {
+ { "ar0832_focuser", 0 },
+ { },
+};
+
+MODULE_DEVICE_TABLE(i2c, ar0832_focuser_id);
+
+static struct i2c_driver ar0832_focuser_i2c_driver = {
+ .driver = {
+ .name = "ar0832_focuser",
+ .owner = THIS_MODULE,
+ },
+ .probe = ar0832_focuser_probe,
+ .remove = ar0832_focuser_remove,
+ .id_table = ar0832_focuser_id,
+};
+
+static int __init ar0832_focuser_init(void)
+{
+ pr_info("ar0832_focuser sensor driver loading\n");
+ i2c_add_driver(&ar0832_focuser_i2c_driver);
+
+ return 0;
+}
+
+static void __exit ar0832_focuser_exit(void)
+{
+ i2c_del_driver(&ar0832_focuser_i2c_driver);
+}
+
+module_init(ar0832_focuser_init);
+module_exit(ar0832_focuser_exit);