iio: light: add support for Broadcom APDS9999
Jose A. Perez de Azpillaga
maintainerv2 cover iio: light: add support for Broadcom APDS9999
Add IIO driver and DT binding for the Broadcom APDS9999 ambient light and proximity sensor. The green channel uses optical coating technology to approximate the human eye spectral response for ALS/lux measurements. IIO_INTENSITY channels are also provided for red, green, blue, and clear/IR so userspace can compute its own weighted lux.
Questions for reviewers:
1. Is the green channel as an ALS/lux approximation acceptable given the datasheet's claim of optical coating to emulate V(lambda)?
2. Is the IIO_INTENSITY channel layout correct? (red, green, blue, clear with modifiers, no scale)
3. Should the binding be merged with avago,apds9300.yaml or kept separate given the extra vled-supply?
v2:
- Added IIO_INTENSITY channels for R, G, B, clear
- Switched to guard(mutex)(), devm_mutex_init(), dev_err_probe()
- Replaced remove() with devm_add_action_or_reset()
- Don't fail probe on PART_ID mismatch (fallback compatibles)
- Replaced manual shifts with FIELD_PREP() / GENMASK
- Used get_unaligned_le24(), sizeof(buf), NSEC_PER_SEC, fsleep()
- Named gain register values, explicit resolution enum values
- Removed section comments, grouped bit defines under registers
- Squashed Kconfig/Makefile into driver patch
- Split MAINTAINERS entry across patches
- DT binding: added vled-supply, interrupts, required vdd-supply
- DT binding: fixed title, added blank lines, full description
- Dropped "DT binding" duplication from binding commit subject
Link to v1: https://lore.kernel.org/linux-iio/[email protected]/
Jose A. Perez de Azpillaga (2):
dt-bindings: iio: light: add Broadcom APDS9999
iio: light: add support for APDS9999 sensor
.../bindings/iio/light/brcm,apds9999.yaml | 53 +++ MAINTAINERS | 7 + drivers/iio/light/Kconfig | 10 + drivers/iio/light/Makefile | 1 + drivers/iio/light/apds9999.c | 344 ++++++++++++++++++ 5 files changed, 415 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/light/brcm,apds9999.yaml create mode 100644 drivers/iio/light/apds9999.c
Signature · 1 line
jose a. p-a
Jose A. Perez de Azpillaga
maintainerRFC PATCH v2 1/2 dt-bindings: iio: light: add Broadcom APDS9999
Add Device Tree binding for the Broadcom APDS9999 ambient light and proximity sensor.
The APDS9999 features individual R, G, B, and IR channels with a green channel that uses optical coating to approximate the human eye spectral response for ALS/lux measurements. Proximity and RGB functionality are not yet implemented in the driver.
--- .../bindings/iio/light/brcm,apds9999.yaml | 53 +++++++++++++++++++ MAINTAINERS | 6 +++ 2 files changed, 59 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/light/brcm,apds9999.yaml
Documentation/devicetree/bindings/iio/light/brcm,apds9999.yaml new file +53 -0
@@ -0,0 +1,53 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/light/brcm,apds9999.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# +title: Broadcom APDS-9999 Digital Proximity and RGB Sensor + +maintainers: + - Jose A. Perez de Azpillaga <[email protected]> + +description: | + Broadcom APDS-9999 is a digital proximity and RGB sensor with + ambient light sensing (ALS) capability. The device uses individual + R, G, B, and IR channels plus a VCSEL for proximity detection. + + Datasheet: https://docs.broadcom.com/docs/APDS-9999-DS + +properties: + compatible: + enum: + - brcm,apds9999 + + reg: + maxItems: 1 + + vdd-supply: true + + vled-supply: + description: VCSEL power supply + + interrupts: + maxItems: 1 + +additionalProperties: false + +required: + - compatible + - reg + - vdd-supply + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + light-sensor@52 { + compatible = "brcm,apds9999"; + reg = <0x52>; + vdd-supply = <&vdd_reg>; + vled-supply = <&vled_reg>; + }; + };
MAINTAINERS +6 -0
@@ -4302,6 +4302,12 @@ S: Maintained F: Documentation/devicetree/bindings/iio/light/avago,apds9300.yaml F: drivers/iio/light/apds9306.c +BROADCOM APDS9999 AMBIENT LIGHT SENSOR DRIVER +M: Jose A. Perez de Azpillaga <[email protected]> +L: [email protected] +S: Maintained +F: Documentation/devicetree/bindings/iio/light/brcm,apds9999.yaml + AVIA HX711 ANALOG DIGITAL CONVERTER IIO DRIVER M: Andreas Klinger <[email protected]> L: [email protected]
[email protected]
AI reviewRe: [RFC PATCH v2 1/2] dt-bindings: iio: light: add Broadcom APDS9999
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Low] Incorrect alphabetical placement of the new entry in the MAINTAINERS file. --
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] Incorrect alphabetical placement of the new entry in the MAINTAINERS file. --
commit 9c652efb6ad9a7c4e83bfb1ad645000b5ee9d6a4
Author: Jose A. Perez de Azpillaga <[email protected]>
dt-bindings: iio: light: add Broadcom APDS9999
This commit adds Device Tree bindings for the Broadcom APDS9999 ambient light and proximity sensor, and updates the MAINTAINERS file.
[ ... ]
Quoted 7 lines
MAINTAINERS +0
-0@@ -3976,6 +3976,12 @@ S: Maintained F: Documentation/devicetree/bindings/iio/light/avago,apds9300.yaml F: drivers/iio/light/apds9306.c
MAINTAINERS S: Maintained · line 3987
Quoted 10 lines
MAINTAINERS +6
-0+BROADCOM APDS9999 AMBIENT LIGHT SENSOR DRIVER +M: Jose A. Perez de Azpillaga <[email protected]> +L: [email protected] +S: Maintained +F: Documentation/devicetree/bindings/iio/light/brcm,apds9999.yaml + AVIA HX711 ANALOG DIGITAL CONVERTER IIO DRIVER M: Andreas Klinger <[email protected]> L: [email protected]
This isn't a bug, but should this new entry be placed further down in the file to maintain alphabetical order?
It looks like the BROADCOM entry is currently inserted between the AVAGO and AVIA entries, rather than in the B section.
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
Jonathan Cameron
maintainerRe: [RFC PATCH v2 1/2] dt-bindings: iio: light: add Broadcom APDS9999
In reply to Jose A. Perez de AzpillagaQuoted 7 lines · On Wed, 13 May 2026 10:10:03 +0200 "Jose A. Perez de Azpillaga"…
Add Device Tree binding for the Broadcom APDS9999 ambient light and proximity sensor.
The APDS9999 features individual R, G, B, and IR channels with a green channel that uses optical coating to approximate the human eye spectral response for ALS/lux measurements. Proximity and RGB functionality are not yet implemented in the driver.
State in this description a little bit of why you didn't merge it with similar bindings.
Trivial stuff inline.
Quoted 18 lines
Signed-off-by Jose A. Perez de Azpillaga <[email protected]>--- .../bindings/iio/light/brcm,apds9999.yaml | 53 +++++++++++++++++++ MAINTAINERS | 6 +++ 2 files changed, 59 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/light/brcm,apds9999.yamlDocumentation/devicetree/bindings/iio/light/brcm,apds9999.yaml new file +4
-0@@ -0,0 +1,53 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/light/brcm,apds9999.yaml#
Documentation/devicetree/bindings/iio/light/brcm,apds9999.yaml line 14
Quoted 10 lines
Documentation/devicetree/bindings/iio/light/brcm,apds9999.yaml new file +10
-0+$schema: http://devicetree.org/meta-schemas/core.yaml# +title: Broadcom APDS-9999 Digital Proximity and RGB Sensor + +maintainers: + - Jose A. Perez de Azpillaga <[email protected]> + +description: | + Broadcom APDS-9999 is a digital proximity and RGB sensor with + ambient light sensing (ALS) capability. The device uses individual + R, G, B, and IR channels plus a VCSEL for proximity detection.
What's a VCSEL? Spell it out.
Otherwise
Quoted 44 lines
Documentation/devicetree/bindings/iio/light/brcm,apds9999.yaml new file +39
-0+ + Datasheet: https://docs.broadcom.com/docs/APDS-9999-DS + +properties: + compatible: + enum: + - brcm,apds9999 + + reg: + maxItems: 1 + + vdd-supply: true + + vled-supply: + description: VCSEL power supply + + interrupts: + maxItems: 1 + +additionalProperties: false + +required: + - compatible + - reg + - vdd-supply + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + light-sensor@52 { + compatible = "brcm,apds9999"; + reg = <0x52>; + vdd-supply = <&vdd_reg>; + vled-supply = <&vled_reg>; + }; + };MAINTAINERS +0
-0
MAINTAINERS S: Maintained · line 4309
Quoted 8 lines
MAINTAINERS +5
-0@@ -4302,6 +4302,12 @@ S: Maintained F: Documentation/devicetree/bindings/iio/light/avago,apds9300.yaml F: drivers/iio/light/apds9306.c +BROADCOM APDS9999 AMBIENT LIGHT SENSOR DRIVER +M: Jose A. Perez de Azpillaga <[email protected]> +L: [email protected] +S: Maintained +F: Documentation/devicetree/bindings/iio/light/brcm,apds9999.yaml
Sashiko pointed out this should be in alphabetical order. Bit annoying in this particular case but we'll just find someone else moves it if we don't do it from the start.
Quoted 7 lines
MAINTAINERS +1
-0+ AVIA HX711 ANALOG DIGITAL CONVERTER IIO DRIVER M: Andreas Klinger <[email protected]> L: [email protected]
Jose A. Perez de Azpillaga
maintainerRFC PATCH v2 2/2 iio: light: add support for APDS9999 sensor
In reply to Jose A. Perez de AzpillagaAdd IIO driver for Broadcom APDS9999 ambient light sensor.
The APDS9999 is a digital proximity and RGB sensor with ALS capability. This driver implements the ALS/Lux functionality using the green channel, which uses optical coating technology to approximate the human eye spectral response.
Proximity (PS) and RGB color features are not yet implemented.
--- MAINTAINERS | 1 + drivers/iio/light/Kconfig | 10 + drivers/iio/light/Makefile | 1 + drivers/iio/light/apds9999.c | 344 +++++++++++++++++++++++++++++++++++ 4 files changed, 356 insertions(+) create mode 100644 drivers/iio/light/apds9999.c
MAINTAINERS +1 -0
@@ -4307,6 +4307,7 @@ M: Jose A. Perez de Azpillaga <[email protected]> L: [email protected] S: Maintained F: Documentation/devicetree/bindings/iio/light/brcm,apds9999.yaml +F: drivers/iio/light/apds9999.c AVIA HX711 ANALOG DIGITAL CONVERTER IIO DRIVER M: Andreas Klinger <[email protected]>
drivers/iio/light/Kconfig +10 -0
@@ -119,6 +119,16 @@ config APDS9960 To compile this driver as a module, choose M here: the module will be called apds9960 +config APDS9999 + tristate "APDS9999 ambient light sensor" + depends on I2C + help + Say Y here if you want to build support for the Broadcom APDS9999 + ambient light sensor (ALS/Lux). + + To compile this driver as a module, choose M here: the + module will be called apds9999. + config AS73211 tristate "AMS AS73211 XYZ color sensor and AMS AS7331 UV sensor" depends on I2C
drivers/iio/light/Makefile +1 -0
@@ -14,6 +14,7 @@ obj-$(CONFIG_APDS9160) += apds9160.o obj-$(CONFIG_APDS9300) += apds9300.o obj-$(CONFIG_APDS9306) += apds9306.o obj-$(CONFIG_APDS9960) += apds9960.o +obj-$(CONFIG_APDS9999) += apds9999.o obj-$(CONFIG_AS73211) += as73211.o obj-$(CONFIG_BH1745) += bh1745.o obj-$(CONFIG_BH1750) += bh1750.o
drivers/iio/light/apds9999.c new file +344 -0
344 lines not on this page · raw message
Jonathan Cameron
maintainerRe: [RFC PATCH v2 2/2] iio: light: add support for APDS9999 sensor
Quoted 6 lines · On Wed, 13 May 2026 10:10:35 +0200 "Jose A. Perez de Azpillaga"…
Add IIO driver for Broadcom APDS9999 ambient light sensor.
The APDS9999 is a digital proximity and RGB sensor with ALS capability. This driver implements the ALS/Lux functionality using the green channel, which uses optical coating technology to approximate the human eye spectral response.
Can wrap commit messages to 75 chars.
Quoted 2 lines
Proximity (PS) and RGB color features are not yet implemented.
Needs an update (sashiko pointed this out)
https://sashiko.dev/#/patchset/cover.1778659152.git.azpijr%40gmail.com
Quoted 2 lines
Signed-off-by Jose A. Perez de Azpillaga <[email protected]>
A few minor things inline.
Thanks,
Jonathan
drivers/iio/light/apds9999.c line 103
Quoted 12 lines
drivers/iio/light/apds9999.c new file +5
-0@@ -0,0 +1,344 @@ +static int apds9999_init (struct apds9999_data *data) +{ +struct device *dev = &data->client->dev; +struct i2c_client *client = data->client; +u8 reg;
I think regval makes it more obvious this isn't a register address.
Quoted 18 lines
drivers/iio/light/apds9999.c new file +18
-0+int ret; + + ret =devm_add_action_or_reset (dev, apds9999_standby, client); +if (ret) +return ret; + +guard (mutex)(&data->lock); + + reg =FIELD_PREP (APDS9999_LS_RES_MASK ,APDS9999_RES_18BIT ) | +FIELD_PREP (APDS9999_LS_RATE_MASK ,APDS9999_RATE_100_MS ); + ret =i2c_smbus_write_byte_data (client,APDS9999_REG_LS_MEAS_RATE , reg); +if (ret) +return ret; + data->als_res =APDS9999_RES_18BIT ; + data->als_rate =APDS9999_RATE_100_MS ; + + ret =i2c_smbus_write_byte_data (client,APDS9999_REG_LS_GAIN , +APDS9999_GAIN_3X );
drivers/iio/light/apds9999.c line 131
Quoted 10 lines
drivers/iio/light/apds9999.c new file +10
-0+if (ret) +return ret; + data->als_gain_idx =APDS9999_GAIN_3X ; + + ret =i2c_smbus_write_byte_data (client,APDS9999_REG_MAIN_CTRL , +APDS9999_MAIN_CTRL_LS_EN ); +if (ret) +return ret; + +return 0 ;
return i2c_smbus_write_byte_data()
Quoted 9 lines
drivers/iio/light/apds9999.c new file +9
-0+} + +static int apds9999_read_channel (struct apds9999_data *data,u8 reg,u32 *counts) +{ +struct i2c_client *client = data->client; +u8 buf[3 ]; +int ret, tries; + +guard (mutex)(&data->lock);
drivers/iio/light/apds9999.c line 150
Quoted 10 lines
drivers/iio/light/apds9999.c new file +10
-0+ +/* +* Poll MAIN_STATUS for new data. Timeout: ~2 integration periods +* plus margin. Each try sleeps 20 ms. +*/ + tries =max (2 , (apds9999_itimes_us[data->als_res] *2 ) /20000 ); + +while (tries--) { + ret =i2c_smbus_read_byte_data (client, +APDS9999_REG_MAIN_STATUS );
Off by one space and it's under 80 chars anyway (would have been fine if a little over a well).
ret = i2c_smbus_read_byte_data(client, APDS9999_REG_MAIN_STATUS);
Quoted 27 lines
drivers/iio/light/apds9999.c new file +27
-0+if (ret <0 ) +return ret; +if (ret &APDS9999_MAIN_STATUS_LS_DATA ) +break ; +fsleep (20000 ); + } + +if (tries <0 ) +return -ETIMEDOUT ; + + ret =i2c_smbus_read_i2c_block_data (client, reg,sizeof (buf), buf); +if (ret <0 ) +return ret; +if (ret !=sizeof (buf)) +return -EIO ; + + *counts =get_unaligned_le24 (buf) &GENMASK (19 ,0 ); +return 0 ; +} + +static int apds9999_read_raw (struct iio_dev *indio_dev, +struct iio_chan_spec const *chan, +int *val,int *val2,long mask) +{ +struct apds9999_data *data =iio_priv (indio_dev); +int gain, itime_us; +u64 scale_nano;
drivers/iio/light/apds9999.c line 187
Quoted 10 lines
drivers/iio/light/apds9999.c new file +10
-0+u32 counts; +int ret; + +switch (mask) { +case IIO_CHAN_INFO_RAW : +switch (chan->type) { +case IIO_LIGHT : + ret =apds9999_read_channel (data, +APDS9999_REG_LS_DATA_GREEN_0 , + &counts);
if (ret) return ret; *val = (int)counts; return IIO_VAL_COUNT;
(See below)
drivers/iio/light/apds9999.c line 197
Quoted 10 lines
drivers/iio/light/apds9999.c new file +10
-0+break ; +case IIO_INTENSITY : + ret =apds9999_read_channel (data, chan->address, + &counts); +break ; +default : +return -EINVAL ; + } +if (ret) +return ret;
I'd shift this up so all errors are dealt with at source. Means we don't need to read on to see where they are going...
drivers/iio/light/apds9999.c line 199
Quoted 2 lines
drivers/iio/light/apds9999.c new file +2
-0+ *val = (int )counts; +return IIO_VAL_INT ;
Not worth sharing this either given only save a tiny bit.
Quoted 55 lines
drivers/iio/light/apds9999.c new file +55
-0+ +case IIO_CHAN_INFO_SCALE : +/* +* Scale (lux per count) = 54 / (gain * integration_time_ms) +* +* The constant 54 is derived from the datasheet table: +* at gain = 3x, itime = 100 ms -> 0.180 lux/count +* -> C = 0.180 * 3 * 100 = 54 +* +* Expressed as IIO_VAL_INT_PLUS_NANO. +*/ + gain = apds9999_gains[data->als_gain_idx]; + itime_us = apds9999_itimes_us[data->als_res]; + +/* scale_nano = 54e12 / (gain * itime_us) nano-lux/count */ + scale_nano =div_u64 (54000000000000ULL , (u32)(gain * itime_us)); + *val = (int )(scale_nano /NSEC_PER_SEC ); + *val2 = (int )(scale_nano %NSEC_PER_SEC ); +return IIO_VAL_INT_PLUS_NANO ; + +case IIO_CHAN_INFO_INT_TIME : + *val =0 ; + *val2 = apds9999_itimes_us[data->als_res]; +return IIO_VAL_INT_PLUS_MICRO ; + +default : +return -EINVAL ; + } +} + +static const struct iio_chan_spec apds9999_channels[] = { + { + .type =IIO_LIGHT , + .info_mask_separate =BIT (IIO_CHAN_INFO_RAW ) | +BIT (IIO_CHAN_INFO_SCALE ), + .info_mask_shared_by_all =BIT (IIO_CHAN_INFO_INT_TIME ), + }, + { + .type =IIO_INTENSITY , + .modified =1 , + .channel2 =IIO_MOD_LIGHT_RED , + .info_mask_separate =BIT (IIO_CHAN_INFO_RAW ), + .info_mask_shared_by_all =BIT (IIO_CHAN_INFO_INT_TIME ), + .address =APDS9999_REG_LS_DATA_RED_0 , + }, + { + .type =IIO_INTENSITY , + .modified =1 , + .channel2 =IIO_MOD_LIGHT_GREEN , + .info_mask_separate =BIT (IIO_CHAN_INFO_RAW ), + .info_mask_shared_by_all =BIT (IIO_CHAN_INFO_INT_TIME ), + .address =APDS9999_REG_LS_DATA_GREEN_0 , + }, + { + .type =IIO_INTENSITY ,
drivers/iio/light/apds9999.c line 264
Quoted 10 lines
drivers/iio/light/apds9999.c new file +10
-0+ .modified = 1, + .channel2 = IIO_MOD_LIGHT_BLUE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME), + .address = APDS9999_REG_LS_DATA_BLUE_0, + }, + { + .type = IIO_INTENSITY, + .modified = 1, + .channel2 = IIO_MOD_LIGHT_CLEAR,
Why clear rather than IR? Sashiko even noticed this.
drivers/iio/light/apds9999.c line 321
Quoted 9 lines
drivers/iio/light/apds9999.c new file +8
-0+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME), + .address = APDS9999_REG_LS_DATA_IR_0, + }, +}; + +static const struct i2c_device_id apds9999_id[] = { + { "apds9999" },
Whilst not traditionally done, we are moving towards using c99 for this.
{ .name = "apds9999" }preferred. Doesn't actually matter if only a name, but later fields are likely to soon become a union. For consistency and ease of explanation I think always using named structure fields is a good direction to move in.
Quoted 24 lines
drivers/iio/light/apds9999.c new file +23
-0+ { } +}; +MODULE_DEVICE_TABLE (i2c, apds9999_id); + +static const struct of_device_id apds9999_of_match[] = { + { .compatible ="brcm,apds9999" }, + { } +}; +MODULE_DEVICE_TABLE (of, apds9999_of_match); + +static struct i2c_driver apds9999_driver = { + .driver = { + .name ="apds9999" , + .of_match_table = apds9999_of_match, + }, + .probe = apds9999_probe, + .id_table = apds9999_id, +}; +module_i2c_driver (apds9999_driver); + +MODULE_AUTHOR ("Jose A. Perez de Azpillaga <[email protected]>" ); +MODULE_DESCRIPTION ("APDS-9999 Lux Light Sensor IIO Driver" ); +MODULE_LICENSE ("GPL" );
David Lechner
reviewerRe: [RFC PATCH v2 2/2] iio: light: add support for APDS9999 sensor
In reply to Jose A. Perez de AzpillagaQuoted 12 lines · Jose A. Perez de Azpillaga
Add IIO driver for Broadcom APDS9999 ambient light sensor.
The APDS9999 is a digital proximity and RGB sensor with ALS capability. This driver implements the ALS/Lux functionality using the green channel, which uses optical coating technology to approximate the human eye spectral response.
Proximity (PS) and RGB color features are not yet implemented.
drivers/iio/light/apds9999.c line 46
Quoted 7 lines
drivers/iio/light/apds9999.c new file +7
-0+enum apds9999_gain { +APDS9999_GAIN_1X =0 , +APDS9999_GAIN_3X =1 , +APDS9999_GAIN_6X =2 , +APDS9999_GAIN_9X =3 , +APDS9999_GAIN_18X =4 , +};
Giving the natural values of enum explicitly just seems like noise IMHO. I wasted time looking for an anomaly.
Quoted 8 lines
drivers/iio/light/apds9999.c new file +8
-0+ +static const int apds9999_gains[] = { + [APDS9999_GAIN_1X ] =1 , + [APDS9999_GAIN_3X ] =3 , + [APDS9999_GAIN_6X ] =6 , + [APDS9999_GAIN_9X ] =9 , + [APDS9999_GAIN_18X ] =18 , +};
drivers/iio/light/apds9999.c line 64
Quoted 10 lines
drivers/iio/light/apds9999.c new file +10
-0+ +enum apds9999_resolution { +APDS9999_RES_20BIT =0 ,/* 400 ms */ +APDS9999_RES_19BIT =1 ,/* 200 ms */ +APDS9999_RES_18BIT =2 ,/* 100 ms (default) */ +APDS9999_RES_17BIT =3 ,/* 50 ms */ +APDS9999_RES_16BIT =4 ,/* 25 ms */ +APDS9999_RES_13BIT =5 ,/* 3.125 ms */ +APDS9999_RES_NUM +};
Comments seems redundant since we have the table below.
drivers/iio/light/apds9999.c line 67
Quoted 3 lines
drivers/iio/light/apds9999.c new file +3
-0+ +static const int apds9999_itimes_us[APDS9999_RES_NUM ] = { + [APDS9999_RES_20BIT ] =400000 ,
Can be written as 400 * USEC_PER_MSEC to make it easier to understand.
Quoted 5 lines
drivers/iio/light/apds9999.c new file +5
-0+ [APDS9999_RES_19BIT] = 200000, + [APDS9999_RES_18BIT] = 100000, + [APDS9999_RES_17BIT] = 50000, + [APDS9999_RES_16BIT] = 25000, + [APDS9999_RES_13BIT] = 3125,
drivers/iio/light/apds9999.c line 82
Quoted 10 lines
drivers/iio/light/apds9999.c new file +10
-0+}; + +enum apds9999_rate { +APDS9999_RATE_25_MS =0 , +APDS9999_RATE_50_MS =1 , +APDS9999_RATE_100_MS =2 , +APDS9999_RATE_200_MS =3 , +APDS9999_RATE_500_MS =4 , +APDS9999_RATE_1000_MS =5 , +APDS9999_RATE_2000_MS =6 ,
again, values are distracting.
Quoted 28 lines
drivers/iio/light/apds9999.c new file +28
-0+}; + +struct apds9999_data { +struct i2c_client *client; +/* lock: protects als_gain_idx, als_res, als_rate */ +struct mutex lock; +int als_gain_idx; +int als_res; +int als_rate; +}; + +static void apds9999_standby (void *client) +{ +i2c_smbus_write_byte_data (client,APDS9999_REG_MAIN_CTRL ,0 ); +} + +static int apds9999_init (struct apds9999_data *data) +{ +struct device *dev = &data->client->dev; +struct i2c_client *client = data->client; +u8 reg; +int ret; + + ret =devm_add_action_or_reset (dev, apds9999_standby, client); +if (ret) +return ret; + +guard (mutex)(&data->lock);
drivers/iio/light/apds9999.c line 118
Quoted 10 lines
drivers/iio/light/apds9999.c new file +8
-0+ + reg =FIELD_PREP (APDS9999_LS_RES_MASK ,APDS9999_RES_18BIT ) | +FIELD_PREP (APDS9999_LS_RATE_MASK ,APDS9999_RATE_100_MS ); + ret =i2c_smbus_write_byte_data (client,APDS9999_REG_LS_MEAS_RATE , reg); +if (ret) +return ret; + data->als_res =APDS9999_RES_18BIT ; + data->als_rate =APDS9999_RATE_100_MS ;
Can we get a comment explaining why these are the default?
Quoted 107 lines
drivers/iio/light/apds9999.c new file +107
-0+ + ret =i2c_smbus_write_byte_data (client,APDS9999_REG_LS_GAIN , +APDS9999_GAIN_3X ); +if (ret) +return ret; + data->als_gain_idx =APDS9999_GAIN_3X ; + + ret =i2c_smbus_write_byte_data (client,APDS9999_REG_MAIN_CTRL , +APDS9999_MAIN_CTRL_LS_EN ); +if (ret) +return ret; + +return 0 ; +} + +static int apds9999_read_channel (struct apds9999_data *data,u8 reg,u32 *counts) +{ +struct i2c_client *client = data->client; +u8 buf[3 ]; +int ret, tries; + +guard (mutex)(&data->lock); + +/* +* Poll MAIN_STATUS for new data. Timeout: ~2 integration periods +* plus margin. Each try sleeps 20 ms. +*/ + tries =max (2 , (apds9999_itimes_us[data->als_res] *2 ) /20000 ); + +while (tries--) { + ret =i2c_smbus_read_byte_data (client, +APDS9999_REG_MAIN_STATUS ); +if (ret <0 ) +return ret; +if (ret &APDS9999_MAIN_STATUS_LS_DATA ) +break ; +fsleep (20000 ); + } + +if (tries <0 ) +return -ETIMEDOUT ; + + ret =i2c_smbus_read_i2c_block_data (client, reg,sizeof (buf), buf); +if (ret <0 ) +return ret; +if (ret !=sizeof (buf)) +return -EIO ; + + *counts =get_unaligned_le24 (buf) &GENMASK (19 ,0 ); +return 0 ; +} + +static int apds9999_read_raw (struct iio_dev *indio_dev, +struct iio_chan_spec const *chan, +int *val,int *val2,long mask) +{ +struct apds9999_data *data =iio_priv (indio_dev); +int gain, itime_us; +u64 scale_nano; +u32 counts; +int ret; + +switch (mask) { +case IIO_CHAN_INFO_RAW : +switch (chan->type) { +case IIO_LIGHT : + ret =apds9999_read_channel (data, +APDS9999_REG_LS_DATA_GREEN_0 , + &counts); +break ; +case IIO_INTENSITY : + ret =apds9999_read_channel (data, chan->address, + &counts); +break ; +default : +return -EINVAL ; + } +if (ret) +return ret; + *val = (int )counts; +return IIO_VAL_INT ; + +case IIO_CHAN_INFO_SCALE : +/* +* Scale (lux per count) = 54 / (gain * integration_time_ms) +* +* The constant 54 is derived from the datasheet table: +* at gain = 3x, itime = 100 ms -> 0.180 lux/count +* -> C = 0.180 * 3 * 100 = 54 +* +* Expressed as IIO_VAL_INT_PLUS_NANO. +*/ + gain = apds9999_gains[data->als_gain_idx]; + itime_us = apds9999_itimes_us[data->als_res]; + +/* scale_nano = 54e12 / (gain * itime_us) nano-lux/count */ + scale_nano =div_u64 (54000000000000ULL , (u32)(gain * itime_us)); + *val = (int )(scale_nano /NSEC_PER_SEC ); + *val2 = (int )(scale_nano %NSEC_PER_SEC ); +return IIO_VAL_INT_PLUS_NANO ; + +case IIO_CHAN_INFO_INT_TIME : + *val =0 ; + *val2 = apds9999_itimes_us[data->als_res]; +return IIO_VAL_INT_PLUS_MICRO ; + +default :
drivers/iio/light/apds9999.c line 235
Quoted 10 lines
drivers/iio/light/apds9999.c new file +10
-0+return -EINVAL ; + } +} + +static const struct iio_chan_spec apds9999_channels[] = { + { + .type =IIO_LIGHT , + .info_mask_separate =BIT (IIO_CHAN_INFO_RAW ) | +BIT (IIO_CHAN_INFO_SCALE ), + .info_mask_shared_by_all =BIT (IIO_CHAN_INFO_INT_TIME ),
It looks like if we put a .address here, we could simplify the raw read. (Could also use a comment explaining what was discussed about the green channel in the cover letter.)
drivers/iio/light/apds9999.c line 241
Quoted 6 lines
drivers/iio/light/apds9999.c new file +6
-0+ }, + { + .type = IIO_INTENSITY, + .modified = 1, + .channel2 = IIO_MOD_LIGHT_RED, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
No scale on these?
Quoted 22 lines
drivers/iio/light/apds9999.c new file +22
-0+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME), + .address = APDS9999_REG_LS_DATA_RED_0, + }, + { + .type = IIO_INTENSITY, + .modified = 1, + .channel2 = IIO_MOD_LIGHT_GREEN, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME), + .address = APDS9999_REG_LS_DATA_GREEN_0, + }, + { + .type = IIO_INTENSITY, + .modified = 1, + .channel2 = IIO_MOD_LIGHT_BLUE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME), + .address = APDS9999_REG_LS_DATA_BLUE_0, + }, + { + .type = IIO_INTENSITY, + .modified = 1,
drivers/iio/light/apds9999.c line 273
Quoted 10 lines
drivers/iio/light/apds9999.c new file +10
-0+ .channel2 = IIO_MOD_LIGHT_CLEAR, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME), + .address = APDS9999_REG_LS_DATA_IR_0, + }, +}; + +static const struct iio_info apds9999_info = { + .read_raw = apds9999_read_raw, +};
Would be more logical to move this struct right after the function it references.
Quoted 71 lines
drivers/iio/light/apds9999.c new file +71
-0+ +static int apds9999_probe (struct i2c_client *client) +{ +struct device *dev = &client->dev; +struct apds9999_data *data; +struct iio_dev *indio_dev; +int ret, part_id; + + indio_dev =devm_iio_device_alloc (dev,sizeof (*data)); +if (!indio_dev) +return -ENOMEM ; + + data =iio_priv (indio_dev); + data->client = client; + + ret =devm_mutex_init (dev, &data->lock); +if (ret) +return ret; + + part_id =i2c_smbus_read_byte_data (client,APDS9999_REG_PART_ID ); +if (part_id <0 ) +return dev_err_probe (dev, part_id, +"failed to read PART_ID\n" ); +if (part_id !=APDS9999_PART_ID ) +dev_info (dev,"unexpected PART_ID 0x%02x (expected 0x%02x)\n" , + part_id,APDS9999_PART_ID ); + + ret =apds9999_init (data); +if (ret) +return dev_err_probe (dev, ret, +"failed to initialize device\n" ); + + indio_dev->name ="apds9999" ; + indio_dev->info = &apds9999_info; + indio_dev->channels = apds9999_channels; + indio_dev->num_channels =ARRAY_SIZE (apds9999_channels); + indio_dev->modes =INDIO_DIRECT_MODE ; + + ret =devm_iio_device_register (dev, indio_dev); +if (ret) +return dev_err_probe (dev, ret, +"failed to register IIO device\n" ); + +return 0 ; +} + +static const struct i2c_device_id apds9999_id[] = { + {"apds9999" }, + { } +}; +MODULE_DEVICE_TABLE (i2c, apds9999_id); + +static const struct of_device_id apds9999_of_match[] = { + { .compatible ="brcm,apds9999" }, + { } +}; +MODULE_DEVICE_TABLE (of, apds9999_of_match); + +static struct i2c_driver apds9999_driver = { + .driver = { + .name ="apds9999" , + .of_match_table = apds9999_of_match, + }, + .probe = apds9999_probe, + .id_table = apds9999_id, +}; +module_i2c_driver (apds9999_driver); + +MODULE_AUTHOR ("Jose A. Perez de Azpillaga <[email protected]>" ); +MODULE_DESCRIPTION ("APDS-9999 Lux Light Sensor IIO Driver" ); +MODULE_LICENSE ("GPL" );
Jonathan Cameron
maintainerRe: [RFC PATCH v2 2/2] iio: light: add support for APDS9999 sensor
Quoted 13 lines · On Sat, 16 May 2026 10:28:47 -0500 David Lechner <[email protected]> wrote:
Quoted 9 lines · Jose A. Perez de Azpillaga
Add IIO driver for Broadcom APDS9999 ambient light sensor.
The APDS9999 is a digital proximity and RGB sensor with ALS capability. This driver implements the ALS/Lux functionality using the green channel, which uses optical coating technology to approximate the human eye spectral response.
Proximity (PS) and RGB color features are not yet implemented.
Quoted 10 lines
drivers/iio/light/apds9999.c line 46
Quoted 7 lines
drivers/iio/light/apds9999.c new file +7
-0+enum apds9999_gain { +APDS9999_GAIN_1X =0 , +APDS9999_GAIN_3X =1 , +APDS9999_GAIN_6X =2 , +APDS9999_GAIN_9X =3 , +APDS9999_GAIN_18X =4 , +};Giving the natural values of enum explicitly just seems like noise IMHO. I wasted time looking for an anomaly.
I tend to ask for this when they are hardware field values to make the point the values matter rather than just being an enum. I'm not that fussed though.
Jonathan
David Lechner
reviewerRe: [RFC PATCH v2 2/2] iio: light: add support for APDS9999 sensor
Quoted 27 lines · Jonathan Cameron
Quoted 13 lines · On Sat, 16 May 2026 10:28:47 -0500 David Lechner <[email protected]> wrote:
Quoted 9 lines · Jose A. Perez de Azpillaga
Add IIO driver for Broadcom APDS9999 ambient light sensor.
The APDS9999 is a digital proximity and RGB sensor with ALS capability. This driver implements the ALS/Lux functionality using the green channel, which uses optical coating technology to approximate the human eye spectral response.
Proximity (PS) and RGB color features are not yet implemented.
Quoted 10 lines
drivers/iio/light/apds9999.c line 46
Quoted 7 lines
drivers/iio/light/apds9999.c new file +7
-0+enum apds9999_gain { +APDS9999_GAIN_1X =0 , +APDS9999_GAIN_3X =1 , +APDS9999_GAIN_6X =2 , +APDS9999_GAIN_9X =3 , +APDS9999_GAIN_18X =4 , +};Giving the natural values of enum explicitly just seems like noise IMHO. I wasted time looking for an anomaly.
Quoted 5 lines
I tend to ask for this when they are hardware field values to make the point the values matter rather than just being an enum. I'm not that fussed though.
Jonathan
In that case, I would just use #define instead of enum.
Jonathan Cameron
maintainerRe: [RFC PATCH v2 2/2] iio: light: add support for APDS9999 sensor
Quoted 28 lines · On Sat, 16 May 2026 13:19:28 -0500 David Lechner <[email protected]> wrote:
Quoted 27 lines · Jonathan Cameron
Quoted 23 lines · On Sat, 16 May 2026 10:28:47 -0500 David Lechner <[email protected]> wrote:
Quoted 12 lines · Jose A. Perez de Azpillaga
Add IIO driver for Broadcom APDS9999 ambient light sensor.
The APDS9999 is a digital proximity and RGB sensor with ALS capability. This driver implements the ALS/Lux functionality using the green channel, which uses optical coating technology to approximate the human eye spectral response.
Proximity (PS) and RGB color features are not yet implemented.
drivers/iio/light/apds9999.c line 46
Quoted 7 lines
drivers/iio/light/apds9999.c new file +7
-0+enum apds9999_gain { +APDS9999_GAIN_1X =0 , +APDS9999_GAIN_3X =1 , +APDS9999_GAIN_6X =2 , +APDS9999_GAIN_9X =3 , +APDS9999_GAIN_18X =4 , +};Giving the natural values of enum explicitly just seems like noise IMHO. I wasted time looking for an anomaly.
Quoted 7 lines
Quoted 5 lines
I tend to ask for this when they are hardware field values to make the point the values matter rather than just being an enum. I'm not that fussed though.
Jonathan
In that case, I would just use #define instead of enum.
Likewise unless the type is actually used.
For that matter if type is not used, should perhaps be an anon enum but that tends to also indicate defines are more appropriate.
Jonathan
Quoted 1 line
Jonathan Cameron
maintainerQuoted 9 lines · On Wed, 13 May 2026 10:09:02 +0200 "Jose A. Perez de Azpillaga"…
Add IIO driver and DT binding for the Broadcom APDS9999 ambient light and proximity sensor. The green channel uses optical coating technology to approximate the human eye spectral response for ALS/lux measurements.
IIO_INTENSITYchannels are also provided for red, green, blue, and clear/IR so userspace can compute its own weighted lux.Questions for reviewers:
1. Is the green channel as an ALS/lux approximation acceptable given the datasheet's claim of optical coating to emulate V(lambda)?
It's the best we can do and the datasheet does say to do this. So whilst I'll express some doubts, it's fine. I think you shout about it enough that we shouldn't get that copied into other places.
Quoted 2 lines
2. Is the
IIO_INTENSITYchannel layout correct? (red, green, blue, clear with modifiers, no scale)
They sometimes still have scale if there is a variable gain amplifier we need to control / express the value of. Looks like that might apply here.
However, it's the one channel where the ABI docs make it clear there is not meaningful unit from _raw * _scale. Just that if raw doubles and scale halves we should get the same value.
Quoted 2 lines
3. Should the binding be merged with avago,apds9300.yaml or kept separate given the extra vled-supply?
I'm fine with this either way. Marginal case.
With those answered, you can drop the RFC for v3 ;)
Jonathan
Quoted 32 lines
v2:
- AddedIIO_INTENSITYchannels for R, G, B, clear
- Switched to guard(mutex)(),devm_mutex_init(),dev_err_probe()
- Replacedremove()withdevm_add_action_or_reset()
- Don't fail probe onPART_IDmismatch (fallback compatibles)
- Replaced manual shifts withFIELD_PREP()/ GENMASK
- Usedget_unaligned_le24(), sizeof(buf),NSEC_PER_SEC,fsleep()
- Named gain register values, explicit resolution enum values
- Removed section comments, grouped bit defines under registers
- Squashed Kconfig/Makefile into driver patch
- SplitMAINTAINERSentry across patches
- DT binding: added vled-supply, interrupts, required vdd-supply
- DT binding: fixed title, added blank lines, full description
- Dropped "DT binding" duplication from binding commit subjectLink to v1: https://lore.kernel.org/linux-iio/[email protected]/
Jose A. Perez de Azpillaga (2):
dt-bindings: iio: light: add Broadcom APDS9999
iio: light: add support for APDS9999 sensor.../bindings/iio/light/brcm,apds9999.yaml | 53 +++ MAINTAINERS | 7 + drivers/iio/light/Kconfig | 10 + drivers/iio/light/Makefile | 1 + drivers/iio/light/apds9999.c | 344 ++++++++++++++++++ 5 files changed, 415 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/light/brcm,apds9999.yaml create mode 100644 drivers/iio/light/apds9999.c