blob: ba3fa986cf7fa39f687bbfb34c56a222072f4c28 [file] [log] [blame]
/*
* 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 <linux/module.h>
#include <media/ad5820.h>
/* Focuser single step & full scale transition time truth table
* in the format of:
* index mode single step transition full scale transition
* 0 0 0 0
* 1 1 50uS 51.2mS
* 2 1 100uS 102.3mS
* 3 1 200uS 204.6mS
* 4 1 400uS 409.2mS
* 5 1 800uS 818.4mS
* 6 1 1600uS 1636.8mS
* 7 1 3200uS 3273.6mS
* 8 0 0 0
* 9 2 50uS 1.1mS
* A 2 100uS 2.2mS
* B 2 200uS 4.4mS
* C 2 400uS 8.8mS
* D 2 800uS 17.6mS
* E 2 1600uS 35.2mS
* F 2 3200uS 70.4mS
*/
/* pick up the mode index setting and its settle time from the above table */
#define AD5820_TRANSITION_MODE 0x0B
#define SETTLETIME_MS 5
#define POS_LOW (0)
#define POS_HIGH (1023)
#define FOCAL_LENGTH (4.507f)
#define FNUMBER (2.8f)
#define FPOS_COUNT 1024
struct ad5820_info {
struct i2c_client *i2c_client;
struct regulator *regulator;
struct ad5820_config config;
};
static int ad5820_write(struct i2c_client *client, u32 value)
{
int count;
struct i2c_msg msg[1];
unsigned char data[2];
if (!client->adapter)
return -ENODEV;
data[0] = (u8) ((value >> 4) & 0x3F);
data[1] = (u8) ((value & 0xF) << 4) | AD5820_TRANSITION_MODE;
msg[0].addr = client->addr;
msg[0].flags = 0;
msg[0].len = ARRAY_SIZE(data);
msg[0].buf = data;
count = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
if (count == ARRAY_SIZE(msg))
return 0;
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);
MODULE_LICENSE("GPL v2");