diff options
118 files changed, 3026 insertions, 1153 deletions
diff --git a/Documentation/devicetree/bindings/input/elan_i2c.txt b/Documentation/devicetree/bindings/input/elan_i2c.txt index ee3242c4ba67..d80a83583238 100644 --- a/Documentation/devicetree/bindings/input/elan_i2c.txt +++ b/Documentation/devicetree/bindings/input/elan_i2c.txt @@ -14,6 +14,7 @@ Optional properties: - pinctrl-0: a phandle pointing to the pin settings for the device (see pinctrl binding [1]). - vcc-supply: a phandle for the regulator supplying 3.3V power. +- elan,trackpoint: touchpad can support a trackpoint (boolean) [0]: Documentation/devicetree/bindings/interrupt-controller/interrupts.txt [1]: Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt diff --git a/Documentation/devicetree/bindings/input/keys.txt b/Documentation/devicetree/bindings/input/keys.txt new file mode 100644 index 000000000000..f5a5ddde53f1 --- /dev/null +++ b/Documentation/devicetree/bindings/input/keys.txt @@ -0,0 +1,8 @@ +General Keys Properties: + +Optional properties for Keys: +- power-off-time-sec: Duration in seconds which the key should be kept + pressed for device to power off automatically. Device with key pressed + shutdown feature can specify this property. +- linux,keycodes: Specifies the numeric keycode values to be used for + reporting key presses. diff --git a/Documentation/devicetree/bindings/input/qcom,pm8941-pwrkey.txt b/Documentation/devicetree/bindings/input/qcom,pm8941-pwrkey.txt index 07bf55f6e0b9..34ab5763f494 100644 --- a/Documentation/devicetree/bindings/input/qcom,pm8941-pwrkey.txt +++ b/Documentation/devicetree/bindings/input/qcom,pm8941-pwrkey.txt @@ -7,6 +7,7 @@ PROPERTIES Value type: <string> Definition: must be one of: "qcom,pm8941-pwrkey" + "qcom,pm8941-resin" - reg: Usage: required @@ -32,6 +33,14 @@ PROPERTIES Definition: presence of this property indicates that the KPDPWR_N pin should be configured for pull up. +- linux,code: + Usage: optional + Value type: <u32> + Definition: The input key-code associated with the power key. + Use the linux event codes defined in + include/dt-bindings/input/linux-event-codes.h + When property is omitted KEY_POWER is assumed. + EXAMPLE pwrkey@800 { @@ -40,4 +49,5 @@ EXAMPLE interrupts = <0x0 0x8 0 IRQ_TYPE_EDGE_BOTH>; debounce = <15625>; bias-pull-up; + linux,code = <KEY_POWER>; }; diff --git a/Documentation/devicetree/bindings/input/sprd,sc27xx-vibra.txt b/Documentation/devicetree/bindings/input/sprd,sc27xx-vibra.txt new file mode 100644 index 000000000000..f2ec0d4f2dff --- /dev/null +++ b/Documentation/devicetree/bindings/input/sprd,sc27xx-vibra.txt @@ -0,0 +1,23 @@ +Spreadtrum SC27xx PMIC Vibrator + +Required properties: +- compatible: should be "sprd,sc2731-vibrator". +- reg: address of vibrator control register. + +Example : + + sc2731_pmic: pmic@0 { + compatible = "sprd,sc2731"; + reg = <0>; + spi-max-frequency = <26000000>; + interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>; + interrupt-controller; + #interrupt-cells = <2>; + #address-cells = <1>; + #size-cells = <0>; + + vibrator@eb4 { + compatible = "sprd,sc2731-vibrator"; + reg = <0xeb4>; + }; + }; diff --git a/Documentation/devicetree/bindings/input/touchscreen/bu21029.txt b/Documentation/devicetree/bindings/input/touchscreen/bu21029.txt new file mode 100644 index 000000000000..8daa0e868a8f --- /dev/null +++ b/Documentation/devicetree/bindings/input/touchscreen/bu21029.txt @@ -0,0 +1,35 @@ +* Rohm BU21029 Touch Screen Controller + +Required properties: + - compatible : must be "rohm,bu21029" + - reg : i2c device address of the chip (0x40 or 0x41) + - interrupt-parent : the phandle for the gpio controller + - interrupts : (gpio) interrupt to which the chip is connected + - rohm,x-plate-ohms : x-plate resistance in Ohm + +Optional properties: + - reset-gpios : gpio pin to reset the chip (active low) + - touchscreen-size-x : horizontal resolution of touchscreen (in pixels) + - touchscreen-size-y : vertical resolution of touchscreen (in pixels) + - touchscreen-max-pressure: maximum pressure value + - vdd-supply : power supply for the controller + +Example: + + &i2c1 { + /* ... */ + + bu21029: bu21029@40 { + compatible = "rohm,bu21029"; + reg = <0x40>; + interrupt-parent = <&gpio1>; + interrupts = <4 IRQ_TYPE_EDGE_FALLING>; + reset-gpios = <&gpio6 16 GPIO_ACTIVE_LOW>; + rohm,x-plate-ohms = <600>; + touchscreen-size-x = <800>; + touchscreen-size-y = <480>; + touchscreen-max-pressure = <4095>; + }; + + /* ... */ + }; diff --git a/Documentation/devicetree/bindings/input/touchscreen/eeti.txt b/Documentation/devicetree/bindings/input/touchscreen/eeti.txt new file mode 100644 index 000000000000..32b3712c916e --- /dev/null +++ b/Documentation/devicetree/bindings/input/touchscreen/eeti.txt @@ -0,0 +1,30 @@ +Bindings for EETI touchscreen controller + +Required properties: +- compatible: should be "eeti,exc3000-i2c" +- reg: I2C address of the chip. Should be set to <0xa> +- interrupts: interrupt to which the chip is connected + +Optional properties: +- attn-gpios: A handle to a GPIO to check whether interrupt is still + latched. This is necessary for platforms that lack + support for level-triggered IRQs. + +The following optional properties described in touchscreen.txt are +also supported: + +- touchscreen-inverted-x +- touchscreen-inverted-y +- touchscreen-swapped-x-y + +Example: + +i2c-master { + touchscreen@a { + compatible = "eeti,exc3000-i2c"; + reg = <0xa>; + interrupt-parent = <&gpio>; + interrupts = <123 IRQ_TYPE_EDGE_RISING>; + attn-gpios = <&gpio 123 GPIO_ACTIVE_HIGH>; + }; +}; diff --git a/MAINTAINERS b/MAINTAINERS index 9c125f705f78..bdf4783ee842 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3431,6 +3431,12 @@ S: Maintained F: Documentation/devicetree/bindings/input/touchscreen/chipone_icn8318.txt F: drivers/input/touchscreen/chipone_icn8318.c +CHIPONE ICN8505 I2C TOUCHSCREEN DRIVER +M: Hans de Goede <hdegoede@redhat.com> +L: linux-input@vger.kernel.org +S: Maintained +F: drivers/input/touchscreen/chipone_icn8505.c + CHROME HARDWARE PLATFORM SUPPORT M: Benson Leung <bleung@chromium.org> M: Olof Johansson <olof@lixom.net> diff --git a/drivers/input/evbug.c b/drivers/input/evbug.c index cd4e6679d61a..5419c1c1f621 100644 --- a/drivers/input/evbug.c +++ b/drivers/input/evbug.c @@ -20,10 +20,6 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: - * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt diff --git a/drivers/input/gameport/emu10k1-gp.c b/drivers/input/gameport/emu10k1-gp.c index 2909e9561cf3..afdc20ca0e24 100644 --- a/drivers/input/gameport/emu10k1-gp.c +++ b/drivers/input/gameport/emu10k1-gp.c @@ -20,10 +20,6 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: - * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include <asm/io.h> diff --git a/drivers/input/gameport/lightning.c b/drivers/input/gameport/lightning.c index 85d6ee09f11f..c6e74c7945cb 100644 --- a/drivers/input/gameport/lightning.c +++ b/drivers/input/gameport/lightning.c @@ -20,10 +20,6 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: - * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include <asm/io.h> diff --git a/drivers/input/gameport/ns558.c b/drivers/input/gameport/ns558.c index 7c217848613e..6437645858f9 100644 --- a/drivers/input/gameport/ns558.c +++ b/drivers/input/gameport/ns558.c @@ -21,10 +21,6 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: - * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include <asm/io.h> diff --git a/drivers/input/input-mt.c b/drivers/input/input-mt.c index a1bbec9cda8d..4a69716e5461 100644 --- a/drivers/input/input-mt.c +++ b/drivers/input/input-mt.c @@ -131,8 +131,10 @@ EXPORT_SYMBOL(input_mt_destroy_slots); * inactive, or if the tool type is changed, a new tracking id is * assigned to the slot. The tool type is only reported if the * corresponding absbit field is set. + * + * Returns true if contact is active. */ -void input_mt_report_slot_state(struct input_dev *dev, +bool input_mt_report_slot_state(struct input_dev *dev, unsigned int tool_type, bool active) { struct input_mt *mt = dev->mt; @@ -140,22 +142,24 @@ void input_mt_report_slot_state(struct input_dev *dev, int id; if (!mt) - return; + return false; slot = &mt->slots[mt->slot]; slot->frame = mt->frame; if (!active) { input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1); - return; + return false; } id = input_mt_get_value(slot, ABS_MT_TRACKING_ID); - if (id < 0 || input_mt_get_value(slot, ABS_MT_TOOL_TYPE) != tool_type) + if (id < 0) id = input_mt_new_trkid(mt); input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, id); input_event(dev, EV_ABS, ABS_MT_TOOL_TYPE, tool_type); + + return true; } EXPORT_SYMBOL(input_mt_report_slot_state); diff --git a/drivers/input/input.c b/drivers/input/input.c index 9785546420a7..6365c1958264 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -1943,8 +1943,7 @@ void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int break; default: - pr_err("input_set_capability: unknown type %u (code %u)\n", - type, code); + pr_err("%s: unknown type %u (code %u)\n", __func__, type, code); dump_stack(); return; } diff --git a/drivers/input/joystick/a3d.c b/drivers/input/joystick/a3d.c index 55efdfc7eb62..98307039a534 100644 --- a/drivers/input/joystick/a3d.c +++ b/drivers/input/joystick/a3d.c @@ -20,10 +20,6 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: - * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include <linux/kernel.h> diff --git a/drivers/input/joystick/adi.c b/drivers/input/joystick/adi.c index 15a71acb6997..f466c0d34247 100644 --- a/drivers/input/joystick/adi.c +++ b/drivers/input/joystick/adi.c @@ -20,10 +20,6 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: - * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include <linux/delay.h> diff --git a/drivers/input/joystick/amijoy.c b/drivers/input/joystick/amijoy.c index c65b5fa69f1e..2b82a838c511 100644 --- a/drivers/input/joystick/amijoy.c +++ b/drivers/input/joystick/amijoy.c @@ -20,10 +20,6 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: - * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include <linux/types.h> diff --git a/drivers/input/joystick/analog.c b/drivers/input/joystick/analog.c index c79dbcb4d146..2b445c8d3fcd 100644 --- a/drivers/input/joystick/analog.c +++ b/drivers/input/joystick/analog.c @@ -20,10 +20,6 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: - * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include <linux/delay.h> diff --git a/drivers/input/joystick/as5011.c b/drivers/input/joystick/as5011.c index 005d852a06e9..f051993c568e 100644 --- a/drivers/input/joystick/as5011.c +++ b/drivers/input/joystick/as5011.c @@ -269,9 +269,7 @@ static int as5011_probe(struct i2c_client *client, input_dev->id.bustype = BUS_I2C; input_dev->dev.parent = &client->dev; - __set_bit(EV_KEY, input_dev->evbit); - __set_bit(EV_ABS, input_dev->evbit); - __set_bit(BTN_JOYSTICK, input_dev->keybit); + input_set_capability(input_dev, EV_KEY, BTN_JOYSTICK); input_set_abs_params(input_dev, ABS_X, AS5011_MIN_AXIS, AS5011_MAX_AXIS, AS5011_FUZZ, AS5011_FLAT); diff --git a/drivers/input/joystick/cobra.c b/drivers/input/joystick/cobra.c index ae3ee24a2368..14cb956beac4 100644 --- a/drivers/input/joystick/cobra.c +++ b/drivers/input/joystick/cobra.c @@ -20,10 +20,6 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: - * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include <linux/kernel.h> diff --git a/drivers/input/joystick/db9.c b/drivers/input/joystick/db9.c index de0dd4756c84..7721cc0bf569 100644 --- a/drivers/input/joystick/db9.c +++ b/drivers/input/joystick/db9.c @@ -23,10 +23,6 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: - * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include <linux/kernel.h> diff --git a/drivers/input/joystick/gamecon.c b/drivers/input/joystick/gamecon.c index 2ffb2e8bdc3b..d62e73dd9f7f 100644 --- a/drivers/input/joystick/gamecon.c +++ b/drivers/input/joystick/gamecon.c @@ -24,10 +24,6 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: - * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -862,7 +858,7 @@ static int gc_setup_pad(struct gc *gc, int idx, int pad_type) case GC_N64: for (i = 0; i < 10; i++) - __set_bit(gc_n64_btn[i], input_dev->keybit); + input_set_capability(input_dev, EV_KEY, gc_n64_btn[i]); for (i = 0; i < 2; i++) { input_set_abs_params(input_dev, ABS_X + i, -127, 126, 0, 2); @@ -879,26 +875,27 @@ static int gc_setup_pad(struct gc *gc, int idx, int pad_type) break; case GC_SNESMOUSE: - __set_bit(BTN_LEFT, input_dev->keybit); - __set_bit(BTN_RIGHT, input_dev->keybit); - __set_bit(REL_X, input_dev->relbit); - __set_bit(REL_Y, input_dev->relbit); + input_set_capability(input_dev, EV_KEY, BTN_LEFT); + input_set_capability(input_dev, EV_KEY, BTN_RIGHT); + input_set_capability(input_dev, EV_REL, REL_X); + input_set_capability(input_dev, EV_REL, REL_Y); break; case GC_SNES: for (i = 4; i < 8; i++) - __set_bit(gc_snes_btn[i], input_dev->keybit); + input_set_capability(input_dev, EV_KEY, gc_snes_btn[i]); /* fall through */ case GC_NES: for (i = 0; i < 4; i++) - __set_bit(gc_snes_btn[i], input_dev->keybit); + input_set_capability(input_dev, EV_KEY, gc_snes_btn[i]); break; case GC_MULTI2: - __set_bit(BTN_THUMB, input_dev->keybit); + input_set_capability(input_dev, EV_KEY, BTN_THUMB); /* fall through */ case GC_MULTI: - __set_bit(BTN_TRIGGER, input_dev->keybit); + input_set_capability(input_dev, EV_KEY, BTN_TRIGGER); + /* fall through */ break; case GC_PSX: @@ -906,15 +903,17 @@ static int gc_setup_pad(struct gc *gc, int idx, int pad_type) input_set_abs_params(input_dev, gc_psx_abs[i], 4, 252, 0, 2); for (i = 0; i < 12; i++) - __set_bit(gc_psx_btn[i], input_dev->keybit); + input_set_capability(input_dev, EV_KEY, gc_psx_btn[i]); + break; break; case GC_DDR: for (i = 0; i < 4; i++) - __set_bit(gc_psx_ddr_btn[i], input_dev->keybit); + input_set_capability(input_dev, EV_KEY, + gc_psx_ddr_btn[i]); for (i = 0; i < 12; i++) - __set_bit(gc_psx_btn[i], input_dev->keybit); + input_set_capability(input_dev, EV_KEY, gc_psx_btn[i]); break; } diff --git a/drivers/input/joystick/gf2k.c b/drivers/input/joystick/gf2k.c index 0f519db64748..50a60065ab14 100644 --- a/drivers/input/joystick/gf2k.c +++ b/drivers/input/joystick/gf2k.c @@ -20,10 +20,6 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: - * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include <linux/delay.h> diff --git a/drivers/input/joystick/grip.c b/drivers/input/joystick/grip.c index eac9c5b8d73e..e10395ba62bc 100644 --- a/drivers/input/joystick/grip.c +++ b/drivers/input/joystick/grip.c @@ -20,10 +20,6 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: - * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include <linux/kernel.h> diff --git a/drivers/input/joystick/guillemot.c b/drivers/input/joystick/guillemot.c index a9ac2f9cfce0..43ff817d80ac 100644 --- a/drivers/input/joystick/guillemot.c +++ b/drivers/input/joystick/guillemot.c @@ -20,10 +20,6 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: - * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include <linux/kernel.h> diff --git a/drivers/input/joystick/iforce/iforce-ff.c b/drivers/input/joystick/iforce/iforce-ff.c index 0de9a0943a9e..3536d5f5ad18 100644 --- a/drivers/input/joystick/iforce/iforce-ff.c +++ b/drivers/input/joystick/iforce/iforce-ff.c @@ -19,10 +19,6 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: - * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include "iforce.h" @@ -56,7 +52,7 @@ static int make_magnitude_modifier(struct iforce* iforce, iforce_send_packet(iforce, FF_CMD_MAGNITUDE, data); - iforce_dump_packet("magnitude: ", FF_CMD_MAGNITUDE, data); + iforce_dump_packet(iforce, "magnitude", FF_CMD_MAGNITUDE, data); return 0; } @@ -178,7 +174,7 @@ static int make_condition_modifier(struct iforce* iforce, data[9] = (100 * lsat) >> 16; iforce_send_packet(iforce, FF_CMD_CONDITION, data); - iforce_dump_packet("condition", FF_CMD_CONDITION, data); + iforce_dump_packet(iforce, "condition", FF_CMD_CONDITION, data); return 0; } diff --git a/drivers/input/joystick/iforce/iforce-main.c b/drivers/input/joystick/iforce/iforce-main.c index daeeb4c7e3b0..58d5cfe46526 100644 --- a/drivers/input/joystick/iforce/iforce-main.c +++ b/drivers/input/joystick/iforce/iforce-main.c @@ -19,10 +19,6 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: - * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include "iforce.h" @@ -33,21 +29,14 @@ MODULE_LICENSE("GPL"); static signed short btn_joystick[] = { BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_TOP2, BTN_BASE, - BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_BASE5, BTN_A, BTN_B, BTN_C, -1 }; - -static signed short btn_avb_pegasus[] = -{ BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_TOP2, BTN_BASE, - BTN_BASE2, BTN_BASE3, BTN_BASE4, -1 }; + BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_BASE5, BTN_A, + BTN_B, BTN_C, BTN_DEAD, -1 }; -static signed short btn_wheel[] = -{ BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_TOP2, BTN_BASE, - BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_BASE5, BTN_A, BTN_B, BTN_C, -1 }; - -static signed short btn_avb_tw[] = +static signed short btn_joystick_avb[] = { BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE, - BTN_BASE2, BTN_BASE3, BTN_BASE4, -1 }; + BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_DEAD, -1 }; -static signed short btn_avb_wheel[] = +static signed short btn_wheel[] = { BTN_GEAR_DOWN, BTN_GEAR_UP, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_BASE5, BTN_BASE6, -1 }; @@ -73,9 +62,9 @@ static struct iforce_device iforce_device[] = { { 0x044f, 0xa01c, "Thrustmaster Motor Sport GT", btn_wheel, abs_wheel, ff_iforce }, { 0x046d, 0xc281, "Logitech WingMan Force", btn_joystick, abs_joystick, ff_iforce }, { 0x046d, 0xc291, "Logitech WingMan Formula Force", btn_wheel, abs_wheel, ff_iforce }, - { 0x05ef, 0x020a, "AVB Top Shot Pegasus", btn_avb_pegasus, abs_avb_pegasus, ff_iforce }, - { 0x05ef, 0x8884, "AVB Mag Turbo Force", btn_avb_wheel, abs_wheel, ff_iforce }, - { 0x05ef, 0x8888, "AVB Top Shot Force Feedback Racing Wheel", btn_avb_tw, abs_wheel, ff_iforce }, //? + { 0x05ef, 0x020a, "AVB Top Shot Pegasus", btn_joystick_avb, abs_avb_pegasus, ff_iforce }, + { 0x05ef, 0x8884, "AVB Mag Turbo Force", btn_wheel, abs_wheel, ff_iforce }, + { 0x05ef, 0x8888, "AVB Top Shot Force Feedback Racing Wheel", btn_wheel, abs_wheel, ff_iforce }, //? { 0x061c, 0xc0a4, "ACT LABS Force RS", btn_wheel, abs_wheel, ff_iforce }, //? { 0x061c, 0xc084, "ACT LABS Force RS", btn_wheel, abs_wheel, ff_iforce }, { 0x06f8, 0x0001, "Guillemot Race Leader Force Feedback", btn_wheel, abs_wheel, ff_iforce }, //? @@ -360,7 +349,7 @@ int iforce_init_device(struct iforce *iforce) for (i = 0; c[i]; i++) if (!iforce_get_id_packet(iforce, c + i)) - iforce_dump_packet("info", iforce->ecmd, iforce->edata); + iforce_dump_packet(iforce, "info", iforce->ecmd, iforce->edata); /* * Disable spring, enable force feedback. @@ -388,7 +377,6 @@ int iforce_init_device(struct iforce *iforce) for (i = 0; iforce->type->btn[i] >= 0; i++) set_bit(iforce->type->btn[i], input_dev->keybit); - set_bit(BTN_DEAD, input_dev->keybit); for (i = 0; iforce->type->abs[i] >= 0; i++) { diff --git a/drivers/input/joystick/iforce/iforce-packets.c b/drivers/input/joystick/iforce/iforce-packets.c index 08f98f2eaf88..c10169f4554e 100644 --- a/drivers/input/joystick/iforce/iforce-packets.c +++ b/drivers/input/joystick/iforce/iforce-packets.c @@ -19,10 +19,6 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: - * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include "iforce.h" @@ -33,14 +29,10 @@ static struct { } iforce_hat_to_axis[16] = {{ 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}}; -void iforce_dump_packet(char *msg, u16 cmd, unsigned char *data) +void iforce_dump_packet(struct iforce *iforce, char *msg, u16 cmd, unsigned char *data) { - int i; - - printk(KERN_DEBUG __FILE__ ": %s cmd = %04x, data = ", msg, cmd); - for (i = 0; i < LO(cmd); i++) - printk("%02x ", data[i]); - printk("\n"); + dev_dbg(iforce->dev->dev.parent, "%s %s cmd = %04x, data = %*ph\n", + __func__, msg, cmd, LO(cmd), data); } /* @@ -255,7 +247,7 @@ int iforce_get_id_packet(struct iforce *iforce, char *packet) iforce->cr.bRequest = packet[0]; iforce->ctrl->dev = iforce->usbdev; - status = usb_submit_urb(iforce->ctrl, GFP_ATOMIC); + status = usb_submit_urb(iforce->ctrl, GFP_KERNEL); if (status) { dev_err(&iforce->intf->dev, "usb_submit_urb failed %d\n", status); diff --git a/drivers/input/joystick/iforce/iforce-serio.c b/drivers/input/joystick/iforce/iforce-serio.c index 154e827b559b..f4ba4a751fe0 100644 --- a/drivers/input/joystick/iforce/iforce-serio.c +++ b/drivers/input/joystick/iforce/iforce-serio.c @@ -19,10 +19,6 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: - * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include "iforce.h" diff --git a/drivers/input/joystick/iforce/iforce-usb.c b/drivers/input/joystick/iforce/iforce-usb.c index e8724f1a4a25..78073259c9a1 100644 --- a/drivers/input/joystick/iforce/iforce-usb.c +++ b/drivers/input/joystick/iforce/iforce-usb.c @@ -19,10 +19,6 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: - * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include "iforce.h" diff --git a/drivers/input/joystick/iforce/iforce.h b/drivers/input/joystick/iforce/iforce.h index 96ae4f5bd0eb..0e9d01f8bcb6 100644 --- a/drivers/input/joystick/iforce/iforce.h +++ b/drivers/input/joystick/iforce/iforce.h @@ -19,10 +19,6 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: - * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include <linux/kernel.h> @@ -158,7 +154,7 @@ int iforce_init_device(struct iforce *iforce); int iforce_control_playback(struct iforce*, u16 id, unsigned int); void iforce_process_packet(struct iforce *iforce, u16 cmd, unsigned char *data); int iforce_send_packet(struct iforce *iforce, u16 cmd, unsigned char* data); -void iforce_dump_packet(char *msg, u16 cmd, unsigned char *data) ; +void iforce_dump_packet(struct iforce *iforce, char *msg, u16 cmd, unsigned char *data); int iforce_get_id_packet(struct iforce *iforce, char *packet); /* iforce-ff.c */ diff --git a/drivers/input/joystick/interact.c b/drivers/input/joystick/interact.c index 17c2c800743c..598788b3da62 100644 --- a/drivers/input/joystick/interact.c +++ b/drivers/input/joystick/interact.c @@ -23,10 +23,6 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: - * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include <linux/kernel.h> diff --git a/drivers/input/joystick/joydump.c b/drivers/input/joystick/joydump.c index d1c6e4846a4a..2ea05ade4d4e 100644 --- a/drivers/input/joystick/joydump.c +++ b/drivers/input/joystick/joydump.c @@ -21,10 +21,6 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: - * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include <linux/module.h> diff --git a/drivers/input/joystick/magellan.c b/drivers/input/joystick/magellan.c index a9d0e3edca94..95a34ab34fc3 100644 --- a/drivers/input/joystick/magellan.c +++ b/drivers/input/joystick/magellan.c @@ -20,10 +20,6 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: - * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include <linux/kernel.h> diff --git a/drivers/input/joystick/pxrc.c b/drivers/input/joystick/pxrc.c index 07a0dbd3ced2..ea2bf5951d67 100644 --- a/drivers/input/joystick/pxrc.c +++ b/drivers/input/joystick/pxrc.c @@ -3,7 +3,6 @@ * Driver for Phoenix RC Flight Controller Adapter * * Copyright (C) 2018 Marcus Folkesson <marcus.folkesson@gmail.com> - * */ #include <linux/kernel.h> @@ -16,31 +15,22 @@ #include <linux/mutex.h> #include <linux/input.h> -#define PXRC_VENDOR_ID (0x1781) -#define PXRC_PRODUCT_ID (0x0898) - -static const struct usb_device_id pxrc_table[] = { - { USB_DEVICE(PXRC_VENDOR_ID, PXRC_PRODUCT_ID) }, - { } -}; -MODULE_DEVICE_TABLE(usb, pxrc_table); +#define PXRC_VENDOR_ID 0x1781 +#define PXRC_PRODUCT_ID 0x0898 struct pxrc { struct input_dev *input; - struct usb_device *udev; struct usb_interface *intf; struct urb *urb; struct mutex pm_mutex; bool is_open; - __u8 epaddr; char phys[64]; - unsigned char *data; - size_t bsize; }; static void pxrc_usb_irq(struct urb *urb) { struct pxrc *pxrc = urb->context; + u8 *data = urb->transfer_buffer; int error; switch (urb->status) { @@ -68,15 +58,15 @@ static void pxrc_usb_irq(struct urb *urb) } if (urb->actual_length == 8) { - input_report_abs(pxrc->input, ABS_X, pxrc->data[0]); - input_report_abs(pxrc->input, ABS_Y, pxrc->data[2]); - input_report_abs(pxrc->input, ABS_RX, pxrc->data[3]); - input_report_abs(pxrc->input, ABS_RY, pxrc->data[4]); - input_report_abs(pxrc->input, ABS_RUDDER, pxrc->data[5]); - input_report_abs(pxrc->input, ABS_THROTTLE, pxrc->data[6]); - input_report_abs(pxrc->input, ABS_MISC, pxrc->data[7]); - - input_report_key(pxrc->input, BTN_A, pxrc->data[1]); + input_report_abs(pxrc->input, ABS_X, data[0]); + input_report_abs(pxrc->input, ABS_Y, data[2]); + input_report_abs(pxrc->input, ABS_RX, data[3]); + input_report_abs(pxrc->input, ABS_RY, data[4]); + input_report_abs(pxrc->input, ABS_RUDDER, data[5]); + input_report_abs(pxrc->input, ABS_THROTTLE, data[6]); + input_report_abs(pxrc->input, ABS_MISC, data[7]); + + input_report_key(pxrc->input, BTN_A, data[1]); } exit: @@ -120,61 +110,73 @@ static void pxrc_close(struct input_dev *input) mutex_unlock(&pxrc->pm_mutex); } -static int pxrc_usb_init(struct pxrc *pxrc) +static void pxrc_free_urb(void *_pxrc) { + struct pxrc *pxrc = _pxrc; + + usb_free_urb(pxrc->urb); +} + +static int pxrc_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct pxrc *pxrc; struct usb_endpoint_descriptor *epirq; - unsigned int pipe; - int retval; + size_t xfer_size; + void *xfer_buf; + int error; - /* Set up the endpoint information */ - /* This device only has an interrupt endpoint */ - retval = usb_find_common_endpoints(pxrc->intf->cur_altsetting, - NULL, NULL, &epirq, NULL); - if (retval) { - dev_err(&pxrc->intf->dev, - "Could not find endpoint\n"); - goto error; + /* + * Locate the endpoint information. This device only has an + * interrupt endpoint. + */ + error = usb_find_common_endpoints(intf->cur_altsetting, + NULL, NULL, &epirq, NULL); + if (error) { + dev_err(&intf->dev, "Could not find endpoint\n"); + return error; } - pxrc->bsize = usb_endpoint_maxp(epirq); - pxrc->epaddr = epirq->bEndpointAddress; - pxrc->data = devm_kmalloc(&pxrc->intf->dev, pxrc->bsize, GFP_KERNEL); - if (!pxrc->data) { - retval = -ENOMEM; - goto error; - } + pxrc = devm_kzalloc(&intf->dev, sizeof(*pxrc), GFP_KERNEL); + if (!pxrc) + return -ENOMEM; - usb_set_intfdata(pxrc->intf, pxrc); - usb_make_path(pxrc->udev, pxrc->phys, sizeof(pxrc->phys)); - strlcat(pxrc->phys, "/input0", sizeof(pxrc->phys)); + mutex_init(&pxrc->pm_mutex); + pxrc->intf = intf; - pxrc->urb = usb_alloc_urb(0, GFP_KERNEL); - if (!pxrc->urb) { - retval = -ENOMEM; - goto error; - } + usb_set_intfdata(pxrc->intf, pxrc); - pipe = usb_rcvintpipe(pxrc->udev, pxrc->epaddr), - usb_fill_int_urb(pxrc->urb, pxrc->udev, pipe, pxrc->data, pxrc->bsize, - pxrc_usb_irq, pxrc, 1); + xfer_size = usb_endpoint_maxp(epirq); + xfer_buf = devm_kmalloc(&intf->dev, xfer_size, GFP_KERNEL); + if (!xfer_buf) + return -ENOMEM; -error: - return retval; + pxrc->urb = usb_alloc_urb(0, GFP_KERNEL); + if (!pxrc->urb) + return -ENOMEM; + error = devm_add_action_or_reset(&intf->dev, pxrc_free_urb, pxrc); + if (error) + return error; -} + usb_fill_int_urb(pxrc->urb, udev, + usb_rcvintpipe(udev, epirq->bEndpointAddress), + xfer_buf, xfer_size, pxrc_usb_irq, pxrc, 1); -static int pxrc_input_init(struct pxrc *pxrc) -{ - pxrc->input = devm_input_allocate_device(&pxrc->intf->dev); - if (pxrc->input == NULL) { - dev_err(&pxrc->intf->dev, "couldn't allocate input device\n"); + pxrc->input = devm_input_allocate_device(&intf->dev); + if (!pxrc->input) { + dev_err(&intf->dev, "couldn't allocate input device\n"); return -ENOMEM; } pxrc->input->name = "PXRC Flight Controller Adapter"; + + usb_make_path(udev, pxrc->phys, sizeof(pxrc->phys)); + strlcat(pxrc->phys, "/input0", sizeof(pxrc->phys)); pxrc->input->phys = pxrc->phys; - usb_to_input_id(pxrc->udev, &pxrc->input->id); + + usb_to_input_id(udev, &pxrc->input->id); pxrc->input->open = pxrc_open; pxrc->input->close = pxrc_close; @@ -190,46 +192,16 @@ static int pxrc_input_init(struct pxrc *pxrc) input_set_drvdata(pxrc->input, pxrc); - return input_register_device(pxrc->input); -} - -static int pxrc_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct pxrc *pxrc; - int retval; - - pxrc = devm_kzalloc(&intf->dev, sizeof(*pxrc), GFP_KERNEL); - if (!pxrc) - return -ENOMEM; - - mutex_init(&pxrc->pm_mutex); - pxrc->udev = usb_get_dev(interface_to_usbdev(intf)); - pxrc->intf = intf; - - retval = pxrc_usb_init(pxrc); - if (retval) - goto error; - - retval = pxrc_input_init(pxrc); - if (retval) - goto err_free_urb; + error = input_register_device(pxrc->input); + if (error) + return error; return 0; - -err_free_urb: - usb_free_urb(pxrc->urb); - -error: - return retval; } static void pxrc_disconnect(struct usb_interface *intf) { - struct pxrc *pxrc = usb_get_intfdata(intf); - - usb_free_urb(pxrc->urb); - usb_set_intfdata(intf, NULL); + /* All driver resources are devm-managed. */ } static int pxrc_suspend(struct usb_interface *intf, pm_message_t message) @@ -284,6 +256,12 @@ static int pxrc_reset_resume(struct usb_interface *intf) return pxrc_resume(intf); } +static const struct usb_device_id pxrc_table[] = { + { USB_DEVICE(PXRC_VENDOR_ID, PXRC_PRODUCT_ID) }, + { } +}; +MODULE_DEVICE_TABLE(usb, pxrc_table); + static struct usb_driver pxrc_driver = { .name = "pxrc", .probe = pxrc_probe, diff --git a/drivers/input/joystick/sidewinder.c b/drivers/input/joystick/sidewinder.c index 5e602a6852b7..f46bf4d41972 100644 --- a/drivers/input/joystick/sidewinder.c +++ b/drivers/input/joystick/sidewinder.c @@ -20,10 +20,6 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: - * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include <linux/delay.h> diff --git a/drivers/input/joystick/spaceball.c b/drivers/input/joystick/spaceball.c index bb3faeff8cac..ffb9c1f495b6 100644 --- a/drivers/input/joystick/spaceball.c +++ b/drivers/input/joystick/spaceball.c @@ -24,10 +24,6 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: - * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include <linux/kernel.h> diff --git a/drivers/input/joystick/spaceorb.c b/drivers/input/joystick/spaceorb.c index 05da0ed514e2..20540ee71d7f 100644 --- a/drivers/input/joystick/spaceorb.c +++ b/drivers/input/joystick/spaceorb.c @@ -23,10 +23,6 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: - * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include <linux/kernel.h> diff --git a/drivers/input/joystick/stinger.c b/drivers/input/joystick/stinger.c index cb10e7b097ae..ba8579435d6c 100644 --- a/drivers/input/joystick/stinger.c +++ b/drivers/input/joystick/stinger.c @@ -21,10 +21,6 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: - * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include <linux/kernel.h> diff --git a/drivers/input/joystick/tmdc.c b/drivers/input/joystick/tmdc.c index 7e17cde464f0..6f4a01cfe79f 100644 --- a/drivers/input/joystick/tmdc.c +++ b/drivers/input/joystick/tmdc.c @@ -23,10 +23,6 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: - * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include <linux/delay.h> diff --git a/drivers/input/joystick/turbografx.c b/drivers/input/joystick/turbografx.c index e2685753e460..bf2f9925e416 100644 --- a/drivers/input/joystick/turbografx.c +++ b/drivers/input/joystick/turbografx.c @@ -23,10 +23,6 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: - * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include <linux/kernel.h> diff --git a/drivers/input/joystick/warrior.c b/drivers/input/joystick/warrior.c index ef5391ba4470..b60cab168e2a 100644 --- a/drivers/input/joystick/warrior.c +++ b/drivers/input/joystick/warrior.c @@ -20,10 +20,6 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: - * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include <linux/kernel.h> diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index 06e9650b3b30..48e36acbeb49 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -86,8 +86,10 @@ #define XPAD_PKT_LEN 64 -/* xbox d-pads should map to buttons, as is required for DDR pads - but we map them to axes when possible to simplify things */ +/* + * xbox d-pads should map to buttons, as is required for DDR pads + * but we map them to axes when possible to simplify things + */ #define MAP_DPAD_TO_BUTTONS (1 << 0) #define MAP_TRIGGERS_TO_BUTTONS (1 << 1) #define MAP_STICKS_TO_NULL (1 << 2) @@ -123,6 +125,7 @@ static const struct xpad_device { u8 mapping; u8 xtype; } xpad_device[] = { + { 0x0079, 0x18d4, "GPD Win 2 Controller", 0, XTYPE_XBOX360 }, { 0x044f, 0x0f00, "Thrustmaster Wheel", 0, XTYPE_XBOX }, { 0x044f, 0x0f03, "Thrustmaster Wheel", 0, XTYPE_XBOX }, { 0x044f, 0x0f07, "Thrustmaster, Inc. Controller", 0, XTYPE_XBOX }, @@ -387,15 +390,15 @@ static const signed short xpad_abs_triggers[] = { * match against vendor id as well. Wired Xbox 360 devices have protocol 1, * wireless controllers have protocol 129. */ -#define XPAD_XBOX360_VENDOR_PROTOCOL(vend,pr) \ +#define XPAD_XBOX360_VENDOR_PROTOCOL(vend, pr) \ .match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_INT_INFO, \ .idVendor = (vend), \ .bInterfaceClass = USB_CLASS_VENDOR_SPEC, \ .bInterfaceSubClass = 93, \ .bInterfaceProtocol = (pr) #define XPAD_XBOX360_VENDOR(vend) \ - { XPAD_XBOX360_VENDOR_PROTOCOL(vend,1) }, \ - { XPAD_XBOX360_VENDOR_PROTOCOL(vend,129) } + { XPAD_XBOX360_VENDOR_PROTOCOL((vend), 1) }, \ + { XPAD_XBOX360_VENDOR_PROTOCOL((vend), 129) } /* The Xbox One controller uses subclass 71 and protocol 208. */ #define XPAD_XBOXONE_VENDOR_PROTOCOL(vend, pr) \ @@ -405,10 +408,11 @@ static const signed short xpad_abs_triggers[] = { .bInterfaceSubClass = 71, \ .bInterfaceProtocol = (pr) #define XPAD_XBOXONE_VENDOR(vend) \ - { XPAD_XBOXONE_VENDOR_PROTOCOL(vend, 208) } + { XPAD_XBOXONE_VENDOR_PROTOCOL((vend), 208) } static const struct usb_device_id xpad_table[] = { { USB_INTERFACE_INFO('X', 'B', 0) }, /* X-Box USB-IF not approved class */ + XPAD_XBOX360_VENDOR(0x0079), /* GPD Win 2 Controller */ XPAD_XBOX360_VENDOR(0x044f), /* Thrustmaster X-Box 360 controllers */ XPAD_XBOX360_VENDOR(0x045e), /* Microsoft X-Box 360 controllers */ XPAD_XBOXONE_VENDOR(0x045e), /* Microsoft X-Box One controllers */ @@ -1573,7 +1577,6 @@ static void xpad_close(struct input_dev *dev) static void xpad_set_up_abs(struct input_dev *input_dev, signed short abs) { struct usb_xpad *xpad = input_get_drvdata(input_dev); - set_bit(abs, input_dev->absbit); switch (abs) { case ABS_X: @@ -1593,6 +1596,9 @@ static void xpad_set_up_abs(struct input_dev *input_dev, signed short abs) case ABS_HAT0Y: /* the d-pad (only if dpad is mapped to axes */ input_set_abs_params(input_dev, abs, -1, 1, 0, 0); break; + default: + input_set_abs_params(input_dev, abs, 0, 0, 0, 0); + break; } } @@ -1633,10 +1639,7 @@ static int xpad_init_input(struct usb_xpad *xpad) input_dev->close = xpad_close; } - __set_bit(EV_KEY, input_dev->evbit); - if (!(xpad->mapping & MAP_STICKS_TO_NULL)) { - __set_bit(EV_ABS, input_dev->evbit); /* set up axes */ for (i = 0; xpad_abs[i] >= 0; i++) xpad_set_up_abs(input_dev, xpad_abs[i]); @@ -1644,21 +1647,22 @@ static int xpad_init_input(struct usb_xpad *xpad) /* set up standard buttons */ for (i = 0; xpad_common_btn[i] >= 0; i++) - __set_bit(xpad_common_btn[i], input_dev->keybit); + input_set_capability(input_dev, EV_KEY, xpad_common_btn[i]); /* set up model-specific ones */ if (xpad->xtype == XTYPE_XBOX360 || xpad->xtype == XTYPE_XBOX360W || xpad->xtype == XTYPE_XBOXONE) { for (i = 0; xpad360_btn[i] >= 0; i++) - __set_bit(xpad360_btn[i], input_dev->keybit); + input_set_capability(input_dev, EV_KEY, xpad360_btn[i]); } else { for (i = 0; xpad_btn[i] >= 0; i++) - __set_bit(xpad_btn[i], input_dev->keybit); + input_set_capability(input_dev, EV_KEY, xpad_btn[i]); } if (xpad->mapping & MAP_DPAD_TO_BUTTONS) { for (i = 0; xpad_btn_pad[i] >= 0; i++) - __set_bit(xpad_btn_pad[i], input_dev->keybit); + input_set_capability(input_dev, EV_KEY, + xpad_btn_pad[i]); } /* @@ -1675,7 +1679,8 @@ static int xpad_init_input(struct usb_xpad *xpad) if (xpad->mapping & MAP_TRIGGERS_TO_BUTTONS) { for (i = 0; xpad_btn_triggers[i] >= 0; i++) - __set_bit(xpad_btn_triggers[i], input_dev->keybit); + input_set_capability(input_dev, EV_KEY, + xpad_btn_triggers[i]); } else { for (i = 0; xpad_abs_triggers[i] >= 0; i++) xpad_set_up_abs(input_dev, xpad_abs_triggers[i]); diff --git a/drivers/input/keyboard/amikbd.c b/drivers/input/keyboard/amikbd.c index e04a3b4e55d6..420e33c49e58 100644 --- a/drivers/input/keyboard/amikbd.c +++ b/drivers/input/keyboard/amikbd.c @@ -23,10 +23,6 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: - * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include <linux/module.h> diff --git a/drivers/input/keyboard/atakbd.c b/drivers/input/keyboard/atakbd.c index f1235831283d..6f62da2909ec 100644 --- a/drivers/input/keyboard/atakbd.c +++ b/drivers/input/keyboard/atakbd.c @@ -34,10 +34,6 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: - * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include <linux/module.h> diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c index 79eb29550c34..81be6f781f0b 100644 --- a/drivers/input/keyboard/cros_ec_keyb.c +++ b/drivers/input/keyboard/cros_ec_keyb.c @@ -1,25 +1,15 @@ -/* - * ChromeOS EC keyboard driver - * - * Copyright (C) 2012 Google, Inc - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * 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. - * - * This driver uses the Chrome OS EC byte-level message-based protocol for - * communicating the keyboard state (which keys are pressed) from a keyboard EC - * to the AP over some bus (such as i2c, lpc, spi). The EC does debouncing, - * but everything else (including deghosting) is done here. The main - * motivation for this is to keep the EC firmware as simple as possible, since - * it cannot be easily upgraded and EC flash/IRAM space is relatively - * expensive. - */ +// SPDX-License-Identifier: GPL-2.0 +// ChromeOS EC keyboard driver +// +// Copyright (C) 2012 Google, Inc. +// +// This driver uses the ChromeOS EC byte-level message-based protocol for +// communicating the keyboard state (which keys are pressed) from a keyboard EC +// to the AP over some bus (such as i2c, lpc, spi). The EC does debouncing, +// but everything else (including deghosting) is done here. The main +// motivation for this is to keep the EC firmware as simple as possible, since +// it cannot be easily upgraded and EC flash/IRAM space is relatively +// expensive. #include <linux/module.h> #include <linux/bitops.h> @@ -170,9 +160,6 @@ static void cros_ec_keyb_process(struct cros_ec_keyb *ckdev, int col, row; int new_state; int old_state; - int num_cols; - - num_cols = len; if (ckdev->ghost_filter && cros_ec_keyb_has_ghosting(ckdev, kb_state)) { /* @@ -242,26 +229,32 @@ static int cros_ec_keyb_work(struct notifier_block *nb, u32 val; unsigned int ev_type; + /* + * If not wake enabled, discard key state changes during + * suspend. Switches will be re-checked in + * cros_ec_keyb_resume() to be sure nothing is lost. + */ + if (queued_during_suspend && !device_may_wakeup(ckdev->dev)) + return NOTIFY_OK; + switch (ckdev->ec->event_data.event_type) { case EC_MKBP_EVENT_KEY_MATRIX: - /* - * If EC is not the wake source, discard key state changes - * during suspend. - */ - if (queued_during_suspend) - return NOTIFY_OK; + pm_wakeup_event(ckdev->dev, 0); if (ckdev->ec->event_size != ckdev->cols) { dev_err(ckdev->dev, "Discarded incomplete key matrix event.\n"); return NOTIFY_OK; } + cros_ec_keyb_process(ckdev, ckdev->ec->event_data.data.key_matrix, ckdev->ec->event_size); break; case EC_MKBP_EVENT_SYSRQ: + pm_wakeup_event(ckdev->dev, 0); + val = get_unaligned_le32(&ckdev->ec->event_data.data.sysrq); dev_dbg(ckdev->dev, "sysrq code from EC: %#x\n", val); handle_sysrq(val); @@ -269,13 +262,7 @@ static int cros_ec_keyb_work(struct notifier_block *nb, case EC_MKBP_EVENT_BUTTON: case EC_MKBP_EVENT_SWITCH: - /* - * If EC is not the wake source, discard key state - * changes during suspend. Switches will be re-checked in - * cros_ec_keyb_resume() to be sure nothing is lost. - */ - if (queued_during_suspend) - return NOTIFY_OK; + pm_wakeup_event(ckdev->dev, 0); if (ckdev->ec->event_data.event_type == EC_MKBP_EVENT_BUTTON) { val = get_unaligned_le32( @@ -639,6 +626,7 @@ static int cros_ec_keyb_probe(struct platform_device *pdev) return err; } + device_init_wakeup(ckdev->dev, true); return 0; } @@ -674,6 +662,6 @@ static struct platform_driver cros_ec_keyb_driver = { module_platform_driver(cros_ec_keyb_driver); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("ChromeOS EC keyboard driver"); MODULE_ALIAS("platform:cros-ec-keyb"); diff --git a/drivers/input/keyboard/goldfish_events.c b/drivers/input/keyboard/goldfish_events.c index f6e643b589b6..e8dae6195b30 100644 --- a/drivers/input/keyboard/goldfish_events.c +++ b/drivers/input/keyboard/goldfish_events.c @@ -45,7 +45,7 @@ struct event_dev { static irqreturn_t events_interrupt(int irq, void *dev_id) { struct event_dev *edev = dev_id; - unsigned type, code, value; + unsigned int type, code, value; type = __raw_readl(edev->addr + REG_READ); code = __raw_readl(edev->addr + REG_READ); @@ -57,7 +57,7 @@ static irqreturn_t events_interrupt(int irq, void *dev_id) } static void events_import_bits(struct event_dev *edev, - unsigned long bits[], unsigned type, size_t count) + unsigned long bits[], unsigned int type, size_t count) { void __iomem *addr = edev->addr; int i, j; @@ -99,6 +99,7 @@ static void events_import_abs_params(struct event_dev *edev) for (j = 0; j < ARRAY_SIZE(val); j++) { int offset = (i * ARRAY_SIZE(val) + j) * sizeof(u32); + val[j] = __raw_readl(edev->addr + REG_DATA + offset); } @@ -112,7 +113,7 @@ static int events_probe(struct platform_device *pdev) struct input_dev *input_dev; struct event_dev *edev; struct resource *res; - unsigned keymapnamelen; + unsigned int keymapnamelen; void __iomem *addr; int irq; int i; @@ -150,7 +151,7 @@ static int events_probe(struct platform_device *pdev) for (i = 0; i < keymapnamelen; i++) edev->name[i] = __raw_readb(edev->addr + REG_DATA + i); - pr_debug("events_probe() keymap=%s\n", edev->name); + pr_debug("%s: keymap=%s\n", __func__, edev->name); input_dev->name = edev->name; input_dev->id.bustype = BUS_HOST; diff --git a/drivers/input/keyboard/imx_keypad.c b/drivers/input/keyboard/imx_keypad.c index 25d61d8d4fc4..539cb670de41 100644 --- a/drivers/input/keyboard/imx_keypad.c +++ b/drivers/input/keyboard/imx_keypad.c @@ -1,11 +1,7 @@ -/* - * Driver for the IMX keypad port. - * Copyright (C) 2009 Alberto Panizzo <maramaopercheseimorto@gmail.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// Driver for the IMX keypad port. +// Copyright (C) 2009 Alberto Panizzo <maramaopercheseimorto@gmail.com> #include <linux/clk.h> #include <linux/delay.h> diff --git a/drivers/input/keyboard/newtonkbd.c b/drivers/input/keyboard/newtonkbd.c index fb9b8e23ab93..de26e2df0ad5 100644 --- a/drivers/input/keyboard/newtonkbd.c +++ b/drivers/input/keyboard/newtonkbd.c @@ -20,10 +20,6 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <j.cormack@doc.ic.ac.uk>, or by paper mail: - * Justin Cormack, 68 Dartmouth Park Road, London NW5 1SN, UK. */ #include <linux/slab.h> diff --git a/drivers/input/keyboard/snvs_pwrkey.c b/drivers/input/keyboard/snvs_pwrkey.c index 53c768b95939..effb63205d3d 100644 --- a/drivers/input/keyboard/snvs_pwrkey.c +++ b/drivers/input/keyboard/snvs_pwrkey.c @@ -1,14 +1,7 @@ -/* - * Driver for the IMX SNVS ON/OFF Power Key - * Copyright (C) 2015 Freescale Semiconductor, Inc. All Rights Reserved. - * - * The code contained herein is licensed under the GNU General Public - * License. You may obtain a copy of the GNU General Public License - * Version 2 or later at the following locations: - * - * http://www.opensource.org/licenses/gpl-license.html - * http://www.gnu.org/copyleft/gpl.html - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// Driver for the IMX SNVS ON/OFF Power Key +// Copyright (C) 2015 Freescale Semiconductor, Inc. All Rights Reserved. #include <linux/device.h> #include <linux/err.h> diff --git a/drivers/input/keyboard/stowaway.c b/drivers/input/keyboard/stowaway.c index 8b6de9a692dc..15a5e74dbe91 100644 --- a/drivers/input/keyboard/stowaway.c +++ b/drivers/input/keyboard/stowaway.c @@ -23,10 +23,6 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <marek.vasut@gmail.com>, or by paper mail: - * Marek Vasut, Liskovecka 559, Frydek-Mistek, 738 01 Czech Republic */ #include <linux/slab.h> diff --git a/drivers/input/keyboard/sunkbd.c b/drivers/input/keyboard/sunkbd.c index c95707ea2656..ad5d7f94f95a 100644 --- a/drivers/input/keyboard/sunkbd.c +++ b/drivers/input/keyboard/sunkbd.c @@ -20,10 +20,6 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: - * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include <linux/delay.h> diff --git a/drivers/input/keyboard/xtkbd.c b/drivers/input/keyboard/xtkbd.c index 8f64b9ded8d0..f7598114b962 100644 --- a/drivers/input/keyboard/xtkbd.c +++ b/drivers/input/keyboard/xtkbd.c @@ -20,10 +20,6 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: - * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include <linux/slab.h> diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 572b15fa18c2..c761c0c4f2b8 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -841,4 +841,14 @@ config INPUT_RAVE_SP_PWRBUTTON To compile this driver as a module, choose M here: the module will be called rave-sp-pwrbutton. +config INPUT_SC27XX_VIBRA + tristate "Spreadtrum sc27xx vibrator support" + depends on MFD_SC27XX_PMIC || COMPILE_TEST + select INPUT_FF_MEMLESS + help + This option enables support for Spreadtrum sc27xx vibrator driver. + + To compile this driver as a module, choose M here. The module will + be called sc27xx_vibra. + endif diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 72cde28649e2..9d0f9d1ff68f 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -66,6 +66,7 @@ obj-$(CONFIG_INPUT_RETU_PWRBUTTON) += retu-pwrbutton.o obj-$(CONFIG_INPUT_AXP20X_PEK) += axp20x-pek.o obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER) += rotary_encoder.o obj-$(CONFIG_INPUT_RK805_PWRKEY) += rk805-pwrkey.o +obj-$(CONFIG_INPUT_SC27XX_VIBRA) += sc27xx-vibra.o obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o obj-$(CONFIG_INPUT_SIRFSOC_ONKEY) += sirfsoc-onkey.o obj-$(CONFIG_INPUT_SOC_BUTTON_ARRAY) += soc_button_array.o diff --git a/drivers/input/misc/ati_remote2.c b/drivers/input/misc/ati_remote2.c index ded5b84e336d..d8fd58fdf050 100644 --- a/drivers/input/misc/ati_remote2.c +++ b/drivers/input/misc/ati_remote2.c @@ -22,7 +22,7 @@ MODULE_LICENSE("GPL"); /* * ATI Remote Wonder II Channel Configuration * - * The remote control can by assigned one of sixteen "channels" in order to facilitate + * The remote control can be assigned one of sixteen "channels" in order to facilitate * the use of multiple remote controls within range of each other. * A remote's "channel" may be altered by pressing and holding the "PC" button for * approximately 3 seconds, after which the button will slowly flash the count of the diff --git a/drivers/input/misc/keyspan_remote.c b/drivers/input/misc/keyspan_remote.c index 67482b248b2d..a8937ceac66a 100644 --- a/drivers/input/misc/keyspan_remote.c +++ b/drivers/input/misc/keyspan_remote.c @@ -466,7 +466,7 @@ static int keyspan_probe(struct usb_interface *interface, const struct usb_devic remote->in_endpoint = endpoint; remote->toggle = -1; /* Set to -1 so we will always not match the toggle from the first remote message. */ - remote->in_buffer = usb_alloc_coherent(udev, RECV_SIZE, GFP_ATOMIC, &remote->in_dma); + remote->in_buffer = usb_alloc_coherent(udev, RECV_SIZE, GFP_KERNEL, &remote->in_dma); if (!remote->in_buffer) { error = -ENOMEM; goto fail1; diff --git a/drivers/input/misc/pm8941-pwrkey.c b/drivers/input/misc/pm8941-pwrkey.c index 18ad956454f1..48153e0ca19a 100644 --- a/drivers/input/misc/pm8941-pwrkey.c +++ b/drivers/input/misc/pm8941-pwrkey.c @@ -20,6 +20,7 @@ #include <linux/log2.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/reboot.h> #include <linux/regmap.h> @@ -28,6 +29,7 @@ #define PON_RT_STS 0x10 #define PON_KPDPWR_N_SET BIT(0) +#define PON_RESIN_N_SET BIT(1) #define PON_PS_HOLD_RST_CTL 0x5a #define PON_PS_HOLD_RST_CTL2 0x5b @@ -38,10 +40,15 @@ #define PON_PULL_CTL 0x70 #define PON_KPDPWR_PULL_UP BIT(1) +#define PON_RESIN_PULL_UP BIT(0) #define PON_DBC_CTL 0x71 #define PON_DBC_DELAY_MASK 0x7 +struct pm8941_data { + unsigned int pull_up_bit; + unsigned int status_bit; +}; struct pm8941_pwrkey { struct device *dev; @@ -52,6 +59,9 @@ struct pm8941_pwrkey { unsigned int revision; struct notifier_block reboot_notifier; + + u32 code; + const struct pm8941_data *data; }; static int pm8941_reboot_notify(struct notifier_block *nb, @@ -124,7 +134,8 @@ static irqreturn_t pm8941_pwrkey_irq(int irq, void *_data) if (error) return IRQ_HANDLED; - input_report_key(pwrkey->input, KEY_POWER, !!(sts & PON_KPDPWR_N_SET)); + input_report_key(pwrkey->input, pwrkey->code, + sts & pwrkey->data->status_bit); input_sync(pwrkey->input); return IRQ_HANDLED; @@ -157,6 +168,7 @@ static int pm8941_pwrkey_probe(struct platform_device *pdev) { struct pm8941_pwrkey *pwrkey; bool pull_up; + struct device *parent; u32 req_delay; int error; @@ -175,12 +187,30 @@ static int pm8941_pwrkey_probe(struct platform_device *pdev) return -ENOMEM; pwrkey->dev = &pdev->dev; + pwrkey->data = of_device_get_match_data(&pdev->dev); - pwrkey->regmap = dev_get_regmap(pdev->dev.parent, NULL); + parent = pdev->dev.parent; + pwrkey->regmap = dev_get_regmap(parent, NULL); if (!pwrkey->regmap) { - dev_err(&pdev->dev, "failed to locate regmap\n"); - return -ENODEV; + /* + * We failed to get regmap for parent. Let's see if we are + * a child of pon node and read regmap and reg from its + * parent. + */ + pwrkey->regmap = dev_get_regmap(parent->parent, NULL); + if (!pwrkey->regmap) { + dev_err(&pdev->dev, "failed to locate regmap\n"); + return -ENODEV; + } + + error = of_property_read_u32(parent->of_node, + "reg", &pwrkey->baseaddr); + } else { + error = of_property_read_u32(pdev->dev.of_node, "reg", + &pwrkey->baseaddr); } + if (error) + return error; pwrkey->irq = platform_get_irq(pdev, 0); if (pwrkey->irq < 0) { @@ -188,11 +218,6 @@ static int pm8941_pwrkey_probe(struct platform_device *pdev) return pwrkey->irq; } - error = of_property_read_u32(pdev->dev.of_node, "reg", - &pwrkey->baseaddr); - if (error) - return error; - error = regmap_read(pwrkey->regmap, pwrkey->baseaddr + PON_REV2, &pwrkey->revision); if (error) { @@ -200,13 +225,21 @@ static int pm8941_pwrkey_probe(struct platform_device *pdev) return error; } + error = of_property_read_u32(pdev->dev.of_node, "linux,code", + &pwrkey->code); + if (error) { + dev_dbg(&pdev->dev, + "no linux,code assuming power (%d)\n", error); + pwrkey->code = KEY_POWER; + } + pwrkey->input = devm_input_allocate_device(&pdev->dev); if (!pwrkey->input) { dev_dbg(&pdev->dev, "unable to allocate input device\n"); return -ENOMEM; } - input_set_capability(pwrkey->input, EV_KEY, KEY_POWER); + input_set_capability(pwrkey->input, EV_KEY, pwrkey->code); pwrkey->input->name = "pm8941_pwrkey"; pwrkey->input->phys = "pm8941_pwrkey/input0"; @@ -225,8 +258,8 @@ static int pm8941_pwrkey_probe(struct platform_device *pdev) error = regmap_update_bits(pwrkey->regmap, pwrkey->baseaddr + PON_PULL_CTL, - PON_KPDPWR_PULL_UP, - pull_up ? PON_KPDPWR_PULL_UP : 0); + pwrkey->data->pull_up_bit, + pull_up ? pwrkey->data->pull_up_bit : 0); if (error) { dev_err(&pdev->dev, "failed to set pull: %d\n", error); return error; @@ -271,8 +304,19 @@ static int pm8941_pwrkey_remove(struct platform_device *pdev) return 0; } +static const struct pm8941_data pwrkey_data = { + .pull_up_bit = PON_KPDPWR_PULL_UP, + .status_bit = PON_KPDPWR_N_SET, +}; + +static const struct pm8941_data resin_data = { + .pull_up_bit = PON_RESIN_PULL_UP, + .status_bit = PON_RESIN_N_SET, +}; + static const struct of_device_id pm8941_pwr_key_id_table[] = { - { .compatible = "qcom,pm8941-pwrkey" }, + { .compatible = "qcom,pm8941-pwrkey", .data = &pwrkey_data }, + { .compatible = "qcom,pm8941-resin", .data = &resin_data }, { } }; MODULE_DEVICE_TABLE(of, pm8941_pwr_key_id_table); diff --git a/drivers/input/misc/powermate.c b/drivers/input/misc/powermate.c index 5c8c79623c87..e8de3aaf9f63 100644 --- a/drivers/input/misc/powermate.c +++ b/drivers/input/misc/powermate.c @@ -277,7 +277,7 @@ static int powermate_input_event(struct input_dev *dev, unsigned int type, unsig static int powermate_alloc_buffers(struct usb_device *udev, struct powermate_device *pm) { pm->data = usb_alloc_coherent(udev, POWERMATE_PAYLOAD_SIZE_MAX, - GFP_ATOMIC, &pm->data_dma); + GFP_KERNEL, &pm->data_dma); if (!pm->data) return -1; diff --git a/drivers/input/misc/sc27xx-vibra.c b/drivers/input/misc/sc27xx-vibra.c new file mode 100644 index 000000000000..295251abbdac --- /dev/null +++ b/drivers/input/misc/sc27xx-vibra.c @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Spreadtrum Communications Inc. + */ + +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/input.h> +#include <linux/workqueue.h> + +#define CUR_DRV_CAL_SEL GENMASK(13, 12) +#define SLP_LDOVIBR_PD_EN BIT(9) +#define LDO_VIBR_PD BIT(8) + +struct vibra_info { + struct input_dev *input_dev; + struct work_struct play_work; + struct regmap *regmap; + u32 base; + u32 strength; + bool enabled; +}; + +static void sc27xx_vibra_set(struct vibra_info *info, bool on) +{ + if (on) { + regmap_update_bits(info->regmap, info->base, LDO_VIBR_PD, 0); + regmap_update_bits(info->regmap, info->base, + SLP_LDOVIBR_PD_EN, 0); + info->enabled = true; + } else { + regmap_update_bits(info->regmap, info->base, LDO_VIBR_PD, + LDO_VIBR_PD); + regmap_update_bits(info->regmap, info->base, + SLP_LDOVIBR_PD_EN, SLP_LDOVIBR_PD_EN); + info->enabled = false; + } +} + +static int sc27xx_vibra_hw_init(struct vibra_info *info) +{ + return regmap_update_bits(info->regmap, info->base, CUR_DRV_CAL_SEL, 0); +} + +static void sc27xx_vibra_play_work(struct work_struct *work) +{ + struct vibra_info *info = container_of(work, struct vibra_info, + play_work); + + if (info->strength && !info->enabled) + sc27xx_vibra_set(info, true); + else if (info->strength == 0 && info->enabled) + sc27xx_vibra_set(info, false); +} + +static int sc27xx_vibra_play(struct input_dev *input, void *data, + struct ff_effect *effect) +{ + struct vibra_info *info = input_get_drvdata(input); + + info->strength = effect->u.rumble.weak_magnitude; + schedule_work(&info->play_work); + + return 0; +} + +static void sc27xx_vibra_close(struct input_dev *input) +{ + struct vibra_info *info = input_get_drvdata(input); + + cancel_work_sync(&info->play_work); + if (info->enabled) + sc27xx_vibra_set(info, false); +} + +static int sc27xx_vibra_probe(struct platform_device *pdev) +{ + struct vibra_info *info; + int error; + + info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!info->regmap) { + dev_err(&pdev->dev, "failed to get vibrator regmap.\n"); + return -ENODEV; + } + + error = device_property_read_u32(&pdev->dev, "reg", &info->base); + if (error) { + dev_err(&pdev->dev, "failed to get vibrator base address.\n"); + return error; + } + + info->input_dev = devm_input_allocate_device(&pdev->dev); + if (!info->input_dev) { + dev_err(&pdev->dev, "failed to allocate input device.\n"); + return -ENOMEM; + } + + info->input_dev->name = "sc27xx:vibrator"; + info->input_dev->id.version = 0; + info->input_dev->close = sc27xx_vibra_close; + + input_set_drvdata(info->input_dev, info); + input_set_capability(info->input_dev, EV_FF, FF_RUMBLE); + INIT_WORK(&info->play_work, sc27xx_vibra_play_work); + info->enabled = false; + + error = sc27xx_vibra_hw_init(info); + if (error) { + dev_err(&pdev->dev, "failed to initialize the vibrator.\n"); + return error; + } + + error = input_ff_create_memless(info->input_dev, NULL, + sc27xx_vibra_play); + if (error) { + dev_err(&pdev->dev, "failed to register vibrator to FF.\n"); + return error; + } + + error = input_register_device(info->input_dev); + if (error) { + dev_err(&pdev->dev, "failed to register input device.\n"); + return error; + } + + return 0; +} + +static const struct of_device_id sc27xx_vibra_of_match[] = { + { .compatible = "sprd,sc2731-vibrator", }, + {} +}; +MODULE_DEVICE_TABLE(of, sc27xx_vibra_of_match); + +static struct platform_driver sc27xx_vibra_driver = { + .driver = { + .name = "sc27xx-vibrator", + .of_match_table = sc27xx_vibra_of_match, + }, + .probe = sc27xx_vibra_probe, +}; + +module_platform_driver(sc27xx_vibra_driver); + +MODULE_DESCRIPTION("Spreadtrum SC27xx Vibrator Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Xiaotong Lu <xiaotong.lu@spreadtrum.com>"); diff --git a/drivers/input/misc/xen-kbdfront.c b/drivers/input/misc/xen-kbdfront.c index d91f3b1c5375..594f72e39639 100644 --- a/drivers/input/misc/xen-kbdfront.c +++ b/drivers/input/misc/xen-kbdfront.c @@ -63,6 +63,9 @@ static void xenkbd_disconnect_backend(struct xenkbd_info *); static void xenkbd_handle_motion_event(struct xenkbd_info *info, struct xenkbd_motion *motion) { + if (unlikely(!info->ptr)) + return; + input_report_rel(info->ptr, REL_X, motion->rel_x); input_report_rel(info->ptr, REL_Y, motion->rel_y); if (motion->rel_z) @@ -73,6 +76,9 @@ static void xenkbd_handle_motion_event(struct xenkbd_info *info, static void xenkbd_handle_position_event(struct xenkbd_info *info, struct xenkbd_position *pos) { + if (unlikely(!info->ptr)) + return; + input_report_abs(info->ptr, ABS_X, pos->abs_x); input_report_abs(info->ptr, ABS_Y, pos->abs_y); if (pos->rel_z) @@ -97,6 +103,9 @@ static void xenkbd_handle_key_event(struct xenkbd_info *info, return; } + if (unlikely(!dev)) + return; + input_event(dev, EV_KEY, key->keycode, value); input_sync(dev); } @@ -192,7 +201,7 @@ static int xenkbd_probe(struct xenbus_device *dev, const struct xenbus_device_id *id) { int ret, i; - unsigned int abs, touch; + bool with_mtouch, with_kbd, with_ptr; struct xenkbd_info *info; struct input_dev *kbd, *ptr, *mtouch; @@ -211,106 +220,127 @@ static int xenkbd_probe(struct xenbus_device *dev, if (!info->page) goto error_nomem; - /* Set input abs params to match backend screen res */ - abs = xenbus_read_unsigned(dev->otherend, - XENKBD_FIELD_FEAT_ABS_POINTER, 0); - ptr_size[KPARAM_X] = xenbus_read_unsigned(dev->otherend, - XENKBD_FIELD_WIDTH, - ptr_size[KPARAM_X]); - ptr_size[KPARAM_Y] = xenbus_read_unsigned(dev->otherend, - XENKBD_FIELD_HEIGHT, - ptr_size[KPARAM_Y]); - if (abs) { - ret = xenbus_write(XBT_NIL, dev->nodename, - XENKBD_FIELD_REQ_ABS_POINTER, "1"); - if (ret) { - pr_warn("xenkbd: can't request abs-pointer\n"); - abs = 0; - } - } + /* + * The below are reverse logic, e.g. if the feature is set, then + * do not expose the corresponding virtual device. + */ + with_kbd = !xenbus_read_unsigned(dev->otherend, + XENKBD_FIELD_FEAT_DSBL_KEYBRD, 0); + + with_ptr = !xenbus_read_unsigned(dev->otherend, + XENKBD_FIELD_FEAT_DSBL_POINTER, 0); - touch = xenbus_read_unsigned(dev->nodename, - XENKBD_FIELD_FEAT_MTOUCH, 0); - if (touch) { + /* Direct logic: if set, then create multi-touch device. */ + with_mtouch = xenbus_read_unsigned(dev->otherend, + XENKBD_FIELD_FEAT_MTOUCH, 0); + if (with_mtouch) { ret = xenbus_write(XBT_NIL, dev->nodename, XENKBD_FIELD_REQ_MTOUCH, "1"); if (ret) { pr_warn("xenkbd: can't request multi-touch"); - touch = 0; + with_mtouch = 0; } } /* keyboard */ - kbd = input_allocate_device(); - if (!kbd) - goto error_nomem; - kbd->name = "Xen Virtual Keyboard"; - kbd->phys = info->phys; - kbd->id.bustype = BUS_PCI; - kbd->id.vendor = 0x5853; - kbd->id.product = 0xffff; - - __set_bit(EV_KEY, kbd->evbit); - for (i = KEY_ESC; i < KEY_UNKNOWN; i++) - __set_bit(i, kbd->keybit); - for (i = KEY_OK; i < KEY_MAX; i++) - __set_bit(i, kbd->keybit); - - ret = input_register_device(kbd); - if (ret) { - input_free_device(kbd); - xenbus_dev_fatal(dev, ret, "input_register_device(kbd)"); - goto error; + if (with_kbd) { + kbd = input_allocate_device(); + if (!kbd) + goto error_nomem; + kbd->name = "Xen Virtual Keyboard"; + kbd->phys = info->phys; + kbd->id.bustype = BUS_PCI; + kbd->id.vendor = 0x5853; + kbd->id.product = 0xffff; + + __set_bit(EV_KEY, kbd->evbit); + for (i = KEY_ESC; i < KEY_UNKNOWN; i++) + __set_bit(i, kbd->keybit); + for (i = KEY_OK; i < KEY_MAX; i++) + __set_bit(i, kbd->keybit); + + ret = input_register_device(kbd); + if (ret) { + input_free_device(kbd); + xenbus_dev_fatal(dev, ret, + "input_register_device(kbd)"); + goto error; + } + info->kbd = kbd; } - info->kbd = kbd; /* pointing device */ - ptr = input_allocate_device(); - if (!ptr) - goto error_nomem; - ptr->name = "Xen Virtual Pointer"; - ptr->phys = info->phys; - ptr->id.bustype = BUS_PCI; - ptr->id.vendor = 0x5853; - ptr->id.product = 0xfffe; - - if (abs) { - __set_bit(EV_ABS, ptr->evbit); - input_set_abs_params(ptr, ABS_X, 0, ptr_size[KPARAM_X], 0, 0); - input_set_abs_params(ptr, ABS_Y, 0, ptr_size[KPARAM_Y], 0, 0); - } else { - input_set_capability(ptr, EV_REL, REL_X); - input_set_capability(ptr, EV_REL, REL_Y); - } - input_set_capability(ptr, EV_REL, REL_WHEEL); + if (with_ptr) { + unsigned int abs; + + /* Set input abs params to match backend screen res */ + abs = xenbus_read_unsigned(dev->otherend, + XENKBD_FIELD_FEAT_ABS_POINTER, 0); + ptr_size[KPARAM_X] = xenbus_read_unsigned(dev->otherend, + XENKBD_FIELD_WIDTH, + ptr_size[KPARAM_X]); + ptr_size[KPARAM_Y] = xenbus_read_unsigned(dev->otherend, + XENKBD_FIELD_HEIGHT, + ptr_size[KPARAM_Y]); + if (abs) { + ret = xenbus_write(XBT_NIL, dev->nodename, + XENKBD_FIELD_REQ_ABS_POINTER, "1"); + if (ret) { + pr_warn("xenkbd: can't request abs-pointer\n"); + abs = 0; + } + } - __set_bit(EV_KEY, ptr->evbit); - for (i = BTN_LEFT; i <= BTN_TASK; i++) - __set_bit(i, ptr->keybit); + ptr = input_allocate_device(); + if (!ptr) + goto error_nomem; + ptr->name = "Xen Virtual Pointer"; + ptr->phys = info->phys; + ptr->id.bustype = BUS_PCI; + ptr->id.vendor = 0x5853; + ptr->id.product = 0xfffe; + + if (abs) { + __set_bit(EV_ABS, ptr->evbit); + input_set_abs_params(ptr, ABS_X, 0, + ptr_size[KPARAM_X], 0, 0); + input_set_abs_params(ptr, ABS_Y, 0, + ptr_size[KPARAM_Y], 0, 0); + } else { + input_set_capability(ptr, EV_REL, REL_X); + input_set_capability(ptr, EV_REL, REL_Y); + } + input_set_capability(ptr, EV_REL, REL_WHEEL); - ret = input_register_device(ptr); - if (ret) { - input_free_device(ptr); - xenbus_dev_fatal(dev, ret, "input_register_device(ptr)"); - goto error; + __set_bit(EV_KEY, ptr->evbit); + for (i = BTN_LEFT; i <= BTN_TASK; i++) + __set_bit(i, ptr->keybit); + + ret = input_register_device(ptr); + if (ret) { + input_free_device(ptr); + xenbus_dev_fatal(dev, ret, + "input_register_device(ptr)"); + goto error; + } + info->ptr = ptr; } - info->ptr = ptr; /* multi-touch device */ - if (touch) { + if (with_mtouch) { int num_cont, width, height; mtouch = input_allocate_device(); if (!mtouch) goto error_nomem; - num_cont = xenbus_read_unsigned(info->xbdev->nodename, + num_cont = xenbus_read_unsigned(info->xbdev->otherend, XENKBD_FIELD_MT_NUM_CONTACTS, 1); - width = xenbus_read_unsigned(info->xbdev->nodename, + width = xenbus_read_unsigned(info->xbdev->otherend, XENKBD_FIELD_MT_WIDTH, XENFB_WIDTH); - height = xenbus_read_unsigned(info->xbdev->nodename, + height = xenbus_read_unsigned(info->xbdev->otherend, XENKBD_FIELD_MT_HEIGHT, XENFB_HEIGHT); @@ -346,6 +376,11 @@ static int xenkbd_probe(struct xenbus_device *dev, info->mtouch = mtouch; } + if (!(with_kbd || with_ptr || with_mtouch)) { + ret = -ENXIO; + goto error; + } + ret = xenkbd_connect_backend(dev, info); if (ret < 0) goto error; diff --git a/drivers/input/misc/yealink.c b/drivers/input/misc/yealink.c index f0c9bf87b4e3..1365cd94ed9b 100644 --- a/drivers/input/misc/yealink.c +++ b/drivers/input/misc/yealink.c @@ -894,12 +894,12 @@ static int usb_probe(struct usb_interface *intf, const struct usb_device_id *id) /* allocate usb buffers */ yld->irq_data = usb_alloc_coherent(udev, USB_PKT_LEN, - GFP_ATOMIC, &yld->irq_dma); + GFP_KERNEL, &yld->irq_dma); if (yld->irq_data == NULL) return usb_cleanup(yld, -ENOMEM); yld->ctl_data = usb_alloc_coherent(udev, USB_PKT_LEN, - GFP_ATOMIC, &yld->ctl_dma); + GFP_KERNEL, &yld->ctl_dma); if (!yld->ctl_data) return usb_cleanup(yld, -ENOMEM); diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig index 89ebb8f39fee..f27f23f2d99a 100644 --- a/drivers/input/mouse/Kconfig +++ b/drivers/input/mouse/Kconfig @@ -133,6 +133,18 @@ config MOUSE_PS2_ELANTECH If unsure, say N. +config MOUSE_PS2_ELANTECH_SMBUS + bool "Elantech PS/2 SMbus companion" if EXPERT + default y + depends on MOUSE_PS2 && MOUSE_PS2_ELANTECH + depends on I2C=y || I2C=MOUSE_PS2 + select MOUSE_PS2_SMBUS + help + Say Y here if you have a Elantech touchpad connected to + to an SMBus, but enumerated through PS/2. + + If unsure, say Y. + config MOUSE_PS2_SENTELIC bool "Sentelic Finger Sensing Pad PS/2 protocol extension" depends on MOUSE_PS2 diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index 38f9501acdf0..cb5579716dba 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -2049,14 +2049,11 @@ static int alps_hw_init_v1_v2(struct psmouse *psmouse) return 0; } -static int alps_hw_init_v6(struct psmouse *psmouse) +/* Must be in passthrough mode when calling this function */ +static int alps_trackstick_enter_extended_mode_v3_v6(struct psmouse *psmouse) { unsigned char param[2] = {0xC8, 0x14}; - /* Enter passthrough mode to let trackpoint enter 6byte raw mode */ - if (alps_passthrough_mode_v2(psmouse, true)) - return -1; - if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || @@ -2064,9 +2061,25 @@ static int alps_hw_init_v6(struct psmouse *psmouse) ps2_command(&psmouse->ps2dev, ¶m[1], PSMOUSE_CMD_SETRATE)) return -1; + return 0; +} + +static int alps_hw_init_v6(struct psmouse *psmouse) +{ + int ret; + + /* Enter passthrough mode to let trackpoint enter 6byte raw mode */ + if (alps_passthrough_mode_v2(psmouse, true)) + return -1; + + ret = alps_trackstick_enter_extended_mode_v3_v6(psmouse); + if (alps_passthrough_mode_v2(psmouse, false)) return -1; + if (ret) + return ret; + if (alps_absolute_mode_v6(psmouse)) { psmouse_err(psmouse, "Failed to enable absolute mode\n"); return -1; @@ -2140,10 +2153,18 @@ error: static int alps_setup_trackstick_v3(struct psmouse *psmouse, int reg_base) { - struct ps2dev *ps2dev = &psmouse->ps2dev; int ret = 0; + int reg_val; unsigned char param[4]; + /* + * We need to configure trackstick to report data for touchpad in + * extended format. And also we need to tell touchpad to expect data + * from trackstick in extended format. Without this configuration + * trackstick packets sent from touchpad are in basic format which is + * different from what we expect. + */ + if (alps_passthrough_mode_v3(psmouse, reg_base, true)) return -EIO; @@ -2161,39 +2182,36 @@ static int alps_setup_trackstick_v3(struct psmouse *psmouse, int reg_base) ret = -ENODEV; } else { psmouse_dbg(psmouse, "trackstick E7 report: %3ph\n", param); - - /* - * Not sure what this does, but it is absolutely - * essential. Without it, the touchpad does not - * work at all and the trackstick just emits normal - * PS/2 packets. - */ - if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || - ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || - ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || - alps_command_mode_send_nibble(psmouse, 0x9) || - alps_command_mode_send_nibble(psmouse, 0x4)) { - psmouse_err(psmouse, - "Error sending magic E6 sequence\n"); + if (alps_trackstick_enter_extended_mode_v3_v6(psmouse)) { + psmouse_err(psmouse, "Failed to enter into trackstick extended mode\n"); ret = -EIO; - goto error; } + } + + if (alps_passthrough_mode_v3(psmouse, reg_base, false)) + return -EIO; + + if (ret) + return ret; + if (alps_enter_command_mode(psmouse)) + return -EIO; + + reg_val = alps_command_mode_read_reg(psmouse, reg_base + 0x08); + if (reg_val == -1) { + ret = -EIO; + } else { /* - * This ensures the trackstick packets are in the format - * supported by this driver. If bit 1 isn't set the packet - * format is different. + * Tell touchpad that trackstick is now in extended mode. + * If bit 1 isn't set the packet format is different. */ - if (alps_enter_command_mode(psmouse) || - alps_command_mode_write_reg(psmouse, - reg_base + 0x08, 0x82) || - alps_exit_command_mode(psmouse)) + reg_val |= BIT(1); + if (__alps_command_mode_write_reg(psmouse, reg_val)) ret = -EIO; } -error: - if (alps_passthrough_mode_v3(psmouse, reg_base, false)) - ret = -EIO; + if (alps_exit_command_mode(psmouse)) + return -EIO; return ret; } diff --git a/drivers/input/mouse/appletouch.c b/drivers/input/mouse/appletouch.c index 032d27983b6c..f593ec96c95f 100644 --- a/drivers/input/mouse/appletouch.c +++ b/drivers/input/mouse/appletouch.c @@ -810,7 +810,7 @@ static int atp_open(struct input_dev *input) { struct atp *dev = input_get_drvdata(input); - if (usb_submit_urb(dev->urb, GFP_ATOMIC)) + if (usb_submit_urb(dev->urb, GFP_KERNEL)) return -EIO; dev->open = true; @@ -976,7 +976,7 @@ static int atp_recover(struct atp *dev) if (error) return error; - if (dev->open && usb_submit_urb(dev->urb, GFP_ATOMIC)) + if (dev->open && usb_submit_urb(dev->urb, GFP_KERNEL)) return -EIO; return 0; @@ -994,7 +994,7 @@ static int atp_resume(struct usb_interface *iface) { struct atp *dev = usb_get_intfdata(iface); - if (dev->open && usb_submit_urb(dev->urb, GFP_ATOMIC)) + if (dev->open && usb_submit_urb(dev->urb, GFP_KERNEL)) return -EIO; return 0; diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c index 75e757520ef0..2690a4b7ed53 100644 --- a/drivers/input/mouse/elan_i2c_core.c +++ b/drivers/input/mouse/elan_i2c_core.c @@ -36,6 +36,7 @@ #include <linux/jiffies.h> #include <linux/completion.h> #include <linux/of.h> +#include <linux/property.h> #include <linux/regulator/consumer.h> #include <asm/unaligned.h> @@ -51,6 +52,7 @@ #define ETP_MAX_FINGERS 5 #define ETP_FINGER_DATA_LEN 5 #define ETP_REPORT_ID 0x5D +#define ETP_TP_REPORT_ID 0x5E #define ETP_REPORT_ID_OFFSET 2 #define ETP_TOUCH_INFO_OFFSET 3 #define ETP_FINGER_DATA_OFFSET 4 @@ -61,6 +63,7 @@ struct elan_tp_data { struct i2c_client *client; struct input_dev *input; + struct input_dev *tp_input; /* trackpoint input node */ struct regulator *vcc; const struct elan_transport_ops *ops; @@ -930,6 +933,33 @@ static void elan_report_absolute(struct elan_tp_data *data, u8 *packet) input_sync(input); } +static void elan_report_trackpoint(struct elan_tp_data *data, u8 *report) +{ + struct input_dev *input = data->tp_input; + u8 *packet = &report[ETP_REPORT_ID_OFFSET + 1]; + int x, y; + + if (!data->tp_input) { + dev_warn_once(&data->client->dev, + "received a trackpoint report while no trackpoint device has been created. Please report upstream.\n"); + return; + } + + input_report_key(input, BTN_LEFT, packet[0] & 0x01); + input_report_key(input, BTN_RIGHT, packet[0] & 0x02); + input_report_key(input, BTN_MIDDLE, packet[0] & 0x04); + + if ((packet[3] & 0x0F) == 0x06) { + x = packet[4] - (int)((packet[1] ^ 0x80) << 1); + y = (int)((packet[2] ^ 0x80) << 1) - packet[5]; + + input_report_rel(input, REL_X, x); + input_report_rel(input, REL_Y, y); + } + + input_sync(input); +} + static irqreturn_t elan_isr(int irq, void *dev_id) { struct elan_tp_data *data = dev_id; @@ -951,11 +981,17 @@ static irqreturn_t elan_isr(int irq, void *dev_id) if (error) goto out; - if (report[ETP_REPORT_ID_OFFSET] != ETP_REPORT_ID) + switch (report[ETP_REPORT_ID_OFFSET]) { + case ETP_REPORT_ID: + elan_report_absolute(data, report); + break; + case ETP_TP_REPORT_ID: + elan_report_trackpoint(data, report); + break; + default: dev_err(dev, "invalid report id data (%x)\n", report[ETP_REPORT_ID_OFFSET]); - else - elan_report_absolute(data, report); + } out: return IRQ_HANDLED; @@ -966,6 +1002,36 @@ out: * Elan initialization functions ****************************************************************** */ + +static int elan_setup_trackpoint_input_device(struct elan_tp_data *data) +{ + struct device *dev = &data->client->dev; + struct input_dev *input; + + input = devm_input_allocate_device(dev); + if (!input) + return -ENOMEM; + + input->name = "Elan TrackPoint"; + input->id.bustype = BUS_I2C; + input->id.vendor = ELAN_VENDOR_ID; + input->id.product = data->product_id; + input_set_drvdata(input, data); + + input_set_capability(input, EV_REL, REL_X); + input_set_capability(input, EV_REL, REL_Y); + input_set_capability(input, EV_KEY, BTN_LEFT); + input_set_capability(input, EV_KEY, BTN_RIGHT); + input_set_capability(input, EV_KEY, BTN_MIDDLE); + + __set_bit(INPUT_PROP_POINTER, input->propbit); + __set_bit(INPUT_PROP_POINTING_STICK, input->propbit); + + data->tp_input = input; + + return 0; +} + static int elan_setup_input_device(struct elan_tp_data *data) { struct device *dev = &data->client->dev; @@ -1140,6 +1206,12 @@ static int elan_probe(struct i2c_client *client, if (error) return error; + if (device_property_read_bool(&client->dev, "elan,trackpoint")) { + error = elan_setup_trackpoint_input_device(data); + if (error) + return error; + } + /* * Platform code (ACPI, DTS) should normally set up interrupt * for us, but in case it did not let's fall back to using falling @@ -1177,6 +1249,16 @@ static int elan_probe(struct i2c_client *client, return error; } + if (data->tp_input) { + error = input_register_device(data->tp_input); + if (error) { + dev_err(&client->dev, + "failed to register TrackPoint input device: %d\n", + error); + return error; + } + } + /* * Systems using device tree should set up wakeup via DTS, * the rest will configure device as wakeup source by default. diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c index db47a5e1d114..fb4d902c4403 100644 --- a/drivers/input/mouse/elantech.c +++ b/drivers/input/mouse/elantech.c @@ -14,17 +14,20 @@ #include <linux/dmi.h> #include <linux/slab.h> #include <linux/module.h> +#include <linux/i2c.h> #include <linux/input.h> #include <linux/input/mt.h> +#include <linux/platform_device.h> #include <linux/serio.h> #include <linux/libps2.h> #include <asm/unaligned.h> #include "psmouse.h" #include "elantech.h" +#include "elan_i2c.h" #define elantech_debug(fmt, ...) \ do { \ - if (etd->debug) \ + if (etd->info.debug) \ psmouse_printk(KERN_DEBUG, psmouse, \ fmt, ##__VA_ARGS__); \ } while (0) @@ -105,7 +108,7 @@ static int elantech_read_reg(struct psmouse *psmouse, unsigned char reg, if (reg > 0x11 && reg < 0x20) return -1; - switch (etd->hw_version) { + switch (etd->info.hw_version) { case 1: if (ps2_sliced_command(&psmouse->ps2dev, ETP_REGISTER_READ) || ps2_sliced_command(&psmouse->ps2dev, reg) || @@ -137,7 +140,7 @@ static int elantech_read_reg(struct psmouse *psmouse, unsigned char reg, if (rc) psmouse_err(psmouse, "failed to read register 0x%02x.\n", reg); - else if (etd->hw_version != 4) + else if (etd->info.hw_version != 4) *val = param[0]; else *val = param[1]; @@ -160,7 +163,7 @@ static int elantech_write_reg(struct psmouse *psmouse, unsigned char reg, if (reg > 0x11 && reg < 0x20) return -1; - switch (etd->hw_version) { + switch (etd->info.hw_version) { case 1: if (ps2_sliced_command(&psmouse->ps2dev, ETP_REGISTER_WRITE) || ps2_sliced_command(&psmouse->ps2dev, reg) || @@ -237,7 +240,7 @@ static void elantech_report_absolute_v1(struct psmouse *psmouse) unsigned char *packet = psmouse->packet; int fingers; - if (etd->fw_version < 0x020000) { + if (etd->info.fw_version < 0x020000) { /* * byte 0: D U p1 p2 1 p3 R L * byte 1: f 0 th tw x9 x8 y9 y8 @@ -252,7 +255,7 @@ static void elantech_report_absolute_v1(struct psmouse *psmouse) fingers = (packet[0] & 0xc0) >> 6; } - if (etd->jumpy_cursor) { + if (etd->info.jumpy_cursor) { if (fingers != 1) { etd->single_finger_reports = 0; } else if (etd->single_finger_reports < 2) { @@ -282,8 +285,8 @@ static void elantech_report_absolute_v1(struct psmouse *psmouse) psmouse_report_standard_buttons(dev, packet[0]); - if (etd->fw_version < 0x020000 && - (etd->capabilities[0] & ETP_CAP_HAS_ROCKER)) { + if (etd->info.fw_version < 0x020000 && + (etd->info.capabilities[0] & ETP_CAP_HAS_ROCKER)) { /* rocker up */ input_report_key(dev, BTN_FORWARD, packet[0] & 0x40); /* rocker down */ @@ -391,7 +394,7 @@ static void elantech_report_absolute_v2(struct psmouse *psmouse) input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3); input_report_key(dev, BTN_TOOL_QUADTAP, fingers == 4); psmouse_report_standard_buttons(dev, packet[0]); - if (etd->reports_pressure) { + if (etd->info.reports_pressure) { input_report_abs(dev, ABS_PRESSURE, pres); input_report_abs(dev, ABS_TOOL_WIDTH, width); } @@ -444,7 +447,7 @@ static void elantech_report_trackpoint(struct psmouse *psmouse, default: /* Dump unexpected packet sequences if debug=1 (default) */ - if (etd->debug == 1) + if (etd->info.debug == 1) elantech_packet_dump(psmouse); break; @@ -523,7 +526,7 @@ static void elantech_report_absolute_v3(struct psmouse *psmouse, input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3); /* For clickpads map both buttons to BTN_LEFT */ - if (etd->fw_version & 0x001000) + if (etd->info.fw_version & 0x001000) input_report_key(dev, BTN_LEFT, packet[0] & 0x03); else psmouse_report_standard_buttons(dev, packet[0]); @@ -541,7 +544,7 @@ static void elantech_input_sync_v4(struct psmouse *psmouse) unsigned char *packet = psmouse->packet; /* For clickpads map both buttons to BTN_LEFT */ - if (etd->fw_version & 0x001000) + if (etd->info.fw_version & 0x001000) input_report_key(dev, BTN_LEFT, packet[0] & 0x03); else psmouse_report_standard_buttons(dev, packet[0]); @@ -669,7 +672,7 @@ static int elantech_packet_check_v1(struct psmouse *psmouse) unsigned char p1, p2, p3; /* Parity bits are placed differently */ - if (etd->fw_version < 0x020000) { + if (etd->info.fw_version < 0x020000) { /* byte 0: D U p1 p2 1 p3 R L */ p1 = (packet[0] & 0x20) >> 5; p2 = (packet[0] & 0x10) >> 4; @@ -714,7 +717,7 @@ static int elantech_packet_check_v2(struct psmouse *psmouse) * With all three cases, if the constant bits are not exactly what I * expected, I consider them invalid. */ - if (etd->reports_pressure) + if (etd->info.reports_pressure) return (packet[0] & 0x0c) == 0x04 && (packet[3] & 0x0f) == 0x02; @@ -751,7 +754,7 @@ static int elantech_packet_check_v3(struct psmouse *psmouse) * If the hardware flag 'crc_enabled' is set the packets have * different signatures. */ - if (etd->crc_enabled) { + if (etd->info.crc_enabled) { if ((packet[3] & 0x09) == 0x08) return PACKET_V3_HEAD; @@ -782,7 +785,7 @@ static int elantech_packet_check_v4(struct psmouse *psmouse) return PACKET_TRACKPOINT; /* This represents the version of IC body. */ - ic_version = (etd->fw_version & 0x0f0000) >> 16; + ic_version = (etd->info.fw_version & 0x0f0000) >> 16; /* * Sanity check based on the constant bits of a packet. @@ -791,9 +794,9 @@ static int elantech_packet_check_v4(struct psmouse *psmouse) * the IC body, but are the same for every packet, * regardless of the type. */ - if (etd->crc_enabled) + if (etd->info.crc_enabled) sanity_check = ((packet[3] & 0x08) == 0x00); - else if (ic_version == 7 && etd->samples[1] == 0x2A) + else if (ic_version == 7 && etd->info.samples[1] == 0x2A) sanity_check = ((packet[3] & 0x1c) == 0x10); else sanity_check = ((packet[0] & 0x0c) == 0x04 && @@ -827,12 +830,12 @@ static psmouse_ret_t elantech_process_byte(struct psmouse *psmouse) if (psmouse->pktcnt < psmouse->pktsize) return PSMOUSE_GOOD_DATA; - if (etd->debug > 1) + if (etd->info.debug > 1) elantech_packet_dump(psmouse); - switch (etd->hw_version) { + switch (etd->info.hw_version) { case 1: - if (etd->paritycheck && !elantech_packet_check_v1(psmouse)) + if (etd->info.paritycheck && !elantech_packet_check_v1(psmouse)) return PSMOUSE_BAD_DATA; elantech_report_absolute_v1(psmouse); @@ -843,7 +846,7 @@ static psmouse_ret_t elantech_process_byte(struct psmouse *psmouse) if (elantech_debounce_check_v2(psmouse)) return PSMOUSE_FULL_PACKET; - if (etd->paritycheck && !elantech_packet_check_v2(psmouse)) + if (etd->info.paritycheck && !elantech_packet_check_v2(psmouse)) return PSMOUSE_BAD_DATA; elantech_report_absolute_v2(psmouse); @@ -916,7 +919,7 @@ static int elantech_set_absolute_mode(struct psmouse *psmouse) int tries = ETP_READ_BACK_TRIES; int rc = 0; - switch (etd->hw_version) { + switch (etd->info.hw_version) { case 1: etd->reg_10 = 0x16; etd->reg_11 = 0x8f; @@ -939,7 +942,7 @@ static int elantech_set_absolute_mode(struct psmouse *psmouse) break; case 3: - if (etd->set_hw_resolution) + if (etd->info.set_hw_resolution) etd->reg_10 = 0x0b; else etd->reg_10 = 0x01; @@ -976,7 +979,7 @@ static int elantech_set_absolute_mode(struct psmouse *psmouse) if (rc) { psmouse_err(psmouse, "failed to read back register 0x10.\n"); - } else if (etd->hw_version == 1 && + } else if (etd->info.hw_version == 1 && !(val & ETP_R10_ABSOLUTE_MODE)) { psmouse_err(psmouse, "touchpad refuses to switch to absolute mode.\n"); @@ -997,10 +1000,11 @@ static int elantech_set_range(struct psmouse *psmouse, unsigned int *width) { struct elantech_data *etd = psmouse->private; + struct elantech_device_info *info = &etd->info; unsigned char param[3]; unsigned char traces; - switch (etd->hw_version) { + switch (info->hw_version) { case 1: *x_min = ETP_XMIN_V1; *y_min = ETP_YMIN_V1; @@ -1009,9 +1013,9 @@ static int elantech_set_range(struct psmouse *psmouse, break; case 2: - if (etd->fw_version == 0x020800 || - etd->fw_version == 0x020b00 || - etd->fw_version == 0x020030) { + if (info->fw_version == 0x020800 || + info->fw_version == 0x020b00 || + info->fw_version == 0x020030) { *x_min = ETP_XMIN_V2; *y_min = ETP_YMIN_V2; *x_max = ETP_XMAX_V2; @@ -1020,35 +1024,35 @@ static int elantech_set_range(struct psmouse *psmouse, int i; int fixed_dpi; - i = (etd->fw_version > 0x020800 && - etd->fw_version < 0x020900) ? 1 : 2; + i = (info->fw_version > 0x020800 && + info->fw_version < 0x020900) ? 1 : 2; - if (etd->send_cmd(psmouse, ETP_FW_ID_QUERY, param)) + if (info->send_cmd(psmouse, ETP_FW_ID_QUERY, param)) return -1; fixed_dpi = param[1] & 0x10; - if (((etd->fw_version >> 16) == 0x14) && fixed_dpi) { - if (etd->send_cmd(psmouse, ETP_SAMPLE_QUERY, param)) + if (((info->fw_version >> 16) == 0x14) && fixed_dpi) { + if (info->send_cmd(psmouse, ETP_SAMPLE_QUERY, param)) return -1; - *x_max = (etd->capabilities[1] - i) * param[1] / 2; - *y_max = (etd->capabilities[2] - i) * param[2] / 2; - } else if (etd->fw_version == 0x040216) { + *x_max = (info->capabilities[1] - i) * param[1] / 2; + *y_max = (info->capabilities[2] - i) * param[2] / 2; + } else if (info->fw_version == 0x040216) { *x_max = 819; *y_max = 405; - } else if (etd->fw_version == 0x040219 || etd->fw_version == 0x040215) { + } else if (info->fw_version == 0x040219 || info->fw_version == 0x040215) { *x_max = 900; *y_max = 500; } else { - *x_max = (etd->capabilities[1] - i) * 64; - *y_max = (etd->capabilities[2] - i) * 64; + *x_max = (info->capabilities[1] - i) * 64; + *y_max = (info->capabilities[2] - i) * 64; } } break; case 3: - if (etd->send_cmd(psmouse, ETP_FW_ID_QUERY, param)) + if (info->send_cmd(psmouse, ETP_FW_ID_QUERY, param)) return -1; *x_max = (0x0f & param[0]) << 8 | param[1]; @@ -1056,12 +1060,12 @@ static int elantech_set_range(struct psmouse *psmouse, break; case 4: - if (etd->send_cmd(psmouse, ETP_FW_ID_QUERY, param)) + if (info->send_cmd(psmouse, ETP_FW_ID_QUERY, param)) return -1; *x_max = (0x0f & param[0]) << 8 | param[1]; *y_max = (0xf0 & param[0]) << 4 | param[2]; - traces = etd->capabilities[1]; + traces = info->capabilities[1]; if ((traces < 2) || (traces > *x_max)) return -1; @@ -1083,7 +1087,8 @@ static unsigned int elantech_convert_res(unsigned int val) static int elantech_get_resolution_v4(struct psmouse *psmouse, unsigned int *x_res, - unsigned int *y_res) + unsigned int *y_res, + unsigned int *bus) { unsigned char param[3]; @@ -1092,6 +1097,7 @@ static int elantech_get_resolution_v4(struct psmouse *psmouse, *x_res = elantech_convert_res(param[1] & 0x0f); *y_res = elantech_convert_res((param[1] & 0xf0) >> 4); + *bus = param[2]; return 0; } @@ -1140,7 +1146,7 @@ static void elantech_set_buttonpad_prop(struct psmouse *psmouse) struct input_dev *dev = psmouse->dev; struct elantech_data *etd = psmouse->private; - if (etd->fw_version & 0x001000) { + if (etd->info.fw_version & 0x001000) { __set_bit(INPUT_PROP_BUTTONPAD, dev->propbit); __clear_bit(BTN_RIGHT, dev->keybit); } @@ -1176,8 +1182,8 @@ static int elantech_set_input_params(struct psmouse *psmouse) { struct input_dev *dev = psmouse->dev; struct elantech_data *etd = psmouse->private; + struct elantech_device_info *info = &etd->info; unsigned int x_min = 0, y_min = 0, x_max = 0, y_max = 0, width = 0; - unsigned int x_res = 31, y_res = 31; if (elantech_set_range(psmouse, &x_min, &y_min, &x_max, &y_max, &width)) return -1; @@ -1197,11 +1203,11 @@ static int elantech_set_input_params(struct psmouse *psmouse) __set_bit(BTN_TOOL_DOUBLETAP, dev->keybit); __set_bit(BTN_TOOL_TRIPLETAP, dev->keybit); - switch (etd->hw_version) { + switch (info->hw_version) { case 1: /* Rocker button */ - if (etd->fw_version < 0x020000 && - (etd->capabilities[0] & ETP_CAP_HAS_ROCKER)) { + if (info->fw_version < 0x020000 && + (info->capabilities[0] & ETP_CAP_HAS_ROCKER)) { __set_bit(BTN_FORWARD, dev->keybit); __set_bit(BTN_BACK, dev->keybit); } @@ -1214,11 +1220,11 @@ static int elantech_set_input_params(struct psmouse *psmouse) __set_bit(INPUT_PROP_SEMI_MT, dev->propbit); /* fall through */ case 3: - if (etd->hw_version == 3) + if (info->hw_version == 3) elantech_set_buttonpad_prop(psmouse); input_set_abs_params(dev, ABS_X, x_min, x_max, 0, 0); input_set_abs_params(dev, ABS_Y, y_min, y_max, 0, 0); - if (etd->reports_pressure) { + if (info->reports_pressure) { input_set_abs_params(dev, ABS_PRESSURE, ETP_PMIN_V2, ETP_PMAX_V2, 0, 0); input_set_abs_params(dev, ABS_TOOL_WIDTH, ETP_WMIN_V2, @@ -1230,13 +1236,6 @@ static int elantech_set_input_params(struct psmouse *psmouse) break; case 4: - if (elantech_get_resolution_v4(psmouse, &x_res, &y_res)) { - /* - * if query failed, print a warning and leave the values - * zero to resemble synaptics.c behavior. - */ - psmouse_warn(psmouse, "couldn't query resolution data.\n"); - } elantech_set_buttonpad_prop(psmouse); __set_bit(BTN_TOOL_QUADTAP, dev->keybit); /* For X to recognize me as touchpad. */ @@ -1265,11 +1264,11 @@ static int elantech_set_input_params(struct psmouse *psmouse) break; } - input_abs_set_res(dev, ABS_X, x_res); - input_abs_set_res(dev, ABS_Y, y_res); - if (etd->hw_version > 1) { - input_abs_set_res(dev, ABS_MT_POSITION_X, x_res); - input_abs_set_res(dev, ABS_MT_POSITION_Y, y_res); + input_abs_set_res(dev, ABS_X, info->x_res); + input_abs_set_res(dev, ABS_Y, info->y_res); + if (info->hw_version > 1) { + input_abs_set_res(dev, ABS_MT_POSITION_X, info->x_res); + input_abs_set_res(dev, ABS_MT_POSITION_Y, info->y_res); } etd->y_max = y_max; @@ -1317,7 +1316,7 @@ static ssize_t elantech_set_int_attr(struct psmouse *psmouse, return err; /* Do we need to preserve some bits for version 2 hardware too? */ - if (etd->hw_version == 1) { + if (etd->info.hw_version == 1) { if (attr->reg == 0x10) /* Force absolute mode always on */ value |= ETP_R10_ABSOLUTE_MODE; @@ -1337,11 +1336,22 @@ static ssize_t elantech_set_int_attr(struct psmouse *psmouse, .field_offset = offsetof(struct elantech_data, _name), \ .reg = _register, \ }; \ - PSMOUSE_DEFINE_ATTR(_name, S_IWUSR | S_IRUGO, \ + PSMOUSE_DEFINE_ATTR(_name, 0644, \ &elantech_attr_##_name, \ elantech_show_int_attr, \ elantech_set_int_attr) +#define ELANTECH_INFO_ATTR(_name) \ + static struct elantech_attr_data elantech_attr_##_name = { \ + .field_offset = offsetof(struct elantech_data, info) + \ + offsetof(struct elantech_device_info, _name), \ + .reg = 0, \ + }; \ + PSMOUSE_DEFINE_ATTR(_name, 0644, \ + &elantech_attr_##_name, \ + elantech_show_int_attr, \ + elantech_set_int_attr) + ELANTECH_INT_ATTR(reg_07, 0x07); ELANTECH_INT_ATTR(reg_10, 0x10); ELANTECH_INT_ATTR(reg_11, 0x11); @@ -1352,9 +1362,9 @@ ELANTECH_INT_ATTR(reg_23, 0x23); ELANTECH_INT_ATTR(reg_24, 0x24); ELANTECH_INT_ATTR(reg_25, 0x25); ELANTECH_INT_ATTR(reg_26, 0x26); -ELANTECH_INT_ATTR(debug, 0); -ELANTECH_INT_ATTR(paritycheck, 0); -ELANTECH_INT_ATTR(crc_enabled, 0); +ELANTECH_INFO_ATTR(debug); +ELANTECH_INFO_ATTR(paritycheck); +ELANTECH_INFO_ATTR(crc_enabled); static struct attribute *elantech_attrs[] = { &psmouse_attr_reg_07.dattr.attr, @@ -1469,6 +1479,12 @@ static void elantech_disconnect(struct psmouse *psmouse) { struct elantech_data *etd = psmouse->private; + /* + * We might have left a breadcrumb when trying to + * set up SMbus companion. + */ + psmouse_smbus_cleanup(psmouse); + if (etd->tp_dev) input_unregister_device(etd->tp_dev); sysfs_remove_group(&psmouse->ps2dev.serio->dev.kobj, @@ -1588,25 +1604,25 @@ static const struct dmi_system_id no_hw_res_dmi_table[] = { /* * determine hardware version and set some properties according to it. */ -static int elantech_set_properties(struct elantech_data *etd) +static int elantech_set_properties(struct elantech_device_info *info) { /* This represents the version of IC body. */ - int ver = (etd->fw_version & 0x0f0000) >> 16; + int ver = (info->fw_version & 0x0f0000) >> 16; /* Early version of Elan touchpads doesn't obey the rule. */ - if (etd->fw_version < 0x020030 || etd->fw_version == 0x020600) - etd->hw_version = 1; + if (info->fw_version < 0x020030 || info->fw_version == 0x020600) + info->hw_version = 1; else { switch (ver) { case 2: case 4: - etd->hw_version = 2; + info->hw_version = 2; break; case 5: - etd->hw_version = 3; + info->hw_version = 3; break; case 6 ... 15: - etd->hw_version = 4; + info->hw_version = 4; break; default: return -1; @@ -1614,100 +1630,88 @@ static int elantech_set_properties(struct elantech_data *etd) } /* decide which send_cmd we're gonna use early */ - etd->send_cmd = etd->hw_version >= 3 ? elantech_send_cmd : - synaptics_send_cmd; + info->send_cmd = info->hw_version >= 3 ? elantech_send_cmd : + synaptics_send_cmd; /* Turn on packet checking by default */ - etd->paritycheck = 1; + info->paritycheck = 1; /* * This firmware suffers from misreporting coordinates when * a touch action starts causing the mouse cursor or scrolled page * to jump. Enable a workaround. */ - etd->jumpy_cursor = - (etd->fw_version == 0x020022 || etd->fw_version == 0x020600); + info->jumpy_cursor = + (info->fw_version == 0x020022 || info->fw_version == 0x020600); - if (etd->hw_version > 1) { + if (info->hw_version > 1) { /* For now show extra debug information */ - etd->debug = 1; + info->debug = 1; - if (etd->fw_version >= 0x020800) - etd->reports_pressure = true; + if (info->fw_version >= 0x020800) + info->reports_pressure = true; } /* * The signatures of v3 and v4 packets change depending on the * value of this hardware flag. */ - etd->crc_enabled = (etd->fw_version & 0x4000) == 0x4000 || - dmi_check_system(elantech_dmi_force_crc_enabled); + info->crc_enabled = (info->fw_version & 0x4000) == 0x4000 || + dmi_check_system(elantech_dmi_force_crc_enabled); /* Enable real hardware resolution on hw_version 3 ? */ - etd->set_hw_resolution = !dmi_check_system(no_hw_res_dmi_table); + info->set_hw_resolution = !dmi_check_system(no_hw_res_dmi_table); return 0; } -/* - * Initialize the touchpad and create sysfs entries - */ -int elantech_init(struct psmouse *psmouse) +static int elantech_query_info(struct psmouse *psmouse, + struct elantech_device_info *info) { - struct elantech_data *etd; - int i; - int error = -EINVAL; unsigned char param[3]; - struct input_dev *tp_dev; - psmouse->private = etd = kzalloc(sizeof(struct elantech_data), GFP_KERNEL); - if (!etd) - return -ENOMEM; - - psmouse_reset(psmouse); - - etd->parity[0] = 1; - for (i = 1; i < 256; i++) - etd->parity[i] = etd->parity[i & (i - 1)] ^ 1; + memset(info, 0, sizeof(*info)); /* * Do the version query again so we can store the result */ if (synaptics_send_cmd(psmouse, ETP_FW_VERSION_QUERY, param)) { psmouse_err(psmouse, "failed to query firmware version.\n"); - goto init_fail; + return -EINVAL; } - etd->fw_version = (param[0] << 16) | (param[1] << 8) | param[2]; + info->fw_version = (param[0] << 16) | (param[1] << 8) | param[2]; - if (elantech_set_properties(etd)) { + if (elantech_set_properties(info)) { psmouse_err(psmouse, "unknown hardware version, aborting...\n"); - goto init_fail; + return -EINVAL; } psmouse_info(psmouse, "assuming hardware version %d (with firmware version 0x%02x%02x%02x)\n", - etd->hw_version, param[0], param[1], param[2]); + info->hw_version, param[0], param[1], param[2]); - if (etd->send_cmd(psmouse, ETP_CAPABILITIES_QUERY, - etd->capabilities)) { + if (info->send_cmd(psmouse, ETP_CAPABILITIES_QUERY, + info->capabilities)) { psmouse_err(psmouse, "failed to query capabilities.\n"); - goto init_fail; + return -EINVAL; } psmouse_info(psmouse, "Synaptics capabilities query result 0x%02x, 0x%02x, 0x%02x.\n", - etd->capabilities[0], etd->capabilities[1], - etd->capabilities[2]); + info->capabilities[0], info->capabilities[1], + info->capabilities[2]); - if (etd->hw_version != 1) { - if (etd->send_cmd(psmouse, ETP_SAMPLE_QUERY, etd->samples)) { + if (info->hw_version != 1) { + if (info->send_cmd(psmouse, ETP_SAMPLE_QUERY, info->samples)) { psmouse_err(psmouse, "failed to query sample data\n"); - goto init_fail; + return -EINVAL; } psmouse_info(psmouse, "Elan sample query result %02x, %02x, %02x\n", - etd->samples[0], etd->samples[1], etd->samples[2]); + info->samples[0], + info->samples[1], + info->samples[2]); } - if (etd->samples[1] == 0x74 && etd->hw_version == 0x03) { + if (info->samples[1] == 0x74 && info->hw_version == 0x03) { /* * This module has a bug which makes absolute mode * unusable, so let's abort so we'll be using standard @@ -1715,16 +1719,181 @@ int elantech_init(struct psmouse *psmouse) */ psmouse_info(psmouse, "absolute mode broken, forcing standard PS/2 protocol\n"); + return -ENODEV; + } + + /* The MSB indicates the presence of the trackpoint */ + info->has_trackpoint = (info->capabilities[0] & 0x80) == 0x80; + + info->x_res = 31; + info->y_res = 31; + if (info->hw_version == 4) { + if (elantech_get_resolution_v4(psmouse, + &info->x_res, + &info->y_res, + &info->bus)) { + psmouse_warn(psmouse, + "failed to query resolution data.\n"); + } + } + + return 0; +} + +#if defined(CONFIG_MOUSE_PS2_ELANTECH_SMBUS) + +/* + * The newest Elantech device can use a secondary bus (over SMBus) which + * provides a better bandwidth and allow a better control of the touchpads. + * This is used to decide if we need to use this bus or not. + */ +enum { + ELANTECH_SMBUS_NOT_SET = -1, + ELANTECH_SMBUS_OFF, + ELANTECH_SMBUS_ON, +}; + +static int elantech_smbus = IS_ENABLED(CONFIG_MOUSE_ELAN_I2C_SMBUS) ? + ELANTECH_SMBUS_NOT_SET : ELANTECH_SMBUS_OFF; +module_param_named(elantech_smbus, elantech_smbus, int, 0644); +MODULE_PARM_DESC(elantech_smbus, "Use a secondary bus for the Elantech device."); + +static int elantech_create_smbus(struct psmouse *psmouse, + struct elantech_device_info *info, + bool leave_breadcrumbs) +{ + const struct property_entry i2c_properties[] = { + PROPERTY_ENTRY_BOOL("elan,trackpoint"), + { }, + }; + struct i2c_board_info smbus_board = { + I2C_BOARD_INFO("elan_i2c", 0x15), + .flags = I2C_CLIENT_HOST_NOTIFY, + }; + + if (info->has_trackpoint) + smbus_board.properties = i2c_properties; + + return psmouse_smbus_init(psmouse, &smbus_board, NULL, 0, false, + leave_breadcrumbs); +} + +/** + * elantech_setup_smbus - called once the PS/2 devices are enumerated + * and decides to instantiate a SMBus InterTouch device. + */ +static int elantech_setup_smbus(struct psmouse *psmouse, + struct elantech_device_info *info, + bool leave_breadcrumbs) +{ + int error; + + if (elantech_smbus == ELANTECH_SMBUS_OFF) + return -ENXIO; + + if (elantech_smbus == ELANTECH_SMBUS_NOT_SET) { + /* + * New ICs are enabled by default. + * Old ICs are up to the user to decide. + */ + if (!ETP_NEW_IC_SMBUS_HOST_NOTIFY(info->fw_version)) + return -ENXIO; + } + + psmouse_info(psmouse, "Trying to set up SMBus access\n"); + + error = elantech_create_smbus(psmouse, info, leave_breadcrumbs); + if (error) { + if (error == -EAGAIN) + psmouse_info(psmouse, "SMbus companion is not ready yet\n"); + else + psmouse_err(psmouse, "unable to create intertouch device\n"); + + return error; + } + + return 0; +} + +static bool elantech_use_host_notify(struct psmouse *psmouse, + struct elantech_device_info *info) +{ + if (ETP_NEW_IC_SMBUS_HOST_NOTIFY(info->fw_version)) + return true; + + switch (info->bus) { + case ETP_BUS_PS2_ONLY: + /* expected case */ + break; + case ETP_BUS_SMB_ALERT_ONLY: + /* fall-through */ + case ETP_BUS_PS2_SMB_ALERT: + psmouse_dbg(psmouse, "Ignoring SMBus provider through alert protocol.\n"); + break; + case ETP_BUS_SMB_HST_NTFY_ONLY: + /* fall-through */ + case ETP_BUS_PS2_SMB_HST_NTFY: + return true; + default: + psmouse_dbg(psmouse, + "Ignoring SMBus bus provider %d.\n", + info->bus); + } + + return false; +} + +int elantech_init_smbus(struct psmouse *psmouse) +{ + struct elantech_device_info info; + int error = -EINVAL; + + psmouse_reset(psmouse); + + error = elantech_query_info(psmouse, &info); + if (error) + goto init_fail; + + if (info.hw_version < 4) { + error = -ENXIO; goto init_fail; } + return elantech_create_smbus(psmouse, &info, false); + init_fail: + psmouse_reset(psmouse); + return error; +} +#endif /* CONFIG_MOUSE_PS2_ELANTECH_SMBUS */ + +/* + * Initialize the touchpad and create sysfs entries + */ +static int elantech_setup_ps2(struct psmouse *psmouse, + struct elantech_device_info *info) +{ + struct elantech_data *etd; + int i; + int error = -EINVAL; + struct input_dev *tp_dev; + + psmouse->private = etd = kzalloc(sizeof(*etd), GFP_KERNEL); + if (!etd) + return -ENOMEM; + + etd->info = *info; + + etd->parity[0] = 1; + for (i = 1; i < 256; i++) + etd->parity[i] = etd->parity[i & (i - 1)] ^ 1; + if (elantech_set_absolute_mode(psmouse)) { psmouse_err(psmouse, "failed to put touchpad into absolute mode.\n"); goto init_fail; } - if (etd->fw_version == 0x381f17) { + if (info->fw_version == 0x381f17) { etd->original_set_rate = psmouse->set_rate; psmouse->set_rate = elantech_set_rate_restore_reg_07; } @@ -1743,8 +1912,7 @@ int elantech_init(struct psmouse *psmouse) goto init_fail; } - /* The MSB indicates the presence of the trackpoint */ - if ((etd->capabilities[0] & 0x80) == 0x80) { + if (info->has_trackpoint) { tp_dev = input_allocate_device(); if (!tp_dev) { @@ -1780,7 +1948,7 @@ int elantech_init(struct psmouse *psmouse) psmouse->protocol_handler = elantech_process_byte; psmouse->disconnect = elantech_disconnect; psmouse->reconnect = elantech_reconnect; - psmouse->pktsize = etd->hw_version > 1 ? 6 : 4; + psmouse->pktsize = info->hw_version > 1 ? 6 : 4; return 0; init_fail_tp_reg: @@ -1789,7 +1957,70 @@ int elantech_init(struct psmouse *psmouse) sysfs_remove_group(&psmouse->ps2dev.serio->dev.kobj, &elantech_attr_group); init_fail: - psmouse_reset(psmouse); kfree(etd); return error; } + +int elantech_init_ps2(struct psmouse *psmouse) +{ + struct elantech_device_info info; + int error = -EINVAL; + + psmouse_reset(psmouse); + + error = elantech_query_info(psmouse, &info); + if (error) + goto init_fail; + + error = elantech_setup_ps2(psmouse, &info); + if (error) + goto init_fail; + + return 0; + init_fail: + psmouse_reset(psmouse); + return error; +} + +int elantech_init(struct psmouse *psmouse) +{ + struct elantech_device_info info; + int error = -EINVAL; + + psmouse_reset(psmouse); + + error = elantech_query_info(psmouse, &info); + if (error) + goto init_fail; + +#if defined(CONFIG_MOUSE_PS2_ELANTECH_SMBUS) + + if (elantech_use_host_notify(psmouse, &info)) { + if (!IS_ENABLED(CONFIG_MOUSE_ELAN_I2C_SMBUS) || + !IS_ENABLED(CONFIG_MOUSE_PS2_ELANTECH_SMBUS)) { + psmouse_warn(psmouse, + "The touchpad can support a better bus than the too old PS/2 protocol. " + "Make sure MOUSE_PS2_ELANTECH_SMBUS and MOUSE_ELAN_I2C_SMBUS are enabled to get a better touchpad experience.\n"); + } + error = elantech_setup_smbus(psmouse, &info, true); + if (!error) + return PSMOUSE_ELANTECH_SMBUS; + } + +#endif /* CONFIG_MOUSE_PS2_ELANTECH_SMBUS */ + + error = elantech_setup_ps2(psmouse, &info); + if (error < 0) { + /* + * Not using any flavor of Elantech support, so clean up + * SMbus breadcrumbs, if any. + */ + psmouse_smbus_cleanup(psmouse); + goto init_fail; + } + + return PSMOUSE_ELANTECH; + init_fail: + psmouse_reset(psmouse); + return error; +} diff --git a/drivers/input/mouse/elantech.h b/drivers/input/mouse/elantech.h index e1cbf409d9c8..119727085a60 100644 --- a/drivers/input/mouse/elantech.h +++ b/drivers/input/mouse/elantech.h @@ -107,6 +107,30 @@ #define ETP_WEIGHT_VALUE 5 /* + * Bus information on 3rd byte of query ETP_RESOLUTION_QUERY(0x04) + */ +#define ETP_BUS_PS2_ONLY 0 +#define ETP_BUS_SMB_ALERT_ONLY 1 +#define ETP_BUS_SMB_HST_NTFY_ONLY 2 +#define ETP_BUS_PS2_SMB_ALERT 3 +#define ETP_BUS_PS2_SMB_HST_NTFY 4 + +/* + * New ICs are either using SMBus Host Notify or just plain PS2. + * + * ETP_FW_VERSION_QUERY is: + * Byte 1: + * - bit 0..3: IC BODY + * Byte 2: + * - bit 4: HiddenButton + * - bit 5: PS2_SMBUS_NOTIFY + * - bit 6: PS2CRCCheck + */ +#define ETP_NEW_IC_SMBUS_HOST_NOTIFY(fw_version) \ + ((((fw_version) & 0x0f2000) == 0x0f2000) && \ + ((fw_version) & 0x0000ff) > 0) + +/* * The base position for one finger, v4 hardware */ struct finger_pos { @@ -114,6 +138,25 @@ struct finger_pos { unsigned int y; }; +struct elantech_device_info { + unsigned char capabilities[3]; + unsigned char samples[3]; + unsigned char debug; + unsigned char hw_version; + unsigned int fw_version; + unsigned int x_res; + unsigned int y_res; + unsigned int bus; + bool paritycheck; + bool jumpy_cursor; + bool reports_pressure; + bool crc_enabled; + bool set_hw_resolution; + bool has_trackpoint; + int (*send_cmd)(struct psmouse *psmouse, unsigned char c, + unsigned char *param); +}; + struct elantech_data { struct input_dev *tp_dev; /* Relative device for trackpoint */ char tp_phys[32]; @@ -127,27 +170,18 @@ struct elantech_data { unsigned char reg_24; unsigned char reg_25; unsigned char reg_26; - unsigned char debug; - unsigned char capabilities[3]; - unsigned char samples[3]; - bool paritycheck; - bool jumpy_cursor; - bool reports_pressure; - bool crc_enabled; - bool set_hw_resolution; - unsigned char hw_version; - unsigned int fw_version; unsigned int single_finger_reports; unsigned int y_max; unsigned int width; struct finger_pos mt[ETP_MAX_FINGERS]; unsigned char parity[256]; - int (*send_cmd)(struct psmouse *psmouse, unsigned char c, unsigned char *param); + struct elantech_device_info info; void (*original_set_rate)(struct psmouse *psmouse, unsigned int rate); }; #ifdef CONFIG_MOUSE_PS2_ELANTECH int elantech_detect(struct psmouse *psmouse, bool set_properties); +int elantech_init_ps2(struct psmouse *psmouse); int elantech_init(struct psmouse *psmouse); #else static inline int elantech_detect(struct psmouse *psmouse, bool set_properties) @@ -158,6 +192,19 @@ static inline int elantech_init(struct psmouse *psmouse) { return -ENOSYS; } +static inline int elantech_init_ps2(struct psmouse *psmouse) +{ + return -ENOSYS; +} #endif /* CONFIG_MOUSE_PS2_ELANTECH */ +#if defined(CONFIG_MOUSE_PS2_ELANTECH_SMBUS) +int elantech_init_smbus(struct psmouse *psmouse); +#else +static inline int elantech_init_smbus(struct psmouse *psmouse) +{ + return -ENOSYS; +} +#endif /* CONFIG_MOUSE_PS2_ELANTECH_SMBUS */ + #endif diff --git a/drivers/input/mouse/inport.c b/drivers/input/mouse/inport.c index 9ce71dfa0de1..b9e68606c44a 100644 --- a/drivers/input/mouse/inport.c +++ b/drivers/input/mouse/inport.c @@ -26,10 +26,6 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: - * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include <linux/module.h> diff --git a/drivers/input/mouse/logibm.c b/drivers/input/mouse/logibm.c index 6f165e053f4d..2fd6c84cd5b7 100644 --- a/drivers/input/mouse/logibm.c +++ b/drivers/input/mouse/logibm.c @@ -27,10 +27,6 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: - * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include <linux/module.h> diff --git a/drivers/input/mouse/pc110pad.c b/drivers/input/mouse/pc110pad.c index 7b02b652e267..b8965e6bc890 100644 --- a/drivers/input/mouse/pc110pad.c +++ b/drivers/input/mouse/pc110pad.c @@ -23,10 +23,6 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: - * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include <linux/module.h> diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index 8900c3166ebf..5ff5b1952be0 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -856,7 +856,17 @@ static const struct psmouse_protocol psmouse_protocols[] = { .name = "ETPS/2", .alias = "elantech", .detect = elantech_detect, - .init = elantech_init, + .init = elantech_init_ps2, + }, +#endif +#ifdef CONFIG_MOUSE_PS2_ELANTECH_SMBUS + { + .type = PSMOUSE_ELANTECH_SMBUS, + .name = "ETSMBus", + .alias = "elantech-smbus", + .detect = elantech_detect, + .init = elantech_init_smbus, + .smbus_companion = true, }, #endif #ifdef CONFIG_MOUSE_PS2_SENTELIC @@ -1158,8 +1168,13 @@ static int psmouse_extensions(struct psmouse *psmouse, /* Try Elantech touchpad */ if (max_proto > PSMOUSE_IMEX && psmouse_try_protocol(psmouse, PSMOUSE_ELANTECH, - &max_proto, set_properties, true)) { - return PSMOUSE_ELANTECH; + &max_proto, set_properties, false)) { + if (!set_properties) + return PSMOUSE_ELANTECH; + + ret = elantech_init(psmouse); + if (ret >= 0) + return ret; } if (max_proto > PSMOUSE_IMEX) { diff --git a/drivers/input/mouse/psmouse-smbus.c b/drivers/input/mouse/psmouse-smbus.c index c7ac24d119c1..852d4b486ddb 100644 --- a/drivers/input/mouse/psmouse-smbus.c +++ b/drivers/input/mouse/psmouse-smbus.c @@ -23,6 +23,7 @@ struct psmouse_smbus_dev { struct i2c_client *client; struct list_head node; bool dead; + bool need_deactivate; }; static LIST_HEAD(psmouse_smbus_list); @@ -118,7 +119,10 @@ static psmouse_ret_t psmouse_smbus_process_byte(struct psmouse *psmouse) static int psmouse_smbus_reconnect(struct psmouse *psmouse) { - psmouse_deactivate(psmouse); + struct psmouse_smbus_dev *smbdev = psmouse->private; + + if (smbdev->need_deactivate) + psmouse_deactivate(psmouse); return 0; } @@ -225,6 +229,7 @@ void psmouse_smbus_cleanup(struct psmouse *psmouse) int psmouse_smbus_init(struct psmouse *psmouse, const struct i2c_board_info *board, const void *pdata, size_t pdata_size, + bool need_deactivate, bool leave_breadcrumbs) { struct psmouse_smbus_dev *smbdev; @@ -236,13 +241,20 @@ int psmouse_smbus_init(struct psmouse *psmouse, smbdev->psmouse = psmouse; smbdev->board = *board; + smbdev->need_deactivate = need_deactivate; - smbdev->board.platform_data = kmemdup(pdata, pdata_size, GFP_KERNEL); - if (!smbdev->board.platform_data) { - kfree(smbdev); - return -ENOMEM; + if (pdata) { + smbdev->board.platform_data = kmemdup(pdata, pdata_size, + GFP_KERNEL); + if (!smbdev->board.platform_data) { + kfree(smbdev); + return -ENOMEM; + } } + if (need_deactivate) + psmouse_deactivate(psmouse); + psmouse->private = smbdev; psmouse->protocol_handler = psmouse_smbus_process_byte; psmouse->reconnect = psmouse_smbus_reconnect; @@ -250,8 +262,6 @@ int psmouse_smbus_init(struct psmouse *psmouse, psmouse->disconnect = psmouse_smbus_disconnect; psmouse->resync_time = 0; - psmouse_deactivate(psmouse); - mutex_lock(&psmouse_smbus_mutex); list_add_tail(&smbdev->node, &psmouse_smbus_list); mutex_unlock(&psmouse_smbus_mutex); diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h index 71ac50082c8b..64c3a5d3fb3e 100644 --- a/drivers/input/mouse/psmouse.h +++ b/drivers/input/mouse/psmouse.h @@ -68,6 +68,7 @@ enum psmouse_type { PSMOUSE_VMMOUSE, PSMOUSE_BYD, PSMOUSE_SYNAPTICS_SMBUS, + PSMOUSE_ELANTECH_SMBUS, PSMOUSE_AUTO /* This one should always be last */ }; @@ -224,6 +225,7 @@ struct i2c_board_info; int psmouse_smbus_init(struct psmouse *psmouse, const struct i2c_board_info *board, const void *pdata, size_t pdata_size, + bool need_deactivate, bool leave_breadcrumbs); void psmouse_smbus_cleanup(struct psmouse *psmouse); diff --git a/drivers/input/mouse/sermouse.c b/drivers/input/mouse/sermouse.c index 8df526620ebf..d27816ac038f 100644 --- a/drivers/input/mouse/sermouse.c +++ b/drivers/input/mouse/sermouse.c @@ -20,10 +20,6 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: - * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include <linux/delay.h> diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index a9591d278145..55d33500d55e 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -1754,7 +1754,7 @@ static int synaptics_create_intertouch(struct psmouse *psmouse, }; return psmouse_smbus_init(psmouse, &intertouch_board, - &pdata, sizeof(pdata), + &pdata, sizeof(pdata), true, leave_breadcrumbs); } diff --git a/drivers/input/rmi4/Kconfig b/drivers/input/rmi4/Kconfig index 7172b88cd064..fad2eae4a118 100644 --- a/drivers/input/rmi4/Kconfig +++ b/drivers/input/rmi4/Kconfig @@ -3,6 +3,7 @@ # config RMI4_CORE tristate "Synaptics RMI4 bus support" + select IRQ_DOMAIN help Say Y here if you want to support the Synaptics RMI4 bus. This is required for all RMI4 device support. diff --git a/drivers/input/rmi4/rmi_2d_sensor.c b/drivers/input/rmi4/rmi_2d_sensor.c index 8bb866c7b985..8eeffa066022 100644 --- a/drivers/input/rmi4/rmi_2d_sensor.c +++ b/drivers/input/rmi4/rmi_2d_sensor.c @@ -32,15 +32,15 @@ void rmi_2d_sensor_abs_process(struct rmi_2d_sensor *sensor, if (obj->type == RMI_2D_OBJECT_NONE) return; - if (axis_align->swap_axes) - swap(obj->x, obj->y); - if (axis_align->flip_x) obj->x = sensor->max_x - obj->x; if (axis_align->flip_y) obj->y = sensor->max_y - obj->y; + if (axis_align->swap_axes) + swap(obj->x, obj->y); + /* * Here checking if X offset or y offset are specified is * redundant. We just add the offsets or clip the values. @@ -120,15 +120,15 @@ void rmi_2d_sensor_rel_report(struct rmi_2d_sensor *sensor, int x, int y) x = min(RMI_2D_REL_POS_MAX, max(RMI_2D_REL_POS_MIN, (int)x)); y = min(RMI_2D_REL_POS_MAX, max(RMI_2D_REL_POS_MIN, (int)y)); - if (axis_align->swap_axes) - swap(x, y); - if (axis_align->flip_x) x = min(RMI_2D_REL_POS_MAX, -x); if (axis_align->flip_y) y = min(RMI_2D_REL_POS_MAX, -y); + if (axis_align->swap_axes) + swap(x, y); + if (x || y) { input_report_rel(sensor->input, REL_X, x); input_report_rel(sensor->input, REL_Y, y); @@ -141,17 +141,10 @@ static void rmi_2d_sensor_set_input_params(struct rmi_2d_sensor *sensor) struct input_dev *input = sensor->input; int res_x; int res_y; + int max_x, max_y; int input_flags = 0; if (sensor->report_abs) { - if (sensor->axis_align.swap_axes) { - swap(sensor->max_x, sensor->max_y); - swap(sensor->axis_align.clip_x_low, - sensor->axis_align.clip_y_low); - swap(sensor->axis_align.clip_x_high, - sensor->axis_align.clip_y_high); - } - sensor->min_x = sensor->axis_align.clip_x_low; if (sensor->axis_align.clip_x_high) sensor->max_x = min(sensor->max_x, @@ -163,14 +156,19 @@ static void rmi_2d_sensor_set_input_params(struct rmi_2d_sensor *sensor) sensor->axis_align.clip_y_high); set_bit(EV_ABS, input->evbit); - input_set_abs_params(input, ABS_MT_POSITION_X, 0, sensor->max_x, - 0, 0); - input_set_abs_params(input, ABS_MT_POSITION_Y, 0, sensor->max_y, - 0, 0); + + max_x = sensor->max_x; + max_y = sensor->max_y; + if (sensor->axis_align.swap_axes) + swap(max_x, max_y); + input_set_abs_params(input, ABS_MT_POSITION_X, 0, max_x, 0, 0); + input_set_abs_params(input, ABS_MT_POSITION_Y, 0, max_y, 0, 0); if (sensor->x_mm && sensor->y_mm) { res_x = (sensor->max_x - sensor->min_x) / sensor->x_mm; res_y = (sensor->max_y - sensor->min_y) / sensor->y_mm; + if (sensor->axis_align.swap_axes) + swap(res_x, res_y); input_abs_set_res(input, ABS_X, res_x); input_abs_set_res(input, ABS_Y, res_y); diff --git a/drivers/input/rmi4/rmi_bus.c b/drivers/input/rmi4/rmi_bus.c index c5fa53adba8d..bd0d5ff01b08 100644 --- a/drivers/input/rmi4/rmi_bus.c +++ b/drivers/input/rmi4/rmi_bus.c @@ -9,6 +9,8 @@ #include <linux/kernel.h> #include <linux/device.h> +#include <linux/irq.h> +#include <linux/irqdomain.h> #include <linux/list.h> #include <linux/pm.h> #include <linux/rmi.h> @@ -167,6 +169,39 @@ static inline void rmi_function_of_probe(struct rmi_function *fn) {} #endif +static struct irq_chip rmi_irq_chip = { + .name = "rmi4", +}; + +static int rmi_create_function_irq(struct rmi_function *fn, + struct rmi_function_handler *handler) +{ + struct rmi_driver_data *drvdata = dev_get_drvdata(&fn->rmi_dev->dev); + int i, error; + + for (i = 0; i < fn->num_of_irqs; i++) { + set_bit(fn->irq_pos + i, fn->irq_mask); + + fn->irq[i] = irq_create_mapping(drvdata->irqdomain, + fn->irq_pos + i); + + irq_set_chip_data(fn->irq[i], fn); + irq_set_chip_and_handler(fn->irq[i], &rmi_irq_chip, + handle_simple_irq); + irq_set_nested_thread(fn->irq[i], 1); + + error = devm_request_threaded_irq(&fn->dev, fn->irq[i], NULL, + handler->attention, IRQF_ONESHOT, + dev_name(&fn->dev), fn); + if (error) { + dev_err(&fn->dev, "Error %d registering IRQ\n", error); + return error; + } + } + + return 0; +} + static int rmi_function_probe(struct device *dev) { struct rmi_function *fn = to_rmi_function(dev); @@ -178,7 +213,14 @@ static int rmi_function_probe(struct device *dev) if (handler->probe) { error = handler->probe(fn); - return error; + if (error) + return error; + } + + if (fn->num_of_irqs && handler->attention) { + error = rmi_create_function_irq(fn, handler); + if (error) + return error; } return 0; @@ -230,12 +272,18 @@ err_put_device: void rmi_unregister_function(struct rmi_function *fn) { + int i; + rmi_dbg(RMI_DEBUG_CORE, &fn->dev, "Unregistering F%02X.\n", fn->fd.function_number); device_del(&fn->dev); of_node_put(fn->dev.of_node); put_device(&fn->dev); + + for (i = 0; i < fn->num_of_irqs; i++) + irq_dispose_mapping(fn->irq[i]); + } /** diff --git a/drivers/input/rmi4/rmi_bus.h b/drivers/input/rmi4/rmi_bus.h index b7625a9ac66a..96383eab41ba 100644 --- a/drivers/input/rmi4/rmi_bus.h +++ b/drivers/input/rmi4/rmi_bus.h @@ -14,6 +14,12 @@ struct rmi_device; +/* + * The interrupt source count in the function descriptor can represent up to + * 6 interrupt sources in the normal manner. + */ +#define RMI_FN_MAX_IRQS 6 + /** * struct rmi_function - represents the implementation of an RMI4 * function for a particular device (basically, a driver for that RMI4 function) @@ -26,6 +32,7 @@ struct rmi_device; * @irq_pos: The position in the irq bitfield this function holds * @irq_mask: For convenience, can be used to mask IRQ bits off during ATTN * interrupt handling. + * @irqs: assigned virq numbers (up to num_of_irqs) * * @node: entry in device's list of functions */ @@ -36,6 +43,7 @@ struct rmi_function { struct list_head node; unsigned int num_of_irqs; + int irq[RMI_FN_MAX_IRQS]; unsigned int irq_pos; unsigned long irq_mask[]; }; @@ -76,7 +84,7 @@ struct rmi_function_handler { void (*remove)(struct rmi_function *fn); int (*config)(struct rmi_function *fn); int (*reset)(struct rmi_function *fn); - int (*attention)(struct rmi_function *fn, unsigned long *irq_bits); + irqreturn_t (*attention)(int irq, void *ctx); int (*suspend)(struct rmi_function *fn); int (*resume)(struct rmi_function *fn); }; diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c index f5954981e9ee..cb6d983e8033 100644 --- a/drivers/input/rmi4/rmi_driver.c +++ b/drivers/input/rmi4/rmi_driver.c @@ -21,6 +21,7 @@ #include <linux/pm.h> #include <linux/slab.h> #include <linux/of.h> +#include <linux/irqdomain.h> #include <uapi/linux/input.h> #include <linux/rmi.h> #include "rmi_bus.h" @@ -127,28 +128,11 @@ static int rmi_driver_process_config_requests(struct rmi_device *rmi_dev) return 0; } -static void process_one_interrupt(struct rmi_driver_data *data, - struct rmi_function *fn) -{ - struct rmi_function_handler *fh; - - if (!fn || !fn->dev.driver) - return; - - fh = to_rmi_function_handler(fn->dev.driver); - if (fh->attention) { - bitmap_and(data->fn_irq_bits, data->irq_status, fn->irq_mask, - data->irq_count); - if (!bitmap_empty(data->fn_irq_bits, data->irq_count)) - fh->attention(fn, data->fn_irq_bits); - } -} - static int rmi_process_interrupt_requests(struct rmi_device *rmi_dev) { struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev); struct device *dev = &rmi_dev->dev; - struct rmi_function *entry; + int i; int error; if (!data) @@ -173,16 +157,8 @@ static int rmi_process_interrupt_requests(struct rmi_device *rmi_dev) */ mutex_unlock(&data->irq_mutex); - /* - * It would be nice to be able to use irq_chip to handle these - * nested IRQs. Unfortunately, most of the current customers for - * this driver are using older kernels (3.0.x) that don't support - * the features required for that. Once they've shifted to more - * recent kernels (say, 3.3 and higher), this should be switched to - * use irq_chip. - */ - list_for_each_entry(entry, &data->function_list, node) - process_one_interrupt(data, entry); + for_each_set_bit(i, data->irq_status, data->irq_count) + handle_nested_irq(irq_find_mapping(data->irqdomain, i)); if (data->input) input_sync(data->input); @@ -1000,9 +976,13 @@ EXPORT_SYMBOL_GPL(rmi_driver_resume); static int rmi_driver_remove(struct device *dev) { struct rmi_device *rmi_dev = to_rmi_device(dev); + struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev); rmi_disable_irq(rmi_dev, false); + irq_domain_remove(data->irqdomain); + data->irqdomain = NULL; + rmi_f34_remove_sysfs(rmi_dev); rmi_free_function_list(rmi_dev); @@ -1034,7 +1014,8 @@ int rmi_probe_interrupts(struct rmi_driver_data *data) { struct rmi_device *rmi_dev = data->rmi_dev; struct device *dev = &rmi_dev->dev; - int irq_count; + struct fwnode_handle *fwnode = rmi_dev->xport->dev->fwnode; + int irq_count = 0; size_t size; int retval; @@ -1045,7 +1026,6 @@ int rmi_probe_interrupts(struct rmi_driver_data *data) * being accessed. */ rmi_dbg(RMI_DEBUG_CORE, dev, "%s: Counting IRQs.\n", __func__); - irq_count = 0; data->bootloader_mode = false; retval = rmi_scan_pdt(rmi_dev, &irq_count, rmi_count_irqs); @@ -1057,6 +1037,15 @@ int rmi_probe_interrupts(struct rmi_driver_data *data) if (data->bootloader_mode) dev_warn(dev, "Device in bootloader mode.\n"); + /* Allocate and register a linear revmap irq_domain */ + data->irqdomain = irq_domain_create_linear(fwnode, irq_count, + &irq_domain_simple_ops, + data); + if (!data->irqdomain) { + dev_err(&rmi_dev->dev, "Failed to create IRQ domain\n"); + return -ENOMEM; + } + data->irq_count = irq_count; data->num_of_irq_regs = (data->irq_count + 7) / 8; @@ -1079,10 +1068,9 @@ int rmi_init_functions(struct rmi_driver_data *data) { struct rmi_device *rmi_dev = data->rmi_dev; struct device *dev = &rmi_dev->dev; - int irq_count; + int irq_count = 0; int retval; - irq_count = 0; rmi_dbg(RMI_DEBUG_CORE, dev, "%s: Creating functions.\n", __func__); retval = rmi_scan_pdt(rmi_dev, &irq_count, rmi_create_function); if (retval < 0) { diff --git a/drivers/input/rmi4/rmi_f01.c b/drivers/input/rmi4/rmi_f01.c index 8a07ae147df6..4edaa14fe878 100644 --- a/drivers/input/rmi4/rmi_f01.c +++ b/drivers/input/rmi4/rmi_f01.c @@ -681,9 +681,9 @@ static int rmi_f01_resume(struct rmi_function *fn) return 0; } -static int rmi_f01_attention(struct rmi_function *fn, - unsigned long *irq_bits) +static irqreturn_t rmi_f01_attention(int irq, void *ctx) { + struct rmi_function *fn = ctx; struct rmi_device *rmi_dev = fn->rmi_dev; int error; u8 device_status; @@ -692,7 +692,7 @@ static int rmi_f01_attention(struct rmi_function *fn, if (error) { dev_err(&fn->dev, "Failed to read device status: %d.\n", error); - return error; + return IRQ_RETVAL(error); } if (RMI_F01_STATUS_BOOTLOADER(device_status)) @@ -704,11 +704,11 @@ static int rmi_f01_attention(struct rmi_function *fn, error = rmi_dev->driver->reset_handler(rmi_dev); if (error) { dev_err(&fn->dev, "Device reset failed: %d\n", error); - return error; + return IRQ_RETVAL(error); } } - return 0; + return IRQ_HANDLED; } struct rmi_function_handler rmi_f01_handler = { diff --git a/drivers/input/rmi4/rmi_f03.c b/drivers/input/rmi4/rmi_f03.c index 88822196d6b7..aaa1edc95522 100644 --- a/drivers/input/rmi4/rmi_f03.c +++ b/drivers/input/rmi4/rmi_f03.c @@ -244,8 +244,9 @@ static int rmi_f03_config(struct rmi_function *fn) return 0; } -static int rmi_f03_attention(struct rmi_function *fn, unsigned long *irq_bits) +static irqreturn_t rmi_f03_attention(int irq, void *ctx) { + struct rmi_function *fn = ctx; struct rmi_device *rmi_dev = fn->rmi_dev; struct rmi_driver_data *drvdata = dev_get_drvdata(&rmi_dev->dev); struct f03_data *f03 = dev_get_drvdata(&fn->dev); @@ -262,7 +263,7 @@ static int rmi_f03_attention(struct rmi_function *fn, unsigned long *irq_bits) /* First grab the data passed by the transport device */ if (drvdata->attn_data.size < ob_len) { dev_warn(&fn->dev, "F03 interrupted, but data is missing!\n"); - return 0; + return IRQ_HANDLED; } memcpy(obs, drvdata->attn_data.data, ob_len); @@ -277,7 +278,7 @@ static int rmi_f03_attention(struct rmi_function *fn, unsigned long *irq_bits) "%s: Failed to read F03 output buffers: %d\n", __func__, error); serio_interrupt(f03->serio, 0, SERIO_TIMEOUT); - return error; + return IRQ_RETVAL(error); } } @@ -303,7 +304,7 @@ static int rmi_f03_attention(struct rmi_function *fn, unsigned long *irq_bits) serio_interrupt(f03->serio, ob_data, serio_flags); } - return 0; + return IRQ_HANDLED; } static void rmi_f03_remove(struct rmi_function *fn) diff --git a/drivers/input/rmi4/rmi_f11.c b/drivers/input/rmi4/rmi_f11.c index bc5e37f30ac1..5f2e1ef5accc 100644 --- a/drivers/input/rmi4/rmi_f11.c +++ b/drivers/input/rmi4/rmi_f11.c @@ -570,9 +570,7 @@ static inline u8 rmi_f11_parse_finger_state(const u8 *f_state, u8 n_finger) } static void rmi_f11_finger_handler(struct f11_data *f11, - struct rmi_2d_sensor *sensor, - unsigned long *irq_bits, int num_irq_regs, - int size) + struct rmi_2d_sensor *sensor, int size) { const u8 *f_state = f11->data.f_state; u8 finger_state; @@ -581,12 +579,7 @@ static void rmi_f11_finger_handler(struct f11_data *f11, int rel_fingers; int abs_size = sensor->nbr_fingers * RMI_F11_ABS_BYTES; - int abs_bits = bitmap_and(f11->result_bits, irq_bits, f11->abs_mask, - num_irq_regs * 8); - int rel_bits = bitmap_and(f11->result_bits, irq_bits, f11->rel_mask, - num_irq_regs * 8); - - if (abs_bits) { + if (sensor->report_abs) { if (abs_size > size) abs_fingers = size / RMI_F11_ABS_BYTES; else @@ -604,19 +597,7 @@ static void rmi_f11_finger_handler(struct f11_data *f11, rmi_f11_abs_pos_process(f11, sensor, &sensor->objs[i], finger_state, i); } - } - if (rel_bits) { - if ((abs_size + sensor->nbr_fingers * RMI_F11_REL_BYTES) > size) - rel_fingers = (size - abs_size) / RMI_F11_REL_BYTES; - else - rel_fingers = sensor->nbr_fingers; - - for (i = 0; i < rel_fingers; i++) - rmi_f11_rel_pos_report(f11, i); - } - - if (abs_bits) { /* * the absolute part is made in 2 parts to allow the kernel * tracking to take place. @@ -638,7 +619,16 @@ static void rmi_f11_finger_handler(struct f11_data *f11, } input_mt_sync_frame(sensor->input); + } else if (sensor->report_rel) { + if ((abs_size + sensor->nbr_fingers * RMI_F11_REL_BYTES) > size) + rel_fingers = (size - abs_size) / RMI_F11_REL_BYTES; + else + rel_fingers = sensor->nbr_fingers; + + for (i = 0; i < rel_fingers; i++) + rmi_f11_rel_pos_report(f11, i); } + } static int f11_2d_construct_data(struct f11_data *f11) @@ -1275,8 +1265,9 @@ static int rmi_f11_config(struct rmi_function *fn) return 0; } -static int rmi_f11_attention(struct rmi_function *fn, unsigned long *irq_bits) +static irqreturn_t rmi_f11_attention(int irq, void *ctx) { + struct rmi_function *fn = ctx; struct rmi_device *rmi_dev = fn->rmi_dev; struct rmi_driver_data *drvdata = dev_get_drvdata(&rmi_dev->dev); struct f11_data *f11 = dev_get_drvdata(&fn->dev); @@ -1302,13 +1293,12 @@ static int rmi_f11_attention(struct rmi_function *fn, unsigned long *irq_bits) data_base_addr, f11->sensor.data_pkt, f11->sensor.pkt_size); if (error < 0) - return error; + return IRQ_RETVAL(error); } - rmi_f11_finger_handler(f11, &f11->sensor, irq_bits, - drvdata->num_of_irq_regs, valid_bytes); + rmi_f11_finger_handler(f11, &f11->sensor, valid_bytes); - return 0; + return IRQ_HANDLED; } static int rmi_f11_resume(struct rmi_function *fn) diff --git a/drivers/input/rmi4/rmi_f12.c b/drivers/input/rmi4/rmi_f12.c index 8b0db086d68a..e226def74d82 100644 --- a/drivers/input/rmi4/rmi_f12.c +++ b/drivers/input/rmi4/rmi_f12.c @@ -197,10 +197,10 @@ static void rmi_f12_process_objects(struct f12_data *f12, u8 *data1, int size) rmi_2d_sensor_abs_report(sensor, &sensor->objs[i], i); } -static int rmi_f12_attention(struct rmi_function *fn, - unsigned long *irq_nr_regs) +static irqreturn_t rmi_f12_attention(int irq, void *ctx) { int retval; + struct rmi_function *fn = ctx; struct rmi_device *rmi_dev = fn->rmi_dev; struct rmi_driver_data *drvdata = dev_get_drvdata(&rmi_dev->dev); struct f12_data *f12 = dev_get_drvdata(&fn->dev); @@ -222,7 +222,7 @@ static int rmi_f12_attention(struct rmi_function *fn, if (retval < 0) { dev_err(&fn->dev, "Failed to read object data. Code: %d.\n", retval); - return retval; + return IRQ_RETVAL(retval); } } @@ -232,7 +232,7 @@ static int rmi_f12_attention(struct rmi_function *fn, input_mt_sync_frame(sensor->input); - return 0; + return IRQ_HANDLED; } static int rmi_f12_write_control_regs(struct rmi_function *fn) diff --git a/drivers/input/rmi4/rmi_f30.c b/drivers/input/rmi4/rmi_f30.c index 82e0f0d43d55..5e3ed5ac0c3e 100644 --- a/drivers/input/rmi4/rmi_f30.c +++ b/drivers/input/rmi4/rmi_f30.c @@ -122,8 +122,9 @@ static void rmi_f30_report_button(struct rmi_function *fn, } } -static int rmi_f30_attention(struct rmi_function *fn, unsigned long *irq_bits) +static irqreturn_t rmi_f30_attention(int irq, void *ctx) { + struct rmi_function *fn = ctx; struct f30_data *f30 = dev_get_drvdata(&fn->dev); struct rmi_driver_data *drvdata = dev_get_drvdata(&fn->rmi_dev->dev); int error; @@ -134,7 +135,7 @@ static int rmi_f30_attention(struct rmi_function *fn, unsigned long *irq_bits) if (drvdata->attn_data.size < f30->register_count) { dev_warn(&fn->dev, "F30 interrupted, but data is missing\n"); - return 0; + return IRQ_HANDLED; } memcpy(f30->data_regs, drvdata->attn_data.data, f30->register_count); @@ -147,7 +148,7 @@ static int rmi_f30_attention(struct rmi_function *fn, unsigned long *irq_bits) dev_err(&fn->dev, "%s: Failed to read F30 data registers: %d\n", __func__, error); - return error; + return IRQ_RETVAL(error); } } @@ -159,7 +160,7 @@ static int rmi_f30_attention(struct rmi_function *fn, unsigned long *irq_bits) rmi_f03_commit_buttons(f30->f03); } - return 0; + return IRQ_HANDLED; } static int rmi_f30_config(struct rmi_function *fn) diff --git a/drivers/input/rmi4/rmi_f34.c b/drivers/input/rmi4/rmi_f34.c index f1f5ac539d5d..87a7d4ba382d 100644 --- a/drivers/input/rmi4/rmi_f34.c +++ b/drivers/input/rmi4/rmi_f34.c @@ -100,8 +100,9 @@ static int rmi_f34_command(struct f34_data *f34, u8 command, return 0; } -static int rmi_f34_attention(struct rmi_function *fn, unsigned long *irq_bits) +static irqreturn_t rmi_f34_attention(int irq, void *ctx) { + struct rmi_function *fn = ctx; struct f34_data *f34 = dev_get_drvdata(&fn->dev); int ret; u8 status; @@ -126,7 +127,7 @@ static int rmi_f34_attention(struct rmi_function *fn, unsigned long *irq_bits) complete(&f34->v7.cmd_done); } - return 0; + return IRQ_HANDLED; } static int rmi_f34_write_blocks(struct f34_data *f34, const void *data, diff --git a/drivers/input/rmi4/rmi_f54.c b/drivers/input/rmi4/rmi_f54.c index 5343f2c08f15..98a935efec8e 100644 --- a/drivers/input/rmi4/rmi_f54.c +++ b/drivers/input/rmi4/rmi_f54.c @@ -610,11 +610,6 @@ error: mutex_unlock(&f54->data_mutex); } -static int rmi_f54_attention(struct rmi_function *fn, unsigned long *irqbits) -{ - return 0; -} - static int rmi_f54_config(struct rmi_function *fn) { struct rmi_driver *drv = fn->rmi_dev->driver; @@ -756,6 +751,5 @@ struct rmi_function_handler rmi_f54_handler = { .func = 0x54, .probe = rmi_f54_probe, .config = rmi_f54_config, - .attention = rmi_f54_attention, .remove = rmi_f54_remove, }; diff --git a/drivers/input/serio/ct82c710.c b/drivers/input/serio/ct82c710.c index 9c54c43c9749..2d1e2993b5a8 100644 --- a/drivers/input/serio/ct82c710.c +++ b/drivers/input/serio/ct82c710.c @@ -20,10 +20,6 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: - * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include <linux/delay.h> diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index 824f4c1c1f31..b8bc71569349 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -573,6 +573,9 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id) port = &i8042_ports[port_no]; serio = port->exists ? port->serio : NULL; + if (irq && serio) + pm_wakeup_event(&serio->dev, 0); + filter_dbg(port->driver_bound, data, "<- i8042 (interrupt, %d, %d%s%s)\n", port_no, irq, dfl & SERIO_PARITY ? ", bad parity" : "", diff --git a/drivers/input/serio/q40kbd.c b/drivers/input/serio/q40kbd.c index d0fccc8ec259..fbb6b33845fa 100644 --- a/drivers/input/serio/q40kbd.c +++ b/drivers/input/serio/q40kbd.c @@ -23,10 +23,6 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: - * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include <linux/module.h> diff --git a/drivers/input/serio/rpckbd.c b/drivers/input/serio/rpckbd.c index 8cf964736902..a308d7811427 100644 --- a/drivers/input/serio/rpckbd.c +++ b/drivers/input/serio/rpckbd.c @@ -21,10 +21,6 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: - * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include <linux/module.h> diff --git a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c index 24a90c8db5b3..2e1fb0649260 100644 --- a/drivers/input/serio/serio.c +++ b/drivers/input/serio/serio.c @@ -20,10 +20,6 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: - * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt diff --git a/drivers/input/tablet/aiptek.c b/drivers/input/tablet/aiptek.c index 545fa6e89035..c82cd5079d0e 100644 --- a/drivers/input/tablet/aiptek.c +++ b/drivers/input/tablet/aiptek.c @@ -1712,7 +1712,7 @@ aiptek_probe(struct usb_interface *intf, const struct usb_device_id *id) } aiptek->data = usb_alloc_coherent(usbdev, AIPTEK_PACKET_LENGTH, - GFP_ATOMIC, &aiptek->data_dma); + GFP_KERNEL, &aiptek->data_dma); if (!aiptek->data) { dev_warn(&intf->dev, "cannot allocate usb buffer\n"); goto fail1; diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 3e613afa10b4..5374bd573e66 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -151,6 +151,18 @@ config TOUCHSCREEN_BU21013 To compile this driver as a module, choose M here: the module will be called bu21013_ts. +config TOUCHSCREEN_BU21029 + tristate "Rohm BU21029 based touch panel controllers" + depends on I2C + help + Say Y here if you have a Rohm BU21029 touchscreen controller + connected to your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called bu21029_ts. + config TOUCHSCREEN_CHIPONE_ICN8318 tristate "chipone icn8318 touchscreen controller" depends on GPIOLIB || COMPILE_TEST @@ -164,6 +176,17 @@ config TOUCHSCREEN_CHIPONE_ICN8318 To compile this driver as a module, choose M here: the module will be called chipone_icn8318. +config TOUCHSCREEN_CHIPONE_ICN8505 + tristate "chipone icn8505 touchscreen controller" + depends on I2C && ACPI + help + Say Y here if you have a ChipOne icn8505 based I2C touchscreen. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called chipone_icn8505. + config TOUCHSCREEN_CY8CTMG110 tristate "cy8ctmg110 touchscreen" depends on I2C diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index dddae7973436..c2175163152d 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -18,7 +18,9 @@ obj-$(CONFIG_TOUCHSCREEN_AR1021_I2C) += ar1021_i2c.o obj-$(CONFIG_TOUCHSCREEN_ATMEL_MXT) += atmel_mxt_ts.o obj-$(CONFIG_TOUCHSCREEN_AUO_PIXCIR) += auo-pixcir-ts.o obj-$(CONFIG_TOUCHSCREEN_BU21013) += bu21013_ts.o +obj-$(CONFIG_TOUCHSCREEN_BU21029) += bu21029_ts.o obj-$(CONFIG_TOUCHSCREEN_CHIPONE_ICN8318) += chipone_icn8318.o +obj-$(CONFIG_TOUCHSCREEN_CHIPONE_ICN8505) += chipone_icn8505.o obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o obj-$(CONFIG_TOUCHSCREEN_CYTTSP_CORE) += cyttsp_core.o obj-$(CONFIG_TOUCHSCREEN_CYTTSP_I2C) += cyttsp_i2c.o cyttsp_i2c_common.o diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 09194721aed2..3232af5dcf89 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -75,6 +75,7 @@ #define MXT_SPT_DIGITIZER_T43 43 #define MXT_SPT_MESSAGECOUNT_T44 44 #define MXT_SPT_CTECONFIG_T46 46 +#define MXT_SPT_DYNAMICCONFIGURATIONCONTAINER_T71 71 #define MXT_TOUCH_MULTITOUCHSCREEN_T100 100 /* MXT_GEN_MESSAGE_T5 object */ @@ -88,12 +89,12 @@ #define MXT_COMMAND_DIAGNOSTIC 5 /* Define for T6 status byte */ -#define MXT_T6_STATUS_RESET (1 << 7) -#define MXT_T6_STATUS_OFL (1 << 6) -#define MXT_T6_STATUS_SIGERR (1 << 5) -#define MXT_T6_STATUS_CAL (1 << 4) -#define MXT_T6_STATUS_CFGERR (1 << 3) -#define MXT_T6_STATUS_COMSERR (1 << 2) +#define MXT_T6_STATUS_RESET BIT(7) +#define MXT_T6_STATUS_OFL BIT(6) +#define MXT_T6_STATUS_SIGERR BIT(5) +#define MXT_T6_STATUS_CAL BIT(4) +#define MXT_T6_STATUS_CFGERR BIT(3) +#define MXT_T6_STATUS_COMSERR BIT(2) /* MXT_GEN_POWER_T7 field */ struct t7_config { @@ -112,14 +113,14 @@ struct t7_config { #define MXT_T9_RANGE 18 /* MXT_TOUCH_MULTI_T9 status */ -#define MXT_T9_UNGRIP (1 << 0) -#define MXT_T9_SUPPRESS (1 << 1) -#define MXT_T9_AMP (1 << 2) -#define MXT_T9_VECTOR (1 << 3) -#define MXT_T9_MOVE (1 << 4) -#define MXT_T9_RELEASE (1 << 5) -#define MXT_T9_PRESS (1 << 6) -#define MXT_T9_DETECT (1 << 7) +#define MXT_T9_UNGRIP BIT(0) +#define MXT_T9_SUPPRESS BIT(1) +#define MXT_T9_AMP BIT(2) +#define MXT_T9_VECTOR BIT(3) +#define MXT_T9_MOVE BIT(4) +#define MXT_T9_RELEASE BIT(5) +#define MXT_T9_PRESS BIT(6) +#define MXT_T9_DETECT BIT(7) struct t9_range { __le16 x; @@ -127,9 +128,9 @@ struct t9_range { } __packed; /* MXT_TOUCH_MULTI_T9 orient */ -#define MXT_T9_ORIENT_SWITCH (1 << 0) -#define MXT_T9_ORIENT_INVERTX (1 << 1) -#define MXT_T9_ORIENT_INVERTY (1 << 2) +#define MXT_T9_ORIENT_SWITCH BIT(0) +#define MXT_T9_ORIENT_INVERTX BIT(1) +#define MXT_T9_ORIENT_INVERTY BIT(2) /* MXT_SPT_COMMSCONFIG_T18 */ #define MXT_COMMS_CTRL 0 @@ -194,6 +195,8 @@ enum t100_type { /* Delay times */ #define MXT_BACKUP_TIME 50 /* msec */ +#define MXT_RESET_GPIO_TIME 20 /* msec */ +#define MXT_RESET_INVALID_CHG 100 /* msec */ #define MXT_RESET_TIME 200 /* msec */ #define MXT_RESET_TIMEOUT 3000 /* msec */ #define MXT_CRC_TIMEOUT 1000 /* msec */ @@ -212,7 +215,7 @@ enum t100_type { #define MXT_FRAME_CRC_PASS 0x04 #define MXT_APP_CRC_FAIL 0x40 /* valid 7 8 bit only */ #define MXT_BOOT_STATUS_MASK 0x3f -#define MXT_BOOT_EXTENDED_ID (1 << 5) +#define MXT_BOOT_EXTENDED_ID BIT(5) #define MXT_BOOT_ID_MASK 0x1f /* Touchscreen absolute values */ @@ -274,6 +277,19 @@ enum mxt_suspend_mode { MXT_SUSPEND_T9_CTRL = 1, }; +/* Config update context */ +struct mxt_cfg { + u8 *raw; + size_t raw_size; + off_t raw_pos; + + u8 *mem; + size_t mem_size; + int start_ofs; + + struct mxt_info info; +}; + /* Each client has this additional data */ struct mxt_data { struct i2c_client *client; @@ -315,6 +331,7 @@ struct mxt_data { u8 T6_reportid; u16 T6_address; u16 T7_address; + u16 T71_address; u8 T9_reportid_min; u8 T9_reportid_max; u8 T19_reportid; @@ -380,6 +397,7 @@ static bool mxt_object_readable(unsigned int type) case MXT_SPT_USERDATA_T38: case MXT_SPT_DIGITIZER_T43: case MXT_SPT_CTECONFIG_T46: + case MXT_SPT_DYNAMICCONFIGURATIONCONTAINER_T71: return true; default: return false; @@ -710,13 +728,13 @@ static void mxt_proc_t6_messages(struct mxt_data *data, u8 *msg) u8 status = msg[1]; u32 crc = msg[2] | (msg[3] << 8) | (msg[4] << 16); - complete(&data->crc_completion); - if (crc != data->config_crc) { data->config_crc = crc; dev_dbg(dev, "T6 Config Checksum: 0x%06X\n", crc); } + complete(&data->crc_completion); + /* Detect reset */ if (status & MXT_T6_STATUS_RESET) complete(&data->reset_completion); @@ -825,6 +843,10 @@ static void mxt_proc_t9_message(struct mxt_data *data, u8 *message) mxt_input_sync(data); } + /* if active, pressure must be non-zero */ + if (!amplitude) + amplitude = MXT_PRESSURE_DEFAULT; + /* Touch active */ input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, 1); input_report_abs(input_dev, ABS_MT_POSITION_X, x); @@ -1208,7 +1230,7 @@ static int mxt_soft_reset(struct mxt_data *data) return ret; /* Ignore CHG line for 100ms after reset */ - msleep(100); + msleep(MXT_RESET_INVALID_CHG); mxt_acquire_irq(data); @@ -1277,12 +1299,7 @@ static u32 mxt_calculate_crc(u8 *base, off_t start_off, off_t end_off) return crc; } -static int mxt_prepare_cfg_mem(struct mxt_data *data, - const struct firmware *cfg, - unsigned int data_pos, - unsigned int cfg_start_ofs, - u8 *config_mem, - size_t config_mem_size) +static int mxt_prepare_cfg_mem(struct mxt_data *data, struct mxt_cfg *cfg) { struct device *dev = &data->client->dev; struct mxt_object *object; @@ -1293,9 +1310,9 @@ static int mxt_prepare_cfg_mem(struct mxt_data *data, u16 reg; u8 val; - while (data_pos < cfg->size) { + while (cfg->raw_pos < cfg->raw_size) { /* Read type, instance, length */ - ret = sscanf(cfg->data + data_pos, "%x %x %x%n", + ret = sscanf(cfg->raw + cfg->raw_pos, "%x %x %x%n", &type, &instance, &size, &offset); if (ret == 0) { /* EOF */ @@ -1304,20 +1321,20 @@ static int mxt_prepare_cfg_mem(struct mxt_data *data, dev_err(dev, "Bad format: failed to parse object\n"); return -EINVAL; } - data_pos += offset; + cfg->raw_pos += offset; object = mxt_get_object(data, type); if (!object) { /* Skip object */ for (i = 0; i < size; i++) { - ret = sscanf(cfg->data + data_pos, "%hhx%n", + ret = sscanf(cfg->raw + cfg->raw_pos, "%hhx%n", &val, &offset); if (ret != 1) { dev_err(dev, "Bad format in T%d at %d\n", type, i); return -EINVAL; } - data_pos += offset; + cfg->raw_pos += offset; } continue; } @@ -1352,7 +1369,7 @@ static int mxt_prepare_cfg_mem(struct mxt_data *data, reg = object->start_address + mxt_obj_size(object) * instance; for (i = 0; i < size; i++) { - ret = sscanf(cfg->data + data_pos, "%hhx%n", + ret = sscanf(cfg->raw + cfg->raw_pos, "%hhx%n", &val, &offset); if (ret != 1) { @@ -1360,15 +1377,15 @@ static int mxt_prepare_cfg_mem(struct mxt_data *data, type, i); return -EINVAL; } - data_pos += offset; + cfg->raw_pos += offset; if (i > mxt_obj_size(object)) continue; - byte_offset = reg + i - cfg_start_ofs; + byte_offset = reg + i - cfg->start_ofs; - if (byte_offset >= 0 && byte_offset < config_mem_size) { - *(config_mem + byte_offset) = val; + if (byte_offset >= 0 && byte_offset < cfg->mem_size) { + *(cfg->mem + byte_offset) = val; } else { dev_err(dev, "Bad object: reg:%d, T%d, ofs=%d\n", reg, object->type, byte_offset); @@ -1380,22 +1397,21 @@ static int mxt_prepare_cfg_mem(struct mxt_data *data, return 0; } -static int mxt_upload_cfg_mem(struct mxt_data *data, unsigned int cfg_start, - u8 *config_mem, size_t config_mem_size) +static int mxt_upload_cfg_mem(struct mxt_data *data, struct mxt_cfg *cfg) { unsigned int byte_offset = 0; int error; /* Write configuration as blocks */ - while (byte_offset < config_mem_size) { - unsigned int size = config_mem_size - byte_offset; + while (byte_offset < cfg->mem_size) { + unsigned int size = cfg->mem_size - byte_offset; if (size > MXT_MAX_BLOCK_WRITE) size = MXT_MAX_BLOCK_WRITE; error = __mxt_write_reg(data->client, - cfg_start + byte_offset, - size, config_mem + byte_offset); + cfg->start_ofs + byte_offset, + size, cfg->mem + byte_offset); if (error) { dev_err(&data->client->dev, "Config write error, ret=%d\n", error); @@ -1429,65 +1445,75 @@ static int mxt_init_t7_power_cfg(struct mxt_data *data); * <SIZE> - 2-byte object size as hex * <CONTENTS> - array of <SIZE> 1-byte hex values */ -static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg) +static int mxt_update_cfg(struct mxt_data *data, const struct firmware *fw) { struct device *dev = &data->client->dev; - struct mxt_info cfg_info; + struct mxt_cfg cfg; int ret; int offset; - int data_pos; int i; - int cfg_start_ofs; u32 info_crc, config_crc, calculated_crc; - u8 *config_mem; - size_t config_mem_size; + u16 crc_start = 0; + + /* Make zero terminated copy of the OBP_RAW file */ + cfg.raw = kmemdup_nul(fw->data, fw->size, GFP_KERNEL); + if (!cfg.raw) + return -ENOMEM; + + cfg.raw_size = fw->size; mxt_update_crc(data, MXT_COMMAND_REPORTALL, 1); - if (strncmp(cfg->data, MXT_CFG_MAGIC, strlen(MXT_CFG_MAGIC))) { + if (strncmp(cfg.raw, MXT_CFG_MAGIC, strlen(MXT_CFG_MAGIC))) { dev_err(dev, "Unrecognised config file\n"); - return -EINVAL; + ret = -EINVAL; + goto release_raw; } - data_pos = strlen(MXT_CFG_MAGIC); + cfg.raw_pos = strlen(MXT_CFG_MAGIC); /* Load information block and check */ for (i = 0; i < sizeof(struct mxt_info); i++) { - ret = sscanf(cfg->data + data_pos, "%hhx%n", - (unsigned char *)&cfg_info + i, + ret = sscanf(cfg.raw + cfg.raw_pos, "%hhx%n", + (unsigned char *)&cfg.info + i, &offset); if (ret != 1) { dev_err(dev, "Bad format\n"); - return -EINVAL; + ret = -EINVAL; + goto release_raw; } - data_pos += offset; + cfg.raw_pos += offset; } - if (cfg_info.family_id != data->info->family_id) { + if (cfg.info.family_id != data->info->family_id) { dev_err(dev, "Family ID mismatch!\n"); - return -EINVAL; + ret = -EINVAL; + goto release_raw; } - if (cfg_info.variant_id != data->info->variant_id) { + if (cfg.info.variant_id != data->info->variant_id) { dev_err(dev, "Variant ID mismatch!\n"); - return -EINVAL; + ret = -EINVAL; + goto release_raw; } /* Read CRCs */ - ret = sscanf(cfg->data + data_pos, "%x%n", &info_crc, &offset); + ret = sscanf(cfg.raw + cfg.raw_pos, "%x%n", &info_crc, &offset); if (ret != 1) { dev_err(dev, "Bad format: failed to parse Info CRC\n"); - return -EINVAL; + ret = -EINVAL; + goto release_raw; } - data_pos += offset; + cfg.raw_pos += offset; - ret = sscanf(cfg->data + data_pos, "%x%n", &config_crc, &offset); + ret = sscanf(cfg.raw + cfg.raw_pos, "%x%n", &config_crc, &offset); if (ret != 1) { dev_err(dev, "Bad format: failed to parse Config CRC\n"); - return -EINVAL; + ret = -EINVAL; + goto release_raw; } - data_pos += offset; + cfg.raw_pos += offset; /* * The Info Block CRC is calculated over mxt_info and the object @@ -1513,39 +1539,39 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg) } /* Malloc memory to store configuration */ - cfg_start_ofs = MXT_OBJECT_START + + cfg.start_ofs = MXT_OBJECT_START + data->info->object_num * sizeof(struct mxt_object) + MXT_INFO_CHECKSUM_SIZE; - config_mem_size = data->mem_size - cfg_start_ofs; - config_mem = kzalloc(config_mem_size, GFP_KERNEL); - if (!config_mem) { - dev_err(dev, "Failed to allocate memory\n"); - return -ENOMEM; + cfg.mem_size = data->mem_size - cfg.start_ofs; + cfg.mem = kzalloc(cfg.mem_size, GFP_KERNEL); + if (!cfg.mem) { + ret = -ENOMEM; + goto release_raw; } - ret = mxt_prepare_cfg_mem(data, cfg, data_pos, cfg_start_ofs, - config_mem, config_mem_size); + ret = mxt_prepare_cfg_mem(data, &cfg); if (ret) goto release_mem; /* Calculate crc of the received configs (not the raw config file) */ - if (data->T7_address < cfg_start_ofs) { - dev_err(dev, "Bad T7 address, T7addr = %x, config offset %x\n", - data->T7_address, cfg_start_ofs); - ret = 0; - goto release_mem; - } + if (data->T71_address) + crc_start = data->T71_address; + else if (data->T7_address) + crc_start = data->T7_address; + else + dev_warn(dev, "Could not find CRC start\n"); - calculated_crc = mxt_calculate_crc(config_mem, - data->T7_address - cfg_start_ofs, - config_mem_size); + if (crc_start > cfg.start_ofs) { + calculated_crc = mxt_calculate_crc(cfg.mem, + crc_start - cfg.start_ofs, + cfg.mem_size); - if (config_crc > 0 && config_crc != calculated_crc) - dev_warn(dev, "Config CRC error, calculated=%06X, file=%06X\n", - calculated_crc, config_crc); + if (config_crc > 0 && config_crc != calculated_crc) + dev_warn(dev, "Config CRC in file inconsistent, calculated=%06X, file=%06X\n", + calculated_crc, config_crc); + } - ret = mxt_upload_cfg_mem(data, cfg_start_ofs, - config_mem, config_mem_size); + ret = mxt_upload_cfg_mem(data, &cfg); if (ret) goto release_mem; @@ -1560,8 +1586,10 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg) /* T7 config may have changed */ mxt_init_t7_power_cfg(data); +release_raw: + kfree(cfg.raw); release_mem: - kfree(config_mem); + kfree(cfg.mem); return ret; } @@ -1589,6 +1617,7 @@ static void mxt_free_object_table(struct mxt_data *data) data->T5_msg_size = 0; data->T6_reportid = 0; data->T7_address = 0; + data->T71_address = 0; data->T9_reportid_min = 0; data->T9_reportid_max = 0; data->T19_reportid = 0; @@ -1654,12 +1683,16 @@ static int mxt_parse_object_table(struct mxt_data *data, case MXT_GEN_POWER_T7: data->T7_address = object->start_address; break; + case MXT_SPT_DYNAMICCONFIGURATIONCONTAINER_T71: + data->T71_address = object->start_address; + break; case MXT_TOUCH_MULTI_T9: data->multitouch = MXT_TOUCH_MULTI_T9; + /* Only handle messages from first T9 instance */ data->T9_reportid_min = min_id; - data->T9_reportid_max = max_id; - data->num_touchids = object->num_report_ids - * mxt_obj_instances(object); + data->T9_reportid_max = min_id + + object->num_report_ids - 1; + data->num_touchids = object->num_report_ids; break; case MXT_SPT_MESSAGECOUNT_T44: data->T44_address = object->start_address; @@ -1979,10 +2012,8 @@ static int mxt_initialize_input_device(struct mxt_data *data) /* Register input device */ input_dev = input_allocate_device(); - if (!input_dev) { - dev_err(dev, "Failed to allocate memory\n"); + if (!input_dev) return -ENOMEM; - } input_dev->name = "Atmel maXTouch Touchscreen"; input_dev->phys = data->phys; @@ -2053,12 +2084,6 @@ static int mxt_initialize_input_device(struct mxt_data *data) } if (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 && - data->t100_aux_ampl) { - input_set_abs_params(input_dev, ABS_MT_PRESSURE, - 0, 255, 0, 0); - } - - if (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 && data->t100_aux_vect) { input_set_abs_params(input_dev, ABS_MT_ORIENTATION, 0, 255, 0, 0); @@ -2999,142 +3024,6 @@ static int mxt_parse_device_properties(struct mxt_data *data) return 0; } -#ifdef CONFIG_ACPI - -struct mxt_acpi_platform_data { - const char *hid; - const struct property_entry *props; -}; - -static unsigned int samus_touchpad_buttons[] = { - KEY_RESERVED, - KEY_RESERVED, - KEY_RESERVED, - BTN_LEFT -}; - -static const struct property_entry samus_touchpad_props[] = { - PROPERTY_ENTRY_U32_ARRAY("linux,gpio-keymap", samus_touchpad_buttons), - { } -}; - -static struct mxt_acpi_platform_data samus_platform_data[] = { - { - /* Touchpad */ - .hid = "ATML0000", - .props = samus_touchpad_props, - }, - { - /* Touchscreen */ - .hid = "ATML0001", - }, - { } -}; - -static unsigned int chromebook_tp_buttons[] = { - KEY_RESERVED, - KEY_RESERVED, - KEY_RESERVED, - KEY_RESERVED, - KEY_RESERVED, - BTN_LEFT -}; - -static const struct property_entry chromebook_tp_props[] = { - PROPERTY_ENTRY_U32_ARRAY("linux,gpio-keymap", chromebook_tp_buttons), - { } -}; - -static struct mxt_acpi_platform_data chromebook_platform_data[] = { - { - /* Touchpad */ - .hid = "ATML0000", - .props = chromebook_tp_props, - }, - { - /* Touchscreen */ - .hid = "ATML0001", - }, - { } -}; - -static const struct dmi_system_id mxt_dmi_table[] = { - { - /* 2015 Google Pixel */ - .ident = "Chromebook Pixel 2", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"), - DMI_MATCH(DMI_PRODUCT_NAME, "Samus"), - }, - .driver_data = samus_platform_data, - }, - { - /* Samsung Chromebook Pro */ - .ident = "Samsung Chromebook Pro", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Google"), - DMI_MATCH(DMI_PRODUCT_NAME, "Caroline"), - }, - .driver_data = samus_platform_data, - }, - { - /* Other Google Chromebooks */ - .ident = "Chromebook", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"), - }, - .driver_data = chromebook_platform_data, - }, - { } -}; - -static int mxt_prepare_acpi_properties(struct i2c_client *client) -{ - struct acpi_device *adev; - const struct dmi_system_id *system_id; - const struct mxt_acpi_platform_data *acpi_pdata; - - adev = ACPI_COMPANION(&client->dev); - if (!adev) - return -ENOENT; - - system_id = dmi_first_match(mxt_dmi_table); - if (!system_id) - return -ENOENT; - - acpi_pdata = system_id->driver_data; - if (!acpi_pdata) - return -ENOENT; - - while (acpi_pdata->hid) { - if (!strcmp(acpi_device_hid(adev), acpi_pdata->hid)) { - /* - * Remove previously installed properties if we - * are probing this device not for the very first - * time. - */ - device_remove_properties(&client->dev); - - /* - * Now install the platform-specific properties - * that are missing from ACPI. - */ - device_add_properties(&client->dev, acpi_pdata->props); - break; - } - - acpi_pdata++; - } - - return 0; -} -#else -static int mxt_prepare_acpi_properties(struct i2c_client *client) -{ - return -ENOENT; -} -#endif - static const struct dmi_system_id chromebook_T9_suspend_dmi[] = { { .matches = { @@ -3156,6 +3045,18 @@ static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id) int error; /* + * Ignore devices that do not have device properties attached to + * them, as we need help determining whether we are dealing with + * touch screen or touchpad. + * + * So far on x86 the only users of Atmel touch controllers are + * Chromebooks, and chromeos_laptop driver will ensure that + * necessary properties are provided (if firmware does not do that). + */ + if (!device_property_present(&client->dev, "compatible")) + return -ENXIO; + + /* * Ignore ACPI devices representing bootloader mode. * * This is a bit of a hack: Google Chromebook BIOS creates ACPI @@ -3186,10 +3087,6 @@ static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id) data->suspend_mode = dmi_check_system(chromebook_T9_suspend_dmi) ? MXT_SUSPEND_T9_CTRL : MXT_SUSPEND_DEEP_SLEEP; - error = mxt_prepare_acpi_properties(client); - if (error && error != -ENOENT) - return error; - error = mxt_parse_device_properties(data); if (error) return error; @@ -3210,20 +3107,14 @@ static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id) return error; } + disable_irq(client->irq); + if (data->reset_gpio) { - data->in_bootloader = true; - msleep(MXT_RESET_TIME); - reinit_completion(&data->bl_completion); + msleep(MXT_RESET_GPIO_TIME); gpiod_set_value(data->reset_gpio, 1); - error = mxt_wait_for_completion(data, &data->bl_completion, - MXT_RESET_TIMEOUT); - if (error) - return error; - data->in_bootloader = false; + msleep(MXT_RESET_INVALID_CHG); } - disable_irq(client->irq); - error = mxt_initialize(data); if (error) return error; diff --git a/drivers/input/touchscreen/bu21029_ts.c b/drivers/input/touchscreen/bu21029_ts.c new file mode 100644 index 000000000000..49a8d4bbca3a --- /dev/null +++ b/drivers/input/touchscreen/bu21029_ts.c @@ -0,0 +1,484 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Rohm BU21029 touchscreen controller driver + * + * Copyright (C) 2015-2018 Bosch Sicherheitssysteme GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/input.h> +#include <linux/input/touchscreen.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/module.h> +#include <linux/regulator/consumer.h> +#include <linux/timer.h> + +/* + * HW_ID1 Register (PAGE=0, ADDR=0x0E, Reset value=0x02, Read only) + * +--------+--------+--------+--------+--------+--------+--------+--------+ + * | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | + * +--------+--------+--------+--------+--------+--------+--------+--------+ + * | HW_IDH | + * +--------+--------+--------+--------+--------+--------+--------+--------+ + * HW_ID2 Register (PAGE=0, ADDR=0x0F, Reset value=0x29, Read only) + * +--------+--------+--------+--------+--------+--------+--------+--------+ + * | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | + * +--------+--------+--------+--------+--------+--------+--------+--------+ + * | HW_IDL | + * +--------+--------+--------+--------+--------+--------+--------+--------+ + * HW_IDH: high 8bits of IC's ID + * HW_IDL: low 8bits of IC's ID + */ +#define BU21029_HWID_REG (0x0E << 3) +#define SUPPORTED_HWID 0x0229 + +/* + * CFR0 Register (PAGE=0, ADDR=0x00, Reset value=0x20) + * +--------+--------+--------+--------+--------+--------+--------+--------+ + * | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | + * +--------+--------+--------+--------+--------+--------+--------+--------+ + * | 0 | 0 | CALIB | INTRM | 0 | 0 | 0 | 0 | + * +--------+--------+--------+--------+--------+--------+--------+--------+ + * CALIB: 0 = not to use calibration result (*) + * 1 = use calibration result + * INTRM: 0 = INT output depend on "pen down" (*) + * 1 = INT output always "0" + */ +#define BU21029_CFR0_REG (0x00 << 3) +#define CFR0_VALUE 0x00 + +/* + * CFR1 Register (PAGE=0, ADDR=0x01, Reset value=0xA6) + * +--------+--------+--------+--------+--------+--------+--------+--------+ + * | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | + * +--------+--------+--------+--------+--------+--------+--------+--------+ + * | MAV | AVE[2:0] | 0 | SMPL[2:0] | + * +--------+--------+--------+--------+--------+--------+--------+--------+ + * MAV: 0 = median average filter off + * 1 = median average filter on (*) + * AVE: AVE+1 = number of average samples for MAV, + * if AVE>SMPL, then AVE=SMPL (=3) + * SMPL: SMPL+1 = number of conversion samples for MAV (=7) + */ +#define BU21029_CFR1_REG (0x01 << 3) +#define CFR1_VALUE 0xA6 + +/* + * CFR2 Register (PAGE=0, ADDR=0x02, Reset value=0x04) + * +--------+--------+--------+--------+--------+--------+--------+--------+ + * | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | + * +--------+--------+--------+--------+--------+--------+--------+--------+ + * | INTVL_TIME[3:0] | TIME_ST_ADC[3:0] | + * +--------+--------+--------+--------+--------+--------+--------+--------+ + * INTVL_TIME: waiting time between completion of conversion + * and start of next conversion, only usable in + * autoscan mode (=20.480ms) + * TIME_ST_ADC: waiting time between application of voltage + * to panel and start of A/D conversion (=100us) + */ +#define BU21029_CFR2_REG (0x02 << 3) +#define CFR2_VALUE 0xC9 + +/* + * CFR3 Register (PAGE=0, ADDR=0x0B, Reset value=0x72) + * +--------+--------+--------+--------+--------+--------+--------+--------+ + * | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | + * +--------+--------+--------+--------+--------+--------+--------+--------+ + * | RM8 | STRETCH| PU90K | DUAL | PIDAC_OFS[3:0] | + * +--------+--------+--------+--------+--------+--------+--------+--------+ + * RM8: 0 = coordinate resolution is 12bit (*) + * 1 = coordinate resolution is 8bit + * STRETCH: 0 = SCL_STRETCH function off + * 1 = SCL_STRETCH function on (*) + * PU90K: 0 = internal pull-up resistance for touch detection is ~50kohms (*) + * 1 = internal pull-up resistance for touch detection is ~90kohms + * DUAL: 0 = dual touch detection off (*) + * 1 = dual touch detection on + * PIDAC_OFS: dual touch detection circuit adjustment, it is not necessary + * to change this from initial value + */ +#define BU21029_CFR3_REG (0x0B << 3) +#define CFR3_VALUE 0x42 + +/* + * LDO Register (PAGE=0, ADDR=0x0C, Reset value=0x00) + * +--------+--------+--------+--------+--------+--------+--------+--------+ + * | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | + * +--------+--------+--------+--------+--------+--------+--------+--------+ + * | 0 | PVDD[2:0] | 0 | AVDD[2:0] | + * +--------+--------+--------+--------+--------+--------+--------+--------+ + * PVDD: output voltage of panel output regulator (=2.000V) + * AVDD: output voltage of analog circuit regulator (=2.000V) + */ +#define BU21029_LDO_REG (0x0C << 3) +#define LDO_VALUE 0x77 + +/* + * Serial Interface Command Byte 1 (CID=1) + * +--------+--------+--------+--------+--------+--------+--------+--------+ + * | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | + * +--------+--------+--------+--------+--------+--------+--------+--------+ + * | 1 | CF | CMSK | PDM | STP | + * +--------+--------+--------+--------+--------+--------+--------+--------+ + * CF: conversion function, see table 3 in datasheet p6 (=0000, automatic scan) + * CMSK: 0 = executes convert function (*) + * 1 = reads the convert result + * PDM: 0 = power down after convert function stops (*) + * 1 = keep power on after convert function stops + * STP: 1 = abort current conversion and power down, set to "0" automatically + */ +#define BU21029_AUTOSCAN 0x80 + +/* + * The timeout value needs to be larger than INTVL_TIME + tConv4 (sample and + * conversion time), where tConv4 is calculated by formula: + * tPON + tDLY1 + (tTIME_ST_ADC + (tADC * tSMPL) * 2 + tDLY2) * 3 + * see figure 8 in datasheet p15 for details of each field. + */ +#define PEN_UP_TIMEOUT_MS 50 + +#define STOP_DELAY_MIN_US 50 +#define STOP_DELAY_MAX_US 1000 +#define START_DELAY_MS 2 +#define BUF_LEN 8 +#define SCALE_12BIT (1 << 12) +#define MAX_12BIT ((1 << 12) - 1) +#define DRIVER_NAME "bu21029" + +struct bu21029_ts_data { + struct i2c_client *client; + struct input_dev *in_dev; + struct timer_list timer; + struct regulator *vdd; + struct gpio_desc *reset_gpios; + u32 x_plate_ohms; + struct touchscreen_properties prop; +}; + +static void bu21029_touch_report(struct bu21029_ts_data *bu21029, const u8 *buf) +{ + u16 x, y, z1, z2; + u32 rz; + s32 max_pressure = input_abs_get_max(bu21029->in_dev, ABS_PRESSURE); + + /* + * compose upper 8 and lower 4 bits into a 12bit value: + * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + * | ByteH | ByteL | + * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + * |b07|b06|b05|b04|b03|b02|b01|b00|b07|b06|b05|b04|b03|b02|b01|b00| + * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + * |v11|v10|v09|v08|v07|v06|v05|v04|v03|v02|v01|v00| 0 | 0 | 0 | 0 | + * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + */ + x = (buf[0] << 4) | (buf[1] >> 4); + y = (buf[2] << 4) | (buf[3] >> 4); + z1 = (buf[4] << 4) | (buf[5] >> 4); + z2 = (buf[6] << 4) | (buf[7] >> 4); + + if (z1 && z2) { + /* + * calculate Rz (pressure resistance value) by equation: + * Rz = Rx * (x/Q) * ((z2/z1) - 1), where + * Rx is x-plate resistance, + * Q is the touch screen resolution (8bit = 256, 12bit = 4096) + * x, z1, z2 are the measured positions. + */ + rz = z2 - z1; + rz *= x; + rz *= bu21029->x_plate_ohms; + rz /= z1; + rz = DIV_ROUND_CLOSEST(rz, SCALE_12BIT); + if (rz <= max_pressure) { + touchscreen_report_pos(bu21029->in_dev, &bu21029->prop, + x, y, false); + input_report_abs(bu21029->in_dev, ABS_PRESSURE, + max_pressure - rz); + input_report_key(bu21029->in_dev, BTN_TOUCH, 1); + input_sync(bu21029->in_dev); + } + } +} + +static void bu21029_touch_release(struct timer_list *t) +{ + struct bu21029_ts_data *bu21029 = from_timer(bu21029, t, timer); + + input_report_abs(bu21029->in_dev, ABS_PRESSURE, 0); + input_report_key(bu21029->in_dev, BTN_TOUCH, 0); + input_sync(bu21029->in_dev); +} + +static irqreturn_t bu21029_touch_soft_irq(int irq, void *data) +{ + struct bu21029_ts_data *bu21029 = data; + u8 buf[BUF_LEN]; + int error; + + /* + * Read touch data and deassert interrupt (will assert again after + * INTVL_TIME + tConv4 for continuous touch) + */ + error = i2c_smbus_read_i2c_block_data(bu21029->client, BU21029_AUTOSCAN, + sizeof(buf), buf); + if (error < 0) + goto out; + + bu21029_touch_report(bu21029, buf); + + /* reset timer for pen up detection */ + mod_timer(&bu21029->timer, + jiffies + msecs_to_jiffies(PEN_UP_TIMEOUT_MS)); + +out: + return IRQ_HANDLED; +} + +static void bu21029_put_chip_in_reset(struct bu21029_ts_data *bu21029) +{ + if (bu21029->reset_gpios) { + gpiod_set_value_cansleep(bu21029->reset_gpios, 1); + usleep_range(STOP_DELAY_MIN_US, STOP_DELAY_MAX_US); + } +} + +static int bu21029_start_chip(struct input_dev *dev) +{ + struct bu21029_ts_data *bu21029 = input_get_drvdata(dev); + struct i2c_client *i2c = bu21029->client; + struct { + u8 reg; + u8 value; + } init_table[] = { + {BU21029_CFR0_REG, CFR0_VALUE}, + {BU21029_CFR1_REG, CFR1_VALUE}, + {BU21029_CFR2_REG, CFR2_VALUE}, + {BU21029_CFR3_REG, CFR3_VALUE}, + {BU21029_LDO_REG, LDO_VALUE} + }; + int error, i; + __be16 hwid; + + error = regulator_enable(bu21029->vdd); + if (error) { + dev_err(&i2c->dev, "failed to power up chip: %d", error); + return error; + } + + /* take chip out of reset */ + if (bu21029->reset_gpios) { + gpiod_set_value_cansleep(bu21029->reset_gpios, 0); + msleep(START_DELAY_MS); + } + + error = i2c_smbus_read_i2c_block_data(i2c, BU21029_HWID_REG, + sizeof(hwid), (u8 *)&hwid); + if (error < 0) { + dev_err(&i2c->dev, "failed to read HW ID\n"); + goto err_out; + } + + if (be16_to_cpu(hwid) != SUPPORTED_HWID) { + dev_err(&i2c->dev, + "unsupported HW ID 0x%x\n", be16_to_cpu(hwid)); + error = -ENODEV; + goto err_out; + } + + for (i = 0; i < ARRAY_SIZE(init_table); ++i) { + error = i2c_smbus_write_byte_data(i2c, + init_table[i].reg, + init_table[i].value); + if (error < 0) { + dev_err(&i2c->dev, + "failed to write %#02x to register %#02x: %d\n", + init_table[i].value, init_table[i].reg, + error); + goto err_out; + } + } + + error = i2c_smbus_write_byte(i2c, BU21029_AUTOSCAN); + if (error < 0) { + dev_err(&i2c->dev, "failed to start autoscan\n"); + goto err_out; + } + + enable_irq(bu21029->client->irq); + return 0; + +err_out: + bu21029_put_chip_in_reset(bu21029); + regulator_disable(bu21029->vdd); + return error; +} + +static void bu21029_stop_chip(struct input_dev *dev) +{ + struct bu21029_ts_data *bu21029 = input_get_drvdata(dev); + + disable_irq(bu21029->client->irq); + del_timer_sync(&bu21029->timer); + + bu21029_put_chip_in_reset(bu21029); + regulator_disable(bu21029->vdd); +} + +static int bu21029_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct bu21029_ts_data *bu21029; + struct input_dev *in_dev; + int error; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WRITE_BYTE | + I2C_FUNC_SMBUS_WRITE_BYTE_DATA | + I2C_FUNC_SMBUS_READ_I2C_BLOCK)) { + dev_err(&client->dev, + "i2c functionality support is not sufficient\n"); + return -EIO; + } + + bu21029 = devm_kzalloc(&client->dev, sizeof(*bu21029), GFP_KERNEL); + if (!bu21029) + return -ENOMEM; + + error = device_property_read_u32(&client->dev, "rohm,x-plate-ohms", + &bu21029->x_plate_ohms); + if (error) { + dev_err(&client->dev, + "invalid 'x-plate-ohms' supplied: %d\n", error); + return error; + } + + bu21029->vdd = devm_regulator_get(&client->dev, "vdd"); + if (IS_ERR(bu21029->vdd)) { + error = PTR_ERR(bu21029->vdd); + if (error != -EPROBE_DEFER) + dev_err(&client->dev, + "failed to acquire 'vdd' supply: %d\n", error); + return error; + } + + bu21029->reset_gpios = devm_gpiod_get_optional(&client->dev, + "reset", GPIOD_OUT_HIGH); + if (IS_ERR(bu21029->reset_gpios)) { + error = PTR_ERR(bu21029->reset_gpios); + if (error != -EPROBE_DEFER) + dev_err(&client->dev, + "failed to acquire 'reset' gpio: %d\n", error); + return error; + } + + in_dev = devm_input_allocate_device(&client->dev); + if (!in_dev) { + dev_err(&client->dev, "unable to allocate input device\n"); + return -ENOMEM; + } + + bu21029->client = client; + bu21029->in_dev = in_dev; + timer_setup(&bu21029->timer, bu21029_touch_release, 0); + + in_dev->name = DRIVER_NAME; + in_dev->id.bustype = BUS_I2C; + in_dev->open = bu21029_start_chip; + in_dev->close = bu21029_stop_chip; + + input_set_capability(in_dev, EV_KEY, BTN_TOUCH); + input_set_abs_params(in_dev, ABS_X, 0, MAX_12BIT, 0, 0); + input_set_abs_params(in_dev, ABS_Y, 0, MAX_12BIT, 0, 0); + input_set_abs_params(in_dev, ABS_PRESSURE, 0, MAX_12BIT, 0, 0); + touchscreen_parse_properties(in_dev, false, &bu21029->prop); + + input_set_drvdata(in_dev, bu21029); + + irq_set_status_flags(client->irq, IRQ_NOAUTOEN); + error = devm_request_threaded_irq(&client->dev, client->irq, + NULL, bu21029_touch_soft_irq, + IRQF_ONESHOT, DRIVER_NAME, bu21029); + if (error) { + dev_err(&client->dev, + "unable to request touch irq: %d\n", error); + return error; + } + + error = input_register_device(in_dev); + if (error) { + dev_err(&client->dev, + "unable to register input device: %d\n", error); + return error; + } + + i2c_set_clientdata(client, bu21029); + + return 0; +} + +static int __maybe_unused bu21029_suspend(struct device *dev) +{ + struct i2c_client *i2c = to_i2c_client(dev); + struct bu21029_ts_data *bu21029 = i2c_get_clientdata(i2c); + + if (!device_may_wakeup(dev)) { + mutex_lock(&bu21029->in_dev->mutex); + if (bu21029->in_dev->users) + bu21029_stop_chip(bu21029->in_dev); + mutex_unlock(&bu21029->in_dev->mutex); + } + + return 0; +} + +static int __maybe_unused bu21029_resume(struct device *dev) +{ + struct i2c_client *i2c = to_i2c_client(dev); + struct bu21029_ts_data *bu21029 = i2c_get_clientdata(i2c); + + if (!device_may_wakeup(dev)) { + mutex_lock(&bu21029->in_dev->mutex); + if (bu21029->in_dev->users) + bu21029_start_chip(bu21029->in_dev); + mutex_unlock(&bu21029->in_dev->mutex); + } + + return 0; +} +static SIMPLE_DEV_PM_OPS(bu21029_pm_ops, bu21029_suspend, bu21029_resume); + +static const struct i2c_device_id bu21029_ids[] = { + { DRIVER_NAME, 0 }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(i2c, bu21029_ids); + +#ifdef CONFIG_OF +static const struct of_device_id bu21029_of_ids[] = { + { .compatible = "rohm,bu21029" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, bu21029_of_ids); +#endif + +static struct i2c_driver bu21029_driver = { + .driver = { + .name = DRIVER_NAME, + .of_match_table = of_match_ptr(bu21029_of_ids), + .pm = &bu21029_pm_ops, + }, + .id_table = bu21029_ids, + .probe = bu21029_probe, +}; +module_i2c_driver(bu21029_driver); + +MODULE_AUTHOR("Zhu Yi <yi.zhu5@cn.bosch.com>"); +MODULE_DESCRIPTION("Rohm BU21029 touchscreen controller driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/chipone_icn8505.c b/drivers/input/touchscreen/chipone_icn8505.c new file mode 100644 index 000000000000..c768186ce856 --- /dev/null +++ b/drivers/input/touchscreen/chipone_icn8505.c @@ -0,0 +1,520 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Driver for ChipOne icn8505 i2c touchscreen controller + * + * Copyright (c) 2015-2018 Red Hat Inc. + * + * Red Hat authors: + * Hans de Goede <hdegoede@redhat.com> + */ + +#include <asm/unaligned.h> +#include <linux/acpi.h> +#include <linux/crc32.h> +#include <linux/delay.h> +#include <linux/firmware.h> +#include <linux/interrupt.h> +#include <linux/i2c.h> +#include <linux/input.h> +#include <linux/input/mt.h> +#include <linux/input/touchscreen.h> +#include <linux/module.h> + +/* Normal operation mode defines */ +#define ICN8505_REG_ADDR_WIDTH 16 + +#define ICN8505_REG_POWER 0x0004 +#define ICN8505_REG_TOUCHDATA 0x1000 +#define ICN8505_REG_CONFIGDATA 0x8000 + +/* ICN8505_REG_POWER commands */ +#define ICN8505_POWER_ACTIVE 0x00 +#define ICN8505_POWER_MONITOR 0x01 +#define ICN8505_POWER_HIBERNATE 0x02 +/* + * The Android driver uses these to turn on/off the charger filter, but the + * filter is way too aggressive making e.g. onscreen keyboards unusable. + */ +#define ICN8505_POWER_ENA_CHARGER_MODE 0x55 +#define ICN8505_POWER_DIS_CHARGER_MODE 0x66 + +#define ICN8505_MAX_TOUCHES 10 + +/* Programming mode defines */ +#define ICN8505_PROG_I2C_ADDR 0x30 +#define ICN8505_PROG_REG_ADDR_WIDTH 24 + +#define MAX_FW_UPLOAD_TRIES 3 + +struct icn8505_touch { + u8 slot; + u8 x[2]; + u8 y[2]; + u8 pressure; /* Seems more like finger width then pressure really */ + u8 event; +/* The difference between 2 and 3 is unclear */ +#define ICN8505_EVENT_NO_DATA 1 /* No finger seen yet since wakeup */ +#define ICN8505_EVENT_UPDATE1 2 /* New or updated coordinates */ +#define ICN8505_EVENT_UPDATE2 3 /* New or updated coordinates */ +#define ICN8505_EVENT_END 4 /* Finger lifted */ +} __packed; + +struct icn8505_touch_data { + u8 softbutton; + u8 touch_count; + struct icn8505_touch touches[ICN8505_MAX_TOUCHES]; +} __packed; + +struct icn8505_data { + struct i2c_client *client; + struct input_dev *input; + struct gpio_desc *wake_gpio; + struct touchscreen_properties prop; + char firmware_name[32]; +}; + +static int icn8505_read_xfer(struct i2c_client *client, u16 i2c_addr, + int reg_addr, int reg_addr_width, + void *data, int len, bool silent) +{ + u8 buf[3]; + int i, ret; + struct i2c_msg msg[2] = { + { + .addr = i2c_addr, + .buf = buf, + .len = reg_addr_width / 8, + }, + { + .addr = i2c_addr, + .flags = I2C_M_RD, + .buf = data, + .len = len, + } + }; + + for (i = 0; i < (reg_addr_width / 8); i++) + buf[i] = (reg_addr >> (reg_addr_width - (i + 1) * 8)) & 0xff; + + ret = i2c_transfer(client->adapter, msg, 2); + if (ret != ARRAY_SIZE(msg)) { + if (ret >= 0) + ret = -EIO; + if (!silent) + dev_err(&client->dev, + "Error reading addr %#x reg %#x: %d\n", + i2c_addr, reg_addr, ret); + return ret; + } + + return 0; +} + +static int icn8505_write_xfer(struct i2c_client *client, u16 i2c_addr, + int reg_addr, int reg_addr_width, + const void *data, int len, bool silent) +{ + u8 buf[3 + 32]; /* 3 bytes for 24 bit reg-addr + 32 bytes max len */ + int i, ret; + struct i2c_msg msg = { + .addr = i2c_addr, + .buf = buf, + .len = reg_addr_width / 8 + len, + }; + + if (WARN_ON(len > 32)) + return -EINVAL; + + for (i = 0; i < (reg_addr_width / 8); i++) + buf[i] = (reg_addr >> (reg_addr_width - (i + 1) * 8)) & 0xff; + + memcpy(buf + reg_addr_width / 8, data, len); + + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret != 1) { + if (ret >= 0) + ret = -EIO; + if (!silent) + dev_err(&client->dev, + "Error writing addr %#x reg %#x: %d\n", + i2c_addr, reg_addr, ret); + return ret; + } + + return 0; +} + +static int icn8505_read_data(struct icn8505_data *icn8505, int reg, + void *buf, int len) +{ + return icn8505_read_xfer(icn8505->client, icn8505->client->addr, reg, + ICN8505_REG_ADDR_WIDTH, buf, len, false); +} + +static int icn8505_read_reg_silent(struct icn8505_data *icn8505, int reg) +{ + u8 buf; + int error; + + error = icn8505_read_xfer(icn8505->client, icn8505->client->addr, reg, + ICN8505_REG_ADDR_WIDTH, &buf, 1, true); + if (error) + return error; + + return buf; +} + +static int icn8505_write_reg(struct icn8505_data *icn8505, int reg, u8 val) +{ + return icn8505_write_xfer(icn8505->client, icn8505->client->addr, reg, + ICN8505_REG_ADDR_WIDTH, &val, 1, false); +} + +static int icn8505_read_prog_data(struct icn8505_data *icn8505, int reg, + void *buf, int len) +{ + return icn8505_read_xfer(icn8505->client, ICN8505_PROG_I2C_ADDR, reg, + ICN8505_PROG_REG_ADDR_WIDTH, buf, len, false); +} + +static int icn8505_write_prog_data(struct icn8505_data *icn8505, int reg, + const void *buf, int len) +{ + return icn8505_write_xfer(icn8505->client, ICN8505_PROG_I2C_ADDR, reg, + ICN8505_PROG_REG_ADDR_WIDTH, buf, len, false); +} + +static int icn8505_write_prog_reg(struct icn8505_data *icn8505, int reg, u8 val) +{ + return icn8505_write_xfer(icn8505->client, ICN8505_PROG_I2C_ADDR, reg, + ICN8505_PROG_REG_ADDR_WIDTH, &val, 1, false); +} + +/* + * Note this function uses a number of magic register addresses and values, + * there are deliberately no defines for these because the algorithm is taken + * from the icn85xx Android driver and I do not want to make up possibly wrong + * names for the addresses and/or values. + */ +static int icn8505_try_fw_upload(struct icn8505_data *icn8505, + const struct firmware *fw) +{ + struct device *dev = &icn8505->client->dev; + size_t offset, count; + int error; + u8 buf[4]; + u32 crc; + + /* Put the controller in programming mode */ + error = icn8505_write_prog_reg(icn8505, 0xcc3355, 0x5a); + if (error) + return error; + + usleep_range(2000, 5000); + + error = icn8505_write_prog_reg(icn8505, 0x040400, 0x01); + if (error) + return error; + + usleep_range(2000, 5000); + + error = icn8505_read_prog_data(icn8505, 0x040002, buf, 1); + if (error) + return error; + + if (buf[0] != 0x85) { + dev_err(dev, "Failed to enter programming mode\n"); + return -ENODEV; + } + + usleep_range(1000, 5000); + + /* Enable CRC mode */ + error = icn8505_write_prog_reg(icn8505, 0x40028, 1); + if (error) + return error; + + /* Send the firmware to SRAM */ + for (offset = 0; offset < fw->size; offset += count) { + count = min_t(size_t, fw->size - offset, 32); + error = icn8505_write_prog_data(icn8505, offset, + fw->data + offset, count); + if (error) + return error; + } + + /* Disable CRC mode */ + error = icn8505_write_prog_reg(icn8505, 0x40028, 0); + if (error) + return error; + + /* Get and check length and CRC */ + error = icn8505_read_prog_data(icn8505, 0x40034, buf, 2); + if (error) + return error; + + if (get_unaligned_le16(buf) != fw->size) { + dev_warn(dev, "Length mismatch after uploading fw\n"); + return -EIO; + } + + error = icn8505_read_prog_data(icn8505, 0x4002c, buf, 4); + if (error) + return error; + + crc = crc32_be(0, fw->data, fw->size); + if (get_unaligned_le32(buf) != crc) { + dev_warn(dev, "CRC mismatch after uploading fw\n"); + return -EIO; + } + + /* Boot controller from SRAM */ + error = icn8505_write_prog_reg(icn8505, 0x40400, 0x03); + if (error) + return error; + + usleep_range(2000, 5000); + return 0; +} + +static int icn8505_upload_fw(struct icn8505_data *icn8505) +{ + struct device *dev = &icn8505->client->dev; + const struct firmware *fw; + int i, error; + + /* + * Always load the firmware, even if we don't need it at boot, we + * we may need it at resume. Having loaded it once will make the + * firmware class code cache it at suspend/resume. + */ + error = request_firmware(&fw, icn8505->firmware_name, dev); + if (error) { + dev_err(dev, "Firmware request error %d\n", error); + return error; + } + + /* Check if the controller is not already up and running */ + if (icn8505_read_reg_silent(icn8505, 0x000a) == 0x85) + goto success; + + for (i = 1; i <= MAX_FW_UPLOAD_TRIES; i++) { + error = icn8505_try_fw_upload(icn8505, fw); + if (!error) + goto success; + + dev_err(dev, "Failed to upload firmware: %d (attempt %d/%d)\n", + error, i, MAX_FW_UPLOAD_TRIES); + usleep_range(2000, 5000); + } + +success: + release_firmware(fw); + return error; +} + +static bool icn8505_touch_active(u8 event) +{ + return event == ICN8505_EVENT_UPDATE1 || + event == ICN8505_EVENT_UPDATE2; +} + +static irqreturn_t icn8505_irq(int irq, void *dev_id) +{ + struct icn8505_data *icn8505 = dev_id; + struct device *dev = &icn8505->client->dev; + struct icn8505_touch_data touch_data; + int i, error; + + error = icn8505_read_data(icn8505, ICN8505_REG_TOUCHDATA, + &touch_data, sizeof(touch_data)); + if (error) { + dev_err(dev, "Error reading touch data: %d\n", error); + return IRQ_HANDLED; + } + + if (touch_data.touch_count > ICN8505_MAX_TOUCHES) { + dev_warn(dev, "Too many touches %d > %d\n", + touch_data.touch_count, ICN8505_MAX_TOUCHES); + touch_data.touch_count = ICN8505_MAX_TOUCHES; + } + + for (i = 0; i < touch_data.touch_count; i++) { + struct icn8505_touch *touch = &touch_data.touches[i]; + bool act = icn8505_touch_active(touch->event); + + input_mt_slot(icn8505->input, touch->slot); + input_mt_report_slot_state(icn8505->input, MT_TOOL_FINGER, act); + if (!act) + continue; + + touchscreen_report_pos(icn8505->input, &icn8505->prop, + get_unaligned_le16(touch->x), + get_unaligned_le16(touch->y), + true); + } + + input_mt_sync_frame(icn8505->input); + input_report_key(icn8505->input, KEY_LEFTMETA, + touch_data.softbutton == 1); + input_sync(icn8505->input); + + return IRQ_HANDLED; +} + +static int icn8505_probe_acpi(struct icn8505_data *icn8505, struct device *dev) +{ + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + const char *subsys = "unknown"; + struct acpi_device *adev; + union acpi_object *obj; + acpi_status status; + + adev = ACPI_COMPANION(dev); + if (!adev) + return -ENODEV; + + status = acpi_evaluate_object(adev->handle, "_SUB", NULL, &buffer); + if (ACPI_SUCCESS(status)) { + obj = buffer.pointer; + if (obj->type == ACPI_TYPE_STRING) + subsys = obj->string.pointer; + else + dev_warn(dev, "Warning ACPI _SUB did not return a string\n"); + } else { + dev_warn(dev, "Warning ACPI _SUB failed: %#x\n", status); + buffer.pointer = NULL; + } + + snprintf(icn8505->firmware_name, sizeof(icn8505->firmware_name), + "chipone/icn8505-%s.fw", subsys); + + kfree(buffer.pointer); + return 0; +} + +static int icn8505_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct icn8505_data *icn8505; + struct input_dev *input; + __le16 resolution[2]; + int error; + + if (!client->irq) { + dev_err(dev, "No irq specified\n"); + return -EINVAL; + } + + icn8505 = devm_kzalloc(dev, sizeof(*icn8505), GFP_KERNEL); + if (!icn8505) + return -ENOMEM; + + input = devm_input_allocate_device(dev); + if (!input) + return -ENOMEM; + + input->name = client->name; + input->id.bustype = BUS_I2C; + + input_set_capability(input, EV_ABS, ABS_MT_POSITION_X); + input_set_capability(input, EV_ABS, ABS_MT_POSITION_Y); + input_set_capability(input, EV_KEY, KEY_LEFTMETA); + + icn8505->client = client; + icn8505->input = input; + input_set_drvdata(input, icn8505); + + error = icn8505_probe_acpi(icn8505, dev); + if (error) + return error; + + error = icn8505_upload_fw(icn8505); + if (error) + return error; + + error = icn8505_read_data(icn8505, ICN8505_REG_CONFIGDATA, + resolution, sizeof(resolution)); + if (error) { + dev_err(dev, "Error reading resolution: %d\n", error); + return error; + } + + input_set_abs_params(input, ABS_MT_POSITION_X, 0, + le16_to_cpu(resolution[0]) - 1, 0, 0); + input_set_abs_params(input, ABS_MT_POSITION_Y, 0, + le16_to_cpu(resolution[1]) - 1, 0, 0); + + touchscreen_parse_properties(input, true, &icn8505->prop); + if (!input_abs_get_max(input, ABS_MT_POSITION_X) || + !input_abs_get_max(input, ABS_MT_POSITION_Y)) { + dev_err(dev, "Error touchscreen-size-x and/or -y missing\n"); + return -EINVAL; + } + + error = input_mt_init_slots(input, ICN8505_MAX_TOUCHES, + INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED); + if (error) + return error; + + error = devm_request_threaded_irq(dev, client->irq, NULL, icn8505_irq, + IRQF_ONESHOT, client->name, icn8505); + if (error) { + dev_err(dev, "Error requesting irq: %d\n", error); + return error; + } + + error = input_register_device(input); + if (error) + return error; + + i2c_set_clientdata(client, icn8505); + return 0; +} + +static int __maybe_unused icn8505_suspend(struct device *dev) +{ + struct icn8505_data *icn8505 = i2c_get_clientdata(to_i2c_client(dev)); + + disable_irq(icn8505->client->irq); + + icn8505_write_reg(icn8505, ICN8505_REG_POWER, ICN8505_POWER_HIBERNATE); + + return 0; +} + +static int __maybe_unused icn8505_resume(struct device *dev) +{ + struct icn8505_data *icn8505 = i2c_get_clientdata(to_i2c_client(dev)); + int error; + + error = icn8505_upload_fw(icn8505); + if (error) + return error; + + enable_irq(icn8505->client->irq); + return 0; +} + +static SIMPLE_DEV_PM_OPS(icn8505_pm_ops, icn8505_suspend, icn8505_resume); + +static const struct acpi_device_id icn8505_acpi_match[] = { + { "CHPN0001" }, + { } +}; +MODULE_DEVICE_TABLE(acpi, icn8505_acpi_match); + +static struct i2c_driver icn8505_driver = { + .driver = { + .name = "chipone_icn8505", + .pm = &icn8505_pm_ops, + .acpi_match_table = icn8505_acpi_match, + }, + .probe_new = icn8505_probe, +}; + +module_i2c_driver(icn8505_driver); + +MODULE_DESCRIPTION("ChipOne icn8505 I2C Touchscreen Driver"); +MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/eeti_ts.c b/drivers/input/touchscreen/eeti_ts.c index 2facad75eb6d..7fe41965c5d1 100644 --- a/drivers/input/touchscreen/eeti_ts.c +++ b/drivers/input/touchscreen/eeti_ts.c @@ -1,9 +1,9 @@ /* * Touch Screen driver for EETI's I2C connected touch screen panels - * Copyright (c) 2009 Daniel Mack <daniel@caiaq.de> + * Copyright (c) 2009,2018 Daniel Mack <daniel@zonque.org> * * See EETI's software guide for the protocol specification: - * http://home.eeti.com.tw/web20/eg/guide.htm + * http://home.eeti.com.tw/documentation.html * * Based on migor_ts.c * Copyright (c) 2008 Magnus Damm @@ -25,28 +25,22 @@ */ #include <linux/module.h> -#include <linux/moduleparam.h> #include <linux/kernel.h> #include <linux/input.h> +#include <linux/input/touchscreen.h> #include <linux/interrupt.h> #include <linux/i2c.h> #include <linux/timer.h> #include <linux/gpio/consumer.h> +#include <linux/of.h> #include <linux/slab.h> #include <asm/unaligned.h> -static bool flip_x; -module_param(flip_x, bool, 0644); -MODULE_PARM_DESC(flip_x, "flip x coordinate"); - -static bool flip_y; -module_param(flip_y, bool, 0644); -MODULE_PARM_DESC(flip_y, "flip y coordinate"); - struct eeti_ts { struct i2c_client *client; struct input_dev *input; struct gpio_desc *attn_gpio; + struct touchscreen_properties props; bool running; }; @@ -73,17 +67,10 @@ static void eeti_ts_report_event(struct eeti_ts *eeti, u8 *buf) x >>= res - EETI_TS_BITDEPTH; y >>= res - EETI_TS_BITDEPTH; - if (flip_x) - x = EETI_MAXVAL - x; - - if (flip_y) - y = EETI_MAXVAL - y; - if (buf[0] & REPORT_BIT_HAS_PRESSURE) input_report_abs(eeti->input, ABS_PRESSURE, buf[5]); - input_report_abs(eeti->input, ABS_X, x); - input_report_abs(eeti->input, ABS_Y, y); + touchscreen_report_pos(eeti->input, &eeti->props, x, y, false); input_report_key(eeti->input, BTN_TOUCH, buf[0] & REPORT_BIT_PRESSED); input_sync(eeti->input); } @@ -178,6 +165,8 @@ static int eeti_ts_probe(struct i2c_client *client, input_set_abs_params(input, ABS_Y, 0, EETI_MAXVAL, 0, 0); input_set_abs_params(input, ABS_PRESSURE, 0, 0xff, 0, 0); + touchscreen_parse_properties(input, false, &eeti->props); + input->name = client->name; input->id.bustype = BUS_I2C; input->open = eeti_ts_open; @@ -262,10 +251,18 @@ static const struct i2c_device_id eeti_ts_id[] = { }; MODULE_DEVICE_TABLE(i2c, eeti_ts_id); +#ifdef CONFIG_OF +static const struct of_device_id of_eeti_ts_match[] = { + { .compatible = "eeti,exc3000-i2c", }, + { } +}; +#endif + static struct i2c_driver eeti_ts_driver = { .driver = { .name = "eeti_ts", .pm = &eeti_ts_pm, + .of_match_table = of_match_ptr(of_eeti_ts_match), }, .probe = eeti_ts_probe, .id_table = eeti_ts_id, @@ -274,5 +271,5 @@ static struct i2c_driver eeti_ts_driver = { module_i2c_driver(eeti_ts_driver); MODULE_DESCRIPTION("EETI Touchscreen driver"); -MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>"); +MODULE_AUTHOR("Daniel Mack <daniel@zonque.org>"); MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/egalax_ts.c b/drivers/input/touchscreen/egalax_ts.c index 752ae9cf4514..80e69bb8283e 100644 --- a/drivers/input/touchscreen/egalax_ts.c +++ b/drivers/input/touchscreen/egalax_ts.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Driver for EETI eGalax Multiple Touch Controller * * Copyright (C) 2011 Freescale Semiconductor, Inc. * * based on max11801_ts.c - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ /* EETI eGalax serial touch screen controller is a I2C based multiple diff --git a/drivers/input/touchscreen/fsl-imx25-tcq.c b/drivers/input/touchscreen/fsl-imx25-tcq.c index 47fe1f184bbc..1d6c8f490b40 100644 --- a/drivers/input/touchscreen/fsl-imx25-tcq.c +++ b/drivers/input/touchscreen/fsl-imx25-tcq.c @@ -1,16 +1,11 @@ -/* - * Copyright (C) 2014-2015 Pengutronix, Markus Pargmann <mpa@pengutronix.de> - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License version 2 as published by the - * Free Software Foundation. - * - * Based on driver from 2011: - * Juergen Beisert, Pengutronix <kernel@pengutronix.de> - * - * This is the driver for the imx25 TCQ (Touchscreen Conversion Queue) - * connected to the imx25 ADC. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (C) 2014-2015 Pengutronix, Markus Pargmann <mpa@pengutronix.de> +// Based on driver from 2011: +// Juergen Beisert, Pengutronix <kernel@pengutronix.de> +// +// This is the driver for the imx25 TCQ (Touchscreen Conversion Queue) +// connected to the imx25 ADC. #include <linux/clk.h> #include <linux/device.h> diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c index 9736c83dd418..f2d9c2c41885 100644 --- a/drivers/input/touchscreen/goodix.c +++ b/drivers/input/touchscreen/goodix.c @@ -933,6 +933,7 @@ MODULE_DEVICE_TABLE(i2c, goodix_ts_id); #ifdef CONFIG_ACPI static const struct acpi_device_id goodix_acpi_match[] = { { "GDIX1001", 0 }, + { "GDIX1002", 0 }, { } }; MODULE_DEVICE_TABLE(acpi, goodix_acpi_match); diff --git a/drivers/input/touchscreen/gunze.c b/drivers/input/touchscreen/gunze.c index 481586909d28..054c2537b392 100644 --- a/drivers/input/touchscreen/gunze.c +++ b/drivers/input/touchscreen/gunze.c @@ -20,10 +20,6 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: - * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include <linux/errno.h> diff --git a/drivers/input/touchscreen/imx6ul_tsc.c b/drivers/input/touchscreen/imx6ul_tsc.c index ee82a975bfd2..c10fc594f94d 100644 --- a/drivers/input/touchscreen/imx6ul_tsc.c +++ b/drivers/input/touchscreen/imx6ul_tsc.c @@ -1,12 +1,8 @@ -/* - * Freescale i.MX6UL touchscreen controller driver - * - * Copyright (C) 2015 Freescale Semiconductor, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// Freescale i.MX6UL touchscreen controller driver +// +// Copyright (C) 2015 Freescale Semiconductor, Inc. #include <linux/errno.h> #include <linux/kernel.h> diff --git a/drivers/input/touchscreen/mk712.c b/drivers/input/touchscreen/mk712.c index bd5352824f77..c179060525ae 100644 --- a/drivers/input/touchscreen/mk712.c +++ b/drivers/input/touchscreen/mk712.c @@ -17,7 +17,7 @@ * found in Gateway AOL Connected Touchpad computers. * * Documentation for ICS MK712 can be found at: - * http://www.idt.com/products/getDoc.cfm?docID=18713923 + * https://www.idt.com/general-parts/mk712-touch-screen-controller */ /* diff --git a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c index f1043ae71dcc..b86c1e5fbc11 100644 --- a/drivers/input/touchscreen/ti_am335x_tsc.c +++ b/drivers/input/touchscreen/ti_am335x_tsc.c @@ -34,6 +34,8 @@ #define SEQ_SETTLE 275 #define MAX_12BIT ((1 << 12) - 1) +#define TSC_IRQENB_MASK (IRQENB_FIFO0THRES | IRQENB_EOS | IRQENB_HW_PEN) + static const int config_pins[] = { STEPCONFIG_XPP, STEPCONFIG_XNN, @@ -274,6 +276,7 @@ static irqreturn_t titsc_irq(int irq, void *dev) if (status & IRQENB_HW_PEN) { ts_dev->pen_down = true; irqclr |= IRQENB_HW_PEN; + pm_stay_awake(ts_dev->mfd_tscadc->dev); } if (status & IRQENB_PENUP) { @@ -283,6 +286,7 @@ static irqreturn_t titsc_irq(int irq, void *dev) input_report_key(input_dev, BTN_TOUCH, 0); input_report_abs(input_dev, ABS_PRESSURE, 0); input_sync(input_dev); + pm_relax(ts_dev->mfd_tscadc->dev); } else { ts_dev->pen_down = true; } @@ -432,6 +436,7 @@ static int titsc_probe(struct platform_device *pdev) goto err_free_mem; } + titsc_writel(ts_dev, REG_IRQSTATUS, TSC_IRQENB_MASK); titsc_writel(ts_dev, REG_IRQENABLE, IRQENB_FIFO0THRES); titsc_writel(ts_dev, REG_IRQENABLE, IRQENB_EOS); err = titsc_config_wires(ts_dev); @@ -495,6 +500,7 @@ static int __maybe_unused titsc_suspend(struct device *dev) tscadc_dev = ti_tscadc_dev_get(to_platform_device(dev)); if (device_may_wakeup(tscadc_dev->dev)) { + titsc_writel(ts_dev, REG_IRQSTATUS, TSC_IRQENB_MASK); idle = titsc_readl(ts_dev, REG_IRQENABLE); titsc_writel(ts_dev, REG_IRQENABLE, (idle | IRQENB_HW_PEN)); @@ -513,6 +519,7 @@ static int __maybe_unused titsc_resume(struct device *dev) titsc_writel(ts_dev, REG_IRQWAKEUP, 0x00); titsc_writel(ts_dev, REG_IRQCLR, IRQENB_HW_PEN); + pm_relax(ts_dev->mfd_tscadc->dev); } titsc_step_config(ts_dev); titsc_writel(ts_dev, REG_FIFO0THR, diff --git a/drivers/input/touchscreen/usbtouchscreen.c b/drivers/input/touchscreen/usbtouchscreen.c index c6cf90868503..d61570d64ee7 100644 --- a/drivers/input/touchscreen/usbtouchscreen.c +++ b/drivers/input/touchscreen/usbtouchscreen.c @@ -440,6 +440,8 @@ static int panjit_read_data(struct usbtouch_usb *dev, unsigned char *pkt) #define MTOUCHUSB_RESET 7 #define MTOUCHUSB_REQ_CTRLLR_ID 10 +#define MTOUCHUSB_REQ_CTRLLR_ID_LEN 16 + static int mtouch_read_data(struct usbtouch_usb *dev, unsigned char *pkt) { if (hwcalib_xy) { @@ -454,11 +456,93 @@ static int mtouch_read_data(struct usbtouch_usb *dev, unsigned char *pkt) return 1; } +struct mtouch_priv { + u8 fw_rev_major; + u8 fw_rev_minor; +}; + +static ssize_t mtouch_firmware_rev_show(struct device *dev, + struct device_attribute *attr, char *output) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct usbtouch_usb *usbtouch = usb_get_intfdata(intf); + struct mtouch_priv *priv = usbtouch->priv; + + return scnprintf(output, PAGE_SIZE, "%1x.%1x\n", + priv->fw_rev_major, priv->fw_rev_minor); +} +static DEVICE_ATTR(firmware_rev, 0444, mtouch_firmware_rev_show, NULL); + +static struct attribute *mtouch_attrs[] = { + &dev_attr_firmware_rev.attr, + NULL +}; + +static const struct attribute_group mtouch_attr_group = { + .attrs = mtouch_attrs, +}; + +static int mtouch_get_fw_revision(struct usbtouch_usb *usbtouch) +{ + struct usb_device *udev = interface_to_usbdev(usbtouch->interface); + struct mtouch_priv *priv = usbtouch->priv; + u8 *buf; + int ret; + + buf = kzalloc(MTOUCHUSB_REQ_CTRLLR_ID_LEN, GFP_NOIO); + if (!buf) + return -ENOMEM; + + ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + MTOUCHUSB_REQ_CTRLLR_ID, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, 0, buf, MTOUCHUSB_REQ_CTRLLR_ID_LEN, + USB_CTRL_SET_TIMEOUT); + if (ret != MTOUCHUSB_REQ_CTRLLR_ID_LEN) { + dev_warn(&usbtouch->interface->dev, + "Failed to read FW rev: %d\n", ret); + ret = ret < 0 ? ret : -EIO; + goto free; + } + + priv->fw_rev_major = buf[3]; + priv->fw_rev_minor = buf[4]; + + ret = 0; + +free: + kfree(buf); + return ret; +} + +static int mtouch_alloc(struct usbtouch_usb *usbtouch) +{ + int ret; + + usbtouch->priv = kmalloc(sizeof(struct mtouch_priv), GFP_KERNEL); + if (!usbtouch->priv) + return -ENOMEM; + + ret = sysfs_create_group(&usbtouch->interface->dev.kobj, + &mtouch_attr_group); + if (ret) { + kfree(usbtouch->priv); + usbtouch->priv = NULL; + return ret; + } + + return 0; +} + static int mtouch_init(struct usbtouch_usb *usbtouch) { int ret, i; struct usb_device *udev = interface_to_usbdev(usbtouch->interface); + ret = mtouch_get_fw_revision(usbtouch); + if (ret) + return ret; + ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), MTOUCHUSB_RESET, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, @@ -492,6 +576,14 @@ static int mtouch_init(struct usbtouch_usb *usbtouch) return 0; } + +static void mtouch_exit(struct usbtouch_usb *usbtouch) +{ + struct mtouch_priv *priv = usbtouch->priv; + + sysfs_remove_group(&usbtouch->interface->dev.kobj, &mtouch_attr_group); + kfree(priv); +} #endif @@ -1119,7 +1211,9 @@ static struct usbtouch_device_info usbtouch_dev_info[] = { .max_yc = 0x4000, .rept_size = 11, .read_data = mtouch_read_data, + .alloc = mtouch_alloc, .init = mtouch_init, + .exit = mtouch_exit, }, #endif diff --git a/drivers/input/touchscreen/wdt87xx_i2c.c b/drivers/input/touchscreen/wdt87xx_i2c.c index 20f7f3902757..166edeb77776 100644 --- a/drivers/input/touchscreen/wdt87xx_i2c.c +++ b/drivers/input/touchscreen/wdt87xx_i2c.c @@ -1142,7 +1142,7 @@ static int __maybe_unused wdt87xx_resume(struct device *dev) * The chip may have been reset while system is resuming, * give it some time to settle. */ - mdelay(100); + msleep(100); error = wdt87xx_send_command(client, VND_CMD_START, 0); if (error) diff --git a/drivers/mfd/cros_ec.c b/drivers/mfd/cros_ec.c index d61024141e2b..36156a41499c 100644 --- a/drivers/mfd/cros_ec.c +++ b/drivers/mfd/cros_ec.c @@ -229,7 +229,7 @@ int cros_ec_suspend(struct cros_ec_device *ec_dev) } EXPORT_SYMBOL(cros_ec_suspend); -static void cros_ec_drain_events(struct cros_ec_device *ec_dev) +static void cros_ec_report_events_during_suspend(struct cros_ec_device *ec_dev) { while (cros_ec_get_next_event(ec_dev, NULL) > 0) blocking_notifier_call_chain(&ec_dev->event_notifier, @@ -253,21 +253,16 @@ int cros_ec_resume(struct cros_ec_device *ec_dev) dev_dbg(ec_dev->dev, "Error %d sending resume event to ec", ret); - /* - * In some cases, we need to distinguish between events that occur - * during suspend if the EC is not a wake source. For example, - * keypresses during suspend should be discarded if it does not wake - * the system. - * - * If the EC is not a wake source, drain the event queue and mark them - * as "queued during suspend". - */ if (ec_dev->wake_enabled) { disable_irq_wake(ec_dev->irq); ec_dev->wake_enabled = 0; - } else { - cros_ec_drain_events(ec_dev); } + /* + * Let the mfd devices know about events that occur during + * suspend. This way the clients know what to do with them. + */ + cros_ec_report_events_during_suspend(ec_dev); + return 0; } diff --git a/drivers/platform/chrome/chromeos_laptop.c b/drivers/platform/chrome/chromeos_laptop.c index 5c47f451e43b..3cecf7933f75 100644 --- a/drivers/platform/chrome/chromeos_laptop.c +++ b/drivers/platform/chrome/chromeos_laptop.c @@ -6,6 +6,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <linux/acpi.h> #include <linux/dmi.h> #include <linux/i2c.h> #include <linux/input.h> @@ -54,6 +55,11 @@ struct i2c_peripheral { struct i2c_client *client; }; +struct acpi_peripheral { + char hid[ACPI_ID_LEN]; + const struct property_entry *properties; +}; + struct chromeos_laptop { /* * Note that we can't mark this pointer as const because @@ -61,6 +67,9 @@ struct chromeos_laptop { */ struct i2c_peripheral *i2c_peripherals; unsigned int num_i2c_peripherals; + + const struct acpi_peripheral *acpi_peripherals; + unsigned int num_acpi_peripherals; }; static const struct chromeos_laptop *cros_laptop; @@ -148,6 +157,38 @@ static void chromeos_laptop_check_adapter(struct i2c_adapter *adapter) } } +static bool chromeos_laptop_adjust_client(struct i2c_client *client) +{ + const struct acpi_peripheral *acpi_dev; + struct acpi_device_id acpi_ids[2] = { }; + int i; + int error; + + if (!has_acpi_companion(&client->dev)) + return false; + + for (i = 0; i < cros_laptop->num_acpi_peripherals; i++) { + acpi_dev = &cros_laptop->acpi_peripherals[i]; + + memcpy(acpi_ids[0].id, acpi_dev->hid, ACPI_ID_LEN); + + if (acpi_match_device(acpi_ids, &client->dev)) { + error = device_add_properties(&client->dev, + acpi_dev->properties); + if (error) { + dev_err(&client->dev, + "failed to add properties: %d\n", + error); + break; + } + + return true; + } + } + + return false; +} + static void chromeos_laptop_detach_i2c_client(struct i2c_client *client) { struct i2c_peripheral *i2c_dev; @@ -170,6 +211,8 @@ static int chromeos_laptop_i2c_notifier_call(struct notifier_block *nb, case BUS_NOTIFY_ADD_DEVICE: if (dev->type == &i2c_adapter_type) chromeos_laptop_check_adapter(to_i2c_adapter(dev)); + else if (dev->type == &i2c_client_type) + chromeos_laptop_adjust_client(to_i2c_client(dev)); break; case BUS_NOTIFY_REMOVED_DEVICE: @@ -191,6 +234,12 @@ static const struct chromeos_laptop _name __initconst = { \ .num_i2c_peripherals = ARRAY_SIZE(_name##_peripherals), \ } +#define DECLARE_ACPI_CROS_LAPTOP(_name) \ +static const struct chromeos_laptop _name __initconst = { \ + .acpi_peripherals = _name##_peripherals, \ + .num_acpi_peripherals = ARRAY_SIZE(_name##_peripherals), \ +} + static struct i2c_peripheral samsung_series_5_550_peripherals[] __initdata = { /* Touchpad. */ { @@ -234,16 +283,25 @@ static const int chromebook_pixel_tp_keys[] __initconst = { static const struct property_entry chromebook_pixel_trackpad_props[] __initconst = { + PROPERTY_ENTRY_STRING("compatible", "atmel,maxtouch"), PROPERTY_ENTRY_U32_ARRAY("linux,gpio-keymap", chromebook_pixel_tp_keys), { } }; +static const struct property_entry +chromebook_atmel_touchscreen_props[] __initconst = { + PROPERTY_ENTRY_STRING("compatible", "atmel,maxtouch"), + { } +}; + static struct i2c_peripheral chromebook_pixel_peripherals[] __initdata = { /* Touch Screen. */ { .board_info = { I2C_BOARD_INFO("atmel_mxt_ts", ATMEL_TS_I2C_ADDR), + .properties = + chromebook_atmel_touchscreen_props, .flags = I2C_CLIENT_WAKE, }, .dmi_name = "touchscreen", @@ -354,6 +412,8 @@ static struct i2c_peripheral acer_c720_peripherals[] __initdata = { .board_info = { I2C_BOARD_INFO("atmel_mxt_ts", ATMEL_TS_I2C_ADDR), + .properties = + chromebook_atmel_touchscreen_props, .flags = I2C_CLIENT_WAKE, }, .dmi_name = "touchscreen", @@ -419,6 +479,47 @@ static struct i2c_peripheral cr48_peripherals[] __initdata = { }; DECLARE_CROS_LAPTOP(cr48); +static const u32 samus_touchpad_buttons[] __initconst = { + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + BTN_LEFT +}; + +static const struct property_entry samus_trackpad_props[] __initconst = { + PROPERTY_ENTRY_STRING("compatible", "atmel,maxtouch"), + PROPERTY_ENTRY_U32_ARRAY("linux,gpio-keymap", samus_touchpad_buttons), + { } +}; + +static struct acpi_peripheral samus_peripherals[] __initdata = { + /* Touchpad */ + { + .hid = "ATML0000", + .properties = samus_trackpad_props, + }, + /* Touchsceen */ + { + .hid = "ATML0001", + .properties = chromebook_atmel_touchscreen_props, + }, +}; +DECLARE_ACPI_CROS_LAPTOP(samus); + +static struct acpi_peripheral generic_atmel_peripherals[] __initdata = { + /* Touchpad */ + { + .hid = "ATML0000", + .properties = chromebook_pixel_trackpad_props, + }, + /* Touchsceen */ + { + .hid = "ATML0001", + .properties = chromebook_atmel_touchscreen_props, + }, +}; +DECLARE_ACPI_CROS_LAPTOP(generic_atmel); + static const struct dmi_system_id chromeos_laptop_dmi_table[] __initconst = { { .ident = "Samsung Series 5 550", @@ -502,17 +603,64 @@ static const struct dmi_system_id chromeos_laptop_dmi_table[] __initconst = { }, .driver_data = (void *)&cr48, }, + /* Devices with peripherals incompletely described in ACPI */ + { + .ident = "Chromebook Pro", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Google"), + DMI_MATCH(DMI_PRODUCT_NAME, "Caroline"), + }, + .driver_data = (void *)&samus, + }, + { + .ident = "Google Pixel 2 (2015)", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"), + DMI_MATCH(DMI_PRODUCT_NAME, "Samus"), + }, + .driver_data = (void *)&samus, + }, + { + /* + * Other Chromebooks with Atmel touch controllers: + * - Celes, Winky (touchpad) + * - Clapper, Expresso, Rambi, Glimmer (touchscreen) + */ + .ident = "Other Chromebook", + .matches = { + /* + * This will match all Google devices, not only devices + * with Atmel, but we will validate that the device + * actually has matching peripherals. + */ + DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"), + }, + .driver_data = (void *)&generic_atmel, + }, { } }; MODULE_DEVICE_TABLE(dmi, chromeos_laptop_dmi_table); -static int __init chromeos_laptop_scan_adapter(struct device *dev, void *data) +static int __init chromeos_laptop_scan_peripherals(struct device *dev, void *data) { - struct i2c_adapter *adapter; + int error; - adapter = i2c_verify_adapter(dev); - if (adapter) - chromeos_laptop_check_adapter(adapter); + if (dev->type == &i2c_adapter_type) { + chromeos_laptop_check_adapter(to_i2c_adapter(dev)); + } else if (dev->type == &i2c_client_type) { + if (chromeos_laptop_adjust_client(to_i2c_client(dev))) { + /* + * Now that we have needed properties re-trigger + * driver probe in case driver was initialized + * earlier and probe failed. + */ + error = device_attach(dev); + if (error < 0) + dev_warn(dev, + "%s: device_attach() failed: %d\n", + __func__, error); + } + } return 0; } @@ -556,27 +704,24 @@ static int __init chromeos_laptop_setup_irq(struct i2c_peripheral *i2c_dev) return 0; } -static struct chromeos_laptop * __init -chromeos_laptop_prepare(const struct chromeos_laptop *src) +static int __init +chromeos_laptop_prepare_i2c_peripherals(struct chromeos_laptop *cros_laptop, + const struct chromeos_laptop *src) { - struct chromeos_laptop *cros_laptop; struct i2c_peripheral *i2c_dev; struct i2c_board_info *info; - int error; int i; + int error; - cros_laptop = kzalloc(sizeof(*cros_laptop), GFP_KERNEL); - if (!cros_laptop) - return ERR_PTR(-ENOMEM); + if (!src->num_i2c_peripherals) + return 0; cros_laptop->i2c_peripherals = kmemdup(src->i2c_peripherals, src->num_i2c_peripherals * sizeof(*src->i2c_peripherals), GFP_KERNEL); - if (!cros_laptop->i2c_peripherals) { - error = -ENOMEM; - goto err_free_cros_laptop; - } + if (!cros_laptop->i2c_peripherals) + return -ENOMEM; cros_laptop->num_i2c_peripherals = src->num_i2c_peripherals; @@ -586,7 +731,7 @@ chromeos_laptop_prepare(const struct chromeos_laptop *src) error = chromeos_laptop_setup_irq(i2c_dev); if (error) - goto err_destroy_cros_peripherals; + goto err_out; /* We need to deep-copy properties */ if (info->properties) { @@ -594,14 +739,14 @@ chromeos_laptop_prepare(const struct chromeos_laptop *src) property_entries_dup(info->properties); if (IS_ERR(info->properties)) { error = PTR_ERR(info->properties); - goto err_destroy_cros_peripherals; + goto err_out; } } } - return cros_laptop; + return 0; -err_destroy_cros_peripherals: +err_out: while (--i >= 0) { i2c_dev = &cros_laptop->i2c_peripherals[i]; info = &i2c_dev->board_info; @@ -609,13 +754,74 @@ err_destroy_cros_peripherals: property_entries_free(info->properties); } kfree(cros_laptop->i2c_peripherals); -err_free_cros_laptop: - kfree(cros_laptop); - return ERR_PTR(error); + return error; +} + +static int __init +chromeos_laptop_prepare_acpi_peripherals(struct chromeos_laptop *cros_laptop, + const struct chromeos_laptop *src) +{ + struct acpi_peripheral *acpi_peripherals; + struct acpi_peripheral *acpi_dev; + const struct acpi_peripheral *src_dev; + int n_peripherals = 0; + int i; + int error; + + for (i = 0; i < src->num_acpi_peripherals; i++) { + if (acpi_dev_present(src->acpi_peripherals[i].hid, NULL, -1)) + n_peripherals++; + } + + if (!n_peripherals) + return 0; + + acpi_peripherals = kcalloc(n_peripherals, + sizeof(*src->acpi_peripherals), + GFP_KERNEL); + if (!acpi_peripherals) + return -ENOMEM; + + acpi_dev = acpi_peripherals; + for (i = 0; i < src->num_acpi_peripherals; i++) { + src_dev = &src->acpi_peripherals[i]; + if (!acpi_dev_present(src_dev->hid, NULL, -1)) + continue; + + *acpi_dev = *src_dev; + + /* We need to deep-copy properties */ + if (src_dev->properties) { + acpi_dev->properties = + property_entries_dup(src_dev->properties); + if (IS_ERR(acpi_dev->properties)) { + error = PTR_ERR(acpi_dev->properties); + goto err_out; + } + } + + acpi_dev++; + } + + cros_laptop->acpi_peripherals = acpi_peripherals; + cros_laptop->num_acpi_peripherals = n_peripherals; + + return 0; + +err_out: + while (--i >= 0) { + acpi_dev = &acpi_peripherals[i]; + if (acpi_dev->properties) + property_entries_free(acpi_dev->properties); + } + + kfree(acpi_peripherals); + return error; } static void chromeos_laptop_destroy(const struct chromeos_laptop *cros_laptop) { + const struct acpi_peripheral *acpi_dev; struct i2c_peripheral *i2c_dev; struct i2c_board_info *info; int i; @@ -631,10 +837,41 @@ static void chromeos_laptop_destroy(const struct chromeos_laptop *cros_laptop) property_entries_free(info->properties); } + for (i = 0; i < cros_laptop->num_acpi_peripherals; i++) { + acpi_dev = &cros_laptop->acpi_peripherals[i]; + + if (acpi_dev->properties) + property_entries_free(acpi_dev->properties); + } + kfree(cros_laptop->i2c_peripherals); + kfree(cros_laptop->acpi_peripherals); kfree(cros_laptop); } +static struct chromeos_laptop * __init +chromeos_laptop_prepare(const struct chromeos_laptop *src) +{ + struct chromeos_laptop *cros_laptop; + int error; + + cros_laptop = kzalloc(sizeof(*cros_laptop), GFP_KERNEL); + if (!cros_laptop) + return ERR_PTR(-ENOMEM); + + error = chromeos_laptop_prepare_i2c_peripherals(cros_laptop, src); + if (!error) + error = chromeos_laptop_prepare_acpi_peripherals(cros_laptop, + src); + + if (error) { + chromeos_laptop_destroy(cros_laptop); + return ERR_PTR(error); + } + + return cros_laptop; +} + static int __init chromeos_laptop_init(void) { const struct dmi_system_id *dmi_id; @@ -652,21 +889,33 @@ static int __init chromeos_laptop_init(void) if (IS_ERR(cros_laptop)) return PTR_ERR(cros_laptop); + if (!cros_laptop->num_i2c_peripherals && + !cros_laptop->num_acpi_peripherals) { + pr_debug("no relevant devices detected\n"); + error = -ENODEV; + goto err_destroy_cros_laptop; + } + error = bus_register_notifier(&i2c_bus_type, &chromeos_laptop_i2c_notifier); if (error) { - pr_err("failed to register i2c bus notifier: %d\n", error); - chromeos_laptop_destroy(cros_laptop); - return error; + pr_err("failed to register i2c bus notifier: %d\n", + error); + goto err_destroy_cros_laptop; } /* - * Scan adapters that have been registered before we installed - * the notifier to make sure we do not miss any devices. + * Scan adapters that have been registered and clients that have + * been created before we installed the notifier to make sure + * we do not miss any devices. */ - i2c_for_each_dev(NULL, chromeos_laptop_scan_adapter); + i2c_for_each_dev(NULL, chromeos_laptop_scan_peripherals); return 0; + +err_destroy_cros_laptop: + chromeos_laptop_destroy(cros_laptop); + return error; } static void __exit chromeos_laptop_exit(void) diff --git a/include/linux/gpio_keys.h b/include/linux/gpio_keys.h index 7160df54a6fe..3f84aeb81e48 100644 --- a/include/linux/gpio_keys.h +++ b/include/linux/gpio_keys.h @@ -2,6 +2,8 @@ #ifndef _GPIO_KEYS_H #define _GPIO_KEYS_H +#include <linux/types.h> + struct device; /** diff --git a/include/linux/input/mt.h b/include/linux/input/mt.h index d7188de4db96..3f4bf60b0bb5 100644 --- a/include/linux/input/mt.h +++ b/include/linux/input/mt.h @@ -100,7 +100,7 @@ static inline bool input_is_mt_axis(int axis) return axis == ABS_MT_SLOT || input_is_mt_value(axis); } -void input_mt_report_slot_state(struct input_dev *dev, +bool input_mt_report_slot_state(struct input_dev *dev, unsigned int tool_type, bool active); void input_mt_report_finger_count(struct input_dev *dev, int count); diff --git a/include/linux/joystick.h b/include/linux/joystick.h index cbf2aa9e93b9..5153f5b9294c 100644 --- a/include/linux/joystick.h +++ b/include/linux/joystick.h @@ -17,10 +17,6 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic */ #ifndef _LINUX_JOYSTICK_H #define _LINUX_JOYSTICK_H diff --git a/include/linux/rmi.h b/include/linux/rmi.h index 64125443f8a6..5ef5c7c412a7 100644 --- a/include/linux/rmi.h +++ b/include/linux/rmi.h @@ -354,6 +354,8 @@ struct rmi_driver_data { struct mutex irq_mutex; struct input_dev *input; + struct irq_domain *irqdomain; + u8 pdt_props; u8 num_rx_electrodes; diff --git a/include/uapi/linux/joystick.h b/include/uapi/linux/joystick.h index 64aabb84a66d..192bf2cf182d 100644 --- a/include/uapi/linux/joystick.h +++ b/include/uapi/linux/joystick.h @@ -18,10 +18,6 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic */ #ifndef _UAPI_LINUX_JOYSTICK_H #define _UAPI_LINUX_JOYSTICK_H diff --git a/include/xen/interface/io/displif.h b/include/xen/interface/io/displif.h index 596578d9be3e..fdc279dc4a88 100644 --- a/include/xen/interface/io/displif.h +++ b/include/xen/interface/io/displif.h @@ -189,6 +189,13 @@ * *----------------------------- Connector settings ---------------------------- * + * unique-id + * Values: <string> + * + * After device instance initialization each connector is assigned a + * unique ID, so it can be identified by the backend by this ID. + * This can be UUID or such. + * * resolution * Values: <width, uint32_t>x<height, uint32_t> * @@ -368,6 +375,7 @@ #define XENDISPL_FIELD_EVT_CHANNEL "evt-event-channel" #define XENDISPL_FIELD_RESOLUTION "resolution" #define XENDISPL_FIELD_BE_ALLOC "be-alloc" +#define XENDISPL_FIELD_UNIQUE_ID "unique-id" /* ****************************************************************************** diff --git a/include/xen/interface/io/kbdif.h b/include/xen/interface/io/kbdif.h index 2a9510ade701..b4439cf48220 100644 --- a/include/xen/interface/io/kbdif.h +++ b/include/xen/interface/io/kbdif.h @@ -51,6 +51,18 @@ * corresponding entries in XenStore and puts 1 as the value of the entry. * If a feature is not supported then 0 must be set or feature entry omitted. * + * feature-disable-keyboard + * Values: <uint> + * + * If there is no need to expose a virtual keyboard device by the + * frontend then this must be set to 1. + * + * feature-disable-pointer + * Values: <uint> + * + * If there is no need to expose a virtual pointer device by the + * frontend then this must be set to 1. + * * feature-abs-pointer * Values: <uint> * @@ -63,6 +75,22 @@ * Backends, which support reporting of multi-touch events * should set this to 1. * + * feature-raw-pointer + * Values: <uint> + * + * Backends, which support reporting raw (unscaled) absolute coordinates + * for pointer devices should set this to 1. Raw (unscaled) values have + * a range of [0, 0x7fff]. + * + *----------------------- Device Instance Parameters ------------------------ + * + * unique-id + * Values: <string> + * + * After device instance initialization it is assigned a unique ID, + * so every instance of the frontend can be identified by the backend + * by this ID. This can be UUID or such. + * *------------------------- Pointer Device Parameters ------------------------ * * width @@ -77,6 +105,25 @@ * Maximum Y coordinate (height) to be used by the frontend * while reporting input events, pixels, [0; UINT32_MAX]. * + *----------------------- Multi-touch Device Parameters ---------------------- + * + * multi-touch-num-contacts + * Values: <uint> + * + * Number of simultaneous touches reported. + * + * multi-touch-width + * Values: <uint> + * + * Width of the touch area to be used by the frontend + * while reporting input events, pixels, [0; UINT32_MAX]. + * + * multi-touch-height + * Values: <uint> + * + * Height of the touch area to be used by the frontend + * while reporting input events, pixels, [0; UINT32_MAX]. + * ***************************************************************************** * Frontend XenBus Nodes ***************************************************************************** @@ -98,6 +145,13 @@ * * Request backend to report multi-touch events. * + * request-raw-pointer + * Values: <uint> + * + * Request backend to report raw unscaled absolute pointer coordinates. + * This option is only valid if request-abs-pointer is also set. + * Raw unscaled coordinates have the range [0, 0x7fff] + * *----------------------- Request Transport Parameters ----------------------- * * event-channel @@ -117,25 +171,6 @@ * * OBSOLETE, not recommended for use. * PFN of the shared page. - * - *----------------------- Multi-touch Device Parameters ----------------------- - * - * multi-touch-num-contacts - * Values: <uint> - * - * Number of simultaneous touches reported. - * - * multi-touch-width - * Values: <uint> - * - * Width of the touch area to be used by the frontend - * while reporting input events, pixels, [0; UINT32_MAX]. - * - * multi-touch-height - * Values: <uint> - * - * Height of the touch area to be used by the frontend - * while reporting input events, pixels, [0; UINT32_MAX]. */ /* @@ -163,9 +198,13 @@ #define XENKBD_DRIVER_NAME "vkbd" +#define XENKBD_FIELD_FEAT_DSBL_KEYBRD "feature-disable-keyboard" +#define XENKBD_FIELD_FEAT_DSBL_POINTER "feature-disable-pointer" #define XENKBD_FIELD_FEAT_ABS_POINTER "feature-abs-pointer" +#define XENKBD_FIELD_FEAT_RAW_POINTER "feature-raw-pointer" #define XENKBD_FIELD_FEAT_MTOUCH "feature-multi-touch" #define XENKBD_FIELD_REQ_ABS_POINTER "request-abs-pointer" +#define XENKBD_FIELD_REQ_RAW_POINTER "request-raw-pointer" #define XENKBD_FIELD_REQ_MTOUCH "request-multi-touch" #define XENKBD_FIELD_RING_GREF "page-gref" #define XENKBD_FIELD_EVT_CHANNEL "event-channel" @@ -174,6 +213,7 @@ #define XENKBD_FIELD_MT_WIDTH "multi-touch-width" #define XENKBD_FIELD_MT_HEIGHT "multi-touch-height" #define XENKBD_FIELD_MT_NUM_CONTACTS "multi-touch-num-contacts" +#define XENKBD_FIELD_UNIQUE_ID "unique-id" /* OBSOLETE, not recommended for use */ #define XENKBD_FIELD_RING_REF "page-ref" diff --git a/include/xen/interface/io/sndif.h b/include/xen/interface/io/sndif.h index 78bb5d9f8d83..2aac8f73614c 100644 --- a/include/xen/interface/io/sndif.h +++ b/include/xen/interface/io/sndif.h @@ -278,13 +278,11 @@ * defined under the same device. * * unique-id - * Values: <uint32_t> + * Values: <string> * - * After stream initialization it is assigned a unique ID (within the front - * driver), so every stream of the frontend can be identified by the - * backend by this ID. This is not equal to stream-idx as the later is - * zero based within the device, but this index is contigous within the - * driver. + * After stream initialization it is assigned a unique ID, so every + * stream of the frontend can be identified by the backend by this ID. + * This can be UUID or such. * *-------------------- Stream Request Transport Parameters -------------------- * |