| /* arch/arm/mach-msm/board-lexikon-keypad.c |
| * |
| * Copyright (C) 2008 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 <linux/platform_device.h> |
| #include <linux/input.h> |
| #include <linux/interrupt.h> |
| #include <linux/gpio_event.h> |
| #include <linux/keyreset.h> |
| #include <asm/mach-types.h> |
| #include <linux/gpio.h> |
| #include <mach/gpio.h> |
| |
| #include "board-lexikon.h" |
| #include "proc_comm.h" |
| #include <linux/mfd/pmic8058.h> |
| #include <linux/input/pmic8058-keypad.h> |
| |
| static char *keycaps = "--qwerty"; |
| #undef MODULE_PARAM_PREFIX |
| #define MODULE_PARAM_PREFIX "board_lexikon." |
| module_param_named(keycaps, keycaps, charp, 0); |
| #if 0 |
| /* fix up last row issue, + 1 */ |
| #define LEXIKON_KEY_MATRIX_ROW_NUM (7 + 1) |
| #define LEXIKON_KEY_MATRIX_COL_NUM 8 |
| |
| #define KEYMAP_INDEX(col, row) ((col) * LEXIKON_KEY_MATRIX_ROW_NUM + (row)) |
| |
| static const unsigned short lexikon_keymap_x0[LEXIKON_KEY_MATRIX_COL_NUM * |
| LEXIKON_KEY_MATRIX_ROW_NUM] = { |
| }; |
| |
| static struct resource lexikon_resources_keypad[] = { |
| { |
| .start = PM8058_IRQ_KEYPAD, |
| .end = PM8058_IRQ_KEYPAD, |
| .flags = IORESOURCE_IRQ, |
| }, |
| { |
| .start = PM8058_IRQ_KEYSTUCK, |
| .end = PM8058_IRQ_KEYSTUCK, |
| .flags = IORESOURCE_IRQ, |
| }, |
| }; |
| |
| struct platform_device lexikon_keypad_resource = { |
| .name = PMIC8058_KEYPAD_NAME, |
| .id = -1, |
| .num_resources = ARRAY_SIZE(lexikon_resources_keypad), |
| .resource = lexikon_resources_keypad, |
| }; |
| |
| static struct pmic8058_keypad_data lexikon_keypad_matrix_info = { |
| .info.func = pmic8058_event_matrix_func, |
| .input_name = PMIC8058_KEYPAD_NAME, |
| .num_rows = LEXIKON_KEY_MATRIX_ROW_NUM, |
| .num_cols = LEXIKON_KEY_MATRIX_COL_NUM, |
| .rows_gpio_start = 8, |
| .cols_gpio_start = 0, |
| .keymap_size = ARRAY_SIZE(lexikon_keymap_x0), |
| .keymap = (unsigned int *)lexikon_keymap_x0, |
| .debounce_ms = {8}, |
| .scan_delay_ms = 32, |
| .row_hold_us = 125, |
| .wakeup = 1, |
| .p_device = &lexikon_keypad_resource, |
| .flags = (/*PMIC8058_PRINT_UNMAPPED_KEYS | |
| PMIC8058_PRINT_MAPPED_KEYS |*/ |
| PMIC8058_PRINT_SEND_KEYS /*| |
| PMIC8058_PRINT_PHANTOM_KEYS*/), |
| }; |
| #endif |
| |
| #ifdef CONFIG_MSM_SSBI |
| static unsigned int lexikon_pmic_col_gpios[] = { |
| PM8058_GPIO_PM_TO_SYS(PMGPIO(1)), PM8058_GPIO_PM_TO_SYS(PMGPIO(2)), |
| PM8058_GPIO_PM_TO_SYS(PMGPIO(3)), PM8058_GPIO_PM_TO_SYS(PMGPIO(4)), |
| PM8058_GPIO_PM_TO_SYS(PMGPIO(5)), PM8058_GPIO_PM_TO_SYS(PMGPIO(6)), |
| PM8058_GPIO_PM_TO_SYS(PMGPIO(7)), |
| }; |
| static unsigned int lexikon_pmic_row_gpios[] = { |
| PM8058_GPIO_PM_TO_SYS(PMGPIO(9)), PM8058_GPIO_PM_TO_SYS(PMGPIO(10)), |
| PM8058_GPIO_PM_TO_SYS(PMGPIO(11)), PM8058_GPIO_PM_TO_SYS(PMGPIO(12)), |
| PM8058_GPIO_PM_TO_SYS(PMGPIO(13)), PM8058_GPIO_PM_TO_SYS(PMGPIO(14)), |
| PM8058_GPIO_PM_TO_SYS(PMGPIO(15)), |
| |
| }; |
| |
| #define KEYMAP_NUM_ROWS ARRAY_SIZE(lexikon_pmic_row_gpios) |
| #define KEYMAP_NUM_COLS ARRAY_SIZE(lexikon_pmic_col_gpios) |
| #define KEYMAP_INDEX(row, col) (((row) * KEYMAP_NUM_COLS) + (col)) |
| #define KEYMAP_SIZE (KEYMAP_NUM_ROWS * KEYMAP_NUM_COLS) |
| |
| static unsigned short lexikon_pmic_keymap[KEYMAP_SIZE] = { |
| [KEYMAP_INDEX(0, 0)] = KEY_RIGHTALT, |
| [KEYMAP_INDEX(0, 1)] = KEY_RIGHTSHIFT, |
| [KEYMAP_INDEX(0, 2)] = KEY_RIGHT, |
| [KEYMAP_INDEX(0, 3)] = KEY_LEFT, |
| [KEYMAP_INDEX(0, 4)] = KEY_UP, |
| [KEYMAP_INDEX(0, 5)] = KEY_DOWN, |
| [KEYMAP_INDEX(0, 6)] = KEY_SEND, |
| |
| [KEYMAP_INDEX(1, 0)] = KEY_MENU, |
| [KEYMAP_INDEX(1, 1)] = KEY_BACKSPACE, |
| [KEYMAP_INDEX(1, 2)] = KEY_M, |
| [KEYMAP_INDEX(1, 3)] = KEY_HOME, |
| [KEYMAP_INDEX(1, 4)] = KEY_RESERVED, |
| [KEYMAP_INDEX(1, 5)] = KEY_RESERVED, |
| [KEYMAP_INDEX(1, 6)] = KEY_END, |
| |
| [KEYMAP_INDEX(2, 0)] = KEY_ENTER, |
| [KEYMAP_INDEX(2, 1)] = KEY_SPACE, |
| [KEYMAP_INDEX(2, 2)] = KEY_Q, |
| [KEYMAP_INDEX(2, 3)] = KEY_W, |
| [KEYMAP_INDEX(2, 4)] = KEY_E, |
| [KEYMAP_INDEX(2, 5)] = KEY_R, |
| [KEYMAP_INDEX(2, 6)] = KEY_T, |
| |
| [KEYMAP_INDEX(3, 0)] = KEY_RESERVED, |
| [KEYMAP_INDEX(3, 1)] = KEY_QUESTION, |
| [KEYMAP_INDEX(3, 2)] = KEY_Y, |
| [KEYMAP_INDEX(3, 3)] = KEY_U, |
| [KEYMAP_INDEX(3, 4)] = KEY_I, |
| [KEYMAP_INDEX(3, 5)] = KEY_O, |
| [KEYMAP_INDEX(3, 6)] = KEY_P, |
| |
| [KEYMAP_INDEX(4, 0)] = KEY_F13, |
| [KEYMAP_INDEX(4, 1)] = KEY_DOT, |
| [KEYMAP_INDEX(4, 2)] = KEY_A, |
| [KEYMAP_INDEX(4, 3)] = KEY_S, |
| [KEYMAP_INDEX(4, 4)] = KEY_D, |
| [KEYMAP_INDEX(4, 5)] = KEY_F, |
| [KEYMAP_INDEX(4, 6)] = KEY_G, |
| |
| [KEYMAP_INDEX(5, 0)] = KEY_BACK, |
| [KEYMAP_INDEX(5, 1)] = KEY_COMMA, |
| [KEYMAP_INDEX(5, 2)] = KEY_H, |
| [KEYMAP_INDEX(5, 3)] = KEY_J, |
| [KEYMAP_INDEX(5, 4)] = KEY_K, |
| [KEYMAP_INDEX(5, 5)] = KEY_L, |
| [KEYMAP_INDEX(5, 6)] = KEY_Z, |
| |
| [KEYMAP_INDEX(6, 0)] = KEY_SEARCH, |
| [KEYMAP_INDEX(6, 1)] = KEY_EMAIL, |
| [KEYMAP_INDEX(6, 2)] = KEY_X, |
| [KEYMAP_INDEX(6, 3)] = KEY_C, |
| [KEYMAP_INDEX(6, 4)] = KEY_V, |
| [KEYMAP_INDEX(6, 5)] = KEY_B, |
| [KEYMAP_INDEX(6, 6)] = KEY_N, |
| |
| }; |
| |
| static struct gpio_event_matrix_info lexikon_keypad_matrix_info = { |
| .info.func = gpio_event_matrix_func, |
| .keymap = lexikon_pmic_keymap, |
| .output_gpios = lexikon_pmic_row_gpios, |
| .input_gpios = lexikon_pmic_col_gpios, |
| .noutputs = KEYMAP_NUM_ROWS, |
| .ninputs = KEYMAP_NUM_COLS, |
| .settle_time.tv.nsec = 40 * NSEC_PER_USEC, |
| .poll_time.tv.nsec = 20 * NSEC_PER_MSEC, |
| .flags = GPIOKPF_LEVEL_TRIGGERED_IRQ | GPIOKPF_REMOVE_PHANTOM_KEYS | GPIOKPF_PRINT_UNMAPPED_KEYS /*| GPIOKPF_PRINT_MAPPED_KEYS*/ |
| }; |
| #endif |
| static struct gpio_event_direct_entry lexikon_keypad_input_map[] = { |
| { |
| .gpio = LEXIKON_GPIO_KEYPAD_POWER_KEY, |
| .code = KEY_POWER, |
| }, |
| { |
| .gpio = PM8058_GPIO_PM_TO_SYS(LEXIKON_VOL_UP), |
| .code = KEY_VOLUMEUP, |
| }, |
| { |
| .gpio = PM8058_GPIO_PM_TO_SYS(LEXIKON_VOL_DN), |
| .code = KEY_VOLUMEDOWN, |
| }, |
| }; |
| |
| static void lexikon_setup_input_gpio(void) |
| { |
| uint32_t inputs_gpio_table[] = { |
| PCOM_GPIO_CFG(LEXIKON_GPIO_KEYPAD_POWER_KEY, 0, GPIO_INPUT, GPIO_PULL_UP, GPIO_4MA), |
| }; |
| |
| config_gpio_table(inputs_gpio_table, ARRAY_SIZE(inputs_gpio_table)); |
| } |
| |
| static struct gpio_event_input_info lexikon_keypad_input_info = { |
| .info.func = gpio_event_input_func, |
| .flags = GPIOEDF_PRINT_KEYS, |
| .type = EV_KEY, |
| .debounce_time.tv.nsec = 5 * NSEC_PER_MSEC, |
| .keymap = lexikon_keypad_input_map, |
| .keymap_size = ARRAY_SIZE(lexikon_keypad_input_map), |
| .setup_input_gpio = lexikon_setup_input_gpio, |
| }; |
| |
| static struct gpio_event_direct_entry lexikon_sliding_switch[] = { |
| { |
| .gpio = LEXIKON_SLIDING_INTZ, |
| .code = SW_LID, |
| }, |
| }; |
| |
| static void lexikon_sliding_input_gpio(void) |
| { |
| static uint32_t inputs_gpio_table[] = { |
| PCOM_GPIO_CFG(LEXIKON_SLIDING_INTZ, 0, GPIO_INPUT, |
| GPIO_NO_PULL, GPIO_4MA), |
| }; |
| |
| config_gpio_table(inputs_gpio_table, |
| ARRAY_SIZE(inputs_gpio_table)); |
| } |
| |
| static void lexikon_set_qty_irq(uint8_t disable) |
| { |
| uint32_t i; |
| static uint8_t already_disabled; |
| pr_info("%s disable=%d, already_disabled=%d\n", |
| __func__, disable, already_disabled); |
| |
| if (!(disable ^ already_disabled)) |
| return; |
| |
| already_disabled = disable; |
| for (i = 0; i < KEYMAP_NUM_COLS; i++) { |
| if (disable) |
| disable_irq(gpio_to_irq(lexikon_pmic_col_gpios[i])); |
| else |
| enable_irq(gpio_to_irq(lexikon_pmic_col_gpios[i])); |
| } |
| } |
| |
| static struct gpio_event_switch_info lexikon_keypad_switch_info = { |
| .info.func = gpio_event_switch_func, |
| #if 1 |
| .flags = GPIOEDF_PRINT_KEYS, |
| #else |
| .flags = GPIOKPF_ACTIVE_HIGH, |
| /* FIXME: since sliding is opened on Latte barebone system, |
| * set the active direction inverse on purpose |
| * just for our convenience in current stage. |
| */ |
| #endif |
| .type = EV_SW, |
| .debounce_time.tv.nsec = 5 * NSEC_PER_MSEC, |
| .keymap = lexikon_sliding_switch, |
| .keymap_size = ARRAY_SIZE(lexikon_sliding_switch), |
| .setup_switch_gpio = lexikon_sliding_input_gpio, |
| .set_qty_irq = lexikon_set_qty_irq, |
| }; |
| |
| static struct gpio_event_info *lexikon_keypad_info[] = { |
| &lexikon_keypad_input_info.info, |
| &lexikon_keypad_switch_info.info, |
| #if CONFIG_MSM_SSBI |
| &lexikon_keypad_matrix_info.info, |
| #endif |
| }; |
| |
| static struct gpio_event_platform_data lexikon_keypad_data = { |
| .names = { |
| "lexikon-keypad", |
| NULL, |
| }, |
| .info = lexikon_keypad_info, |
| .info_count = ARRAY_SIZE(lexikon_keypad_info), |
| }; |
| |
| static struct platform_device lexikon_keypad_input_device = { |
| .name = GPIO_EVENT_DEV_NAME, |
| .id = 0, |
| .dev = { |
| .platform_data = &lexikon_keypad_data, |
| }, |
| }; |
| /* |
| static int lexikon_reset_keys_up[] = { |
| KEY_VOLUMEUP, |
| 0 |
| }; |
| */ |
| static struct keyreset_platform_data lexikon_reset_keys_pdata = { |
| /*.keys_up = lexikon_reset_keys_up,*/ |
| .keys_down = { |
| KEY_POWER, |
| KEY_VOLUMEDOWN, |
| KEY_VOLUMEUP, |
| 0 |
| }, |
| }; |
| |
| struct platform_device lexikon_reset_keys_device = { |
| .name = KEYRESET_NAME, |
| .dev.platform_data = &lexikon_reset_keys_pdata, |
| }; |
| |
| int __init lexikon_init_keypad(void) |
| { |
| printk(KERN_DEBUG "%s\n", __func__); |
| |
| if (platform_device_register(&lexikon_reset_keys_device)) |
| printk(KERN_WARNING "%s: register reset key fail\n", __func__); |
| |
| if (system_rev == 0) { |
| lexikon_pmic_keymap[KEYMAP_INDEX(3, 0)] = KEY_F13; |
| lexikon_pmic_keymap[KEYMAP_INDEX(4, 0)] = KEY_F14; |
| } |
| return platform_device_register(&lexikon_keypad_input_device); |
| } |
| |