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