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:
parent
56d73a8b0c
commit
3862c227d4
2 changed files with 104 additions and 13 deletions
|
@ -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), \
|
||||
}; \
|
||||
\
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue