Re: [PATCH 2/5] Input: omap4-keypad - scan keys in two phases and simplify with bitmask

From: Tony Lindgren
Date: Mon Jan 11 2021 - 00:58:41 EST


* Dmitry Torokhov <dmitry.torokhov@xxxxxxxxx> [210111 04:48]:
> Technically speaking, userspace is free to accumulate the events until
> it receives EV_SYN/SYN_REPORT event and process the events in the event
> packet in order it sees fit. So to achieve what you want, I think we
> should issue 2 input_sync()s, one for the release block, and another is
> for press. I think we can also simplify the code if we pass into the new
> scan function exact set of keys that are being released or pressed.
>
> How about the version below?

Yes that works nicely.

Thanks,

Tony


>
> Input: omap4-keypad - scan keys in two phases and simplify with bitmask
>
> From: Tony Lindgren <tony@xxxxxxxxxxx>
>
> 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@xxxxxxxxx>.
>
> Signed-off-by: Tony Lindgren <tony@xxxxxxxxxxx>
> Signed-off-by: Dmitry Torokhov <dmitry.torokhov@xxxxxxxxx>
> ---
> drivers/input/keyboard/omap4-keypad.c | 73 ++++++++++++++++++++-------------
> 1 file changed, 45 insertions(+), 28 deletions(-)
>
> diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c
> index ab761aa66b6d..6dcf27af856d 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,