Support for Texas Instruments OPT4060 RGBW Color sensor.
Subsystems (2)
v4 cover Support for Texas Instruments OPT4060 RGBW Color sensor.
This patch series adds support for Texas Instruments OPT4060 RGBW Color sensor using the i2c interface.
The driver exposes both raw adc values and sensor compensated values through sysfs. Integration time can be configured through sysfs as well. The OPT4060 sensor supports both rising and falling threshold interrupts. These interrupts are exposed as IIO events. The driver also implements an IIO triggered buffer with two triggers, one trigger for conversion ready interrupts and one trigger for threshold interrupts. The typical use case for this is to define a threshold and listen for the events, and at the same time enable the triggered buffer with the threshold trigger. Once the application gets the threshold event, the values from the time of the event will be available in the triggered buffer. This limits the number of interrupts between sensor and host and also the the usage of sysfs for reading values after events.
Changes in v4:
- Fix for a warning found by test robot in opt4060_write_event(...).
- Correction of early return in opt4060_write_event(...) and opt4060_read_event(...), missed from review of version 2.
- Correction of timeout for sample conversion.
- Correction of bug when changing integration time with buffer enabled.
- Link to V3: https://lore.kernel.org/lkml/[email protected]/
Changes in v3:
- Cover letter: Removed lux from description.
- OPT_4060_DRV_NAME define removed.
- Corrected alignment for struct opt4060_buffer.
- Added description of the CRC calculation.
- Cleaned variable declaration in several places.
- Added a path for the non-irq case in opt4060_read_chan_value(...).
- Added a description of processed values.
- Use of regmap_clear_bits in opt4060_power_down(...).
- Switched to IIO_INTENSITY instead of IIO_LIGHT.
- Correction of channel index in IIO_UNMOD_EVENT_CODE, found by test robot.
- Added iio_chan_spec for the non-irq case without events.
- Fixed braces in a few if-else statements.
- Refactoring with early returns in a few places to reduce indentation.
- Replaced for_each_set_bit with iio_for_each_active_channel.
- Removed various too obvious comments.
- Fixed various other code style problems.
- Link to V2: https://lore.kernel.org/lkml/[email protected]/
Changes in v2:
- dt-bindings: Removed incorrect allOf.
- dt-bindings: Changed to generic node name.
- Correction in opt4060_trigger_one_shot(...) for continuous mode.
- Correction in opt4060_power_down(...), wrong register was read.
- Corrected usage of active_scan_mask in opt4060_trigger_handler(...).
- Clean-up of various comments.
- Link to V1: https://lore.kernel.org/lkml/[email protected]/
Per-Daniel Olsson (2):
dt-bindings: iio: light: Document TI OPT4060 RGBW sensor
iio: light: Add support for TI OPT4060 color sensor
.../bindings/iio/light/ti,opt4060.yaml | 51 + drivers/iio/light/Kconfig | 13 + drivers/iio/light/Makefile | 1 + drivers/iio/light/opt4060.c | 1259 +++++++++++++++++ 4 files changed, 1324 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/light/ti,opt4060.yaml create mode 100644 drivers/iio/light/opt4060.c
base-commit: c964ced7726294d40913f2127c3f185a92cb4a41 --
2.39.5
PATCH v4 1/2 dt-bindings: iio: light: Document TI OPT4060 RGBW sensor
Add devicetree bindings for the OPT4060 RGBW color sensor.
--- .../bindings/iio/light/ti,opt4060.yaml | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/light/ti,opt4060.yaml
Documentation/devicetree/bindings/iio/light/ti,opt4060.yaml new file +51 -0
@@ -0,0 +1,51 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/light/ti,opt4060.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments OPT4060 RGBW Color Sensor + +maintainers: + - Per-Daniel Olsson <[email protected]> + +description: + Texas Instrument RGBW high resolution color sensor over I2C. + https://www.ti.com/lit/gpn/opt4060 + +properties: + compatible: + enum: + - ti,opt4060 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + vdd-supply: + description: Regulator that provides power to the sensor. + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/irq.h> + i2c { + #address-cells = <1>; + #size-cells = <0>; + + light-sensor@44 { + compatible = "ti,opt4060"; + reg = <0x44>; + vdd-supply = <&vdd_reg>; + interrupt-parent = <&gpio5>; + interrupts = <13 IRQ_TYPE_EDGE_FALLING>; + }; + }; +...
Signature · 1 line
2.39.5
PATCH v4 2/2 iio: light: Add support for TI OPT4060 color sensor
In reply to Per-Daniel OlssonAdd support for Texas Instruments OPT4060 RGBW Color sensor.
--- drivers/iio/light/Kconfig | 13 + drivers/iio/light/Makefile | 1 + drivers/iio/light/opt4060.c | 1259 +++++++++++++++++++++++++++++++++++ 3 files changed, 1273 insertions(+) create mode 100644 drivers/iio/light/opt4060.c
drivers/iio/light/Kconfig +13 -0
@@ -490,6 +490,19 @@ config OPT4001 If built as a dynamically linked module, it will be called opt4001. +config OPT4060 + tristate "Texas Instruments OPT4060 RGBW Color Sensor" + depends on I2C + select REGMAP_I2C + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + If you say Y or M here, you get support for Texas Instruments + OPT4060 RGBW Color Sensor. + + If built as a dynamically linked module, it will be called + opt4060. + config PA12203001 tristate "TXC PA12203001 light and proximity sensor" depends on I2C
drivers/iio/light/Makefile +1 -0
@@ -42,6 +42,7 @@ obj-$(CONFIG_MAX44009) += max44009.o obj-$(CONFIG_NOA1305) += noa1305.o obj-$(CONFIG_OPT3001) += opt3001.o obj-$(CONFIG_OPT4001) += opt4001.o +obj-$(CONFIG_OPT4060) += opt4060.o obj-$(CONFIG_PA12203001) += pa12203001.o obj-$(CONFIG_ROHM_BU27008) += rohm-bu27008.o obj-$(CONFIG_ROHM_BU27034) += rohm-bu27034.o
drivers/iio/light/opt4060.c new file +1259 -0
1,259 lines not on this page · raw message
Signature · 1 line
2.39.5
Jonathan Cameron
maintainerRe: [PATCH v4 2/2] iio: light: Add support for TI OPT4060 color sensor
Quoted 3 lines · On Wed, 16 Oct 2024 23:34:09 +0200 Per-Daniel Olsson…
Add support for Texas Instruments OPT4060 RGBW Color sensor.
Signed-off-by Per-Daniel Olsson <[email protected]>
Hi Per-Daniel,
Comments inline.
Jonathan
Quoted 6 lines
drivers/iio/light/opt4060.c new file +0
-0@@ -0,0 +1,1259 @@
...
drivers/iio/light/opt4060.c line 146
Quoted 4 lines
drivers/iio/light/opt4060.c new file +4
-0+ +struct opt4060_buffer { +u32 chan[OPT4060_NUM_CHANS ]; +s64 ts__aligned(8) ;
aligned_s64 is now available in linux-next + the IIO tree.
drivers/iio/light/opt4060.c line 151
Quoted 5 lines
drivers/iio/light/opt4060.c new file +5
-0+}; + +static const struct opt4060_channel_factor opt4060_channel_factors[] = { + { +/* RED 2.4 * 2.15e-3 */
This needs more details on wrt to what standard etc.
The datasheet is a little vague, but it seems to me like TI invented their own standard. To use this stuff in a consistent ABI we need to have a common standard or at least an approximation of one.
The illuminance estimates from some devices are bad approximations, but they are at least attempting to approximate a well defined standard.
Quoted 25 lines
drivers/iio/light/opt4060.c new file +24
-0+ .mul = 516, + .div = 100000, + }, { + /* GREEN 1.0 * 2.15e-3 */ + .mul = 215, + .div = 100000, + }, { + /* BLUE 1.3 * 2.15e-3 */ + .mul = 258, + .div = 100000, + }, { + /* CLEAR 1.0 * 2.15e-3 */ + .mul = 215, + .div = 100000, + }, +}; + +static int opt4060_trigger_one_shot(struct opt4060_chip *chip) +{ + int ret; + unsigned int ctrl_reg, oper_mode; + + /* Enable interrupt for conversion ready of all channels */ + ret = opt4060_set_int_state(chip, OPT4060_INT_CTRL_ALL_CH);
drivers/iio/light/opt4060.c line 265
Quoted 10 lines
drivers/iio/light/opt4060.c new file +10
-0+if (ret) +return ret; + + ret =regmap_read (chip->regmap,OPT4060_CTRL , &ctrl_reg); +if (ret <0 ) { +dev_err (chip->dev,"Failed to read ctrl register\n" ); +return ret; + } + + oper_mode =FIELD_GET (OPT4060_CTRL_OPER_MODE_MASK , ctrl_reg);
You are only using this once. Just put it inline and flip the logic
if (FIELD_GET(....) == OPT4060_CTRL_OPER_MODE_CONTINUOUS) return 0; ctrl_reg...
Quoted 14 lines
drivers/iio/light/opt4060.c new file +14
-0+ +/* If the device is already in continuous mode, one-shot is not needed. */ +if (oper_mode !=OPT4060_CTRL_OPER_MODE_CONTINUOUS ) { + ctrl_reg &= ~OPT4060_CTRL_OPER_MODE_MASK ; + ctrl_reg |=FIELD_PREP (OPT4060_CTRL_OPER_MODE_MASK , +OPT4060_CTRL_OPER_MODE_ONE_SHOT ); + +/* Trigger a new conversion by writing to CRTL register. */ + ret =regmap_write (chip->regmap,OPT4060_CTRL , ctrl_reg); +if (ret) +dev_err (chip->dev,"Failed to set ctrl register\n" ); + } +return ret; +}
drivers/iio/light/opt4060.c line 310
Quoted 10 lines
drivers/iio/light/opt4060.c new file +9
-0+static int opt4060_read_raw_value (struct opt4060_chip *chip, +unsigned long address,u32 *raw) +{ +int ret; +u32 result, mantissa_raw; +u16 msb, lsb; +u8 exp, count, crc, calc_crc; + + ret =regmap_bulk_read (chip->regmap, address, &result,2 );
You have the channel types for buffered access as little endian which is not what this code will generate.
If this bulk read is little endian, then you need to read into an appropriate type buffer and convert that to CPU endian before doing any of the maths.
Then the channel spec needs to be native endian not little endian.
drivers/iio/light/opt4060.c line 312
Quoted 2 lines
drivers/iio/light/opt4060.c new file +2
-0+if (ret) { +dev_err (chip->dev,"Reading channel data failed\n" );
return here.
drivers/iio/light/opt4060.c
Quoted 1 line
drivers/iio/light/opt4060.c new file +1
-0+ }else {
All this then doesn't need to be indented.
drivers/iio/light/opt4060.c line 326
Quoted 13 lines
drivers/iio/light/opt4060.c new file +13
-0+ count =FIELD_GET (OPT4060_COUNTER_MASK , result >>16 ); + crc =FIELD_GET (OPT4060_CRC_MASK , result >>16 ); + lsb =FIELD_GET (OPT4060_LSB_MASK , result >>16 ); + exp =FIELD_GET (OPT4060_EXPONENT_MASK , result); + msb =FIELD_GET (OPT4060_MSB_MASK , result); + mantissa_raw = (msb <<8 ) + lsb; + calc_crc =opt4060_calculate_crc (exp, mantissa_raw, count); +if (calc_crc != crc) +return -EIO ; + + *raw = mantissa_raw << exp; + } +return ret;
return 0.
Quoted 16 lines
drivers/iio/light/opt4060.c new file +16
-0+} + +static int opt4060_read_chan_value(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, bool processed) +{ + struct opt4060_chip *chip = iio_priv(indio_dev); + u32 adc_raw; + int ret; + /* + * The conversion time should be 500us startup time plus the integration time + * times the number of channels. An exact timeout isn't critical, it's better + * not to get incorrect errors in the log. Setting the timeout to double the + * theoretical time plus and extra 100ms margin. + */ + unsigned int timeout_us = (500 + OPT4060_NUM_CHANS *
drivers/iio/light/opt4060.c line 352
Quoted 10 lines
drivers/iio/light/opt4060.c new file +10
-0+ opt4060_int_time_reg[chip->int_time][0]) * 2 + 100000; + +if (chip->irq) { +reinit_completion (&chip->completion); + ret =opt4060_trigger_one_shot (chip); +if (ret) +return ret; +if (wait_for_completion_timeout (&chip->completion, +usecs_to_jiffies (timeout_us)) ==0 ) +dev_err (chip->dev,"Completion timed out.\n" );
If so, return an appropriate error value, don't just carry on.
Quoted 15 lines
drivers/iio/light/opt4060.c new file +15
-0+ }else { +unsigned int ready; + + ret =opt4060_trigger_one_shot (chip); +if (ret) +return ret; + + ret =regmap_read_poll_timeout (chip->regmap,OPT4060_RES_CTRL , + ready, (ready &OPT4060_RES_CTRL_CONV_READY ), +1000 , timeout_us); +if (ret) +dev_err (chip->dev,"Conversion ready did not finish within timeout.\n" ); + } + + ret =opt4060_read_raw_value (chip, chan->address, &adc_raw);
drivers/iio/light/opt4060.c line 377
Quoted 10 lines
drivers/iio/light/opt4060.c new file +10
-0+if (ret) { +dev_err (chip->dev,"Reading raw channel data failed\n" ); +return ret; + } +if (processed) { +/* +* The processed value are multiplied by factors that correspond to optical +* parameters in the sensor. The processed values gives more correct comparison +* between channels. +*/
No to these. We discussed this in review thread of earlier version. There is no standard unit of comparison defined so this ABI is not something that userspace can currently understand.
I'm not against introducing one, though it would need a bunch of investigation into what the appropriate way to define this is. TI have chosen an option that I don't recall seeing used by anyone else but maybe I'm wrong on that. There are several competing RGB standards. https://en.wikipedia.org/wiki/RGB_color_spaces
provides some info
Also, thse are linear scales, so if we support them in the future it would be via IIO_CHAN_INFO_SCALE not _PROCESSED.
Quoted 6 lines
drivers/iio/light/opt4060.c new file +6
-0+u32 lux_raw, rem; +u32 mul = opt4060_channel_factors[chan->scan_index].mul; +u32 div = opt4060_channel_factors[chan->scan_index].div; + + lux_raw = adc_raw * mul; + *val =div_u64_rem (lux_raw, div, &rem);
drivers/iio/light/opt4060.c
Quoted 10 lines
drivers/iio/light/opt4060.c new file +7
-0+ *val2 = rem *div_u64 (div,10 ); +return IIO_VAL_INT_PLUS_NANO ; + } + *val = adc_raw; +return IIO_VAL_INT ; +} +static int opt4060_trigger_set_state (struct iio_trigger *trig,bool state)
This should be just enabling or disabling the hardware trigger, not modifying anything else about the state tracking in the IIO core etc so if you need to do that, maybe we have a bug in the core.
drivers/iio/light/opt4060.c line 627
Quoted 8 lines
drivers/iio/light/opt4060.c new file +8
-0+{ +struct iio_dev *indio_dev =iio_trigger_get_drvdata (trig); +struct opt4060_chip *chip =iio_priv (indio_dev); +int ret =0 ; + +if (indio_dev->trig) { +iio_trigger_put (indio_dev->trig); + indio_dev->trig =NULL ;
You should not touch indio_dev->trig directly. Why do you think it is needed? If there is another driver doing that, then which one as it's probably a bug.
drivers/iio/light/opt4060.c line 631
Quoted 4 lines
drivers/iio/light/opt4060.c new file +4
-0+ } + +if (state) { + indio_dev->trig =iio_trigger_get (trig);
This should all be handled via the sysfs attribute handling in the core. You should not need to modify the assigned trigger or do any reference counting.
The only use for setting this when there is only one valid trigger and you want to initialize the driver using it at probe. After that, don't touch indio-dev->trigger from your driver.
drivers/iio/light/opt4060.c line 635
Quoted 4 lines
drivers/iio/light/opt4060.c new file +4
-0+ ret =opt4060_set_continous_mode (chip, true); + }else if (!opt4060_event_active (chip) && chip->irq) { + ret =opt4060_set_continous_mode (chip, false); + }
If these failed. Return an error. Don't try to just carry on.
Quoted 22 lines
drivers/iio/light/opt4060.c new file +21
-0+ +if (trig == chip->data_trig) { + chip->data_trigger_active = state; + chip->threshold_trigger_active = !state; + }else if (trig == chip->threshold_trig) { + chip->data_trigger_active = !state; + chip->threshold_trigger_active = state; + } + +return ret; +} + +static int opt4060_read_event_config (struct iio_dev *indio_dev, +const struct iio_chan_spec *chan, +enum iio_event_type type, +enum iio_event_direction dir) +{ +int ch_sel, ch_idx = chan->scan_index; +struct opt4060_chip *chip =iio_priv (indio_dev); + +if (chan->type !=IIO_INTENSITY )
drivers/iio/light/opt4060.c
Quoted 10 lines
drivers/iio/light/opt4060.c new file +10
-0+return -EINVAL ; +if (type !=IIO_EV_TYPE_THRESH ) +return -EINVAL ; + +if (opt4060_get_channel_sel (chip, &ch_sel)) +return -EFAULT ; + +if (((dir ==IIO_EV_DIR_FALLING ) && chip->thresh_event_lo_active) || + ((dir ==IIO_EV_DIR_RISING ) && chip->thresh_event_hi_active)) +return (ch_sel == ch_idx);
Brackets not needed
return ch_sel == ch_idx;
is fine.
drivers/iio/light/opt4060.c line 860
Quoted 2 lines
drivers/iio/light/opt4060.c new file +2
-0+ +return FALSE;
false.
Check for any more of these.
Quoted 15 lines
drivers/iio/light/opt4060.c new file +15
-0+} + +static int opt4060_write_event_config (struct iio_dev *indio_dev, +const struct iio_chan_spec *chan, +enum iio_event_type type, +enum iio_event_direction dir,int state) +{ +int ch_sel, ch_idx = chan->scan_index; +struct opt4060_chip *chip =iio_priv (indio_dev); + +if (chan->type !=IIO_INTENSITY ) +return -EINVAL ; +if (type !=IIO_EV_TYPE_THRESH ) +return -EINVAL ; +
drivers/iio/light/opt4060.c line 885
Quoted 10 lines
drivers/iio/light/opt4060.c new file +10
-0+if (opt4060_get_channel_sel (chip, &ch_sel)) +return -EFAULT ; + +if (state) { +/* Only one channel can be active at the same time */ +if ((chip->thresh_event_lo_active || + chip->thresh_event_hi_active) && (ch_idx != ch_sel)) +return -EBUSY ; +if (dir ==IIO_EV_DIR_FALLING ) + chip->thresh_event_lo_active = TRUE;
true
drivers/iio/light/opt4060.c
Quoted 2 lines
drivers/iio/light/opt4060.c new file +2
-0+else if (dir ==IIO_EV_DIR_RISING ) + chip->thresh_event_hi_active = TRUE;
true
drivers/iio/light/opt4060.c
Quoted 2 lines
drivers/iio/light/opt4060.c new file +2
-0+if (opt4060_set_channel_sel (chip, ch_idx)) +return -EFAULT ;
Don't eat the return value. Return whatever the function gives you if it is an error. May convey more useful information.
drivers/iio/light/opt4060.c line 901
Quoted 12 lines
drivers/iio/light/opt4060.c new file +12
-0+ }else { +if (ch_idx == ch_sel) { +if (dir ==IIO_EV_DIR_FALLING ) + chip->thresh_event_lo_active = FALSE; +else if (dir ==IIO_EV_DIR_RISING ) + chip->thresh_event_hi_active = FALSE; + } + } + +if (opt4060_event_set_state (chip, chip->thresh_event_hi_active | + chip->thresh_event_lo_active)) +return -EFAULT ;
As above. Avoid eating error values from lower levels. Check for other instances of this as I won't comment on more.
Quoted 2 lines
drivers/iio/light/opt4060.c new file +2
-0+return 0 ; +}
...
Quoted 11 lines
drivers/iio/light/opt4060.c new file +11
-0+static int opt4060_load_defaults (struct opt4060_chip *chip) +{ +u16 reg; +int ret; + + chip->int_time =OPT4060_DEFAULT_CONVERSION_TIME ; + +/* Set initial MIN/MAX thresholds */ + ret =opt4060_set_thresholds (chip,0 ,UINT_MAX ); +if (ret) +return ret;
drivers/iio/light/opt4060.c line 936
Quoted 10 lines
drivers/iio/light/opt4060.c new file +10
-0+ +/* +* Setting auto-range, latched window for thresholds, one-shot conversion +* and quick wake-up mode as default. +*/ + reg =FIELD_PREP (OPT4060_CTRL_RANGE_MASK , +OPT4060_CTRL_LIGHT_SCALE_AUTO ); + reg |=FIELD_PREP (OPT4060_CTRL_CONV_TIME_MASK , chip->int_time); + reg |=FIELD_PREP (OPT4060_CTRL_OPER_MODE_MASK , +OPT4060_CTRL_OPER_MODE_ONE_SHOT );
As below - this seems to be where the device is turned on, so...
drivers/iio/light/opt4060.c line 942
Quoted 6 lines
drivers/iio/light/opt4060.c new file +6
-0+ reg |=OPT4060_CTRL_QWAKE_MASK |OPT4060_CTRL_LATCH_MASK ; + + ret =regmap_write (chip->regmap,OPT4060_CTRL , reg); +if (ret) +dev_err (chip->dev,"Failed to set configuration\n" ); +
I'd really expect to see the devm_add_action_or_reset() that turns it off again here (or immediately after handling any errors from this function at the caller).
drivers/iio/light/opt4060.c line 1016
Quoted 9 lines
drivers/iio/light/opt4060.c new file +8
-0+return ret; +} + +static int opt4060_buffer_preenable (struct iio_dev *idev) +{ +struct opt4060_chip *chip =iio_priv (idev); + +return opt4060_trigger_one_shot (chip);
This needs a comment. Not obvious why a preenable would trigger anything.
Quoted 12 lines
drivers/iio/light/opt4060.c new file +12
-0+} + +static const struct iio_buffer_setup_ops opt4060_buffer_ops = { + .preenable = opt4060_buffer_preenable, +}; + +static irqreturn_t opt4060_irq_thread (int irq,void *private) +{ +struct iio_dev *idev = private; +struct opt4060_chip *chip =iio_priv (idev); +int ret, dummy; +unsigned int int_res;
drivers/iio/light/opt4060.c line 1038
Quoted 10 lines
drivers/iio/light/opt4060.c new file +10
-0+ + ret =regmap_read (chip->regmap,OPT4060_RES_CTRL , &int_res); +if (ret <0 ) { +dev_err (chip->dev,"Failed to read interrupt reasons.\n" ); +return IRQ_NONE ; + } + +if (!chip->data_trigger_active) { +/* Only trigger interrupts on thresholds */ + ret =opt4060_set_int_state (chip,OPT4060_INT_CTRL_THRESHOLD );
This looks unusual. It seems to be modifying the interrupt
control register in an interrupt handler. Writing a status register to clear is fine but this doesn't make much sense to me as you are changing what generates an interrupt in the handler for that interrupt.
Quoted 88 lines
drivers/iio/light/opt4060.c new file +88
-0+if (ret) { +dev_err (chip->dev,"Failed to set interrupt state\n" ); +return IRQ_NONE ; + } + } + +/* Read OPT4060_CTRL to clear interrupt */ + ret =regmap_read (chip->regmap,OPT4060_CTRL , &dummy); +if (ret <0 ) { +dev_err (chip->dev,"Failed to clear interrupt\n" ); +return IRQ_NONE ; + } + +/* Handle events */ +if (int_res & (OPT4060_RES_CTRL_FLAG_H |OPT4060_RES_CTRL_FLAG_L )) { +u64 code; +int chan =0 ; + + ret =opt4060_get_channel_sel (chip, &chan); +if (ret) { +dev_err (chip->dev,"Failed to read threshold channel.\n" ); +return IRQ_NONE ; + } + +/* Check if the interrupt is from the lower threshold */ +if (int_res &OPT4060_RES_CTRL_FLAG_L ) { + code =IIO_UNMOD_EVENT_CODE (IIO_INTENSITY , + chan, +IIO_EV_TYPE_THRESH , +IIO_EV_DIR_FALLING ); +iio_push_event (idev, code,iio_get_time_ns (idev)); + } +/* Check if the interrupt is from the upper threshold */ +if (int_res &OPT4060_RES_CTRL_FLAG_H ) { + code =IIO_UNMOD_EVENT_CODE (IIO_INTENSITY , + chan, +IIO_EV_TYPE_THRESH , +IIO_EV_DIR_RISING ); +iio_push_event (idev, code,iio_get_time_ns (idev)); + } +/* Handle threshold triggers */ +if (chip->threshold_trigger_active &&iio_buffer_enabled (idev)) +iio_trigger_poll_nested (chip->threshold_trig); + } + +/* Handle conversion ready */ +if (int_res &OPT4060_RES_CTRL_CONV_READY ) { +/* Signal completion for potentially waiting reads */ +complete (&chip->completion); + +/* Handle data ready triggers */ +if (chip->data_trigger_active &&iio_buffer_enabled (idev)) +iio_trigger_poll_nested (chip->data_trig); + } +return IRQ_HANDLED ; +} + +static int opt4060_setup_triggers (struct opt4060_chip *chip,struct iio_dev *idev) +{ +struct iio_trigger *data_trigger; +struct iio_trigger *threshold_trigger; +char *name; +int ret; + + ret =devm_iio_triggered_buffer_setup (chip->dev, idev, + &iio_pollfunc_store_time, + opt4060_trigger_handler, + &opt4060_buffer_ops); +if (ret) +return dev_err_probe (chip->dev, ret, +"iio_triggered_buffer_setup_ext FAIL\n" ); + + data_trigger =devm_iio_trigger_alloc (chip->dev,"%s-data-ready-dev%d" , + idev->name,iio_device_id (idev)); +if (!data_trigger) +return -ENOMEM ; + +/* The data trigger allows for sample capture on each new conversion ready interrupt. */ + chip->data_trig = data_trigger; + data_trigger->ops = &opt4060_trigger_ops; +iio_trigger_set_drvdata (data_trigger, idev); + ret =devm_iio_trigger_register (chip->dev, data_trigger); +if (ret) +return dev_err_probe (chip->dev, ret, +"Data ready trigger registration failed\n" ); + + threshold_trigger =devm_iio_trigger_alloc (chip->dev,"%s-threshold-dev%d" , + idev->name,iio_device_id (idev));
drivers/iio/light/opt4060.c line 1136
Quoted 10 lines
drivers/iio/light/opt4060.c new file +10
-0+if (!threshold_trigger) +return -ENOMEM ; + +/* +* The threshold trigger allows for sample capture on threshold interrupts. +* Just using events does not enable a way to get the samples that actually +* triggered the threshold interrupt. +*/ + chip->threshold_trig = threshold_trigger; + threshold_trigger->ops = &opt4060_trigger_ops;
Whilst I understand your usecase for this, I'd rather not have it in the initial driver. The use of events to trigger data capture should be a generic feature not one buried in a specific driver. Best bet for initial merge would be to just stick to the dataready trigger alone.
+ I'd also encourage you to look at if your usespace code can be made less sensitive to slightly late read back and hence potentially not a precise match with the trigger event.
Quoted 41 lines
drivers/iio/light/opt4060.c new file +41
-0+iio_trigger_set_drvdata (threshold_trigger, idev); + ret =devm_iio_trigger_register (chip->dev, threshold_trigger); +if (ret) +return dev_err_probe (chip->dev, ret, +"Threshold trigger registration failed\n" ); + + name =devm_kasprintf (chip->dev,GFP_KERNEL ,"%s-opt4060" , +dev_name (chip->dev)); +if (!name) +return dev_err_probe (chip->dev, -ENOMEM ,"Failed to alloc chip name\n" ); + + ret =devm_request_threaded_irq (chip->dev, chip->irq,NULL , opt4060_irq_thread, +IRQF_TRIGGER_FALLING |IRQF_TRIGGER_RISING | +IRQF_ONESHOT , name, idev); +if (ret) +return dev_err_probe (chip->dev, ret,"Could not request IRQ\n" ); + + ret =regmap_write_bits (chip->regmap,OPT4060_INT_CTRL , +OPT4060_INT_CTRL_OUTPUT , +OPT4060_INT_CTRL_OUTPUT ); +if (ret) +return dev_err_probe (chip->dev, ret, +"Failed to set interrupt as output\n" ); + +return 0 ; +} + +static int opt4060_probe (struct i2c_client *client) +{ +struct device *dev = &client->dev; +struct opt4060_chip *chip; +struct iio_dev *indio_dev; +int ret; +uint dev_id; + + indio_dev =devm_iio_device_alloc (dev,sizeof (*chip)); +if (!indio_dev) +return -ENOMEM ; + + chip =iio_priv (indio_dev); +
drivers/iio/light/opt4060.c line 1187
Quoted 10 lines
drivers/iio/light/opt4060.c new file +10
-0+ ret =devm_regulator_get_enable (dev,"vdd" ); +if (ret) +return dev_err_probe (dev, ret,"Failed to enable vdd supply\n" ); + + chip->regmap =devm_regmap_init_i2c (client, &opt4060_regmap_config); +if (IS_ERR (chip->regmap)) +return dev_err_probe (dev,PTR_ERR (chip->regmap), +"regmap initialization failed\n" ); + + ret =devm_add_action_or_reset (dev, opt4060_chip_off_action, chip);
This isn't obvious paired with a setup routine. I think it matches with the write to oneshot mode in opt4060_load_defaults.
As such this devm action setup should be immediately after that call, not up here.
Functionally it makes little difference because turning the chip off should be idempotent, but none the less it makes code more logical to follow if we can keep that type coupling of setup and tear down.
Quoted 23 lines
drivers/iio/light/opt4060.c new file +23
-0+if (ret <0 ) +return dev_err_probe (dev, ret, +"Failed to setup power off action\n" ); + + chip->dev = dev; + chip->irq = client->irq; +init_completion (&chip->completion); + + indio_dev->info = &opt4060_info; + + ret =regmap_reinit_cache (chip->regmap, &opt4060_regmap_config); +if (ret) +return dev_err_probe (dev, ret, +"failed to reinit regmap cache\n" ); + + ret =regmap_read (chip->regmap,OPT4060_DEVICE_ID , &dev_id); +if (ret <0 ) +return dev_err_probe (dev, ret, +"Failed to read the device ID register\n" ); + + dev_id =FIELD_GET (OPT4060_DEVICE_ID_MASK , dev_id); +if (dev_id !=OPT4060_DEVICE_ID_VAL ) +dev_info (dev,"Device ID: %#04x unknown\n" , dev_id);
drivers/iio/light/opt4060.c line 1220
Quoted 10 lines
drivers/iio/light/opt4060.c new file +10
-0+ +if (chip->irq) { + indio_dev->channels = opt4060_channels; + indio_dev->num_channels =ARRAY_SIZE (opt4060_channels); + }else { + indio_dev->channels = opt4060_channels_no_irq; + indio_dev->num_channels =ARRAY_SIZE (opt4060_channels_no_irq); + } + indio_dev->modes =INDIO_DIRECT_MODE ; + indio_dev->name = dev->driver->name;
I'd prefer it hard coded. The driver name can change more easily than ABI like this. Better to just have the string inline here as well.
drivers/iio/light/opt4060.c line 1225
Quoted 5 lines
drivers/iio/light/opt4060.c new file +5
-0+ + ret =opt4060_load_defaults (chip); +if (ret <0 ) +return dev_err_probe (dev, ret, +"Failed to set sensor defaults\n" );
ret = devm_add_action_or_oreset(dev, opt4060_chip_off_action, chip) should either be here, or moved into load_defaults to be right next to the register write this is reversing.
Quoted 9 lines
drivers/iio/light/opt4060.c new file +9
-0+ +if (chip->irq) { + ret =opt4060_setup_triggers (chip, indio_dev); +if (ret) +return ret; + } + +return devm_iio_device_register (dev, indio_dev); +}
Re: [PATCH v4 2/2] iio: light: Add support for TI OPT4060 color sensor
Hi Jonathan,
Thank you for your feedback, much appreciated. I have added questions and comments inline below regarding channels and triggers. I will address the other comments in the next patch.
Best regards / Per-Daniel
Quoted 36 lines · Jonathan Cameron
Quoted 3 lines · On Wed, 16 Oct 2024 23:34:09 +0200 Per-Daniel Olsson…
Add support for Texas Instruments OPT4060 RGBW Color sensor.
Signed-off-by Per-Daniel Olsson <[email protected]>Hi Per-Daniel,
Comments inline.
Jonathan
Quoted 6 lines
drivers/iio/light/opt4060.c new file +0
-0@@ -0,0 +1,1259 @@...
drivers/iio/light/opt4060.c line 146
Quoted 4 lines
drivers/iio/light/opt4060.c new file +4
-0+ +struct opt4060_buffer { +u32 chan[OPT4060_NUM_CHANS ]; +s64 ts__aligned(8) ;
aligned_s64is now available in linux-next + the IIO tree.drivers/iio/light/opt4060.c line 151
Quoted 5 lines
drivers/iio/light/opt4060.c new file +5
-0+}; + +static const struct opt4060_channel_factor opt4060_channel_factors[] = { + { +/* RED 2.4 * 2.15e-3 */ This needs more details on wrt to what standard etc.
Quoted 5 lines
The datasheet is a little vague, but it seems to me like TI invented their own standard. To use this stuff in a consistent ABI we need to have a common standard or at least an approximation of one.
The illuminance estimates from some devices are bad approximations, but they are at least attempting to approximate a well defined standard.
I have read the datasheet again to try to figure out what TI means. When I read it now with your remarks from this email and previous emails in mind, I think I'm starting to understand more.
I think we should expose the data from the sensor in the following way:
- Four raw channels (R, G, B and Clear)
- Three processed IIO_INTENSITY channels with normalized values (R, G, B) to get the relative color components independent of light intensity.
- One IIO_LIGHT channel giving the lux value.
This is basically what TI is stating in chapter 8.4.5.2. I know that you don't like how TI are calculating the lux value using the green channel. But after reading the description and detailed description parts of the datasheet again, I think it sort of makes sense. Looking at the spectral response curves on the first page, the green curve covers the whole visible spectrum. It seems like this is what the sensor is actually designed for, measuring light intensity in lux and color independent of the light intensity.
Does this sound like a way forward you think?
Quoted 2 lines
...
Quoted 11 lines
drivers/iio/light/opt4060.c line 377
Quoted 6 lines
drivers/iio/light/opt4060.c new file +6
-0+if (processed) { +/* +* The processed value are multiplied by factors that correspond to optical +* parameters in the sensor. The processed values gives more correct comparison +* between channels. +*/ No to these. We discussed this in review thread of earlier version. There is no standard unit of comparison defined so this ABI is not something that userspace can currently understand.
Quoted 6 lines
I'm not against introducing one, though it would need a bunch of investigation into what the appropriate way to define this is. TI have chosen an option that I don't recall seeing used by anyone else but maybe I'm wrong on that. There are several competing RGB standards. https://en.wikipedia.org/wiki/RGB_color_spaces
provides some info
See my remarks and question above.
...
Quoted 8 lines
drivers/iio/light/opt4060.c line 1136
Quoted 7 lines
drivers/iio/light/opt4060.c new file +7
-0+/* +* The threshold trigger allows for sample capture on threshold interrupts. +* Just using events does not enable a way to get the samples that actually +* triggered the threshold interrupt. +*/ + chip->threshold_trig = threshold_trigger; + threshold_trigger->ops = &opt4060_trigger_ops;
Quoted 8 lines
Whilst I understand your usecase for this, I'd rather not have it in the initial driver. The use of events to trigger data capture should be a generic feature not one buried in a specific driver. Best bet for initial merge would be to just stick to the dataready trigger alone.
+ I'd also encourage you to look at if your usespace code can be made less sensitive to slightly late read back and hence potentially not a precise match with the trigger event.
I will remove the threshold trigger in the next patch. I have started looking at the generic feature that you described in a previous email but it will take some time and it will have to be another set of patches. The team here working on the userspace app will have to adapt until this is done.
...
Re: [PATCH v4 2/2] iio: light: Add support for TI OPT4060 color sensor
Quoted 69 lines · On Wed, 23 Oct 2024 09:29:08 +0200 Per-Daniel Olsson…
Hi Jonathan,
Thank you for your feedback, much appreciated. I have added questions and comments inline below regarding channels and triggers. I will address the other comments in the next patch.
Best regards / Per-Daniel
Quoted 36 lines · Jonathan Cameron
Quoted 3 lines · On Wed, 16 Oct 2024 23:34:09 +0200 Per-Daniel Olsson…
Add support for Texas Instruments OPT4060 RGBW Color sensor.
Signed-off-by Per-Daniel Olsson <[email protected]>Hi Per-Daniel,
Comments inline.
Jonathan
Quoted 6 lines
drivers/iio/light/opt4060.c new file +0
-0@@ -0,0 +1,1259 @@...
drivers/iio/light/opt4060.c line 146
Quoted 4 lines
drivers/iio/light/opt4060.c new file +4
-0+ +struct opt4060_buffer { +u32 chan[OPT4060_NUM_CHANS ]; +s64 ts__aligned(8) ;
aligned_s64is now available in linux-next + the IIO tree.drivers/iio/light/opt4060.c line 151
Quoted 5 lines
drivers/iio/light/opt4060.c new file +5
-0+}; + +static const struct opt4060_channel_factor opt4060_channel_factors[] = { + { +/* RED 2.4 * 2.15e-3 */ This needs more details on wrt to what standard etc.
Quoted 5 lines
The datasheet is a little vague, but it seems to me like TI invented their own standard. To use this stuff in a consistent ABI we need to have a common standard or at least an approximation of one.
The illuminance estimates from some devices are bad approximations, but they are at least attempting to approximate a well defined standard.I have read the datasheet again to try to figure out what TI means. When I read it now with your remarks from this email and previous emails in mind, I think I'm starting to understand more.
I think we should expose the data from the sensor in the following way:
- Four raw channels (R, G, B and Clear)
- Three processedIIO_INTENSITYchannels with normalized values (R, G, B) to get the relative color components independent of light intensity.
- OneIIO_LIGHTchannel giving the lux value.This is basically what TI is stating in chapter 8.4.5.2. I know that you don't like how TI are calculating the lux value using the green channel. But after reading the description and detailed description parts of the datasheet again, I think it sort of makes sense. Looking at the spectral response curves on the first page, the green curve covers the whole visible spectrum. It seems like this is what the sensor is actually designed for, measuring light intensity in lux and color independent of the light intensity.
Quoted 1 line
Does this sound like a way forward you think?
Not keen on the colour part.
As far as I can tell TI made up a colour standard. If it were
CIE 1931 RGB or then 'maybe' we could consider presenting them as processed, though as they are linear scales even then should present _raw and _scale, not _input (processed). We would still need to figure out if we needed to handle multiple colour space definitions.
As it is, if we have two different colour sensors, there is no way to compare the values. In particular that Green is way too broad for the colour standards I quickly compared this with.
The green curve does (based on eyeballing it rather than anything formal) look much closer to the luminosity function (one used for illuminance) than I was assuming (given it's called green!)
So not ideal but that one feels ok (with comments in the code explaining this) to use for illuminance.
For the color channels maybe we could present with _scale provided if we add suitable documentation to say that the scaling is to arbitrary datasheet specified normalization and that the resulting _raw * _scale values cannot be compared across different sensors. I don't like that but it does seem silly to not present the scaling if it might be useful to someone. So if you want to do this, propose some additions to Documentation/testing/ABI/sysfs-bus-iio
to cover this for in_intensity_red_scale
etc.
Jonathan
Re: [PATCH v4 2/2] iio: light: Add support for TI OPT4060 color sensor
Quoted 102 lines · Jonathan Cameron
Quoted 69 lines · On Wed, 23 Oct 2024 09:29:08 +0200 Per-Daniel Olsson…
Hi Jonathan,
Thank you for your feedback, much appreciated. I have added questions and comments inline below regarding channels and triggers. I will address the other comments in the next patch.
Best regards / Per-Daniel
Quoted 36 lines · Jonathan Cameron
Quoted 3 lines · On Wed, 16 Oct 2024 23:34:09 +0200 Per-Daniel Olsson…
Add support for Texas Instruments OPT4060 RGBW Color sensor.
Signed-off-by Per-Daniel Olsson <[email protected]>Hi Per-Daniel,
Comments inline.
Jonathan
Quoted 6 lines
drivers/iio/light/opt4060.c new file +0
-0@@ -0,0 +1,1259 @@...
drivers/iio/light/opt4060.c line 146
Quoted 4 lines
drivers/iio/light/opt4060.c new file +4
-0+ +struct opt4060_buffer { +u32 chan[OPT4060_NUM_CHANS ]; +s64 ts__aligned(8) ;
aligned_s64is now available in linux-next + the IIO tree.drivers/iio/light/opt4060.c line 151
Quoted 5 lines
drivers/iio/light/opt4060.c new file +5
-0+}; + +static const struct opt4060_channel_factor opt4060_channel_factors[] = { + { +/* RED 2.4 * 2.15e-3 */ This needs more details on wrt to what standard etc.
Quoted 5 lines
The datasheet is a little vague, but it seems to me like TI invented their own standard. To use this stuff in a consistent ABI we need to have a common standard or at least an approximation of one.
The illuminance estimates from some devices are bad approximations, but they are at least attempting to approximate a well defined standard.I have read the datasheet again to try to figure out what TI means. When I read it now with your remarks from this email and previous emails in mind, I think I'm starting to understand more.
I think we should expose the data from the sensor in the following way:
- Four raw channels (R, G, B and Clear)
- Three processedIIO_INTENSITYchannels with normalized values (R, G, B) to get the relative color components independent of light intensity.
- OneIIO_LIGHTchannel giving the lux value.This is basically what TI is stating in chapter 8.4.5.2. I know that you don't like how TI are calculating the lux value using the green channel. But after reading the description and detailed description parts of the datasheet again, I think it sort of makes sense. Looking at the spectral response curves on the first page, the green curve covers the whole visible spectrum. It seems like this is what the sensor is actually designed for, measuring light intensity in lux and color independent of the light intensity.
Quoted 1 line
Does this sound like a way forward you think?
Not keen on the colour part.
As far as I can tell TI made up a colour standard. If it were
CIE 1931 RGB or then 'maybe' we could consider presenting them as processed, though as they are linear scales even then should present_rawand_scale, not_input(processed). We would still need to figure out if we needed to handle multiple colour space definitions.
As it is, if we have two different colour sensors, there is no way to compare the values. In particular that Green is way too broad for the colour standards I quickly compared this with.The green curve does (based on eyeballing it rather than anything formal) look much closer to the luminosity function (one used for illuminance) than I was assuming (given it's called green!)
So not ideal but that one feels ok (with comments in the code explaining this) to use for illuminance.
For the color channels maybe we could present with
_scaleprovided if we add suitable documentation to say that the scaling is to arbitrary datasheet specified normalization and that the resulting_raw*_scalevalues cannot be compared across different sensors. I don't like that but it does seem silly to not present the scaling if it might be useful to someone. So if you want to do this, propose some additions toDocumentation/testing/ABI/sysfs-bus-iio
to cover this forin_intensity_red_scale
etc.
Quoted 3 lines
Jonathan
Ok, great. Thank you for responding so quickly. I will try to implement it according to your suggestions in the next patch and also patch the documentation.
/ P-D