From 6cd72493ff2235b3f9f560d3f6e58a0068c1ffdd Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Thu, 9 Nov 2023 10:12:59 +0000 Subject: [PATCH] input: gpio_kbd_matrix: add direct access support When the matrix is connected to consecutive pins on the same port, it's possible to read the whole row or set the whole column in a single operation. For the column, this is only possible if the matrix is configured for driving unselected column, as there's no API to configure multiple pins at the same time at the moment. This is more efficient than checking the pins individually, and it's particularly useful if the row or columns are driven from a GPIO port expander. Add some code to detect the condition and enable it automatically as long as the hw configuration supports it. Signed-off-by: Fabio Baltieri --- drivers/input/input_gpio_kbd_matrix.c | 53 +++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/drivers/input/input_gpio_kbd_matrix.c b/drivers/input/input_gpio_kbd_matrix.c index f7310d37b65e58..3d8cc2a3104750 100644 --- a/drivers/input/input_gpio_kbd_matrix.c +++ b/drivers/input/input_gpio_kbd_matrix.c @@ -30,6 +30,8 @@ struct gpio_kbd_matrix_config { struct gpio_kbd_matrix_data { struct input_kbd_matrix_common_data common; uint32_t last_col_state; + bool direct_read; + bool direct_write; }; INPUT_KBD_STRUCT_CHECK(struct gpio_kbd_matrix_config, @@ -50,6 +52,19 @@ static void gpio_kbd_matrix_drive_column(const struct device *dev, int col) state = BIT(col); } + if (data->direct_write) { + const struct gpio_dt_spec *gpio0 = &cfg->col_gpio[0]; + gpio_port_pins_t gpio_mask; + gpio_port_value_t gpio_val; + + gpio_mask = BIT_MASK(common->col_size) << gpio0->pin; + gpio_val = state << gpio0->pin; + + gpio_port_set_masked(gpio0->port, gpio_mask, gpio_val); + + return; + } + for (int i = 0; i < common->col_size; i++) { const struct gpio_dt_spec *gpio = &cfg->col_gpio[i]; @@ -71,8 +86,18 @@ static kbd_row_t gpio_kbd_matrix_read_row(const struct device *dev) { const struct gpio_kbd_matrix_config *cfg = dev->config; const struct input_kbd_matrix_common_config *common = &cfg->common; + struct gpio_kbd_matrix_data *data = dev->data; int val = 0; + if (data->direct_read) { + const struct gpio_dt_spec *gpio0 = &cfg->row_gpio[0]; + gpio_port_value_t gpio_val; + + gpio_port_get(gpio0->port, &gpio_val); + + return (gpio_val >> gpio0->pin) & BIT_MASK(common->row_size); + } + for (int i = 0; i < common->row_size; i++) { const struct gpio_dt_spec *gpio = &cfg->row_gpio[i]; @@ -102,10 +127,27 @@ static void gpio_kbd_matrix_set_detect_mode(const struct device *dev, bool enabl } } +static bool gpio_kbd_matrix_is_gpio_coherent( + const struct gpio_dt_spec *gpio, int gpio_count) +{ + const struct gpio_dt_spec *gpio0 = &gpio[0]; + + for (int i = 1; i < gpio_count; i++) { + if (gpio[i].port != gpio0->port || + gpio[i].dt_flags != gpio0->dt_flags || + gpio[i].pin != gpio0->pin + i) { + return false; + } + } + + return true; +} + static int gpio_kbd_matrix_init(const struct device *dev) { const struct gpio_kbd_matrix_config *cfg = dev->config; const struct input_kbd_matrix_common_config *common = &cfg->common; + struct gpio_kbd_matrix_data *data = dev->data; int ret; int i; @@ -152,6 +194,17 @@ static int gpio_kbd_matrix_init(const struct device *dev) } } + data->direct_read = gpio_kbd_matrix_is_gpio_coherent( + cfg->row_gpio, common->row_size); + + if (cfg->col_drive_inactive) { + data->direct_write = gpio_kbd_matrix_is_gpio_coherent( + cfg->col_gpio, common->col_size); + } + + LOG_DBG("direct_read: %d direct_write: %d", + data->direct_read, data->direct_write); + gpio_kbd_matrix_set_detect_mode(dev, true); return input_kbd_matrix_common_init(dev);