summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/input/cros_ec_keyb.c34
1 files changed, 30 insertions, 4 deletions
diff --git a/drivers/input/cros_ec_keyb.c b/drivers/input/cros_ec_keyb.c
index e8dac23..a2501e0 100644
--- a/drivers/input/cros_ec_keyb.c
+++ b/drivers/input/cros_ec_keyb.c
@@ -8,6 +8,7 @@
#include <common.h>
#include <cros_ec.h>
+#include <errno.h>
#include <fdtdec.h>
#include <input.h>
#include <key_matrix.h>
@@ -39,20 +40,34 @@ static struct keyb {
* @param config Keyboard config
* @param keys List of keys that we have detected
* @param max_count Maximum number of keys to return
- * @return number of pressed keys, 0 for none
+ * @param samep Set to true if this scan repeats the last, else false
+ * @return number of pressed keys, 0 for none, -EIO on error
*/
static int check_for_keys(struct keyb *config,
- struct key_matrix_key *keys, int max_count)
+ struct key_matrix_key *keys, int max_count,
+ bool *samep)
{
struct key_matrix_key *key;
+ static struct mbkp_keyscan last_scan;
+ static bool last_scan_valid;
struct mbkp_keyscan scan;
unsigned int row, col, bit, data;
int num_keys;
if (cros_ec_scan_keyboard(config->dev, &scan)) {
debug("%s: keyboard scan failed\n", __func__);
- return -1;
+ return -EIO;
}
+ *samep = last_scan_valid && !memcmp(&last_scan, &scan, sizeof(scan));
+
+ /*
+ * This is a bit odd. The EC has no way to tell us that it has run
+ * out of key scans. It just returns the same scan over and over
+ * again. So the only way to detect that we have run out is to detect
+ * that this scan is the same as the last.
+ */
+ last_scan_valid = true;
+ memcpy(&last_scan, &scan, sizeof(last_scan));
for (col = num_keys = bit = 0; col < config->matrix.num_cols;
col++) {
@@ -112,6 +127,7 @@ int cros_ec_kbc_check(struct input_config *input)
int keycodes[KBC_MAX_KEYS];
int num_keys, num_keycodes;
int irq_pending, sent;
+ bool same = false;
/*
* Loop until the EC has no more keyscan records, or we have
@@ -125,7 +141,10 @@ int cros_ec_kbc_check(struct input_config *input)
do {
irq_pending = cros_ec_interrupt_pending(config.dev);
if (irq_pending) {
- num_keys = check_for_keys(&config, keys, KBC_MAX_KEYS);
+ num_keys = check_for_keys(&config, keys, KBC_MAX_KEYS,
+ &same);
+ if (num_keys < 0)
+ return 0;
last_num_keys = num_keys;
memcpy(last_keys, keys, sizeof(keys));
} else {
@@ -142,6 +161,13 @@ int cros_ec_kbc_check(struct input_config *input)
num_keycodes = key_matrix_decode(&config.matrix, keys,
num_keys, keycodes, KBC_MAX_KEYS);
sent = input_send_keycodes(input, keycodes, num_keycodes);
+
+ /*
+ * For those ECs without an interrupt, stop scanning when we
+ * see that the scan is the same as last time.
+ */
+ if ((irq_pending < 0) && same)
+ break;
} while (irq_pending && !sent);
return 1;