]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/input/mouse/byd.c
Merge remote-tracking branch 'usb-chipidea-next/ci-for-usb-next'
[karo-tx-linux.git] / drivers / input / mouse / byd.c
1 /*
2  * BYD TouchPad PS/2 mouse driver
3  *
4  * Copyright (C) 2015 Chris Diamand <chris@diamand.org>
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License version 2 as published by
8  * the Free Software Foundation.
9  */
10
11 #include <linux/delay.h>
12 #include <linux/input.h>
13 #include <linux/libps2.h>
14 #include <linux/serio.h>
15
16 #include "psmouse.h"
17 #include "byd.h"
18
19 #define PS2_Y_OVERFLOW  BIT_MASK(7)
20 #define PS2_X_OVERFLOW  BIT_MASK(6)
21 #define PS2_Y_SIGN      BIT_MASK(5)
22 #define PS2_X_SIGN      BIT_MASK(4)
23 #define PS2_ALWAYS_1    BIT_MASK(3)
24 #define PS2_MIDDLE      BIT_MASK(2)
25 #define PS2_RIGHT       BIT_MASK(1)
26 #define PS2_LEFT        BIT_MASK(0)
27
28 /*
29  * The touchpad reports gestures in the last byte of each packet. It can take
30  * any of the following values:
31  */
32
33 /* One-finger scrolling in one of the edge scroll zones. */
34 #define BYD_SCROLLUP            0xCA
35 #define BYD_SCROLLDOWN          0x36
36 #define BYD_SCROLLLEFT          0xCB
37 #define BYD_SCROLLRIGHT         0x35
38 /* Two-finger scrolling. */
39 #define BYD_2DOWN               0x2B
40 #define BYD_2UP                 0xD5
41 #define BYD_2LEFT               0xD6
42 #define BYD_2RIGHT              0x2A
43 /* Pinching in or out. */
44 #define BYD_ZOOMOUT             0xD8
45 #define BYD_ZOOMIN              0x28
46 /* Three-finger swipe. */
47 #define BYD_3UP                 0xD3
48 #define BYD_3DOWN               0x2D
49 #define BYD_3LEFT               0xD4
50 #define BYD_3RIGHT              0x2C
51 /* Four-finger swipe. */
52 #define BYD_4UP                 0xCD
53 #define BYD_4DOWN               0x33
54
55 int byd_detect(struct psmouse *psmouse, bool set_properties)
56 {
57         struct ps2dev *ps2dev = &psmouse->ps2dev;
58         unsigned char param[4];
59
60         param[0] = 0x03;
61         param[1] = 0x00;
62         param[2] = 0x00;
63         param[3] = 0x00;
64
65         if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
66                 return -1;
67         if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
68                 return -1;
69         if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
70                 return -1;
71         if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
72                 return -1;
73         if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
74                 return -1;
75
76         if (param[1] != 0x03 || param[2] != 0x64)
77                 return -ENODEV;
78
79         psmouse_dbg(psmouse, "BYD touchpad detected\n");
80
81         if (set_properties) {
82                 psmouse->vendor = "BYD";
83                 psmouse->name = "TouchPad";
84         }
85
86         return 0;
87 }
88
89 static psmouse_ret_t byd_process_byte(struct psmouse *psmouse)
90 {
91         struct input_dev *dev = psmouse->dev;
92         u8 *pkt = psmouse->packet;
93
94         if (psmouse->pktcnt > 0 && !(pkt[0] & PS2_ALWAYS_1)) {
95                 psmouse_warn(psmouse, "Always_1 bit not 1. pkt[0] = %02x\n",
96                              pkt[0]);
97                 return PSMOUSE_BAD_DATA;
98         }
99
100         if (psmouse->pktcnt < psmouse->pktsize)
101                 return PSMOUSE_GOOD_DATA;
102
103         /* Otherwise, a full packet has been received */
104         switch (pkt[3]) {
105         case 0: {
106                 /* Standard packet */
107                 /* Sign-extend if a sign bit is set. */
108                 unsigned int signx = pkt[0] & PS2_X_SIGN ? ~0xFF : 0;
109                 unsigned int signy = pkt[0] & PS2_Y_SIGN ? ~0xFF : 0;
110                 int dx = signx | (int) pkt[1];
111                 int dy = signy | (int) pkt[2];
112
113                 input_report_rel(psmouse->dev, REL_X, dx);
114                 input_report_rel(psmouse->dev, REL_Y, -dy);
115
116                 input_report_key(psmouse->dev, BTN_LEFT, pkt[0] & PS2_LEFT);
117                 input_report_key(psmouse->dev, BTN_RIGHT, pkt[0] & PS2_RIGHT);
118                 input_report_key(psmouse->dev, BTN_MIDDLE, pkt[0] & PS2_MIDDLE);
119                 break;
120         }
121
122         case BYD_SCROLLDOWN:
123         case BYD_2DOWN:
124                 input_report_rel(dev, REL_WHEEL, -1);
125                 break;
126
127         case BYD_SCROLLUP:
128         case BYD_2UP:
129                 input_report_rel(dev, REL_WHEEL, 1);
130                 break;
131
132         case BYD_SCROLLLEFT:
133         case BYD_2LEFT:
134                 input_report_rel(dev, REL_HWHEEL, -1);
135                 break;
136
137         case BYD_SCROLLRIGHT:
138         case BYD_2RIGHT:
139                 input_report_rel(dev, REL_HWHEEL, 1);
140                 break;
141
142         case BYD_ZOOMOUT:
143         case BYD_ZOOMIN:
144         case BYD_3UP:
145         case BYD_3DOWN:
146         case BYD_3LEFT:
147         case BYD_3RIGHT:
148         case BYD_4UP:
149         case BYD_4DOWN:
150                 break;
151
152         default:
153                 psmouse_warn(psmouse,
154                              "Unrecognized Z: pkt = %02x %02x %02x %02x\n",
155                              psmouse->packet[0], psmouse->packet[1],
156                              psmouse->packet[2], psmouse->packet[3]);
157                 return PSMOUSE_BAD_DATA;
158         }
159
160         input_sync(dev);
161
162         return PSMOUSE_FULL_PACKET;
163 }
164
165 /* Send a sequence of bytes, where each is ACKed before the next is sent. */
166 static int byd_send_sequence(struct psmouse *psmouse, const u8 *seq, size_t len)
167 {
168         unsigned int i;
169
170         for (i = 0; i < len; ++i) {
171                 if (ps2_command(&psmouse->ps2dev, NULL, seq[i]))
172                         return -1;
173         }
174         return 0;
175 }
176
177 /* Keep scrolling after fingers are removed. */
178 #define SCROLL_INERTIAL         0x01
179 #define SCROLL_NO_INERTIAL      0x02
180
181 /* Clicking can be done by tapping or pressing. */
182 #define CLICK_BOTH              0x01
183 /* Clicking can only be done by pressing. */
184 #define CLICK_PRESS_ONLY        0x02
185
186 static int byd_enable(struct psmouse *psmouse)
187 {
188         const u8 seq1[] = { 0xE2, 0x00, 0xE0, 0x02, 0xE0 };
189         const u8 seq2[] = {
190                 0xD3, 0x01,
191                 0xD0, 0x00,
192                 0xD0, 0x04,
193                 /* Whether clicking is done by tapping or pressing. */
194                 0xD4, CLICK_PRESS_ONLY,
195                 0xD5, 0x01,
196                 0xD7, 0x03,
197                 /* Vertical and horizontal one-finger scroll zone inertia. */
198                 0xD8, SCROLL_INERTIAL,
199                 0xDA, 0x05,
200                 0xDB, 0x02,
201                 0xE4, 0x05,
202                 0xD6, 0x01,
203                 0xDE, 0x04,
204                 0xE3, 0x01,
205                 0xCF, 0x00,
206                 0xD2, 0x03,
207                 /* Vertical and horizontal two-finger scrolling inertia. */
208                 0xE5, SCROLL_INERTIAL,
209                 0xD9, 0x02,
210                 0xD9, 0x07,
211                 0xDC, 0x03,
212                 0xDD, 0x03,
213                 0xDF, 0x03,
214                 0xE1, 0x03,
215                 0xD1, 0x00,
216                 0xCE, 0x00,
217                 0xCC, 0x00,
218                 0xE0, 0x00,
219                 0xE2, 0x01
220         };
221         u8 param[4];
222
223         if (byd_send_sequence(psmouse, seq1, ARRAY_SIZE(seq1)))
224                 return -1;
225
226         /* Send a 0x01 command, which should return 4 bytes. */
227         if (ps2_command(&psmouse->ps2dev, param, 0x0401))
228                 return -1;
229
230         if (byd_send_sequence(psmouse, seq2, ARRAY_SIZE(seq2)))
231                 return -1;
232
233         return 0;
234 }
235
236 /*
237  * Send the set of PS/2 commands required to make it identify as an
238  * intellimouse with 4-byte instead of 3-byte packets.
239  */
240 static int byd_send_intellimouse_sequence(struct psmouse *psmouse)
241 {
242         struct ps2dev *ps2dev = &psmouse->ps2dev;
243         u8 param[4];
244         int i;
245         const struct {
246                 u16 command;
247                 u8 arg;
248         } seq[] = {
249                 { PSMOUSE_CMD_RESET_BAT, 0 },
250                 { PSMOUSE_CMD_RESET_BAT, 0 },
251                 { PSMOUSE_CMD_GETID, 0 },
252                 { PSMOUSE_CMD_SETSCALE11, 0 },
253                 { PSMOUSE_CMD_SETSCALE11, 0 },
254                 { PSMOUSE_CMD_SETSCALE11, 0 },
255                 { PSMOUSE_CMD_GETINFO, 0 },
256                 { PSMOUSE_CMD_SETRES, 0x03 },
257                 { PSMOUSE_CMD_SETRATE, 0xC8 },
258                 { PSMOUSE_CMD_SETRATE, 0x64 },
259                 { PSMOUSE_CMD_SETRATE, 0x50 },
260                 { PSMOUSE_CMD_GETID, 0 },
261                 { PSMOUSE_CMD_SETRATE, 0xC8 },
262                 { PSMOUSE_CMD_SETRATE, 0xC8 },
263                 { PSMOUSE_CMD_SETRATE, 0x50 },
264                 { PSMOUSE_CMD_GETID, 0 },
265                 { PSMOUSE_CMD_SETRATE, 0x64 },
266                 { PSMOUSE_CMD_SETRES, 0x03 },
267                 { PSMOUSE_CMD_ENABLE, 0 }
268         };
269
270         memset(param, 0, sizeof(param));
271         for (i = 0; i < ARRAY_SIZE(seq); ++i) {
272                 param[0] = seq[i].arg;
273                 if (ps2_command(ps2dev, param, seq[i].command))
274                         return -1;
275         }
276
277         return 0;
278 }
279
280 static int byd_reset_touchpad(struct psmouse *psmouse)
281 {
282         if (byd_send_intellimouse_sequence(psmouse))
283                 return -EIO;
284
285         if (byd_enable(psmouse))
286                 return -EIO;
287
288         return 0;
289 }
290
291 static int byd_reconnect(struct psmouse *psmouse)
292 {
293         int retry = 0, error = 0;
294
295         psmouse_dbg(psmouse, "Reconnect\n");
296         do {
297                 psmouse_reset(psmouse);
298                 if (retry)
299                         ssleep(1);
300                 error = byd_detect(psmouse, 0);
301         } while (error && ++retry < 3);
302
303         if (error)
304                 return error;
305
306         psmouse_dbg(psmouse, "Reconnected after %d attempts\n", retry);
307
308         error = byd_reset_touchpad(psmouse);
309         if (error) {
310                 psmouse_err(psmouse, "Unable to initialize device\n");
311                 return error;
312         }
313
314         return 0;
315 }
316
317 int byd_init(struct psmouse *psmouse)
318 {
319         struct input_dev *dev = psmouse->dev;
320
321         if (psmouse_reset(psmouse))
322                 return -EIO;
323
324         if (byd_reset_touchpad(psmouse))
325                 return -EIO;
326
327         psmouse->reconnect = byd_reconnect;
328         psmouse->protocol_handler = byd_process_byte;
329         psmouse->pktsize = 4;
330         psmouse->resync_time = 0;
331
332         __set_bit(BTN_MIDDLE, dev->keybit);
333         __set_bit(REL_WHEEL, dev->relbit);
334         __set_bit(REL_HWHEEL, dev->relbit);
335
336         return 0;
337 }