Commit 24b84444 authored by Gwendal Grignou's avatar Gwendal Grignou Committed by Jonathan Cameron

iio: acpi_als: Add trigger support

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: default avatarGwendal Grignou <gwendal@chromium.org>
Reviewed-by: default avatarAndy Shevchenko <andy.shevchenko@gmail.com>
Link: https://lore.kernel.org/r/20210317074012.2336454-4-gwendal@chromium.orgSigned-off-by: default avatarJonathan Cameron <Jonathan.Cameron@huawei.com>
parent ddaf14da
...@@ -16,11 +16,14 @@ ...@@ -16,11 +16,14 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/irq.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/iio/iio.h> #include <linux/iio/iio.h>
#include <linux/iio/buffer.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_CLASS "als"
#define ACPI_ALS_DEVICE_NAME "acpi-als" #define ACPI_ALS_DEVICE_NAME "acpi-als"
...@@ -59,6 +62,7 @@ static const struct iio_chan_spec acpi_als_channels[] = { ...@@ -59,6 +62,7 @@ static const struct iio_chan_spec acpi_als_channels[] = {
struct acpi_als { struct acpi_als {
struct acpi_device *device; struct acpi_device *device;
struct mutex lock; struct mutex lock;
struct iio_trigger *trig;
s32 evt_buffer[ACPI_ALS_EVT_BUFFER_SIZE / sizeof(s32)] __aligned(8); 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) ...@@ -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 iio_dev *indio_dev = acpi_driver_data(device);
struct acpi_als *als = iio_priv(indio_dev); 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);
if (iio_buffer_enabled(indio_dev) && iio_trigger_using_own(indio_dev)) {
switch (event) { switch (event) {
case ACPI_ALS_NOTIFY_ILLUMINANCE: case ACPI_ALS_NOTIFY_ILLUMINANCE:
ret = acpi_als_read_value(als, ACPI_ALS_ILLUMINANCE, &val); iio_trigger_poll_chained(als->trig);
if (ret < 0)
goto out;
*buffer++ = val;
break; break;
default: default:
/* Unhandled event */ /* Unhandled event */
dev_dbg(&device->dev, "Unhandled ACPI ALS event (%08x)!\n", dev_dbg(&device->dev,
"Unhandled ACPI ALS event (%08x)!\n",
event); event);
goto out;
} }
}
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, static int acpi_als_read_raw(struct iio_dev *indio_dev,
...@@ -159,6 +149,41 @@ static const struct iio_info acpi_als_info = { ...@@ -159,6 +149,41 @@ static const struct iio_info acpi_als_info = {
.read_raw = acpi_als_read_raw, .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) static int acpi_als_add(struct acpi_device *device)
{ {
struct device *dev = &device->dev; struct device *dev = &device->dev;
...@@ -181,8 +206,23 @@ static int acpi_als_add(struct acpi_device *device) ...@@ -181,8 +206,23 @@ static int acpi_als_add(struct acpi_device *device)
indio_dev->channels = acpi_als_channels; indio_dev->channels = acpi_als_channels;
indio_dev->num_channels = ARRAY_SIZE(acpi_als_channels); indio_dev->num_channels = ARRAY_SIZE(acpi_als_channels);
ret = devm_iio_kfifo_buffer_setup(dev, indio_dev, als->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name, indio_dev->id);
INDIO_BUFFER_SOFTWARE, NULL); 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) if (ret)
return ret; return ret;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment