diff options
Diffstat (limited to 'drivers/input')
62 files changed, 2731 insertions, 866 deletions
diff --git a/drivers/input/ff-core.c b/drivers/input/ff-core.c index b527308cb52e..66f7ffe8c7e0 100644 --- a/drivers/input/ff-core.c +++ b/drivers/input/ff-core.c @@ -8,9 +8,9 @@ /* #define DEBUG */ +#include <linux/export.h> #include <linux/input.h> #include <linux/limits.h> -#include <linux/module.h> #include <linux/mutex.h> #include <linux/overflow.h> #include <linux/sched.h> diff --git a/drivers/input/ff-memless.c b/drivers/input/ff-memless.c index 91636479ee3c..e0c1c61aae71 100644 --- a/drivers/input/ff-memless.c +++ b/drivers/input/ff-memless.c @@ -10,6 +10,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <linux/export.h> #include <linux/slab.h> #include <linux/input.h> #include <linux/module.h> diff --git a/drivers/input/gameport/gameport.c b/drivers/input/gameport/gameport.c index a832bc46bc92..f4f12dd00fff 100644 --- a/drivers/input/gameport/gameport.c +++ b/drivers/input/gameport/gameport.c @@ -9,6 +9,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <linux/export.h> #include <linux/stddef.h> #include <linux/module.h> #include <linux/io.h> diff --git a/drivers/input/input-compat.c b/drivers/input/input-compat.c index 2ccd3eedbd67..a5043193ead8 100644 --- a/drivers/input/input-compat.c +++ b/drivers/input/input-compat.c @@ -6,6 +6,7 @@ */ #include <linux/export.h> +#include <linux/sprintf.h> #include <linux/uaccess.h> #include "input-compat.h" @@ -94,6 +95,28 @@ int input_ff_effect_from_user(const char __user *buffer, size_t size, return 0; } +int input_bits_to_string(char *buf, int buf_size, unsigned long bits, + bool skip_empty) +{ + int len = 0; + + if (in_compat_syscall()) { + u32 dword = bits >> 32; + if (dword || !skip_empty) + len += snprintf(buf, buf_size, "%x ", dword); + + dword = bits & 0xffffffffUL; + if (dword || !skip_empty || len) + len += snprintf(buf + len, max(buf_size - len, 0), + "%x", dword); + } else { + if (bits || !skip_empty) + len += snprintf(buf, buf_size, "%lx", bits); + } + + return len; +} + #else int input_event_from_user(const char __user *buffer, @@ -126,6 +149,13 @@ int input_ff_effect_from_user(const char __user *buffer, size_t size, return 0; } +int input_bits_to_string(char *buf, int buf_size, unsigned long bits, + bool skip_empty) +{ + return bits || !skip_empty ? + snprintf(buf, buf_size, "%lx", bits) : 0; +} + #endif /* CONFIG_COMPAT */ EXPORT_SYMBOL_GPL(input_event_from_user); diff --git a/drivers/input/input-compat.h b/drivers/input/input-compat.h index 3b7bb12b023b..99c87ceb923d 100644 --- a/drivers/input/input-compat.h +++ b/drivers/input/input-compat.h @@ -75,4 +75,7 @@ int input_event_to_user(char __user *buffer, int input_ff_effect_from_user(const char __user *buffer, size_t size, struct ff_effect *effect); +int input_bits_to_string(char *buf, int buf_size, unsigned long bits, + bool skip_empty); + #endif /* _INPUT_COMPAT_H */ diff --git a/drivers/input/input-mt.c b/drivers/input/input-mt.c index 337006dd9dcf..09f518897d4a 100644 --- a/drivers/input/input-mt.c +++ b/drivers/input/input-mt.c @@ -198,6 +198,7 @@ void input_mt_report_pointer_emulation(struct input_dev *dev, bool use_count) struct input_mt *mt = dev->mt; struct input_mt_slot *oldest; int oldid, count, i; + int p, reported_p = 0; if (!mt) return; @@ -216,6 +217,13 @@ void input_mt_report_pointer_emulation(struct input_dev *dev, bool use_count) oldest = ps; oldid = id; } + if (test_bit(ABS_MT_PRESSURE, dev->absbit)) { + p = input_mt_get_value(ps, ABS_MT_PRESSURE); + if (mt->flags & INPUT_MT_TOTAL_FORCE) + reported_p += p; + else if (oldid == id) + reported_p = p; + } count++; } @@ -245,10 +253,8 @@ void input_mt_report_pointer_emulation(struct input_dev *dev, bool use_count) input_event(dev, EV_ABS, ABS_X, x); input_event(dev, EV_ABS, ABS_Y, y); - if (test_bit(ABS_MT_PRESSURE, dev->absbit)) { - int p = input_mt_get_value(oldest, ABS_MT_PRESSURE); - input_event(dev, EV_ABS, ABS_PRESSURE, p); - } + if (test_bit(ABS_MT_PRESSURE, dev->absbit)) + input_event(dev, EV_ABS, ABS_PRESSURE, reported_p); } else { if (test_bit(ABS_MT_PRESSURE, dev->absbit)) input_event(dev, EV_ABS, ABS_PRESSURE, 0); diff --git a/drivers/input/input-poller.c b/drivers/input/input-poller.c index 9c57713a6151..1ce83d6521bb 100644 --- a/drivers/input/input-poller.c +++ b/drivers/input/input-poller.c @@ -4,6 +4,7 @@ */ #include <linux/device.h> +#include <linux/export.h> #include <linux/input.h> #include <linux/jiffies.h> #include <linux/mutex.h> diff --git a/drivers/input/input.c b/drivers/input/input.c index 1da41324362b..a500e1e276c2 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -8,6 +8,7 @@ #define pr_fmt(fmt) KBUILD_BASENAME ": " fmt +#include <linux/export.h> #include <linux/init.h> #include <linux/types.h> #include <linux/idr.h> @@ -998,41 +999,6 @@ static int input_attach_handler(struct input_dev *dev, struct input_handler *han return error; } -#ifdef CONFIG_COMPAT - -static int input_bits_to_string(char *buf, int buf_size, - unsigned long bits, bool skip_empty) -{ - int len = 0; - - if (in_compat_syscall()) { - u32 dword = bits >> 32; - if (dword || !skip_empty) - len += snprintf(buf, buf_size, "%x ", dword); - - dword = bits & 0xffffffffUL; - if (dword || !skip_empty || len) - len += snprintf(buf + len, max(buf_size - len, 0), - "%x", dword); - } else { - if (bits || !skip_empty) - len += snprintf(buf, buf_size, "%lx", bits); - } - - return len; -} - -#else /* !CONFIG_COMPAT */ - -static int input_bits_to_string(char *buf, int buf_size, - unsigned long bits, bool skip_empty) -{ - return bits || !skip_empty ? - snprintf(buf, buf_size, "%lx", bits) : 0; -} - -#endif - #ifdef CONFIG_PROC_FS static struct proc_dir_entry *proc_bus_input_dir; diff --git a/drivers/input/joystick/iforce/iforce-main.c b/drivers/input/joystick/iforce/iforce-main.c index 55e6321adab9..86d09faa685c 100644 --- a/drivers/input/joystick/iforce/iforce-main.c +++ b/drivers/input/joystick/iforce/iforce-main.c @@ -6,6 +6,7 @@ * USB/RS232 I-Force joysticks and wheels. */ +#include <linux/export.h> #include <linux/unaligned.h> #include "iforce.h" diff --git a/drivers/input/joystick/iforce/iforce-packets.c b/drivers/input/joystick/iforce/iforce-packets.c index 74181d5123cd..fd1cd731d781 100644 --- a/drivers/input/joystick/iforce/iforce-packets.c +++ b/drivers/input/joystick/iforce/iforce-packets.c @@ -6,6 +6,7 @@ * USB/RS232 I-Force joysticks and wheels. */ +#include <linux/export.h> #include <linux/unaligned.h> #include "iforce.h" diff --git a/drivers/input/joystick/psxpad-spi.c b/drivers/input/joystick/psxpad-spi.c index c47fc5f34bd0..f902a56d011f 100644 --- a/drivers/input/joystick/psxpad-spi.c +++ b/drivers/input/joystick/psxpad-spi.c @@ -344,7 +344,11 @@ static int psxpad_spi_probe(struct spi_device *spi) /* (PlayStation 1/2 joypad might be possible works 250kHz/500kHz) */ spi->controller->min_speed_hz = 125000; spi->controller->max_speed_hz = 125000; - spi_setup(spi); + err = spi_setup(spi); + if (err) { + dev_err(&spi->dev, "failed to set up SPI: %d\n", err); + return err; + } /* pad settings */ psxpad_set_motor_level(pad, 0, 0); diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 7c4f309a4cb6..2ff4fef322c2 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -262,24 +262,6 @@ config KEYBOARD_GPIO_POLLED To compile this driver as a module, choose M here: the module will be called gpio_keys_polled. -config KEYBOARD_TCA6416 - tristate "TCA6416/TCA6408A Keypad Support" - depends on I2C - help - This driver implements basic keypad functionality - for keys connected through TCA6416/TCA6408A IO expanders. - - Say Y here if your device has keys connected to - TCA6416/TCA6408A IO expander. Your board-specific setup logic - must also provide pin-mask details(of which TCA6416 pins - are used for keypad). - - If enabled the entire TCA6416 device will be managed through - this driver. - - To compile this driver as a module, choose M here: the - module will be called tca6416_keypad. - config KEYBOARD_TCA8418 tristate "TCA8418 Keypad Support" depends on I2C @@ -422,6 +404,18 @@ config KEYBOARD_MAX7359 To compile this driver as a module, choose M here: the module will be called max7359_keypad. +config KEYBOARD_MAX7360 + tristate "Maxim MAX7360 Key Switch Controller" + select INPUT_MATRIXKMAP + depends on I2C + depends on MFD_MAX7360 + help + If you say yes here you get support for the keypad controller on the + Maxim MAX7360 I/O Expander. + + To compile this driver as a module, choose M here: the module will be + called max7360_keypad. + config KEYBOARD_MPR121 tristate "Freescale MPR121 Touchkey" depends on I2C diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 8bc20ab2b103..2d906e14f3e2 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -23,7 +23,6 @@ obj-$(CONFIG_KEYBOARD_EP93XX) += ep93xx_keypad.o obj-$(CONFIG_KEYBOARD_GOLDFISH_EVENTS) += goldfish_events.o obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o obj-$(CONFIG_KEYBOARD_GPIO_POLLED) += gpio_keys_polled.o -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 @@ -42,6 +41,7 @@ obj-$(CONFIG_KEYBOARD_LPC32XX) += lpc32xx-keys.o obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o obj-$(CONFIG_KEYBOARD_MATRIX) += matrix_keypad.o obj-$(CONFIG_KEYBOARD_MAX7359) += max7359_keypad.o +obj-$(CONFIG_KEYBOARD_MAX7360) += max7360-keypad.o obj-$(CONFIG_KEYBOARD_MPR121) += mpr121_touchkey.o obj-$(CONFIG_KEYBOARD_MT6779) += mt6779-keypad.o obj-$(CONFIG_KEYBOARD_MTK_PMIC) += mtk-pmic-keys.o diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c index c1e53d87c8a7..f7209c8ebbcc 100644 --- a/drivers/input/keyboard/cros_ec_keyb.c +++ b/drivers/input/keyboard/cros_ec_keyb.c @@ -705,6 +705,12 @@ static int cros_ec_keyb_probe(struct platform_device *pdev) ec = dev_get_drvdata(pdev->dev.parent); if (!ec) return -EPROBE_DEFER; + /* + * Even if the cros_ec_device pointer is available, still need to check + * if the device is fully registered before using it. + */ + if (!cros_ec_device_registered(ec)) + return -EPROBE_DEFER; ckdev = devm_kzalloc(dev, sizeof(*ckdev), GFP_KERNEL); if (!ckdev) diff --git a/drivers/input/keyboard/max7360-keypad.c b/drivers/input/keyboard/max7360-keypad.c new file mode 100644 index 000000000000..503be952b0a6 --- /dev/null +++ b/drivers/input/keyboard/max7360-keypad.c @@ -0,0 +1,308 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2025 Bootlin + * + * Author: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com> + */ + +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/dev_printk.h> +#include <linux/device/devres.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/input.h> +#include <linux/input/matrix_keypad.h> +#include <linux/interrupt.h> +#include <linux/mfd/max7360.h> +#include <linux/mod_devicetable.h> +#include <linux/minmax.h> +#include <linux/module.h> +#include <linux/property.h> +#include <linux/platform_device.h> +#include <linux/pm_wakeirq.h> +#include <linux/regmap.h> + +struct max7360_keypad { + struct input_dev *input; + unsigned int rows; + unsigned int cols; + unsigned int debounce_ms; + int irq; + struct regmap *regmap; + unsigned short keycodes[MAX7360_MAX_KEY_ROWS * MAX7360_MAX_KEY_COLS]; +}; + +static irqreturn_t max7360_keypad_irq(int irq, void *data) +{ + struct max7360_keypad *max7360_keypad = data; + struct device *dev = max7360_keypad->input->dev.parent; + unsigned int val; + unsigned int row, col; + unsigned int release; + unsigned int code; + int error; + + error = regmap_read(max7360_keypad->regmap, MAX7360_REG_KEYFIFO, &val); + if (error) { + dev_err(dev, "Failed to read MAX7360 FIFO"); + return IRQ_NONE; + } + + /* FIFO overflow: ignore it and get next event. */ + if (val == MAX7360_FIFO_OVERFLOW) { + dev_warn(dev, "max7360 FIFO overflow"); + error = regmap_read_poll_timeout(max7360_keypad->regmap, MAX7360_REG_KEYFIFO, + val, val != MAX7360_FIFO_OVERFLOW, 0, 1000); + if (error) { + dev_err(dev, "Failed to empty MAX7360 FIFO"); + return IRQ_NONE; + } + } + + if (val == MAX7360_FIFO_EMPTY) { + dev_dbg(dev, "Got a spurious interrupt"); + + return IRQ_NONE; + } + + row = FIELD_GET(MAX7360_FIFO_ROW, val); + col = FIELD_GET(MAX7360_FIFO_COL, val); + release = val & MAX7360_FIFO_RELEASE; + + code = MATRIX_SCAN_CODE(row, col, get_count_order(max7360_keypad->cols)); + + dev_dbg(dev, "key[%d:%d] %s\n", row, col, release ? "release" : "press"); + + input_event(max7360_keypad->input, EV_MSC, MSC_SCAN, code); + input_report_key(max7360_keypad->input, max7360_keypad->keycodes[code], !release); + input_sync(max7360_keypad->input); + + return IRQ_HANDLED; +} + +static int max7360_keypad_open(struct input_dev *pdev) +{ + struct max7360_keypad *max7360_keypad = input_get_drvdata(pdev); + struct device *dev = max7360_keypad->input->dev.parent; + int error; + + /* Somebody is using the device: get out of sleep. */ + error = regmap_write_bits(max7360_keypad->regmap, MAX7360_REG_CONFIG, + MAX7360_CFG_SLEEP, MAX7360_CFG_SLEEP); + if (error) + dev_err(dev, "Failed to write max7360 configuration: %d\n", error); + + return error; +} + +static void max7360_keypad_close(struct input_dev *pdev) +{ + struct max7360_keypad *max7360_keypad = input_get_drvdata(pdev); + struct device *dev = max7360_keypad->input->dev.parent; + int error; + + /* Nobody is using the device anymore: go to sleep. */ + error = regmap_write_bits(max7360_keypad->regmap, MAX7360_REG_CONFIG, MAX7360_CFG_SLEEP, 0); + if (error) + dev_err(dev, "Failed to write max7360 configuration: %d\n", error); +} + +static int max7360_keypad_hw_init(struct max7360_keypad *max7360_keypad) +{ + struct device *dev = max7360_keypad->input->dev.parent; + unsigned int val; + int error; + + val = max7360_keypad->debounce_ms - MAX7360_DEBOUNCE_MIN; + error = regmap_write_bits(max7360_keypad->regmap, MAX7360_REG_DEBOUNCE, + MAX7360_DEBOUNCE, + FIELD_PREP(MAX7360_DEBOUNCE, val)); + if (error) + return dev_err_probe(dev, error, + "Failed to write max7360 debounce configuration\n"); + + error = regmap_write_bits(max7360_keypad->regmap, MAX7360_REG_INTERRUPT, + MAX7360_INTERRUPT_TIME_MASK, + FIELD_PREP(MAX7360_INTERRUPT_TIME_MASK, 1)); + if (error) + return dev_err_probe(dev, error, + "Failed to write max7360 keypad interrupt configuration\n"); + + return 0; +} + +static int max7360_keypad_build_keymap(struct max7360_keypad *max7360_keypad) +{ + struct input_dev *input_dev = max7360_keypad->input; + struct device *dev = input_dev->dev.parent->parent; + struct matrix_keymap_data keymap_data; + const char *propname = "linux,keymap"; + unsigned int max_keys; + int error; + int size; + + size = device_property_count_u32(dev, propname); + if (size <= 0) { + dev_err(dev, "missing or malformed property %s: %d\n", propname, size); + return size < 0 ? size : -EINVAL; + } + + max_keys = max7360_keypad->cols * max7360_keypad->rows; + if (size > max_keys) { + dev_err(dev, "%s size overflow (%d vs max %u)\n", propname, size, max_keys); + return -EINVAL; + } + + u32 *keys __free(kfree) = kmalloc_array(size, sizeof(*keys), GFP_KERNEL); + if (!keys) + return -ENOMEM; + + error = device_property_read_u32_array(dev, propname, keys, size); + if (error) { + dev_err(dev, "failed to read %s property: %d\n", propname, error); + return error; + } + + keymap_data.keymap = keys; + keymap_data.keymap_size = size; + error = matrix_keypad_build_keymap(&keymap_data, NULL, + max7360_keypad->rows, max7360_keypad->cols, + max7360_keypad->keycodes, max7360_keypad->input); + if (error) + return error; + + return 0; +} + +static int max7360_keypad_parse_fw(struct device *dev, + struct max7360_keypad *max7360_keypad, + bool *autorepeat) +{ + int error; + + error = matrix_keypad_parse_properties(dev->parent, &max7360_keypad->rows, + &max7360_keypad->cols); + if (error) + return error; + + if (!max7360_keypad->rows || !max7360_keypad->cols || + max7360_keypad->rows > MAX7360_MAX_KEY_ROWS || + max7360_keypad->cols > MAX7360_MAX_KEY_COLS) { + dev_err(dev, "Invalid number of columns or rows (%ux%u)\n", + max7360_keypad->cols, max7360_keypad->rows); + return -EINVAL; + } + + *autorepeat = device_property_read_bool(dev->parent, "autorepeat"); + + max7360_keypad->debounce_ms = MAX7360_DEBOUNCE_MIN; + error = device_property_read_u32(dev->parent, "keypad-debounce-delay-ms", + &max7360_keypad->debounce_ms); + if (error == -EINVAL) { + dev_info(dev, "Using default keypad-debounce-delay-ms: %u\n", + max7360_keypad->debounce_ms); + } else if (error < 0) { + dev_err(dev, "Failed to read keypad-debounce-delay-ms property\n"); + return error; + } + + if (!in_range(max7360_keypad->debounce_ms, MAX7360_DEBOUNCE_MIN, + MAX7360_DEBOUNCE_MAX - MAX7360_DEBOUNCE_MIN + 1)) { + dev_err(dev, "Invalid keypad-debounce-delay-ms: %u, should be between %u and %u.\n", + max7360_keypad->debounce_ms, MAX7360_DEBOUNCE_MIN, MAX7360_DEBOUNCE_MAX); + return -EINVAL; + } + + return 0; +} + +static int max7360_keypad_probe(struct platform_device *pdev) +{ + struct max7360_keypad *max7360_keypad; + struct device *dev = &pdev->dev; + struct input_dev *input; + struct regmap *regmap; + bool autorepeat; + int error; + int irq; + + regmap = dev_get_regmap(dev->parent, NULL); + if (!regmap) + return dev_err_probe(dev, -ENODEV, "Could not get parent regmap\n"); + + irq = fwnode_irq_get_byname(dev_fwnode(dev->parent), "intk"); + if (irq < 0) + return dev_err_probe(dev, irq, "Failed to get IRQ\n"); + + max7360_keypad = devm_kzalloc(dev, sizeof(*max7360_keypad), GFP_KERNEL); + if (!max7360_keypad) + return -ENOMEM; + + max7360_keypad->regmap = regmap; + + error = max7360_keypad_parse_fw(dev, max7360_keypad, &autorepeat); + if (error) + return error; + + input = devm_input_allocate_device(dev); + if (!input) + return -ENOMEM; + + max7360_keypad->input = input; + + input->id.bustype = BUS_I2C; + input->name = pdev->name; + input->open = max7360_keypad_open; + input->close = max7360_keypad_close; + + error = max7360_keypad_build_keymap(max7360_keypad); + if (error) + return dev_err_probe(dev, error, "Failed to build keymap\n"); + + input_set_capability(input, EV_MSC, MSC_SCAN); + if (autorepeat) + __set_bit(EV_REP, input->evbit); + + input_set_drvdata(input, max7360_keypad); + + error = devm_request_threaded_irq(dev, irq, NULL, max7360_keypad_irq, + IRQF_ONESHOT, + "max7360-keypad", max7360_keypad); + if (error) + return dev_err_probe(dev, error, "Failed to register interrupt\n"); + + error = input_register_device(input); + if (error) + return dev_err_probe(dev, error, "Could not register input device\n"); + + error = max7360_keypad_hw_init(max7360_keypad); + if (error) + return dev_err_probe(dev, error, "Failed to initialize max7360 keypad\n"); + + device_init_wakeup(dev, true); + error = dev_pm_set_wake_irq(dev, irq); + if (error) + dev_warn(dev, "Failed to set up wakeup irq: %d\n", error); + + return 0; +} + +static void max7360_keypad_remove(struct platform_device *pdev) +{ + dev_pm_clear_wake_irq(&pdev->dev); + device_init_wakeup(&pdev->dev, false); +} + +static struct platform_driver max7360_keypad_driver = { + .driver = { + .name = "max7360-keypad", + }, + .probe = max7360_keypad_probe, + .remove = max7360_keypad_remove, +}; +module_platform_driver(max7360_keypad_driver); + +MODULE_DESCRIPTION("MAX7360 Keypad driver"); +MODULE_AUTHOR("Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/keyboard/pxa27x_keypad.c b/drivers/input/keyboard/pxa27x_keypad.c index 38ec619aa359..4519eecb317b 100644 --- a/drivers/input/keyboard/pxa27x_keypad.c +++ b/drivers/input/keyboard/pxa27x_keypad.c @@ -12,7 +12,8 @@ * on some suggestions by Nicolas Pitre <nico@fluxnic.net>. */ - +#include <linux/bits.h> +#include <linux/bitfield.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/interrupt.h> @@ -20,124 +21,148 @@ #include <linux/io.h> #include <linux/device.h> #include <linux/platform_device.h> +#include <linux/property.h> #include <linux/clk.h> #include <linux/err.h> #include <linux/input/matrix_keypad.h> #include <linux/slab.h> #include <linux/of.h> -#include <linux/platform_data/keypad-pxa27x.h> /* * Keypad Controller registers */ -#define KPC 0x0000 /* Keypad Control register */ -#define KPDK 0x0008 /* Keypad Direct Key register */ -#define KPREC 0x0010 /* Keypad Rotary Encoder register */ -#define KPMK 0x0018 /* Keypad Matrix Key register */ -#define KPAS 0x0020 /* Keypad Automatic Scan register */ +#define KPC 0x0000 /* Keypad Control register */ +#define KPDK 0x0008 /* Keypad Direct Key register */ +#define KPREC 0x0010 /* Keypad Rotary Encoder register */ +#define KPMK 0x0018 /* Keypad Matrix Key register */ +#define KPAS 0x0020 /* Keypad Automatic Scan register */ /* Keypad Automatic Scan Multiple Key Presser register 0-3 */ -#define KPASMKP0 0x0028 -#define KPASMKP1 0x0030 -#define KPASMKP2 0x0038 -#define KPASMKP3 0x0040 -#define KPKDI 0x0048 +#define KPASMKP0 0x0028 +#define KPASMKP1 0x0030 +#define KPASMKP2 0x0038 +#define KPASMKP3 0x0040 +#define KPKDI 0x0048 /* bit definitions */ -#define KPC_MKRN(n) ((((n) - 1) & 0x7) << 26) /* matrix key row number */ -#define KPC_MKCN(n) ((((n) - 1) & 0x7) << 23) /* matrix key column number */ -#define KPC_DKN(n) ((((n) - 1) & 0x7) << 6) /* direct key number */ - -#define KPC_AS (0x1 << 30) /* Automatic Scan bit */ -#define KPC_ASACT (0x1 << 29) /* Automatic Scan on Activity */ -#define KPC_MI (0x1 << 22) /* Matrix interrupt bit */ -#define KPC_IMKP (0x1 << 21) /* Ignore Multiple Key Press */ - -#define KPC_MS(n) (0x1 << (13 + (n))) /* Matrix scan line 'n' */ -#define KPC_MS_ALL (0xff << 13) - -#define KPC_ME (0x1 << 12) /* Matrix Keypad Enable */ -#define KPC_MIE (0x1 << 11) /* Matrix Interrupt Enable */ -#define KPC_DK_DEB_SEL (0x1 << 9) /* Direct Keypad Debounce Select */ -#define KPC_DI (0x1 << 5) /* Direct key interrupt bit */ -#define KPC_RE_ZERO_DEB (0x1 << 4) /* Rotary Encoder Zero Debounce */ -#define KPC_REE1 (0x1 << 3) /* Rotary Encoder1 Enable */ -#define KPC_REE0 (0x1 << 2) /* Rotary Encoder0 Enable */ -#define KPC_DE (0x1 << 1) /* Direct Keypad Enable */ -#define KPC_DIE (0x1 << 0) /* Direct Keypad interrupt Enable */ - -#define KPDK_DKP (0x1 << 31) -#define KPDK_DK(n) ((n) & 0xff) - -#define KPREC_OF1 (0x1 << 31) -#define kPREC_UF1 (0x1 << 30) -#define KPREC_OF0 (0x1 << 15) -#define KPREC_UF0 (0x1 << 14) - -#define KPREC_RECOUNT0(n) ((n) & 0xff) -#define KPREC_RECOUNT1(n) (((n) >> 16) & 0xff) - -#define KPMK_MKP (0x1 << 31) -#define KPAS_SO (0x1 << 31) -#define KPASMKPx_SO (0x1 << 31) - -#define KPAS_MUKP(n) (((n) >> 26) & 0x1f) -#define KPAS_RP(n) (((n) >> 4) & 0xf) -#define KPAS_CP(n) ((n) & 0xf) - -#define KPASMKP_MKC_MASK (0xff) +#define KPC_MKRN_MASK GENMASK(28, 26) +#define KPC_MKCN_MASK GENMASK(25, 23) +#define KPC_DKN_MASK GENMASK(8, 6) +#define KPC_MKRN(n) FIELD_PREP(KPC_MKRN_MASK, (n) - 1) +#define KPC_MKCN(n) FIELD_PREP(KPC_MKCN_MASK, (n) - 1) +#define KPC_DKN(n) FIELD_PREP(KPC_DKN_MASK, (n) - 1) + +#define KPC_AS BIT(30) /* Automatic Scan bit */ +#define KPC_ASACT BIT(29) /* Automatic Scan on Activity */ +#define KPC_MI BIT(22) /* Matrix interrupt bit */ +#define KPC_IMKP BIT(21) /* Ignore Multiple Key Press */ + +#define KPC_MS(n) BIT(13 + (n)) /* Matrix scan line 'n' */ +#define KPC_MS_ALL GENMASK(20, 13) + +#define KPC_ME BIT(12) /* Matrix Keypad Enable */ +#define KPC_MIE BIT(11) /* Matrix Interrupt Enable */ +#define KPC_DK_DEB_SEL BIT(9) /* Direct Keypad Debounce Select */ +#define KPC_DI BIT(5) /* Direct key interrupt bit */ +#define KPC_RE_ZERO_DEB BIT(4) /* Rotary Encoder Zero Debounce */ +#define KPC_REE1 BIT(3) /* Rotary Encoder1 Enable */ +#define KPC_REE0 BIT(2) /* Rotary Encoder0 Enable */ +#define KPC_DE BIT(1) /* Direct Keypad Enable */ +#define KPC_DIE BIT(0) /* Direct Keypad interrupt Enable */ + +#define KPDK_DKP BIT(31) +#define KPDK_DK_MASK GENMASK(7, 0) +#define KPDK_DK(n) FIELD_GET(KPDK_DK_MASK, n) + +#define KPREC_OF1 BIT(31) +#define KPREC_UF1 BIT(30) +#define KPREC_OF0 BIT(15) +#define KPREC_UF0 BIT(14) + +#define KPREC_RECOUNT0_MASK GENMASK(7, 0) +#define KPREC_RECOUNT1_MASK GENMASK(23, 16) +#define KPREC_RECOUNT0(n) FIELD_GET(KPREC_RECOUNT0_MASK, n) +#define KPREC_RECOUNT1(n) FIELD_GET(KPREC_RECOUNT1_MASK, n) + +#define KPMK_MKP BIT(31) +#define KPAS_SO BIT(31) +#define KPASMKPx_SO BIT(31) + +#define KPAS_MUKP_MASK GENMASK(30, 26) +#define KPAS_RP_MASK GENMASK(7, 4) +#define KPAS_CP_MASK GENMASK(3, 0) +#define KPAS_MUKP(n) FIELD_GET(KPAS_MUKP_MASK, n) +#define KPAS_RP(n) FIELD_GET(KPAS_RP_MASK, n) +#define KPAS_CP(n) FIELD_GET(KPAS_CP_MASK, n) + +#define KPASMKP_MKC_MASK GENMASK(7, 0) #define keypad_readl(off) __raw_readl(keypad->mmio_base + (off)) #define keypad_writel(off, v) __raw_writel((v), keypad->mmio_base + (off)) +#define MAX_MATRIX_KEY_ROWS 8 +#define MAX_MATRIX_KEY_COLS 8 +#define MAX_DIRECT_KEY_NUM 8 +#define MAX_ROTARY_ENCODERS 2 + #define MAX_MATRIX_KEY_NUM (MAX_MATRIX_KEY_ROWS * MAX_MATRIX_KEY_COLS) #define MAX_KEYPAD_KEYS (MAX_MATRIX_KEY_NUM + MAX_DIRECT_KEY_NUM) -struct pxa27x_keypad { - const struct pxa27x_keypad_platform_data *pdata; +struct pxa27x_keypad_rotary { + unsigned short *key_codes; + int rel_code; + bool enabled; +}; +struct pxa27x_keypad { struct clk *clk; struct input_dev *input_dev; void __iomem *mmio_base; int irq; - unsigned short keycodes[MAX_KEYPAD_KEYS]; - int rotary_rel_code[2]; - + unsigned int matrix_key_rows; + unsigned int matrix_key_cols; unsigned int row_shift; + unsigned int direct_key_num; + unsigned int direct_key_mask; + bool direct_key_low_active; + + /* key debounce interval */ + unsigned int debounce_interval; + + unsigned short keycodes[MAX_KEYPAD_KEYS]; + /* state row bits of each column scan */ - uint32_t matrix_key_state[MAX_MATRIX_KEY_COLS]; - uint32_t direct_key_state; + u32 matrix_key_state[MAX_MATRIX_KEY_COLS]; + u32 direct_key_state; - unsigned int direct_key_mask; + struct pxa27x_keypad_rotary rotary[MAX_ROTARY_ENCODERS]; }; -#ifdef CONFIG_OF -static int pxa27x_keypad_matrix_key_parse_dt(struct pxa27x_keypad *keypad, - struct pxa27x_keypad_platform_data *pdata) +static int pxa27x_keypad_matrix_key_parse(struct pxa27x_keypad *keypad) { struct input_dev *input_dev = keypad->input_dev; struct device *dev = input_dev->dev.parent; - u32 rows, cols; int error; - error = matrix_keypad_parse_properties(dev, &rows, &cols); + error = matrix_keypad_parse_properties(dev, &keypad->matrix_key_rows, + &keypad->matrix_key_cols); if (error) return error; - if (rows > MAX_MATRIX_KEY_ROWS || cols > MAX_MATRIX_KEY_COLS) { + if (keypad->matrix_key_rows > MAX_MATRIX_KEY_ROWS || + keypad->matrix_key_cols > MAX_MATRIX_KEY_COLS) { dev_err(dev, "rows or cols exceeds maximum value\n"); return -EINVAL; } - pdata->matrix_key_rows = rows; - pdata->matrix_key_cols = cols; + keypad->row_shift = get_count_order(keypad->matrix_key_cols); error = matrix_keypad_build_keymap(NULL, NULL, - pdata->matrix_key_rows, - pdata->matrix_key_cols, + keypad->matrix_key_rows, + keypad->matrix_key_cols, keypad->keycodes, input_dev); if (error) return error; @@ -145,20 +170,17 @@ static int pxa27x_keypad_matrix_key_parse_dt(struct pxa27x_keypad *keypad, return 0; } -static int pxa27x_keypad_direct_key_parse_dt(struct pxa27x_keypad *keypad, - struct pxa27x_keypad_platform_data *pdata) +static int pxa27x_keypad_direct_key_parse(struct pxa27x_keypad *keypad) { struct input_dev *input_dev = keypad->input_dev; struct device *dev = input_dev->dev.parent; - struct device_node *np = dev->of_node; - const __be16 *prop; unsigned short code; - unsigned int proplen, size; + int count; int i; int error; - error = of_property_read_u32(np, "marvell,direct-key-count", - &pdata->direct_key_num); + error = device_property_read_u32(dev, "marvell,direct-key-count", + &keypad->direct_key_num); if (error) { /* * If do not have marvel,direct-key-count defined, @@ -167,151 +189,121 @@ static int pxa27x_keypad_direct_key_parse_dt(struct pxa27x_keypad *keypad, return error == -EINVAL ? 0 : error; } - error = of_property_read_u32(np, "marvell,direct-key-mask", - &pdata->direct_key_mask); + error = device_property_read_u32(dev, "marvell,direct-key-mask", + &keypad->direct_key_mask); if (error) { if (error != -EINVAL) return error; /* * If marvell,direct-key-mask is not defined, driver will use - * default value. Default value is set when configure the keypad. + * a default value based on number of direct keys set up. + * The default value is calculated in pxa27x_keypad_config(). */ - pdata->direct_key_mask = 0; + keypad->direct_key_mask = 0; } - pdata->direct_key_low_active = of_property_read_bool(np, - "marvell,direct-key-low-active"); - - prop = of_get_property(np, "marvell,direct-key-map", &proplen); - if (!prop) - return -EINVAL; + keypad->direct_key_low_active = + device_property_read_bool(dev, "marvell,direct-key-low-active"); - if (proplen % sizeof(u16)) + count = device_property_count_u16(dev, "marvell,direct-key-map"); + if (count <= 0 || count > MAX_DIRECT_KEY_NUM) return -EINVAL; - size = proplen / sizeof(u16); + error = device_property_read_u16_array(dev, "marvell,direct-key-map", + &keypad->keycodes[MAX_MATRIX_KEY_NUM], + count); - /* Only MAX_DIRECT_KEY_NUM is accepted.*/ - if (size > MAX_DIRECT_KEY_NUM) - return -EINVAL; - - for (i = 0; i < size; i++) { - code = be16_to_cpup(prop + i); - keypad->keycodes[MAX_MATRIX_KEY_NUM + i] = code; + for (i = 0; i < count; i++) { + code = keypad->keycodes[MAX_MATRIX_KEY_NUM + i]; __set_bit(code, input_dev->keybit); } return 0; } -static int pxa27x_keypad_rotary_parse_dt(struct pxa27x_keypad *keypad, - struct pxa27x_keypad_platform_data *pdata) +static int pxa27x_keypad_rotary_parse(struct pxa27x_keypad *keypad) { - const __be32 *prop; - int i, relkey_ret; - unsigned int code, proplen; - const char *rotaryname[2] = { - "marvell,rotary0", "marvell,rotary1"}; - const char relkeyname[] = {"marvell,rotary-rel-key"}; + static const char * const rotaryname[] = { "marvell,rotary0", "marvell,rotary1" }; struct input_dev *input_dev = keypad->input_dev; struct device *dev = input_dev->dev.parent; - struct device_node *np = dev->of_node; - - relkey_ret = of_property_read_u32(np, relkeyname, &code); - /* if can read correct rotary key-code, we do not need this. */ - if (relkey_ret == 0) { - unsigned short relcode; + struct pxa27x_keypad_rotary *encoder; + unsigned int code; + int i; + int error; - /* rotary0 taks lower half, rotary1 taks upper half. */ - relcode = code & 0xffff; - pdata->rotary0_rel_code = (code & 0xffff); - __set_bit(relcode, input_dev->relbit); + error = device_property_read_u32(dev, "marvell,rotary-rel-key", &code); + if (!error) { + for (i = 0; i < MAX_ROTARY_ENCODERS; i++, code >>= 16) { + encoder = &keypad->rotary[i]; + encoder->enabled = true; + encoder->rel_code = code & 0xffff; + input_set_capability(input_dev, EV_REL, encoder->rel_code); + } - relcode = code >> 16; - pdata->rotary1_rel_code = relcode; - __set_bit(relcode, input_dev->relbit); + return 0; } - for (i = 0; i < 2; i++) { - prop = of_get_property(np, rotaryname[i], &proplen); + for (i = 0; i < MAX_ROTARY_ENCODERS; i++) { + encoder = &keypad->rotary[i]; + /* * If the prop is not set, it means keypad does not need * initialize the rotaryX. */ - if (!prop) + if (!device_property_present(dev, rotaryname[i])) continue; - code = be32_to_cpup(prop); + error = device_property_read_u32(dev, rotaryname[i], &code); + if (error) + return error; + /* * Not all up/down key code are valid. * Now we depends on direct-rel-code. */ - if ((!(code & 0xffff) || !(code >> 16)) && relkey_ret) { - return relkey_ret; - } else { - unsigned int n = MAX_MATRIX_KEY_NUM + (i << 1); - unsigned short keycode; - - keycode = code & 0xffff; - keypad->keycodes[n] = keycode; - __set_bit(keycode, input_dev->keybit); - - keycode = code >> 16; - keypad->keycodes[n + 1] = keycode; - __set_bit(keycode, input_dev->keybit); - - if (i == 0) - pdata->rotary0_rel_code = -1; - else - pdata->rotary1_rel_code = -1; - } - if (i == 0) - pdata->enable_rotary0 = 1; - else - pdata->enable_rotary1 = 1; - } + if (!(code & 0xffff) || !(code >> 16)) + return -EINVAL; + + encoder->enabled = true; + encoder->rel_code = -1; + encoder->key_codes = &keypad->keycodes[MAX_MATRIX_KEY_NUM + i * 2]; + encoder->key_codes[0] = code & 0xffff; + encoder->key_codes[1] = code >> 16; - keypad->rotary_rel_code[0] = pdata->rotary0_rel_code; - keypad->rotary_rel_code[1] = pdata->rotary1_rel_code; + input_set_capability(input_dev, EV_KEY, encoder->key_codes[0]); + input_set_capability(input_dev, EV_KEY, encoder->key_codes[1]); + } return 0; } -static int pxa27x_keypad_build_keycode_from_dt(struct pxa27x_keypad *keypad) +static int pxa27x_keypad_parse_properties(struct pxa27x_keypad *keypad) { struct input_dev *input_dev = keypad->input_dev; struct device *dev = input_dev->dev.parent; - struct device_node *np = dev->of_node; - struct pxa27x_keypad_platform_data *pdata; int error; - pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) { - dev_err(dev, "failed to allocate memory for pdata\n"); - return -ENOMEM; - } - - error = pxa27x_keypad_matrix_key_parse_dt(keypad, pdata); + error = pxa27x_keypad_matrix_key_parse(keypad); if (error) { dev_err(dev, "failed to parse matrix key\n"); return error; } - error = pxa27x_keypad_direct_key_parse_dt(keypad, pdata); + error = pxa27x_keypad_direct_key_parse(keypad); if (error) { dev_err(dev, "failed to parse direct key\n"); return error; } - error = pxa27x_keypad_rotary_parse_dt(keypad, pdata); + error = pxa27x_keypad_rotary_parse(keypad); if (error) { dev_err(dev, "failed to parse rotary key\n"); return error; } - error = of_property_read_u32(np, "marvell,debounce-interval", - &pdata->debounce_interval); + error = device_property_read_u32(dev, "marvell,debounce-interval", + &keypad->debounce_interval); if (error) { dev_err(dev, "failed to parse debounce-interval\n"); return error; @@ -323,95 +315,15 @@ static int pxa27x_keypad_build_keycode_from_dt(struct pxa27x_keypad *keypad) */ input_dev->keycodemax = ARRAY_SIZE(keypad->keycodes); - keypad->pdata = pdata; - return 0; -} - -#else - -static int pxa27x_keypad_build_keycode_from_dt(struct pxa27x_keypad *keypad) -{ - dev_info(keypad->input_dev->dev.parent, "missing platform data\n"); - - return -EINVAL; -} - -#endif - -static int pxa27x_keypad_build_keycode(struct pxa27x_keypad *keypad) -{ - const struct pxa27x_keypad_platform_data *pdata = keypad->pdata; - struct input_dev *input_dev = keypad->input_dev; - unsigned short keycode; - int i; - int error; - - error = matrix_keypad_build_keymap(pdata->matrix_keymap_data, NULL, - pdata->matrix_key_rows, - pdata->matrix_key_cols, - keypad->keycodes, input_dev); - if (error) - return error; - - /* - * The keycodes may not only include matrix keys but also the direct - * or rotary keys. - */ - input_dev->keycodemax = ARRAY_SIZE(keypad->keycodes); - - /* For direct keys. */ - for (i = 0; i < pdata->direct_key_num; i++) { - keycode = pdata->direct_key_map[i]; - keypad->keycodes[MAX_MATRIX_KEY_NUM + i] = keycode; - __set_bit(keycode, input_dev->keybit); - } - - if (pdata->enable_rotary0) { - if (pdata->rotary0_up_key && pdata->rotary0_down_key) { - keycode = pdata->rotary0_up_key; - keypad->keycodes[MAX_MATRIX_KEY_NUM + 0] = keycode; - __set_bit(keycode, input_dev->keybit); - - keycode = pdata->rotary0_down_key; - keypad->keycodes[MAX_MATRIX_KEY_NUM + 1] = keycode; - __set_bit(keycode, input_dev->keybit); - - keypad->rotary_rel_code[0] = -1; - } else { - keypad->rotary_rel_code[0] = pdata->rotary0_rel_code; - __set_bit(pdata->rotary0_rel_code, input_dev->relbit); - } - } - - if (pdata->enable_rotary1) { - if (pdata->rotary1_up_key && pdata->rotary1_down_key) { - keycode = pdata->rotary1_up_key; - keypad->keycodes[MAX_MATRIX_KEY_NUM + 2] = keycode; - __set_bit(keycode, input_dev->keybit); - - keycode = pdata->rotary1_down_key; - keypad->keycodes[MAX_MATRIX_KEY_NUM + 3] = keycode; - __set_bit(keycode, input_dev->keybit); - - keypad->rotary_rel_code[1] = -1; - } else { - keypad->rotary_rel_code[1] = pdata->rotary1_rel_code; - __set_bit(pdata->rotary1_rel_code, input_dev->relbit); - } - } - - __clear_bit(KEY_RESERVED, input_dev->keybit); - return 0; } static void pxa27x_keypad_scan_matrix(struct pxa27x_keypad *keypad) { - const struct pxa27x_keypad_platform_data *pdata = keypad->pdata; struct input_dev *input_dev = keypad->input_dev; int row, col, num_keys_pressed = 0; - uint32_t new_state[MAX_MATRIX_KEY_COLS]; - uint32_t kpas = keypad_readl(KPAS); + u32 new_state[MAX_MATRIX_KEY_COLS]; + u32 kpas = keypad_readl(KPAS); num_keys_pressed = KPAS_MUKP(kpas); @@ -425,19 +337,19 @@ static void pxa27x_keypad_scan_matrix(struct pxa27x_keypad *keypad) row = KPAS_RP(kpas); /* if invalid row/col, treat as no key pressed */ - if (col >= pdata->matrix_key_cols || - row >= pdata->matrix_key_rows) + if (col >= keypad->matrix_key_cols || + row >= keypad->matrix_key_rows) goto scan; - new_state[col] = (1 << row); + new_state[col] = BIT(row); goto scan; } if (num_keys_pressed > 1) { - uint32_t kpasmkp0 = keypad_readl(KPASMKP0); - uint32_t kpasmkp1 = keypad_readl(KPASMKP1); - uint32_t kpasmkp2 = keypad_readl(KPASMKP2); - uint32_t kpasmkp3 = keypad_readl(KPASMKP3); + u32 kpasmkp0 = keypad_readl(KPASMKP0); + u32 kpasmkp1 = keypad_readl(KPASMKP1); + u32 kpasmkp2 = keypad_readl(KPASMKP2); + u32 kpasmkp3 = keypad_readl(KPASMKP3); new_state[0] = kpasmkp0 & KPASMKP_MKC_MASK; new_state[1] = (kpasmkp0 >> 16) & KPASMKP_MKC_MASK; @@ -449,23 +361,23 @@ static void pxa27x_keypad_scan_matrix(struct pxa27x_keypad *keypad) new_state[7] = (kpasmkp3 >> 16) & KPASMKP_MKC_MASK; } scan: - for (col = 0; col < pdata->matrix_key_cols; col++) { - uint32_t bits_changed; + for (col = 0; col < keypad->matrix_key_cols; col++) { + u32 bits_changed; int code; bits_changed = keypad->matrix_key_state[col] ^ new_state[col]; if (bits_changed == 0) continue; - for (row = 0; row < pdata->matrix_key_rows; row++) { - if ((bits_changed & (1 << row)) == 0) + for (row = 0; row < keypad->matrix_key_rows; row++) { + if ((bits_changed & BIT(row)) == 0) continue; code = MATRIX_SCAN_CODE(row, col, keypad->row_shift); input_event(input_dev, EV_MSC, MSC_SCAN, code); input_report_key(input_dev, keypad->keycodes[code], - new_state[col] & (1 << row)); + new_state[col] & BIT(row)); } } input_sync(input_dev); @@ -474,7 +386,7 @@ scan: #define DEFAULT_KPREC (0x007f007f) -static inline int rotary_delta(uint32_t kprec) +static inline int rotary_delta(u32 kprec) { if (kprec & KPREC_OF0) return (kprec & 0xff) + 0x7f; @@ -486,14 +398,16 @@ static inline int rotary_delta(uint32_t kprec) static void report_rotary_event(struct pxa27x_keypad *keypad, int r, int delta) { + struct pxa27x_keypad_rotary *encoder = &keypad->rotary[r]; struct input_dev *dev = keypad->input_dev; - if (delta == 0) + if (!encoder->enabled || delta == 0) return; - if (keypad->rotary_rel_code[r] == -1) { - int code = MAX_MATRIX_KEY_NUM + 2 * r + (delta > 0 ? 0 : 1); - unsigned char keycode = keypad->keycodes[code]; + if (encoder->rel_code == -1) { + int idx = delta > 0 ? 0 : 1; + int code = MAX_MATRIX_KEY_NUM + 2 * r + idx; + unsigned char keycode = encoder->key_codes[idx]; /* simulate a press-n-release */ input_event(dev, EV_MSC, MSC_SCAN, code); @@ -503,45 +417,43 @@ static void report_rotary_event(struct pxa27x_keypad *keypad, int r, int delta) input_report_key(dev, keycode, 0); input_sync(dev); } else { - input_report_rel(dev, keypad->rotary_rel_code[r], delta); + input_report_rel(dev, encoder->rel_code, delta); input_sync(dev); } } static void pxa27x_keypad_scan_rotary(struct pxa27x_keypad *keypad) { - const struct pxa27x_keypad_platform_data *pdata = keypad->pdata; - uint32_t kprec; + u32 kprec; + int i; /* read and reset to default count value */ kprec = keypad_readl(KPREC); keypad_writel(KPREC, DEFAULT_KPREC); - if (pdata->enable_rotary0) + for (i = 0; i < MAX_ROTARY_ENCODERS; i++) { report_rotary_event(keypad, 0, rotary_delta(kprec)); - - if (pdata->enable_rotary1) - report_rotary_event(keypad, 1, rotary_delta(kprec >> 16)); + kprec >>= 16; + } } static void pxa27x_keypad_scan_direct(struct pxa27x_keypad *keypad) { - const struct pxa27x_keypad_platform_data *pdata = keypad->pdata; struct input_dev *input_dev = keypad->input_dev; unsigned int new_state; - uint32_t kpdk, bits_changed; + u32 kpdk, bits_changed; int i; kpdk = keypad_readl(KPDK); - if (pdata->enable_rotary0 || pdata->enable_rotary1) + if (keypad->rotary[0].enabled || keypad->rotary[1].enabled) pxa27x_keypad_scan_rotary(keypad); /* * The KPDR_DK only output the key pin level, so it relates to board, * and low level may be active. */ - if (pdata->direct_key_low_active) + if (keypad->direct_key_low_active) new_state = ~KPDK_DK(kpdk) & keypad->direct_key_mask; else new_state = KPDK_DK(kpdk) & keypad->direct_key_mask; @@ -551,34 +463,24 @@ static void pxa27x_keypad_scan_direct(struct pxa27x_keypad *keypad) if (bits_changed == 0) return; - for (i = 0; i < pdata->direct_key_num; i++) { - if (bits_changed & (1 << i)) { + for (i = 0; i < keypad->direct_key_num; i++) { + if (bits_changed & BIT(i)) { int code = MAX_MATRIX_KEY_NUM + i; input_event(input_dev, EV_MSC, MSC_SCAN, code); input_report_key(input_dev, keypad->keycodes[code], - new_state & (1 << i)); + new_state & BIT(i)); } } input_sync(input_dev); keypad->direct_key_state = new_state; } -static void clear_wakeup_event(struct pxa27x_keypad *keypad) -{ - const struct pxa27x_keypad_platform_data *pdata = keypad->pdata; - - if (pdata->clear_wakeup_event) - (pdata->clear_wakeup_event)(); -} - static irqreturn_t pxa27x_keypad_irq_handler(int irq, void *dev_id) { struct pxa27x_keypad *keypad = dev_id; unsigned long kpc = keypad_readl(KPC); - clear_wakeup_event(keypad); - if (kpc & KPC_DI) pxa27x_keypad_scan_direct(keypad); @@ -590,7 +492,6 @@ static irqreturn_t pxa27x_keypad_irq_handler(int irq, void *dev_id) static void pxa27x_keypad_config(struct pxa27x_keypad *keypad) { - const struct pxa27x_keypad_platform_data *pdata = keypad->pdata; unsigned int mask = 0, direct_key_num = 0; unsigned long kpc = 0; @@ -598,36 +499,34 @@ static void pxa27x_keypad_config(struct pxa27x_keypad *keypad) keypad_readl(KPC); /* enable matrix keys with automatic scan */ - if (pdata->matrix_key_rows && pdata->matrix_key_cols) { + if (keypad->matrix_key_rows && keypad->matrix_key_cols) { kpc |= KPC_ASACT | KPC_MIE | KPC_ME | KPC_MS_ALL; - kpc |= KPC_MKRN(pdata->matrix_key_rows) | - KPC_MKCN(pdata->matrix_key_cols); + kpc |= KPC_MKRN(keypad->matrix_key_rows) | + KPC_MKCN(keypad->matrix_key_cols); } /* enable rotary key, debounce interval same as direct keys */ - if (pdata->enable_rotary0) { + if (keypad->rotary[0].enabled) { mask |= 0x03; direct_key_num = 2; kpc |= KPC_REE0; } - if (pdata->enable_rotary1) { + if (keypad->rotary[1].enabled) { mask |= 0x0c; direct_key_num = 4; kpc |= KPC_REE1; } - if (pdata->direct_key_num > direct_key_num) - direct_key_num = pdata->direct_key_num; + if (keypad->direct_key_num > direct_key_num) + direct_key_num = keypad->direct_key_num; /* * Direct keys usage may not start from KP_DKIN0, check the platfrom * mask data to config the specific. */ - if (pdata->direct_key_mask) - keypad->direct_key_mask = pdata->direct_key_mask; - else - keypad->direct_key_mask = ((1 << direct_key_num) - 1) & ~mask; + if (!keypad->direct_key_mask) + keypad->direct_key_mask = GENMASK(direct_key_num - 1, 0) & ~mask; /* enable direct key */ if (direct_key_num) @@ -635,7 +534,7 @@ static void pxa27x_keypad_config(struct pxa27x_keypad *keypad) keypad_writel(KPC, kpc | KPC_RE_ZERO_DEB); keypad_writel(KPREC, DEFAULT_KPREC); - keypad_writel(KPKDI, pdata->debounce_interval); + keypad_writel(KPKDI, keypad->debounce_interval); } static int pxa27x_keypad_open(struct input_dev *dev) @@ -709,19 +608,12 @@ static int pxa27x_keypad_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(pxa27x_keypad_pm_ops, pxa27x_keypad_suspend, pxa27x_keypad_resume); - static int pxa27x_keypad_probe(struct platform_device *pdev) { - const struct pxa27x_keypad_platform_data *pdata = - dev_get_platdata(&pdev->dev); - struct device_node *np = pdev->dev.of_node; struct pxa27x_keypad *keypad; struct input_dev *input_dev; - int irq, error; - - /* Driver need build keycode from device tree or pdata */ - if (!np && !pdata) - return -EINVAL; + int irq; + int error; irq = platform_get_irq(pdev, 0); if (irq < 0) @@ -736,7 +628,6 @@ static int pxa27x_keypad_probe(struct platform_device *pdev) if (!input_dev) return -ENOMEM; - keypad->pdata = pdata; keypad->input_dev = input_dev; keypad->irq = irq; @@ -765,29 +656,12 @@ static int pxa27x_keypad_probe(struct platform_device *pdev) input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); input_set_capability(input_dev, EV_MSC, MSC_SCAN); - if (pdata) { - error = pxa27x_keypad_build_keycode(keypad); - } else { - error = pxa27x_keypad_build_keycode_from_dt(keypad); - /* - * Data that we get from DT resides in dynamically - * allocated memory so we need to update our pdata - * pointer. - */ - pdata = keypad->pdata; - } + error = pxa27x_keypad_parse_properties(keypad); if (error) { - dev_err(&pdev->dev, "failed to build keycode\n"); + dev_err(&pdev->dev, "failed to parse keypad properties\n"); return error; } - keypad->row_shift = get_count_order(pdata->matrix_key_cols); - - if ((pdata->enable_rotary0 && keypad->rotary_rel_code[0] != -1) || - (pdata->enable_rotary1 && keypad->rotary_rel_code[1] != -1)) { - input_dev->evbit[0] |= BIT_MASK(EV_REL); - } - error = devm_request_irq(&pdev->dev, irq, pxa27x_keypad_irq_handler, 0, pdev->name, keypad); if (error) { diff --git a/drivers/input/keyboard/spear-keyboard.c b/drivers/input/keyboard/spear-keyboard.c index 2fae337562a2..53f3ac64c980 100644 --- a/drivers/input/keyboard/spear-keyboard.c +++ b/drivers/input/keyboard/spear-keyboard.c @@ -14,6 +14,7 @@ #include <linux/errno.h> #include <linux/interrupt.h> #include <linux/input.h> +#include <linux/input/matrix_keypad.h> #include <linux/io.h> #include <linux/irq.h> #include <linux/kernel.h> @@ -22,7 +23,6 @@ #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/types.h> -#include <linux/platform_data/keyboard-spear.h> /* Keyboard Registers */ #define MODE_CTL_REG 0x00 @@ -56,13 +56,12 @@ struct spear_kbd { void __iomem *io_base; struct clk *clk; unsigned int irq; - unsigned int mode; - unsigned int suspended_rate; + u32 mode; + u32 suspended_rate; + u32 mode_ctl_reg; unsigned short last_key; unsigned short keycodes[NUM_ROWS * NUM_COLS]; - bool rep; bool irq_wake_enabled; - u32 mode_ctl_reg; }; static irqreturn_t spear_kbd_interrupt(int irq, void *dev_id) @@ -143,46 +142,8 @@ static void spear_kbd_close(struct input_dev *dev) kbd->last_key = KEY_RESERVED; } -#ifdef CONFIG_OF -static int spear_kbd_parse_dt(struct platform_device *pdev, - struct spear_kbd *kbd) -{ - struct device_node *np = pdev->dev.of_node; - int error; - u32 val, suspended_rate; - - if (!np) { - dev_err(&pdev->dev, "Missing DT data\n"); - return -EINVAL; - } - - if (of_property_read_bool(np, "autorepeat")) - kbd->rep = true; - - if (of_property_read_u32(np, "suspended_rate", &suspended_rate)) - kbd->suspended_rate = suspended_rate; - - error = of_property_read_u32(np, "st,mode", &val); - if (error) { - dev_err(&pdev->dev, "DT: Invalid or missing mode\n"); - return error; - } - - kbd->mode = val; - return 0; -} -#else -static inline int spear_kbd_parse_dt(struct platform_device *pdev, - struct spear_kbd *kbd) -{ - return -ENOSYS; -} -#endif - static int spear_kbd_probe(struct platform_device *pdev) { - struct kbd_platform_data *pdata = dev_get_platdata(&pdev->dev); - const struct matrix_keymap_data *keymap = pdata ? pdata->keymap : NULL; struct spear_kbd *kbd; struct input_dev *input_dev; int irq; @@ -198,6 +159,14 @@ static int spear_kbd_probe(struct platform_device *pdev) return -ENOMEM; } + error = device_property_read_u32(&pdev->dev, "st,mode", &kbd->mode); + if (error) { + dev_err(&pdev->dev, "Invalid or missing mode\n"); + return error; + } + + device_property_read_u32(&pdev->dev, "suspended_rate", &kbd->suspended_rate); + input_dev = devm_input_allocate_device(&pdev->dev); if (!input_dev) { dev_err(&pdev->dev, "unable to allocate input device\n"); @@ -207,16 +176,6 @@ static int spear_kbd_probe(struct platform_device *pdev) kbd->input = input_dev; kbd->irq = irq; - if (!pdata) { - error = spear_kbd_parse_dt(pdev, kbd); - if (error) - return error; - } else { - kbd->mode = pdata->mode; - kbd->rep = pdata->rep; - kbd->suspended_rate = pdata->suspended_rate; - } - kbd->io_base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); if (IS_ERR(kbd->io_base)) return PTR_ERR(kbd->io_base); @@ -234,21 +193,21 @@ static int spear_kbd_probe(struct platform_device *pdev) input_dev->open = spear_kbd_open; input_dev->close = spear_kbd_close; - error = matrix_keypad_build_keymap(keymap, NULL, NUM_ROWS, NUM_COLS, + error = matrix_keypad_build_keymap(NULL, NULL, NUM_ROWS, NUM_COLS, kbd->keycodes, input_dev); if (error) { dev_err(&pdev->dev, "Failed to build keymap\n"); return error; } - if (kbd->rep) + if (device_property_read_bool(&pdev->dev, "autorepeat")) __set_bit(EV_REP, input_dev->evbit); input_set_capability(input_dev, EV_MSC, MSC_SCAN); input_set_drvdata(input_dev, kbd); error = devm_request_irq(&pdev->dev, irq, spear_kbd_interrupt, 0, - "keyboard", kbd); + "keyboard", kbd); if (error) { dev_err(&pdev->dev, "request_irq failed\n"); return error; diff --git a/drivers/input/keyboard/tca6416-keypad.c b/drivers/input/keyboard/tca6416-keypad.c deleted file mode 100644 index fbc674d7b9f0..000000000000 --- a/drivers/input/keyboard/tca6416-keypad.c +++ /dev/null @@ -1,305 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Driver for keys on TCA6416 I2C IO expander - * - * Copyright (C) 2010 Texas Instruments - * - * Author : Sriramakrishnan.A.G. <srk@ti.com> - */ - -#include <linux/types.h> -#include <linux/module.h> -#include <linux/init.h> -#include <linux/delay.h> -#include <linux/slab.h> -#include <linux/interrupt.h> -#include <linux/workqueue.h> -#include <linux/i2c.h> -#include <linux/input.h> -#include <linux/tca6416_keypad.h> - -#define TCA6416_INPUT 0 -#define TCA6416_OUTPUT 1 -#define TCA6416_INVERT 2 -#define TCA6416_DIRECTION 3 - -#define TCA6416_POLL_INTERVAL 100 /* msec */ - -static const struct i2c_device_id tca6416_id[] = { - { "tca6416-keys", 16, }, - { "tca6408-keys", 8, }, - { } -}; -MODULE_DEVICE_TABLE(i2c, tca6416_id); - -struct tca6416_keypad_chip { - uint16_t reg_output; - uint16_t reg_direction; - uint16_t reg_input; - - struct i2c_client *client; - struct input_dev *input; - int io_size; - u16 pinmask; - bool use_polling; - struct tca6416_button buttons[]; -}; - -static int tca6416_write_reg(struct tca6416_keypad_chip *chip, int reg, u16 val) -{ - int error; - - error = chip->io_size > 8 ? - i2c_smbus_write_word_data(chip->client, reg << 1, val) : - i2c_smbus_write_byte_data(chip->client, reg, val); - if (error < 0) { - dev_err(&chip->client->dev, - "%s failed, reg: %d, val: %d, error: %d\n", - __func__, reg, val, error); - return error; - } - - return 0; -} - -static int tca6416_read_reg(struct tca6416_keypad_chip *chip, int reg, u16 *val) -{ - int retval; - - retval = chip->io_size > 8 ? - i2c_smbus_read_word_data(chip->client, reg << 1) : - i2c_smbus_read_byte_data(chip->client, reg); - if (retval < 0) { - dev_err(&chip->client->dev, "%s failed, reg: %d, error: %d\n", - __func__, reg, retval); - return retval; - } - - *val = (u16)retval; - return 0; -} - -static void tca6416_keys_scan(struct input_dev *input) -{ - struct tca6416_keypad_chip *chip = input_get_drvdata(input); - u16 reg_val, val; - int error, i, pin_index; - - error = tca6416_read_reg(chip, TCA6416_INPUT, ®_val); - if (error) - return; - - reg_val &= chip->pinmask; - - /* Figure out which lines have changed */ - val = reg_val ^ chip->reg_input; - chip->reg_input = reg_val; - - for (i = 0, pin_index = 0; i < 16; i++) { - if (val & (1 << i)) { - struct tca6416_button *button = &chip->buttons[pin_index]; - unsigned int type = button->type ?: EV_KEY; - int state = ((reg_val & (1 << i)) ? 1 : 0) - ^ button->active_low; - - input_event(input, type, button->code, !!state); - input_sync(input); - } - - if (chip->pinmask & (1 << i)) - pin_index++; - } -} - -/* - * This is threaded IRQ handler and this can (and will) sleep. - */ -static irqreturn_t tca6416_keys_isr(int irq, void *dev_id) -{ - tca6416_keys_scan(dev_id); - - return IRQ_HANDLED; -} - -static int tca6416_keys_open(struct input_dev *dev) -{ - struct tca6416_keypad_chip *chip = input_get_drvdata(dev); - - if (!chip->use_polling) { - /* Get initial device state in case it has switches */ - tca6416_keys_scan(dev); - enable_irq(chip->client->irq); - } - - return 0; -} - -static void tca6416_keys_close(struct input_dev *dev) -{ - struct tca6416_keypad_chip *chip = input_get_drvdata(dev); - - if (!chip->use_polling) - disable_irq(chip->client->irq); -} - -static int tca6416_setup_registers(struct tca6416_keypad_chip *chip) -{ - int error; - - error = tca6416_read_reg(chip, TCA6416_OUTPUT, &chip->reg_output); - if (error) - return error; - - error = tca6416_read_reg(chip, TCA6416_DIRECTION, &chip->reg_direction); - if (error) - return error; - - /* ensure that keypad pins are set to input */ - error = tca6416_write_reg(chip, TCA6416_DIRECTION, - chip->reg_direction | chip->pinmask); - if (error) - return error; - - error = tca6416_read_reg(chip, TCA6416_DIRECTION, &chip->reg_direction); - if (error) - return error; - - error = tca6416_read_reg(chip, TCA6416_INPUT, &chip->reg_input); - if (error) - return error; - - chip->reg_input &= chip->pinmask; - - return 0; -} - -static int tca6416_keypad_probe(struct i2c_client *client) -{ - const struct i2c_device_id *id = i2c_client_get_device_id(client); - struct tca6416_keys_platform_data *pdata; - struct tca6416_keypad_chip *chip; - struct input_dev *input; - int error; - int i; - - /* Check functionality */ - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE)) { - dev_err(&client->dev, "%s adapter not supported\n", - dev_driver_string(&client->adapter->dev)); - return -ENODEV; - } - - pdata = dev_get_platdata(&client->dev); - if (!pdata) { - dev_dbg(&client->dev, "no platform data\n"); - return -EINVAL; - } - - chip = devm_kzalloc(&client->dev, - struct_size(chip, buttons, pdata->nbuttons), - GFP_KERNEL); - if (!chip) - return -ENOMEM; - - input = devm_input_allocate_device(&client->dev); - if (!input) - return -ENOMEM; - - chip->client = client; - chip->input = input; - chip->io_size = id->driver_data; - chip->pinmask = pdata->pinmask; - chip->use_polling = pdata->use_polling; - - input->phys = "tca6416-keys/input0"; - input->name = client->name; - - input->open = tca6416_keys_open; - input->close = tca6416_keys_close; - - input->id.bustype = BUS_HOST; - input->id.vendor = 0x0001; - input->id.product = 0x0001; - input->id.version = 0x0100; - - /* Enable auto repeat feature of Linux input subsystem */ - if (pdata->rep) - __set_bit(EV_REP, input->evbit); - - for (i = 0; i < pdata->nbuttons; i++) { - unsigned int type; - - chip->buttons[i] = pdata->buttons[i]; - type = (pdata->buttons[i].type) ?: EV_KEY; - input_set_capability(input, type, pdata->buttons[i].code); - } - - input_set_drvdata(input, chip); - - /* - * Initialize cached registers from their original values. - * we can't share this chip with another i2c master. - */ - error = tca6416_setup_registers(chip); - if (error) - return error; - - if (chip->use_polling) { - error = input_setup_polling(input, tca6416_keys_scan); - if (error) { - dev_err(&client->dev, "Failed to setup polling\n"); - return error; - } - - input_set_poll_interval(input, TCA6416_POLL_INTERVAL); - } else { - error = devm_request_threaded_irq(&client->dev, client->irq, - NULL, tca6416_keys_isr, - IRQF_TRIGGER_FALLING | - IRQF_ONESHOT | - IRQF_NO_AUTOEN, - "tca6416-keypad", input); - if (error) { - dev_dbg(&client->dev, - "Unable to claim irq %d; error %d\n", - client->irq, error); - return error; - } - } - - error = input_register_device(input); - if (error) { - dev_dbg(&client->dev, - "Unable to register input device, error: %d\n", error); - return error; - } - - i2c_set_clientdata(client, chip); - - return 0; -} - -static struct i2c_driver tca6416_keypad_driver = { - .driver = { - .name = "tca6416-keypad", - }, - .probe = tca6416_keypad_probe, - .id_table = tca6416_id, -}; - -static int __init tca6416_keypad_init(void) -{ - return i2c_add_driver(&tca6416_keypad_driver); -} - -subsys_initcall(tca6416_keypad_init); - -static void __exit tca6416_keypad_exit(void) -{ - i2c_del_driver(&tca6416_keypad_driver); -} -module_exit(tca6416_keypad_exit); - -MODULE_AUTHOR("Sriramakrishnan <srk@ti.com>"); -MODULE_DESCRIPTION("Keypad driver over tca6416 IO expander"); -MODULE_LICENSE("GPL"); diff --git a/drivers/input/keyboard/tca8418_keypad.c b/drivers/input/keyboard/tca8418_keypad.c index 76fc19ffe21d..68c0afafee7b 100644 --- a/drivers/input/keyboard/tca8418_keypad.c +++ b/drivers/input/keyboard/tca8418_keypad.c @@ -373,18 +373,7 @@ static struct i2c_driver tca8418_keypad_driver = { .probe = tca8418_keypad_probe, .id_table = tca8418_id, }; - -static int __init tca8418_keypad_init(void) -{ - return i2c_add_driver(&tca8418_keypad_driver); -} -subsys_initcall(tca8418_keypad_init); - -static void __exit tca8418_keypad_exit(void) -{ - i2c_del_driver(&tca8418_keypad_driver); -} -module_exit(tca8418_keypad_exit); +module_i2c_driver(tca8418_keypad_driver); MODULE_AUTHOR("Kyle Manna <kyle.manna@fuel7.com>"); MODULE_DESCRIPTION("Keypad driver for TCA8418"); diff --git a/drivers/input/keyboard/twl4030_keypad.c b/drivers/input/keyboard/twl4030_keypad.c index 77e0743a3cf8..5e3d17c5dc9b 100644 --- a/drivers/input/keyboard/twl4030_keypad.c +++ b/drivers/input/keyboard/twl4030_keypad.c @@ -28,10 +28,6 @@ * an internal state machine that decodes pressed keys, including * multi-key combinations. * - * This driver lets boards define what keycodes they wish to report for - * which scancodes, as part of the "struct twl4030_keypad_data" used in - * the probe() routine. - * * See the TPS65950 documentation; that's the general availability * version of the TWL5030 second generation part. */ @@ -47,7 +43,6 @@ struct twl4030_keypad { unsigned short keymap[TWL4030_KEYMAP_SIZE]; u16 kp_state[TWL4030_MAX_ROWS]; - bool autorepeat; unsigned int n_rows; unsigned int n_cols; int irq; @@ -322,8 +317,6 @@ static int twl4030_kp_program(struct twl4030_keypad *kp) */ static int twl4030_kp_probe(struct platform_device *pdev) { - struct twl4030_keypad_data *pdata = dev_get_platdata(&pdev->dev); - const struct matrix_keymap_data *keymap_data = NULL; struct twl4030_keypad *kp; struct input_dev *input; u8 reg; @@ -350,24 +343,10 @@ static int twl4030_kp_probe(struct platform_device *pdev) input->id.product = 0x0001; input->id.version = 0x0003; - if (pdata) { - if (!pdata->rows || !pdata->cols || !pdata->keymap_data) { - dev_err(&pdev->dev, "Missing platform_data\n"); - return -EINVAL; - } - - kp->n_rows = pdata->rows; - kp->n_cols = pdata->cols; - kp->autorepeat = pdata->rep; - keymap_data = pdata->keymap_data; - } else { - error = matrix_keypad_parse_properties(&pdev->dev, &kp->n_rows, - &kp->n_cols); - if (error) - return error; - - kp->autorepeat = true; - } + error = matrix_keypad_parse_properties(&pdev->dev, + &kp->n_rows, &kp->n_cols); + if (error) + return error; if (kp->n_rows > TWL4030_MAX_ROWS || kp->n_cols > TWL4030_MAX_COLS) { dev_err(&pdev->dev, @@ -379,7 +358,7 @@ static int twl4030_kp_probe(struct platform_device *pdev) if (kp->irq < 0) return kp->irq; - error = matrix_keypad_build_keymap(keymap_data, NULL, + error = matrix_keypad_build_keymap(NULL, NULL, TWL4030_MAX_ROWS, 1 << TWL4030_ROW_SHIFT, kp->keymap, input); @@ -389,9 +368,7 @@ static int twl4030_kp_probe(struct platform_device *pdev) } input_set_capability(input, EV_MSC, MSC_SCAN); - /* Enable auto repeat feature of Linux input subsystem */ - if (kp->autorepeat) - __set_bit(EV_REP, input->evbit); + __set_bit(EV_REP, input->evbit); error = input_register_device(input); if (error) { diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 0fb21c99a5e3..cc2558630797 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -126,6 +126,17 @@ config INPUT_ATMEL_CAPTOUCH To compile this driver as a module, choose M here: the module will be called atmel_captouch. +config INPUT_AW86927 + tristate "Awinic AW86927 Haptic Driver Support" + depends on I2C && INPUT + select INPUT_FF_MEMLESS + select REGMAP_I2C + help + Say Y here if you have an Awinic AW86927 haptic chip. + + To compile this driver as a module, choose M here: the + module will be called aw86927. + config INPUT_BBNSM_PWRKEY tristate "NXP BBNSM Power Key Driver" depends on ARCH_MXC || COMPILE_TEST @@ -230,6 +241,16 @@ config INPUT_M68K_BEEP tristate "M68k Beeper support" depends on M68K +config INPUT_MAX7360_ROTARY + tristate "Maxim MAX7360 Rotary Encoder" + depends on MFD_MAX7360 + help + If you say yes here you get support for the rotary encoder on the + Maxim MAX7360 I/O Expander. + + To compile this driver as a module, choose M here: the module will be + called max7360_rotary. + config INPUT_MAX77650_ONKEY tristate "Maxim MAX77650 ONKEY support" depends on MFD_MAX77650 @@ -506,6 +527,16 @@ config INPUT_TPS65219_PWRBUTTON To compile this driver as a module, choose M here. The module will be called tps65219-pwrbutton. +config INPUT_TPS6594_PWRBUTTON + tristate "TPS6594 Power button driver" + depends on MFD_TPS6594 + help + Say Y here if you want to enable power button reporting for + TPS6594 Power Management IC devices. + + To compile this driver as a module, choose M here. The module will + be called tps6594-pwrbutton. + config INPUT_AXP20X_PEK tristate "X-Powers AXP20X power button driver" depends on MFD_AXP20X diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index d468c8140b93..f5ebfa9d9983 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_INPUT_ATC260X_ONKEY) += atc260x-onkey.o obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o obj-$(CONFIG_INPUT_ATLAS_BTNS) += atlas_btns.o obj-$(CONFIG_INPUT_ATMEL_CAPTOUCH) += atmel_captouch.o +obj-$(CONFIG_INPUT_AW86927) += aw86927.o obj-$(CONFIG_INPUT_BBNSM_PWRKEY) += nxp-bbnsm-pwrkey.o obj-$(CONFIG_INPUT_BMA150) += bma150.o obj-$(CONFIG_INPUT_CM109) += cm109.o @@ -51,6 +52,7 @@ obj-$(CONFIG_INPUT_IQS7222) += iqs7222.o obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o obj-$(CONFIG_INPUT_KXTJ9) += kxtj9.o obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o +obj-$(CONFIG_INPUT_MAX7360_ROTARY) += max7360-rotary.o obj-$(CONFIG_INPUT_MAX77650_ONKEY) += max77650-onkey.o obj-$(CONFIG_INPUT_MAX77693_HAPTIC) += max77693-haptic.o obj-$(CONFIG_INPUT_MAX8925_ONKEY) += max8925_onkey.o @@ -83,6 +85,7 @@ obj-$(CONFIG_INPUT_SPARCSPKR) += sparcspkr.o obj-$(CONFIG_INPUT_STPMIC1_ONKEY) += stpmic1_onkey.o obj-$(CONFIG_INPUT_TPS65218_PWRBUTTON) += tps65218-pwrbutton.o obj-$(CONFIG_INPUT_TPS65219_PWRBUTTON) += tps65219-pwrbutton.o +obj-$(CONFIG_INPUT_TPS6594_PWRBUTTON) += tps6594-pwrbutton.o obj-$(CONFIG_INPUT_TWL4030_PWRBUTTON) += twl4030-pwrbutton.o obj-$(CONFIG_INPUT_TWL4030_VIBRA) += twl4030-vibra.o obj-$(CONFIG_INPUT_TWL6040_VIBRA) += twl6040-vibra.o diff --git a/drivers/input/misc/ad714x.c b/drivers/input/misc/ad714x.c index d106f37df6bc..c9fa789337ba 100644 --- a/drivers/input/misc/ad714x.c +++ b/drivers/input/misc/ad714x.c @@ -6,6 +6,7 @@ */ #include <linux/device.h> +#include <linux/export.h> #include <linux/input.h> #include <linux/interrupt.h> #include <linux/slab.h> diff --git a/drivers/input/misc/adxl34x.c b/drivers/input/misc/adxl34x.c index 7cafbf8d5f1a..ac7674647c09 100644 --- a/drivers/input/misc/adxl34x.c +++ b/drivers/input/misc/adxl34x.c @@ -9,6 +9,7 @@ #include <linux/device.h> #include <linux/delay.h> +#include <linux/export.h> #include <linux/input.h> #include <linux/interrupt.h> #include <linux/irq.h> diff --git a/drivers/input/misc/aw86927.c b/drivers/input/misc/aw86927.c new file mode 100644 index 000000000000..8ad361239cfe --- /dev/null +++ b/drivers/input/misc/aw86927.c @@ -0,0 +1,846 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2025 Griffin Kroah-Hartman <griffin.kroah@fairphone.com> + * + * Partially based on vendor driver: + * Copyright (c) 2021 AWINIC Technology CO., LTD + * + */ + +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/input.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/types.h> + +#define AW86927_RSTCFG_REG 0x00 +#define AW86927_RSTCFG_SOFTRST 0xaa + +#define AW86927_SYSINT_REG 0x02 +#define AW86927_SYSINT_BST_SCPI BIT(7) +#define AW86927_SYSINT_BST_OVPI BIT(6) +#define AW86927_SYSINT_UVLI BIT(5) +#define AW86927_SYSINT_FF_AEI BIT(4) +#define AW86927_SYSINT_FF_AFI BIT(3) +#define AW86927_SYSINT_OCDI BIT(2) +#define AW86927_SYSINT_OTI BIT(1) +#define AW86927_SYSINT_DONEI BIT(0) + +#define AW86927_SYSINTM_REG 0x03 +#define AW86927_SYSINTM_BST_OVPM BIT(6) +#define AW86927_SYSINTM_FF_AEM BIT(4) +#define AW86927_SYSINTM_FF_AFM BIT(3) +#define AW86927_SYSINTM_DONEM BIT(0) + +#define AW86927_PLAYCFG1_REG 0x06 +#define AW86927_PLAYCFG1_BST_MODE_MASK GENMASK(7, 7) +#define AW86927_PLAYCFG1_BST_MODE_BYPASS 0 +#define AW86927_PLAYCFG1_BST_VOUT_VREFSET_MASK GENMASK(6, 0) +#define AW86927_PLAYCFG1_BST_8500MV 0x50 + +#define AW86927_PLAYCFG2_REG 0x07 + +#define AW86927_PLAYCFG3_REG 0x08 +#define AW86927_PLAYCFG3_AUTO_BST_MASK GENMASK(4, 4) +#define AW86927_PLAYCFG3_AUTO_BST_ENABLE 1 +#define AW86927_PLAYCFG3_AUTO_BST_DISABLE 0 +#define AW86927_PLAYCFG3_PLAY_MODE_MASK GENMASK(1, 0) +#define AW86927_PLAYCFG3_PLAY_MODE_RAM 0 + +#define AW86927_PLAYCFG4_REG 0x09 +#define AW86927_PLAYCFG4_STOP BIT(1) +#define AW86927_PLAYCFG4_GO BIT(0) + +#define AW86927_WAVCFG1_REG 0x0a +#define AW86927_WAVCFG1_WAVSEQ1_MASK GENMASK(6, 0) + +#define AW86927_WAVCFG2_REG 0x0b +#define AW86927_WAVCFG2_WAVSEQ2_MASK GENMASK(6, 0) + +#define AW86927_WAVCFG9_REG 0x12 +#define AW86927_WAVCFG9_SEQ1LOOP_MASK GENMASK(7, 4) +#define AW86927_WAVCFG9_SEQ1LOOP_INFINITELY 0x0f + +#define AW86927_CONTCFG1_REG 0x18 +#define AW86927_CONTCFG1_BRK_BST_MD_MASK GENMASK(6, 6) + +#define AW86927_CONTCFG5_REG 0x1c +#define AW86927_CONTCFG5_BST_BRK_GAIN_MASK GENMASK(7, 4) +#define AW86927_CONTCFG5_BRK_GAIN_MASK GENMASK(3, 0) + +#define AW86927_CONTCFG10_REG 0x21 +#define AW86927_CONTCFG10_BRK_TIME_MASK GENMASK(7, 0) +#define AW86927_CONTCFG10_BRK_TIME_DEFAULT 8 + +#define AW86927_CONTCFG13_REG 0x24 +#define AW86927_CONTCFG13_TSET_MASK GENMASK(7, 4) +#define AW86927_CONTCFG13_BEME_SET_MASK GENMASK(3, 0) + +#define AW86927_BASEADDRH_REG 0x2d +#define AW86927_BASEADDRL_REG 0x2e + +#define AW86927_GLBRD5_REG 0x3f +#define AW86927_GLBRD5_STATE_MASK GENMASK(3, 0) +#define AW86927_GLBRD5_STATE_STANDBY 0 + +#define AW86927_RAMADDRH_REG 0x40 + +#define AW86927_RAMADDRL_REG 0x41 + +#define AW86927_RAMDATA_REG 0x42 + +#define AW86927_SYSCTRL3_REG 0x45 +#define AW86927_SYSCTRL3_STANDBY_MASK GENMASK(5, 5) +#define AW86927_SYSCTRL3_STANDBY_ON 1 +#define AW86927_SYSCTRL3_STANDBY_OFF 0 +#define AW86927_SYSCTRL3_EN_RAMINIT_MASK GENMASK(2, 2) +#define AW86927_SYSCTRL3_EN_RAMINIT_ON 1 +#define AW86927_SYSCTRL3_EN_RAMINIT_OFF 0 + +#define AW86927_SYSCTRL4_REG 0x46 +#define AW86927_SYSCTRL4_WAVDAT_MODE_MASK GENMASK(6, 5) +#define AW86927_SYSCTRL4_WAVDAT_24K 0 +#define AW86927_SYSCTRL4_INT_EDGE_MODE_MASK GENMASK(4, 4) +#define AW86927_SYSCTRL4_INT_EDGE_MODE_POS 0 +#define AW86927_SYSCTRL4_INT_MODE_MASK GENMASK(3, 3) +#define AW86927_SYSCTRL4_INT_MODE_EDGE 1 +#define AW86927_SYSCTRL4_GAIN_BYPASS_MASK GENMASK(0, 0) + +#define AW86927_PWMCFG1_REG 0x48 +#define AW86927_PWMCFG1_PRC_EN_MASK GENMASK(7, 7) +#define AW86927_PWMCFG1_PRC_DISABLE 0 + +#define AW86927_PWMCFG3_REG 0x4a +#define AW86927_PWMCFG3_PR_EN_MASK GENMASK(7, 7) +#define AW86927_PWMCFG3_PRCTIME_MASK GENMASK(6, 0) + +#define AW86927_PWMCFG4_REG 0x4b +#define AW86927_PWMCFG4_PRTIME_MASK GENMASK(7, 0) + +#define AW86927_VBATCTRL_REG 0x4c +#define AW86927_VBATCTRL_VBAT_MODE_MASK GENMASK(6, 6) +#define AW86927_VBATCTRL_VBAT_MODE_SW 0 + +#define AW86927_DETCFG1_REG 0x4d +#define AW86927_DETCFG1_DET_GO_MASK GENMASK(1, 0) +#define AW86927_DETCFG1_DET_GO_DET_SEQ0 1 +#define AW86927_DETCFG1_DET_GO_NA 0 + +#define AW86927_DETCFG2_REG 0x4e +#define AW86927_DETCFG2_DET_SEQ0_MASK GENMASK(6, 3) +#define AW86927_DETCFG2_DET_SEQ0_VBAT 0 +#define AW86927_DETCFG2_D2S_GAIN_MASK GENMASK(2, 0) +#define AW86927_DETCFG2_D2S_GAIN_10 4 + +#define AW86927_CHIPIDH_REG 0x57 +#define AW86927_CHIPIDL_REG 0x58 +#define AW86927_CHIPID 0x9270 + +#define AW86927_TMCFG_REG 0x5b +#define AW86927_TMCFG_UNLOCK 0x7d +#define AW86927_TMCFG_LOCK 0x00 + +#define AW86927_ANACFG11_REG 0x70 + +#define AW86927_ANACFG12_REG 0x71 +#define AW86927_ANACFG12_BST_SKIP_MASK GENMASK(7, 7) +#define AW86927_ANACFG12_BST_SKIP_SHUTDOWN 1 + +#define AW86927_ANACFG13_REG 0x72 +#define AW86927_ANACFG13_BST_PC_MASK GENMASK(7, 4) +#define AW86927_ANACFG13_BST_PEAKCUR_3P45A 6 + +#define AW86927_ANACFG15_REG 0x74 +#define AW86927_ANACFG15_BST_PEAK_MODE_MASK GENMASK(7, 7) +#define AW86927_ANACFG15_BST_PEAK_BACK 1 + +#define AW86927_ANACFG16_REG 0x75 +#define AW86927_ANACFG16_BST_SRC_MASK GENMASK(4, 4) +#define AW86927_ANACFG16_BST_SRC_3NS 0 + +/* default value of base addr */ +#define AW86927_RAM_BASE_ADDR 0x800 +#define AW86927_BASEADDRH_VAL 0x08 +#define AW86927_BASEADDRL_VAL 0x00 + +enum aw86927_work_mode { + AW86927_STANDBY_MODE, + AW86927_RAM_MODE, +}; + +struct aw86927_data { + struct work_struct play_work; + struct device *dev; + struct input_dev *input_dev; + struct i2c_client *client; + struct regmap *regmap; + struct gpio_desc *reset_gpio; + bool running; +}; + +static const struct regmap_config aw86927_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_NONE, + .max_register = 0x80, +}; + +/* + * Sine wave representing the magnitude of the drive to be used. + * Data is encoded in two's complement. + * round(84 * sin(x / 16.25)) + */ +static const u8 aw86927_waveform[] = { + 0x00, 0x05, 0x0a, 0x0f, 0x14, 0x1a, 0x1f, 0x23, 0x28, 0x2d, 0x31, 0x35, + 0x39, 0x3d, 0x41, 0x44, 0x47, 0x4a, 0x4c, 0x4f, 0x51, 0x52, 0x53, 0x54, + 0x55, 0x55, 0x55, 0x55, 0x55, 0x54, 0x52, 0x51, 0x4f, 0x4d, 0x4a, 0x47, + 0x44, 0x41, 0x3d, 0x3a, 0x36, 0x31, 0x2d, 0x28, 0x24, 0x1f, 0x1a, 0x15, + 0x10, 0x0a, 0x05, 0x00, 0xfc, 0xf6, 0xf1, 0xec, 0xe7, 0xe2, 0xdd, 0xd8, + 0xd4, 0xcf, 0xcb, 0xc7, 0xc3, 0xbf, 0xbc, 0xb9, 0xb6, 0xb4, 0xb1, 0xb0, + 0xae, 0xad, 0xac, 0xab, 0xab, 0xab, 0xab, 0xab, 0xac, 0xae, 0xaf, 0xb1, + 0xb3, 0xb6, 0xb8, 0xbc, 0xbf, 0xc2, 0xc6, 0xca, 0xce, 0xd3, 0xd7, 0xdc, + 0xe1, 0xe6, 0xeb, 0xf0, 0xf5, 0xfb +}; + +struct aw86927_sram_waveform_header { + u8 version; + __be16 start_address; + __be16 end_address; +} __packed; + +static const struct aw86927_sram_waveform_header sram_waveform_header = { + .version = 0x01, + .start_address = cpu_to_be16(AW86927_RAM_BASE_ADDR + + sizeof(struct aw86927_sram_waveform_header)), + .end_address = cpu_to_be16(AW86927_RAM_BASE_ADDR + + sizeof(struct aw86927_sram_waveform_header) + + ARRAY_SIZE(aw86927_waveform) - 1), +}; + +static int aw86927_wait_enter_standby(struct aw86927_data *haptics) +{ + unsigned int reg_val; + int err; + + err = regmap_read_poll_timeout(haptics->regmap, AW86927_GLBRD5_REG, reg_val, + (FIELD_GET(AW86927_GLBRD5_STATE_MASK, reg_val) == + AW86927_GLBRD5_STATE_STANDBY), + 2500, 2500 * 100); + + if (err) { + dev_err(haptics->dev, "did not enter standby: %d\n", err); + return err; + } + return 0; +} + +static int aw86927_play_mode(struct aw86927_data *haptics, u8 play_mode) +{ + int err; + + switch (play_mode) { + case AW86927_STANDBY_MODE: + /* Briefly toggle standby, then toggle back to standby off */ + err = regmap_update_bits(haptics->regmap, + AW86927_SYSCTRL3_REG, + AW86927_SYSCTRL3_STANDBY_MASK, + FIELD_PREP(AW86927_SYSCTRL3_STANDBY_MASK, + AW86927_SYSCTRL3_STANDBY_ON)); + if (err) + return err; + + err = regmap_update_bits(haptics->regmap, + AW86927_SYSCTRL3_REG, + AW86927_SYSCTRL3_STANDBY_MASK, + FIELD_PREP(AW86927_SYSCTRL3_STANDBY_MASK, + AW86927_SYSCTRL3_STANDBY_OFF)); + if (err) + return err; + + break; + + case AW86927_RAM_MODE: + err = regmap_update_bits(haptics->regmap, + AW86927_PLAYCFG3_REG, + AW86927_PLAYCFG3_PLAY_MODE_MASK, + FIELD_PREP(AW86927_PLAYCFG3_PLAY_MODE_MASK, + AW86927_PLAYCFG3_PLAY_MODE_RAM)); + if (err) + return err; + + err = regmap_update_bits(haptics->regmap, + AW86927_PLAYCFG1_REG, + AW86927_PLAYCFG1_BST_MODE_MASK, + FIELD_PREP(AW86927_PLAYCFG1_BST_MODE_MASK, + AW86927_PLAYCFG1_BST_MODE_BYPASS)); + if (err) + return err; + + err = regmap_update_bits(haptics->regmap, + AW86927_VBATCTRL_REG, + AW86927_VBATCTRL_VBAT_MODE_MASK, + FIELD_PREP(AW86927_VBATCTRL_VBAT_MODE_MASK, + AW86927_VBATCTRL_VBAT_MODE_SW)); + if (err) + return err; + + break; + } + + return 0; +} + +static int aw86927_stop(struct aw86927_data *haptics) +{ + int err; + + err = regmap_write(haptics->regmap, AW86927_PLAYCFG4_REG, AW86927_PLAYCFG4_STOP); + if (err) { + dev_err(haptics->dev, "Failed to stop playback: %d\n", err); + return err; + } + + err = aw86927_wait_enter_standby(haptics); + if (err) { + dev_err(haptics->dev, "Failed to enter standby, trying to force it\n"); + err = aw86927_play_mode(haptics, AW86927_STANDBY_MODE); + if (err) + return err; + } + + return 0; +} + +static int aw86927_haptics_play(struct input_dev *dev, void *data, struct ff_effect *effect) +{ + struct aw86927_data *haptics = input_get_drvdata(dev); + int level; + + level = effect->u.rumble.strong_magnitude; + if (!level) + level = effect->u.rumble.weak_magnitude; + + /* If already running, don't restart playback */ + if (haptics->running && level) + return 0; + + haptics->running = level; + schedule_work(&haptics->play_work); + + return 0; +} + +static int aw86927_play_sine(struct aw86927_data *haptics) +{ + int err; + + err = aw86927_stop(haptics); + if (err) + return err; + + err = aw86927_play_mode(haptics, AW86927_RAM_MODE); + if (err) + return err; + + err = regmap_update_bits(haptics->regmap, AW86927_PLAYCFG3_REG, + AW86927_PLAYCFG3_AUTO_BST_MASK, + FIELD_PREP(AW86927_PLAYCFG3_AUTO_BST_MASK, + AW86927_PLAYCFG3_AUTO_BST_ENABLE)); + if (err) + return err; + + /* Set waveseq 1 to the first wave */ + err = regmap_update_bits(haptics->regmap, AW86927_WAVCFG1_REG, + AW86927_WAVCFG1_WAVSEQ1_MASK, + FIELD_PREP(AW86927_WAVCFG1_WAVSEQ1_MASK, 1)); + if (err) + return err; + + /* set wavseq 2 to zero */ + err = regmap_update_bits(haptics->regmap, AW86927_WAVCFG2_REG, + AW86927_WAVCFG2_WAVSEQ2_MASK, + FIELD_PREP(AW86927_WAVCFG2_WAVSEQ2_MASK, 0)); + if (err) + return err; + + err = regmap_update_bits(haptics->regmap, + AW86927_WAVCFG9_REG, + AW86927_WAVCFG9_SEQ1LOOP_MASK, + FIELD_PREP(AW86927_WAVCFG9_SEQ1LOOP_MASK, + AW86927_WAVCFG9_SEQ1LOOP_INFINITELY)); + if (err) + return err; + + /* set gain to value lower than 0x80 to avoid distorted playback */ + err = regmap_write(haptics->regmap, AW86927_PLAYCFG2_REG, 0x7c); + if (err) + return err; + + /* Start playback */ + err = regmap_write(haptics->regmap, AW86927_PLAYCFG4_REG, AW86927_PLAYCFG4_GO); + if (err) + return err; + + return 0; +} + +static void aw86927_close(struct input_dev *input) +{ + struct aw86927_data *haptics = input_get_drvdata(input); + struct device *dev = &haptics->client->dev; + int err; + + cancel_work_sync(&haptics->play_work); + + err = aw86927_stop(haptics); + if (err) + dev_err(dev, "Failed to close the Driver: %d\n", err); +} + +static void aw86927_haptics_play_work(struct work_struct *work) +{ + struct aw86927_data *haptics = + container_of(work, struct aw86927_data, play_work); + struct device *dev = &haptics->client->dev; + int err; + + if (haptics->running) + err = aw86927_play_sine(haptics); + else + err = aw86927_stop(haptics); + + if (err) + dev_err(dev, "Failed to execute work command: %d\n", err); +} + +static void aw86927_hw_reset(struct aw86927_data *haptics) +{ + /* Assert reset */ + gpiod_set_value_cansleep(haptics->reset_gpio, 1); + /* Wait ~1ms */ + usleep_range(1000, 2000); + /* Deassert reset */ + gpiod_set_value_cansleep(haptics->reset_gpio, 0); + /* Wait ~8ms until I2C is accessible */ + usleep_range(8000, 8500); +} + +static int aw86927_haptic_init(struct aw86927_data *haptics) +{ + int err; + + err = regmap_update_bits(haptics->regmap, + AW86927_SYSCTRL4_REG, + AW86927_SYSCTRL4_WAVDAT_MODE_MASK, + FIELD_PREP(AW86927_SYSCTRL4_WAVDAT_MODE_MASK, + AW86927_SYSCTRL4_WAVDAT_24K)); + if (err) + return err; + + /* enable gain bypass */ + err = regmap_update_bits(haptics->regmap, + AW86927_SYSCTRL4_REG, + AW86927_SYSCTRL4_GAIN_BYPASS_MASK, + FIELD_PREP(AW86927_SYSCTRL4_GAIN_BYPASS_MASK, + 0x01)); + if (err) + return err; + + err = regmap_write(haptics->regmap, + AW86927_TMCFG_REG, AW86927_TMCFG_UNLOCK); + if (err) + return err; + + err = regmap_write(haptics->regmap, AW86927_ANACFG11_REG, 0x0f); + if (err) + return err; + + err = regmap_update_bits(haptics->regmap, + AW86927_ANACFG12_REG, + AW86927_ANACFG12_BST_SKIP_MASK, + FIELD_PREP(AW86927_ANACFG12_BST_SKIP_MASK, + AW86927_ANACFG12_BST_SKIP_SHUTDOWN)); + if (err) + return err; + + err = regmap_update_bits(haptics->regmap, + AW86927_ANACFG15_REG, + AW86927_ANACFG15_BST_PEAK_MODE_MASK, + FIELD_PREP(AW86927_ANACFG15_BST_PEAK_MODE_MASK, + AW86927_ANACFG15_BST_PEAK_BACK)); + if (err) + return err; + + err = regmap_update_bits(haptics->regmap, + AW86927_ANACFG16_REG, + AW86927_ANACFG16_BST_SRC_MASK, + FIELD_PREP(AW86927_ANACFG16_BST_SRC_MASK, + AW86927_ANACFG16_BST_SRC_3NS)); + if (err) + return err; + + err = regmap_write(haptics->regmap, + AW86927_TMCFG_REG, AW86927_TMCFG_LOCK); + if (err) + return err; + + err = regmap_update_bits(haptics->regmap, + AW86927_CONTCFG1_REG, + AW86927_CONTCFG1_BRK_BST_MD_MASK, + FIELD_PREP(AW86927_CONTCFG1_BRK_BST_MD_MASK, 0x00)); + if (err) + return err; + + err = regmap_write(haptics->regmap, + AW86927_CONTCFG5_REG, + FIELD_PREP(AW86927_CONTCFG5_BST_BRK_GAIN_MASK, 0x05) | + FIELD_PREP(AW86927_CONTCFG5_BRK_GAIN_MASK, 0x08)); + if (err) + return err; + + err = regmap_update_bits(haptics->regmap, AW86927_CONTCFG10_REG, + AW86927_CONTCFG10_BRK_TIME_MASK, + FIELD_PREP(AW86927_CONTCFG10_BRK_TIME_MASK, + AW86927_CONTCFG10_BRK_TIME_DEFAULT)); + if (err) + return err; + + err = regmap_write(haptics->regmap, + AW86927_CONTCFG13_REG, + FIELD_PREP(AW86927_CONTCFG13_TSET_MASK, 0x06) | + FIELD_PREP(AW86927_CONTCFG13_BEME_SET_MASK, 0x02)); + if (err) + return err; + + err = regmap_update_bits(haptics->regmap, + AW86927_DETCFG2_REG, + AW86927_DETCFG2_D2S_GAIN_MASK, + FIELD_PREP(AW86927_DETCFG2_D2S_GAIN_MASK, + AW86927_DETCFG2_D2S_GAIN_10)); + if (err) + return err; + + err = regmap_update_bits(haptics->regmap, + AW86927_PWMCFG1_REG, + AW86927_PWMCFG1_PRC_EN_MASK, + FIELD_PREP(AW86927_PWMCFG1_PRC_EN_MASK, + AW86927_PWMCFG1_PRC_DISABLE)); + if (err) + return err; + + err = regmap_write(haptics->regmap, + AW86927_PWMCFG3_REG, + FIELD_PREP(AW86927_PWMCFG3_PR_EN_MASK, 0x01) | + FIELD_PREP(AW86927_PWMCFG3_PRCTIME_MASK, 0x3f)); + if (err) + return err; + + err = regmap_update_bits(haptics->regmap, + AW86927_PWMCFG4_REG, + AW86927_PWMCFG4_PRTIME_MASK, + FIELD_PREP(AW86927_PWMCFG4_PRTIME_MASK, 0x32)); + if (err) + return err; + + err = regmap_write(haptics->regmap, + AW86927_TMCFG_REG, AW86927_TMCFG_UNLOCK); + if (err) + return err; + + err = regmap_update_bits(haptics->regmap, + AW86927_ANACFG13_REG, + AW86927_ANACFG13_BST_PC_MASK, + FIELD_PREP(AW86927_ANACFG13_BST_PC_MASK, + AW86927_ANACFG13_BST_PEAKCUR_3P45A)); + if (err) + return err; + + err = regmap_write(haptics->regmap, + AW86927_TMCFG_REG, AW86927_TMCFG_LOCK); + if (err) + return err; + + err = regmap_update_bits(haptics->regmap, + AW86927_PLAYCFG1_REG, + AW86927_PLAYCFG1_BST_VOUT_VREFSET_MASK, + FIELD_PREP(AW86927_PLAYCFG1_BST_VOUT_VREFSET_MASK, + AW86927_PLAYCFG1_BST_8500MV)); + if (err) + return err; + + err = regmap_update_bits(haptics->regmap, + AW86927_PLAYCFG3_REG, + AW86927_PLAYCFG3_AUTO_BST_MASK, + FIELD_PREP(AW86927_PLAYCFG3_AUTO_BST_MASK, + AW86927_PLAYCFG3_AUTO_BST_DISABLE)); + if (err) + return err; + + return 0; +} + +static int aw86927_ram_init(struct aw86927_data *haptics) +{ + int err; + + err = aw86927_wait_enter_standby(haptics); + if (err) + return err; + + /* Enable SRAM init */ + err = regmap_update_bits(haptics->regmap, + AW86927_SYSCTRL3_REG, + AW86927_SYSCTRL3_EN_RAMINIT_MASK, + FIELD_PREP(AW86927_SYSCTRL3_EN_RAMINIT_MASK, + AW86927_SYSCTRL3_EN_RAMINIT_ON)); + + /* Set base address for the start of the SRAM waveforms */ + err = regmap_write(haptics->regmap, + AW86927_BASEADDRH_REG, AW86927_BASEADDRH_VAL); + if (err) + return err; + + err = regmap_write(haptics->regmap, + AW86927_BASEADDRL_REG, AW86927_BASEADDRL_VAL); + if (err) + return err; + + /* Set start of SRAM, before the data is written it will be the same as the base */ + err = regmap_write(haptics->regmap, + AW86927_RAMADDRH_REG, AW86927_BASEADDRH_VAL); + if (err) + return err; + + err = regmap_write(haptics->regmap, + AW86927_RAMADDRL_REG, AW86927_BASEADDRL_VAL); + if (err) + return err; + + /* Write waveform header to SRAM */ + err = regmap_noinc_write(haptics->regmap, AW86927_RAMDATA_REG, + &sram_waveform_header, sizeof(sram_waveform_header)); + if (err) + return err; + + /* Write waveform to SRAM */ + err = regmap_noinc_write(haptics->regmap, AW86927_RAMDATA_REG, + aw86927_waveform, ARRAY_SIZE(aw86927_waveform)); + if (err) + return err; + + err = regmap_update_bits(haptics->regmap, + AW86927_DETCFG2_REG, + AW86927_DETCFG2_DET_SEQ0_MASK, + FIELD_PREP(AW86927_DETCFG2_DET_SEQ0_MASK, + AW86927_DETCFG2_DET_SEQ0_VBAT)); + if (err) + return err; + + err = regmap_update_bits(haptics->regmap, + AW86927_DETCFG1_REG, + AW86927_DETCFG1_DET_GO_MASK, + FIELD_PREP(AW86927_DETCFG1_DET_GO_MASK, + AW86927_DETCFG1_DET_GO_DET_SEQ0)); + if (err) + return err; + + usleep_range(3000, 3500); + + err = regmap_update_bits(haptics->regmap, + AW86927_DETCFG1_REG, + AW86927_DETCFG1_DET_GO_MASK, + FIELD_PREP(AW86927_DETCFG1_DET_GO_MASK, + AW86927_DETCFG1_DET_GO_NA)); + if (err) + return err; + + /* Disable SRAM init */ + err = regmap_update_bits(haptics->regmap, + AW86927_SYSCTRL3_REG, + AW86927_SYSCTRL3_EN_RAMINIT_MASK, + FIELD_PREP(AW86927_SYSCTRL3_EN_RAMINIT_MASK, + AW86927_SYSCTRL3_EN_RAMINIT_OFF)); + if (err) + return err; + + return 0; +} + +static irqreturn_t aw86927_irq(int irq, void *data) +{ + struct aw86927_data *haptics = data; + struct device *dev = &haptics->client->dev; + unsigned int reg_val; + int err; + + err = regmap_read(haptics->regmap, AW86927_SYSINT_REG, ®_val); + if (err) { + dev_err(dev, "Failed to read SYSINT register: %d\n", err); + return IRQ_NONE; + } + + if (reg_val & AW86927_SYSINT_BST_SCPI) + dev_err(dev, "Received a Short Circuit Protection interrupt\n"); + if (reg_val & AW86927_SYSINT_BST_OVPI) + dev_err(dev, "Received an Over Voltage Protection interrupt\n"); + if (reg_val & AW86927_SYSINT_UVLI) + dev_err(dev, "Received an Under Voltage Lock Out interrupt\n"); + if (reg_val & AW86927_SYSINT_OCDI) + dev_err(dev, "Received an Over Current interrupt\n"); + if (reg_val & AW86927_SYSINT_OTI) + dev_err(dev, "Received an Over Temperature interrupt\n"); + + if (reg_val & AW86927_SYSINT_DONEI) + dev_dbg(dev, "Chip playback done!\n"); + if (reg_val & AW86927_SYSINT_FF_AFI) + dev_dbg(dev, "The RTP mode FIFO is almost full!\n"); + if (reg_val & AW86927_SYSINT_FF_AEI) + dev_dbg(dev, "The RTP mode FIFO is almost empty!\n"); + + return IRQ_HANDLED; +} + +static int aw86927_detect(struct aw86927_data *haptics) +{ + __be16 read_buf; + u16 chip_id; + int err; + + err = regmap_bulk_read(haptics->regmap, AW86927_CHIPIDH_REG, &read_buf, 2); + if (err) + return dev_err_probe(haptics->dev, err, "Failed to read CHIPID registers\n"); + + chip_id = be16_to_cpu(read_buf); + + if (chip_id != AW86927_CHIPID) { + dev_err(haptics->dev, "Unexpected CHIPID value 0x%x\n", chip_id); + return -ENODEV; + } + + return 0; +} + +static int aw86927_probe(struct i2c_client *client) +{ + struct aw86927_data *haptics; + int err; + + haptics = devm_kzalloc(&client->dev, sizeof(struct aw86927_data), GFP_KERNEL); + if (!haptics) + return -ENOMEM; + + haptics->dev = &client->dev; + haptics->client = client; + + i2c_set_clientdata(client, haptics); + + haptics->regmap = devm_regmap_init_i2c(client, &aw86927_regmap_config); + if (IS_ERR(haptics->regmap)) + return dev_err_probe(haptics->dev, PTR_ERR(haptics->regmap), + "Failed to allocate register map\n"); + + haptics->input_dev = devm_input_allocate_device(haptics->dev); + if (!haptics->input_dev) + return -ENOMEM; + + haptics->reset_gpio = devm_gpiod_get(haptics->dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(haptics->reset_gpio)) + return dev_err_probe(haptics->dev, PTR_ERR(haptics->reset_gpio), + "Failed to get reset gpio\n"); + + /* Hardware reset */ + aw86927_hw_reset(haptics); + + /* Software reset */ + err = regmap_write(haptics->regmap, AW86927_RSTCFG_REG, AW86927_RSTCFG_SOFTRST); + if (err) + return dev_err_probe(haptics->dev, err, "Failed Software reset\n"); + + /* Wait ~3ms until I2C is accessible */ + usleep_range(3000, 3500); + + err = aw86927_detect(haptics); + if (err) + return dev_err_probe(haptics->dev, err, "Failed to find chip\n"); + + /* IRQ config */ + err = regmap_write(haptics->regmap, AW86927_SYSCTRL4_REG, + FIELD_PREP(AW86927_SYSCTRL4_INT_MODE_MASK, + AW86927_SYSCTRL4_INT_MODE_EDGE) | + FIELD_PREP(AW86927_SYSCTRL4_INT_EDGE_MODE_MASK, + AW86927_SYSCTRL4_INT_EDGE_MODE_POS)); + if (err) + return dev_err_probe(haptics->dev, err, "Failed to configure interrupt modes\n"); + + err = regmap_write(haptics->regmap, AW86927_SYSINTM_REG, + AW86927_SYSINTM_BST_OVPM | + AW86927_SYSINTM_FF_AEM | + AW86927_SYSINTM_FF_AFM | + AW86927_SYSINTM_DONEM); + if (err) + return dev_err_probe(haptics->dev, err, "Failed to configure interrupt masks\n"); + + err = devm_request_threaded_irq(haptics->dev, client->irq, NULL, + aw86927_irq, IRQF_ONESHOT, NULL, haptics); + if (err) + return dev_err_probe(haptics->dev, err, "Failed to request threaded irq\n"); + + INIT_WORK(&haptics->play_work, aw86927_haptics_play_work); + + haptics->input_dev->name = "aw86927-haptics"; + haptics->input_dev->close = aw86927_close; + + input_set_drvdata(haptics->input_dev, haptics); + input_set_capability(haptics->input_dev, EV_FF, FF_RUMBLE); + + err = input_ff_create_memless(haptics->input_dev, NULL, aw86927_haptics_play); + if (err) + return dev_err_probe(haptics->dev, err, "Failed to create FF dev\n"); + + /* Set up registers */ + err = aw86927_play_mode(haptics, AW86927_STANDBY_MODE); + if (err) + return dev_err_probe(haptics->dev, err, + "Failed to enter standby for Haptic init\n"); + + err = aw86927_haptic_init(haptics); + if (err) + return dev_err_probe(haptics->dev, err, "Haptic init failed\n"); + + /* RAM init, upload the waveform for playback */ + err = aw86927_ram_init(haptics); + if (err) + return dev_err_probe(haptics->dev, err, "Failed to init aw86927 sram\n"); + + err = input_register_device(haptics->input_dev); + if (err) + return dev_err_probe(haptics->dev, err, "Failed to register input device\n"); + + return 0; +} + +static const struct of_device_id aw86927_of_id[] = { + { .compatible = "awinic,aw86927" }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, aw86927_of_id); + +static struct i2c_driver aw86927_driver = { + .driver = { + .name = "aw86927-haptics", + .of_match_table = aw86927_of_id, + }, + .probe = aw86927_probe, +}; + +module_i2c_driver(aw86927_driver); + +MODULE_AUTHOR("Griffin Kroah-Hartman <griffin.kroah@fairphone.com>"); +MODULE_DESCRIPTION("AWINIC AW86927 LRA Haptic Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/cma3000_d0x.c b/drivers/input/misc/cma3000_d0x.c index cfc12332bee1..b4232b0a3957 100644 --- a/drivers/input/misc/cma3000_d0x.c +++ b/drivers/input/misc/cma3000_d0x.c @@ -6,6 +6,7 @@ * Author: Hemanth V <hemanthv@ti.com> */ +#include <linux/export.h> #include <linux/types.h> #include <linux/interrupt.h> #include <linux/delay.h> diff --git a/drivers/input/misc/max7360-rotary.c b/drivers/input/misc/max7360-rotary.c new file mode 100644 index 000000000000..385831ef34b6 --- /dev/null +++ b/drivers/input/misc/max7360-rotary.c @@ -0,0 +1,192 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2025 Bootlin + * + * Author: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com> + */ + +#include <linux/bitfield.h> +#include <linux/device/devres.h> +#include <linux/dev_printk.h> +#include <linux/init.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/mfd/max7360.h> +#include <linux/property.h> +#include <linux/platform_device.h> +#include <linux/pm_wakeirq.h> +#include <linux/regmap.h> +#include <linux/types.h> + +#define MAX7360_ROTARY_DEFAULT_STEPS 24 + +struct max7360_rotary { + struct input_dev *input; + struct regmap *regmap; + unsigned int debounce_ms; + + unsigned int pos; + + u32 steps; + u32 axis; + bool relative_axis; + bool rollover; +}; + +static void max7360_rotary_report_event(struct max7360_rotary *max7360_rotary, int steps) +{ + if (max7360_rotary->relative_axis) { + input_report_rel(max7360_rotary->input, max7360_rotary->axis, steps); + } else { + int pos = max7360_rotary->pos; + int maxval = max7360_rotary->steps; + + /* + * Add steps to the position. + * Make sure added steps are always in ]-maxval; maxval[ + * interval, so (pos + maxval) is always >= 0. + * Then set back pos to the [0; maxval[ interval. + */ + pos += steps % maxval; + if (max7360_rotary->rollover) + pos = (pos + maxval) % maxval; + else + pos = clamp(pos, 0, maxval - 1); + + max7360_rotary->pos = pos; + input_report_abs(max7360_rotary->input, max7360_rotary->axis, max7360_rotary->pos); + } + + input_sync(max7360_rotary->input); +} + +static irqreturn_t max7360_rotary_irq(int irq, void *data) +{ + struct max7360_rotary *max7360_rotary = data; + struct device *dev = max7360_rotary->input->dev.parent; + unsigned int val; + int error; + + error = regmap_read(max7360_rotary->regmap, MAX7360_REG_RTR_CNT, &val); + if (error < 0) { + dev_err(dev, "Failed to read rotary counter\n"); + return IRQ_NONE; + } + + if (val == 0) + return IRQ_NONE; + + max7360_rotary_report_event(max7360_rotary, sign_extend32(val, 7)); + + return IRQ_HANDLED; +} + +static int max7360_rotary_hw_init(struct max7360_rotary *max7360_rotary) +{ + struct device *dev = max7360_rotary->input->dev.parent; + int val; + int error; + + val = FIELD_PREP(MAX7360_ROT_DEBOUNCE, max7360_rotary->debounce_ms) | + FIELD_PREP(MAX7360_ROT_INTCNT, 1) | MAX7360_ROT_INTCNT_DLY; + error = regmap_write(max7360_rotary->regmap, MAX7360_REG_RTRCFG, val); + if (error) + dev_err(dev, "Failed to set max7360 rotary encoder configuration\n"); + + return error; +} + +static int max7360_rotary_probe(struct platform_device *pdev) +{ + struct max7360_rotary *max7360_rotary; + struct device *dev = &pdev->dev; + struct input_dev *input; + struct regmap *regmap; + int irq; + int error; + + regmap = dev_get_regmap(dev->parent, NULL); + if (!regmap) + return dev_err_probe(dev, -ENODEV, "Could not get parent regmap\n"); + + irq = fwnode_irq_get_byname(dev_fwnode(dev->parent), "inti"); + if (irq < 0) + return dev_err_probe(dev, irq, "Failed to get IRQ\n"); + + max7360_rotary = devm_kzalloc(dev, sizeof(*max7360_rotary), GFP_KERNEL); + if (!max7360_rotary) + return -ENOMEM; + + max7360_rotary->regmap = regmap; + + device_property_read_u32(dev->parent, "linux,axis", &max7360_rotary->axis); + max7360_rotary->rollover = device_property_read_bool(dev->parent, + "rotary-encoder,rollover"); + max7360_rotary->relative_axis = + device_property_read_bool(dev->parent, "rotary-encoder,relative-axis"); + + error = device_property_read_u32(dev->parent, "rotary-encoder,steps", + &max7360_rotary->steps); + if (error) + max7360_rotary->steps = MAX7360_ROTARY_DEFAULT_STEPS; + + device_property_read_u32(dev->parent, "rotary-debounce-delay-ms", + &max7360_rotary->debounce_ms); + if (max7360_rotary->debounce_ms > MAX7360_ROT_DEBOUNCE_MAX) + return dev_err_probe(dev, -EINVAL, "Invalid debounce timing: %u\n", + max7360_rotary->debounce_ms); + + input = devm_input_allocate_device(dev); + if (!input) + return -ENOMEM; + + max7360_rotary->input = input; + + input->id.bustype = BUS_I2C; + input->name = pdev->name; + + if (max7360_rotary->relative_axis) + input_set_capability(input, EV_REL, max7360_rotary->axis); + else + input_set_abs_params(input, max7360_rotary->axis, 0, max7360_rotary->steps, 0, 1); + + error = devm_request_threaded_irq(dev, irq, NULL, max7360_rotary_irq, + IRQF_ONESHOT | IRQF_SHARED, + "max7360-rotary", max7360_rotary); + if (error) + return dev_err_probe(dev, error, "Failed to register interrupt\n"); + + error = input_register_device(input); + if (error) + return dev_err_probe(dev, error, "Could not register input device\n"); + + error = max7360_rotary_hw_init(max7360_rotary); + if (error) + return dev_err_probe(dev, error, "Failed to initialize max7360 rotary\n"); + + device_init_wakeup(dev, true); + error = dev_pm_set_wake_irq(dev, irq); + if (error) + dev_warn(dev, "Failed to set up wakeup irq: %d\n", error); + + return 0; +} + +static void max7360_rotary_remove(struct platform_device *pdev) +{ + dev_pm_clear_wake_irq(&pdev->dev); + device_init_wakeup(&pdev->dev, false); +} + +static struct platform_driver max7360_rotary_driver = { + .driver = { + .name = "max7360-rotary", + }, + .probe = max7360_rotary_probe, + .remove = max7360_rotary_remove, +}; +module_platform_driver(max7360_rotary_driver); + +MODULE_DESCRIPTION("MAX7360 Rotary driver"); +MODULE_AUTHOR("Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/mc13783-pwrbutton.c b/drivers/input/misc/mc13783-pwrbutton.c index 1c7faa9b7afe..b83d762ae2e9 100644 --- a/drivers/input/misc/mc13783-pwrbutton.c +++ b/drivers/input/misc/mc13783-pwrbutton.c @@ -57,7 +57,6 @@ static irqreturn_t button_irq(int irq, void *_priv) struct mc13783_pwrb *priv = _priv; int val; - mc13xxx_irq_ack(priv->mc13783, irq); mc13xxx_reg_read(priv->mc13783, MC13783_REG_INTERRUPT_SENSE_1, &val); switch (irq) { diff --git a/drivers/input/misc/pm8941-pwrkey.c b/drivers/input/misc/pm8941-pwrkey.c index d952c16f2458..53249d2c081f 100644 --- a/drivers/input/misc/pm8941-pwrkey.c +++ b/drivers/input/misc/pm8941-pwrkey.c @@ -60,6 +60,7 @@ struct pm8941_data { bool supports_ps_hold_poff_config; bool supports_debounce_config; bool has_pon_pbs; + bool wakeup_source_default; const char *name; const char *phys; }; @@ -245,7 +246,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(pm8941_pwr_key_pm_ops, static int pm8941_pwrkey_probe(struct platform_device *pdev) { struct pm8941_pwrkey *pwrkey; - bool pull_up; + bool pull_up, wakeup; struct device *parent; struct device_node *regmap_node; const __be32 *addr; @@ -402,8 +403,11 @@ static int pm8941_pwrkey_probe(struct platform_device *pdev) } } + wakeup = pwrkey->data->wakeup_source_default || + of_property_read_bool(pdev->dev.of_node, "wakeup-source"); + platform_set_drvdata(pdev, pwrkey); - device_init_wakeup(&pdev->dev, 1); + device_init_wakeup(&pdev->dev, wakeup); return 0; } @@ -424,6 +428,7 @@ static const struct pm8941_data pwrkey_data = { .supports_ps_hold_poff_config = true, .supports_debounce_config = true, .has_pon_pbs = false, + .wakeup_source_default = true, }; static const struct pm8941_data resin_data = { @@ -434,6 +439,7 @@ static const struct pm8941_data resin_data = { .supports_ps_hold_poff_config = true, .supports_debounce_config = true, .has_pon_pbs = false, + .wakeup_source_default = false, }; static const struct pm8941_data pon_gen3_pwrkey_data = { @@ -443,6 +449,7 @@ static const struct pm8941_data pon_gen3_pwrkey_data = { .supports_ps_hold_poff_config = false, .supports_debounce_config = false, .has_pon_pbs = true, + .wakeup_source_default = true, }; static const struct pm8941_data pon_gen3_resin_data = { @@ -452,6 +459,7 @@ static const struct pm8941_data pon_gen3_resin_data = { .supports_ps_hold_poff_config = false, .supports_debounce_config = false, .has_pon_pbs = true, + .wakeup_source_default = false, }; static const struct of_device_id pm8941_pwr_key_id_table[] = { diff --git a/drivers/input/misc/tps6594-pwrbutton.c b/drivers/input/misc/tps6594-pwrbutton.c new file mode 100644 index 000000000000..cd039b3866dc --- /dev/null +++ b/drivers/input/misc/tps6594-pwrbutton.c @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * power button driver for TI TPS6594 PMICs + * + * Copyright (C) 2025 Critical Link LLC - https://www.criticallink.com/ + */ +#include <linux/init.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/mfd/tps6594.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +struct tps6594_pwrbutton { + struct device *dev; + struct input_dev *idev; + char phys[32]; +}; + +static irqreturn_t tps6594_pb_push_irq(int irq, void *_pwr) +{ + struct tps6594_pwrbutton *pwr = _pwr; + + input_report_key(pwr->idev, KEY_POWER, 1); + pm_wakeup_event(pwr->dev, 0); + input_sync(pwr->idev); + + return IRQ_HANDLED; +} + +static irqreturn_t tps6594_pb_release_irq(int irq, void *_pwr) +{ + struct tps6594_pwrbutton *pwr = _pwr; + + input_report_key(pwr->idev, KEY_POWER, 0); + input_sync(pwr->idev); + + return IRQ_HANDLED; +} + +static int tps6594_pb_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct tps6594_pwrbutton *pwr; + struct input_dev *idev; + int error; + int push_irq; + int release_irq; + + pwr = devm_kzalloc(dev, sizeof(*pwr), GFP_KERNEL); + if (!pwr) + return -ENOMEM; + + idev = devm_input_allocate_device(dev); + if (!idev) + return -ENOMEM; + + idev->name = pdev->name; + snprintf(pwr->phys, sizeof(pwr->phys), "%s/input0", + pdev->name); + idev->phys = pwr->phys; + idev->id.bustype = BUS_I2C; + + input_set_capability(idev, EV_KEY, KEY_POWER); + + pwr->dev = dev; + pwr->idev = idev; + device_init_wakeup(dev, true); + + push_irq = platform_get_irq(pdev, 0); + if (push_irq < 0) + return -EINVAL; + + release_irq = platform_get_irq(pdev, 1); + if (release_irq < 0) + return -EINVAL; + + error = devm_request_threaded_irq(dev, push_irq, NULL, + tps6594_pb_push_irq, + IRQF_ONESHOT, + pdev->resource[0].name, pwr); + if (error) { + dev_err(dev, "failed to request push IRQ #%d: %d\n", push_irq, + error); + return error; + } + + error = devm_request_threaded_irq(dev, release_irq, NULL, + tps6594_pb_release_irq, + IRQF_ONESHOT, + pdev->resource[1].name, pwr); + if (error) { + dev_err(dev, "failed to request release IRQ #%d: %d\n", + release_irq, error); + return error; + } + + error = input_register_device(idev); + if (error) { + dev_err(dev, "Can't register power button: %d\n", error); + return error; + } + + return 0; +} + +static const struct platform_device_id tps6594_pwrbtn_id_table[] = { + { "tps6594-pwrbutton", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(platform, tps6594_pwrbtn_id_table); + +static struct platform_driver tps6594_pb_driver = { + .probe = tps6594_pb_probe, + .driver = { + .name = "tps6594_pwrbutton", + }, + .id_table = tps6594_pwrbtn_id_table, +}; +module_platform_driver(tps6594_pb_driver); + +MODULE_DESCRIPTION("TPS6594 Power Button"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c index 2c51ea9d01d7..13336a2fd49c 100644 --- a/drivers/input/misc/uinput.c +++ b/drivers/input/misc/uinput.c @@ -775,6 +775,7 @@ static int uinput_ff_upload_to_user(char __user *buffer, if (in_compat_syscall()) { struct uinput_ff_upload_compat ff_up_compat; + memset(&ff_up_compat, 0, sizeof(ff_up_compat)); ff_up_compat.request_id = ff_up->request_id; ff_up_compat.retval = ff_up->retval; /* diff --git a/drivers/input/rmi4/rmi_2d_sensor.c b/drivers/input/rmi4/rmi_2d_sensor.c index b7fe6eb35a4e..ea3eb87a89af 100644 --- a/drivers/input/rmi4/rmi_2d_sensor.c +++ b/drivers/input/rmi4/rmi_2d_sensor.c @@ -4,6 +4,7 @@ * Copyright (c) 2011 Unixphere */ +#include <linux/export.h> #include <linux/kernel.h> #include <linux/device.h> #include <linux/of.h> diff --git a/drivers/input/rmi4/rmi_2d_sensor.h b/drivers/input/rmi4/rmi_2d_sensor.h index 7d335d809710..61a99c8a7a26 100644 --- a/drivers/input/rmi4/rmi_2d_sensor.h +++ b/drivers/input/rmi4/rmi_2d_sensor.h @@ -7,6 +7,9 @@ #ifndef _RMI_2D_SENSOR_H #define _RMI_2D_SENSOR_H +#include <linux/rmi.h> +#include <linux/types.h> + enum rmi_2d_sensor_object_type { RMI_2D_OBJECT_NONE, RMI_2D_OBJECT_FINGER, diff --git a/drivers/input/rmi4/rmi_bus.c b/drivers/input/rmi4/rmi_bus.c index 5f98c3bcfd46..b85ee9db87b0 100644 --- a/drivers/input/rmi4/rmi_bus.c +++ b/drivers/input/rmi4/rmi_bus.c @@ -4,6 +4,7 @@ * Copyright (c) 2011 Unixphere */ +#include <linux/export.h> #include <linux/kernel.h> #include <linux/device.h> #include <linux/irq.h> diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c index 2168b6cd7167..ccd9338a44db 100644 --- a/drivers/input/rmi4/rmi_driver.c +++ b/drivers/input/rmi4/rmi_driver.c @@ -21,6 +21,7 @@ #include <linux/irqdomain.h> #include <uapi/linux/input.h> #include <linux/rmi.h> +#include <linux/export.h> #include "rmi_bus.h" #include "rmi_driver.h" diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig index 17edc1597446..c7ef347a4dff 100644 --- a/drivers/input/serio/Kconfig +++ b/drivers/input/serio/Kconfig @@ -276,8 +276,8 @@ config SERIO_OLPC_APSP config HYPERV_KEYBOARD tristate "Microsoft Synthetic Keyboard driver" - depends on HYPERV - default HYPERV + depends on HYPERV_VMBUS + default HYPERV_VMBUS help Select this option to enable the Hyper-V Keyboard driver. diff --git a/drivers/input/serio/hil_mlc.c b/drivers/input/serio/hil_mlc.c index 94e8bcbbf94d..3fedfc5abc73 100644 --- a/drivers/input/serio/hil_mlc.c +++ b/drivers/input/serio/hil_mlc.c @@ -54,6 +54,7 @@ #include <linux/hil_mlc.h> #include <linux/errno.h> +#include <linux/export.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> diff --git a/drivers/input/serio/hp_sdc.c b/drivers/input/serio/hp_sdc.c index 0eec4c5585cb..1461ef319f92 100644 --- a/drivers/input/serio/hp_sdc.c +++ b/drivers/input/serio/hp_sdc.c @@ -63,6 +63,7 @@ #include <linux/hp_sdc.h> #include <linux/errno.h> +#include <linux/export.h> #include <linux/init.h> #include <linux/module.h> #include <linux/ioport.h> diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index cab5a4c5baf5..c135254665b6 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -10,6 +10,7 @@ #include <linux/types.h> #include <linux/delay.h> +#include <linux/export.h> #include <linux/module.h> #include <linux/interrupt.h> #include <linux/ioport.h> diff --git a/drivers/input/serio/libps2.c b/drivers/input/serio/libps2.c index c22ea532276e..269df83a167d 100644 --- a/drivers/input/serio/libps2.c +++ b/drivers/input/serio/libps2.c @@ -8,6 +8,7 @@ #include <linux/delay.h> +#include <linux/export.h> #include <linux/module.h> #include <linux/sched.h> #include <linux/interrupt.h> diff --git a/drivers/input/serio/ps2-gpio.c b/drivers/input/serio/ps2-gpio.c index 93769910ce24..46fb7667b244 100644 --- a/drivers/input/serio/ps2-gpio.c +++ b/drivers/input/serio/ps2-gpio.c @@ -50,7 +50,7 @@ * interrupt interval should be ~60us. Let's allow +/- 20us for frequency * deviations and interrupt latency. * - * The data line must be samples after ~30us to 50us after the falling edge, + * The data line must be sampled after ~30us to 50us after the falling edge, * since the device updates the data line at the rising edge. * * ___ ______ ______ ______ ___ diff --git a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c index 4468018cef66..2b5ddc5dac19 100644 --- a/drivers/input/serio/serio.c +++ b/drivers/input/serio/serio.c @@ -9,6 +9,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <linux/export.h> #include <linux/stddef.h> #include <linux/module.h> #include <linux/serio.h> diff --git a/drivers/input/sparse-keymap.c b/drivers/input/sparse-keymap.c index 96f23ae57d5a..164f8fcfd1aa 100644 --- a/drivers/input/sparse-keymap.c +++ b/drivers/input/sparse-keymap.c @@ -10,6 +10,7 @@ * Copyright (C) 2005 Dmitry Torokhov <dtor@mail.ru> */ +#include <linux/export.h> #include <linux/input.h> #include <linux/input/sparse-keymap.h> #include <linux/module.h> diff --git a/drivers/input/touch-overlay.c b/drivers/input/touch-overlay.c index 8806373f7a4a..b9fd82c4829d 100644 --- a/drivers/input/touch-overlay.c +++ b/drivers/input/touch-overlay.c @@ -5,6 +5,7 @@ * Copyright (c) 2023 Javier Carrasco <javier.carrasco@wolfvision.net> */ +#include <linux/export.h> #include <linux/input.h> #include <linux/input/mt.h> #include <linux/input/touch-overlay.h> diff --git a/drivers/input/touchscreen.c b/drivers/input/touchscreen.c index 4620e20d0190..d699b24bb548 100644 --- a/drivers/input/touchscreen.c +++ b/drivers/input/touchscreen.c @@ -6,6 +6,7 @@ * Copyright (c) 2014 Sebastian Reichel <sre@kernel.org> */ +#include <linux/export.h> #include <linux/property.h> #include <linux/input.h> #include <linux/input/mt.h> diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 196905162945..7d5b72ee07fa 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -441,6 +441,16 @@ config TOUCHSCREEN_HIDEEP To compile this driver as a module, choose M here : the module will be called hideep_ts. +config TOUCHSCREEN_HIMAX_HX852X + tristate "Himax HX852x(ES) touchscreen" + depends on I2C + help + Say Y here if you have a Himax HX852x(ES) touchscreen. + If unsure, say N. + + To compile this driver as a module, choose M here: the module + will be called himax_hx852x. + config TOUCHSCREEN_HYCON_HY46XX tristate "Hycon hy46xx touchscreen support" depends on I2C @@ -465,6 +475,18 @@ config TOUCHSCREEN_HYNITRON_CSTXXX To compile this driver as a module, choose M here: the module will be called hynitron-cstxxx. +config TOUCHSCREEN_HYNITRON_CST816X + tristate "Hynitron CST816x touchscreen" + depends on I2C + help + Say Y here if you have a touchscreen using a Hynitron + CST816x series touchscreen controller. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called hynitron-cst816x. + config TOUCHSCREEN_ILI210X tristate "Ilitek ILI210X based touchscreen" depends on I2C diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 97a025c6a377..ab9abd151078 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -49,7 +49,9 @@ obj-$(CONFIG_TOUCHSCREEN_GOODIX_BERLIN_CORE) += goodix_berlin_core.o obj-$(CONFIG_TOUCHSCREEN_GOODIX_BERLIN_I2C) += goodix_berlin_i2c.o obj-$(CONFIG_TOUCHSCREEN_GOODIX_BERLIN_SPI) += goodix_berlin_spi.o obj-$(CONFIG_TOUCHSCREEN_HIDEEP) += hideep.o +obj-$(CONFIG_TOUCHSCREEN_HIMAX_HX852X) += himax_hx852x.o obj-$(CONFIG_TOUCHSCREEN_HYNITRON_CSTXXX) += hynitron_cstxxx.o +obj-$(CONFIG_TOUCHSCREEN_HYNITRON_CST816X) += hynitron-cst816x.o obj-$(CONFIG_TOUCHSCREEN_ILI210X) += ili210x.o obj-$(CONFIG_TOUCHSCREEN_ILITEK) += ilitek_ts_i2c.o obj-$(CONFIG_TOUCHSCREEN_IMAGIS) += imagis.o diff --git a/drivers/input/touchscreen/ad7879.c b/drivers/input/touchscreen/ad7879.c index 8b4f3e3660b8..4c448f39bf57 100644 --- a/drivers/input/touchscreen/ad7879.c +++ b/drivers/input/touchscreen/ad7879.c @@ -22,6 +22,7 @@ #include <linux/device.h> #include <linux/delay.h> +#include <linux/export.h> #include <linux/input.h> #include <linux/interrupt.h> #include <linux/irq.h> diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 322d5a3d40a0..dd0544cc1bc1 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -19,6 +19,7 @@ #include <linux/firmware.h> #include <linux/i2c.h> #include <linux/input/mt.h> +#include <linux/input/touchscreen.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/of.h> @@ -355,6 +356,8 @@ struct mxt_data { enum mxt_suspend_mode suspend_mode; u32 wakeup_method; + + struct touchscreen_properties prop; }; struct mxt_vb2_buffer { @@ -888,8 +891,7 @@ static void mxt_proc_t9_message(struct mxt_data *data, u8 *message) /* Touch active */ input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, 1); - input_report_abs(input_dev, ABS_MT_POSITION_X, x); - input_report_abs(input_dev, ABS_MT_POSITION_Y, y); + touchscreen_report_pos(input_dev, &data->prop, x, y, true); input_report_abs(input_dev, ABS_MT_PRESSURE, amplitude); input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, area); } else { @@ -1010,8 +1012,7 @@ static void mxt_proc_t100_message(struct mxt_data *data, u8 *message) 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); + touchscreen_report_pos(input_dev, &data->prop, x, y, true); 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); @@ -2212,6 +2213,8 @@ static int mxt_initialize_input_device(struct mxt_data *data) 0, 255, 0, 0); } + touchscreen_parse_properties(input_dev, true, &data->prop); + /* For T15 and T97 Key Array */ if (data->T15_reportid_min || data->T97_reportid_min) { for (i = 0; i < data->t15_num_keys; i++) @@ -3317,7 +3320,7 @@ static int mxt_probe(struct i2c_client *client) if (data->reset_gpio) { /* Wait a while and then de-assert the RESET GPIO line */ msleep(MXT_RESET_GPIO_TIME); - gpiod_set_value(data->reset_gpio, 0); + gpiod_set_value_cansleep(data->reset_gpio, 0); msleep(MXT_RESET_INVALID_CHG); } diff --git a/drivers/input/touchscreen/cyttsp_core.c b/drivers/input/touchscreen/cyttsp_core.c index b8ce6012364c..9e729910fbc8 100644 --- a/drivers/input/touchscreen/cyttsp_core.c +++ b/drivers/input/touchscreen/cyttsp_core.c @@ -14,6 +14,7 @@ */ #include <linux/delay.h> +#include <linux/export.h> #include <linux/input.h> #include <linux/input/mt.h> #include <linux/input/touchscreen.h> diff --git a/drivers/input/touchscreen/fsl-imx25-tcq.c b/drivers/input/touchscreen/fsl-imx25-tcq.c index a32708652d10..ff270b3b8572 100644 --- a/drivers/input/touchscreen/fsl-imx25-tcq.c +++ b/drivers/input/touchscreen/fsl-imx25-tcq.c @@ -39,7 +39,6 @@ struct mx25_tcq_priv { }; static const struct regmap_config mx25_tcq_regconfig = { - .fast_io = true, .max_register = 0x5c, .reg_bits = 32, .val_bits = 32, diff --git a/drivers/input/touchscreen/goodix_berlin_core.c b/drivers/input/touchscreen/goodix_berlin_core.c index c78d512d97cd..83f28b870531 100644 --- a/drivers/input/touchscreen/goodix_berlin_core.c +++ b/drivers/input/touchscreen/goodix_berlin_core.c @@ -24,6 +24,7 @@ */ #include <linux/bitfield.h> +#include <linux/export.h> #include <linux/gpio/consumer.h> #include <linux/input.h> #include <linux/input/mt.h> diff --git a/drivers/input/touchscreen/himax_hx852x.c b/drivers/input/touchscreen/himax_hx852x.c new file mode 100644 index 000000000000..83c60e137a55 --- /dev/null +++ b/drivers/input/touchscreen/himax_hx852x.c @@ -0,0 +1,503 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Himax HX852x(ES) Touchscreen Driver + * Copyright (c) 2020-2024 Stephan Gerhold <stephan@gerhold.net> + * Copyright (c) 2020 Jonathan Albrieux <jonathan.albrieux@gmail.com> + * + * Based on the Himax Android Driver Sample Code Ver 0.3 for HMX852xES chipset: + * Copyright (c) 2014 Himax Corporation. + */ + +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/input.h> +#include <linux/input/mt.h> +#include <linux/input/touchscreen.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/regulator/consumer.h> +#include <linux/unaligned.h> + +#define HX852X_COORD_SIZE(fingers) ((fingers) * sizeof(struct hx852x_coord)) +#define HX852X_WIDTH_SIZE(fingers) ALIGN(fingers, 4) +#define HX852X_BUF_SIZE(fingers) (HX852X_COORD_SIZE(fingers) + \ + HX852X_WIDTH_SIZE(fingers) + \ + sizeof(struct hx852x_touch_info)) + +#define HX852X_MAX_FINGERS 12 +#define HX852X_MAX_KEY_COUNT 4 +#define HX852X_MAX_BUF_SIZE HX852X_BUF_SIZE(HX852X_MAX_FINGERS) + +#define HX852X_TS_SLEEP_IN 0x80 +#define HX852X_TS_SLEEP_OUT 0x81 +#define HX852X_TS_SENSE_OFF 0x82 +#define HX852X_TS_SENSE_ON 0x83 +#define HX852X_READ_ONE_EVENT 0x85 +#define HX852X_READ_ALL_EVENTS 0x86 +#define HX852X_READ_LATEST_EVENT 0x87 +#define HX852X_CLEAR_EVENT_STACK 0x88 + +#define HX852X_REG_SRAM_SWITCH 0x8c +#define HX852X_REG_SRAM_ADDR 0x8b +#define HX852X_REG_FLASH_RPLACE 0x5a + +#define HX852X_SRAM_SWITCH_TEST_MODE 0x14 +#define HX852X_SRAM_ADDR_CONFIG 0x7000 + +struct hx852x { + struct i2c_client *client; + struct input_dev *input_dev; + struct touchscreen_properties props; + struct gpio_desc *reset_gpiod; + struct regulator_bulk_data supplies[2]; + unsigned int max_fingers; + unsigned int keycount; + unsigned int keycodes[HX852X_MAX_KEY_COUNT]; +}; + +struct hx852x_config { + u8 rx_num; + u8 tx_num; + u8 max_pt; + u8 padding1[3]; + __be16 x_res; + __be16 y_res; + u8 padding2[2]; +} __packed __aligned(4); + +struct hx852x_coord { + __be16 x; + __be16 y; +} __packed __aligned(4); + +struct hx852x_touch_info { + u8 finger_num; + __le16 finger_pressed; + u8 padding; +} __packed __aligned(4); + +static int hx852x_i2c_read(struct hx852x *hx, u8 cmd, void *data, u16 len) +{ + struct i2c_client *client = hx->client; + int error; + int ret; + + struct i2c_msg msg[] = { + { + .addr = client->addr, + .flags = 0, + .len = 1, + .buf = &cmd, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = len, + .buf = data, + }, + }; + + ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); + if (ret != ARRAY_SIZE(msg)) { + error = ret < 0 ? ret : -EIO; + dev_err(&client->dev, "failed to read %#x: %d\n", cmd, error); + return error; + } + + return 0; +} + +static int hx852x_power_on(struct hx852x *hx) +{ + struct device *dev = &hx->client->dev; + int error; + + error = regulator_bulk_enable(ARRAY_SIZE(hx->supplies), hx->supplies); + if (error) { + dev_err(dev, "failed to enable regulators: %d\n", error); + return error; + } + + gpiod_set_value_cansleep(hx->reset_gpiod, 1); + msleep(20); + gpiod_set_value_cansleep(hx->reset_gpiod, 0); + msleep(50); + + return 0; +} + +static int hx852x_start(struct hx852x *hx) +{ + struct device *dev = &hx->client->dev; + int error; + + error = i2c_smbus_write_byte(hx->client, HX852X_TS_SLEEP_OUT); + if (error) { + dev_err(dev, "failed to send TS_SLEEP_OUT: %d\n", error); + return error; + } + msleep(30); + + error = i2c_smbus_write_byte(hx->client, HX852X_TS_SENSE_ON); + if (error) { + dev_err(dev, "failed to send TS_SENSE_ON: %d\n", error); + return error; + } + msleep(20); + + return 0; +} + +static int hx852x_stop(struct hx852x *hx) +{ + struct device *dev = &hx->client->dev; + int error; + + error = i2c_smbus_write_byte(hx->client, HX852X_TS_SENSE_OFF); + if (error) { + dev_err(dev, "failed to send TS_SENSE_OFF: %d\n", error); + return error; + } + msleep(20); + + error = i2c_smbus_write_byte(hx->client, HX852X_TS_SLEEP_IN); + if (error) { + dev_err(dev, "failed to send TS_SLEEP_IN: %d\n", error); + return error; + } + msleep(30); + + return 0; +} + +static int hx852x_power_off(struct hx852x *hx) +{ + struct device *dev = &hx->client->dev; + int error; + + error = regulator_bulk_disable(ARRAY_SIZE(hx->supplies), hx->supplies); + if (error) { + dev_err(dev, "failed to disable regulators: %d\n", error); + return error; + } + + return 0; +} + +static int hx852x_read_config(struct hx852x *hx) +{ + struct device *dev = &hx->client->dev; + struct hx852x_config conf; + int x_res, y_res; + int error, error2; + + error = hx852x_power_on(hx); + if (error) + return error; + + /* Sensing must be turned on briefly to load the config */ + error = hx852x_start(hx); + if (error) + goto err_power_off; + + error = hx852x_stop(hx); + if (error) + goto err_power_off; + + error = i2c_smbus_write_byte_data(hx->client, HX852X_REG_SRAM_SWITCH, + HX852X_SRAM_SWITCH_TEST_MODE); + if (error) + goto err_power_off; + + error = i2c_smbus_write_word_data(hx->client, HX852X_REG_SRAM_ADDR, + HX852X_SRAM_ADDR_CONFIG); + if (error) + goto err_test_mode; + + error = hx852x_i2c_read(hx, HX852X_REG_FLASH_RPLACE, &conf, sizeof(conf)); + if (error) + goto err_test_mode; + + x_res = be16_to_cpu(conf.x_res); + y_res = be16_to_cpu(conf.y_res); + hx->max_fingers = (conf.max_pt & 0xf0) >> 4; + dev_dbg(dev, "x res: %u, y res: %u, max fingers: %u\n", + x_res, y_res, hx->max_fingers); + + if (hx->max_fingers > HX852X_MAX_FINGERS) { + dev_err(dev, "max supported fingers: %u, found: %u\n", + HX852X_MAX_FINGERS, hx->max_fingers); + error = -EINVAL; + goto err_test_mode; + } + + if (x_res && y_res) { + input_set_abs_params(hx->input_dev, ABS_MT_POSITION_X, 0, x_res - 1, 0, 0); + input_set_abs_params(hx->input_dev, ABS_MT_POSITION_Y, 0, y_res - 1, 0, 0); + } + +err_test_mode: + error2 = i2c_smbus_write_byte_data(hx->client, HX852X_REG_SRAM_SWITCH, 0); + error = error ?: error2; +err_power_off: + error2 = hx852x_power_off(hx); + return error ?: error2; +} + +static int hx852x_handle_events(struct hx852x *hx) +{ + /* + * The event packets have variable size, depending on the amount of + * supported fingers (hx->max_fingers). They are laid out as follows: + * - struct hx852x_coord[hx->max_fingers]: Coordinates for each finger + * - u8[ALIGN(hx->max_fingers, 4)]: Touch width for each finger + * with padding for 32-bit alignment + * - struct hx852x_touch_info + * + * Load everything into a 32-bit aligned buffer so the coordinates + * can be assigned directly, without using get_unaligned_*(). + */ + u8 buf[HX852X_MAX_BUF_SIZE] __aligned(4); + struct hx852x_coord *coord = (struct hx852x_coord *)buf; + u8 *width = &buf[HX852X_COORD_SIZE(hx->max_fingers)]; + struct hx852x_touch_info *info = (struct hx852x_touch_info *) + &width[HX852X_WIDTH_SIZE(hx->max_fingers)]; + unsigned long finger_pressed, key_pressed; + unsigned int i, x, y, w; + int error; + + error = hx852x_i2c_read(hx, HX852X_READ_ALL_EVENTS, buf, + HX852X_BUF_SIZE(hx->max_fingers)); + if (error) + return error; + + finger_pressed = get_unaligned_le16(&info->finger_pressed); + key_pressed = finger_pressed >> HX852X_MAX_FINGERS; + + /* All bits are set when no touch is detected */ + if (info->finger_num == 0xff || !(info->finger_num & 0x0f)) + finger_pressed = 0; + if (key_pressed == 0xf) + key_pressed = 0; + + for_each_set_bit(i, &finger_pressed, hx->max_fingers) { + x = be16_to_cpu(coord[i].x); + y = be16_to_cpu(coord[i].y); + w = width[i]; + + input_mt_slot(hx->input_dev, i); + input_mt_report_slot_state(hx->input_dev, MT_TOOL_FINGER, 1); + touchscreen_report_pos(hx->input_dev, &hx->props, x, y, true); + input_report_abs(hx->input_dev, ABS_MT_TOUCH_MAJOR, w); + } + input_mt_sync_frame(hx->input_dev); + + for (i = 0; i < hx->keycount; i++) + input_report_key(hx->input_dev, hx->keycodes[i], key_pressed & BIT(i)); + + input_sync(hx->input_dev); + return 0; +} + +static irqreturn_t hx852x_interrupt(int irq, void *ptr) +{ + struct hx852x *hx = ptr; + int error; + + error = hx852x_handle_events(hx); + if (error) { + dev_err_ratelimited(&hx->client->dev, + "failed to handle events: %d\n", error); + return IRQ_NONE; + } + + return IRQ_HANDLED; +} + +static int hx852x_input_open(struct input_dev *dev) +{ + struct hx852x *hx = input_get_drvdata(dev); + int error; + + error = hx852x_power_on(hx); + if (error) + return error; + + error = hx852x_start(hx); + if (error) { + hx852x_power_off(hx); + return error; + } + + enable_irq(hx->client->irq); + return 0; +} + +static void hx852x_input_close(struct input_dev *dev) +{ + struct hx852x *hx = input_get_drvdata(dev); + + hx852x_stop(hx); + disable_irq(hx->client->irq); + hx852x_power_off(hx); +} + +static int hx852x_parse_properties(struct hx852x *hx) +{ + struct device *dev = &hx->client->dev; + int error, count; + + count = device_property_count_u32(dev, "linux,keycodes"); + if (count == -EINVAL) { + /* Property does not exist, keycodes are optional */ + return 0; + } else if (count < 0) { + dev_err(dev, "Failed to read linux,keycodes: %d\n", count); + return count; + } else if (count > HX852X_MAX_KEY_COUNT) { + dev_err(dev, "max supported keys: %u, found: %u\n", + HX852X_MAX_KEY_COUNT, hx->keycount); + return -EINVAL; + } + hx->keycount = count; + + error = device_property_read_u32_array(dev, "linux,keycodes", + hx->keycodes, hx->keycount); + if (error) { + dev_err(dev, "failed to read linux,keycodes: %d\n", error); + return error; + } + + return 0; +} + +static int hx852x_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct hx852x *hx; + int error, i; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C | + I2C_FUNC_SMBUS_WRITE_BYTE | + I2C_FUNC_SMBUS_WRITE_BYTE_DATA | + I2C_FUNC_SMBUS_WRITE_WORD_DATA)) { + dev_err(dev, "not all required i2c functionality supported\n"); + return -ENXIO; + } + + hx = devm_kzalloc(dev, sizeof(*hx), GFP_KERNEL); + if (!hx) + return -ENOMEM; + + hx->client = client; + hx->input_dev = devm_input_allocate_device(dev); + if (!hx->input_dev) + return -ENOMEM; + + hx->input_dev->name = "Himax HX852x"; + hx->input_dev->id.bustype = BUS_I2C; + hx->input_dev->open = hx852x_input_open; + hx->input_dev->close = hx852x_input_close; + + i2c_set_clientdata(client, hx); + input_set_drvdata(hx->input_dev, hx); + + hx->supplies[0].supply = "vcca"; + hx->supplies[1].supply = "vccd"; + error = devm_regulator_bulk_get(dev, ARRAY_SIZE(hx->supplies), hx->supplies); + if (error) + return dev_err_probe(dev, error, "failed to get regulators\n"); + + hx->reset_gpiod = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(hx->reset_gpiod)) + return dev_err_probe(dev, PTR_ERR(hx->reset_gpiod), + "failed to get reset gpio\n"); + + error = devm_request_threaded_irq(dev, client->irq, NULL, hx852x_interrupt, + IRQF_ONESHOT | IRQF_NO_AUTOEN, NULL, hx); + if (error) + return dev_err_probe(dev, error, "failed to request irq %d", client->irq); + + error = hx852x_read_config(hx); + if (error) + return error; + + input_set_capability(hx->input_dev, EV_ABS, ABS_MT_POSITION_X); + input_set_capability(hx->input_dev, EV_ABS, ABS_MT_POSITION_Y); + input_set_abs_params(hx->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); + + touchscreen_parse_properties(hx->input_dev, true, &hx->props); + error = hx852x_parse_properties(hx); + if (error) + return error; + + hx->input_dev->keycode = hx->keycodes; + hx->input_dev->keycodemax = hx->keycount; + hx->input_dev->keycodesize = sizeof(hx->keycodes[0]); + for (i = 0; i < hx->keycount; i++) + input_set_capability(hx->input_dev, EV_KEY, hx->keycodes[i]); + + error = input_mt_init_slots(hx->input_dev, hx->max_fingers, + INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED); + if (error) + return dev_err_probe(dev, error, "failed to init MT slots\n"); + + error = input_register_device(hx->input_dev); + if (error) + return dev_err_probe(dev, error, "failed to register input device\n"); + + return 0; +} + +static int hx852x_suspend(struct device *dev) +{ + struct hx852x *hx = dev_get_drvdata(dev); + + guard(mutex)(&hx->input_dev->mutex); + + if (input_device_enabled(hx->input_dev)) + return hx852x_stop(hx); + + return 0; +} + +static int hx852x_resume(struct device *dev) +{ + struct hx852x *hx = dev_get_drvdata(dev); + + guard(mutex)(&hx->input_dev->mutex); + + if (input_device_enabled(hx->input_dev)) + return hx852x_start(hx); + + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(hx852x_pm_ops, hx852x_suspend, hx852x_resume); + +#ifdef CONFIG_OF +static const struct of_device_id hx852x_of_match[] = { + { .compatible = "himax,hx852es" }, + { } +}; +MODULE_DEVICE_TABLE(of, hx852x_of_match); +#endif + +static struct i2c_driver hx852x_driver = { + .probe = hx852x_probe, + .driver = { + .name = "himax_hx852x", + .pm = pm_sleep_ptr(&hx852x_pm_ops), + .of_match_table = of_match_ptr(hx852x_of_match), + }, +}; +module_i2c_driver(hx852x_driver); + +MODULE_DESCRIPTION("Himax HX852x(ES) Touchscreen Driver"); +MODULE_AUTHOR("Jonathan Albrieux <jonathan.albrieux@gmail.com>"); +MODULE_AUTHOR("Stephan Gerhold <stephan@gerhold.net>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/hynitron-cst816x.c b/drivers/input/touchscreen/hynitron-cst816x.c new file mode 100644 index 000000000000..b64d7928e18f --- /dev/null +++ b/drivers/input/touchscreen/hynitron-cst816x.c @@ -0,0 +1,253 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for I2C connected Hynitron CST816x Series Touchscreen + * + * Copyright (C) 2025 Oleh Kuzhylnyi <kuzhylol@gmail.com> + */ + +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/input.h> +#include <linux/unaligned.h> +#include <linux/interrupt.h> +#include <linux/module.h> + +#define CST816X_RD_REG 0x01 +#define CST816X_NUM_KEYS 5 + +struct cst816x_touch { + u8 gest; + u8 active; + u16 abs_x; + u16 abs_y; +} __packed; + +struct cst816x_priv { + struct i2c_client *client; + struct gpio_desc *reset; + struct input_dev *input; + unsigned int keycode[CST816X_NUM_KEYS]; + unsigned int keycodemax; +}; + +static int cst816x_parse_keycodes(struct device *dev, struct cst816x_priv *priv) +{ + int count; + int error; + + if (device_property_present(dev, "linux,keycodes")) { + count = device_property_count_u32(dev, "linux,keycodes"); + if (count < 0) { + error = count; + dev_err(dev, "failed to count keys: %d\n", error); + return error; + } else if (count > ARRAY_SIZE(priv->keycode)) { + dev_err(dev, "too many keys defined: %d\n", count); + return -EINVAL; + } + priv->keycodemax = count; + + error = device_property_read_u32_array(dev, "linux,keycodes", + priv->keycode, + priv->keycodemax); + if (error) { + dev_err(dev, "failed to read keycodes: %d\n", error); + return error; + } + } + + return 0; +} + +static int cst816x_i2c_read_register(struct cst816x_priv *priv, u8 reg, + void *buf, size_t len) +{ + struct i2c_msg xfer[] = { + { + .addr = priv->client->addr, + .flags = 0, + .buf = ®, + .len = sizeof(reg), + }, + { + .addr = priv->client->addr, + .flags = I2C_M_RD, + .buf = buf, + .len = len, + }, + }; + int error; + int ret; + + ret = i2c_transfer(priv->client->adapter, xfer, ARRAY_SIZE(xfer)); + if (ret != ARRAY_SIZE(xfer)) { + error = ret < 0 ? ret : -EIO; + dev_err(&priv->client->dev, "i2c rx err: %d\n", error); + return error; + } + + return 0; +} + +static u8 cst816x_gest_idx(u8 gest) +{ + u8 index; + + switch (gest) { + case 0x01: /* Slide up gesture */ + case 0x02: /* Slide down gesture */ + case 0x03: /* Slide left gesture */ + case 0x04: /* Slide right gesture */ + index = gest; + break; + case 0x0c: /* Long press gesture */ + default: + index = CST816X_NUM_KEYS; + break; + } + + return index - 1; +} + +static bool cst816x_process_touch(struct cst816x_priv *priv, + struct cst816x_touch *tch) +{ + if (cst816x_i2c_read_register(priv, CST816X_RD_REG, tch, sizeof(*tch))) + return false; + + tch->abs_x = get_unaligned_be16(&tch->abs_x) & GENMASK(11, 0); + tch->abs_y = get_unaligned_be16(&tch->abs_y) & GENMASK(11, 0); + + dev_dbg(&priv->client->dev, "x: %u, y: %u, t: %u, g: 0x%x\n", + tch->abs_x, tch->abs_y, tch->active, tch->gest); + + return true; +} + +static int cst816x_register_input(struct cst816x_priv *priv) +{ + priv->input = devm_input_allocate_device(&priv->client->dev); + if (!priv->input) + return -ENOMEM; + + priv->input->name = "Hynitron CST816x Series Touchscreen"; + priv->input->phys = "input/ts"; + priv->input->id.bustype = BUS_I2C; + + input_set_drvdata(priv->input, priv); + + input_set_abs_params(priv->input, ABS_X, 0, 240, 0, 0); + input_set_abs_params(priv->input, ABS_Y, 0, 240, 0, 0); + input_set_capability(priv->input, EV_KEY, BTN_TOUCH); + + priv->input->keycode = priv->keycode; + priv->input->keycodesize = sizeof(priv->keycode[0]); + priv->input->keycodemax = priv->keycodemax; + + for (int i = 0; i < priv->keycodemax; i++) { + if (priv->keycode[i] == KEY_RESERVED) + continue; + + input_set_capability(priv->input, EV_KEY, priv->keycode[i]); + } + + return input_register_device(priv->input); +} + +static void cst816x_reset(struct cst816x_priv *priv) +{ + gpiod_set_value_cansleep(priv->reset, 1); + msleep(50); + gpiod_set_value_cansleep(priv->reset, 0); + msleep(100); +} + +static irqreturn_t cst816x_irq_cb(int irq, void *cookie) +{ + struct cst816x_priv *priv = cookie; + struct cst816x_touch tch; + + if (!cst816x_process_touch(priv, &tch)) + return IRQ_HANDLED; + + input_report_abs(priv->input, ABS_X, tch.abs_x); + input_report_abs(priv->input, ABS_Y, tch.abs_y); + + if (tch.gest) + input_report_key(priv->input, + priv->keycode[cst816x_gest_idx(tch.gest)], + tch.active); + + input_report_key(priv->input, BTN_TOUCH, tch.active); + + input_sync(priv->input); + + return IRQ_HANDLED; +} + +static int cst816x_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct cst816x_priv *priv; + int error; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->client = client; + + priv->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(priv->reset)) + return dev_err_probe(dev, PTR_ERR(priv->reset), + "gpio reset request failed\n"); + + if (priv->reset) + cst816x_reset(priv); + + error = cst816x_parse_keycodes(dev, priv); + if (error) + dev_warn(dev, "no gestures found in dt\n"); + + error = cst816x_register_input(priv); + if (error) + return dev_err_probe(dev, error, "input register failed\n"); + + error = devm_request_threaded_irq(dev, client->irq, + NULL, cst816x_irq_cb, IRQF_ONESHOT, + dev_driver_string(dev), priv); + if (error) + return dev_err_probe(dev, error, "irq request failed\n"); + + return 0; +} + +static const struct i2c_device_id cst816x_id[] = { + { .name = "cst816s", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, cst816x_id); + +static const struct of_device_id cst816x_of_match[] = { + { .compatible = "hynitron,cst816s", }, + { } +}; +MODULE_DEVICE_TABLE(of, cst816x_of_match); + +static struct i2c_driver cst816x_driver = { + .driver = { + .name = "cst816x", + .of_match_table = cst816x_of_match, + }, + .id_table = cst816x_id, + .probe = cst816x_probe, +}; + +module_i2c_driver(cst816x_driver); + +MODULE_AUTHOR("Oleh Kuzhylnyi <kuzhylol@gmail.com>"); +MODULE_DESCRIPTION("Hynitron CST816x Series Touchscreen Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/imx6ul_tsc.c b/drivers/input/touchscreen/imx6ul_tsc.c index 6ac8fa84ed9f..85f697de2b7e 100644 --- a/drivers/input/touchscreen/imx6ul_tsc.c +++ b/drivers/input/touchscreen/imx6ul_tsc.c @@ -7,6 +7,7 @@ #include <linux/errno.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/bitfield.h> #include <linux/gpio/consumer.h> #include <linux/input.h> #include <linux/slab.h> @@ -20,25 +21,23 @@ #include <linux/log2.h> /* ADC configuration registers field define */ -#define ADC_AIEN (0x1 << 7) +#define ADC_AIEN BIT(7) +#define ADC_ADCH_MASK GENMASK(4, 0) #define ADC_CONV_DISABLE 0x1F -#define ADC_AVGE (0x1 << 5) -#define ADC_CAL (0x1 << 7) -#define ADC_CALF 0x2 -#define ADC_12BIT_MODE (0x2 << 2) -#define ADC_CONV_MODE_MASK (0x3 << 2) +#define ADC_AVGE BIT(5) +#define ADC_CAL BIT(7) +#define ADC_CALF BIT(1) +#define ADC_CONV_MODE_MASK GENMASK(3, 2) +#define ADC_12BIT_MODE 0x2 #define ADC_IPG_CLK 0x00 -#define ADC_INPUT_CLK_MASK 0x3 -#define ADC_CLK_DIV_8 (0x03 << 5) -#define ADC_CLK_DIV_MASK (0x3 << 5) -#define ADC_SHORT_SAMPLE_MODE (0x0 << 4) -#define ADC_SAMPLE_MODE_MASK (0x1 << 4) -#define ADC_HARDWARE_TRIGGER (0x1 << 13) -#define ADC_AVGS_SHIFT 14 -#define ADC_AVGS_MASK (0x3 << 14) +#define ADC_INPUT_CLK_MASK GENMASK(1, 0) +#define ADC_CLK_DIV_8 0x03 +#define ADC_CLK_DIV_MASK GENMASK(6, 5) +#define ADC_SAMPLE_MODE BIT(4) +#define ADC_HARDWARE_TRIGGER BIT(13) +#define ADC_AVGS_MASK GENMASK(15, 14) #define SELECT_CHANNEL_4 0x04 #define SELECT_CHANNEL_1 0x01 -#define DISABLE_CONVERSION_INT (0x0 << 7) /* ADC registers */ #define REG_ADC_HC0 0x00 @@ -55,7 +54,7 @@ #define ADC_TIMEOUT msecs_to_jiffies(100) /* TSC registers */ -#define REG_TSC_BASIC_SETING 0x00 +#define REG_TSC_BASIC_SETTING 0x00 #define REG_TSC_PRE_CHARGE_TIME 0x10 #define REG_TSC_FLOW_CONTROL 0x20 #define REG_TSC_MEASURE_VALUE 0x30 @@ -65,19 +64,26 @@ #define REG_TSC_DEBUG_MODE 0x70 #define REG_TSC_DEBUG_MODE2 0x80 +/* TSC_MEASURE_VALUE register field define */ +#define X_VALUE_MASK GENMASK(27, 16) +#define Y_VALUE_MASK GENMASK(11, 0) + /* TSC configuration registers field define */ -#define DETECT_4_WIRE_MODE (0x0 << 4) -#define AUTO_MEASURE 0x1 -#define MEASURE_SIGNAL 0x1 -#define DETECT_SIGNAL (0x1 << 4) -#define VALID_SIGNAL (0x1 << 8) -#define MEASURE_INT_EN 0x1 -#define MEASURE_SIG_EN 0x1 -#define VALID_SIG_EN (0x1 << 8) -#define DE_GLITCH_2 (0x2 << 29) -#define START_SENSE (0x1 << 12) -#define TSC_DISABLE (0x1 << 16) +#define MEASURE_DELAY_TIME_MASK GENMASK(31, 8) +#define DETECT_5_WIRE_MODE BIT(4) +#define AUTO_MEASURE BIT(0) +#define MEASURE_SIGNAL BIT(0) +#define DETECT_SIGNAL BIT(4) +#define VALID_SIGNAL BIT(8) +#define MEASURE_INT_EN BIT(0) +#define MEASURE_SIG_EN BIT(0) +#define VALID_SIG_EN BIT(8) +#define DE_GLITCH_MASK GENMASK(30, 29) +#define DE_GLITCH_DEF 0x02 +#define START_SENSE BIT(12) +#define TSC_DISABLE BIT(16) #define DETECT_MODE 0x2 +#define STATE_MACHINE_MASK GENMASK(22, 20) struct imx6ul_tsc { struct device *dev; @@ -92,6 +98,7 @@ struct imx6ul_tsc { u32 pre_charge_time; bool average_enable; u32 average_select; + u32 de_glitch; struct completion completion; }; @@ -112,19 +119,20 @@ static int imx6ul_adc_init(struct imx6ul_tsc *tsc) adc_cfg = readl(tsc->adc_regs + REG_ADC_CFG); adc_cfg &= ~(ADC_CONV_MODE_MASK | ADC_INPUT_CLK_MASK); - adc_cfg |= ADC_12BIT_MODE | ADC_IPG_CLK; - adc_cfg &= ~(ADC_CLK_DIV_MASK | ADC_SAMPLE_MODE_MASK); - adc_cfg |= ADC_CLK_DIV_8 | ADC_SHORT_SAMPLE_MODE; + adc_cfg |= FIELD_PREP(ADC_CONV_MODE_MASK, ADC_12BIT_MODE) | + FIELD_PREP(ADC_INPUT_CLK_MASK, ADC_IPG_CLK); + adc_cfg &= ~(ADC_CLK_DIV_MASK | ADC_SAMPLE_MODE); + adc_cfg |= FIELD_PREP(ADC_CLK_DIV_MASK, ADC_CLK_DIV_8); if (tsc->average_enable) { adc_cfg &= ~ADC_AVGS_MASK; - adc_cfg |= (tsc->average_select) << ADC_AVGS_SHIFT; + adc_cfg |= FIELD_PREP(ADC_AVGS_MASK, tsc->average_select); } adc_cfg &= ~ADC_HARDWARE_TRIGGER; writel(adc_cfg, tsc->adc_regs + REG_ADC_CFG); /* enable calibration interrupt */ adc_hc |= ADC_AIEN; - adc_hc |= ADC_CONV_DISABLE; + adc_hc |= FIELD_PREP(ADC_ADCH_MASK, ADC_CONV_DISABLE); writel(adc_hc, tsc->adc_regs + REG_ADC_HC0); /* start ADC calibration */ @@ -164,19 +172,21 @@ static void imx6ul_tsc_channel_config(struct imx6ul_tsc *tsc) { u32 adc_hc0, adc_hc1, adc_hc2, adc_hc3, adc_hc4; - adc_hc0 = DISABLE_CONVERSION_INT; + adc_hc0 = FIELD_PREP(ADC_AIEN, 0); writel(adc_hc0, tsc->adc_regs + REG_ADC_HC0); - adc_hc1 = DISABLE_CONVERSION_INT | SELECT_CHANNEL_4; + adc_hc1 = FIELD_PREP(ADC_AIEN, 0) | + FIELD_PREP(ADC_ADCH_MASK, SELECT_CHANNEL_4); writel(adc_hc1, tsc->adc_regs + REG_ADC_HC1); - adc_hc2 = DISABLE_CONVERSION_INT; + adc_hc2 = FIELD_PREP(ADC_AIEN, 0); writel(adc_hc2, tsc->adc_regs + REG_ADC_HC2); - adc_hc3 = DISABLE_CONVERSION_INT | SELECT_CHANNEL_1; + adc_hc3 = FIELD_PREP(ADC_AIEN, 0) | + FIELD_PREP(ADC_ADCH_MASK, SELECT_CHANNEL_1); writel(adc_hc3, tsc->adc_regs + REG_ADC_HC3); - adc_hc4 = DISABLE_CONVERSION_INT; + adc_hc4 = FIELD_PREP(ADC_AIEN, 0); writel(adc_hc4, tsc->adc_regs + REG_ADC_HC4); } @@ -188,13 +198,16 @@ static void imx6ul_tsc_channel_config(struct imx6ul_tsc *tsc) static void imx6ul_tsc_set(struct imx6ul_tsc *tsc) { u32 basic_setting = 0; + u32 debug_mode2; u32 start; - basic_setting |= tsc->measure_delay_time << 8; - basic_setting |= DETECT_4_WIRE_MODE | AUTO_MEASURE; - writel(basic_setting, tsc->tsc_regs + REG_TSC_BASIC_SETING); + basic_setting |= FIELD_PREP(MEASURE_DELAY_TIME_MASK, + tsc->measure_delay_time); + basic_setting |= AUTO_MEASURE; + writel(basic_setting, tsc->tsc_regs + REG_TSC_BASIC_SETTING); - writel(DE_GLITCH_2, tsc->tsc_regs + REG_TSC_DEBUG_MODE2); + debug_mode2 = FIELD_PREP(DE_GLITCH_MASK, tsc->de_glitch); + writel(debug_mode2, tsc->tsc_regs + REG_TSC_DEBUG_MODE2); writel(tsc->pre_charge_time, tsc->tsc_regs + REG_TSC_PRE_CHARGE_TIME); writel(MEASURE_INT_EN, tsc->tsc_regs + REG_TSC_INT_EN); @@ -250,7 +263,7 @@ static bool tsc_wait_detect_mode(struct imx6ul_tsc *tsc) usleep_range(200, 400); debug_mode2 = readl(tsc->tsc_regs + REG_TSC_DEBUG_MODE2); - state_machine = (debug_mode2 >> 20) & 0x7; + state_machine = FIELD_GET(STATE_MACHINE_MASK, debug_mode2); } while (state_machine != DETECT_MODE); usleep_range(200, 400); @@ -278,8 +291,8 @@ static irqreturn_t tsc_irq_fn(int irq, void *dev_id) if (status & MEASURE_SIGNAL) { value = readl(tsc->tsc_regs + REG_TSC_MEASURE_VALUE); - x = (value >> 16) & 0x0fff; - y = value & 0x0fff; + x = FIELD_GET(X_VALUE_MASK, value); + y = FIELD_GET(Y_VALUE_MASK, value); /* * In detect mode, we can get the xnur gpio value, @@ -379,6 +392,7 @@ static int imx6ul_tsc_probe(struct platform_device *pdev) int tsc_irq; int adc_irq; u32 average_samples; + u32 de_glitch; tsc = devm_kzalloc(&pdev->dev, sizeof(*tsc), GFP_KERNEL); if (!tsc) @@ -501,6 +515,25 @@ static int imx6ul_tsc_probe(struct platform_device *pdev) return -EINVAL; } + err = of_property_read_u32(np, "debounce-delay-us", &de_glitch); + if (err) { + tsc->de_glitch = DE_GLITCH_DEF; + } else { + u64 cycles; + unsigned long rate = clk_get_rate(tsc->tsc_clk); + + cycles = DIV64_U64_ROUND_UP((u64)de_glitch * rate, USEC_PER_SEC); + + if (cycles <= 0x3ff) + tsc->de_glitch = 3; + else if (cycles <= 0x7ff) + tsc->de_glitch = 2; + else if (cycles <= 0xfff) + tsc->de_glitch = 1; + else + tsc->de_glitch = 0; + } + err = input_register_device(tsc->input); if (err) { dev_err(&pdev->dev, diff --git a/drivers/input/touchscreen/mc13783_ts.c b/drivers/input/touchscreen/mc13783_ts.c index 33635da85079..47b8da00027f 100644 --- a/drivers/input/touchscreen/mc13783_ts.c +++ b/drivers/input/touchscreen/mc13783_ts.c @@ -42,8 +42,6 @@ static irqreturn_t mc13783_ts_handler(int irq, void *data) { struct mc13783_ts_priv *priv = data; - mc13xxx_irq_ack(priv->mc13xxx, irq); - /* * Kick off reading coordinates. Note that if work happens already * be queued for future execution (it rearms itself) it will not @@ -137,8 +135,6 @@ static int mc13783_ts_open(struct input_dev *dev) mc13xxx_lock(priv->mc13xxx); - mc13xxx_irq_ack(priv->mc13xxx, MC13XXX_IRQ_TS); - ret = mc13xxx_irq_request(priv->mc13xxx, MC13XXX_IRQ_TS, mc13783_ts_handler, MC13783_TS_NAME, priv); if (ret) diff --git a/drivers/input/touchscreen/tsc2007_core.c b/drivers/input/touchscreen/tsc2007_core.c index 5252301686ec..948935de894b 100644 --- a/drivers/input/touchscreen/tsc2007_core.c +++ b/drivers/input/touchscreen/tsc2007_core.c @@ -23,6 +23,7 @@ #include <linux/input.h> #include <linux/interrupt.h> #include <linux/i2c.h> +#include <linux/math64.h> #include <linux/mod_devicetable.h> #include <linux/property.h> #include <linux/platform_data/tsc2007.h> @@ -68,7 +69,7 @@ static void tsc2007_read_values(struct tsc2007 *tsc, struct ts_event *tc) u32 tsc2007_calculate_resistance(struct tsc2007 *tsc, struct ts_event *tc) { - u32 rt = 0; + u64 rt = 0; /* range filtering */ if (tc->x == MAX_12BIT) @@ -79,11 +80,13 @@ u32 tsc2007_calculate_resistance(struct tsc2007 *tsc, struct ts_event *tc) rt = tc->z2 - tc->z1; rt *= tc->x; rt *= tsc->x_plate_ohms; - rt /= tc->z1; + rt = div_u64(rt, tc->z1); rt = (rt + 2047) >> 12; } - return rt; + if (rt > U32_MAX) + return U32_MAX; + return (u32) rt; } bool tsc2007_is_pen_down(struct tsc2007 *ts) @@ -177,7 +180,8 @@ static void tsc2007_stop(struct tsc2007 *ts) mb(); wake_up(&ts->wait); - disable_irq(ts->irq); + if (ts->irq) + disable_irq(ts->irq); } static int tsc2007_open(struct input_dev *input_dev) @@ -188,7 +192,8 @@ static int tsc2007_open(struct input_dev *input_dev) ts->stopped = false; mb(); - enable_irq(ts->irq); + if (ts->irq) + enable_irq(ts->irq); /* Prepare for touch readings - power down ADC and enable PENIRQ */ err = tsc2007_xfer(ts, PWRDOWN); @@ -253,7 +258,7 @@ static int tsc2007_probe_properties(struct device *dev, struct tsc2007 *ts) if (ts->gpiod) ts->get_pendown_state = tsc2007_get_pendown_state_gpio; else - dev_warn(dev, "Pen down GPIO is not specified in properties\n"); + dev_dbg(dev, "Pen down GPIO is not specified in properties\n"); return 0; } @@ -361,17 +366,19 @@ static int tsc2007_probe(struct i2c_client *client) pdata->init_platform_hw(); } - err = devm_request_threaded_irq(&client->dev, ts->irq, - NULL, tsc2007_soft_irq, - IRQF_ONESHOT, - client->dev.driver->name, ts); - if (err) { - dev_err(&client->dev, "Failed to request irq %d: %d\n", - ts->irq, err); - return err; - } + if (ts->irq) { + err = devm_request_threaded_irq(&client->dev, ts->irq, + NULL, tsc2007_soft_irq, + IRQF_ONESHOT, + client->dev.driver->name, ts); + if (err) { + dev_err(&client->dev, "Failed to request irq %d: %d\n", + ts->irq, err); + return err; + } - tsc2007_stop(ts); + tsc2007_stop(ts); + } /* power down the chip (TSC2007_SETUP does not ACK on I2C) */ err = tsc2007_xfer(ts, PWRDOWN); diff --git a/drivers/input/touchscreen/tsc200x-core.c b/drivers/input/touchscreen/tsc200x-core.c index 82d7d1cf5010..eba53613b005 100644 --- a/drivers/input/touchscreen/tsc200x-core.c +++ b/drivers/input/touchscreen/tsc200x-core.c @@ -10,6 +10,7 @@ * based on TSC2301 driver by Klaus K. Pedersen <klaus.k.pedersen@nokia.com> */ +#include <linux/export.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/input.h> diff --git a/drivers/input/touchscreen/wm9705.c b/drivers/input/touchscreen/wm9705.c index 4b55d5e1ea0f..96484aae030c 100644 --- a/drivers/input/touchscreen/wm9705.c +++ b/drivers/input/touchscreen/wm9705.c @@ -9,6 +9,7 @@ * Russell King <rmk@arm.linux.org.uk> */ +#include <linux/export.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/kernel.h> diff --git a/drivers/input/touchscreen/wm9712.c b/drivers/input/touchscreen/wm9712.c index 6947714dfefa..087ece57741a 100644 --- a/drivers/input/touchscreen/wm9712.c +++ b/drivers/input/touchscreen/wm9712.c @@ -9,6 +9,7 @@ * Russell King <rmk@arm.linux.org.uk> */ +#include <linux/export.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/kernel.h> diff --git a/drivers/input/touchscreen/wm9713.c b/drivers/input/touchscreen/wm9713.c index a67fbe304f92..6f13f46ce6e6 100644 --- a/drivers/input/touchscreen/wm9713.c +++ b/drivers/input/touchscreen/wm9713.c @@ -9,6 +9,7 @@ * Russell King <rmk@arm.linux.org.uk> */ +#include <linux/export.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/kernel.h> diff --git a/drivers/input/touchscreen/wm97xx-core.c b/drivers/input/touchscreen/wm97xx-core.c index b25771a8df2b..96354c44af87 100644 --- a/drivers/input/touchscreen/wm97xx-core.c +++ b/drivers/input/touchscreen/wm97xx-core.c @@ -29,6 +29,7 @@ * - Support for async sampling control for noisy LCDs. */ +#include <linux/export.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/kernel.h> |