* [PATCH 0/3] iio:light: add driver for veml6046x00 RGBIR color sensor
@ 2025-03-16 11:31 Andreas Klinger
2025-03-16 11:31 ` [PATCH 1/3] dt-bindings: iio: light: veml6046x00: add " Andreas Klinger
` (2 more replies)
0 siblings, 3 replies; 18+ messages in thread
From: Andreas Klinger @ 2025-03-16 11:31 UTC (permalink / raw)
To: jic23, robh, krzk+dt, conor+dt
Cc: lars, linux-iio, devicetree, linux-kernel, javier.carrasco.cruz,
mazziesaccount, subhajit.ghosh, muditsharma.info, arthur.becker,
ivan.orlov0322, ak
This patchset adds an IIO driver for Vishay veml6046x00 RGBIR color sensor
Andreas Klinger (3):
dt-bindings: iio: light: veml6046x00: add color sensor
iio: light: add support for veml6046x00 RGBIR color sensor
MAINTAINER: add maintainer for veml6046x00
.../iio/light/vishay,veml6046x00.yaml | 49 +
MAINTAINERS | 6 +
drivers/iio/light/Kconfig | 13 +
drivers/iio/light/Makefile | 1 +
drivers/iio/light/veml6046x00.c | 890 ++++++++++++++++++
5 files changed, 959 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/light/vishay,veml6046x00.yaml
create mode 100644 drivers/iio/light/veml6046x00.c
--
2.39.5
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH 1/3] dt-bindings: iio: light: veml6046x00: add color sensor
2025-03-16 11:31 [PATCH 0/3] iio:light: add driver for veml6046x00 RGBIR color sensor Andreas Klinger
@ 2025-03-16 11:31 ` Andreas Klinger
2025-03-16 12:19 ` Rob Herring (Arm)
2025-03-17 11:00 ` Jonathan Cameron
2025-03-16 11:31 ` [PATCH 2/3] iio: light: add support for veml6046x00 RGBIR " Andreas Klinger
2025-03-16 11:31 ` [PATCH 3/3] MAINTAINER: add maintainer for veml6046x00 Andreas Klinger
2 siblings, 2 replies; 18+ messages in thread
From: Andreas Klinger @ 2025-03-16 11:31 UTC (permalink / raw)
To: jic23, robh, krzk+dt, conor+dt
Cc: lars, linux-iio, devicetree, linux-kernel, javier.carrasco.cruz,
mazziesaccount, subhajit.ghosh, muditsharma.info, arthur.becker,
ivan.orlov0322, ak
Add a new compatible for Vishay high accuracy RGBIR color sensor
veml6046x00.
Signed-off-by: Andreas Klinger <ak@it-klinger.de>
---
.../iio/light/vishay,veml6046x00.yaml | 49 +++++++++++++++++++
1 file changed, 49 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/light/vishay,veml6046x00.yaml
diff --git a/Documentation/devicetree/bindings/iio/light/vishay,veml6046x00.yaml b/Documentation/devicetree/bindings/iio/light/vishay,veml6046x00.yaml
new file mode 100644
index 000000000000..3207800fc539
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/light/vishay,veml6046x00.yaml
@@ -0,0 +1,49 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/light/vishay,veml6046x00.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Vishay VEML6046X00 High accuracy RGBIR color sensor
+
+maintainers:
+ - Andreas Klinger <ak@it-klinger.de>
+
+description:
+ VEML6046X00 datasheet at https://www.vishay.com/docs/80173/veml6046x00.pdf
+
+properties:
+ compatible:
+ enum:
+ - vishay,veml6046x00
+
+ reg:
+ maxItems: 1
+
+ vdd-supply: true
+
+ interrupts:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+ - vdd-supply
+
+additionalProperties: false
+
+examples:
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ color-sensor@29 {
+ compatible = "vishay,veml6046x00";
+ reg = <0x29>;
+ vdd-supply = <&vdd_reg>;
+ interrupt-parent = <&gpio2>;
+ interrupts = <3 IRQ_TYPE_EDGE_FALLING>;
+ };
+ };
+...
--
2.39.5
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH 2/3] iio: light: add support for veml6046x00 RGBIR color sensor
2025-03-16 11:31 [PATCH 0/3] iio:light: add driver for veml6046x00 RGBIR color sensor Andreas Klinger
2025-03-16 11:31 ` [PATCH 1/3] dt-bindings: iio: light: veml6046x00: add " Andreas Klinger
@ 2025-03-16 11:31 ` Andreas Klinger
2025-03-17 11:50 ` Jonathan Cameron
2025-03-16 11:31 ` [PATCH 3/3] MAINTAINER: add maintainer for veml6046x00 Andreas Klinger
2 siblings, 1 reply; 18+ messages in thread
From: Andreas Klinger @ 2025-03-16 11:31 UTC (permalink / raw)
To: jic23, robh, krzk+dt, conor+dt
Cc: lars, linux-iio, devicetree, linux-kernel, javier.carrasco.cruz,
mazziesaccount, subhajit.ghosh, muditsharma.info, arthur.becker,
ivan.orlov0322, ak
Add Vishay VEML6046X00 high accuracy RGBIR color sensor.
This sensor provides three colour (red, green and blue) as well as one
infrared (IR) channel through I2C.
Support direct and buffered mode.
An optional interrupt for signaling green colour threshold underflow or
overflow is not supported so far.
Signed-off-by: Andreas Klinger <ak@it-klinger.de>
---
drivers/iio/light/Kconfig | 13 +
drivers/iio/light/Makefile | 1 +
drivers/iio/light/veml6046x00.c | 890 ++++++++++++++++++++++++++++++++
3 files changed, 904 insertions(+)
create mode 100644 drivers/iio/light/veml6046x00.c
diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig
index e34e551eef3e..d79bc302afab 100644
--- a/drivers/iio/light/Kconfig
+++ b/drivers/iio/light/Kconfig
@@ -702,6 +702,19 @@ config VEML6040
To compile this driver as a module, choose M here: the
module will be called veml6040.
+config VEML6046X00
+ tristate "VEML6046X00 RGBIR color sensor"
+ select REGMAP_I2C
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ depends on I2C
+ help
+ Say Y here if you want to build a driver for the Vishay VEML6046X00
+ high accuracy RGBIR color sensor.
+
+ To compile this driver as a module, choose M here: the
+ module will be called veml6046x00.
+
config VEML6070
tristate "VEML6070 UV A light sensor"
depends on I2C
diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile
index 11a4041b918a..14c9dc7813d6 100644
--- a/drivers/iio/light/Makefile
+++ b/drivers/iio/light/Makefile
@@ -65,6 +65,7 @@ obj-$(CONFIG_VCNL4035) += vcnl4035.o
obj-$(CONFIG_VEML3235) += veml3235.o
obj-$(CONFIG_VEML6030) += veml6030.o
obj-$(CONFIG_VEML6040) += veml6040.o
+obj-$(CONFIG_VEML6046X00) += veml6046x00.o
obj-$(CONFIG_VEML6070) += veml6070.o
obj-$(CONFIG_VEML6075) += veml6075.o
obj-$(CONFIG_VL6180) += vl6180.o
diff --git a/drivers/iio/light/veml6046x00.c b/drivers/iio/light/veml6046x00.c
new file mode 100644
index 000000000000..8e6232e1ab70
--- /dev/null
+++ b/drivers/iio/light/veml6046x00.c
@@ -0,0 +1,890 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * VEML6046X00 High Accuracy RGBIR Color Sensor
+ *
+ * Copyright (c) 2025 Andreas Klinger <ak@it-klinger.de>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+#include <linux/units.h>
+#include <linux/pm_runtime.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+/* Device registers */
+#define VEML6046X00_REG_CONF0 0x00
+#define VEML6046X00_REG_CONF1 0x01
+#define VEML6046X00_REG_THDH_L 0x04
+#define VEML6046X00_REG_THDH_H 0x05
+#define VEML6046X00_REG_THDL_L 0x06
+#define VEML6046X00_REG_THDL_H 0x07
+#define VEML6046X00_REG_R_L 0x10
+#define VEML6046X00_REG_R_H 0x11
+#define VEML6046X00_REG_G_L 0x12
+#define VEML6046X00_REG_G_H 0x13
+#define VEML6046X00_REG_B_L 0x14
+#define VEML6046X00_REG_B_H 0x15
+#define VEML6046X00_REG_IR_L 0x16
+#define VEML6046X00_REG_IR_H 0x17
+#define VEML6046X00_REG_ID_L 0x18
+#define VEML6046X00_REG_ID_H 0x19
+#define VEML6046X00_REG_INT_L 0x1A
+#define VEML6046X00_REG_INT_H 0x1B
+#define VEML6046X00_REG_DATA(ch) (VEML6046X00_REG_R_L + (ch))
+
+/* Bit masks for specific functionality */
+#define VEML6046X00_CONF0_ON_0 BIT(0)
+#define VEML6046X00_CONF0_INT BIT(1)
+#define VEML6046X00_CONF0_AF_TRIG BIT(2)
+#define VEML6046X00_CONF0_AF BIT(3)
+#define VEML6046X00_CONF0_IT GENMASK(6, 4)
+#define VEML6046X00_CONF1_CAL BIT(0)
+#define VEML6046X00_CONF1_PERS GENMASK(2, 1)
+#define VEML6046X00_CONF1_GAIN GENMASK(4, 3)
+#define VEML6046X00_CONF1_PD_D2 BIT(6)
+#define VEML6046X00_CONF1_ON_1 BIT(7)
+#define VEML6046X00_INT_TH_H BIT(1)
+#define VEML6046X00_INT_TH_L BIT(2)
+#define VEML6046X00_INT_DRDY BIT(3)
+#define VEML6046X00_INT_MASK (VEML6046X00_INT_TH_H | \
+ VEML6046X00_INT_TH_L | VEML6046X00_INT_DRDY)
+
+#define VEML6046X00_GAIN_1 0x0
+#define VEML6046X00_GAIN_2 0x1
+#define VEML6046X00_GAIN_0_66 0x2
+#define VEML6046X00_GAIN_0_5 0x3
+
+/* Autosuspend delay */
+#define VEML6046X00_AUTOSUSPEND_MS 3000
+
+enum veml6046x00_scan {
+ VEML6046X00_SCAN_R,
+ VEML6046X00_SCAN_G,
+ VEML6046X00_SCAN_B,
+ VEML6046X00_SCAN_IR,
+ VEML6046X00_SCAN_TIMESTAMP,
+};
+
+/*
+ * regmap fields
+ */
+struct veml6046x00_rf {
+ struct regmap_field *int_en;
+ struct regmap_field *mode;
+ struct regmap_field *trig;
+ struct regmap_field *it;
+ struct regmap_field *pers;
+};
+
+struct veml6046x00_data {
+ struct device *dev;
+ struct regmap *regmap;
+ struct iio_trigger *trig;
+ struct veml6046x00_rf rf;
+};
+
+struct veml6046x00_scan_buf {
+ __le16 chans[4];
+
+ s64 timestamp __aligned(8);
+};
+
+static const int veml6046x00_it[][2] = {
+ { 0, 3125 },
+ { 0, 6250 },
+ { 0, 12500 },
+ { 0, 25000 },
+ { 0, 50000 },
+ { 0, 100000 },
+ { 0, 200000 },
+ { 0, 400000 },
+};
+
+static const int veml6046x00_gains[][2] = {
+ { 0, 250000 },
+ { 0, 330000 },
+ { 0, 500000 },
+ { 0, 660000 },
+ { 1, 0 },
+ { 2, 0 },
+};
+
+/*
+ * Persistence = 1/2/4/8 x integration time
+ * Minimum time for which light readings must stay above configured
+ * threshold to assert the interrupt.
+ */
+static const char * const period_values[] = {
+ "0.003125 0.00625 0.0125 0.025",
+ "0.00625 0.0125 0.025 0.05",
+ "0.0125 0.025 0.05 0.1",
+ "0.025 0.050 0.1 0.2",
+ "0.05 0.1 0.2 0.4",
+ "0.1 0.2 0.4 0.8",
+ "0.2 0.4 0.8 1.6",
+ "0.4 0.8 1.6 3.2"
+};
+
+/*
+ * Return list of valid period values in seconds corresponding to
+ * the currently active integration time.
+ */
+static ssize_t in_illuminance_period_available_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct veml6046x00_data *data = iio_priv(dev_to_iio_dev(dev));
+ int ret, reg;
+
+ ret = regmap_field_read(data->rf.it, ®);
+ if (ret)
+ return ret;
+
+ if (reg < 0 || reg >= ARRAY_SIZE(period_values))
+ return -EINVAL;
+
+ return sysfs_emit(buf, "%s\n", period_values[reg]);
+}
+
+static IIO_DEVICE_ATTR_RO(in_illuminance_period_available, 0);
+
+static struct attribute *veml6046x00_event_attributes[] = {
+ &iio_dev_attr_in_illuminance_period_available.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group veml6046x00_event_attr_group = {
+ .attrs = veml6046x00_event_attributes,
+};
+
+/*
+ * Two bits (RGB_ON_0 and RGB_ON_1) must be cleared to power on the device.
+ */
+static int veml6046x00_power_on(struct veml6046x00_data *data)
+{
+ int ret;
+
+ ret = regmap_clear_bits(data->regmap, VEML6046X00_REG_CONF0,
+ VEML6046X00_CONF0_ON_0);
+ if (ret) {
+ dev_err(data->dev, "Failed to set bit for power on %d\n", ret);
+ return ret;
+ }
+
+ return regmap_clear_bits(data->regmap, VEML6046X00_REG_CONF1,
+ VEML6046X00_CONF1_ON_1);
+}
+
+/*
+ * Two bits (RGB_ON_0 and RGB_ON_1) must be set to power off the device.
+ */
+static int veml6046x00_shutdown(struct veml6046x00_data *data)
+{
+ int ret;
+
+ ret = regmap_set_bits(data->regmap, VEML6046X00_REG_CONF0,
+ VEML6046X00_CONF0_ON_0);
+ if (ret) {
+ dev_err(data->dev, "Failed to set bit for shutdown %d\n", ret);
+ return ret;
+ }
+
+ return regmap_set_bits(data->regmap, VEML6046X00_REG_CONF1,
+ VEML6046X00_CONF1_ON_1);
+}
+
+static void veml6046x00_shutdown_action(void *data)
+{
+ veml6046x00_shutdown(data);
+}
+
+static const struct iio_chan_spec veml6046x00_channels[] = {
+ {
+ .type = IIO_LIGHT,
+ .address = VEML6046X00_REG_R_L,
+ .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) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .scan_index = VEML6046X00_SCAN_R,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 16,
+ .storagebits = 16,
+ .endianness = IIO_LE,
+ },
+ },
+ {
+ .type = IIO_LIGHT,
+ .address = VEML6046X00_REG_G_L,
+ .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) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .scan_index = VEML6046X00_SCAN_G,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 16,
+ .storagebits = 16,
+ .endianness = IIO_LE,
+ },
+ },
+ {
+ .type = IIO_LIGHT,
+ .address = VEML6046X00_REG_B_L,
+ .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) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .scan_index = VEML6046X00_SCAN_B,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 16,
+ .storagebits = 16,
+ .endianness = IIO_LE,
+ },
+ },
+ {
+ .type = IIO_LIGHT,
+ .address = VEML6046X00_REG_IR_L,
+ .modified = 1,
+ .channel2 = IIO_MOD_LIGHT_IR,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .scan_index = VEML6046X00_SCAN_IR,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 16,
+ .storagebits = 16,
+ .endianness = IIO_LE,
+ },
+ },
+ IIO_CHAN_SOFT_TIMESTAMP(VEML6046X00_SCAN_TIMESTAMP),
+};
+
+static const struct regmap_config veml6046x00_regmap_config = {
+ .name = "veml6046x00_regmap",
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = VEML6046X00_REG_INT_H,
+};
+
+static const struct reg_field veml6046x00_rf_int_en =
+ REG_FIELD(VEML6046X00_REG_CONF0, 1, 1);
+
+static const struct reg_field veml6046x00_rf_trig =
+ REG_FIELD(VEML6046X00_REG_CONF0, 2, 2);
+
+static const struct reg_field veml6046x00_rf_mode =
+ REG_FIELD(VEML6046X00_REG_CONF0, 3, 3);
+
+static const struct reg_field veml6046x00_rf_it =
+ REG_FIELD(VEML6046X00_REG_CONF0, 4, 6);
+
+static const struct reg_field veml6046x00_rf_pers =
+ REG_FIELD(VEML6046X00_REG_CONF1, 1, 2);
+
+static int veml6046x00_regfield_init(struct veml6046x00_data *data)
+{
+ struct regmap *regmap = data->regmap;
+ struct device *dev = data->dev;
+ struct regmap_field *rm_field;
+ struct veml6046x00_rf *rf = &data->rf;
+
+ rm_field = devm_regmap_field_alloc(dev, regmap, veml6046x00_rf_int_en);
+ if (IS_ERR(rm_field))
+ return PTR_ERR(rm_field);
+ rf->int_en = rm_field;
+
+ rm_field = devm_regmap_field_alloc(dev, regmap, veml6046x00_rf_mode);
+ if (IS_ERR(rm_field))
+ return PTR_ERR(rm_field);
+ rf->mode = rm_field;
+
+ rm_field = devm_regmap_field_alloc(dev, regmap, veml6046x00_rf_trig);
+ if (IS_ERR(rm_field))
+ return PTR_ERR(rm_field);
+ rf->trig = rm_field;
+
+ rm_field = devm_regmap_field_alloc(dev, regmap, veml6046x00_rf_it);
+ if (IS_ERR(rm_field))
+ return PTR_ERR(rm_field);
+ rf->it = rm_field;
+
+ rm_field = devm_regmap_field_alloc(dev, regmap, veml6046x00_rf_pers);
+ if (IS_ERR(rm_field))
+ return PTR_ERR(rm_field);
+ rf->pers = rm_field;
+
+ return 0;
+}
+
+static int veml6046x00_get_it_usec(struct veml6046x00_data *data, int *it_usec)
+{
+ int ret, reg;
+
+ ret = regmap_field_read(data->rf.it, ®);
+ if (ret)
+ return ret;
+
+ switch (reg) {
+ case 0:
+ *it_usec = 3125;
+ break;
+ case 1:
+ *it_usec = 6250;
+ break;
+ case 2:
+ *it_usec = 12500;
+ break;
+ case 3:
+ *it_usec = 25000;
+ break;
+ case 4:
+ *it_usec = 50000;
+ break;
+ case 5:
+ *it_usec = 100000;
+ break;
+ case 6:
+ *it_usec = 200000;
+ break;
+ case 7:
+ *it_usec = 400000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static int veml6046x00_set_it(struct iio_dev *iio, int val, int val2)
+{
+ struct veml6046x00_data *data = iio_priv(iio);
+ int ret, new_it;
+
+ if (val)
+ return -EINVAL;
+
+ switch (val2) {
+ case 3125:
+ new_it = 0x00;
+ break;
+ case 6250:
+ new_it = 0x01;
+ break;
+ case 12500:
+ new_it = 0x02;
+ break;
+ case 25000:
+ new_it = 0x03;
+ break;
+ case 50000:
+ new_it = 0x04;
+ break;
+ case 100000:
+ new_it = 0x05;
+ break;
+ case 200000:
+ new_it = 0x06;
+ break;
+ case 400000:
+ new_it = 0x07;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = regmap_field_write(data->rf.it, new_it);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int veml6046x00_set_scale(struct iio_dev *iio, int val, int val2)
+{
+ struct veml6046x00_data *data = iio_priv(iio);
+ int new_scale;
+
+ if (val == 0 && val2 == 250000) {
+ new_scale = FIELD_PREP(VEML6046X00_CONF1_GAIN, VEML6046X00_GAIN_0_5) |
+ VEML6046X00_CONF1_PD_D2;
+ } else if (val == 0 && val2 == 330000) {
+ new_scale = FIELD_PREP(VEML6046X00_CONF1_GAIN, VEML6046X00_GAIN_0_66) |
+ VEML6046X00_CONF1_PD_D2;
+ } else if (val == 0 && val2 == 500000) {
+ new_scale = FIELD_PREP(VEML6046X00_CONF1_GAIN, VEML6046X00_GAIN_0_5);
+ } else if (val == 0 && val2 == 660000) {
+ new_scale = FIELD_PREP(VEML6046X00_CONF1_GAIN, VEML6046X00_GAIN_0_66);
+ } else if (val == 1 && val2 == 0) {
+ new_scale = FIELD_PREP(VEML6046X00_CONF1_GAIN, VEML6046X00_GAIN_1);
+ } else if (val == 2 && val2 == 0) {
+ new_scale = FIELD_PREP(VEML6046X00_CONF1_GAIN, VEML6046X00_GAIN_2);
+ } else {
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(data->regmap, VEML6046X00_REG_CONF1,
+ VEML6046X00_CONF1_GAIN |
+ VEML6046X00_CONF1_PD_D2,
+ new_scale);
+}
+
+static int veml6046x00_get_scale(struct veml6046x00_data *data,
+ int *val, int *val2)
+{
+ int ret, reg;
+
+ ret = regmap_read(data->regmap, VEML6046X00_REG_CONF1, ®);
+ if (ret)
+ return ret;
+
+ switch (FIELD_GET(VEML6046X00_CONF1_GAIN, reg)) {
+ case 0:
+ *val = 1;
+ *val2 = 0;
+ break;
+ case 1:
+ *val = 2;
+ *val2 = 0;
+ break;
+ case 2:
+ *val = 0;
+ *val2 = 660000;
+ break;
+ case 3:
+ *val = 0;
+ *val2 = 500000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (reg & VEML6046X00_CONF1_PD_D2)
+ *val2 /= 2;
+
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static int veml6046x00_set_mode(struct veml6046x00_data *data, bool state)
+{
+ return regmap_field_write(data->rf.mode, state);
+}
+
+static int veml6046x00_set_trig(struct veml6046x00_data *data, bool state)
+{
+ return regmap_field_write(data->rf.trig, state);
+}
+
+static int veml6046x00_wait_data_available(struct iio_dev *iio, int usecs)
+{
+ struct veml6046x00_data *data = iio_priv(iio);
+ int ret, reg;
+ int cnt = 2;
+ int i;
+
+ for (i = 0; i < cnt; i++) {
+ ret = regmap_read(data->regmap, VEML6046X00_REG_INT_H, ®);
+ if (ret) {
+ dev_err(data->dev,
+ "Failed to read interrupt register %d\n", ret);
+ return -EIO;
+ }
+
+ if (reg & VEML6046X00_INT_DRDY)
+ return 1;
+
+ if (i < cnt)
+ fsleep(usecs);
+ }
+
+ return 0;
+}
+
+static int veml6046x00_single_read(struct iio_dev *iio,
+ enum iio_modifier modifier, int *val)
+{
+ struct veml6046x00_data *data = iio_priv(iio);
+ int addr, it_usec, ret;
+ uint8_t reg[2];
+
+ switch (modifier) {
+ case IIO_MOD_LIGHT_RED:
+ addr = VEML6046X00_REG_R_L;
+ break;
+ case IIO_MOD_LIGHT_GREEN:
+ addr = VEML6046X00_REG_G_L;
+ break;
+ case IIO_MOD_LIGHT_BLUE:
+ addr = VEML6046X00_REG_B_L;
+ break;
+ case IIO_MOD_LIGHT_IR:
+ addr = VEML6046X00_REG_IR_L;
+ break;
+ default:
+ return -EINVAL;
+ }
+ ret = pm_runtime_resume_and_get(data->dev);
+ if (ret)
+ return ret;
+
+ ret = veml6046x00_get_it_usec(data, &it_usec);
+ if (ret < 0)
+ return ret;
+
+ veml6046x00_set_mode(data, 1);
+
+ veml6046x00_set_trig(data, 1);
+
+ /* integration time + 10 % to ensure completion */
+ fsleep(it_usec + (it_usec / 10));
+
+ ret = veml6046x00_wait_data_available(iio, it_usec * 10);
+ if (ret == 1) {
+ dev_dbg(data->dev, "data ready\n");
+ } else {
+ dev_warn(data->dev, "no data ready ret: %d\n", ret);
+ goto no_data;
+ }
+
+ ret = iio_device_claim_direct_mode(iio);
+ if (ret)
+ return ret;
+
+ ret = regmap_bulk_read(data->regmap, addr, reg, sizeof(reg));
+ iio_device_release_direct_mode(iio);
+ if (ret)
+ return ret;
+
+ pm_runtime_mark_last_busy(data->dev);
+ pm_runtime_put_autosuspend(data->dev);
+
+ *val = reg[1] << 8 | reg[0];
+
+ return IIO_VAL_INT;
+
+no_data:
+ pm_runtime_mark_last_busy(data->dev);
+ pm_runtime_put_autosuspend(data->dev);
+
+ return -EINVAL;
+}
+
+static int veml6046x00_read_raw(struct iio_dev *iio,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ struct veml6046x00_data *data = iio_priv(iio);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (chan->type != IIO_LIGHT)
+ return -EINVAL;
+ return veml6046x00_single_read(iio, chan->channel2, val);
+ case IIO_CHAN_INFO_INT_TIME:
+ *val = 0;
+ return veml6046x00_get_it_usec(data, val2);
+ case IIO_CHAN_INFO_SCALE:
+ return veml6046x00_get_scale(data, val, val2);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int veml6046x00_read_avail(struct iio_dev *iio,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length,
+ long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_INT_TIME:
+ *vals = (int *)&veml6046x00_it;
+ *length = 2 * ARRAY_SIZE(veml6046x00_it);
+ *type = IIO_VAL_INT_PLUS_MICRO;
+ return IIO_AVAIL_LIST;
+ case IIO_CHAN_INFO_SCALE:
+ *vals = (int *)&veml6046x00_gains;
+ *length = 2 * ARRAY_SIZE(veml6046x00_gains);
+ *type = IIO_VAL_INT_PLUS_MICRO;
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int veml6046x00_write_raw(struct iio_dev *iio,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_INT_TIME:
+ return veml6046x00_set_it(iio, val, val2);
+ case IIO_CHAN_INFO_SCALE:
+ return veml6046x00_set_scale(iio, val, val2);
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info veml6046x00_info_no_irq = {
+ .read_raw = veml6046x00_read_raw,
+ .read_avail = veml6046x00_read_avail,
+ .write_raw = veml6046x00_write_raw,
+};
+
+static int veml6046x00_buffer_preenable(struct iio_dev *iio)
+{
+ struct veml6046x00_data *data = iio_priv(iio);
+ struct device *dev = data->dev;
+ int ret;
+
+ ret = veml6046x00_set_mode(data, 0);
+ if (ret)
+ dev_err(data->dev, "Failed to set mode %d\n", ret);
+
+ ret = veml6046x00_set_trig(data, 0);
+ if (ret)
+ dev_err(data->dev, "Failed to set trigger %d\n", ret);
+
+ return pm_runtime_resume_and_get(dev);
+}
+
+static int veml6046x00_buffer_postdisable(struct iio_dev *iio)
+{
+ struct veml6046x00_data *data = iio_priv(iio);
+ struct device *dev = data->dev;
+ int ret;
+
+ ret = veml6046x00_set_mode(data, 1);
+ if (ret)
+ dev_err(data->dev, "Failed to set mode %d\n", ret);
+
+
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+
+ return 0;
+}
+
+static const struct iio_buffer_setup_ops veml6046x00_buffer_setup_ops = {
+ .preenable = veml6046x00_buffer_preenable,
+ .postdisable = veml6046x00_buffer_postdisable,
+};
+
+static irqreturn_t veml6046x00_trig_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *iio = pf->indio_dev;
+ struct veml6046x00_data *data = iio_priv(iio);
+ int ret;
+ struct veml6046x00_scan_buf scan;
+
+ memset(&scan, 0, sizeof(scan));
+
+ ret = regmap_bulk_read(data->regmap, VEML6046X00_REG_R_L, &scan.chans,
+ sizeof(scan.chans));
+ if (ret)
+ goto done;
+
+ iio_push_to_buffers_with_timestamp(iio, &scan, iio_get_time_ns(iio));
+
+done:
+ iio_trigger_notify_done(iio->trig);
+
+ return IRQ_HANDLED;
+}
+
+static int veml6046x00_validate_part_id(struct veml6046x00_data *data)
+{
+ int part_id, ret;
+ __le16 reg;
+
+ ret = regmap_bulk_read(data->regmap, VEML6046X00_REG_ID_L, ®, sizeof(reg));
+ if (ret) {
+ dev_info(data->dev, "Failed to read ID\n");
+ return -EIO;
+ }
+
+ part_id = le16_to_cpu(reg);
+ if (part_id != 0x0001)
+ dev_info(data->dev, "Unknown ID %#02x\n", part_id);
+
+ return 0;
+}
+
+static int veml6046x00_setup_device(struct iio_dev *iio)
+{
+ struct veml6046x00_data *data = iio_priv(iio);
+ struct device *dev = data->dev;
+ int ret, val;
+ __le16 reg16;
+ uint8_t reg[2];
+
+ reg[0] = VEML6046X00_CONF0_AF;
+ reg[1] = 0x00;
+ ret = regmap_bulk_write(data->regmap, VEML6046X00_REG_CONF0, reg, sizeof(reg));
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to set configuration\n");
+
+ reg16 = cpu_to_le16(0);
+ ret = regmap_bulk_write(data->regmap, VEML6046X00_REG_THDL_L, ®16, sizeof(reg16));
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to set low threshold\n");
+
+ reg16 = cpu_to_le16(U16_MAX);
+ ret = regmap_bulk_write(data->regmap, VEML6046X00_REG_THDH_L, ®16, sizeof(reg16));
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to set high threshold\n");
+
+ ret = regmap_read(data->regmap, VEML6046X00_REG_INT_H, &val);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to clear interrupts\n");
+
+ return 0;
+}
+
+static int veml6046x00_probe(struct i2c_client *i2c)
+{
+ struct device *dev = &i2c->dev;
+ struct veml6046x00_data *data;
+ struct iio_dev *iio;
+ struct regmap *regmap;
+ int ret;
+
+ regmap = devm_regmap_init_i2c(i2c, &veml6046x00_regmap_config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap),
+ "Failed to set regmap\n");
+
+ iio = devm_iio_device_alloc(dev, sizeof(*data));
+ if (!iio)
+ return -ENOMEM;
+
+ data = iio_priv(iio);
+ i2c_set_clientdata(i2c, iio);
+ data->dev = dev;
+ data->regmap = regmap;
+
+ ret = veml6046x00_regfield_init(data);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to init regfield\n");
+
+ ret = devm_regulator_get_enable(dev, "vdd");
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable regulator\n");
+
+ ret = devm_add_action_or_reset(dev, veml6046x00_shutdown_action, data);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to add shut down action\n");
+
+ ret = pm_runtime_set_active(dev);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to activate PM runtime\n");
+
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable PM runtime\n");
+
+ pm_runtime_get_noresume(dev);
+ pm_runtime_set_autosuspend_delay(dev, VEML6046X00_AUTOSUSPEND_MS);
+ pm_runtime_use_autosuspend(dev);
+
+ ret = veml6046x00_validate_part_id(data);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to validate device ID\n");
+
+ iio->name = i2c->name;
+ iio->channels = veml6046x00_channels;
+ iio->num_channels = ARRAY_SIZE(veml6046x00_channels);
+ iio->modes = INDIO_DIRECT_MODE;
+
+ iio->info = &veml6046x00_info_no_irq;
+
+ ret = veml6046x00_setup_device(iio);
+ if (ret < 0)
+ return ret;
+
+ ret = devm_iio_triggered_buffer_setup(dev, iio, NULL,
+ veml6046x00_trig_handler,
+ &veml6046x00_buffer_setup_ops);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to register triggered buffer");
+
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+
+ ret = devm_iio_device_register(dev, iio);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to register iio device");
+
+ return 0;
+}
+
+static int veml6046x00_runtime_suspend(struct device *dev)
+{
+ struct veml6046x00_data *data = iio_priv(dev_get_drvdata(dev));
+
+ return veml6046x00_shutdown(data);
+}
+
+static int veml6046x00_runtime_resume(struct device *dev)
+{
+ struct veml6046x00_data *data = iio_priv(dev_get_drvdata(dev));
+
+ return veml6046x00_power_on(data);
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(veml6046x00_pm_ops, veml6046x00_runtime_suspend,
+ veml6046x00_runtime_resume, NULL);
+
+static const struct of_device_id veml6046x00_of_match[] = {
+ {
+ .compatible = "vishay,veml6046x00",
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, veml6046x00_of_match);
+
+static const struct i2c_device_id veml6046x00_id[] = {
+ { "veml6046x00" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, veml6046x00_id);
+
+static struct i2c_driver veml6046x00_driver = {
+ .driver = {
+ .name = "veml6046x00",
+ .of_match_table = veml6046x00_of_match,
+ .pm = pm_ptr(&veml6046x00_pm_ops),
+ },
+ .probe = veml6046x00_probe,
+ .id_table = veml6046x00_id,
+};
+module_i2c_driver(veml6046x00_driver);
+
+MODULE_AUTHOR("Andreas Klinger <ak@it-klinger.de>");
+MODULE_DESCRIPTION("VEML6046X00 RGBIR Color Sensor");
+MODULE_LICENSE("GPL");
--
2.39.5
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH 3/3] MAINTAINER: add maintainer for veml6046x00
2025-03-16 11:31 [PATCH 0/3] iio:light: add driver for veml6046x00 RGBIR color sensor Andreas Klinger
2025-03-16 11:31 ` [PATCH 1/3] dt-bindings: iio: light: veml6046x00: add " Andreas Klinger
2025-03-16 11:31 ` [PATCH 2/3] iio: light: add support for veml6046x00 RGBIR " Andreas Klinger
@ 2025-03-16 11:31 ` Andreas Klinger
2 siblings, 0 replies; 18+ messages in thread
From: Andreas Klinger @ 2025-03-16 11:31 UTC (permalink / raw)
To: jic23, robh, krzk+dt, conor+dt
Cc: lars, linux-iio, devicetree, linux-kernel, javier.carrasco.cruz,
mazziesaccount, subhajit.ghosh, muditsharma.info, arthur.becker,
ivan.orlov0322, ak
Add maintainer for Vishay veml6046x00 RGBIR color sensor driver and dt
binding.
Signed-off-by: Andreas Klinger <ak@it-klinger.de>
---
MAINTAINERS | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index c9763412a508..5dc2ada88f2c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -25281,6 +25281,12 @@ S: Maintained
F: Documentation/devicetree/bindings/iio/light/vishay,veml6030.yaml
F: drivers/iio/light/veml6030.c
+VISHAY VEML6046X00 RGBIR COLOR SENSOR DRIVER
+M: Andreas Klinger <ak@it-klinger.de>
+S: Maintained
+F: Documentation/devicetree/bindings/iio/light/vishay,veml6046x00.yaml
+F: drivers/iio/light/veml6046x00.c
+
VISHAY VEML6075 UVA AND UVB LIGHT SENSOR DRIVER
M: Javier Carrasco <javier.carrasco.cruz@gmail.com>
S: Maintained
--
2.39.5
^ permalink raw reply related [flat|nested] 18+ messages in thread
* Re: [PATCH 1/3] dt-bindings: iio: light: veml6046x00: add color sensor
2025-03-16 11:31 ` [PATCH 1/3] dt-bindings: iio: light: veml6046x00: add " Andreas Klinger
@ 2025-03-16 12:19 ` Rob Herring (Arm)
2025-03-17 11:00 ` Jonathan Cameron
1 sibling, 0 replies; 18+ messages in thread
From: Rob Herring (Arm) @ 2025-03-16 12:19 UTC (permalink / raw)
To: Andreas Klinger
Cc: mazziesaccount, arthur.becker, subhajit.ghosh, linux-iio,
conor+dt, linux-kernel, javier.carrasco.cruz, muditsharma.info,
devicetree, ivan.orlov0322, lars, jic23, krzk+dt
On Sun, 16 Mar 2025 12:31:29 +0100, Andreas Klinger wrote:
> Add a new compatible for Vishay high accuracy RGBIR color sensor
> veml6046x00.
>
> Signed-off-by: Andreas Klinger <ak@it-klinger.de>
> ---
> .../iio/light/vishay,veml6046x00.yaml | 49 +++++++++++++++++++
> 1 file changed, 49 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/iio/light/vishay,veml6046x00.yaml
>
My bot found errors running 'make dt_binding_check' on your patch:
yamllint warnings/errors:
dtschema/dtc warnings/errors:
Error: Documentation/devicetree/bindings/iio/light/vishay,veml6046x00.example.dts:33.33-34 syntax error
FATAL ERROR: Unable to parse input tree
make[2]: *** [scripts/Makefile.dtbs:131: Documentation/devicetree/bindings/iio/light/vishay,veml6046x00.example.dtb] Error 1
make[2]: *** Waiting for unfinished jobs....
make[1]: *** [/builds/robherring/dt-review-ci/linux/Makefile:1518: dt_binding_check] Error 2
make: *** [Makefile:248: __sub-make] Error 2
doc reference errors (make refcheckdocs):
See https://patchwork.ozlabs.org/project/devicetree-bindings/patch/20250316113131.62884-2-ak@it-klinger.de
The base for the series is generally the latest rc1. A different dependency
should be noted in *this* patch.
If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure 'yamllint' is installed and dt-schema is up to
date:
pip3 install dtschema --upgrade
Please check and re-submit after running the above command yourself. Note
that DT_SCHEMA_FILES can be set to your schema file to speed up checking
your schema. However, it must be unset to test all examples with your schema.
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH 1/3] dt-bindings: iio: light: veml6046x00: add color sensor
2025-03-16 11:31 ` [PATCH 1/3] dt-bindings: iio: light: veml6046x00: add " Andreas Klinger
2025-03-16 12:19 ` Rob Herring (Arm)
@ 2025-03-17 11:00 ` Jonathan Cameron
2025-03-17 11:17 ` Andreas Klinger
1 sibling, 1 reply; 18+ messages in thread
From: Jonathan Cameron @ 2025-03-17 11:00 UTC (permalink / raw)
To: Andreas Klinger
Cc: robh, krzk+dt, conor+dt, lars, linux-iio, devicetree,
linux-kernel, javier.carrasco.cruz, mazziesaccount,
subhajit.ghosh, muditsharma.info, arthur.becker, ivan.orlov0322
On Sun, 16 Mar 2025 12:31:29 +0100
Andreas Klinger <ak@it-klinger.de> wrote:
> Add a new compatible for Vishay high accuracy RGBIR color sensor
> veml6046x00.
>
> Signed-off-by: Andreas Klinger <ak@it-klinger.de>
> ---
> .../iio/light/vishay,veml6046x00.yaml | 49 +++++++++++++++++++
> 1 file changed, 49 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/iio/light/vishay,veml6046x00.yaml
>
> diff --git a/Documentation/devicetree/bindings/iio/light/vishay,veml6046x00.yaml b/Documentation/devicetree/bindings/iio/light/vishay,veml6046x00.yaml
> new file mode 100644
> index 000000000000..3207800fc539
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/light/vishay,veml6046x00.yaml
> @@ -0,0 +1,49 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/iio/light/vishay,veml6046x00.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Vishay VEML6046X00 High accuracy RGBIR color sensor
> +
> +maintainers:
> + - Andreas Klinger <ak@it-klinger.de>
> +
> +description:
> + VEML6046X00 datasheet at https://www.vishay.com/docs/80173/veml6046x00.pdf
> +
> +properties:
> + compatible:
> + enum:
> + - vishay,veml6046x00
> +
> + reg:
> + maxItems: 1
> +
> + vdd-supply: true
> +
> + interrupts:
> + maxItems: 1
> +
> +required:
> + - compatible
> + - reg
> + - vdd-supply
> +
> +additionalProperties: false
> +
> +examples:
> + - |
> + i2c {
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + color-sensor@29 {
> + compatible = "vishay,veml6046x00";
> + reg = <0x29>;
> + vdd-supply = <&vdd_reg>;
> + interrupt-parent = <&gpio2>;
> + interrupts = <3 IRQ_TYPE_EDGE_FALLING>;
Need an include for this I think. Make sure to test build your
bindings following the instructions in the bot message.
Thanks,
Jonathan
> + };
> + };
> +...
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH 1/3] dt-bindings: iio: light: veml6046x00: add color sensor
2025-03-17 11:00 ` Jonathan Cameron
@ 2025-03-17 11:17 ` Andreas Klinger
2025-03-17 11:26 ` Krzysztof Kozlowski
0 siblings, 1 reply; 18+ messages in thread
From: Andreas Klinger @ 2025-03-17 11:17 UTC (permalink / raw)
To: Jonathan Cameron
Cc: robh, krzk+dt, conor+dt, lars, linux-iio, devicetree,
linux-kernel, javier.carrasco.cruz, mazziesaccount,
subhajit.ghosh, muditsharma.info, arthur.becker, ivan.orlov0322
[-- Attachment #1: Type: text/plain, Size: 2427 bytes --]
Hi Jonathan,
Jonathan Cameron <jic23@kernel.org> schrieb am Mo, 17. Mär 11:00:
> On Sun, 16 Mar 2025 12:31:29 +0100
> Andreas Klinger <ak@it-klinger.de> wrote:
>
> > Add a new compatible for Vishay high accuracy RGBIR color sensor
> > veml6046x00.
> >
> > Signed-off-by: Andreas Klinger <ak@it-klinger.de>
> > ---
> > .../iio/light/vishay,veml6046x00.yaml | 49 +++++++++++++++++++
> > 1 file changed, 49 insertions(+)
> > create mode 100644 Documentation/devicetree/bindings/iio/light/vishay,veml6046x00.yaml
> >
> > diff --git a/Documentation/devicetree/bindings/iio/light/vishay,veml6046x00.yaml b/Documentation/devicetree/bindings/iio/light/vishay,veml6046x00.yaml
> > new file mode 100644
> > index 000000000000..3207800fc539
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/iio/light/vishay,veml6046x00.yaml
> > @@ -0,0 +1,49 @@
> > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > +%YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/iio/light/vishay,veml6046x00.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: Vishay VEML6046X00 High accuracy RGBIR color sensor
> > +
> > +maintainers:
> > + - Andreas Klinger <ak@it-klinger.de>
> > +
> > +description:
> > + VEML6046X00 datasheet at https://www.vishay.com/docs/80173/veml6046x00.pdf
> > +
> > +properties:
> > + compatible:
> > + enum:
> > + - vishay,veml6046x00
> > +
> > + reg:
> > + maxItems: 1
> > +
> > + vdd-supply: true
> > +
> > + interrupts:
> > + maxItems: 1
> > +
> > +required:
> > + - compatible
> > + - reg
> > + - vdd-supply
> > +
> > +additionalProperties: false
> > +
> > +examples:
> > + - |
> > + i2c {
> > + #address-cells = <1>;
> > + #size-cells = <0>;
> > +
> > + color-sensor@29 {
> > + compatible = "vishay,veml6046x00";
> > + reg = <0x29>;
> > + vdd-supply = <&vdd_reg>;
> > + interrupt-parent = <&gpio2>;
> > + interrupts = <3 IRQ_TYPE_EDGE_FALLING>;
> Need an include for this I think. Make sure to test build your
> bindings following the instructions in the bot message.
I already sent out an version 2 yesterday which is with the include, tested and
already reviewed by Krzysztof.
Andreas
>
> Thanks,
>
> Jonathan
>
> > + };
> > + };
> > +...
>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 659 bytes --]
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH 1/3] dt-bindings: iio: light: veml6046x00: add color sensor
2025-03-17 11:17 ` Andreas Klinger
@ 2025-03-17 11:26 ` Krzysztof Kozlowski
0 siblings, 0 replies; 18+ messages in thread
From: Krzysztof Kozlowski @ 2025-03-17 11:26 UTC (permalink / raw)
To: Andreas Klinger, Jonathan Cameron
Cc: robh, krzk+dt, conor+dt, lars, linux-iio, devicetree,
linux-kernel, javier.carrasco.cruz, mazziesaccount,
subhajit.ghosh, muditsharma.info, arthur.becker, ivan.orlov0322
On 17/03/2025 12:17, Andreas Klinger wrote:
>>> + - |
>>> + i2c {
>>> + #address-cells = <1>;
>>> + #size-cells = <0>;
>>> +
>>> + color-sensor@29 {
>>> + compatible = "vishay,veml6046x00";
>>> + reg = <0x29>;
>>> + vdd-supply = <&vdd_reg>;
>>> + interrupt-parent = <&gpio2>;
>>> + interrupts = <3 IRQ_TYPE_EDGE_FALLING>;
>> Need an include for this I think. Make sure to test build your
>> bindings following the instructions in the bot message.
>
> I already sent out an version 2 yesterday which is with the include, tested and
> already reviewed by Krzysztof.
The point is that you should test it, not rely on us and our tools (e.g.
my or Rob's machine). If our tools report such issues, it is already too
late because it means you did not follow the process.
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH 2/3] iio: light: add support for veml6046x00 RGBIR color sensor
2025-03-16 11:31 ` [PATCH 2/3] iio: light: add support for veml6046x00 RGBIR " Andreas Klinger
@ 2025-03-17 11:50 ` Jonathan Cameron
2025-03-18 2:15 ` Andreas Klinger
2025-04-06 8:43 ` Andreas Klinger
0 siblings, 2 replies; 18+ messages in thread
From: Jonathan Cameron @ 2025-03-17 11:50 UTC (permalink / raw)
To: Andreas Klinger
Cc: robh, krzk+dt, conor+dt, lars, linux-iio, devicetree,
linux-kernel, javier.carrasco.cruz, mazziesaccount,
subhajit.ghosh, muditsharma.info, arthur.becker, ivan.orlov0322
On Sun, 16 Mar 2025 12:31:30 +0100
Andreas Klinger <ak@it-klinger.de> wrote:
> Add Vishay VEML6046X00 high accuracy RGBIR color sensor.
>
> This sensor provides three colour (red, green and blue) as well as one
> infrared (IR) channel through I2C.
>
> Support direct and buffered mode.
>
> An optional interrupt for signaling green colour threshold underflow or
> overflow is not supported so far.
>
> Signed-off-by: Andreas Klinger <ak@it-klinger.de>
Hi Andreas,
A nice clean driver.
A few comments and questions inline.
Jonathan
> diff --git a/drivers/iio/light/veml6046x00.c b/drivers/iio/light/veml6046x00.c
> new file mode 100644
> index 000000000000..8e6232e1ab70
> --- /dev/null
> +++ b/drivers/iio/light/veml6046x00.c
> @@ -0,0 +1,890 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * VEML6046X00 High Accuracy RGBIR Color Sensor
> + *
> + * Copyright (c) 2025 Andreas Klinger <ak@it-klinger.de>
> + */
> +
> +#include <linux/bitfield.h>
> +#include <linux/i2c.h>
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/regmap.h>
> +#include <linux/types.h>
> +#include <linux/units.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/iio/trigger.h>
So far you aren't providing a trigger so I'm not
expect to see this header used. Looking at the datasheet
I see there is a dataready interrupt, but it doesn't look like
the device has an autonomous / sequencer type mode, so will always
need to use another trigger (hrtimer, sysfs or some other hardware
source).
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/triggered_buffer.h>
> +
> +struct veml6046x00_scan_buf {
> + __le16 chans[4];
> +
> + s64 timestamp __aligned(8);
aligned_s64 now available as a type to use here.
> +};
> +static IIO_DEVICE_ATTR_RO(in_illuminance_period_available, 0);
> +
> +static struct attribute *veml6046x00_event_attributes[] = {
> + &iio_dev_attr_in_illuminance_period_available.dev_attr.attr,
I thought we didn't have event support yet? If not why do we
have event related attributes?
> + NULL
> +};
> +
> +static const struct attribute_group veml6046x00_event_attr_group = {
> + .attrs = veml6046x00_event_attributes,
> +};
> +
> +static int veml6046x00_set_it(struct iio_dev *iio, int val, int val2)
> +{
> + struct veml6046x00_data *data = iio_priv(iio);
> + int ret, new_it;
> +
> + if (val)
> + return -EINVAL;
> +
> + switch (val2) {
> + case 3125:
> + new_it = 0x00;
> + break;
> + case 6250:
> + new_it = 0x01;
> + break;
> + case 12500:
> + new_it = 0x02;
> + break;
> + case 25000:
> + new_it = 0x03;
> + break;
> + case 50000:
> + new_it = 0x04;
> + break;
> + case 100000:
> + new_it = 0x05;
> + break;
> + case 200000:
> + new_it = 0x06;
> + break;
> + case 400000:
> + new_it = 0x07;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + ret = regmap_field_write(data->rf.it, new_it);
> + if (ret)
> + return ret;
return regmap_field_write()
> +
> + return 0;
> +}
> +
> +static int veml6046x00_set_scale(struct iio_dev *iio, int val, int val2)
> +{
> + struct veml6046x00_data *data = iio_priv(iio);
> + int new_scale;
> +
> + if (val == 0 && val2 == 250000) {
Maybe a lookup table and a loop?
> + new_scale = FIELD_PREP(VEML6046X00_CONF1_GAIN, VEML6046X00_GAIN_0_5) |
> + VEML6046X00_CONF1_PD_D2;
> + } else if (val == 0 && val2 == 330000) {
> + new_scale = FIELD_PREP(VEML6046X00_CONF1_GAIN, VEML6046X00_GAIN_0_66) |
> + VEML6046X00_CONF1_PD_D2;
> + } else if (val == 0 && val2 == 500000) {
> + new_scale = FIELD_PREP(VEML6046X00_CONF1_GAIN, VEML6046X00_GAIN_0_5);
> + } else if (val == 0 && val2 == 660000) {
> + new_scale = FIELD_PREP(VEML6046X00_CONF1_GAIN, VEML6046X00_GAIN_0_66);
> + } else if (val == 1 && val2 == 0) {
> + new_scale = FIELD_PREP(VEML6046X00_CONF1_GAIN, VEML6046X00_GAIN_1);
> + } else if (val == 2 && val2 == 0) {
> + new_scale = FIELD_PREP(VEML6046X00_CONF1_GAIN, VEML6046X00_GAIN_2);
> + } else {
> + return -EINVAL;
> + }
> +
> + return regmap_update_bits(data->regmap, VEML6046X00_REG_CONF1,
> + VEML6046X00_CONF1_GAIN |
> + VEML6046X00_CONF1_PD_D2,
> + new_scale);
> +}
> +
> +static int veml6046x00_get_scale(struct veml6046x00_data *data,
> + int *val, int *val2)
How is this related to integration time? I'd normally expect
to see that read in here somewhere as well as doubling integration
time tends to double scale.
> +{
> + int ret, reg;
> +
> + ret = regmap_read(data->regmap, VEML6046X00_REG_CONF1, ®);
> + if (ret)
> + return ret;
> +
> + switch (FIELD_GET(VEML6046X00_CONF1_GAIN, reg)) {
> + case 0:
> + *val = 1;
> + *val2 = 0;
> + break;
> + case 1:
> + *val = 2;
> + *val2 = 0;
> + break;
> + case 2:
> + *val = 0;
> + *val2 = 660000;
> + break;
> + case 3:
> + *val = 0;
> + *val2 = 500000;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + if (reg & VEML6046X00_CONF1_PD_D2)
> + *val2 /= 2;
> +
> + return IIO_VAL_INT_PLUS_MICRO;
> +}
> +
> +static int veml6046x00_set_mode(struct veml6046x00_data *data, bool state)
> +{
> + return regmap_field_write(data->rf.mode, state);
as below.
> +}
> +
> +static int veml6046x00_set_trig(struct veml6046x00_data *data, bool state)
> +{
> + return regmap_field_write(data->rf.trig, state);
I'd argue these two aren't worth bothering with because the
field naming etc makes a direct call to regmap_field_write() obvious enough.
> +}
> +
> +static int veml6046x00_wait_data_available(struct iio_dev *iio, int usecs)
Document return value as non obvious.
> +{
> + struct veml6046x00_data *data = iio_priv(iio);
> + int ret, reg;
> + int cnt = 2;
> + int i;
> +
> + for (i = 0; i < cnt; i++) {
> + ret = regmap_read(data->regmap, VEML6046X00_REG_INT_H, ®);
> + if (ret) {
> + dev_err(data->dev,
> + "Failed to read interrupt register %d\n", ret);
> + return -EIO;
> + }
> +
> + if (reg & VEML6046X00_INT_DRDY)
> + return 1;
> +
> + if (i < cnt)
> + fsleep(usecs);
> + }
> +
> + return 0;
> +}
> +
> +static int veml6046x00_single_read(struct iio_dev *iio,
> + enum iio_modifier modifier, int *val)
> +{
> + struct veml6046x00_data *data = iio_priv(iio);
> + int addr, it_usec, ret;
> + uint8_t reg[2];
> +
> + switch (modifier) {
> + case IIO_MOD_LIGHT_RED:
> + addr = VEML6046X00_REG_R_L;
> + break;
break indent not matching kernel style. Needs to be one more tab in.
> + case IIO_MOD_LIGHT_GREEN:
> + addr = VEML6046X00_REG_G_L;
> + break;
> + case IIO_MOD_LIGHT_BLUE:
> + addr = VEML6046X00_REG_B_L;
> + break;
> + case IIO_MOD_LIGHT_IR:
> + addr = VEML6046X00_REG_IR_L;
> + break;
> + default:
> + return -EINVAL;
> + }
> + ret = pm_runtime_resume_and_get(data->dev);
> + if (ret)
> + return ret;
> +
> + ret = veml6046x00_get_it_usec(data, &it_usec);
> + if (ret < 0)
> + return ret;
> +
> + veml6046x00_set_mode(data, 1);
Check for errors.
> +
> + veml6046x00_set_trig(data, 1);
> +
> + /* integration time + 10 % to ensure completion */
> + fsleep(it_usec + (it_usec / 10));
> +
> + ret = veml6046x00_wait_data_available(iio, it_usec * 10);
> + if (ret == 1) {
> + dev_dbg(data->dev, "data ready\n");
> + } else {
> + dev_warn(data->dev, "no data ready ret: %d\n", ret);
> + goto no_data;
> + }
> +
> + ret = iio_device_claim_direct_mode(iio);
I'm killing these off slowly. To save a follow up patch,
if (!iio_device_claim_direct(iio))
return -EBUSY;
> + if (ret)
> + return ret;
> +
> + ret = regmap_bulk_read(data->regmap, addr, reg, sizeof(reg));
> + iio_device_release_direct_mode(iio);
iio_device_release_direct(iio);
If not I'll roll in the change with the many other driver updates
this entails.
> + if (ret)
> + return ret;
> +
> + pm_runtime_mark_last_busy(data->dev);
> + pm_runtime_put_autosuspend(data->dev);
> +
> + *val = reg[1] << 8 | reg[0];
That's an endian conversion. Use get_unaligned_le16() I think
Or read into an __le16 in the first place and you can use
le16_to_cpu() or similar.
> +
> + return IIO_VAL_INT;
> +
> +no_data:
> + pm_runtime_mark_last_busy(data->dev);
> + pm_runtime_put_autosuspend(data->dev);
> +
> + return -EINVAL;
> +}
> +static int veml6046x00_buffer_preenable(struct iio_dev *iio)
> +{
> + struct veml6046x00_data *data = iio_priv(iio);
> + struct device *dev = data->dev;
> + int ret;
> +
> + ret = veml6046x00_set_mode(data, 0);
> + if (ret)
> + dev_err(data->dev, "Failed to set mode %d\n", ret);
If these fail, error out. We will fail to enter buffered mode, but
that is probably the correct thing to do if we are having comms
issues or similar.
> +
> + ret = veml6046x00_set_trig(data, 0);
> + if (ret)
> + dev_err(data->dev, "Failed to set trigger %d\n", ret);
> +
> + return pm_runtime_resume_and_get(dev);
> +}
> +static int veml6046x00_validate_part_id(struct veml6046x00_data *data)
> +{
> + int part_id, ret;
> + __le16 reg;
> +
> + ret = regmap_bulk_read(data->regmap, VEML6046X00_REG_ID_L, ®, sizeof(reg));
> + if (ret) {
> + dev_info(data->dev, "Failed to read ID\n");
return dev_err_probe() for this one.
> + return -EIO;
> + }
>
> +static int veml6046x00_setup_device(struct iio_dev *iio)
> +{
> + struct veml6046x00_data *data = iio_priv(iio);
> + struct device *dev = data->dev;
> + int ret, val;
> + __le16 reg16;
> + uint8_t reg[2];
> +
> + reg[0] = VEML6046X00_CONF0_AF;
> + reg[1] = 0x00;
> + ret = regmap_bulk_write(data->regmap, VEML6046X00_REG_CONF0, reg, sizeof(reg));
> + if (ret)
> + return dev_err_probe(dev, ret, "Failed to set configuration\n");
> +
> + reg16 = cpu_to_le16(0);
> + ret = regmap_bulk_write(data->regmap, VEML6046X00_REG_THDL_L, ®16, sizeof(reg16));
> + if (ret)
> + return dev_err_probe(dev, ret, "Failed to set low threshold\n");
> +
> + reg16 = cpu_to_le16(U16_MAX);
> + ret = regmap_bulk_write(data->regmap, VEML6046X00_REG_THDH_L, ®16, sizeof(reg16));
> + if (ret)
> + return dev_err_probe(dev, ret, "Failed to set high threshold\n");
> +
> + ret = regmap_read(data->regmap, VEML6046X00_REG_INT_H, &val);
> + if (ret < 0)
if (ret) here as well. Good to be consistent for all regmap calls. None of them
return > 0 as far as I know.
> + return dev_err_probe(dev, ret, "Failed to clear interrupts\n");
> +
> + return 0;
> +}
> +
> +static int veml6046x00_probe(struct i2c_client *i2c)
> +{
> + struct device *dev = &i2c->dev;
> + struct veml6046x00_data *data;
> + struct iio_dev *iio;
> + struct regmap *regmap;
> + int ret;
> +
> + regmap = devm_regmap_init_i2c(i2c, &veml6046x00_regmap_config);
> + if (IS_ERR(regmap))
> + return dev_err_probe(dev, PTR_ERR(regmap),
> + "Failed to set regmap\n");
> +
> + iio = devm_iio_device_alloc(dev, sizeof(*data));
> + if (!iio)
> + return -ENOMEM;
> +
> + data = iio_priv(iio);
> + i2c_set_clientdata(i2c, iio);
> + data->dev = dev;
> + data->regmap = regmap;
> +
> + ret = veml6046x00_regfield_init(data);
> + if (ret)
> + return dev_err_probe(dev, ret, "Failed to init regfield\n");
> +
> + ret = devm_regulator_get_enable(dev, "vdd");
> + if (ret)
> + return dev_err_probe(dev, ret, "Failed to enable regulator\n");
> +
> + ret = devm_add_action_or_reset(dev, veml6046x00_shutdown_action, data);
Mostly we want a devm action to match against a specific setup operation. Here is
it that the device comes up in non shut down state? Perhaps a comment to
make it clear. Also, how do we know it's in a good state rather than part
configured by someone else? I'm not seeing a reset sequence though perhaps
that effectively happens in setup_device()
> + if (ret < 0)
> + return dev_err_probe(dev, ret, "Failed to add shut down action\n");
> +
> + ret = pm_runtime_set_active(dev);
> + if (ret < 0)
> + return dev_err_probe(dev, ret, "Failed to activate PM runtime\n");
> +
> + ret = devm_pm_runtime_enable(dev);
> + if (ret)
> + return dev_err_probe(dev, ret, "Failed to enable PM runtime\n");
> +
> + pm_runtime_get_noresume(dev);
> + pm_runtime_set_autosuspend_delay(dev, VEML6046X00_AUTOSUSPEND_MS);
> + pm_runtime_use_autosuspend(dev);
> +
> + ret = veml6046x00_validate_part_id(data);
> + if (ret)
> + return dev_err_probe(dev, ret, "Failed to validate device ID\n");
> +
> + iio->name = i2c->name;
Prefer this hard coded. Depending on the firmware type, things like that have
an annoying habbit of not remaining predictable or stable.
> + iio->channels = veml6046x00_channels;
> + iio->num_channels = ARRAY_SIZE(veml6046x00_channels);
> + iio->modes = INDIO_DIRECT_MODE;
> +
> + iio->info = &veml6046x00_info_no_irq;
> +
> + ret = veml6046x00_setup_device(iio);
> + if (ret < 0)
> + return ret;
> +
> + ret = devm_iio_triggered_buffer_setup(dev, iio, NULL,
> + veml6046x00_trig_handler,
> + &veml6046x00_buffer_setup_ops);
> + if (ret)
> + return dev_err_probe(dev, ret,
> + "Failed to register triggered buffer");
> +
> + pm_runtime_mark_last_busy(dev);
> + pm_runtime_put_autosuspend(dev);
> +
> + ret = devm_iio_device_register(dev, iio);
> + if (ret)
> + return dev_err_probe(dev, ret, "Failed to register iio device");
> +
> + return 0;
> +}
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH 2/3] iio: light: add support for veml6046x00 RGBIR color sensor
2025-03-17 11:50 ` Jonathan Cameron
@ 2025-03-18 2:15 ` Andreas Klinger
2025-03-23 11:51 ` Jonathan Cameron
2025-04-06 8:43 ` Andreas Klinger
1 sibling, 1 reply; 18+ messages in thread
From: Andreas Klinger @ 2025-03-18 2:15 UTC (permalink / raw)
To: Jonathan Cameron
Cc: robh, krzk+dt, conor+dt, lars, linux-iio, devicetree,
linux-kernel, javier.carrasco.cruz, mazziesaccount,
subhajit.ghosh, muditsharma.info, arthur.becker, ivan.orlov0322
[-- Attachment #1: Type: text/plain, Size: 15491 bytes --]
Hi Jonathan,
thanks for the review and comments. Answers are inline below.
Andreas
Jonathan Cameron <jic23@kernel.org> schrieb am Mo, 17. Mär 11:50:
> On Sun, 16 Mar 2025 12:31:30 +0100
> Andreas Klinger <ak@it-klinger.de> wrote:
>
> > Add Vishay VEML6046X00 high accuracy RGBIR color sensor.
> >
> > This sensor provides three colour (red, green and blue) as well as one
> > infrared (IR) channel through I2C.
> >
> > Support direct and buffered mode.
> >
> > An optional interrupt for signaling green colour threshold underflow or
> > overflow is not supported so far.
> >
> > Signed-off-by: Andreas Klinger <ak@it-klinger.de>
>
> Hi Andreas,
>
> A nice clean driver.
> A few comments and questions inline.
>
> Jonathan
>
> > diff --git a/drivers/iio/light/veml6046x00.c b/drivers/iio/light/veml6046x00.c
> > new file mode 100644
> > index 000000000000..8e6232e1ab70
> > --- /dev/null
> > +++ b/drivers/iio/light/veml6046x00.c
> > @@ -0,0 +1,890 @@
> > +// SPDX-License-Identifier: GPL-2.0+
> > +/*
> > + * VEML6046X00 High Accuracy RGBIR Color Sensor
> > + *
> > + * Copyright (c) 2025 Andreas Klinger <ak@it-klinger.de>
> > + */
> > +
> > +#include <linux/bitfield.h>
> > +#include <linux/i2c.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/module.h>
> > +#include <linux/regmap.h>
> > +#include <linux/types.h>
> > +#include <linux/units.h>
> > +#include <linux/pm_runtime.h>
> > +#include <linux/iio/iio.h>
> > +#include <linux/iio/sysfs.h>
> > +#include <linux/iio/trigger.h>
>
> So far you aren't providing a trigger so I'm not
> expect to see this header used. Looking at the datasheet
> I see there is a dataready interrupt, but it doesn't look like
> the device has an autonomous / sequencer type mode, so will always
> need to use another trigger (hrtimer, sysfs or some other hardware
> source).
I'll remove.
> > +#include <linux/iio/trigger_consumer.h>
> > +#include <linux/iio/triggered_buffer.h>
>
> > +
> > +struct veml6046x00_scan_buf {
> > + __le16 chans[4];
> > +
> > + s64 timestamp __aligned(8);
> aligned_s64 now available as a type to use here.
>
> > +};
>
> > +static IIO_DEVICE_ATTR_RO(in_illuminance_period_available, 0);
> > +
> > +static struct attribute *veml6046x00_event_attributes[] = {
> > + &iio_dev_attr_in_illuminance_period_available.dev_attr.attr,
> I thought we didn't have event support yet? If not why do we
> have event related attributes?
I started implementing including event support by using the interrupt and the
threshold values which can be programmed on the sensor. It turned out that this
is not working as i expected on my side. Then i removed everything which is
about events, but forgot this here. I'm in the process of clarifying this with
the vendor. The plan is to submit a follow up patch later.
So i'll remove for this turn.
> > + NULL
> > +};
> > +
> > +static const struct attribute_group veml6046x00_event_attr_group = {
> > + .attrs = veml6046x00_event_attributes,
> > +};
>
>
> > +
> > +static int veml6046x00_set_it(struct iio_dev *iio, int val, int val2)
> > +{
> > + struct veml6046x00_data *data = iio_priv(iio);
> > + int ret, new_it;
> > +
> > + if (val)
> > + return -EINVAL;
> > +
> > + switch (val2) {
> > + case 3125:
> > + new_it = 0x00;
> > + break;
> > + case 6250:
> > + new_it = 0x01;
> > + break;
> > + case 12500:
> > + new_it = 0x02;
> > + break;
> > + case 25000:
> > + new_it = 0x03;
> > + break;
> > + case 50000:
> > + new_it = 0x04;
> > + break;
> > + case 100000:
> > + new_it = 0x05;
> > + break;
> > + case 200000:
> > + new_it = 0x06;
> > + break;
> > + case 400000:
> > + new_it = 0x07;
> > + break;
> > + default:
> > + return -EINVAL;
> > + }
> > +
> > + ret = regmap_field_write(data->rf.it, new_it);
> > + if (ret)
> > + return ret;
>
> return regmap_field_write()
>
> > +
> > + return 0;
> > +}
> > +
> > +static int veml6046x00_set_scale(struct iio_dev *iio, int val, int val2)
> > +{
> > + struct veml6046x00_data *data = iio_priv(iio);
> > + int new_scale;
> > +
> > + if (val == 0 && val2 == 250000) {
>
> Maybe a lookup table and a loop?
Yes, would be nicer.
> > + new_scale = FIELD_PREP(VEML6046X00_CONF1_GAIN, VEML6046X00_GAIN_0_5) |
> > + VEML6046X00_CONF1_PD_D2;
> > + } else if (val == 0 && val2 == 330000) {
> > + new_scale = FIELD_PREP(VEML6046X00_CONF1_GAIN, VEML6046X00_GAIN_0_66) |
> > + VEML6046X00_CONF1_PD_D2;
> > + } else if (val == 0 && val2 == 500000) {
> > + new_scale = FIELD_PREP(VEML6046X00_CONF1_GAIN, VEML6046X00_GAIN_0_5);
> > + } else if (val == 0 && val2 == 660000) {
> > + new_scale = FIELD_PREP(VEML6046X00_CONF1_GAIN, VEML6046X00_GAIN_0_66);
> > + } else if (val == 1 && val2 == 0) {
> > + new_scale = FIELD_PREP(VEML6046X00_CONF1_GAIN, VEML6046X00_GAIN_1);
> > + } else if (val == 2 && val2 == 0) {
> > + new_scale = FIELD_PREP(VEML6046X00_CONF1_GAIN, VEML6046X00_GAIN_2);
> > + } else {
> > + return -EINVAL;
> > + }
> > +
> > + return regmap_update_bits(data->regmap, VEML6046X00_REG_CONF1,
> > + VEML6046X00_CONF1_GAIN |
> > + VEML6046X00_CONF1_PD_D2,
> > + new_scale);
> > +}
> > +
> > +static int veml6046x00_get_scale(struct veml6046x00_data *data,
> > + int *val, int *val2)
>
> How is this related to integration time? I'd normally expect
> to see that read in here somewhere as well as doubling integration
> time tends to double scale.
>
That's true.
So i'll also need to present different gain values depending on the integration time.
> > +{
> > + int ret, reg;
> > +
> > + ret = regmap_read(data->regmap, VEML6046X00_REG_CONF1, ®);
> > + if (ret)
> > + return ret;
> > +
> > + switch (FIELD_GET(VEML6046X00_CONF1_GAIN, reg)) {
> > + case 0:
> > + *val = 1;
> > + *val2 = 0;
> > + break;
> > + case 1:
> > + *val = 2;
> > + *val2 = 0;
> > + break;
> > + case 2:
> > + *val = 0;
> > + *val2 = 660000;
> > + break;
> > + case 3:
> > + *val = 0;
> > + *val2 = 500000;
> > + break;
> > + default:
> > + return -EINVAL;
> > + }
> > +
> > + if (reg & VEML6046X00_CONF1_PD_D2)
> > + *val2 /= 2;
> > +
> > + return IIO_VAL_INT_PLUS_MICRO;
> > +}
> > +
> > +static int veml6046x00_set_mode(struct veml6046x00_data *data, bool state)
> > +{
> > + return regmap_field_write(data->rf.mode, state);
> as below.
>
> > +}
> > +
> > +static int veml6046x00_set_trig(struct veml6046x00_data *data, bool state)
> > +{
> > + return regmap_field_write(data->rf.trig, state);
>
> I'd argue these two aren't worth bothering with because the
> field naming etc makes a direct call to regmap_field_write() obvious enough.
>
> > +}
> > +
> > +static int veml6046x00_wait_data_available(struct iio_dev *iio, int usecs)
>
> Document return value as non obvious.
>
> > +{
> > + struct veml6046x00_data *data = iio_priv(iio);
> > + int ret, reg;
> > + int cnt = 2;
> > + int i;
> > +
> > + for (i = 0; i < cnt; i++) {
> > + ret = regmap_read(data->regmap, VEML6046X00_REG_INT_H, ®);
> > + if (ret) {
> > + dev_err(data->dev,
> > + "Failed to read interrupt register %d\n", ret);
> > + return -EIO;
> > + }
> > +
> > + if (reg & VEML6046X00_INT_DRDY)
> > + return 1;
> > +
> > + if (i < cnt)
> > + fsleep(usecs);
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int veml6046x00_single_read(struct iio_dev *iio,
> > + enum iio_modifier modifier, int *val)
> > +{
> > + struct veml6046x00_data *data = iio_priv(iio);
> > + int addr, it_usec, ret;
> > + uint8_t reg[2];
> > +
> > + switch (modifier) {
> > + case IIO_MOD_LIGHT_RED:
> > + addr = VEML6046X00_REG_R_L;
> > + break;
>
> break indent not matching kernel style. Needs to be one more tab in.
>
> > + case IIO_MOD_LIGHT_GREEN:
> > + addr = VEML6046X00_REG_G_L;
> > + break;
> > + case IIO_MOD_LIGHT_BLUE:
> > + addr = VEML6046X00_REG_B_L;
> > + break;
> > + case IIO_MOD_LIGHT_IR:
> > + addr = VEML6046X00_REG_IR_L;
> > + break;
> > + default:
> > + return -EINVAL;
> > + }
> > + ret = pm_runtime_resume_and_get(data->dev);
> > + if (ret)
> > + return ret;
> > +
> > + ret = veml6046x00_get_it_usec(data, &it_usec);
> > + if (ret < 0)
> > + return ret;
> > +
> > + veml6046x00_set_mode(data, 1);
> Check for errors.
> > +
> > + veml6046x00_set_trig(data, 1);
> > +
> > + /* integration time + 10 % to ensure completion */
> > + fsleep(it_usec + (it_usec / 10));
> > +
> > + ret = veml6046x00_wait_data_available(iio, it_usec * 10);
> > + if (ret == 1) {
> > + dev_dbg(data->dev, "data ready\n");
> > + } else {
> > + dev_warn(data->dev, "no data ready ret: %d\n", ret);
> > + goto no_data;
> > + }
> > +
> > + ret = iio_device_claim_direct_mode(iio);
> I'm killing these off slowly. To save a follow up patch,
> if (!iio_device_claim_direct(iio))
> return -EBUSY;
> > + if (ret)
> > + return ret;
> > +
> > + ret = regmap_bulk_read(data->regmap, addr, reg, sizeof(reg));
> > + iio_device_release_direct_mode(iio);
>
> iio_device_release_direct(iio);
>
> If not I'll roll in the change with the many other driver updates
> this entails.
>
>
> > + if (ret)
> > + return ret;
> > +
> > + pm_runtime_mark_last_busy(data->dev);
> > + pm_runtime_put_autosuspend(data->dev);
> > +
> > + *val = reg[1] << 8 | reg[0];
>
> That's an endian conversion. Use get_unaligned_le16() I think
> Or read into an __le16 in the first place and you can use
> le16_to_cpu() or similar.
>
>
> > +
> > + return IIO_VAL_INT;
> > +
> > +no_data:
> > + pm_runtime_mark_last_busy(data->dev);
> > + pm_runtime_put_autosuspend(data->dev);
> > +
> > + return -EINVAL;
> > +}
>
>
>
> > +static int veml6046x00_buffer_preenable(struct iio_dev *iio)
> > +{
> > + struct veml6046x00_data *data = iio_priv(iio);
> > + struct device *dev = data->dev;
> > + int ret;
> > +
> > + ret = veml6046x00_set_mode(data, 0);
> > + if (ret)
> > + dev_err(data->dev, "Failed to set mode %d\n", ret);
>
> If these fail, error out. We will fail to enter buffered mode, but
> that is probably the correct thing to do if we are having comms
> issues or similar.
>
> > +
> > + ret = veml6046x00_set_trig(data, 0);
> > + if (ret)
> > + dev_err(data->dev, "Failed to set trigger %d\n", ret);
> > +
> > + return pm_runtime_resume_and_get(dev);
> > +}
>
>
>
> > +static int veml6046x00_validate_part_id(struct veml6046x00_data *data)
> > +{
> > + int part_id, ret;
> > + __le16 reg;
> > +
> > + ret = regmap_bulk_read(data->regmap, VEML6046X00_REG_ID_L, ®, sizeof(reg));
> > + if (ret) {
> > + dev_info(data->dev, "Failed to read ID\n");
>
> return dev_err_probe() for this one.
>
> > + return -EIO;
> > + }
> >
> > +static int veml6046x00_setup_device(struct iio_dev *iio)
> > +{
> > + struct veml6046x00_data *data = iio_priv(iio);
> > + struct device *dev = data->dev;
> > + int ret, val;
> > + __le16 reg16;
> > + uint8_t reg[2];
> > +
> > + reg[0] = VEML6046X00_CONF0_AF;
> > + reg[1] = 0x00;
> > + ret = regmap_bulk_write(data->regmap, VEML6046X00_REG_CONF0, reg, sizeof(reg));
> > + if (ret)
> > + return dev_err_probe(dev, ret, "Failed to set configuration\n");
> > +
> > + reg16 = cpu_to_le16(0);
> > + ret = regmap_bulk_write(data->regmap, VEML6046X00_REG_THDL_L, ®16, sizeof(reg16));
> > + if (ret)
> > + return dev_err_probe(dev, ret, "Failed to set low threshold\n");
> > +
> > + reg16 = cpu_to_le16(U16_MAX);
> > + ret = regmap_bulk_write(data->regmap, VEML6046X00_REG_THDH_L, ®16, sizeof(reg16));
> > + if (ret)
> > + return dev_err_probe(dev, ret, "Failed to set high threshold\n");
> > +
> > + ret = regmap_read(data->regmap, VEML6046X00_REG_INT_H, &val);
> > + if (ret < 0)
>
> if (ret) here as well. Good to be consistent for all regmap calls. None of them
> return > 0 as far as I know.
>
>
> > + return dev_err_probe(dev, ret, "Failed to clear interrupts\n");
> > +
> > + return 0;
> > +}
> > +
> > +static int veml6046x00_probe(struct i2c_client *i2c)
> > +{
> > + struct device *dev = &i2c->dev;
> > + struct veml6046x00_data *data;
> > + struct iio_dev *iio;
> > + struct regmap *regmap;
> > + int ret;
> > +
> > + regmap = devm_regmap_init_i2c(i2c, &veml6046x00_regmap_config);
> > + if (IS_ERR(regmap))
> > + return dev_err_probe(dev, PTR_ERR(regmap),
> > + "Failed to set regmap\n");
> > +
> > + iio = devm_iio_device_alloc(dev, sizeof(*data));
> > + if (!iio)
> > + return -ENOMEM;
> > +
> > + data = iio_priv(iio);
> > + i2c_set_clientdata(i2c, iio);
> > + data->dev = dev;
> > + data->regmap = regmap;
> > +
> > + ret = veml6046x00_regfield_init(data);
> > + if (ret)
> > + return dev_err_probe(dev, ret, "Failed to init regfield\n");
> > +
> > + ret = devm_regulator_get_enable(dev, "vdd");
> > + if (ret)
> > + return dev_err_probe(dev, ret, "Failed to enable regulator\n");
> > +
> > + ret = devm_add_action_or_reset(dev, veml6046x00_shutdown_action, data);
>
> Mostly we want a devm action to match against a specific setup operation. Here is
> it that the device comes up in non shut down state? Perhaps a comment to
> make it clear. Also, how do we know it's in a good state rather than part
> configured by someone else? I'm not seeing a reset sequence though perhaps
> that effectively happens in setup_device()
In veml6046x00_setup_device() all registers are set up to bring the device in a
known state. This function also switches the device on. I could move the call to
setup_device() up to here and add a comment to make it clear.
>
> > + if (ret < 0)
> > + return dev_err_probe(dev, ret, "Failed to add shut down action\n");
> > +
> > + ret = pm_runtime_set_active(dev);
> > + if (ret < 0)
> > + return dev_err_probe(dev, ret, "Failed to activate PM runtime\n");
> > +
> > + ret = devm_pm_runtime_enable(dev);
> > + if (ret)
> > + return dev_err_probe(dev, ret, "Failed to enable PM runtime\n");
> > +
> > + pm_runtime_get_noresume(dev);
> > + pm_runtime_set_autosuspend_delay(dev, VEML6046X00_AUTOSUSPEND_MS);
> > + pm_runtime_use_autosuspend(dev);
> > +
> > + ret = veml6046x00_validate_part_id(data);
> > + if (ret)
> > + return dev_err_probe(dev, ret, "Failed to validate device ID\n");
> > +
> > + iio->name = i2c->name;
>
> Prefer this hard coded. Depending on the firmware type, things like that have
> an annoying habbit of not remaining predictable or stable.
>
> > + iio->channels = veml6046x00_channels;
> > + iio->num_channels = ARRAY_SIZE(veml6046x00_channels);
> > + iio->modes = INDIO_DIRECT_MODE;
> > +
> > + iio->info = &veml6046x00_info_no_irq;
> > +
> > + ret = veml6046x00_setup_device(iio);
> > + if (ret < 0)
> > + return ret;
> > +
> > + ret = devm_iio_triggered_buffer_setup(dev, iio, NULL,
> > + veml6046x00_trig_handler,
> > + &veml6046x00_buffer_setup_ops);
> > + if (ret)
> > + return dev_err_probe(dev, ret,
> > + "Failed to register triggered buffer");
> > +
> > + pm_runtime_mark_last_busy(dev);
> > + pm_runtime_put_autosuspend(dev);
> > +
> > + ret = devm_iio_device_register(dev, iio);
> > + if (ret)
> > + return dev_err_probe(dev, ret, "Failed to register iio device");
> > +
> > + return 0;
> > +}
>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 659 bytes --]
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH 2/3] iio: light: add support for veml6046x00 RGBIR color sensor
2025-03-18 2:15 ` Andreas Klinger
@ 2025-03-23 11:51 ` Jonathan Cameron
0 siblings, 0 replies; 18+ messages in thread
From: Jonathan Cameron @ 2025-03-23 11:51 UTC (permalink / raw)
To: Andreas Klinger
Cc: robh, krzk+dt, conor+dt, lars, linux-iio, devicetree,
linux-kernel, javier.carrasco.cruz, mazziesaccount,
subhajit.ghosh, muditsharma.info, arthur.becker, ivan.orlov0322
> > > + data = iio_priv(iio);
> > > + i2c_set_clientdata(i2c, iio);
> > > + data->dev = dev;
> > > + data->regmap = regmap;
> > > +
> > > + ret = veml6046x00_regfield_init(data);
> > > + if (ret)
> > > + return dev_err_probe(dev, ret, "Failed to init regfield\n");
> > > +
> > > + ret = devm_regulator_get_enable(dev, "vdd");
> > > + if (ret)
> > > + return dev_err_probe(dev, ret, "Failed to enable regulator\n");
> > > +
> > > + ret = devm_add_action_or_reset(dev, veml6046x00_shutdown_action, data);
> >
> > Mostly we want a devm action to match against a specific setup operation. Here is
> > it that the device comes up in non shut down state? Perhaps a comment to
> > make it clear. Also, how do we know it's in a good state rather than part
> > configured by someone else? I'm not seeing a reset sequence though perhaps
> > that effectively happens in setup_device()
>
> In veml6046x00_setup_device() all registers are set up to bring the device in a
> known state. This function also switches the device on. I could move the call to
> setup_device() up to here and add a comment to make it clear.
Perfect.
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH 2/3] iio: light: add support for veml6046x00 RGBIR color sensor
2025-03-17 11:50 ` Jonathan Cameron
2025-03-18 2:15 ` Andreas Klinger
@ 2025-04-06 8:43 ` Andreas Klinger
2025-04-06 11:08 ` Jonathan Cameron
1 sibling, 1 reply; 18+ messages in thread
From: Andreas Klinger @ 2025-04-06 8:43 UTC (permalink / raw)
To: Jonathan Cameron
Cc: robh, krzk+dt, conor+dt, lars, linux-iio, devicetree,
linux-kernel, javier.carrasco.cruz, mazziesaccount,
subhajit.ghosh, muditsharma.info, arthur.becker, ivan.orlov0322
[-- Attachment #1: Type: text/plain, Size: 2184 bytes --]
Hi Jonathan,
I need to pick up the meaning of scale once again for clarification.
Jonathan Cameron <jic23@kernel.org> schrieb am Mo, 17. Mär 11:50:
> On Sun, 16 Mar 2025 12:31:30 +0100
> Andreas Klinger <ak@it-klinger.de> wrote:
>
> > +static int veml6046x00_get_scale(struct veml6046x00_data *data,
> > + int *val, int *val2)
>
> How is this related to integration time? I'd normally expect
> to see that read in here somewhere as well as doubling integration
> time tends to double scale.
In the documentation file "sysfs-bus-iio" it says:
"
What: /sys/.../iio:deviceX/in_illuminanceY_raw
[...]
Description:
Illuminance measurement, units after application of scale
and offset are lux.
"
This means that the scale should be the real factor and not the gain multiplied
by photodiode size (PDDIV) as i implemented it so far.
This means also that doubling integration time should halve the scale. The
higher raw value should lead to the same lux value.
The documentation of the sensor (veml6046x00.pdf) provides us the calculation
between raw green values and lux.
Wouldn't it be better to give the user the real factor to be able to get lux?
The fact that only the green channel can be used for calculation could be
documented in the driver.
Then i found the "in_illuminance_hardwaregain" property. It seems that this is
exactly what the combination of gain and PDDIV is used for.
So what is the scale at the moment could become the hardwaregain and the scale
the factor from raw value to lux.
To sum up the suggested interface under /sys/bus/iio/devices/iio\:deviceX would
be something like:
in_illuminance_hardwaregain --> set and get gain and PDDIV on the sensor
integration_time --> set and get integration time on the sensor
integration_time_available --> show available integration time values
scale --> (only) get real calculation value, taken from
sensor documenation, e.g. 1.3440
scale_available --> not existing anymore
What do you think?
Andreas
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 659 bytes --]
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH 2/3] iio: light: add support for veml6046x00 RGBIR color sensor
2025-04-06 8:43 ` Andreas Klinger
@ 2025-04-06 11:08 ` Jonathan Cameron
2025-04-07 5:52 ` Matti Vaittinen
0 siblings, 1 reply; 18+ messages in thread
From: Jonathan Cameron @ 2025-04-06 11:08 UTC (permalink / raw)
To: Andreas Klinger
Cc: robh, krzk+dt, conor+dt, lars, linux-iio, devicetree,
linux-kernel, javier.carrasco.cruz, mazziesaccount,
subhajit.ghosh, muditsharma.info, arthur.becker, ivan.orlov0322,
Matti Vaittinen
On Sun, 6 Apr 2025 10:43:23 +0200
Andreas Klinger <ak@it-klinger.de> wrote:
> Hi Jonathan,
>
> I need to pick up the meaning of scale once again for clarification.
>
> Jonathan Cameron <jic23@kernel.org> schrieb am Mo, 17. Mär 11:50:
> > On Sun, 16 Mar 2025 12:31:30 +0100
> > Andreas Klinger <ak@it-klinger.de> wrote:
> >
> > > +static int veml6046x00_get_scale(struct veml6046x00_data *data,
> > > + int *val, int *val2)
> >
> > How is this related to integration time? I'd normally expect
> > to see that read in here somewhere as well as doubling integration
> > time tends to double scale.
>
> In the documentation file "sysfs-bus-iio" it says:
> "
> What: /sys/.../iio:deviceX/in_illuminanceY_raw
> [...]
> Description:
> Illuminance measurement, units after application of scale
> and offset are lux.
> "
>
> This means that the scale should be the real factor and not the gain multiplied
> by photodiode size (PDDIV) as i implemented it so far.
>
> This means also that doubling integration time should halve the scale. The
> higher raw value should lead to the same lux value.
Sounds correct.
>
> The documentation of the sensor (veml6046x00.pdf) provides us the calculation
> between raw green values and lux.
> Wouldn't it be better to give the user the real factor to be able to get lux?
Absolutely. That's the expectation if we are providing illuminance_raw and
illuminance_scale.
>
> The fact that only the green channel can be used for calculation could be
> documented in the driver.
Ah. One of these devices. Hmm. Why do people pretend they can get from
Green to illuminance. That has to assume 'white light'.
I get grumpy about this, but if it is the best we can do I guess we have
to live with it (I might not be consistent on this).
>
> Then i found the "in_illuminance_hardwaregain" property. It seems that this is
> exactly what the combination of gain and PDDIV is used for.
>
> So what is the scale at the moment could become the hardwaregain and the scale
> the factor from raw value to lux.
If it is useful to export it separately that works, however it's not typically
the control attribute - those tend to be read only because, without access to
datasheets simple software has no idea how to control them.
The alternative is the GTS helpers that attempt to figure out the best
way to meet the user requirements in setting the integration time and amplifier
gain when a scale is requested.
+CC Matti who is the expert on those.
>
>
> To sum up the suggested interface under /sys/bus/iio/devices/iio\:deviceX would
> be something like:
>
> in_illuminance_hardwaregain --> set and get gain and PDDIV on the sensor
This is usually the read only one as it reflects things that aren't
easy for a userspace program / user to tune. They typically want to control
integration time because it reflects noise level and scale because they want
to avoid saturation etc and because we need it to get to the actual value
in lux.
>
> integration_time --> set and get integration time on the sensor
driving these directly is fine.
>
> integration_time_available --> show available integration time values
>
> scale --> (only) get real calculation value, taken from
> sensor documenation, e.g. 1.3440
This should remain a main control attribute.
>
> scale_available --> not existing anymore
This gets tricky but the GTS helpers will calculate it for you I think.
Jonathan
>
>
> What do you think?
>
> Andreas
>
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH 2/3] iio: light: add support for veml6046x00 RGBIR color sensor
2025-04-06 11:08 ` Jonathan Cameron
@ 2025-04-07 5:52 ` Matti Vaittinen
2025-05-05 19:10 ` Andreas Klinger
0 siblings, 1 reply; 18+ messages in thread
From: Matti Vaittinen @ 2025-04-07 5:52 UTC (permalink / raw)
To: Jonathan Cameron, Andreas Klinger
Cc: robh, krzk+dt, conor+dt, lars, linux-iio, devicetree,
linux-kernel, javier.carrasco.cruz, subhajit.ghosh,
muditsharma.info, arthur.becker, ivan.orlov0322
On 06/04/2025 14:08, Jonathan Cameron wrote:
> On Sun, 6 Apr 2025 10:43:23 +0200
> Andreas Klinger <ak@it-klinger.de> wrote:
>
>> Hi Jonathan,
>>
>> I need to pick up the meaning of scale once again for clarification.
>>
>> Jonathan Cameron <jic23@kernel.org> schrieb am Mo, 17. Mär 11:50:
>>> On Sun, 16 Mar 2025 12:31:30 +0100
>>> Andreas Klinger <ak@it-klinger.de> wrote:
>>>
>>>> +static int veml6046x00_get_scale(struct veml6046x00_data *data,
>>>> + int *val, int *val2)
>>>
>>> How is this related to integration time? I'd normally expect
>>> to see that read in here somewhere as well as doubling integration
>>> time tends to double scale.
>>
>> In the documentation file "sysfs-bus-iio" it says:
>> "
>> What: /sys/.../iio:deviceX/in_illuminanceY_raw
>> [...]
>> Description:
>> Illuminance measurement, units after application of scale
>> and offset are lux.
>> "
>>
>> This means that the scale should be the real factor and not the gain multiplied
>> by photodiode size (PDDIV) as i implemented it so far.
>>
>> This means also that doubling integration time should halve the scale. The
>> higher raw value should lead to the same lux value.
>
> Sounds correct.
I was CC'd due to the GTS (gain-time-scale)-helpers. The above is the
beef of those helpers - which, attempt to aid drivers to convert the
impact of the hardware gain + integration time into a single scale
value. This approach has some caveats, but the goal is to fulfill the
expectations of those user-space apps which expect only scale to change
the gain, while also need to have the integration time controllable (for
example to reduce the measurement time for one reason or another).
Problem is that, especially when there are multiple channels with
separate gain control but common integration time, there will be some
situations where the integration time change _will_ cause changes to
"total gain (E.g. scale)" too. There may also be cases where some scale
values can be met only with certain integration times, or where a scale
for a channel can't be met maintaining the scale for other channels etc.
All in all, I am not sure if the 'unchangeable hardware gain' approach
makes things as simple as possible - but as long as we want to have it,
the GTS helpers may be of use :) There are couple of drivers using them
- feel free to take a look. "git grep gts_ drivers/iio/light/" should
point you the current users.
>> The documentation of the sensor (veml6046x00.pdf) provides us the calculation
>> between raw green values and lux.
>> Wouldn't it be better to give the user the real factor to be able to get lux?
>
> Absolutely. That's the expectation if we are providing illuminance_raw and
> illuminance_scale.
>
>>
>> The fact that only the green channel can be used for calculation could be
>> documented in the driver.
>
> Ah. One of these devices. Hmm. Why do people pretend they can get from
> Green to illuminance. That has to assume 'white light'.
> I get grumpy about this, but if it is the best we can do I guess we have
> to live with it (I might not be consistent on this).
>
>>
>> Then i found the "in_illuminance_hardwaregain" property. It seems that this is
>> exactly what the combination of gain and PDDIV is used for.
>>
>> So what is the scale at the moment could become the hardwaregain and the scale
>> the factor from raw value to lux.
>
> If it is useful to export it separately that works, however it's not typically
> the control attribute - those tend to be read only because, without access to
> datasheets simple software has no idea how to control them.
>
> The alternative is the GTS helpers that attempt to figure out the best
> way to meet the user requirements in setting the integration time and amplifier
> gain when a scale is requested.
>
> +CC Matti who is the expert on those.
Seems it doesn't take much to be an expert XD
I have understood that the simplest way to go is to always use the
maximum integration time which provides the required scale. This should
probably result the most accurate readings. If users for some reason
want to explicitly set a shorter time, then the GTS helpers might be useful.
>> To sum up the suggested interface under /sys/bus/iio/devices/iio\:deviceX would
>> be something like:
>>
>> in_illuminance_hardwaregain --> set and get gain and PDDIV on the sensor
>
> This is usually the read only one as it reflects things that aren't
> easy for a userspace program / user to tune. They typically want to control
> integration time because it reflects noise level and scale because they want
> to avoid saturation etc and because we need it to get to the actual value
> in lux.
>
>>
>> integration_time --> set and get integration time on the sensor
> driving these directly is fine.
>>
>> integration_time_available --> show available integration time values
>>
>> scale --> (only) get real calculation value, taken from
>> sensor documenation, e.g. 1.3440
> This should remain a main control attribute.
>>
>> scale_available --> not existing anymore
> This gets tricky but the GTS helpers will calculate it for you I think.
The GTS helpers can (or, at least should be able to) calculate the
available_scales. AFAIR they offer two approaches for this. First one is
listing _all_ scales which can be achieved by changing both the
integration time and the hardware gain. The second one lists only the
scales which can be set with the currently selected integration time.
I think that all of the current GTS users use this first approach of
listing all the values, so the 'per time tables' - approach is not very
thoroughly tested. Please, let me know if you hit to any hiccups.
Yours,
-- Matti
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH 2/3] iio: light: add support for veml6046x00 RGBIR color sensor
2025-04-07 5:52 ` Matti Vaittinen
@ 2025-05-05 19:10 ` Andreas Klinger
2025-05-06 6:03 ` Matti Vaittinen
0 siblings, 1 reply; 18+ messages in thread
From: Andreas Klinger @ 2025-05-05 19:10 UTC (permalink / raw)
To: Jonathan Cameron, Matti Vaittinen
Cc: robh, krzk+dt, conor+dt, lars, linux-iio, devicetree,
linux-kernel, javier.carrasco.cruz, subhajit.ghosh,
muditsharma.info, arthur.becker, ivan.orlov0322
[-- Attachment #1: Type: text/plain, Size: 3419 bytes --]
Hi Jonathan,
hi Matti,
Matti Vaittinen <mazziesaccount@gmail.com> schrieb am Mo, 07. Apr 08:52:
> On 06/04/2025 14:08, Jonathan Cameron wrote:
> > On Sun, 6 Apr 2025 10:43:23 +0200
> > Andreas Klinger <ak@it-klinger.de> wrote:
> >
> > > Hi Jonathan,
> > >
> > > I need to pick up the meaning of scale once again for clarification.
> > >
> > > Jonathan Cameron <jic23@kernel.org> schrieb am Mo, 17. Mär 11:50:
> > > > On Sun, 16 Mar 2025 12:31:30 +0100
> > > > Andreas Klinger <ak@it-klinger.de> wrote:
> > > > > +static int veml6046x00_get_scale(struct veml6046x00_data *data,
> > > > > + int *val, int *val2)
> > > >
> > > > How is this related to integration time? I'd normally expect
> > > > to see that read in here somewhere as well as doubling integration
> > > > time tends to double scale.
> > >
> > > In the documentation file "sysfs-bus-iio" it says:
> > > "
> > > What: /sys/.../iio:deviceX/in_illuminanceY_raw
> > > [...]
> > > Description:
> > > Illuminance measurement, units after application of scale
> > > and offset are lux.
> > > "
> > >
> > > This means that the scale should be the real factor and not the gain multiplied
> > > by photodiode size (PDDIV) as i implemented it so far.
> > >
> > > This means also that doubling integration time should halve the scale. The
> > > higher raw value should lead to the same lux value.
> >
> > Sounds correct.
>
> I was CC'd due to the GTS (gain-time-scale)-helpers. The above is the beef
> of those helpers - which, attempt to aid drivers to convert the impact of
> the hardware gain + integration time into a single scale value. This
> approach has some caveats, but the goal is to fulfill the expectations of
> those user-space apps which expect only scale to change the gain, while also
> need to have the integration time controllable (for example to reduce the
> measurement time for one reason or another).
>
> Problem is that, especially when there are multiple channels with separate
> gain control but common integration time, there will be some situations
> where the integration time change _will_ cause changes to "total gain (E.g.
> scale)" too. There may also be cases where some scale values can be met only
> with certain integration times, or where a scale for a channel can't be met
> maintaining the scale for other channels etc.
>
> All in all, I am not sure if the 'unchangeable hardware gain' approach makes
> things as simple as possible - but as long as we want to have it, the GTS
> helpers may be of use :) There are couple of drivers using them - feel free
> to take a look. "git grep gts_ drivers/iio/light/" should point you the
> current users.
>
Thanks a lot for illustrating and explaining the GTS. I implemented the driver
with GTS and by this learned a lot about it. But at the end i found it in my
case to be simpler to implement it without GTS for some reasons:
- User wants to be able to set up the integration time as well as scale and the
driver should not optimize it somehow.
- There is not only a relation from the scale to the gain of the sensor but also
to the photodiode size. Because of this i need another helper table asize of
GTS for translating the scale into sensor gain and photodiode size.
I'll come up with a version 3 shortly.
Andreas
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 659 bytes --]
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH 2/3] iio: light: add support for veml6046x00 RGBIR color sensor
2025-05-05 19:10 ` Andreas Klinger
@ 2025-05-06 6:03 ` Matti Vaittinen
2025-05-06 8:28 ` Andreas Klinger
0 siblings, 1 reply; 18+ messages in thread
From: Matti Vaittinen @ 2025-05-06 6:03 UTC (permalink / raw)
To: Andreas Klinger, Jonathan Cameron
Cc: robh, krzk+dt, conor+dt, lars, linux-iio, devicetree,
linux-kernel, javier.carrasco.cruz, subhajit.ghosh,
muditsharma.info, arthur.becker, ivan.orlov0322
On 05/05/2025 22:10, Andreas Klinger wrote:
>>
>> I was CC'd due to the GTS (gain-time-scale)-helpers. The above is the beef
>> of those helpers - which, attempt to aid drivers to convert the impact of
>> the hardware gain + integration time into a single scale value. This
>> approach has some caveats, but the goal is to fulfill the expectations of
>> those user-space apps which expect only scale to change the gain, while also
>> need to have the integration time controllable (for example to reduce the
>> measurement time for one reason or another).
>>
>> Problem is that, especially when there are multiple channels with separate
>> gain control but common integration time, there will be some situations
>> where the integration time change _will_ cause changes to "total gain (E.g.
>> scale)" too. There may also be cases where some scale values can be met only
>> with certain integration times, or where a scale for a channel can't be met
>> maintaining the scale for other channels etc.
>>
>> All in all, I am not sure if the 'unchangeable hardware gain' approach makes
>> things as simple as possible - but as long as we want to have it, the GTS
>> helpers may be of use :) There are couple of drivers using them - feel free
>> to take a look. "git grep gts_ drivers/iio/light/" should point you the
>> current users.
>>
>
> Thanks a lot for illustrating and explaining the GTS. I implemented the driver
> with GTS and by this learned a lot about it. But at the end i found it in my
> case to be simpler to implement it without GTS for some reasons:
Firstly, it's perfectly Ok to choose to not to use the GTS helpers. Yet,
I would like to understand couple of things.
> - User wants to be able to set up the integration time as well as scale and the
> driver should not optimize it somehow.
How does using GTS to do the conversion from
HWGAIN + time => scale
cause optimization? Or, do you just mean that part of the functionality
provided by these helpers wouldn't be applicable, while the conversion
could still be done?
> - There is not only a relation from the scale to the gain of the sensor but also
> to the photodiode size. Because of this i need another helper table asize of
> GTS for translating the scale into sensor gain and photodiode size.
I suppose that using the GTS with gains and times without PD, and then
computing the impact of the PD size after the GTS conversions would have
caused the available scales to be wrong, right?
Couldn't you still have used two different set of gain tables (one for
each PD size) if you chose to use the GTS?
> I'll come up with a version 3 shortly.
I took a very quick look at the v3 - not one worth a reviewed-by :) And,
as I wrote, I have nothing against skipping the GTS. I'm not sure the
values in the multi-dimensional array were clear to me at a glance - but
I assume it's lean and efficient when one wraps his head around it. So,
I'm not trying to argue you should've used GTS - I just want to
understand what was the exact issue with it :)
Yours,
-- Matti
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH 2/3] iio: light: add support for veml6046x00 RGBIR color sensor
2025-05-06 6:03 ` Matti Vaittinen
@ 2025-05-06 8:28 ` Andreas Klinger
2025-05-06 9:17 ` Matti Vaittinen
0 siblings, 1 reply; 18+ messages in thread
From: Andreas Klinger @ 2025-05-06 8:28 UTC (permalink / raw)
To: Matti Vaittinen
Cc: Jonathan Cameron, robh, krzk+dt, conor+dt, lars, linux-iio,
devicetree, linux-kernel, javier.carrasco.cruz, subhajit.ghosh,
muditsharma.info, arthur.becker, ivan.orlov0322
[-- Attachment #1: Type: text/plain, Size: 4910 bytes --]
Hi Matti,
Matti Vaittinen <mazziesaccount@gmail.com> schrieb am Di, 06. Mai 09:03:
> On 05/05/2025 22:10, Andreas Klinger wrote:
> > >
> > > I was CC'd due to the GTS (gain-time-scale)-helpers. The above is the beef
> > > of those helpers - which, attempt to aid drivers to convert the impact of
> > > the hardware gain + integration time into a single scale value. This
> > > approach has some caveats, but the goal is to fulfill the expectations of
> > > those user-space apps which expect only scale to change the gain, while also
> > > need to have the integration time controllable (for example to reduce the
> > > measurement time for one reason or another).
> > >
> > > Problem is that, especially when there are multiple channels with separate
> > > gain control but common integration time, there will be some situations
> > > where the integration time change _will_ cause changes to "total gain (E.g.
> > > scale)" too. There may also be cases where some scale values can be met only
> > > with certain integration times, or where a scale for a channel can't be met
> > > maintaining the scale for other channels etc.
> > >
> > > All in all, I am not sure if the 'unchangeable hardware gain' approach makes
> > > things as simple as possible - but as long as we want to have it, the GTS
> > > helpers may be of use :) There are couple of drivers using them - feel free
> > > to take a look. "git grep gts_ drivers/iio/light/" should point you the
> > > current users.
> > >
> >
> > Thanks a lot for illustrating and explaining the GTS. I implemented the driver
> > with GTS and by this learned a lot about it. But at the end i found it in my
> > case to be simpler to implement it without GTS for some reasons:
>
> Firstly, it's perfectly Ok to choose to not to use the GTS helpers. Yet, I
> would like to understand couple of things.
>
> > - User wants to be able to set up the integration time as well as scale and the
> > driver should not optimize it somehow.
>
> How does using GTS to do the conversion from
> HWGAIN + time => scale
> cause optimization? Or, do you just mean that part of the functionality
> provided by these helpers wouldn't be applicable, while the conversion could
> still be done?
This functionality is not applicable. The user wants to set up the time and gain
manually. There are applications in which the driver should be fast with a lower
integration time and others in which the best accurate result is required. As
i'm developing it for the sensor vendor they want to be able to demonstrate
all the different settings to their customers.
> > - There is not only a relation from the scale to the gain of the sensor but also
> > to the photodiode size. Because of this i need another helper table asize of
> > GTS for translating the scale into sensor gain and photodiode size.
>
> I suppose that using the GTS with gains and times without PD, and then
> computing the impact of the PD size after the GTS conversions would have
> caused the available scales to be wrong, right?
Exactly this caused me most of the headache. With GTS i need to use a "virtual"
scale for the calculation. When user asks for available scales or set up the
desired scale i need to convert it to the real scale and then further translate
it to the register values of gain and PD.
> Couldn't you still have used two different set of gain tables (one for each
> PD size) if you chose to use the GTS?
Of course there are solutions for it and i was about to finish one with GTS as i
saw that the driver will get simpler without. As i cannot use the nice features
of GTS it turns out to use only the data structures and functions on it. But the
data structures don't fit exactly to this sensor as we have gain and PD in two
different regfields. So another table to translate the scale to the regfields is
needed anyway.
> > I'll come up with a version 3 shortly.
>
> I took a very quick look at the v3 - not one worth a reviewed-by :) And, as
> I wrote, I have nothing against skipping the GTS. I'm not sure the values in
> the multi-dimensional array were clear to me at a glance - but I assume it's
> lean and efficient when one wraps his head around it.
The large table contains exactly the values from the datasheet for the
conversion of raw measured counts to lux. I only combined the two tables for PD
1/2 and 2/2 as hwgain x0.5 and x1 are existing twice.
> So, I'm not trying to argue you should've used GTS - I just want to understand
> what was the exact issue with it :)
There is not an issue with GTS. In my opinion it's not the simplest solution for
this sensor in this case. I'm quite sure there are other cases in which it makes
a lot of sense to use it and as i'm now a little bit familiar with it i'll be
using it if appropriate.
Best regards,
Andreas
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 659 bytes --]
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH 2/3] iio: light: add support for veml6046x00 RGBIR color sensor
2025-05-06 8:28 ` Andreas Klinger
@ 2025-05-06 9:17 ` Matti Vaittinen
0 siblings, 0 replies; 18+ messages in thread
From: Matti Vaittinen @ 2025-05-06 9:17 UTC (permalink / raw)
To: Andreas Klinger
Cc: Jonathan Cameron, robh, krzk+dt, conor+dt, lars, linux-iio,
devicetree, linux-kernel, javier.carrasco.cruz, subhajit.ghosh,
muditsharma.info, arthur.becker, ivan.orlov0322
On 06/05/2025 11:28, Andreas Klinger wrote:
> Hi Matti,
>
> Matti Vaittinen <mazziesaccount@gmail.com> schrieb am Di, 06. Mai 09:03:
>> On 05/05/2025 22:10, Andreas Klinger wrote:
>> Couldn't you still have used two different set of gain tables (one for each
>> PD size) if you chose to use the GTS?
>
> Of course there are solutions for it and i was about to finish one with GTS as i
> saw that the driver will get simpler without. As i cannot use the nice features
> of GTS it turns out to use only the data structures and functions on it. But the
> data structures don't fit exactly to this sensor as we have gain and PD in two
> different regfields. So another table to translate the scale to the regfields is
> needed anyway.
Ah. Understood. Thanks for taking the time to explain :)
Yours,
-- Matti
^ permalink raw reply [flat|nested] 18+ messages in thread
end of thread, other threads:[~2025-05-06 9:17 UTC | newest]
Thread overview: 18+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-03-16 11:31 [PATCH 0/3] iio:light: add driver for veml6046x00 RGBIR color sensor Andreas Klinger
2025-03-16 11:31 ` [PATCH 1/3] dt-bindings: iio: light: veml6046x00: add " Andreas Klinger
2025-03-16 12:19 ` Rob Herring (Arm)
2025-03-17 11:00 ` Jonathan Cameron
2025-03-17 11:17 ` Andreas Klinger
2025-03-17 11:26 ` Krzysztof Kozlowski
2025-03-16 11:31 ` [PATCH 2/3] iio: light: add support for veml6046x00 RGBIR " Andreas Klinger
2025-03-17 11:50 ` Jonathan Cameron
2025-03-18 2:15 ` Andreas Klinger
2025-03-23 11:51 ` Jonathan Cameron
2025-04-06 8:43 ` Andreas Klinger
2025-04-06 11:08 ` Jonathan Cameron
2025-04-07 5:52 ` Matti Vaittinen
2025-05-05 19:10 ` Andreas Klinger
2025-05-06 6:03 ` Matti Vaittinen
2025-05-06 8:28 ` Andreas Klinger
2025-05-06 9:17 ` Matti Vaittinen
2025-03-16 11:31 ` [PATCH 3/3] MAINTAINER: add maintainer for veml6046x00 Andreas Klinger
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).