Commit | Line | Data |
---|---|---|
f9705fcb NB |
1 | /* |
2 | * Keyboard driver for the AAED-2000 dev board | |
3 | * | |
4 | * Copyright (c) 2006 Nicolas Bellido Y Ortega | |
5 | * | |
6 | * Based on corgikbd.c | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License version 2 as | |
10 | * published by the Free Software Foundation. | |
11 | * | |
12 | */ | |
13 | ||
14 | #include <linux/delay.h> | |
15 | #include <linux/platform_device.h> | |
16 | #include <linux/init.h> | |
3f07d879 | 17 | #include <linux/input-polldev.h> |
f9705fcb NB |
18 | #include <linux/interrupt.h> |
19 | #include <linux/jiffies.h> | |
20 | #include <linux/module.h> | |
21 | #include <linux/slab.h> | |
f9705fcb | 22 | |
a09e64fb RK |
23 | #include <mach/hardware.h> |
24 | #include <mach/aaed2000.h> | |
f9705fcb NB |
25 | |
26 | #define KB_ROWS 12 | |
27 | #define KB_COLS 8 | |
28 | #define KB_ROWMASK(r) (1 << (r)) | |
29 | #define SCANCODE(r,c) (((c) * KB_ROWS) + (r)) | |
30 | #define NR_SCANCODES (KB_COLS * KB_ROWS) | |
31 | ||
32 | #define SCAN_INTERVAL (50) /* ms */ | |
33 | #define KB_ACTIVATE_DELAY (20) /* us */ | |
34 | ||
35 | static unsigned char aaedkbd_keycode[NR_SCANCODES] = { | |
36 | KEY_9, KEY_0, KEY_MINUS, KEY_EQUAL, KEY_BACKSPACE, 0, KEY_SPACE, KEY_KP6, 0, KEY_KPDOT, 0, 0, | |
37 | KEY_K, KEY_M, KEY_O, KEY_DOT, KEY_SLASH, 0, KEY_F, 0, 0, 0, KEY_LEFTSHIFT, 0, | |
38 | KEY_I, KEY_P, KEY_LEFTBRACE, KEY_RIGHTBRACE, KEY_BACKSLASH, 0, 0, 0, 0, 0, KEY_RIGHTSHIFT, 0, | |
39 | KEY_8, KEY_L, KEY_SEMICOLON, KEY_APOSTROPHE, KEY_ENTER, 0, 0, 0, 0, 0, 0, 0, | |
40 | KEY_J, KEY_H, KEY_B, KEY_KP8, KEY_KP4, 0, KEY_C, KEY_D, KEY_S, KEY_A, 0, KEY_CAPSLOCK, | |
41 | KEY_Y, KEY_U, KEY_N, KEY_T, 0, 0, KEY_R, KEY_E, KEY_W, KEY_Q, 0, KEY_TAB, | |
42 | KEY_7, KEY_6, KEY_G, 0, KEY_5, 0, KEY_4, KEY_3, KEY_2, KEY_1, 0, KEY_GRAVE, | |
43 | 0, 0, KEY_COMMA, 0, KEY_KP2, 0, KEY_V, KEY_LEFTALT, KEY_X, KEY_Z, 0, KEY_LEFTCTRL | |
44 | }; | |
45 | ||
46 | struct aaedkbd { | |
47 | unsigned char keycode[ARRAY_SIZE(aaedkbd_keycode)]; | |
3f07d879 | 48 | struct input_polled_dev *poll_dev; |
f9705fcb NB |
49 | int kbdscan_state[KB_COLS]; |
50 | int kbdscan_count[KB_COLS]; | |
51 | }; | |
52 | ||
53 | #define KBDSCAN_STABLE_COUNT 2 | |
54 | ||
55 | static void aaedkbd_report_col(struct aaedkbd *aaedkbd, | |
56 | unsigned int col, unsigned int rowd) | |
57 | { | |
58 | unsigned int scancode, pressed; | |
59 | unsigned int row; | |
60 | ||
61 | for (row = 0; row < KB_ROWS; row++) { | |
62 | scancode = SCANCODE(row, col); | |
63 | pressed = rowd & KB_ROWMASK(row); | |
64 | ||
3f07d879 DT |
65 | input_report_key(aaedkbd->poll_dev->input, |
66 | aaedkbd->keycode[scancode], pressed); | |
f9705fcb NB |
67 | } |
68 | } | |
69 | ||
70 | /* Scan the hardware keyboard and push any changes up through the input layer */ | |
3f07d879 | 71 | static void aaedkbd_poll(struct input_polled_dev *dev) |
f9705fcb | 72 | { |
3f07d879 | 73 | struct aaedkbd *aaedkbd = dev->private; |
f9705fcb NB |
74 | unsigned int col, rowd; |
75 | ||
76 | col = 0; | |
77 | do { | |
78 | AAEC_GPIO_KSCAN = col + 8; | |
79 | udelay(KB_ACTIVATE_DELAY); | |
80 | rowd = AAED_EXT_GPIO & AAED_EGPIO_KBD_SCAN; | |
81 | ||
82 | if (rowd != aaedkbd->kbdscan_state[col]) { | |
83 | aaedkbd->kbdscan_count[col] = 0; | |
84 | aaedkbd->kbdscan_state[col] = rowd; | |
85 | } else if (++aaedkbd->kbdscan_count[col] >= KBDSCAN_STABLE_COUNT) { | |
86 | aaedkbd_report_col(aaedkbd, col, rowd); | |
87 | col++; | |
88 | } | |
89 | } while (col < KB_COLS); | |
90 | ||
91 | AAEC_GPIO_KSCAN = 0x07; | |
3f07d879 | 92 | input_sync(dev->input); |
f9705fcb NB |
93 | } |
94 | ||
95 | static int __devinit aaedkbd_probe(struct platform_device *pdev) | |
96 | { | |
97 | struct aaedkbd *aaedkbd; | |
3f07d879 | 98 | struct input_polled_dev *poll_dev; |
f9705fcb NB |
99 | struct input_dev *input_dev; |
100 | int i; | |
101 | int error; | |
102 | ||
103 | aaedkbd = kzalloc(sizeof(struct aaedkbd), GFP_KERNEL); | |
3f07d879 DT |
104 | poll_dev = input_allocate_polled_device(); |
105 | if (!aaedkbd || !poll_dev) { | |
f9705fcb NB |
106 | error = -ENOMEM; |
107 | goto fail; | |
108 | } | |
109 | ||
110 | platform_set_drvdata(pdev, aaedkbd); | |
111 | ||
3f07d879 | 112 | aaedkbd->poll_dev = poll_dev; |
f9705fcb NB |
113 | memcpy(aaedkbd->keycode, aaedkbd_keycode, sizeof(aaedkbd->keycode)); |
114 | ||
3f07d879 DT |
115 | poll_dev->private = aaedkbd; |
116 | poll_dev->poll = aaedkbd_poll; | |
117 | poll_dev->poll_interval = SCAN_INTERVAL; | |
118 | ||
119 | input_dev = poll_dev->input; | |
f9705fcb NB |
120 | input_dev->name = "AAED-2000 Keyboard"; |
121 | input_dev->phys = "aaedkbd/input0"; | |
122 | input_dev->id.bustype = BUS_HOST; | |
123 | input_dev->id.vendor = 0x0001; | |
124 | input_dev->id.product = 0x0001; | |
125 | input_dev->id.version = 0x0100; | |
469ba4df DT |
126 | input_dev->dev.parent = &pdev->dev; |
127 | ||
7b19ada2 | 128 | input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); |
f9705fcb NB |
129 | input_dev->keycode = aaedkbd->keycode; |
130 | input_dev->keycodesize = sizeof(unsigned char); | |
131 | input_dev->keycodemax = ARRAY_SIZE(aaedkbd_keycode); | |
132 | ||
133 | for (i = 0; i < ARRAY_SIZE(aaedkbd_keycode); i++) | |
134 | set_bit(aaedkbd->keycode[i], input_dev->keybit); | |
135 | clear_bit(0, input_dev->keybit); | |
136 | ||
3f07d879 | 137 | error = input_register_polled_device(aaedkbd->poll_dev); |
f9705fcb NB |
138 | if (error) |
139 | goto fail; | |
140 | ||
141 | return 0; | |
142 | ||
143 | fail: kfree(aaedkbd); | |
3f07d879 | 144 | input_free_polled_device(poll_dev); |
f9705fcb NB |
145 | return error; |
146 | } | |
147 | ||
148 | static int __devexit aaedkbd_remove(struct platform_device *pdev) | |
149 | { | |
150 | struct aaedkbd *aaedkbd = platform_get_drvdata(pdev); | |
151 | ||
3f07d879 DT |
152 | input_unregister_polled_device(aaedkbd->poll_dev); |
153 | input_free_polled_device(aaedkbd->poll_dev); | |
f9705fcb NB |
154 | kfree(aaedkbd); |
155 | ||
156 | return 0; | |
157 | } | |
158 | ||
d7b5247b KS |
159 | /* work with hotplug and coldplug */ |
160 | MODULE_ALIAS("platform:aaed2000-keyboard"); | |
161 | ||
f9705fcb NB |
162 | static struct platform_driver aaedkbd_driver = { |
163 | .probe = aaedkbd_probe, | |
164 | .remove = __devexit_p(aaedkbd_remove), | |
165 | .driver = { | |
166 | .name = "aaed2000-keyboard", | |
d7b5247b | 167 | .owner = THIS_MODULE, |
f9705fcb NB |
168 | }, |
169 | }; | |
170 | ||
171 | static int __init aaedkbd_init(void) | |
172 | { | |
173 | return platform_driver_register(&aaedkbd_driver); | |
174 | } | |
175 | ||
176 | static void __exit aaedkbd_exit(void) | |
177 | { | |
178 | platform_driver_unregister(&aaedkbd_driver); | |
179 | } | |
180 | ||
181 | module_init(aaedkbd_init); | |
182 | module_exit(aaedkbd_exit); | |
183 | ||
184 | MODULE_AUTHOR("Nicolas Bellido Y Ortega"); | |
185 | MODULE_DESCRIPTION("AAED-2000 Keyboard Driver"); | |
839cd310 | 186 | MODULE_LICENSE("GPL v2"); |