summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFrank Chen <frankc@nvidia.com>2011-04-06 18:44:36 -0700
committerVarun Colbert <vcolbert@nvidia.com>2011-04-15 18:34:49 -0700
commitc20dd5116d966629a88536443121425b2655ef67 (patch)
tree2307bd90ebad75d24b25107253a8b5239b048902
parent063b3c474383cb38a515e4460196bd8ed3946306 (diff)
media: video: tegra: AD5820: Add AD5820 focuser driver.
Add AD5820 focuser driver for Whistler. Bug 783488 Change-Id: I0d8a08e7df9f472ffc4edfe7bfa255357bff5126 Reviewed-on: http://git-master/r/26970 Reviewed-by: Varun Colbert <vcolbert@nvidia.com> Tested-by: Varun Colbert <vcolbert@nvidia.com>
-rw-r--r--drivers/media/video/tegra/Kconfig6
-rw-r--r--drivers/media/video/tegra/Makefile1
-rw-r--r--drivers/media/video/tegra/ad5820.c215
-rw-r--r--include/media/ad5820.h41
4 files changed, 263 insertions, 0 deletions
diff --git a/drivers/media/video/tegra/Kconfig b/drivers/media/video/tegra/Kconfig
index 3d5467c26edf..fd1908ba781b 100644
--- a/drivers/media/video/tegra/Kconfig
+++ b/drivers/media/video/tegra/Kconfig
@@ -37,3 +37,9 @@ config VIDEO_SH532U
This is a driver for the SEMCO SH532U focuser
for use with the tegra isp.
+config VIDEO_AD5820
+ tristate "AD5820 focuser support"
+ depends on I2C && ARCH_TEGRA
+ ---help---
+ This is a driver for the AD5820 focuser
+ for use with the tegra isp.
diff --git a/drivers/media/video/tegra/Makefile b/drivers/media/video/tegra/Makefile
index 45f77e00b155..60d2b5041974 100644
--- a/drivers/media/video/tegra/Makefile
+++ b/drivers/media/video/tegra/Makefile
@@ -8,5 +8,6 @@ obj-$(CONFIG_VIDEO_OV5650) += ov5650.o
obj-$(CONFIG_VIDEO_OV2710) += ov2710.o
obj-$(CONFIG_TORCH_SSL3250A) += ssl3250a.o
obj-$(CONFIG_VIDEO_SH532U) += sh532u.o
+obj-$(CONFIG_VIDEO_AD5820) += ad5820.o
diff --git a/drivers/media/video/tegra/ad5820.c b/drivers/media/video/tegra/ad5820.c
new file mode 100644
index 000000000000..365c0869dc86
--- /dev/null
+++ b/drivers/media/video/tegra/ad5820.c
@@ -0,0 +1,215 @@
+/*
+ * AD5820 focuser driver.
+ *
+ * Copyright (C) 2010-2011 NVIDIA Corporation.
+ *
+ * Contributors:
+ * Sachin Nikam <snikam@nvidia.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 <linux/uaccess.h>
+#include <media/ad5820.h>
+
+#define POS_LOW (144)
+#define POS_HIGH (520)
+#define SETTLETIME_MS 100
+#define FOCAL_LENGTH (4.507f)
+#define FNUMBER (2.8f)
+#define FPOS_COUNT 1024
+
+#define AD5820_MAX_RETRIES (3)
+
+struct ad5820_info {
+ struct i2c_client *i2c_client;
+ struct regulator *regulator;
+ struct ad5820_config config;
+};
+
+static int ad5820_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[1] = (u8) ((value >> 4) & 0x3F);
+ data[0] = (u8) ((value & 0xF) << 4);
+
+ 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("ad5820: i2c transfer failed, retrying %x\n",
+ value);
+ msleep(3);
+ } while (retry <= AD5820_MAX_RETRIES);
+ return -EIO;
+}
+
+static int ad5820_set_position(struct ad5820_info *info, u32 position)
+{
+ if (position < info->config.pos_low ||
+ position > info->config.pos_high)
+ return -EINVAL;
+
+ return ad5820_write(info->i2c_client, position);
+}
+
+static long ad5820_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct ad5820_info *info = file->private_data;
+
+ switch (cmd) {
+ case AD5820_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 AD5820_IOCTL_SET_POSITION:
+ return ad5820_set_position(info, (u32) arg);
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+struct ad5820_info *info;
+
+static int ad5820_open(struct inode *inode, struct file *file)
+{
+ file->private_data = info;
+ if (info->regulator)
+ regulator_enable(info->regulator);
+ return 0;
+}
+
+int ad5820_release(struct inode *inode, struct file *file)
+{
+ if (info->regulator)
+ regulator_disable(info->regulator);
+ file->private_data = NULL;
+ return 0;
+}
+
+
+static const struct file_operations ad5820_fileops = {
+ .owner = THIS_MODULE,
+ .open = ad5820_open,
+ .unlocked_ioctl = ad5820_ioctl,
+ .release = ad5820_release,
+};
+
+static struct miscdevice ad5820_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "ad5820",
+ .fops = &ad5820_fileops,
+};
+
+static int ad5820_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int err;
+
+ pr_info("ad5820: probing sensor.\n");
+
+ info = kzalloc(sizeof(struct ad5820_info), GFP_KERNEL);
+ if (!info) {
+ pr_err("ad5820: Unable to allocate memory!\n");
+ return -ENOMEM;
+ }
+
+ err = misc_register(&ad5820_device);
+ if (err) {
+ pr_err("ad5820: Unable to register misc device!\n");
+ kfree(info);
+ return err;
+ }
+
+ info->regulator = regulator_get(&client->dev, "vdd_vcore_af");
+ 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;
+ 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;
+ i2c_set_clientdata(client, info);
+ return 0;
+}
+
+static int ad5820_remove(struct i2c_client *client)
+{
+ struct ad5820_info *info;
+ info = i2c_get_clientdata(client);
+ misc_deregister(&ad5820_device);
+ kfree(info);
+ return 0;
+}
+
+static const struct i2c_device_id ad5820_id[] = {
+ { "ad5820", 0 },
+ { },
+};
+
+MODULE_DEVICE_TABLE(i2c, ad5820_id);
+
+static struct i2c_driver ad5820_i2c_driver = {
+ .driver = {
+ .name = "ad5820",
+ .owner = THIS_MODULE,
+ },
+ .probe = ad5820_probe,
+ .remove = ad5820_remove,
+ .id_table = ad5820_id,
+};
+
+static int __init ad5820_init(void)
+{
+ pr_info("ad5820 sensor driver loading\n");
+ return i2c_add_driver(&ad5820_i2c_driver);
+}
+
+static void __exit ad5820_exit(void)
+{
+ i2c_del_driver(&ad5820_i2c_driver);
+}
+
+module_init(ad5820_init);
+module_exit(ad5820_exit);
+
diff --git a/include/media/ad5820.h b/include/media/ad5820.h
new file mode 100644
index 000000000000..c3e710113402
--- /dev/null
+++ b/include/media/ad5820.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2010-2011 NVIDIA Corporation.
+ *
+ * Contributors:
+ * Sachin Nikam <snikam@nvidia.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 __AD5820_H__
+#define __AD5820_H__
+
+#include <linux/ioctl.h> /* For IOCTL macros */
+
+#define AD5820_IOCTL_GET_CONFIG _IOR('o', 1, struct ad5820_config)
+#define AD5820_IOCTL_SET_POSITION _IOW('o', 2, u32)
+
+struct ad5820_config {
+ __u32 settle_time;
+ __u32 actuator_range;
+ __u32 pos_low;
+ __u32 pos_high;
+ float focal_length;
+ float fnumber;
+ float max_aperture;
+};
+
+#endif /* __AD5820_H__ */
+