diff mbox series

[2/4] Input: mt-matrix-keypad: Add Bosch mt matrix keypad driver

Message ID 20220504105254.1576-2-Gireesh.Hiremath@in.bosch.com
State New
Headers show
Series [1/4] ARM: dts: am335x: Guardian: switch to AM33XX_PADCONF pinmux macro | expand

Commit Message

Gireesh.Hiremath@in.bosch.com May 4, 2022, 10:52 a.m. UTC
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>
---
 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    |  63 ++
 4 files changed, 815 insertions(+)
 create mode 100644 drivers/input/keyboard/mt_matrix_keypad.c
 create mode 100644 include/linux/input/mt_matrix_keypad.h
diff mbox series

Patch

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..9310faf8b237
--- /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;
+}
+
+const 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_button; 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;
+}
+
+const struct button_states
+get_btn_id_state(const struct mt_matrix_keypad_platform_data *pdata,
+		 int btn_index)
+{
+	if (btn_index < pdata->num_of_button)
+		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_button)
+		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_button) {
+					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_button; 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_button; 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_button;
+		     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-button", &pdata->num_of_button);
+	if (pdata->num_of_button <= 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_button),
+			     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..8c5fbf109dbc
--- /dev/null
+++ b/include/linux/input/mt_matrix_keypad.h
@@ -0,0 +1,63 @@ 
+/* 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_button;
+	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;
+};
+
+#endif /* _MT_MATRIX_KEYPAD_H */