.. _gpio-kbd:

GPIO Keyboard Matrix
####################

The :dtcompatible:`gpio-kbd-matrix` driver supports a large variety of keyboard
matrix hardware configurations and has numerous options to change its behavior.
This is an overview of some common setups and how they can be supported by the
driver.

The conventional configuration for all of these is that the driver reads on the
row GPIOs (inputs) and selects on the columns GPIOs (output).

Base use case, no isolation diodes, interrupt capable GPIOs
***********************************************************

This is the common configuration found on consumer keyboards with membrane
switches and flexible circuit boards, no isolation diodes, requires ghosting
detection (which is enabled by default).

.. figure:: no-diodes.svg
      :align: center
      :width: 50%

      A 3x3 matrix, no diodes

The system must support GPIO interrupts, and the interrupt can be enabled on all
row GPIOs at the same time.

.. code-block:: devicetree

   kbd-matrix {
        compatible = "gpio-kbd-matrix";
        row-gpios = <&gpio0 0 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>,
                    <&gpio0 1 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>,
                    <&gpio0 2 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
        col-gpios = <&gpio0 3 GPIO_ACTIVE_LOW>,
                    <&gpio0 4 GPIO_ACTIVE_LOW>,
                    <&gpio0 5 GPIO_ACTIVE_LOW>;
   };

In this configuration the matrix scanning library enters idle mode once all
keys are released, and the keyboard matrix thread only wakes up when a key has
been pressed.

GPIOs for columns that are not currently selected are configured in high
impedance mode. This means that the row state may need some time to settle to
avoid misreading the key state from a column to the following one. The settle
time can be tweaked by changing the ``settle-time-us`` property.

Isolation diodes
****************

If the matrix has isolation diodes for every key, then it's possible to:

 - disable ghosting detection, allowing any key combination to be detected
 - configuring the driver to drive unselected columns GPIO to inactive state
   rather than high impedance, this allows to reduce the settle time
   (potentially down to 0), and use the more efficient port wide GPIO read APIs
   (happens automatically if the GPIO pins are sequential)

Matrixes with diodes going from rows to columns must use pull-ups on rows and
active low columns.

.. figure:: diodes-rc.svg
      :align: center
      :width: 50%

      A 3x3 matrix with row to column isolation diodes.

.. code-block:: devicetree

   kbd-matrix {
        compatible = "gpio-kbd-matrix";
        row-gpios = <&gpio0 0 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>,
                    <&gpio0 1 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>,
                    <&gpio0 2 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
        col-gpios = <&gpio0 3 GPIO_ACTIVE_LOW>,
                    <&gpio0 4 GPIO_ACTIVE_LOW>,
                    <&gpio0 5 GPIO_ACTIVE_LOW>;
        col-drive-inactive;
        settle-time-us = <0>;
        no-ghostkey-check;
   };

Matrixes with diodes going from columns to rows must use pull-downs on rows and
active high columns.

.. figure:: diodes-cr.svg
      :align: center
      :width: 50%

      A 3x3 matrix with column to row isolation diodes.

.. code-block:: devicetree

   kbd-matrix {
        compatible = "gpio-kbd-matrix";
        row-gpios = <&gpio0 0 (GPIO_PULL_DOWN | GPIO_ACTIVE_HIGH)>,
                    <&gpio0 1 (GPIO_PULL_DOWN | GPIO_ACTIVE_HIGH)>,
                    <&gpio0 2 (GPIO_PULL_DOWN | GPIO_ACTIVE_HIGH)>;
        col-gpios = <&gpio0 3 GPIO_ACTIVE_HIGH>,
                    <&gpio0 4 GPIO_ACTIVE_HIGH>,
                    <&gpio0 5 GPIO_ACTIVE_HIGH>;
        col-drive-inactive;
        settle-time-us = <0>;
        no-ghostkey-check;
   };

GPIO with no interrupt support
******************************

Some GPIO controllers have limitations on GPIO interrupts, and may not support
enabling interrupts on all row GPIOs at the same time.

In this case, the driver can be configured to not use interrupt at all, and
instead idle by selecting all columns and keep polling on the row GPIOs, which
is a single GPIO API operation if the pins are sequential.

This configuration can be enabled by setting the ``idle-mode`` property to
``poll``:

.. code-block:: devicetree

   kbd-matrix {
        compatible = "gpio-kbd-matrix";
        ...
        idle-mode = "poll";
   };

GPIO multiplexer
****************

In more extreme cases, such as if the columns are using a multiplexer and it's
impossible to select all of them at the same time, the driver can be configured
to scan continuously.

This can be done by setting ``idle-mode`` to ``scan`` and ``poll-timeout-ms``
to ``0``.

.. code-block:: devicetree

   kbd-matrix {
        compatible = "gpio-kbd-matrix";
        ...
        poll-timeout-ms = <0>;
        idle-mode = "scan";
   };

Row and column GPIO selection
*****************************

If the row GPIOs are sequential and on the same gpio controller, the driver
automatically switches API to read from the whole GPIO port rather than the
individual pins. This is particularly useful if the GPIOs are not memory
mapped, for example on an I2C or SPI port expander, as this significantly
reduces the number of transactions on the corresponding bus.

The same is true for column GPIOs, but only if the matrix is configured for
``col-drive-inactive``, so that is only usable for matrixes with isolation
diodes.

16-bit row support
******************

The driver uses an 8-bit datatype to store the row state by default, which
limits the matrix row size to 8. This can be increased to 16 by enabling the
:kconfig:option:`CONFIG_INPUT_KBD_MATRIX_16_BIT_ROW` option.

Actual key mask configuration
*****************************

If the key matrix is not complete, a map of the keys that are actually
populated can be specified using the ``actual-key-mask`` property. This allows
the matrix state to be filtered to remove keys that are not present before
ghosting detection, potentially allowing key combinations that would otherwise
be blocked by it.

For example for a 3x3 matrix missing a key:

.. figure:: no-sw4.svg
      :align: center
      :width: 50%

      A 3x3 matrix missing a key.

.. code-block:: devicetree

   kbd-matrix {
        compatible = "gpio-kbd-matrix";
        ...
        actual-key-mask = <0x07 0x05 0x07>;
   };

This would allow, for example, to detect pressing ``Sw1``, ``SW2`` and  ``SW4``
at the same time without triggering anti ghosting.

The actual key mask can be changed at runtime by enabling
:kconfig:option:`CONFIG_INPUT_KBD_ACTUAL_KEY_MASK_DYNAMIC` and the using the
:c:func:`input_kbd_matrix_actual_key_mask_set` API.

Keymap configuration
********************

Keyboard matrix devices report a series of x/y/touch events. These can be
mapped to normal key events using the :dtcompatible:`input-keymap` driver.

For example, the following would setup a ``keymap`` device that take the
x/y/touch events as an input and generate corresponding key events as an
output:

.. code-block:: devicetree

  kbd {
      ...
      keymap {
          compatible = "input-keymap";
          keymap = <
              MATRIX_KEY(0, 0, INPUT_KEY_1)
              MATRIX_KEY(0, 1, INPUT_KEY_2)
              MATRIX_KEY(0, 2, INPUT_KEY_3)
              MATRIX_KEY(1, 0, INPUT_KEY_4)
              MATRIX_KEY(1, 1, INPUT_KEY_5)
              MATRIX_KEY(1, 2, INPUT_KEY_6)
              MATRIX_KEY(2, 0, INPUT_KEY_7)
              MATRIX_KEY(2, 1, INPUT_KEY_8)
              MATRIX_KEY(2, 2, INPUT_KEY_9)
          >;
          row-size = <3>;
          col-size = <3>;
      };
  };

.. doxygengroup:: input_keymap

Keyboard matrix shell commands
******************************

The shell command ``kbd_matrix_state_dump`` can be used to test the
functionality of any keyboard matrix driver implemented using the keyboard
matrix library. Once enabled it logs the state of the matrix every time it
changes, and once disabled it prints an or-mask of any key that has been
detected, which can be used to set the ``actual-key-mask`` property.

The command can be enabled using the
:kconfig:option:`CONFIG_INPUT_SHELL_KBD_MATRIX_STATE`.

Example usage:

.. code-block:: console

   uart:~$ device list
   devices:
   - kbd-matrix (READY)
   uart:~$ input kbd_matrix_state_dump kbd-matrix
   Keyboard state logging enabled for kbd-matrix
   [00:01:41.678,466] <inf> input: kbd-matrix state [01 -- -- --] (1)
   [00:01:41.784,912] <inf> input: kbd-matrix state [-- -- -- --] (0)
   ...
   press more buttons
   ...
   uart:~$ input kbd_matrix_state_dump off
   Keyboard state logging disabled
   [00:01:47.967,651] <inf> input: kbd-matrix key-mask [07 05 07 --] (8)

Keyboard matrix library
***********************

The GPIO keyboard matrix driver is based on a generic keyboard matrix library,
which implements the core functionalities such as scanning delays, debouncing,
idle mode etc. This can be reused to implement other keyboard matrix drivers,
potentially application specific.

.. doxygengroup:: input_kbd_matrix
