diff options
author | Joonyoung Shim <jy0922.shim@samsung.com> | 2010-07-21 03:45:10 -0400 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2010-07-21 03:49:06 -0400 |
commit | 0fffed27f92d9d7a34de9fe017b7082b5958bb93 (patch) | |
tree | d1ca545f052935ee87206ab6f84adb027bb56560 /drivers/input | |
parent | 1ca56e513a9fd356d5a9e0de45dbe0e189e00386 (diff) |
Input: samsung-keypad - Add samsung keypad driver
This patch adds support for keypad driver running on Samsung cpus. This
driver is tested on GONI and Aquila board using S5PC110 cpu.
[ch.naveen@samsung.com: tested on SMDK6410, SMDKC100, and SMDKV210]
Signed-off-by: Joonyoung Shim <jy0922.shim@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Tested-by: Naveen Krishna Ch <ch.naveen@samsung.com>
Acked-by: Kukjin Kim <kgene.kim@samsung.com>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
Diffstat (limited to 'drivers/input')
-rw-r--r-- | drivers/input/keyboard/Kconfig | 9 | ||||
-rw-r--r-- | drivers/input/keyboard/Makefile | 1 | ||||
-rw-r--r-- | drivers/input/keyboard/samsung-keypad.c | 491 |
3 files changed, 501 insertions, 0 deletions
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index c7480934ceeb..451c38bc5807 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig | |||
@@ -354,6 +354,15 @@ config KEYBOARD_PXA930_ROTARY | |||
354 | To compile this driver as a module, choose M here: the | 354 | To compile this driver as a module, choose M here: the |
355 | module will be called pxa930_rotary. | 355 | module will be called pxa930_rotary. |
356 | 356 | ||
357 | config KEYBOARD_SAMSUNG | ||
358 | tristate "Samsung keypad support" | ||
359 | depends on SAMSUNG_DEV_KEYPAD | ||
360 | help | ||
361 | Say Y here if you want to use the Samsung keypad. | ||
362 | |||
363 | To compile this driver as a module, choose M here: the | ||
364 | module will be called samsung-keypad. | ||
365 | |||
357 | config KEYBOARD_STOWAWAY | 366 | config KEYBOARD_STOWAWAY |
358 | tristate "Stowaway keyboard" | 367 | tristate "Stowaway keyboard" |
359 | select SERIO | 368 | select SERIO |
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 0a16674ed3d2..1a66d5f1ca8b 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile | |||
@@ -33,6 +33,7 @@ obj-$(CONFIG_KEYBOARD_OPENCORES) += opencores-kbd.o | |||
33 | obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x_keypad.o | 33 | obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x_keypad.o |
34 | obj-$(CONFIG_KEYBOARD_PXA930_ROTARY) += pxa930_rotary.o | 34 | obj-$(CONFIG_KEYBOARD_PXA930_ROTARY) += pxa930_rotary.o |
35 | obj-$(CONFIG_KEYBOARD_QT2160) += qt2160.o | 35 | obj-$(CONFIG_KEYBOARD_QT2160) += qt2160.o |
36 | obj-$(CONFIG_KEYBOARD_SAMSUNG) += samsung-keypad.o | ||
36 | obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o | 37 | obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o |
37 | obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o | 38 | obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o |
38 | obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o | 39 | obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o |
diff --git a/drivers/input/keyboard/samsung-keypad.c b/drivers/input/keyboard/samsung-keypad.c new file mode 100644 index 000000000000..f689f49e3109 --- /dev/null +++ b/drivers/input/keyboard/samsung-keypad.c | |||
@@ -0,0 +1,491 @@ | |||
1 | /* | ||
2 | * Samsung keypad driver | ||
3 | * | ||
4 | * Copyright (C) 2010 Samsung Electronics Co.Ltd | ||
5 | * Author: Joonyoung Shim <jy0922.shim@samsung.com> | ||
6 | * Author: Donghwa Lee <dh09.lee@samsung.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify it | ||
9 | * under the terms of the GNU General Public License as published by the | ||
10 | * Free Software Foundation; either version 2 of the License, or (at your | ||
11 | * option) any later version. | ||
12 | */ | ||
13 | |||
14 | #include <linux/clk.h> | ||
15 | #include <linux/delay.h> | ||
16 | #include <linux/err.h> | ||
17 | #include <linux/init.h> | ||
18 | #include <linux/input.h> | ||
19 | #include <linux/interrupt.h> | ||
20 | #include <linux/io.h> | ||
21 | #include <linux/module.h> | ||
22 | #include <linux/platform_device.h> | ||
23 | #include <linux/slab.h> | ||
24 | #include <linux/sched.h> | ||
25 | #include <plat/keypad.h> | ||
26 | |||
27 | #define SAMSUNG_KEYIFCON 0x00 | ||
28 | #define SAMSUNG_KEYIFSTSCLR 0x04 | ||
29 | #define SAMSUNG_KEYIFCOL 0x08 | ||
30 | #define SAMSUNG_KEYIFROW 0x0c | ||
31 | #define SAMSUNG_KEYIFFC 0x10 | ||
32 | |||
33 | /* SAMSUNG_KEYIFCON */ | ||
34 | #define SAMSUNG_KEYIFCON_INT_F_EN (1 << 0) | ||
35 | #define SAMSUNG_KEYIFCON_INT_R_EN (1 << 1) | ||
36 | #define SAMSUNG_KEYIFCON_DF_EN (1 << 2) | ||
37 | #define SAMSUNG_KEYIFCON_FC_EN (1 << 3) | ||
38 | #define SAMSUNG_KEYIFCON_WAKEUPEN (1 << 4) | ||
39 | |||
40 | /* SAMSUNG_KEYIFSTSCLR */ | ||
41 | #define SAMSUNG_KEYIFSTSCLR_P_INT_MASK (0xff << 0) | ||
42 | #define SAMSUNG_KEYIFSTSCLR_R_INT_MASK (0xff << 8) | ||
43 | #define SAMSUNG_KEYIFSTSCLR_R_INT_OFFSET 8 | ||
44 | #define S5PV210_KEYIFSTSCLR_P_INT_MASK (0x3fff << 0) | ||
45 | #define S5PV210_KEYIFSTSCLR_R_INT_MASK (0x3fff << 16) | ||
46 | #define S5PV210_KEYIFSTSCLR_R_INT_OFFSET 16 | ||
47 | |||
48 | /* SAMSUNG_KEYIFCOL */ | ||
49 | #define SAMSUNG_KEYIFCOL_MASK (0xff << 0) | ||
50 | #define S5PV210_KEYIFCOLEN_MASK (0xff << 8) | ||
51 | |||
52 | /* SAMSUNG_KEYIFROW */ | ||
53 | #define SAMSUNG_KEYIFROW_MASK (0xff << 0) | ||
54 | #define S5PV210_KEYIFROW_MASK (0x3fff << 0) | ||
55 | |||
56 | /* SAMSUNG_KEYIFFC */ | ||
57 | #define SAMSUNG_KEYIFFC_MASK (0x3ff << 0) | ||
58 | |||
59 | enum samsung_keypad_type { | ||
60 | KEYPAD_TYPE_SAMSUNG, | ||
61 | KEYPAD_TYPE_S5PV210, | ||
62 | }; | ||
63 | |||
64 | struct samsung_keypad { | ||
65 | struct input_dev *input_dev; | ||
66 | struct clk *clk; | ||
67 | void __iomem *base; | ||
68 | wait_queue_head_t wait; | ||
69 | bool stopped; | ||
70 | int irq; | ||
71 | unsigned int row_shift; | ||
72 | unsigned int rows; | ||
73 | unsigned int cols; | ||
74 | unsigned int row_state[SAMSUNG_MAX_COLS]; | ||
75 | unsigned short keycodes[]; | ||
76 | }; | ||
77 | |||
78 | static int samsung_keypad_is_s5pv210(struct device *dev) | ||
79 | { | ||
80 | struct platform_device *pdev = to_platform_device(dev); | ||
81 | enum samsung_keypad_type type = | ||
82 | platform_get_device_id(pdev)->driver_data; | ||
83 | |||
84 | return type == KEYPAD_TYPE_S5PV210; | ||
85 | } | ||
86 | |||
87 | static void samsung_keypad_scan(struct samsung_keypad *keypad, | ||
88 | unsigned int *row_state) | ||
89 | { | ||
90 | struct device *dev = keypad->input_dev->dev.parent; | ||
91 | unsigned int col; | ||
92 | unsigned int val; | ||
93 | |||
94 | for (col = 0; col < keypad->cols; col++) { | ||
95 | if (samsung_keypad_is_s5pv210(dev)) { | ||
96 | val = S5PV210_KEYIFCOLEN_MASK; | ||
97 | val &= ~(1 << col) << 8; | ||
98 | } else { | ||
99 | val = SAMSUNG_KEYIFCOL_MASK; | ||
100 | val &= ~(1 << col); | ||
101 | } | ||
102 | |||
103 | writel(val, keypad->base + SAMSUNG_KEYIFCOL); | ||
104 | mdelay(1); | ||
105 | |||
106 | val = readl(keypad->base + SAMSUNG_KEYIFROW); | ||
107 | row_state[col] = ~val & ((1 << keypad->rows) - 1); | ||
108 | } | ||
109 | |||
110 | /* KEYIFCOL reg clear */ | ||
111 | writel(0, keypad->base + SAMSUNG_KEYIFCOL); | ||
112 | } | ||
113 | |||
114 | static bool samsung_keypad_report(struct samsung_keypad *keypad, | ||
115 | unsigned int *row_state) | ||
116 | { | ||
117 | struct input_dev *input_dev = keypad->input_dev; | ||
118 | unsigned int changed; | ||
119 | unsigned int pressed; | ||
120 | unsigned int key_down = 0; | ||
121 | unsigned int val; | ||
122 | unsigned int col, row; | ||
123 | |||
124 | for (col = 0; col < keypad->cols; col++) { | ||
125 | changed = row_state[col] ^ keypad->row_state[col]; | ||
126 | key_down |= row_state[col]; | ||
127 | if (!changed) | ||
128 | continue; | ||
129 | |||
130 | for (row = 0; row < keypad->rows; row++) { | ||
131 | if (!(changed & (1 << row))) | ||
132 | continue; | ||
133 | |||
134 | pressed = row_state[col] & (1 << row); | ||
135 | |||
136 | dev_dbg(&keypad->input_dev->dev, | ||
137 | "key %s, row: %d, col: %d\n", | ||
138 | pressed ? "pressed" : "released", row, col); | ||
139 | |||
140 | val = MATRIX_SCAN_CODE(row, col, keypad->row_shift); | ||
141 | |||
142 | input_event(input_dev, EV_MSC, MSC_SCAN, val); | ||
143 | input_report_key(input_dev, | ||
144 | keypad->keycodes[val], pressed); | ||
145 | } | ||
146 | input_sync(keypad->input_dev); | ||
147 | } | ||
148 | |||
149 | memcpy(keypad->row_state, row_state, sizeof(keypad->row_state)); | ||
150 | |||
151 | return key_down; | ||
152 | } | ||
153 | |||
154 | static irqreturn_t samsung_keypad_irq(int irq, void *dev_id) | ||
155 | { | ||
156 | struct samsung_keypad *keypad = dev_id; | ||
157 | unsigned int row_state[SAMSUNG_MAX_COLS]; | ||
158 | unsigned int val; | ||
159 | bool key_down; | ||
160 | |||
161 | do { | ||
162 | val = readl(keypad->base + SAMSUNG_KEYIFSTSCLR); | ||
163 | /* Clear interrupt. */ | ||
164 | writel(~0x0, keypad->base + SAMSUNG_KEYIFSTSCLR); | ||
165 | |||
166 | samsung_keypad_scan(keypad, row_state); | ||
167 | |||
168 | key_down = samsung_keypad_report(keypad, row_state); | ||
169 | if (key_down) | ||
170 | wait_event_timeout(keypad->wait, keypad->stopped, | ||
171 | msecs_to_jiffies(50)); | ||
172 | |||
173 | } while (key_down && !keypad->stopped); | ||
174 | |||
175 | return IRQ_HANDLED; | ||
176 | } | ||
177 | |||
178 | static void samsung_keypad_start(struct samsung_keypad *keypad) | ||
179 | { | ||
180 | unsigned int val; | ||
181 | |||
182 | /* Tell IRQ thread that it may poll the device. */ | ||
183 | keypad->stopped = false; | ||
184 | |||
185 | clk_enable(keypad->clk); | ||
186 | |||
187 | /* Enable interrupt bits. */ | ||
188 | val = readl(keypad->base + SAMSUNG_KEYIFCON); | ||
189 | val |= SAMSUNG_KEYIFCON_INT_F_EN | SAMSUNG_KEYIFCON_INT_R_EN; | ||
190 | writel(val, keypad->base + SAMSUNG_KEYIFCON); | ||
191 | |||
192 | /* KEYIFCOL reg clear. */ | ||
193 | writel(0, keypad->base + SAMSUNG_KEYIFCOL); | ||
194 | } | ||
195 | |||
196 | static void samsung_keypad_stop(struct samsung_keypad *keypad) | ||
197 | { | ||
198 | unsigned int val; | ||
199 | |||
200 | /* Signal IRQ thread to stop polling and disable the handler. */ | ||
201 | keypad->stopped = true; | ||
202 | wake_up(&keypad->wait); | ||
203 | disable_irq(keypad->irq); | ||
204 | |||
205 | /* Clear interrupt. */ | ||
206 | writel(~0x0, keypad->base + SAMSUNG_KEYIFSTSCLR); | ||
207 | |||
208 | /* Disable interrupt bits. */ | ||
209 | val = readl(keypad->base + SAMSUNG_KEYIFCON); | ||
210 | val &= ~(SAMSUNG_KEYIFCON_INT_F_EN | SAMSUNG_KEYIFCON_INT_R_EN); | ||
211 | writel(val, keypad->base + SAMSUNG_KEYIFCON); | ||
212 | |||
213 | clk_disable(keypad->clk); | ||
214 | |||
215 | /* | ||
216 | * Now that chip should not generate interrupts we can safely | ||
217 | * re-enable the handler. | ||
218 | */ | ||
219 | enable_irq(keypad->irq); | ||
220 | } | ||
221 | |||
222 | static int samsung_keypad_open(struct input_dev *input_dev) | ||
223 | { | ||
224 | struct samsung_keypad *keypad = input_get_drvdata(input_dev); | ||
225 | |||
226 | samsung_keypad_start(keypad); | ||
227 | |||
228 | return 0; | ||
229 | } | ||
230 | |||
231 | static void samsung_keypad_close(struct input_dev *input_dev) | ||
232 | { | ||
233 | struct samsung_keypad *keypad = input_get_drvdata(input_dev); | ||
234 | |||
235 | samsung_keypad_stop(keypad); | ||
236 | } | ||
237 | |||
238 | static int __devinit samsung_keypad_probe(struct platform_device *pdev) | ||
239 | { | ||
240 | const struct samsung_keypad_platdata *pdata; | ||
241 | const struct matrix_keymap_data *keymap_data; | ||
242 | struct samsung_keypad *keypad; | ||
243 | struct resource *res; | ||
244 | struct input_dev *input_dev; | ||
245 | unsigned int row_shift; | ||
246 | unsigned int keymap_size; | ||
247 | int error; | ||
248 | |||
249 | pdata = pdev->dev.platform_data; | ||
250 | if (!pdata) { | ||
251 | dev_err(&pdev->dev, "no platform data defined\n"); | ||
252 | return -EINVAL; | ||
253 | } | ||
254 | |||
255 | keymap_data = pdata->keymap_data; | ||
256 | if (!keymap_data) { | ||
257 | dev_err(&pdev->dev, "no keymap data defined\n"); | ||
258 | return -EINVAL; | ||
259 | } | ||
260 | |||
261 | if (!pdata->rows || pdata->rows > SAMSUNG_MAX_ROWS) | ||
262 | return -EINVAL; | ||
263 | |||
264 | if (!pdata->cols || pdata->cols > SAMSUNG_MAX_COLS) | ||
265 | return -EINVAL; | ||
266 | |||
267 | /* initialize the gpio */ | ||
268 | if (pdata->cfg_gpio) | ||
269 | pdata->cfg_gpio(pdata->rows, pdata->cols); | ||
270 | |||
271 | row_shift = get_count_order(pdata->cols); | ||
272 | keymap_size = (pdata->rows << row_shift) * sizeof(keypad->keycodes[0]); | ||
273 | |||
274 | keypad = kzalloc(sizeof(*keypad) + keymap_size, GFP_KERNEL); | ||
275 | input_dev = input_allocate_device(); | ||
276 | if (!keypad || !input_dev) { | ||
277 | error = -ENOMEM; | ||
278 | goto err_free_mem; | ||
279 | } | ||
280 | |||
281 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
282 | if (!res) { | ||
283 | error = -ENODEV; | ||
284 | goto err_free_mem; | ||
285 | } | ||
286 | |||
287 | keypad->base = ioremap(res->start, resource_size(res)); | ||
288 | if (!keypad->base) { | ||
289 | error = -EBUSY; | ||
290 | goto err_free_mem; | ||
291 | } | ||
292 | |||
293 | keypad->clk = clk_get(&pdev->dev, "keypad"); | ||
294 | if (IS_ERR(keypad->clk)) { | ||
295 | dev_err(&pdev->dev, "failed to get keypad clk\n"); | ||
296 | error = PTR_ERR(keypad->clk); | ||
297 | goto err_unmap_base; | ||
298 | } | ||
299 | |||
300 | keypad->input_dev = input_dev; | ||
301 | keypad->row_shift = row_shift; | ||
302 | keypad->rows = pdata->rows; | ||
303 | keypad->cols = pdata->cols; | ||
304 | init_waitqueue_head(&keypad->wait); | ||
305 | |||
306 | input_dev->name = pdev->name; | ||
307 | input_dev->id.bustype = BUS_HOST; | ||
308 | input_dev->dev.parent = &pdev->dev; | ||
309 | input_set_drvdata(input_dev, keypad); | ||
310 | |||
311 | input_dev->open = samsung_keypad_open; | ||
312 | input_dev->close = samsung_keypad_close; | ||
313 | |||
314 | input_dev->evbit[0] = BIT_MASK(EV_KEY); | ||
315 | if (!pdata->no_autorepeat) | ||
316 | input_dev->evbit[0] |= BIT_MASK(EV_REP); | ||
317 | |||
318 | input_set_capability(input_dev, EV_MSC, MSC_SCAN); | ||
319 | |||
320 | input_dev->keycode = keypad->keycodes; | ||
321 | input_dev->keycodesize = sizeof(keypad->keycodes[0]); | ||
322 | input_dev->keycodemax = pdata->rows << row_shift; | ||
323 | |||
324 | matrix_keypad_build_keymap(keymap_data, row_shift, | ||
325 | input_dev->keycode, input_dev->keybit); | ||
326 | |||
327 | keypad->irq = platform_get_irq(pdev, 0); | ||
328 | if (keypad->irq < 0) { | ||
329 | error = keypad->irq; | ||
330 | goto err_put_clk; | ||
331 | } | ||
332 | |||
333 | error = request_threaded_irq(keypad->irq, NULL, samsung_keypad_irq, | ||
334 | IRQF_ONESHOT, dev_name(&pdev->dev), keypad); | ||
335 | if (error) { | ||
336 | dev_err(&pdev->dev, "failed to register keypad interrupt\n"); | ||
337 | goto err_put_clk; | ||
338 | } | ||
339 | |||
340 | error = input_register_device(keypad->input_dev); | ||
341 | if (error) | ||
342 | goto err_free_irq; | ||
343 | |||
344 | device_init_wakeup(&pdev->dev, pdata->wakeup); | ||
345 | platform_set_drvdata(pdev, keypad); | ||
346 | return 0; | ||
347 | |||
348 | err_free_irq: | ||
349 | free_irq(keypad->irq, keypad); | ||
350 | err_put_clk: | ||
351 | clk_put(keypad->clk); | ||
352 | err_unmap_base: | ||
353 | iounmap(keypad->base); | ||
354 | err_free_mem: | ||
355 | input_free_device(input_dev); | ||
356 | kfree(keypad); | ||
357 | |||
358 | return error; | ||
359 | } | ||
360 | |||
361 | static int __devexit samsung_keypad_remove(struct platform_device *pdev) | ||
362 | { | ||
363 | struct samsung_keypad *keypad = platform_get_drvdata(pdev); | ||
364 | |||
365 | device_init_wakeup(&pdev->dev, 0); | ||
366 | platform_set_drvdata(pdev, NULL); | ||
367 | |||
368 | input_unregister_device(keypad->input_dev); | ||
369 | |||
370 | /* | ||
371 | * It is safe to free IRQ after unregistering device because | ||
372 | * samsung_keypad_close will shut off interrupts. | ||
373 | */ | ||
374 | free_irq(keypad->irq, keypad); | ||
375 | |||
376 | clk_put(keypad->clk); | ||
377 | |||
378 | iounmap(keypad->base); | ||
379 | kfree(keypad); | ||
380 | |||
381 | return 0; | ||
382 | } | ||
383 | |||
384 | #ifdef CONFIG_PM | ||
385 | static void samsung_keypad_toggle_wakeup(struct samsung_keypad *keypad, | ||
386 | bool enable) | ||
387 | { | ||
388 | struct device *dev = keypad->input_dev->dev.parent; | ||
389 | unsigned int val; | ||
390 | |||
391 | clk_enable(keypad->clk); | ||
392 | |||
393 | val = readl(keypad->base + SAMSUNG_KEYIFCON); | ||
394 | if (enable) { | ||
395 | val |= SAMSUNG_KEYIFCON_WAKEUPEN; | ||
396 | if (device_may_wakeup(dev)) | ||
397 | enable_irq_wake(keypad->irq); | ||
398 | } else { | ||
399 | val &= ~SAMSUNG_KEYIFCON_WAKEUPEN; | ||
400 | if (device_may_wakeup(dev)) | ||
401 | disable_irq_wake(keypad->irq); | ||
402 | } | ||
403 | writel(val, keypad->base + SAMSUNG_KEYIFCON); | ||
404 | |||
405 | clk_disable(keypad->clk); | ||
406 | } | ||
407 | |||
408 | static int samsung_keypad_suspend(struct device *dev) | ||
409 | { | ||
410 | struct platform_device *pdev = to_platform_device(dev); | ||
411 | struct samsung_keypad *keypad = platform_get_drvdata(pdev); | ||
412 | struct input_dev *input_dev = keypad->input_dev; | ||
413 | |||
414 | mutex_lock(&input_dev->mutex); | ||
415 | |||
416 | if (input_dev->users) | ||
417 | samsung_keypad_stop(keypad); | ||
418 | |||
419 | samsung_keypad_toggle_wakeup(keypad, true); | ||
420 | |||
421 | mutex_unlock(&input_dev->mutex); | ||
422 | |||
423 | return 0; | ||
424 | } | ||
425 | |||
426 | static int samsung_keypad_resume(struct device *dev) | ||
427 | { | ||
428 | struct platform_device *pdev = to_platform_device(dev); | ||
429 | struct samsung_keypad *keypad = platform_get_drvdata(pdev); | ||
430 | struct input_dev *input_dev = keypad->input_dev; | ||
431 | |||
432 | mutex_lock(&input_dev->mutex); | ||
433 | |||
434 | samsung_keypad_toggle_wakeup(keypad, false); | ||
435 | |||
436 | if (input_dev->users) | ||
437 | samsung_keypad_start(keypad); | ||
438 | |||
439 | mutex_unlock(&input_dev->mutex); | ||
440 | |||
441 | return 0; | ||
442 | } | ||
443 | |||
444 | static const struct dev_pm_ops samsung_keypad_pm_ops = { | ||
445 | .suspend = samsung_keypad_suspend, | ||
446 | .resume = samsung_keypad_resume, | ||
447 | }; | ||
448 | #endif | ||
449 | |||
450 | static struct platform_device_id samsung_keypad_driver_ids[] = { | ||
451 | { | ||
452 | .name = "samsung-keypad", | ||
453 | .driver_data = KEYPAD_TYPE_SAMSUNG, | ||
454 | }, { | ||
455 | .name = "s5pv210-keypad", | ||
456 | .driver_data = KEYPAD_TYPE_S5PV210, | ||
457 | }, | ||
458 | { }, | ||
459 | }; | ||
460 | MODULE_DEVICE_TABLE(platform, samsung_keypad_driver_ids); | ||
461 | |||
462 | static struct platform_driver samsung_keypad_driver = { | ||
463 | .probe = samsung_keypad_probe, | ||
464 | .remove = __devexit_p(samsung_keypad_remove), | ||
465 | .driver = { | ||
466 | .name = "samsung-keypad", | ||
467 | .owner = THIS_MODULE, | ||
468 | #ifdef CONFIG_PM | ||
469 | .pm = &samsung_keypad_pm_ops, | ||
470 | #endif | ||
471 | }, | ||
472 | .id_table = samsung_keypad_driver_ids, | ||
473 | }; | ||
474 | |||
475 | static int __init samsung_keypad_init(void) | ||
476 | { | ||
477 | return platform_driver_register(&samsung_keypad_driver); | ||
478 | } | ||
479 | module_init(samsung_keypad_init); | ||
480 | |||
481 | static void __exit samsung_keypad_exit(void) | ||
482 | { | ||
483 | platform_driver_unregister(&samsung_keypad_driver); | ||
484 | } | ||
485 | module_exit(samsung_keypad_exit); | ||
486 | |||
487 | MODULE_DESCRIPTION("Samsung keypad driver"); | ||
488 | MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>"); | ||
489 | MODULE_AUTHOR("Donghwa Lee <dh09.lee@samsung.com>"); | ||
490 | MODULE_LICENSE("GPL"); | ||
491 | MODULE_ALIAS("platform:samsung-keypad"); | ||