linux/drivers/iio/light/hid-sensor-als.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * HID Sensors Driver
   4 * Copyright (c) 2012, Intel Corporation.
   5 */
   6#include <linux/device.h>
   7#include <linux/platform_device.h>
   8#include <linux/module.h>
   9#include <linux/interrupt.h>
  10#include <linux/irq.h>
  11#include <linux/slab.h>
  12#include <linux/delay.h>
  13#include <linux/hid-sensor-hub.h>
  14#include <linux/iio/iio.h>
  15#include <linux/iio/sysfs.h>
  16#include <linux/iio/buffer.h>
  17#include "../common/hid-sensors/hid-sensor-trigger.h"
  18
  19enum {
  20        CHANNEL_SCAN_INDEX_INTENSITY = 0,
  21        CHANNEL_SCAN_INDEX_ILLUM = 1,
  22        CHANNEL_SCAN_INDEX_MAX
  23};
  24
  25struct als_state {
  26        struct hid_sensor_hub_callbacks callbacks;
  27        struct hid_sensor_common common_attributes;
  28        struct hid_sensor_hub_attribute_info als_illum;
  29        u32 illum[CHANNEL_SCAN_INDEX_MAX];
  30        int scale_pre_decml;
  31        int scale_post_decml;
  32        int scale_precision;
  33        int value_offset;
  34};
  35
  36/* Channel definitions */
  37static const struct iio_chan_spec als_channels[] = {
  38        {
  39                .type = IIO_INTENSITY,
  40                .modified = 1,
  41                .channel2 = IIO_MOD_LIGHT_BOTH,
  42                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
  43                .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
  44                BIT(IIO_CHAN_INFO_SCALE) |
  45                BIT(IIO_CHAN_INFO_SAMP_FREQ) |
  46                BIT(IIO_CHAN_INFO_HYSTERESIS),
  47                .scan_index = CHANNEL_SCAN_INDEX_INTENSITY,
  48        },
  49        {
  50                .type = IIO_LIGHT,
  51                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
  52                .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
  53                BIT(IIO_CHAN_INFO_SCALE) |
  54                BIT(IIO_CHAN_INFO_SAMP_FREQ) |
  55                BIT(IIO_CHAN_INFO_HYSTERESIS),
  56                .scan_index = CHANNEL_SCAN_INDEX_ILLUM,
  57        }
  58};
  59
  60/* Adjust channel real bits based on report descriptor */
  61static void als_adjust_channel_bit_mask(struct iio_chan_spec *channels,
  62                                        int channel, int size)
  63{
  64        channels[channel].scan_type.sign = 's';
  65        /* Real storage bits will change based on the report desc. */
  66        channels[channel].scan_type.realbits = size * 8;
  67        /* Maximum size of a sample to capture is u32 */
  68        channels[channel].scan_type.storagebits = sizeof(u32) * 8;
  69}
  70
  71/* Channel read_raw handler */
  72static int als_read_raw(struct iio_dev *indio_dev,
  73                              struct iio_chan_spec const *chan,
  74                              int *val, int *val2,
  75                              long mask)
  76{
  77        struct als_state *als_state = iio_priv(indio_dev);
  78        int report_id = -1;
  79        u32 address;
  80        int ret_type;
  81        s32 min;
  82
  83        *val = 0;
  84        *val2 = 0;
  85        switch (mask) {
  86        case IIO_CHAN_INFO_RAW:
  87                switch (chan->scan_index) {
  88                case  CHANNEL_SCAN_INDEX_INTENSITY:
  89                case  CHANNEL_SCAN_INDEX_ILLUM:
  90                        report_id = als_state->als_illum.report_id;
  91                        min = als_state->als_illum.logical_minimum;
  92                        address = HID_USAGE_SENSOR_LIGHT_ILLUM;
  93                        break;
  94                default:
  95                        report_id = -1;
  96                        break;
  97                }
  98                if (report_id >= 0) {
  99                        hid_sensor_power_state(&als_state->common_attributes,
 100                                                true);
 101                        *val = sensor_hub_input_attr_get_raw_value(
 102                                        als_state->common_attributes.hsdev,
 103                                        HID_USAGE_SENSOR_ALS, address,
 104                                        report_id,
 105                                        SENSOR_HUB_SYNC,
 106                                        min < 0);
 107                        hid_sensor_power_state(&als_state->common_attributes,
 108                                                false);
 109                } else {
 110                        *val = 0;
 111                        return -EINVAL;
 112                }
 113                ret_type = IIO_VAL_INT;
 114                break;
 115        case IIO_CHAN_INFO_SCALE:
 116                *val = als_state->scale_pre_decml;
 117                *val2 = als_state->scale_post_decml;
 118                ret_type = als_state->scale_precision;
 119                break;
 120        case IIO_CHAN_INFO_OFFSET:
 121                *val = als_state->value_offset;
 122                ret_type = IIO_VAL_INT;
 123                break;
 124        case IIO_CHAN_INFO_SAMP_FREQ:
 125                ret_type = hid_sensor_read_samp_freq_value(
 126                                &als_state->common_attributes, val, val2);
 127                break;
 128        case IIO_CHAN_INFO_HYSTERESIS:
 129                ret_type = hid_sensor_read_raw_hyst_value(
 130                                &als_state->common_attributes, val, val2);
 131                break;
 132        default:
 133                ret_type = -EINVAL;
 134                break;
 135        }
 136
 137        return ret_type;
 138}
 139
 140/* Channel write_raw handler */
 141static int als_write_raw(struct iio_dev *indio_dev,
 142                               struct iio_chan_spec const *chan,
 143                               int val,
 144                               int val2,
 145                               long mask)
 146{
 147        struct als_state *als_state = iio_priv(indio_dev);
 148        int ret = 0;
 149
 150        switch (mask) {
 151        case IIO_CHAN_INFO_SAMP_FREQ:
 152                ret = hid_sensor_write_samp_freq_value(
 153                                &als_state->common_attributes, val, val2);
 154                break;
 155        case IIO_CHAN_INFO_HYSTERESIS:
 156                ret = hid_sensor_write_raw_hyst_value(
 157                                &als_state->common_attributes, val, val2);
 158                break;
 159        default:
 160                ret = -EINVAL;
 161        }
 162
 163        return ret;
 164}
 165
 166static const struct iio_info als_info = {
 167        .read_raw = &als_read_raw,
 168        .write_raw = &als_write_raw,
 169};
 170
 171/* Function to push data to buffer */
 172static void hid_sensor_push_data(struct iio_dev *indio_dev, const void *data,
 173                                        int len)
 174{
 175        dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n");
 176        iio_push_to_buffers(indio_dev, data);
 177}
 178
 179/* Callback handler to send event after all samples are received and captured */
 180static int als_proc_event(struct hid_sensor_hub_device *hsdev,
 181                                unsigned usage_id,
 182                                void *priv)
 183{
 184        struct iio_dev *indio_dev = platform_get_drvdata(priv);
 185        struct als_state *als_state = iio_priv(indio_dev);
 186
 187        dev_dbg(&indio_dev->dev, "als_proc_event\n");
 188        if (atomic_read(&als_state->common_attributes.data_ready))
 189                hid_sensor_push_data(indio_dev,
 190                                &als_state->illum,
 191                                sizeof(als_state->illum));
 192
 193        return 0;
 194}
 195
 196/* Capture samples in local storage */
 197static int als_capture_sample(struct hid_sensor_hub_device *hsdev,
 198                                unsigned usage_id,
 199                                size_t raw_len, char *raw_data,
 200                                void *priv)
 201{
 202        struct iio_dev *indio_dev = platform_get_drvdata(priv);
 203        struct als_state *als_state = iio_priv(indio_dev);
 204        int ret = -EINVAL;
 205        u32 sample_data = *(u32 *)raw_data;
 206
 207        switch (usage_id) {
 208        case HID_USAGE_SENSOR_LIGHT_ILLUM:
 209                als_state->illum[CHANNEL_SCAN_INDEX_INTENSITY] = sample_data;
 210                als_state->illum[CHANNEL_SCAN_INDEX_ILLUM] = sample_data;
 211                ret = 0;
 212                break;
 213        default:
 214                break;
 215        }
 216
 217        return ret;
 218}
 219
 220/* Parse report which is specific to an usage id*/
 221static int als_parse_report(struct platform_device *pdev,
 222                                struct hid_sensor_hub_device *hsdev,
 223                                struct iio_chan_spec *channels,
 224                                unsigned usage_id,
 225                                struct als_state *st)
 226{
 227        int ret;
 228
 229        ret = sensor_hub_input_get_attribute_info(hsdev, HID_INPUT_REPORT,
 230                        usage_id,
 231                        HID_USAGE_SENSOR_LIGHT_ILLUM,
 232                        &st->als_illum);
 233        if (ret < 0)
 234                return ret;
 235        als_adjust_channel_bit_mask(channels, CHANNEL_SCAN_INDEX_INTENSITY,
 236                                    st->als_illum.size);
 237        als_adjust_channel_bit_mask(channels, CHANNEL_SCAN_INDEX_ILLUM,
 238                                        st->als_illum.size);
 239
 240        dev_dbg(&pdev->dev, "als %x:%x\n", st->als_illum.index,
 241                        st->als_illum.report_id);
 242
 243        st->scale_precision = hid_sensor_format_scale(
 244                                HID_USAGE_SENSOR_ALS,
 245                                &st->als_illum,
 246                                &st->scale_pre_decml, &st->scale_post_decml);
 247
 248        /* Set Sensitivity field ids, when there is no individual modifier */
 249        if (st->common_attributes.sensitivity.index < 0) {
 250                sensor_hub_input_get_attribute_info(hsdev,
 251                        HID_FEATURE_REPORT, usage_id,
 252                        HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS |
 253                        HID_USAGE_SENSOR_DATA_LIGHT,
 254                        &st->common_attributes.sensitivity);
 255                dev_dbg(&pdev->dev, "Sensitivity index:report %d:%d\n",
 256                        st->common_attributes.sensitivity.index,
 257                        st->common_attributes.sensitivity.report_id);
 258        }
 259        return ret;
 260}
 261
 262/* Function to initialize the processing for usage id */
 263static int hid_als_probe(struct platform_device *pdev)
 264{
 265        int ret = 0;
 266        static const char *name = "als";
 267        struct iio_dev *indio_dev;
 268        struct als_state *als_state;
 269        struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
 270
 271        indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct als_state));
 272        if (!indio_dev)
 273                return -ENOMEM;
 274        platform_set_drvdata(pdev, indio_dev);
 275
 276        als_state = iio_priv(indio_dev);
 277        als_state->common_attributes.hsdev = hsdev;
 278        als_state->common_attributes.pdev = pdev;
 279
 280        ret = hid_sensor_parse_common_attributes(hsdev, HID_USAGE_SENSOR_ALS,
 281                                        &als_state->common_attributes);
 282        if (ret) {
 283                dev_err(&pdev->dev, "failed to setup common attributes\n");
 284                return ret;
 285        }
 286
 287        indio_dev->channels = kmemdup(als_channels,
 288                                      sizeof(als_channels), GFP_KERNEL);
 289        if (!indio_dev->channels) {
 290                dev_err(&pdev->dev, "failed to duplicate channels\n");
 291                return -ENOMEM;
 292        }
 293
 294        ret = als_parse_report(pdev, hsdev,
 295                               (struct iio_chan_spec *)indio_dev->channels,
 296                               HID_USAGE_SENSOR_ALS, als_state);
 297        if (ret) {
 298                dev_err(&pdev->dev, "failed to setup attributes\n");
 299                goto error_free_dev_mem;
 300        }
 301
 302        indio_dev->num_channels =
 303                                ARRAY_SIZE(als_channels);
 304        indio_dev->info = &als_info;
 305        indio_dev->name = name;
 306        indio_dev->modes = INDIO_DIRECT_MODE;
 307
 308        atomic_set(&als_state->common_attributes.data_ready, 0);
 309
 310        ret = hid_sensor_setup_trigger(indio_dev, name,
 311                                &als_state->common_attributes);
 312        if (ret < 0) {
 313                dev_err(&pdev->dev, "trigger setup failed\n");
 314                goto error_free_dev_mem;
 315        }
 316
 317        ret = iio_device_register(indio_dev);
 318        if (ret) {
 319                dev_err(&pdev->dev, "device register failed\n");
 320                goto error_remove_trigger;
 321        }
 322
 323        als_state->callbacks.send_event = als_proc_event;
 324        als_state->callbacks.capture_sample = als_capture_sample;
 325        als_state->callbacks.pdev = pdev;
 326        ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_ALS,
 327                                        &als_state->callbacks);
 328        if (ret < 0) {
 329                dev_err(&pdev->dev, "callback reg failed\n");
 330                goto error_iio_unreg;
 331        }
 332
 333        return ret;
 334
 335error_iio_unreg:
 336        iio_device_unregister(indio_dev);
 337error_remove_trigger:
 338        hid_sensor_remove_trigger(indio_dev, &als_state->common_attributes);
 339error_free_dev_mem:
 340        kfree(indio_dev->channels);
 341        return ret;
 342}
 343
 344/* Function to deinitialize the processing for usage id */
 345static int hid_als_remove(struct platform_device *pdev)
 346{
 347        struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
 348        struct iio_dev *indio_dev = platform_get_drvdata(pdev);
 349        struct als_state *als_state = iio_priv(indio_dev);
 350
 351        sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_ALS);
 352        iio_device_unregister(indio_dev);
 353        hid_sensor_remove_trigger(indio_dev, &als_state->common_attributes);
 354        kfree(indio_dev->channels);
 355
 356        return 0;
 357}
 358
 359static const struct platform_device_id hid_als_ids[] = {
 360        {
 361                /* Format: HID-SENSOR-usage_id_in_hex_lowercase */
 362                .name = "HID-SENSOR-200041",
 363        },
 364        { /* sentinel */ }
 365};
 366MODULE_DEVICE_TABLE(platform, hid_als_ids);
 367
 368static struct platform_driver hid_als_platform_driver = {
 369        .id_table = hid_als_ids,
 370        .driver = {
 371                .name   = KBUILD_MODNAME,
 372                .pm     = &hid_sensor_pm_ops,
 373        },
 374        .probe          = hid_als_probe,
 375        .remove         = hid_als_remove,
 376};
 377module_platform_driver(hid_als_platform_driver);
 378
 379MODULE_DESCRIPTION("HID Sensor ALS");
 380MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>");
 381MODULE_LICENSE("GPL");
 382