diff options
Diffstat (limited to 'drivers/input/mouse')
-rw-r--r-- | drivers/input/mouse/Kconfig | 18 | ||||
-rw-r--r-- | drivers/input/mouse/Makefile | 1 | ||||
-rw-r--r-- | drivers/input/mouse/alps.c | 31 | ||||
-rw-r--r-- | drivers/input/mouse/appletouch.c | 2 | ||||
-rw-r--r-- | drivers/input/mouse/lifebook.c | 25 | ||||
-rw-r--r-- | drivers/input/mouse/psmouse-base.c | 4 | ||||
-rw-r--r-- | drivers/input/mouse/synaptics.c | 28 | ||||
-rw-r--r-- | drivers/input/mouse/synaptics.h | 2 | ||||
-rw-r--r-- | drivers/input/mouse/synaptics_i2c.c | 676 |
9 files changed, 760 insertions, 27 deletions
diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig index c66cc3d08c2f..8a2c5b14c8d8 100644 --- a/drivers/input/mouse/Kconfig +++ b/drivers/input/mouse/Kconfig | |||
@@ -303,4 +303,22 @@ config MOUSE_MAPLE | |||
303 | To compile this driver as a module choose M here: the module will be | 303 | To compile this driver as a module choose M here: the module will be |
304 | called maplemouse. | 304 | called maplemouse. |
305 | 305 | ||
306 | config MOUSE_SYNAPTICS_I2C | ||
307 | tristate "Synaptics I2C Touchpad support" | ||
308 | depends on I2C | ||
309 | help | ||
310 | This driver supports Synaptics I2C touchpad controller on eXeda | ||
311 | mobile device. | ||
312 | The device will not work the synaptics X11 driver because | ||
313 | (i) it reports only relative coordinates and has no capabilities | ||
314 | to report absolute coordinates | ||
315 | (ii) the eXeda device itself uses Xfbdev as X Server and it does | ||
316 | not allow using xf86-input-* drivers. | ||
317 | |||
318 | Say y here if you have eXeda device and want to use a Synaptics | ||
319 | I2C Touchpad. | ||
320 | |||
321 | To compile this driver as a module, choose M here: the | ||
322 | module will be called synaptics_i2c. | ||
323 | |||
306 | endif | 324 | endif |
diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile index 472189468d67..010f265ec152 100644 --- a/drivers/input/mouse/Makefile +++ b/drivers/input/mouse/Makefile | |||
@@ -18,6 +18,7 @@ obj-$(CONFIG_MOUSE_PS2) += psmouse.o | |||
18 | obj-$(CONFIG_MOUSE_PXA930_TRKBALL) += pxa930_trkball.o | 18 | obj-$(CONFIG_MOUSE_PXA930_TRKBALL) += pxa930_trkball.o |
19 | obj-$(CONFIG_MOUSE_RISCPC) += rpcmouse.o | 19 | obj-$(CONFIG_MOUSE_RISCPC) += rpcmouse.o |
20 | obj-$(CONFIG_MOUSE_SERIAL) += sermouse.o | 20 | obj-$(CONFIG_MOUSE_SERIAL) += sermouse.o |
21 | obj-$(CONFIG_MOUSE_SYNAPTICS_I2C) += synaptics_i2c.o | ||
21 | obj-$(CONFIG_MOUSE_VSXXXAA) += vsxxxaa.o | 22 | obj-$(CONFIG_MOUSE_VSXXXAA) += vsxxxaa.o |
22 | 23 | ||
23 | psmouse-objs := psmouse-base.o synaptics.o | 24 | psmouse-objs := psmouse-base.o synaptics.o |
diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index daecc75c72e6..5547e2429fbe 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c | |||
@@ -38,25 +38,25 @@ | |||
38 | 38 | ||
39 | static const struct alps_model_info alps_model_data[] = { | 39 | static const struct alps_model_info alps_model_data[] = { |
40 | { { 0x32, 0x02, 0x14 }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* Toshiba Salellite Pro M10 */ | 40 | { { 0x32, 0x02, 0x14 }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* Toshiba Salellite Pro M10 */ |
41 | { { 0x33, 0x02, 0x0a }, 0x88, 0xf8, ALPS_OLDPROTO }, /* UMAX-530T */ | 41 | { { 0x33, 0x02, 0x0a }, 0x88, 0xf8, ALPS_OLDPROTO }, /* UMAX-530T */ |
42 | { { 0x53, 0x02, 0x0a }, 0xf8, 0xf8, 0 }, | 42 | { { 0x53, 0x02, 0x0a }, 0xf8, 0xf8, 0 }, |
43 | { { 0x53, 0x02, 0x14 }, 0xf8, 0xf8, 0 }, | 43 | { { 0x53, 0x02, 0x14 }, 0xf8, 0xf8, 0 }, |
44 | { { 0x60, 0x03, 0xc8 }, 0xf8, 0xf8, 0 }, /* HP ze1115 */ | 44 | { { 0x60, 0x03, 0xc8 }, 0xf8, 0xf8, 0 }, /* HP ze1115 */ |
45 | { { 0x63, 0x02, 0x0a }, 0xf8, 0xf8, 0 }, | 45 | { { 0x63, 0x02, 0x0a }, 0xf8, 0xf8, 0 }, |
46 | { { 0x63, 0x02, 0x14 }, 0xf8, 0xf8, 0 }, | 46 | { { 0x63, 0x02, 0x14 }, 0xf8, 0xf8, 0 }, |
47 | { { 0x63, 0x02, 0x28 }, 0xf8, 0xf8, ALPS_FW_BK_2 }, /* Fujitsu Siemens S6010 */ | 47 | { { 0x63, 0x02, 0x28 }, 0xf8, 0xf8, ALPS_FW_BK_2 }, /* Fujitsu Siemens S6010 */ |
48 | { { 0x63, 0x02, 0x3c }, 0x8f, 0x8f, ALPS_WHEEL }, /* Toshiba Satellite S2400-103 */ | 48 | { { 0x63, 0x02, 0x3c }, 0x8f, 0x8f, ALPS_WHEEL }, /* Toshiba Satellite S2400-103 */ |
49 | { { 0x63, 0x02, 0x50 }, 0xef, 0xef, ALPS_FW_BK_1 }, /* NEC Versa L320 */ | 49 | { { 0x63, 0x02, 0x50 }, 0xef, 0xef, ALPS_FW_BK_1 }, /* NEC Versa L320 */ |
50 | { { 0x63, 0x02, 0x64 }, 0xf8, 0xf8, 0 }, | 50 | { { 0x63, 0x02, 0x64 }, 0xf8, 0xf8, 0 }, |
51 | { { 0x63, 0x03, 0xc8 }, 0xf8, 0xf8, ALPS_PASS }, /* Dell Latitude D800 */ | 51 | { { 0x63, 0x03, 0xc8 }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude D800 */ |
52 | { { 0x73, 0x00, 0x0a }, 0xf8, 0xf8, ALPS_DUALPOINT }, /* ThinkPad R61 8918-5QG */ | 52 | { { 0x73, 0x00, 0x0a }, 0xf8, 0xf8, ALPS_DUALPOINT }, /* ThinkPad R61 8918-5QG */ |
53 | { { 0x73, 0x02, 0x0a }, 0xf8, 0xf8, 0 }, | 53 | { { 0x73, 0x02, 0x0a }, 0xf8, 0xf8, 0 }, |
54 | { { 0x73, 0x02, 0x14 }, 0xf8, 0xf8, ALPS_FW_BK_2 }, /* Ahtec Laptop */ | 54 | { { 0x73, 0x02, 0x14 }, 0xf8, 0xf8, ALPS_FW_BK_2 }, /* Ahtec Laptop */ |
55 | { { 0x20, 0x02, 0x0e }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* XXX */ | 55 | { { 0x20, 0x02, 0x0e }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* XXX */ |
56 | { { 0x22, 0x02, 0x0a }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, | 56 | { { 0x22, 0x02, 0x0a }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, |
57 | { { 0x22, 0x02, 0x14 }, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude D600 */ | 57 | { { 0x22, 0x02, 0x14 }, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude D600 */ |
58 | { { 0x62, 0x02, 0x14 }, 0xcf, 0xcf, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude E6500 */ | 58 | { { 0x62, 0x02, 0x14 }, 0xcf, 0xcf, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude E6500 */ |
59 | { { 0x73, 0x02, 0x50 }, 0xcf, 0xcf, ALPS_FW_BK_1 } /* Dell Vostro 1400 */ | 59 | { { 0x73, 0x02, 0x50 }, 0xcf, 0xcf, ALPS_FW_BK_1 }, /* Dell Vostro 1400 */ |
60 | }; | 60 | }; |
61 | 61 | ||
62 | /* | 62 | /* |
@@ -132,18 +132,23 @@ static void alps_process_packet(struct psmouse *psmouse) | |||
132 | ges = packet[2] & 1; | 132 | ges = packet[2] & 1; |
133 | fin = packet[2] & 2; | 133 | fin = packet[2] & 2; |
134 | 134 | ||
135 | input_report_key(dev, BTN_LEFT, left); | ||
136 | input_report_key(dev, BTN_RIGHT, right); | ||
137 | input_report_key(dev, BTN_MIDDLE, middle); | ||
138 | |||
139 | if ((priv->i->flags & ALPS_DUALPOINT) && z == 127) { | 135 | if ((priv->i->flags & ALPS_DUALPOINT) && z == 127) { |
140 | input_report_rel(dev2, REL_X, (x > 383 ? (x - 768) : x)); | 136 | input_report_rel(dev2, REL_X, (x > 383 ? (x - 768) : x)); |
141 | input_report_rel(dev2, REL_Y, -(y > 255 ? (y - 512) : y)); | 137 | input_report_rel(dev2, REL_Y, -(y > 255 ? (y - 512) : y)); |
138 | |||
139 | input_report_key(dev2, BTN_LEFT, left); | ||
140 | input_report_key(dev2, BTN_RIGHT, right); | ||
141 | input_report_key(dev2, BTN_MIDDLE, middle); | ||
142 | |||
142 | input_sync(dev); | 143 | input_sync(dev); |
143 | input_sync(dev2); | 144 | input_sync(dev2); |
144 | return; | 145 | return; |
145 | } | 146 | } |
146 | 147 | ||
148 | input_report_key(dev, BTN_LEFT, left); | ||
149 | input_report_key(dev, BTN_RIGHT, right); | ||
150 | input_report_key(dev, BTN_MIDDLE, middle); | ||
151 | |||
147 | /* Convert hardware tap to a reasonable Z value */ | 152 | /* Convert hardware tap to a reasonable Z value */ |
148 | if (ges && !fin) z = 40; | 153 | if (ges && !fin) z = 40; |
149 | 154 | ||
diff --git a/drivers/input/mouse/appletouch.c b/drivers/input/mouse/appletouch.c index e0140fdc02a5..908b5b44052f 100644 --- a/drivers/input/mouse/appletouch.c +++ b/drivers/input/mouse/appletouch.c | |||
@@ -361,7 +361,7 @@ static int atp_calculate_abs(int *xy_sensors, int nb_sensors, int fact, | |||
361 | (!is_increasing && xy_sensors[i - 1] < xy_sensors[i])) { | 361 | (!is_increasing && xy_sensors[i - 1] < xy_sensors[i])) { |
362 | (*fingers)++; | 362 | (*fingers)++; |
363 | is_increasing = 1; | 363 | is_increasing = 1; |
364 | } else if (i > 0 && xy_sensors[i - 1] >= xy_sensors[i]) { | 364 | } else if (i > 0 && (xy_sensors[i - 1] - xy_sensors[i] > threshold)) { |
365 | is_increasing = 0; | 365 | is_increasing = 0; |
366 | } | 366 | } |
367 | 367 | ||
diff --git a/drivers/input/mouse/lifebook.c b/drivers/input/mouse/lifebook.c index 15ac3205ac05..dcd4236af1e3 100644 --- a/drivers/input/mouse/lifebook.c +++ b/drivers/input/mouse/lifebook.c | |||
@@ -159,21 +159,22 @@ static psmouse_ret_t lifebook_process_byte(struct psmouse *psmouse) | |||
159 | if (!dev2) | 159 | if (!dev2) |
160 | printk(KERN_WARNING "lifebook.c: got relative packet " | 160 | printk(KERN_WARNING "lifebook.c: got relative packet " |
161 | "but no relative device set up\n"); | 161 | "but no relative device set up\n"); |
162 | } else if (lifebook_use_6byte_proto) { | ||
163 | input_report_abs(dev1, ABS_X, | ||
164 | ((packet[1] & 0x3f) << 6) | (packet[2] & 0x3f)); | ||
165 | input_report_abs(dev1, ABS_Y, | ||
166 | 4096 - (((packet[4] & 0x3f) << 6) | (packet[5] & 0x3f))); | ||
167 | } else { | 162 | } else { |
168 | input_report_abs(dev1, ABS_X, | 163 | if (lifebook_use_6byte_proto) { |
169 | (packet[1] | ((packet[0] & 0x30) << 4))); | 164 | input_report_abs(dev1, ABS_X, |
170 | input_report_abs(dev1, ABS_Y, | 165 | ((packet[1] & 0x3f) << 6) | (packet[2] & 0x3f)); |
171 | 1024 - (packet[2] | ((packet[0] & 0xC0) << 2))); | 166 | input_report_abs(dev1, ABS_Y, |
167 | 4096 - (((packet[4] & 0x3f) << 6) | (packet[5] & 0x3f))); | ||
168 | } else { | ||
169 | input_report_abs(dev1, ABS_X, | ||
170 | (packet[1] | ((packet[0] & 0x30) << 4))); | ||
171 | input_report_abs(dev1, ABS_Y, | ||
172 | 1024 - (packet[2] | ((packet[0] & 0xC0) << 2))); | ||
173 | } | ||
174 | input_report_key(dev1, BTN_TOUCH, packet[0] & 0x04); | ||
175 | input_sync(dev1); | ||
172 | } | 176 | } |
173 | 177 | ||
174 | input_report_key(dev1, BTN_TOUCH, packet[0] & 0x04); | ||
175 | input_sync(dev1); | ||
176 | |||
177 | if (dev2) { | 178 | if (dev2) { |
178 | if (relative_packet) { | 179 | if (relative_packet) { |
179 | input_report_rel(dev2, REL_X, | 180 | input_report_rel(dev2, REL_X, |
diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index f8f86de694bb..b407b355dceb 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c | |||
@@ -327,7 +327,9 @@ static irqreturn_t psmouse_interrupt(struct serio *serio, | |||
327 | goto out; | 327 | goto out; |
328 | } | 328 | } |
329 | 329 | ||
330 | if (psmouse->packet[1] == PSMOUSE_RET_ID) { | 330 | if (psmouse->packet[1] == PSMOUSE_RET_ID || |
331 | (psmouse->type == PSMOUSE_HGPK && | ||
332 | psmouse->packet[1] == PSMOUSE_RET_BAT)) { | ||
331 | __psmouse_set_state(psmouse, PSMOUSE_IGNORE); | 333 | __psmouse_set_state(psmouse, PSMOUSE_IGNORE); |
332 | serio_reconnect(serio); | 334 | serio_reconnect(serio); |
333 | goto out; | 335 | goto out; |
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index f3e4f7b0240d..19984bf06cad 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c | |||
@@ -180,6 +180,29 @@ static int synaptics_identify(struct psmouse *psmouse) | |||
180 | return -1; | 180 | return -1; |
181 | } | 181 | } |
182 | 182 | ||
183 | /* | ||
184 | * Read touchpad resolution | ||
185 | * Resolution is left zero if touchpad does not support the query | ||
186 | */ | ||
187 | static int synaptics_resolution(struct psmouse *psmouse) | ||
188 | { | ||
189 | struct synaptics_data *priv = psmouse->private; | ||
190 | unsigned char res[3]; | ||
191 | |||
192 | if (SYN_ID_MAJOR(priv->identity) < 4) | ||
193 | return 0; | ||
194 | |||
195 | if (synaptics_send_cmd(psmouse, SYN_QUE_RESOLUTION, res)) | ||
196 | return 0; | ||
197 | |||
198 | if ((res[0] != 0) && (res[1] & 0x80) && (res[2] != 0)) { | ||
199 | priv->x_res = res[0]; /* x resolution in units/mm */ | ||
200 | priv->y_res = res[2]; /* y resolution in units/mm */ | ||
201 | } | ||
202 | |||
203 | return 0; | ||
204 | } | ||
205 | |||
183 | static int synaptics_query_hardware(struct psmouse *psmouse) | 206 | static int synaptics_query_hardware(struct psmouse *psmouse) |
184 | { | 207 | { |
185 | if (synaptics_identify(psmouse)) | 208 | if (synaptics_identify(psmouse)) |
@@ -188,6 +211,8 @@ static int synaptics_query_hardware(struct psmouse *psmouse) | |||
188 | return -1; | 211 | return -1; |
189 | if (synaptics_capability(psmouse)) | 212 | if (synaptics_capability(psmouse)) |
190 | return -1; | 213 | return -1; |
214 | if (synaptics_resolution(psmouse)) | ||
215 | return -1; | ||
191 | 216 | ||
192 | return 0; | 217 | return 0; |
193 | } | 218 | } |
@@ -563,6 +588,9 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv) | |||
563 | clear_bit(EV_REL, dev->evbit); | 588 | clear_bit(EV_REL, dev->evbit); |
564 | clear_bit(REL_X, dev->relbit); | 589 | clear_bit(REL_X, dev->relbit); |
565 | clear_bit(REL_Y, dev->relbit); | 590 | clear_bit(REL_Y, dev->relbit); |
591 | |||
592 | dev->absres[ABS_X] = priv->x_res; | ||
593 | dev->absres[ABS_Y] = priv->y_res; | ||
566 | } | 594 | } |
567 | 595 | ||
568 | static void synaptics_disconnect(struct psmouse *psmouse) | 596 | static void synaptics_disconnect(struct psmouse *psmouse) |
diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h index 02aa4cf7bc77..302382151752 100644 --- a/drivers/input/mouse/synaptics.h +++ b/drivers/input/mouse/synaptics.h | |||
@@ -97,6 +97,8 @@ struct synaptics_data { | |||
97 | unsigned long int capabilities; /* Capabilities */ | 97 | unsigned long int capabilities; /* Capabilities */ |
98 | unsigned long int ext_cap; /* Extended Capabilities */ | 98 | unsigned long int ext_cap; /* Extended Capabilities */ |
99 | unsigned long int identity; /* Identification */ | 99 | unsigned long int identity; /* Identification */ |
100 | int x_res; /* X resolution in units/mm */ | ||
101 | int y_res; /* Y resolution in units/mm */ | ||
100 | 102 | ||
101 | unsigned char pkt_type; /* packet type - old, new, etc */ | 103 | unsigned char pkt_type; /* packet type - old, new, etc */ |
102 | unsigned char mode; /* current mode byte */ | 104 | unsigned char mode; /* current mode byte */ |
diff --git a/drivers/input/mouse/synaptics_i2c.c b/drivers/input/mouse/synaptics_i2c.c new file mode 100644 index 000000000000..eac9fdde7ee9 --- /dev/null +++ b/drivers/input/mouse/synaptics_i2c.c | |||
@@ -0,0 +1,676 @@ | |||
1 | /* | ||
2 | * Synaptics touchpad with I2C interface | ||
3 | * | ||
4 | * Copyright (C) 2009 Compulab, Ltd. | ||
5 | * Mike Rapoport <mike@compulab.co.il> | ||
6 | * Igor Grinberg <grinberg@compulab.co.il> | ||
7 | * | ||
8 | * This file is subject to the terms and conditions of the GNU General Public | ||
9 | * License. See the file COPYING in the main directory of this archive for | ||
10 | * more details. | ||
11 | */ | ||
12 | |||
13 | #include <linux/module.h> | ||
14 | #include <linux/i2c.h> | ||
15 | #include <linux/irq.h> | ||
16 | #include <linux/interrupt.h> | ||
17 | #include <linux/input.h> | ||
18 | #include <linux/delay.h> | ||
19 | #include <linux/workqueue.h> | ||
20 | |||
21 | #define DRIVER_NAME "synaptics_i2c" | ||
22 | /* maximum product id is 15 characters */ | ||
23 | #define PRODUCT_ID_LENGTH 15 | ||
24 | #define REGISTER_LENGTH 8 | ||
25 | |||
26 | /* | ||
27 | * after soft reset, we should wait for 1 ms | ||
28 | * before the device becomes operational | ||
29 | */ | ||
30 | #define SOFT_RESET_DELAY_MS 3 | ||
31 | /* and after hard reset, we should wait for max 500ms */ | ||
32 | #define HARD_RESET_DELAY_MS 500 | ||
33 | |||
34 | /* Registers by SMBus address */ | ||
35 | #define PAGE_SEL_REG 0xff | ||
36 | #define DEVICE_STATUS_REG 0x09 | ||
37 | |||
38 | /* Registers by RMI address */ | ||
39 | #define DEV_CONTROL_REG 0x0000 | ||
40 | #define INTERRUPT_EN_REG 0x0001 | ||
41 | #define ERR_STAT_REG 0x0002 | ||
42 | #define INT_REQ_STAT_REG 0x0003 | ||
43 | #define DEV_COMMAND_REG 0x0004 | ||
44 | |||
45 | #define RMI_PROT_VER_REG 0x0200 | ||
46 | #define MANUFACT_ID_REG 0x0201 | ||
47 | #define PHYS_INT_VER_REG 0x0202 | ||
48 | #define PROD_PROPERTY_REG 0x0203 | ||
49 | #define INFO_QUERY_REG0 0x0204 | ||
50 | #define INFO_QUERY_REG1 (INFO_QUERY_REG0 + 1) | ||
51 | #define INFO_QUERY_REG2 (INFO_QUERY_REG0 + 2) | ||
52 | #define INFO_QUERY_REG3 (INFO_QUERY_REG0 + 3) | ||
53 | |||
54 | #define PRODUCT_ID_REG0 0x0210 | ||
55 | #define PRODUCT_ID_REG1 (PRODUCT_ID_REG0 + 1) | ||
56 | #define PRODUCT_ID_REG2 (PRODUCT_ID_REG0 + 2) | ||
57 | #define PRODUCT_ID_REG3 (PRODUCT_ID_REG0 + 3) | ||
58 | #define PRODUCT_ID_REG4 (PRODUCT_ID_REG0 + 4) | ||
59 | #define PRODUCT_ID_REG5 (PRODUCT_ID_REG0 + 5) | ||
60 | #define PRODUCT_ID_REG6 (PRODUCT_ID_REG0 + 6) | ||
61 | #define PRODUCT_ID_REG7 (PRODUCT_ID_REG0 + 7) | ||
62 | #define PRODUCT_ID_REG8 (PRODUCT_ID_REG0 + 8) | ||
63 | #define PRODUCT_ID_REG9 (PRODUCT_ID_REG0 + 9) | ||
64 | #define PRODUCT_ID_REG10 (PRODUCT_ID_REG0 + 10) | ||
65 | #define PRODUCT_ID_REG11 (PRODUCT_ID_REG0 + 11) | ||
66 | #define PRODUCT_ID_REG12 (PRODUCT_ID_REG0 + 12) | ||
67 | #define PRODUCT_ID_REG13 (PRODUCT_ID_REG0 + 13) | ||
68 | #define PRODUCT_ID_REG14 (PRODUCT_ID_REG0 + 14) | ||
69 | #define PRODUCT_ID_REG15 (PRODUCT_ID_REG0 + 15) | ||
70 | |||
71 | #define DATA_REG0 0x0400 | ||
72 | #define ABS_PRESSURE_REG 0x0401 | ||
73 | #define ABS_MSB_X_REG 0x0402 | ||
74 | #define ABS_LSB_X_REG (ABS_MSB_X_REG + 1) | ||
75 | #define ABS_MSB_Y_REG 0x0404 | ||
76 | #define ABS_LSB_Y_REG (ABS_MSB_Y_REG + 1) | ||
77 | #define REL_X_REG 0x0406 | ||
78 | #define REL_Y_REG 0x0407 | ||
79 | |||
80 | #define DEV_QUERY_REG0 0x1000 | ||
81 | #define DEV_QUERY_REG1 (DEV_QUERY_REG0 + 1) | ||
82 | #define DEV_QUERY_REG2 (DEV_QUERY_REG0 + 2) | ||
83 | #define DEV_QUERY_REG3 (DEV_QUERY_REG0 + 3) | ||
84 | #define DEV_QUERY_REG4 (DEV_QUERY_REG0 + 4) | ||
85 | #define DEV_QUERY_REG5 (DEV_QUERY_REG0 + 5) | ||
86 | #define DEV_QUERY_REG6 (DEV_QUERY_REG0 + 6) | ||
87 | #define DEV_QUERY_REG7 (DEV_QUERY_REG0 + 7) | ||
88 | #define DEV_QUERY_REG8 (DEV_QUERY_REG0 + 8) | ||
89 | |||
90 | #define GENERAL_2D_CONTROL_REG 0x1041 | ||
91 | #define SENSOR_SENSITIVITY_REG 0x1044 | ||
92 | #define SENS_MAX_POS_MSB_REG 0x1046 | ||
93 | #define SENS_MAX_POS_LSB_REG (SENS_MAX_POS_UPPER_REG + 1) | ||
94 | |||
95 | /* Register bits */ | ||
96 | /* Device Control Register Bits */ | ||
97 | #define REPORT_RATE_1ST_BIT 6 | ||
98 | |||
99 | /* Interrupt Enable Register Bits (INTERRUPT_EN_REG) */ | ||
100 | #define F10_ABS_INT_ENA 0 | ||
101 | #define F10_REL_INT_ENA 1 | ||
102 | #define F20_INT_ENA 2 | ||
103 | |||
104 | /* Interrupt Request Register Bits (INT_REQ_STAT_REG | DEVICE_STATUS_REG) */ | ||
105 | #define F10_ABS_INT_REQ 0 | ||
106 | #define F10_REL_INT_REQ 1 | ||
107 | #define F20_INT_REQ 2 | ||
108 | /* Device Status Register Bits (DEVICE_STATUS_REG) */ | ||
109 | #define STAT_CONFIGURED 6 | ||
110 | #define STAT_ERROR 7 | ||
111 | |||
112 | /* Device Command Register Bits (DEV_COMMAND_REG) */ | ||
113 | #define RESET_COMMAND 0x01 | ||
114 | #define REZERO_COMMAND 0x02 | ||
115 | |||
116 | /* Data Register 0 Bits (DATA_REG0) */ | ||
117 | #define GESTURE 3 | ||
118 | |||
119 | /* Device Query Registers Bits */ | ||
120 | /* DEV_QUERY_REG3 */ | ||
121 | #define HAS_PALM_DETECT 1 | ||
122 | #define HAS_MULTI_FING 2 | ||
123 | #define HAS_SCROLLER 4 | ||
124 | #define HAS_2D_SCROLL 5 | ||
125 | |||
126 | /* General 2D Control Register Bits (GENERAL_2D_CONTROL_REG) */ | ||
127 | #define NO_DECELERATION 1 | ||
128 | #define REDUCE_REPORTING 3 | ||
129 | #define NO_FILTER 5 | ||
130 | |||
131 | /* Function Masks */ | ||
132 | /* Device Control Register Masks (DEV_CONTROL_REG) */ | ||
133 | #define REPORT_RATE_MSK 0xc0 | ||
134 | #define SLEEP_MODE_MSK 0x07 | ||
135 | |||
136 | /* Device Sleep Modes */ | ||
137 | #define FULL_AWAKE 0x0 | ||
138 | #define NORMAL_OP 0x1 | ||
139 | #define LOW_PWR_OP 0x2 | ||
140 | #define VERY_LOW_PWR_OP 0x3 | ||
141 | #define SENS_SLEEP 0x4 | ||
142 | #define SLEEP_MOD 0x5 | ||
143 | #define DEEP_SLEEP 0x6 | ||
144 | #define HIBERNATE 0x7 | ||
145 | |||
146 | /* Interrupt Register Mask */ | ||
147 | /* (INT_REQ_STAT_REG | DEVICE_STATUS_REG | INTERRUPT_EN_REG) */ | ||
148 | #define INT_ENA_REQ_MSK 0x07 | ||
149 | #define INT_ENA_ABS_MSK 0x01 | ||
150 | #define INT_ENA_REL_MSK 0x02 | ||
151 | #define INT_ENA_F20_MSK 0x04 | ||
152 | |||
153 | /* Device Status Register Masks (DEVICE_STATUS_REG) */ | ||
154 | #define CONFIGURED_MSK 0x40 | ||
155 | #define ERROR_MSK 0x80 | ||
156 | |||
157 | /* Data Register 0 Masks */ | ||
158 | #define FINGER_WIDTH_MSK 0xf0 | ||
159 | #define GESTURE_MSK 0x08 | ||
160 | #define SENSOR_STATUS_MSK 0x07 | ||
161 | |||
162 | /* | ||
163 | * MSB Position Register Masks | ||
164 | * ABS_MSB_X_REG | ABS_MSB_Y_REG | SENS_MAX_POS_MSB_REG | | ||
165 | * DEV_QUERY_REG3 | DEV_QUERY_REG5 | ||
166 | */ | ||
167 | #define MSB_POSITION_MSK 0x1f | ||
168 | |||
169 | /* Device Query Registers Masks */ | ||
170 | |||
171 | /* DEV_QUERY_REG2 */ | ||
172 | #define NUM_EXTRA_POS_MSK 0x07 | ||
173 | |||
174 | /* When in IRQ mode read the device every THREAD_IRQ_SLEEP_SECS */ | ||
175 | #define THREAD_IRQ_SLEEP_SECS 2 | ||
176 | #define THREAD_IRQ_SLEEP_MSECS (THREAD_IRQ_SLEEP_SECS * MSEC_PER_SEC) | ||
177 | |||
178 | /* | ||
179 | * When in Polling mode and no data received for NO_DATA_THRES msecs | ||
180 | * reduce the polling rate to NO_DATA_SLEEP_MSECS | ||
181 | */ | ||
182 | #define NO_DATA_THRES (MSEC_PER_SEC) | ||
183 | #define NO_DATA_SLEEP_MSECS (MSEC_PER_SEC / 4) | ||
184 | |||
185 | /* Control touchpad's No Deceleration option */ | ||
186 | static int no_decel = 1; | ||
187 | module_param(no_decel, bool, 0644); | ||
188 | MODULE_PARM_DESC(no_decel, "No Deceleration. Default = 1 (on)"); | ||
189 | |||
190 | /* Control touchpad's Reduced Reporting option */ | ||
191 | static int reduce_report; | ||
192 | module_param(reduce_report, bool, 0644); | ||
193 | MODULE_PARM_DESC(reduce_report, "Reduced Reporting. Default = 0 (off)"); | ||
194 | |||
195 | /* Control touchpad's No Filter option */ | ||
196 | static int no_filter; | ||
197 | module_param(no_filter, bool, 0644); | ||
198 | MODULE_PARM_DESC(no_filter, "No Filter. Default = 0 (off)"); | ||
199 | |||
200 | /* | ||
201 | * touchpad Attention line is Active Low and Open Drain, | ||
202 | * therefore should be connected to pulled up line | ||
203 | * and the irq configuration should be set to Falling Edge Trigger | ||
204 | */ | ||
205 | /* Control IRQ / Polling option */ | ||
206 | static int polling_req; | ||
207 | module_param(polling_req, bool, 0444); | ||
208 | MODULE_PARM_DESC(polling_req, "Request Polling. Default = 0 (use irq)"); | ||
209 | |||
210 | /* Control Polling Rate */ | ||
211 | static int scan_rate = 80; | ||
212 | module_param(scan_rate, int, 0644); | ||
213 | MODULE_PARM_DESC(scan_rate, "Polling rate in times/sec. Default = 80"); | ||
214 | |||
215 | /* The main device structure */ | ||
216 | struct synaptics_i2c { | ||
217 | struct i2c_client *client; | ||
218 | struct input_dev *input; | ||
219 | struct delayed_work dwork; | ||
220 | int no_data_count; | ||
221 | int no_decel_param; | ||
222 | int reduce_report_param; | ||
223 | int no_filter_param; | ||
224 | int scan_rate_param; | ||
225 | int scan_ms; | ||
226 | }; | ||
227 | |||
228 | static inline void set_scan_rate(struct synaptics_i2c *touch, int scan_rate) | ||
229 | { | ||
230 | touch->scan_ms = MSEC_PER_SEC / scan_rate; | ||
231 | touch->scan_rate_param = scan_rate; | ||
232 | } | ||
233 | |||
234 | /* | ||
235 | * Driver's initial design makes no race condition possible on i2c bus, | ||
236 | * so there is no need in any locking. | ||
237 | * Keep it in mind, while playing with the code. | ||
238 | */ | ||
239 | static s32 synaptics_i2c_reg_get(struct i2c_client *client, u16 reg) | ||
240 | { | ||
241 | int ret; | ||
242 | |||
243 | ret = i2c_smbus_write_byte_data(client, PAGE_SEL_REG, reg >> 8); | ||
244 | if (ret == 0) | ||
245 | ret = i2c_smbus_read_byte_data(client, reg & 0xff); | ||
246 | |||
247 | return ret; | ||
248 | } | ||
249 | |||
250 | static s32 synaptics_i2c_reg_set(struct i2c_client *client, u16 reg, u8 val) | ||
251 | { | ||
252 | int ret; | ||
253 | |||
254 | ret = i2c_smbus_write_byte_data(client, PAGE_SEL_REG, reg >> 8); | ||
255 | if (ret == 0) | ||
256 | ret = i2c_smbus_write_byte_data(client, reg & 0xff, val); | ||
257 | |||
258 | return ret; | ||
259 | } | ||
260 | |||
261 | static s32 synaptics_i2c_word_get(struct i2c_client *client, u16 reg) | ||
262 | { | ||
263 | int ret; | ||
264 | |||
265 | ret = i2c_smbus_write_byte_data(client, PAGE_SEL_REG, reg >> 8); | ||
266 | if (ret == 0) | ||
267 | ret = i2c_smbus_read_word_data(client, reg & 0xff); | ||
268 | |||
269 | return ret; | ||
270 | } | ||
271 | |||
272 | static int synaptics_i2c_config(struct i2c_client *client) | ||
273 | { | ||
274 | int ret, control; | ||
275 | u8 int_en; | ||
276 | |||
277 | /* set Report Rate to Device Highest (>=80) and Sleep to normal */ | ||
278 | ret = synaptics_i2c_reg_set(client, DEV_CONTROL_REG, 0xc1); | ||
279 | if (ret) | ||
280 | return ret; | ||
281 | |||
282 | /* set Interrupt Disable to Func20 / Enable to Func10) */ | ||
283 | int_en = (polling_req) ? 0 : INT_ENA_ABS_MSK | INT_ENA_REL_MSK; | ||
284 | ret = synaptics_i2c_reg_set(client, INTERRUPT_EN_REG, int_en); | ||
285 | if (ret) | ||
286 | return ret; | ||
287 | |||
288 | control = synaptics_i2c_reg_get(client, GENERAL_2D_CONTROL_REG); | ||
289 | /* No Deceleration */ | ||
290 | control |= no_decel ? 1 << NO_DECELERATION : 0; | ||
291 | /* Reduced Reporting */ | ||
292 | control |= reduce_report ? 1 << REDUCE_REPORTING : 0; | ||
293 | /* No Filter */ | ||
294 | control |= no_filter ? 1 << NO_FILTER : 0; | ||
295 | ret = synaptics_i2c_reg_set(client, GENERAL_2D_CONTROL_REG, control); | ||
296 | if (ret) | ||
297 | return ret; | ||
298 | |||
299 | return 0; | ||
300 | } | ||
301 | |||
302 | static int synaptics_i2c_reset_config(struct i2c_client *client) | ||
303 | { | ||
304 | int ret; | ||
305 | |||
306 | /* Reset the Touchpad */ | ||
307 | ret = synaptics_i2c_reg_set(client, DEV_COMMAND_REG, RESET_COMMAND); | ||
308 | if (ret) { | ||
309 | dev_err(&client->dev, "Unable to reset device\n"); | ||
310 | } else { | ||
311 | msleep(SOFT_RESET_DELAY_MS); | ||
312 | ret = synaptics_i2c_config(client); | ||
313 | if (ret) | ||
314 | dev_err(&client->dev, "Unable to config device\n"); | ||
315 | } | ||
316 | |||
317 | return ret; | ||
318 | } | ||
319 | |||
320 | static int synaptics_i2c_check_error(struct i2c_client *client) | ||
321 | { | ||
322 | int status, ret = 0; | ||
323 | |||
324 | status = i2c_smbus_read_byte_data(client, DEVICE_STATUS_REG) & | ||
325 | (CONFIGURED_MSK | ERROR_MSK); | ||
326 | |||
327 | if (status != CONFIGURED_MSK) | ||
328 | ret = synaptics_i2c_reset_config(client); | ||
329 | |||
330 | return ret; | ||
331 | } | ||
332 | |||
333 | static bool synaptics_i2c_get_input(struct synaptics_i2c *touch) | ||
334 | { | ||
335 | struct input_dev *input = touch->input; | ||
336 | int xy_delta, gesture; | ||
337 | s32 data; | ||
338 | s8 x_delta, y_delta; | ||
339 | |||
340 | /* Deal with spontanious resets and errors */ | ||
341 | if (synaptics_i2c_check_error(touch->client)) | ||
342 | return 0; | ||
343 | |||
344 | /* Get Gesture Bit */ | ||
345 | data = synaptics_i2c_reg_get(touch->client, DATA_REG0); | ||
346 | gesture = (data >> GESTURE) & 0x1; | ||
347 | |||
348 | /* | ||
349 | * Get Relative axes. we have to get them in one shot, | ||
350 | * so we get 2 bytes starting from REL_X_REG. | ||
351 | */ | ||
352 | xy_delta = synaptics_i2c_word_get(touch->client, REL_X_REG) & 0xffff; | ||
353 | |||
354 | /* Separate X from Y */ | ||
355 | x_delta = xy_delta & 0xff; | ||
356 | y_delta = (xy_delta >> REGISTER_LENGTH) & 0xff; | ||
357 | |||
358 | /* Report the button event */ | ||
359 | input_report_key(input, BTN_LEFT, gesture); | ||
360 | |||
361 | /* Report the deltas */ | ||
362 | input_report_rel(input, REL_X, x_delta); | ||
363 | input_report_rel(input, REL_Y, -y_delta); | ||
364 | input_sync(input); | ||
365 | |||
366 | return xy_delta || gesture; | ||
367 | } | ||
368 | |||
369 | static irqreturn_t synaptics_i2c_irq(int irq, void *dev_id) | ||
370 | { | ||
371 | struct synaptics_i2c *touch = dev_id; | ||
372 | |||
373 | /* | ||
374 | * We want to have the work run immediately but it might have | ||
375 | * already been scheduled with a delay, that's why we have to | ||
376 | * cancel it first. | ||
377 | */ | ||
378 | cancel_delayed_work(&touch->dwork); | ||
379 | schedule_delayed_work(&touch->dwork, 0); | ||
380 | |||
381 | return IRQ_HANDLED; | ||
382 | } | ||
383 | |||
384 | static void synaptics_i2c_check_params(struct synaptics_i2c *touch) | ||
385 | { | ||
386 | bool reset = false; | ||
387 | |||
388 | if (scan_rate != touch->scan_rate_param) | ||
389 | set_scan_rate(touch, scan_rate); | ||
390 | |||
391 | if (no_decel != touch->no_decel_param) { | ||
392 | touch->no_decel_param = no_decel; | ||
393 | reset = true; | ||
394 | } | ||
395 | |||
396 | if (no_filter != touch->no_filter_param) { | ||
397 | touch->no_filter_param = no_filter; | ||
398 | reset = true; | ||
399 | } | ||
400 | |||
401 | if (reduce_report != touch->reduce_report_param) { | ||
402 | touch->reduce_report_param = reduce_report; | ||
403 | reset = true; | ||
404 | } | ||
405 | |||
406 | if (reset) | ||
407 | synaptics_i2c_reset_config(touch->client); | ||
408 | } | ||
409 | |||
410 | /* Control the Device polling rate / Work Handler sleep time */ | ||
411 | unsigned long synaptics_i2c_adjust_delay(struct synaptics_i2c *touch, | ||
412 | bool have_data) | ||
413 | { | ||
414 | unsigned long delay, nodata_count_thres; | ||
415 | |||
416 | if (polling_req) { | ||
417 | delay = touch->scan_ms; | ||
418 | if (have_data) { | ||
419 | touch->no_data_count = 0; | ||
420 | } else { | ||
421 | nodata_count_thres = NO_DATA_THRES / touch->scan_ms; | ||
422 | if (touch->no_data_count < nodata_count_thres) | ||
423 | touch->no_data_count++; | ||
424 | else | ||
425 | delay = NO_DATA_SLEEP_MSECS; | ||
426 | } | ||
427 | return msecs_to_jiffies(delay); | ||
428 | } else { | ||
429 | delay = msecs_to_jiffies(THREAD_IRQ_SLEEP_MSECS); | ||
430 | return round_jiffies_relative(delay); | ||
431 | } | ||
432 | } | ||
433 | |||
434 | /* Work Handler */ | ||
435 | static void synaptics_i2c_work_handler(struct work_struct *work) | ||
436 | { | ||
437 | bool have_data; | ||
438 | struct synaptics_i2c *touch = | ||
439 | container_of(work, struct synaptics_i2c, dwork.work); | ||
440 | unsigned long delay; | ||
441 | |||
442 | synaptics_i2c_check_params(touch); | ||
443 | |||
444 | have_data = synaptics_i2c_get_input(touch); | ||
445 | delay = synaptics_i2c_adjust_delay(touch, have_data); | ||
446 | |||
447 | /* | ||
448 | * While interrupt driven, there is no real need to poll the device. | ||
449 | * But touchpads are very sensitive, so there could be errors | ||
450 | * related to physical environment and the attention line isn't | ||
451 | * neccesarily asserted. In such case we can lose the touchpad. | ||
452 | * We poll the device once in THREAD_IRQ_SLEEP_SECS and | ||
453 | * if error is detected, we try to reset and reconfigure the touchpad. | ||
454 | */ | ||
455 | schedule_delayed_work(&touch->dwork, delay); | ||
456 | } | ||
457 | |||
458 | static int synaptics_i2c_open(struct input_dev *input) | ||
459 | { | ||
460 | struct synaptics_i2c *touch = input_get_drvdata(input); | ||
461 | int ret; | ||
462 | |||
463 | ret = synaptics_i2c_reset_config(touch->client); | ||
464 | if (ret) | ||
465 | return ret; | ||
466 | |||
467 | if (polling_req) | ||
468 | schedule_delayed_work(&touch->dwork, | ||
469 | msecs_to_jiffies(NO_DATA_SLEEP_MSECS)); | ||
470 | |||
471 | return 0; | ||
472 | } | ||
473 | |||
474 | static void synaptics_i2c_close(struct input_dev *input) | ||
475 | { | ||
476 | struct synaptics_i2c *touch = input_get_drvdata(input); | ||
477 | |||
478 | if (!polling_req) | ||
479 | synaptics_i2c_reg_set(touch->client, INTERRUPT_EN_REG, 0); | ||
480 | |||
481 | cancel_delayed_work_sync(&touch->dwork); | ||
482 | |||
483 | /* Save some power */ | ||
484 | synaptics_i2c_reg_set(touch->client, DEV_CONTROL_REG, DEEP_SLEEP); | ||
485 | } | ||
486 | |||
487 | static void synaptics_i2c_set_input_params(struct synaptics_i2c *touch) | ||
488 | { | ||
489 | struct input_dev *input = touch->input; | ||
490 | |||
491 | input->name = touch->client->name; | ||
492 | input->phys = touch->client->adapter->name; | ||
493 | input->id.bustype = BUS_I2C; | ||
494 | input->id.version = synaptics_i2c_word_get(touch->client, | ||
495 | INFO_QUERY_REG0); | ||
496 | input->dev.parent = &touch->client->dev; | ||
497 | input->open = synaptics_i2c_open; | ||
498 | input->close = synaptics_i2c_close; | ||
499 | input_set_drvdata(input, touch); | ||
500 | |||
501 | /* Register the device as mouse */ | ||
502 | __set_bit(EV_REL, input->evbit); | ||
503 | __set_bit(REL_X, input->relbit); | ||
504 | __set_bit(REL_Y, input->relbit); | ||
505 | |||
506 | /* Register device's buttons and keys */ | ||
507 | __set_bit(EV_KEY, input->evbit); | ||
508 | __set_bit(BTN_LEFT, input->keybit); | ||
509 | } | ||
510 | |||
511 | struct synaptics_i2c *synaptics_i2c_touch_create(struct i2c_client *client) | ||
512 | { | ||
513 | struct synaptics_i2c *touch; | ||
514 | |||
515 | touch = kzalloc(sizeof(struct synaptics_i2c), GFP_KERNEL); | ||
516 | if (!touch) | ||
517 | return NULL; | ||
518 | |||
519 | touch->client = client; | ||
520 | touch->no_decel_param = no_decel; | ||
521 | touch->scan_rate_param = scan_rate; | ||
522 | set_scan_rate(touch, scan_rate); | ||
523 | INIT_DELAYED_WORK(&touch->dwork, synaptics_i2c_work_handler); | ||
524 | |||
525 | return touch; | ||
526 | } | ||
527 | |||
528 | static int __devinit synaptics_i2c_probe(struct i2c_client *client, | ||
529 | const struct i2c_device_id *dev_id) | ||
530 | { | ||
531 | int ret; | ||
532 | struct synaptics_i2c *touch; | ||
533 | |||
534 | touch = synaptics_i2c_touch_create(client); | ||
535 | if (!touch) | ||
536 | return -ENOMEM; | ||
537 | |||
538 | i2c_set_clientdata(client, touch); | ||
539 | |||
540 | ret = synaptics_i2c_reset_config(client); | ||
541 | if (ret) | ||
542 | goto err_mem_free; | ||
543 | |||
544 | if (client->irq < 1) | ||
545 | polling_req = 1; | ||
546 | |||
547 | touch->input = input_allocate_device(); | ||
548 | if (!touch->input) { | ||
549 | ret = -ENOMEM; | ||
550 | goto err_mem_free; | ||
551 | } | ||
552 | |||
553 | synaptics_i2c_set_input_params(touch); | ||
554 | |||
555 | if (!polling_req) { | ||
556 | dev_dbg(&touch->client->dev, | ||
557 | "Requesting IRQ: %d\n", touch->client->irq); | ||
558 | |||
559 | ret = request_irq(touch->client->irq, synaptics_i2c_irq, | ||
560 | IRQF_DISABLED|IRQ_TYPE_EDGE_FALLING, | ||
561 | DRIVER_NAME, touch); | ||
562 | if (ret) { | ||
563 | dev_warn(&touch->client->dev, | ||
564 | "IRQ request failed: %d, " | ||
565 | "falling back to polling\n", ret); | ||
566 | polling_req = 1; | ||
567 | synaptics_i2c_reg_set(touch->client, | ||
568 | INTERRUPT_EN_REG, 0); | ||
569 | } | ||
570 | } | ||
571 | |||
572 | if (polling_req) | ||
573 | dev_dbg(&touch->client->dev, | ||
574 | "Using polling at rate: %d times/sec\n", scan_rate); | ||
575 | |||
576 | /* Register the device in input subsystem */ | ||
577 | ret = input_register_device(touch->input); | ||
578 | if (ret) { | ||
579 | dev_err(&client->dev, | ||
580 | "Input device register failed: %d\n", ret); | ||
581 | goto err_input_free; | ||
582 | } | ||
583 | return 0; | ||
584 | |||
585 | err_input_free: | ||
586 | input_free_device(touch->input); | ||
587 | err_mem_free: | ||
588 | i2c_set_clientdata(client, NULL); | ||
589 | kfree(touch); | ||
590 | |||
591 | return ret; | ||
592 | } | ||
593 | |||
594 | static int __devexit synaptics_i2c_remove(struct i2c_client *client) | ||
595 | { | ||
596 | struct synaptics_i2c *touch = i2c_get_clientdata(client); | ||
597 | |||
598 | if (!polling_req) | ||
599 | free_irq(touch->client->irq, touch); | ||
600 | |||
601 | input_unregister_device(touch->input); | ||
602 | i2c_set_clientdata(client, NULL); | ||
603 | kfree(touch); | ||
604 | |||
605 | return 0; | ||
606 | } | ||
607 | |||
608 | #ifdef CONFIG_PM | ||
609 | static int synaptics_i2c_suspend(struct i2c_client *client, pm_message_t mesg) | ||
610 | { | ||
611 | struct synaptics_i2c *touch = i2c_get_clientdata(client); | ||
612 | |||
613 | cancel_delayed_work_sync(&touch->dwork); | ||
614 | |||
615 | /* Save some power */ | ||
616 | synaptics_i2c_reg_set(touch->client, DEV_CONTROL_REG, DEEP_SLEEP); | ||
617 | |||
618 | return 0; | ||
619 | } | ||
620 | |||
621 | static int synaptics_i2c_resume(struct i2c_client *client) | ||
622 | { | ||
623 | int ret; | ||
624 | struct synaptics_i2c *touch = i2c_get_clientdata(client); | ||
625 | |||
626 | ret = synaptics_i2c_reset_config(client); | ||
627 | if (ret) | ||
628 | return ret; | ||
629 | |||
630 | schedule_delayed_work(&touch->dwork, | ||
631 | msecs_to_jiffies(NO_DATA_SLEEP_MSECS)); | ||
632 | |||
633 | return 0; | ||
634 | } | ||
635 | #else | ||
636 | #define synaptics_i2c_suspend NULL | ||
637 | #define synaptics_i2c_resume NULL | ||
638 | #endif | ||
639 | |||
640 | static const struct i2c_device_id synaptics_i2c_id_table[] = { | ||
641 | { "synaptics_i2c", 0 }, | ||
642 | { }, | ||
643 | }; | ||
644 | MODULE_DEVICE_TABLE(i2c, synaptics_i2c_id_table); | ||
645 | |||
646 | static struct i2c_driver synaptics_i2c_driver = { | ||
647 | .driver = { | ||
648 | .name = DRIVER_NAME, | ||
649 | .owner = THIS_MODULE, | ||
650 | }, | ||
651 | |||
652 | .probe = synaptics_i2c_probe, | ||
653 | .remove = __devexit_p(synaptics_i2c_remove), | ||
654 | |||
655 | .suspend = synaptics_i2c_suspend, | ||
656 | .resume = synaptics_i2c_resume, | ||
657 | .id_table = synaptics_i2c_id_table, | ||
658 | }; | ||
659 | |||
660 | static int __init synaptics_i2c_init(void) | ||
661 | { | ||
662 | return i2c_add_driver(&synaptics_i2c_driver); | ||
663 | } | ||
664 | |||
665 | static void __exit synaptics_i2c_exit(void) | ||
666 | { | ||
667 | i2c_del_driver(&synaptics_i2c_driver); | ||
668 | } | ||
669 | |||
670 | module_init(synaptics_i2c_init); | ||
671 | module_exit(synaptics_i2c_exit); | ||
672 | |||
673 | MODULE_DESCRIPTION("Synaptics I2C touchpad driver"); | ||
674 | MODULE_AUTHOR("Mike Rapoport, Igor Grinberg, Compulab"); | ||
675 | MODULE_LICENSE("GPL"); | ||
676 | |||