aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/video/tegra/ad5820.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/video/tegra/ad5820.c')
-rw-r--r--drivers/media/video/tegra/ad5820.c231
1 files changed, 231 insertions, 0 deletions
diff --git a/drivers/media/video/tegra/ad5820.c b/drivers/media/video/tegra/ad5820.c
new file mode 100644
index 00000000000..19d35bca5b0
--- /dev/null
+++ b/drivers/media/video/tegra/ad5820.c
@@ -0,0 +1,231 @@
1/*
2 * AD5820 focuser driver.
3 *
4 * Copyright (C) 2010-2011 NVIDIA Corporation.
5 *
6 * Contributors:
7 * Sachin Nikam <snikam@nvidia.com>
8 *
9 * Based on ov5650.c.
10 *
11 * This file is licensed under the terms of the GNU General Public License
12 * version 2. This program is licensed "as is" without any warranty of any
13 * kind, whether express or implied.
14 */
15
16#include <linux/delay.h>
17#include <linux/fs.h>
18#include <linux/i2c.h>
19#include <linux/miscdevice.h>
20#include <linux/regulator/consumer.h>
21#include <linux/slab.h>
22#include <linux/uaccess.h>
23#include <media/ad5820.h>
24
25/* Focuser single step & full scale transition time truth table
26 * in the format of:
27 * index mode single step transition full scale transition
28 * 0 0 0 0
29 * 1 1 50uS 51.2mS
30 * 2 1 100uS 102.3mS
31 * 3 1 200uS 204.6mS
32 * 4 1 400uS 409.2mS
33 * 5 1 800uS 818.4mS
34 * 6 1 1600uS 1636.8mS
35 * 7 1 3200uS 3273.6mS
36 * 8 0 0 0
37 * 9 2 50uS 1.1mS
38 * A 2 100uS 2.2mS
39 * B 2 200uS 4.4mS
40 * C 2 400uS 8.8mS
41 * D 2 800uS 17.6mS
42 * E 2 1600uS 35.2mS
43 * F 2 3200uS 70.4mS
44 */
45
46/* pick up the mode index setting and its settle time from the above table */
47#define AD5820_TRANSITION_MODE 0x0B
48#define SETTLETIME_MS 5
49
50#define POS_LOW (0)
51#define POS_HIGH (1023)
52#define FOCAL_LENGTH (4.507f)
53#define FNUMBER (2.8f)
54#define FPOS_COUNT 1024
55
56struct ad5820_info {
57 struct i2c_client *i2c_client;
58 struct regulator *regulator;
59 struct ad5820_config config;
60};
61
62static int ad5820_write(struct i2c_client *client, u32 value)
63{
64 int count;
65 struct i2c_msg msg[1];
66 unsigned char data[2];
67
68 if (!client->adapter)
69 return -ENODEV;
70
71 data[0] = (u8) ((value >> 4) & 0x3F);
72 data[1] = (u8) ((value & 0xF) << 4) | AD5820_TRANSITION_MODE;
73
74 msg[0].addr = client->addr;
75 msg[0].flags = 0;
76 msg[0].len = ARRAY_SIZE(data);
77 msg[0].buf = data;
78
79 count = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
80 if (count == ARRAY_SIZE(msg))
81 return 0;
82
83 return -EIO;
84}
85
86static int ad5820_set_position(struct ad5820_info *info, u32 position)
87{
88 if (position < info->config.pos_low ||
89 position > info->config.pos_high)
90 return -EINVAL;
91
92 return ad5820_write(info->i2c_client, position);
93}
94
95static long ad5820_ioctl(struct file *file,
96 unsigned int cmd, unsigned long arg)
97{
98 struct ad5820_info *info = file->private_data;
99
100 switch (cmd) {
101 case AD5820_IOCTL_GET_CONFIG:
102 {
103 if (copy_to_user((void __user *) arg,
104 &info->config,
105 sizeof(info->config))) {
106 pr_err("%s: 0x%x\n", __func__, __LINE__);
107 return -EFAULT;
108 }
109
110 break;
111 }
112 case AD5820_IOCTL_SET_POSITION:
113 return ad5820_set_position(info, (u32) arg);
114 default:
115 return -EINVAL;
116 }
117
118 return 0;
119}
120
121struct ad5820_info *info;
122
123static int ad5820_open(struct inode *inode, struct file *file)
124{
125 file->private_data = info;
126 if (info->regulator)
127 regulator_enable(info->regulator);
128 return 0;
129}
130
131int ad5820_release(struct inode *inode, struct file *file)
132{
133 if (info->regulator)
134 regulator_disable(info->regulator);
135 file->private_data = NULL;
136 return 0;
137}
138
139
140static const struct file_operations ad5820_fileops = {
141 .owner = THIS_MODULE,
142 .open = ad5820_open,
143 .unlocked_ioctl = ad5820_ioctl,
144 .release = ad5820_release,
145};
146
147static struct miscdevice ad5820_device = {
148 .minor = MISC_DYNAMIC_MINOR,
149 .name = "ad5820",
150 .fops = &ad5820_fileops,
151};
152
153static int ad5820_probe(struct i2c_client *client,
154 const struct i2c_device_id *id)
155{
156 int err;
157
158 pr_info("ad5820: probing sensor.\n");
159
160 info = kzalloc(sizeof(struct ad5820_info), GFP_KERNEL);
161 if (!info) {
162 pr_err("ad5820: Unable to allocate memory!\n");
163 return -ENOMEM;
164 }
165
166 err = misc_register(&ad5820_device);
167 if (err) {
168 pr_err("ad5820: Unable to register misc device!\n");
169 kfree(info);
170 return err;
171 }
172
173 info->regulator = regulator_get(&client->dev, "vdd_vcore_af");
174 if (IS_ERR_OR_NULL(info->regulator)) {
175 dev_err(&client->dev, "unable to get regulator %s\n",
176 dev_name(&client->dev));
177 info->regulator = NULL;
178 } else {
179 regulator_enable(info->regulator);
180 }
181
182 info->i2c_client = client;
183 info->config.settle_time = SETTLETIME_MS;
184 info->config.focal_length = FOCAL_LENGTH;
185 info->config.fnumber = FNUMBER;
186 info->config.pos_low = POS_LOW;
187 info->config.pos_high = POS_HIGH;
188 i2c_set_clientdata(client, info);
189 return 0;
190}
191
192static int ad5820_remove(struct i2c_client *client)
193{
194 struct ad5820_info *info;
195 info = i2c_get_clientdata(client);
196 misc_deregister(&ad5820_device);
197 kfree(info);
198 return 0;
199}
200
201static const struct i2c_device_id ad5820_id[] = {
202 { "ad5820", 0 },
203 { },
204};
205
206MODULE_DEVICE_TABLE(i2c, ad5820_id);
207
208static struct i2c_driver ad5820_i2c_driver = {
209 .driver = {
210 .name = "ad5820",
211 .owner = THIS_MODULE,
212 },
213 .probe = ad5820_probe,
214 .remove = ad5820_remove,
215 .id_table = ad5820_id,
216};
217
218static int __init ad5820_init(void)
219{
220 pr_info("ad5820 sensor driver loading\n");
221 return i2c_add_driver(&ad5820_i2c_driver);
222}
223
224static void __exit ad5820_exit(void)
225{
226 i2c_del_driver(&ad5820_i2c_driver);
227}
228
229module_init(ad5820_init);
230module_exit(ad5820_exit);
231