diff options
author | Matt Ranostay <mranostay@gmail.com> | 2015-08-17 13:30:16 -0400 |
---|---|---|
committer | Jonathan Cameron <jic23@kernel.org> | 2015-08-31 11:23:09 -0400 |
commit | aff268cd532e99ced3c8f48d01118912eb002bbf (patch) | |
tree | dcba00b981b6f7bf7ee875f486620a5b1ffc2c82 | |
parent | 078d02cfdafdd06562286b2f222e6532f369eb27 (diff) |
iio: light: add APDS9960 ALS + promixity driver
APDS9960 is a combination of ALS, proximity, and gesture sensors.
This patch adds support for these functions along with gain control,
integration time, and event thresholds.
Signed-off-by: Matt Ranostay <mranostay@gmail.com>
Signed-off-by: Jonathan Cameron <jic23@kernel.org>
-rw-r--r-- | drivers/iio/light/Kconfig | 13 | ||||
-rw-r--r-- | drivers/iio/light/Makefile | 1 | ||||
-rw-r--r-- | drivers/iio/light/apds9960.c | 1135 |
3 files changed, 1149 insertions, 0 deletions
diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index 7ed859a700c4..19b9a173fe61 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig | |||
@@ -50,6 +50,19 @@ config APDS9300 | |||
50 | To compile this driver as a module, choose M here: the | 50 | To compile this driver as a module, choose M here: the |
51 | module will be called apds9300. | 51 | module will be called apds9300. |
52 | 52 | ||
53 | config APDS9960 | ||
54 | tristate "Avago APDS9960 gesture/RGB/ALS/proximity sensor" | ||
55 | select REGMAP_I2C | ||
56 | select IIO_BUFFER | ||
57 | select IIO_KFIFO_BUF | ||
58 | depends on I2C | ||
59 | help | ||
60 | Say Y here to build I2C interface support for the Avago | ||
61 | APDS9960 gesture/RGB/ALS/proximity sensor. | ||
62 | |||
63 | To compile this driver as a module, choose M here: the | ||
64 | module will be called apds9960 | ||
65 | |||
53 | config BH1750 | 66 | config BH1750 |
54 | tristate "ROHM BH1750 ambient light sensor" | 67 | tristate "ROHM BH1750 ambient light sensor" |
55 | depends on I2C | 68 | depends on I2C |
diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile index 91c74c014b6f..7b2244550747 100644 --- a/drivers/iio/light/Makefile +++ b/drivers/iio/light/Makefile | |||
@@ -7,6 +7,7 @@ obj-$(CONFIG_ACPI_ALS) += acpi-als.o | |||
7 | obj-$(CONFIG_ADJD_S311) += adjd_s311.o | 7 | obj-$(CONFIG_ADJD_S311) += adjd_s311.o |
8 | obj-$(CONFIG_AL3320A) += al3320a.o | 8 | obj-$(CONFIG_AL3320A) += al3320a.o |
9 | obj-$(CONFIG_APDS9300) += apds9300.o | 9 | obj-$(CONFIG_APDS9300) += apds9300.o |
10 | obj-$(CONFIG_APDS9960) += apds9960.o | ||
10 | obj-$(CONFIG_BH1750) += bh1750.o | 11 | obj-$(CONFIG_BH1750) += bh1750.o |
11 | obj-$(CONFIG_CM32181) += cm32181.o | 12 | obj-$(CONFIG_CM32181) += cm32181.o |
12 | obj-$(CONFIG_CM3232) += cm3232.o | 13 | obj-$(CONFIG_CM3232) += cm3232.o |
diff --git a/drivers/iio/light/apds9960.c b/drivers/iio/light/apds9960.c new file mode 100644 index 000000000000..27f415743733 --- /dev/null +++ b/drivers/iio/light/apds9960.c | |||
@@ -0,0 +1,1135 @@ | |||
1 | /* | ||
2 | * apds9960.c - Support for Avago APDS9960 gesture/RGB/ALS/proximity sensor | ||
3 | * | ||
4 | * Copyright (C) 2015 Matt Ranostay <mranostay@gmail.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * TODO: gesture + proximity calib offsets | ||
17 | */ | ||
18 | |||
19 | #include <linux/module.h> | ||
20 | #include <linux/init.h> | ||
21 | #include <linux/interrupt.h> | ||
22 | #include <linux/delay.h> | ||
23 | #include <linux/mutex.h> | ||
24 | #include <linux/err.h> | ||
25 | #include <linux/irq.h> | ||
26 | #include <linux/gpio.h> | ||
27 | #include <linux/i2c.h> | ||
28 | #include <linux/pm_runtime.h> | ||
29 | #include <linux/regmap.h> | ||
30 | #include <linux/iio/iio.h> | ||
31 | #include <linux/iio/buffer.h> | ||
32 | #include <linux/iio/events.h> | ||
33 | #include <linux/iio/kfifo_buf.h> | ||
34 | #include <linux/iio/sysfs.h> | ||
35 | #include <linux/of_gpio.h> | ||
36 | |||
37 | #define APDS9960_REGMAP_NAME "apds9960_regmap" | ||
38 | #define APDS9960_DRV_NAME "apds9960" | ||
39 | |||
40 | #define APDS9960_REG_RAM_START 0x00 | ||
41 | #define APDS9960_REG_RAM_END 0x7f | ||
42 | |||
43 | #define APDS9960_REG_ENABLE 0x80 | ||
44 | #define APDS9960_REG_ATIME 0x81 | ||
45 | #define APDS9960_REG_WTIME 0x83 | ||
46 | |||
47 | #define APDS9960_REG_AILTL 0x84 | ||
48 | #define APDS9960_REG_AILTH 0x85 | ||
49 | #define APDS9960_REG_AIHTL 0x86 | ||
50 | #define APDS9960_REG_AIHTH 0x87 | ||
51 | |||
52 | #define APDS9960_REG_PILT 0x89 | ||
53 | #define APDS9960_REG_PIHT 0x8b | ||
54 | #define APDS9960_REG_PERS 0x8c | ||
55 | |||
56 | #define APDS9960_REG_CONFIG_1 0x8d | ||
57 | #define APDS9960_REG_PPULSE 0x8e | ||
58 | |||
59 | #define APDS9960_REG_CONTROL 0x8f | ||
60 | #define APDS9960_REG_CONTROL_AGAIN_MASK 0x03 | ||
61 | #define APDS9960_REG_CONTROL_PGAIN_MASK 0x0c | ||
62 | #define APDS9960_REG_CONTROL_AGAIN_MASK_SHIFT 0 | ||
63 | #define APDS9960_REG_CONTROL_PGAIN_MASK_SHIFT 2 | ||
64 | |||
65 | #define APDS9960_REG_CONFIG_2 0x90 | ||
66 | #define APDS9960_REG_CONFIG_2_GGAIN_MASK 0x60 | ||
67 | #define APDS9960_REG_CONFIG_2_GGAIN_MASK_SHIFT 5 | ||
68 | |||
69 | #define APDS9960_REG_ID 0x92 | ||
70 | |||
71 | #define APDS9960_REG_STATUS 0x93 | ||
72 | #define APDS9960_REG_STATUS_PS_INT BIT(5) | ||
73 | #define APDS9960_REG_STATUS_ALS_INT BIT(4) | ||
74 | #define APDS9960_REG_STATUS_GINT BIT(2) | ||
75 | |||
76 | #define APDS9960_REG_PDATA 0x9c | ||
77 | #define APDS9960_REG_POFFSET_UR 0x9d | ||
78 | #define APDS9960_REG_POFFSET_DL 0x9e | ||
79 | #define APDS9960_REG_CONFIG_3 0x9f | ||
80 | |||
81 | #define APDS9960_REG_GPENTH 0xa0 | ||
82 | #define APDS9960_REG_GEXTH 0xa1 | ||
83 | |||
84 | #define APDS9960_REG_GCONF_1 0xa2 | ||
85 | #define APDS9960_REG_GCONF_1_GFIFO_THRES_MASK 0xc0 | ||
86 | #define APDS9960_REG_GCONF_1_GFIFO_THRES_MASK_SHIFT 6 | ||
87 | |||
88 | #define APDS9960_REG_GCONF_2 0xa3 | ||
89 | #define APDS9960_REG_GOFFSET_U 0xa4 | ||
90 | #define APDS9960_REG_GOFFSET_D 0xa5 | ||
91 | #define APDS9960_REG_GPULSE 0xa6 | ||
92 | #define APDS9960_REG_GOFFSET_L 0xa7 | ||
93 | #define APDS9960_REG_GOFFSET_R 0xa9 | ||
94 | #define APDS9960_REG_GCONF_3 0xaa | ||
95 | |||
96 | #define APDS9960_REG_GCONF_4 0xab | ||
97 | #define APDS9960_REG_GFLVL 0xae | ||
98 | #define APDS9960_REG_GSTATUS 0xaf | ||
99 | |||
100 | #define APDS9960_REG_IFORCE 0xe4 | ||
101 | #define APDS9960_REG_PICLEAR 0xe5 | ||
102 | #define APDS9960_REG_CICLEAR 0xe6 | ||
103 | #define APDS9960_REG_AICLEAR 0xe7 | ||
104 | |||
105 | #define APDS9960_DEFAULT_PERS 0x33 | ||
106 | #define APDS9960_DEFAULT_GPENTH 0x50 | ||
107 | #define APDS9960_DEFAULT_GEXTH 0x40 | ||
108 | |||
109 | #define APDS9960_MAX_PXS_THRES_VAL 255 | ||
110 | #define APDS9960_MAX_ALS_THRES_VAL 0xffff | ||
111 | #define APDS9960_MAX_INT_TIME_IN_US 1000000 | ||
112 | |||
113 | enum apds9960_als_channel_idx { | ||
114 | IDX_ALS_CLEAR, IDX_ALS_RED, IDX_ALS_GREEN, IDX_ALS_BLUE, | ||
115 | }; | ||
116 | |||
117 | #define APDS9960_REG_ALS_BASE 0x94 | ||
118 | #define APDS9960_REG_ALS_CHANNEL(_colour) \ | ||
119 | (APDS9960_REG_ALS_BASE + (IDX_ALS_##_colour * 2)) | ||
120 | |||
121 | enum apds9960_gesture_channel_idx { | ||
122 | IDX_DIR_UP, IDX_DIR_DOWN, IDX_DIR_LEFT, IDX_DIR_RIGHT, | ||
123 | }; | ||
124 | |||
125 | #define APDS9960_REG_GFIFO_BASE 0xfc | ||
126 | #define APDS9960_REG_GFIFO_DIR(_dir) \ | ||
127 | (APDS9960_REG_GFIFO_BASE + IDX_DIR_##_dir) | ||
128 | |||
129 | struct apds9960_data { | ||
130 | struct i2c_client *client; | ||
131 | struct iio_dev *indio_dev; | ||
132 | struct mutex lock; | ||
133 | |||
134 | /* regmap fields */ | ||
135 | struct regmap *regmap; | ||
136 | struct regmap_field *reg_int_als; | ||
137 | struct regmap_field *reg_int_ges; | ||
138 | struct regmap_field *reg_int_pxs; | ||
139 | |||
140 | struct regmap_field *reg_enable_als; | ||
141 | struct regmap_field *reg_enable_ges; | ||
142 | struct regmap_field *reg_enable_pxs; | ||
143 | |||
144 | /* state */ | ||
145 | int als_int; | ||
146 | int pxs_int; | ||
147 | int gesture_mode_running; | ||
148 | |||
149 | /* gain values */ | ||
150 | int als_gain; | ||
151 | int pxs_gain; | ||
152 | |||
153 | /* integration time value in us */ | ||
154 | int als_adc_int_us; | ||
155 | |||
156 | /* gesture buffer */ | ||
157 | u8 buffer[4]; /* 4 8-bit channels */ | ||
158 | }; | ||
159 | |||
160 | static const struct reg_default apds9960_reg_defaults[] = { | ||
161 | /* Default ALS integration time = 2.48ms */ | ||
162 | { APDS9960_REG_ATIME, 0xff }, | ||
163 | }; | ||
164 | |||
165 | static const struct regmap_range apds9960_volatile_ranges[] = { | ||
166 | regmap_reg_range(APDS9960_REG_STATUS, | ||
167 | APDS9960_REG_PDATA), | ||
168 | regmap_reg_range(APDS9960_REG_GFLVL, | ||
169 | APDS9960_REG_GSTATUS), | ||
170 | regmap_reg_range(APDS9960_REG_GFIFO_DIR(UP), | ||
171 | APDS9960_REG_GFIFO_DIR(RIGHT)), | ||
172 | regmap_reg_range(APDS9960_REG_IFORCE, | ||
173 | APDS9960_REG_AICLEAR), | ||
174 | }; | ||
175 | |||
176 | static const struct regmap_access_table apds9960_volatile_table = { | ||
177 | .yes_ranges = apds9960_volatile_ranges, | ||
178 | .n_yes_ranges = ARRAY_SIZE(apds9960_volatile_ranges), | ||
179 | }; | ||
180 | |||
181 | static const struct regmap_range apds9960_precious_ranges[] = { | ||
182 | regmap_reg_range(APDS9960_REG_RAM_START, APDS9960_REG_RAM_END), | ||
183 | }; | ||
184 | |||
185 | static const struct regmap_access_table apds9960_precious_table = { | ||
186 | .yes_ranges = apds9960_precious_ranges, | ||
187 | .n_yes_ranges = ARRAY_SIZE(apds9960_precious_ranges), | ||
188 | }; | ||
189 | |||
190 | static const struct regmap_range apds9960_readable_ranges[] = { | ||
191 | regmap_reg_range(APDS9960_REG_ENABLE, | ||
192 | APDS9960_REG_GSTATUS), | ||
193 | regmap_reg_range(APDS9960_REG_GFIFO_DIR(UP), | ||
194 | APDS9960_REG_GFIFO_DIR(RIGHT)), | ||
195 | }; | ||
196 | |||
197 | static const struct regmap_access_table apds9960_readable_table = { | ||
198 | .yes_ranges = apds9960_readable_ranges, | ||
199 | .n_yes_ranges = ARRAY_SIZE(apds9960_readable_ranges), | ||
200 | }; | ||
201 | |||
202 | static const struct regmap_range apds9960_writeable_ranges[] = { | ||
203 | regmap_reg_range(APDS9960_REG_ENABLE, APDS9960_REG_CONFIG_2), | ||
204 | regmap_reg_range(APDS9960_REG_POFFSET_UR, APDS9960_REG_GCONF_4), | ||
205 | regmap_reg_range(APDS9960_REG_IFORCE, APDS9960_REG_AICLEAR), | ||
206 | }; | ||
207 | |||
208 | static const struct regmap_access_table apds9960_writeable_table = { | ||
209 | .yes_ranges = apds9960_writeable_ranges, | ||
210 | .n_yes_ranges = ARRAY_SIZE(apds9960_writeable_ranges), | ||
211 | }; | ||
212 | |||
213 | static const struct regmap_config apds9960_regmap_config = { | ||
214 | .name = APDS9960_REGMAP_NAME, | ||
215 | .reg_bits = 8, | ||
216 | .val_bits = 8, | ||
217 | .use_single_rw = 1, | ||
218 | |||
219 | .volatile_table = &apds9960_volatile_table, | ||
220 | .precious_table = &apds9960_precious_table, | ||
221 | .rd_table = &apds9960_readable_table, | ||
222 | .wr_table = &apds9960_writeable_table, | ||
223 | |||
224 | .reg_defaults = apds9960_reg_defaults, | ||
225 | .num_reg_defaults = ARRAY_SIZE(apds9960_reg_defaults), | ||
226 | .max_register = APDS9960_REG_GFIFO_DIR(RIGHT), | ||
227 | .cache_type = REGCACHE_RBTREE, | ||
228 | }; | ||
229 | |||
230 | static const struct iio_event_spec apds9960_pxs_event_spec[] = { | ||
231 | { | ||
232 | .type = IIO_EV_TYPE_THRESH, | ||
233 | .dir = IIO_EV_DIR_RISING, | ||
234 | .mask_separate = BIT(IIO_EV_INFO_VALUE) | | ||
235 | BIT(IIO_EV_INFO_ENABLE), | ||
236 | }, | ||
237 | { | ||
238 | .type = IIO_EV_TYPE_THRESH, | ||
239 | .dir = IIO_EV_DIR_FALLING, | ||
240 | .mask_separate = BIT(IIO_EV_INFO_VALUE) | | ||
241 | BIT(IIO_EV_INFO_ENABLE), | ||
242 | }, | ||
243 | }; | ||
244 | |||
245 | static const struct iio_event_spec apds9960_als_event_spec[] = { | ||
246 | { | ||
247 | .type = IIO_EV_TYPE_THRESH, | ||
248 | .dir = IIO_EV_DIR_RISING, | ||
249 | .mask_separate = BIT(IIO_EV_INFO_VALUE) | | ||
250 | BIT(IIO_EV_INFO_ENABLE), | ||
251 | }, | ||
252 | { | ||
253 | .type = IIO_EV_TYPE_THRESH, | ||
254 | .dir = IIO_EV_DIR_FALLING, | ||
255 | .mask_separate = BIT(IIO_EV_INFO_VALUE) | | ||
256 | BIT(IIO_EV_INFO_ENABLE), | ||
257 | }, | ||
258 | }; | ||
259 | |||
260 | #define APDS9960_GESTURE_CHANNEL(_dir, _si) { \ | ||
261 | .type = IIO_PROXIMITY, \ | ||
262 | .channel = _si + 1, \ | ||
263 | .scan_index = _si, \ | ||
264 | .indexed = 1, \ | ||
265 | .scan_type = { \ | ||
266 | .sign = 'u', \ | ||
267 | .realbits = 8, \ | ||
268 | .storagebits = 8, \ | ||
269 | }, \ | ||
270 | } | ||
271 | |||
272 | #define APDS9960_INTENSITY_CHANNEL(_colour) { \ | ||
273 | .type = IIO_INTENSITY, \ | ||
274 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ | ||
275 | .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ | ||
276 | BIT(IIO_CHAN_INFO_INT_TIME), \ | ||
277 | .channel2 = IIO_MOD_LIGHT_##_colour, \ | ||
278 | .address = APDS9960_REG_ALS_CHANNEL(_colour), \ | ||
279 | .modified = 1, \ | ||
280 | .scan_index = -1, \ | ||
281 | } | ||
282 | |||
283 | static const unsigned long apds9960_scan_masks[] = {0xf, 0}; | ||
284 | |||
285 | static const struct iio_chan_spec apds9960_channels[] = { | ||
286 | { | ||
287 | .type = IIO_PROXIMITY, | ||
288 | .address = APDS9960_REG_PDATA, | ||
289 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), | ||
290 | .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), | ||
291 | .channel = 0, | ||
292 | .indexed = 0, | ||
293 | .scan_index = -1, | ||
294 | |||
295 | .event_spec = apds9960_pxs_event_spec, | ||
296 | .num_event_specs = ARRAY_SIZE(apds9960_pxs_event_spec), | ||
297 | }, | ||
298 | /* Gesture Sensor */ | ||
299 | APDS9960_GESTURE_CHANNEL(UP, 0), | ||
300 | APDS9960_GESTURE_CHANNEL(DOWN, 1), | ||
301 | APDS9960_GESTURE_CHANNEL(LEFT, 2), | ||
302 | APDS9960_GESTURE_CHANNEL(RIGHT, 3), | ||
303 | /* ALS */ | ||
304 | { | ||
305 | .type = IIO_INTENSITY, | ||
306 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), | ||
307 | .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | | ||
308 | BIT(IIO_CHAN_INFO_INT_TIME), | ||
309 | .channel2 = IIO_MOD_LIGHT_CLEAR, | ||
310 | .address = APDS9960_REG_ALS_CHANNEL(CLEAR), | ||
311 | .modified = 1, | ||
312 | .scan_index = -1, | ||
313 | |||
314 | .event_spec = apds9960_als_event_spec, | ||
315 | .num_event_specs = ARRAY_SIZE(apds9960_als_event_spec), | ||
316 | }, | ||
317 | /* RGB Sensor */ | ||
318 | APDS9960_INTENSITY_CHANNEL(RED), | ||
319 | APDS9960_INTENSITY_CHANNEL(GREEN), | ||
320 | APDS9960_INTENSITY_CHANNEL(BLUE), | ||
321 | }; | ||
322 | |||
323 | /* integration time in us */ | ||
324 | static const int apds9960_int_time[][2] = | ||
325 | { {28000, 246}, {100000, 219}, {200000, 182}, {700000, 0} }; | ||
326 | |||
327 | /* gain mapping */ | ||
328 | static const int apds9960_pxs_gain_map[] = {1, 2, 4, 8}; | ||
329 | static const int apds9960_als_gain_map[] = {1, 4, 16, 64}; | ||
330 | |||
331 | static IIO_CONST_ATTR(proximity_scale_available, "1 2 4 8"); | ||
332 | static IIO_CONST_ATTR(intensity_scale_available, "1 4 16 64"); | ||
333 | static IIO_CONST_ATTR_INT_TIME_AVAIL("0.028 0.1 0.2 0.7"); | ||
334 | |||
335 | static struct attribute *apds9960_attributes[] = { | ||
336 | &iio_const_attr_proximity_scale_available.dev_attr.attr, | ||
337 | &iio_const_attr_intensity_scale_available.dev_attr.attr, | ||
338 | &iio_const_attr_integration_time_available.dev_attr.attr, | ||
339 | NULL, | ||
340 | }; | ||
341 | |||
342 | static struct attribute_group apds9960_attribute_group = { | ||
343 | .attrs = apds9960_attributes, | ||
344 | }; | ||
345 | |||
346 | static const struct reg_field apds9960_reg_field_int_als = | ||
347 | REG_FIELD(APDS9960_REG_ENABLE, 4, 4); | ||
348 | |||
349 | static const struct reg_field apds9960_reg_field_int_ges = | ||
350 | REG_FIELD(APDS9960_REG_GCONF_4, 1, 1); | ||
351 | |||
352 | static const struct reg_field apds9960_reg_field_int_pxs = | ||
353 | REG_FIELD(APDS9960_REG_ENABLE, 5, 5); | ||
354 | |||
355 | static const struct reg_field apds9960_reg_field_enable_als = | ||
356 | REG_FIELD(APDS9960_REG_ENABLE, 1, 1); | ||
357 | |||
358 | static const struct reg_field apds9960_reg_field_enable_ges = | ||
359 | REG_FIELD(APDS9960_REG_ENABLE, 6, 6); | ||
360 | |||
361 | static const struct reg_field apds9960_reg_field_enable_pxs = | ||
362 | REG_FIELD(APDS9960_REG_ENABLE, 2, 2); | ||
363 | |||
364 | static int apds9960_set_it_time(struct apds9960_data *data, int val2) | ||
365 | { | ||
366 | int ret = -EINVAL; | ||
367 | int idx; | ||
368 | |||
369 | for (idx = 0; idx < ARRAY_SIZE(apds9960_int_time); idx++) { | ||
370 | if (apds9960_int_time[idx][0] == val2) { | ||
371 | mutex_lock(&data->lock); | ||
372 | ret = regmap_write(data->regmap, APDS9960_REG_ATIME, | ||
373 | apds9960_int_time[idx][1]); | ||
374 | if (!ret) | ||
375 | data->als_adc_int_us = val2; | ||
376 | mutex_unlock(&data->lock); | ||
377 | break; | ||
378 | } | ||
379 | } | ||
380 | |||
381 | return ret; | ||
382 | } | ||
383 | |||
384 | static int apds9960_set_pxs_gain(struct apds9960_data *data, int val) | ||
385 | { | ||
386 | int ret = -EINVAL; | ||
387 | int idx; | ||
388 | |||
389 | for (idx = 0; idx < ARRAY_SIZE(apds9960_pxs_gain_map); idx++) { | ||
390 | if (apds9960_pxs_gain_map[idx] == val) { | ||
391 | /* pxs + gesture gains are mirrored */ | ||
392 | mutex_lock(&data->lock); | ||
393 | ret = regmap_update_bits(data->regmap, | ||
394 | APDS9960_REG_CONTROL, | ||
395 | APDS9960_REG_CONTROL_PGAIN_MASK, | ||
396 | idx << APDS9960_REG_CONTROL_PGAIN_MASK_SHIFT); | ||
397 | if (ret) { | ||
398 | mutex_unlock(&data->lock); | ||
399 | break; | ||
400 | } | ||
401 | |||
402 | ret = regmap_update_bits(data->regmap, | ||
403 | APDS9960_REG_CONFIG_2, | ||
404 | APDS9960_REG_CONFIG_2_GGAIN_MASK, | ||
405 | idx << APDS9960_REG_CONFIG_2_GGAIN_MASK_SHIFT); | ||
406 | if (!ret) | ||
407 | data->pxs_gain = idx; | ||
408 | mutex_unlock(&data->lock); | ||
409 | break; | ||
410 | } | ||
411 | } | ||
412 | |||
413 | return ret; | ||
414 | } | ||
415 | |||
416 | static int apds9960_set_als_gain(struct apds9960_data *data, int val) | ||
417 | { | ||
418 | int ret = -EINVAL; | ||
419 | int idx; | ||
420 | |||
421 | for (idx = 0; idx < ARRAY_SIZE(apds9960_als_gain_map); idx++) { | ||
422 | if (apds9960_als_gain_map[idx] == val) { | ||
423 | mutex_lock(&data->lock); | ||
424 | ret = regmap_update_bits(data->regmap, | ||
425 | APDS9960_REG_CONTROL, | ||
426 | APDS9960_REG_CONTROL_AGAIN_MASK, idx); | ||
427 | if (!ret) | ||
428 | data->als_gain = idx; | ||
429 | mutex_unlock(&data->lock); | ||
430 | break; | ||
431 | } | ||
432 | } | ||
433 | |||
434 | return ret; | ||
435 | } | ||
436 | |||
437 | #ifdef CONFIG_PM | ||
438 | static int apds9960_set_power_state(struct apds9960_data *data, bool on) | ||
439 | { | ||
440 | struct device *dev = &data->client->dev; | ||
441 | int ret = 0; | ||
442 | |||
443 | mutex_lock(&data->lock); | ||
444 | |||
445 | if (on) { | ||
446 | int suspended; | ||
447 | |||
448 | suspended = pm_runtime_suspended(dev); | ||
449 | ret = pm_runtime_get_sync(dev); | ||
450 | |||
451 | /* Allow one integration cycle before allowing a reading */ | ||
452 | if (suspended) | ||
453 | usleep_range(data->als_adc_int_us, | ||
454 | APDS9960_MAX_INT_TIME_IN_US); | ||
455 | } else { | ||
456 | ret = pm_runtime_put_autosuspend(dev); | ||
457 | } | ||
458 | |||
459 | mutex_unlock(&data->lock); | ||
460 | |||
461 | return ret; | ||
462 | } | ||
463 | #else | ||
464 | static int apds9960_set_power_state(struct apds9960_data *data, bool on) | ||
465 | { | ||
466 | return 0; | ||
467 | } | ||
468 | #endif | ||
469 | |||
470 | static int apds9960_read_raw(struct iio_dev *indio_dev, | ||
471 | struct iio_chan_spec const *chan, | ||
472 | int *val, int *val2, long mask) | ||
473 | { | ||
474 | struct apds9960_data *data = iio_priv(indio_dev); | ||
475 | u16 buf; | ||
476 | int ret = -EINVAL; | ||
477 | |||
478 | if (data->gesture_mode_running) | ||
479 | return -EBUSY; | ||
480 | |||
481 | switch (mask) { | ||
482 | case IIO_CHAN_INFO_RAW: | ||
483 | apds9960_set_power_state(data, true); | ||
484 | switch (chan->type) { | ||
485 | case IIO_PROXIMITY: | ||
486 | ret = regmap_read(data->regmap, chan->address, val); | ||
487 | if (!ret) | ||
488 | ret = IIO_VAL_INT; | ||
489 | break; | ||
490 | case IIO_INTENSITY: | ||
491 | ret = regmap_bulk_read(data->regmap, chan->address, | ||
492 | &buf, 2); | ||
493 | if (!ret) | ||
494 | ret = IIO_VAL_INT; | ||
495 | *val = le16_to_cpu(buf); | ||
496 | break; | ||
497 | default: | ||
498 | ret = -EINVAL; | ||
499 | } | ||
500 | apds9960_set_power_state(data, false); | ||
501 | break; | ||
502 | case IIO_CHAN_INFO_INT_TIME: | ||
503 | /* RGB + ALS sensors only have integration time */ | ||
504 | mutex_lock(&data->lock); | ||
505 | switch (chan->type) { | ||
506 | case IIO_INTENSITY: | ||
507 | *val = 0; | ||
508 | *val2 = data->als_adc_int_us; | ||
509 | ret = IIO_VAL_INT_PLUS_MICRO; | ||
510 | break; | ||
511 | default: | ||
512 | ret = -EINVAL; | ||
513 | } | ||
514 | mutex_unlock(&data->lock); | ||
515 | break; | ||
516 | case IIO_CHAN_INFO_SCALE: | ||
517 | mutex_lock(&data->lock); | ||
518 | switch (chan->type) { | ||
519 | case IIO_PROXIMITY: | ||
520 | *val = apds9960_pxs_gain_map[data->pxs_gain]; | ||
521 | ret = IIO_VAL_INT; | ||
522 | break; | ||
523 | case IIO_INTENSITY: | ||
524 | *val = apds9960_als_gain_map[data->als_gain]; | ||
525 | ret = IIO_VAL_INT; | ||
526 | break; | ||
527 | default: | ||
528 | ret = -EINVAL; | ||
529 | } | ||
530 | mutex_unlock(&data->lock); | ||
531 | break; | ||
532 | } | ||
533 | |||
534 | return ret; | ||
535 | }; | ||
536 | |||
537 | static int apds9960_write_raw(struct iio_dev *indio_dev, | ||
538 | struct iio_chan_spec const *chan, | ||
539 | int val, int val2, long mask) | ||
540 | { | ||
541 | struct apds9960_data *data = iio_priv(indio_dev); | ||
542 | |||
543 | switch (mask) { | ||
544 | case IIO_CHAN_INFO_INT_TIME: | ||
545 | /* RGB + ALS sensors only have int time */ | ||
546 | switch (chan->type) { | ||
547 | case IIO_INTENSITY: | ||
548 | if (val != 0) | ||
549 | return -EINVAL; | ||
550 | return apds9960_set_it_time(data, val2); | ||
551 | default: | ||
552 | return -EINVAL; | ||
553 | } | ||
554 | case IIO_CHAN_INFO_SCALE: | ||
555 | if (val2 != 0) | ||
556 | return -EINVAL; | ||
557 | switch (chan->type) { | ||
558 | case IIO_PROXIMITY: | ||
559 | return apds9960_set_pxs_gain(data, val); | ||
560 | case IIO_INTENSITY: | ||
561 | return apds9960_set_als_gain(data, val); | ||
562 | default: | ||
563 | return -EINVAL; | ||
564 | } | ||
565 | default: | ||
566 | return -EINVAL; | ||
567 | }; | ||
568 | |||
569 | return 0; | ||
570 | } | ||
571 | |||
572 | static inline int apds9960_get_thres_reg(const struct iio_chan_spec *chan, | ||
573 | enum iio_event_direction dir, | ||
574 | u8 *reg) | ||
575 | { | ||
576 | switch (dir) { | ||
577 | case IIO_EV_DIR_RISING: | ||
578 | switch (chan->type) { | ||
579 | case IIO_PROXIMITY: | ||
580 | *reg = APDS9960_REG_PIHT; | ||
581 | break; | ||
582 | case IIO_INTENSITY: | ||
583 | *reg = APDS9960_REG_AIHTL; | ||
584 | break; | ||
585 | default: | ||
586 | return -EINVAL; | ||
587 | } | ||
588 | break; | ||
589 | case IIO_EV_DIR_FALLING: | ||
590 | switch (chan->type) { | ||
591 | case IIO_PROXIMITY: | ||
592 | *reg = APDS9960_REG_PILT; | ||
593 | break; | ||
594 | case IIO_INTENSITY: | ||
595 | *reg = APDS9960_REG_AILTL; | ||
596 | break; | ||
597 | default: | ||
598 | return -EINVAL; | ||
599 | } | ||
600 | break; | ||
601 | default: | ||
602 | return -EINVAL; | ||
603 | } | ||
604 | |||
605 | return 0; | ||
606 | } | ||
607 | |||
608 | static int apds9960_read_event(struct iio_dev *indio_dev, | ||
609 | const struct iio_chan_spec *chan, | ||
610 | enum iio_event_type type, | ||
611 | enum iio_event_direction dir, | ||
612 | enum iio_event_info info, | ||
613 | int *val, int *val2) | ||
614 | { | ||
615 | u8 reg; | ||
616 | u16 buf; | ||
617 | int ret = 0; | ||
618 | struct apds9960_data *data = iio_priv(indio_dev); | ||
619 | |||
620 | if (info != IIO_EV_INFO_VALUE) | ||
621 | return -EINVAL; | ||
622 | |||
623 | ret = apds9960_get_thres_reg(chan, dir, ®); | ||
624 | if (ret < 0) | ||
625 | return ret; | ||
626 | |||
627 | if (chan->type == IIO_PROXIMITY) { | ||
628 | ret = regmap_read(data->regmap, reg, val); | ||
629 | if (ret < 0) | ||
630 | return ret; | ||
631 | } else if (chan->type == IIO_INTENSITY) { | ||
632 | ret = regmap_bulk_read(data->regmap, reg, &buf, 2); | ||
633 | if (ret < 0) | ||
634 | return ret; | ||
635 | *val = le16_to_cpu(buf); | ||
636 | } else | ||
637 | return -EINVAL; | ||
638 | |||
639 | *val2 = 0; | ||
640 | |||
641 | return IIO_VAL_INT; | ||
642 | } | ||
643 | |||
644 | static int apds9960_write_event(struct iio_dev *indio_dev, | ||
645 | const struct iio_chan_spec *chan, | ||
646 | enum iio_event_type type, | ||
647 | enum iio_event_direction dir, | ||
648 | enum iio_event_info info, | ||
649 | int val, int val2) | ||
650 | { | ||
651 | u8 reg; | ||
652 | u16 buf; | ||
653 | int ret = 0; | ||
654 | struct apds9960_data *data = iio_priv(indio_dev); | ||
655 | |||
656 | if (info != IIO_EV_INFO_VALUE) | ||
657 | return -EINVAL; | ||
658 | |||
659 | ret = apds9960_get_thres_reg(chan, dir, ®); | ||
660 | if (ret < 0) | ||
661 | return ret; | ||
662 | |||
663 | if (chan->type == IIO_PROXIMITY) { | ||
664 | if (val < 0 || val > APDS9960_MAX_PXS_THRES_VAL) | ||
665 | return -EINVAL; | ||
666 | ret = regmap_write(data->regmap, reg, val); | ||
667 | if (ret < 0) | ||
668 | return ret; | ||
669 | } else if (chan->type == IIO_INTENSITY) { | ||
670 | if (val < 0 || val > APDS9960_MAX_ALS_THRES_VAL) | ||
671 | return -EINVAL; | ||
672 | buf = cpu_to_le16(val); | ||
673 | ret = regmap_bulk_write(data->regmap, reg, &buf, 2); | ||
674 | if (ret < 0) | ||
675 | return ret; | ||
676 | } else | ||
677 | return -EINVAL; | ||
678 | |||
679 | return 0; | ||
680 | } | ||
681 | |||
682 | static int apds9960_read_event_config(struct iio_dev *indio_dev, | ||
683 | const struct iio_chan_spec *chan, | ||
684 | enum iio_event_type type, | ||
685 | enum iio_event_direction dir) | ||
686 | { | ||
687 | struct apds9960_data *data = iio_priv(indio_dev); | ||
688 | |||
689 | switch (chan->type) { | ||
690 | case IIO_PROXIMITY: | ||
691 | return data->pxs_int; | ||
692 | case IIO_INTENSITY: | ||
693 | return data->als_int; | ||
694 | default: | ||
695 | return -EINVAL; | ||
696 | } | ||
697 | |||
698 | return 0; | ||
699 | } | ||
700 | |||
701 | static int apds9960_write_event_config(struct iio_dev *indio_dev, | ||
702 | const struct iio_chan_spec *chan, | ||
703 | enum iio_event_type type, | ||
704 | enum iio_event_direction dir, | ||
705 | int state) | ||
706 | { | ||
707 | struct apds9960_data *data = iio_priv(indio_dev); | ||
708 | int ret; | ||
709 | |||
710 | state = !!state; | ||
711 | |||
712 | switch (chan->type) { | ||
713 | case IIO_PROXIMITY: | ||
714 | if (data->pxs_int == state) | ||
715 | return -EINVAL; | ||
716 | |||
717 | ret = regmap_field_write(data->reg_int_pxs, state); | ||
718 | if (ret) | ||
719 | return ret; | ||
720 | data->pxs_int = state; | ||
721 | apds9960_set_power_state(data, state); | ||
722 | break; | ||
723 | case IIO_INTENSITY: | ||
724 | if (data->als_int == state) | ||
725 | return -EINVAL; | ||
726 | |||
727 | ret = regmap_field_write(data->reg_int_als, state); | ||
728 | if (ret) | ||
729 | return ret; | ||
730 | data->als_int = state; | ||
731 | apds9960_set_power_state(data, state); | ||
732 | break; | ||
733 | default: | ||
734 | return -EINVAL; | ||
735 | } | ||
736 | |||
737 | return 0; | ||
738 | } | ||
739 | |||
740 | static const struct iio_info apds9960_info = { | ||
741 | .driver_module = THIS_MODULE, | ||
742 | .attrs = &apds9960_attribute_group, | ||
743 | .read_raw = apds9960_read_raw, | ||
744 | .write_raw = apds9960_write_raw, | ||
745 | .read_event_value = apds9960_read_event, | ||
746 | .write_event_value = apds9960_write_event, | ||
747 | .read_event_config = apds9960_read_event_config, | ||
748 | .write_event_config = apds9960_write_event_config, | ||
749 | |||
750 | }; | ||
751 | |||
752 | static inline int apds9660_fifo_is_empty(struct apds9960_data *data) | ||
753 | { | ||
754 | int cnt; | ||
755 | int ret; | ||
756 | |||
757 | ret = regmap_read(data->regmap, APDS9960_REG_GFLVL, &cnt); | ||
758 | if (ret) | ||
759 | return ret; | ||
760 | |||
761 | return cnt; | ||
762 | } | ||
763 | |||
764 | static void apds9960_read_gesture_fifo(struct apds9960_data *data) | ||
765 | { | ||
766 | int ret, cnt = 0; | ||
767 | |||
768 | mutex_lock(&data->lock); | ||
769 | data->gesture_mode_running = 1; | ||
770 | |||
771 | while (cnt-- || (cnt = apds9660_fifo_is_empty(data) > 0)) { | ||
772 | ret = regmap_bulk_read(data->regmap, APDS9960_REG_GFIFO_BASE, | ||
773 | &data->buffer, 4); | ||
774 | |||
775 | if (ret) | ||
776 | goto err_read; | ||
777 | |||
778 | iio_push_to_buffers(data->indio_dev, data->buffer); | ||
779 | } | ||
780 | |||
781 | err_read: | ||
782 | data->gesture_mode_running = 0; | ||
783 | mutex_unlock(&data->lock); | ||
784 | } | ||
785 | |||
786 | static irqreturn_t apds9960_interrupt_handler(int irq, void *private) | ||
787 | { | ||
788 | struct iio_dev *indio_dev = private; | ||
789 | struct apds9960_data *data = iio_priv(indio_dev); | ||
790 | int ret, status; | ||
791 | |||
792 | ret = regmap_read(data->regmap, APDS9960_REG_STATUS, &status); | ||
793 | if (ret < 0) { | ||
794 | dev_err(&data->client->dev, "irq status reg read failed\n"); | ||
795 | return IRQ_HANDLED; | ||
796 | } | ||
797 | |||
798 | if ((status & APDS9960_REG_STATUS_ALS_INT) && data->als_int) { | ||
799 | iio_push_event(indio_dev, | ||
800 | IIO_UNMOD_EVENT_CODE(IIO_INTENSITY, 0, | ||
801 | IIO_EV_TYPE_THRESH, | ||
802 | IIO_EV_DIR_EITHER), | ||
803 | iio_get_time_ns()); | ||
804 | regmap_write(data->regmap, APDS9960_REG_CICLEAR, 1); | ||
805 | } | ||
806 | |||
807 | if ((status & APDS9960_REG_STATUS_PS_INT) && data->pxs_int) { | ||
808 | iio_push_event(indio_dev, | ||
809 | IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0, | ||
810 | IIO_EV_TYPE_THRESH, | ||
811 | IIO_EV_DIR_EITHER), | ||
812 | iio_get_time_ns()); | ||
813 | regmap_write(data->regmap, APDS9960_REG_PICLEAR, 1); | ||
814 | } | ||
815 | |||
816 | if (status & APDS9960_REG_STATUS_GINT) | ||
817 | apds9960_read_gesture_fifo(data); | ||
818 | |||
819 | return IRQ_HANDLED; | ||
820 | } | ||
821 | |||
822 | static int apds9960_set_powermode(struct apds9960_data *data, bool state) | ||
823 | { | ||
824 | return regmap_update_bits(data->regmap, APDS9960_REG_ENABLE, 1, state); | ||
825 | } | ||
826 | |||
827 | static int apds9960_buffer_postenable(struct iio_dev *indio_dev) | ||
828 | { | ||
829 | struct apds9960_data *data = iio_priv(indio_dev); | ||
830 | int ret; | ||
831 | |||
832 | ret = regmap_field_write(data->reg_int_ges, 1); | ||
833 | if (ret) | ||
834 | return ret; | ||
835 | |||
836 | ret = regmap_field_write(data->reg_enable_ges, 1); | ||
837 | if (ret) | ||
838 | return ret; | ||
839 | |||
840 | pm_runtime_get_sync(&data->client->dev); | ||
841 | |||
842 | return 0; | ||
843 | } | ||
844 | |||
845 | static int apds9960_buffer_predisable(struct iio_dev *indio_dev) | ||
846 | { | ||
847 | struct apds9960_data *data = iio_priv(indio_dev); | ||
848 | int ret; | ||
849 | |||
850 | ret = regmap_field_write(data->reg_enable_ges, 0); | ||
851 | if (ret) | ||
852 | return ret; | ||
853 | |||
854 | ret = regmap_field_write(data->reg_int_ges, 0); | ||
855 | if (ret) | ||
856 | return ret; | ||
857 | |||
858 | pm_runtime_put_autosuspend(&data->client->dev); | ||
859 | |||
860 | return 0; | ||
861 | } | ||
862 | |||
863 | static const struct iio_buffer_setup_ops apds9960_buffer_setup_ops = { | ||
864 | .postenable = apds9960_buffer_postenable, | ||
865 | .predisable = apds9960_buffer_predisable, | ||
866 | }; | ||
867 | |||
868 | static int apds9960_regfield_init(struct apds9960_data *data) | ||
869 | { | ||
870 | struct device *dev = &data->client->dev; | ||
871 | struct regmap *regmap = data->regmap; | ||
872 | |||
873 | data->reg_int_als = devm_regmap_field_alloc(dev, regmap, | ||
874 | apds9960_reg_field_int_als); | ||
875 | if (IS_ERR(data->reg_int_als)) { | ||
876 | dev_err(dev, "INT ALS reg field init failed\n"); | ||
877 | return PTR_ERR(data->reg_int_als); | ||
878 | } | ||
879 | |||
880 | data->reg_int_ges = devm_regmap_field_alloc(dev, regmap, | ||
881 | apds9960_reg_field_int_ges); | ||
882 | if (IS_ERR(data->reg_int_ges)) { | ||
883 | dev_err(dev, "INT gesture reg field init failed\n"); | ||
884 | return PTR_ERR(data->reg_int_ges); | ||
885 | } | ||
886 | |||
887 | data->reg_int_pxs = devm_regmap_field_alloc(dev, regmap, | ||
888 | apds9960_reg_field_int_pxs); | ||
889 | if (IS_ERR(data->reg_int_pxs)) { | ||
890 | dev_err(dev, "INT pxs reg field init failed\n"); | ||
891 | return PTR_ERR(data->reg_int_pxs); | ||
892 | } | ||
893 | |||
894 | data->reg_enable_als = devm_regmap_field_alloc(dev, regmap, | ||
895 | apds9960_reg_field_enable_als); | ||
896 | if (IS_ERR(data->reg_enable_als)) { | ||
897 | dev_err(dev, "Enable ALS reg field init failed\n"); | ||
898 | return PTR_ERR(data->reg_enable_als); | ||
899 | } | ||
900 | |||
901 | data->reg_enable_ges = devm_regmap_field_alloc(dev, regmap, | ||
902 | apds9960_reg_field_enable_ges); | ||
903 | if (IS_ERR(data->reg_enable_ges)) { | ||
904 | dev_err(dev, "Enable gesture reg field init failed\n"); | ||
905 | return PTR_ERR(data->reg_enable_ges); | ||
906 | } | ||
907 | |||
908 | data->reg_enable_pxs = devm_regmap_field_alloc(dev, regmap, | ||
909 | apds9960_reg_field_enable_pxs); | ||
910 | if (IS_ERR(data->reg_enable_pxs)) { | ||
911 | dev_err(dev, "Enable PXS reg field init failed\n"); | ||
912 | return PTR_ERR(data->reg_enable_pxs); | ||
913 | } | ||
914 | |||
915 | return 0; | ||
916 | } | ||
917 | |||
918 | static int apds9960_chip_init(struct apds9960_data *data) | ||
919 | { | ||
920 | int ret; | ||
921 | |||
922 | /* Default IT for ALS of 28 ms */ | ||
923 | ret = apds9960_set_it_time(data, 28000); | ||
924 | if (ret) | ||
925 | return ret; | ||
926 | |||
927 | /* Ensure gesture interrupt is OFF */ | ||
928 | ret = regmap_field_write(data->reg_int_ges, 0); | ||
929 | if (ret) | ||
930 | return ret; | ||
931 | |||
932 | /* Disable gesture sensor, since polling is useless from user-space */ | ||
933 | ret = regmap_field_write(data->reg_enable_ges, 0); | ||
934 | if (ret) | ||
935 | return ret; | ||
936 | |||
937 | /* Ensure proximity interrupt is OFF */ | ||
938 | ret = regmap_field_write(data->reg_int_pxs, 0); | ||
939 | if (ret) | ||
940 | return ret; | ||
941 | |||
942 | /* Enable proximity sensor for polling */ | ||
943 | ret = regmap_field_write(data->reg_enable_pxs, 1); | ||
944 | if (ret) | ||
945 | return ret; | ||
946 | |||
947 | /* Ensure ALS interrupt is OFF */ | ||
948 | ret = regmap_field_write(data->reg_int_als, 0); | ||
949 | if (ret) | ||
950 | return ret; | ||
951 | |||
952 | /* Enable ALS sensor for polling */ | ||
953 | ret = regmap_field_write(data->reg_enable_als, 1); | ||
954 | if (ret) | ||
955 | return ret; | ||
956 | /* | ||
957 | * When enabled trigger an interrupt after 3 readings | ||
958 | * outside threshold for ALS + PXS | ||
959 | */ | ||
960 | ret = regmap_write(data->regmap, APDS9960_REG_PERS, | ||
961 | APDS9960_DEFAULT_PERS); | ||
962 | if (ret) | ||
963 | return ret; | ||
964 | |||
965 | /* | ||
966 | * Wait for 4 event outside gesture threshold to prevent interrupt | ||
967 | * flooding. | ||
968 | */ | ||
969 | ret = regmap_update_bits(data->regmap, APDS9960_REG_GCONF_1, | ||
970 | APDS9960_REG_GCONF_1_GFIFO_THRES_MASK, | ||
971 | BIT(0) << APDS9960_REG_GCONF_1_GFIFO_THRES_MASK_SHIFT); | ||
972 | if (ret) | ||
973 | return ret; | ||
974 | |||
975 | /* Default ENTER and EXIT thresholds for the GESTURE engine. */ | ||
976 | ret = regmap_write(data->regmap, APDS9960_REG_GPENTH, | ||
977 | APDS9960_DEFAULT_GPENTH); | ||
978 | if (ret) | ||
979 | return ret; | ||
980 | |||
981 | ret = regmap_write(data->regmap, APDS9960_REG_GEXTH, | ||
982 | APDS9960_DEFAULT_GEXTH); | ||
983 | if (ret) | ||
984 | return ret; | ||
985 | |||
986 | ret = apds9960_set_powermode(data, 1); | ||
987 | if (ret) | ||
988 | return ret; | ||
989 | |||
990 | return 0; | ||
991 | } | ||
992 | |||
993 | static int apds9960_probe(struct i2c_client *client, | ||
994 | const struct i2c_device_id *id) | ||
995 | { | ||
996 | struct apds9960_data *data; | ||
997 | struct iio_buffer *buffer; | ||
998 | struct iio_dev *indio_dev; | ||
999 | int ret; | ||
1000 | |||
1001 | indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); | ||
1002 | if (!indio_dev) | ||
1003 | return -ENOMEM; | ||
1004 | |||
1005 | buffer = devm_iio_kfifo_allocate(&client->dev); | ||
1006 | if (!buffer) | ||
1007 | return -ENOMEM; | ||
1008 | |||
1009 | iio_device_attach_buffer(indio_dev, buffer); | ||
1010 | |||
1011 | indio_dev->info = &apds9960_info; | ||
1012 | indio_dev->name = APDS9960_DRV_NAME; | ||
1013 | indio_dev->channels = apds9960_channels; | ||
1014 | indio_dev->num_channels = ARRAY_SIZE(apds9960_channels); | ||
1015 | indio_dev->available_scan_masks = apds9960_scan_masks; | ||
1016 | indio_dev->modes = (INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE); | ||
1017 | indio_dev->setup_ops = &apds9960_buffer_setup_ops; | ||
1018 | |||
1019 | data = iio_priv(indio_dev); | ||
1020 | i2c_set_clientdata(client, indio_dev); | ||
1021 | |||
1022 | data->regmap = devm_regmap_init_i2c(client, &apds9960_regmap_config); | ||
1023 | if (IS_ERR(data->regmap)) { | ||
1024 | dev_err(&client->dev, "regmap initialization failed.\n"); | ||
1025 | return PTR_ERR(data->regmap); | ||
1026 | } | ||
1027 | |||
1028 | data->client = client; | ||
1029 | data->indio_dev = indio_dev; | ||
1030 | mutex_init(&data->lock); | ||
1031 | |||
1032 | ret = pm_runtime_set_active(&client->dev); | ||
1033 | if (ret) | ||
1034 | goto error_power_down; | ||
1035 | |||
1036 | pm_runtime_enable(&client->dev); | ||
1037 | pm_runtime_set_autosuspend_delay(&client->dev, 5000); | ||
1038 | pm_runtime_use_autosuspend(&client->dev); | ||
1039 | |||
1040 | apds9960_set_power_state(data, true); | ||
1041 | |||
1042 | ret = apds9960_regfield_init(data); | ||
1043 | if (ret) | ||
1044 | goto error_power_down; | ||
1045 | |||
1046 | ret = apds9960_chip_init(data); | ||
1047 | if (ret) | ||
1048 | goto error_power_down; | ||
1049 | |||
1050 | if (client->irq <= 0) { | ||
1051 | dev_err(&client->dev, "no valid irq defined\n"); | ||
1052 | ret = -EINVAL; | ||
1053 | goto error_power_down; | ||
1054 | } | ||
1055 | ret = devm_request_threaded_irq(&client->dev, client->irq, | ||
1056 | NULL, apds9960_interrupt_handler, | ||
1057 | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, | ||
1058 | "apds9960_event", | ||
1059 | indio_dev); | ||
1060 | if (ret) { | ||
1061 | dev_err(&client->dev, "request irq (%d) failed\n", client->irq); | ||
1062 | goto error_power_down; | ||
1063 | } | ||
1064 | |||
1065 | ret = iio_device_register(indio_dev); | ||
1066 | if (ret) | ||
1067 | goto error_power_down; | ||
1068 | |||
1069 | apds9960_set_power_state(data, false); | ||
1070 | |||
1071 | return 0; | ||
1072 | |||
1073 | error_power_down: | ||
1074 | apds9960_set_power_state(data, false); | ||
1075 | |||
1076 | return ret; | ||
1077 | } | ||
1078 | |||
1079 | static int apds9960_remove(struct i2c_client *client) | ||
1080 | { | ||
1081 | struct iio_dev *indio_dev = i2c_get_clientdata(client); | ||
1082 | struct apds9960_data *data = iio_priv(indio_dev); | ||
1083 | |||
1084 | iio_device_unregister(indio_dev); | ||
1085 | pm_runtime_disable(&client->dev); | ||
1086 | pm_runtime_set_suspended(&client->dev); | ||
1087 | apds9960_set_powermode(data, 0); | ||
1088 | |||
1089 | return 0; | ||
1090 | } | ||
1091 | |||
1092 | #ifdef CONFIG_PM | ||
1093 | static int apds9960_runtime_suspend(struct device *dev) | ||
1094 | { | ||
1095 | struct apds9960_data *data = | ||
1096 | iio_priv(i2c_get_clientdata(to_i2c_client(dev))); | ||
1097 | |||
1098 | return apds9960_set_powermode(data, 0); | ||
1099 | } | ||
1100 | |||
1101 | static int apds9960_runtime_resume(struct device *dev) | ||
1102 | { | ||
1103 | struct apds9960_data *data = | ||
1104 | iio_priv(i2c_get_clientdata(to_i2c_client(dev))); | ||
1105 | |||
1106 | return apds9960_set_powermode(data, 1); | ||
1107 | } | ||
1108 | #endif | ||
1109 | |||
1110 | static const struct dev_pm_ops apds9960_pm_ops = { | ||
1111 | SET_RUNTIME_PM_OPS(apds9960_runtime_suspend, | ||
1112 | apds9960_runtime_resume, NULL) | ||
1113 | }; | ||
1114 | |||
1115 | static const struct i2c_device_id apds9960_id[] = { | ||
1116 | { "apds9960", 0 }, | ||
1117 | {} | ||
1118 | }; | ||
1119 | MODULE_DEVICE_TABLE(i2c, apds9960_id); | ||
1120 | |||
1121 | static struct i2c_driver apds9960_driver = { | ||
1122 | .driver = { | ||
1123 | .name = APDS9960_DRV_NAME, | ||
1124 | .pm = &apds9960_pm_ops, | ||
1125 | .owner = THIS_MODULE, | ||
1126 | }, | ||
1127 | .probe = apds9960_probe, | ||
1128 | .remove = apds9960_remove, | ||
1129 | .id_table = apds9960_id, | ||
1130 | }; | ||
1131 | module_i2c_driver(apds9960_driver); | ||
1132 | |||
1133 | MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>"); | ||
1134 | MODULE_DESCRIPTION("ADPS9960 Gesture/RGB/ALS/Proximity sensor"); | ||
1135 | MODULE_LICENSE("GPL"); | ||