xref: /openbmc/linux/drivers/input/mouse/byd.c (revision ce23cbc8)
198ee3771SChris Diamand /*
298ee3771SChris Diamand  * BYD TouchPad PS/2 mouse driver
398ee3771SChris Diamand  *
498ee3771SChris Diamand  * Copyright (C) 2015 Chris Diamand <chris@diamand.org>
582aaa086SChris Diamand  * Copyright (C) 2015 Richard Pospesel
682aaa086SChris Diamand  * Copyright (C) 2015 Tai Chi Minh Ralph Eastwood
782aaa086SChris Diamand  * Copyright (C) 2015 Martin Wimpress
882aaa086SChris Diamand  * Copyright (C) 2015 Jay Kuri
998ee3771SChris Diamand  *
1098ee3771SChris Diamand  * This program is free software; you can redistribute it and/or modify it
1198ee3771SChris Diamand  * under the terms of the GNU General Public License version 2 as published by
1298ee3771SChris Diamand  * the Free Software Foundation.
1398ee3771SChris Diamand  */
1498ee3771SChris Diamand 
1598ee3771SChris Diamand #include <linux/delay.h>
1698ee3771SChris Diamand #include <linux/input.h>
1798ee3771SChris Diamand #include <linux/libps2.h>
1898ee3771SChris Diamand #include <linux/serio.h>
192d5f5611SRichard Pospesel #include <linux/slab.h>
2098ee3771SChris Diamand 
2198ee3771SChris Diamand #include "psmouse.h"
2298ee3771SChris Diamand #include "byd.h"
2398ee3771SChris Diamand 
242d5f5611SRichard Pospesel /* PS2 Bits */
2598ee3771SChris Diamand #define PS2_Y_OVERFLOW	BIT_MASK(7)
2698ee3771SChris Diamand #define PS2_X_OVERFLOW	BIT_MASK(6)
2798ee3771SChris Diamand #define PS2_Y_SIGN	BIT_MASK(5)
2898ee3771SChris Diamand #define PS2_X_SIGN	BIT_MASK(4)
2998ee3771SChris Diamand #define PS2_ALWAYS_1	BIT_MASK(3)
3098ee3771SChris Diamand #define PS2_MIDDLE	BIT_MASK(2)
3198ee3771SChris Diamand #define PS2_RIGHT	BIT_MASK(1)
3298ee3771SChris Diamand #define PS2_LEFT	BIT_MASK(0)
3398ee3771SChris Diamand 
3498ee3771SChris Diamand /*
352d5f5611SRichard Pospesel  * BYD pad constants
3698ee3771SChris Diamand  */
3798ee3771SChris Diamand 
382d5f5611SRichard Pospesel /*
392d5f5611SRichard Pospesel  * True device resolution is unknown, however experiments show the
402d5f5611SRichard Pospesel  * resolution is about 111 units/mm.
412d5f5611SRichard Pospesel  * Absolute coordinate packets are in the range 0-255 for both X and Y
422d5f5611SRichard Pospesel  * we pick ABS_X/ABS_Y dimensions which are multiples of 256 and in
432d5f5611SRichard Pospesel  * the right ballpark given the touchpad's physical dimensions and estimate
442d5f5611SRichard Pospesel  * resolution per spec sheet, device active area dimensions are
452d5f5611SRichard Pospesel  * 101.6 x 60.1 mm.
462d5f5611SRichard Pospesel  */
472d5f5611SRichard Pospesel #define BYD_PAD_WIDTH		11264
482d5f5611SRichard Pospesel #define BYD_PAD_HEIGHT		6656
492d5f5611SRichard Pospesel #define BYD_PAD_RESOLUTION	111
5098ee3771SChris Diamand 
512d5f5611SRichard Pospesel /*
522d5f5611SRichard Pospesel  * Given the above dimensions, relative packets velocity is in multiples of
532d5f5611SRichard Pospesel  * 1 unit / 11 milliseconds.  We use this dt to estimate distance traveled
542d5f5611SRichard Pospesel  */
552d5f5611SRichard Pospesel #define BYD_DT			11
562d5f5611SRichard Pospesel /* Time in jiffies used to timeout various touch events (64 ms) */
572d5f5611SRichard Pospesel #define BYD_TOUCH_TIMEOUT	msecs_to_jiffies(64)
582d5f5611SRichard Pospesel 
592d5f5611SRichard Pospesel /* BYD commands reverse engineered from windows driver */
602d5f5611SRichard Pospesel 
612d5f5611SRichard Pospesel /*
622d5f5611SRichard Pospesel  * Swipe gesture from off-pad to on-pad
632d5f5611SRichard Pospesel  *  0 : disable
642d5f5611SRichard Pospesel  *  1 : enable
652d5f5611SRichard Pospesel  */
662d5f5611SRichard Pospesel #define BYD_CMD_SET_OFFSCREEN_SWIPE		0x10cc
672d5f5611SRichard Pospesel /*
682d5f5611SRichard Pospesel  * Tap and drag delay time
692d5f5611SRichard Pospesel  *  0 : disable
702d5f5611SRichard Pospesel  *  1 - 8 : least to most delay
712d5f5611SRichard Pospesel  */
722d5f5611SRichard Pospesel #define BYD_CMD_SET_TAP_DRAG_DELAY_TIME		0x10cf
732d5f5611SRichard Pospesel /*
742d5f5611SRichard Pospesel  * Physical buttons function mapping
752d5f5611SRichard Pospesel  *  0 : enable
762d5f5611SRichard Pospesel  *  4 : normal
772d5f5611SRichard Pospesel  *  5 : left button custom command
782d5f5611SRichard Pospesel  *  6 : right button custom command
792d5f5611SRichard Pospesel  *  8 : disable
802d5f5611SRichard Pospesel  */
812d5f5611SRichard Pospesel #define BYD_CMD_SET_PHYSICAL_BUTTONS		0x10d0
822d5f5611SRichard Pospesel /*
832d5f5611SRichard Pospesel  * Absolute mode (1 byte X/Y resolution)
842d5f5611SRichard Pospesel  *  0 : disable
852d5f5611SRichard Pospesel  *  2 : enable
862d5f5611SRichard Pospesel  */
872d5f5611SRichard Pospesel #define BYD_CMD_SET_ABSOLUTE_MODE		0x10d1
882d5f5611SRichard Pospesel /*
892d5f5611SRichard Pospesel  * Two finger scrolling
902d5f5611SRichard Pospesel  *  1 : vertical
912d5f5611SRichard Pospesel  *  2 : horizontal
922d5f5611SRichard Pospesel  *  3 : vertical + horizontal
932d5f5611SRichard Pospesel  *  4 : disable
942d5f5611SRichard Pospesel  */
952d5f5611SRichard Pospesel #define BYD_CMD_SET_TWO_FINGER_SCROLL		0x10d2
962d5f5611SRichard Pospesel /*
972d5f5611SRichard Pospesel  * Handedness
982d5f5611SRichard Pospesel  *  1 : right handed
992d5f5611SRichard Pospesel  *  2 : left handed
1002d5f5611SRichard Pospesel  */
1012d5f5611SRichard Pospesel #define BYD_CMD_SET_HANDEDNESS			0x10d3
1022d5f5611SRichard Pospesel /*
1032d5f5611SRichard Pospesel  * Tap to click
1042d5f5611SRichard Pospesel  *  1 : enable
1052d5f5611SRichard Pospesel  *  2 : disable
1062d5f5611SRichard Pospesel  */
1072d5f5611SRichard Pospesel #define BYD_CMD_SET_TAP				0x10d4
1082d5f5611SRichard Pospesel /*
1092d5f5611SRichard Pospesel  * Tap and drag
1102d5f5611SRichard Pospesel  *  1 : tap and hold to drag
1112d5f5611SRichard Pospesel  *  2 : tap and hold to drag + lock
1122d5f5611SRichard Pospesel  *  3 : disable
1132d5f5611SRichard Pospesel  */
1142d5f5611SRichard Pospesel #define BYD_CMD_SET_TAP_DRAG			0x10d5
1152d5f5611SRichard Pospesel /*
1162d5f5611SRichard Pospesel  * Touch sensitivity
1172d5f5611SRichard Pospesel  *  1 - 7 : least to most sensitive
1182d5f5611SRichard Pospesel  */
1192d5f5611SRichard Pospesel #define BYD_CMD_SET_TOUCH_SENSITIVITY		0x10d6
1202d5f5611SRichard Pospesel /*
1212d5f5611SRichard Pospesel  * One finger scrolling
1222d5f5611SRichard Pospesel  *  1 : vertical
1232d5f5611SRichard Pospesel  *  2 : horizontal
1242d5f5611SRichard Pospesel  *  3 : vertical + horizontal
1252d5f5611SRichard Pospesel  *  4 : disable
1262d5f5611SRichard Pospesel  */
1272d5f5611SRichard Pospesel #define BYD_CMD_SET_ONE_FINGER_SCROLL		0x10d7
1282d5f5611SRichard Pospesel /*
1292d5f5611SRichard Pospesel  * One finger scrolling function
1302d5f5611SRichard Pospesel  *  1 : free scrolling
1312d5f5611SRichard Pospesel  *  2 : edge motion
1322d5f5611SRichard Pospesel  *  3 : free scrolling + edge motion
1332d5f5611SRichard Pospesel  *  4 : disable
1342d5f5611SRichard Pospesel  */
1352d5f5611SRichard Pospesel #define BYD_CMD_SET_ONE_FINGER_SCROLL_FUNC	0x10d8
1362d5f5611SRichard Pospesel /*
1372d5f5611SRichard Pospesel  * Sliding speed
1382d5f5611SRichard Pospesel  *  1 - 5 : slowest to fastest
1392d5f5611SRichard Pospesel  */
1402d5f5611SRichard Pospesel #define BYD_CMD_SET_SLIDING_SPEED		0x10da
1412d5f5611SRichard Pospesel /*
1422d5f5611SRichard Pospesel  * Edge motion
1432d5f5611SRichard Pospesel  *  1 : disable
1442d5f5611SRichard Pospesel  *  2 : enable when dragging
1452d5f5611SRichard Pospesel  *  3 : enable when dragging and pointing
1462d5f5611SRichard Pospesel  */
1472d5f5611SRichard Pospesel #define BYD_CMD_SET_EDGE_MOTION			0x10db
1482d5f5611SRichard Pospesel /*
1492d5f5611SRichard Pospesel  * Left edge region size
1502d5f5611SRichard Pospesel  *  0 - 7 : smallest to largest width
1512d5f5611SRichard Pospesel  */
1522d5f5611SRichard Pospesel #define BYD_CMD_SET_LEFT_EDGE_REGION		0x10dc
1532d5f5611SRichard Pospesel /*
1542d5f5611SRichard Pospesel  * Top edge region size
1552d5f5611SRichard Pospesel  *  0 - 9 : smallest to largest height
1562d5f5611SRichard Pospesel  */
1572d5f5611SRichard Pospesel #define BYD_CMD_SET_TOP_EDGE_REGION		0x10dd
1582d5f5611SRichard Pospesel /*
1592d5f5611SRichard Pospesel  * Disregard palm press as clicks
1602d5f5611SRichard Pospesel  *  1 - 6 : smallest to largest
1612d5f5611SRichard Pospesel  */
1622d5f5611SRichard Pospesel #define BYD_CMD_SET_PALM_CHECK			0x10de
1632d5f5611SRichard Pospesel /*
1642d5f5611SRichard Pospesel  * Right edge region size
1652d5f5611SRichard Pospesel  *  0 - 7 : smallest to largest width
1662d5f5611SRichard Pospesel  */
1672d5f5611SRichard Pospesel #define BYD_CMD_SET_RIGHT_EDGE_REGION		0x10df
1682d5f5611SRichard Pospesel /*
1692d5f5611SRichard Pospesel  * Bottom edge region size
1702d5f5611SRichard Pospesel  *  0 - 9 : smallest to largest height
1712d5f5611SRichard Pospesel  */
1722d5f5611SRichard Pospesel #define BYD_CMD_SET_BOTTOM_EDGE_REGION		0x10e1
1732d5f5611SRichard Pospesel /*
1742d5f5611SRichard Pospesel  * Multitouch gestures
1752d5f5611SRichard Pospesel  *  1 : enable
1762d5f5611SRichard Pospesel  *  2 : disable
1772d5f5611SRichard Pospesel  */
1782d5f5611SRichard Pospesel #define BYD_CMD_SET_MULTITOUCH			0x10e3
1792d5f5611SRichard Pospesel /*
1802d5f5611SRichard Pospesel  * Edge motion speed
1812d5f5611SRichard Pospesel  *  0 : control with finger pressure
1822d5f5611SRichard Pospesel  *  1 - 9 : slowest to fastest
1832d5f5611SRichard Pospesel  */
1842d5f5611SRichard Pospesel #define BYD_CMD_SET_EDGE_MOTION_SPEED		0x10e4
1852d5f5611SRichard Pospesel /*
1862d5f5611SRichard Pospesel  * Two finger scolling function
1872d5f5611SRichard Pospesel  *  0 : free scrolling
1882d5f5611SRichard Pospesel  *  1 : free scrolling (with momentum)
1892d5f5611SRichard Pospesel  *  2 : edge motion
1902d5f5611SRichard Pospesel  *  3 : free scrolling (with momentum) + edge motion
1912d5f5611SRichard Pospesel  *  4 : disable
1922d5f5611SRichard Pospesel  */
1932d5f5611SRichard Pospesel #define BYD_CMD_SET_TWO_FINGER_SCROLL_FUNC	0x10e5
1942d5f5611SRichard Pospesel 
1952d5f5611SRichard Pospesel /*
1962d5f5611SRichard Pospesel  * The touchpad generates a mixture of absolute and relative packets, indicated
1972d5f5611SRichard Pospesel  * by the the last byte of each packet being set to one of the following:
1982d5f5611SRichard Pospesel  */
1992d5f5611SRichard Pospesel #define BYD_PACKET_ABSOLUTE			0xf8
2002d5f5611SRichard Pospesel #define BYD_PACKET_RELATIVE			0x00
2012d5f5611SRichard Pospesel /* Multitouch gesture packets */
2022d5f5611SRichard Pospesel #define BYD_PACKET_PINCH_IN			0xd8
2032d5f5611SRichard Pospesel #define BYD_PACKET_PINCH_OUT			0x28
2042d5f5611SRichard Pospesel #define BYD_PACKET_ROTATE_CLOCKWISE		0x29
2052d5f5611SRichard Pospesel #define BYD_PACKET_ROTATE_ANTICLOCKWISE		0xd7
2062d5f5611SRichard Pospesel #define BYD_PACKET_TWO_FINGER_SCROLL_RIGHT	0x2a
2072d5f5611SRichard Pospesel #define BYD_PACKET_TWO_FINGER_SCROLL_DOWN	0x2b
2082d5f5611SRichard Pospesel #define BYD_PACKET_TWO_FINGER_SCROLL_UP		0xd5
2092d5f5611SRichard Pospesel #define BYD_PACKET_TWO_FINGER_SCROLL_LEFT	0xd6
2102d5f5611SRichard Pospesel #define BYD_PACKET_THREE_FINGER_SWIPE_RIGHT	0x2c
2112d5f5611SRichard Pospesel #define BYD_PACKET_THREE_FINGER_SWIPE_DOWN	0x2d
2122d5f5611SRichard Pospesel #define BYD_PACKET_THREE_FINGER_SWIPE_UP	0xd3
2132d5f5611SRichard Pospesel #define BYD_PACKET_THREE_FINGER_SWIPE_LEFT	0xd4
2142d5f5611SRichard Pospesel #define BYD_PACKET_FOUR_FINGER_DOWN		0x33
2152d5f5611SRichard Pospesel #define BYD_PACKET_FOUR_FINGER_UP		0xcd
2162d5f5611SRichard Pospesel #define BYD_PACKET_REGION_SCROLL_RIGHT		0x35
2172d5f5611SRichard Pospesel #define BYD_PACKET_REGION_SCROLL_DOWN		0x36
2182d5f5611SRichard Pospesel #define BYD_PACKET_REGION_SCROLL_UP		0xca
2192d5f5611SRichard Pospesel #define BYD_PACKET_REGION_SCROLL_LEFT		0xcb
2202d5f5611SRichard Pospesel #define BYD_PACKET_RIGHT_CORNER_CLICK		0xd2
2212d5f5611SRichard Pospesel #define BYD_PACKET_LEFT_CORNER_CLICK		0x2e
2222d5f5611SRichard Pospesel #define BYD_PACKET_LEFT_AND_RIGHT_CORNER_CLICK	0x2f
2232d5f5611SRichard Pospesel #define BYD_PACKET_ONTO_PAD_SWIPE_RIGHT		0x37
2242d5f5611SRichard Pospesel #define BYD_PACKET_ONTO_PAD_SWIPE_DOWN		0x30
2252d5f5611SRichard Pospesel #define BYD_PACKET_ONTO_PAD_SWIPE_UP		0xd0
2262d5f5611SRichard Pospesel #define BYD_PACKET_ONTO_PAD_SWIPE_LEFT		0xc9
2272d5f5611SRichard Pospesel 
2282d5f5611SRichard Pospesel struct byd_data {
2292d5f5611SRichard Pospesel 	struct timer_list timer;
230ce23cbc8SKees Cook 	struct psmouse *psmouse;
2312d5f5611SRichard Pospesel 	s32 abs_x;
2322d5f5611SRichard Pospesel 	s32 abs_y;
2332d5f5611SRichard Pospesel 	typeof(jiffies) last_touch_time;
2342d5f5611SRichard Pospesel 	bool btn_left;
2352d5f5611SRichard Pospesel 	bool btn_right;
2362d5f5611SRichard Pospesel 	bool touch;
2372d5f5611SRichard Pospesel };
2382d5f5611SRichard Pospesel 
2392d5f5611SRichard Pospesel static void byd_report_input(struct psmouse *psmouse)
24098ee3771SChris Diamand {
2412d5f5611SRichard Pospesel 	struct byd_data *priv = psmouse->private;
2422d5f5611SRichard Pospesel 	struct input_dev *dev = psmouse->dev;
24398ee3771SChris Diamand 
2442d5f5611SRichard Pospesel 	input_report_key(dev, BTN_TOUCH, priv->touch);
2452d5f5611SRichard Pospesel 	input_report_key(dev, BTN_TOOL_FINGER, priv->touch);
24698ee3771SChris Diamand 
2472d5f5611SRichard Pospesel 	input_report_abs(dev, ABS_X, priv->abs_x);
2482d5f5611SRichard Pospesel 	input_report_abs(dev, ABS_Y, priv->abs_y);
2492d5f5611SRichard Pospesel 	input_report_key(dev, BTN_LEFT, priv->btn_left);
2502d5f5611SRichard Pospesel 	input_report_key(dev, BTN_RIGHT, priv->btn_right);
25198ee3771SChris Diamand 
2522d5f5611SRichard Pospesel 	input_sync(dev);
25398ee3771SChris Diamand }
25498ee3771SChris Diamand 
255ce23cbc8SKees Cook static void byd_clear_touch(struct timer_list *t)
2562d5f5611SRichard Pospesel {
257ce23cbc8SKees Cook 	struct byd_data *priv = from_timer(priv, t, timer);
258ce23cbc8SKees Cook 	struct psmouse *psmouse = priv->psmouse;
2592d5f5611SRichard Pospesel 
2602d5f5611SRichard Pospesel 	serio_pause_rx(psmouse->ps2dev.serio);
2612d5f5611SRichard Pospesel 	priv->touch = false;
2622d5f5611SRichard Pospesel 
2632d5f5611SRichard Pospesel 	byd_report_input(psmouse);
2642d5f5611SRichard Pospesel 
2652d5f5611SRichard Pospesel 	serio_continue_rx(psmouse->ps2dev.serio);
2662d5f5611SRichard Pospesel 
2672d5f5611SRichard Pospesel 	/*
2682d5f5611SRichard Pospesel 	 * Move cursor back to center of pad when we lose touch - this
2692d5f5611SRichard Pospesel 	 * specifically improves user experience when moving cursor with one
2702d5f5611SRichard Pospesel 	 * finger, and pressing a button with another.
2712d5f5611SRichard Pospesel 	 */
2722d5f5611SRichard Pospesel 	priv->abs_x = BYD_PAD_WIDTH / 2;
2732d5f5611SRichard Pospesel 	priv->abs_y = BYD_PAD_HEIGHT / 2;
27498ee3771SChris Diamand }
27598ee3771SChris Diamand 
27698ee3771SChris Diamand static psmouse_ret_t byd_process_byte(struct psmouse *psmouse)
27798ee3771SChris Diamand {
2782d5f5611SRichard Pospesel 	struct byd_data *priv = psmouse->private;
27998ee3771SChris Diamand 	u8 *pkt = psmouse->packet;
28098ee3771SChris Diamand 
28198ee3771SChris Diamand 	if (psmouse->pktcnt > 0 && !(pkt[0] & PS2_ALWAYS_1)) {
28298ee3771SChris Diamand 		psmouse_warn(psmouse, "Always_1 bit not 1. pkt[0] = %02x\n",
28398ee3771SChris Diamand 			     pkt[0]);
28498ee3771SChris Diamand 		return PSMOUSE_BAD_DATA;
28598ee3771SChris Diamand 	}
28698ee3771SChris Diamand 
28798ee3771SChris Diamand 	if (psmouse->pktcnt < psmouse->pktsize)
28898ee3771SChris Diamand 		return PSMOUSE_GOOD_DATA;
28998ee3771SChris Diamand 
29098ee3771SChris Diamand 	/* Otherwise, a full packet has been received */
29198ee3771SChris Diamand 	switch (pkt[3]) {
2922d5f5611SRichard Pospesel 	case BYD_PACKET_ABSOLUTE:
2932d5f5611SRichard Pospesel 		/* Only use absolute packets for the start of movement. */
2942d5f5611SRichard Pospesel 		if (!priv->touch) {
2952d5f5611SRichard Pospesel 			/* needed to detect tap */
2962d5f5611SRichard Pospesel 			typeof(jiffies) tap_time =
2972d5f5611SRichard Pospesel 				priv->last_touch_time + BYD_TOUCH_TIMEOUT;
2982d5f5611SRichard Pospesel 			priv->touch = time_after(jiffies, tap_time);
2992d5f5611SRichard Pospesel 
3002d5f5611SRichard Pospesel 			/* init abs position */
3012d5f5611SRichard Pospesel 			priv->abs_x = pkt[1] * (BYD_PAD_WIDTH / 256);
3022d5f5611SRichard Pospesel 			priv->abs_y = (255 - pkt[2]) * (BYD_PAD_HEIGHT / 256);
3032d5f5611SRichard Pospesel 		}
3042d5f5611SRichard Pospesel 		break;
3052d5f5611SRichard Pospesel 	case BYD_PACKET_RELATIVE: {
30698ee3771SChris Diamand 		/* Standard packet */
30798ee3771SChris Diamand 		/* Sign-extend if a sign bit is set. */
3082d5f5611SRichard Pospesel 		u32 signx = pkt[0] & PS2_X_SIGN ? ~0xFF : 0;
3092d5f5611SRichard Pospesel 		u32 signy = pkt[0] & PS2_Y_SIGN ? ~0xFF : 0;
3102d5f5611SRichard Pospesel 		s32 dx = signx | (int) pkt[1];
3112d5f5611SRichard Pospesel 		s32 dy = signy | (int) pkt[2];
31298ee3771SChris Diamand 
3132d5f5611SRichard Pospesel 		/* Update position based on velocity */
3142d5f5611SRichard Pospesel 		priv->abs_x += dx * BYD_DT;
3152d5f5611SRichard Pospesel 		priv->abs_y -= dy * BYD_DT;
31698ee3771SChris Diamand 
3172d5f5611SRichard Pospesel 		priv->touch = true;
31898ee3771SChris Diamand 		break;
31998ee3771SChris Diamand 	}
32098ee3771SChris Diamand 	default:
32198ee3771SChris Diamand 		psmouse_warn(psmouse,
32298ee3771SChris Diamand 			     "Unrecognized Z: pkt = %02x %02x %02x %02x\n",
32398ee3771SChris Diamand 			     psmouse->packet[0], psmouse->packet[1],
32498ee3771SChris Diamand 			     psmouse->packet[2], psmouse->packet[3]);
32598ee3771SChris Diamand 		return PSMOUSE_BAD_DATA;
32698ee3771SChris Diamand 	}
32798ee3771SChris Diamand 
3282d5f5611SRichard Pospesel 	priv->btn_left = pkt[0] & PS2_LEFT;
3292d5f5611SRichard Pospesel 	priv->btn_right = pkt[0] & PS2_RIGHT;
3302d5f5611SRichard Pospesel 
3312d5f5611SRichard Pospesel 	byd_report_input(psmouse);
3322d5f5611SRichard Pospesel 
3332d5f5611SRichard Pospesel 	/* Reset time since last touch. */
3342d5f5611SRichard Pospesel 	if (priv->touch) {
3352d5f5611SRichard Pospesel 		priv->last_touch_time = jiffies;
3362d5f5611SRichard Pospesel 		mod_timer(&priv->timer, jiffies + BYD_TOUCH_TIMEOUT);
3372d5f5611SRichard Pospesel 	}
33898ee3771SChris Diamand 
33998ee3771SChris Diamand 	return PSMOUSE_FULL_PACKET;
34098ee3771SChris Diamand }
34198ee3771SChris Diamand 
3422d5f5611SRichard Pospesel static int byd_reset_touchpad(struct psmouse *psmouse)
34398ee3771SChris Diamand {
34498ee3771SChris Diamand 	struct ps2dev *ps2dev = &psmouse->ps2dev;
34598ee3771SChris Diamand 	u8 param[4];
3462d5f5611SRichard Pospesel 	size_t i;
3472d5f5611SRichard Pospesel 
34808d6ac9eSColin Ian King 	static const struct {
34998ee3771SChris Diamand 		u16 command;
35098ee3771SChris Diamand 		u8 arg;
35198ee3771SChris Diamand 	} seq[] = {
3522d5f5611SRichard Pospesel 		/*
3532d5f5611SRichard Pospesel 		 * Intellimouse initialization sequence, to get 4-byte instead
3542d5f5611SRichard Pospesel 		 * of 3-byte packets.
3552d5f5611SRichard Pospesel 		 */
35698ee3771SChris Diamand 		{ PSMOUSE_CMD_SETRATE, 0xC8 },
35798ee3771SChris Diamand 		{ PSMOUSE_CMD_SETRATE, 0x64 },
35898ee3771SChris Diamand 		{ PSMOUSE_CMD_SETRATE, 0x50 },
35998ee3771SChris Diamand 		{ PSMOUSE_CMD_GETID, 0 },
3602d5f5611SRichard Pospesel 		{ PSMOUSE_CMD_ENABLE, 0 },
3612d5f5611SRichard Pospesel 		/*
3622d5f5611SRichard Pospesel 		 * BYD-specific initialization, which enables absolute mode and
3632d5f5611SRichard Pospesel 		 * (if desired), the touchpad's built-in gesture detection.
3642d5f5611SRichard Pospesel 		 */
3652d5f5611SRichard Pospesel 		{ 0x10E2, 0x00 },
3662d5f5611SRichard Pospesel 		{ 0x10E0, 0x02 },
3672d5f5611SRichard Pospesel 		/* The touchpad should reply with 4 seemingly-random bytes */
3682d5f5611SRichard Pospesel 		{ 0x14E0, 0x01 },
3692d5f5611SRichard Pospesel 		/* Pairs of parameters and values. */
3702d5f5611SRichard Pospesel 		{ BYD_CMD_SET_HANDEDNESS, 0x01 },
3712d5f5611SRichard Pospesel 		{ BYD_CMD_SET_PHYSICAL_BUTTONS, 0x04 },
3722d5f5611SRichard Pospesel 		{ BYD_CMD_SET_TAP, 0x02 },
3732d5f5611SRichard Pospesel 		{ BYD_CMD_SET_ONE_FINGER_SCROLL, 0x04 },
3742d5f5611SRichard Pospesel 		{ BYD_CMD_SET_ONE_FINGER_SCROLL_FUNC, 0x04 },
3752d5f5611SRichard Pospesel 		{ BYD_CMD_SET_EDGE_MOTION, 0x01 },
3762d5f5611SRichard Pospesel 		{ BYD_CMD_SET_PALM_CHECK, 0x00 },
3772d5f5611SRichard Pospesel 		{ BYD_CMD_SET_MULTITOUCH, 0x02 },
3782d5f5611SRichard Pospesel 		{ BYD_CMD_SET_TWO_FINGER_SCROLL, 0x04 },
3792d5f5611SRichard Pospesel 		{ BYD_CMD_SET_TWO_FINGER_SCROLL_FUNC, 0x04 },
3802d5f5611SRichard Pospesel 		{ BYD_CMD_SET_LEFT_EDGE_REGION, 0x00 },
3812d5f5611SRichard Pospesel 		{ BYD_CMD_SET_TOP_EDGE_REGION, 0x00 },
3822d5f5611SRichard Pospesel 		{ BYD_CMD_SET_RIGHT_EDGE_REGION, 0x00 },
3832d5f5611SRichard Pospesel 		{ BYD_CMD_SET_BOTTOM_EDGE_REGION, 0x00 },
3842d5f5611SRichard Pospesel 		{ BYD_CMD_SET_ABSOLUTE_MODE, 0x02 },
3852d5f5611SRichard Pospesel 		/* Finalize initialization. */
3862d5f5611SRichard Pospesel 		{ 0x10E0, 0x00 },
3872d5f5611SRichard Pospesel 		{ 0x10E2, 0x01 },
38898ee3771SChris Diamand 	};
38998ee3771SChris Diamand 
39098ee3771SChris Diamand 	for (i = 0; i < ARRAY_SIZE(seq); ++i) {
3912d5f5611SRichard Pospesel 		memset(param, 0, sizeof(param));
39298ee3771SChris Diamand 		param[0] = seq[i].arg;
39398ee3771SChris Diamand 		if (ps2_command(ps2dev, param, seq[i].command))
3942d5f5611SRichard Pospesel 			return -EIO;
39598ee3771SChris Diamand 	}
39698ee3771SChris Diamand 
3972d5f5611SRichard Pospesel 	psmouse_set_state(psmouse, PSMOUSE_ACTIVATED);
39898ee3771SChris Diamand 	return 0;
39998ee3771SChris Diamand }
40098ee3771SChris Diamand 
40198ee3771SChris Diamand static int byd_reconnect(struct psmouse *psmouse)
40298ee3771SChris Diamand {
40398ee3771SChris Diamand 	int retry = 0, error = 0;
40498ee3771SChris Diamand 
40598ee3771SChris Diamand 	psmouse_dbg(psmouse, "Reconnect\n");
40698ee3771SChris Diamand 	do {
40798ee3771SChris Diamand 		psmouse_reset(psmouse);
40898ee3771SChris Diamand 		if (retry)
40998ee3771SChris Diamand 			ssleep(1);
41098ee3771SChris Diamand 		error = byd_detect(psmouse, 0);
41198ee3771SChris Diamand 	} while (error && ++retry < 3);
41298ee3771SChris Diamand 
41398ee3771SChris Diamand 	if (error)
41498ee3771SChris Diamand 		return error;
41598ee3771SChris Diamand 
41698ee3771SChris Diamand 	psmouse_dbg(psmouse, "Reconnected after %d attempts\n", retry);
41798ee3771SChris Diamand 
41898ee3771SChris Diamand 	error = byd_reset_touchpad(psmouse);
41998ee3771SChris Diamand 	if (error) {
42098ee3771SChris Diamand 		psmouse_err(psmouse, "Unable to initialize device\n");
42198ee3771SChris Diamand 		return error;
42298ee3771SChris Diamand 	}
42398ee3771SChris Diamand 
42498ee3771SChris Diamand 	return 0;
42598ee3771SChris Diamand }
42698ee3771SChris Diamand 
4272d5f5611SRichard Pospesel static void byd_disconnect(struct psmouse *psmouse)
4282d5f5611SRichard Pospesel {
4292d5f5611SRichard Pospesel 	struct byd_data *priv = psmouse->private;
4302d5f5611SRichard Pospesel 
4312d5f5611SRichard Pospesel 	if (priv) {
4322d5f5611SRichard Pospesel 		del_timer(&priv->timer);
4332d5f5611SRichard Pospesel 		kfree(psmouse->private);
4342d5f5611SRichard Pospesel 		psmouse->private = NULL;
4352d5f5611SRichard Pospesel 	}
4362d5f5611SRichard Pospesel }
4372d5f5611SRichard Pospesel 
4382d5f5611SRichard Pospesel int byd_detect(struct psmouse *psmouse, bool set_properties)
4392d5f5611SRichard Pospesel {
4402d5f5611SRichard Pospesel 	struct ps2dev *ps2dev = &psmouse->ps2dev;
4412d5f5611SRichard Pospesel 	u8 param[4] = {0x03, 0x00, 0x00, 0x00};
4422d5f5611SRichard Pospesel 
4432d5f5611SRichard Pospesel 	if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
4442d5f5611SRichard Pospesel 		return -1;
4452d5f5611SRichard Pospesel 	if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
4462d5f5611SRichard Pospesel 		return -1;
4472d5f5611SRichard Pospesel 	if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
4482d5f5611SRichard Pospesel 		return -1;
4492d5f5611SRichard Pospesel 	if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
4502d5f5611SRichard Pospesel 		return -1;
4512d5f5611SRichard Pospesel 	if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
4522d5f5611SRichard Pospesel 		return -1;
4532d5f5611SRichard Pospesel 
4542d5f5611SRichard Pospesel 	if (param[1] != 0x03 || param[2] != 0x64)
4552d5f5611SRichard Pospesel 		return -ENODEV;
4562d5f5611SRichard Pospesel 
4572d5f5611SRichard Pospesel 	psmouse_dbg(psmouse, "BYD touchpad detected\n");
4582d5f5611SRichard Pospesel 
4592d5f5611SRichard Pospesel 	if (set_properties) {
4602d5f5611SRichard Pospesel 		psmouse->vendor = "BYD";
4612d5f5611SRichard Pospesel 		psmouse->name = "TouchPad";
4622d5f5611SRichard Pospesel 	}
4632d5f5611SRichard Pospesel 
4642d5f5611SRichard Pospesel 	return 0;
4652d5f5611SRichard Pospesel }
4662d5f5611SRichard Pospesel 
46798ee3771SChris Diamand int byd_init(struct psmouse *psmouse)
46898ee3771SChris Diamand {
46998ee3771SChris Diamand 	struct input_dev *dev = psmouse->dev;
4702d5f5611SRichard Pospesel 	struct byd_data *priv;
47198ee3771SChris Diamand 
47298ee3771SChris Diamand 	if (psmouse_reset(psmouse))
47398ee3771SChris Diamand 		return -EIO;
47498ee3771SChris Diamand 
47598ee3771SChris Diamand 	if (byd_reset_touchpad(psmouse))
47698ee3771SChris Diamand 		return -EIO;
47798ee3771SChris Diamand 
4782d5f5611SRichard Pospesel 	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
4792d5f5611SRichard Pospesel 	if (!priv)
4802d5f5611SRichard Pospesel 		return -ENOMEM;
4812d5f5611SRichard Pospesel 
482ce23cbc8SKees Cook 	priv->psmouse = psmouse;
483ce23cbc8SKees Cook 	timer_setup(&priv->timer, byd_clear_touch, 0);
4842d5f5611SRichard Pospesel 
4852d5f5611SRichard Pospesel 	psmouse->private = priv;
4862d5f5611SRichard Pospesel 	psmouse->disconnect = byd_disconnect;
48798ee3771SChris Diamand 	psmouse->reconnect = byd_reconnect;
48898ee3771SChris Diamand 	psmouse->protocol_handler = byd_process_byte;
48998ee3771SChris Diamand 	psmouse->pktsize = 4;
49098ee3771SChris Diamand 	psmouse->resync_time = 0;
49198ee3771SChris Diamand 
4922d5f5611SRichard Pospesel 	__set_bit(INPUT_PROP_POINTER, dev->propbit);
4932d5f5611SRichard Pospesel 	/* Touchpad */
4942d5f5611SRichard Pospesel 	__set_bit(BTN_TOUCH, dev->keybit);
4952d5f5611SRichard Pospesel 	__set_bit(BTN_TOOL_FINGER, dev->keybit);
4962d5f5611SRichard Pospesel 	/* Buttons */
4972d5f5611SRichard Pospesel 	__set_bit(BTN_LEFT, dev->keybit);
4982d5f5611SRichard Pospesel 	__set_bit(BTN_RIGHT, dev->keybit);
4992d5f5611SRichard Pospesel 	__clear_bit(BTN_MIDDLE, dev->keybit);
5002d5f5611SRichard Pospesel 
5012d5f5611SRichard Pospesel 	/* Absolute position */
5022d5f5611SRichard Pospesel 	__set_bit(EV_ABS, dev->evbit);
5032d5f5611SRichard Pospesel 	input_set_abs_params(dev, ABS_X, 0, BYD_PAD_WIDTH, 0, 0);
5042d5f5611SRichard Pospesel 	input_set_abs_params(dev, ABS_Y, 0, BYD_PAD_HEIGHT, 0, 0);
5052d5f5611SRichard Pospesel 	input_abs_set_res(dev, ABS_X, BYD_PAD_RESOLUTION);
5062d5f5611SRichard Pospesel 	input_abs_set_res(dev, ABS_Y, BYD_PAD_RESOLUTION);
5072d5f5611SRichard Pospesel 	/* No relative support */
5082d5f5611SRichard Pospesel 	__clear_bit(EV_REL, dev->evbit);
5092d5f5611SRichard Pospesel 	__clear_bit(REL_X, dev->relbit);
5102d5f5611SRichard Pospesel 	__clear_bit(REL_Y, dev->relbit);
51198ee3771SChris Diamand 
51298ee3771SChris Diamand 	return 0;
51398ee3771SChris Diamand }
514