* [PATCH v5] input: tegra-kbc - Add tegra keyboard driver @ 2011-01-13 18:27 riyer 2011-01-18 5:12 ` Dmitry Torokhov 0 siblings, 1 reply; 5+ messages in thread From: riyer @ 2011-01-13 18:27 UTC (permalink / raw) To: tsoni, dmitry.torokhov, pavel, shubhrajyoti, ccross, konkers Cc: olof, achew, linux-tegra, linux-kernel, linux-input, Rakesh Iyer From: Rakesh Iyer <riyer@nvidia.com> This patch adds support for the internal matrix keyboard controller for Nvidia Tegra platforms. Signed-off-by: Rakesh Iyer <riyer@nvidia.com> --- Changes Done - Wrap the users field check within the mutex. Remove the KBC_MAX_KEYS define and use existing KBC_MAX_KEY. Patch v4 was named incorrectly as PATCH 1/1, so skipping ahead to PATCH v5. arch/arm/mach-tegra/include/mach/kbc.h | 61 +++ drivers/input/keyboard/Kconfig | 10 + drivers/input/keyboard/Makefile | 1 + drivers/input/keyboard/tegra-kbc.c | 634 ++++++++++++++++++++++++++++++++ 4 files changed, 706 insertions(+), 0 deletions(-) create mode 100644 arch/arm/mach-tegra/include/mach/kbc.h create mode 100644 drivers/input/keyboard/tegra-kbc.c diff --git a/arch/arm/mach-tegra/include/mach/kbc.h b/arch/arm/mach-tegra/include/mach/kbc.h new file mode 100644 index 0000000..029a468 --- /dev/null +++ b/arch/arm/mach-tegra/include/mach/kbc.h @@ -0,0 +1,61 @@ +/* + * kbc.h + * + * Platform definitions for tegra-kbc keyboard input driver + * + * Copyright (c) 2010, NVIDIA Corporation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef ASMARM_ARCH_TEGRA_KBC_H +#define ASMARM_ARCH_TEGRA_KBC_H + +#include <linux/types.h> + +#ifdef CONFIG_ARCH_TEGRA_2x_SOC +#define KBC_MAX_GPIO 24 +#define KBC_MAX_KPENT 8 +#else +#define KBC_MAX_GPIO 20 +#define KBC_MAX_KPENT 7 +#endif + +#define KBC_MAX_ROW 16 +#define KBC_MAX_COL 8 + +#define KBC_MAX_KEY (KBC_MAX_ROW*KBC_MAX_COL) + +struct tegra_kbc_pin_cfg { + bool is_row; + bool is_col; + unsigned char num; +}; + +struct tegra_kbc_wake_key { + u8 row:4; + u8 col:4; +}; + +struct tegra_kbc_platform_data { + unsigned int debounce_cnt; + unsigned int repeat_cnt; + int wake_cnt; /* 0:wake on any key >1:wake on wake_cfg */ + int *keycode; + bool wakeup; + struct tegra_kbc_pin_cfg pin_cfg[KBC_MAX_GPIO]; + struct tegra_kbc_wake_key *wake_cfg; +}; +#endif diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 9cc488d..8be47da 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -327,6 +327,16 @@ config KEYBOARD_NEWTON To compile this driver as a module, choose M here: the module will be called newtonkbd. +config KEYBOARD_TEGRA + tristate "NVIDIA Tegra internal matrix keyboard controller support" + depends on ARCH_TEGRA + help + Say Y here if you want to use a matrix keyboard connected directly + to the internal keyboard controller on Tegra SoCs. + + To compile this driver as a module, choose M here: the + module will be called tegra-kbc. + config KEYBOARD_OPENCORES tristate "OpenCores Keyboard Controller" help diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 504b591..ac0dcb9 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -38,6 +38,7 @@ obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o obj-$(CONFIG_KEYBOARD_STMPE) += stmpe-keypad.o obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o +obj-$(CONFIG_KEYBOARD_TEGRA) += tegra-kbc.o obj-$(CONFIG_KEYBOARD_TWL4030) += twl4030_keypad.o obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o obj-$(CONFIG_KEYBOARD_W90P910) += w90p910_keypad.o diff --git a/drivers/input/keyboard/tegra-kbc.c b/drivers/input/keyboard/tegra-kbc.c new file mode 100644 index 0000000..02c8ece --- /dev/null +++ b/drivers/input/keyboard/tegra-kbc.c @@ -0,0 +1,634 @@ +/* + * tegra-kbc.c + * + * Keyboard class input driver for the NVIDIA Tegra SoC internal matrix + * keyboard controller + * + * Copyright (c) 2009-2010, NVIDIA Corporation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <linux/module.h> +#include <linux/input.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/interrupt.h> +#include <linux/clk.h> +#include <linux/slab.h> +#include <mach/clk.h> +#include <mach/kbc.h> + +#define KBC_MAX_DEBOUNCE_CNT 0x3fful + +/* KBC row scan time and delay for beginning the row scan. */ +#define KBC_ROW_SCAN_TIME 16 +#define KBC_ROW_SCAN_DLY 5 + +/* KBC uses a 32KHz clock so a cycle = 1/32Khz */ +#define KBC_CYCLE_USEC 32 + +/* KBC Registers */ + +/* KBC Control Register */ +#define KBC_CONTROL_0 0x0 +#define KBC_FIFO_TH_CNT_SHIFT(cnt) (cnt << 14) +#define KBC_DEBOUNCE_CNT_SHIFT(cnt) (cnt << 4) +#define KBC_CONTROL_FIFO_CNT_INT_EN (1 << 3) +#define KBC_CONTROL_KBC_EN (1 << 0) + +/* KBC Interrupt Register */ +#define KBC_INT_0 0x4 +#define KBC_INT_FIFO_CNT_INT_STATUS (1 << 2) + +#define KBC_ROW_CFG0_0 0x8 +#define KBC_COL_CFG0_0 0x18 +#define KBC_INIT_DLY_0 0x28 +#define KBC_RPT_DLY_0 0x2c +#define KBC_KP_ENT0_0 0x30 +#define KBC_KP_ENT1_0 0x34 +#define KBC_ROW0_MASK_0 0x38 + +struct tegra_kbc { + void __iomem *mmio; + struct input_dev *idev; + int irq; + unsigned int wake_enable_rows; + unsigned int wake_enable_cols; + spinlock_t lock; + unsigned int repoll_dly; + unsigned long cp_dly_jiffies; + int fifo[KBC_MAX_KPENT]; + const struct tegra_kbc_platform_data *pdata; + int keycode[KBC_MAX_KEY]; + struct timer_list timer; + struct clk *clk; +}; + +static int tegra_kbd_keycode[KBC_MAX_KEY] = { + KEY_RESERVED, KEY_RESERVED, KEY_W, KEY_S, + KEY_A, KEY_Z, KEY_RESERVED, KEY_FN, + KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, + KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_MENU, + KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, + KEY_RESERVED, KEY_RESERVED, KEY_RIGHTALT, KEY_LEFTALT, + KEY_5, KEY_4, KEY_R, KEY_E, + KEY_F, KEY_D, KEY_X, KEY_RESERVED, + KEY_7, KEY_6, KEY_T, KEY_H, + KEY_G, KEY_V, KEY_C, KEY_SPACE, + KEY_9, KEY_8, KEY_U, KEY_Y, + KEY_J, KEY_N, KEY_B, KEY_BACKSLASH, + KEY_MINUS, KEY_0, KEY_O, KEY_I, + KEY_L, KEY_K, KEY_COMMA, KEY_M, + KEY_RESERVED, KEY_EQUAL, KEY_RIGHTBRACE, KEY_ENTER, + KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_MENU, + KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, + KEY_RIGHTSHIFT, KEY_LEFTSHIFT, KEY_RESERVED, KEY_RESERVED, + KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, + KEY_RESERVED, KEY_RIGHTCTRL, KEY_RESERVED, KEY_LEFTCTRL, + KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, + KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, + KEY_LEFTBRACE, KEY_P, KEY_APOSTROPHE, KEY_SEMICOLON, + KEY_SLASH, KEY_DOT, KEY_RESERVED, KEY_RESERVED, + KEY_F10, KEY_F9, KEY_BACKSPACE, KEY_3, + KEY_2, KEY_UP, KEY_PRINT, KEY_PAUSE, + KEY_INSERT, KEY_DELETE, KEY_RESERVED, KEY_PAGEUP, + KEY_PAGEDOWN, KEY_RIGHT, KEY_DOWN, KEY_LEFT, + KEY_F11, KEY_F12, KEY_F8, KEY_Q, + KEY_F4, KEY_F3, KEY_1, KEY_F7, + KEY_ESC, KEY_GRAVE, KEY_F5, KEY_TAB, + KEY_F1, KEY_F2, KEY_CAPSLOCK, KEY_F6 +}; + +static void tegra_kbc_report_keys(struct tegra_kbc *kbc, int *fifo) +{ + int curr_fifo[KBC_MAX_KPENT]; + int rows_val[KBC_MAX_KPENT], cols_val[KBC_MAX_KPENT]; + u32 kp_ent_val[(KBC_MAX_KPENT + 3) / 4]; + u32 *kp_ents = kp_ent_val; + u32 kp_ent = 0; + unsigned long flags; + int i, j, valid = 0; + + spin_lock_irqsave(&kbc->lock, flags); + for (i = 0; i < ARRAY_SIZE(kp_ent_val); i++) + kp_ent_val[i] = readl(kbc->mmio + KBC_KP_ENT0_0 + (i*4)); + spin_unlock_irqrestore(&kbc->lock, flags); + + valid = 0; + for (i = 0; i < KBC_MAX_KPENT; i++) { + if (!(i&3)) + kp_ent = *kp_ents++; + + if (kp_ent & 0x80) { + cols_val[valid] = kp_ent & 0x7; + rows_val[valid++] = (kp_ent >> 3) & 0xf; + } + kp_ent >>= 8; + } + + j = 0; + for (i = 0; i < valid; i++) { + int k = kbc->keycode[(rows_val[i] * KBC_MAX_COL) + cols_val[i]]; + if (likely(k != -1)) + curr_fifo[j++] = k; + } + valid = j; + + for (i = 0; i < KBC_MAX_KPENT; i++) { + if (fifo[i] == -1) + continue; + for (j = 0; j < valid; j++) { + if (curr_fifo[j] == fifo[i]) { + curr_fifo[j] = -1; + break; + } + } + if (j == valid) { + input_report_key(kbc->idev, fifo[i], 0); + fifo[i] = -1; + } + } + for (j = 0; j < valid; j++) { + if (curr_fifo[j] == -1) + continue; + for (i = 0; i < KBC_MAX_KPENT; i++) { + if (fifo[i] == -1) + break; + } + if (i != KBC_MAX_KPENT) { + fifo[i] = curr_fifo[j]; + input_report_key(kbc->idev, fifo[i], 1); + } else + WARN_ON(1); + } +} + +static void tegra_kbc_keypress_timer(unsigned long data) +{ + struct tegra_kbc *kbc = (struct tegra_kbc *)data; + unsigned long flags; + u32 val; + int i; + + val = (readl(kbc->mmio + KBC_INT_0) >> 4) & 0xf; + if (val) { + unsigned long dly; + + tegra_kbc_report_keys(kbc, kbc->fifo); + + /* If more than one keys are pressed we need not wait + * for the repoll delay. */ + dly = (val == 1) ? kbc->repoll_dly : 1; + mod_timer(&kbc->timer, jiffies + msecs_to_jiffies(dly)); + } else { + /* release any pressed keys and exit the loop */ + for (i = 0; i < ARRAY_SIZE(kbc->fifo); i++) { + if (kbc->fifo[i] == -1) + continue; + input_report_key(kbc->idev, kbc->fifo[i], 0); + kbc->fifo[i] = -1; + } + + /* All keys are released so enable the keypress interrupt */ + spin_lock_irqsave(&kbc->lock, flags); + val = readl(kbc->mmio + KBC_CONTROL_0); + val |= KBC_CONTROL_FIFO_CNT_INT_EN; + writel(val, kbc->mmio + KBC_CONTROL_0); + spin_unlock_irqrestore(&kbc->lock, flags); + } +} + +static void tegra_kbc_close(struct input_dev *dev) +{ + struct tegra_kbc *kbc = input_get_drvdata(dev); + unsigned long flags; + u32 val; + + spin_lock_irqsave(&kbc->lock, flags); + val = readl(kbc->mmio + KBC_CONTROL_0); + val &= ~1; + writel(val, kbc->mmio + KBC_CONTROL_0); + spin_unlock_irqrestore(&kbc->lock, flags); + + clk_disable(kbc->clk); +} + +static void tegra_kbc_setup_wakekeys(struct tegra_kbc *kbc, bool filter) +{ + int i; + unsigned int rst_val; + + BUG_ON(kbc->pdata->wake_cnt > KBC_MAX_KEY); + rst_val = (filter && kbc->pdata->wake_cnt) ? ~0 : 0; + + for (i = 0; i < KBC_MAX_ROW; i++) + writel(rst_val, kbc->mmio+KBC_ROW0_MASK_0+i*4); + + if (filter) { + for (i = 0; i < kbc->pdata->wake_cnt; i++) { + u32 val, addr; + addr = kbc->pdata->wake_cfg[i].row*4 + KBC_ROW0_MASK_0; + val = readl(kbc->mmio + addr); + val &= ~(1<<kbc->pdata->wake_cfg[i].col); + writel(val, kbc->mmio + addr); + } + } +} + +static void tegra_kbc_config_pins(struct tegra_kbc *kbc) +{ + const struct tegra_kbc_platform_data *pdata = kbc->pdata; + int i; + + for (i = 0; i < KBC_MAX_GPIO; i++) { + u32 row_cfg, col_cfg; + u32 r_shift = 5 * (i%6); + u32 c_shift = 4 * (i%8); + u32 r_mask = 0x1f << r_shift; + u32 c_mask = 0xf << c_shift; + u32 r_offs = (i / 6) * 4 + KBC_ROW_CFG0_0; + u32 c_offs = (i / 8) * 4 + KBC_COL_CFG0_0; + + row_cfg = readl(kbc->mmio + r_offs); + col_cfg = readl(kbc->mmio + c_offs); + + row_cfg &= ~r_mask; + col_cfg &= ~c_mask; + + if (pdata->pin_cfg[i].is_row) + row_cfg |= ((pdata->pin_cfg[i].num<<1) | 1) << r_shift; + else if (pdata->pin_cfg[i].is_col) + col_cfg |= ((pdata->pin_cfg[i].num<<1) | 1) << c_shift; + + writel(row_cfg, kbc->mmio + r_offs); + writel(col_cfg, kbc->mmio + c_offs); + } +} + +static int tegra_kbc_open(struct input_dev *dev) +{ + struct tegra_kbc *kbc = input_get_drvdata(dev); + unsigned long flags; + unsigned int debounce_cnt; + u32 val = 0; + + clk_enable(kbc->clk); + + /* Reset the KBC controller to clear all previous status.*/ + tegra_periph_reset_assert(kbc->clk); + udelay(100); + tegra_periph_reset_deassert(kbc->clk); + udelay(100); + + tegra_kbc_config_pins(kbc); + tegra_kbc_setup_wakekeys(kbc, false); + + writel(kbc->pdata->repeat_cnt, kbc->mmio + KBC_RPT_DLY_0); + + /* Keyboard debounce count is maximum of 12 bits. */ + debounce_cnt = kbc->pdata->debounce_cnt; + debounce_cnt = min_t(unsigned int, debounce_cnt, KBC_MAX_DEBOUNCE_CNT); + val = KBC_DEBOUNCE_CNT_SHIFT(debounce_cnt); + val |= KBC_FIFO_TH_CNT_SHIFT(1); /* set fifo interrupt threshold to 1 */ + val |= KBC_CONTROL_FIFO_CNT_INT_EN; /* interrupt on FIFO threshold */ + val |= KBC_CONTROL_KBC_EN; /* enable */ + writel(val, kbc->mmio + KBC_CONTROL_0); + + /* Compute the delay(ns) from interrupt mode to continuous polling mode + * so the timer routine is scheduled appropriately. */ + val = readl(kbc->mmio + KBC_INIT_DLY_0); + kbc->cp_dly_jiffies = usecs_to_jiffies((val & 0xfffff) * 32); + + /* atomically clear out any remaining entries in the key FIFO + * and enable keyboard interrupts */ + spin_lock_irqsave(&kbc->lock, flags); + while (1) { + val = readl(kbc->mmio + KBC_INT_0); + val >>= 4; + if (val) { + val = readl(kbc->mmio + KBC_KP_ENT0_0); + val = readl(kbc->mmio + KBC_KP_ENT1_0); + } else { + break; + } + } + writel(0x7, kbc->mmio + KBC_INT_0); + spin_unlock_irqrestore(&kbc->lock, flags); + + return 0; +} + + +static int __devexit tegra_kbc_remove(struct platform_device *pdev) +{ + struct tegra_kbc *kbc = platform_get_drvdata(pdev); + struct resource *res; + + free_irq(kbc->irq, pdev); + del_timer_sync(&kbc->timer); + clk_disable(kbc->clk); + clk_put(kbc->clk); + + input_unregister_device(kbc->idev); + iounmap(kbc->mmio); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, resource_size(res)); + + kfree(kbc); + return 0; +} + +static irqreturn_t tegra_kbc_isr(int irq, void *args) +{ + struct tegra_kbc *kbc = args; + u32 val, ctl; + + /* until all keys are released, defer further processing to + * the polling loop in tegra_kbc_keypress_timer */ + ctl = readl(kbc->mmio + KBC_CONTROL_0); + ctl &= ~KBC_CONTROL_FIFO_CNT_INT_EN; + writel(ctl, kbc->mmio + KBC_CONTROL_0); + + /* quickly bail out & reenable interrupts if the fifo threshold count + * interrupt wasn't the interrupt source */ + val = readl(kbc->mmio + KBC_INT_0); + writel(val, kbc->mmio + KBC_INT_0); + + if (!(val & KBC_INT_FIFO_CNT_INT_STATUS)) { + ctl |= KBC_CONTROL_FIFO_CNT_INT_EN; + writel(ctl, kbc->mmio + KBC_CONTROL_0); + return IRQ_HANDLED; + } + + /* Schedule timer to run when hardware is in continuous polling mode. */ + mod_timer(&kbc->timer, jiffies + kbc->cp_dly_jiffies); + return IRQ_HANDLED; +} + +static int __devinit tegra_kbc_probe(struct platform_device *pdev) +{ + struct tegra_kbc *kbc; + const struct tegra_kbc_platform_data *pdata = pdev->dev.platform_data; + struct resource *res; + int irq; + int err; + int rows[KBC_MAX_ROW]; + int cols[KBC_MAX_COL]; + int i, j; + int num_rows = 0; + unsigned int debounce_cnt; + unsigned int scan_time_rows; + + if (!pdata) + return -EINVAL; + + kbc = kzalloc(sizeof(*kbc), GFP_KERNEL); + if (!kbc) + return -ENOMEM; + + kbc->pdata = pdata; + kbc->irq = -EINVAL; + + memset(rows, 0, sizeof(rows)); + memset(cols, 0, sizeof(cols)); + + kbc->idev = input_allocate_device(); + if (!kbc->idev) { + err = -ENOMEM; + goto fail_allocateinput; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "failed to get I/O memory\n"); + err = -ENXIO; + goto fail_memoryresource; + } + res = request_mem_region(res->start, resource_size(res), pdev->name); + if (!res) { + dev_err(&pdev->dev, "failed to request I/O memory\n"); + err = -EBUSY; + goto fail_memoryresource; + } + kbc->mmio = ioremap(res->start, resource_size(res)); + if (!kbc->mmio) { + dev_err(&pdev->dev, "failed to remap I/O memory\n"); + err = -ENXIO; + goto fail_memoryresource; + } + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "failed to get keyboard IRQ\n"); + err = -ENXIO; + goto fail_keyboardresource; + } + kbc->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR_OR_NULL(kbc->clk)) { + dev_err(&pdev->dev, "failed to get keyboard clock\n"); + err = (kbc->clk) ? PTR_ERR(kbc->clk) : -ENODEV; + kbc->clk = NULL; + goto fail_keyboardresource; + } + + platform_set_drvdata(pdev, kbc); + + kbc->idev->name = pdev->name; + input_set_drvdata(kbc->idev, kbc); + kbc->idev->id.bustype = BUS_HOST; + kbc->idev->open = tegra_kbc_open; + kbc->idev->close = tegra_kbc_close; + kbc->idev->dev.parent = &pdev->dev; + spin_lock_init(&kbc->lock); + + for (i = 0; i < KBC_MAX_GPIO; i++) { + if (pdata->pin_cfg[i].is_row && pdata->pin_cfg[i].is_col) { + dev_err(&pdev->dev, "invalid pin configuration data\n"); + err = -EINVAL; + goto fail_configurekeyboard; + } + + if (pdata->pin_cfg[i].is_row) { + if (pdata->pin_cfg[i].num >= KBC_MAX_ROW) { + dev_err(&pdev->dev, "invalid row number\n"); + err = -EINVAL; + goto fail_configurekeyboard; + } + rows[pdata->pin_cfg[i].num] = 1; + num_rows++; + } else if (pdata->pin_cfg[i].is_col) { + if (pdata->pin_cfg[i].num >= KBC_MAX_COL) { + dev_err(&pdev->dev, "invalid column number\n"); + err = -EINVAL; + goto fail_configurekeyboard; + } + cols[pdata->pin_cfg[i].num] = 1; + } + } + kbc->wake_enable_rows = 0; + kbc->wake_enable_cols = 0; + + for (i = 0; i < pdata->wake_cnt; i++) { + kbc->wake_enable_rows |= (1 << kbc->pdata->wake_cfg[i].row); + kbc->wake_enable_cols |= (1 << kbc->pdata->wake_cfg[i].col); + } + + debounce_cnt = pdata->debounce_cnt; + debounce_cnt = min_t(unsigned int, debounce_cnt, KBC_MAX_DEBOUNCE_CNT); + + /* The time delay between two consecutive reads of the FIFO is the sum + * of the repeat time and the time taken for scanning the rows. + * There is an additional delay before the row scanning starts. + * The repoll delay is computed in milliseconds. */ + scan_time_rows = (KBC_ROW_SCAN_TIME + debounce_cnt) * num_rows; + kbc->repoll_dly = KBC_ROW_SCAN_DLY + scan_time_rows + pdata->repeat_cnt; + kbc->repoll_dly = ((kbc->repoll_dly * KBC_CYCLE_USEC) + 999) / 1000; + + kbc->idev->evbit[0] = BIT_MASK(EV_KEY); + + /* Override the default keycodes with the board supplied ones. */ + if (pdata->keycode) + memcpy(kbc->keycode, pdata->keycode, sizeof(kbc->keycode)); + else + memcpy(kbc->keycode, tegra_kbd_keycode, sizeof(kbc->keycode)); + + kbc->idev->keycode = kbc->keycode; + kbc->idev->keycodesize = sizeof(kbc->keycode[0]); + kbc->idev->keycodemax = ARRAY_SIZE(kbc->keycode); + + for (i = 0; i < KBC_MAX_COL; i++) { + if (!cols[i]) + continue; + for (j = 0; j < KBC_MAX_ROW; j++) { + int keycode; + + if (!rows[j]) + continue; + + /* enable all the mapped keys. */ + keycode = kbc->keycode[(j * KBC_MAX_COL) + i]; + if (keycode != -1) + set_bit(keycode, kbc->idev->keybit); + + } + } + + setup_timer(&kbc->timer, tegra_kbc_keypress_timer, (unsigned long)kbc); + /* Initialize the FIFO to invalid entries */ + for (i = 0; i < ARRAY_SIZE(kbc->fifo); i++) + kbc->fifo[i] = -1; + + /* keycode FIFO needs to be read atomically; leave local + * interrupts disabled when handling KBC interrupt */ + err = request_irq(irq, tegra_kbc_isr, IRQF_TRIGGER_HIGH, + pdev->name, kbc); + if (err) { + dev_err(&pdev->dev, "failed to request keyboard IRQ\n"); + goto fail_configurekeyboard; + } + kbc->irq = irq; + + err = input_register_device(kbc->idev); + if (err) { + dev_err(&pdev->dev, "failed to register input device\n"); + goto fail_registerinput; + } + + device_init_wakeup(&pdev->dev, pdata->wakeup); + return 0; + +fail_registerinput: + free_irq(kbc->irq, pdev); +fail_configurekeyboard: + clk_put(kbc->clk); +fail_keyboardresource: + iounmap(kbc->mmio); +fail_memoryresource: + input_free_device(kbc->idev); +fail_allocateinput: + kfree(kbc); + return err; +} + +#ifdef CONFIG_PM +static int tegra_kbc_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct tegra_kbc *kbc = platform_get_drvdata(pdev); + + if (device_may_wakeup(&pdev->dev)) { + tegra_kbc_setup_wakekeys(kbc, true); + enable_irq_wake(kbc->irq); + /* Forcefully clear the interrupt status */ + writel(0x7, kbc->mmio + KBC_INT_0); + msleep(30); + } else { + mutex_lock(&kbc->idev->mutex); + if (kbc->idev->users) + tegra_kbc_close(kbc->idev); + mutex_unlock(&kbc->idev->mutex); + } + + return 0; +} + +static int tegra_kbc_resume(struct platform_device *pdev) +{ + struct tegra_kbc *kbc = platform_get_drvdata(pdev); + int err = 0; + + if (device_may_wakeup(&pdev->dev)) { + disable_irq_wake(kbc->irq); + tegra_kbc_setup_wakekeys(kbc, false); + } else { + mutex_lock(&kbc->idev->mutex); + if (kbc->idev->users) + err = tegra_kbc_open(kbc->idev); + mutex_unlock(&kbc->idev->mutex); + } + + return err; +} +#endif + +static struct platform_driver tegra_kbc_driver = { + .probe = tegra_kbc_probe, + .remove = __devexit_p(tegra_kbc_remove), +#ifdef CONFIG_PM + .suspend = tegra_kbc_suspend, + .resume = tegra_kbc_resume, +#endif + .driver = { + .name = "tegra-kbc", + .owner = THIS_MODULE, + } +}; + +static void __exit tegra_kbc_exit(void) +{ + platform_driver_unregister(&tegra_kbc_driver); +} +module_exit(tegra_kbc_exit); + +static int __init tegra_kbc_init(void) +{ + return platform_driver_register(&tegra_kbc_driver); +} +module_init(tegra_kbc_init); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Rakesh Iyer <riyer@nvidia.com>"); +MODULE_DESCRIPTION("Tegra matrix keyboard controller driver"); +MODULE_ALIAS("platform:tegra-kbc"); -- 1.7.0.4 ^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH v5] input: tegra-kbc - Add tegra keyboard driver 2011-01-13 18:27 [PATCH v5] input: tegra-kbc - Add tegra keyboard driver riyer @ 2011-01-18 5:12 ` Dmitry Torokhov 2011-01-18 5:13 ` Dmitry Torokhov 2011-01-19 3:28 ` Rakesh Iyer 0 siblings, 2 replies; 5+ messages in thread From: Dmitry Torokhov @ 2011-01-18 5:12 UTC (permalink / raw) To: riyer Cc: tsoni, pavel, shubhrajyoti, ccross, konkers, olof, achew, linux-tegra, linux-kernel, linux-input On Thu, Jan 13, 2011 at 10:27:28AM -0800, riyer@nvidia.com wrote: > From: Rakesh Iyer <riyer@nvidia.com> > > This patch adds support for the internal matrix keyboard controller for > Nvidia Tegra platforms. > > Signed-off-by: Rakesh Iyer <riyer@nvidia.com> > --- > Changes Done - > Wrap the users field check within the mutex. > Remove the KBC_MAX_KEYS define and use existing KBC_MAX_KEY. > Patch v4 was named incorrectly as PATCH 1/1, so skipping ahead to PATCH v5. > As I said, we should tty to reused definitions from matrix_keypad for matrix keypads. Does the following still work for you? Thanks. -- Dmitry Input: tegra-kbc - convert to use matrix-keypad definitions From: Dmitry Torokhov <dmitry.torokhov@gmail.com> and other miscellaneous rearrangements. Signed-off-by: Dmitry Torokhov <dtor@mail.ru> --- arch/arm/mach-tegra/include/mach/kbc.h | 28 + drivers/input/keyboard/tegra-kbc.c | 679 ++++++++++++++++++-------------- 2 files changed, 399 insertions(+), 308 deletions(-) diff --git a/arch/arm/mach-tegra/include/mach/kbc.h b/arch/arm/mach-tegra/include/mach/kbc.h index 029a468..adb267f 100644 --- a/arch/arm/mach-tegra/include/mach/kbc.h +++ b/arch/arm/mach-tegra/include/mach/kbc.h @@ -1,6 +1,4 @@ /* - * kbc.h - * * Platform definitions for tegra-kbc keyboard input driver * * Copyright (c) 2010, NVIDIA Corporation. @@ -24,23 +22,18 @@ #define ASMARM_ARCH_TEGRA_KBC_H #include <linux/types.h> +#include <linux/input/matrix_keypad.h> #ifdef CONFIG_ARCH_TEGRA_2x_SOC -#define KBC_MAX_GPIO 24 -#define KBC_MAX_KPENT 8 +#define KBC_MAX_GPIO 24 +#define KBC_MAX_KPENT 8 #else -#define KBC_MAX_GPIO 20 -#define KBC_MAX_KPENT 7 +#define KBC_MAX_GPIO 20 +#define KBC_MAX_KPENT 7 #endif -#define KBC_MAX_ROW 16 -#define KBC_MAX_COL 8 - -#define KBC_MAX_KEY (KBC_MAX_ROW*KBC_MAX_COL) - struct tegra_kbc_pin_cfg { bool is_row; - bool is_col; unsigned char num; }; @@ -52,10 +45,13 @@ struct tegra_kbc_wake_key { struct tegra_kbc_platform_data { unsigned int debounce_cnt; unsigned int repeat_cnt; - int wake_cnt; /* 0:wake on any key >1:wake on wake_cfg */ - int *keycode; - bool wakeup; + + unsigned int wake_cnt; /* 0:wake on any key >1:wake on wake_cfg */ + const struct tegra_kbc_wake_key *wake_cfg; + struct tegra_kbc_pin_cfg pin_cfg[KBC_MAX_GPIO]; - struct tegra_kbc_wake_key *wake_cfg; + const struct matrix_keymap_data *keymap_data; + + bool wakeup; }; #endif diff --git a/drivers/input/keyboard/tegra-kbc.c b/drivers/input/keyboard/tegra-kbc.c index 02c8ece..a6ec3fc 100644 --- a/drivers/input/keyboard/tegra-kbc.c +++ b/drivers/input/keyboard/tegra-kbc.c @@ -1,6 +1,4 @@ /* - * tegra-kbc.c - * * Keyboard class input driver for the NVIDIA Tegra SoC internal matrix * keyboard controller * @@ -32,7 +30,7 @@ #include <mach/clk.h> #include <mach/kbc.h> -#define KBC_MAX_DEBOUNCE_CNT 0x3fful +#define KBC_MAX_DEBOUNCE_CNT 0x3ffu /* KBC row scan time and delay for beginning the row scan. */ #define KBC_ROW_SCAN_TIME 16 @@ -62,119 +60,201 @@ #define KBC_KP_ENT1_0 0x34 #define KBC_ROW0_MASK_0 0x38 +#define KBC_MAX_ROW 16 +#define KBC_MAX_COL 8 +#define KBC_ROW_SHIFT 3 +#define KBC_MAX_KEY (KBC_MAX_ROW * KBC_MAX_COL) + struct tegra_kbc { void __iomem *mmio; struct input_dev *idev; - int irq; + unsigned int irq; unsigned int wake_enable_rows; unsigned int wake_enable_cols; spinlock_t lock; unsigned int repoll_dly; unsigned long cp_dly_jiffies; - int fifo[KBC_MAX_KPENT]; const struct tegra_kbc_platform_data *pdata; - int keycode[KBC_MAX_KEY]; + unsigned short keycode[KBC_MAX_KEY]; + unsigned short current_keys[KBC_MAX_KPENT]; + unsigned int num_pressed_keys; struct timer_list timer; struct clk *clk; }; -static int tegra_kbd_keycode[KBC_MAX_KEY] = { - KEY_RESERVED, KEY_RESERVED, KEY_W, KEY_S, - KEY_A, KEY_Z, KEY_RESERVED, KEY_FN, - KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, - KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_MENU, - KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, - KEY_RESERVED, KEY_RESERVED, KEY_RIGHTALT, KEY_LEFTALT, - KEY_5, KEY_4, KEY_R, KEY_E, - KEY_F, KEY_D, KEY_X, KEY_RESERVED, - KEY_7, KEY_6, KEY_T, KEY_H, - KEY_G, KEY_V, KEY_C, KEY_SPACE, - KEY_9, KEY_8, KEY_U, KEY_Y, - KEY_J, KEY_N, KEY_B, KEY_BACKSLASH, - KEY_MINUS, KEY_0, KEY_O, KEY_I, - KEY_L, KEY_K, KEY_COMMA, KEY_M, - KEY_RESERVED, KEY_EQUAL, KEY_RIGHTBRACE, KEY_ENTER, - KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_MENU, - KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, - KEY_RIGHTSHIFT, KEY_LEFTSHIFT, KEY_RESERVED, KEY_RESERVED, - KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, - KEY_RESERVED, KEY_RIGHTCTRL, KEY_RESERVED, KEY_LEFTCTRL, - KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, - KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, - KEY_LEFTBRACE, KEY_P, KEY_APOSTROPHE, KEY_SEMICOLON, - KEY_SLASH, KEY_DOT, KEY_RESERVED, KEY_RESERVED, - KEY_F10, KEY_F9, KEY_BACKSPACE, KEY_3, - KEY_2, KEY_UP, KEY_PRINT, KEY_PAUSE, - KEY_INSERT, KEY_DELETE, KEY_RESERVED, KEY_PAGEUP, - KEY_PAGEDOWN, KEY_RIGHT, KEY_DOWN, KEY_LEFT, - KEY_F11, KEY_F12, KEY_F8, KEY_Q, - KEY_F4, KEY_F3, KEY_1, KEY_F7, - KEY_ESC, KEY_GRAVE, KEY_F5, KEY_TAB, - KEY_F1, KEY_F2, KEY_CAPSLOCK, KEY_F6 +static const u32 tegra_kbc_default_keymap[] = { + KEY(0, 2, KEY_W), + KEY(0, 3, KEY_S), + KEY(0, 4, KEY_A), + KEY(0, 5, KEY_Z), + KEY(0, 7, KEY_FN), + + KEY(1, 7, KEY_MENU), + + KEY(2, 6, KEY_RIGHTALT), + KEY(2, 7, KEY_LEFTALT), + + KEY(3, 0, KEY_5), + KEY(3, 1, KEY_4), + KEY(3, 2, KEY_R), + KEY(3, 3, KEY_E), + KEY(3, 4, KEY_F), + KEY(3, 5, KEY_D), + KEY(3, 6, KEY_X), + + KEY(4, 0, KEY_7), + KEY(4, 1, KEY_6), + KEY(4, 2, KEY_T), + KEY(4, 3, KEY_H), + KEY(4, 4, KEY_G), + KEY(4, 5, KEY_V), + KEY(4, 6, KEY_C), + KEY(4, 7, KEY_SPACE), + + KEY(5, 0, KEY_9), + KEY(5, 1, KEY_8), + KEY(5, 2, KEY_U), + KEY(5, 3, KEY_Y), + KEY(5, 4, KEY_J), + KEY(5, 5, KEY_N), + KEY(5, 6, KEY_B), + KEY(5, 7, KEY_BACKSLASH), + + KEY(6, 0, KEY_MINUS), + KEY(6, 1, KEY_0), + KEY(6, 2, KEY_O), + KEY(6, 3, KEY_I), + KEY(6, 4, KEY_L), + KEY(6, 5, KEY_K), + KEY(6, 6, KEY_COMMA), + KEY(6, 7, KEY_M), + + KEY(7, 1, KEY_EQUAL), + KEY(7, 2, KEY_RIGHTBRACE), + KEY(7, 3, KEY_ENTER), + KEY(7, 7, KEY_MENU), + + KEY(8, 4, KEY_RIGHTSHIFT), + KEY(8, 5, KEY_LEFTSHIFT), + + KEY(9, 5, KEY_RIGHTCTRL), + KEY(9, 7, KEY_LEFTCTRL), + + KEY(11, 0, KEY_LEFTBRACE), + KEY(11, 1, KEY_P), + KEY(11, 2, KEY_APOSTROPHE), + KEY(11, 3, KEY_SEMICOLON), + KEY(11, 4, KEY_SLASH), + KEY(11, 5, KEY_DOT), + + KEY(12, 0, KEY_F10), + KEY(12, 1, KEY_F9), + KEY(12, 2, KEY_BACKSPACE), + KEY(12, 3, KEY_3), + KEY(12, 4, KEY_2), + KEY(12, 5, KEY_UP), + KEY(12, 6, KEY_PRINT), + KEY(12, 7, KEY_PAUSE), + + KEY(13, 0, KEY_INSERT), + KEY(13, 1, KEY_DELETE), + KEY(13, 3, KEY_PAGEUP), + KEY(13, 4, KEY_PAGEDOWN), + KEY(13, 5, KEY_RIGHT), + KEY(13, 6, KEY_DOWN), + KEY(13, 7, KEY_LEFT), + + KEY(14, 0, KEY_F11), + KEY(14, 1, KEY_F12), + KEY(14, 2, KEY_F8), + KEY(14, 3, KEY_Q), + KEY(14, 4, KEY_F4), + KEY(14, 5, KEY_F3), + KEY(14, 6, KEY_1), + KEY(14, 7, KEY_F7), + + KEY(15, 0, KEY_ESC), + KEY(15, 1, KEY_GRAVE), + KEY(15, 2, KEY_F5), + KEY(15, 3, KEY_TAB), + KEY(15, 4, KEY_F1), + KEY(15, 5, KEY_F2), + KEY(15, 6, KEY_CAPSLOCK), + KEY(15, 7, KEY_F6), }; -static void tegra_kbc_report_keys(struct tegra_kbc *kbc, int *fifo) -{ - int curr_fifo[KBC_MAX_KPENT]; - int rows_val[KBC_MAX_KPENT], cols_val[KBC_MAX_KPENT]; - u32 kp_ent_val[(KBC_MAX_KPENT + 3) / 4]; - u32 *kp_ents = kp_ent_val; - u32 kp_ent = 0; - unsigned long flags; - int i, j, valid = 0; +static const struct matrix_keymap_data tegra_kbc_default_keymap_data = { + .keymap = tegra_kbc_default_keymap, + .keymap_size = ARRAY_SIZE(tegra_kbc_default_keymap), +}; - spin_lock_irqsave(&kbc->lock, flags); - for (i = 0; i < ARRAY_SIZE(kp_ent_val); i++) - kp_ent_val[i] = readl(kbc->mmio + KBC_KP_ENT0_0 + (i*4)); - spin_unlock_irqrestore(&kbc->lock, flags); +static void tegra_kbc_report_released_keys(struct input_dev *input, + unsigned short old_keycodes[], + unsigned int old_num_keys, + unsigned short new_keycodes[], + unsigned int new_num_keys) +{ + unsigned int i, j; - valid = 0; - for (i = 0; i < KBC_MAX_KPENT; i++) { - if (!(i&3)) - kp_ent = *kp_ents++; + for (i = 0; i < old_num_keys; i++) { + for (j = 0; j < new_num_keys; j++) + if (old_keycodes[i] == new_keycodes[j]) + break; - if (kp_ent & 0x80) { - cols_val[valid] = kp_ent & 0x7; - rows_val[valid++] = (kp_ent >> 3) & 0xf; - } - kp_ent >>= 8; + if (j == new_num_keys) + input_report_key(input, old_keycodes[i], 0); } +} - j = 0; - for (i = 0; i < valid; i++) { - int k = kbc->keycode[(rows_val[i] * KBC_MAX_COL) + cols_val[i]]; - if (likely(k != -1)) - curr_fifo[j++] = k; +static void tegra_kbc_report_pressed_keys(struct input_dev *input, + unsigned char scancodes[], + unsigned short keycodes[], + unsigned int num_pressed_keys) +{ + unsigned int i; + + for (i = 0; i < num_pressed_keys; i++) { + input_event(input, EV_MSC, MSC_SCAN, scancodes[i]); + input_report_key(input, keycodes[i], 1); } - valid = j; +} + +static void tegra_kbc_report_keys(struct tegra_kbc *kbc) +{ + unsigned char scancodes[KBC_MAX_KPENT]; + unsigned short keycodes[KBC_MAX_KPENT]; + u32 val = 0; + unsigned int i; + unsigned int num_down = 0; + unsigned long flags; + spin_lock_irqsave(&kbc->lock, flags); for (i = 0; i < KBC_MAX_KPENT; i++) { - if (fifo[i] == -1) - continue; - for (j = 0; j < valid; j++) { - if (curr_fifo[j] == fifo[i]) { - curr_fifo[j] = -1; - break; - } - } - if (j == valid) { - input_report_key(kbc->idev, fifo[i], 0); - fifo[i] = -1; - } - } - for (j = 0; j < valid; j++) { - if (curr_fifo[j] == -1) - continue; - for (i = 0; i < KBC_MAX_KPENT; i++) { - if (fifo[i] == -1) - break; + if ((i % 4) == 0) + val = readl(kbc->mmio + KBC_KP_ENT0_0 + (i * 4)); + + if (val & 0x80) { + unsigned int col = val & 0x07; + unsigned int row = (val >> 3) & 0x0f; + unsigned char scancode = + MATRIX_SCAN_CODE(row, col, KBC_ROW_SHIFT); + + scancodes[num_down] = scancode; + keycodes[num_down++] = kbc->keycode[scancode]; } - if (i != KBC_MAX_KPENT) { - fifo[i] = curr_fifo[j]; - input_report_key(kbc->idev, fifo[i], 1); - } else - WARN_ON(1); + + val >>= 8; } + spin_unlock_irqrestore(&kbc->lock, flags); + + tegra_kbc_report_released_keys(kbc->idev, + kbc->current_keys, kbc->num_pressed_keys, + keycodes, num_down); + tegra_kbc_report_pressed_keys(kbc->idev, scancodes, keycodes, num_down); + input_sync(kbc->idev); + + memcpy(kbc->current_keys, keycodes, sizeof(kbc->current_keys)); + kbc->num_pressed_keys = num_down; } static void tegra_kbc_keypress_timer(unsigned long data) @@ -182,26 +262,27 @@ static void tegra_kbc_keypress_timer(unsigned long data) struct tegra_kbc *kbc = (struct tegra_kbc *)data; unsigned long flags; u32 val; - int i; + unsigned int i; val = (readl(kbc->mmio + KBC_INT_0) >> 4) & 0xf; if (val) { unsigned long dly; - tegra_kbc_report_keys(kbc, kbc->fifo); + tegra_kbc_report_keys(kbc); - /* If more than one keys are pressed we need not wait - * for the repoll delay. */ + /* + * If more than one keys are pressed we need not wait + * for the repoll delay. + */ dly = (val == 1) ? kbc->repoll_dly : 1; mod_timer(&kbc->timer, jiffies + msecs_to_jiffies(dly)); } else { - /* release any pressed keys and exit the loop */ - for (i = 0; i < ARRAY_SIZE(kbc->fifo); i++) { - if (kbc->fifo[i] == -1) - continue; - input_report_key(kbc->idev, kbc->fifo[i], 0); - kbc->fifo[i] = -1; - } + /* Release any pressed keys and exit the polling loop */ + for (i = 0; i < kbc->num_pressed_keys; i++) + input_report_key(kbc->idev, kbc->current_keys[i], 0); + input_sync(kbc->idev); + + kbc->num_pressed_keys = 0; /* All keys are released so enable the keypress interrupt */ spin_lock_irqsave(&kbc->lock, flags); @@ -212,38 +293,58 @@ static void tegra_kbc_keypress_timer(unsigned long data) } } -static void tegra_kbc_close(struct input_dev *dev) +static irqreturn_t tegra_kbc_isr(int irq, void *args) { - struct tegra_kbc *kbc = input_get_drvdata(dev); - unsigned long flags; - u32 val; + struct tegra_kbc *kbc = args; + u32 val, ctl; - spin_lock_irqsave(&kbc->lock, flags); - val = readl(kbc->mmio + KBC_CONTROL_0); - val &= ~1; - writel(val, kbc->mmio + KBC_CONTROL_0); - spin_unlock_irqrestore(&kbc->lock, flags); + /* + * Until all keys are released, defer further processing to + * the polling loop in tegra_kbc_keypress_timer + */ + ctl = readl(kbc->mmio + KBC_CONTROL_0); + ctl &= ~KBC_CONTROL_FIFO_CNT_INT_EN; + writel(ctl, kbc->mmio + KBC_CONTROL_0); - clk_disable(kbc->clk); + /* + * Quickly bail out & reenable interrupts if the fifo threshold + * count interrupt wasn't the interrupt source + */ + val = readl(kbc->mmio + KBC_INT_0); + writel(val, kbc->mmio + KBC_INT_0); + + if (val & KBC_INT_FIFO_CNT_INT_STATUS) { + /* + * Schedule timer to run when hardware is in continuous + * polling mode. + */ + mod_timer(&kbc->timer, jiffies + kbc->cp_dly_jiffies); + } else { + ctl |= KBC_CONTROL_FIFO_CNT_INT_EN; + writel(ctl, kbc->mmio + KBC_CONTROL_0); + } + + return IRQ_HANDLED; } static void tegra_kbc_setup_wakekeys(struct tegra_kbc *kbc, bool filter) { + const struct tegra_kbc_platform_data *pdata = kbc->pdata; int i; unsigned int rst_val; - BUG_ON(kbc->pdata->wake_cnt > KBC_MAX_KEY); - rst_val = (filter && kbc->pdata->wake_cnt) ? ~0 : 0; + BUG_ON(pdata->wake_cnt > KBC_MAX_KEY); + rst_val = (filter && pdata->wake_cnt) ? ~0 : 0; for (i = 0; i < KBC_MAX_ROW; i++) - writel(rst_val, kbc->mmio+KBC_ROW0_MASK_0+i*4); + writel(rst_val, kbc->mmio + KBC_ROW0_MASK_0 + i * 4); if (filter) { - for (i = 0; i < kbc->pdata->wake_cnt; i++) { + for (i = 0; i < pdata->wake_cnt; i++) { u32 val, addr; - addr = kbc->pdata->wake_cfg[i].row*4 + KBC_ROW0_MASK_0; + addr = pdata->wake_cfg[i].row * 4 + KBC_ROW0_MASK_0; val = readl(kbc->mmio + addr); - val &= ~(1<<kbc->pdata->wake_cfg[i].col); + val &= ~(1 << pdata->wake_cfg[i].col); writel(val, kbc->mmio + addr); } } @@ -255,33 +356,31 @@ static void tegra_kbc_config_pins(struct tegra_kbc *kbc) int i; for (i = 0; i < KBC_MAX_GPIO; i++) { - u32 row_cfg, col_cfg; - u32 r_shift = 5 * (i%6); - u32 c_shift = 4 * (i%8); + u32 r_shift = 5 * (i % 6); + u32 c_shift = 4 * (i % 8); u32 r_mask = 0x1f << r_shift; - u32 c_mask = 0xf << c_shift; + u32 c_mask = 0x0f << c_shift; u32 r_offs = (i / 6) * 4 + KBC_ROW_CFG0_0; u32 c_offs = (i / 8) * 4 + KBC_COL_CFG0_0; - - row_cfg = readl(kbc->mmio + r_offs); - col_cfg = readl(kbc->mmio + c_offs); + u32 row_cfg = readl(kbc->mmio + r_offs); + u32 col_cfg = readl(kbc->mmio + c_offs); row_cfg &= ~r_mask; col_cfg &= ~c_mask; if (pdata->pin_cfg[i].is_row) - row_cfg |= ((pdata->pin_cfg[i].num<<1) | 1) << r_shift; - else if (pdata->pin_cfg[i].is_col) - col_cfg |= ((pdata->pin_cfg[i].num<<1) | 1) << c_shift; + row_cfg |= ((pdata->pin_cfg[i].num << 1) | 1) << r_shift; + else + col_cfg |= ((pdata->pin_cfg[i].num << 1) | 1) << c_shift; writel(row_cfg, kbc->mmio + r_offs); writel(col_cfg, kbc->mmio + c_offs); } } -static int tegra_kbc_open(struct input_dev *dev) +static int tegra_kbc_start(struct tegra_kbc *kbc) { - struct tegra_kbc *kbc = input_get_drvdata(dev); + const struct tegra_kbc_platform_data *pdata = kbc->pdata; unsigned long flags; unsigned int debounce_cnt; u32 val = 0; @@ -297,98 +396,120 @@ static int tegra_kbc_open(struct input_dev *dev) tegra_kbc_config_pins(kbc); tegra_kbc_setup_wakekeys(kbc, false); - writel(kbc->pdata->repeat_cnt, kbc->mmio + KBC_RPT_DLY_0); + writel(pdata->repeat_cnt, kbc->mmio + KBC_RPT_DLY_0); /* Keyboard debounce count is maximum of 12 bits. */ - debounce_cnt = kbc->pdata->debounce_cnt; - debounce_cnt = min_t(unsigned int, debounce_cnt, KBC_MAX_DEBOUNCE_CNT); + debounce_cnt = min(pdata->debounce_cnt, KBC_MAX_DEBOUNCE_CNT); val = KBC_DEBOUNCE_CNT_SHIFT(debounce_cnt); val |= KBC_FIFO_TH_CNT_SHIFT(1); /* set fifo interrupt threshold to 1 */ val |= KBC_CONTROL_FIFO_CNT_INT_EN; /* interrupt on FIFO threshold */ val |= KBC_CONTROL_KBC_EN; /* enable */ writel(val, kbc->mmio + KBC_CONTROL_0); - /* Compute the delay(ns) from interrupt mode to continuous polling mode - * so the timer routine is scheduled appropriately. */ + /* + * Compute the delay(ns) from interrupt mode to continuous polling + * mode so the timer routine is scheduled appropriately. + */ val = readl(kbc->mmio + KBC_INIT_DLY_0); kbc->cp_dly_jiffies = usecs_to_jiffies((val & 0xfffff) * 32); - /* atomically clear out any remaining entries in the key FIFO - * and enable keyboard interrupts */ + kbc->num_pressed_keys = 0; + + /* + * Atomically clear out any remaining entries in the key FIFO + * and enable keyboard interrupts. + */ spin_lock_irqsave(&kbc->lock, flags); while (1) { val = readl(kbc->mmio + KBC_INT_0); val >>= 4; - if (val) { - val = readl(kbc->mmio + KBC_KP_ENT0_0); - val = readl(kbc->mmio + KBC_KP_ENT1_0); - } else { + if (!val) break; - } + + val = readl(kbc->mmio + KBC_KP_ENT0_0); + val = readl(kbc->mmio + KBC_KP_ENT1_0); } writel(0x7, kbc->mmio + KBC_INT_0); spin_unlock_irqrestore(&kbc->lock, flags); + enable_irq(kbc->irq); + return 0; } - -static int __devexit tegra_kbc_remove(struct platform_device *pdev) +static void tegra_kbc_stop( struct tegra_kbc *kbc) { - struct tegra_kbc *kbc = platform_get_drvdata(pdev); - struct resource *res; + unsigned long flags; + u32 val; - free_irq(kbc->irq, pdev); + spin_lock_irqsave(&kbc->lock, flags); + val = readl(kbc->mmio + KBC_CONTROL_0); + val &= ~1; + writel(val, kbc->mmio + KBC_CONTROL_0); + spin_unlock_irqrestore(&kbc->lock, flags); + + disable_irq(kbc->irq); del_timer_sync(&kbc->timer); + clk_disable(kbc->clk); - clk_put(kbc->clk); +} - input_unregister_device(kbc->idev); - iounmap(kbc->mmio); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(res->start, resource_size(res)); +static int tegra_kbc_open(struct input_dev *dev) +{ + struct tegra_kbc *kbc = input_get_drvdata(dev); - kfree(kbc); - return 0; + return tegra_kbc_start(kbc); } -static irqreturn_t tegra_kbc_isr(int irq, void *args) +static void tegra_kbc_close(struct input_dev *dev) { - struct tegra_kbc *kbc = args; - u32 val, ctl; + struct tegra_kbc *kbc = input_get_drvdata(dev); - /* until all keys are released, defer further processing to - * the polling loop in tegra_kbc_keypress_timer */ - ctl = readl(kbc->mmio + KBC_CONTROL_0); - ctl &= ~KBC_CONTROL_FIFO_CNT_INT_EN; - writel(ctl, kbc->mmio + KBC_CONTROL_0); + return tegra_kbc_stop(kbc); +} - /* quickly bail out & reenable interrupts if the fifo threshold count - * interrupt wasn't the interrupt source */ - val = readl(kbc->mmio + KBC_INT_0); - writel(val, kbc->mmio + KBC_INT_0); +static bool __devinit +tegra_kbc_check_pin_cfg(const struct tegra_kbc_platform_data *pdata, + struct device *dev, unsigned int *num_rows) +{ + int i; - if (!(val & KBC_INT_FIFO_CNT_INT_STATUS)) { - ctl |= KBC_CONTROL_FIFO_CNT_INT_EN; - writel(ctl, kbc->mmio + KBC_CONTROL_0); - return IRQ_HANDLED; + *num_rows = 0; + + for (i = 0; i < KBC_MAX_GPIO; i++) { + const struct tegra_kbc_pin_cfg *pin_cfg = &pdata->pin_cfg[i]; + + if (pin_cfg->is_row) { + if (pin_cfg->num >= KBC_MAX_ROW) { + dev_err(dev, + "pin_cfg[%d]: invalid row number %d\n", + i, pin_cfg->num); + return false; + } + (*num_rows)++; + } else { + if (pin_cfg->num >= KBC_MAX_COL) { + dev_err(dev, + "pin_cfg[%d]: invalid column number %d\n", + i, pin_cfg->num); + return false; + } + } } - /* Schedule timer to run when hardware is in continuous polling mode. */ - mod_timer(&kbc->timer, jiffies + kbc->cp_dly_jiffies); - return IRQ_HANDLED; + return true; } static int __devinit tegra_kbc_probe(struct platform_device *pdev) { - struct tegra_kbc *kbc; const struct tegra_kbc_platform_data *pdata = pdev->dev.platform_data; + const struct matrix_keymap_data *keymap_data; + struct tegra_kbc *kbc; + struct input_dev *input_dev; struct resource *res; int irq; int err; - int rows[KBC_MAX_ROW]; - int cols[KBC_MAX_COL]; - int i, j; + int i; int num_rows = 0; unsigned int debounce_cnt; unsigned int scan_time_rows; @@ -396,173 +517,147 @@ static int __devinit tegra_kbc_probe(struct platform_device *pdev) if (!pdata) return -EINVAL; - kbc = kzalloc(sizeof(*kbc), GFP_KERNEL); - if (!kbc) - return -ENOMEM; + if (!tegra_kbc_check_pin_cfg(pdata, &pdev->dev, &num_rows)) + return -EINVAL; - kbc->pdata = pdata; - kbc->irq = -EINVAL; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "failed to get I/O memory\n"); + return -ENXIO; + } - memset(rows, 0, sizeof(rows)); - memset(cols, 0, sizeof(cols)); + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "failed to get keyboard IRQ\n"); + return -ENXIO; + } - kbc->idev = input_allocate_device(); - if (!kbc->idev) { + kbc = kzalloc(sizeof(*kbc), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!kbc || !input_dev) { err = -ENOMEM; - goto fail_allocateinput; + goto err_free_mem; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "failed to get I/O memory\n"); - err = -ENXIO; - goto fail_memoryresource; - } + kbc->pdata = pdata; + kbc->idev = input_dev; + kbc->irq = irq; + spin_lock_init(&kbc->lock); + setup_timer(&kbc->timer, tegra_kbc_keypress_timer, (unsigned long)kbc); + res = request_mem_region(res->start, resource_size(res), pdev->name); if (!res) { dev_err(&pdev->dev, "failed to request I/O memory\n"); err = -EBUSY; - goto fail_memoryresource; + goto err_free_mem; } + kbc->mmio = ioremap(res->start, resource_size(res)); if (!kbc->mmio) { dev_err(&pdev->dev, "failed to remap I/O memory\n"); err = -ENXIO; - goto fail_memoryresource; - } - irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&pdev->dev, "failed to get keyboard IRQ\n"); - err = -ENXIO; - goto fail_keyboardresource; + goto err_free_mem_region; } + kbc->clk = clk_get(&pdev->dev, NULL); - if (IS_ERR_OR_NULL(kbc->clk)) { + if (IS_ERR(kbc->clk)) { dev_err(&pdev->dev, "failed to get keyboard clock\n"); - err = (kbc->clk) ? PTR_ERR(kbc->clk) : -ENODEV; - kbc->clk = NULL; - goto fail_keyboardresource; + err = PTR_ERR(kbc->clk); + goto err_iounmap; } - platform_set_drvdata(pdev, kbc); - - kbc->idev->name = pdev->name; - input_set_drvdata(kbc->idev, kbc); - kbc->idev->id.bustype = BUS_HOST; - kbc->idev->open = tegra_kbc_open; - kbc->idev->close = tegra_kbc_close; - kbc->idev->dev.parent = &pdev->dev; - spin_lock_init(&kbc->lock); - - for (i = 0; i < KBC_MAX_GPIO; i++) { - if (pdata->pin_cfg[i].is_row && pdata->pin_cfg[i].is_col) { - dev_err(&pdev->dev, "invalid pin configuration data\n"); - err = -EINVAL; - goto fail_configurekeyboard; - } - - if (pdata->pin_cfg[i].is_row) { - if (pdata->pin_cfg[i].num >= KBC_MAX_ROW) { - dev_err(&pdev->dev, "invalid row number\n"); - err = -EINVAL; - goto fail_configurekeyboard; - } - rows[pdata->pin_cfg[i].num] = 1; - num_rows++; - } else if (pdata->pin_cfg[i].is_col) { - if (pdata->pin_cfg[i].num >= KBC_MAX_COL) { - dev_err(&pdev->dev, "invalid column number\n"); - err = -EINVAL; - goto fail_configurekeyboard; - } - cols[pdata->pin_cfg[i].num] = 1; - } - } kbc->wake_enable_rows = 0; kbc->wake_enable_cols = 0; - for (i = 0; i < pdata->wake_cnt; i++) { - kbc->wake_enable_rows |= (1 << kbc->pdata->wake_cfg[i].row); - kbc->wake_enable_cols |= (1 << kbc->pdata->wake_cfg[i].col); + kbc->wake_enable_rows |= (1 << pdata->wake_cfg[i].row); + kbc->wake_enable_cols |= (1 << pdata->wake_cfg[i].col); } - debounce_cnt = pdata->debounce_cnt; - debounce_cnt = min_t(unsigned int, debounce_cnt, KBC_MAX_DEBOUNCE_CNT); - - /* The time delay between two consecutive reads of the FIFO is the sum - * of the repeat time and the time taken for scanning the rows. - * There is an additional delay before the row scanning starts. - * The repoll delay is computed in milliseconds. */ + /* + * The time delay between two consecutive reads of the FIFO is + * the sum of the repeat time and the time taken for scanning + * the rows. There is an additional delay before the row scanning + * starts. The repoll delay is computed in milliseconds. + */ + debounce_cnt = min(pdata->debounce_cnt, KBC_MAX_DEBOUNCE_CNT); scan_time_rows = (KBC_ROW_SCAN_TIME + debounce_cnt) * num_rows; kbc->repoll_dly = KBC_ROW_SCAN_DLY + scan_time_rows + pdata->repeat_cnt; kbc->repoll_dly = ((kbc->repoll_dly * KBC_CYCLE_USEC) + 999) / 1000; - kbc->idev->evbit[0] = BIT_MASK(EV_KEY); + input_dev->name = pdev->name; + input_dev->id.bustype = BUS_HOST; + input_dev->dev.parent = &pdev->dev; + input_dev->open = tegra_kbc_open; + input_dev->close = tegra_kbc_close; - /* Override the default keycodes with the board supplied ones. */ - if (pdata->keycode) - memcpy(kbc->keycode, pdata->keycode, sizeof(kbc->keycode)); - else - memcpy(kbc->keycode, tegra_kbd_keycode, sizeof(kbc->keycode)); + input_set_drvdata(input_dev, kbc); - kbc->idev->keycode = kbc->keycode; - kbc->idev->keycodesize = sizeof(kbc->keycode[0]); - kbc->idev->keycodemax = ARRAY_SIZE(kbc->keycode); + input_dev->evbit[0] = BIT_MASK(EV_KEY); + input_set_capability(input_dev, EV_MSC, MSC_SCAN); - for (i = 0; i < KBC_MAX_COL; i++) { - if (!cols[i]) - continue; - for (j = 0; j < KBC_MAX_ROW; j++) { - int keycode; + input_dev->keycode = kbc->keycode; + input_dev->keycodesize = sizeof(kbc->keycode[0]); + input_dev->keycodemax = ARRAY_SIZE(kbc->keycode); - if (!rows[j]) - continue; + keymap_data = pdata->keymap_data ?: &tegra_kbc_default_keymap_data; + matrix_keypad_build_keymap(keymap_data, KBC_ROW_SHIFT, + input_dev->keycode, input_dev->keybit); - /* enable all the mapped keys. */ - keycode = kbc->keycode[(j * KBC_MAX_COL) + i]; - if (keycode != -1) - set_bit(keycode, kbc->idev->keybit); - - } - } - - setup_timer(&kbc->timer, tegra_kbc_keypress_timer, (unsigned long)kbc); - /* Initialize the FIFO to invalid entries */ - for (i = 0; i < ARRAY_SIZE(kbc->fifo); i++) - kbc->fifo[i] = -1; - - /* keycode FIFO needs to be read atomically; leave local - * interrupts disabled when handling KBC interrupt */ - err = request_irq(irq, tegra_kbc_isr, IRQF_TRIGGER_HIGH, - pdev->name, kbc); + err = request_irq(kbc->irq, tegra_kbc_isr, IRQF_TRIGGER_HIGH, + pdev->name, kbc); if (err) { dev_err(&pdev->dev, "failed to request keyboard IRQ\n"); - goto fail_configurekeyboard; + goto err_put_clk; } - kbc->irq = irq; + + disable_irq(kbc->irq); err = input_register_device(kbc->idev); if (err) { dev_err(&pdev->dev, "failed to register input device\n"); - goto fail_registerinput; + goto err_free_irq; } + platform_set_drvdata(pdev, kbc); device_init_wakeup(&pdev->dev, pdata->wakeup); + return 0; -fail_registerinput: +err_free_irq: free_irq(kbc->irq, pdev); -fail_configurekeyboard: +err_put_clk: clk_put(kbc->clk); -fail_keyboardresource: +err_iounmap: iounmap(kbc->mmio); -fail_memoryresource: +err_free_mem_region: + release_mem_region(res->start, resource_size(res)); +err_free_mem: input_free_device(kbc->idev); -fail_allocateinput: kfree(kbc); + return err; } +static int __devexit tegra_kbc_remove(struct platform_device *pdev) +{ + struct tegra_kbc *kbc = platform_get_drvdata(pdev); + struct resource *res; + + free_irq(kbc->irq, pdev); + clk_put(kbc->clk); + + input_unregister_device(kbc->idev); + iounmap(kbc->mmio); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, resource_size(res)); + + kfree(kbc); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + #ifdef CONFIG_PM static int tegra_kbc_suspend(struct platform_device *pdev, pm_message_t state) { @@ -577,7 +672,7 @@ static int tegra_kbc_suspend(struct platform_device *pdev, pm_message_t state) } else { mutex_lock(&kbc->idev->mutex); if (kbc->idev->users) - tegra_kbc_close(kbc->idev); + tegra_kbc_stop(kbc); mutex_unlock(&kbc->idev->mutex); } @@ -595,7 +690,7 @@ static int tegra_kbc_resume(struct platform_device *pdev) } else { mutex_lock(&kbc->idev->mutex); if (kbc->idev->users) - err = tegra_kbc_open(kbc->idev); + err = tegra_kbc_start(kbc); mutex_unlock(&kbc->idev->mutex); } ^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH v5] input: tegra-kbc - Add tegra keyboard driver 2011-01-18 5:12 ` Dmitry Torokhov @ 2011-01-18 5:13 ` Dmitry Torokhov 2011-01-19 3:28 ` Rakesh Iyer 1 sibling, 0 replies; 5+ messages in thread From: Dmitry Torokhov @ 2011-01-18 5:13 UTC (permalink / raw) To: riyer Cc: tsoni, pavel, shubhrajyoti, ccross, konkers, olof, achew, linux-tegra, linux-kernel, linux-input On Mon, Jan 17, 2011 at 09:12:16PM -0800, Dmitry Torokhov wrote: > On Thu, Jan 13, 2011 at 10:27:28AM -0800, riyer@nvidia.com wrote: > > From: Rakesh Iyer <riyer@nvidia.com> > > > > This patch adds support for the internal matrix keyboard controller for > > Nvidia Tegra platforms. > > > > Signed-off-by: Rakesh Iyer <riyer@nvidia.com> > > --- > > Changes Done - > > Wrap the users field check within the mutex. > > Remove the KBC_MAX_KEYS define and use existing KBC_MAX_KEY. > > Patch v4 was named incorrectly as PATCH 1/1, so skipping ahead to PATCH v5. > > > > As I said, we should tty to reused definitions from matrix_keypad for > matrix keypads. Does the following still work for you? > And if it does then could you please try this one as well? Thanks. -- Dmitry Input: tegra-kbc - convert to dev_pm_ops From: Dmitry Torokhov <dmitry.torokhov@gmail.com> Signed-off-by: Dmitry Torokhov <dtor@mail.ru> --- drivers/input/keyboard/tegra-kbc.c | 17 +++++++++-------- 1 files changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/input/keyboard/tegra-kbc.c b/drivers/input/keyboard/tegra-kbc.c index a6ec3fc..c3c0c65 100644 --- a/drivers/input/keyboard/tegra-kbc.c +++ b/drivers/input/keyboard/tegra-kbc.c @@ -658,9 +658,10 @@ static int __devexit tegra_kbc_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM -static int tegra_kbc_suspend(struct platform_device *pdev, pm_message_t state) +#ifdef CONFIG_PM_SLEEP +static int tegra_kbc_suspend(struct device *dev) { + struct platform_device *pdev = to_platform_device(dev); struct tegra_kbc *kbc = platform_get_drvdata(pdev); if (device_may_wakeup(&pdev->dev)) { @@ -679,8 +680,9 @@ static int tegra_kbc_suspend(struct platform_device *pdev, pm_message_t state) return 0; } -static int tegra_kbc_resume(struct platform_device *pdev) +static int tegra_kbc_resume(struct device *dev) { + struct platform_device *pdev = to_platform_device(dev); struct tegra_kbc *kbc = platform_get_drvdata(pdev); int err = 0; @@ -698,17 +700,16 @@ static int tegra_kbc_resume(struct platform_device *pdev) } #endif +static SIMPLE_DEV_PM_OPS(tegra_kbc_pm_ops, tegra_kbc_suspend, tegra_kbc_resume); + static struct platform_driver tegra_kbc_driver = { .probe = tegra_kbc_probe, .remove = __devexit_p(tegra_kbc_remove), -#ifdef CONFIG_PM - .suspend = tegra_kbc_suspend, - .resume = tegra_kbc_resume, -#endif .driver = { .name = "tegra-kbc", .owner = THIS_MODULE, - } + .pm = &tegra_kbc_pm_ops, + }, }; static void __exit tegra_kbc_exit(void) ^ permalink raw reply related [flat|nested] 5+ messages in thread
* RE: [PATCH v5] input: tegra-kbc - Add tegra keyboard driver 2011-01-18 5:12 ` Dmitry Torokhov 2011-01-18 5:13 ` Dmitry Torokhov @ 2011-01-19 3:28 ` Rakesh Iyer 2011-01-19 5:23 ` Dmitry Torokhov 1 sibling, 1 reply; 5+ messages in thread From: Rakesh Iyer @ 2011-01-19 3:28 UTC (permalink / raw) To: 'Dmitry Torokhov' Cc: tsoni@codeaurora.org, pavel@ucw.cz, shubhrajyoti@ti.com, ccross@android.com, konkers@android.com, olof@lixom.net, Andrew Chew, linux-tegra@vger.kernel.org, linux-kernel@vger.kernel.org, linux-input@vger.kernel.org Sorry for the late response as I was not working on it on Monday. The patch as is did not work. I am debugging this currently. Once I find a fix, can I resend all the driver code as one patch file? I will include your 2nd patch in it as well. Secondly, the driver is a platform driver for Tegra platforms. The platform code needs some of the #defines to be present in kbc.h, so you will see some of the defines moved back to kbc.h from tegra-kbc.c Thanks and Regards Rakesh > -----Original Message----- > From: Dmitry Torokhov [mailto:dmitry.torokhov@gmail.com] > Sent: Monday, January 17, 2011 9:12 PM > To: Rakesh Iyer > Cc: tsoni@codeaurora.org; pavel@ucw.cz; shubhrajyoti@ti.com; ccross@android.com; > konkers@android.com; olof@lixom.net; Andrew Chew; linux-tegra@vger.kernel.org; linux- > kernel@vger.kernel.org; linux-input@vger.kernel.org > Subject: Re: [PATCH v5] input: tegra-kbc - Add tegra keyboard driver > > On Thu, Jan 13, 2011 at 10:27:28AM -0800, riyer@nvidia.com wrote: > > From: Rakesh Iyer <riyer@nvidia.com> > > > > This patch adds support for the internal matrix keyboard controller for > > Nvidia Tegra platforms. > > > > Signed-off-by: Rakesh Iyer <riyer@nvidia.com> > > --- > > Changes Done - > > Wrap the users field check within the mutex. > > Remove the KBC_MAX_KEYS define and use existing KBC_MAX_KEY. > > Patch v4 was named incorrectly as PATCH 1/1, so skipping ahead to PATCH v5. > > > > As I said, we should tty to reused definitions from matrix_keypad for > matrix keypads. Does the following still work for you? > > Thanks. > > -- > Dmitry > > Input: tegra-kbc - convert to use matrix-keypad definitions > > From: Dmitry Torokhov <dmitry.torokhov@gmail.com> > > and other miscellaneous rearrangements. > > Signed-off-by: Dmitry Torokhov <dtor@mail.ru> > --- > > arch/arm/mach-tegra/include/mach/kbc.h | 28 + > drivers/input/keyboard/tegra-kbc.c | 679 ++++++++++++++++++-------------- > 2 files changed, 399 insertions(+), 308 deletions(-) > > > diff --git a/arch/arm/mach-tegra/include/mach/kbc.h b/arch/arm/mach- > tegra/include/mach/kbc.h > index 029a468..adb267f 100644 > --- a/arch/arm/mach-tegra/include/mach/kbc.h > +++ b/arch/arm/mach-tegra/include/mach/kbc.h > @@ -1,6 +1,4 @@ > /* > - * kbc.h > - * > * Platform definitions for tegra-kbc keyboard input driver > * > * Copyright (c) 2010, NVIDIA Corporation. > @@ -24,23 +22,18 @@ > #define ASMARM_ARCH_TEGRA_KBC_H > > #include <linux/types.h> > +#include <linux/input/matrix_keypad.h> > > #ifdef CONFIG_ARCH_TEGRA_2x_SOC > -#define KBC_MAX_GPIO 24 > -#define KBC_MAX_KPENT 8 > +#define KBC_MAX_GPIO 24 > +#define KBC_MAX_KPENT 8 > #else > -#define KBC_MAX_GPIO 20 > -#define KBC_MAX_KPENT 7 > +#define KBC_MAX_GPIO 20 > +#define KBC_MAX_KPENT 7 > #endif > > -#define KBC_MAX_ROW 16 > -#define KBC_MAX_COL 8 > - > -#define KBC_MAX_KEY (KBC_MAX_ROW*KBC_MAX_COL) > - > struct tegra_kbc_pin_cfg { > bool is_row; > - bool is_col; > unsigned char num; > }; > > @@ -52,10 +45,13 @@ struct tegra_kbc_wake_key { > struct tegra_kbc_platform_data { > unsigned int debounce_cnt; > unsigned int repeat_cnt; > - int wake_cnt; /* 0:wake on any key >1:wake on wake_cfg */ > - int *keycode; > - bool wakeup; > + > + unsigned int wake_cnt; /* 0:wake on any key >1:wake on wake_cfg */ > + const struct tegra_kbc_wake_key *wake_cfg; > + > struct tegra_kbc_pin_cfg pin_cfg[KBC_MAX_GPIO]; > - struct tegra_kbc_wake_key *wake_cfg; > + const struct matrix_keymap_data *keymap_data; > + > + bool wakeup; > }; > #endif > diff --git a/drivers/input/keyboard/tegra-kbc.c b/drivers/input/keyboard/tegra-kbc.c > index 02c8ece..a6ec3fc 100644 > --- a/drivers/input/keyboard/tegra-kbc.c > +++ b/drivers/input/keyboard/tegra-kbc.c > @@ -1,6 +1,4 @@ > /* > - * tegra-kbc.c > - * > * Keyboard class input driver for the NVIDIA Tegra SoC internal matrix > * keyboard controller > * > @@ -32,7 +30,7 @@ > #include <mach/clk.h> > #include <mach/kbc.h> > > -#define KBC_MAX_DEBOUNCE_CNT 0x3fful > +#define KBC_MAX_DEBOUNCE_CNT 0x3ffu > > /* KBC row scan time and delay for beginning the row scan. */ > #define KBC_ROW_SCAN_TIME 16 > @@ -62,119 +60,201 @@ > #define KBC_KP_ENT1_0 0x34 > #define KBC_ROW0_MASK_0 0x38 > > +#define KBC_MAX_ROW 16 > +#define KBC_MAX_COL 8 > +#define KBC_ROW_SHIFT 3 > +#define KBC_MAX_KEY (KBC_MAX_ROW * KBC_MAX_COL) > + > struct tegra_kbc { > void __iomem *mmio; > struct input_dev *idev; > - int irq; > + unsigned int irq; > unsigned int wake_enable_rows; > unsigned int wake_enable_cols; > spinlock_t lock; > unsigned int repoll_dly; > unsigned long cp_dly_jiffies; > - int fifo[KBC_MAX_KPENT]; > const struct tegra_kbc_platform_data *pdata; > - int keycode[KBC_MAX_KEY]; > + unsigned short keycode[KBC_MAX_KEY]; > + unsigned short current_keys[KBC_MAX_KPENT]; > + unsigned int num_pressed_keys; > struct timer_list timer; > struct clk *clk; > }; > > -static int tegra_kbd_keycode[KBC_MAX_KEY] = { > - KEY_RESERVED, KEY_RESERVED, KEY_W, KEY_S, > - KEY_A, KEY_Z, KEY_RESERVED, KEY_FN, > - KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, > - KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_MENU, > - KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, > - KEY_RESERVED, KEY_RESERVED, KEY_RIGHTALT, KEY_LEFTALT, > - KEY_5, KEY_4, KEY_R, KEY_E, > - KEY_F, KEY_D, KEY_X, KEY_RESERVED, > - KEY_7, KEY_6, KEY_T, KEY_H, > - KEY_G, KEY_V, KEY_C, KEY_SPACE, > - KEY_9, KEY_8, KEY_U, KEY_Y, > - KEY_J, KEY_N, KEY_B, KEY_BACKSLASH, > - KEY_MINUS, KEY_0, KEY_O, KEY_I, > - KEY_L, KEY_K, KEY_COMMA, KEY_M, > - KEY_RESERVED, KEY_EQUAL, KEY_RIGHTBRACE, KEY_ENTER, > - KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_MENU, > - KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, > - KEY_RIGHTSHIFT, KEY_LEFTSHIFT, KEY_RESERVED, > KEY_RESERVED, > - KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, > - KEY_RESERVED, KEY_RIGHTCTRL, KEY_RESERVED, KEY_LEFTCTRL, > - KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, > - KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, > - KEY_LEFTBRACE, KEY_P, KEY_APOSTROPHE, KEY_SEMICOLON, > - KEY_SLASH, KEY_DOT, KEY_RESERVED, KEY_RESERVED, > - KEY_F10, KEY_F9, KEY_BACKSPACE, KEY_3, > - KEY_2, KEY_UP, KEY_PRINT, KEY_PAUSE, > - KEY_INSERT, KEY_DELETE, KEY_RESERVED, KEY_PAGEUP, > - KEY_PAGEDOWN, KEY_RIGHT, KEY_DOWN, KEY_LEFT, > - KEY_F11, KEY_F12, KEY_F8, KEY_Q, > - KEY_F4, KEY_F3, KEY_1, KEY_F7, > - KEY_ESC, KEY_GRAVE, KEY_F5, KEY_TAB, > - KEY_F1, KEY_F2, KEY_CAPSLOCK, KEY_F6 > +static const u32 tegra_kbc_default_keymap[] = { > + KEY(0, 2, KEY_W), > + KEY(0, 3, KEY_S), > + KEY(0, 4, KEY_A), > + KEY(0, 5, KEY_Z), > + KEY(0, 7, KEY_FN), > + > + KEY(1, 7, KEY_MENU), > + > + KEY(2, 6, KEY_RIGHTALT), > + KEY(2, 7, KEY_LEFTALT), > + > + KEY(3, 0, KEY_5), > + KEY(3, 1, KEY_4), > + KEY(3, 2, KEY_R), > + KEY(3, 3, KEY_E), > + KEY(3, 4, KEY_F), > + KEY(3, 5, KEY_D), > + KEY(3, 6, KEY_X), > + > + KEY(4, 0, KEY_7), > + KEY(4, 1, KEY_6), > + KEY(4, 2, KEY_T), > + KEY(4, 3, KEY_H), > + KEY(4, 4, KEY_G), > + KEY(4, 5, KEY_V), > + KEY(4, 6, KEY_C), > + KEY(4, 7, KEY_SPACE), > + > + KEY(5, 0, KEY_9), > + KEY(5, 1, KEY_8), > + KEY(5, 2, KEY_U), > + KEY(5, 3, KEY_Y), > + KEY(5, 4, KEY_J), > + KEY(5, 5, KEY_N), > + KEY(5, 6, KEY_B), > + KEY(5, 7, KEY_BACKSLASH), > + > + KEY(6, 0, KEY_MINUS), > + KEY(6, 1, KEY_0), > + KEY(6, 2, KEY_O), > + KEY(6, 3, KEY_I), > + KEY(6, 4, KEY_L), > + KEY(6, 5, KEY_K), > + KEY(6, 6, KEY_COMMA), > + KEY(6, 7, KEY_M), > + > + KEY(7, 1, KEY_EQUAL), > + KEY(7, 2, KEY_RIGHTBRACE), > + KEY(7, 3, KEY_ENTER), > + KEY(7, 7, KEY_MENU), > + > + KEY(8, 4, KEY_RIGHTSHIFT), > + KEY(8, 5, KEY_LEFTSHIFT), > + > + KEY(9, 5, KEY_RIGHTCTRL), > + KEY(9, 7, KEY_LEFTCTRL), > + > + KEY(11, 0, KEY_LEFTBRACE), > + KEY(11, 1, KEY_P), > + KEY(11, 2, KEY_APOSTROPHE), > + KEY(11, 3, KEY_SEMICOLON), > + KEY(11, 4, KEY_SLASH), > + KEY(11, 5, KEY_DOT), > + > + KEY(12, 0, KEY_F10), > + KEY(12, 1, KEY_F9), > + KEY(12, 2, KEY_BACKSPACE), > + KEY(12, 3, KEY_3), > + KEY(12, 4, KEY_2), > + KEY(12, 5, KEY_UP), > + KEY(12, 6, KEY_PRINT), > + KEY(12, 7, KEY_PAUSE), > + > + KEY(13, 0, KEY_INSERT), > + KEY(13, 1, KEY_DELETE), > + KEY(13, 3, KEY_PAGEUP), > + KEY(13, 4, KEY_PAGEDOWN), > + KEY(13, 5, KEY_RIGHT), > + KEY(13, 6, KEY_DOWN), > + KEY(13, 7, KEY_LEFT), > + > + KEY(14, 0, KEY_F11), > + KEY(14, 1, KEY_F12), > + KEY(14, 2, KEY_F8), > + KEY(14, 3, KEY_Q), > + KEY(14, 4, KEY_F4), > + KEY(14, 5, KEY_F3), > + KEY(14, 6, KEY_1), > + KEY(14, 7, KEY_F7), > + > + KEY(15, 0, KEY_ESC), > + KEY(15, 1, KEY_GRAVE), > + KEY(15, 2, KEY_F5), > + KEY(15, 3, KEY_TAB), > + KEY(15, 4, KEY_F1), > + KEY(15, 5, KEY_F2), > + KEY(15, 6, KEY_CAPSLOCK), > + KEY(15, 7, KEY_F6), > }; > > -static void tegra_kbc_report_keys(struct tegra_kbc *kbc, int *fifo) > -{ > - int curr_fifo[KBC_MAX_KPENT]; > - int rows_val[KBC_MAX_KPENT], cols_val[KBC_MAX_KPENT]; > - u32 kp_ent_val[(KBC_MAX_KPENT + 3) / 4]; > - u32 *kp_ents = kp_ent_val; > - u32 kp_ent = 0; > - unsigned long flags; > - int i, j, valid = 0; > +static const struct matrix_keymap_data tegra_kbc_default_keymap_data = { > + .keymap = tegra_kbc_default_keymap, > + .keymap_size = ARRAY_SIZE(tegra_kbc_default_keymap), > +}; > > - spin_lock_irqsave(&kbc->lock, flags); > - for (i = 0; i < ARRAY_SIZE(kp_ent_val); i++) > - kp_ent_val[i] = readl(kbc->mmio + KBC_KP_ENT0_0 + (i*4)); > - spin_unlock_irqrestore(&kbc->lock, flags); > +static void tegra_kbc_report_released_keys(struct input_dev *input, > + unsigned short old_keycodes[], > + unsigned int old_num_keys, > + unsigned short new_keycodes[], > + unsigned int new_num_keys) > +{ > + unsigned int i, j; > > - valid = 0; > - for (i = 0; i < KBC_MAX_KPENT; i++) { > - if (!(i&3)) > - kp_ent = *kp_ents++; > + for (i = 0; i < old_num_keys; i++) { > + for (j = 0; j < new_num_keys; j++) > + if (old_keycodes[i] == new_keycodes[j]) > + break; > > - if (kp_ent & 0x80) { > - cols_val[valid] = kp_ent & 0x7; > - rows_val[valid++] = (kp_ent >> 3) & 0xf; > - } > - kp_ent >>= 8; > + if (j == new_num_keys) > + input_report_key(input, old_keycodes[i], 0); > } > +} > > - j = 0; > - for (i = 0; i < valid; i++) { > - int k = kbc->keycode[(rows_val[i] * KBC_MAX_COL) + cols_val[i]]; > - if (likely(k != -1)) > - curr_fifo[j++] = k; > +static void tegra_kbc_report_pressed_keys(struct input_dev *input, > + unsigned char scancodes[], > + unsigned short keycodes[], > + unsigned int num_pressed_keys) > +{ > + unsigned int i; > + > + for (i = 0; i < num_pressed_keys; i++) { > + input_event(input, EV_MSC, MSC_SCAN, scancodes[i]); > + input_report_key(input, keycodes[i], 1); > } > - valid = j; > +} > + > +static void tegra_kbc_report_keys(struct tegra_kbc *kbc) > +{ > + unsigned char scancodes[KBC_MAX_KPENT]; > + unsigned short keycodes[KBC_MAX_KPENT]; > + u32 val = 0; > + unsigned int i; > + unsigned int num_down = 0; > + unsigned long flags; > > + spin_lock_irqsave(&kbc->lock, flags); > for (i = 0; i < KBC_MAX_KPENT; i++) { > - if (fifo[i] == -1) > - continue; > - for (j = 0; j < valid; j++) { > - if (curr_fifo[j] == fifo[i]) { > - curr_fifo[j] = -1; > - break; > - } > - } > - if (j == valid) { > - input_report_key(kbc->idev, fifo[i], 0); > - fifo[i] = -1; > - } > - } > - for (j = 0; j < valid; j++) { > - if (curr_fifo[j] == -1) > - continue; > - for (i = 0; i < KBC_MAX_KPENT; i++) { > - if (fifo[i] == -1) > - break; > + if ((i % 4) == 0) > + val = readl(kbc->mmio + KBC_KP_ENT0_0 + (i * 4)); > + > + if (val & 0x80) { > + unsigned int col = val & 0x07; > + unsigned int row = (val >> 3) & 0x0f; > + unsigned char scancode = > + MATRIX_SCAN_CODE(row, col, KBC_ROW_SHIFT); > + > + scancodes[num_down] = scancode; > + keycodes[num_down++] = kbc->keycode[scancode]; > } > - if (i != KBC_MAX_KPENT) { > - fifo[i] = curr_fifo[j]; > - input_report_key(kbc->idev, fifo[i], 1); > - } else > - WARN_ON(1); > + > + val >>= 8; > } > + spin_unlock_irqrestore(&kbc->lock, flags); > + > + tegra_kbc_report_released_keys(kbc->idev, > + kbc->current_keys, kbc->num_pressed_keys, > + keycodes, num_down); > + tegra_kbc_report_pressed_keys(kbc->idev, scancodes, keycodes, num_down); > + input_sync(kbc->idev); > + > + memcpy(kbc->current_keys, keycodes, sizeof(kbc->current_keys)); > + kbc->num_pressed_keys = num_down; > } > > static void tegra_kbc_keypress_timer(unsigned long data) > @@ -182,26 +262,27 @@ static void tegra_kbc_keypress_timer(unsigned long data) > struct tegra_kbc *kbc = (struct tegra_kbc *)data; > unsigned long flags; > u32 val; > - int i; > + unsigned int i; > > val = (readl(kbc->mmio + KBC_INT_0) >> 4) & 0xf; > if (val) { > unsigned long dly; > > - tegra_kbc_report_keys(kbc, kbc->fifo); > + tegra_kbc_report_keys(kbc); > > - /* If more than one keys are pressed we need not wait > - * for the repoll delay. */ > + /* > + * If more than one keys are pressed we need not wait > + * for the repoll delay. > + */ > dly = (val == 1) ? kbc->repoll_dly : 1; > mod_timer(&kbc->timer, jiffies + msecs_to_jiffies(dly)); > } else { > - /* release any pressed keys and exit the loop */ > - for (i = 0; i < ARRAY_SIZE(kbc->fifo); i++) { > - if (kbc->fifo[i] == -1) > - continue; > - input_report_key(kbc->idev, kbc->fifo[i], 0); > - kbc->fifo[i] = -1; > - } > + /* Release any pressed keys and exit the polling loop */ > + for (i = 0; i < kbc->num_pressed_keys; i++) > + input_report_key(kbc->idev, kbc->current_keys[i], 0); > + input_sync(kbc->idev); > + > + kbc->num_pressed_keys = 0; > > /* All keys are released so enable the keypress interrupt */ > spin_lock_irqsave(&kbc->lock, flags); > @@ -212,38 +293,58 @@ static void tegra_kbc_keypress_timer(unsigned long data) > } > } > > -static void tegra_kbc_close(struct input_dev *dev) > +static irqreturn_t tegra_kbc_isr(int irq, void *args) > { > - struct tegra_kbc *kbc = input_get_drvdata(dev); > - unsigned long flags; > - u32 val; > + struct tegra_kbc *kbc = args; > + u32 val, ctl; > > - spin_lock_irqsave(&kbc->lock, flags); > - val = readl(kbc->mmio + KBC_CONTROL_0); > - val &= ~1; > - writel(val, kbc->mmio + KBC_CONTROL_0); > - spin_unlock_irqrestore(&kbc->lock, flags); > + /* > + * Until all keys are released, defer further processing to > + * the polling loop in tegra_kbc_keypress_timer > + */ > + ctl = readl(kbc->mmio + KBC_CONTROL_0); > + ctl &= ~KBC_CONTROL_FIFO_CNT_INT_EN; > + writel(ctl, kbc->mmio + KBC_CONTROL_0); > > - clk_disable(kbc->clk); > + /* > + * Quickly bail out & reenable interrupts if the fifo threshold > + * count interrupt wasn't the interrupt source > + */ > + val = readl(kbc->mmio + KBC_INT_0); > + writel(val, kbc->mmio + KBC_INT_0); > + > + if (val & KBC_INT_FIFO_CNT_INT_STATUS) { > + /* > + * Schedule timer to run when hardware is in continuous > + * polling mode. > + */ > + mod_timer(&kbc->timer, jiffies + kbc->cp_dly_jiffies); > + } else { > + ctl |= KBC_CONTROL_FIFO_CNT_INT_EN; > + writel(ctl, kbc->mmio + KBC_CONTROL_0); > + } > + > + return IRQ_HANDLED; > } > > static void tegra_kbc_setup_wakekeys(struct tegra_kbc *kbc, bool filter) > { > + const struct tegra_kbc_platform_data *pdata = kbc->pdata; > int i; > unsigned int rst_val; > > - BUG_ON(kbc->pdata->wake_cnt > KBC_MAX_KEY); > - rst_val = (filter && kbc->pdata->wake_cnt) ? ~0 : 0; > + BUG_ON(pdata->wake_cnt > KBC_MAX_KEY); > + rst_val = (filter && pdata->wake_cnt) ? ~0 : 0; > > for (i = 0; i < KBC_MAX_ROW; i++) > - writel(rst_val, kbc->mmio+KBC_ROW0_MASK_0+i*4); > + writel(rst_val, kbc->mmio + KBC_ROW0_MASK_0 + i * 4); > > if (filter) { > - for (i = 0; i < kbc->pdata->wake_cnt; i++) { > + for (i = 0; i < pdata->wake_cnt; i++) { > u32 val, addr; > - addr = kbc->pdata->wake_cfg[i].row*4 + KBC_ROW0_MASK_0; > + addr = pdata->wake_cfg[i].row * 4 + KBC_ROW0_MASK_0; > val = readl(kbc->mmio + addr); > - val &= ~(1<<kbc->pdata->wake_cfg[i].col); > + val &= ~(1 << pdata->wake_cfg[i].col); > writel(val, kbc->mmio + addr); > } > } > @@ -255,33 +356,31 @@ static void tegra_kbc_config_pins(struct tegra_kbc *kbc) > int i; > > for (i = 0; i < KBC_MAX_GPIO; i++) { > - u32 row_cfg, col_cfg; > - u32 r_shift = 5 * (i%6); > - u32 c_shift = 4 * (i%8); > + u32 r_shift = 5 * (i % 6); > + u32 c_shift = 4 * (i % 8); > u32 r_mask = 0x1f << r_shift; > - u32 c_mask = 0xf << c_shift; > + u32 c_mask = 0x0f << c_shift; > u32 r_offs = (i / 6) * 4 + KBC_ROW_CFG0_0; > u32 c_offs = (i / 8) * 4 + KBC_COL_CFG0_0; > - > - row_cfg = readl(kbc->mmio + r_offs); > - col_cfg = readl(kbc->mmio + c_offs); > + u32 row_cfg = readl(kbc->mmio + r_offs); > + u32 col_cfg = readl(kbc->mmio + c_offs); > > row_cfg &= ~r_mask; > col_cfg &= ~c_mask; > > if (pdata->pin_cfg[i].is_row) > - row_cfg |= ((pdata->pin_cfg[i].num<<1) | 1) << r_shift; > - else if (pdata->pin_cfg[i].is_col) > - col_cfg |= ((pdata->pin_cfg[i].num<<1) | 1) << c_shift; > + row_cfg |= ((pdata->pin_cfg[i].num << 1) | 1) << r_shift; > + else > + col_cfg |= ((pdata->pin_cfg[i].num << 1) | 1) << c_shift; > > writel(row_cfg, kbc->mmio + r_offs); > writel(col_cfg, kbc->mmio + c_offs); > } > } > > -static int tegra_kbc_open(struct input_dev *dev) > +static int tegra_kbc_start(struct tegra_kbc *kbc) > { > - struct tegra_kbc *kbc = input_get_drvdata(dev); > + const struct tegra_kbc_platform_data *pdata = kbc->pdata; > unsigned long flags; > unsigned int debounce_cnt; > u32 val = 0; > @@ -297,98 +396,120 @@ static int tegra_kbc_open(struct input_dev *dev) > tegra_kbc_config_pins(kbc); > tegra_kbc_setup_wakekeys(kbc, false); > > - writel(kbc->pdata->repeat_cnt, kbc->mmio + KBC_RPT_DLY_0); > + writel(pdata->repeat_cnt, kbc->mmio + KBC_RPT_DLY_0); > > /* Keyboard debounce count is maximum of 12 bits. */ > - debounce_cnt = kbc->pdata->debounce_cnt; > - debounce_cnt = min_t(unsigned int, debounce_cnt, KBC_MAX_DEBOUNCE_CNT); > + debounce_cnt = min(pdata->debounce_cnt, KBC_MAX_DEBOUNCE_CNT); > val = KBC_DEBOUNCE_CNT_SHIFT(debounce_cnt); > val |= KBC_FIFO_TH_CNT_SHIFT(1); /* set fifo interrupt threshold to 1 */ > val |= KBC_CONTROL_FIFO_CNT_INT_EN; /* interrupt on FIFO threshold */ > val |= KBC_CONTROL_KBC_EN; /* enable */ > writel(val, kbc->mmio + KBC_CONTROL_0); > > - /* Compute the delay(ns) from interrupt mode to continuous polling mode > - * so the timer routine is scheduled appropriately. */ > + /* > + * Compute the delay(ns) from interrupt mode to continuous polling > + * mode so the timer routine is scheduled appropriately. > + */ > val = readl(kbc->mmio + KBC_INIT_DLY_0); > kbc->cp_dly_jiffies = usecs_to_jiffies((val & 0xfffff) * 32); > > - /* atomically clear out any remaining entries in the key FIFO > - * and enable keyboard interrupts */ > + kbc->num_pressed_keys = 0; > + > + /* > + * Atomically clear out any remaining entries in the key FIFO > + * and enable keyboard interrupts. > + */ > spin_lock_irqsave(&kbc->lock, flags); > while (1) { > val = readl(kbc->mmio + KBC_INT_0); > val >>= 4; > - if (val) { > - val = readl(kbc->mmio + KBC_KP_ENT0_0); > - val = readl(kbc->mmio + KBC_KP_ENT1_0); > - } else { > + if (!val) > break; > - } > + > + val = readl(kbc->mmio + KBC_KP_ENT0_0); > + val = readl(kbc->mmio + KBC_KP_ENT1_0); > } > writel(0x7, kbc->mmio + KBC_INT_0); > spin_unlock_irqrestore(&kbc->lock, flags); > > + enable_irq(kbc->irq); > + > return 0; > } > > - > -static int __devexit tegra_kbc_remove(struct platform_device *pdev) > +static void tegra_kbc_stop( struct tegra_kbc *kbc) > { > - struct tegra_kbc *kbc = platform_get_drvdata(pdev); > - struct resource *res; > + unsigned long flags; > + u32 val; > > - free_irq(kbc->irq, pdev); > + spin_lock_irqsave(&kbc->lock, flags); > + val = readl(kbc->mmio + KBC_CONTROL_0); > + val &= ~1; > + writel(val, kbc->mmio + KBC_CONTROL_0); > + spin_unlock_irqrestore(&kbc->lock, flags); > + > + disable_irq(kbc->irq); > del_timer_sync(&kbc->timer); > + > clk_disable(kbc->clk); > - clk_put(kbc->clk); > +} > > - input_unregister_device(kbc->idev); > - iounmap(kbc->mmio); > - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > - release_mem_region(res->start, resource_size(res)); > +static int tegra_kbc_open(struct input_dev *dev) > +{ > + struct tegra_kbc *kbc = input_get_drvdata(dev); > > - kfree(kbc); > - return 0; > + return tegra_kbc_start(kbc); > } > > -static irqreturn_t tegra_kbc_isr(int irq, void *args) > +static void tegra_kbc_close(struct input_dev *dev) > { > - struct tegra_kbc *kbc = args; > - u32 val, ctl; > + struct tegra_kbc *kbc = input_get_drvdata(dev); > > - /* until all keys are released, defer further processing to > - * the polling loop in tegra_kbc_keypress_timer */ > - ctl = readl(kbc->mmio + KBC_CONTROL_0); > - ctl &= ~KBC_CONTROL_FIFO_CNT_INT_EN; > - writel(ctl, kbc->mmio + KBC_CONTROL_0); > + return tegra_kbc_stop(kbc); > +} > > - /* quickly bail out & reenable interrupts if the fifo threshold count > - * interrupt wasn't the interrupt source */ > - val = readl(kbc->mmio + KBC_INT_0); > - writel(val, kbc->mmio + KBC_INT_0); > +static bool __devinit > +tegra_kbc_check_pin_cfg(const struct tegra_kbc_platform_data *pdata, > + struct device *dev, unsigned int *num_rows) > +{ > + int i; > > - if (!(val & KBC_INT_FIFO_CNT_INT_STATUS)) { > - ctl |= KBC_CONTROL_FIFO_CNT_INT_EN; > - writel(ctl, kbc->mmio + KBC_CONTROL_0); > - return IRQ_HANDLED; > + *num_rows = 0; > + > + for (i = 0; i < KBC_MAX_GPIO; i++) { > + const struct tegra_kbc_pin_cfg *pin_cfg = &pdata->pin_cfg[i]; > + > + if (pin_cfg->is_row) { > + if (pin_cfg->num >= KBC_MAX_ROW) { > + dev_err(dev, > + "pin_cfg[%d]: invalid row number %d\n", > + i, pin_cfg->num); > + return false; > + } > + (*num_rows)++; > + } else { > + if (pin_cfg->num >= KBC_MAX_COL) { > + dev_err(dev, > + "pin_cfg[%d]: invalid column number %d\n", > + i, pin_cfg->num); > + return false; > + } > + } > } > > - /* Schedule timer to run when hardware is in continuous polling mode. */ > - mod_timer(&kbc->timer, jiffies + kbc->cp_dly_jiffies); > - return IRQ_HANDLED; > + return true; > } > > static int __devinit tegra_kbc_probe(struct platform_device *pdev) > { > - struct tegra_kbc *kbc; > const struct tegra_kbc_platform_data *pdata = pdev->dev.platform_data; > + const struct matrix_keymap_data *keymap_data; > + struct tegra_kbc *kbc; > + struct input_dev *input_dev; > struct resource *res; > int irq; > int err; > - int rows[KBC_MAX_ROW]; > - int cols[KBC_MAX_COL]; > - int i, j; > + int i; > int num_rows = 0; > unsigned int debounce_cnt; > unsigned int scan_time_rows; > @@ -396,173 +517,147 @@ static int __devinit tegra_kbc_probe(struct platform_device > *pdev) > if (!pdata) > return -EINVAL; > > - kbc = kzalloc(sizeof(*kbc), GFP_KERNEL); > - if (!kbc) > - return -ENOMEM; > + if (!tegra_kbc_check_pin_cfg(pdata, &pdev->dev, &num_rows)) > + return -EINVAL; > > - kbc->pdata = pdata; > - kbc->irq = -EINVAL; > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + if (!res) { > + dev_err(&pdev->dev, "failed to get I/O memory\n"); > + return -ENXIO; > + } > > - memset(rows, 0, sizeof(rows)); > - memset(cols, 0, sizeof(cols)); > + irq = platform_get_irq(pdev, 0); > + if (irq < 0) { > + dev_err(&pdev->dev, "failed to get keyboard IRQ\n"); > + return -ENXIO; > + } > > - kbc->idev = input_allocate_device(); > - if (!kbc->idev) { > + kbc = kzalloc(sizeof(*kbc), GFP_KERNEL); > + input_dev = input_allocate_device(); > + if (!kbc || !input_dev) { > err = -ENOMEM; > - goto fail_allocateinput; > + goto err_free_mem; > } > > - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > - if (!res) { > - dev_err(&pdev->dev, "failed to get I/O memory\n"); > - err = -ENXIO; > - goto fail_memoryresource; > - } > + kbc->pdata = pdata; > + kbc->idev = input_dev; > + kbc->irq = irq; > + spin_lock_init(&kbc->lock); > + setup_timer(&kbc->timer, tegra_kbc_keypress_timer, (unsigned long)kbc); > + > res = request_mem_region(res->start, resource_size(res), pdev->name); > if (!res) { > dev_err(&pdev->dev, "failed to request I/O memory\n"); > err = -EBUSY; > - goto fail_memoryresource; > + goto err_free_mem; > } > + > kbc->mmio = ioremap(res->start, resource_size(res)); > if (!kbc->mmio) { > dev_err(&pdev->dev, "failed to remap I/O memory\n"); > err = -ENXIO; > - goto fail_memoryresource; > - } > - irq = platform_get_irq(pdev, 0); > - if (irq < 0) { > - dev_err(&pdev->dev, "failed to get keyboard IRQ\n"); > - err = -ENXIO; > - goto fail_keyboardresource; > + goto err_free_mem_region; > } > + > kbc->clk = clk_get(&pdev->dev, NULL); > - if (IS_ERR_OR_NULL(kbc->clk)) { > + if (IS_ERR(kbc->clk)) { > dev_err(&pdev->dev, "failed to get keyboard clock\n"); > - err = (kbc->clk) ? PTR_ERR(kbc->clk) : -ENODEV; > - kbc->clk = NULL; > - goto fail_keyboardresource; > + err = PTR_ERR(kbc->clk); > + goto err_iounmap; > } > > - platform_set_drvdata(pdev, kbc); > - > - kbc->idev->name = pdev->name; > - input_set_drvdata(kbc->idev, kbc); > - kbc->idev->id.bustype = BUS_HOST; > - kbc->idev->open = tegra_kbc_open; > - kbc->idev->close = tegra_kbc_close; > - kbc->idev->dev.parent = &pdev->dev; > - spin_lock_init(&kbc->lock); > - > - for (i = 0; i < KBC_MAX_GPIO; i++) { > - if (pdata->pin_cfg[i].is_row && pdata->pin_cfg[i].is_col) { > - dev_err(&pdev->dev, "invalid pin configuration data\n"); > - err = -EINVAL; > - goto fail_configurekeyboard; > - } > - > - if (pdata->pin_cfg[i].is_row) { > - if (pdata->pin_cfg[i].num >= KBC_MAX_ROW) { > - dev_err(&pdev->dev, "invalid row number\n"); > - err = -EINVAL; > - goto fail_configurekeyboard; > - } > - rows[pdata->pin_cfg[i].num] = 1; > - num_rows++; > - } else if (pdata->pin_cfg[i].is_col) { > - if (pdata->pin_cfg[i].num >= KBC_MAX_COL) { > - dev_err(&pdev->dev, "invalid column number\n"); > - err = -EINVAL; > - goto fail_configurekeyboard; > - } > - cols[pdata->pin_cfg[i].num] = 1; > - } > - } > kbc->wake_enable_rows = 0; > kbc->wake_enable_cols = 0; > - > for (i = 0; i < pdata->wake_cnt; i++) { > - kbc->wake_enable_rows |= (1 << kbc->pdata->wake_cfg[i].row); > - kbc->wake_enable_cols |= (1 << kbc->pdata->wake_cfg[i].col); > + kbc->wake_enable_rows |= (1 << pdata->wake_cfg[i].row); > + kbc->wake_enable_cols |= (1 << pdata->wake_cfg[i].col); > } > > - debounce_cnt = pdata->debounce_cnt; > - debounce_cnt = min_t(unsigned int, debounce_cnt, KBC_MAX_DEBOUNCE_CNT); > - > - /* The time delay between two consecutive reads of the FIFO is the sum > - * of the repeat time and the time taken for scanning the rows. > - * There is an additional delay before the row scanning starts. > - * The repoll delay is computed in milliseconds. */ > + /* > + * The time delay between two consecutive reads of the FIFO is > + * the sum of the repeat time and the time taken for scanning > + * the rows. There is an additional delay before the row scanning > + * starts. The repoll delay is computed in milliseconds. > + */ > + debounce_cnt = min(pdata->debounce_cnt, KBC_MAX_DEBOUNCE_CNT); > scan_time_rows = (KBC_ROW_SCAN_TIME + debounce_cnt) * num_rows; > kbc->repoll_dly = KBC_ROW_SCAN_DLY + scan_time_rows + pdata->repeat_cnt; > kbc->repoll_dly = ((kbc->repoll_dly * KBC_CYCLE_USEC) + 999) / 1000; > > - kbc->idev->evbit[0] = BIT_MASK(EV_KEY); > + input_dev->name = pdev->name; > + input_dev->id.bustype = BUS_HOST; > + input_dev->dev.parent = &pdev->dev; > + input_dev->open = tegra_kbc_open; > + input_dev->close = tegra_kbc_close; > > - /* Override the default keycodes with the board supplied ones. */ > - if (pdata->keycode) > - memcpy(kbc->keycode, pdata->keycode, sizeof(kbc->keycode)); > - else > - memcpy(kbc->keycode, tegra_kbd_keycode, sizeof(kbc->keycode)); > + input_set_drvdata(input_dev, kbc); > > - kbc->idev->keycode = kbc->keycode; > - kbc->idev->keycodesize = sizeof(kbc->keycode[0]); > - kbc->idev->keycodemax = ARRAY_SIZE(kbc->keycode); > + input_dev->evbit[0] = BIT_MASK(EV_KEY); > + input_set_capability(input_dev, EV_MSC, MSC_SCAN); > > - for (i = 0; i < KBC_MAX_COL; i++) { > - if (!cols[i]) > - continue; > - for (j = 0; j < KBC_MAX_ROW; j++) { > - int keycode; > + input_dev->keycode = kbc->keycode; > + input_dev->keycodesize = sizeof(kbc->keycode[0]); > + input_dev->keycodemax = ARRAY_SIZE(kbc->keycode); > > - if (!rows[j]) > - continue; > + keymap_data = pdata->keymap_data ?: &tegra_kbc_default_keymap_data; > + matrix_keypad_build_keymap(keymap_data, KBC_ROW_SHIFT, > + input_dev->keycode, input_dev->keybit); > > - /* enable all the mapped keys. */ > - keycode = kbc->keycode[(j * KBC_MAX_COL) + i]; > - if (keycode != -1) > - set_bit(keycode, kbc->idev->keybit); > - > - } > - } > - > - setup_timer(&kbc->timer, tegra_kbc_keypress_timer, (unsigned long)kbc); > - /* Initialize the FIFO to invalid entries */ > - for (i = 0; i < ARRAY_SIZE(kbc->fifo); i++) > - kbc->fifo[i] = -1; > - > - /* keycode FIFO needs to be read atomically; leave local > - * interrupts disabled when handling KBC interrupt */ > - err = request_irq(irq, tegra_kbc_isr, IRQF_TRIGGER_HIGH, > - pdev->name, kbc); > + err = request_irq(kbc->irq, tegra_kbc_isr, IRQF_TRIGGER_HIGH, > + pdev->name, kbc); > if (err) { > dev_err(&pdev->dev, "failed to request keyboard IRQ\n"); > - goto fail_configurekeyboard; > + goto err_put_clk; > } > - kbc->irq = irq; > + > + disable_irq(kbc->irq); > > err = input_register_device(kbc->idev); > if (err) { > dev_err(&pdev->dev, "failed to register input device\n"); > - goto fail_registerinput; > + goto err_free_irq; > } > > + platform_set_drvdata(pdev, kbc); > device_init_wakeup(&pdev->dev, pdata->wakeup); > + > return 0; > > -fail_registerinput: > +err_free_irq: > free_irq(kbc->irq, pdev); > -fail_configurekeyboard: > +err_put_clk: > clk_put(kbc->clk); > -fail_keyboardresource: > +err_iounmap: > iounmap(kbc->mmio); > -fail_memoryresource: > +err_free_mem_region: > + release_mem_region(res->start, resource_size(res)); > +err_free_mem: > input_free_device(kbc->idev); > -fail_allocateinput: > kfree(kbc); > + > return err; > } > > +static int __devexit tegra_kbc_remove(struct platform_device *pdev) > +{ > + struct tegra_kbc *kbc = platform_get_drvdata(pdev); > + struct resource *res; > + > + free_irq(kbc->irq, pdev); > + clk_put(kbc->clk); > + > + input_unregister_device(kbc->idev); > + iounmap(kbc->mmio); > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + release_mem_region(res->start, resource_size(res)); > + > + kfree(kbc); > + > + platform_set_drvdata(pdev, NULL); > + > + return 0; > +} > + > #ifdef CONFIG_PM > static int tegra_kbc_suspend(struct platform_device *pdev, pm_message_t state) > { > @@ -577,7 +672,7 @@ static int tegra_kbc_suspend(struct platform_device *pdev, > pm_message_t state) > } else { > mutex_lock(&kbc->idev->mutex); > if (kbc->idev->users) > - tegra_kbc_close(kbc->idev); > + tegra_kbc_stop(kbc); > mutex_unlock(&kbc->idev->mutex); > } > > @@ -595,7 +690,7 @@ static int tegra_kbc_resume(struct platform_device *pdev) > } else { > mutex_lock(&kbc->idev->mutex); > if (kbc->idev->users) > - err = tegra_kbc_open(kbc->idev); > + err = tegra_kbc_start(kbc); > mutex_unlock(&kbc->idev->mutex); > } > ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v5] input: tegra-kbc - Add tegra keyboard driver 2011-01-19 3:28 ` Rakesh Iyer @ 2011-01-19 5:23 ` Dmitry Torokhov 0 siblings, 0 replies; 5+ messages in thread From: Dmitry Torokhov @ 2011-01-19 5:23 UTC (permalink / raw) To: Rakesh Iyer Cc: tsoni@codeaurora.org, pavel@ucw.cz, shubhrajyoti@ti.com, ccross@android.com, konkers@android.com, olof@lixom.net, Andrew Chew, linux-tegra@vger.kernel.org, linux-kernel@vger.kernel.org, linux-input@vger.kernel.org On Tue, Jan 18, 2011 at 07:28:29PM -0800, Rakesh Iyer wrote: > Sorry for the late response as I was not working on it on Monday. > > The patch as is did not work. I am debugging this currently. Sorry about that. If you describe the effects you are seeing I might try to think where I messed up. > > Once I find a fix, can I resend all the driver code as one patch file? > I will include your 2nd patch in it as well. Sure. > > Secondly, the driver is a platform driver for Tegra platforms. The > platform code needs some of the #defines to be present in kbc.h, so > you will see some of the defines moved back to kbc.h from tegra-kbc.c > I only moved KBC_MAX_COL, KBC_MAX_ROW and KBC_MAX_KEY since they are not needed in keymaps expressed via struct matrix_keypad_data. But if they are needed for something else that's fine. -- Dmitry ^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2011-01-19 5:23 UTC | newest] Thread overview: 5+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2011-01-13 18:27 [PATCH v5] input: tegra-kbc - Add tegra keyboard driver riyer 2011-01-18 5:12 ` Dmitry Torokhov 2011-01-18 5:13 ` Dmitry Torokhov 2011-01-19 3:28 ` Rakesh Iyer 2011-01-19 5:23 ` Dmitry Torokhov
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).