blob: e2f73547fdc52c16253afc39e2992dffc5118180 [file] [log] [blame]
Peter Meerwald2690be92014-10-01 22:01:00 +01001/*
2 * ltr501.c - Support for Lite-On LTR501 ambient light and proximity sensor
3 *
4 * Copyright 2014 Peter Meerwald <pmeerw@pmeerw.net>
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 * 7-bit I2C slave address 0x23
11 *
12 * TODO: interrupt, threshold, measurement rate, IR LED characteristics
13 */
14
15#include <linux/module.h>
16#include <linux/i2c.h>
17#include <linux/err.h>
18#include <linux/delay.h>
Kuppuswamy Sathyanarayanan2f2c9632015-04-17 22:15:10 -070019#include <linux/regmap.h>
Peter Meerwald2690be92014-10-01 22:01:00 +010020
21#include <linux/iio/iio.h>
22#include <linux/iio/sysfs.h>
23#include <linux/iio/trigger_consumer.h>
24#include <linux/iio/buffer.h>
25#include <linux/iio/triggered_buffer.h>
26
27#define LTR501_DRV_NAME "ltr501"
28
29#define LTR501_ALS_CONTR 0x80 /* ALS operation mode, SW reset */
30#define LTR501_PS_CONTR 0x81 /* PS operation mode */
31#define LTR501_PART_ID 0x86
32#define LTR501_MANUFAC_ID 0x87
33#define LTR501_ALS_DATA1 0x88 /* 16-bit, little endian */
34#define LTR501_ALS_DATA0 0x8a /* 16-bit, little endian */
35#define LTR501_ALS_PS_STATUS 0x8c
36#define LTR501_PS_DATA 0x8d /* 16-bit, little endian */
Kuppuswamy Sathyanarayanan2f2c9632015-04-17 22:15:10 -070037#define LTR501_MAX_REG 0x9f
Peter Meerwald2690be92014-10-01 22:01:00 +010038
39#define LTR501_ALS_CONTR_SW_RESET BIT(2)
40#define LTR501_CONTR_PS_GAIN_MASK (BIT(3) | BIT(2))
41#define LTR501_CONTR_PS_GAIN_SHIFT 2
42#define LTR501_CONTR_ALS_GAIN_MASK BIT(3)
43#define LTR501_CONTR_ACTIVE BIT(1)
44
45#define LTR501_STATUS_ALS_RDY BIT(2)
46#define LTR501_STATUS_PS_RDY BIT(0)
47
48#define LTR501_PS_DATA_MASK 0x7ff
49
Kuppuswamy Sathyanarayanan2f2c9632015-04-17 22:15:10 -070050#define LTR501_REGMAP_NAME "ltr501_regmap"
51
Peter Meerwald2690be92014-10-01 22:01:00 +010052struct ltr501_data {
53 struct i2c_client *client;
54 struct mutex lock_als, lock_ps;
55 u8 als_contr, ps_contr;
Kuppuswamy Sathyanarayanan2f2c9632015-04-17 22:15:10 -070056 struct regmap *regmap;
Peter Meerwald2690be92014-10-01 22:01:00 +010057};
58
59static int ltr501_drdy(struct ltr501_data *data, u8 drdy_mask)
60{
61 int tries = 100;
Kuppuswamy Sathyanarayanan2f2c9632015-04-17 22:15:10 -070062 int ret, status;
Peter Meerwald2690be92014-10-01 22:01:00 +010063
64 while (tries--) {
Kuppuswamy Sathyanarayanan2f2c9632015-04-17 22:15:10 -070065 ret = regmap_read(data->regmap, LTR501_ALS_PS_STATUS, &status);
Peter Meerwald2690be92014-10-01 22:01:00 +010066 if (ret < 0)
67 return ret;
Kuppuswamy Sathyanarayanan2f2c9632015-04-17 22:15:10 -070068 if ((status & drdy_mask) == drdy_mask)
Peter Meerwald2690be92014-10-01 22:01:00 +010069 return 0;
70 msleep(25);
71 }
72
73 dev_err(&data->client->dev, "ltr501_drdy() failed, data not ready\n");
74 return -EIO;
75}
76
77static int ltr501_read_als(struct ltr501_data *data, __le16 buf[2])
78{
Kuppuswamy Sathyanarayanan2f2c9632015-04-17 22:15:10 -070079 int ret;
80
81 ret = ltr501_drdy(data, LTR501_STATUS_ALS_RDY);
Peter Meerwald2690be92014-10-01 22:01:00 +010082 if (ret < 0)
83 return ret;
84 /* always read both ALS channels in given order */
Kuppuswamy Sathyanarayanan2f2c9632015-04-17 22:15:10 -070085 return regmap_bulk_read(data->regmap, LTR501_ALS_DATA1,
86 buf, 2 * sizeof(__le16));
Peter Meerwald2690be92014-10-01 22:01:00 +010087}
88
89static int ltr501_read_ps(struct ltr501_data *data)
90{
Kuppuswamy Sathyanarayanan2f2c9632015-04-17 22:15:10 -070091 int ret, status;
92
93 ret = ltr501_drdy(data, LTR501_STATUS_PS_RDY);
Peter Meerwald2690be92014-10-01 22:01:00 +010094 if (ret < 0)
95 return ret;
Kuppuswamy Sathyanarayanan2f2c9632015-04-17 22:15:10 -070096
97 ret = regmap_bulk_read(data->regmap, LTR501_PS_DATA,
98 &status, 2);
99 if (ret < 0)
100 return ret;
101
102 return status;
Peter Meerwald2690be92014-10-01 22:01:00 +0100103}
104
105#define LTR501_INTENSITY_CHANNEL(_idx, _addr, _mod, _shared) { \
106 .type = IIO_INTENSITY, \
107 .modified = 1, \
108 .address = (_addr), \
109 .channel2 = (_mod), \
110 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
111 .info_mask_shared_by_type = (_shared), \
112 .scan_index = (_idx), \
113 .scan_type = { \
114 .sign = 'u', \
115 .realbits = 16, \
116 .storagebits = 16, \
117 .endianness = IIO_CPU, \
118 } \
119}
120
121static const struct iio_chan_spec ltr501_channels[] = {
122 LTR501_INTENSITY_CHANNEL(0, LTR501_ALS_DATA0, IIO_MOD_LIGHT_BOTH, 0),
123 LTR501_INTENSITY_CHANNEL(1, LTR501_ALS_DATA1, IIO_MOD_LIGHT_IR,
Daniel Balutaabda2b42015-04-09 17:17:47 +0300124 BIT(IIO_CHAN_INFO_SCALE)),
Peter Meerwald2690be92014-10-01 22:01:00 +0100125 {
126 .type = IIO_PROXIMITY,
127 .address = LTR501_PS_DATA,
128 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
129 BIT(IIO_CHAN_INFO_SCALE),
130 .scan_index = 2,
131 .scan_type = {
132 .sign = 'u',
133 .realbits = 11,
134 .storagebits = 16,
135 .endianness = IIO_CPU,
136 },
137 },
138 IIO_CHAN_SOFT_TIMESTAMP(3),
139};
140
141static const int ltr501_ps_gain[4][2] = {
142 {1, 0}, {0, 250000}, {0, 125000}, {0, 62500}
143};
144
145static int ltr501_read_raw(struct iio_dev *indio_dev,
Daniel Balutaabda2b42015-04-09 17:17:47 +0300146 struct iio_chan_spec const *chan,
147 int *val, int *val2, long mask)
Peter Meerwald2690be92014-10-01 22:01:00 +0100148{
149 struct ltr501_data *data = iio_priv(indio_dev);
150 __le16 buf[2];
151 int ret, i;
152
153 switch (mask) {
154 case IIO_CHAN_INFO_RAW:
155 if (iio_buffer_enabled(indio_dev))
156 return -EBUSY;
157
158 switch (chan->type) {
159 case IIO_INTENSITY:
160 mutex_lock(&data->lock_als);
161 ret = ltr501_read_als(data, buf);
162 mutex_unlock(&data->lock_als);
163 if (ret < 0)
164 return ret;
165 *val = le16_to_cpu(chan->address == LTR501_ALS_DATA1 ?
Daniel Balutaabda2b42015-04-09 17:17:47 +0300166 buf[0] : buf[1]);
Peter Meerwald2690be92014-10-01 22:01:00 +0100167 return IIO_VAL_INT;
168 case IIO_PROXIMITY:
169 mutex_lock(&data->lock_ps);
170 ret = ltr501_read_ps(data);
171 mutex_unlock(&data->lock_ps);
172 if (ret < 0)
173 return ret;
174 *val = ret & LTR501_PS_DATA_MASK;
175 return IIO_VAL_INT;
176 default:
177 return -EINVAL;
178 }
179 case IIO_CHAN_INFO_SCALE:
180 switch (chan->type) {
181 case IIO_INTENSITY:
182 if (data->als_contr & LTR501_CONTR_ALS_GAIN_MASK) {
183 *val = 0;
184 *val2 = 5000;
185 return IIO_VAL_INT_PLUS_MICRO;
Peter Meerwald2690be92014-10-01 22:01:00 +0100186 }
Kuppuswamy Sathyanarayanan2f2c9632015-04-17 22:15:10 -0700187 *val = 1;
188 *val2 = 0;
189 return IIO_VAL_INT;
Peter Meerwald2690be92014-10-01 22:01:00 +0100190 case IIO_PROXIMITY:
191 i = (data->ps_contr & LTR501_CONTR_PS_GAIN_MASK) >>
192 LTR501_CONTR_PS_GAIN_SHIFT;
193 *val = ltr501_ps_gain[i][0];
194 *val2 = ltr501_ps_gain[i][1];
195 return IIO_VAL_INT_PLUS_MICRO;
196 default:
197 return -EINVAL;
198 }
199 }
200 return -EINVAL;
201}
202
203static int ltr501_get_ps_gain_index(int val, int val2)
204{
205 int i;
206
207 for (i = 0; i < ARRAY_SIZE(ltr501_ps_gain); i++)
208 if (val == ltr501_ps_gain[i][0] && val2 == ltr501_ps_gain[i][1])
209 return i;
210
211 return -1;
212}
213
214static int ltr501_write_raw(struct iio_dev *indio_dev,
Daniel Balutaabda2b42015-04-09 17:17:47 +0300215 struct iio_chan_spec const *chan,
216 int val, int val2, long mask)
Peter Meerwald2690be92014-10-01 22:01:00 +0100217{
218 struct ltr501_data *data = iio_priv(indio_dev);
219 int i;
220
221 if (iio_buffer_enabled(indio_dev))
222 return -EBUSY;
223
224 switch (mask) {
225 case IIO_CHAN_INFO_SCALE:
226 switch (chan->type) {
227 case IIO_INTENSITY:
228 if (val == 0 && val2 == 5000)
229 data->als_contr |= LTR501_CONTR_ALS_GAIN_MASK;
230 else if (val == 1 && val2 == 0)
231 data->als_contr &= ~LTR501_CONTR_ALS_GAIN_MASK;
232 else
233 return -EINVAL;
Kuppuswamy Sathyanarayanan2f2c9632015-04-17 22:15:10 -0700234
235 return regmap_write(data->regmap, LTR501_ALS_CONTR,
236 data->als_contr);
Peter Meerwald2690be92014-10-01 22:01:00 +0100237 case IIO_PROXIMITY:
238 i = ltr501_get_ps_gain_index(val, val2);
239 if (i < 0)
240 return -EINVAL;
241 data->ps_contr &= ~LTR501_CONTR_PS_GAIN_MASK;
242 data->ps_contr |= i << LTR501_CONTR_PS_GAIN_SHIFT;
Kuppuswamy Sathyanarayanan2f2c9632015-04-17 22:15:10 -0700243
244 return regmap_write(data->regmap, LTR501_PS_CONTR,
245 data->ps_contr);
Peter Meerwald2690be92014-10-01 22:01:00 +0100246 default:
247 return -EINVAL;
248 }
249 }
250 return -EINVAL;
251}
252
253static IIO_CONST_ATTR(in_proximity_scale_available, "1 0.25 0.125 0.0625");
254static IIO_CONST_ATTR(in_intensity_scale_available, "1 0.005");
255
256static struct attribute *ltr501_attributes[] = {
257 &iio_const_attr_in_proximity_scale_available.dev_attr.attr,
258 &iio_const_attr_in_intensity_scale_available.dev_attr.attr,
259 NULL
260};
261
262static const struct attribute_group ltr501_attribute_group = {
263 .attrs = ltr501_attributes,
264};
265
266static const struct iio_info ltr501_info = {
267 .read_raw = ltr501_read_raw,
268 .write_raw = ltr501_write_raw,
269 .attrs = &ltr501_attribute_group,
270 .driver_module = THIS_MODULE,
271};
272
Kuppuswamy Sathyanarayanan2f2c9632015-04-17 22:15:10 -0700273static int ltr501_write_contr(struct ltr501_data *data, u8 als_val, u8 ps_val)
Peter Meerwald2690be92014-10-01 22:01:00 +0100274{
Kuppuswamy Sathyanarayanan2f2c9632015-04-17 22:15:10 -0700275 int ret;
276
277 ret = regmap_write(data->regmap, LTR501_ALS_CONTR, als_val);
Peter Meerwald2690be92014-10-01 22:01:00 +0100278 if (ret < 0)
279 return ret;
280
Kuppuswamy Sathyanarayanan2f2c9632015-04-17 22:15:10 -0700281 return regmap_write(data->regmap, LTR501_PS_CONTR, ps_val);
Peter Meerwald2690be92014-10-01 22:01:00 +0100282}
283
284static irqreturn_t ltr501_trigger_handler(int irq, void *p)
285{
286 struct iio_poll_func *pf = p;
287 struct iio_dev *indio_dev = pf->indio_dev;
288 struct ltr501_data *data = iio_priv(indio_dev);
289 u16 buf[8];
290 __le16 als_buf[2];
291 u8 mask = 0;
292 int j = 0;
Kuppuswamy Sathyanarayanan2f2c9632015-04-17 22:15:10 -0700293 int ret, psdata;
Peter Meerwald2690be92014-10-01 22:01:00 +0100294
295 memset(buf, 0, sizeof(buf));
296
297 /* figure out which data needs to be ready */
298 if (test_bit(0, indio_dev->active_scan_mask) ||
Daniel Balutaabda2b42015-04-09 17:17:47 +0300299 test_bit(1, indio_dev->active_scan_mask))
Peter Meerwald2690be92014-10-01 22:01:00 +0100300 mask |= LTR501_STATUS_ALS_RDY;
301 if (test_bit(2, indio_dev->active_scan_mask))
302 mask |= LTR501_STATUS_PS_RDY;
303
304 ret = ltr501_drdy(data, mask);
305 if (ret < 0)
306 goto done;
307
308 if (mask & LTR501_STATUS_ALS_RDY) {
Kuppuswamy Sathyanarayanan2f2c9632015-04-17 22:15:10 -0700309 ret = regmap_bulk_read(data->regmap, LTR501_ALS_DATA1,
310 (u8 *)als_buf, sizeof(als_buf));
Peter Meerwald2690be92014-10-01 22:01:00 +0100311 if (ret < 0)
312 return ret;
313 if (test_bit(0, indio_dev->active_scan_mask))
314 buf[j++] = le16_to_cpu(als_buf[1]);
315 if (test_bit(1, indio_dev->active_scan_mask))
316 buf[j++] = le16_to_cpu(als_buf[0]);
317 }
318
319 if (mask & LTR501_STATUS_PS_RDY) {
Kuppuswamy Sathyanarayanan2f2c9632015-04-17 22:15:10 -0700320 ret = regmap_bulk_read(data->regmap, LTR501_PS_DATA,
321 &psdata, 2);
Peter Meerwald2690be92014-10-01 22:01:00 +0100322 if (ret < 0)
323 goto done;
Kuppuswamy Sathyanarayanan2f2c9632015-04-17 22:15:10 -0700324 buf[j++] = psdata & LTR501_PS_DATA_MASK;
Peter Meerwald2690be92014-10-01 22:01:00 +0100325 }
326
Daniel Balutaabda2b42015-04-09 17:17:47 +0300327 iio_push_to_buffers_with_timestamp(indio_dev, buf, iio_get_time_ns());
Peter Meerwald2690be92014-10-01 22:01:00 +0100328
329done:
330 iio_trigger_notify_done(indio_dev->trig);
331
332 return IRQ_HANDLED;
333}
334
335static int ltr501_init(struct ltr501_data *data)
336{
Kuppuswamy Sathyanarayanan2f2c9632015-04-17 22:15:10 -0700337 int ret, status;
Peter Meerwald2690be92014-10-01 22:01:00 +0100338
Kuppuswamy Sathyanarayanan2f2c9632015-04-17 22:15:10 -0700339 ret = regmap_read(data->regmap, LTR501_ALS_CONTR, &status);
Peter Meerwald2690be92014-10-01 22:01:00 +0100340 if (ret < 0)
341 return ret;
Peter Meerwald2690be92014-10-01 22:01:00 +0100342
Kuppuswamy Sathyanarayanan2f2c9632015-04-17 22:15:10 -0700343 data->als_contr = status | LTR501_CONTR_ACTIVE;
344
345 ret = regmap_read(data->regmap, LTR501_PS_CONTR, &status);
Peter Meerwald2690be92014-10-01 22:01:00 +0100346 if (ret < 0)
347 return ret;
Peter Meerwald2690be92014-10-01 22:01:00 +0100348
Kuppuswamy Sathyanarayanan2f2c9632015-04-17 22:15:10 -0700349 data->ps_contr = status | LTR501_CONTR_ACTIVE;
350
351 return ltr501_write_contr(data, data->als_contr, data->ps_contr);
Peter Meerwald2690be92014-10-01 22:01:00 +0100352}
353
Kuppuswamy Sathyanarayanan2f2c9632015-04-17 22:15:10 -0700354static bool ltr501_is_volatile_reg(struct device *dev, unsigned int reg)
355{
356 switch (reg) {
357 case LTR501_ALS_DATA1:
358 case LTR501_ALS_DATA0:
359 case LTR501_ALS_PS_STATUS:
360 case LTR501_PS_DATA:
361 return true;
362 default:
363 return false;
364 }
365}
366
367static struct regmap_config ltr501_regmap_config = {
368 .name = LTR501_REGMAP_NAME,
369 .reg_bits = 8,
370 .val_bits = 8,
371 .max_register = LTR501_MAX_REG,
372 .cache_type = REGCACHE_RBTREE,
373 .volatile_reg = ltr501_is_volatile_reg,
374};
375
Cristina Opriceana1ca510b2015-04-01 18:50:17 +0300376static int ltr501_powerdown(struct ltr501_data *data)
377{
Kuppuswamy Sathyanarayanan2f2c9632015-04-17 22:15:10 -0700378 return ltr501_write_contr(data, data->als_contr & ~LTR501_CONTR_ACTIVE,
Cristina Opriceana1ca510b2015-04-01 18:50:17 +0300379 data->ps_contr & ~LTR501_CONTR_ACTIVE);
380}
381
Peter Meerwald2690be92014-10-01 22:01:00 +0100382static int ltr501_probe(struct i2c_client *client,
Daniel Balutaabda2b42015-04-09 17:17:47 +0300383 const struct i2c_device_id *id)
Peter Meerwald2690be92014-10-01 22:01:00 +0100384{
385 struct ltr501_data *data;
386 struct iio_dev *indio_dev;
Kuppuswamy Sathyanarayanan2f2c9632015-04-17 22:15:10 -0700387 struct regmap *regmap;
388 int ret, partid;
Peter Meerwald2690be92014-10-01 22:01:00 +0100389
390 indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
391 if (!indio_dev)
392 return -ENOMEM;
393
Kuppuswamy Sathyanarayanan2f2c9632015-04-17 22:15:10 -0700394 regmap = devm_regmap_init_i2c(client, &ltr501_regmap_config);
395 if (IS_ERR(regmap)) {
396 dev_err(&client->dev, "Regmap initialization failed.\n");
397 return PTR_ERR(regmap);
398 }
399
Peter Meerwald2690be92014-10-01 22:01:00 +0100400 data = iio_priv(indio_dev);
401 i2c_set_clientdata(client, indio_dev);
402 data->client = client;
Kuppuswamy Sathyanarayanan2f2c9632015-04-17 22:15:10 -0700403 data->regmap = regmap;
Peter Meerwald2690be92014-10-01 22:01:00 +0100404 mutex_init(&data->lock_als);
405 mutex_init(&data->lock_ps);
406
Kuppuswamy Sathyanarayanan2f2c9632015-04-17 22:15:10 -0700407 ret = regmap_read(data->regmap, LTR501_PART_ID, &partid);
Peter Meerwald2690be92014-10-01 22:01:00 +0100408 if (ret < 0)
409 return ret;
Kuppuswamy Sathyanarayanan2f2c9632015-04-17 22:15:10 -0700410 if ((partid >> 4) != 0x8)
Peter Meerwald2690be92014-10-01 22:01:00 +0100411 return -ENODEV;
412
413 indio_dev->dev.parent = &client->dev;
414 indio_dev->info = &ltr501_info;
415 indio_dev->channels = ltr501_channels;
416 indio_dev->num_channels = ARRAY_SIZE(ltr501_channels);
417 indio_dev->name = LTR501_DRV_NAME;
418 indio_dev->modes = INDIO_DIRECT_MODE;
419
420 ret = ltr501_init(data);
421 if (ret < 0)
422 return ret;
423
424 ret = iio_triggered_buffer_setup(indio_dev, NULL,
Daniel Balutaabda2b42015-04-09 17:17:47 +0300425 ltr501_trigger_handler, NULL);
Peter Meerwald2690be92014-10-01 22:01:00 +0100426 if (ret)
Cristina Opriceana1ca510b2015-04-01 18:50:17 +0300427 goto powerdown_on_error;
Peter Meerwald2690be92014-10-01 22:01:00 +0100428
429 ret = iio_device_register(indio_dev);
430 if (ret)
431 goto error_unreg_buffer;
432
433 return 0;
434
435error_unreg_buffer:
436 iio_triggered_buffer_cleanup(indio_dev);
Cristina Opriceana1ca510b2015-04-01 18:50:17 +0300437powerdown_on_error:
438 ltr501_powerdown(data);
Peter Meerwald2690be92014-10-01 22:01:00 +0100439 return ret;
440}
441
Peter Meerwald2690be92014-10-01 22:01:00 +0100442static int ltr501_remove(struct i2c_client *client)
443{
444 struct iio_dev *indio_dev = i2c_get_clientdata(client);
445
446 iio_device_unregister(indio_dev);
447 iio_triggered_buffer_cleanup(indio_dev);
448 ltr501_powerdown(iio_priv(indio_dev));
449
450 return 0;
451}
452
453#ifdef CONFIG_PM_SLEEP
454static int ltr501_suspend(struct device *dev)
455{
456 struct ltr501_data *data = iio_priv(i2c_get_clientdata(
Daniel Balutaabda2b42015-04-09 17:17:47 +0300457 to_i2c_client(dev)));
Peter Meerwald2690be92014-10-01 22:01:00 +0100458 return ltr501_powerdown(data);
459}
460
461static int ltr501_resume(struct device *dev)
462{
463 struct ltr501_data *data = iio_priv(i2c_get_clientdata(
Daniel Balutaabda2b42015-04-09 17:17:47 +0300464 to_i2c_client(dev)));
Peter Meerwald2690be92014-10-01 22:01:00 +0100465
Kuppuswamy Sathyanarayanan2f2c9632015-04-17 22:15:10 -0700466 return ltr501_write_contr(data, data->als_contr,
Peter Meerwald2690be92014-10-01 22:01:00 +0100467 data->ps_contr);
468}
469#endif
470
471static SIMPLE_DEV_PM_OPS(ltr501_pm_ops, ltr501_suspend, ltr501_resume);
472
473static const struct i2c_device_id ltr501_id[] = {
474 { "ltr501", 0 },
475 { }
476};
477MODULE_DEVICE_TABLE(i2c, ltr501_id);
478
479static struct i2c_driver ltr501_driver = {
480 .driver = {
481 .name = LTR501_DRV_NAME,
482 .pm = &ltr501_pm_ops,
483 .owner = THIS_MODULE,
484 },
485 .probe = ltr501_probe,
486 .remove = ltr501_remove,
487 .id_table = ltr501_id,
488};
489
490module_i2c_driver(ltr501_driver);
491
492MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
493MODULE_DESCRIPTION("Lite-On LTR501 ambient light and proximity sensor driver");
494MODULE_LICENSE("GPL");