mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-26 10:49:33 +00:00
Input: omap4-keypad - scan keys in two phases and simplify with bitmask
Because of errata i689 the keyboard can idle with state where no key up interrupts are seen until after the next key press. This means we need to first check for any lost key up events before scanning for new down events. For example, rapidly pressing shift-shift-j can sometimes produce a J instead of j. Let's fix the issue by scanning the keyboard in two phases. First we scan for any key up events that we may have missed, and then we scan for key down events. Let's also simplify things with for_each_set_bit() as suggested by Dmitry Torokhov <dmitry.torokhov@gmail.com>. Signed-off-by: Tony Lindgren <tony@atomide.com> Link: https://lore.kernel.org/r/20210110190529.46135-3-tony@atomide.com Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
This commit is contained in:
parent
7e0541b257
commit
905dbf1d39
1 changed files with 42 additions and 25 deletions
|
@ -78,7 +78,7 @@ struct omap4_keypad {
|
|||
u32 irqreg_offset;
|
||||
unsigned int row_shift;
|
||||
bool no_autorepeat;
|
||||
unsigned char key_state[8];
|
||||
u64 keys;
|
||||
unsigned short *keymap;
|
||||
};
|
||||
|
||||
|
@ -107,6 +107,33 @@ static void kbd_write_irqreg(struct omap4_keypad *keypad_data,
|
|||
keypad_data->base + keypad_data->irqreg_offset + offset);
|
||||
}
|
||||
|
||||
static int omap4_keypad_report_keys(struct omap4_keypad *keypad_data,
|
||||
u64 keys, bool down)
|
||||
{
|
||||
struct input_dev *input_dev = keypad_data->input;
|
||||
unsigned int col, row, code;
|
||||
DECLARE_BITMAP(mask, 64);
|
||||
unsigned long bit;
|
||||
int events = 0;
|
||||
|
||||
bitmap_from_u64(mask, keys);
|
||||
|
||||
for_each_set_bit(bit, mask, keypad_data->rows * BITS_PER_BYTE) {
|
||||
row = bit / BITS_PER_BYTE;
|
||||
col = bit % BITS_PER_BYTE;
|
||||
code = MATRIX_SCAN_CODE(row, col, keypad_data->row_shift);
|
||||
|
||||
input_event(input_dev, EV_MSC, MSC_SCAN, code);
|
||||
input_report_key(input_dev, keypad_data->keymap[code], down);
|
||||
|
||||
events++;
|
||||
}
|
||||
|
||||
if (events)
|
||||
input_sync(input_dev);
|
||||
|
||||
return events;
|
||||
}
|
||||
|
||||
/* Interrupt handlers */
|
||||
static irqreturn_t omap4_keypad_irq_handler(int irq, void *dev_id)
|
||||
|
@ -122,35 +149,25 @@ static irqreturn_t omap4_keypad_irq_handler(int irq, void *dev_id)
|
|||
static irqreturn_t omap4_keypad_irq_thread_fn(int irq, void *dev_id)
|
||||
{
|
||||
struct omap4_keypad *keypad_data = dev_id;
|
||||
struct input_dev *input_dev = keypad_data->input;
|
||||
unsigned char key_state[ARRAY_SIZE(keypad_data->key_state)];
|
||||
unsigned int col, row, code, changed;
|
||||
u32 *new_state = (u32 *) key_state;
|
||||
u32 low, high;
|
||||
u64 keys, changed;
|
||||
|
||||
*new_state = kbd_readl(keypad_data, OMAP4_KBD_FULLCODE31_0);
|
||||
*(new_state + 1) = kbd_readl(keypad_data, OMAP4_KBD_FULLCODE63_32);
|
||||
low = kbd_readl(keypad_data, OMAP4_KBD_FULLCODE31_0);
|
||||
high = kbd_readl(keypad_data, OMAP4_KBD_FULLCODE63_32);
|
||||
keys = low | (u64)high << 32;
|
||||
|
||||
for (row = 0; row < keypad_data->rows; row++) {
|
||||
changed = key_state[row] ^ keypad_data->key_state[row];
|
||||
if (!changed)
|
||||
continue;
|
||||
changed = keys ^ keypad_data->keys;
|
||||
|
||||
for (col = 0; col < keypad_data->cols; col++) {
|
||||
if (changed & (1 << col)) {
|
||||
code = MATRIX_SCAN_CODE(row, col,
|
||||
keypad_data->row_shift);
|
||||
input_event(input_dev, EV_MSC, MSC_SCAN, code);
|
||||
input_report_key(input_dev,
|
||||
keypad_data->keymap[code],
|
||||
key_state[row] & (1 << col));
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Report key up events separately and first. This matters in case we
|
||||
* lost key-up interrupt and just now catching up.
|
||||
*/
|
||||
omap4_keypad_report_keys(keypad_data, changed & ~keys, false);
|
||||
|
||||
input_sync(input_dev);
|
||||
/* Report key down events */
|
||||
omap4_keypad_report_keys(keypad_data, changed & keys, true);
|
||||
|
||||
memcpy(keypad_data->key_state, key_state,
|
||||
sizeof(keypad_data->key_state));
|
||||
keypad_data->keys = keys;
|
||||
|
||||
/* clear pending interrupts */
|
||||
kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS,
|
||||
|
|
Loading…
Reference in a new issue