diff options
author | Astrid Rost <astrid.rost@axis.com> | 2023-06-13 15:50:24 +0200 |
---|---|---|
committer | Jonathan Cameron <Jonathan.Cameron@huawei.com> | 2023-07-20 20:21:29 +0200 |
commit | bb33e75149886ec13cc5c54951eb3a92d1a99b42 (patch) | |
tree | d2afe341c425ec871639e52ccb7b681fd0bf72dc /drivers/iio/light | |
parent | iio: light: vcnl4000: Add oversampling_ratio for 4040/4200 (diff) | |
download | linux-bb33e75149886ec13cc5c54951eb3a92d1a99b42.tar.xz linux-bb33e75149886ec13cc5c54951eb3a92d1a99b42.zip |
iio: light: vcnl4000: Add calibration bias for 4040/4200
The calibration bias is setting the LED current to change the detection
distance. Add read/write attribute for proximity calibration bias and
read attribute for available values. This is supported for vcnl4040 and
vcnl4200.
Signed-off-by: Astrid Rost <astrid.rost@axis.com>
Link: https://lore.kernel.org/r/20230613135025.2596641-9-astrid.rost@axis.com
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Diffstat (limited to 'drivers/iio/light')
-rw-r--r-- | drivers/iio/light/vcnl4000.c | 93 |
1 files changed, 91 insertions, 2 deletions
diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c index 17470a9234e9..3a52b09c2823 100644 --- a/drivers/iio/light/vcnl4000.c +++ b/drivers/iio/light/vcnl4000.c @@ -92,6 +92,7 @@ #define VCNL4040_CONF1_PS_PERS GENMASK(5, 4) /* Proximity interrupt persistence setting */ #define VCNL4040_PS_CONF2_PS_INT GENMASK(9, 8) /* Proximity interrupt mode */ #define VCNL4040_PS_CONF3_MPS GENMASK(6, 5) /* Proximity multi pulse number */ +#define VCNL4040_PS_MS_LED_I GENMASK(10, 8) /* Proximity current */ #define VCNL4040_PS_IF_AWAY BIT(8) /* Proximity event cross low threshold */ #define VCNL4040_PS_IF_CLOSE BIT(9) /* Proximity event cross high threshold */ #define VCNL4040_ALS_RISING BIT(12) /* Ambient Light cross high threshold */ @@ -158,6 +159,17 @@ static const int vcnl4200_als_it_times[][2] = { {0, 400000}, }; +static const int vcnl4040_ps_calibbias_ua[][2] = { + {0, 50000}, + {0, 75000}, + {0, 100000}, + {0, 120000}, + {0, 140000}, + {0, 160000}, + {0, 180000}, + {0, 200000}, +}; + static const int vcnl4040_als_persistence[] = {1, 2, 4, 8}; static const int vcnl4040_ps_persistence[] = {1, 2, 3, 4}; static const int vcnl4040_ps_oversampling_ratio[] = {1, 2, 4, 8}; @@ -820,6 +832,54 @@ out_unlock: return ret; } +static ssize_t vcnl4040_read_ps_calibbias(struct vcnl4000_data *data, int *val, int *val2) +{ + int ret; + + ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF3); + if (ret < 0) + return ret; + + ret = FIELD_GET(VCNL4040_PS_MS_LED_I, ret); + if (ret >= ARRAY_SIZE(vcnl4040_ps_calibbias_ua)) + return -EINVAL; + + *val = vcnl4040_ps_calibbias_ua[ret][0]; + *val2 = vcnl4040_ps_calibbias_ua[ret][1]; + + return ret; +} + +static ssize_t vcnl4040_write_ps_calibbias(struct vcnl4000_data *data, int val) +{ + unsigned int i; + int ret; + u16 regval; + + for (i = 0; i < ARRAY_SIZE(vcnl4040_ps_calibbias_ua); i++) { + if (val == vcnl4040_ps_calibbias_ua[i][1]) + break; + } + + if (i >= ARRAY_SIZE(vcnl4040_ps_calibbias_ua)) + return -EINVAL; + + mutex_lock(&data->vcnl4000_lock); + + ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF3); + if (ret < 0) + goto out_unlock; + + regval = (ret & ~VCNL4040_PS_MS_LED_I); + regval |= FIELD_PREP(VCNL4040_PS_MS_LED_I, i); + ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF3, + regval); + +out_unlock: + mutex_unlock(&data->vcnl4000_lock); + return ret; +} + static int vcnl4000_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) @@ -880,6 +940,16 @@ static int vcnl4000_read_raw(struct iio_dev *indio_dev, default: return -EINVAL; } + case IIO_CHAN_INFO_CALIBBIAS: + switch (chan->type) { + case IIO_PROXIMITY: + ret = vcnl4040_read_ps_calibbias(data, val, val2); + if (ret < 0) + return ret; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } default: return -EINVAL; } @@ -910,6 +980,13 @@ static int vcnl4040_write_raw(struct iio_dev *indio_dev, default: return -EINVAL; } + case IIO_CHAN_INFO_CALIBBIAS: + switch (chan->type) { + case IIO_PROXIMITY: + return vcnl4040_write_ps_calibbias(data, val2); + default: + return -EINVAL; + } default: return -EINVAL; } @@ -948,6 +1025,16 @@ static int vcnl4040_read_avail(struct iio_dev *indio_dev, default: return -EINVAL; } + case IIO_CHAN_INFO_CALIBBIAS: + switch (chan->type) { + case IIO_PROXIMITY: + *vals = (int *)vcnl4040_ps_calibbias_ua; + *length = 2 * ARRAY_SIZE(vcnl4040_ps_calibbias_ua); + *type = IIO_VAL_INT_PLUS_MICRO; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } default: return -EINVAL; } @@ -1721,9 +1808,11 @@ static const struct iio_chan_spec vcnl4040_channels[] = { .type = IIO_PROXIMITY, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_INT_TIME) | - BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) | + BIT(IIO_CHAN_INFO_CALIBBIAS), .info_mask_separate_available = BIT(IIO_CHAN_INFO_INT_TIME) | - BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) | + BIT(IIO_CHAN_INFO_CALIBBIAS), .ext_info = vcnl4000_ext_info, .event_spec = vcnl4040_event_spec, .num_event_specs = ARRAY_SIZE(vcnl4040_event_spec), |