diff mbox series

[v3,2/3] driver: input: matric-keypad: add reduced matrix support

Message ID 20220819065946.9572-2-Gireesh.Hiremath@in.bosch.com
State New
Headers show
Series [v3,1/3] driver: input: matric-keypad: switch to gpiod | expand

Commit Message

Gireesh.Hiremath@in.bosch.com Aug. 19, 2022, 6:59 a.m. UTC
From: Gireesh Hiremath <Gireesh.Hiremath@in.bosch.com>

Add support to reduced matrix keypad driver.
Tested on Bosch Guardian Dtect board(TI-am335x).

Signed-off-by: Gireesh Hiremath <Gireesh.Hiremath@in.bosch.com>
---
 drivers/input/keyboard/matrix_keypad.c | 559 +++++++++++++++++++++----
 include/linux/input/matrix_keypad.h    |  68 +--
 2 files changed, 520 insertions(+), 107 deletions(-)

Comments

Dan Carpenter Aug. 22, 2022, 7:40 a.m. UTC | #1
Hi,

https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Gireesh-Hiremath-in-bosch-com/driver-input-matric-keypad-switch-to-gpiod/20220819-151155
base:   https://git.kernel.org/pub/scm/linux/kernel/git/dtor/input.git next
config: s390-randconfig-m041-20220819 (https://download.01.org/0day-ci/archive/20220820/202208200111.6wvFtbES-lkp@intel.com/config)
compiler: s390-linux-gcc (GCC) 12.1.0

If you fix the issue, kindly add following tag where applicable
Reported-by: kernel test robot <lkp@intel.com>
Reported-by: Dan Carpenter <dan.carpenter@oracle.com>

smatch warnings:
drivers/input/keyboard/matrix_keypad.c:932 matrix_keypad_probe() error: uninitialized symbol 'err'.

vim +/err +932 drivers/input/keyboard/matrix_keypad.c

5298cc4cc753bb Bill Pemberton   2012-11-23  823  static int matrix_keypad_probe(struct platform_device *pdev)
bab7614d6d1b1f Eric Miao        2009-06-29  824  {
bab7614d6d1b1f Eric Miao        2009-06-29  825  	const struct matrix_keypad_platform_data *pdata;
bab7614d6d1b1f Eric Miao        2009-06-29  826  	struct matrix_keypad *keypad;
bab7614d6d1b1f Eric Miao        2009-06-29  827  	struct input_dev *input_dev;
bab7614d6d1b1f Eric Miao        2009-06-29  828  	int err;
bab7614d6d1b1f Eric Miao        2009-06-29  829  
4a83eecff65bd3 AnilKumar Ch     2012-11-20  830  	pdata = dev_get_platdata(&pdev->dev);
bab7614d6d1b1f Eric Miao        2009-06-29  831  	if (!pdata) {
4a83eecff65bd3 AnilKumar Ch     2012-11-20  832  		pdata = matrix_keypad_parse_dt(&pdev->dev);
d55bda1b3e7c5a Christian Hoff   2018-11-12  833  		if (IS_ERR(pdata))
4a83eecff65bd3 AnilKumar Ch     2012-11-20  834  			return PTR_ERR(pdata);
4a83eecff65bd3 AnilKumar Ch     2012-11-20  835  	} else if (!pdata->keymap_data) {
bab7614d6d1b1f Eric Miao        2009-06-29  836  		dev_err(&pdev->dev, "no keymap data defined\n");
bab7614d6d1b1f Eric Miao        2009-06-29  837  		return -EINVAL;
bab7614d6d1b1f Eric Miao        2009-06-29  838  	}
bab7614d6d1b1f Eric Miao        2009-06-29  839  
4a83eecff65bd3 AnilKumar Ch     2012-11-20  840  	keypad = kzalloc(sizeof(struct matrix_keypad), GFP_KERNEL);
bab7614d6d1b1f Eric Miao        2009-06-29  841  	input_dev = input_allocate_device();
01111fcd42b050 Dmitry Torokhov  2012-04-20  842  	if (!keypad || !input_dev) {
bab7614d6d1b1f Eric Miao        2009-06-29  843  		err = -ENOMEM;
bab7614d6d1b1f Eric Miao        2009-06-29  844  		goto err_free_mem;
bab7614d6d1b1f Eric Miao        2009-06-29  845  	}
bab7614d6d1b1f Eric Miao        2009-06-29  846  
bab7614d6d1b1f Eric Miao        2009-06-29  847  	keypad->input_dev = input_dev;
bab7614d6d1b1f Eric Miao        2009-06-29  848  	keypad->pdata = pdata;
4a83eecff65bd3 AnilKumar Ch     2012-11-20  849  	keypad->row_shift = get_count_order(pdata->num_col_gpios);
bab7614d6d1b1f Eric Miao        2009-06-29  850  	keypad->stopped = true;
a0b420e08e3b87 Gireesh Hiremath 2022-08-19  851  
a0b420e08e3b87 Gireesh Hiremath 2022-08-19  852  	if (pdata->mode == REDUCED) {
a0b420e08e3b87 Gireesh Hiremath 2022-08-19  853  		keypad->button_array = devm_kzalloc(
a0b420e08e3b87 Gireesh Hiremath 2022-08-19  854  			&pdev->dev,
a0b420e08e3b87 Gireesh Hiremath 2022-08-19  855  			sizeof(struct button) * (pdata->num_of_buttons),
a0b420e08e3b87 Gireesh Hiremath 2022-08-19  856  			GFP_KERNEL);
a0b420e08e3b87 Gireesh Hiremath 2022-08-19  857  		if (!keypad->button_array) {
a0b420e08e3b87 Gireesh Hiremath 2022-08-19  858  			dev_err(&pdev->dev,
a0b420e08e3b87 Gireesh Hiremath 2022-08-19  859  				"could not allocate memory for button array\n");

err = -ENOMEM;

a0b420e08e3b87 Gireesh Hiremath 2022-08-19  860  			goto err_free_mem;
a0b420e08e3b87 Gireesh Hiremath 2022-08-19  861  			;

Why the extra ;?

a0b420e08e3b87 Gireesh Hiremath 2022-08-19  862  		}
a0b420e08e3b87 Gireesh Hiremath 2022-08-19  863  
a0b420e08e3b87 Gireesh Hiremath 2022-08-19  864  		poll_prepare(keypad);
a0b420e08e3b87 Gireesh Hiremath 2022-08-19  865  
a0b420e08e3b87 Gireesh Hiremath 2022-08-19  866  		err = input_setup_polling(input_dev, matrix_keypad_poll);
a0b420e08e3b87 Gireesh Hiremath 2022-08-19  867  		if (err) {
a0b420e08e3b87 Gireesh Hiremath 2022-08-19  868  			dev_err(&pdev->dev,
a0b420e08e3b87 Gireesh Hiremath 2022-08-19  869  				"unable to set up polling, err=%d\n", err);
a0b420e08e3b87 Gireesh Hiremath 2022-08-19  870  			return err;

Memory leaks.  Needs to goto err_free_mem;

a0b420e08e3b87 Gireesh Hiremath 2022-08-19  871  		}
a0b420e08e3b87 Gireesh Hiremath 2022-08-19  872  
a0b420e08e3b87 Gireesh Hiremath 2022-08-19  873  		input_set_poll_interval(input_dev, pdata->poll_interval_ms);
a0b420e08e3b87 Gireesh Hiremath 2022-08-19  874  	} else {
bab7614d6d1b1f Eric Miao        2009-06-29  875  		INIT_DELAYED_WORK(&keypad->work, matrix_keypad_scan);
a0b420e08e3b87 Gireesh Hiremath 2022-08-19  876  	}
bab7614d6d1b1f Eric Miao        2009-06-29  877  	spin_lock_init(&keypad->lock);
bab7614d6d1b1f Eric Miao        2009-06-29  878  
bab7614d6d1b1f Eric Miao        2009-06-29  879  	input_dev->name = pdev->name;
bab7614d6d1b1f Eric Miao        2009-06-29  880  	input_dev->id.bustype = BUS_HOST;
bab7614d6d1b1f Eric Miao        2009-06-29  881  	input_dev->dev.parent = &pdev->dev;
bab7614d6d1b1f Eric Miao        2009-06-29  882  	input_dev->open = matrix_keypad_start;
bab7614d6d1b1f Eric Miao        2009-06-29  883  	input_dev->close = matrix_keypad_stop;
bab7614d6d1b1f Eric Miao        2009-06-29  884  
a0b420e08e3b87 Gireesh Hiremath 2022-08-19  885  	if (pdata->mode == REDUCED) {
a0b420e08e3b87 Gireesh Hiremath 2022-08-19  886  		err = matrix_keypad_build_keymap(pdata->keymap_data, NULL,
a0b420e08e3b87 Gireesh Hiremath 2022-08-19  887  						 pdata->num_line_gpios,
a0b420e08e3b87 Gireesh Hiremath 2022-08-19  888  						 pdata->num_line_gpios, NULL,
a0b420e08e3b87 Gireesh Hiremath 2022-08-19  889  						 input_dev);
a0b420e08e3b87 Gireesh Hiremath 2022-08-19  890  		if (err) {
a0b420e08e3b87 Gireesh Hiremath 2022-08-19  891  			dev_err(&pdev->dev, "failed to build keymap for reduced mode\n");
a0b420e08e3b87 Gireesh Hiremath 2022-08-19  892  			goto err_free_mem;
a0b420e08e3b87 Gireesh Hiremath 2022-08-19  893  		}
a0b420e08e3b87 Gireesh Hiremath 2022-08-19  894  	} else {
4a83eecff65bd3 AnilKumar Ch     2012-11-20  895  		err = matrix_keypad_build_keymap(pdata->keymap_data, NULL,
1932811f426fee Dmitry Torokhov  2012-05-10  896  						 pdata->num_row_gpios,
a0b420e08e3b87 Gireesh Hiremath 2022-08-19  897  						 pdata->num_col_gpios, NULL,
a0b420e08e3b87 Gireesh Hiremath 2022-08-19  898  						 input_dev);
4a83eecff65bd3 AnilKumar Ch     2012-11-20  899  		if (err) {
a0b420e08e3b87 Gireesh Hiremath 2022-08-19  900  			dev_err(&pdev->dev, "failed to build keymap for generic mode\n");
1932811f426fee Dmitry Torokhov  2012-05-10  901  			goto err_free_mem;
4a83eecff65bd3 AnilKumar Ch     2012-11-20  902  		}
a0b420e08e3b87 Gireesh Hiremath 2022-08-19  903  	}
bab7614d6d1b1f Eric Miao        2009-06-29  904  
1932811f426fee Dmitry Torokhov  2012-05-10  905  	if (!pdata->no_autorepeat)
1932811f426fee Dmitry Torokhov  2012-05-10  906  		__set_bit(EV_REP, input_dev->evbit);
bab7614d6d1b1f Eric Miao        2009-06-29  907  	input_set_capability(input_dev, EV_MSC, MSC_SCAN);
bab7614d6d1b1f Eric Miao        2009-06-29  908  	input_set_drvdata(input_dev, keypad);
bab7614d6d1b1f Eric Miao        2009-06-29  909  
a0b420e08e3b87 Gireesh Hiremath 2022-08-19  910  	if (pdata->mode == REDUCED) {
a0b420e08e3b87 Gireesh Hiremath 2022-08-19  911  		button_hdl_init(keypad);
a0b420e08e3b87 Gireesh Hiremath 2022-08-19  912  	} else {
b83643ebf22423 Dmitry Torokhov  2012-04-20  913  		err = matrix_keypad_init_gpio(pdev, keypad);
bab7614d6d1b1f Eric Miao        2009-06-29  914  		if (err)
bab7614d6d1b1f Eric Miao        2009-06-29  915  			goto err_free_mem;
a0b420e08e3b87 Gireesh Hiremath 2022-08-19  916  	}
bab7614d6d1b1f Eric Miao        2009-06-29  917  
bab7614d6d1b1f Eric Miao        2009-06-29  918  	err = input_register_device(keypad->input_dev);
bab7614d6d1b1f Eric Miao        2009-06-29  919  	if (err)
b83643ebf22423 Dmitry Torokhov  2012-04-20  920  		goto err_free_gpio;
bab7614d6d1b1f Eric Miao        2009-06-29  921  
bab7614d6d1b1f Eric Miao        2009-06-29  922  	device_init_wakeup(&pdev->dev, pdata->wakeup);
bab7614d6d1b1f Eric Miao        2009-06-29  923  	platform_set_drvdata(pdev, keypad);
bab7614d6d1b1f Eric Miao        2009-06-29  924  
bab7614d6d1b1f Eric Miao        2009-06-29  925  	return 0;
bab7614d6d1b1f Eric Miao        2009-06-29  926  
b83643ebf22423 Dmitry Torokhov  2012-04-20  927  err_free_gpio:
b83643ebf22423 Dmitry Torokhov  2012-04-20  928  	matrix_keypad_free_gpio(keypad);
bab7614d6d1b1f Eric Miao        2009-06-29  929  err_free_mem:
bab7614d6d1b1f Eric Miao        2009-06-29  930  	input_free_device(input_dev);
bab7614d6d1b1f Eric Miao        2009-06-29  931  	kfree(keypad);
bab7614d6d1b1f Eric Miao        2009-06-29 @932  	return err;
bab7614d6d1b1f Eric Miao        2009-06-29  933  }
diff mbox series

Patch

diff --git a/drivers/input/keyboard/matrix_keypad.c b/drivers/input/keyboard/matrix_keypad.c
index b99574edad9c..72c0663b6ed3 100644
--- a/drivers/input/keyboard/matrix_keypad.c
+++ b/drivers/input/keyboard/matrix_keypad.c
@@ -21,9 +21,30 @@ 
 #include <linux/of.h>
 #include <linux/of_platform.h>
 
+struct button_states {
+	uint8_t previous_state : 1;
+	uint8_t current_state : 1;
+};
+
+union button_event {
+	uint8_t clear_event;
+	struct {
+		uint8_t global_changed : 1;
+		uint8_t pressed : 1;
+		uint8_t released : 1;
+	} status;
+};
+
+struct button {
+	uint8_t key;
+	union button_event event;
+	struct button_states state;
+};
+
 struct matrix_keypad {
 	const struct matrix_keypad_platform_data *pdata;
 	struct input_dev *input_dev;
+	struct button *button_array;
 	unsigned int row_shift;
 
 	DECLARE_BITMAP(disabled_gpios, MATRIX_MAX_ROWS);
@@ -31,11 +52,34 @@  struct matrix_keypad {
 	uint32_t last_key_state[MATRIX_MAX_COLS];
 	struct delayed_work work;
 	spinlock_t lock;
+	int8_t current_line_gpio;
 	bool scan_pending;
 	bool stopped;
 	bool gpio_all_disabled;
 };
 
+struct keypad_info {
+	unsigned char mode;
+};
+
+static const struct keypad_info keypad_infos[] = {
+	{
+		.mode = GENERIC,
+	},
+	{
+		.mode = REDUCED,
+	},
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id matrix_keypad_dt_match[] = {
+	{ .compatible = "gpio-matrix-keypad", .data = &keypad_infos[0] },
+	{ .compatible = "gpio-matrix-keypad-reduced",
+	  .data = &keypad_infos[1] },
+	{}
+};
+MODULE_DEVICE_TABLE(of, matrix_keypad_dt_match);
+#endif
 /*
  * NOTE: If drive_inactive_cols is false, then the GPIO has to be put into
  * HiZ when de-activated to cause minmal side effect when scanning other
@@ -78,7 +122,8 @@  static bool row_asserted(const struct matrix_keypad_platform_data *pdata,
 			 int row)
 {
 	return gpiod_get_value_cansleep(pdata->row_gpios[row]) ?
-			!pdata->active_low : pdata->active_low;
+		       !pdata->active_low :
+		       pdata->active_low;
 }
 
 static void enable_row_irqs(struct matrix_keypad *keypad)
@@ -107,6 +152,247 @@  static void disable_row_irqs(struct matrix_keypad *keypad)
 	}
 }
 
+static void
+__activate_line_driving(const struct matrix_keypad_platform_data *pdata,
+			int line, bool on)
+{
+	bool level_on = pdata->active_low;
+
+	if (on)
+		gpiod_direction_output(pdata->desc_gpios[line], level_on);
+	else
+		gpiod_direction_input(pdata->desc_gpios[line]);
+}
+
+static void
+activate_line_driving(const struct 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 void button_init(struct button *btn, bool btn_hw_state, int key)
+{
+	btn->state.previous_state = btn_hw_state;
+	btn->state.current_state = btn_hw_state;
+	btn->event.clear_event = 0;
+	btn->key = key;
+}
+
+static bool check_button_changes(struct button *btn)
+{
+	/* Check if Button is pressed */
+	if ((!btn->state.previous_state) && btn->state.current_state)
+		btn->event.status.pressed = true;
+
+	/* Check if Button is released */
+	else if (btn->state.previous_state && (!btn->state.current_state))
+		btn->event.status.released = true;
+
+	if (btn->event.clear_event != 0)
+		btn->event.status.global_changed = true;
+
+	btn->state.previous_state = btn->state.current_state;
+
+	return btn->event.status.global_changed;
+}
+
+static union button_event get_and_clear_events(struct button *btn)
+{
+	union button_event temp = btn->event;
+
+	btn->event.clear_event = 0;
+
+	return temp;
+}
+
+static union button_event get_and_clear_btn_events(struct matrix_keypad *keypad,
+						   int btn_index)
+{
+	if (btn_index < keypad->pdata->num_of_buttons)
+		return get_and_clear_events(&keypad->button_array[btn_index]);
+	else
+		return get_and_clear_events(&keypad->button_array[0]);
+}
+
+static void button_hdl_init(struct matrix_keypad *keypad)
+{
+	const struct matrix_keypad_platform_data *pdata = keypad->pdata;
+	int row, col, index;
+	int i;
+
+	/* 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->keycode[index] != pdata->keycode[0]) {
+				if (i < pdata->num_of_buttons) {
+					button_init(&keypad->button_array[i],
+						    false,
+						    pdata->keycode[index]);
+					i++;
+				}
+			}
+		}
+
+	pr_debug("[matrix-keypad]: %s Done\n", __func__);
+}
+
+static void on_button_event(struct matrix_keypad *keypad, int btn_index,
+			    union button_event btn_event,
+			    struct input_dev *input_dev)
+{
+	unsigned int key_code = 0;
+	int key_value = 0;
+
+	key_code = keypad->button_array[btn_index].key;
+
+	if (btn_event.status.pressed) {
+		key_value = 1;
+		pr_debug("[matrix-keypad]:%d Pressed\n", key_code);
+	}
+
+	if (btn_event.status.released) {
+		key_value = 0;
+		pr_debug("[matrix-keypad]:%d Released\n", key_code);
+	}
+
+	input_report_key(input_dev, key_code, key_value);
+	input_sync(input_dev);
+}
+
+static void process_button_events(struct matrix_keypad *keypad,
+				  struct input_dev *input_dev)
+{
+	int btn_index;
+
+	for (btn_index = 0; btn_index < keypad->pdata->num_of_buttons;
+	     btn_index++) {
+		const union button_event beEvent =
+			get_and_clear_btn_events(keypad, (int)btn_index);
+
+		if (beEvent.status.global_changed) {
+			on_button_event(keypad, (int)btn_index, beEvent,
+					input_dev);
+		}
+	}
+}
+
+static bool get_gpio_line_value(const struct matrix_keypad_platform_data *pdata,
+				int row)
+{
+	return gpiod_get_value(pdata->desc_gpios[row]) ? pdata->active_low :
+							 !pdata->active_low;
+}
+
+static uint8_t get_btn_index(struct matrix_keypad *keypad, int btn_key)
+{
+	uint8_t i;
+
+	for (i = 0; i < keypad->pdata->num_of_buttons; i++) {
+		if (keypad->button_array[i].key == btn_key)
+			break;
+	}
+	return i;
+}
+
+static void set_btn_state_by_hw(struct button *btn, bool buttonstate)
+{
+	btn->state.current_state = buttonstate;
+}
+
+static void poll_prepare(struct matrix_keypad *keypad)
+{
+	const struct matrix_keypad_platform_data *pdata = keypad->pdata;
+
+	keypad->current_line_gpio = 0;
+	activate_line_driving(pdata, (int)keypad->current_line_gpio, true);
+}
+
+static void poll_process(struct matrix_keypad *keypad,
+			 struct input_dev *input_dev)
+{
+	const struct matrix_keypad_platform_data *pdata = keypad->pdata;
+	bool btn_changes_occured = false;
+	int btn_index;
+
+	for (btn_index = 0; btn_index < pdata->num_of_buttons; btn_index++) {
+		btn_changes_occured |=
+			check_button_changes(&keypad->button_array[btn_index]);
+	}
+
+	if (btn_changes_occured)
+		process_button_events(keypad, input_dev);
+
+	keypad->current_line_gpio = 0;
+}
+
+static void poll_update(struct matrix_keypad *keypad,
+			struct input_dev *input_dev)
+{
+	const struct matrix_keypad_platform_data *pdata = keypad->pdata;
+	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 = (keypad->current_line_gpio * pdata->num_line_gpios) + i;
+		btn_key = pdata->keycode[index];
+		btn_keylines[i] = false;
+
+		if ((btn_key != pdata->keycode[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 = (keypad->current_line_gpio *
+				 pdata->num_line_gpios) +
+				i;
+			btn_key = pdata->keycode[index];
+			if (btn_key != pdata->keycode[0]) {
+				btn_index = get_btn_index(keypad, btn_key);
+				set_btn_state_by_hw(
+					&keypad->button_array[btn_index],
+					btn_keylines[i]);
+			}
+		}
+	}
+
+	kfree(btn_keylines);
+	activate_line_driving(pdata, (int)keypad->current_line_gpio, false);
+	keypad->current_line_gpio++;
+	activate_line_driving(
+		pdata, (int)(keypad->current_line_gpio % pdata->num_line_gpios),
+		true);
+}
+
+static void matrix_keypad_poll(struct input_dev *input)
+{
+	struct matrix_keypad *keypad = input_get_drvdata(input);
+	struct input_dev *input_dev = keypad->input_dev;
+
+	if (keypad->stopped == false) {
+		if (keypad->current_line_gpio ==
+		    keypad->pdata->num_line_gpios) {
+			poll_process(keypad, input_dev);
+		} else {
+			poll_update(keypad, input_dev);
+		}
+	}
+}
+
 /*
  * This gets the keys from keyboard and reports it to input subsystem
  */
@@ -127,7 +413,6 @@  static void matrix_keypad_scan(struct work_struct *work)
 
 	/* assert each column and read the row status out */
 	for (col = 0; col < pdata->num_col_gpios; col++) {
-
 		activate_col(pdata, col, true);
 
 		for (row = 0; row < pdata->num_row_gpios; row++)
@@ -150,8 +435,7 @@  static void matrix_keypad_scan(struct work_struct *work)
 
 			code = MATRIX_SCAN_CODE(row, col, keypad->row_shift);
 			input_event(input_dev, EV_MSC, MSC_SCAN, code);
-			input_report_key(input_dev,
-					 keycodes[code],
+			input_report_key(input_dev, keycodes[code],
 					 new_state[col] & (1 << row));
 		}
 	}
@@ -186,7 +470,7 @@  static irqreturn_t matrix_keypad_interrupt(int irq, void *id)
 	disable_row_irqs(keypad);
 	keypad->scan_pending = true;
 	schedule_delayed_work(&keypad->work,
-		msecs_to_jiffies(keypad->pdata->debounce_ms));
+			      msecs_to_jiffies(keypad->pdata->debounce_ms));
 
 out:
 	spin_unlock_irqrestore(&keypad->lock, flags);
@@ -204,7 +488,8 @@  static int matrix_keypad_start(struct input_dev *dev)
 	 * 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);
+	if (keypad->pdata->mode == GENERIC)
+		schedule_delayed_work(&keypad->work, 0);
 
 	return 0;
 }
@@ -217,7 +502,9 @@  static void matrix_keypad_stop(struct input_dev *dev)
 	keypad->stopped = true;
 	spin_unlock_irq(&keypad->lock);
 
-	flush_delayed_work(&keypad->work);
+	if (keypad->pdata->mode == GENERIC)
+		flush_delayed_work(&keypad->work);
+
 	/*
 	 * matrix_keypad_scan() will leave IRQs enabled;
 	 * we should disable them now.
@@ -236,7 +523,6 @@  static void matrix_keypad_enable_wakeup(struct matrix_keypad *keypad)
 		if (enable_irq_wake(pdata->clustered_irq) == 0)
 			keypad->gpio_all_disabled = true;
 	} else {
-
 		for (i = 0; i < pdata->num_row_gpios; i++) {
 			if (!test_bit(i, keypad->disabled_gpios)) {
 				gpio = pdata->row_gpios[i];
@@ -296,8 +582,8 @@  static int matrix_keypad_resume(struct device *dev)
 }
 #endif
 
-static SIMPLE_DEV_PM_OPS(matrix_keypad_pm_ops,
-			 matrix_keypad_suspend, matrix_keypad_resume);
+static SIMPLE_DEV_PM_OPS(matrix_keypad_pm_ops, matrix_keypad_suspend,
+			 matrix_keypad_resume);
 
 static int matrix_keypad_init_gpio(struct platform_device *pdev,
 				   struct matrix_keypad *keypad)
@@ -316,9 +602,9 @@  static int matrix_keypad_init_gpio(struct platform_device *pdev,
 
 	if (pdata->clustered_irq > 0) {
 		err = request_any_context_irq(pdata->clustered_irq,
-				matrix_keypad_interrupt,
-				pdata->clustered_irq_flags,
-				"matrix-keypad", keypad);
+					      matrix_keypad_interrupt,
+					      pdata->clustered_irq_flags,
+					      "matrix-keypad", keypad);
 		if (err < 0) {
 			dev_err(&pdev->dev,
 				"Unable to acquire clustered interrupt\n");
@@ -327,11 +613,10 @@  static int matrix_keypad_init_gpio(struct platform_device *pdev,
 	} else {
 		for (i = 0; i < pdata->num_row_gpios; i++) {
 			err = request_any_context_irq(
-					gpiod_to_irq(pdata->row_gpios[i]),
-					matrix_keypad_interrupt,
-					IRQF_TRIGGER_RISING |
-					IRQF_TRIGGER_FALLING,
-					"matrix-keypad", keypad);
+				gpiod_to_irq(pdata->row_gpios[i]),
+				matrix_keypad_interrupt,
+				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+				"matrix-keypad", keypad);
 			if (err < 0) {
 				dev_err(&pdev->dev,
 					"Unable to acquire interrupt for GPIO line %i\n",
@@ -361,30 +646,42 @@  static void matrix_keypad_free_gpio(struct matrix_keypad *keypad)
 {
 	const struct matrix_keypad_platform_data *pdata = keypad->pdata;
 	int i;
-
-	if (pdata->clustered_irq > 0) {
-		free_irq(pdata->clustered_irq, keypad);
+	if (pdata->mode == REDUCED) {
+		for (i = 0; i < pdata->num_line_gpios; i++)
+			gpiod_put(pdata->desc_gpios[i]);
 	} else {
-		for (i = 0; i < pdata->num_row_gpios; i++)
-			free_irq(gpiod_to_irq(pdata->row_gpios[i]), keypad);
-	}
+		if (pdata->clustered_irq > 0) {
+			free_irq(pdata->clustered_irq, keypad);
+		} else {
+			for (i = 0; i < pdata->num_row_gpios; i++)
+				free_irq(gpiod_to_irq(pdata->row_gpios[i]),
+					 keypad);
+		}
 
-	for (i = 0; i < pdata->num_row_gpios; i++)
-		gpiod_put(pdata->row_gpios[i]);
+		for (i = 0; i < pdata->num_row_gpios; i++)
+			gpiod_put(pdata->row_gpios[i]);
 
-	for (i = 0; i < pdata->num_col_gpios; i++)
-		gpiod_put(pdata->col_gpios[i]);
+		for (i = 0; i < pdata->num_col_gpios; i++)
+			gpiod_put(pdata->col_gpios[i]);
+	}
 }
 
 #ifdef CONFIG_OF
 static struct matrix_keypad_platform_data *
 matrix_keypad_parse_dt(struct device *dev)
 {
+	const struct of_device_id *of_id =
+		of_match_device(matrix_keypad_dt_match, dev);
 	struct matrix_keypad_platform_data *pdata;
 	struct device_node *np = dev->of_node;
+	const struct keypad_info *info = of_id->data;
 	struct gpio_desc **gpios;
 	struct gpio_desc *desc;
 	int ret, i, nrow, ncol;
+	int8_t *keycode;
+	uint32_t *ptr;
+	int num_gpio;
+	int keymap;
 
 	if (!np) {
 		dev_err(dev, "device lacks DT data\n");
@@ -397,11 +694,23 @@  matrix_keypad_parse_dt(struct device *dev)
 		return ERR_PTR(-ENOMEM);
 	}
 
-	pdata->num_row_gpios = nrow = gpiod_count(dev, "row");
-	pdata->num_col_gpios = ncol = gpiod_count(dev, "col");
-	if (nrow <= 0 || ncol <= 0) {
-		dev_err(dev, "number of keypad rows/columns not specified\n");
-		return ERR_PTR(-EINVAL);
+	pdata->mode = info->mode;
+
+	if (pdata->mode == REDUCED) {
+		num_gpio = gpiod_count(dev, "line");
+		if (num_gpio <= 0) {
+			dev_err(dev, "number of gpio line not specified\n");
+			return ERR_PTR(-EINVAL);
+		}
+	} else {
+		pdata->num_row_gpios = nrow = gpiod_count(dev, "row");
+		pdata->num_col_gpios = ncol = gpiod_count(dev, "col");
+
+		if (nrow <= 0 || ncol <= 0) {
+			dev_err(dev,
+				"number of keypad rows/columns not specified\n");
+			return ERR_PTR(-EINVAL);
+		}
 	}
 
 	if (of_get_property(np, "linux,no-autorepeat", NULL))
@@ -415,34 +724,90 @@  matrix_keypad_parse_dt(struct device *dev)
 
 	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);
-
-	gpios = devm_kcalloc(dev,
-			     pdata->num_row_gpios + pdata->num_col_gpios,
-			     sizeof(*gpios),
-			     GFP_KERNEL);
-	if (!gpios) {
-		dev_err(dev, "could not allocate memory for gpios\n");
-		return ERR_PTR(-ENOMEM);
-	}
+			     &pdata->col_scan_delay_us);
+	if (pdata->mode == REDUCED) {
+		gpios = devm_kcalloc(dev, num_gpio, sizeof(*gpios), GFP_KERNEL);
+		if (!gpios)
+			return ERR_PTR(-ENOMEM);
+
+		for (i = 0; i < num_gpio; i++) {
+			desc = devm_gpiod_get_index(dev, "line", i, GPIOD_ASIS);
+			if (IS_ERR(desc))
+				return ERR_PTR(-EPROBE_DEFER);
+			gpios[i] = desc;
+		}
 
-	for (i = 0; i < nrow; i++) {
-		desc = devm_gpiod_get_index(dev, "row", i, GPIOD_IN);
-		if (IS_ERR(desc))
-			return ERR_PTR(ret);
-		gpios[i] = desc;
-	}
+		pdata->desc_gpios = gpios;
+		pdata->num_line_gpios = num_gpio;
+
+		if (!gpiod_is_active_low(pdata->desc_gpios[0]))
+			pdata->active_low = true;
+	} else {
+		gpios = devm_kcalloc(
+			dev, pdata->num_row_gpios + pdata->num_col_gpios,
+			sizeof(*gpios), GFP_KERNEL);
+		if (!gpios) {
+			dev_err(dev, "could not allocate memory for gpios\n");
+			return ERR_PTR(-ENOMEM);
+		}
+
+		for (i = 0; i < nrow; i++) {
+			desc = devm_gpiod_get_index(dev, "row", i, GPIOD_IN);
+			if (IS_ERR(desc))
+				return ERR_PTR(ret);
+			gpios[i] = desc;
+		}
+
+		for (i = 0; i < ncol; i++) {
+			desc = devm_gpiod_get_index(dev, "col", i, GPIOD_IN);
+			if (IS_ERR(desc))
+				return ERR_PTR(ret);
+			gpios[nrow + i] = desc;
+		}
 
-	for (i = 0; i < ncol; i++) {
-		desc = devm_gpiod_get_index(dev, "col", i, GPIOD_IN);
-		if (IS_ERR(desc))
-			return ERR_PTR(ret);
-		gpios[nrow + i] = desc;
+		pdata->row_gpios = gpios;
+		pdata->col_gpios = &gpios[pdata->num_row_gpios];
 	}
 
-	pdata->row_gpios = gpios;
-	pdata->col_gpios = &gpios[pdata->num_row_gpios];
+	if (pdata->mode == REDUCED) {
+		if (of_property_read_u32(np, "poll-interval-ms",
+					 &pdata->poll_interval_ms))
+			pdata->poll_interval_ms = 10;
 
+		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);
+		}
+
+		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 invalid");
+			return ERR_PTR(-ENXIO);
+		}
+
+		ptr = devm_kzalloc(dev, (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");
+			return ERR_PTR(-EINVAL);
+		}
+
+		keycode = devm_kzalloc(dev, (keymap * sizeof(int8_t)),
+				       GFP_KERNEL);
+		if (!keycode)
+			return ERR_PTR(-ENOMEM);
+
+		pdata->keycode = keycode;
+		for (i = 0; i < keymap; i++)
+			pdata->keycode[i] = KEYCODE(ptr[i]);
+	}
 	return pdata;
 }
 #else
@@ -483,22 +848,58 @@  static int matrix_keypad_probe(struct platform_device *pdev)
 	keypad->pdata = pdata;
 	keypad->row_shift = get_count_order(pdata->num_col_gpios);
 	keypad->stopped = true;
-	INIT_DELAYED_WORK(&keypad->work, matrix_keypad_scan);
+
+	if (pdata->mode == REDUCED) {
+		keypad->button_array = devm_kzalloc(
+			&pdev->dev,
+			sizeof(struct button) * (pdata->num_of_buttons),
+			GFP_KERNEL);
+		if (!keypad->button_array) {
+			dev_err(&pdev->dev,
+				"could not allocate memory for button array\n");
+			goto err_free_mem;
+			;
+		}
+
+		poll_prepare(keypad);
+
+		err = input_setup_polling(input_dev, matrix_keypad_poll);
+		if (err) {
+			dev_err(&pdev->dev,
+				"unable to set up polling, err=%d\n", err);
+			return err;
+		}
+
+		input_set_poll_interval(input_dev, pdata->poll_interval_ms);
+	} else {
+		INIT_DELAYED_WORK(&keypad->work, 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		= matrix_keypad_start;
-	input_dev->close	= matrix_keypad_stop;
-
-	err = matrix_keypad_build_keymap(pdata->keymap_data, NULL,
-					 pdata->num_row_gpios,
-					 pdata->num_col_gpios,
-					 NULL, input_dev);
-	if (err) {
-		dev_err(&pdev->dev, "failed to build keymap\n");
-		goto err_free_mem;
+	input_dev->name = pdev->name;
+	input_dev->id.bustype = BUS_HOST;
+	input_dev->dev.parent = &pdev->dev;
+	input_dev->open = matrix_keypad_start;
+	input_dev->close = matrix_keypad_stop;
+
+	if (pdata->mode == REDUCED) {
+		err = matrix_keypad_build_keymap(pdata->keymap_data, NULL,
+						 pdata->num_line_gpios,
+						 pdata->num_line_gpios, NULL,
+						 input_dev);
+		if (err) {
+			dev_err(&pdev->dev, "failed to build keymap for reduced mode\n");
+			goto err_free_mem;
+		}
+	} else {
+		err = matrix_keypad_build_keymap(pdata->keymap_data, NULL,
+						 pdata->num_row_gpios,
+						 pdata->num_col_gpios, NULL,
+						 input_dev);
+		if (err) {
+			dev_err(&pdev->dev, "failed to build keymap for generic mode\n");
+			goto err_free_mem;
+		}
 	}
 
 	if (!pdata->no_autorepeat)
@@ -506,9 +907,13 @@  static int matrix_keypad_probe(struct platform_device *pdev)
 	input_set_capability(input_dev, EV_MSC, MSC_SCAN);
 	input_set_drvdata(input_dev, keypad);
 
-	err = matrix_keypad_init_gpio(pdev, keypad);
-	if (err)
-		goto err_free_mem;
+	if (pdata->mode == REDUCED) {
+		button_hdl_init(keypad);
+	} else {
+		err = matrix_keypad_init_gpio(pdev, keypad);
+		if (err)
+			goto err_free_mem;
+	}
 
 	err = input_register_device(keypad->input_dev);
 	if (err)
@@ -538,14 +943,6 @@  static int matrix_keypad_remove(struct platform_device *pdev)
 	return 0;
 }
 
-#ifdef CONFIG_OF
-static const struct of_device_id matrix_keypad_dt_match[] = {
-	{ .compatible = "gpio-matrix-keypad" },
-	{ }
-};
-MODULE_DEVICE_TABLE(of, matrix_keypad_dt_match);
-#endif
-
 static struct platform_driver matrix_keypad_driver = {
 	.probe		= matrix_keypad_probe,
 	.remove		= matrix_keypad_remove,
diff --git a/include/linux/input/matrix_keypad.h b/include/linux/input/matrix_keypad.h
index 8ad7d4626e62..d5ba9ec21752 100644
--- a/include/linux/input/matrix_keypad.h
+++ b/include/linux/input/matrix_keypad.h
@@ -6,18 +6,22 @@ 
 #include <linux/input.h>
 #include <linux/of.h>
 
-#define MATRIX_MAX_ROWS		32
-#define MATRIX_MAX_COLS		32
+#define MATRIX_MAX_ROWS 32
+#define MATRIX_MAX_COLS 32
 
-#define KEY(row, col, val)	((((row) & (MATRIX_MAX_ROWS - 1)) << 24) |\
-				 (((col) & (MATRIX_MAX_COLS - 1)) << 16) |\
-				 ((val) & 0xffff))
+#define KEY(row, col, val)                                                     \
+	((((row) & (MATRIX_MAX_ROWS - 1)) << 24) |                             \
+	 (((col) & (MATRIX_MAX_COLS - 1)) << 16) | ((val)&0xffff))
 
-#define KEY_ROW(k)		(((k) >> 24) & 0xff)
-#define KEY_COL(k)		(((k) >> 16) & 0xff)
-#define KEY_VAL(k)		((k) & 0xffff)
+#define KEY_ROW(k) (((k) >> 24) & 0xff)
+#define KEY_COL(k) (((k) >> 16) & 0xff)
+#define KEY_VAL(k) ((k)&0xffff)
 
-#define MATRIX_SCAN_CODE(row, col, row_shift)	(((row) << (row_shift)) + (col))
+#define MATRIX_SCAN_CODE(row, col, row_shift) (((row) << (row_shift)) + (col))
+#define KEYCODE(keymap) (keymap & 0xFFFF)
+
+#define GENERIC (1) /* keypad mode generic */
+#define REDUCED (2) /* keypad mode reduced */
 
 /**
  * struct matrix_keymap_data - keymap for matrix keyboards
@@ -30,7 +34,7 @@ 
  */
 struct matrix_keymap_data {
 	const uint32_t *keymap;
-	unsigned int	keymap_size;
+	unsigned int keymap_size;
 };
 
 /**
@@ -38,6 +42,7 @@  struct matrix_keymap_data {
  * @keymap_data: pointer to &matrix_keymap_data
  * @row_gpios: pointer to array of gpio numbers representing rows
  * @col_gpios: pointer to array of gpio numbers reporesenting colums
+ * @desc_gpios: actual number of gpio used devide in reduced mode
  * @num_row_gpios: actual number of row gpios used by device
  * @num_col_gpios: actual number of col gpios used by device
  * @col_scan_delay_us: delay, measured in microseconds, that is
@@ -46,6 +51,12 @@  struct matrix_keymap_data {
  * @clustered_irq: may be specified if interrupts of all row/column GPIOs
  *	are bundled to one single irq
  * @clustered_irq_flags: flags that are needed for the clustered irq
+ * @num_line_gpios: actual number of gpio line in reduced mode
+ * @num_of_buttons: number of physical buttons on keypad
+ *	in reduced mode
+ * @poll_interval_ms: interval to poll gpio in reduced mode
+ * @keycode: hold the keycode value
+ * @mode: flag to set mode
  * @active_low: gpio polarity
  * @wakeup: controls whether the device should be set up as wakeup
  *	source
@@ -61,32 +72,37 @@  struct matrix_keypad_platform_data {
 
 	struct gpio_desc **row_gpios;
 	struct gpio_desc **col_gpios;
+	struct gpio_desc **desc_gpios;
 
-	unsigned int	num_row_gpios;
-	unsigned int	num_col_gpios;
+	unsigned int num_row_gpios;
+	unsigned int num_col_gpios;
 
-	unsigned int	col_scan_delay_us;
+	unsigned int col_scan_delay_us;
 
 	/* key debounce interval in milli-second */
-	unsigned int	debounce_ms;
+	unsigned int debounce_ms;
+
+	unsigned int clustered_irq;
+	unsigned int clustered_irq_flags;
+	unsigned int num_line_gpios;
+	unsigned int num_of_buttons;
+	unsigned int poll_interval_ms;
 
-	unsigned int	clustered_irq;
-	unsigned int	clustered_irq_flags;
+	int8_t *keycode;
+	int8_t mode;
 
-	bool		active_low;
-	bool		wakeup;
-	bool		no_autorepeat;
-	bool		drive_inactive_cols;
+	bool active_low;
+	bool wakeup;
+	bool no_autorepeat;
+	bool drive_inactive_cols;
 };
 
 int matrix_keypad_build_keymap(const struct matrix_keymap_data *keymap_data,
-			       const char *keymap_name,
-			       unsigned int rows, unsigned int cols,
-			       unsigned short *keymap,
+			       const char *keymap_name, unsigned int rows,
+			       unsigned int cols, unsigned short *keymap,
 			       struct input_dev *input_dev);
-int matrix_keypad_parse_properties(struct device *dev,
-				   unsigned int *rows, unsigned int *cols);
-
+int matrix_keypad_parse_properties(struct device *dev, unsigned int *rows,
+				   unsigned int *cols);
 #define matrix_keypad_parse_of_params matrix_keypad_parse_properties
 
 #endif /* _MATRIX_KEYPAD_H */