diff mbox

[v3,1/2] input: Add new driver for ARM CLPS711X keypad

Message ID 1395165739-17378-1-git-send-email-shc_work@mail.ru (mailing list archive)
State New, archived
Headers show

Commit Message

Alexander Shiyan March 18, 2014, 6:02 p.m. UTC
This patch adds a new driver for keypad for Cirrus Logic CLPS711X CPUs.
Target CPU contain keyboard interface which can scan 8 column lines,
so we can read row GPIOs to read status and determine asserted state.

Signed-off-by: Alexander Shiyan <shc_work@mail.ru>
---
 drivers/input/keyboard/Kconfig           |  12 ++
 drivers/input/keyboard/Makefile          |   1 +
 drivers/input/keyboard/clps711x-keypad.c | 209 +++++++++++++++++++++++++++++++
 3 files changed, 222 insertions(+)
 create mode 100644 drivers/input/keyboard/clps711x-keypad.c

Comments

Dmitry Torokhov March 28, 2014, 4:42 p.m. UTC | #1
Hi Alexander,

On Tue, Mar 18, 2014 at 10:02:19PM +0400, Alexander Shiyan wrote:
> +	err = input_register_polled_device(poll_dev);
> +	if (!err) {
> +		/* Report initial state */
> +		clps711x_keypad_poll(poll_dev);
> +
> +		return 0;
> +	}

The polled device core will report initial state when input device is
opened (as long as polling is enabled) so I removed this cunk and
applied the rest.

Thanks.
Alexander Shiyan March 28, 2014, 4:46 p.m. UTC | #2
Fri, 28 Mar 2014 09:42:28 -0700 ?? Dmitry Torokhov <dmitry.torokhov@gmail.com>:
> Hi Alexander,
> 
> On Tue, Mar 18, 2014 at 10:02:19PM +0400, Alexander Shiyan wrote:
> > +	err = input_register_polled_device(poll_dev);
> > +	if (!err) {
> > +		/* Report initial state */
> > +		clps711x_keypad_poll(poll_dev);
> > +
> > +		return 0;
> > +	}
> 
> The polled device core will report initial state when input device is
> opened (as long as polling is enabled) so I removed this cunk and
> applied the rest.

Today I stumbled with race condition with this, so your solution is OK.
Thanks.

---
Dmitry Torokhov March 31, 2014, 1:19 a.m. UTC | #3
On Monday, March 31, 2014 12:19:35 AM Alexander Shiyan wrote:
> Fri, 28 Mar 2014 09:42:28 -0700 ?? Dmitry Torokhov 
<dmitry.torokhov@gmail.com>:
> > Hi Alexander,
> > 
> > On Tue, Mar 18, 2014 at 10:02:19PM +0400, Alexander Shiyan wrote:
> > > +	err = input_register_polled_device(poll_dev);
> > > +	if (!err) {
> > > +		/* Report initial state */
> > > +		clps711x_keypad_poll(poll_dev);
> > > +
> > > +		return 0;
> > > +	}
> > 
> > The polled device core will report initial state when input device is
> > opened (as long as polling is enabled) so I removed this cunk and
> > applied the rest.
> 
> I looked amended version, it is made incorrectly.
> ...
> + err = input_register_polled_device(poll_dev);
> + if (err)
> + goto out_err;
> +
> +out_err:
> + input_free_polled_device(poll_dev);
> ...
> 
> Should be:
>  err = input_register_polled_device(poll_dev);
>  if (!err)
>  return 0;

Yes, I missed that "return 0;" should be fixed now.
diff mbox

Patch

diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index a673c9f..45274e3 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -151,6 +151,18 @@  config KEYBOARD_BFIN
 	  To compile this driver as a module, choose M here: the
 	  module will be called bf54x-keys.
 
+config KEYBOARD_CLPS711X
+	tristate "CLPS711X Keypad support"
+	depends on OF_GPIO && (ARCH_CLPS711X || COMPILE_TEST)
+	select INPUT_MATRIXKMAP
+	select INPUT_POLLDEV
+	help
+	  Say Y here to enable the matrix keypad on the Cirrus Logic
+	  CLPS711X CPUs.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called clps711x-keypad.
+
 config KEYBOARD_LKKBD
 	tristate "DECstation/VAXstation LK201/LK401 keyboard"
 	select SERIO
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index a699b61..f8589fe 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -11,6 +11,7 @@  obj-$(CONFIG_KEYBOARD_AMIGA)		+= amikbd.o
 obj-$(CONFIG_KEYBOARD_ATARI)		+= atakbd.o
 obj-$(CONFIG_KEYBOARD_ATKBD)		+= atkbd.o
 obj-$(CONFIG_KEYBOARD_BFIN)		+= bf54x-keys.o
+obj-$(CONFIG_KEYBOARD_CLPS711X)		+= clps711x-keypad.o
 obj-$(CONFIG_KEYBOARD_CROS_EC)		+= cros_ec_keyb.o
 obj-$(CONFIG_KEYBOARD_DAVINCI)		+= davinci_keyscan.o
 obj-$(CONFIG_KEYBOARD_EP93XX)		+= ep93xx_keypad.o
diff --git a/drivers/input/keyboard/clps711x-keypad.c b/drivers/input/keyboard/clps711x-keypad.c
new file mode 100644
index 0000000..51c4dd7
--- /dev/null
+++ b/drivers/input/keyboard/clps711x-keypad.c
@@ -0,0 +1,209 @@ 
+/*
+ * Cirrus Logic CLPS711X Keypad driver
+ *
+ * Copyright (C) 2014 Alexander Shiyan <shc_work@mail.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/input.h>
+#include <linux/input-polldev.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/sched.h>
+#include <linux/input/matrix_keypad.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mfd/syscon/clps711x.h>
+
+#define CLPS711X_KEYPAD_COL_COUNT	8
+
+struct clps711x_gpio_data {
+	struct gpio_desc *desc;
+	DECLARE_BITMAP(last_state, CLPS711X_KEYPAD_COL_COUNT);
+};
+
+struct clps711x_keypad_data {
+	struct regmap			*syscon;
+	int				row_count;
+	unsigned int			row_shift;
+	struct clps711x_gpio_data	*gpio_data;
+};
+
+static void clps711x_keypad_poll(struct input_polled_dev *dev)
+{
+	const unsigned short *keycodes = dev->input->keycode;
+	struct clps711x_keypad_data *priv = dev->private;
+	bool sync = false;
+	int col, row;
+
+	for (col = 0; col < CLPS711X_KEYPAD_COL_COUNT; col++) {
+		/* Assert column */
+		regmap_update_bits(priv->syscon, SYSCON_OFFSET,
+				   SYSCON1_KBDSCAN_MASK,
+				   SYSCON1_KBDSCAN(8 + col));
+
+		/* Scan rows */
+		for (row = 0; row < priv->row_count; row++) {
+			struct clps711x_gpio_data *data = &priv->gpio_data[row];
+			bool state, state1;
+
+			/* Read twice for protection against fluctuations */
+			do {
+				state = gpiod_get_value_cansleep(data->desc);
+				cond_resched();
+				state1 = gpiod_get_value_cansleep(data->desc);
+			} while (state != state1);
+
+			if (test_bit(col, data->last_state) != state) {
+				int code = MATRIX_SCAN_CODE(row, col,
+							    priv->row_shift);
+
+				if (state) {
+					set_bit(col, data->last_state);
+					input_event(dev->input, EV_MSC,
+						    MSC_SCAN, code);
+				} else {
+					clear_bit(col, data->last_state);
+				}
+
+				if (keycodes[code])
+					input_report_key(dev->input,
+							 keycodes[code], state);
+				sync = true;
+			}
+		}
+
+		/* Set all columns to low */
+		regmap_update_bits(priv->syscon, SYSCON_OFFSET,
+				   SYSCON1_KBDSCAN_MASK, SYSCON1_KBDSCAN(1));
+	}
+
+	if (sync)
+		input_sync(dev->input);
+}
+
+static int clps711x_keypad_probe(struct platform_device *pdev)
+{
+	struct clps711x_keypad_data *priv;
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+	struct input_polled_dev *poll_dev;
+	u32 poll_interval;
+	int i, err;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->syscon =
+		syscon_regmap_lookup_by_compatible("cirrus,clps711x-syscon1");
+	if (IS_ERR(priv->syscon))
+		return PTR_ERR(priv->syscon);
+
+	priv->row_count = of_gpio_named_count(np, "row-gpios");
+	if (priv->row_count < 1)
+		return -EINVAL;
+
+	priv->gpio_data = devm_kzalloc(dev, sizeof(*priv->gpio_data) *
+				       priv->row_count, GFP_KERNEL);
+	if (!priv->gpio_data)
+		return -ENOMEM;
+
+	priv->row_shift = get_count_order(CLPS711X_KEYPAD_COL_COUNT);
+
+	for (i = 0; i < priv->row_count; i++) {
+		struct clps711x_gpio_data *data = &priv->gpio_data[i];
+
+		data->desc = devm_gpiod_get_index(dev, "row", i);
+		if (!data->desc)
+			return -EINVAL;
+
+		if (IS_ERR(data->desc))
+			return PTR_ERR(data->desc);
+
+		gpiod_direction_input(data->desc);
+	}
+
+	err = of_property_read_u32(np, "poll-interval", &poll_interval);
+	if (err)
+		return err;
+
+	poll_dev = input_allocate_polled_device();
+	if (!poll_dev)
+		return -ENOMEM;
+
+	poll_dev->private		= priv;
+	poll_dev->poll			= clps711x_keypad_poll;
+	poll_dev->poll_interval		= poll_interval;
+	poll_dev->input->name		= pdev->name;
+	poll_dev->input->dev.parent	= dev;
+	poll_dev->input->id.bustype	= BUS_HOST;
+	poll_dev->input->id.vendor	= 0x0001;
+	poll_dev->input->id.product	= 0x0001;
+	poll_dev->input->id.version	= 0x0100;
+
+	err = matrix_keypad_build_keymap(NULL, NULL, priv->row_count,
+					 CLPS711X_KEYPAD_COL_COUNT,
+					 NULL, poll_dev->input);
+	if (err)
+		goto out_err;
+
+	input_set_capability(poll_dev->input, EV_MSC, MSC_SCAN);
+	if (of_property_read_bool(np, "autorepeat"))
+		__set_bit(EV_REP, poll_dev->input->evbit);
+
+	platform_set_drvdata(pdev, poll_dev);
+
+	/* Set all columns to low */
+	regmap_update_bits(priv->syscon, SYSCON_OFFSET, SYSCON1_KBDSCAN_MASK,
+			   SYSCON1_KBDSCAN(1));
+
+	err = input_register_polled_device(poll_dev);
+	if (!err) {
+		/* Report initial state */
+		clps711x_keypad_poll(poll_dev);
+
+		return 0;
+	}
+
+out_err:
+	input_free_polled_device(poll_dev);
+
+	return err;
+}
+
+static int clps711x_keypad_remove(struct platform_device *pdev)
+{
+	struct input_polled_dev *poll_dev = platform_get_drvdata(pdev);
+
+	input_unregister_polled_device(poll_dev);
+	input_free_polled_device(poll_dev);
+
+	return 0;
+}
+
+static struct of_device_id clps711x_keypad_of_match[] = {
+	{ .compatible = "cirrus,clps711x-keypad", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, clps711x_keypad_of_match);
+
+static struct platform_driver clps711x_keypad_driver = {
+	.driver	= {
+		.name		= "clps711x-keypad",
+		.owner		= THIS_MODULE,
+		.of_match_table	= clps711x_keypad_of_match,
+	},
+	.probe	= clps711x_keypad_probe,
+	.remove	= clps711x_keypad_remove,
+};
+module_platform_driver(clps711x_keypad_driver);
+
+MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");
+MODULE_DESCRIPTION("Cirrus Logic CLPS711X Keypad driver");
+MODULE_LICENSE("GPL");