diff options
Diffstat (limited to 'drivers/input')
-rw-r--r-- | drivers/input/evdev.c | 35 | ||||
-rw-r--r-- | drivers/input/input.c | 40 | ||||
-rw-r--r-- | drivers/input/joystick/Kconfig | 10 | ||||
-rw-r--r-- | drivers/input/joystick/Makefile | 5 | ||||
-rw-r--r-- | drivers/input/joystick/fsia6b.c | 231 | ||||
-rw-r--r-- | drivers/input/keyboard/Kconfig | 11 | ||||
-rw-r--r-- | drivers/input/keyboard/Makefile | 1 | ||||
-rw-r--r-- | drivers/input/keyboard/w90p910_keypad.c | 264 | ||||
-rw-r--r-- | drivers/input/misc/Kconfig | 6 | ||||
-rw-r--r-- | drivers/input/misc/soc_button_array.c | 105 | ||||
-rw-r--r-- | drivers/input/mouse/cyapa.c | 16 | ||||
-rw-r--r-- | drivers/input/mouse/elan_i2c_core.c | 20 | ||||
-rw-r--r-- | drivers/input/mouse/synaptics.c | 2 | ||||
-rw-r--r-- | drivers/input/rmi4/rmi_2d_sensor.c | 1 | ||||
-rw-r--r-- | drivers/input/touchscreen/Kconfig | 9 | ||||
-rw-r--r-- | drivers/input/touchscreen/Makefile | 1 | ||||
-rw-r--r-- | drivers/input/touchscreen/ads7846.c | 38 | ||||
-rw-r--r-- | drivers/input/touchscreen/fsl-imx25-tcq.c | 4 | ||||
-rw-r--r-- | drivers/input/touchscreen/mxs-lradc-ts.c | 10 | ||||
-rw-r--r-- | drivers/input/touchscreen/sun4i-ts.c | 3 | ||||
-rw-r--r-- | drivers/input/touchscreen/ts4800-ts.c | 4 | ||||
-rw-r--r-- | drivers/input/touchscreen/w90p910_ts.c | 331 |
22 files changed, 428 insertions, 719 deletions
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index 867c2cfd0038..d7dd6fcf2db0 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -25,13 +25,6 @@ #include <linux/cdev.h> #include "input-compat.h" -enum evdev_clock_type { - EV_CLK_REAL = 0, - EV_CLK_MONO, - EV_CLK_BOOT, - EV_CLK_MAX -}; - struct evdev { int open; struct input_handle handle; @@ -53,7 +46,7 @@ struct evdev_client { struct fasync_struct *fasync; struct evdev *evdev; struct list_head node; - unsigned int clk_type; + enum input_clock_type clk_type; bool revoked; unsigned long *evmasks[EV_CNT]; unsigned int bufsize; @@ -149,17 +142,10 @@ static void __evdev_flush_queue(struct evdev_client *client, unsigned int type) static void __evdev_queue_syn_dropped(struct evdev_client *client) { + ktime_t *ev_time = input_get_timestamp(client->evdev->handle.dev); + struct timespec64 ts = ktime_to_timespec64(ev_time[client->clk_type]); struct input_event ev; - ktime_t time; - struct timespec64 ts; - time = client->clk_type == EV_CLK_REAL ? - ktime_get_real() : - client->clk_type == EV_CLK_MONO ? - ktime_get() : - ktime_get_boottime(); - - ts = ktime_to_timespec64(time); ev.input_event_sec = ts.tv_sec; ev.input_event_usec = ts.tv_nsec / NSEC_PER_USEC; ev.type = EV_SYN; @@ -188,18 +174,18 @@ static void evdev_queue_syn_dropped(struct evdev_client *client) static int evdev_set_clk_type(struct evdev_client *client, unsigned int clkid) { unsigned long flags; - unsigned int clk_type; + enum input_clock_type clk_type; switch (clkid) { case CLOCK_REALTIME: - clk_type = EV_CLK_REAL; + clk_type = INPUT_CLK_REAL; break; case CLOCK_MONOTONIC: - clk_type = EV_CLK_MONO; + clk_type = INPUT_CLK_MONO; break; case CLOCK_BOOTTIME: - clk_type = EV_CLK_BOOT; + clk_type = INPUT_CLK_BOOT; break; default: return -EINVAL; @@ -307,12 +293,7 @@ static void evdev_events(struct input_handle *handle, { struct evdev *evdev = handle->private; struct evdev_client *client; - ktime_t ev_time[EV_CLK_MAX]; - - ev_time[EV_CLK_MONO] = ktime_get(); - ev_time[EV_CLK_REAL] = ktime_mono_to_real(ev_time[EV_CLK_MONO]); - ev_time[EV_CLK_BOOT] = ktime_mono_to_any(ev_time[EV_CLK_MONO], - TK_OFFS_BOOT); + ktime_t *ev_time = input_get_timestamp(handle->dev); rcu_read_lock(); diff --git a/drivers/input/input.c b/drivers/input/input.c index 7f3c5fcb9ed6..7494a0dede79 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -1895,6 +1895,46 @@ void input_free_device(struct input_dev *dev) EXPORT_SYMBOL(input_free_device); /** + * input_set_timestamp - set timestamp for input events + * @dev: input device to set timestamp for + * @timestamp: the time at which the event has occurred + * in CLOCK_MONOTONIC + * + * This function is intended to provide to the input system a more + * accurate time of when an event actually occurred. The driver should + * call this function as soon as a timestamp is acquired ensuring + * clock conversions in input_set_timestamp are done correctly. + * + * The system entering suspend state between timestamp acquisition and + * calling input_set_timestamp can result in inaccurate conversions. + */ +void input_set_timestamp(struct input_dev *dev, ktime_t timestamp) +{ + dev->timestamp[INPUT_CLK_MONO] = timestamp; + dev->timestamp[INPUT_CLK_REAL] = ktime_mono_to_real(timestamp); + dev->timestamp[INPUT_CLK_BOOT] = ktime_mono_to_any(timestamp, + TK_OFFS_BOOT); +} +EXPORT_SYMBOL(input_set_timestamp); + +/** + * input_get_timestamp - get timestamp for input events + * @dev: input device to get timestamp from + * + * A valid timestamp is a timestamp of non-zero value. + */ +ktime_t *input_get_timestamp(struct input_dev *dev) +{ + const ktime_t invalid_timestamp = ktime_set(0, 0); + + if (!ktime_compare(dev->timestamp[INPUT_CLK_MONO], invalid_timestamp)) + input_set_timestamp(dev, ktime_get()); + + return dev->timestamp; +} +EXPORT_SYMBOL(input_get_timestamp); + +/** * input_set_capability - mark device as capable of a certain event * @dev: device that is capable of emitting or accepting event * @type: type of the event (EV_KEY, EV_REL, etc...) diff --git a/drivers/input/joystick/Kconfig b/drivers/input/joystick/Kconfig index 72b932901d00..312b854b5506 100644 --- a/drivers/input/joystick/Kconfig +++ b/drivers/input/joystick/Kconfig @@ -362,4 +362,14 @@ config JOYSTICK_PXRC To compile this driver as a module, choose M here: the module will be called pxrc. +config JOYSTICK_FSIA6B + tristate "FlySky FS-iA6B RC Receiver" + select SERIO + help + Say Y here if you use a FlySky FS-i6 RC remote control along with the + FS-iA6B RC receiver as a joystick input device. + + To compile this driver as a module, choose M here: the + module will be called fsia6b. + endif diff --git a/drivers/input/joystick/Makefile b/drivers/input/joystick/Makefile index dd0492ebbed7..8656023f6ef5 100644 --- a/drivers/input/joystick/Makefile +++ b/drivers/input/joystick/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_JOYSTICK_AS5011) += as5011.o obj-$(CONFIG_JOYSTICK_ANALOG) += analog.o obj-$(CONFIG_JOYSTICK_COBRA) += cobra.o obj-$(CONFIG_JOYSTICK_DB9) += db9.o +obj-$(CONFIG_JOYSTICK_FSIA6B) += fsia6b.o obj-$(CONFIG_JOYSTICK_GAMECON) += gamecon.o obj-$(CONFIG_JOYSTICK_GF2K) += gf2k.o obj-$(CONFIG_JOYSTICK_GRIP) += grip.o @@ -23,7 +24,7 @@ obj-$(CONFIG_JOYSTICK_JOYDUMP) += joydump.o obj-$(CONFIG_JOYSTICK_MAGELLAN) += magellan.o obj-$(CONFIG_JOYSTICK_MAPLE) += maplecontrol.o obj-$(CONFIG_JOYSTICK_PSXPAD_SPI) += psxpad-spi.o -obj-$(CONFIG_JOYSTICK_PXRC) += pxrc.o +obj-$(CONFIG_JOYSTICK_PXRC) += pxrc.o obj-$(CONFIG_JOYSTICK_SIDEWINDER) += sidewinder.o obj-$(CONFIG_JOYSTICK_SPACEBALL) += spaceball.o obj-$(CONFIG_JOYSTICK_SPACEORB) += spaceorb.o @@ -32,7 +33,7 @@ obj-$(CONFIG_JOYSTICK_TMDC) += tmdc.o obj-$(CONFIG_JOYSTICK_TURBOGRAFX) += turbografx.o obj-$(CONFIG_JOYSTICK_TWIDJOY) += twidjoy.o obj-$(CONFIG_JOYSTICK_WARRIOR) += warrior.o +obj-$(CONFIG_JOYSTICK_WALKERA0701) += walkera0701.o obj-$(CONFIG_JOYSTICK_XPAD) += xpad.o obj-$(CONFIG_JOYSTICK_ZHENHUA) += zhenhua.o -obj-$(CONFIG_JOYSTICK_WALKERA0701) += walkera0701.o diff --git a/drivers/input/joystick/fsia6b.c b/drivers/input/joystick/fsia6b.c new file mode 100644 index 000000000000..e78c4c768990 --- /dev/null +++ b/drivers/input/joystick/fsia6b.c @@ -0,0 +1,231 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * FS-iA6B iBus RC receiver driver + * + * This driver provides all 14 channels of the FlySky FS-ia6B RC receiver + * as analog values. + * + * Additionally, the channels can be converted to discrete switch values. + * By default, it is configured for the offical FS-i6 remote control. + * If you use a different hardware configuration, you can configure it + * using the `switch_config` parameter. + */ + +#include <linux/device.h> +#include <linux/input.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/serio.h> +#include <linux/slab.h> +#include <linux/types.h> + +#define DRIVER_DESC "FS-iA6B iBus RC receiver" + +MODULE_AUTHOR("Markus Koch <markus@notsyncing.net>"); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +#define IBUS_SERVO_COUNT 14 + +static char *switch_config = "00000022320000"; +module_param(switch_config, charp, 0444); +MODULE_PARM_DESC(switch_config, + "Amount of switch positions per channel (14 characters, 0-3)"); + +static int fsia6b_axes[IBUS_SERVO_COUNT] = { + ABS_X, ABS_Y, + ABS_Z, ABS_RX, + ABS_RY, ABS_RZ, + ABS_HAT0X, ABS_HAT0Y, + ABS_HAT1X, ABS_HAT1Y, + ABS_HAT2X, ABS_HAT2Y, + ABS_HAT3X, ABS_HAT3Y +}; + +enum ibus_state { SYNC, COLLECT, PROCESS }; + +struct ibus_packet { + enum ibus_state state; + + int offset; + u16 ibuf; + u16 channel[IBUS_SERVO_COUNT]; +}; + +struct fsia6b { + struct input_dev *dev; + struct ibus_packet packet; + + char phys[32]; +}; + +static irqreturn_t fsia6b_serio_irq(struct serio *serio, + unsigned char data, unsigned int flags) +{ + struct fsia6b *fsia6b = serio_get_drvdata(serio); + int i; + int sw_state; + int sw_id = BTN_0; + + fsia6b->packet.ibuf = (data << 8) | ((fsia6b->packet.ibuf >> 8) & 0xFF); + + switch (fsia6b->packet.state) { + case SYNC: + if (fsia6b->packet.ibuf == 0x4020) + fsia6b->packet.state = COLLECT; + break; + + case COLLECT: + fsia6b->packet.state = PROCESS; + break; + + case PROCESS: + fsia6b->packet.channel[fsia6b->packet.offset] = + fsia6b->packet.ibuf; + fsia6b->packet.offset++; + + if (fsia6b->packet.offset == IBUS_SERVO_COUNT) { + fsia6b->packet.offset = 0; + fsia6b->packet.state = SYNC; + for (i = 0; i < IBUS_SERVO_COUNT; ++i) { + input_report_abs(fsia6b->dev, fsia6b_axes[i], + fsia6b->packet.channel[i]); + + sw_state = 0; + if (fsia6b->packet.channel[i] > 1900) + sw_state = 1; + else if (fsia6b->packet.channel[i] < 1100) + sw_state = 2; + + switch (switch_config[i]) { + case '3': + input_report_key(fsia6b->dev, + sw_id++, + sw_state == 0); + /* fall-through */ + case '2': + input_report_key(fsia6b->dev, + sw_id++, + sw_state == 1); + /* fall-through */ + case '1': + input_report_key(fsia6b->dev, + sw_id++, + sw_state == 2); + } + } + input_sync(fsia6b->dev); + } else { + fsia6b->packet.state = COLLECT; + } + break; + } + + return IRQ_HANDLED; +} + +static int fsia6b_serio_connect(struct serio *serio, struct serio_driver *drv) +{ + struct fsia6b *fsia6b; + struct input_dev *input_dev; + int err; + int i, j; + int sw_id = 0; + + fsia6b = kzalloc(sizeof(*fsia6b), GFP_KERNEL); + if (!fsia6b) + return -ENOMEM; + + fsia6b->packet.ibuf = 0; + fsia6b->packet.offset = 0; + fsia6b->packet.state = SYNC; + + serio_set_drvdata(serio, fsia6b); + + input_dev = input_allocate_device(); + if (!input_dev) { + err = -ENOMEM; + goto fail1; + } + fsia6b->dev = input_dev; + + snprintf(fsia6b->phys, sizeof(fsia6b->phys), "%s/input0", serio->phys); + + input_dev->name = DRIVER_DESC; + input_dev->phys = fsia6b->phys; + input_dev->id.bustype = BUS_RS232; + input_dev->id.vendor = SERIO_FSIA6B; + input_dev->id.product = serio->id.id; + input_dev->id.version = 0x0100; + input_dev->dev.parent = &serio->dev; + + for (i = 0; i < IBUS_SERVO_COUNT; i++) + input_set_abs_params(input_dev, fsia6b_axes[i], + 1000, 2000, 2, 2); + + /* Register switch configuration */ + for (i = 0; i < IBUS_SERVO_COUNT; i++) { + if (switch_config[i] < '0' || switch_config[i] > '3') { + dev_err(&fsia6b->dev->dev, + "Invalid switch configuration supplied for fsia6b.\n"); + err = -EINVAL; + goto fail2; + } + + for (j = '1'; j <= switch_config[i]; j++) { + input_set_capability(input_dev, EV_KEY, BTN_0 + sw_id); + sw_id++; + } + } + + err = serio_open(serio, drv); + if (err) + goto fail2; + + err = input_register_device(fsia6b->dev); + if (err) + goto fail3; + + return 0; + +fail3: serio_close(serio); +fail2: input_free_device(input_dev); +fail1: serio_set_drvdata(serio, NULL); + kfree(fsia6b); + return err; +} + +static void fsia6b_serio_disconnect(struct serio *serio) +{ + struct fsia6b *fsia6b = serio_get_drvdata(serio); + + serio_close(serio); + serio_set_drvdata(serio, NULL); + input_unregister_device(fsia6b->dev); + kfree(fsia6b); +} + +static const struct serio_device_id fsia6b_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_FSIA6B, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, fsia6b_serio_ids); + +static struct serio_driver fsia6b_serio_drv = { + .driver = { + .name = "fsia6b" + }, + .description = DRIVER_DESC, + .id_table = fsia6b_serio_ids, + .interrupt = fsia6b_serio_irq, + .connect = fsia6b_serio_connect, + .disconnect = fsia6b_serio_disconnect +}; + +module_serio_driver(fsia6b_serio_drv) diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 90e8a7f2f07c..2e6d2887eec1 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -733,17 +733,6 @@ config KEYBOARD_XTKBD To compile this driver as a module, choose M here: the module will be called xtkbd. -config KEYBOARD_W90P910 - tristate "W90P910 Matrix Keypad support" - depends on ARCH_W90X900 - select INPUT_MATRIXKMAP - help - Say Y here to enable the matrix keypad on evaluation board - based on W90P910. - - To compile this driver as a module, choose M here: the - module will be called w90p910_keypad. - config KEYBOARD_CROS_EC tristate "ChromeOS EC keyboard" select INPUT_MATRIXKMAP diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 06a0af6efeae..9510325c0c5d 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -68,4 +68,3 @@ obj-$(CONFIG_KEYBOARD_TEGRA) += tegra-kbc.o obj-$(CONFIG_KEYBOARD_TM2_TOUCHKEY) += tm2-touchkey.o obj-$(CONFIG_KEYBOARD_TWL4030) += twl4030_keypad.o obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o -obj-$(CONFIG_KEYBOARD_W90P910) += w90p910_keypad.o diff --git a/drivers/input/keyboard/w90p910_keypad.c b/drivers/input/keyboard/w90p910_keypad.c deleted file mode 100644 index c88d05d6108a..000000000000 --- a/drivers/input/keyboard/w90p910_keypad.c +++ /dev/null @@ -1,264 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (c) 2008-2009 Nuvoton technology corporation. - * - * Wan ZongShun <mcuos.com@gmail.com> - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/interrupt.h> -#include <linux/input.h> -#include <linux/device.h> -#include <linux/platform_device.h> -#include <linux/clk.h> -#include <linux/err.h> -#include <linux/io.h> -#include <linux/slab.h> - -#include <linux/platform_data/keypad-w90p910.h> - -/* Keypad Interface Control Registers */ -#define KPI_CONF 0x00 -#define KPI_3KCONF 0x04 -#define KPI_LPCONF 0x08 -#define KPI_STATUS 0x0C - -#define IS1KEY (0x01 << 16) -#define INTTR (0x01 << 21) -#define KEY0R (0x0f << 3) -#define KEY0C 0x07 -#define DEBOUNCE_BIT 0x08 -#define KSIZE0 (0x01 << 16) -#define KSIZE1 (0x01 << 17) -#define KPSEL (0x01 << 19) -#define ENKP (0x01 << 18) - -#define KGET_RAW(n) (((n) & KEY0R) >> 3) -#define KGET_COLUMN(n) ((n) & KEY0C) - -#define W90P910_NUM_ROWS 8 -#define W90P910_NUM_COLS 8 -#define W90P910_ROW_SHIFT 3 - -struct w90p910_keypad { - const struct w90p910_keypad_platform_data *pdata; - struct clk *clk; - struct input_dev *input_dev; - void __iomem *mmio_base; - int irq; - unsigned short keymap[W90P910_NUM_ROWS * W90P910_NUM_COLS]; -}; - -static void w90p910_keypad_scan_matrix(struct w90p910_keypad *keypad, - unsigned int status) -{ - struct input_dev *input_dev = keypad->input_dev; - unsigned int row = KGET_RAW(status); - unsigned int col = KGET_COLUMN(status); - unsigned int code = MATRIX_SCAN_CODE(row, col, W90P910_ROW_SHIFT); - unsigned int key = keypad->keymap[code]; - - input_event(input_dev, EV_MSC, MSC_SCAN, code); - input_report_key(input_dev, key, 1); - input_sync(input_dev); - - input_event(input_dev, EV_MSC, MSC_SCAN, code); - input_report_key(input_dev, key, 0); - input_sync(input_dev); -} - -static irqreturn_t w90p910_keypad_irq_handler(int irq, void *dev_id) -{ - struct w90p910_keypad *keypad = dev_id; - unsigned int kstatus, val; - - kstatus = __raw_readl(keypad->mmio_base + KPI_STATUS); - - val = INTTR | IS1KEY; - - if (kstatus & val) - w90p910_keypad_scan_matrix(keypad, kstatus); - - return IRQ_HANDLED; -} - -static int w90p910_keypad_open(struct input_dev *dev) -{ - struct w90p910_keypad *keypad = input_get_drvdata(dev); - const struct w90p910_keypad_platform_data *pdata = keypad->pdata; - unsigned int val, config; - - /* Enable unit clock */ - clk_enable(keypad->clk); - - val = __raw_readl(keypad->mmio_base + KPI_CONF); - val |= (KPSEL | ENKP); - val &= ~(KSIZE0 | KSIZE1); - - config = pdata->prescale | (pdata->debounce << DEBOUNCE_BIT); - - val |= config; - - __raw_writel(val, keypad->mmio_base + KPI_CONF); - - return 0; -} - -static void w90p910_keypad_close(struct input_dev *dev) -{ - struct w90p910_keypad *keypad = input_get_drvdata(dev); - - /* Disable clock unit */ - clk_disable(keypad->clk); -} - -static int w90p910_keypad_probe(struct platform_device *pdev) -{ - const struct w90p910_keypad_platform_data *pdata = - dev_get_platdata(&pdev->dev); - const struct matrix_keymap_data *keymap_data; - struct w90p910_keypad *keypad; - struct input_dev *input_dev; - struct resource *res; - int irq; - int error; - - if (!pdata) { - dev_err(&pdev->dev, "no platform data defined\n"); - return -EINVAL; - } - - keymap_data = pdata->keymap_data; - - irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&pdev->dev, "failed to get keypad irq\n"); - return -ENXIO; - } - - keypad = kzalloc(sizeof(struct w90p910_keypad), GFP_KERNEL); - input_dev = input_allocate_device(); - if (!keypad || !input_dev) { - dev_err(&pdev->dev, "failed to allocate driver data\n"); - error = -ENOMEM; - goto failed_free; - } - - keypad->pdata = pdata; - keypad->input_dev = input_dev; - keypad->irq = irq; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res == NULL) { - dev_err(&pdev->dev, "failed to get I/O memory\n"); - error = -ENXIO; - goto failed_free; - } - - res = request_mem_region(res->start, resource_size(res), pdev->name); - if (res == NULL) { - dev_err(&pdev->dev, "failed to request I/O memory\n"); - error = -EBUSY; - goto failed_free; - } - - keypad->mmio_base = ioremap(res->start, resource_size(res)); - if (keypad->mmio_base == NULL) { - dev_err(&pdev->dev, "failed to remap I/O memory\n"); - error = -ENXIO; - goto failed_free_res; - } - - keypad->clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(keypad->clk)) { - dev_err(&pdev->dev, "failed to get keypad clock\n"); - error = PTR_ERR(keypad->clk); - goto failed_free_io; - } - - /* set multi-function pin for w90p910 kpi. */ - mfp_set_groupi(&pdev->dev); - - input_dev->name = pdev->name; - input_dev->id.bustype = BUS_HOST; - input_dev->open = w90p910_keypad_open; - input_dev->close = w90p910_keypad_close; - input_dev->dev.parent = &pdev->dev; - - error = matrix_keypad_build_keymap(keymap_data, NULL, - W90P910_NUM_ROWS, W90P910_NUM_COLS, - keypad->keymap, input_dev); - if (error) { - dev_err(&pdev->dev, "failed to build keymap\n"); - goto failed_put_clk; - } - - error = request_irq(keypad->irq, w90p910_keypad_irq_handler, - 0, pdev->name, keypad); - if (error) { - dev_err(&pdev->dev, "failed to request IRQ\n"); - goto failed_put_clk; - } - - __set_bit(EV_REP, input_dev->evbit); - input_set_capability(input_dev, EV_MSC, MSC_SCAN); - input_set_drvdata(input_dev, keypad); - - /* Register the input device */ - error = input_register_device(input_dev); - if (error) { - dev_err(&pdev->dev, "failed to register input device\n"); - goto failed_free_irq; - } - - platform_set_drvdata(pdev, keypad); - return 0; - -failed_free_irq: - free_irq(irq, keypad); -failed_put_clk: - clk_put(keypad->clk); -failed_free_io: - iounmap(keypad->mmio_base); -failed_free_res: - release_mem_region(res->start, resource_size(res)); -failed_free: - input_free_device(input_dev); - kfree(keypad); - return error; -} - -static int w90p910_keypad_remove(struct platform_device *pdev) -{ - struct w90p910_keypad *keypad = platform_get_drvdata(pdev); - struct resource *res; - - free_irq(keypad->irq, keypad); - - clk_put(keypad->clk); - - input_unregister_device(keypad->input_dev); - - iounmap(keypad->mmio_base); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(res->start, resource_size(res)); - - kfree(keypad); - - return 0; -} - -static struct platform_driver w90p910_keypad_driver = { - .probe = w90p910_keypad_probe, - .remove = w90p910_keypad_remove, - .driver = { - .name = "nuc900-kpi", - }, -}; -module_platform_driver(w90p910_keypad_driver); - -MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>"); -MODULE_DESCRIPTION("w90p910 keypad driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:nuc900-keypad"); diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index d07c1eb15aa6..7d9ae394e597 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -813,10 +813,10 @@ config INPUT_IDEAPAD_SLIDEBAR config INPUT_SOC_BUTTON_ARRAY tristate "Windows-compatible SoC Button Array" - depends on KEYBOARD_GPIO + depends on KEYBOARD_GPIO && ACPI help - Say Y here if you have a SoC-based tablet that originally - runs Windows 8. + Say Y here if you have a SoC-based tablet that originally runs + Windows 8 or a Microsoft Surface Book 2, Pro 5, Laptop 1 or later. To compile this driver as a module, choose M here: the module will be called soc_button_array. diff --git a/drivers/input/misc/soc_button_array.c b/drivers/input/misc/soc_button_array.c index 5e59f8e57f8e..6f0133fe1546 100644 --- a/drivers/input/misc/soc_button_array.c +++ b/drivers/input/misc/soc_button_array.c @@ -25,6 +25,11 @@ struct soc_button_info { bool wakeup; }; +struct soc_device_data { + const struct soc_button_info *button_info; + int (*check)(struct device *dev); +}; + /* * Some of the buttons like volume up/down are auto repeat, while others * are not. To support both, we register two platform devices, and put @@ -87,8 +92,13 @@ soc_button_device_create(struct platform_device *pdev, continue; gpio = soc_button_lookup_gpio(&pdev->dev, info->acpi_index); - if (!gpio_is_valid(gpio)) + if (gpio < 0 && gpio != -ENOENT) { + error = gpio; + goto err_free_mem; + } else if (!gpio_is_valid(gpio)) { + /* Skip GPIO if not present */ continue; + } gpio_keys[n_buttons].type = info->event_type; gpio_keys[n_buttons].code = info->event_code; @@ -309,23 +319,26 @@ static int soc_button_remove(struct platform_device *pdev) static int soc_button_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - const struct acpi_device_id *id; - struct soc_button_info *button_info; + const struct soc_device_data *device_data; + const struct soc_button_info *button_info; struct soc_button_data *priv; struct platform_device *pd; int i; int error; - id = acpi_match_device(dev->driver->acpi_match_table, dev); - if (!id) - return -ENODEV; + device_data = acpi_device_get_match_data(dev); + if (device_data && device_data->check) { + error = device_data->check(dev); + if (error) + return error; + } - if (!id->driver_data) { + if (device_data && device_data->button_info) { + button_info = device_data->button_info; + } else { button_info = soc_button_get_button_info(dev); if (IS_ERR(button_info)) return PTR_ERR(button_info); - } else { - button_info = (struct soc_button_info *)id->driver_data; } error = gpiod_count(dev, NULL); @@ -357,7 +370,7 @@ static int soc_button_probe(struct platform_device *pdev) if (!priv->children[0] && !priv->children[1]) return -ENODEV; - if (!id->driver_data) + if (!device_data || !device_data->button_info) devm_kfree(dev, button_info); return 0; @@ -368,7 +381,7 @@ static int soc_button_probe(struct platform_device *pdev) * is defined in section 2.8.7.2 of "Windows ACPI Design Guide for SoC * Platforms" */ -static struct soc_button_info soc_button_PNP0C40[] = { +static const struct soc_button_info soc_button_PNP0C40[] = { { "power", 0, EV_KEY, KEY_POWER, false, true }, { "home", 1, EV_KEY, KEY_LEFTMETA, false, true }, { "volume_up", 2, EV_KEY, KEY_VOLUMEUP, true, false }, @@ -377,9 +390,77 @@ static struct soc_button_info soc_button_PNP0C40[] = { { } }; +static const struct soc_device_data soc_device_PNP0C40 = { + .button_info = soc_button_PNP0C40, +}; + +/* + * Special device check for Surface Book 2 and Surface Pro (2017). + * Both, the Surface Pro 4 (surfacepro3_button.c) and the above mentioned + * devices use MSHW0040 for power and volume buttons, however the way they + * have to be addressed differs. Make sure that we only load this drivers + * for the correct devices by checking the OEM Platform Revision provided by + * the _DSM method. + */ +#define MSHW0040_DSM_REVISION 0x01 +#define MSHW0040_DSM_GET_OMPR 0x02 // get OEM Platform Revision +static const guid_t MSHW0040_DSM_UUID = + GUID_INIT(0x6fd05c69, 0xcde3, 0x49f4, 0x95, 0xed, 0xab, 0x16, 0x65, + 0x49, 0x80, 0x35); + +static int soc_device_check_MSHW0040(struct device *dev) +{ + acpi_handle handle = ACPI_HANDLE(dev); + union acpi_object *result; + u64 oem_platform_rev = 0; // valid revisions are nonzero + + // get OEM platform revision + result = acpi_evaluate_dsm_typed(handle, &MSHW0040_DSM_UUID, + MSHW0040_DSM_REVISION, + MSHW0040_DSM_GET_OMPR, NULL, + ACPI_TYPE_INTEGER); + + if (result) { + oem_platform_rev = result->integer.value; + ACPI_FREE(result); + } + + /* + * If the revision is zero here, the _DSM evaluation has failed. This + * indicates that we have a Pro 4 or Book 1 and this driver should not + * be used. + */ + if (oem_platform_rev == 0) + return -ENODEV; + + dev_dbg(dev, "OEM Platform Revision %llu\n", oem_platform_rev); + + return 0; +} + +/* + * Button infos for Microsoft Surface Book 2 and Surface Pro (2017). + * Obtained from DSDT/testing. + */ +static const struct soc_button_info soc_button_MSHW0040[] = { + { "power", 0, EV_KEY, KEY_POWER, false, true }, + { "volume_up", 2, EV_KEY, KEY_VOLUMEUP, true, false }, + { "volume_down", 4, EV_KEY, KEY_VOLUMEDOWN, true, false }, + { } +}; + +static const struct soc_device_data soc_device_MSHW0040 = { + .button_info = soc_button_MSHW0040, + .check = soc_device_check_MSHW0040, +}; + static const struct acpi_device_id soc_button_acpi_match[] = { - { "PNP0C40", (unsigned long)soc_button_PNP0C40 }, + { "PNP0C40", (unsigned long)&soc_device_PNP0C40 }, { "ACPI0011", 0 }, + + /* Microsoft Surface Devices (5th and 6th generation) */ + { "MSHW0040", (unsigned long)&soc_device_MSHW0040 }, + { } }; diff --git a/drivers/input/mouse/cyapa.c b/drivers/input/mouse/cyapa.c index dfd3873513e4..c675f156948b 100644 --- a/drivers/input/mouse/cyapa.c +++ b/drivers/input/mouse/cyapa.c @@ -1238,13 +1238,6 @@ static const struct attribute_group cyapa_sysfs_group = { .attrs = cyapa_sysfs_entries, }; -static void cyapa_remove_sysfs_group(void *data) -{ - struct cyapa *cyapa = data; - - sysfs_remove_group(&cyapa->client->dev.kobj, &cyapa_sysfs_group); -} - static void cyapa_disable_regulator(void *data) { struct cyapa *cyapa = data; @@ -1312,19 +1305,12 @@ static int cyapa_probe(struct i2c_client *client, return error; } - error = sysfs_create_group(&dev->kobj, &cyapa_sysfs_group); + error = devm_device_add_group(dev, &cyapa_sysfs_group); if (error) { dev_err(dev, "failed to create sysfs entries: %d\n", error); return error; } - error = devm_add_action(dev, cyapa_remove_sysfs_group, cyapa); - if (error) { - cyapa_remove_sysfs_group(cyapa); - dev_err(dev, "failed to add sysfs cleanup action: %d\n", error); - return error; - } - error = cyapa_prepare_wakeup_controls(cyapa); if (error) { dev_err(dev, "failed to prepare wakeup controls: %d\n", error); diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c index d9b103a81a79..8719da540383 100644 --- a/drivers/input/mouse/elan_i2c_core.c +++ b/drivers/input/mouse/elan_i2c_core.c @@ -1138,13 +1138,6 @@ static void elan_disable_regulator(void *_data) regulator_disable(data->vcc); } -static void elan_remove_sysfs_groups(void *_data) -{ - struct elan_tp_data *data = _data; - - sysfs_remove_groups(&data->client->dev.kobj, elan_sysfs_groups); -} - static int elan_probe(struct i2c_client *client, const struct i2c_device_id *dev_id) { @@ -1194,9 +1187,8 @@ static int elan_probe(struct i2c_client *client, return error; } - error = devm_add_action(dev, elan_disable_regulator, data); + error = devm_add_action_or_reset(dev, elan_disable_regulator, data); if (error) { - regulator_disable(data->vcc); dev_err(dev, "Failed to add disable regulator action: %d\n", error); return error; @@ -1269,20 +1261,12 @@ static int elan_probe(struct i2c_client *client, return error; } - error = sysfs_create_groups(&dev->kobj, elan_sysfs_groups); + error = devm_device_add_groups(dev, elan_sysfs_groups); if (error) { dev_err(dev, "failed to create sysfs attributes: %d\n", error); return error; } - error = devm_add_action(dev, elan_remove_sysfs_groups, data); - if (error) { - elan_remove_sysfs_groups(data); - dev_err(dev, "Failed to add sysfs cleanup action: %d\n", - error); - return error; - } - error = input_register_device(data->input); if (error) { dev_err(dev, "failed to register input device: %d\n", error); diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 46bbe99d6511..56fae3472114 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -193,7 +193,7 @@ static const char * const forcepad_pnp_ids[] = { }; /* - * Send a command to the synpatics touchpad by special commands + * Send a command to the synaptics touchpad by special commands */ static int synaptics_send_cmd(struct psmouse *psmouse, u8 cmd, u8 *param) { diff --git a/drivers/input/rmi4/rmi_2d_sensor.c b/drivers/input/rmi4/rmi_2d_sensor.c index ea549efe4bc4..b7fe6eb35a4e 100644 --- a/drivers/input/rmi4/rmi_2d_sensor.c +++ b/drivers/input/rmi4/rmi_2d_sensor.c @@ -204,7 +204,6 @@ static void rmi_2d_sensor_set_input_params(struct rmi_2d_sensor *sensor) if (sensor->topbuttonpad) set_bit(INPUT_PROP_TOPBUTTONPAD, input->propbit); } -EXPORT_SYMBOL_GPL(rmi_2d_sensor_set_input_params); int rmi_2d_sensor_configure_input(struct rmi_function *fn, struct rmi_2d_sensor *sensor) diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index fb91f2d4049e..46ad9090493b 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -1112,15 +1112,6 @@ config TOUCHSCREEN_TSC2007_IIO or ambient light monitoring), temperature and raw input values. -config TOUCHSCREEN_W90X900 - tristate "W90P910 touchscreen driver" - depends on ARCH_W90X900 - help - Say Y here if you have a W90P910 based touchscreen. - - To compile this driver as a module, choose M here: the - module will be called w90p910_ts. - config TOUCHSCREEN_PCAP tristate "Motorola PCAP touchscreen" depends on EZX_PCAP diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 084a596a0c8b..94c6162409b3 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -102,7 +102,6 @@ wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9712) += wm9712.o wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9713) += wm9713.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_ZET6223) += zet6223.o diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index accbbe8d2966..51ddb204ca1b 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -20,6 +20,7 @@ #include <linux/sched.h> #include <linux/delay.h> #include <linux/input.h> +#include <linux/input/touchscreen.h> #include <linux/interrupt.h> #include <linux/slab.h> #include <linux/pm.h> @@ -129,6 +130,8 @@ struct ads7846 { u16 penirq_recheck_delay_usecs; + struct touchscreen_properties core_prop; + struct mutex lock; bool stopped; /* P: lock */ bool disabled; /* P: lock */ @@ -823,17 +826,13 @@ static void ads7846_report_state(struct ads7846 *ts) if (Rt) { struct input_dev *input = ts->input; - if (ts->swap_xy) - swap(x, y); - if (!ts->pendown) { input_report_key(input, BTN_TOUCH, 1); ts->pendown = true; dev_vdbg(&ts->spi->dev, "DOWN\n"); } - input_report_abs(input, ABS_X, x); - input_report_abs(input, ABS_Y, y); + touchscreen_report_pos(input, &ts->core_prop, x, y, false); input_report_abs(input, ABS_PRESSURE, ts->pressure_max - Rt); input_sync(input); @@ -1185,6 +1184,7 @@ static const struct ads7846_platform_data *ads7846_probe_dt(struct device *dev) struct ads7846_platform_data *pdata; struct device_node *node = dev->of_node; const struct of_device_id *match; + u32 value; if (!node) { dev_err(dev, "Device does not have associated DT data\n"); @@ -1223,10 +1223,18 @@ static const struct ads7846_platform_data *ads7846_probe_dt(struct device *dev) of_property_read_u16(node, "ti,x-max", &pdata->x_max); of_property_read_u16(node, "ti,y-max", &pdata->y_max); + /* + * touchscreen-max-pressure gets parsed during + * touchscreen_parse_properties() + */ of_property_read_u16(node, "ti,pressure-min", &pdata->pressure_min); + if (!of_property_read_u32(node, "touchscreen-min-pressure", &value)) + pdata->pressure_min = (u16) value; of_property_read_u16(node, "ti,pressure-max", &pdata->pressure_max); of_property_read_u16(node, "ti,debounce-max", &pdata->debounce_max); + if (!of_property_read_u32(node, "touchscreen-average-samples", &value)) + pdata->debounce_max = (u16) value; of_property_read_u16(node, "ti,debounce-tol", &pdata->debounce_tol); of_property_read_u16(node, "ti,debounce-rep", &pdata->debounce_rep); @@ -1309,10 +1317,7 @@ static int ads7846_probe(struct spi_device *spi) ts->model = pdata->model ? : 7846; ts->vref_delay_usecs = pdata->vref_delay_usecs ? : 100; ts->x_plate_ohms = pdata->x_plate_ohms ? : 400; - ts->pressure_max = pdata->pressure_max ? : ~0; - ts->vref_mv = pdata->vref_mv; - ts->swap_xy = pdata->swap_xy; if (pdata->filter != NULL) { if (pdata->filter_init != NULL) { @@ -1364,6 +1369,23 @@ static int ads7846_probe(struct spi_device *spi) input_set_abs_params(input_dev, ABS_PRESSURE, pdata->pressure_min, pdata->pressure_max, 0, 0); + /* + * Parse common framework properties. Must be done here to ensure the + * correct behaviour in case of using the legacy vendor bindings. The + * general binding value overrides the vendor specific one. + */ + touchscreen_parse_properties(ts->input, false, &ts->core_prop); + ts->pressure_max = input_abs_get_max(input_dev, ABS_PRESSURE) ? : ~0; + + /* + * Check if legacy ti,swap-xy binding is used instead of + * touchscreen-swapped-x-y + */ + if (!ts->core_prop.swap_x_y && pdata->swap_xy) { + swap(input_dev->absinfo[ABS_X], input_dev->absinfo[ABS_Y]); + ts->core_prop.swap_x_y = true; + } + ads7846_setup_spi_msg(ts, pdata); ts->reg = regulator_get(&spi->dev, "vcc"); diff --git a/drivers/input/touchscreen/fsl-imx25-tcq.c b/drivers/input/touchscreen/fsl-imx25-tcq.c index 1d6c8f490b40..b66df8ab89f2 100644 --- a/drivers/input/touchscreen/fsl-imx25-tcq.c +++ b/drivers/input/touchscreen/fsl-imx25-tcq.c @@ -503,7 +503,6 @@ static int mx25_tcq_probe(struct platform_device *pdev) struct input_dev *idev; struct mx25_tcq_priv *priv; struct mx25_tsadc *tsadc = dev_get_drvdata(dev->parent); - struct resource *res; void __iomem *mem; int error; @@ -512,8 +511,7 @@ static int mx25_tcq_probe(struct platform_device *pdev) return -ENOMEM; priv->dev = dev; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - mem = devm_ioremap_resource(dev, res); + mem = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(mem)) return PTR_ERR(mem); diff --git a/drivers/input/touchscreen/mxs-lradc-ts.c b/drivers/input/touchscreen/mxs-lradc-ts.c index 593b8d3e90b5..9e36fee38d61 100644 --- a/drivers/input/touchscreen/mxs-lradc-ts.c +++ b/drivers/input/touchscreen/mxs-lradc-ts.c @@ -606,7 +606,6 @@ static int mxs_lradc_ts_probe(struct platform_device *pdev) struct device_node *node = dev->parent->of_node; struct mxs_lradc *lradc = dev_get_drvdata(dev->parent); struct mxs_lradc_ts *ts; - struct resource *iores; int ret, irq, virq, i; u32 ts_wires = 0, adapt; @@ -620,12 +619,9 @@ static int mxs_lradc_ts_probe(struct platform_device *pdev) ts->dev = dev; spin_lock_init(&ts->lock); - iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!iores) - return -EINVAL; - ts->base = devm_ioremap(dev, iores->start, resource_size(iores)); - if (!ts->base) - return -ENOMEM; + ts->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(ts->base)) + return PTR_ERR(ts->base); ret = of_property_read_u32(node, "fsl,lradc-touchscreen-wires", &ts_wires); diff --git a/drivers/input/touchscreen/sun4i-ts.c b/drivers/input/touchscreen/sun4i-ts.c index f11ba7f2dca7..0af0fe8c40d7 100644 --- a/drivers/input/touchscreen/sun4i-ts.c +++ b/drivers/input/touchscreen/sun4i-ts.c @@ -300,8 +300,7 @@ static int sun4i_ts_probe(struct platform_device *pdev) input_set_drvdata(ts->input, ts); } - ts->base = devm_ioremap_resource(dev, - platform_get_resource(pdev, IORESOURCE_MEM, 0)); + ts->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(ts->base)) return PTR_ERR(ts->base); diff --git a/drivers/input/touchscreen/ts4800-ts.c b/drivers/input/touchscreen/ts4800-ts.c index fed73eeb47b3..5b4f5362c67b 100644 --- a/drivers/input/touchscreen/ts4800-ts.c +++ b/drivers/input/touchscreen/ts4800-ts.c @@ -148,7 +148,6 @@ static int ts4800_ts_probe(struct platform_device *pdev) { struct input_polled_dev *poll_dev; struct ts4800_ts *ts; - struct resource *res; int error; ts = devm_kzalloc(&pdev->dev, sizeof(*ts), GFP_KERNEL); @@ -159,8 +158,7 @@ static int ts4800_ts_probe(struct platform_device *pdev) if (error) return error; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - ts->base = devm_ioremap_resource(&pdev->dev, res); + ts->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(ts->base)) return PTR_ERR(ts->base); diff --git a/drivers/input/touchscreen/w90p910_ts.c b/drivers/input/touchscreen/w90p910_ts.c deleted file mode 100644 index 7893d7fa398c..000000000000 --- a/drivers/input/touchscreen/w90p910_ts.c +++ /dev/null @@ -1,331 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (c) 2008 Nuvoton technology corporation. - * - * Wan ZongShun <mcuos.com@gmail.com> - */ - -#include <linux/delay.h> -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/io.h> -#include <linux/clk.h> -#include <linux/input.h> -#include <linux/interrupt.h> -#include <linux/slab.h> - -/* ADC controller bit defines */ -#define ADC_DELAY 0xf00 -#define ADC_DOWN 0x01 -#define ADC_TSC_Y (0x01 << 8) -#define ADC_TSC_X (0x00 << 8) -#define TSC_FOURWIRE (~(0x03 << 1)) -#define ADC_CLK_EN (0x01 << 28) /* ADC clock enable */ -#define ADC_READ_CON (0x01 << 12) -#define ADC_CONV (0x01 << 13) -#define ADC_SEMIAUTO (0x01 << 14) -#define ADC_WAITTRIG (0x03 << 14) -#define ADC_RST1 (0x01 << 16) -#define ADC_RST0 (0x00 << 16) -#define ADC_EN (0x01 << 17) -#define ADC_INT (0x01 << 18) -#define WT_INT (0x01 << 20) -#define ADC_INT_EN (0x01 << 21) -#define LVD_INT_EN (0x01 << 22) -#define WT_INT_EN (0x01 << 23) -#define ADC_DIV (0x04 << 1) /* div = 6 */ - -enum ts_state { - TS_WAIT_NEW_PACKET, /* We are waiting next touch report */ - TS_WAIT_X_COORD, /* We are waiting for ADC to report X coord */ - TS_WAIT_Y_COORD, /* We are waiting for ADC to report Y coord */ - TS_IDLE, /* Input device is closed, don't do anything */ -}; - -struct w90p910_ts { - struct input_dev *input; - struct timer_list timer; - struct clk *clk; - int irq_num; - void __iomem *ts_reg; - spinlock_t lock; - enum ts_state state; -}; - -static void w90p910_report_event(struct w90p910_ts *w90p910_ts, bool down) -{ - struct input_dev *dev = w90p910_ts->input; - - if (down) { - input_report_abs(dev, ABS_X, - __raw_readl(w90p910_ts->ts_reg + 0x0c)); - input_report_abs(dev, ABS_Y, - __raw_readl(w90p910_ts->ts_reg + 0x10)); - } - - input_report_key(dev, BTN_TOUCH, down); - input_sync(dev); -} - -static void w90p910_prepare_x_reading(struct w90p910_ts *w90p910_ts) -{ - unsigned long ctlreg; - - __raw_writel(ADC_TSC_X, w90p910_ts->ts_reg + 0x04); - ctlreg = __raw_readl(w90p910_ts->ts_reg); - ctlreg &= ~(ADC_WAITTRIG | WT_INT | WT_INT_EN); - ctlreg |= ADC_SEMIAUTO | ADC_INT_EN | ADC_CONV; - __raw_writel(ctlreg, w90p910_ts->ts_reg); - - w90p910_ts->state = TS_WAIT_X_COORD; -} - -static void w90p910_prepare_y_reading(struct w90p910_ts *w90p910_ts) -{ - unsigned long ctlreg; - - __raw_writel(ADC_TSC_Y, w90p910_ts->ts_reg + 0x04); - ctlreg = __raw_readl(w90p910_ts->ts_reg); - ctlreg &= ~(ADC_WAITTRIG | ADC_INT | WT_INT_EN); - ctlreg |= ADC_SEMIAUTO | ADC_INT_EN | ADC_CONV; - __raw_writel(ctlreg, w90p910_ts->ts_reg); - - w90p910_ts->state = TS_WAIT_Y_COORD; -} - -static void w90p910_prepare_next_packet(struct w90p910_ts *w90p910_ts) -{ - unsigned long ctlreg; - - ctlreg = __raw_readl(w90p910_ts->ts_reg); - ctlreg &= ~(ADC_INT | ADC_INT_EN | ADC_SEMIAUTO | ADC_CONV); - ctlreg |= ADC_WAITTRIG | WT_INT_EN; - __raw_writel(ctlreg, w90p910_ts->ts_reg); - - w90p910_ts->state = TS_WAIT_NEW_PACKET; -} - -static irqreturn_t w90p910_ts_interrupt(int irq, void *dev_id) -{ - struct w90p910_ts *w90p910_ts = dev_id; - unsigned long flags; - - spin_lock_irqsave(&w90p910_ts->lock, flags); - - switch (w90p910_ts->state) { - case TS_WAIT_NEW_PACKET: - /* - * The controller only generates interrupts when pen - * is down. - */ - del_timer(&w90p910_ts->timer); - w90p910_prepare_x_reading(w90p910_ts); - break; - - - case TS_WAIT_X_COORD: - w90p910_prepare_y_reading(w90p910_ts); - break; - - case TS_WAIT_Y_COORD: - w90p910_report_event(w90p910_ts, true); - w90p910_prepare_next_packet(w90p910_ts); - mod_timer(&w90p910_ts->timer, jiffies + msecs_to_jiffies(100)); - break; - - case TS_IDLE: - break; - } - - spin_unlock_irqrestore(&w90p910_ts->lock, flags); - - return IRQ_HANDLED; -} - -static void w90p910_check_pen_up(struct timer_list *t) -{ - struct w90p910_ts *w90p910_ts = from_timer(w90p910_ts, t, timer); - unsigned long flags; - - spin_lock_irqsave(&w90p910_ts->lock, flags); - - if (w90p910_ts->state == TS_WAIT_NEW_PACKET && - !(__raw_readl(w90p910_ts->ts_reg + 0x04) & ADC_DOWN)) { - - w90p910_report_event(w90p910_ts, false); - } - - spin_unlock_irqrestore(&w90p910_ts->lock, flags); -} - -static int w90p910_open(struct input_dev *dev) -{ - struct w90p910_ts *w90p910_ts = input_get_drvdata(dev); - unsigned long val; - - /* enable the ADC clock */ - clk_enable(w90p910_ts->clk); - - __raw_writel(ADC_RST1, w90p910_ts->ts_reg); - msleep(1); - __raw_writel(ADC_RST0, w90p910_ts->ts_reg); - msleep(1); - - /* set delay and screen type */ - val = __raw_readl(w90p910_ts->ts_reg + 0x04); - __raw_writel(val & TSC_FOURWIRE, w90p910_ts->ts_reg + 0x04); - __raw_writel(ADC_DELAY, w90p910_ts->ts_reg + 0x08); - - w90p910_ts->state = TS_WAIT_NEW_PACKET; - wmb(); - - /* set trigger mode */ - val = __raw_readl(w90p910_ts->ts_reg); - val |= ADC_WAITTRIG | ADC_DIV | ADC_EN | WT_INT_EN; - __raw_writel(val, w90p910_ts->ts_reg); - - return 0; -} - -static void w90p910_close(struct input_dev *dev) -{ - struct w90p910_ts *w90p910_ts = input_get_drvdata(dev); - unsigned long val; - - /* disable trigger mode */ - - spin_lock_irq(&w90p910_ts->lock); - - w90p910_ts->state = TS_IDLE; - - val = __raw_readl(w90p910_ts->ts_reg); - val &= ~(ADC_WAITTRIG | ADC_DIV | ADC_EN | WT_INT_EN | ADC_INT_EN); - __raw_writel(val, w90p910_ts->ts_reg); - - spin_unlock_irq(&w90p910_ts->lock); - - /* Now that interrupts are shut off we can safely delete timer */ - del_timer_sync(&w90p910_ts->timer); - - /* stop the ADC clock */ - clk_disable(w90p910_ts->clk); -} - -static int w90x900ts_probe(struct platform_device *pdev) -{ - struct w90p910_ts *w90p910_ts; - struct input_dev *input_dev; - struct resource *res; - int err; - - w90p910_ts = kzalloc(sizeof(struct w90p910_ts), GFP_KERNEL); - input_dev = input_allocate_device(); - if (!w90p910_ts || !input_dev) { - err = -ENOMEM; - goto fail1; - } - - w90p910_ts->input = input_dev; - w90p910_ts->state = TS_IDLE; - spin_lock_init(&w90p910_ts->lock); - timer_setup(&w90p910_ts->timer, w90p910_check_pen_up, 0); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - err = -ENXIO; - goto fail1; - } - - if (!request_mem_region(res->start, resource_size(res), - pdev->name)) { - err = -EBUSY; - goto fail1; - } - - w90p910_ts->ts_reg = ioremap(res->start, resource_size(res)); - if (!w90p910_ts->ts_reg) { - err = -ENOMEM; - goto fail2; - } - - w90p910_ts->clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(w90p910_ts->clk)) { - err = PTR_ERR(w90p910_ts->clk); - goto fail3; - } - - input_dev->name = "W90P910 TouchScreen"; - input_dev->phys = "w90p910ts/event0"; - input_dev->id.bustype = BUS_HOST; - input_dev->id.vendor = 0x0005; - input_dev->id.product = 0x0001; - input_dev->id.version = 0x0100; - input_dev->dev.parent = &pdev->dev; - input_dev->open = w90p910_open; - input_dev->close = w90p910_close; - - input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); - input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); - - input_set_abs_params(input_dev, ABS_X, 0, 0x400, 0, 0); - input_set_abs_params(input_dev, ABS_Y, 0, 0x400, 0, 0); - - input_set_drvdata(input_dev, w90p910_ts); - - w90p910_ts->irq_num = platform_get_irq(pdev, 0); - if (request_irq(w90p910_ts->irq_num, w90p910_ts_interrupt, - 0, "w90p910ts", w90p910_ts)) { - err = -EBUSY; - goto fail4; - } - - err = input_register_device(w90p910_ts->input); - if (err) - goto fail5; - - platform_set_drvdata(pdev, w90p910_ts); - - return 0; - -fail5: free_irq(w90p910_ts->irq_num, w90p910_ts); -fail4: clk_put(w90p910_ts->clk); -fail3: iounmap(w90p910_ts->ts_reg); -fail2: release_mem_region(res->start, resource_size(res)); -fail1: input_free_device(input_dev); - kfree(w90p910_ts); - return err; -} - -static int w90x900ts_remove(struct platform_device *pdev) -{ - struct w90p910_ts *w90p910_ts = platform_get_drvdata(pdev); - struct resource *res; - - free_irq(w90p910_ts->irq_num, w90p910_ts); - del_timer_sync(&w90p910_ts->timer); - iounmap(w90p910_ts->ts_reg); - - clk_put(w90p910_ts->clk); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(res->start, resource_size(res)); - - input_unregister_device(w90p910_ts->input); - kfree(w90p910_ts); - - return 0; -} - -static struct platform_driver w90x900ts_driver = { - .probe = w90x900ts_probe, - .remove = w90x900ts_remove, - .driver = { - .name = "nuc900-ts", - }, -}; -module_platform_driver(w90x900ts_driver); - -MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>"); -MODULE_DESCRIPTION("w90p910 touch screen driver!"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:nuc900-ts"); |