Skip to content

Commit 3e44193

Browse files
AaronDotKexyBiscuit
authored andcommitted
FROMLIST: Input: Add driver for PixArt PS/2 touchpad
This patch introduces a driver for the PixArt PS/2 touchpad, which supports both clickpad and touchpad types. At the same time, we extended the single data packet length to 16, because according to the current PixArt hardware and FW design, we need 11 bytes/15 bytes to represent the complete three-finger/four-finger data. Co-developed-by: Jon Xie <[email protected]> Signed-off-by: Jon Xie <[email protected]> Co-developed-by: Jay Lee <[email protected]> Signed-off-by: Jay Lee <[email protected]> Signed-off-by: Binbin Zhou <[email protected]> Tested-by: Kexy Biscuit <[email protected]> Link: https://lore.kernel.org/all/[email protected]/ Signed-off-by: Kexy Biscuit <[email protected]>
1 parent 7ad6b4e commit 3e44193

File tree

6 files changed

+378
-1
lines changed

6 files changed

+378
-1
lines changed

drivers/input/mouse/Kconfig

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,18 @@ config MOUSE_PS2_LOGIPS2PP
6969

7070
If unsure, say Y.
7171

72+
config MOUSE_PS2_PIXART
73+
bool "PixArt PS/2 touchpad protocol extension" if EXPERT
74+
default y
75+
depends on MOUSE_PS2
76+
help
77+
This driver supports the PixArt PS/2 touchpad found in some
78+
laptops.
79+
Say Y here if you have a PixArt PS/2 TouchPad connected to
80+
your system.
81+
82+
If unsure, say Y.
83+
7284
config MOUSE_PS2_SYNAPTICS
7385
bool "Synaptics PS/2 mouse protocol extension" if EXPERT
7486
default y

drivers/input/mouse/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ psmouse-$(CONFIG_MOUSE_PS2_ELANTECH) += elantech.o
3232
psmouse-$(CONFIG_MOUSE_PS2_OLPC) += hgpk.o
3333
psmouse-$(CONFIG_MOUSE_PS2_LOGIPS2PP) += logips2pp.o
3434
psmouse-$(CONFIG_MOUSE_PS2_LIFEBOOK) += lifebook.o
35+
psmouse-$(CONFIG_MOUSE_PS2_PIXART) += pixart_ps2.o
3536
psmouse-$(CONFIG_MOUSE_PS2_SENTELIC) += sentelic.o
3637
psmouse-$(CONFIG_MOUSE_PS2_TRACKPOINT) += trackpoint.o
3738
psmouse-$(CONFIG_MOUSE_PS2_TOUCHKIT) += touchkit_ps2.o

drivers/input/mouse/pixart_ps2.c

Lines changed: 310 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,310 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
/*
3+
* Pixart Touchpad Controller 1336U PS2 driver
4+
*
5+
* Author: Jon Xie <[email protected]>
6+
* Jay Lee <[email protected]>
7+
* Further cleanup and restructuring by:
8+
* Binbin Zhou <[email protected]>
9+
*
10+
* Copyright (C) 2021-2024 Pixart Imaging.
11+
* Copyright (C) 2024 Loongson Technology Corporation Limited.
12+
*
13+
*/
14+
15+
#include <linux/bitfield.h>
16+
#include <linux/delay.h>
17+
#include <linux/device.h>
18+
#include <linux/input.h>
19+
#include <linux/input/mt.h>
20+
#include <linux/libps2.h>
21+
#include <linux/serio.h>
22+
#include <linux/slab.h>
23+
24+
#include "pixart_ps2.h"
25+
26+
static int pixart_read_tp_mode(struct ps2dev *ps2dev, u8 *mode)
27+
{
28+
int error;
29+
u8 param[1] = { 0 };
30+
31+
error = ps2_command(ps2dev, param, PIXART_CMD_REPORT_FORMAT);
32+
if (error)
33+
return error;
34+
35+
*mode = param[0] == 1 ? PIXART_MODE_ABS : PIXART_MODE_REL;
36+
37+
return 0;
38+
}
39+
40+
static int pixart_read_tp_type(struct ps2dev *ps2dev, u8 *type)
41+
{
42+
int error;
43+
u8 param[3] = { 0 };
44+
45+
param[0] = 0xa;
46+
error = ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
47+
if (error)
48+
return error;
49+
50+
param[0] = 0x0;
51+
error = ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
52+
if (error)
53+
return error;
54+
55+
error = ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
56+
if (error)
57+
return error;
58+
59+
error = ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
60+
if (error)
61+
return error;
62+
63+
param[0] = 0x3;
64+
error = ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
65+
if (error)
66+
return error;
67+
68+
error = ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO);
69+
if (error)
70+
return error;
71+
72+
switch (param[0]) {
73+
case 0xc:
74+
*type = PIXART_TYPE_CLICKPAD;
75+
break;
76+
case 0xe:
77+
*type = PIXART_TYPE_TOUCHPAD;
78+
break;
79+
default:
80+
return -EIO;
81+
}
82+
83+
return 0;
84+
}
85+
86+
static void pixart_reset(struct psmouse *psmouse)
87+
{
88+
ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
89+
90+
/* according to PixArt, 100ms is required for the upcoming reset */
91+
msleep(100);
92+
psmouse_reset(psmouse);
93+
}
94+
95+
static void pixart_process_packet(struct psmouse *psmouse)
96+
{
97+
struct pixart_data *priv = psmouse->private;
98+
struct input_dev *dev = psmouse->dev;
99+
const u8 *pkt = psmouse->packet;
100+
unsigned int contact_cnt = FIELD_GET(CONTACT_CNT_MASK, pkt[0]);
101+
unsigned int i, id, abs_x, abs_y;
102+
bool tip;
103+
104+
for (i = 0; i < contact_cnt; i++) {
105+
const u8 *p = &pkt[i * 3];
106+
107+
id = FIELD_GET(SLOT_ID_MASK, p[3]);
108+
abs_y = FIELD_GET(ABS_Y_MASK, p[3]) << 8 | p[1];
109+
abs_x = FIELD_GET(ABS_X_MASK, p[3]) << 8 | p[2];
110+
111+
if (i == PIXART_MAX_FINGERS - 1)
112+
tip = pkt[14] & BIT(1);
113+
else
114+
tip = pkt[3 * contact_cnt + 1] & BIT(2 * i + 1);
115+
116+
input_mt_slot(dev, id);
117+
if (input_mt_report_slot_state(dev, MT_TOOL_FINGER, tip)) {
118+
input_report_abs(dev, ABS_MT_POSITION_Y, abs_y);
119+
input_report_abs(dev, ABS_MT_POSITION_X, abs_x);
120+
}
121+
}
122+
123+
input_mt_sync_frame(dev);
124+
125+
if (priv->type == PIXART_TYPE_CLICKPAD) {
126+
input_report_key(dev, BTN_LEFT, pkt[0] & 0x03);
127+
} else {
128+
input_report_key(dev, BTN_LEFT, pkt[0] & BIT(0));
129+
input_report_key(dev, BTN_RIGHT, pkt[0] & BIT(1));
130+
}
131+
132+
input_sync(dev);
133+
}
134+
135+
static psmouse_ret_t pixart_protocol_handler(struct psmouse *psmouse)
136+
{
137+
u8 *pkt = psmouse->packet;
138+
u8 contact_cnt;
139+
140+
if ((pkt[0] & 0x8c) != 0x80)
141+
return PSMOUSE_BAD_DATA;
142+
143+
contact_cnt = FIELD_GET(CONTACT_CNT_MASK, pkt[0]);
144+
if (contact_cnt > PIXART_MAX_FINGERS)
145+
return PSMOUSE_BAD_DATA;
146+
147+
if (contact_cnt == PIXART_MAX_FINGERS &&
148+
psmouse->pktcnt < psmouse->pktsize) {
149+
return PSMOUSE_GOOD_DATA;
150+
}
151+
152+
if (contact_cnt == 0 && psmouse->pktcnt < 5)
153+
return PSMOUSE_GOOD_DATA;
154+
155+
if (psmouse->pktcnt < 3 * contact_cnt + 2)
156+
return PSMOUSE_GOOD_DATA;
157+
158+
pixart_process_packet(psmouse);
159+
160+
return PSMOUSE_FULL_PACKET;
161+
}
162+
163+
static void pixart_disconnect(struct psmouse *psmouse)
164+
{
165+
pixart_reset(psmouse);
166+
kfree(psmouse->private);
167+
psmouse->private = NULL;
168+
}
169+
170+
static int pixart_reconnect(struct psmouse *psmouse)
171+
{
172+
struct ps2dev *ps2dev = &psmouse->ps2dev;
173+
u8 mode;
174+
int error;
175+
176+
pixart_reset(psmouse);
177+
178+
error = pixart_read_tp_mode(ps2dev, &mode);
179+
if (error)
180+
return error;
181+
182+
if (mode != PIXART_MODE_ABS)
183+
return -EIO;
184+
185+
error = ps2_command(ps2dev, NULL, PIXART_CMD_SWITCH_PROTO);
186+
if (error)
187+
return error;
188+
189+
return 0;
190+
}
191+
192+
static int pixart_set_input_params(struct input_dev *dev,
193+
struct pixart_data *priv)
194+
{
195+
/* No relative support */
196+
__clear_bit(EV_REL, dev->evbit);
197+
__clear_bit(REL_X, dev->relbit);
198+
__clear_bit(REL_Y, dev->relbit);
199+
__clear_bit(BTN_MIDDLE, dev->keybit);
200+
201+
/* Buttons */
202+
__set_bit(EV_KEY, dev->evbit);
203+
__set_bit(BTN_LEFT, dev->keybit);
204+
if (priv->type == PIXART_TYPE_CLICKPAD)
205+
__set_bit(INPUT_PROP_BUTTONPAD, dev->propbit);
206+
else
207+
__set_bit(BTN_RIGHT, dev->keybit);
208+
209+
/* Absolute position */
210+
input_set_abs_params(dev, ABS_X, 0, PIXART_PAD_WIDTH, 0, 0);
211+
input_set_abs_params(dev, ABS_Y, 0, PIXART_PAD_HEIGHT, 0, 0);
212+
213+
input_set_abs_params(dev, ABS_MT_POSITION_X,
214+
0, PIXART_PAD_WIDTH, 0, 0);
215+
input_set_abs_params(dev, ABS_MT_POSITION_Y,
216+
0, PIXART_PAD_HEIGHT, 0, 0);
217+
218+
return input_mt_init_slots(dev, PIXART_MAX_FINGERS,
219+
INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED);
220+
}
221+
222+
static int pixart_query_hardware(struct ps2dev *ps2dev, u8 *mode, u8 *type)
223+
{
224+
int error;
225+
226+
error = pixart_read_tp_type(ps2dev, type);
227+
if (error)
228+
return error;
229+
230+
error = pixart_read_tp_mode(ps2dev, mode);
231+
if (error)
232+
return error;
233+
234+
return 0;
235+
}
236+
237+
int pixart_detect(struct psmouse *psmouse, bool set_properties)
238+
{
239+
u8 type;
240+
int error;
241+
242+
pixart_reset(psmouse);
243+
244+
error = pixart_read_tp_type(&psmouse->ps2dev, &type);
245+
if (error)
246+
return error;
247+
248+
if (set_properties) {
249+
psmouse->vendor = "PixArt";
250+
psmouse->name = (type == PIXART_TYPE_TOUCHPAD) ?
251+
"touchpad" : "clickpad";
252+
}
253+
254+
return 0;
255+
}
256+
257+
int pixart_init(struct psmouse *psmouse)
258+
{
259+
int error;
260+
struct pixart_data *priv;
261+
262+
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
263+
if (!priv)
264+
return -ENOMEM;
265+
266+
psmouse->private = priv;
267+
pixart_reset(psmouse);
268+
269+
error = pixart_query_hardware(&psmouse->ps2dev,
270+
&priv->mode, &priv->type);
271+
if (error) {
272+
psmouse_err(psmouse, "init: Unable to query PixArt touchpad hardware.\n");
273+
goto err_exit;
274+
}
275+
276+
/* Relative mode follows standard PS/2 mouse protocol */
277+
if (priv->mode != PIXART_MODE_ABS) {
278+
error = -EIO;
279+
goto err_exit;
280+
}
281+
282+
/* Set absolute mode */
283+
error = ps2_command(&psmouse->ps2dev, NULL, PIXART_CMD_SWITCH_PROTO);
284+
if (error) {
285+
psmouse_err(psmouse, "init: Unable to initialize PixArt absolute mode.\n");
286+
goto err_exit;
287+
}
288+
289+
error = pixart_set_input_params(psmouse->dev, priv);
290+
if (error) {
291+
psmouse_err(psmouse, "init: Unable to set input params.\n");
292+
goto err_exit;
293+
}
294+
295+
psmouse->pktsize = 15;
296+
psmouse->protocol_handler = pixart_protocol_handler;
297+
psmouse->disconnect = pixart_disconnect;
298+
psmouse->reconnect = pixart_reconnect;
299+
psmouse->cleanup = pixart_reset;
300+
/* resync is not supported yet */
301+
psmouse->resync_time = 0;
302+
303+
return 0;
304+
305+
err_exit:
306+
pixart_reset(psmouse);
307+
kfree(priv);
308+
psmouse->private = NULL;
309+
return error;
310+
}

drivers/input/mouse/pixart_ps2.h

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/* SPDX-License-Identifier: GPL-2.0-or-later */
2+
#ifndef _PIXART_PS2_H
3+
#define _PIXART_PS2_H
4+
5+
#include "psmouse.h"
6+
7+
#define PIXART_PAD_WIDTH 1023
8+
#define PIXART_PAD_HEIGHT 579
9+
#define PIXART_MAX_FINGERS 4
10+
11+
#define PIXART_CMD_REPORT_FORMAT 0x01d8
12+
#define PIXART_CMD_SWITCH_PROTO 0x00de
13+
14+
#define PIXART_MODE_REL 0
15+
#define PIXART_MODE_ABS 1
16+
17+
#define PIXART_TYPE_CLICKPAD 0
18+
#define PIXART_TYPE_TOUCHPAD 1
19+
20+
#define CONTACT_CNT_MASK GENMASK(6, 4)
21+
22+
#define SLOT_ID_MASK GENMASK(2, 0)
23+
#define ABS_Y_MASK GENMASK(5, 4)
24+
#define ABS_X_MASK GENMASK(7, 6)
25+
26+
struct pixart_data {
27+
u8 mode;
28+
u8 type;
29+
int x_max;
30+
int y_max;
31+
};
32+
33+
int pixart_detect(struct psmouse *psmouse, bool set_properties);
34+
int pixart_init(struct psmouse *psmouse);
35+
36+
#endif /* _PIXART_PS2_H */

0 commit comments

Comments
 (0)