media: video: tegra: Add dw9714l focuser driver
authorAndrei Warkentin <andreiw@motorola.com>
Fri, 3 Sep 2010 01:26:33 +0000 (18:26 -0700)
committerColin Cross <ccross@android.com>
Wed, 6 Oct 2010 23:50:47 +0000 (16:50 -0700)
Change-Id: I4e3c690c782ea25c50fec25bf6252acd32a64242
Signed-off-by: Rebecca Schultz Zavin <rebecca@android.com>
drivers/media/video/tegra/Kconfig
drivers/media/video/tegra/Makefile
drivers/media/video/tegra/dw9714l.c [new file with mode: 0644]
include/media/dw9714l.h [new file with mode: 0644]

index b2926322b7f85bed6dbc2e5fbaec813124536f03..6c3972c1365b6158e9aefbc94b6346faa3a09cc6 100644 (file)
@@ -12,3 +12,9 @@ config VIDEO_SOC2030
           This is a driver for the SOC2030 2MP camera sensor
          for use with the tegra isp.
 
+config VIDEO_DW9714L
+        tristate "DW9714L focuser support"
+        depends on I2C && ARCH_TEGRA
+        ---help---
+          This is a driver for the the dw9714l focuser.
+
index 7709f16f22696dfb796c2d004575db6ce7156d28..2c80cff939a15d8deffd99a2ba880fad1cf8003b 100644 (file)
@@ -4,3 +4,4 @@
 obj-y                          += tegra_camera.o
 obj-$(CONFIG_VIDEO_OV5650)     += ov5650.o
 obj-$(CONFIG_VIDEO_SOC2030)    += soc2030.o
+obj-$(CONFIG_VIDEO_DW9714L)    += dw9714l.o
\ No newline at end of file
diff --git a/drivers/media/video/tegra/dw9714l.c b/drivers/media/video/tegra/dw9714l.c
new file mode 100644 (file)
index 0000000..7ac4f3c
--- /dev/null
@@ -0,0 +1,213 @@
+/*
+ * DW9714L focuser driver.
+ *
+ * Copyright (C) 2010 Motorola Inc.
+ *
+ * Contributors:
+ *      Andrei Warkentin <andreiw@motorola.com>
+ *
+ * Based on ov5650.c.
+ *
+ * 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 <media/dw9714l.h>
+
+#define DW9714L_MAX_RETRIES (3)
+
+#define POS_PER_STEP (3)
+
+struct dw9714l_info {
+       struct i2c_client *i2c_client;
+       struct regulator *regulator;
+};
+
+static int dw9714l_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 >> 8);
+       data[1] = (u8) (value & 0xFF);
+
+       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("dw9714l: i2c transfer failed, retrying %x\n",
+                      value);
+               msleep(3);
+       } while (retry <= DW9714L_MAX_RETRIES);
+       return -EIO;
+}
+
+static int dw9714l_set_position(struct dw9714l_info *info, u32 position)
+{
+       int ret;
+
+       /* Protection off. */
+       ret = dw9714l_write(info->i2c_client, 0xECA3);
+       if (ret)
+               return ret;
+
+       ret = dw9714l_write(info->i2c_client, 0xF200 | (0x0F << 3));
+       if (ret)
+               return ret;
+
+       /* Protection on. */
+       ret = dw9714l_write(info->i2c_client, 0xDC51);
+       if (ret)
+               return ret;
+
+       ret = dw9714l_write(info->i2c_client,
+                           (position << 4) |
+                           (POS_PER_STEP << 2));
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int dw9714l_ioctl(struct inode *inode, struct file *file,
+                       unsigned int cmd, unsigned long arg)
+{
+       struct dw9714l_info *info = file->private_data;
+
+       switch (cmd) {
+       case DW9714L_IOCTL_SET_POSITION:
+               return dw9714l_set_position(info, (u32) arg);
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+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);
+       return 0;
+}
+
+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;
+       return 0;
+}
+
+
+static const struct file_operations dw9714l_fileops = {
+       .owner = THIS_MODULE,
+       .open = dw9714l_open,
+       .ioctl = dw9714l_ioctl,
+       .release = dw9714l_release,
+};
+
+static struct miscdevice dw9714l_device = {
+       .minor = MISC_DYNAMIC_MINOR,
+       .name = "dw9714l",
+       .fops = &dw9714l_fileops,
+};
+
+static int dw9714l_probe(struct i2c_client *client,
+                       const struct i2c_device_id *id)
+{
+       int err;
+
+       pr_info("dw9714l: probing sensor.\n");
+
+       info = kzalloc(sizeof(struct dw9714l_info), GFP_KERNEL);
+       if (!info) {
+               pr_err("dw9714l: Unable to allocate memory!\n");
+               return -ENOMEM;
+       }
+
+       err = misc_register(&dw9714l_device);
+       if (err) {
+               pr_err("dw9714l: Unable to register misc device!\n");
+               kfree(info);
+               return err;
+       }
+
+       info->regulator = regulator_get(&client->dev, "vcc");
+       if (IS_ERR_OR_NULL(info->regulator)) {
+               dev_err(&client->dev, "unable to get regulator %s\n",
+                       dev_name(&client->dev));
+               info->regulator = NULL;
+       } else {
+               regulator_enable(info->regulator);
+       }
+
+       info->i2c_client = client;
+       i2c_set_clientdata(client, info);
+       return 0;
+}
+
+static int dw9714l_remove(struct i2c_client *client)
+{
+       struct dw9714l_info *info;
+       info = i2c_get_clientdata(client);
+       misc_deregister(&dw9714l_device);
+       kfree(info);
+       return 0;
+}
+
+static const struct i2c_device_id dw9714l_id[] = {
+       { "dw9714l", 0 },
+       { },
+};
+
+MODULE_DEVICE_TABLE(i2c, dw9714l_id);
+
+static struct i2c_driver dw9714l_i2c_driver = {
+       .driver = {
+               .name = "dw9714l",
+               .owner = THIS_MODULE,
+       },
+       .probe = dw9714l_probe,
+       .remove = dw9714l_remove,
+       .id_table = dw9714l_id,
+};
+
+static int __init dw9714l_init(void)
+{
+       pr_info("dw9714l sensor driver loading\n");
+       return i2c_add_driver(&dw9714l_i2c_driver);
+}
+
+static void __exit dw9714l_exit(void)
+{
+       i2c_del_driver(&dw9714l_i2c_driver);
+}
+
+module_init(dw9714l_init);
+module_exit(dw9714l_exit);
+
diff --git a/include/media/dw9714l.h b/include/media/dw9714l.h
new file mode 100644 (file)
index 0000000..3d91140
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2010 Motorola, Inc.
+ *
+ * Contributors:
+ *      Andrei Warkentin <andreiw@motorola.com>
+ *
+ * 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 __DW9714L_H__
+#define __DW9714L_H__
+
+#include <linux/ioctl.h>  /* For IOCTL macros */
+
+#define DW9714L_IOCTL_SET_POSITION             _IOW('o', 1, u32)
+
+#endif  /* __DW9714L_H__ */
+