diff options
Diffstat (limited to 'drivers/iio/light/acpi-als.c')
| -rw-r--r-- | drivers/iio/light/acpi-als.c | 121 | 
1 files changed, 78 insertions, 43 deletions
| diff --git a/drivers/iio/light/acpi-als.c b/drivers/iio/light/acpi-als.c index 2be7180e2cbf..0a6ab5761eec 100644 --- a/drivers/iio/light/acpi-als.c +++ b/drivers/iio/light/acpi-als.c @@ -16,18 +16,19 @@  #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"  #define ACPI_ALS_NOTIFY_ILLUMINANCE	0x80 -ACPI_MODULE_NAME("acpi-als"); -  /*   * So far, there's only one channel in here, but the specification for   * ACPI0008 says there can be more to what the block can report. Like @@ -45,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);  };  /* @@ -91,7 +91,7 @@ static int acpi_als_read_value(struct acpi_als *als, char *prop, s32 *val)  				       &temp_val);  	if (ACPI_FAILURE(status)) { -		ACPI_EXCEPTION((AE_INFO, status, "Error reading ALS %s", prop)); +		acpi_evaluation_failure_warn(als->device->handle, prop, status);  		return -EIO;  	} @@ -104,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, @@ -161,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; @@ -179,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[] = { | 
