Commit 584bf366 authored by Dmitry Torokhov's avatar Dmitry Torokhov

Input: matrix_keypad - switch to gpiod API and generic device properties

gpiod API and generic device properties work with software nodes and
static properties, which will allow removing platform data support
from the driver, simplifying and streamlining the code.
Acked-by: default avatarArnd Bergmann <arnd@arndb.de>
Reviewed-by: default avatarLinus Walleij <linus.walleij@linaro.org>
Link: https://lore.kernel.org/r/20240805014710.1961677-3-dmitry.torokhov@gmail.comSigned-off-by: default avatarDmitry Torokhov <dmitry.torokhov@gmail.com>
parent 5080f62a
......@@ -17,17 +17,26 @@
#include <linux/jiffies.h>
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/input/matrix_keypad.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_platform.h>
struct matrix_keypad {
const struct matrix_keypad_platform_data *pdata;
struct input_dev *input_dev;
unsigned int row_shift;
unsigned int col_scan_delay_us;
/* key debounce interval in milli-second */
unsigned int debounce_ms;
bool drive_inactive_cols;
struct gpio_desc *row_gpios[MATRIX_MAX_ROWS];
unsigned int num_row_gpios;
struct gpio_desc *col_gpios[MATRIX_MAX_ROWS];
unsigned int num_col_gpios;
unsigned int row_irqs[MATRIX_MAX_ROWS];
DECLARE_BITMAP(wakeup_enabled_irqs, MATRIX_MAX_ROWS);
......@@ -44,50 +53,43 @@ struct matrix_keypad {
* columns. In that case it is configured here to be input, otherwise it is
* driven with the inactive value.
*/
static void __activate_col(const struct matrix_keypad_platform_data *pdata,
int col, bool on)
static void __activate_col(struct matrix_keypad *keypad, int col, bool on)
{
bool level_on = !pdata->active_low;
if (on) {
gpio_direction_output(pdata->col_gpios[col], level_on);
gpiod_direction_output(keypad->col_gpios[col], 1);
} else {
gpio_set_value_cansleep(pdata->col_gpios[col], !level_on);
if (!pdata->drive_inactive_cols)
gpio_direction_input(pdata->col_gpios[col]);
gpiod_set_value_cansleep(keypad->col_gpios[col], 0);
if (!keypad->drive_inactive_cols)
gpiod_direction_input(keypad->col_gpios[col]);
}
}
static void activate_col(const struct matrix_keypad_platform_data *pdata,
int col, bool on)
static void activate_col(struct matrix_keypad *keypad, int col, bool on)
{
__activate_col(pdata, col, on);
__activate_col(keypad, col, on);
if (on && pdata->col_scan_delay_us)
udelay(pdata->col_scan_delay_us);
if (on && keypad->col_scan_delay_us)
udelay(keypad->col_scan_delay_us);
}
static void activate_all_cols(const struct matrix_keypad_platform_data *pdata,
bool on)
static void activate_all_cols(struct matrix_keypad *keypad, bool on)
{
int col;
for (col = 0; col < pdata->num_col_gpios; col++)
__activate_col(pdata, col, on);
for (col = 0; col < keypad->num_col_gpios; col++)
__activate_col(keypad, col, on);
}
static bool row_asserted(const struct matrix_keypad_platform_data *pdata,
int row)
static bool row_asserted(struct matrix_keypad *keypad, int row)
{
return gpio_get_value_cansleep(pdata->row_gpios[row]) ?
!pdata->active_low : pdata->active_low;
return gpiod_get_value_cansleep(keypad->row_gpios[row]);
}
static void enable_row_irqs(struct matrix_keypad *keypad)
{
int i;
for (i = 0; i < keypad->pdata->num_row_gpios; i++)
for (i = 0; i < keypad->num_row_gpios; i++)
enable_irq(keypad->row_irqs[i]);
}
......@@ -95,7 +97,7 @@ static void disable_row_irqs(struct matrix_keypad *keypad)
{
int i;
for (i = 0; i < keypad->pdata->num_row_gpios; i++)
for (i = 0; i < keypad->num_row_gpios; i++)
disable_irq_nosync(keypad->row_irqs[i]);
}
......@@ -108,39 +110,38 @@ static void matrix_keypad_scan(struct work_struct *work)
container_of(work, struct matrix_keypad, work.work);
struct input_dev *input_dev = keypad->input_dev;
const unsigned short *keycodes = input_dev->keycode;
const struct matrix_keypad_platform_data *pdata = keypad->pdata;
uint32_t new_state[MATRIX_MAX_COLS];
int row, col, code;
/* de-activate all columns for scanning */
activate_all_cols(pdata, false);
activate_all_cols(keypad, false);
memset(new_state, 0, sizeof(new_state));
for (row = 0; row < pdata->num_row_gpios; row++)
gpio_direction_input(pdata->row_gpios[row]);
for (row = 0; row < keypad->num_row_gpios; row++)
gpiod_direction_input(keypad->row_gpios[row]);
/* assert each column and read the row status out */
for (col = 0; col < pdata->num_col_gpios; col++) {
for (col = 0; col < keypad->num_col_gpios; col++) {
activate_col(pdata, col, true);
activate_col(keypad, col, true);
for (row = 0; row < pdata->num_row_gpios; row++)
for (row = 0; row < keypad->num_row_gpios; row++)
new_state[col] |=
row_asserted(pdata, row) ? (1 << row) : 0;
row_asserted(keypad, row) ? BIT(row) : 0;
activate_col(pdata, col, false);
activate_col(keypad, col, false);
}
for (col = 0; col < pdata->num_col_gpios; col++) {
for (col = 0; col < keypad->num_col_gpios; col++) {
uint32_t bits_changed;
bits_changed = keypad->last_key_state[col] ^ new_state[col];
if (bits_changed == 0)
continue;
for (row = 0; row < pdata->num_row_gpios; row++) {
if ((bits_changed & (1 << row)) == 0)
for (row = 0; row < keypad->num_row_gpios; row++) {
if (!(bits_changed & BIT(row)))
continue;
code = MATRIX_SCAN_CODE(row, col, keypad->row_shift);
......@@ -154,7 +155,7 @@ static void matrix_keypad_scan(struct work_struct *work)
memcpy(keypad->last_key_state, new_state, sizeof(new_state));
activate_all_cols(pdata, true);
activate_all_cols(keypad, true);
/* Enable IRQs again */
spin_lock_irq(&keypad->lock);
......@@ -181,7 +182,7 @@ static irqreturn_t matrix_keypad_interrupt(int irq, void *id)
disable_row_irqs(keypad);
keypad->scan_pending = true;
schedule_delayed_work(&keypad->work,
msecs_to_jiffies(keypad->pdata->debounce_ms));
msecs_to_jiffies(keypad->debounce_ms));
out:
spin_unlock_irqrestore(&keypad->lock, flags);
......@@ -225,7 +226,7 @@ static void matrix_keypad_enable_wakeup(struct matrix_keypad *keypad)
int i;
for_each_clear_bit(i, keypad->wakeup_enabled_irqs,
keypad->pdata->num_row_gpios)
keypad->num_row_gpios)
if (enable_irq_wake(keypad->row_irqs[i]) == 0)
__set_bit(i, keypad->wakeup_enabled_irqs);
}
......@@ -235,7 +236,7 @@ static void matrix_keypad_disable_wakeup(struct matrix_keypad *keypad)
int i;
for_each_set_bit(i, keypad->wakeup_enabled_irqs,
keypad->pdata->num_row_gpios) {
keypad->num_row_gpios) {
disable_irq_wake(keypad->row_irqs[i]);
__clear_bit(i, keypad->wakeup_enabled_irqs);
}
......@@ -270,11 +271,14 @@ static int matrix_keypad_resume(struct device *dev)
static DEFINE_SIMPLE_DEV_PM_OPS(matrix_keypad_pm_ops,
matrix_keypad_suspend, matrix_keypad_resume);
static int matrix_keypad_init_gpio(struct platform_device *pdev,
struct matrix_keypad *keypad)
static int matrix_keypad_init_pdata_gpio(struct platform_device *pdev,
const struct matrix_keypad_platform_data *pdata,
struct matrix_keypad *keypad)
{
const struct matrix_keypad_platform_data *pdata = keypad->pdata;
int i, irq, err;
int i, err;
keypad->num_col_gpios = pdata->num_col_gpios;
keypad->num_row_gpios = pdata->num_row_gpios;
/* initialized strobe lines as outputs, activated */
for (i = 0; i < pdata->num_col_gpios; i++) {
......@@ -287,7 +291,12 @@ static int matrix_keypad_init_gpio(struct platform_device *pdev,
return err;
}
gpio_direction_output(pdata->col_gpios[i], !pdata->active_low);
keypad->col_gpios[i] = gpio_to_desc(pdata->col_gpios[i]);
if (pdata->active_low ^ gpiod_is_active_low(keypad->col_gpios[i]))
gpiod_toggle_active_low(keypad->col_gpios[i]);
gpiod_direction_output(keypad->col_gpios[i], 1);
}
for (i = 0; i < pdata->num_row_gpios; i++) {
......@@ -300,137 +309,125 @@ static int matrix_keypad_init_gpio(struct platform_device *pdev,
return err;
}
gpio_direction_input(pdata->row_gpios[i]);
}
for (i = 0; i < pdata->num_row_gpios; i++) {
irq = gpio_to_irq(pdata->row_gpios[i]);
if (irq < 0) {
err = irq;
dev_err(&pdev->dev,
"Unable to convert GPIO line %i to irq: %d\n",
pdata->row_gpios[i], err);
return err;
}
keypad->row_gpios[i] = gpio_to_desc(pdata->row_gpios[i]);
err = devm_request_any_context_irq(&pdev->dev,
irq,
matrix_keypad_interrupt,
IRQF_TRIGGER_RISING |
IRQF_TRIGGER_FALLING,
"matrix-keypad", keypad);
if (err < 0) {
dev_err(&pdev->dev,
"Unable to acquire interrupt for GPIO line %i\n",
pdata->row_gpios[i]);
return err;
}
if (pdata->active_low ^ gpiod_is_active_low(keypad->row_gpios[i]))
gpiod_toggle_active_low(keypad->row_gpios[i]);
keypad->row_irqs[i] = irq;
gpiod_direction_input(keypad->row_gpios[i]);
}
/* initialized as disabled - enabled by input->open */
disable_row_irqs(keypad);
return 0;
}
#ifdef CONFIG_OF
static struct matrix_keypad_platform_data *
matrix_keypad_parse_dt(struct device *dev)
static int matrix_keypad_init_gpio(struct platform_device *pdev,
struct matrix_keypad *keypad)
{
struct matrix_keypad_platform_data *pdata;
struct device_node *np = dev->of_node;
unsigned int *gpios;
int ret, i, nrow, ncol;
if (!np) {
dev_err(dev, "device lacks DT data\n");
return ERR_PTR(-ENODEV);
}
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata) {
dev_err(dev, "could not allocate memory for platform data\n");
return ERR_PTR(-ENOMEM);
}
bool active_low;
int nrow, ncol;
int err;
int i;
pdata->num_row_gpios = nrow = gpiod_count(dev, "row");
pdata->num_col_gpios = ncol = gpiod_count(dev, "col");
nrow = gpiod_count(&pdev->dev, "row");
ncol = gpiod_count(&pdev->dev, "col");
if (nrow < 0 || ncol < 0) {
dev_err(dev, "number of keypad rows/columns not specified\n");
return ERR_PTR(-EINVAL);
dev_err(&pdev->dev, "missing row or column GPIOs\n");
return -EINVAL;
}
pdata->no_autorepeat = of_property_read_bool(np, "linux,no-autorepeat");
keypad->num_row_gpios = nrow;
keypad->num_col_gpios = ncol;
pdata->wakeup = of_property_read_bool(np, "wakeup-source") ||
of_property_read_bool(np, "linux,wakeup"); /* legacy */
active_low = device_property_read_bool(&pdev->dev, "gpio-activelow");
pdata->active_low = of_property_read_bool(np, "gpio-activelow");
/* initialize strobe lines as outputs, activated */
for (i = 0; i < keypad->num_col_gpios; i++) {
keypad->col_gpios[i] = devm_gpiod_get_index(&pdev->dev, "col",
i, GPIOD_ASIS);
err = PTR_ERR_OR_ZERO(keypad->col_gpios[i]);
if (err) {
dev_err(&pdev->dev,
"failed to request GPIO for COL%d: %d\n",
i, err);
return err;
}
pdata->drive_inactive_cols =
of_property_read_bool(np, "drive-inactive-cols");
gpiod_set_consumer_name(keypad->col_gpios[i], "matrix_kbd_col");
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);
if (active_low ^ gpiod_is_active_low(keypad->col_gpios[i]))
gpiod_toggle_active_low(keypad->col_gpios[i]);
gpios = devm_kcalloc(dev,
pdata->num_row_gpios + pdata->num_col_gpios,
sizeof(unsigned int),
GFP_KERNEL);
if (!gpios) {
dev_err(dev, "could not allocate memory for gpios\n");
return ERR_PTR(-ENOMEM);
gpiod_direction_output(keypad->col_gpios[i], 1);
}
for (i = 0; i < nrow; i++) {
ret = of_get_named_gpio(np, "row-gpios", i);
if (ret < 0)
return ERR_PTR(ret);
gpios[i] = ret;
}
for (i = 0; i < keypad->num_row_gpios; i++) {
keypad->row_gpios[i] = devm_gpiod_get_index(&pdev->dev, "row",
i, GPIOD_IN);
err = PTR_ERR_OR_ZERO(keypad->row_gpios[i]);
if (err) {
dev_err(&pdev->dev,
"failed to request GPIO for ROW%d: %d\n",
i, err);
return err;
}
for (i = 0; i < ncol; i++) {
ret = of_get_named_gpio(np, "col-gpios", i);
if (ret < 0)
return ERR_PTR(ret);
gpios[nrow + i] = ret;
}
gpiod_set_consumer_name(keypad->row_gpios[i], "matrix_kbd_row");
pdata->row_gpios = gpios;
pdata->col_gpios = &gpios[pdata->num_row_gpios];
if (active_low ^ gpiod_is_active_low(keypad->row_gpios[i]))
gpiod_toggle_active_low(keypad->row_gpios[i]);
}
return pdata;
return 0;
}
#else
static inline struct matrix_keypad_platform_data *
matrix_keypad_parse_dt(struct device *dev)
static int matrix_keypad_setup_interrupts(struct platform_device *pdev,
struct matrix_keypad *keypad)
{
dev_err(dev, "no platform data defined\n");
int err;
int irq;
int i;
for (i = 0; i < keypad->num_row_gpios; i++) {
irq = gpiod_to_irq(keypad->row_gpios[i]);
if (irq < 0) {
err = irq;
dev_err(&pdev->dev,
"Unable to convert GPIO line %i to irq: %d\n",
i, err);
return err;
}
err = devm_request_any_context_irq(&pdev->dev, irq,
matrix_keypad_interrupt,
IRQF_TRIGGER_RISING |
IRQF_TRIGGER_FALLING,
"matrix-keypad", keypad);
if (err < 0) {
dev_err(&pdev->dev,
"Unable to acquire interrupt for row %i: %d\n",
i, err);
return err;
}
keypad->row_irqs[i] = irq;
}
return ERR_PTR(-EINVAL);
/* initialized as disabled - enabled by input->open */
disable_row_irqs(keypad);
return 0;
}
#endif
static int matrix_keypad_probe(struct platform_device *pdev)
{
const struct matrix_keypad_platform_data *pdata;
const struct matrix_keypad_platform_data *pdata =
dev_get_platdata(&pdev->dev);
struct matrix_keypad *keypad;
struct input_dev *input_dev;
bool autorepeat;
bool wakeup;
int err;
pdata = dev_get_platdata(&pdev->dev);
if (!pdata) {
pdata = matrix_keypad_parse_dt(&pdev->dev);
if (IS_ERR(pdata))
return PTR_ERR(pdata);
} else if (!pdata->keymap_data) {
dev_err(&pdev->dev, "no keymap data defined\n");
return -EINVAL;
}
keypad = devm_kzalloc(&pdev->dev, sizeof(*keypad), GFP_KERNEL);
if (!keypad)
return -ENOMEM;
......@@ -440,40 +437,72 @@ static int matrix_keypad_probe(struct platform_device *pdev)
return -ENOMEM;
keypad->input_dev = input_dev;
keypad->pdata = pdata;
keypad->row_shift = get_count_order(pdata->num_col_gpios);
keypad->stopped = true;
INIT_DELAYED_WORK(&keypad->work, matrix_keypad_scan);
spin_lock_init(&keypad->lock);
keypad->drive_inactive_cols =
device_property_read_bool(&pdev->dev, "drive-inactive-cols");
device_property_read_u32(&pdev->dev, "debounce-delay-ms",
&keypad->debounce_ms);
device_property_read_u32(&pdev->dev, "col-scan-delay-us",
&keypad->col_scan_delay_us);
if (pdata) {
keypad->col_scan_delay_us = pdata->col_scan_delay_us;
keypad->debounce_ms = pdata->debounce_ms;
keypad->drive_inactive_cols = pdata->drive_inactive_cols;
}
if (pdata)
err = matrix_keypad_init_pdata_gpio(pdev, pdata, keypad);
else
err = matrix_keypad_init_gpio(pdev, keypad);
if (err)
return err;
keypad->row_shift = get_count_order(keypad->num_col_gpios);
err = matrix_keypad_setup_interrupts(pdev, keypad);
if (err)
return err;
input_dev->name = pdev->name;
input_dev->id.bustype = BUS_HOST;
input_dev->open = matrix_keypad_start;
input_dev->close = matrix_keypad_stop;
err = matrix_keypad_build_keymap(pdata->keymap_data, NULL,
pdata->num_row_gpios,
pdata->num_col_gpios,
err = matrix_keypad_build_keymap(pdata ? pdata->keymap_data : NULL,
NULL,
keypad->num_row_gpios,
keypad->num_col_gpios,
NULL, input_dev);
if (err) {
dev_err(&pdev->dev, "failed to build keymap\n");
return -ENOMEM;
}
if (!pdata->no_autorepeat)
autorepeat = !device_property_read_bool(&pdev->dev,
"linux,no-autorepeat");
if (autorepeat && pdata->no_autorepeat)
autorepeat = false;
if (autorepeat)
__set_bit(EV_REP, input_dev->evbit);
input_set_capability(input_dev, EV_MSC, MSC_SCAN);
input_set_drvdata(input_dev, keypad);
err = matrix_keypad_init_gpio(pdev, keypad);
if (err)
return err;
err = input_register_device(keypad->input_dev);
if (err)
return err;
device_init_wakeup(&pdev->dev, pdata->wakeup);
wakeup = device_property_read_bool(&pdev->dev, "wakeup-source") ||
/* legacy */
device_property_read_bool(&pdev->dev, "linux,wakeup");
if (!wakeup && pdata)
wakeup = pdata->wakeup;
device_init_wakeup(&pdev->dev, wakeup);
platform_set_drvdata(pdev, keypad);
return 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment