diff options
author | Tai-hwa Liang <avatar@sentelic.com> | 2009-05-10 18:15:39 -0700 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2009-08-19 21:46:09 -0700 |
commit | fc69f4a6af49ee69475dc4217924d9edf77760e0 (patch) | |
tree | 2b200846dc0848e8fa85f439d9a1c98652356348 | |
parent | 3b72094409ab673d096b3852f4636be540780faf (diff) |
Input: add new driver for Sentelic Finger Sensing Pad
This is the driver for Sentelic Finger Sensing Pad which can be found
on MSI WIND Netbook.
Signed-off-by: Tai-hwa Liang <avatar@sentelic.com>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
-rw-r--r-- | Documentation/input/sentelic.txt | 475 | ||||
-rw-r--r-- | drivers/input/mouse/Kconfig | 8 | ||||
-rw-r--r-- | drivers/input/mouse/Makefile | 1 | ||||
-rw-r--r-- | drivers/input/mouse/psmouse-base.c | 26 | ||||
-rw-r--r-- | drivers/input/mouse/psmouse.h | 1 | ||||
-rw-r--r-- | drivers/input/mouse/sentelic.c | 867 | ||||
-rw-r--r-- | drivers/input/mouse/sentelic.h | 98 | ||||
-rw-r--r-- | drivers/input/serio/libps2.c | 15 | ||||
-rw-r--r-- | include/linux/libps2.h | 1 |
9 files changed, 1488 insertions, 4 deletions
diff --git a/Documentation/input/sentelic.txt b/Documentation/input/sentelic.txt new file mode 100644 index 00000000000..f7160a2fb6a --- /dev/null +++ b/Documentation/input/sentelic.txt @@ -0,0 +1,475 @@ +Copyright (C) 2002-2008 Sentelic Corporation. +Last update: Oct-31-2008 + +============================================================================== +* Finger Sensing Pad Intellimouse Mode(scrolling wheel, 4th and 5th buttons) +============================================================================== +A) MSID 4: Scrolling wheel mode plus Forward page(4th button) and Backward + page (5th button) +@1. Set sample rate to 200; +@2. Set sample rate to 200; +@3. Set sample rate to 80; +@4. Issuing the "Get device ID" command (0xF2) and waits for the response; +@5. FSP will respond 0x04. + +Packet 1 + Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +BYTE |---------------|BYTE |---------------|BYTE|---------------|BYTE|---------------| + 1 |Y|X|y|x|1|M|R|L| 2 |X|X|X|X|X|X|X|X| 3 |Y|Y|Y|Y|Y|Y|Y|Y| 4 | | |B|F|W|W|W|W| + |---------------| |---------------| |---------------| |---------------| + +Byte 1: Bit7 => Y overflow + Bit6 => X overflow + Bit5 => Y sign bit + Bit4 => X sign bit + Bit3 => 1 + Bit2 => Middle Button, 1 is pressed, 0 is not pressed. + Bit1 => Right Button, 1 is pressed, 0 is not pressed. + Bit0 => Left Button, 1 is pressed, 0 is not pressed. +Byte 2: X Movement(9-bit 2's complement integers) +Byte 3: Y Movement(9-bit 2's complement integers) +Byte 4: Bit3~Bit0 => the scrolling wheel's movement since the last data report. + valid values, -8 ~ +7 + Bit4 => 1 = 4th mouse button is pressed, Forward one page. + 0 = 4th mouse button is not pressed. + Bit5 => 1 = 5th mouse button is pressed, Backward one page. + 0 = 5th mouse button is not pressed. + +B) MSID 6: Horizontal and Vertical scrolling. +@ Set bit 1 in register 0x40 to 1 + +# FSP replaces scrolling wheel's movement as 4 bits to show horizontal and + vertical scrolling. + +Packet 1 + Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +BYTE |---------------|BYTE |---------------|BYTE|---------------|BYTE|---------------| + 1 |Y|X|y|x|1|M|R|L| 2 |X|X|X|X|X|X|X|X| 3 |Y|Y|Y|Y|Y|Y|Y|Y| 4 | | |B|F|l|r|u|d| + |---------------| |---------------| |---------------| |---------------| + +Byte 1: Bit7 => Y overflow + Bit6 => X overflow + Bit5 => Y sign bit + Bit4 => X sign bit + Bit3 => 1 + Bit2 => Middle Button, 1 is pressed, 0 is not pressed. + Bit1 => Right Button, 1 is pressed, 0 is not pressed. + Bit0 => Left Button, 1 is pressed, 0 is not pressed. +Byte 2: X Movement(9-bit 2's complement integers) +Byte 3: Y Movement(9-bit 2's complement integers) +Byte 4: Bit0 => the Vertical scrolling movement downward. + Bit1 => the Vertical scrolling movement upward. + Bit2 => the Vertical scrolling movement rightward. + Bit3 => the Vertical scrolling movement leftward. + Bit4 => 1 = 4th mouse button is pressed, Forward one page. + 0 = 4th mouse button is not pressed. + Bit5 => 1 = 5th mouse button is pressed, Backward one page. + 0 = 5th mouse button is not pressed. + +C) MSID 7: +# FSP uses 2 packets(8 Bytes) data to represent Absolute Position + so we have PACKET NUMBER to identify packets. + If PACKET NUMBER is 0, the packet is Packet 1. + If PACKET NUMBER is 1, the packet is Packet 2. + Please count this number in program. + +# MSID6 special packet will be enable at the same time when enable MSID 7. + +============================================================================== +* Absolute position for STL3886-G0. +============================================================================== +@ Set bit 2 or 3 in register 0x40 to 1 +@ Set bit 6 in register 0x40 to 1 + +Packet 1 (ABSOLUTE POSITION) + Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +BYTE |---------------|BYTE |---------------|BYTE|---------------|BYTE|---------------| + 1 |0|1|V|1|1|M|R|L| 2 |X|X|X|X|X|X|X|X| 3 |Y|Y|Y|Y|Y|Y|Y|Y| 4 |r|l|d|u|X|X|Y|Y| + |---------------| |---------------| |---------------| |---------------| + +Byte 1: Bit7~Bit6 => 00, Normal data packet + => 01, Absolute coordination packet + => 10, Notify packet + Bit5 => valid bit + Bit4 => 1 + Bit3 => 1 + Bit2 => Middle Button, 1 is pressed, 0 is not pressed. + Bit1 => Right Button, 1 is pressed, 0 is not pressed. + Bit0 => Left Button, 1 is pressed, 0 is not pressed. +Byte 2: X coordinate (xpos[9:2]) +Byte 3: Y coordinate (ypos[9:2]) +Byte 4: Bit1~Bit0 => Y coordinate (xpos[1:0]) + Bit3~Bit2 => X coordinate (ypos[1:0]) + Bit4 => scroll up + Bit5 => scroll down + Bit6 => scroll left + Bit7 => scroll right + +Notify Packet for G0 + Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +BYTE |---------------|BYTE |---------------|BYTE|---------------|BYTE|---------------| + 1 |1|0|0|1|1|M|R|L| 2 |C|C|C|C|C|C|C|C| 3 |M|M|M|M|M|M|M|M| 4 |0|0|0|0|0|0|0|0| + |---------------| |---------------| |---------------| |---------------| + +Byte 1: Bit7~Bit6 => 00, Normal data packet + => 01, Absolute coordination packet + => 10, Notify packet + Bit5 => 0 + Bit4 => 1 + Bit3 => 1 + Bit2 => Middle Button, 1 is pressed, 0 is not pressed. + Bit1 => Right Button, 1 is pressed, 0 is not pressed. + Bit0 => Left Button, 1 is pressed, 0 is not pressed. +Byte 2: Message Type => 0x5A (Enable/Disable status packet) + Mode Type => 0xA5 (Normal/Icon mode status) +Byte 3: Message Type => 0x00 (Disabled) + => 0x01 (Enabled) + Mode Type => 0x00 (Normal) + => 0x01 (Icon) +Byte 4: Bit7~Bit0 => Don't Care + +============================================================================== +* Absolute position for STL3888-A0. +============================================================================== +Packet 1 (ABSOLUTE POSITION) + Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +BYTE |---------------|BYTE |---------------|BYTE|---------------|BYTE|---------------| + 1 |0|1|V|A|1|L|0|1| 2 |X|X|X|X|X|X|X|X| 3 |Y|Y|Y|Y|Y|Y|Y|Y| 4 |x|x|y|y|X|X|Y|Y| + |---------------| |---------------| |---------------| |---------------| + +Byte 1: Bit7~Bit6 => 00, Normal data packet + => 01, Absolute coordination packet + => 10, Notify packet + Bit5 => Valid bit, 0 means that the coordinate is invalid or finger up. + When both fingers are up, the last two reports have zero valid + bit. + Bit4 => arc + Bit3 => 1 + Bit2 => Left Button, 1 is pressed, 0 is released. + Bit1 => 0 + Bit0 => 1 +Byte 2: X coordinate (xpos[9:2]) +Byte 3: Y coordinate (ypos[9:2]) +Byte 4: Bit1~Bit0 => Y coordinate (xpos[1:0]) + Bit3~Bit2 => X coordinate (ypos[1:0]) + Bit5~Bit4 => y1_g + Bit7~Bit6 => x1_g + +Packet 2 (ABSOLUTE POSITION) + Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +BYTE |---------------|BYTE |---------------|BYTE|---------------|BYTE|---------------| + 1 |0|1|V|A|1|R|1|0| 2 |X|X|X|X|X|X|X|X| 3 |Y|Y|Y|Y|Y|Y|Y|Y| 4 |x|x|y|y|X|X|Y|Y| + |---------------| |---------------| |---------------| |---------------| + +Byte 1: Bit7~Bit6 => 00, Normal data packet + => 01, Absolute coordinates packet + => 10, Notify packet + Bit5 => Valid bit, 0 means that the coordinate is invalid or finger up. + When both fingers are up, the last two reports have zero valid + bit. + Bit4 => arc + Bit3 => 1 + Bit2 => Right Button, 1 is pressed, 0 is released. + Bit1 => 1 + Bit0 => 0 +Byte 2: X coordinate (xpos[9:2]) +Byte 3: Y coordinate (ypos[9:2]) +Byte 4: Bit1~Bit0 => Y coordinate (xpos[1:0]) + Bit3~Bit2 => X coordinate (ypos[1:0]) + Bit5~Bit4 => y2_g + Bit7~Bit6 => x2_g + +Notify Packet for STL3888-A0 + Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +BYTE |---------------|BYTE |---------------|BYTE|---------------|BYTE|---------------| + 1 |1|0|1|P|1|M|R|L| 2 |C|C|C|C|C|C|C|C| 3 |0|0|F|F|0|0|0|i| 4 |r|l|d|u|0|0|0|0| + |---------------| |---------------| |---------------| |---------------| + +Byte 1: Bit7~Bit6 => 00, Normal data packet + => 01, Absolute coordination packet + => 10, Notify packet + Bit5 => 1 + Bit4 => when in absolute coordinates mode (valid when EN_PKT_GO is 1): + 0: left button is generated by the on-pad command + 1: left button is generated by the external button + Bit3 => 1 + Bit2 => Middle Button, 1 is pressed, 0 is not pressed. + Bit1 => Right Button, 1 is pressed, 0 is not pressed. + Bit0 => Left Button, 1 is pressed, 0 is not pressed. +Byte 2: Message Type => 0xB7 (Multi Finger, Multi Coordinate mode) +Byte 3: Bit7~Bit6 => Don't care + Bit5~Bit4 => Number of fingers + Bit3~Bit1 => Reserved + Bit0 => 1: enter gesture mode; 0: leaving gesture mode +Byte 4: Bit7 => scroll right button + Bit6 => scroll left button + Bit5 => scroll down button + Bit4 => scroll up button + * Note that if gesture and additional button (Bit4~Bit7) + happen at the same time, the button information will not + be sent. + Bit3~Bit0 => Reserved + +Sample sequence of Multi-finger, Multi-coordinate mode: + + notify packet (valid bit == 1), abs pkt 1, abs pkt 2, abs pkt 1, + abs pkt 2, ..., notify packet(valid bit == 0) + +============================================================================== +* FSP Enable/Disable packet +============================================================================== + Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +BYTE |---------------|BYTE |---------------|BYTE|---------------|BYTE|---------------| + 1 |Y|X|0|0|1|M|R|L| 2 |0|1|0|1|1|0|1|E| 3 | | | | | | | | | 4 | | | | | | | | | + |---------------| |---------------| |---------------| |---------------| + +FSP will send out enable/disable packet when FSP receive PS/2 enable/disable +command. Host will receive the packet which Middle, Right, Left button will +be set. The packet only use byte 0 and byte 1 as a pattern of original packet. +Ignore the other bytes of the packet. + +Byte 1: Bit7 => 0, Y overflow + Bit6 => 0, X overflow + Bit5 => 0, Y sign bit + Bit4 => 0, X sign bit + Bit3 => 1 + Bit2 => 1, Middle Button + Bit1 => 1, Right Button + Bit0 => 1, Left Button +Byte 2: Bit7~1 => (0101101b) + Bit0 => 1 = Enable + 0 = Disable +Byte 3: Don't care +Byte 4: Don't care (MOUSE ID 3, 4) +Byte 5~8: Don't care (Absolute packet) + +============================================================================== +* PS/2 Command Set +============================================================================== + +FSP supports basic PS/2 commanding set and modes, refer to following URL for +details about PS/2 commands: + +http://www.computer-engineering.org/index.php?title=PS/2_Mouse_Interface + +============================================================================== +* Programming Sequence for Determining Packet Parsing Flow +============================================================================== +1. Identify FSP by reading device ID(0x00) and version(0x01) register + +2. Determine number of buttons by reading status2 (0x0b) register + + buttons = reg[0x0b] & 0x30 + + if buttons == 0x30 or buttons == 0x20: + # two/four buttons + Refer to 'Finger Sensing Pad PS/2 Mouse Intellimouse' + section A for packet parsing detail(ignore byte 4, bit ~ 7) + elif buttons == 0x10: + # 6 buttons + Refer to 'Finger Sensing Pad PS/2 Mouse Intellimouse' + section B for packet parsing detail + elif buttons == 0x00: + # 6 buttons + Refer to 'Finger Sensing Pad PS/2 Mouse Intellimouse' + section A for packet parsing detail + +============================================================================== +* Programming Sequence for Register Reading/Writing +============================================================================== + +Register inversion requirement: + + Following values needed to be inverted(the '~' operator in C) before being +sent to FSP: + + 0xe9, 0xee, 0xf2 and 0xff. + +Register swapping requirement: + + Following values needed to have their higher 4 bits and lower 4 bits being +swapped before being sent to FSP: + + 10, 20, 40, 60, 80, 100 and 200. + +Register reading sequence: + + 1. send 0xf3 PS/2 command to FSP; + + 2. send 0x66 PS/2 command to FSP; + + 3. send 0x88 PS/2 command to FSP; + + 4. send 0xf3 PS/2 command to FSP; + + 5. if the register address being to read is not required to be + inverted(refer to the 'Register inversion requirement' section), + goto step 6 + + 5a. send 0x68 PS/2 command to FSP; + + 5b. send the inverted register address to FSP and goto step 8; + + 6. if the register address being to read is not required to be + swapped(refer to the 'Register swapping requirement' section), + goto step 7 + + 6a. send 0xcc PS/2 command to FSP; + + 6b. send the swapped register address to FSP and goto step 8; + + 7. send 0x66 PS/2 command to FSP; + + 7a. send the original register address to FSP and goto step 8; + + 8. send 0xe9(status request) PS/2 command to FSP; + + 9. the response read from FSP should be the requested register value. + +Register writing sequence: + + 1. send 0xf3 PS/2 command to FSP; + + 2. if the register address being to write is not required to be + inverted(refer to the 'Register inversion requirement' section), + goto step 3 + + 2a. send 0x74 PS/2 command to FSP; + + 2b. send the inverted register address to FSP and goto step 5; + + 3. if the register address being to write is not required to be + swapped(refer to the 'Register swapping requirement' section), + goto step 4 + + 3a. send 0x77 PS/2 command to FSP; + + 3b. send the swapped register address to FSP and goto step 5; + + 4. send 0x55 PS/2 command to FSP; + + 4a. send the register address to FSP and goto step 5; + + 5. send 0xf3 PS/2 command to FSP; + + 6. if the register value being to write is not required to be + inverted(refer to the 'Register inversion requirement' section), + goto step 7 + + 6a. send 0x47 PS/2 command to FSP; + + 6b. send the inverted register value to FSP and goto step 9; + + 7. if the register value being to write is not required to be + swapped(refer to the 'Register swapping requirement' section), + goto step 8 + + 7a. send 0x44 PS/2 command to FSP; + + 7b. send the swapped register value to FSP and goto step 9; + + 8. send 0x33 PS/2 command to FSP; + + 8a. send the register value to FSP; + + 9. the register writing sequence is completed. + +============================================================================== +* Register Listing +============================================================================== + +offset width default r/w name +0x00 bit7~bit0 0x01 RO device ID + +0x01 bit7~bit0 0xc0 RW version ID + +0x02 bit7~bit0 0x01 RO vendor ID + +0x03 bit7~bit0 0x01 RO product ID + +0x04 bit3~bit0 0x01 RW revision ID + +0x0b RO test mode status 1 + bit3 1 RO 0: rotate 180 degree, 1: no rotation + + bit5~bit4 RO number of buttons + 11 => 2, lbtn/rbtn + 10 => 4, lbtn/rbtn/scru/scrd + 01 => 6, lbtn/rbtn/scru/scrd/scrl/scrr + 00 => 6, lbtn/rbtn/scru/scrd/fbtn/bbtn + +0x0f RW register file page control + bit0 0 RW 1 to enable page 1 register files + +0x10 RW system control 1 + bit0 1 RW Reserved, must be 1 + bit1 0 RW Reserved, must be 0 + bit4 1 RW Reserved, must be 0 + bit5 0 RW register clock gating enable + 0: read only, 1: read/write enable + (Note that following registers does not require clock gating being + enabled prior to write: 05 06 07 08 09 0c 0f 10 11 12 16 17 18 23 2e + 40 41 42 43.) + +0x31 RW on-pad command detection + bit7 0 RW on-pad command left button down tag + enable + 0: disable, 1: enable + +0x34 RW on-pad command control 5 + bit4~bit0 0x05 RW XLO in 0s/4/1, so 03h = 0010.1b = 2.5 + (Note that position unit is in 0.5 scanline) + + bit7 0 RW on-pad tap zone enable + 0: disable, 1: enable + +0x35 RW on-pad command control 6 + bit4~bit0 0x1d RW XHI in 0s/4/1, so 19h = 1100.1b = 12.5 + (Note that position unit is in 0.5 scanline) + +0x36 RW on-pad command control 7 + bit4~bit0 0x04 RW YLO in 0s/4/1, so 03h = 0010.1b = 2.5 + (Note that position unit is in 0.5 scanline) + +0x37 RW on-pad command control 8 + bit4~bit0 0x13 RW YHI in 0s/4/1, so 11h = 1000.1b = 8.5 + (Note that position unit is in 0.5 scanline) + +0x40 RW system control 5 + bit1 0 RW FSP Intellimouse mode enable + 0: disable, 1: enable + + bit2 0 RW movement + abs. coordinate mode enable + 0: disable, 1: enable + (Note that this function has the functionality of bit 1 even when + bit 1 is not set. However, the format is different from that of bit 1. + In addition, when bit 1 and bit 2 are set at the same time, bit 2 will + override bit 1.) + + bit3 0 RW abs. coordinate only mode enable + 0: disable, 1: enable + (Note that this function has the functionality of bit 1 even when + bit 1 is not set. However, the format is different from that of bit 1. + In addition, when bit 1, bit 2 and bit 3 are set at the same time, + bit 3 will override bit 1 and 2.) + + bit5 0 RW auto switch enable + 0: disable, 1: enable + + bit6 0 RW G0 abs. + notify packet format enable + 0: disable, 1: enable + (Note that the absolute/relative coordinate output still depends on + bit 2 and 3. That is, if any of those bit is 1, host will receive + absolute coordinates; otherwise, host only receives packets with + relative coordinate.) + +0x43 RW on-pad control + bit0 0 RW on-pad control enable + 0: disable, 1: enable + (Note that if this bit is cleared, bit 3/5 will be ineffective) + + bit3 0 RW on-pad fix vertical scrolling enable + 0: disable, 1: enable + + bit5 0 RW on-pad fix horizontal scrolling enable + 0: disable, 1: enable diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig index 90bef5d498f..3feeb3af8ab 100644 --- a/drivers/input/mouse/Kconfig +++ b/drivers/input/mouse/Kconfig @@ -107,6 +107,14 @@ config MOUSE_PS2_ELANTECH entries. For further information, see <file:Documentation/input/elantech.txt>. +config MOUSE_PS2_SENTELIC + bool "Sentelic Finger Sensing Pad PS/2 protocol extension" + depends on MOUSE_PS2 + help + Say Y here if you have a laptop (such as MSI WIND Netbook) + with Sentelic Finger Sensing Pad touchpad. + + If unsure, say N. config MOUSE_PS2_TOUCHKIT bool "eGalax TouchKit PS/2 protocol extension" diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile index ea58c9a372b..570c84a4a65 100644 --- a/drivers/input/mouse/Makefile +++ b/drivers/input/mouse/Makefile @@ -27,5 +27,6 @@ psmouse-$(CONFIG_MOUSE_PS2_ELANTECH) += elantech.o psmouse-$(CONFIG_MOUSE_PS2_OLPC) += hgpk.o psmouse-$(CONFIG_MOUSE_PS2_LOGIPS2PP) += logips2pp.o psmouse-$(CONFIG_MOUSE_PS2_LIFEBOOK) += lifebook.o +psmouse-$(CONFIG_MOUSE_PS2_SENTELIC) += sentelic.o psmouse-$(CONFIG_MOUSE_PS2_TRACKPOINT) += trackpoint.o psmouse-$(CONFIG_MOUSE_PS2_TOUCHKIT) += touchkit_ps2.o diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index b407b355dce..df318887ca0 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -30,6 +30,7 @@ #include "trackpoint.h" #include "touchkit_ps2.h" #include "elantech.h" +#include "sentelic.h" #define DRIVER_DESC "PS/2 mouse driver" @@ -666,6 +667,20 @@ static int psmouse_extensions(struct psmouse *psmouse, max_proto = PSMOUSE_IMEX; } +/* + * Try Finger Sensing Pad + */ + if (max_proto > PSMOUSE_IMEX) { + if (fsp_detect(psmouse, set_properties) == 0) { + if (!set_properties || fsp_init(psmouse) == 0) + return PSMOUSE_FSP; +/* + * Init failed, try basic relative protocols + */ + max_proto = PSMOUSE_IMEX; + } + } + if (max_proto > PSMOUSE_IMEX) { if (genius_detect(psmouse, set_properties) == 0) return PSMOUSE_GENPS; @@ -813,7 +828,16 @@ static const struct psmouse_protocol psmouse_protocols[] = { .detect = elantech_detect, .init = elantech_init, }, - #endif +#endif +#ifdef CONFIG_MOUSE_PS2_SENTELIC + { + .type = PSMOUSE_FSP, + .name = "FSPPS/2", + .alias = "fsp", + .detect = fsp_detect, + .init = fsp_init, + }, +#endif { .type = PSMOUSE_CORTRON, .name = "CortronPS/2", diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h index e3562daeb2e..cca1744c2a0 100644 --- a/drivers/input/mouse/psmouse.h +++ b/drivers/input/mouse/psmouse.h @@ -91,6 +91,7 @@ enum psmouse_type { PSMOUSE_CORTRON, PSMOUSE_HGPK, PSMOUSE_ELANTECH, + PSMOUSE_FSP, PSMOUSE_AUTO /* This one should always be last */ }; diff --git a/drivers/input/mouse/sentelic.c b/drivers/input/mouse/sentelic.c new file mode 100644 index 00000000000..97b1e72855a --- /dev/null +++ b/drivers/input/mouse/sentelic.c @@ -0,0 +1,867 @@ +/*- + * Finger Sensing Pad PS/2 mouse driver. + * + * Copyright (C) 2005-2007 Asia Vital Components Co., Ltd. + * Copyright (C) 2005-2009 Tai-hwa Liang, Sentelic Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/version.h> +#include <linux/input.h> +#include <linux/ctype.h> +#include <linux/libps2.h> +#include <linux/serio.h> +#include <linux/jiffies.h> + +#include "psmouse.h" +#include "sentelic.h" + +/* + * Timeout for FSP PS/2 command only (in milliseconds). + */ +#define FSP_CMD_TIMEOUT 200 +#define FSP_CMD_TIMEOUT2 30 + +/** Driver version. */ +static const char fsp_drv_ver[] = "1.0.0-K"; + +/* + * Make sure that the value being sent to FSP will not conflict with + * possible sample rate values. + */ +static unsigned char fsp_test_swap_cmd(unsigned char reg_val) +{ + switch (reg_val) { + case 10: case 20: case 40: case 60: case 80: case 100: case 200: + /* + * The requested value being sent to FSP matched to possible + * sample rates, swap the given value such that the hardware + * wouldn't get confused. + */ + return (reg_val >> 4) | (reg_val << 4); + default: + return reg_val; /* swap isn't necessary */ + } +} + +/* + * Make sure that the value being sent to FSP will not conflict with certain + * commands. + */ +static unsigned char fsp_test_invert_cmd(unsigned char reg_val) +{ + switch (reg_val) { + case 0xe9: case 0xee: case 0xf2: case 0xff: + /* + * The requested value being sent to FSP matched to certain + * commands, inverse the given value such that the hardware + * wouldn't get confused. + */ + return ~reg_val; + default: + return reg_val; /* inversion isn't necessary */ + } +} + +static int fsp_reg_read(struct psmouse *psmouse, int reg_addr, int *reg_val) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + unsigned char param[3]; + unsigned char addr; + int rc = -1; + + /* + * We need to shut off the device and switch it into command + * mode so we don't confuse our protocol handler. We don't need + * to do that for writes because sysfs set helper does this for + * us. + */ + ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE); + psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); + mutex_lock(&ps2dev->cmd_mutex); + + if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0) + goto out; + + /* should return 0xfe(request for resending) */ + ps2_sendbyte(ps2dev, 0x66, FSP_CMD_TIMEOUT2); + /* should return 0xfc(failed) */ + ps2_sendbyte(ps2dev, 0x88, FSP_CMD_TIMEOUT2); + + if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0) + goto out; + + if ((addr = fsp_test_invert_cmd(reg_addr)) != reg_addr) { + ps2_sendbyte(ps2dev, 0x68, FSP_CMD_TIMEOUT2); + } else if ((addr = fsp_test_swap_cmd(reg_addr)) != reg_addr) { + /* swapping is required */ + ps2_sendbyte(ps2dev, 0xcc, FSP_CMD_TIMEOUT2); + /* expect 0xfe */ + } else { + /* swapping isn't necessary */ + ps2_sendbyte(ps2dev, 0x66, FSP_CMD_TIMEOUT2); + /* expect 0xfe */ + } + /* should return 0xfc(failed) */ + ps2_sendbyte(ps2dev, addr, FSP_CMD_TIMEOUT); + + if (__ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO) < 0) + goto out; + + *reg_val = param[2]; + rc = 0; + + out: + mutex_unlock(&ps2dev->cmd_mutex); + ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE); + psmouse_set_state(psmouse, PSMOUSE_ACTIVATED); + dev_dbg(&ps2dev->serio->dev, "READ REG: 0x%02x is 0x%02x (rc = %d)\n", + reg_addr, *reg_val, rc); + return rc; +} + +static int fsp_reg_write(struct psmouse *psmouse, int reg_addr, int reg_val) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + unsigned char v; + int rc = -1; + + mutex_lock(&ps2dev->cmd_mutex); + + if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0) + goto out; + + if ((v = fsp_test_invert_cmd(reg_addr)) != reg_addr) { + /* inversion is required */ + ps2_sendbyte(ps2dev, 0x74, FSP_CMD_TIMEOUT2); + } else { + if ((v = fsp_test_swap_cmd(reg_addr)) != reg_addr) { + /* swapping is required */ + ps2_sendbyte(ps2dev, 0x77, FSP_CMD_TIMEOUT2); + } else { + /* swapping isn't necessary */ + ps2_sendbyte(ps2dev, 0x55, FSP_CMD_TIMEOUT2); + } + } + /* write the register address in correct order */ + ps2_sendbyte(ps2dev, v, FSP_CMD_TIMEOUT2); + + if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0) + return -1; + + if ((v = fsp_test_invert_cmd(reg_val)) != reg_val) { + /* inversion is required */ + ps2_sendbyte(ps2dev, 0x47, FSP_CMD_TIMEOUT2); + } else if ((v = fsp_test_swap_cmd(reg_val)) != reg_val) { + /* swapping is required */ + ps2_sendbyte(ps2dev, 0x44, FSP_CMD_TIMEOUT2); + } else { + /* swapping isn't necessary */ + ps2_sendbyte(ps2dev, 0x33, FSP_CMD_TIMEOUT2); + } + + /* write the register value in correct order */ + ps2_sendbyte(ps2dev, v, FSP_CMD_TIMEOUT2); + rc = 0; + + out: + mutex_unlock(&ps2dev->cmd_mutex); + dev_dbg(&ps2dev->serio->dev, "WRITE REG: 0x%02x to 0x%02x (rc = %d)\n", + reg_addr, reg_val, rc); + return rc; +} + +/* Enable register clock gating for writing certain registers */ +static int fsp_reg_write_enable(struct psmouse *psmouse, bool enable) +{ + int v, nv; + + if (fsp_reg_read(psmouse, FSP_REG_SYSCTL1, &v) == -1) + return -1; + + if (enable) + nv = v | FSP_BIT_EN_REG_CLK; + else + nv = v & ~FSP_BIT_EN_REG_CLK; + + /* only write if necessary */ + if (nv != v) + if (fsp_reg_write(psmouse, FSP_REG_SYSCTL1, nv) == -1) + return -1; + + return 0; +} + +static int fsp_page_reg_read(struct psmouse *psmouse, int *reg_val) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + unsigned char param[3]; + int rc = -1; + + ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE); + psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); + mutex_lock(&ps2dev->cmd_mutex); + + if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0) + goto out; + + ps2_sendbyte(ps2dev, 0x66, FSP_CMD_TIMEOUT2); + ps2_sendbyte(ps2dev, 0x88, FSP_CMD_TIMEOUT2); + + if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0) + goto out; + + ps2_sendbyte(ps2dev, 0x83, FSP_CMD_TIMEOUT2); + ps2_sendbyte(ps2dev, 0x88, FSP_CMD_TIMEOUT2); + + /* get the returned result */ + if (__ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) + goto out; + + *reg_val = param[2]; + rc = 0; + + out: + mutex_unlock(&ps2dev->cmd_mutex); + ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE); + psmouse_set_state(psmouse, PSMOUSE_ACTIVATED); + dev_dbg(&ps2dev->serio->dev, "READ PAGE REG: 0x%02x (rc = %d)\n", + *reg_val, rc); + return rc; +} + +static int fsp_page_reg_write(struct psmouse *psmouse, int reg_val) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + unsigned char v; + int rc = -1; + + mutex_lock(&ps2dev->cmd_mutex); + + if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0) + goto out; + + ps2_sendbyte(ps2dev, 0x38, FSP_CMD_TIMEOUT2); + ps2_sendbyte(ps2dev, 0x88, FSP_CMD_TIMEOUT2); + + if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0) + return -1; + + if ((v = fsp_test_invert_cmd(reg_val)) != reg_val) { + ps2_sendbyte(ps2dev, 0x47, FSP_CMD_TIMEOUT2); + } else if ((v = fsp_test_swap_cmd(reg_val)) != reg_val) { + /* swapping is required */ + ps2_sendbyte(ps2dev, 0x44, FSP_CMD_TIMEOUT2); + } else { + /* swapping isn't necessary */ + ps2_sendbyte(ps2dev, 0x33, FSP_CMD_TIMEOUT2); + } + + ps2_sendbyte(ps2dev, v, FSP_CMD_TIMEOUT2); + rc = 0; + + out: + mutex_unlock(&ps2dev->cmd_mutex); + dev_dbg(&ps2dev->serio->dev, "WRITE PAGE REG: to 0x%02x (rc = %d)\n", + reg_val, rc); + return rc; +} + +static int fsp_get_version(struct psmouse *psmouse, int *version) +{ + if (fsp_reg_read(psmouse, FSP_REG_VERSION, version)) + return -EIO; + + return 0; +} + +static int fsp_get_revision(struct psmouse *psmouse, int *rev) +{ + if (fsp_reg_read(psmouse, FSP_REG_REVISION, rev)) + return -EIO; + + return 0; +} + +static int fsp_get_buttons(struct psmouse *psmouse, int *btn) +{ + static const int buttons[] = { + 0x16, /* Left/Middle/Right/Forward/Backward & Scroll Up/Down */ + 0x06, /* Left/Middle/Right & Scroll Up/Down/Right/Left */ + 0x04, /* Left/Middle/Right & Scroll Up/Down */ + 0x02, /* Left/Middle/Right */ + }; + int val; + + if (fsp_reg_read(psmouse, FSP_REG_TMOD_STATUS1, &val) == -1) + return -EIO; + + *btn = buttons[(val & 0x30) >> 4]; + return 0; +} + +/* Enable on-pad command tag output */ +static int fsp_opc_tag_enable(struct psmouse *psmouse, bool enable) +{ + int v, nv; + int res = 0; + + if (fsp_reg_read(psmouse, FSP_REG_OPC_QDOWN, &v) == -1) { + dev_err(&psmouse->ps2dev.serio->dev, "Unable get OPC state.\n"); + return -EIO; + } + + if (enable) + nv = v | FSP_BIT_EN_OPC_TAG; + else + nv = v & ~FSP_BIT_EN_OPC_TAG; + + /* only write if necessary */ + if (nv != v) { + fsp_reg_write_enable(psmouse, true); + res = fsp_reg_write(psmouse, FSP_REG_OPC_QDOWN, nv); + fsp_reg_write_enable(psmouse, false); + } + + if (res != 0) { + dev_err(&psmouse->ps2dev.serio->dev, + "Unable to enable OPC tag.\n"); + res = -EIO; + } + + return res; +} + +static int fsp_onpad_vscr(struct psmouse *psmouse, bool enable) +{ + struct fsp_data *pad = psmouse->private; + int val; + + if (fsp_reg_read(psmouse, FSP_REG_ONPAD_CTL, &val)) + return -EIO; + + pad->vscroll = enable; + + if (enable) + val |= (FSP_BIT_FIX_VSCR | FSP_BIT_ONPAD_ENABLE); + else + val &= ~FSP_BIT_FIX_VSCR; + + if (fsp_reg_write(psmouse, FSP_REG_ONPAD_CTL, val)) + return -EIO; + + return 0; +} + +static int fsp_onpad_hscr(struct psmouse *psmouse, bool enable) +{ + struct fsp_data *pad = psmouse->private; + int val, v2; + + if (fsp_reg_read(psmouse, FSP_REG_ONPAD_CTL, &val)) + return -EIO; + + if (fsp_reg_read(psmouse, FSP_REG_SYSCTL5, &v2)) + return -EIO; + + pad->hscroll = enable; + + if (enable) { + val |= (FSP_BIT_FIX_HSCR | FSP_BIT_ONPAD_ENABLE); + v2 |= FSP_BIT_EN_MSID6; + } else { + val &= ~FSP_BIT_FIX_HSCR; + v2 &= ~(FSP_BIT_EN_MSID6 | FSP_BIT_EN_MSID7 | FSP_BIT_EN_MSID8); + } + + if (fsp_reg_write(psmouse, FSP_REG_ONPAD_CTL, val)) + return -EIO; + + /* reconfigure horizontal scrolling packet output */ + if (fsp_reg_write(psmouse, FSP_REG_SYSCTL5, v2)) + return -EIO; + + return 0; +} + +/* + * Write device specific initial parameters. + * + * ex: 0xab 0xcd - write oxcd into register 0xab + */ +static ssize_t fsp_attr_set_setreg(struct psmouse *psmouse, void *data, + const char *buf, size_t count) +{ + unsigned long reg, val; + char *rest; + ssize_t retval; + + reg = simple_strtoul(buf, &rest, 16); + if (rest == buf || *rest != ' ' || reg > 0xf |