@@ -43,6 +43,10 @@ struct matrix_keypad {
};
/*
+ * this routine controls a physical column pin which the keypad matrix
+ * is connected to, and takes care of the pin's polarity as well as its
+ * mode of operation (fully driven push/pull, or emulated open drain)
+ *
* former comment before introduction of optional push/pull behaviour:
* <cite>
* NOTE: normally the GPIO has to be put into HiZ when de-activated to cause
@@ -77,6 +81,14 @@ static void __activate_col_pin(const struct matrix_keypad_platform_data *pdata,
}
}
+/*
+ * this routine addresses logical columns of the keypad matrix, and
+ * makes sure that the "scan delay" is applied upon activation of the
+ * column (the delay between activating the column and reading rows)
+ *
+ * the caller ensures that this routine need not de-activate other
+ * columns when dealing with the column specified for the invocation
+ */
static void activate_col(const struct matrix_keypad_platform_data *pdata,
int col, bool on)
{
@@ -86,6 +98,12 @@ static void activate_col(const struct matrix_keypad_platform_data *pdata,
udelay(pdata->col_scan_delay_us);
}
+/*
+ * this routine either de-activates all columns before scanning the
+ * matrix, or re-activates all columns at the same time after the scan
+ * is complete, to make changes in the key press state trigger the
+ * condition to re-scan the matrix
+ */
static void activate_all_cols(const struct matrix_keypad_platform_data *pdata,
bool on)
{
@@ -95,6 +113,10 @@ static void activate_all_cols(const struct matrix_keypad_platform_data *pdata,
__activate_col_pin(pdata, col, on);
}
+/*
+ * this routine checks a single row while a specific column is active,
+ * it takes care of the pin's polarity, the pin level had time to settle
+ */
static bool row_asserted(const struct matrix_keypad_platform_data *pdata,
int row)
{
@@ -103,6 +125,12 @@ static bool row_asserted(const struct matrix_keypad_platform_data *pdata,
pdata->row_gpios_active_low;
}
+/*
+ * this routine enables IRQs after a keypad matrix scan has completed,
+ * to have any subsequent change in the key press status trigger the ISR
+ *
+ * a single IRQ line can be used if all involved GPIOs share that IRQ
+ */
static void enable_row_irqs(struct matrix_keypad *keypad)
{
const struct matrix_keypad_platform_data *pdata = keypad->pdata;
@@ -116,6 +144,13 @@ static void enable_row_irqs(struct matrix_keypad *keypad)
}
}
+/*
+ * this routine disables IRQs for changes in the key press status, which
+ * applies to shutdown or suspend modes, to periods where the keypad
+ * matrix is not used (not opened by any application), as well as to the
+ * interval between the first detected change and scanning the complete
+ * matrix (the debounce interval)
+ */
static void disable_row_irqs(struct matrix_keypad *keypad)
{
const struct matrix_keypad_platform_data *pdata = keypad->pdata;
@@ -130,7 +165,9 @@ static void disable_row_irqs(struct matrix_keypad *keypad)
}
/*
- * This gets the keys from keyboard and reports it to input subsystem
+ * this routine scans the keypad matrix and detects changes in the keys'
+ * status against a previously sampled status, from which events for the
+ * input subsystem get derived
*/
static void matrix_keypad_scan(struct work_struct *work)
{
@@ -142,12 +179,12 @@ static void matrix_keypad_scan(struct work_struct *work)
uint32_t new_state[MATRIX_MAX_COLS];
int row, col, code;
- /* de-activate all columns for scanning */
+ /* de-activate all columns before scanning the matrix */
activate_all_cols(pdata, false);
memset(new_state, 0, sizeof(new_state));
- /* assert each column and read the row status out */
+ /* assert each column in turn and read back the row status */
for (col = 0; col < pdata->num_col_gpios; col++) {
activate_col(pdata, col, true);
@@ -159,6 +196,7 @@ static void matrix_keypad_scan(struct work_struct *work)
activate_col(pdata, col, false);
}
+ /* detect changes and derive input events, update the snapshot */
for (col = 0; col < pdata->num_col_gpios; col++) {
uint32_t bits_changed;
@@ -171,6 +209,15 @@ static void matrix_keypad_scan(struct work_struct *work)
continue;
code = MATRIX_SCAN_CODE(row, col, keypad->row_shift);
+ /*
+ * TODO: the key code matrix may be sparse,
+ * ignore items in gaps or report changes in all
+ * positions? this consideration may especially
+ * apply where the key code matrix gets setup or
+ * manipulated from user space, or where the key
+ * code matrix gets switched (shift or function
+ * keys, alternate keyboard modes)
+ */
input_event(input_dev, EV_MSC, MSC_SCAN, code);
input_report_key(input_dev,
keycodes[code],
@@ -178,9 +225,13 @@ static void matrix_keypad_scan(struct work_struct *work)
}
}
input_sync(input_dev);
-
memcpy(keypad->last_key_state, new_state, sizeof(new_state));
+ /*
+ * re-enable all columns after the scan has completed, to have
+ * changes in their key press status issue interrupts and
+ * trigger another complete scan of the matrix
+ */
activate_all_cols(pdata, true);
/* Enable IRQs again */
@@ -190,6 +241,14 @@ static void matrix_keypad_scan(struct work_struct *work)
spin_unlock_irq(&keypad->lock);
}
+/*
+ * interrupt service routine, invoked upon the first detected change in
+ * the key press status, initiating a debounce delay, and suppressing
+ * subsequent interrupts until scanning all of the matrix has completed
+ *
+ * copes with interrupts which arrive during the debounce interval or
+ * the actual matrix scan or a shutdown or suspend sequence
+ */
static irqreturn_t matrix_keypad_interrupt(int irq, void *id)
{
struct matrix_keypad *keypad = id;
@@ -310,6 +369,10 @@ static int matrix_keypad_resume(struct device *dev)
if (device_may_wakeup(&pdev->dev))
matrix_keypad_disable_wakeup(keypad);
+ /*
+ * TODO: consider whether to only (re-)start the keypad matrix
+ * driver when it was opened by applications?
+ */
matrix_keypad_start(keypad->input_dev);
return 0;
@@ -425,7 +488,7 @@ matrix_keypad_parse_dt(struct device *dev)
int i, nrow, ncol;
if (!np) {
- dev_err(dev, "device lacks DT data\n");
+ dev_err(dev, "device lacks DT data for the keypad config\n");
return ERR_PTR(-ENODEV);
}
@@ -435,6 +498,7 @@ matrix_keypad_parse_dt(struct device *dev)
return ERR_PTR(-ENOMEM);
}
+ /* get the pin count for rows and columns */
pdata->num_row_gpios = nrow = of_gpio_named_count(np, "row-gpios");
pdata->num_col_gpios = ncol = of_gpio_named_count(np, "col-gpios");
if (nrow <= 0 || ncol <= 0) {
@@ -442,6 +506,7 @@ matrix_keypad_parse_dt(struct device *dev)
return ERR_PTR(-EINVAL);
}
+ /* get input, power, and GPIO pin properties */
if (of_get_property(np, "linux,no-autorepeat", NULL))
pdata->no_autorepeat = true;
if (of_get_property(np, "linux,wakeup", NULL))
@@ -457,10 +522,12 @@ matrix_keypad_parse_dt(struct device *dev)
if (of_get_property(np, "col-gpios-pushpull", NULL))
pdata->col_gpios_push_pull = true;
+ /* get delay and interval timing specs */
of_property_read_u32(np, "debounce-delay-ms", &pdata->debounce_ms);
of_property_read_u32(np, "col-scan-delay-us",
&pdata->col_scan_delay_us);
+ /* get the individual row and column GPIO pins */
gpios = devm_kzalloc(dev,
sizeof(unsigned int) *
(pdata->num_row_gpios + pdata->num_col_gpios),
@@ -486,7 +553,7 @@ matrix_keypad_parse_dt(struct device *dev)
static inline struct matrix_keypad_platform_data *
matrix_keypad_parse_dt(struct device *dev)
{
- dev_err(dev, "no platform data defined\n");
+ dev_err(dev, "device lacks DT support for the keypad config\n");
return ERR_PTR(-EINVAL);
}
@@ -521,6 +588,8 @@ static int matrix_keypad_probe(struct platform_device *pdev)
keypad->input_dev = input_dev;
keypad->pdata = pdata;
keypad->row_shift = get_count_order(pdata->num_col_gpios);
+
+ /* start in stopped state, open(2) will activate the scan */
keypad->stopped = true;
INIT_DELAYED_WORK(&keypad->work_scan_matrix, matrix_keypad_scan);
spin_lock_init(&keypad->lock);
@@ -103,7 +103,9 @@ static int matrix_keypad_parse_of_keymap(const char *propname,
size = proplen / sizeof(u32);
if (size > max_keys) {
- dev_err(dev, "OF: %s size overflow\n", propname);
+ dev_err(dev,
+ "OF: %s size overflow, keymap size %u, max keys %u\n",
+ propname, size, max_keys);
return -EINVAL;
}
@@ -39,9 +39,11 @@ struct matrix_keymap_data {
* @col_gpios: pointer to array of gpio numbers reporesenting colums
* @num_row_gpios: actual number of row gpios used by device
* @num_col_gpios: actual number of col gpios used by device
- * @col_scan_delay_us: delay, measured in microseconds, that is
- * needed before we can keypad after activating column gpio
- * @debounce_ms: debounce interval in milliseconds
+ * @col_scan_delay_us: delay in microseconds, the interval between
+ * activating a column and reading back row information
+ * @debounce_ms: debounce interval in milliseconds, the interval between
+ * detecting a change in the key press status and determining the new
+ * overall keypad matrix status
* @clustered_irq: may be specified if interrupts of all row/column GPIOs
* are bundled to one single irq
* @clustered_irq_flags: flags that are needed for the clustered irq
@@ -53,26 +55,29 @@ struct matrix_keymap_data {
* source
* @no_autorepeat: disable key autorepeat
*
- * This structure represents platform-specific data that use used by
+ * This structure represents platform-specific data that is used by
* matrix_keypad driver to perform proper initialization.
*/
struct matrix_keypad_platform_data {
+ /* map keys to codes */
const struct matrix_keymap_data *keymap_data;
+ /* the physical GPIO pin connections */
const unsigned int *row_gpios;
const unsigned int *col_gpios;
-
unsigned int num_row_gpios;
unsigned int num_col_gpios;
+ /* delays and intervals specs */
unsigned int col_scan_delay_us;
- /* key debounce interval in milli-second */
unsigned int debounce_ms;
+ /* optionally reduce interrupt mgmt overhead */
unsigned int clustered_irq;
unsigned int clustered_irq_flags;
+ /* pin and input system properties */
bool row_gpios_active_low;
bool col_gpios_active_low;
bool col_gpios_push_pull;
- add comments about individual routines' purpose and their interaction, pre-conditions and consequences - mark a few spots which may need some more attention or clarification - rephrase some diagnostics messages Signed-off-by: Gerhard Sittig <gsi@denx.de> --- drivers/input/keyboard/matrix_keypad.c | 81 +++++++++++++++++++++++++++++--- drivers/input/matrix-keymap.c | 4 +- include/linux/input/matrix_keypad.h | 17 ++++--- 3 files changed, 89 insertions(+), 13 deletions(-)