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 * 7*1a459660SWolfgang Denk * SPDX-License-Identifier: GPL-2.0+ 892c27c51SBernie Thompson */ 992c27c51SBernie Thompson 10c20dbf64SStephan Linz #include <common.h> 1192c27c51SBernie Thompson #include <fdtdec.h> 1292c27c51SBernie Thompson #include <key_matrix.h> 1392c27c51SBernie Thompson #include <malloc.h> 1492c27c51SBernie Thompson #include <linux/input.h> 1592c27c51SBernie Thompson 1692c27c51SBernie Thompson /** 1792c27c51SBernie Thompson * Determine if the current keypress configuration can cause key ghosting 1892c27c51SBernie Thompson * 1992c27c51SBernie Thompson * We figure this out by seeing if we have two or more keys in the same 2092c27c51SBernie Thompson * column, as well as two or more keys in the same row. 2192c27c51SBernie Thompson * 2292c27c51SBernie Thompson * @param config Keyboard matrix config 2392c27c51SBernie Thompson * @param keys List of keys to check 2492c27c51SBernie Thompson * @param valid Number of valid keypresses to check 2592c27c51SBernie Thompson * @return 0 if no ghosting is possible, 1 if it is 2692c27c51SBernie Thompson */ 2792c27c51SBernie Thompson static int has_ghosting(struct key_matrix *config, struct key_matrix_key *keys, 2892c27c51SBernie Thompson int valid) 2992c27c51SBernie Thompson { 3092c27c51SBernie Thompson int key_in_same_col = 0, key_in_same_row = 0; 3192c27c51SBernie Thompson int i, j; 3292c27c51SBernie Thompson 3371dc6bcaSSimon Glass if (!config->ghost_filter || valid < 3) 3471dc6bcaSSimon Glass return 0; 3571dc6bcaSSimon Glass 3692c27c51SBernie Thompson for (i = 0; i < valid; i++) { 3792c27c51SBernie Thompson /* 3892c27c51SBernie Thompson * Find 2 keys such that one key is in the same row 3992c27c51SBernie Thompson * and the other is in the same column as the i-th key. 4092c27c51SBernie Thompson */ 4192c27c51SBernie Thompson for (j = i + 1; j < valid; j++) { 4292c27c51SBernie Thompson if (keys[j].col == keys[i].col) 4392c27c51SBernie Thompson key_in_same_col = 1; 4492c27c51SBernie Thompson if (keys[j].row == keys[i].row) 4592c27c51SBernie Thompson key_in_same_row = 1; 4692c27c51SBernie Thompson } 4792c27c51SBernie Thompson } 4892c27c51SBernie Thompson 4992c27c51SBernie Thompson if (key_in_same_col && key_in_same_row) 5092c27c51SBernie Thompson return 1; 5192c27c51SBernie Thompson else 5292c27c51SBernie Thompson return 0; 5392c27c51SBernie Thompson } 5492c27c51SBernie Thompson 5592c27c51SBernie Thompson int key_matrix_decode(struct key_matrix *config, struct key_matrix_key keys[], 5692c27c51SBernie Thompson int num_keys, int keycode[], int max_keycodes) 5792c27c51SBernie Thompson { 5892c27c51SBernie Thompson const u8 *keymap; 5992c27c51SBernie Thompson int valid, upto; 6092c27c51SBernie Thompson int pos; 6192c27c51SBernie Thompson 6292c27c51SBernie Thompson debug("%s: num_keys = %d\n", __func__, num_keys); 6392c27c51SBernie Thompson keymap = config->plain_keycode; 6492c27c51SBernie Thompson for (valid = upto = 0; upto < num_keys; upto++) { 6592c27c51SBernie Thompson struct key_matrix_key *key = &keys[upto]; 6692c27c51SBernie Thompson 6792c27c51SBernie Thompson debug(" valid=%d, row=%d, col=%d\n", key->valid, key->row, 6892c27c51SBernie Thompson key->col); 6992c27c51SBernie Thompson if (!key->valid) 7092c27c51SBernie Thompson continue; 7192c27c51SBernie Thompson pos = key->row * config->num_cols + key->col; 7292c27c51SBernie Thompson if (config->fn_keycode && pos == config->fn_pos) 7392c27c51SBernie Thompson keymap = config->fn_keycode; 7492c27c51SBernie Thompson 7592c27c51SBernie Thompson /* Convert the (row, col) values into a keycode */ 7692c27c51SBernie Thompson if (valid < max_keycodes) 7792c27c51SBernie Thompson keycode[valid++] = keymap[pos]; 7892c27c51SBernie Thompson debug(" keycode=%d\n", keymap[pos]); 7992c27c51SBernie Thompson } 8092c27c51SBernie Thompson 8192c27c51SBernie Thompson /* For a ghost key config, ignore the keypresses for this iteration. */ 8271dc6bcaSSimon Glass if (has_ghosting(config, keys, valid)) { 8392c27c51SBernie Thompson valid = 0; 8492c27c51SBernie Thompson debug(" ghosting detected!\n"); 8592c27c51SBernie Thompson } 8692c27c51SBernie Thompson debug(" %d valid keycodes found\n", valid); 8792c27c51SBernie Thompson 8892c27c51SBernie Thompson return valid; 8992c27c51SBernie Thompson } 9092c27c51SBernie Thompson 9192c27c51SBernie Thompson /** 9292c27c51SBernie Thompson * Create a new keycode map from some provided data 9392c27c51SBernie Thompson * 9492c27c51SBernie Thompson * This decodes a keycode map in the format used by the fdt, which is one 9592c27c51SBernie Thompson * word per entry, with the row, col and keycode encoded in that word. 9692c27c51SBernie Thompson * 9792c27c51SBernie Thompson * We create a (row x col) size byte array with each entry containing the 9892c27c51SBernie Thompson * keycode for that (row, col). We also search for map_keycode and return 9992c27c51SBernie Thompson * its position if found (this is used for finding the Fn key). 10092c27c51SBernie Thompson * 10192c27c51SBernie Thompson * @param config Key matrix dimensions structure 10292c27c51SBernie Thompson * @param data Keycode data 10392c27c51SBernie Thompson * @param len Number of entries in keycode table 10492c27c51SBernie Thompson * @param map_keycode Key code to find in the map 10592c27c51SBernie Thompson * @param pos Returns position of map_keycode, if found, else -1 10692c27c51SBernie Thompson * @return map Pointer to allocated map 10792c27c51SBernie Thompson */ 10892c27c51SBernie Thompson static uchar *create_keymap(struct key_matrix *config, u32 *data, int len, 10992c27c51SBernie Thompson int map_keycode, int *pos) 11092c27c51SBernie Thompson { 11192c27c51SBernie Thompson uchar *map; 11292c27c51SBernie Thompson 11392c27c51SBernie Thompson if (pos) 11492c27c51SBernie Thompson *pos = -1; 11592c27c51SBernie Thompson map = (uchar *)calloc(1, config->key_count); 11692c27c51SBernie Thompson if (!map) { 11792c27c51SBernie Thompson debug("%s: failed to malloc %d bytes\n", __func__, 11892c27c51SBernie Thompson config->key_count); 11992c27c51SBernie Thompson return NULL; 12092c27c51SBernie Thompson } 12192c27c51SBernie Thompson 12292c27c51SBernie Thompson for (; len >= sizeof(u32); data++, len -= 4) { 12392c27c51SBernie Thompson u32 tmp = fdt32_to_cpu(*data); 12492c27c51SBernie Thompson int key_code, row, col; 12592c27c51SBernie Thompson int entry; 12692c27c51SBernie Thompson 12792c27c51SBernie Thompson row = (tmp >> 24) & 0xff; 12892c27c51SBernie Thompson col = (tmp >> 16) & 0xff; 12992c27c51SBernie Thompson key_code = tmp & 0xffff; 13092c27c51SBernie Thompson entry = row * config->num_cols + col; 13192c27c51SBernie Thompson map[entry] = key_code; 13214813f19SSimon Glass debug(" map %d, %d: pos=%d, keycode=%d\n", row, col, 13314813f19SSimon Glass entry, key_code); 13492c27c51SBernie Thompson if (pos && map_keycode == key_code) 13592c27c51SBernie Thompson *pos = entry; 13692c27c51SBernie Thompson } 13792c27c51SBernie Thompson 13892c27c51SBernie Thompson return map; 13992c27c51SBernie Thompson } 14092c27c51SBernie Thompson 141df637fa6SStephen Warren int key_matrix_decode_fdt(struct key_matrix *config, const void *blob, int node) 14292c27c51SBernie Thompson { 14392c27c51SBernie Thompson const struct fdt_property *prop; 144df637fa6SStephen Warren int proplen; 145df637fa6SStephen Warren uchar *plain_keycode; 14692c27c51SBernie Thompson 147df637fa6SStephen Warren prop = fdt_get_property(blob, node, "linux,keymap", &proplen); 148df637fa6SStephen Warren /* Basic keymap is required */ 149374e8370SStephen Warren if (!prop) { 150374e8370SStephen Warren debug("%s: cannot find keycode-plain map\n", __func__); 151df637fa6SStephen Warren return -1; 152374e8370SStephen Warren } 15392c27c51SBernie Thompson 154df637fa6SStephen Warren plain_keycode = create_keymap(config, (u32 *)prop->data, 155df637fa6SStephen Warren proplen, KEY_FN, &config->fn_pos); 156df637fa6SStephen Warren config->plain_keycode = plain_keycode; 157df637fa6SStephen Warren /* Conversion error -> fail */ 158df637fa6SStephen Warren if (!config->plain_keycode) 159df637fa6SStephen Warren return -1; 16092c27c51SBernie Thompson 161df637fa6SStephen Warren prop = fdt_get_property(blob, node, "linux,fn-keymap", &proplen); 162df637fa6SStephen Warren /* fn keymap is optional */ 163df637fa6SStephen Warren if (!prop) 164df637fa6SStephen Warren goto done; 16592c27c51SBernie Thompson 166df637fa6SStephen Warren config->fn_keycode = create_keymap(config, (u32 *)prop->data, 167df637fa6SStephen Warren proplen, -1, NULL); 168df637fa6SStephen Warren /* Conversion error -> fail */ 169374e8370SStephen Warren if (!config->fn_keycode) { 170df637fa6SStephen Warren free(plain_keycode); 17192c27c51SBernie Thompson return -1; 17292c27c51SBernie Thompson } 17392c27c51SBernie Thompson 174df637fa6SStephen Warren done: 175df637fa6SStephen Warren debug("%s: Decoded key maps %p, %p from fdt\n", __func__, 176df637fa6SStephen Warren config->plain_keycode, config->fn_keycode); 17792c27c51SBernie Thompson return 0; 17892c27c51SBernie Thompson } 17992c27c51SBernie Thompson 18071dc6bcaSSimon Glass int key_matrix_init(struct key_matrix *config, int rows, int cols, 18171dc6bcaSSimon Glass int ghost_filter) 18292c27c51SBernie Thompson { 18392c27c51SBernie Thompson memset(config, '\0', sizeof(*config)); 18492c27c51SBernie Thompson config->num_rows = rows; 18592c27c51SBernie Thompson config->num_cols = cols; 18692c27c51SBernie Thompson config->key_count = rows * cols; 18771dc6bcaSSimon Glass config->ghost_filter = ghost_filter; 18892c27c51SBernie Thompson assert(config->key_count > 0); 18992c27c51SBernie Thompson 19092c27c51SBernie Thompson return 0; 19192c27c51SBernie Thompson } 192