iio: vcnl4000: Add sampling frequency support for VCNL4010/20.
authorMathieu Othacehe <m.othacehe@gmail.com>
Sun, 3 May 2020 09:29:58 +0000 (11:29 +0200)
committerJonathan Cameron <Jonathan.Cameron@huawei.com>
Sun, 3 May 2020 13:53:03 +0000 (14:53 +0100)
Add sampling frequency support for proximity data on VCNL4010 and VCNL4020
chips.

Signed-off-by: Mathieu Othacehe <m.othacehe@gmail.com>
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
drivers/iio/light/vcnl4000.c

index b8610dd..f6198c6 100644 (file)
 #define VCNL4010_INT_DRDY \
        (BIT(VCNL4010_INT_PROXIMITY) | BIT(VCNL4010_INT_ALS))
 
+static const int vcnl4010_prox_sampling_frequency[][2] = {
+       {1, 950000},
+       {3, 906250},
+       {7, 812500},
+       {16, 625000},
+       {31, 250000},
+       {62, 500000},
+       {125, 0},
+       {250, 0},
+};
 
 #define VCNL4000_SLEEP_DELAY_MS        2000 /* before we enter pm_runtime_suspend */
 
@@ -364,6 +374,24 @@ static int vcnl4200_measure_proximity(struct vcnl4000_data *data, int *val)
        return vcnl4200_measure(data, &data->vcnl4200_ps, val);
 }
 
+static int vcnl4010_read_proxy_samp_freq(struct vcnl4000_data *data, int *val,
+                                        int *val2)
+{
+       int ret;
+
+       ret = i2c_smbus_read_byte_data(data->client, VCNL4010_PROX_RATE);
+       if (ret < 0)
+               return ret;
+
+       if (ret >= ARRAY_SIZE(vcnl4010_prox_sampling_frequency))
+               return -EINVAL;
+
+       *val = vcnl4010_prox_sampling_frequency[ret][0];
+       *val2 = vcnl4010_prox_sampling_frequency[ret][1];
+
+       return 0;
+}
+
 static bool vcnl4010_is_in_periodic_mode(struct vcnl4000_data *data)
 {
        int ret;
@@ -457,11 +485,95 @@ static int vcnl4010_read_raw(struct iio_dev *indio_dev,
 
                iio_device_release_direct_mode(indio_dev);
                return ret;
+       case IIO_CHAN_INFO_SAMP_FREQ:
+               switch (chan->type) {
+               case IIO_PROXIMITY:
+                       ret = vcnl4010_read_proxy_samp_freq(data, val, val2);
+                       if (ret < 0)
+                               return ret;
+                       return IIO_VAL_INT_PLUS_MICRO;
+               default:
+                       return -EINVAL;
+               }
        default:
                return -EINVAL;
        }
 }
 
+static int vcnl4010_read_avail(struct iio_dev *indio_dev,
+                              struct iio_chan_spec const *chan,
+                              const int **vals, int *type, int *length,
+                              long mask)
+{
+       switch (mask) {
+       case IIO_CHAN_INFO_SAMP_FREQ:
+               *vals = (int *)vcnl4010_prox_sampling_frequency;
+               *type = IIO_VAL_INT_PLUS_MICRO;
+               *length = 2 * ARRAY_SIZE(vcnl4010_prox_sampling_frequency);
+               return IIO_AVAIL_LIST;
+       default:
+               return -EINVAL;
+       }
+}
+
+static int vcnl4010_write_proxy_samp_freq(struct vcnl4000_data *data, int val,
+                                         int val2)
+{
+       unsigned int i;
+       int index = -1;
+
+       for (i = 0; i < ARRAY_SIZE(vcnl4010_prox_sampling_frequency); i++) {
+               if (val == vcnl4010_prox_sampling_frequency[i][0] &&
+                   val2 == vcnl4010_prox_sampling_frequency[i][1]) {
+                       index = i;
+                       break;
+               }
+       }
+
+       if (index < 0)
+               return -EINVAL;
+
+       return i2c_smbus_write_byte_data(data->client, VCNL4010_PROX_RATE,
+                                        index);
+}
+
+static int vcnl4010_write_raw(struct iio_dev *indio_dev,
+                             struct iio_chan_spec const *chan,
+                             int val, int val2, long mask)
+{
+       int ret;
+       struct vcnl4000_data *data = iio_priv(indio_dev);
+
+       ret = iio_device_claim_direct_mode(indio_dev);
+       if (ret)
+               return ret;
+
+       /* Protect against event capture. */
+       if (vcnl4010_is_in_periodic_mode(data)) {
+               ret = -EBUSY;
+               goto end;
+       }
+
+       switch (mask) {
+       case IIO_CHAN_INFO_SAMP_FREQ:
+               switch (chan->type) {
+               case IIO_PROXIMITY:
+                       ret = vcnl4010_write_proxy_samp_freq(data, val, val2);
+                       goto end;
+               default:
+                       ret = -EINVAL;
+                       goto end;
+               }
+       default:
+               ret = -EINVAL;
+               goto end;
+       }
+
+end:
+       iio_device_release_direct_mode(indio_dev);
+       return ret;
+}
+
 static int vcnl4010_read_event(struct iio_dev *indio_dev,
                               const struct iio_chan_spec *chan,
                               enum iio_event_type type,
@@ -664,7 +776,9 @@ static const struct iio_chan_spec vcnl4010_channels[] = {
                        BIT(IIO_CHAN_INFO_SCALE),
        }, {
                .type = IIO_PROXIMITY,
-               .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+               .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+                       BIT(IIO_CHAN_INFO_SAMP_FREQ),
+               .info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),
                .event_spec = vcnl4000_event_spec,
                .num_event_specs = ARRAY_SIZE(vcnl4000_event_spec),
                .ext_info = vcnl4000_ext_info,
@@ -677,6 +791,8 @@ static const struct iio_info vcnl4000_info = {
 
 static const struct iio_info vcnl4010_info = {
        .read_raw = vcnl4010_read_raw,
+       .read_avail = vcnl4010_read_avail,
+       .write_raw = vcnl4010_write_raw,
        .read_event_value = vcnl4010_read_event,
        .write_event_value = vcnl4010_write_event,
        .read_event_config = vcnl4010_read_event_config,