diff options
author | Erik Lilliebjerg <elilliebjerg@nvidia.com> | 2011-11-22 00:39:43 -0700 |
---|---|---|
committer | Dan Willemsen <dwillemsen@nvidia.com> | 2011-11-30 21:50:21 -0800 |
commit | c38635b4fbb53312f528520649baea4e361d7ce5 (patch) | |
tree | 26a52907b67df7850acf04b7e1322e669165d735 /drivers/media | |
parent | e8d42ed7a2ac6f93f52b7df71b9895a21536545a (diff) |
media: video: tegra: SSL3250A torch driver
- Added multi-instance support with sync capability
- Added GLOS power scheme
- Standardized the IOCTL API
Bug 882012
Bug 866726
Bug 896181
Bug 894789
Bug 861828
Bug 852480
Bug 872156
Change-Id: Ic315a9ed404854e13568c5b5afae60415a19b093
Signed-off-by: Erik Lilliebjerg <elilliebjerg@nvidia.com>
Reviewed-on: http://git-master/r/66052
Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com>
Rebase-Id: R516fb9110c4fc654ba0d3e1289a3d0eb938a805d
Diffstat (limited to 'drivers/media')
-rw-r--r-- | drivers/media/video/tegra/ssl3250a.c | 1047 |
1 files changed, 850 insertions, 197 deletions
diff --git a/drivers/media/video/tegra/ssl3250a.c b/drivers/media/video/tegra/ssl3250a.c index f832ffee713c..30c70bf1cdc5 100644 --- a/drivers/media/video/tegra/ssl3250a.c +++ b/drivers/media/video/tegra/ssl3250a.c @@ -1,11 +1,84 @@ /* * ssl3250a.c - ssl3250a flash/torch kernel driver * - * Copyright (C) 2011 NVIDIA Corp. + * Copyright (C) 2011 NVIDIA Corporation. * - * 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. + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +/* Implementation + * -------------- + * The board level details about the device need to be provided in the board + * file with the ssl3250a_platform_data structure. + * Standard among NVC kernel drivers in this structure is: + * .cfg = Use the NVC_CFG_ defines that are in nvc_torch.h. + * Descriptions of the configuration options are with the defines. + * This value is typically 0. + * .num = The number of the instance of the device. This should start at 1 and + * and increment for each device on the board. This number will be + * appended to the MISC driver name, Example: /dev/ssl3250a.1 + * .sync = If there is a need to synchronize two devices, then this value is + * the number of the device instance this device is allowed to sync to. + * This is typically used for stereo applications. + * .dev_name = The MISC driver name the device registers as. If not used, + * then the part number of the device is used for the driver name. + * If using the NVC user driver then use the name found in this + * driver under _default_pdata. + * + * The following is specific to NVC kernel flash/torch drivers: + * .pinstate = a pointer to the nvc_torch_pin_state structure. This + * structure gives the details of which VI GPIO to use to trigger + * the flash. The mask tells which pin and the values is the + * level. For example, if VI GPIO pin 6 is used, then + * .mask = 0x0040 + * .values = 0x0040 + * If VI GPIO pin 0 is used, then + * .mask = 0x0001 + * .values = 0x0001 + * This is typically just one pin but there is some legacy + * here that insinuates more than one pin can be used. + * When the flash level is set, then the driver will return the + * value in values. When the flash level is off, the driver will + * return 0 for the values to deassert the signal. + * If a VI GPIO is not used, then the mask and values must be set + * to 0. The flash may then be triggered via I2C instead. + * However, a VI GPIO is strongly encouraged since it allows + * tighter timing with the picture taken as well as reduced power + * by asserting the trigger signal for only when needed. + * .max_amp_torch = Is the maximum torch value allowed. The value is 0 to + * _MAX_TORCH_LEVEL. This is to allow a limit to the amount + * of amps used. If left blank then _MAX_TORCH_LEVEL will be + * used. + * .max_amp_flash = Is the maximum flash value allowed. The value is 0 to + * _MAX_FLASH_LEVEL. This is to allow a limit to the amount + * of amps used. If left blank then _MAX_FLASH_LEVEL will be + * used. + * + * The following is specific to only this NVC kernel flash/torch driver: + * .gpio_act = Is the GPIO needed to control the ACT signal. If tied high, + * then this can be left blank. + * + * Power Requirements + * The board power file must contain the following labels for the power + * regulator(s) of this device: + * "vdd_i2c" = the power regulator for the I2C power. + * Note that this device is typically connected directly to the battery rail + * and does not need a source power regulator (vdd). + * + * The above values should be all that is needed to use the device with this + * driver. Modifications of this driver should not be needed. */ @@ -14,250 +87,785 @@ #include <linux/miscdevice.h> #include <linux/slab.h> #include <linux/delay.h> +#include <linux/uaccess.h> +#include <linux/list.h> +#include <linux/regulator/consumer.h> +#include <linux/gpio.h> +#include <media/nvc.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 - +#define SSL3250A_REG_AMP 0x00 +#define SSL3250A_REG_TMR 0x01 +#define SSL3250A_REG_STRB 0x02 +#define SSL3250A_REG_STS 0x03 +#define ssl3250a_flash_cap_size (sizeof(ssl3250a_flash_cap.numberoflevels) \ + + (sizeof(ssl3250a_flash_cap.levels[0]) \ + * (SSL3250A_MAX_FLASH_LEVEL + 1))) +#define ssl3250a_torch_cap_size (sizeof(ssl3250a_torch_cap.numberoflevels) \ + + (sizeof(ssl3250a_torch_cap.guidenum[0]) \ + * (SSL3250A_MAX_TORCH_LEVEL + 1))) + + +static struct nvc_torch_flash_capabilities ssl3250a_flash_cap = { + SSL3250A_MAX_FLASH_LEVEL + 1, + { + { 0, 0xFFFFFFFF, 0 }, + { 215, 820, 20 }, + { 230, 820, 20 }, + { 245, 820, 20 }, + { 260, 820, 20 }, + { 275, 820, 20 }, + { 290, 820, 20 }, + { 305, 820, 20 }, + { 320, 820, 20 }, + { 335, 820, 20 }, + { 350, 820, 20 }, + { 365, 820, 20 }, + { 380, 820, 20 }, + { 395, 820, 20 }, + { 410, 820, 20 }, + { 425, 820, 20 }, + { 440, 820, 20 }, + { 455, 820, 20 }, + { 470, 820, 20 }, + { 485, 820, 20 }, + { 500, 820, 20 } + } +}; -enum { - SSL3250A_GPIO_ACT, - SSL3250A_GPIO_EN1, - SSL3250A_GPIO_EN2, - SSL3250A_GPIO_STRB, +static struct nvc_torch_torch_capabilities ssl3250a_torch_cap = { + SSL3250A_MAX_TORCH_LEVEL + 1, + { + 0, + 50, + 65, + 80, + 95, + 110, + 125, + 140, + 155, + 170, + 185, + 200 + } }; struct ssl3250a_info { + atomic_t in_use; struct i2c_client *i2c_client; struct ssl3250a_platform_data *pdata; + struct miscdevice miscdev; + struct list_head list; + int pwr_api; + int pwr_dev; + struct nvc_regulator vreg_i2c; + u8 s_mode; + struct ssl3250a_info *s_info; +}; + +static struct nvc_torch_pin_state ssl3250a_default_pinstate = { + .mask = 0x0000, + .values = 0x0000, }; -static struct ssl3250a_info *info; +static struct ssl3250a_platform_data ssl3250a_default_pdata = { + .cfg = 0, + .num = 0, + .sync = 0, + .dev_name = "torch", + .pinstate = &ssl3250a_default_pinstate, + .max_amp_torch = SSL3250A_MAX_TORCH_LEVEL, + .max_amp_flash = SSL3250A_MAX_FLASH_LEVEL, +}; + +static LIST_HEAD(ssl3250a_info_list); +static DEFINE_SPINLOCK(ssl3250a_spinlock); + + +static int ssl3250a_i2c_rd(struct ssl3250a_info *info, u8 reg, u8 *val) +{ + struct i2c_msg msg[2]; + u8 buf[2]; + + buf[0] = reg; + msg[0].addr = info->i2c_client->addr; + msg[0].flags = 0; + msg[0].len = 1; + msg[0].buf = &buf[0]; + msg[1].addr = info->i2c_client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = 1; + msg[1].buf = &buf[1]; + *val = 0; + if (i2c_transfer(info->i2c_client->adapter, msg, 2) != 2) + return -EIO; + + *val = buf[1]; + return 0; +} + +static int ssl3250a_i2c_wr(struct ssl3250a_info *info, u8 reg, u8 val) +{ + struct i2c_msg msg; + u8 buf[2]; + + buf[0] = reg; + buf[1] = val; + msg.addr = info->i2c_client->addr; + msg.flags = 0; + msg.len = 2; + msg.buf = &buf[0]; + if (i2c_transfer(info->i2c_client->adapter, &msg, 1) != 1) + return -EIO; + + return 0; +} -static int ssl3250a_gpio(u8 gpio, u8 val) +static void ssl3250a_gpio_act(struct ssl3250a_info *info, int val) { int prev_val; - switch (gpio) { - case SSL3250A_GPIO_ACT: - if (info->pdata && info->pdata->gpio_act) { - prev_val = info->pdata->gpio_act(val); - if (val && (prev_val ^ val)) - mdelay(1); /*delay for device ready*/ - return 0; + if (info->pdata->gpio_act) { + prev_val = gpio_get_value(info->pdata->gpio_act); + if (val != prev_val) { + gpio_set_value(info->pdata->gpio_act, val); + if (val) + mdelay(1); /* delay for device startup */ } - return -1; + } +} - case SSL3250A_GPIO_EN1: - if (info->pdata && info->pdata->gpio_en1) { - info->pdata->gpio_en1(val); - return 0; - } - return -1; +static void ssl3250a_pm_regulator_put(struct nvc_regulator *sreg) +{ + regulator_put(sreg->vreg); + sreg->vreg = NULL; +} - case SSL3250A_GPIO_EN2: - if (info->pdata && info->pdata->gpio_en2) { - info->pdata->gpio_en2(val); - return 0; - } - return -1; +static int ssl3250a_pm_regulator_get(struct ssl3250a_info *info, + struct nvc_regulator *sreg, + char vreg_name[]) +{ + int err = 0; + + sreg->vreg_flag = 0; + sreg->vreg = regulator_get(&info->i2c_client->dev, vreg_name); + if (WARN_ON(IS_ERR(sreg->vreg))) { + dev_err(&info->i2c_client->dev, + "%s err for regulator: %s err: %d\n", + __func__, vreg_name, (int)sreg->vreg); + err = PTR_ERR(sreg->vreg); + sreg->vreg = NULL; + } else { + sreg->vreg_name = vreg_name; + dev_dbg(&info->i2c_client->dev, + "%s vreg_name: %s\n", + __func__, sreg->vreg_name); + } + return err; +} - case SSL3250A_GPIO_STRB: - if (info->pdata && info->pdata->gpio_strb) { - info->pdata->gpio_strb(val); - return 0; +static int ssl3250a_pm_regulator_en(struct ssl3250a_info *info, + struct nvc_regulator *sreg) +{ + int err = 0; + + if (!sreg->vreg_flag && (sreg->vreg != NULL)) { + err = regulator_enable(sreg->vreg); + if (!err) { + dev_dbg(&info->i2c_client->dev, + "%s vreg_name: %s\n", + __func__, sreg->vreg_name); + sreg->vreg_flag = 1; + err = 1; /* flag regulator state change */ + } else { + dev_err(&info->i2c_client->dev, + "%s err, regulator: %s\n", + __func__, sreg->vreg_name); } - - default: - return -1; } + return err; } -static int ssl3250a_get_reg(u8 addr, u8 *val) +static int ssl3250a_pm_regulator_dis(struct ssl3250a_info *info, + struct nvc_regulator *sreg) { - struct i2c_client *client = info->i2c_client; - struct i2c_msg msg[2]; - unsigned char data[2]; + int err = 0; + + if (sreg->vreg_flag && (sreg->vreg != NULL)) { + err = regulator_disable(sreg->vreg); + if (!err) + dev_dbg(&info->i2c_client->dev, + "%s vreg_name: %s\n", + __func__, sreg->vreg_name); + else + dev_err(&info->i2c_client->dev, + "%s err, regulator: %s\n", + __func__, sreg->vreg_name); + } + sreg->vreg_flag = 0; + return err; +} - msg[0].addr = client->addr; - msg[0].flags = 0; - msg[0].len = 1; - msg[0].buf = data; +static int ssl3250a_pm_wr(struct ssl3250a_info *info, int pwr) +{ + int err = 0; - data[0] = (u8) (addr); + if (pwr == info->pwr_dev) + return 0; - msg[1].addr = client->addr; - msg[1].flags = I2C_M_RD; - msg[1].len = 1; - msg[1].buf = data + 1; + switch (pwr) { + case NVC_PWR_OFF: + if ((info->pdata->cfg & NVC_CFG_OFF2STDBY) || + (info->pdata->cfg & NVC_CFG_BOOT_INIT)) { + pwr = NVC_PWR_STDBY; + } else { + ssl3250a_gpio_act(info, 0); + err = ssl3250a_pm_regulator_dis(info, &info->vreg_i2c); + break; + } + case NVC_PWR_STDBY_OFF: + if ((info->pdata->cfg & NVC_CFG_OFF2STDBY) || + (info->pdata->cfg & NVC_CFG_BOOT_INIT)) { + pwr = NVC_PWR_STDBY; + } else { + ssl3250a_gpio_act(info, 0); + err = ssl3250a_pm_regulator_en(info, &info->vreg_i2c); + break; + } + case NVC_PWR_STDBY: + err = ssl3250a_pm_regulator_en(info, &info->vreg_i2c); + ssl3250a_gpio_act(info, 1); + err |= ssl3250a_i2c_wr(info, SSL3250A_REG_AMP, 0x00); + break; + + case NVC_PWR_COMM: + case NVC_PWR_ON: + err = ssl3250a_pm_regulator_en(info, &info->vreg_i2c); + ssl3250a_gpio_act(info, 1); + break; - *val = 0; + default: + err = -EINVAL; + break; + } - if (i2c_transfer(client->adapter, msg, 2) == 2) { - *val = data[1]; - return 0; - } else { - return -1; + if (err < 0) { + dev_err(&info->i2c_client->dev, "%s error\n", __func__); + pwr = NVC_PWR_ERR; } + info->pwr_dev = pwr; + if (err > 0) + return 0; + + return err; } -static int ssl3250a_set_reg(u8 addr, u8 val) +static int ssl3250a_pm_wr_s(struct ssl3250a_info *info, int pwr) { - struct i2c_client *client = info->i2c_client; - struct i2c_msg msg; - unsigned char data[2]; + int err1 = 0; + int err2 = 0; + + if ((info->s_mode == NVC_SYNC_OFF) || + (info->s_mode == NVC_SYNC_MASTER) || + (info->s_mode == NVC_SYNC_STEREO)) + err1 = ssl3250a_pm_wr(info, pwr); + if ((info->s_mode == NVC_SYNC_SLAVE) || + (info->s_mode == NVC_SYNC_STEREO)) + err2 = ssl3250a_pm_wr(info->s_info, pwr); + return err1 | err2; +} - data[0] = (u8) (addr); - data[1] = (u8) (val); - msg.addr = client->addr; - msg.flags = 0; - msg.len = 2; - msg.buf = data; +static int ssl3250a_pm_api_wr(struct ssl3250a_info *info, int pwr) +{ + int err = 0; - if (i2c_transfer(client->adapter, &msg, 1) == 1) + if (!pwr || (pwr > NVC_PWR_ON)) return 0; + + if (pwr > info->pwr_dev) + err = ssl3250a_pm_wr_s(info, pwr); + if (!err) + info->pwr_api = pwr; else - return -1; + info->pwr_api = NVC_PWR_ERR; + if (info->pdata->cfg & NVC_CFG_NOERR) + return 0; + + return err; } -static long ssl3250a_ioctl( - struct file *file, - unsigned int cmd, - unsigned long arg) +static int ssl3250a_pm_dev_wr(struct ssl3250a_info *info, int pwr) { - u8 val = (u8)arg; - u8 reg; + if (pwr < info->pwr_api) + pwr = info->pwr_api; + return ssl3250a_pm_wr(info, pwr); +} - switch (cmd) { - case SSL3250A_IOCTL_MODE_SHUTDOWN: - ssl3250a_gpio(SSL3250A_GPIO_ACT, 0); - return 0; +static void ssl3250a_pm_exit(struct ssl3250a_info *info) +{ + ssl3250a_pm_wr_s(info, NVC_PWR_OFF); + ssl3250a_pm_regulator_put(&info->vreg_i2c); +} - 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); - } +static void ssl3250a_pm_init(struct ssl3250a_info *info) +{ + ssl3250a_pm_regulator_get(info, &info->vreg_i2c, "vdd_i2c"); +} -/* 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; +static int ssl3250a_dev_id(struct ssl3250a_info *info) +{ + u8 addr; + u8 reg; + int err; + + ssl3250a_pm_dev_wr(info, NVC_PWR_COMM); + /* There isn't a device ID so we just check that all the registers + * equal their startup defaults, which in this case, is 0. + */ + for (addr = 0; addr < SSL3250A_REG_STS; addr++) { + err = ssl3250a_i2c_rd(info, addr, ®); + if (err) { + break; } 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; + if (reg) { + err = -ENODEV; + break; } } + } + ssl3250a_pm_dev_wr(info, NVC_PWR_OFF); + return err; +} - 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; - } +static int ssl3250a_param_rd(struct ssl3250a_info *info, long arg) +{ + struct nvc_param params; + struct nvc_torch_pin_state pinstate; + const void *data_ptr; + u32 data_size = 0; + int err; + u8 reg; + + if (copy_from_user(¶ms, + (const void __user *)arg, + sizeof(struct nvc_param))) { + dev_err(&info->i2c_client->dev, "%s %d copy_from_user err\n", + __func__, __LINE__); + return -EINVAL; + } + + if (info->s_mode == NVC_SYNC_SLAVE) + info = info->s_info; + switch (params.param) { + case NVC_PARAM_FLASH_CAPS: + dev_dbg(&info->i2c_client->dev, "%s FLASH_CAPS\n", __func__); + data_ptr = &ssl3250a_flash_cap; + data_size = ssl3250a_flash_cap_size; + break; + + case NVC_PARAM_FLASH_LEVEL: + ssl3250a_pm_dev_wr(info, NVC_PWR_COMM); + err = ssl3250a_i2c_rd(info, SSL3250A_REG_AMP, ®); + ssl3250a_pm_dev_wr(info, NVC_PWR_OFF); + if (err < 0) + return err; + + reg >>= 3; /*7:3=flash amps*/ + reg &= 0x1F; /*4:0=flash amps*/ + if (reg < 12) /*flash starts at 12*/ + reg = 0; /*<12=torch or off*/ + else + reg -= 11; /*create flash index*/ + dev_dbg(&info->i2c_client->dev, "%s FLASH_LEVEL: %u\n", + __func__, + (unsigned)ssl3250a_flash_cap.levels[reg].guidenum); + data_ptr = &ssl3250a_flash_cap.levels[reg].guidenum; + data_size = sizeof(ssl3250a_flash_cap.levels[reg].guidenum); + break; + + case NVC_PARAM_TORCH_CAPS: + dev_dbg(&info->i2c_client->dev, "%s TORCH_CAPS\n", __func__); + data_ptr = &ssl3250a_torch_cap; + data_size = ssl3250a_torch_cap_size; + break; + + case NVC_PARAM_TORCH_LEVEL: + ssl3250a_pm_dev_wr(info, NVC_PWR_COMM); + err = ssl3250a_i2c_rd(info, SSL3250A_REG_AMP, ®); + ssl3250a_pm_dev_wr(info, NVC_PWR_OFF); + if (err < 0) + return err; + + reg >>= 3; /*7:3=torch amps*/ + reg &= 0x1F; /*4:0=torch amps*/ + if (reg > 11) /*flash starts at 12*/ + reg = 0; /*>11=flash mode (torch off)*/ + dev_dbg(&info->i2c_client->dev, "%s TORCH_LEVEL: %u\n", + __func__, + (unsigned)ssl3250a_torch_cap.guidenum[reg]); + data_ptr = &ssl3250a_torch_cap.guidenum[reg]; + data_size = sizeof(ssl3250a_torch_cap.guidenum[reg]); + break; + + case NVC_PARAM_FLASH_PIN_STATE: + pinstate.mask = info->pdata->pinstate->mask; + ssl3250a_pm_dev_wr(info, NVC_PWR_COMM); + err = ssl3250a_i2c_rd(info, SSL3250A_REG_AMP, ®); + ssl3250a_pm_dev_wr(info, NVC_PWR_OFF); + if (err < 0) + return err; + + reg >>= 3; /*7:3=flash amps*/ + reg &= 0x1F; /*4:0=flash amps*/ + if (reg < 12) /*flash starts at 12*/ + pinstate.values = 0; /*deassert strobe*/ + else + /*assert strobe*/ + pinstate.values = info->pdata->pinstate->values; + dev_dbg(&info->i2c_client->dev, "%s FLASH_PIN_STATE: %x&%x\n", + __func__, pinstate.mask, pinstate.values); + data_ptr = &pinstate; + data_size = sizeof(struct nvc_torch_pin_state); + break; + + case NVC_PARAM_STEREO: + dev_dbg(&info->i2c_client->dev, "%s STEREO: %d\n", + __func__, info->s_mode); + data_ptr = &info->s_mode; + data_size = sizeof(info->s_mode); + break; + + default: + dev_err(&info->i2c_client->dev, + "%s unsupported parameter: %d\n", + __func__, params.param); + return -EINVAL; + } + + if (params.sizeofvalue < data_size) { + dev_err(&info->i2c_client->dev, + "%s data size mismatch %d != %d\n", + __func__, params.sizeofvalue, data_size); + return -EINVAL; + } + + if (copy_to_user((void __user *)params.p_value, + data_ptr, + data_size)) { + dev_err(&info->i2c_client->dev, + "%s copy_to_user err line %d\n", + __func__, __LINE__); + return -EFAULT; + } + + return 0; +} + +static int ssl3250a_param_wr_s(struct ssl3250a_info *info, + struct nvc_param *params, + u8 val) +{ + int err; + + switch (params->param) { + case NVC_PARAM_FLASH_LEVEL: + dev_dbg(&info->i2c_client->dev, "%s FLASH_LEVEL: %d\n", + __func__, val); + ssl3250a_pm_dev_wr(info, NVC_PWR_ON); + if (val > ssl3250a_default_pdata.max_amp_flash) + val = ssl3250a_default_pdata.max_amp_flash; + /*Amp limit values are in the board-sensors file.*/ + if (info->pdata->max_amp_flash && + (val > info->pdata->max_amp_flash)) + val = info->pdata->max_amp_flash; + if (val) { + val += 11; /*flash starts at 12*/ + val <<= 3; /*7:3=flash/torch amps*/ } + err = ssl3250a_i2c_wr(info, SSL3250A_REG_AMP, val); + if (!val) /*turn pwr off if no flash && no pwr_api*/ + ssl3250a_pm_dev_wr(info, NVC_PWR_OFF); + return err; + + case NVC_PARAM_TORCH_LEVEL: + dev_dbg(&info->i2c_client->dev, "%s TORCH_LEVEL: %d\n", + __func__, val); + ssl3250a_pm_dev_wr(info, NVC_PWR_ON); + if (val > ssl3250a_default_pdata.max_amp_torch) + val = ssl3250a_default_pdata.max_amp_torch; + /*Amp limit values are in the board-sensors file.*/ + if (info->pdata->max_amp_torch && + (val > info->pdata->max_amp_torch)) + val = info->pdata->max_amp_torch; + if (val) + val <<= 3; /*7:3=flash/torch amps*/ + err = ssl3250a_i2c_wr(info, SSL3250A_REG_AMP, val); + if (!val) /*turn pwr off if no torch && no pwr_api*/ + ssl3250a_pm_dev_wr(info, NVC_PWR_OFF); + return err; - 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); + case NVC_PARAM_FLASH_PIN_STATE: + dev_dbg(&info->i2c_client->dev, "%s FLASH_PIN_STATE: %d\n", + __func__, val); + if (val) + val = 0x01; /*0:0=soft trigger*/ + return ssl3250a_i2c_wr(info, SSL3250A_REG_STRB, val); + + default: + dev_err(&info->i2c_client->dev, + "%s unsupported parameter: %d\n", + __func__, params->param); + return -EINVAL; + } +} + +static int ssl3250a_param_wr(struct ssl3250a_info *info, long arg) +{ + struct nvc_param params; + u8 val; + int err = 0; + + if (copy_from_user(¶ms, + (const void __user *)arg, + sizeof(struct nvc_param))) { + dev_err(&info->i2c_client->dev, "%s %d copy_from_user err\n", + __func__, __LINE__); + return -EINVAL; + } + + if (copy_from_user(&val, (const void __user *)params.p_value, + sizeof(val))) { + dev_err(&info->i2c_client->dev, "%s %d copy_from_user err\n", + __func__, __LINE__); + return -EINVAL; + } + + /* parameters independent of sync mode */ + switch (params.param) { + case NVC_PARAM_STEREO: + dev_dbg(&info->i2c_client->dev, "%s STEREO: %d\n", + __func__, (int)val); + if (val == info->s_mode) 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); + + switch (val) { + case NVC_SYNC_OFF: + info->s_mode = val; + if (info->s_info != NULL) { + info->s_info->s_mode = val; + ssl3250a_pm_wr(info->s_info, NVC_PWR_OFF); + } + break; + + case NVC_SYNC_MASTER: + info->s_mode = val; + if (info->s_info != NULL) + info->s_info->s_mode = val; + break; + + case NVC_SYNC_SLAVE: + case NVC_SYNC_STEREO: + if (info->s_info != NULL) { + /* sync power */ + info->s_info->pwr_api = info->pwr_api; + err = ssl3250a_pm_wr(info->s_info, + info->pwr_dev); + if (!err) { + info->s_mode = val; + info->s_info->s_mode = val; + } else { + ssl3250a_pm_wr(info->s_info, + NVC_PWR_OFF); + err = -EIO; + } } else { - return -1; + err = -EINVAL; } - } + break; - 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)) + default: + err = -EINVAL; + } + if (info->pdata->cfg & NVC_CFG_NOERR) return 0; - if (!info->pdata->config & 0x01) /*0:0 0=I2C, 1=GPIO*/ - return ssl3250a_set_reg(SSL3250A_I2C_REG_STRB, val); + + return err; + + default: + /* parameters dependent on sync mode */ + switch (info->s_mode) { + case NVC_SYNC_OFF: + case NVC_SYNC_MASTER: + return ssl3250a_param_wr_s(info, ¶ms, val); + + case NVC_SYNC_SLAVE: + return ssl3250a_param_wr_s(info->s_info, + ¶ms, + val); + + case NVC_SYNC_STEREO: + err = ssl3250a_param_wr_s(info, ¶ms, val); + if (!(info->pdata->cfg & NVC_CFG_SYNC_I2C_MUX)) + err |= ssl3250a_param_wr_s(info->s_info, + ¶ms, + val); + return err; + + default: + dev_err(&info->i2c_client->dev, "%s %d internal err\n", + __func__, __LINE__); + return -EINVAL; + } + } +} + +static long ssl3250a_ioctl(struct file *file, + unsigned int cmd, + unsigned long arg) +{ + struct ssl3250a_info *info = file->private_data; + int pwr; + + switch (cmd) { + case NVC_IOCTL_PARAM_WR: + return ssl3250a_param_wr(info, arg); + + case NVC_IOCTL_PARAM_RD: + return ssl3250a_param_rd(info, arg); + + case NVC_IOCTL_PWR_WR: + /* This is a Guaranteed Level of Service (GLOS) call */ + pwr = (int)arg * 2; + dev_dbg(&info->i2c_client->dev, "%s PWR_WR: %d\n", + __func__, pwr); + return ssl3250a_pm_api_wr(info, pwr); + + case NVC_IOCTL_PWR_RD: + if (info->s_mode == NVC_SYNC_SLAVE) + pwr = info->s_info->pwr_api / 2; else - return -1; + pwr = info->pwr_api / 2; + dev_dbg(&info->i2c_client->dev, "%s PWR_RD: %d\n", + __func__, pwr); + if (copy_to_user((void __user *)arg, (const void *)&pwr, + sizeof(pwr))) { + dev_err(&info->i2c_client->dev, + "%s copy_to_user err line %d\n", + __func__, __LINE__); + return -EFAULT; + } - case SSL3250A_IOCTL_TIMER: - if (!info->pdata->config & 0x01) /*if I2C mode*/ - return ssl3250a_set_reg(SSL3250A_I2C_REG_TMR, val); + return 0; default: - return -1; + dev_err(&info->i2c_client->dev, "%s unsupported ioctl: %x\n", + __func__, cmd); + return -EINVAL; } } +static int ssl3250a_sync_en(int dev1, int dev2) +{ + struct ssl3250a_info *sync1 = NULL; + struct ssl3250a_info *sync2 = NULL; + struct ssl3250a_info *pos = NULL; + + rcu_read_lock(); + list_for_each_entry_rcu(pos, &ssl3250a_info_list, list) { + if (pos->pdata->num == dev1) { + sync1 = pos; + break; + } + } + pos = NULL; + list_for_each_entry_rcu(pos, &ssl3250a_info_list, list) { + if (pos->pdata->num == dev2) { + sync2 = pos; + break; + } + } + rcu_read_unlock(); + if (sync1 != NULL) + sync1->s_info = NULL; + if (sync2 != NULL) + sync2->s_info = NULL; + if (!dev1 && !dev2) + return 0; /* no err if default instance 0's used */ + + if (dev1 == dev2) + return -EINVAL; /* err if sync instance is itself */ + + if ((sync1 != NULL) && (sync2 != NULL)) { + sync1->s_info = sync2; + sync2->s_info = sync1; + } + + return 0; +} + +static int ssl3250a_sync_dis(struct ssl3250a_info *info) +{ + if (info->s_info != NULL) { + info->s_info->s_mode = 0; + info->s_info->s_info = NULL; + info->s_mode = 0; + info->s_info = NULL; + return 0; + } + + return -EINVAL; +} static int ssl3250a_open(struct inode *inode, struct file *file) { + struct ssl3250a_info *info = NULL; + struct ssl3250a_info *pos = NULL; int err; - u8 reg; - file->private_data = info; - pr_info("%s\n", __func__); - if (info->pdata && info->pdata->init) { - err = info->pdata->init(); - if (err) - pr_err("ssl3250a_open: Board init failed\n"); + rcu_read_lock(); + list_for_each_entry_rcu(pos, &ssl3250a_info_list, list) { + if (pos->miscdev.minor == iminor(inode)) { + info = pos; + break; + } } - ssl3250a_gpio(SSL3250A_GPIO_ACT, 1); - err = ssl3250a_get_reg(SSL3250A_I2C_REG_STS, ®); - ssl3250a_gpio(SSL3250A_GPIO_ACT, 0); - if (err) - pr_err("ssl3250a_open: Device init failed\n"); + rcu_read_unlock(); + if (!info) + return -ENODEV; + + err = ssl3250a_sync_en(info->pdata->num, info->pdata->sync); + if (err == -EINVAL) + dev_err(&info->i2c_client->dev, + "%s err: invalid num (%u) and sync (%u) instance\n", + __func__, info->pdata->num, info->pdata->sync); + if (atomic_xchg(&info->in_use, 1)) + return -EBUSY; + + if (info->s_info != NULL) { + if (atomic_xchg(&info->s_info->in_use, 1)) + return -EBUSY; + } + + file->private_data = info; + dev_dbg(&info->i2c_client->dev, "%s\n", __func__); return 0; } -int ssl3250a_release(struct inode *inode, struct file *file) +static int ssl3250a_release(struct inode *inode, struct file *file) { - if (info->pdata && info->pdata->exit) - info->pdata->exit(); + struct ssl3250a_info *info = file->private_data; + + dev_dbg(&info->i2c_client->dev, "%s\n", __func__); + ssl3250a_pm_wr_s(info, NVC_PWR_OFF); file->private_data = NULL; + WARN_ON(!atomic_xchg(&info->in_use, 0)); + if (info->s_info != NULL) + WARN_ON(!atomic_xchg(&info->s_info->in_use, 0)); + ssl3250a_sync_dis(info); return 0; } - static const struct file_operations ssl3250a_fileops = { .owner = THIS_MODULE, .open = ssl3250a_open, @@ -265,39 +873,84 @@ static const struct file_operations ssl3250a_fileops = { .release = ssl3250a_release, }; -static struct miscdevice ssl3250a_device = { - .minor = MISC_DYNAMIC_MINOR, - .name = "ssl3250a", - .fops = &ssl3250a_fileops, -}; +static void ssl3250a_del(struct ssl3250a_info *info) +{ + ssl3250a_pm_exit(info); + ssl3250a_sync_dis(info); + spin_lock(&ssl3250a_spinlock); + list_del_rcu(&info->list); + spin_unlock(&ssl3250a_spinlock); + synchronize_rcu(); +} + +static int ssl3250a_remove(struct i2c_client *client) +{ + struct ssl3250a_info *info = i2c_get_clientdata(client); + + dev_dbg(&info->i2c_client->dev, "%s\n", __func__); + misc_deregister(&info->miscdev); + ssl3250a_del(info); + return 0; +} static int ssl3250a_probe( struct i2c_client *client, const struct i2c_device_id *id) { + struct ssl3250a_info *info; + char dname[16]; int err; - info = kzalloc(sizeof(struct ssl3250a_info), GFP_KERNEL); - if (!info) { - pr_err("ssl3250a: Unable to allocate memory!\n"); + + dev_dbg(&client->dev, "%s\n", __func__); + info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL); + if (info == NULL) { + dev_err(&client->dev, "%s: kzalloc error\n", __func__); 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; + if (client->dev.platform_data) { + info->pdata = client->dev.platform_data; + } else { + info->pdata = &ssl3250a_default_pdata; + dev_dbg(&client->dev, + "%s No platform data. Using defaults.\n", + __func__); + } i2c_set_clientdata(client, info); - return 0; -} + INIT_LIST_HEAD(&info->list); + spin_lock(&ssl3250a_spinlock); + list_add_rcu(&info->list, &ssl3250a_info_list); + spin_unlock(&ssl3250a_spinlock); + ssl3250a_pm_init(info); + err = ssl3250a_dev_id(info); + if (err < 0) { + dev_err(&client->dev, "%s device not found\n", __func__); + if (info->pdata->cfg & NVC_CFG_NODEV) { + ssl3250a_del(info); + return -ENODEV; + } + } else { + dev_dbg(&client->dev, "%s device found\n", __func__); + } + + if (info->pdata->dev_name != 0) + strcpy(dname, info->pdata->dev_name); + else + strcpy(dname, "ssl3250a"); + if (info->pdata->num) + snprintf(dname, sizeof(dname), "%s.%u", + dname, info->pdata->num); + info->miscdev.name = dname; + info->miscdev.fops = &ssl3250a_fileops; + info->miscdev.minor = MISC_DYNAMIC_MINOR; + if (misc_register(&info->miscdev)) { + dev_err(&client->dev, "%s unable to register misc device %s\n", + __func__, dname); + ssl3250a_del(info); + return -ENODEV; + } -static int ssl3250a_remove(struct i2c_client *client) -{ - info = i2c_get_clientdata(client); - misc_deregister(&ssl3250a_device); - kfree(info); return 0; } @@ -313,9 +966,9 @@ static struct i2c_driver ssl3250a_i2c_driver = { .name = "ssl3250a", .owner = THIS_MODULE, }, + .id_table = ssl3250a_id, .probe = ssl3250a_probe, .remove = ssl3250a_remove, - .id_table = ssl3250a_id, }; static int __init ssl3250a_init(void) |