input: gpio_kbd_matrix: add poll and scan mode support

Add a poll and scan mode for the driver. If any of these are set, the
driver does not use the GPIO interrupts to detect when the matrix has to
switch to polling mode. Instead, it keeps polling it all the time,
either by enabling all the columns and poll the rows for activity, or
just keep scanning all the time.

Poll mode is useful if the specific SoC used does not support GPIO
interrupt on all the row GPIOs at the same time, scan mode if it does
not even support selecting all the columns at the same time.

Signed-off-by: Fabio Baltieri <fabiobaltieri@google.com>
This commit is contained in:
Fabio Baltieri 2023-11-17 09:59:31 +00:00 committed by Fabio Baltieri
commit 3862c227d4
2 changed files with 104 additions and 13 deletions

View file

@ -22,8 +22,13 @@ struct gpio_kbd_matrix_config {
struct input_kbd_matrix_common_config common;
const struct gpio_dt_spec *row_gpio;
const struct gpio_dt_spec *col_gpio;
struct gpio_callback *gpio_cb;
gpio_callback_handler_t handler;
gpio_callback_handler_t gpio_cb_handler;
struct k_work_delayable *idle_poll_dwork;
k_work_handler_t idle_poll_handler;
bool col_drive_inactive;
};
@ -109,6 +114,20 @@ static kbd_row_t gpio_kbd_matrix_read_row(const struct device *dev)
return val;
}
static __maybe_unused void gpio_kbd_matrix_idle_poll_handler(const struct device *dev)
{
const struct gpio_kbd_matrix_config *cfg = dev->config;
const struct input_kbd_matrix_common_config *common = &cfg->common;
if (gpio_kbd_matrix_read_row(dev) == 0) {
k_work_reschedule(cfg->idle_poll_dwork,
K_USEC(common->poll_period_us));
return;
}
input_kbd_matrix_poll_start(dev);
}
static void gpio_kbd_matrix_set_detect_mode(const struct device *dev, bool enabled)
{
const struct gpio_kbd_matrix_config *cfg = dev->config;
@ -116,6 +135,14 @@ static void gpio_kbd_matrix_set_detect_mode(const struct device *dev, bool enabl
unsigned int flags = enabled ? GPIO_INT_EDGE_BOTH : GPIO_INT_DISABLE;
int ret;
if (cfg->idle_poll_dwork != NULL) {
if (enabled) {
k_work_reschedule(cfg->idle_poll_dwork,
K_USEC(common->poll_period_us));
}
return;
}
for (int i = 0; i < common->row_size; i++) {
const struct gpio_dt_spec *gpio = &cfg->row_gpio[i];
@ -143,6 +170,17 @@ static bool gpio_kbd_matrix_is_gpio_coherent(
return true;
}
static bool gpio_kbd_continuous_scan_mode(const struct device *dev)
{
const struct gpio_kbd_matrix_config *cfg = dev->config;
if (cfg->gpio_cb == NULL && cfg->idle_poll_dwork == NULL) {
return true;
}
return false;
}
static int gpio_kbd_matrix_init(const struct device *dev)
{
const struct gpio_kbd_matrix_config *cfg = dev->config;
@ -172,7 +210,7 @@ static int gpio_kbd_matrix_init(const struct device *dev)
for (i = 0; i < common->row_size; i++) {
const struct gpio_dt_spec *gpio = &cfg->row_gpio[i];
struct gpio_callback *gpio_cb = &cfg->gpio_cb[i];
struct gpio_callback *gpio_cb;
if (!gpio_is_ready_dt(gpio)) {
LOG_ERR("%s is not ready", gpio->port->name);
@ -185,7 +223,13 @@ static int gpio_kbd_matrix_init(const struct device *dev)
return ret;
}
gpio_init_callback(gpio_cb, cfg->handler, BIT(gpio->pin));
if (cfg->gpio_cb == NULL) {
continue;
}
gpio_cb = &cfg->gpio_cb[i];
gpio_init_callback(gpio_cb, cfg->gpio_cb_handler,
BIT(gpio->pin));
ret = gpio_add_callback_dt(gpio, gpio_cb);
if (ret < 0) {
@ -194,6 +238,11 @@ static int gpio_kbd_matrix_init(const struct device *dev)
}
}
if (cfg->idle_poll_dwork != NULL) {
k_work_init_delayable(cfg->idle_poll_dwork,
cfg->idle_poll_handler);
}
data->direct_read = gpio_kbd_matrix_is_gpio_coherent(
cfg->row_gpio, common->row_size);
@ -205,7 +254,16 @@ static int gpio_kbd_matrix_init(const struct device *dev)
LOG_DBG("direct_read: %d direct_write: %d",
data->direct_read, data->direct_write);
return input_kbd_matrix_common_init(dev);
ret = input_kbd_matrix_common_init(dev);
if (ret != 0) {
return ret;
}
if (gpio_kbd_continuous_scan_mode(dev)) {
input_kbd_matrix_poll_start(dev);
}
return 0;
}
static const struct input_kbd_matrix_api gpio_kbd_matrix_api = {
@ -220,12 +278,6 @@ static const struct input_kbd_matrix_api gpio_kbd_matrix_api = {
INPUT_KBD_MATRIX_DT_INST_DEFINE_ROW_COL( \
n, DT_INST_PROP_LEN(n, row_gpios), DT_INST_PROP_LEN(n, col_gpios)); \
\
static void gpio_kbd_matrix_cb_##n(const struct device *gpio_dev, \
struct gpio_callback *cb, uint32_t pins) \
{ \
input_kbd_matrix_poll_start(DEVICE_DT_INST_GET(n)); \
} \
\
static const struct gpio_dt_spec gpio_kbd_matrix_row_gpio_##n[DT_INST_PROP_LEN( \
n, row_gpios)] = { \
DT_INST_FOREACH_PROP_ELEM_SEP(n, row_gpios, GPIO_DT_SPEC_GET_BY_IDX, (,)) \
@ -234,7 +286,26 @@ static const struct input_kbd_matrix_api gpio_kbd_matrix_api = {
n, col_gpios)] = { \
DT_INST_FOREACH_PROP_ELEM_SEP(n, col_gpios, GPIO_DT_SPEC_GET_BY_IDX, (,)) \
}; \
\
IF_ENABLED(DT_INST_ENUM_HAS_VALUE(n, idle_mode, interrupt), ( \
static struct gpio_callback gpio_kbd_matrix_gpio_cb_##n[DT_INST_PROP_LEN(n, row_gpios)];\
static void gpio_kbd_matrix_cb_##n(const struct device *gpio_dev, \
struct gpio_callback *cb, uint32_t pins) \
{ \
input_kbd_matrix_poll_start(DEVICE_DT_INST_GET(n)); \
} \
)) \
IF_ENABLED(DT_INST_ENUM_HAS_VALUE(n, idle_mode, poll), ( \
static struct k_work_delayable gpio_kbd_matrix_idle_poll_dwork_##n; \
static void gpio_kbd_matrix_idle_poll_handler_##n(struct k_work *work) \
{ \
gpio_kbd_matrix_idle_poll_handler(DEVICE_DT_INST_GET(n)); \
} \
)) \
IF_ENABLED(DT_INST_ENUM_HAS_VALUE(n, idle_mode, scan), ( \
BUILD_ASSERT(DT_INST_PROP(n, poll_timeout_ms) == 0, \
"poll-timeout-ms must be set to 0 for scan mode to work correctly"); \
)) \
\
static const struct gpio_kbd_matrix_config gpio_kbd_matrix_cfg_##n = { \
.common = INPUT_KBD_MATRIX_DT_INST_COMMON_CONFIG_INIT_ROW_COL( \
@ -242,8 +313,14 @@ static const struct input_kbd_matrix_api gpio_kbd_matrix_api = {
DT_INST_PROP_LEN(n, row_gpios), DT_INST_PROP_LEN(n, col_gpios)), \
.row_gpio = gpio_kbd_matrix_row_gpio_##n, \
.col_gpio = gpio_kbd_matrix_col_gpio_##n, \
IF_ENABLED(DT_INST_ENUM_HAS_VALUE(n, idle_mode, interrupt), ( \
.gpio_cb = gpio_kbd_matrix_gpio_cb_##n, \
.handler = gpio_kbd_matrix_cb_##n, \
.gpio_cb_handler = gpio_kbd_matrix_cb_##n, \
)) \
IF_ENABLED(DT_INST_ENUM_HAS_VALUE(n, idle_mode, poll), ( \
.idle_poll_dwork = &gpio_kbd_matrix_idle_poll_dwork_##n, \
.idle_poll_handler = gpio_kbd_matrix_idle_poll_handler_##n, \
)) \
.col_drive_inactive = DT_INST_PROP(n, col_drive_inactive), \
}; \
\