Revert "media: camera config changes"
[linux-2.6.git] / drivers / media / video / tegra / ad5820.c
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
56 struct ad5820_info {
57         struct i2c_client *i2c_client;
58         struct regulator *regulator;
59         struct ad5820_config config;
60 };
61
62 static 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
86 static 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
95 static 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
121 struct ad5820_info *info;
122
123 static 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
131 int 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
140 static 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
147 static struct miscdevice ad5820_device = {
148         .minor = MISC_DYNAMIC_MINOR,
149         .name = "ad5820",
150         .fops = &ad5820_fileops,
151 };
152
153 static 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
192 static 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
201 static const struct i2c_device_id ad5820_id[] = {
202         { "ad5820", 0 },
203         { },
204 };
205
206 MODULE_DEVICE_TABLE(i2c, ad5820_id);
207
208 static 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
218 static int __init ad5820_init(void)
219 {
220         pr_info("ad5820 sensor driver loading\n");
221         return i2c_add_driver(&ad5820_i2c_driver);
222 }
223
224 static void __exit ad5820_exit(void)
225 {
226         i2c_del_driver(&ad5820_i2c_driver);
227 }
228
229 module_init(ad5820_init);
230 module_exit(ad5820_exit);
231 MODULE_LICENSE("GPL v2");