/* drivers/input/misc/gpio_matrix.c * * Copyright (C) 2007 Google, Inc. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * * 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 struct gpio_kp { struct gpio_event_input_devs *input_devs; struct gpio_event_matrix_info *keypad_info; struct hrtimer timer; struct wakeup_source wake_src; int current_output; unsigned int use_irq:1; unsigned int key_state_changed:1; unsigned int last_key_state_changed:1; unsigned int some_keys_pressed:2; unsigned int disabled_irq:1; unsigned long keys_pressed[0]; }; static void clear_phantom_key(struct gpio_kp *kp, int out, int in) { struct gpio_event_matrix_info *mi = kp->keypad_info; int key_index = out * mi->ninputs + in; unsigned short keyentry = mi->keymap[key_index]; unsigned short keycode = keyentry & MATRIX_KEY_MASK; unsigned short dev = keyentry >> MATRIX_CODE_BITS; if (!test_bit(keycode, kp->input_devs->dev[dev]->key)) { if (mi->flags & GPIOKPF_PRINT_PHANTOM_KEYS) pr_info("gpiomatrix: phantom key %x, %d-%d (%d-%d) " "cleared\n", keycode, out, in, mi->output_gpios[out], mi->input_gpios[in]); __clear_bit(key_index, kp->keys_pressed); } else { if (mi->flags & GPIOKPF_PRINT_PHANTOM_KEYS) pr_info("gpiomatrix: phantom key %x, %d-%d (%d-%d) " "not cleared\n", keycode, out, in, mi->output_gpios[out], mi->input_gpios[in]); } } static int restore_keys_for_input(struct gpio_kp *kp, int out, int in) { int rv = 0; int key_index; key_index = out * kp->keypad_info->ninputs + in; while (out < kp->keypad_info->noutputs) { if (test_bit(key_index, kp->keys_pressed)) { rv = 1; clear_phantom_key(kp, out, in); } key_index += kp->keypad_info->ninputs; out++; } return rv; } static void remove_phantom_keys(struct gpio_kp *kp) { int out, in, inp; int key_index; if (kp->some_keys_pressed < 3) return; for (out = 0; out < kp->keypad_info->noutputs; out++) { inp = -1; key_index = out * kp->keypad_info->ninputs; for (in = 0; in < kp->keypad_info->ninputs; in++, key_index++) { if (test_bit(key_index, kp->keys_pressed)) { if (inp == -1) { inp = in; continue; } if (inp >= 0) { if (!restore_keys_for_input(kp, out + 1, inp)) break; clear_phantom_key(kp, out, inp); inp = -2; } restore_keys_for_input(kp, out, in); } } } } static void report_key(struct gpio_kp *kp, int key_index, int out, int in) { struct gpio_event_matrix_info *mi = kp->keypad_info; int pressed = test_bit(key_index, kp->keys_pressed); unsigned short keyentry = mi->keymap[key_index]; unsigned short keycode = keyentry & MATRIX_KEY_MASK; unsigned short dev = keyentry >> MATRIX_CODE_BITS; if (pressed != test_bit(keycode, kp->input_devs->dev[dev]->key)) { if (keycode == KEY_RESERVED) { if (mi->flags & GPIOKPF_PRINT_UNMAPPED_KEYS) pr_info("gpiomatrix: unmapped key, %d-%d " "(%d-%d) changed to %d\n", out, in, mi->output_gpios[out], mi->input_gpios[in], pressed); } else { if (mi->flags & GPIOKPF_PRINT_MAPPED_KEYS) pr_info("gpiomatrix: key %x, %d-%d (%d-%d) " "changed to %d\n", keycode, out, in, mi->output_gpios[out], mi->input_gpios[in], pressed); input_report_key(kp->input_devs->dev[dev], keycode, pressed); } } } static void report_sync(struct gpio_kp *kp) { int i; for (i = 0; i < kp->input_devs->count; i++) input_sync(kp->input_devs->dev[i]); } static enum hrtimer_restart gpio_keypad_timer_func(struct hrtimer *timer) { int out, in; int key_index; int gpio; struct gpio_kp *kp = container_of(timer, struct gpio_kp, timer); struct gpio_event_matrix_info *mi = kp->keypad_info; unsigned gpio_keypad_flags = mi->flags; unsigned polarity = !!(gpio_keypad_flags & GPIOKPF_ACTIVE_HIGH); out = kp->current_output; if (out == mi->noutputs) { out = 0; kp->last_key_state_changed = kp->key_state_changed; kp->key_state_changed = 0; kp->some_keys_pressed = 0; } else { key_index = out * mi->ninputs; for (in = 0; in < mi->ninputs; in++, key_index++) { gpio = mi->input_gpios[in]; if (gpio_get_value(gpio) ^ !polarity) { if (kp->some_keys_pressed < 3) kp->some_keys_pressed++; kp->key_state_changed |= !__test_and_set_bit( key_index, kp->keys_pressed); } else kp->key_state_changed |= __test_and_clear_bit( key_index, kp->keys_pressed); } gpio = mi->output_gpios[out]; if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE) gpio_set_value(gpio, !polarity); else gpio_direction_input(gpio); out++; } kp->current_output = out; if (out < mi->noutputs) { gpio = mi->output_gpios[out]; if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE) gpio_set_value(gpio, polarity); else gpio_direction_output(gpio, polarity); hrtimer_start(timer, mi->settle_time, HRTIMER_MODE_REL); return HRTIMER_NORESTART; } if (gpio_keypad_flags & GPIOKPF_DEBOUNCE) { if (kp->key_state_changed) { hrtimer_start(&kp->timer, mi->debounce_delay, HRTIMER_MODE_REL); return HRTIMER_NORESTART; } kp->key_state_changed = kp->last_key_state_changed; } if (kp->key_state_changed) { if (gpio_keypad_flags & GPIOKPF_REMOVE_SOME_PHANTOM_KEYS) remove_phantom_keys(kp); key_index = 0; for (out = 0; out < mi->noutputs; out++) for (in = 0; in < mi->ninputs; in++, key_index++) report_key(kp, key_index, out, in); report_sync(kp); } if (!kp->use_irq || kp->some_keys_pressed) { hrtimer_start(timer, mi->poll_time, HRTIMER_MODE_REL); return HRTIMER_NORESTART; } /* No keys are pressed, reenable interrupt */ for (out = 0; out < mi->noutputs; out++) { if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE) gpio_set_value(mi->output_gpios[out], polarity); else gpio_direction_output(mi->output_gpios[out], polarity); } for (in = 0; in < mi->ninputs; in++) enable_irq(gpio_to_irq(mi->input_gpios[in])); __pm_relax(&kp->wake_src); return HRTIMER_NORESTART; } static irqreturn_t gpio_keypad_irq_handler(int irq_in, void *dev_id) { int i; struct gpio_kp *kp = dev_id; struct gpio_event_matrix_info *mi = kp->keypad_info; unsigned gpio_keypad_flags = mi->flags; if (!kp->use_irq) { /* ignore interrupt while registering the handler */ kp->disabled_irq = 1; disable_irq_nosync(irq_in); return IRQ_HANDLED; } for (i = 0; i < mi->ninputs; i++) disable_irq_nosync(gpio_to_irq(mi->input_gpios[i])); for (i = 0; i < mi->noutputs; i++) { if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE) gpio_set_value(mi->output_gpios[i], !(gpio_keypad_flags & GPIOKPF_ACTIVE_HIGH)); else gpio_direction_input(mi->output_gpios[i]); } __pm_stay_awake(&kp->wake_src); hrtimer_start(&kp->timer, ktime_set(0, 0), HRTIMER_MODE_REL); return IRQ_HANDLED; } static int gpio_keypad_request_irqs(struct gpio_kp *kp) { int i; int err; unsigned int irq; unsigned long request_flags; struct gpio_event_matrix_info *mi = kp->keypad_info; switch (mi->flags & (GPIOKPF_ACTIVE_HIGH|GPIOKPF_LEVEL_TRIGGERED_IRQ)) { default: request_flags = IRQF_TRIGGER_FALLING; break; case GPIOKPF_ACTIVE_HIGH: request_flags = IRQF_TRIGGER_RISING; break; case GPIOKPF_LEVEL_TRIGGERED_IRQ: request_flags = IRQF_TRIGGER_LOW; break; case GPIOKPF_LEVEL_TRIGGERED_IRQ | GPIOKPF_ACTIVE_HIGH: request_flags = IRQF_TRIGGER_HIGH; break; } for (i = 0; i < mi->ninputs; i++) { err = irq = gpio_to_irq(mi->input_gpios[i]); if (err < 0) goto err_gpio_get_irq_num_failed; err = request_irq(irq, gpio_keypad_irq_handler, request_flags, "gpio_kp", kp); if (err) { pr_err("gpiomatrix: request_irq failed for input %d, " "irq %d\n", mi->input_gpios[i], irq); goto err_request_irq_failed; } err = enable_irq_wake(irq); if (err) { pr_err("gpiomatrix: set_irq_wake failed for input %d, " "irq %d\n", mi->input_gpios[i], irq); } disable_irq(irq); if (kp->disabled_irq) { kp->disabled_irq = 0; enable_irq(irq); } } return 0; for (i = mi->noutputs - 1; i >= 0; i--) { free_irq(gpio_to_irq(mi->input_gpios[i]), kp); err_request_irq_failed: err_gpio_get_irq_num_failed: ; } return err; } int gpio_event_matrix_func(struct gpio_event_input_devs *input_devs, struct gpio_event_info *info, void **data, int func) { int i; int err; int key_count; struct gpio_kp *kp; struct gpio_event_matrix_info *mi; mi = container_of(info, struct gpio_event_matrix_info, info); if (func == GPIO_EVENT_FUNC_SUSPEND || func == GPIO_EVENT_FUNC_RESUME) { /* TODO: disable scanning */ return 0; } if (func == GPIO_EVENT_FUNC_INIT) { if (mi->keymap == NULL || mi->input_gpios == NULL || mi->output_gpios == NULL) { err = -ENODEV; pr_err("gpiomatrix: Incomplete pdata\n"); goto err_invalid_platform_data; } key_count = mi->ninputs * mi->noutputs; *data = kp = kzalloc(sizeof(*kp) + sizeof(kp->keys_pressed[0]) * BITS_TO_LONGS(key_count), GFP_KERNEL); if (kp == NULL) { err = -ENOMEM; pr_err("gpiomatrix: Failed to allocate private data\n"); goto err_kp_alloc_failed; } kp->input_devs = input_devs; kp->keypad_info = mi; for (i = 0; i < key_count; i++) { unsigned short keyentry = mi->keymap[i]; unsigned short keycode = keyentry & MATRIX_KEY_MASK; unsigned short dev = keyentry >> MATRIX_CODE_BITS; if (dev >= input_devs->count) { pr_err("gpiomatrix: bad device index %d >= " "%d for key code %d\n", dev, input_devs->count, keycode); err = -EINVAL; goto err_bad_keymap; } if (keycode && keycode <= KEY_MAX) input_set_capability(input_devs->dev[dev], EV_KEY, keycode); } for (i = 0; i < mi->noutputs; i++) { err = gpio_request(mi->output_gpios[i], "gpio_kp_out"); if (err) { pr_err("gpiomatrix: gpio_request failed for " "output %d\n", mi->output_gpios[i]); goto err_request_output_gpio_failed; } if (gpio_cansleep(mi->output_gpios[i])) { pr_err("gpiomatrix: unsupported output gpio %d," " can sleep\n", mi->output_gpios[i]); err = -EINVAL; goto err_output_gpio_configure_failed; } if (mi->flags & GPIOKPF_DRIVE_INACTIVE) err = gpio_direction_output(mi->output_gpios[i], !(mi->flags & GPIOKPF_ACTIVE_HIGH)); else err = gpio_direction_input(mi->output_gpios[i]); if (err) { pr_err("gpiomatrix: gpio_configure failed for " "output %d\n", mi->output_gpios[i]); goto err_output_gpio_configure_failed; } } for (i = 0; i < mi->ninputs; i++) { err = gpio_request(mi->input_gpios[i], "gpio_kp_in"); if (err) { pr_err("gpiomatrix: gpio_request failed for " "input %d\n", mi->input_gpios[i]); goto err_request_input_gpio_failed; } err = gpio_direction_input(mi->input_gpios[i]); if (err) { pr_err("gpiomatrix: gpio_direction_input failed" " for input %d\n", mi->input_gpios[i]); goto err_gpio_direction_input_failed; } } kp->current_output = mi->noutputs; kp->key_state_changed = 1; hrtimer_init(&kp->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); kp->timer.function = gpio_keypad_timer_func; wakeup_source_init(&kp->wake_src, "gpio_kp"); err = gpio_keypad_request_irqs(kp); kp->use_irq = err == 0; pr_info("GPIO Matrix Keypad Driver: Start keypad matrix for " "%s%s in %s mode\n", input_devs->dev[0]->name, (input_devs->count > 1) ? "..." : "", kp->use_irq ? "interrupt" : "polling"); if (kp->use_irq) __pm_stay_awake(&kp->wake_src); hrtimer_start(&kp->timer, ktime_set(0, 0), HRTIMER_MODE_REL); return 0; } err = 0; kp = *data; if (kp->use_irq) for (i = mi->noutputs - 1; i >= 0; i--) free_irq(gpio_to_irq(mi->input_gpios[i]), kp); hrtimer_cancel(&kp->timer); wakeup_source_trash(&kp->wake_src); for (i = mi->noutputs - 1; i >= 0; i--) { err_gpio_direction_input_failed: gpio_free(mi->input_gpios[i]); err_request_input_gpio_failed: ; } for (i = mi->noutputs - 1; i >= 0; i--) { err_output_gpio_configure_failed: gpio_free(mi->output_gpios[i]); err_request_output_gpio_failed: ; } err_bad_keymap: kfree(kp); err_kp_alloc_failed: err_invalid_platform_data: return err; } MODULE_DESCRIPTION("GPIO matrix driver"); MODULE_AUTHOR("Google, Inc"); MODULE_LICENSE("GPL v2");