Merge tag 'acpi-5.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael...
[linux-2.6-microblaze.git] / drivers / iio / light / acpi-als.c
index 5ad45f1..0a6ab57 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"
@@ -43,24 +46,23 @@ static const struct iio_chan_spec acpi_als_channels[] = {
                .info_mask_separate     = BIT(IIO_CHAN_INFO_RAW) |
                                          BIT(IIO_CHAN_INFO_PROCESSED),
        },
+       IIO_CHAN_SOFT_TIMESTAMP(1),
 };
 
 /*
  * The event buffer contains timestamp and all the data from
  * the ACPI0008 block. There are multiple, but so far we only
- * support _ALI (illuminance). Once someone adds new channels
- * to acpi_als_channels[], the evt_buffer below will grow
- * automatically.
+ * support _ALI (illuminance): One channel, padding and timestamp.
  */
-#define ACPI_ALS_EVT_NR_SOURCES                ARRAY_SIZE(acpi_als_channels)
 #define ACPI_ALS_EVT_BUFFER_SIZE               \
-       (sizeof(s64) + (ACPI_ALS_EVT_NR_SOURCES * sizeof(s32)))
+       (sizeof(s32) + sizeof(s32) + sizeof(s64))
 
 struct acpi_als {
        struct acpi_device      *device;
        struct mutex            lock;
+       struct iio_trigger      *trig;
 
-       s32                     evt_buffer[ACPI_ALS_EVT_BUFFER_SIZE];
+       s32 evt_buffer[ACPI_ALS_EVT_BUFFER_SIZE / sizeof(s32)]  __aligned(8);
 };
 
 /*
@@ -102,33 +104,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,13 +147,49 @@ 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 acpi_als *als;
+       struct device *dev = &device->dev;
        struct iio_dev *indio_dev;
-       struct iio_buffer *buffer;
+       struct acpi_als *als;
+       int ret;
 
-       indio_dev = devm_iio_device_alloc(&device->dev, sizeof(*als));
+       indio_dev = devm_iio_device_alloc(dev, sizeof(*als));
        if (!indio_dev)
                return -ENOMEM;
 
@@ -177,17 +201,30 @@ static int acpi_als_add(struct acpi_device *device)
 
        indio_dev->name = ACPI_ALS_DEVICE_NAME;
        indio_dev->info = &acpi_als_info;
-       indio_dev->modes = INDIO_BUFFER_SOFTWARE;
        indio_dev->channels = acpi_als_channels;
        indio_dev->num_channels = ARRAY_SIZE(acpi_als_channels);
 
-       buffer = devm_iio_kfifo_allocate(&device->dev);
-       if (!buffer)
+       als->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name, indio_dev->id);
+       if (!als->trig)
                return -ENOMEM;
 
-       iio_device_attach_buffer(indio_dev, buffer);
+       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;
 
-       return devm_iio_device_register(&device->dev, indio_dev);
+       return devm_iio_device_register(dev, indio_dev);
 }
 
 static const struct acpi_device_id acpi_als_device_ids[] = {