#include <asm/jz4730/regs.h>
#include <asm/jz4730/ops.h>
///#define SCAN_USE_TIMER 1
#if 0 //wjx
#define DBG(args...) printk(args)
#else
#define DBG(args...)
#endif
#define SCANHZ (HZ/20)
#define CAPSLOCKLED 27 //wjx 2007.12.10
#define NUMLOCKLED 86
static int capslock_flag = 0; // wjx
static int numlock_flag = 0;
static int fn_f5_flag = 0;
/* KBD */
static struct input_dev *kbd_dev;
//static int kbd_thread_pid = -1;
#ifdef SCAN_USE_TIMER
struct timer_list scan_timer;
#else
static int kbd_start = 0;
static int kbd_tick = 0;
#endif
static int kbd_refcount = 0;
#if 1
const static unsigned int key_c[] = {
/* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 */
96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
125,
};
const static unsigned int key_r[] = {
0, 1, 2, 3, 4, 5, 6, 7
};
#else
const static unsigned int key_c[] = {
/* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 */
104, 97, 106, 6, 2, 5, 102, 1, 96, 111, 98, 7, 4, 110, 105, 125, 109,
};
const static unsigned int key_r[] = {
3, 103, 100, 0, 108, 101, 99, 107,
};
#endif
#define KB_SCAN_LINE (sizeof(key_c)/sizeof(*key_c))
static unsigned char last_keys[KB_SCAN_LINE];
static unsigned char cur_keys[KB_SCAN_LINE];
#define KEY_FN 0
const static int kmap[KB_SCAN_LINE][8] = {
/* 0*/ {KEY_PAUSE, 0, 0, 0, 0, 0, KEY_LEFTCTRL, KEY_F5,},
/* 1*/ {KEY_Q, KEY_TAB, KEY_A, KEY_ESC, KEY_Z, 0, KEY_GRAVE, KEY_1,},
/* 2*/ {KEY_W, KEY_CAPSLOCK, KEY_S, KEY_102ND, KEY_X, 0, 0, KEY_2,},//CZJ delet the KEY_BACKSLASH
/* 3*/ {KEY_E, KEY_F3, KEY_D, KEY_F4, KEY_C, 0, 0, KEY_3,},
/* 4*/ {KEY_R, KEY_T, KEY_F, KEY_G, KEY_V, KEY_B, KEY_5, KEY_4,},
/* 5*/ {KEY_U, KEY_Y, KEY_J, KEY_H, KEY_M, KEY_N, KEY_6, KEY_7,},
/* 6*/ {KEY_I, KEY_RIGHTBRACE, KEY_K, KEY_F6, KEY_COMMA, 0, KEY_EQUAL,
KEY_8,},
/* 7*/ {KEY_O, KEY_F7, KEY_L, 0, KEY_DOT, KEY_F19, KEY_F8, KEY_9,},
/* 8*/ {0, 0, 0, KEY_SPACE, KEY_NUMLOCK, 0, KEY_DELETE, 0,},
/* 9*/ {0, KEY_BACKSPACE, 0, 0, KEY_ENTER, 0, KEY_F9, 0,},
/*10*/ {0, 0, 0, KEY_LEFTALT, 0, 0, 0, KEY_SYSRQ,},
/*11*/ {KEY_P, KEY_LEFTBRACE, KEY_SEMICOLON, KEY_APOSTROPHE, KEY_BACKSLASH,
KEY_SLASH, KEY_MINUS, KEY_0,},
/*12*/ {KEY_KP0, KEY_F20, KEY_KP1, KEY_KP2, KEY_KP3, KEY_KP4, KEY_KP5, KEY_F10,}, //CZJ ADD NUM PACK
/*13*/ {KEY_KP6, KEY_KP7, KEY_KP8, KEY_KP9, KEY_KPPLUS, KEY_KPMINUS, KEY_F2, KEY_KPSLASH,}, //CZJ ADD NUM PACK
/*14*/ {KEY_KPDOT, KEY_KPASTERISK, 0, 0, 0, 0, KEY_INSERT, 0,}, //CZJ ADD NUM PACK
/*15*/ {0, 0, KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, 0, 0,},
/*16*/ {0, KEY_LEFTSHIFT, KEY_RIGHTSHIFT, 0, 0, 0, KEY_F1, KEY_FN,},
};
static int
xy2code (int col, int row)
{
if ((col < KB_SCAN_LINE) && (row < 8))
return kmap[col][row];
else
return 0;
}
#define KB_SCAN_LINE (sizeof(key_c)/sizeof(*key_c))
static void
setup_keybd (void)
{
int i;
for (i = 0; i < sizeof (key_r) / sizeof (*key_r); i++)
{
__gpio_as_input (key_r[i]);
__gpio_disable_pull (key_r[i]);
}
__gpio_as_input (8); /* key_int */
for (i = 0; i < KB_SCAN_LINE; i++)
{
__gpio_as_output (key_c[i]);
__gpio_clear_pin (key_c[i]);
}
__gpio_as_output (NUMLOCKLED); //wjx 2007.12.10
__gpio_set_pin (NUMLOCKLED);
__gpio_as_output (CAPSLOCKLED);
__gpio_set_pin (CAPSLOCKLED);
}
#define kb_scan_delay() udelay(3)
static int
scan_keybd (unsigned char *kr)
{
int i, k;
for (i = 0; i < 8; i++)
{
if (!__gpio_get_pin (key_r[i]))
{
for (k = 0; k < KB_SCAN_LINE; k++)
{
__gpio_set_pin (key_c[k]);
kb_scan_delay ();
if (__gpio_get_pin (key_r[i]))
{
if (kmap[k][i] == 0)
DBG ("input [%d] %d\n", k, i);
kr[k] |= (1U << i);
}
__gpio_clear_pin (key_c[k]);
}
}
}
return (int) __gpio_get_pin (8);
}
static void scan_kbd (unsigned long);
static void
start_scan_timer (void)
{
#ifdef SCAN_USE_TIMER
scan_timer.expires = jiffies + SCANHZ;
scan_timer.data = 0;
scan_timer.function = scan_kbd;
add_timer (&scan_timer);
#else
kbd_start = 1;
#endif
}
static void
set_capslock_led ()
{
capslock_flag = !capslock_flag;
if (capslock_flag)
{
__gpio_clear_pin (CAPSLOCKLED);
}
else
{
__gpio_set_pin (CAPSLOCKLED);
}
}
static void
set_numlock_led ()
{
numlock_flag = !numlock_flag;
if (numlock_flag)
{
__gpio_clear_pin (NUMLOCKLED);
}
else
{
__gpio_set_pin (NUMLOCKLED);
}
}
/*for the "left-right" buttons beside touch pad.edwin*/
/*define gpio pin*/
#define TP_MOUSE_RIGHT 13
#define TP_MOUSE_LEFT 16
int left_up = 1;
int right_up = 1;
static struct input_dev *tp_mouse_dev;
static int
tp_mouse_event (struct input_dev *dev, unsigned int type, unsigned int code,
int value)
{
return 1;
}
static int
tp_mouse_open (struct input_dev *dev)
{
return 1;
}
static void
scan_mouse_left (void)
{
int state;
if (left_up != (state = __gpio_get_pin (TP_MOUSE_LEFT)))
{
left_up = state;
input_report_key (tp_mouse_dev, BTN_LEFT, state);
}
}
static void
scan_mouse_right (void)
{
int state;
if (right_up != (state = __gpio_get_pin (TP_MOUSE_RIGHT)))
{
right_up = state;
input_report_key (tp_mouse_dev, BTN_RIGHT, state);
}
}
static int
tp_mouse_setup (void)
{
int retval;
if (NULL ==
(tp_mouse_dev = kmalloc (sizeof (struct input_dev), GFP_KERNEL)))
return -ENOMEM;
/*init gpio */
__gpio_as_input (TP_MOUSE_LEFT);
__gpio_disable_pull (TP_MOUSE_LEFT);
__gpio_as_input (TP_MOUSE_RIGHT);
__gpio_disable_pull (TP_MOUSE_RIGHT);
memset (tp_mouse_dev, 0, sizeof (struct input_dev));
tp_mouse_dev->evbit[0] = BIT (EV_KEY) | BIT (EV_REL);
tp_mouse_dev->keybit[LONG (BTN_MOUSE)] = BIT (BTN_LEFT) | BIT (BTN_RIGHT);
tp_mouse_dev->relbit[0] = BIT (REL_X);
input_register_device (tp_mouse_dev);
return 0;
}
static void
tp_mouse_exit (void)
{
if (tp_mouse_dev)
{
input_unregister_device (tp_mouse_dev);
kfree (tp_mouse_dev);
}
}
/* end */
//wjx Fn + F5 change the lcd backlight
extern inline void __lcd_set_backlight_level (int n);
static void
scan_kbd (unsigned long dummy)
{
int col, scancode;
static int testcode = KEY_RESERVED;
int state;
int lcd_pwm_dut, key_down_flag = 0;
dummy = dummy;
memset (cur_keys, 0, sizeof (cur_keys));
scan_keybd (cur_keys);
for (col = 0; col < KB_SCAN_LINE; col++)
{
unsigned char change = cur_keys[col] ^ last_keys[col];
if (change)
{
int row;
for (row = 0; row < 8; row++)
{
if (((1U << row) & change) && (scancode = xy2code (col, row)))
{
DBG ("KB col=%d row=%d %d\n", col, row, scancode);
if (cur_keys[col] & (1U << row))
{
key_down_flag = 1;
}
else
{
key_down_flag = 0;
}
if (last_keys[16] & (1 << 7))
{ // Fn key down
extern void keyboard_set_codec_volume (int);
switch (scancode)
{
case KEY_F1:
scancode = KEY_F11;
break;
case KEY_F2:
scancode = KEY_F12;
break;
case KEY_F10:
scancode = KEY_SCROLLLOCK;
break;
case KEY_UP:
scancode = KEY_PAGEUP;
break;
case KEY_DOWN:
scancode = KEY_PAGEDOWN;
break;
case KEY_LEFT:
scancode = KEY_HOME;
break;
case KEY_RIGHT:
scancode = KEY_END;
break;
case KEY_MINUS:
if (key_down_flag)
keyboard_set_codec_volume (0);
scancode = KEY_RESERVED; //wjx filter the - key
break;
case KEY_EQUAL:
if (key_down_flag)
keyboard_set_codec_volume (1);
scancode = KEY_RESERVED; //wjx filter the = key
break;
case KEY_F5:
if (key_down_flag && !(cur_keys[0] & (1U << 6) && cur_keys[16] & (1U << 6))) // wjx 2008.2.27 change the lcd light (filter Fn + Ctrl + F1 change light )
{
#if 0
lcd_pwm_dut = REG_PWM_DUT (0) + 60;
if (lcd_pwm_dut > 300)
lcd_pwm_dut = 0;
__lcd_set_backlight_level (lcd_pwm_dut);
#else
if (REG_PWM_DUT (0) != 0)
{
__lcd_set_backlight_level (0); //wjx close the lcd backlight
}
else
{
__lcd_set_backlight_level (300); //wjx open the backlight
}
#endif
fn_f5_flag = 1;
}
else
{
fn_f5_flag = 0;
}
scancode = KEY_RESERVED; //wjx filter the F5 key
break;
case KEY_F20:
if (key_down_flag && cur_keys[16] & (1U << 7)) // wjx 2008.5.19 change close the lcd back light
{
if (REG_PWM_DUT (0) != 0)
{
__lcd_set_backlight_level (0);
}
else
{
__lcd_set_backlight_level (300);
}
}
scancode = KEY_RESERVED; //wjx filter the F5 key
break;
#if 0
case KEY_F9:
if (cur_keys[col] & (1U << row))
testcode++;
scancode = testcode;
if (testcode == KEY_MAX)
testcode = KEY_RESERVED;
break;
#endif
}
}
if (scancode == KEY_CAPSLOCK && key_down_flag) //wjx
{
set_capslock_led ();
}
if (scancode == KEY_NUMLOCK && key_down_flag)
{
set_numlock_led ();
}
if (numlock_flag) //wjx 2008.2.25 add numlock keyboard function
{
switch (scancode)
{
case KEY_7:
scancode = KEY_KP7;
break;
case KEY_8:
scancode = KEY_KP8;
break;
case KEY_9:
scancode = KEY_KP9;
break;
#if 0
case KEY_0:
if (key_down_flag)
{
input_report_key (kbd_dev, KEY_RIGHTSHIFT, 1);
scancode = KEY_8;
}
else
{
input_report_key (kbd_dev, KEY_8, 0);
scancode = KEY_KPRIGHTSHIFT;
}
break;
#endif
case KEY_0:
scancode = KEY_KPASTERISK;
break;
case KEY_U:
scancode = KEY_KP4;
break;
case KEY_I:
scancode = KEY_KP5;
break;
case KEY_O:
scancode = KEY_KP6;
break;
case KEY_P:
scancode = KEY_KPMINUS;
break;
case KEY_J:
scancode = KEY_KP1;
break;
case KEY_K:
scancode = KEY_KP2;
break;
case KEY_L:
scancode = KEY_KP3;
break;
#if 0
case KEY_SEMICOLON:
if (key_down_flag)
{
input_report_key (kbd_dev, KEY_RIGHTSHIFT, 1);
scancode = KEY_EQUAL;
}
else
{
input_report_key (kbd_dev, KEY_EQUAL, 0);
scancode = KEY_RIGHTSHIFT;
}
break;
#endif
case KEY_SEMICOLON:
scancode = KEY_KPPLUS;
break;
case KEY_SLASH:
scancode = KEY_KPSLASH;
break;
case KEY_M:
scancode = KEY_KP0;
break;
case KEY_DOT:
scancode = KEY_KPDOT;
break;
// case KEY_SLASH: scancode = KEY_SLASH; break;
}
}
if (scancode == KEY_F5 && fn_f5_flag) //wjx filter the fn + f5 key
{
scancode = KEY_RESERVED;
fn_f5_flag = 0;
}
if (scancode == KEY_F20)
scancode = KEY_RESERVED; //wjx filter the "zzz" key
if (scancode != KEY_RESERVED)
input_report_key (kbd_dev, scancode,
(cur_keys[col] & (1U << row)) ? 1 : 0);
}
}
last_keys[col] = cur_keys[col];
}
}
/*for the "left-right" buttons beside touch pad.edwin */
scan_mouse_left ();
scan_mouse_right ();
/*end */
start_scan_timer ();
}
#ifndef SCAN_USE_TIMER
void
minipc_kbd_on_timer (void)
{
if (kbd_start)
{
kbd_tick++;
kbd_tick %= SCANHZ;
if (0 == kbd_tick)
{
scan_kbd (0);
}
}
}
#endif
static int
kbd_open (struct input_dev *dev)
{
DBG ("%s %d\n", __FUNCTION__, kbd_refcount);
if (0 == kbd_refcount)
{
#ifdef SCAN_USE_TIMER
//memset (last_keys, 0, sizeof(last_keys));
init_timer (&scan_timer);
start_scan_timer ();
#endif
}
kbd_refcount++;
return 0;
}
static void
kbd_close (struct input_dev *dev)
{
DBG ("%s %d\n", __FUNCTION__, kbd_refcount);
kbd_refcount--;
if (0 == kbd_refcount)
{
#ifdef SCAN_USE_TIMER
del_timer (&scan_timer);
#endif
}
}
static int
kbd_event (struct input_dev *dev, unsigned int type, unsigned int code,
int value)
{
return -1;
}
static int Fn_scancode[] =
{ KEY_F11, KEY_F12, KEY_SCROLLLOCK, KEY_PAGEUP, KEY_PAGEDOWN, KEY_HOME,
KEY_END, };
int __init
minipc_kbd_init (void)
{
int nr;
int i, j;
DBG ("%s 1\n", __FUNCTION__);
setup_keybd ();
DBG ("%s 2\n", __FUNCTION__);
if (NULL == (kbd_dev = kmalloc (sizeof (*kbd_dev), GFP_KERNEL)))
return -ENOMEM;
memset (kbd_dev, 0, sizeof (*kbd_dev));
set_bit (EV_KEY, (kbd_dev->evbit));
set_bit (EV_REP, (kbd_dev->evbit));
DBG ("%s 3.2\n", __FUNCTION__);
for (i = 0; i < KB_SCAN_LINE; i++)
{
for (j = 0; j < 8; j++)
{
if (kmap[i][j])
set_bit (kmap[i][j], (kbd_dev->keybit));
}
}
for (i = 0; i < (sizeof (Fn_scancode) / sizeof (Fn_scancode[0])); i++)
set_bit (Fn_scancode[i], (kbd_dev->keybit));
clear_bit (0, kbd_dev->keybit);
kbd_dev->event = kbd_event;
kbd_dev->open = kbd_open;
kbd_dev->close = kbd_close;
input_register_device (kbd_dev);
/*for the "left-right" buttons beside touch pad.edwin */
tp_mouse_setup ();
DBG ("%s 4 %p\n", __FUNCTION__, kbd_dev->handle);
#ifndef SCAN_USE_TIMER
kbd_start = 1;
#endif
return 0;
}
static int __init
mod_minipc_kbd_init (void)
{
#ifdef CONFIG_MINIPC_GPIOKB_MODULE
minipc_kbd_init ();
#endif
}
static void __exit
minipc_kbd_exit (void)
{
DBG ("%s 1\n", __FUNCTION__);
if (kbd_dev)
{
input_unregister_device (kbd_dev);
kfree (kbd_dev);
}
/*for the "left-right" buttons beside touch pad.edwin */
tp_mouse_exit ();
}
module_init (mod_minipc_kbd_init) module_exit (minipc_kbd_exit);