[PATCH v5 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 | 100 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+) 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 30cccda51ca0..0cccd40d729e 100644 --- a/drivers/iio/light/veml6031x00.c +++ b/drivers/iio/light/veml6031x00.c @@ -11,6 +11,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/mod_devicetable.h> @@ -22,6 +23,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 @@ -37,6 +40,12 @@ #define VEML6031X00_CONF0_SD BIT(0) #define VEML6031X00_CONF1_IR_SD BIT(7) +enum veml6031x00_scan { + VEML6031X00_SCAN_ALS, + VEML6031X00_SCAN_IR, + VEML6031X00_SCAN_TIMESTAMP, +}; + struct veml6031x00_rf { struct regmap_field *gain; struct regmap_field *it; @@ -392,6 +401,10 @@ static int veml6031x00_single_read(struct iio_dev *iio, enum iio_chan_type type, guard(mutex)(&data->scale_lock); + IIO_DEV_ACQUIRE_DIRECT_MODE(iio, claim); + if (IIO_DEV_ACQUIRE_FAILED(claim)) + return -EBUSY; + PM_RUNTIME_ACQUIRE_AUTOSUSPEND(regmap_get_device(data->regmap), pm); ret = PM_RUNTIME_ACQUIRE_ERR(&pm); if (ret) @@ -453,6 +466,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); @@ -484,6 +501,81 @@ 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); + int ret, it_usec; + + ret = pm_runtime_resume_and_get(regmap_get_device(data->regmap)); + if (ret) + return ret; + + ret = veml6031x00_get_it(data, &it_usec); + if (ret < 0) { + pm_runtime_put_autosuspend(regmap_get_device(data->regmap)); + 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) { int part_id, ret; @@ -588,6 +680,14 @@ static int veml6031x00_probe(struct i2c_client *i2c) if (ret) return ret; + ret = devm_iio_triggered_buffer_setup(regmap_get_device(data->regmap), iio, + iio_pollfunc_store_time, + veml6031x00_trig_handler, + &veml6031x00_buffer_setup_ops); + if (ret) + return dev_err_probe(&i2c->dev, ret, + "Failed to register triggered buffer\n"); + pm_runtime_put_autosuspend(&i2c->dev); ret = devm_iio_device_register(&i2c->dev, iio); -- 2.43.0