iio: acpi_als: Add trigger support
authorGwendal Grignou <gwendal@chromium.org>
Wed, 17 Mar 2021 07:40:12 +0000 (00:40 -0700)
committerJonathan Cameron <Jonathan.Cameron@huawei.com>
Thu, 25 Mar 2021 19:13:52 +0000 (19:13 +0000)
As some firmware does not notify on illuminance changes, add a
trigger to be able to query light via software (sysfs-trigger or
hrtrigger).
Add a hardware trigger set as the default trigger to maintain backward
compatibility.

Check iio_info reports the sensor as buffer capable:
  iio:device0: acpi-als (buffer capable)

To test, check we can get data on demand on an Intel based chromebook:

  IIO_DEV="iio:device0"
  echo 1 > iio_sysfs_trigger/add_trigger
  cat trigger2/name > ${IIO_DEV}/trigger/current_trigger
  for i in ${IIO_DEV}/scan_elements/*_en ${IIO_DEV}/buffer/enable ; do
    echo 1 > $i
  done
  od -x /dev/${IIO_DEV} &
  echo 1 > trigger2/trigger_now

Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
Link: https://lore.kernel.org/r/20210317074012.2336454-4-gwendal@chromium.org
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
drivers/iio/light/acpi-als.c

index a210794..30393f0 100644 (file)
 #include <linux/module.h>
 #include <linux/acpi.h>
 #include <linux/err.h>
+#include <linux/irq.h>
 #include <linux/mutex.h>
 
 #include <linux/iio/iio.h>
 #include <linux/iio/buffer.h>
-#include <linux/iio/kfifo_buf.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
 
 #define ACPI_ALS_CLASS                 "als"
 #define ACPI_ALS_DEVICE_NAME           "acpi-als"
@@ -59,6 +62,7 @@ static const struct iio_chan_spec acpi_als_channels[] = {
 struct acpi_als {
        struct acpi_device      *device;
        struct mutex            lock;
+       struct iio_trigger      *trig;
 
        s32 evt_buffer[ACPI_ALS_EVT_BUFFER_SIZE / sizeof(s32)]  __aligned(8);
 };
@@ -102,33 +106,19 @@ static void acpi_als_notify(struct acpi_device *device, u32 event)
 {
        struct iio_dev *indio_dev = acpi_driver_data(device);
        struct acpi_als *als = iio_priv(indio_dev);
-       s32 *buffer = als->evt_buffer;
-       s64 time_ns = iio_get_time_ns(indio_dev);
-       s32 val;
-       int ret;
-
-       mutex_lock(&als->lock);
 
-       memset(buffer, 0, ACPI_ALS_EVT_BUFFER_SIZE);
-
-       switch (event) {
-       case ACPI_ALS_NOTIFY_ILLUMINANCE:
-               ret = acpi_als_read_value(als, ACPI_ALS_ILLUMINANCE, &val);
-               if (ret < 0)
-                       goto out;
-               *buffer++ = val;
-               break;
-       default:
-               /* Unhandled event */
-               dev_dbg(&device->dev, "Unhandled ACPI ALS event (%08x)!\n",
-                       event);
-               goto out;
+       if (iio_buffer_enabled(indio_dev) && iio_trigger_using_own(indio_dev)) {
+               switch (event) {
+               case ACPI_ALS_NOTIFY_ILLUMINANCE:
+                       iio_trigger_poll_chained(als->trig);
+                       break;
+               default:
+                       /* Unhandled event */
+                       dev_dbg(&device->dev,
+                               "Unhandled ACPI ALS event (%08x)!\n",
+                               event);
+               }
        }
-
-       iio_push_to_buffers_with_timestamp(indio_dev, als->evt_buffer, time_ns);
-
-out:
-       mutex_unlock(&als->lock);
 }
 
 static int acpi_als_read_raw(struct iio_dev *indio_dev,
@@ -159,6 +149,41 @@ static const struct iio_info acpi_als_info = {
        .read_raw               = acpi_als_read_raw,
 };
 
+static irqreturn_t acpi_als_trigger_handler(int irq, void *p)
+{
+       struct iio_poll_func *pf = p;
+       struct iio_dev *indio_dev = pf->indio_dev;
+       struct acpi_als *als = iio_priv(indio_dev);
+       s32 *buffer = als->evt_buffer;
+       s32 val;
+       int ret;
+
+       mutex_lock(&als->lock);
+
+       ret = acpi_als_read_value(als, ACPI_ALS_ILLUMINANCE, &val);
+       if (ret < 0)
+               goto out;
+       *buffer = val;
+
+       /*
+        * When coming from own trigger via polls, set polling function
+        * timestamp here. Given ACPI notifier is already in a thread and call
+        * function directly, there is no need to set the timestamp in the
+        * notify function.
+        *
+        * If the timestamp was actually 0, the timestamp is set one more time.
+        */
+       if (!pf->timestamp)
+               pf->timestamp = iio_get_time_ns(indio_dev);
+
+       iio_push_to_buffers_with_timestamp(indio_dev, buffer, pf->timestamp);
+out:
+       mutex_unlock(&als->lock);
+       iio_trigger_notify_done(indio_dev->trig);
+
+       return IRQ_HANDLED;
+}
+
 static int acpi_als_add(struct acpi_device *device)
 {
        struct device *dev = &device->dev;
@@ -181,8 +206,23 @@ static int acpi_als_add(struct acpi_device *device)
        indio_dev->channels = acpi_als_channels;
        indio_dev->num_channels = ARRAY_SIZE(acpi_als_channels);
 
-       ret = devm_iio_kfifo_buffer_setup(dev, indio_dev,
-                                         INDIO_BUFFER_SOFTWARE, NULL);
+       als->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name, indio_dev->id);
+       if (!als->trig)
+               return -ENOMEM;
+
+       ret = devm_iio_trigger_register(dev, als->trig);
+       if (ret)
+               return ret;
+       /*
+        * Set hardware trigger by default to let events flow when
+        * BIOS support notification.
+        */
+       indio_dev->trig = iio_trigger_get(als->trig);
+
+       ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
+                                             iio_pollfunc_store_time,
+                                             acpi_als_trigger_handler,
+                                             NULL);
        if (ret)
                return ret;