All of lore.kernel.org
 help / color / mirror / Atom feed
From: <Gireesh.Hiremath@in.bosch.com>
To: <linux-omap@vger.kernel.org>, <devicetree@vger.kernel.org>,
	<linux-kernel@vger.kernel.org>, <linux-input@vger.kernel.org>,
	<bcousson@baylibre.com>, <tony@atomide.com>, <robh+dt@kernel.org>,
	<krzysztof.kozlowski+dt@linaro.org>, <dmitry.torokhov@gmail.com>,
	<mkorpershoek@baylibre.com>, <davidgow@google.com>,
	<m.felsch@pengutronix.de>, <swboyd@chromium.org>,
	<fengping.yu@mediatek.com>, <y.oudjana@protonmail.com>,
	<rdunlap@infradead.org>, <colin.king@intel.com>,
	<Gireesh.Hiremath@in.bosch.com>
Cc: <sjoerd.simons@collabora.co.uk>,
	<VinayKumar.Shettar@in.bosch.com>,
	<Govindaraji.Sivanantham@in.bosch.com>,
	<anaclaudia.dias@de.bosch.com>
Subject: [PATCH v2 2/4] Input: mt-matrix-keypad: Add Bosch mt matrix keypad driver
Date: Fri, 6 May 2022 07:27:35 +0000	[thread overview]
Message-ID: <20220506072737.1590-2-Gireesh.Hiremath@in.bosch.com> (raw)
In-Reply-To: <20220506072737.1590-1-Gireesh.Hiremath@in.bosch.com>

From: Gireesh Hiremath <Gireesh.Hiremath@in.bosch.com>

add support for keypad driver running on Bosch Guardian
Dtect board using TI-am335x cpu. Driver implementation
is based on matrix_keypad.c

Signed-off-by: Gireesh Hiremath <Gireesh.Hiremath@in.bosch.com>
---
Hi Marco,

Changes since v1: resolved compilation warnings

>On 22-05-04, Gireesh.Hiremath@in.bosch.com wrote:
>> From: Gireesh Hiremath <Gireesh.Hiremath@in.bosch.com>
>>
>> The existing matric_keypad.c use different gpio line for row and colunm,
>> where in mt_matrix_kepad.c use same gpio line for row as well as column.
>> a key can be placed at each intersection of a unique row number
>> not equal to a unique column and they are diagonally symmetric.
>> Advantage of this is with existed gpio line we can get more keys
>>
>> example: in matrix_keypad.c for 5 gpio line possible matrix is 2X3 or 3X2
>> and maximum possible keys are 6 but
>> in mt_matrix_kepad.c for same 5 gpio line possible matrix is 5X5 and maximum
>> possible buttons are 10, below table will discribe that
>
>Nobody should stop you to increase the amount of max. possible keys, so
>this isn't a real block.
>> 	------------------------------------------------------
>> 	|Row\Col |GPIO 0 | GPIO 1 | GPIO 2 | GPIO 3 | GPIO 4 |
>> 	------------------------------------------------------
>> 	| GPIO 0 |  X    | KEY_9  | KEY_2  | KEY_3  | KEY_1  |
>> 	------------------------------------------------------
>> 	| GPIO 1 | KEY_9 |  X     | KEY_6  | KEY_5  |  KEY_0 |
>> 	------------------------------------------------------
>> 	| GPIO 2 | KEY_2 | KEY_6  |  X     | KEY_4  | KEY_7  |
>> 	------------------------------------------------------
>> 	| GPIO 3 | KEY_3 | KEY_5  | KEY_4  |  X     | KEY_8  |
>> 	------------------------------------------------------
>> 	| GPIO 4 | KEY_1 |  KEY_0 | KEY_7  | KEY_8  |  X     |
>> 	------------------------------------------------------
>> 	X - invalid key
>> 	KEY_x - preferred key code
>
>That should be pointed somewhere very clearly, thanks for the
>description. Also what is than the benefit of the original matrix_keypad
>driver?

we have special keypad for Bosch measuring tools, which is not completely
matric keypad so we have derived from matrix_kepad.c to make our keypad
to work.

In this keypad there may or may not have keymap for all gpio line
as below

	------------------------------------------------------
	|Row\Col |GPIO 0 | GPIO 1 | GPIO 2 | GPIO 3 | GPIO 4 |
	------------------------------------------------------
	| GPIO 0 |  X    | KEY_9  | KEY_2  |  X     | KEY_1  |
	------------------------------------------------------
	| GPIO 1 | KEY_9 |  X     | KEY_6  |  X     |   X    |
	------------------------------------------------------
	| GPIO 2 | KEY_2 | KEY_6  |  X     | KEY_4  | KEY_7  |
	------------------------------------------------------
	| GPIO 3 |  X    |  X     | KEY_4  |  X     | KEY_8  |
	------------------------------------------------------
	| GPIO 4 | KEY_1 |  X     | KEY_7  | KEY_8  |  X     |
	------------------------------------------------------
   X - invalid key

example DTS

    line-gpios = <
             &gpio1 24 1     /*gpio_56*/
             &gpio1 23 1     /*gpio_55*/
             &gpio1 22 1     /*gpio_54*/
             &gpio1 20 1     /*gpio_52*/
             &gpio1 16 1     /*gpio_48*/
     >;
     linux,keymap = <
             0x00000000 /* row 0, col 0, KEY_RESERVED */
             0x0001000a /* row 0, col 1, KEY_9 */
             0x00020003 /* row 0, col 2, KEY_2 */
             0x00030000 /* row 0, col 3, KEY_RESERVED */
             0x00040002 /* row 0, col 4, KEY_1 */
             0x0100000a /* row 1, col 0, KEY_9 */
             0x01010000 /* row 1, col 1, KEY_RESERVED */
             0x01020007 /* row 1, col 2, KEY_6 */
             0x01030000 /* row 1, col 3, KEY_RESERVED */
             0x01040000 /* row 1, col 4, KEY_RESERVED */
             0x02000003 /* row 2, col 0, KEY_2 */
             0x02010007 /* row 2, col 1, KEY_6 */
             0x02020000 /* row 2, col 2, KEY_RESERVED */
             0x02030005 /* row 2, col 3, KEY_4 */
             0x02040008 /* row 2, col 4, KEY_7 */
             0x03000000 /* row 3, col 0, KEY_RESERVED */
             0x03010000 /* row 3, col 1, KEY_RESERVED */
             0x03020005 /* row 3, col 2, KEY_4 */
             0x03030000 /* row 3, col 3, KEY_RESERVED */
             0x03040009 /* row 3, col 4, KEY_8 */
             0x04000002 /* row 4, col 0, KEY_1 */
             0x04010000 /* row 4, col 1, KEY_RESERVED */
             0x04020008 /* row 4, col 2, KEY_7 */
             0x04030009 /* row 4, col 3, KEY_8 */
             0x04040000 /* row 4, col 4, KEY_RESERVED */
     >;

>
>> both matric_keypad.c and mt_matrix_kepad.c logically operate differently,
>> my openion is not to merge both.
>
>IMHO from the user/system-integrator pov it is looking the same and so
>one driver should be fine. To distinguish between both modes we could
>add dt-property or add a new dt-compatible like "gpio-matrix-keypad-v2".
>

as mentioned above our keypad is not complete matrix keypad  and it will
not be compatible with matrix_keypad diver. that is the reason we derived
mt matrix keypad driver.

to avoid confusion, we will rename the driver as bosch_mt_keypad.c
if you suggest.

>Regards,
>  Marco

Regards
Gireesh Hiremath

 drivers/input/keyboard/Kconfig            |  10 +
 drivers/input/keyboard/Makefile           |   1 +
 drivers/input/keyboard/mt_matrix_keypad.c | 741 ++++++++++++++++++++++
 include/linux/input/mt_matrix_keypad.h    |  85 +++
 4 files changed, 837 insertions(+)
 create mode 100644 drivers/input/keyboard/mt_matrix_keypad.c
 create mode 100644 include/linux/input/mt_matrix_keypad.h

diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index 4ea79db8f134..a55ee8656194 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -303,6 +303,16 @@ config KEYBOARD_MATRIX
 	  To compile this driver as a module, choose M here: the
 	  module will be called matrix_keypad.
 
+config KEYBOARD_MT_MATRIX
+        tristate "GPIO driven MT matrix keypad support"
+        depends on GPIOLIB || COMPILE_TEST
+        help
+	  This driver enable support for GPIO driven
+	  mt matrix keypad.
+
+          To compile this driver as a module, choose M here: the
+          module will be called mt_matrix_keypad.
+
 config KEYBOARD_HIL_OLD
 	tristate "HP HIL keyboard support (simple driver)"
 	depends on GSC || HP300
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index 721936e90290..c7686d338b5d 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -41,6 +41,7 @@ obj-$(CONFIG_KEYBOARD_LOCOMO)		+= locomokbd.o
 obj-$(CONFIG_KEYBOARD_LPC32XX)		+= lpc32xx-keys.o
 obj-$(CONFIG_KEYBOARD_MAPLE)		+= maple_keyb.o
 obj-$(CONFIG_KEYBOARD_MATRIX)		+= matrix_keypad.o
+obj-$(CONFIG_KEYBOARD_MT_MATRIX)	+= mt_matrix_keypad.o
 obj-$(CONFIG_KEYBOARD_MAX7359)		+= max7359_keypad.o
 obj-$(CONFIG_KEYBOARD_MCS)		+= mcs_touchkey.o
 obj-$(CONFIG_KEYBOARD_MPR121)		+= mpr121_touchkey.o
diff --git a/drivers/input/keyboard/mt_matrix_keypad.c b/drivers/input/keyboard/mt_matrix_keypad.c
new file mode 100644
index 000000000000..2664ce3c2653
--- /dev/null
+++ b/drivers/input/keyboard/mt_matrix_keypad.c
@@ -0,0 +1,741 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *  GPIO driven mt matrix keyboard driver
+ *
+ *  Copyright (c) 2008 Marek Vasut <marek.vasut@gmail.com>
+ *  Copyright (c) 2017 vinay <VinayKumar.Shettar@in.bosch.com>
+ *
+ *  Based on matrix_keypad.c
+ *
+ */
+
+#include <linux/stddef.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/gpio.h>
+#include <linux/input/mt_matrix_keypad.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/of_platform.h>
+#include <linux/pinctrl/consumer.h>
+
+#define MODULE_NAME "mt-matrix-keypad"
+
+struct mt_matrix_keypad {
+	struct mt_matrix_keypad_platform_data *pdata;
+	struct input_dev *input_dev;
+
+	DECLARE_BITMAP(disabled_gpios, MATRIX_MAX_ROWS);
+
+	struct delayed_work work;
+	spinlock_t lock;
+	bool scan_pending;
+	bool stopped;
+	bool gpio_all_disabled;
+};
+
+static bool
+get_gpio_line_value(const struct mt_matrix_keypad_platform_data *pdata,
+		    int row);
+static void
+activate_line_driving(const struct mt_matrix_keypad_platform_data *pdata,
+		      int line, bool on);
+
+void init_phase(struct mt_matrix_keypad_platform_data *pdata)
+{
+	pdata->phase_state.phase_prepare = -1;
+	pdata->phase_state.phase_start = 0;
+	pdata->phase_state.phase_update_button = pdata->num_line_gpios;
+}
+
+void button_init(struct button *btn, bool btn_hw_state, int key)
+{
+	btn->state.boPrevious = btn_hw_state;
+	btn->state.boCurrentStateOfHw = btn_hw_state;
+	btn->state.boCurrentStateOfSw = false;
+	btn->state.boCurrent = btn_hw_state;
+	btn->state.boEnabled = true;
+	btn->state.boStateAtInit = btn_hw_state;
+	btn->event.ui8Register = 0;
+	btn->key = key;
+}
+
+struct button_states get_button_state(struct button *btn)
+{
+	return btn->state;
+}
+
+union typeEvent get_and_clear_events(struct button *btn)
+{
+	union typeEvent beTemp = btn->event;
+
+	btn->event.ui8Register = 0;
+
+	return beTemp;
+}
+
+uint8_t get_btn_index(struct mt_matrix_keypad_platform_data *pdata, int btn_key)
+{
+	uint8_t i;
+
+	for (i = 0; i < pdata->num_of_buttons; i++) {
+		if (pdata->button_array[i].key == btn_key)
+			break;
+	}
+	return i;
+}
+
+void set_btn_state_by_hw(struct button *btn, bool boButtonState)
+{
+	btn->state.boCurrentStateOfHw = boButtonState;
+}
+
+bool check_button_changes(struct button *btn)
+{
+	btn->state.boPrevious = btn->state.boCurrent;
+	btn->state.boCurrent =
+		btn->state.boCurrentStateOfHw || btn->state.boCurrentStateOfSw;
+
+	/* Check if Button is pressed */
+	if ((btn->state.boPrevious == false) &&
+	    (btn->state.boCurrent == true)) {
+		btn->event.status.boPressed = true;
+	}
+
+	/* Check if Button is released */
+	else if ((btn->state.boPrevious == true) &&
+		 (btn->state.boCurrent == false)) {
+		btn->event.status.boReleased = true;
+	}
+
+	if (btn->event.ui8Register != 0)
+		btn->event.status.boGlobalChanged = true;
+
+	return btn->event.status.boGlobalChanged;
+}
+
+struct button_states
+get_btn_id_state(const struct mt_matrix_keypad_platform_data *pdata,
+		 int btn_index)
+{
+	if (btn_index < pdata->num_of_buttons)
+		return get_button_state(&pdata->button_array[btn_index]);
+	else
+		return get_button_state(&pdata->button_array[0]);
+}
+
+union typeEvent
+get_and_clear_btn_events(const struct mt_matrix_keypad_platform_data *pdata,
+			 int btn_index)
+{
+	if (btn_index < pdata->num_of_buttons)
+		return get_and_clear_events(&pdata->button_array[btn_index]);
+	else
+		return get_and_clear_events(&pdata->button_array[0]);
+}
+
+void button_hdl_init(struct mt_matrix_keypad_platform_data *pdata)
+{
+	int row, col, index;
+	int i;
+
+	pdata->scan_phase = pdata->phase_state.phase_prepare;
+	pdata->intialize_buttons = true;
+
+	/* Init Button Objects, will be reinited once states are captured */
+	i = 0;
+	for (row = 1; row < pdata->num_line_gpios; row++)
+		for (col = 0; col < row; col++) {
+			index = (row * pdata->num_line_gpios) + col;
+			if (pdata->button_matrix[index] !=
+			    pdata->button_matrix[0]) {
+				if (i < pdata->num_of_buttons) {
+					button_init(
+						&pdata->button_array[i], false,
+						pdata->button_matrix[index]);
+					i++;
+				}
+			}
+		}
+
+	pr_debug("[%s]: %s Done\n", MODULE_NAME, __func__);
+}
+
+bool on_button_event(const struct mt_matrix_keypad_platform_data *pdata,
+		     int btn_index, union typeEvent btn_event,
+		     struct input_dev *input_dev)
+{
+	bool any_btn_served = true;
+	unsigned int key_code = 0;
+	int key_value = 0;
+
+	key_code = pdata->button_array[btn_index].key;
+
+	if (btn_event.status.boPressed) {
+		key_value = 1;
+		pr_debug("[%s]:%d Pressed\n", MODULE_NAME, key_code);
+	}
+
+	if (btn_event.status.boReleased) {
+		key_value = 0;
+		pr_debug("[%s]:%d Released\n", MODULE_NAME, key_code);
+	}
+
+	input_report_key(input_dev, key_code, key_value);
+	input_sync(input_dev);
+	return any_btn_served;
+}
+
+void process_button_events(const struct mt_matrix_keypad_platform_data *pdata,
+			   struct input_dev *input_dev)
+{
+	int btn_index;
+	bool any_btn_served = false;
+
+	for (btn_index = 0; btn_index < pdata->num_of_buttons; btn_index++) {
+		const union typeEvent beEvent =
+			get_and_clear_btn_events(pdata, (int)btn_index);
+
+		if (beEvent.status.boGlobalChanged) {
+			const struct button_states bsState =
+				get_btn_id_state(pdata, (int)btn_index);
+
+			if (bsState.boEnabled) {
+				any_btn_served |=
+					on_button_event(pdata, (int)btn_index,
+							beEvent, input_dev);
+			}
+		}
+	}
+}
+
+void update_buttons(struct mt_matrix_keypad_platform_data *pdata,
+		    struct input_dev *input_dev)
+{
+	if (pdata->scan_phase == pdata->phase_state.phase_prepare) {
+		pdata->scan_phase = pdata->phase_state.phase_start;
+		activate_line_driving(pdata, (int)pdata->scan_phase, true);
+	} else if (pdata->scan_phase ==
+		   pdata->phase_state.phase_update_button) {
+		bool btn_changes_occured = false;
+		int btn_index;
+
+		if (pdata->intialize_buttons) {
+			int i;
+
+			pdata->intialize_buttons = false;
+
+			for (i = 0; i < pdata->num_of_buttons; i++) {
+				const bool btn_curr_hw_state =
+					get_button_state(
+						&pdata->button_array[i])
+						.boCurrentStateOfHw;
+				button_init(&pdata->button_array[i],
+					    btn_curr_hw_state,
+					    pdata->button_array[i].key);
+			}
+		}
+
+		for (btn_index = 0; btn_index < pdata->num_of_buttons;
+		     btn_index++) {
+			btn_changes_occured |= check_button_changes(
+				&pdata->button_array[btn_index]);
+		}
+
+		if (btn_changes_occured)
+			process_button_events(pdata, input_dev);
+
+		pdata->scan_phase = pdata->phase_state.phase_start;
+	} else {
+		uint8_t *btn_keylines;
+		uint8_t number_of_buttons_pressed = 0;
+		uint8_t btn_index;
+		uint8_t btn_key;
+		uint16_t index;
+		int i;
+
+		btn_keylines = kcalloc(pdata->num_line_gpios, sizeof(uint8_t),
+				       GFP_KERNEL);
+		for (i = 0; i < pdata->num_line_gpios; i++) {
+			index = (pdata->scan_phase * pdata->num_line_gpios) + i;
+			btn_key = pdata->button_matrix[index];
+			btn_keylines[i] = false;
+
+			if ((btn_key != pdata->button_matrix[0]) &&
+			    (get_gpio_line_value(pdata, (int)i) != false)) {
+				btn_keylines[i] = true;
+				number_of_buttons_pressed++;
+			}
+		}
+		if (number_of_buttons_pressed < 2) {
+			for (i = 0; i < pdata->num_line_gpios; i++) {
+				index = (pdata->scan_phase *
+					 pdata->num_line_gpios) +
+					i;
+				btn_key = pdata->button_matrix[index];
+				if (btn_key != pdata->button_matrix[0]) {
+					btn_index =
+						get_btn_index(pdata, btn_key);
+					set_btn_state_by_hw(
+						&pdata->button_array[btn_index],
+						btn_keylines[i]);
+				}
+			}
+		}
+
+		kfree(btn_keylines);
+		activate_line_driving(pdata, (int)pdata->scan_phase, false);
+		pdata->scan_phase++;
+		activate_line_driving(
+			pdata, (int)(pdata->scan_phase % pdata->num_line_gpios),
+			true);
+	}
+}
+
+/*
+ * NOTE: normally the GPIO has to be put into HiZ when de-activated to cause
+ * minmal side effect when scanning other columns, here it is configured to
+ * be input, and it should work on most platforms.
+ */
+static void
+__activate_line_driving(const struct mt_matrix_keypad_platform_data *pdata,
+			int line, bool on)
+{
+	bool level_on = pdata->active_low;
+
+	if (on)
+		gpio_direction_output(pdata->line_gpios[line], level_on);
+	else
+		gpio_direction_input(pdata->line_gpios[line]);
+}
+
+static void
+activate_line_driving(const struct mt_matrix_keypad_platform_data *pdata,
+		      int line, bool on)
+{
+	__activate_line_driving(pdata, line, on);
+
+	if (on && pdata->col_scan_delay_us)
+		udelay(pdata->col_scan_delay_us);
+}
+
+static bool
+get_gpio_line_value(const struct mt_matrix_keypad_platform_data *pdata, int row)
+{
+	return gpio_get_value(pdata->line_gpios[row]) ? pdata->active_low :
+							!pdata->active_low;
+}
+
+/*
+ * This gets the keys from keyboard and reports it to input subsystem
+ */
+static void mt_matrix_keypad_scan(struct work_struct *work)
+{
+	struct mt_matrix_keypad *keypad =
+		container_of(work, struct mt_matrix_keypad, work.work);
+	struct input_dev *input_dev = keypad->input_dev;
+	struct mt_matrix_keypad_platform_data *pdata = keypad->pdata;
+
+	if (keypad->stopped == false) {
+		update_buttons(pdata, input_dev);
+		schedule_delayed_work(
+			&keypad->work,
+			msecs_to_jiffies(keypad->pdata->debounce_ms));
+	}
+}
+
+static int mt_matrix_keypad_start(struct input_dev *dev)
+{
+	struct mt_matrix_keypad *keypad = input_get_drvdata(dev);
+
+	keypad->stopped = false;
+	/*
+	 * memory access initiated before the memory barrier
+	 * will be complete before passing the barrier
+	 */
+	mb();
+
+	/*
+	 * Schedule an immediate key scan to capture current key state;
+	 * columns will be activated and IRQs be enabled after the scan.
+	 */
+	schedule_delayed_work(&keypad->work, 0);
+
+	return 0;
+}
+
+static void mt_matrix_keypad_stop(struct input_dev *dev)
+{
+	struct mt_matrix_keypad *keypad = input_get_drvdata(dev);
+
+	keypad->stopped = true;
+	/*
+	 * memory access initiated before the memory barrier
+	 * will be complete before passing the barrier
+	 */
+	mb();
+	cancel_delayed_work_sync(&keypad->work);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static void mt_matrix_keypad_enable_wakeup(struct mt_matrix_keypad *keypad)
+{
+	const struct mt_matrix_keypad_platform_data *pdata = keypad->pdata;
+	unsigned int gpio;
+	int i;
+
+	if (pdata->clustered_irq > 0) {
+		if (enable_irq_wake(pdata->clustered_irq) == 0)
+			keypad->gpio_all_disabled = true;
+	} else {
+		for (i = 0; i < pdata->num_line_gpios; i++) {
+			if (!test_bit(i, keypad->disabled_gpios)) {
+				gpio = pdata->line_gpios[i];
+
+				if (enable_irq_wake(gpio_to_irq(gpio)) == 0)
+					__set_bit(i, keypad->disabled_gpios);
+			}
+		}
+	}
+}
+
+static void mt_matrix_keypad_disable_wakeup(struct mt_matrix_keypad *keypad)
+{
+	const struct mt_matrix_keypad_platform_data *pdata = keypad->pdata;
+	unsigned int gpio;
+	int i;
+
+	if (pdata->clustered_irq > 0) {
+		if (keypad->gpio_all_disabled) {
+			disable_irq_wake(pdata->clustered_irq);
+			keypad->gpio_all_disabled = false;
+		}
+	} else {
+		for (i = 0; i < pdata->num_line_gpios; i++) {
+			if (test_and_clear_bit(i, keypad->disabled_gpios)) {
+				gpio = pdata->line_gpios[i];
+				disable_irq_wake(gpio_to_irq(gpio));
+			}
+		}
+	}
+}
+
+static int mt_matrix_keypad_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct mt_matrix_keypad *keypad = platform_get_drvdata(pdev);
+
+	mt_matrix_keypad_stop(keypad->input_dev);
+
+	if (device_may_wakeup(&pdev->dev))
+		mt_matrix_keypad_enable_wakeup(keypad);
+
+	pinctrl_pm_select_sleep_state(dev);
+
+	return 0;
+}
+
+static int mt_matrix_keypad_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct mt_matrix_keypad *keypad = platform_get_drvdata(pdev);
+
+	if (device_may_wakeup(&pdev->dev))
+		mt_matrix_keypad_disable_wakeup(keypad);
+
+	pinctrl_pm_select_default_state(dev);
+
+	mt_matrix_keypad_start(keypad->input_dev);
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(mt_matrix_keypad_pm_ops, mt_matrix_keypad_suspend,
+			 mt_matrix_keypad_resume);
+
+static int mt_matrix_keypad_init_gpio(struct platform_device *pdev,
+				      struct mt_matrix_keypad *keypad)
+{
+	const struct mt_matrix_keypad_platform_data *pdata = keypad->pdata;
+	int i, err;
+
+	for (i = 0; i < pdata->num_line_gpios; i++) {
+		err = gpio_request(pdata->line_gpios[i], "mt_kbd_row");
+		if (err) {
+			dev_err(&pdev->dev,
+				"failed to request GPIO%d for ROW%d\n",
+				pdata->line_gpios[i], i);
+			goto err_free_rows;
+		}
+
+		gpio_direction_input(pdata->line_gpios[i]);
+	}
+
+	return 0;
+
+err_free_rows:
+	while (--i >= 0)
+		gpio_free(pdata->line_gpios[i]);
+
+	i = pdata->num_line_gpios;
+	return err;
+}
+
+static void mt_matrix_keypad_free_gpio(struct mt_matrix_keypad *keypad)
+{
+	const struct mt_matrix_keypad_platform_data *pdata = keypad->pdata;
+	int i;
+
+	for (i = 0; i < pdata->num_line_gpios; i++)
+		gpio_free(pdata->line_gpios[i]);
+}
+
+#ifdef CONFIG_OF
+static struct mt_matrix_keypad_platform_data *
+mt_matrix_keypad_parse_dt(struct device *dev)
+{
+	struct mt_matrix_keypad_platform_data *pdata = NULL;
+	struct device_node *np = dev->of_node;
+	unsigned int *gpios;
+	struct button *button_array;
+	int8_t *button_matrix;
+	uint16_t keycode;
+	uint32_t *ptr;
+	int keymap;
+	int i;
+
+	if (!np) {
+		dev_err(dev, "device lacks DT data\n");
+		return ERR_PTR(-ENODEV);
+	}
+
+	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+	if (!pdata)
+		return ERR_PTR(-ENOMEM);
+
+	pdata->num_line_gpios = of_gpio_named_count(np, "line-gpios");
+	if (pdata->num_line_gpios <= 0) {
+		dev_err(dev, "number of gpio line not specified\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	if (of_get_property(np, "linux,no-autorepeat", NULL))
+		pdata->no_autorepeat = true;
+
+	pdata->wakeup = of_property_read_bool(np, "wakeup-source") ||
+			of_property_read_bool(np, "linux,wakeup"); /* legacy */
+
+	if (of_get_property(np, "gpio-activelow", NULL))
+		pdata->active_low = true;
+
+	of_property_read_u32(np, "debounce-delay-ms", &pdata->debounce_ms);
+	of_property_read_u32(np, "col-scan-delay-us",
+			     &pdata->col_scan_delay_us);
+	of_property_read_u32(np, "number-of-buttons", &pdata->num_of_buttons);
+	if (pdata->num_of_buttons <= 0) {
+		dev_err(dev, "number of button not specified\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	button_array =
+		devm_kzalloc(dev,
+			     sizeof(struct button) * (pdata->num_of_buttons),
+			     GFP_KERNEL);
+	if (!button_array) {
+		dev_err(dev, "could not allocate memory for button array\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	pdata->button_array = button_array;
+
+	gpios = devm_kzalloc(dev,
+			     sizeof(unsigned int) * (pdata->num_line_gpios),
+			     GFP_KERNEL);
+	if (!gpios) {
+		dev_err(dev, "could not allocate memory for gpios\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	for (i = 0; i < pdata->num_line_gpios; i++)
+		gpios[i] = of_get_named_gpio(np, "line-gpios", i);
+
+	pdata->line_gpios = gpios;
+
+	keymap = device_property_count_u32(dev, "linux,keymap");
+	if (keymap <= 0 ||
+	    keymap > (pdata->num_line_gpios * pdata->num_line_gpios)) {
+		dev_err(dev, "linux,keymap property count is more");
+		return ERR_PTR(-ENXIO);
+	}
+
+	ptr = kcalloc(keymap, sizeof(uint32_t), GFP_KERNEL);
+	if (!ptr)
+		return ERR_PTR(-ENOMEM);
+
+	if (device_property_read_u32_array(dev, "linux,keymap", ptr, keymap)) {
+		dev_err(dev, "problem parsing keymap property\n");
+		kfree(ptr);
+		return ERR_PTR(-EINVAL);
+	}
+
+	button_matrix =
+		devm_kzalloc(dev, (keymap * sizeof(int8_t)), GFP_KERNEL);
+	if (!button_matrix) {
+		dev_err(dev, "could not allocate memory for button matrix\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	pdata->button_matrix = button_matrix;
+	for (i = 0; i < keymap; i++) {
+		keycode = KEYCODE(ptr[i]);
+		pdata->button_matrix[i] = keycode;
+	}
+	kfree(ptr);
+
+	return pdata;
+}
+
+#else
+static inline struct mt_matrix_keypad_platform_data *
+mt_matrix_keypad_parse_dt(struct device *dev)
+{
+	dev_err(dev, "no platform data defined\n");
+
+	return ERR_PTR(-EINVAL);
+}
+#endif
+
+static int mt_matrix_keypad_probe(struct platform_device *pdev)
+{
+	struct mt_matrix_keypad_platform_data *pdata;
+	struct mt_matrix_keypad *keypad;
+	struct input_dev *input_dev;
+	int err;
+	int row, col, index;
+
+	dev_info(&pdev->dev, "[%s]: Probe\n", MODULE_NAME);
+	pdata = dev_get_platdata(&pdev->dev);
+
+	if (!pdata) {
+		pdata = mt_matrix_keypad_parse_dt(&pdev->dev);
+		if (IS_ERR(pdata)) {
+			dev_err(&pdev->dev, "Mt platform data not defined\n");
+			return PTR_ERR(pdata);
+		}
+	}
+
+	err = pdata->line_gpios[0];
+	if (err < 0)
+		return dev_err_probe(
+			&pdev->dev, err,
+			"Could not register gpio chip for mt matrix keypad\n");
+
+	keypad = kzalloc(sizeof(struct mt_matrix_keypad), GFP_KERNEL);
+	input_dev = input_allocate_device();
+
+	if (!keypad || !input_dev) {
+		dev_err(&pdev->dev, "[%s]: Allocation Failed\n", MODULE_NAME);
+		err = -ENOMEM;
+		goto err_free_mem;
+	}
+
+	init_phase(pdata);
+	keypad->input_dev = input_dev;
+	keypad->pdata = pdata;
+	keypad->stopped = true;
+	INIT_DELAYED_WORK(&keypad->work, mt_matrix_keypad_scan);
+	spin_lock_init(&keypad->lock);
+
+	input_dev->name = pdev->name;
+	input_dev->id.bustype = BUS_HOST;
+	input_dev->dev.parent = &pdev->dev;
+	input_dev->open = mt_matrix_keypad_start;
+	input_dev->close = mt_matrix_keypad_stop;
+
+	if (!pdata->no_autorepeat)
+		__set_bit(EV_REP, input_dev->evbit);
+
+	input_set_capability(input_dev, EV_MSC, MSC_SCAN);
+
+	for (row = 1; row < pdata->num_line_gpios; row++) {
+		for (col = 0; col < row; col++) {
+			index = (row * pdata->num_line_gpios) + col;
+			if (pdata->button_matrix[index] !=
+			    pdata->button_matrix[0]) {
+				input_set_capability(
+					input_dev, EV_KEY,
+					pdata->button_matrix[index]);
+			}
+		}
+	}
+
+	input_set_drvdata(input_dev, keypad);
+	err = mt_matrix_keypad_init_gpio(pdev, keypad);
+
+	if (err)
+		goto err_free_mem;
+
+	button_hdl_init(pdata);
+
+	err = input_register_device(keypad->input_dev);
+
+	if (err)
+		goto err_free_gpio;
+
+	device_init_wakeup(&pdev->dev, pdata->wakeup);
+	platform_set_drvdata(pdev, keypad);
+
+	return 0;
+
+err_free_gpio:
+	mt_matrix_keypad_free_gpio(keypad);
+
+err_free_mem:
+	input_free_device(input_dev);
+	kfree(keypad);
+	return err;
+}
+
+static int mt_matrix_keypad_remove(struct platform_device *pdev)
+{
+	struct mt_matrix_keypad *keypad = platform_get_drvdata(pdev);
+
+	device_init_wakeup(&pdev->dev, 0);
+	input_unregister_device(keypad->input_dev);
+	mt_matrix_keypad_free_gpio(keypad);
+	kfree(keypad);
+
+	return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id mt_matrix_keypad_dt_match[] = {
+	{ .compatible = "gpio-mt-matrix-keypad" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, mt_matrix_keypad_dt_match);
+#endif
+
+static struct platform_driver mt_matrix_keypad_driver = {
+	.probe		= mt_matrix_keypad_probe,
+	.remove		= mt_matrix_keypad_remove,
+	.driver		= {
+		.name	= "mt-matrix-keypad",
+		.pm	= &mt_matrix_keypad_pm_ops,
+		.of_match_table = of_match_ptr(mt_matrix_keypad_dt_match),
+	},
+};
+module_platform_driver(mt_matrix_keypad_driver);
+
+MODULE_AUTHOR("vinay");
+MODULE_DESCRIPTION("GPIO Driven Mt Matrix Keypad Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:mt-matrix-keypad");
diff --git a/include/linux/input/mt_matrix_keypad.h b/include/linux/input/mt_matrix_keypad.h
new file mode 100644
index 000000000000..46dfe49c5fe1
--- /dev/null
+++ b/include/linux/input/mt_matrix_keypad.h
@@ -0,0 +1,85 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _MT_MATRIX_KEYPAD_H
+#define _MT_MATRIX_KEYPAD_H
+
+#include <linux/types.h>
+#include <linux/input.h>
+#include <linux/of.h>
+
+#define MATRIX_MAX_ROWS 32
+#define KEYCODE(keymap) (keymap & 0xFFFF)
+
+struct button_states {
+	uint8_t boPrevious : 1;
+	uint8_t boCurrent : 1;
+	uint8_t boCurrentStateOfHw : 1;
+	uint8_t boCurrentStateOfSw : 1;
+	uint8_t boEnabled : 1;
+	uint8_t boStateAtInit : 1;
+};
+
+union typeEvent {
+	uint8_t ui8Register;
+	struct {
+		uint8_t boGlobalChanged : 1;
+		uint8_t boPressed : 1;
+		uint8_t boReleased : 1;
+	} status;
+};
+
+struct button {
+	uint8_t key;
+	union typeEvent event;
+	struct button_states state;
+};
+
+struct phase {
+	int phase_prepare;
+	int phase_start;
+	int phase_update_button;
+};
+
+struct mt_matrix_keypad_platform_data {
+	const struct mt_keymap_data *keymap_data;
+	const unsigned int *line_gpios;
+	unsigned int num_line_gpios;
+	unsigned int num_of_buttons;
+	unsigned int col_scan_delay_us;
+	unsigned int debounce_ms;
+	unsigned int clustered_irq;
+	unsigned int clustered_irq_flags;
+
+	bool active_low;
+	bool wakeup;
+	bool no_autorepeat;
+	bool intialize_buttons;
+
+	int8_t scan_phase;
+	int8_t *button_matrix;
+	struct button *button_array;
+	struct phase phase_state;
+};
+
+void init_phase(struct mt_matrix_keypad_platform_data *pdata);
+void button_init(struct button *btn, bool btn_hw_state, int key);
+struct button_states get_button_state(struct button *btn);
+union typeEvent get_and_clear_events(struct button *btn);
+uint8_t get_btn_index(struct mt_matrix_keypad_platform_data *pdata,
+		      int btn_key);
+void set_btn_state_by_hw(struct button *btn, bool boButtonState);
+bool check_button_changes(struct button *btn);
+struct button_states
+get_btn_id_state(const struct mt_matrix_keypad_platform_data *pdata,
+		 int btn_index);
+union typeEvent
+get_and_clear_btn_events(const struct mt_matrix_keypad_platform_data *pdata,
+			 int btn_index);
+void button_hdl_init(struct mt_matrix_keypad_platform_data *pdata);
+bool on_button_event(const struct mt_matrix_keypad_platform_data *pdata,
+		     int btn_index, union typeEvent btn_event,
+		     struct input_dev *input_dev);
+void process_button_events(const struct mt_matrix_keypad_platform_data *pdata,
+			   struct input_dev *input_dev);
+void update_buttons(struct mt_matrix_keypad_platform_data *pdata,
+		    struct input_dev *input_dev);
+#endif /* _MT_MATRIX_KEYPAD_H */
-- 
2.20.1


  reply	other threads:[~2022-05-06  7:29 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-05-06  7:27 [PATCH v2 1/4] ARM: dts: am335x: Guardian: switch to AM33XX_PADCONF pinmux macro Gireesh.Hiremath
2022-05-06  7:27 ` Gireesh.Hiremath [this message]
2022-05-07 15:46   ` [PATCH v2 2/4] Input: mt-matrix-keypad: Add Bosch mt matrix keypad driver Krzysztof Kozlowski
2022-05-10 14:13   ` Gireesh.Hiremath
2022-05-11 16:46     ` Krzysztof Kozlowski
2022-05-12  4:43   ` kernel test robot
2022-06-13  8:06   ` [v2,2/4] " Gireesh.Hiremath
2022-06-15  8:28     ` Marco Felsch
2022-06-15 20:58     ` Krzysztof Kozlowski
2022-08-19  6:56   ` Gireesh.Hiremath
2022-05-06  7:27 ` [PATCH v2 3/4] ARM: dts: am335x: Guardian: add keymap to mt matrix keypad Gireesh.Hiremath
2022-05-06  7:27 ` [PATCH v2 4/4] dt-bindings: input: mt-matrix-keypad: add guardian mt matrix keypad bindings definition Gireesh.Hiremath
2022-05-07 15:44   ` Krzysztof Kozlowski

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20220506072737.1590-2-Gireesh.Hiremath@in.bosch.com \
    --to=gireesh.hiremath@in.bosch.com \
    --cc=Govindaraji.Sivanantham@in.bosch.com \
    --cc=VinayKumar.Shettar@in.bosch.com \
    --cc=anaclaudia.dias@de.bosch.com \
    --cc=bcousson@baylibre.com \
    --cc=colin.king@intel.com \
    --cc=davidgow@google.com \
    --cc=devicetree@vger.kernel.org \
    --cc=dmitry.torokhov@gmail.com \
    --cc=fengping.yu@mediatek.com \
    --cc=krzysztof.kozlowski+dt@linaro.org \
    --cc=linux-input@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-omap@vger.kernel.org \
    --cc=m.felsch@pengutronix.de \
    --cc=mkorpershoek@baylibre.com \
    --cc=rdunlap@infradead.org \
    --cc=robh+dt@kernel.org \
    --cc=sjoerd.simons@collabora.co.uk \
    --cc=swboyd@chromium.org \
    --cc=tony@atomide.com \
    --cc=y.oudjana@protonmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.