* [PATCH 1/3] dt-bindings: iio: light: opt4001: add opt4041
2026-10-23 7:48 [PATCH 0/3] This series adds support for Texas Instruments OPT4041 Ambient light sensor Jesper X Nilsson
@ 2026-10-23 7:48 ` Jesper X Nilsson
2026-10-23 7:48 ` [PATCH 2/3] iio: light: opt4001: refactor driver to prepare to support more devices Jesper X Nilsson
` (2 subsequent siblings)
3 siblings, 0 replies; 13+ messages in thread
From: Jesper X Nilsson @ 2026-10-23 7:48 UTC (permalink / raw)
To: Jonathan Cameron, David Lechner, Nuno Sá,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Stefan Windfeldt-Prytz
Cc: linux-iio, devicetree, linux-kernel, Jesper X Nilsson, kernel
Add a new compatible for opt4041. The opt4041 is only available in the
SOT-5X3 package. The device is pin compatible with the opt4001-sot-5x3
and shares the same properties. The opt4041 has one additional channel
compared to opt4001, the extra channel has a wide spectral bandwidth
ranging from 380nm to 1000nm.
Datasheet: https://www.ti.com/lit/gpn/opt4041
Signed-off-by: Jesper X Nilsson <jesper.x.nilsson@axis.com>
---
Documentation/devicetree/bindings/iio/light/ti,opt4001.yaml | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/Documentation/devicetree/bindings/iio/light/ti,opt4001.yaml b/Documentation/devicetree/bindings/iio/light/ti,opt4001.yaml
index 12b0c7ed5d72..35e9b0deb944 100644
--- a/Documentation/devicetree/bindings/iio/light/ti,opt4001.yaml
+++ b/Documentation/devicetree/bindings/iio/light/ti,opt4001.yaml
@@ -4,7 +4,7 @@
$id: http://devicetree.org/schemas/iio/light/ti,opt4001.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
-title: Texas Instruments OPT4001 Ambient Light Sensor
+title: Texas Instruments OPT4001/OPT4041 Ambient Light Sensor
maintainers:
- Stefan Windfeldt-Prytz <stefan.windfeldt-prytz@axis.com>
@@ -13,13 +13,16 @@ description:
Ambient light sensor with an i2c interface.
Last part of compatible is for the packaging used.
Picostar is a 4 pinned SMT and sot-5x3 is a 8 pinned SOT.
+ OPT4041 is only available as sot-5x3 and adds a wide band channel.
https://www.ti.com/lit/gpn/opt4001
+ https://www.ti.com/lit/gpn/opt4041
properties:
compatible:
enum:
- ti,opt4001-picostar
- ti,opt4001-sot-5x3
+ - ti,opt4041-sot-5x3
reg:
maxItems: 1
@@ -39,7 +42,9 @@ allOf:
properties:
compatible:
contains:
- const: ti,opt4001-sot-5x3
+ enum:
+ - ti,opt4001-sot-5x3
+ - ti,opt4041-sot-5x3
then:
properties:
interrupts:
--
2.43.0
^ permalink raw reply [flat|nested] 13+ messages in thread* [PATCH 2/3] iio: light: opt4001: refactor driver to prepare to support more devices
2026-10-23 7:48 [PATCH 0/3] This series adds support for Texas Instruments OPT4041 Ambient light sensor Jesper X Nilsson
2026-10-23 7:48 ` [PATCH 1/3] dt-bindings: iio: light: opt4001: add opt4041 Jesper X Nilsson
@ 2026-10-23 7:48 ` Jesper X Nilsson
2026-10-23 9:07 ` Andy Shevchenko
2026-10-23 9:10 ` Joshua Crofts
2026-10-23 7:48 ` [PATCH 3/3] iio: light: opt4001: add support for opt4041 Jesper X Nilsson
2026-10-23 9:17 ` [PATCH 0/3] This series adds support for Texas Instruments OPT4041 Ambient light sensor Andy Shevchenko
3 siblings, 2 replies; 13+ messages in thread
From: Jesper X Nilsson @ 2026-10-23 7:48 UTC (permalink / raw)
To: Jonathan Cameron, David Lechner, Nuno Sá,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Stefan Windfeldt-Prytz
Cc: linux-iio, devicetree, linux-kernel, Jesper X Nilsson, kernel
Prepare opt4001 driver to be able to support OPT4041. Extend struct
opt4001_chip_info with fields channels and device_id because OPT4041 has
two channels and another device id.
Datasheet: https://www.ti.com/lit/gpn/opt4041
Assisted-by: LLM
Signed-off-by: Jesper X Nilsson <jesper.x.nilsson@axis.com>
---
drivers/iio/light/opt4001.c | 77 +++++++++++++++++++++++++++++----------------
1 file changed, 50 insertions(+), 27 deletions(-)
diff --git a/drivers/iio/light/opt4001.c b/drivers/iio/light/opt4001.c
index aa3d87995b7c..bf5dc1c8514c 100644
--- a/drivers/iio/light/opt4001.c
+++ b/drivers/iio/light/opt4001.c
@@ -78,6 +78,9 @@ struct opt4001_chip_info {
int mul;
int div;
const char *name;
+ const struct iio_chan_spec *channels;
+ int num_channels;
+ u16 device_id;
};
struct opt4001_chip {
@@ -87,16 +90,32 @@ struct opt4001_chip {
const struct opt4001_chip_info *chip_info;
};
+static const struct iio_chan_spec opt4001_channels[] = {
+ {
+ .type = IIO_LIGHT,
+ .address = OPT4001_LIGHT1_MSB,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME),
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME)
+ },
+};
+
static const struct opt4001_chip_info opt4001_sot_5x3_info = {
.mul = 4375,
.div = 10000000,
- .name = "opt4001-sot-5x3"
+ .name = "opt4001-sot-5x3",
+ .channels = opt4001_channels,
+ .num_channels = ARRAY_SIZE(opt4001_channels),
+ .device_id = OPT4001_DEVICE_ID_VAL,
};
static const struct opt4001_chip_info opt4001_picostar_info = {
.mul = 3125,
.div = 10000000,
- .name = "opt4001-picostar"
+ .name = "opt4001-picostar",
+ .channels = opt4001_channels,
+ .num_channels = ARRAY_SIZE(opt4001_channels),
+ .device_id = OPT4001_DEVICE_ID_VAL,
};
static const int opt4001_int_time_available[][2] = {
@@ -159,10 +178,9 @@ static u8 opt4001_calculate_crc(u8 exp, u32 mantissa, u8 count)
return crc;
}
-static int opt4001_read_lux_value(struct iio_dev *indio_dev,
- int *val, int *val2)
+static int opt4001_read_raw_value(struct opt4001_chip *chip, unsigned int reg,
+ u64 *raw_value)
{
- struct opt4001_chip *chip = iio_priv(indio_dev);
struct device *dev = &chip->client->dev;
unsigned int light1;
unsigned int light2;
@@ -172,17 +190,16 @@ static int opt4001_read_lux_value(struct iio_dev *indio_dev,
u8 count;
u8 crc;
u8 calc_crc;
- u64 lux_raw;
- u32 rem;
+ u32 mantissa;
int ret;
- ret = regmap_read(chip->regmap, OPT4001_LIGHT1_MSB, &light1);
+ ret = regmap_read(chip->regmap, reg, &light1);
if (ret < 0) {
dev_err(dev, "Failed to read data bytes");
return ret;
}
- ret = regmap_read(chip->regmap, OPT4001_LIGHT1_LSB, &light2);
+ ret = regmap_read(chip->regmap, reg + 1, &light2);
if (ret < 0) {
dev_err(dev, "Failed to read data bytes");
return ret;
@@ -193,12 +210,27 @@ static int opt4001_read_lux_value(struct iio_dev *indio_dev,
crc = FIELD_GET(OPT4001_CRC_MASK, light2);
msb = FIELD_GET(OPT4001_MSB_MASK, light1);
lsb = FIELD_GET(OPT4001_LSB_MASK, light2);
- lux_raw = (msb << 8) + lsb;
- calc_crc = opt4001_calculate_crc(exp, lux_raw, count);
+ mantissa = (msb << 8) + lsb;
+ calc_crc = opt4001_calculate_crc(exp, mantissa, count);
if (calc_crc != crc)
return -EIO;
- lux_raw = lux_raw << exp;
+ *raw_value = (u64)mantissa << exp;
+
+ return 0;
+}
+
+static int opt4001_read_lux_value(struct opt4001_chip *chip, unsigned int reg,
+ int *val, int *val2)
+{
+ u64 lux_raw;
+ u32 rem;
+ int ret;
+
+ ret = opt4001_read_raw_value(chip, reg, &lux_raw);
+ if (ret)
+ return ret;
+
lux_raw = lux_raw * chip->chip_info->mul;
*val = div_u64_rem(lux_raw, chip->chip_info->div, &rem);
*val2 = rem * 100;
@@ -233,15 +265,6 @@ static void opt4001_chip_off_action(void *data)
dev_err(&chip->client->dev, "Failed to power down\n");
}
-static const struct iio_chan_spec opt4001_channels[] = {
- {
- .type = IIO_LIGHT,
- .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
- .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME),
- .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME)
- },
-};
-
static int opt4001_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
@@ -250,7 +273,7 @@ static int opt4001_read_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_PROCESSED:
- return opt4001_read_lux_value(indio_dev, val, val2);
+ return opt4001_read_lux_value(chip, chan->address, val, val2);
case IIO_CHAN_INFO_INT_TIME:
*val = 0;
*val2 = opt4001_int_time_reg[chip->int_time][0];
@@ -388,19 +411,19 @@ static int opt4001_probe(struct i2c_client *client)
return dev_err_probe(&client->dev, ret,
"failed to reinit regmap cache\n");
+ chip->chip_info = i2c_get_match_data(client);
+
ret = regmap_read(chip->regmap, OPT4001_DEVICE_ID, &dev_id);
if (ret < 0)
return dev_err_probe(&client->dev, ret,
"Failed to read the device ID register\n");
dev_id = FIELD_GET(OPT4001_DEVICE_ID_MASK, dev_id);
- if (dev_id != OPT4001_DEVICE_ID_VAL)
+ if (dev_id != chip->chip_info->device_id)
dev_warn(&client->dev, "Device ID: %#04x unknown\n", dev_id);
- chip->chip_info = i2c_get_match_data(client);
-
- indio_dev->channels = opt4001_channels;
- indio_dev->num_channels = ARRAY_SIZE(opt4001_channels);
+ indio_dev->channels = chip->chip_info->channels;
+ indio_dev->num_channels = chip->chip_info->num_channels;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->name = chip->chip_info->name;
--
2.43.0
^ permalink raw reply [flat|nested] 13+ messages in thread* Re: [PATCH 2/3] iio: light: opt4001: refactor driver to prepare to support more devices
2026-10-23 7:48 ` [PATCH 2/3] iio: light: opt4001: refactor driver to prepare to support more devices Jesper X Nilsson
@ 2026-10-23 9:07 ` Andy Shevchenko
2026-10-23 10:07 ` Jesper X Nilsson
2026-10-23 9:10 ` Joshua Crofts
1 sibling, 1 reply; 13+ messages in thread
From: Andy Shevchenko @ 2026-10-23 9:07 UTC (permalink / raw)
To: Jesper X Nilsson
Cc: Jonathan Cameron, David Lechner, Nuno Sá,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Stefan Windfeldt-Prytz, linux-iio, devicetree, linux-kernel,
kernel
On Fri, Oct 23, 2026 at 09:48:14AM +0200, Jesper X Nilsson wrote:
> Prepare opt4001 driver to be able to support OPT4041. Extend struct
> opt4001_chip_info with fields channels and device_id because OPT4041 has
> two channels and another device id.
...
> Assisted-by: LLM
Drop this tag.
...
> +static const struct iio_chan_spec opt4001_channels[] = {
> + {
> + .type = IIO_LIGHT,
> + .address = OPT4001_LIGHT1_MSB,
> + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
> + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME),
> + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME)
While at it (and I see it was in the original code), add a trailing comma here.
> + },
> +};
...
> u8 count;
> u8 crc;
> u8 calc_crc;
> - u64 lux_raw;
> - u32 rem;
> + u32 mantissa;
Try to put it to make more of reversed xmas tree order.
> int ret;
...
> + chip->chip_info = i2c_get_match_data(client);
For now it requires
if (!chip->chip_info)
return -ENODATA;
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 13+ messages in thread* Re: [PATCH 2/3] iio: light: opt4001: refactor driver to prepare to support more devices
2026-10-23 9:07 ` Andy Shevchenko
@ 2026-10-23 10:07 ` Jesper X Nilsson
0 siblings, 0 replies; 13+ messages in thread
From: Jesper X Nilsson @ 2026-10-23 10:07 UTC (permalink / raw)
To: Andy Shevchenko, Jesper X Nilsson
Cc: Jonathan Cameron, David Lechner, Nuno Sá,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Stefan Windfeldt-Prytz, linux-iio, devicetree, linux-kernel,
kernel
On 10/23/26 11:07, Andy Shevchenko wrote:
> [Some people who received this message don't often get email from andriy.shevchenko@intel.com. Learn why this is important at https://aka.ms/LearnAboutSenderIdentification ]
>
> On Fri, Oct 23, 2026 at 09:48:14AM +0200, Jesper X Nilsson wrote:
>> Prepare opt4001 driver to be able to support OPT4041. Extend struct
>> opt4001_chip_info with fields channels and device_id because OPT4041 has
>> two channels and another device id.
>
> ...
>
>> Assisted-by: LLM
>
> Drop this tag.
>
I will drop the tag in v2.> ...
>
>> +static const struct iio_chan_spec opt4001_channels[] = {
>> + {
>> + .type = IIO_LIGHT,
>> + .address = OPT4001_LIGHT1_MSB,
>> + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
>> + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME),
>> + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME)
>
> While at it (and I see it was in the original code), add a trailing comma here.
>
I will add trailing comma in v2.
>> + },
>> +};
>
> ...
>
>> u8 count;
>> u8 crc;
>> u8 calc_crc;
>> - u64 lux_raw;
>> - u32 rem;
>> + u32 mantissa;
>
> Try to put it to make more of reversed xmas tree order.
>
I will fix it in v2.
>> int ret;
>
> ...
>
>> + chip->chip_info = i2c_get_match_data(client);
>
> For now it requires
>
> if (!chip->chip_info)
> return -ENODATA;
>
I will add the if check in v2.
> --
> With Best Regards,
> Andy Shevchenko
Thanks for the fast review!
Best regards,
Jesper
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 2/3] iio: light: opt4001: refactor driver to prepare to support more devices
2026-10-23 7:48 ` [PATCH 2/3] iio: light: opt4001: refactor driver to prepare to support more devices Jesper X Nilsson
2026-10-23 9:07 ` Andy Shevchenko
@ 2026-10-23 9:10 ` Joshua Crofts
2026-10-23 9:16 ` Joshua Crofts
1 sibling, 1 reply; 13+ messages in thread
From: Joshua Crofts @ 2026-10-23 9:10 UTC (permalink / raw)
To: Jesper X Nilsson
Cc: Jonathan Cameron, David Lechner, Nuno Sá,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Stefan Windfeldt-Prytz, linux-iio, devicetree, linux-kernel,
kernel
On Fri, 23 Oct 2026 09:48:14 +0200
Jesper X Nilsson <jesper.x.nilsson@axis.com> wrote:
> Prepare opt4001 driver to be able to support OPT4041. Extend struct
> opt4001_chip_info with fields channels and device_id because OPT4041 has
> two channels and another device id.
>
> Datasheet: https://www.ti.com/lit/gpn/opt4041
> Assisted-by: LLM
This tag has been deprecated.
> Signed-off-by: Jesper X Nilsson <jesper.x.nilsson@axis.com>
...
> @@ -388,19 +411,19 @@ static int opt4001_probe(struct i2c_client *client)
> return dev_err_probe(&client->dev, ret,
> "failed to reinit regmap cache\n");
>
> + chip->chip_info = i2c_get_match_data(client);
+ null check and return -ENODATA on failure.
--
Kind regards,
Joshua Crofts
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 2/3] iio: light: opt4001: refactor driver to prepare to support more devices
2026-10-23 9:10 ` Joshua Crofts
@ 2026-10-23 9:16 ` Joshua Crofts
2026-10-23 10:22 ` Jesper X Nilsson
0 siblings, 1 reply; 13+ messages in thread
From: Joshua Crofts @ 2026-10-23 9:16 UTC (permalink / raw)
To: Jesper X Nilsson
Cc: Jonathan Cameron, David Lechner, Nuno Sá,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Stefan Windfeldt-Prytz, linux-iio, devicetree, linux-kernel,
kernel
On Fri, 23 Oct 2026 11:10:32 +0200
Joshua Crofts <joshua.crofts1@gmail.com> wrote:
> On Fri, 23 Oct 2026 09:48:14 +0200
> Jesper X Nilsson <jesper.x.nilsson@axis.com> wrote:
>
> > Prepare opt4001 driver to be able to support OPT4041. Extend struct
> > opt4001_chip_info with fields channels and device_id because OPT4041 has
> > two channels and another device id.
> >
> > Datasheet: https://www.ti.com/lit/gpn/opt4041
> > Assisted-by: LLM
>
> This tag has been deprecated.
>
> > Signed-off-by: Jesper X Nilsson <jesper.x.nilsson@axis.com>
>
> ...
>
> > @@ -388,19 +411,19 @@ static int opt4001_probe(struct i2c_client *client)
> > return dev_err_probe(&client->dev, ret,
> > "failed to reinit regmap cache\n");
> >
> > + chip->chip_info = i2c_get_match_data(client);
>
> + null check and return -ENODATA on failure.
>
Just noticed that Andy was faster :)
--
Kind regards,
Joshua Crofts
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 2/3] iio: light: opt4001: refactor driver to prepare to support more devices
2026-10-23 9:16 ` Joshua Crofts
@ 2026-10-23 10:22 ` Jesper X Nilsson
0 siblings, 0 replies; 13+ messages in thread
From: Jesper X Nilsson @ 2026-10-23 10:22 UTC (permalink / raw)
To: Joshua Crofts, Jesper X Nilsson
Cc: Jonathan Cameron, David Lechner, Nuno Sá,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Stefan Windfeldt-Prytz, linux-iio, devicetree, linux-kernel,
kernel
On 10/23/26 11:16, Joshua Crofts wrote:
> On Fri, 23 Oct 2026 11:10:32 +0200
> Joshua Crofts <joshua.crofts1@gmail.com> wrote:
>
>> On Fri, 23 Oct 2026 09:48:14 +0200
>> Jesper X Nilsson <jesper.x.nilsson@axis.com> wrote:
>>
>>> Prepare opt4001 driver to be able to support OPT4041. Extend struct
>>> opt4001_chip_info with fields channels and device_id because OPT4041 has
>>> two channels and another device id.
>>>
>>> Datasheet: https://www.ti.com/lit/gpn/opt4041
>>> Assisted-by: LLM
>>
>> This tag has been deprecated.
>>
>>> Signed-off-by: Jesper X Nilsson <jesper.x.nilsson@axis.com>
>>
>> ...
>>
>>> @@ -388,19 +411,19 @@ static int opt4001_probe(struct i2c_client *client)
>>> return dev_err_probe(&client->dev, ret,
>>> "failed to reinit regmap cache\n");
>>>
>>> + chip->chip_info = i2c_get_match_data(client);
>>
>> + null check and return -ENODATA on failure.
>>
>
> Just noticed that Andy was faster :)
>
Yes, you were both fast :)
Thanks for the review!
Best regards,
Jesper
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 3/3] iio: light: opt4001: add support for opt4041
2026-10-23 7:48 [PATCH 0/3] This series adds support for Texas Instruments OPT4041 Ambient light sensor Jesper X Nilsson
2026-10-23 7:48 ` [PATCH 1/3] dt-bindings: iio: light: opt4001: add opt4041 Jesper X Nilsson
2026-10-23 7:48 ` [PATCH 2/3] iio: light: opt4001: refactor driver to prepare to support more devices Jesper X Nilsson
@ 2026-10-23 7:48 ` Jesper X Nilsson
2026-10-23 9:14 ` Andy Shevchenko
2026-10-23 9:17 ` [PATCH 0/3] This series adds support for Texas Instruments OPT4041 Ambient light sensor Andy Shevchenko
3 siblings, 1 reply; 13+ messages in thread
From: Jesper X Nilsson @ 2026-10-23 7:48 UTC (permalink / raw)
To: Jonathan Cameron, David Lechner, Nuno Sá,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Stefan Windfeldt-Prytz
Cc: linux-iio, devicetree, linux-kernel, Jesper X Nilsson, kernel
Extend the opt4001 driver to also support OPT4041. The devices are very
similar, with the difference being OPT4041 having two channels, one
visible and one wide band channel ranging from 380nm to 1000nm.
The register map is almost the same. OPT4001 has registers to read out
the last four values sampled. OPT4041 maps to the same registers, but
has two channels, saving the last two samples for each channel.
Datasheet: https://www.ti.com/lit/gpn/opt4041
Assisted-by: LLM
Signed-off-by: Jesper X Nilsson <jesper.x.nilsson@axis.com>
---
drivers/iio/light/Kconfig | 2 +-
drivers/iio/light/opt4001.c | 67 ++++++++++++++++++++++++++++++++++++++++++---
2 files changed, 64 insertions(+), 5 deletions(-)
diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig
index 462fff1ddef2..625ebb10ad1b 100644
--- a/drivers/iio/light/Kconfig
+++ b/drivers/iio/light/Kconfig
@@ -508,7 +508,7 @@ config OPT4001
select REGMAP_I2C
help
If you say Y or M here, you get support for Texas Instruments
- OPT4001 Ambient Light Sensor.
+ OPT4001 and OPT4041 Ambient Light Sensors.
If built as a dynamically linked module, it will be called
opt4001.
diff --git a/drivers/iio/light/opt4001.c b/drivers/iio/light/opt4001.c
index bf5dc1c8514c..ff142591fcba 100644
--- a/drivers/iio/light/opt4001.c
+++ b/drivers/iio/light/opt4001.c
@@ -2,9 +2,11 @@
/*
* Copyright (C) 2023 Axis Communications AB
*
- * Datasheet: https://www.ti.com/lit/gpn/opt4001
+ * Datasheets:
+ * https://www.ti.com/lit/gpn/opt4001
+ * https://www.ti.com/lit/gpn/opt4041
*
- * Device driver for the Texas Instruments OPT4001.
+ * Device driver for the Texas Instruments OPT4001 and OPT4041.
*/
#include <linux/bitfield.h>
@@ -19,6 +21,8 @@
/* OPT4001 register set */
#define OPT4001_LIGHT1_MSB 0x00
#define OPT4001_LIGHT1_LSB 0x01
+#define OPT4041_LIGHT2_MSB 0x02
+#define OPT4041_LIGHT2_LSB 0x03
#define OPT4001_CTRL 0x0A
#define OPT4001_DEVICE_ID 0x11
@@ -43,6 +47,7 @@
/* OPT4001 constants */
#define OPT4001_DEVICE_ID_VAL 0x121
+#define OPT4041_DEVICE_ID_VAL 0x221
/* OPT4001 operating modes */
#define OPT4001_CTRL_OPER_MODE_OFF 0x0
@@ -72,7 +77,7 @@
/*
* The different packaging of OPT4001 has different constants used when calculating
- * lux values.
+ * lux values and OPT4041 has one additional wide band channel.
*/
struct opt4001_chip_info {
int mul;
@@ -100,6 +105,25 @@ static const struct iio_chan_spec opt4001_channels[] = {
},
};
+static const struct iio_chan_spec opt4041_channels[] = {
+ {
+ .type = IIO_LIGHT,
+ .address = OPT4001_LIGHT1_MSB,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME),
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME)
+ },
+ {
+ .type = IIO_INTENSITY,
+ .modified = 1,
+ .channel2 = IIO_MOD_LIGHT_BOTH,
+ .address = OPT4041_LIGHT2_MSB,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME),
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME)
+ },
+};
+
static const struct opt4001_chip_info opt4001_sot_5x3_info = {
.mul = 4375,
.div = 10000000,
@@ -118,6 +142,15 @@ static const struct opt4001_chip_info opt4001_picostar_info = {
.device_id = OPT4001_DEVICE_ID_VAL,
};
+static const struct opt4001_chip_info opt4041_sot_5x3_info = {
+ .mul = 5850,
+ .div = 10000000,
+ .name = "opt4041-sot-5x3",
+ .channels = opt4041_channels,
+ .num_channels = ARRAY_SIZE(opt4041_channels),
+ .device_id = OPT4041_DEVICE_ID_VAL,
+};
+
static const int opt4001_int_time_available[][2] = {
{ 0, 600 },
{ 0, 1000 },
@@ -270,10 +303,30 @@ static int opt4001_read_raw(struct iio_dev *indio_dev,
int *val, int *val2, long mask)
{
struct opt4001_chip *chip = iio_priv(indio_dev);
+ u64 raw_value;
+ u32 rem;
+ int ret;
switch (mask) {
case IIO_CHAN_INFO_PROCESSED:
return opt4001_read_lux_value(chip, chan->address, val, val2);
+ case IIO_CHAN_INFO_RAW:
+ ret = opt4001_read_raw_value(chip, chan->address, &raw_value);
+ if (ret)
+ return ret;
+
+ *val = div_u64_rem(raw_value, 100, &rem);
+ *val2 = rem * 10000;
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_SCALE:
+ /*
+ * OPT4041 CH1: datasheet specifies 0.1923 nW/cm^2 per ADC code
+ * with a 520 nm stimulus. Convert to nW/m^2 (x10000) and
+ * compensate for dividing the reported raw value by 100 (x100).
+ * The resulting scale is 192300 nW/m^2 per reported unit.
+ */
+ *val = 192300;
+ return IIO_VAL_INT;
case IIO_CHAN_INFO_INT_TIME:
*val = 0;
*val2 = opt4001_int_time_reg[chip->int_time][0];
@@ -340,6 +393,8 @@ static bool opt4001_readable_reg(struct device *dev, unsigned int reg)
switch (reg) {
case OPT4001_LIGHT1_MSB:
case OPT4001_LIGHT1_LSB:
+ case OPT4041_LIGHT2_MSB:
+ case OPT4041_LIGHT2_LSB:
case OPT4001_CTRL:
case OPT4001_DEVICE_ID:
return true;
@@ -363,6 +418,8 @@ static bool opt4001_volatile_reg(struct device *dev, unsigned int reg)
switch (reg) {
case OPT4001_LIGHT1_MSB:
case OPT4001_LIGHT1_LSB:
+ case OPT4041_LIGHT2_MSB:
+ case OPT4041_LIGHT2_LSB:
return true;
default:
return false;
@@ -448,6 +505,7 @@ static int opt4001_probe(struct i2c_client *client)
static const struct i2c_device_id opt4001_id[] = {
{ .name = "opt4001-sot-5x3", .driver_data = (kernel_ulong_t)&opt4001_sot_5x3_info },
{ .name = "opt4001-picostar", .driver_data = (kernel_ulong_t)&opt4001_picostar_info },
+ { .name = "opt4041-sot-5x3", .driver_data = (kernel_ulong_t)&opt4041_sot_5x3_info },
{ }
};
MODULE_DEVICE_TABLE(i2c, opt4001_id);
@@ -455,6 +513,7 @@ MODULE_DEVICE_TABLE(i2c, opt4001_id);
static const struct of_device_id opt4001_of_match[] = {
{ .compatible = "ti,opt4001-sot-5x3", .data = &opt4001_sot_5x3_info},
{ .compatible = "ti,opt4001-picostar", .data = &opt4001_picostar_info},
+ { .compatible = "ti,opt4041-sot-5x3", .data = &opt4041_sot_5x3_info},
{ }
};
MODULE_DEVICE_TABLE(of, opt4001_of_match);
@@ -470,5 +529,5 @@ static struct i2c_driver opt4001_driver = {
module_i2c_driver(opt4001_driver);
MODULE_AUTHOR("Stefan Windfeldt-Prytz <stefan.windfeldt-prytz@axis.com>");
-MODULE_DESCRIPTION("Texas Instruments opt4001 ambient light sensor driver");
+MODULE_DESCRIPTION("Texas Instruments opt4001 and opt4041 ambient light sensor driver");
MODULE_LICENSE("GPL");
--
2.43.0
^ permalink raw reply [flat|nested] 13+ messages in thread* Re: [PATCH 3/3] iio: light: opt4001: add support for opt4041
2026-10-23 7:48 ` [PATCH 3/3] iio: light: opt4001: add support for opt4041 Jesper X Nilsson
@ 2026-10-23 9:14 ` Andy Shevchenko
2026-10-23 10:19 ` Jesper X Nilsson
0 siblings, 1 reply; 13+ messages in thread
From: Andy Shevchenko @ 2026-10-23 9:14 UTC (permalink / raw)
To: Jesper X Nilsson
Cc: Jonathan Cameron, David Lechner, Nuno Sá,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Stefan Windfeldt-Prytz, linux-iio, devicetree, linux-kernel,
kernel
On Fri, Oct 23, 2026 at 09:48:15AM +0200, Jesper X Nilsson wrote:
> Extend the opt4001 driver to also support OPT4041. The devices are very
> similar, with the difference being OPT4041 having two channels, one
> visible and one wide band channel ranging from 380nm to 1000nm.
>
> The register map is almost the same. OPT4001 has registers to read out
> the last four values sampled. OPT4041 maps to the same registers, but
> has two channels, saving the last two samples for each channel.
...
> Assisted-by: LLM
Drop this.
...
> help
> If you say Y or M here, you get support for Texas Instruments
> - OPT4001 Ambient Light Sensor.
> + OPT4001 and OPT4041 Ambient Light Sensors.
We in IIO established kinda a style for this to be scalable, id est list them
one-per-line:
If you say Y or M here, you get support for Texas Instruments
Ambient Light Sensors:
- OPT4001 (...can add some short useful detail here...)
- OPT4041 (...can add some short useful detail here...)
> If built as a dynamically linked module, it will be called
> opt4001.
Perhaps also consider changing summary text at tristate to make it like
"...OPT4001 and compatible".
...
> - * Device driver for the Texas Instruments OPT4001.
> + * Device driver for the Texas Instruments OPT4001 and OPT4041.
Make this and MODULE_DESCRIPTION() to be the same as summary in the Kconfig,
see above.
> */
...
> +static const struct iio_chan_spec opt4041_channels[] = {
> + {
> + .type = IIO_LIGHT,
> + .address = OPT4001_LIGHT1_MSB,
> + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
> + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME),
> + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME)
Trailing comma.
> + },
> + {
> + .type = IIO_INTENSITY,
> + .modified = 1,
> + .channel2 = IIO_MOD_LIGHT_BOTH,
> + .address = OPT4041_LIGHT2_MSB,
> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
> + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME),
> + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME)
Ditto.
> + },
> +};
...
> +static const struct opt4001_chip_info opt4041_sot_5x3_info = {
> + .mul = 5850,
> + .div = 10000000,
Just sent a patch to move this to use struct s32_fract, I believe you may
incorporate it into your series as a precursor).
> + .name = "opt4041-sot-5x3",
> + .channels = opt4041_channels,
> + .num_channels = ARRAY_SIZE(opt4041_channels),
> + .device_id = OPT4041_DEVICE_ID_VAL,
> +};
...
> +MODULE_DESCRIPTION("Texas Instruments opt4001 and opt4041 ambient light sensor driver");
See above.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 13+ messages in thread* Re: [PATCH 3/3] iio: light: opt4001: add support for opt4041
2026-10-23 9:14 ` Andy Shevchenko
@ 2026-10-23 10:19 ` Jesper X Nilsson
0 siblings, 0 replies; 13+ messages in thread
From: Jesper X Nilsson @ 2026-10-23 10:19 UTC (permalink / raw)
To: Andy Shevchenko, Jesper X Nilsson
Cc: Jonathan Cameron, David Lechner, Nuno Sá,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Stefan Windfeldt-Prytz, linux-iio, devicetree, linux-kernel,
kernel
On 10/23/26 11:14, Andy Shevchenko wrote:
> On Fri, Oct 23, 2026 at 09:48:15AM +0200, Jesper X Nilsson wrote:
>> Extend the opt4001 driver to also support OPT4041. The devices are very
>> similar, with the difference being OPT4041 having two channels, one
>> visible and one wide band channel ranging from 380nm to 1000nm.
>>
>> The register map is almost the same. OPT4001 has registers to read out
>> the last four values sampled. OPT4041 maps to the same registers, but
>> has two channels, saving the last two samples for each channel.
>
> ...
>
>> Assisted-by: LLM
>
> Drop this.
>
I will drop the tag in v2.
> ...
>
>> help
>> If you say Y or M here, you get support for Texas Instruments
>> - OPT4001 Ambient Light Sensor.
>> + OPT4001 and OPT4041 Ambient Light Sensors.
>
> We in IIO established kinda a style for this to be scalable, id est list them
> one-per-line:
>
> If you say Y or M here, you get support for Texas Instruments
> Ambient Light Sensors:
> - OPT4001 (...can add some short useful detail here...)
> - OPT4041 (...can add some short useful detail here...)
>
I will change to this style in v2.
>> If built as a dynamically linked module, it will be called
>> opt4001.
>
> Perhaps also consider changing summary text at tristate to make it like
> "...OPT4001 and compatible".
>
Good suggestion, will add it in v2.
> ...
>
>> - * Device driver for the Texas Instruments OPT4001.
>> + * Device driver for the Texas Instruments OPT4001 and OPT4041.
>
> Make this and MODULE_DESCRIPTION() to be the same as summary in the Kconfig,
> see above.
>
I will update the MODULE_DESCRIPTION() in v2.
>> */
>
> ...
>
>> +static const struct iio_chan_spec opt4041_channels[] = {
>> + {
>> + .type = IIO_LIGHT,
>> + .address = OPT4001_LIGHT1_MSB,
>> + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
>> + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME),
>> + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME)
>
> Trailing comma.
>
>> + },
>> + {
>> + .type = IIO_INTENSITY,
>> + .modified = 1,
>> + .channel2 = IIO_MOD_LIGHT_BOTH,
>> + .address = OPT4041_LIGHT2_MSB,
>> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
>> + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME),
>> + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME)
>
> Ditto.
>
>> + },
>> +};
>
> ...
>
>> +static const struct opt4001_chip_info opt4041_sot_5x3_info = {
>> + .mul = 5850,
>> + .div = 10000000,
>
> Just sent a patch to move this to use struct s32_fract, I believe you may
> incorporate it into your series as a precursor).
>
I will add the patch to my series.
>> + .name = "opt4041-sot-5x3",
>> + .channels = opt4041_channels,
>> + .num_channels = ARRAY_SIZE(opt4041_channels),
>> + .device_id = OPT4041_DEVICE_ID_VAL,
>> +};
>
> ...
>
>> +MODULE_DESCRIPTION("Texas Instruments opt4001 and opt4041 ambient light sensor driver");
>
> See above.
>
Thanks for the fast review!
Best regards,
Jesper
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 0/3] This series adds support for Texas Instruments OPT4041 Ambient light sensor
2026-10-23 7:48 [PATCH 0/3] This series adds support for Texas Instruments OPT4041 Ambient light sensor Jesper X Nilsson
` (2 preceding siblings ...)
2026-10-23 7:48 ` [PATCH 3/3] iio: light: opt4001: add support for opt4041 Jesper X Nilsson
@ 2026-10-23 9:17 ` Andy Shevchenko
2026-10-23 10:20 ` Jesper X Nilsson
3 siblings, 1 reply; 13+ messages in thread
From: Andy Shevchenko @ 2026-10-23 9:17 UTC (permalink / raw)
To: Jesper X Nilsson
Cc: Jonathan Cameron, David Lechner, Nuno Sá,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Stefan Windfeldt-Prytz, linux-iio, devicetree, linux-kernel,
kernel
On Fri, Oct 23, 2026 at 09:48:12AM +0200, Jesper X Nilsson wrote:
> Extend the opt4001 driver to support OPT4041, since it is so similar to
> OPT4001. The OPT4041 adds a second channel, a wide band channel ranging
> from 380nm to 1000nm. The register map is almost the same for both
> devices. OPT4001 has an internal fifo of size four, OPT4041 has two
> fifos of size two, one for each channel.
>
> Since OPT4041 has two channels it has one bit in register 0Bh to select
> which channel is compared to the threshold register. The driver does not
> support setting the threshold registers, so no new support has been
> added for this.
>
> The OPT4041 is pin compatible and has the same package as
> opt4001-sot-5x3. Although the device id is different, I added a new
> compatible string since the driver already had different compatible
> strings to differentiate between the two OPT4001.
Overall very good small series, I have a few comments, but they like 15 minutes
to address, id est mostly style related. I'll give my tag if you send a v2 with
the changes as suggested.
> Datasheet: https://www.ti.com/lit/gpn/opt4041
>
No blank lines in the tag block :-)
> Signed-off-by: Jesper X Nilsson <jesper.x.nilsson@axis.com>
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 13+ messages in thread* Re: [PATCH 0/3] This series adds support for Texas Instruments OPT4041 Ambient light sensor
2026-10-23 9:17 ` [PATCH 0/3] This series adds support for Texas Instruments OPT4041 Ambient light sensor Andy Shevchenko
@ 2026-10-23 10:20 ` Jesper X Nilsson
0 siblings, 0 replies; 13+ messages in thread
From: Jesper X Nilsson @ 2026-10-23 10:20 UTC (permalink / raw)
To: Andy Shevchenko, Jesper X Nilsson
Cc: Jonathan Cameron, David Lechner, Nuno Sá,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Stefan Windfeldt-Prytz, linux-iio, devicetree, linux-kernel,
kernel
On 10/23/26 11:17, Andy Shevchenko wrote:
> On Fri, Oct 23, 2026 at 09:48:12AM +0200, Jesper X Nilsson wrote:
>> Extend the opt4001 driver to support OPT4041, since it is so similar to
>> OPT4001. The OPT4041 adds a second channel, a wide band channel ranging
>> from 380nm to 1000nm. The register map is almost the same for both
>> devices. OPT4001 has an internal fifo of size four, OPT4041 has two
>> fifos of size two, one for each channel.
>>
>> Since OPT4041 has two channels it has one bit in register 0Bh to select
>> which channel is compared to the threshold register. The driver does not
>> support setting the threshold registers, so no new support has been
>> added for this.
>>
>> The OPT4041 is pin compatible and has the same package as
>> opt4001-sot-5x3. Although the device id is different, I added a new
>> compatible string since the driver already had different compatible
>> strings to differentiate between the two OPT4001.
>
> Overall very good small series, I have a few comments, but they like 15 minutes
> to address, id est mostly style related. I'll give my tag if you send a v2 with
> the changes as suggested.
>
>> Datasheet: https://www.ti.com/lit/gpn/opt4041
>
>>
>
> No blank lines in the tag block :-)
>
I will update it in v2.
>> Signed-off-by: Jesper X Nilsson <jesper.x.nilsson@axis.com>
>
Best regards,
Jesper
^ permalink raw reply [flat|nested] 13+ messages in thread