18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Synaptics TouchPad PS/2 mouse driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * 2003 Dmitry Torokhov <dtor@mail.ru> 68c2ecf20Sopenharmony_ci * Added support for pass-through port. Special thanks to Peter Berg Larsen 78c2ecf20Sopenharmony_ci * for explaining various Synaptics quirks. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * 2003 Peter Osterlund <petero2@telia.com> 108c2ecf20Sopenharmony_ci * Ported to 2.5 input device infrastructure. 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * Copyright (C) 2001 Stefan Gmeiner <riddlebox@freesurf.ch> 138c2ecf20Sopenharmony_ci * start merging tpconfig and gpm code to a xfree-input module 148c2ecf20Sopenharmony_ci * adding some changes and extensions (ex. 3rd and 4th button) 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * Copyright (c) 1997 C. Scott Ananian <cananian@alumni.priceton.edu> 178c2ecf20Sopenharmony_ci * Copyright (c) 1998-2000 Bruce Kalk <kall@compass.com> 188c2ecf20Sopenharmony_ci * code for the special synaptics commands (from the tpconfig-source) 198c2ecf20Sopenharmony_ci * 208c2ecf20Sopenharmony_ci * Trademarks are the property of their respective owners. 218c2ecf20Sopenharmony_ci */ 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include <linux/module.h> 248c2ecf20Sopenharmony_ci#include <linux/delay.h> 258c2ecf20Sopenharmony_ci#include <linux/dmi.h> 268c2ecf20Sopenharmony_ci#include <linux/input/mt.h> 278c2ecf20Sopenharmony_ci#include <linux/serio.h> 288c2ecf20Sopenharmony_ci#include <linux/libps2.h> 298c2ecf20Sopenharmony_ci#include <linux/rmi.h> 308c2ecf20Sopenharmony_ci#include <linux/i2c.h> 318c2ecf20Sopenharmony_ci#include <linux/slab.h> 328c2ecf20Sopenharmony_ci#include "psmouse.h" 338c2ecf20Sopenharmony_ci#include "synaptics.h" 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci/* 368c2ecf20Sopenharmony_ci * The x/y limits are taken from the Synaptics TouchPad interfacing Guide, 378c2ecf20Sopenharmony_ci * section 2.3.2, which says that they should be valid regardless of the 388c2ecf20Sopenharmony_ci * actual size of the sensor. 398c2ecf20Sopenharmony_ci * Note that newer firmware allows querying device for maximum useable 408c2ecf20Sopenharmony_ci * coordinates. 418c2ecf20Sopenharmony_ci */ 428c2ecf20Sopenharmony_ci#define XMIN 0 438c2ecf20Sopenharmony_ci#define XMAX 6143 448c2ecf20Sopenharmony_ci#define YMIN 0 458c2ecf20Sopenharmony_ci#define YMAX 6143 468c2ecf20Sopenharmony_ci#define XMIN_NOMINAL 1472 478c2ecf20Sopenharmony_ci#define XMAX_NOMINAL 5472 488c2ecf20Sopenharmony_ci#define YMIN_NOMINAL 1408 498c2ecf20Sopenharmony_ci#define YMAX_NOMINAL 4448 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/* Size in bits of absolute position values reported by the hardware */ 528c2ecf20Sopenharmony_ci#define ABS_POS_BITS 13 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/* 558c2ecf20Sopenharmony_ci * These values should represent the absolute maximum value that will 568c2ecf20Sopenharmony_ci * be reported for a positive position value. Some Synaptics firmware 578c2ecf20Sopenharmony_ci * uses this value to indicate a finger near the edge of the touchpad 588c2ecf20Sopenharmony_ci * whose precise position cannot be determined. 598c2ecf20Sopenharmony_ci * 608c2ecf20Sopenharmony_ci * At least one touchpad is known to report positions in excess of this 618c2ecf20Sopenharmony_ci * value which are actually negative values truncated to the 13-bit 628c2ecf20Sopenharmony_ci * reporting range. These values have never been observed to be lower 638c2ecf20Sopenharmony_ci * than 8184 (i.e. -8), so we treat all values greater than 8176 as 648c2ecf20Sopenharmony_ci * negative and any other value as positive. 658c2ecf20Sopenharmony_ci */ 668c2ecf20Sopenharmony_ci#define X_MAX_POSITIVE 8176 678c2ecf20Sopenharmony_ci#define Y_MAX_POSITIVE 8176 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci/* maximum ABS_MT_POSITION displacement (in mm) */ 708c2ecf20Sopenharmony_ci#define DMAX 10 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci/***************************************************************************** 738c2ecf20Sopenharmony_ci * Stuff we need even when we do not want native Synaptics support 748c2ecf20Sopenharmony_ci ****************************************************************************/ 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci/* 778c2ecf20Sopenharmony_ci * Set the synaptics touchpad mode byte by special commands 788c2ecf20Sopenharmony_ci */ 798c2ecf20Sopenharmony_cistatic int synaptics_mode_cmd(struct psmouse *psmouse, u8 mode) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci u8 param[1]; 828c2ecf20Sopenharmony_ci int error; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci error = ps2_sliced_command(&psmouse->ps2dev, mode); 858c2ecf20Sopenharmony_ci if (error) 868c2ecf20Sopenharmony_ci return error; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci param[0] = SYN_PS_SET_MODE2; 898c2ecf20Sopenharmony_ci error = ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_SETRATE); 908c2ecf20Sopenharmony_ci if (error) 918c2ecf20Sopenharmony_ci return error; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci return 0; 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ciint synaptics_detect(struct psmouse *psmouse, bool set_properties) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci struct ps2dev *ps2dev = &psmouse->ps2dev; 998c2ecf20Sopenharmony_ci u8 param[4] = { 0 }; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); 1028c2ecf20Sopenharmony_ci ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); 1038c2ecf20Sopenharmony_ci ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); 1048c2ecf20Sopenharmony_ci ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); 1058c2ecf20Sopenharmony_ci ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci if (param[1] != 0x47) 1088c2ecf20Sopenharmony_ci return -ENODEV; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci if (set_properties) { 1118c2ecf20Sopenharmony_ci psmouse->vendor = "Synaptics"; 1128c2ecf20Sopenharmony_ci psmouse->name = "TouchPad"; 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci return 0; 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_civoid synaptics_reset(struct psmouse *psmouse) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci /* reset touchpad back to relative mode, gestures enabled */ 1218c2ecf20Sopenharmony_ci synaptics_mode_cmd(psmouse, 0); 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci#if defined(CONFIG_MOUSE_PS2_SYNAPTICS) || \ 1258c2ecf20Sopenharmony_ci defined(CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS) 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci/* This list has been kindly provided by Synaptics. */ 1288c2ecf20Sopenharmony_cistatic const char * const topbuttonpad_pnp_ids[] = { 1298c2ecf20Sopenharmony_ci "LEN0017", 1308c2ecf20Sopenharmony_ci "LEN0018", 1318c2ecf20Sopenharmony_ci "LEN0019", 1328c2ecf20Sopenharmony_ci "LEN0023", 1338c2ecf20Sopenharmony_ci "LEN002A", 1348c2ecf20Sopenharmony_ci "LEN002B", 1358c2ecf20Sopenharmony_ci "LEN002C", 1368c2ecf20Sopenharmony_ci "LEN002D", 1378c2ecf20Sopenharmony_ci "LEN002E", 1388c2ecf20Sopenharmony_ci "LEN0033", /* Helix */ 1398c2ecf20Sopenharmony_ci "LEN0034", /* T431s, L440, L540, T540, W540, X1 Carbon 2nd */ 1408c2ecf20Sopenharmony_ci "LEN0035", /* X240 */ 1418c2ecf20Sopenharmony_ci "LEN0036", /* T440 */ 1428c2ecf20Sopenharmony_ci "LEN0037", /* X1 Carbon 2nd */ 1438c2ecf20Sopenharmony_ci "LEN0038", 1448c2ecf20Sopenharmony_ci "LEN0039", /* T440s */ 1458c2ecf20Sopenharmony_ci "LEN0041", 1468c2ecf20Sopenharmony_ci "LEN0042", /* Yoga */ 1478c2ecf20Sopenharmony_ci "LEN0045", 1488c2ecf20Sopenharmony_ci "LEN0047", 1498c2ecf20Sopenharmony_ci "LEN2000", /* S540 */ 1508c2ecf20Sopenharmony_ci "LEN2001", /* Edge E431 */ 1518c2ecf20Sopenharmony_ci "LEN2002", /* Edge E531 */ 1528c2ecf20Sopenharmony_ci "LEN2003", 1538c2ecf20Sopenharmony_ci "LEN2004", /* L440 */ 1548c2ecf20Sopenharmony_ci "LEN2005", 1558c2ecf20Sopenharmony_ci "LEN2006", /* Edge E440/E540 */ 1568c2ecf20Sopenharmony_ci "LEN2007", 1578c2ecf20Sopenharmony_ci "LEN2008", 1588c2ecf20Sopenharmony_ci "LEN2009", 1598c2ecf20Sopenharmony_ci "LEN200A", 1608c2ecf20Sopenharmony_ci "LEN200B", 1618c2ecf20Sopenharmony_ci NULL 1628c2ecf20Sopenharmony_ci}; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic const char * const smbus_pnp_ids[] = { 1658c2ecf20Sopenharmony_ci /* all of the topbuttonpad_pnp_ids are valid, we just add some extras */ 1668c2ecf20Sopenharmony_ci "LEN0048", /* X1 Carbon 3 */ 1678c2ecf20Sopenharmony_ci "LEN0046", /* X250 */ 1688c2ecf20Sopenharmony_ci "LEN0049", /* Yoga 11e */ 1698c2ecf20Sopenharmony_ci "LEN004a", /* W541 */ 1708c2ecf20Sopenharmony_ci "LEN005b", /* P50 */ 1718c2ecf20Sopenharmony_ci "LEN005e", /* T560 */ 1728c2ecf20Sopenharmony_ci "LEN006c", /* T470s */ 1738c2ecf20Sopenharmony_ci "LEN007a", /* T470s */ 1748c2ecf20Sopenharmony_ci "LEN0071", /* T480 */ 1758c2ecf20Sopenharmony_ci "LEN0072", /* X1 Carbon Gen 5 (2017) - Elan/ALPS trackpoint */ 1768c2ecf20Sopenharmony_ci "LEN0073", /* X1 Carbon G5 (Elantech) */ 1778c2ecf20Sopenharmony_ci "LEN0091", /* X1 Carbon 6 */ 1788c2ecf20Sopenharmony_ci "LEN0092", /* X1 Carbon 6 */ 1798c2ecf20Sopenharmony_ci "LEN0093", /* T480 */ 1808c2ecf20Sopenharmony_ci "LEN0096", /* X280 */ 1818c2ecf20Sopenharmony_ci "LEN0097", /* X280 -> ALPS trackpoint */ 1828c2ecf20Sopenharmony_ci "LEN0099", /* X1 Extreme Gen 1 / P1 Gen 1 */ 1838c2ecf20Sopenharmony_ci "LEN009b", /* T580 */ 1848c2ecf20Sopenharmony_ci "LEN0402", /* X1 Extreme Gen 2 / P1 Gen 2 */ 1858c2ecf20Sopenharmony_ci "LEN200f", /* T450s */ 1868c2ecf20Sopenharmony_ci "LEN2044", /* L470 */ 1878c2ecf20Sopenharmony_ci "LEN2054", /* E480 */ 1888c2ecf20Sopenharmony_ci "LEN2055", /* E580 */ 1898c2ecf20Sopenharmony_ci "LEN2068", /* T14 Gen 1 */ 1908c2ecf20Sopenharmony_ci "SYN3052", /* HP EliteBook 840 G4 */ 1918c2ecf20Sopenharmony_ci "SYN3221", /* HP 15-ay000 */ 1928c2ecf20Sopenharmony_ci "SYN323d", /* HP Spectre X360 13-w013dx */ 1938c2ecf20Sopenharmony_ci "SYN3257", /* HP Envy 13-ad105ng */ 1948c2ecf20Sopenharmony_ci NULL 1958c2ecf20Sopenharmony_ci}; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic const char * const forcepad_pnp_ids[] = { 1988c2ecf20Sopenharmony_ci "SYN300D", 1998c2ecf20Sopenharmony_ci "SYN3014", 2008c2ecf20Sopenharmony_ci NULL 2018c2ecf20Sopenharmony_ci}; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci/* 2048c2ecf20Sopenharmony_ci * Send a command to the synaptics touchpad by special commands 2058c2ecf20Sopenharmony_ci */ 2068c2ecf20Sopenharmony_cistatic int synaptics_send_cmd(struct psmouse *psmouse, u8 cmd, u8 *param) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci int error; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci error = ps2_sliced_command(&psmouse->ps2dev, cmd); 2118c2ecf20Sopenharmony_ci if (error) 2128c2ecf20Sopenharmony_ci return error; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci error = ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO); 2158c2ecf20Sopenharmony_ci if (error) 2168c2ecf20Sopenharmony_ci return error; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci return 0; 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_cistatic int synaptics_query_int(struct psmouse *psmouse, u8 query_cmd, u32 *val) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci int error; 2248c2ecf20Sopenharmony_ci union { 2258c2ecf20Sopenharmony_ci __be32 be_val; 2268c2ecf20Sopenharmony_ci char buf[4]; 2278c2ecf20Sopenharmony_ci } resp = { 0 }; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci error = synaptics_send_cmd(psmouse, query_cmd, resp.buf + 1); 2308c2ecf20Sopenharmony_ci if (error) 2318c2ecf20Sopenharmony_ci return error; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci *val = be32_to_cpu(resp.be_val); 2348c2ecf20Sopenharmony_ci return 0; 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci/* 2388c2ecf20Sopenharmony_ci * Identify Touchpad 2398c2ecf20Sopenharmony_ci * See also the SYN_ID_* macros 2408c2ecf20Sopenharmony_ci */ 2418c2ecf20Sopenharmony_cistatic int synaptics_identify(struct psmouse *psmouse, 2428c2ecf20Sopenharmony_ci struct synaptics_device_info *info) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci int error; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci error = synaptics_query_int(psmouse, SYN_QUE_IDENTIFY, &info->identity); 2478c2ecf20Sopenharmony_ci if (error) 2488c2ecf20Sopenharmony_ci return error; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci return SYN_ID_IS_SYNAPTICS(info->identity) ? 0 : -ENXIO; 2518c2ecf20Sopenharmony_ci} 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci/* 2548c2ecf20Sopenharmony_ci * Read the model-id bytes from the touchpad 2558c2ecf20Sopenharmony_ci * see also SYN_MODEL_* macros 2568c2ecf20Sopenharmony_ci */ 2578c2ecf20Sopenharmony_cistatic int synaptics_model_id(struct psmouse *psmouse, 2588c2ecf20Sopenharmony_ci struct synaptics_device_info *info) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci return synaptics_query_int(psmouse, SYN_QUE_MODEL, &info->model_id); 2618c2ecf20Sopenharmony_ci} 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci/* 2648c2ecf20Sopenharmony_ci * Read the firmware id from the touchpad 2658c2ecf20Sopenharmony_ci */ 2668c2ecf20Sopenharmony_cistatic int synaptics_firmware_id(struct psmouse *psmouse, 2678c2ecf20Sopenharmony_ci struct synaptics_device_info *info) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci return synaptics_query_int(psmouse, SYN_QUE_FIRMWARE_ID, 2708c2ecf20Sopenharmony_ci &info->firmware_id); 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci/* 2748c2ecf20Sopenharmony_ci * Read the board id and the "More Extended Queries" from the touchpad 2758c2ecf20Sopenharmony_ci * The board id is encoded in the "QUERY MODES" response 2768c2ecf20Sopenharmony_ci */ 2778c2ecf20Sopenharmony_cistatic int synaptics_query_modes(struct psmouse *psmouse, 2788c2ecf20Sopenharmony_ci struct synaptics_device_info *info) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci u8 bid[3]; 2818c2ecf20Sopenharmony_ci int error; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci /* firmwares prior 7.5 have no board_id encoded */ 2848c2ecf20Sopenharmony_ci if (SYN_ID_FULL(info->identity) < 0x705) 2858c2ecf20Sopenharmony_ci return 0; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci error = synaptics_send_cmd(psmouse, SYN_QUE_MODES, bid); 2888c2ecf20Sopenharmony_ci if (error) 2898c2ecf20Sopenharmony_ci return error; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci info->board_id = ((bid[0] & 0xfc) << 6) | bid[1]; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci if (SYN_MEXT_CAP_BIT(bid[0])) 2948c2ecf20Sopenharmony_ci return synaptics_query_int(psmouse, SYN_QUE_MEXT_CAPAB_10, 2958c2ecf20Sopenharmony_ci &info->ext_cap_10); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci return 0; 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci/* 3018c2ecf20Sopenharmony_ci * Read the capability-bits from the touchpad 3028c2ecf20Sopenharmony_ci * see also the SYN_CAP_* macros 3038c2ecf20Sopenharmony_ci */ 3048c2ecf20Sopenharmony_cistatic int synaptics_capability(struct psmouse *psmouse, 3058c2ecf20Sopenharmony_ci struct synaptics_device_info *info) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci int error; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci error = synaptics_query_int(psmouse, SYN_QUE_CAPABILITIES, 3108c2ecf20Sopenharmony_ci &info->capabilities); 3118c2ecf20Sopenharmony_ci if (error) 3128c2ecf20Sopenharmony_ci return error; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci info->ext_cap = info->ext_cap_0c = 0; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci /* 3178c2ecf20Sopenharmony_ci * Older firmwares had submodel ID fixed to 0x47 3188c2ecf20Sopenharmony_ci */ 3198c2ecf20Sopenharmony_ci if (SYN_ID_FULL(info->identity) < 0x705 && 3208c2ecf20Sopenharmony_ci SYN_CAP_SUBMODEL_ID(info->capabilities) != 0x47) { 3218c2ecf20Sopenharmony_ci return -ENXIO; 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci /* 3258c2ecf20Sopenharmony_ci * Unless capExtended is set the rest of the flags should be ignored 3268c2ecf20Sopenharmony_ci */ 3278c2ecf20Sopenharmony_ci if (!SYN_CAP_EXTENDED(info->capabilities)) 3288c2ecf20Sopenharmony_ci info->capabilities = 0; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci if (SYN_EXT_CAP_REQUESTS(info->capabilities) >= 1) { 3318c2ecf20Sopenharmony_ci error = synaptics_query_int(psmouse, SYN_QUE_EXT_CAPAB, 3328c2ecf20Sopenharmony_ci &info->ext_cap); 3338c2ecf20Sopenharmony_ci if (error) { 3348c2ecf20Sopenharmony_ci psmouse_warn(psmouse, 3358c2ecf20Sopenharmony_ci "device claims to have extended capabilities, but I'm not able to read them.\n"); 3368c2ecf20Sopenharmony_ci } else { 3378c2ecf20Sopenharmony_ci /* 3388c2ecf20Sopenharmony_ci * if nExtBtn is greater than 8 it should be considered 3398c2ecf20Sopenharmony_ci * invalid and treated as 0 3408c2ecf20Sopenharmony_ci */ 3418c2ecf20Sopenharmony_ci if (SYN_CAP_MULTI_BUTTON_NO(info->ext_cap) > 8) 3428c2ecf20Sopenharmony_ci info->ext_cap &= ~SYN_CAP_MB_MASK; 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci if (SYN_EXT_CAP_REQUESTS(info->capabilities) >= 4) { 3478c2ecf20Sopenharmony_ci error = synaptics_query_int(psmouse, SYN_QUE_EXT_CAPAB_0C, 3488c2ecf20Sopenharmony_ci &info->ext_cap_0c); 3498c2ecf20Sopenharmony_ci if (error) 3508c2ecf20Sopenharmony_ci psmouse_warn(psmouse, 3518c2ecf20Sopenharmony_ci "device claims to have extended capability 0x0c, but I'm not able to read it.\n"); 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci return 0; 3558c2ecf20Sopenharmony_ci} 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci/* 3588c2ecf20Sopenharmony_ci * Read touchpad resolution and maximum reported coordinates 3598c2ecf20Sopenharmony_ci * Resolution is left zero if touchpad does not support the query 3608c2ecf20Sopenharmony_ci */ 3618c2ecf20Sopenharmony_cistatic int synaptics_resolution(struct psmouse *psmouse, 3628c2ecf20Sopenharmony_ci struct synaptics_device_info *info) 3638c2ecf20Sopenharmony_ci{ 3648c2ecf20Sopenharmony_ci u8 resp[3]; 3658c2ecf20Sopenharmony_ci int error; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci if (SYN_ID_MAJOR(info->identity) < 4) 3688c2ecf20Sopenharmony_ci return 0; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci error = synaptics_send_cmd(psmouse, SYN_QUE_RESOLUTION, resp); 3718c2ecf20Sopenharmony_ci if (!error) { 3728c2ecf20Sopenharmony_ci if (resp[0] != 0 && (resp[1] & 0x80) && resp[2] != 0) { 3738c2ecf20Sopenharmony_ci info->x_res = resp[0]; /* x resolution in units/mm */ 3748c2ecf20Sopenharmony_ci info->y_res = resp[2]; /* y resolution in units/mm */ 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci if (SYN_EXT_CAP_REQUESTS(info->capabilities) >= 5 && 3798c2ecf20Sopenharmony_ci SYN_CAP_MAX_DIMENSIONS(info->ext_cap_0c)) { 3808c2ecf20Sopenharmony_ci error = synaptics_send_cmd(psmouse, 3818c2ecf20Sopenharmony_ci SYN_QUE_EXT_MAX_COORDS, resp); 3828c2ecf20Sopenharmony_ci if (error) { 3838c2ecf20Sopenharmony_ci psmouse_warn(psmouse, 3848c2ecf20Sopenharmony_ci "device claims to have max coordinates query, but I'm not able to read it.\n"); 3858c2ecf20Sopenharmony_ci } else { 3868c2ecf20Sopenharmony_ci info->x_max = (resp[0] << 5) | ((resp[1] & 0x0f) << 1); 3878c2ecf20Sopenharmony_ci info->y_max = (resp[2] << 5) | ((resp[1] & 0xf0) >> 3); 3888c2ecf20Sopenharmony_ci psmouse_info(psmouse, 3898c2ecf20Sopenharmony_ci "queried max coordinates: x [..%d], y [..%d]\n", 3908c2ecf20Sopenharmony_ci info->x_max, info->y_max); 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci } 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci if (SYN_CAP_MIN_DIMENSIONS(info->ext_cap_0c) && 3958c2ecf20Sopenharmony_ci (SYN_EXT_CAP_REQUESTS(info->capabilities) >= 7 || 3968c2ecf20Sopenharmony_ci /* 3978c2ecf20Sopenharmony_ci * Firmware v8.1 does not report proper number of extended 3988c2ecf20Sopenharmony_ci * capabilities, but has been proven to report correct min 3998c2ecf20Sopenharmony_ci * coordinates. 4008c2ecf20Sopenharmony_ci */ 4018c2ecf20Sopenharmony_ci SYN_ID_FULL(info->identity) == 0x801)) { 4028c2ecf20Sopenharmony_ci error = synaptics_send_cmd(psmouse, 4038c2ecf20Sopenharmony_ci SYN_QUE_EXT_MIN_COORDS, resp); 4048c2ecf20Sopenharmony_ci if (error) { 4058c2ecf20Sopenharmony_ci psmouse_warn(psmouse, 4068c2ecf20Sopenharmony_ci "device claims to have min coordinates query, but I'm not able to read it.\n"); 4078c2ecf20Sopenharmony_ci } else { 4088c2ecf20Sopenharmony_ci info->x_min = (resp[0] << 5) | ((resp[1] & 0x0f) << 1); 4098c2ecf20Sopenharmony_ci info->y_min = (resp[2] << 5) | ((resp[1] & 0xf0) >> 3); 4108c2ecf20Sopenharmony_ci psmouse_info(psmouse, 4118c2ecf20Sopenharmony_ci "queried min coordinates: x [%d..], y [%d..]\n", 4128c2ecf20Sopenharmony_ci info->x_min, info->y_min); 4138c2ecf20Sopenharmony_ci } 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci return 0; 4178c2ecf20Sopenharmony_ci} 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_cistatic int synaptics_query_hardware(struct psmouse *psmouse, 4208c2ecf20Sopenharmony_ci struct synaptics_device_info *info) 4218c2ecf20Sopenharmony_ci{ 4228c2ecf20Sopenharmony_ci int error; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci memset(info, 0, sizeof(*info)); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci error = synaptics_identify(psmouse, info); 4278c2ecf20Sopenharmony_ci if (error) 4288c2ecf20Sopenharmony_ci return error; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci error = synaptics_model_id(psmouse, info); 4318c2ecf20Sopenharmony_ci if (error) 4328c2ecf20Sopenharmony_ci return error; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci error = synaptics_firmware_id(psmouse, info); 4358c2ecf20Sopenharmony_ci if (error) 4368c2ecf20Sopenharmony_ci return error; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci error = synaptics_query_modes(psmouse, info); 4398c2ecf20Sopenharmony_ci if (error) 4408c2ecf20Sopenharmony_ci return error; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci error = synaptics_capability(psmouse, info); 4438c2ecf20Sopenharmony_ci if (error) 4448c2ecf20Sopenharmony_ci return error; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci error = synaptics_resolution(psmouse, info); 4478c2ecf20Sopenharmony_ci if (error) 4488c2ecf20Sopenharmony_ci return error; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci return 0; 4518c2ecf20Sopenharmony_ci} 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci#endif /* CONFIG_MOUSE_PS2_SYNAPTICS || CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS */ 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci#ifdef CONFIG_MOUSE_PS2_SYNAPTICS 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_cistatic bool cr48_profile_sensor; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci#define ANY_BOARD_ID 0 4608c2ecf20Sopenharmony_cistruct min_max_quirk { 4618c2ecf20Sopenharmony_ci const char * const *pnp_ids; 4628c2ecf20Sopenharmony_ci struct { 4638c2ecf20Sopenharmony_ci u32 min, max; 4648c2ecf20Sopenharmony_ci } board_id; 4658c2ecf20Sopenharmony_ci u32 x_min, x_max, y_min, y_max; 4668c2ecf20Sopenharmony_ci}; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_cistatic const struct min_max_quirk min_max_pnpid_table[] = { 4698c2ecf20Sopenharmony_ci { 4708c2ecf20Sopenharmony_ci (const char * const []){"LEN0033", NULL}, 4718c2ecf20Sopenharmony_ci {ANY_BOARD_ID, ANY_BOARD_ID}, 4728c2ecf20Sopenharmony_ci 1024, 5052, 2258, 4832 4738c2ecf20Sopenharmony_ci }, 4748c2ecf20Sopenharmony_ci { 4758c2ecf20Sopenharmony_ci (const char * const []){"LEN0042", NULL}, 4768c2ecf20Sopenharmony_ci {ANY_BOARD_ID, ANY_BOARD_ID}, 4778c2ecf20Sopenharmony_ci 1232, 5710, 1156, 4696 4788c2ecf20Sopenharmony_ci }, 4798c2ecf20Sopenharmony_ci { 4808c2ecf20Sopenharmony_ci (const char * const []){"LEN0034", "LEN0036", "LEN0037", 4818c2ecf20Sopenharmony_ci "LEN0039", "LEN2002", "LEN2004", 4828c2ecf20Sopenharmony_ci NULL}, 4838c2ecf20Sopenharmony_ci {ANY_BOARD_ID, 2961}, 4848c2ecf20Sopenharmony_ci 1024, 5112, 2024, 4832 4858c2ecf20Sopenharmony_ci }, 4868c2ecf20Sopenharmony_ci { 4878c2ecf20Sopenharmony_ci (const char * const []){"LEN2000", NULL}, 4888c2ecf20Sopenharmony_ci {ANY_BOARD_ID, ANY_BOARD_ID}, 4898c2ecf20Sopenharmony_ci 1024, 5113, 2021, 4832 4908c2ecf20Sopenharmony_ci }, 4918c2ecf20Sopenharmony_ci { 4928c2ecf20Sopenharmony_ci (const char * const []){"LEN2001", NULL}, 4938c2ecf20Sopenharmony_ci {ANY_BOARD_ID, ANY_BOARD_ID}, 4948c2ecf20Sopenharmony_ci 1024, 5022, 2508, 4832 4958c2ecf20Sopenharmony_ci }, 4968c2ecf20Sopenharmony_ci { 4978c2ecf20Sopenharmony_ci (const char * const []){"LEN2006", NULL}, 4988c2ecf20Sopenharmony_ci {2691, 2691}, 4998c2ecf20Sopenharmony_ci 1024, 5045, 2457, 4832 5008c2ecf20Sopenharmony_ci }, 5018c2ecf20Sopenharmony_ci { 5028c2ecf20Sopenharmony_ci (const char * const []){"LEN2006", NULL}, 5038c2ecf20Sopenharmony_ci {ANY_BOARD_ID, ANY_BOARD_ID}, 5048c2ecf20Sopenharmony_ci 1264, 5675, 1171, 4688 5058c2ecf20Sopenharmony_ci }, 5068c2ecf20Sopenharmony_ci { } 5078c2ecf20Sopenharmony_ci}; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci/***************************************************************************** 5108c2ecf20Sopenharmony_ci * Synaptics communications functions 5118c2ecf20Sopenharmony_ci ****************************************************************************/ 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci/* 5148c2ecf20Sopenharmony_ci * Synaptics touchpads report the y coordinate from bottom to top, which is 5158c2ecf20Sopenharmony_ci * opposite from what userspace expects. 5168c2ecf20Sopenharmony_ci * This function is used to invert y before reporting. 5178c2ecf20Sopenharmony_ci */ 5188c2ecf20Sopenharmony_cistatic int synaptics_invert_y(int y) 5198c2ecf20Sopenharmony_ci{ 5208c2ecf20Sopenharmony_ci return YMAX_NOMINAL + YMIN_NOMINAL - y; 5218c2ecf20Sopenharmony_ci} 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci/* 5248c2ecf20Sopenharmony_ci * Apply quirk(s) if the hardware matches 5258c2ecf20Sopenharmony_ci */ 5268c2ecf20Sopenharmony_cistatic void synaptics_apply_quirks(struct psmouse *psmouse, 5278c2ecf20Sopenharmony_ci struct synaptics_device_info *info) 5288c2ecf20Sopenharmony_ci{ 5298c2ecf20Sopenharmony_ci int i; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci for (i = 0; min_max_pnpid_table[i].pnp_ids; i++) { 5328c2ecf20Sopenharmony_ci if (!psmouse_matches_pnp_id(psmouse, 5338c2ecf20Sopenharmony_ci min_max_pnpid_table[i].pnp_ids)) 5348c2ecf20Sopenharmony_ci continue; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci if (min_max_pnpid_table[i].board_id.min != ANY_BOARD_ID && 5378c2ecf20Sopenharmony_ci info->board_id < min_max_pnpid_table[i].board_id.min) 5388c2ecf20Sopenharmony_ci continue; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci if (min_max_pnpid_table[i].board_id.max != ANY_BOARD_ID && 5418c2ecf20Sopenharmony_ci info->board_id > min_max_pnpid_table[i].board_id.max) 5428c2ecf20Sopenharmony_ci continue; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci info->x_min = min_max_pnpid_table[i].x_min; 5458c2ecf20Sopenharmony_ci info->x_max = min_max_pnpid_table[i].x_max; 5468c2ecf20Sopenharmony_ci info->y_min = min_max_pnpid_table[i].y_min; 5478c2ecf20Sopenharmony_ci info->y_max = min_max_pnpid_table[i].y_max; 5488c2ecf20Sopenharmony_ci psmouse_info(psmouse, 5498c2ecf20Sopenharmony_ci "quirked min/max coordinates: x [%d..%d], y [%d..%d]\n", 5508c2ecf20Sopenharmony_ci info->x_min, info->x_max, 5518c2ecf20Sopenharmony_ci info->y_min, info->y_max); 5528c2ecf20Sopenharmony_ci break; 5538c2ecf20Sopenharmony_ci } 5548c2ecf20Sopenharmony_ci} 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_cistatic bool synaptics_has_agm(struct synaptics_data *priv) 5578c2ecf20Sopenharmony_ci{ 5588c2ecf20Sopenharmony_ci return (SYN_CAP_ADV_GESTURE(priv->info.ext_cap_0c) || 5598c2ecf20Sopenharmony_ci SYN_CAP_IMAGE_SENSOR(priv->info.ext_cap_0c)); 5608c2ecf20Sopenharmony_ci} 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_cistatic int synaptics_set_advanced_gesture_mode(struct psmouse *psmouse) 5638c2ecf20Sopenharmony_ci{ 5648c2ecf20Sopenharmony_ci static u8 param = 0xc8; 5658c2ecf20Sopenharmony_ci int error; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci error = ps2_sliced_command(&psmouse->ps2dev, SYN_QUE_MODEL); 5688c2ecf20Sopenharmony_ci if (error) 5698c2ecf20Sopenharmony_ci return error; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci error = ps2_command(&psmouse->ps2dev, ¶m, PSMOUSE_CMD_SETRATE); 5728c2ecf20Sopenharmony_ci if (error) 5738c2ecf20Sopenharmony_ci return error; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci return 0; 5768c2ecf20Sopenharmony_ci} 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_cistatic int synaptics_set_mode(struct psmouse *psmouse) 5798c2ecf20Sopenharmony_ci{ 5808c2ecf20Sopenharmony_ci struct synaptics_data *priv = psmouse->private; 5818c2ecf20Sopenharmony_ci int error; 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci priv->mode = 0; 5848c2ecf20Sopenharmony_ci if (priv->absolute_mode) 5858c2ecf20Sopenharmony_ci priv->mode |= SYN_BIT_ABSOLUTE_MODE; 5868c2ecf20Sopenharmony_ci if (priv->disable_gesture) 5878c2ecf20Sopenharmony_ci priv->mode |= SYN_BIT_DISABLE_GESTURE; 5888c2ecf20Sopenharmony_ci if (psmouse->rate >= 80) 5898c2ecf20Sopenharmony_ci priv->mode |= SYN_BIT_HIGH_RATE; 5908c2ecf20Sopenharmony_ci if (SYN_CAP_EXTENDED(priv->info.capabilities)) 5918c2ecf20Sopenharmony_ci priv->mode |= SYN_BIT_W_MODE; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci error = synaptics_mode_cmd(psmouse, priv->mode); 5948c2ecf20Sopenharmony_ci if (error) 5958c2ecf20Sopenharmony_ci return error; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci if (priv->absolute_mode && synaptics_has_agm(priv)) { 5988c2ecf20Sopenharmony_ci error = synaptics_set_advanced_gesture_mode(psmouse); 5998c2ecf20Sopenharmony_ci if (error) { 6008c2ecf20Sopenharmony_ci psmouse_err(psmouse, 6018c2ecf20Sopenharmony_ci "Advanced gesture mode init failed: %d\n", 6028c2ecf20Sopenharmony_ci error); 6038c2ecf20Sopenharmony_ci return error; 6048c2ecf20Sopenharmony_ci } 6058c2ecf20Sopenharmony_ci } 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci return 0; 6088c2ecf20Sopenharmony_ci} 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_cistatic void synaptics_set_rate(struct psmouse *psmouse, unsigned int rate) 6118c2ecf20Sopenharmony_ci{ 6128c2ecf20Sopenharmony_ci struct synaptics_data *priv = psmouse->private; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci if (rate >= 80) { 6158c2ecf20Sopenharmony_ci priv->mode |= SYN_BIT_HIGH_RATE; 6168c2ecf20Sopenharmony_ci psmouse->rate = 80; 6178c2ecf20Sopenharmony_ci } else { 6188c2ecf20Sopenharmony_ci priv->mode &= ~SYN_BIT_HIGH_RATE; 6198c2ecf20Sopenharmony_ci psmouse->rate = 40; 6208c2ecf20Sopenharmony_ci } 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci synaptics_mode_cmd(psmouse, priv->mode); 6238c2ecf20Sopenharmony_ci} 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci/***************************************************************************** 6268c2ecf20Sopenharmony_ci * Synaptics pass-through PS/2 port support 6278c2ecf20Sopenharmony_ci ****************************************************************************/ 6288c2ecf20Sopenharmony_cistatic int synaptics_pt_write(struct serio *serio, u8 c) 6298c2ecf20Sopenharmony_ci{ 6308c2ecf20Sopenharmony_ci struct psmouse *parent = serio_get_drvdata(serio->parent); 6318c2ecf20Sopenharmony_ci u8 rate_param = SYN_PS_CLIENT_CMD; /* indicates that we want pass-through port */ 6328c2ecf20Sopenharmony_ci int error; 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci error = ps2_sliced_command(&parent->ps2dev, c); 6358c2ecf20Sopenharmony_ci if (error) 6368c2ecf20Sopenharmony_ci return error; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci error = ps2_command(&parent->ps2dev, &rate_param, PSMOUSE_CMD_SETRATE); 6398c2ecf20Sopenharmony_ci if (error) 6408c2ecf20Sopenharmony_ci return error; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci return 0; 6438c2ecf20Sopenharmony_ci} 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_cistatic int synaptics_pt_start(struct serio *serio) 6468c2ecf20Sopenharmony_ci{ 6478c2ecf20Sopenharmony_ci struct psmouse *parent = serio_get_drvdata(serio->parent); 6488c2ecf20Sopenharmony_ci struct synaptics_data *priv = parent->private; 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci serio_pause_rx(parent->ps2dev.serio); 6518c2ecf20Sopenharmony_ci priv->pt_port = serio; 6528c2ecf20Sopenharmony_ci serio_continue_rx(parent->ps2dev.serio); 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci return 0; 6558c2ecf20Sopenharmony_ci} 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_cistatic void synaptics_pt_stop(struct serio *serio) 6588c2ecf20Sopenharmony_ci{ 6598c2ecf20Sopenharmony_ci struct psmouse *parent = serio_get_drvdata(serio->parent); 6608c2ecf20Sopenharmony_ci struct synaptics_data *priv = parent->private; 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci serio_pause_rx(parent->ps2dev.serio); 6638c2ecf20Sopenharmony_ci priv->pt_port = NULL; 6648c2ecf20Sopenharmony_ci serio_continue_rx(parent->ps2dev.serio); 6658c2ecf20Sopenharmony_ci} 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_cistatic int synaptics_is_pt_packet(u8 *buf) 6688c2ecf20Sopenharmony_ci{ 6698c2ecf20Sopenharmony_ci return (buf[0] & 0xFC) == 0x84 && (buf[3] & 0xCC) == 0xC4; 6708c2ecf20Sopenharmony_ci} 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_cistatic void synaptics_pass_pt_packet(struct serio *ptport, u8 *packet) 6738c2ecf20Sopenharmony_ci{ 6748c2ecf20Sopenharmony_ci struct psmouse *child = serio_get_drvdata(ptport); 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci if (child && child->state == PSMOUSE_ACTIVATED) { 6778c2ecf20Sopenharmony_ci serio_interrupt(ptport, packet[1], 0); 6788c2ecf20Sopenharmony_ci serio_interrupt(ptport, packet[4], 0); 6798c2ecf20Sopenharmony_ci serio_interrupt(ptport, packet[5], 0); 6808c2ecf20Sopenharmony_ci if (child->pktsize == 4) 6818c2ecf20Sopenharmony_ci serio_interrupt(ptport, packet[2], 0); 6828c2ecf20Sopenharmony_ci } else { 6838c2ecf20Sopenharmony_ci serio_interrupt(ptport, packet[1], 0); 6848c2ecf20Sopenharmony_ci } 6858c2ecf20Sopenharmony_ci} 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_cistatic void synaptics_pt_activate(struct psmouse *psmouse) 6888c2ecf20Sopenharmony_ci{ 6898c2ecf20Sopenharmony_ci struct synaptics_data *priv = psmouse->private; 6908c2ecf20Sopenharmony_ci struct psmouse *child = serio_get_drvdata(priv->pt_port); 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci /* adjust the touchpad to child's choice of protocol */ 6938c2ecf20Sopenharmony_ci if (child) { 6948c2ecf20Sopenharmony_ci if (child->pktsize == 4) 6958c2ecf20Sopenharmony_ci priv->mode |= SYN_BIT_FOUR_BYTE_CLIENT; 6968c2ecf20Sopenharmony_ci else 6978c2ecf20Sopenharmony_ci priv->mode &= ~SYN_BIT_FOUR_BYTE_CLIENT; 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci if (synaptics_mode_cmd(psmouse, priv->mode)) 7008c2ecf20Sopenharmony_ci psmouse_warn(psmouse, 7018c2ecf20Sopenharmony_ci "failed to switch guest protocol\n"); 7028c2ecf20Sopenharmony_ci } 7038c2ecf20Sopenharmony_ci} 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_cistatic void synaptics_pt_create(struct psmouse *psmouse) 7068c2ecf20Sopenharmony_ci{ 7078c2ecf20Sopenharmony_ci struct serio *serio; 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci serio = kzalloc(sizeof(struct serio), GFP_KERNEL); 7108c2ecf20Sopenharmony_ci if (!serio) { 7118c2ecf20Sopenharmony_ci psmouse_err(psmouse, 7128c2ecf20Sopenharmony_ci "not enough memory for pass-through port\n"); 7138c2ecf20Sopenharmony_ci return; 7148c2ecf20Sopenharmony_ci } 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci serio->id.type = SERIO_PS_PSTHRU; 7178c2ecf20Sopenharmony_ci strlcpy(serio->name, "Synaptics pass-through", sizeof(serio->name)); 7188c2ecf20Sopenharmony_ci strlcpy(serio->phys, "synaptics-pt/serio0", sizeof(serio->phys)); 7198c2ecf20Sopenharmony_ci serio->write = synaptics_pt_write; 7208c2ecf20Sopenharmony_ci serio->start = synaptics_pt_start; 7218c2ecf20Sopenharmony_ci serio->stop = synaptics_pt_stop; 7228c2ecf20Sopenharmony_ci serio->parent = psmouse->ps2dev.serio; 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci psmouse->pt_activate = synaptics_pt_activate; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci psmouse_info(psmouse, "serio: %s port at %s\n", 7278c2ecf20Sopenharmony_ci serio->name, psmouse->phys); 7288c2ecf20Sopenharmony_ci serio_register_port(serio); 7298c2ecf20Sopenharmony_ci} 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci/***************************************************************************** 7328c2ecf20Sopenharmony_ci * Functions to interpret the absolute mode packets 7338c2ecf20Sopenharmony_ci ****************************************************************************/ 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_cistatic void synaptics_parse_agm(const u8 buf[], 7368c2ecf20Sopenharmony_ci struct synaptics_data *priv, 7378c2ecf20Sopenharmony_ci struct synaptics_hw_state *hw) 7388c2ecf20Sopenharmony_ci{ 7398c2ecf20Sopenharmony_ci struct synaptics_hw_state *agm = &priv->agm; 7408c2ecf20Sopenharmony_ci int agm_packet_type; 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci agm_packet_type = (buf[5] & 0x30) >> 4; 7438c2ecf20Sopenharmony_ci switch (agm_packet_type) { 7448c2ecf20Sopenharmony_ci case 1: 7458c2ecf20Sopenharmony_ci /* Gesture packet: (x, y, z) half resolution */ 7468c2ecf20Sopenharmony_ci agm->w = hw->w; 7478c2ecf20Sopenharmony_ci agm->x = (((buf[4] & 0x0f) << 8) | buf[1]) << 1; 7488c2ecf20Sopenharmony_ci agm->y = (((buf[4] & 0xf0) << 4) | buf[2]) << 1; 7498c2ecf20Sopenharmony_ci agm->z = ((buf[3] & 0x30) | (buf[5] & 0x0f)) << 1; 7508c2ecf20Sopenharmony_ci break; 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci case 2: 7538c2ecf20Sopenharmony_ci /* AGM-CONTACT packet: we are only interested in the count */ 7548c2ecf20Sopenharmony_ci priv->agm_count = buf[1]; 7558c2ecf20Sopenharmony_ci break; 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci default: 7588c2ecf20Sopenharmony_ci break; 7598c2ecf20Sopenharmony_ci } 7608c2ecf20Sopenharmony_ci} 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_cistatic void synaptics_parse_ext_buttons(const u8 buf[], 7638c2ecf20Sopenharmony_ci struct synaptics_data *priv, 7648c2ecf20Sopenharmony_ci struct synaptics_hw_state *hw) 7658c2ecf20Sopenharmony_ci{ 7668c2ecf20Sopenharmony_ci unsigned int ext_bits = 7678c2ecf20Sopenharmony_ci (SYN_CAP_MULTI_BUTTON_NO(priv->info.ext_cap) + 1) >> 1; 7688c2ecf20Sopenharmony_ci unsigned int ext_mask = GENMASK(ext_bits - 1, 0); 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci hw->ext_buttons = buf[4] & ext_mask; 7718c2ecf20Sopenharmony_ci hw->ext_buttons |= (buf[5] & ext_mask) << ext_bits; 7728c2ecf20Sopenharmony_ci} 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_cistatic int synaptics_parse_hw_state(const u8 buf[], 7758c2ecf20Sopenharmony_ci struct synaptics_data *priv, 7768c2ecf20Sopenharmony_ci struct synaptics_hw_state *hw) 7778c2ecf20Sopenharmony_ci{ 7788c2ecf20Sopenharmony_ci memset(hw, 0, sizeof(struct synaptics_hw_state)); 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci if (SYN_MODEL_NEWABS(priv->info.model_id)) { 7818c2ecf20Sopenharmony_ci hw->w = (((buf[0] & 0x30) >> 2) | 7828c2ecf20Sopenharmony_ci ((buf[0] & 0x04) >> 1) | 7838c2ecf20Sopenharmony_ci ((buf[3] & 0x04) >> 2)); 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci if (synaptics_has_agm(priv) && hw->w == 2) { 7868c2ecf20Sopenharmony_ci synaptics_parse_agm(buf, priv, hw); 7878c2ecf20Sopenharmony_ci return 1; 7888c2ecf20Sopenharmony_ci } 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci hw->x = (((buf[3] & 0x10) << 8) | 7918c2ecf20Sopenharmony_ci ((buf[1] & 0x0f) << 8) | 7928c2ecf20Sopenharmony_ci buf[4]); 7938c2ecf20Sopenharmony_ci hw->y = (((buf[3] & 0x20) << 7) | 7948c2ecf20Sopenharmony_ci ((buf[1] & 0xf0) << 4) | 7958c2ecf20Sopenharmony_ci buf[5]); 7968c2ecf20Sopenharmony_ci hw->z = buf[2]; 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci hw->left = (buf[0] & 0x01) ? 1 : 0; 7998c2ecf20Sopenharmony_ci hw->right = (buf[0] & 0x02) ? 1 : 0; 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci if (priv->is_forcepad) { 8028c2ecf20Sopenharmony_ci /* 8038c2ecf20Sopenharmony_ci * ForcePads, like Clickpads, use middle button 8048c2ecf20Sopenharmony_ci * bits to report primary button clicks. 8058c2ecf20Sopenharmony_ci * Unfortunately they report primary button not 8068c2ecf20Sopenharmony_ci * only when user presses on the pad above certain 8078c2ecf20Sopenharmony_ci * threshold, but also when there are more than one 8088c2ecf20Sopenharmony_ci * finger on the touchpad, which interferes with 8098c2ecf20Sopenharmony_ci * out multi-finger gestures. 8108c2ecf20Sopenharmony_ci */ 8118c2ecf20Sopenharmony_ci if (hw->z == 0) { 8128c2ecf20Sopenharmony_ci /* No contacts */ 8138c2ecf20Sopenharmony_ci priv->press = priv->report_press = false; 8148c2ecf20Sopenharmony_ci } else if (hw->w >= 4 && ((buf[0] ^ buf[3]) & 0x01)) { 8158c2ecf20Sopenharmony_ci /* 8168c2ecf20Sopenharmony_ci * Single-finger touch with pressure above 8178c2ecf20Sopenharmony_ci * the threshold. If pressure stays long 8188c2ecf20Sopenharmony_ci * enough, we'll start reporting primary 8198c2ecf20Sopenharmony_ci * button. We rely on the device continuing 8208c2ecf20Sopenharmony_ci * sending data even if finger does not 8218c2ecf20Sopenharmony_ci * move. 8228c2ecf20Sopenharmony_ci */ 8238c2ecf20Sopenharmony_ci if (!priv->press) { 8248c2ecf20Sopenharmony_ci priv->press_start = jiffies; 8258c2ecf20Sopenharmony_ci priv->press = true; 8268c2ecf20Sopenharmony_ci } else if (time_after(jiffies, 8278c2ecf20Sopenharmony_ci priv->press_start + 8288c2ecf20Sopenharmony_ci msecs_to_jiffies(50))) { 8298c2ecf20Sopenharmony_ci priv->report_press = true; 8308c2ecf20Sopenharmony_ci } 8318c2ecf20Sopenharmony_ci } else { 8328c2ecf20Sopenharmony_ci priv->press = false; 8338c2ecf20Sopenharmony_ci } 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci hw->left = priv->report_press; 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci } else if (SYN_CAP_CLICKPAD(priv->info.ext_cap_0c)) { 8388c2ecf20Sopenharmony_ci /* 8398c2ecf20Sopenharmony_ci * Clickpad's button is transmitted as middle button, 8408c2ecf20Sopenharmony_ci * however, since it is primary button, we will report 8418c2ecf20Sopenharmony_ci * it as BTN_LEFT. 8428c2ecf20Sopenharmony_ci */ 8438c2ecf20Sopenharmony_ci hw->left = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0; 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci } else if (SYN_CAP_MIDDLE_BUTTON(priv->info.capabilities)) { 8468c2ecf20Sopenharmony_ci hw->middle = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0; 8478c2ecf20Sopenharmony_ci if (hw->w == 2) 8488c2ecf20Sopenharmony_ci hw->scroll = (s8)buf[1]; 8498c2ecf20Sopenharmony_ci } 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci if (SYN_CAP_FOUR_BUTTON(priv->info.capabilities)) { 8528c2ecf20Sopenharmony_ci hw->up = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0; 8538c2ecf20Sopenharmony_ci hw->down = ((buf[0] ^ buf[3]) & 0x02) ? 1 : 0; 8548c2ecf20Sopenharmony_ci } 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci if (SYN_CAP_MULTI_BUTTON_NO(priv->info.ext_cap) > 0 && 8578c2ecf20Sopenharmony_ci ((buf[0] ^ buf[3]) & 0x02)) { 8588c2ecf20Sopenharmony_ci synaptics_parse_ext_buttons(buf, priv, hw); 8598c2ecf20Sopenharmony_ci } 8608c2ecf20Sopenharmony_ci } else { 8618c2ecf20Sopenharmony_ci hw->x = (((buf[1] & 0x1f) << 8) | buf[2]); 8628c2ecf20Sopenharmony_ci hw->y = (((buf[4] & 0x1f) << 8) | buf[5]); 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci hw->z = (((buf[0] & 0x30) << 2) | (buf[3] & 0x3F)); 8658c2ecf20Sopenharmony_ci hw->w = (((buf[1] & 0x80) >> 4) | ((buf[0] & 0x04) >> 1)); 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci hw->left = (buf[0] & 0x01) ? 1 : 0; 8688c2ecf20Sopenharmony_ci hw->right = (buf[0] & 0x02) ? 1 : 0; 8698c2ecf20Sopenharmony_ci } 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci /* 8728c2ecf20Sopenharmony_ci * Convert wrap-around values to negative. (X|Y)_MAX_POSITIVE 8738c2ecf20Sopenharmony_ci * is used by some firmware to indicate a finger at the edge of 8748c2ecf20Sopenharmony_ci * the touchpad whose precise position cannot be determined, so 8758c2ecf20Sopenharmony_ci * convert these values to the maximum axis value. 8768c2ecf20Sopenharmony_ci */ 8778c2ecf20Sopenharmony_ci if (hw->x > X_MAX_POSITIVE) 8788c2ecf20Sopenharmony_ci hw->x -= 1 << ABS_POS_BITS; 8798c2ecf20Sopenharmony_ci else if (hw->x == X_MAX_POSITIVE) 8808c2ecf20Sopenharmony_ci hw->x = XMAX; 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci if (hw->y > Y_MAX_POSITIVE) 8838c2ecf20Sopenharmony_ci hw->y -= 1 << ABS_POS_BITS; 8848c2ecf20Sopenharmony_ci else if (hw->y == Y_MAX_POSITIVE) 8858c2ecf20Sopenharmony_ci hw->y = YMAX; 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci return 0; 8888c2ecf20Sopenharmony_ci} 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_cistatic void synaptics_report_semi_mt_slot(struct input_dev *dev, int slot, 8918c2ecf20Sopenharmony_ci bool active, int x, int y) 8928c2ecf20Sopenharmony_ci{ 8938c2ecf20Sopenharmony_ci input_mt_slot(dev, slot); 8948c2ecf20Sopenharmony_ci input_mt_report_slot_state(dev, MT_TOOL_FINGER, active); 8958c2ecf20Sopenharmony_ci if (active) { 8968c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_MT_POSITION_X, x); 8978c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_MT_POSITION_Y, synaptics_invert_y(y)); 8988c2ecf20Sopenharmony_ci } 8998c2ecf20Sopenharmony_ci} 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_cistatic void synaptics_report_semi_mt_data(struct input_dev *dev, 9028c2ecf20Sopenharmony_ci const struct synaptics_hw_state *a, 9038c2ecf20Sopenharmony_ci const struct synaptics_hw_state *b, 9048c2ecf20Sopenharmony_ci int num_fingers) 9058c2ecf20Sopenharmony_ci{ 9068c2ecf20Sopenharmony_ci if (num_fingers >= 2) { 9078c2ecf20Sopenharmony_ci synaptics_report_semi_mt_slot(dev, 0, true, min(a->x, b->x), 9088c2ecf20Sopenharmony_ci min(a->y, b->y)); 9098c2ecf20Sopenharmony_ci synaptics_report_semi_mt_slot(dev, 1, true, max(a->x, b->x), 9108c2ecf20Sopenharmony_ci max(a->y, b->y)); 9118c2ecf20Sopenharmony_ci } else if (num_fingers == 1) { 9128c2ecf20Sopenharmony_ci synaptics_report_semi_mt_slot(dev, 0, true, a->x, a->y); 9138c2ecf20Sopenharmony_ci synaptics_report_semi_mt_slot(dev, 1, false, 0, 0); 9148c2ecf20Sopenharmony_ci } else { 9158c2ecf20Sopenharmony_ci synaptics_report_semi_mt_slot(dev, 0, false, 0, 0); 9168c2ecf20Sopenharmony_ci synaptics_report_semi_mt_slot(dev, 1, false, 0, 0); 9178c2ecf20Sopenharmony_ci } 9188c2ecf20Sopenharmony_ci} 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_cistatic void synaptics_report_ext_buttons(struct psmouse *psmouse, 9218c2ecf20Sopenharmony_ci const struct synaptics_hw_state *hw) 9228c2ecf20Sopenharmony_ci{ 9238c2ecf20Sopenharmony_ci struct input_dev *dev = psmouse->dev; 9248c2ecf20Sopenharmony_ci struct synaptics_data *priv = psmouse->private; 9258c2ecf20Sopenharmony_ci int ext_bits = (SYN_CAP_MULTI_BUTTON_NO(priv->info.ext_cap) + 1) >> 1; 9268c2ecf20Sopenharmony_ci int i; 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci if (!SYN_CAP_MULTI_BUTTON_NO(priv->info.ext_cap)) 9298c2ecf20Sopenharmony_ci return; 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci /* Bug in FW 8.1 & 8.2, buttons are reported only when ExtBit is 1 */ 9328c2ecf20Sopenharmony_ci if ((SYN_ID_FULL(priv->info.identity) == 0x801 || 9338c2ecf20Sopenharmony_ci SYN_ID_FULL(priv->info.identity) == 0x802) && 9348c2ecf20Sopenharmony_ci !((psmouse->packet[0] ^ psmouse->packet[3]) & 0x02)) 9358c2ecf20Sopenharmony_ci return; 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci if (!SYN_CAP_EXT_BUTTONS_STICK(priv->info.ext_cap_10)) { 9388c2ecf20Sopenharmony_ci for (i = 0; i < ext_bits; i++) { 9398c2ecf20Sopenharmony_ci input_report_key(dev, BTN_0 + 2 * i, 9408c2ecf20Sopenharmony_ci hw->ext_buttons & BIT(i)); 9418c2ecf20Sopenharmony_ci input_report_key(dev, BTN_1 + 2 * i, 9428c2ecf20Sopenharmony_ci hw->ext_buttons & BIT(i + ext_bits)); 9438c2ecf20Sopenharmony_ci } 9448c2ecf20Sopenharmony_ci return; 9458c2ecf20Sopenharmony_ci } 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci /* 9488c2ecf20Sopenharmony_ci * This generation of touchpads has the trackstick buttons 9498c2ecf20Sopenharmony_ci * physically wired to the touchpad. Re-route them through 9508c2ecf20Sopenharmony_ci * the pass-through interface. 9518c2ecf20Sopenharmony_ci */ 9528c2ecf20Sopenharmony_ci if (priv->pt_port) { 9538c2ecf20Sopenharmony_ci u8 pt_buttons; 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci /* The trackstick expects at most 3 buttons */ 9568c2ecf20Sopenharmony_ci pt_buttons = SYN_EXT_BUTTON_STICK_L(hw->ext_buttons) | 9578c2ecf20Sopenharmony_ci SYN_EXT_BUTTON_STICK_R(hw->ext_buttons) << 1 | 9588c2ecf20Sopenharmony_ci SYN_EXT_BUTTON_STICK_M(hw->ext_buttons) << 2; 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci serio_interrupt(priv->pt_port, 9618c2ecf20Sopenharmony_ci PSMOUSE_OOB_EXTRA_BTNS, SERIO_OOB_DATA); 9628c2ecf20Sopenharmony_ci serio_interrupt(priv->pt_port, pt_buttons, SERIO_OOB_DATA); 9638c2ecf20Sopenharmony_ci } 9648c2ecf20Sopenharmony_ci} 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_cistatic void synaptics_report_buttons(struct psmouse *psmouse, 9678c2ecf20Sopenharmony_ci const struct synaptics_hw_state *hw) 9688c2ecf20Sopenharmony_ci{ 9698c2ecf20Sopenharmony_ci struct input_dev *dev = psmouse->dev; 9708c2ecf20Sopenharmony_ci struct synaptics_data *priv = psmouse->private; 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci input_report_key(dev, BTN_LEFT, hw->left); 9738c2ecf20Sopenharmony_ci input_report_key(dev, BTN_RIGHT, hw->right); 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci if (SYN_CAP_MIDDLE_BUTTON(priv->info.capabilities)) 9768c2ecf20Sopenharmony_ci input_report_key(dev, BTN_MIDDLE, hw->middle); 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci if (SYN_CAP_FOUR_BUTTON(priv->info.capabilities)) { 9798c2ecf20Sopenharmony_ci input_report_key(dev, BTN_FORWARD, hw->up); 9808c2ecf20Sopenharmony_ci input_report_key(dev, BTN_BACK, hw->down); 9818c2ecf20Sopenharmony_ci } 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci synaptics_report_ext_buttons(psmouse, hw); 9848c2ecf20Sopenharmony_ci} 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_cistatic void synaptics_report_mt_data(struct psmouse *psmouse, 9878c2ecf20Sopenharmony_ci const struct synaptics_hw_state *sgm, 9888c2ecf20Sopenharmony_ci int num_fingers) 9898c2ecf20Sopenharmony_ci{ 9908c2ecf20Sopenharmony_ci struct input_dev *dev = psmouse->dev; 9918c2ecf20Sopenharmony_ci struct synaptics_data *priv = psmouse->private; 9928c2ecf20Sopenharmony_ci const struct synaptics_hw_state *hw[2] = { sgm, &priv->agm }; 9938c2ecf20Sopenharmony_ci struct input_mt_pos pos[2]; 9948c2ecf20Sopenharmony_ci int slot[2], nsemi, i; 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci nsemi = clamp_val(num_fingers, 0, 2); 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci for (i = 0; i < nsemi; i++) { 9998c2ecf20Sopenharmony_ci pos[i].x = hw[i]->x; 10008c2ecf20Sopenharmony_ci pos[i].y = synaptics_invert_y(hw[i]->y); 10018c2ecf20Sopenharmony_ci } 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci input_mt_assign_slots(dev, slot, pos, nsemi, DMAX * priv->info.x_res); 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci for (i = 0; i < nsemi; i++) { 10068c2ecf20Sopenharmony_ci input_mt_slot(dev, slot[i]); 10078c2ecf20Sopenharmony_ci input_mt_report_slot_state(dev, MT_TOOL_FINGER, true); 10088c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_MT_POSITION_X, pos[i].x); 10098c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_MT_POSITION_Y, pos[i].y); 10108c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_MT_PRESSURE, hw[i]->z); 10118c2ecf20Sopenharmony_ci } 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci input_mt_drop_unused(dev); 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci /* Don't use active slot count to generate BTN_TOOL events. */ 10168c2ecf20Sopenharmony_ci input_mt_report_pointer_emulation(dev, false); 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci /* Send the number of fingers reported by touchpad itself. */ 10198c2ecf20Sopenharmony_ci input_mt_report_finger_count(dev, num_fingers); 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci synaptics_report_buttons(psmouse, sgm); 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_ci input_sync(dev); 10248c2ecf20Sopenharmony_ci} 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_cistatic void synaptics_image_sensor_process(struct psmouse *psmouse, 10278c2ecf20Sopenharmony_ci struct synaptics_hw_state *sgm) 10288c2ecf20Sopenharmony_ci{ 10298c2ecf20Sopenharmony_ci struct synaptics_data *priv = psmouse->private; 10308c2ecf20Sopenharmony_ci int num_fingers; 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci /* 10338c2ecf20Sopenharmony_ci * Update mt_state using the new finger count and current mt_state. 10348c2ecf20Sopenharmony_ci */ 10358c2ecf20Sopenharmony_ci if (sgm->z == 0) 10368c2ecf20Sopenharmony_ci num_fingers = 0; 10378c2ecf20Sopenharmony_ci else if (sgm->w >= 4) 10388c2ecf20Sopenharmony_ci num_fingers = 1; 10398c2ecf20Sopenharmony_ci else if (sgm->w == 0) 10408c2ecf20Sopenharmony_ci num_fingers = 2; 10418c2ecf20Sopenharmony_ci else if (sgm->w == 1) 10428c2ecf20Sopenharmony_ci num_fingers = priv->agm_count ? priv->agm_count : 3; 10438c2ecf20Sopenharmony_ci else 10448c2ecf20Sopenharmony_ci num_fingers = 4; 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci /* Send resulting input events to user space */ 10478c2ecf20Sopenharmony_ci synaptics_report_mt_data(psmouse, sgm, num_fingers); 10488c2ecf20Sopenharmony_ci} 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_cistatic bool synaptics_has_multifinger(struct synaptics_data *priv) 10518c2ecf20Sopenharmony_ci{ 10528c2ecf20Sopenharmony_ci if (SYN_CAP_MULTIFINGER(priv->info.capabilities)) 10538c2ecf20Sopenharmony_ci return true; 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci /* Advanced gesture mode also sends multi finger data */ 10568c2ecf20Sopenharmony_ci return synaptics_has_agm(priv); 10578c2ecf20Sopenharmony_ci} 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci/* 10608c2ecf20Sopenharmony_ci * called for each full received packet from the touchpad 10618c2ecf20Sopenharmony_ci */ 10628c2ecf20Sopenharmony_cistatic void synaptics_process_packet(struct psmouse *psmouse) 10638c2ecf20Sopenharmony_ci{ 10648c2ecf20Sopenharmony_ci struct input_dev *dev = psmouse->dev; 10658c2ecf20Sopenharmony_ci struct synaptics_data *priv = psmouse->private; 10668c2ecf20Sopenharmony_ci struct synaptics_device_info *info = &priv->info; 10678c2ecf20Sopenharmony_ci struct synaptics_hw_state hw; 10688c2ecf20Sopenharmony_ci int num_fingers; 10698c2ecf20Sopenharmony_ci int finger_width; 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_ci if (synaptics_parse_hw_state(psmouse->packet, priv, &hw)) 10728c2ecf20Sopenharmony_ci return; 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci if (SYN_CAP_IMAGE_SENSOR(info->ext_cap_0c)) { 10758c2ecf20Sopenharmony_ci synaptics_image_sensor_process(psmouse, &hw); 10768c2ecf20Sopenharmony_ci return; 10778c2ecf20Sopenharmony_ci } 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci if (hw.scroll) { 10808c2ecf20Sopenharmony_ci priv->scroll += hw.scroll; 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci while (priv->scroll >= 4) { 10838c2ecf20Sopenharmony_ci input_report_key(dev, BTN_BACK, !hw.down); 10848c2ecf20Sopenharmony_ci input_sync(dev); 10858c2ecf20Sopenharmony_ci input_report_key(dev, BTN_BACK, hw.down); 10868c2ecf20Sopenharmony_ci input_sync(dev); 10878c2ecf20Sopenharmony_ci priv->scroll -= 4; 10888c2ecf20Sopenharmony_ci } 10898c2ecf20Sopenharmony_ci while (priv->scroll <= -4) { 10908c2ecf20Sopenharmony_ci input_report_key(dev, BTN_FORWARD, !hw.up); 10918c2ecf20Sopenharmony_ci input_sync(dev); 10928c2ecf20Sopenharmony_ci input_report_key(dev, BTN_FORWARD, hw.up); 10938c2ecf20Sopenharmony_ci input_sync(dev); 10948c2ecf20Sopenharmony_ci priv->scroll += 4; 10958c2ecf20Sopenharmony_ci } 10968c2ecf20Sopenharmony_ci return; 10978c2ecf20Sopenharmony_ci } 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci if (hw.z > 0 && hw.x > 1) { 11008c2ecf20Sopenharmony_ci num_fingers = 1; 11018c2ecf20Sopenharmony_ci finger_width = 5; 11028c2ecf20Sopenharmony_ci if (SYN_CAP_EXTENDED(info->capabilities)) { 11038c2ecf20Sopenharmony_ci switch (hw.w) { 11048c2ecf20Sopenharmony_ci case 0 ... 1: 11058c2ecf20Sopenharmony_ci if (synaptics_has_multifinger(priv)) 11068c2ecf20Sopenharmony_ci num_fingers = hw.w + 2; 11078c2ecf20Sopenharmony_ci break; 11088c2ecf20Sopenharmony_ci case 2: 11098c2ecf20Sopenharmony_ci if (SYN_MODEL_PEN(info->model_id)) 11108c2ecf20Sopenharmony_ci ; /* Nothing, treat a pen as a single finger */ 11118c2ecf20Sopenharmony_ci break; 11128c2ecf20Sopenharmony_ci case 4 ... 15: 11138c2ecf20Sopenharmony_ci if (SYN_CAP_PALMDETECT(info->capabilities)) 11148c2ecf20Sopenharmony_ci finger_width = hw.w; 11158c2ecf20Sopenharmony_ci break; 11168c2ecf20Sopenharmony_ci } 11178c2ecf20Sopenharmony_ci } 11188c2ecf20Sopenharmony_ci } else { 11198c2ecf20Sopenharmony_ci num_fingers = 0; 11208c2ecf20Sopenharmony_ci finger_width = 0; 11218c2ecf20Sopenharmony_ci } 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_ci if (cr48_profile_sensor) { 11248c2ecf20Sopenharmony_ci synaptics_report_mt_data(psmouse, &hw, num_fingers); 11258c2ecf20Sopenharmony_ci return; 11268c2ecf20Sopenharmony_ci } 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci if (SYN_CAP_ADV_GESTURE(info->ext_cap_0c)) 11298c2ecf20Sopenharmony_ci synaptics_report_semi_mt_data(dev, &hw, &priv->agm, 11308c2ecf20Sopenharmony_ci num_fingers); 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci /* Post events 11338c2ecf20Sopenharmony_ci * BTN_TOUCH has to be first as mousedev relies on it when doing 11348c2ecf20Sopenharmony_ci * absolute -> relative conversion 11358c2ecf20Sopenharmony_ci */ 11368c2ecf20Sopenharmony_ci if (hw.z > 30) input_report_key(dev, BTN_TOUCH, 1); 11378c2ecf20Sopenharmony_ci if (hw.z < 25) input_report_key(dev, BTN_TOUCH, 0); 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci if (num_fingers > 0) { 11408c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_X, hw.x); 11418c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_Y, synaptics_invert_y(hw.y)); 11428c2ecf20Sopenharmony_ci } 11438c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_PRESSURE, hw.z); 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci if (SYN_CAP_PALMDETECT(info->capabilities)) 11468c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_TOOL_WIDTH, finger_width); 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ci input_report_key(dev, BTN_TOOL_FINGER, num_fingers == 1); 11498c2ecf20Sopenharmony_ci if (synaptics_has_multifinger(priv)) { 11508c2ecf20Sopenharmony_ci input_report_key(dev, BTN_TOOL_DOUBLETAP, num_fingers == 2); 11518c2ecf20Sopenharmony_ci input_report_key(dev, BTN_TOOL_TRIPLETAP, num_fingers == 3); 11528c2ecf20Sopenharmony_ci } 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci synaptics_report_buttons(psmouse, &hw); 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_ci input_sync(dev); 11578c2ecf20Sopenharmony_ci} 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_cistatic bool synaptics_validate_byte(struct psmouse *psmouse, 11608c2ecf20Sopenharmony_ci int idx, enum synaptics_pkt_type pkt_type) 11618c2ecf20Sopenharmony_ci{ 11628c2ecf20Sopenharmony_ci static const u8 newabs_mask[] = { 0xC8, 0x00, 0x00, 0xC8, 0x00 }; 11638c2ecf20Sopenharmony_ci static const u8 newabs_rel_mask[] = { 0xC0, 0x00, 0x00, 0xC0, 0x00 }; 11648c2ecf20Sopenharmony_ci static const u8 newabs_rslt[] = { 0x80, 0x00, 0x00, 0xC0, 0x00 }; 11658c2ecf20Sopenharmony_ci static const u8 oldabs_mask[] = { 0xC0, 0x60, 0x00, 0xC0, 0x60 }; 11668c2ecf20Sopenharmony_ci static const u8 oldabs_rslt[] = { 0xC0, 0x00, 0x00, 0x80, 0x00 }; 11678c2ecf20Sopenharmony_ci const u8 *packet = psmouse->packet; 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci if (idx < 0 || idx > 4) 11708c2ecf20Sopenharmony_ci return false; 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci switch (pkt_type) { 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci case SYN_NEWABS: 11758c2ecf20Sopenharmony_ci case SYN_NEWABS_RELAXED: 11768c2ecf20Sopenharmony_ci return (packet[idx] & newabs_rel_mask[idx]) == newabs_rslt[idx]; 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci case SYN_NEWABS_STRICT: 11798c2ecf20Sopenharmony_ci return (packet[idx] & newabs_mask[idx]) == newabs_rslt[idx]; 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_ci case SYN_OLDABS: 11828c2ecf20Sopenharmony_ci return (packet[idx] & oldabs_mask[idx]) == oldabs_rslt[idx]; 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci default: 11858c2ecf20Sopenharmony_ci psmouse_err(psmouse, "unknown packet type %d\n", pkt_type); 11868c2ecf20Sopenharmony_ci return false; 11878c2ecf20Sopenharmony_ci } 11888c2ecf20Sopenharmony_ci} 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_cistatic enum synaptics_pkt_type 11918c2ecf20Sopenharmony_cisynaptics_detect_pkt_type(struct psmouse *psmouse) 11928c2ecf20Sopenharmony_ci{ 11938c2ecf20Sopenharmony_ci int i; 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_ci for (i = 0; i < 5; i++) { 11968c2ecf20Sopenharmony_ci if (!synaptics_validate_byte(psmouse, i, SYN_NEWABS_STRICT)) { 11978c2ecf20Sopenharmony_ci psmouse_info(psmouse, "using relaxed packet validation\n"); 11988c2ecf20Sopenharmony_ci return SYN_NEWABS_RELAXED; 11998c2ecf20Sopenharmony_ci } 12008c2ecf20Sopenharmony_ci } 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_ci return SYN_NEWABS_STRICT; 12038c2ecf20Sopenharmony_ci} 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_cistatic psmouse_ret_t synaptics_process_byte(struct psmouse *psmouse) 12068c2ecf20Sopenharmony_ci{ 12078c2ecf20Sopenharmony_ci struct synaptics_data *priv = psmouse->private; 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_ci if (psmouse->pktcnt >= 6) { /* Full packet received */ 12108c2ecf20Sopenharmony_ci if (unlikely(priv->pkt_type == SYN_NEWABS)) 12118c2ecf20Sopenharmony_ci priv->pkt_type = synaptics_detect_pkt_type(psmouse); 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_ci if (SYN_CAP_PASS_THROUGH(priv->info.capabilities) && 12148c2ecf20Sopenharmony_ci synaptics_is_pt_packet(psmouse->packet)) { 12158c2ecf20Sopenharmony_ci if (priv->pt_port) 12168c2ecf20Sopenharmony_ci synaptics_pass_pt_packet(priv->pt_port, 12178c2ecf20Sopenharmony_ci psmouse->packet); 12188c2ecf20Sopenharmony_ci } else 12198c2ecf20Sopenharmony_ci synaptics_process_packet(psmouse); 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_ci return PSMOUSE_FULL_PACKET; 12228c2ecf20Sopenharmony_ci } 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci return synaptics_validate_byte(psmouse, psmouse->pktcnt - 1, priv->pkt_type) ? 12258c2ecf20Sopenharmony_ci PSMOUSE_GOOD_DATA : PSMOUSE_BAD_DATA; 12268c2ecf20Sopenharmony_ci} 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci/***************************************************************************** 12298c2ecf20Sopenharmony_ci * Driver initialization/cleanup functions 12308c2ecf20Sopenharmony_ci ****************************************************************************/ 12318c2ecf20Sopenharmony_cistatic void set_abs_position_params(struct input_dev *dev, 12328c2ecf20Sopenharmony_ci struct synaptics_device_info *info, 12338c2ecf20Sopenharmony_ci int x_code, int y_code) 12348c2ecf20Sopenharmony_ci{ 12358c2ecf20Sopenharmony_ci int x_min = info->x_min ?: XMIN_NOMINAL; 12368c2ecf20Sopenharmony_ci int x_max = info->x_max ?: XMAX_NOMINAL; 12378c2ecf20Sopenharmony_ci int y_min = info->y_min ?: YMIN_NOMINAL; 12388c2ecf20Sopenharmony_ci int y_max = info->y_max ?: YMAX_NOMINAL; 12398c2ecf20Sopenharmony_ci int fuzz = SYN_CAP_REDUCED_FILTERING(info->ext_cap_0c) ? 12408c2ecf20Sopenharmony_ci SYN_REDUCED_FILTER_FUZZ : 0; 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_ci input_set_abs_params(dev, x_code, x_min, x_max, fuzz, 0); 12438c2ecf20Sopenharmony_ci input_set_abs_params(dev, y_code, y_min, y_max, fuzz, 0); 12448c2ecf20Sopenharmony_ci input_abs_set_res(dev, x_code, info->x_res); 12458c2ecf20Sopenharmony_ci input_abs_set_res(dev, y_code, info->y_res); 12468c2ecf20Sopenharmony_ci} 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_cistatic int set_input_params(struct psmouse *psmouse, 12498c2ecf20Sopenharmony_ci struct synaptics_data *priv) 12508c2ecf20Sopenharmony_ci{ 12518c2ecf20Sopenharmony_ci struct input_dev *dev = psmouse->dev; 12528c2ecf20Sopenharmony_ci struct synaptics_device_info *info = &priv->info; 12538c2ecf20Sopenharmony_ci int i; 12548c2ecf20Sopenharmony_ci int error; 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci /* Reset default psmouse capabilities */ 12578c2ecf20Sopenharmony_ci __clear_bit(EV_REL, dev->evbit); 12588c2ecf20Sopenharmony_ci bitmap_zero(dev->relbit, REL_CNT); 12598c2ecf20Sopenharmony_ci bitmap_zero(dev->keybit, KEY_CNT); 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ci /* Things that apply to both modes */ 12628c2ecf20Sopenharmony_ci __set_bit(INPUT_PROP_POINTER, dev->propbit); 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci input_set_capability(dev, EV_KEY, BTN_LEFT); 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci /* Clickpads report only left button */ 12678c2ecf20Sopenharmony_ci if (!SYN_CAP_CLICKPAD(info->ext_cap_0c)) { 12688c2ecf20Sopenharmony_ci input_set_capability(dev, EV_KEY, BTN_RIGHT); 12698c2ecf20Sopenharmony_ci if (SYN_CAP_MIDDLE_BUTTON(info->capabilities)) 12708c2ecf20Sopenharmony_ci input_set_capability(dev, EV_KEY, BTN_MIDDLE); 12718c2ecf20Sopenharmony_ci } 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_ci if (!priv->absolute_mode) { 12748c2ecf20Sopenharmony_ci /* Relative mode */ 12758c2ecf20Sopenharmony_ci input_set_capability(dev, EV_REL, REL_X); 12768c2ecf20Sopenharmony_ci input_set_capability(dev, EV_REL, REL_Y); 12778c2ecf20Sopenharmony_ci return 0; 12788c2ecf20Sopenharmony_ci } 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_ci /* Absolute mode */ 12818c2ecf20Sopenharmony_ci set_abs_position_params(dev, &priv->info, ABS_X, ABS_Y); 12828c2ecf20Sopenharmony_ci input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0); 12838c2ecf20Sopenharmony_ci 12848c2ecf20Sopenharmony_ci if (cr48_profile_sensor) 12858c2ecf20Sopenharmony_ci input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 255, 0, 0); 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_ci if (SYN_CAP_IMAGE_SENSOR(info->ext_cap_0c)) { 12888c2ecf20Sopenharmony_ci set_abs_position_params(dev, info, 12898c2ecf20Sopenharmony_ci ABS_MT_POSITION_X, ABS_MT_POSITION_Y); 12908c2ecf20Sopenharmony_ci /* Image sensors can report per-contact pressure */ 12918c2ecf20Sopenharmony_ci input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 255, 0, 0); 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci error = input_mt_init_slots(dev, 2, 12948c2ecf20Sopenharmony_ci INPUT_MT_POINTER | INPUT_MT_TRACK); 12958c2ecf20Sopenharmony_ci if (error) 12968c2ecf20Sopenharmony_ci return error; 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci /* Image sensors can signal 4 and 5 finger clicks */ 12998c2ecf20Sopenharmony_ci input_set_capability(dev, EV_KEY, BTN_TOOL_QUADTAP); 13008c2ecf20Sopenharmony_ci input_set_capability(dev, EV_KEY, BTN_TOOL_QUINTTAP); 13018c2ecf20Sopenharmony_ci } else if (SYN_CAP_ADV_GESTURE(info->ext_cap_0c)) { 13028c2ecf20Sopenharmony_ci set_abs_position_params(dev, info, 13038c2ecf20Sopenharmony_ci ABS_MT_POSITION_X, ABS_MT_POSITION_Y); 13048c2ecf20Sopenharmony_ci /* 13058c2ecf20Sopenharmony_ci * Profile sensor in CR-48 tracks contacts reasonably well, 13068c2ecf20Sopenharmony_ci * other non-image sensors with AGM use semi-mt. 13078c2ecf20Sopenharmony_ci */ 13088c2ecf20Sopenharmony_ci error = input_mt_init_slots(dev, 2, 13098c2ecf20Sopenharmony_ci INPUT_MT_POINTER | 13108c2ecf20Sopenharmony_ci (cr48_profile_sensor ? 13118c2ecf20Sopenharmony_ci INPUT_MT_TRACK : 13128c2ecf20Sopenharmony_ci INPUT_MT_SEMI_MT)); 13138c2ecf20Sopenharmony_ci if (error) 13148c2ecf20Sopenharmony_ci return error; 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_ci /* 13178c2ecf20Sopenharmony_ci * For semi-mt devices we send ABS_X/Y ourselves instead of 13188c2ecf20Sopenharmony_ci * input_mt_report_pointer_emulation. But 13198c2ecf20Sopenharmony_ci * input_mt_init_slots() resets the fuzz to 0, leading to a 13208c2ecf20Sopenharmony_ci * filtered ABS_MT_POSITION_X but an unfiltered ABS_X 13218c2ecf20Sopenharmony_ci * position. Let's re-initialize ABS_X/Y here. 13228c2ecf20Sopenharmony_ci */ 13238c2ecf20Sopenharmony_ci if (!cr48_profile_sensor) 13248c2ecf20Sopenharmony_ci set_abs_position_params(dev, &priv->info, ABS_X, ABS_Y); 13258c2ecf20Sopenharmony_ci } 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_ci if (SYN_CAP_PALMDETECT(info->capabilities)) 13288c2ecf20Sopenharmony_ci input_set_abs_params(dev, ABS_TOOL_WIDTH, 0, 15, 0, 0); 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_ci input_set_capability(dev, EV_KEY, BTN_TOUCH); 13318c2ecf20Sopenharmony_ci input_set_capability(dev, EV_KEY, BTN_TOOL_FINGER); 13328c2ecf20Sopenharmony_ci 13338c2ecf20Sopenharmony_ci if (synaptics_has_multifinger(priv)) { 13348c2ecf20Sopenharmony_ci input_set_capability(dev, EV_KEY, BTN_TOOL_DOUBLETAP); 13358c2ecf20Sopenharmony_ci input_set_capability(dev, EV_KEY, BTN_TOOL_TRIPLETAP); 13368c2ecf20Sopenharmony_ci } 13378c2ecf20Sopenharmony_ci 13388c2ecf20Sopenharmony_ci if (SYN_CAP_FOUR_BUTTON(info->capabilities) || 13398c2ecf20Sopenharmony_ci SYN_CAP_MIDDLE_BUTTON(info->capabilities)) { 13408c2ecf20Sopenharmony_ci input_set_capability(dev, EV_KEY, BTN_FORWARD); 13418c2ecf20Sopenharmony_ci input_set_capability(dev, EV_KEY, BTN_BACK); 13428c2ecf20Sopenharmony_ci } 13438c2ecf20Sopenharmony_ci 13448c2ecf20Sopenharmony_ci if (!SYN_CAP_EXT_BUTTONS_STICK(info->ext_cap_10)) 13458c2ecf20Sopenharmony_ci for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(info->ext_cap); i++) 13468c2ecf20Sopenharmony_ci input_set_capability(dev, EV_KEY, BTN_0 + i); 13478c2ecf20Sopenharmony_ci 13488c2ecf20Sopenharmony_ci if (SYN_CAP_CLICKPAD(info->ext_cap_0c)) { 13498c2ecf20Sopenharmony_ci __set_bit(INPUT_PROP_BUTTONPAD, dev->propbit); 13508c2ecf20Sopenharmony_ci if (psmouse_matches_pnp_id(psmouse, topbuttonpad_pnp_ids) && 13518c2ecf20Sopenharmony_ci !SYN_CAP_EXT_BUTTONS_STICK(info->ext_cap_10)) 13528c2ecf20Sopenharmony_ci __set_bit(INPUT_PROP_TOPBUTTONPAD, dev->propbit); 13538c2ecf20Sopenharmony_ci } 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_ci return 0; 13568c2ecf20Sopenharmony_ci} 13578c2ecf20Sopenharmony_ci 13588c2ecf20Sopenharmony_cistatic ssize_t synaptics_show_disable_gesture(struct psmouse *psmouse, 13598c2ecf20Sopenharmony_ci void *data, char *buf) 13608c2ecf20Sopenharmony_ci{ 13618c2ecf20Sopenharmony_ci struct synaptics_data *priv = psmouse->private; 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_ci return sprintf(buf, "%c\n", priv->disable_gesture ? '1' : '0'); 13648c2ecf20Sopenharmony_ci} 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_cistatic ssize_t synaptics_set_disable_gesture(struct psmouse *psmouse, 13678c2ecf20Sopenharmony_ci void *data, const char *buf, 13688c2ecf20Sopenharmony_ci size_t len) 13698c2ecf20Sopenharmony_ci{ 13708c2ecf20Sopenharmony_ci struct synaptics_data *priv = psmouse->private; 13718c2ecf20Sopenharmony_ci unsigned int value; 13728c2ecf20Sopenharmony_ci int err; 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_ci err = kstrtouint(buf, 10, &value); 13758c2ecf20Sopenharmony_ci if (err) 13768c2ecf20Sopenharmony_ci return err; 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_ci if (value > 1) 13798c2ecf20Sopenharmony_ci return -EINVAL; 13808c2ecf20Sopenharmony_ci 13818c2ecf20Sopenharmony_ci if (value == priv->disable_gesture) 13828c2ecf20Sopenharmony_ci return len; 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_ci priv->disable_gesture = value; 13858c2ecf20Sopenharmony_ci if (value) 13868c2ecf20Sopenharmony_ci priv->mode |= SYN_BIT_DISABLE_GESTURE; 13878c2ecf20Sopenharmony_ci else 13888c2ecf20Sopenharmony_ci priv->mode &= ~SYN_BIT_DISABLE_GESTURE; 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_ci if (synaptics_mode_cmd(psmouse, priv->mode)) 13918c2ecf20Sopenharmony_ci return -EIO; 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_ci return len; 13948c2ecf20Sopenharmony_ci} 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_ciPSMOUSE_DEFINE_ATTR(disable_gesture, S_IWUSR | S_IRUGO, NULL, 13978c2ecf20Sopenharmony_ci synaptics_show_disable_gesture, 13988c2ecf20Sopenharmony_ci synaptics_set_disable_gesture); 13998c2ecf20Sopenharmony_ci 14008c2ecf20Sopenharmony_cistatic void synaptics_disconnect(struct psmouse *psmouse) 14018c2ecf20Sopenharmony_ci{ 14028c2ecf20Sopenharmony_ci struct synaptics_data *priv = psmouse->private; 14038c2ecf20Sopenharmony_ci 14048c2ecf20Sopenharmony_ci /* 14058c2ecf20Sopenharmony_ci * We might have left a breadcrumb when trying to 14068c2ecf20Sopenharmony_ci * set up SMbus companion. 14078c2ecf20Sopenharmony_ci */ 14088c2ecf20Sopenharmony_ci psmouse_smbus_cleanup(psmouse); 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_ci if (!priv->absolute_mode && 14118c2ecf20Sopenharmony_ci SYN_ID_DISGEST_SUPPORTED(priv->info.identity)) 14128c2ecf20Sopenharmony_ci device_remove_file(&psmouse->ps2dev.serio->dev, 14138c2ecf20Sopenharmony_ci &psmouse_attr_disable_gesture.dattr); 14148c2ecf20Sopenharmony_ci 14158c2ecf20Sopenharmony_ci synaptics_reset(psmouse); 14168c2ecf20Sopenharmony_ci kfree(priv); 14178c2ecf20Sopenharmony_ci psmouse->private = NULL; 14188c2ecf20Sopenharmony_ci} 14198c2ecf20Sopenharmony_ci 14208c2ecf20Sopenharmony_cistatic int synaptics_reconnect(struct psmouse *psmouse) 14218c2ecf20Sopenharmony_ci{ 14228c2ecf20Sopenharmony_ci struct synaptics_data *priv = psmouse->private; 14238c2ecf20Sopenharmony_ci struct synaptics_device_info info; 14248c2ecf20Sopenharmony_ci u8 param[2]; 14258c2ecf20Sopenharmony_ci int retry = 0; 14268c2ecf20Sopenharmony_ci int error; 14278c2ecf20Sopenharmony_ci 14288c2ecf20Sopenharmony_ci do { 14298c2ecf20Sopenharmony_ci psmouse_reset(psmouse); 14308c2ecf20Sopenharmony_ci if (retry) { 14318c2ecf20Sopenharmony_ci /* 14328c2ecf20Sopenharmony_ci * On some boxes, right after resuming, the touchpad 14338c2ecf20Sopenharmony_ci * needs some time to finish initializing (I assume 14348c2ecf20Sopenharmony_ci * it needs time to calibrate) and start responding 14358c2ecf20Sopenharmony_ci * to Synaptics-specific queries, so let's wait a 14368c2ecf20Sopenharmony_ci * bit. 14378c2ecf20Sopenharmony_ci */ 14388c2ecf20Sopenharmony_ci ssleep(1); 14398c2ecf20Sopenharmony_ci } 14408c2ecf20Sopenharmony_ci ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETID); 14418c2ecf20Sopenharmony_ci error = synaptics_detect(psmouse, 0); 14428c2ecf20Sopenharmony_ci } while (error && ++retry < 3); 14438c2ecf20Sopenharmony_ci 14448c2ecf20Sopenharmony_ci if (error) 14458c2ecf20Sopenharmony_ci return error; 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_ci if (retry > 1) 14488c2ecf20Sopenharmony_ci psmouse_dbg(psmouse, "reconnected after %d tries\n", retry); 14498c2ecf20Sopenharmony_ci 14508c2ecf20Sopenharmony_ci error = synaptics_query_hardware(psmouse, &info); 14518c2ecf20Sopenharmony_ci if (error) { 14528c2ecf20Sopenharmony_ci psmouse_err(psmouse, "Unable to query device.\n"); 14538c2ecf20Sopenharmony_ci return error; 14548c2ecf20Sopenharmony_ci } 14558c2ecf20Sopenharmony_ci 14568c2ecf20Sopenharmony_ci error = synaptics_set_mode(psmouse); 14578c2ecf20Sopenharmony_ci if (error) { 14588c2ecf20Sopenharmony_ci psmouse_err(psmouse, "Unable to initialize device.\n"); 14598c2ecf20Sopenharmony_ci return error; 14608c2ecf20Sopenharmony_ci } 14618c2ecf20Sopenharmony_ci 14628c2ecf20Sopenharmony_ci if (info.identity != priv->info.identity || 14638c2ecf20Sopenharmony_ci info.model_id != priv->info.model_id || 14648c2ecf20Sopenharmony_ci info.capabilities != priv->info.capabilities || 14658c2ecf20Sopenharmony_ci info.ext_cap != priv->info.ext_cap) { 14668c2ecf20Sopenharmony_ci psmouse_err(psmouse, 14678c2ecf20Sopenharmony_ci "hardware appears to be different: id(%u-%u), model(%u-%u), caps(%x-%x), ext(%x-%x).\n", 14688c2ecf20Sopenharmony_ci priv->info.identity, info.identity, 14698c2ecf20Sopenharmony_ci priv->info.model_id, info.model_id, 14708c2ecf20Sopenharmony_ci priv->info.capabilities, info.capabilities, 14718c2ecf20Sopenharmony_ci priv->info.ext_cap, info.ext_cap); 14728c2ecf20Sopenharmony_ci return -ENXIO; 14738c2ecf20Sopenharmony_ci } 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_ci return 0; 14768c2ecf20Sopenharmony_ci} 14778c2ecf20Sopenharmony_ci 14788c2ecf20Sopenharmony_cistatic bool impaired_toshiba_kbc; 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_cistatic const struct dmi_system_id toshiba_dmi_table[] __initconst = { 14818c2ecf20Sopenharmony_ci#if defined(CONFIG_DMI) && defined(CONFIG_X86) 14828c2ecf20Sopenharmony_ci { 14838c2ecf20Sopenharmony_ci /* Toshiba Satellite */ 14848c2ecf20Sopenharmony_ci .matches = { 14858c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), 14868c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "Satellite"), 14878c2ecf20Sopenharmony_ci }, 14888c2ecf20Sopenharmony_ci }, 14898c2ecf20Sopenharmony_ci { 14908c2ecf20Sopenharmony_ci /* Toshiba Dynabook */ 14918c2ecf20Sopenharmony_ci .matches = { 14928c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), 14938c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "dynabook"), 14948c2ecf20Sopenharmony_ci }, 14958c2ecf20Sopenharmony_ci }, 14968c2ecf20Sopenharmony_ci { 14978c2ecf20Sopenharmony_ci /* Toshiba Portege M300 */ 14988c2ecf20Sopenharmony_ci .matches = { 14998c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), 15008c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "PORTEGE M300"), 15018c2ecf20Sopenharmony_ci }, 15028c2ecf20Sopenharmony_ci 15038c2ecf20Sopenharmony_ci }, 15048c2ecf20Sopenharmony_ci { 15058c2ecf20Sopenharmony_ci /* Toshiba Portege M300 */ 15068c2ecf20Sopenharmony_ci .matches = { 15078c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), 15088c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "Portable PC"), 15098c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_VERSION, "Version 1.0"), 15108c2ecf20Sopenharmony_ci }, 15118c2ecf20Sopenharmony_ci 15128c2ecf20Sopenharmony_ci }, 15138c2ecf20Sopenharmony_ci#endif 15148c2ecf20Sopenharmony_ci { } 15158c2ecf20Sopenharmony_ci}; 15168c2ecf20Sopenharmony_ci 15178c2ecf20Sopenharmony_cistatic bool broken_olpc_ec; 15188c2ecf20Sopenharmony_ci 15198c2ecf20Sopenharmony_cistatic const struct dmi_system_id olpc_dmi_table[] __initconst = { 15208c2ecf20Sopenharmony_ci#if defined(CONFIG_DMI) && defined(CONFIG_OLPC) 15218c2ecf20Sopenharmony_ci { 15228c2ecf20Sopenharmony_ci /* OLPC XO-1 or XO-1.5 */ 15238c2ecf20Sopenharmony_ci .matches = { 15248c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "OLPC"), 15258c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "XO"), 15268c2ecf20Sopenharmony_ci }, 15278c2ecf20Sopenharmony_ci }, 15288c2ecf20Sopenharmony_ci#endif 15298c2ecf20Sopenharmony_ci { } 15308c2ecf20Sopenharmony_ci}; 15318c2ecf20Sopenharmony_ci 15328c2ecf20Sopenharmony_cistatic const struct dmi_system_id __initconst cr48_dmi_table[] = { 15338c2ecf20Sopenharmony_ci#if defined(CONFIG_DMI) && defined(CONFIG_X86) 15348c2ecf20Sopenharmony_ci { 15358c2ecf20Sopenharmony_ci /* Cr-48 Chromebook (Codename Mario) */ 15368c2ecf20Sopenharmony_ci .matches = { 15378c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "IEC"), 15388c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "Mario"), 15398c2ecf20Sopenharmony_ci }, 15408c2ecf20Sopenharmony_ci }, 15418c2ecf20Sopenharmony_ci#endif 15428c2ecf20Sopenharmony_ci { } 15438c2ecf20Sopenharmony_ci}; 15448c2ecf20Sopenharmony_ci 15458c2ecf20Sopenharmony_civoid __init synaptics_module_init(void) 15468c2ecf20Sopenharmony_ci{ 15478c2ecf20Sopenharmony_ci impaired_toshiba_kbc = dmi_check_system(toshiba_dmi_table); 15488c2ecf20Sopenharmony_ci broken_olpc_ec = dmi_check_system(olpc_dmi_table); 15498c2ecf20Sopenharmony_ci cr48_profile_sensor = dmi_check_system(cr48_dmi_table); 15508c2ecf20Sopenharmony_ci} 15518c2ecf20Sopenharmony_ci 15528c2ecf20Sopenharmony_cistatic int synaptics_init_ps2(struct psmouse *psmouse, 15538c2ecf20Sopenharmony_ci struct synaptics_device_info *info, 15548c2ecf20Sopenharmony_ci bool absolute_mode) 15558c2ecf20Sopenharmony_ci{ 15568c2ecf20Sopenharmony_ci struct synaptics_data *priv; 15578c2ecf20Sopenharmony_ci int err; 15588c2ecf20Sopenharmony_ci 15598c2ecf20Sopenharmony_ci synaptics_apply_quirks(psmouse, info); 15608c2ecf20Sopenharmony_ci 15618c2ecf20Sopenharmony_ci psmouse->private = priv = kzalloc(sizeof(struct synaptics_data), GFP_KERNEL); 15628c2ecf20Sopenharmony_ci if (!priv) 15638c2ecf20Sopenharmony_ci return -ENOMEM; 15648c2ecf20Sopenharmony_ci 15658c2ecf20Sopenharmony_ci priv->info = *info; 15668c2ecf20Sopenharmony_ci priv->absolute_mode = absolute_mode; 15678c2ecf20Sopenharmony_ci if (SYN_ID_DISGEST_SUPPORTED(info->identity)) 15688c2ecf20Sopenharmony_ci priv->disable_gesture = true; 15698c2ecf20Sopenharmony_ci 15708c2ecf20Sopenharmony_ci /* 15718c2ecf20Sopenharmony_ci * Unfortunately ForcePad capability is not exported over PS/2, 15728c2ecf20Sopenharmony_ci * so we have to resort to checking PNP IDs. 15738c2ecf20Sopenharmony_ci */ 15748c2ecf20Sopenharmony_ci priv->is_forcepad = psmouse_matches_pnp_id(psmouse, forcepad_pnp_ids); 15758c2ecf20Sopenharmony_ci 15768c2ecf20Sopenharmony_ci err = synaptics_set_mode(psmouse); 15778c2ecf20Sopenharmony_ci if (err) { 15788c2ecf20Sopenharmony_ci psmouse_err(psmouse, "Unable to initialize device.\n"); 15798c2ecf20Sopenharmony_ci goto init_fail; 15808c2ecf20Sopenharmony_ci } 15818c2ecf20Sopenharmony_ci 15828c2ecf20Sopenharmony_ci priv->pkt_type = SYN_MODEL_NEWABS(info->model_id) ? 15838c2ecf20Sopenharmony_ci SYN_NEWABS : SYN_OLDABS; 15848c2ecf20Sopenharmony_ci 15858c2ecf20Sopenharmony_ci psmouse_info(psmouse, 15868c2ecf20Sopenharmony_ci "Touchpad model: %lu, fw: %lu.%lu, id: %#x, caps: %#x/%#x/%#x/%#x, board id: %u, fw id: %u\n", 15878c2ecf20Sopenharmony_ci SYN_ID_MODEL(info->identity), 15888c2ecf20Sopenharmony_ci SYN_ID_MAJOR(info->identity), SYN_ID_MINOR(info->identity), 15898c2ecf20Sopenharmony_ci info->model_id, 15908c2ecf20Sopenharmony_ci info->capabilities, info->ext_cap, info->ext_cap_0c, 15918c2ecf20Sopenharmony_ci info->ext_cap_10, info->board_id, info->firmware_id); 15928c2ecf20Sopenharmony_ci 15938c2ecf20Sopenharmony_ci err = set_input_params(psmouse, priv); 15948c2ecf20Sopenharmony_ci if (err) { 15958c2ecf20Sopenharmony_ci psmouse_err(psmouse, 15968c2ecf20Sopenharmony_ci "failed to set up capabilities: %d\n", err); 15978c2ecf20Sopenharmony_ci goto init_fail; 15988c2ecf20Sopenharmony_ci } 15998c2ecf20Sopenharmony_ci 16008c2ecf20Sopenharmony_ci /* 16018c2ecf20Sopenharmony_ci * Encode touchpad model so that it can be used to set 16028c2ecf20Sopenharmony_ci * input device->id.version and be visible to userspace. 16038c2ecf20Sopenharmony_ci * Because version is __u16 we have to drop something. 16048c2ecf20Sopenharmony_ci * Hardware info bits seem to be good candidates as they 16058c2ecf20Sopenharmony_ci * are documented to be for Synaptics corp. internal use. 16068c2ecf20Sopenharmony_ci */ 16078c2ecf20Sopenharmony_ci psmouse->model = ((info->model_id & 0x00ff0000) >> 8) | 16088c2ecf20Sopenharmony_ci (info->model_id & 0x000000ff); 16098c2ecf20Sopenharmony_ci 16108c2ecf20Sopenharmony_ci if (absolute_mode) { 16118c2ecf20Sopenharmony_ci psmouse->protocol_handler = synaptics_process_byte; 16128c2ecf20Sopenharmony_ci psmouse->pktsize = 6; 16138c2ecf20Sopenharmony_ci } else { 16148c2ecf20Sopenharmony_ci /* Relative mode follows standard PS/2 mouse protocol */ 16158c2ecf20Sopenharmony_ci psmouse->protocol_handler = psmouse_process_byte; 16168c2ecf20Sopenharmony_ci psmouse->pktsize = 3; 16178c2ecf20Sopenharmony_ci } 16188c2ecf20Sopenharmony_ci 16198c2ecf20Sopenharmony_ci psmouse->set_rate = synaptics_set_rate; 16208c2ecf20Sopenharmony_ci psmouse->disconnect = synaptics_disconnect; 16218c2ecf20Sopenharmony_ci psmouse->reconnect = synaptics_reconnect; 16228c2ecf20Sopenharmony_ci psmouse->fast_reconnect = NULL; 16238c2ecf20Sopenharmony_ci psmouse->cleanup = synaptics_reset; 16248c2ecf20Sopenharmony_ci /* Synaptics can usually stay in sync without extra help */ 16258c2ecf20Sopenharmony_ci psmouse->resync_time = 0; 16268c2ecf20Sopenharmony_ci 16278c2ecf20Sopenharmony_ci if (SYN_CAP_PASS_THROUGH(info->capabilities)) 16288c2ecf20Sopenharmony_ci synaptics_pt_create(psmouse); 16298c2ecf20Sopenharmony_ci 16308c2ecf20Sopenharmony_ci /* 16318c2ecf20Sopenharmony_ci * Toshiba's KBC seems to have trouble handling data from 16328c2ecf20Sopenharmony_ci * Synaptics at full rate. Switch to a lower rate (roughly 16338c2ecf20Sopenharmony_ci * the same rate as a standard PS/2 mouse). 16348c2ecf20Sopenharmony_ci */ 16358c2ecf20Sopenharmony_ci if (psmouse->rate >= 80 && impaired_toshiba_kbc) { 16368c2ecf20Sopenharmony_ci psmouse_info(psmouse, 16378c2ecf20Sopenharmony_ci "Toshiba %s detected, limiting rate to 40pps.\n", 16388c2ecf20Sopenharmony_ci dmi_get_system_info(DMI_PRODUCT_NAME)); 16398c2ecf20Sopenharmony_ci psmouse->rate = 40; 16408c2ecf20Sopenharmony_ci } 16418c2ecf20Sopenharmony_ci 16428c2ecf20Sopenharmony_ci if (!priv->absolute_mode && SYN_ID_DISGEST_SUPPORTED(info->identity)) { 16438c2ecf20Sopenharmony_ci err = device_create_file(&psmouse->ps2dev.serio->dev, 16448c2ecf20Sopenharmony_ci &psmouse_attr_disable_gesture.dattr); 16458c2ecf20Sopenharmony_ci if (err) { 16468c2ecf20Sopenharmony_ci psmouse_err(psmouse, 16478c2ecf20Sopenharmony_ci "Failed to create disable_gesture attribute (%d)", 16488c2ecf20Sopenharmony_ci err); 16498c2ecf20Sopenharmony_ci goto init_fail; 16508c2ecf20Sopenharmony_ci } 16518c2ecf20Sopenharmony_ci } 16528c2ecf20Sopenharmony_ci 16538c2ecf20Sopenharmony_ci return 0; 16548c2ecf20Sopenharmony_ci 16558c2ecf20Sopenharmony_ci init_fail: 16568c2ecf20Sopenharmony_ci kfree(priv); 16578c2ecf20Sopenharmony_ci return err; 16588c2ecf20Sopenharmony_ci} 16598c2ecf20Sopenharmony_ci 16608c2ecf20Sopenharmony_cistatic int __synaptics_init(struct psmouse *psmouse, bool absolute_mode) 16618c2ecf20Sopenharmony_ci{ 16628c2ecf20Sopenharmony_ci struct synaptics_device_info info; 16638c2ecf20Sopenharmony_ci int error; 16648c2ecf20Sopenharmony_ci 16658c2ecf20Sopenharmony_ci psmouse_reset(psmouse); 16668c2ecf20Sopenharmony_ci 16678c2ecf20Sopenharmony_ci error = synaptics_query_hardware(psmouse, &info); 16688c2ecf20Sopenharmony_ci if (error) { 16698c2ecf20Sopenharmony_ci psmouse_err(psmouse, "Unable to query device: %d\n", error); 16708c2ecf20Sopenharmony_ci return error; 16718c2ecf20Sopenharmony_ci } 16728c2ecf20Sopenharmony_ci 16738c2ecf20Sopenharmony_ci return synaptics_init_ps2(psmouse, &info, absolute_mode); 16748c2ecf20Sopenharmony_ci} 16758c2ecf20Sopenharmony_ci 16768c2ecf20Sopenharmony_ciint synaptics_init_absolute(struct psmouse *psmouse) 16778c2ecf20Sopenharmony_ci{ 16788c2ecf20Sopenharmony_ci return __synaptics_init(psmouse, true); 16798c2ecf20Sopenharmony_ci} 16808c2ecf20Sopenharmony_ci 16818c2ecf20Sopenharmony_ciint synaptics_init_relative(struct psmouse *psmouse) 16828c2ecf20Sopenharmony_ci{ 16838c2ecf20Sopenharmony_ci return __synaptics_init(psmouse, false); 16848c2ecf20Sopenharmony_ci} 16858c2ecf20Sopenharmony_ci 16868c2ecf20Sopenharmony_cistatic int synaptics_setup_ps2(struct psmouse *psmouse, 16878c2ecf20Sopenharmony_ci struct synaptics_device_info *info) 16888c2ecf20Sopenharmony_ci{ 16898c2ecf20Sopenharmony_ci bool absolute_mode = true; 16908c2ecf20Sopenharmony_ci int error; 16918c2ecf20Sopenharmony_ci 16928c2ecf20Sopenharmony_ci /* 16938c2ecf20Sopenharmony_ci * The OLPC XO has issues with Synaptics' absolute mode; the constant 16948c2ecf20Sopenharmony_ci * packet spew overloads the EC such that key presses on the keyboard 16958c2ecf20Sopenharmony_ci * are missed. Given that, don't even attempt to use Absolute mode. 16968c2ecf20Sopenharmony_ci * Relative mode seems to work just fine. 16978c2ecf20Sopenharmony_ci */ 16988c2ecf20Sopenharmony_ci if (broken_olpc_ec) { 16998c2ecf20Sopenharmony_ci psmouse_info(psmouse, 17008c2ecf20Sopenharmony_ci "OLPC XO detected, forcing relative protocol.\n"); 17018c2ecf20Sopenharmony_ci absolute_mode = false; 17028c2ecf20Sopenharmony_ci } 17038c2ecf20Sopenharmony_ci 17048c2ecf20Sopenharmony_ci error = synaptics_init_ps2(psmouse, info, absolute_mode); 17058c2ecf20Sopenharmony_ci if (error) 17068c2ecf20Sopenharmony_ci return error; 17078c2ecf20Sopenharmony_ci 17088c2ecf20Sopenharmony_ci return absolute_mode ? PSMOUSE_SYNAPTICS : PSMOUSE_SYNAPTICS_RELATIVE; 17098c2ecf20Sopenharmony_ci} 17108c2ecf20Sopenharmony_ci 17118c2ecf20Sopenharmony_ci#else /* CONFIG_MOUSE_PS2_SYNAPTICS */ 17128c2ecf20Sopenharmony_ci 17138c2ecf20Sopenharmony_civoid __init synaptics_module_init(void) 17148c2ecf20Sopenharmony_ci{ 17158c2ecf20Sopenharmony_ci} 17168c2ecf20Sopenharmony_ci 17178c2ecf20Sopenharmony_cistatic int __maybe_unused 17188c2ecf20Sopenharmony_cisynaptics_setup_ps2(struct psmouse *psmouse, 17198c2ecf20Sopenharmony_ci struct synaptics_device_info *info) 17208c2ecf20Sopenharmony_ci{ 17218c2ecf20Sopenharmony_ci return -ENOSYS; 17228c2ecf20Sopenharmony_ci} 17238c2ecf20Sopenharmony_ci 17248c2ecf20Sopenharmony_ci#endif /* CONFIG_MOUSE_PS2_SYNAPTICS */ 17258c2ecf20Sopenharmony_ci 17268c2ecf20Sopenharmony_ci#ifdef CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS 17278c2ecf20Sopenharmony_ci 17288c2ecf20Sopenharmony_ci/* 17298c2ecf20Sopenharmony_ci * The newest Synaptics device can use a secondary bus (called InterTouch) which 17308c2ecf20Sopenharmony_ci * provides a better bandwidth and allow a better control of the touchpads. 17318c2ecf20Sopenharmony_ci * This is used to decide if we need to use this bus or not. 17328c2ecf20Sopenharmony_ci */ 17338c2ecf20Sopenharmony_cienum { 17348c2ecf20Sopenharmony_ci SYNAPTICS_INTERTOUCH_NOT_SET = -1, 17358c2ecf20Sopenharmony_ci SYNAPTICS_INTERTOUCH_OFF, 17368c2ecf20Sopenharmony_ci SYNAPTICS_INTERTOUCH_ON, 17378c2ecf20Sopenharmony_ci}; 17388c2ecf20Sopenharmony_ci 17398c2ecf20Sopenharmony_cistatic int synaptics_intertouch = IS_ENABLED(CONFIG_RMI4_SMB) ? 17408c2ecf20Sopenharmony_ci SYNAPTICS_INTERTOUCH_NOT_SET : SYNAPTICS_INTERTOUCH_OFF; 17418c2ecf20Sopenharmony_cimodule_param_named(synaptics_intertouch, synaptics_intertouch, int, 0644); 17428c2ecf20Sopenharmony_ciMODULE_PARM_DESC(synaptics_intertouch, "Use a secondary bus for the Synaptics device."); 17438c2ecf20Sopenharmony_ci 17448c2ecf20Sopenharmony_cistatic int synaptics_create_intertouch(struct psmouse *psmouse, 17458c2ecf20Sopenharmony_ci struct synaptics_device_info *info, 17468c2ecf20Sopenharmony_ci bool leave_breadcrumbs) 17478c2ecf20Sopenharmony_ci{ 17488c2ecf20Sopenharmony_ci bool topbuttonpad = 17498c2ecf20Sopenharmony_ci psmouse_matches_pnp_id(psmouse, topbuttonpad_pnp_ids) && 17508c2ecf20Sopenharmony_ci !SYN_CAP_EXT_BUTTONS_STICK(info->ext_cap_10); 17518c2ecf20Sopenharmony_ci const struct rmi_device_platform_data pdata = { 17528c2ecf20Sopenharmony_ci .reset_delay_ms = 30, 17538c2ecf20Sopenharmony_ci .sensor_pdata = { 17548c2ecf20Sopenharmony_ci .sensor_type = rmi_sensor_touchpad, 17558c2ecf20Sopenharmony_ci .axis_align.flip_y = true, 17568c2ecf20Sopenharmony_ci .kernel_tracking = false, 17578c2ecf20Sopenharmony_ci .topbuttonpad = topbuttonpad, 17588c2ecf20Sopenharmony_ci }, 17598c2ecf20Sopenharmony_ci .gpio_data = { 17608c2ecf20Sopenharmony_ci .buttonpad = SYN_CAP_CLICKPAD(info->ext_cap_0c), 17618c2ecf20Sopenharmony_ci .trackstick_buttons = 17628c2ecf20Sopenharmony_ci !!SYN_CAP_EXT_BUTTONS_STICK(info->ext_cap_10), 17638c2ecf20Sopenharmony_ci }, 17648c2ecf20Sopenharmony_ci }; 17658c2ecf20Sopenharmony_ci const struct i2c_board_info intertouch_board = { 17668c2ecf20Sopenharmony_ci I2C_BOARD_INFO("rmi4_smbus", 0x2c), 17678c2ecf20Sopenharmony_ci .flags = I2C_CLIENT_HOST_NOTIFY, 17688c2ecf20Sopenharmony_ci }; 17698c2ecf20Sopenharmony_ci 17708c2ecf20Sopenharmony_ci return psmouse_smbus_init(psmouse, &intertouch_board, 17718c2ecf20Sopenharmony_ci &pdata, sizeof(pdata), true, 17728c2ecf20Sopenharmony_ci leave_breadcrumbs); 17738c2ecf20Sopenharmony_ci} 17748c2ecf20Sopenharmony_ci 17758c2ecf20Sopenharmony_ci/** 17768c2ecf20Sopenharmony_ci * synaptics_setup_intertouch - called once the PS/2 devices are enumerated 17778c2ecf20Sopenharmony_ci * and decides to instantiate a SMBus InterTouch device. 17788c2ecf20Sopenharmony_ci */ 17798c2ecf20Sopenharmony_cistatic int synaptics_setup_intertouch(struct psmouse *psmouse, 17808c2ecf20Sopenharmony_ci struct synaptics_device_info *info, 17818c2ecf20Sopenharmony_ci bool leave_breadcrumbs) 17828c2ecf20Sopenharmony_ci{ 17838c2ecf20Sopenharmony_ci int error; 17848c2ecf20Sopenharmony_ci 17858c2ecf20Sopenharmony_ci if (synaptics_intertouch == SYNAPTICS_INTERTOUCH_OFF) 17868c2ecf20Sopenharmony_ci return -ENXIO; 17878c2ecf20Sopenharmony_ci 17888c2ecf20Sopenharmony_ci if (synaptics_intertouch == SYNAPTICS_INTERTOUCH_NOT_SET) { 17898c2ecf20Sopenharmony_ci if (!psmouse_matches_pnp_id(psmouse, topbuttonpad_pnp_ids) && 17908c2ecf20Sopenharmony_ci !psmouse_matches_pnp_id(psmouse, smbus_pnp_ids)) { 17918c2ecf20Sopenharmony_ci 17928c2ecf20Sopenharmony_ci if (!psmouse_matches_pnp_id(psmouse, forcepad_pnp_ids)) 17938c2ecf20Sopenharmony_ci psmouse_info(psmouse, 17948c2ecf20Sopenharmony_ci "Your touchpad (%s) says it can support a different bus. " 17958c2ecf20Sopenharmony_ci "If i2c-hid and hid-rmi are not used, you might want to try setting psmouse.synaptics_intertouch to 1 and report this to linux-input@vger.kernel.org.\n", 17968c2ecf20Sopenharmony_ci psmouse->ps2dev.serio->firmware_id); 17978c2ecf20Sopenharmony_ci 17988c2ecf20Sopenharmony_ci return -ENXIO; 17998c2ecf20Sopenharmony_ci } 18008c2ecf20Sopenharmony_ci } 18018c2ecf20Sopenharmony_ci 18028c2ecf20Sopenharmony_ci psmouse_info(psmouse, "Trying to set up SMBus access\n"); 18038c2ecf20Sopenharmony_ci 18048c2ecf20Sopenharmony_ci error = synaptics_create_intertouch(psmouse, info, leave_breadcrumbs); 18058c2ecf20Sopenharmony_ci if (error) { 18068c2ecf20Sopenharmony_ci if (error == -EAGAIN) 18078c2ecf20Sopenharmony_ci psmouse_info(psmouse, "SMbus companion is not ready yet\n"); 18088c2ecf20Sopenharmony_ci else 18098c2ecf20Sopenharmony_ci psmouse_err(psmouse, "unable to create intertouch device\n"); 18108c2ecf20Sopenharmony_ci 18118c2ecf20Sopenharmony_ci return error; 18128c2ecf20Sopenharmony_ci } 18138c2ecf20Sopenharmony_ci 18148c2ecf20Sopenharmony_ci return 0; 18158c2ecf20Sopenharmony_ci} 18168c2ecf20Sopenharmony_ci 18178c2ecf20Sopenharmony_ciint synaptics_init_smbus(struct psmouse *psmouse) 18188c2ecf20Sopenharmony_ci{ 18198c2ecf20Sopenharmony_ci struct synaptics_device_info info; 18208c2ecf20Sopenharmony_ci int error; 18218c2ecf20Sopenharmony_ci 18228c2ecf20Sopenharmony_ci psmouse_reset(psmouse); 18238c2ecf20Sopenharmony_ci 18248c2ecf20Sopenharmony_ci error = synaptics_query_hardware(psmouse, &info); 18258c2ecf20Sopenharmony_ci if (error) { 18268c2ecf20Sopenharmony_ci psmouse_err(psmouse, "Unable to query device: %d\n", error); 18278c2ecf20Sopenharmony_ci return error; 18288c2ecf20Sopenharmony_ci } 18298c2ecf20Sopenharmony_ci 18308c2ecf20Sopenharmony_ci if (!SYN_CAP_INTERTOUCH(info.ext_cap_0c)) 18318c2ecf20Sopenharmony_ci return -ENXIO; 18328c2ecf20Sopenharmony_ci 18338c2ecf20Sopenharmony_ci return synaptics_create_intertouch(psmouse, &info, false); 18348c2ecf20Sopenharmony_ci} 18358c2ecf20Sopenharmony_ci 18368c2ecf20Sopenharmony_ci#else /* CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS */ 18378c2ecf20Sopenharmony_ci 18388c2ecf20Sopenharmony_cistatic int __maybe_unused 18398c2ecf20Sopenharmony_cisynaptics_setup_intertouch(struct psmouse *psmouse, 18408c2ecf20Sopenharmony_ci struct synaptics_device_info *info, 18418c2ecf20Sopenharmony_ci bool leave_breadcrumbs) 18428c2ecf20Sopenharmony_ci{ 18438c2ecf20Sopenharmony_ci return -ENOSYS; 18448c2ecf20Sopenharmony_ci} 18458c2ecf20Sopenharmony_ci 18468c2ecf20Sopenharmony_ciint synaptics_init_smbus(struct psmouse *psmouse) 18478c2ecf20Sopenharmony_ci{ 18488c2ecf20Sopenharmony_ci return -ENOSYS; 18498c2ecf20Sopenharmony_ci} 18508c2ecf20Sopenharmony_ci 18518c2ecf20Sopenharmony_ci#endif /* CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS */ 18528c2ecf20Sopenharmony_ci 18538c2ecf20Sopenharmony_ci#if defined(CONFIG_MOUSE_PS2_SYNAPTICS) || \ 18548c2ecf20Sopenharmony_ci defined(CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS) 18558c2ecf20Sopenharmony_ci 18568c2ecf20Sopenharmony_ciint synaptics_init(struct psmouse *psmouse) 18578c2ecf20Sopenharmony_ci{ 18588c2ecf20Sopenharmony_ci struct synaptics_device_info info; 18598c2ecf20Sopenharmony_ci int error; 18608c2ecf20Sopenharmony_ci int retval; 18618c2ecf20Sopenharmony_ci 18628c2ecf20Sopenharmony_ci psmouse_reset(psmouse); 18638c2ecf20Sopenharmony_ci 18648c2ecf20Sopenharmony_ci error = synaptics_query_hardware(psmouse, &info); 18658c2ecf20Sopenharmony_ci if (error) { 18668c2ecf20Sopenharmony_ci psmouse_err(psmouse, "Unable to query device: %d\n", error); 18678c2ecf20Sopenharmony_ci return error; 18688c2ecf20Sopenharmony_ci } 18698c2ecf20Sopenharmony_ci 18708c2ecf20Sopenharmony_ci if (SYN_CAP_INTERTOUCH(info.ext_cap_0c)) { 18718c2ecf20Sopenharmony_ci if ((!IS_ENABLED(CONFIG_RMI4_SMB) || 18728c2ecf20Sopenharmony_ci !IS_ENABLED(CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS)) && 18738c2ecf20Sopenharmony_ci /* Forcepads need F21, which is not ready */ 18748c2ecf20Sopenharmony_ci !psmouse_matches_pnp_id(psmouse, forcepad_pnp_ids)) { 18758c2ecf20Sopenharmony_ci psmouse_warn(psmouse, 18768c2ecf20Sopenharmony_ci "The touchpad can support a better bus than the too old PS/2 protocol. " 18778c2ecf20Sopenharmony_ci "Make sure MOUSE_PS2_SYNAPTICS_SMBUS and RMI4_SMB are enabled to get a better touchpad experience.\n"); 18788c2ecf20Sopenharmony_ci } 18798c2ecf20Sopenharmony_ci 18808c2ecf20Sopenharmony_ci error = synaptics_setup_intertouch(psmouse, &info, true); 18818c2ecf20Sopenharmony_ci if (!error) 18828c2ecf20Sopenharmony_ci return PSMOUSE_SYNAPTICS_SMBUS; 18838c2ecf20Sopenharmony_ci } 18848c2ecf20Sopenharmony_ci 18858c2ecf20Sopenharmony_ci retval = synaptics_setup_ps2(psmouse, &info); 18868c2ecf20Sopenharmony_ci if (retval < 0) { 18878c2ecf20Sopenharmony_ci /* 18888c2ecf20Sopenharmony_ci * Not using any flavor of Synaptics support, so clean up 18898c2ecf20Sopenharmony_ci * SMbus breadcrumbs, if any. 18908c2ecf20Sopenharmony_ci */ 18918c2ecf20Sopenharmony_ci psmouse_smbus_cleanup(psmouse); 18928c2ecf20Sopenharmony_ci } 18938c2ecf20Sopenharmony_ci 18948c2ecf20Sopenharmony_ci return retval; 18958c2ecf20Sopenharmony_ci} 18968c2ecf20Sopenharmony_ci 18978c2ecf20Sopenharmony_ci#else /* CONFIG_MOUSE_PS2_SYNAPTICS || CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS */ 18988c2ecf20Sopenharmony_ci 18998c2ecf20Sopenharmony_ciint synaptics_init(struct psmouse *psmouse) 19008c2ecf20Sopenharmony_ci{ 19018c2ecf20Sopenharmony_ci return -ENOSYS; 19028c2ecf20Sopenharmony_ci} 19038c2ecf20Sopenharmony_ci 19048c2ecf20Sopenharmony_ci#endif /* CONFIG_MOUSE_PS2_SYNAPTICS || CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS */ 1905