diff options
Diffstat (limited to 'drivers/input/touchscreen/wacom_w8001.c')
-rw-r--r-- | drivers/input/touchscreen/wacom_w8001.c | 185 |
1 files changed, 177 insertions, 8 deletions
diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c index 56dc35c94bb1..9ae4c7b16ba7 100644 --- a/drivers/input/touchscreen/wacom_w8001.c +++ b/drivers/input/touchscreen/wacom_w8001.c | |||
@@ -2,6 +2,7 @@ | |||
2 | * Wacom W8001 penabled serial touchscreen driver | 2 | * Wacom W8001 penabled serial touchscreen driver |
3 | * | 3 | * |
4 | * Copyright (c) 2008 Jaya Kumar | 4 | * Copyright (c) 2008 Jaya Kumar |
5 | * Copyright (c) 2010 Red Hat, Inc. | ||
5 | * | 6 | * |
6 | * This file is subject to the terms and conditions of the GNU General Public | 7 | * This file is subject to the terms and conditions of the GNU General Public |
7 | * License. See the file COPYING in the main directory of this archive for | 8 | * License. See the file COPYING in the main directory of this archive for |
@@ -30,11 +31,24 @@ MODULE_LICENSE("GPL"); | |||
30 | #define W8001_LEAD_BYTE 0x80 | 31 | #define W8001_LEAD_BYTE 0x80 |
31 | #define W8001_TAB_MASK 0x40 | 32 | #define W8001_TAB_MASK 0x40 |
32 | #define W8001_TAB_BYTE 0x40 | 33 | #define W8001_TAB_BYTE 0x40 |
34 | /* set in first byte of touch data packets */ | ||
35 | #define W8001_TOUCH_MASK (0x10 | W8001_LEAD_MASK) | ||
36 | #define W8001_TOUCH_BYTE (0x10 | W8001_LEAD_BYTE) | ||
33 | 37 | ||
34 | #define W8001_QUERY_PACKET 0x20 | 38 | #define W8001_QUERY_PACKET 0x20 |
35 | 39 | ||
36 | #define W8001_CMD_START '1' | 40 | #define W8001_CMD_START '1' |
37 | #define W8001_CMD_QUERY '*' | 41 | #define W8001_CMD_QUERY '*' |
42 | #define W8001_CMD_TOUCHQUERY '%' | ||
43 | |||
44 | /* length of data packets in bytes, depends on device. */ | ||
45 | #define W8001_PKTLEN_TOUCH93 5 | ||
46 | #define W8001_PKTLEN_TOUCH9A 7 | ||
47 | #define W8001_PKTLEN_TPCPEN 9 | ||
48 | #define W8001_PKTLEN_TPCCTL 11 /* control packet */ | ||
49 | #define W8001_PKTLEN_TOUCH2FG 13 | ||
50 | |||
51 | #define MAX_TRACKING_ID 0xFF /* arbitrarily chosen */ | ||
38 | 52 | ||
39 | struct w8001_coord { | 53 | struct w8001_coord { |
40 | u8 rdy; | 54 | u8 rdy; |
@@ -48,6 +62,15 @@ struct w8001_coord { | |||
48 | u8 tilt_y; | 62 | u8 tilt_y; |
49 | }; | 63 | }; |
50 | 64 | ||
65 | /* touch query reply packet */ | ||
66 | struct w8001_touch_query { | ||
67 | u8 panel_res; | ||
68 | u8 capacity_res; | ||
69 | u8 sensor_id; | ||
70 | u16 x; | ||
71 | u16 y; | ||
72 | }; | ||
73 | |||
51 | /* | 74 | /* |
52 | * Per-touchscreen data. | 75 | * Per-touchscreen data. |
53 | */ | 76 | */ |
@@ -62,6 +85,9 @@ struct w8001 { | |||
62 | unsigned char response[W8001_MAX_LENGTH]; | 85 | unsigned char response[W8001_MAX_LENGTH]; |
63 | unsigned char data[W8001_MAX_LENGTH]; | 86 | unsigned char data[W8001_MAX_LENGTH]; |
64 | char phys[32]; | 87 | char phys[32]; |
88 | int type; | ||
89 | unsigned int pktlen; | ||
90 | int trkid[2]; | ||
65 | }; | 91 | }; |
66 | 92 | ||
67 | static void parse_data(u8 *data, struct w8001_coord *coord) | 93 | static void parse_data(u8 *data, struct w8001_coord *coord) |
@@ -88,11 +114,98 @@ static void parse_data(u8 *data, struct w8001_coord *coord) | |||
88 | coord->tilt_y = data[8] & 0x7F; | 114 | coord->tilt_y = data[8] & 0x7F; |
89 | } | 115 | } |
90 | 116 | ||
117 | static void parse_touch(struct w8001 *w8001) | ||
118 | { | ||
119 | static int trkid; | ||
120 | struct input_dev *dev = w8001->dev; | ||
121 | unsigned char *data = w8001->data; | ||
122 | int i; | ||
123 | |||
124 | for (i = 0; i < 2; i++) { | ||
125 | input_mt_slot(dev, i); | ||
126 | |||
127 | if (data[0] & (1 << i)) { | ||
128 | int x = (data[6 * i + 1] << 7) | (data[6 * i + 2]); | ||
129 | int y = (data[6 * i + 3] << 7) | (data[6 * i + 4]); | ||
130 | /* data[5,6] and [11,12] is finger capacity */ | ||
131 | |||
132 | input_report_abs(dev, ABS_MT_POSITION_X, x); | ||
133 | input_report_abs(dev, ABS_MT_POSITION_Y, y); | ||
134 | input_report_abs(dev, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER); | ||
135 | if (w8001->trkid[i] < 0) | ||
136 | w8001->trkid[i] = trkid++ & MAX_TRACKING_ID; | ||
137 | } else { | ||
138 | w8001->trkid[i] = -1; | ||
139 | } | ||
140 | input_report_abs(dev, ABS_MT_TRACKING_ID, w8001->trkid[i]); | ||
141 | } | ||
142 | |||
143 | input_sync(dev); | ||
144 | } | ||
145 | |||
146 | static void parse_touchquery(u8 *data, struct w8001_touch_query *query) | ||
147 | { | ||
148 | memset(query, 0, sizeof(*query)); | ||
149 | |||
150 | query->panel_res = data[1]; | ||
151 | query->sensor_id = data[2] & 0x7; | ||
152 | query->capacity_res = data[7]; | ||
153 | |||
154 | query->x = data[3] << 9; | ||
155 | query->x |= data[4] << 2; | ||
156 | query->x |= (data[2] >> 5) & 0x3; | ||
157 | |||
158 | query->y = data[5] << 9; | ||
159 | query->y |= data[6] << 2; | ||
160 | query->y |= (data[2] >> 3) & 0x3; | ||
161 | } | ||
162 | |||
163 | static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord) | ||
164 | { | ||
165 | struct input_dev *dev = w8001->dev; | ||
166 | |||
167 | /* | ||
168 | * We have 1 bit for proximity (rdy) and 3 bits for tip, side, | ||
169 | * side2/eraser. If rdy && f2 are set, this can be either pen + side2, | ||
170 | * or eraser. assume | ||
171 | * - if dev is already in proximity and f2 is toggled → pen + side2 | ||
172 | * - if dev comes into proximity with f2 set → eraser | ||
173 | * If f2 disappears after assuming eraser, fake proximity out for | ||
174 | * eraser and in for pen. | ||
175 | */ | ||
176 | |||
177 | if (!w8001->type) { | ||
178 | w8001->type = coord->f2 ? BTN_TOOL_RUBBER : BTN_TOOL_PEN; | ||
179 | } else if (w8001->type == BTN_TOOL_RUBBER) { | ||
180 | if (!coord->f2) { | ||
181 | input_report_abs(dev, ABS_PRESSURE, 0); | ||
182 | input_report_key(dev, BTN_TOUCH, 0); | ||
183 | input_report_key(dev, BTN_STYLUS, 0); | ||
184 | input_report_key(dev, BTN_STYLUS2, 0); | ||
185 | input_report_key(dev, BTN_TOOL_RUBBER, 0); | ||
186 | input_sync(dev); | ||
187 | w8001->type = BTN_TOOL_PEN; | ||
188 | } | ||
189 | } else { | ||
190 | input_report_key(dev, BTN_STYLUS2, coord->f2); | ||
191 | } | ||
192 | |||
193 | input_report_abs(dev, ABS_X, coord->x); | ||
194 | input_report_abs(dev, ABS_Y, coord->y); | ||
195 | input_report_abs(dev, ABS_PRESSURE, coord->pen_pressure); | ||
196 | input_report_key(dev, BTN_TOUCH, coord->tsw); | ||
197 | input_report_key(dev, BTN_STYLUS, coord->f1); | ||
198 | input_report_key(dev, w8001->type, coord->rdy); | ||
199 | input_sync(dev); | ||
200 | |||
201 | if (!coord->rdy) | ||
202 | w8001->type = 0; | ||
203 | } | ||
204 | |||
91 | static irqreturn_t w8001_interrupt(struct serio *serio, | 205 | static irqreturn_t w8001_interrupt(struct serio *serio, |
92 | unsigned char data, unsigned int flags) | 206 | unsigned char data, unsigned int flags) |
93 | { | 207 | { |
94 | struct w8001 *w8001 = serio_get_drvdata(serio); | 208 | struct w8001 *w8001 = serio_get_drvdata(serio); |
95 | struct input_dev *dev = w8001->dev; | ||
96 | struct w8001_coord coord; | 209 | struct w8001_coord coord; |
97 | unsigned char tmp; | 210 | unsigned char tmp; |
98 | 211 | ||
@@ -105,26 +218,45 @@ static irqreturn_t w8001_interrupt(struct serio *serio, | |||
105 | } | 218 | } |
106 | break; | 219 | break; |
107 | 220 | ||
108 | case 8: | 221 | case W8001_PKTLEN_TOUCH93 - 1: |
222 | case W8001_PKTLEN_TOUCH9A - 1: | ||
223 | /* ignore one-finger touch packet. */ | ||
224 | if (w8001->pktlen == w8001->idx) | ||
225 | w8001->idx = 0; | ||
226 | break; | ||
227 | |||
228 | /* Pen coordinates packet */ | ||
229 | case W8001_PKTLEN_TPCPEN - 1: | ||
109 | tmp = w8001->data[0] & W8001_TAB_MASK; | 230 | tmp = w8001->data[0] & W8001_TAB_MASK; |
110 | if (unlikely(tmp == W8001_TAB_BYTE)) | 231 | if (unlikely(tmp == W8001_TAB_BYTE)) |
111 | break; | 232 | break; |
112 | 233 | ||
234 | tmp = (w8001->data[0] & W8001_TOUCH_BYTE); | ||
235 | if (tmp == W8001_TOUCH_BYTE) | ||
236 | break; | ||
237 | |||
113 | w8001->idx = 0; | 238 | w8001->idx = 0; |
114 | parse_data(w8001->data, &coord); | 239 | parse_data(w8001->data, &coord); |
115 | input_report_abs(dev, ABS_X, coord.x); | 240 | report_pen_events(w8001, &coord); |
116 | input_report_abs(dev, ABS_Y, coord.y); | ||
117 | input_report_abs(dev, ABS_PRESSURE, coord.pen_pressure); | ||
118 | input_report_key(dev, BTN_TOUCH, coord.tsw); | ||
119 | input_sync(dev); | ||
120 | break; | 241 | break; |
121 | 242 | ||
122 | case 10: | 243 | /* control packet */ |
244 | case W8001_PKTLEN_TPCCTL - 1: | ||
245 | tmp = (w8001->data[0] & W8001_TOUCH_MASK); | ||
246 | if (tmp == W8001_TOUCH_BYTE) | ||
247 | break; | ||
248 | |||
123 | w8001->idx = 0; | 249 | w8001->idx = 0; |
124 | memcpy(w8001->response, w8001->data, W8001_MAX_LENGTH); | 250 | memcpy(w8001->response, w8001->data, W8001_MAX_LENGTH); |
125 | w8001->response_type = W8001_QUERY_PACKET; | 251 | w8001->response_type = W8001_QUERY_PACKET; |
126 | complete(&w8001->cmd_done); | 252 | complete(&w8001->cmd_done); |
127 | break; | 253 | break; |
254 | |||
255 | /* 2 finger touch packet */ | ||
256 | case W8001_PKTLEN_TOUCH2FG - 1: | ||
257 | w8001->idx = 0; | ||
258 | parse_touch(w8001); | ||
259 | break; | ||
128 | } | 260 | } |
129 | 261 | ||
130 | return IRQ_HANDLED; | 262 | return IRQ_HANDLED; |
@@ -167,6 +299,38 @@ static int w8001_setup(struct w8001 *w8001) | |||
167 | input_set_abs_params(dev, ABS_TILT_X, 0, coord.tilt_x, 0, 0); | 299 | input_set_abs_params(dev, ABS_TILT_X, 0, coord.tilt_x, 0, 0); |
168 | input_set_abs_params(dev, ABS_TILT_Y, 0, coord.tilt_y, 0, 0); | 300 | input_set_abs_params(dev, ABS_TILT_Y, 0, coord.tilt_y, 0, 0); |
169 | 301 | ||
302 | error = w8001_command(w8001, W8001_CMD_TOUCHQUERY, true); | ||
303 | if (!error) { | ||
304 | struct w8001_touch_query touch; | ||
305 | |||
306 | parse_touchquery(w8001->response, &touch); | ||
307 | |||
308 | switch (touch.sensor_id) { | ||
309 | case 0: | ||
310 | case 2: | ||
311 | w8001->pktlen = W8001_PKTLEN_TOUCH93; | ||
312 | break; | ||
313 | case 1: | ||
314 | case 3: | ||
315 | case 4: | ||
316 | w8001->pktlen = W8001_PKTLEN_TOUCH9A; | ||
317 | break; | ||
318 | case 5: | ||
319 | w8001->pktlen = W8001_PKTLEN_TOUCH2FG; | ||
320 | |||
321 | input_mt_create_slots(dev, 2); | ||
322 | input_set_abs_params(dev, ABS_MT_TRACKING_ID, | ||
323 | 0, MAX_TRACKING_ID, 0, 0); | ||
324 | input_set_abs_params(dev, ABS_MT_POSITION_X, | ||
325 | 0, touch.x, 0, 0); | ||
326 | input_set_abs_params(dev, ABS_MT_POSITION_Y, | ||
327 | 0, touch.y, 0, 0); | ||
328 | input_set_abs_params(dev, ABS_MT_TOOL_TYPE, | ||
329 | 0, 0, 0, 0); | ||
330 | break; | ||
331 | } | ||
332 | } | ||
333 | |||
170 | return w8001_command(w8001, W8001_CMD_START, false); | 334 | return w8001_command(w8001, W8001_CMD_START, false); |
171 | } | 335 | } |
172 | 336 | ||
@@ -208,6 +372,7 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv) | |||
208 | w8001->serio = serio; | 372 | w8001->serio = serio; |
209 | w8001->id = serio->id.id; | 373 | w8001->id = serio->id.id; |
210 | w8001->dev = input_dev; | 374 | w8001->dev = input_dev; |
375 | w8001->trkid[0] = w8001->trkid[1] = -1; | ||
211 | init_completion(&w8001->cmd_done); | 376 | init_completion(&w8001->cmd_done); |
212 | snprintf(w8001->phys, sizeof(w8001->phys), "%s/input0", serio->phys); | 377 | snprintf(w8001->phys, sizeof(w8001->phys), "%s/input0", serio->phys); |
213 | 378 | ||
@@ -221,6 +386,10 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv) | |||
221 | 386 | ||
222 | input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); | 387 | input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); |
223 | input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); | 388 | input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); |
389 | input_dev->keybit[BIT_WORD(BTN_TOOL_PEN)] |= BIT_MASK(BTN_TOOL_PEN); | ||
390 | input_dev->keybit[BIT_WORD(BTN_TOOL_RUBBER)] |= BIT_MASK(BTN_TOOL_RUBBER); | ||
391 | input_dev->keybit[BIT_WORD(BTN_STYLUS)] |= BIT_MASK(BTN_STYLUS); | ||
392 | input_dev->keybit[BIT_WORD(BTN_STYLUS2)] |= BIT_MASK(BTN_STYLUS2); | ||
224 | 393 | ||
225 | serio_set_drvdata(serio, w8001); | 394 | serio_set_drvdata(serio, w8001); |
226 | err = serio_open(serio, drv); | 395 | err = serio_open(serio, drv); |