Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0+
  2/*
  3 * Vishay VEML6040 RGBW light sensor driver
  4 *
  5 * Copyright (C) 2024 Sentec AG
  6 * Author: Arthur Becker <arthur.becker@sentec.com>
  7 *
  8 */
  9
 10#include <linux/bitfield.h>
 11#include <linux/err.h>
 12#include <linux/i2c.h>
 13#include <linux/iio/iio.h>
 14#include <linux/iio/sysfs.h>
 15#include <linux/module.h>
 16#include <linux/regmap.h>
 17
 18/* VEML6040 Configuration Registers
 19 *
 20 * SD: Shutdown
 21 * AF: Auto / Force Mode (Auto Measurements On:0, Off:1)
 22 * TR: Trigger Measurement (when AF Bit is set)
 23 * IT: Integration Time
 24 */
 25#define VEML6040_CONF_REG 0x000
 26#define VEML6040_CONF_SD_MSK BIT(0)
 27#define VEML6040_CONF_AF_MSK BIT(1)
 28#define VEML6040_CONF_TR_MSK BIT(2)
 29#define VEML6040_CONF_IT_MSK GENMASK(6, 4)
 30#define VEML6040_CONF_IT_40_MS 0
 31#define VEML6040_CONF_IT_80_MS 1
 32#define VEML6040_CONF_IT_160_MS 2
 33#define VEML6040_CONF_IT_320_MS 3
 34#define VEML6040_CONF_IT_640_MS 4
 35#define VEML6040_CONF_IT_1280_MS 5
 36
 37/* VEML6040 Read Only Registers */
 38#define VEML6040_REG_R 0x08
 39#define VEML6040_REG_G 0x09
 40#define VEML6040_REG_B 0x0A
 41#define VEML6040_REG_W 0x0B
 42
 43static const int veml6040_it_ms[] = { 40, 80, 160, 320, 640, 1280 };
 44
 45enum veml6040_chan {
 46	CH_RED,
 47	CH_GREEN,
 48	CH_BLUE,
 49	CH_WHITE,
 50};
 51
 52struct veml6040_data {
 53	struct i2c_client *client;
 54	struct regmap *regmap;
 55};
 56
 57static const struct regmap_config veml6040_regmap_config = {
 58	.name = "veml6040_regmap",
 59	.reg_bits = 8,
 60	.val_bits = 16,
 61	.max_register = VEML6040_REG_W,
 62	.val_format_endian = REGMAP_ENDIAN_LITTLE,
 63};
 64
 65static int veml6040_read_raw(struct iio_dev *indio_dev,
 66			     struct iio_chan_spec const *chan, int *val,
 67			     int *val2, long mask)
 68{
 69	int ret, reg, it_index;
 70	struct veml6040_data *data = iio_priv(indio_dev);
 71	struct regmap *regmap = data->regmap;
 72	struct device *dev = &data->client->dev;
 73
 74	switch (mask) {
 75	case IIO_CHAN_INFO_RAW:
 76		ret = regmap_read(regmap, chan->address, &reg);
 77		if (ret) {
 78			dev_err(dev, "Data read failed: %d\n", ret);
 79			return ret;
 80		}
 81		*val = reg;
 82		return IIO_VAL_INT;
 83
 84	case IIO_CHAN_INFO_INT_TIME:
 85		ret = regmap_read(regmap, VEML6040_CONF_REG, &reg);
 86		if (ret) {
 87			dev_err(dev, "Data read failed: %d\n", ret);
 88			return ret;
 89		}
 90		it_index = FIELD_GET(VEML6040_CONF_IT_MSK, reg);
 91		if (it_index >= ARRAY_SIZE(veml6040_it_ms)) {
 92			dev_err(dev, "Invalid Integration Time Set");
 93			return -EINVAL;
 94		}
 95		*val = veml6040_it_ms[it_index];
 96		return IIO_VAL_INT;
 97
 98	default:
 99		return -EINVAL;
100	}
101}
102
103static int veml6040_write_raw(struct iio_dev *indio_dev,
104			      struct iio_chan_spec const *chan, int val,
105			      int val2, long mask)
106{
107	struct veml6040_data *data = iio_priv(indio_dev);
108
109	switch (mask) {
110	case IIO_CHAN_INFO_INT_TIME:
111		for (int i = 0; i < ARRAY_SIZE(veml6040_it_ms); i++) {
112			if (veml6040_it_ms[i] != val)
113				continue;
114
115			return regmap_update_bits(data->regmap,
116					VEML6040_CONF_REG,
117					VEML6040_CONF_IT_MSK,
118					FIELD_PREP(VEML6040_CONF_IT_MSK, i));
119		}
120		return -EINVAL;
121	default:
122		return -EINVAL;
123	}
124}
125
126static int veml6040_read_avail(struct iio_dev *indio_dev,
127			       struct iio_chan_spec const *chan,
128			       const int **vals, int *type, int *length,
129			       long mask)
130{
131	switch (mask) {
132	case IIO_CHAN_INFO_INT_TIME:
133		*length = ARRAY_SIZE(veml6040_it_ms);
134		*vals = veml6040_it_ms;
135		*type = IIO_VAL_INT;
136		return IIO_AVAIL_LIST;
137
138	default:
139		return -EINVAL;
140	}
141}
142
143static const struct iio_info veml6040_info = {
144	.read_raw = veml6040_read_raw,
145	.write_raw = veml6040_write_raw,
146	.read_avail = veml6040_read_avail,
147};
148
149static const struct iio_chan_spec veml6040_channels[] = {
150	{
151		.type = IIO_INTENSITY,
152		.address = VEML6040_REG_R,
153		.channel = CH_RED,
154		.channel2 = IIO_MOD_LIGHT_RED,
155		.modified = 1,
156		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
157		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_INT_TIME),
158		.info_mask_shared_by_type_available =
159			BIT(IIO_CHAN_INFO_INT_TIME),
160	},
161	{
162		.type = IIO_INTENSITY,
163		.address = VEML6040_REG_G,
164		.channel = CH_GREEN,
165		.channel2 = IIO_MOD_LIGHT_GREEN,
166		.modified = 1,
167		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
168		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_INT_TIME),
169		.info_mask_shared_by_type_available =
170			BIT(IIO_CHAN_INFO_INT_TIME),
171	},
172	{
173		.type = IIO_INTENSITY,
174		.address = VEML6040_REG_B,
175		.channel = CH_BLUE,
176		.channel2 = IIO_MOD_LIGHT_BLUE,
177		.modified = 1,
178		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
179		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_INT_TIME),
180		.info_mask_shared_by_type_available =
181			BIT(IIO_CHAN_INFO_INT_TIME),
182	},
183	{
184		.type = IIO_INTENSITY,
185		.address = VEML6040_REG_W,
186		.channel = CH_WHITE,
187		.channel2 = IIO_MOD_LIGHT_CLEAR,
188		.modified = 1,
189		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
190		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_INT_TIME),
191		.info_mask_shared_by_type_available =
192			BIT(IIO_CHAN_INFO_INT_TIME),
193	}
194};
195
196static void veml6040_shutdown_action(void *data)
197{
198	struct veml6040_data *veml6040_data = data;
199
200	regmap_update_bits(veml6040_data->regmap, VEML6040_CONF_REG,
201			   VEML6040_CONF_SD_MSK, VEML6040_CONF_SD_MSK);
202}
203
204static int veml6040_probe(struct i2c_client *client)
205{
206	struct device *dev = &client->dev;
207	struct veml6040_data *data;
208	struct iio_dev *indio_dev;
209	struct regmap *regmap;
210	const int init_config =
211		FIELD_PREP(VEML6040_CONF_IT_MSK, VEML6040_CONF_IT_40_MS) |
212		FIELD_PREP(VEML6040_CONF_AF_MSK, 0) |
213		FIELD_PREP(VEML6040_CONF_SD_MSK, 0);
214	int ret;
215
216	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
217		return dev_err_probe(dev, -EOPNOTSUPP,
218				     "I2C adapter doesn't support plain I2C\n");
219
220	indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
221	if (!indio_dev)
222		return dev_err_probe(dev, -ENOMEM,
223				     "IIO device allocation failed\n");
224
225	regmap = devm_regmap_init_i2c(client, &veml6040_regmap_config);
226	if (IS_ERR(regmap))
227		return dev_err_probe(dev, PTR_ERR(regmap),
228				     "Regmap setup failed\n");
229
230	data = iio_priv(indio_dev);
231	i2c_set_clientdata(client, indio_dev);
232	data->client = client;
233	data->regmap = regmap;
234
235	indio_dev->name = "veml6040";
236	indio_dev->info = &veml6040_info;
237	indio_dev->channels = veml6040_channels;
238	indio_dev->num_channels = ARRAY_SIZE(veml6040_channels);
239	indio_dev->modes = INDIO_DIRECT_MODE;
240
241	ret = devm_regulator_get_enable(dev, "vdd");
242	if (ret)
243		return ret;
244
245	ret = regmap_write(regmap, VEML6040_CONF_REG, init_config);
246	if (ret)
247		return dev_err_probe(dev, ret,
248				     "Could not set initial config\n");
249
250	ret = devm_add_action_or_reset(dev, veml6040_shutdown_action, data);
251	if (ret)
252		return ret;
253
254	return devm_iio_device_register(dev, indio_dev);
255}
256
257static const struct i2c_device_id veml6040_id_table[] = {
258	{"veml6040"},
259	{}
260};
261MODULE_DEVICE_TABLE(i2c, veml6040_id_table);
262
263static const struct of_device_id veml6040_of_match[] = {
264	{.compatible = "vishay,veml6040"},
265	{}
266};
267MODULE_DEVICE_TABLE(of, veml6040_of_match);
268
269static struct i2c_driver veml6040_driver = {
270	.probe = veml6040_probe,
271	.id_table = veml6040_id_table,
272	.driver = {
273		.name = "veml6040",
274		.of_match_table = veml6040_of_match,
275	},
276};
277module_i2c_driver(veml6040_driver);
278
279MODULE_DESCRIPTION("veml6040 RGBW light sensor driver");
280MODULE_AUTHOR("Arthur Becker <arthur.becker@sentec.com>");
281MODULE_LICENSE("GPL");