18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Cypress Trackpad PS/2 mouse driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2012 Cypress Semiconductor Corporation.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Author:
88c2ecf20Sopenharmony_ci *   Dudley Du <dudl@cypress.com>
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * Additional contributors include:
118c2ecf20Sopenharmony_ci *   Kamal Mostafa <kamal@canonical.com>
128c2ecf20Sopenharmony_ci *   Kyle Fazzari <git@status.e4ward.com>
138c2ecf20Sopenharmony_ci */
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <linux/module.h>
168c2ecf20Sopenharmony_ci#include <linux/kernel.h>
178c2ecf20Sopenharmony_ci#include <linux/slab.h>
188c2ecf20Sopenharmony_ci#include <linux/serio.h>
198c2ecf20Sopenharmony_ci#include <linux/libps2.h>
208c2ecf20Sopenharmony_ci#include <linux/input.h>
218c2ecf20Sopenharmony_ci#include <linux/input/mt.h>
228c2ecf20Sopenharmony_ci#include <linux/sched.h>
238c2ecf20Sopenharmony_ci#include <linux/wait.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include "cypress_ps2.h"
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#undef CYTP_DEBUG_VERBOSE  /* define this and DEBUG for more verbose dump */
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistatic void cypress_set_packet_size(struct psmouse *psmouse, unsigned int n)
308c2ecf20Sopenharmony_ci{
318c2ecf20Sopenharmony_ci	struct cytp_data *cytp = psmouse->private;
328c2ecf20Sopenharmony_ci	cytp->pkt_size = n;
338c2ecf20Sopenharmony_ci}
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic const unsigned char cytp_rate[] = {10, 20, 40, 60, 100, 200};
368c2ecf20Sopenharmony_cistatic const unsigned char cytp_resolution[] = {0x00, 0x01, 0x02, 0x03};
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic int cypress_ps2_sendbyte(struct psmouse *psmouse, int value)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	struct ps2dev *ps2dev = &psmouse->ps2dev;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	if (ps2_sendbyte(ps2dev, value & 0xff, CYTP_CMD_TIMEOUT) < 0) {
438c2ecf20Sopenharmony_ci		psmouse_dbg(psmouse,
448c2ecf20Sopenharmony_ci				"sending command 0x%02x failed, resp 0x%02x\n",
458c2ecf20Sopenharmony_ci				value & 0xff, ps2dev->nak);
468c2ecf20Sopenharmony_ci		if (ps2dev->nak == CYTP_PS2_RETRY)
478c2ecf20Sopenharmony_ci			return CYTP_PS2_RETRY;
488c2ecf20Sopenharmony_ci		else
498c2ecf20Sopenharmony_ci			return CYTP_PS2_ERROR;
508c2ecf20Sopenharmony_ci	}
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci#ifdef CYTP_DEBUG_VERBOSE
538c2ecf20Sopenharmony_ci	psmouse_dbg(psmouse, "sending command 0x%02x succeeded, resp 0xfa\n",
548c2ecf20Sopenharmony_ci			value & 0xff);
558c2ecf20Sopenharmony_ci#endif
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	return 0;
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic int cypress_ps2_ext_cmd(struct psmouse *psmouse, unsigned short cmd,
618c2ecf20Sopenharmony_ci			       unsigned char data)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	struct ps2dev *ps2dev = &psmouse->ps2dev;
648c2ecf20Sopenharmony_ci	int tries = CYTP_PS2_CMD_TRIES;
658c2ecf20Sopenharmony_ci	int rc;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	ps2_begin_command(ps2dev);
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	do {
708c2ecf20Sopenharmony_ci		/*
718c2ecf20Sopenharmony_ci		 * Send extension command byte (0xE8 or 0xF3).
728c2ecf20Sopenharmony_ci		 * If sending the command fails, send recovery command
738c2ecf20Sopenharmony_ci		 * to make the device return to the ready state.
748c2ecf20Sopenharmony_ci		 */
758c2ecf20Sopenharmony_ci		rc = cypress_ps2_sendbyte(psmouse, cmd & 0xff);
768c2ecf20Sopenharmony_ci		if (rc == CYTP_PS2_RETRY) {
778c2ecf20Sopenharmony_ci			rc = cypress_ps2_sendbyte(psmouse, 0x00);
788c2ecf20Sopenharmony_ci			if (rc == CYTP_PS2_RETRY)
798c2ecf20Sopenharmony_ci				rc = cypress_ps2_sendbyte(psmouse, 0x0a);
808c2ecf20Sopenharmony_ci		}
818c2ecf20Sopenharmony_ci		if (rc == CYTP_PS2_ERROR)
828c2ecf20Sopenharmony_ci			continue;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci		rc = cypress_ps2_sendbyte(psmouse, data);
858c2ecf20Sopenharmony_ci		if (rc == CYTP_PS2_RETRY)
868c2ecf20Sopenharmony_ci			rc = cypress_ps2_sendbyte(psmouse, data);
878c2ecf20Sopenharmony_ci		if (rc == CYTP_PS2_ERROR)
888c2ecf20Sopenharmony_ci			continue;
898c2ecf20Sopenharmony_ci		else
908c2ecf20Sopenharmony_ci			break;
918c2ecf20Sopenharmony_ci	} while (--tries > 0);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	ps2_end_command(ps2dev);
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	return rc;
968c2ecf20Sopenharmony_ci}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_cistatic int cypress_ps2_read_cmd_status(struct psmouse *psmouse,
998c2ecf20Sopenharmony_ci				       unsigned char cmd,
1008c2ecf20Sopenharmony_ci				       unsigned char *param)
1018c2ecf20Sopenharmony_ci{
1028c2ecf20Sopenharmony_ci	int rc;
1038c2ecf20Sopenharmony_ci	struct ps2dev *ps2dev = &psmouse->ps2dev;
1048c2ecf20Sopenharmony_ci	enum psmouse_state old_state;
1058c2ecf20Sopenharmony_ci	int pktsize;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	ps2_begin_command(ps2dev);
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	old_state = psmouse->state;
1108c2ecf20Sopenharmony_ci	psmouse->state = PSMOUSE_CMD_MODE;
1118c2ecf20Sopenharmony_ci	psmouse->pktcnt = 0;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	pktsize = (cmd == CYTP_CMD_READ_TP_METRICS) ? 8 : 3;
1148c2ecf20Sopenharmony_ci	memset(param, 0, pktsize);
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	rc = cypress_ps2_sendbyte(psmouse, 0xe9);
1178c2ecf20Sopenharmony_ci	if (rc < 0)
1188c2ecf20Sopenharmony_ci		goto out;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	wait_event_timeout(ps2dev->wait,
1218c2ecf20Sopenharmony_ci			(psmouse->pktcnt >= pktsize),
1228c2ecf20Sopenharmony_ci			msecs_to_jiffies(CYTP_CMD_TIMEOUT));
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	memcpy(param, psmouse->packet, pktsize);
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	psmouse_dbg(psmouse, "Command 0x%02x response data (0x): %*ph\n",
1278c2ecf20Sopenharmony_ci			cmd, pktsize, param);
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ciout:
1308c2ecf20Sopenharmony_ci	psmouse->state = old_state;
1318c2ecf20Sopenharmony_ci	psmouse->pktcnt = 0;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	ps2_end_command(ps2dev);
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	return rc;
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_cistatic bool cypress_verify_cmd_state(struct psmouse *psmouse,
1398c2ecf20Sopenharmony_ci				     unsigned char cmd, unsigned char *param)
1408c2ecf20Sopenharmony_ci{
1418c2ecf20Sopenharmony_ci	bool rate_match = false;
1428c2ecf20Sopenharmony_ci	bool resolution_match = false;
1438c2ecf20Sopenharmony_ci	int i;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	/* callers will do further checking. */
1468c2ecf20Sopenharmony_ci	if (cmd == CYTP_CMD_READ_CYPRESS_ID ||
1478c2ecf20Sopenharmony_ci	    cmd == CYTP_CMD_STANDARD_MODE ||
1488c2ecf20Sopenharmony_ci	    cmd == CYTP_CMD_READ_TP_METRICS)
1498c2ecf20Sopenharmony_ci		return true;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	if ((~param[0] & DFLT_RESP_BITS_VALID) == DFLT_RESP_BITS_VALID &&
1528c2ecf20Sopenharmony_ci	    (param[0] & DFLT_RESP_BIT_MODE) == DFLT_RESP_STREAM_MODE) {
1538c2ecf20Sopenharmony_ci		for (i = 0; i < sizeof(cytp_resolution); i++)
1548c2ecf20Sopenharmony_ci			if (cytp_resolution[i] == param[1])
1558c2ecf20Sopenharmony_ci				resolution_match = true;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci		for (i = 0; i < sizeof(cytp_rate); i++)
1588c2ecf20Sopenharmony_ci			if (cytp_rate[i] == param[2])
1598c2ecf20Sopenharmony_ci				rate_match = true;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci		if (resolution_match && rate_match)
1628c2ecf20Sopenharmony_ci			return true;
1638c2ecf20Sopenharmony_ci	}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	psmouse_dbg(psmouse, "verify cmd state failed.\n");
1668c2ecf20Sopenharmony_ci	return false;
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistatic int cypress_send_ext_cmd(struct psmouse *psmouse, unsigned char cmd,
1708c2ecf20Sopenharmony_ci				unsigned char *param)
1718c2ecf20Sopenharmony_ci{
1728c2ecf20Sopenharmony_ci	int tries = CYTP_PS2_CMD_TRIES;
1738c2ecf20Sopenharmony_ci	int rc;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	psmouse_dbg(psmouse, "send extension cmd 0x%02x, [%d %d %d %d]\n",
1768c2ecf20Sopenharmony_ci		 cmd, DECODE_CMD_AA(cmd), DECODE_CMD_BB(cmd),
1778c2ecf20Sopenharmony_ci		 DECODE_CMD_CC(cmd), DECODE_CMD_DD(cmd));
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	do {
1808c2ecf20Sopenharmony_ci		cypress_ps2_ext_cmd(psmouse,
1818c2ecf20Sopenharmony_ci				    PSMOUSE_CMD_SETRES, DECODE_CMD_DD(cmd));
1828c2ecf20Sopenharmony_ci		cypress_ps2_ext_cmd(psmouse,
1838c2ecf20Sopenharmony_ci				    PSMOUSE_CMD_SETRES, DECODE_CMD_CC(cmd));
1848c2ecf20Sopenharmony_ci		cypress_ps2_ext_cmd(psmouse,
1858c2ecf20Sopenharmony_ci				    PSMOUSE_CMD_SETRES, DECODE_CMD_BB(cmd));
1868c2ecf20Sopenharmony_ci		cypress_ps2_ext_cmd(psmouse,
1878c2ecf20Sopenharmony_ci				    PSMOUSE_CMD_SETRES, DECODE_CMD_AA(cmd));
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci		rc = cypress_ps2_read_cmd_status(psmouse, cmd, param);
1908c2ecf20Sopenharmony_ci		if (rc)
1918c2ecf20Sopenharmony_ci			continue;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci		if (cypress_verify_cmd_state(psmouse, cmd, param))
1948c2ecf20Sopenharmony_ci			return 0;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	} while (--tries > 0);
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	return -EIO;
1998c2ecf20Sopenharmony_ci}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ciint cypress_detect(struct psmouse *psmouse, bool set_properties)
2028c2ecf20Sopenharmony_ci{
2038c2ecf20Sopenharmony_ci	unsigned char param[3];
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	if (cypress_send_ext_cmd(psmouse, CYTP_CMD_READ_CYPRESS_ID, param))
2068c2ecf20Sopenharmony_ci		return -ENODEV;
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	/* Check for Cypress Trackpad signature bytes: 0x33 0xCC */
2098c2ecf20Sopenharmony_ci	if (param[0] != 0x33 || param[1] != 0xCC)
2108c2ecf20Sopenharmony_ci		return -ENODEV;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	if (set_properties) {
2138c2ecf20Sopenharmony_ci		psmouse->vendor = "Cypress";
2148c2ecf20Sopenharmony_ci		psmouse->name = "Trackpad";
2158c2ecf20Sopenharmony_ci	}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	return 0;
2188c2ecf20Sopenharmony_ci}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_cistatic int cypress_read_fw_version(struct psmouse *psmouse)
2218c2ecf20Sopenharmony_ci{
2228c2ecf20Sopenharmony_ci	struct cytp_data *cytp = psmouse->private;
2238c2ecf20Sopenharmony_ci	unsigned char param[3];
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	if (cypress_send_ext_cmd(psmouse, CYTP_CMD_READ_CYPRESS_ID, param))
2268c2ecf20Sopenharmony_ci		return -ENODEV;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	/* Check for Cypress Trackpad signature bytes: 0x33 0xCC */
2298c2ecf20Sopenharmony_ci	if (param[0] != 0x33 || param[1] != 0xCC)
2308c2ecf20Sopenharmony_ci		return -ENODEV;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	cytp->fw_version = param[2] & FW_VERSION_MASX;
2338c2ecf20Sopenharmony_ci	cytp->tp_metrics_supported = (param[2] & TP_METRICS_MASK) ? 1 : 0;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	/*
2368c2ecf20Sopenharmony_ci	 * Trackpad fw_version 11 (in Dell XPS12) yields a bogus response to
2378c2ecf20Sopenharmony_ci	 * CYTP_CMD_READ_TP_METRICS so do not try to use it. LP: #1103594.
2388c2ecf20Sopenharmony_ci	 */
2398c2ecf20Sopenharmony_ci	if (cytp->fw_version >= 11)
2408c2ecf20Sopenharmony_ci		cytp->tp_metrics_supported = 0;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	psmouse_dbg(psmouse, "cytp->fw_version = %d\n", cytp->fw_version);
2438c2ecf20Sopenharmony_ci	psmouse_dbg(psmouse, "cytp->tp_metrics_supported = %d\n",
2448c2ecf20Sopenharmony_ci		 cytp->tp_metrics_supported);
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	return 0;
2478c2ecf20Sopenharmony_ci}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_cistatic int cypress_read_tp_metrics(struct psmouse *psmouse)
2508c2ecf20Sopenharmony_ci{
2518c2ecf20Sopenharmony_ci	struct cytp_data *cytp = psmouse->private;
2528c2ecf20Sopenharmony_ci	unsigned char param[8];
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	/* set default values for tp metrics. */
2558c2ecf20Sopenharmony_ci	cytp->tp_width = CYTP_DEFAULT_WIDTH;
2568c2ecf20Sopenharmony_ci	cytp->tp_high = CYTP_DEFAULT_HIGH;
2578c2ecf20Sopenharmony_ci	cytp->tp_max_abs_x = CYTP_ABS_MAX_X;
2588c2ecf20Sopenharmony_ci	cytp->tp_max_abs_y = CYTP_ABS_MAX_Y;
2598c2ecf20Sopenharmony_ci	cytp->tp_min_pressure = CYTP_MIN_PRESSURE;
2608c2ecf20Sopenharmony_ci	cytp->tp_max_pressure = CYTP_MAX_PRESSURE;
2618c2ecf20Sopenharmony_ci	cytp->tp_res_x = cytp->tp_max_abs_x / cytp->tp_width;
2628c2ecf20Sopenharmony_ci	cytp->tp_res_y = cytp->tp_max_abs_y / cytp->tp_high;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	if (!cytp->tp_metrics_supported)
2658c2ecf20Sopenharmony_ci		return 0;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	memset(param, 0, sizeof(param));
2688c2ecf20Sopenharmony_ci	if (cypress_send_ext_cmd(psmouse, CYTP_CMD_READ_TP_METRICS, param) == 0) {
2698c2ecf20Sopenharmony_ci		/* Update trackpad parameters. */
2708c2ecf20Sopenharmony_ci		cytp->tp_max_abs_x = (param[1] << 8) | param[0];
2718c2ecf20Sopenharmony_ci		cytp->tp_max_abs_y = (param[3] << 8) | param[2];
2728c2ecf20Sopenharmony_ci		cytp->tp_min_pressure = param[4];
2738c2ecf20Sopenharmony_ci		cytp->tp_max_pressure = param[5];
2748c2ecf20Sopenharmony_ci	}
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	if (!cytp->tp_max_pressure ||
2778c2ecf20Sopenharmony_ci	    cytp->tp_max_pressure < cytp->tp_min_pressure ||
2788c2ecf20Sopenharmony_ci	    !cytp->tp_width || !cytp->tp_high ||
2798c2ecf20Sopenharmony_ci	    !cytp->tp_max_abs_x ||
2808c2ecf20Sopenharmony_ci	    cytp->tp_max_abs_x < cytp->tp_width ||
2818c2ecf20Sopenharmony_ci	    !cytp->tp_max_abs_y ||
2828c2ecf20Sopenharmony_ci	    cytp->tp_max_abs_y < cytp->tp_high)
2838c2ecf20Sopenharmony_ci		return -EINVAL;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	cytp->tp_res_x = cytp->tp_max_abs_x / cytp->tp_width;
2868c2ecf20Sopenharmony_ci	cytp->tp_res_y = cytp->tp_max_abs_y / cytp->tp_high;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci#ifdef CYTP_DEBUG_VERBOSE
2898c2ecf20Sopenharmony_ci	psmouse_dbg(psmouse, "Dump trackpad hardware configuration as below:\n");
2908c2ecf20Sopenharmony_ci	psmouse_dbg(psmouse, "cytp->tp_width = %d\n", cytp->tp_width);
2918c2ecf20Sopenharmony_ci	psmouse_dbg(psmouse, "cytp->tp_high = %d\n", cytp->tp_high);
2928c2ecf20Sopenharmony_ci	psmouse_dbg(psmouse, "cytp->tp_max_abs_x = %d\n", cytp->tp_max_abs_x);
2938c2ecf20Sopenharmony_ci	psmouse_dbg(psmouse, "cytp->tp_max_abs_y = %d\n", cytp->tp_max_abs_y);
2948c2ecf20Sopenharmony_ci	psmouse_dbg(psmouse, "cytp->tp_min_pressure = %d\n", cytp->tp_min_pressure);
2958c2ecf20Sopenharmony_ci	psmouse_dbg(psmouse, "cytp->tp_max_pressure = %d\n", cytp->tp_max_pressure);
2968c2ecf20Sopenharmony_ci	psmouse_dbg(psmouse, "cytp->tp_res_x = %d\n", cytp->tp_res_x);
2978c2ecf20Sopenharmony_ci	psmouse_dbg(psmouse, "cytp->tp_res_y = %d\n", cytp->tp_res_y);
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	psmouse_dbg(psmouse, "tp_type_APA = %d\n",
3008c2ecf20Sopenharmony_ci			(param[6] & TP_METRICS_BIT_APA) ? 1 : 0);
3018c2ecf20Sopenharmony_ci	psmouse_dbg(psmouse, "tp_type_MTG = %d\n",
3028c2ecf20Sopenharmony_ci			(param[6] & TP_METRICS_BIT_MTG) ? 1 : 0);
3038c2ecf20Sopenharmony_ci	psmouse_dbg(psmouse, "tp_palm = %d\n",
3048c2ecf20Sopenharmony_ci			(param[6] & TP_METRICS_BIT_PALM) ? 1 : 0);
3058c2ecf20Sopenharmony_ci	psmouse_dbg(psmouse, "tp_stubborn = %d\n",
3068c2ecf20Sopenharmony_ci			(param[6] & TP_METRICS_BIT_STUBBORN) ? 1 : 0);
3078c2ecf20Sopenharmony_ci	psmouse_dbg(psmouse, "tp_1f_jitter = %d\n",
3088c2ecf20Sopenharmony_ci			(param[6] & TP_METRICS_BIT_1F_JITTER) >> 2);
3098c2ecf20Sopenharmony_ci	psmouse_dbg(psmouse, "tp_2f_jitter = %d\n",
3108c2ecf20Sopenharmony_ci			(param[6] & TP_METRICS_BIT_2F_JITTER) >> 4);
3118c2ecf20Sopenharmony_ci	psmouse_dbg(psmouse, "tp_1f_spike = %d\n",
3128c2ecf20Sopenharmony_ci			param[7] & TP_METRICS_BIT_1F_SPIKE);
3138c2ecf20Sopenharmony_ci	psmouse_dbg(psmouse, "tp_2f_spike = %d\n",
3148c2ecf20Sopenharmony_ci			(param[7] & TP_METRICS_BIT_2F_SPIKE) >> 2);
3158c2ecf20Sopenharmony_ci	psmouse_dbg(psmouse, "tp_abs_packet_format_set = %d\n",
3168c2ecf20Sopenharmony_ci			(param[7] & TP_METRICS_BIT_ABS_PKT_FORMAT_SET) >> 4);
3178c2ecf20Sopenharmony_ci#endif
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	return 0;
3208c2ecf20Sopenharmony_ci}
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_cistatic int cypress_query_hardware(struct psmouse *psmouse)
3238c2ecf20Sopenharmony_ci{
3248c2ecf20Sopenharmony_ci	int ret;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	ret = cypress_read_fw_version(psmouse);
3278c2ecf20Sopenharmony_ci	if (ret)
3288c2ecf20Sopenharmony_ci		return ret;
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	ret = cypress_read_tp_metrics(psmouse);
3318c2ecf20Sopenharmony_ci	if (ret)
3328c2ecf20Sopenharmony_ci		return ret;
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	return 0;
3358c2ecf20Sopenharmony_ci}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_cistatic int cypress_set_absolute_mode(struct psmouse *psmouse)
3388c2ecf20Sopenharmony_ci{
3398c2ecf20Sopenharmony_ci	struct cytp_data *cytp = psmouse->private;
3408c2ecf20Sopenharmony_ci	unsigned char param[3];
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	if (cypress_send_ext_cmd(psmouse, CYTP_CMD_ABS_WITH_PRESSURE_MODE, param) < 0)
3438c2ecf20Sopenharmony_ci		return -1;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	cytp->mode = (cytp->mode & ~CYTP_BIT_ABS_REL_MASK)
3468c2ecf20Sopenharmony_ci			| CYTP_BIT_ABS_PRESSURE;
3478c2ecf20Sopenharmony_ci	cypress_set_packet_size(psmouse, 5);
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	return 0;
3508c2ecf20Sopenharmony_ci}
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci/*
3538c2ecf20Sopenharmony_ci * Reset trackpad device.
3548c2ecf20Sopenharmony_ci * This is also the default mode when trackpad powered on.
3558c2ecf20Sopenharmony_ci */
3568c2ecf20Sopenharmony_cistatic void cypress_reset(struct psmouse *psmouse)
3578c2ecf20Sopenharmony_ci{
3588c2ecf20Sopenharmony_ci	struct cytp_data *cytp = psmouse->private;
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	cytp->mode = 0;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	psmouse_reset(psmouse);
3638c2ecf20Sopenharmony_ci}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_cistatic int cypress_set_input_params(struct input_dev *input,
3668c2ecf20Sopenharmony_ci				    struct cytp_data *cytp)
3678c2ecf20Sopenharmony_ci{
3688c2ecf20Sopenharmony_ci	int ret;
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	if (!cytp->tp_res_x || !cytp->tp_res_y)
3718c2ecf20Sopenharmony_ci		return -EINVAL;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	__set_bit(EV_ABS, input->evbit);
3748c2ecf20Sopenharmony_ci	input_set_abs_params(input, ABS_X, 0, cytp->tp_max_abs_x, 0, 0);
3758c2ecf20Sopenharmony_ci	input_set_abs_params(input, ABS_Y, 0, cytp->tp_max_abs_y, 0, 0);
3768c2ecf20Sopenharmony_ci	input_set_abs_params(input, ABS_PRESSURE,
3778c2ecf20Sopenharmony_ci			     cytp->tp_min_pressure, cytp->tp_max_pressure, 0, 0);
3788c2ecf20Sopenharmony_ci	input_set_abs_params(input, ABS_TOOL_WIDTH, 0, 255, 0, 0);
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	/* finger position */
3818c2ecf20Sopenharmony_ci	input_set_abs_params(input, ABS_MT_POSITION_X, 0, cytp->tp_max_abs_x, 0, 0);
3828c2ecf20Sopenharmony_ci	input_set_abs_params(input, ABS_MT_POSITION_Y, 0, cytp->tp_max_abs_y, 0, 0);
3838c2ecf20Sopenharmony_ci	input_set_abs_params(input, ABS_MT_PRESSURE, 0, 255, 0, 0);
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	ret = input_mt_init_slots(input, CYTP_MAX_MT_SLOTS,
3868c2ecf20Sopenharmony_ci			INPUT_MT_DROP_UNUSED|INPUT_MT_TRACK);
3878c2ecf20Sopenharmony_ci	if (ret < 0)
3888c2ecf20Sopenharmony_ci		return ret;
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	__set_bit(INPUT_PROP_SEMI_MT, input->propbit);
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	input_abs_set_res(input, ABS_X, cytp->tp_res_x);
3938c2ecf20Sopenharmony_ci	input_abs_set_res(input, ABS_Y, cytp->tp_res_y);
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	input_abs_set_res(input, ABS_MT_POSITION_X, cytp->tp_res_x);
3968c2ecf20Sopenharmony_ci	input_abs_set_res(input, ABS_MT_POSITION_Y, cytp->tp_res_y);
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	__set_bit(BTN_TOUCH, input->keybit);
3998c2ecf20Sopenharmony_ci	__set_bit(BTN_TOOL_FINGER, input->keybit);
4008c2ecf20Sopenharmony_ci	__set_bit(BTN_TOOL_DOUBLETAP, input->keybit);
4018c2ecf20Sopenharmony_ci	__set_bit(BTN_TOOL_TRIPLETAP, input->keybit);
4028c2ecf20Sopenharmony_ci	__set_bit(BTN_TOOL_QUADTAP, input->keybit);
4038c2ecf20Sopenharmony_ci	__set_bit(BTN_TOOL_QUINTTAP, input->keybit);
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	__clear_bit(EV_REL, input->evbit);
4068c2ecf20Sopenharmony_ci	__clear_bit(REL_X, input->relbit);
4078c2ecf20Sopenharmony_ci	__clear_bit(REL_Y, input->relbit);
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	__set_bit(EV_KEY, input->evbit);
4108c2ecf20Sopenharmony_ci	__set_bit(BTN_LEFT, input->keybit);
4118c2ecf20Sopenharmony_ci	__set_bit(BTN_RIGHT, input->keybit);
4128c2ecf20Sopenharmony_ci	__set_bit(BTN_MIDDLE, input->keybit);
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	return 0;
4158c2ecf20Sopenharmony_ci}
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_cistatic int cypress_get_finger_count(unsigned char header_byte)
4188c2ecf20Sopenharmony_ci{
4198c2ecf20Sopenharmony_ci	unsigned char bits6_7;
4208c2ecf20Sopenharmony_ci	int finger_count;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	bits6_7 = header_byte >> 6;
4238c2ecf20Sopenharmony_ci	finger_count = bits6_7 & 0x03;
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	if (finger_count == 1)
4268c2ecf20Sopenharmony_ci		return 1;
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	if (header_byte & ABS_HSCROLL_BIT) {
4298c2ecf20Sopenharmony_ci		/* HSCROLL gets added on to 0 finger count. */
4308c2ecf20Sopenharmony_ci		switch (finger_count) {
4318c2ecf20Sopenharmony_ci			case 0:	return 4;
4328c2ecf20Sopenharmony_ci			case 2: return 5;
4338c2ecf20Sopenharmony_ci			default:
4348c2ecf20Sopenharmony_ci				/* Invalid contact (e.g. palm). Ignore it. */
4358c2ecf20Sopenharmony_ci				return 0;
4368c2ecf20Sopenharmony_ci		}
4378c2ecf20Sopenharmony_ci	}
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	return finger_count;
4408c2ecf20Sopenharmony_ci}
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_cistatic int cypress_parse_packet(struct psmouse *psmouse,
4448c2ecf20Sopenharmony_ci				struct cytp_data *cytp, struct cytp_report_data *report_data)
4458c2ecf20Sopenharmony_ci{
4468c2ecf20Sopenharmony_ci	unsigned char *packet = psmouse->packet;
4478c2ecf20Sopenharmony_ci	unsigned char header_byte = packet[0];
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	memset(report_data, 0, sizeof(struct cytp_report_data));
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	report_data->contact_cnt = cypress_get_finger_count(header_byte);
4528c2ecf20Sopenharmony_ci	report_data->tap = (header_byte & ABS_MULTIFINGER_TAP) ? 1 : 0;
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	if (report_data->contact_cnt == 1) {
4558c2ecf20Sopenharmony_ci		report_data->contacts[0].x =
4568c2ecf20Sopenharmony_ci			((packet[1] & 0x70) << 4) | packet[2];
4578c2ecf20Sopenharmony_ci		report_data->contacts[0].y =
4588c2ecf20Sopenharmony_ci			((packet[1] & 0x07) << 8) | packet[3];
4598c2ecf20Sopenharmony_ci		if (cytp->mode & CYTP_BIT_ABS_PRESSURE)
4608c2ecf20Sopenharmony_ci			report_data->contacts[0].z = packet[4];
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	} else if (report_data->contact_cnt >= 2) {
4638c2ecf20Sopenharmony_ci		report_data->contacts[0].x =
4648c2ecf20Sopenharmony_ci			((packet[1] & 0x70) << 4) | packet[2];
4658c2ecf20Sopenharmony_ci		report_data->contacts[0].y =
4668c2ecf20Sopenharmony_ci			((packet[1] & 0x07) << 8) | packet[3];
4678c2ecf20Sopenharmony_ci		if (cytp->mode & CYTP_BIT_ABS_PRESSURE)
4688c2ecf20Sopenharmony_ci			report_data->contacts[0].z = packet[4];
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci		report_data->contacts[1].x =
4718c2ecf20Sopenharmony_ci			((packet[5] & 0xf0) << 4) | packet[6];
4728c2ecf20Sopenharmony_ci		report_data->contacts[1].y =
4738c2ecf20Sopenharmony_ci			((packet[5] & 0x0f) << 8) | packet[7];
4748c2ecf20Sopenharmony_ci		if (cytp->mode & CYTP_BIT_ABS_PRESSURE)
4758c2ecf20Sopenharmony_ci			report_data->contacts[1].z = report_data->contacts[0].z;
4768c2ecf20Sopenharmony_ci	}
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	report_data->left = (header_byte & BTN_LEFT_BIT) ? 1 : 0;
4798c2ecf20Sopenharmony_ci	report_data->right = (header_byte & BTN_RIGHT_BIT) ? 1 : 0;
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	/*
4828c2ecf20Sopenharmony_ci	 * This is only true if one of the mouse buttons were tapped.  Make
4838c2ecf20Sopenharmony_ci	 * sure it doesn't turn into a click. The regular tap-to-click
4848c2ecf20Sopenharmony_ci	 * functionality will handle that on its own. If we don't do this,
4858c2ecf20Sopenharmony_ci	 * disabling tap-to-click won't affect the mouse button zones.
4868c2ecf20Sopenharmony_ci	 */
4878c2ecf20Sopenharmony_ci	if (report_data->tap)
4888c2ecf20Sopenharmony_ci		report_data->left = 0;
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci#ifdef CYTP_DEBUG_VERBOSE
4918c2ecf20Sopenharmony_ci	{
4928c2ecf20Sopenharmony_ci		int i;
4938c2ecf20Sopenharmony_ci		int n = report_data->contact_cnt;
4948c2ecf20Sopenharmony_ci		psmouse_dbg(psmouse, "Dump parsed report data as below:\n");
4958c2ecf20Sopenharmony_ci		psmouse_dbg(psmouse, "contact_cnt = %d\n",
4968c2ecf20Sopenharmony_ci			report_data->contact_cnt);
4978c2ecf20Sopenharmony_ci		if (n > CYTP_MAX_MT_SLOTS)
4988c2ecf20Sopenharmony_ci		    n = CYTP_MAX_MT_SLOTS;
4998c2ecf20Sopenharmony_ci		for (i = 0; i < n; i++)
5008c2ecf20Sopenharmony_ci			psmouse_dbg(psmouse, "contacts[%d] = {%d, %d, %d}\n", i,
5018c2ecf20Sopenharmony_ci					report_data->contacts[i].x,
5028c2ecf20Sopenharmony_ci					report_data->contacts[i].y,
5038c2ecf20Sopenharmony_ci					report_data->contacts[i].z);
5048c2ecf20Sopenharmony_ci		psmouse_dbg(psmouse, "left = %d\n", report_data->left);
5058c2ecf20Sopenharmony_ci		psmouse_dbg(psmouse, "right = %d\n", report_data->right);
5068c2ecf20Sopenharmony_ci		psmouse_dbg(psmouse, "middle = %d\n", report_data->middle);
5078c2ecf20Sopenharmony_ci	}
5088c2ecf20Sopenharmony_ci#endif
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	return 0;
5118c2ecf20Sopenharmony_ci}
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_cistatic void cypress_process_packet(struct psmouse *psmouse, bool zero_pkt)
5148c2ecf20Sopenharmony_ci{
5158c2ecf20Sopenharmony_ci	int i;
5168c2ecf20Sopenharmony_ci	struct input_dev *input = psmouse->dev;
5178c2ecf20Sopenharmony_ci	struct cytp_data *cytp = psmouse->private;
5188c2ecf20Sopenharmony_ci	struct cytp_report_data report_data;
5198c2ecf20Sopenharmony_ci	struct cytp_contact *contact;
5208c2ecf20Sopenharmony_ci	struct input_mt_pos pos[CYTP_MAX_MT_SLOTS];
5218c2ecf20Sopenharmony_ci	int slots[CYTP_MAX_MT_SLOTS];
5228c2ecf20Sopenharmony_ci	int n;
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	cypress_parse_packet(psmouse, cytp, &report_data);
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	n = report_data.contact_cnt;
5278c2ecf20Sopenharmony_ci	if (n > CYTP_MAX_MT_SLOTS)
5288c2ecf20Sopenharmony_ci		n = CYTP_MAX_MT_SLOTS;
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	for (i = 0; i < n; i++) {
5318c2ecf20Sopenharmony_ci		contact = &report_data.contacts[i];
5328c2ecf20Sopenharmony_ci		pos[i].x = contact->x;
5338c2ecf20Sopenharmony_ci		pos[i].y = contact->y;
5348c2ecf20Sopenharmony_ci	}
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	input_mt_assign_slots(input, slots, pos, n, 0);
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	for (i = 0; i < n; i++) {
5398c2ecf20Sopenharmony_ci		contact = &report_data.contacts[i];
5408c2ecf20Sopenharmony_ci		input_mt_slot(input, slots[i]);
5418c2ecf20Sopenharmony_ci		input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
5428c2ecf20Sopenharmony_ci		input_report_abs(input, ABS_MT_POSITION_X, contact->x);
5438c2ecf20Sopenharmony_ci		input_report_abs(input, ABS_MT_POSITION_Y, contact->y);
5448c2ecf20Sopenharmony_ci		input_report_abs(input, ABS_MT_PRESSURE, contact->z);
5458c2ecf20Sopenharmony_ci	}
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	input_mt_sync_frame(input);
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	input_mt_report_finger_count(input, report_data.contact_cnt);
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	input_report_key(input, BTN_LEFT, report_data.left);
5528c2ecf20Sopenharmony_ci	input_report_key(input, BTN_RIGHT, report_data.right);
5538c2ecf20Sopenharmony_ci	input_report_key(input, BTN_MIDDLE, report_data.middle);
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	input_sync(input);
5568c2ecf20Sopenharmony_ci}
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_cistatic psmouse_ret_t cypress_validate_byte(struct psmouse *psmouse)
5598c2ecf20Sopenharmony_ci{
5608c2ecf20Sopenharmony_ci	int contact_cnt;
5618c2ecf20Sopenharmony_ci	int index = psmouse->pktcnt - 1;
5628c2ecf20Sopenharmony_ci	unsigned char *packet = psmouse->packet;
5638c2ecf20Sopenharmony_ci	struct cytp_data *cytp = psmouse->private;
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	if (index < 0 || index > cytp->pkt_size)
5668c2ecf20Sopenharmony_ci		return PSMOUSE_BAD_DATA;
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	if (index == 0 && (packet[0] & 0xfc) == 0) {
5698c2ecf20Sopenharmony_ci		/* call packet process for reporting finger leave. */
5708c2ecf20Sopenharmony_ci		cypress_process_packet(psmouse, 1);
5718c2ecf20Sopenharmony_ci		return PSMOUSE_FULL_PACKET;
5728c2ecf20Sopenharmony_ci	}
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	/*
5758c2ecf20Sopenharmony_ci	 * Perform validation (and adjust packet size) based only on the
5768c2ecf20Sopenharmony_ci	 * first byte; allow all further bytes through.
5778c2ecf20Sopenharmony_ci	 */
5788c2ecf20Sopenharmony_ci	if (index != 0)
5798c2ecf20Sopenharmony_ci		return PSMOUSE_GOOD_DATA;
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci	/*
5828c2ecf20Sopenharmony_ci	 * If absolute/relative mode bit has not been set yet, just pass
5838c2ecf20Sopenharmony_ci	 * the byte through.
5848c2ecf20Sopenharmony_ci	 */
5858c2ecf20Sopenharmony_ci	if ((cytp->mode & CYTP_BIT_ABS_REL_MASK) == 0)
5868c2ecf20Sopenharmony_ci		return PSMOUSE_GOOD_DATA;
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	if ((packet[0] & 0x08) == 0x08)
5898c2ecf20Sopenharmony_ci		return PSMOUSE_BAD_DATA;
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	contact_cnt = cypress_get_finger_count(packet[0]);
5928c2ecf20Sopenharmony_ci	if (cytp->mode & CYTP_BIT_ABS_NO_PRESSURE)
5938c2ecf20Sopenharmony_ci		cypress_set_packet_size(psmouse, contact_cnt == 2 ? 7 : 4);
5948c2ecf20Sopenharmony_ci	else
5958c2ecf20Sopenharmony_ci		cypress_set_packet_size(psmouse, contact_cnt == 2 ? 8 : 5);
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	return PSMOUSE_GOOD_DATA;
5988c2ecf20Sopenharmony_ci}
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_cistatic psmouse_ret_t cypress_protocol_handler(struct psmouse *psmouse)
6018c2ecf20Sopenharmony_ci{
6028c2ecf20Sopenharmony_ci	struct cytp_data *cytp = psmouse->private;
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci	if (psmouse->pktcnt >= cytp->pkt_size) {
6058c2ecf20Sopenharmony_ci		cypress_process_packet(psmouse, 0);
6068c2ecf20Sopenharmony_ci		return PSMOUSE_FULL_PACKET;
6078c2ecf20Sopenharmony_ci	}
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci	return cypress_validate_byte(psmouse);
6108c2ecf20Sopenharmony_ci}
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_cistatic void cypress_set_rate(struct psmouse *psmouse, unsigned int rate)
6138c2ecf20Sopenharmony_ci{
6148c2ecf20Sopenharmony_ci	struct cytp_data *cytp = psmouse->private;
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci	if (rate >= 80) {
6178c2ecf20Sopenharmony_ci		psmouse->rate = 80;
6188c2ecf20Sopenharmony_ci		cytp->mode |= CYTP_BIT_HIGH_RATE;
6198c2ecf20Sopenharmony_ci	} else {
6208c2ecf20Sopenharmony_ci		psmouse->rate = 40;
6218c2ecf20Sopenharmony_ci		cytp->mode &= ~CYTP_BIT_HIGH_RATE;
6228c2ecf20Sopenharmony_ci	}
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	ps2_command(&psmouse->ps2dev, (unsigned char *)&psmouse->rate,
6258c2ecf20Sopenharmony_ci		    PSMOUSE_CMD_SETRATE);
6268c2ecf20Sopenharmony_ci}
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_cistatic void cypress_disconnect(struct psmouse *psmouse)
6298c2ecf20Sopenharmony_ci{
6308c2ecf20Sopenharmony_ci	cypress_reset(psmouse);
6318c2ecf20Sopenharmony_ci	kfree(psmouse->private);
6328c2ecf20Sopenharmony_ci	psmouse->private = NULL;
6338c2ecf20Sopenharmony_ci}
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_cistatic int cypress_reconnect(struct psmouse *psmouse)
6368c2ecf20Sopenharmony_ci{
6378c2ecf20Sopenharmony_ci	int tries = CYTP_PS2_CMD_TRIES;
6388c2ecf20Sopenharmony_ci	int rc;
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci	do {
6418c2ecf20Sopenharmony_ci		cypress_reset(psmouse);
6428c2ecf20Sopenharmony_ci		rc = cypress_detect(psmouse, false);
6438c2ecf20Sopenharmony_ci	} while (rc && (--tries > 0));
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci	if (rc) {
6468c2ecf20Sopenharmony_ci		psmouse_err(psmouse, "Reconnect: unable to detect trackpad.\n");
6478c2ecf20Sopenharmony_ci		return -1;
6488c2ecf20Sopenharmony_ci	}
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci	if (cypress_set_absolute_mode(psmouse)) {
6518c2ecf20Sopenharmony_ci		psmouse_err(psmouse, "Reconnect: Unable to initialize Cypress absolute mode.\n");
6528c2ecf20Sopenharmony_ci		return -1;
6538c2ecf20Sopenharmony_ci	}
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci	return 0;
6568c2ecf20Sopenharmony_ci}
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ciint cypress_init(struct psmouse *psmouse)
6598c2ecf20Sopenharmony_ci{
6608c2ecf20Sopenharmony_ci	struct cytp_data *cytp;
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	cytp = kzalloc(sizeof(struct cytp_data), GFP_KERNEL);
6638c2ecf20Sopenharmony_ci	if (!cytp)
6648c2ecf20Sopenharmony_ci		return -ENOMEM;
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	psmouse->private = cytp;
6678c2ecf20Sopenharmony_ci	psmouse->pktsize = 8;
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	cypress_reset(psmouse);
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci	if (cypress_query_hardware(psmouse)) {
6728c2ecf20Sopenharmony_ci		psmouse_err(psmouse, "Unable to query Trackpad hardware.\n");
6738c2ecf20Sopenharmony_ci		goto err_exit;
6748c2ecf20Sopenharmony_ci	}
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci	if (cypress_set_absolute_mode(psmouse)) {
6778c2ecf20Sopenharmony_ci		psmouse_err(psmouse, "init: Unable to initialize Cypress absolute mode.\n");
6788c2ecf20Sopenharmony_ci		goto err_exit;
6798c2ecf20Sopenharmony_ci	}
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	if (cypress_set_input_params(psmouse->dev, cytp) < 0) {
6828c2ecf20Sopenharmony_ci		psmouse_err(psmouse, "init: Unable to set input params.\n");
6838c2ecf20Sopenharmony_ci		goto err_exit;
6848c2ecf20Sopenharmony_ci	}
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci	psmouse->model = 1;
6878c2ecf20Sopenharmony_ci	psmouse->protocol_handler = cypress_protocol_handler;
6888c2ecf20Sopenharmony_ci	psmouse->set_rate = cypress_set_rate;
6898c2ecf20Sopenharmony_ci	psmouse->disconnect = cypress_disconnect;
6908c2ecf20Sopenharmony_ci	psmouse->reconnect = cypress_reconnect;
6918c2ecf20Sopenharmony_ci	psmouse->cleanup = cypress_reset;
6928c2ecf20Sopenharmony_ci	psmouse->resync_time = 0;
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_ci	return 0;
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_cierr_exit:
6978c2ecf20Sopenharmony_ci	/*
6988c2ecf20Sopenharmony_ci	 * Reset Cypress Trackpad as a standard mouse. Then
6998c2ecf20Sopenharmony_ci	 * let psmouse driver commmunicating with it as default PS2 mouse.
7008c2ecf20Sopenharmony_ci	 */
7018c2ecf20Sopenharmony_ci	cypress_reset(psmouse);
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci	psmouse->private = NULL;
7048c2ecf20Sopenharmony_ci	kfree(cytp);
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci	return -1;
7078c2ecf20Sopenharmony_ci}
708