18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Elantech Touchpad driver (v6) 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2007-2009 Arjan Opmeer <arjan@opmeer.net> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Trademarks are the property of their respective owners. 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/delay.h> 118c2ecf20Sopenharmony_ci#include <linux/dmi.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/i2c.h> 158c2ecf20Sopenharmony_ci#include <linux/input.h> 168c2ecf20Sopenharmony_ci#include <linux/input/mt.h> 178c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 188c2ecf20Sopenharmony_ci#include <linux/serio.h> 198c2ecf20Sopenharmony_ci#include <linux/libps2.h> 208c2ecf20Sopenharmony_ci#include <asm/unaligned.h> 218c2ecf20Sopenharmony_ci#include "psmouse.h" 228c2ecf20Sopenharmony_ci#include "elantech.h" 238c2ecf20Sopenharmony_ci#include "elan_i2c.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define elantech_debug(fmt, ...) \ 268c2ecf20Sopenharmony_ci do { \ 278c2ecf20Sopenharmony_ci if (etd->info.debug) \ 288c2ecf20Sopenharmony_ci psmouse_printk(KERN_DEBUG, psmouse, \ 298c2ecf20Sopenharmony_ci fmt, ##__VA_ARGS__); \ 308c2ecf20Sopenharmony_ci } while (0) 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/* 338c2ecf20Sopenharmony_ci * Send a Synaptics style sliced query command 348c2ecf20Sopenharmony_ci */ 358c2ecf20Sopenharmony_cistatic int synaptics_send_cmd(struct psmouse *psmouse, unsigned char c, 368c2ecf20Sopenharmony_ci unsigned char *param) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci if (ps2_sliced_command(&psmouse->ps2dev, c) || 398c2ecf20Sopenharmony_ci ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO)) { 408c2ecf20Sopenharmony_ci psmouse_err(psmouse, "%s query 0x%02x failed.\n", __func__, c); 418c2ecf20Sopenharmony_ci return -1; 428c2ecf20Sopenharmony_ci } 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci return 0; 458c2ecf20Sopenharmony_ci} 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci/* 488c2ecf20Sopenharmony_ci * V3 and later support this fast command 498c2ecf20Sopenharmony_ci */ 508c2ecf20Sopenharmony_cistatic int elantech_send_cmd(struct psmouse *psmouse, unsigned char c, 518c2ecf20Sopenharmony_ci unsigned char *param) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci struct ps2dev *ps2dev = &psmouse->ps2dev; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci if (ps2_command(ps2dev, NULL, ETP_PS2_CUSTOM_COMMAND) || 568c2ecf20Sopenharmony_ci ps2_command(ps2dev, NULL, c) || 578c2ecf20Sopenharmony_ci ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) { 588c2ecf20Sopenharmony_ci psmouse_err(psmouse, "%s query 0x%02x failed.\n", __func__, c); 598c2ecf20Sopenharmony_ci return -1; 608c2ecf20Sopenharmony_ci } 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci return 0; 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci/* 668c2ecf20Sopenharmony_ci * A retrying version of ps2_command 678c2ecf20Sopenharmony_ci */ 688c2ecf20Sopenharmony_cistatic int elantech_ps2_command(struct psmouse *psmouse, 698c2ecf20Sopenharmony_ci unsigned char *param, int command) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci struct ps2dev *ps2dev = &psmouse->ps2dev; 728c2ecf20Sopenharmony_ci struct elantech_data *etd = psmouse->private; 738c2ecf20Sopenharmony_ci int rc; 748c2ecf20Sopenharmony_ci int tries = ETP_PS2_COMMAND_TRIES; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci do { 778c2ecf20Sopenharmony_ci rc = ps2_command(ps2dev, param, command); 788c2ecf20Sopenharmony_ci if (rc == 0) 798c2ecf20Sopenharmony_ci break; 808c2ecf20Sopenharmony_ci tries--; 818c2ecf20Sopenharmony_ci elantech_debug("retrying ps2 command 0x%02x (%d).\n", 828c2ecf20Sopenharmony_ci command, tries); 838c2ecf20Sopenharmony_ci msleep(ETP_PS2_COMMAND_DELAY); 848c2ecf20Sopenharmony_ci } while (tries > 0); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci if (rc) 878c2ecf20Sopenharmony_ci psmouse_err(psmouse, "ps2 command 0x%02x failed.\n", command); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci return rc; 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci/* 938c2ecf20Sopenharmony_ci * Send an Elantech style special command to read 3 bytes from a register 948c2ecf20Sopenharmony_ci */ 958c2ecf20Sopenharmony_cistatic int elantech_read_reg_params(struct psmouse *psmouse, u8 reg, u8 *param) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || 988c2ecf20Sopenharmony_ci elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READWRITE) || 998c2ecf20Sopenharmony_ci elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || 1008c2ecf20Sopenharmony_ci elantech_ps2_command(psmouse, NULL, reg) || 1018c2ecf20Sopenharmony_ci elantech_ps2_command(psmouse, param, PSMOUSE_CMD_GETINFO)) { 1028c2ecf20Sopenharmony_ci psmouse_err(psmouse, 1038c2ecf20Sopenharmony_ci "failed to read register %#02x\n", reg); 1048c2ecf20Sopenharmony_ci return -EIO; 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci return 0; 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci/* 1118c2ecf20Sopenharmony_ci * Send an Elantech style special command to write a register with a parameter 1128c2ecf20Sopenharmony_ci */ 1138c2ecf20Sopenharmony_cistatic int elantech_write_reg_params(struct psmouse *psmouse, u8 reg, u8 *param) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || 1168c2ecf20Sopenharmony_ci elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READWRITE) || 1178c2ecf20Sopenharmony_ci elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || 1188c2ecf20Sopenharmony_ci elantech_ps2_command(psmouse, NULL, reg) || 1198c2ecf20Sopenharmony_ci elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || 1208c2ecf20Sopenharmony_ci elantech_ps2_command(psmouse, NULL, param[0]) || 1218c2ecf20Sopenharmony_ci elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || 1228c2ecf20Sopenharmony_ci elantech_ps2_command(psmouse, NULL, param[1]) || 1238c2ecf20Sopenharmony_ci elantech_ps2_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11)) { 1248c2ecf20Sopenharmony_ci psmouse_err(psmouse, 1258c2ecf20Sopenharmony_ci "failed to write register %#02x with value %#02x%#02x\n", 1268c2ecf20Sopenharmony_ci reg, param[0], param[1]); 1278c2ecf20Sopenharmony_ci return -EIO; 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci return 0; 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci/* 1348c2ecf20Sopenharmony_ci * Send an Elantech style special command to read a value from a register 1358c2ecf20Sopenharmony_ci */ 1368c2ecf20Sopenharmony_cistatic int elantech_read_reg(struct psmouse *psmouse, unsigned char reg, 1378c2ecf20Sopenharmony_ci unsigned char *val) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci struct elantech_data *etd = psmouse->private; 1408c2ecf20Sopenharmony_ci unsigned char param[3]; 1418c2ecf20Sopenharmony_ci int rc = 0; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci if (reg < 0x07 || reg > 0x26) 1448c2ecf20Sopenharmony_ci return -1; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci if (reg > 0x11 && reg < 0x20) 1478c2ecf20Sopenharmony_ci return -1; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci switch (etd->info.hw_version) { 1508c2ecf20Sopenharmony_ci case 1: 1518c2ecf20Sopenharmony_ci if (ps2_sliced_command(&psmouse->ps2dev, ETP_REGISTER_READ) || 1528c2ecf20Sopenharmony_ci ps2_sliced_command(&psmouse->ps2dev, reg) || 1538c2ecf20Sopenharmony_ci ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO)) { 1548c2ecf20Sopenharmony_ci rc = -1; 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci break; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci case 2: 1598c2ecf20Sopenharmony_ci if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || 1608c2ecf20Sopenharmony_ci elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READ) || 1618c2ecf20Sopenharmony_ci elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || 1628c2ecf20Sopenharmony_ci elantech_ps2_command(psmouse, NULL, reg) || 1638c2ecf20Sopenharmony_ci elantech_ps2_command(psmouse, param, PSMOUSE_CMD_GETINFO)) { 1648c2ecf20Sopenharmony_ci rc = -1; 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci break; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci case 3 ... 4: 1698c2ecf20Sopenharmony_ci if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || 1708c2ecf20Sopenharmony_ci elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READWRITE) || 1718c2ecf20Sopenharmony_ci elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || 1728c2ecf20Sopenharmony_ci elantech_ps2_command(psmouse, NULL, reg) || 1738c2ecf20Sopenharmony_ci elantech_ps2_command(psmouse, param, PSMOUSE_CMD_GETINFO)) { 1748c2ecf20Sopenharmony_ci rc = -1; 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci break; 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci if (rc) 1808c2ecf20Sopenharmony_ci psmouse_err(psmouse, "failed to read register 0x%02x.\n", reg); 1818c2ecf20Sopenharmony_ci else if (etd->info.hw_version != 4) 1828c2ecf20Sopenharmony_ci *val = param[0]; 1838c2ecf20Sopenharmony_ci else 1848c2ecf20Sopenharmony_ci *val = param[1]; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci return rc; 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci/* 1908c2ecf20Sopenharmony_ci * Send an Elantech style special command to write a register with a value 1918c2ecf20Sopenharmony_ci */ 1928c2ecf20Sopenharmony_cistatic int elantech_write_reg(struct psmouse *psmouse, unsigned char reg, 1938c2ecf20Sopenharmony_ci unsigned char val) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci struct elantech_data *etd = psmouse->private; 1968c2ecf20Sopenharmony_ci int rc = 0; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci if (reg < 0x07 || reg > 0x26) 1998c2ecf20Sopenharmony_ci return -1; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci if (reg > 0x11 && reg < 0x20) 2028c2ecf20Sopenharmony_ci return -1; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci switch (etd->info.hw_version) { 2058c2ecf20Sopenharmony_ci case 1: 2068c2ecf20Sopenharmony_ci if (ps2_sliced_command(&psmouse->ps2dev, ETP_REGISTER_WRITE) || 2078c2ecf20Sopenharmony_ci ps2_sliced_command(&psmouse->ps2dev, reg) || 2088c2ecf20Sopenharmony_ci ps2_sliced_command(&psmouse->ps2dev, val) || 2098c2ecf20Sopenharmony_ci ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11)) { 2108c2ecf20Sopenharmony_ci rc = -1; 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci break; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci case 2: 2158c2ecf20Sopenharmony_ci if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || 2168c2ecf20Sopenharmony_ci elantech_ps2_command(psmouse, NULL, ETP_REGISTER_WRITE) || 2178c2ecf20Sopenharmony_ci elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || 2188c2ecf20Sopenharmony_ci elantech_ps2_command(psmouse, NULL, reg) || 2198c2ecf20Sopenharmony_ci elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || 2208c2ecf20Sopenharmony_ci elantech_ps2_command(psmouse, NULL, val) || 2218c2ecf20Sopenharmony_ci elantech_ps2_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11)) { 2228c2ecf20Sopenharmony_ci rc = -1; 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci break; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci case 3: 2278c2ecf20Sopenharmony_ci if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || 2288c2ecf20Sopenharmony_ci elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READWRITE) || 2298c2ecf20Sopenharmony_ci elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || 2308c2ecf20Sopenharmony_ci elantech_ps2_command(psmouse, NULL, reg) || 2318c2ecf20Sopenharmony_ci elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || 2328c2ecf20Sopenharmony_ci elantech_ps2_command(psmouse, NULL, val) || 2338c2ecf20Sopenharmony_ci elantech_ps2_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11)) { 2348c2ecf20Sopenharmony_ci rc = -1; 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci break; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci case 4: 2398c2ecf20Sopenharmony_ci if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || 2408c2ecf20Sopenharmony_ci elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READWRITE) || 2418c2ecf20Sopenharmony_ci elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || 2428c2ecf20Sopenharmony_ci elantech_ps2_command(psmouse, NULL, reg) || 2438c2ecf20Sopenharmony_ci elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || 2448c2ecf20Sopenharmony_ci elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READWRITE) || 2458c2ecf20Sopenharmony_ci elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || 2468c2ecf20Sopenharmony_ci elantech_ps2_command(psmouse, NULL, val) || 2478c2ecf20Sopenharmony_ci elantech_ps2_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11)) { 2488c2ecf20Sopenharmony_ci rc = -1; 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci break; 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci if (rc) 2548c2ecf20Sopenharmony_ci psmouse_err(psmouse, 2558c2ecf20Sopenharmony_ci "failed to write register 0x%02x with value 0x%02x.\n", 2568c2ecf20Sopenharmony_ci reg, val); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci return rc; 2598c2ecf20Sopenharmony_ci} 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci/* 2628c2ecf20Sopenharmony_ci * Dump a complete mouse movement packet to the syslog 2638c2ecf20Sopenharmony_ci */ 2648c2ecf20Sopenharmony_cistatic void elantech_packet_dump(struct psmouse *psmouse) 2658c2ecf20Sopenharmony_ci{ 2668c2ecf20Sopenharmony_ci psmouse_printk(KERN_DEBUG, psmouse, "PS/2 packet [%*ph]\n", 2678c2ecf20Sopenharmony_ci psmouse->pktsize, psmouse->packet); 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci/* 2718c2ecf20Sopenharmony_ci * Advertise INPUT_PROP_BUTTONPAD for clickpads. The testing of bit 12 in 2728c2ecf20Sopenharmony_ci * fw_version for this is based on the following fw_version & caps table: 2738c2ecf20Sopenharmony_ci * 2748c2ecf20Sopenharmony_ci * Laptop-model: fw_version: caps: buttons: 2758c2ecf20Sopenharmony_ci * Acer S3 0x461f00 10, 13, 0e clickpad 2768c2ecf20Sopenharmony_ci * Acer S7-392 0x581f01 50, 17, 0d clickpad 2778c2ecf20Sopenharmony_ci * Acer V5-131 0x461f02 01, 16, 0c clickpad 2788c2ecf20Sopenharmony_ci * Acer V5-551 0x461f00 ? clickpad 2798c2ecf20Sopenharmony_ci * Asus K53SV 0x450f01 78, 15, 0c 2 hw buttons 2808c2ecf20Sopenharmony_ci * Asus G46VW 0x460f02 00, 18, 0c 2 hw buttons 2818c2ecf20Sopenharmony_ci * Asus G750JX 0x360f00 00, 16, 0c 2 hw buttons 2828c2ecf20Sopenharmony_ci * Asus TP500LN 0x381f17 10, 14, 0e clickpad 2838c2ecf20Sopenharmony_ci * Asus X750JN 0x381f17 10, 14, 0e clickpad 2848c2ecf20Sopenharmony_ci * Asus UX31 0x361f00 20, 15, 0e clickpad 2858c2ecf20Sopenharmony_ci * Asus UX32VD 0x361f02 00, 15, 0e clickpad 2868c2ecf20Sopenharmony_ci * Avatar AVIU-145A2 0x361f00 ? clickpad 2878c2ecf20Sopenharmony_ci * Fujitsu CELSIUS H760 0x570f02 40, 14, 0c 3 hw buttons (**) 2888c2ecf20Sopenharmony_ci * Fujitsu CELSIUS H780 0x5d0f02 41, 16, 0d 3 hw buttons (**) 2898c2ecf20Sopenharmony_ci * Fujitsu LIFEBOOK E544 0x470f00 d0, 12, 09 2 hw buttons 2908c2ecf20Sopenharmony_ci * Fujitsu LIFEBOOK E546 0x470f00 50, 12, 09 2 hw buttons 2918c2ecf20Sopenharmony_ci * Fujitsu LIFEBOOK E547 0x470f00 50, 12, 09 2 hw buttons 2928c2ecf20Sopenharmony_ci * Fujitsu LIFEBOOK E554 0x570f01 40, 14, 0c 2 hw buttons 2938c2ecf20Sopenharmony_ci * Fujitsu LIFEBOOK E557 0x570f01 40, 14, 0c 2 hw buttons 2948c2ecf20Sopenharmony_ci * Fujitsu T725 0x470f01 05, 12, 09 2 hw buttons 2958c2ecf20Sopenharmony_ci * Fujitsu H730 0x570f00 c0, 14, 0c 3 hw buttons (**) 2968c2ecf20Sopenharmony_ci * Gigabyte U2442 0x450f01 58, 17, 0c 2 hw buttons 2978c2ecf20Sopenharmony_ci * Lenovo L430 0x350f02 b9, 15, 0c 2 hw buttons (*) 2988c2ecf20Sopenharmony_ci * Lenovo L530 0x350f02 b9, 15, 0c 2 hw buttons (*) 2998c2ecf20Sopenharmony_ci * Samsung NF210 0x150b00 78, 14, 0a 2 hw buttons 3008c2ecf20Sopenharmony_ci * Samsung NP770Z5E 0x575f01 10, 15, 0f clickpad 3018c2ecf20Sopenharmony_ci * Samsung NP700Z5B 0x361f06 21, 15, 0f clickpad 3028c2ecf20Sopenharmony_ci * Samsung NP900X3E-A02 0x575f03 ? clickpad 3038c2ecf20Sopenharmony_ci * Samsung NP-QX410 0x851b00 19, 14, 0c clickpad 3048c2ecf20Sopenharmony_ci * Samsung RC512 0x450f00 08, 15, 0c 2 hw buttons 3058c2ecf20Sopenharmony_ci * Samsung RF710 0x450f00 ? 2 hw buttons 3068c2ecf20Sopenharmony_ci * System76 Pangolin 0x250f01 ? 2 hw buttons 3078c2ecf20Sopenharmony_ci * (*) + 3 trackpoint buttons 3088c2ecf20Sopenharmony_ci * (**) + 0 trackpoint buttons 3098c2ecf20Sopenharmony_ci * Note: Lenovo L430 and Lenovo L530 have the same fw_version/caps 3108c2ecf20Sopenharmony_ci */ 3118c2ecf20Sopenharmony_cistatic inline int elantech_is_buttonpad(struct elantech_device_info *info) 3128c2ecf20Sopenharmony_ci{ 3138c2ecf20Sopenharmony_ci return info->fw_version & 0x001000; 3148c2ecf20Sopenharmony_ci} 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci/* 3178c2ecf20Sopenharmony_ci * Interpret complete data packets and report absolute mode input events for 3188c2ecf20Sopenharmony_ci * hardware version 1. (4 byte packets) 3198c2ecf20Sopenharmony_ci */ 3208c2ecf20Sopenharmony_cistatic void elantech_report_absolute_v1(struct psmouse *psmouse) 3218c2ecf20Sopenharmony_ci{ 3228c2ecf20Sopenharmony_ci struct input_dev *dev = psmouse->dev; 3238c2ecf20Sopenharmony_ci struct elantech_data *etd = psmouse->private; 3248c2ecf20Sopenharmony_ci unsigned char *packet = psmouse->packet; 3258c2ecf20Sopenharmony_ci int fingers; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci if (etd->info.fw_version < 0x020000) { 3288c2ecf20Sopenharmony_ci /* 3298c2ecf20Sopenharmony_ci * byte 0: D U p1 p2 1 p3 R L 3308c2ecf20Sopenharmony_ci * byte 1: f 0 th tw x9 x8 y9 y8 3318c2ecf20Sopenharmony_ci */ 3328c2ecf20Sopenharmony_ci fingers = ((packet[1] & 0x80) >> 7) + 3338c2ecf20Sopenharmony_ci ((packet[1] & 0x30) >> 4); 3348c2ecf20Sopenharmony_ci } else { 3358c2ecf20Sopenharmony_ci /* 3368c2ecf20Sopenharmony_ci * byte 0: n1 n0 p2 p1 1 p3 R L 3378c2ecf20Sopenharmony_ci * byte 1: 0 0 0 0 x9 x8 y9 y8 3388c2ecf20Sopenharmony_ci */ 3398c2ecf20Sopenharmony_ci fingers = (packet[0] & 0xc0) >> 6; 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci if (etd->info.jumpy_cursor) { 3438c2ecf20Sopenharmony_ci if (fingers != 1) { 3448c2ecf20Sopenharmony_ci etd->single_finger_reports = 0; 3458c2ecf20Sopenharmony_ci } else if (etd->single_finger_reports < 2) { 3468c2ecf20Sopenharmony_ci /* Discard first 2 reports of one finger, bogus */ 3478c2ecf20Sopenharmony_ci etd->single_finger_reports++; 3488c2ecf20Sopenharmony_ci elantech_debug("discarding packet\n"); 3498c2ecf20Sopenharmony_ci return; 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci input_report_key(dev, BTN_TOUCH, fingers != 0); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci /* 3568c2ecf20Sopenharmony_ci * byte 2: x7 x6 x5 x4 x3 x2 x1 x0 3578c2ecf20Sopenharmony_ci * byte 3: y7 y6 y5 y4 y3 y2 y1 y0 3588c2ecf20Sopenharmony_ci */ 3598c2ecf20Sopenharmony_ci if (fingers) { 3608c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_X, 3618c2ecf20Sopenharmony_ci ((packet[1] & 0x0c) << 6) | packet[2]); 3628c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_Y, 3638c2ecf20Sopenharmony_ci etd->y_max - (((packet[1] & 0x03) << 8) | packet[3])); 3648c2ecf20Sopenharmony_ci } 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci input_report_key(dev, BTN_TOOL_FINGER, fingers == 1); 3678c2ecf20Sopenharmony_ci input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2); 3688c2ecf20Sopenharmony_ci input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci psmouse_report_standard_buttons(dev, packet[0]); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci if (etd->info.fw_version < 0x020000 && 3738c2ecf20Sopenharmony_ci (etd->info.capabilities[0] & ETP_CAP_HAS_ROCKER)) { 3748c2ecf20Sopenharmony_ci /* rocker up */ 3758c2ecf20Sopenharmony_ci input_report_key(dev, BTN_FORWARD, packet[0] & 0x40); 3768c2ecf20Sopenharmony_ci /* rocker down */ 3778c2ecf20Sopenharmony_ci input_report_key(dev, BTN_BACK, packet[0] & 0x80); 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci input_sync(dev); 3818c2ecf20Sopenharmony_ci} 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_cistatic void elantech_set_slot(struct input_dev *dev, int slot, bool active, 3848c2ecf20Sopenharmony_ci unsigned int x, unsigned int y) 3858c2ecf20Sopenharmony_ci{ 3868c2ecf20Sopenharmony_ci input_mt_slot(dev, slot); 3878c2ecf20Sopenharmony_ci input_mt_report_slot_state(dev, MT_TOOL_FINGER, active); 3888c2ecf20Sopenharmony_ci if (active) { 3898c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_MT_POSITION_X, x); 3908c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_MT_POSITION_Y, y); 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci} 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci/* x1 < x2 and y1 < y2 when two fingers, x = y = 0 when not pressed */ 3958c2ecf20Sopenharmony_cistatic void elantech_report_semi_mt_data(struct input_dev *dev, 3968c2ecf20Sopenharmony_ci unsigned int num_fingers, 3978c2ecf20Sopenharmony_ci unsigned int x1, unsigned int y1, 3988c2ecf20Sopenharmony_ci unsigned int x2, unsigned int y2) 3998c2ecf20Sopenharmony_ci{ 4008c2ecf20Sopenharmony_ci elantech_set_slot(dev, 0, num_fingers != 0, x1, y1); 4018c2ecf20Sopenharmony_ci elantech_set_slot(dev, 1, num_fingers >= 2, x2, y2); 4028c2ecf20Sopenharmony_ci} 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci/* 4058c2ecf20Sopenharmony_ci * Interpret complete data packets and report absolute mode input events for 4068c2ecf20Sopenharmony_ci * hardware version 2. (6 byte packets) 4078c2ecf20Sopenharmony_ci */ 4088c2ecf20Sopenharmony_cistatic void elantech_report_absolute_v2(struct psmouse *psmouse) 4098c2ecf20Sopenharmony_ci{ 4108c2ecf20Sopenharmony_ci struct elantech_data *etd = psmouse->private; 4118c2ecf20Sopenharmony_ci struct input_dev *dev = psmouse->dev; 4128c2ecf20Sopenharmony_ci unsigned char *packet = psmouse->packet; 4138c2ecf20Sopenharmony_ci unsigned int fingers, x1 = 0, y1 = 0, x2 = 0, y2 = 0; 4148c2ecf20Sopenharmony_ci unsigned int width = 0, pres = 0; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci /* byte 0: n1 n0 . . . . R L */ 4178c2ecf20Sopenharmony_ci fingers = (packet[0] & 0xc0) >> 6; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci switch (fingers) { 4208c2ecf20Sopenharmony_ci case 3: 4218c2ecf20Sopenharmony_ci /* 4228c2ecf20Sopenharmony_ci * Same as one finger, except report of more than 3 fingers: 4238c2ecf20Sopenharmony_ci * byte 3: n4 . w1 w0 . . . . 4248c2ecf20Sopenharmony_ci */ 4258c2ecf20Sopenharmony_ci if (packet[3] & 0x80) 4268c2ecf20Sopenharmony_ci fingers = 4; 4278c2ecf20Sopenharmony_ci fallthrough; 4288c2ecf20Sopenharmony_ci case 1: 4298c2ecf20Sopenharmony_ci /* 4308c2ecf20Sopenharmony_ci * byte 1: . . . . x11 x10 x9 x8 4318c2ecf20Sopenharmony_ci * byte 2: x7 x6 x5 x4 x4 x2 x1 x0 4328c2ecf20Sopenharmony_ci */ 4338c2ecf20Sopenharmony_ci x1 = ((packet[1] & 0x0f) << 8) | packet[2]; 4348c2ecf20Sopenharmony_ci /* 4358c2ecf20Sopenharmony_ci * byte 4: . . . . y11 y10 y9 y8 4368c2ecf20Sopenharmony_ci * byte 5: y7 y6 y5 y4 y3 y2 y1 y0 4378c2ecf20Sopenharmony_ci */ 4388c2ecf20Sopenharmony_ci y1 = etd->y_max - (((packet[4] & 0x0f) << 8) | packet[5]); 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci pres = (packet[1] & 0xf0) | ((packet[4] & 0xf0) >> 4); 4418c2ecf20Sopenharmony_ci width = ((packet[0] & 0x30) >> 2) | ((packet[3] & 0x30) >> 4); 4428c2ecf20Sopenharmony_ci break; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci case 2: 4458c2ecf20Sopenharmony_ci /* 4468c2ecf20Sopenharmony_ci * The coordinate of each finger is reported separately 4478c2ecf20Sopenharmony_ci * with a lower resolution for two finger touches: 4488c2ecf20Sopenharmony_ci * byte 0: . . ay8 ax8 . . . . 4498c2ecf20Sopenharmony_ci * byte 1: ax7 ax6 ax5 ax4 ax3 ax2 ax1 ax0 4508c2ecf20Sopenharmony_ci */ 4518c2ecf20Sopenharmony_ci x1 = (((packet[0] & 0x10) << 4) | packet[1]) << 2; 4528c2ecf20Sopenharmony_ci /* byte 2: ay7 ay6 ay5 ay4 ay3 ay2 ay1 ay0 */ 4538c2ecf20Sopenharmony_ci y1 = etd->y_max - 4548c2ecf20Sopenharmony_ci ((((packet[0] & 0x20) << 3) | packet[2]) << 2); 4558c2ecf20Sopenharmony_ci /* 4568c2ecf20Sopenharmony_ci * byte 3: . . by8 bx8 . . . . 4578c2ecf20Sopenharmony_ci * byte 4: bx7 bx6 bx5 bx4 bx3 bx2 bx1 bx0 4588c2ecf20Sopenharmony_ci */ 4598c2ecf20Sopenharmony_ci x2 = (((packet[3] & 0x10) << 4) | packet[4]) << 2; 4608c2ecf20Sopenharmony_ci /* byte 5: by7 by8 by5 by4 by3 by2 by1 by0 */ 4618c2ecf20Sopenharmony_ci y2 = etd->y_max - 4628c2ecf20Sopenharmony_ci ((((packet[3] & 0x20) << 3) | packet[5]) << 2); 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci /* Unknown so just report sensible values */ 4658c2ecf20Sopenharmony_ci pres = 127; 4668c2ecf20Sopenharmony_ci width = 7; 4678c2ecf20Sopenharmony_ci break; 4688c2ecf20Sopenharmony_ci } 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci input_report_key(dev, BTN_TOUCH, fingers != 0); 4718c2ecf20Sopenharmony_ci if (fingers != 0) { 4728c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_X, x1); 4738c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_Y, y1); 4748c2ecf20Sopenharmony_ci } 4758c2ecf20Sopenharmony_ci elantech_report_semi_mt_data(dev, fingers, x1, y1, x2, y2); 4768c2ecf20Sopenharmony_ci input_report_key(dev, BTN_TOOL_FINGER, fingers == 1); 4778c2ecf20Sopenharmony_ci input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2); 4788c2ecf20Sopenharmony_ci input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3); 4798c2ecf20Sopenharmony_ci input_report_key(dev, BTN_TOOL_QUADTAP, fingers == 4); 4808c2ecf20Sopenharmony_ci psmouse_report_standard_buttons(dev, packet[0]); 4818c2ecf20Sopenharmony_ci if (etd->info.reports_pressure) { 4828c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_PRESSURE, pres); 4838c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_TOOL_WIDTH, width); 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci input_sync(dev); 4878c2ecf20Sopenharmony_ci} 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_cistatic void elantech_report_trackpoint(struct psmouse *psmouse, 4908c2ecf20Sopenharmony_ci int packet_type) 4918c2ecf20Sopenharmony_ci{ 4928c2ecf20Sopenharmony_ci /* 4938c2ecf20Sopenharmony_ci * byte 0: 0 0 sx sy 0 M R L 4948c2ecf20Sopenharmony_ci * byte 1:~sx 0 0 0 0 0 0 0 4958c2ecf20Sopenharmony_ci * byte 2:~sy 0 0 0 0 0 0 0 4968c2ecf20Sopenharmony_ci * byte 3: 0 0 ~sy ~sx 0 1 1 0 4978c2ecf20Sopenharmony_ci * byte 4: x7 x6 x5 x4 x3 x2 x1 x0 4988c2ecf20Sopenharmony_ci * byte 5: y7 y6 y5 y4 y3 y2 y1 y0 4998c2ecf20Sopenharmony_ci * 5008c2ecf20Sopenharmony_ci * x and y are written in two's complement spread 5018c2ecf20Sopenharmony_ci * over 9 bits with sx/sy the relative top bit and 5028c2ecf20Sopenharmony_ci * x7..x0 and y7..y0 the lower bits. 5038c2ecf20Sopenharmony_ci * The sign of y is opposite to what the input driver 5048c2ecf20Sopenharmony_ci * expects for a relative movement 5058c2ecf20Sopenharmony_ci */ 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci struct elantech_data *etd = psmouse->private; 5088c2ecf20Sopenharmony_ci struct input_dev *tp_dev = etd->tp_dev; 5098c2ecf20Sopenharmony_ci unsigned char *packet = psmouse->packet; 5108c2ecf20Sopenharmony_ci int x, y; 5118c2ecf20Sopenharmony_ci u32 t; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci t = get_unaligned_le32(&packet[0]); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci switch (t & ~7U) { 5168c2ecf20Sopenharmony_ci case 0x06000030U: 5178c2ecf20Sopenharmony_ci case 0x16008020U: 5188c2ecf20Sopenharmony_ci case 0x26800010U: 5198c2ecf20Sopenharmony_ci case 0x36808000U: 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci /* 5228c2ecf20Sopenharmony_ci * This firmware misreport coordinates for trackpoint 5238c2ecf20Sopenharmony_ci * occasionally. Discard packets outside of [-127, 127] range 5248c2ecf20Sopenharmony_ci * to prevent cursor jumps. 5258c2ecf20Sopenharmony_ci */ 5268c2ecf20Sopenharmony_ci if (packet[4] == 0x80 || packet[5] == 0x80 || 5278c2ecf20Sopenharmony_ci packet[1] >> 7 == packet[4] >> 7 || 5288c2ecf20Sopenharmony_ci packet[2] >> 7 == packet[5] >> 7) { 5298c2ecf20Sopenharmony_ci elantech_debug("discarding packet [%6ph]\n", packet); 5308c2ecf20Sopenharmony_ci break; 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci } 5338c2ecf20Sopenharmony_ci x = packet[4] - (int)((packet[1]^0x80) << 1); 5348c2ecf20Sopenharmony_ci y = (int)((packet[2]^0x80) << 1) - packet[5]; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci psmouse_report_standard_buttons(tp_dev, packet[0]); 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci input_report_rel(tp_dev, REL_X, x); 5398c2ecf20Sopenharmony_ci input_report_rel(tp_dev, REL_Y, y); 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci input_sync(tp_dev); 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci break; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci default: 5468c2ecf20Sopenharmony_ci /* Dump unexpected packet sequences if debug=1 (default) */ 5478c2ecf20Sopenharmony_ci if (etd->info.debug == 1) 5488c2ecf20Sopenharmony_ci elantech_packet_dump(psmouse); 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci break; 5518c2ecf20Sopenharmony_ci } 5528c2ecf20Sopenharmony_ci} 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci/* 5558c2ecf20Sopenharmony_ci * Interpret complete data packets and report absolute mode input events for 5568c2ecf20Sopenharmony_ci * hardware version 3. (12 byte packets for two fingers) 5578c2ecf20Sopenharmony_ci */ 5588c2ecf20Sopenharmony_cistatic void elantech_report_absolute_v3(struct psmouse *psmouse, 5598c2ecf20Sopenharmony_ci int packet_type) 5608c2ecf20Sopenharmony_ci{ 5618c2ecf20Sopenharmony_ci struct input_dev *dev = psmouse->dev; 5628c2ecf20Sopenharmony_ci struct elantech_data *etd = psmouse->private; 5638c2ecf20Sopenharmony_ci unsigned char *packet = psmouse->packet; 5648c2ecf20Sopenharmony_ci unsigned int fingers = 0, x1 = 0, y1 = 0, x2 = 0, y2 = 0; 5658c2ecf20Sopenharmony_ci unsigned int width = 0, pres = 0; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci /* byte 0: n1 n0 . . . . R L */ 5688c2ecf20Sopenharmony_ci fingers = (packet[0] & 0xc0) >> 6; 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci switch (fingers) { 5718c2ecf20Sopenharmony_ci case 3: 5728c2ecf20Sopenharmony_ci case 1: 5738c2ecf20Sopenharmony_ci /* 5748c2ecf20Sopenharmony_ci * byte 1: . . . . x11 x10 x9 x8 5758c2ecf20Sopenharmony_ci * byte 2: x7 x6 x5 x4 x4 x2 x1 x0 5768c2ecf20Sopenharmony_ci */ 5778c2ecf20Sopenharmony_ci x1 = ((packet[1] & 0x0f) << 8) | packet[2]; 5788c2ecf20Sopenharmony_ci /* 5798c2ecf20Sopenharmony_ci * byte 4: . . . . y11 y10 y9 y8 5808c2ecf20Sopenharmony_ci * byte 5: y7 y6 y5 y4 y3 y2 y1 y0 5818c2ecf20Sopenharmony_ci */ 5828c2ecf20Sopenharmony_ci y1 = etd->y_max - (((packet[4] & 0x0f) << 8) | packet[5]); 5838c2ecf20Sopenharmony_ci break; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci case 2: 5868c2ecf20Sopenharmony_ci if (packet_type == PACKET_V3_HEAD) { 5878c2ecf20Sopenharmony_ci /* 5888c2ecf20Sopenharmony_ci * byte 1: . . . . ax11 ax10 ax9 ax8 5898c2ecf20Sopenharmony_ci * byte 2: ax7 ax6 ax5 ax4 ax3 ax2 ax1 ax0 5908c2ecf20Sopenharmony_ci */ 5918c2ecf20Sopenharmony_ci etd->mt[0].x = ((packet[1] & 0x0f) << 8) | packet[2]; 5928c2ecf20Sopenharmony_ci /* 5938c2ecf20Sopenharmony_ci * byte 4: . . . . ay11 ay10 ay9 ay8 5948c2ecf20Sopenharmony_ci * byte 5: ay7 ay6 ay5 ay4 ay3 ay2 ay1 ay0 5958c2ecf20Sopenharmony_ci */ 5968c2ecf20Sopenharmony_ci etd->mt[0].y = etd->y_max - 5978c2ecf20Sopenharmony_ci (((packet[4] & 0x0f) << 8) | packet[5]); 5988c2ecf20Sopenharmony_ci /* 5998c2ecf20Sopenharmony_ci * wait for next packet 6008c2ecf20Sopenharmony_ci */ 6018c2ecf20Sopenharmony_ci return; 6028c2ecf20Sopenharmony_ci } 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci /* packet_type == PACKET_V3_TAIL */ 6058c2ecf20Sopenharmony_ci x1 = etd->mt[0].x; 6068c2ecf20Sopenharmony_ci y1 = etd->mt[0].y; 6078c2ecf20Sopenharmony_ci x2 = ((packet[1] & 0x0f) << 8) | packet[2]; 6088c2ecf20Sopenharmony_ci y2 = etd->y_max - (((packet[4] & 0x0f) << 8) | packet[5]); 6098c2ecf20Sopenharmony_ci break; 6108c2ecf20Sopenharmony_ci } 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci pres = (packet[1] & 0xf0) | ((packet[4] & 0xf0) >> 4); 6138c2ecf20Sopenharmony_ci width = ((packet[0] & 0x30) >> 2) | ((packet[3] & 0x30) >> 4); 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci input_report_key(dev, BTN_TOUCH, fingers != 0); 6168c2ecf20Sopenharmony_ci if (fingers != 0) { 6178c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_X, x1); 6188c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_Y, y1); 6198c2ecf20Sopenharmony_ci } 6208c2ecf20Sopenharmony_ci elantech_report_semi_mt_data(dev, fingers, x1, y1, x2, y2); 6218c2ecf20Sopenharmony_ci input_report_key(dev, BTN_TOOL_FINGER, fingers == 1); 6228c2ecf20Sopenharmony_ci input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2); 6238c2ecf20Sopenharmony_ci input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3); 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci /* For clickpads map both buttons to BTN_LEFT */ 6268c2ecf20Sopenharmony_ci if (elantech_is_buttonpad(&etd->info)) 6278c2ecf20Sopenharmony_ci input_report_key(dev, BTN_LEFT, packet[0] & 0x03); 6288c2ecf20Sopenharmony_ci else 6298c2ecf20Sopenharmony_ci psmouse_report_standard_buttons(dev, packet[0]); 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_PRESSURE, pres); 6328c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_TOOL_WIDTH, width); 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci input_sync(dev); 6358c2ecf20Sopenharmony_ci} 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_cistatic void elantech_input_sync_v4(struct psmouse *psmouse) 6388c2ecf20Sopenharmony_ci{ 6398c2ecf20Sopenharmony_ci struct input_dev *dev = psmouse->dev; 6408c2ecf20Sopenharmony_ci struct elantech_data *etd = psmouse->private; 6418c2ecf20Sopenharmony_ci unsigned char *packet = psmouse->packet; 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci /* For clickpads map both buttons to BTN_LEFT */ 6448c2ecf20Sopenharmony_ci if (elantech_is_buttonpad(&etd->info)) 6458c2ecf20Sopenharmony_ci input_report_key(dev, BTN_LEFT, packet[0] & 0x03); 6468c2ecf20Sopenharmony_ci else 6478c2ecf20Sopenharmony_ci psmouse_report_standard_buttons(dev, packet[0]); 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci input_mt_report_pointer_emulation(dev, true); 6508c2ecf20Sopenharmony_ci input_sync(dev); 6518c2ecf20Sopenharmony_ci} 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_cistatic void process_packet_status_v4(struct psmouse *psmouse) 6548c2ecf20Sopenharmony_ci{ 6558c2ecf20Sopenharmony_ci struct input_dev *dev = psmouse->dev; 6568c2ecf20Sopenharmony_ci unsigned char *packet = psmouse->packet; 6578c2ecf20Sopenharmony_ci unsigned fingers; 6588c2ecf20Sopenharmony_ci int i; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci /* notify finger state change */ 6618c2ecf20Sopenharmony_ci fingers = packet[1] & 0x1f; 6628c2ecf20Sopenharmony_ci for (i = 0; i < ETP_MAX_FINGERS; i++) { 6638c2ecf20Sopenharmony_ci if ((fingers & (1 << i)) == 0) { 6648c2ecf20Sopenharmony_ci input_mt_slot(dev, i); 6658c2ecf20Sopenharmony_ci input_mt_report_slot_state(dev, MT_TOOL_FINGER, false); 6668c2ecf20Sopenharmony_ci } 6678c2ecf20Sopenharmony_ci } 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci elantech_input_sync_v4(psmouse); 6708c2ecf20Sopenharmony_ci} 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_cistatic void process_packet_head_v4(struct psmouse *psmouse) 6738c2ecf20Sopenharmony_ci{ 6748c2ecf20Sopenharmony_ci struct input_dev *dev = psmouse->dev; 6758c2ecf20Sopenharmony_ci struct elantech_data *etd = psmouse->private; 6768c2ecf20Sopenharmony_ci unsigned char *packet = psmouse->packet; 6778c2ecf20Sopenharmony_ci int id; 6788c2ecf20Sopenharmony_ci int pres, traces; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci id = ((packet[3] & 0xe0) >> 5) - 1; 6818c2ecf20Sopenharmony_ci if (id < 0 || id >= ETP_MAX_FINGERS) 6828c2ecf20Sopenharmony_ci return; 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci etd->mt[id].x = ((packet[1] & 0x0f) << 8) | packet[2]; 6858c2ecf20Sopenharmony_ci etd->mt[id].y = etd->y_max - (((packet[4] & 0x0f) << 8) | packet[5]); 6868c2ecf20Sopenharmony_ci pres = (packet[1] & 0xf0) | ((packet[4] & 0xf0) >> 4); 6878c2ecf20Sopenharmony_ci traces = (packet[0] & 0xf0) >> 4; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci input_mt_slot(dev, id); 6908c2ecf20Sopenharmony_ci input_mt_report_slot_state(dev, MT_TOOL_FINGER, true); 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_MT_POSITION_X, etd->mt[id].x); 6938c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_MT_POSITION_Y, etd->mt[id].y); 6948c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_MT_PRESSURE, pres); 6958c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_MT_TOUCH_MAJOR, traces * etd->width); 6968c2ecf20Sopenharmony_ci /* report this for backwards compatibility */ 6978c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_TOOL_WIDTH, traces); 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci elantech_input_sync_v4(psmouse); 7008c2ecf20Sopenharmony_ci} 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_cistatic void process_packet_motion_v4(struct psmouse *psmouse) 7038c2ecf20Sopenharmony_ci{ 7048c2ecf20Sopenharmony_ci struct input_dev *dev = psmouse->dev; 7058c2ecf20Sopenharmony_ci struct elantech_data *etd = psmouse->private; 7068c2ecf20Sopenharmony_ci unsigned char *packet = psmouse->packet; 7078c2ecf20Sopenharmony_ci int weight, delta_x1 = 0, delta_y1 = 0, delta_x2 = 0, delta_y2 = 0; 7088c2ecf20Sopenharmony_ci int id, sid; 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci id = ((packet[0] & 0xe0) >> 5) - 1; 7118c2ecf20Sopenharmony_ci if (id < 0 || id >= ETP_MAX_FINGERS) 7128c2ecf20Sopenharmony_ci return; 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci sid = ((packet[3] & 0xe0) >> 5) - 1; 7158c2ecf20Sopenharmony_ci weight = (packet[0] & 0x10) ? ETP_WEIGHT_VALUE : 1; 7168c2ecf20Sopenharmony_ci /* 7178c2ecf20Sopenharmony_ci * Motion packets give us the delta of x, y values of specific fingers, 7188c2ecf20Sopenharmony_ci * but in two's complement. Let the compiler do the conversion for us. 7198c2ecf20Sopenharmony_ci * Also _enlarge_ the numbers to int, in case of overflow. 7208c2ecf20Sopenharmony_ci */ 7218c2ecf20Sopenharmony_ci delta_x1 = (signed char)packet[1]; 7228c2ecf20Sopenharmony_ci delta_y1 = (signed char)packet[2]; 7238c2ecf20Sopenharmony_ci delta_x2 = (signed char)packet[4]; 7248c2ecf20Sopenharmony_ci delta_y2 = (signed char)packet[5]; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci etd->mt[id].x += delta_x1 * weight; 7278c2ecf20Sopenharmony_ci etd->mt[id].y -= delta_y1 * weight; 7288c2ecf20Sopenharmony_ci input_mt_slot(dev, id); 7298c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_MT_POSITION_X, etd->mt[id].x); 7308c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_MT_POSITION_Y, etd->mt[id].y); 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci if (sid >= 0 && sid < ETP_MAX_FINGERS) { 7338c2ecf20Sopenharmony_ci etd->mt[sid].x += delta_x2 * weight; 7348c2ecf20Sopenharmony_ci etd->mt[sid].y -= delta_y2 * weight; 7358c2ecf20Sopenharmony_ci input_mt_slot(dev, sid); 7368c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_MT_POSITION_X, etd->mt[sid].x); 7378c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_MT_POSITION_Y, etd->mt[sid].y); 7388c2ecf20Sopenharmony_ci } 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci elantech_input_sync_v4(psmouse); 7418c2ecf20Sopenharmony_ci} 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_cistatic void elantech_report_absolute_v4(struct psmouse *psmouse, 7448c2ecf20Sopenharmony_ci int packet_type) 7458c2ecf20Sopenharmony_ci{ 7468c2ecf20Sopenharmony_ci switch (packet_type) { 7478c2ecf20Sopenharmony_ci case PACKET_V4_STATUS: 7488c2ecf20Sopenharmony_ci process_packet_status_v4(psmouse); 7498c2ecf20Sopenharmony_ci break; 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci case PACKET_V4_HEAD: 7528c2ecf20Sopenharmony_ci process_packet_head_v4(psmouse); 7538c2ecf20Sopenharmony_ci break; 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci case PACKET_V4_MOTION: 7568c2ecf20Sopenharmony_ci process_packet_motion_v4(psmouse); 7578c2ecf20Sopenharmony_ci break; 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci case PACKET_UNKNOWN: 7608c2ecf20Sopenharmony_ci default: 7618c2ecf20Sopenharmony_ci /* impossible to get here */ 7628c2ecf20Sopenharmony_ci break; 7638c2ecf20Sopenharmony_ci } 7648c2ecf20Sopenharmony_ci} 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_cistatic int elantech_packet_check_v1(struct psmouse *psmouse) 7678c2ecf20Sopenharmony_ci{ 7688c2ecf20Sopenharmony_ci struct elantech_data *etd = psmouse->private; 7698c2ecf20Sopenharmony_ci unsigned char *packet = psmouse->packet; 7708c2ecf20Sopenharmony_ci unsigned char p1, p2, p3; 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci /* Parity bits are placed differently */ 7738c2ecf20Sopenharmony_ci if (etd->info.fw_version < 0x020000) { 7748c2ecf20Sopenharmony_ci /* byte 0: D U p1 p2 1 p3 R L */ 7758c2ecf20Sopenharmony_ci p1 = (packet[0] & 0x20) >> 5; 7768c2ecf20Sopenharmony_ci p2 = (packet[0] & 0x10) >> 4; 7778c2ecf20Sopenharmony_ci } else { 7788c2ecf20Sopenharmony_ci /* byte 0: n1 n0 p2 p1 1 p3 R L */ 7798c2ecf20Sopenharmony_ci p1 = (packet[0] & 0x10) >> 4; 7808c2ecf20Sopenharmony_ci p2 = (packet[0] & 0x20) >> 5; 7818c2ecf20Sopenharmony_ci } 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci p3 = (packet[0] & 0x04) >> 2; 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci return etd->parity[packet[1]] == p1 && 7868c2ecf20Sopenharmony_ci etd->parity[packet[2]] == p2 && 7878c2ecf20Sopenharmony_ci etd->parity[packet[3]] == p3; 7888c2ecf20Sopenharmony_ci} 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_cistatic int elantech_debounce_check_v2(struct psmouse *psmouse) 7918c2ecf20Sopenharmony_ci{ 7928c2ecf20Sopenharmony_ci /* 7938c2ecf20Sopenharmony_ci * When we encounter packet that matches this exactly, it means the 7948c2ecf20Sopenharmony_ci * hardware is in debounce status. Just ignore the whole packet. 7958c2ecf20Sopenharmony_ci */ 7968c2ecf20Sopenharmony_ci static const u8 debounce_packet[] = { 7978c2ecf20Sopenharmony_ci 0x84, 0xff, 0xff, 0x02, 0xff, 0xff 7988c2ecf20Sopenharmony_ci }; 7998c2ecf20Sopenharmony_ci unsigned char *packet = psmouse->packet; 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci return !memcmp(packet, debounce_packet, sizeof(debounce_packet)); 8028c2ecf20Sopenharmony_ci} 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_cistatic int elantech_packet_check_v2(struct psmouse *psmouse) 8058c2ecf20Sopenharmony_ci{ 8068c2ecf20Sopenharmony_ci struct elantech_data *etd = psmouse->private; 8078c2ecf20Sopenharmony_ci unsigned char *packet = psmouse->packet; 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci /* 8108c2ecf20Sopenharmony_ci * V2 hardware has two flavors. Older ones that do not report pressure, 8118c2ecf20Sopenharmony_ci * and newer ones that reports pressure and width. With newer ones, all 8128c2ecf20Sopenharmony_ci * packets (1, 2, 3 finger touch) have the same constant bits. With 8138c2ecf20Sopenharmony_ci * older ones, 1/3 finger touch packets and 2 finger touch packets 8148c2ecf20Sopenharmony_ci * have different constant bits. 8158c2ecf20Sopenharmony_ci * With all three cases, if the constant bits are not exactly what I 8168c2ecf20Sopenharmony_ci * expected, I consider them invalid. 8178c2ecf20Sopenharmony_ci */ 8188c2ecf20Sopenharmony_ci if (etd->info.reports_pressure) 8198c2ecf20Sopenharmony_ci return (packet[0] & 0x0c) == 0x04 && 8208c2ecf20Sopenharmony_ci (packet[3] & 0x0f) == 0x02; 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci if ((packet[0] & 0xc0) == 0x80) 8238c2ecf20Sopenharmony_ci return (packet[0] & 0x0c) == 0x0c && 8248c2ecf20Sopenharmony_ci (packet[3] & 0x0e) == 0x08; 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci return (packet[0] & 0x3c) == 0x3c && 8278c2ecf20Sopenharmony_ci (packet[1] & 0xf0) == 0x00 && 8288c2ecf20Sopenharmony_ci (packet[3] & 0x3e) == 0x38 && 8298c2ecf20Sopenharmony_ci (packet[4] & 0xf0) == 0x00; 8308c2ecf20Sopenharmony_ci} 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci/* 8338c2ecf20Sopenharmony_ci * We check the constant bits to determine what packet type we get, 8348c2ecf20Sopenharmony_ci * so packet checking is mandatory for v3 and later hardware. 8358c2ecf20Sopenharmony_ci */ 8368c2ecf20Sopenharmony_cistatic int elantech_packet_check_v3(struct psmouse *psmouse) 8378c2ecf20Sopenharmony_ci{ 8388c2ecf20Sopenharmony_ci struct elantech_data *etd = psmouse->private; 8398c2ecf20Sopenharmony_ci static const u8 debounce_packet[] = { 8408c2ecf20Sopenharmony_ci 0xc4, 0xff, 0xff, 0x02, 0xff, 0xff 8418c2ecf20Sopenharmony_ci }; 8428c2ecf20Sopenharmony_ci unsigned char *packet = psmouse->packet; 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci /* 8458c2ecf20Sopenharmony_ci * check debounce first, it has the same signature in byte 0 8468c2ecf20Sopenharmony_ci * and byte 3 as PACKET_V3_HEAD. 8478c2ecf20Sopenharmony_ci */ 8488c2ecf20Sopenharmony_ci if (!memcmp(packet, debounce_packet, sizeof(debounce_packet))) 8498c2ecf20Sopenharmony_ci return PACKET_DEBOUNCE; 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci /* 8528c2ecf20Sopenharmony_ci * If the hardware flag 'crc_enabled' is set the packets have 8538c2ecf20Sopenharmony_ci * different signatures. 8548c2ecf20Sopenharmony_ci */ 8558c2ecf20Sopenharmony_ci if (etd->info.crc_enabled) { 8568c2ecf20Sopenharmony_ci if ((packet[3] & 0x09) == 0x08) 8578c2ecf20Sopenharmony_ci return PACKET_V3_HEAD; 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci if ((packet[3] & 0x09) == 0x09) 8608c2ecf20Sopenharmony_ci return PACKET_V3_TAIL; 8618c2ecf20Sopenharmony_ci } else { 8628c2ecf20Sopenharmony_ci if ((packet[0] & 0x0c) == 0x04 && (packet[3] & 0xcf) == 0x02) 8638c2ecf20Sopenharmony_ci return PACKET_V3_HEAD; 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci if ((packet[0] & 0x0c) == 0x0c && (packet[3] & 0xce) == 0x0c) 8668c2ecf20Sopenharmony_ci return PACKET_V3_TAIL; 8678c2ecf20Sopenharmony_ci if ((packet[3] & 0x0f) == 0x06) 8688c2ecf20Sopenharmony_ci return PACKET_TRACKPOINT; 8698c2ecf20Sopenharmony_ci } 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci return PACKET_UNKNOWN; 8728c2ecf20Sopenharmony_ci} 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_cistatic int elantech_packet_check_v4(struct psmouse *psmouse) 8758c2ecf20Sopenharmony_ci{ 8768c2ecf20Sopenharmony_ci struct elantech_data *etd = psmouse->private; 8778c2ecf20Sopenharmony_ci unsigned char *packet = psmouse->packet; 8788c2ecf20Sopenharmony_ci unsigned char packet_type = packet[3] & 0x03; 8798c2ecf20Sopenharmony_ci unsigned int ic_version; 8808c2ecf20Sopenharmony_ci bool sanity_check; 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci if (etd->tp_dev && (packet[3] & 0x0f) == 0x06) 8838c2ecf20Sopenharmony_ci return PACKET_TRACKPOINT; 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci /* This represents the version of IC body. */ 8868c2ecf20Sopenharmony_ci ic_version = (etd->info.fw_version & 0x0f0000) >> 16; 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci /* 8898c2ecf20Sopenharmony_ci * Sanity check based on the constant bits of a packet. 8908c2ecf20Sopenharmony_ci * The constant bits change depending on the value of 8918c2ecf20Sopenharmony_ci * the hardware flag 'crc_enabled' and the version of 8928c2ecf20Sopenharmony_ci * the IC body, but are the same for every packet, 8938c2ecf20Sopenharmony_ci * regardless of the type. 8948c2ecf20Sopenharmony_ci */ 8958c2ecf20Sopenharmony_ci if (etd->info.crc_enabled) 8968c2ecf20Sopenharmony_ci sanity_check = ((packet[3] & 0x08) == 0x00); 8978c2ecf20Sopenharmony_ci else if (ic_version == 7 && etd->info.samples[1] == 0x2A) 8988c2ecf20Sopenharmony_ci sanity_check = ((packet[3] & 0x1c) == 0x10); 8998c2ecf20Sopenharmony_ci else 9008c2ecf20Sopenharmony_ci sanity_check = ((packet[0] & 0x08) == 0x00 && 9018c2ecf20Sopenharmony_ci (packet[3] & 0x1c) == 0x10); 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci if (!sanity_check) 9048c2ecf20Sopenharmony_ci return PACKET_UNKNOWN; 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci switch (packet_type) { 9078c2ecf20Sopenharmony_ci case 0: 9088c2ecf20Sopenharmony_ci return PACKET_V4_STATUS; 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci case 1: 9118c2ecf20Sopenharmony_ci return PACKET_V4_HEAD; 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci case 2: 9148c2ecf20Sopenharmony_ci return PACKET_V4_MOTION; 9158c2ecf20Sopenharmony_ci } 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci return PACKET_UNKNOWN; 9188c2ecf20Sopenharmony_ci} 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci/* 9218c2ecf20Sopenharmony_ci * Process byte stream from mouse and handle complete packets 9228c2ecf20Sopenharmony_ci */ 9238c2ecf20Sopenharmony_cistatic psmouse_ret_t elantech_process_byte(struct psmouse *psmouse) 9248c2ecf20Sopenharmony_ci{ 9258c2ecf20Sopenharmony_ci struct elantech_data *etd = psmouse->private; 9268c2ecf20Sopenharmony_ci int packet_type; 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci if (psmouse->pktcnt < psmouse->pktsize) 9298c2ecf20Sopenharmony_ci return PSMOUSE_GOOD_DATA; 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci if (etd->info.debug > 1) 9328c2ecf20Sopenharmony_ci elantech_packet_dump(psmouse); 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci switch (etd->info.hw_version) { 9358c2ecf20Sopenharmony_ci case 1: 9368c2ecf20Sopenharmony_ci if (etd->info.paritycheck && !elantech_packet_check_v1(psmouse)) 9378c2ecf20Sopenharmony_ci return PSMOUSE_BAD_DATA; 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci elantech_report_absolute_v1(psmouse); 9408c2ecf20Sopenharmony_ci break; 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci case 2: 9438c2ecf20Sopenharmony_ci /* ignore debounce */ 9448c2ecf20Sopenharmony_ci if (elantech_debounce_check_v2(psmouse)) 9458c2ecf20Sopenharmony_ci return PSMOUSE_FULL_PACKET; 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci if (etd->info.paritycheck && !elantech_packet_check_v2(psmouse)) 9488c2ecf20Sopenharmony_ci return PSMOUSE_BAD_DATA; 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci elantech_report_absolute_v2(psmouse); 9518c2ecf20Sopenharmony_ci break; 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci case 3: 9548c2ecf20Sopenharmony_ci packet_type = elantech_packet_check_v3(psmouse); 9558c2ecf20Sopenharmony_ci switch (packet_type) { 9568c2ecf20Sopenharmony_ci case PACKET_UNKNOWN: 9578c2ecf20Sopenharmony_ci return PSMOUSE_BAD_DATA; 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci case PACKET_DEBOUNCE: 9608c2ecf20Sopenharmony_ci /* ignore debounce */ 9618c2ecf20Sopenharmony_ci break; 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci case PACKET_TRACKPOINT: 9648c2ecf20Sopenharmony_ci elantech_report_trackpoint(psmouse, packet_type); 9658c2ecf20Sopenharmony_ci break; 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci default: 9688c2ecf20Sopenharmony_ci elantech_report_absolute_v3(psmouse, packet_type); 9698c2ecf20Sopenharmony_ci break; 9708c2ecf20Sopenharmony_ci } 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci break; 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci case 4: 9758c2ecf20Sopenharmony_ci packet_type = elantech_packet_check_v4(psmouse); 9768c2ecf20Sopenharmony_ci switch (packet_type) { 9778c2ecf20Sopenharmony_ci case PACKET_UNKNOWN: 9788c2ecf20Sopenharmony_ci return PSMOUSE_BAD_DATA; 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci case PACKET_TRACKPOINT: 9818c2ecf20Sopenharmony_ci elantech_report_trackpoint(psmouse, packet_type); 9828c2ecf20Sopenharmony_ci break; 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci default: 9858c2ecf20Sopenharmony_ci elantech_report_absolute_v4(psmouse, packet_type); 9868c2ecf20Sopenharmony_ci break; 9878c2ecf20Sopenharmony_ci } 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci break; 9908c2ecf20Sopenharmony_ci } 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci return PSMOUSE_FULL_PACKET; 9938c2ecf20Sopenharmony_ci} 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci/* 9968c2ecf20Sopenharmony_ci * This writes the reg_07 value again to the hardware at the end of every 9978c2ecf20Sopenharmony_ci * set_rate call because the register loses its value. reg_07 allows setting 9988c2ecf20Sopenharmony_ci * absolute mode on v4 hardware 9998c2ecf20Sopenharmony_ci */ 10008c2ecf20Sopenharmony_cistatic void elantech_set_rate_restore_reg_07(struct psmouse *psmouse, 10018c2ecf20Sopenharmony_ci unsigned int rate) 10028c2ecf20Sopenharmony_ci{ 10038c2ecf20Sopenharmony_ci struct elantech_data *etd = psmouse->private; 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci etd->original_set_rate(psmouse, rate); 10068c2ecf20Sopenharmony_ci if (elantech_write_reg(psmouse, 0x07, etd->reg_07)) 10078c2ecf20Sopenharmony_ci psmouse_err(psmouse, "restoring reg_07 failed\n"); 10088c2ecf20Sopenharmony_ci} 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci/* 10118c2ecf20Sopenharmony_ci * Put the touchpad into absolute mode 10128c2ecf20Sopenharmony_ci */ 10138c2ecf20Sopenharmony_cistatic int elantech_set_absolute_mode(struct psmouse *psmouse) 10148c2ecf20Sopenharmony_ci{ 10158c2ecf20Sopenharmony_ci struct elantech_data *etd = psmouse->private; 10168c2ecf20Sopenharmony_ci unsigned char val; 10178c2ecf20Sopenharmony_ci int tries = ETP_READ_BACK_TRIES; 10188c2ecf20Sopenharmony_ci int rc = 0; 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci switch (etd->info.hw_version) { 10218c2ecf20Sopenharmony_ci case 1: 10228c2ecf20Sopenharmony_ci etd->reg_10 = 0x16; 10238c2ecf20Sopenharmony_ci etd->reg_11 = 0x8f; 10248c2ecf20Sopenharmony_ci if (elantech_write_reg(psmouse, 0x10, etd->reg_10) || 10258c2ecf20Sopenharmony_ci elantech_write_reg(psmouse, 0x11, etd->reg_11)) { 10268c2ecf20Sopenharmony_ci rc = -1; 10278c2ecf20Sopenharmony_ci } 10288c2ecf20Sopenharmony_ci break; 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci case 2: 10318c2ecf20Sopenharmony_ci /* Windows driver values */ 10328c2ecf20Sopenharmony_ci etd->reg_10 = 0x54; 10338c2ecf20Sopenharmony_ci etd->reg_11 = 0x88; /* 0x8a */ 10348c2ecf20Sopenharmony_ci etd->reg_21 = 0x60; /* 0x00 */ 10358c2ecf20Sopenharmony_ci if (elantech_write_reg(psmouse, 0x10, etd->reg_10) || 10368c2ecf20Sopenharmony_ci elantech_write_reg(psmouse, 0x11, etd->reg_11) || 10378c2ecf20Sopenharmony_ci elantech_write_reg(psmouse, 0x21, etd->reg_21)) { 10388c2ecf20Sopenharmony_ci rc = -1; 10398c2ecf20Sopenharmony_ci } 10408c2ecf20Sopenharmony_ci break; 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci case 3: 10438c2ecf20Sopenharmony_ci if (etd->info.set_hw_resolution) 10448c2ecf20Sopenharmony_ci etd->reg_10 = 0x0b; 10458c2ecf20Sopenharmony_ci else 10468c2ecf20Sopenharmony_ci etd->reg_10 = 0x01; 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci if (elantech_write_reg(psmouse, 0x10, etd->reg_10)) 10498c2ecf20Sopenharmony_ci rc = -1; 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci break; 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci case 4: 10548c2ecf20Sopenharmony_ci etd->reg_07 = 0x01; 10558c2ecf20Sopenharmony_ci if (elantech_write_reg(psmouse, 0x07, etd->reg_07)) 10568c2ecf20Sopenharmony_ci rc = -1; 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci goto skip_readback_reg_10; /* v4 has no reg 0x10 to read */ 10598c2ecf20Sopenharmony_ci } 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci if (rc == 0) { 10628c2ecf20Sopenharmony_ci /* 10638c2ecf20Sopenharmony_ci * Read back reg 0x10. For hardware version 1 we must make 10648c2ecf20Sopenharmony_ci * sure the absolute mode bit is set. For hardware version 2 10658c2ecf20Sopenharmony_ci * the touchpad is probably initializing and not ready until 10668c2ecf20Sopenharmony_ci * we read back the value we just wrote. 10678c2ecf20Sopenharmony_ci */ 10688c2ecf20Sopenharmony_ci do { 10698c2ecf20Sopenharmony_ci rc = elantech_read_reg(psmouse, 0x10, &val); 10708c2ecf20Sopenharmony_ci if (rc == 0) 10718c2ecf20Sopenharmony_ci break; 10728c2ecf20Sopenharmony_ci tries--; 10738c2ecf20Sopenharmony_ci elantech_debug("retrying read (%d).\n", tries); 10748c2ecf20Sopenharmony_ci msleep(ETP_READ_BACK_DELAY); 10758c2ecf20Sopenharmony_ci } while (tries > 0); 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci if (rc) { 10788c2ecf20Sopenharmony_ci psmouse_err(psmouse, 10798c2ecf20Sopenharmony_ci "failed to read back register 0x10.\n"); 10808c2ecf20Sopenharmony_ci } else if (etd->info.hw_version == 1 && 10818c2ecf20Sopenharmony_ci !(val & ETP_R10_ABSOLUTE_MODE)) { 10828c2ecf20Sopenharmony_ci psmouse_err(psmouse, 10838c2ecf20Sopenharmony_ci "touchpad refuses to switch to absolute mode.\n"); 10848c2ecf20Sopenharmony_ci rc = -1; 10858c2ecf20Sopenharmony_ci } 10868c2ecf20Sopenharmony_ci } 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci skip_readback_reg_10: 10898c2ecf20Sopenharmony_ci if (rc) 10908c2ecf20Sopenharmony_ci psmouse_err(psmouse, "failed to initialise registers.\n"); 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci return rc; 10938c2ecf20Sopenharmony_ci} 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci/* 10968c2ecf20Sopenharmony_ci * (value from firmware) * 10 + 790 = dpi 10978c2ecf20Sopenharmony_ci * we also have to convert dpi to dots/mm (*10/254 to avoid floating point) 10988c2ecf20Sopenharmony_ci */ 10998c2ecf20Sopenharmony_cistatic unsigned int elantech_convert_res(unsigned int val) 11008c2ecf20Sopenharmony_ci{ 11018c2ecf20Sopenharmony_ci return (val * 10 + 790) * 10 / 254; 11028c2ecf20Sopenharmony_ci} 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_cistatic int elantech_get_resolution_v4(struct psmouse *psmouse, 11058c2ecf20Sopenharmony_ci unsigned int *x_res, 11068c2ecf20Sopenharmony_ci unsigned int *y_res, 11078c2ecf20Sopenharmony_ci unsigned int *bus) 11088c2ecf20Sopenharmony_ci{ 11098c2ecf20Sopenharmony_ci unsigned char param[3]; 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci if (elantech_send_cmd(psmouse, ETP_RESOLUTION_QUERY, param)) 11128c2ecf20Sopenharmony_ci return -1; 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_ci *x_res = elantech_convert_res(param[1] & 0x0f); 11158c2ecf20Sopenharmony_ci *y_res = elantech_convert_res((param[1] & 0xf0) >> 4); 11168c2ecf20Sopenharmony_ci *bus = param[2]; 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci return 0; 11198c2ecf20Sopenharmony_ci} 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_cistatic void elantech_set_buttonpad_prop(struct psmouse *psmouse) 11228c2ecf20Sopenharmony_ci{ 11238c2ecf20Sopenharmony_ci struct input_dev *dev = psmouse->dev; 11248c2ecf20Sopenharmony_ci struct elantech_data *etd = psmouse->private; 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci if (elantech_is_buttonpad(&etd->info)) { 11278c2ecf20Sopenharmony_ci __set_bit(INPUT_PROP_BUTTONPAD, dev->propbit); 11288c2ecf20Sopenharmony_ci __clear_bit(BTN_RIGHT, dev->keybit); 11298c2ecf20Sopenharmony_ci } 11308c2ecf20Sopenharmony_ci} 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci/* 11338c2ecf20Sopenharmony_ci * Some hw_version 4 models do have a middle button 11348c2ecf20Sopenharmony_ci */ 11358c2ecf20Sopenharmony_cistatic const struct dmi_system_id elantech_dmi_has_middle_button[] = { 11368c2ecf20Sopenharmony_ci#if defined(CONFIG_DMI) && defined(CONFIG_X86) 11378c2ecf20Sopenharmony_ci { 11388c2ecf20Sopenharmony_ci /* Fujitsu H730 has a middle button */ 11398c2ecf20Sopenharmony_ci .matches = { 11408c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), 11418c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "CELSIUS H730"), 11428c2ecf20Sopenharmony_ci }, 11438c2ecf20Sopenharmony_ci }, 11448c2ecf20Sopenharmony_ci { 11458c2ecf20Sopenharmony_ci /* Fujitsu H760 also has a middle button */ 11468c2ecf20Sopenharmony_ci .matches = { 11478c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), 11488c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "CELSIUS H760"), 11498c2ecf20Sopenharmony_ci }, 11508c2ecf20Sopenharmony_ci }, 11518c2ecf20Sopenharmony_ci { 11528c2ecf20Sopenharmony_ci /* Fujitsu H780 also has a middle button */ 11538c2ecf20Sopenharmony_ci .matches = { 11548c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), 11558c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "CELSIUS H780"), 11568c2ecf20Sopenharmony_ci }, 11578c2ecf20Sopenharmony_ci }, 11588c2ecf20Sopenharmony_ci#endif 11598c2ecf20Sopenharmony_ci { } 11608c2ecf20Sopenharmony_ci}; 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci/* 11638c2ecf20Sopenharmony_ci * Set the appropriate event bits for the input subsystem 11648c2ecf20Sopenharmony_ci */ 11658c2ecf20Sopenharmony_cistatic int elantech_set_input_params(struct psmouse *psmouse) 11668c2ecf20Sopenharmony_ci{ 11678c2ecf20Sopenharmony_ci struct input_dev *dev = psmouse->dev; 11688c2ecf20Sopenharmony_ci struct elantech_data *etd = psmouse->private; 11698c2ecf20Sopenharmony_ci struct elantech_device_info *info = &etd->info; 11708c2ecf20Sopenharmony_ci unsigned int x_min = info->x_min, y_min = info->y_min, 11718c2ecf20Sopenharmony_ci x_max = info->x_max, y_max = info->y_max, 11728c2ecf20Sopenharmony_ci width = info->width; 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci __set_bit(INPUT_PROP_POINTER, dev->propbit); 11758c2ecf20Sopenharmony_ci __set_bit(EV_KEY, dev->evbit); 11768c2ecf20Sopenharmony_ci __set_bit(EV_ABS, dev->evbit); 11778c2ecf20Sopenharmony_ci __clear_bit(EV_REL, dev->evbit); 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_ci __set_bit(BTN_LEFT, dev->keybit); 11808c2ecf20Sopenharmony_ci if (info->has_middle_button) 11818c2ecf20Sopenharmony_ci __set_bit(BTN_MIDDLE, dev->keybit); 11828c2ecf20Sopenharmony_ci __set_bit(BTN_RIGHT, dev->keybit); 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci __set_bit(BTN_TOUCH, dev->keybit); 11858c2ecf20Sopenharmony_ci __set_bit(BTN_TOOL_FINGER, dev->keybit); 11868c2ecf20Sopenharmony_ci __set_bit(BTN_TOOL_DOUBLETAP, dev->keybit); 11878c2ecf20Sopenharmony_ci __set_bit(BTN_TOOL_TRIPLETAP, dev->keybit); 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_ci switch (info->hw_version) { 11908c2ecf20Sopenharmony_ci case 1: 11918c2ecf20Sopenharmony_ci /* Rocker button */ 11928c2ecf20Sopenharmony_ci if (info->fw_version < 0x020000 && 11938c2ecf20Sopenharmony_ci (info->capabilities[0] & ETP_CAP_HAS_ROCKER)) { 11948c2ecf20Sopenharmony_ci __set_bit(BTN_FORWARD, dev->keybit); 11958c2ecf20Sopenharmony_ci __set_bit(BTN_BACK, dev->keybit); 11968c2ecf20Sopenharmony_ci } 11978c2ecf20Sopenharmony_ci input_set_abs_params(dev, ABS_X, x_min, x_max, 0, 0); 11988c2ecf20Sopenharmony_ci input_set_abs_params(dev, ABS_Y, y_min, y_max, 0, 0); 11998c2ecf20Sopenharmony_ci break; 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_ci case 2: 12028c2ecf20Sopenharmony_ci __set_bit(BTN_TOOL_QUADTAP, dev->keybit); 12038c2ecf20Sopenharmony_ci __set_bit(INPUT_PROP_SEMI_MT, dev->propbit); 12048c2ecf20Sopenharmony_ci fallthrough; 12058c2ecf20Sopenharmony_ci case 3: 12068c2ecf20Sopenharmony_ci if (info->hw_version == 3) 12078c2ecf20Sopenharmony_ci elantech_set_buttonpad_prop(psmouse); 12088c2ecf20Sopenharmony_ci input_set_abs_params(dev, ABS_X, x_min, x_max, 0, 0); 12098c2ecf20Sopenharmony_ci input_set_abs_params(dev, ABS_Y, y_min, y_max, 0, 0); 12108c2ecf20Sopenharmony_ci if (info->reports_pressure) { 12118c2ecf20Sopenharmony_ci input_set_abs_params(dev, ABS_PRESSURE, ETP_PMIN_V2, 12128c2ecf20Sopenharmony_ci ETP_PMAX_V2, 0, 0); 12138c2ecf20Sopenharmony_ci input_set_abs_params(dev, ABS_TOOL_WIDTH, ETP_WMIN_V2, 12148c2ecf20Sopenharmony_ci ETP_WMAX_V2, 0, 0); 12158c2ecf20Sopenharmony_ci } 12168c2ecf20Sopenharmony_ci input_mt_init_slots(dev, 2, INPUT_MT_SEMI_MT); 12178c2ecf20Sopenharmony_ci input_set_abs_params(dev, ABS_MT_POSITION_X, x_min, x_max, 0, 0); 12188c2ecf20Sopenharmony_ci input_set_abs_params(dev, ABS_MT_POSITION_Y, y_min, y_max, 0, 0); 12198c2ecf20Sopenharmony_ci break; 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_ci case 4: 12228c2ecf20Sopenharmony_ci elantech_set_buttonpad_prop(psmouse); 12238c2ecf20Sopenharmony_ci __set_bit(BTN_TOOL_QUADTAP, dev->keybit); 12248c2ecf20Sopenharmony_ci /* For X to recognize me as touchpad. */ 12258c2ecf20Sopenharmony_ci input_set_abs_params(dev, ABS_X, x_min, x_max, 0, 0); 12268c2ecf20Sopenharmony_ci input_set_abs_params(dev, ABS_Y, y_min, y_max, 0, 0); 12278c2ecf20Sopenharmony_ci /* 12288c2ecf20Sopenharmony_ci * range of pressure and width is the same as v2, 12298c2ecf20Sopenharmony_ci * report ABS_PRESSURE, ABS_TOOL_WIDTH for compatibility. 12308c2ecf20Sopenharmony_ci */ 12318c2ecf20Sopenharmony_ci input_set_abs_params(dev, ABS_PRESSURE, ETP_PMIN_V2, 12328c2ecf20Sopenharmony_ci ETP_PMAX_V2, 0, 0); 12338c2ecf20Sopenharmony_ci input_set_abs_params(dev, ABS_TOOL_WIDTH, ETP_WMIN_V2, 12348c2ecf20Sopenharmony_ci ETP_WMAX_V2, 0, 0); 12358c2ecf20Sopenharmony_ci /* Multitouch capable pad, up to 5 fingers. */ 12368c2ecf20Sopenharmony_ci input_mt_init_slots(dev, ETP_MAX_FINGERS, 0); 12378c2ecf20Sopenharmony_ci input_set_abs_params(dev, ABS_MT_POSITION_X, x_min, x_max, 0, 0); 12388c2ecf20Sopenharmony_ci input_set_abs_params(dev, ABS_MT_POSITION_Y, y_min, y_max, 0, 0); 12398c2ecf20Sopenharmony_ci input_set_abs_params(dev, ABS_MT_PRESSURE, ETP_PMIN_V2, 12408c2ecf20Sopenharmony_ci ETP_PMAX_V2, 0, 0); 12418c2ecf20Sopenharmony_ci /* 12428c2ecf20Sopenharmony_ci * The firmware reports how many trace lines the finger spans, 12438c2ecf20Sopenharmony_ci * convert to surface unit as Protocol-B requires. 12448c2ecf20Sopenharmony_ci */ 12458c2ecf20Sopenharmony_ci input_set_abs_params(dev, ABS_MT_TOUCH_MAJOR, 0, 12468c2ecf20Sopenharmony_ci ETP_WMAX_V2 * width, 0, 0); 12478c2ecf20Sopenharmony_ci break; 12488c2ecf20Sopenharmony_ci } 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ci input_abs_set_res(dev, ABS_X, info->x_res); 12518c2ecf20Sopenharmony_ci input_abs_set_res(dev, ABS_Y, info->y_res); 12528c2ecf20Sopenharmony_ci if (info->hw_version > 1) { 12538c2ecf20Sopenharmony_ci input_abs_set_res(dev, ABS_MT_POSITION_X, info->x_res); 12548c2ecf20Sopenharmony_ci input_abs_set_res(dev, ABS_MT_POSITION_Y, info->y_res); 12558c2ecf20Sopenharmony_ci } 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_ci etd->y_max = y_max; 12588c2ecf20Sopenharmony_ci etd->width = width; 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_ci return 0; 12618c2ecf20Sopenharmony_ci} 12628c2ecf20Sopenharmony_ci 12638c2ecf20Sopenharmony_cistruct elantech_attr_data { 12648c2ecf20Sopenharmony_ci size_t field_offset; 12658c2ecf20Sopenharmony_ci unsigned char reg; 12668c2ecf20Sopenharmony_ci}; 12678c2ecf20Sopenharmony_ci 12688c2ecf20Sopenharmony_ci/* 12698c2ecf20Sopenharmony_ci * Display a register value by reading a sysfs entry 12708c2ecf20Sopenharmony_ci */ 12718c2ecf20Sopenharmony_cistatic ssize_t elantech_show_int_attr(struct psmouse *psmouse, void *data, 12728c2ecf20Sopenharmony_ci char *buf) 12738c2ecf20Sopenharmony_ci{ 12748c2ecf20Sopenharmony_ci struct elantech_data *etd = psmouse->private; 12758c2ecf20Sopenharmony_ci struct elantech_attr_data *attr = data; 12768c2ecf20Sopenharmony_ci unsigned char *reg = (unsigned char *) etd + attr->field_offset; 12778c2ecf20Sopenharmony_ci int rc = 0; 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_ci if (attr->reg) 12808c2ecf20Sopenharmony_ci rc = elantech_read_reg(psmouse, attr->reg, reg); 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_ci return sprintf(buf, "0x%02x\n", (attr->reg && rc) ? -1 : *reg); 12838c2ecf20Sopenharmony_ci} 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_ci/* 12868c2ecf20Sopenharmony_ci * Write a register value by writing a sysfs entry 12878c2ecf20Sopenharmony_ci */ 12888c2ecf20Sopenharmony_cistatic ssize_t elantech_set_int_attr(struct psmouse *psmouse, 12898c2ecf20Sopenharmony_ci void *data, const char *buf, size_t count) 12908c2ecf20Sopenharmony_ci{ 12918c2ecf20Sopenharmony_ci struct elantech_data *etd = psmouse->private; 12928c2ecf20Sopenharmony_ci struct elantech_attr_data *attr = data; 12938c2ecf20Sopenharmony_ci unsigned char *reg = (unsigned char *) etd + attr->field_offset; 12948c2ecf20Sopenharmony_ci unsigned char value; 12958c2ecf20Sopenharmony_ci int err; 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_ci err = kstrtou8(buf, 16, &value); 12988c2ecf20Sopenharmony_ci if (err) 12998c2ecf20Sopenharmony_ci return err; 13008c2ecf20Sopenharmony_ci 13018c2ecf20Sopenharmony_ci /* Do we need to preserve some bits for version 2 hardware too? */ 13028c2ecf20Sopenharmony_ci if (etd->info.hw_version == 1) { 13038c2ecf20Sopenharmony_ci if (attr->reg == 0x10) 13048c2ecf20Sopenharmony_ci /* Force absolute mode always on */ 13058c2ecf20Sopenharmony_ci value |= ETP_R10_ABSOLUTE_MODE; 13068c2ecf20Sopenharmony_ci else if (attr->reg == 0x11) 13078c2ecf20Sopenharmony_ci /* Force 4 byte mode always on */ 13088c2ecf20Sopenharmony_ci value |= ETP_R11_4_BYTE_MODE; 13098c2ecf20Sopenharmony_ci } 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_ci if (!attr->reg || elantech_write_reg(psmouse, attr->reg, value) == 0) 13128c2ecf20Sopenharmony_ci *reg = value; 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_ci return count; 13158c2ecf20Sopenharmony_ci} 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_ci#define ELANTECH_INT_ATTR(_name, _register) \ 13188c2ecf20Sopenharmony_ci static struct elantech_attr_data elantech_attr_##_name = { \ 13198c2ecf20Sopenharmony_ci .field_offset = offsetof(struct elantech_data, _name), \ 13208c2ecf20Sopenharmony_ci .reg = _register, \ 13218c2ecf20Sopenharmony_ci }; \ 13228c2ecf20Sopenharmony_ci PSMOUSE_DEFINE_ATTR(_name, 0644, \ 13238c2ecf20Sopenharmony_ci &elantech_attr_##_name, \ 13248c2ecf20Sopenharmony_ci elantech_show_int_attr, \ 13258c2ecf20Sopenharmony_ci elantech_set_int_attr) 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_ci#define ELANTECH_INFO_ATTR(_name) \ 13288c2ecf20Sopenharmony_ci static struct elantech_attr_data elantech_attr_##_name = { \ 13298c2ecf20Sopenharmony_ci .field_offset = offsetof(struct elantech_data, info) + \ 13308c2ecf20Sopenharmony_ci offsetof(struct elantech_device_info, _name), \ 13318c2ecf20Sopenharmony_ci .reg = 0, \ 13328c2ecf20Sopenharmony_ci }; \ 13338c2ecf20Sopenharmony_ci PSMOUSE_DEFINE_ATTR(_name, 0644, \ 13348c2ecf20Sopenharmony_ci &elantech_attr_##_name, \ 13358c2ecf20Sopenharmony_ci elantech_show_int_attr, \ 13368c2ecf20Sopenharmony_ci elantech_set_int_attr) 13378c2ecf20Sopenharmony_ci 13388c2ecf20Sopenharmony_ciELANTECH_INT_ATTR(reg_07, 0x07); 13398c2ecf20Sopenharmony_ciELANTECH_INT_ATTR(reg_10, 0x10); 13408c2ecf20Sopenharmony_ciELANTECH_INT_ATTR(reg_11, 0x11); 13418c2ecf20Sopenharmony_ciELANTECH_INT_ATTR(reg_20, 0x20); 13428c2ecf20Sopenharmony_ciELANTECH_INT_ATTR(reg_21, 0x21); 13438c2ecf20Sopenharmony_ciELANTECH_INT_ATTR(reg_22, 0x22); 13448c2ecf20Sopenharmony_ciELANTECH_INT_ATTR(reg_23, 0x23); 13458c2ecf20Sopenharmony_ciELANTECH_INT_ATTR(reg_24, 0x24); 13468c2ecf20Sopenharmony_ciELANTECH_INT_ATTR(reg_25, 0x25); 13478c2ecf20Sopenharmony_ciELANTECH_INT_ATTR(reg_26, 0x26); 13488c2ecf20Sopenharmony_ciELANTECH_INFO_ATTR(debug); 13498c2ecf20Sopenharmony_ciELANTECH_INFO_ATTR(paritycheck); 13508c2ecf20Sopenharmony_ciELANTECH_INFO_ATTR(crc_enabled); 13518c2ecf20Sopenharmony_ci 13528c2ecf20Sopenharmony_cistatic struct attribute *elantech_attrs[] = { 13538c2ecf20Sopenharmony_ci &psmouse_attr_reg_07.dattr.attr, 13548c2ecf20Sopenharmony_ci &psmouse_attr_reg_10.dattr.attr, 13558c2ecf20Sopenharmony_ci &psmouse_attr_reg_11.dattr.attr, 13568c2ecf20Sopenharmony_ci &psmouse_attr_reg_20.dattr.attr, 13578c2ecf20Sopenharmony_ci &psmouse_attr_reg_21.dattr.attr, 13588c2ecf20Sopenharmony_ci &psmouse_attr_reg_22.dattr.attr, 13598c2ecf20Sopenharmony_ci &psmouse_attr_reg_23.dattr.attr, 13608c2ecf20Sopenharmony_ci &psmouse_attr_reg_24.dattr.attr, 13618c2ecf20Sopenharmony_ci &psmouse_attr_reg_25.dattr.attr, 13628c2ecf20Sopenharmony_ci &psmouse_attr_reg_26.dattr.attr, 13638c2ecf20Sopenharmony_ci &psmouse_attr_debug.dattr.attr, 13648c2ecf20Sopenharmony_ci &psmouse_attr_paritycheck.dattr.attr, 13658c2ecf20Sopenharmony_ci &psmouse_attr_crc_enabled.dattr.attr, 13668c2ecf20Sopenharmony_ci NULL 13678c2ecf20Sopenharmony_ci}; 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_cistatic const struct attribute_group elantech_attr_group = { 13708c2ecf20Sopenharmony_ci .attrs = elantech_attrs, 13718c2ecf20Sopenharmony_ci}; 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_cistatic bool elantech_is_signature_valid(const unsigned char *param) 13748c2ecf20Sopenharmony_ci{ 13758c2ecf20Sopenharmony_ci static const unsigned char rates[] = { 200, 100, 80, 60, 40, 20, 10 }; 13768c2ecf20Sopenharmony_ci int i; 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_ci if (param[0] == 0) 13798c2ecf20Sopenharmony_ci return false; 13808c2ecf20Sopenharmony_ci 13818c2ecf20Sopenharmony_ci if (param[1] == 0) 13828c2ecf20Sopenharmony_ci return true; 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_ci /* 13858c2ecf20Sopenharmony_ci * Some hw_version >= 4 models have a revision higher then 20. Meaning 13868c2ecf20Sopenharmony_ci * that param[2] may be 10 or 20, skip the rates check for these. 13878c2ecf20Sopenharmony_ci */ 13888c2ecf20Sopenharmony_ci if ((param[0] & 0x0f) >= 0x06 && (param[1] & 0xaf) == 0x0f && 13898c2ecf20Sopenharmony_ci param[2] < 40) 13908c2ecf20Sopenharmony_ci return true; 13918c2ecf20Sopenharmony_ci 13928c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(rates); i++) 13938c2ecf20Sopenharmony_ci if (param[2] == rates[i]) 13948c2ecf20Sopenharmony_ci return false; 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_ci return true; 13978c2ecf20Sopenharmony_ci} 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_ci/* 14008c2ecf20Sopenharmony_ci * Use magic knock to detect Elantech touchpad 14018c2ecf20Sopenharmony_ci */ 14028c2ecf20Sopenharmony_ciint elantech_detect(struct psmouse *psmouse, bool set_properties) 14038c2ecf20Sopenharmony_ci{ 14048c2ecf20Sopenharmony_ci struct ps2dev *ps2dev = &psmouse->ps2dev; 14058c2ecf20Sopenharmony_ci unsigned char param[3]; 14068c2ecf20Sopenharmony_ci 14078c2ecf20Sopenharmony_ci ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_DIS); 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_ci if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || 14108c2ecf20Sopenharmony_ci ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || 14118c2ecf20Sopenharmony_ci ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || 14128c2ecf20Sopenharmony_ci ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || 14138c2ecf20Sopenharmony_ci ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) { 14148c2ecf20Sopenharmony_ci psmouse_dbg(psmouse, "sending Elantech magic knock failed.\n"); 14158c2ecf20Sopenharmony_ci return -1; 14168c2ecf20Sopenharmony_ci } 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_ci /* 14198c2ecf20Sopenharmony_ci * Report this in case there are Elantech models that use a different 14208c2ecf20Sopenharmony_ci * set of magic numbers 14218c2ecf20Sopenharmony_ci */ 14228c2ecf20Sopenharmony_ci if (param[0] != 0x3c || param[1] != 0x03 || 14238c2ecf20Sopenharmony_ci (param[2] != 0xc8 && param[2] != 0x00)) { 14248c2ecf20Sopenharmony_ci psmouse_dbg(psmouse, 14258c2ecf20Sopenharmony_ci "unexpected magic knock result 0x%02x, 0x%02x, 0x%02x.\n", 14268c2ecf20Sopenharmony_ci param[0], param[1], param[2]); 14278c2ecf20Sopenharmony_ci return -1; 14288c2ecf20Sopenharmony_ci } 14298c2ecf20Sopenharmony_ci 14308c2ecf20Sopenharmony_ci /* 14318c2ecf20Sopenharmony_ci * Query touchpad's firmware version and see if it reports known 14328c2ecf20Sopenharmony_ci * value to avoid mis-detection. Logitech mice are known to respond 14338c2ecf20Sopenharmony_ci * to Elantech magic knock and there might be more. 14348c2ecf20Sopenharmony_ci */ 14358c2ecf20Sopenharmony_ci if (synaptics_send_cmd(psmouse, ETP_FW_VERSION_QUERY, param)) { 14368c2ecf20Sopenharmony_ci psmouse_dbg(psmouse, "failed to query firmware version.\n"); 14378c2ecf20Sopenharmony_ci return -1; 14388c2ecf20Sopenharmony_ci } 14398c2ecf20Sopenharmony_ci 14408c2ecf20Sopenharmony_ci psmouse_dbg(psmouse, 14418c2ecf20Sopenharmony_ci "Elantech version query result 0x%02x, 0x%02x, 0x%02x.\n", 14428c2ecf20Sopenharmony_ci param[0], param[1], param[2]); 14438c2ecf20Sopenharmony_ci 14448c2ecf20Sopenharmony_ci if (!elantech_is_signature_valid(param)) { 14458c2ecf20Sopenharmony_ci psmouse_dbg(psmouse, 14468c2ecf20Sopenharmony_ci "Probably not a real Elantech touchpad. Aborting.\n"); 14478c2ecf20Sopenharmony_ci return -1; 14488c2ecf20Sopenharmony_ci } 14498c2ecf20Sopenharmony_ci 14508c2ecf20Sopenharmony_ci if (set_properties) { 14518c2ecf20Sopenharmony_ci psmouse->vendor = "Elantech"; 14528c2ecf20Sopenharmony_ci psmouse->name = "Touchpad"; 14538c2ecf20Sopenharmony_ci } 14548c2ecf20Sopenharmony_ci 14558c2ecf20Sopenharmony_ci return 0; 14568c2ecf20Sopenharmony_ci} 14578c2ecf20Sopenharmony_ci 14588c2ecf20Sopenharmony_ci/* 14598c2ecf20Sopenharmony_ci * Clean up sysfs entries when disconnecting 14608c2ecf20Sopenharmony_ci */ 14618c2ecf20Sopenharmony_cistatic void elantech_disconnect(struct psmouse *psmouse) 14628c2ecf20Sopenharmony_ci{ 14638c2ecf20Sopenharmony_ci struct elantech_data *etd = psmouse->private; 14648c2ecf20Sopenharmony_ci 14658c2ecf20Sopenharmony_ci /* 14668c2ecf20Sopenharmony_ci * We might have left a breadcrumb when trying to 14678c2ecf20Sopenharmony_ci * set up SMbus companion. 14688c2ecf20Sopenharmony_ci */ 14698c2ecf20Sopenharmony_ci psmouse_smbus_cleanup(psmouse); 14708c2ecf20Sopenharmony_ci 14718c2ecf20Sopenharmony_ci if (etd->tp_dev) 14728c2ecf20Sopenharmony_ci input_unregister_device(etd->tp_dev); 14738c2ecf20Sopenharmony_ci sysfs_remove_group(&psmouse->ps2dev.serio->dev.kobj, 14748c2ecf20Sopenharmony_ci &elantech_attr_group); 14758c2ecf20Sopenharmony_ci kfree(psmouse->private); 14768c2ecf20Sopenharmony_ci psmouse->private = NULL; 14778c2ecf20Sopenharmony_ci} 14788c2ecf20Sopenharmony_ci 14798c2ecf20Sopenharmony_ci/* 14808c2ecf20Sopenharmony_ci * Put the touchpad back into absolute mode when reconnecting 14818c2ecf20Sopenharmony_ci */ 14828c2ecf20Sopenharmony_cistatic int elantech_reconnect(struct psmouse *psmouse) 14838c2ecf20Sopenharmony_ci{ 14848c2ecf20Sopenharmony_ci psmouse_reset(psmouse); 14858c2ecf20Sopenharmony_ci 14868c2ecf20Sopenharmony_ci if (elantech_detect(psmouse, 0)) 14878c2ecf20Sopenharmony_ci return -1; 14888c2ecf20Sopenharmony_ci 14898c2ecf20Sopenharmony_ci if (elantech_set_absolute_mode(psmouse)) { 14908c2ecf20Sopenharmony_ci psmouse_err(psmouse, 14918c2ecf20Sopenharmony_ci "failed to put touchpad back into absolute mode.\n"); 14928c2ecf20Sopenharmony_ci return -1; 14938c2ecf20Sopenharmony_ci } 14948c2ecf20Sopenharmony_ci 14958c2ecf20Sopenharmony_ci return 0; 14968c2ecf20Sopenharmony_ci} 14978c2ecf20Sopenharmony_ci 14988c2ecf20Sopenharmony_ci/* 14998c2ecf20Sopenharmony_ci * Some hw_version 4 models do not work with crc_disabled 15008c2ecf20Sopenharmony_ci */ 15018c2ecf20Sopenharmony_cistatic const struct dmi_system_id elantech_dmi_force_crc_enabled[] = { 15028c2ecf20Sopenharmony_ci#if defined(CONFIG_DMI) && defined(CONFIG_X86) 15038c2ecf20Sopenharmony_ci { 15048c2ecf20Sopenharmony_ci /* Fujitsu H730 does not work with crc_enabled == 0 */ 15058c2ecf20Sopenharmony_ci .matches = { 15068c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), 15078c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "CELSIUS H730"), 15088c2ecf20Sopenharmony_ci }, 15098c2ecf20Sopenharmony_ci }, 15108c2ecf20Sopenharmony_ci { 15118c2ecf20Sopenharmony_ci /* Fujitsu H760 does not work with crc_enabled == 0 */ 15128c2ecf20Sopenharmony_ci .matches = { 15138c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), 15148c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "CELSIUS H760"), 15158c2ecf20Sopenharmony_ci }, 15168c2ecf20Sopenharmony_ci }, 15178c2ecf20Sopenharmony_ci { 15188c2ecf20Sopenharmony_ci /* Fujitsu LIFEBOOK E544 does not work with crc_enabled == 0 */ 15198c2ecf20Sopenharmony_ci .matches = { 15208c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), 15218c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E544"), 15228c2ecf20Sopenharmony_ci }, 15238c2ecf20Sopenharmony_ci }, 15248c2ecf20Sopenharmony_ci { 15258c2ecf20Sopenharmony_ci /* Fujitsu LIFEBOOK E546 does not work with crc_enabled == 0 */ 15268c2ecf20Sopenharmony_ci .matches = { 15278c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), 15288c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E546"), 15298c2ecf20Sopenharmony_ci }, 15308c2ecf20Sopenharmony_ci }, 15318c2ecf20Sopenharmony_ci { 15328c2ecf20Sopenharmony_ci /* Fujitsu LIFEBOOK E547 does not work with crc_enabled == 0 */ 15338c2ecf20Sopenharmony_ci .matches = { 15348c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), 15358c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E547"), 15368c2ecf20Sopenharmony_ci }, 15378c2ecf20Sopenharmony_ci }, 15388c2ecf20Sopenharmony_ci { 15398c2ecf20Sopenharmony_ci /* Fujitsu LIFEBOOK E554 does not work with crc_enabled == 0 */ 15408c2ecf20Sopenharmony_ci .matches = { 15418c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), 15428c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E554"), 15438c2ecf20Sopenharmony_ci }, 15448c2ecf20Sopenharmony_ci }, 15458c2ecf20Sopenharmony_ci { 15468c2ecf20Sopenharmony_ci /* Fujitsu LIFEBOOK E556 does not work with crc_enabled == 0 */ 15478c2ecf20Sopenharmony_ci .matches = { 15488c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), 15498c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E556"), 15508c2ecf20Sopenharmony_ci }, 15518c2ecf20Sopenharmony_ci }, 15528c2ecf20Sopenharmony_ci { 15538c2ecf20Sopenharmony_ci /* Fujitsu LIFEBOOK E557 does not work with crc_enabled == 0 */ 15548c2ecf20Sopenharmony_ci .matches = { 15558c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), 15568c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E557"), 15578c2ecf20Sopenharmony_ci }, 15588c2ecf20Sopenharmony_ci }, 15598c2ecf20Sopenharmony_ci { 15608c2ecf20Sopenharmony_ci /* Fujitsu LIFEBOOK U745 does not work with crc_enabled == 0 */ 15618c2ecf20Sopenharmony_ci .matches = { 15628c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), 15638c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK U745"), 15648c2ecf20Sopenharmony_ci }, 15658c2ecf20Sopenharmony_ci }, 15668c2ecf20Sopenharmony_ci#endif 15678c2ecf20Sopenharmony_ci { } 15688c2ecf20Sopenharmony_ci}; 15698c2ecf20Sopenharmony_ci 15708c2ecf20Sopenharmony_ci/* 15718c2ecf20Sopenharmony_ci * Some hw_version 3 models go into error state when we try to set 15728c2ecf20Sopenharmony_ci * bit 3 and/or bit 1 of r10. 15738c2ecf20Sopenharmony_ci */ 15748c2ecf20Sopenharmony_cistatic const struct dmi_system_id no_hw_res_dmi_table[] = { 15758c2ecf20Sopenharmony_ci#if defined(CONFIG_DMI) && defined(CONFIG_X86) 15768c2ecf20Sopenharmony_ci { 15778c2ecf20Sopenharmony_ci /* Gigabyte U2442 */ 15788c2ecf20Sopenharmony_ci .matches = { 15798c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), 15808c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "U2442"), 15818c2ecf20Sopenharmony_ci }, 15828c2ecf20Sopenharmony_ci }, 15838c2ecf20Sopenharmony_ci#endif 15848c2ecf20Sopenharmony_ci { } 15858c2ecf20Sopenharmony_ci}; 15868c2ecf20Sopenharmony_ci 15878c2ecf20Sopenharmony_ci/* 15888c2ecf20Sopenharmony_ci * Change Report id 0x5E to 0x5F. 15898c2ecf20Sopenharmony_ci */ 15908c2ecf20Sopenharmony_cistatic int elantech_change_report_id(struct psmouse *psmouse) 15918c2ecf20Sopenharmony_ci{ 15928c2ecf20Sopenharmony_ci /* 15938c2ecf20Sopenharmony_ci * NOTE: the code is expecting to receive param[] as an array of 3 15948c2ecf20Sopenharmony_ci * items (see __ps2_command()), even if in this case only 2 are 15958c2ecf20Sopenharmony_ci * actually needed. Make sure the array size is 3 to avoid potential 15968c2ecf20Sopenharmony_ci * stack out-of-bound accesses. 15978c2ecf20Sopenharmony_ci */ 15988c2ecf20Sopenharmony_ci unsigned char param[3] = { 0x10, 0x03 }; 15998c2ecf20Sopenharmony_ci 16008c2ecf20Sopenharmony_ci if (elantech_write_reg_params(psmouse, 0x7, param) || 16018c2ecf20Sopenharmony_ci elantech_read_reg_params(psmouse, 0x7, param) || 16028c2ecf20Sopenharmony_ci param[0] != 0x10 || param[1] != 0x03) { 16038c2ecf20Sopenharmony_ci psmouse_err(psmouse, "Unable to change report ID to 0x5f.\n"); 16048c2ecf20Sopenharmony_ci return -EIO; 16058c2ecf20Sopenharmony_ci } 16068c2ecf20Sopenharmony_ci 16078c2ecf20Sopenharmony_ci return 0; 16088c2ecf20Sopenharmony_ci} 16098c2ecf20Sopenharmony_ci/* 16108c2ecf20Sopenharmony_ci * determine hardware version and set some properties according to it. 16118c2ecf20Sopenharmony_ci */ 16128c2ecf20Sopenharmony_cistatic int elantech_set_properties(struct elantech_device_info *info) 16138c2ecf20Sopenharmony_ci{ 16148c2ecf20Sopenharmony_ci /* This represents the version of IC body. */ 16158c2ecf20Sopenharmony_ci info->ic_version = (info->fw_version & 0x0f0000) >> 16; 16168c2ecf20Sopenharmony_ci 16178c2ecf20Sopenharmony_ci /* Early version of Elan touchpads doesn't obey the rule. */ 16188c2ecf20Sopenharmony_ci if (info->fw_version < 0x020030 || info->fw_version == 0x020600) 16198c2ecf20Sopenharmony_ci info->hw_version = 1; 16208c2ecf20Sopenharmony_ci else { 16218c2ecf20Sopenharmony_ci switch (info->ic_version) { 16228c2ecf20Sopenharmony_ci case 2: 16238c2ecf20Sopenharmony_ci case 4: 16248c2ecf20Sopenharmony_ci info->hw_version = 2; 16258c2ecf20Sopenharmony_ci break; 16268c2ecf20Sopenharmony_ci case 5: 16278c2ecf20Sopenharmony_ci info->hw_version = 3; 16288c2ecf20Sopenharmony_ci break; 16298c2ecf20Sopenharmony_ci case 6 ... 15: 16308c2ecf20Sopenharmony_ci info->hw_version = 4; 16318c2ecf20Sopenharmony_ci break; 16328c2ecf20Sopenharmony_ci default: 16338c2ecf20Sopenharmony_ci return -1; 16348c2ecf20Sopenharmony_ci } 16358c2ecf20Sopenharmony_ci } 16368c2ecf20Sopenharmony_ci 16378c2ecf20Sopenharmony_ci /* Get information pattern for hw_version 4 */ 16388c2ecf20Sopenharmony_ci info->pattern = 0x00; 16398c2ecf20Sopenharmony_ci if (info->ic_version == 0x0f && (info->fw_version & 0xff) <= 0x02) 16408c2ecf20Sopenharmony_ci info->pattern = info->fw_version & 0xff; 16418c2ecf20Sopenharmony_ci 16428c2ecf20Sopenharmony_ci /* decide which send_cmd we're gonna use early */ 16438c2ecf20Sopenharmony_ci info->send_cmd = info->hw_version >= 3 ? elantech_send_cmd : 16448c2ecf20Sopenharmony_ci synaptics_send_cmd; 16458c2ecf20Sopenharmony_ci 16468c2ecf20Sopenharmony_ci /* Turn on packet checking by default */ 16478c2ecf20Sopenharmony_ci info->paritycheck = 1; 16488c2ecf20Sopenharmony_ci 16498c2ecf20Sopenharmony_ci /* 16508c2ecf20Sopenharmony_ci * This firmware suffers from misreporting coordinates when 16518c2ecf20Sopenharmony_ci * a touch action starts causing the mouse cursor or scrolled page 16528c2ecf20Sopenharmony_ci * to jump. Enable a workaround. 16538c2ecf20Sopenharmony_ci */ 16548c2ecf20Sopenharmony_ci info->jumpy_cursor = 16558c2ecf20Sopenharmony_ci (info->fw_version == 0x020022 || info->fw_version == 0x020600); 16568c2ecf20Sopenharmony_ci 16578c2ecf20Sopenharmony_ci if (info->hw_version > 1) { 16588c2ecf20Sopenharmony_ci /* For now show extra debug information */ 16598c2ecf20Sopenharmony_ci info->debug = 1; 16608c2ecf20Sopenharmony_ci 16618c2ecf20Sopenharmony_ci if (info->fw_version >= 0x020800) 16628c2ecf20Sopenharmony_ci info->reports_pressure = true; 16638c2ecf20Sopenharmony_ci } 16648c2ecf20Sopenharmony_ci 16658c2ecf20Sopenharmony_ci /* 16668c2ecf20Sopenharmony_ci * The signatures of v3 and v4 packets change depending on the 16678c2ecf20Sopenharmony_ci * value of this hardware flag. 16688c2ecf20Sopenharmony_ci */ 16698c2ecf20Sopenharmony_ci info->crc_enabled = (info->fw_version & 0x4000) == 0x4000 || 16708c2ecf20Sopenharmony_ci dmi_check_system(elantech_dmi_force_crc_enabled); 16718c2ecf20Sopenharmony_ci 16728c2ecf20Sopenharmony_ci /* Enable real hardware resolution on hw_version 3 ? */ 16738c2ecf20Sopenharmony_ci info->set_hw_resolution = !dmi_check_system(no_hw_res_dmi_table); 16748c2ecf20Sopenharmony_ci 16758c2ecf20Sopenharmony_ci return 0; 16768c2ecf20Sopenharmony_ci} 16778c2ecf20Sopenharmony_ci 16788c2ecf20Sopenharmony_cistatic int elantech_query_info(struct psmouse *psmouse, 16798c2ecf20Sopenharmony_ci struct elantech_device_info *info) 16808c2ecf20Sopenharmony_ci{ 16818c2ecf20Sopenharmony_ci unsigned char param[3]; 16828c2ecf20Sopenharmony_ci unsigned char traces; 16838c2ecf20Sopenharmony_ci unsigned char ic_body[3]; 16848c2ecf20Sopenharmony_ci 16858c2ecf20Sopenharmony_ci memset(info, 0, sizeof(*info)); 16868c2ecf20Sopenharmony_ci 16878c2ecf20Sopenharmony_ci /* 16888c2ecf20Sopenharmony_ci * Do the version query again so we can store the result 16898c2ecf20Sopenharmony_ci */ 16908c2ecf20Sopenharmony_ci if (synaptics_send_cmd(psmouse, ETP_FW_VERSION_QUERY, param)) { 16918c2ecf20Sopenharmony_ci psmouse_err(psmouse, "failed to query firmware version.\n"); 16928c2ecf20Sopenharmony_ci return -EINVAL; 16938c2ecf20Sopenharmony_ci } 16948c2ecf20Sopenharmony_ci info->fw_version = (param[0] << 16) | (param[1] << 8) | param[2]; 16958c2ecf20Sopenharmony_ci 16968c2ecf20Sopenharmony_ci if (elantech_set_properties(info)) { 16978c2ecf20Sopenharmony_ci psmouse_err(psmouse, "unknown hardware version, aborting...\n"); 16988c2ecf20Sopenharmony_ci return -EINVAL; 16998c2ecf20Sopenharmony_ci } 17008c2ecf20Sopenharmony_ci psmouse_info(psmouse, 17018c2ecf20Sopenharmony_ci "assuming hardware version %d (with firmware version 0x%02x%02x%02x)\n", 17028c2ecf20Sopenharmony_ci info->hw_version, param[0], param[1], param[2]); 17038c2ecf20Sopenharmony_ci 17048c2ecf20Sopenharmony_ci if (info->send_cmd(psmouse, ETP_CAPABILITIES_QUERY, 17058c2ecf20Sopenharmony_ci info->capabilities)) { 17068c2ecf20Sopenharmony_ci psmouse_err(psmouse, "failed to query capabilities.\n"); 17078c2ecf20Sopenharmony_ci return -EINVAL; 17088c2ecf20Sopenharmony_ci } 17098c2ecf20Sopenharmony_ci psmouse_info(psmouse, 17108c2ecf20Sopenharmony_ci "Synaptics capabilities query result 0x%02x, 0x%02x, 0x%02x.\n", 17118c2ecf20Sopenharmony_ci info->capabilities[0], info->capabilities[1], 17128c2ecf20Sopenharmony_ci info->capabilities[2]); 17138c2ecf20Sopenharmony_ci 17148c2ecf20Sopenharmony_ci if (info->hw_version != 1) { 17158c2ecf20Sopenharmony_ci if (info->send_cmd(psmouse, ETP_SAMPLE_QUERY, info->samples)) { 17168c2ecf20Sopenharmony_ci psmouse_err(psmouse, "failed to query sample data\n"); 17178c2ecf20Sopenharmony_ci return -EINVAL; 17188c2ecf20Sopenharmony_ci } 17198c2ecf20Sopenharmony_ci psmouse_info(psmouse, 17208c2ecf20Sopenharmony_ci "Elan sample query result %02x, %02x, %02x\n", 17218c2ecf20Sopenharmony_ci info->samples[0], 17228c2ecf20Sopenharmony_ci info->samples[1], 17238c2ecf20Sopenharmony_ci info->samples[2]); 17248c2ecf20Sopenharmony_ci } 17258c2ecf20Sopenharmony_ci 17268c2ecf20Sopenharmony_ci if (info->pattern > 0x00 && info->ic_version == 0xf) { 17278c2ecf20Sopenharmony_ci if (info->send_cmd(psmouse, ETP_ICBODY_QUERY, ic_body)) { 17288c2ecf20Sopenharmony_ci psmouse_err(psmouse, "failed to query ic body\n"); 17298c2ecf20Sopenharmony_ci return -EINVAL; 17308c2ecf20Sopenharmony_ci } 17318c2ecf20Sopenharmony_ci info->ic_version = be16_to_cpup((__be16 *)ic_body); 17328c2ecf20Sopenharmony_ci psmouse_info(psmouse, 17338c2ecf20Sopenharmony_ci "Elan ic body: %#04x, current fw version: %#02x\n", 17348c2ecf20Sopenharmony_ci info->ic_version, ic_body[2]); 17358c2ecf20Sopenharmony_ci } 17368c2ecf20Sopenharmony_ci 17378c2ecf20Sopenharmony_ci info->product_id = be16_to_cpup((__be16 *)info->samples); 17388c2ecf20Sopenharmony_ci if (info->pattern == 0x00) 17398c2ecf20Sopenharmony_ci info->product_id &= 0xff; 17408c2ecf20Sopenharmony_ci 17418c2ecf20Sopenharmony_ci if (info->samples[1] == 0x74 && info->hw_version == 0x03) { 17428c2ecf20Sopenharmony_ci /* 17438c2ecf20Sopenharmony_ci * This module has a bug which makes absolute mode 17448c2ecf20Sopenharmony_ci * unusable, so let's abort so we'll be using standard 17458c2ecf20Sopenharmony_ci * PS/2 protocol. 17468c2ecf20Sopenharmony_ci */ 17478c2ecf20Sopenharmony_ci psmouse_info(psmouse, 17488c2ecf20Sopenharmony_ci "absolute mode broken, forcing standard PS/2 protocol\n"); 17498c2ecf20Sopenharmony_ci return -ENODEV; 17508c2ecf20Sopenharmony_ci } 17518c2ecf20Sopenharmony_ci 17528c2ecf20Sopenharmony_ci /* The MSB indicates the presence of the trackpoint */ 17538c2ecf20Sopenharmony_ci info->has_trackpoint = (info->capabilities[0] & 0x80) == 0x80; 17548c2ecf20Sopenharmony_ci 17558c2ecf20Sopenharmony_ci if (info->has_trackpoint && info->ic_version == 0x0011 && 17568c2ecf20Sopenharmony_ci (info->product_id == 0x08 || info->product_id == 0x09 || 17578c2ecf20Sopenharmony_ci info->product_id == 0x0d || info->product_id == 0x0e)) { 17588c2ecf20Sopenharmony_ci /* 17598c2ecf20Sopenharmony_ci * This module has a bug which makes trackpoint in SMBus 17608c2ecf20Sopenharmony_ci * mode return invalid data unless trackpoint is switched 17618c2ecf20Sopenharmony_ci * from using 0x5e reports to 0x5f. If we are not able to 17628c2ecf20Sopenharmony_ci * make the switch, let's abort initialization so we'll be 17638c2ecf20Sopenharmony_ci * using standard PS/2 protocol. 17648c2ecf20Sopenharmony_ci */ 17658c2ecf20Sopenharmony_ci if (elantech_change_report_id(psmouse)) { 17668c2ecf20Sopenharmony_ci psmouse_info(psmouse, 17678c2ecf20Sopenharmony_ci "Trackpoint report is broken, forcing standard PS/2 protocol\n"); 17688c2ecf20Sopenharmony_ci return -ENODEV; 17698c2ecf20Sopenharmony_ci } 17708c2ecf20Sopenharmony_ci } 17718c2ecf20Sopenharmony_ci 17728c2ecf20Sopenharmony_ci info->x_res = 31; 17738c2ecf20Sopenharmony_ci info->y_res = 31; 17748c2ecf20Sopenharmony_ci if (info->hw_version == 4) { 17758c2ecf20Sopenharmony_ci if (elantech_get_resolution_v4(psmouse, 17768c2ecf20Sopenharmony_ci &info->x_res, 17778c2ecf20Sopenharmony_ci &info->y_res, 17788c2ecf20Sopenharmony_ci &info->bus)) { 17798c2ecf20Sopenharmony_ci psmouse_warn(psmouse, 17808c2ecf20Sopenharmony_ci "failed to query resolution data.\n"); 17818c2ecf20Sopenharmony_ci } 17828c2ecf20Sopenharmony_ci } 17838c2ecf20Sopenharmony_ci 17848c2ecf20Sopenharmony_ci /* query range information */ 17858c2ecf20Sopenharmony_ci switch (info->hw_version) { 17868c2ecf20Sopenharmony_ci case 1: 17878c2ecf20Sopenharmony_ci info->x_min = ETP_XMIN_V1; 17888c2ecf20Sopenharmony_ci info->y_min = ETP_YMIN_V1; 17898c2ecf20Sopenharmony_ci info->x_max = ETP_XMAX_V1; 17908c2ecf20Sopenharmony_ci info->y_max = ETP_YMAX_V1; 17918c2ecf20Sopenharmony_ci break; 17928c2ecf20Sopenharmony_ci 17938c2ecf20Sopenharmony_ci case 2: 17948c2ecf20Sopenharmony_ci if (info->fw_version == 0x020800 || 17958c2ecf20Sopenharmony_ci info->fw_version == 0x020b00 || 17968c2ecf20Sopenharmony_ci info->fw_version == 0x020030) { 17978c2ecf20Sopenharmony_ci info->x_min = ETP_XMIN_V2; 17988c2ecf20Sopenharmony_ci info->y_min = ETP_YMIN_V2; 17998c2ecf20Sopenharmony_ci info->x_max = ETP_XMAX_V2; 18008c2ecf20Sopenharmony_ci info->y_max = ETP_YMAX_V2; 18018c2ecf20Sopenharmony_ci } else { 18028c2ecf20Sopenharmony_ci int i; 18038c2ecf20Sopenharmony_ci int fixed_dpi; 18048c2ecf20Sopenharmony_ci 18058c2ecf20Sopenharmony_ci i = (info->fw_version > 0x020800 && 18068c2ecf20Sopenharmony_ci info->fw_version < 0x020900) ? 1 : 2; 18078c2ecf20Sopenharmony_ci 18088c2ecf20Sopenharmony_ci if (info->send_cmd(psmouse, ETP_FW_ID_QUERY, param)) 18098c2ecf20Sopenharmony_ci return -EINVAL; 18108c2ecf20Sopenharmony_ci 18118c2ecf20Sopenharmony_ci fixed_dpi = param[1] & 0x10; 18128c2ecf20Sopenharmony_ci 18138c2ecf20Sopenharmony_ci if (((info->fw_version >> 16) == 0x14) && fixed_dpi) { 18148c2ecf20Sopenharmony_ci if (info->send_cmd(psmouse, ETP_SAMPLE_QUERY, param)) 18158c2ecf20Sopenharmony_ci return -EINVAL; 18168c2ecf20Sopenharmony_ci 18178c2ecf20Sopenharmony_ci info->x_max = (info->capabilities[1] - i) * param[1] / 2; 18188c2ecf20Sopenharmony_ci info->y_max = (info->capabilities[2] - i) * param[2] / 2; 18198c2ecf20Sopenharmony_ci } else if (info->fw_version == 0x040216) { 18208c2ecf20Sopenharmony_ci info->x_max = 819; 18218c2ecf20Sopenharmony_ci info->y_max = 405; 18228c2ecf20Sopenharmony_ci } else if (info->fw_version == 0x040219 || info->fw_version == 0x040215) { 18238c2ecf20Sopenharmony_ci info->x_max = 900; 18248c2ecf20Sopenharmony_ci info->y_max = 500; 18258c2ecf20Sopenharmony_ci } else { 18268c2ecf20Sopenharmony_ci info->x_max = (info->capabilities[1] - i) * 64; 18278c2ecf20Sopenharmony_ci info->y_max = (info->capabilities[2] - i) * 64; 18288c2ecf20Sopenharmony_ci } 18298c2ecf20Sopenharmony_ci } 18308c2ecf20Sopenharmony_ci break; 18318c2ecf20Sopenharmony_ci 18328c2ecf20Sopenharmony_ci case 3: 18338c2ecf20Sopenharmony_ci if (info->send_cmd(psmouse, ETP_FW_ID_QUERY, param)) 18348c2ecf20Sopenharmony_ci return -EINVAL; 18358c2ecf20Sopenharmony_ci 18368c2ecf20Sopenharmony_ci info->x_max = (0x0f & param[0]) << 8 | param[1]; 18378c2ecf20Sopenharmony_ci info->y_max = (0xf0 & param[0]) << 4 | param[2]; 18388c2ecf20Sopenharmony_ci break; 18398c2ecf20Sopenharmony_ci 18408c2ecf20Sopenharmony_ci case 4: 18418c2ecf20Sopenharmony_ci if (info->send_cmd(psmouse, ETP_FW_ID_QUERY, param)) 18428c2ecf20Sopenharmony_ci return -EINVAL; 18438c2ecf20Sopenharmony_ci 18448c2ecf20Sopenharmony_ci info->x_max = (0x0f & param[0]) << 8 | param[1]; 18458c2ecf20Sopenharmony_ci info->y_max = (0xf0 & param[0]) << 4 | param[2]; 18468c2ecf20Sopenharmony_ci traces = info->capabilities[1]; 18478c2ecf20Sopenharmony_ci if ((traces < 2) || (traces > info->x_max)) 18488c2ecf20Sopenharmony_ci return -EINVAL; 18498c2ecf20Sopenharmony_ci 18508c2ecf20Sopenharmony_ci info->width = info->x_max / (traces - 1); 18518c2ecf20Sopenharmony_ci 18528c2ecf20Sopenharmony_ci /* column number of traces */ 18538c2ecf20Sopenharmony_ci info->x_traces = traces; 18548c2ecf20Sopenharmony_ci 18558c2ecf20Sopenharmony_ci /* row number of traces */ 18568c2ecf20Sopenharmony_ci traces = info->capabilities[2]; 18578c2ecf20Sopenharmony_ci if ((traces >= 2) && (traces <= info->y_max)) 18588c2ecf20Sopenharmony_ci info->y_traces = traces; 18598c2ecf20Sopenharmony_ci 18608c2ecf20Sopenharmony_ci break; 18618c2ecf20Sopenharmony_ci } 18628c2ecf20Sopenharmony_ci 18638c2ecf20Sopenharmony_ci /* check for the middle button: DMI matching or new v4 firmwares */ 18648c2ecf20Sopenharmony_ci info->has_middle_button = dmi_check_system(elantech_dmi_has_middle_button) || 18658c2ecf20Sopenharmony_ci (ETP_NEW_IC_SMBUS_HOST_NOTIFY(info->fw_version) && 18668c2ecf20Sopenharmony_ci !elantech_is_buttonpad(info)); 18678c2ecf20Sopenharmony_ci 18688c2ecf20Sopenharmony_ci return 0; 18698c2ecf20Sopenharmony_ci} 18708c2ecf20Sopenharmony_ci 18718c2ecf20Sopenharmony_ci#if defined(CONFIG_MOUSE_PS2_ELANTECH_SMBUS) 18728c2ecf20Sopenharmony_ci 18738c2ecf20Sopenharmony_ci/* 18748c2ecf20Sopenharmony_ci * The newest Elantech device can use a secondary bus (over SMBus) which 18758c2ecf20Sopenharmony_ci * provides a better bandwidth and allow a better control of the touchpads. 18768c2ecf20Sopenharmony_ci * This is used to decide if we need to use this bus or not. 18778c2ecf20Sopenharmony_ci */ 18788c2ecf20Sopenharmony_cienum { 18798c2ecf20Sopenharmony_ci ELANTECH_SMBUS_NOT_SET = -1, 18808c2ecf20Sopenharmony_ci ELANTECH_SMBUS_OFF, 18818c2ecf20Sopenharmony_ci ELANTECH_SMBUS_ON, 18828c2ecf20Sopenharmony_ci}; 18838c2ecf20Sopenharmony_ci 18848c2ecf20Sopenharmony_cistatic int elantech_smbus = IS_ENABLED(CONFIG_MOUSE_ELAN_I2C_SMBUS) ? 18858c2ecf20Sopenharmony_ci ELANTECH_SMBUS_NOT_SET : ELANTECH_SMBUS_OFF; 18868c2ecf20Sopenharmony_cimodule_param_named(elantech_smbus, elantech_smbus, int, 0644); 18878c2ecf20Sopenharmony_ciMODULE_PARM_DESC(elantech_smbus, "Use a secondary bus for the Elantech device."); 18888c2ecf20Sopenharmony_ci 18898c2ecf20Sopenharmony_cistatic const char * const i2c_blacklist_pnp_ids[] = { 18908c2ecf20Sopenharmony_ci /* 18918c2ecf20Sopenharmony_ci * These are known to not be working properly as bits are missing 18928c2ecf20Sopenharmony_ci * in elan_i2c. 18938c2ecf20Sopenharmony_ci */ 18948c2ecf20Sopenharmony_ci NULL 18958c2ecf20Sopenharmony_ci}; 18968c2ecf20Sopenharmony_ci 18978c2ecf20Sopenharmony_cistatic int elantech_create_smbus(struct psmouse *psmouse, 18988c2ecf20Sopenharmony_ci struct elantech_device_info *info, 18998c2ecf20Sopenharmony_ci bool leave_breadcrumbs) 19008c2ecf20Sopenharmony_ci{ 19018c2ecf20Sopenharmony_ci struct property_entry i2c_props[11] = {}; 19028c2ecf20Sopenharmony_ci struct i2c_board_info smbus_board = { 19038c2ecf20Sopenharmony_ci I2C_BOARD_INFO("elan_i2c", 0x15), 19048c2ecf20Sopenharmony_ci .flags = I2C_CLIENT_HOST_NOTIFY, 19058c2ecf20Sopenharmony_ci }; 19068c2ecf20Sopenharmony_ci unsigned int idx = 0; 19078c2ecf20Sopenharmony_ci 19088c2ecf20Sopenharmony_ci smbus_board.properties = i2c_props; 19098c2ecf20Sopenharmony_ci 19108c2ecf20Sopenharmony_ci i2c_props[idx++] = PROPERTY_ENTRY_U32("touchscreen-size-x", 19118c2ecf20Sopenharmony_ci info->x_max + 1); 19128c2ecf20Sopenharmony_ci i2c_props[idx++] = PROPERTY_ENTRY_U32("touchscreen-size-y", 19138c2ecf20Sopenharmony_ci info->y_max + 1); 19148c2ecf20Sopenharmony_ci i2c_props[idx++] = PROPERTY_ENTRY_U32("touchscreen-min-x", 19158c2ecf20Sopenharmony_ci info->x_min); 19168c2ecf20Sopenharmony_ci i2c_props[idx++] = PROPERTY_ENTRY_U32("touchscreen-min-y", 19178c2ecf20Sopenharmony_ci info->y_min); 19188c2ecf20Sopenharmony_ci if (info->x_res) 19198c2ecf20Sopenharmony_ci i2c_props[idx++] = PROPERTY_ENTRY_U32("touchscreen-x-mm", 19208c2ecf20Sopenharmony_ci (info->x_max + 1) / info->x_res); 19218c2ecf20Sopenharmony_ci if (info->y_res) 19228c2ecf20Sopenharmony_ci i2c_props[idx++] = PROPERTY_ENTRY_U32("touchscreen-y-mm", 19238c2ecf20Sopenharmony_ci (info->y_max + 1) / info->y_res); 19248c2ecf20Sopenharmony_ci 19258c2ecf20Sopenharmony_ci if (info->has_trackpoint) 19268c2ecf20Sopenharmony_ci i2c_props[idx++] = PROPERTY_ENTRY_BOOL("elan,trackpoint"); 19278c2ecf20Sopenharmony_ci 19288c2ecf20Sopenharmony_ci if (info->has_middle_button) 19298c2ecf20Sopenharmony_ci i2c_props[idx++] = PROPERTY_ENTRY_BOOL("elan,middle-button"); 19308c2ecf20Sopenharmony_ci 19318c2ecf20Sopenharmony_ci if (info->x_traces) 19328c2ecf20Sopenharmony_ci i2c_props[idx++] = PROPERTY_ENTRY_U32("elan,x_traces", 19338c2ecf20Sopenharmony_ci info->x_traces); 19348c2ecf20Sopenharmony_ci if (info->y_traces) 19358c2ecf20Sopenharmony_ci i2c_props[idx++] = PROPERTY_ENTRY_U32("elan,y_traces", 19368c2ecf20Sopenharmony_ci info->y_traces); 19378c2ecf20Sopenharmony_ci 19388c2ecf20Sopenharmony_ci if (elantech_is_buttonpad(info)) 19398c2ecf20Sopenharmony_ci i2c_props[idx++] = PROPERTY_ENTRY_BOOL("elan,clickpad"); 19408c2ecf20Sopenharmony_ci 19418c2ecf20Sopenharmony_ci return psmouse_smbus_init(psmouse, &smbus_board, NULL, 0, false, 19428c2ecf20Sopenharmony_ci leave_breadcrumbs); 19438c2ecf20Sopenharmony_ci} 19448c2ecf20Sopenharmony_ci 19458c2ecf20Sopenharmony_ci/** 19468c2ecf20Sopenharmony_ci * elantech_setup_smbus - called once the PS/2 devices are enumerated 19478c2ecf20Sopenharmony_ci * and decides to instantiate a SMBus InterTouch device. 19488c2ecf20Sopenharmony_ci */ 19498c2ecf20Sopenharmony_cistatic int elantech_setup_smbus(struct psmouse *psmouse, 19508c2ecf20Sopenharmony_ci struct elantech_device_info *info, 19518c2ecf20Sopenharmony_ci bool leave_breadcrumbs) 19528c2ecf20Sopenharmony_ci{ 19538c2ecf20Sopenharmony_ci int error; 19548c2ecf20Sopenharmony_ci 19558c2ecf20Sopenharmony_ci if (elantech_smbus == ELANTECH_SMBUS_OFF) 19568c2ecf20Sopenharmony_ci return -ENXIO; 19578c2ecf20Sopenharmony_ci 19588c2ecf20Sopenharmony_ci if (elantech_smbus == ELANTECH_SMBUS_NOT_SET) { 19598c2ecf20Sopenharmony_ci /* 19608c2ecf20Sopenharmony_ci * New ICs are enabled by default, unless mentioned in 19618c2ecf20Sopenharmony_ci * i2c_blacklist_pnp_ids. 19628c2ecf20Sopenharmony_ci * Old ICs are up to the user to decide. 19638c2ecf20Sopenharmony_ci */ 19648c2ecf20Sopenharmony_ci if (!ETP_NEW_IC_SMBUS_HOST_NOTIFY(info->fw_version) || 19658c2ecf20Sopenharmony_ci psmouse_matches_pnp_id(psmouse, i2c_blacklist_pnp_ids)) 19668c2ecf20Sopenharmony_ci return -ENXIO; 19678c2ecf20Sopenharmony_ci } 19688c2ecf20Sopenharmony_ci 19698c2ecf20Sopenharmony_ci psmouse_info(psmouse, "Trying to set up SMBus access\n"); 19708c2ecf20Sopenharmony_ci 19718c2ecf20Sopenharmony_ci error = elantech_create_smbus(psmouse, info, leave_breadcrumbs); 19728c2ecf20Sopenharmony_ci if (error) { 19738c2ecf20Sopenharmony_ci if (error == -EAGAIN) 19748c2ecf20Sopenharmony_ci psmouse_info(psmouse, "SMbus companion is not ready yet\n"); 19758c2ecf20Sopenharmony_ci else 19768c2ecf20Sopenharmony_ci psmouse_err(psmouse, "unable to create intertouch device\n"); 19778c2ecf20Sopenharmony_ci 19788c2ecf20Sopenharmony_ci return error; 19798c2ecf20Sopenharmony_ci } 19808c2ecf20Sopenharmony_ci 19818c2ecf20Sopenharmony_ci return 0; 19828c2ecf20Sopenharmony_ci} 19838c2ecf20Sopenharmony_ci 19848c2ecf20Sopenharmony_cistatic bool elantech_use_host_notify(struct psmouse *psmouse, 19858c2ecf20Sopenharmony_ci struct elantech_device_info *info) 19868c2ecf20Sopenharmony_ci{ 19878c2ecf20Sopenharmony_ci if (ETP_NEW_IC_SMBUS_HOST_NOTIFY(info->fw_version)) 19888c2ecf20Sopenharmony_ci return true; 19898c2ecf20Sopenharmony_ci 19908c2ecf20Sopenharmony_ci switch (info->bus) { 19918c2ecf20Sopenharmony_ci case ETP_BUS_PS2_ONLY: 19928c2ecf20Sopenharmony_ci /* expected case */ 19938c2ecf20Sopenharmony_ci break; 19948c2ecf20Sopenharmony_ci case ETP_BUS_SMB_ALERT_ONLY: 19958c2ecf20Sopenharmony_ci case ETP_BUS_PS2_SMB_ALERT: 19968c2ecf20Sopenharmony_ci psmouse_dbg(psmouse, "Ignoring SMBus provider through alert protocol.\n"); 19978c2ecf20Sopenharmony_ci break; 19988c2ecf20Sopenharmony_ci case ETP_BUS_SMB_HST_NTFY_ONLY: 19998c2ecf20Sopenharmony_ci case ETP_BUS_PS2_SMB_HST_NTFY: 20008c2ecf20Sopenharmony_ci return true; 20018c2ecf20Sopenharmony_ci default: 20028c2ecf20Sopenharmony_ci psmouse_dbg(psmouse, 20038c2ecf20Sopenharmony_ci "Ignoring SMBus bus provider %d.\n", 20048c2ecf20Sopenharmony_ci info->bus); 20058c2ecf20Sopenharmony_ci } 20068c2ecf20Sopenharmony_ci 20078c2ecf20Sopenharmony_ci return false; 20088c2ecf20Sopenharmony_ci} 20098c2ecf20Sopenharmony_ci 20108c2ecf20Sopenharmony_ciint elantech_init_smbus(struct psmouse *psmouse) 20118c2ecf20Sopenharmony_ci{ 20128c2ecf20Sopenharmony_ci struct elantech_device_info info; 20138c2ecf20Sopenharmony_ci int error; 20148c2ecf20Sopenharmony_ci 20158c2ecf20Sopenharmony_ci psmouse_reset(psmouse); 20168c2ecf20Sopenharmony_ci 20178c2ecf20Sopenharmony_ci error = elantech_query_info(psmouse, &info); 20188c2ecf20Sopenharmony_ci if (error) 20198c2ecf20Sopenharmony_ci goto init_fail; 20208c2ecf20Sopenharmony_ci 20218c2ecf20Sopenharmony_ci if (info.hw_version < 4) { 20228c2ecf20Sopenharmony_ci error = -ENXIO; 20238c2ecf20Sopenharmony_ci goto init_fail; 20248c2ecf20Sopenharmony_ci } 20258c2ecf20Sopenharmony_ci 20268c2ecf20Sopenharmony_ci return elantech_create_smbus(psmouse, &info, false); 20278c2ecf20Sopenharmony_ci init_fail: 20288c2ecf20Sopenharmony_ci psmouse_reset(psmouse); 20298c2ecf20Sopenharmony_ci return error; 20308c2ecf20Sopenharmony_ci} 20318c2ecf20Sopenharmony_ci#endif /* CONFIG_MOUSE_PS2_ELANTECH_SMBUS */ 20328c2ecf20Sopenharmony_ci 20338c2ecf20Sopenharmony_ci/* 20348c2ecf20Sopenharmony_ci * Initialize the touchpad and create sysfs entries 20358c2ecf20Sopenharmony_ci */ 20368c2ecf20Sopenharmony_cistatic int elantech_setup_ps2(struct psmouse *psmouse, 20378c2ecf20Sopenharmony_ci struct elantech_device_info *info) 20388c2ecf20Sopenharmony_ci{ 20398c2ecf20Sopenharmony_ci struct elantech_data *etd; 20408c2ecf20Sopenharmony_ci int i; 20418c2ecf20Sopenharmony_ci int error = -EINVAL; 20428c2ecf20Sopenharmony_ci struct input_dev *tp_dev; 20438c2ecf20Sopenharmony_ci 20448c2ecf20Sopenharmony_ci psmouse->private = etd = kzalloc(sizeof(*etd), GFP_KERNEL); 20458c2ecf20Sopenharmony_ci if (!etd) 20468c2ecf20Sopenharmony_ci return -ENOMEM; 20478c2ecf20Sopenharmony_ci 20488c2ecf20Sopenharmony_ci etd->info = *info; 20498c2ecf20Sopenharmony_ci 20508c2ecf20Sopenharmony_ci etd->parity[0] = 1; 20518c2ecf20Sopenharmony_ci for (i = 1; i < 256; i++) 20528c2ecf20Sopenharmony_ci etd->parity[i] = etd->parity[i & (i - 1)] ^ 1; 20538c2ecf20Sopenharmony_ci 20548c2ecf20Sopenharmony_ci if (elantech_set_absolute_mode(psmouse)) { 20558c2ecf20Sopenharmony_ci psmouse_err(psmouse, 20568c2ecf20Sopenharmony_ci "failed to put touchpad into absolute mode.\n"); 20578c2ecf20Sopenharmony_ci goto init_fail; 20588c2ecf20Sopenharmony_ci } 20598c2ecf20Sopenharmony_ci 20608c2ecf20Sopenharmony_ci if (info->fw_version == 0x381f17) { 20618c2ecf20Sopenharmony_ci etd->original_set_rate = psmouse->set_rate; 20628c2ecf20Sopenharmony_ci psmouse->set_rate = elantech_set_rate_restore_reg_07; 20638c2ecf20Sopenharmony_ci } 20648c2ecf20Sopenharmony_ci 20658c2ecf20Sopenharmony_ci if (elantech_set_input_params(psmouse)) { 20668c2ecf20Sopenharmony_ci psmouse_err(psmouse, "failed to query touchpad range.\n"); 20678c2ecf20Sopenharmony_ci goto init_fail; 20688c2ecf20Sopenharmony_ci } 20698c2ecf20Sopenharmony_ci 20708c2ecf20Sopenharmony_ci error = sysfs_create_group(&psmouse->ps2dev.serio->dev.kobj, 20718c2ecf20Sopenharmony_ci &elantech_attr_group); 20728c2ecf20Sopenharmony_ci if (error) { 20738c2ecf20Sopenharmony_ci psmouse_err(psmouse, 20748c2ecf20Sopenharmony_ci "failed to create sysfs attributes, error: %d.\n", 20758c2ecf20Sopenharmony_ci error); 20768c2ecf20Sopenharmony_ci goto init_fail; 20778c2ecf20Sopenharmony_ci } 20788c2ecf20Sopenharmony_ci 20798c2ecf20Sopenharmony_ci if (info->has_trackpoint) { 20808c2ecf20Sopenharmony_ci tp_dev = input_allocate_device(); 20818c2ecf20Sopenharmony_ci 20828c2ecf20Sopenharmony_ci if (!tp_dev) { 20838c2ecf20Sopenharmony_ci error = -ENOMEM; 20848c2ecf20Sopenharmony_ci goto init_fail_tp_alloc; 20858c2ecf20Sopenharmony_ci } 20868c2ecf20Sopenharmony_ci 20878c2ecf20Sopenharmony_ci etd->tp_dev = tp_dev; 20888c2ecf20Sopenharmony_ci snprintf(etd->tp_phys, sizeof(etd->tp_phys), "%s/input1", 20898c2ecf20Sopenharmony_ci psmouse->ps2dev.serio->phys); 20908c2ecf20Sopenharmony_ci tp_dev->phys = etd->tp_phys; 20918c2ecf20Sopenharmony_ci tp_dev->name = "ETPS/2 Elantech TrackPoint"; 20928c2ecf20Sopenharmony_ci tp_dev->id.bustype = BUS_I8042; 20938c2ecf20Sopenharmony_ci tp_dev->id.vendor = 0x0002; 20948c2ecf20Sopenharmony_ci tp_dev->id.product = PSMOUSE_ELANTECH; 20958c2ecf20Sopenharmony_ci tp_dev->id.version = 0x0000; 20968c2ecf20Sopenharmony_ci tp_dev->dev.parent = &psmouse->ps2dev.serio->dev; 20978c2ecf20Sopenharmony_ci tp_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); 20988c2ecf20Sopenharmony_ci tp_dev->relbit[BIT_WORD(REL_X)] = 20998c2ecf20Sopenharmony_ci BIT_MASK(REL_X) | BIT_MASK(REL_Y); 21008c2ecf20Sopenharmony_ci tp_dev->keybit[BIT_WORD(BTN_LEFT)] = 21018c2ecf20Sopenharmony_ci BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_MIDDLE) | 21028c2ecf20Sopenharmony_ci BIT_MASK(BTN_RIGHT); 21038c2ecf20Sopenharmony_ci 21048c2ecf20Sopenharmony_ci __set_bit(INPUT_PROP_POINTER, tp_dev->propbit); 21058c2ecf20Sopenharmony_ci __set_bit(INPUT_PROP_POINTING_STICK, tp_dev->propbit); 21068c2ecf20Sopenharmony_ci 21078c2ecf20Sopenharmony_ci error = input_register_device(etd->tp_dev); 21088c2ecf20Sopenharmony_ci if (error < 0) 21098c2ecf20Sopenharmony_ci goto init_fail_tp_reg; 21108c2ecf20Sopenharmony_ci } 21118c2ecf20Sopenharmony_ci 21128c2ecf20Sopenharmony_ci psmouse->protocol_handler = elantech_process_byte; 21138c2ecf20Sopenharmony_ci psmouse->disconnect = elantech_disconnect; 21148c2ecf20Sopenharmony_ci psmouse->reconnect = elantech_reconnect; 21158c2ecf20Sopenharmony_ci psmouse->fast_reconnect = NULL; 21168c2ecf20Sopenharmony_ci psmouse->pktsize = info->hw_version > 1 ? 6 : 4; 21178c2ecf20Sopenharmony_ci 21188c2ecf20Sopenharmony_ci return 0; 21198c2ecf20Sopenharmony_ci init_fail_tp_reg: 21208c2ecf20Sopenharmony_ci input_free_device(tp_dev); 21218c2ecf20Sopenharmony_ci init_fail_tp_alloc: 21228c2ecf20Sopenharmony_ci sysfs_remove_group(&psmouse->ps2dev.serio->dev.kobj, 21238c2ecf20Sopenharmony_ci &elantech_attr_group); 21248c2ecf20Sopenharmony_ci init_fail: 21258c2ecf20Sopenharmony_ci kfree(etd); 21268c2ecf20Sopenharmony_ci return error; 21278c2ecf20Sopenharmony_ci} 21288c2ecf20Sopenharmony_ci 21298c2ecf20Sopenharmony_ciint elantech_init_ps2(struct psmouse *psmouse) 21308c2ecf20Sopenharmony_ci{ 21318c2ecf20Sopenharmony_ci struct elantech_device_info info; 21328c2ecf20Sopenharmony_ci int error; 21338c2ecf20Sopenharmony_ci 21348c2ecf20Sopenharmony_ci psmouse_reset(psmouse); 21358c2ecf20Sopenharmony_ci 21368c2ecf20Sopenharmony_ci error = elantech_query_info(psmouse, &info); 21378c2ecf20Sopenharmony_ci if (error) 21388c2ecf20Sopenharmony_ci goto init_fail; 21398c2ecf20Sopenharmony_ci 21408c2ecf20Sopenharmony_ci error = elantech_setup_ps2(psmouse, &info); 21418c2ecf20Sopenharmony_ci if (error) 21428c2ecf20Sopenharmony_ci goto init_fail; 21438c2ecf20Sopenharmony_ci 21448c2ecf20Sopenharmony_ci return 0; 21458c2ecf20Sopenharmony_ci init_fail: 21468c2ecf20Sopenharmony_ci psmouse_reset(psmouse); 21478c2ecf20Sopenharmony_ci return error; 21488c2ecf20Sopenharmony_ci} 21498c2ecf20Sopenharmony_ci 21508c2ecf20Sopenharmony_ciint elantech_init(struct psmouse *psmouse) 21518c2ecf20Sopenharmony_ci{ 21528c2ecf20Sopenharmony_ci struct elantech_device_info info; 21538c2ecf20Sopenharmony_ci int error; 21548c2ecf20Sopenharmony_ci 21558c2ecf20Sopenharmony_ci psmouse_reset(psmouse); 21568c2ecf20Sopenharmony_ci 21578c2ecf20Sopenharmony_ci error = elantech_query_info(psmouse, &info); 21588c2ecf20Sopenharmony_ci if (error) 21598c2ecf20Sopenharmony_ci goto init_fail; 21608c2ecf20Sopenharmony_ci 21618c2ecf20Sopenharmony_ci#if defined(CONFIG_MOUSE_PS2_ELANTECH_SMBUS) 21628c2ecf20Sopenharmony_ci 21638c2ecf20Sopenharmony_ci if (elantech_use_host_notify(psmouse, &info)) { 21648c2ecf20Sopenharmony_ci if (!IS_ENABLED(CONFIG_MOUSE_ELAN_I2C_SMBUS) || 21658c2ecf20Sopenharmony_ci !IS_ENABLED(CONFIG_MOUSE_PS2_ELANTECH_SMBUS)) { 21668c2ecf20Sopenharmony_ci psmouse_warn(psmouse, 21678c2ecf20Sopenharmony_ci "The touchpad can support a better bus than the too old PS/2 protocol. " 21688c2ecf20Sopenharmony_ci "Make sure MOUSE_PS2_ELANTECH_SMBUS and MOUSE_ELAN_I2C_SMBUS are enabled to get a better touchpad experience.\n"); 21698c2ecf20Sopenharmony_ci } 21708c2ecf20Sopenharmony_ci error = elantech_setup_smbus(psmouse, &info, true); 21718c2ecf20Sopenharmony_ci if (!error) 21728c2ecf20Sopenharmony_ci return PSMOUSE_ELANTECH_SMBUS; 21738c2ecf20Sopenharmony_ci } 21748c2ecf20Sopenharmony_ci 21758c2ecf20Sopenharmony_ci#endif /* CONFIG_MOUSE_PS2_ELANTECH_SMBUS */ 21768c2ecf20Sopenharmony_ci 21778c2ecf20Sopenharmony_ci error = elantech_setup_ps2(psmouse, &info); 21788c2ecf20Sopenharmony_ci if (error < 0) { 21798c2ecf20Sopenharmony_ci /* 21808c2ecf20Sopenharmony_ci * Not using any flavor of Elantech support, so clean up 21818c2ecf20Sopenharmony_ci * SMbus breadcrumbs, if any. 21828c2ecf20Sopenharmony_ci */ 21838c2ecf20Sopenharmony_ci psmouse_smbus_cleanup(psmouse); 21848c2ecf20Sopenharmony_ci goto init_fail; 21858c2ecf20Sopenharmony_ci } 21868c2ecf20Sopenharmony_ci 21878c2ecf20Sopenharmony_ci return PSMOUSE_ELANTECH; 21888c2ecf20Sopenharmony_ci init_fail: 21898c2ecf20Sopenharmony_ci psmouse_reset(psmouse); 21908c2ecf20Sopenharmony_ci return error; 21918c2ecf20Sopenharmony_ci} 2192