diff options
| -rw-r--r-- | drivers/input/keyboard/pxa27x_keypad.c | 224 | ||||
| -rw-r--r-- | include/asm-arm/arch-pxa/pxa27x_keypad.h | 21 |
2 files changed, 184 insertions, 61 deletions
diff --git a/drivers/input/keyboard/pxa27x_keypad.c b/drivers/input/keyboard/pxa27x_keypad.c index 43fb63d68122..8de35b0500f3 100644 --- a/drivers/input/keyboard/pxa27x_keypad.c +++ b/drivers/input/keyboard/pxa27x_keypad.c | |||
| @@ -37,20 +37,120 @@ | |||
| 37 | 37 | ||
| 38 | #define DRIVER_NAME "pxa27x-keypad" | 38 | #define DRIVER_NAME "pxa27x-keypad" |
| 39 | 39 | ||
| 40 | #define KPASMKP(col) (col/2 == 0 ? KPASMKP0 : \ | 40 | #define KPAS_MUKP(n) (((n) >> 26) & 0x1f) |
| 41 | col/2 == 1 ? KPASMKP1 : \ | 41 | #define KPAS_RP(n) (((n) >> 4) & 0xf) |
| 42 | col/2 == 2 ? KPASMKP2 : KPASMKP3) | 42 | #define KPAS_CP(n) ((n) & 0xf) |
| 43 | #define KPASMKPx_MKC(row, col) (1 << (row + 16 * (col % 2))) | ||
| 44 | 43 | ||
| 45 | static struct clk *pxa27x_keypad_clk; | 44 | #define KPASMKP_MKC_MASK (0xff) |
| 45 | |||
| 46 | #define MAX_MATRIX_KEY_NUM (8 * 8) | ||
| 47 | |||
| 48 | struct pxa27x_keypad { | ||
| 49 | struct pxa27x_keypad_platform_data *pdata; | ||
| 50 | |||
| 51 | struct clk *clk; | ||
| 52 | struct input_dev *input_dev; | ||
| 53 | |||
| 54 | /* matrix key code map */ | ||
| 55 | unsigned int matrix_keycodes[MAX_MATRIX_KEY_NUM]; | ||
| 56 | |||
| 57 | /* state row bits of each column scan */ | ||
| 58 | uint32_t matrix_key_state[MAX_MATRIX_KEY_COLS]; | ||
| 59 | }; | ||
| 60 | |||
| 61 | static void pxa27x_keypad_build_keycode(struct pxa27x_keypad *keypad) | ||
| 62 | { | ||
| 63 | struct pxa27x_keypad_platform_data *pdata = keypad->pdata; | ||
| 64 | struct input_dev *input_dev = keypad->input_dev; | ||
| 65 | unsigned int *key; | ||
| 66 | int i; | ||
| 67 | |||
| 68 | key = &pdata->matrix_key_map[0]; | ||
| 69 | for (i = 0; i < pdata->matrix_key_map_size; i++, key++) { | ||
| 70 | int row = ((*key) >> 28) & 0xf; | ||
| 71 | int col = ((*key) >> 24) & 0xf; | ||
| 72 | int code = (*key) & 0xffffff; | ||
| 73 | |||
| 74 | keypad->matrix_keycodes[(row << 3) + col] = code; | ||
| 75 | set_bit(code, input_dev->keybit); | ||
| 76 | } | ||
| 77 | } | ||
| 78 | |||
| 79 | static inline unsigned int lookup_matrix_keycode( | ||
| 80 | struct pxa27x_keypad *keypad, int row, int col) | ||
| 81 | { | ||
| 82 | return keypad->matrix_keycodes[(row << 3) + col]; | ||
| 83 | } | ||
| 84 | |||
| 85 | static void pxa27x_keypad_scan_matrix(struct pxa27x_keypad *keypad) | ||
| 86 | { | ||
| 87 | struct pxa27x_keypad_platform_data *pdata = keypad->pdata; | ||
| 88 | int row, col, num_keys_pressed = 0; | ||
| 89 | uint32_t new_state[MAX_MATRIX_KEY_COLS]; | ||
| 90 | uint32_t kpas = KPAS; | ||
| 91 | |||
| 92 | num_keys_pressed = KPAS_MUKP(kpas); | ||
| 93 | |||
| 94 | memset(new_state, 0, sizeof(new_state)); | ||
| 95 | |||
| 96 | if (num_keys_pressed == 0) | ||
| 97 | goto scan; | ||
| 98 | |||
| 99 | if (num_keys_pressed == 1) { | ||
| 100 | col = KPAS_CP(kpas); | ||
| 101 | row = KPAS_RP(kpas); | ||
| 102 | |||
| 103 | /* if invalid row/col, treat as no key pressed */ | ||
| 104 | if (col >= pdata->matrix_key_cols || | ||
| 105 | row >= pdata->matrix_key_rows) | ||
| 106 | goto scan; | ||
| 107 | |||
| 108 | new_state[col] = (1 << row); | ||
| 109 | goto scan; | ||
| 110 | } | ||
| 111 | |||
| 112 | if (num_keys_pressed > 1) { | ||
| 113 | uint32_t kpasmkp0 = KPASMKP0; | ||
| 114 | uint32_t kpasmkp1 = KPASMKP1; | ||
| 115 | uint32_t kpasmkp2 = KPASMKP2; | ||
| 116 | uint32_t kpasmkp3 = KPASMKP3; | ||
| 117 | |||
| 118 | new_state[0] = kpasmkp0 & KPASMKP_MKC_MASK; | ||
| 119 | new_state[1] = (kpasmkp0 >> 16) & KPASMKP_MKC_MASK; | ||
| 120 | new_state[2] = kpasmkp1 & KPASMKP_MKC_MASK; | ||
| 121 | new_state[3] = (kpasmkp1 >> 16) & KPASMKP_MKC_MASK; | ||
| 122 | new_state[4] = kpasmkp2 & KPASMKP_MKC_MASK; | ||
| 123 | new_state[5] = (kpasmkp2 >> 16) & KPASMKP_MKC_MASK; | ||
| 124 | new_state[6] = kpasmkp3 & KPASMKP_MKC_MASK; | ||
| 125 | new_state[7] = (kpasmkp3 >> 16) & KPASMKP_MKC_MASK; | ||
| 126 | } | ||
| 127 | scan: | ||
| 128 | for (col = 0; col < pdata->matrix_key_cols; col++) { | ||
| 129 | uint32_t bits_changed; | ||
| 130 | |||
| 131 | bits_changed = keypad->matrix_key_state[col] ^ new_state[col]; | ||
| 132 | if (bits_changed == 0) | ||
| 133 | continue; | ||
| 134 | |||
| 135 | for (row = 0; row < pdata->matrix_key_rows; row++) { | ||
| 136 | if ((bits_changed & (1 << row)) == 0) | ||
| 137 | continue; | ||
| 138 | |||
| 139 | input_report_key(keypad->input_dev, | ||
| 140 | lookup_matrix_keycode(keypad, row, col), | ||
| 141 | new_state[col] & (1 << row)); | ||
| 142 | } | ||
| 143 | } | ||
| 144 | input_sync(keypad->input_dev); | ||
| 145 | memcpy(keypad->matrix_key_state, new_state, sizeof(new_state)); | ||
| 146 | } | ||
| 46 | 147 | ||
| 47 | static irqreturn_t pxa27x_keypad_irq_handler(int irq, void *dev_id) | 148 | static irqreturn_t pxa27x_keypad_irq_handler(int irq, void *dev_id) |
| 48 | { | 149 | { |
| 49 | struct platform_device *pdev = dev_id; | 150 | struct pxa27x_keypad *keypad = dev_id; |
| 50 | struct pxa27x_keypad_platform_data *pdata = pdev->dev.platform_data; | 151 | struct input_dev *input_dev = keypad->input_dev; |
| 51 | struct input_dev *input_dev = platform_get_drvdata(pdev); | ||
| 52 | unsigned long kpc = KPC; | 152 | unsigned long kpc = KPC; |
| 53 | int p, row, col, rel; | 153 | int rel; |
| 54 | 154 | ||
| 55 | if (kpc & KPC_DI) { | 155 | if (kpc & KPC_DI) { |
| 56 | unsigned long kpdk = KPDK; | 156 | unsigned long kpdk = KPDK; |
| @@ -75,26 +175,16 @@ static irqreturn_t pxa27x_keypad_irq_handler(int irq, void *dev_id) | |||
| 75 | } | 175 | } |
| 76 | } | 176 | } |
| 77 | 177 | ||
| 78 | if (kpc & KPC_MI) { | 178 | if (kpc & KPC_MI) |
| 79 | /* report the status of every button */ | 179 | pxa27x_keypad_scan_matrix(keypad); |
| 80 | for (row = 0; row < pdata->nr_rows; row++) { | ||
| 81 | for (col = 0; col < pdata->nr_cols; col++) { | ||
| 82 | p = KPASMKP(col) & KPASMKPx_MKC(row, col) ? | ||
| 83 | 1 : 0; | ||
| 84 | pr_debug("keycode %x - pressed %x\n", | ||
| 85 | pdata->keycodes[row][col], p); | ||
| 86 | input_report_key(input_dev, | ||
| 87 | pdata->keycodes[row][col], p); | ||
| 88 | } | ||
| 89 | } | ||
| 90 | input_sync(input_dev); | ||
| 91 | } | ||
| 92 | 180 | ||
| 93 | return IRQ_HANDLED; | 181 | return IRQ_HANDLED; |
| 94 | } | 182 | } |
| 95 | 183 | ||
| 96 | static int pxa27x_keypad_open(struct input_dev *dev) | 184 | static int pxa27x_keypad_open(struct input_dev *dev) |
| 97 | { | 185 | { |
| 186 | struct pxa27x_keypad *keypad = input_get_drvdata(dev); | ||
| 187 | |||
| 98 | /* Set keypad control register */ | 188 | /* Set keypad control register */ |
| 99 | KPC |= (KPC_ASACT | | 189 | KPC |= (KPC_ASACT | |
| 100 | KPC_MS_ALL | | 190 | KPC_MS_ALL | |
| @@ -108,21 +198,24 @@ static int pxa27x_keypad_open(struct input_dev *dev) | |||
| 108 | KPREC = 0x7F; | 198 | KPREC = 0x7F; |
| 109 | 199 | ||
| 110 | /* Enable unit clock */ | 200 | /* Enable unit clock */ |
| 111 | clk_enable(pxa27x_keypad_clk); | 201 | clk_enable(keypad->clk); |
| 112 | 202 | ||
| 113 | return 0; | 203 | return 0; |
| 114 | } | 204 | } |
| 115 | 205 | ||
| 116 | static void pxa27x_keypad_close(struct input_dev *dev) | 206 | static void pxa27x_keypad_close(struct input_dev *dev) |
| 117 | { | 207 | { |
| 208 | struct pxa27x_keypad *keypad = input_get_drvdata(dev); | ||
| 209 | |||
| 118 | /* Disable clock unit */ | 210 | /* Disable clock unit */ |
| 119 | clk_disable(pxa27x_keypad_clk); | 211 | clk_disable(keypad->clk); |
| 120 | } | 212 | } |
| 121 | 213 | ||
| 122 | #ifdef CONFIG_PM | 214 | #ifdef CONFIG_PM |
| 123 | static int pxa27x_keypad_suspend(struct platform_device *pdev, pm_message_t state) | 215 | static int pxa27x_keypad_suspend(struct platform_device *pdev, pm_message_t state) |
| 124 | { | 216 | { |
| 125 | struct pxa27x_keypad_platform_data *pdata = pdev->dev.platform_data; | 217 | struct pxa27x_keypad *keypad = platform_get_drvdata(pdev); |
| 218 | struct pxa27x_keypad_platform_data *pdata = keypad->pdata; | ||
| 126 | 219 | ||
| 127 | /* Save controller status */ | 220 | /* Save controller status */ |
| 128 | pdata->reg_kpc = KPC; | 221 | pdata->reg_kpc = KPC; |
| @@ -133,8 +226,9 @@ static int pxa27x_keypad_suspend(struct platform_device *pdev, pm_message_t stat | |||
| 133 | 226 | ||
| 134 | static int pxa27x_keypad_resume(struct platform_device *pdev) | 227 | static int pxa27x_keypad_resume(struct platform_device *pdev) |
| 135 | { | 228 | { |
| 136 | struct pxa27x_keypad_platform_data *pdata = pdev->dev.platform_data; | 229 | struct pxa27x_keypad *keypad = platform_get_drvdata(pdev); |
| 137 | struct input_dev *input_dev = platform_get_drvdata(pdev); | 230 | struct pxa27x_keypad_platform_data *pdata = keypad->pdata; |
| 231 | struct input_dev *input_dev = keypad->input_dev; | ||
| 138 | 232 | ||
| 139 | mutex_lock(&input_dev->mutex); | 233 | mutex_lock(&input_dev->mutex); |
| 140 | 234 | ||
| @@ -144,8 +238,7 @@ static int pxa27x_keypad_resume(struct platform_device *pdev) | |||
| 144 | KPREC = pdata->reg_kprec; | 238 | KPREC = pdata->reg_kprec; |
| 145 | 239 | ||
| 146 | /* Enable unit clock */ | 240 | /* Enable unit clock */ |
| 147 | clk_disable(pxa27x_keypad_clk); | 241 | clk_enable(keypad->clk); |
| 148 | clk_enable(pxa27x_keypad_clk); | ||
| 149 | } | 242 | } |
| 150 | 243 | ||
| 151 | mutex_unlock(&input_dev->mutex); | 244 | mutex_unlock(&input_dev->mutex); |
| @@ -159,22 +252,36 @@ static int pxa27x_keypad_resume(struct platform_device *pdev) | |||
| 159 | 252 | ||
| 160 | static int __devinit pxa27x_keypad_probe(struct platform_device *pdev) | 253 | static int __devinit pxa27x_keypad_probe(struct platform_device *pdev) |
| 161 | { | 254 | { |
| 162 | struct pxa27x_keypad_platform_data *pdata = pdev->dev.platform_data; | 255 | struct pxa27x_keypad *keypad; |
| 163 | struct input_dev *input_dev; | 256 | struct input_dev *input_dev; |
| 164 | int i, row, col, error; | 257 | int col, error; |
| 165 | 258 | ||
| 166 | pxa27x_keypad_clk = clk_get(&pdev->dev, "KBDCLK"); | 259 | keypad = kzalloc(sizeof(struct pxa27x_keypad), GFP_KERNEL); |
| 167 | if (IS_ERR(pxa27x_keypad_clk)) { | 260 | if (keypad == NULL) { |
| 168 | error = PTR_ERR(pxa27x_keypad_clk); | 261 | dev_err(&pdev->dev, "failed to allocate driver data\n"); |
| 169 | goto err_clk; | 262 | return -ENOMEM; |
| 263 | } | ||
| 264 | |||
| 265 | keypad->pdata = pdev->dev.platform_data; | ||
| 266 | if (keypad->pdata == NULL) { | ||
| 267 | dev_err(&pdev->dev, "no platform data defined\n"); | ||
| 268 | error = -EINVAL; | ||
| 269 | goto failed_free; | ||
| 270 | } | ||
| 271 | |||
| 272 | keypad->clk = clk_get(&pdev->dev, "KBDCLK"); | ||
| 273 | if (IS_ERR(keypad->clk)) { | ||
| 274 | dev_err(&pdev->dev, "failed to get keypad clock\n"); | ||
| 275 | error = PTR_ERR(keypad->clk); | ||
| 276 | goto failed_free; | ||
| 170 | } | 277 | } |
| 171 | 278 | ||
| 172 | /* Create and register the input driver. */ | 279 | /* Create and register the input driver. */ |
| 173 | input_dev = input_allocate_device(); | 280 | input_dev = input_allocate_device(); |
| 174 | if (!input_dev) { | 281 | if (!input_dev) { |
| 175 | printk(KERN_ERR "Cannot request keypad device\n"); | 282 | dev_err(&pdev->dev, "failed to allocate input device\n"); |
| 176 | error = -ENOMEM; | 283 | error = -ENOMEM; |
| 177 | goto err_alloc; | 284 | goto failed_put_clk; |
| 178 | } | 285 | } |
| 179 | 286 | ||
| 180 | input_dev->name = DRIVER_NAME; | 287 | input_dev->name = DRIVER_NAME; |
| @@ -183,25 +290,23 @@ static int __devinit pxa27x_keypad_probe(struct platform_device *pdev) | |||
| 183 | input_dev->close = pxa27x_keypad_close; | 290 | input_dev->close = pxa27x_keypad_close; |
| 184 | input_dev->dev.parent = &pdev->dev; | 291 | input_dev->dev.parent = &pdev->dev; |
| 185 | 292 | ||
| 293 | keypad->input_dev = input_dev; | ||
| 294 | input_set_drvdata(input_dev, keypad); | ||
| 295 | |||
| 186 | input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP) | | 296 | input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP) | |
| 187 | BIT_MASK(EV_REL); | 297 | BIT_MASK(EV_REL); |
| 188 | input_dev->relbit[BIT_WORD(REL_WHEEL)] = BIT_MASK(REL_WHEEL); | 298 | input_dev->relbit[BIT_WORD(REL_WHEEL)] = BIT_MASK(REL_WHEEL); |
| 189 | for (row = 0; row < pdata->nr_rows; row++) { | 299 | |
| 190 | for (col = 0; col < pdata->nr_cols; col++) { | 300 | pxa27x_keypad_build_keycode(keypad); |
| 191 | int code = pdata->keycodes[row][col]; | ||
| 192 | if (code > 0) | ||
| 193 | set_bit(code, input_dev->keybit); | ||
| 194 | } | ||
| 195 | } | ||
| 196 | 301 | ||
| 197 | error = request_irq(IRQ_KEYPAD, pxa27x_keypad_irq_handler, IRQF_DISABLED, | 302 | error = request_irq(IRQ_KEYPAD, pxa27x_keypad_irq_handler, IRQF_DISABLED, |
| 198 | DRIVER_NAME, pdev); | 303 | DRIVER_NAME, keypad); |
| 199 | if (error) { | 304 | if (error) { |
| 200 | printk(KERN_ERR "Cannot request keypad IRQ\n"); | 305 | printk(KERN_ERR "Cannot request keypad IRQ\n"); |
| 201 | goto err_free_dev; | 306 | goto err_free_dev; |
| 202 | } | 307 | } |
| 203 | 308 | ||
| 204 | platform_set_drvdata(pdev, input_dev); | 309 | platform_set_drvdata(pdev, keypad); |
| 205 | 310 | ||
| 206 | /* Register the input device */ | 311 | /* Register the input device */ |
| 207 | error = input_register_device(input_dev); | 312 | error = input_register_device(input_dev); |
| @@ -212,10 +317,10 @@ static int __devinit pxa27x_keypad_probe(struct platform_device *pdev) | |||
| 212 | * Store rows/cols info into keyboard registers. | 317 | * Store rows/cols info into keyboard registers. |
| 213 | */ | 318 | */ |
| 214 | 319 | ||
| 215 | KPC |= (pdata->nr_rows - 1) << 26; | 320 | KPC |= (keypad->pdata->matrix_key_rows - 1) << 26; |
| 216 | KPC |= (pdata->nr_cols - 1) << 23; | 321 | KPC |= (keypad->pdata->matrix_key_cols - 1) << 23; |
| 217 | 322 | ||
| 218 | for (col = 0; col < pdata->nr_cols; col++) | 323 | for (col = 0; col < keypad->pdata->matrix_key_cols; col++) |
| 219 | KPC |= KPC_MS0 << col; | 324 | KPC |= KPC_MS0 << col; |
| 220 | 325 | ||
| 221 | return 0; | 326 | return 0; |
| @@ -225,21 +330,26 @@ static int __devinit pxa27x_keypad_probe(struct platform_device *pdev) | |||
| 225 | free_irq(IRQ_KEYPAD, pdev); | 330 | free_irq(IRQ_KEYPAD, pdev); |
| 226 | err_free_dev: | 331 | err_free_dev: |
| 227 | input_free_device(input_dev); | 332 | input_free_device(input_dev); |
| 228 | err_alloc: | 333 | failed_put_clk: |
| 229 | clk_put(pxa27x_keypad_clk); | 334 | clk_put(keypad->clk); |
| 230 | err_clk: | 335 | failed_free: |
| 336 | kfree(keypad); | ||
| 231 | return error; | 337 | return error; |
| 232 | } | 338 | } |
| 233 | 339 | ||
| 234 | static int __devexit pxa27x_keypad_remove(struct platform_device *pdev) | 340 | static int __devexit pxa27x_keypad_remove(struct platform_device *pdev) |
| 235 | { | 341 | { |
| 236 | struct input_dev *input_dev = platform_get_drvdata(pdev); | 342 | struct pxa27x_keypad *keypad = platform_get_drvdata(pdev); |
| 237 | 343 | ||
| 238 | input_unregister_device(input_dev); | ||
| 239 | free_irq(IRQ_KEYPAD, pdev); | 344 | free_irq(IRQ_KEYPAD, pdev); |
| 240 | clk_put(pxa27x_keypad_clk); | ||
| 241 | platform_set_drvdata(pdev, NULL); | ||
| 242 | 345 | ||
| 346 | clk_disable(keypad->clk); | ||
| 347 | clk_put(keypad->clk); | ||
| 348 | |||
| 349 | input_unregister_device(keypad->input_dev); | ||
| 350 | |||
| 351 | platform_set_drvdata(pdev, NULL); | ||
| 352 | kfree(keypad); | ||
| 243 | return 0; | 353 | return 0; |
| 244 | } | 354 | } |
| 245 | 355 | ||
diff --git a/include/asm-arm/arch-pxa/pxa27x_keypad.h b/include/asm-arm/arch-pxa/pxa27x_keypad.h index ef17db6d791e..1b1bf9fe6d81 100644 --- a/include/asm-arm/arch-pxa/pxa27x_keypad.h +++ b/include/asm-arm/arch-pxa/pxa27x_keypad.h | |||
| @@ -1,12 +1,25 @@ | |||
| 1 | #define PXAKBD_MAXROW 8 | 1 | #ifndef __ASM_ARCH_PXA27x_KEYPAD_H |
| 2 | #define PXAKBD_MAXCOL 8 | 2 | #define __ASM_ARCH_PXA27x_KEYPAD_H |
| 3 | |||
| 4 | #include <linux/input.h> | ||
| 5 | |||
| 6 | #define MAX_MATRIX_KEY_ROWS (8) | ||
| 7 | #define MAX_MATRIX_KEY_COLS (8) | ||
| 3 | 8 | ||
| 4 | struct pxa27x_keypad_platform_data { | 9 | struct pxa27x_keypad_platform_data { |
| 5 | int nr_rows, nr_cols; | 10 | |
| 6 | int keycodes[PXAKBD_MAXROW][PXAKBD_MAXCOL]; | 11 | /* code map for the matrix keys */ |
| 12 | unsigned int matrix_key_rows; | ||
| 13 | unsigned int matrix_key_cols; | ||
| 14 | unsigned int *matrix_key_map; | ||
| 15 | int matrix_key_map_size; | ||
| 7 | 16 | ||
| 8 | #ifdef CONFIG_PM | 17 | #ifdef CONFIG_PM |
| 9 | u32 reg_kpc; | 18 | u32 reg_kpc; |
| 10 | u32 reg_kprec; | 19 | u32 reg_kprec; |
| 11 | #endif | 20 | #endif |
| 12 | }; | 21 | }; |
| 22 | |||
| 23 | #define KEY(row, col, val) (((row) << 28) | ((col) << 24) | (val)) | ||
| 24 | |||
| 25 | #endif /* __ASM_ARCH_PXA27x_KEYPAD_H */ | ||
