Skip to content

Commit 8b05442

Browse files
Daniel Balutajic23
authored andcommitted
iio: light: Add support for Capella CM3323 color sensor
Minimal implementation providing raw light intensity and integration time attribute. Userspace applications can use GREEN channel for raw illuminance readings following this table: Integration Time | G Sensitivity ================================ 40 ms | 0.18 80 ms | 0.09 160 ms | 0.045 320 ms | 0.0225 640 ms | 0.01125 1280 ms | 0.005625 Signed-off-by: Daniel Baluta <[email protected]> Signed-off-by: Jonathan Cameron <[email protected]>
1 parent 40dbbfb commit 8b05442

File tree

3 files changed

+297
-0
lines changed

3 files changed

+297
-0
lines changed

drivers/iio/light/Kconfig

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,16 @@ config CM3232
5959
To compile this driver as a module, choose M here:
6060
the module will be called cm3232.
6161

62+
config CM3323
63+
depends on I2C
64+
tristate "Capella CM3323 color light sensor"
65+
help
66+
Say Y here if you want to build a driver for Capela CM3323
67+
color sensor.
68+
69+
To compile this driver as a module, choose M here: the module will
70+
be called cm3323.
71+
6272
config CM36651
6373
depends on I2C
6474
tristate "CM36651 driver"

drivers/iio/light/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ obj-$(CONFIG_AL3320A) += al3320a.o
88
obj-$(CONFIG_APDS9300) += apds9300.o
99
obj-$(CONFIG_CM32181) += cm32181.o
1010
obj-$(CONFIG_CM3232) += cm3232.o
11+
obj-$(CONFIG_CM3323) += cm3323.o
1112
obj-$(CONFIG_CM36651) += cm36651.o
1213
obj-$(CONFIG_GP2AP020A00F) += gp2ap020a00f.o
1314
obj-$(CONFIG_HID_SENSOR_ALS) += hid-sensor-als.o

drivers/iio/light/cm3323.c

Lines changed: 286 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,286 @@
1+
/*
2+
* CM3323 - Capella Color Light Sensor
3+
*
4+
* Copyright (c) 2015, Intel Corporation.
5+
*
6+
* This file is subject to the terms and conditions of version 2 of
7+
* the GNU General Public License. See the file COPYING in the main
8+
* directory of this archive for more details.
9+
*
10+
* IIO driver for CM3323 (7-bit I2C slave address 0x10)
11+
*
12+
* TODO: calibscale to correct the lens factor
13+
*/
14+
#include <linux/module.h>
15+
#include <linux/init.h>
16+
#include <linux/i2c.h>
17+
#include <linux/mutex.h>
18+
19+
#include <linux/iio/iio.h>
20+
#include <linux/iio/sysfs.h>
21+
22+
#define CM3323_DRV_NAME "cm3323"
23+
24+
#define CM3323_CMD_CONF 0x00
25+
#define CM3323_CMD_RED_DATA 0x08
26+
#define CM3323_CMD_GREEN_DATA 0x09
27+
#define CM3323_CMD_BLUE_DATA 0x0A
28+
#define CM3323_CMD_CLEAR_DATA 0x0B
29+
30+
#define CM3323_CONF_SD_BIT BIT(0) /* sensor disable */
31+
#define CM3323_CONF_AF_BIT BIT(1) /* auto/manual force mode */
32+
#define CM3323_CONF_IT_MASK (BIT(4) | BIT(5) | BIT(6))
33+
#define CM3323_CONF_IT_SHIFT 4
34+
35+
#define CM3323_INT_TIME_AVAILABLE "0.04 0.08 0.16 0.32 0.64 1.28"
36+
37+
static const struct {
38+
int val;
39+
int val2;
40+
} cm3323_int_time[] = {
41+
{0, 40000}, /* 40 ms */
42+
{0, 80000}, /* 80 ms */
43+
{0, 160000}, /* 160 ms */
44+
{0, 320000}, /* 320 ms */
45+
{0, 640000}, /* 640 ms */
46+
{1, 280000}, /* 1280 ms */
47+
};
48+
49+
struct cm3323_data {
50+
struct i2c_client *client;
51+
u16 reg_conf;
52+
struct mutex mutex;
53+
};
54+
55+
#define CM3323_COLOR_CHANNEL(_color, _addr) { \
56+
.type = IIO_INTENSITY, \
57+
.modified = 1, \
58+
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
59+
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME), \
60+
.channel2 = IIO_MOD_LIGHT_##_color, \
61+
.address = _addr, \
62+
}
63+
64+
static const struct iio_chan_spec cm3323_channels[] = {
65+
CM3323_COLOR_CHANNEL(RED, CM3323_CMD_RED_DATA),
66+
CM3323_COLOR_CHANNEL(GREEN, CM3323_CMD_GREEN_DATA),
67+
CM3323_COLOR_CHANNEL(BLUE, CM3323_CMD_BLUE_DATA),
68+
CM3323_COLOR_CHANNEL(CLEAR, CM3323_CMD_CLEAR_DATA),
69+
};
70+
71+
static IIO_CONST_ATTR_INT_TIME_AVAIL(CM3323_INT_TIME_AVAILABLE);
72+
73+
static struct attribute *cm3323_attributes[] = {
74+
&iio_const_attr_integration_time_available.dev_attr.attr,
75+
NULL
76+
};
77+
78+
static const struct attribute_group cm3323_attribute_group = {
79+
.attrs = cm3323_attributes,
80+
};
81+
82+
static int cm3323_init(struct iio_dev *indio_dev)
83+
{
84+
int ret;
85+
struct cm3323_data *data = iio_priv(indio_dev);
86+
87+
ret = i2c_smbus_read_word_data(data->client, CM3323_CMD_CONF);
88+
if (ret < 0) {
89+
dev_err(&data->client->dev, "Error reading reg_conf\n");
90+
return ret;
91+
}
92+
93+
/* enable sensor and set auto force mode */
94+
ret &= ~(CM3323_CONF_SD_BIT | CM3323_CONF_AF_BIT);
95+
96+
ret = i2c_smbus_write_word_data(data->client, CM3323_CMD_CONF, ret);
97+
if (ret < 0) {
98+
dev_err(&data->client->dev, "Error writing reg_conf\n");
99+
return ret;
100+
}
101+
102+
data->reg_conf = ret;
103+
104+
return 0;
105+
}
106+
107+
static void cm3323_disable(struct iio_dev *indio_dev)
108+
{
109+
int ret;
110+
struct cm3323_data *data = iio_priv(indio_dev);
111+
112+
ret = i2c_smbus_write_word_data(data->client, CM3323_CMD_CONF,
113+
CM3323_CONF_SD_BIT);
114+
if (ret < 0)
115+
dev_err(&data->client->dev, "Error writing reg_conf\n");
116+
}
117+
118+
static int cm3323_set_it_bits(struct cm3323_data *data, int val, int val2)
119+
{
120+
int i, ret;
121+
u16 reg_conf;
122+
123+
for (i = 0; i < ARRAY_SIZE(cm3323_int_time); i++) {
124+
if (val == cm3323_int_time[i].val &&
125+
val2 == cm3323_int_time[i].val2) {
126+
reg_conf = data->reg_conf;
127+
reg_conf |= i << CM3323_CONF_IT_SHIFT;
128+
129+
ret = i2c_smbus_write_word_data(data->client,
130+
CM3323_CMD_CONF,
131+
reg_conf);
132+
if (ret < 0)
133+
return ret;
134+
135+
data->reg_conf = reg_conf;
136+
return 0;
137+
}
138+
}
139+
return -EINVAL;
140+
}
141+
142+
static int cm3323_get_it_bits(struct cm3323_data *data)
143+
{
144+
int bits;
145+
146+
bits = (data->reg_conf & CM3323_CONF_IT_MASK) >>
147+
CM3323_CONF_IT_SHIFT;
148+
149+
if (bits >= ARRAY_SIZE(cm3323_int_time))
150+
return -EINVAL;
151+
return bits;
152+
}
153+
154+
static int cm3323_read_raw(struct iio_dev *indio_dev,
155+
struct iio_chan_spec const *chan, int *val,
156+
int *val2, long mask)
157+
{
158+
int i, ret;
159+
struct cm3323_data *data = iio_priv(indio_dev);
160+
161+
switch (mask) {
162+
case IIO_CHAN_INFO_RAW:
163+
mutex_lock(&data->mutex);
164+
ret = i2c_smbus_read_word_data(data->client, chan->address);
165+
if (ret < 0) {
166+
mutex_unlock(&data->mutex);
167+
return ret;
168+
}
169+
*val = ret;
170+
mutex_unlock(&data->mutex);
171+
172+
return IIO_VAL_INT;
173+
case IIO_CHAN_INFO_INT_TIME:
174+
mutex_lock(&data->mutex);
175+
i = cm3323_get_it_bits(data);
176+
if (i < 0) {
177+
mutex_unlock(&data->mutex);
178+
return -EINVAL;
179+
}
180+
181+
*val = cm3323_int_time[i].val;
182+
*val2 = cm3323_int_time[i].val2;
183+
mutex_unlock(&data->mutex);
184+
185+
return IIO_VAL_INT_PLUS_MICRO;
186+
default:
187+
return -EINVAL;
188+
}
189+
}
190+
191+
static int cm3323_write_raw(struct iio_dev *indio_dev,
192+
struct iio_chan_spec const *chan, int val,
193+
int val2, long mask)
194+
{
195+
struct cm3323_data *data = iio_priv(indio_dev);
196+
int ret;
197+
198+
switch (mask) {
199+
case IIO_CHAN_INFO_INT_TIME:
200+
mutex_lock(&data->mutex);
201+
ret = cm3323_set_it_bits(data, val, val2);
202+
mutex_unlock(&data->mutex);
203+
204+
return ret;
205+
default:
206+
return -EINVAL;
207+
}
208+
}
209+
210+
static const struct iio_info cm3323_info = {
211+
.driver_module = THIS_MODULE,
212+
.read_raw = cm3323_read_raw,
213+
.write_raw = cm3323_write_raw,
214+
.attrs = &cm3323_attribute_group,
215+
};
216+
217+
static int cm3323_probe(struct i2c_client *client,
218+
const struct i2c_device_id *id)
219+
{
220+
struct cm3323_data *data;
221+
struct iio_dev *indio_dev;
222+
int ret;
223+
224+
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
225+
if (!indio_dev)
226+
return -ENOMEM;
227+
228+
data = iio_priv(indio_dev);
229+
i2c_set_clientdata(client, indio_dev);
230+
data->client = client;
231+
232+
mutex_init(&data->mutex);
233+
234+
indio_dev->dev.parent = &client->dev;
235+
indio_dev->info = &cm3323_info;
236+
indio_dev->name = CM3323_DRV_NAME;
237+
indio_dev->channels = cm3323_channels;
238+
indio_dev->num_channels = ARRAY_SIZE(cm3323_channels);
239+
indio_dev->modes = INDIO_DIRECT_MODE;
240+
241+
ret = cm3323_init(indio_dev);
242+
if (ret < 0) {
243+
dev_err(&client->dev, "cm3323 chip init failed\n");
244+
return ret;
245+
}
246+
ret = iio_device_register(indio_dev);
247+
if (ret < 0) {
248+
dev_err(&client->dev, "failed to register iio dev\n");
249+
goto err_init;
250+
}
251+
return 0;
252+
err_init:
253+
cm3323_disable(indio_dev);
254+
return ret;
255+
}
256+
257+
static int cm3323_remove(struct i2c_client *client)
258+
{
259+
struct iio_dev *indio_dev = i2c_get_clientdata(client);
260+
261+
iio_device_unregister(indio_dev);
262+
cm3323_disable(indio_dev);
263+
264+
return 0;
265+
}
266+
267+
static const struct i2c_device_id cm3323_id[] = {
268+
{"cm3323", 0},
269+
{}
270+
};
271+
MODULE_DEVICE_TABLE(i2c, cm3323_id);
272+
273+
static struct i2c_driver cm3323_driver = {
274+
.driver = {
275+
.name = CM3323_DRV_NAME,
276+
},
277+
.probe = cm3323_probe,
278+
.remove = cm3323_remove,
279+
.id_table = cm3323_id,
280+
};
281+
282+
module_i2c_driver(cm3323_driver);
283+
284+
MODULE_AUTHOR("Daniel Baluta <[email protected]>");
285+
MODULE_DESCRIPTION("Capella CM3323 Color Light Sensor driver");
286+
MODULE_LICENSE("GPL v2");

0 commit comments

Comments
 (0)