linux/drivers/iio/light/acpi-als.c
<<
>>
Prefs
   1/*
   2 * ACPI Ambient Light Sensor Driver
   3 *
   4 * Based on ALS driver:
   5 * Copyright (C) 2009 Zhang Rui <rui.zhang@intel.com>
   6 *
   7 * Rework for IIO subsystem:
   8 * Copyright (C) 2012-2013 Martin Liska <marxin.liska@gmail.com>
   9 *
  10 * Final cleanup and debugging:
  11 * Copyright (C) 2013-2014 Marek Vasut <marex@denx.de>
  12 * Copyright (C) 2015 Gabriele Mazzotta <gabriele.mzt@gmail.com>
  13 *
  14 * This program is free software; you can redistribute it and/or modify it
  15 * under the terms of the GNU General Public License as published by the
  16 * Free Software Foundation; either version 2 of the License, or (at your
  17 * option) any later version.
  18 *
  19 * This program is distributed in the hope that it will be useful, but
  20 * WITHOUT ANY WARRANTY; without even the implied warranty of
  21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  22 * General Public License for more details.
  23 *
  24 * You should have received a copy of the GNU General Public License along
  25 * with this program; if not, write to the Free Software Foundation, Inc.,
  26 * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  27 */
  28
  29#include <linux/module.h>
  30#include <linux/acpi.h>
  31#include <linux/err.h>
  32#include <linux/mutex.h>
  33
  34#include <linux/iio/iio.h>
  35#include <linux/iio/buffer.h>
  36#include <linux/iio/kfifo_buf.h>
  37
  38#define ACPI_ALS_CLASS                  "als"
  39#define ACPI_ALS_DEVICE_NAME            "acpi-als"
  40#define ACPI_ALS_NOTIFY_ILLUMINANCE     0x80
  41
  42ACPI_MODULE_NAME("acpi-als");
  43
  44/*
  45 * So far, there's only one channel in here, but the specification for
  46 * ACPI0008 says there can be more to what the block can report. Like
  47 * chromaticity and such. We are ready for incoming additions!
  48 */
  49static const struct iio_chan_spec acpi_als_channels[] = {
  50        {
  51                .type           = IIO_LIGHT,
  52                .scan_type      = {
  53                        .sign           = 's',
  54                        .realbits       = 32,
  55                        .storagebits    = 32,
  56                },
  57                .info_mask_separate     = BIT(IIO_CHAN_INFO_RAW),
  58        },
  59};
  60
  61/*
  62 * The event buffer contains timestamp and all the data from
  63 * the ACPI0008 block. There are multiple, but so far we only
  64 * support _ALI (illuminance). Once someone adds new channels
  65 * to acpi_als_channels[], the evt_buffer below will grow
  66 * automatically.
  67 */
  68#define ACPI_ALS_EVT_NR_SOURCES         ARRAY_SIZE(acpi_als_channels)
  69#define ACPI_ALS_EVT_BUFFER_SIZE                \
  70        (sizeof(s64) + (ACPI_ALS_EVT_NR_SOURCES * sizeof(s32)))
  71
  72struct acpi_als {
  73        struct acpi_device      *device;
  74        struct mutex            lock;
  75
  76        s32                     evt_buffer[ACPI_ALS_EVT_BUFFER_SIZE];
  77};
  78
  79/*
  80 * All types of properties the ACPI0008 block can report. The ALI, ALC, ALT
  81 * and ALP can all be handled by acpi_als_read_value() below, while the ALR is
  82 * special.
  83 *
  84 * The _ALR property returns tables that can be used to fine-tune the values
  85 * reported by the other props based on the particular hardware type and it's
  86 * location (it contains tables for "rainy", "bright inhouse lighting" etc.).
  87 *
  88 * So far, we support only ALI (illuminance).
  89 */
  90#define ACPI_ALS_ILLUMINANCE    "_ALI"
  91#define ACPI_ALS_CHROMATICITY   "_ALC"
  92#define ACPI_ALS_COLOR_TEMP     "_ALT"
  93#define ACPI_ALS_POLLING        "_ALP"
  94#define ACPI_ALS_TABLES         "_ALR"
  95
  96static int acpi_als_read_value(struct acpi_als *als, char *prop, s32 *val)
  97{
  98        unsigned long long temp_val;
  99        acpi_status status;
 100
 101        status = acpi_evaluate_integer(als->device->handle, prop, NULL,
 102                                       &temp_val);
 103
 104        if (ACPI_FAILURE(status)) {
 105                ACPI_EXCEPTION((AE_INFO, status, "Error reading ALS %s", prop));
 106                return -EIO;
 107        }
 108
 109        *val = temp_val;
 110
 111        return 0;
 112}
 113
 114static void acpi_als_notify(struct acpi_device *device, u32 event)
 115{
 116        struct iio_dev *indio_dev = acpi_driver_data(device);
 117        struct acpi_als *als = iio_priv(indio_dev);
 118        s32 *buffer = als->evt_buffer;
 119        s64 time_ns = iio_get_time_ns();
 120        s32 val;
 121        int ret;
 122
 123        mutex_lock(&als->lock);
 124
 125        memset(buffer, 0, ACPI_ALS_EVT_BUFFER_SIZE);
 126
 127        switch (event) {
 128        case ACPI_ALS_NOTIFY_ILLUMINANCE:
 129                ret = acpi_als_read_value(als, ACPI_ALS_ILLUMINANCE, &val);
 130                if (ret < 0)
 131                        goto out;
 132                *buffer++ = val;
 133                break;
 134        default:
 135                /* Unhandled event */
 136                dev_dbg(&device->dev, "Unhandled ACPI ALS event (%08x)!\n",
 137                        event);
 138                goto out;
 139        }
 140
 141        iio_push_to_buffers_with_timestamp(indio_dev, als->evt_buffer, time_ns);
 142
 143out:
 144        mutex_unlock(&als->lock);
 145}
 146
 147static int acpi_als_read_raw(struct iio_dev *indio_dev,
 148                             struct iio_chan_spec const *chan, int *val,
 149                             int *val2, long mask)
 150{
 151        struct acpi_als *als = iio_priv(indio_dev);
 152        s32 temp_val;
 153        int ret;
 154
 155        if (mask != IIO_CHAN_INFO_RAW)
 156                return -EINVAL;
 157
 158        /* we support only illumination (_ALI) so far. */
 159        if (chan->type != IIO_LIGHT)
 160                return -EINVAL;
 161
 162        ret = acpi_als_read_value(als, ACPI_ALS_ILLUMINANCE, &temp_val);
 163        if (ret < 0)
 164                return ret;
 165
 166        *val = temp_val;
 167
 168        return IIO_VAL_INT;
 169}
 170
 171static const struct iio_info acpi_als_info = {
 172        .driver_module          = THIS_MODULE,
 173        .read_raw               = acpi_als_read_raw,
 174};
 175
 176static int acpi_als_add(struct acpi_device *device)
 177{
 178        struct acpi_als *als;
 179        struct iio_dev *indio_dev;
 180        struct iio_buffer *buffer;
 181
 182        indio_dev = devm_iio_device_alloc(&device->dev, sizeof(*als));
 183        if (!indio_dev)
 184                return -ENOMEM;
 185
 186        als = iio_priv(indio_dev);
 187
 188        device->driver_data = indio_dev;
 189        als->device = device;
 190        mutex_init(&als->lock);
 191
 192        indio_dev->name = ACPI_ALS_DEVICE_NAME;
 193        indio_dev->dev.parent = &device->dev;
 194        indio_dev->info = &acpi_als_info;
 195        indio_dev->modes = INDIO_BUFFER_SOFTWARE;
 196        indio_dev->channels = acpi_als_channels;
 197        indio_dev->num_channels = ARRAY_SIZE(acpi_als_channels);
 198
 199        buffer = devm_iio_kfifo_allocate(&device->dev);
 200        if (!buffer)
 201                return -ENOMEM;
 202
 203        iio_device_attach_buffer(indio_dev, buffer);
 204
 205        return devm_iio_device_register(&device->dev, indio_dev);
 206}
 207
 208static const struct acpi_device_id acpi_als_device_ids[] = {
 209        {"ACPI0008", 0},
 210        {},
 211};
 212
 213MODULE_DEVICE_TABLE(acpi, acpi_als_device_ids);
 214
 215static struct acpi_driver acpi_als_driver = {
 216        .name   = "acpi_als",
 217        .class  = ACPI_ALS_CLASS,
 218        .ids    = acpi_als_device_ids,
 219        .ops = {
 220                .add    = acpi_als_add,
 221                .notify = acpi_als_notify,
 222        },
 223};
 224
 225module_acpi_driver(acpi_als_driver);
 226
 227MODULE_AUTHOR("Zhang Rui <rui.zhang@intel.com>");
 228MODULE_AUTHOR("Martin Liska <marxin.liska@gmail.com>");
 229MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
 230MODULE_DESCRIPTION("ACPI Ambient Light Sensor Driver");
 231MODULE_LICENSE("GPL");
 232