Commit | Line | Data |
---|---|---|
68077264 AB |
1 | /* |
2 | * Input driver for resistor ladder connected on ADC | |
3 | * | |
4 | * Copyright (c) 2016 Alexandre Belloni | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify it | |
7 | * under the terms of the GNU General Public License version 2 as published by | |
8 | * the Free Software Foundation. | |
9 | */ | |
10 | ||
11 | #include <linux/err.h> | |
12 | #include <linux/iio/consumer.h> | |
13 | #include <linux/iio/types.h> | |
14 | #include <linux/input.h> | |
15 | #include <linux/input-polldev.h> | |
16 | #include <linux/kernel.h> | |
17 | #include <linux/module.h> | |
18 | #include <linux/of.h> | |
19 | #include <linux/platform_device.h> | |
20 | #include <linux/property.h> | |
21 | #include <linux/slab.h> | |
22 | ||
23 | struct adc_keys_button { | |
24 | u32 voltage; | |
25 | u32 keycode; | |
26 | }; | |
27 | ||
28 | struct adc_keys_state { | |
29 | struct iio_channel *channel; | |
30 | u32 num_keys; | |
31 | u32 last_key; | |
32 | u32 keyup_voltage; | |
33 | const struct adc_keys_button *map; | |
34 | }; | |
35 | ||
36 | static void adc_keys_poll(struct input_polled_dev *dev) | |
37 | { | |
38 | struct adc_keys_state *st = dev->private; | |
39 | int i, value, ret; | |
40 | u32 diff, closest = 0xffffffff; | |
41 | int keycode = 0; | |
42 | ||
43 | ret = iio_read_channel_processed(st->channel, &value); | |
44 | if (unlikely(ret < 0)) { | |
45 | /* Forcibly release key if any was pressed */ | |
46 | value = st->keyup_voltage; | |
47 | } else { | |
48 | for (i = 0; i < st->num_keys; i++) { | |
49 | diff = abs(st->map[i].voltage - value); | |
50 | if (diff < closest) { | |
51 | closest = diff; | |
52 | keycode = st->map[i].keycode; | |
53 | } | |
54 | } | |
55 | } | |
56 | ||
57 | if (abs(st->keyup_voltage - value) < closest) | |
58 | keycode = 0; | |
59 | ||
60 | if (st->last_key && st->last_key != keycode) | |
61 | input_report_key(dev->input, st->last_key, 0); | |
62 | ||
63 | if (keycode) | |
64 | input_report_key(dev->input, keycode, 1); | |
65 | ||
66 | input_sync(dev->input); | |
67 | st->last_key = keycode; | |
68 | } | |
69 | ||
70 | static int adc_keys_load_keymap(struct device *dev, struct adc_keys_state *st) | |
71 | { | |
72 | struct adc_keys_button *map; | |
73 | struct fwnode_handle *child; | |
74 | int i; | |
75 | ||
76 | st->num_keys = device_get_child_node_count(dev); | |
77 | if (st->num_keys == 0) { | |
78 | dev_err(dev, "keymap is missing\n"); | |
79 | return -EINVAL; | |
80 | } | |
81 | ||
82 | map = devm_kmalloc_array(dev, st->num_keys, sizeof(*map), GFP_KERNEL); | |
83 | if (!map) | |
84 | return -ENOMEM; | |
85 | ||
86 | i = 0; | |
87 | device_for_each_child_node(dev, child) { | |
88 | if (fwnode_property_read_u32(child, "press-threshold-microvolt", | |
89 | &map[i].voltage)) { | |
90 | dev_err(dev, "Key with invalid or missing voltage\n"); | |
91 | fwnode_handle_put(child); | |
92 | return -EINVAL; | |
93 | } | |
94 | map[i].voltage /= 1000; | |
95 | ||
96 | if (fwnode_property_read_u32(child, "linux,code", | |
97 | &map[i].keycode)) { | |
98 | dev_err(dev, "Key with invalid or missing linux,code\n"); | |
99 | fwnode_handle_put(child); | |
100 | return -EINVAL; | |
101 | } | |
102 | ||
103 | i++; | |
104 | } | |
105 | ||
106 | st->map = map; | |
107 | return 0; | |
108 | } | |
109 | ||
110 | static int adc_keys_probe(struct platform_device *pdev) | |
111 | { | |
112 | struct device *dev = &pdev->dev; | |
113 | struct adc_keys_state *st; | |
114 | struct input_polled_dev *poll_dev; | |
115 | struct input_dev *input; | |
116 | enum iio_chan_type type; | |
117 | int i, value; | |
118 | int error; | |
119 | ||
120 | st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL); | |
121 | if (!st) | |
122 | return -ENOMEM; | |
123 | ||
124 | st->channel = devm_iio_channel_get(dev, "buttons"); | |
125 | if (IS_ERR(st->channel)) | |
126 | return PTR_ERR(st->channel); | |
127 | ||
128 | if (!st->channel->indio_dev) | |
129 | return -ENXIO; | |
130 | ||
131 | error = iio_get_channel_type(st->channel, &type); | |
132 | if (error < 0) | |
133 | return error; | |
134 | ||
135 | if (type != IIO_VOLTAGE) { | |
136 | dev_err(dev, "Incompatible channel type %d\n", type); | |
137 | return -EINVAL; | |
138 | } | |
139 | ||
140 | if (device_property_read_u32(dev, "keyup-threshold-microvolt", | |
141 | &st->keyup_voltage)) { | |
142 | dev_err(dev, "Invalid or missing keyup voltage\n"); | |
143 | return -EINVAL; | |
144 | } | |
145 | st->keyup_voltage /= 1000; | |
146 | ||
147 | error = adc_keys_load_keymap(dev, st); | |
148 | if (error) | |
149 | return error; | |
150 | ||
68077264 AB |
151 | poll_dev = devm_input_allocate_polled_device(dev); |
152 | if (!poll_dev) { | |
153 | dev_err(dev, "failed to allocate input device\n"); | |
154 | return -ENOMEM; | |
155 | } | |
156 | ||
157 | if (!device_property_read_u32(dev, "poll-interval", &value)) | |
158 | poll_dev->poll_interval = value; | |
159 | ||
160 | poll_dev->poll = adc_keys_poll; | |
161 | poll_dev->private = st; | |
162 | ||
163 | input = poll_dev->input; | |
164 | ||
165 | input->name = pdev->name; | |
166 | input->phys = "adc-keys/input0"; | |
167 | ||
168 | input->id.bustype = BUS_HOST; | |
169 | input->id.vendor = 0x0001; | |
170 | input->id.product = 0x0001; | |
171 | input->id.version = 0x0100; | |
172 | ||
173 | __set_bit(EV_KEY, input->evbit); | |
174 | for (i = 0; i < st->num_keys; i++) | |
175 | __set_bit(st->map[i].keycode, input->keybit); | |
176 | ||
177 | if (device_property_read_bool(dev, "autorepeat")) | |
178 | __set_bit(EV_REP, input->evbit); | |
179 | ||
180 | error = input_register_polled_device(poll_dev); | |
181 | if (error) { | |
182 | dev_err(dev, "Unable to register input device: %d\n", error); | |
183 | return error; | |
184 | } | |
185 | ||
186 | return 0; | |
187 | } | |
188 | ||
189 | #ifdef CONFIG_OF | |
190 | static const struct of_device_id adc_keys_of_match[] = { | |
191 | { .compatible = "adc-keys", }, | |
192 | { } | |
193 | }; | |
194 | MODULE_DEVICE_TABLE(of, adc_keys_of_match); | |
195 | #endif | |
196 | ||
197 | static struct platform_driver __refdata adc_keys_driver = { | |
198 | .driver = { | |
199 | .name = "adc_keys", | |
200 | .of_match_table = of_match_ptr(adc_keys_of_match), | |
201 | }, | |
202 | .probe = adc_keys_probe, | |
203 | }; | |
204 | module_platform_driver(adc_keys_driver); | |
205 | ||
206 | MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@free-electrons.com>"); | |
207 | MODULE_DESCRIPTION("Input driver for resistor ladder connected on ADC"); | |
208 | MODULE_LICENSE("GPL v2"); |