Input: matrix_keypad - add support for clustered irq
authorLuotao Fu <l.fu@pengutronix.de>
Thu, 10 Jun 2010 19:05:23 +0000 (12:05 -0700)
committerDmitry Torokhov <dmitry.torokhov@gmail.com>
Thu, 10 Jun 2010 19:33:59 +0000 (12:33 -0700)
This one adds support of a combined irq source for the whole matrix keypad.
This can be useful if all rows and columns of the keypad are e.g. connected
to a GPIO expander, which only has one interrupt line for all events on
every single GPIO.

Signed-off-by: Luotao Fu <l.fu@pengutronix.de>
Acked-by: Eric Miao <eric.y.miao@gmail.com>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
drivers/input/keyboard/matrix_keypad.c
include/linux/input/matrix_keypad.h

index b443e08..b02e426 100644 (file)
@@ -37,6 +37,7 @@ struct matrix_keypad {
        spinlock_t lock;
        bool scan_pending;
        bool stopped;
+       bool gpio_all_disabled;
 };
 
 /*
@@ -87,8 +88,12 @@ static void enable_row_irqs(struct matrix_keypad *keypad)
        const struct matrix_keypad_platform_data *pdata = keypad->pdata;
        int i;
 
-       for (i = 0; i < pdata->num_row_gpios; i++)
-               enable_irq(gpio_to_irq(pdata->row_gpios[i]));
+       if (pdata->clustered_irq > 0)
+               enable_irq(pdata->clustered_irq);
+       else {
+               for (i = 0; i < pdata->num_row_gpios; i++)
+                       enable_irq(gpio_to_irq(pdata->row_gpios[i]));
+       }
 }
 
 static void disable_row_irqs(struct matrix_keypad *keypad)
@@ -96,8 +101,12 @@ static void disable_row_irqs(struct matrix_keypad *keypad)
        const struct matrix_keypad_platform_data *pdata = keypad->pdata;
        int i;
 
-       for (i = 0; i < pdata->num_row_gpios; i++)
-               disable_irq_nosync(gpio_to_irq(pdata->row_gpios[i]));
+       if (pdata->clustered_irq > 0)
+               disable_irq_nosync(pdata->clustered_irq);
+       else {
+               for (i = 0; i < pdata->num_row_gpios; i++)
+                       disable_irq_nosync(gpio_to_irq(pdata->row_gpios[i]));
+       }
 }
 
 /*
@@ -216,45 +225,69 @@ static void matrix_keypad_stop(struct input_dev *dev)
 }
 
 #ifdef CONFIG_PM
-static int matrix_keypad_suspend(struct device *dev)
+static void matrix_keypad_enable_wakeup(struct matrix_keypad *keypad)
 {
-       struct platform_device *pdev = to_platform_device(dev);
-       struct matrix_keypad *keypad = platform_get_drvdata(pdev);
        const struct matrix_keypad_platform_data *pdata = keypad->pdata;
+       unsigned int gpio;
        int i;
 
-       matrix_keypad_stop(keypad->input_dev);
+       if (pdata->clustered_irq > 0) {
+               if (enable_irq_wake(pdata->clustered_irq) == 0)
+                       keypad->gpio_all_disabled = true;
+       } else {
 
-       if (device_may_wakeup(&pdev->dev)) {
                for (i = 0; i < pdata->num_row_gpios; i++) {
                        if (!test_bit(i, keypad->disabled_gpios)) {
-                               unsigned int gpio = pdata->row_gpios[i];
+                               gpio = pdata->row_gpios[i];
 
                                if (enable_irq_wake(gpio_to_irq(gpio)) == 0)
                                        __set_bit(i, keypad->disabled_gpios);
                        }
                }
        }
-
-       return 0;
 }
 
-static int matrix_keypad_resume(struct device *dev)
+static void matrix_keypad_disable_wakeup(struct matrix_keypad *keypad)
 {
-       struct platform_device *pdev = to_platform_device(dev);
-       struct matrix_keypad *keypad = platform_get_drvdata(pdev);
        const struct matrix_keypad_platform_data *pdata = keypad->pdata;
+       unsigned int gpio;
        int i;
 
-       if (device_may_wakeup(&pdev->dev)) {
+       if (pdata->clustered_irq > 0) {
+               if (keypad->gpio_all_disabled) {
+                       disable_irq_wake(pdata->clustered_irq);
+                       keypad->gpio_all_disabled = false;
+               }
+       } else {
                for (i = 0; i < pdata->num_row_gpios; i++) {
                        if (test_and_clear_bit(i, keypad->disabled_gpios)) {
-                               unsigned int gpio = pdata->row_gpios[i];
-
+                               gpio = pdata->row_gpios[i];
                                disable_irq_wake(gpio_to_irq(gpio));
                        }
                }
        }
+}
+
+static int matrix_keypad_suspend(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct matrix_keypad *keypad = platform_get_drvdata(pdev);
+
+       matrix_keypad_stop(keypad->input_dev);
+
+       if (device_may_wakeup(&pdev->dev))
+               matrix_keypad_enable_wakeup(keypad);
+
+       return 0;
+}
+
+static int matrix_keypad_resume(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct matrix_keypad *keypad = platform_get_drvdata(pdev);
+
+       if (device_may_wakeup(&pdev->dev))
+               matrix_keypad_disable_wakeup(keypad);
 
        matrix_keypad_start(keypad->input_dev);
 
@@ -296,17 +329,31 @@ static int __devinit init_matrix_gpio(struct platform_device *pdev,
                gpio_direction_input(pdata->row_gpios[i]);
        }
 
-       for (i = 0; i < pdata->num_row_gpios; i++) {
-               err = request_irq(gpio_to_irq(pdata->row_gpios[i]),
+       if (pdata->clustered_irq > 0) {
+               err = request_irq(pdata->clustered_irq,
                                matrix_keypad_interrupt,
-                               IRQF_DISABLED |
-                               IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+                               pdata->clustered_irq_flags,
                                "matrix-keypad", keypad);
                if (err) {
                        dev_err(&pdev->dev,
-                               "Unable to acquire interrupt for GPIO line %i\n",
-                               pdata->row_gpios[i]);
-                       goto err_free_irqs;
+                               "Unable to acquire clustered interrupt\n");
+                       goto err_free_rows;
+               }
+       } else {
+               for (i = 0; i < pdata->num_row_gpios; i++) {
+                       err = request_irq(gpio_to_irq(pdata->row_gpios[i]),
+                                       matrix_keypad_interrupt,
+                                       IRQF_DISABLED |
+                                       IRQF_TRIGGER_RISING |
+                                       IRQF_TRIGGER_FALLING,
+                                       "matrix-keypad", keypad);
+                       if (err) {
+                               dev_err(&pdev->dev,
+                                       "Unable to acquire interrupt "
+                                       "for GPIO line %i\n",
+                                       pdata->row_gpios[i]);
+                               goto err_free_irqs;
+                       }
                }
        }
 
@@ -418,11 +465,16 @@ static int __devexit matrix_keypad_remove(struct platform_device *pdev)
 
        device_init_wakeup(&pdev->dev, 0);
 
-       for (i = 0; i < pdata->num_row_gpios; i++) {
-               free_irq(gpio_to_irq(pdata->row_gpios[i]), keypad);
-               gpio_free(pdata->row_gpios[i]);
+       if (pdata->clustered_irq > 0) {
+               free_irq(pdata->clustered_irq, keypad);
+       } else {
+               for (i = 0; i < pdata->num_row_gpios; i++)
+                       free_irq(gpio_to_irq(pdata->row_gpios[i]), keypad);
        }
 
+       for (i = 0; i < pdata->num_row_gpios; i++)
+               gpio_free(pdata->row_gpios[i]);
+
        for (i = 0; i < pdata->num_col_gpios; i++)
                gpio_free(pdata->col_gpios[i]);
 
index c964cd7..80352ad 100644 (file)
@@ -41,6 +41,9 @@ struct matrix_keymap_data {
  * @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
+ * @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
  * @active_low: gpio polarity
  * @wakeup: controls whether the device should be set up as wakeup
  *     source
@@ -63,6 +66,9 @@ struct matrix_keypad_platform_data {
        /* key debounce interval in milli-second */
        unsigned int    debounce_ms;
 
+       unsigned int    clustered_irq;
+       unsigned int    clustered_irq_flags;
+
        bool            active_low;
        bool            wakeup;
        bool            no_autorepeat;