From ca474b95ed43801d9454f7ae7631abf03ed220b5 Mon Sep 17 00:00:00 2001 From: Yingyuan Zhu Date: Thu, 28 Jun 2018 15:54:02 +0800 Subject: [PATCH] keypad: gpio key drive optimization PD#169209: keypad: gpio key drive optimization 1.replace the old interface gpio_ with gpiod_. 2.add irq detection mode,can be configured as interrupt mode or polling mode through DTS. 3.add keys to eliminate jitter. 4.remove useless code. Change-Id: I81ef3cd0061e36faa10f58932588ff5031208b25 Signed-off-by: Yingyuan Zhu --- arch/arm64/boot/dts/amlogic/g12a_s905d2_u200.dts | 6 +- .../arm64/boot/dts/amlogic/g12a_s905d2_u200_1g.dts | 6 +- .../dts/amlogic/g12a_s905d2_u200_buildroot.dts | 6 +- .../dts/amlogic/g12a_s905d2_u200_drm_buildroot.dts | 7 +- arch/arm64/boot/dts/amlogic/g12a_s905d2_u220.dts | 6 +- arch/arm64/boot/dts/amlogic/g12b_a311d_skt.dts | 3 +- arch/arm64/boot/dts/amlogic/g12b_a311d_w400.dts | 3 +- .../boot/dts/amlogic/g12b_a311d_w400_buildroot.dts | 3 +- arch/arm64/boot/dts/amlogic/gxl_p212_1g.dts | 6 +- arch/arm64/boot/dts/amlogic/gxl_p212_1g_hd.dts | 6 +- arch/arm64/boot/dts/amlogic/gxl_p212_2g.dts | 6 +- arch/arm64/boot/dts/amlogic/gxl_p230_2g.dts | 3 +- arch/arm64/boot/dts/amlogic/gxl_p241_1g.dts | 6 +- arch/arm64/boot/dts/amlogic/gxl_p241_v2-1g.dts | 6 +- drivers/amlogic/input/keyboard/gpio_keypad.c | 617 ++++++++------------- drivers/amlogic/input/keyboard/gpio_keypad_old.c | 505 +++++++++++++++++ 16 files changed, 750 insertions(+), 445 deletions(-) create mode 100644 drivers/amlogic/input/keyboard/gpio_keypad_old.c diff --git a/arch/arm64/boot/dts/amlogic/g12a_s905d2_u200.dts b/arch/arm64/boot/dts/amlogic/g12a_s905d2_u200.dts index 8ac09a1..6754cd8 100644 --- a/arch/arm64/boot/dts/amlogic/g12a_s905d2_u200.dts +++ b/arch/arm64/boot/dts/amlogic/g12a_s905d2_u200.dts @@ -290,10 +290,8 @@ key_num = <1>; key_name = "power"; key_code = <116>; - key_pin = <&gpio_ao GPIOAO_3 GPIO_ACTIVE_HIGH>; - interrupts = <0 70 1 - 0 71 2>; - interrupt-names = "irq_keyup", "irq_keydown"; + key-gpios = <&gpio_ao GPIOAO_3 GPIO_ACTIVE_HIGH>; + detect_mode = <0>;/*0:polling mode, 1:irq mode*/ }; adc_keypad { diff --git a/arch/arm64/boot/dts/amlogic/g12a_s905d2_u200_1g.dts b/arch/arm64/boot/dts/amlogic/g12a_s905d2_u200_1g.dts index 4f2e6ec..a93ec82 100644 --- a/arch/arm64/boot/dts/amlogic/g12a_s905d2_u200_1g.dts +++ b/arch/arm64/boot/dts/amlogic/g12a_s905d2_u200_1g.dts @@ -281,10 +281,8 @@ key_num = <1>; key_name = "power"; key_code = <116>; - key_pin = <&gpio_ao GPIOAO_3 GPIO_ACTIVE_HIGH>; - interrupts = <0 70 1 - 0 71 2>; - interrupt-names = "irq_keyup", "irq_keydown"; + key-gpios = <&gpio_ao GPIOAO_3 GPIO_ACTIVE_HIGH>; + detect_mode = <0>;/*0:polling mode, 1:irq mode*/ }; adc_keypad { diff --git a/arch/arm64/boot/dts/amlogic/g12a_s905d2_u200_buildroot.dts b/arch/arm64/boot/dts/amlogic/g12a_s905d2_u200_buildroot.dts index 365533b..f5332c7 100644 --- a/arch/arm64/boot/dts/amlogic/g12a_s905d2_u200_buildroot.dts +++ b/arch/arm64/boot/dts/amlogic/g12a_s905d2_u200_buildroot.dts @@ -275,10 +275,8 @@ key_num = <1>; key_name = "power"; key_code = <116>; - key_pin = <&gpio_ao GPIOAO_3 GPIO_ACTIVE_HIGH>; - interrupts = <0 70 1 - 0 71 2>; - interrupt-names = "irq_keyup", "irq_keydown"; + key-gpios = <&gpio_ao GPIOAO_3 GPIO_ACTIVE_HIGH>; + detect_mode = <0>;/*0:polling mode, 1:irq mode*/ }; adc_keypad { diff --git a/arch/arm64/boot/dts/amlogic/g12a_s905d2_u200_drm_buildroot.dts b/arch/arm64/boot/dts/amlogic/g12a_s905d2_u200_drm_buildroot.dts index f46a70f..c774dcb 100644 --- a/arch/arm64/boot/dts/amlogic/g12a_s905d2_u200_drm_buildroot.dts +++ b/arch/arm64/boot/dts/amlogic/g12a_s905d2_u200_drm_buildroot.dts @@ -276,11 +276,8 @@ key_num = <1>; key_name = "power"; key_code = <116>; - key_pin = <&gpio_ao GPIOAO_3 GPIO_ACTIVE_HIGH>; - interrupts = <0 70 1 - 0 71 2>; - interrupt-names = "irq_keyup", "irq_keydown"; - }; + key-gpios = <&gpio_ao GPIOAO_3 GPIO_ACTIVE_HIGH>; + detect_mode = <0>;/*0:polling mode, 1:irq mode*/ adc_keypad { compatible = "amlogic, adc_keypad"; diff --git a/arch/arm64/boot/dts/amlogic/g12a_s905d2_u220.dts b/arch/arm64/boot/dts/amlogic/g12a_s905d2_u220.dts index 017abf0..ca6b4c6 100644 --- a/arch/arm64/boot/dts/amlogic/g12a_s905d2_u220.dts +++ b/arch/arm64/boot/dts/amlogic/g12a_s905d2_u220.dts @@ -272,10 +272,8 @@ key_num = <1>; key_name = "power"; key_code = <116>; - key_pin = <&gpio_ao GPIOAO_3 GPIO_ACTIVE_HIGH>; - interrupts = <0 70 1 - 0 71 2>; - interrupt-names = "irq_keyup", "irq_keydown"; + key-gpios = <&gpio_ao GPIOAO_3 GPIO_ACTIVE_HIGH>; + detect_mode = <0>;/*0:polling mode, 1:irq mode*/ }; adc_keypad { diff --git a/arch/arm64/boot/dts/amlogic/g12b_a311d_skt.dts b/arch/arm64/boot/dts/amlogic/g12b_a311d_skt.dts index e52abbd..1ed8102 100644 --- a/arch/arm64/boot/dts/amlogic/g12b_a311d_skt.dts +++ b/arch/arm64/boot/dts/amlogic/g12b_a311d_skt.dts @@ -281,7 +281,8 @@ key_num = <1>; key_name = "power"; key_code = <116>; - key_pin = <&gpio_ao GPIOAO_3 GPIO_ACTIVE_HIGH>; + key-gpios = <&gpio_ao GPIOAO_3 GPIO_ACTIVE_HIGH>; + detect_mode = <0>;/*0:polling mode, 1:irq mode*/ }; adc_keypad { diff --git a/arch/arm64/boot/dts/amlogic/g12b_a311d_w400.dts b/arch/arm64/boot/dts/amlogic/g12b_a311d_w400.dts index d046800..7949c00 100644 --- a/arch/arm64/boot/dts/amlogic/g12b_a311d_w400.dts +++ b/arch/arm64/boot/dts/amlogic/g12b_a311d_w400.dts @@ -260,7 +260,8 @@ key_num = <1>; key_name = "power"; key_code = <116>; - key_pin = <&gpio_ao GPIOAO_3 GPIO_ACTIVE_HIGH>; + key-gpios = <&gpio_ao GPIOAO_3 GPIO_ACTIVE_HIGH>; + detect_mode = <0>;/*0:polling mode, 1:irq mode*/ }; adc_keypad { diff --git a/arch/arm64/boot/dts/amlogic/g12b_a311d_w400_buildroot.dts b/arch/arm64/boot/dts/amlogic/g12b_a311d_w400_buildroot.dts index 8139f07..0a330f2 100644 --- a/arch/arm64/boot/dts/amlogic/g12b_a311d_w400_buildroot.dts +++ b/arch/arm64/boot/dts/amlogic/g12b_a311d_w400_buildroot.dts @@ -368,7 +368,8 @@ key_num = <1>; key_name = "power"; key_code = <116>; - key_pin = <&gpio_ao GPIOAO_3 GPIO_ACTIVE_HIGH>; + key-gpios = <&gpio_ao GPIOAO_3 GPIO_ACTIVE_HIGH>; + detect_mode = <0>;/*0:polling mode, 1:irq mode*/ }; adc_keypad { diff --git a/arch/arm64/boot/dts/amlogic/gxl_p212_1g.dts b/arch/arm64/boot/dts/amlogic/gxl_p212_1g.dts index 36939bb..e219ad0 100644 --- a/arch/arm64/boot/dts/amlogic/gxl_p212_1g.dts +++ b/arch/arm64/boot/dts/amlogic/gxl_p212_1g.dts @@ -744,10 +744,8 @@ key_num = <1>; key_name = "power"; key_code = <116>; - key_pin = <&gpio_ao GPIOAO_2 GPIO_ACTIVE_HIGH>; - interrupts = <0 70 1 - 0 71 2>; - interrupt-names = "irq_keyup", "irq_keydown"; + key-gpios = <&gpio_ao GPIOAO_2 GPIO_ACTIVE_HIGH>; + detect_mode = <0>;/*0:polling mode, 1:irq mode*/ }; meson-fb { compatible = "amlogic, meson-gxl"; diff --git a/arch/arm64/boot/dts/amlogic/gxl_p212_1g_hd.dts b/arch/arm64/boot/dts/amlogic/gxl_p212_1g_hd.dts index 2c40f70..3a34777 100644 --- a/arch/arm64/boot/dts/amlogic/gxl_p212_1g_hd.dts +++ b/arch/arm64/boot/dts/amlogic/gxl_p212_1g_hd.dts @@ -734,10 +734,8 @@ key_num = <1>; key_name = "power"; key_code = <116>; - key_pin = <&gpio_ao GPIOAO_2 GPIO_ACTIVE_HIGH>; - interrupts = <0 70 1 - 0 71 2>; - interrupt-names = "irq_keyup", "irq_keydown"; + key-gpios = <&gpio_ao GPIOAO_2 GPIO_ACTIVE_HIGH>; + detect_mode = <0>;/*0:polling mode, 1:irq mode*/ }; meson-fb { compatible = "amlogic, meson-gxl"; diff --git a/arch/arm64/boot/dts/amlogic/gxl_p212_2g.dts b/arch/arm64/boot/dts/amlogic/gxl_p212_2g.dts index 98d49d7..01e81fb 100644 --- a/arch/arm64/boot/dts/amlogic/gxl_p212_2g.dts +++ b/arch/arm64/boot/dts/amlogic/gxl_p212_2g.dts @@ -508,10 +508,8 @@ key_num = <1>; key_name = "power"; key_code = <116>; - key_pin = <&gpio_ao GPIOAO_2 GPIO_ACTIVE_HIGH>; - interrupts = < 0 70 1 - 0 71 2>; - interrupt-names = "irq_keyup", "irq_keydown"; + key-gpios = <&gpio_ao GPIOAO_2 GPIO_ACTIVE_HIGH>; + detect_mode = <0>;/*0:polling mode, 1:irq mode*/ }; aml_sensor0: aml-sensor@0 { diff --git a/arch/arm64/boot/dts/amlogic/gxl_p230_2g.dts b/arch/arm64/boot/dts/amlogic/gxl_p230_2g.dts index c8e8b39..3ed702f 100644 --- a/arch/arm64/boot/dts/amlogic/gxl_p230_2g.dts +++ b/arch/arm64/boot/dts/amlogic/gxl_p230_2g.dts @@ -495,9 +495,10 @@ key_num = <1>; key_name = "power"; key_code = <116>; - key_pin = <&gpio_ao GPIOAO_2 GPIO_ACTIVE_HIGH>;/*"GPIOAO_2";*/ irq_keyup = <6>; irq_keydown = <7>; + key-gpios = <&gpio_ao GPIOAO_2 GPIO_ACTIVE_HIGH>; + detect_mode = <0>;/*0:polling mode, 1:irq mode*/ }; diff --git a/arch/arm64/boot/dts/amlogic/gxl_p241_1g.dts b/arch/arm64/boot/dts/amlogic/gxl_p241_1g.dts index c5a2755..ed5256c 100644 --- a/arch/arm64/boot/dts/amlogic/gxl_p241_1g.dts +++ b/arch/arm64/boot/dts/amlogic/gxl_p241_1g.dts @@ -804,10 +804,8 @@ key_num = <1>; key_name = "power"; key_code = <116>; - key_pin = <&gpio_ao GPIOAO_2 GPIO_ACTIVE_HIGH>; - interrupts = <0 70 1 - 0 71 2>; - interrupt-names = "irq_keyup", "irq_keydown"; + key-gpios = <&gpio_ao GPIOAO_2 GPIO_ACTIVE_HIGH>; + detect_mode = <0>;/*0:polling mode, 1:irq mode*/ }; meson-fb { compatible = "amlogic, meson-gxl"; diff --git a/arch/arm64/boot/dts/amlogic/gxl_p241_v2-1g.dts b/arch/arm64/boot/dts/amlogic/gxl_p241_v2-1g.dts index b43d901..49605c5 100644 --- a/arch/arm64/boot/dts/amlogic/gxl_p241_v2-1g.dts +++ b/arch/arm64/boot/dts/amlogic/gxl_p241_v2-1g.dts @@ -805,10 +805,8 @@ key_num = <1>; key_name = "power"; key_code = <116>; - key_pin = <&gpio_ao GPIOAO_2 GPIO_ACTIVE_HIGH>; - interrupts = <0 70 1 - 0 71 2>; - interrupt-names = "irq_keyup", "irq_keydown"; + key-gpios = <&gpio_ao GPIOAO_2 GPIO_ACTIVE_HIGH>; + detect_mode = <0>;/*0:polling mode, 1:irq mode*/ }; meson-fb { compatible = "amlogic, meson-gxl"; diff --git a/drivers/amlogic/input/keyboard/gpio_keypad.c b/drivers/amlogic/input/keyboard/gpio_keypad.c index 35abea9..44e3238 100644 --- a/drivers/amlogic/input/keyboard/gpio_keypad.c +++ b/drivers/amlogic/input/keyboard/gpio_keypad.c @@ -14,451 +14,290 @@ * more details. * */ - #include #include -#include -#include #include -#include -#include #include -#include -#include -#include -#include - -#include #include -#include -#include -#include -#include -#include +#include +#include #include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include "../../../gpio/gpiolib.h" - -#define OFFSET 24 - -#define MOD_NAME "gpio_key" #undef pr_fmt -#define pr_fmt(fmt) "gpio_key: " fmt +#define pr_fmt(fmt) "gpio-keypad: " fmt -struct gpio_key { - int code; /* input key code */ - const char *name; - int pin; /*pin number*/ - int status; /*0 up , 1 down*/ -}; +#define DEFAULT_SCAN_PERION 20 +#define DEFAULT_POLL_MODE 0 +#define KEY_JITTER_COUNT 1 -struct gpio_platform_data { - struct gpio_key *key; - int key_num; - int repeat_delay; - int repeat_period; - int irq_keyup; - int irq_keydown; +struct pin_desc { + int current_status; + struct gpio_desc *desc; + int irq_num; + u32 code; + const char *name; }; -struct kp { - struct input_dev *input; - struct timer_list timer; - int config_major; - char config_name[20]; - struct class *config_class; - struct device *config_dev; - struct gpio_key *keys; - int key_num; - struct work_struct work_update; +struct gpio_keypad { + int key_size; + int use_irq;//1:irq mode ; 0:polling mode + int scan_period; + int current_irq; + int count; + int index; + struct pin_desc *key; + struct pin_desc *current_key; + struct timer_list polling_timer; + struct input_dev *input_dev; }; -static struct kp *gp_kp; - -int amlogic_gpio_name_map_num(struct platform_device *pdev, const char *value) -{ - return of_property_read_string(pdev->dev.of_node, "key_pin", &value); -} - - -static void kp_work(struct kp *kp) -{ - struct gpio_key *key; - int i; - int io_status; - - key = kp->keys; - for (i = 0; i < kp->key_num; i++) { - io_status = gpiod_get_value(gpio_to_desc(key->pin)); - if (io_status != key->status) { - if (io_status) { - dev_info(&kp->input->dev, - "key %d up\n", key->code); - input_report_key(kp->input, key->code, 0); - input_sync(kp->input); - } else { - dev_info(&kp->input->dev, - "key %d down\n", key->code); - input_report_key(kp->input, key->code, 1); - input_sync(kp->input); - } - key->status = io_status; - } - key++; - } -} - -static void update_work_func(struct work_struct *work) -{ - struct kp *kp_data = container_of(work, struct kp, work_update); - - kp_work(kp_data); -} - -/* - * What we do here is just for loss wakeup key when suspend. - * In suspend routine , the intr is disable. - * we need do more things to adapt the gpio change. - */ -int det_pwr_key(void) -{ - /*return aml_read_reg32(P_AO_IRQ_STAT) & (1<<8);*/ - return 0; -} -/*Enable gpio interrupt for AO domain interrupt*/ -void set_pwr_key(void) +static irqreturn_t gpio_irq_handler(int irq, void *data) { - /* aml_write_reg32(P_AO_IRQ_GPIO_REG, - * aml_read_reg32(P_AO_IRQ_GPIO_REG)|(1<<18)|(1<<16)|(0x3<<0)); - * aml_write_reg32(P_AO_IRQ_MASK_FIQ_SEL, - * aml_read_reg32(P_AO_IRQ_MASK_FIQ_SEL)|(1<<8)); - * aml_set_reg32_mask(P_AO_IRQ_STAT_CLR, 1<<8); - */ -} + struct gpio_keypad *keypad; -void clr_pwr_key(void) -{ - /*aml_set_reg32_mask(P_AO_IRQ_STAT_CLR, 1<<8);*/ -} - - -#ifdef USE_IRQ - -static irqreturn_t kp_isr(int irq, void *data) -{ - struct kp *kp_data = (struct kp *)data; - - queue_work(system_freezable_wq, &(kp_data->work_update)); - - /* if (!deep_suspend_flag) - * clr_pwr_key(); - */ + keypad = (struct gpio_keypad *)data; + keypad->current_irq = irq; + keypad->count++; + mod_timer(&(keypad->polling_timer), + jiffies+msecs_to_jiffies(20)); return IRQ_HANDLED; } -#else -void kp_timer_sr(unsigned long data) +static struct pin_desc *get_current_key(struct gpio_keypad *keypad) { - struct kp *kp_data = (struct kp *)data; + int i; - queue_work(system_freezable_wq, &(kp_data->work_update)); - mod_timer(&kp_data->timer, jiffies+msecs_to_jiffies(25)); + for (i = 0; i < keypad->key_size; i++) { + if (keypad->current_irq == keypad->key[i].irq_num) + return &(keypad->key[i]); + } + return NULL; } -#endif -static int gpio_key_config_open(struct inode *inode, struct file *file) +static void report_key_code(struct gpio_keypad *keypad, int key_status) { - file->private_data = gp_kp; - return 0; -} - -static int gpio_key_config_release(struct inode *inode, struct file *file) -{ - file->private_data = NULL; - return 0; + struct pin_desc *key; + + if (keypad->count < KEY_JITTER_COUNT) + keypad->count++; + else { + key = keypad->current_key; + key->current_status = gpiod_get_value(key->desc); + input_report_key(keypad->input_dev, + key->code, key_status); + input_sync(keypad->input_dev); + if (key_status) + dev_info(&(keypad->input_dev->dev), + "key %d down.\n", + key->code); + else + dev_info(&(keypad->input_dev->dev), + "key %d up.\n", + key->code); + keypad->count = 0; + } } - -static const struct file_operations keypad_fops = { - .owner = THIS_MODULE, - .open = gpio_key_config_open, - .release = gpio_key_config_release, -}; - -static int register_keypad_dev(struct kp *kp) +static void polling_timer_handler(unsigned long data) { - int ret = 0; - char name[] = "gpio_keyboard"; + struct gpio_keypad *keypad; + struct pin_desc *key; + int i; - strncpy(kp->config_name, name, sizeof(name)); - ret = register_chrdev(0, kp->config_name, &keypad_fops); - if (ret <= 0) { - dev_info(&kp->input->dev, "register char device error\n"); - return ret; + keypad = (struct gpio_keypad *)data; + if (keypad->use_irq) {//irq mode + keypad->current_key = get_current_key(keypad); + if (!(keypad->current_key)) + return; + key = keypad->current_key; + if (key->current_status != gpiod_get_value(key->desc)) + report_key_code(keypad, key->current_status); + else + keypad->count = 0; + if (key->current_status == 0) + mod_timer(&(keypad->polling_timer), + jiffies+msecs_to_jiffies(keypad->scan_period)); + } else {//polling mode + for (i = 0; i < keypad->key_size; i++) { + if (keypad->key[i].current_status != + gpiod_get_value(keypad->key[i].desc)) { + keypad->index = i; + keypad->current_key = &(keypad->key[i]); + report_key_code(keypad, + keypad->key[i].current_status); + } else if (keypad->index == i) + keypad->count = 0; + mod_timer(&(keypad->polling_timer), + jiffies+msecs_to_jiffies(keypad->scan_period)); + } } - kp->config_major = ret; - dev_info(&kp->input->dev, "gpio keypad major:%d\n", ret); - kp->config_class = class_create(THIS_MODULE, kp->config_name); - kp->config_dev = device_create(kp->config_class, NULL, - MKDEV(kp->config_major, 0), NULL, kp->config_name); - return ret; } -static int gpio_key_probe(struct platform_device *pdev) +static int meson_gpio_kp_probe(struct platform_device *pdev) { - const char *str; - struct kp *kp; - struct input_dev *input_dev; - int i, ret, key_size; - struct gpio_key *key; - struct gpio_platform_data *pdata = NULL; struct gpio_desc *desc; - int *key_param = NULL; - int state = -EINVAL; -#ifdef USE_IRQ - int irq_keyup; - int irq_keydown; -#endif - - int gpio_highz = 0; - - if (!pdev->dev.of_node) { - dev_info(&pdev->dev, "gpio_key: pdev->dev.of_node == NULL!\n"); - state = -EINVAL; - goto get_key_node_fail; - } - ret = of_property_read_u32(pdev->dev.of_node, "key_num", &key_size); - if (ret) { - dev_info(&pdev->dev, "gpio_key: failed to get key_num!\n"); - state = -EINVAL; - goto get_key_node_fail; - } + int ret, i; + struct input_dev *input_dev; + struct gpio_keypad *keypad; - pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); - if (!pdata) { - state = -EINVAL; - goto get_key_node_fail; + if (!(pdev->dev.of_node)) { + dev_err(&pdev->dev, + "pdev->dev.of_node == NULL!\n"); + return -EINVAL; } - - ret = of_property_read_bool(pdev->dev.of_node, "gpio_high_z"); + keypad = devm_kzalloc(&pdev->dev, + sizeof(struct gpio_keypad), GFP_KERNEL); + if (!keypad) + return -EINVAL; + ret = of_property_read_u32(pdev->dev.of_node, + "detect_mode", &(keypad->use_irq)); + if (ret) + //The default mode is polling. + keypad->use_irq = DEFAULT_POLL_MODE; + ret = of_property_read_u32(pdev->dev.of_node, + "scan_period", &(keypad->scan_period)); + if (ret) + //The default scan period is 20. + keypad->scan_period = DEFAULT_SCAN_PERION; + ret = of_property_read_u32(pdev->dev.of_node, + "key_num", &(keypad->key_size)); if (ret) { - gpio_highz = 1; - dev_info(&pdev->dev, "gpio request set to High-Z status\n"); - } - - pdata->key = kcalloc(key_size, sizeof(*(pdata->key)), GFP_KERNEL); - if (!(pdata->key)) { - dev_err(&pdev->dev, "platform key is required!\n"); - goto get_key_mem_fail; + dev_err(&pdev->dev, + "failed to get key_num!\n"); + return -EINVAL; } - - pdata->key_num = key_size; - for (i = 0; i < key_size; i++) { - ret = of_property_read_string_index(pdev->dev.of_node, - "key_name", i, &(pdata->key[i].name)); + keypad->key = devm_kzalloc(&pdev->dev, + (keypad->key_size)*sizeof(*(keypad->key)), GFP_KERNEL); + if (!(keypad->key)) + return -EINVAL; + for (i = 0; i < keypad->key_size; i++) { + //get all gpio desc. + desc = devm_gpiod_get_index(&pdev->dev, "key", i, GPIOD_IN); + if (!desc) + return -EINVAL; + keypad->key[i].desc = desc; + //The gpio default is high level. + keypad->key[i].current_status = 1; + ret = of_property_read_u32_index(pdev->dev.of_node, + "key_code", i, &(keypad->key[i].code)); if (ret < 0) { - dev_info(&pdev->dev, - "gpio_key: find key_name=%d finished\n", i); - break; + dev_err(&pdev->dev, + "find key_code=%d finished\n", i); + return -EINVAL; } - } - key_param = kzalloc((sizeof(*key_param))*(pdata->key_num), GFP_KERNEL); - if (!key_param) - goto get_param_mem_fail; - ret = of_property_read_u32_array(pdev->dev.of_node, - "key_code", key_param, pdata->key_num); - if (ret) { - dev_info(&pdev->dev, "gpio_key: failed to get key_code!\n"); - goto get_key_param_failed; - } - - for (i = 0; i < pdata->key_num; i++) { - pdata->key[i].code = *(key_param+i); - pdata->key[i].status = -1; - } - -#ifdef USE_IRQ - pdata->irq_keyup = irq_of_parse_and_map(pdev->dev.of_node, 0); - pdata->irq_keydown = irq_of_parse_and_map(pdev->dev.of_node, 1); - pdata->irq_keyup = irq_keyup; - pdata->irq_keydown = irq_keydown; - pr_info("irq_keyup:%d,irq_keydown:%d\n", - pdata->irq_keyup, pdata->irq_keydown); -#endif - - for (i = 0; i < key_size; i++) { ret = of_property_read_string_index(pdev->dev.of_node, - "key_pin", i, &str); - dev_info(&pdev->dev, "gpio_key: %d name(%s) pin(%s)\n", - i, (pdata->key[i].name), str); - if (ret < 0) { - dev_info(&pdev->dev, - "gpio_key: find key_name=%d finished\n", i); - break; - } - ret = amlogic_gpio_name_map_num(pdev, str); - dev_info(&pdev->dev, "amlogic_gpio_name_map_num pin %d!%s::\n", - ret, str); + "key_name", i, &(keypad->key[i].name)); if (ret < 0) { - dev_info(&pdev->dev, "gpio_key bad pin !\n"); - goto get_key_param_failed; + dev_err(&pdev->dev, + "find key_name=%d finished\n", i); + return -EINVAL; } - desc = of_get_named_gpiod_flags(pdev->dev.of_node, - "key_pin", i, NULL); - pdata->key[i].pin = desc_to_gpio(desc); - dev_info(&pdev->dev, "gpio_key: %d %s(%d)\n", i, - (pdata->key[i].name), pdata->key[i].pin); - /*pdata->key[i].pin = ret;*/ - gpio_request(pdata->key[i].pin, MOD_NAME); - if (!gpio_highz) { - gpio_direction_input(pdata->key[i].pin); - gpiod_set_pull(desc, GPIOD_PULL_UP); - } -#ifdef USE_IRQ - gpio_for_irq(pdata->key[i].pin, - AML_GPIO_IRQ(irq_keyup, FILTER_NUM7, IRQF_TRIGGER_RISING)); - gpio_for_irq(pdata->key[i].pin, - AML_GPIO_IRQ(irq_keydown, FILTER_NUM7, IRQF_TRIGGER_FALLING)); -#endif + gpiod_direction_input(keypad->key[i].desc); + gpiod_set_pull(keypad->key[i].desc, GPIOD_PULL_UP); } - - kp = kzalloc(sizeof(struct kp), GFP_KERNEL); + //input input_dev = input_allocate_device(); - if (!kp || !input_dev) { - kfree(kp); - input_free_device(input_dev); - state = -ENOMEM; - goto get_key_param_failed; - } - gp_kp = kp; - platform_set_drvdata(pdev, pdata); - kp->input = input_dev; - INIT_WORK(&(kp->work_update), update_work_func); -#ifdef USE_IRQ - if (request_irq(irq_keyup, kp_isr, IRQF_DISABLED, - "irq_keyup", kp)) { - dev_info(&pdev->dev, "Failed to request gpio key up irq.\n"); - kfree(kp); - input_free_device(input_dev); - state = -EINVAL; - goto get_key_param_failed; - } - - if (request_irq(irq_keydown, kp_isr, IRQF_DISABLED, - "irq_keydown", kp)) { - dev_info(&pdev->dev, "Failed to request gpio key down irq.\n"); - kfree(kp); - input_free_device(input_dev); - state = -EINVAL; - goto get_key_param_failed; - } -#else - dev_info(&pdev->dev, "start setup_timer"); - setup_timer(&kp->timer, kp_timer_sr, (unsigned long) kp); - mod_timer(&kp->timer, jiffies+msecs_to_jiffies(100)); -#endif - /* setup input device */ + if (!input_dev) + return -EINVAL; set_bit(EV_KEY, input_dev->evbit); set_bit(EV_REP, input_dev->evbit); - kp->keys = pdata->key; - kp->key_num = pdata->key_num; - key = pdata->key; - - for (i = 0; i < kp->key_num; i++) { - set_bit(pdata->key[i].code, input_dev->keybit); - dev_info(&pdev->dev, "%s key(%d) registed.\n", - key->name, pdata->key[i].code); - } input_dev->name = "gpio_keypad"; input_dev->phys = "gpio_keypad/input0"; input_dev->dev.parent = &pdev->dev; - input_dev->id.bustype = BUS_ISA; input_dev->id.vendor = 0x0001; input_dev->id.product = 0x0001; input_dev->id.version = 0x0100; - input_dev->rep[REP_DELAY] = 0xffffffff; input_dev->rep[REP_PERIOD] = 0xffffffff; - input_dev->keycodesize = sizeof(unsigned short); input_dev->keycodemax = 0x1ff; - ret = input_register_device(kp->input); + keypad->input_dev = input_dev; + ret = input_register_device(keypad->input_dev); if (ret < 0) { - dev_err(&pdev->dev, "Unable to register keypad input device.\n"); - kfree(kp); - input_free_device(input_dev); - state = -EINVAL; - goto get_key_param_failed; + input_free_device(keypad->input_dev); + return -EINVAL; + } + platform_set_drvdata(pdev, keypad); + keypad->count = 0; + keypad->index = -1; + setup_timer(&(keypad->polling_timer), + polling_timer_handler, (unsigned long) keypad); + if (keypad->use_irq) { + for (i = 0; i < keypad->key_size; i++) { + keypad->key[i].irq_num = + gpiod_to_irq(keypad->key[i].desc); + ret = devm_request_irq(&pdev->dev, + keypad->key[i].irq_num, + gpio_irq_handler, IRQF_TRIGGER_FALLING, + "gpio_keypad", keypad); + if (ret) { + dev_err(&pdev->dev, + "Requesting irq failed!\n"); + input_free_device(keypad->input_dev); + del_timer(&(keypad->polling_timer)); + return -EINVAL; + } + } + } else { + mod_timer(&(keypad->polling_timer), + jiffies+msecs_to_jiffies(keypad->scan_period)); } -/*set_pwr_key();*/ - dev_info(&pdev->dev, "gpio keypad register input device completed.\n"); - register_keypad_dev(gp_kp); - kfree(key_param); return 0; - -get_key_param_failed: - kfree(key_param); -get_param_mem_fail: - kfree(pdata->key); -get_key_mem_fail: - kfree(pdata); -get_key_node_fail: - return state; } -static int gpio_key_remove(struct platform_device *pdev) +static int meson_gpio_kp_remove(struct platform_device *pdev) { - struct gpio_platform_data *pdata = platform_get_drvdata(pdev); - struct kp *kp = gp_kp; + struct gpio_keypad *keypad; - input_unregister_device(kp->input); - input_free_device(kp->input); - unregister_chrdev(kp->config_major, kp->config_name); - if (kp->config_class) { - if (kp->config_dev) - device_destroy(kp->config_class, - MKDEV(kp->config_major, 0)); - class_destroy(kp->config_class); - } - kfree(kp); -#ifdef CONFIG_OF - kfree(pdata->key); - kfree(pdata); -#endif - gp_kp = NULL; + keypad = platform_get_drvdata(pdev); + input_unregister_device(keypad->input_dev); + input_free_device(keypad->input_dev); + del_timer(&(keypad->polling_timer)); return 0; } -static int gpio_key_suspend(struct platform_device *dev, pm_message_t state) +static const struct of_device_id key_dt_match[] = { + { .compatible = "amlogic, gpio_keypad", }, + {}, +}; + +static int meson_gpio_kp_suspend(struct platform_device *dev, + pm_message_t state) { + int i; + struct gpio_keypad *pdata; + + pdata = (struct gpio_keypad *)platform_get_drvdata(dev); + if (pdata->use_irq) { + for (i = 0; i < pdata->key_size; i++) + disable_irq_nosync(pdata->key[i].irq_num); + } else + del_timer(&(pdata->polling_timer)); return 0; } -static int gpio_key_resume(struct platform_device *dev) +static int meson_gpio_kp_resume(struct platform_device *dev) { int i; - struct gpio_platform_data *pdata; - - pdata = (struct gpio_platform_data *)platform_get_drvdata(dev); - + struct gpio_keypad *pdata; + + pdata = (struct gpio_keypad *)platform_get_drvdata(dev); + if (pdata->use_irq) { + for (i = 0; i < pdata->key_size; i++) + enable_irq(pdata->key[i].irq_num); + } else + mod_timer(&(pdata->polling_timer), + jiffies+msecs_to_jiffies(5)); if (get_resume_method() == POWER_KEY_WAKEUP) { - for (i = 0; i < pdata->key_num; i++) { + for (i = 0; i < pdata->key_size; i++) { if (pdata->key[i].code == KEY_POWER) { pr_info("gpio keypad wakeup\n"); - - input_report_key(gp_kp->input, KEY_POWER, 1); - input_sync(gp_kp->input); - input_report_key(gp_kp->input, KEY_POWER, 0); - input_sync(gp_kp->input); - + input_report_key(pdata->input_dev, + KEY_POWER, 1); + input_sync(pdata->input_dev); + input_report_key(pdata->input_dev, + KEY_POWER, 0); + input_sync(pdata->input_dev); break; } } @@ -466,40 +305,18 @@ static int gpio_key_resume(struct platform_device *dev) return 0; } -#ifdef CONFIG_OF -static const struct of_device_id key_dt_match[] = { - { .compatible = "amlogic, gpio_keypad", }, - {}, -}; -#else -#define key_dt_match NULL -#endif - -static struct platform_driver gpio_driver = { - .probe = gpio_key_probe, - .remove = gpio_key_remove, - .suspend = gpio_key_suspend, - .resume = gpio_key_resume, +static struct platform_driver meson_gpio_kp_driver = { + .probe = meson_gpio_kp_probe, + .remove = meson_gpio_kp_remove, + .suspend = meson_gpio_kp_suspend, + .resume = meson_gpio_kp_resume, .driver = { - .name = "gpio-key", + .name = "gpio-keypad", .of_match_table = key_dt_match, }, }; -static int __init gpio_key_init(void) -{ - return platform_driver_register(&gpio_driver); -} - -static void __exit gpio_key_exit(void) -{ - platform_driver_unregister(&gpio_driver); -} - -module_init(gpio_key_init); -module_exit(gpio_key_exit); - -MODULE_AUTHOR("Frank Chen"); +module_platform_driver(meson_gpio_kp_driver); +MODULE_AUTHOR("Amlogic"); MODULE_DESCRIPTION("GPIO Keypad Driver"); MODULE_LICENSE("GPL"); - diff --git a/drivers/amlogic/input/keyboard/gpio_keypad_old.c b/drivers/amlogic/input/keyboard/gpio_keypad_old.c new file mode 100644 index 0000000..35abea9 --- /dev/null +++ b/drivers/amlogic/input/keyboard/gpio_keypad_old.c @@ -0,0 +1,505 @@ +/* + * drivers/amlogic/input/keyboard/gpio_keypad.c + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * 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. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../../gpio/gpiolib.h" + +#define OFFSET 24 + +#define MOD_NAME "gpio_key" + +#undef pr_fmt +#define pr_fmt(fmt) "gpio_key: " fmt + +struct gpio_key { + int code; /* input key code */ + const char *name; + int pin; /*pin number*/ + int status; /*0 up , 1 down*/ +}; + +struct gpio_platform_data { + struct gpio_key *key; + int key_num; + int repeat_delay; + int repeat_period; + int irq_keyup; + int irq_keydown; +}; + +struct kp { + struct input_dev *input; + struct timer_list timer; + int config_major; + char config_name[20]; + struct class *config_class; + struct device *config_dev; + struct gpio_key *keys; + int key_num; + struct work_struct work_update; +}; + +static struct kp *gp_kp; + +int amlogic_gpio_name_map_num(struct platform_device *pdev, const char *value) +{ + return of_property_read_string(pdev->dev.of_node, "key_pin", &value); +} + + +static void kp_work(struct kp *kp) +{ + struct gpio_key *key; + int i; + int io_status; + + key = kp->keys; + for (i = 0; i < kp->key_num; i++) { + io_status = gpiod_get_value(gpio_to_desc(key->pin)); + if (io_status != key->status) { + if (io_status) { + dev_info(&kp->input->dev, + "key %d up\n", key->code); + input_report_key(kp->input, key->code, 0); + input_sync(kp->input); + } else { + dev_info(&kp->input->dev, + "key %d down\n", key->code); + input_report_key(kp->input, key->code, 1); + input_sync(kp->input); + } + key->status = io_status; + } + key++; + } +} + +static void update_work_func(struct work_struct *work) +{ + struct kp *kp_data = container_of(work, struct kp, work_update); + + kp_work(kp_data); +} + +/* + * What we do here is just for loss wakeup key when suspend. + * In suspend routine , the intr is disable. + * we need do more things to adapt the gpio change. + */ +int det_pwr_key(void) +{ + /*return aml_read_reg32(P_AO_IRQ_STAT) & (1<<8);*/ + return 0; +} +/*Enable gpio interrupt for AO domain interrupt*/ +void set_pwr_key(void) +{ + /* aml_write_reg32(P_AO_IRQ_GPIO_REG, + * aml_read_reg32(P_AO_IRQ_GPIO_REG)|(1<<18)|(1<<16)|(0x3<<0)); + * aml_write_reg32(P_AO_IRQ_MASK_FIQ_SEL, + * aml_read_reg32(P_AO_IRQ_MASK_FIQ_SEL)|(1<<8)); + * aml_set_reg32_mask(P_AO_IRQ_STAT_CLR, 1<<8); + */ +} + +void clr_pwr_key(void) +{ + /*aml_set_reg32_mask(P_AO_IRQ_STAT_CLR, 1<<8);*/ +} + + +#ifdef USE_IRQ + +static irqreturn_t kp_isr(int irq, void *data) +{ + struct kp *kp_data = (struct kp *)data; + + queue_work(system_freezable_wq, &(kp_data->work_update)); + + /* if (!deep_suspend_flag) + * clr_pwr_key(); + */ + return IRQ_HANDLED; +} + +#else +void kp_timer_sr(unsigned long data) +{ + struct kp *kp_data = (struct kp *)data; + + queue_work(system_freezable_wq, &(kp_data->work_update)); + mod_timer(&kp_data->timer, jiffies+msecs_to_jiffies(25)); +} +#endif +static int gpio_key_config_open(struct inode *inode, struct file *file) +{ + file->private_data = gp_kp; + return 0; +} + +static int gpio_key_config_release(struct inode *inode, struct file *file) +{ + file->private_data = NULL; + return 0; +} + +static const struct file_operations keypad_fops = { + .owner = THIS_MODULE, + .open = gpio_key_config_open, + .release = gpio_key_config_release, +}; + +static int register_keypad_dev(struct kp *kp) +{ + int ret = 0; + char name[] = "gpio_keyboard"; + + strncpy(kp->config_name, name, sizeof(name)); + ret = register_chrdev(0, kp->config_name, &keypad_fops); + if (ret <= 0) { + dev_info(&kp->input->dev, "register char device error\n"); + return ret; + } + kp->config_major = ret; + dev_info(&kp->input->dev, "gpio keypad major:%d\n", ret); + kp->config_class = class_create(THIS_MODULE, kp->config_name); + kp->config_dev = device_create(kp->config_class, NULL, + MKDEV(kp->config_major, 0), NULL, kp->config_name); + return ret; +} + +static int gpio_key_probe(struct platform_device *pdev) +{ + const char *str; + struct kp *kp; + struct input_dev *input_dev; + int i, ret, key_size; + struct gpio_key *key; + struct gpio_platform_data *pdata = NULL; + struct gpio_desc *desc; + int *key_param = NULL; + int state = -EINVAL; +#ifdef USE_IRQ + int irq_keyup; + int irq_keydown; +#endif + + int gpio_highz = 0; + + if (!pdev->dev.of_node) { + dev_info(&pdev->dev, "gpio_key: pdev->dev.of_node == NULL!\n"); + state = -EINVAL; + goto get_key_node_fail; + } + ret = of_property_read_u32(pdev->dev.of_node, "key_num", &key_size); + if (ret) { + dev_info(&pdev->dev, "gpio_key: failed to get key_num!\n"); + state = -EINVAL; + goto get_key_node_fail; + } + + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + state = -EINVAL; + goto get_key_node_fail; + } + + ret = of_property_read_bool(pdev->dev.of_node, "gpio_high_z"); + if (ret) { + gpio_highz = 1; + dev_info(&pdev->dev, "gpio request set to High-Z status\n"); + } + + pdata->key = kcalloc(key_size, sizeof(*(pdata->key)), GFP_KERNEL); + if (!(pdata->key)) { + dev_err(&pdev->dev, "platform key is required!\n"); + goto get_key_mem_fail; + } + + pdata->key_num = key_size; + for (i = 0; i < key_size; i++) { + ret = of_property_read_string_index(pdev->dev.of_node, + "key_name", i, &(pdata->key[i].name)); + if (ret < 0) { + dev_info(&pdev->dev, + "gpio_key: find key_name=%d finished\n", i); + break; + } + } + key_param = kzalloc((sizeof(*key_param))*(pdata->key_num), GFP_KERNEL); + if (!key_param) + goto get_param_mem_fail; + ret = of_property_read_u32_array(pdev->dev.of_node, + "key_code", key_param, pdata->key_num); + if (ret) { + dev_info(&pdev->dev, "gpio_key: failed to get key_code!\n"); + goto get_key_param_failed; + } + + for (i = 0; i < pdata->key_num; i++) { + pdata->key[i].code = *(key_param+i); + pdata->key[i].status = -1; + } + +#ifdef USE_IRQ + pdata->irq_keyup = irq_of_parse_and_map(pdev->dev.of_node, 0); + pdata->irq_keydown = irq_of_parse_and_map(pdev->dev.of_node, 1); + pdata->irq_keyup = irq_keyup; + pdata->irq_keydown = irq_keydown; + pr_info("irq_keyup:%d,irq_keydown:%d\n", + pdata->irq_keyup, pdata->irq_keydown); +#endif + + for (i = 0; i < key_size; i++) { + ret = of_property_read_string_index(pdev->dev.of_node, + "key_pin", i, &str); + dev_info(&pdev->dev, "gpio_key: %d name(%s) pin(%s)\n", + i, (pdata->key[i].name), str); + if (ret < 0) { + dev_info(&pdev->dev, + "gpio_key: find key_name=%d finished\n", i); + break; + } + ret = amlogic_gpio_name_map_num(pdev, str); + dev_info(&pdev->dev, "amlogic_gpio_name_map_num pin %d!%s::\n", + ret, str); + if (ret < 0) { + dev_info(&pdev->dev, "gpio_key bad pin !\n"); + goto get_key_param_failed; + } + desc = of_get_named_gpiod_flags(pdev->dev.of_node, + "key_pin", i, NULL); + pdata->key[i].pin = desc_to_gpio(desc); + dev_info(&pdev->dev, "gpio_key: %d %s(%d)\n", i, + (pdata->key[i].name), pdata->key[i].pin); + /*pdata->key[i].pin = ret;*/ + gpio_request(pdata->key[i].pin, MOD_NAME); + if (!gpio_highz) { + gpio_direction_input(pdata->key[i].pin); + gpiod_set_pull(desc, GPIOD_PULL_UP); + } +#ifdef USE_IRQ + gpio_for_irq(pdata->key[i].pin, + AML_GPIO_IRQ(irq_keyup, FILTER_NUM7, IRQF_TRIGGER_RISING)); + gpio_for_irq(pdata->key[i].pin, + AML_GPIO_IRQ(irq_keydown, FILTER_NUM7, IRQF_TRIGGER_FALLING)); +#endif + } + + kp = kzalloc(sizeof(struct kp), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!kp || !input_dev) { + kfree(kp); + input_free_device(input_dev); + state = -ENOMEM; + goto get_key_param_failed; + } + gp_kp = kp; + platform_set_drvdata(pdev, pdata); + kp->input = input_dev; + INIT_WORK(&(kp->work_update), update_work_func); +#ifdef USE_IRQ + if (request_irq(irq_keyup, kp_isr, IRQF_DISABLED, + "irq_keyup", kp)) { + dev_info(&pdev->dev, "Failed to request gpio key up irq.\n"); + kfree(kp); + input_free_device(input_dev); + state = -EINVAL; + goto get_key_param_failed; + } + + if (request_irq(irq_keydown, kp_isr, IRQF_DISABLED, + "irq_keydown", kp)) { + dev_info(&pdev->dev, "Failed to request gpio key down irq.\n"); + kfree(kp); + input_free_device(input_dev); + state = -EINVAL; + goto get_key_param_failed; + } +#else + dev_info(&pdev->dev, "start setup_timer"); + setup_timer(&kp->timer, kp_timer_sr, (unsigned long) kp); + mod_timer(&kp->timer, jiffies+msecs_to_jiffies(100)); +#endif + /* setup input device */ + set_bit(EV_KEY, input_dev->evbit); + set_bit(EV_REP, input_dev->evbit); + kp->keys = pdata->key; + kp->key_num = pdata->key_num; + key = pdata->key; + + for (i = 0; i < kp->key_num; i++) { + set_bit(pdata->key[i].code, input_dev->keybit); + dev_info(&pdev->dev, "%s key(%d) registed.\n", + key->name, pdata->key[i].code); + } + input_dev->name = "gpio_keypad"; + input_dev->phys = "gpio_keypad/input0"; + input_dev->dev.parent = &pdev->dev; + + input_dev->id.bustype = BUS_ISA; + input_dev->id.vendor = 0x0001; + input_dev->id.product = 0x0001; + input_dev->id.version = 0x0100; + + input_dev->rep[REP_DELAY] = 0xffffffff; + input_dev->rep[REP_PERIOD] = 0xffffffff; + + input_dev->keycodesize = sizeof(unsigned short); + input_dev->keycodemax = 0x1ff; + ret = input_register_device(kp->input); + if (ret < 0) { + dev_err(&pdev->dev, "Unable to register keypad input device.\n"); + kfree(kp); + input_free_device(input_dev); + state = -EINVAL; + goto get_key_param_failed; + } +/*set_pwr_key();*/ + dev_info(&pdev->dev, "gpio keypad register input device completed.\n"); + register_keypad_dev(gp_kp); + kfree(key_param); + return 0; + +get_key_param_failed: + kfree(key_param); +get_param_mem_fail: + kfree(pdata->key); +get_key_mem_fail: + kfree(pdata); +get_key_node_fail: + return state; +} + +static int gpio_key_remove(struct platform_device *pdev) +{ + struct gpio_platform_data *pdata = platform_get_drvdata(pdev); + struct kp *kp = gp_kp; + + input_unregister_device(kp->input); + input_free_device(kp->input); + unregister_chrdev(kp->config_major, kp->config_name); + if (kp->config_class) { + if (kp->config_dev) + device_destroy(kp->config_class, + MKDEV(kp->config_major, 0)); + class_destroy(kp->config_class); + } + kfree(kp); +#ifdef CONFIG_OF + kfree(pdata->key); + kfree(pdata); +#endif + gp_kp = NULL; + return 0; +} + +static int gpio_key_suspend(struct platform_device *dev, pm_message_t state) +{ + return 0; +} + +static int gpio_key_resume(struct platform_device *dev) +{ + int i; + struct gpio_platform_data *pdata; + + pdata = (struct gpio_platform_data *)platform_get_drvdata(dev); + + if (get_resume_method() == POWER_KEY_WAKEUP) { + for (i = 0; i < pdata->key_num; i++) { + if (pdata->key[i].code == KEY_POWER) { + pr_info("gpio keypad wakeup\n"); + + input_report_key(gp_kp->input, KEY_POWER, 1); + input_sync(gp_kp->input); + input_report_key(gp_kp->input, KEY_POWER, 0); + input_sync(gp_kp->input); + + break; + } + } + } + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id key_dt_match[] = { + { .compatible = "amlogic, gpio_keypad", }, + {}, +}; +#else +#define key_dt_match NULL +#endif + +static struct platform_driver gpio_driver = { + .probe = gpio_key_probe, + .remove = gpio_key_remove, + .suspend = gpio_key_suspend, + .resume = gpio_key_resume, + .driver = { + .name = "gpio-key", + .of_match_table = key_dt_match, + }, +}; + +static int __init gpio_key_init(void) +{ + return platform_driver_register(&gpio_driver); +} + +static void __exit gpio_key_exit(void) +{ + platform_driver_unregister(&gpio_driver); +} + +module_init(gpio_key_init); +module_exit(gpio_key_exit); + +MODULE_AUTHOR("Frank Chen"); +MODULE_DESCRIPTION("GPIO Keypad Driver"); +MODULE_LICENSE("GPL"); + -- 2.7.4