linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 1/2] input: adc-keys: add DT binding documentation
@ 2016-07-01 21:30 Alexandre Belloni
  2016-07-01 21:30 ` [PATCH 2/2] input: add ADC resistor ladder driver Alexandre Belloni
  2016-07-03 13:43 ` [PATCH 1/2] input: adc-keys: add DT binding documentation Jonathan Cameron
  0 siblings, 2 replies; 7+ messages in thread
From: Alexandre Belloni @ 2016-07-01 21:30 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Jonathan Cameron, Maxime Ripard, linux-input, linux-iio,
	linux-kernel, Alexandre Belloni, Rob Herring, devicetree

Add documentation for ADC keys

Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
---
Cc: Rob Herring <robh+dt@kernel.org>
Cc: devicetree@vger.kernel.org

 .../devicetree/bindings/input/adc-keys.txt         | 45 ++++++++++++++++++++++
 1 file changed, 45 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/input/adc-keys.txt

diff --git a/Documentation/devicetree/bindings/input/adc-keys.txt b/Documentation/devicetree/bindings/input/adc-keys.txt
new file mode 100644
index 000000000000..873476bb4eab
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/adc-keys.txt
@@ -0,0 +1,45 @@
+Allwinner sun4i low res adc attached tablet keys
+------------------------------------------------
+
+Required properties:
+ - compatible: "adc-keys"
+ - io-channels: Phandle to an ADC channel
+ - io-channel-names = "buttons";
+ - voltage-keyup-mvolt: Voltage at which all the keys are considered up.
+
+Each key is represented as a sub-node of "adc-keys":
+
+Required subnode-properties:
+	- label: Descriptive name of the key.
+	- linux,code: Keycode to emit.
+	- voltage-mvolt: Voltage adc input when this key is pressed.
+
+Example:
+
+#include <dt-bindings/input/input.h>
+
+	adc-keys {
+		compatible = "adc-keys";
+		io-channels = <&lradc 0>;
+		io-channel-names = "buttons";
+		voltage-keyup-mvolt = <2000>;
+
+		button@1500 {
+			label = "Volume Up";
+			linux,code = <KEY_VOLUMEUP>;
+			voltage-mvolt = <1500>;
+		};
+
+		button@1000 {
+			label = "Volume Down";
+			linux,code = <KEY_VOLUMEDOWN>;
+			voltage-mvolt = <1000>;
+		};
+
+		button@500 {
+			label = "Enter";
+			linux,code = <KEY_ENTER>;
+			voltage-mvolt = <500>;
+		};
+	};
+
-- 
2.8.1

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 2/2] input: add ADC resistor ladder driver
  2016-07-01 21:30 [PATCH 1/2] input: adc-keys: add DT binding documentation Alexandre Belloni
@ 2016-07-01 21:30 ` Alexandre Belloni
  2016-07-02 22:21   ` Matt Ranostay
  2016-07-03 13:50   ` Jonathan Cameron
  2016-07-03 13:43 ` [PATCH 1/2] input: adc-keys: add DT binding documentation Jonathan Cameron
  1 sibling, 2 replies; 7+ messages in thread
From: Alexandre Belloni @ 2016-07-01 21:30 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Jonathan Cameron, Maxime Ripard, linux-input, linux-iio,
	linux-kernel, Alexandre Belloni

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.

Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
---
 drivers/input/keyboard/Kconfig    |  15 +++
 drivers/input/keyboard/Makefile   |   1 +
 drivers/input/keyboard/adc-keys.c | 209 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 225 insertions(+)
 create mode 100644 drivers/input/keyboard/adc-keys.c

diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index 509608c95994..ba20808f31e0 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -12,6 +12,21 @@ menuconfig INPUT_KEYBOARD
 
 if INPUT_KEYBOARD
 
+config KEYBOARD_ADC
+	tristate "ADC ladder Buttons"
+	depends on IIO || COMPILE_TEST
+	select INPUT_POLLDEV
+	help
+	  This driver implements support for buttons connected
+	  to an ADC using a resistor ladder.
+
+	  Say Y here if your device has such buttons connected to an ADC.  Your
+	  board-specific setup logic must also provide a configuration data
+	  saying mapping voltages to buttons.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called adc_keys.
+
 config KEYBOARD_ADP5520
 	tristate "Keypad Support for ADP5520 PMIC"
 	depends on PMIC_ADP5520
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index 1d416ddf84e4..d9f4cfcf3410 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -4,6 +4,7 @@
 
 # Each configuration option enables a list of files.
 
+obj-$(CONFIG_KEYBOARD_ADC)		+= adc-keys.o
 obj-$(CONFIG_KEYBOARD_ADP5520)		+= adp5520-keys.o
 obj-$(CONFIG_KEYBOARD_ADP5588)		+= adp5588-keys.o
 obj-$(CONFIG_KEYBOARD_ADP5589)		+= adp5589-keys.o
diff --git a/drivers/input/keyboard/adc-keys.c b/drivers/input/keyboard/adc-keys.c
new file mode 100644
index 000000000000..ba6ee91272b2
--- /dev/null
+++ b/drivers/input/keyboard/adc-keys.c
@@ -0,0 +1,209 @@
+/* Input driver for resistor ladder connected on ADC
+ *
+ * Copyright (c) 2016 Alexandre Belloni
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/input.h>
+#include <linux/input-polldev.h>
+#include <linux/iio/consumer.h>
+#include <linux/iio/types.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+
+struct adc_keys_button {
+	u32 voltage;
+	u32 keycode;
+};
+
+struct adc_keys_state {
+	struct iio_channel *channel;
+	u32 num_keys;
+	u32 last_key;
+	u32 keyup_voltage;
+	struct adc_keys_button *map;
+};
+
+static void adc_keys_poll(struct input_polled_dev *dev)
+{
+	struct adc_keys_state *st = dev->private;
+	int i, value, ret;
+	u32 diff, closest = 0xffffffff;
+	int keycode = 0;
+
+	ret = iio_read_channel_processed(st->channel, &value);
+	if (ret < 0) {
+		if (st->last_key) {
+			input_report_key(dev->input, st->last_key, 0);
+			input_sync(dev->input);
+			st->last_key = 0;
+		}
+		return;
+	}
+
+	for (i = 0; i < st->num_keys; i++) {
+		diff = abs(st->map[i].voltage - value);
+		if (diff < closest) {
+			closest = diff;
+			keycode = st->map[i].keycode;
+		}
+	}
+
+	if (abs(st->keyup_voltage - value) < closest) {
+		input_report_key(dev->input, st->last_key, 0);
+		st->last_key = 0;
+	} else {
+		if (st->last_key && st->last_key != keycode)
+			input_report_key(dev->input, st->last_key, 0);
+		input_report_key(dev->input, keycode, 1);
+		st->last_key = keycode;
+	}
+
+	input_sync(dev->input);
+}
+
+static int adc_keys_load_dt_keymap(struct device *dev,
+				   struct adc_keys_state *st)
+{
+	struct device_node *pp, *np = dev->of_node;
+	int i;
+
+	st->num_keys = of_get_child_count(np);
+	if (st->num_keys == 0) {
+		dev_err(dev, "keymap is missing\n");
+		return -EINVAL;
+	}
+
+	st->map = devm_kmalloc_array(dev, st->num_keys, sizeof(*st->map),
+				     GFP_KERNEL);
+	if (!st->map)
+		return -ENOMEM;
+
+	i = 0;
+	for_each_child_of_node(np, pp) {
+		struct adc_keys_button *map = &st->map[i];
+
+		if (of_property_read_u32(pp, "voltage-mvolt", &map->voltage)) {
+			dev_err(dev, "%s: Invalid or missing voltage\n",
+				pp->name);
+			return -EINVAL;
+		}
+
+		if (of_property_read_u32(pp, "linux,code", &map->keycode)) {
+			dev_err(dev, "%s: Invalid or missing linux,code\n",
+				pp->name);
+			return -EINVAL;
+		}
+
+		i++;
+	}
+
+	return 0;
+}
+
+static int adc_keys_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *node = dev->of_node;
+	struct adc_keys_state *st;
+	struct input_polled_dev *poll_dev = NULL;
+	struct input_dev *input;
+	enum iio_chan_type type;
+	int i, value, ret;
+
+	st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);
+	if (!st)
+		return -ENOMEM;
+
+	st->channel = devm_iio_channel_get(dev, "buttons");
+	if (IS_ERR(st->channel))
+		return PTR_ERR(st->channel);
+
+	if (!st->channel->indio_dev)
+		return -ENODEV;
+
+	ret = iio_get_channel_type(st->channel, &type);
+	if (ret < 0)
+		return ret;
+
+	if (type != IIO_VOLTAGE) {
+		dev_err(dev, "Incompatible channel type %d\n", type);
+		return -EINVAL;
+	}
+
+	if (of_property_read_u32(node, "voltage-keyup-mvolt",
+				 &st->keyup_voltage)) {
+		dev_err(dev, "Invalid or missing keyup voltage\n");
+		return -EINVAL;
+	}
+
+	ret = adc_keys_load_dt_keymap(dev, st);
+	if (ret)
+		return ret;
+
+	platform_set_drvdata(pdev, st);
+
+	poll_dev = devm_input_allocate_polled_device(dev);
+	if (!poll_dev) {
+		dev_err(dev, "failed to allocate input device\n");
+		return -ENOMEM;
+	}
+
+	if (!of_property_read_u32(node, "poll-interval", &value))
+		poll_dev->poll_interval = value;
+	poll_dev->poll = adc_keys_poll;
+	poll_dev->private = st;
+
+	input = poll_dev->input;
+
+	input->name = pdev->name;
+	input->phys = "adc-keys/input0";
+	input->dev.parent = &pdev->dev;
+
+	input->id.bustype = BUS_HOST;
+	input->id.vendor = 0x0001;
+	input->id.product = 0x0001;
+	input->id.version = 0x0100;
+
+	__set_bit(EV_KEY, input->evbit);
+	for (i = 0; i < st->num_keys; i++)
+		__set_bit(st->map[i].keycode, input->keybit);
+
+	if (!!of_get_property(node, "autorepeat", NULL))
+		__set_bit(EV_REP, input->evbit);
+
+	ret = input_register_polled_device(poll_dev);
+	if (ret) {
+		dev_err(dev, "Unable to register input device\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static const struct of_device_id adc_keys_of_match[] = {
+	{ .compatible = "adc-keys", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, adc_keys_of_match);
+
+static struct platform_driver __refdata adc_keys_driver = {
+	.driver = {
+		.name = "adc_keys",
+		.of_match_table = adc_keys_of_match,
+	},
+	.probe = adc_keys_probe,
+};
+
+module_platform_driver(adc_keys_driver);
+
+MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@free-electrons.com>");
+MODULE_DESCRIPTION("Input driver for resistor ladder connected on ADC");
+MODULE_LICENSE("GPL v2");
-- 
2.8.1

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH 2/2] input: add ADC resistor ladder driver
  2016-07-01 21:30 ` [PATCH 2/2] input: add ADC resistor ladder driver Alexandre Belloni
@ 2016-07-02 22:21   ` Matt Ranostay
  2016-07-03 13:50   ` Jonathan Cameron
  1 sibling, 0 replies; 7+ messages in thread
From: Matt Ranostay @ 2016-07-02 22:21 UTC (permalink / raw)
  To: Alexandre Belloni
  Cc: Dmitry Torokhov, Jonathan Cameron, Maxime Ripard, linux-input,
	linux-iio, linux-kernel

On Fri, Jul 1, 2016 at 2:30 PM, Alexandre Belloni
<alexandre.belloni@free-electrons.com> wrote:
> 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.
>
> Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
> ---
>  drivers/input/keyboard/Kconfig    |  15 +++
>  drivers/input/keyboard/Makefile   |   1 +
>  drivers/input/keyboard/adc-keys.c | 209 ++++++++++++++++++++++++++++++++++++++
>  3 files changed, 225 insertions(+)
>  create mode 100644 drivers/input/keyboard/adc-keys.c
>
> diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
> index 509608c95994..ba20808f31e0 100644
> --- a/drivers/input/keyboard/Kconfig
> +++ b/drivers/input/keyboard/Kconfig
> @@ -12,6 +12,21 @@ menuconfig INPUT_KEYBOARD
>
>  if INPUT_KEYBOARD
>
> +config KEYBOARD_ADC
> +       tristate "ADC ladder Buttons"
> +       depends on IIO || COMPILE_TEST

Why is this IIO or COMPILE_TEST? shouldn't it depend on both?

> +       select INPUT_POLLDEV
> +       help
> +         This driver implements support for buttons connected
> +         to an ADC using a resistor ladder.
> +
> +         Say Y here if your device has such buttons connected to an ADC.  Your
> +         board-specific setup logic must also provide a configuration data
> +         saying mapping voltages to buttons.
> +
> +         To compile this driver as a module, choose M here: the
> +         module will be called adc_keys.
> +
>  config KEYBOARD_ADP5520
>         tristate "Keypad Support for ADP5520 PMIC"
>         depends on PMIC_ADP5520
> diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
> index 1d416ddf84e4..d9f4cfcf3410 100644
> --- a/drivers/input/keyboard/Makefile
> +++ b/drivers/input/keyboard/Makefile
> @@ -4,6 +4,7 @@
>
>  # Each configuration option enables a list of files.
>
> +obj-$(CONFIG_KEYBOARD_ADC)             += adc-keys.o
>  obj-$(CONFIG_KEYBOARD_ADP5520)         += adp5520-keys.o
>  obj-$(CONFIG_KEYBOARD_ADP5588)         += adp5588-keys.o
>  obj-$(CONFIG_KEYBOARD_ADP5589)         += adp5589-keys.o
> diff --git a/drivers/input/keyboard/adc-keys.c b/drivers/input/keyboard/adc-keys.c
> new file mode 100644
> index 000000000000..ba6ee91272b2
> --- /dev/null
> +++ b/drivers/input/keyboard/adc-keys.c
> @@ -0,0 +1,209 @@
> +/* Input driver for resistor ladder connected on ADC
> + *
> + * Copyright (c) 2016 Alexandre Belloni
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License version 2 as published by
> + * the Free Software Foundation.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include <linux/module.h>
> +#include <linux/err.h>
> +#include <linux/input.h>
> +#include <linux/input-polldev.h>
> +#include <linux/iio/consumer.h>
> +#include <linux/iio/types.h>
> +#include <linux/platform_device.h>
> +#include <linux/of.h>
> +
> +struct adc_keys_button {
> +       u32 voltage;
> +       u32 keycode;
> +};
> +
> +struct adc_keys_state {
> +       struct iio_channel *channel;
> +       u32 num_keys;
> +       u32 last_key;
> +       u32 keyup_voltage;
> +       struct adc_keys_button *map;
> +};
> +
> +static void adc_keys_poll(struct input_polled_dev *dev)
> +{
> +       struct adc_keys_state *st = dev->private;
> +       int i, value, ret;
> +       u32 diff, closest = 0xffffffff;
> +       int keycode = 0;
> +
> +       ret = iio_read_channel_processed(st->channel, &value);
> +       if (ret < 0) {
> +               if (st->last_key) {
> +                       input_report_key(dev->input, st->last_key, 0);
> +                       input_sync(dev->input);
> +                       st->last_key = 0;
> +               }
> +               return;
> +       }
> +
> +       for (i = 0; i < st->num_keys; i++) {
> +               diff = abs(st->map[i].voltage - value);
> +               if (diff < closest) {
> +                       closest = diff;
> +                       keycode = st->map[i].keycode;
> +               }
> +       }
> +
> +       if (abs(st->keyup_voltage - value) < closest) {
> +               input_report_key(dev->input, st->last_key, 0);
> +               st->last_key = 0;
> +       } else {
> +               if (st->last_key && st->last_key != keycode)
> +                       input_report_key(dev->input, st->last_key, 0);
> +               input_report_key(dev->input, keycode, 1);
> +               st->last_key = keycode;
> +       }
> +
> +       input_sync(dev->input);
> +}
> +
> +static int adc_keys_load_dt_keymap(struct device *dev,
> +                                  struct adc_keys_state *st)
> +{
> +       struct device_node *pp, *np = dev->of_node;
> +       int i;
> +
> +       st->num_keys = of_get_child_count(np);
> +       if (st->num_keys == 0) {
> +               dev_err(dev, "keymap is missing\n");
> +               return -EINVAL;
> +       }
> +
> +       st->map = devm_kmalloc_array(dev, st->num_keys, sizeof(*st->map),
> +                                    GFP_KERNEL);
> +       if (!st->map)
> +               return -ENOMEM;
> +
> +       i = 0;
> +       for_each_child_of_node(np, pp) {
> +               struct adc_keys_button *map = &st->map[i];
> +
> +               if (of_property_read_u32(pp, "voltage-mvolt", &map->voltage)) {
> +                       dev_err(dev, "%s: Invalid or missing voltage\n",
> +                               pp->name);
> +                       return -EINVAL;
> +               }
> +
> +               if (of_property_read_u32(pp, "linux,code", &map->keycode)) {
> +                       dev_err(dev, "%s: Invalid or missing linux,code\n",
> +                               pp->name);
> +                       return -EINVAL;
> +               }
> +
> +               i++;
> +       }
> +
> +       return 0;
> +}
> +
> +static int adc_keys_probe(struct platform_device *pdev)
> +{
> +       struct device *dev = &pdev->dev;
> +       struct device_node *node = dev->of_node;
> +       struct adc_keys_state *st;
> +       struct input_polled_dev *poll_dev = NULL;
> +       struct input_dev *input;
> +       enum iio_chan_type type;
> +       int i, value, ret;
> +
> +       st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);
> +       if (!st)
> +               return -ENOMEM;
> +
> +       st->channel = devm_iio_channel_get(dev, "buttons");
> +       if (IS_ERR(st->channel))
> +               return PTR_ERR(st->channel);
> +
> +       if (!st->channel->indio_dev)
> +               return -ENODEV;
> +
> +       ret = iio_get_channel_type(st->channel, &type);
> +       if (ret < 0)
> +               return ret;
> +
> +       if (type != IIO_VOLTAGE) {
> +               dev_err(dev, "Incompatible channel type %d\n", type);
> +               return -EINVAL;
> +       }
> +
> +       if (of_property_read_u32(node, "voltage-keyup-mvolt",
> +                                &st->keyup_voltage)) {
> +               dev_err(dev, "Invalid or missing keyup voltage\n");
> +               return -EINVAL;
> +       }
> +
> +       ret = adc_keys_load_dt_keymap(dev, st);
> +       if (ret)
> +               return ret;
> +
> +       platform_set_drvdata(pdev, st);
> +
> +       poll_dev = devm_input_allocate_polled_device(dev);
> +       if (!poll_dev) {
> +               dev_err(dev, "failed to allocate input device\n");
> +               return -ENOMEM;
> +       }
> +
> +       if (!of_property_read_u32(node, "poll-interval", &value))
> +               poll_dev->poll_interval = value;
> +       poll_dev->poll = adc_keys_poll;
> +       poll_dev->private = st;
> +
> +       input = poll_dev->input;
> +
> +       input->name = pdev->name;
> +       input->phys = "adc-keys/input0";
> +       input->dev.parent = &pdev->dev;
> +
> +       input->id.bustype = BUS_HOST;
> +       input->id.vendor = 0x0001;
> +       input->id.product = 0x0001;
> +       input->id.version = 0x0100;
> +
> +       __set_bit(EV_KEY, input->evbit);
> +       for (i = 0; i < st->num_keys; i++)
> +               __set_bit(st->map[i].keycode, input->keybit);
> +
> +       if (!!of_get_property(node, "autorepeat", NULL))
> +               __set_bit(EV_REP, input->evbit);
> +
> +       ret = input_register_polled_device(poll_dev);
> +       if (ret) {
> +               dev_err(dev, "Unable to register input device\n");
> +               return ret;
> +       }
> +
> +       return 0;
> +}
> +
> +static const struct of_device_id adc_keys_of_match[] = {
> +       { .compatible = "adc-keys", },
> +       { }
> +};
> +MODULE_DEVICE_TABLE(of, adc_keys_of_match);
> +
> +static struct platform_driver __refdata adc_keys_driver = {
> +       .driver = {
> +               .name = "adc_keys",
> +               .of_match_table = adc_keys_of_match,
> +       },
> +       .probe = adc_keys_probe,
> +};
> +
> +module_platform_driver(adc_keys_driver);
> +
> +MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@free-electrons.com>");
> +MODULE_DESCRIPTION("Input driver for resistor ladder connected on ADC");
> +MODULE_LICENSE("GPL v2");
> --
> 2.8.1
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 1/2] input: adc-keys: add DT binding documentation
  2016-07-01 21:30 [PATCH 1/2] input: adc-keys: add DT binding documentation Alexandre Belloni
  2016-07-01 21:30 ` [PATCH 2/2] input: add ADC resistor ladder driver Alexandre Belloni
@ 2016-07-03 13:43 ` Jonathan Cameron
  2016-07-04 16:29   ` Alexandre Belloni
  2016-07-05 14:53   ` Rob Herring
  1 sibling, 2 replies; 7+ messages in thread
From: Jonathan Cameron @ 2016-07-03 13:43 UTC (permalink / raw)
  To: Alexandre Belloni, Dmitry Torokhov
  Cc: Maxime Ripard, linux-input, linux-iio, linux-kernel, Rob Herring,
	devicetree

On 01/07/16 22:30, Alexandre Belloni wrote:
> Add documentation for ADC keys
> 
> Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
Clean looking binding to me - one comment inline.

Jonathan
> ---
> Cc: Rob Herring <robh+dt@kernel.org>
> Cc: devicetree@vger.kernel.org
> 
>  .../devicetree/bindings/input/adc-keys.txt         | 45 ++++++++++++++++++++++
>  1 file changed, 45 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/input/adc-keys.txt
> 
> diff --git a/Documentation/devicetree/bindings/input/adc-keys.txt b/Documentation/devicetree/bindings/input/adc-keys.txt
> new file mode 100644
> index 000000000000..873476bb4eab
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/input/adc-keys.txt
> @@ -0,0 +1,45 @@
> +Allwinner sun4i low res adc attached tablet keys
What is allwinner specific in here?  Looks pretty general purpose to me!
> +------------------------------------------------
> +
> +Required properties:
> + - compatible: "adc-keys"
> + - io-channels: Phandle to an ADC channel
> + - io-channel-names = "buttons";
> + - voltage-keyup-mvolt: Voltage at which all the keys are considered up.
> +
> +Each key is represented as a sub-node of "adc-keys":
> +
> +Required subnode-properties:
> +	- label: Descriptive name of the key.
> +	- linux,code: Keycode to emit.
> +	- voltage-mvolt: Voltage adc input when this key is pressed.
> +
> +Example:
> +
> +#include <dt-bindings/input/input.h>
> +
> +	adc-keys {
> +		compatible = "adc-keys";
> +		io-channels = <&lradc 0>;
> +		io-channel-names = "buttons";
> +		voltage-keyup-mvolt = <2000>;
> +
> +		button@1500 {
> +			label = "Volume Up";
> +			linux,code = <KEY_VOLUMEUP>;
> +			voltage-mvolt = <1500>;
> +		};
> +
> +		button@1000 {
> +			label = "Volume Down";
> +			linux,code = <KEY_VOLUMEDOWN>;
> +			voltage-mvolt = <1000>;
> +		};
> +
> +		button@500 {
> +			label = "Enter";
> +			linux,code = <KEY_ENTER>;
> +			voltage-mvolt = <500>;
> +		};
> +	};
> +
> 

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 2/2] input: add ADC resistor ladder driver
  2016-07-01 21:30 ` [PATCH 2/2] input: add ADC resistor ladder driver Alexandre Belloni
  2016-07-02 22:21   ` Matt Ranostay
@ 2016-07-03 13:50   ` Jonathan Cameron
  1 sibling, 0 replies; 7+ messages in thread
From: Jonathan Cameron @ 2016-07-03 13:50 UTC (permalink / raw)
  To: Alexandre Belloni, Dmitry Torokhov
  Cc: Maxime Ripard, linux-input, linux-iio, linux-kernel

On 01/07/16 22:30, Alexandre Belloni wrote:
> 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.
> 
> Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
I like this a lot.  It's small elegant and does what it says on the tin.

Even when we do extend IIO to output threshold events, we'll still want the
polled version as there are plenty of ADCs out there without threshold
interrupts.

Next step add the iio event push support needed to interrupt drive this.
*cross fingers* :)

We may also want to do a full pushed mode at some point for self clocking
/ hardware sequencer equiped ADCs.  Easy enough to add when someone wants
it.

Jonathan
> ---
>  drivers/input/keyboard/Kconfig    |  15 +++
>  drivers/input/keyboard/Makefile   |   1 +
>  drivers/input/keyboard/adc-keys.c | 209 ++++++++++++++++++++++++++++++++++++++
>  3 files changed, 225 insertions(+)
>  create mode 100644 drivers/input/keyboard/adc-keys.c
> 
> diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
> index 509608c95994..ba20808f31e0 100644
> --- a/drivers/input/keyboard/Kconfig
> +++ b/drivers/input/keyboard/Kconfig
> @@ -12,6 +12,21 @@ menuconfig INPUT_KEYBOARD
>  
>  if INPUT_KEYBOARD
>  
> +config KEYBOARD_ADC
> +	tristate "ADC ladder Buttons"
> +	depends on IIO || COMPILE_TEST
> +	select INPUT_POLLDEV
> +	help
> +	  This driver implements support for buttons connected
> +	  to an ADC using a resistor ladder.
> +
> +	  Say Y here if your device has such buttons connected to an ADC.  Your
> +	  board-specific setup logic must also provide a configuration data
> +	  saying mapping voltages to buttons.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called adc_keys.
> +
>  config KEYBOARD_ADP5520
>  	tristate "Keypad Support for ADP5520 PMIC"
>  	depends on PMIC_ADP5520
> diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
> index 1d416ddf84e4..d9f4cfcf3410 100644
> --- a/drivers/input/keyboard/Makefile
> +++ b/drivers/input/keyboard/Makefile
> @@ -4,6 +4,7 @@
>  
>  # Each configuration option enables a list of files.
>  
> +obj-$(CONFIG_KEYBOARD_ADC)		+= adc-keys.o
>  obj-$(CONFIG_KEYBOARD_ADP5520)		+= adp5520-keys.o
>  obj-$(CONFIG_KEYBOARD_ADP5588)		+= adp5588-keys.o
>  obj-$(CONFIG_KEYBOARD_ADP5589)		+= adp5589-keys.o
> diff --git a/drivers/input/keyboard/adc-keys.c b/drivers/input/keyboard/adc-keys.c
> new file mode 100644
> index 000000000000..ba6ee91272b2
> --- /dev/null
> +++ b/drivers/input/keyboard/adc-keys.c
> @@ -0,0 +1,209 @@
> +/* Input driver for resistor ladder connected on ADC
> + *
> + * Copyright (c) 2016 Alexandre Belloni
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License version 2 as published by
> + * the Free Software Foundation.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include <linux/module.h>
> +#include <linux/err.h>
> +#include <linux/input.h>
> +#include <linux/input-polldev.h>
> +#include <linux/iio/consumer.h>
> +#include <linux/iio/types.h>
> +#include <linux/platform_device.h>
> +#include <linux/of.h>
> +
> +struct adc_keys_button {
> +	u32 voltage;
> +	u32 keycode;
> +};
> +
> +struct adc_keys_state {
> +	struct iio_channel *channel;
> +	u32 num_keys;
> +	u32 last_key;
> +	u32 keyup_voltage;
> +	struct adc_keys_button *map;
> +};
> +
> +static void adc_keys_poll(struct input_polled_dev *dev)
> +{
> +	struct adc_keys_state *st = dev->private;
> +	int i, value, ret;
> +	u32 diff, closest = 0xffffffff;
> +	int keycode = 0;
> +
> +	ret = iio_read_channel_processed(st->channel, &value);
> +	if (ret < 0) {
> +		if (st->last_key) {
> +			input_report_key(dev->input, st->last_key, 0);
> +			input_sync(dev->input);
> +			st->last_key = 0;
> +		}
> +		return;
> +	}
> +
> +	for (i = 0; i < st->num_keys; i++) {
> +		diff = abs(st->map[i].voltage - value);
> +		if (diff < closest) {
> +			closest = diff;
> +			keycode = st->map[i].keycode;
> +		}
> +	}
> +
> +	if (abs(st->keyup_voltage - value) < closest) {
> +		input_report_key(dev->input, st->last_key, 0);
> +		st->last_key = 0;
> +	} else {
> +		if (st->last_key && st->last_key != keycode)
> +			input_report_key(dev->input, st->last_key, 0);
> +		input_report_key(dev->input, keycode, 1);
> +		st->last_key = keycode;
> +	}
> +
> +	input_sync(dev->input);
> +}
> +
> +static int adc_keys_load_dt_keymap(struct device *dev,
> +				   struct adc_keys_state *st)
> +{
> +	struct device_node *pp, *np = dev->of_node;
> +	int i;
> +
> +	st->num_keys = of_get_child_count(np);
> +	if (st->num_keys == 0) {
> +		dev_err(dev, "keymap is missing\n");
> +		return -EINVAL;
> +	}
> +
> +	st->map = devm_kmalloc_array(dev, st->num_keys, sizeof(*st->map),
> +				     GFP_KERNEL);
> +	if (!st->map)
> +		return -ENOMEM;
> +
> +	i = 0;
> +	for_each_child_of_node(np, pp) {
> +		struct adc_keys_button *map = &st->map[i];
> +
> +		if (of_property_read_u32(pp, "voltage-mvolt", &map->voltage)) {
> +			dev_err(dev, "%s: Invalid or missing voltage\n",
> +				pp->name);
> +			return -EINVAL;
> +		}
> +
> +		if (of_property_read_u32(pp, "linux,code", &map->keycode)) {
> +			dev_err(dev, "%s: Invalid or missing linux,code\n",
> +				pp->name);
> +			return -EINVAL;
> +		}
> +
> +		i++;
> +	}
> +
> +	return 0;
> +}
> +
> +static int adc_keys_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct device_node *node = dev->of_node;
> +	struct adc_keys_state *st;
> +	struct input_polled_dev *poll_dev = NULL;
> +	struct input_dev *input;
> +	enum iio_chan_type type;
> +	int i, value, ret;
> +
> +	st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);
> +	if (!st)
> +		return -ENOMEM;
> +
> +	st->channel = devm_iio_channel_get(dev, "buttons");
> +	if (IS_ERR(st->channel))
> +		return PTR_ERR(st->channel);
> +
> +	if (!st->channel->indio_dev)
> +		return -ENODEV;
> +
> +	ret = iio_get_channel_type(st->channel, &type);
> +	if (ret < 0)
> +		return ret;
> +
> +	if (type != IIO_VOLTAGE) {
> +		dev_err(dev, "Incompatible channel type %d\n", type);
> +		return -EINVAL;
> +	}
> +
> +	if (of_property_read_u32(node, "voltage-keyup-mvolt",
> +				 &st->keyup_voltage)) {
> +		dev_err(dev, "Invalid or missing keyup voltage\n");
> +		return -EINVAL;
> +	}
> +
> +	ret = adc_keys_load_dt_keymap(dev, st);
> +	if (ret)
> +		return ret;
> +
> +	platform_set_drvdata(pdev, st);
> +
> +	poll_dev = devm_input_allocate_polled_device(dev);
> +	if (!poll_dev) {
> +		dev_err(dev, "failed to allocate input device\n");
> +		return -ENOMEM;
> +	}
> +
> +	if (!of_property_read_u32(node, "poll-interval", &value))
> +		poll_dev->poll_interval = value;
> +	poll_dev->poll = adc_keys_poll;
> +	poll_dev->private = st;
> +
> +	input = poll_dev->input;
> +
> +	input->name = pdev->name;
> +	input->phys = "adc-keys/input0";
> +	input->dev.parent = &pdev->dev;
> +
> +	input->id.bustype = BUS_HOST;
> +	input->id.vendor = 0x0001;
> +	input->id.product = 0x0001;
> +	input->id.version = 0x0100;
> +
> +	__set_bit(EV_KEY, input->evbit);
> +	for (i = 0; i < st->num_keys; i++)
> +		__set_bit(st->map[i].keycode, input->keybit);
> +
> +	if (!!of_get_property(node, "autorepeat", NULL))
> +		__set_bit(EV_REP, input->evbit);
> +
> +	ret = input_register_polled_device(poll_dev);
> +	if (ret) {
> +		dev_err(dev, "Unable to register input device\n");
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id adc_keys_of_match[] = {
> +	{ .compatible = "adc-keys", },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, adc_keys_of_match);
> +
> +static struct platform_driver __refdata adc_keys_driver = {
> +	.driver = {
> +		.name = "adc_keys",
> +		.of_match_table = adc_keys_of_match,
> +	},
> +	.probe = adc_keys_probe,
> +};
> +
> +module_platform_driver(adc_keys_driver);
> +
> +MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@free-electrons.com>");
> +MODULE_DESCRIPTION("Input driver for resistor ladder connected on ADC");
> +MODULE_LICENSE("GPL v2");
> 

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 1/2] input: adc-keys: add DT binding documentation
  2016-07-03 13:43 ` [PATCH 1/2] input: adc-keys: add DT binding documentation Jonathan Cameron
@ 2016-07-04 16:29   ` Alexandre Belloni
  2016-07-05 14:53   ` Rob Herring
  1 sibling, 0 replies; 7+ messages in thread
From: Alexandre Belloni @ 2016-07-04 16:29 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Dmitry Torokhov, Maxime Ripard, linux-input, linux-iio,
	linux-kernel, Rob Herring, devicetree

On 03/07/2016 at 14:43:56 +0100, Jonathan Cameron wrote :
> On 01/07/16 22:30, Alexandre Belloni wrote:
> > Add documentation for ADC keys
> > 
> > Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
> Clean looking binding to me - one comment inline.
> 
> Jonathan
> > ---
> > Cc: Rob Herring <robh+dt@kernel.org>
> > Cc: devicetree@vger.kernel.org
> > 
> >  .../devicetree/bindings/input/adc-keys.txt         | 45 ++++++++++++++++++++++
> >  1 file changed, 45 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/input/adc-keys.txt
> > 
> > diff --git a/Documentation/devicetree/bindings/input/adc-keys.txt b/Documentation/devicetree/bindings/input/adc-keys.txt
> > new file mode 100644
> > index 000000000000..873476bb4eab
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/input/adc-keys.txt
> > @@ -0,0 +1,45 @@
> > +Allwinner sun4i low res adc attached tablet keys
> What is allwinner specific in here?  Looks pretty general purpose to me!

That's true, I forgot to change that after shamelessly copy pasting...

I was planning to use something like ADC attached resistor ladder
buttons, unless you have a better way to describe that.


-- 
Alexandre Belloni, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 1/2] input: adc-keys: add DT binding documentation
  2016-07-03 13:43 ` [PATCH 1/2] input: adc-keys: add DT binding documentation Jonathan Cameron
  2016-07-04 16:29   ` Alexandre Belloni
@ 2016-07-05 14:53   ` Rob Herring
  1 sibling, 0 replies; 7+ messages in thread
From: Rob Herring @ 2016-07-05 14:53 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Alexandre Belloni, Dmitry Torokhov, Maxime Ripard, linux-input,
	linux-iio, linux-kernel, devicetree

On Sun, Jul 03, 2016 at 02:43:56PM +0100, Jonathan Cameron wrote:
> On 01/07/16 22:30, Alexandre Belloni wrote:
> > Add documentation for ADC keys
> > 
> > Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
> Clean looking binding to me - one comment inline.
> 
> Jonathan
> > ---
> > Cc: Rob Herring <robh+dt@kernel.org>
> > Cc: devicetree@vger.kernel.org
> > 
> >  .../devicetree/bindings/input/adc-keys.txt         | 45 ++++++++++++++++++++++
> >  1 file changed, 45 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/input/adc-keys.txt
> > 
> > diff --git a/Documentation/devicetree/bindings/input/adc-keys.txt b/Documentation/devicetree/bindings/input/adc-keys.txt
> > new file mode 100644
> > index 000000000000..873476bb4eab
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/input/adc-keys.txt
> > @@ -0,0 +1,45 @@
> > +Allwinner sun4i low res adc attached tablet keys
> What is allwinner specific in here?  Looks pretty general purpose to me!
> > +------------------------------------------------
> > +
> > +Required properties:
> > + - compatible: "adc-keys"
> > + - io-channels: Phandle to an ADC channel
> > + - io-channel-names = "buttons";
> > + - voltage-keyup-mvolt: Voltage at which all the keys are considered up.
> > +
> > +Each key is represented as a sub-node of "adc-keys":
> > +
> > +Required subnode-properties:
> > +	- label: Descriptive name of the key.
> > +	- linux,code: Keycode to emit.
> > +	- voltage-mvolt: Voltage adc input when this key is pressed.

voltage-mcvoltface?

"voltage" is redundant and use standard units. How about 
{press,up}-threshold-microvolt?

Rob

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2016-07-05 14:53 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-07-01 21:30 [PATCH 1/2] input: adc-keys: add DT binding documentation Alexandre Belloni
2016-07-01 21:30 ` [PATCH 2/2] input: add ADC resistor ladder driver Alexandre Belloni
2016-07-02 22:21   ` Matt Ranostay
2016-07-03 13:50   ` Jonathan Cameron
2016-07-03 13:43 ` [PATCH 1/2] input: adc-keys: add DT binding documentation Jonathan Cameron
2016-07-04 16:29   ` Alexandre Belloni
2016-07-05 14:53   ` Rob Herring

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).