diff mbox series

Input: psmouse - add support for FocalTech PS/2 Protocol v2

Message ID 20210210233926.GA3348@Hamzas-MacBook (mailing list archive)
State Superseded
Headers show
Series Input: psmouse - add support for FocalTech PS/2 Protocol v2 | expand

Commit Message

Hamza Farooq Feb. 10, 2021, 11:39 p.m. UTC
Haier Y11C Laptop have FocalTech PS/2 Touchpad, BIOS Device Name is FTE0001.
This device have different protocol than exisiting FocalTech PS/2 Driver.

This commit adds a basic multitouch-capable driver.

Some of the protcol is still unknown (just like the other FocalTech driver)
Device can only be identified with PNP ID.

Signed-off-by: Hamza Farooq <0xA6C4@gmail.com>
---
 drivers/input/mouse/Kconfig        |  10 ++
 drivers/input/mouse/Makefile       |   2 +-
 drivers/input/mouse/focaltech_v2.c | 265 +++++++++++++++++++++++++++++
 drivers/input/mouse/focaltech_v2.h |  56 ++++++
 drivers/input/mouse/psmouse-base.c |  32 ++++
 drivers/input/mouse/psmouse.h      |   1 +
 6 files changed, 365 insertions(+), 1 deletion(-)
 create mode 100644 drivers/input/mouse/focaltech_v2.c
 create mode 100644 drivers/input/mouse/focaltech_v2.h

Comments

kernel test robot Feb. 11, 2021, 10:36 a.m. UTC | #1
Hi Hamza,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on input/next]
[also build test ERROR on linux/master hid/for-next linus/master v5.11-rc7 next-20210125]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Hamza-Farooq/Input-psmouse-add-support-for-FocalTech-PS-2-Protocol-v2/20210211-074527
base:   https://git.kernel.org/pub/scm/linux/kernel/git/dtor/input.git next
config: parisc-randconfig-r004-20210211 (attached as .config)
compiler: hppa-linux-gcc (GCC) 9.3.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/0day-ci/linux/commit/958fb71223bb82ea01edbcbf4970af1d888b1050
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Hamza-Farooq/Input-psmouse-add-support-for-FocalTech-PS-2-Protocol-v2/20210211-074527
        git checkout 958fb71223bb82ea01edbcbf4970af1d888b1050
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross ARCH=parisc 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All error/warnings (new ones prefixed by >>):

   In file included from include/linux/build_bug.h:5,
                    from include/linux/bits.h:22,
                    from include/linux/bitops.h:5,
                    from drivers/input/mouse/psmouse-base.c:13:
   drivers/input/mouse/psmouse-base.c: In function 'psmouse_extensions':
>> drivers/input/mouse/psmouse-base.c:1089:24: error: 'focaltech_v2_detect' undeclared (first use in this function); did you mean 'focaltech_detect'?
    1089 |  if (psmouse_do_detect(focaltech_v2_detect,
         |                        ^~~~~~~~~~~~~~~~~~~
   include/linux/compiler.h:58:52: note: in definition of macro '__trace_if_var'
      58 | #define __trace_if_var(cond) (__builtin_constant_p(cond) ? (cond) : __trace_if_value(cond))
         |                                                    ^~~~
   drivers/input/mouse/psmouse-base.c:1089:2: note: in expansion of macro 'if'
    1089 |  if (psmouse_do_detect(focaltech_v2_detect,
         |  ^~
   drivers/input/mouse/psmouse-base.c:1089:24: note: each undeclared identifier is reported only once for each function it appears in
    1089 |  if (psmouse_do_detect(focaltech_v2_detect,
         |                        ^~~~~~~~~~~~~~~~~~~
   include/linux/compiler.h:58:52: note: in definition of macro '__trace_if_var'
      58 | #define __trace_if_var(cond) (__builtin_constant_p(cond) ? (cond) : __trace_if_value(cond))
         |                                                    ^~~~
   drivers/input/mouse/psmouse-base.c:1089:2: note: in expansion of macro 'if'
    1089 |  if (psmouse_do_detect(focaltech_v2_detect,
         |  ^~
--
>> drivers/input/mouse/focaltech_v2.c:27:5: warning: no previous prototype for 'focaltech_v2_detect' [-Wmissing-prototypes]
      27 | int focaltech_v2_detect(struct psmouse *psmouse, bool set_properties)
         |     ^~~~~~~~~~~~~~~~~~~
   drivers/input/mouse/focaltech_v2.c:17:33: warning: 'switch_protocol' defined but not used [-Wunused-const-variable=]
      17 | static const struct fte_command switch_protocol[] = {
         |                                 ^~~~~~~~~~~~~~~


vim +1089 drivers/input/mouse/psmouse-base.c

  1052	
  1053	/*
  1054	 * psmouse_extensions() probes for any extensions to the basic PS/2 protocol
  1055	 * the mouse may have.
  1056	 */
  1057	static int psmouse_extensions(struct psmouse *psmouse,
  1058				      unsigned int max_proto, bool set_properties)
  1059	{
  1060		bool synaptics_hardware = false;
  1061		int ret;
  1062	
  1063		/*
  1064		 * Always check for focaltech, this is safe as it uses pnp-id
  1065		 * matching.
  1066		 */
  1067		if (psmouse_do_detect(focaltech_detect,
  1068				      psmouse, false, set_properties)) {
  1069			if (max_proto > PSMOUSE_IMEX &&
  1070			    IS_ENABLED(CONFIG_MOUSE_PS2_FOCALTECH) &&
  1071			    (!set_properties || focaltech_init(psmouse) == 0)) {
  1072				return PSMOUSE_FOCALTECH;
  1073			}
  1074			/*
  1075			 * Restrict psmouse_max_proto so that psmouse_initialize()
  1076			 * does not try to reset rate and resolution, because even
  1077			 * that upsets the device.
  1078			 * This also causes us to basically fall through to basic
  1079			 * protocol detection, where we fully reset the mouse,
  1080			 * and set it up as bare PS/2 protocol device.
  1081			 */
  1082			psmouse_max_proto = max_proto = PSMOUSE_PS2;
  1083		}
  1084	
  1085		/*
  1086		 * Always check for focaltech-v2, this is safe as it uses pnp-id
  1087		 * matching.
  1088		 */
> 1089		if (psmouse_do_detect(focaltech_v2_detect,
  1090				      psmouse, false, set_properties)) {
  1091			if (max_proto > PSMOUSE_IMEX &&
  1092			    IS_ENABLED(CONFIG_MOUSE_PS2_FOCALTECH_V2) &&
  1093			    (!set_properties || focaltech_v2_init(psmouse) == 0)) {
  1094				return PSMOUSE_FOCALTECH_V2;
  1095			}
  1096			/*
  1097			 * Restrict psmouse_max_proto so that psmouse_initialize()
  1098			 * does not try to reset rate and resolution, because even
  1099			 * that upsets the device.
  1100			 * This also causes us to basically fall through to basic
  1101			 * protocol detection, where we fully reset the mouse,
  1102			 * and set it up as bare PS/2 protocol device.
  1103			 */
  1104			psmouse_max_proto = max_proto = PSMOUSE_PS2;
  1105		}
  1106	
  1107		/*
  1108		 * We always check for LifeBook because it does not disturb mouse
  1109		 * (it only checks DMI information).
  1110		 */
  1111		if (psmouse_try_protocol(psmouse, PSMOUSE_LIFEBOOK, &max_proto,
  1112					 set_properties, max_proto > PSMOUSE_IMEX))
  1113			return PSMOUSE_LIFEBOOK;
  1114	
  1115		if (psmouse_try_protocol(psmouse, PSMOUSE_VMMOUSE, &max_proto,
  1116					 set_properties, max_proto > PSMOUSE_IMEX))
  1117			return PSMOUSE_VMMOUSE;
  1118	
  1119		/*
  1120		 * Try Kensington ThinkingMouse (we try first, because Synaptics
  1121		 * probe upsets the ThinkingMouse).
  1122		 */
  1123		if (max_proto > PSMOUSE_IMEX &&
  1124		    psmouse_try_protocol(psmouse, PSMOUSE_THINKPS, &max_proto,
  1125					 set_properties, true)) {
  1126			return PSMOUSE_THINKPS;
  1127		}
  1128	
  1129		/*
  1130		 * Try Synaptics TouchPad. Note that probing is done even if
  1131		 * Synaptics protocol support is disabled in config - we need to
  1132		 * know if it is Synaptics so we can reset it properly after
  1133		 * probing for IntelliMouse.
  1134		 */
  1135		if (max_proto > PSMOUSE_PS2 &&
  1136		    psmouse_do_detect(synaptics_detect,
  1137				      psmouse, false, set_properties)) {
  1138			synaptics_hardware = true;
  1139	
  1140			if (max_proto > PSMOUSE_IMEX) {
  1141				/*
  1142				 * Try activating protocol, but check if support is
  1143				 * enabled first, since we try detecting Synaptics
  1144				 * even when protocol is disabled.
  1145				 */
  1146				if (IS_ENABLED(CONFIG_MOUSE_PS2_SYNAPTICS) ||
  1147				    IS_ENABLED(CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS)) {
  1148					if (!set_properties)
  1149						return PSMOUSE_SYNAPTICS;
  1150	
  1151					ret = synaptics_init(psmouse);
  1152					if (ret >= 0)
  1153						return ret;
  1154				}
  1155	
  1156				/*
  1157				 * Some Synaptics touchpads can emulate extended
  1158				 * protocols (like IMPS/2).  Unfortunately
  1159				 * Logitech/Genius probes confuse some firmware
  1160				 * versions so we'll have to skip them.
  1161				 */
  1162				max_proto = PSMOUSE_IMEX;
  1163			}
  1164	
  1165			/*
  1166			 * Make sure that touchpad is in relative mode, gestures
  1167			 * (taps) are enabled.
  1168			 */
  1169			synaptics_reset(psmouse);
  1170		}
  1171	
  1172		/*
  1173		 * Try Cypress Trackpad. We must try it before Finger Sensing Pad
  1174		 * because Finger Sensing Pad probe upsets some modules of Cypress
  1175		 * Trackpads.
  1176		 */
  1177		if (max_proto > PSMOUSE_IMEX &&
  1178		    psmouse_try_protocol(psmouse, PSMOUSE_CYPRESS, &max_proto,
  1179					 set_properties, true)) {
  1180			return PSMOUSE_CYPRESS;
  1181		}
  1182	
  1183		/* Try ALPS TouchPad */
  1184		if (max_proto > PSMOUSE_IMEX) {
  1185			ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
  1186			if (psmouse_try_protocol(psmouse, PSMOUSE_ALPS,
  1187						 &max_proto, set_properties, true))
  1188				return PSMOUSE_ALPS;
  1189		}
  1190	
  1191		/* Try OLPC HGPK touchpad */
  1192		if (max_proto > PSMOUSE_IMEX &&
  1193		    psmouse_try_protocol(psmouse, PSMOUSE_HGPK, &max_proto,
  1194					 set_properties, true)) {
  1195			return PSMOUSE_HGPK;
  1196		}
  1197	
  1198		/* Try Elantech touchpad */
  1199		if (max_proto > PSMOUSE_IMEX &&
  1200		    psmouse_try_protocol(psmouse, PSMOUSE_ELANTECH,
  1201					 &max_proto, set_properties, false)) {
  1202			if (!set_properties)
  1203				return PSMOUSE_ELANTECH;
  1204	
  1205			ret = elantech_init(psmouse);
  1206			if (ret >= 0)
  1207				return ret;
  1208		}
  1209	
  1210		if (max_proto > PSMOUSE_IMEX) {
  1211			if (psmouse_try_protocol(psmouse, PSMOUSE_GENPS,
  1212						 &max_proto, set_properties, true))
  1213				return PSMOUSE_GENPS;
  1214	
  1215			if (psmouse_try_protocol(psmouse, PSMOUSE_PS2PP,
  1216						 &max_proto, set_properties, true))
  1217				return PSMOUSE_PS2PP;
  1218	
  1219			if (psmouse_try_protocol(psmouse, PSMOUSE_TRACKPOINT,
  1220						 &max_proto, set_properties, true))
  1221				return PSMOUSE_TRACKPOINT;
  1222	
  1223			if (psmouse_try_protocol(psmouse, PSMOUSE_TOUCHKIT_PS2,
  1224						 &max_proto, set_properties, true))
  1225				return PSMOUSE_TOUCHKIT_PS2;
  1226		}
  1227	
  1228		/*
  1229		 * Try Finger Sensing Pad. We do it here because its probe upsets
  1230		 * Trackpoint devices (causing TP_READ_ID command to time out).
  1231		 */
  1232		if (max_proto > PSMOUSE_IMEX &&
  1233		    psmouse_try_protocol(psmouse, PSMOUSE_FSP,
  1234					 &max_proto, set_properties, true)) {
  1235			return PSMOUSE_FSP;
  1236		}
  1237	
  1238		/*
  1239		 * Reset to defaults in case the device got confused by extended
  1240		 * protocol probes. Note that we follow up with full reset because
  1241		 * some mice put themselves to sleep when they see PSMOUSE_RESET_DIS.
  1242		 */
  1243		ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
  1244		psmouse_reset(psmouse);
  1245	
  1246		if (max_proto >= PSMOUSE_IMEX &&
  1247		    psmouse_try_protocol(psmouse, PSMOUSE_IMEX,
  1248					 &max_proto, set_properties, true)) {
  1249			return PSMOUSE_IMEX;
  1250		}
  1251	
  1252		if (max_proto >= PSMOUSE_IMPS &&
  1253		    psmouse_try_protocol(psmouse, PSMOUSE_IMPS,
  1254					 &max_proto, set_properties, true)) {
  1255			return PSMOUSE_IMPS;
  1256		}
  1257	
  1258		/*
  1259		 * Okay, all failed, we have a standard mouse here. The number of
  1260		 * the buttons is still a question, though. We assume 3.
  1261		 */
  1262		psmouse_try_protocol(psmouse, PSMOUSE_PS2,
  1263				     &max_proto, set_properties, true);
  1264	
  1265		if (synaptics_hardware) {
  1266			/*
  1267			 * We detected Synaptics hardware but it did not respond to
  1268			 * IMPS/2 probes.  We need to reset the touchpad because if
  1269			 * there is a track point on the pass through port it could
  1270			 * get disabled while probing for protocol extensions.
  1271			 */
  1272			psmouse_reset(psmouse);
  1273		}
  1274	
  1275		return PSMOUSE_PS2;
  1276	}
  1277	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
diff mbox series

Patch

diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig
index 63c9cda55..843509c01 100644
--- a/drivers/input/mouse/Kconfig
+++ b/drivers/input/mouse/Kconfig
@@ -184,6 +184,16 @@  config MOUSE_PS2_FOCALTECH
 
 	  If unsure, say Y.
 
+config MOUSE_PS2_FOCALTECH_V2
+	bool "FocalTech-v2 PS/2 mouse protocol extension" if EXPERT
+	default y
+	depends on MOUSE_PS2
+	help
+	  Say Y here if you have a FocalTech-V2 PS/2 TouchPad connected to
+	  your system.
+
+	  If unsure, say Y.
+
 config MOUSE_PS2_VMMOUSE
 	bool "Virtual mouse (vmmouse)"
 	depends on MOUSE_PS2 && X86 && HYPERVISOR_GUEST
diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile
index e49f08565..31673ea8d 100644
--- a/drivers/input/mouse/Makefile
+++ b/drivers/input/mouse/Makefile
@@ -26,7 +26,7 @@  obj-$(CONFIG_MOUSE_SYNAPTICS_USB)	+= synaptics_usb.o
 obj-$(CONFIG_MOUSE_VSXXXAA)		+= vsxxxaa.o
 
 cyapatp-objs := cyapa.o cyapa_gen3.o cyapa_gen5.o cyapa_gen6.o
-psmouse-objs := psmouse-base.o synaptics.o focaltech.o
+psmouse-objs := psmouse-base.o synaptics.o focaltech.o focaltech_v2.o
 
 psmouse-$(CONFIG_MOUSE_PS2_ALPS)	+= alps.o
 psmouse-$(CONFIG_MOUSE_PS2_BYD)		+= byd.o
diff --git a/drivers/input/mouse/focaltech_v2.c b/drivers/input/mouse/focaltech_v2.c
new file mode 100644
index 000000000..a3c7e81c5
--- /dev/null
+++ b/drivers/input/mouse/focaltech_v2.c
@@ -0,0 +1,265 @@ 
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Focaltech v2 TouchPad PS/2 mouse driver
+ *
+ * Copyright (c) 2021 Hamza Farooq <0xA6C4@gmail.com>
+ */
+
+
+#include <linux/device.h>
+#include <linux/libps2.h>
+#include <linux/input/mt.h>
+#include <linux/serio.h>
+#include <linux/slab.h>
+#include "psmouse.h"
+#include "focaltech_v2.h"
+
+static const struct fte_command switch_protocol[] = {
+	{PSMOUSE_CMD_SETRATE,	0xea},
+	{PSMOUSE_CMD_SETRATE,	0xed},
+	{PSMOUSE_CMD_ENABLE,	0x00},
+};
+
+static const char *const focaltech_pnp_ids[] = {
+	"FTE0001",
+	NULL};
+
+int focaltech_v2_detect(struct psmouse *psmouse, bool set_properties)
+{
+	if (!psmouse_matches_pnp_id(psmouse, focaltech_pnp_ids))
+		return -ENODEV;
+
+	if (set_properties) {
+		psmouse->vendor = "FocalTech";
+		psmouse->name = "Touchpad V2";
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_MOUSE_PS2_FOCALTECH_V2
+
+static void focaltech_report_state(struct psmouse *psmouse)
+{
+	struct focaltech_data *priv = psmouse->private;
+	struct focaltech_hw_state *state = &priv->state;
+	struct input_dev *dev = psmouse->dev;
+	int i;
+
+	for (i = 0; i < FOCALTECH_MAX_FINGERS; i++) {
+		struct focaltech_finger_state *finger = &state->fingers[i];
+
+		input_mt_slot(dev, i);
+		input_mt_report_slot_state(dev, MT_TOOL_FINGER, finger->valid);
+		if (finger->valid) {
+			input_report_abs(dev, ABS_MT_POSITION_X, finger->x);
+			input_report_abs(dev, ABS_MT_POSITION_Y, finger->y);
+			input_report_abs(dev, ABS_MT_TOUCH_MAJOR, finger->major);
+			input_report_abs(dev, ABS_MT_TOUCH_MINOR, finger->minor);
+			input_report_abs(dev, ABS_MT_PRESSURE, finger->pressure);
+		}
+	}
+	input_mt_sync_frame(dev);
+	input_report_key(dev, BTN_LEFT, state->left);
+	input_report_key(dev, BTN_RIGHT, state->right);
+	input_mt_report_finger_count(dev, state->fingerCount);
+	input_sync(dev);
+}
+
+static void focaltech_process_packet(struct psmouse *psmouse)
+{
+	unsigned char *packet = psmouse->packet;
+	struct focaltech_data *priv = psmouse->private;
+	struct focaltech_hw_state *state = &priv->state;
+	int i, j;
+
+	if (!priv->isReadNext) {
+		for (i = 0; i < 8; i++)
+			priv->lastDeviceData[i] = packet[i];
+		for (i = 8; i < 16; i++)
+			priv->lastDeviceData[i] = 0xff;
+		state->fingerCount = (int)(packet[4] & 3) + ((packet[4] & 48) >> 2);
+		if ((state->fingerCount > 2) && (packet[0] != 0xff && packet[1] != 0xff && packet[2] != 0xff && packet[3] != 0xff) && (packet[0] & 48) != 32)
+			priv->isReadNext = true;
+	} else {
+		priv->isReadNext = false;
+		for (i = 8; i < 16; i++)
+			priv->lastDeviceData[i] = packet[i - 8];
+	}
+	if (!priv->isReadNext) {
+		if (!((priv->lastDeviceData[0] == 0xff) && (priv->lastDeviceData[1] == 0xff) && (priv->lastDeviceData[2] == 0xff) && (priv->lastDeviceData[3] == 0xff))) {
+			if ((priv->lastDeviceData[0] & 1) == 1)
+				state->left = true;
+			else
+				state->left = false;
+			if ((priv->lastDeviceData[0] & 2) == 2)
+				state->right = true;
+			else
+				state->right = false;
+			if ((priv->lastDeviceData[0] & 48) == 16) {
+				for (i = 0; i < 4; i++) {
+					j = i * 4;
+					if (!((priv->lastDeviceData[j + 1] == 0xff) && (priv->lastDeviceData[j + 2] == 0xff) && (priv->lastDeviceData[j + 3] == 0xff))) {
+						state->fingers[i].minor = priv->lastDeviceData[j + 1];
+						state->fingers[i].major = priv->lastDeviceData[j + 2];
+						state->fingers[i].pressure = priv->lastDeviceData[j + 3] * 2;
+						if (state->fingers[i].pressure > MAX_PRESSURE)
+							state->fingers[i].pressure = MAX_PRESSURE;
+					}
+				}
+			} else {
+				for (i = 0; i < 4; i++) {
+					j = i * 4;
+					if (!((priv->lastDeviceData[j + 1] == 0xff) && (priv->lastDeviceData[j + 2] == 0xff) && (priv->lastDeviceData[j + 3] == 0xff))) {
+						state->fingers[i].valid = true;
+						state->fingers[i].x = (priv->lastDeviceData[j + 1] << 4) + ((priv->lastDeviceData[j + 3] & 240) >> 4);
+						state->fingers[i].y = (priv->lastDeviceData[j + 2] << 4) + (priv->lastDeviceData[j + 3] & 15);
+					} else
+						state->fingers[i].valid = false;
+				}
+			}
+			if (state->fingerCount == 0)
+				for (i = 0; i < 4; i++)
+					state->fingers[i].valid = false;
+		}
+	}
+	focaltech_report_state(psmouse);
+}
+
+static psmouse_ret_t focaltech_process_byte(struct psmouse *psmouse)
+{
+	if (psmouse->pktcnt >= 8) { /* packet received */
+		focaltech_process_packet(psmouse);
+		return PSMOUSE_FULL_PACKET;
+	}
+	return PSMOUSE_GOOD_DATA;
+}
+
+static int focaltech_switch_protocol(struct psmouse *psmouse)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	unsigned char param[4];
+	size_t i;
+
+	for (i = 0; i < ARRAY_SIZE(switch_protocol); ++i) {
+		memset(param, 0, sizeof(param));
+		param[0] = switch_protocol[i].data;
+		if (ps2_command(ps2dev, param, switch_protocol[i].command))
+			return -EIO;
+	}
+
+	return 0;
+}
+
+static void focaltech_reset(struct psmouse *psmouse)
+{
+	ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
+	psmouse_reset(psmouse);
+}
+
+static void focaltech_disconnect(struct psmouse *psmouse)
+{
+	focaltech_reset(psmouse);
+	kfree(psmouse->private);
+	psmouse->private = NULL;
+}
+
+static int focaltech_reconnect(struct psmouse *psmouse)
+{
+	int error;
+
+	focaltech_reset(psmouse);
+
+	error = focaltech_switch_protocol(psmouse);
+	if (error) {
+		psmouse_err(psmouse, "Unable to initialize the device\n");
+		return error;
+	}
+
+	return 0;
+}
+
+static void focaltech_set_input_params(struct psmouse *psmouse)
+{
+	struct input_dev *dev = psmouse->dev;
+
+	/*
+	 * Undo part of setup done for us by psmouse core since touchpad
+	 * is not a relative device.
+	 */
+	__clear_bit(EV_REL, dev->evbit);
+	__clear_bit(REL_X, dev->relbit);
+	__clear_bit(REL_Y, dev->relbit);
+
+	/*
+	 * Now set up our capabilities.
+	 */
+	__set_bit(EV_ABS, dev->evbit);
+	input_set_abs_params(dev, ABS_MT_POSITION_X, 0, MAX_X, 0, 0);
+	input_set_abs_params(dev, ABS_MT_POSITION_Y, 0, MAX_Y, 0, 0);
+	input_set_abs_params(dev, ABS_MT_PRESSURE, 0, MAX_PRESSURE, 0, 0);
+	input_set_abs_params(dev, ABS_MT_TOUCH_MINOR, 0, MAX_MAJOR, 0, 0);
+	input_set_abs_params(dev, ABS_MT_TOUCH_MAJOR, 0, MAX_MINOR, 0, 0);
+	input_abs_set_res(dev, ABS_MT_POSITION_X, RESOLUTION);
+	input_abs_set_res(dev, ABS_MT_POSITION_Y, RESOLUTION);
+	input_mt_init_slots(dev, FOCALTECH_MAX_FINGERS, INPUT_MT_POINTER);
+}
+
+static void focaltech_set_resolution(struct psmouse *psmouse, unsigned int resolution)
+{
+	/* not supported yet */
+}
+
+static void focaltech_set_rate(struct psmouse *psmouse, unsigned int rate)
+{
+	/* not supported yet */
+}
+
+static void focaltech_set_scale(struct psmouse *psmouse, enum psmouse_scale scale)
+{
+	/* not supported yet */
+}
+
+int focaltech_v2_init(struct psmouse *psmouse)
+{
+	struct focaltech_data *priv;
+	int error;
+
+	psmouse->private = priv = kzalloc(sizeof(struct focaltech_data), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	focaltech_reset(psmouse);
+
+	error = focaltech_switch_protocol(psmouse);
+	if (error) {
+		psmouse_err(psmouse, "Unable to initialize the device\n");
+		goto fail;
+	}
+
+	focaltech_set_input_params(psmouse);
+
+	psmouse->protocol_handler = focaltech_process_byte;
+	psmouse->pktsize = 8;
+	psmouse->disconnect = focaltech_disconnect;
+	psmouse->reconnect = focaltech_reconnect;
+	psmouse->cleanup = focaltech_reset;
+	/* resync is not supported yet */
+	psmouse->resync_time = 0;
+	/*
+	 * rate/resolution/scale changes are not supported yet, and
+	 * the generic implementations of these functions seem to
+	 * confuse some touchpads
+	 */
+	psmouse->set_resolution = focaltech_set_resolution;
+	psmouse->set_rate = focaltech_set_rate;
+	psmouse->set_scale = focaltech_set_scale;
+
+	return 0;
+
+fail:
+	focaltech_reset(psmouse);
+	kfree(priv);
+	return error;
+}
+#endif /* CONFIG_MOUSE_PS2_FOCALTECH_V2 */
diff --git a/drivers/input/mouse/focaltech_v2.h b/drivers/input/mouse/focaltech_v2.h
new file mode 100644
index 000000000..49c69b9a2
--- /dev/null
+++ b/drivers/input/mouse/focaltech_v2.h
@@ -0,0 +1,56 @@ 
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Focaltech v2 TouchPad PS/2 mouse driver
+ *
+ * Copyright (c) 2021 Hamza Farooq <0xA6C4@gmail.com>
+ */
+
+#ifndef _FOCALTECH_V2_H
+#define _FOCALTECH_V2_H
+
+#define FOCALTECH_MAX_FINGERS 4
+#define MAX_X			0x08E0  /* 2272 */
+#define MAX_Y			0x03E0  /* 992 */
+#define RESOLUTION		26	/* 87mm x 38mm */
+#define MAX_MAJOR		10
+#define MAX_MINOR		10
+#define MAX_PRESSURE	127
+
+struct fte_command {
+	int command;
+	unsigned char data;
+};
+
+struct focaltech_finger_state {
+	int x;
+	int y;
+	int major;
+	int minor;
+	int pressure;
+	bool valid;
+};
+
+struct focaltech_hw_state {
+	struct focaltech_finger_state fingers[FOCALTECH_MAX_FINGERS];
+	int fingerCount;
+	bool left;
+	bool right;
+};
+
+struct focaltech_data {
+	bool isReadNext;
+	int lastDeviceData[16];
+	struct focaltech_hw_state state;
+};
+
+#ifdef CONFIG_MOUSE_PS2_FOCALTECH_V2
+int focaltech_v2_detect(struct psmouse *psmouse, bool set_properties);
+int focaltech_v2_init(struct psmouse *psmouse);
+#else
+static inline int focaltech_v2_init(struct psmouse *psmouse)
+{
+	return -ENOSYS;
+}
+#endif
+
+#endif
diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c
index 0b4a3039f..5f720af51 100644
--- a/drivers/input/mouse/psmouse-base.c
+++ b/drivers/input/mouse/psmouse-base.c
@@ -34,6 +34,7 @@ 
 #include "sentelic.h"
 #include "cypress_ps2.h"
 #include "focaltech.h"
+#include "focaltech_v2.h"
 #include "vmmouse.h"
 #include "byd.h"
 
@@ -891,6 +892,15 @@  static const struct psmouse_protocol psmouse_protocols[] = {
 		.init		= focaltech_init,
 	},
 #endif
+#ifdef CONFIG_MOUSE_PS2_FOCALTECH_V2
+	{
+		.type		= PSMOUSE_FOCALTECH_V2,
+		.name		= "FocalTechPS/2",
+		.alias		= "focaltech-v2",
+		.detect		= focaltech_detect,
+		.init		= focaltech_init,
+	},
+#endif
 #ifdef CONFIG_MOUSE_PS2_VMMOUSE
 	{
 		.type		= PSMOUSE_VMMOUSE,
@@ -1072,6 +1082,28 @@  static int psmouse_extensions(struct psmouse *psmouse,
 		psmouse_max_proto = max_proto = PSMOUSE_PS2;
 	}
 
+	/*
+	 * Always check for focaltech-v2, this is safe as it uses pnp-id
+	 * matching.
+	 */
+	if (psmouse_do_detect(focaltech_v2_detect,
+			      psmouse, false, set_properties)) {
+		if (max_proto > PSMOUSE_IMEX &&
+		    IS_ENABLED(CONFIG_MOUSE_PS2_FOCALTECH_V2) &&
+		    (!set_properties || focaltech_v2_init(psmouse) == 0)) {
+			return PSMOUSE_FOCALTECH_V2;
+		}
+		/*
+		 * Restrict psmouse_max_proto so that psmouse_initialize()
+		 * does not try to reset rate and resolution, because even
+		 * that upsets the device.
+		 * This also causes us to basically fall through to basic
+		 * protocol detection, where we fully reset the mouse,
+		 * and set it up as bare PS/2 protocol device.
+		 */
+		psmouse_max_proto = max_proto = PSMOUSE_PS2;
+	}
+
 	/*
 	 * We always check for LifeBook because it does not disturb mouse
 	 * (it only checks DMI information).
diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h
index 64c3a5d3f..68e06aaac 100644
--- a/drivers/input/mouse/psmouse.h
+++ b/drivers/input/mouse/psmouse.h
@@ -65,6 +65,7 @@  enum psmouse_type {
 	PSMOUSE_SYNAPTICS_RELATIVE,
 	PSMOUSE_CYPRESS,
 	PSMOUSE_FOCALTECH,
+	PSMOUSE_FOCALTECH_V2,
 	PSMOUSE_VMMOUSE,
 	PSMOUSE_BYD,
 	PSMOUSE_SYNAPTICS_SMBUS,