1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * Synaptics TouchPad PS/2 mouse driver
41da177e4SLinus Torvalds *
51da177e4SLinus Torvalds * 2003 Dmitry Torokhov <[email protected]>
61da177e4SLinus Torvalds * Added support for pass-through port. Special thanks to Peter Berg Larsen
71da177e4SLinus Torvalds * for explaining various Synaptics quirks.
81da177e4SLinus Torvalds *
91da177e4SLinus Torvalds * 2003 Peter Osterlund <[email protected]>
101da177e4SLinus Torvalds * Ported to 2.5 input device infrastructure.
111da177e4SLinus Torvalds *
121da177e4SLinus Torvalds * Copyright (C) 2001 Stefan Gmeiner <[email protected]>
131da177e4SLinus Torvalds * start merging tpconfig and gpm code to a xfree-input module
141da177e4SLinus Torvalds * adding some changes and extensions (ex. 3rd and 4th button)
151da177e4SLinus Torvalds *
161da177e4SLinus Torvalds * Copyright (c) 1997 C. Scott Ananian <[email protected]>
171da177e4SLinus Torvalds * Copyright (c) 1998-2000 Bruce Kalk <[email protected]>
181da177e4SLinus Torvalds * code for the special synaptics commands (from the tpconfig-source)
191da177e4SLinus Torvalds *
201da177e4SLinus Torvalds * Trademarks are the property of their respective owners.
211da177e4SLinus Torvalds */
221da177e4SLinus Torvalds
231da177e4SLinus Torvalds #include <linux/module.h>
248521478fSDmitry Torokhov #include <linux/delay.h>
257705d548SDmitry Torokhov #include <linux/dmi.h>
26fec6e525SHenrik Rydberg #include <linux/input/mt.h>
271da177e4SLinus Torvalds #include <linux/serio.h>
281da177e4SLinus Torvalds #include <linux/libps2.h>
29e839ffabSBenjamin Tissoires #include <linux/rmi.h>
30e839ffabSBenjamin Tissoires #include <linux/i2c.h>
315a0e3ad6STejun Heo #include <linux/slab.h>
321da177e4SLinus Torvalds #include "psmouse.h"
331da177e4SLinus Torvalds #include "synaptics.h"
341da177e4SLinus Torvalds
351da177e4SLinus Torvalds /*
361da177e4SLinus Torvalds * The x/y limits are taken from the Synaptics TouchPad interfacing Guide,
371da177e4SLinus Torvalds * section 2.3.2, which says that they should be valid regardless of the
381da177e4SLinus Torvalds * actual size of the sensor.
3983ba9ea8SDmitry Torokhov * Note that newer firmware allows querying device for maximum useable
4083ba9ea8SDmitry Torokhov * coordinates.
411da177e4SLinus Torvalds */
42c0394506SSeth Forshee #define XMIN 0
43c0394506SSeth Forshee #define XMAX 6143
44c0394506SSeth Forshee #define YMIN 0
45c0394506SSeth Forshee #define YMAX 6143
461da177e4SLinus Torvalds #define XMIN_NOMINAL 1472
471da177e4SLinus Torvalds #define XMAX_NOMINAL 5472
481da177e4SLinus Torvalds #define YMIN_NOMINAL 1408
491da177e4SLinus Torvalds #define YMAX_NOMINAL 4448
501da177e4SLinus Torvalds
51c0394506SSeth Forshee /* Size in bits of absolute position values reported by the hardware */
52c0394506SSeth Forshee #define ABS_POS_BITS 13
53c0394506SSeth Forshee
54c0394506SSeth Forshee /*
55824efd37SSeth Forshee * These values should represent the absolute maximum value that will
56824efd37SSeth Forshee * be reported for a positive position value. Some Synaptics firmware
57824efd37SSeth Forshee * uses this value to indicate a finger near the edge of the touchpad
58824efd37SSeth Forshee * whose precise position cannot be determined.
59824efd37SSeth Forshee *
60824efd37SSeth Forshee * At least one touchpad is known to report positions in excess of this
61824efd37SSeth Forshee * value which are actually negative values truncated to the 13-bit
62824efd37SSeth Forshee * reporting range. These values have never been observed to be lower
63824efd37SSeth Forshee * than 8184 (i.e. -8), so we treat all values greater than 8176 as
64824efd37SSeth Forshee * negative and any other value as positive.
65c0394506SSeth Forshee */
66824efd37SSeth Forshee #define X_MAX_POSITIVE 8176
67824efd37SSeth Forshee #define Y_MAX_POSITIVE 8176
681da177e4SLinus Torvalds
6958fd9af6SBenjamin Tissoires /* maximum ABS_MT_POSITION displacement (in mm) */
7058fd9af6SBenjamin Tissoires #define DMAX 10
7158fd9af6SBenjamin Tissoires
7255e3d922SAndres Salomon /*****************************************************************************
7355e3d922SAndres Salomon * Stuff we need even when we do not want native Synaptics support
7455e3d922SAndres Salomon ****************************************************************************/
751da177e4SLinus Torvalds
761da177e4SLinus Torvalds /*
771da177e4SLinus Torvalds * Set the synaptics touchpad mode byte by special commands
781da177e4SLinus Torvalds */
synaptics_mode_cmd(struct psmouse * psmouse,u8 mode)79f6c4442bSDmitry Torokhov static int synaptics_mode_cmd(struct psmouse *psmouse, u8 mode)
801da177e4SLinus Torvalds {
81f6c4442bSDmitry Torokhov u8 param[1];
82212baf03SDmitry Torokhov int error;
831da177e4SLinus Torvalds
8408be954bSDmitry Torokhov error = ps2_sliced_command(&psmouse->ps2dev, mode);
85212baf03SDmitry Torokhov if (error)
86212baf03SDmitry Torokhov return error;
87212baf03SDmitry Torokhov
881da177e4SLinus Torvalds param[0] = SYN_PS_SET_MODE2;
89212baf03SDmitry Torokhov error = ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_SETRATE);
90212baf03SDmitry Torokhov if (error)
91212baf03SDmitry Torokhov return error;
92212baf03SDmitry Torokhov
931da177e4SLinus Torvalds return 0;
941da177e4SLinus Torvalds }
951da177e4SLinus Torvalds
synaptics_detect(struct psmouse * psmouse,bool set_properties)96b7802c5cSDmitry Torokhov int synaptics_detect(struct psmouse *psmouse, bool set_properties)
9755e3d922SAndres Salomon {
9855e3d922SAndres Salomon struct ps2dev *ps2dev = &psmouse->ps2dev;
99f39f8688SDmitry Torokhov u8 param[4] = { 0 };
10055e3d922SAndres Salomon
10155e3d922SAndres Salomon ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
10255e3d922SAndres Salomon ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
10355e3d922SAndres Salomon ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
10455e3d922SAndres Salomon ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
10555e3d922SAndres Salomon ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO);
10655e3d922SAndres Salomon
10755e3d922SAndres Salomon if (param[1] != 0x47)
10855e3d922SAndres Salomon return -ENODEV;
10955e3d922SAndres Salomon
11055e3d922SAndres Salomon if (set_properties) {
11155e3d922SAndres Salomon psmouse->vendor = "Synaptics";
11255e3d922SAndres Salomon psmouse->name = "TouchPad";
11355e3d922SAndres Salomon }
11455e3d922SAndres Salomon
11555e3d922SAndres Salomon return 0;
11655e3d922SAndres Salomon }
11755e3d922SAndres Salomon
synaptics_reset(struct psmouse * psmouse)11855e3d922SAndres Salomon void synaptics_reset(struct psmouse *psmouse)
11955e3d922SAndres Salomon {
12055e3d922SAndres Salomon /* reset touchpad back to relative mode, gestures enabled */
12155e3d922SAndres Salomon synaptics_mode_cmd(psmouse, 0);
12255e3d922SAndres Salomon }
12355e3d922SAndres Salomon
124e839ffabSBenjamin Tissoires #if defined(CONFIG_MOUSE_PS2_SYNAPTICS) || \
125e839ffabSBenjamin Tissoires defined(CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS)
1260f68f39cSHans de Goede
12743e19888SHans de Goede /* This list has been kindly provided by Synaptics. */
12843e19888SHans de Goede static const char * const topbuttonpad_pnp_ids[] = {
12943e19888SHans de Goede "LEN0017",
13043e19888SHans de Goede "LEN0018",
13143e19888SHans de Goede "LEN0019",
13243e19888SHans de Goede "LEN0023",
13343e19888SHans de Goede "LEN002A",
13443e19888SHans de Goede "LEN002B",
13543e19888SHans de Goede "LEN002C",
13643e19888SHans de Goede "LEN002D",
13743e19888SHans de Goede "LEN002E",
13843e19888SHans de Goede "LEN0033", /* Helix */
1390f68f39cSHans de Goede "LEN0034", /* T431s, L440, L540, T540, W540, X1 Carbon 2nd */
14043e19888SHans de Goede "LEN0035", /* X240 */
14143e19888SHans de Goede "LEN0036", /* T440 */
1428543cf1cSPeter Hutterer "LEN0037", /* X1 Carbon 2nd */
14343e19888SHans de Goede "LEN0038",
144e4742b1eSTakashi Iwai "LEN0039", /* T440s */
14543e19888SHans de Goede "LEN0041",
14643e19888SHans de Goede "LEN0042", /* Yoga */
14743e19888SHans de Goede "LEN0045",
14843e19888SHans de Goede "LEN0047",
1497f2ca8b5SPeter Hutterer "LEN2000", /* S540 */
1500f68f39cSHans de Goede "LEN2001", /* Edge E431 */
151e76aed9dSHans de Goede "LEN2002", /* Edge E531 */
15243e19888SHans de Goede "LEN2003",
15343e19888SHans de Goede "LEN2004", /* L440 */
15443e19888SHans de Goede "LEN2005",
15598dc0703SRamiro Morales "LEN2006", /* Edge E440/E540 */
15643e19888SHans de Goede "LEN2007",
15743e19888SHans de Goede "LEN2008",
15843e19888SHans de Goede "LEN2009",
15943e19888SHans de Goede "LEN200A",
16043e19888SHans de Goede "LEN200B",
16143e19888SHans de Goede NULL
16243e19888SHans de Goede };
16355e3d922SAndres Salomon
164496b7d2eSArnd Bergmann #ifdef CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS
165e839ffabSBenjamin Tissoires static const char * const smbus_pnp_ids[] = {
166e839ffabSBenjamin Tissoires /* all of the topbuttonpad_pnp_ids are valid, we just add some extras */
167*a609cb4cSAditya Garg "DLL060d", /* Dell Precision M3800 */
168e839ffabSBenjamin Tissoires "LEN0048", /* X1 Carbon 3 */
169e839ffabSBenjamin Tissoires "LEN0046", /* X250 */
1705179a9dfSBenjamin Tissoires "LEN0049", /* Yoga 11e */
171e839ffabSBenjamin Tissoires "LEN004a", /* W541 */
1729df39bedSLyude Paul "LEN005b", /* P50 */
173ca504728SYussuf Khalil "LEN005e", /* T560 */
174bf502391SLyude Paul "LEN006c", /* T470s */
175642aa86eSDennis Kadioglu "LEN007a", /* T470s */
176ad8fb554SBenjamin Tissoires "LEN0071", /* T480 */
1779b207102SDmitry Torokhov "LEN0072", /* X1 Carbon Gen 5 (2017) - Elan/ALPS trackpoint */
17815e2cffeSEdvard Holst "LEN0073", /* X1 Carbon G5 (Elantech) */
179fc1156f3SHans Verkuil "LEN0091", /* X1 Carbon 6 */
1805717a09aSAaron Ma "LEN0092", /* X1 Carbon 6 */
181abbe3acdSCole Rogers "LEN0093", /* T480 */
1825717a09aSAaron Ma "LEN0096", /* X280 */
183ad8fb554SBenjamin Tissoires "LEN0097", /* X280 -> ALPS trackpoint */
184127e4a1bSJason A. Donenfeld "LEN0099", /* X1 Extreme Gen 1 / P1 Gen 1 */
1851976d7d2SNick Black "LEN009b", /* T580 */
186127e4a1bSJason A. Donenfeld "LEN0402", /* X1 Extreme Gen 2 / P1 Gen 2 */
1877984b435SLyude Paul "LEN040f", /* P1 Gen 3 */
188c1f342f3SJosé Pekkarinen "LEN0411", /* L14 Gen 1 */
189e839ffabSBenjamin Tissoires "LEN200f", /* T450s */
190b8a3d819SGaurav Agrawal "LEN2044", /* L470 */
1919843f3e0SAlexander Mikhaylenko "LEN2054", /* E480 */
1929843f3e0SAlexander Mikhaylenko "LEN2055", /* E580 */
193470d154aSHans de Goede "LEN2068", /* T14 Gen 1 */
1942abc698aSAditya Garg "SYN1221", /* TUXEDO InfinityBook Pro 14 v5 */
195f04f03d3SDmitry Torokhov "SYN3003", /* HP EliteBook 850 G1 */
196da897484SJonathan Denose "SYN3015", /* HP EliteBook 840 G2 */
1977a717122SMantas Mikulėnas "SYN3052", /* HP EliteBook 840 G4 */
1985a6dab15STeika Kazura "SYN3221", /* HP 15-ay000 */
19925f8c834SDmitry Torokhov "SYN323d", /* HP Spectre X360 13-w013dx */
2001369d0abSYussuf Khalil "SYN3257", /* HP Envy 13-ad105ng */
20147d768b3SAditya Garg "TOS01f6", /* Dynabook Portege X30L-G */
2026d7ea088SManuel Fombuena "TOS0213", /* Dynabook Portege X30-D */
203de4e374bSDmitry Torokhov NULL
204de4e374bSDmitry Torokhov };
205496b7d2eSArnd Bergmann #endif
206de4e374bSDmitry Torokhov
207f4101ff8SBenjamin Tissoires static const char * const forcepad_pnp_ids[] = {
208f4101ff8SBenjamin Tissoires "SYN300D",
209f4101ff8SBenjamin Tissoires "SYN3014",
210f4101ff8SBenjamin Tissoires NULL
211f4101ff8SBenjamin Tissoires };
212f4101ff8SBenjamin Tissoires
2131a49a0a0SJJ Ding /*
21426332247SChristophe JAILLET * Send a command to the synaptics touchpad by special commands
21555e3d922SAndres Salomon */
synaptics_send_cmd(struct psmouse * psmouse,u8 cmd,u8 * param)216f6c4442bSDmitry Torokhov static int synaptics_send_cmd(struct psmouse *psmouse, u8 cmd, u8 *param)
21755e3d922SAndres Salomon {
218e839ffabSBenjamin Tissoires int error;
219e839ffabSBenjamin Tissoires
22008be954bSDmitry Torokhov error = ps2_sliced_command(&psmouse->ps2dev, cmd);
221e839ffabSBenjamin Tissoires if (error)
222e839ffabSBenjamin Tissoires return error;
223e839ffabSBenjamin Tissoires
224e839ffabSBenjamin Tissoires error = ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO);
225e839ffabSBenjamin Tissoires if (error)
226e839ffabSBenjamin Tissoires return error;
227e839ffabSBenjamin Tissoires
22855e3d922SAndres Salomon return 0;
22955e3d922SAndres Salomon }
23055e3d922SAndres Salomon
synaptics_query_int(struct psmouse * psmouse,u8 query_cmd,u32 * val)2312c6ecbbaSDmitry Torokhov static int synaptics_query_int(struct psmouse *psmouse, u8 query_cmd, u32 *val)
2322c6ecbbaSDmitry Torokhov {
2332c6ecbbaSDmitry Torokhov int error;
2342c6ecbbaSDmitry Torokhov union {
2352c6ecbbaSDmitry Torokhov __be32 be_val;
2362c6ecbbaSDmitry Torokhov char buf[4];
2372c6ecbbaSDmitry Torokhov } resp = { 0 };
2382c6ecbbaSDmitry Torokhov
2392c6ecbbaSDmitry Torokhov error = synaptics_send_cmd(psmouse, query_cmd, resp.buf + 1);
2402c6ecbbaSDmitry Torokhov if (error)
2412c6ecbbaSDmitry Torokhov return error;
2422c6ecbbaSDmitry Torokhov
2432c6ecbbaSDmitry Torokhov *val = be32_to_cpu(resp.be_val);
2442c6ecbbaSDmitry Torokhov return 0;
2452c6ecbbaSDmitry Torokhov }
2462c6ecbbaSDmitry Torokhov
2471da177e4SLinus Torvalds /*
248e839ffabSBenjamin Tissoires * Identify Touchpad
249e839ffabSBenjamin Tissoires * See also the SYN_ID_* macros
250e839ffabSBenjamin Tissoires */
synaptics_identify(struct psmouse * psmouse,struct synaptics_device_info * info)251e839ffabSBenjamin Tissoires static int synaptics_identify(struct psmouse *psmouse,
252e839ffabSBenjamin Tissoires struct synaptics_device_info *info)
253e839ffabSBenjamin Tissoires {
254e839ffabSBenjamin Tissoires int error;
255e839ffabSBenjamin Tissoires
2562c6ecbbaSDmitry Torokhov error = synaptics_query_int(psmouse, SYN_QUE_IDENTIFY, &info->identity);
257e839ffabSBenjamin Tissoires if (error)
258e839ffabSBenjamin Tissoires return error;
259e839ffabSBenjamin Tissoires
260e839ffabSBenjamin Tissoires return SYN_ID_IS_SYNAPTICS(info->identity) ? 0 : -ENXIO;
261e839ffabSBenjamin Tissoires }
262e839ffabSBenjamin Tissoires
263e839ffabSBenjamin Tissoires /*
2641da177e4SLinus Torvalds * Read the model-id bytes from the touchpad
2651da177e4SLinus Torvalds * see also SYN_MODEL_* macros
2661da177e4SLinus Torvalds */
synaptics_model_id(struct psmouse * psmouse,struct synaptics_device_info * info)2676c53694fSDmitry Torokhov static int synaptics_model_id(struct psmouse *psmouse,
2686c53694fSDmitry Torokhov struct synaptics_device_info *info)
2691da177e4SLinus Torvalds {
2702c6ecbbaSDmitry Torokhov return synaptics_query_int(psmouse, SYN_QUE_MODEL, &info->model_id);
2711da177e4SLinus Torvalds }
2721da177e4SLinus Torvalds
273e839ffabSBenjamin Tissoires /*
274e839ffabSBenjamin Tissoires * Read the firmware id from the touchpad
275e839ffabSBenjamin Tissoires */
synaptics_firmware_id(struct psmouse * psmouse,struct synaptics_device_info * info)276e839ffabSBenjamin Tissoires static int synaptics_firmware_id(struct psmouse *psmouse,
277e839ffabSBenjamin Tissoires struct synaptics_device_info *info)
278e839ffabSBenjamin Tissoires {
2792c6ecbbaSDmitry Torokhov return synaptics_query_int(psmouse, SYN_QUE_FIRMWARE_ID,
2802c6ecbbaSDmitry Torokhov &info->firmware_id);
28106aa374bSBenjamin Tissoires }
28206aa374bSBenjamin Tissoires
2831da177e4SLinus Torvalds /*
28406aa374bSBenjamin Tissoires * Read the board id and the "More Extended Queries" from the touchpad
285c6bd9d46SDaniel Kurtz * The board id is encoded in the "QUERY MODES" response
286c6bd9d46SDaniel Kurtz */
synaptics_query_modes(struct psmouse * psmouse,struct synaptics_device_info * info)2876c53694fSDmitry Torokhov static int synaptics_query_modes(struct psmouse *psmouse,
2886c53694fSDmitry Torokhov struct synaptics_device_info *info)
289c6bd9d46SDaniel Kurtz {
290f6c4442bSDmitry Torokhov u8 bid[3];
2916c53694fSDmitry Torokhov int error;
292c6bd9d46SDaniel Kurtz
293b57a7128SBenjamin Tissoires /* firmwares prior 7.5 have no board_id encoded */
2946c53694fSDmitry Torokhov if (SYN_ID_FULL(info->identity) < 0x705)
295b57a7128SBenjamin Tissoires return 0;
296b57a7128SBenjamin Tissoires
2976c53694fSDmitry Torokhov error = synaptics_send_cmd(psmouse, SYN_QUE_MODES, bid);
2986c53694fSDmitry Torokhov if (error)
2996c53694fSDmitry Torokhov return error;
3006c53694fSDmitry Torokhov
3016c53694fSDmitry Torokhov info->board_id = ((bid[0] & 0xfc) << 6) | bid[1];
30206aa374bSBenjamin Tissoires
30306aa374bSBenjamin Tissoires if (SYN_MEXT_CAP_BIT(bid[0]))
3042c6ecbbaSDmitry Torokhov return synaptics_query_int(psmouse, SYN_QUE_MEXT_CAPAB_10,
3052c6ecbbaSDmitry Torokhov &info->ext_cap_10);
30606aa374bSBenjamin Tissoires
307c6bd9d46SDaniel Kurtz return 0;
308c6bd9d46SDaniel Kurtz }
309c6bd9d46SDaniel Kurtz
310c6bd9d46SDaniel Kurtz /*
3111da177e4SLinus Torvalds * Read the capability-bits from the touchpad
3121da177e4SLinus Torvalds * see also the SYN_CAP_* macros
3131da177e4SLinus Torvalds */
synaptics_capability(struct psmouse * psmouse,struct synaptics_device_info * info)3146c53694fSDmitry Torokhov static int synaptics_capability(struct psmouse *psmouse,
3156c53694fSDmitry Torokhov struct synaptics_device_info *info)
3161da177e4SLinus Torvalds {
3176c53694fSDmitry Torokhov int error;
3181da177e4SLinus Torvalds
3192c6ecbbaSDmitry Torokhov error = synaptics_query_int(psmouse, SYN_QUE_CAPABILITIES,
3202c6ecbbaSDmitry Torokhov &info->capabilities);
3216c53694fSDmitry Torokhov if (error)
3226c53694fSDmitry Torokhov return error;
3236c53694fSDmitry Torokhov
3246c53694fSDmitry Torokhov info->ext_cap = info->ext_cap_0c = 0;
3255f57d67dSTakashi Iwai
3263619b8feSDmitry Torokhov /*
3273619b8feSDmitry Torokhov * Older firmwares had submodel ID fixed to 0x47
3283619b8feSDmitry Torokhov */
3296c53694fSDmitry Torokhov if (SYN_ID_FULL(info->identity) < 0x705 &&
3306c53694fSDmitry Torokhov SYN_CAP_SUBMODEL_ID(info->capabilities) != 0x47) {
3316c53694fSDmitry Torokhov return -ENXIO;
3323619b8feSDmitry Torokhov }
3331da177e4SLinus Torvalds
3341da177e4SLinus Torvalds /*
3351da177e4SLinus Torvalds * Unless capExtended is set the rest of the flags should be ignored
3361da177e4SLinus Torvalds */
3376c53694fSDmitry Torokhov if (!SYN_CAP_EXTENDED(info->capabilities))
3386c53694fSDmitry Torokhov info->capabilities = 0;
3391da177e4SLinus Torvalds
3406c53694fSDmitry Torokhov if (SYN_EXT_CAP_REQUESTS(info->capabilities) >= 1) {
3412c6ecbbaSDmitry Torokhov error = synaptics_query_int(psmouse, SYN_QUE_EXT_CAPAB,
3422c6ecbbaSDmitry Torokhov &info->ext_cap);
3432c6ecbbaSDmitry Torokhov if (error) {
344b5d21704SDmitry Torokhov psmouse_warn(psmouse,
345b5d21704SDmitry Torokhov "device claims to have extended capabilities, but I'm not able to read them.\n");
3461da177e4SLinus Torvalds } else {
3471da177e4SLinus Torvalds /*
3481da177e4SLinus Torvalds * if nExtBtn is greater than 8 it should be considered
3491da177e4SLinus Torvalds * invalid and treated as 0
3501da177e4SLinus Torvalds */
3516c53694fSDmitry Torokhov if (SYN_CAP_MULTI_BUTTON_NO(info->ext_cap) > 8)
3522c6ecbbaSDmitry Torokhov info->ext_cap &= ~SYN_CAP_MB_MASK;
3531da177e4SLinus Torvalds }
3541da177e4SLinus Torvalds }
3555f57d67dSTakashi Iwai
3566c53694fSDmitry Torokhov if (SYN_EXT_CAP_REQUESTS(info->capabilities) >= 4) {
3572c6ecbbaSDmitry Torokhov error = synaptics_query_int(psmouse, SYN_QUE_EXT_CAPAB_0C,
3582c6ecbbaSDmitry Torokhov &info->ext_cap_0c);
3596c53694fSDmitry Torokhov if (error)
360b5d21704SDmitry Torokhov psmouse_warn(psmouse,
361b5d21704SDmitry Torokhov "device claims to have extended capability 0x0c, but I'm not able to read it.\n");
3625f57d67dSTakashi Iwai }
3635f57d67dSTakashi Iwai
3641da177e4SLinus Torvalds return 0;
3651da177e4SLinus Torvalds }
3661da177e4SLinus Torvalds
3671da177e4SLinus Torvalds /*
36883ba9ea8SDmitry Torokhov * Read touchpad resolution and maximum reported coordinates
369ec20a022STero Saarni * Resolution is left zero if touchpad does not support the query
370ec20a022STero Saarni */
synaptics_resolution(struct psmouse * psmouse,struct synaptics_device_info * info)3716c53694fSDmitry Torokhov static int synaptics_resolution(struct psmouse *psmouse,
3726c53694fSDmitry Torokhov struct synaptics_device_info *info)
373ec20a022STero Saarni {
374f6c4442bSDmitry Torokhov u8 resp[3];
3756c53694fSDmitry Torokhov int error;
376ec20a022STero Saarni
3776c53694fSDmitry Torokhov if (SYN_ID_MAJOR(info->identity) < 4)
378bbddd199STakashi Iwai return 0;
379ec20a022STero Saarni
3806c53694fSDmitry Torokhov error = synaptics_send_cmd(psmouse, SYN_QUE_RESOLUTION, resp);
3816c53694fSDmitry Torokhov if (!error) {
382a66413fbSDmitry Torokhov if (resp[0] != 0 && (resp[1] & 0x80) && resp[2] != 0) {
3836c53694fSDmitry Torokhov info->x_res = resp[0]; /* x resolution in units/mm */
3846c53694fSDmitry Torokhov info->y_res = resp[2]; /* y resolution in units/mm */
385ec20a022STero Saarni }
38683ba9ea8SDmitry Torokhov }
38783ba9ea8SDmitry Torokhov
3886c53694fSDmitry Torokhov if (SYN_EXT_CAP_REQUESTS(info->capabilities) >= 5 &&
3896c53694fSDmitry Torokhov SYN_CAP_MAX_DIMENSIONS(info->ext_cap_0c)) {
3906c53694fSDmitry Torokhov error = synaptics_send_cmd(psmouse,
3916c53694fSDmitry Torokhov SYN_QUE_EXT_MAX_COORDS, resp);
3926c53694fSDmitry Torokhov if (error) {
393b5d21704SDmitry Torokhov psmouse_warn(psmouse,
394b5d21704SDmitry Torokhov "device claims to have max coordinates query, but I'm not able to read it.\n");
39583ba9ea8SDmitry Torokhov } else {
3966c53694fSDmitry Torokhov info->x_max = (resp[0] << 5) | ((resp[1] & 0x0f) << 1);
3976c53694fSDmitry Torokhov info->y_max = (resp[2] << 5) | ((resp[1] & 0xf0) >> 3);
3989aff6598SDaniel Martin psmouse_info(psmouse,
3999aff6598SDaniel Martin "queried max coordinates: x [..%d], y [..%d]\n",
4006c53694fSDmitry Torokhov info->x_max, info->y_max);
401a66413fbSDmitry Torokhov }
402a66413fbSDmitry Torokhov }
403a66413fbSDmitry Torokhov
4046c53694fSDmitry Torokhov if (SYN_CAP_MIN_DIMENSIONS(info->ext_cap_0c) &&
4056c53694fSDmitry Torokhov (SYN_EXT_CAP_REQUESTS(info->capabilities) >= 7 ||
406ac097930SDaniel Martin /*
407ac097930SDaniel Martin * Firmware v8.1 does not report proper number of extended
408ac097930SDaniel Martin * capabilities, but has been proven to report correct min
409ac097930SDaniel Martin * coordinates.
410ac097930SDaniel Martin */
4116c53694fSDmitry Torokhov SYN_ID_FULL(info->identity) == 0x801)) {
4126c53694fSDmitry Torokhov error = synaptics_send_cmd(psmouse,
4136c53694fSDmitry Torokhov SYN_QUE_EXT_MIN_COORDS, resp);
4146c53694fSDmitry Torokhov if (error) {
415b5d21704SDmitry Torokhov psmouse_warn(psmouse,
416b5d21704SDmitry Torokhov "device claims to have min coordinates query, but I'm not able to read it.\n");
417a66413fbSDmitry Torokhov } else {
4186c53694fSDmitry Torokhov info->x_min = (resp[0] << 5) | ((resp[1] & 0x0f) << 1);
4196c53694fSDmitry Torokhov info->y_min = (resp[2] << 5) | ((resp[1] & 0xf0) >> 3);
4209aff6598SDaniel Martin psmouse_info(psmouse,
4219aff6598SDaniel Martin "queried min coordinates: x [%d..], y [%d..]\n",
4226c53694fSDmitry Torokhov info->x_min, info->y_min);
42383ba9ea8SDmitry Torokhov }
42483ba9ea8SDmitry Torokhov }
425ec20a022STero Saarni
426ec20a022STero Saarni return 0;
427ec20a022STero Saarni }
428ec20a022STero Saarni
synaptics_query_hardware(struct psmouse * psmouse,struct synaptics_device_info * info)4296c53694fSDmitry Torokhov static int synaptics_query_hardware(struct psmouse *psmouse,
4306c53694fSDmitry Torokhov struct synaptics_device_info *info)
4311da177e4SLinus Torvalds {
4326c53694fSDmitry Torokhov int error;
4331da177e4SLinus Torvalds
43427555511SEric Biggers memset(info, 0, sizeof(*info));
43527555511SEric Biggers
4366c53694fSDmitry Torokhov error = synaptics_identify(psmouse, info);
4376c53694fSDmitry Torokhov if (error)
4386c53694fSDmitry Torokhov return error;
4396c53694fSDmitry Torokhov
4406c53694fSDmitry Torokhov error = synaptics_model_id(psmouse, info);
4416c53694fSDmitry Torokhov if (error)
4426c53694fSDmitry Torokhov return error;
4436c53694fSDmitry Torokhov
4446c53694fSDmitry Torokhov error = synaptics_firmware_id(psmouse, info);
4456c53694fSDmitry Torokhov if (error)
4466c53694fSDmitry Torokhov return error;
4476c53694fSDmitry Torokhov
4486c53694fSDmitry Torokhov error = synaptics_query_modes(psmouse, info);
4496c53694fSDmitry Torokhov if (error)
4506c53694fSDmitry Torokhov return error;
4516c53694fSDmitry Torokhov
4526c53694fSDmitry Torokhov error = synaptics_capability(psmouse, info);
4536c53694fSDmitry Torokhov if (error)
4546c53694fSDmitry Torokhov return error;
4556c53694fSDmitry Torokhov
4566c53694fSDmitry Torokhov error = synaptics_resolution(psmouse, info);
4576c53694fSDmitry Torokhov if (error)
4586c53694fSDmitry Torokhov return error;
4598b04babaSDaniel Martin
4601da177e4SLinus Torvalds return 0;
4611da177e4SLinus Torvalds }
4621da177e4SLinus Torvalds
463e839ffabSBenjamin Tissoires #endif /* CONFIG_MOUSE_PS2_SYNAPTICS || CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS */
464e839ffabSBenjamin Tissoires
465e839ffabSBenjamin Tissoires #ifdef CONFIG_MOUSE_PS2_SYNAPTICS
466e839ffabSBenjamin Tissoires
467e839ffabSBenjamin Tissoires static bool cr48_profile_sensor;
468e839ffabSBenjamin Tissoires
469e839ffabSBenjamin Tissoires #define ANY_BOARD_ID 0
470e839ffabSBenjamin Tissoires struct min_max_quirk {
471e839ffabSBenjamin Tissoires const char * const *pnp_ids;
472e839ffabSBenjamin Tissoires struct {
473e839ffabSBenjamin Tissoires u32 min, max;
474e839ffabSBenjamin Tissoires } board_id;
475e839ffabSBenjamin Tissoires u32 x_min, x_max, y_min, y_max;
476e839ffabSBenjamin Tissoires };
477e839ffabSBenjamin Tissoires
478e839ffabSBenjamin Tissoires static const struct min_max_quirk min_max_pnpid_table[] = {
479e839ffabSBenjamin Tissoires {
480e839ffabSBenjamin Tissoires (const char * const []){"LEN0033", NULL},
481e839ffabSBenjamin Tissoires {ANY_BOARD_ID, ANY_BOARD_ID},
482e839ffabSBenjamin Tissoires 1024, 5052, 2258, 4832
483e839ffabSBenjamin Tissoires },
484e839ffabSBenjamin Tissoires {
485e839ffabSBenjamin Tissoires (const char * const []){"LEN0042", NULL},
486e839ffabSBenjamin Tissoires {ANY_BOARD_ID, ANY_BOARD_ID},
487e839ffabSBenjamin Tissoires 1232, 5710, 1156, 4696
488e839ffabSBenjamin Tissoires },
489e839ffabSBenjamin Tissoires {
490e839ffabSBenjamin Tissoires (const char * const []){"LEN0034", "LEN0036", "LEN0037",
491e839ffabSBenjamin Tissoires "LEN0039", "LEN2002", "LEN2004",
492e839ffabSBenjamin Tissoires NULL},
493e839ffabSBenjamin Tissoires {ANY_BOARD_ID, 2961},
494e839ffabSBenjamin Tissoires 1024, 5112, 2024, 4832
495e839ffabSBenjamin Tissoires },
496e839ffabSBenjamin Tissoires {
497e839ffabSBenjamin Tissoires (const char * const []){"LEN2000", NULL},
498e839ffabSBenjamin Tissoires {ANY_BOARD_ID, ANY_BOARD_ID},
499e839ffabSBenjamin Tissoires 1024, 5113, 2021, 4832
500e839ffabSBenjamin Tissoires },
501e839ffabSBenjamin Tissoires {
502e839ffabSBenjamin Tissoires (const char * const []){"LEN2001", NULL},
503e839ffabSBenjamin Tissoires {ANY_BOARD_ID, ANY_BOARD_ID},
504e839ffabSBenjamin Tissoires 1024, 5022, 2508, 4832
505e839ffabSBenjamin Tissoires },
506e839ffabSBenjamin Tissoires {
507e839ffabSBenjamin Tissoires (const char * const []){"LEN2006", NULL},
508e839ffabSBenjamin Tissoires {2691, 2691},
509e839ffabSBenjamin Tissoires 1024, 5045, 2457, 4832
510e839ffabSBenjamin Tissoires },
511e839ffabSBenjamin Tissoires {
512e839ffabSBenjamin Tissoires (const char * const []){"LEN2006", NULL},
513e839ffabSBenjamin Tissoires {ANY_BOARD_ID, ANY_BOARD_ID},
514e839ffabSBenjamin Tissoires 1264, 5675, 1171, 4688
515e839ffabSBenjamin Tissoires },
516e839ffabSBenjamin Tissoires { }
517e839ffabSBenjamin Tissoires };
518e839ffabSBenjamin Tissoires
519e839ffabSBenjamin Tissoires /*****************************************************************************
520e839ffabSBenjamin Tissoires * Synaptics communications functions
521e839ffabSBenjamin Tissoires ****************************************************************************/
522e839ffabSBenjamin Tissoires
523e839ffabSBenjamin Tissoires /*
524e839ffabSBenjamin Tissoires * Synaptics touchpads report the y coordinate from bottom to top, which is
525e839ffabSBenjamin Tissoires * opposite from what userspace expects.
526e839ffabSBenjamin Tissoires * This function is used to invert y before reporting.
527e839ffabSBenjamin Tissoires */
synaptics_invert_y(int y)528e839ffabSBenjamin Tissoires static int synaptics_invert_y(int y)
529e839ffabSBenjamin Tissoires {
530e839ffabSBenjamin Tissoires return YMAX_NOMINAL + YMIN_NOMINAL - y;
531e839ffabSBenjamin Tissoires }
532e839ffabSBenjamin Tissoires
533e839ffabSBenjamin Tissoires /*
534e839ffabSBenjamin Tissoires * Apply quirk(s) if the hardware matches
535e839ffabSBenjamin Tissoires */
synaptics_apply_quirks(struct psmouse * psmouse,struct synaptics_device_info * info)536e839ffabSBenjamin Tissoires static void synaptics_apply_quirks(struct psmouse *psmouse,
537e839ffabSBenjamin Tissoires struct synaptics_device_info *info)
538e839ffabSBenjamin Tissoires {
539e839ffabSBenjamin Tissoires int i;
540e839ffabSBenjamin Tissoires
541e839ffabSBenjamin Tissoires for (i = 0; min_max_pnpid_table[i].pnp_ids; i++) {
542e839ffabSBenjamin Tissoires if (!psmouse_matches_pnp_id(psmouse,
543e839ffabSBenjamin Tissoires min_max_pnpid_table[i].pnp_ids))
544e839ffabSBenjamin Tissoires continue;
545e839ffabSBenjamin Tissoires
546e839ffabSBenjamin Tissoires if (min_max_pnpid_table[i].board_id.min != ANY_BOARD_ID &&
547e839ffabSBenjamin Tissoires info->board_id < min_max_pnpid_table[i].board_id.min)
548e839ffabSBenjamin Tissoires continue;
549e839ffabSBenjamin Tissoires
550e839ffabSBenjamin Tissoires if (min_max_pnpid_table[i].board_id.max != ANY_BOARD_ID &&
551e839ffabSBenjamin Tissoires info->board_id > min_max_pnpid_table[i].board_id.max)
552e839ffabSBenjamin Tissoires continue;
553e839ffabSBenjamin Tissoires
554e839ffabSBenjamin Tissoires info->x_min = min_max_pnpid_table[i].x_min;
555e839ffabSBenjamin Tissoires info->x_max = min_max_pnpid_table[i].x_max;
556e839ffabSBenjamin Tissoires info->y_min = min_max_pnpid_table[i].y_min;
557e839ffabSBenjamin Tissoires info->y_max = min_max_pnpid_table[i].y_max;
558e839ffabSBenjamin Tissoires psmouse_info(psmouse,
559e839ffabSBenjamin Tissoires "quirked min/max coordinates: x [%d..%d], y [%d..%d]\n",
560e839ffabSBenjamin Tissoires info->x_min, info->x_max,
561e839ffabSBenjamin Tissoires info->y_min, info->y_max);
562e839ffabSBenjamin Tissoires break;
563e839ffabSBenjamin Tissoires }
564e839ffabSBenjamin Tissoires }
565e839ffabSBenjamin Tissoires
synaptics_has_agm(struct synaptics_data * priv)5663f9db52dSAnthony Martin static bool synaptics_has_agm(struct synaptics_data *priv)
5673f9db52dSAnthony Martin {
5683f9db52dSAnthony Martin return (SYN_CAP_ADV_GESTURE(priv->info.ext_cap_0c) ||
5693f9db52dSAnthony Martin SYN_CAP_IMAGE_SENSOR(priv->info.ext_cap_0c));
5703f9db52dSAnthony Martin }
5713f9db52dSAnthony Martin
synaptics_set_advanced_gesture_mode(struct psmouse * psmouse)5727968a5ddSDaniel Drake static int synaptics_set_advanced_gesture_mode(struct psmouse *psmouse)
5737968a5ddSDaniel Drake {
574f6c4442bSDmitry Torokhov static u8 param = 0xc8;
575212baf03SDmitry Torokhov int error;
5767968a5ddSDaniel Drake
57708be954bSDmitry Torokhov error = ps2_sliced_command(&psmouse->ps2dev, SYN_QUE_MODEL);
578212baf03SDmitry Torokhov if (error)
579212baf03SDmitry Torokhov return error;
5807968a5ddSDaniel Drake
581212baf03SDmitry Torokhov error = ps2_command(&psmouse->ps2dev, ¶m, PSMOUSE_CMD_SETRATE);
582212baf03SDmitry Torokhov if (error)
583212baf03SDmitry Torokhov return error;
5847968a5ddSDaniel Drake
5857968a5ddSDaniel Drake return 0;
5867968a5ddSDaniel Drake }
5877968a5ddSDaniel Drake
synaptics_set_mode(struct psmouse * psmouse)5887968a5ddSDaniel Drake static int synaptics_set_mode(struct psmouse *psmouse)
5891da177e4SLinus Torvalds {
5901da177e4SLinus Torvalds struct synaptics_data *priv = psmouse->private;
591212baf03SDmitry Torokhov int error;
5921da177e4SLinus Torvalds
5937968a5ddSDaniel Drake priv->mode = 0;
59462d78461SDmitry Torokhov if (priv->absolute_mode)
5957968a5ddSDaniel Drake priv->mode |= SYN_BIT_ABSOLUTE_MODE;
59662d78461SDmitry Torokhov if (priv->disable_gesture)
597e51e3849SDmitry Torokhov priv->mode |= SYN_BIT_DISABLE_GESTURE;
598e51e3849SDmitry Torokhov if (psmouse->rate >= 80)
599e51e3849SDmitry Torokhov priv->mode |= SYN_BIT_HIGH_RATE;
6006c53694fSDmitry Torokhov if (SYN_CAP_EXTENDED(priv->info.capabilities))
60162d78461SDmitry Torokhov priv->mode |= SYN_BIT_W_MODE;
6021da177e4SLinus Torvalds
603212baf03SDmitry Torokhov error = synaptics_mode_cmd(psmouse, priv->mode);
604212baf03SDmitry Torokhov if (error)
605212baf03SDmitry Torokhov return error;
6061da177e4SLinus Torvalds
6073f9db52dSAnthony Martin if (priv->absolute_mode && synaptics_has_agm(priv)) {
608212baf03SDmitry Torokhov error = synaptics_set_advanced_gesture_mode(psmouse);
609212baf03SDmitry Torokhov if (error) {
610212baf03SDmitry Torokhov psmouse_err(psmouse,
611212baf03SDmitry Torokhov "Advanced gesture mode init failed: %d\n",
612212baf03SDmitry Torokhov error);
613212baf03SDmitry Torokhov return error;
614212baf03SDmitry Torokhov }
6157968a5ddSDaniel Drake }
6167968a5ddSDaniel Drake
6171da177e4SLinus Torvalds return 0;
6181da177e4SLinus Torvalds }
6191da177e4SLinus Torvalds
synaptics_set_rate(struct psmouse * psmouse,unsigned int rate)6201da177e4SLinus Torvalds static void synaptics_set_rate(struct psmouse *psmouse, unsigned int rate)
6211da177e4SLinus Torvalds {
6221da177e4SLinus Torvalds struct synaptics_data *priv = psmouse->private;
6231da177e4SLinus Torvalds
6241da177e4SLinus Torvalds if (rate >= 80) {
6251da177e4SLinus Torvalds priv->mode |= SYN_BIT_HIGH_RATE;
6261da177e4SLinus Torvalds psmouse->rate = 80;
6271da177e4SLinus Torvalds } else {
6281da177e4SLinus Torvalds priv->mode &= ~SYN_BIT_HIGH_RATE;
6291da177e4SLinus Torvalds psmouse->rate = 40;
6301da177e4SLinus Torvalds }
6311da177e4SLinus Torvalds
6321da177e4SLinus Torvalds synaptics_mode_cmd(psmouse, priv->mode);
6331da177e4SLinus Torvalds }
6341da177e4SLinus Torvalds
6351da177e4SLinus Torvalds /*****************************************************************************
6361da177e4SLinus Torvalds * Synaptics pass-through PS/2 port support
6371da177e4SLinus Torvalds ****************************************************************************/
synaptics_pt_write(struct serio * serio,u8 c)638f6c4442bSDmitry Torokhov static int synaptics_pt_write(struct serio *serio, u8 c)
6391da177e4SLinus Torvalds {
640100e1695SDmitry Torokhov struct psmouse *parent = psmouse_from_serio(serio->parent);
641212baf03SDmitry Torokhov u8 rate_param = SYN_PS_CLIENT_CMD; /* indicates that we want pass-through port */
642212baf03SDmitry Torokhov int error;
6431da177e4SLinus Torvalds
64408be954bSDmitry Torokhov error = ps2_sliced_command(&parent->ps2dev, c);
645212baf03SDmitry Torokhov if (error)
646212baf03SDmitry Torokhov return error;
647212baf03SDmitry Torokhov
648212baf03SDmitry Torokhov error = ps2_command(&parent->ps2dev, &rate_param, PSMOUSE_CMD_SETRATE);
649212baf03SDmitry Torokhov if (error)
650212baf03SDmitry Torokhov return error;
651212baf03SDmitry Torokhov
6521da177e4SLinus Torvalds return 0;
6531da177e4SLinus Torvalds }
6541da177e4SLinus Torvalds
synaptics_pt_start(struct serio * serio)655a8b3c0f5SDmitry Torokhov static int synaptics_pt_start(struct serio *serio)
656a8b3c0f5SDmitry Torokhov {
657100e1695SDmitry Torokhov struct psmouse *parent = psmouse_from_serio(serio->parent);
658a8b3c0f5SDmitry Torokhov struct synaptics_data *priv = parent->private;
659a8b3c0f5SDmitry Torokhov
6605866065fSDmitry Torokhov guard(serio_pause_rx)(parent->ps2dev.serio);
661a8b3c0f5SDmitry Torokhov priv->pt_port = serio;
662a8b3c0f5SDmitry Torokhov
663a8b3c0f5SDmitry Torokhov return 0;
664a8b3c0f5SDmitry Torokhov }
665a8b3c0f5SDmitry Torokhov
synaptics_pt_stop(struct serio * serio)666a8b3c0f5SDmitry Torokhov static void synaptics_pt_stop(struct serio *serio)
667a8b3c0f5SDmitry Torokhov {
668100e1695SDmitry Torokhov struct psmouse *parent = psmouse_from_serio(serio->parent);
669a8b3c0f5SDmitry Torokhov struct synaptics_data *priv = parent->private;
670a8b3c0f5SDmitry Torokhov
6715866065fSDmitry Torokhov guard(serio_pause_rx)(parent->ps2dev.serio);
672a8b3c0f5SDmitry Torokhov priv->pt_port = NULL;
673a8b3c0f5SDmitry Torokhov }
674a8b3c0f5SDmitry Torokhov
synaptics_pt_open(struct serio * serio)67508bd5b7cSDmitry Torokhov static int synaptics_pt_open(struct serio *serio)
67608bd5b7cSDmitry Torokhov {
67708bd5b7cSDmitry Torokhov struct psmouse *parent = psmouse_from_serio(serio->parent);
67808bd5b7cSDmitry Torokhov struct synaptics_data *priv = parent->private;
67908bd5b7cSDmitry Torokhov
68008bd5b7cSDmitry Torokhov guard(serio_pause_rx)(parent->ps2dev.serio);
68108bd5b7cSDmitry Torokhov priv->pt_port_open = true;
68208bd5b7cSDmitry Torokhov
68308bd5b7cSDmitry Torokhov return 0;
68408bd5b7cSDmitry Torokhov }
68508bd5b7cSDmitry Torokhov
synaptics_pt_close(struct serio * serio)68608bd5b7cSDmitry Torokhov static void synaptics_pt_close(struct serio *serio)
68708bd5b7cSDmitry Torokhov {
68808bd5b7cSDmitry Torokhov struct psmouse *parent = psmouse_from_serio(serio->parent);
68908bd5b7cSDmitry Torokhov struct synaptics_data *priv = parent->private;
69008bd5b7cSDmitry Torokhov
69108bd5b7cSDmitry Torokhov guard(serio_pause_rx)(parent->ps2dev.serio);
69208bd5b7cSDmitry Torokhov priv->pt_port_open = false;
69308bd5b7cSDmitry Torokhov }
69408bd5b7cSDmitry Torokhov
synaptics_is_pt_packet(u8 * buf)695f6c4442bSDmitry Torokhov static int synaptics_is_pt_packet(u8 *buf)
6961da177e4SLinus Torvalds {
6971da177e4SLinus Torvalds return (buf[0] & 0xFC) == 0x84 && (buf[3] & 0xCC) == 0xC4;
6981da177e4SLinus Torvalds }
6991da177e4SLinus Torvalds
synaptics_pass_pt_packet(struct synaptics_data * priv,u8 * packet)70008bd5b7cSDmitry Torokhov static void synaptics_pass_pt_packet(struct synaptics_data *priv, u8 *packet)
7011da177e4SLinus Torvalds {
70208bd5b7cSDmitry Torokhov struct serio *ptport;
70308bd5b7cSDmitry Torokhov
70408bd5b7cSDmitry Torokhov ptport = priv->pt_port;
70508bd5b7cSDmitry Torokhov if (!ptport)
70608bd5b7cSDmitry Torokhov return;
70708bd5b7cSDmitry Torokhov
70808bd5b7cSDmitry Torokhov serio_interrupt(ptport, packet[1], 0);
70908bd5b7cSDmitry Torokhov
71008bd5b7cSDmitry Torokhov if (priv->pt_port_open) {
711100e1695SDmitry Torokhov struct psmouse *child = psmouse_from_serio(ptport);
7121da177e4SLinus Torvalds
71308bd5b7cSDmitry Torokhov if (child->state == PSMOUSE_ACTIVATED) {
7147d12e780SDavid Howells serio_interrupt(ptport, packet[4], 0);
7157d12e780SDavid Howells serio_interrupt(ptport, packet[5], 0);
71633fdfa97SSergey Vlasov if (child->pktsize == 4)
7177d12e780SDavid Howells serio_interrupt(ptport, packet[2], 0);
71808bd5b7cSDmitry Torokhov }
7191da177e4SLinus Torvalds }
720cdd9dc19SBenjamin Tissoires }
7211da177e4SLinus Torvalds
synaptics_pt_activate(struct psmouse * psmouse)7221da177e4SLinus Torvalds static void synaptics_pt_activate(struct psmouse *psmouse)
7231da177e4SLinus Torvalds {
7241da177e4SLinus Torvalds struct synaptics_data *priv = psmouse->private;
725100e1695SDmitry Torokhov struct psmouse *child = psmouse_from_serio(priv->pt_port);
7261da177e4SLinus Torvalds
7271da177e4SLinus Torvalds /* adjust the touchpad to child's choice of protocol */
7281da177e4SLinus Torvalds if (child) {
72933fdfa97SSergey Vlasov if (child->pktsize == 4)
7301da177e4SLinus Torvalds priv->mode |= SYN_BIT_FOUR_BYTE_CLIENT;
7311da177e4SLinus Torvalds else
7321da177e4SLinus Torvalds priv->mode &= ~SYN_BIT_FOUR_BYTE_CLIENT;
7331da177e4SLinus Torvalds
7341da177e4SLinus Torvalds if (synaptics_mode_cmd(psmouse, priv->mode))
735b5d21704SDmitry Torokhov psmouse_warn(psmouse,
736b5d21704SDmitry Torokhov "failed to switch guest protocol\n");
7371da177e4SLinus Torvalds }
7381da177e4SLinus Torvalds }
7391da177e4SLinus Torvalds
synaptics_pt_create(struct psmouse * psmouse)7401da177e4SLinus Torvalds static void synaptics_pt_create(struct psmouse *psmouse)
7411da177e4SLinus Torvalds {
7421da177e4SLinus Torvalds struct serio *serio;
7431da177e4SLinus Torvalds
744dc2f1423SErick Archer serio = kzalloc(sizeof(*serio), GFP_KERNEL);
7451da177e4SLinus Torvalds if (!serio) {
746b5d21704SDmitry Torokhov psmouse_err(psmouse,
747b5d21704SDmitry Torokhov "not enough memory for pass-through port\n");
7481da177e4SLinus Torvalds return;
7491da177e4SLinus Torvalds }
7501da177e4SLinus Torvalds
7511da177e4SLinus Torvalds serio->id.type = SERIO_PS_PSTHRU;
752a9f08ad7SWolfram Sang strscpy(serio->name, "Synaptics pass-through", sizeof(serio->name));
753a9f08ad7SWolfram Sang strscpy(serio->phys, "synaptics-pt/serio0", sizeof(serio->phys));
7541da177e4SLinus Torvalds serio->write = synaptics_pt_write;
755a8b3c0f5SDmitry Torokhov serio->start = synaptics_pt_start;
756a8b3c0f5SDmitry Torokhov serio->stop = synaptics_pt_stop;
75708bd5b7cSDmitry Torokhov serio->open = synaptics_pt_open;
75808bd5b7cSDmitry Torokhov serio->close = synaptics_pt_close;
7591da177e4SLinus Torvalds serio->parent = psmouse->ps2dev.serio;
7601da177e4SLinus Torvalds
7611da177e4SLinus Torvalds psmouse->pt_activate = synaptics_pt_activate;
7621da177e4SLinus Torvalds
763b5d21704SDmitry Torokhov psmouse_info(psmouse, "serio: %s port at %s\n",
764b5d21704SDmitry Torokhov serio->name, psmouse->phys);
7651da177e4SLinus Torvalds serio_register_port(serio);
7661da177e4SLinus Torvalds }
7671da177e4SLinus Torvalds
7681da177e4SLinus Torvalds /*****************************************************************************
7691da177e4SLinus Torvalds * Functions to interpret the absolute mode packets
7701da177e4SLinus Torvalds ****************************************************************************/
7711da177e4SLinus Torvalds
synaptics_parse_agm(const u8 buf[],struct synaptics_data * priv,struct synaptics_hw_state * hw)772f6c4442bSDmitry Torokhov static void synaptics_parse_agm(const u8 buf[],
773a6ca40c1SDaniel Kurtz struct synaptics_data *priv,
774a6ca40c1SDaniel Kurtz struct synaptics_hw_state *hw)
7757afdb842SDaniel Kurtz {
7767afdb842SDaniel Kurtz struct synaptics_hw_state *agm = &priv->agm;
777a6ca40c1SDaniel Kurtz int agm_packet_type;
7787afdb842SDaniel Kurtz
779a6ca40c1SDaniel Kurtz agm_packet_type = (buf[5] & 0x30) >> 4;
780a6ca40c1SDaniel Kurtz switch (agm_packet_type) {
781a6ca40c1SDaniel Kurtz case 1:
782a6ca40c1SDaniel Kurtz /* Gesture packet: (x, y, z) half resolution */
783a6ca40c1SDaniel Kurtz agm->w = hw->w;
7847afdb842SDaniel Kurtz agm->x = (((buf[4] & 0x0f) << 8) | buf[1]) << 1;
7857afdb842SDaniel Kurtz agm->y = (((buf[4] & 0xf0) << 4) | buf[2]) << 1;
7867afdb842SDaniel Kurtz agm->z = ((buf[3] & 0x30) | (buf[5] & 0x0f)) << 1;
787a6ca40c1SDaniel Kurtz break;
788a6ca40c1SDaniel Kurtz
789a6ca40c1SDaniel Kurtz case 2:
790e9e8520fSBenjamin Tissoires /* AGM-CONTACT packet: we are only interested in the count */
791e9e8520fSBenjamin Tissoires priv->agm_count = buf[1];
792a6ca40c1SDaniel Kurtz break;
793a6ca40c1SDaniel Kurtz
794a6ca40c1SDaniel Kurtz default:
795a6ca40c1SDaniel Kurtz break;
796a6ca40c1SDaniel Kurtz }
7977afdb842SDaniel Kurtz }
7987afdb842SDaniel Kurtz
synaptics_parse_ext_buttons(const u8 buf[],struct synaptics_data * priv,struct synaptics_hw_state * hw)799f6c4442bSDmitry Torokhov static void synaptics_parse_ext_buttons(const u8 buf[],
800dc5465dcSDmitry Torokhov struct synaptics_data *priv,
801dc5465dcSDmitry Torokhov struct synaptics_hw_state *hw)
802dc5465dcSDmitry Torokhov {
803dc5465dcSDmitry Torokhov unsigned int ext_bits =
8046c53694fSDmitry Torokhov (SYN_CAP_MULTI_BUTTON_NO(priv->info.ext_cap) + 1) >> 1;
805dc5465dcSDmitry Torokhov unsigned int ext_mask = GENMASK(ext_bits - 1, 0);
806dc5465dcSDmitry Torokhov
807dc5465dcSDmitry Torokhov hw->ext_buttons = buf[4] & ext_mask;
808dc5465dcSDmitry Torokhov hw->ext_buttons |= (buf[5] & ext_mask) << ext_bits;
809dc5465dcSDmitry Torokhov }
810dc5465dcSDmitry Torokhov
synaptics_parse_hw_state(const u8 buf[],struct synaptics_data * priv,struct synaptics_hw_state * hw)811f6c4442bSDmitry Torokhov static int synaptics_parse_hw_state(const u8 buf[],
812fec6e525SHenrik Rydberg struct synaptics_data *priv,
813fec6e525SHenrik Rydberg struct synaptics_hw_state *hw)
8141da177e4SLinus Torvalds {
8151da177e4SLinus Torvalds memset(hw, 0, sizeof(struct synaptics_hw_state));
8161da177e4SLinus Torvalds
8176c53694fSDmitry Torokhov if (SYN_MODEL_NEWABS(priv->info.model_id)) {
8181da177e4SLinus Torvalds hw->w = (((buf[0] & 0x30) >> 2) |
8191da177e4SLinus Torvalds ((buf[0] & 0x04) >> 1) |
8201da177e4SLinus Torvalds ((buf[3] & 0x04) >> 2));
8211da177e4SLinus Torvalds
8223f9db52dSAnthony Martin if (synaptics_has_agm(priv) && hw->w == 2) {
8235715fc76SDmitry Torokhov synaptics_parse_agm(buf, priv, hw);
8245715fc76SDmitry Torokhov return 1;
8255715fc76SDmitry Torokhov }
8265715fc76SDmitry Torokhov
8275715fc76SDmitry Torokhov hw->x = (((buf[3] & 0x10) << 8) |
8285715fc76SDmitry Torokhov ((buf[1] & 0x0f) << 8) |
8295715fc76SDmitry Torokhov buf[4]);
8305715fc76SDmitry Torokhov hw->y = (((buf[3] & 0x20) << 7) |
8315715fc76SDmitry Torokhov ((buf[1] & 0xf0) << 4) |
8325715fc76SDmitry Torokhov buf[5]);
8335715fc76SDmitry Torokhov hw->z = buf[2];
8345715fc76SDmitry Torokhov
8351da177e4SLinus Torvalds hw->left = (buf[0] & 0x01) ? 1 : 0;
8361da177e4SLinus Torvalds hw->right = (buf[0] & 0x02) ? 1 : 0;
8371da177e4SLinus Torvalds
838de4e374bSDmitry Torokhov if (priv->is_forcepad) {
8395715fc76SDmitry Torokhov /*
8405715fc76SDmitry Torokhov * ForcePads, like Clickpads, use middle button
8415715fc76SDmitry Torokhov * bits to report primary button clicks.
8425715fc76SDmitry Torokhov * Unfortunately they report primary button not
8435715fc76SDmitry Torokhov * only when user presses on the pad above certain
8445715fc76SDmitry Torokhov * threshold, but also when there are more than one
8455715fc76SDmitry Torokhov * finger on the touchpad, which interferes with
8465715fc76SDmitry Torokhov * out multi-finger gestures.
8475715fc76SDmitry Torokhov */
8485715fc76SDmitry Torokhov if (hw->z == 0) {
8495715fc76SDmitry Torokhov /* No contacts */
8505715fc76SDmitry Torokhov priv->press = priv->report_press = false;
8515715fc76SDmitry Torokhov } else if (hw->w >= 4 && ((buf[0] ^ buf[3]) & 0x01)) {
8525715fc76SDmitry Torokhov /*
8535715fc76SDmitry Torokhov * Single-finger touch with pressure above
8545715fc76SDmitry Torokhov * the threshold. If pressure stays long
8555715fc76SDmitry Torokhov * enough, we'll start reporting primary
8565715fc76SDmitry Torokhov * button. We rely on the device continuing
8575715fc76SDmitry Torokhov * sending data even if finger does not
8585715fc76SDmitry Torokhov * move.
8595715fc76SDmitry Torokhov */
8605715fc76SDmitry Torokhov if (!priv->press) {
8615715fc76SDmitry Torokhov priv->press_start = jiffies;
8625715fc76SDmitry Torokhov priv->press = true;
8635715fc76SDmitry Torokhov } else if (time_after(jiffies,
8645715fc76SDmitry Torokhov priv->press_start +
8655715fc76SDmitry Torokhov msecs_to_jiffies(50))) {
8665715fc76SDmitry Torokhov priv->report_press = true;
8675715fc76SDmitry Torokhov }
8685715fc76SDmitry Torokhov } else {
8695715fc76SDmitry Torokhov priv->press = false;
8705715fc76SDmitry Torokhov }
8715715fc76SDmitry Torokhov
8725715fc76SDmitry Torokhov hw->left = priv->report_press;
8735715fc76SDmitry Torokhov
8746c53694fSDmitry Torokhov } else if (SYN_CAP_CLICKPAD(priv->info.ext_cap_0c)) {
8755f57d67dSTakashi Iwai /*
8765f57d67dSTakashi Iwai * Clickpad's button is transmitted as middle button,
8775f57d67dSTakashi Iwai * however, since it is primary button, we will report
8785f57d67dSTakashi Iwai * it as BTN_LEFT.
8795f57d67dSTakashi Iwai */
8805f57d67dSTakashi Iwai hw->left = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0;
8815f57d67dSTakashi Iwai
8826c53694fSDmitry Torokhov } else if (SYN_CAP_MIDDLE_BUTTON(priv->info.capabilities)) {
8831da177e4SLinus Torvalds hw->middle = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0;
8841da177e4SLinus Torvalds if (hw->w == 2)
885f6c4442bSDmitry Torokhov hw->scroll = (s8)buf[1];
8861da177e4SLinus Torvalds }
8871da177e4SLinus Torvalds
8886c53694fSDmitry Torokhov if (SYN_CAP_FOUR_BUTTON(priv->info.capabilities)) {
8891da177e4SLinus Torvalds hw->up = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0;
8901da177e4SLinus Torvalds hw->down = ((buf[0] ^ buf[3]) & 0x02) ? 1 : 0;
8911da177e4SLinus Torvalds }
8921da177e4SLinus Torvalds
8936c53694fSDmitry Torokhov if (SYN_CAP_MULTI_BUTTON_NO(priv->info.ext_cap) > 0 &&
8941da177e4SLinus Torvalds ((buf[0] ^ buf[3]) & 0x02)) {
895dc5465dcSDmitry Torokhov synaptics_parse_ext_buttons(buf, priv, hw);
8961da177e4SLinus Torvalds }
8971da177e4SLinus Torvalds } else {
8981da177e4SLinus Torvalds hw->x = (((buf[1] & 0x1f) << 8) | buf[2]);
8991da177e4SLinus Torvalds hw->y = (((buf[4] & 0x1f) << 8) | buf[5]);
9001da177e4SLinus Torvalds
9011da177e4SLinus Torvalds hw->z = (((buf[0] & 0x30) << 2) | (buf[3] & 0x3F));
9021da177e4SLinus Torvalds hw->w = (((buf[1] & 0x80) >> 4) | ((buf[0] & 0x04) >> 1));
9031da177e4SLinus Torvalds
9041da177e4SLinus Torvalds hw->left = (buf[0] & 0x01) ? 1 : 0;
9051da177e4SLinus Torvalds hw->right = (buf[0] & 0x02) ? 1 : 0;
9061da177e4SLinus Torvalds }
907fec6e525SHenrik Rydberg
908824efd37SSeth Forshee /*
909824efd37SSeth Forshee * Convert wrap-around values to negative. (X|Y)_MAX_POSITIVE
910824efd37SSeth Forshee * is used by some firmware to indicate a finger at the edge of
911824efd37SSeth Forshee * the touchpad whose precise position cannot be determined, so
912824efd37SSeth Forshee * convert these values to the maximum axis value.
913824efd37SSeth Forshee */
914c0394506SSeth Forshee if (hw->x > X_MAX_POSITIVE)
915c0394506SSeth Forshee hw->x -= 1 << ABS_POS_BITS;
916824efd37SSeth Forshee else if (hw->x == X_MAX_POSITIVE)
917824efd37SSeth Forshee hw->x = XMAX;
918824efd37SSeth Forshee
919c0394506SSeth Forshee if (hw->y > Y_MAX_POSITIVE)
920c0394506SSeth Forshee hw->y -= 1 << ABS_POS_BITS;
921824efd37SSeth Forshee else if (hw->y == Y_MAX_POSITIVE)
922824efd37SSeth Forshee hw->y = YMAX;
923c0394506SSeth Forshee
924fec6e525SHenrik Rydberg return 0;
925fec6e525SHenrik Rydberg }
926fec6e525SHenrik Rydberg
synaptics_report_semi_mt_slot(struct input_dev * dev,int slot,bool active,int x,int y)927bea9f0ffSDaniel Kurtz static void synaptics_report_semi_mt_slot(struct input_dev *dev, int slot,
928bea9f0ffSDaniel Kurtz bool active, int x, int y)
929fec6e525SHenrik Rydberg {
930fec6e525SHenrik Rydberg input_mt_slot(dev, slot);
931fec6e525SHenrik Rydberg input_mt_report_slot_state(dev, MT_TOOL_FINGER, active);
932fec6e525SHenrik Rydberg if (active) {
933fec6e525SHenrik Rydberg input_report_abs(dev, ABS_MT_POSITION_X, x);
9346de58dd6SDaniel Kurtz input_report_abs(dev, ABS_MT_POSITION_Y, synaptics_invert_y(y));
935fec6e525SHenrik Rydberg }
936fec6e525SHenrik Rydberg }
937fec6e525SHenrik Rydberg
synaptics_report_semi_mt_data(struct input_dev * dev,const struct synaptics_hw_state * a,const struct synaptics_hw_state * b,int num_fingers)938fec6e525SHenrik Rydberg static void synaptics_report_semi_mt_data(struct input_dev *dev,
939fec6e525SHenrik Rydberg const struct synaptics_hw_state *a,
940fec6e525SHenrik Rydberg const struct synaptics_hw_state *b,
941fec6e525SHenrik Rydberg int num_fingers)
942fec6e525SHenrik Rydberg {
943fec6e525SHenrik Rydberg if (num_fingers >= 2) {
944bea9f0ffSDaniel Kurtz synaptics_report_semi_mt_slot(dev, 0, true, min(a->x, b->x),
945bea9f0ffSDaniel Kurtz min(a->y, b->y));
946bea9f0ffSDaniel Kurtz synaptics_report_semi_mt_slot(dev, 1, true, max(a->x, b->x),
947bea9f0ffSDaniel Kurtz max(a->y, b->y));
948fec6e525SHenrik Rydberg } else if (num_fingers == 1) {
949bea9f0ffSDaniel Kurtz synaptics_report_semi_mt_slot(dev, 0, true, a->x, a->y);
950bea9f0ffSDaniel Kurtz synaptics_report_semi_mt_slot(dev, 1, false, 0, 0);
951fec6e525SHenrik Rydberg } else {
952bea9f0ffSDaniel Kurtz synaptics_report_semi_mt_slot(dev, 0, false, 0, 0);
953bea9f0ffSDaniel Kurtz synaptics_report_semi_mt_slot(dev, 1, false, 0, 0);
954fec6e525SHenrik Rydberg }
9551da177e4SLinus Torvalds }
9561da177e4SLinus Torvalds
synaptics_report_ext_buttons(struct psmouse * psmouse,const struct synaptics_hw_state * hw)957ebc80840SBenjamin Tissoires static void synaptics_report_ext_buttons(struct psmouse *psmouse,
9583cdfee9eSDaniel Kurtz const struct synaptics_hw_state *hw)
9593cdfee9eSDaniel Kurtz {
9603cdfee9eSDaniel Kurtz struct input_dev *dev = psmouse->dev;
9613cdfee9eSDaniel Kurtz struct synaptics_data *priv = psmouse->private;
9626c53694fSDmitry Torokhov int ext_bits = (SYN_CAP_MULTI_BUTTON_NO(priv->info.ext_cap) + 1) >> 1;
9633cdfee9eSDaniel Kurtz int i;
9643cdfee9eSDaniel Kurtz
9656c53694fSDmitry Torokhov if (!SYN_CAP_MULTI_BUTTON_NO(priv->info.ext_cap))
966ebc80840SBenjamin Tissoires return;
967ebc80840SBenjamin Tissoires
96882be788cSBenjamin Tissoires /* Bug in FW 8.1 & 8.2, buttons are reported only when ExtBit is 1 */
9696c53694fSDmitry Torokhov if ((SYN_ID_FULL(priv->info.identity) == 0x801 ||
9706c53694fSDmitry Torokhov SYN_ID_FULL(priv->info.identity) == 0x802) &&
971ebc80840SBenjamin Tissoires !((psmouse->packet[0] ^ psmouse->packet[3]) & 0x02))
972ebc80840SBenjamin Tissoires return;
973ebc80840SBenjamin Tissoires
9746c53694fSDmitry Torokhov if (!SYN_CAP_EXT_BUTTONS_STICK(priv->info.ext_cap_10)) {
975ebc80840SBenjamin Tissoires for (i = 0; i < ext_bits; i++) {
976ebc80840SBenjamin Tissoires input_report_key(dev, BTN_0 + 2 * i,
977991d29feSDmitry Torokhov hw->ext_buttons & BIT(i));
978ebc80840SBenjamin Tissoires input_report_key(dev, BTN_1 + 2 * i,
979991d29feSDmitry Torokhov hw->ext_buttons & BIT(i + ext_bits));
980ebc80840SBenjamin Tissoires }
981cdd9dc19SBenjamin Tissoires return;
982cdd9dc19SBenjamin Tissoires }
983cdd9dc19SBenjamin Tissoires
984cdd9dc19SBenjamin Tissoires /*
985cdd9dc19SBenjamin Tissoires * This generation of touchpads has the trackstick buttons
986cdd9dc19SBenjamin Tissoires * physically wired to the touchpad. Re-route them through
987cdd9dc19SBenjamin Tissoires * the pass-through interface.
988cdd9dc19SBenjamin Tissoires */
989bf23cfc3SDmitry Torokhov if (priv->pt_port) {
990bf23cfc3SDmitry Torokhov u8 pt_buttons;
991cdd9dc19SBenjamin Tissoires
992cdd9dc19SBenjamin Tissoires /* The trackstick expects at most 3 buttons */
993996b9eedSDmitry Torokhov pt_buttons = SYN_EXT_BUTTON_STICK_L(hw->ext_buttons) |
994996b9eedSDmitry Torokhov SYN_EXT_BUTTON_STICK_R(hw->ext_buttons) << 1 |
995996b9eedSDmitry Torokhov SYN_EXT_BUTTON_STICK_M(hw->ext_buttons) << 2;
996cdd9dc19SBenjamin Tissoires
997bf23cfc3SDmitry Torokhov serio_interrupt(priv->pt_port,
998bf23cfc3SDmitry Torokhov PSMOUSE_OOB_EXTRA_BTNS, SERIO_OOB_DATA);
999bf23cfc3SDmitry Torokhov serio_interrupt(priv->pt_port, pt_buttons, SERIO_OOB_DATA);
1000bf23cfc3SDmitry Torokhov }
1001ebc80840SBenjamin Tissoires }
1002ebc80840SBenjamin Tissoires
synaptics_report_buttons(struct psmouse * psmouse,const struct synaptics_hw_state * hw)10033cdfee9eSDaniel Kurtz static void synaptics_report_buttons(struct psmouse *psmouse,
10043cdfee9eSDaniel Kurtz const struct synaptics_hw_state *hw)
10053cdfee9eSDaniel Kurtz {
10063cdfee9eSDaniel Kurtz struct input_dev *dev = psmouse->dev;
10073cdfee9eSDaniel Kurtz struct synaptics_data *priv = psmouse->private;
10083cdfee9eSDaniel Kurtz
10093cdfee9eSDaniel Kurtz input_report_key(dev, BTN_LEFT, hw->left);
10103cdfee9eSDaniel Kurtz input_report_key(dev, BTN_RIGHT, hw->right);
10113cdfee9eSDaniel Kurtz
10126c53694fSDmitry Torokhov if (SYN_CAP_MIDDLE_BUTTON(priv->info.capabilities))
10133cdfee9eSDaniel Kurtz input_report_key(dev, BTN_MIDDLE, hw->middle);
10143cdfee9eSDaniel Kurtz
10156c53694fSDmitry Torokhov if (SYN_CAP_FOUR_BUTTON(priv->info.capabilities)) {
10163cdfee9eSDaniel Kurtz input_report_key(dev, BTN_FORWARD, hw->up);
10173cdfee9eSDaniel Kurtz input_report_key(dev, BTN_BACK, hw->down);
10183cdfee9eSDaniel Kurtz }
10193cdfee9eSDaniel Kurtz
1020ebc80840SBenjamin Tissoires synaptics_report_ext_buttons(psmouse, hw);
10213cdfee9eSDaniel Kurtz }
10223cdfee9eSDaniel Kurtz
synaptics_report_mt_data(struct psmouse * psmouse,const struct synaptics_hw_state * sgm,int num_fingers)10233cdfee9eSDaniel Kurtz static void synaptics_report_mt_data(struct psmouse *psmouse,
1024e9e8520fSBenjamin Tissoires const struct synaptics_hw_state *sgm,
1025e9e8520fSBenjamin Tissoires int num_fingers)
10263cdfee9eSDaniel Kurtz {
10273cdfee9eSDaniel Kurtz struct input_dev *dev = psmouse->dev;
10283cdfee9eSDaniel Kurtz struct synaptics_data *priv = psmouse->private;
1029e9e8520fSBenjamin Tissoires const struct synaptics_hw_state *hw[2] = { sgm, &priv->agm };
1030e9e8520fSBenjamin Tissoires struct input_mt_pos pos[2];
1031e9e8520fSBenjamin Tissoires int slot[2], nsemi, i;
10323cdfee9eSDaniel Kurtz
1033e9e8520fSBenjamin Tissoires nsemi = clamp_val(num_fingers, 0, 2);
10344dc772d2SDaniel Kurtz
1035e9e8520fSBenjamin Tissoires for (i = 0; i < nsemi; i++) {
1036e9e8520fSBenjamin Tissoires pos[i].x = hw[i]->x;
1037e9e8520fSBenjamin Tissoires pos[i].y = synaptics_invert_y(hw[i]->y);
10383cdfee9eSDaniel Kurtz }
10393cdfee9eSDaniel Kurtz
10406c53694fSDmitry Torokhov input_mt_assign_slots(dev, slot, pos, nsemi, DMAX * priv->info.x_res);
1041e9e8520fSBenjamin Tissoires
1042e9e8520fSBenjamin Tissoires for (i = 0; i < nsemi; i++) {
1043e9e8520fSBenjamin Tissoires input_mt_slot(dev, slot[i]);
1044e9e8520fSBenjamin Tissoires input_mt_report_slot_state(dev, MT_TOOL_FINGER, true);
1045e9e8520fSBenjamin Tissoires input_report_abs(dev, ABS_MT_POSITION_X, pos[i].x);
1046e9e8520fSBenjamin Tissoires input_report_abs(dev, ABS_MT_POSITION_Y, pos[i].y);
1047e9e8520fSBenjamin Tissoires input_report_abs(dev, ABS_MT_PRESSURE, hw[i]->z);
1048e9e8520fSBenjamin Tissoires }
1049e9e8520fSBenjamin Tissoires
1050e9e8520fSBenjamin Tissoires input_mt_drop_unused(dev);
1051e9e8520fSBenjamin Tissoires
10523cdfee9eSDaniel Kurtz /* Don't use active slot count to generate BTN_TOOL events. */
10533cdfee9eSDaniel Kurtz input_mt_report_pointer_emulation(dev, false);
10543cdfee9eSDaniel Kurtz
10553cdfee9eSDaniel Kurtz /* Send the number of fingers reported by touchpad itself. */
1056e9e8520fSBenjamin Tissoires input_mt_report_finger_count(dev, num_fingers);
10573cdfee9eSDaniel Kurtz
10583cdfee9eSDaniel Kurtz synaptics_report_buttons(psmouse, sgm);
10593cdfee9eSDaniel Kurtz
10603cdfee9eSDaniel Kurtz input_sync(dev);
10613cdfee9eSDaniel Kurtz }
10623cdfee9eSDaniel Kurtz
synaptics_image_sensor_process(struct psmouse * psmouse,struct synaptics_hw_state * sgm)10633cdfee9eSDaniel Kurtz static void synaptics_image_sensor_process(struct psmouse *psmouse,
10643cdfee9eSDaniel Kurtz struct synaptics_hw_state *sgm)
10653cdfee9eSDaniel Kurtz {
10664dc772d2SDaniel Kurtz struct synaptics_data *priv = psmouse->private;
1067e9e8520fSBenjamin Tissoires int num_fingers;
10684dc772d2SDaniel Kurtz
10694dc772d2SDaniel Kurtz /*
10704dc772d2SDaniel Kurtz * Update mt_state using the new finger count and current mt_state.
10714dc772d2SDaniel Kurtz */
10723cdfee9eSDaniel Kurtz if (sgm->z == 0)
1073e9e8520fSBenjamin Tissoires num_fingers = 0;
10743cdfee9eSDaniel Kurtz else if (sgm->w >= 4)
1075e9e8520fSBenjamin Tissoires num_fingers = 1;
10763cdfee9eSDaniel Kurtz else if (sgm->w == 0)
1077e9e8520fSBenjamin Tissoires num_fingers = 2;
1078e9e8520fSBenjamin Tissoires else if (sgm->w == 1)
1079e9e8520fSBenjamin Tissoires num_fingers = priv->agm_count ? priv->agm_count : 3;
10806b4b49feSDaniel Kurtz else
1081e9e8520fSBenjamin Tissoires num_fingers = 4;
10823cdfee9eSDaniel Kurtz
10833cdfee9eSDaniel Kurtz /* Send resulting input events to user space */
1084e9e8520fSBenjamin Tissoires synaptics_report_mt_data(psmouse, sgm, num_fingers);
10853cdfee9eSDaniel Kurtz }
10863cdfee9eSDaniel Kurtz
synaptics_has_multifinger(struct synaptics_data * priv)10873f9db52dSAnthony Martin static bool synaptics_has_multifinger(struct synaptics_data *priv)
10883f9db52dSAnthony Martin {
10893f9db52dSAnthony Martin if (SYN_CAP_MULTIFINGER(priv->info.capabilities))
10903f9db52dSAnthony Martin return true;
10913f9db52dSAnthony Martin
10923f9db52dSAnthony Martin /* Advanced gesture mode also sends multi finger data */
10933f9db52dSAnthony Martin return synaptics_has_agm(priv);
10943f9db52dSAnthony Martin }
10953f9db52dSAnthony Martin
10961da177e4SLinus Torvalds /*
10971da177e4SLinus Torvalds * called for each full received packet from the touchpad
10981da177e4SLinus Torvalds */
synaptics_process_packet(struct psmouse * psmouse)10991da177e4SLinus Torvalds static void synaptics_process_packet(struct psmouse *psmouse)
11001da177e4SLinus Torvalds {
11012e5b636bSDmitry Torokhov struct input_dev *dev = psmouse->dev;
11021da177e4SLinus Torvalds struct synaptics_data *priv = psmouse->private;
11036c53694fSDmitry Torokhov struct synaptics_device_info *info = &priv->info;
11041da177e4SLinus Torvalds struct synaptics_hw_state hw;
11051da177e4SLinus Torvalds int num_fingers;
11061da177e4SLinus Torvalds int finger_width;
11071da177e4SLinus Torvalds
1108fec6e525SHenrik Rydberg if (synaptics_parse_hw_state(psmouse->packet, priv, &hw))
1109fec6e525SHenrik Rydberg return;
11101da177e4SLinus Torvalds
11116c53694fSDmitry Torokhov if (SYN_CAP_IMAGE_SENSOR(info->ext_cap_0c)) {
11123cdfee9eSDaniel Kurtz synaptics_image_sensor_process(psmouse, &hw);
11133cdfee9eSDaniel Kurtz return;
11143cdfee9eSDaniel Kurtz }
11153cdfee9eSDaniel Kurtz
11161da177e4SLinus Torvalds if (hw.scroll) {
11171da177e4SLinus Torvalds priv->scroll += hw.scroll;
11181da177e4SLinus Torvalds
11191da177e4SLinus Torvalds while (priv->scroll >= 4) {
11201da177e4SLinus Torvalds input_report_key(dev, BTN_BACK, !hw.down);
11211da177e4SLinus Torvalds input_sync(dev);
11221da177e4SLinus Torvalds input_report_key(dev, BTN_BACK, hw.down);
11231da177e4SLinus Torvalds input_sync(dev);
11241da177e4SLinus Torvalds priv->scroll -= 4;
11251da177e4SLinus Torvalds }
11261da177e4SLinus Torvalds while (priv->scroll <= -4) {
11271da177e4SLinus Torvalds input_report_key(dev, BTN_FORWARD, !hw.up);
11281da177e4SLinus Torvalds input_sync(dev);
11291da177e4SLinus Torvalds input_report_key(dev, BTN_FORWARD, hw.up);
11301da177e4SLinus Torvalds input_sync(dev);
11311da177e4SLinus Torvalds priv->scroll += 4;
11321da177e4SLinus Torvalds }
11331da177e4SLinus Torvalds return;
11341da177e4SLinus Torvalds }
11351da177e4SLinus Torvalds
11364f56ce92SHenrik Rydberg if (hw.z > 0 && hw.x > 1) {
11371da177e4SLinus Torvalds num_fingers = 1;
11381da177e4SLinus Torvalds finger_width = 5;
11396c53694fSDmitry Torokhov if (SYN_CAP_EXTENDED(info->capabilities)) {
11401da177e4SLinus Torvalds switch (hw.w) {
11411da177e4SLinus Torvalds case 0 ... 1:
11423f9db52dSAnthony Martin if (synaptics_has_multifinger(priv))
11431da177e4SLinus Torvalds num_fingers = hw.w + 2;
11441da177e4SLinus Torvalds break;
11451da177e4SLinus Torvalds case 2:
1146a1ba9c29SLee Jones /*
1147a1ba9c29SLee Jones * SYN_MODEL_PEN(info->model_id): even if
1148a1ba9c29SLee Jones * the device supports pen, we treat it as
1149a1ba9c29SLee Jones * a single finger.
1150a1ba9c29SLee Jones */
11511da177e4SLinus Torvalds break;
11521da177e4SLinus Torvalds case 4 ... 15:
11536c53694fSDmitry Torokhov if (SYN_CAP_PALMDETECT(info->capabilities))
11541da177e4SLinus Torvalds finger_width = hw.w;
11551da177e4SLinus Torvalds break;
11561da177e4SLinus Torvalds }
11571da177e4SLinus Torvalds }
11581da177e4SLinus Torvalds } else {
11591da177e4SLinus Torvalds num_fingers = 0;
11601da177e4SLinus Torvalds finger_width = 0;
11611da177e4SLinus Torvalds }
11621da177e4SLinus Torvalds
1163e08d9afaSHenrik Rydberg if (cr48_profile_sensor) {
1164aa104b1aSBenjamin Tissoires synaptics_report_mt_data(psmouse, &hw, num_fingers);
1165e08d9afaSHenrik Rydberg return;
1166e08d9afaSHenrik Rydberg }
1167e08d9afaSHenrik Rydberg
11686c53694fSDmitry Torokhov if (SYN_CAP_ADV_GESTURE(info->ext_cap_0c))
11697afdb842SDaniel Kurtz synaptics_report_semi_mt_data(dev, &hw, &priv->agm,
11707afdb842SDaniel Kurtz num_fingers);
1171fec6e525SHenrik Rydberg
11721da177e4SLinus Torvalds /* Post events
11731da177e4SLinus Torvalds * BTN_TOUCH has to be first as mousedev relies on it when doing
11741da177e4SLinus Torvalds * absolute -> relative conversion
11751da177e4SLinus Torvalds */
11761da177e4SLinus Torvalds if (hw.z > 30) input_report_key(dev, BTN_TOUCH, 1);
11771da177e4SLinus Torvalds if (hw.z < 25) input_report_key(dev, BTN_TOUCH, 0);
11781da177e4SLinus Torvalds
11794f56ce92SHenrik Rydberg if (num_fingers > 0) {
11801da177e4SLinus Torvalds input_report_abs(dev, ABS_X, hw.x);
11816de58dd6SDaniel Kurtz input_report_abs(dev, ABS_Y, synaptics_invert_y(hw.y));
11821da177e4SLinus Torvalds }
11831da177e4SLinus Torvalds input_report_abs(dev, ABS_PRESSURE, hw.z);
11841da177e4SLinus Torvalds
11856c53694fSDmitry Torokhov if (SYN_CAP_PALMDETECT(info->capabilities))
11861da177e4SLinus Torvalds input_report_abs(dev, ABS_TOOL_WIDTH, finger_width);
11872a8e7710SChris Bagwell
11881da177e4SLinus Torvalds input_report_key(dev, BTN_TOOL_FINGER, num_fingers == 1);
11893f9db52dSAnthony Martin if (synaptics_has_multifinger(priv)) {
1190e42b6646SPeter Hutterer input_report_key(dev, BTN_TOOL_DOUBLETAP, num_fingers == 2);
1191e42b6646SPeter Hutterer input_report_key(dev, BTN_TOOL_TRIPLETAP, num_fingers == 3);
1192e42b6646SPeter Hutterer }
1193e42b6646SPeter Hutterer
11943cdfee9eSDaniel Kurtz synaptics_report_buttons(psmouse, &hw);
11951da177e4SLinus Torvalds
11961da177e4SLinus Torvalds input_sync(dev);
11971da177e4SLinus Torvalds }
11981da177e4SLinus Torvalds
synaptics_validate_byte(struct psmouse * psmouse,int idx,enum synaptics_pkt_type pkt_type)1199f6c4442bSDmitry Torokhov static bool synaptics_validate_byte(struct psmouse *psmouse,
1200f6c4442bSDmitry Torokhov int idx, enum synaptics_pkt_type pkt_type)
12011da177e4SLinus Torvalds {
1202f6c4442bSDmitry Torokhov static const u8 newabs_mask[] = { 0xC8, 0x00, 0x00, 0xC8, 0x00 };
1203f6c4442bSDmitry Torokhov static const u8 newabs_rel_mask[] = { 0xC0, 0x00, 0x00, 0xC0, 0x00 };
1204f6c4442bSDmitry Torokhov static const u8 newabs_rslt[] = { 0x80, 0x00, 0x00, 0xC0, 0x00 };
1205f6c4442bSDmitry Torokhov static const u8 oldabs_mask[] = { 0xC0, 0x60, 0x00, 0xC0, 0x60 };
1206f6c4442bSDmitry Torokhov static const u8 oldabs_rslt[] = { 0xC0, 0x00, 0x00, 0x80, 0x00 };
1207f6c4442bSDmitry Torokhov const u8 *packet = psmouse->packet;
12081da177e4SLinus Torvalds
12091da177e4SLinus Torvalds if (idx < 0 || idx > 4)
1210f6c4442bSDmitry Torokhov return false;
12111da177e4SLinus Torvalds
12121da177e4SLinus Torvalds switch (pkt_type) {
1213a62f0d27SDmitry Torokhov
12141da177e4SLinus Torvalds case SYN_NEWABS:
12151da177e4SLinus Torvalds case SYN_NEWABS_RELAXED:
12161da177e4SLinus Torvalds return (packet[idx] & newabs_rel_mask[idx]) == newabs_rslt[idx];
12171da177e4SLinus Torvalds
12181da177e4SLinus Torvalds case SYN_NEWABS_STRICT:
12191da177e4SLinus Torvalds return (packet[idx] & newabs_mask[idx]) == newabs_rslt[idx];
12201da177e4SLinus Torvalds
12211da177e4SLinus Torvalds case SYN_OLDABS:
12221da177e4SLinus Torvalds return (packet[idx] & oldabs_mask[idx]) == oldabs_rslt[idx];
12231da177e4SLinus Torvalds
12241da177e4SLinus Torvalds default:
1225b5d21704SDmitry Torokhov psmouse_err(psmouse, "unknown packet type %d\n", pkt_type);
1226f6c4442bSDmitry Torokhov return false;
12271da177e4SLinus Torvalds }
12281da177e4SLinus Torvalds }
12291da177e4SLinus Torvalds
1230f6c4442bSDmitry Torokhov static enum synaptics_pkt_type
synaptics_detect_pkt_type(struct psmouse * psmouse)1231f6c4442bSDmitry Torokhov synaptics_detect_pkt_type(struct psmouse *psmouse)
12321da177e4SLinus Torvalds {
12331da177e4SLinus Torvalds int i;
12341da177e4SLinus Torvalds
1235f6c4442bSDmitry Torokhov for (i = 0; i < 5; i++) {
1236b5d21704SDmitry Torokhov if (!synaptics_validate_byte(psmouse, i, SYN_NEWABS_STRICT)) {
1237b5d21704SDmitry Torokhov psmouse_info(psmouse, "using relaxed packet validation\n");
12381da177e4SLinus Torvalds return SYN_NEWABS_RELAXED;
12391da177e4SLinus Torvalds }
1240f6c4442bSDmitry Torokhov }
12411da177e4SLinus Torvalds
12421da177e4SLinus Torvalds return SYN_NEWABS_STRICT;
12431da177e4SLinus Torvalds }
12441da177e4SLinus Torvalds
synaptics_process_byte(struct psmouse * psmouse)12457d12e780SDavid Howells static psmouse_ret_t synaptics_process_byte(struct psmouse *psmouse)
12461da177e4SLinus Torvalds {
12471da177e4SLinus Torvalds struct synaptics_data *priv = psmouse->private;
12481da177e4SLinus Torvalds
12491da177e4SLinus Torvalds if (psmouse->pktcnt >= 6) { /* Full packet received */
12501da177e4SLinus Torvalds if (unlikely(priv->pkt_type == SYN_NEWABS))
12511da177e4SLinus Torvalds priv->pkt_type = synaptics_detect_pkt_type(psmouse);
12521da177e4SLinus Torvalds
12536c53694fSDmitry Torokhov if (SYN_CAP_PASS_THROUGH(priv->info.capabilities) &&
1254a8b3c0f5SDmitry Torokhov synaptics_is_pt_packet(psmouse->packet)) {
125508bd5b7cSDmitry Torokhov synaptics_pass_pt_packet(priv, psmouse->packet);
125608bd5b7cSDmitry Torokhov } else {
12571da177e4SLinus Torvalds synaptics_process_packet(psmouse);
125808bd5b7cSDmitry Torokhov }
12591da177e4SLinus Torvalds
12601da177e4SLinus Torvalds return PSMOUSE_FULL_PACKET;
12611da177e4SLinus Torvalds }
12621da177e4SLinus Torvalds
1263b5d21704SDmitry Torokhov return synaptics_validate_byte(psmouse, psmouse->pktcnt - 1, priv->pkt_type) ?
12641da177e4SLinus Torvalds PSMOUSE_GOOD_DATA : PSMOUSE_BAD_DATA;
12651da177e4SLinus Torvalds }
12661da177e4SLinus Torvalds
12671da177e4SLinus Torvalds /*****************************************************************************
12681da177e4SLinus Torvalds * Driver initialization/cleanup functions
12691da177e4SLinus Torvalds ****************************************************************************/
set_abs_position_params(struct input_dev * dev,struct synaptics_device_info * info,int x_code,int y_code)127085615476SDaniel Kurtz static void set_abs_position_params(struct input_dev *dev,
12716c53694fSDmitry Torokhov struct synaptics_device_info *info,
12726c53694fSDmitry Torokhov int x_code, int y_code)
127385615476SDaniel Kurtz {
12746c53694fSDmitry Torokhov int x_min = info->x_min ?: XMIN_NOMINAL;
12756c53694fSDmitry Torokhov int x_max = info->x_max ?: XMAX_NOMINAL;
12766c53694fSDmitry Torokhov int y_min = info->y_min ?: YMIN_NOMINAL;
12776c53694fSDmitry Torokhov int y_max = info->y_max ?: YMAX_NOMINAL;
12786c53694fSDmitry Torokhov int fuzz = SYN_CAP_REDUCED_FILTERING(info->ext_cap_0c) ?
127985615476SDaniel Kurtz SYN_REDUCED_FILTER_FUZZ : 0;
128085615476SDaniel Kurtz
128185615476SDaniel Kurtz input_set_abs_params(dev, x_code, x_min, x_max, fuzz, 0);
128285615476SDaniel Kurtz input_set_abs_params(dev, y_code, y_min, y_max, fuzz, 0);
12836c53694fSDmitry Torokhov input_abs_set_res(dev, x_code, info->x_res);
12846c53694fSDmitry Torokhov input_abs_set_res(dev, y_code, info->y_res);
128585615476SDaniel Kurtz }
128685615476SDaniel Kurtz
set_input_params(struct psmouse * psmouse,struct synaptics_data * priv)1287cdc2466dSDmitry Torokhov static int set_input_params(struct psmouse *psmouse,
128843e19888SHans de Goede struct synaptics_data *priv)
12891da177e4SLinus Torvalds {
129043e19888SHans de Goede struct input_dev *dev = psmouse->dev;
12916c53694fSDmitry Torokhov struct synaptics_device_info *info = &priv->info;
12921da177e4SLinus Torvalds int i;
1293cdc2466dSDmitry Torokhov int error;
12941da177e4SLinus Torvalds
129529aa6194SDmitry Torokhov /* Reset default psmouse capabilities */
129629aa6194SDmitry Torokhov __clear_bit(EV_REL, dev->evbit);
129729aa6194SDmitry Torokhov bitmap_zero(dev->relbit, REL_CNT);
129829aa6194SDmitry Torokhov bitmap_zero(dev->keybit, KEY_CNT);
129929aa6194SDmitry Torokhov
13007968a5ddSDaniel Drake /* Things that apply to both modes */
1301c14890a8SHenrik Rydberg __set_bit(INPUT_PROP_POINTER, dev->propbit);
1302c14890a8SHenrik Rydberg
130329aa6194SDmitry Torokhov input_set_capability(dev, EV_KEY, BTN_LEFT);
130429aa6194SDmitry Torokhov
130529aa6194SDmitry Torokhov /* Clickpads report only left button */
130629aa6194SDmitry Torokhov if (!SYN_CAP_CLICKPAD(info->ext_cap_0c)) {
130729aa6194SDmitry Torokhov input_set_capability(dev, EV_KEY, BTN_RIGHT);
13086c53694fSDmitry Torokhov if (SYN_CAP_MIDDLE_BUTTON(info->capabilities))
130929aa6194SDmitry Torokhov input_set_capability(dev, EV_KEY, BTN_MIDDLE);
131029aa6194SDmitry Torokhov }
13117968a5ddSDaniel Drake
13127968a5ddSDaniel Drake if (!priv->absolute_mode) {
13137968a5ddSDaniel Drake /* Relative mode */
131429aa6194SDmitry Torokhov input_set_capability(dev, EV_REL, REL_X);
131529aa6194SDmitry Torokhov input_set_capability(dev, EV_REL, REL_Y);
1316cdc2466dSDmitry Torokhov return 0;
13177968a5ddSDaniel Drake }
13187968a5ddSDaniel Drake
13197968a5ddSDaniel Drake /* Absolute mode */
13206c53694fSDmitry Torokhov set_abs_position_params(dev, &priv->info, ABS_X, ABS_Y);
13211da177e4SLinus Torvalds input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0);
13222a8e7710SChris Bagwell
1323e08d9afaSHenrik Rydberg if (cr48_profile_sensor)
1324e08d9afaSHenrik Rydberg input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 255, 0, 0);
1325e08d9afaSHenrik Rydberg
13266c53694fSDmitry Torokhov if (SYN_CAP_IMAGE_SENSOR(info->ext_cap_0c)) {
13276c53694fSDmitry Torokhov set_abs_position_params(dev, info,
13286c53694fSDmitry Torokhov ABS_MT_POSITION_X, ABS_MT_POSITION_Y);
13293cdfee9eSDaniel Kurtz /* Image sensors can report per-contact pressure */
13303cdfee9eSDaniel Kurtz input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 255, 0, 0);
1331cdc2466dSDmitry Torokhov
1332cdc2466dSDmitry Torokhov error = input_mt_init_slots(dev, 2,
1333cdc2466dSDmitry Torokhov INPUT_MT_POINTER | INPUT_MT_TRACK);
1334cdc2466dSDmitry Torokhov if (error)
1335cdc2466dSDmitry Torokhov return error;
13366b4b49feSDaniel Kurtz
13376b4b49feSDaniel Kurtz /* Image sensors can signal 4 and 5 finger clicks */
133829aa6194SDmitry Torokhov input_set_capability(dev, EV_KEY, BTN_TOOL_QUADTAP);
133929aa6194SDmitry Torokhov input_set_capability(dev, EV_KEY, BTN_TOOL_QUINTTAP);
13406c53694fSDmitry Torokhov } else if (SYN_CAP_ADV_GESTURE(info->ext_cap_0c)) {
13416c53694fSDmitry Torokhov set_abs_position_params(dev, info,
13426c53694fSDmitry Torokhov ABS_MT_POSITION_X, ABS_MT_POSITION_Y);
1343e08d9afaSHenrik Rydberg /*
1344e08d9afaSHenrik Rydberg * Profile sensor in CR-48 tracks contacts reasonably well,
1345e08d9afaSHenrik Rydberg * other non-image sensors with AGM use semi-mt.
1346e08d9afaSHenrik Rydberg */
1347cdc2466dSDmitry Torokhov error = input_mt_init_slots(dev, 2,
1348e08d9afaSHenrik Rydberg INPUT_MT_POINTER |
1349e08d9afaSHenrik Rydberg (cr48_profile_sensor ?
1350cdc2466dSDmitry Torokhov INPUT_MT_TRACK :
1351cdc2466dSDmitry Torokhov INPUT_MT_SEMI_MT));
1352cdc2466dSDmitry Torokhov if (error)
1353cdc2466dSDmitry Torokhov return error;
135419eb4ed1SPeter Hutterer
135519eb4ed1SPeter Hutterer /*
135619eb4ed1SPeter Hutterer * For semi-mt devices we send ABS_X/Y ourselves instead of
135719eb4ed1SPeter Hutterer * input_mt_report_pointer_emulation. But
135819eb4ed1SPeter Hutterer * input_mt_init_slots() resets the fuzz to 0, leading to a
135919eb4ed1SPeter Hutterer * filtered ABS_MT_POSITION_X but an unfiltered ABS_X
136019eb4ed1SPeter Hutterer * position. Let's re-initialize ABS_X/Y here.
136119eb4ed1SPeter Hutterer */
136219eb4ed1SPeter Hutterer if (!cr48_profile_sensor)
136319eb4ed1SPeter Hutterer set_abs_position_params(dev, &priv->info, ABS_X, ABS_Y);
1364fec6e525SHenrik Rydberg }
1365fec6e525SHenrik Rydberg
13666c53694fSDmitry Torokhov if (SYN_CAP_PALMDETECT(info->capabilities))
136758fb0218SChris Bagwell input_set_abs_params(dev, ABS_TOOL_WIDTH, 0, 15, 0, 0);
13681da177e4SLinus Torvalds
136929aa6194SDmitry Torokhov input_set_capability(dev, EV_KEY, BTN_TOUCH);
137029aa6194SDmitry Torokhov input_set_capability(dev, EV_KEY, BTN_TOOL_FINGER);
13711da177e4SLinus Torvalds
13723f9db52dSAnthony Martin if (synaptics_has_multifinger(priv)) {
137329aa6194SDmitry Torokhov input_set_capability(dev, EV_KEY, BTN_TOOL_DOUBLETAP);
137429aa6194SDmitry Torokhov input_set_capability(dev, EV_KEY, BTN_TOOL_TRIPLETAP);
1375e42b6646SPeter Hutterer }
1376e42b6646SPeter Hutterer
13776c53694fSDmitry Torokhov if (SYN_CAP_FOUR_BUTTON(info->capabilities) ||
13786c53694fSDmitry Torokhov SYN_CAP_MIDDLE_BUTTON(info->capabilities)) {
137929aa6194SDmitry Torokhov input_set_capability(dev, EV_KEY, BTN_FORWARD);
138029aa6194SDmitry Torokhov input_set_capability(dev, EV_KEY, BTN_BACK);
13811da177e4SLinus Torvalds }
13821da177e4SLinus Torvalds
13836c53694fSDmitry Torokhov if (!SYN_CAP_EXT_BUTTONS_STICK(info->ext_cap_10))
13846c53694fSDmitry Torokhov for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(info->ext_cap); i++)
138529aa6194SDmitry Torokhov input_set_capability(dev, EV_KEY, BTN_0 + i);
1386ec20a022STero Saarni
13876c53694fSDmitry Torokhov if (SYN_CAP_CLICKPAD(info->ext_cap_0c)) {
1388c14890a8SHenrik Rydberg __set_bit(INPUT_PROP_BUTTONPAD, dev->propbit);
13893adde1f5SBenjamin Tissoires if (psmouse_matches_pnp_id(psmouse, topbuttonpad_pnp_ids) &&
13906c53694fSDmitry Torokhov !SYN_CAP_EXT_BUTTONS_STICK(info->ext_cap_10))
1391e2f61102SHans de Goede __set_bit(INPUT_PROP_TOPBUTTONPAD, dev->propbit);
13925f57d67dSTakashi Iwai }
1393cdc2466dSDmitry Torokhov
1394cdc2466dSDmitry Torokhov return 0;
13951da177e4SLinus Torvalds }
13961da177e4SLinus Torvalds
synaptics_show_disable_gesture(struct psmouse * psmouse,void * data,char * buf)13977968a5ddSDaniel Drake static ssize_t synaptics_show_disable_gesture(struct psmouse *psmouse,
13987968a5ddSDaniel Drake void *data, char *buf)
13997968a5ddSDaniel Drake {
14007968a5ddSDaniel Drake struct synaptics_data *priv = psmouse->private;
14017968a5ddSDaniel Drake
14027968a5ddSDaniel Drake return sprintf(buf, "%c\n", priv->disable_gesture ? '1' : '0');
14037968a5ddSDaniel Drake }
14047968a5ddSDaniel Drake
synaptics_set_disable_gesture(struct psmouse * psmouse,void * data,const char * buf,size_t len)14057968a5ddSDaniel Drake static ssize_t synaptics_set_disable_gesture(struct psmouse *psmouse,
14067968a5ddSDaniel Drake void *data, const char *buf,
14077968a5ddSDaniel Drake size_t len)
14087968a5ddSDaniel Drake {
14097968a5ddSDaniel Drake struct synaptics_data *priv = psmouse->private;
14107968a5ddSDaniel Drake unsigned int value;
14117968a5ddSDaniel Drake int err;
14127968a5ddSDaniel Drake
14137968a5ddSDaniel Drake err = kstrtouint(buf, 10, &value);
14147968a5ddSDaniel Drake if (err)
14157968a5ddSDaniel Drake return err;
14167968a5ddSDaniel Drake
14177968a5ddSDaniel Drake if (value > 1)
14187968a5ddSDaniel Drake return -EINVAL;
14197968a5ddSDaniel Drake
14207968a5ddSDaniel Drake if (value == priv->disable_gesture)
14217968a5ddSDaniel Drake return len;
14227968a5ddSDaniel Drake
14237968a5ddSDaniel Drake priv->disable_gesture = value;
14247968a5ddSDaniel Drake if (value)
14257968a5ddSDaniel Drake priv->mode |= SYN_BIT_DISABLE_GESTURE;
14267968a5ddSDaniel Drake else
14277968a5ddSDaniel Drake priv->mode &= ~SYN_BIT_DISABLE_GESTURE;
14287968a5ddSDaniel Drake
14297968a5ddSDaniel Drake if (synaptics_mode_cmd(psmouse, priv->mode))
14307968a5ddSDaniel Drake return -EIO;
14317968a5ddSDaniel Drake
14327968a5ddSDaniel Drake return len;
14337968a5ddSDaniel Drake }
14347968a5ddSDaniel Drake
14357968a5ddSDaniel Drake PSMOUSE_DEFINE_ATTR(disable_gesture, S_IWUSR | S_IRUGO, NULL,
14367968a5ddSDaniel Drake synaptics_show_disable_gesture,
14377968a5ddSDaniel Drake synaptics_set_disable_gesture);
14387968a5ddSDaniel Drake
synaptics_disconnect(struct psmouse * psmouse)14391da177e4SLinus Torvalds static void synaptics_disconnect(struct psmouse *psmouse)
14401da177e4SLinus Torvalds {
14417968a5ddSDaniel Drake struct synaptics_data *priv = psmouse->private;
14427968a5ddSDaniel Drake
1443e839ffabSBenjamin Tissoires /*
1444e839ffabSBenjamin Tissoires * We might have left a breadcrumb when trying to
1445e839ffabSBenjamin Tissoires * set up SMbus companion.
1446e839ffabSBenjamin Tissoires */
1447e839ffabSBenjamin Tissoires psmouse_smbus_cleanup(psmouse);
1448e839ffabSBenjamin Tissoires
14496c53694fSDmitry Torokhov if (!priv->absolute_mode &&
14506c53694fSDmitry Torokhov SYN_ID_DISGEST_SUPPORTED(priv->info.identity))
14517968a5ddSDaniel Drake device_remove_file(&psmouse->ps2dev.serio->dev,
14527968a5ddSDaniel Drake &psmouse_attr_disable_gesture.dattr);
14537968a5ddSDaniel Drake
14541da177e4SLinus Torvalds synaptics_reset(psmouse);
14557968a5ddSDaniel Drake kfree(priv);
14561da177e4SLinus Torvalds psmouse->private = NULL;
14571da177e4SLinus Torvalds }
14581da177e4SLinus Torvalds
synaptics_reconnect(struct psmouse * psmouse)14591da177e4SLinus Torvalds static int synaptics_reconnect(struct psmouse *psmouse)
14601da177e4SLinus Torvalds {
14611da177e4SLinus Torvalds struct synaptics_data *priv = psmouse->private;
14626c53694fSDmitry Torokhov struct synaptics_device_info info;
1463f6c4442bSDmitry Torokhov u8 param[2];
1464c63fe0a4SAlexandre Peixoto Ferreira int retry = 0;
1465c63fe0a4SAlexandre Peixoto Ferreira int error;
14661da177e4SLinus Torvalds
1467c63fe0a4SAlexandre Peixoto Ferreira do {
14684d368456SAndy Whitcroft psmouse_reset(psmouse);
14698521478fSDmitry Torokhov if (retry) {
14708521478fSDmitry Torokhov /*
14718521478fSDmitry Torokhov * On some boxes, right after resuming, the touchpad
14728521478fSDmitry Torokhov * needs some time to finish initializing (I assume
14738521478fSDmitry Torokhov * it needs time to calibrate) and start responding
14748521478fSDmitry Torokhov * to Synaptics-specific queries, so let's wait a
14758521478fSDmitry Torokhov * bit.
14768521478fSDmitry Torokhov */
14778521478fSDmitry Torokhov ssleep(1);
14788521478fSDmitry Torokhov }
1479eeb06558SEric Miao ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETID);
1480c63fe0a4SAlexandre Peixoto Ferreira error = synaptics_detect(psmouse, 0);
1481c63fe0a4SAlexandre Peixoto Ferreira } while (error && ++retry < 3);
14824d368456SAndy Whitcroft
1483c63fe0a4SAlexandre Peixoto Ferreira if (error)
1484212baf03SDmitry Torokhov return error;
14851da177e4SLinus Torvalds
1486c63fe0a4SAlexandre Peixoto Ferreira if (retry > 1)
1487b5d21704SDmitry Torokhov psmouse_dbg(psmouse, "reconnected after %d tries\n", retry);
1488c63fe0a4SAlexandre Peixoto Ferreira
1489212baf03SDmitry Torokhov error = synaptics_query_hardware(psmouse, &info);
1490212baf03SDmitry Torokhov if (error) {
1491b5d21704SDmitry Torokhov psmouse_err(psmouse, "Unable to query device.\n");
1492212baf03SDmitry Torokhov return error;
14931da177e4SLinus Torvalds }
14941da177e4SLinus Torvalds
1495212baf03SDmitry Torokhov error = synaptics_set_mode(psmouse);
1496212baf03SDmitry Torokhov if (error) {
1497b5d21704SDmitry Torokhov psmouse_err(psmouse, "Unable to initialize device.\n");
1498212baf03SDmitry Torokhov return error;
14991da177e4SLinus Torvalds }
15001da177e4SLinus Torvalds
15016c53694fSDmitry Torokhov if (info.identity != priv->info.identity ||
15026c53694fSDmitry Torokhov info.model_id != priv->info.model_id ||
15036c53694fSDmitry Torokhov info.capabilities != priv->info.capabilities ||
15046c53694fSDmitry Torokhov info.ext_cap != priv->info.ext_cap) {
1505b5d21704SDmitry Torokhov psmouse_err(psmouse,
15066c53694fSDmitry Torokhov "hardware appears to be different: id(%u-%u), model(%u-%u), caps(%x-%x), ext(%x-%x).\n",
15076c53694fSDmitry Torokhov priv->info.identity, info.identity,
15086c53694fSDmitry Torokhov priv->info.model_id, info.model_id,
15096c53694fSDmitry Torokhov priv->info.capabilities, info.capabilities,
15106c53694fSDmitry Torokhov priv->info.ext_cap, info.ext_cap);
1511212baf03SDmitry Torokhov return -ENXIO;
1512baddf589SAlexandre Peixoto Ferreira }
1513baddf589SAlexandre Peixoto Ferreira
15141da177e4SLinus Torvalds return 0;
15151da177e4SLinus Torvalds }
15161da177e4SLinus Torvalds
15177705d548SDmitry Torokhov static bool impaired_toshiba_kbc;
15187705d548SDmitry Torokhov
1519c963156cSSachin Kamat static const struct dmi_system_id toshiba_dmi_table[] __initconst = {
15207705d548SDmitry Torokhov #if defined(CONFIG_DMI) && defined(CONFIG_X86)
15211da177e4SLinus Torvalds {
15229961e259SDmitry Torokhov /* Toshiba Satellite */
15231da177e4SLinus Torvalds .matches = {
15241da177e4SLinus Torvalds DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
15251da177e4SLinus Torvalds DMI_MATCH(DMI_PRODUCT_NAME, "Satellite"),
15261da177e4SLinus Torvalds },
15271da177e4SLinus Torvalds },
15289ba5eaafSSimon Horman {
15299961e259SDmitry Torokhov /* Toshiba Dynabook */
15309ba5eaafSSimon Horman .matches = {
15319ba5eaafSSimon Horman DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
15329ba5eaafSSimon Horman DMI_MATCH(DMI_PRODUCT_NAME, "dynabook"),
153353a2670cSRichard Thrippleton },
153453a2670cSRichard Thrippleton },
153553a2670cSRichard Thrippleton {
15369961e259SDmitry Torokhov /* Toshiba Portege M300 */
153753a2670cSRichard Thrippleton .matches = {
153853a2670cSRichard Thrippleton DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
153953a2670cSRichard Thrippleton DMI_MATCH(DMI_PRODUCT_NAME, "PORTEGE M300"),
15409ba5eaafSSimon Horman },
15415f5eeff4SDmitry Torokhov
15425f5eeff4SDmitry Torokhov },
15435f5eeff4SDmitry Torokhov {
15449961e259SDmitry Torokhov /* Toshiba Portege M300 */
15455f5eeff4SDmitry Torokhov .matches = {
15465f5eeff4SDmitry Torokhov DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
15475f5eeff4SDmitry Torokhov DMI_MATCH(DMI_PRODUCT_NAME, "Portable PC"),
15485f5eeff4SDmitry Torokhov DMI_MATCH(DMI_PRODUCT_VERSION, "Version 1.0"),
15495f5eeff4SDmitry Torokhov },
15505f5eeff4SDmitry Torokhov
15519ba5eaafSSimon Horman },
15521da177e4SLinus Torvalds #endif
155370874867SJan Beulich { }
15547705d548SDmitry Torokhov };
15557705d548SDmitry Torokhov
1556ef8313bbSAndres Salomon static bool broken_olpc_ec;
1557ef8313bbSAndres Salomon
1558c963156cSSachin Kamat static const struct dmi_system_id olpc_dmi_table[] __initconst = {
1559ef8313bbSAndres Salomon #if defined(CONFIG_DMI) && defined(CONFIG_OLPC)
1560ef8313bbSAndres Salomon {
1561ef8313bbSAndres Salomon /* OLPC XO-1 or XO-1.5 */
1562ef8313bbSAndres Salomon .matches = {
1563ef8313bbSAndres Salomon DMI_MATCH(DMI_SYS_VENDOR, "OLPC"),
1564ef8313bbSAndres Salomon DMI_MATCH(DMI_PRODUCT_NAME, "XO"),
1565ef8313bbSAndres Salomon },
1566ef8313bbSAndres Salomon },
1567ef8313bbSAndres Salomon #endif
156870874867SJan Beulich { }
1569ef8313bbSAndres Salomon };
1570ef8313bbSAndres Salomon
1571e08d9afaSHenrik Rydberg static const struct dmi_system_id __initconst cr48_dmi_table[] = {
1572e08d9afaSHenrik Rydberg #if defined(CONFIG_DMI) && defined(CONFIG_X86)
1573e08d9afaSHenrik Rydberg {
1574e08d9afaSHenrik Rydberg /* Cr-48 Chromebook (Codename Mario) */
1575e08d9afaSHenrik Rydberg .matches = {
1576e08d9afaSHenrik Rydberg DMI_MATCH(DMI_SYS_VENDOR, "IEC"),
1577e08d9afaSHenrik Rydberg DMI_MATCH(DMI_PRODUCT_NAME, "Mario"),
1578e08d9afaSHenrik Rydberg },
1579e08d9afaSHenrik Rydberg },
1580e08d9afaSHenrik Rydberg #endif
1581e08d9afaSHenrik Rydberg { }
1582e08d9afaSHenrik Rydberg };
1583e08d9afaSHenrik Rydberg
synaptics_module_init(void)15847705d548SDmitry Torokhov void __init synaptics_module_init(void)
15857705d548SDmitry Torokhov {
15867705d548SDmitry Torokhov impaired_toshiba_kbc = dmi_check_system(toshiba_dmi_table);
1587ef8313bbSAndres Salomon broken_olpc_ec = dmi_check_system(olpc_dmi_table);
1588e08d9afaSHenrik Rydberg cr48_profile_sensor = dmi_check_system(cr48_dmi_table);
15897705d548SDmitry Torokhov }
15901da177e4SLinus Torvalds
synaptics_init_ps2(struct psmouse * psmouse,struct synaptics_device_info * info,bool absolute_mode)1591e839ffabSBenjamin Tissoires static int synaptics_init_ps2(struct psmouse *psmouse,
1592e839ffabSBenjamin Tissoires struct synaptics_device_info *info,
1593e839ffabSBenjamin Tissoires bool absolute_mode)
15941da177e4SLinus Torvalds {
15951da177e4SLinus Torvalds struct synaptics_data *priv;
1596e839ffabSBenjamin Tissoires int err;
15971da177e4SLinus Torvalds
1598e839ffabSBenjamin Tissoires synaptics_apply_quirks(psmouse, info);
1599ef8313bbSAndres Salomon
1600dc2f1423SErick Archer psmouse->private = priv = kzalloc(sizeof(*priv), GFP_KERNEL);
16011da177e4SLinus Torvalds if (!priv)
16026792cbbbSDavidlohr Bueso return -ENOMEM;
16031da177e4SLinus Torvalds
1604e839ffabSBenjamin Tissoires priv->info = *info;
16057968a5ddSDaniel Drake priv->absolute_mode = absolute_mode;
16066c53694fSDmitry Torokhov if (SYN_ID_DISGEST_SUPPORTED(info->identity))
16077968a5ddSDaniel Drake priv->disable_gesture = true;
16081da177e4SLinus Torvalds
1609de4e374bSDmitry Torokhov /*
1610de4e374bSDmitry Torokhov * Unfortunately ForcePad capability is not exported over PS/2,
1611de4e374bSDmitry Torokhov * so we have to resort to checking PNP IDs.
1612de4e374bSDmitry Torokhov */
1613de4e374bSDmitry Torokhov priv->is_forcepad = psmouse_matches_pnp_id(psmouse, forcepad_pnp_ids);
1614de4e374bSDmitry Torokhov
1615e839ffabSBenjamin Tissoires err = synaptics_set_mode(psmouse);
1616e839ffabSBenjamin Tissoires if (err) {
16177968a5ddSDaniel Drake psmouse_err(psmouse, "Unable to initialize device.\n");
1618fec6e525SHenrik Rydberg goto init_fail;
1619fec6e525SHenrik Rydberg }
1620fec6e525SHenrik Rydberg
16216c53694fSDmitry Torokhov priv->pkt_type = SYN_MODEL_NEWABS(info->model_id) ?
16226c53694fSDmitry Torokhov SYN_NEWABS : SYN_OLDABS;
16231da177e4SLinus Torvalds
1624b5d21704SDmitry Torokhov psmouse_info(psmouse,
1625991d29feSDmitry Torokhov "Touchpad model: %lu, fw: %lu.%lu, id: %#x, caps: %#x/%#x/%#x/%#x, board id: %u, fw id: %u\n",
16266c53694fSDmitry Torokhov SYN_ID_MODEL(info->identity),
16276c53694fSDmitry Torokhov SYN_ID_MAJOR(info->identity), SYN_ID_MINOR(info->identity),
16286c53694fSDmitry Torokhov info->model_id,
16296c53694fSDmitry Torokhov info->capabilities, info->ext_cap, info->ext_cap_0c,
16306c53694fSDmitry Torokhov info->ext_cap_10, info->board_id, info->firmware_id);
1631409b7506SDmitry Torokhov
1632cdc2466dSDmitry Torokhov err = set_input_params(psmouse, priv);
1633cdc2466dSDmitry Torokhov if (err) {
1634cdc2466dSDmitry Torokhov psmouse_err(psmouse,
1635cdc2466dSDmitry Torokhov "failed to set up capabilities: %d\n", err);
1636cdc2466dSDmitry Torokhov goto init_fail;
1637cdc2466dSDmitry Torokhov }
16381da177e4SLinus Torvalds
1639887cc127SDmitry Torokhov /*
1640887cc127SDmitry Torokhov * Encode touchpad model so that it can be used to set
1641887cc127SDmitry Torokhov * input device->id.version and be visible to userspace.
1642887cc127SDmitry Torokhov * Because version is __u16 we have to drop something.
1643887cc127SDmitry Torokhov * Hardware info bits seem to be good candidates as they
1644887cc127SDmitry Torokhov * are documented to be for Synaptics corp. internal use.
1645887cc127SDmitry Torokhov */
16466c53694fSDmitry Torokhov psmouse->model = ((info->model_id & 0x00ff0000) >> 8) |
16476c53694fSDmitry Torokhov (info->model_id & 0x000000ff);
1648887cc127SDmitry Torokhov
16497968a5ddSDaniel Drake if (absolute_mode) {
16501da177e4SLinus Torvalds psmouse->protocol_handler = synaptics_process_byte;
16517968a5ddSDaniel Drake psmouse->pktsize = 6;
16527968a5ddSDaniel Drake } else {
16537968a5ddSDaniel Drake /* Relative mode follows standard PS/2 mouse protocol */
16547968a5ddSDaniel Drake psmouse->protocol_handler = psmouse_process_byte;
16557968a5ddSDaniel Drake psmouse->pktsize = 3;
16567968a5ddSDaniel Drake }
16577968a5ddSDaniel Drake
16581da177e4SLinus Torvalds psmouse->set_rate = synaptics_set_rate;
16591da177e4SLinus Torvalds psmouse->disconnect = synaptics_disconnect;
16601da177e4SLinus Torvalds psmouse->reconnect = synaptics_reconnect;
1661e2cb5cc8SJeffery Miller psmouse->fast_reconnect = NULL;
1662a1cec061SDmitry Torokhov psmouse->cleanup = synaptics_reset;
1663f0d5c6f4SDmitry Torokhov /* Synaptics can usually stay in sync without extra help */
1664f0d5c6f4SDmitry Torokhov psmouse->resync_time = 0;
16651da177e4SLinus Torvalds
16666c53694fSDmitry Torokhov if (SYN_CAP_PASS_THROUGH(info->capabilities))
16671da177e4SLinus Torvalds synaptics_pt_create(psmouse);
16681da177e4SLinus Torvalds
16691da177e4SLinus Torvalds /*
16701da177e4SLinus Torvalds * Toshiba's KBC seems to have trouble handling data from
16717ee99161SAndres Salomon * Synaptics at full rate. Switch to a lower rate (roughly
16727ee99161SAndres Salomon * the same rate as a standard PS/2 mouse).
16731da177e4SLinus Torvalds */
16747705d548SDmitry Torokhov if (psmouse->rate >= 80 && impaired_toshiba_kbc) {
1675b5d21704SDmitry Torokhov psmouse_info(psmouse,
1676b5d21704SDmitry Torokhov "Toshiba %s detected, limiting rate to 40pps.\n",
16779ba5eaafSSimon Horman dmi_get_system_info(DMI_PRODUCT_NAME));
16781da177e4SLinus Torvalds psmouse->rate = 40;
16791da177e4SLinus Torvalds }
16801da177e4SLinus Torvalds
16816c53694fSDmitry Torokhov if (!priv->absolute_mode && SYN_ID_DISGEST_SUPPORTED(info->identity)) {
16827968a5ddSDaniel Drake err = device_create_file(&psmouse->ps2dev.serio->dev,
16837968a5ddSDaniel Drake &psmouse_attr_disable_gesture.dattr);
16847968a5ddSDaniel Drake if (err) {
16857968a5ddSDaniel Drake psmouse_err(psmouse,
16867968a5ddSDaniel Drake "Failed to create disable_gesture attribute (%d)",
16877968a5ddSDaniel Drake err);
16887968a5ddSDaniel Drake goto init_fail;
16897968a5ddSDaniel Drake }
16907968a5ddSDaniel Drake }
16917968a5ddSDaniel Drake
16921da177e4SLinus Torvalds return 0;
16931da177e4SLinus Torvalds
16941da177e4SLinus Torvalds init_fail:
16951da177e4SLinus Torvalds kfree(priv);
16967968a5ddSDaniel Drake return err;
16977968a5ddSDaniel Drake }
16987968a5ddSDaniel Drake
__synaptics_init(struct psmouse * psmouse,bool absolute_mode)1699e839ffabSBenjamin Tissoires static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode)
1700e839ffabSBenjamin Tissoires {
1701e839ffabSBenjamin Tissoires struct synaptics_device_info info;
1702e839ffabSBenjamin Tissoires int error;
1703e839ffabSBenjamin Tissoires
1704e839ffabSBenjamin Tissoires psmouse_reset(psmouse);
1705e839ffabSBenjamin Tissoires
1706e839ffabSBenjamin Tissoires error = synaptics_query_hardware(psmouse, &info);
1707e839ffabSBenjamin Tissoires if (error) {
1708e839ffabSBenjamin Tissoires psmouse_err(psmouse, "Unable to query device: %d\n", error);
1709e839ffabSBenjamin Tissoires return error;
1710e839ffabSBenjamin Tissoires }
1711e839ffabSBenjamin Tissoires
1712e839ffabSBenjamin Tissoires return synaptics_init_ps2(psmouse, &info, absolute_mode);
1713e839ffabSBenjamin Tissoires }
1714e839ffabSBenjamin Tissoires
synaptics_init_absolute(struct psmouse * psmouse)1715e839ffabSBenjamin Tissoires int synaptics_init_absolute(struct psmouse *psmouse)
17167968a5ddSDaniel Drake {
17177968a5ddSDaniel Drake return __synaptics_init(psmouse, true);
17187968a5ddSDaniel Drake }
17197968a5ddSDaniel Drake
synaptics_init_relative(struct psmouse * psmouse)17207968a5ddSDaniel Drake int synaptics_init_relative(struct psmouse *psmouse)
17217968a5ddSDaniel Drake {
17227968a5ddSDaniel Drake return __synaptics_init(psmouse, false);
17231da177e4SLinus Torvalds }
17241da177e4SLinus Torvalds
synaptics_setup_ps2(struct psmouse * psmouse,struct synaptics_device_info * info)1725e839ffabSBenjamin Tissoires static int synaptics_setup_ps2(struct psmouse *psmouse,
1726e839ffabSBenjamin Tissoires struct synaptics_device_info *info)
1727e839ffabSBenjamin Tissoires {
1728e839ffabSBenjamin Tissoires bool absolute_mode = true;
1729e839ffabSBenjamin Tissoires int error;
1730e839ffabSBenjamin Tissoires
1731e839ffabSBenjamin Tissoires /*
1732e839ffabSBenjamin Tissoires * The OLPC XO has issues with Synaptics' absolute mode; the constant
1733e839ffabSBenjamin Tissoires * packet spew overloads the EC such that key presses on the keyboard
1734e839ffabSBenjamin Tissoires * are missed. Given that, don't even attempt to use Absolute mode.
1735e839ffabSBenjamin Tissoires * Relative mode seems to work just fine.
1736e839ffabSBenjamin Tissoires */
1737e839ffabSBenjamin Tissoires if (broken_olpc_ec) {
1738e839ffabSBenjamin Tissoires psmouse_info(psmouse,
1739e839ffabSBenjamin Tissoires "OLPC XO detected, forcing relative protocol.\n");
1740e839ffabSBenjamin Tissoires absolute_mode = false;
1741e839ffabSBenjamin Tissoires }
1742e839ffabSBenjamin Tissoires
1743e839ffabSBenjamin Tissoires error = synaptics_init_ps2(psmouse, info, absolute_mode);
1744e839ffabSBenjamin Tissoires if (error)
1745e839ffabSBenjamin Tissoires return error;
1746e839ffabSBenjamin Tissoires
1747e839ffabSBenjamin Tissoires return absolute_mode ? PSMOUSE_SYNAPTICS : PSMOUSE_SYNAPTICS_RELATIVE;
1748e839ffabSBenjamin Tissoires }
1749e839ffabSBenjamin Tissoires
175055e3d922SAndres Salomon #else /* CONFIG_MOUSE_PS2_SYNAPTICS */
175155e3d922SAndres Salomon
synaptics_module_init(void)17527705d548SDmitry Torokhov void __init synaptics_module_init(void)
17537705d548SDmitry Torokhov {
17547705d548SDmitry Torokhov }
17557705d548SDmitry Torokhov
1756e839ffabSBenjamin Tissoires static int __maybe_unused
synaptics_setup_ps2(struct psmouse * psmouse,struct synaptics_device_info * info)1757e839ffabSBenjamin Tissoires synaptics_setup_ps2(struct psmouse *psmouse,
1758e839ffabSBenjamin Tissoires struct synaptics_device_info *info)
175955e3d922SAndres Salomon {
176055e3d922SAndres Salomon return -ENOSYS;
176155e3d922SAndres Salomon }
176255e3d922SAndres Salomon
176355e3d922SAndres Salomon #endif /* CONFIG_MOUSE_PS2_SYNAPTICS */
1764e839ffabSBenjamin Tissoires
1765e839ffabSBenjamin Tissoires #ifdef CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS
1766e839ffabSBenjamin Tissoires
1767e839ffabSBenjamin Tissoires /*
1768e839ffabSBenjamin Tissoires * The newest Synaptics device can use a secondary bus (called InterTouch) which
1769e839ffabSBenjamin Tissoires * provides a better bandwidth and allow a better control of the touchpads.
1770e839ffabSBenjamin Tissoires * This is used to decide if we need to use this bus or not.
1771e839ffabSBenjamin Tissoires */
1772e839ffabSBenjamin Tissoires enum {
1773e839ffabSBenjamin Tissoires SYNAPTICS_INTERTOUCH_NOT_SET = -1,
1774e839ffabSBenjamin Tissoires SYNAPTICS_INTERTOUCH_OFF,
1775e839ffabSBenjamin Tissoires SYNAPTICS_INTERTOUCH_ON,
1776e839ffabSBenjamin Tissoires };
1777e839ffabSBenjamin Tissoires
1778f4947d79SBenjamin Tissoires static int synaptics_intertouch = IS_ENABLED(CONFIG_RMI4_SMB) ?
1779f4947d79SBenjamin Tissoires SYNAPTICS_INTERTOUCH_NOT_SET : SYNAPTICS_INTERTOUCH_OFF;
1780e839ffabSBenjamin Tissoires module_param_named(synaptics_intertouch, synaptics_intertouch, int, 0644);
1781e839ffabSBenjamin Tissoires MODULE_PARM_DESC(synaptics_intertouch, "Use a secondary bus for the Synaptics device.");
1782e839ffabSBenjamin Tissoires
synaptics_create_intertouch(struct psmouse * psmouse,struct synaptics_device_info * info,bool leave_breadcrumbs)1783e839ffabSBenjamin Tissoires static int synaptics_create_intertouch(struct psmouse *psmouse,
1784e839ffabSBenjamin Tissoires struct synaptics_device_info *info,
1785e839ffabSBenjamin Tissoires bool leave_breadcrumbs)
1786e839ffabSBenjamin Tissoires {
1787e839ffabSBenjamin Tissoires bool topbuttonpad =
1788e839ffabSBenjamin Tissoires psmouse_matches_pnp_id(psmouse, topbuttonpad_pnp_ids) &&
1789e839ffabSBenjamin Tissoires !SYN_CAP_EXT_BUTTONS_STICK(info->ext_cap_10);
1790e839ffabSBenjamin Tissoires const struct rmi_device_platform_data pdata = {
17915030b2feSDmitry Torokhov .reset_delay_ms = 30,
1792e839ffabSBenjamin Tissoires .sensor_pdata = {
1793e839ffabSBenjamin Tissoires .sensor_type = rmi_sensor_touchpad,
1794e839ffabSBenjamin Tissoires .axis_align.flip_y = true,
17952b30297dSAndrew Duggan .kernel_tracking = false,
1796e839ffabSBenjamin Tissoires .topbuttonpad = topbuttonpad,
1797e839ffabSBenjamin Tissoires },
1798261bfb33SVincent Huang .gpio_data = {
1799e839ffabSBenjamin Tissoires .buttonpad = SYN_CAP_CLICKPAD(info->ext_cap_0c),
1800e839ffabSBenjamin Tissoires .trackstick_buttons =
1801e839ffabSBenjamin Tissoires !!SYN_CAP_EXT_BUTTONS_STICK(info->ext_cap_10),
1802e839ffabSBenjamin Tissoires },
1803e839ffabSBenjamin Tissoires };
1804e839ffabSBenjamin Tissoires const struct i2c_board_info intertouch_board = {
1805e839ffabSBenjamin Tissoires I2C_BOARD_INFO("rmi4_smbus", 0x2c),
1806e839ffabSBenjamin Tissoires .flags = I2C_CLIENT_HOST_NOTIFY,
1807e839ffabSBenjamin Tissoires };
1808e839ffabSBenjamin Tissoires
1809e839ffabSBenjamin Tissoires return psmouse_smbus_init(psmouse, &intertouch_board,
1810bf232e46SBenjamin Tissoires &pdata, sizeof(pdata), true,
1811e839ffabSBenjamin Tissoires leave_breadcrumbs);
1812e839ffabSBenjamin Tissoires }
1813e839ffabSBenjamin Tissoires
181458e5183aSLee Jones /*
1815e839ffabSBenjamin Tissoires * synaptics_setup_intertouch - called once the PS/2 devices are enumerated
1816e839ffabSBenjamin Tissoires * and decides to instantiate a SMBus InterTouch device.
1817e839ffabSBenjamin Tissoires */
synaptics_setup_intertouch(struct psmouse * psmouse,struct synaptics_device_info * info,bool leave_breadcrumbs)1818e839ffabSBenjamin Tissoires static int synaptics_setup_intertouch(struct psmouse *psmouse,
1819e839ffabSBenjamin Tissoires struct synaptics_device_info *info,
1820e839ffabSBenjamin Tissoires bool leave_breadcrumbs)
1821e839ffabSBenjamin Tissoires {
1822e839ffabSBenjamin Tissoires int error;
1823e839ffabSBenjamin Tissoires
1824e839ffabSBenjamin Tissoires if (synaptics_intertouch == SYNAPTICS_INTERTOUCH_OFF)
1825e839ffabSBenjamin Tissoires return -ENXIO;
1826e839ffabSBenjamin Tissoires
1827e839ffabSBenjamin Tissoires if (synaptics_intertouch == SYNAPTICS_INTERTOUCH_NOT_SET) {
1828e839ffabSBenjamin Tissoires if (!psmouse_matches_pnp_id(psmouse, topbuttonpad_pnp_ids) &&
18292fef826eSBenjamin Tissoires !psmouse_matches_pnp_id(psmouse, smbus_pnp_ids)) {
18302fef826eSBenjamin Tissoires
18312fef826eSBenjamin Tissoires if (!psmouse_matches_pnp_id(psmouse, forcepad_pnp_ids))
18322fef826eSBenjamin Tissoires psmouse_info(psmouse,
18332fef826eSBenjamin Tissoires "Your touchpad (%s) says it can support a different bus. "
18342fef826eSBenjamin Tissoires "If i2c-hid and hid-rmi are not used, you might want to try setting psmouse.synaptics_intertouch to 1 and report this to [email protected].\n",
18352fef826eSBenjamin Tissoires psmouse->ps2dev.serio->firmware_id);
18362fef826eSBenjamin Tissoires
1837e839ffabSBenjamin Tissoires return -ENXIO;
1838e839ffabSBenjamin Tissoires }
18392fef826eSBenjamin Tissoires }
1840e839ffabSBenjamin Tissoires
1841e839ffabSBenjamin Tissoires psmouse_info(psmouse, "Trying to set up SMBus access\n");
1842e839ffabSBenjamin Tissoires
1843e839ffabSBenjamin Tissoires error = synaptics_create_intertouch(psmouse, info, leave_breadcrumbs);
1844e839ffabSBenjamin Tissoires if (error) {
1845e839ffabSBenjamin Tissoires if (error == -EAGAIN)
1846e839ffabSBenjamin Tissoires psmouse_info(psmouse, "SMbus companion is not ready yet\n");
1847e839ffabSBenjamin Tissoires else
1848e839ffabSBenjamin Tissoires psmouse_err(psmouse, "unable to create intertouch device\n");
1849e839ffabSBenjamin Tissoires
1850e839ffabSBenjamin Tissoires return error;
1851e839ffabSBenjamin Tissoires }
1852e839ffabSBenjamin Tissoires
1853e839ffabSBenjamin Tissoires return 0;
1854e839ffabSBenjamin Tissoires }
1855e839ffabSBenjamin Tissoires
synaptics_init_smbus(struct psmouse * psmouse)1856e839ffabSBenjamin Tissoires int synaptics_init_smbus(struct psmouse *psmouse)
1857e839ffabSBenjamin Tissoires {
1858e839ffabSBenjamin Tissoires struct synaptics_device_info info;
1859e839ffabSBenjamin Tissoires int error;
1860e839ffabSBenjamin Tissoires
1861e839ffabSBenjamin Tissoires psmouse_reset(psmouse);
1862e839ffabSBenjamin Tissoires
1863e839ffabSBenjamin Tissoires error = synaptics_query_hardware(psmouse, &info);
1864e839ffabSBenjamin Tissoires if (error) {
1865e839ffabSBenjamin Tissoires psmouse_err(psmouse, "Unable to query device: %d\n", error);
1866e839ffabSBenjamin Tissoires return error;
1867e839ffabSBenjamin Tissoires }
1868e839ffabSBenjamin Tissoires
1869e839ffabSBenjamin Tissoires if (!SYN_CAP_INTERTOUCH(info.ext_cap_0c))
1870e839ffabSBenjamin Tissoires return -ENXIO;
1871e839ffabSBenjamin Tissoires
1872e839ffabSBenjamin Tissoires return synaptics_create_intertouch(psmouse, &info, false);
1873e839ffabSBenjamin Tissoires }
1874e839ffabSBenjamin Tissoires
1875e839ffabSBenjamin Tissoires #else /* CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS */
1876e839ffabSBenjamin Tissoires
1877e839ffabSBenjamin Tissoires static int __maybe_unused
synaptics_setup_intertouch(struct psmouse * psmouse,struct synaptics_device_info * info,bool leave_breadcrumbs)1878e839ffabSBenjamin Tissoires synaptics_setup_intertouch(struct psmouse *psmouse,
1879e839ffabSBenjamin Tissoires struct synaptics_device_info *info,
1880e839ffabSBenjamin Tissoires bool leave_breadcrumbs)
1881e839ffabSBenjamin Tissoires {
1882e839ffabSBenjamin Tissoires return -ENOSYS;
1883e839ffabSBenjamin Tissoires }
1884e839ffabSBenjamin Tissoires
synaptics_init_smbus(struct psmouse * psmouse)1885e839ffabSBenjamin Tissoires int synaptics_init_smbus(struct psmouse *psmouse)
1886e839ffabSBenjamin Tissoires {
1887e839ffabSBenjamin Tissoires return -ENOSYS;
1888e839ffabSBenjamin Tissoires }
1889e839ffabSBenjamin Tissoires
1890e839ffabSBenjamin Tissoires #endif /* CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS */
1891e839ffabSBenjamin Tissoires
1892e839ffabSBenjamin Tissoires #if defined(CONFIG_MOUSE_PS2_SYNAPTICS) || \
1893e839ffabSBenjamin Tissoires defined(CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS)
1894e839ffabSBenjamin Tissoires
synaptics_init(struct psmouse * psmouse)1895e839ffabSBenjamin Tissoires int synaptics_init(struct psmouse *psmouse)
1896e839ffabSBenjamin Tissoires {
1897e839ffabSBenjamin Tissoires struct synaptics_device_info info;
1898e839ffabSBenjamin Tissoires int error;
1899e839ffabSBenjamin Tissoires int retval;
1900e839ffabSBenjamin Tissoires
1901e839ffabSBenjamin Tissoires psmouse_reset(psmouse);
1902e839ffabSBenjamin Tissoires
1903e839ffabSBenjamin Tissoires error = synaptics_query_hardware(psmouse, &info);
1904e839ffabSBenjamin Tissoires if (error) {
1905e839ffabSBenjamin Tissoires psmouse_err(psmouse, "Unable to query device: %d\n", error);
1906e839ffabSBenjamin Tissoires return error;
1907e839ffabSBenjamin Tissoires }
1908e839ffabSBenjamin Tissoires
1909e839ffabSBenjamin Tissoires if (SYN_CAP_INTERTOUCH(info.ext_cap_0c)) {
1910f4101ff8SBenjamin Tissoires if ((!IS_ENABLED(CONFIG_RMI4_SMB) ||
1911f4101ff8SBenjamin Tissoires !IS_ENABLED(CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS)) &&
1912f4101ff8SBenjamin Tissoires /* Forcepads need F21, which is not ready */
1913f4101ff8SBenjamin Tissoires !psmouse_matches_pnp_id(psmouse, forcepad_pnp_ids)) {
1914f4101ff8SBenjamin Tissoires psmouse_warn(psmouse,
1915f4101ff8SBenjamin Tissoires "The touchpad can support a better bus than the too old PS/2 protocol. "
1916f4101ff8SBenjamin Tissoires "Make sure MOUSE_PS2_SYNAPTICS_SMBUS and RMI4_SMB are enabled to get a better touchpad experience.\n");
1917f4101ff8SBenjamin Tissoires }
1918f4101ff8SBenjamin Tissoires
1919e839ffabSBenjamin Tissoires error = synaptics_setup_intertouch(psmouse, &info, true);
1920e839ffabSBenjamin Tissoires if (!error)
1921e839ffabSBenjamin Tissoires return PSMOUSE_SYNAPTICS_SMBUS;
1922e839ffabSBenjamin Tissoires }
1923e839ffabSBenjamin Tissoires
1924e839ffabSBenjamin Tissoires retval = synaptics_setup_ps2(psmouse, &info);
1925e839ffabSBenjamin Tissoires if (retval < 0) {
1926e839ffabSBenjamin Tissoires /*
1927e839ffabSBenjamin Tissoires * Not using any flavor of Synaptics support, so clean up
1928e839ffabSBenjamin Tissoires * SMbus breadcrumbs, if any.
1929e839ffabSBenjamin Tissoires */
1930e839ffabSBenjamin Tissoires psmouse_smbus_cleanup(psmouse);
1931e839ffabSBenjamin Tissoires }
1932e839ffabSBenjamin Tissoires
1933e839ffabSBenjamin Tissoires return retval;
1934e839ffabSBenjamin Tissoires }
1935e839ffabSBenjamin Tissoires
1936e839ffabSBenjamin Tissoires #else /* CONFIG_MOUSE_PS2_SYNAPTICS || CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS */
1937e839ffabSBenjamin Tissoires
synaptics_init(struct psmouse * psmouse)1938e839ffabSBenjamin Tissoires int synaptics_init(struct psmouse *psmouse)
1939e839ffabSBenjamin Tissoires {
1940e839ffabSBenjamin Tissoires return -ENOSYS;
1941e839ffabSBenjamin Tissoires }
1942e839ffabSBenjamin Tissoires
1943e839ffabSBenjamin Tissoires #endif /* CONFIG_MOUSE_PS2_SYNAPTICS || CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS */
1944