From 71daf89476144343df5db1686759a06459292a5f Mon Sep 17 00:00:00 2001 From: Stefan Sauer Date: Wed, 25 Feb 2015 17:11:04 -0800 Subject: Input: mma8450 - add parent device Add the parent device so that udev can show the full hierarchy. This avoids the device showing up under /devices/virtual/input instead of the i2c bus it is actually attached to. Signed-off-by: Stefan Sauer Signed-off-by: Dmitry Torokhov --- drivers/input/misc/mma8450.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/input') diff --git a/drivers/input/misc/mma8450.c b/drivers/input/misc/mma8450.c index 59d4dcddf6de..98228773a111 100644 --- a/drivers/input/misc/mma8450.c +++ b/drivers/input/misc/mma8450.c @@ -187,6 +187,7 @@ static int mma8450_probe(struct i2c_client *c, idev->private = m; idev->input->name = MMA8450_DRV_NAME; idev->input->id.bustype = BUS_I2C; + idev->input->dev.parent = &c->dev; idev->poll = mma8450_poll; idev->poll_interval = POLL_INTERVAL; idev->poll_interval_max = POLL_INTERVAL_MAX; -- cgit v1.2.3 From 17a28055d51922672701465f26360720aa6f97bd Mon Sep 17 00:00:00 2001 From: Dudley Du Date: Mon, 2 Mar 2015 09:37:59 -0800 Subject: Input: cyapa - fix unaligned functions redefinition error Use asm/unaligned.h instead of linux/unaligned/access_ok.h header file to fix compiling issues such as following while doing cross platform compiling: "include/linux/unaligned/access_ok.h:7:19: error: redefinition of 'get_unaligned_le16' ... include/linux/unaligned/le_struct.h:6:19: note: previous definition of 'get_unaligned_le16' was here". Reported-by: kbuild test robot Signed-off-by: Dudley Du Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/cyapa_gen3.c | 2 +- drivers/input/mouse/cyapa_gen5.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/cyapa_gen3.c b/drivers/input/mouse/cyapa_gen3.c index 77e9d70a986b..1e2291c378fe 100644 --- a/drivers/input/mouse/cyapa_gen3.c +++ b/drivers/input/mouse/cyapa_gen3.c @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include "cyapa.h" diff --git a/drivers/input/mouse/cyapa_gen5.c b/drivers/input/mouse/cyapa_gen5.c index ddf5393a1180..aa68edd46b76 100644 --- a/drivers/input/mouse/cyapa_gen5.c +++ b/drivers/input/mouse/cyapa_gen5.c @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include #include "cyapa.h" -- cgit v1.2.3 From 2523caab3ce98ddd8a3eaf76eadd941dae5fb57e Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 2 Mar 2015 09:39:24 -0800 Subject: Input: cyapa - remove superfluous type check in cyapa_gen5_read_idac_data() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drivers/input/mouse/cyapa_gen5.c: In function ‘cyapa_gen5_read_idac_data’: drivers/input/mouse/cyapa_gen5.c:1876: warning: ‘max_element_cnt’ may be used uninitialized in this function drivers/input/mouse/cyapa_gen5.c:1873: warning: ‘offset’ may be used uninitialized in this function If *data_size is non-zero, and idac_data_type contains an unknown type, max_element_cnt and offset will be uninitialized, and the loop will process non-existing data. However, this cannot happen (for now), as there's a test for unknown types at the top of cyapa_gen5_read_idac_data(). As no "if ... else if ..." is used in other places, remove the superfluous "if" to silence the compiler warning. Signed-off-by: Geert Uytterhoeven Acked-by: Dudley Du Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/cyapa_gen5.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/cyapa_gen5.c b/drivers/input/mouse/cyapa_gen5.c index aa68edd46b76..5b611dd71e79 100644 --- a/drivers/input/mouse/cyapa_gen5.c +++ b/drivers/input/mouse/cyapa_gen5.c @@ -1926,7 +1926,7 @@ static int cyapa_gen5_read_idac_data(struct cyapa *cyapa, electrodes_tx = cyapa->electrodes_x; max_element_cnt = ((cyapa->aligned_electrodes_rx + 7) & ~7u) * electrodes_tx; - } else if (idac_data_type == GEN5_RETRIEVE_SELF_CAP_PWC_DATA) { + } else { offset = 2; max_element_cnt = cyapa->electrodes_x + cyapa->electrodes_y; -- cgit v1.2.3 From 4a6155a465650e8a3c7ae4e23b580ad9b84908aa Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 25 Feb 2015 17:26:58 -0800 Subject: Input: sun4i-ts - add thermal driver dependency The sun4i-ts driver has had a dependency on the thermal code with the addition of the thermal zone sensor support, but this is not currently enforced in Kconfig, so with TOUCHSCREEN_SUN4I=y, THERMAL=m and THERMAL_OF=y we get drivers/built-in.o: In function `sun4i_ts_remove': :(.text+0x2376f4): undefined reference to `thermal_zone_of_sensor_unregister' drivers/built-in.o: In function `sun4i_ts_probe': :(.text+0x237a94): undefined reference to `thermal_zone_of_sensor_register' :(.text+0x237c00): undefined reference to `thermal_zone_of_sensor_unregister' We need the dependency on THERMAL in order to ensure that this driver becomes a loadable module if the thermal support itself is modular, while the dependency on THERMAL_OF is a runtime dependency and the driver will still build if it is missing. It is entirely possible to build sun4i-ts without THERMAL_OF just to use the hwmon sensors and/or touchscreen. Fixes: 223697107949 ("Input: sun4i-ts - add thermal zone sensor support") Signed-off-by: Arnd Bergmann [wens@csie.org: Fix description and Kconfig dependencies] Signed-off-by: Chen-Yu Tsai Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 58917525126e..6261fd6d7c3c 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -943,6 +943,7 @@ config TOUCHSCREEN_SUN4I tristate "Allwinner sun4i resistive touchscreen controller support" depends on ARCH_SUNXI || COMPILE_TEST depends on HWMON + depends on THERMAL || !THERMAL_OF help This selects support for the resistive touchscreen controller found on Allwinner sunxi SoCs. -- cgit v1.2.3 From 0c7e67a928ac5328d30a0638adec771511dc7074 Mon Sep 17 00:00:00 2001 From: Scott Branden Date: Sat, 28 Feb 2015 13:33:30 -0800 Subject: Input: add driver for Broadcom keypad controller Broadcom Keypad controller is used to interface a SoC with a matrix-type keypad device. The keypad controller supports multiple row and column lines. A key can be placed at each intersection of a unique row and a unique column. The keypad controller can sense a key-press and key-release and report the event using an interrupt to the CPU. Reviewed-by: Ray Jui Signed-off-by: Scott Branden Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/Kconfig | 11 + drivers/input/keyboard/Makefile | 1 + drivers/input/keyboard/bcm-keypad.c | 458 ++++++++++++++++++++++++++++++++++++ 3 files changed, 470 insertions(+) create mode 100644 drivers/input/keyboard/bcm-keypad.c (limited to 'drivers/input') diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index a89ba7cb96f1..17de1dcac637 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -686,4 +686,15 @@ config KEYBOARD_CAP11XX To compile this driver as a module, choose M here: the module will be called cap11xx. +config KEYBOARD_BCM + tristate "Broadcom keypad driver" + depends on OF && HAVE_CLK + select INPUT_MATRIXKMAP + default ARCH_BCM_CYGNUS + help + Say Y here if you want to use Broadcom keypad. + + To compile this driver as a module, choose M here: the + module will be called bcm-keypad. + endif diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 470767884bd8..a648f6c6bbfa 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_KEYBOARD_ADP5589) += adp5589-keys.o obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o obj-$(CONFIG_KEYBOARD_ATARI) += atakbd.o obj-$(CONFIG_KEYBOARD_ATKBD) += atkbd.o +obj-$(CONFIG_KEYBOARD_BCM) += bcm-keypad.o obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o obj-$(CONFIG_KEYBOARD_CAP11XX) += cap11xx.o obj-$(CONFIG_KEYBOARD_CLPS711X) += clps711x-keypad.o diff --git a/drivers/input/keyboard/bcm-keypad.c b/drivers/input/keyboard/bcm-keypad.c new file mode 100644 index 000000000000..86a8b723ae15 --- /dev/null +++ b/drivers/input/keyboard/bcm-keypad.c @@ -0,0 +1,458 @@ +/* + * Copyright (C) 2014 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEFAULT_CLK_HZ 31250 +#define MAX_ROWS 8 +#define MAX_COLS 8 + +/* Register/field definitions */ +#define KPCR_OFFSET 0x00000080 +#define KPCR_MODE 0x00000002 +#define KPCR_MODE_SHIFT 1 +#define KPCR_MODE_MASK 1 +#define KPCR_ENABLE 0x00000001 +#define KPCR_STATUSFILTERENABLE 0x00008000 +#define KPCR_STATUSFILTERTYPE_SHIFT 12 +#define KPCR_COLFILTERENABLE 0x00000800 +#define KPCR_COLFILTERTYPE_SHIFT 8 +#define KPCR_ROWWIDTH_SHIFT 20 +#define KPCR_COLUMNWIDTH_SHIFT 16 + +#define KPIOR_OFFSET 0x00000084 +#define KPIOR_ROWOCONTRL_SHIFT 24 +#define KPIOR_ROWOCONTRL_MASK 0xFF000000 +#define KPIOR_COLUMNOCONTRL_SHIFT 16 +#define KPIOR_COLUMNOCONTRL_MASK 0x00FF0000 +#define KPIOR_COLUMN_IO_DATA_SHIFT 0 + +#define KPEMR0_OFFSET 0x00000090 +#define KPEMR1_OFFSET 0x00000094 +#define KPEMR2_OFFSET 0x00000098 +#define KPEMR3_OFFSET 0x0000009C +#define KPEMR_EDGETYPE_BOTH 3 + +#define KPSSR0_OFFSET 0x000000A0 +#define KPSSR1_OFFSET 0x000000A4 +#define KPSSRN_OFFSET(reg_n) (KPSSR0_OFFSET + 4 * (reg_n)) +#define KPIMR0_OFFSET 0x000000B0 +#define KPIMR1_OFFSET 0x000000B4 +#define KPICR0_OFFSET 0x000000B8 +#define KPICR1_OFFSET 0x000000BC +#define KPICRN_OFFSET(reg_n) (KPICR0_OFFSET + 4 * (reg_n)) +#define KPISR0_OFFSET 0x000000C0 +#define KPISR1_OFFSET 0x000000C4 + +#define KPCR_STATUSFILTERTYPE_MAX 7 +#define KPCR_COLFILTERTYPE_MAX 7 + +/* Macros to determine the row/column from a bit that is set in SSR0/1. */ +#define BIT_TO_ROW_SSRN(bit_nr, reg_n) (((bit_nr) >> 3) + 4 * (reg_n)) +#define BIT_TO_COL(bit_nr) ((bit_nr) % 8) + +/* Structure representing various run-time entities */ +struct bcm_kp { + void __iomem *base; + int irq; + struct clk *clk; + struct input_dev *input_dev; + unsigned long last_state[2]; + unsigned int n_rows; + unsigned int n_cols; + u32 kpcr; + u32 kpior; + u32 kpemr; + u32 imr0_val; + u32 imr1_val; +}; + +/* + * Returns the keycode from the input device keymap given the row and + * column. + */ +static int bcm_kp_get_keycode(struct bcm_kp *kp, int row, int col) +{ + unsigned int row_shift = get_count_order(kp->n_cols); + unsigned short *keymap = kp->input_dev->keycode; + + return keymap[MATRIX_SCAN_CODE(row, col, row_shift)]; +} + +static void bcm_kp_report_keys(struct bcm_kp *kp, int reg_num, int pull_mode) +{ + unsigned long state, change; + int bit_nr; + int key_press; + int row, col; + unsigned int keycode; + + /* Clear interrupts */ + writel(0xFFFFFFFF, kp->base + KPICRN_OFFSET(reg_num)); + + state = readl(kp->base + KPSSRN_OFFSET(reg_num)); + change = kp->last_state[reg_num] ^ state; + kp->last_state[reg_num] = state; + + for_each_set_bit(bit_nr, &change, BITS_PER_LONG) { + key_press = state & BIT(bit_nr); + /* The meaning of SSR register depends on pull mode. */ + key_press = pull_mode ? !key_press : key_press; + row = BIT_TO_ROW_SSRN(bit_nr, reg_num); + col = BIT_TO_COL(bit_nr); + keycode = bcm_kp_get_keycode(kp, row, col); + input_report_key(kp->input_dev, keycode, key_press); + } +} + +static irqreturn_t bcm_kp_isr_thread(int irq, void *dev_id) +{ + struct bcm_kp *kp = dev_id; + int pull_mode = (kp->kpcr >> KPCR_MODE_SHIFT) & KPCR_MODE_MASK; + int reg_num; + + for (reg_num = 0; reg_num <= 1; reg_num++) + bcm_kp_report_keys(kp, reg_num, pull_mode); + + input_sync(kp->input_dev); + + return IRQ_HANDLED; +} + +static int bcm_kp_start(struct bcm_kp *kp) +{ + int error; + + if (kp->clk) { + error = clk_prepare_enable(kp->clk); + if (error) + return error; + } + + writel(kp->kpior, kp->base + KPIOR_OFFSET); + + writel(kp->imr0_val, kp->base + KPIMR0_OFFSET); + writel(kp->imr1_val, kp->base + KPIMR1_OFFSET); + + writel(kp->kpemr, kp->base + KPEMR0_OFFSET); + writel(kp->kpemr, kp->base + KPEMR1_OFFSET); + writel(kp->kpemr, kp->base + KPEMR2_OFFSET); + writel(kp->kpemr, kp->base + KPEMR3_OFFSET); + + writel(0xFFFFFFFF, kp->base + KPICR0_OFFSET); + writel(0xFFFFFFFF, kp->base + KPICR1_OFFSET); + + kp->last_state[0] = readl(kp->base + KPSSR0_OFFSET); + kp->last_state[0] = readl(kp->base + KPSSR1_OFFSET); + + writel(kp->kpcr | KPCR_ENABLE, kp->base + KPCR_OFFSET); + + return 0; +} + +static void bcm_kp_stop(const struct bcm_kp *kp) +{ + u32 val; + + val = readl(kp->base + KPCR_OFFSET); + val &= ~KPCR_ENABLE; + writel(0, kp->base + KPCR_OFFSET); + writel(0, kp->base + KPIMR0_OFFSET); + writel(0, kp->base + KPIMR1_OFFSET); + writel(0xFFFFFFFF, kp->base + KPICR0_OFFSET); + writel(0xFFFFFFFF, kp->base + KPICR1_OFFSET); + + if (kp->clk) + clk_disable_unprepare(kp->clk); +} + +static int bcm_kp_open(struct input_dev *dev) +{ + struct bcm_kp *kp = input_get_drvdata(dev); + + return bcm_kp_start(kp); +} + +static void bcm_kp_close(struct input_dev *dev) +{ + struct bcm_kp *kp = input_get_drvdata(dev); + + bcm_kp_stop(kp); +} + +static int bcm_kp_matrix_key_parse_dt(struct bcm_kp *kp) +{ + struct device *dev = kp->input_dev->dev.parent; + struct device_node *np = dev->of_node; + int error; + unsigned int dt_val; + unsigned int i; + unsigned int num_rows, col_mask, rows_set; + + /* Initialize the KPCR Keypad Configuration Register */ + kp->kpcr = KPCR_STATUSFILTERENABLE | KPCR_COLFILTERENABLE; + + error = matrix_keypad_parse_of_params(dev, &kp->n_rows, &kp->n_cols); + if (error) { + dev_err(dev, "failed to parse kp params\n"); + return error; + } + + /* Set row width for the ASIC block. */ + kp->kpcr |= (kp->n_rows - 1) << KPCR_ROWWIDTH_SHIFT; + + /* Set column width for the ASIC block. */ + kp->kpcr |= (kp->n_cols - 1) << KPCR_COLUMNWIDTH_SHIFT; + + /* Configure the IMR registers */ + + /* + * IMR registers contain interrupt enable bits for 8x8 matrix + * IMR0 register format: + * IMR1 register format: + */ + col_mask = (1 << (kp->n_cols)) - 1; + num_rows = kp->n_rows; + + /* Set column bits in rows 0 to 3 in IMR0 */ + kp->imr0_val = col_mask; + + rows_set = 1; + while (--num_rows && rows_set++ < 4) + kp->imr0_val |= kp->imr0_val << MAX_COLS; + + /* Set column bits in rows 4 to 7 in IMR1 */ + kp->imr1_val = 0; + if (num_rows) { + kp->imr1_val = col_mask; + while (--num_rows) + kp->imr1_val |= kp->imr1_val << MAX_COLS; + } + + /* Initialize the KPEMR Keypress Edge Mode Registers */ + /* Trigger on both edges */ + kp->kpemr = 0; + for (i = 0; i <= 30; i += 2) + kp->kpemr |= (KPEMR_EDGETYPE_BOTH << i); + + /* + * Obtain the Status filter debounce value and verify against the + * possible values specified in the DT binding. + */ + of_property_read_u32(np, "status-debounce-filter-period", &dt_val); + + if (dt_val > KPCR_STATUSFILTERTYPE_MAX) { + dev_err(dev, "Invalid Status filter debounce value %d\n", + dt_val); + return -EINVAL; + } + + kp->kpcr |= dt_val << KPCR_STATUSFILTERTYPE_SHIFT; + + /* + * Obtain the Column filter debounce value and verify against the + * possible values specified in the DT binding. + */ + of_property_read_u32(np, "col-debounce-filter-period", &dt_val); + + if (dt_val > KPCR_COLFILTERTYPE_MAX) { + dev_err(dev, "Invalid Column filter debounce value %d\n", + dt_val); + return -EINVAL; + } + + kp->kpcr |= dt_val << KPCR_COLFILTERTYPE_SHIFT; + + /* + * Determine between the row and column, + * which should be configured as output. + */ + if (of_property_read_bool(np, "row-output-enabled")) { + /* + * Set RowOContrl or ColumnOContrl in KPIOR + * to the number of pins to drive as outputs + */ + kp->kpior = ((1 << kp->n_rows) - 1) << + KPIOR_ROWOCONTRL_SHIFT; + } else { + kp->kpior = ((1 << kp->n_cols) - 1) << + KPIOR_COLUMNOCONTRL_SHIFT; + } + + /* + * Determine if the scan pull up needs to be enabled + */ + if (of_property_read_bool(np, "pull-up-enabled")) + kp->kpcr |= KPCR_MODE; + + dev_dbg(dev, "n_rows=%d n_col=%d kpcr=%x kpior=%x kpemr=%x\n", + kp->n_rows, kp->n_cols, + kp->kpcr, kp->kpior, kp->kpemr); + + return 0; +} + + +static int bcm_kp_probe(struct platform_device *pdev) +{ + struct bcm_kp *kp; + struct input_dev *input_dev; + struct resource *res; + int error; + + kp = devm_kzalloc(&pdev->dev, sizeof(*kp), GFP_KERNEL); + if (!kp) + return -ENOMEM; + + input_dev = devm_input_allocate_device(&pdev->dev); + if (!input_dev) { + dev_err(&pdev->dev, "failed to allocate the input device\n"); + return -ENOMEM; + } + + __set_bit(EV_KEY, input_dev->evbit); + + /* Enable auto repeat feature of Linux input subsystem */ + if (of_property_read_bool(pdev->dev.of_node, "autorepeat")) + __set_bit(EV_REP, input_dev->evbit); + + input_dev->name = pdev->name; + input_dev->phys = "keypad/input0"; + input_dev->dev.parent = &pdev->dev; + input_dev->open = bcm_kp_open; + input_dev->close = bcm_kp_close; + + input_dev->id.bustype = BUS_HOST; + input_dev->id.vendor = 0x0001; + input_dev->id.product = 0x0001; + input_dev->id.version = 0x0100; + + input_set_drvdata(input_dev, kp); + + kp->input_dev = input_dev; + + platform_set_drvdata(pdev, kp); + + error = bcm_kp_matrix_key_parse_dt(kp); + if (error) + return error; + + error = matrix_keypad_build_keymap(NULL, NULL, + kp->n_rows, kp->n_cols, + NULL, input_dev); + if (error) { + dev_err(&pdev->dev, "failed to build keymap\n"); + return error; + } + + /* Get the KEYPAD base address */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "Missing keypad base address resource\n"); + return -ENODEV; + } + + kp->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(kp->base)) + return PTR_ERR(kp->base); + + /* Enable clock */ + kp->clk = devm_clk_get(&pdev->dev, "peri_clk"); + if (IS_ERR(kp->clk)) { + error = PTR_ERR(kp->clk); + if (error != -ENOENT) { + if (error != -EPROBE_DEFER) + dev_err(&pdev->dev, "Failed to get clock\n"); + return error; + } + dev_dbg(&pdev->dev, + "No clock specified. Assuming it's enabled\n"); + kp->clk = NULL; + } else { + unsigned int desired_rate; + long actual_rate; + + error = of_property_read_u32(pdev->dev.of_node, + "clock-frequency", &desired_rate); + if (error < 0) + desired_rate = DEFAULT_CLK_HZ; + + actual_rate = clk_round_rate(kp->clk, desired_rate); + if (actual_rate <= 0) + return -EINVAL; + + error = clk_set_rate(kp->clk, actual_rate); + if (error) + return error; + + error = clk_prepare_enable(kp->clk); + if (error) + return error; + } + + /* Put the kp into a known sane state */ + bcm_kp_stop(kp); + + kp->irq = platform_get_irq(pdev, 0); + if (kp->irq < 0) { + dev_err(&pdev->dev, "no IRQ specified\n"); + return -EINVAL; + } + + error = devm_request_threaded_irq(&pdev->dev, kp->irq, + NULL, bcm_kp_isr_thread, + IRQF_ONESHOT, pdev->name, kp); + if (error) { + dev_err(&pdev->dev, "failed to request IRQ\n"); + return error; + } + + error = input_register_device(input_dev); + if (error) { + dev_err(&pdev->dev, "failed to register input device\n"); + return error; + } + + return 0; +} + +static const struct of_device_id bcm_kp_of_match[] = { + { .compatible = "brcm,bcm-keypad" }, + { }, +}; +MODULE_DEVICE_TABLE(of, bcm_kp_of_match); + +static struct platform_driver bcm_kp_device_driver = { + .probe = bcm_kp_probe, + .driver = { + .name = "bcm-keypad", + .of_match_table = of_match_ptr(bcm_kp_of_match), + } +}; + +module_platform_driver(bcm_kp_device_driver); + +MODULE_AUTHOR("Broadcom Corporation"); +MODULE_DESCRIPTION("BCM Keypad Driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 1feb57a245a4910b03202a814ffc51a900bd4aca Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Wed, 21 Jan 2015 22:33:46 +0100 Subject: gpio: add parameter to allow the use named gpios The gpio binding document says that new code should always use named gpios. Patch 40b73183 added support to parse a list of gpios from child nodes, but does not make it possible to use named gpios. This patch adds the con_id property and implements it is done in gpiolib.c, where the old-style of using unnamed gpios still works. Signed-off-by: Olliver Schinagl Acked-by: Bryan Wu Acked-by: Dmitry Torokhov Reviewed-by: Alexandre Courbot Signed-off-by: Linus Walleij --- drivers/input/keyboard/gpio_keys_polled.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/gpio_keys_polled.c b/drivers/input/keyboard/gpio_keys_polled.c index 90df4df58b07..097d7216d98e 100644 --- a/drivers/input/keyboard/gpio_keys_polled.c +++ b/drivers/input/keyboard/gpio_keys_polled.c @@ -125,7 +125,7 @@ static struct gpio_keys_platform_data *gpio_keys_polled_get_devtree_pdata(struct device_for_each_child_node(dev, child) { struct gpio_desc *desc; - desc = devm_get_gpiod_from_child(dev, child); + desc = devm_get_gpiod_from_child(dev, NULL, child); if (IS_ERR(desc)) { error = PTR_ERR(desc); if (error != -EPROBE_DEFER) -- cgit v1.2.3 From 93050db2065726c7fd0db1b9a53311a74eee94c3 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 27 Feb 2015 15:49:51 -0800 Subject: Input: ALPS - fix memory leak when detection fails MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes memory leak introduced by commit a09221e83e13e09a33109b9b037484eade901cea Acked-by: Pali Rohár Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/alps.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index d28726a0ef85..1bd15ebc01f2 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -2605,8 +2605,10 @@ int alps_detect(struct psmouse *psmouse, bool set_properties) return -ENOMEM; error = alps_identify(psmouse, priv); - if (error) + if (error) { + kfree(priv); return error; + } if (set_properties) { psmouse->vendor = "ALPS"; -- cgit v1.2.3 From 20f02d66f042f2b6a929519fd9ee62f77013ccaa Mon Sep 17 00:00:00 2001 From: Valentin Rothberg Date: Wed, 4 Mar 2015 15:10:45 -0800 Subject: Input: tc3589x-keypad - set IRQF_ONESHOT flag to ensure IRQ request Since commit 1c6c69525b40eb76de8adf039409722015927dc3 ("genirq: Reject bogus threaded irq requests") threaded IRQs without a primary handler need to be requested with IRQF_ONESHOT, otherwise the request will fail. Currently, plat->irqtype is only set to IRQF_TRIGGER_FALLING. This patch sets the ONESHOT flag directly in request_threaded_irq() to enforce the flag without being affected by future changes to plat->irqtype. Generated by: scripts/coccinelle/misc/irqf_oneshot.cocci Signed-off-by: Valentin Rothberg Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/tc3589x-keypad.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/tc3589x-keypad.c b/drivers/input/keyboard/tc3589x-keypad.c index 8ff612d160b0..563932500ff1 100644 --- a/drivers/input/keyboard/tc3589x-keypad.c +++ b/drivers/input/keyboard/tc3589x-keypad.c @@ -411,9 +411,9 @@ static int tc3589x_keypad_probe(struct platform_device *pdev) input_set_drvdata(input, keypad); - error = request_threaded_irq(irq, NULL, - tc3589x_keypad_irq, plat->irqtype, - "tc3589x-keypad", keypad); + error = request_threaded_irq(irq, NULL, tc3589x_keypad_irq, + plat->irqtype | IRQF_ONESHOT, + "tc3589x-keypad", keypad); if (error < 0) { dev_err(&pdev->dev, "Could not allocate irq %d,error %d\n", -- cgit v1.2.3 From e5abff1fe2e400bdabd14feb4e69e9ad661ba71a Mon Sep 17 00:00:00 2001 From: Jaewon Kim Date: Wed, 4 Mar 2015 14:43:40 -0800 Subject: Input: add haptic support for max77843 This patch adds support for haptic on max77843 MFD (Multi Function Device) with PMIC, MUIC, LED, CHARGER. This driver supports external pwm and LRA (Linear Resonant Actuator) motor. Signed-off-by: Jaewon Kim [Jim Davis : should depend on REGULATOR not PWM] Signed-off-by: Dmitry Torokhov --- drivers/input/misc/Kconfig | 12 ++ drivers/input/misc/Makefile | 1 + drivers/input/misc/max77843-haptic.c | 358 +++++++++++++++++++++++++++++++++++ 3 files changed, 371 insertions(+) create mode 100644 drivers/input/misc/max77843-haptic.c (limited to 'drivers/input') diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 6deb8dae3205..ef542f7ad944 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -165,6 +165,18 @@ config INPUT_MAX77693_HAPTIC To compile this driver as module, choose M here: the module will be called max77693-haptic. +config INPUT_MAX77843_HAPTIC + tristate "MAXIM MAX77843 haptic controller support" + depends on MFD_MAX77843 && REGULATOR + select INPUT_FF_MEMLESS + help + This option enables support for the haptic controller on + MAXIM MAX77843 chip. The driver supports ff-memless interface + from input framework. + + To compile this driver as module, choose M here: the + module will be called max77843-haptic. + config INPUT_MAX8925_ONKEY tristate "MAX8925 ONKEY support" depends on MFD_MAX8925 diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 403a1a54a76c..75b58841d5a7 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o obj-$(CONFIG_INPUT_KXTJ9) += kxtj9.o obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o obj-$(CONFIG_INPUT_MAX77693_HAPTIC) += max77693-haptic.o +obj-$(CONFIG_INPUT_MAX77843_HAPTIC) += max77843-haptic.o obj-$(CONFIG_INPUT_MAX8925_ONKEY) += max8925_onkey.o obj-$(CONFIG_INPUT_MAX8997_HAPTIC) += max8997_haptic.o obj-$(CONFIG_INPUT_MC13783_PWRBUTTON) += mc13783-pwrbutton.o diff --git a/drivers/input/misc/max77843-haptic.c b/drivers/input/misc/max77843-haptic.c new file mode 100644 index 000000000000..dccbb465a055 --- /dev/null +++ b/drivers/input/misc/max77843-haptic.c @@ -0,0 +1,358 @@ +/* + * MAXIM MAX77693 Haptic device driver + * + * Copyright (C) 2015 Samsung Electronics + * Author: Jaewon Kim + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_MAGNITUDE_SHIFT 16 + +enum max77843_haptic_motor_type { + MAX77843_HAPTIC_ERM = 0, + MAX77843_HAPTIC_LRA, +}; + +enum max77843_haptic_pwm_divisor { + MAX77843_HAPTIC_PWM_DIVISOR_32 = 0, + MAX77843_HAPTIC_PWM_DIVISOR_64, + MAX77843_HAPTIC_PWM_DIVISOR_128, + MAX77843_HAPTIC_PWM_DIVISOR_256, +}; + +struct max77843_haptic { + struct regmap *regmap_haptic; + struct device *dev; + struct input_dev *input_dev; + struct pwm_device *pwm_dev; + struct regulator *motor_reg; + struct work_struct work; + struct mutex mutex; + + unsigned int magnitude; + unsigned int pwm_duty; + + bool active; + bool suspended; + + enum max77843_haptic_motor_type type; + enum max77843_haptic_pwm_divisor pwm_divisor; +}; + +static int max77843_haptic_set_duty_cycle(struct max77843_haptic *haptic) +{ + int delta = (haptic->pwm_dev->period + haptic->pwm_duty) / 2; + int error; + + error = pwm_config(haptic->pwm_dev, delta, haptic->pwm_dev->period); + if (error) { + dev_err(haptic->dev, "failed to configure pwm: %d\n", error); + return error; + } + + return 0; +} + +static int max77843_haptic_bias(struct max77843_haptic *haptic, bool on) +{ + int error; + + error = regmap_update_bits(haptic->regmap_haptic, + MAX77843_SYS_REG_MAINCTRL1, + MAX77843_MAINCTRL1_BIASEN_MASK, + on << MAINCTRL1_BIASEN_SHIFT); + if (error) { + dev_err(haptic->dev, "failed to %s bias: %d\n", + on ? "enable" : "disable", error); + return error; + } + + return 0; +} + +static int max77843_haptic_config(struct max77843_haptic *haptic, bool enable) +{ + unsigned int value; + int error; + + value = (haptic->type << MCONFIG_MODE_SHIFT) | + (enable << MCONFIG_MEN_SHIFT) | + (haptic->pwm_divisor << MCONFIG_PDIV_SHIFT); + + error = regmap_write(haptic->regmap_haptic, + MAX77843_HAP_REG_MCONFIG, value); + if (error) { + dev_err(haptic->dev, + "failed to update haptic config: %d\n", error); + return error; + } + + return 0; +} + +static int max77843_haptic_enable(struct max77843_haptic *haptic) +{ + int error; + + if (haptic->active) + return 0; + + error = pwm_enable(haptic->pwm_dev); + if (error) { + dev_err(haptic->dev, + "failed to enable pwm device: %d\n", error); + return error; + } + + error = max77843_haptic_config(haptic, true); + if (error) + goto err_config; + + haptic->active = true; + + return 0; + +err_config: + pwm_disable(haptic->pwm_dev); + + return error; +} + +static int max77843_haptic_disable(struct max77843_haptic *haptic) +{ + int error; + + if (!haptic->active) + return 0; + + error = max77843_haptic_config(haptic, false); + if (error) + return error; + + pwm_disable(haptic->pwm_dev); + + haptic->active = false; + + return 0; +} + +static void max77843_haptic_play_work(struct work_struct *work) +{ + struct max77843_haptic *haptic = + container_of(work, struct max77843_haptic, work); + int error; + + mutex_lock(&haptic->mutex); + + if (haptic->suspended) + goto out_unlock; + + if (haptic->magnitude) { + error = max77843_haptic_set_duty_cycle(haptic); + if (error) { + dev_err(haptic->dev, + "failed to set duty cycle: %d\n", error); + goto out_unlock; + } + + error = max77843_haptic_enable(haptic); + if (error) + dev_err(haptic->dev, + "cannot enable haptic: %d\n", error); + } else { + error = max77843_haptic_disable(haptic); + if (error) + dev_err(haptic->dev, + "cannot disable haptic: %d\n", error); + } + +out_unlock: + mutex_unlock(&haptic->mutex); +} + +static int max77843_haptic_play_effect(struct input_dev *dev, void *data, + struct ff_effect *effect) +{ + struct max77843_haptic *haptic = input_get_drvdata(dev); + u64 period_mag_multi; + + haptic->magnitude = effect->u.rumble.strong_magnitude; + if (!haptic->magnitude) + haptic->magnitude = effect->u.rumble.weak_magnitude; + + period_mag_multi = (u64)haptic->pwm_dev->period * haptic->magnitude; + haptic->pwm_duty = (unsigned int)(period_mag_multi >> + MAX_MAGNITUDE_SHIFT); + + schedule_work(&haptic->work); + + return 0; +} + +static int max77843_haptic_open(struct input_dev *dev) +{ + struct max77843_haptic *haptic = input_get_drvdata(dev); + int error; + + error = max77843_haptic_bias(haptic, true); + if (error) + return error; + + error = regulator_enable(haptic->motor_reg); + if (error) { + dev_err(haptic->dev, + "failed to enable regulator: %d\n", error); + return error; + } + + return 0; +} + +static void max77843_haptic_close(struct input_dev *dev) +{ + struct max77843_haptic *haptic = input_get_drvdata(dev); + int error; + + cancel_work_sync(&haptic->work); + max77843_haptic_disable(haptic); + + error = regulator_disable(haptic->motor_reg); + if (error) + dev_err(haptic->dev, + "failed to disable regulator: %d\n", error); + + max77843_haptic_bias(haptic, false); +} + +static int max77843_haptic_probe(struct platform_device *pdev) +{ + struct max77843 *max77843 = dev_get_drvdata(pdev->dev.parent); + struct max77843_haptic *haptic; + int error; + + haptic = devm_kzalloc(&pdev->dev, sizeof(*haptic), GFP_KERNEL); + if (!haptic) + return -ENOMEM; + + haptic->regmap_haptic = max77843->regmap; + haptic->dev = &pdev->dev; + haptic->type = MAX77843_HAPTIC_LRA; + haptic->pwm_divisor = MAX77843_HAPTIC_PWM_DIVISOR_128; + + INIT_WORK(&haptic->work, max77843_haptic_play_work); + mutex_init(&haptic->mutex); + + haptic->pwm_dev = devm_pwm_get(&pdev->dev, NULL); + if (IS_ERR(haptic->pwm_dev)) { + dev_err(&pdev->dev, "failed to get pwm device\n"); + return PTR_ERR(haptic->pwm_dev); + } + + haptic->motor_reg = devm_regulator_get_exclusive(&pdev->dev, "haptic"); + if (IS_ERR(haptic->motor_reg)) { + dev_err(&pdev->dev, "failed to get regulator\n"); + return PTR_ERR(haptic->motor_reg); + } + + haptic->input_dev = devm_input_allocate_device(&pdev->dev); + if (!haptic->input_dev) { + dev_err(&pdev->dev, "failed to allocate input device\n"); + return -ENOMEM; + } + + haptic->input_dev->name = "max77843-haptic"; + haptic->input_dev->id.version = 1; + haptic->input_dev->dev.parent = &pdev->dev; + haptic->input_dev->open = max77843_haptic_open; + haptic->input_dev->close = max77843_haptic_close; + input_set_drvdata(haptic->input_dev, haptic); + input_set_capability(haptic->input_dev, EV_FF, FF_RUMBLE); + + error = input_ff_create_memless(haptic->input_dev, NULL, + max77843_haptic_play_effect); + if (error) { + dev_err(&pdev->dev, "failed to create force-feedback\n"); + return error; + } + + error = input_register_device(haptic->input_dev); + if (error) { + dev_err(&pdev->dev, "failed to register input device\n"); + return error; + } + + platform_set_drvdata(pdev, haptic); + + return 0; +} + +static int __maybe_unused max77843_haptic_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct max77843_haptic *haptic = platform_get_drvdata(pdev); + int error; + + error = mutex_lock_interruptible(&haptic->mutex); + if (error) + return error; + + max77843_haptic_disable(haptic); + + haptic->suspended = true; + + mutex_unlock(&haptic->mutex); + + return 0; +} + +static int __maybe_unused max77843_haptic_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct max77843_haptic *haptic = platform_get_drvdata(pdev); + unsigned int magnitude; + + mutex_lock(&haptic->mutex); + + haptic->suspended = false; + + magnitude = ACCESS_ONCE(haptic->magnitude); + if (magnitude) + max77843_haptic_enable(haptic); + + mutex_unlock(&haptic->mutex); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(max77843_haptic_pm_ops, + max77843_haptic_suspend, max77843_haptic_resume); + +static struct platform_driver max77843_haptic_driver = { + .driver = { + .name = "max77843-haptic", + .pm = &max77843_haptic_pm_ops, + }, + .probe = max77843_haptic_probe, +}; +module_platform_driver(max77843_haptic_driver); + +MODULE_AUTHOR("Jaewon Kim "); +MODULE_DESCRIPTION("MAXIM MAX77843 Haptic driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From d7535ffa427b8976b2d41f8d9f7fb9f1c97d786c Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 4 Mar 2015 14:47:58 -0800 Subject: Input: driver for microcontroller keys on the iPaq h3xxx This adds a key input driver for the keys found on the h3xxx iPAQ series. Based on a driver from handhelds.org 2.6.21 kernel, written by Alessandro GARDICH. Signed-off-by: Alessandro GARDICH Signed-off-by: Dmitry Artamonow Signed-off-by: Linus Walleij Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/Kconfig | 10 ++ drivers/input/keyboard/Makefile | 1 + drivers/input/keyboard/ipaq-micro-keys.c | 168 +++++++++++++++++++++++++++++++ 3 files changed, 179 insertions(+) create mode 100644 drivers/input/keyboard/ipaq-micro-keys.c (limited to 'drivers/input') diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 17de1dcac637..106fbac7f8c5 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -588,6 +588,16 @@ config KEYBOARD_DAVINCI To compile this driver as a module, choose M here: the module will be called davinci_keyscan. +config KEYBOARD_IPAQ_MICRO + tristate "Buttons on Micro SoC (iPaq h3100,h3600,h3700)" + depends on MFD_IPAQ_MICRO + help + Say Y to enable support for the buttons attached to + Micro peripheral controller on iPAQ h3100/h3600/h3700 + + To compile this driver as a module, choose M here: the + module will be called ipaq-micro-keys. + config KEYBOARD_OMAP tristate "TI OMAP keypad support" depends on ARCH_OMAP1 diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index a648f6c6bbfa..df28d5553c05 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_KEYBOARD_TCA6416) += tca6416-keypad.o obj-$(CONFIG_KEYBOARD_TCA8418) += tca8418_keypad.o obj-$(CONFIG_KEYBOARD_HIL) += hil_kbd.o obj-$(CONFIG_KEYBOARD_HIL_OLD) += hilkbd.o +obj-$(CONFIG_KEYBOARD_IPAQ_MICRO) += ipaq-micro-keys.o obj-$(CONFIG_KEYBOARD_IMX) += imx_keypad.o obj-$(CONFIG_KEYBOARD_HP6XX) += jornada680_kbd.o obj-$(CONFIG_KEYBOARD_HP7XX) += jornada720_kbd.o diff --git a/drivers/input/keyboard/ipaq-micro-keys.c b/drivers/input/keyboard/ipaq-micro-keys.c new file mode 100644 index 000000000000..602900d1f937 --- /dev/null +++ b/drivers/input/keyboard/ipaq-micro-keys.c @@ -0,0 +1,168 @@ +/* + * 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. + * + * h3600 atmel micro companion support, key subdevice + * based on previous kernel 2.4 version + * Author : Alessandro Gardich + * Author : Linus Walleij + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct ipaq_micro_keys { + struct ipaq_micro *micro; + struct input_dev *input; + u16 *codes; +}; + +static const u16 micro_keycodes[] = { + KEY_RECORD, /* 1: Record button */ + KEY_CALENDAR, /* 2: Calendar */ + KEY_ADDRESSBOOK, /* 3: Contacts (looks like Outlook) */ + KEY_MAIL, /* 4: Envelope (Q on older iPAQs) */ + KEY_HOMEPAGE, /* 5: Start (looks like swoopy arrow) */ + KEY_UP, /* 6: Up */ + KEY_RIGHT, /* 7: Right */ + KEY_LEFT, /* 8: Left */ + KEY_DOWN, /* 9: Down */ +}; + +static void micro_key_receive(void *data, int len, unsigned char *msg) +{ + struct ipaq_micro_keys *keys = data; + int key, down; + + down = 0x80 & msg[0]; + key = 0x7f & msg[0]; + + if (key < ARRAY_SIZE(micro_keycodes)) { + input_report_key(keys->input, keys->codes[key], down); + input_sync(keys->input); + } +} + +static void micro_key_start(struct ipaq_micro_keys *keys) +{ + spin_lock(&keys->micro->lock); + keys->micro->key = micro_key_receive; + keys->micro->key_data = keys; + spin_unlock(&keys->micro->lock); +} + +static void micro_key_stop(struct ipaq_micro_keys *keys) +{ + spin_lock(&keys->micro->lock); + keys->micro->key = NULL; + keys->micro->key_data = NULL; + spin_unlock(&keys->micro->lock); +} + +static int micro_key_open(struct input_dev *input) +{ + struct ipaq_micro_keys *keys = input_get_drvdata(input); + + micro_key_start(keys); + + return 0; +} + +static void micro_key_close(struct input_dev *input) +{ + struct ipaq_micro_keys *keys = input_get_drvdata(input); + + micro_key_stop(keys); +} + +static int micro_key_probe(struct platform_device *pdev) +{ + struct ipaq_micro_keys *keys; + int error; + int i; + + keys = devm_kzalloc(&pdev->dev, sizeof(*keys), GFP_KERNEL); + if (!keys) + return -ENOMEM; + + keys->micro = dev_get_drvdata(pdev->dev.parent); + + keys->input = devm_input_allocate_device(&pdev->dev); + if (!keys->input) + return -ENOMEM; + + keys->input->keycodesize = sizeof(micro_keycodes[0]); + keys->input->keycodemax = ARRAY_SIZE(micro_keycodes); + keys->codes = devm_kmemdup(&pdev->dev, micro_keycodes, + keys->input->keycodesize * keys->input->keycodemax, + GFP_KERNEL); + keys->input->keycode = keys->codes; + + __set_bit(EV_KEY, keys->input->evbit); + for (i = 0; i < ARRAY_SIZE(micro_keycodes); i++) + __set_bit(micro_keycodes[i], keys->input->keybit); + + keys->input->name = "h3600 micro keys"; + keys->input->open = micro_key_open; + keys->input->close = micro_key_close; + input_set_drvdata(keys->input, keys); + + error = input_register_device(keys->input); + if (error) + return error; + + platform_set_drvdata(pdev, keys); + return 0; +} + +static int __maybe_unused micro_key_suspend(struct device *dev) +{ + struct ipaq_micro_keys *keys = dev_get_drvdata(dev); + + micro_key_stop(keys); + + return 0; +} + +static int __maybe_unused micro_key_resume(struct device *dev) +{ + struct ipaq_micro_keys *keys = dev_get_drvdata(dev); + struct input_dev *input = keys->input; + + mutex_lock(&input->mutex); + + if (input->users) + micro_key_start(keys); + + mutex_unlock(&input->mutex); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(micro_key_dev_pm_ops, + micro_key_suspend, micro_key_resume); + +static struct platform_driver micro_key_device_driver = { + .driver = { + .name = "ipaq-micro-keys", + .pm = µ_key_dev_pm_ops, + }, + .probe = micro_key_probe, +}; +module_platform_driver(micro_key_device_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("driver for iPAQ Atmel micro keys"); +MODULE_ALIAS("platform:ipaq-micro-keys"); -- cgit v1.2.3 From 99e14c1e23108c34c4216d68c488fcd937310c32 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 27 Feb 2015 16:17:59 -0800 Subject: Input: psmouse - when comparing PNP IDs ignore case PNP IDs are supposed to be case-insensitive and so we should compare them as such. Acked-by: Hans de Goede Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/psmouse-base.c | 40 +++++++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 7 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index 4ccd01d7a48d..bd91a8efa0f1 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -463,19 +463,45 @@ static int psmouse_poll(struct psmouse *psmouse) PSMOUSE_CMD_POLL | (psmouse->pktsize << 8)); } +static bool psmouse_check_pnp_id(const char *id, const char * const ids[]) +{ + int i; + + for (i = 0; ids[i]; i++) + if (!strcasecmp(id, ids[i])) + return true; + + return false; +} + /* * psmouse_matches_pnp_id - check if psmouse matches one of the passed in ids. */ bool psmouse_matches_pnp_id(struct psmouse *psmouse, const char * const ids[]) { - int i; - - if (!strncmp(psmouse->ps2dev.serio->firmware_id, "PNP:", 4)) - for (i = 0; ids[i]; i++) - if (strstr(psmouse->ps2dev.serio->firmware_id, ids[i])) - return true; + struct serio *serio = psmouse->ps2dev.serio; + char *p, *fw_id_copy, *save_ptr; + bool found = false; + + if (strncmp(serio->firmware_id, "PNP: ", 5)) + return false; + + fw_id_copy = kstrndup(&serio->firmware_id[5], + sizeof(serio->firmware_id) - 5, + GFP_KERNEL); + if (!fw_id_copy) + return false; + + save_ptr = fw_id_copy; + while ((p = strsep(&fw_id_copy, " ")) != NULL) { + if (psmouse_check_pnp_id(p, ids)) { + found = true; + break; + } + } - return false; + kfree(save_ptr); + return found; } /* -- cgit v1.2.3 From de4e374b401a736a2c278babe99b94756060d0e8 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 29 Dec 2014 14:43:44 -0800 Subject: Input: synaptics - switch ForcePad detection to PNP IDs According to Synaptics devices with ForcePads use SYN300D and SYN3014 as PNP IDs, so let's switch from DMI-bases detection scheme to PNP-based one, which should be more reliable. Suggested-by: Hans de Goede Acked-by: Hans de Goede Reviewed-by: Benjamin Tissoires Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/synaptics.c | 35 ++++++++++++++--------------------- drivers/input/mouse/synaptics.h | 1 + 2 files changed, 15 insertions(+), 21 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index f2cceb6493a0..4c69e3304011 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -194,6 +194,13 @@ static const char * const topbuttonpad_pnp_ids[] = { NULL }; +/* This list has been kindly provided by Synaptics. */ +static const char * const forcepad_pnp_ids[] = { + "SYN300D", + "SYN3014", + NULL +}; + /***************************************************************************** * Synaptics communications functions ****************************************************************************/ @@ -605,8 +612,6 @@ static void synaptics_parse_agm(const unsigned char buf[], } } -static bool is_forcepad; - static int synaptics_parse_hw_state(const unsigned char buf[], struct synaptics_data *priv, struct synaptics_hw_state *hw) @@ -636,7 +641,7 @@ static int synaptics_parse_hw_state(const unsigned char buf[], hw->left = (buf[0] & 0x01) ? 1 : 0; hw->right = (buf[0] & 0x02) ? 1 : 0; - if (is_forcepad) { + if (priv->is_forcepad) { /* * ForcePads, like Clickpads, use middle button * bits to report primary button clicks. @@ -1311,29 +1316,11 @@ static const struct dmi_system_id __initconst cr48_dmi_table[] = { { } }; -static const struct dmi_system_id forcepad_dmi_table[] __initconst = { -#if defined(CONFIG_DMI) && defined(CONFIG_X86) - { - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), - DMI_MATCH(DMI_PRODUCT_NAME, "HP EliteBook Folio 1040 G1"), - }, - }, -#endif - { } -}; - void __init synaptics_module_init(void) { impaired_toshiba_kbc = dmi_check_system(toshiba_dmi_table); broken_olpc_ec = dmi_check_system(olpc_dmi_table); cr48_profile_sensor = dmi_check_system(cr48_dmi_table); - - /* - * Unfortunately ForcePad capability is not exported over PS/2, - * so we have to resort to checking DMI. - */ - is_forcepad = dmi_check_system(forcepad_dmi_table); } static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode) @@ -1368,6 +1355,12 @@ static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode) if (SYN_ID_DISGEST_SUPPORTED(priv->identity)) priv->disable_gesture = true; + /* + * Unfortunately ForcePad capability is not exported over PS/2, + * so we have to resort to checking PNP IDs. + */ + priv->is_forcepad = psmouse_matches_pnp_id(psmouse, forcepad_pnp_ids); + if (synaptics_set_mode(psmouse)) { psmouse_err(psmouse, "Unable to initialize device.\n"); goto init_fail; diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h index aedc3299b14e..fb3838ca28de 100644 --- a/drivers/input/mouse/synaptics.h +++ b/drivers/input/mouse/synaptics.h @@ -168,6 +168,7 @@ struct synaptics_data { unsigned long press_start; bool press; bool report_press; + bool is_forcepad; }; void synaptics_module_init(void); -- cgit v1.2.3 From a7ac7c95d4682883d141c5d7a7544d2818f0a09f Mon Sep 17 00:00:00 2001 From: Aleksei Mamlin Date: Fri, 6 Mar 2015 16:38:16 -0800 Subject: Input: goodix - use max touch number from device config Use max number of touches from device config instead of hardcoding. Signed-off-by: Aleksei Mamlin Tested-by: Bastien Nocera Acked-by: Bastien Nocera Tested-by: Antonio Ospite Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/goodix.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c index ca196689f025..3ab7232ac1af 100644 --- a/drivers/input/touchscreen/goodix.c +++ b/drivers/input/touchscreen/goodix.c @@ -48,6 +48,7 @@ struct goodix_ts_data { #define GOODIX_REG_VERSION 0x8140 #define RESOLUTION_LOC 1 +#define MAX_CONTACTS_LOC 5 #define TRIGGER_LOC 6 static const unsigned long goodix_irq_flags[] = { @@ -99,7 +100,7 @@ static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8 *data) } touch_num = data[0] & 0x0f; - if (touch_num > GOODIX_MAX_CONTACTS) + if (touch_num > ts->max_touch_num) return -EPROTO; if (touch_num > 1) { @@ -141,7 +142,7 @@ static void goodix_ts_report_touch(struct goodix_ts_data *ts, u8 *coor_data) */ static void goodix_process_events(struct goodix_ts_data *ts) { - u8 point_data[1 + GOODIX_CONTACT_SIZE * GOODIX_MAX_CONTACTS]; + u8 point_data[1 + GOODIX_CONTACT_SIZE * ts->max_touch_num]; int touch_num; int i; @@ -202,21 +203,23 @@ static void goodix_read_config(struct goodix_ts_data *ts) ts->abs_x_max = GOODIX_MAX_WIDTH; ts->abs_y_max = GOODIX_MAX_HEIGHT; ts->int_trigger_type = GOODIX_INT_TRIGGER; + ts->max_touch_num = GOODIX_MAX_CONTACTS; return; } ts->abs_x_max = get_unaligned_le16(&config[RESOLUTION_LOC]); ts->abs_y_max = get_unaligned_le16(&config[RESOLUTION_LOC + 2]); - ts->int_trigger_type = (config[TRIGGER_LOC]) & 0x03; - if (!ts->abs_x_max || !ts->abs_y_max) { + ts->int_trigger_type = config[TRIGGER_LOC] & 0x03; + ts->max_touch_num = config[MAX_CONTACTS_LOC] & 0x0f; + if (!ts->abs_x_max || !ts->abs_y_max || !ts->max_touch_num) { dev_err(&ts->client->dev, "Invalid config, using defaults\n"); ts->abs_x_max = GOODIX_MAX_WIDTH; ts->abs_y_max = GOODIX_MAX_HEIGHT; + ts->max_touch_num = GOODIX_MAX_CONTACTS; } } - /** * goodix_read_version - Read goodix touchscreen version * @@ -295,7 +298,7 @@ static int goodix_request_input_dev(struct goodix_ts_data *ts) input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0); input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); - input_mt_init_slots(ts->input_dev, GOODIX_MAX_CONTACTS, + input_mt_init_slots(ts->input_dev, ts->max_touch_num, INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED); ts->input_dev->name = "Goodix Capacitive TouchScreen"; -- cgit v1.2.3 From 771d8f1b178e7e09fcc641fccd48852958dbc329 Mon Sep 17 00:00:00 2001 From: Aleksei Mamlin Date: Fri, 6 Mar 2015 16:43:38 -0800 Subject: Input: goodix - add device tree support This change adds device tree support and binding information for Goodix GT9xx series touchscreen controller. It also adds support for 5-finger chips, like GT911 and GT912, which can be found on ARM tablets, such as Wexler TAB7200 and MSI Primo73. Datasheets can be found here: https://drive.google.com/folderview?id=0BxCVOQS3ZymGfmJyY2RKbE5XbVlKNlktVTlwV0lxNEdxd2dzeWZER094cmJPVnMxN1F0Yzg&usp=sharing Signed-off-by: Aleksei Mamlin Reviewed-by: Bastien Nocera Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/Kconfig | 5 +++-- drivers/input/touchscreen/goodix.c | 21 ++++++++++++++++++++- 2 files changed, 23 insertions(+), 3 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 58917525126e..2adf7244b025 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -297,11 +297,12 @@ config TOUCHSCREEN_FUJITSU config TOUCHSCREEN_GOODIX tristate "Goodix I2C touchscreen" - depends on I2C && ACPI + depends on I2C help Say Y here if you have the Goodix touchscreen (such as one installed in Onda v975w tablets) connected to your - system. + system. It also supports 5-finger chip models, which can be + found on ARM tablets, like Wexler TAB7200 and MSI Primo73. If unsure, say N. diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c index 3ab7232ac1af..3af16984d57c 100644 --- a/drivers/input/touchscreen/goodix.c +++ b/drivers/input/touchscreen/goodix.c @@ -23,6 +23,8 @@ #include #include #include +#include +#include #include struct goodix_ts_data { @@ -375,11 +377,27 @@ static const struct i2c_device_id goodix_ts_id[] = { { } }; +#ifdef CONFIG_ACPI static const struct acpi_device_id goodix_acpi_match[] = { { "GDIX1001", 0 }, { } }; MODULE_DEVICE_TABLE(acpi, goodix_acpi_match); +#endif + +#ifdef CONFIG_OF +static const struct of_device_id goodix_of_match[] = { + { .compatible = "goodix,gt911" }, + { .compatible = "goodix,gt9110" }, + { .compatible = "goodix,gt912" }, + { .compatible = "goodix,gt927" }, + { .compatible = "goodix,gt9271" }, + { .compatible = "goodix,gt928" }, + { .compatible = "goodix,gt967" }, + { } +}; +MODULE_DEVICE_TABLE(of, goodix_of_match); +#endif static struct i2c_driver goodix_ts_driver = { .probe = goodix_ts_probe, @@ -387,7 +405,8 @@ static struct i2c_driver goodix_ts_driver = { .driver = { .name = "Goodix-TS", .owner = THIS_MODULE, - .acpi_match_table = goodix_acpi_match, + .acpi_match_table = ACPI_PTR(goodix_acpi_match), + .of_match_table = of_match_ptr(goodix_of_match), }, }; module_i2c_driver(goodix_ts_driver); -- cgit v1.2.3 From 902cb3afab8d4c924376de51ec5c02d171992914 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Szymanski?= Date: Fri, 6 Mar 2015 16:49:38 -0800 Subject: Input: add support for Semtech SX8654 I2C touchscreen controller MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sébastien Szymanski Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/Kconfig | 11 ++ drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/sx8654.c | 286 +++++++++++++++++++++++++++++++++++++ 3 files changed, 298 insertions(+) create mode 100644 drivers/input/touchscreen/sx8654.c (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 2adf7244b025..2310d863a6f1 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -962,6 +962,17 @@ config TOUCHSCREEN_SUR40 To compile this driver as a module, choose M here: the module will be called sur40. +config TOUCHSCREEN_SX8654 + tristate "Semtech SX8654 touchscreen" + depends on I2C + help + Say Y here if you have a Semtech SX8654 touchscreen controller. + + If unsure, say N + + To compile this driver as a module, choose M here: the + module will be called sx8654. + config TOUCHSCREEN_TPS6507X tristate "TPS6507x based touchscreens" depends on I2C diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 0242fea2102a..a06a752966fe 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -79,5 +79,6 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_ATMEL) += atmel-wm97xx.o obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE) += mainstone-wm97xx.o obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE) += zylonite-wm97xx.o obj-$(CONFIG_TOUCHSCREEN_W90X900) += w90p910_ts.o +obj-$(CONFIG_TOUCHSCREEN_SX8654) += sx8654.o obj-$(CONFIG_TOUCHSCREEN_TPS6507X) += tps6507x-ts.o obj-$(CONFIG_TOUCHSCREEN_ZFORCE) += zforce_ts.o diff --git a/drivers/input/touchscreen/sx8654.c b/drivers/input/touchscreen/sx8654.c new file mode 100644 index 000000000000..8e531ac0ecde --- /dev/null +++ b/drivers/input/touchscreen/sx8654.c @@ -0,0 +1,286 @@ +/* + * Driver for Semtech SX8654 I2C touchscreen controller. + * + * Copyright (c) 2015 Armadeus Systems + * Sébastien Szymanski + * + * Using code from: + * - sx865x.c + * Copyright (c) 2013 U-MoBo Srl + * Pierluigi Passaro + * - sx8650.c + * Copyright (c) 2009 Wayne Roberts + * - tsc2007.c + * Copyright (c) 2008 Kwangwoo Lee + * - ads7846.c + * Copyright (c) 2005 David Brownell + * Copyright (c) 2006 Nokia Corporation + * - corgi_ts.c + * Copyright (C) 2004-2005 Richard Purdie + * - omap_ts.[hc], ads7846.h, ts_osk.c + * Copyright (C) 2002 MontaVista Software + * Copyright (C) 2004 Texas Instruments + * Copyright (C) 2005 Dirk Behme + * + * 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 +#include +#include +#include +#include +#include + +/* register addresses */ +#define I2C_REG_TOUCH0 0x00 +#define I2C_REG_TOUCH1 0x01 +#define I2C_REG_CHANMASK 0x04 +#define I2C_REG_IRQMASK 0x22 +#define I2C_REG_IRQSRC 0x23 +#define I2C_REG_SOFTRESET 0x3f + +/* commands */ +#define CMD_READ_REGISTER 0x40 +#define CMD_MANUAL 0xc0 +#define CMD_PENTRG 0xe0 + +/* value for I2C_REG_SOFTRESET */ +#define SOFTRESET_VALUE 0xde + +/* bits for I2C_REG_IRQSRC */ +#define IRQ_PENTOUCH_TOUCHCONVDONE 0x08 +#define IRQ_PENRELEASE 0x04 + +/* bits for RegTouch1 */ +#define CONDIRQ 0x20 +#define FILT_7SA 0x03 + +/* bits for I2C_REG_CHANMASK */ +#define CONV_X 0x80 +#define CONV_Y 0x40 + +/* coordinates rate: higher nibble of CTRL0 register */ +#define RATE_MANUAL 0x00 +#define RATE_5000CPS 0xf0 + +/* power delay: lower nibble of CTRL0 register */ +#define POWDLY_1_1MS 0x0b + +#define MAX_12BIT ((1 << 12) - 1) + +struct sx8654 { + struct input_dev *input; + struct i2c_client *client; +}; + +static irqreturn_t sx8654_irq(int irq, void *handle) +{ + struct sx8654 *sx8654 = handle; + u8 irqsrc; + u8 data[4]; + unsigned int x, y; + int retval; + + irqsrc = i2c_smbus_read_byte_data(sx8654->client, + CMD_READ_REGISTER | I2C_REG_IRQSRC); + dev_dbg(&sx8654->client->dev, "irqsrc = 0x%x", irqsrc); + + if (irqsrc < 0) + goto out; + + if (irqsrc & IRQ_PENRELEASE) { + dev_dbg(&sx8654->client->dev, "pen release interrupt"); + + input_report_key(sx8654->input, BTN_TOUCH, 0); + input_sync(sx8654->input); + } + + if (irqsrc & IRQ_PENTOUCH_TOUCHCONVDONE) { + dev_dbg(&sx8654->client->dev, "pen touch interrupt"); + + retval = i2c_master_recv(sx8654->client, data, sizeof(data)); + if (retval != sizeof(data)) + goto out; + + /* invalid data */ + if (unlikely(data[0] & 0x80 || data[2] & 0x80)) + goto out; + + x = ((data[0] & 0xf) << 8) | (data[1]); + y = ((data[2] & 0xf) << 8) | (data[3]); + + input_report_abs(sx8654->input, ABS_X, x); + input_report_abs(sx8654->input, ABS_Y, y); + input_report_key(sx8654->input, BTN_TOUCH, 1); + input_sync(sx8654->input); + + dev_dbg(&sx8654->client->dev, "point(%4d,%4d)\n", x, y); + } + +out: + return IRQ_HANDLED; +} + +static int sx8654_open(struct input_dev *dev) +{ + struct sx8654 *sx8654 = input_get_drvdata(dev); + struct i2c_client *client = sx8654->client; + int error; + + /* enable pen trigger mode */ + error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH0, + RATE_5000CPS | POWDLY_1_1MS); + if (error) { + dev_err(&client->dev, "writing to I2C_REG_TOUCH0 failed"); + return error; + } + + error = i2c_smbus_write_byte(client, CMD_PENTRG); + if (error) { + dev_err(&client->dev, "writing command CMD_PENTRG failed"); + return error; + } + + enable_irq(client->irq); + + return 0; +} + +static void sx8654_close(struct input_dev *dev) +{ + struct sx8654 *sx8654 = input_get_drvdata(dev); + struct i2c_client *client = sx8654->client; + int error; + + disable_irq(client->irq); + + /* enable manual mode mode */ + error = i2c_smbus_write_byte(client, CMD_MANUAL); + if (error) { + dev_err(&client->dev, "writing command CMD_MANUAL failed"); + return; + } + + error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH0, 0); + if (error) { + dev_err(&client->dev, "writing to I2C_REG_TOUCH0 failed"); + return; + } +} + +static int sx8654_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct sx8654 *sx8654; + struct input_dev *input; + int error; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_WORD_DATA)) + return -ENXIO; + + sx8654 = devm_kzalloc(&client->dev, sizeof(*sx8654), GFP_KERNEL); + if (!sx8654) + return -ENOMEM; + + input = devm_input_allocate_device(&client->dev); + if (!sx8654) + return -ENOMEM; + + input->name = "SX8654 I2C Touchscreen"; + input->id.bustype = BUS_I2C; + input->dev.parent = &client->dev; + input->open = sx8654_open; + input->close = sx8654_close; + + __set_bit(INPUT_PROP_DIRECT, input->propbit); + input_set_capability(input, EV_KEY, BTN_TOUCH); + input_set_abs_params(input, ABS_X, 0, MAX_12BIT, 0, 0); + input_set_abs_params(input, ABS_Y, 0, MAX_12BIT, 0, 0); + + sx8654->client = client; + sx8654->input = input; + + input_set_drvdata(sx8654->input, sx8654); + + error = i2c_smbus_write_byte_data(client, I2C_REG_SOFTRESET, + SOFTRESET_VALUE); + if (error) { + dev_err(&client->dev, "writing softreset value failed"); + return error; + } + + error = i2c_smbus_write_byte_data(client, I2C_REG_CHANMASK, + CONV_X | CONV_Y); + if (error) { + dev_err(&client->dev, "writing to I2C_REG_CHANMASK failed"); + return error; + } + + error = i2c_smbus_write_byte_data(client, I2C_REG_IRQMASK, + IRQ_PENTOUCH_TOUCHCONVDONE | + IRQ_PENRELEASE); + if (error) { + dev_err(&client->dev, "writing to I2C_REG_IRQMASK failed"); + return error; + } + + error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH1, + CONDIRQ | FILT_7SA); + if (error) { + dev_err(&client->dev, "writing to I2C_REG_TOUCH1 failed"); + return error; + } + + error = devm_request_threaded_irq(&client->dev, client->irq, + NULL, sx8654_irq, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + client->name, sx8654); + if (error) { + dev_err(&client->dev, + "Failed to enable IRQ %d, error: %d\n", + client->irq, error); + return error; + } + + /* Disable the IRQ, we'll enable it in sx8654_open() */ + disable_irq(client->irq); + + error = input_register_device(sx8654->input); + if (error) + return error; + + i2c_set_clientdata(client, sx8654); + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id sx8654_of_match[] = { + { .compatible = "semtech,sx8654", }, + { }, +}; +MODULE_DEVICE_TABLE(of, sx8654_of_match); +#endif + +static const struct i2c_device_id sx8654_id_table[] = { + { "semtech_sx8654", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, sx8654_id_table); + +static struct i2c_driver sx8654_driver = { + .driver = { + .name = "sx8654", + .of_match_table = of_match_ptr(sx8654_of_match), + }, + .id_table = sx8654_id_table, + .probe = sx8654_probe, +}; +module_i2c_driver(sx8654_driver); + +MODULE_AUTHOR("Sébastien Szymanski "); +MODULE_DESCRIPTION("Semtech SX8654 I2C Touchscreen Driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 3e9845251926723319fb60c9e546fe42d3d11687 Mon Sep 17 00:00:00 2001 From: Mathias Gottschlag Date: Sat, 7 Mar 2015 13:26:31 -0800 Subject: Input: psmouse - remove hardcoded touchpad size from the focaltech driver The size has in most cases already been fetched from the touchpad, the hardcoded values should have been removed. Signed-off-by: Mathias Gottschlag Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/focaltech.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/focaltech.c b/drivers/input/mouse/focaltech.c index 757f78a94aec..e8fafe8785a7 100644 --- a/drivers/input/mouse/focaltech.c +++ b/drivers/input/mouse/focaltech.c @@ -67,9 +67,6 @@ static void focaltech_reset(struct psmouse *psmouse) #define FOC_MAX_FINGERS 5 -#define FOC_MAX_X 2431 -#define FOC_MAX_Y 1663 - /* * Current state of a single finger on the touchpad. */ @@ -131,7 +128,7 @@ static void focaltech_report_state(struct psmouse *psmouse) if (active) { input_report_abs(dev, ABS_MT_POSITION_X, finger->x); input_report_abs(dev, ABS_MT_POSITION_Y, - FOC_MAX_Y - finger->y); + priv->y_max - finger->y); } } input_mt_report_pointer_emulation(dev, true); -- cgit v1.2.3 From 679d83ea9390636ded518f533af0cefbade317c7 Mon Sep 17 00:00:00 2001 From: Mathias Gottschlag Date: Sat, 7 Mar 2015 13:27:08 -0800 Subject: Input: psmouse - ensure that focaltech reports consistent coordinates We don't know whether x_max or y_max really hold the maximum possible coordinates, and we don't know for sure whether we correctly interpret the coordinates sent by the touchpad, so we clamp the reported values to prevent confusion in userspace code. Signed-off-by: Mathias Gottschlag Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/focaltech.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/focaltech.c b/drivers/input/mouse/focaltech.c index e8fafe8785a7..c66e0e04bb7e 100644 --- a/drivers/input/mouse/focaltech.c +++ b/drivers/input/mouse/focaltech.c @@ -126,9 +126,17 @@ static void focaltech_report_state(struct psmouse *psmouse) input_mt_slot(dev, i); input_mt_report_slot_state(dev, MT_TOOL_FINGER, active); if (active) { - input_report_abs(dev, ABS_MT_POSITION_X, finger->x); + unsigned int clamped_x, clamped_y; + /* + * The touchpad might report invalid data, so we clamp + * the resulting values so that we do not confuse + * userspace. + */ + clamped_x = clamp(finger->x, 0U, priv->x_max); + clamped_y = clamp(finger->y, 0U, priv->y_max); + input_report_abs(dev, ABS_MT_POSITION_X, clamped_x); input_report_abs(dev, ABS_MT_POSITION_Y, - priv->y_max - finger->y); + priv->y_max - clamped_y); } } input_mt_report_pointer_emulation(dev, true); -- cgit v1.2.3 From 4ec212f003d2430b0b2748b8a3008255f39cfe13 Mon Sep 17 00:00:00 2001 From: Mathias Gottschlag Date: Sat, 7 Mar 2015 13:32:10 -0800 Subject: Input: psmouse - disable changing resolution/rate/scale for FocalTech These PS/2 commands make some touchpads stop responding, so this commit adds some dummy functions to replace the generic implementation. Because scale changes were not encapsulated in a method of struct psmouse yet, this commit adds a method set_scale to psmouse. Signed-off-by: Mathias Gottschlag Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/focaltech.c | 25 +++++++++++++++++++++++++ drivers/input/mouse/psmouse-base.c | 14 +++++++++++++- drivers/input/mouse/psmouse.h | 6 ++++++ 3 files changed, 44 insertions(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/focaltech.c b/drivers/input/mouse/focaltech.c index c66e0e04bb7e..891a2716d6a0 100644 --- a/drivers/input/mouse/focaltech.c +++ b/drivers/input/mouse/focaltech.c @@ -386,6 +386,23 @@ static int focaltech_read_size(struct psmouse *psmouse) return 0; } + +void focaltech_set_resolution(struct psmouse *psmouse, unsigned int resolution) +{ + /* not supported yet */ +} + +static void focaltech_set_rate(struct psmouse *psmouse, unsigned int rate) +{ + /* not supported yet */ +} + +static void focaltech_set_scale(struct psmouse *psmouse, + enum psmouse_scale scale) +{ + /* not supported yet */ +} + int focaltech_init(struct psmouse *psmouse) { struct focaltech_data *priv; @@ -420,6 +437,14 @@ int focaltech_init(struct psmouse *psmouse) psmouse->cleanup = focaltech_reset; /* resync is not supported yet */ psmouse->resync_time = 0; + /* + * rate/resolution/scale changes are not supported yet, and + * the generic implementations of these functions seem to + * confuse some touchpads + */ + psmouse->set_resolution = focaltech_set_resolution; + psmouse->set_rate = focaltech_set_rate; + psmouse->set_scale = focaltech_set_scale; return 0; diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index 4ccd01d7a48d..8bc61237bc1b 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -453,6 +453,17 @@ static void psmouse_set_rate(struct psmouse *psmouse, unsigned int rate) psmouse->rate = r; } +/* + * Here we set the mouse scaling. + */ + +static void psmouse_set_scale(struct psmouse *psmouse, enum psmouse_scale scale) +{ + ps2_command(&psmouse->ps2dev, NULL, + scale == PSMOUSE_SCALE21 ? PSMOUSE_CMD_SETSCALE21 : + PSMOUSE_CMD_SETSCALE11); +} + /* * psmouse_poll() - default poll handler. Everyone except for ALPS uses it. */ @@ -689,6 +700,7 @@ static void psmouse_apply_defaults(struct psmouse *psmouse) psmouse->set_rate = psmouse_set_rate; psmouse->set_resolution = psmouse_set_resolution; + psmouse->set_scale = psmouse_set_scale; psmouse->poll = psmouse_poll; psmouse->protocol_handler = psmouse_process_byte; psmouse->pktsize = 3; @@ -1160,7 +1172,7 @@ static void psmouse_initialize(struct psmouse *psmouse) if (psmouse_max_proto != PSMOUSE_PS2) { psmouse->set_rate(psmouse, psmouse->rate); psmouse->set_resolution(psmouse, psmouse->resolution); - ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11); + psmouse->set_scale(psmouse, PSMOUSE_SCALE11); } } diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h index c2ff137ecbdb..d02e1bdc9ae4 100644 --- a/drivers/input/mouse/psmouse.h +++ b/drivers/input/mouse/psmouse.h @@ -36,6 +36,11 @@ typedef enum { PSMOUSE_FULL_PACKET } psmouse_ret_t; +enum psmouse_scale { + PSMOUSE_SCALE11, + PSMOUSE_SCALE21 +}; + struct psmouse { void *private; struct input_dev *dev; @@ -67,6 +72,7 @@ struct psmouse { psmouse_ret_t (*protocol_handler)(struct psmouse *psmouse); void (*set_rate)(struct psmouse *psmouse, unsigned int rate); void (*set_resolution)(struct psmouse *psmouse, unsigned int resolution); + void (*set_scale)(struct psmouse *psmouse, enum psmouse_scale scale); int (*reconnect)(struct psmouse *psmouse); void (*disconnect)(struct psmouse *psmouse); -- cgit v1.2.3 From 4eb8d6e7e5aa14572bc389e554aad9869188cdcd Mon Sep 17 00:00:00 2001 From: Mathias Gottschlag Date: Sat, 7 Mar 2015 13:38:52 -0800 Subject: Input: psmouse - disable "palm detection" in the focaltech driver Apparently, the threshold for large contact area seems to be rather low on some devices, causing the touchpad to frequently freeze during normal usage. Because we do now know how we are supposed to use the value in question, this commit just drops the related code completely. Signed-off-by: Mathias Gottschlag Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/focaltech.c | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/focaltech.c b/drivers/input/mouse/focaltech.c index 891a2716d6a0..23d259416f2f 100644 --- a/drivers/input/mouse/focaltech.c +++ b/drivers/input/mouse/focaltech.c @@ -185,16 +185,6 @@ static void focaltech_process_abs_packet(struct psmouse *psmouse, state->pressed = (packet[0] >> 4) & 1; - /* - * packet[5] contains some kind of tool size in the most - * significant nibble. 0xff is a special value (latching) that - * signals a large contact area. - */ - if (packet[5] == 0xff) { - state->fingers[finger].valid = false; - return; - } - state->fingers[finger].x = ((packet[1] & 0xf) << 8) | packet[2]; state->fingers[finger].y = (packet[3] << 8) | packet[4]; state->fingers[finger].valid = true; -- cgit v1.2.3 From ceb5b6c8bee4841abac9e010c6069934d0f7c996 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 19 Feb 2015 16:22:31 -0800 Subject: Input: pwm-beeper - remove unneeded PWM_BEEPER_PM_OPS define The device->pm pointer is always present so there is no need to do tricks with conditionally defining the pointer. Signed-off-by: Dmitry Torokhov --- drivers/input/misc/pwm-beeper.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/misc/pwm-beeper.c b/drivers/input/misc/pwm-beeper.c index a28ee70ff158..a562071f6385 100644 --- a/drivers/input/misc/pwm-beeper.c +++ b/drivers/input/misc/pwm-beeper.c @@ -169,12 +169,6 @@ static int __maybe_unused pwm_beeper_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(pwm_beeper_pm_ops, pwm_beeper_suspend, pwm_beeper_resume); -#ifdef CONFIG_PM_SLEEP -#define PWM_BEEPER_PM_OPS (&pwm_beeper_pm_ops) -#else -#define PWM_BEEPER_PM_OPS NULL -#endif - #ifdef CONFIG_OF static const struct of_device_id pwm_beeper_match[] = { { .compatible = "pwm-beeper", }, @@ -187,7 +181,7 @@ static struct platform_driver pwm_beeper_driver = { .remove = pwm_beeper_remove, .driver = { .name = "pwm-beeper", - .pm = PWM_BEEPER_PM_OPS, + .pm = &pwm_beeper_pm_ops, .of_match_table = of_match_ptr(pwm_beeper_match), }, }; -- cgit v1.2.3 From b3beed7fe83b077291aa32d1f3006c8480f6344b Mon Sep 17 00:00:00 2001 From: Duson Lin Date: Sat, 7 Mar 2015 20:57:54 -0800 Subject: Input: elan_i2c - return error code when resume fails In order to better diagnose potential issues let's return error to the upper layers when resuming the device fails and also add a few diagnostic messages. Signed-off-by: Duson Lin Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/elan_i2c_core.c | 14 +++++++++----- drivers/input/mouse/elan_i2c_i2c.c | 10 +++++++++- 2 files changed, 18 insertions(+), 6 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c index 7ce8bfe22d7e..b7535385fcdd 100644 --- a/drivers/input/mouse/elan_i2c_core.c +++ b/drivers/input/mouse/elan_i2c_core.c @@ -99,7 +99,7 @@ static int elan_enable_power(struct elan_tp_data *data) error = regulator_enable(data->vcc); if (error) { dev_err(&data->client->dev, - "Failed to enable regulator: %d\n", error); + "failed to enable regulator: %d\n", error); return error; } @@ -111,6 +111,7 @@ static int elan_enable_power(struct elan_tp_data *data) msleep(30); } while (--repeat > 0); + dev_err(&data->client->dev, "failed to enable power: %d\n", error); return error; } @@ -125,7 +126,7 @@ static int elan_disable_power(struct elan_tp_data *data) error = regulator_disable(data->vcc); if (error) { dev_err(&data->client->dev, - "Failed to disable regulator: %d\n", + "failed to disable regulator: %d\n", error); /* Attempt to power the chip back up */ data->ops->power_control(data->client, true); @@ -138,6 +139,7 @@ static int elan_disable_power(struct elan_tp_data *data) msleep(30); } while (--repeat > 0); + dev_err(&data->client->dev, "failed to disable power: %d\n", error); return error; } @@ -1084,16 +1086,18 @@ static int __maybe_unused elan_resume(struct device *dev) } error = elan_enable_power(data); - if (error) + if (error) { dev_err(dev, "power up when resuming failed: %d\n", error); + goto err; + } error = elan_initialize(data); if (error) dev_err(dev, "initialize when resuming failed: %d\n", error); +err: enable_irq(data->client->irq); - - return 0; + return error; } static SIMPLE_DEV_PM_OPS(elan_pm_ops, elan_suspend, elan_resume); diff --git a/drivers/input/mouse/elan_i2c_i2c.c b/drivers/input/mouse/elan_i2c_i2c.c index 029941f861af..6cf0def6d35e 100644 --- a/drivers/input/mouse/elan_i2c_i2c.c +++ b/drivers/input/mouse/elan_i2c_i2c.c @@ -117,7 +117,15 @@ static int elan_i2c_write_cmd(struct i2c_client *client, u16 reg, u16 cmd) int ret; ret = i2c_transfer(client->adapter, &msg, 1); - return ret == 1 ? 0 : (ret < 0 ? ret : -EIO); + if (ret != 1) { + if (ret >= 0) + ret = -EIO; + dev_err(&client->dev, "writing cmd (0x%04x) failed: %d\n", + reg, ret); + return ret; + } + + return 0; } static int elan_i2c_initialize(struct i2c_client *client) -- cgit v1.2.3 From 973877477e2f1eecdf8ae4305cd6e030c7cf08db Mon Sep 17 00:00:00 2001 From: Duson Lin Date: Sun, 8 Mar 2015 14:08:19 -0700 Subject: Input: elan_i2c - remove duplicate repeat code Remove duplicate "repeat--" from function elan_initialize. Signed-off-by: Duson Lin Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/elan_i2c_core.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c index b7535385fcdd..375d98f47483 100644 --- a/drivers/input/mouse/elan_i2c_core.c +++ b/drivers/input/mouse/elan_i2c_core.c @@ -198,7 +198,6 @@ static int elan_initialize(struct elan_tp_data *data) if (!error) return 0; - repeat--; msleep(30); } while (--repeat > 0); -- cgit v1.2.3 From 91c68a7c1d92b48287f2f3111a9b09b26a263d3f Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 8 Mar 2015 14:12:41 -0700 Subject: Input: sun4i-ts - A10 (sun4i) has a different temperature curve Testing has revealed that the temperature in the rtp controller of the A10 (sun4i) SoC has a different curve then on the A13 (sun5i) and later models. Add a new sun5i-a13-ts compatible to differentiate the newer models and set the curve based on the compatible string. The new curve is still not ideal on all A10-s, that seems to have to do with there being a large spread between different A10-s out there, the new curve us based on callibration results on 4 completely different models: raw min raw max temp min temp max stepsize offset Tong Zhang's hackberry 2402 2680 45.0 80.0 0.125 -255.3 Hansg's Cubieboard 2207 2300 36.0 45.0 0.096 -175.8 Olliver's lime 1 (*): 2258 2537 48.3 87.1 0.139 -265.7 Olliver's lime 2 (*): 2222 2486 46.7 91.7 0.170 -331.0 *) from: http://linux-sunxi.org/Temperature_Calibration Average all 4: 0.133 -257.0 Average without outliers (middle 2): 0.132 -261.0 Since it is better to slightly overreport the temperature this patch uses the average of all 4 as curve. This fixes the temperature reported on the A10 being much higher then expected. Reported-by: Tong Zhang Signed-off-by: Hans de Goede Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/sun4i-ts.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/sun4i-ts.c b/drivers/input/touchscreen/sun4i-ts.c index b93a28b955fd..66ccd5af537d 100644 --- a/drivers/input/touchscreen/sun4i-ts.c +++ b/drivers/input/touchscreen/sun4i-ts.c @@ -258,6 +258,15 @@ static int sun4i_ts_probe(struct platform_device *pdev) /* Allwinner SDK has temperature = -271 + (value / 6) (C) */ ts->temp_offset = 1626; ts->temp_step = 167; + } else if (of_device_is_compatible(np, "allwinner,sun4i-a10-ts")) { + /* + * The A10 temperature sensor has quite a wide spread, these + * parameters are based on the averaging of the calibration + * results of 4 completely different boards, with a spread of + * temp_step from 96 - 170 and temp_offset from 1758 - 3310. + */ + ts->temp_offset = 2570; + ts->temp_step = 133; } else { /* * The user manuals do not contain the formula for calculating @@ -330,10 +339,10 @@ static int sun4i_ts_probe(struct platform_device *pdev) * finally enable tp mode. */ reg = STYLUS_UP_DEBOUN(5) | STYLUS_UP_DEBOUN_EN(1); - if (of_device_is_compatible(np, "allwinner,sun4i-a10-ts")) - reg |= TP_MODE_EN(1); - else + if (of_device_is_compatible(np, "allwinner,sun6i-a31-ts")) reg |= SUN6I_TP_MODE_EN(1); + else + reg |= TP_MODE_EN(1); writel(reg, ts->base + TP_CTRL1); /* @@ -383,6 +392,7 @@ static int sun4i_ts_remove(struct platform_device *pdev) static const struct of_device_id sun4i_ts_of_match[] = { { .compatible = "allwinner,sun4i-a10-ts", }, + { .compatible = "allwinner,sun5i-a13-ts", }, { .compatible = "allwinner,sun6i-a31-ts", }, { /* sentinel */ } }; -- cgit v1.2.3 From 8b04baba10b007f8b6c245a50be73cf09cc3a414 Mon Sep 17 00:00:00 2001 From: Daniel Martin Date: Sun, 8 Mar 2015 22:27:37 -0700 Subject: Input: synaptics - split synaptics_resolution(), query first Split the function synaptics_resolution() into synaptics_resolution() and synaptics_quirks(). synaptics_resolution() will be called before synaptics_quirks() to query dimensions and resolutions before overwriting them with quirks. Cc: stable@vger.kernel.org Signed-off-by: Daniel Martin Acked-by: Hans de Goede Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/synaptics.c | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 23e26e0768b5..b501dda75dcb 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -343,7 +343,6 @@ static int synaptics_resolution(struct psmouse *psmouse) { struct synaptics_data *priv = psmouse->private; unsigned char resp[3]; - int i; if (SYN_ID_MAJOR(priv->identity) < 4) return 0; @@ -355,17 +354,6 @@ static int synaptics_resolution(struct psmouse *psmouse) } } - for (i = 0; min_max_pnpid_table[i].pnp_ids; i++) { - if (psmouse_matches_pnp_id(psmouse, - min_max_pnpid_table[i].pnp_ids)) { - priv->x_min = min_max_pnpid_table[i].x_min; - priv->x_max = min_max_pnpid_table[i].x_max; - priv->y_min = min_max_pnpid_table[i].y_min; - priv->y_max = min_max_pnpid_table[i].y_max; - return 0; - } - } - if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 5 && SYN_CAP_MAX_DIMENSIONS(priv->ext_cap_0c)) { if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_MAX_COORDS, resp)) { @@ -391,6 +379,27 @@ static int synaptics_resolution(struct psmouse *psmouse) return 0; } +/* + * Apply quirk(s) if the hardware matches + */ + +static void synaptics_apply_quirks(struct psmouse *psmouse) +{ + struct synaptics_data *priv = psmouse->private; + int i; + + for (i = 0; min_max_pnpid_table[i].pnp_ids; i++) { + if (psmouse_matches_pnp_id(psmouse, + min_max_pnpid_table[i].pnp_ids)) { + priv->x_min = min_max_pnpid_table[i].x_min; + priv->x_max = min_max_pnpid_table[i].x_max; + priv->y_min = min_max_pnpid_table[i].y_min; + priv->y_max = min_max_pnpid_table[i].y_max; + break; + } + } +} + static int synaptics_query_hardware(struct psmouse *psmouse) { if (synaptics_identify(psmouse)) @@ -406,6 +415,8 @@ static int synaptics_query_hardware(struct psmouse *psmouse) if (synaptics_resolution(psmouse)) return -1; + synaptics_apply_quirks(psmouse); + return 0; } -- cgit v1.2.3 From 9aff65982d0f58a78a27769fba7e97bc937b2593 Mon Sep 17 00:00:00 2001 From: Daniel Martin Date: Sun, 8 Mar 2015 22:28:29 -0700 Subject: Input: synaptics - log queried and quirked dimension values Logging the dimension values we queried and the values we use from a quirk to overwrite can be helpful for debugging. This partly relates to bug: https://bugzilla.kernel.org/show_bug.cgi?id=91541 Cc: stable@vger.kernel.org Signed-off-by: Daniel Martin Acked-by: Hans de Goede Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/synaptics.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index b501dda75dcb..47c5dca20a60 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -362,6 +362,9 @@ static int synaptics_resolution(struct psmouse *psmouse) } else { priv->x_max = (resp[0] << 5) | ((resp[1] & 0x0f) << 1); priv->y_max = (resp[2] << 5) | ((resp[1] & 0xf0) >> 3); + psmouse_info(psmouse, + "queried max coordinates: x [..%d], y [..%d]\n", + priv->x_max, priv->y_max); } } @@ -373,6 +376,9 @@ static int synaptics_resolution(struct psmouse *psmouse) } else { priv->x_min = (resp[0] << 5) | ((resp[1] & 0x0f) << 1); priv->y_min = (resp[2] << 5) | ((resp[1] & 0xf0) >> 3); + psmouse_info(psmouse, + "queried min coordinates: x [%d..], y [%d..]\n", + priv->x_min, priv->y_min); } } @@ -395,6 +401,10 @@ static void synaptics_apply_quirks(struct psmouse *psmouse) priv->x_max = min_max_pnpid_table[i].x_max; priv->y_min = min_max_pnpid_table[i].y_min; priv->y_max = min_max_pnpid_table[i].y_max; + psmouse_info(psmouse, + "quirked min/max coordinates: x [%d..%d], y [%d..%d]\n", + priv->x_min, priv->x_max, + priv->y_min, priv->y_max); break; } } -- cgit v1.2.3 From ac097930f0730a9b777737de2b51e0fc49d2be7a Mon Sep 17 00:00:00 2001 From: Daniel Martin Date: Sun, 8 Mar 2015 22:28:40 -0700 Subject: Input: synaptics - query min dimensions for fw v8.1 Query the min dimensions even if the check SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 7 fails, but we know that the firmware version 8.1 is safe. With that we don't need quirks for post-2013 models anymore as they expose correct min and max dimensions. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=91541 Cc: stable@vger.kernel.org Signed-off-by: Daniel Martin re-order the tests to check SYN_CAP_MIN_DIMENSIONS even on FW 8.1 Signed-off-by: Benjamin Tissoires Acked-by: Hans de Goede Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/synaptics.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 47c5dca20a60..87c37f745b92 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -368,8 +368,14 @@ static int synaptics_resolution(struct psmouse *psmouse) } } - if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 7 && - SYN_CAP_MIN_DIMENSIONS(priv->ext_cap_0c)) { + if (SYN_CAP_MIN_DIMENSIONS(priv->ext_cap_0c) && + (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 7 || + /* + * Firmware v8.1 does not report proper number of extended + * capabilities, but has been proven to report correct min + * coordinates. + */ + SYN_ID_FULL(priv->identity) == 0x801)) { if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_MIN_COORDS, resp)) { psmouse_warn(psmouse, "device claims to have min coordinates query, but I'm not able to read it.\n"); -- cgit v1.2.3 From b05f4d1c332a22f98c037fa64f249aa30877adaf Mon Sep 17 00:00:00 2001 From: Daniel Martin Date: Sun, 8 Mar 2015 22:29:07 -0700 Subject: Input: synaptics - remove obsolete min/max quirk for X240 The firmware of the X240 (LEN0035, 2013/12) exposes the same values x [1232..5710], y [1156..4696] as the quirk applies. Cc: stable@vger.kernel.org Signed-off-by: Daniel Martin Acked-by: Hans de Goede Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/synaptics.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 87c37f745b92..af686a82b02b 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -131,7 +131,7 @@ static const struct min_max_quirk min_max_pnpid_table[] = { 1024, 5052, 2258, 4832 }, { - (const char * const []){"LEN0035", "LEN0042", NULL}, + (const char * const []){"LEN0042", NULL}, 1232, 5710, 1156, 4696 }, { -- cgit v1.2.3 From 5b3089ddb540401c1ad2e385a03d7e89ff954585 Mon Sep 17 00:00:00 2001 From: Daniel Martin Date: Sun, 8 Mar 2015 22:29:15 -0700 Subject: Input: synaptics - support min/max board id in min_max_pnpid_table Add a min/max range for board ids to the min/max coordinates quirk. This makes it possible to restrict quirks to specific models based upon their board id. The define ANY_BOARD_ID (0) serves as a wild card. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=91541 Cc: stable@vger.kernel.org Signed-off-by: Daniel Martin Acked-by: Hans de Goede Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/synaptics.c | 42 +++++++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 12 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index af686a82b02b..a900a385e5c3 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -120,32 +120,41 @@ void synaptics_reset(struct psmouse *psmouse) static bool cr48_profile_sensor; +#define ANY_BOARD_ID 0 struct min_max_quirk { const char * const *pnp_ids; + struct { + unsigned long int min, max; + } board_id; int x_min, x_max, y_min, y_max; }; static const struct min_max_quirk min_max_pnpid_table[] = { { (const char * const []){"LEN0033", NULL}, + {ANY_BOARD_ID, ANY_BOARD_ID}, 1024, 5052, 2258, 4832 }, { (const char * const []){"LEN0042", NULL}, + {ANY_BOARD_ID, ANY_BOARD_ID}, 1232, 5710, 1156, 4696 }, { (const char * const []){"LEN0034", "LEN0036", "LEN0037", "LEN0039", "LEN2002", "LEN2004", NULL}, + {ANY_BOARD_ID, ANY_BOARD_ID}, 1024, 5112, 2024, 4832 }, { (const char * const []){"LEN2001", NULL}, + {ANY_BOARD_ID, ANY_BOARD_ID}, 1024, 5022, 2508, 4832 }, { (const char * const []){"LEN2006", NULL}, + {ANY_BOARD_ID, ANY_BOARD_ID}, 1264, 5675, 1171, 4688 }, { } @@ -401,18 +410,27 @@ static void synaptics_apply_quirks(struct psmouse *psmouse) int i; for (i = 0; min_max_pnpid_table[i].pnp_ids; i++) { - if (psmouse_matches_pnp_id(psmouse, - min_max_pnpid_table[i].pnp_ids)) { - priv->x_min = min_max_pnpid_table[i].x_min; - priv->x_max = min_max_pnpid_table[i].x_max; - priv->y_min = min_max_pnpid_table[i].y_min; - priv->y_max = min_max_pnpid_table[i].y_max; - psmouse_info(psmouse, - "quirked min/max coordinates: x [%d..%d], y [%d..%d]\n", - priv->x_min, priv->x_max, - priv->y_min, priv->y_max); - break; - } + if (!psmouse_matches_pnp_id(psmouse, + min_max_pnpid_table[i].pnp_ids)) + continue; + + if (min_max_pnpid_table[i].board_id.min != ANY_BOARD_ID && + priv->board_id < min_max_pnpid_table[i].board_id.min) + continue; + + if (min_max_pnpid_table[i].board_id.max != ANY_BOARD_ID && + priv->board_id > min_max_pnpid_table[i].board_id.max) + continue; + + priv->x_min = min_max_pnpid_table[i].x_min; + priv->x_max = min_max_pnpid_table[i].x_max; + priv->y_min = min_max_pnpid_table[i].y_min; + priv->y_max = min_max_pnpid_table[i].y_max; + psmouse_info(psmouse, + "quirked min/max coordinates: x [%d..%d], y [%d..%d]\n", + priv->x_min, priv->x_max, + priv->y_min, priv->y_max); + break; } } -- cgit v1.2.3 From 02e07492cdfae9c86e3bd21c0beec88dbcc1e9e8 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Sun, 8 Mar 2015 22:29:25 -0700 Subject: Input: synaptics - skip quirks when post-2013 dimensions Post-2013 Lenovo laptops provide correct min/max dimensions, which are different with the ones currently quirked. According to https://bugzilla.kernel.org/show_bug.cgi?id=91541 the following board ids are assigned in the post-2013 touchpads: t440p/t440s: LEN0036 -> 2964/2962 t540p: LEN0034 -> 2964 Using 2961 as the common minimum makes these 3 laptops OK. We may need to update those values later if other pnp_ids has a lower board_id. Cc: stable@vger.kernel.org Signed-off-by: Benjamin Tissoires Acked-by: Hans de Goede Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/synaptics.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index a900a385e5c3..9567a708aa64 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -144,7 +144,7 @@ static const struct min_max_quirk min_max_pnpid_table[] = { (const char * const []){"LEN0034", "LEN0036", "LEN0037", "LEN0039", "LEN2002", "LEN2004", NULL}, - {ANY_BOARD_ID, ANY_BOARD_ID}, + {ANY_BOARD_ID, 2961}, 1024, 5112, 2024, 4832 }, { -- cgit v1.2.3 From dc5465dc8a6d5cae8a0e1d8826bdcb2e4cb261ab Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sun, 8 Mar 2015 22:30:43 -0700 Subject: Input: synaptics - fix middle button on Lenovo 2015 products On the X1 Carbon 3rd gen (with a 2015 broadwell cpu), the physical middle button of the trackstick (attached to the touchpad serio device, of course) seems to get lost. Actually, the touchpads reports 3 extra buttons, which falls in the switch below to the '2' case. Let's handle the case of odd numbers also, so that the middle button finds its way back. Cc: stable@vger.kernel.org Signed-off-by: Benjamin Tissoires Acked-by: Hans de Goede Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/synaptics.c | 44 ++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 23 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 9567a708aa64..e78cc5578527 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -658,6 +658,18 @@ static void synaptics_parse_agm(const unsigned char buf[], priv->agm_pending = true; } +static void synaptics_parse_ext_buttons(const unsigned char buf[], + struct synaptics_data *priv, + struct synaptics_hw_state *hw) +{ + unsigned int ext_bits = + (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) + 1) >> 1; + unsigned int ext_mask = GENMASK(ext_bits - 1, 0); + + hw->ext_buttons = buf[4] & ext_mask; + hw->ext_buttons |= (buf[5] & ext_mask) << ext_bits; +} + static bool is_forcepad; static int synaptics_parse_hw_state(const unsigned char buf[], @@ -744,28 +756,9 @@ static int synaptics_parse_hw_state(const unsigned char buf[], hw->down = ((buf[0] ^ buf[3]) & 0x02) ? 1 : 0; } - if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) && + if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) > 0 && ((buf[0] ^ buf[3]) & 0x02)) { - switch (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) & ~0x01) { - default: - /* - * if nExtBtn is greater than 8 it should be - * considered invalid and treated as 0 - */ - break; - case 8: - hw->ext_buttons |= ((buf[5] & 0x08)) ? 0x80 : 0; - hw->ext_buttons |= ((buf[4] & 0x08)) ? 0x40 : 0; - case 6: - hw->ext_buttons |= ((buf[5] & 0x04)) ? 0x20 : 0; - hw->ext_buttons |= ((buf[4] & 0x04)) ? 0x10 : 0; - case 4: - hw->ext_buttons |= ((buf[5] & 0x02)) ? 0x08 : 0; - hw->ext_buttons |= ((buf[4] & 0x02)) ? 0x04 : 0; - case 2: - hw->ext_buttons |= ((buf[5] & 0x01)) ? 0x02 : 0; - hw->ext_buttons |= ((buf[4] & 0x01)) ? 0x01 : 0; - } + synaptics_parse_ext_buttons(buf, priv, hw); } } else { hw->x = (((buf[1] & 0x1f) << 8) | buf[2]); @@ -832,6 +825,7 @@ static void synaptics_report_buttons(struct psmouse *psmouse, { struct input_dev *dev = psmouse->dev; struct synaptics_data *priv = psmouse->private; + int ext_bits = (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) + 1) >> 1; int i; input_report_key(dev, BTN_LEFT, hw->left); @@ -845,8 +839,12 @@ static void synaptics_report_buttons(struct psmouse *psmouse, input_report_key(dev, BTN_BACK, hw->down); } - for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap); i++) - input_report_key(dev, BTN_0 + i, hw->ext_buttons & (1 << i)); + for (i = 0; i < ext_bits; i++) { + input_report_key(dev, BTN_0 + 2 * i, + hw->ext_buttons & (1 << i)); + input_report_key(dev, BTN_1 + 2 * i, + hw->ext_buttons & (1 << (i + ext_bits))); + } } static void synaptics_report_slot(struct input_dev *dev, int slot, -- cgit v1.2.3 From ebc80840b850db72f7ae84fbcf77630ae5409629 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Sun, 8 Mar 2015 22:32:43 -0700 Subject: Input: synaptics - handle spurious release of trackstick buttons The Fimware 8.1 has a bug in which the extra buttons are only sent when the ExtBit is 1. This should be fixed in a future FW update which should have a bump of the minor version. Cc: stable@vger.kernel.org Signed-off-by: Benjamin Tissoires Acked-by: Hans de Goede Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/synaptics.c | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index e78cc5578527..2f42a712f3e0 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -820,14 +820,36 @@ static void synaptics_report_semi_mt_data(struct input_dev *dev, } } -static void synaptics_report_buttons(struct psmouse *psmouse, - const struct synaptics_hw_state *hw) +static void synaptics_report_ext_buttons(struct psmouse *psmouse, + const struct synaptics_hw_state *hw) { struct input_dev *dev = psmouse->dev; struct synaptics_data *priv = psmouse->private; int ext_bits = (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) + 1) >> 1; int i; + if (!SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap)) + return; + + /* Bug in FW 8.1, buttons are reported only when ExtBit is 1 */ + if (SYN_ID_FULL(priv->identity) == 0x801 && + !((psmouse->packet[0] ^ psmouse->packet[3]) & 0x02)) + return; + + for (i = 0; i < ext_bits; i++) { + input_report_key(dev, BTN_0 + 2 * i, + hw->ext_buttons & (1 << i)); + input_report_key(dev, BTN_1 + 2 * i, + hw->ext_buttons & (1 << (i + ext_bits))); + } +} + +static void synaptics_report_buttons(struct psmouse *psmouse, + const struct synaptics_hw_state *hw) +{ + struct input_dev *dev = psmouse->dev; + struct synaptics_data *priv = psmouse->private; + input_report_key(dev, BTN_LEFT, hw->left); input_report_key(dev, BTN_RIGHT, hw->right); @@ -839,12 +861,7 @@ static void synaptics_report_buttons(struct psmouse *psmouse, input_report_key(dev, BTN_BACK, hw->down); } - for (i = 0; i < ext_bits; i++) { - input_report_key(dev, BTN_0 + 2 * i, - hw->ext_buttons & (1 << i)); - input_report_key(dev, BTN_1 + 2 * i, - hw->ext_buttons & (1 << (i + ext_bits))); - } + synaptics_report_ext_buttons(psmouse, hw); } static void synaptics_report_slot(struct input_dev *dev, int slot, -- cgit v1.2.3 From b57a7128be24062b5b5b26032b7cd58f1651547e Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Sun, 8 Mar 2015 22:33:36 -0700 Subject: Input: synaptics - do not retrieve the board id on old firmwares The board id capability has been added in firmware 7.5. Cc: stable@vger.kernel.org Signed-off-by: Benjamin Tissoires Acked-by: Hans de Goede Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/synaptics.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 2f42a712f3e0..2176874a41b1 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -250,6 +250,10 @@ static int synaptics_board_id(struct psmouse *psmouse) struct synaptics_data *priv = psmouse->private; unsigned char bid[3]; + /* firmwares prior 7.5 have no board_id encoded */ + if (SYN_ID_FULL(priv->identity) < 0x705) + return 0; + if (synaptics_send_cmd(psmouse, SYN_QUE_MODES, bid)) return -1; priv->board_id = ((bid[0] & 0xfc) << 6) | bid[1]; -- cgit v1.2.3 From 06aa374bc70468b517dd36b95c48c8f391c08a27 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Sun, 8 Mar 2015 22:34:03 -0700 Subject: Input: synaptics - retrieve the extended capabilities in query $10 Newer Synaptics touchpads need to get information from the query $10. Retrieve it if available. Signed-off-by: Benjamin Tissoires Acked-by: Hans de Goede Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/synaptics.c | 23 ++++++++++++++++++++--- drivers/input/mouse/synaptics.h | 23 +++++++++++++++++++++++ 2 files changed, 43 insertions(+), 3 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 2176874a41b1..8f6a153677b9 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -241,11 +241,24 @@ static int synaptics_model_id(struct psmouse *psmouse) return 0; } +static int synaptics_more_extended_queries(struct psmouse *psmouse) +{ + struct synaptics_data *priv = psmouse->private; + unsigned char buf[3]; + + if (synaptics_send_cmd(psmouse, SYN_QUE_MEXT_CAPAB_10, buf)) + return -1; + + priv->ext_cap_10 = (buf[0]<<16) | (buf[1]<<8) | buf[2]; + + return 0; +} + /* - * Read the board id from the touchpad + * Read the board id and the "More Extended Queries" from the touchpad * The board id is encoded in the "QUERY MODES" response */ -static int synaptics_board_id(struct psmouse *psmouse) +static int synaptics_query_modes(struct psmouse *psmouse) { struct synaptics_data *priv = psmouse->private; unsigned char bid[3]; @@ -257,6 +270,10 @@ static int synaptics_board_id(struct psmouse *psmouse) if (synaptics_send_cmd(psmouse, SYN_QUE_MODES, bid)) return -1; priv->board_id = ((bid[0] & 0xfc) << 6) | bid[1]; + + if (SYN_MEXT_CAP_BIT(bid[0])) + return synaptics_more_extended_queries(psmouse); + return 0; } @@ -446,7 +463,7 @@ static int synaptics_query_hardware(struct psmouse *psmouse) return -1; if (synaptics_firmware_id(psmouse)) return -1; - if (synaptics_board_id(psmouse)) + if (synaptics_query_modes(psmouse)) return -1; if (synaptics_capability(psmouse)) return -1; diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h index 1bd01f21783b..8d3761ce8f54 100644 --- a/drivers/input/mouse/synaptics.h +++ b/drivers/input/mouse/synaptics.h @@ -22,6 +22,7 @@ #define SYN_QUE_EXT_CAPAB_0C 0x0c #define SYN_QUE_EXT_MAX_COORDS 0x0d #define SYN_QUE_EXT_MIN_COORDS 0x0f +#define SYN_QUE_MEXT_CAPAB_10 0x10 /* synatics modes */ #define SYN_BIT_ABSOLUTE_MODE (1 << 7) @@ -53,6 +54,7 @@ #define SYN_EXT_CAP_REQUESTS(c) (((c) & 0x700000) >> 20) #define SYN_CAP_MULTI_BUTTON_NO(ec) (((ec) & 0x00f000) >> 12) #define SYN_CAP_PRODUCT_ID(ec) (((ec) & 0xff0000) >> 16) +#define SYN_MEXT_CAP_BIT(m) ((m) & (1 << 1)) /* * The following describes response for the 0x0c query. @@ -89,6 +91,26 @@ #define SYN_CAP_REDUCED_FILTERING(ex0c) ((ex0c) & 0x000400) #define SYN_CAP_IMAGE_SENSOR(ex0c) ((ex0c) & 0x000800) +/* + * The following descibes response for the 0x10 query. + * + * byte mask name meaning + * ---- ---- ------- ------------ + * 1 0x01 ext buttons are stick buttons exported in the extended + * capability are actually meant to be used + * by the tracktick (pass-through). + * 1 0x02 SecurePad the touchpad is a SecurePad, so it + * contains a built-in fingerprint reader. + * 1 0xe0 more ext count how many more extented queries are + * available after this one. + * 2 0xff SecurePad width the width of the SecurePad fingerprint + * reader. + * 3 0xff SecurePad height the height of the SecurePad fingerprint + * reader. + */ +#define SYN_CAP_EXT_BUTTONS_STICK(ex10) ((ex10) & 0x010000) +#define SYN_CAP_SECUREPAD(ex10) ((ex10) & 0x020000) + /* synaptics modes query bits */ #define SYN_MODE_ABSOLUTE(m) ((m) & (1 << 7)) #define SYN_MODE_RATE(m) ((m) & (1 << 6)) @@ -156,6 +178,7 @@ struct synaptics_data { unsigned long int capabilities; /* Capabilities */ unsigned long int ext_cap; /* Extended Capabilities */ unsigned long int ext_cap_0c; /* Ext Caps from 0x0c query */ + unsigned long int ext_cap_10; /* Ext Caps from 0x10 query */ unsigned long int identity; /* Identification */ unsigned int x_res, y_res; /* X/Y resolution in units/mm */ unsigned int x_max, y_max; /* Max coordinates (from FW) */ -- cgit v1.2.3 From 3adde1f59195df2965f632e22b31f97fb371612f Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Sun, 8 Mar 2015 22:34:50 -0700 Subject: Input: synaptics - remove TOPBUTTONPAD property for Lenovos 2015 The 2015 series of the Lenovo thinkpads added back the hardware buttons on top of the touchpad for the trackstick. Unfortunately, Lenovo used the PNPIDs that are supposed to be "5 buttons" touchpads, so the new laptops also have the INPUT_PROP_TOPBUTTONPAD. Yay! Instead of manually removing each of the new ones, or hoping that we know all the current ones, we can consider that the PNPIDs list that were given contains touchpads that have the trackstick buttons, either physically wired to them, or emulated with the top software button property. Thanks to the extra buttons capability in query $10, we can reliably detect the physical buttons from the software ones, and so we can remove the TOPBUTTONPAD property even if it was declared as such. Signed-off-by: Benjamin Tissoires Acked-by: Hans de Goede Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/synaptics.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 8f6a153677b9..9d599eb79f17 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -1570,7 +1570,8 @@ static void set_input_params(struct psmouse *psmouse, if (SYN_CAP_CLICKPAD(priv->ext_cap_0c)) { __set_bit(INPUT_PROP_BUTTONPAD, dev->propbit); - if (psmouse_matches_pnp_id(psmouse, topbuttonpad_pnp_ids)) + if (psmouse_matches_pnp_id(psmouse, topbuttonpad_pnp_ids) && + !SYN_CAP_EXT_BUTTONS_STICK(priv->ext_cap_10)) __set_bit(INPUT_PROP_TOPBUTTONPAD, dev->propbit); /* Clickpads report only left button */ __clear_bit(BTN_RIGHT, dev->keybit); -- cgit v1.2.3 From cdd9dc195916ef5644cfac079094c3c1d1616e4c Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Sun, 8 Mar 2015 22:35:41 -0700 Subject: Input: synaptics - re-route tracksticks buttons on the Lenovo 2015 series The 2015 series of the Lenovo thinkpads added back the hardware buttons on top of the touchpad for the trackstick. Unfortunately, they are wired to the touchpad, and not the trackstick. Thus, they are seen as extra buttons from the kernel point of view. This leads to a problem in user space because extra buttons on synaptics devices used to be used as scroll up/down buttons. So in the end, the experience for the user is scroll events for buttons left and right when using the trackstick. Yay! Fortunately, the firmware advertises such behavior in the extended capability $10, and so we can re-route the buttons through the pass-through interface. Hallelujah-expressed-by: Peter Hutterer Signed-off-by: Benjamin Tissoires Acked-by: Hans de Goede Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/synaptics.c | 47 +++++++++++++++++++++++++++++++---------- drivers/input/mouse/synaptics.h | 5 +++++ 2 files changed, 41 insertions(+), 11 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 9d599eb79f17..ecc7811cbd46 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -579,18 +579,22 @@ static int synaptics_is_pt_packet(unsigned char *buf) return (buf[0] & 0xFC) == 0x84 && (buf[3] & 0xCC) == 0xC4; } -static void synaptics_pass_pt_packet(struct serio *ptport, unsigned char *packet) +static void synaptics_pass_pt_packet(struct psmouse *psmouse, + struct serio *ptport, + unsigned char *packet) { + struct synaptics_data *priv = psmouse->private; struct psmouse *child = serio_get_drvdata(ptport); if (child && child->state == PSMOUSE_ACTIVATED) { - serio_interrupt(ptport, packet[1], 0); + serio_interrupt(ptport, packet[1] | priv->pt_buttons, 0); serio_interrupt(ptport, packet[4], 0); serio_interrupt(ptport, packet[5], 0); if (child->pktsize == 4) serio_interrupt(ptport, packet[2], 0); - } else + } else { serio_interrupt(ptport, packet[1], 0); + } } static void synaptics_pt_activate(struct psmouse *psmouse) @@ -847,6 +851,7 @@ static void synaptics_report_ext_buttons(struct psmouse *psmouse, struct input_dev *dev = psmouse->dev; struct synaptics_data *priv = psmouse->private; int ext_bits = (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) + 1) >> 1; + char buf[6] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; int i; if (!SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap)) @@ -857,12 +862,30 @@ static void synaptics_report_ext_buttons(struct psmouse *psmouse, !((psmouse->packet[0] ^ psmouse->packet[3]) & 0x02)) return; - for (i = 0; i < ext_bits; i++) { - input_report_key(dev, BTN_0 + 2 * i, - hw->ext_buttons & (1 << i)); - input_report_key(dev, BTN_1 + 2 * i, - hw->ext_buttons & (1 << (i + ext_bits))); + if (!SYN_CAP_EXT_BUTTONS_STICK(priv->ext_cap_10)) { + for (i = 0; i < ext_bits; i++) { + input_report_key(dev, BTN_0 + 2 * i, + hw->ext_buttons & (1 << i)); + input_report_key(dev, BTN_1 + 2 * i, + hw->ext_buttons & (1 << (i + ext_bits))); + } + return; } + + /* + * This generation of touchpads has the trackstick buttons + * physically wired to the touchpad. Re-route them through + * the pass-through interface. + */ + if (!priv->pt_port) + return; + + /* The trackstick expects at most 3 buttons */ + priv->pt_buttons = SYN_CAP_EXT_BUTTON_STICK_L(hw->ext_buttons) | + SYN_CAP_EXT_BUTTON_STICK_R(hw->ext_buttons) << 1 | + SYN_CAP_EXT_BUTTON_STICK_M(hw->ext_buttons) << 2; + + synaptics_pass_pt_packet(psmouse, priv->pt_port, buf); } static void synaptics_report_buttons(struct psmouse *psmouse, @@ -1459,7 +1482,8 @@ static psmouse_ret_t synaptics_process_byte(struct psmouse *psmouse) if (SYN_CAP_PASS_THROUGH(priv->capabilities) && synaptics_is_pt_packet(psmouse->packet)) { if (priv->pt_port) - synaptics_pass_pt_packet(priv->pt_port, psmouse->packet); + synaptics_pass_pt_packet(psmouse, priv->pt_port, + psmouse->packet); } else synaptics_process_packet(psmouse); @@ -1561,8 +1585,9 @@ static void set_input_params(struct psmouse *psmouse, __set_bit(BTN_BACK, dev->keybit); } - for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap); i++) - __set_bit(BTN_0 + i, dev->keybit); + if (!SYN_CAP_EXT_BUTTONS_STICK(priv->ext_cap_10)) + for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap); i++) + __set_bit(BTN_0 + i, dev->keybit); __clear_bit(EV_REL, dev->evbit); __clear_bit(REL_X, dev->relbit); diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h index 8d3761ce8f54..f39539c70219 100644 --- a/drivers/input/mouse/synaptics.h +++ b/drivers/input/mouse/synaptics.h @@ -111,6 +111,10 @@ #define SYN_CAP_EXT_BUTTONS_STICK(ex10) ((ex10) & 0x010000) #define SYN_CAP_SECUREPAD(ex10) ((ex10) & 0x020000) +#define SYN_CAP_EXT_BUTTON_STICK_L(eb) (!!((eb) & 0x01)) +#define SYN_CAP_EXT_BUTTON_STICK_M(eb) (!!((eb) & 0x02)) +#define SYN_CAP_EXT_BUTTON_STICK_R(eb) (!!((eb) & 0x04)) + /* synaptics modes query bits */ #define SYN_MODE_ABSOLUTE(m) ((m) & (1 << 7)) #define SYN_MODE_RATE(m) ((m) & (1 << 6)) @@ -192,6 +196,7 @@ struct synaptics_data { bool disable_gesture; /* disable gestures */ struct serio *pt_port; /* Pass-through serio port */ + unsigned char pt_buttons; /* Pass-through buttons */ struct synaptics_mt_state mt_state; /* Current mt finger state */ bool mt_state_lost; /* mt_state may be incorrect */ -- cgit v1.2.3 From 860e6f7fcbe5653ec4e394f9ee335f2032398beb Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Sun, 8 Mar 2015 22:38:55 -0700 Subject: Input: synaptics - remove X1 Carbon 3rd gen from the topbuttonpad list Lenovo decided to switch back to physical buttons for the trackstick on their latest series. The PNPId list was provided before they reverted back to physical buttons, so it contains the new models too. We can know from the touchpad capabilities that the touchpad has physical buttons, so removing the ids from the list is not mandatory. It is still nicer to remove the wrong ids, so start by removing the X1 Carbon 3rd gen, with the PNPId of LEN0048. Signed-off-by: Benjamin Tissoires Acked-by: Hans de Goede Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/synaptics.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index ecc7811cbd46..160def02cde2 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -183,7 +183,6 @@ static const char * const topbuttonpad_pnp_ids[] = { "LEN0045", "LEN0046", "LEN0047", - "LEN0048", "LEN0049", "LEN2000", "LEN2001", /* Edge E431 */ -- cgit v1.2.3 From 8f004f3f4daf5dc98dc78f8e62497ad834053855 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Sun, 8 Mar 2015 22:39:17 -0700 Subject: Input: synaptics - remove X250 from the topbuttonpad list Lenovo X250 has a PnpID of LEN0046, but it does not have the top software button requirement. For the record, Lenovo T450s and W541 have a PnpID of LEN200f and LEN004a, so they are not on the top software button list. Signed-off-by: Benjamin Tissoires Reviewed-by: Daniel Martin Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/synaptics.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 160def02cde2..ca7ca8d4eb33 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -181,7 +181,6 @@ static const char * const topbuttonpad_pnp_ids[] = { "LEN0041", "LEN0042", /* Yoga */ "LEN0045", - "LEN0046", "LEN0047", "LEN0049", "LEN2000", -- cgit v1.2.3 From f13b2065de8147a1652b830ea5db961cf80c09df Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 9 Mar 2015 17:03:07 -0700 Subject: Input: i8042 - allow KBD and AUX ports to wake up from suspend-to-idle While registering serio device for i8042, mark them as wakeup-capable and check their user space wakeup settings in i8042_pm_suspend() and i8042_pm_resume() to enable or disable, respectively, their interrupts to wake up the system. This makes it possible to use the PC keyboard to wake up the system from suspend-to-idle, among other things, after writing "enabled" to the keyboard serio device's power/wakeup sysfs attribute. Signed-off-by: Rafael J. Wysocki Signed-off-by: Dmitry Torokhov --- drivers/input/serio/i8042.c | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index 986a71c614b0..cb5ece77fd7d 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -1162,13 +1162,32 @@ static int i8042_controller_resume(bool force_reset) static int i8042_pm_suspend(struct device *dev) { + int i; + i8042_controller_reset(true); + /* Set up serio interrupts for system wakeup. */ + for (i = 0; i < I8042_NUM_PORTS; i++) { + struct serio *serio = i8042_ports[i].serio; + + if (serio && device_may_wakeup(&serio->dev)) + enable_irq_wake(i8042_ports[i].irq); + } + return 0; } static int i8042_pm_resume(struct device *dev) { + int i; + + for (i = 0; i < I8042_NUM_PORTS; i++) { + struct serio *serio = i8042_ports[i].serio; + + if (serio && device_may_wakeup(&serio->dev)) + disable_irq_wake(i8042_ports[i].irq); + } + /* * On resume from S2R we always try to reset the controller * to bring it in a sane state. (In case of S2D we expect @@ -1300,13 +1319,16 @@ static void __init i8042_register_ports(void) int i; for (i = 0; i < I8042_NUM_PORTS; i++) { - if (i8042_ports[i].serio) { + struct serio *serio = i8042_ports[i].serio; + + if (serio) { printk(KERN_INFO "serio: %s at %#lx,%#lx irq %d\n", - i8042_ports[i].serio->name, + serio->name, (unsigned long) I8042_DATA_REG, (unsigned long) I8042_COMMAND_REG, i8042_ports[i].irq); - serio_register_port(i8042_ports[i].serio); + serio_register_port(serio); + device_set_wakeup_capable(&serio->dev, true); } } } -- cgit v1.2.3 From 37dee1acdd587c09826888738d78649a2c529125 Mon Sep 17 00:00:00 2001 From: Charlie Mooney Date: Tue, 10 Mar 2015 18:26:18 -0700 Subject: Input: elants_i2c - append hw_version to FW file Currently the elants_i2c driver simply requests a static filename /lib/firmware/elants_i2c.bin when it gets firmware updates. This is a problem if you have two Elan touchscreens using the same driver. If both touchscreens have different firmwares, you would need to move the files around in your filesystem when you're updating them so that they don't get updated with the other's FW. If you have a read-only filesystem then this is impossible, even. This patch changes the elants_i2c driver to automatically append the four-hex-digit hw_version of the device onto the name of the FW file it's requesting for update. Since different touchscreens should have a different hw_version's this means the user needs to append the hw version of the touchscreen he or she intends to update onto the end of the firmware filename and then the driver will do the rest. The firmware filenames it looks for now are of the form: elants_i2c_${HW_VERSION}.bin eg: elants_i2c_2a44.bin Signed-off-by: Charlie Mooney Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/elants_i2c.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/elants_i2c.c b/drivers/input/touchscreen/elants_i2c.c index 926c58e540c0..43b3c9c2d788 100644 --- a/drivers/input/touchscreen/elants_i2c.c +++ b/drivers/input/touchscreen/elants_i2c.c @@ -98,7 +98,6 @@ #define MAX_FW_UPDATE_RETRIES 30 #define ELAN_FW_PAGESIZE 132 -#define ELAN_FW_FILENAME "elants_i2c.bin" /* calibration timeout definition */ #define ELAN_CALI_TIMEOUT_MSEC 10000 @@ -697,12 +696,19 @@ static int elants_i2c_fw_update(struct elants_data *ts) { struct i2c_client *client = ts->client; const struct firmware *fw; + char *fw_name; int error; - error = request_firmware(&fw, ELAN_FW_FILENAME, &client->dev); + fw_name = kasprintf(GFP_KERNEL, "elants_i2c_%4x.bin", ts->hw_version); + if (!fw_name) + return -ENOMEM; + + dev_info(&client->dev, "requesting fw name = %s\n", fw_name); + error = request_firmware(&fw, fw_name, &client->dev); + kfree(fw_name); if (error) { - dev_err(&client->dev, "failed to request firmware %s: %d\n", - ELAN_FW_FILENAME, error); + dev_err(&client->dev, "failed to request firmware: %d\n", + error); return error; } -- cgit v1.2.3 From b6310affbe2bf14cbe6b6d28db48e1e37d52fbca Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 11 Mar 2015 10:42:13 -0700 Subject: Input: sx8654 - signedness bug in sx8654_irq() "irqsrc" needs to be signed for the error handling to work. Signed-off-by: Dan Carpenter Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/sx8654.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/sx8654.c b/drivers/input/touchscreen/sx8654.c index 8e531ac0ecde..aecb9ad2e701 100644 --- a/drivers/input/touchscreen/sx8654.c +++ b/drivers/input/touchscreen/sx8654.c @@ -79,7 +79,7 @@ struct sx8654 { static irqreturn_t sx8654_irq(int irq, void *handle) { struct sx8654 *sx8654 = handle; - u8 irqsrc; + int irqsrc; u8 data[4]; unsigned int x, y; int retval; -- cgit v1.2.3 From 877bef7de141db241e0c5d06db988b4ecbe29cf3 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 12 Mar 2015 14:50:36 -0700 Subject: Input: sun4i-ts - really fix A10 temperature reporting The commit titled: "Input: sun4i-ts - A10 (sun4i) has a different temperature curve" contains a math error, the offset it uses is in degrees, but the actual code applies the offset before multiplying by stepsize :| Given that this is rather backwards (every math course ever thought applies the multiplication before the offset for linear functions), this commit fixes things by changing the code applying the offset to do the logical thing, adjusting the offset for the other models accordingly. This has been tested on an A10, A13, A20 and A31 to make sure everything really is correct now. Signed-off-by: Hans de Goede Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/sun4i-ts.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/sun4i-ts.c b/drivers/input/touchscreen/sun4i-ts.c index 66ccd5af537d..178d2efb8353 100644 --- a/drivers/input/touchscreen/sun4i-ts.c +++ b/drivers/input/touchscreen/sun4i-ts.c @@ -193,7 +193,7 @@ static int sun4i_get_temp(const struct sun4i_ts_data *ts, long *temp) if (ts->temp_data == -1) return -EAGAIN; - *temp = (ts->temp_data - ts->temp_offset) * ts->temp_step; + *temp = ts->temp_data * ts->temp_step - ts->temp_offset; return 0; } @@ -255,17 +255,17 @@ static int sun4i_ts_probe(struct platform_device *pdev) ts->ignore_fifo_data = true; ts->temp_data = -1; if (of_device_is_compatible(np, "allwinner,sun6i-a31-ts")) { - /* Allwinner SDK has temperature = -271 + (value / 6) (C) */ - ts->temp_offset = 1626; + /* Allwinner SDK has temperature (C) = (value / 6) - 271 */ + ts->temp_offset = 271000; ts->temp_step = 167; } else if (of_device_is_compatible(np, "allwinner,sun4i-a10-ts")) { /* * The A10 temperature sensor has quite a wide spread, these * parameters are based on the averaging of the calibration * results of 4 completely different boards, with a spread of - * temp_step from 96 - 170 and temp_offset from 1758 - 3310. + * temp_step from 0.096 - 0.170 and temp_offset from 176 - 331. */ - ts->temp_offset = 2570; + ts->temp_offset = 257000; ts->temp_step = 133; } else { /* @@ -273,13 +273,13 @@ static int sun4i_ts_probe(struct platform_device *pdev) * the temperature. The formula used here is from the AXP209, * which is designed by X-Powers, an affiliate of Allwinner: * - * temperature = -144.7 + (value * 0.1) + * temperature (C) = (value * 0.1) - 144.7 * * Allwinner does not have any documentation whatsoever for * this hardware. Moreover, it is claimed that the sensor * is inaccurate and cannot work properly. */ - ts->temp_offset = 1447; + ts->temp_offset = 144700; ts->temp_step = 100; } -- cgit v1.2.3 From d1b12075ffa808dce33dd46b7ad035bebf8da215 Mon Sep 17 00:00:00 2001 From: Olivier Sobrie Date: Thu, 12 Mar 2015 14:47:13 -0700 Subject: Input: pwm-beeper - remove useless call to pwm_config() Calling pwm_config() with a period equal to zero always results in error (-EINVAL) and pwm chip config method is never called. Signed-off-by: Olivier Sobrie Signed-off-by: Dmitry Torokhov --- drivers/input/misc/pwm-beeper.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/misc/pwm-beeper.c b/drivers/input/misc/pwm-beeper.c index a562071f6385..e82edf810d1f 100644 --- a/drivers/input/misc/pwm-beeper.c +++ b/drivers/input/misc/pwm-beeper.c @@ -50,7 +50,6 @@ static int pwm_beeper_event(struct input_dev *input, } if (value == 0) { - pwm_config(beeper->pwm, 0, 0); pwm_disable(beeper->pwm); } else { period = HZ_TO_NANOSECONDS(value); -- cgit v1.2.3 From 09d042a2eb90ee2c86d80c48ad096ae3f5776cef Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 16 Mar 2015 09:17:16 -0700 Subject: Revert "Input: synaptics - use dmax in input_mt_assign_slots" This reverts commit 6ab17a8484f03c188a93713369912f1545eb26e9 since it, according to Benjamin, causes issues with slot assignment: E: 15.669119 0000 0000 0000 # ------------ SYN_REPORT (0) ---------- E: 15.954242 0003 002f 0000 # EV_ABS / ABS_MT_SLOT 0 E: 15.954242 0003 0039 0505 # EV_ABS / ABS_MT_TRACKING_ID 505 E: 15.954242 0003 0035 3851 # EV_ABS / ABS_MT_POSITION_X 3851 E: 15.954242 0003 0036 4076 # EV_ABS / ABS_MT_POSITION_Y 4076 E: 15.954242 0003 003a 0034 # EV_ABS / ABS_MT_PRESSURE 34 E: 15.954242 0001 014a 0001 # EV_KEY / BTN_TOUCH 1 E: 15.954242 0003 0000 3851 # EV_ABS / ABS_X 3851 E: 15.954242 0003 0001 4076 # EV_ABS / ABS_Y 4076 E: 15.954242 0003 0018 0034 # EV_ABS / ABS_PRESSURE 34 E: 15.954242 0001 0145 0001 # EV_KEY / BTN_TOOL_FINGER 1 E: 15.954242 0000 0000 0000 # ------------ SYN_REPORT (0) ---------- ... (bunch of regular events)... E: 16.020614 0000 0000 0000 # ------------ SYN_REPORT (0) ---------- E: 16.043601 0003 0035 3873 # EV_ABS / ABS_MT_POSITION_X 3873 E: 16.043601 0003 0036 3903 # EV_ABS / ABS_MT_POSITION_Y 3903 E: 16.043601 0003 003a 0050 # EV_ABS / ABS_MT_PRESSURE 50 E: 16.043601 0003 0035 3032 # EV_ABS / ABS_MT_POSITION_X 3032 E: 16.043601 0003 0036 3832 # EV_ABS / ABS_MT_POSITION_Y 3832 E: 16.043601 0003 003a 0044 # EV_ABS / ABS_MT_PRESSURE 44 E: 16.043601 0003 0000 3032 # EV_ABS / ABS_X 3032 E: 16.043601 0003 0001 3832 # EV_ABS / ABS_Y 3832 E: 16.043601 0003 0018 0044 # EV_ABS / ABS_PRESSURE 44 E: 16.043601 0001 0145 0000 # EV_KEY / BTN_TOOL_FINGER 0 E: 16.043601 0001 014d 0001 # EV_KEY / BTN_TOOL_DOUBLETAP 1 E: 16.043601 0000 0000 0000 # ------------ SYN_REPORT (0) ---------- E: 16.068837 0003 002f 0001 # EV_ABS / ABS_MT_SLOT 1 E: 16.068837 0003 0039 0506 # EV_ABS / ABS_MT_TRACKING_ID 506 E: 16.068837 0003 0035 3912 # EV_ABS / ABS_MT_POSITION_X 3912 E: 16.068837 0003 0036 3743 # EV_ABS / ABS_MT_POSITION_Y 3743 E: 16.068837 0003 003a 0056 # EV_ABS / ABS_MT_PRESSURE 56 E: 16.068837 0003 002f 0000 # EV_ABS / ABS_MT_SLOT 0 E: 16.068837 0003 0035 3026 # EV_ABS / ABS_MT_POSITION_X 3026 E: 16.068837 0003 0036 3708 # EV_ABS / ABS_MT_POSITION_Y 3708 E: 16.068837 0003 003a 0052 # EV_ABS / ABS_MT_PRESSURE 52 E: 16.068837 0003 0000 3026 # EV_ABS / ABS_X 3026 E: 16.068837 0003 0001 3708 # EV_ABS / ABS_Y 3708 E: 16.068837 0003 0018 0052 # EV_ABS / ABS_PRESSURE 52 E: 16.068837 0000 0000 0000 # ------------ SYN_REPORT (0) ---------- Slot 0 and 1 gets inverted in the second report above, which introduces a cursor jump. The problem is that this cursor jump is often enough to leave the current widget, and X sends the scrolling events to whoever is now under the cursor. Reported-by: Benjamin Tissoires Reported-by: Hans de Goede --- drivers/input/mouse/synaptics.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index c74bfa1c05e3..dda605836546 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -67,9 +67,6 @@ #define X_MAX_POSITIVE 8176 #define Y_MAX_POSITIVE 8176 -/* maximum ABS_MT_POSITION displacement (in mm) */ -#define DMAX 10 - /***************************************************************************** * Stuff we need even when we do not want native Synaptics support ****************************************************************************/ @@ -915,7 +912,7 @@ static void synaptics_report_mt_data(struct psmouse *psmouse, pos[i].y = synaptics_invert_y(hw[i]->y); } - input_mt_assign_slots(dev, slot, pos, nsemi, DMAX * priv->x_res); + input_mt_assign_slots(dev, slot, pos, nsemi, 0); for (i = 0; i < nsemi; i++) { input_mt_slot(dev, slot[i]); -- cgit v1.2.3 From 245165de9896cca918701b116d72d486a0398437 Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Wed, 18 Mar 2015 09:58:47 -0700 Subject: Input: constify of_device_id arrays of_device_id is always used as const argument (See driver.of_match_table and open firmware functions). Signed-off-by: Fabian Frederick Signed-off-by: Dmitry Torokhov --- drivers/input/misc/palmas-pwrbutton.c | 2 +- drivers/input/misc/regulator-haptic.c | 2 +- drivers/input/misc/tps65218-pwrbutton.c | 2 +- drivers/input/touchscreen/ar1021_i2c.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/misc/palmas-pwrbutton.c b/drivers/input/misc/palmas-pwrbutton.c index 1f9b5ee92746..1e1baed63929 100644 --- a/drivers/input/misc/palmas-pwrbutton.c +++ b/drivers/input/misc/palmas-pwrbutton.c @@ -304,7 +304,7 @@ static SIMPLE_DEV_PM_OPS(palmas_pwron_pm, palmas_pwron_suspend, palmas_pwron_resume); #ifdef CONFIG_OF -static struct of_device_id of_palmas_pwr_match[] = { +static const struct of_device_id of_palmas_pwr_match[] = { { .compatible = "ti,palmas-pwrbutton" }, { }, }; diff --git a/drivers/input/misc/regulator-haptic.c b/drivers/input/misc/regulator-haptic.c index 132eb914ea3e..6bf3f1082f71 100644 --- a/drivers/input/misc/regulator-haptic.c +++ b/drivers/input/misc/regulator-haptic.c @@ -245,7 +245,7 @@ static int __maybe_unused regulator_haptic_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(regulator_haptic_pm_ops, regulator_haptic_suspend, regulator_haptic_resume); -static struct of_device_id regulator_haptic_dt_match[] = { +static const struct of_device_id regulator_haptic_dt_match[] = { { .compatible = "regulator-haptic" }, { /* sentinel */ }, }; diff --git a/drivers/input/misc/tps65218-pwrbutton.c b/drivers/input/misc/tps65218-pwrbutton.c index 54508dec4eb3..a39b62651a4b 100644 --- a/drivers/input/misc/tps65218-pwrbutton.c +++ b/drivers/input/misc/tps65218-pwrbutton.c @@ -106,7 +106,7 @@ static int tps65218_pwron_probe(struct platform_device *pdev) return 0; } -static struct of_device_id of_tps65218_pwr_match[] = { +static const struct of_device_id of_tps65218_pwr_match[] = { { .compatible = "ti,tps65218-pwrbutton" }, { }, }; diff --git a/drivers/input/touchscreen/ar1021_i2c.c b/drivers/input/touchscreen/ar1021_i2c.c index ba30578e296e..f0b954d46a25 100644 --- a/drivers/input/touchscreen/ar1021_i2c.c +++ b/drivers/input/touchscreen/ar1021_i2c.c @@ -157,7 +157,7 @@ static const struct i2c_device_id ar1021_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, ar1021_i2c_id); -static struct of_device_id ar1021_i2c_of_match[] = { +static const struct of_device_id ar1021_i2c_of_match[] = { { .compatible = "microchip,ar1021-i2c", }, { } }; -- cgit v1.2.3 From 61c797d52dae232a4636888f71c45119a7853d7e Mon Sep 17 00:00:00 2001 From: Nicolas Iooss Date: Thu, 19 Mar 2015 09:13:34 -0700 Subject: Input: elan_i2c - fix typo in include header guard Signed-off-by: Nicolas Iooss Fixes: 6696777c6506 ("Input: add driver for Elan I2C/SMbus touchpad") Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/elan_i2c.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/elan_i2c.h b/drivers/input/mouse/elan_i2c.h index e100c1b31597..9b2dc015f20c 100644 --- a/drivers/input/mouse/elan_i2c.h +++ b/drivers/input/mouse/elan_i2c.h @@ -17,7 +17,7 @@ */ #ifndef _ELAN_I2C_H -#define _ELAN_i2C_H +#define _ELAN_I2C_H #include -- cgit v1.2.3 From 7e174702c88f265600917dacd0f7385db660cca2 Mon Sep 17 00:00:00 2001 From: "Brian K. Turner" Date: Fri, 20 Mar 2015 09:36:35 -0700 Subject: Input: lifebook - fix tabbing issue This change fixes a style issue where spaces where used instead of tabs. Signed-off-by: Brian K. Turner Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/lifebook.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/lifebook.c b/drivers/input/mouse/lifebook.c index 23222dd5a66f..e5ed216824e9 100644 --- a/drivers/input/mouse/lifebook.c +++ b/drivers/input/mouse/lifebook.c @@ -256,8 +256,8 @@ static void lifebook_disconnect(struct psmouse *psmouse) int lifebook_detect(struct psmouse *psmouse, bool set_properties) { - if (!lifebook_present) - return -1; + if (!lifebook_present) + return -1; if (desired_serio_phys && strcmp(psmouse->ps2dev.serio->phys, desired_serio_phys)) @@ -268,7 +268,7 @@ int lifebook_detect(struct psmouse *psmouse, bool set_properties) psmouse->name = "Lifebook TouchScreen"; } - return 0; + return 0; } static int lifebook_create_relative_device(struct psmouse *psmouse) -- cgit v1.2.3 From 68c581d5e7d834d8e97534cafd60bd6716ee6fbc Mon Sep 17 00:00:00 2001 From: Courtney Cavin Date: Fri, 20 Mar 2015 21:45:59 -0700 Subject: Input: add Qualcomm PM8941 power key driver Signed-off-by: Courtney Cavin Signed-off-by: Bjorn Andersson Tested-by: Ivan T. Ivanov Signed-off-by: Dmitry Torokhov --- drivers/input/misc/Kconfig | 12 ++ drivers/input/misc/Makefile | 1 + drivers/input/misc/pm8941-pwrkey.c | 293 +++++++++++++++++++++++++++++++++++++ 3 files changed, 306 insertions(+) create mode 100644 drivers/input/misc/pm8941-pwrkey.c (limited to 'drivers/input') diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index ef542f7ad944..4436ab1b9735 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -115,6 +115,18 @@ config INPUT_PCSPKR To compile this driver as a module, choose M here: the module will be called pcspkr. +config INPUT_PM8941_PWRKEY + tristate "Qualcomm PM8941 power key support" + depends on MFD_SPMI_PMIC + help + Say Y here if you want support for the power key usually found + on boards using a Qualcomm PM8941 compatible PMIC. + + If unsure, say Y. + + To compile this driver as a module, choose M here: the module + will be called pm8941-pwrkey. + config INPUT_PM8XXX_VIBRATOR tristate "Qualcomm PM8XXX vibrator support" depends on MFD_PM8XXX diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 75b58841d5a7..78ba4c1b8532 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -50,6 +50,7 @@ obj-$(CONFIG_INPUT_PCAP) += pcap_keys.o obj-$(CONFIG_INPUT_PCF50633_PMU) += pcf50633-input.o obj-$(CONFIG_INPUT_PCF8574) += pcf8574_keypad.o obj-$(CONFIG_INPUT_PCSPKR) += pcspkr.o +obj-$(CONFIG_INPUT_PM8941_PWRKEY) += pm8941-pwrkey.o obj-$(CONFIG_INPUT_PM8XXX_VIBRATOR) += pm8xxx-vibrator.o obj-$(CONFIG_INPUT_PMIC8XXX_PWRKEY) += pmic8xxx-pwrkey.o obj-$(CONFIG_INPUT_POWERMATE) += powermate.o diff --git a/drivers/input/misc/pm8941-pwrkey.c b/drivers/input/misc/pm8941-pwrkey.c new file mode 100644 index 000000000000..867db8a91372 --- /dev/null +++ b/drivers/input/misc/pm8941-pwrkey.c @@ -0,0 +1,293 @@ +/* + * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * Copyright (c) 2014, Sony Mobile Communications 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 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PON_REV2 0x01 + +#define PON_RT_STS 0x10 +#define PON_KPDPWR_N_SET BIT(0) + +#define PON_PS_HOLD_RST_CTL 0x5a +#define PON_PS_HOLD_RST_CTL2 0x5b +#define PON_PS_HOLD_ENABLE BIT(7) +#define PON_PS_HOLD_TYPE_MASK 0x0f +#define PON_PS_HOLD_TYPE_SHUTDOWN 4 +#define PON_PS_HOLD_TYPE_HARD_RESET 7 + +#define PON_PULL_CTL 0x70 +#define PON_KPDPWR_PULL_UP BIT(1) + +#define PON_DBC_CTL 0x71 +#define PON_DBC_DELAY_MASK 0x7 + + +struct pm8941_pwrkey { + struct device *dev; + int irq; + u32 baseaddr; + struct regmap *regmap; + struct input_dev *input; + + unsigned int revision; + struct notifier_block reboot_notifier; +}; + +static int pm8941_reboot_notify(struct notifier_block *nb, + unsigned long code, void *unused) +{ + struct pm8941_pwrkey *pwrkey = container_of(nb, struct pm8941_pwrkey, + reboot_notifier); + unsigned int enable_reg; + unsigned int reset_type; + int error; + + /* PMICs with revision 0 have the enable bit in same register as ctrl */ + if (pwrkey->revision == 0) + enable_reg = PON_PS_HOLD_RST_CTL; + else + enable_reg = PON_PS_HOLD_RST_CTL2; + + error = regmap_update_bits(pwrkey->regmap, + pwrkey->baseaddr + enable_reg, + PON_PS_HOLD_ENABLE, + 0); + if (error) + dev_err(pwrkey->dev, + "unable to clear ps hold reset enable: %d\n", + error); + + /* + * Updates of PON_PS_HOLD_ENABLE requires 3 sleep cycles between + * writes. + */ + usleep_range(100, 1000); + + switch (code) { + case SYS_HALT: + case SYS_POWER_OFF: + reset_type = PON_PS_HOLD_TYPE_SHUTDOWN; + break; + case SYS_RESTART: + default: + reset_type = PON_PS_HOLD_TYPE_HARD_RESET; + break; + }; + + error = regmap_update_bits(pwrkey->regmap, + pwrkey->baseaddr + PON_PS_HOLD_RST_CTL, + PON_PS_HOLD_TYPE_MASK, + reset_type); + if (error) + dev_err(pwrkey->dev, "unable to set ps hold reset type: %d\n", + error); + + error = regmap_update_bits(pwrkey->regmap, + pwrkey->baseaddr + enable_reg, + PON_PS_HOLD_ENABLE, + PON_PS_HOLD_ENABLE); + if (error) + dev_err(pwrkey->dev, "unable to re-set enable: %d\n", error); + + return NOTIFY_DONE; +} + +static irqreturn_t pm8941_pwrkey_irq(int irq, void *_data) +{ + struct pm8941_pwrkey *pwrkey = _data; + unsigned int sts; + int error; + + error = regmap_read(pwrkey->regmap, + pwrkey->baseaddr + PON_RT_STS, &sts); + if (error) + return IRQ_HANDLED; + + input_report_key(pwrkey->input, KEY_POWER, !!(sts & PON_KPDPWR_N_SET)); + input_sync(pwrkey->input); + + return IRQ_HANDLED; +} + +static int __maybe_unused pm8941_pwrkey_suspend(struct device *dev) +{ + struct pm8941_pwrkey *pwrkey = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + enable_irq_wake(pwrkey->irq); + + return 0; +} + +static int __maybe_unused pm8941_pwrkey_resume(struct device *dev) +{ + struct pm8941_pwrkey *pwrkey = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(pwrkey->irq); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(pm8941_pwr_key_pm_ops, + pm8941_pwrkey_suspend, pm8941_pwrkey_resume); + +static int pm8941_pwrkey_probe(struct platform_device *pdev) +{ + struct pm8941_pwrkey *pwrkey; + bool pull_up; + u32 req_delay; + int error; + + if (of_property_read_u32(pdev->dev.of_node, "debounce", &req_delay)) + req_delay = 15625; + + if (req_delay > 2000000 || req_delay == 0) { + dev_err(&pdev->dev, "invalid debounce time: %u\n", req_delay); + return -EINVAL; + } + + pull_up = of_property_read_bool(pdev->dev.of_node, "bias-pull-up"); + + pwrkey = devm_kzalloc(&pdev->dev, sizeof(*pwrkey), GFP_KERNEL); + if (!pwrkey) + return -ENOMEM; + + pwrkey->dev = &pdev->dev; + + pwrkey->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!pwrkey->regmap) { + dev_err(&pdev->dev, "failed to locate regmap\n"); + return -ENODEV; + } + + pwrkey->irq = platform_get_irq(pdev, 0); + if (pwrkey->irq < 0) { + dev_err(&pdev->dev, "failed to get irq\n"); + 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) { + dev_err(&pdev->dev, "failed to set debounce: %d\n", error); + return error; + } + + 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); + + pwrkey->input->name = "pm8941_pwrkey"; + pwrkey->input->phys = "pm8941_pwrkey/input0"; + + req_delay = (req_delay << 6) / USEC_PER_SEC; + req_delay = ilog2(req_delay); + + error = regmap_update_bits(pwrkey->regmap, + pwrkey->baseaddr + PON_DBC_CTL, + PON_DBC_DELAY_MASK, + req_delay); + if (error) { + dev_err(&pdev->dev, "failed to set debounce: %d\n", error); + return error; + } + + error = regmap_update_bits(pwrkey->regmap, + pwrkey->baseaddr + PON_PULL_CTL, + PON_KPDPWR_PULL_UP, + pull_up ? PON_KPDPWR_PULL_UP : 0); + if (error) { + dev_err(&pdev->dev, "failed to set pull: %d\n", error); + return error; + } + + error = devm_request_threaded_irq(&pdev->dev, pwrkey->irq, + NULL, pm8941_pwrkey_irq, + IRQF_ONESHOT, + "pm8941_pwrkey", pwrkey); + if (error) { + dev_err(&pdev->dev, "failed requesting IRQ: %d\n", error); + return error; + } + + error = input_register_device(pwrkey->input); + if (error) { + dev_err(&pdev->dev, "failed to register input device: %d\n", + error); + return error; + } + + pwrkey->reboot_notifier.notifier_call = pm8941_reboot_notify, + error = register_reboot_notifier(&pwrkey->reboot_notifier); + if (error) { + dev_err(&pdev->dev, "failed to register reboot notifier: %d\n", + error); + return error; + } + + platform_set_drvdata(pdev, pwrkey); + device_init_wakeup(&pdev->dev, 1); + + return 0; +} + +static int pm8941_pwrkey_remove(struct platform_device *pdev) +{ + struct pm8941_pwrkey *pwrkey = platform_get_drvdata(pdev); + + device_init_wakeup(&pdev->dev, 0); + unregister_reboot_notifier(&pwrkey->reboot_notifier); + + return 0; +} + +static const struct of_device_id pm8941_pwr_key_id_table[] = { + { .compatible = "qcom,pm8941-pwrkey" }, + { } +}; +MODULE_DEVICE_TABLE(of, pm8941_pwr_key_id_table); + +static struct platform_driver pm8941_pwrkey_driver = { + .probe = pm8941_pwrkey_probe, + .remove = pm8941_pwrkey_remove, + .driver = { + .name = "pm8941-pwrkey", + .pm = &pm8941_pwr_key_pm_ops, + .of_match_table = of_match_ptr(pm8941_pwr_key_id_table), + }, +}; +module_platform_driver(pm8941_pwrkey_driver); + +MODULE_DESCRIPTION("PM8941 Power Key driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 4ed0e032c3cf27c6fabc154164d003c4e0ac4654 Mon Sep 17 00:00:00 2001 From: Jens Thiele Date: Mon, 23 Mar 2015 09:04:56 -0700 Subject: Input: sun4i-ts - allow controlling filter and sensitivity via DT This commit introduces two new optional device-tree properties: "tp-sensitive-adjust": adjust sensitivity of pen down detection "filter-type": select median and averaging filter The previous fixed defaults, didn't work well for the Olimex A13-LCD10TS (I have). Signed-off-by: Jens Thiele Reviewed-by: Hans de Goede Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/sun4i-ts.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/sun4i-ts.c b/drivers/input/touchscreen/sun4i-ts.c index 178d2efb8353..c0116994067d 100644 --- a/drivers/input/touchscreen/sun4i-ts.c +++ b/drivers/input/touchscreen/sun4i-ts.c @@ -30,6 +30,10 @@ * These kinds of heuristics are just asking for trouble (and don't belong * in the kernel). So this driver offers straight forward, reliable single * touch functionality only. + * + * s.a. A20 User Manual "1.15 TP" (Documentation/arm/sunxi/README) + * (looks like the description in the A20 User Manual v1.3 is better + * than the one in the A10 User Manual v.1.5) */ #include @@ -246,6 +250,8 @@ static int sun4i_ts_probe(struct platform_device *pdev) int error; u32 reg; bool ts_attached; + u32 tp_sensitive_adjust = 15; + u32 filter_type = 1; ts = devm_kzalloc(dev, sizeof(struct sun4i_ts_data), GFP_KERNEL); if (!ts) @@ -322,14 +328,20 @@ static int sun4i_ts_probe(struct platform_device *pdev) ts->base + TP_CTRL0); /* - * sensitive_adjust = 15 : max, which is not all that sensitive, + * tp_sensitive_adjust is an optional property * tp_mode = 0 : only x and y coordinates, as we don't use dual touch */ - writel(TP_SENSITIVE_ADJUST(15) | TP_MODE_SELECT(0), + of_property_read_u32(np, "allwinner,tp-sensitive-adjust", + &tp_sensitive_adjust); + writel(TP_SENSITIVE_ADJUST(tp_sensitive_adjust) | TP_MODE_SELECT(0), ts->base + TP_CTRL2); - /* Enable median filter, type 1 : 5/3 */ - writel(FILTER_EN(1) | FILTER_TYPE(1), ts->base + TP_CTRL3); + /* + * Enable median and averaging filter, optional property for + * filter type. + */ + of_property_read_u32(np, "allwinner,filter-type", &filter_type); + writel(FILTER_EN(1) | FILTER_TYPE(filter_type), ts->base + TP_CTRL3); /* Enable temperature measurement, period 1953 (2 seconds) */ writel(TEMP_ENABLE(1) | TEMP_PERIOD(1953), ts->base + TP_TPR); -- cgit v1.2.3 From c164c147c9a0a371c4710186972a02b6ee2eb984 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sat, 21 Mar 2015 20:29:34 -0700 Subject: Input: ALPS - fix max coordinates for v5 and v7 protocols MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 3296f71cd2fde7a2ad52e66a27eae419f6328066 ("Input: ALPS - consolidate setting protocol parameters") inadvertently moved call to alps_dolphin_get_device_area() from v5 to v7 protocol, causing both protocols report incorrect maximum values for X and Y axes which resulted in crash in Synaptics X driver. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=94801 Reported-by: Santiago Gala Reported-by: Pali Rohár Acked-by: Hans de Goede Acked-by: Pali Rohár Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/alps.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index 1bd15ebc01f2..33198b91bebf 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -2281,10 +2281,12 @@ static int alps_set_protocol(struct psmouse *psmouse, priv->set_abs_params = alps_set_abs_params_mt; priv->nibble_commands = alps_v3_nibble_commands; priv->addr_command = PSMOUSE_CMD_RESET_WRAP; - priv->x_max = 1360; - priv->y_max = 660; priv->x_bits = 23; priv->y_bits = 12; + + if (alps_dolphin_get_device_area(psmouse, priv)) + return -EIO; + break; case ALPS_PROTO_V6: @@ -2303,9 +2305,8 @@ static int alps_set_protocol(struct psmouse *psmouse, priv->set_abs_params = alps_set_abs_params_mt; priv->nibble_commands = alps_v3_nibble_commands; priv->addr_command = PSMOUSE_CMD_RESET_WRAP; - - if (alps_dolphin_get_device_area(psmouse, priv)) - return -EIO; + priv->x_max = 0xfff; + priv->y_max = 0x7ff; if (priv->fw_ver[1] != 0xba) priv->flags |= ALPS_BUTTONPAD; -- cgit v1.2.3 From 98dc0703735d9cfc483522d5ffbce0c0b07c1f86 Mon Sep 17 00:00:00 2001 From: Ramiro Morales Date: Mon, 23 Mar 2015 10:33:07 -0700 Subject: Input: synaptics - add quirk for Thinkpad E440 Its ClickPad shares PNP ID "LEN2006" with the one in model E540 which is already handled by the driver (both are Haswell iterations of the Edge line, launched in 2014) but the dimensions it reports are different: $ sudo ./touchpad-edge-detector /dev/input/event3 Touchpad SynPS/2 Synaptics TouchPad on /dev/input/event3 Move one finger around the touchpad to detect the actual edges Kernel says: x [1472..5044], y [1408..3398] Touchpad sends: x [1024..5045], y [2457..4832] /^C Fortunately we can use the board ID, which is also different, to distinguish among them. $ dmesg | grep -i synaptics psmouse serio1: synaptics: Touchpad model: 1, fw: 8.1, id: 0x1e2b1, caps: 0xd001a3/0x940300/0x127c00, board id: 2691, fw id: 1494646 psmouse serio1: synaptics: serio: Synaptics pass-through port at isa0060/serio1/input0 input: SynPS/2 Synaptics TouchPad as /devices/platform/i8042/serio1/input/input4 Board ID in E540 is 2722: psmouse serio1: synaptics: Touchpad model: 1, fw: 8.1, id: 0x1e2b1, caps: 0xd001a3/0x940300/0x127c00, board id: 2722, fw id: 1484859 (from https://launchpadlibrarian.net/179702965/BootDmesg.txt) Signed-off-by: Ramiro Morales Reviewed-by: Benjamin Tissoires Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/synaptics.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index dda605836546..f6a3a7b7d1ad 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -152,6 +152,11 @@ static const struct min_max_quirk min_max_pnpid_table[] = { {ANY_BOARD_ID, ANY_BOARD_ID}, 1024, 5022, 2508, 4832 }, + { + (const char * const []){"LEN2006", NULL}, + {2691, 2691}, + 1024, 5045, 2457, 4632 + }, { (const char * const []){"LEN2006", NULL}, {ANY_BOARD_ID, ANY_BOARD_ID}, @@ -189,7 +194,7 @@ static const char * const topbuttonpad_pnp_ids[] = { "LEN2003", "LEN2004", /* L440 */ "LEN2005", - "LEN2006", + "LEN2006", /* Edge E440/E540 */ "LEN2007", "LEN2008", "LEN2009", -- cgit v1.2.3 From d5ae685f15307c85c6267c5a2be9ba3d70eb3297 Mon Sep 17 00:00:00 2001 From: Jonathan Richardson Date: Sat, 21 Mar 2015 21:04:59 -0700 Subject: Input: add Broadcom iProc touchscreen driver Add initial version of the Broadcom touchscreen driver. Reviewed-by: Ray Jui Reviewed-by: Scott Branden Tested-by: Scott Branden Signed-off-by: Jonathan Richardson Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/Kconfig | 12 + drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/bcm_iproc_tsc.c | 522 ++++++++++++++++++++++++++++++ 3 files changed, 535 insertions(+) create mode 100644 drivers/input/touchscreen/bcm_iproc_tsc.c (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 1130c4059104..a1cfe16e9a81 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -324,6 +324,18 @@ config TOUCHSCREEN_ILI210X To compile this driver as a module, choose M here: the module will be called ili210x. +config TOUCHSCREEN_IPROC + tristate "IPROC touch panel driver support" + depends on ARCH_BCM_IPROC || COMPILE_TEST + help + Say Y here if you want to add support for the IPROC touch + controller to your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called bcm_iproc_tsc. + config TOUCHSCREEN_S3C2410 tristate "Samsung S3C2410/generic touchscreen input driver" depends on ARCH_S3C24XX || SAMSUNG_DEV_TS diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index a06a752966fe..09c33531ab8e 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix.o obj-$(CONFIG_TOUCHSCREEN_ILI210X) += ili210x.o obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o obj-$(CONFIG_TOUCHSCREEN_INTEL_MID) += intel-mid-touch.o +obj-$(CONFIG_TOUCHSCREEN_IPROC) += bcm_iproc_tsc.o obj-$(CONFIG_TOUCHSCREEN_LPC32XX) += lpc32xx_ts.o obj-$(CONFIG_TOUCHSCREEN_MAX11801) += max11801_ts.o obj-$(CONFIG_TOUCHSCREEN_MC13783) += mc13783_ts.o diff --git a/drivers/input/touchscreen/bcm_iproc_tsc.c b/drivers/input/touchscreen/bcm_iproc_tsc.c new file mode 100644 index 000000000000..ae460a5c93d5 --- /dev/null +++ b/drivers/input/touchscreen/bcm_iproc_tsc.c @@ -0,0 +1,522 @@ +/* +* Copyright (C) 2015 Broadcom Corporation +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation version 2. +* +* This program is distributed "as is" WITHOUT ANY WARRANTY of any +* kind, whether express or implied; without even the implied warranty +* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IPROC_TS_NAME "iproc-ts" + +#define PEN_DOWN_STATUS 1 +#define PEN_UP_STATUS 0 + +#define X_MIN 0 +#define Y_MIN 0 +#define X_MAX 0xFFF +#define Y_MAX 0xFFF + +/* Value given by controller for invalid coordinate. */ +#define INVALID_COORD 0xFFFFFFFF + +/* Register offsets */ +#define REGCTL1 0x00 +#define REGCTL2 0x04 +#define INTERRUPT_THRES 0x08 +#define INTERRUPT_MASK 0x0c + +#define INTERRUPT_STATUS 0x10 +#define CONTROLLER_STATUS 0x14 +#define FIFO_DATA 0x18 +#define FIFO_DATA_X_Y_MASK 0xFFFF +#define ANALOG_CONTROL 0x1c + +#define AUX_DATA 0x20 +#define DEBOUNCE_CNTR_STAT 0x24 +#define SCAN_CNTR_STAT 0x28 +#define REM_CNTR_STAT 0x2c + +#define SETTLING_TIMER_STAT 0x30 +#define SPARE_REG 0x34 +#define SOFT_BYPASS_CONTROL 0x38 +#define SOFT_BYPASS_DATA 0x3c + + +/* Bit values for INTERRUPT_MASK and INTERRUPT_STATUS regs */ +#define TS_PEN_INTR_MASK BIT(0) +#define TS_FIFO_INTR_MASK BIT(2) + +/* Bit values for CONTROLLER_STATUS reg1 */ +#define TS_PEN_DOWN BIT(0) + +/* Shift values for control reg1 */ +#define SCANNING_PERIOD_SHIFT 24 +#define DEBOUNCE_TIMEOUT_SHIFT 16 +#define SETTLING_TIMEOUT_SHIFT 8 +#define TOUCH_TIMEOUT_SHIFT 0 + +/* Shift values for coordinates from fifo */ +#define X_COORD_SHIFT 0 +#define Y_COORD_SHIFT 16 + +/* Bit values for REGCTL2 */ +#define TS_CONTROLLER_EN_BIT BIT(16) +#define TS_CONTROLLER_AVGDATA_SHIFT 8 +#define TS_CONTROLLER_AVGDATA_MASK (0x7 << TS_CONTROLLER_AVGDATA_SHIFT) +#define TS_CONTROLLER_PWR_LDO BIT(5) +#define TS_CONTROLLER_PWR_ADC BIT(4) +#define TS_CONTROLLER_PWR_BGP BIT(3) +#define TS_CONTROLLER_PWR_TS BIT(2) +#define TS_WIRE_MODE_BIT BIT(1) + +#define dbg_reg(dev, priv, reg) \ + dev_dbg(dev, "%20s= 0x%08x\n", #reg, readl((priv)->regs + reg)) + +struct tsc_param { + /* Each step is 1024 us. Valid 1-256 */ + u32 scanning_period; + + /* Each step is 512 us. Valid 0-255 */ + u32 debounce_timeout; + + /* + * The settling duration (in ms) is the amount of time the tsc + * waits to allow the voltage to settle after turning on the + * drivers in detection mode. Valid values: 0-11 + * 0 = 0.008 ms + * 1 = 0.01 ms + * 2 = 0.02 ms + * 3 = 0.04 ms + * 4 = 0.08 ms + * 5 = 0.16 ms + * 6 = 0.32 ms + * 7 = 0.64 ms + * 8 = 1.28 ms + * 9 = 2.56 ms + * 10 = 5.12 ms + * 11 = 10.24 ms + */ + u32 settling_timeout; + + /* touch timeout in sample counts */ + u32 touch_timeout; + + /* + * Number of data samples which are averaged before a final data point + * is placed into the FIFO + */ + u32 average_data; + + /* FIFO threshold */ + u32 fifo_threshold; + + /* Optional standard touchscreen properties. */ + u32 max_x; + u32 max_y; + u32 fuzz_x; + u32 fuzz_y; + bool invert_x; + bool invert_y; +}; + +struct iproc_ts_priv { + struct platform_device *pdev; + struct input_dev *idev; + + void __iomem *regs; + struct clk *tsc_clk; + + int pen_status; + struct tsc_param cfg_params; +}; + +/* + * Set default values the same as hardware reset values + * except for fifo_threshold with is set to 1. + */ +static const struct tsc_param iproc_default_config = { + .scanning_period = 0x5, /* 1 to 256 */ + .debounce_timeout = 0x28, /* 0 to 255 */ + .settling_timeout = 0x7, /* 0 to 11 */ + .touch_timeout = 0xa, /* 0 to 255 */ + .average_data = 5, /* entry 5 = 32 pts */ + .fifo_threshold = 1, /* 0 to 31 */ + .max_x = X_MAX, + .max_y = Y_MAX, +}; + +static void ts_reg_dump(struct iproc_ts_priv *priv) +{ + struct device *dev = &priv->pdev->dev; + + dbg_reg(dev, priv, REGCTL1); + dbg_reg(dev, priv, REGCTL2); + dbg_reg(dev, priv, INTERRUPT_THRES); + dbg_reg(dev, priv, INTERRUPT_MASK); + dbg_reg(dev, priv, INTERRUPT_STATUS); + dbg_reg(dev, priv, CONTROLLER_STATUS); + dbg_reg(dev, priv, FIFO_DATA); + dbg_reg(dev, priv, ANALOG_CONTROL); + dbg_reg(dev, priv, AUX_DATA); + dbg_reg(dev, priv, DEBOUNCE_CNTR_STAT); + dbg_reg(dev, priv, SCAN_CNTR_STAT); + dbg_reg(dev, priv, REM_CNTR_STAT); + dbg_reg(dev, priv, SETTLING_TIMER_STAT); + dbg_reg(dev, priv, SPARE_REG); + dbg_reg(dev, priv, SOFT_BYPASS_CONTROL); + dbg_reg(dev, priv, SOFT_BYPASS_DATA); +} + +static irqreturn_t iproc_touchscreen_interrupt(int irq, void *data) +{ + struct platform_device *pdev = data; + struct iproc_ts_priv *priv = platform_get_drvdata(pdev); + u32 intr_status; + u32 raw_coordinate; + u16 x; + u16 y; + int i; + bool needs_sync = false; + + intr_status = readl(priv->regs + INTERRUPT_STATUS); + intr_status &= TS_PEN_INTR_MASK | TS_FIFO_INTR_MASK; + if (intr_status == 0) + return IRQ_NONE; + + /* Clear all interrupt status bits, write-1-clear */ + writel(intr_status, priv->regs + INTERRUPT_STATUS); + + /* Pen up/down */ + if (intr_status & TS_PEN_INTR_MASK) { + if (readl(priv->regs + CONTROLLER_STATUS) & TS_PEN_DOWN) + priv->pen_status = PEN_DOWN_STATUS; + else + priv->pen_status = PEN_UP_STATUS; + + input_report_key(priv->idev, BTN_TOUCH, priv->pen_status); + needs_sync = true; + + dev_dbg(&priv->pdev->dev, + "pen up-down (%d)\n", priv->pen_status); + } + + /* coordinates in FIFO exceed the theshold */ + if (intr_status & TS_FIFO_INTR_MASK) { + for (i = 0; i < priv->cfg_params.fifo_threshold; i++) { + raw_coordinate = readl(priv->regs + FIFO_DATA); + if (raw_coordinate == INVALID_COORD) + continue; + + /* + * The x and y coordinate are 16 bits each + * with the x in the lower 16 bits and y in the + * upper 16 bits. + */ + x = (raw_coordinate >> X_COORD_SHIFT) & + FIFO_DATA_X_Y_MASK; + y = (raw_coordinate >> Y_COORD_SHIFT) & + FIFO_DATA_X_Y_MASK; + + /* We only want to retain the 12 msb of the 16 */ + x = (x >> 4) & 0x0FFF; + y = (y >> 4) & 0x0FFF; + + /* adjust x y according to lcd tsc mount angle */ + if (priv->cfg_params.invert_x) + x = priv->cfg_params.max_x - x; + + if (priv->cfg_params.invert_y) + y = priv->cfg_params.max_y - y; + + input_report_abs(priv->idev, ABS_X, x); + input_report_abs(priv->idev, ABS_Y, y); + needs_sync = true; + + dev_dbg(&priv->pdev->dev, "xy (0x%x 0x%x)\n", x, y); + } + } + + if (needs_sync) + input_sync(priv->idev); + + return IRQ_HANDLED; +} + +static int iproc_ts_start(struct input_dev *idev) +{ + struct iproc_ts_priv *priv = input_get_drvdata(idev); + u32 val; + int error; + + /* Enable clock */ + error = clk_prepare_enable(priv->tsc_clk); + if (error) { + dev_err(&priv->pdev->dev, "%s clk_prepare_enable failed %d\n", + __func__, error); + return error; + } + + /* + * Interrupt is generated when: + * FIFO reaches the int_th value, and pen event(up/down) + */ + val = TS_PEN_INTR_MASK | TS_FIFO_INTR_MASK; + writel(val, priv->regs + INTERRUPT_MASK); + + writel(priv->cfg_params.fifo_threshold, priv->regs + INTERRUPT_THRES); + + /* Initialize control reg1 */ + val = 0; + val |= priv->cfg_params.scanning_period << SCANNING_PERIOD_SHIFT; + val |= priv->cfg_params.debounce_timeout << DEBOUNCE_TIMEOUT_SHIFT; + val |= priv->cfg_params.settling_timeout << SETTLING_TIMEOUT_SHIFT; + val |= priv->cfg_params.touch_timeout << TOUCH_TIMEOUT_SHIFT; + writel(val, priv->regs + REGCTL1); + + /* Try to clear all interrupt status */ + val = readl(priv->regs + INTERRUPT_STATUS); + val |= TS_FIFO_INTR_MASK | TS_PEN_INTR_MASK; + writel(val, priv->regs + INTERRUPT_STATUS); + + /* Initialize control reg2 */ + val = readl(priv->regs + REGCTL2); + val |= TS_CONTROLLER_EN_BIT | TS_WIRE_MODE_BIT; + + val &= ~TS_CONTROLLER_AVGDATA_MASK; + val |= priv->cfg_params.average_data << TS_CONTROLLER_AVGDATA_SHIFT; + + val &= ~(TS_CONTROLLER_PWR_LDO | /* PWR up LDO */ + TS_CONTROLLER_PWR_ADC | /* PWR up ADC */ + TS_CONTROLLER_PWR_BGP | /* PWR up BGP */ + TS_CONTROLLER_PWR_TS); /* PWR up TS */ + + writel(val, priv->regs + REGCTL2); + + ts_reg_dump(priv); + + return 0; +} + +static void iproc_ts_stop(struct input_dev *dev) +{ + u32 val; + struct iproc_ts_priv *priv = input_get_drvdata(dev); + + writel(0, priv->regs + INTERRUPT_MASK); /* Disable all interrupts */ + + /* Only power down touch screen controller */ + val = readl(priv->regs + REGCTL2); + val |= TS_CONTROLLER_PWR_TS; + writel(val, priv->regs + REGCTL2); + + clk_disable(priv->tsc_clk); +} + +static int iproc_get_tsc_config(struct device *dev, struct iproc_ts_priv *priv) +{ + struct device_node *np = dev->of_node; + u32 val; + + priv->cfg_params = iproc_default_config; + + if (!np) + return 0; + + if (of_property_read_u32(np, "scanning_period", &val) >= 0) { + if (val < 1 || val > 256) { + dev_err(dev, "scanning_period (%u) must be [1-256]\n", + val); + return -EINVAL; + } + priv->cfg_params.scanning_period = val; + } + + if (of_property_read_u32(np, "debounce_timeout", &val) >= 0) { + if (val > 255) { + dev_err(dev, "debounce_timeout (%u) must be [0-255]\n", + val); + return -EINVAL; + } + priv->cfg_params.debounce_timeout = val; + } + + if (of_property_read_u32(np, "settling_timeout", &val) >= 0) { + if (val > 11) { + dev_err(dev, "settling_timeout (%u) must be [0-11]\n", + val); + return -EINVAL; + } + priv->cfg_params.settling_timeout = val; + } + + if (of_property_read_u32(np, "touch_timeout", &val) >= 0) { + if (val > 255) { + dev_err(dev, "touch_timeout (%u) must be [0-255]\n", + val); + return -EINVAL; + } + priv->cfg_params.touch_timeout = val; + } + + if (of_property_read_u32(np, "average_data", &val) >= 0) { + if (val > 8) { + dev_err(dev, "average_data (%u) must be [0-8]\n", val); + return -EINVAL; + } + priv->cfg_params.average_data = val; + } + + if (of_property_read_u32(np, "fifo_threshold", &val) >= 0) { + if (val > 31) { + dev_err(dev, "fifo_threshold (%u)) must be [0-31]\n", + val); + return -EINVAL; + } + priv->cfg_params.fifo_threshold = val; + } + + /* Parse optional properties. */ + of_property_read_u32(np, "touchscreen-size-x", &priv->cfg_params.max_x); + of_property_read_u32(np, "touchscreen-size-y", &priv->cfg_params.max_y); + + of_property_read_u32(np, "touchscreen-fuzz-x", + &priv->cfg_params.fuzz_x); + of_property_read_u32(np, "touchscreen-fuzz-y", + &priv->cfg_params.fuzz_y); + + priv->cfg_params.invert_x = + of_property_read_bool(np, "touchscreen-inverted-x"); + priv->cfg_params.invert_y = + of_property_read_bool(np, "touchscreen-inverted-y"); + + return 0; +} + +static int iproc_ts_probe(struct platform_device *pdev) +{ + struct iproc_ts_priv *priv; + struct input_dev *idev; + struct resource *res; + int irq; + int error; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + /* touchscreen controller memory mapped regs */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->regs)) { + error = PTR_ERR(priv->regs); + dev_err(&pdev->dev, "unable to map I/O memory: %d\n", error); + return error; + } + + priv->tsc_clk = devm_clk_get(&pdev->dev, "tsc_clk"); + if (IS_ERR(priv->tsc_clk)) { + error = PTR_ERR(priv->tsc_clk); + dev_err(&pdev->dev, + "failed getting clock tsc_clk: %d\n", error); + return error; + } + + priv->pdev = pdev; + error = iproc_get_tsc_config(&pdev->dev, priv); + if (error) { + dev_err(&pdev->dev, "get_tsc_config failed: %d\n", error); + return error; + } + + idev = devm_input_allocate_device(&pdev->dev); + if (!idev) { + dev_err(&pdev->dev, "failed to allocate input device\n"); + return -ENOMEM; + } + + priv->idev = idev; + priv->pen_status = PEN_UP_STATUS; + + /* Set input device info */ + idev->name = IPROC_TS_NAME; + idev->dev.parent = &pdev->dev; + + idev->id.bustype = BUS_HOST; + idev->id.vendor = SERIO_UNKNOWN; + idev->id.product = 0; + idev->id.version = 0; + + idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + __set_bit(BTN_TOUCH, idev->keybit); + + input_set_abs_params(idev, ABS_X, X_MIN, priv->cfg_params.max_x, + priv->cfg_params.fuzz_x, 0); + input_set_abs_params(idev, ABS_Y, Y_MIN, priv->cfg_params.max_y, + priv->cfg_params.fuzz_y, 0); + + idev->open = iproc_ts_start; + idev->close = iproc_ts_stop; + + input_set_drvdata(idev, priv); + platform_set_drvdata(pdev, priv); + + /* get interrupt */ + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "platform_get_irq failed: %d\n", irq); + return irq; + } + + error = devm_request_irq(&pdev->dev, irq, + iproc_touchscreen_interrupt, + IRQF_SHARED, IPROC_TS_NAME, pdev); + if (error) + return error; + + error = input_register_device(priv->idev); + if (error) { + dev_err(&pdev->dev, + "failed to register input device: %d\n", error); + return error; + } + + return 0; +} + +static const struct of_device_id iproc_ts_of_match[] = { + {.compatible = "brcm,iproc-touchscreen", }, + { }, +}; +MODULE_DEVICE_TABLE(of, iproc_ts_of_match); + +static struct platform_driver iproc_ts_driver = { + .probe = iproc_ts_probe, + .driver = { + .name = IPROC_TS_NAME, + .of_match_table = of_match_ptr(iproc_ts_of_match), + }, +}; + +module_platform_driver(iproc_ts_driver); + +MODULE_DESCRIPTION("IPROC Touchscreen driver"); +MODULE_AUTHOR("Broadcom"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From dbea4032f8024e5ea886341f7c39cf023e30e828 Mon Sep 17 00:00:00 2001 From: Lars Poeschel Date: Tue, 24 Mar 2015 09:25:10 -0700 Subject: Input: usbtouchscreen - add new model from IRTOUCHSYSTEMS This adds support for another model of IRTOUCH SYSTEMS Co.,LtD infrared touchscreens. The USB vendorID/deviceID is 6615/0012. It is also sold under the label "Elektrosil". The datasheet states that coordinates for x and y are in the range from 0 to 32767. Signed-off-by: Lars Poeschel Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/usbtouchscreen.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/usbtouchscreen.c b/drivers/input/touchscreen/usbtouchscreen.c index a0966331a89b..f2c6c352c55a 100644 --- a/drivers/input/touchscreen/usbtouchscreen.c +++ b/drivers/input/touchscreen/usbtouchscreen.c @@ -132,6 +132,7 @@ enum { DEVTYPE_GUNZE, DEVTYPE_DMC_TSC10, DEVTYPE_IRTOUCH, + DEVTYPE_IRTOUCH_HIRES, DEVTYPE_IDEALTEK, DEVTYPE_GENERAL_TOUCH, DEVTYPE_GOTOP, @@ -198,6 +199,7 @@ static const struct usb_device_id usbtouch_devices[] = { #ifdef CONFIG_TOUCHSCREEN_USB_IRTOUCH {USB_DEVICE(0x595a, 0x0001), .driver_info = DEVTYPE_IRTOUCH}, {USB_DEVICE(0x6615, 0x0001), .driver_info = DEVTYPE_IRTOUCH}, + {USB_DEVICE(0x6615, 0x0012), .driver_info = DEVTYPE_IRTOUCH_HIRES}, #endif #ifdef CONFIG_TOUCHSCREEN_USB_IDEALTEK @@ -1177,6 +1179,15 @@ static struct usbtouch_device_info usbtouch_dev_info[] = { .rept_size = 8, .read_data = irtouch_read_data, }, + + [DEVTYPE_IRTOUCH_HIRES] = { + .min_xc = 0x0, + .max_xc = 0x7fff, + .min_yc = 0x0, + .max_yc = 0x7fff, + .rept_size = 8, + .read_data = irtouch_read_data, + }, #endif #ifdef CONFIG_TOUCHSCREEN_USB_IDEALTEK -- cgit v1.2.3 From a93ad65d375f216025902a73ff25900d82a9de25 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 21 Mar 2015 20:40:45 -0700 Subject: Input: add support for ChipOne icn8318 based touchscreens The ChipOne icn8318 is an i2c capacitive touchscreen controller typically used in cheap android tablets, this commit adds a driver for it. Signed-off-by: Hans de Goede Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/Kconfig | 13 ++ drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/chipone_icn8318.c | 316 ++++++++++++++++++++++++++++ 3 files changed, 330 insertions(+) create mode 100644 drivers/input/touchscreen/chipone_icn8318.c (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index a1cfe16e9a81..547f67d65372 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -140,6 +140,19 @@ config TOUCHSCREEN_BU21013 To compile this driver as a module, choose M here: the module will be called bu21013_ts. +config TOUCHSCREEN_CHIPONE_ICN8318 + tristate "chipone icn8318 touchscreen controller" + depends on GPIOLIB + depends on I2C + depends on OF + help + Say Y here if you have a ChipOne icn8318 based I2C touchscreen. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called chipone_icn8318. + config TOUCHSCREEN_CY8CTMG110 tristate "cy8ctmg110 touchscreen" depends on I2C diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 09c33531ab8e..44deea743d02 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -17,6 +17,7 @@ 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_CHIPONE_ICN8318) += chipone_icn8318.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/chipone_icn8318.c b/drivers/input/touchscreen/chipone_icn8318.c new file mode 100644 index 000000000000..32e9db0e04bf --- /dev/null +++ b/drivers/input/touchscreen/chipone_icn8318.c @@ -0,0 +1,316 @@ +/* + * Driver for ChipOne icn8318 i2c touchscreen controller + * + * Copyright (c) 2015 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Red Hat authors: + * Hans de Goede + */ + +#include +#include +#include +#include +#include +#include +#include + +#define ICN8318_REG_POWER 4 +#define ICN8318_REG_TOUCHDATA 16 + +#define ICN8318_POWER_ACTIVE 0 +#define ICN8318_POWER_MONITOR 1 +#define ICN8318_POWER_HIBERNATE 2 + +#define ICN8318_MAX_TOUCHES 5 + +struct icn8318_touch { + __u8 slot; + __be16 x; + __be16 y; + __u8 pressure; /* Seems more like finger width then pressure really */ + __u8 event; +/* The difference between 2 and 3 is unclear */ +#define ICN8318_EVENT_NO_DATA 1 /* No finger seen yet since wakeup */ +#define ICN8318_EVENT_UPDATE1 2 /* New or updated coordinates */ +#define ICN8318_EVENT_UPDATE2 3 /* New or updated coordinates */ +#define ICN8318_EVENT_END 4 /* Finger lifted */ +} __packed; + +struct icn8318_touch_data { + __u8 softbutton; + __u8 touch_count; + struct icn8318_touch touches[ICN8318_MAX_TOUCHES]; +} __packed; + +struct icn8318_data { + struct i2c_client *client; + struct input_dev *input; + struct gpio_desc *wake_gpio; + u32 max_x; + u32 max_y; + bool invert_x; + bool invert_y; + bool swap_x_y; +}; + +static int icn8318_read_touch_data(struct i2c_client *client, + struct icn8318_touch_data *touch_data) +{ + u8 reg = ICN8318_REG_TOUCHDATA; + struct i2c_msg msg[2] = { + { + .addr = client->addr, + .len = 1, + .buf = ® + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = sizeof(struct icn8318_touch_data), + .buf = (u8 *)touch_data + } + }; + + return i2c_transfer(client->adapter, msg, 2); +} + +static inline bool icn8318_touch_active(u8 event) +{ + return (event == ICN8318_EVENT_UPDATE1) || + (event == ICN8318_EVENT_UPDATE2); +} + +static irqreturn_t icn8318_irq(int irq, void *dev_id) +{ + struct icn8318_data *data = dev_id; + struct device *dev = &data->client->dev; + struct icn8318_touch_data touch_data; + int i, ret, x, y; + + ret = icn8318_read_touch_data(data->client, &touch_data); + if (ret < 0) { + dev_err(dev, "Error reading touch data: %d\n", ret); + return IRQ_HANDLED; + } + + if (touch_data.softbutton) { + /* + * Other data is invalid when a softbutton is pressed. + * This needs some extra devicetree bindings to map the icn8318 + * softbutton codes to evdev codes. Currently no known devices + * use this. + */ + return IRQ_HANDLED; + } + + if (touch_data.touch_count > ICN8318_MAX_TOUCHES) { + dev_warn(dev, "Too much touches %d > %d\n", + touch_data.touch_count, ICN8318_MAX_TOUCHES); + touch_data.touch_count = ICN8318_MAX_TOUCHES; + } + + for (i = 0; i < touch_data.touch_count; i++) { + struct icn8318_touch *touch = &touch_data.touches[i]; + bool act = icn8318_touch_active(touch->event); + + input_mt_slot(data->input, touch->slot); + input_mt_report_slot_state(data->input, MT_TOOL_FINGER, act); + if (!act) + continue; + + x = be16_to_cpu(touch->x); + y = be16_to_cpu(touch->y); + + if (data->invert_x) + x = data->max_x - x; + + if (data->invert_y) + y = data->max_y - y; + + if (!data->swap_x_y) { + input_event(data->input, EV_ABS, ABS_MT_POSITION_X, x); + input_event(data->input, EV_ABS, ABS_MT_POSITION_Y, y); + } else { + input_event(data->input, EV_ABS, ABS_MT_POSITION_X, y); + input_event(data->input, EV_ABS, ABS_MT_POSITION_Y, x); + } + } + + input_mt_sync_frame(data->input); + input_sync(data->input); + + return IRQ_HANDLED; +} + +static int icn8318_start(struct input_dev *dev) +{ + struct icn8318_data *data = input_get_drvdata(dev); + + enable_irq(data->client->irq); + gpiod_set_value_cansleep(data->wake_gpio, 1); + + return 0; +} + +static void icn8318_stop(struct input_dev *dev) +{ + struct icn8318_data *data = input_get_drvdata(dev); + + disable_irq(data->client->irq); + i2c_smbus_write_byte_data(data->client, ICN8318_REG_POWER, + ICN8318_POWER_HIBERNATE); + gpiod_set_value_cansleep(data->wake_gpio, 0); +} + +#ifdef CONFIG_PM_SLEEP +static int icn8318_suspend(struct device *dev) +{ + struct icn8318_data *data = i2c_get_clientdata(to_i2c_client(dev)); + + mutex_lock(&data->input->mutex); + if (data->input->users) + icn8318_stop(data->input); + mutex_unlock(&data->input->mutex); + + return 0; +} + +static int icn8318_resume(struct device *dev) +{ + struct icn8318_data *data = i2c_get_clientdata(to_i2c_client(dev)); + + mutex_lock(&data->input->mutex); + if (data->input->users) + icn8318_start(data->input); + mutex_unlock(&data->input->mutex); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(icn8318_pm_ops, icn8318_suspend, icn8318_resume); + +static int icn8318_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device_node *np = dev->of_node; + struct icn8318_data *data; + struct input_dev *input; + u32 fuzz_x = 0, fuzz_y = 0; + int error; + + if (!client->irq) { + dev_err(dev, "Error no irq specified\n"); + return -EINVAL; + } + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->wake_gpio = devm_gpiod_get(dev, "wake", GPIOD_OUT_LOW); + if (IS_ERR(data->wake_gpio)) { + error = PTR_ERR(data->wake_gpio); + if (error != -EPROBE_DEFER) + dev_err(dev, "Error getting wake gpio: %d\n", error); + return error; + } + + if (of_property_read_u32(np, "touchscreen-size-x", &data->max_x) || + of_property_read_u32(np, "touchscreen-size-y", &data->max_y)) { + dev_err(dev, "Error touchscreen-size-x and/or -y missing\n"); + return -EINVAL; + } + + /* Optional */ + of_property_read_u32(np, "touchscreen-fuzz-x", &fuzz_x); + of_property_read_u32(np, "touchscreen-fuzz-y", &fuzz_y); + data->invert_x = of_property_read_bool(np, "touchscreen-inverted-x"); + data->invert_y = of_property_read_bool(np, "touchscreen-inverted-y"); + data->swap_x_y = of_property_read_bool(np, "touchscreen-swapped-x-y"); + + input = devm_input_allocate_device(dev); + if (!input) + return -ENOMEM; + + input->name = client->name; + input->id.bustype = BUS_I2C; + input->open = icn8318_start; + input->close = icn8318_stop; + input->dev.parent = dev; + + if (!data->swap_x_y) { + input_set_abs_params(input, ABS_MT_POSITION_X, 0, + data->max_x, fuzz_x, 0); + input_set_abs_params(input, ABS_MT_POSITION_Y, 0, + data->max_y, fuzz_y, 0); + } else { + input_set_abs_params(input, ABS_MT_POSITION_X, 0, + data->max_y, fuzz_y, 0); + input_set_abs_params(input, ABS_MT_POSITION_Y, 0, + data->max_x, fuzz_x, 0); + } + + error = input_mt_init_slots(input, ICN8318_MAX_TOUCHES, + INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED); + if (error) + return error; + + data->client = client; + data->input = input; + input_set_drvdata(input, data); + + error = devm_request_threaded_irq(dev, client->irq, NULL, icn8318_irq, + IRQF_ONESHOT, client->name, data); + if (error) { + dev_err(dev, "Error requesting irq: %d\n", error); + return error; + } + + /* Stop device till opened */ + icn8318_stop(data->input); + + error = input_register_device(input); + if (error) + return error; + + i2c_set_clientdata(client, data); + + return 0; +} + +static const struct of_device_id icn8318_of_match[] = { + { .compatible = "chipone,icn8318" }, + { } +}; +MODULE_DEVICE_TABLE(of, icn8318_of_match); + +/* This is useless for OF-enabled devices, but it is needed by I2C subsystem */ +static const struct i2c_device_id icn8318_i2c_id[] = { + { }, +}; +MODULE_DEVICE_TABLE(i2c, icn8318_i2c_id); + +static struct i2c_driver icn8318_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "chipone_icn8318", + .pm = &icn8318_pm_ops, + .of_match_table = icn8318_of_match, + }, + .probe = icn8318_probe, + .id_table = icn8318_i2c_id, +}; + +module_i2c_driver(icn8318_driver); + +MODULE_DESCRIPTION("ChipOne icn8318 I2C Touchscreen Driver"); +MODULE_AUTHOR("Hans de Goede "); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 5037a17966967bde3f561cc543c9b3596afabf70 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 25 Feb 2015 17:44:55 -0800 Subject: Input: mma8450 - convert to using managed resources This simplifies error handling and device removal code. Also let's get rid of setting driver's owner since i2c core does it for us. Tested-by: Stefan Sauer Signed-off-by: Dmitry Torokhov --- drivers/input/misc/mma8450.c | 35 ++++++++--------------------------- 1 file changed, 8 insertions(+), 27 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/misc/mma8450.c b/drivers/input/misc/mma8450.c index 98228773a111..19c73574458e 100644 --- a/drivers/input/misc/mma8450.c +++ b/drivers/input/misc/mma8450.c @@ -174,12 +174,13 @@ static int mma8450_probe(struct i2c_client *c, struct mma8450 *m; int err; - m = kzalloc(sizeof(struct mma8450), GFP_KERNEL); - idev = input_allocate_polled_device(); - if (!m || !idev) { - err = -ENOMEM; - goto err_free_mem; - } + m = devm_kzalloc(&c->dev, sizeof(*m), GFP_KERNEL); + if (!m) + return -ENOMEM; + + idev = devm_input_allocate_polled_device(&c->dev); + if (!idev) + return -ENOMEM; m->client = c; m->idev = idev; @@ -187,7 +188,6 @@ static int mma8450_probe(struct i2c_client *c, idev->private = m; idev->input->name = MMA8450_DRV_NAME; idev->input->id.bustype = BUS_I2C; - idev->input->dev.parent = &c->dev; idev->poll = mma8450_poll; idev->poll_interval = POLL_INTERVAL; idev->poll_interval_max = POLL_INTERVAL_MAX; @@ -202,28 +202,11 @@ static int mma8450_probe(struct i2c_client *c, err = input_register_polled_device(idev); if (err) { dev_err(&c->dev, "failed to register polled input device\n"); - goto err_free_mem; + return err; } i2c_set_clientdata(c, m); - return 0; - -err_free_mem: - input_free_polled_device(idev); - kfree(m); - return err; -} - -static int mma8450_remove(struct i2c_client *c) -{ - struct mma8450 *m = i2c_get_clientdata(c); - struct input_polled_dev *idev = m->idev; - - input_unregister_polled_device(idev); - input_free_polled_device(idev); - kfree(m); - return 0; } @@ -242,11 +225,9 @@ MODULE_DEVICE_TABLE(of, mma8450_dt_ids); static struct i2c_driver mma8450_driver = { .driver = { .name = MMA8450_DRV_NAME, - .owner = THIS_MODULE, .of_match_table = mma8450_dt_ids, }, .probe = mma8450_probe, - .remove = mma8450_remove, .id_table = mma8450_id, }; -- cgit v1.2.3 From 3eea8b5d68c801fec788b411582b803463834752 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Sat, 21 Mar 2015 20:17:48 -0700 Subject: Input: of_touchscreen - rework the DT parsing function The DT parsing function currently duplicates a lot of the code to parse the touchscreen DT properties. In order to ease further additions to this parsing routine, rework it slightly to create new helper functions. Signed-off-by: Maxime Ripard Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/of_touchscreen.c | 50 ++++++++++++++++++++++++------ 1 file changed, 40 insertions(+), 10 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/of_touchscreen.c b/drivers/input/touchscreen/of_touchscreen.c index f8f9b84230b1..fe80d8ba7efa 100644 --- a/drivers/input/touchscreen/of_touchscreen.c +++ b/drivers/input/touchscreen/of_touchscreen.c @@ -13,6 +13,33 @@ #include #include +static u32 of_get_optional_u32(struct device_node *np, + const char *property) +{ + u32 val = 0; + + of_property_read_u32(np, property, &val); + + return val; +} + +static void touchscreen_set_params(struct input_dev *dev, + unsigned long axis, + int max, int fuzz) +{ + struct input_absinfo *absinfo; + + if (!test_bit(axis, dev->absbit)) { + dev_warn(&dev->dev, + "DT specifies parameters but the axis is not set up\n"); + return; + } + + absinfo = &dev->absinfo[axis]; + absinfo->maximum = max; + absinfo->fuzz = fuzz; +} + /** * touchscreen_parse_of_params - parse common touchscreen DT properties * @dev: device that should be parsed @@ -24,22 +51,25 @@ void touchscreen_parse_of_params(struct input_dev *dev) { struct device_node *np = dev->dev.parent->of_node; - struct input_absinfo *absinfo; + u32 maximum, fuzz; input_alloc_absinfo(dev); if (!dev->absinfo) return; - absinfo = &dev->absinfo[ABS_X]; - of_property_read_u32(np, "touchscreen-size-x", &absinfo->maximum); - of_property_read_u32(np, "touchscreen-fuzz-x", &absinfo->fuzz); + maximum = of_get_optional_u32(np, "touchscreen-size-x"); + fuzz = of_get_optional_u32(np, "touchscreen-fuzz-x"); + if (maximum || fuzz) + touchscreen_set_params(dev, ABS_X, maximum, fuzz); - absinfo = &dev->absinfo[ABS_Y]; - of_property_read_u32(np, "touchscreen-size-y", &absinfo->maximum); - of_property_read_u32(np, "touchscreen-fuzz-y", &absinfo->fuzz); + maximum = of_get_optional_u32(np, "touchscreen-size-y"); + fuzz = of_get_optional_u32(np, "touchscreen-fuzz-y"); + if (maximum || fuzz) + touchscreen_set_params(dev, ABS_Y, maximum, fuzz); - absinfo = &dev->absinfo[ABS_PRESSURE]; - of_property_read_u32(np, "touchscreen-max-pressure", &absinfo->maximum); - of_property_read_u32(np, "touchscreen-fuzz-pressure", &absinfo->fuzz); + maximum = of_get_optional_u32(np, "touchscreen-max-pressure"); + fuzz = of_get_optional_u32(np, "touchscreen-fuzz-pressure"); + if (maximum || fuzz) + touchscreen_set_params(dev, ABS_PRESSURE, maximum, fuzz); } EXPORT_SYMBOL(touchscreen_parse_of_params); -- cgit v1.2.3 From 0a363a380954e10fece7cd9931b66056eeb07d56 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Sat, 21 Mar 2015 20:17:57 -0700 Subject: Input: of_touchscreen - register multitouch axes So far, the DT parsing code was only setting up the regular input axes, completely ignoring their multitouch counter parts. Fill them with the same parameters than the regular axes. Signed-off-by: Maxime Ripard Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/of_touchscreen.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/of_touchscreen.c b/drivers/input/touchscreen/of_touchscreen.c index fe80d8ba7efa..b82b5207c78b 100644 --- a/drivers/input/touchscreen/of_touchscreen.c +++ b/drivers/input/touchscreen/of_touchscreen.c @@ -11,6 +11,7 @@ #include #include +#include #include static u32 of_get_optional_u32(struct device_node *np, @@ -30,8 +31,13 @@ static void touchscreen_set_params(struct input_dev *dev, struct input_absinfo *absinfo; if (!test_bit(axis, dev->absbit)) { - dev_warn(&dev->dev, - "DT specifies parameters but the axis is not set up\n"); + /* + * Emit a warning only if the axis is not a multitouch + * axis, which might not be set by the driver. + */ + if (!input_is_mt_axis(axis)) + dev_warn(&dev->dev, + "DT specifies parameters but the axis is not set up\n"); return; } @@ -59,17 +65,23 @@ void touchscreen_parse_of_params(struct input_dev *dev) maximum = of_get_optional_u32(np, "touchscreen-size-x"); fuzz = of_get_optional_u32(np, "touchscreen-fuzz-x"); - if (maximum || fuzz) + if (maximum || fuzz) { touchscreen_set_params(dev, ABS_X, maximum, fuzz); + touchscreen_set_params(dev, ABS_MT_POSITION_X, maximum, fuzz); + } maximum = of_get_optional_u32(np, "touchscreen-size-y"); fuzz = of_get_optional_u32(np, "touchscreen-fuzz-y"); - if (maximum || fuzz) + if (maximum || fuzz) { touchscreen_set_params(dev, ABS_Y, maximum, fuzz); + touchscreen_set_params(dev, ABS_MT_POSITION_Y, maximum, fuzz); + } maximum = of_get_optional_u32(np, "touchscreen-max-pressure"); fuzz = of_get_optional_u32(np, "touchscreen-fuzz-pressure"); - if (maximum || fuzz) + if (maximum || fuzz) { touchscreen_set_params(dev, ABS_PRESSURE, maximum, fuzz); + touchscreen_set_params(dev, ABS_MT_PRESSURE, maximum, fuzz); + } } EXPORT_SYMBOL(touchscreen_parse_of_params); -- cgit v1.2.3 From 2c00559884e09398a6451672472b4ea7f4118291 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Sat, 21 Mar 2015 20:18:06 -0700 Subject: Input: edt-ft5x06 - allow to setting the maximum axes value through the DT Currently the driver relies on some obscure and undocumented register to set the maximum axis value. The reported value is way too high to be meaningful, which confuses some userspace tools like QT's evdevtouch plugin which try to scale the reported events to the maximum values. Use the values from the DT to set meaningful values. Signed-off-by: Maxime Ripard Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/edt-ft5x06.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index d4c24fb7704f..cd8a7472266b 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #define MAX_SUPPORT_POINTS 5 @@ -1044,6 +1045,10 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, 0, tsdata->num_x * 64 - 1, 0, 0); input_set_abs_params(input, ABS_MT_POSITION_Y, 0, tsdata->num_y * 64 - 1, 0, 0); + + if (!pdata) + touchscreen_parse_of_params(input); + error = input_mt_init_slots(input, MAX_SUPPORT_POINTS, 0); if (error) { dev_err(&client->dev, "Unable to init MT slots.\n"); -- cgit v1.2.3 From 709583e894afad7f33add66953026ceb82cdf115 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Sat, 21 Mar 2015 20:18:59 -0700 Subject: Input: edt-ft5x06 - remove EV_SYN event report input_register_device already sets the EV_SYN event since all devices can generate them. Remove the redundant affectation. Signed-off-by: Maxime Ripard Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/edt-ft5x06.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index cd8a7472266b..e6aef3e48bd9 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -1035,7 +1035,6 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, input->id.bustype = BUS_I2C; input->dev.parent = &client->dev; - __set_bit(EV_SYN, input->evbit); __set_bit(EV_KEY, input->evbit); __set_bit(EV_ABS, input->evbit); __set_bit(BTN_TOUCH, input->keybit); -- cgit v1.2.3 From 1422731dbc14063fc7083cb090108c0d95267361 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 19 Mar 2015 15:52:45 +0100 Subject: mfd: tc3589x: Enforce device-tree only mode All systems using the TC3589x multifunction expander uses devicetree, so don't clutter the place with a lot of and assume it is there. Signed-off-by: Linus Walleij Signed-off-by: Lee Jones --- drivers/input/keyboard/tc3589x-keypad.c | 9 --------- 1 file changed, 9 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/tc3589x-keypad.c b/drivers/input/keyboard/tc3589x-keypad.c index 8ff612d160b0..aa695a80ec32 100644 --- a/drivers/input/keyboard/tc3589x-keypad.c +++ b/drivers/input/keyboard/tc3589x-keypad.c @@ -296,7 +296,6 @@ static void tc3589x_keypad_close(struct input_dev *input) tc3589x_keypad_disable(keypad); } -#ifdef CONFIG_OF static const struct tc3589x_keypad_platform_data * tc3589x_keypad_of_probe(struct device *dev) { @@ -346,14 +345,6 @@ tc3589x_keypad_of_probe(struct device *dev) return plat; } -#else -static inline const struct tc3589x_keypad_platform_data * -tc3589x_keypad_of_probe(struct device *dev) -{ - return ERR_PTR(-ENODEV); -} -#endif - static int tc3589x_keypad_probe(struct platform_device *pdev) { -- cgit v1.2.3 From bbdb38a22e8a5197a6db1e4d22d7e5c6c368e7f7 Mon Sep 17 00:00:00 2001 From: Aaron Sierra Date: Tue, 31 Mar 2015 14:35:36 -0700 Subject: Input: tsc2007 - Convert msecs to jiffies only once Eliminate redundant calculations by performing millisecond to jiffy calculations once during driver initialization. Signed-off-by: Aaron Sierra Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/tsc2007.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/tsc2007.c b/drivers/input/touchscreen/tsc2007.c index 1bf9906b5a3f..ccc8aa615709 100644 --- a/drivers/input/touchscreen/tsc2007.c +++ b/drivers/input/touchscreen/tsc2007.c @@ -75,7 +75,7 @@ struct tsc2007 { u16 model; u16 x_plate_ohms; u16 max_rt; - unsigned long poll_period; + unsigned long poll_period; /* in jiffies */ int fuzzx; int fuzzy; int fuzzz; @@ -214,8 +214,7 @@ static irqreturn_t tsc2007_soft_irq(int irq, void *handle) dev_dbg(&ts->client->dev, "ignored pressure %d\n", rt); } - wait_event_timeout(ts->wait, ts->stopped, - msecs_to_jiffies(ts->poll_period)); + wait_event_timeout(ts->wait, ts->stopped, ts->poll_period); } dev_dbg(&ts->client->dev, "UP\n"); @@ -314,9 +313,9 @@ static int tsc2007_probe_dt(struct i2c_client *client, struct tsc2007 *ts) ts->fuzzz = val32; if (!of_property_read_u64(np, "ti,poll-period", &val64)) - ts->poll_period = val64; + ts->poll_period = msecs_to_jiffies(val64); else - ts->poll_period = 1; + ts->poll_period = msecs_to_jiffies(1); if (!of_property_read_u32(np, "ti,x-plate-ohms", &val32)) { ts->x_plate_ohms = val32; @@ -350,7 +349,7 @@ static int tsc2007_probe_pdev(struct i2c_client *client, struct tsc2007 *ts, ts->model = pdata->model; ts->x_plate_ohms = pdata->x_plate_ohms; ts->max_rt = pdata->max_rt ? : MAX_12BIT; - ts->poll_period = pdata->poll_period ? : 1; + ts->poll_period = msecs_to_jiffies(pdata->poll_period ? : 1); ts->get_pendown_state = pdata->get_pendown_state; ts->clear_penirq = pdata->clear_penirq; ts->fuzzx = pdata->fuzzx; -- cgit v1.2.3 From 85734b1a5de80341d7d01cd29bbea9cf6f77094b Mon Sep 17 00:00:00 2001 From: Filip Ayazi Date: Wed, 25 Mar 2015 15:53:04 -0700 Subject: Input: synaptics - fix min-max quirk value for E440 Commit 98dc070373 ("Input: synaptics - add quirk for Thinkpad E440") had a typo in ymax, this changes the value to the one reported by touchpad-edge-detector and mentioned in the commit. Signed-off-by: Filip Ayazi Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/synaptics.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index f6a3a7b7d1ad..3b06c8a360b6 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -155,7 +155,7 @@ static const struct min_max_quirk min_max_pnpid_table[] = { { (const char * const []){"LEN2006", NULL}, {2691, 2691}, - 1024, 5045, 2457, 4632 + 1024, 5045, 2457, 4832 }, { (const char * const []){"LEN2006", NULL}, -- cgit v1.2.3 From 8b6f53c2f6d3d04c077e74fdb07130242a2d6f58 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 27 Mar 2015 09:28:16 -0700 Subject: Input: tc3589x - localize platform data This driver can only get its platform data from the device tree, and all platforms using it do that. Localize the platform data for the keypad. A later patch will enforce the device tree / OF dependence. Signed-off-by: Linus Walleij Acked-by: Lee Jones Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/tc3589x-keypad.c | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/tc3589x-keypad.c b/drivers/input/keyboard/tc3589x-keypad.c index 563932500ff1..ae90df3468e0 100644 --- a/drivers/input/keyboard/tc3589x-keypad.c +++ b/drivers/input/keyboard/tc3589x-keypad.c @@ -69,6 +69,28 @@ #define TC3589x_EVT_INT_CLR 0x2 #define TC3589x_KBD_INT_CLR 0x1 +/** + * struct tc35893_keypad_platform_data - platform specific keypad data + * @keymap_data: matrix scan code table for keycodes + * @krow: mask for available rows, value is 0xFF + * @kcol: mask for available columns, value is 0xFF + * @debounce_period: platform specific debounce time + * @settle_time: platform specific settle down time + * @irqtype: type of interrupt, falling or rising edge + * @enable_wakeup: specifies if keypad event can wake up system from sleep + * @no_autorepeat: flag for auto repetition + */ +struct tc3589x_keypad_platform_data { + const struct matrix_keymap_data *keymap_data; + u8 krow; + u8 kcol; + u8 debounce_period; + u8 settle_time; + unsigned long irqtype; + bool enable_wakeup; + bool no_autorepeat; +}; + /** * struct tc_keypad - data structure used by keypad driver * @tc3589x: pointer to tc35893 @@ -363,13 +385,10 @@ static int tc3589x_keypad_probe(struct platform_device *pdev) const struct tc3589x_keypad_platform_data *plat; int error, irq; - plat = tc3589x->pdata->keypad; - if (!plat) { - plat = tc3589x_keypad_of_probe(&pdev->dev); - if (IS_ERR(plat)) { - dev_err(&pdev->dev, "invalid keypad platform data\n"); - return PTR_ERR(plat); - } + plat = tc3589x_keypad_of_probe(&pdev->dev); + if (IS_ERR(plat)) { + dev_err(&pdev->dev, "invalid keypad platform data\n"); + return PTR_ERR(plat); } irq = platform_get_irq(pdev, 0); -- cgit v1.2.3 From 61125591e1de23992279938eebf8c54f47d7e0a9 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 27 Mar 2015 09:32:06 -0700 Subject: mfd: tc3589x: enforce device-tree only mode All systems using the TC3589x multifunction expander uses devicetree, so don't clutter the place with a lot of and assume it is there. Signed-off-by: Linus Walleij Acked-by: Lee Jones Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/tc3589x-keypad.c | 9 --------- 1 file changed, 9 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/tc3589x-keypad.c b/drivers/input/keyboard/tc3589x-keypad.c index ae90df3468e0..31c606a4dd31 100644 --- a/drivers/input/keyboard/tc3589x-keypad.c +++ b/drivers/input/keyboard/tc3589x-keypad.c @@ -318,7 +318,6 @@ static void tc3589x_keypad_close(struct input_dev *input) tc3589x_keypad_disable(keypad); } -#ifdef CONFIG_OF static const struct tc3589x_keypad_platform_data * tc3589x_keypad_of_probe(struct device *dev) { @@ -368,14 +367,6 @@ tc3589x_keypad_of_probe(struct device *dev) return plat; } -#else -static inline const struct tc3589x_keypad_platform_data * -tc3589x_keypad_of_probe(struct device *dev) -{ - return ERR_PTR(-ENODEV); -} -#endif - static int tc3589x_keypad_probe(struct platform_device *pdev) { -- cgit v1.2.3 From 59c30afbd37c26168597e737297a1de68848c332 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 3 Apr 2015 17:14:40 -0700 Subject: Input: alps - report interleaved bare PS/2 packets via dev3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bare packets should be reported via the same evdev device independent on whether they are detected on the beginning of a packet or in the middle of a packet. This has been tested on a Dell Latitude E6400, where the DualPoint Stick reports bare packets, which get reported via dev3 when the touchpad is idle, and via dev2 when the touchpad and stick are used simultaneously. This commit fixes this inconsistency by always reporting bare packets via dev3. Note that since the come from a DualPoint Stick they really should be reported via dev2, this gets fixed in a later commit. Signed-off-by: Hans de Goede Reviewed-by: Pali Rohár Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/alps.c | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index 33198b91bebf..e32625ccf8b6 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -1154,10 +1154,23 @@ out: mutex_unlock(&alps_mutex); } -static void alps_report_bare_ps2_packet(struct input_dev *dev, +static void alps_report_bare_ps2_packet(struct psmouse *psmouse, unsigned char packet[], bool report_buttons) { + struct alps_data *priv = psmouse->private; + struct input_dev *dev; + + if (unlikely(IS_ERR_OR_NULL(priv->dev3))) { + /* Register dev3 mouse if we received PS/2 packet first time */ + if (!IS_ERR(priv->dev3)) + psmouse_queue_work(psmouse, &priv->dev3_register_work, + 0); + return; + } else { + dev = priv->dev3; + } + if (report_buttons) alps_report_buttons(dev, NULL, packet[0] & 1, packet[0] & 2, packet[0] & 4); @@ -1232,8 +1245,8 @@ static psmouse_ret_t alps_handle_interleaved_ps2(struct psmouse *psmouse) * de-synchronization. */ - alps_report_bare_ps2_packet(priv->dev2, - &psmouse->packet[3], false); + alps_report_bare_ps2_packet(psmouse, &psmouse->packet[3], + false); /* * Continue with the standard ALPS protocol handling, @@ -1289,18 +1302,9 @@ static psmouse_ret_t alps_process_byte(struct psmouse *psmouse) * properly we only do this if the device is fully synchronized. */ if (!psmouse->out_of_sync_cnt && (psmouse->packet[0] & 0xc8) == 0x08) { - - /* Register dev3 mouse if we received PS/2 packet first time */ - if (unlikely(!priv->dev3)) - psmouse_queue_work(psmouse, - &priv->dev3_register_work, 0); - if (psmouse->pktcnt == 3) { - /* Once dev3 mouse device is registered report data */ - if (likely(!IS_ERR_OR_NULL(priv->dev3))) - alps_report_bare_ps2_packet(priv->dev3, - psmouse->packet, - true); + alps_report_bare_ps2_packet(psmouse, psmouse->packet, + true); return PSMOUSE_FULL_PACKET; } return PSMOUSE_GOOD_DATA; -- cgit v1.2.3 From e3a79212eae6eb64ed68c78409778f8d1a84c2a1 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 3 Apr 2015 17:20:05 -0700 Subject: Input: alps - report V2 Dualpoint Stick events via the right evdev node MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On V2 devices the DualPoint Stick reports bare packets, these should be reported via the "AlpsPS/2 ALPS DualPoint Stick" dev2 evdev node, which also has the INPUT_PROP_POINTING_STICK propbit set. Note that since there is no way to distinguish these packets from an external PS/2 mouse (insofar as these laptops have an external PS/2 port) this means that we will be reporting PS/2 mouse events via this evdev node too, as we've been doing in kernel 3.19 and older. This has been tested on a Dell Latitude D620 and a Dell Latitude E6400, which both have a V2 touchpad + a DualPoint Stick which reports bare packets. Signed-off-by: Hans de Goede Reviewed-by: Pali Rohár Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/alps.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index e32625ccf8b6..27bcdbc950c9 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -1161,7 +1161,12 @@ static void alps_report_bare_ps2_packet(struct psmouse *psmouse, struct alps_data *priv = psmouse->private; struct input_dev *dev; - if (unlikely(IS_ERR_OR_NULL(priv->dev3))) { + /* Figure out which device to use to report the bare packet */ + if (priv->proto_version == ALPS_PROTO_V2 && + (priv->flags & ALPS_DUALPOINT)) { + /* On V2 devices the DualPoint Stick reports bare packets */ + dev = priv->dev2; + } else if (unlikely(IS_ERR_OR_NULL(priv->dev3))) { /* Register dev3 mouse if we received PS/2 packet first time */ if (!IS_ERR(priv->dev3)) psmouse_queue_work(psmouse, &priv->dev3_register_work, -- cgit v1.2.3 From 73e8a8e777d8dde6a67df1cf4b18371e27d080d8 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Sun, 5 Apr 2015 13:41:35 -0700 Subject: Input: MT - make slot assignment work for overcovered solutions The recent inclusion of a deassignment cost in the slot assignment algorithm did not properly account for the corner cases where the solutions are overcovered. This change makes sure the resulting assignment is unique, allocating new slots when necessary. Signed-off-by: Henrik Rydberg Signed-off-by: Benjamin Tissoires Acked-by: Hans de Goede Signed-off-by: Dmitry Torokhov --- drivers/input/input-mt.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/input-mt.c b/drivers/input/input-mt.c index cb150a1dbaff..b097af269e00 100644 --- a/drivers/input/input-mt.c +++ b/drivers/input/input-mt.c @@ -365,27 +365,35 @@ static void input_mt_set_slots(struct input_mt *mt, int *slots, int num_pos) { struct input_mt_slot *s; - int *w = mt->red, *p; + int *w = mt->red, j; - for (p = slots; p != slots + num_pos; p++) - *p = -1; + for (j = 0; j != num_pos; j++) + slots[j] = -1; for (s = mt->slots; s != mt->slots + mt->num_slots; s++) { if (!input_mt_is_active(s)) continue; - for (p = slots; p != slots + num_pos; p++) - if (*w++ < 0) - *p = s - mt->slots; + + for (j = 0; j != num_pos; j++) { + if (w[j] < 0) { + slots[j] = s - mt->slots; + break; + } + } + + w += num_pos; } for (s = mt->slots; s != mt->slots + mt->num_slots; s++) { if (input_mt_is_active(s)) continue; - for (p = slots; p != slots + num_pos; p++) - if (*p < 0) { - *p = s - mt->slots; + + for (j = 0; j != num_pos; j++) { + if (slots[j] < 0) { + slots[j] = s - mt->slots; break; } + } } } -- cgit v1.2.3 From 58fd9af6e1ccabec6bc799b32eea22a82103d5ae Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Sun, 5 Apr 2015 13:44:12 -0700 Subject: Input: Revert "Revert "synaptics - use dmax in input_mt_assign_slots"" This reverts commit 09d042a2eb90 ("Revert "Input: synaptics - use dmax in input_mt_assign_slots"") Now that balanced slots assignments seem to be fixed, let's re-enable the use in synaptics.c and wait for users to complain if there are still problems. Signed-off-by: Benjamin Tissoires Acked-by: Hans de Goede Acked-by: Henrik Rydberg --- drivers/input/mouse/synaptics.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index d73a94270211..3c54b9b0f65f 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -67,6 +67,9 @@ #define X_MAX_POSITIVE 8176 #define Y_MAX_POSITIVE 8176 +/* maximum ABS_MT_POSITION displacement (in mm) */ +#define DMAX 10 + /***************************************************************************** * Stuff we need even when we do not want native Synaptics support ****************************************************************************/ @@ -917,7 +920,7 @@ static void synaptics_report_mt_data(struct psmouse *psmouse, pos[i].y = synaptics_invert_y(hw[i]->y); } - input_mt_assign_slots(dev, slot, pos, nsemi, 0); + input_mt_assign_slots(dev, slot, pos, nsemi, DMAX * priv->x_res); for (i = 0; i < nsemi; i++) { input_mt_slot(dev, slot[i]); -- cgit v1.2.3 From 63c4fda3c0bb841b1aad1298fc7fe94058fc79f8 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Sun, 5 Apr 2015 13:45:04 -0700 Subject: Input: synaptics - allocate 3 slots to keep stability in image sensors When slowly dropping 1, 2 and then 3 fingers on an image sensor touchpad, we can see that the first finger gets reassigned a new slot while it did not move. This is due to the kernel tracking algorithm which can not assign correctly the 3 touches, being out of slots. Declaring that we support 3 slots allows to actually forward: slot 0 -> down, slot 1 -> up, slot 2 -> down Signed-off-by: Benjamin Tissoires Acked-by: Hans de Goede Acked-by: Henrik Rydberg --- drivers/input/mouse/synaptics.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 3c54b9b0f65f..95898333834f 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -1189,7 +1189,7 @@ static void set_input_params(struct psmouse *psmouse, ABS_MT_POSITION_Y); /* Image sensors can report per-contact pressure */ input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 255, 0, 0); - input_mt_init_slots(dev, 2, INPUT_MT_POINTER | INPUT_MT_TRACK); + input_mt_init_slots(dev, 3, INPUT_MT_POINTER | INPUT_MT_TRACK); /* Image sensors can signal 4 and 5 finger clicks */ __set_bit(BTN_TOOL_QUADTAP, dev->keybit); -- cgit v1.2.3 From a54aecbd3ad08f6a5e9546a3602af7d88cc5a613 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Mon, 6 Apr 2015 09:23:30 -0700 Subject: Input: gscps2 - drop pci_ids dependency This driver does not use any PCI IDs, don't include the pci_ids.h header. Signed-off-by: Michael S. Tsirkin Signed-off-by: Dmitry Torokhov --- drivers/input/serio/gscps2.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/serio/gscps2.c b/drivers/input/serio/gscps2.c index 94ab494a6ade..ecba666afadb 100644 --- a/drivers/input/serio/gscps2.c +++ b/drivers/input/serio/gscps2.c @@ -31,7 +31,6 @@ #include #include #include -#include #include #include -- cgit v1.2.3 From ef30a406468a3eae007210ae0dc5ed8d5eb59b7d Mon Sep 17 00:00:00 2001 From: Tomeu Vizoso Date: Mon, 6 Apr 2015 09:44:59 -0700 Subject: Input: cros_ec_keyb - fix clearing keyboard state on wakeup As the comment right before explains, the keyboard state is to be cleared only if the EC wasn't a wakeup source in the last suspend. Without this commit, there's an unneeded delay when resuming from suspend and we also lose the key that was pressed while suspended. Signed-off-by: Tomeu Vizoso Reviewed-by: Javier Martinez Canillas Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/cros_ec_keyb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c index ffa989f2c785..64b9b59ad4cb 100644 --- a/drivers/input/keyboard/cros_ec_keyb.c +++ b/drivers/input/keyboard/cros_ec_keyb.c @@ -338,7 +338,7 @@ static int cros_ec_keyb_resume(struct device *dev) * wake source (e.g. the lid is open and the user might press a key to * wake) then the key scan buffer should be preserved. */ - if (ckdev->ec->was_wake_device) + if (!ckdev->ec->was_wake_device) cros_ec_keyb_clear_keyboard(ckdev); return 0; -- cgit v1.2.3 From b23157dc74272ac8ebffd1a566e3e822dbc3e65f Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Mon, 6 Apr 2015 11:25:13 -0700 Subject: Input: atmel_mxt_ts - implement support for T100 touch object Add support for the new T100 object which replaces the previous T9 multitouch touchscreen object in recent maXTouch devices. T100 provides improved reporting with selectable auxiliary information, and a type field for hover/stylus/glove reporting. The hovering finger support was based on Chung-Yih's work in the ChromiumOS downstream kernel: https://chromium-review.googlesource.com/#/c/219280/ Signed-off-by: Nick Dyer Acked-by: Yufeng Shen [javier: Factor out T9 and T100 init functions and rework hover support] Signed-off-by: Javier Martinez Canillas Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/atmel_mxt_ts.c | 345 ++++++++++++++++++++++++++++--- 1 file changed, 318 insertions(+), 27 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 95ee92a91bd2..1b3b845d92f4 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -25,6 +25,7 @@ #include #include #include +#include /* Version */ #define MXT_VER_20 20 @@ -79,6 +80,7 @@ #define MXT_SPT_DIGITIZER_T43 43 #define MXT_SPT_MESSAGECOUNT_T44 44 #define MXT_SPT_CTECONFIG_T46 46 +#define MXT_TOUCH_MULTITOUCHSCREEN_T100 100 /* MXT_GEN_MESSAGE_T5 object */ #define MXT_RPTID_NOMSG 0xff @@ -185,6 +187,36 @@ struct t9_range { #define MXT_RESET_VALUE 0x01 #define MXT_BACKUP_VALUE 0x55 +/* T100 Multiple Touch Touchscreen */ +#define MXT_T100_CTRL 0 +#define MXT_T100_CFG1 1 +#define MXT_T100_TCHAUX 3 +#define MXT_T100_XRANGE 13 +#define MXT_T100_YRANGE 24 + +#define MXT_T100_CFG_SWITCHXY BIT(5) + +#define MXT_T100_TCHAUX_VECT BIT(0) +#define MXT_T100_TCHAUX_AMPL BIT(1) +#define MXT_T100_TCHAUX_AREA BIT(2) + +#define MXT_T100_DETECT BIT(7) +#define MXT_T100_TYPE_MASK 0x70 + +enum t100_type { + MXT_T100_TYPE_FINGER = 1, + MXT_T100_TYPE_PASSIVE_STYLUS = 2, + MXT_T100_TYPE_HOVERING_FINGER = 4, + MXT_T100_TYPE_GLOVE = 5, + MXT_T100_TYPE_LARGE_TOUCH = 6, +}; + +#define MXT_DISTANCE_ACTIVE_TOUCH 0 +#define MXT_DISTANCE_HOVERING 1 + +#define MXT_TOUCH_MAJOR_DEFAULT 1 +#define MXT_PRESSURE_DEFAULT 1 + /* Delay times */ #define MXT_BACKUP_TIME 50 /* msec */ #define MXT_RESET_TIME 200 /* msec */ @@ -244,6 +276,9 @@ struct mxt_data { unsigned int max_y; bool in_bootloader; u16 mem_size; + u8 t100_aux_ampl; + u8 t100_aux_area; + u8 t100_aux_vect; u8 max_reportid; u32 config_crc; u32 info_crc; @@ -253,6 +288,7 @@ struct mxt_data { bool update_input; u8 last_message_count; u8 num_touchids; + u8 multitouch; /* Cached parameters from object table */ u16 T5_address; @@ -264,6 +300,8 @@ struct mxt_data { u8 T9_reportid_max; u8 T19_reportid; u16 T44_address; + u8 T100_reportid_min; + u8 T100_reportid_max; /* for fw update in bootloader */ struct completion bl_completion; @@ -771,6 +809,114 @@ static void mxt_proc_t9_message(struct mxt_data *data, u8 *message) data->update_input = true; } +static void mxt_proc_t100_message(struct mxt_data *data, u8 *message) +{ + struct device *dev = &data->client->dev; + struct input_dev *input_dev = data->input_dev; + int id; + u8 status; + u8 type = 0; + u16 x; + u16 y; + int distance = 0; + int tool = 0; + u8 major = 0; + u8 pressure = 0; + u8 orientation = 0; + + id = message[0] - data->T100_reportid_min - 2; + + /* ignore SCRSTATUS events */ + if (id < 0) + return; + + status = message[1]; + x = get_unaligned_le16(&message[2]); + y = get_unaligned_le16(&message[4]); + + if (status & MXT_T100_DETECT) { + type = (status & MXT_T100_TYPE_MASK) >> 4; + + switch (type) { + case MXT_T100_TYPE_HOVERING_FINGER: + tool = MT_TOOL_FINGER; + distance = MXT_DISTANCE_HOVERING; + + if (data->t100_aux_vect) + orientation = message[data->t100_aux_vect]; + + break; + + case MXT_T100_TYPE_FINGER: + case MXT_T100_TYPE_GLOVE: + tool = MT_TOOL_FINGER; + distance = MXT_DISTANCE_ACTIVE_TOUCH; + + if (data->t100_aux_area) + major = message[data->t100_aux_area]; + + if (data->t100_aux_ampl) + pressure = message[data->t100_aux_ampl]; + + if (data->t100_aux_vect) + orientation = message[data->t100_aux_vect]; + + break; + + case MXT_T100_TYPE_PASSIVE_STYLUS: + tool = MT_TOOL_PEN; + + /* + * Passive stylus is reported with size zero so + * hardcode. + */ + major = MXT_TOUCH_MAJOR_DEFAULT; + + if (data->t100_aux_ampl) + pressure = message[data->t100_aux_ampl]; + + break; + + case MXT_T100_TYPE_LARGE_TOUCH: + /* Ignore suppressed touch */ + break; + + default: + dev_dbg(dev, "Unexpected T100 type\n"); + return; + } + } + + /* + * Values reported should be non-zero if tool is touching the + * device + */ + if (!pressure && type != MXT_T100_TYPE_HOVERING_FINGER) + pressure = MXT_PRESSURE_DEFAULT; + + input_mt_slot(input_dev, id); + + if (status & MXT_T100_DETECT) { + dev_dbg(dev, "[%u] type:%u x:%u y:%u a:%02X p:%02X v:%02X\n", + id, type, x, y, major, pressure, orientation); + + input_mt_report_slot_state(input_dev, tool, 1); + input_report_abs(input_dev, ABS_MT_POSITION_X, x); + input_report_abs(input_dev, ABS_MT_POSITION_Y, y); + input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, major); + input_report_abs(input_dev, ABS_MT_PRESSURE, pressure); + input_report_abs(input_dev, ABS_MT_DISTANCE, distance); + input_report_abs(input_dev, ABS_MT_ORIENTATION, orientation); + } else { + dev_dbg(dev, "[%u] release\n", id); + + /* close out slot */ + input_mt_report_slot_state(input_dev, 0, 0); + } + + data->update_input = true; +} + static int mxt_proc_message(struct mxt_data *data, u8 *message) { u8 report_id = message[0]; @@ -786,9 +932,12 @@ static int mxt_proc_message(struct mxt_data *data, u8 *message) * is not yet registered. */ mxt_dump_message(data, message); - } else if (report_id >= data->T9_reportid_min - && report_id <= data->T9_reportid_max) { + } else if (report_id >= data->T9_reportid_min && + report_id <= data->T9_reportid_max) { mxt_proc_t9_message(data, message); + } else if (report_id >= data->T100_reportid_min && + report_id <= data->T100_reportid_max) { + mxt_proc_t100_message(data, message); } else if (report_id == data->T19_reportid) { mxt_input_button(data, message); data->update_input = true; @@ -1411,6 +1560,8 @@ static void mxt_free_object_table(struct mxt_data *data) data->T9_reportid_max = 0; data->T19_reportid = 0; data->T44_address = 0; + data->T100_reportid_min = 0; + data->T100_reportid_max = 0; data->max_reportid = 0; } @@ -1487,6 +1638,7 @@ static int mxt_get_object_table(struct mxt_data *data) data->T7_address = object->start_address; break; case MXT_TOUCH_MULTI_T9: + data->multitouch = MXT_TOUCH_MULTI_T9; data->T9_reportid_min = min_id; data->T9_reportid_max = max_id; data->num_touchids = object->num_report_ids @@ -1498,6 +1650,13 @@ static int mxt_get_object_table(struct mxt_data *data) case MXT_SPT_GPIOPWM_T19: data->T19_reportid = min_id; break; + case MXT_TOUCH_MULTITOUCHSCREEN_T100: + data->multitouch = MXT_TOUCH_MULTITOUCHSCREEN_T100; + data->T100_reportid_min = min_id; + data->T100_reportid_max = max_id; + /* first two report IDs reserved */ + data->num_touchids = object->num_report_ids - 2; + break; } end_address = object->start_address @@ -1582,10 +1741,88 @@ static int mxt_read_t9_resolution(struct mxt_data *data) return 0; } +static int mxt_read_t100_config(struct mxt_data *data) +{ + struct i2c_client *client = data->client; + int error; + struct mxt_object *object; + u16 range_x, range_y; + u8 cfg, tchaux; + u8 aux; + + object = mxt_get_object(data, MXT_TOUCH_MULTITOUCHSCREEN_T100); + if (!object) + return -EINVAL; + + error = __mxt_read_reg(client, + object->start_address + MXT_T100_XRANGE, + sizeof(range_x), &range_x); + if (error) + return error; + + le16_to_cpus(&range_x); + + error = __mxt_read_reg(client, + object->start_address + MXT_T100_YRANGE, + sizeof(range_y), &range_y); + if (error) + return error; + + le16_to_cpus(&range_y); + + error = __mxt_read_reg(client, + object->start_address + MXT_T100_CFG1, + 1, &cfg); + if (error) + return error; + + error = __mxt_read_reg(client, + object->start_address + MXT_T100_TCHAUX, + 1, &tchaux); + if (error) + return error; + + /* Handle default values */ + if (range_x == 0) + range_x = 1023; + + if (range_y == 0) + range_y = 1023; + + if (cfg & MXT_T100_CFG_SWITCHXY) { + data->max_x = range_y; + data->max_y = range_x; + } else { + data->max_x = range_x; + data->max_y = range_y; + } + + /* allocate aux bytes */ + aux = 6; + + if (tchaux & MXT_T100_TCHAUX_VECT) + data->t100_aux_vect = aux++; + + if (tchaux & MXT_T100_TCHAUX_AMPL) + data->t100_aux_ampl = aux++; + + if (tchaux & MXT_T100_TCHAUX_AREA) + data->t100_aux_area = aux++; + + dev_dbg(&client->dev, + "T100 aux mappings vect:%u ampl:%u area:%u\n", + data->t100_aux_vect, data->t100_aux_ampl, data->t100_aux_area); + + dev_info(&client->dev, + "T100 Touchscreen size X%uY%u\n", data->max_x, data->max_y); + + return 0; +} + static int mxt_input_open(struct input_dev *dev); static void mxt_input_close(struct input_dev *dev); -static int mxt_initialize_t9_input_device(struct mxt_data *data) +static int mxt_initialize_input_device(struct mxt_data *data) { struct device *dev = &data->client->dev; const struct mxt_platform_data *pdata = data->pdata; @@ -1595,9 +1832,25 @@ static int mxt_initialize_t9_input_device(struct mxt_data *data) unsigned int mt_flags = 0; int i; - error = mxt_read_t9_resolution(data); - if (error) - dev_warn(dev, "Failed to initialize T9 resolution\n"); + switch (data->multitouch) { + case MXT_TOUCH_MULTI_T9: + num_mt_slots = data->T9_reportid_max - data->T9_reportid_min + 1; + error = mxt_read_t9_resolution(data); + if (error) + dev_warn(dev, "Failed to initialize T9 resolution\n"); + break; + + case MXT_TOUCH_MULTITOUCHSCREEN_T100: + num_mt_slots = data->num_touchids; + error = mxt_read_t100_config(data); + if (error) + dev_warn(dev, "Failed to read T100 config\n"); + break; + + default: + dev_err(dev, "Invalid multitouch object\n"); + return -EINVAL; + } input_dev = input_allocate_device(); if (!input_dev) { @@ -1612,9 +1865,7 @@ static int mxt_initialize_t9_input_device(struct mxt_data *data) input_dev->open = mxt_input_open; input_dev->close = mxt_input_close; - __set_bit(EV_ABS, input_dev->evbit); - __set_bit(EV_KEY, input_dev->evbit); - __set_bit(BTN_TOUCH, input_dev->keybit); + input_set_capability(input_dev, EV_KEY, BTN_TOUCH); if (pdata->t19_num_keys) { __set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit); @@ -1637,29 +1888,67 @@ static int mxt_initialize_t9_input_device(struct mxt_data *data) } /* For single touch */ - input_set_abs_params(input_dev, ABS_X, - 0, data->max_x, 0, 0); - input_set_abs_params(input_dev, ABS_Y, - 0, data->max_y, 0, 0); - input_set_abs_params(input_dev, ABS_PRESSURE, - 0, 255, 0, 0); + input_set_abs_params(input_dev, ABS_X, 0, data->max_x, 0, 0); + input_set_abs_params(input_dev, ABS_Y, 0, data->max_y, 0, 0); + + if (data->multitouch == MXT_TOUCH_MULTI_T9 || + (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 && + data->t100_aux_ampl)) { + input_set_abs_params(input_dev, ABS_PRESSURE, 0, 255, 0, 0); + } /* For multi touch */ - num_mt_slots = data->T9_reportid_max - data->T9_reportid_min + 1; error = input_mt_init_slots(input_dev, num_mt_slots, mt_flags); if (error) { dev_err(dev, "Error %d initialising slots\n", error); goto err_free_mem; } - input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, - 0, MXT_MAX_AREA, 0, 0); + if (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100) { + input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE, + 0, MT_TOOL_MAX, 0, 0); + input_set_abs_params(input_dev, ABS_MT_DISTANCE, + MXT_DISTANCE_ACTIVE_TOUCH, + MXT_DISTANCE_HOVERING, + 0, 0); + } + input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, data->max_x, 0, 0); input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, data->max_y, 0, 0); - input_set_abs_params(input_dev, ABS_MT_PRESSURE, - 0, 255, 0, 0); + + if (data->multitouch == MXT_TOUCH_MULTI_T9 || + (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 && + data->t100_aux_area)) { + input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, + 0, MXT_MAX_AREA, 0, 0); + } + + if (data->multitouch == MXT_TOUCH_MULTI_T9 || + (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); + } + + 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); + } input_set_drvdata(input_dev, data); @@ -1765,9 +2054,13 @@ static int mxt_configure_objects(struct mxt_data *data, dev_warn(dev, "Error %d updating config\n", error); } - error = mxt_initialize_t9_input_device(data); - if (error) - return error; + if (data->multitouch) { + error = mxt_initialize_input_device(data); + if (error) + return error; + } else { + dev_warn(dev, "No touch object detected\n"); + } dev_info(dev, "Family: %u Variant: %u Firmware V%u.%u.%02X Objects: %u\n", @@ -2044,15 +2337,13 @@ static const struct attribute_group mxt_attr_group = { static void mxt_start(struct mxt_data *data) { /* Touch enable */ - mxt_write_object(data, - MXT_TOUCH_MULTI_T9, MXT_TOUCH_CTRL, 0x83); + mxt_write_object(data, data->multitouch, MXT_TOUCH_CTRL, 0x83); } static void mxt_stop(struct mxt_data *data) { /* Touch disable */ - mxt_write_object(data, - MXT_TOUCH_MULTI_T9, MXT_TOUCH_CTRL, 0); + mxt_write_object(data, data->multitouch, MXT_TOUCH_CTRL, 0); } static int mxt_input_open(struct input_dev *dev) -- cgit v1.2.3 From b6d2d3289f84e9c7449dff04fb959e29207acd80 Mon Sep 17 00:00:00 2001 From: Sjoerd Simons Date: Mon, 6 Apr 2015 13:10:30 -0700 Subject: Input: atmel_mxt_ts - split out touchpad initialisation logic If the "linux,gpio-keymap" DT property is defined, the T19 keys are configured and the device is setup as a touchpad rather than a touchscreen. The logic is part of the input device initialization routine but it can be factored out to its own function to simplify the former. Signed-off-by: Sjoerd Simons Signed-off-by: Javier Martinez Canillas Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/atmel_mxt_ts.c | 52 ++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 22 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 1b3b845d92f4..2875ddf37289 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -1822,15 +1822,37 @@ static int mxt_read_t100_config(struct mxt_data *data) static int mxt_input_open(struct input_dev *dev); static void mxt_input_close(struct input_dev *dev); +static void mxt_set_up_as_touchpad(struct input_dev *input_dev, + struct mxt_data *data) +{ + const struct mxt_platform_data *pdata = data->pdata; + int i; + + input_dev->name = "Atmel maXTouch Touchpad"; + + __set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit); + + input_abs_set_res(input_dev, ABS_X, MXT_PIXELS_PER_MM); + input_abs_set_res(input_dev, ABS_Y, MXT_PIXELS_PER_MM); + input_abs_set_res(input_dev, ABS_MT_POSITION_X, + MXT_PIXELS_PER_MM); + input_abs_set_res(input_dev, ABS_MT_POSITION_Y, + MXT_PIXELS_PER_MM); + + for (i = 0; i < pdata->t19_num_keys; i++) + if (pdata->t19_keymap[i] != KEY_RESERVED) + input_set_capability(input_dev, EV_KEY, + pdata->t19_keymap[i]); +} + static int mxt_initialize_input_device(struct mxt_data *data) { - struct device *dev = &data->client->dev; const struct mxt_platform_data *pdata = data->pdata; + struct device *dev = &data->client->dev; struct input_dev *input_dev; int error; unsigned int num_mt_slots; unsigned int mt_flags = 0; - int i; switch (data->multitouch) { case MXT_TOUCH_MULTI_T9: @@ -1867,26 +1889,6 @@ static int mxt_initialize_input_device(struct mxt_data *data) input_set_capability(input_dev, EV_KEY, BTN_TOUCH); - if (pdata->t19_num_keys) { - __set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit); - - for (i = 0; i < pdata->t19_num_keys; i++) - if (pdata->t19_keymap[i] != KEY_RESERVED) - input_set_capability(input_dev, EV_KEY, - pdata->t19_keymap[i]); - - mt_flags |= INPUT_MT_POINTER; - - input_abs_set_res(input_dev, ABS_X, MXT_PIXELS_PER_MM); - input_abs_set_res(input_dev, ABS_Y, MXT_PIXELS_PER_MM); - input_abs_set_res(input_dev, ABS_MT_POSITION_X, - MXT_PIXELS_PER_MM); - input_abs_set_res(input_dev, ABS_MT_POSITION_Y, - MXT_PIXELS_PER_MM); - - input_dev->name = "Atmel maXTouch Touchpad"; - } - /* For single touch */ input_set_abs_params(input_dev, ABS_X, 0, data->max_x, 0, 0); input_set_abs_params(input_dev, ABS_Y, 0, data->max_y, 0, 0); @@ -1897,6 +1899,12 @@ static int mxt_initialize_input_device(struct mxt_data *data) input_set_abs_params(input_dev, ABS_PRESSURE, 0, 255, 0, 0); } + /* If device has buttons we assume it is a touchpad */ + if (pdata->t19_num_keys) { + mxt_set_up_as_touchpad(input_dev, data); + mt_flags |= INPUT_MT_POINTER; + } + /* For multi touch */ error = input_mt_init_slots(input_dev, num_mt_slots, mt_flags); if (error) { -- cgit v1.2.3 From bd884149aca61de269fd9bad83fe2a4232ffab21 Mon Sep 17 00:00:00 2001 From: Ulrik De Bie Date: Mon, 6 Apr 2015 15:35:38 -0700 Subject: Input: elantech - fix absolute mode setting on some ASUS laptops On ASUS TP500LN and X750JN, the touchpad absolute mode is reset each time set_rate is done. In order to fix this, we will verify the firmware version, and if it matches the one in those laptops, the set_rate function is overloaded with a function elantech_set_rate_restore_reg_07 that performs the set_rate with the original function, followed by a restore of reg_07 (the register that sets the absolute mode on elantech v4 hardware). Also the ASUS TP500LN and X750JN firmware version, capabilities, and button constellation is added to elantech.c Cc: stable@vger.kernel.org Reported-and-tested-by: George Moutsopoulos Signed-off-by: Ulrik De Bie Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/elantech.c | 22 ++++++++++++++++++++++ drivers/input/mouse/elantech.h | 1 + 2 files changed, 23 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c index 6e22682c8255..991dc6b20a58 100644 --- a/drivers/input/mouse/elantech.c +++ b/drivers/input/mouse/elantech.c @@ -892,6 +892,21 @@ static psmouse_ret_t elantech_process_byte(struct psmouse *psmouse) return PSMOUSE_FULL_PACKET; } +/* + * This writes the reg_07 value again to the hardware at the end of every + * set_rate call because the register loses its value. reg_07 allows setting + * absolute mode on v4 hardware + */ +static void elantech_set_rate_restore_reg_07(struct psmouse *psmouse, + unsigned int rate) +{ + struct elantech_data *etd = psmouse->private; + + etd->original_set_rate(psmouse, rate); + if (elantech_write_reg(psmouse, 0x07, etd->reg_07)) + psmouse_err(psmouse, "restoring reg_07 failed\n"); +} + /* * Put the touchpad into absolute mode */ @@ -1094,6 +1109,8 @@ static int elantech_get_resolution_v4(struct psmouse *psmouse, * Asus K53SV 0x450f01 78, 15, 0c 2 hw buttons * Asus G46VW 0x460f02 00, 18, 0c 2 hw buttons * Asus G750JX 0x360f00 00, 16, 0c 2 hw buttons + * Asus TP500LN 0x381f17 10, 14, 0e clickpad + * Asus X750JN 0x381f17 10, 14, 0e clickpad * Asus UX31 0x361f00 20, 15, 0e clickpad * Asus UX32VD 0x361f02 00, 15, 0e clickpad * Avatar AVIU-145A2 0x361f00 ? clickpad @@ -1635,6 +1652,11 @@ int elantech_init(struct psmouse *psmouse) goto init_fail; } + if (etd->fw_version == 0x381f17) { + etd->original_set_rate = psmouse->set_rate; + psmouse->set_rate = elantech_set_rate_restore_reg_07; + } + if (elantech_set_input_params(psmouse)) { psmouse_err(psmouse, "failed to query touchpad range.\n"); goto init_fail; diff --git a/drivers/input/mouse/elantech.h b/drivers/input/mouse/elantech.h index 6f3afec02f03..f965d1569cc3 100644 --- a/drivers/input/mouse/elantech.h +++ b/drivers/input/mouse/elantech.h @@ -142,6 +142,7 @@ struct elantech_data { struct finger_pos mt[ETP_MAX_FINGERS]; unsigned char parity[256]; int (*send_cmd)(struct psmouse *psmouse, unsigned char c, unsigned char *param); + void (*original_set_rate)(struct psmouse *psmouse, unsigned int rate); }; #ifdef CONFIG_MOUSE_PS2_ELANTECH -- cgit v1.2.3 From c627589282213bbef12a482c0b87f23526ceb3f0 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Mon, 30 Mar 2015 14:54:15 -0400 Subject: Input - mt: Fix input_mt_get_slot_by_key The case occurred recently with a touchscreen using twice a slot during a single EV_SYN event: E: 0.288415 0000 0000 0000 # ------------ SYN_REPORT (0) ---------- E: 0.296207 0003 002f 0000 # EV_ABS / ABS_MT_SLOT 0 E: 0.296207 0003 0039 -001 # EV_ABS / ABS_MT_TRACKING_ID -1 E: 0.296207 0003 002f 0001 # EV_ABS / ABS_MT_SLOT 1 E: 0.296207 0003 0035 0908 # EV_ABS / ABS_MT_POSITION_X 908 E: 0.296207 0003 0036 1062 # EV_ABS / ABS_MT_POSITION_Y 1062 E: 0.296207 0003 002f 0000 # EV_ABS / ABS_MT_SLOT 0 E: 0.296207 0003 0039 8787 # EV_ABS / ABS_MT_TRACKING_ID 8787 E: 0.296207 0003 0035 1566 # EV_ABS / ABS_MT_POSITION_X 1566 E: 0.296207 0003 0036 0861 # EV_ABS / ABS_MT_POSITION_Y 861 E: 0.296207 0003 0000 0908 # EV_ABS / ABS_X 908 E: 0.296207 0003 0001 1062 # EV_ABS / ABS_Y 1062 E: 0.296207 0000 0000 0000 # ------------ SYN_REPORT (0) ---------- This occurred because while having already slots 0 and 1 assigned, the touchscreen sent: 0.293377 Tip Switch: 0 | Contact Id: 0 | X: 539 | Y: 1960 | Contact Count: 3 0.294783 Tip Switch: 1 | Contact Id: 1 | X: 908 | Y: 1062 | Contact Count: 0 0.296187 Tip Switch: 1 | Contact Id: 2 | X: 1566 | Y: 861 | Contact Count: 0 Slot 0 is released correclty, but when we look for Contact ID 2, the slot 0 is then picked up again because it is marked as inactive (trackingID < 0). This is wrong, and we should not reuse a slot in the same frame. The test should also check for input_mt_is_used(). In addition, we need to initialize mt->frame to an other value than 0. With mt->frame being 0, all slots are tags as currently used, and so input_mt_get_slot_by_key() would return -1 for all requests. Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=88903 Signed-off-by: Benjamin Tissoires Acked-by: Dmitry Torokhov Signed-off-by: Jiri Kosina --- drivers/input/input-mt.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/input-mt.c b/drivers/input/input-mt.c index fbe29fcb15c5..f4b58f77c524 100644 --- a/drivers/input/input-mt.c +++ b/drivers/input/input-mt.c @@ -88,10 +88,13 @@ int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots, goto err_mem; } - /* Mark slots as 'unused' */ + /* Mark slots as 'inactive' */ for (i = 0; i < num_slots; i++) input_mt_set_value(&mt->slots[i], ABS_MT_TRACKING_ID, -1); + /* Mark slots as 'unused' */ + mt->frame = 1; + dev->mt = mt; return 0; err_mem: @@ -430,6 +433,8 @@ EXPORT_SYMBOL(input_mt_assign_slots); * set the key on the first unused slot and return. * * If no available slot can be found, -1 is returned. + * Note that for this function to work properly, input_mt_sync_frame() has + * to be called at each frame. */ int input_mt_get_slot_by_key(struct input_dev *dev, int key) { @@ -444,7 +449,7 @@ int input_mt_get_slot_by_key(struct input_dev *dev, int key) return s - mt->slots; for (s = mt->slots; s != mt->slots + mt->num_slots; s++) - if (!input_mt_is_active(s)) { + if (!input_mt_is_active(s) && !input_mt_is_used(mt, s)) { s->key = key; return s - mt->slots; } -- cgit v1.2.3 From 8eccd39340adb502ac7131552e8a4a2f35c35e06 Mon Sep 17 00:00:00 2001 From: Masaki Ota Date: Fri, 27 Mar 2015 20:44:21 -0700 Subject: Input: ALPS - refactor alps_set_abs_params_mt() In preparation for adding support for SS4 touchpads, let's split alps_set_abs_params_mt into common, v7-specific, and other protocols portions. Signed-off-by: Masaki Ota Acked-by: Hans de Goede Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/alps.c | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index 27bcdbc950c9..c7924b6f7d4b 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -157,6 +157,8 @@ static void alps_set_abs_params_st(struct alps_data *priv, struct input_dev *dev1); static void alps_set_abs_params_mt(struct alps_data *priv, struct input_dev *dev1); +static void alps_set_abs_params_v7(struct alps_data *priv, + struct input_dev *dev1); /* Packet formats are described in Documentation/input/alps.txt */ @@ -2311,7 +2313,7 @@ static int alps_set_protocol(struct psmouse *psmouse, priv->hw_init = alps_hw_init_v7; priv->process_packet = alps_process_packet_v7; priv->decode_fields = alps_decode_packet_v7; - priv->set_abs_params = alps_set_abs_params_mt; + priv->set_abs_params = alps_set_abs_params_v7; priv->nibble_commands = alps_v3_nibble_commands; priv->addr_command = PSMOUSE_CMD_RESET_WRAP; priv->x_max = 0xfff; @@ -2437,10 +2439,11 @@ static void alps_set_abs_params_st(struct alps_data *priv, { input_set_abs_params(dev1, ABS_X, 0, priv->x_max, 0, 0); input_set_abs_params(dev1, ABS_Y, 0, priv->y_max, 0, 0); + input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0); } -static void alps_set_abs_params_mt(struct alps_data *priv, - struct input_dev *dev1) +static void alps_set_abs_params_mt_common(struct alps_data *priv, + struct input_dev *dev1) { input_set_abs_params(dev1, ABS_MT_POSITION_X, 0, priv->x_max, 0, 0); input_set_abs_params(dev1, ABS_MT_POSITION_Y, 0, priv->y_max, 0, 0); @@ -2448,15 +2451,29 @@ static void alps_set_abs_params_mt(struct alps_data *priv, input_abs_set_res(dev1, ABS_MT_POSITION_X, priv->x_res); input_abs_set_res(dev1, ABS_MT_POSITION_Y, priv->y_res); - input_mt_init_slots(dev1, MAX_TOUCHES, INPUT_MT_POINTER | - INPUT_MT_DROP_UNUSED | INPUT_MT_TRACK | INPUT_MT_SEMI_MT); - set_bit(BTN_TOOL_TRIPLETAP, dev1->keybit); set_bit(BTN_TOOL_QUADTAP, dev1->keybit); +} + +static void alps_set_abs_params_mt(struct alps_data *priv, + struct input_dev *dev1) +{ + alps_set_abs_params_mt_common(priv, dev1); + input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0); + + input_mt_init_slots(dev1, MAX_TOUCHES, + INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED | + INPUT_MT_TRACK | INPUT_MT_SEMI_MT); +} + +static void alps_set_abs_params_v7(struct alps_data *priv, + struct input_dev *dev1) +{ + alps_set_abs_params_mt_common(priv, dev1); - /* V7 is real multi-touch */ - if (priv->proto_version == ALPS_PROTO_V7) - clear_bit(INPUT_PROP_SEMI_MT, dev1->propbit); + input_mt_init_slots(dev1, MAX_TOUCHES, + INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED | + INPUT_MT_TRACK); } int alps_init(struct psmouse *psmouse) @@ -2489,9 +2506,6 @@ int alps_init(struct psmouse *psmouse) dev1->evbit[BIT_WORD(EV_ABS)] |= BIT_MASK(EV_ABS); priv->set_abs_params(priv, dev1); - /* No pressure on V7 */ - if (priv->proto_version != ALPS_PROTO_V7) - input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0); if (priv->flags & ALPS_WHEEL) { dev1->evbit[BIT_WORD(EV_REL)] |= BIT_MASK(EV_REL); -- cgit v1.2.3 From 3db5b9f782b50a309fe0fb89fb890f6b4f66d9ae Mon Sep 17 00:00:00 2001 From: Masaki Ota Date: Fri, 27 Mar 2015 20:57:52 -0700 Subject: Input: ALPS - add support for SS4 touchpad devices This change adds support for SS4 touchpad devices as ALPS_PROTO_V8 protocol. They are real multi-touch devices and can be found in TOSHIBA Tecra C50. Signed-off-by: Masaki Ota Acked-by: Hans de Goede Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/alps.c | 329 ++++++++++++++++++++++++++++++++++++++++++++- drivers/input/mouse/alps.h | 78 ++++++++++- 2 files changed, 403 insertions(+), 4 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index c7924b6f7d4b..ad741c04936d 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -153,12 +153,18 @@ static const struct alps_protocol_info alps_v7_protocol_data = { ALPS_PROTO_V7, 0x48, 0x48, ALPS_DUALPOINT }; +static const struct alps_protocol_info alps_v8_protocol_data = { + ALPS_PROTO_V8, 0x18, 0x18, 0 +}; + static void alps_set_abs_params_st(struct alps_data *priv, struct input_dev *dev1); static void alps_set_abs_params_mt(struct alps_data *priv, struct input_dev *dev1); static void alps_set_abs_params_v7(struct alps_data *priv, struct input_dev *dev1); +static void alps_set_abs_params_ss4_v2(struct alps_data *priv, + struct input_dev *dev1); /* Packet formats are described in Documentation/input/alps.txt */ @@ -1087,6 +1093,176 @@ static void alps_process_packet_v7(struct psmouse *psmouse) alps_process_touchpad_packet_v7(psmouse); } +unsigned char alps_get_pkt_id_ss4_v2(unsigned char *byte) +{ + unsigned char pkt_id = SS4_PACKET_ID_IDLE; + + if (byte[0] == 0x18 && byte[1] == 0x10 && byte[2] == 0x00 && + (byte[3] & 0x88) == 0x08 && byte[4] == 0x10 && byte[5] == 0x00) { + pkt_id = SS4_PACKET_ID_IDLE; + } else if (!(byte[3] & 0x10)) { + pkt_id = SS4_PACKET_ID_ONE; + } else if (!(byte[3] & 0x20)) { + pkt_id = SS4_PACKET_ID_TWO; + } else { + pkt_id = SS4_PACKET_ID_MULTI; + } + + return pkt_id; +} + +static int alps_decode_ss4_v2(struct alps_fields *f, + unsigned char *p, struct psmouse *psmouse) +{ + struct alps_data *priv = psmouse->private; + unsigned char pkt_id; + unsigned int no_data_x, no_data_y; + + pkt_id = alps_get_pkt_id_ss4_v2(p); + + /* Current packet is 1Finger coordinate packet */ + switch (pkt_id) { + case SS4_PACKET_ID_ONE: + f->mt[0].x = SS4_1F_X_V2(p); + f->mt[0].y = SS4_1F_Y_V2(p); + f->pressure = ((SS4_1F_Z_V2(p)) * 2) & 0x7f; + f->fingers = 1; + f->first_mp = 0; + f->is_mp = 0; + break; + + case SS4_PACKET_ID_TWO: + if (priv->flags & ALPS_BUTTONPAD) { + f->mt[0].x = SS4_BTL_MF_X_V2(p, 0); + f->mt[0].y = SS4_BTL_MF_Y_V2(p, 0); + f->mt[1].x = SS4_BTL_MF_X_V2(p, 1); + f->mt[1].y = SS4_BTL_MF_Y_V2(p, 1); + } else { + f->mt[0].x = SS4_STD_MF_X_V2(p, 0); + f->mt[0].y = SS4_STD_MF_Y_V2(p, 0); + f->mt[1].x = SS4_STD_MF_X_V2(p, 1); + f->mt[1].y = SS4_STD_MF_Y_V2(p, 1); + } + f->pressure = SS4_MF_Z_V2(p, 0) ? 0x30 : 0; + + if (SS4_IS_MF_CONTINUE(p)) { + f->first_mp = 1; + } else { + f->fingers = 2; + f->first_mp = 0; + } + f->is_mp = 0; + + break; + + case SS4_PACKET_ID_MULTI: + if (priv->flags & ALPS_BUTTONPAD) { + f->mt[2].x = SS4_BTL_MF_X_V2(p, 0); + f->mt[2].y = SS4_BTL_MF_Y_V2(p, 0); + f->mt[3].x = SS4_BTL_MF_X_V2(p, 1); + f->mt[3].y = SS4_BTL_MF_Y_V2(p, 1); + no_data_x = SS4_MFPACKET_NO_AX_BL; + no_data_y = SS4_MFPACKET_NO_AY_BL; + } else { + f->mt[2].x = SS4_STD_MF_X_V2(p, 0); + f->mt[2].y = SS4_STD_MF_Y_V2(p, 0); + f->mt[3].x = SS4_STD_MF_X_V2(p, 1); + f->mt[3].y = SS4_STD_MF_Y_V2(p, 1); + no_data_x = SS4_MFPACKET_NO_AX; + no_data_y = SS4_MFPACKET_NO_AY; + } + + f->first_mp = 0; + f->is_mp = 1; + + if (SS4_IS_5F_DETECTED(p)) { + f->fingers = 5; + } else if (f->mt[3].x == no_data_x && + f->mt[3].y == no_data_y) { + f->mt[3].x = 0; + f->mt[3].y = 0; + f->fingers = 3; + } else { + f->fingers = 4; + } + break; + + case SS4_PACKET_ID_IDLE: + default: + memset(f, 0, sizeof(struct alps_fields)); + break; + } + + f->left = !!(SS4_BTN_V2(p) & 0x01); + if (!(priv->flags & ALPS_BUTTONPAD)) { + f->right = !!(SS4_BTN_V2(p) & 0x02); + f->middle = !!(SS4_BTN_V2(p) & 0x04); + } + + return 0; +} + +static void alps_process_packet_ss4_v2(struct psmouse *psmouse) +{ + struct alps_data *priv = psmouse->private; + unsigned char *packet = psmouse->packet; + struct input_dev *dev = psmouse->dev; + struct alps_fields *f = &priv->f; + + memset(f, 0, sizeof(struct alps_fields)); + priv->decode_fields(f, packet, psmouse); + if (priv->multi_packet) { + /* + * Sometimes the first packet will indicate a multi-packet + * sequence, but sometimes the next multi-packet would not + * come. Check for this, and when it happens process the + * position packet as usual. + */ + if (f->is_mp) { + /* Now process the 1st packet */ + priv->decode_fields(f, priv->multi_data, psmouse); + } else { + priv->multi_packet = 0; + } + } + + /* + * "f.is_mp" would always be '0' after merging the 1st and 2nd packet. + * When it is set, it means 2nd packet comes without 1st packet come. + */ + if (f->is_mp) + return; + + /* Save the first packet */ + if (!priv->multi_packet && f->first_mp) { + priv->multi_packet = 1; + memcpy(priv->multi_data, packet, sizeof(priv->multi_data)); + return; + } + + priv->multi_packet = 0; + + alps_report_mt_data(psmouse, (f->fingers <= 4) ? f->fingers : 4); + + input_mt_report_finger_count(dev, f->fingers); + + input_report_key(dev, BTN_LEFT, f->left); + input_report_key(dev, BTN_RIGHT, f->right); + input_report_key(dev, BTN_MIDDLE, f->middle); + + input_report_abs(dev, ABS_PRESSURE, f->pressure); + input_sync(dev); +} + +static bool alps_is_valid_package_ss4_v2(struct psmouse *psmouse) +{ + if (psmouse->pktcnt == 4 && ((psmouse->packet[3] & 0x08) != 0x08)) + return false; + if (psmouse->pktcnt == 6 && ((psmouse->packet[5] & 0x10) != 0x0)) + return false; + return true; +} + static DEFINE_MUTEX(alps_mutex); static void alps_register_bare_ps2_mouse(struct work_struct *work) @@ -1307,8 +1483,12 @@ static psmouse_ret_t alps_process_byte(struct psmouse *psmouse) * a device connected to the external PS/2 port. Because bare PS/2 * protocol does not have enough constant bits to self-synchronize * properly we only do this if the device is fully synchronized. + * Can not distinguish V8's first byte from PS/2 packet's */ - if (!psmouse->out_of_sync_cnt && (psmouse->packet[0] & 0xc8) == 0x08) { + if (priv->proto_version != ALPS_PROTO_V8 && + !psmouse->out_of_sync_cnt && + (psmouse->packet[0] & 0xc8) == 0x08) { + if (psmouse->pktcnt == 3) { alps_report_bare_ps2_packet(psmouse, psmouse->packet, true); @@ -1356,8 +1536,10 @@ static psmouse_ret_t alps_process_byte(struct psmouse *psmouse) return PSMOUSE_BAD_DATA; } - if (priv->proto_version == ALPS_PROTO_V7 && - !alps_is_valid_package_v7(psmouse)) { + if ((priv->proto_version == ALPS_PROTO_V7 && + !alps_is_valid_package_v7(psmouse)) || + (priv->proto_version == ALPS_PROTO_V8 && + !alps_is_valid_package_ss4_v2(psmouse))) { psmouse_dbg(psmouse, "refusing packet[%i] = %x\n", psmouse->pktcnt - 1, psmouse->packet[psmouse->pktcnt - 1]); @@ -2132,6 +2314,88 @@ error: return -1; } +static int alps_get_otp_values_ss4_v2(struct psmouse *psmouse, + unsigned char index, unsigned char otp[]) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + + switch (index) { + case 0: + if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM) || + ps2_command(ps2dev, otp, PSMOUSE_CMD_GETINFO)) + return -1; + + break; + + case 1: + if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETPOLL) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETPOLL) || + ps2_command(ps2dev, otp, PSMOUSE_CMD_GETINFO)) + return -1; + + break; + } + + return 0; +} + +int alps_update_device_area_ss4_v2(unsigned char otp[][4], + struct alps_data *priv) +{ + int num_x_electrode; + int num_y_electrode; + int x_pitch, y_pitch, x_phys, y_phys; + + num_x_electrode = SS4_NUMSENSOR_XOFFSET + (otp[1][0] & 0x0F); + num_y_electrode = SS4_NUMSENSOR_YOFFSET + ((otp[1][0] >> 4) & 0x0F); + + priv->x_max = (num_x_electrode - 1) * SS4_COUNT_PER_ELECTRODE; + priv->y_max = (num_y_electrode - 1) * SS4_COUNT_PER_ELECTRODE; + + x_pitch = ((otp[1][2] >> 2) & 0x07) + SS4_MIN_PITCH_MM; + y_pitch = ((otp[1][2] >> 5) & 0x07) + SS4_MIN_PITCH_MM; + + x_phys = x_pitch * (num_x_electrode - 1); /* In 0.1 mm units */ + y_phys = y_pitch * (num_y_electrode - 1); /* In 0.1 mm units */ + + priv->x_res = priv->x_max * 10 / x_phys; /* units / mm */ + priv->y_res = priv->y_max * 10 / y_phys; /* units / mm */ + + return 0; +} + +int alps_update_btn_info_ss4_v2(unsigned char otp[][4], struct alps_data *priv) +{ + + unsigned char is_btnless; + + is_btnless = (otp[1][1] >> 3) & 0x01; + + if (is_btnless) + priv->flags |= ALPS_BUTTONPAD; + + return 0; +} + +static int alps_set_defaults_ss4_v2(struct psmouse *psmouse, + struct alps_data *priv) +{ + unsigned char otp[2][4]; + + memset(otp, 0, sizeof(otp)); + + if (alps_get_otp_values_ss4_v2(psmouse, 0, &otp[0][0]) || + alps_get_otp_values_ss4_v2(psmouse, 1, &otp[1][0])) + return -1; + + alps_update_device_area_ss4_v2(otp, priv); + + alps_update_btn_info_ss4_v2(otp, priv); + + return 0; +} + static int alps_dolphin_get_device_area(struct psmouse *psmouse, struct alps_data *priv) { @@ -2224,6 +2488,35 @@ error: return ret; } +static int alps_hw_init_ss4_v2(struct psmouse *psmouse) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + char param[2] = {0x64, 0x28}; + int ret = -1; + + /* enter absolute mode */ + if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM) || + ps2_command(ps2dev, ¶m[0], PSMOUSE_CMD_SETRATE) || + ps2_command(ps2dev, ¶m[1], PSMOUSE_CMD_SETRATE)) { + goto error; + } + + /* T.B.D. Decread noise packet number, delete in the future */ + if (alps_exit_command_mode(psmouse) || + alps_enter_command_mode(psmouse) || + alps_command_mode_write_reg(psmouse, 0x001D, 0x20)) { + goto error; + } + alps_exit_command_mode(psmouse); + + return ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE); + +error: + alps_exit_command_mode(psmouse); + return ret; +} + static int alps_set_protocol(struct psmouse *psmouse, struct alps_data *priv, const struct alps_protocol_info *protocol) @@ -2323,6 +2616,19 @@ static int alps_set_protocol(struct psmouse *psmouse, priv->flags |= ALPS_BUTTONPAD; break; + + case ALPS_PROTO_V8: + priv->hw_init = alps_hw_init_ss4_v2; + priv->process_packet = alps_process_packet_ss4_v2; + priv->decode_fields = alps_decode_ss4_v2; + priv->set_abs_params = alps_set_abs_params_ss4_v2; + priv->nibble_commands = alps_v3_nibble_commands; + priv->addr_command = PSMOUSE_CMD_RESET_WRAP; + + if (alps_set_defaults_ss4_v2(psmouse, priv)) + return -EIO; + + break; } return 0; @@ -2391,6 +2697,9 @@ static int alps_identify(struct psmouse *psmouse, struct alps_data *priv) } else if (ec[0] == 0x88 && ec[1] == 0x07 && ec[2] >= 0x90 && ec[2] <= 0x9d) { protocol = &alps_v3_protocol_data; + } else if (e7[0] == 0x73 && e7[1] == 0x03 && + e7[2] == 0x14 && ec[1] == 0x02) { + protocol = &alps_v8_protocol_data; } else { psmouse_dbg(psmouse, "Likely not an ALPS touchpad: E7=%3ph, EC=%3ph\n", e7, ec); @@ -2471,6 +2780,20 @@ static void alps_set_abs_params_v7(struct alps_data *priv, { alps_set_abs_params_mt_common(priv, dev1); + input_mt_init_slots(dev1, MAX_TOUCHES, + INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED | + INPUT_MT_TRACK); + + set_bit(BTN_TOOL_QUINTTAP, dev1->keybit); +} + +static void alps_set_abs_params_ss4_v2(struct alps_data *priv, + struct input_dev *dev1) +{ + alps_set_abs_params_mt_common(priv, dev1); + input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0); + set_bit(BTN_TOOL_QUINTTAP, dev1->keybit); + input_mt_init_slots(dev1, MAX_TOUCHES, INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED | INPUT_MT_TRACK); diff --git a/drivers/input/mouse/alps.h b/drivers/input/mouse/alps.h index 02513c0502fc..6dfdccc3a7c6 100644 --- a/drivers/input/mouse/alps.h +++ b/drivers/input/mouse/alps.h @@ -22,13 +22,89 @@ #define ALPS_PROTO_V5 0x500 #define ALPS_PROTO_V6 0x600 #define ALPS_PROTO_V7 0x700 /* t3btl t4s */ +#define ALPS_PROTO_V8 0x800 /* SS4btl SS4s */ -#define MAX_TOUCHES 2 +#define MAX_TOUCHES 4 #define DOLPHIN_COUNT_PER_ELECTRODE 64 #define DOLPHIN_PROFILE_XOFFSET 8 /* x-electrode offset */ #define DOLPHIN_PROFILE_YOFFSET 1 /* y-electrode offset */ +/* + * enum SS4_PACKET_ID - defines the packet type for V8 + * SS4_PACKET_ID_IDLE: There's no finger and no button activity. + * SS4_PACKET_ID_ONE: There's one finger on touchpad + * or there's button activities. + * SS4_PACKET_ID_TWO: There's two or more fingers on touchpad + * SS4_PACKET_ID_MULTI: There's three or more fingers on touchpad +*/ +enum SS4_PACKET_ID { + SS4_PACKET_ID_IDLE = 0, + SS4_PACKET_ID_ONE, + SS4_PACKET_ID_TWO, + SS4_PACKET_ID_MULTI, +}; + +#define SS4_COUNT_PER_ELECTRODE 256 +#define SS4_NUMSENSOR_XOFFSET 7 +#define SS4_NUMSENSOR_YOFFSET 7 +#define SS4_MIN_PITCH_MM 50 + +#define SS4_MASK_NORMAL_BUTTONS 0x07 + +#define SS4_1F_X_V2(_b) ((_b[0] & 0x0007) | \ + ((_b[1] << 3) & 0x0078) | \ + ((_b[1] << 2) & 0x0380) | \ + ((_b[2] << 5) & 0x1C00) \ + ) + +#define SS4_1F_Y_V2(_b) (((_b[2]) & 0x000F) | \ + ((_b[3] >> 2) & 0x0030) | \ + ((_b[4] << 6) & 0x03C0) | \ + ((_b[4] << 5) & 0x0C00) \ + ) + +#define SS4_1F_Z_V2(_b) (((_b[5]) & 0x0F) | \ + ((_b[5] >> 1) & 0x70) | \ + ((_b[4]) & 0x80) \ + ) + +#define SS4_1F_LFB_V2(_b) (((_b[2] >> 4) & 0x01) == 0x01) + +#define SS4_MF_LF_V2(_b, _i) ((_b[1 + (_i) * 3] & 0x0004) == 0x0004) + +#define SS4_BTN_V2(_b) ((_b[0] >> 5) & SS4_MASK_NORMAL_BUTTONS) + +#define SS4_STD_MF_X_V2(_b, _i) (((_b[0 + (_i) * 3] << 5) & 0x00E0) | \ + ((_b[1 + _i * 3] << 5) & 0x1F00) \ + ) + +#define SS4_STD_MF_Y_V2(_b, _i) (((_b[1 + (_i) * 3] << 3) & 0x0010) | \ + ((_b[2 + (_i) * 3] << 5) & 0x01E0) | \ + ((_b[2 + (_i) * 3] << 4) & 0x0E00) \ + ) + +#define SS4_BTL_MF_X_V2(_b, _i) (SS4_STD_MF_X_V2(_b, _i) | \ + ((_b[0 + (_i) * 3] >> 3) & 0x0010) \ + ) + +#define SS4_BTL_MF_Y_V2(_b, _i) (SS4_STD_MF_Y_V2(_b, _i) | \ + ((_b[0 + (_i) * 3] >> 3) & 0x0008) \ + ) + +#define SS4_MF_Z_V2(_b, _i) (((_b[1 + (_i) * 3]) & 0x0001) | \ + ((_b[1 + (_i) * 3] >> 1) & 0x0002) \ + ) + +#define SS4_IS_MF_CONTINUE(_b) ((_b[2] & 0x10) == 0x10) +#define SS4_IS_5F_DETECTED(_b) ((_b[2] & 0x10) == 0x10) + + +#define SS4_MFPACKET_NO_AX 8160 /* X-Coordinate value */ +#define SS4_MFPACKET_NO_AY 4080 /* Y-Coordinate value */ +#define SS4_MFPACKET_NO_AX_BL 8176 /* Buttonless X-Coordinate value */ +#define SS4_MFPACKET_NO_AY_BL 4088 /* Buttonless Y-Coordinate value */ + /* * enum V7_PACKET_ID - defines the packet type for V7 * V7_PACKET_ID_IDLE: There's no finger and no button activity. -- cgit v1.2.3 From 8d289842c8ab99edc9437609bd41656295b72cf9 Mon Sep 17 00:00:00 2001 From: Masaki Ota Date: Fri, 27 Mar 2015 21:30:46 -0700 Subject: Input: ALPS - V7 devices can report 5-finger taps Signed-off-by: Masaki Ota Acked-by: Hans de Goede Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/alps.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/input') diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index ad741c04936d..51716eb8ef1e 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -2779,6 +2779,7 @@ static void alps_set_abs_params_v7(struct alps_data *priv, struct input_dev *dev1) { alps_set_abs_params_mt_common(priv, dev1); + set_bit(BTN_TOOL_QUINTTAP, dev1->keybit); input_mt_init_slots(dev1, MAX_TOUCHES, INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED | -- cgit v1.2.3 From 07f19e3db89507ff902b1bb0f64a25ea161d645c Mon Sep 17 00:00:00 2001 From: Fengguang Wu Date: Fri, 10 Apr 2015 23:54:31 -0700 Subject: Input: ALPS - make alps_get_pkt_id_ss4_v2() and others static Signed-off-by: Fengguang Wu Acked-by: Hans de Goede Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/alps.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index 51716eb8ef1e..15d6bffdea77 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -1093,7 +1093,7 @@ static void alps_process_packet_v7(struct psmouse *psmouse) alps_process_touchpad_packet_v7(psmouse); } -unsigned char alps_get_pkt_id_ss4_v2(unsigned char *byte) +static unsigned char alps_get_pkt_id_ss4_v2(unsigned char *byte) { unsigned char pkt_id = SS4_PACKET_ID_IDLE; @@ -2340,8 +2340,8 @@ static int alps_get_otp_values_ss4_v2(struct psmouse *psmouse, return 0; } -int alps_update_device_area_ss4_v2(unsigned char otp[][4], - struct alps_data *priv) +static int alps_update_device_area_ss4_v2(unsigned char otp[][4], + struct alps_data *priv) { int num_x_electrode; int num_y_electrode; @@ -2365,9 +2365,9 @@ int alps_update_device_area_ss4_v2(unsigned char otp[][4], return 0; } -int alps_update_btn_info_ss4_v2(unsigned char otp[][4], struct alps_data *priv) +static int alps_update_btn_info_ss4_v2(unsigned char otp[][4], + struct alps_data *priv) { - unsigned char is_btnless; is_btnless = (otp[1][1] >> 3) & 0x01; -- cgit v1.2.3 From 8b8a518ef16be2de27207991e32fc32b0475c767 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 9 Apr 2015 09:52:31 -0700 Subject: Input: atkbd - document "no new force-release quirks" policy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To save people some time let's document that we do not want new quirks for "force-release" keys in the kernel and that they should patch userspace (udev) instead. Suggested-by: Pali Rohár Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/atkbd.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c index 387c51f4b4e4..ec876b5b1382 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c @@ -1653,6 +1653,12 @@ static int __init atkbd_deactivate_fixup(const struct dmi_system_id *id) return 1; } +/* + * NOTE: do not add any more "force release" quirks to this table. The + * task of adjusting list of keys that should be "released" automatically + * by the driver is now delegated to userspace tools, such as udev, so + * submit such quirks there. + */ static const struct dmi_system_id atkbd_dmi_quirk_table[] __initconst = { { .matches = { -- cgit v1.2.3 From 6bcca19f5dcedc3a006ca0bcc3699a437cadee74 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 8 Apr 2015 09:26:42 -0700 Subject: Input: alps - fix touchpad buttons getting stuck when used with trackpoint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the left touchpad button gets pressed, and then the trackpoint is moved, and then the button is released, the following happens: 1) touchpad packet is received, touchpad evdev node reports BTN_LEFT 1 2) pointing stick packet is received, the hw will report a BTN_LEFT 1 in this packet because when the trackstick is active it communicates the combined touchpad + pointing stick buttons in the trackstick packet, since alps_report_bare_ps2_packet passes NULL (*) for the dev2 parameter to alps_report_buttons the combining is not detected and the pointing stick evdev node will also report BTN_LEFT 1 3) on release of the button a pointing stick packet with BTN_LEFT 0 is received and the pointing stick evdev node will report BTN_LEFT 0 Note how because of the passing as NULL for dev2 the touchpad evdev node will never send BTN_LEFT 0 in this scenario leading to a stuck mouse button. This is a regression in 4.0 introduced by commit 04aae283ba6a8 ("Input: ALPS - do not mix trackstick and external PS/2 mouse data") This commit fixes this by passing in the touchpad evdev as dev2 parameter when calling alps_report_buttons for the pointingstick on alps v2 devices, so that alps_report_buttons correctly detect that we're already reporting the button as pressed via the touchpad evdev node, and will also send the release event there. Cc: stable@vger.kernel.org # 4.0 Reported-by: Hans de Bruin Signed-off-by: Hans de Goede Acked-by: Pali Rohár Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/alps.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index 27bcdbc950c9..ea6cb64dfb28 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -1159,13 +1159,14 @@ static void alps_report_bare_ps2_packet(struct psmouse *psmouse, bool report_buttons) { struct alps_data *priv = psmouse->private; - struct input_dev *dev; + struct input_dev *dev, *dev2 = NULL; /* Figure out which device to use to report the bare packet */ if (priv->proto_version == ALPS_PROTO_V2 && (priv->flags & ALPS_DUALPOINT)) { /* On V2 devices the DualPoint Stick reports bare packets */ dev = priv->dev2; + dev2 = psmouse->dev; } else if (unlikely(IS_ERR_OR_NULL(priv->dev3))) { /* Register dev3 mouse if we received PS/2 packet first time */ if (!IS_ERR(priv->dev3)) @@ -1177,7 +1178,7 @@ static void alps_report_bare_ps2_packet(struct psmouse *psmouse, } if (report_buttons) - alps_report_buttons(dev, NULL, + alps_report_buttons(dev, dev2, packet[0] & 1, packet[0] & 2, packet[0] & 4); input_report_rel(dev, REL_X, -- cgit v1.2.3 From 92bac83dd79e60e65c475222e41a992a70434beb Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 12 Apr 2015 15:42:35 -0700 Subject: Input: alps - non interleaved V2 dualpoint has separate stick button bits Non interleaved V2 dualpoint touchpad / stick combos have separate stick button bits in the touchpad packets, if we do not check these then the trackpoint buttons will not work when using the touchpad, and when pressed when the user starts using the touchpad will report a release event even though the button is still pressed. This commit fixes this by checking the separate bits, note that we simply combine the buttons, since the hardware does the same when using the touchpad buttons with the trackpoint, so we do not have enough information to properly separate them. Reported-by: Hans de Bruin Signed-off-by: Hans de Goede Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/alps.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index ea6cb64dfb28..da3af8db697c 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -243,6 +243,14 @@ static void alps_process_packet_v1_v2(struct psmouse *psmouse) return; } + /* Non interleaved V2 dualpoint has separate stick button bits */ + if (priv->proto_version == ALPS_PROTO_V2 && + priv->flags == (ALPS_PASS | ALPS_DUALPOINT)) { + left |= packet[0] & 1; + right |= packet[0] & 2; + middle |= packet[0] & 4; + } + alps_report_buttons(dev, dev2, left, right, middle); /* Convert hardware tap to a reasonable Z value */ -- cgit v1.2.3