diff options
author | Eric Miao <eric.y.miao@gmail.com> | 2008-01-31 00:59:03 -0500 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2008-01-31 00:59:03 -0500 |
commit | 62059d9e912717abbfb875440621d935d091f289 (patch) | |
tree | 574d242a19fd54c5af19faea473448cb4efcf0ec /drivers/input | |
parent | d7416f9eaa5427f47648973aac3a65e7a0eeda04 (diff) | |
download | linux-62059d9e912717abbfb875440621d935d091f289.tar.gz linux-62059d9e912717abbfb875440621d935d091f289.tar.xz |
Input: pxa27x_keypad - enable rotary encoders and direct keys
1. Rotary encoder events can be configured either as relative events
as the legacy code does or as any specified key code, this is
useful on some platform which uses the rotary keys as
KEY_{UP/DOWN/LEFT/RIGHT}
2. Add support for direct keys, the corresponding keycodes for each
direct key can now be specified within the platform data
3. Remove the direct/rotary key detection code from the IRQ handler
to dedicated functions to improve readability
Signed-off-by: Eric Miao <eric.miao@marvell.com>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
Diffstat (limited to 'drivers/input')
-rw-r--r-- | drivers/input/keyboard/pxa27x_keypad.c | 163 |
1 files changed, 134 insertions, 29 deletions
diff --git a/drivers/input/keyboard/pxa27x_keypad.c b/drivers/input/keyboard/pxa27x_keypad.c index e9d4e227a009..cd25b3414491 100644 --- a/drivers/input/keyboard/pxa27x_keypad.c +++ b/drivers/input/keyboard/pxa27x_keypad.c @@ -41,6 +41,9 @@ #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) @@ -60,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) @@ -78,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( @@ -151,35 +180,92 @@ scan: #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); @@ -190,6 +276,7 @@ static irqreturn_t pxa27x_keypad_irq_handler(int irq, void *dev_id) static void pxa27x_keypad_config(struct pxa27x_keypad *keypad) { 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 */ @@ -199,10 +286,29 @@ static void pxa27x_keypad_config(struct pxa27x_keypad *keypad) KPC_MKCN(pdata->matrix_key_cols); } - /* FIXME: hardcoded to enable rotary 0 _only_ */ - kpc |= KPC_DKN(2) | KPC_REE0 | KPC_DI | KPC_DIE; + /* 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; + + keypad->direct_key_mask = ((2 << direct_key_num) - 1) & ~mask; + + /* enable direct key */ + if (direct_key_num) + kpc |= KPC_DE | KPC_DIE | KPC_DKN(direct_key_num); - KPC = kpc; + KPC = kpc | KPC_RE_ZERO_DEB; KPREC = DEFAULT_KPREC; } @@ -301,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); |