summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Blair <chris.blair@stericsson.com>2011-12-05 10:01:21 +0100
committerPhilippe Langlais <philippe.langlais@stericsson.com>2012-05-22 11:03:39 +0200
commitdec331e62c2d978ebf73b9f0f04b53ace3c24907 (patch)
tree0bc05a64ddfe00c92504c1caa7f48d43ccf910ea
parent961a50cea677e23992aa0fc49ed33f50779ea328 (diff)
Nomadik ske: Support keypad smaller than 8x8
Adds support to the driver for keypads with less than 8 rows and 8 columns. ST-Ericsson ID: 373774 ST-Ericsson FOSS-OUT ID: Trivial ST-Ericsson Linux next: NA Signed-off-by: Chris Blair <chris.blair@stericsson.com> Conflicts: drivers/input/keyboard/nomadik-ske-keypad.c Change-Id: Ib357ed6247c82dd326ce00fe826777fd459088b4 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/46286 Reviewed-by: QATOOLS Reviewed-by: Michel JAOUEN <michel.jaouen@stericsson.com> Tested-by: Michel JAOUEN <michel.jaouen@stericsson.com> Reviewed-by: QATEST Reviewed-by: Christopher BLAIR <chris.blair@stericsson.com> Reviewed-by: Linus WALLEIJ <linus.walleij@stericsson.com>
-rw-r--r--arch/arm/plat-nomadik/include/plat/ske.h4
-rw-r--r--drivers/input/keyboard/nomadik-ske-keypad.c143
2 files changed, 122 insertions, 25 deletions
diff --git a/arch/arm/plat-nomadik/include/plat/ske.h b/arch/arm/plat-nomadik/include/plat/ske.h
index 7a4fbdf3c13..834c015c935 100644
--- a/arch/arm/plat-nomadik/include/plat/ske.h
+++ b/arch/arm/plat-nomadik/include/plat/ske.h
@@ -38,6 +38,8 @@
* @keymap_data: matrix scan code table for keycodes
* @krow: maximum number of rows
* @kcol: maximum number of columns
+ * @kconnected_rows: number of rows actually connected
+ * @kconnected_cols: number of columns actually connected
* @debounce_ms: platform specific debounce time
* @no_autorepeat: flag for auto repetition
* @wakeup_enable: allow waking up the system
@@ -51,6 +53,8 @@ struct ske_keypad_platform_data {
const struct matrix_keymap_data *keymap_data;
u8 krow;
u8 kcol;
+ u8 kconnected_rows;
+ u8 kconnected_cols;
u8 debounce_ms;
bool no_autorepeat;
bool wakeup_enable;
diff --git a/drivers/input/keyboard/nomadik-ske-keypad.c b/drivers/input/keyboard/nomadik-ske-keypad.c
index 5bc91685f90..156f64b1339 100644
--- a/drivers/input/keyboard/nomadik-ske-keypad.c
+++ b/drivers/input/keyboard/nomadik-ske-keypad.c
@@ -93,15 +93,15 @@ struct ske_keypad {
bool enable;
bool enable_on_resume;
struct regulator *regulator;
- int gpio_input_irq[SKE_KPD_MAX_ROWS];
+ int *gpio_input_irq;
int key_pressed;
struct delayed_work work;
- int ske_rows[SKE_KPD_MAX_ROWS];
- int ske_cols[SKE_KPD_MAX_COLS];
+ int *ske_rows;
+ int *ske_cols;
int gpio_row;
int gpio_col;
struct delayed_work gpio_work;
- u8 keys[SKE_KPD_MAX_ROWS][SKE_KPD_MAX_COLS];
+ u8 **keys;
struct delayed_work scan_work;
};
@@ -180,7 +180,7 @@ static void ske_mode_enable(struct ske_keypad *keypad, bool enable)
writel(0, keypad->reg_base + SKE_CR);
if (keypad->board->exit)
keypad->board->exit();
- for (i = 0; i < keypad->board->krow; i++) {
+ for (i = 0; i < keypad->board->kconnected_rows; i++) {
enable_irq(keypad->gpio_input_irq[i]);
enable_irq_wake(keypad->gpio_input_irq[i]);
}
@@ -190,7 +190,7 @@ static void ske_mode_enable(struct ske_keypad *keypad, bool enable)
dev_dbg(keypad->dev, "%s enable keypad\n", __func__);
regulator_enable(keypad->regulator);
clk_enable(keypad->clk);
- for (i = 0; i < keypad->board->krow; i++) {
+ for (i = 0; i < keypad->board->kconnected_rows; i++) {
disable_irq_nosync(keypad->gpio_input_irq[i]);
disable_irq_wake(keypad->gpio_input_irq[i]);
}
@@ -264,6 +264,10 @@ static void ske_keypad_report(struct ske_keypad *keypad, u8 status, int col)
row = pos;
status &= ~(1 << pos);
+ if (row >= keypad->board->krow)
+ /* no more rows supported by this keypad */
+ break;
+
code = MATRIX_SCAN_CODE(row, col, SKE_KEYPAD_ROW_SHIFT);
ske_ris = readl(keypad->reg_base + SKE_RIS);
keypad->key_pressed = ske_ris & SKE_KPRISA;
@@ -301,11 +305,17 @@ static void ske_keypad_read_data(struct ske_keypad *keypad)
status = ske_asr & 0xff;
if (status) {
col = i * 2;
+ if (col >= keypad->board->kcol)
+ /* no more columns supported by this keypad */
+ break;
ske_keypad_report(keypad, status, col);
}
status = (ske_asr & 0xff00) >> 8;
if (status) {
col = (i * 2) + 1;
+ if (col >= keypad->board->kcol)
+ /* no more columns supported by this keypad */
+ break;
ske_keypad_report(keypad, status, col);
}
}
@@ -327,8 +337,8 @@ static void ske_keypad_scan_work(struct work_struct *work)
ske_keypad_read_data(keypad);
/* Check for key actions */
- for (i = 0; i < SKE_KPD_MAX_ROWS; i++) {
- for (j = 0; j < SKE_KPD_MAX_COLS; j++) {
+ for (i = 0; i < keypad->board->krow; i++) {
+ for (j = 0; j < keypad->board->kcol; j++) {
switch (keypad->keys[i][j]) {
case KEY_REPORTED:
/**
@@ -343,8 +353,9 @@ static void ske_keypad_scan_work(struct work_struct *work)
input_sync(input);
keypad->keys[i][j] = 0;
dev_dbg(keypad->dev,
- "%s Key release reported, code:%d\n",
- __func__, code);
+ "%s Key release reported, code:%d "
+ "(key %d)\n",
+ __func__, code, keypad->keymap[code]);
break;
case KEY_PRESSED:
/* Key pressed but not yet reported, report */
@@ -355,8 +366,9 @@ static void ske_keypad_scan_work(struct work_struct *work)
1);
input_sync(input);
dev_dbg(keypad->dev,
- "%s Key press reported, code:%d\n",
- __func__, code);
+ "%s Key press reported, code:%d "
+ "(key %d)\n",
+ __func__, code, keypad->keymap[code]);
/* Intentional fall though */
case (KEY_REPORTED | KEY_PRESSED):
/**
@@ -424,8 +436,8 @@ static void ske_gpio_release_work(struct work_struct *work)
code = MATRIX_SCAN_CODE(keypad->gpio_row, keypad->gpio_col,
SKE_KEYPAD_ROW_SHIFT);
- dev_dbg(keypad->dev, "%s Key press reported, code:%d\n",
- __func__, code);
+ dev_dbg(keypad->dev, "%s Key press reported, code:%d (key %d)\n",
+ __func__, code, keypad->keymap[code]);
input_event(input, EV_MSC, MSC_SCAN, code);
input_report_key(input, keypad->keymap[code], 1);
@@ -441,13 +453,13 @@ static int ske_read_get_gpio_row(struct ske_keypad *keypad)
int ret;
/* read all rows GPIO data register values */
- for (row = 0; row < SKE_KPD_MAX_ROWS ; row++) {
+ for (row = 0; row < keypad->board->kconnected_rows ; row++) {
ret = gpio_get_value(keypad->ske_rows[row]);
value += (1 << row) * ret;
}
/* get the exact row */
- for (row = 0; row < keypad->board->krow; row++) {
+ for (row = 0; row < keypad->board->kconnected_rows; row++) {
if (((1 << row) & value) == 0)
return row;
}
@@ -464,7 +476,7 @@ static void ske_set_cols(struct ske_keypad *keypad, int col)
* Set all columns except the requested column
* output pin as high
*/
- for (i = 0; i < SKE_KPD_MAX_COLS; i++) {
+ for (i = 0; i < keypad->board->kconnected_cols; i++) {
if (i == col)
value = 0;
else
@@ -479,7 +491,7 @@ static void ske_free_cols(struct ske_keypad *keypad)
{
int i ;
- for (i = 0; i < SKE_KPD_MAX_COLS; i++) {
+ for (i = 0; i < keypad->board->kconnected_cols; i++) {
gpio_request(keypad->ske_cols[i], "ske-kp");
gpio_direction_output(keypad->ske_cols[i], 0);
gpio_free(keypad->ske_cols[i]);
@@ -491,7 +503,7 @@ static void ske_manual_scan(struct ske_keypad *keypad)
int row;
int col;
- for (col = 0; col < keypad->board->kcol; col++) {
+ for (col = 0; col < keypad->board->kconnected_cols; col++) {
ske_set_cols(keypad, col);
row = ske_read_get_gpio_row(keypad);
if (row >= 0) {
@@ -682,14 +694,78 @@ static int __init ske_keypad_probe(struct platform_device *pdev)
ret = -EINVAL;
goto out_unregisterinput;
}
- for (i = 0; i < SKE_KPD_MAX_ROWS; i++) {
- keypad->ske_rows[i] = plat->gpio_input_pins[i];
- keypad->ske_cols[i] = plat->gpio_output_pins[i];
+
+ if (plat->kconnected_rows == 0) {
+ /*
+ * Board config data does not specify the number of connected
+ * rows and columns; assume that it matches the specified max
+ * values.
+ */
+ plat->kconnected_rows = plat->krow;
+ plat->kconnected_cols = plat->kcol;
+ }
+
+ /* this code doesn't currently support non-square keypad */
+ if (plat->kconnected_rows != plat->kconnected_cols) {
+ dev_err(&pdev->dev,
+ "invalid keypad configuration (not square)\n"),
+ ret = -EINVAL;
+ goto out_unregisterinput;
+ }
+
+ if (plat->kconnected_rows > SKE_KPD_MAX_ROWS ||
+ plat->kconnected_cols > SKE_KPD_MAX_COLS) {
+ dev_err(&pdev->dev,
+ "invalid keypad configuration (too many rows/cols)\n"),
+ ret = -EINVAL;
+ goto out_unregisterinput;
+ }
+
+ keypad->gpio_input_irq = kmalloc(sizeof(*keypad->gpio_input_irq) *
+ plat->kconnected_rows,
+ GFP_KERNEL);
+ if (!keypad->gpio_input_irq) {
+ dev_err(&pdev->dev, "failed to allocate input_irq memory\n");
+ goto out_unregisterinput;
+ }
+
+ keypad->ske_rows = kmalloc(sizeof(*keypad->ske_rows) *
+ plat->kconnected_rows, GFP_KERNEL);
+ if (!keypad->ske_rows) {
+ dev_err(&pdev->dev, "failed to allocate ske_rows memory\n");
+ goto out_freemem_input_irq;
+ }
+
+ keypad->ske_cols = kmalloc(sizeof(*keypad->ske_cols) *
+ plat->kconnected_cols, GFP_KERNEL);
+ if (!keypad->ske_cols) {
+ dev_err(&pdev->dev, "failed to allocate ske_cols memory\n");
+ goto out_freemem_rows;
+ }
+
+ keypad->keys = kzalloc(sizeof(*keypad->keys) * plat->krow, GFP_KERNEL);
+ if (!keypad->keys) {
+ dev_err(&pdev->dev, "failed to allocate keys:rows memory\n");
+ goto out_freemem_cols;
+ }
+ for (i = 0; i < plat->krow; i++) {
+ keypad->keys[i] = kzalloc(sizeof(*keypad->keys[i]) *
+ plat->kcol, GFP_KERNEL);
+ if (!keypad->keys[i]) {
+ dev_err(&pdev->dev,
+ "failed to allocate keys:cols memory\n");
+ goto out_freemem_keys;
+ }
+ }
+
+ for (i = 0; i < plat->kconnected_rows; i++) {
+ keypad->ske_rows[i] = *plat->gpio_input_pins;
+ keypad->ske_cols[i] = *plat->gpio_output_pins;
keypad->gpio_input_irq[i] =
NOMADIK_GPIO_TO_IRQ(keypad->ske_rows[i]);
}
- for (i = 0; i < keypad->board->krow; i++) {
+ for (i = 0; i < keypad->board->kconnected_rows; i++) {
ret = request_threaded_irq(keypad->gpio_input_irq[i],
NULL, ske_keypad_gpio_irq,
IRQF_TRIGGER_FALLING | IRQF_NO_SUSPEND,
@@ -697,7 +773,7 @@ static int __init ske_keypad_probe(struct platform_device *pdev)
if (ret) {
dev_err(&pdev->dev, "allocate gpio irq %d failed\n",
keypad->gpio_input_irq[i]);
- goto out_unregisterinput;
+ goto out_freemem_keys;
}
enable_irq_wake(keypad->gpio_input_irq[i]);
}
@@ -706,7 +782,7 @@ static int __init ske_keypad_probe(struct platform_device *pdev)
IRQF_ONESHOT, "ske-keypad", keypad);
if (ret) {
dev_err(&pdev->dev, "allocate irq %d failed\n", keypad->irq);
- goto out_unregisterinput;
+ goto out_freemem_keys;
}
/* sysfs implementation for dynamic enable/disable the input event */
@@ -728,6 +804,16 @@ static int __init ske_keypad_probe(struct platform_device *pdev)
out_free_irq:
free_irq(keypad->irq, keypad);
+out_freemem_keys:
+ for (i = 0; i < plat->krow; i++)
+ kfree(keypad->keys[i]);
+ kfree(keypad->keys);
+out_freemem_cols:
+ kfree(keypad->ske_cols);
+out_freemem_rows:
+ kfree(keypad->ske_rows);
+out_freemem_input_irq:
+ kfree(keypad->gpio_input_irq);
out_unregisterinput:
input_unregister_device(input);
input = NULL;
@@ -759,6 +845,13 @@ static int __devexit ske_keypad_remove(struct platform_device *pdev)
cancel_delayed_work_sync(&keypad->work);
cancel_delayed_work_sync(&keypad->scan_work);
+ for (i = 0; i < keypad->board->krow; i++)
+ kfree(keypad->keys[i]);
+ kfree(keypad->keys);
+ kfree(keypad->ske_cols);
+ kfree(keypad->ske_rows);
+ kfree(keypad->gpio_input_irq);
+
input_unregister_device(keypad->input);
sysfs_remove_group(&pdev->dev.kobj, &ske_attr_group);
if (keypad->enable)