[PATCH v6 3/4] iio: light: veml6031x00: add support for triggered buffers
Javier Carrasco <[email protected]>
| Newsgroups | org.kernel.vger.linux-iio,org.kernel.vger.linux-devicetree,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
Add triggered buffer functionality for the two channels the device provides (ALS and IR). Signed-off-by: Javier Carrasco <[email protected]> --- drivers/iio/light/Kconfig | 2 + drivers/iio/light/veml6031x00.c | 117 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 118 insertions(+), 1 deletion(-) diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index 985b776e71d6..397b3e0d4408 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -742,6 +742,8 @@ config VEML6031X00 tristate "VEML6031X00 ambient light sensor series" select REGMAP_I2C select IIO_GTS_HELPER + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER depends on I2C help Say Y here if you want to build a driver for the Vishay VEML6031X00 diff --git a/drivers/iio/light/veml6031x00.c b/drivers/iio/light/veml6031x00.c index 021bf726c60b..43a701f62aea 100644 --- a/drivers/iio/light/veml6031x00.c +++ b/drivers/iio/light/veml6031x00.c @@ -13,6 +13,7 @@ #include <linux/device.h> #include <linux/err.h> #include <linux/i2c.h> +#include <linux/interrupt.h> #include <linux/limits.h> #include <linux/module.h> #include <linux/mutex.h> @@ -25,6 +26,8 @@ #include <linux/iio/iio.h> #include <linux/iio/iio-gts-helper.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/iio/trigger_consumer.h> /* Device registers */ #define VEML6031X00_REG_CONF0 0x00 @@ -42,6 +45,12 @@ #define VEML6031X00_GAIN_SEL(pd_div4, gain) (((pd_div4) << 2) | (gain)) +enum veml6031x00_scan { + VEML6031X00_SCAN_ALS, + VEML6031X00_SCAN_IR, + VEML6031X00_SCAN_TIMESTAMP, +}; + struct veml6031x00_rf { struct regmap_field *gain; struct regmap_field *it; @@ -137,6 +146,13 @@ static const struct iio_chan_spec veml6031x00_channels[] = { BIT(IIO_CHAN_INFO_SCALE), .info_mask_separate_available = BIT(IIO_CHAN_INFO_INT_TIME) | BIT(IIO_CHAN_INFO_SCALE), + .scan_index = VEML6031X00_SCAN_ALS, + .scan_type = { + .format = IIO_SCAN_FORMAT_UNSIGNED_INT, + .realbits = 16, + .storagebits = 16, + .endianness = IIO_LE, + }, }, { .type = IIO_INTENSITY, @@ -144,7 +160,15 @@ static const struct iio_chan_spec veml6031x00_channels[] = { .modified = 1, .channel2 = IIO_MOD_LIGHT_IR, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), - } + .scan_index = VEML6031X00_SCAN_IR, + .scan_type = { + .format = IIO_SCAN_FORMAT_UNSIGNED_INT, + .realbits = 16, + .storagebits = 16, + .endianness = IIO_LE, + }, + }, + IIO_CHAN_SOFT_TIMESTAMP(VEML6031X00_SCAN_TIMESTAMP), }; static const struct regmap_range veml6031x00_readable_ranges[] = { @@ -388,6 +412,10 @@ static int veml6031x00_single_read(struct iio_dev *iio, enum iio_chan_type type, return -EINVAL; } + IIO_DEV_ACQUIRE_DIRECT_MODE(iio, claim); + if (IIO_DEV_ACQUIRE_FAILED(claim)) + return -EBUSY; + guard(mutex)(&data->scale_lock); PM_RUNTIME_ACQUIRE_AUTOSUSPEND(regmap_get_device(data->regmap), pm); @@ -451,6 +479,10 @@ static int veml6031x00_write_raw(struct iio_dev *iio, struct iio_chan_spec const *chan, int val, int val2, long mask) { + IIO_DEV_ACQUIRE_DIRECT_MODE(iio, claim); + if (IIO_DEV_ACQUIRE_FAILED(claim)) + return -EBUSY; + switch (mask) { case IIO_CHAN_INFO_INT_TIME: return veml6031x00_set_it(iio, val, val2); @@ -482,6 +514,82 @@ static const struct iio_info veml6031x00_info = { .write_raw_get_fmt = veml6031x00_write_raw_get_fmt, }; +static int veml6031x00_buffer_preenable(struct iio_dev *iio) +{ + struct veml6031x00_data *data = iio_priv(iio); + struct device *dev = regmap_get_device(data->regmap); + int ret, it_usec; + + ret = pm_runtime_resume_and_get(dev); + if (ret) + return ret; + + ret = veml6031x00_get_it(data, &it_usec); + if (ret < 0) { + pm_runtime_put_autosuspend(dev); + return ret; + } + + /* + * Wait one integration period + 10% margin so the first triggered + * read does not race with the sensor completing its first conversion + * after power-on. + */ + fsleep(it_usec + (it_usec / 10)); + + return 0; +} + +static int veml6031x00_buffer_postdisable(struct iio_dev *iio) +{ + struct veml6031x00_data *data = iio_priv(iio); + + pm_runtime_put_autosuspend(regmap_get_device(data->regmap)); + + return 0; +} + +static const struct iio_buffer_setup_ops veml6031x00_buffer_setup_ops = { + .preenable = veml6031x00_buffer_preenable, + .postdisable = veml6031x00_buffer_postdisable, +}; + +static irqreturn_t veml6031x00_trig_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *iio = pf->indio_dev; + struct veml6031x00_data *data = iio_priv(iio); + IIO_DECLARE_BUFFER_WITH_TS(__le16, scan, 2) = { }; + unsigned int i = 0; + int ch, ret; + + if (test_bit(VEML6031X00_SCAN_ALS, iio->active_scan_mask) && + test_bit(VEML6031X00_SCAN_IR, iio->active_scan_mask)) { + ret = regmap_bulk_read(data->regmap, + VEML6031X00_REG_ALS_L, + scan, + 2 * sizeof(*scan)); + if (ret) + goto done; + } else { + iio_for_each_active_channel(iio, ch) { + ret = regmap_bulk_read(data->regmap, + iio->channels[ch].address, + &scan[i++], + sizeof(*scan)); + if (ret) + goto done; + } + } + + iio_push_to_buffers_with_ts(iio, scan, sizeof(scan), pf->timestamp); + +done: + iio_trigger_notify_done(iio->trig); + + return IRQ_HANDLED; +} + static int veml6031x00_validate_part_id(struct veml6031x00_data *data) { struct device *dev = regmap_get_device(data->regmap); @@ -587,6 +695,13 @@ static int veml6031x00_probe(struct i2c_client *i2c) pm_runtime_put_autosuspend(dev); + ret = devm_iio_triggered_buffer_setup(dev, iio, + iio_pollfunc_store_time, + veml6031x00_trig_handler, + &veml6031x00_buffer_setup_ops); + if (ret) + return dev_err_probe(dev, ret, "Failed to register triggered buffer\n"); + ret = devm_iio_device_register(dev, iio); if (ret) return dev_err_probe(dev, ret, "Failed to register iio device\n"); -- 2.43.0