diff options
Diffstat (limited to 'drivers/iio/light/cm3391.c')
| -rw-r--r-- | drivers/iio/light/cm3391.c | 265 |
1 files changed, 156 insertions, 109 deletions
diff --git a/drivers/iio/light/cm3391.c b/drivers/iio/light/cm3391.c index ed2d84a61fe..abe8b8e7f5c 100644 --- a/drivers/iio/light/cm3391.c +++ b/drivers/iio/light/cm3391.c @@ -20,7 +20,6 @@ #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> #include <linux/iio/events.h> - #include <linux/init.h> #ifdef CONFIG_ACPI @@ -30,51 +29,60 @@ #define OLDAPI /* Registers Address */ -#define CM3391_REG_ADDR_CMD 0x00 -#define CM3391_REG_ADDR_TEST 0x01 -#define CM3391_REG_ADDR_WH 0x02 -#define CM3391_REG_ADDR_WL 0x03 -#define CM3391_REG_ADDR_RED 0x08 -#define CM3391_REG_ADDR_ALS 0x09 -#define CM3391_REG_ADDR_BLUE 0x0A -#define CM3391_REG_ADDR_CLEAR 0x0B -#define CM3391_REG_ADDR_ID 0x0C -#define CM3391_REG_ADDR_STATUS 0x0D +#define CM3391_REG_ADDR_CMD 0x00 +#define CM3391_REG_ADDR_TEST 0x01 +#define CM3391_REG_ADDR_WH 0x02 +#define CM3391_REG_ADDR_WL 0x03 +#define CM3391_REG_ADDR_RED 0x08 +#define CM3391_REG_ADDR_ALS 0x09 +#define CM3391_REG_ADDR_BLUE 0x0A +#define CM3391_REG_ADDR_CLEAR 0x0B +#define CM3391_REG_ADDR_ID 0x0C +#define CM3391_REG_ADDR_STATUS 0x0D /* Number of Configurable Registers */ -#define CM3391_CONF_REG_NUM 16 +#define CM3391_CONF_REG_NUM 16 /* CMD register */ -#define CM3391_CMD_CS_DISABLE BIT(0) -#define CM3391_CMD_CS_INT_EN BIT(8) +#define CM3391_CMD_CS_DISABLE BIT(0) +#define CM3391_CMD_CS_INT_EN BIT(8) + +#define CM3391_CMD_CS_PERS_SHIFT 10 +#define CM3391_CMD_CS_PERS_MASK (BIT(10) | BIT(11)) +#define CM3391_CMD_CS_PERS_DEFAULT (0x01 << CM3391_CMD_CS_PERS_SHIFT) -#define CM3391_CMD_CS_PERS_SHIFT 10 -#define CM3391_CMD_CS_PERS_MASK (0x03 << CM3391_CMD_CS_PERS_SHIFT) -#define CM3391_CMD_CS_PERS_DEFAULT (0x01 << CM3391_CMD_CS_PERS_SHIFT) +#define CM3391_CMD_CS_IT_SHIFT 4 +#define CM3391_CMD_CS_IT_MASK (BIT(4) | BIT(5) | BIT(6)) +#define CM3391_CMD_CS_IT_DEFAULT (0x01 << CM3391_CMD_CS_IT_SHIFT) -#define CM3391_CMD_CS_IT_SHIFT 4 -#define CM3391_CMD_CS_IT_MASK (0x07 << CM3391_CMD_CS_IT_SHIFT) -#define CM3391_CMD_CS_IT_DEFAULT (0x01 << CM3391_CMD_CS_IT_SHIFT) +#define CM3391_CMD_CS_HS_SHIFT BIT(3) -#define CM3391_CMD_CS_HS_SHIFT 3 -#define CM3391_CMD_CS_HS_MASK (0x01 << CM3391_CMD_CS_HS_SHIFT) -#define CM3391_CMD_CS_HS_DEFAULT (0x00 << CM3391_CMD_CS_HS_SHIFT) +#define CM3391_CMD_DEFAULT (CM3391_CMD_CS_PERS_DEFAULT |\ + CM3391_CMD_CS_IT_DEFAULT) -#define CM3391_CMD_DEFAULT (\ - CM3391_CMD_CS_PERS_DEFAULT |\ - CM3391_CMD_CS_IT_DEFAULT |\ - CM3391_CMD_CS_HS_DEFAULT) +#define CM3391_WH_DEFAULT 0xFFFF +#define CM3391_WL_DEFAULT 0x0000 -#define CM3391_WH_DEFAULT 0xFFFF -#define CM3391_WL_DEFAULT 0x0000 +#define CM3391_CALIBSCALE_DEFAULT 100000 +#define CM3391_CALIBSCALE_RESOLUTION 100000 +#define CM3391_MLUX_PER_LUX 1000 +#define CM3391_THRESHOLD_PERCENT 10 /* 10 percent */ -#define CM3391_CALIBSCALE_DEFAULT 100000 -#define CM3391_CALIBSCALE_RESOLUTION 100000 -#define CM3391_MLUX_PER_LUX 1000 -#define CM3391_THRESHOLD_PERCENT 10 /* 10 percent */ +#define CM3391_MLUX_PER_BIT_DEFAULT 550 +#define CM3391_MLUX_PER_BIT_BASE_IT 100000 + +static const struct { + int val; + int val2; + u8 it; +} cm3391_cs_it_scales[] = { + {0, 50000, 0}, /* 0.050000 */ + {0, 100000, 1}, /* 0.100000 */ + {0, 200000, 2}, /* 0.200000 */ + {0, 400000, 3}, /* 0.400000 */ + {0, 800000, 4}, /* 0.800000 */ +}; -#define CM3391_MLUX_PER_BIT_DEFAULT 550 -#define CM3391_MLUX_PER_BIT_BASE_IT 100000 static const int CM3391_cs_it_bits[] = { 0, 1, 2, 3, 4}; static const int CM3391_cs_it_values[] = { 50000, 100000, 200000, 400000, 800000}; @@ -111,7 +119,7 @@ struct cm3391_chip { static int cm3391_get_lux(struct cm3391_chip *chip); static int cm3391_threshold_update(struct cm3391_chip *chip, int percent); -static int cm3391_read_cs_it(struct cm3391_chip *chip, int *val2); +static int cm3391_read_cs_it(struct cm3391_chip *chip, int *val, int *val2); /** * cm3391_interrupt_config() - Enable/Disable CM3391 interrupt @@ -202,7 +210,7 @@ static int cm3391_acpi_get_cpm_info(struct i2c_client *client, char *obj_name, * cm3391_reg_init() - Initialize CM3391 registers * @chip: pointer of struct cm3391. * - * Initialize CM3391 color sensor register to default values. + * Initialize CM3391 color sensor registers to default values. * Return: 0 for success; otherwise for error code. */ @@ -217,7 +225,6 @@ static int cm3391_reg_init(struct cm3391_chip *chip) u64 cpm_elems[20]; #endif /* CONFIG_ACPI */ - /* Default device */ cs_info = chip->cs_info = &cm3391_cs_info_default; chip->conf_regs[CM3391_REG_ADDR_CMD] = CM3391_CMD_DEFAULT; @@ -237,14 +244,11 @@ static int cm3391_reg_init(struct cm3391_chip *chip) /* Identify device */ ret = i2c_smbus_read_word_data(client, CM3391_REG_ADDR_ID); if (ret < 0) - return ret; - + return ret; if ((ret & 0xFF) != 0x91) return -ENODEV; #ifdef CONFIG_ACPI -#error ACPI is enabled, strange! OLIO MFJ - if (ACPI_HANDLE(&client->dev)) { /* Load from ACPI */ cpm_elem_count = cm3391_acpi_get_cpm_info(client, "CPM0", @@ -281,34 +285,81 @@ static int cm3391_reg_init(struct cm3391_chip *chip) ret = i2c_smbus_write_word_data(client, i, chip->conf_regs[i]); if (ret < 0) - return ret; + return ret; } } return 0; } +void cm3391_autoit(struct cm3391_chip *chip, u16 als_raw) +{ + struct i2c_client *client = chip->client; + u16 cs_it, cmd; + int i; + int update; + s32 ret; + + cs_it = chip->conf_regs[CM3391_REG_ADDR_CMD]; + cs_it &= CM3391_CMD_CS_IT_MASK; + cs_it >>= CM3391_CMD_CS_IT_SHIFT; + for (i = 0; i < ARRAY_SIZE(cm3391_cs_it_scales); i++) + if (cs_it == cm3391_cs_it_scales[i].it) + break; + update = 0; + if (als_raw > 60000) { + if (i > 0) { + i--; + update = 1; + } + } + if (als_raw < 100) { + if (i < ARRAY_SIZE(cm3391_cs_it_scales) - 1) { + i++; + update = 1; + } + } + + if (update) { + cs_it = cm3391_cs_it_scales[i].it; + cs_it <<= CM3391_CMD_CS_IT_SHIFT; + + cmd = chip->conf_regs[CM3391_REG_ADDR_CMD]; + cmd &= ~CM3391_CMD_CS_IT_MASK; + cmd |= cs_it; + ret = i2c_smbus_write_word_data(client, + CM3391_REG_ADDR_CMD, cmd); + + if (ret < 0) + return; + + chip->conf_regs[CM3391_REG_ADDR_CMD] = cmd; + return; + } +} + /** - * cm3391_read_cs_it() - Get sensor integration time (ms) + * cm3391_read_cs_it() - Get sensor integration time * @chip: pointer of struct cm3391 - * @val2: pointer of int to load the cs_it value. + * #val: pointer of int to load the integration time(sec). + * @val2: pointer of int to load the integration time(microsecond). * - * Report the current integration time in milliseconds. + * Report the current integration time. * * Return: IIO_VAL_INT_PLUS_MICRO for success, otherwise -EINVAL. */ -static int cm3391_read_cs_it(struct cm3391_chip *chip, int *val2) +static int cm3391_read_cs_it(struct cm3391_chip *chip, int *val, int *val2) { - struct cm3391_cs_info *cs_info = chip->cs_info; u16 cs_it; int i; cs_it = chip->conf_regs[CM3391_REG_ADDR_CMD]; cs_it &= CM3391_CMD_CS_IT_MASK; cs_it >>= CM3391_CMD_CS_IT_SHIFT; - for (i = 0; i < cs_info->num_cs_it; i++) { - if (cs_it == cs_info->cs_it_bits[i]) { - *val2 = cs_info->cs_it_values[i]; + for (i = 0; i < ARRAY_SIZE(cm3391_cs_it_scales); i++) { + if (cs_it == cm3391_cs_it_scales[i].it) { + *val = cm3391_cs_it_scales[i].val; + *val2 = cm3391_cs_it_scales[i].val2; return IIO_VAL_INT_PLUS_MICRO; } } @@ -326,31 +377,33 @@ static int cm3391_read_cs_it(struct cm3391_chip *chip, int *val2) * * Return: i2c_smbus_write_word_data command return value. */ -static int cm3391_write_cs_it(struct cm3391_chip *chip, int val) +static int cm3391_write_cs_it(struct cm3391_chip *chip, int val, int val2) { struct i2c_client *client = chip->client; - struct cm3391_cs_info *cs_info = chip->cs_info; - u16 cs_it; - int ret, i; + u16 cs_it, cmd; + int i; + s32 ret; - for (i = 0; i < cs_info->num_cs_it; i++) - if (val <= cs_info->cs_it_values[i]) - break; - if (i >= cs_info->num_cs_it) - i = cs_info->num_cs_it - 1; + for (i = 0; i < ARRAY_SIZE(cm3391_cs_it_scales); i++) { + if (val == cm3391_cs_it_scales[i].val && + val2 == cm3391_cs_it_scales[i].val2) { - cs_it = cs_info->cs_it_bits[i]; - cs_it <<= CM3391_CMD_CS_IT_SHIFT; + cs_it = cm3391_cs_it_scales[i].it; + cs_it <<= CM3391_CMD_CS_IT_SHIFT; - mutex_lock(&chip->lock); - chip->conf_regs[CM3391_REG_ADDR_CMD] &= - ~CM3391_CMD_CS_IT_MASK; - chip->conf_regs[CM3391_REG_ADDR_CMD] |= - cs_it; - ret = i2c_smbus_write_word_data(client, CM3391_REG_ADDR_CMD, - chip->conf_regs[CM3391_REG_ADDR_CMD]); - mutex_unlock(&chip->lock); + cmd = chip->conf_regs[CM3391_REG_ADDR_CMD]; + cmd &= ~CM3391_CMD_CS_IT_MASK; + cmd |= cs_it; + ret = i2c_smbus_write_word_data(client, + CM3391_REG_ADDR_CMD, cmd); + if (ret < 0) + return ret; + + chip->conf_regs[CM3391_REG_ADDR_CMD] = cmd; + return 0; + } + } return ret; } #endif /* !OLDAPI */ @@ -369,34 +422,37 @@ static int cm3391_get_lux(struct cm3391_chip *chip) struct i2c_client *client = chip->client; struct cm3391_cs_info *cs_info = chip->cs_info; int ret; + int val, val2; int cs_it; - u64 tmp; + u64 lux; /* Calculate mlux per bit based on cs_it */ - ret = cm3391_read_cs_it(chip, &cs_it); + ret = cm3391_read_cs_it(chip, &val, &val2); if (ret < 0) return -EINVAL; - tmp = (__force u64)cs_info->mlux_per_bit; - tmp *= cs_info->mlux_per_bit_base_it; - tmp = div_u64(tmp, cs_it); + cs_it = val * 1000000 + val2; + lux = (__force u64)cs_info->mlux_per_bit; + lux *= cs_info->mlux_per_bit_base_it; + lux = div_u64(lux, cs_it); /* Get als_raw */ - if (!(chip->conf_regs[CM3391_REG_ADDR_CMD] & CM3391_CMD_CS_INT_EN)) - cs_info->als_raw = i2c_smbus_read_word_data( - client, - CM3391_REG_ADDR_ALS); - if (cs_info->als_raw < 0) - return cs_info->als_raw; + ret = i2c_smbus_read_word_data(client, CM3391_REG_ADDR_ALS); + if (ret < 0) { + dev_err(&client->dev, "Error reading reg_addr_als\n"); + return ret; + } + cs_info->als_raw = (u16)ret; + lux *= cs_info->als_raw; + lux *= cs_info->calibscale; + lux = div_u64(lux, CM3391_CALIBSCALE_RESOLUTION); + lux = div_u64(lux, CM3391_MLUX_PER_LUX); - tmp *= cs_info->als_raw; - tmp *= cs_info->calibscale; - tmp = div_u64(tmp, CM3391_CALIBSCALE_RESOLUTION); - tmp = div_u64(tmp, CM3391_MLUX_PER_LUX); + if (lux > 0xFFFF) + lux = 0xFFFF; - if (tmp > 0xFFFF) - tmp = 0xFFFF; + cm3391_autoit(chip, cs_info->als_raw); - return (int)tmp; + return (int)lux; } static int cm3391_read_raw(struct iio_dev *indio_dev, @@ -426,10 +482,9 @@ static int cm3391_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; #ifndef OLDAPI case IIO_CHAN_INFO_INT_TIME: - *val = 0; - ret = cm3391_read_cs_it(chip, val2); + ret = cm3391_read_cs_it(chip, val, val2); return ret; -#endif /* !OLDAPI */ +#endif /* OLDAPI */ } return -EINVAL; @@ -441,26 +496,22 @@ static int cm3391_write_raw(struct iio_dev *indio_dev, { struct cm3391_chip *chip = iio_priv(indio_dev); struct cm3391_cs_info *cs_info = chip->cs_info; -#ifndef OLDAPI - long ms; -#endif /* !OLDAPI */ switch (mask) { case IIO_CHAN_INFO_CALIBSCALE: cs_info->calibscale = val; - return val; + return 0; #ifndef OLDAPI case IIO_CHAN_INFO_INT_TIME: - ms = val * 1000000 + val2; - return cm3391_write_cs_it(chip, (int)ms); -#endif /* !OLDAPI */ + return cm3391_write_cs_it(chip, val, val2); +#endif /* OLDAPI */ } return -EINVAL; } /** - * cm3391_get_it_available() - Get available IT value + * cm3391_get_it_available() - Get available CS IT value * @dev: pointer of struct device. * @attr: pointer of struct device_attribute. * @buf: pointer of return string buffer. @@ -472,14 +523,12 @@ static int cm3391_write_raw(struct iio_dev *indio_dev, static ssize_t cm3391_get_it_available(struct device *dev, struct device_attribute *attr, char *buf) { - struct cm3391_chip *chip = iio_priv(dev_to_iio_dev(dev)); - struct cm3391_cs_info *cs_info = chip->cs_info; int i, len; - for (i = 0, len = 0; i < cs_info->num_cs_it; i++) + for (i = 0, len = 0; i < ARRAY_SIZE(cm3391_cs_it_scales); i++) len += scnprintf(buf + len, PAGE_SIZE - len, "%u.%06u ", - cs_info->cs_it_values[i]/1000000, - cs_info->cs_it_values[i]%1000000); + cm3391_cs_it_scales[i].val, + cm3391_cs_it_scales[i].val2); return len + scnprintf(buf + len, PAGE_SIZE - len, "\n"); } @@ -582,14 +631,13 @@ static const struct iio_chan_spec cm3391_channels[] = { .type = IIO_LIGHT, .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | - BIT(IIO_CHAN_INFO_CALIBSCALE) + BIT(IIO_CHAN_INFO_CALIBSCALE), }, CM3391_COLOR_CHANNEL(RED, CM3391_REG_ADDR_RED), CM3391_COLOR_CHANNEL(GREEN, CM3391_REG_ADDR_ALS), CM3391_COLOR_CHANNEL(BLUE, CM3391_REG_ADDR_BLUE), CM3391_COLOR_CHANNEL(CLEAR, CM3391_REG_ADDR_CLEAR), }; - #else #define CM3391_COLOR_CHANNEL(_color, _addr) { \ .type = IIO_INTENSITY, \ @@ -613,7 +661,7 @@ static const struct iio_chan_spec cm3391_channels[] = { CM3391_COLOR_CHANNEL(BLUE, CM3391_REG_ADDR_BLUE), CM3391_COLOR_CHANNEL(CLEAR, CM3391_REG_ADDR_CLEAR), }; -#endif /* !OLDAPI */ +#endif /* OLDAPI */ static IIO_DEVICE_ATTR(in_illuminance_integration_time_available, S_IRUGO, cm3391_get_it_available, NULL, 0); @@ -644,7 +692,7 @@ static int cm3391_probe(struct i2c_client *client, #ifdef OLDAPI indio_dev = iio_device_alloc(sizeof(*chip)); if (!indio_dev) { - dev_err(&client->dev, "iio_device_alloc fails\n"); + dev_err(&client->dev, "iio_device_alloc failed\n"); return -ENOMEM; } #else @@ -723,7 +771,6 @@ error_free_irq: #endif /* OLDAPI */ error_disable_int: cm3391_interrupt_config(chip, 0); - return ret; } |