Commit 0fffed27f92d9d7a34de9fe017b7082b5958bb93
Committed by
Dmitry Torokhov
1 parent
1ca56e513a
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>
Showing 4 changed files with 544 additions and 0 deletions Side-by-side Diff
arch/arm/plat-samsung/include/plat/keypad.h
1 | +/* | |
2 | + * Samsung Platform - Keypad platform data definitions | |
3 | + * | |
4 | + * Copyright (C) 2010 Samsung Electronics Co.Ltd | |
5 | + * Author: Joonyoung Shim <jy0922.shim@samsung.com> | |
6 | + * | |
7 | + * This program is free software; you can redistribute it and/or modify it | |
8 | + * under the terms of the GNU General Public License as published by the | |
9 | + * Free Software Foundation; either version 2 of the License, or (at your | |
10 | + * option) any later version. | |
11 | + */ | |
12 | + | |
13 | +#ifndef __PLAT_SAMSUNG_KEYPAD_H | |
14 | +#define __PLAT_SAMSUNG_KEYPAD_H | |
15 | + | |
16 | +#include <linux/input/matrix_keypad.h> | |
17 | + | |
18 | +#define SAMSUNG_MAX_ROWS 8 | |
19 | +#define SAMSUNG_MAX_COLS 8 | |
20 | + | |
21 | +/** | |
22 | + * struct samsung_keypad_platdata - Platform device data for Samsung Keypad. | |
23 | + * @keymap_data: pointer to &matrix_keymap_data. | |
24 | + * @rows: number of keypad row supported. | |
25 | + * @cols: number of keypad col supported. | |
26 | + * @no_autorepeat: disable key autorepeat. | |
27 | + * @wakeup: controls whether the device should be set up as wakeup source. | |
28 | + * @cfg_gpio: configure the GPIO. | |
29 | + * | |
30 | + * Initialisation data specific to either the machine or the platform | |
31 | + * for the device driver to use or call-back when configuring gpio. | |
32 | + */ | |
33 | +struct samsung_keypad_platdata { | |
34 | + const struct matrix_keymap_data *keymap_data; | |
35 | + unsigned int rows; | |
36 | + unsigned int cols; | |
37 | + bool no_autorepeat; | |
38 | + bool wakeup; | |
39 | + | |
40 | + void (*cfg_gpio)(unsigned int rows, unsigned int cols); | |
41 | +}; | |
42 | + | |
43 | +#endif /* __PLAT_SAMSUNG_KEYPAD_H */ |
drivers/input/keyboard/Kconfig
... | ... | @@ -354,6 +354,15 @@ |
354 | 354 | To compile this driver as a module, choose M here: the |
355 | 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 | 366 | config KEYBOARD_STOWAWAY |
358 | 367 | tristate "Stowaway keyboard" |
359 | 368 | select SERIO |
drivers/input/keyboard/Makefile
... | ... | @@ -33,6 +33,7 @@ |
33 | 33 | obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x_keypad.o |
34 | 34 | obj-$(CONFIG_KEYBOARD_PXA930_ROTARY) += pxa930_rotary.o |
35 | 35 | obj-$(CONFIG_KEYBOARD_QT2160) += qt2160.o |
36 | +obj-$(CONFIG_KEYBOARD_SAMSUNG) += samsung-keypad.o | |
36 | 37 | obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o |
37 | 38 | obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o |
38 | 39 | obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o |
drivers/input/keyboard/samsung-keypad.c
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"); |