192c27c51SBernie Thompson /* 292c27c51SBernie Thompson * Manage Keyboard Matrices 392c27c51SBernie Thompson * 492c27c51SBernie Thompson * Copyright (c) 2012 The Chromium OS Authors. 592c27c51SBernie Thompson * (C) Copyright 2004 DENX Software Engineering, Wolfgang Denk, wd@denx.de 692c27c51SBernie Thompson * 792c27c51SBernie Thompson * See file CREDITS for list of people who contributed to this 892c27c51SBernie Thompson * project. 992c27c51SBernie Thompson * 1092c27c51SBernie Thompson * This program is free software; you can redistribute it and/or 1192c27c51SBernie Thompson * modify it under the terms of the GNU General Public License as 1292c27c51SBernie Thompson * published by the Free Software Foundation; either version 2 of 1392c27c51SBernie Thompson * the License, or (at your option) any later version. 1492c27c51SBernie Thompson * 1592c27c51SBernie Thompson * This program is distributed in the hope that it will be useful, 1692c27c51SBernie Thompson * but WITHOUT ANY WARRANTY; without even the implied warranty of 1792c27c51SBernie Thompson * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1892c27c51SBernie Thompson * GNU General Public License for more details. 1992c27c51SBernie Thompson * 2092c27c51SBernie Thompson * You should have received a copy of the GNU General Public License 2192c27c51SBernie Thompson * along with this program; if not, write to the Free Software 2292c27c51SBernie Thompson * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 2392c27c51SBernie Thompson * MA 02111-1307 USA 2492c27c51SBernie Thompson */ 2592c27c51SBernie Thompson 26c20dbf64SStephan Linz #include <common.h> 2792c27c51SBernie Thompson #include <fdtdec.h> 2892c27c51SBernie Thompson #include <key_matrix.h> 2992c27c51SBernie Thompson #include <malloc.h> 3092c27c51SBernie Thompson #include <linux/input.h> 3192c27c51SBernie Thompson 3292c27c51SBernie Thompson /** 3392c27c51SBernie Thompson * Determine if the current keypress configuration can cause key ghosting 3492c27c51SBernie Thompson * 3592c27c51SBernie Thompson * We figure this out by seeing if we have two or more keys in the same 3692c27c51SBernie Thompson * column, as well as two or more keys in the same row. 3792c27c51SBernie Thompson * 3892c27c51SBernie Thompson * @param config Keyboard matrix config 3992c27c51SBernie Thompson * @param keys List of keys to check 4092c27c51SBernie Thompson * @param valid Number of valid keypresses to check 4192c27c51SBernie Thompson * @return 0 if no ghosting is possible, 1 if it is 4292c27c51SBernie Thompson */ 4392c27c51SBernie Thompson static int has_ghosting(struct key_matrix *config, struct key_matrix_key *keys, 4492c27c51SBernie Thompson int valid) 4592c27c51SBernie Thompson { 4692c27c51SBernie Thompson int key_in_same_col = 0, key_in_same_row = 0; 4792c27c51SBernie Thompson int i, j; 4892c27c51SBernie Thompson 4971dc6bcaSSimon Glass if (!config->ghost_filter || valid < 3) 5071dc6bcaSSimon Glass return 0; 5171dc6bcaSSimon Glass 5292c27c51SBernie Thompson for (i = 0; i < valid; i++) { 5392c27c51SBernie Thompson /* 5492c27c51SBernie Thompson * Find 2 keys such that one key is in the same row 5592c27c51SBernie Thompson * and the other is in the same column as the i-th key. 5692c27c51SBernie Thompson */ 5792c27c51SBernie Thompson for (j = i + 1; j < valid; j++) { 5892c27c51SBernie Thompson if (keys[j].col == keys[i].col) 5992c27c51SBernie Thompson key_in_same_col = 1; 6092c27c51SBernie Thompson if (keys[j].row == keys[i].row) 6192c27c51SBernie Thompson key_in_same_row = 1; 6292c27c51SBernie Thompson } 6392c27c51SBernie Thompson } 6492c27c51SBernie Thompson 6592c27c51SBernie Thompson if (key_in_same_col && key_in_same_row) 6692c27c51SBernie Thompson return 1; 6792c27c51SBernie Thompson else 6892c27c51SBernie Thompson return 0; 6992c27c51SBernie Thompson } 7092c27c51SBernie Thompson 7192c27c51SBernie Thompson int key_matrix_decode(struct key_matrix *config, struct key_matrix_key keys[], 7292c27c51SBernie Thompson int num_keys, int keycode[], int max_keycodes) 7392c27c51SBernie Thompson { 7492c27c51SBernie Thompson const u8 *keymap; 7592c27c51SBernie Thompson int valid, upto; 7692c27c51SBernie Thompson int pos; 7792c27c51SBernie Thompson 7892c27c51SBernie Thompson debug("%s: num_keys = %d\n", __func__, num_keys); 7992c27c51SBernie Thompson keymap = config->plain_keycode; 8092c27c51SBernie Thompson for (valid = upto = 0; upto < num_keys; upto++) { 8192c27c51SBernie Thompson struct key_matrix_key *key = &keys[upto]; 8292c27c51SBernie Thompson 8392c27c51SBernie Thompson debug(" valid=%d, row=%d, col=%d\n", key->valid, key->row, 8492c27c51SBernie Thompson key->col); 8592c27c51SBernie Thompson if (!key->valid) 8692c27c51SBernie Thompson continue; 8792c27c51SBernie Thompson pos = key->row * config->num_cols + key->col; 8892c27c51SBernie Thompson if (config->fn_keycode && pos == config->fn_pos) 8992c27c51SBernie Thompson keymap = config->fn_keycode; 9092c27c51SBernie Thompson 9192c27c51SBernie Thompson /* Convert the (row, col) values into a keycode */ 9292c27c51SBernie Thompson if (valid < max_keycodes) 9392c27c51SBernie Thompson keycode[valid++] = keymap[pos]; 9492c27c51SBernie Thompson debug(" keycode=%d\n", keymap[pos]); 9592c27c51SBernie Thompson } 9692c27c51SBernie Thompson 9792c27c51SBernie Thompson /* For a ghost key config, ignore the keypresses for this iteration. */ 9871dc6bcaSSimon Glass if (has_ghosting(config, keys, valid)) { 9992c27c51SBernie Thompson valid = 0; 10092c27c51SBernie Thompson debug(" ghosting detected!\n"); 10192c27c51SBernie Thompson } 10292c27c51SBernie Thompson debug(" %d valid keycodes found\n", valid); 10392c27c51SBernie Thompson 10492c27c51SBernie Thompson return valid; 10592c27c51SBernie Thompson } 10692c27c51SBernie Thompson 10792c27c51SBernie Thompson /** 10892c27c51SBernie Thompson * Create a new keycode map from some provided data 10992c27c51SBernie Thompson * 11092c27c51SBernie Thompson * This decodes a keycode map in the format used by the fdt, which is one 11192c27c51SBernie Thompson * word per entry, with the row, col and keycode encoded in that word. 11292c27c51SBernie Thompson * 11392c27c51SBernie Thompson * We create a (row x col) size byte array with each entry containing the 11492c27c51SBernie Thompson * keycode for that (row, col). We also search for map_keycode and return 11592c27c51SBernie Thompson * its position if found (this is used for finding the Fn key). 11692c27c51SBernie Thompson * 11792c27c51SBernie Thompson * @param config Key matrix dimensions structure 11892c27c51SBernie Thompson * @param data Keycode data 11992c27c51SBernie Thompson * @param len Number of entries in keycode table 12092c27c51SBernie Thompson * @param map_keycode Key code to find in the map 12192c27c51SBernie Thompson * @param pos Returns position of map_keycode, if found, else -1 12292c27c51SBernie Thompson * @return map Pointer to allocated map 12392c27c51SBernie Thompson */ 12492c27c51SBernie Thompson static uchar *create_keymap(struct key_matrix *config, u32 *data, int len, 12592c27c51SBernie Thompson int map_keycode, int *pos) 12692c27c51SBernie Thompson { 12792c27c51SBernie Thompson uchar *map; 12892c27c51SBernie Thompson 12992c27c51SBernie Thompson if (pos) 13092c27c51SBernie Thompson *pos = -1; 13192c27c51SBernie Thompson map = (uchar *)calloc(1, config->key_count); 13292c27c51SBernie Thompson if (!map) { 13392c27c51SBernie Thompson debug("%s: failed to malloc %d bytes\n", __func__, 13492c27c51SBernie Thompson config->key_count); 13592c27c51SBernie Thompson return NULL; 13692c27c51SBernie Thompson } 13792c27c51SBernie Thompson 13892c27c51SBernie Thompson for (; len >= sizeof(u32); data++, len -= 4) { 13992c27c51SBernie Thompson u32 tmp = fdt32_to_cpu(*data); 14092c27c51SBernie Thompson int key_code, row, col; 14192c27c51SBernie Thompson int entry; 14292c27c51SBernie Thompson 14392c27c51SBernie Thompson row = (tmp >> 24) & 0xff; 14492c27c51SBernie Thompson col = (tmp >> 16) & 0xff; 14592c27c51SBernie Thompson key_code = tmp & 0xffff; 14692c27c51SBernie Thompson entry = row * config->num_cols + col; 14792c27c51SBernie Thompson map[entry] = key_code; 14814813f19SSimon Glass debug(" map %d, %d: pos=%d, keycode=%d\n", row, col, 14914813f19SSimon Glass entry, key_code); 15092c27c51SBernie Thompson if (pos && map_keycode == key_code) 15192c27c51SBernie Thompson *pos = entry; 15292c27c51SBernie Thompson } 15392c27c51SBernie Thompson 15492c27c51SBernie Thompson return map; 15592c27c51SBernie Thompson } 15692c27c51SBernie Thompson 157*df637fa6SStephen Warren int key_matrix_decode_fdt(struct key_matrix *config, const void *blob, int node) 15892c27c51SBernie Thompson { 15992c27c51SBernie Thompson const struct fdt_property *prop; 160*df637fa6SStephen Warren int proplen; 161*df637fa6SStephen Warren uchar *plain_keycode; 16292c27c51SBernie Thompson 163*df637fa6SStephen Warren prop = fdt_get_property(blob, node, "linux,keymap", &proplen); 164*df637fa6SStephen Warren /* Basic keymap is required */ 165*df637fa6SStephen Warren if (!prop) 166*df637fa6SStephen Warren return -1; 16792c27c51SBernie Thompson 168*df637fa6SStephen Warren plain_keycode = create_keymap(config, (u32 *)prop->data, 169*df637fa6SStephen Warren proplen, KEY_FN, &config->fn_pos); 170*df637fa6SStephen Warren config->plain_keycode = plain_keycode; 171*df637fa6SStephen Warren /* Conversion error -> fail */ 172*df637fa6SStephen Warren if (!config->plain_keycode) 173*df637fa6SStephen Warren return -1; 17492c27c51SBernie Thompson 175*df637fa6SStephen Warren prop = fdt_get_property(blob, node, "linux,fn-keymap", &proplen); 176*df637fa6SStephen Warren /* fn keymap is optional */ 177*df637fa6SStephen Warren if (!prop) 178*df637fa6SStephen Warren goto done; 17992c27c51SBernie Thompson 180*df637fa6SStephen Warren config->fn_keycode = create_keymap(config, (u32 *)prop->data, 181*df637fa6SStephen Warren proplen, -1, NULL); 182*df637fa6SStephen Warren /* Conversion error -> fail */ 18392c27c51SBernie Thompson if (!config->plain_keycode) { 184*df637fa6SStephen Warren free(plain_keycode); 18592c27c51SBernie Thompson return -1; 18692c27c51SBernie Thompson } 18792c27c51SBernie Thompson 188*df637fa6SStephen Warren done: 189*df637fa6SStephen Warren debug("%s: Decoded key maps %p, %p from fdt\n", __func__, 190*df637fa6SStephen Warren config->plain_keycode, config->fn_keycode); 19192c27c51SBernie Thompson return 0; 19292c27c51SBernie Thompson } 19392c27c51SBernie Thompson 19471dc6bcaSSimon Glass int key_matrix_init(struct key_matrix *config, int rows, int cols, 19571dc6bcaSSimon Glass int ghost_filter) 19692c27c51SBernie Thompson { 19792c27c51SBernie Thompson memset(config, '\0', sizeof(*config)); 19892c27c51SBernie Thompson config->num_rows = rows; 19992c27c51SBernie Thompson config->num_cols = cols; 20092c27c51SBernie Thompson config->key_count = rows * cols; 20171dc6bcaSSimon Glass config->ghost_filter = ghost_filter; 20292c27c51SBernie Thompson assert(config->key_count > 0); 20392c27c51SBernie Thompson 20492c27c51SBernie Thompson return 0; 20592c27c51SBernie Thompson } 206