blob: 883b1c7bdb0aaf41a49b3eb7549f0b393361c9c0 [file] [log] [blame]
/*
* drivers/input/keyboard/tegra-nvec.c
*
* Keyboard class input driver for keyboards connected to an NvEc compliant
* embedded controller
*
* Copyright (c) 2009, 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/device.h>
#include <linux/kthread.h>
#include <linux/tegra_devices.h>
#include <linux/freezer.h>
#include "nvos.h"
#include "nvec.h"
#include "nvodm_services.h"
#include "nvodm_keyboard.h"
#include "nvec_device.h"
#define DRIVER_DESC "NvEc keyboard driver"
#define DRIVER_LICENSE "GPL"
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE(DRIVER_LICENSE);
#define NVEC_PAYLOAD 32
#define KEYBOARD_SCANNING_DISABLED_IN_SUSPEND 0
/* The total number of scan codes will be (first - last) */
#define EC_FIRST_CODE 0x00
#define EC_LAST_CODE 0x58
#define EC_TOTAL_CODES (EC_LAST_CODE - EC_FIRST_CODE + 1)
/**
* @brief This is the actual Scan-code-to-VKey mapping table. For new layouts
* this is the only structure which needs to be modified to return the
* proper vkey depending on the scan code.
*/
NvU8 code_tab_102us[EC_TOTAL_CODES] = {
KEY_GRAVE, // 0x00
KEY_ESC,
KEY_1,
KEY_2,
KEY_3,
KEY_4,
KEY_5,
KEY_6,
KEY_7,
KEY_8,
KEY_9,
KEY_0,
KEY_MINUS,
KEY_EQUAL,
KEY_BACKSPACE,
KEY_TAB,
KEY_Q, // 0x10
KEY_W,
KEY_E,
KEY_R,
KEY_T,
KEY_Y,
KEY_U,
KEY_I,
KEY_O,
KEY_P,
KEY_LEFTBRACE,
KEY_RIGHTBRACE,
KEY_ENTER,
KEY_LEFTCTRL,
KEY_A,
KEY_S,
KEY_D, // 0x20
KEY_F,
KEY_G,
KEY_H,
KEY_J,
KEY_K,
KEY_L,
KEY_SEMICOLON,
KEY_APOSTROPHE,
KEY_GRAVE,
KEY_LEFTSHIFT,
KEY_BACKSLASH,
KEY_Z,
KEY_X,
KEY_C,
KEY_V,
KEY_B, // 0x30
KEY_N,
KEY_M,
KEY_COMMA,
KEY_DOT,
KEY_SLASH,
KEY_RIGHTSHIFT,
KEY_KPASTERISK,
KEY_LEFTALT,
KEY_SPACE,
KEY_CAPSLOCK,
KEY_F1,
KEY_F2,
KEY_F3,
KEY_F4,
KEY_F5,
KEY_F6, // 0x40
KEY_F7,
KEY_F8,
KEY_F9,
KEY_F10,
KEY_NUMLOCK,
0, //VK_SCROLL
KEY_KP7,
KEY_KP8,
KEY_KP9,
KEY_KPMINUS,
KEY_KP4,
KEY_KP5,
KEY_KP6,
KEY_KPPLUS,
KEY_KP1,
KEY_KP2, // 0x50
KEY_KP3,
KEY_KP0,
KEY_KPDOT,
0, //VK_SNAPSHOT
0,
0, //VK_OEM_102
0, //VK_F11
0, //VK_F12
};
/* The total number of scan codes will be (first - last) */
#define EC_EXT_CODE_FIRST 0xE010
#define EC_EXT_CODE_LAST 0xE06D
#define EC_EXT_TOTAL_CODES (EC_EXT_CODE_LAST - EC_EXT_CODE_FIRST + 1)
/**
* @brief This table consists of the scan codes which were added after the
* original scan codes were designed.
* To avoid moving the already designed buttons to accomodate these
* new buttons, the new scan codes are preceded by 'E0'.
*/
NvU8 extcode_tab_us102[EC_EXT_TOTAL_CODES] = {
0, // 0xE0 0x10
0,
0,
0,
0,
0,
0,
0,
0,
0, //VK_MEDIA_NEXT_TRACK,
0,
0,
0, //VK_RETURN,
0, //VK_RCONTROL,
0,
0,
KEY_MUTE, // 0xE0 0x20
0, //VK_LAUNCH_APP1
0, //VK_MEDIA_PLAY_PAUSE
0,
0, //VK_MEDIA_STOP
0,
0,
0,
0,
0,
0,
0,
0,
0,
KEY_VOLUMEDOWN,
0,
KEY_VOLUMEUP, // 0xE0 0x30
0,
0, //VK_BROWSER_HOME
0,
0,
KEY_KPSLASH, //VK_DIVIDE
0,
0, //VK_SNAPSHOT
0, //VK_RMENU
0, //VK_OEM_NV_BACKLIGHT_UP
0, //VK_OEM_NV_BACKLIGHT_DN
0, //VK_OEM_NV_BACKLIGHT_AUTOTOGGLE
0, //VK_OEM_NV_POWER_INFO
0, //VK_OEM_NV_WIFI_TOGGLE
0, //VK_OEM_NV_DISPLAY_SELECT
0, //VK_OEM_NV_AIRPLANE_TOGGLE
0, //0xE0 0x40
0, //VK_OEM_NV_RESERVED
0, //VK_OEM_NV_RESERVED
0, //VK_OEM_NV_RESERVED
0, //VK_OEM_NV_RESERVED
0, //VK_OEM_NV_RESERVED
KEY_CANCEL,
KEY_HOME,
KEY_UP,
0, //VK_PRIOR
0,
KEY_LEFT,
0,
KEY_RIGHT,
0,
KEY_END,
KEY_DOWN, // 0xE0 0x50
0, //VK_NEXT
KEY_INSERT,
KEY_DELETE,
0,
0,
0,
0,
0,
0,
0,
KEY_MENU, //VK_LWIN
0, //VK_RWIN
KEY_BACK, //VK_APPS
0,
0,
0, // 0xE0 0x60
0,
0,
0,
0,
0, //VK_BROWSER_SEARCH
0, //VK_BROWSER_FAVORITES
0, //VK_BROWSER_REFRESH
0, //VK_BROWSER_STOP
0, //VK_BROWSER_FORWARD
0, //VK_BROWSER_BACK
0, //VK_LAUNCH_APP2
0, //VK_LAUNCH_MAIL
0, //VK_LAUNCH_MEDIA_SELECT
};
struct nvec_keyboard
{
struct input_dev *input_dev;
struct task_struct *task;
char name[128];
int shutdown;
unsigned short keycode[512];
NvEcHandle hNvec;
NvEcEventRegistrationHandle hEvent;
};
static int nvec_keyboard_recv(void *arg)
{
struct input_dev *input_dev = (struct input_dev *)arg;
struct nvec_keyboard *keyboard = input_get_drvdata(input_dev);
/* keyboard event thread should be frozen before suspending the
* keyboard and NVEC drivers */
set_freezable_with_signal();
while (!keyboard->shutdown) {
unsigned int pressed;
NvU32 code;
NvU8 flags;
if (!NvOdmKeyboardGetKeyData(&code, &flags, 0)) {
printk(KERN_INFO "nvec_keyboard: unhandled "
"scancode %x\n", code);
continue;
}
if (keyboard->shutdown)
break;
pressed = (flags & NV_ODM_SCAN_CODE_FLAG_MAKE);
if ((code >= EC_FIRST_CODE) && (code <= EC_LAST_CODE)) {
code -= EC_FIRST_CODE;
code = code_tab_102us[code];
input_report_key(keyboard->input_dev, code, pressed);
}
else if ((code >= EC_EXT_CODE_FIRST) &&
(code <= EC_EXT_CODE_LAST)) {
code -= EC_EXT_CODE_FIRST;
code = extcode_tab_us102[code];
input_report_key(keyboard->input_dev, code, pressed);
}
}
return 0;
}
static int nvec_keyboard_open(struct input_dev *dev)
{
return 0;
}
static void nvec_keyboard_close(struct input_dev *dev)
{
return;
}
static int __devinit nvec_keyboard_probe(struct nvec_device *pdev)
{
int error;
NvError nverr;
struct nvec_keyboard *keyboard;
struct input_dev *input_dev;
int i;
keyboard = kzalloc(sizeof(struct nvec_keyboard), GFP_KERNEL);
input_dev = input_allocate_device();
if (!keyboard || !input_dev) {
error = -ENOMEM;
goto fail;
}
keyboard->input_dev = input_dev;
input_set_drvdata(input_dev, keyboard);
nvec_set_drvdata(pdev, input_dev);
if (!NvOdmKeyboardInit()) {
error = -ENODEV;
pr_err("tegra_keyboard_probe: no keyboard\n");
goto fail_keyboard_init;
}
keyboard->task = kthread_create(nvec_keyboard_recv, input_dev,
"nvec_keyboard_thread");
if (keyboard->task == NULL) {
error = -ENOMEM;
goto fail_thread_create;
}
wake_up_process(keyboard->task);
if (!strlen(keyboard->name))
snprintf(keyboard->name, sizeof(keyboard->name),
"nvec keyboard");
input_dev->name = keyboard->name;
input_dev->open = nvec_keyboard_open;
input_dev->close = nvec_keyboard_close;
__set_bit(EV_KEY, input_dev->evbit);
for (i=1; i<EC_TOTAL_CODES; i++) {
__set_bit(code_tab_102us[i], input_dev->keybit);
}
for (i=1; i<EC_EXT_TOTAL_CODES; i++) {
__set_bit(extcode_tab_us102[i], input_dev->keybit);
}
/* get EC handle */
nverr = NvEcOpen(&keyboard->hNvec, 0 /* instance */);
if (nverr != NvError_Success) {
error = -ENODEV;
goto fail_input_register;
}
error = input_register_device(keyboard->input_dev);
if (error)
goto fail_input_register;
return 0;
fail_input_register:
(void)kthread_stop(keyboard->task);
fail_thread_create:
NvOdmKeyboardDeInit();
fail_keyboard_init:
fail:
NvEcClose(keyboard->hNvec);
keyboard->hNvec = NULL;
input_free_device(input_dev);
kfree(keyboard);
return error;
}
static void nvec_keyboard_remove(struct nvec_device *dev)
{
struct input_dev *input_dev = nvec_get_drvdata(dev);
struct nvec_keyboard *keyboard = input_get_drvdata(input_dev);
(void)kthread_stop(keyboard->task);
NvOdmKeyboardDeInit();
NvEcClose(keyboard->hNvec);
keyboard->hNvec = NULL;
keyboard->shutdown = 1;
input_free_device(input_dev);
kfree(keyboard);
}
static int nvec_keyboard_suspend(struct nvec_device *pdev, pm_message_t state)
{
#if KEYBOARD_SCANNING_DISABLED_IN_SUSPEND
NvEcRequest Request = {0};
NvEcResponse Response = {0};
NvError err = NvError_Success;
#endif
struct input_dev *input_dev = nvec_get_drvdata(pdev);
struct nvec_keyboard *keyboard = input_get_drvdata(input_dev);
if (!keyboard) {
printk("%s: device handle is NULL\n", __func__);
return -1;
}
#if KEYBOARD_SCANNING_DISABLED_IN_SUSPEND
/* disable keyboard scanning */
Request.PacketType = NvEcPacketType_Request;
Request.RequestType = NvEcRequestResponseType_Keyboard;
Request.RequestSubtype =
(NvEcRequestResponseSubtype)NvEcKeyboardSubtype_Disable;
Request.NumPayloadBytes = 0;
err = NvEcSendRequest(
keyboard->hNvec,
&Request,
&Response,
sizeof(Request),
sizeof(Response));
if (err != NvError_Success) {
printk("%s: scanning disable request send fail\n", __func__);
return -1;
}
if (Response.Status != NvEcStatus_Success) {
printk("%s: scanning could not be disabled\n", __func__);
return -1;
}
#endif
/* power down hardware */
if (!NvOdmKeyboardPowerHandler(NV_TRUE)) {
printk("%s: hardware power down fail\n", __func__);
return -1;
}
return 0;
}
static int nvec_keyboard_resume(struct nvec_device *pdev)
{
#if KEYBOARD_SCANNING_DISABLED_IN_SUSPEND
NvEcRequest Request = {0};
NvEcResponse Response = {0};
NvError err = NvError_Success;
#endif
struct input_dev *input_dev = nvec_get_drvdata(pdev);
struct nvec_keyboard *keyboard = input_get_drvdata(input_dev);
if (!keyboard) {
printk("%s: device handle is NULL\n", __func__);
return -1;
}
/* power up hardware */
if (!NvOdmKeyboardPowerHandler(NV_FALSE)) {
printk("%s: hardware power up fail\n", __func__);
return -1;
}
#if KEYBOARD_SCANNING_DISABLED_IN_SUSPEND
/* re-enable keyboard scanning */
Request.PacketType = NvEcPacketType_Request;
Request.RequestType = NvEcRequestResponseType_Keyboard;
Request.RequestSubtype =
(NvEcRequestResponseSubtype)NvEcKeyboardSubtype_Enable;
Request.NumPayloadBytes = 0;
err = NvEcSendRequest(
keyboard->hNvec,
&Request,
&Response,
sizeof(Request),
sizeof(Response));
if (err != NvError_Success) {
printk("%s: scanning enable request send fail\n", __func__);
return -1;
}
if (Response.Status != NvEcStatus_Success) {
printk("%s: scanning could not be enabled\n", __func__);
return -1;
}
#endif
return 0;
}
static struct nvec_driver nvec_keyboard_driver = {
.name = "nvec_keyboard",
.probe = nvec_keyboard_probe,
.remove = nvec_keyboard_remove,
.suspend = nvec_keyboard_suspend,
.resume = nvec_keyboard_resume,
};
static struct nvec_device nvec_keyboard_device = {
.name = "nvec_keyboard",
.driver = &nvec_keyboard_driver,
};
static int __init nvec_keyboard_init(void)
{
int err;
err = nvec_register_driver(&nvec_keyboard_driver);
if (err)
{
pr_err("**nvec_keyboard_init: nvec_register_driver: fail\n");
return err;
}
err = nvec_register_device(&nvec_keyboard_device);
if (err)
{
pr_err("**nvec_keyboard_init: nvec_device_add: fail\n");
nvec_unregister_driver(&nvec_keyboard_driver);
return err;
}
return 0;
}
static void __exit nvec_keyboard_exit(void)
{
nvec_unregister_device(&nvec_keyboard_device);
nvec_unregister_driver(&nvec_keyboard_driver);
}
module_init(nvec_keyboard_init);
module_exit(nvec_keyboard_exit);