/* * AD5820 focuser driver. * * Copyright (C) 2010-2011 NVIDIA Corporation. * * Contributors: * Sachin Nikam * * 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 #include #include #include #include #include #include #include /* 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);