Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

iio: light: Add support for Azoteq IQS621/622 ambient light sensors

This patch adds support for the Azoteq IQS621 and IQS622 ambient light
sensors, both of which can report a four-bit representation of ambient
light intensity.

The IQS621 can additionally report illuminace directly in units of lux,
while the IQS622 can report a four-bit representation of infrared light
intensity. Furthermore, the IQS622 can report a unitless measurement of
a target's proximity to the device.

Signed-off-by: Jeff LaBundy <jeff@labundy.com>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Signed-off-by: Lee Jones <lee.jones@linaro.org>

authored by

Jeff LaBundy and committed by
Lee Jones
b081b738 8ba44710

+628
+10
drivers/iio/light/Kconfig
··· 173 173 To compile this driver as a module, choose M here: the 174 174 module will be called gp2ap020a00f. 175 175 176 + config IQS621_ALS 177 + tristate "Azoteq IQS621/622 ambient light sensors" 178 + depends on MFD_IQS62X || COMPILE_TEST 179 + help 180 + Say Y here if you want to build support for the Azoteq IQS621 181 + and IQS622 ambient light sensors. 182 + 183 + To compile this driver as a module, choose M here: the module 184 + will be called iqs621-als. 185 + 176 186 config SENSORS_ISL29018 177 187 tristate "Intersil 29018 light and proximity sensor" 178 188 depends on I2C
+1
drivers/iio/light/Makefile
··· 21 21 obj-$(CONFIG_GP2AP020A00F) += gp2ap020a00f.o 22 22 obj-$(CONFIG_HID_SENSOR_ALS) += hid-sensor-als.o 23 23 obj-$(CONFIG_HID_SENSOR_PROX) += hid-sensor-prox.o 24 + obj-$(CONFIG_IQS621_ALS) += iqs621-als.o 24 25 obj-$(CONFIG_SENSORS_ISL29018) += isl29018.o 25 26 obj-$(CONFIG_SENSORS_ISL29028) += isl29028.o 26 27 obj-$(CONFIG_ISL29125) += isl29125.o
+617
drivers/iio/light/iqs621-als.c
··· 1 + // SPDX-License-Identifier: GPL-2.0+ 2 + /* 3 + * Azoteq IQS621/622 Ambient Light Sensors 4 + * 5 + * Copyright (C) 2019 Jeff LaBundy <jeff@labundy.com> 6 + */ 7 + 8 + #include <linux/device.h> 9 + #include <linux/iio/events.h> 10 + #include <linux/iio/iio.h> 11 + #include <linux/kernel.h> 12 + #include <linux/mfd/iqs62x.h> 13 + #include <linux/module.h> 14 + #include <linux/mutex.h> 15 + #include <linux/notifier.h> 16 + #include <linux/platform_device.h> 17 + #include <linux/regmap.h> 18 + 19 + #define IQS621_ALS_FLAGS_LIGHT BIT(7) 20 + #define IQS621_ALS_FLAGS_RANGE GENMASK(3, 0) 21 + 22 + #define IQS621_ALS_UI_OUT 0x17 23 + 24 + #define IQS621_ALS_THRESH_DARK 0x80 25 + #define IQS621_ALS_THRESH_LIGHT 0x81 26 + 27 + #define IQS622_IR_RANGE 0x15 28 + #define IQS622_IR_FLAGS 0x16 29 + #define IQS622_IR_FLAGS_TOUCH BIT(1) 30 + #define IQS622_IR_FLAGS_PROX BIT(0) 31 + 32 + #define IQS622_IR_UI_OUT 0x17 33 + 34 + #define IQS622_IR_THRESH_PROX 0x91 35 + #define IQS622_IR_THRESH_TOUCH 0x92 36 + 37 + struct iqs621_als_private { 38 + struct iqs62x_core *iqs62x; 39 + struct notifier_block notifier; 40 + struct mutex lock; 41 + bool light_en; 42 + bool range_en; 43 + bool prox_en; 44 + u8 als_flags; 45 + u8 ir_flags_mask; 46 + u8 ir_flags; 47 + u8 thresh_light; 48 + u8 thresh_dark; 49 + u8 thresh_prox; 50 + }; 51 + 52 + static int iqs621_als_init(struct iqs621_als_private *iqs621_als) 53 + { 54 + struct iqs62x_core *iqs62x = iqs621_als->iqs62x; 55 + unsigned int event_mask = 0; 56 + int ret; 57 + 58 + switch (iqs621_als->ir_flags_mask) { 59 + case IQS622_IR_FLAGS_TOUCH: 60 + ret = regmap_write(iqs62x->regmap, IQS622_IR_THRESH_TOUCH, 61 + iqs621_als->thresh_prox); 62 + break; 63 + 64 + case IQS622_IR_FLAGS_PROX: 65 + ret = regmap_write(iqs62x->regmap, IQS622_IR_THRESH_PROX, 66 + iqs621_als->thresh_prox); 67 + break; 68 + 69 + default: 70 + ret = regmap_write(iqs62x->regmap, IQS621_ALS_THRESH_LIGHT, 71 + iqs621_als->thresh_light); 72 + if (ret) 73 + return ret; 74 + 75 + ret = regmap_write(iqs62x->regmap, IQS621_ALS_THRESH_DARK, 76 + iqs621_als->thresh_dark); 77 + } 78 + 79 + if (ret) 80 + return ret; 81 + 82 + if (iqs621_als->light_en || iqs621_als->range_en) 83 + event_mask |= iqs62x->dev_desc->als_mask; 84 + 85 + if (iqs621_als->prox_en) 86 + event_mask |= iqs62x->dev_desc->ir_mask; 87 + 88 + return regmap_update_bits(iqs62x->regmap, IQS620_GLBL_EVENT_MASK, 89 + event_mask, 0); 90 + } 91 + 92 + static int iqs621_als_notifier(struct notifier_block *notifier, 93 + unsigned long event_flags, void *context) 94 + { 95 + struct iqs62x_event_data *event_data = context; 96 + struct iqs621_als_private *iqs621_als; 97 + struct iio_dev *indio_dev; 98 + bool light_new, light_old; 99 + bool prox_new, prox_old; 100 + u8 range_new, range_old; 101 + s64 timestamp; 102 + int ret; 103 + 104 + iqs621_als = container_of(notifier, struct iqs621_als_private, 105 + notifier); 106 + indio_dev = iio_priv_to_dev(iqs621_als); 107 + timestamp = iio_get_time_ns(indio_dev); 108 + 109 + mutex_lock(&iqs621_als->lock); 110 + 111 + if (event_flags & BIT(IQS62X_EVENT_SYS_RESET)) { 112 + ret = iqs621_als_init(iqs621_als); 113 + if (ret) { 114 + dev_err(indio_dev->dev.parent, 115 + "Failed to re-initialize device: %d\n", ret); 116 + ret = NOTIFY_BAD; 117 + } else { 118 + ret = NOTIFY_OK; 119 + } 120 + 121 + goto err_mutex; 122 + } 123 + 124 + if (!iqs621_als->light_en && !iqs621_als->range_en && 125 + !iqs621_als->prox_en) { 126 + ret = NOTIFY_DONE; 127 + goto err_mutex; 128 + } 129 + 130 + /* IQS621 only */ 131 + light_new = event_data->als_flags & IQS621_ALS_FLAGS_LIGHT; 132 + light_old = iqs621_als->als_flags & IQS621_ALS_FLAGS_LIGHT; 133 + 134 + if (iqs621_als->light_en && light_new && !light_old) 135 + iio_push_event(indio_dev, 136 + IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0, 137 + IIO_EV_TYPE_THRESH, 138 + IIO_EV_DIR_RISING), 139 + timestamp); 140 + else if (iqs621_als->light_en && !light_new && light_old) 141 + iio_push_event(indio_dev, 142 + IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0, 143 + IIO_EV_TYPE_THRESH, 144 + IIO_EV_DIR_FALLING), 145 + timestamp); 146 + 147 + /* IQS621 and IQS622 */ 148 + range_new = event_data->als_flags & IQS621_ALS_FLAGS_RANGE; 149 + range_old = iqs621_als->als_flags & IQS621_ALS_FLAGS_RANGE; 150 + 151 + if (iqs621_als->range_en && (range_new > range_old)) 152 + iio_push_event(indio_dev, 153 + IIO_UNMOD_EVENT_CODE(IIO_INTENSITY, 0, 154 + IIO_EV_TYPE_CHANGE, 155 + IIO_EV_DIR_RISING), 156 + timestamp); 157 + else if (iqs621_als->range_en && (range_new < range_old)) 158 + iio_push_event(indio_dev, 159 + IIO_UNMOD_EVENT_CODE(IIO_INTENSITY, 0, 160 + IIO_EV_TYPE_CHANGE, 161 + IIO_EV_DIR_FALLING), 162 + timestamp); 163 + 164 + /* IQS622 only */ 165 + prox_new = event_data->ir_flags & iqs621_als->ir_flags_mask; 166 + prox_old = iqs621_als->ir_flags & iqs621_als->ir_flags_mask; 167 + 168 + if (iqs621_als->prox_en && prox_new && !prox_old) 169 + iio_push_event(indio_dev, 170 + IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0, 171 + IIO_EV_TYPE_THRESH, 172 + IIO_EV_DIR_RISING), 173 + timestamp); 174 + else if (iqs621_als->prox_en && !prox_new && prox_old) 175 + iio_push_event(indio_dev, 176 + IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0, 177 + IIO_EV_TYPE_THRESH, 178 + IIO_EV_DIR_FALLING), 179 + timestamp); 180 + 181 + iqs621_als->als_flags = event_data->als_flags; 182 + iqs621_als->ir_flags = event_data->ir_flags; 183 + ret = NOTIFY_OK; 184 + 185 + err_mutex: 186 + mutex_unlock(&iqs621_als->lock); 187 + 188 + return ret; 189 + } 190 + 191 + static void iqs621_als_notifier_unregister(void *context) 192 + { 193 + struct iqs621_als_private *iqs621_als = context; 194 + struct iio_dev *indio_dev = iio_priv_to_dev(iqs621_als); 195 + int ret; 196 + 197 + ret = blocking_notifier_chain_unregister(&iqs621_als->iqs62x->nh, 198 + &iqs621_als->notifier); 199 + if (ret) 200 + dev_err(indio_dev->dev.parent, 201 + "Failed to unregister notifier: %d\n", ret); 202 + } 203 + 204 + static int iqs621_als_read_raw(struct iio_dev *indio_dev, 205 + struct iio_chan_spec const *chan, 206 + int *val, int *val2, long mask) 207 + { 208 + struct iqs621_als_private *iqs621_als = iio_priv(indio_dev); 209 + struct iqs62x_core *iqs62x = iqs621_als->iqs62x; 210 + int ret; 211 + __le16 val_buf; 212 + 213 + switch (chan->type) { 214 + case IIO_INTENSITY: 215 + ret = regmap_read(iqs62x->regmap, chan->address, val); 216 + if (ret) 217 + return ret; 218 + 219 + *val &= IQS621_ALS_FLAGS_RANGE; 220 + return IIO_VAL_INT; 221 + 222 + case IIO_PROXIMITY: 223 + case IIO_LIGHT: 224 + ret = regmap_raw_read(iqs62x->regmap, chan->address, &val_buf, 225 + sizeof(val_buf)); 226 + if (ret) 227 + return ret; 228 + 229 + *val = le16_to_cpu(val_buf); 230 + return IIO_VAL_INT; 231 + 232 + default: 233 + return -EINVAL; 234 + } 235 + } 236 + 237 + static int iqs621_als_read_event_config(struct iio_dev *indio_dev, 238 + const struct iio_chan_spec *chan, 239 + enum iio_event_type type, 240 + enum iio_event_direction dir) 241 + { 242 + struct iqs621_als_private *iqs621_als = iio_priv(indio_dev); 243 + int ret; 244 + 245 + mutex_lock(&iqs621_als->lock); 246 + 247 + switch (chan->type) { 248 + case IIO_LIGHT: 249 + ret = iqs621_als->light_en; 250 + break; 251 + 252 + case IIO_INTENSITY: 253 + ret = iqs621_als->range_en; 254 + break; 255 + 256 + case IIO_PROXIMITY: 257 + ret = iqs621_als->prox_en; 258 + break; 259 + 260 + default: 261 + ret = -EINVAL; 262 + } 263 + 264 + mutex_unlock(&iqs621_als->lock); 265 + 266 + return ret; 267 + } 268 + 269 + static int iqs621_als_write_event_config(struct iio_dev *indio_dev, 270 + const struct iio_chan_spec *chan, 271 + enum iio_event_type type, 272 + enum iio_event_direction dir, 273 + int state) 274 + { 275 + struct iqs621_als_private *iqs621_als = iio_priv(indio_dev); 276 + struct iqs62x_core *iqs62x = iqs621_als->iqs62x; 277 + unsigned int val; 278 + int ret; 279 + 280 + mutex_lock(&iqs621_als->lock); 281 + 282 + ret = regmap_read(iqs62x->regmap, iqs62x->dev_desc->als_flags, &val); 283 + if (ret) 284 + goto err_mutex; 285 + iqs621_als->als_flags = val; 286 + 287 + switch (chan->type) { 288 + case IIO_LIGHT: 289 + ret = regmap_update_bits(iqs62x->regmap, IQS620_GLBL_EVENT_MASK, 290 + iqs62x->dev_desc->als_mask, 291 + iqs621_als->range_en || state ? 0 : 292 + 0xFF); 293 + if (!ret) 294 + iqs621_als->light_en = state; 295 + break; 296 + 297 + case IIO_INTENSITY: 298 + ret = regmap_update_bits(iqs62x->regmap, IQS620_GLBL_EVENT_MASK, 299 + iqs62x->dev_desc->als_mask, 300 + iqs621_als->light_en || state ? 0 : 301 + 0xFF); 302 + if (!ret) 303 + iqs621_als->range_en = state; 304 + break; 305 + 306 + case IIO_PROXIMITY: 307 + ret = regmap_read(iqs62x->regmap, IQS622_IR_FLAGS, &val); 308 + if (ret) 309 + goto err_mutex; 310 + iqs621_als->ir_flags = val; 311 + 312 + ret = regmap_update_bits(iqs62x->regmap, IQS620_GLBL_EVENT_MASK, 313 + iqs62x->dev_desc->ir_mask, 314 + state ? 0 : 0xFF); 315 + if (!ret) 316 + iqs621_als->prox_en = state; 317 + break; 318 + 319 + default: 320 + ret = -EINVAL; 321 + } 322 + 323 + err_mutex: 324 + mutex_unlock(&iqs621_als->lock); 325 + 326 + return ret; 327 + } 328 + 329 + static int iqs621_als_read_event_value(struct iio_dev *indio_dev, 330 + const struct iio_chan_spec *chan, 331 + enum iio_event_type type, 332 + enum iio_event_direction dir, 333 + enum iio_event_info info, 334 + int *val, int *val2) 335 + { 336 + struct iqs621_als_private *iqs621_als = iio_priv(indio_dev); 337 + int ret = IIO_VAL_INT; 338 + 339 + mutex_lock(&iqs621_als->lock); 340 + 341 + switch (dir) { 342 + case IIO_EV_DIR_RISING: 343 + *val = iqs621_als->thresh_light * 16; 344 + break; 345 + 346 + case IIO_EV_DIR_FALLING: 347 + *val = iqs621_als->thresh_dark * 4; 348 + break; 349 + 350 + case IIO_EV_DIR_EITHER: 351 + if (iqs621_als->ir_flags_mask == IQS622_IR_FLAGS_TOUCH) 352 + *val = iqs621_als->thresh_prox * 4; 353 + else 354 + *val = iqs621_als->thresh_prox; 355 + break; 356 + 357 + default: 358 + ret = -EINVAL; 359 + } 360 + 361 + mutex_unlock(&iqs621_als->lock); 362 + 363 + return ret; 364 + } 365 + 366 + static int iqs621_als_write_event_value(struct iio_dev *indio_dev, 367 + const struct iio_chan_spec *chan, 368 + enum iio_event_type type, 369 + enum iio_event_direction dir, 370 + enum iio_event_info info, 371 + int val, int val2) 372 + { 373 + struct iqs621_als_private *iqs621_als = iio_priv(indio_dev); 374 + struct iqs62x_core *iqs62x = iqs621_als->iqs62x; 375 + unsigned int thresh_reg, thresh_val; 376 + u8 ir_flags_mask, *thresh_cache; 377 + int ret = -EINVAL; 378 + 379 + mutex_lock(&iqs621_als->lock); 380 + 381 + switch (dir) { 382 + case IIO_EV_DIR_RISING: 383 + thresh_reg = IQS621_ALS_THRESH_LIGHT; 384 + thresh_val = val / 16; 385 + 386 + thresh_cache = &iqs621_als->thresh_light; 387 + ir_flags_mask = 0; 388 + break; 389 + 390 + case IIO_EV_DIR_FALLING: 391 + thresh_reg = IQS621_ALS_THRESH_DARK; 392 + thresh_val = val / 4; 393 + 394 + thresh_cache = &iqs621_als->thresh_dark; 395 + ir_flags_mask = 0; 396 + break; 397 + 398 + case IIO_EV_DIR_EITHER: 399 + /* 400 + * The IQS622 supports two detection thresholds, both measured 401 + * in the same arbitrary units reported by read_raw: proximity 402 + * (0 through 255 in steps of 1), and touch (0 through 1020 in 403 + * steps of 4). 404 + * 405 + * Based on the single detection threshold chosen by the user, 406 + * select the hardware threshold that gives the best trade-off 407 + * between range and resolution. 408 + * 409 + * By default, the close-range (but coarse) touch threshold is 410 + * chosen during probe. 411 + */ 412 + switch (val) { 413 + case 0 ... 255: 414 + thresh_reg = IQS622_IR_THRESH_PROX; 415 + thresh_val = val; 416 + 417 + ir_flags_mask = IQS622_IR_FLAGS_PROX; 418 + break; 419 + 420 + case 256 ... 1020: 421 + thresh_reg = IQS622_IR_THRESH_TOUCH; 422 + thresh_val = val / 4; 423 + 424 + ir_flags_mask = IQS622_IR_FLAGS_TOUCH; 425 + break; 426 + 427 + default: 428 + goto err_mutex; 429 + } 430 + 431 + thresh_cache = &iqs621_als->thresh_prox; 432 + break; 433 + 434 + default: 435 + goto err_mutex; 436 + } 437 + 438 + if (thresh_val > 0xFF) 439 + goto err_mutex; 440 + 441 + ret = regmap_write(iqs62x->regmap, thresh_reg, thresh_val); 442 + if (ret) 443 + goto err_mutex; 444 + 445 + *thresh_cache = thresh_val; 446 + iqs621_als->ir_flags_mask = ir_flags_mask; 447 + 448 + err_mutex: 449 + mutex_unlock(&iqs621_als->lock); 450 + 451 + return ret; 452 + } 453 + 454 + static const struct iio_info iqs621_als_info = { 455 + .read_raw = &iqs621_als_read_raw, 456 + .read_event_config = iqs621_als_read_event_config, 457 + .write_event_config = iqs621_als_write_event_config, 458 + .read_event_value = iqs621_als_read_event_value, 459 + .write_event_value = iqs621_als_write_event_value, 460 + }; 461 + 462 + static const struct iio_event_spec iqs621_als_range_events[] = { 463 + { 464 + .type = IIO_EV_TYPE_CHANGE, 465 + .dir = IIO_EV_DIR_EITHER, 466 + .mask_separate = BIT(IIO_EV_INFO_ENABLE), 467 + }, 468 + }; 469 + 470 + static const struct iio_event_spec iqs621_als_light_events[] = { 471 + { 472 + .type = IIO_EV_TYPE_THRESH, 473 + .dir = IIO_EV_DIR_EITHER, 474 + .mask_separate = BIT(IIO_EV_INFO_ENABLE), 475 + }, 476 + { 477 + .type = IIO_EV_TYPE_THRESH, 478 + .dir = IIO_EV_DIR_RISING, 479 + .mask_separate = BIT(IIO_EV_INFO_VALUE), 480 + }, 481 + { 482 + .type = IIO_EV_TYPE_THRESH, 483 + .dir = IIO_EV_DIR_FALLING, 484 + .mask_separate = BIT(IIO_EV_INFO_VALUE), 485 + }, 486 + }; 487 + 488 + static const struct iio_chan_spec iqs621_als_channels[] = { 489 + { 490 + .type = IIO_INTENSITY, 491 + .address = IQS621_ALS_FLAGS, 492 + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 493 + .event_spec = iqs621_als_range_events, 494 + .num_event_specs = ARRAY_SIZE(iqs621_als_range_events), 495 + }, 496 + { 497 + .type = IIO_LIGHT, 498 + .address = IQS621_ALS_UI_OUT, 499 + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), 500 + .event_spec = iqs621_als_light_events, 501 + .num_event_specs = ARRAY_SIZE(iqs621_als_light_events), 502 + }, 503 + }; 504 + 505 + static const struct iio_event_spec iqs622_als_prox_events[] = { 506 + { 507 + .type = IIO_EV_TYPE_THRESH, 508 + .dir = IIO_EV_DIR_EITHER, 509 + .mask_separate = BIT(IIO_EV_INFO_ENABLE) | 510 + BIT(IIO_EV_INFO_VALUE), 511 + }, 512 + }; 513 + 514 + static const struct iio_chan_spec iqs622_als_channels[] = { 515 + { 516 + .type = IIO_INTENSITY, 517 + .channel2 = IIO_MOD_LIGHT_BOTH, 518 + .address = IQS622_ALS_FLAGS, 519 + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 520 + .event_spec = iqs621_als_range_events, 521 + .num_event_specs = ARRAY_SIZE(iqs621_als_range_events), 522 + .modified = true, 523 + }, 524 + { 525 + .type = IIO_INTENSITY, 526 + .channel2 = IIO_MOD_LIGHT_IR, 527 + .address = IQS622_IR_RANGE, 528 + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 529 + .modified = true, 530 + }, 531 + { 532 + .type = IIO_PROXIMITY, 533 + .address = IQS622_IR_UI_OUT, 534 + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 535 + .event_spec = iqs622_als_prox_events, 536 + .num_event_specs = ARRAY_SIZE(iqs622_als_prox_events), 537 + }, 538 + }; 539 + 540 + static int iqs621_als_probe(struct platform_device *pdev) 541 + { 542 + struct iqs62x_core *iqs62x = dev_get_drvdata(pdev->dev.parent); 543 + struct iqs621_als_private *iqs621_als; 544 + struct iio_dev *indio_dev; 545 + unsigned int val; 546 + int ret; 547 + 548 + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*iqs621_als)); 549 + if (!indio_dev) 550 + return -ENOMEM; 551 + 552 + iqs621_als = iio_priv(indio_dev); 553 + iqs621_als->iqs62x = iqs62x; 554 + 555 + if (iqs62x->dev_desc->prod_num == IQS622_PROD_NUM) { 556 + ret = regmap_read(iqs62x->regmap, IQS622_IR_THRESH_TOUCH, 557 + &val); 558 + if (ret) 559 + return ret; 560 + iqs621_als->thresh_prox = val; 561 + iqs621_als->ir_flags_mask = IQS622_IR_FLAGS_TOUCH; 562 + 563 + indio_dev->channels = iqs622_als_channels; 564 + indio_dev->num_channels = ARRAY_SIZE(iqs622_als_channels); 565 + } else { 566 + ret = regmap_read(iqs62x->regmap, IQS621_ALS_THRESH_LIGHT, 567 + &val); 568 + if (ret) 569 + return ret; 570 + iqs621_als->thresh_light = val; 571 + 572 + ret = regmap_read(iqs62x->regmap, IQS621_ALS_THRESH_DARK, 573 + &val); 574 + if (ret) 575 + return ret; 576 + iqs621_als->thresh_dark = val; 577 + 578 + indio_dev->channels = iqs621_als_channels; 579 + indio_dev->num_channels = ARRAY_SIZE(iqs621_als_channels); 580 + } 581 + 582 + indio_dev->modes = INDIO_DIRECT_MODE; 583 + indio_dev->dev.parent = &pdev->dev; 584 + indio_dev->name = iqs62x->dev_desc->dev_name; 585 + indio_dev->info = &iqs621_als_info; 586 + 587 + mutex_init(&iqs621_als->lock); 588 + 589 + iqs621_als->notifier.notifier_call = iqs621_als_notifier; 590 + ret = blocking_notifier_chain_register(&iqs621_als->iqs62x->nh, 591 + &iqs621_als->notifier); 592 + if (ret) { 593 + dev_err(&pdev->dev, "Failed to register notifier: %d\n", ret); 594 + return ret; 595 + } 596 + 597 + ret = devm_add_action_or_reset(&pdev->dev, 598 + iqs621_als_notifier_unregister, 599 + iqs621_als); 600 + if (ret) 601 + return ret; 602 + 603 + return devm_iio_device_register(&pdev->dev, indio_dev); 604 + } 605 + 606 + static struct platform_driver iqs621_als_platform_driver = { 607 + .driver = { 608 + .name = "iqs621-als", 609 + }, 610 + .probe = iqs621_als_probe, 611 + }; 612 + module_platform_driver(iqs621_als_platform_driver); 613 + 614 + MODULE_AUTHOR("Jeff LaBundy <jeff@labundy.com>"); 615 + MODULE_DESCRIPTION("Azoteq IQS621/622 Ambient Light Sensors"); 616 + MODULE_LICENSE("GPL"); 617 + MODULE_ALIAS("platform:iqs621-als");