Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Input: add ADC resistor ladder driver

A common way of multiplexing buttons on a single input in cheap devices is
to use a resistor ladder on an ADC. This driver supports that configuration
by polling an ADC channel provided by IIO.

Acked-by: Jonathan Cameron <jic23@kernel.org>
Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
Acked-by: Rob Herring <robh@kernel.org>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>

authored by

Alexandre Belloni and committed by
Dmitry Torokhov
68077264 f959cd8c

+275
+49
Documentation/devicetree/bindings/input/adc-keys.txt
··· 1 + ADC attached resistor ladder buttons 2 + ------------------------------------ 3 + 4 + Required properties: 5 + - compatible: "adc-keys" 6 + - io-channels: Phandle to an ADC channel 7 + - io-channel-names = "buttons"; 8 + - keyup-threshold-microvolt: Voltage at which all the keys are considered up. 9 + 10 + Optional properties: 11 + - poll-interval: Poll interval time in milliseconds 12 + - autorepeat: Boolean, Enable auto repeat feature of Linux input 13 + subsystem. 14 + 15 + Each button (key) is represented as a sub-node of "adc-keys": 16 + 17 + Required subnode-properties: 18 + - label: Descriptive name of the key. 19 + - linux,code: Keycode to emit. 20 + - press-threshold-microvolt: Voltage ADC input when this key is pressed. 21 + 22 + Example: 23 + 24 + #include <dt-bindings/input/input.h> 25 + 26 + adc-keys { 27 + compatible = "adc-keys"; 28 + io-channels = <&lradc 0>; 29 + io-channel-names = "buttons"; 30 + keyup-threshold-microvolt = <2000000>; 31 + 32 + button-up { 33 + label = "Volume Up"; 34 + linux,code = <KEY_VOLUMEUP>; 35 + press-threshold-microvolt = <1500000>; 36 + }; 37 + 38 + button-down { 39 + label = "Volume Down"; 40 + linux,code = <KEY_VOLUMEDOWN>; 41 + press-threshold-microvolt = <1000000>; 42 + }; 43 + 44 + button-enter { 45 + label = "Enter"; 46 + linux,code = <KEY_ENTER>; 47 + press-threshold-microvolt = <500000>; 48 + }; 49 + };
+15
drivers/input/keyboard/Kconfig
··· 12 12 13 13 if INPUT_KEYBOARD 14 14 15 + config KEYBOARD_ADC 16 + tristate "ADC Ladder Buttons" 17 + depends on IIO 18 + select INPUT_POLLDEV 19 + help 20 + This driver implements support for buttons connected 21 + to an ADC using a resistor ladder. 22 + 23 + Say Y here if your device has such buttons connected to an ADC. Your 24 + board-specific setup logic must also provide a configuration data 25 + for mapping voltages to buttons. 26 + 27 + To compile this driver as a module, choose M here: the 28 + module will be called adc_keys. 29 + 15 30 config KEYBOARD_ADP5520 16 31 tristate "Keypad Support for ADP5520 PMIC" 17 32 depends on PMIC_ADP5520
+1
drivers/input/keyboard/Makefile
··· 4 4 5 5 # Each configuration option enables a list of files. 6 6 7 + obj-$(CONFIG_KEYBOARD_ADC) += adc-keys.o 7 8 obj-$(CONFIG_KEYBOARD_ADP5520) += adp5520-keys.o 8 9 obj-$(CONFIG_KEYBOARD_ADP5588) += adp5588-keys.o 9 10 obj-$(CONFIG_KEYBOARD_ADP5589) += adp5589-keys.o
+210
drivers/input/keyboard/adc-keys.c
··· 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 + 151 + platform_set_drvdata(pdev, st); 152 + 153 + poll_dev = devm_input_allocate_polled_device(dev); 154 + if (!poll_dev) { 155 + dev_err(dev, "failed to allocate input device\n"); 156 + return -ENOMEM; 157 + } 158 + 159 + if (!device_property_read_u32(dev, "poll-interval", &value)) 160 + poll_dev->poll_interval = value; 161 + 162 + poll_dev->poll = adc_keys_poll; 163 + poll_dev->private = st; 164 + 165 + input = poll_dev->input; 166 + 167 + input->name = pdev->name; 168 + input->phys = "adc-keys/input0"; 169 + 170 + input->id.bustype = BUS_HOST; 171 + input->id.vendor = 0x0001; 172 + input->id.product = 0x0001; 173 + input->id.version = 0x0100; 174 + 175 + __set_bit(EV_KEY, input->evbit); 176 + for (i = 0; i < st->num_keys; i++) 177 + __set_bit(st->map[i].keycode, input->keybit); 178 + 179 + if (device_property_read_bool(dev, "autorepeat")) 180 + __set_bit(EV_REP, input->evbit); 181 + 182 + error = input_register_polled_device(poll_dev); 183 + if (error) { 184 + dev_err(dev, "Unable to register input device: %d\n", error); 185 + return error; 186 + } 187 + 188 + return 0; 189 + } 190 + 191 + #ifdef CONFIG_OF 192 + static const struct of_device_id adc_keys_of_match[] = { 193 + { .compatible = "adc-keys", }, 194 + { } 195 + }; 196 + MODULE_DEVICE_TABLE(of, adc_keys_of_match); 197 + #endif 198 + 199 + static struct platform_driver __refdata adc_keys_driver = { 200 + .driver = { 201 + .name = "adc_keys", 202 + .of_match_table = of_match_ptr(adc_keys_of_match), 203 + }, 204 + .probe = adc_keys_probe, 205 + }; 206 + module_platform_driver(adc_keys_driver); 207 + 208 + MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@free-electrons.com>"); 209 + MODULE_DESCRIPTION("Input driver for resistor ladder connected on ADC"); 210 + MODULE_LICENSE("GPL v2");