summaryrefslogtreecommitdiff
path: root/drivers/media
diff options
context:
space:
mode:
authorErik Lilliebjerg <elilliebjerg@nvidia.com>2011-11-22 00:39:43 -0700
committerDan Willemsen <dwillemsen@nvidia.com>2011-11-30 21:50:21 -0800
commitc38635b4fbb53312f528520649baea4e361d7ce5 (patch)
tree26a52907b67df7850acf04b7e1322e669165d735 /drivers/media
parente8d42ed7a2ac6f93f52b7df71b9895a21536545a (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.c1047
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, &reg);
+ 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, &reg)) {
- 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, &reg)) {
- 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(&params,
+ (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, &reg);
+ 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, &reg);
+ 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, &reg);
+ 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(&params,
+ (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, &reg)) {
- 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, &params, val);
+
+ case NVC_SYNC_SLAVE:
+ return ssl3250a_param_wr_s(info->s_info,
+ &params,
+ val);
+
+ case NVC_SYNC_STEREO:
+ err = ssl3250a_param_wr_s(info, &params, val);
+ if (!(info->pdata->cfg & NVC_CFG_SYNC_I2C_MUX))
+ err |= ssl3250a_param_wr_s(info->s_info,
+ &params,
+ 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, &reg);
- 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)