]> Pileus Git - ~andy/linux/blobdiff - drivers/input/keyboard/pxa27x_keypad.c
Input: pxa27x_keypad - enable rotary encoders and direct keys
[~andy/linux] / drivers / input / keyboard / pxa27x_keypad.c
index 8de35b0500f324717d30658e918433f2209067dc..cd25b34144914480672a0b31e8a767229d0b29c8 100644 (file)
 
 #define DRIVER_NAME            "pxa27x-keypad"
 
+#define KPC_MKRN(n)    ((((n) & 0x7) - 1) << 26) /* matrix key row number */
+#define KPC_MKCN(n)    ((((n) & 0x7) - 1) << 23) /* matrix key column number */
+#define KPC_DKN(n)     ((((n) & 0x7) - 1) << 6)  /* direct key number */
+
+#define KPDK_DKP        (0x1 << 31)
+#define KPDK_DK(n)     ((n) & 0xff)
+
 #define KPAS_MUKP(n)           (((n) >> 26) & 0x1f)
 #define KPAS_RP(n)             (((n) >> 4) & 0xf)
 #define KPAS_CP(n)             ((n) & 0xf)
@@ -56,6 +63,13 @@ struct pxa27x_keypad {
 
        /* state row bits of each column scan */
        uint32_t matrix_key_state[MAX_MATRIX_KEY_COLS];
+       uint32_t direct_key_state;
+
+       unsigned int direct_key_mask;
+
+       int rotary_rel_code[2];
+       int rotary_up_key[2];
+       int rotary_down_key[2];
 };
 
 static void pxa27x_keypad_build_keycode(struct pxa27x_keypad *keypad)
@@ -74,6 +88,25 @@ static void pxa27x_keypad_build_keycode(struct pxa27x_keypad *keypad)
                keypad->matrix_keycodes[(row << 3) + col] = code;
                set_bit(code, input_dev->keybit);
        }
+
+       keypad->rotary_up_key[0] = pdata->rotary0_up_key;
+       keypad->rotary_up_key[1] = pdata->rotary1_up_key;
+       keypad->rotary_down_key[0] = pdata->rotary0_down_key;
+       keypad->rotary_down_key[1] = pdata->rotary1_down_key;
+       keypad->rotary_rel_code[0] = pdata->rotary0_rel_code;
+       keypad->rotary_rel_code[1] = pdata->rotary1_rel_code;
+
+       if (pdata->rotary0_up_key && pdata->rotary0_down_key) {
+               set_bit(pdata->rotary0_up_key, input_dev->keybit);
+               set_bit(pdata->rotary0_down_key, input_dev->keybit);
+       } else
+               set_bit(pdata->rotary0_rel_code, input_dev->relbit);
+
+       if (pdata->rotary1_up_key && pdata->rotary1_down_key) {
+               set_bit(pdata->rotary1_up_key, input_dev->keybit);
+               set_bit(pdata->rotary1_down_key, input_dev->keybit);
+       } else
+               set_bit(pdata->rotary1_rel_code, input_dev->relbit);
 }
 
 static inline unsigned int lookup_matrix_keycode(
@@ -145,35 +178,94 @@ scan:
        memcpy(keypad->matrix_key_state, new_state, sizeof(new_state));
 }
 
+#define DEFAULT_KPREC  (0x007f007f)
+
+static inline int rotary_delta(uint32_t kprec)
+{
+       if (kprec & KPREC_OF0)
+               return (kprec & 0xff) + 0x7f;
+       else if (kprec & KPREC_UF0)
+               return (kprec & 0xff) - 0x7f - 0xff;
+       else
+               return (kprec & 0xff) - 0x7f;
+}
+
+static void report_rotary_event(struct pxa27x_keypad *keypad, int r, int delta)
+{
+       struct input_dev *dev = keypad->input_dev;
+
+       if (delta == 0)
+               return;
+
+       if (keypad->rotary_up_key[r] && keypad->rotary_down_key[r]) {
+               int keycode = (delta > 0) ? keypad->rotary_up_key[r] :
+                                           keypad->rotary_down_key[r];
+
+               /* simulate a press-n-release */
+               input_report_key(dev, keycode, 1);
+               input_sync(dev);
+               input_report_key(dev, keycode, 0);
+               input_sync(dev);
+       } else {
+               input_report_rel(dev, keypad->rotary_rel_code[r], delta);
+               input_sync(dev);
+       }
+}
+
+static void pxa27x_keypad_scan_rotary(struct pxa27x_keypad *keypad)
+{
+       struct pxa27x_keypad_platform_data *pdata = keypad->pdata;
+       uint32_t kprec;
+
+       /* read and reset to default count value */
+       kprec = KPREC;
+       KPREC = DEFAULT_KPREC;
+
+       if (pdata->enable_rotary0)
+               report_rotary_event(keypad, 0, rotary_delta(kprec));
+
+       if (pdata->enable_rotary1)
+               report_rotary_event(keypad, 1, rotary_delta(kprec >> 16));
+}
+
+static void pxa27x_keypad_scan_direct(struct pxa27x_keypad *keypad)
+{
+       struct pxa27x_keypad_platform_data *pdata = keypad->pdata;
+       unsigned int new_state;
+       uint32_t kpdk, bits_changed;
+       int i;
+
+       kpdk = KPDK;
+
+       if (pdata->enable_rotary0 || pdata->enable_rotary1)
+               pxa27x_keypad_scan_rotary(keypad);
+
+       if (pdata->direct_key_map == NULL)
+               return;
+
+       new_state = KPDK_DK(kpdk) & keypad->direct_key_mask;
+       bits_changed = keypad->direct_key_state ^ new_state;
+
+       if (bits_changed == 0)
+               return;
+
+       for (i = 0; i < pdata->direct_key_num; i++) {
+               if (bits_changed & (1 << i))
+                       input_report_key(keypad->input_dev,
+                                       pdata->direct_key_map[i],
+                                       (new_state & (1 << i)));
+       }
+       input_sync(keypad->input_dev);
+       keypad->direct_key_state = new_state;
+}
+
 static irqreturn_t pxa27x_keypad_irq_handler(int irq, void *dev_id)
 {
        struct pxa27x_keypad *keypad = dev_id;
-       struct input_dev *input_dev = keypad->input_dev;
        unsigned long kpc = KPC;
-       int rel;
-
-       if (kpc & KPC_DI) {
-               unsigned long kpdk = KPDK;
-
-               if (!(kpdk & KPDK_DKP)) {
-                       /* better luck next time */
-               } else if (kpc & KPC_REE0) {
-                       unsigned long kprec = KPREC;
-                       KPREC = 0x7f;
-
-                       if (kprec & KPREC_OF0)
-                               rel = (kprec & 0xff) + 0x7f;
-                       else if (kprec & KPREC_UF0)
-                               rel = (kprec & 0xff) - 0x7f - 0xff;
-                       else
-                               rel = (kprec & 0xff) - 0x7f;
-
-                       if (rel) {
-                               input_report_rel(input_dev, REL_WHEEL, rel);
-                               input_sync(input_dev);
-                       }
-               }
-       }
+
+       if (kpc & KPC_DI)
+               pxa27x_keypad_scan_direct(keypad);
 
        if (kpc & KPC_MI)
                pxa27x_keypad_scan_matrix(keypad);
@@ -181,24 +273,52 @@ static irqreturn_t pxa27x_keypad_irq_handler(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
-static int pxa27x_keypad_open(struct input_dev *dev)
+static void pxa27x_keypad_config(struct pxa27x_keypad *keypad)
 {
-       struct pxa27x_keypad *keypad = input_get_drvdata(dev);
+       struct pxa27x_keypad_platform_data *pdata = keypad->pdata;
+       unsigned int mask = 0, direct_key_num = 0;
+       unsigned long kpc = 0;
+
+       /* enable matrix keys with automatic scan */
+       if (pdata->matrix_key_rows && pdata->matrix_key_cols) {
+               kpc |= KPC_ASACT | KPC_MIE | KPC_ME | KPC_MS_ALL;
+               kpc |= KPC_MKRN(pdata->matrix_key_rows) |
+                      KPC_MKCN(pdata->matrix_key_cols);
+       }
+
+       /* enable rotary key, debounce interval same as direct keys */
+       if (pdata->enable_rotary0) {
+               mask |= 0x03;
+               direct_key_num = 2;
+               kpc |= KPC_REE0;
+       }
+
+       if (pdata->enable_rotary1) {
+               mask |= 0x0c;
+               direct_key_num = 4;
+               kpc |= KPC_REE1;
+       }
+
+       if (pdata->direct_key_num > direct_key_num)
+               direct_key_num = pdata->direct_key_num;
 
-       /* Set keypad control register */
-       KPC |= (KPC_ASACT |
-               KPC_MS_ALL |
-               (2 << 6) | KPC_REE0 | KPC_DK_DEB_SEL |
-               KPC_ME | KPC_MIE | KPC_DE | KPC_DIE);
+       keypad->direct_key_mask = ((2 << direct_key_num) - 1) & ~mask;
 
-       KPC &= ~KPC_AS;         /* disable automatic scan */
-       KPC &= ~KPC_IMKP;       /* do not ignore multiple keypresses */
+       /* enable direct key */
+       if (direct_key_num)
+               kpc |= KPC_DE | KPC_DIE | KPC_DKN(direct_key_num);
+
+       KPC = kpc | KPC_RE_ZERO_DEB;
+       KPREC = DEFAULT_KPREC;
+}
 
-       /* Set rotary count to mid-point value */
-       KPREC = 0x7F;
+static int pxa27x_keypad_open(struct input_dev *dev)
+{
+       struct pxa27x_keypad *keypad = input_get_drvdata(dev);
 
        /* Enable unit clock */
        clk_enable(keypad->clk);
+       pxa27x_keypad_config(keypad);
 
        return 0;
 }
@@ -215,30 +335,22 @@ static void pxa27x_keypad_close(struct input_dev *dev)
 static int pxa27x_keypad_suspend(struct platform_device *pdev, pm_message_t state)
 {
        struct pxa27x_keypad *keypad = platform_get_drvdata(pdev);
-       struct pxa27x_keypad_platform_data *pdata = keypad->pdata;
-
-       /* Save controller status */
-       pdata->reg_kpc = KPC;
-       pdata->reg_kprec = KPREC;
 
+       clk_disable(keypad->clk);
        return 0;
 }
 
 static int pxa27x_keypad_resume(struct platform_device *pdev)
 {
        struct pxa27x_keypad *keypad = platform_get_drvdata(pdev);
-       struct pxa27x_keypad_platform_data *pdata = keypad->pdata;
        struct input_dev *input_dev = keypad->input_dev;
 
        mutex_lock(&input_dev->mutex);
 
        if (input_dev->users) {
-               /* Restore controller status */
-               KPC = pdata->reg_kpc;
-               KPREC = pdata->reg_kprec;
-
                /* Enable unit clock */
                clk_enable(keypad->clk);
+               pxa27x_keypad_config(keypad);
        }
 
        mutex_unlock(&input_dev->mutex);
@@ -254,7 +366,7 @@ static int __devinit pxa27x_keypad_probe(struct platform_device *pdev)
 {
        struct pxa27x_keypad *keypad;
        struct input_dev *input_dev;
-       int col, error;
+       int error;
 
        keypad = kzalloc(sizeof(struct pxa27x_keypad), GFP_KERNEL);
        if (keypad == NULL) {
@@ -295,7 +407,6 @@ static int __devinit pxa27x_keypad_probe(struct platform_device *pdev)
 
        input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP) |
                BIT_MASK(EV_REL);
-       input_dev->relbit[BIT_WORD(REL_WHEEL)] = BIT_MASK(REL_WHEEL);
 
        pxa27x_keypad_build_keycode(keypad);
 
@@ -313,16 +424,6 @@ static int __devinit pxa27x_keypad_probe(struct platform_device *pdev)
        if (error)
                goto err_free_irq;
 
-       /*
-        * Store rows/cols info into keyboard registers.
-        */
-
-       KPC |= (keypad->pdata->matrix_key_rows - 1) << 26;
-       KPC |= (keypad->pdata->matrix_key_cols - 1) << 23;
-
-       for (col = 0; col < keypad->pdata->matrix_key_cols; col++)
-               KPC |= KPC_MS0 << col;
-
        return 0;
 
  err_free_irq: