diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-08-19 08:59:24 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-08-19 08:59:24 -0700 |
| commit | 1be05c6afbb2c4052d0a13fc91ddcd89aaee9614 (patch) | |
| tree | 86f6afe38778cdb96e85da662dc34510cb73d40b /drivers | |
| parent | e8bf40d154020dd323596933ffaebda7111828fa (diff) | |
| parent | b4d85f863e5a6f1fa01b186001af0f68f4f5239e (diff) | |
Merge tag 'input-for-v7.3-rc0' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
Pull input updates from Dmitry Torokhov:
- A new driver and device tree binding for Imagis ISA1200 haptic motor
controller
- Improvements to input core opening, closing and inhibiting devices,
ensuring devices are fully ready before delivering events, deferring
handler start() until the device is opened, resyncing state on
uninhibit, and rejecting inhibit requests during unregistration
- Updates to cap11xx capacitive touch driver to support Microchip
CAP1114, optional hardware reset GPIO handling, and per-chip LED
constraints
- Fixes for MELFAS MMS114 touchscreen driver hardening incoming data
parsing, endianness fixes for I2C packet layout, Y-resolution
configuration, and refactoring to use chip variant descriptors
- Updates for psmouse driver resolving a potential UAF during protocol
disconnect, cleaning up PNP ID matching, and making use of guard()
- Fix for FocalTech PS/2 protocol to prevent coordinate underflow and
cursor jumps at boundaries
- A change to Synaptics driver to enable InterTouch (SMBus) mode on
Dell Inspiron 3521
- Refactoring of PA-RISC keyboard support in gscps2 to supply keymaps
via software node device properties, removing architecture-specific
tables from the generic atkbd driver
- Updates to Samsung keypad driver to keep interrupts disabled while
device is closed, along with wakeup logic cleanups and use of
pm_runtime_active guards
- Updates to NXP i.MX SNVS power key driver to report press events
during resume to avoid lost events, and error handling cleanups
- Updated TCA8418 keypad driver enabling overflow mode per hardware
errata
- Conversion of ROHM BD718x7 and BD71828 PMIC drivers to instantiate
gpio-keys child devices using software nodes instead of platform data
(coming from MFD immutable branch)
- Updates to Synaptics RMI4 driver to use touchscreen dimensions from
platform data when specified
- Firmware update speed optimization for IC Type 0x19 in ELAN I2C
driver
- A fix to Azoteq IQS5xx driver to validate firmware record spans
against programmable map size
- Update to Samsung SUR40 contact count based on PixelSense
specification
- A number of updates to device tree bindings, including TI TPS65217
power button schema conversion and new compatibles for FocalTech
FT3D81 and Synaptics S3706B
- Other assorted driver cleanups, style fixes, and conversions to
modern string and cleanup helpers
* tag 'input-for-v7.3-rc0' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (61 commits)
Input: rmi4 - use platform data instead of query, when available
Input: elan_i2c - sort include statements
Input: elan_i2c - optimize update speed for IC Type 0x19.
Input: elan_i2c - use device-id/acpi.h for ACPI IDs
Input: reject inhibit and uninhibit requests on unregistering devices
Input: defer handler's start() until device is opened
Input: call handler->start() when uninhibiting device
Input: clear inhibited flag before re-opening device on uninhibit
Input: ensure device is ready before delivering events
Input: gscps2 - supply PA-RISC keyboard keymap via device property
Input: synaptics_i2c - return 0 explicitly on success
Input: rmi_smbus - remove conditional return with no effect
Input: pmic8xxx-keypad - remove conditional return with no effect
Input: focaltech - use signed coordinates to prevent underflow
Input: psmouse - use guard() for resource management
Input: psmouse - modernize PNP ID parsing
Input: psmouse - clean up locking around disable_work_sync()
Input: psmouse - fix use-after-free during protocol disconnect
Input: samsung-keypad - use pm_runtime_active guard
Input: samsung-keypad - keep interrupt disabled while closed
...
Diffstat (limited to 'drivers')
36 files changed, 1602 insertions, 714 deletions
diff --git a/drivers/input/input.c b/drivers/input/input.c index cf6fecea79b8..78c10eea7328 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -318,7 +318,7 @@ static int input_get_disposition(struct input_dev *dev, static void input_event_dispose(struct input_dev *dev, int disposition, unsigned int type, unsigned int code, int value) { - if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event) + if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event && dev->ready) dev->event(dev, type, code, value); if (disposition & INPUT_PASS_TO_HANDLERS) { @@ -568,6 +568,75 @@ void input_release_device(struct input_handle *handle) } EXPORT_SYMBOL(input_release_device); +#define INPUT_DO_TOGGLE(dev, type, bits, on) \ + do { \ + int i; \ + bool active; \ + \ + if (!test_bit(EV_##type, dev->evbit)) \ + break; \ + \ + for_each_set_bit(i, dev->bits##bit, type##_CNT) { \ + active = test_bit(i, dev->bits); \ + if (!active && !on) \ + continue; \ + \ + dev->event(dev, EV_##type, i, on ? active : 0); \ + } \ + } while (0) + +/* + * Iterate through the logical state of the input device (LEDs, sounds, + * auto-repeat) and explicitly push that state down to the hardware + * via dev->event() to match the current logical state (if activate is true), + * or forcibly turn off all feedback like LEDs and sounds during teardown + * or suspend (if activate is false). + * + * Primarily used as a state-replay mechanism after a device is opened + * or uninhibited, as events might have been dropped by the core while the + * hardware was not marked as ready. + */ +static void input_dev_toggle(struct input_dev *dev, bool activate) +{ + if (!dev->event || !dev->ready) + return; + + INPUT_DO_TOGGLE(dev, LED, led, activate); + INPUT_DO_TOGGLE(dev, SND, snd, activate); + + if (activate && test_bit(EV_REP, dev->evbit)) { + dev->event(dev, EV_REP, REP_PERIOD, dev->rep[REP_PERIOD]); + dev->event(dev, EV_REP, REP_DELAY, dev->rep[REP_DELAY]); + } +} + +static int input_start_device(struct input_dev *dev) +{ + int error; + + lockdep_assert_held(&dev->mutex); + + if (dev->users++ == 0 && !dev->inhibited) { + if (dev->open) { + error = dev->open(dev); + if (error) { + dev->users--; + return error; + } + } + + scoped_guard(spinlock_irq, &dev->event_lock) { + dev->ready = true; + input_dev_toggle(dev, true); + } + + if (dev->poller) + input_dev_poller_start(dev->poller); + } + + return 0; +} + /** * input_open_device - open input device * @handle: handle through which device is being accessed @@ -586,21 +655,9 @@ int input_open_device(struct input_handle *handle) handle->open++; - if (handle->handler->passive_observer) - return 0; - - if (dev->users++ || dev->inhibited) { - /* - * Device is already opened and/or inhibited, - * so we can exit immediately and report success. - */ - return 0; - } - - if (dev->open) { - error = dev->open(dev); + if (!handle->handler->passive_observer) { + error = input_start_device(dev); if (error) { - dev->users--; handle->open--; /* * Make sure we are not delivering any more @@ -611,8 +668,8 @@ int input_open_device(struct input_handle *handle) } } - if (dev->poller) - input_dev_poller_start(dev->poller); + if (handle->open == 1 && handle->handler->start) + handle->handler->start(handle); } return 0; @@ -651,6 +708,12 @@ void input_close_device(struct input_handle *handle) if (!--dev->users && !dev->inhibited) { if (dev->poller) input_dev_poller_stop(dev->poller); + + scoped_guard(spinlock_irq, &dev->event_lock) { + input_dev_toggle(dev, false); + dev->ready = false; + } + if (dev->close) dev->close(dev); } @@ -1702,37 +1765,6 @@ static int input_dev_uevent(const struct device *device, struct kobj_uevent_env return 0; } -#define INPUT_DO_TOGGLE(dev, type, bits, on) \ - do { \ - int i; \ - bool active; \ - \ - if (!test_bit(EV_##type, dev->evbit)) \ - break; \ - \ - for_each_set_bit(i, dev->bits##bit, type##_CNT) { \ - active = test_bit(i, dev->bits); \ - if (!active && !on) \ - continue; \ - \ - dev->event(dev, EV_##type, i, on ? active : 0); \ - } \ - } while (0) - -static void input_dev_toggle(struct input_dev *dev, bool activate) -{ - if (!dev->event) - return; - - INPUT_DO_TOGGLE(dev, LED, led, activate); - INPUT_DO_TOGGLE(dev, SND, snd, activate); - - if (activate && test_bit(EV_REP, dev->evbit)) { - dev->event(dev, EV_REP, REP_PERIOD, dev->rep[REP_PERIOD]); - dev->event(dev, EV_REP, REP_DELAY, dev->rep[REP_DELAY]); - } -} - /** * input_reset_device() - reset/restore the state of input device * @dev: input device whose state needs to be reset @@ -1756,52 +1788,73 @@ static int input_inhibit_device(struct input_dev *dev) { guard(mutex)(&dev->mutex); + if (dev->going_away) + return -ENODEV; + if (dev->inhibited) return 0; if (dev->users) { - if (dev->close) - dev->close(dev); if (dev->poller) input_dev_poller_stop(dev->poller); + + scoped_guard(spinlock_irq, &dev->event_lock) { + input_dev_toggle(dev, false); + dev->ready = false; + } + + if (dev->close) + dev->close(dev); } scoped_guard(spinlock_irq, &dev->event_lock) { input_mt_release_slots(dev); input_dev_release_keys(dev); input_handle_event(dev, EV_SYN, SYN_REPORT, 1); - input_dev_toggle(dev, false); + dev->inhibited = true; } - dev->inhibited = true; - return 0; } static int input_uninhibit_device(struct input_dev *dev) { + struct input_handle *handle; int error; guard(mutex)(&dev->mutex); + if (dev->going_away) + return -ENODEV; + if (!dev->inhibited) return 0; + dev->inhibited = false; + if (dev->users) { if (dev->open) { error = dev->open(dev); - if (error) + if (error) { + dev->inhibited = true; return error; + } } - if (dev->poller) - input_dev_poller_start(dev->poller); + scoped_guard(spinlock_irq, &dev->event_lock) + dev->ready = true; } - dev->inhibited = false; - scoped_guard(spinlock_irq, &dev->event_lock) input_dev_toggle(dev, true); + if (dev->users && dev->poller) + input_dev_poller_start(dev->poller); + + list_for_each_entry(handle, &dev->h_list, d_node) { + if (handle->open && handle->handler->start) + handle->handler->start(handle); + } + return 0; } @@ -2655,9 +2708,6 @@ int input_register_handle(struct input_handle *handle) */ list_add_tail_rcu(&handle->h_node, &handler->h_list); - if (handler->start) - handler->start(handle); - return 0; } EXPORT_SYMBOL(input_register_handle); diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 9d1019ba0245..d8c64f333462 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -119,44 +119,6 @@ config KEYBOARD_ATKBD To compile this driver as a module, choose M here: the module will be called atkbd. -config KEYBOARD_ATKBD_HP_KEYCODES - bool "Use HP keyboard scancodes" - depends on PARISC && KEYBOARD_ATKBD - default y - help - Say Y here if you have a PA-RISC machine and want to use an AT or - PS/2 keyboard, and your keyboard uses keycodes that are specific to - PA-RISC keyboards. - - Say N if you use a standard keyboard. - -config KEYBOARD_ATKBD_RDI_KEYCODES - bool "Use PrecisionBook keyboard scancodes" - depends on KEYBOARD_ATKBD_HP_KEYCODES - default n - help - If you have an RDI PrecisionBook, say Y here if you want to use its - built-in keyboard (as opposed to an external keyboard). - - The PrecisionBook has five keys that conflict with those used by most - AT and PS/2 keyboards. These are as follows: - - PrecisionBook Standard AT or PS/2 - - F1 F12 - Left Ctrl Left Alt - Caps Lock Left Ctrl - Right Ctrl Caps Lock - Left 102nd key (the key to the right of Left Shift) - - If you say N here, and use the PrecisionBook keyboard, then each key - in the left-hand column will be interpreted as the corresponding key - in the right-hand column. - - If you say Y here, and use an external keyboard, then each key in the - right-hand column will be interpreted as the key shown in the - left-hand column. - config KEYBOARD_QT1050 tristate "Microchip AT42QT1050 Touch Sensor Chip" depends on I2C diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c index 4c82e988260e..5736f4bc5a50 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c @@ -73,13 +73,6 @@ MODULE_PARM_DESC(terminal, "Enable break codes on an IBM Terminal keyboard conne #define ATKBD_KEYMAP_SIZE 512 static const unsigned short atkbd_set2_keycode[ATKBD_KEYMAP_SIZE] = { -#ifdef CONFIG_KEYBOARD_ATKBD_HP_KEYCODES - -/* XXX: need a more general approach */ - -#include "hpps2atkbd.h" /* include the keyboard scancodes */ - -#else 0, 67, 65, 63, 61, 59, 60, 88,183, 68, 66, 64, 62, 15, 41,117, 184, 56, 42, 93, 29, 16, 2, 0,185, 0, 44, 31, 30, 17, 3, 0, 186, 46, 45, 32, 18, 5, 4, 95,187, 57, 47, 33, 20, 19, 6,183, @@ -99,7 +92,6 @@ static const unsigned short atkbd_set2_keycode[ATKBD_KEYMAP_SIZE] = { 110,111,108,112,106,103, 0,119, 0,118,109, 0, 99,104,119, 0, 0, 0, 0, 65, 99, -#endif }; static const unsigned short atkbd_set3_keycode[ATKBD_KEYMAP_SIZE] = { diff --git a/drivers/input/keyboard/cap11xx.c b/drivers/input/keyboard/cap11xx.c index 2447c1ae2166..25b6f7bd97ee 100644 --- a/drivers/input/keyboard/cap11xx.c +++ b/drivers/input/keyboard/cap11xx.c @@ -5,6 +5,8 @@ * (c) 2014 Daniel Mack <linux@zonque.org> */ +#include <linux/bits.h> +#include <linux/delay.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/interrupt.h> @@ -16,63 +18,43 @@ #include <linux/gpio/consumer.h> #include <linux/bitfield.h> +#define CAP1114_REG_BUTTON_STATUS1 0x03 +#define CAP1114_REG_BUTTON_STATUS2 0x04 +#define CAP1114_REG_CONFIG2 0x40 +#define CAP1114_REG_CONFIG2_VOL_UP_DOWN BIT(1) +#define CAP1114_REG_LED_OUTPUT_CONTROL1 0x73 + #define CAP11XX_REG_MAIN_CONTROL 0x00 #define CAP11XX_REG_MAIN_CONTROL_GAIN_SHIFT (6) #define CAP11XX_REG_MAIN_CONTROL_GAIN_MASK (0xc0) #define CAP11XX_REG_MAIN_CONTROL_DLSEEP BIT(4) -#define CAP11XX_REG_GENERAL_STATUS 0x02 #define CAP11XX_REG_SENSOR_INPUT 0x03 -#define CAP11XX_REG_NOISE_FLAG_STATUS 0x0a +#define CAP1114_REG_BUTTON_STATUS2 0x04 #define CAP11XX_REG_SENOR_DELTA(X) (0x10 + (X)) #define CAP11XX_REG_SENSITIVITY_CONTROL 0x1f #define CAP11XX_REG_SENSITIVITY_CONTROL_DELTA_SENSE_MASK 0x70 -#define CAP11XX_REG_CONFIG 0x20 -#define CAP11XX_REG_SENSOR_ENABLE 0x21 -#define CAP11XX_REG_SENSOR_CONFIG 0x22 -#define CAP11XX_REG_SENSOR_CONFIG2 0x23 -#define CAP11XX_REG_SAMPLING_CONFIG 0x24 -#define CAP11XX_REG_CALIBRATION 0x26 -#define CAP11XX_REG_INT_ENABLE 0x27 #define CAP11XX_REG_REPEAT_RATE 0x28 #define CAP11XX_REG_SIGNAL_GUARD_ENABLE 0x29 -#define CAP11XX_REG_MT_CONFIG 0x2a -#define CAP11XX_REG_MT_PATTERN_CONFIG 0x2b -#define CAP11XX_REG_MT_PATTERN 0x2d -#define CAP11XX_REG_RECALIB_CONFIG 0x2f #define CAP11XX_REG_SENSOR_THRESH(X) (0x30 + (X)) -#define CAP11XX_REG_SENSOR_NOISE_THRESH 0x38 -#define CAP11XX_REG_STANDBY_CHANNEL 0x40 -#define CAP11XX_REG_STANDBY_CONFIG 0x41 -#define CAP11XX_REG_STANDBY_SENSITIVITY 0x42 -#define CAP11XX_REG_STANDBY_THRESH 0x43 #define CAP11XX_REG_CONFIG2 0x44 #define CAP11XX_REG_CONFIG2_ALT_POL BIT(6) -#define CAP11XX_REG_SENSOR_BASE_CNT(X) (0x50 + (X)) -#define CAP11XX_REG_LED_POLARITY 0x73 #define CAP11XX_REG_LED_OUTPUT_CONTROL 0x74 #define CAP11XX_REG_CALIB_SENSITIVITY_CONFIG 0x80 #define CAP11XX_REG_CALIB_SENSITIVITY_CONFIG2 0x81 - -#define CAP11XX_REG_LED_DUTY_CYCLE_1 0x90 -#define CAP11XX_REG_LED_DUTY_CYCLE_2 0x91 -#define CAP11XX_REG_LED_DUTY_CYCLE_3 0x92 #define CAP11XX_REG_LED_DUTY_CYCLE_4 0x93 -#define CAP11XX_REG_LED_DUTY_MIN_MASK (0x0f) -#define CAP11XX_REG_LED_DUTY_MIN_MASK_SHIFT (0) #define CAP11XX_REG_LED_DUTY_MAX_MASK (0xf0) -#define CAP11XX_REG_LED_DUTY_MAX_MASK_SHIFT (4) #define CAP11XX_REG_LED_DUTY_MAX_VALUE (15) -#define CAP11XX_REG_SENSOR_CALIB (0xb1 + (X)) -#define CAP11XX_REG_SENSOR_CALIB_LSB1 0xb9 -#define CAP11XX_REG_SENSOR_CALIB_LSB2 0xba #define CAP11XX_REG_PRODUCT_ID 0xfd #define CAP11XX_REG_MANUFACTURER_ID 0xfe #define CAP11XX_REG_REVISION 0xff #define CAP11XX_MANUFACTURER_ID 0x5d +#define CAP11XX_T_RST_FILT_MIN_US 10000 +#define CAP11XX_T_RST_ON_MIN_MS 400 + #ifdef CONFIG_LEDS_CLASS struct cap11xx_led { struct cap11xx_priv *priv; @@ -85,6 +67,7 @@ struct cap11xx_priv { struct regmap *regmap; struct device *dev; struct input_dev *idev; + struct gpio_desc *reset_gpio; const struct cap11xx_hw_model *model; struct cap11xx_led *leds; @@ -101,47 +84,32 @@ struct cap11xx_priv { struct cap11xx_hw_model { u8 product_id; + u8 led_output_control_reg_base; + u8 sensor_input_reg_base; unsigned int num_channels; unsigned int num_leds; + unsigned int num_sensor_thresholds; bool has_gain; + bool has_grouped_sensors; bool has_irq_config; + bool has_repeat_en; bool has_sensitivity_control; bool has_signal_guard; }; static const struct reg_default cap11xx_reg_defaults[] = { { CAP11XX_REG_MAIN_CONTROL, 0x00 }, - { CAP11XX_REG_GENERAL_STATUS, 0x00 }, - { CAP11XX_REG_SENSOR_INPUT, 0x00 }, - { CAP11XX_REG_NOISE_FLAG_STATUS, 0x00 }, { CAP11XX_REG_SENSITIVITY_CONTROL, 0x2f }, - { CAP11XX_REG_CONFIG, 0x20 }, - { CAP11XX_REG_SENSOR_ENABLE, 0x3f }, - { CAP11XX_REG_SENSOR_CONFIG, 0xa4 }, - { CAP11XX_REG_SENSOR_CONFIG2, 0x07 }, - { CAP11XX_REG_SAMPLING_CONFIG, 0x39 }, - { CAP11XX_REG_CALIBRATION, 0x00 }, - { CAP11XX_REG_INT_ENABLE, 0x3f }, { CAP11XX_REG_REPEAT_RATE, 0x3f }, - { CAP11XX_REG_MT_CONFIG, 0x80 }, - { CAP11XX_REG_MT_PATTERN_CONFIG, 0x00 }, - { CAP11XX_REG_MT_PATTERN, 0x3f }, - { CAP11XX_REG_RECALIB_CONFIG, 0x8a }, { CAP11XX_REG_SENSOR_THRESH(0), 0x40 }, { CAP11XX_REG_SENSOR_THRESH(1), 0x40 }, { CAP11XX_REG_SENSOR_THRESH(2), 0x40 }, { CAP11XX_REG_SENSOR_THRESH(3), 0x40 }, { CAP11XX_REG_SENSOR_THRESH(4), 0x40 }, { CAP11XX_REG_SENSOR_THRESH(5), 0x40 }, - { CAP11XX_REG_SENSOR_NOISE_THRESH, 0x01 }, - { CAP11XX_REG_STANDBY_CHANNEL, 0x00 }, - { CAP11XX_REG_STANDBY_CONFIG, 0x39 }, - { CAP11XX_REG_STANDBY_SENSITIVITY, 0x02 }, - { CAP11XX_REG_STANDBY_THRESH, 0x40 }, + { CAP11XX_REG_SENSOR_THRESH(6), 0x40 }, + { CAP11XX_REG_SENSOR_THRESH(7), 0x40 }, { CAP11XX_REG_CONFIG2, 0x40 }, - { CAP11XX_REG_LED_POLARITY, 0x00 }, - { CAP11XX_REG_SENSOR_CALIB_LSB1, 0x00 }, - { CAP11XX_REG_SENSOR_CALIB_LSB2, 0x00 }, }; static bool cap11xx_volatile_reg(struct device *dev, unsigned int reg) @@ -149,6 +117,12 @@ static bool cap11xx_volatile_reg(struct device *dev, unsigned int reg) switch (reg) { case CAP11XX_REG_MAIN_CONTROL: case CAP11XX_REG_SENSOR_INPUT: + /* + * CAP1114_REG_BUTTON_STATUS1 (CAP11XX_REG_SENSOR_INPUT) and + * CAP1114_REG_BUTTON_STATUS2 is volatile for the CAP1114, + * which supports more than 8 touch channels. + */ + case CAP1114_REG_BUTTON_STATUS2: case CAP11XX_REG_SENOR_DELTA(0): case CAP11XX_REG_SENOR_DELTA(1): case CAP11XX_REG_SENOR_DELTA(2): @@ -257,8 +231,8 @@ static int cap11xx_init_keys(struct cap11xx_priv *priv) } if (!of_property_read_u32_array(node, "microchip,input-threshold", - priv->thresholds, priv->model->num_channels)) { - for (i = 0; i < priv->model->num_channels; i++) { + priv->thresholds, priv->model->num_sensor_thresholds)) { + for (i = 0; i < priv->model->num_sensor_thresholds; i++) { if (priv->thresholds[i] > 127) { dev_err(dev, "Invalid input-threshold value %u\n", priv->thresholds[i]); @@ -273,10 +247,13 @@ static int cap11xx_init_keys(struct cap11xx_priv *priv) } } - if (!of_property_read_u32_array(node, "microchip,calib-sensitivity", - priv->calib_sensitivities, - priv->model->num_channels)) { - if (priv->model->has_sensitivity_control) { + if (of_property_present(node, "microchip,calib-sensitivity")) { + if (!priv->model->has_sensitivity_control) { + dev_warn(dev, + "This model doesn't support 'calib-sensitivity'\n"); + } else if (!of_property_read_u32_array(node, "microchip,calib-sensitivity", + priv->calib_sensitivities, + priv->model->num_channels)) { for (i = 0; i < priv->model->num_channels; i++) { if (!is_power_of_2(priv->calib_sensitivities[i]) || priv->calib_sensitivities[i] > 4) { @@ -296,32 +273,31 @@ static int cap11xx_init_keys(struct cap11xx_priv *priv) if (error) return error; } - } else { - dev_warn(dev, - "This model doesn't support 'calib-sensitivity'\n"); } } - for (i = 0; i < priv->model->num_channels; i++) { - if (!of_property_read_u32_index(node, "microchip,signal-guard", - i, &u32_val)) { - if (u32_val > 1) - return -EINVAL; - if (u32_val) - priv->signal_guard_inputs_mask |= 0x01 << i; - } - } - - if (priv->signal_guard_inputs_mask) { - if (priv->model->has_signal_guard) { - error = regmap_write(priv->regmap, - CAP11XX_REG_SIGNAL_GUARD_ENABLE, - priv->signal_guard_inputs_mask); - if (error) - return error; - } else { + if (of_property_present(node, "microchip,signal-guard")) { + if (!priv->model->has_signal_guard) { dev_warn(dev, "This model doesn't support 'signal-guard'\n"); + } else { + for (i = 0; i < priv->model->num_channels; i++) { + if (!of_property_read_u32_index(node, "microchip,signal-guard", + i, &u32_val)) { + if (u32_val > 1) + return -EINVAL; + if (u32_val) + priv->signal_guard_inputs_mask |= 0x01 << i; + } + } + + if (priv->signal_guard_inputs_mask) { + error = regmap_write(priv->regmap, + CAP11XX_REG_SIGNAL_GUARD_ENABLE, + priv->signal_guard_inputs_mask); + if (error) + return error; + } } } @@ -332,10 +308,23 @@ static int cap11xx_init_keys(struct cap11xx_priv *priv) of_property_read_u32_array(node, "linux,keycodes", priv->keycodes, priv->model->num_channels); - /* Disable autorepeat. The Linux input system has its own handling. */ - error = regmap_write(priv->regmap, CAP11XX_REG_REPEAT_RATE, 0); - if (error) - return error; + /* + * CAP1114 needs dedicated configuration to split + * grouped sensors into independent inputs. + */ + if (priv->model->has_grouped_sensors) { + error = regmap_set_bits(priv->regmap, CAP1114_REG_CONFIG2, + CAP1114_REG_CONFIG2_VOL_UP_DOWN); + if (error) + return error; + } + + if (priv->model->has_repeat_en) { + /* Disable autorepeat. The Linux input system has its own handling. */ + error = regmap_write(priv->regmap, CAP11XX_REG_REPEAT_RATE, 0); + if (error) + return error; + } return 0; } @@ -354,10 +343,25 @@ static irqreturn_t cap11xx_thread_func(int irq_num, void *data) if (ret < 0) goto out; - ret = regmap_read(priv->regmap, CAP11XX_REG_SENSOR_INPUT, &status); + ret = regmap_read(priv->regmap, priv->model->sensor_input_reg_base, &status); if (ret < 0) goto out; + if (priv->model->num_channels > 8) { + unsigned int status2; + + ret = regmap_read(priv->regmap, priv->model->sensor_input_reg_base + 1, &status2); + if (ret < 0) + goto out; + + /* + * CAP1114 STATUS1 register only contains data for the first 6 channels. + * the remaining channels is stored in STATUS2. + */ + status &= GENMASK(5, 0); + status |= FIELD_PREP(GENMASK(13, 6), status2); + } + for (i = 0; i < priv->idev->keycodemax; i++) input_report_key(priv->idev, priv->keycodes[i], status & (1 << i)); @@ -407,10 +411,16 @@ static int cap11xx_led_set(struct led_classdev *cdev, * limitation. Brightness levels per LED are either * 0 (OFF) and 1 (ON). */ - return regmap_update_bits(priv->regmap, - CAP11XX_REG_LED_OUTPUT_CONTROL, - BIT(led->reg), - value ? BIT(led->reg) : 0); + if (led->reg >= 8) + return regmap_update_bits(priv->regmap, + priv->model->led_output_control_reg_base + 1, + BIT(led->reg - 8), + value ? BIT(led->reg - 8) : 0); + else + return regmap_update_bits(priv->regmap, + priv->model->led_output_control_reg_base, + BIT(led->reg), + value ? BIT(led->reg) : 0); } static int cap11xx_init_leds(struct device *dev, @@ -420,6 +430,7 @@ static int cap11xx_init_leds(struct device *dev, struct cap11xx_led *led; int cnt = of_get_child_count(node); int error; + u32 duty_val; if (!num_leds || !cnt) return 0; @@ -433,15 +444,26 @@ static int cap11xx_init_leds(struct device *dev, priv->leds = led; + /* Set all LEDs to off */ error = regmap_update_bits(priv->regmap, - CAP11XX_REG_LED_OUTPUT_CONTROL, 0xff, 0); + priv->model->led_output_control_reg_base, + GENMASK(min(num_leds, 8) - 1, 0), 0); if (error) return error; + if (num_leds > 8) { + error = regmap_update_bits(priv->regmap, + priv->model->led_output_control_reg_base + 1, + GENMASK(num_leds - 8 - 1, 0), 0); + if (error) + return error; + } + + duty_val = FIELD_PREP(CAP11XX_REG_LED_DUTY_MAX_MASK, + CAP11XX_REG_LED_DUTY_MAX_VALUE); + error = regmap_update_bits(priv->regmap, CAP11XX_REG_LED_DUTY_CYCLE_4, - CAP11XX_REG_LED_DUTY_MAX_MASK, - CAP11XX_REG_LED_DUTY_MAX_VALUE << - CAP11XX_REG_LED_DUTY_MAX_MASK_SHIFT); + CAP11XX_REG_LED_DUTY_MAX_MASK, duty_val); if (error) return error; @@ -510,9 +532,20 @@ static int cap11xx_i2c_probe(struct i2c_client *i2c_client) if (IS_ERR(priv->regmap)) return PTR_ERR(priv->regmap); + priv->reset_gpio = devm_gpiod_get_optional(dev, |
