| /* |
| * ssl3250a.c - ssl3250a flash/torch kernel driver |
| * |
| * Copyright (C) 2011 NVIDIA Corporation. |
| * |
| * 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. |
| */ |
| |
| |
| #include <linux/fs.h> |
| #include <linux/i2c.h> |
| #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 <linux/module.h> |
| |
| #include <media/nvc.h> |
| #include <media/ssl3250a.h> |
| |
| #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 } |
| } |
| }; |
| |
| 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_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 void ssl3250a_gpio_act(struct ssl3250a_info *info, int val) |
| { |
| int prev_val; |
| |
| 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 */ |
| } |
| } |
| } |
| |
| static void ssl3250a_pm_regulator_put(struct nvc_regulator *sreg) |
| { |
| regulator_put(sreg->vreg); |
| sreg->vreg = NULL; |
| } |
| |
| 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; |
| } |
| |
| 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); |
| } |
| } |
| return err; |
| } |
| |
| static int ssl3250a_pm_regulator_dis(struct ssl3250a_info *info, |
| struct nvc_regulator *sreg) |
| { |
| 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; |
| } |
| |
| static int ssl3250a_pm_wr(struct ssl3250a_info *info, int pwr) |
| { |
| int err = 0; |
| |
| if (pwr == info->pwr_dev) |
| return 0; |
| |
| 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; |
| |
| default: |
| err = -EINVAL; |
| break; |
| } |
| |
| 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_pm_wr_s(struct ssl3250a_info *info, int pwr) |
| { |
| 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; |
| } |
| |
| static int ssl3250a_pm_api_wr(struct ssl3250a_info *info, int pwr) |
| { |
| int err = 0; |
| |
| 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 |
| info->pwr_api = NVC_PWR_ERR; |
| if (info->pdata->cfg & NVC_CFG_NOERR) |
| return 0; |
| |
| return err; |
| } |
| |
| static int ssl3250a_pm_dev_wr(struct ssl3250a_info *info, int pwr) |
| { |
| if (pwr < info->pwr_api) |
| pwr = info->pwr_api; |
| return ssl3250a_pm_wr(info, pwr); |
| } |
| |
| static void ssl3250a_pm_exit(struct ssl3250a_info *info) |
| { |
| ssl3250a_pm_wr_s(info, NVC_PWR_OFF); |
| ssl3250a_pm_regulator_put(&info->vreg_i2c); |
| } |
| |
| static void ssl3250a_pm_init(struct ssl3250a_info *info) |
| { |
| ssl3250a_pm_regulator_get(info, &info->vreg_i2c, "vdd_i2c"); |
| } |
| |
| 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 (reg) { |
| err = -ENODEV; |
| break; |
| } |
| } |
| } |
| ssl3250a_pm_dev_wr(info, NVC_PWR_OFF); |
| return err; |
| } |
| |
| 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 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; |
| |
| 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 { |
| err = -EINVAL; |
| } |
| break; |
| |
| default: |
| err = -EINVAL; |
| } |
| if (info->pdata->cfg & NVC_CFG_NOERR) |
| return 0; |
| |
| 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 |
| 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; |
| } |
| |
| return 0; |
| |
| default: |
| 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; |
| |
| rcu_read_lock(); |
| list_for_each_entry_rcu(pos, &ssl3250a_info_list, list) { |
| if (pos->miscdev.minor == iminor(inode)) { |
| info = pos; |
| break; |
| } |
| } |
| 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; |
| } |
| |
| static int ssl3250a_release(struct inode *inode, struct file *file) |
| { |
| 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, |
| .unlocked_ioctl = ssl3250a_ioctl, |
| .release = ssl3250a_release, |
| }; |
| |
| 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; |
| |
| 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; |
| } |
| |
| 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); |
| 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; |
| } |
| |
| 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, |
| }, |
| .id_table = ssl3250a_id, |
| .probe = ssl3250a_probe, |
| .remove = ssl3250a_remove, |
| }; |
| |
| 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); |
| MODULE_LICENSE("GPL"); |