12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
23ace3686SHans de Goede /*
33ace3686SHans de Goede * Focaltech TouchPad PS/2 mouse driver
43ace3686SHans de Goede *
53ace3686SHans de Goede * Copyright (c) 2014 Red Hat Inc.
605be1d07SMathias Gottschlag * Copyright (c) 2014 Mathias Gottschlag <mgottschlag@gmail.com>
73ace3686SHans de Goede *
83ace3686SHans de Goede * Red Hat authors:
93ace3686SHans de Goede *
103ace3686SHans de Goede * Hans de Goede <hdegoede@redhat.com>
113ace3686SHans de Goede */
123ace3686SHans de Goede
133ace3686SHans de Goede
143ace3686SHans de Goede #include <linux/device.h>
153ace3686SHans de Goede #include <linux/libps2.h>
1605be1d07SMathias Gottschlag #include <linux/input/mt.h>
1705be1d07SMathias Gottschlag #include <linux/serio.h>
1805be1d07SMathias Gottschlag #include <linux/slab.h>
193ace3686SHans de Goede #include "psmouse.h"
2005be1d07SMathias Gottschlag #include "focaltech.h"
213ace3686SHans de Goede
223ace3686SHans de Goede static const char * const focaltech_pnp_ids[] = {
233ace3686SHans de Goede "FLT0101",
243ace3686SHans de Goede "FLT0102",
253ace3686SHans de Goede "FLT0103",
263ace3686SHans de Goede NULL
273ace3686SHans de Goede };
283ace3686SHans de Goede
2905be1d07SMathias Gottschlag /*
3005be1d07SMathias Gottschlag * Even if the kernel is built without support for Focaltech PS/2 touchpads (or
3105be1d07SMathias Gottschlag * when the real driver fails to recognize the device), we still have to detect
3205be1d07SMathias Gottschlag * them in order to avoid further detection attempts confusing the touchpad.
3305be1d07SMathias Gottschlag * This way it at least works in PS/2 mouse compatibility mode.
3405be1d07SMathias Gottschlag */
focaltech_detect(struct psmouse * psmouse,bool set_properties)353ace3686SHans de Goede int focaltech_detect(struct psmouse *psmouse, bool set_properties)
363ace3686SHans de Goede {
373ace3686SHans de Goede if (!psmouse_matches_pnp_id(psmouse, focaltech_pnp_ids))
383ace3686SHans de Goede return -ENODEV;
393ace3686SHans de Goede
403ace3686SHans de Goede if (set_properties) {
413ace3686SHans de Goede psmouse->vendor = "FocalTech";
42324ae095SDmitry Tunin psmouse->name = "Touchpad";
433ace3686SHans de Goede }
443ace3686SHans de Goede
453ace3686SHans de Goede return 0;
463ace3686SHans de Goede }
473ace3686SHans de Goede
4805be1d07SMathias Gottschlag #ifdef CONFIG_MOUSE_PS2_FOCALTECH
4905be1d07SMathias Gottschlag
5005be1d07SMathias Gottschlag /*
5105be1d07SMathias Gottschlag * Packet types - the numbers are not consecutive, so we might be missing
5205be1d07SMathias Gottschlag * something here.
5305be1d07SMathias Gottschlag */
5405be1d07SMathias Gottschlag #define FOC_TOUCH 0x3 /* bitmap of active fingers */
5505be1d07SMathias Gottschlag #define FOC_ABS 0x6 /* absolute position of one finger */
5605be1d07SMathias Gottschlag #define FOC_REL 0x9 /* relative position of 1-2 fingers */
5705be1d07SMathias Gottschlag
5805be1d07SMathias Gottschlag #define FOC_MAX_FINGERS 5
5905be1d07SMathias Gottschlag
6005be1d07SMathias Gottschlag /*
6105be1d07SMathias Gottschlag * Current state of a single finger on the touchpad.
6205be1d07SMathias Gottschlag */
6305be1d07SMathias Gottschlag struct focaltech_finger_state {
6405be1d07SMathias Gottschlag /* The touchpad has generated a touch event for the finger */
6505be1d07SMathias Gottschlag bool active;
6605be1d07SMathias Gottschlag
6705be1d07SMathias Gottschlag /*
6805be1d07SMathias Gottschlag * The touchpad has sent position data for the finger. The
6905be1d07SMathias Gottschlag * flag is 0 when the finger is not active, and there is a
7005be1d07SMathias Gottschlag * time between the first touch event for the finger and the
7105be1d07SMathias Gottschlag * following absolute position packet for the finger where the
7205be1d07SMathias Gottschlag * touchpad has declared the finger to be valid, but we do not
7305be1d07SMathias Gottschlag * have any valid position yet.
7405be1d07SMathias Gottschlag */
7505be1d07SMathias Gottschlag bool valid;
7605be1d07SMathias Gottschlag
7705be1d07SMathias Gottschlag /*
7805be1d07SMathias Gottschlag * Absolute position (from the bottom left corner) of the
7905be1d07SMathias Gottschlag * finger.
8005be1d07SMathias Gottschlag */
8105be1d07SMathias Gottschlag unsigned int x;
8205be1d07SMathias Gottschlag unsigned int y;
8305be1d07SMathias Gottschlag };
8405be1d07SMathias Gottschlag
8505be1d07SMathias Gottschlag /*
8605be1d07SMathias Gottschlag * Description of the current state of the touchpad hardware.
8705be1d07SMathias Gottschlag */
8805be1d07SMathias Gottschlag struct focaltech_hw_state {
8905be1d07SMathias Gottschlag /*
9005be1d07SMathias Gottschlag * The touchpad tracks the positions of the fingers for us,
9105be1d07SMathias Gottschlag * the array indices correspond to the finger indices returned
9205be1d07SMathias Gottschlag * in the report packages.
9305be1d07SMathias Gottschlag */
9405be1d07SMathias Gottschlag struct focaltech_finger_state fingers[FOC_MAX_FINGERS];
9505be1d07SMathias Gottschlag
9685919a00SDmitry Tunin /*
9785919a00SDmitry Tunin * Finger width 0-7 and 15 for a very big contact area.
9885919a00SDmitry Tunin * 15 value stays until the finger is released.
9985919a00SDmitry Tunin * Width is reported only in absolute packets.
10085919a00SDmitry Tunin * Since hardware reports width only for last touching finger,
10185919a00SDmitry Tunin * there is no need to store width for every specific finger,
10285919a00SDmitry Tunin * so we keep only last value reported.
10385919a00SDmitry Tunin */
10485919a00SDmitry Tunin unsigned int width;
10585919a00SDmitry Tunin
10605be1d07SMathias Gottschlag /* True if the clickpad has been pressed. */
10705be1d07SMathias Gottschlag bool pressed;
10805be1d07SMathias Gottschlag };
10905be1d07SMathias Gottschlag
11005be1d07SMathias Gottschlag struct focaltech_data {
11105be1d07SMathias Gottschlag unsigned int x_max, y_max;
11205be1d07SMathias Gottschlag struct focaltech_hw_state state;
11305be1d07SMathias Gottschlag };
11405be1d07SMathias Gottschlag
focaltech_report_state(struct psmouse * psmouse)11505be1d07SMathias Gottschlag static void focaltech_report_state(struct psmouse *psmouse)
11605be1d07SMathias Gottschlag {
11705be1d07SMathias Gottschlag struct focaltech_data *priv = psmouse->private;
11805be1d07SMathias Gottschlag struct focaltech_hw_state *state = &priv->state;
11905be1d07SMathias Gottschlag struct input_dev *dev = psmouse->dev;
12005be1d07SMathias Gottschlag int i;
12105be1d07SMathias Gottschlag
12205be1d07SMathias Gottschlag for (i = 0; i < FOC_MAX_FINGERS; i++) {
12305be1d07SMathias Gottschlag struct focaltech_finger_state *finger = &state->fingers[i];
12405be1d07SMathias Gottschlag bool active = finger->active && finger->valid;
12505be1d07SMathias Gottschlag
12605be1d07SMathias Gottschlag input_mt_slot(dev, i);
12705be1d07SMathias Gottschlag input_mt_report_slot_state(dev, MT_TOOL_FINGER, active);
12805be1d07SMathias Gottschlag if (active) {
129679d83eaSMathias Gottschlag unsigned int clamped_x, clamped_y;
130679d83eaSMathias Gottschlag /*
131679d83eaSMathias Gottschlag * The touchpad might report invalid data, so we clamp
132679d83eaSMathias Gottschlag * the resulting values so that we do not confuse
133679d83eaSMathias Gottschlag * userspace.
134679d83eaSMathias Gottschlag */
135679d83eaSMathias Gottschlag clamped_x = clamp(finger->x, 0U, priv->x_max);
136679d83eaSMathias Gottschlag clamped_y = clamp(finger->y, 0U, priv->y_max);
137679d83eaSMathias Gottschlag input_report_abs(dev, ABS_MT_POSITION_X, clamped_x);
13805be1d07SMathias Gottschlag input_report_abs(dev, ABS_MT_POSITION_Y,
139679d83eaSMathias Gottschlag priv->y_max - clamped_y);
14085919a00SDmitry Tunin input_report_abs(dev, ABS_TOOL_WIDTH, state->width);
14105be1d07SMathias Gottschlag }
14205be1d07SMathias Gottschlag }
14305be1d07SMathias Gottschlag input_mt_report_pointer_emulation(dev, true);
14405be1d07SMathias Gottschlag
145324ae095SDmitry Tunin input_report_key(dev, BTN_LEFT, state->pressed);
146324ae095SDmitry Tunin input_sync(dev);
14705be1d07SMathias Gottschlag }
14805be1d07SMathias Gottschlag
focaltech_process_touch_packet(struct psmouse * psmouse,unsigned char * packet)14905be1d07SMathias Gottschlag static void focaltech_process_touch_packet(struct psmouse *psmouse,
15005be1d07SMathias Gottschlag unsigned char *packet)
15105be1d07SMathias Gottschlag {
15205be1d07SMathias Gottschlag struct focaltech_data *priv = psmouse->private;
15305be1d07SMathias Gottschlag struct focaltech_hw_state *state = &priv->state;
15405be1d07SMathias Gottschlag unsigned char fingers = packet[1];
15505be1d07SMathias Gottschlag int i;
15605be1d07SMathias Gottschlag
15705be1d07SMathias Gottschlag state->pressed = (packet[0] >> 4) & 1;
15805be1d07SMathias Gottschlag
15905be1d07SMathias Gottschlag /* the second byte contains a bitmap of all fingers touching the pad */
16005be1d07SMathias Gottschlag for (i = 0; i < FOC_MAX_FINGERS; i++) {
16105be1d07SMathias Gottschlag state->fingers[i].active = fingers & 0x1;
16205be1d07SMathias Gottschlag if (!state->fingers[i].active) {
16305be1d07SMathias Gottschlag /*
16405be1d07SMathias Gottschlag * Even when the finger becomes active again, we still
16505be1d07SMathias Gottschlag * will have to wait for the first valid position.
16605be1d07SMathias Gottschlag */
16705be1d07SMathias Gottschlag state->fingers[i].valid = false;
16805be1d07SMathias Gottschlag }
16905be1d07SMathias Gottschlag fingers >>= 1;
17005be1d07SMathias Gottschlag }
17105be1d07SMathias Gottschlag }
17205be1d07SMathias Gottschlag
focaltech_process_abs_packet(struct psmouse * psmouse,unsigned char * packet)17305be1d07SMathias Gottschlag static void focaltech_process_abs_packet(struct psmouse *psmouse,
17405be1d07SMathias Gottschlag unsigned char *packet)
17505be1d07SMathias Gottschlag {
17605be1d07SMathias Gottschlag struct focaltech_data *priv = psmouse->private;
17705be1d07SMathias Gottschlag struct focaltech_hw_state *state = &priv->state;
17805be1d07SMathias Gottschlag unsigned int finger;
17905be1d07SMathias Gottschlag
18005be1d07SMathias Gottschlag finger = (packet[1] >> 4) - 1;
18105be1d07SMathias Gottschlag if (finger >= FOC_MAX_FINGERS) {
18205be1d07SMathias Gottschlag psmouse_err(psmouse, "Invalid finger in abs packet: %d\n",
18305be1d07SMathias Gottschlag finger);
18405be1d07SMathias Gottschlag return;
18505be1d07SMathias Gottschlag }
18605be1d07SMathias Gottschlag
18705be1d07SMathias Gottschlag state->pressed = (packet[0] >> 4) & 1;
18805be1d07SMathias Gottschlag
18905be1d07SMathias Gottschlag state->fingers[finger].x = ((packet[1] & 0xf) << 8) | packet[2];
19005be1d07SMathias Gottschlag state->fingers[finger].y = (packet[3] << 8) | packet[4];
19185919a00SDmitry Tunin state->width = packet[5] >> 4;
19205be1d07SMathias Gottschlag state->fingers[finger].valid = true;
19305be1d07SMathias Gottschlag }
19405be1d07SMathias Gottschlag
focaltech_process_rel_packet(struct psmouse * psmouse,unsigned char * packet)19505be1d07SMathias Gottschlag static void focaltech_process_rel_packet(struct psmouse *psmouse,
19605be1d07SMathias Gottschlag unsigned char *packet)
19705be1d07SMathias Gottschlag {
19805be1d07SMathias Gottschlag struct focaltech_data *priv = psmouse->private;
19905be1d07SMathias Gottschlag struct focaltech_hw_state *state = &priv->state;
20005be1d07SMathias Gottschlag int finger1, finger2;
20105be1d07SMathias Gottschlag
20205be1d07SMathias Gottschlag state->pressed = packet[0] >> 7;
20305be1d07SMathias Gottschlag finger1 = ((packet[0] >> 4) & 0x7) - 1;
20405be1d07SMathias Gottschlag if (finger1 < FOC_MAX_FINGERS) {
205*8980f190SJason A. Donenfeld state->fingers[finger1].x += (s8)packet[1];
206*8980f190SJason A. Donenfeld state->fingers[finger1].y += (s8)packet[2];
20705be1d07SMathias Gottschlag } else {
20805be1d07SMathias Gottschlag psmouse_err(psmouse, "First finger in rel packet invalid: %d\n",
20905be1d07SMathias Gottschlag finger1);
21005be1d07SMathias Gottschlag }
21105be1d07SMathias Gottschlag
21205be1d07SMathias Gottschlag /*
21305be1d07SMathias Gottschlag * If there is an odd number of fingers, the last relative
21405be1d07SMathias Gottschlag * packet only contains one finger. In this case, the second
21505be1d07SMathias Gottschlag * finger index in the packet is 0 (we subtract 1 in the lines
21605be1d07SMathias Gottschlag * above to create array indices, so the finger will overflow
21705be1d07SMathias Gottschlag * and be above FOC_MAX_FINGERS).
21805be1d07SMathias Gottschlag */
21905be1d07SMathias Gottschlag finger2 = ((packet[3] >> 4) & 0x7) - 1;
22005be1d07SMathias Gottschlag if (finger2 < FOC_MAX_FINGERS) {
221*8980f190SJason A. Donenfeld state->fingers[finger2].x += (s8)packet[4];
222*8980f190SJason A. Donenfeld state->fingers[finger2].y += (s8)packet[5];
22305be1d07SMathias Gottschlag }
22405be1d07SMathias Gottschlag }
22505be1d07SMathias Gottschlag
focaltech_process_packet(struct psmouse * psmouse)22605be1d07SMathias Gottschlag static void focaltech_process_packet(struct psmouse *psmouse)
22705be1d07SMathias Gottschlag {
22805be1d07SMathias Gottschlag unsigned char *packet = psmouse->packet;
22905be1d07SMathias Gottschlag
23005be1d07SMathias Gottschlag switch (packet[0] & 0xf) {
23105be1d07SMathias Gottschlag case FOC_TOUCH:
23205be1d07SMathias Gottschlag focaltech_process_touch_packet(psmouse, packet);
23305be1d07SMathias Gottschlag break;
23405be1d07SMathias Gottschlag
23505be1d07SMathias Gottschlag case FOC_ABS:
23605be1d07SMathias Gottschlag focaltech_process_abs_packet(psmouse, packet);
23705be1d07SMathias Gottschlag break;
23805be1d07SMathias Gottschlag
23905be1d07SMathias Gottschlag case FOC_REL:
24005be1d07SMathias Gottschlag focaltech_process_rel_packet(psmouse, packet);
24105be1d07SMathias Gottschlag break;
24205be1d07SMathias Gottschlag
24305be1d07SMathias Gottschlag default:
24405be1d07SMathias Gottschlag psmouse_err(psmouse, "Unknown packet type: %02x\n", packet[0]);
24505be1d07SMathias Gottschlag break;
24605be1d07SMathias Gottschlag }
24705be1d07SMathias Gottschlag
24805be1d07SMathias Gottschlag focaltech_report_state(psmouse);
24905be1d07SMathias Gottschlag }
25005be1d07SMathias Gottschlag
focaltech_process_byte(struct psmouse * psmouse)25105be1d07SMathias Gottschlag static psmouse_ret_t focaltech_process_byte(struct psmouse *psmouse)
25205be1d07SMathias Gottschlag {
25305be1d07SMathias Gottschlag if (psmouse->pktcnt >= 6) { /* Full packet received */
25405be1d07SMathias Gottschlag focaltech_process_packet(psmouse);
25505be1d07SMathias Gottschlag return PSMOUSE_FULL_PACKET;
25605be1d07SMathias Gottschlag }
25705be1d07SMathias Gottschlag
25805be1d07SMathias Gottschlag /*
25905be1d07SMathias Gottschlag * We might want to do some validation of the data here, but
26005be1d07SMathias Gottschlag * we do not know the protocol well enough
26105be1d07SMathias Gottschlag */
26205be1d07SMathias Gottschlag return PSMOUSE_GOOD_DATA;
26305be1d07SMathias Gottschlag }
26405be1d07SMathias Gottschlag
focaltech_switch_protocol(struct psmouse * psmouse)26505be1d07SMathias Gottschlag static int focaltech_switch_protocol(struct psmouse *psmouse)
26605be1d07SMathias Gottschlag {
26705be1d07SMathias Gottschlag struct ps2dev *ps2dev = &psmouse->ps2dev;
26805be1d07SMathias Gottschlag unsigned char param[3];
26905be1d07SMathias Gottschlag
27005be1d07SMathias Gottschlag param[0] = 0;
27105be1d07SMathias Gottschlag if (ps2_command(ps2dev, param, 0x10f8))
27205be1d07SMathias Gottschlag return -EIO;
27305be1d07SMathias Gottschlag
27405be1d07SMathias Gottschlag if (ps2_command(ps2dev, param, 0x10f8))
27505be1d07SMathias Gottschlag return -EIO;
27605be1d07SMathias Gottschlag
27705be1d07SMathias Gottschlag if (ps2_command(ps2dev, param, 0x10f8))
27805be1d07SMathias Gottschlag return -EIO;
27905be1d07SMathias Gottschlag
28005be1d07SMathias Gottschlag param[0] = 1;
28105be1d07SMathias Gottschlag if (ps2_command(ps2dev, param, 0x10f8))
28205be1d07SMathias Gottschlag return -EIO;
28305be1d07SMathias Gottschlag
28405be1d07SMathias Gottschlag if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETSCALE11))
28505be1d07SMathias Gottschlag return -EIO;
28605be1d07SMathias Gottschlag
28705be1d07SMathias Gottschlag if (ps2_command(ps2dev, param, PSMOUSE_CMD_ENABLE))
28805be1d07SMathias Gottschlag return -EIO;
2893ace3686SHans de Goede
2903ace3686SHans de Goede return 0;
2913ace3686SHans de Goede }
29205be1d07SMathias Gottschlag
focaltech_reset(struct psmouse * psmouse)2932b6f39e9SDmitry Torokhov static void focaltech_reset(struct psmouse *psmouse)
2942b6f39e9SDmitry Torokhov {
2952b6f39e9SDmitry Torokhov ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
2962b6f39e9SDmitry Torokhov psmouse_reset(psmouse);
2972b6f39e9SDmitry Torokhov }
2982b6f39e9SDmitry Torokhov
focaltech_disconnect(struct psmouse * psmouse)29905be1d07SMathias Gottschlag static void focaltech_disconnect(struct psmouse *psmouse)
30005be1d07SMathias Gottschlag {
30105be1d07SMathias Gottschlag focaltech_reset(psmouse);
30205be1d07SMathias Gottschlag kfree(psmouse->private);
30305be1d07SMathias Gottschlag psmouse->private = NULL;
30405be1d07SMathias Gottschlag }
30505be1d07SMathias Gottschlag
focaltech_reconnect(struct psmouse * psmouse)30605be1d07SMathias Gottschlag static int focaltech_reconnect(struct psmouse *psmouse)
30705be1d07SMathias Gottschlag {
30805be1d07SMathias Gottschlag int error;
30905be1d07SMathias Gottschlag
31005be1d07SMathias Gottschlag focaltech_reset(psmouse);
31105be1d07SMathias Gottschlag
31205be1d07SMathias Gottschlag error = focaltech_switch_protocol(psmouse);
31305be1d07SMathias Gottschlag if (error) {
31405be1d07SMathias Gottschlag psmouse_err(psmouse, "Unable to initialize the device\n");
31505be1d07SMathias Gottschlag return error;
31605be1d07SMathias Gottschlag }
31705be1d07SMathias Gottschlag
31805be1d07SMathias Gottschlag return 0;
31905be1d07SMathias Gottschlag }
32005be1d07SMathias Gottschlag
focaltech_set_input_params(struct psmouse * psmouse)32105be1d07SMathias Gottschlag static void focaltech_set_input_params(struct psmouse *psmouse)
32205be1d07SMathias Gottschlag {
32305be1d07SMathias Gottschlag struct input_dev *dev = psmouse->dev;
32405be1d07SMathias Gottschlag struct focaltech_data *priv = psmouse->private;
32505be1d07SMathias Gottschlag
32605be1d07SMathias Gottschlag /*
32705be1d07SMathias Gottschlag * Undo part of setup done for us by psmouse core since touchpad
32805be1d07SMathias Gottschlag * is not a relative device.
32905be1d07SMathias Gottschlag */
33005be1d07SMathias Gottschlag __clear_bit(EV_REL, dev->evbit);
33105be1d07SMathias Gottschlag __clear_bit(REL_X, dev->relbit);
33205be1d07SMathias Gottschlag __clear_bit(REL_Y, dev->relbit);
33305be1d07SMathias Gottschlag __clear_bit(BTN_RIGHT, dev->keybit);
33405be1d07SMathias Gottschlag __clear_bit(BTN_MIDDLE, dev->keybit);
33505be1d07SMathias Gottschlag
33605be1d07SMathias Gottschlag /*
33705be1d07SMathias Gottschlag * Now set up our capabilities.
33805be1d07SMathias Gottschlag */
33905be1d07SMathias Gottschlag __set_bit(EV_ABS, dev->evbit);
34005be1d07SMathias Gottschlag input_set_abs_params(dev, ABS_MT_POSITION_X, 0, priv->x_max, 0, 0);
34105be1d07SMathias Gottschlag input_set_abs_params(dev, ABS_MT_POSITION_Y, 0, priv->y_max, 0, 0);
34285919a00SDmitry Tunin input_set_abs_params(dev, ABS_TOOL_WIDTH, 0, 15, 0, 0);
34305be1d07SMathias Gottschlag input_mt_init_slots(dev, 5, INPUT_MT_POINTER);
34405be1d07SMathias Gottschlag __set_bit(INPUT_PROP_BUTTONPAD, dev->propbit);
34505be1d07SMathias Gottschlag }
34605be1d07SMathias Gottschlag
focaltech_read_register(struct ps2dev * ps2dev,int reg,unsigned char * param)34705be1d07SMathias Gottschlag static int focaltech_read_register(struct ps2dev *ps2dev, int reg,
34805be1d07SMathias Gottschlag unsigned char *param)
34905be1d07SMathias Gottschlag {
35005be1d07SMathias Gottschlag if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETSCALE11))
35105be1d07SMathias Gottschlag return -EIO;
35205be1d07SMathias Gottschlag
35305be1d07SMathias Gottschlag param[0] = 0;
35405be1d07SMathias Gottschlag if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
35505be1d07SMathias Gottschlag return -EIO;
35605be1d07SMathias Gottschlag
35705be1d07SMathias Gottschlag if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
35805be1d07SMathias Gottschlag return -EIO;
35905be1d07SMathias Gottschlag
36005be1d07SMathias Gottschlag if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
36105be1d07SMathias Gottschlag return -EIO;
36205be1d07SMathias Gottschlag
36305be1d07SMathias Gottschlag param[0] = reg;
36405be1d07SMathias Gottschlag if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
36505be1d07SMathias Gottschlag return -EIO;
36605be1d07SMathias Gottschlag
36705be1d07SMathias Gottschlag if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
36805be1d07SMathias Gottschlag return -EIO;
36905be1d07SMathias Gottschlag
37005be1d07SMathias Gottschlag return 0;
37105be1d07SMathias Gottschlag }
37205be1d07SMathias Gottschlag
focaltech_read_size(struct psmouse * psmouse)37305be1d07SMathias Gottschlag static int focaltech_read_size(struct psmouse *psmouse)
37405be1d07SMathias Gottschlag {
37505be1d07SMathias Gottschlag struct ps2dev *ps2dev = &psmouse->ps2dev;
37605be1d07SMathias Gottschlag struct focaltech_data *priv = psmouse->private;
37705be1d07SMathias Gottschlag char param[3];
37805be1d07SMathias Gottschlag
37905be1d07SMathias Gottschlag if (focaltech_read_register(ps2dev, 2, param))
38005be1d07SMathias Gottschlag return -EIO;
38105be1d07SMathias Gottschlag
38205be1d07SMathias Gottschlag /* not sure whether this is 100% correct */
38305be1d07SMathias Gottschlag priv->x_max = (unsigned char)param[1] * 128;
38405be1d07SMathias Gottschlag priv->y_max = (unsigned char)param[2] * 128;
38505be1d07SMathias Gottschlag
38605be1d07SMathias Gottschlag return 0;
38705be1d07SMathias Gottschlag }
3884ec212f0SMathias Gottschlag
focaltech_set_resolution(struct psmouse * psmouse,unsigned int resolution)389db808498SBaoyou Xie static void focaltech_set_resolution(struct psmouse *psmouse,
390db808498SBaoyou Xie unsigned int resolution)
3914ec212f0SMathias Gottschlag {
3924ec212f0SMathias Gottschlag /* not supported yet */
3934ec212f0SMathias Gottschlag }
3944ec212f0SMathias Gottschlag
focaltech_set_rate(struct psmouse * psmouse,unsigned int rate)3954ec212f0SMathias Gottschlag static void focaltech_set_rate(struct psmouse *psmouse, unsigned int rate)
3964ec212f0SMathias Gottschlag {
3974ec212f0SMathias Gottschlag /* not supported yet */
3984ec212f0SMathias Gottschlag }
3994ec212f0SMathias Gottschlag
focaltech_set_scale(struct psmouse * psmouse,enum psmouse_scale scale)4004ec212f0SMathias Gottschlag static void focaltech_set_scale(struct psmouse *psmouse,
4014ec212f0SMathias Gottschlag enum psmouse_scale scale)
4024ec212f0SMathias Gottschlag {
4034ec212f0SMathias Gottschlag /* not supported yet */
4044ec212f0SMathias Gottschlag }
4054ec212f0SMathias Gottschlag
focaltech_init(struct psmouse * psmouse)40605be1d07SMathias Gottschlag int focaltech_init(struct psmouse *psmouse)
40705be1d07SMathias Gottschlag {
40805be1d07SMathias Gottschlag struct focaltech_data *priv;
40905be1d07SMathias Gottschlag int error;
41005be1d07SMathias Gottschlag
41105be1d07SMathias Gottschlag psmouse->private = priv = kzalloc(sizeof(struct focaltech_data),
41205be1d07SMathias Gottschlag GFP_KERNEL);
41305be1d07SMathias Gottschlag if (!priv)
41405be1d07SMathias Gottschlag return -ENOMEM;
41505be1d07SMathias Gottschlag
41605be1d07SMathias Gottschlag focaltech_reset(psmouse);
41705be1d07SMathias Gottschlag
41805be1d07SMathias Gottschlag error = focaltech_read_size(psmouse);
41905be1d07SMathias Gottschlag if (error) {
42005be1d07SMathias Gottschlag psmouse_err(psmouse,
42105be1d07SMathias Gottschlag "Unable to read the size of the touchpad\n");
42205be1d07SMathias Gottschlag goto fail;
42305be1d07SMathias Gottschlag }
42405be1d07SMathias Gottschlag
42505be1d07SMathias Gottschlag error = focaltech_switch_protocol(psmouse);
42605be1d07SMathias Gottschlag if (error) {
42705be1d07SMathias Gottschlag psmouse_err(psmouse, "Unable to initialize the device\n");
42805be1d07SMathias Gottschlag goto fail;
42905be1d07SMathias Gottschlag }
43005be1d07SMathias Gottschlag
43105be1d07SMathias Gottschlag focaltech_set_input_params(psmouse);
43205be1d07SMathias Gottschlag
43305be1d07SMathias Gottschlag psmouse->protocol_handler = focaltech_process_byte;
43405be1d07SMathias Gottschlag psmouse->pktsize = 6;
43505be1d07SMathias Gottschlag psmouse->disconnect = focaltech_disconnect;
43605be1d07SMathias Gottschlag psmouse->reconnect = focaltech_reconnect;
43705be1d07SMathias Gottschlag psmouse->cleanup = focaltech_reset;
43805be1d07SMathias Gottschlag /* resync is not supported yet */
43905be1d07SMathias Gottschlag psmouse->resync_time = 0;
4404ec212f0SMathias Gottschlag /*
4414ec212f0SMathias Gottschlag * rate/resolution/scale changes are not supported yet, and
4424ec212f0SMathias Gottschlag * the generic implementations of these functions seem to
4434ec212f0SMathias Gottschlag * confuse some touchpads
4444ec212f0SMathias Gottschlag */
4454ec212f0SMathias Gottschlag psmouse->set_resolution = focaltech_set_resolution;
4464ec212f0SMathias Gottschlag psmouse->set_rate = focaltech_set_rate;
4474ec212f0SMathias Gottschlag psmouse->set_scale = focaltech_set_scale;
44805be1d07SMathias Gottschlag
44905be1d07SMathias Gottschlag return 0;
45005be1d07SMathias Gottschlag
45105be1d07SMathias Gottschlag fail:
45205be1d07SMathias Gottschlag focaltech_reset(psmouse);
45305be1d07SMathias Gottschlag kfree(priv);
45405be1d07SMathias Gottschlag return error;
45505be1d07SMathias Gottschlag }
45605be1d07SMathias Gottschlag #endif /* CONFIG_MOUSE_PS2_FOCALTECH */
457