diff options
author | Erik Lilliebjerg <elilliebjerg@nvidia.com> | 2011-03-02 03:26:15 -0700 |
---|---|---|
committer | Varun Colbert <vcolbert@nvidia.com> | 2011-03-04 18:20:22 -0800 |
commit | 670f92a641139a15a2f4a3cccf0c816d3892f1f3 (patch) | |
tree | de9dc2092f33c62fb56977a16b19be7c51727db1 /drivers | |
parent | 736b573fa3c130ca61e1780b05e179be5a25baea (diff) |
Kernel driver for SSL3250A flash/torch camera device.
Change-Id: Id5cb40c9823c7fcfd26e45e4608c45b16a2d431a
Reviewed-on: http://git-master/r/21351
Reviewed-by: Erik M Lilliebjerg <elilliebjerg@nvidia.com>
Tested-by: Erik M Lilliebjerg <elilliebjerg@nvidia.com>
Reviewed-by: George Bauernschmidt <georgeb@nvidia.com>
Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/media/video/tegra/Kconfig | 7 | ||||
-rw-r--r-- | drivers/media/video/tegra/Makefile | 1 | ||||
-rw-r--r-- | drivers/media/video/tegra/ssl3250a.c | 308 |
3 files changed, 316 insertions, 0 deletions
diff --git a/drivers/media/video/tegra/Kconfig b/drivers/media/video/tegra/Kconfig index edf6b5b3c1d3..d6e5368f5652 100644 --- a/drivers/media/video/tegra/Kconfig +++ b/drivers/media/video/tegra/Kconfig @@ -23,3 +23,10 @@ config VIDEO_OV2710 This is a driver for the Omnivision OV2710 camera sensor for use with the tegra isp. +config TORCH_SSL3250A + tristate "SSL3250A flash/torch support" + depends on I2C && ARCH_TEGRA + ---help--- + This is a driver for the SSL3250A flash/torch camera device + for use with the tegra isp. + diff --git a/drivers/media/video/tegra/Makefile b/drivers/media/video/tegra/Makefile index ca43f67445f1..1aacd5ef07d2 100644 --- a/drivers/media/video/tegra/Makefile +++ b/drivers/media/video/tegra/Makefile @@ -5,4 +5,5 @@ obj-y += avp/ obj-$(CONFIG_TEGRA_CAMERA) += tegra_camera.o obj-$(CONFIG_VIDEO_OV5650) += ov5650.o obj-$(CONFIG_VIDEO_OV2710) += ov2710.o +obj-$(CONFIG_TORCH_SSL3250A) += ssl3250a.o diff --git a/drivers/media/video/tegra/ssl3250a.c b/drivers/media/video/tegra/ssl3250a.c new file mode 100644 index 000000000000..f2082a5c193c --- /dev/null +++ b/drivers/media/video/tegra/ssl3250a.c @@ -0,0 +1,308 @@ +/* + * ssl3250a.c - ssl3250a flash/torch kernel driver + * + * Copyright (C) 2011 NVIDIA Corp. + * + * 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/fs.h> +#include <linux/i2c.h> +#include <linux/miscdevice.h> +#include <linux/slab.h> +#include <media/ssl3250a.h> + +#define SSL3250A_I2C_REG_AMP 0x00 +#define SSL3250A_I2C_REG_TMR 0x01 +#define SSL3250A_I2C_REG_STRB 0x02 +#define SSL3250A_I2C_REG_STS 0x03 + + +enum { + SSL3250A_GPIO_ACT, + SSL3250A_GPIO_EN1, + SSL3250A_GPIO_EN2, + SSL3250A_GPIO_STRB, +}; + +struct ssl3250a_info { + struct i2c_client *i2c_client; + struct ssl3250a_platform_data *pdata; +}; + +static struct ssl3250a_info *info; + +static int ssl3250a_gpio(u8 gpio, u8 val) +{ + switch (gpio) { + case SSL3250A_GPIO_ACT: + if (info->pdata && info->pdata->gpio_act) + return info->pdata->gpio_act(val); + return -1; + + case SSL3250A_GPIO_EN1: + if (info->pdata && info->pdata->gpio_en1) + return info->pdata->gpio_en1(val); + return -1; + + case SSL3250A_GPIO_EN2: + if (info->pdata && info->pdata->gpio_en2) + return info->pdata->gpio_en2(val); + return -1; + + case SSL3250A_GPIO_STRB: + if (info->pdata && info->pdata->gpio_strb) + return info->pdata->gpio_strb(val); + + default: + return -1; + } +} + +static int ssl3250a_get_reg(u8 addr, u8 *val) +{ + struct i2c_client *client = info->i2c_client; + struct i2c_msg msg[2]; + unsigned char data[2]; + + msg[0].addr = client->addr; + msg[0].flags = 0; + msg[0].len = 1; + msg[0].buf = data; + + data[0] = (u8) (addr); + + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = 1; + msg[1].buf = data + 1; + + *val = 0; + + if (i2c_transfer(client->adapter, msg, 2) == 2) { + *val = data[1]; + return 0; + } else { + return -1; + } +} + +static int ssl3250a_set_reg(u8 addr, u8 val) +{ + struct i2c_client *client = info->i2c_client; + struct i2c_msg msg; + unsigned char data[2]; + + data[0] = (u8) (addr); + data[1] = (u8) (val); + msg.addr = client->addr; + msg.flags = 0; + msg.len = 2; + msg.buf = data; + + if (i2c_transfer(client->adapter, &msg, 1) == 1) + return 0; + else + return -1; +} + +static long ssl3250a_ioctl( + struct file *file, + unsigned int cmd, + unsigned long arg) +{ + u8 val = (u8)arg; + u8 reg; + + switch (cmd) { + case SSL3250A_IOCTL_MODE_SHUTDOWN: + ssl3250a_gpio(SSL3250A_GPIO_ACT, 0); + return 0; + + case SSL3250A_IOCTL_MODE_STANDBY: + ssl3250a_gpio(SSL3250A_GPIO_ACT, 1); + if (info->pdata->config & 0x01) { /*0:0 0=I2C, 1=GPIO*/ + ssl3250a_gpio(SSL3250A_GPIO_EN1, 0); + ssl3250a_gpio(SSL3250A_GPIO_EN2, 0); + return 0; + } else { + return ssl3250a_set_reg(SSL3250A_I2C_REG_AMP, 0x00); + } + +/* Amp limit for torch, flash, and LED is controlled by external circuitry in + * GPIO mode. In I2C mode amp limit is controlled by chip registers and the + * limit values are in the board-sensors file. + */ + case SSL3250A_IOTCL_MODE_TORCH: + ssl3250a_gpio(SSL3250A_GPIO_ACT, 1); + if (info->pdata->config & 0x01) { /*0:0 0=I2C, 1=GPIO*/ + ssl3250a_gpio(SSL3250A_GPIO_EN1, 0); + ssl3250a_gpio(SSL3250A_GPIO_EN2, 1); + return 0; + } else { + if (val > info->pdata->max_amp_torch) + val = info->pdata->max_amp_torch; + val = ((val << 3) & 0xF8); /*7:3=torch amps*/ + if (!ssl3250a_get_reg(SSL3250A_I2C_REG_AMP, ®)) { + val = val | (reg & 0x07); /*shared w/ LED 2:0*/ + return ssl3250a_set_reg(SSL3250A_I2C_REG_AMP, + val); + } else { + return -1; + } + } + + case SSL3250A_IOCTL_MODE_FLASH: + ssl3250a_gpio(SSL3250A_GPIO_ACT, 1); + if (info->pdata->config & 0x01) { /*0:0 0=I2C, 1=GPIO*/ + ssl3250a_gpio(SSL3250A_GPIO_EN1, 1); + ssl3250a_gpio(SSL3250A_GPIO_EN2, 1); + return 0; + } else { + if (val != 0) /*if 0 then flash=off*/ + val = val + 11; /*flash starts at 12*/ + if (val > info->pdata->max_amp_flash) + val = info->pdata->max_amp_flash; + val = ((val << 3) & 0xF8); /*7:3=flash amps*/ + if (!ssl3250a_get_reg(SSL3250A_I2C_REG_AMP, ®)) { + val = val | (reg & 0x07); /*shared w/ LED 2:0*/ + return ssl3250a_set_reg(SSL3250A_I2C_REG_AMP, + val); + } else { + return -1; + } + } + + case SSL3250A_IOCTL_MODE_LED: + ssl3250a_gpio(SSL3250A_GPIO_ACT, 1); + if (info->pdata->config & 0x01) { /*0:0 0=I2C, 1=GPIO*/ + ssl3250a_gpio(SSL3250A_GPIO_EN1, 1); + ssl3250a_gpio(SSL3250A_GPIO_EN2, 0); + return 0; + } else { + if (val > info->pdata->max_amp_indic) + val = info->pdata->max_amp_indic; + val = (val & 0x07); /*2:0=LED amps*/ + if (!ssl3250a_get_reg(SSL3250A_I2C_REG_AMP, ®)) { + val = val | (reg & 0xF8); /*shared w/ 7:3*/ + return ssl3250a_set_reg(SSL3250A_I2C_REG_AMP, + val); + } else { + return -1; + } + } + + case SSL3250A_IOCTL_STRB: + if (val) + val = 0x01; /*bit 0=I2C, >0=GPIO*/ + /* if STRB GPIO use that regardless of operation mode */ + if (!ssl3250a_gpio(SSL3250A_GPIO_STRB, val)) + return 0; + if (!info->pdata->config & 0x01) /*0:0 0=I2C, 1=GPIO*/ + return ssl3250a_set_reg(SSL3250A_I2C_REG_STRB, val); + else + return -1; + + case SSL3250A_IOCTL_TIMER: + if (!info->pdata->config & 0x01) /*if I2C mode*/ + return ssl3250a_set_reg(SSL3250A_I2C_REG_TMR, val); + + default: + return -1; + } +} + + +static int ssl3250a_open(struct inode *inode, struct file *file) +{ + file->private_data = info; + if (info->pdata && info->pdata->init) + info->pdata->init(); + return 0; +} + +int ssl3250a_release(struct inode *inode, struct file *file) +{ + if (info->pdata && info->pdata->exit) + info->pdata->exit(); + file->private_data = NULL; + return 0; +} + + +static const struct file_operations ssl3250a_fileops = { + .owner = THIS_MODULE, + .open = ssl3250a_open, + .unlocked_ioctl = ssl3250a_ioctl, + .release = ssl3250a_release, +}; + +static struct miscdevice ssl3250a_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "ssl3250a", + .fops = &ssl3250a_fileops, +}; + +static int ssl3250a_probe( + struct i2c_client *client, + const struct i2c_device_id *id) +{ + int err; + info = kzalloc(sizeof(struct ssl3250a_info), GFP_KERNEL); + if (!info) { + pr_err("ssl3250a: Unable to allocate memory!\n"); + return -ENOMEM; + } + err = misc_register(&ssl3250a_device); + if (err) { + pr_err("ssl3250a: 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 ssl3250a_remove(struct i2c_client *client) +{ + info = i2c_get_clientdata(client); + misc_deregister(&ssl3250a_device); + kfree(info); + return 0; +} + +static const struct i2c_device_id ssl3250a_id[] = { + { "ssl3250a", 0 }, + { }, +}; + +MODULE_DEVICE_TABLE(i2c, ssl3250a_id); + +static struct i2c_driver ssl3250a_i2c_driver = { + .driver = { + .name = "ssl3250a", + .owner = THIS_MODULE, + }, + .probe = ssl3250a_probe, + .remove = ssl3250a_remove, + .id_table = ssl3250a_id, +}; + +static int __init ssl3250a_init(void) +{ + return i2c_add_driver(&ssl3250a_i2c_driver); +} + +static void __exit ssl3250a_exit(void) +{ + i2c_del_driver(&ssl3250a_i2c_driver); +} + +module_init(ssl3250a_init); +module_exit(ssl3250a_exit); + |