diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c index ab761aa66b6d33d1ea8753646e8dea679369b17f..6dcf27af856d944c7eb28c8c568303e06d07d859 100644 --- a/drivers/input/keyboard/omap4-keypad.c +++ b/drivers/input/keyboard/omap4-keypad.c @@ -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; - - *new_state = kbd_readl(keypad_data, OMAP4_KBD_FULLCODE31_0); - *(new_state + 1) = kbd_readl(keypad_data, OMAP4_KBD_FULLCODE63_32); - - for (row = 0; row < keypad_data->rows; row++) { - changed = key_state[row] ^ keypad_data->key_state[row]; - if (!changed) - continue; - - 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)); - } - } - } + u32 low, high; + u64 keys, changed; + + low = kbd_readl(keypad_data, OMAP4_KBD_FULLCODE31_0); + high = kbd_readl(keypad_data, OMAP4_KBD_FULLCODE63_32); + keys = low | (u64)high << 32; + + changed = keys ^ keypad_data->keys; + + /* + * 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,