/* * soc380.c - soc380 sensor driver * * Copyright (c) 2011, NVIDIA, All Rights Reserved. * * Contributors: * Abhinav Sinha * * Leverage OV2710.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. */ /** * SetMode Sequence for 640x480. Phase 0. Sensor Dependent. * This sequence should put sensor in streaming mode for 640x480 * This is usually given by the FAE or the sensor vendor. */ #include #include #include #include #include #include #include struct soc380_reg { u16 addr; u16 val; }; struct soc380_info { int mode; struct i2c_client *i2c_client; struct soc380_platform_data *pdata; }; #define SOC380_TABLE_WAIT_MS 0 #define SOC380_TABLE_END 1 #define SOC380_MAX_RETRIES 3 static struct soc380_reg mode_640x480[] = { {0x001A, 0x0011}, {SOC380_TABLE_WAIT_MS, 1}, {0x001A, 0x0010}, {SOC380_TABLE_WAIT_MS, 1}, {0x0018, 0x4028}, {0x001A, 0x0210}, {0x001E, 0x0777}, {0x0016, 0x42DF}, {0x0010, 0x0217}, {0x0012, 0x0000}, {0x0014, 0x2147}, {SOC380_TABLE_WAIT_MS, 50}, {0x0014, 0x2047}, {SOC380_TABLE_WAIT_MS, 10}, {0x0014, 0xA046}, {SOC380_TABLE_WAIT_MS, 10}, {0x3040, 0x0027}, {0x301A, 0x1218}, {SOC380_TABLE_WAIT_MS, 10}, {0x301A, 0x121C}, {0x098C, 0x2703}, {0x0990, 0x0280}, {0x098C, 0x2705}, {0x0990, 0x01E0}, {0x098C, 0x2707}, {0x0990, 0x0280}, {0x098C, 0x2709}, {0x0990, 0x01E0}, {0x098C, 0x270D}, {0x0990, 0x0004}, {0x098C, 0x270F}, {0x0990, 0x0004}, {0x098C, 0x2711}, {0x0990, 0x01EB}, {0x098C, 0x2713}, {0x0990, 0x028B}, {0x098C, 0x2715}, {0x0990, 0x0001}, {0x098C, 0x2717}, {0x0990, 0x0026}, {0x098C, 0x2719}, {0x0990, 0x001A}, {0x098C, 0x271B}, {0x0990, 0x006B}, {0x098C, 0x271D}, {0x0990, 0x006B}, {0x098C, 0x271F}, {0x0990, 0x046F}, {0x098C, 0x2721}, {0x0990, 0x034A}, {0x098C, 0x2723}, {0x0990, 0x0004}, {0x098C, 0x2725}, {0x0990, 0x0004}, {0x098C, 0x2727}, {0x0990, 0x01EB}, {0x098C, 0x2729}, {0x0990, 0x028B}, {0x098C, 0x272B}, {0x0990, 0x0001}, {0x098C, 0x272D}, {0x0990, 0x0026}, {0x098C, 0x272F}, {0x0990, 0x001A}, {0x098C, 0x2731}, {0x0990, 0x006B}, {0x098C, 0x2733}, {0x0990, 0x006B}, {0x098C, 0x2735}, {0x0990, 0x046F}, {0x098C, 0x2737}, {0x0990, 0x034A}, {0x098C, 0x2739}, {0x0990, 0x0000}, {0x098C, 0x273B}, {0x0990, 0x027F}, {0x098C, 0x273D}, {0x0990, 0x0000}, {0x098C, 0x273F}, {0x0990, 0x01DF}, {0x098C, 0x2747}, {0x0990, 0x0000}, {0x098C, 0x2749}, {0x0990, 0x027F}, {0x098C, 0x274B}, {0x0990, 0x0000}, {0x098C, 0x274D}, {0x0990, 0x01DF}, {0x098C, 0x222D}, {0x0990, 0x008B}, {0x098C, 0xA408}, {0x0990, 0x001F}, {0x098C, 0xA409}, {0x0990, 0x0022}, {0x098C, 0xA40A}, {0x0990, 0x0019}, {0x098C, 0xA40B}, {0x0990, 0x001C}, {0x098C, 0x2411}, {0x0990, 0x008B}, {0x098C, 0x2413}, {0x0990, 0x00A6}, {0x098C, 0x2415}, {0x0990, 0x008B}, {0x098C, 0x2417}, {0x0990, 0x00A6}, {0x098C, 0xA40D}, {0x0990, 0x0002}, {0x098C, 0xA410}, {0x0990, 0x0001}, {0x098C, 0xA103}, {0x0990, 0x0006}, {SOC380_TABLE_WAIT_MS, 50}, {0x098C, 0xA103}, {0x0990, 0x0005}, {0x3012, 0x0384}, {0x098C, 0x2115}, {0x0990, 0x0002}, {0x321C, 0x0003}, {0x3330, 0x0000}, {0x098C, 0x2103}, {0x0990, 0x0002}, {0x321C, 0x0003}, {0x3330, 0x0000}, {0x3330, 0x0000}, {0x321C, 0x0003}, {0x098C, 0xA103}, {0x0990, 0x0000}, {0x098C, 0xA104}, {0x0990, 0x0007}, {0x098C, 0xA115}, {0x0990, 0x0002}, {0x098C, 0xA103}, {0x0990, 0x0002}, {SOC380_TABLE_WAIT_MS, 500}, {SOC380_TABLE_END, 0x0000} }; enum { SOC380_MODE_680x480, }; static struct soc380_reg *mode_table[] = { [SOC380_MODE_680x480] = mode_640x480, }; static int soc380_read_reg(struct i2c_client *client, u16 addr, u16 *val) { int err; struct i2c_msg msg[2]; unsigned char data[4]; if (!client->adapter) return -ENODEV; msg[0].addr = client->addr; msg[0].flags = 0; msg[0].len = 2; msg[0].buf = data; /* high byte goes out first */ data[0] = (u8) (addr >> 8); data[1] = (u8) (addr & 0xff); msg[1].addr = client->addr; msg[1].flags = I2C_M_RD; msg[1].len = 2; msg[1].buf = data + 2; err = i2c_transfer(client->adapter, msg, 2); if (err != 2) return -EINVAL; *val = data[2] << 8 | data[3]; return 0; } static int soc380_write_reg(struct i2c_client *client, u16 addr, u16 val) { int err; struct i2c_msg msg; unsigned char data[4]; int retry = 0; if (!client->adapter) return -ENODEV; data[0] = (u8) (addr >> 8); data[1] = (u8) (addr & 0xff); data[2] = (u8) (val >> 8); data[3] = (u8) (val & 0xff); msg.addr = client->addr; msg.flags = 0; msg.len = 4; msg.buf = data; do { err = i2c_transfer(client->adapter, &msg, 1); if (err == 1) return 0; retry++; pr_err("soc380: i2c transfer failed, retrying %x %x\n", addr, val); msleep(3); } while (retry <= SOC380_MAX_RETRIES); return err; } static int soc380_write_table(struct i2c_client *client, const struct soc380_reg table[], const struct soc380_reg override_list[], int num_override_regs) { int err; const struct soc380_reg *next; int i; u16 val; for (next = table; next->addr != SOC380_TABLE_END; next++) { if (next->addr == SOC380_TABLE_WAIT_MS) { msleep(next->val); continue; } val = next->val; /* When an override list is passed in, replace the reg */ /* value to write if the reg is in the list */ if (override_list) { for (i = 0; i < num_override_regs; i++) { if (next->addr == override_list[i].addr) { val = override_list[i].val; break; } } } err = soc380_write_reg(client, next->addr, val); if (err) return err; } return 0; } static int soc380_set_mode(struct soc380_info *info, struct soc380_mode *mode) { int sensor_mode; int err; pr_info("%s: xres %u yres %u\n", __func__, mode->xres, mode->yres); if (mode->xres == 640 && mode->yres == 480) sensor_mode = SOC380_MODE_680x480; else { pr_err("%s: invalid resolution supplied to set mode %d %d\n", __func__, mode->xres, mode->yres); return -EINVAL; } err = soc380_write_table(info->i2c_client, mode_table[sensor_mode], NULL, 0); if (err) return err; info->mode = sensor_mode; return 0; } static int soc380_get_status(struct soc380_info *info, struct soc380_status *dev_status) { int err; err = soc380_write_reg(info->i2c_client, 0x98C, dev_status->data); if (err) return err; err = soc380_read_reg(info->i2c_client, 0x0990, (u16 *) &dev_status->status); if (err) return err; return err; } static long soc380_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { int err; struct soc380_info *info = file->private_data; switch (cmd) { case SOC380_IOCTL_SET_MODE: { struct soc380_mode mode; if (copy_from_user(&mode, (const void __user *)arg, sizeof(struct soc380_mode))) { return -EFAULT; } return soc380_set_mode(info, &mode); } case SOC380_IOCTL_GET_STATUS: { struct soc380_status dev_status; if (copy_from_user(&dev_status, (const void __user *)arg, sizeof(struct soc380_status))) { return -EFAULT; } err = soc380_get_status(info, &dev_status); if (err) return err; if (copy_to_user((void __user *)arg, &dev_status, sizeof(struct soc380_status))) { return -EFAULT; } return 0; } default: return -EINVAL; } return 0; } static struct soc380_info *info; static int soc380_open(struct inode *inode, struct file *file) { struct soc380_status dev_status; int err; file->private_data = info; if (info->pdata && info->pdata->power_on) info->pdata->power_on(); dev_status.data = 0; dev_status.status = 0; err = soc380_get_status(info, &dev_status); return err; } int soc380_release(struct inode *inode, struct file *file) { if (info->pdata && info->pdata->power_off) info->pdata->power_off(); file->private_data = NULL; return 0; } static const struct file_operations soc380_fileops = { .owner = THIS_MODULE, .open = soc380_open, .unlocked_ioctl = soc380_ioctl, .release = soc380_release, }; static struct miscdevice soc380_device = { .minor = MISC_DYNAMIC_MINOR, .name = "soc380", .fops = &soc380_fileops, }; static int soc380_probe(struct i2c_client *client, const struct i2c_device_id *id) { int err; pr_info("soc380: probing sensor.\n"); info = kzalloc(sizeof(struct soc380_info), GFP_KERNEL); if (!info) { pr_err("soc380: Unable to allocate memory!\n"); return -ENOMEM; } err = misc_register(&soc380_device); if (err) { pr_err("soc380: Unable to register misc device!\n"); kfree(info); return err; } info->pdata = client->dev.platform_data; info->i2c_client = client; i2c_set_clientdata(client, info); return 0; } static int soc380_remove(struct i2c_client *client) { struct soc380_info *info; info = i2c_get_clientdata(client); misc_deregister(&soc380_device); kfree(info); return 0; } static const struct i2c_device_id soc380_id[] = { { "soc380", 0 }, { }, }; MODULE_DEVICE_TABLE(i2c, soc380_id); static struct i2c_driver soc380_i2c_driver = { .driver = { .name = "soc380", .owner = THIS_MODULE, }, .probe = soc380_probe, .remove = soc380_remove, .id_table = soc380_id, }; static int __init soc380_init(void) { pr_info("soc380 sensor driver loading\n"); return i2c_add_driver(&soc380_i2c_driver); } static void __exit soc380_exit(void) { i2c_del_driver(&soc380_i2c_driver); } module_init(soc380_init); module_exit(soc380_exit);