722 lines
17 KiB
C
722 lines
17 KiB
C
/* Quanta I2C Keyboard Driver
|
|
*
|
|
* Copyright (C) 2009 Quanta Computer Inc.
|
|
* Copyright (c) 2010, The Linux Foundation. All rights reserved.
|
|
* Author: Hsin Wu <hsin.wu@quantatw.com>
|
|
* Author: Austin Lai <austin.lai@quantatw.com>
|
|
*
|
|
* This software is licensed under the terms of the GNU General Public
|
|
* License version 2, as published by the Free Software Foundation, and
|
|
* may be copied, distributed, and modified under those terms.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
*
|
|
* The Driver with I/O communications via the I2C Interface for ON2 of AP BU.
|
|
* And it is only working on the nuvoTon WPCE775x Embedded Controller.
|
|
*
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/init.h>
|
|
#include <linux/module.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/jiffies.h>
|
|
#include <linux/i2c.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/input.h>
|
|
#include <linux/keyboard.h>
|
|
#include <linux/gpio.h>
|
|
#include <linux/delay.h>
|
|
|
|
#include <linux/input/qci_kbd.h>
|
|
|
|
/* Keyboard special scancode */
|
|
#define RC_KEY_FN 0x70
|
|
#define RC_KEY_BREAK 0x80
|
|
#define KEY_ACK_FA 0xFA
|
|
#define SCAN_EMUL0 0xE0
|
|
#define SCAN_EMUL1 0xE1
|
|
#define SCAN_PAUSE1 0x1D
|
|
#define SCAN_PAUSE2 0x45
|
|
#define SCAN_LIDSW_OPEN 0x70
|
|
#define SCAN_LIDSW_CLOSE 0x71
|
|
|
|
/* Keyboard keycodes */
|
|
#define NOKEY KEY_RESERVED
|
|
#define KEY_LEFTWIN KEY_LEFTMETA
|
|
#define KEY_RIGHTWIN KEY_RIGHTMETA
|
|
#define KEY_APPS KEY_COMPOSE
|
|
#define KEY_PRINTSCR KEY_SYSRQ
|
|
|
|
#define KEYBOARD_ID_NAME "qci-i2ckbd"
|
|
#define KEYBOARD_NAME "Quanta Keyboard"
|
|
#define KEYBOARD_DEVICE "/i2c/input0"
|
|
#define KEYBOARD_CMD_ENABLE 0xF4
|
|
#define KEYBOARD_CMD_SET_LED 0xED
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
* Keyboard scancode to linux keycode translation table
|
|
*---------------------------------------------------------------------------*/
|
|
|
|
static const unsigned char on2_keycode[256] = {
|
|
[0] = NOKEY,
|
|
[1] = NOKEY,
|
|
[2] = NOKEY,
|
|
[3] = KEY_5,
|
|
[4] = KEY_7,
|
|
[5] = KEY_9,
|
|
[6] = KEY_MINUS,
|
|
[7] = NOKEY,
|
|
[8] = NOKEY,
|
|
[9] = NOKEY,
|
|
[10] = NOKEY,
|
|
[11] = KEY_LEFTBRACE,
|
|
[12] = KEY_F10,
|
|
[13] = KEY_INSERT,
|
|
[14] = KEY_F11,
|
|
[15] = KEY_ESC,
|
|
[16] = NOKEY,
|
|
[17] = NOKEY,
|
|
[18] = NOKEY,
|
|
[19] = KEY_4,
|
|
[20] = KEY_6,
|
|
[21] = KEY_8,
|
|
[22] = KEY_0,
|
|
[23] = KEY_EQUAL,
|
|
[24] = NOKEY,
|
|
[25] = NOKEY,
|
|
[26] = NOKEY,
|
|
[27] = KEY_P,
|
|
[28] = KEY_F9,
|
|
[29] = KEY_DELETE,
|
|
[30] = KEY_F12,
|
|
[31] = KEY_GRAVE,
|
|
[32] = KEY_W,
|
|
[33] = NOKEY,
|
|
[34] = NOKEY,
|
|
[35] = KEY_R,
|
|
[36] = KEY_T,
|
|
[37] = KEY_U,
|
|
[38] = KEY_O,
|
|
[39] = KEY_RIGHTBRACE,
|
|
[40] = NOKEY,
|
|
[41] = NOKEY,
|
|
[42] = NOKEY,
|
|
[43] = KEY_APOSTROPHE,
|
|
[44] = KEY_BACKSPACE,
|
|
[45] = NOKEY,
|
|
[46] = KEY_F8,
|
|
[47] = KEY_F5,
|
|
[48] = KEY_S,
|
|
[49] = NOKEY,
|
|
[50] = NOKEY,
|
|
[51] = KEY_E,
|
|
[52] = KEY_H,
|
|
[53] = KEY_Y,
|
|
[54] = KEY_I,
|
|
[55] = KEY_ENTER,
|
|
[56] = NOKEY,
|
|
[57] = NOKEY,
|
|
[58] = NOKEY,
|
|
[59] = KEY_SEMICOLON,
|
|
[60] = KEY_3,
|
|
[61] = KEY_PAGEUP,
|
|
[62] = KEY_Q,
|
|
[63] = KEY_TAB,
|
|
[64] = KEY_A,
|
|
[65] = NOKEY,
|
|
[66] = NOKEY,
|
|
[67] = KEY_F,
|
|
[68] = KEY_G,
|
|
[69] = KEY_J,
|
|
[70] = KEY_L,
|
|
[71] = NOKEY,
|
|
[72] = KEY_RIGHTSHIFT,
|
|
[73] = NOKEY,
|
|
[74] = NOKEY,
|
|
[75] = KEY_SLASH,
|
|
[76] = KEY_2,
|
|
[77] = KEY_PAGEDOWN,
|
|
[78] = KEY_F4,
|
|
[79] = KEY_F1,
|
|
[80] = KEY_Z,
|
|
[81] = NOKEY,
|
|
[82] = NOKEY,
|
|
[83] = KEY_D,
|
|
[84] = KEY_V,
|
|
[85] = KEY_N,
|
|
[86] = KEY_K,
|
|
[87] = NOKEY,
|
|
[88] = KEY_LEFTSHIFT,
|
|
[89] = KEY_RIGHTCTRL,
|
|
[90] = NOKEY,
|
|
[91] = KEY_DOT,
|
|
[92] = KEY_UP,
|
|
[93] = KEY_RIGHT,
|
|
[94] = KEY_F3,
|
|
[95] = KEY_F2,
|
|
[96] = NOKEY,
|
|
[97] = NOKEY,
|
|
[98] = KEY_RIGHTALT,
|
|
[99] = KEY_X,
|
|
[100] = KEY_C,
|
|
[101] = KEY_B,
|
|
[102] = KEY_COMMA,
|
|
[103] = NOKEY,
|
|
[104] = NOKEY,
|
|
[105] = NOKEY,
|
|
[106] = NOKEY,
|
|
[107] = NOKEY,
|
|
[108] = KEY_PRINTSCR,
|
|
[109] = KEY_DOWN,
|
|
[110] = KEY_1,
|
|
[111] = KEY_CAPSLOCK,
|
|
[112] = KEY_F24,
|
|
[113] = KEY_HOME,
|
|
[114] = KEY_LEFTALT,
|
|
[115] = NOKEY,
|
|
[116] = KEY_SPACE,
|
|
[117] = KEY_BACKSLASH,
|
|
[118] = KEY_M,
|
|
[119] = KEY_COMPOSE,
|
|
[120] = NOKEY,
|
|
[121] = KEY_LEFTCTRL,
|
|
[122] = NOKEY,
|
|
[123] = NOKEY,
|
|
[124] = KEY_PAUSE,
|
|
[125] = KEY_LEFT,
|
|
[126] = KEY_F7,
|
|
[127] = KEY_F6,
|
|
[128] = NOKEY,
|
|
[129] = NOKEY,
|
|
[130] = NOKEY,
|
|
[131] = NOKEY,
|
|
[132] = NOKEY,
|
|
[133] = NOKEY,
|
|
[134] = NOKEY,
|
|
[135] = NOKEY,
|
|
[136] = NOKEY,
|
|
[137] = NOKEY,
|
|
[138] = NOKEY,
|
|
[139] = NOKEY,
|
|
[140] = NOKEY,
|
|
[141] = NOKEY,
|
|
[142] = NOKEY,
|
|
[143] = NOKEY,
|
|
[144] = NOKEY,
|
|
[145] = NOKEY,
|
|
[146] = NOKEY,
|
|
[147] = NOKEY,
|
|
[148] = NOKEY,
|
|
[149] = NOKEY,
|
|
[150] = NOKEY,
|
|
[151] = NOKEY,
|
|
[152] = NOKEY,
|
|
[153] = NOKEY,
|
|
[154] = NOKEY,
|
|
[155] = NOKEY,
|
|
[156] = NOKEY,
|
|
[157] = NOKEY,
|
|
[158] = NOKEY,
|
|
[159] = NOKEY,
|
|
[160] = NOKEY,
|
|
[161] = NOKEY,
|
|
[162] = NOKEY,
|
|
[163] = NOKEY,
|
|
[164] = NOKEY,
|
|
[165] = NOKEY,
|
|
[166] = NOKEY,
|
|
[167] = NOKEY,
|
|
[168] = NOKEY,
|
|
[169] = NOKEY,
|
|
[170] = NOKEY,
|
|
[171] = NOKEY,
|
|
[172] = NOKEY,
|
|
[173] = NOKEY,
|
|
[174] = NOKEY,
|
|
[175] = NOKEY,
|
|
[176] = NOKEY,
|
|
[177] = NOKEY,
|
|
[178] = NOKEY,
|
|
[179] = NOKEY,
|
|
[180] = NOKEY,
|
|
[181] = NOKEY,
|
|
[182] = NOKEY,
|
|
[183] = NOKEY,
|
|
[184] = NOKEY,
|
|
[185] = NOKEY,
|
|
[186] = NOKEY,
|
|
[187] = NOKEY,
|
|
[188] = NOKEY,
|
|
[189] = KEY_HOME,
|
|
[190] = NOKEY,
|
|
[191] = NOKEY,
|
|
[192] = NOKEY,
|
|
[193] = NOKEY,
|
|
[194] = NOKEY,
|
|
[195] = NOKEY,
|
|
[196] = NOKEY,
|
|
[197] = NOKEY,
|
|
[198] = NOKEY,
|
|
[199] = NOKEY,
|
|
[200] = NOKEY,
|
|
[201] = NOKEY,
|
|
[202] = NOKEY,
|
|
[203] = NOKEY,
|
|
[204] = NOKEY,
|
|
[205] = KEY_END,
|
|
[206] = NOKEY,
|
|
[207] = NOKEY,
|
|
[208] = NOKEY,
|
|
[209] = NOKEY,
|
|
[210] = NOKEY,
|
|
[211] = NOKEY,
|
|
[212] = NOKEY,
|
|
[213] = NOKEY,
|
|
[214] = NOKEY,
|
|
[215] = NOKEY,
|
|
[216] = NOKEY,
|
|
[217] = NOKEY,
|
|
[218] = NOKEY,
|
|
[219] = NOKEY,
|
|
[220] = KEY_VOLUMEUP,
|
|
[221] = KEY_BRIGHTNESSUP,
|
|
[222] = NOKEY,
|
|
[223] = NOKEY,
|
|
[224] = NOKEY,
|
|
[225] = NOKEY,
|
|
[226] = NOKEY,
|
|
[227] = NOKEY,
|
|
[228] = NOKEY,
|
|
[229] = NOKEY,
|
|
[230] = NOKEY,
|
|
[231] = NOKEY,
|
|
[232] = NOKEY,
|
|
[233] = NOKEY,
|
|
[234] = NOKEY,
|
|
[235] = NOKEY,
|
|
[236] = NOKEY,
|
|
[237] = KEY_VOLUMEDOWN,
|
|
[238] = NOKEY,
|
|
[239] = NOKEY,
|
|
[240] = NOKEY,
|
|
[241] = NOKEY,
|
|
[242] = NOKEY,
|
|
[243] = NOKEY,
|
|
[244] = NOKEY,
|
|
[245] = NOKEY,
|
|
[246] = NOKEY,
|
|
[247] = NOKEY,
|
|
[248] = NOKEY,
|
|
[249] = NOKEY,
|
|
[250] = NOKEY,
|
|
[251] = NOKEY,
|
|
[252] = NOKEY,
|
|
[253] = KEY_BRIGHTNESSDOWN,
|
|
[254] = NOKEY,
|
|
[255] = NOKEY,
|
|
};
|
|
|
|
static const u8 emul0_map[128] = {
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 97, 0, 0,
|
|
113, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 114, 0,
|
|
115, 0, 0, 0, 0, 98, 0, 99, 100, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 102, 103, 104, 0, 105, 0, 106, 0, 107,
|
|
108, 109, 110, 111, 0, 0, 0, 0, 0, 0, 0, 139, 0, 150, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
};
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
* Global variables
|
|
*---------------------------------------------------------------------------*/
|
|
|
|
struct input_dev *g_qci_keyboard_dev;
|
|
|
|
/* General structure to hold the driver data */
|
|
struct i2ckbd_drv_data {
|
|
struct i2c_client *ki2c_client;
|
|
struct work_struct work;
|
|
struct input_dev *qcikbd_dev;
|
|
struct mutex kb_mutex;
|
|
unsigned int qcikbd_gpio; /* GPIO used for interrupt */
|
|
unsigned int qcikbd_irq;
|
|
unsigned int key_down;
|
|
unsigned int escape;
|
|
unsigned int pause_seq;
|
|
unsigned int fn;
|
|
unsigned char led_status;
|
|
bool standard_scancodes;
|
|
bool kb_leds;
|
|
bool event_led;
|
|
bool emul0;
|
|
bool emul1;
|
|
bool pause1;
|
|
};
|
|
#ifdef CONFIG_PM
|
|
static int qcikbd_suspend(struct device *dev)
|
|
{
|
|
struct i2ckbd_drv_data *context = input_get_drvdata(g_qci_keyboard_dev);
|
|
|
|
enable_irq_wake(context->qcikbd_irq);
|
|
return 0;
|
|
}
|
|
|
|
static int qcikbd_resume(struct device *dev)
|
|
{
|
|
struct i2ckbd_drv_data *context = input_get_drvdata(g_qci_keyboard_dev);
|
|
struct i2c_client *ikbdclient = context->ki2c_client;
|
|
|
|
disable_irq_wake(context->qcikbd_irq);
|
|
|
|
/* consume any keypress generated while suspended */
|
|
i2c_smbus_read_byte(ikbdclient);
|
|
return 0;
|
|
}
|
|
#endif
|
|
static int __devinit qcikbd_probe(struct i2c_client *client,
|
|
const struct i2c_device_id *id);
|
|
static int __devexit qcikbd_remove(struct i2c_client *kbd);
|
|
|
|
static const struct i2c_device_id qcikbd_idtable[] = {
|
|
{ KEYBOARD_ID_NAME, 0 },
|
|
{ }
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(i2c, qcikbd_idtable);
|
|
|
|
#ifdef CONFIG_PM
|
|
static struct dev_pm_ops qcikbd_pm_ops = {
|
|
.suspend = qcikbd_suspend,
|
|
.resume = qcikbd_resume,
|
|
};
|
|
#endif
|
|
static struct i2c_driver i2ckbd_driver = {
|
|
.driver = {
|
|
.owner = THIS_MODULE,
|
|
.name = KEYBOARD_ID_NAME,
|
|
#ifdef CONFIG_PM
|
|
.pm = &qcikbd_pm_ops,
|
|
#endif
|
|
},
|
|
.probe = qcikbd_probe,
|
|
.remove = __devexit_p(qcikbd_remove),
|
|
.id_table = qcikbd_idtable,
|
|
};
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
* Driver functions
|
|
*---------------------------------------------------------------------------*/
|
|
|
|
#ifdef CONFIG_KEYBOARD_QCIKBD_LID
|
|
static void process_lid(struct input_dev *ikbdev, unsigned char scancode)
|
|
{
|
|
if (scancode == SCAN_LIDSW_OPEN)
|
|
input_report_switch(ikbdev, SW_LID, 0);
|
|
else if (scancode == SCAN_LIDSW_CLOSE)
|
|
input_report_switch(ikbdev, SW_LID, 1);
|
|
else
|
|
return;
|
|
input_sync(ikbdev);
|
|
}
|
|
#endif
|
|
|
|
static irqreturn_t qcikbd_interrupt(int irq, void *dev_id)
|
|
{
|
|
struct i2ckbd_drv_data *ikbd_drv_data = dev_id;
|
|
schedule_work(&ikbd_drv_data->work);
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static void qcikbd_work_handler(struct work_struct *_work)
|
|
{
|
|
unsigned char scancode;
|
|
unsigned char scancode_only;
|
|
unsigned int keycode;
|
|
|
|
struct i2ckbd_drv_data *ikbd_drv_data =
|
|
container_of(_work, struct i2ckbd_drv_data, work);
|
|
|
|
struct i2c_client *ikbdclient = ikbd_drv_data->ki2c_client;
|
|
struct input_dev *ikbdev = ikbd_drv_data->qcikbd_dev;
|
|
|
|
mutex_lock(&ikbd_drv_data->kb_mutex);
|
|
|
|
if ((ikbd_drv_data->kb_leds) && (ikbd_drv_data->event_led)) {
|
|
i2c_smbus_write_byte(ikbdclient, KEYBOARD_CMD_SET_LED);
|
|
i2c_smbus_write_byte(ikbdclient, ikbd_drv_data->led_status);
|
|
ikbd_drv_data->event_led = 0;
|
|
goto work_exit;
|
|
}
|
|
|
|
scancode = i2c_smbus_read_byte(ikbdclient);
|
|
|
|
if (scancode == KEY_ACK_FA)
|
|
goto work_exit;
|
|
|
|
if (ikbd_drv_data->standard_scancodes) {
|
|
/* pause key is E1 1D 45 */
|
|
if (scancode == SCAN_EMUL1) {
|
|
ikbd_drv_data->emul1 = 1;
|
|
goto work_exit;
|
|
}
|
|
if (ikbd_drv_data->emul1) {
|
|
ikbd_drv_data->emul1 = 0;
|
|
if ((scancode & 0x7f) == SCAN_PAUSE1)
|
|
ikbd_drv_data->pause1 = 1;
|
|
goto work_exit;
|
|
}
|
|
if (ikbd_drv_data->pause1) {
|
|
ikbd_drv_data->pause1 = 0;
|
|
if ((scancode & 0x7f) == SCAN_PAUSE2) {
|
|
input_report_key(ikbdev, KEY_PAUSE,
|
|
!(scancode & 0x80));
|
|
input_sync(ikbdev);
|
|
}
|
|
goto work_exit;
|
|
}
|
|
|
|
if (scancode == SCAN_EMUL0) {
|
|
ikbd_drv_data->emul0 = 1;
|
|
goto work_exit;
|
|
}
|
|
if (ikbd_drv_data->emul0) {
|
|
ikbd_drv_data->emul0 = 0;
|
|
scancode_only = scancode & 0x7f;
|
|
#ifdef CONFIG_KEYBOARD_QCIKBD_LID
|
|
if ((scancode_only == SCAN_LIDSW_OPEN) ||
|
|
(scancode_only == SCAN_LIDSW_CLOSE)) {
|
|
process_lid(ikbdev, scancode);
|
|
goto work_exit;
|
|
}
|
|
#endif
|
|
keycode = emul0_map[scancode_only];
|
|
if (!keycode) {
|
|
dev_err(&ikbdev->dev,
|
|
"Unrecognized scancode %02x %02x\n",
|
|
SCAN_EMUL0, scancode);
|
|
goto work_exit;
|
|
}
|
|
} else {
|
|
keycode = scancode & 0x7f;
|
|
}
|
|
/* MS bit of scancode indicates direction of keypress */
|
|
ikbd_drv_data->key_down = !(scancode & 0x80);
|
|
if (keycode) {
|
|
input_event(ikbdev, EV_MSC, MSC_SCAN, scancode);
|
|
input_report_key(ikbdev, keycode,
|
|
ikbd_drv_data->key_down);
|
|
input_sync(ikbdev);
|
|
}
|
|
goto work_exit;
|
|
}
|
|
|
|
mutex_unlock(&ikbd_drv_data->kb_mutex);
|
|
|
|
if (scancode == RC_KEY_FN) {
|
|
ikbd_drv_data->fn = 0x80; /* select keycode table > 0x7F */
|
|
} else {
|
|
ikbd_drv_data->key_down = 1;
|
|
if (scancode & RC_KEY_BREAK) {
|
|
ikbd_drv_data->key_down = 0;
|
|
if ((scancode & 0x7F) == RC_KEY_FN)
|
|
ikbd_drv_data->fn = 0;
|
|
}
|
|
keycode = on2_keycode[(scancode & 0x7F) | ikbd_drv_data->fn];
|
|
if (keycode != NOKEY) {
|
|
input_report_key(ikbdev,
|
|
keycode,
|
|
ikbd_drv_data->key_down);
|
|
input_sync(ikbdev);
|
|
}
|
|
}
|
|
return;
|
|
|
|
work_exit:
|
|
mutex_unlock(&ikbd_drv_data->kb_mutex);
|
|
}
|
|
|
|
static int qcikbd_input_event(struct input_dev *dev, unsigned int type,
|
|
unsigned int code, int value)
|
|
{
|
|
struct i2ckbd_drv_data *ikbd_drv_data = input_get_drvdata(dev);
|
|
struct input_dev *ikbdev = ikbd_drv_data->qcikbd_dev;
|
|
|
|
if (type != EV_LED)
|
|
return -EINVAL;
|
|
|
|
ikbd_drv_data->led_status =
|
|
(test_bit(LED_SCROLLL, ikbdev->led) ? 1 : 0) |
|
|
(test_bit(LED_NUML, ikbdev->led) ? 2 : 0) |
|
|
(test_bit(LED_CAPSL, ikbdev->led) ? 4 : 0);
|
|
ikbd_drv_data->event_led = 1;
|
|
|
|
schedule_work(&ikbd_drv_data->work);
|
|
return 0;
|
|
}
|
|
|
|
static int qcikbd_open(struct input_dev *dev)
|
|
{
|
|
struct i2ckbd_drv_data *ikbd_drv_data = input_get_drvdata(dev);
|
|
struct i2c_client *ikbdclient = ikbd_drv_data->ki2c_client;
|
|
|
|
/* Send F4h - enable keyboard */
|
|
i2c_smbus_write_byte(ikbdclient, KEYBOARD_CMD_ENABLE);
|
|
return 0;
|
|
}
|
|
|
|
static int __devinit qcikbd_probe(struct i2c_client *client,
|
|
const struct i2c_device_id *id)
|
|
{
|
|
int err;
|
|
int i;
|
|
struct i2ckbd_drv_data *context;
|
|
struct qci_kbd_platform_data *pdata = client->dev.platform_data;
|
|
|
|
if (!pdata) {
|
|
pr_err("[KBD] platform data not supplied\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
context = kzalloc(sizeof(struct i2ckbd_drv_data), GFP_KERNEL);
|
|
if (!context)
|
|
return -ENOMEM;
|
|
i2c_set_clientdata(client, context);
|
|
context->ki2c_client = client;
|
|
context->qcikbd_gpio = client->irq;
|
|
client->driver = &i2ckbd_driver;
|
|
|
|
INIT_WORK(&context->work, qcikbd_work_handler);
|
|
mutex_init(&context->kb_mutex);
|
|
|
|
err = gpio_request(context->qcikbd_gpio, "qci-kbd");
|
|
if (err) {
|
|
pr_err("[KBD] err gpio request\n");
|
|
goto gpio_request_fail;
|
|
}
|
|
|
|
context->qcikbd_irq = gpio_to_irq(context->qcikbd_gpio);
|
|
err = request_irq(context->qcikbd_irq,
|
|
qcikbd_interrupt,
|
|
IRQF_TRIGGER_FALLING,
|
|
KEYBOARD_ID_NAME,
|
|
context);
|
|
if (err) {
|
|
pr_err("[KBD] err unable to get IRQ\n");
|
|
goto request_irq_fail;
|
|
}
|
|
|
|
context->standard_scancodes = pdata->standard_scancodes;
|
|
context->kb_leds = pdata->kb_leds;
|
|
context->qcikbd_dev = input_allocate_device();
|
|
if (!context->qcikbd_dev) {
|
|
pr_err("[KBD]allocting memory err\n");
|
|
err = -ENOMEM;
|
|
goto allocate_fail;
|
|
}
|
|
|
|
context->qcikbd_dev->name = KEYBOARD_NAME;
|
|
context->qcikbd_dev->phys = KEYBOARD_DEVICE;
|
|
context->qcikbd_dev->id.bustype = BUS_I2C;
|
|
context->qcikbd_dev->id.vendor = 0x1050;
|
|
context->qcikbd_dev->id.product = 0x0006;
|
|
context->qcikbd_dev->id.version = 0x0004;
|
|
context->qcikbd_dev->open = qcikbd_open;
|
|
set_bit(EV_KEY, context->qcikbd_dev->evbit);
|
|
__set_bit(MSC_SCAN, context->qcikbd_dev->mscbit);
|
|
|
|
if (pdata->repeat)
|
|
set_bit(EV_REP, context->qcikbd_dev->evbit);
|
|
|
|
/* Enable all supported keys */
|
|
for (i = 1; i < ARRAY_SIZE(on2_keycode) ; i++)
|
|
set_bit(on2_keycode[i], context->qcikbd_dev->keybit);
|
|
|
|
set_bit(KEY_POWER, context->qcikbd_dev->keybit);
|
|
set_bit(KEY_END, context->qcikbd_dev->keybit);
|
|
set_bit(KEY_VOLUMEUP, context->qcikbd_dev->keybit);
|
|
set_bit(KEY_VOLUMEDOWN, context->qcikbd_dev->keybit);
|
|
set_bit(KEY_ZOOMIN, context->qcikbd_dev->keybit);
|
|
set_bit(KEY_ZOOMOUT, context->qcikbd_dev->keybit);
|
|
|
|
#ifdef CONFIG_KEYBOARD_QCIKBD_LID
|
|
set_bit(EV_SW, context->qcikbd_dev->evbit);
|
|
set_bit(SW_LID, context->qcikbd_dev->swbit);
|
|
#endif
|
|
|
|
if (context->kb_leds) {
|
|
context->qcikbd_dev->event = qcikbd_input_event;
|
|
__set_bit(EV_LED, context->qcikbd_dev->evbit);
|
|
__set_bit(LED_NUML, context->qcikbd_dev->ledbit);
|
|
__set_bit(LED_CAPSL, context->qcikbd_dev->ledbit);
|
|
__set_bit(LED_SCROLLL, context->qcikbd_dev->ledbit);
|
|
}
|
|
|
|
input_set_drvdata(context->qcikbd_dev, context);
|
|
err = input_register_device(context->qcikbd_dev);
|
|
if (err) {
|
|
pr_err("[KBD] err input register device\n");
|
|
goto register_fail;
|
|
}
|
|
g_qci_keyboard_dev = context->qcikbd_dev;
|
|
return 0;
|
|
register_fail:
|
|
input_free_device(context->qcikbd_dev);
|
|
|
|
allocate_fail:
|
|
free_irq(context->qcikbd_irq, context);
|
|
|
|
request_irq_fail:
|
|
gpio_free(context->qcikbd_gpio);
|
|
|
|
gpio_request_fail:
|
|
i2c_set_clientdata(client, NULL);
|
|
kfree(context);
|
|
return err;
|
|
}
|
|
|
|
static int __devexit qcikbd_remove(struct i2c_client *dev)
|
|
{
|
|
struct i2ckbd_drv_data *context = i2c_get_clientdata(dev);
|
|
|
|
free_irq(context->qcikbd_irq, context);
|
|
gpio_free(context->qcikbd_gpio);
|
|
input_free_device(context->qcikbd_dev);
|
|
input_unregister_device(context->qcikbd_dev);
|
|
kfree(context);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int __init qcikbd_init(void)
|
|
{
|
|
return i2c_add_driver(&i2ckbd_driver);
|
|
}
|
|
|
|
static void __exit qcikbd_exit(void)
|
|
{
|
|
i2c_del_driver(&i2ckbd_driver);
|
|
}
|
|
|
|
struct input_dev *nkbc_keypad_get_input_dev(void)
|
|
{
|
|
return g_qci_keyboard_dev;
|
|
}
|
|
EXPORT_SYMBOL(nkbc_keypad_get_input_dev);
|
|
module_init(qcikbd_init);
|
|
module_exit(qcikbd_exit);
|
|
|
|
MODULE_AUTHOR("Quanta Computer Inc.");
|
|
MODULE_DESCRIPTION("Quanta Embedded Controller I2C Keyboard Driver");
|
|
MODULE_LICENSE("GPL v2");
|
|
|