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 49*71dc6bcaSSimon Glass if (!config->ghost_filter || valid < 3) 50*71dc6bcaSSimon Glass return 0; 51*71dc6bcaSSimon 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. */ 98*71dc6bcaSSimon 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; 14892c27c51SBernie Thompson if (pos && map_keycode == key_code) 14992c27c51SBernie Thompson *pos = entry; 15092c27c51SBernie Thompson } 15192c27c51SBernie Thompson 15292c27c51SBernie Thompson return map; 15392c27c51SBernie Thompson } 15492c27c51SBernie Thompson 15592c27c51SBernie Thompson int key_matrix_decode_fdt(struct key_matrix *config, const void *blob, 15692c27c51SBernie Thompson int node) 15792c27c51SBernie Thompson { 15892c27c51SBernie Thompson const struct fdt_property *prop; 15900f1099eSSimon Glass const char prefix[] = "linux,"; 16000f1099eSSimon Glass int plen = sizeof(prefix) - 1; 16192c27c51SBernie Thompson int offset; 16292c27c51SBernie Thompson 16392c27c51SBernie Thompson /* Check each property name for ones that we understand */ 16492c27c51SBernie Thompson for (offset = fdt_first_property_offset(blob, node); 16592c27c51SBernie Thompson offset > 0; 16692c27c51SBernie Thompson offset = fdt_next_property_offset(blob, offset)) { 16792c27c51SBernie Thompson const char *name; 16892c27c51SBernie Thompson int len; 16992c27c51SBernie Thompson 17092c27c51SBernie Thompson prop = fdt_get_property_by_offset(blob, offset, NULL); 17192c27c51SBernie Thompson name = fdt_string(blob, fdt32_to_cpu(prop->nameoff)); 17292c27c51SBernie Thompson len = strlen(name); 17392c27c51SBernie Thompson 17492c27c51SBernie Thompson /* Name needs to match "1,<type>keymap" */ 17592c27c51SBernie Thompson debug("%s: property '%s'\n", __func__, name); 17600f1099eSSimon Glass if (strncmp(name, prefix, plen) || 17700f1099eSSimon Glass len < plen + 6 || 17892c27c51SBernie Thompson strcmp(name + len - 6, "keymap")) 17992c27c51SBernie Thompson continue; 18092c27c51SBernie Thompson 18100f1099eSSimon Glass len -= plen + 6; 18292c27c51SBernie Thompson if (len == 0) { 18392c27c51SBernie Thompson config->plain_keycode = create_keymap(config, 18492c27c51SBernie Thompson (u32 *)prop->data, fdt32_to_cpu(prop->len), 18592c27c51SBernie Thompson KEY_FN, &config->fn_pos); 18600f1099eSSimon Glass } else if (0 == strncmp(name + plen, "fn-", len)) { 18792c27c51SBernie Thompson config->fn_keycode = create_keymap(config, 18892c27c51SBernie Thompson (u32 *)prop->data, fdt32_to_cpu(prop->len), 18992c27c51SBernie Thompson -1, NULL); 19092c27c51SBernie Thompson } else { 19192c27c51SBernie Thompson debug("%s: unrecognised property '%s'\n", __func__, 19292c27c51SBernie Thompson name); 19392c27c51SBernie Thompson } 19492c27c51SBernie Thompson } 19592c27c51SBernie Thompson debug("%s: Decoded key maps %p, %p from fdt\n", __func__, 19692c27c51SBernie Thompson config->plain_keycode, config->fn_keycode); 19792c27c51SBernie Thompson 19892c27c51SBernie Thompson if (!config->plain_keycode) { 19992c27c51SBernie Thompson debug("%s: cannot find keycode-plain map\n", __func__); 20092c27c51SBernie Thompson return -1; 20192c27c51SBernie Thompson } 20292c27c51SBernie Thompson 20392c27c51SBernie Thompson return 0; 20492c27c51SBernie Thompson } 20592c27c51SBernie Thompson 206*71dc6bcaSSimon Glass int key_matrix_init(struct key_matrix *config, int rows, int cols, 207*71dc6bcaSSimon Glass int ghost_filter) 20892c27c51SBernie Thompson { 20992c27c51SBernie Thompson memset(config, '\0', sizeof(*config)); 21092c27c51SBernie Thompson config->num_rows = rows; 21192c27c51SBernie Thompson config->num_cols = cols; 21292c27c51SBernie Thompson config->key_count = rows * cols; 213*71dc6bcaSSimon Glass config->ghost_filter = ghost_filter; 21492c27c51SBernie Thompson assert(config->key_count > 0); 21592c27c51SBernie Thompson 21692c27c51SBernie Thompson return 0; 21792c27c51SBernie Thompson } 218