Commit | Line | Data |
---|---|---|
6af6dc2d SG |
1 | /* |
2 | * ChromeOS EC keyboard driver | |
3 | * | |
4 | * Copyright (C) 2012 Google, Inc | |
5 | * | |
6 | * This software is licensed under the terms of the GNU General Public | |
7 | * License version 2, as published by the Free Software Foundation, and | |
8 | * may be copied, distributed, and modified under those terms. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | * | |
15 | * This driver uses the Chrome OS EC byte-level message-based protocol for | |
16 | * communicating the keyboard state (which keys are pressed) from a keyboard EC | |
17 | * to the AP over some bus (such as i2c, lpc, spi). The EC does debouncing, | |
18 | * but everything else (including deghosting) is done here. The main | |
19 | * motivation for this is to keep the EC firmware as simple as possible, since | |
20 | * it cannot be easily upgraded and EC flash/IRAM space is relatively | |
21 | * expensive. | |
22 | */ | |
23 | ||
24 | #include <linux/module.h> | |
017f14e8 | 25 | #include <linux/bitops.h> |
6af6dc2d SG |
26 | #include <linux/i2c.h> |
27 | #include <linux/input.h> | |
d1fd345e | 28 | #include <linux/interrupt.h> |
6af6dc2d | 29 | #include <linux/kernel.h> |
44051a68 | 30 | #include <linux/notifier.h> |
6af6dc2d SG |
31 | #include <linux/platform_device.h> |
32 | #include <linux/slab.h> | |
33 | #include <linux/input/matrix_keypad.h> | |
34 | #include <linux/mfd/cros_ec.h> | |
35 | #include <linux/mfd/cros_ec_commands.h> | |
36 | ||
37 | /* | |
38 | * @rows: Number of rows in the keypad | |
39 | * @cols: Number of columns in the keypad | |
40 | * @row_shift: log2 or number of rows, rounded up | |
41 | * @keymap_data: Matrix keymap data used to convert to keyscan values | |
42 | * @ghost_filter: true to enable the matrix key-ghosting filter | |
017f14e8 | 43 | * @valid_keys: bitmap of existing keys for each matrix column |
64757eba | 44 | * @old_kb_state: bitmap of keys pressed last scan |
6af6dc2d SG |
45 | * @dev: Device pointer |
46 | * @idev: Input device | |
47 | * @ec: Top level ChromeOS device to use to talk to EC | |
44051a68 | 48 | * @notifier: interrupt event notifier for transport devices |
6af6dc2d SG |
49 | */ |
50 | struct cros_ec_keyb { | |
51 | unsigned int rows; | |
52 | unsigned int cols; | |
53 | int row_shift; | |
54 | const struct matrix_keymap_data *keymap_data; | |
55 | bool ghost_filter; | |
017f14e8 | 56 | uint8_t *valid_keys; |
64757eba | 57 | uint8_t *old_kb_state; |
6af6dc2d SG |
58 | |
59 | struct device *dev; | |
60 | struct input_dev *idev; | |
61 | struct cros_ec_device *ec; | |
44051a68 | 62 | struct notifier_block notifier; |
6af6dc2d SG |
63 | }; |
64 | ||
65 | ||
6af6dc2d SG |
66 | /* |
67 | * Returns true when there is at least one combination of pressed keys that | |
68 | * results in ghosting. | |
69 | */ | |
70 | static bool cros_ec_keyb_has_ghosting(struct cros_ec_keyb *ckdev, uint8_t *buf) | |
71 | { | |
017f14e8 TB |
72 | int col1, col2, buf1, buf2; |
73 | struct device *dev = ckdev->dev; | |
74 | uint8_t *valid_keys = ckdev->valid_keys; | |
6af6dc2d SG |
75 | |
76 | /* | |
77 | * Ghosting happens if for any pressed key X there are other keys | |
78 | * pressed both in the same row and column of X as, for instance, | |
79 | * in the following diagram: | |
80 | * | |
81 | * . . Y . g . | |
82 | * . . . . . . | |
83 | * . . . . . . | |
84 | * . . X . Z . | |
85 | * | |
86 | * In this case only X, Y, and Z are pressed, but g appears to be | |
87 | * pressed too (see Wikipedia). | |
6af6dc2d | 88 | */ |
017f14e8 TB |
89 | for (col1 = 0; col1 < ckdev->cols; col1++) { |
90 | buf1 = buf[col1] & valid_keys[col1]; | |
91 | for (col2 = col1 + 1; col2 < ckdev->cols; col2++) { | |
92 | buf2 = buf[col2] & valid_keys[col2]; | |
93 | if (hweight8(buf1 & buf2) > 1) { | |
94 | dev_dbg(dev, "ghost found at: B[%02d]:0x%02x & B[%02d]:0x%02x", | |
95 | col1, buf1, col2, buf2); | |
96 | return true; | |
97 | } | |
98 | } | |
99 | } | |
6af6dc2d SG |
100 | |
101 | return false; | |
102 | } | |
103 | ||
017f14e8 | 104 | |
6af6dc2d SG |
105 | /* |
106 | * Compares the new keyboard state to the old one and produces key | |
107 | * press/release events accordingly. The keyboard state is 13 bytes (one byte | |
108 | * per column) | |
109 | */ | |
110 | static void cros_ec_keyb_process(struct cros_ec_keyb *ckdev, | |
111 | uint8_t *kb_state, int len) | |
112 | { | |
113 | struct input_dev *idev = ckdev->idev; | |
114 | int col, row; | |
115 | int new_state; | |
64757eba | 116 | int old_state; |
6af6dc2d SG |
117 | int num_cols; |
118 | ||
119 | num_cols = len; | |
120 | ||
121 | if (ckdev->ghost_filter && cros_ec_keyb_has_ghosting(ckdev, kb_state)) { | |
122 | /* | |
123 | * Simple-minded solution: ignore this state. The obvious | |
124 | * improvement is to only ignore changes to keys involved in | |
125 | * the ghosting, but process the other changes. | |
126 | */ | |
127 | dev_dbg(ckdev->dev, "ghosting found\n"); | |
128 | return; | |
129 | } | |
130 | ||
131 | for (col = 0; col < ckdev->cols; col++) { | |
132 | for (row = 0; row < ckdev->rows; row++) { | |
133 | int pos = MATRIX_SCAN_CODE(row, col, ckdev->row_shift); | |
134 | const unsigned short *keycodes = idev->keycode; | |
6af6dc2d | 135 | |
6af6dc2d | 136 | new_state = kb_state[col] & (1 << row); |
64757eba DA |
137 | old_state = ckdev->old_kb_state[col] & (1 << row); |
138 | if (new_state != old_state) { | |
6af6dc2d SG |
139 | dev_dbg(ckdev->dev, |
140 | "changed: [r%d c%d]: byte %02x\n", | |
141 | row, col, new_state); | |
142 | ||
64757eba DA |
143 | input_report_key(idev, keycodes[pos], |
144 | new_state); | |
6af6dc2d SG |
145 | } |
146 | } | |
64757eba | 147 | ckdev->old_kb_state[col] = kb_state[col]; |
6af6dc2d SG |
148 | } |
149 | input_sync(ckdev->idev); | |
150 | } | |
151 | ||
44051a68 | 152 | static int cros_ec_keyb_open(struct input_dev *dev) |
6af6dc2d | 153 | { |
44051a68 | 154 | struct cros_ec_keyb *ckdev = input_get_drvdata(dev); |
6af6dc2d | 155 | |
44051a68 VY |
156 | return blocking_notifier_chain_register(&ckdev->ec->event_notifier, |
157 | &ckdev->notifier); | |
d1fd345e AB |
158 | } |
159 | ||
44051a68 | 160 | static void cros_ec_keyb_close(struct input_dev *dev) |
d1fd345e AB |
161 | { |
162 | struct cros_ec_keyb *ckdev = input_get_drvdata(dev); | |
d1fd345e | 163 | |
44051a68 VY |
164 | blocking_notifier_chain_unregister(&ckdev->ec->event_notifier, |
165 | &ckdev->notifier); | |
d1fd345e AB |
166 | } |
167 | ||
44051a68 VY |
168 | static int cros_ec_keyb_work(struct notifier_block *nb, |
169 | unsigned long queued_during_suspend, void *_notify) | |
d1fd345e | 170 | { |
44051a68 VY |
171 | struct cros_ec_keyb *ckdev = container_of(nb, struct cros_ec_keyb, |
172 | notifier); | |
d1fd345e | 173 | |
44051a68 VY |
174 | if (ckdev->ec->event_data.event_type != EC_MKBP_EVENT_KEY_MATRIX) |
175 | return NOTIFY_DONE; | |
176 | /* | |
177 | * If EC is not the wake source, discard key state changes during | |
178 | * suspend. | |
179 | */ | |
180 | if (queued_during_suspend) | |
181 | return NOTIFY_OK; | |
182 | if (ckdev->ec->event_size != ckdev->cols) { | |
183 | dev_err(ckdev->dev, | |
184 | "Discarded incomplete key matrix event.\n"); | |
185 | return NOTIFY_OK; | |
186 | } | |
187 | cros_ec_keyb_process(ckdev, ckdev->ec->event_data.data.key_matrix, | |
188 | ckdev->ec->event_size); | |
189 | return NOTIFY_OK; | |
6af6dc2d SG |
190 | } |
191 | ||
017f14e8 TB |
192 | /* |
193 | * Walks keycodes flipping bit in buffer COLUMNS deep where bit is ROW. Used by | |
194 | * ghosting logic to ignore NULL or virtual keys. | |
195 | */ | |
196 | static void cros_ec_keyb_compute_valid_keys(struct cros_ec_keyb *ckdev) | |
197 | { | |
198 | int row, col; | |
199 | int row_shift = ckdev->row_shift; | |
200 | unsigned short *keymap = ckdev->idev->keycode; | |
201 | unsigned short code; | |
202 | ||
203 | BUG_ON(ckdev->idev->keycodesize != sizeof(*keymap)); | |
204 | ||
205 | for (col = 0; col < ckdev->cols; col++) { | |
206 | for (row = 0; row < ckdev->rows; row++) { | |
207 | code = keymap[MATRIX_SCAN_CODE(row, col, row_shift)]; | |
208 | if (code && (code != KEY_BATTERY)) | |
209 | ckdev->valid_keys[col] |= 1 << row; | |
210 | } | |
211 | dev_dbg(ckdev->dev, "valid_keys[%02d] = 0x%02x\n", | |
212 | col, ckdev->valid_keys[col]); | |
213 | } | |
214 | } | |
215 | ||
6af6dc2d SG |
216 | static int cros_ec_keyb_probe(struct platform_device *pdev) |
217 | { | |
218 | struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent); | |
8f97f8e5 | 219 | struct device *dev = &pdev->dev; |
6af6dc2d SG |
220 | struct cros_ec_keyb *ckdev; |
221 | struct input_dev *idev; | |
222 | struct device_node *np; | |
223 | int err; | |
224 | ||
225 | np = pdev->dev.of_node; | |
226 | if (!np) | |
227 | return -ENODEV; | |
228 | ||
8f97f8e5 | 229 | ckdev = devm_kzalloc(dev, sizeof(*ckdev), GFP_KERNEL); |
6af6dc2d SG |
230 | if (!ckdev) |
231 | return -ENOMEM; | |
8f97f8e5 | 232 | err = matrix_keypad_parse_of_params(dev, &ckdev->rows, &ckdev->cols); |
6af6dc2d SG |
233 | if (err) |
234 | return err; | |
017f14e8 | 235 | |
8f97f8e5 | 236 | ckdev->valid_keys = devm_kzalloc(dev, ckdev->cols, GFP_KERNEL); |
017f14e8 TB |
237 | if (!ckdev->valid_keys) |
238 | return -ENOMEM; | |
239 | ||
8f97f8e5 | 240 | ckdev->old_kb_state = devm_kzalloc(dev, ckdev->cols, GFP_KERNEL); |
64757eba DA |
241 | if (!ckdev->old_kb_state) |
242 | return -ENOMEM; | |
6af6dc2d | 243 | |
8f97f8e5 | 244 | idev = devm_input_allocate_device(dev); |
6af6dc2d SG |
245 | if (!idev) |
246 | return -ENOMEM; | |
247 | ||
248 | ckdev->ec = ec; | |
44051a68 | 249 | ckdev->notifier.notifier_call = cros_ec_keyb_work; |
6af6dc2d | 250 | ckdev->dev = dev; |
8f97f8e5 | 251 | dev_set_drvdata(dev, ckdev); |
6af6dc2d | 252 | |
57b33ff0 | 253 | idev->name = CROS_EC_DEV_NAME; |
6af6dc2d SG |
254 | idev->phys = ec->phys_name; |
255 | __set_bit(EV_REP, idev->evbit); | |
256 | ||
257 | idev->id.bustype = BUS_VIRTUAL; | |
258 | idev->id.version = 1; | |
259 | idev->id.product = 0; | |
8f97f8e5 | 260 | idev->dev.parent = dev; |
6af6dc2d SG |
261 | idev->open = cros_ec_keyb_open; |
262 | idev->close = cros_ec_keyb_close; | |
263 | ||
264 | ckdev->ghost_filter = of_property_read_bool(np, | |
265 | "google,needs-ghost-filter"); | |
266 | ||
267 | err = matrix_keypad_build_keymap(NULL, NULL, ckdev->rows, ckdev->cols, | |
268 | NULL, idev); | |
269 | if (err) { | |
270 | dev_err(dev, "cannot build key matrix\n"); | |
271 | return err; | |
272 | } | |
273 | ||
274 | ckdev->row_shift = get_count_order(ckdev->cols); | |
275 | ||
276 | input_set_capability(idev, EV_MSC, MSC_SCAN); | |
277 | input_set_drvdata(idev, ckdev); | |
278 | ckdev->idev = idev; | |
017f14e8 TB |
279 | cros_ec_keyb_compute_valid_keys(ckdev); |
280 | ||
6af6dc2d SG |
281 | err = input_register_device(ckdev->idev); |
282 | if (err) { | |
283 | dev_err(dev, "cannot register input device\n"); | |
284 | return err; | |
285 | } | |
286 | ||
287 | return 0; | |
288 | } | |
289 | ||
3f1fe73b SS |
290 | #ifdef CONFIG_OF |
291 | static const struct of_device_id cros_ec_keyb_of_match[] = { | |
292 | { .compatible = "google,cros-ec-keyb" }, | |
293 | {}, | |
294 | }; | |
295 | MODULE_DEVICE_TABLE(of, cros_ec_keyb_of_match); | |
296 | #endif | |
297 | ||
6af6dc2d SG |
298 | static struct platform_driver cros_ec_keyb_driver = { |
299 | .probe = cros_ec_keyb_probe, | |
300 | .driver = { | |
301 | .name = "cros-ec-keyb", | |
3f1fe73b | 302 | .of_match_table = of_match_ptr(cros_ec_keyb_of_match), |
6af6dc2d SG |
303 | }, |
304 | }; | |
305 | ||
306 | module_platform_driver(cros_ec_keyb_driver); | |
307 | ||
308 | MODULE_LICENSE("GPL"); | |
309 | MODULE_DESCRIPTION("ChromeOS EC keyboard driver"); | |
310 | MODULE_ALIAS("platform:cros-ec-keyb"); |