diff options
author | Chris Blair <chris.blair@stericsson.com> | 2011-12-05 10:01:21 +0100 |
---|---|---|
committer | Philippe Langlais <philippe.langlais@stericsson.com> | 2012-05-22 11:03:39 +0200 |
commit | dec331e62c2d978ebf73b9f0f04b53ace3c24907 (patch) | |
tree | 0bc05a64ddfe00c92504c1caa7f48d43ccf910ea | |
parent | 961a50cea677e23992aa0fc49ed33f50779ea328 (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.h | 4 | ||||
-rw-r--r-- | drivers/input/keyboard/nomadik-ske-keypad.c | 143 |
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) |