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

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.org


Signed-off-by: default avatarJonathan Cameron <Jonathan.Cameron@huawei.com>
parent ddaf14da
Loading
Loading
Loading
Loading
+68 −28
Original line number Diff line number Diff line
@@ -16,11 +16,14 @@
#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);

	if (iio_buffer_enabled(indio_dev) && iio_trigger_using_own(indio_dev)) {
		switch (event) {
		case ACPI_ALS_NOTIFY_ILLUMINANCE:
		ret = acpi_als_read_value(als, ACPI_ALS_ILLUMINANCE, &val);
		if (ret < 0)
			goto out;
		*buffer++ = val;
			iio_trigger_poll_chained(als->trig);
			break;
		default:
			/* Unhandled event */
		dev_dbg(&device->dev, "Unhandled ACPI ALS event (%08x)!\n",
			dev_dbg(&device->dev,
				"Unhandled ACPI ALS event (%08x)!\n",
				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,
@@ -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;