The Sensortek STK36C61 is a 3-in-1 ambient light / proximity / RGB colour sensor (chip ID 0x95) found in the Fairphone 6. Its register interface is compatible with the feature set this driver uses: the STATE/FLAG bit layout, the data and threshold registers and the gain and integration-time fields, verified on that device (the ALS and proximity readings scale with their gain and integration-time fields, thresholds written through the event interface read back from the chip, and the FLAG near/far bit crosses with them). Add its chip ID to the known-ID list and the device table entries.
Whenever the ALS engine runs, the chip also measures four colour channels, laid out directly after the ALS data as 16-bit big-endian values in R (0x15), G (0x17), B (0x19), C (0x1B) order; the R, G and B assignments were each confirmed by the matching channel dominating under red, green and blue illumination, and clear by its broadband response. The ALS data register tracks the green channel exactly. There is no public datasheet and therefore no counts-to-physical mapping for the colour channels, so they are exposed as raw-only intensity channels; the gain field the scale attribute controls does not govern clear either. Assisted-by: Claude:claude-fable-5 Signed-off-by: Jorijn van der Graaf <[email protected]> --- drivers/iio/light/stk3310.c | 44 ++++++++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/drivers/iio/light/stk3310.c b/drivers/iio/light/stk3310.c index 249396b9bf80..288b544a8abb 100644 --- a/drivers/iio/light/stk3310.c +++ b/drivers/iio/light/stk3310.c @@ -40,6 +40,11 @@ #define STK3310_REG_PS_DATA_LSB 0x12 #define STK3310_REG_ALS_DATA_MSB 0x13 #define STK3310_REG_ALS_DATA_LSB 0x14 +#define STK36C61_REG_RED_DATA_MSB 0x15 +#define STK36C61_REG_GREEN_DATA_MSB 0x17 +#define STK36C61_REG_BLUE_DATA_MSB 0x19 +#define STK36C61_REG_CLEAR_DATA_MSB 0x1B +#define STK36C61_REG_CLEAR_DATA_LSB 0x1C #define STK3310_REG_ID 0x3E #define STK3310_MAX_REG 0x80 @@ -54,6 +59,7 @@ #define STK3311S34_CHIP_ID_VAL 0x1E #define STK3311X_CHIP_ID_VAL 0x12 #define STK3335_CHIP_ID_VAL 0x51 +#define STK36C61_CHIP_ID_VAL 0x95 #define STK3310_PSINT_EN 0x01 #define STK3310_PS_MAX_VAL 0xFFFF @@ -102,6 +108,7 @@ static const u8 stk3310_chip_ids[] = { STK3311X_CHIP_ID_VAL, STK3311_CHIP_ID_VAL, STK3335_CHIP_ID_VAL, + STK36C61_CHIP_ID_VAL, }; /* Estimate maximum proximity values with regard to measurement scale. */ @@ -202,11 +209,28 @@ static const struct iio_chan_spec_ext_info stk3310_ext_info[] = { .ext_info = stk3310_ext_info, \ } +#define STK36C61_INTENSITY_CHANNEL(_mod, _reg) { \ + .type = IIO_INTENSITY, \ + .modified = 1, \ + .channel2 = IIO_MOD_LIGHT_##_mod, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .address = _reg, \ +} + static const struct iio_chan_spec stk3310_channels[] = { STK3310_LIGHT_CHANNEL, STK3310_PROXIMITY_CHANNEL, }; +static const struct iio_chan_spec stk36c61_channels[] = { + STK3310_LIGHT_CHANNEL, + STK3310_PROXIMITY_CHANNEL, + STK36C61_INTENSITY_CHANNEL(RED, STK36C61_REG_RED_DATA_MSB), + STK36C61_INTENSITY_CHANNEL(GREEN, STK36C61_REG_GREEN_DATA_MSB), + STK36C61_INTENSITY_CHANNEL(BLUE, STK36C61_REG_BLUE_DATA_MSB), + STK36C61_INTENSITY_CHANNEL(CLEAR, STK36C61_REG_CLEAR_DATA_MSB), +}; + /** * struct stk3310_chip_info - chip-specific data * @name: device name reported to the IIO core @@ -225,6 +249,12 @@ static const struct stk3310_chip_info stk3310_chip_info = { .num_channels = ARRAY_SIZE(stk3310_channels), }; +static const struct stk3310_chip_info stk36c61_chip_info = { + .name = "stk36c61", + .channels = stk36c61_channels, + .num_channels = ARRAY_SIZE(stk36c61_channels), +}; + static IIO_CONST_ATTR(in_illuminance_scale_available, STK3310_SCALE_AVAILABLE); static IIO_CONST_ATTR(in_proximity_scale_available, STK3310_SCALE_AVAILABLE); @@ -400,7 +430,8 @@ static int stk3310_read_raw(struct iio_dev *indio_dev, struct stk3310_data *data = iio_priv(indio_dev); struct i2c_client *client = data->client; - if (chan->type != IIO_LIGHT && chan->type != IIO_PROXIMITY) + if (chan->type != IIO_LIGHT && chan->type != IIO_PROXIMITY && + chan->type != IIO_INTENSITY) return -EINVAL; switch (mask) { @@ -419,8 +450,10 @@ static int stk3310_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_INT_TIME: if (chan->type == IIO_LIGHT) ret = regmap_field_read(data->reg_als_it, &index); - else + else if (chan->type == IIO_PROXIMITY) ret = regmap_field_read(data->reg_ps_it, &index); + else + return -EINVAL; if (ret < 0) return ret; @@ -430,8 +463,10 @@ static int stk3310_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_SCALE: if (chan->type == IIO_LIGHT) ret = regmap_field_read(data->reg_als_gain, &index); - else + else if (chan->type == IIO_PROXIMITY) ret = regmap_field_read(data->reg_ps_gain, &index); + else + return -EINVAL; if (ret < 0) return ret; @@ -567,6 +602,7 @@ static bool stk3310_is_volatile_reg(struct device *dev, unsigned int reg) switch (reg) { case STK3310_REG_ALS_DATA_MSB: case STK3310_REG_ALS_DATA_LSB: + case STK36C61_REG_RED_DATA_MSB ... STK36C61_REG_CLEAR_DATA_LSB: case STK3310_REG_PS_DATA_LSB: case STK3310_REG_PS_DATA_MSB: case STK3310_REG_FLAG: @@ -801,6 +837,7 @@ static const struct i2c_device_id stk3310_i2c_id[] = { { .name = "STK3310", .driver_data = (kernel_ulong_t)&stk3310_chip_info }, { .name = "STK3311", .driver_data = (kernel_ulong_t)&stk3310_chip_info }, { .name = "STK3335", .driver_data = (kernel_ulong_t)&stk3310_chip_info }, + { .name = "STK36C61", .driver_data = (kernel_ulong_t)&stk36c61_chip_info }, { } }; MODULE_DEVICE_TABLE(i2c, stk3310_i2c_id); @@ -819,6 +856,7 @@ static const struct of_device_id stk3310_of_match[] = { { .compatible = "sensortek,stk3310", .data = &stk3310_chip_info }, { .compatible = "sensortek,stk3311", .data = &stk3310_chip_info }, { .compatible = "sensortek,stk3335", .data = &stk3310_chip_info }, + { .compatible = "sensortek,stk36c61", .data = &stk36c61_chip_info }, { } }; MODULE_DEVICE_TABLE(of, stk3310_of_match); -- 2.55.0

