From patchwork Thu Aug 8 03:57:11 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: lixin X-Patchwork-Id: 18845 Return-Path: X-Original-To: linaro@patches.linaro.org Delivered-To: linaro@patches.linaro.org Received: from mail-qa0-f71.google.com (mail-qa0-f71.google.com [209.85.216.71]) by ip-10-151-82-157.ec2.internal (Postfix) with ESMTPS id BA574246A4 for ; Thu, 8 Aug 2013 03:57:26 +0000 (UTC) Received: by mail-qa0-f71.google.com with SMTP id bq6sf241399qab.10 for ; Wed, 07 Aug 2013 20:57:26 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=mime-version:x-gm-message-state:delivered-to:from:to:cc:subject :date:message-id:in-reply-to:references:x-original-sender :x-original-authentication-results:precedence:mailing-list:list-id :list-post:list-help:list-archive:list-unsubscribe; bh=wQ5zW0dhfxeUMBdTgZrV60wNrQFu5O3lHPriN+F3azU=; b=cuizi6Z9yMiibxqrnF1O/iQhs7s1taQca7pbQt823WI/+cZplP7mXiRZFouTtFRUwA HFldpDdRyCzLca3ZcDsaIT4HIDYttVkvvZ3rE8TZWNh2hC1iMoWhBz5Hv+6spCzDuKqn 9k/bllWRr0erlCUMcwYI0S3hmKUYUF3ZNyGuW8FSzHaUIYu783gf6hjqhmzny0JaZqkq H+m19QmUHhxisf5uwwMtfaf4ksJO/aalQ9KvgTgiZBoBoYn+jVT5utCOFqHVl4E7Xolb JkXjDcO3J8AabdEOFHIkQ7cpEHhV4dkuIOahkek6/rUMciLYrZ16A/nXfSFr+koM0bPm 3q0g== X-Received: by 10.236.117.11 with SMTP id i11mr2582470yhh.16.1375934246190; Wed, 07 Aug 2013 20:57:26 -0700 (PDT) MIME-Version: 1.0 X-BeenThere: patchwork-forward@linaro.org Received: by 10.49.34.176 with SMTP id a16ls869972qej.63.gmail; Wed, 07 Aug 2013 20:57:26 -0700 (PDT) X-Received: by 10.52.0.82 with SMTP id 18mr1808261vdc.62.1375934246054; Wed, 07 Aug 2013 20:57:26 -0700 (PDT) Received: from mail-vb0-f49.google.com (mail-vb0-f49.google.com [209.85.212.49]) by mx.google.com with ESMTPS id se5si2563987vdc.10.2013.08.07.20.57.26 for (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Wed, 07 Aug 2013 20:57:26 -0700 (PDT) Received-SPF: neutral (google.com: 209.85.212.49 is neither permitted nor denied by best guess record for domain of patch+caf_=patchwork-forward=linaro.org@linaro.org) client-ip=209.85.212.49; Received: by mail-vb0-f49.google.com with SMTP id w16so2700137vbb.8 for ; Wed, 07 Aug 2013 20:57:25 -0700 (PDT) X-Gm-Message-State: ALoCoQlsd9jDeXoEtXgpxyTnlWo9oT4UUlAO0Hb1SgYteskcIriFUrRFfeGhkx+kkGY5VJllLhl0 X-Received: by 10.58.211.227 with SMTP id nf3mr2174605vec.20.1375934245841; Wed, 07 Aug 2013 20:57:25 -0700 (PDT) X-Forwarded-To: patchwork-forward@linaro.org X-Forwarded-For: patch@linaro.org patchwork-forward@linaro.org Delivered-To: patches@linaro.org Received: by 10.221.11.8 with SMTP id pc8csp236020vcb; Wed, 7 Aug 2013 20:57:24 -0700 (PDT) X-Received: by 10.68.130.2 with SMTP id oa2mr3788739pbb.134.1375934244385; Wed, 07 Aug 2013 20:57:24 -0700 (PDT) Received: from mail-pb0-f42.google.com (mail-pb0-f42.google.com [209.85.160.42]) by mx.google.com with ESMTPS id qf5si8111413pac.269.2013.08.07.20.57.23 for (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Wed, 07 Aug 2013 20:57:24 -0700 (PDT) Received-SPF: neutral (google.com: 209.85.160.42 is neither permitted nor denied by best guess record for domain of li.xin@linaro.org) client-ip=209.85.160.42; Received: by mail-pb0-f42.google.com with SMTP id un15so2681330pbc.1 for ; Wed, 07 Aug 2013 20:57:23 -0700 (PDT) X-Received: by 10.68.209.196 with SMTP id mo4mr3859152pbc.114.1375934243819; Wed, 07 Aug 2013 20:57:23 -0700 (PDT) Received: from localhost.localdomain ([58.251.159.202]) by mx.google.com with ESMTPSA id lm2sm13773303pab.2.2013.08.07.20.57.19 for (version=TLSv1.1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Wed, 07 Aug 2013 20:57:22 -0700 (PDT) From: LiXin To: shaojie.sun@linaro.org, haojian.zhuang@linaro.org, guodong.xu@linaro.org, zhang.mingjun@linaro.org, zhangfei.gao@linaro.org, li.xin@linaro.org, victor.lixin@huawei.com Cc: patches@linaro.org Subject: [PATCH] ARM: Hi3620: Hi3620 Keypad driver. Date: Thu, 8 Aug 2013 11:57:11 +0800 Message-Id: <1375934231-6509-1-git-send-email-li.xin@linaro.org> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: References: X-Removed-Original-Auth: Dkim didn't pass. X-Original-Sender: li.xin@linaro.org X-Original-Authentication-Results: mx.google.com; spf=neutral (google.com: 209.85.212.49 is neither permitted nor denied by best guess record for domain of patch+caf_=patchwork-forward=linaro.org@linaro.org) smtp.mail=patch+caf_=patchwork-forward=linaro.org@linaro.org Precedence: list Mailing-list: list patchwork-forward@linaro.org; contact patchwork-forward+owners@linaro.org List-ID: X-Google-Group-Id: 836684582541 List-Post: , List-Help: , List-Archive: List-Unsubscribe: , Hi3620 Keypad driver. Signed-off-by: LiXin --- arch/arm/configs/hs_defconfig | 1 + drivers/input/keyboard/Kconfig | 10 + drivers/input/keyboard/Makefile | 1 + drivers/input/keyboard/k3_keypad.c | 680 ++++++++++++++++++++++++++++++++++++ drivers/input/keyboard/k3_keypad.h | 47 +++ 5 files changed, 739 insertions(+) create mode 100644 drivers/input/keyboard/k3_keypad.c create mode 100644 drivers/input/keyboard/k3_keypad.h diff --git a/arch/arm/configs/hs_defconfig b/arch/arm/configs/hs_defconfig index a146d21..8ac5ea0 100644 --- a/arch/arm/configs/hs_defconfig +++ b/arch/arm/configs/hs_defconfig @@ -857,6 +857,7 @@ CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 CONFIG_INPUT_KEYBOARD=y # CONFIG_KEYBOARD_ADP5588 is not set # CONFIG_KEYBOARD_ADP5589 is not set +# CONFIG_KEYBOARD_K3V200 is not set CONFIG_KEYBOARD_ATKBD=y # CONFIG_KEYBOARD_QT1070 is not set # CONFIG_KEYBOARD_QT2160 is not set diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 7ac9c98..d85370b 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -629,6 +629,16 @@ config KEYBOARD_W90P910 To compile this driver as a module, choose M here: the module will be called w90p910_keypad. +config KEYBOARD_K3V200 + tristate "K3V200 Matrix Keypad support" + depends on ARCH_HS + select INPUT_MATRIXKMAP + help + Say Y here to enable the matrix keypad support for k3v200 platform. + + To compile this driver as a module, choose M here: the + module will be called k3_keypad. + config KEYBOARD_CROS_EC tristate "ChromeOS EC keyboard" select INPUT_MATRIXKMAP diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 0c43e8c..75048fa 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -56,3 +56,4 @@ obj-$(CONFIG_KEYBOARD_TNETV107X) += tnetv107x-keypad.o obj-$(CONFIG_KEYBOARD_TWL4030) += twl4030_keypad.o obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o obj-$(CONFIG_KEYBOARD_W90P910) += w90p910_keypad.o +obj-$(CONFIG_KEYBOARD_K3V200) += k3_keypad.o diff --git a/drivers/input/keyboard/k3_keypad.c b/drivers/input/keyboard/k3_keypad.c new file mode 100644 index 0000000..84fbf95 --- /dev/null +++ b/drivers/input/keyboard/k3_keypad.c @@ -0,0 +1,680 @@ + +/* + * Hisilicon hi3620 keypda driver + * + * Copyright (c) 2012-2013 Hisilicon Limited. + * Copyright (c) 2012-2013 Linaro Limited. + * + * Author: Xin Li + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "k3_keypad.h" +#include + +/*The switch to support long-press key and combo key*/ +#define ADVANCED_FUNCTION_SUPPORT + +#define KPC_MAX_ROWS (8) +#define KPC_MAX_COLS (8) +#define KEY_RELEASE (0) +#define KEY_PRESS (1) +#define KPC_BIT_PER_KEY (6) +#define KPC_BIT_PER_KEYROW (3) + +/*KPC clock frequency*/ +#define KPC_CLK_RATE (32768) + +/*KPC Register Offset*/ +#define KPC_CONTROL_OFFSET (0x000) +#define KPC_INTERVAL_SHORT_OFFSET (0x004) +#define KPC_INT_STATUS_OFFSET (0x01C) +#define KPC_INT_CLR_OFFSET (0x058) +#define KPC_RELEASE_INT_CLR_OFFSET (0x06C) + +/*BITMASK in KPC_INT_STATUS REG */ +#define KEY_NUM_BITMASK (0x3C0000) +#define KEY_NUM_BITPOS (18) +#define KEY_VALUE_BITMASK (0xFFF) +#define KEY_VALUE_BITPOS (0) +#define KPC_RELEASE_INT_BITMASK (0x40000000) +#define KPC_RELEASE_INT_BITPOS (30) + +/*REG config value */ +/* [8:0]: 0_0000_0011 */ +#define KPC_VAL_CTLREG (0x023) +/* 120 x 250us = 30ms */ +#define KPC_VAL_SHORT_INTERVAL (0x28) + +struct k3v2_keypad { + struct input_dev *input_dev; + void __iomem *base; + int irq; + struct clk *clk; + int rows; + int cols; + int row_shift; + /* Used for keymap*/ + unsigned short keycodes[KPC_MAX_ROWS * KPC_MAX_COLS]; + /* Used for result of keypad scan*/ + unsigned char scancode_state[KPC_MAX_ROWS]; + /* Used for store all keycode state*/ + uint16_t keycode_state[KPC_MAX_ROWS * 2]; +}; + + +static struct keypad_remap *keypad_long_remap; +static struct keypad_remap_state *g_long_remap_state; + +static int k3v2_keypad_open(struct input_dev *dev) +{ + struct k3v2_keypad *keypad = input_get_drvdata(dev); + struct pinctrl *block = NULL; + int ret = 0; + + if (keypad == NULL) { + dev_err(&dev->dev, "get invalid keypad pointer\n"); + return -EINVAL; + } + + block = devm_pinctrl_get_select_default(&dev->dev); + if (!block) { + dev_warn(&keypad->input_dev->dev, "Failed to get KPC GPIO BLOCK\n"); + ret = -EINVAL; + } + /*Clean interrupt*/ + writel(0x1, keypad->base + KPC_INT_CLR_OFFSET); + writel(0x1, keypad->base + KPC_RELEASE_INT_CLR_OFFSET); + /*config KPC_CONTROL REG*/ + writel(KPC_VAL_CTLREG, keypad->base + KPC_CONTROL_OFFSET); + /*config KPC_INTERVAL_SHORT REG*/ + writel(KPC_VAL_SHORT_INTERVAL, + keypad->base + KPC_INTERVAL_SHORT_OFFSET); + + enable_irq(keypad->irq); + return ret; +} + +static void k3v2_keypad_close(struct input_dev *dev) +{ + struct k3v2_keypad *keypad = input_get_drvdata(dev); + + if (keypad == NULL) { + dev_err(&dev->dev, "get invalid keypad pointer\n"); + return; + } + + disable_irq(keypad->irq); +} + +/* Update the new_keycode_state*/ +static void +k3v2_keypad_update_keycode(struct k3v2_keypad *keypad, + unsigned char *new_scancode_state, + uint16_t *new_keycode_state) +{ + int row = 0; + int col = 0; + int r = 0; + int c = 0; + int index = 0; + uint8_t keycode = 0; + + for (row = 0; row < KPC_MAX_ROWS; row++) { + for (col = 0; col < KPC_MAX_COLS; col++) { + if (new_scancode_state[row] & (1 << col)) { + index = MATRIX_SCAN_CODE(row, col, + keypad->row_shift); + keycode = keypad->keycodes[index]; + c = keycode & 0x0F; + r = keycode >> 4; + new_keycode_state[r] |= (1 << c); + } + } + } +} + +/*Remap long-press func key or combo keys to target keys.*/ +static void +k3v2_keypad_remap_keycode(struct k3v2_keypad *keypad, + struct keypad_remap_state *remap_state, + uint16_t *new_keycode_state) +{ + int i = 0; + int j = 0; + + unsigned long current_time = jiffies_to_msecs(jiffies); + + if (!remap_state) + return; + + for (i = 0; i < keypad_long_remap->count; i++) { + + int down_num = 0; + uint16_t keycode; + unsigned char key_down_state = 0; + struct keypad_remap_item *item = &keypad_long_remap->items[i]; + struct keypad_remap_state *state = &remap_state[i]; + + for (j = 0; j < item->keynum; j++) { + keycode = item->keycodes[j]; + if (KEYPAD_CHECK_KEYCODE(new_keycode_state, keycode) + != 0) { + key_down_state |= (1 << j); + down_num++; + } + } + /*the number of down keys are enough to remap.*/ + if (down_num >= item->keynum) { + if (item->keynum > 1) { + /*clean all mapping keys in new_keycode_state*/ + for (j = 0; j < item->keynum; j++) { + keycode = item->keycodes[j]; + KEYPAD_CLR_KEYCODE(new_keycode_state, + keycode); + } + /*set the remapped keycode*/ + keycode = item->target_keycode; + KEYPAD_SET_KEYCODE(new_keycode_state, keycode); + state->pending = false; + state->remapped = true; + } else { + /*start pending period*/ + if ((state->pending == false) && + (state->remapped == false)) { + state->pending = true; + state->time = current_time + + item->delay; + } + KEYPAD_CLR_KEYCODE(new_keycode_state, + item->keycodes[0]); + /*if pending, then check if it is timeout*/ + if (state->pending == true) { + /* + * it behinds timeout, + * then set the remapped keycode + */ + if (current_time >= state->time) { + keycode = item->target_keycode; + KEYPAD_SET_KEYCODE( + new_keycode_state, + keycode); + + state->pending = false; + state->remapped = true; + } + } else if (state->remapped == true) { + keycode = item->target_keycode; + KEYPAD_SET_KEYCODE(new_keycode_state, + keycode); + } + } + } + /*keys down, but not enough number for combo keys*/ + else if (down_num > 0) { + if ((state->remapped == true) || + (state->pending == false) || + (current_time < state->time)) { + + if ((state->pending == false) && + (state->remapped == false)) { + state->pending = true; + state->time = current_time + + item->delay; + } + + for (j = 0; j < item->keynum; j++) { + keycode = item->keycodes[j]; + KEYPAD_CLR_KEYCODE(new_keycode_state, + keycode); + } + } + } else { + /*All keys are up. + *If pending, set the cleaned remapping keys back. + *Then call timer to report again.*/ + if (state->pending) { + for (j = 0; j < item->keynum; j++) { + if (((state->down_state) & + (1 << j)) != 0) { + keycode = item->keycodes[j]; + input_report_key( + keypad->input_dev, + keycode, 1); + input_report_key( + keypad->input_dev, + keycode, 0); + input_sync(keypad->input_dev); + } + } + state->pending = false; + } + state->remapped = false; + } + /*save keys*/ + state->down_state = key_down_state; + } +} + +static void k3v2_keypad_report_keycode(struct k3v2_keypad *keypad, + uint16_t *new_keycode_state) +{ + int row = 0; + int col = 0; + unsigned int keycode = 0; + unsigned int pressed = 0; + uint16_t changed = 0; + + for (row = 0; row < KPC_MAX_ROWS * 2; row++) { + changed = keypad->keycode_state[row] ^ new_keycode_state[row]; + if (0 == changed) + continue; + for (col = 0; col < KPC_MAX_COLS * 2; col++) { + if (changed & (1 << col)) { + keycode = (row << 4) | (col & 0x0F); + pressed = (new_keycode_state[row] & + (1 << col)) >> col; + dev_dbg(&keypad->input_dev->dev, + "row = %d, col = %d, keycode = %d, press = %d\n", + row, col, keycode, pressed); + input_report_key(keypad->input_dev, + keycode, pressed); + } + } + } + input_sync(keypad->input_dev); +} + + +static irqreturn_t k3v2_keypad_irq_handler(int irq, void *dev_id) +{ + struct k3v2_keypad *keypad = dev_id; + unsigned int reg = 0; + unsigned int key_num = 0; + unsigned int key_val = 0; + + int row = 0; + int col = 0; + int i = 0; +#ifndef ADVANCED_FUNCTION_SUPPORT + unsigned int changed; + unsigned int pressed; + unsigned int val = 0; +#endif /* #ifndef ADVANCED_FUNCTION_SUPPORT */ + unsigned char new_scancode_state[ARRAY_SIZE(keypad->scancode_state)]; + uint16_t new_keycode_state[ARRAY_SIZE(keypad->keycode_state)]; + + memset(new_scancode_state, 0, sizeof(new_scancode_state)); + memset(new_keycode_state, 0, sizeof(new_keycode_state)); + + reg = readl(keypad->base + KPC_INT_STATUS_OFFSET); + key_num = (reg & KEY_NUM_BITMASK) >> KEY_NUM_BITPOS; + key_val = (reg & KEY_VALUE_BITMASK) >> KEY_VALUE_BITPOS; + + for (i = 0; i < key_num; i++) { + row = (key_val >> (i*KPC_BIT_PER_KEY)) & 0x7; + col = (key_val >> (i*KPC_BIT_PER_KEY + KPC_BIT_PER_KEYROW)) & + 0x7; + new_scancode_state[row] |= (1 << col); + } + +#ifndef ADVANCED_FUNCTION_SUPPORT + for (row = 0; row < keypad->rows; row++) { + changed = new_scancode_state[row] ^ keypad->scancode_state[row]; + if (!changed) + continue; + + for (col = 0; col < keypad->cols; col++) { + if (changed & (1 << col)) { + val = MATRIX_SCAN_CODE(row, col, + keypad->row_shift); + pressed = (new_scancode_state[row] & + (1 << col)) >> col; + input_report_key(keypad->input_dev, + keypad->keycodes[val], + pressed); + dev_dbg(&keypad->input_dev->dev, + "key_num = %d, row = %d, col = %d, press = %d\n", + key_num, row, col, pressed); + } + } + } + input_sync(keypad->input_dev); + memcpy(keypad->scancode_state, new_scancode_state, + sizeof(new_scancode_state)); +#else + k3v2_keypad_update_keycode(keypad, new_scancode_state, + new_keycode_state); + k3v2_keypad_remap_keycode(keypad, g_long_remap_state, + new_keycode_state); + k3v2_keypad_report_keycode(keypad, new_keycode_state); + memcpy(keypad->scancode_state, new_scancode_state, + sizeof(new_scancode_state)); + memcpy(keypad->keycode_state, new_keycode_state, + sizeof(new_keycode_state)); +#endif + + /*Clean interrupt*/ + writel(0x1, keypad->base + KPC_INT_CLR_OFFSET); + writel(0x1, keypad->base + KPC_RELEASE_INT_CLR_OFFSET); + return IRQ_HANDLED; +} + +#ifdef CONFIG_OF +/* Keypad device and platform data start, use KPC realizing keypad. */ +static const uint32_t default_keymap[] = { + /*row, col, key*/ + /* used for truly platform.*/ + KEY(0, 0, KEY_MENU), + KEY(1, 0, KEY_SEND), + KEY(2, 0, KEY_VOLUMEUP), + + KEY(0, 1, KEY_HOME), + KEY(1, 1, KEY_END), + KEY(2, 1, KEY_VOLUMEDOWN), + + KEY(0, 2, KEY_CAMERA_FOCUS), + KEY(1, 2, KEY_CAMERA), + KEY(2, 2, DPAD_CENTER), + + /* TODO: add your keys below*/ + + /*Used for software function, not physical connection!*/ + +}; + +static struct matrix_keymap_data hisik3_keymap_data = { + .keymap = default_keymap, + .keymap_size = ARRAY_SIZE(default_keymap), +}; +static uint16_t long_func_key1[] = {KEY_BACK}; + +static struct keypad_remap_item remap_items[] = { + {KEY_HOME, 1, 1000/*ms*/, long_func_key1}, + /*TODO: add your remap_item here*/ +}; + +static struct keypad_remap hisik3_keypad_long_remap = { + .count = ARRAY_SIZE(remap_items), + .items = remap_items, +}; + +static struct k3v2_keypad_platdata hisik3_keypad_platdata = { + .keymap_data = &hisik3_keymap_data, + .keypad_remap = &hisik3_keypad_long_remap, + .rows = 8, + .cols = 8, + .row_shift = 3, +}; + +static const struct of_device_id keypad_match[] = { + { .compatible = "hisilicon,k3_keypad", + .data = &hisik3_keypad_platdata, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, keypad_match); +#endif + +static int k3v2_keypad_probe(struct platform_device *pdev) +{ + const struct k3v2_keypad_platdata *platdata; + const struct matrix_keymap_data *keymap_data; + struct k3v2_keypad *keypad; + struct resource *res; + struct input_dev *input_dev; + int err; +#ifdef CONFIG_OF + const struct of_device_id *match; + + match = of_match_node(keypad_match, pdev->dev.of_node); + platdata = match->data; +#else + platdata = pdev->dev.platform_data; +#endif + if (!platdata) { + dev_err(&pdev->dev, "platform data is null!\n"); + return -EINVAL; + } + keymap_data = platdata->keymap_data; + if (!keymap_data) { + dev_err(&pdev->dev, "keymap data is null!\n"); + return -EINVAL; + } + + if (!platdata->rows || platdata->rows > KPC_MAX_ROWS) { + dev_err(&pdev->dev, "keypad rows is null or bigger than the max rows!\n"); + return -EINVAL; + } + + if (!platdata->cols || platdata->cols > KPC_MAX_COLS) { + dev_err(&pdev->dev, "keypad cols is null or bigger than the max cols!\n"); + return -EINVAL; + } + + keypad = kzalloc(sizeof(struct k3v2_keypad) , GFP_KERNEL); + if (!keypad) { + dev_err(&pdev->dev, "Failed to allocate struct k3v2_keypad!\n"); + err = -ENOMEM; + } + + input_dev = input_allocate_device(); + if (!input_dev) { + dev_err(&pdev->dev, "Failed to allocate struct k3v2_keypad or input_dev!\n"); + err = -ENOMEM; + goto err_alloc_input_device; + } + + keypad_long_remap = platdata->keypad_remap; + if (!keypad_long_remap) { + dev_err(&pdev->dev, "Failed to get_keypad_long_remap!\n"); + err = -EINVAL; + goto err_get_keypad_remap; + } + + g_long_remap_state = kzalloc(keypad_long_remap->count * + sizeof(struct keypad_remap_state), + GFP_KERNEL); + if (!g_long_remap_state) { + dev_err(&pdev->dev, "Failed to allocate g_long_remap_state!\n"); + err = -ENOMEM; + goto err_alloc_remap_state; + } + /*Get REG_BASE_KPC address*/ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "Failed to get KPC base address!\n"); + err = -ENODEV; + goto err_get_base; + } + + keypad->base = ioremap(res->start, resource_size(res)); + if (!keypad->base) { + dev_err(&pdev->dev, "Failed to remap KPC base address!\n"); + err = -EBUSY; + goto err_ioremap_base; + } + + /*get clock*/ + keypad->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(keypad->clk)) { + dev_err(&pdev->dev, "Failed to get clk_kpc!\n"); + err = -ENODEV; + goto err_get_clk; + } + err = clk_set_rate(keypad->clk, KPC_CLK_RATE); + if (err < 0) { + dev_err(&pdev->dev, "Failed to set clk rate!\n"); + goto err_set_clk; + } + clk_prepare_enable(keypad->clk); + + keypad->input_dev = input_dev; + keypad->row_shift = platdata->row_shift; + keypad->rows = platdata->rows; + keypad->cols = platdata->cols; + + input_dev->name = pdev->name; + input_dev->id.bustype = BUS_HOST; + input_dev->dev.parent = &pdev->dev; + input_set_drvdata(input_dev, keypad); + set_bit(EV_KEY, input_dev->evbit); + set_bit(EV_SYN, input_dev->evbit); + input_dev->keycode = keypad->keycodes; + input_dev->keycodesize = sizeof(keypad->keycodes[0]); + input_dev->open = k3v2_keypad_open; + input_dev->close = k3v2_keypad_close; + + matrix_keypad_build_keymap(keymap_data, "keypad_default_keymap", + keypad->rows, keypad->cols, + input_dev->keycode, input_dev); + + keypad->irq = platform_get_irq(pdev, 0); + if (keypad->irq < 0) { + dev_err(&pdev->dev, "Failed to get irq!\n"); + err = keypad->irq; + goto err_get_irq; + + } + + err = request_irq(keypad->irq, k3v2_keypad_irq_handler, + IRQF_NO_SUSPEND, pdev->name, keypad); + if (err) { + dev_err(&pdev->dev, "Failed to request interupt handler!\n"); + goto err_request_irq; + } + + disable_irq(keypad->irq); + + err = input_register_device(keypad->input_dev); + if (err) { + dev_err(&pdev->dev, "Failed to register input device!\n"); + goto err_register_device; + } + + device_init_wakeup(&pdev->dev, true); + platform_set_drvdata(pdev, keypad); + dev_info(&pdev->dev, "k3v2 keypad probe successfully!\n"); + + return 0; + +err_register_device: + free_irq(keypad->irq, keypad); +err_request_irq: +err_get_irq: + clk_disable_unprepare(keypad->clk); +err_set_clk: + clk_put(keypad->clk); +err_get_clk: + iounmap(keypad->base); +err_ioremap_base: +err_get_base: + kfree(g_long_remap_state); +err_alloc_remap_state: +err_get_keypad_remap: + input_free_device(input_dev); +err_alloc_input_device: + kfree(keypad); + + pr_info("K3v2 keypad probe failed! ret = %d\n", err); + return err; +} + +static int k3v2_keypad_remove(struct platform_device *pdev) +{ + struct k3v2_keypad *keypad = platform_get_drvdata(pdev); + + if (keypad == NULL) { + dev_err(&pdev->dev, "get invalid keypad pointer\n"); + return -EINVAL; + } + + free_irq(keypad->irq, keypad); + iounmap(keypad->base); + + clk_disable_unprepare(keypad->clk); + clk_put(keypad->clk); + + input_unregister_device(keypad->input_dev); + platform_set_drvdata(pdev, NULL); + kfree(keypad); + + if (!g_long_remap_state) + kfree(g_long_remap_state); + + return 0; +} + +#ifdef CONFIG_PM +static int k3v2_keypad_suspend(struct platform_device *pdev, pm_message_t state) +{ + pr_info("[keypad]suspend successfully\n"); + return 0; +} + +static int k3v2_keypad_resume(struct platform_device *pdev) +{ + pr_info("[keypad]resume successfully\n"); + return 0; +} +#endif + + + +struct platform_driver k3v2_keypad_driver = { + .probe = k3v2_keypad_probe, + .remove = k3v2_keypad_remove, + .driver = { + .name = "k3_keypad", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(keypad_match), + }, +#ifdef CONFIG_PM + .suspend = k3v2_keypad_suspend, + .resume = k3v2_keypad_resume, +#endif +}; + +static int __init k3v2_keypad_init(void) +{ + pr_info("k3v2 keypad init!\n"); + return platform_driver_register(&k3v2_keypad_driver); +} + +static void __exit k3v2_keypad_exit(void) +{ + platform_driver_unregister(&k3v2_keypad_driver); +} + +module_init(k3v2_keypad_init); +module_exit(k3v2_keypad_exit); +MODULE_DESCRIPTION("Hi3620 keypad platform driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/keyboard/k3_keypad.h b/drivers/input/keyboard/k3_keypad.h new file mode 100644 index 0000000..8183021 --- /dev/null +++ b/drivers/input/keyboard/k3_keypad.h @@ -0,0 +1,47 @@ +#ifndef _K3_KEYPAD_H_ +#define _K3_KEYPAD_H_ +#include + +#define DPAD_CENTER 232 + +#define KEYPAD_CHECK_KEYCODE(map, keycode) \ + (map[(keycode) >> 4] & (1 << ((keycode) & 0x0F))) +#define KEYPAD_SET_KEYCODE(map, keycode) \ + (map[(keycode) >> 4] |= (1 << ((keycode) & 0x0F))) +#define KEYPAD_CLR_KEYCODE(map, keycode) \ + (map[(keycode) >> 4] &= ~(1 << ((keycode) & 0x0F))) + + +struct k3v2_keypad_platdata { + struct matrix_keymap_data *keymap_data; + struct keypad_remap *keypad_remap; + unsigned int rows; + unsigned int cols; + unsigned int row_shift; +}; + +/* + * target_keycode: target keycode remapped to + * keynum : number of keycodes to remap + * delay : timeout for pending + */ +struct keypad_remap_item { + uint16_t target_keycode; + uint16_t keynum; + uint16_t delay; + uint16_t *keycodes; +}; + +struct keypad_remap_state { + bool pending; + bool remapped; + unsigned int time; + unsigned char down_state; +}; + +struct keypad_remap { + int count; + struct keypad_remap_item *items; +}; + +#endif