aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/iio/Kconfig1
-rw-r--r--drivers/iio/Makefile1
-rw-r--r--drivers/iio/light/Kconfig10
-rw-r--r--drivers/iio/light/Makefile1
-rw-r--r--drivers/iio/light/hid-sensor-als.c386
5 files changed, 399 insertions, 0 deletions
diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
index fc937aca71fb..6e3f143fc71d 100644
--- a/drivers/iio/Kconfig
+++ b/drivers/iio/Kconfig
@@ -62,6 +62,7 @@ source "drivers/iio/frequency/Kconfig"
62source "drivers/iio/dac/Kconfig" 62source "drivers/iio/dac/Kconfig"
63source "drivers/iio/common/Kconfig" 63source "drivers/iio/common/Kconfig"
64source "drivers/iio/gyro/Kconfig" 64source "drivers/iio/gyro/Kconfig"
65source "drivers/iio/light/Kconfig"
65source "drivers/iio/magnetometer/Kconfig" 66source "drivers/iio/magnetometer/Kconfig"
66 67
67endif # IIO 68endif # IIO
diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
index 761f2b65ac52..f7fa3c0867b4 100644
--- a/drivers/iio/Makefile
+++ b/drivers/iio/Makefile
@@ -18,4 +18,5 @@ obj-y += frequency/
18obj-y += dac/ 18obj-y += dac/
19obj-y += common/ 19obj-y += common/
20obj-y += gyro/ 20obj-y += gyro/
21obj-y += light/
21obj-y += magnetometer/ 22obj-y += magnetometer/
diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig
index 91d15d2f694f..1763c9bcb98a 100644
--- a/drivers/iio/light/Kconfig
+++ b/drivers/iio/light/Kconfig
@@ -42,4 +42,14 @@ config VCNL4000
42 To compile this driver as a module, choose M here: the 42 To compile this driver as a module, choose M here: the
43 module will be called vcnl4000. 43 module will be called vcnl4000.
44 44
45config HID_SENSOR_ALS
46 depends on HID_SENSOR_HUB
47 select IIO_BUFFER
48 select IIO_TRIGGERED_BUFFER
49 select HID_SENSOR_IIO_COMMON
50 tristate "HID ALS"
51 help
52 Say yes here to build support for the HID SENSOR
53 Ambient light sensor.
54
45endmenu 55endmenu
diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile
index 13f8a782d292..21a8f0df1407 100644
--- a/drivers/iio/light/Makefile
+++ b/drivers/iio/light/Makefile
@@ -5,3 +5,4 @@
5obj-$(CONFIG_ADJD_S311) += adjd_s311.o 5obj-$(CONFIG_ADJD_S311) += adjd_s311.o
6obj-$(CONFIG_SENSORS_LM3533) += lm3533-als.o 6obj-$(CONFIG_SENSORS_LM3533) += lm3533-als.o
7obj-$(CONFIG_VCNL4000) += vcnl4000.o 7obj-$(CONFIG_VCNL4000) += vcnl4000.o
8obj-$(CONFIG_HID_SENSOR_ALS) += hid-sensor-als.o
diff --git a/drivers/iio/light/hid-sensor-als.c b/drivers/iio/light/hid-sensor-als.c
new file mode 100644
index 000000000000..2cff7d5167b2
--- /dev/null
+++ b/drivers/iio/light/hid-sensor-als.c
@@ -0,0 +1,386 @@
1/*
2 * HID Sensors Driver
3 * Copyright (c) 2012, Intel Corporation.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU General Public License,
7 * version 2, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 */
19#include <linux/device.h>
20#include <linux/platform_device.h>
21#include <linux/module.h>
22#include <linux/interrupt.h>
23#include <linux/irq.h>
24#include <linux/slab.h>
25#include <linux/hid-sensor-hub.h>
26#include <linux/iio/iio.h>
27#include <linux/iio/sysfs.h>
28#include <linux/iio/buffer.h>
29#include <linux/iio/trigger_consumer.h>
30#include <linux/iio/triggered_buffer.h>
31#include "../common/hid-sensors/hid-sensor-attributes.h"
32#include "../common/hid-sensors/hid-sensor-trigger.h"
33
34/*Format: HID-SENSOR-usage_id_in_hex*/
35/*Usage ID from spec for Accelerometer-3D: 0x200041*/
36#define DRIVER_NAME "HID-SENSOR-200041"
37
38#define CHANNEL_SCAN_INDEX_ILLUM 0
39
40struct als_state {
41 struct hid_sensor_hub_callbacks callbacks;
42 struct hid_sensor_iio_common common_attributes;
43 struct hid_sensor_hub_attribute_info als_illum;
44 u32 illum;
45};
46
47/* Channel definitions */
48static const struct iio_chan_spec als_channels[] = {
49 {
50 .type = IIO_INTENSITY,
51 .modified = 1,
52 .channel2 = IIO_MOD_LIGHT_BOTH,
53 .info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT |
54 IIO_CHAN_INFO_SCALE_SHARED_BIT |
55 IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT |
56 IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT,
57 .scan_index = CHANNEL_SCAN_INDEX_ILLUM,
58 }
59};
60
61/* Adjust channel real bits based on report descriptor */
62static void als_adjust_channel_bit_mask(struct iio_chan_spec *channels,
63 int channel, int size)
64{
65 channels[channel].scan_type.sign = 's';
66 /* Real storage bits will change based on the report desc. */
67 channels[channel].scan_type.realbits = size * 8;
68 /* Maximum size of a sample to capture is u32 */
69 channels[channel].scan_type.storagebits = sizeof(u32) * 8;
70}
71
72/* Channel read_raw handler */
73static int als_read_raw(struct iio_dev *indio_dev,
74 struct iio_chan_spec const *chan,
75 int *val, int *val2,
76 long mask)
77{
78 struct als_state *als_state = iio_priv(indio_dev);
79 int report_id = -1;
80 u32 address;
81 int ret;
82 int ret_type;
83
84 *val = 0;
85 *val2 = 0;
86 switch (mask) {
87 case 0:
88 switch (chan->scan_index) {
89 case CHANNEL_SCAN_INDEX_ILLUM:
90 report_id = als_state->als_illum.report_id;
91 address =
92 HID_USAGE_SENSOR_LIGHT_ILLUM;
93 break;
94 default:
95 report_id = -1;
96 break;
97 }
98 if (report_id >= 0)
99 *val = sensor_hub_input_attr_get_raw_value(
100 als_state->common_attributes.hsdev,
101 HID_USAGE_SENSOR_ALS, address,
102 report_id);
103 else {
104 *val = 0;
105 return -EINVAL;
106 }
107 ret_type = IIO_VAL_INT;
108 break;
109 case IIO_CHAN_INFO_SCALE:
110 *val = als_state->als_illum.units;
111 ret_type = IIO_VAL_INT;
112 break;
113 case IIO_CHAN_INFO_OFFSET:
114 *val = hid_sensor_convert_exponent(
115 als_state->als_illum.unit_expo);
116 ret_type = IIO_VAL_INT;
117 break;
118 case IIO_CHAN_INFO_SAMP_FREQ:
119 ret = hid_sensor_read_samp_freq_value(
120 &als_state->common_attributes, val, val2);
121 ret_type = IIO_VAL_INT_PLUS_MICRO;
122 break;
123 case IIO_CHAN_INFO_HYSTERESIS:
124 ret = hid_sensor_read_raw_hyst_value(
125 &als_state->common_attributes, val, val2);
126 ret_type = IIO_VAL_INT_PLUS_MICRO;
127 break;
128 default:
129 ret_type = -EINVAL;
130 break;
131 }
132
133 return ret_type;
134}
135
136/* Channel write_raw handler */
137static int als_write_raw(struct iio_dev *indio_dev,
138 struct iio_chan_spec const *chan,
139 int val,
140 int val2,
141 long mask)
142{
143 struct als_state *als_state = iio_priv(indio_dev);
144 int ret = 0;
145
146 switch (mask) {
147 case IIO_CHAN_INFO_SAMP_FREQ:
148 ret = hid_sensor_write_samp_freq_value(
149 &als_state->common_attributes, val, val2);
150 break;
151 case IIO_CHAN_INFO_HYSTERESIS:
152 ret = hid_sensor_write_raw_hyst_value(
153 &als_state->common_attributes, val, val2);
154 break;
155 default:
156 ret = -EINVAL;
157 }
158
159 return ret;
160}
161
162static int als_write_raw_get_fmt(struct iio_dev *indio_dev,
163 struct iio_chan_spec const *chan,
164 long mask)
165{
166 return IIO_VAL_INT_PLUS_MICRO;
167}
168
169static const struct iio_info als_info = {
170 .driver_module = THIS_MODULE,
171 .read_raw = &als_read_raw,
172 .write_raw = &als_write_raw,
173 .write_raw_get_fmt = &als_write_raw_get_fmt,
174};
175
176/* Function to push data to buffer */
177static void hid_sensor_push_data(struct iio_dev *indio_dev, u8 *data, int len)
178{
179 struct iio_buffer *buffer = indio_dev->buffer;
180 s64 timestamp = iio_get_time_ns();
181 int datum_sz;
182
183 dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n");
184 if (!buffer) {
185 dev_err(&indio_dev->dev, "Buffer == NULL\n");
186 return;
187 }
188 datum_sz = buffer->access->get_bytes_per_datum(buffer);
189 if (len > datum_sz) {
190 dev_err(&indio_dev->dev, "Datum size mismatch %d:%d\n", len,
191 datum_sz);
192 return;
193 }
194 buffer->access->store_to(buffer, (u8 *)data, timestamp);
195}
196
197/* Callback handler to send event after all samples are received and captured */
198static int als_proc_event(struct hid_sensor_hub_device *hsdev,
199 unsigned usage_id,
200 void *priv)
201{
202 struct iio_dev *indio_dev = platform_get_drvdata(priv);
203 struct als_state *als_state = iio_priv(indio_dev);
204
205 dev_dbg(&indio_dev->dev, "als_proc_event [%d]\n",
206 als_state->common_attributes.data_ready);
207 if (als_state->common_attributes.data_ready)
208 hid_sensor_push_data(indio_dev,
209 (u8 *)&als_state->illum,
210 sizeof(als_state->illum));
211
212 return 0;
213}
214
215/* Capture samples in local storage */
216static int als_capture_sample(struct hid_sensor_hub_device *hsdev,
217 unsigned usage_id,
218 size_t raw_len, char *raw_data,
219 void *priv)
220{
221 struct iio_dev *indio_dev = platform_get_drvdata(priv);
222 struct als_state *als_state = iio_priv(indio_dev);
223 int ret = -EINVAL;
224
225 switch (usage_id) {
226 case HID_USAGE_SENSOR_LIGHT_ILLUM:
227 als_state->illum = *(u32 *)raw_data;
228 ret = 0;
229 break;
230 default:
231 break;
232 }
233
234 return ret;
235}
236
237/* Parse report which is specific to an usage id*/
238static int als_parse_report(struct platform_device *pdev,
239 struct hid_sensor_hub_device *hsdev,
240 struct iio_chan_spec *channels,
241 unsigned usage_id,
242 struct als_state *st)
243{
244 int ret;
245
246 ret = sensor_hub_input_get_attribute_info(hsdev, HID_INPUT_REPORT,
247 usage_id,
248 HID_USAGE_SENSOR_LIGHT_ILLUM,
249 &st->als_illum);
250 if (ret < 0)
251 return ret;
252 als_adjust_channel_bit_mask(channels, CHANNEL_SCAN_INDEX_ILLUM,
253 st->als_illum.size);
254
255 dev_dbg(&pdev->dev, "als %x:%x\n", st->als_illum.index,
256 st->als_illum.report_id);
257
258 return ret;
259}
260
261/* Function to initialize the processing for usage id */
262static int __devinit hid_als_probe(struct platform_device *pdev)
263{
264 int ret = 0;
265 static const char *name = "als";
266 struct iio_dev *indio_dev;
267 struct als_state *als_state;
268 struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
269 struct iio_chan_spec *channels;
270
271 indio_dev = iio_device_alloc(sizeof(struct als_state));
272 if (indio_dev == NULL) {
273 ret = -ENOMEM;
274 goto error_ret;
275 }
276 platform_set_drvdata(pdev, indio_dev);
277
278 als_state = iio_priv(indio_dev);
279 als_state->common_attributes.hsdev = hsdev;
280 als_state->common_attributes.pdev = pdev;
281
282 ret = hid_sensor_parse_common_attributes(hsdev, HID_USAGE_SENSOR_ALS,
283 &als_state->common_attributes);
284 if (ret) {
285 dev_err(&pdev->dev, "failed to setup common attributes\n");
286 goto error_free_dev;
287 }
288
289 channels = kmemdup(als_channels,
290 sizeof(als_channels),
291 GFP_KERNEL);
292 if (!channels) {
293 dev_err(&pdev->dev, "failed to duplicate channels\n");
294 goto error_free_dev;
295 }
296
297 ret = als_parse_report(pdev, hsdev, channels,
298 HID_USAGE_SENSOR_ALS, als_state);
299 if (ret) {
300 dev_err(&pdev->dev, "failed to setup attributes\n");
301 goto error_free_dev_mem;
302 }
303
304 indio_dev->channels = channels;
305 indio_dev->num_channels =
306 ARRAY_SIZE(als_channels);
307 indio_dev->dev.parent = &pdev->dev;
308 indio_dev->info = &als_info;
309 indio_dev->name = name;
310 indio_dev->modes = INDIO_DIRECT_MODE;
311
312 ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
313 NULL, NULL);
314 if (ret) {
315 dev_err(&pdev->dev, "failed to initialize trigger buffer\n");
316 goto error_free_dev_mem;
317 }
318 als_state->common_attributes.data_ready = false;
319 ret = hid_sensor_setup_trigger(indio_dev, name,
320 &als_state->common_attributes);
321 if (ret < 0) {
322 dev_err(&pdev->dev, "trigger setup failed\n");
323 goto error_unreg_buffer_funcs;
324 }
325
326 ret = iio_device_register(indio_dev);
327 if (ret) {
328 dev_err(&pdev->dev, "device register failed\n");
329 goto error_remove_trigger;
330 }
331
332 als_state->callbacks.send_event = als_proc_event;
333 als_state->callbacks.capture_sample = als_capture_sample;
334 als_state->callbacks.pdev = pdev;
335 ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_ALS,
336 &als_state->callbacks);
337 if (ret < 0) {
338 dev_err(&pdev->dev, "callback reg failed\n");
339 goto error_iio_unreg;
340 }
341
342 return ret;
343
344error_iio_unreg:
345 iio_device_unregister(indio_dev);
346error_remove_trigger:
347 hid_sensor_remove_trigger(indio_dev);
348error_unreg_buffer_funcs:
349 iio_triggered_buffer_cleanup(indio_dev);
350error_free_dev_mem:
351 kfree(indio_dev->channels);
352error_free_dev:
353 iio_device_free(indio_dev);
354error_ret:
355 return ret;
356}
357
358/* Function to deinitialize the processing for usage id */
359static int __devinit hid_als_remove(struct platform_device *pdev)
360{
361 struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
362 struct iio_dev *indio_dev = platform_get_drvdata(pdev);
363
364 sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_ALS);
365 iio_device_unregister(indio_dev);
366 hid_sensor_remove_trigger(indio_dev);
367 iio_triggered_buffer_cleanup(indio_dev);
368 kfree(indio_dev->channels);
369 iio_device_free(indio_dev);
370
371 return 0;
372}
373
374static struct platform_driver hid_als_platform_driver = {
375 .driver = {
376 .name = DRIVER_NAME,
377 .owner = THIS_MODULE,
378 },
379 .probe = hid_als_probe,
380 .remove = hid_als_remove,
381};
382module_platform_driver(hid_als_platform_driver);
383
384MODULE_DESCRIPTION("HID Sensor ALS");
385MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>");
386MODULE_LICENSE("GPL");