diff options
Diffstat (limited to 'drivers')
47 files changed, 1258 insertions, 324 deletions
diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index a11ff74a5127..a35532ec00e4 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -25,6 +25,19 @@ config INPUT if INPUT +config INPUT_LEDS + tristate "Export input device LEDs in sysfs" + depends on LEDS_CLASS + default INPUT + help + Say Y here if you would like to export LEDs on input devices + as standard LED class devices in sysfs. + + If unsure, say Y. + + To compile this driver as a module, choose M here: the + module will be called input-leds. + config INPUT_FF_MEMLESS tristate "Support for memoryless force-feedback devices" help diff --git a/drivers/input/Makefile b/drivers/input/Makefile index 5ca3f631497f..0c9302ca9954 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_INPUT_POLLDEV) += input-polldev.o obj-$(CONFIG_INPUT_SPARSEKMAP) += sparse-keymap.o obj-$(CONFIG_INPUT_MATRIXKMAP) += matrix-keymap.o +obj-$(CONFIG_INPUT_LEDS) += input-leds.o obj-$(CONFIG_INPUT_MOUSEDEV) += mousedev.o obj-$(CONFIG_INPUT_JOYDEV) += joydev.o obj-$(CONFIG_INPUT_EVDEV) += evdev.o diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index a18f41b89b6a..9d35499faca4 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -422,10 +422,7 @@ static int evdev_release(struct inode *inode, struct file *file) evdev_detach_client(evdev, client); - if (is_vmalloc_addr(client)) - vfree(client); - else - kfree(client); + kvfree(client); evdev_close_device(evdev); diff --git a/drivers/input/ff-core.c b/drivers/input/ff-core.c index b81c88c43452..8f4a30fccbb6 100644 --- a/drivers/input/ff-core.c +++ b/drivers/input/ff-core.c @@ -70,7 +70,7 @@ static int compat_effect(struct ff_device *ff, struct ff_effect *effect) return -EINVAL; /* - * calculate manginude of sine wave as average of rumble's + * calculate magnitude of sine wave as average of rumble's * 2/3 of strong magnitude and 1/3 of weak magnitude */ magnitude = effect->u.rumble.strong_magnitude / 3 + @@ -213,7 +213,7 @@ static int erase_effect(struct input_dev *dev, int effect_id, /** * input_ff_erase - erase a force-feedback effect from device * @dev: input device to erase effect from - * @effect_id: id of the ffect to be erased + * @effect_id: id of the effect to be erased * @file: purported owner of the request * * This function erases a force-feedback effect from specified device. diff --git a/drivers/input/input-leds.c b/drivers/input/input-leds.c new file mode 100644 index 000000000000..074a65ed17bb --- /dev/null +++ b/drivers/input/input-leds.c @@ -0,0 +1,212 @@ +/* + * LED support for the input layer + * + * Copyright 2010-2015 Samuel Thibault <samuel.thibault@ens-lyon.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/leds.h> +#include <linux/input.h> + +#if IS_ENABLED(CONFIG_VT) +#define VT_TRIGGER(_name) .trigger = _name +#else +#define VT_TRIGGER(_name) .trigger = NULL +#endif + +static const struct { + const char *name; + const char *trigger; +} input_led_info[LED_CNT] = { + [LED_NUML] = { "numlock", VT_TRIGGER("kbd-numlock") }, + [LED_CAPSL] = { "capslock", VT_TRIGGER("kbd-capslock") }, + [LED_SCROLLL] = { "scrolllock", VT_TRIGGER("kbd-scrolllock") }, + [LED_COMPOSE] = { "compose" }, + [LED_KANA] = { "kana", VT_TRIGGER("kbd-kanalock") }, + [LED_SLEEP] = { "sleep" } , + [LED_SUSPEND] = { "suspend" }, + [LED_MUTE] = { "mute" }, + [LED_MISC] = { "misc" }, + [LED_MAIL] = { "mail" }, + [LED_CHARGING] = { "charging" }, +}; + +struct input_led { + struct led_classdev cdev; + struct input_handle *handle; + unsigned int code; /* One of LED_* constants */ +}; + +struct input_leds { + struct input_handle handle; + unsigned int num_leds; + struct input_led leds[]; +}; + +static enum led_brightness input_leds_brightness_get(struct led_classdev *cdev) +{ + struct input_led *led = container_of(cdev, struct input_led, cdev); + struct input_dev *input = led->handle->dev; + + return test_bit(led->code, input->led) ? cdev->max_brightness : 0; +} + +static void input_leds_brightness_set(struct led_classdev *cdev, + enum led_brightness brightness) +{ + struct input_led *led = container_of(cdev, struct input_led, cdev); + + input_inject_event(led->handle, EV_LED, led->code, !!brightness); +} + +static void input_leds_event(struct input_handle *handle, unsigned int type, + unsigned int code, int value) +{ +} + +static int input_leds_connect(struct input_handler *handler, + struct input_dev *dev, + const struct input_device_id *id) +{ + struct input_leds *leds; + unsigned int num_leds; + unsigned int led_code; + int led_no; + int error; + + num_leds = bitmap_weight(dev->ledbit, LED_CNT); + if (!num_leds) + return -ENXIO; + + leds = kzalloc(sizeof(*leds) + num_leds * sizeof(*leds->leds), + GFP_KERNEL); + if (!leds) + return -ENOMEM; + + leds->num_leds = num_leds; + + leds->handle.dev = dev; + leds->handle.handler = handler; + leds->handle.name = "leds"; + leds->handle.private = leds; + + error = input_register_handle(&leds->handle); + if (error) + goto err_free_mem; + + error = input_open_device(&leds->handle); + if (error) + goto err_unregister_handle; + + led_no = 0; + for_each_set_bit(led_code, dev->ledbit, LED_CNT) { + struct input_led *led = &leds->leds[led_no]; + + led->handle = &leds->handle; + led->code = led_code; + + if (WARN_ON(!input_led_info[led_code].name)) + continue; + + led->cdev.name = kasprintf(GFP_KERNEL, "%s::%s", + dev_name(&dev->dev), + input_led_info[led_code].name); + if (!led->cdev.name) { + error = -ENOMEM; + goto err_unregister_leds; + } + + led->cdev.max_brightness = 1; + led->cdev.brightness_get = input_leds_brightness_get; + led->cdev.brightness_set = input_leds_brightness_set; + led->cdev.default_trigger = input_led_info[led_code].trigger; + + error = led_classdev_register(&dev->dev, &led->cdev); + if (error) { + dev_err(&dev->dev, "failed to register LED %s: %d\n", + led->cdev.name, error); + kfree(led->cdev.name); + goto err_unregister_leds; + } + + led_no++; + } + + return 0; + +err_unregister_leds: + while (--led_no >= 0) { + struct input_led *led = &leds->leds[led_no]; + + led_classdev_unregister(&led->cdev); + kfree(led->cdev.name); + } + + input_close_device(&leds->handle); + +err_unregister_handle: + input_unregister_handle(&leds->handle); + +err_free_mem: + kfree(leds); + return error; +} + +static void input_leds_disconnect(struct input_handle *handle) +{ + struct input_leds *leds = handle->private; + int i; + + for (i = 0; i < leds->num_leds; i++) { + struct input_led *led = &leds->leds[i]; + + led_classdev_unregister(&led->cdev); + kfree(led->cdev.name); + } + + input_close_device(handle); + input_unregister_handle(handle); + + kfree(leds); +} + +static const struct input_device_id input_leds_ids[] = { + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT, + .evbit = { BIT_MASK(EV_LED) }, + }, + { }, +}; +MODULE_DEVICE_TABLE(input, input_leds_ids); + +static struct input_handler input_leds_handler = { + .event = input_leds_event, + .connect = input_leds_connect, + .disconnect = input_leds_disconnect, + .name = "leds", + .id_table = input_leds_ids, +}; + +static int __init input_leds_init(void) +{ + return input_register_handler(&input_leds_handler); +} +module_init(input_leds_init); + +static void __exit input_leds_exit(void) +{ + input_unregister_handler(&input_leds_handler); +} +module_exit(input_leds_exit); + +MODULE_AUTHOR("Samuel Thibault <samuel.thibault@ens-lyon.org>"); +MODULE_AUTHOR("Dmitry Torokhov <dmitry.torokhov@gmail.com>"); +MODULE_DESCRIPTION("Input -> LEDs Bridge"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/input.c b/drivers/input/input.c index cc357f1516a7..f31578423636 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -2262,7 +2262,7 @@ EXPORT_SYMBOL(input_unregister_handler); * * Iterate over @bus's list of devices, and call @fn for each, passing * it @data and stop when @fn returns a non-zero value. The function is - * using RCU to traverse the list and therefore may be usind in atonic + * using RCU to traverse the list and therefore may be using in atomic * contexts. The @fn callback is invoked from RCU critical section and * thus must not sleep. */ diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index e8eb60c6d83e..4cd94fd6cbad 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -367,6 +367,7 @@ config KEYBOARD_MAPLE config KEYBOARD_MAX7359 tristate "Maxim MAX7359 Key Switch Controller" + select INPUT_MATRIXKMAP depends on I2C help If you say yes here you get support for the Maxim MAX7359 Key diff --git a/drivers/input/keyboard/adp5589-keys.c b/drivers/input/keyboard/adp5589-keys.c index a45267729dfc..6ed83cf8b74e 100644 --- a/drivers/input/keyboard/adp5589-keys.c +++ b/drivers/input/keyboard/adp5589-keys.c @@ -180,7 +180,7 @@ #define LOGIC2_STAT (1 << 7) /* ADP5589 only */ #define LOGIC1_STAT (1 << 6) #define LOCK_STAT (1 << 5) /* ADP5589 only */ -#define KEC 0xF +#define KEC 0x1F /* PIN_CONFIG_D Register */ #define C4_EXTEND_CFG (1 << 6) /* RESET2 */ @@ -726,7 +726,7 @@ static int adp5589_setup(struct adp5589_kpad *kpad) pull_mask |= val << (2 * (i & 0x3)); - if (i == 3 || i == kpad->var->max_row_num) { + if (i % 4 == 3 || i == kpad->var->max_row_num) { ret |= adp5589_write(client, reg(ADP5585_RPULL_CONFIG_A) + (i >> 2), pull_mask); pull_mask = 0; @@ -746,7 +746,7 @@ static int adp5589_setup(struct adp5589_kpad *kpad) pull_mask |= val << (2 * (i & 0x3)); - if (i == 3 || i == kpad->var->max_col_num) { + if (i % 4 == 3 || i == kpad->var->max_col_num) { ret |= adp5589_write(client, reg(ADP5585_RPULL_CONFIG_C) + (i >> 2), pull_mask); diff --git a/drivers/input/keyboard/clps711x-keypad.c b/drivers/input/keyboard/clps711x-keypad.c index 27ef29f8fe6a..b637f1af842e 100644 --- a/drivers/input/keyboard/clps711x-keypad.c +++ b/drivers/input/keyboard/clps711x-keypad.c @@ -120,14 +120,9 @@ static int clps711x_keypad_probe(struct platform_device *pdev) for (i = 0; i < priv->row_count; i++) { struct clps711x_gpio_data *data = &priv->gpio_data[i]; - data->desc = devm_gpiod_get_index(dev, "row", i); - if (!data->desc) - return -EINVAL; - + data->desc = devm_gpiod_get_index(dev, "row", i, GPIOD_IN); if (IS_ERR(data->desc)) return PTR_ERR(data->desc); - - gpiod_direction_input(data->desc); } err = of_property_read_u32(np, "poll-interval", &poll_interval); diff --git a/drivers/input/keyboard/max7359_keypad.c b/drivers/input/keyboard/max7359_keypad.c index faa6da53eba8..5091133b7b8e 100644 --- a/drivers/input/keyboard/max7359_keypad.c +++ b/drivers/input/keyboard/max7359_keypad.c @@ -84,26 +84,6 @@ static int max7359_read_reg(struct i2c_client *client, int reg) return ret; } -static void max7359_build_keycode(struct max7359_keypad *keypad, - const struct matrix_keymap_data *keymap_data) -{ - struct input_dev *input_dev = keypad->input_dev; - int i; - - for (i = 0; i < keymap_data->keymap_size; i++) { - unsigned int key = keymap_data->keymap[i]; - unsigned int row = KEY_ROW(key); - unsigned int col = KEY_COL(key); - unsigned int scancode = MATRIX_SCAN_CODE(row, col, - MAX7359_ROW_SHIFT); - unsigned short keycode = KEY_VAL(key); - - keypad->keycodes[scancode] = keycode; - __set_bit(keycode, input_dev->keybit); - } - __clear_bit(KEY_RESERVED, input_dev->keybit); -} - /* runs in an IRQ thread -- can (and will!) sleep */ static irqreturn_t max7359_interrupt(int irq, void *dev_id) { @@ -166,7 +146,6 @@ static void max7359_close(struct input_dev *dev) static void max7359_initialize(struct i2c_client *client) { max7359_write_reg(client, MAX7359_REG_CONFIG, - MAX7359_CFG_INTERRUPT | /* Irq clears after host read */ MAX7359_CFG_KEY_RELEASE | /* Key release enable */ MAX7359_CFG_WAKEUP); /* Key press wakeup enable */ @@ -233,7 +212,15 @@ static int max7359_probe(struct i2c_client *client, input_set_capability(input_dev, EV_MSC, MSC_SCAN); input_set_drvdata(input_dev, keypad); - max7359_build_keycode(keypad, keymap_data); + error = matrix_keypad_build_keymap(keymap_data, NULL, + MAX7359_MAX_KEY_ROWS, + MAX7359_MAX_KEY_COLS, + keypad->keycodes, + input_dev); + if (error) { + dev_err(&client->dev, "failed to build keymap\n"); + return error; + } error = devm_request_threaded_irq(&client->dev, client->irq, NULL, max7359_interrupt, diff --git a/drivers/input/keyboard/samsung-keypad.c b/drivers/input/keyboard/samsung-keypad.c index 6b9fdf6cf8e8..43e48dac7687 100644 --- a/drivers/input/keyboard/samsung-keypad.c +++ b/drivers/input/keyboard/samsung-keypad.c @@ -585,7 +585,7 @@ static const struct of_device_id samsung_keypad_dt_match[] = { MODULE_DEVICE_TABLE(of, samsung_keypad_dt_match); #endif -static struct platform_device_id samsung_keypad_driver_ids[] = { +static const struct platform_device_id samsung_keypad_driver_ids[] = { { .name = "samsung-keypad", .driver_data = KEYPAD_TYPE_SAMSUNG, diff --git a/drivers/input/keyboard/spear-keyboard.c b/drivers/input/keyboard/spear-keyboard.c index f42a543db043..623d451767e3 100644 --- a/drivers/input/keyboard/spear-keyboard.c +++ b/drivers/input/keyboard/spear-keyboard.c @@ -3,7 +3,7 @@ * Based on omap-keypad driver * * Copyright (C) 2010 ST Microelectronics - * Rajeev Kumar<rajeev-dlh.kumar@st.com> + * Rajeev Kumar <rajeevkumar.linux@gmail.com> * * This file is licensed under the terms of the GNU General Public * License version 2. This program is licensed "as is" without any diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 4436ab1b9735..d4f0a817e858 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -610,6 +610,16 @@ config INPUT_DA9055_ONKEY To compile this driver as a module, choose M here: the module will be called da9055_onkey. +config INPUT_DA9063_ONKEY + tristate "Dialog DA9063 OnKey" + depends on MFD_DA9063 + help + Support the ONKEY of Dialog DA9063 Power Management IC as an + input device reporting power button statue. + + To compile this driver as a module, choose M here: the module + will be called da9063_onkey. + config INPUT_DM355EVM tristate "TI DaVinci DM355 EVM Keypad and IR Remote" depends on MFD_DM355EVM_MSP @@ -775,6 +785,17 @@ config INPUT_DRV260X_HAPTICS To compile this driver as a module, choose M here: the module will be called drv260x-haptics. +config INPUT_DRV2665_HAPTICS + tristate "TI DRV2665 haptics support" + depends on INPUT && I2C + select INPUT_FF_MEMLESS + select REGMAP_I2C + help + Say Y to enable support for the TI DRV2665 haptics driver. + + To compile this driver as a module, choose M here: the + module will be called drv2665-haptics. + config INPUT_DRV2667_HAPTICS tristate "TI DRV2667 haptics support" depends on INPUT && I2C @@ -784,6 +805,6 @@ config INPUT_DRV2667_HAPTICS Say Y to enable support for the TI DRV2667 haptics driver. To compile this driver as a module, choose M here: the - module will be called drv260x-haptics. + module will be called drv2667-haptics. endif diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 78ba4c1b8532..53df07dcc23c 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -25,9 +25,11 @@ obj-$(CONFIG_INPUT_CMA3000_I2C) += cma3000_d0x_i2c.o obj-$(CONFIG_INPUT_COBALT_BTNS) += cobalt_btns.o obj-$(CONFIG_INPUT_DA9052_ONKEY) += da9052_onkey.o obj-$(CONFIG_INPUT_DA9055_ONKEY) += da9055_onkey.o +obj-$(CONFIG_INPUT_DA9063_ONKEY) += da9063_onkey.o obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o obj-$(CONFIG_INPUT_E3X0_BUTTON) += e3x0-button.o obj-$(CONFIG_INPUT_DRV260X_HAPTICS) += drv260x.o +obj-$(CONFIG_INPUT_DRV2665_HAPTICS) += drv2665.o obj-$(CONFIG_INPUT_DRV2667_HAPTICS) += drv2667.o obj-$(CONFIG_INPUT_GP2A) += gp2ap002a00f.o obj-$(CONFIG_INPUT_GPIO_BEEPER) += gpio-beeper.o diff --git a/drivers/input/misc/adxl34x-i2c.c b/drivers/input/misc/adxl34x-i2c.c index 470bfd6f0830..bdb5d03b296e 100644 --- a/drivers/input/misc/adxl34x-i2c.c +++ b/drivers/input/misc/adxl34x-i2c.c @@ -10,6 +10,7 @@ #include <linux/input.h> /* BUS_I2C */ #include <linux/i2c.h> #include <linux/module.h> +#include <linux/of.h> #include <linux/types.h> #include <linux/pm.h> #include "adxl34x.h" @@ -135,11 +136,31 @@ static const struct i2c_device_id adxl34x_id[] = { MODULE_DEVICE_TABLE(i2c, adxl34x_id); +#ifdef CONFIG_OF +static const struct of_device_id adxl34x_of_id[] = { + /* + * The ADXL346 is backward-compatible with the ADXL345. Differences are + * handled by runtime detection of the device model, there's thus no + * need for listing the "adi,adxl346" compatible value explicitly. + */ + { .compatible = "adi,adxl345", }, + /* + * Deprecated, DT nodes should use one or more of the device-specific + * compatible values "adi,adxl345" and "adi,adxl346". + */ + { .compatible = "adi,adxl34x", }, + { } +}; + +MODULE_DEVICE_TABLE(of, adxl34x_of_id); +#endif + static struct i2c_driver adxl34x_driver = { .driver = { .name = "adxl34x", .owner = THIS_MODULE, .pm = &adxl34x_i2c_pm, + .of_match_table = of_match_ptr(adxl34x_of_id), }, .probe = adxl34x_i2c_probe, .remove = adxl34x_i2c_remove, diff --git a/drivers/input/misc/da9063_onkey.c b/drivers/input/misc/da9063_onkey.c new file mode 100644 index 000000000000..f577585ef999 --- /dev/null +++ b/drivers/input/misc/da9063_onkey.c @@ -0,0 +1,226 @@ +/* + * OnKey device driver for DA9063 + * Copyright (C) 2015 Dialog Semiconductor Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/workqueue.h> +#include <linux/regmap.h> +#include <linux/of.h> +#include <linux/mfd/da9063/core.h> +#include <linux/mfd/da9063/pdata.h> +#include <linux/mfd/da9063/registers.h> + +struct da9063_onkey { + struct da9063 *hw; + struct delayed_work work; + struct input_dev *input; + struct device *dev; + bool key_power; +}; + +static void da9063_poll_on(struct work_struct *work) +{ + struct da9063_onkey *onkey = container_of(work, struct da9063_onkey, + work.work); + unsigned int val; + int fault_log = 0; + bool poll = true; + int error; + + /* Poll to see when the pin is released */ + error = regmap_read(onkey->hw->regmap, DA9063_REG_STATUS_A, &val); + if (error) { + dev_err(onkey->dev, + "Failed to read ON status: %d\n", error); + goto err_poll; + } + + if (!(val & DA9063_NONKEY)) { + error = regmap_update_bits(onkey->hw->regmap, + DA9063_REG_CONTROL_B, + DA9063_NONKEY_LOCK, 0); + if (error) { + dev_err(onkey->dev, + "Failed to reset the Key Delay %d\n", error); + goto err_poll; + } + + input_report_key(onkey->input, KEY_POWER, 0); + input_sync(onkey->input); + + poll = false; + } + + /* + * If the fault log KEY_RESET is detected, then clear it + * and shut down the system. + */ + error = regmap_read(onkey->hw->regmap, + DA9063_REG_FAULT_LOG, &fault_log); + if (error) { + dev_warn(&onkey->input->dev, + "Cannot read FAULT_LOG: %d\n", error); + } else if (fault_log & DA9063_KEY_RESET) { + error = regmap_write(onkey->hw->regmap, + DA9063_REG_FAULT_LOG, + DA9063_KEY_RESET); + if (error) { + dev_warn(&onkey->input->dev, + "Cannot reset KEY_RESET fault log: %d\n", + error); + } else { + /* at this point we do any S/W housekeeping + * and then send shutdown command + */ + dev_dbg(&onkey->input->dev, + "Sending SHUTDOWN to DA9063 ...\n"); + error = regmap_write(onkey->hw->regmap, + DA9063_REG_CONTROL_F, + DA9063_SHUTDOWN); + if (error) + dev_err(&onkey->input->dev, + "Cannot SHUTDOWN DA9063: %d\n", + error); + } + } + +err_poll: + if (poll) + schedule_delayed_work(&onkey->work, msecs_to_jiffies(50)); +} + +static irqreturn_t da9063_onkey_irq_handler(int irq, void *data) +{ + struct da9063_onkey *onkey = data; + unsigned int val; + int error; + + error = regmap_read(onkey->hw->regmap, DA9063_REG_STATUS_A, &val); + if (onkey->key_power && !error && (val & DA9063_NONKEY)) { + input_report_key(onkey->input, KEY_POWER, 1); + input_sync(onkey->input); + schedule_delayed_work(&onkey->work, 0); + dev_dbg(onkey->dev, "KEY_POWER pressed.\n"); + } else { + input_report_key(onkey->input, KEY_SLEEP, 1); + input_sync(onkey->input); + input_report_key(onkey->input, KEY_SLEEP, 0); + input_sync(onkey->input); + dev_dbg(onkey->dev, "KEY_SLEEP pressed.\n"); + } + + return IRQ_HANDLED; +} + +static void da9063_cancel_poll(void *data) +{ + struct da9063_onkey *onkey = data; + + cancel_delayed_work_sync(&onkey->work); +} + +static int da9063_onkey_probe(struct platform_device *pdev) +{ + struct da9063 *da9063 = dev_get_drvdata(pdev->dev.parent); + struct da9063_pdata *pdata = dev_get_platdata(da9063->dev); + struct da9063_onkey *onkey; + int irq; + int error; + + onkey = devm_kzalloc(&pdev->dev, sizeof(struct da9063_onkey), + GFP_KERNEL); + if (!onkey) { + dev_err(&pdev->dev, "Failed to allocate memory.\n"); + return -ENOMEM; + } + + onkey->dev = &pdev->dev; + onkey->hw = da9063; + + if (pdata) + onkey->key_power = pdata->key_power; + else + onkey->key_power = + !of_property_read_bool(pdev->dev.of_node, + "dlg,disable-key-power"); + + onkey->input = devm_input_allocate_device(&pdev->dev); + if (!onkey->input) { + dev_err(&pdev->dev, "Failed to allocated input device.\n"); + return -ENOMEM; + } + + onkey->input->name = DA9063_DRVNAME_ONKEY; + onkey->input->phys = DA9063_DRVNAME_ONKEY "/input0"; + onkey->input->dev.parent = &pdev->dev; + + if (onkey->key_power) + input_set_capability(onkey->input, EV_KEY, KEY_POWER); + + input_set_capability(onkey->input, EV_KEY, KEY_SLEEP); + + INIT_DELAYED_WORK(&onkey->work, da9063_poll_on); + + error = devm_add_action(&pdev->dev, da9063_cancel_poll, onkey); + if (error) { + dev_err(&pdev->dev, + "Failed to add cancel poll action: %d\n", + error); + return error; + } + + irq = platform_get_irq_byname(pdev, "ONKEY"); + if (irq < 0) { + error = irq; + dev_err(&pdev->dev, "Failed to get platform IRQ: %d\n", error); + return error; + } + + error = devm_request_threaded_irq(&pdev->dev, irq, + NULL, da9063_onkey_irq_handler, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "ONKEY", onkey); + if (error) { + dev_err(&pdev->dev, + "Failed to request IRQ %d: %d\n", irq, error); + return error; + } + + error = input_register_device(onkey->input); + if (error) { + dev_err(&pdev->dev, + "Failed to register input device: %d\n", error); + return error; + } + + platform_set_drvdata(pdev, onkey); + return 0; +} + +static struct platform_driver da9063_onkey_driver = { + .probe = da9063_onkey_probe, + .driver = { + .name = DA9063_DRVNAME_ONKEY, + }, +}; +module_platform_driver(da9063_onkey_driver); + +MODULE_AUTHOR("S Twiss <stwiss.opensource@diasemi.com>"); +MODULE_DESCRIPTION("Onkey device driver for Dialog DA9063"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DA9063_DRVNAME_ONKEY); diff --git a/drivers/input/misc/drv260x.c b/drivers/input/misc/drv260x.c index 599578042ea0..e5d60ecd29a4 100644 --- a/drivers/input/misc/drv260x.c +++ b/drivers/input/misc/drv260x.c @@ -580,15 +580,10 @@ static int drv260x_probe(struct i2c_client *client, return error; } - haptics->enable_gpio = devm_gpiod_get(&client->dev, "enable"); - if (IS_ERR(haptics->enable_gpio)) { - error = PTR_ERR(haptics->enable_gpio); - if (error != -ENOENT && error != -ENOSYS) - return error; - haptics->enable_gpio = NULL; - } else { - gpiod_direction_output(haptics->enable_gpio, 1); - } + haptics->enable_gpio = devm_gpiod_get_optional(&client->dev, "enable", + GPIOD_OUT_HIGH); + if (IS_ERR(haptics->enable_gpio)) + return PTR_ERR(haptics->enable_gpio); haptics->input_dev = devm_input_allocate_device(&client->dev); if (!haptics->input_dev) { diff --git a/drivers/input/misc/drv2665.c b/drivers/input/misc/drv2665.c new file mode 100644 index 000000000000..0afaa33de07d --- /dev/null +++ b/drivers/input/misc/drv2665.c @@ -0,0 +1,322 @@ +/* + * DRV2665 haptics driver family + * + * Author: Dan Murphy <dmurphy@ti.com> + * + * Copyright: (C) 2015 Texas Instruments, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include <linux/i2c.h> +#include <linux/input.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/regulator/consumer.h> + +/* Contol registers */ +#define DRV2665_STATUS 0x00 +#define DRV2665_CTRL_1 0x01 +#define DRV2665_CTRL_2 0x02 +#define DRV2665_FIFO 0x0b + +/* Status Register */ +#define DRV2665_FIFO_FULL BIT(0) +#define DRV2665_FIFO_EMPTY BIT(1) + +/* Control 1 Register */ +#define DRV2665_25_VPP_GAIN 0x00 +#define DRV2665_50_VPP_GAIN 0x01 +#define DRV2665_75_VPP_GAIN 0x02 +#define DRV2665_100_VPP_GAIN 0x03 +#define DRV2665_DIGITAL_IN 0xfc +#define DRV2665_ANALOG_IN BIT(2) + +/* Control 2 Register */ +#define DRV2665_BOOST_EN BIT(1) +#define DRV2665_STANDBY BIT(6) +#define DRV2665_DEV_RST BIT(7) +#define DRV2665_5_MS_IDLE_TOUT 0x00 +#define DRV2665_10_MS_IDLE_TOUT 0x04 +#define DRV2665_15_MS_IDLE_TOUT 0x08 +#define DRV2665_20_MS_IDLE_TOUT 0x0c + +/** + * struct drv2665_data - + * @input_dev - Pointer to the input device + * @client - Pointer to the I2C client + * @regmap - Register map of the device + * @work - Work item used to off load the enable/disable of the vibration + * @regulator - Pointer to the regulator for the IC + */ +struct drv2665_data { + struct input_dev *input_dev; + struct i2c_client *client; + struct regmap *regmap; + struct work_struct work; + struct regulator *regulator; +}; + +/* 8kHz Sine wave to stream to the FIFO */ +static const u8 drv2665_sine_wave_form[] = { + 0x00, 0x10, 0x20, 0x2e, 0x3c, 0x48, 0x53, 0x5b, 0x61, 0x65, 0x66, + 0x65, 0x61, 0x5b, 0x53, 0x48, 0x3c, 0x2e, 0x20, 0x10, + 0x00, 0xf0, 0xe0, 0xd2, 0xc4, 0xb8, 0xad, 0xa5, 0x9f, 0x9b, 0x9a, + 0x9b, 0x9f, 0xa5, 0xad, 0xb8, 0xc4, 0xd2, 0xe0, 0xf0, 0x00, +}; + +static struct reg_default drv2665_reg_defs[] = { + { DRV2665_STATUS, 0x02 }, + { DRV2665_CTRL_1, 0x28 }, + { DRV2665_CTRL_2, 0x40 }, + { DRV2665_FIFO, 0x00 }, +}; + +static void drv2665_worker(struct work_struct *work) +{ + struct drv2665_data *haptics = + container_of(work, struct drv2665_data, work); + unsigned int read_buf; + int error; + + error = regmap_read(haptics->regmap, DRV2665_STATUS, &read_buf); + if (error) { + dev_err(&haptics->client->dev, + "Failed to read status: %d\n", error); + return; + } + + if (read_buf & DRV2665_FIFO_EMPTY) { + error = regmap_bulk_write(haptics->regmap, + DRV2665_FIFO, + drv2665_sine_wave_form, + ARRAY_SIZE(drv2665_sine_wave_form)); + if (error) { + dev_err(&haptics->client->dev, + "Failed to write FIFO: %d\n", error); + return; + } + } +} + +static int drv2665_haptics_play(struct input_dev *input, void *data, + struct ff_effect *effect) +{ + struct drv2665_data *haptics = input_get_drvdata(input); + + schedule_work(&haptics->work); + + return 0; +} + +static void drv2665_close(struct input_dev *input) +{ + struct drv2665_data *haptics = input_get_drvdata(input); + int error; + + cancel_work_sync(&haptics->work); + + error = regmap_update_bits(haptics->regmap, + DRV2665_CTRL_2, DRV2665_STANDBY, 1); + if (error) + dev_err(&haptics->client->dev, + "Failed to enter standby mode: %d\n", error); +} + +static const struct reg_default drv2665_init_regs[] = { + { DRV2665_CTRL_2, 0 | DRV2665_10_MS_IDLE_TOUT }, + { DRV2665_CTRL_1, DRV2665_25_VPP_GAIN }, +}; + +static int drv2665_init(struct drv2665_data *haptics) +{ + int error; + + error = regmap_register_patch(haptics->regmap, + drv2665_init_regs, + ARRAY_SIZE(drv2665_init_regs)); + if (error) { + dev_err(&haptics->client->dev, + "Failed to write init registers: %d\n", + error); + return error; + } + + return 0; +} + +static const struct regmap_config drv2665_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = DRV2665_FIFO, + .reg_defaults = drv2665_reg_defs, + .num_reg_defaults = ARRAY_SIZE(drv2665_reg_defs), + .cache_type = REGCACHE_NONE, +}; + +static int drv2665_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct drv2665_data *haptics; + int error; + + haptics = devm_kzalloc(&client->dev, sizeof(*haptics), GFP_KERNEL); + if (!haptics) + return -ENOMEM; + + haptics->regulator = devm_regulator_get(&client->dev, "vbat"); + if (IS_ERR(haptics->regulator)) { + error = PTR_ERR(haptics->regulator); + dev_err(&client->dev, + "unable to get regulator, error: %d\n", error); + return error; + } + + haptics->input_dev = devm_input_allocate_device(&client->dev); + if (!haptics->input_dev) { + dev_err(&client->dev, "Failed to allocate input device\n"); + return -ENOMEM; + } + + haptics->input_dev->name = "drv2665:haptics"; + haptics->input_dev->dev.parent = client->dev.parent; + haptics->input_dev->close = drv2665_close; + input_set_drvdata(haptics->input_dev, haptics); + input_set_capability(haptics->input_dev, EV_FF, FF_RUMBLE); + + error = input_ff_create_memless(haptics->input_dev, NULL, + drv2665_haptics_play); + if (error) { + dev_err(&client->dev, "input_ff_create() failed: %d\n", + error); + return error; + } + + INIT_WORK(&haptics->work, drv2665_worker); + + haptics->client = client; + i2c_set_clientdata(client, haptics); + + haptics->regmap = devm_regmap_init_i2c(client, &drv2665_regmap_config); + if (IS_ERR(haptics->regmap)) { + error = PTR_ERR(haptics->regmap); + dev_err(&client->dev, "Failed to allocate register map: %d\n", + error); + return error; + } + + error = drv2665_init(haptics); + if (error) { + dev_err(&client->dev, "Device init failed: %d\n", error); + return error; + } + + error = input_register_device(haptics->input_dev); + if (error) { + dev_err(&client->dev, "couldn't register input device: %d\n", + error); + return error; + } + + return 0; +} + +static int __maybe_unused drv2665_suspend(struct device *dev) +{ + struct drv2665_data *haptics = dev_get_drvdata(dev); + int ret = 0; + + mutex_lock(&haptics->input_dev->mutex); + + if (haptics->input_dev->users) { + ret = regmap_update_bits(haptics->regmap, DRV2665_CTRL_2, + DRV2665_STANDBY, 1); + if (ret) { + dev_err(dev, "Failed to set standby mode\n"); + regulator_disable(haptics->regulator); + goto out; + } + + ret = regulator_disable(haptics->regulator); + if (ret) { + dev_err(dev, "Failed to disable regulator\n"); + regmap_update_bits(haptics->regmap, + DRV2665_CTRL_2, + DRV2665_STANDBY, 0); + } + } +out: + mutex_unlock(&haptics->input_dev->mutex); + return ret; +} + +static int __maybe_unused drv2665_resume(struct device *dev) +{ + struct drv2665_data *haptics = dev_get_drvdata(dev); + int ret = 0; + + mutex_lock(&haptics->input_dev->mutex); + + if (haptics->input_dev->users) { + ret = regulator_enable(haptics->regulator); + if (ret) { + dev_err(dev, "Failed to enable regulator\n"); + goto out; + } + + ret = regmap_update_bits(haptics->regmap, DRV2665_CTRL_2, + DRV2665_STANDBY, 0); + if (ret) { + dev_err(dev, "Failed to unset standby mode\n"); + regulator_disable(haptics->regulator); + goto out; + } + + } + +out: + mutex_unlock(&haptics->input_dev->mutex); + return ret; +} + +static SIMPLE_DEV_PM_OPS(drv2665_pm_ops, drv2665_suspend, drv2665_resume); + +static const struct i2c_device_id drv2665_id[] = { + { "drv2665", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, drv2665_id); + +#ifdef CONFIG_OF +static const struct of_device_id drv2665_of_match[] = { + { .compatible = "ti,drv2665", }, + { } +}; +MODULE_DEVICE_TABLE(of, drv2665_of_match); +#endif + +static struct i2c_driver drv2665_driver = { + .probe = drv2665_probe, + .driver = { + .name = "drv2665-haptics", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(drv2665_of_match), + .pm = &drv2665_pm_ops, + }, + .id_table = drv2665_id, +}; +module_i2c_driver(drv2665_driver); + +MODULE_DESCRIPTION("TI DRV2665 haptics driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>"); diff --git a/drivers/input/misc/gpio-beeper.c b/drivers/input/misc/gpio-beeper.c index 4817c5f0c3e4..16272fffeb7e 100644 --- a/drivers/input/misc/gpio-beeper.c +++ b/drivers/input/misc/gpio-beeper.c @@ -66,13 +66,12 @@ static int gpio_beeper_probe(struct platform_device *pdev) { struct gpio_beeper *beep; struct input_dev *input; - int err; beep = devm_kzalloc(&pdev->dev, sizeof(*beep), GFP_KERNEL); if (!beep) return -ENOMEM; - beep->desc = devm_gpiod_get(&pdev->dev, NULL); + beep->desc = devm_gpiod_get(&pdev->dev, NULL, GPIOD_OUT_LOW); if (IS_ERR(beep->desc)) return PTR_ERR(beep->desc); @@ -92,10 +91,6 @@ static int gpio_beeper_probe(struct platform_device *pdev) input_set_capability(input, EV_SND, SND_BELL); - err = gpiod_direction_output(beep->desc, 0); - if (err) - return err; - input_set_drvdata(input, beep); return input_register_device(input); diff --git a/drivers/input/misc/retu-pwrbutton.c b/drivers/input/misc/retu-pwrbutton.c index 0c8ac60e2639..30b459b6b344 100644 --- a/drivers/input/misc/retu-pwrbutton.c +++ b/drivers/input/misc/retu-pwrbutton.c @@ -63,7 +63,8 @@ static int retu_pwrbutton_probe(struct platform_device *pdev) input_set_drvdata(idev, rdev); error = devm_request_threaded_irq(&pdev->dev, irq, - NULL, retu_pwrbutton_irq, 0, + NULL, retu_pwrbutton_irq, + IRQF_ONESHOT, "retu-pwrbutton", idev); if (error) return error; diff --git a/drivers/input/misc/soc_button_array.c b/drivers/input/misc/soc_button_array.c index e8e010a85484..c14b82709b0f 100644 --- a/drivers/input/misc/soc_button_array.c +++ b/drivers/input/misc/soc_button_array.c @@ -18,7 +18,6 @@ #include <linux/gpio/consumer.h> #include <linux/gpio_keys.h> #include <linux/platform_device.h> -#include <linux/acpi.h> /* * Definition of buttons on the tablet. The ACPI index of each button diff --git a/drivers/input/misc/twl4030-pwrbutton.c b/drivers/input/misc/twl4030-pwrbutton.c index e98cc81a84c6..603fc2fadf05 100644 --- a/drivers/input/misc/twl4030-pwrbutton.c +++ b/drivers/input/misc/twl4030-pwrbutton.c @@ -71,7 +71,8 @@ static int twl4030_pwrbutton_probe(struct platform_device *pdev) pwr->dev.parent = &pdev->dev; err = devm_request_threaded_irq(&pwr->dev, irq, NULL, powerbutton_irq, - IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | + IRQF_ONESHOT, "twl4030_pwrbutton", pwr); if (err < 0) { dev_err(&pdev->dev, "Can't get IRQ for pwrbutton: %d\n", err); diff --git a/drivers/input/misc/twl6040-vibra.c b/drivers/input/misc/twl6040-vibra.c index 0e0d094df2e6..ea63fad48de6 100644 --- a/drivers/input/misc/twl6040-vibra.c +++ b/drivers/input/misc/twl6040-vibra.c @@ -308,7 +308,8 @@ static int twl6040_vibra_probe(struct platform_device *pdev) mutex_init(&info->mutex); error = devm_request_threaded_irq(&pdev->dev, info->irq, NULL, - twl6040_vib_irq_handler, 0, + twl6040_vib_irq_handler, + IRQF_ONESHOT, "twl6040_irq_vib", info); if (error) { dev_err(info->dev, "VIB IRQ request failed: %d\n", error); diff --git a/drivers/input/misc/wm831x-on.c b/drivers/input/misc/wm831x-on.c index 59d4f7bcb4a3..1b44de265a0a 100644 --- a/drivers/input/misc/wm831x-on.c +++ b/drivers/input/misc/wm831x-on.c @@ -99,7 +99,8 @@ static int wm831x_on_probe(struct platform_device *pdev) wm831x_on->dev->dev.parent = &pdev->dev; ret = request_threaded_irq(irq, NULL, wm831x_on_irq, - IRQF_TRIGGER_RISING, "wm831x_on", + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + "wm831x_on", wm831x_on); if (ret < 0) { dev_err(&pdev->dev, "Unable to request IRQ: %d\n", ret); diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index a353b7de6d22..113d6f1516a5 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -159,8 +159,8 @@ static const struct alps_protocol_info alps_v8_protocol_data = { static void alps_set_abs_params_st(struct alps_data *priv, struct input_dev *dev1); -static void alps_set_abs_params_mt(struct alps_data *priv, - struct input_dev *dev1); +static void alps_set_abs_params_semi_mt(struct alps_data *priv, + struct input_dev *dev1); static void alps_set_abs_params_v7(struct alps_data *priv, struct input_dev *dev1); static void alps_set_abs_params_ss4_v2(struct alps_data *priv, @@ -310,53 +310,6 @@ static void alps_process_packet_v1_v2(struct psmouse *psmouse) input_sync(dev); } -/* - * Process bitmap data for V5 protocols. Return value is null. - * - * The bitmaps don't have enough data to track fingers, so this function - * only generates points representing a bounding box of at most two contacts. - * These two points are returned in fields->mt. - */ -static void alps_process_bitmap_dolphin(struct alps_data *priv, - struct alps_fields *fields) -{ - int box_middle_x, box_middle_y; - unsigned int x_map, y_map; - unsigned char start_bit, end_bit; - unsigned char x_msb, x_lsb, y_msb, y_lsb; - - x_map = fields->x_map; - y_map = fields->y_map; - - if (!x_map || !y_map) - return; - - /* Get Most-significant and Least-significant bit */ - x_msb = fls(x_map); - x_lsb = ffs(x_map); - y_msb = fls(y_map); - y_lsb = ffs(y_map); - - /* Most-significant bit should never exceed max sensor line number */ - if (x_msb > priv->x_bits || y_msb > priv->y_bits) - return; - - if (fields->fingers > 1) { - start_bit = priv->x_bits - x_msb; - end_bit = priv->x_bits - x_lsb; - box_middle_x = (priv->x_max * (start_bit + end_bit)) / - (2 * (priv->x_bits - 1)); - - start_bit = y_lsb - 1; - end_bit = y_msb - 1; - box_middle_y = (priv->y_max * (start_bit + end_bit)) / - (2 * (priv->y_bits - 1)); - fields->mt[0] = fields->st; - fields->mt[1].x = 2 * box_middle_x - fields->mt[0].x; - fields->mt[1].y = 2 * box_middle_y - fields->mt[0].y; - } -} - static void alps_get_bitmap_points(unsigned int map, struct alps_bitmap_point *low, struct alps_bitmap_point *high, @@ -384,7 +337,7 @@ static void alps_get_bitmap_points(unsigned int map, } /* - * Process bitmap data from v3 and v4 protocols. Returns the number of + * Process bitmap data from semi-mt protocols. Returns the number of * fingers detected. A return value of 0 means at least one of the * bitmaps was empty. * @@ -396,9 +349,10 @@ static void alps_get_bitmap_points(unsigned int map, static int alps_process_bitmap(struct alps_data *priv, struct alps_fields *fields) { - int i, fingers_x = 0, fingers_y = 0, fingers; + int i, fingers_x = 0, fingers_y = 0, fingers, closest; struct alps_bitmap_point x_low = {0,}, x_high = {0,}; struct alps_bitmap_point y_low = {0,}, y_high = {0,}; + struct input_mt_pos corner[4]; if (!fields->x_map || !fields->y_map) return 0; @@ -429,26 +383,76 @@ static int alps_process_bitmap(struct alps_data *priv, y_high.num_bits = max(i, 1); } - fields->mt[0].x = + /* top-left corner */ + corner[0].x = (priv->x_max * (2 * x_low.start_bit + x_low.num_bits - 1)) / (2 * (priv->x_bits - 1)); - fields->mt[0].y = + corner[0].y = (priv->y_max * (2 * y_low.start_bit + y_low.num_bits - 1)) / (2 * (priv->y_bits - 1)); - fields->mt[1].x = + /* top-right corner */ + corner[1].x = (priv->x_max * (2 * x_high.start_bit + x_high.num_bits - 1)) / (2 * (priv->x_bits - 1)); - fields->mt[1].y = + corner[1].y = + (priv->y_max * (2 * y_low.start_bit + y_low.num_bits - 1)) / + (2 * (priv->y_bits - 1)); + + /* bottom-right corner */ + corner[2].x = + (priv->x_max * (2 * x_high.start_bit + x_high.num_bits - 1)) / + (2 * (priv->x_bits - 1)); + corner[2].y = + (priv->y_max * (2 * y_high.start_bit + y_high.num_bits - 1)) / + (2 * (priv->y_bits - 1)); + + /* bottom-left corner */ + corner[3].x = + (priv->x_max * (2 * x_low.start_bit + x_low.num_bits - 1)) / + (2 * (priv->x_bits - 1)); + corner[3].y = (priv->y_max * (2 * y_high.start_bit + y_high.num_bits - 1)) / (2 * (priv->y_bits - 1)); - /* y-bitmap order is reversed, except on rushmore */ - if (priv->proto_version != ALPS_PROTO_V3_RUSHMORE) { - fields->mt[0].y = priv->y_max - fields->mt[0].y; - fields->mt[1].y = priv->y_max - fields->mt[1].y; + /* x-bitmap order is reversed on v5 touchpads */ + if (priv->proto_version == ALPS_PROTO_V5) { + for (i = 0; i < 4; i++) + corner[i].x = priv->x_max - corner[i].x; + } + + /* y-bitmap order is reversed on v3 and v4 touchpads */ + if (priv->proto_version == ALPS_PROTO_V3 || + priv->proto_version == ALPS_PROTO_V4) { + for (i = 0; i < 4; i++) + corner[i].y = priv->y_max - corner[i].y; + } + + /* + * We only select a corner for the second touch once per 2 finger + * touch sequence to avoid the chosen corner (and thus the coordinates) + * jumping around when the first touch is in the middle. + */ + if (priv->second_touch == -1) { + /* Find corner closest to our st coordinates */ + closest = 0x7fffffff; + for (i = 0; i < 4; i++) { + int dx = fields->st.x - corner[i].x; + int dy = fields->st.y - corner[i].y; + int distance = dx * dx + dy * dy; + + if (distance < closest) { + priv->second_touch = i; + closest = distance; + } + } + /* And select the opposite corner to use for the 2nd touch */ + priv->second_touch = (priv->second_touch + 2) % 4; } + fields->mt[0] = fields->st; + fields->mt[1] = corner[priv->second_touch]; + return fingers; } @@ -485,9 +489,14 @@ static void alps_report_semi_mt_data(struct psmouse *psmouse, int fingers) f->mt[0].x = f->st.x; f->mt[0].y = f->st.y; fingers = f->pressure > 0 ? 1 : 0; + priv->second_touch = -1; } - alps_report_mt_data(psmouse, (fingers <= 2) ? fingers : 2); + if (fingers >= 1) + alps_set_slot(dev, 0, f->mt[0].x, f->mt[0].y); + if (fingers >= 2) + alps_set_slot(dev, 1, f->mt[1].x, f->mt[1].y); + input_mt_sync_frame(dev); input_mt_report_finger_count(dev, fingers); @@ -584,20 +593,22 @@ static int alps_decode_pinnacle(struct alps_fields *f, unsigned char *p, f->first_mp = !!(p[4] & 0x40); f->is_mp = !!(p[0] & 0x40); - f->fingers = (p[5] & 0x3) + 1; - f->x_map = ((p[4] & 0x7e) << 8) | - ((p[1] & 0x7f) << 2) | - ((p[0] & 0x30) >> 4); - f->y_map = ((p[3] & 0x70) << 4) | - ((p[2] & 0x7f) << 1) | - (p[4] & 0x01); - - f->st.x = ((p[1] & 0x7f) << 4) | ((p[4] & 0x30) >> 2) | - ((p[0] & 0x30) >> 4); - f->st.y = ((p[2] & 0x7f) << 4) | (p[4] & 0x0f); - f->pressure = p[5] & 0x7f; + if (f->is_mp) { + f->fingers = (p[5] & 0x3) + 1; + f->x_map = ((p[4] & 0x7e) << 8) | + ((p[1] & 0x7f) << 2) | + ((p[0] & 0x30) >> 4); + f->y_map = ((p[3] & 0x70) << 4) | + ((p[2] & 0x7f) << 1) | + (p[4] & 0x01); + } else { + f->st.x = ((p[1] & 0x7f) << 4) | ((p[4] & 0x30) >> 2) | + ((p[0] & 0x30) >> 4); + f->st.y = ((p[2] & 0x7f) << 4) | (p[4] & 0x0f); + f->pressure = p[5] & 0x7f; - alps_decode_buttons_v3(f, p); + alps_decode_buttons_v3(f, p); + } return 0; } @@ -605,13 +616,27 @@ static int alps_decode_pinnacle(struct alps_fields *f, unsigned char *p, static int alps_decode_rushmore(struct alps_fields *f, unsigned char *p, struct psmouse *psmouse) { - alps_decode_pinnacle(f, p, psmouse); - - /* Rushmore's packet decode has a bit difference with Pinnacle's */ + f->first_mp = !!(p[4] & 0x40); f->is_mp = !!(p[5] & 0x40); - f->fingers = max((p[5] & 0x3), ((p[5] >> 2) & 0x3)) + 1; - f->x_map |= (p[5] & 0x10) << 11; - f->y_map |= (p[5] & 0x20) << 6; + + if (f->is_mp) { + f->fingers = max((p[5] & 0x3), ((p[5] >> 2) & 0x3)) + 1; + f->x_map = ((p[5] & 0x10) << 11) | + ((p[4] & 0x7e) << 8) | + ((p[1] & 0x7f) << 2) | + ((p[0] & 0x30) >> 4); + f->y_map = ((p[5] & 0x20) << 6) | + ((p[3] & 0x70) << 4) | + ((p[2] & 0x7f) << 1) | + (p[4] & 0x01); + } else { + f->st.x = ((p[1] & 0x7f) << 4) | ((p[4] & 0x30) >> 2) | + ((p[0] & 0x30) >> 4); + f->st.y = ((p[2] & 0x7f) << 4) | (p[4] & 0x0f); + f->pressure = p[5] & 0x7f; + + alps_decode_buttons_v3(f, p); + } return 0; } @@ -680,30 +705,13 @@ static void alps_process_touchpad_packet_v3_v5(struct psmouse *psmouse) */ if (f->is_mp) { fingers = f->fingers; - if (priv->proto_version == ALPS_PROTO_V3 || - priv->proto_version == ALPS_PROTO_V3_RUSHMORE) { - if (alps_process_bitmap(priv, f) == 0) - fingers = 0; /* Use st data */ - - /* Now process position packet */ - priv->decode_fields(f, priv->multi_data, - psmouse); - } else { - /* - * Because Dolphin uses position packet's - * coordinate data as Pt1 and uses it to - * calculate Pt2, so we need to do position - * packet decode first. - */ - priv->decode_fields(f, priv->multi_data, - psmouse); - - /* - * Since Dolphin's finger number is reliable, - * there is no need to compare with bmap_fn. - */ - alps_process_bitmap_dolphin(priv, f); - } + /* + * Bitmap processing uses position packet's coordinate + * data, so we need to do decode it first. + */ + priv->decode_fields(f, priv->multi_data, psmouse); + if (alps_process_bitmap(priv, f) == 0) + fingers = 0; /* Use st data */ } else { priv->multi_packet = 0; } @@ -865,6 +873,14 @@ static void alps_process_packet_v4(struct psmouse *psmouse) priv->multi_data[offset] = packet[6]; priv->multi_data[offset + 1] = packet[7]; + f->left = !!(packet[4] & 0x01); + f->right = !!(packet[4] & 0x02); + + f->st.x = ((packet[1] & 0x7f) << 4) | ((packet[3] & 0x30) >> 2) | + ((packet[0] & 0x30) >> 4); + f->st.y = ((packet[2] & 0x7f) << 4) | (packet[3] & 0x0f); + f->pressure = packet[5] & 0x7f; + if (++priv->multi_packet > 2) { priv->multi_packet = 0; @@ -879,14 +895,6 @@ static void alps_process_packet_v4(struct psmouse *psmouse) f->fingers = alps_process_bitmap(priv, f); } - f->left = !!(packet[4] & 0x01); - f->right = !!(packet[4] & 0x02); - - f->st.x = ((packet[1] & 0x7f) << 4) | ((packet[3] & 0x30) >> 2) | - ((packet[0] & 0x30) >> 4); - f->st.y = ((packet[2] & 0x7f) << 4) | (packet[3] & 0x0f); - f->pressure = packet[5] & 0x7f; - alps_report_semi_mt_data(psmouse, f->fingers); } @@ -2561,7 +2569,7 @@ static int alps_set_protocol(struct psmouse *psmouse, case ALPS_PROTO_V3: priv->hw_init = alps_hw_init_v3; priv->process_packet = alps_process_packet_v3; - priv->set_abs_params = alps_set_abs_params_mt; + priv->set_abs_params = alps_set_abs_params_semi_mt; priv->decode_fields = alps_decode_pinnacle; priv->nibble_commands = alps_v3_nibble_commands; priv->addr_command = PSMOUSE_CMD_RESET_WRAP; @@ -2570,7 +2578,7 @@ static int alps_set_protocol(struct psmouse *psmouse, case ALPS_PROTO_V3_RUSHMORE: priv->hw_init = alps_hw_init_rushmore_v3; priv->process_packet = alps_process_packet_v3; - priv->set_abs_params = alps_set_abs_params_mt; + priv->set_abs_params = alps_set_abs_params_semi_mt; priv->decode_fields = alps_decode_rushmore; priv->nibble_commands = alps_v3_nibble_commands; priv->addr_command = PSMOUSE_CMD_RESET_WRAP; @@ -2586,7 +2594,7 @@ static int alps_set_protocol(struct psmouse *psmouse, case ALPS_PROTO_V4: priv->hw_init = alps_hw_init_v4; priv->process_packet = alps_process_packet_v4; - priv->set_abs_params = alps_set_abs_params_mt; + priv->set_abs_params = alps_set_abs_params_semi_mt; priv->nibble_commands = alps_v4_nibble_commands; priv->addr_command = PSMOUSE_CMD_DISABLE; break; @@ -2595,7 +2603,7 @@ static int alps_set_protocol(struct psmouse *psmouse, priv->hw_init = alps_hw_init_dolphin_v1; priv->process_packet = alps_process_touchpad_packet_v3_v5; priv->decode_fields = alps_decode_dolphin; - priv->set_abs_params = alps_set_abs_params_mt; + priv->set_abs_params = alps_set_abs_params_semi_mt; priv->nibble_commands = alps_v3_nibble_commands; priv->addr_command = PSMOUSE_CMD_RESET_WRAP; priv->x_bits = 23; @@ -2777,15 +2785,15 @@ static void alps_set_abs_params_mt_common(struct alps_data *priv, set_bit(BTN_TOOL_QUADTAP, dev1->keybit); } -static void alps_set_abs_params_mt(struct alps_data *priv, - struct input_dev *dev1) +static void alps_set_abs_params_semi_mt(struct alps_data *priv, + struct input_dev *dev1) { alps_set_abs_params_mt_common(priv, dev1); input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0); input_mt_init_slots(dev1, MAX_TOUCHES, INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED | - INPUT_MT_TRACK | INPUT_MT_SEMI_MT); + INPUT_MT_SEMI_MT); } static void alps_set_abs_params_v7(struct alps_data *priv, diff --git a/drivers/input/mouse/alps.h b/drivers/input/mouse/alps.h index 6dfdccc3a7c6..d37f814dc447 100644 --- a/drivers/input/mouse/alps.h +++ b/drivers/input/mouse/alps.h @@ -278,6 +278,7 @@ struct alps_data { int prev_fin; int multi_packet; + int second_touch; unsigned char multi_data[6]; struct alps_fields f; u8 quirks; diff --git a/drivers/input/mouse/cyapa_gen3.c b/drivers/input/mouse/cyapa_gen3.c index 1e2291c378fe..3faf01c1b191 100644 --- a/drivers/input/mouse/cyapa_gen3.c +++ b/drivers/input/mouse/cyapa_gen3.c @@ -950,14 +950,13 @@ static u16 cyapa_get_wait_time_for_pwr_cmd(u8 pwr_mode) * Device power mode can only be set when device is in operational mode. */ static int cyapa_gen3_set_power_mode(struct cyapa *cyapa, u8 power_mode, - u16 always_unused) + u16 always_unused) { int ret; u8 power; int tries; u16 sleep_time; - always_unused = 0; if (cyapa->state != CYAPA_STATE_OP) return 0; diff --git a/drivers/input/mouse/cyapa_gen5.c b/drivers/input/mouse/cyapa_gen5.c index 5b611dd71e79..afc39e799da2 100644 --- a/drivers/input/mouse/cyapa_gen5.c +++ b/drivers/input/mouse/cyapa_gen5.c @@ -352,7 +352,7 @@ struct gen5_app_cmd_head { u8 parameter_data[0]; /* Parameter data variable based on cmd_code */ } __packed; -/* Applicaton get/set parameter command data structure */ +/* Application get/set parameter command data structure */ struct gen5_app_set_parameter_data { u8 parameter_id; u8 parameter_size; @@ -832,7 +832,7 @@ static int gen5_hid_description_header_parse(struct cyapa *cyapa, u8 *reg_data) int ret; /* 0x20 0x00 0xF7 is Gen5 Application HID Description Header; - * 0x20 0x00 0xFF is Gen5 Booloader HID Description Header. + * 0x20 0x00 0xFF is Gen5 Bootloader HID Description Header. * * Must read HID Description content through out, * otherwise Gen5 trackpad cannot response next command @@ -1654,8 +1654,8 @@ static int cyapa_gen5_set_power_mode(struct cyapa *cyapa, * that trackpad unable to report signal to wake system up * in the special situation that system is in suspending, and * at the same time, user touch trackpad to wake system up. - * This function can avoid the data to be buffured when system - * is suspending which may cause interrput line unable to be + * This function can avoid the data to be buffered when system + * is suspending which may cause interrupt line unable to be * asserted again. */ cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL); @@ -2546,16 +2546,11 @@ static bool cyapa_gen5_irq_cmd_handler(struct cyapa *cyapa) gen5_pip->resp_sort_func(cyapa, gen5_pip->irq_cmd_buf, length))) { /* - * Cover the Gen5 V1 firmware issue. - * The issue is there is no interrut will be - * asserted to notityf host to read a command - * data out when always has finger touch on - * trackpad during the command is issued to - * trackad device. - * This issue has the scenario is that, - * user always has his fingers touched on - * trackpad device when booting/rebooting - * their chrome book. + * Work around the Gen5 V1 firmware + * that does not assert interrupt signalling + * that command response is ready if user + * keeps touching the trackpad while command + * is sent to the device. */ length = 0; if (gen5_pip->resp_len) diff --git a/drivers/input/mouse/elan_i2c.h b/drivers/input/mouse/elan_i2c.h index 6d5f8a4c1748..73670f2aebfd 100644 --- a/drivers/input/mouse/elan_i2c.h +++ b/drivers/input/mouse/elan_i2c.h @@ -28,14 +28,13 @@ #define ETP_PRESSURE_OFFSET 25 /* IAP Firmware handling */ -#define ETP_FW_NAME "elan_i2c.bin" +#define ETP_PRODUCT_ID_FORMAT_STRING "%d.0" +#define ETP_FW_NAME "elan_i2c_" ETP_PRODUCT_ID_FORMAT_STRING ".bin" #define ETP_IAP_START_ADDR 0x0083 #define ETP_FW_IAP_PAGE_ERR (1 << 5) #define ETP_FW_IAP_INTF_ERR (1 << 4) #define ETP_FW_PAGE_SIZE 64 -#define ETP_FW_VAILDPAGE_COUNT 768 #define ETP_FW_SIGNATURE_SIZE 6 -#define ETP_FW_SIGNATURE_ADDRESS 0xBFFA struct i2c_client; struct completion; @@ -58,7 +57,8 @@ struct elan_transport_ops { bool max_baseliune, u8 *value); int (*get_version)(struct i2c_client *client, bool iap, u8 *version); - int (*get_sm_version)(struct i2c_client *client, u8 *version); + int (*get_sm_version)(struct i2c_client *client, + u8* ic_type, u8 *version); int (*get_checksum)(struct i2c_client *client, bool iap, u16 *csum); int (*get_product_id)(struct i2c_client *client, u8 *id); diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c index fd5068b2542d..62641f2adaf7 100644 --- a/drivers/input/mouse/elan_i2c_core.c +++ b/drivers/input/mouse/elan_i2c_core.c @@ -4,7 +4,7 @@ * Copyright (c) 2013 ELAN Microelectronics Corp. * * Author: 林政維 (Duson Lin) <dusonlin@emc.com.tw> - * Version: 1.5.7 + * Version: 1.5.9 * * Based on cyapa driver: * copyright (c) 2011-2012 Cypress Semiconductor, Inc. @@ -40,7 +40,7 @@ #include "elan_i2c.h" #define DRIVER_NAME "elan_i2c" -#define ELAN_DRIVER_VERSION "1.5.7" +#define ELAN_DRIVER_VERSION "1.5.9" #define ETP_MAX_PRESSURE 255 #define ETP_FWIDTH_REDUCE 90 #define ETP_FINGER_WIDTH 15 @@ -83,6 +83,9 @@ struct elan_tp_data { u16 fw_checksum; int pressure_adjustment; u8 mode; + u8 ic_type; + u16 fw_vaildpage_count; + u16 fw_signature_address; bool irq_wake; @@ -91,6 +94,29 @@ struct elan_tp_data { bool baseline_ready; }; +static int elan_get_fwinfo(u8 ic_type, u16 *vaildpage_count, + u16 *signature_address) +{ + switch(ic_type) { + case 0x09: + *vaildpage_count = 768; + break; + case 0x0D: + *vaildpage_count = 896; + break; + default: + /* unknown ic type clear value */ + *vaildpage_count = 0; + *signature_address = 0; + return -ENXIO; + } + + *signature_address = + (*vaildpage_count * ETP_FW_PAGE_SIZE) - ETP_FW_SIGNATURE_SIZE; + + return 0; +} + static int elan_enable_power(struct elan_tp_data *data) { int repeat = ETP_RETRY_COUNT; @@ -221,7 +247,8 @@ static int elan_query_device_info(struct elan_tp_data *data) if (error) return error; - error = data->ops->get_sm_version(data->client, &data->sm_version); + error = data->ops->get_sm_version(data->client, &data->ic_type, + &data->sm_version); if (error) return error; @@ -234,6 +261,14 @@ static int elan_query_device_info(struct elan_tp_data *data) if (error) return error; + error = elan_get_fwinfo(data->ic_type, &data->fw_vaildpage_count, + &data->fw_signature_address); + if (error) { + dev_err(&data->client->dev, + "unknown ic type %d\n", data->ic_type); + return error; + } + return 0; } @@ -318,7 +353,7 @@ static int __elan_update_firmware(struct elan_tp_data *data, iap_start_addr = get_unaligned_le16(&fw->data[ETP_IAP_START_ADDR * 2]); boot_page_count = (iap_start_addr * 2) / ETP_FW_PAGE_SIZE; - for (i = boot_page_count; i < ETP_FW_VAILDPAGE_COUNT; i++) { + for (i = boot_page_count; i < data->fw_vaildpage_count; i++) { u16 checksum = 0; const u8 *page = &fw->data[i * ETP_FW_PAGE_SIZE]; @@ -403,7 +438,8 @@ static ssize_t elan_sysfs_read_product_id(struct device *dev, struct i2c_client *client = to_i2c_client(dev); struct elan_tp_data *data = i2c_get_clientdata(client); - return sprintf(buf, "%d.0\n", data->product_id); + return sprintf(buf, ETP_PRODUCT_ID_FORMAT_STRING "\n", + data->product_id); } static ssize_t elan_sysfs_read_fw_ver(struct device *dev, @@ -442,19 +478,28 @@ static ssize_t elan_sysfs_update_fw(struct device *dev, { struct elan_tp_data *data = dev_get_drvdata(dev); const struct firmware *fw; + char *fw_name; int error; const u8 *fw_signature; static const u8 signature[] = {0xAA, 0x55, 0xCC, 0x33, 0xFF, 0xFF}; - error = request_firmware(&fw, ETP_FW_NAME, dev); + /* Look for a firmware with the product id appended. */ + fw_name = kasprintf(GFP_KERNEL, ETP_FW_NAME, data->product_id); + if (!fw_name) { + dev_err(dev, "failed to allocate memory for firmware name\n"); + return -ENOMEM; + } + + dev_info(dev, "requesting fw '%s'\n", fw_name); + error = request_firmware(&fw, fw_name, dev); + kfree(fw_name); if (error) { - dev_err(dev, "cannot load firmware %s: %d\n", - ETP_FW_NAME, error); + dev_err(dev, "failed to request firmware: %d\n", error); return error; } /* Firmware file must match signature data */ - fw_signature = &fw->data[ETP_FW_SIGNATURE_ADDRESS]; + fw_signature = &fw->data[data->fw_signature_address]; if (memcmp(fw_signature, signature, sizeof(signature)) != 0) { dev_err(dev, "signature mismatch (expected %*ph, got %*ph)\n", (int)sizeof(signature), signature, diff --git a/drivers/input/mouse/elan_i2c_i2c.c b/drivers/input/mouse/elan_i2c_i2c.c index a0acbbf83bfd..683c840c9dd7 100644 --- a/drivers/input/mouse/elan_i2c_i2c.c +++ b/drivers/input/mouse/elan_i2c_i2c.c @@ -259,7 +259,8 @@ static int elan_i2c_get_version(struct i2c_client *client, return 0; } -static int elan_i2c_get_sm_version(struct i2c_client *client, u8 *version) +static int elan_i2c_get_sm_version(struct i2c_client *client, + u8 *ic_type, u8 *version) { int error; u8 val[3]; @@ -271,6 +272,7 @@ static int elan_i2c_get_sm_version(struct i2c_client *client, u8 *version) } *version = val[0]; + *ic_type = val[1]; return 0; } diff --git a/drivers/input/mouse/elan_i2c_smbus.c b/drivers/input/mouse/elan_i2c_smbus.c index 30ab80dbcdd6..ff36a366b2aa 100644 --- a/drivers/input/mouse/elan_i2c_smbus.c +++ b/drivers/input/mouse/elan_i2c_smbus.c @@ -165,7 +165,8 @@ static int elan_smbus_get_version(struct i2c_client *client, return 0; } -static int elan_smbus_get_sm_version(struct i2c_client *client, u8 *version) +static int elan_smbus_get_sm_version(struct i2c_client *client, + u8 *ic_type, u8 *version) { int error; u8 val[3]; @@ -177,7 +178,8 @@ static int elan_smbus_get_sm_version(struct i2c_client *client, u8 *version) return error; } - *version = val[0]; /* XXX Why 0 and not 2 as in IAP/FW versions? */ + *version = val[0]; + *ic_type = val[1]; return 0; } diff --git a/drivers/input/mouse/focaltech.c b/drivers/input/mouse/focaltech.c index 23d259416f2f..4d5576de81be 100644 --- a/drivers/input/mouse/focaltech.c +++ b/drivers/input/mouse/focaltech.c @@ -103,6 +103,16 @@ struct focaltech_hw_state { */ struct focaltech_finger_state fingers[FOC_MAX_FINGERS]; + /* + * Finger width 0-7 and 15 for a very big contact area. + * 15 value stays until the finger is released. + * Width is reported only in absolute packets. + * Since hardware reports width only for last touching finger, + * there is no need to store width for every specific finger, + * so we keep only last value reported. + */ + unsigned int width; + /* True if the clickpad has been pressed. */ bool pressed; }; @@ -137,6 +147,7 @@ static void focaltech_report_state(struct psmouse *psmouse) input_report_abs(dev, ABS_MT_POSITION_X, clamped_x); input_report_abs(dev, ABS_MT_POSITION_Y, priv->y_max - clamped_y); + input_report_abs(dev, ABS_TOOL_WIDTH, state->width); } } input_mt_report_pointer_emulation(dev, true); @@ -187,6 +198,7 @@ static void focaltech_process_abs_packet(struct psmouse *psmouse, state->fingers[finger].x = ((packet[1] & 0xf) << 8) | packet[2]; state->fingers[finger].y = (packet[3] << 8) | packet[4]; + state->width = packet[5] >> 4; state->fingers[finger].valid = true; } @@ -331,6 +343,7 @@ static void focaltech_set_input_params(struct psmouse *psmouse) __set_bit(EV_ABS, dev->evbit); input_set_abs_params(dev, ABS_MT_POSITION_X, 0, priv->x_max, 0, 0); input_set_abs_params(dev, ABS_MT_POSITION_Y, 0, priv->y_max, 0, 0); + input_set_abs_params(dev, ABS_TOOL_WIDTH, 0, 15, 0, 0); input_mt_init_slots(dev, 5, INPUT_MT_POINTER); __set_bit(INPUT_PROP_BUTTONPAD, dev->propbit); } diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index 5bb1658f60c7..7c4ba43d253e 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -63,7 +63,7 @@ static unsigned int psmouse_rate = 100; module_param_named(rate, psmouse_rate, uint, 0644); MODULE_PARM_DESC(rate, "Report rate, in reports per second."); -static bool psmouse_smartscroll = 1; +static bool psmouse_smartscroll = true; module_param_named(smartscroll, psmouse_smartscroll, bool, 0644); MODULE_PARM_DESC(smartscroll, "Logitech Smartscroll autorepeat, 1 = enabled (default), 0 = disabled."); diff --git a/drivers/input/mouse/sentelic.h b/drivers/input/mouse/sentelic.h index aa697ece405b..42df9e3beae8 100644 --- a/drivers/input/mouse/sentelic.h +++ b/drivers/input/mouse/sentelic.h @@ -123,11 +123,11 @@ struct fsp_data { extern int fsp_detect(struct psmouse *psmouse, bool set_properties); extern int fsp_init(struct psmouse *psmouse); #else -inline int fsp_detect(struct psmouse *psmouse, bool set_properties) +static inline int fsp_detect(struct psmouse *psmouse, bool set_properties) { return -ENOSYS; } -inline int fsp_init(struct psmouse *psmouse) +static inline int fsp_init(struct psmouse *psmouse) { return -ENOSYS; } diff --git a/drivers/input/mouse/synaptics_i2c.c b/drivers/input/mouse/synaptics_i2c.c index 878f18498f3b..ffceedcaf3c8 100644 --- a/drivers/input/mouse/synaptics_i2c.c +++ b/drivers/input/mouse/synaptics_i2c.c @@ -185,7 +185,7 @@ #define NO_DATA_SLEEP_MSECS (MSEC_PER_SEC / 4) /* Control touchpad's No Deceleration option */ -static bool no_decel = 1; +static bool no_decel = true; module_param(no_decel, bool, 0644); MODULE_PARM_DESC(no_decel, "No Deceleration. Default = 1 (on)"); @@ -340,9 +340,9 @@ static bool synaptics_i2c_get_input(struct synaptics_i2c *touch) s32 data; s8 x_delta, y_delta; - /* Deal with spontanious resets and errors */ + /* Deal with spontaneous resets and errors */ if (synaptics_i2c_check_error(touch->client)) - return 0; + return false; /* Get Gesture Bit */ data = synaptics_i2c_reg_get(touch->client, DATA_REG0); diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 80f6386709bf..7afa6a2b776c 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -958,6 +958,7 @@ config TOUCHSCREEN_ST1232 config TOUCHSCREEN_STMPE tristate "STMicroelectronics STMPE touchscreens" depends on MFD_STMPE + depends on (OF || COMPILE_TEST) help Say Y here if you want support for STMicroelectronics STMPE touchscreen controllers. diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 40b98dda8f38..dfc7309e3d38 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -726,15 +726,15 @@ static void mxt_input_button(struct mxt_data *data, u8 *message) { struct input_dev *input = data->input_dev; const struct mxt_platform_data *pdata = data->pdata; - bool button; int i; - /* Active-low switch */ for (i = 0; i < pdata->t19_num_keys; i++) { if (pdata->t19_keymap[i] == KEY_RESERVED) continue; - button = !(message[1] & (1 << i)); - input_report_key(input, pdata->t19_keymap[i], button); + + /* Active-low switch */ + input_report_key(input, pdata->t19_keymap[i], + !(message[1] & BIT(i))); } } diff --git a/drivers/input/touchscreen/cyttsp4_core.c b/drivers/input/touchscreen/cyttsp4_core.c index 568a3d340c8a..5ed31057430c 100644 --- a/drivers/input/touchscreen/cyttsp4_core.c +++ b/drivers/input/touchscreen/cyttsp4_core.c @@ -775,7 +775,6 @@ static void cyttsp4_get_touch(struct cyttsp4_mt_data *md, struct device *dev = &md->input->dev; struct cyttsp4_sysinfo *si = md->si; enum cyttsp4_tch_abs abs; - int tmp; bool flipped; for (abs = CY_TCH_X; abs < CY_TCH_NUM_ABS; abs++) { @@ -790,9 +789,7 @@ static void cyttsp4_get_touch(struct cyttsp4_mt_data *md, } if (md->pdata->flags & CY_FLAG_FLIP) { - tmp = touch->abs[CY_TCH_X]; - touch->abs[CY_TCH_X] = touch->abs[CY_TCH_Y]; - touch->abs[CY_TCH_Y] = tmp; + swap(touch->abs[CY_TCH_X], touch->abs[CY_TCH_Y]); flipped = true; } else flipped = false; diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c index 3af16984d57c..b4d12e29abff 100644 --- a/drivers/input/touchscreen/goodix.c +++ b/drivers/input/touchscreen/goodix.c @@ -47,7 +47,7 @@ struct goodix_ts_data { /* Register defines */ #define GOODIX_READ_COOR_ADDR 0x814E #define GOODIX_REG_CONFIG_DATA 0x8047 -#define GOODIX_REG_VERSION 0x8140 +#define GOODIX_REG_ID 0x8140 #define RESOLUTION_LOC 1 #define MAX_CONTACTS_LOC 5 @@ -69,7 +69,7 @@ static const unsigned long goodix_irq_flags[] = { * @len: length of the buffer to write */ static int goodix_i2c_read(struct i2c_client *client, - u16 reg, u8 *buf, int len) + u16 reg, u8 *buf, int len) { struct i2c_msg msgs[2]; u16 wbuf = cpu_to_be16(reg); @@ -78,7 +78,7 @@ static int goodix_i2c_read(struct i2c_client *client, msgs[0].flags = 0; msgs[0].addr = client->addr; msgs[0].len = 2; - msgs[0].buf = (u8 *) &wbuf; + msgs[0].buf = (u8 *)&wbuf; msgs[1].flags = I2C_M_RD; msgs[1].addr = client->addr; @@ -101,6 +101,9 @@ static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8 *data) return error; } + if (!(data[0] & 0x80)) + return -EAGAIN; + touch_num = data[0] & 0x0f; if (touch_num > ts->max_touch_num) return -EPROTO; @@ -144,7 +147,7 @@ static void goodix_ts_report_touch(struct goodix_ts_data *ts, u8 *coor_data) */ static void goodix_process_events(struct goodix_ts_data *ts) { - u8 point_data[1 + GOODIX_CONTACT_SIZE * ts->max_touch_num]; + u8 point_data[1 + GOODIX_CONTACT_SIZE * GOODIX_MAX_CONTACTS]; int touch_num; int i; @@ -196,8 +199,8 @@ static void goodix_read_config(struct goodix_ts_data *ts) int error; error = goodix_i2c_read(ts->client, GOODIX_REG_CONFIG_DATA, - config, - GOODIX_CONFIG_MAX_LENGTH); + config, + GOODIX_CONFIG_MAX_LENGTH); if (error) { dev_warn(&ts->client->dev, "Error reading config (%d), using defaults\n", @@ -227,22 +230,28 @@ static void goodix_read_config(struct goodix_ts_data *ts) * * @client: the i2c client * @version: output buffer containing the version on success + * @id: output buffer containing the id on success */ -static int goodix_read_version(struct i2c_client *client, u16 *version) +static int goodix_read_version(struct i2c_client *client, u16 *version, u16 *id) { int error; u8 buf[6]; + char id_str[5]; - error = goodix_i2c_read(client, GOODIX_REG_VERSION, buf, sizeof(buf)); + error = goodix_i2c_read(client, GOODIX_REG_ID, buf, sizeof(buf)); if (error) { dev_err(&client->dev, "read version failed: %d\n", error); return error; } - if (version) - *version = get_unaligned_le16(&buf[4]); + memcpy(id_str, buf, 4); + id_str[4] = 0; + if (kstrtou16(id_str, 10, id)) + *id = 0x1001; + + *version = get_unaligned_le16(&buf[4]); - dev_info(&client->dev, "IC VERSION: %6ph\n", buf); + dev_info(&client->dev, "ID %d, version: %04x\n", *id, *version); return 0; } @@ -276,10 +285,13 @@ static int goodix_i2c_test(struct i2c_client *client) * goodix_request_input_dev - Allocate, populate and register the input device * * @ts: our goodix_ts_data pointer + * @version: device firmware version + * @id: device ID * * Must be called during probe */ -static int goodix_request_input_dev(struct goodix_ts_data *ts) +static int goodix_request_input_dev(struct goodix_ts_data *ts, u16 version, + u16 id) { int error; @@ -289,14 +301,10 @@ static int goodix_request_input_dev(struct goodix_ts_data *ts) return -ENOMEM; } - ts->input_dev->evbit[0] = BIT_MASK(EV_SYN) | - BIT_MASK(EV_KEY) | - BIT_MASK(EV_ABS); - - input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, 0, - ts->abs_x_max, 0, 0); - input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, 0, - ts->abs_y_max, 0, 0); + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, + 0, ts->abs_x_max, 0, 0); + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, + 0, ts->abs_y_max, 0, 0); input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0); input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); @@ -307,8 +315,8 @@ static int goodix_request_input_dev(struct goodix_ts_data *ts) ts->input_dev->phys = "input/ts"; ts->input_dev->id.bustype = BUS_I2C; ts->input_dev->id.vendor = 0x0416; - ts->input_dev->id.product = 0x1001; - ts->input_dev->id.version = 10427; + ts->input_dev->id.product = id; + ts->input_dev->id.version = version; error = input_register_device(ts->input_dev); if (error) { @@ -326,7 +334,7 @@ static int goodix_ts_probe(struct i2c_client *client, struct goodix_ts_data *ts; unsigned long irq_flags; int error; - u16 version_info; + u16 version_info, id_info; dev_dbg(&client->dev, "I2C Address: 0x%02x\n", client->addr); @@ -348,7 +356,7 @@ static int goodix_ts_probe(struct i2c_client *client, return error; } - error = goodix_read_version(client, &version_info); + error = goodix_read_version(client, &version_info, &id_info); if (error) { dev_err(&client->dev, "Read version failed.\n"); return error; @@ -356,7 +364,7 @@ static int goodix_ts_probe(struct i2c_client *client, goodix_read_config(ts); - error = goodix_request_input_dev(ts); + error = goodix_request_input_dev(ts, version_info, id_info); if (error) return error; diff --git a/drivers/input/touchscreen/pixcir_i2c_ts.c b/drivers/input/touchscreen/pixcir_i2c_ts.c index 2c2107147319..8f3e243a62bf 100644 --- a/drivers/input/touchscreen/pixcir_i2c_ts.c +++ b/drivers/input/touchscreen/pixcir_i2c_ts.c @@ -78,7 +78,7 @@ static void pixcir_ts_parse(struct pixcir_i2c_ts_data *tsdata, } ret = i2c_master_recv(tsdata->client, rdbuf, readsize); - if (ret != sizeof(rdbuf)) { + if (ret != readsize) { dev_err(&tsdata->client->dev, "%s: i2c_master_recv failed(), ret=%d\n", __func__, ret); diff --git a/drivers/input/touchscreen/s3c2410_ts.c b/drivers/input/touchscreen/s3c2410_ts.c index bdfa27dc097b..a4a103e1d11b 100644 --- a/drivers/input/touchscreen/s3c2410_ts.c +++ b/drivers/input/touchscreen/s3c2410_ts.c @@ -411,7 +411,7 @@ static const struct dev_pm_ops s3c_ts_pmops = { }; #endif -static struct platform_device_id s3cts_driver_ids[] = { +static const struct platform_device_id s3cts_driver_ids[] = { { "s3c2410-ts", 0 }, { "s3c2440-ts", 0 }, { "s3c64xx-ts", FEAT_PEN_IRQ }, diff --git a/drivers/input/touchscreen/stmpe-ts.c b/drivers/input/touchscreen/stmpe-ts.c index e4c31256a74d..e414d43e5159 100644 --- a/drivers/input/touchscreen/stmpe-ts.c +++ b/drivers/input/touchscreen/stmpe-ts.c @@ -267,27 +267,10 @@ static void stmpe_ts_close(struct input_dev *dev) static void stmpe_ts_get_platform_info(struct platform_device *pdev, struct stmpe_touch *ts) { - struct stmpe *stmpe = dev_get_drvdata(pdev->dev.parent); struct device_node *np = pdev->dev.of_node; - struct stmpe_ts_platform_data *ts_pdata = NULL; - - ts->stmpe = stmpe; - - if (stmpe->pdata && stmpe->pdata->ts) { - ts_pdata = stmpe->pdata->ts; - - ts->sample_time = ts_pdata->sample_time; - ts->mod_12b = ts_pdata->mod_12b; - ts->ref_sel = ts_pdata->ref_sel; - ts->adc_freq = ts_pdata->adc_freq; - ts->ave_ctrl = ts_pdata->ave_ctrl; - ts->touch_det_delay = ts_pdata->touch_det_delay; - ts->settling = ts_pdata->settling; - ts->fraction_z = ts_pdata->fraction_z; - ts->i_drive = ts_pdata->i_drive; - } else if (np) { - u32 val; + u32 val; + if (np) { if (!of_property_read_u32(np, "st,sample-time", &val)) ts->sample_time = val; if (!of_property_read_u32(np, "st,mod-12b", &val)) @@ -311,6 +294,7 @@ static void stmpe_ts_get_platform_info(struct platform_device *pdev, static int stmpe_input_probe(struct platform_device *pdev) { + struct stmpe *stmpe = dev_get_drvdata(pdev->dev.parent); struct stmpe_touch *ts; struct input_dev *idev; int error; @@ -329,6 +313,7 @@ static int stmpe_input_probe(struct platform_device *pdev) return -ENOMEM; platform_set_drvdata(pdev, ts); + ts->stmpe = stmpe; ts->idev = idev; ts->dev = &pdev->dev; @@ -351,14 +336,13 @@ static int stmpe_input_probe(struct platform_device *pdev) idev->name = STMPE_TS_NAME; idev->phys = STMPE_TS_NAME"/input0"; idev->id.bustype = BUS_I2C; - idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); - idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); idev->open = stmpe_ts_open; idev->close = stmpe_ts_close; input_set_drvdata(idev, ts); + input_set_capability(idev, EV_KEY, BTN_TOUCH); input_set_abs_params(idev, ABS_X, 0, XY_MASK, 0, 0); input_set_abs_params(idev, ABS_Y, 0, XY_MASK, 0, 0); input_set_abs_params(idev, ABS_PRESSURE, 0x0, 0xff, 0, 0); @@ -383,14 +367,19 @@ static int stmpe_ts_remove(struct platform_device *pdev) static struct platform_driver stmpe_ts_driver = { .driver = { - .name = STMPE_TS_NAME, - }, + .name = STMPE_TS_NAME, + }, .probe = stmpe_input_probe, .remove = stmpe_ts_remove, }; module_platform_driver(stmpe_ts_driver); +static const struct of_device_id stmpe_ts_ids[] = { + { .compatible = "st,stmpe-ts", }, + { }, +}; +MODULE_DEVICE_TABLE(of, stmpe_ts_ids); + MODULE_AUTHOR("Luotao Fu <l.fu@pengutronix.de>"); MODULE_DESCRIPTION("STMPEXXX touchscreen driver"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:" STMPE_TS_NAME); diff --git a/drivers/input/touchscreen/zforce_ts.c b/drivers/input/touchscreen/zforce_ts.c index 19880c7385e3..f58a196521a9 100644 --- a/drivers/input/touchscreen/zforce_ts.c +++ b/drivers/input/touchscreen/zforce_ts.c @@ -30,7 +30,6 @@ #include <linux/input/mt.h> #include <linux/platform_data/zforce_ts.h> #include <linux/regulator/consumer.h> -#include <linux/delay.h> #include <linux/of.h> #include <linux/of_gpio.h> diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 966b9605f5f0..4191614c4651 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -11,9 +11,6 @@ menuconfig NEW_LEDS Say Y to enable Linux LED support. This allows control of supported LEDs from both userspace and optionally, by kernel events (triggers). - This is not related to standard keyboard LEDs which are controlled - via the input system. - if NEW_LEDS config LEDS_CLASS diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c index 843f2cdc280b..9ffdfcf2ec6e 100644 --- a/drivers/tty/sysrq.c +++ b/drivers/tty/sysrq.c @@ -55,9 +55,6 @@ static int __read_mostly sysrq_enabled = CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE; static bool __read_mostly sysrq_always_enabled; -unsigned short platform_sysrq_reset_seq[] __weak = { KEY_RESERVED }; -int sysrq_reset_downtime_ms __weak; - static bool sysrq_on(void) { return sysrq_enabled || sysrq_always_enabled; @@ -569,6 +566,7 @@ void handle_sysrq(int key) EXPORT_SYMBOL(handle_sysrq); #ifdef CONFIG_INPUT +static int sysrq_reset_downtime_ms; /* Simple translation table for the SysRq keys */ static const unsigned char sysrq_xlate[KEY_CNT] = @@ -949,23 +947,8 @@ static bool sysrq_handler_registered; static inline void sysrq_register_handler(void) { - unsigned short key; int error; - int i; - - /* First check if a __weak interface was instantiated. */ - for (i = 0; i < ARRAY_SIZE(sysrq_reset_seq); i++) { - key = platform_sysrq_reset_seq[i]; - if (key == KEY_RESERVED || key > KEY_MAX) - break; - - sysrq_reset_seq[sysrq_reset_seq_len++] = key; - } - /* - * DT configuration takes precedence over anything that would - * have been defined via the __weak interface. - */ sysrq_of_get_keyreset_config(); error = input_register_handler(&sysrq_handler); diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c index 8a89f6e7715d..6f0336fff501 100644 --- a/drivers/tty/vt/keyboard.c +++ b/drivers/tty/vt/keyboard.c @@ -33,6 +33,7 @@ #include <linux/string.h> #include <linux/init.h> #include <linux/slab.h> +#include <linux/leds.h> #include <linux/kbd_kern.h> #include <linux/kbd_diacr.h> @@ -129,7 +130,7 @@ static char rep; /* flag telling character repeat */ static int shift_state = 0; -static unsigned char ledstate = 0xff; /* undefined */ +static unsigned int ledstate = -1U; /* undefined */ static unsigned char ledioctl; /* @@ -961,6 +962,122 @@ static void k_brl(struct vc_data *vc, unsigned char value, char up_flag) } } +#if IS_ENABLED(CONFIG_INPUT_LEDS) && IS_ENABLED(CONFIG_LEDS_TRIGGERS) + +struct kbd_led_trigger { + struct led_trigger trigger; + unsigned int mask; +}; + +static void kbd_led_trigger_activate(struct led_classdev *cdev) +{ + struct kbd_led_trigger *trigger = + container_of(cdev->trigger, struct kbd_led_trigger, trigger); + + tasklet_disable(&keyboard_tasklet); + if (ledstate != -1U) + led_trigger_event(&trigger->trigger, + ledstate & trigger->mask ? + LED_FULL : LED_OFF); + tasklet_enable(&keyboard_tasklet); +} + +#define KBD_LED_TRIGGER(_led_bit, _name) { \ + .trigger = { \ + .name = _name, \ + .activate = kbd_led_trigger_activate, \ + }, \ + .mask = BIT(_led_bit), \ + } + +#define KBD_LOCKSTATE_TRIGGER(_led_bit, _name) \ + KBD_LED_TRIGGER((_led_bit) + 8, _name) + +static struct kbd_led_trigger kbd_led_triggers[] = { + KBD_LED_TRIGGER(VC_SCROLLOCK, "kbd-scrollock"), + KBD_LED_TRIGGER(VC_NUMLOCK, "kbd-numlock"), + KBD_LED_TRIGGER(VC_CAPSLOCK, "kbd-capslock"), + KBD_LED_TRIGGER(VC_KANALOCK, "kbd-kanalock"), + + KBD_LOCKSTATE_TRIGGER(VC_SHIFTLOCK, "kbd-shiftlock"), + KBD_LOCKSTATE_TRIGGER(VC_ALTGRLOCK, "kbd-altgrlock"), + KBD_LOCKSTATE_TRIGGER(VC_CTRLLOCK, "kbd-ctrllock"), + KBD_LOCKSTATE_TRIGGER(VC_ALTLOCK, "kbd-altlock"), + KBD_LOCKSTATE_TRIGGER(VC_SHIFTLLOCK, "kbd-shiftllock"), + KBD_LOCKSTATE_TRIGGER(VC_SHIFTRLOCK, "kbd-shiftrlock"), + KBD_LOCKSTATE_TRIGGER(VC_CTRLLLOCK, "kbd-ctrlllock"), + KBD_LOCKSTATE_TRIGGER(VC_CTRLRLOCK, "kbd-ctrlrlock"), +}; + +static void kbd_propagate_led_state(unsigned int old_state, + unsigned int new_state) +{ + struct kbd_led_trigger *trigger; + unsigned int changed = old_state ^ new_state; + int i; + + for (i = 0; i < ARRAY_SIZE(kbd_led_triggers); i++) { + trigger = &kbd_led_triggers[i]; + + if (changed & trigger->mask) + led_trigger_event(&trigger->trigger, + new_state & trigger->mask ? + LED_FULL : LED_OFF); + } +} + +static int kbd_update_leds_helper(struct input_handle *handle, void *data) +{ + unsigned int led_state = *(unsigned int *)data; + + if (test_bit(EV_LED, handle->dev->evbit)) + kbd_propagate_led_state(~led_state, led_state); + + return 0; +} + +static void kbd_init_leds(void) +{ + int error; + int i; + + for (i = 0; i < ARRAY_SIZE(kbd_led_triggers); i++) { + error = led_trigger_register(&kbd_led_triggers[i].trigger); + if (error) + pr_err("error %d while registering trigger %s\n", + error, kbd_led_triggers[i].trigger.name); + } +} + +#else + +static int kbd_update_leds_helper(struct input_handle *handle, void *data) +{ + unsigned int leds = *(unsigned int *)data; + + if (test_bit(EV_LED, handle->dev->evbit)) { + input_inject_event(handle, EV_LED, LED_SCROLLL, !!(leds & 0x01)); + input_inject_event(handle, EV_LED, LED_NUML, !!(leds & 0x02)); + input_inject_event(handle, EV_LED, LED_CAPSL, !!(leds & 0x04)); + input_inject_event(handle, EV_SYN, SYN_REPORT, 0); + } + + return 0; +} + +static void kbd_propagate_led_state(unsigned int old_state, + unsigned int new_state) +{ + input_handler_for_each_handle(&kbd_handler, &new_state, + kbd_update_leds_helper); +} + +static void kbd_init_leds(void) +{ +} + +#endif + /* * The leds display either (i) the status of NumLock, CapsLock, ScrollLock, * or (ii) whatever pattern of lights people want to show using KDSETLED, @@ -968,7 +1085,7 @@ static void k_brl(struct vc_data *vc, unsigned char value, char up_flag) */ static unsigned char getledstate(void) { - return ledstate; + return ledstate & 0xff; } void setledstate(struct kbd_struct *kb, unsigned int led) @@ -995,20 +1112,6 @@ static inline unsigned char getleds(void) return kb->ledflagstate; } -static int kbd_update_leds_helper(struct input_handle *handle, void *data) -{ - unsigned char leds = *(unsigned char *)data; - - if (test_bit(EV_LED, handle->dev->evbit)) { - input_inject_event(handle, EV_LED, LED_SCROLLL, !!(leds & 0x01)); - input_inject_event(handle, EV_LED, LED_NUML, !!(leds & 0x02)); - input_inject_event(handle, EV_LED, LED_CAPSL, !!(leds & 0x04)); - input_inject_event(handle, EV_SYN, SYN_REPORT, 0); - } - - return 0; -} - /** * vt_get_leds - helper for braille console * @console: console to read @@ -1085,24 +1188,23 @@ void vt_kbd_con_stop(int console) } /* - * This is the tasklet that updates LED state on all keyboards - * attached to the box. The reason we use tasklet is that we - * need to handle the scenario when keyboard handler is not - * registered yet but we already getting updates from the VT to - * update led state. + * This is the tasklet that updates LED state of LEDs using standard + * keyboard triggers. The reason we use tasklet is that we need to + * handle the scenario when keyboard handler is not registered yet + * but we already getting updates from the VT to update led state. */ static void kbd_bh(unsigned long dummy) { - unsigned char leds; + unsigned int leds; unsigned long flags; - + spin_lock_irqsave(&led_lock, flags); leds = getleds(); + leds |= (unsigned int)kbd->lockstate << 8; spin_unlock_irqrestore(&led_lock, flags); if (leds != ledstate) { - input_handler_for_each_handle(&kbd_handler, &leds, - kbd_update_leds_helper); + kbd_propagate_led_state(ledstate, leds); ledstate = leds; } } @@ -1450,7 +1552,7 @@ static void kbd_start(struct input_handle *handle) { tasklet_disable(&keyboard_tasklet); - if (ledstate != 0xff) + if (ledstate != -1U) kbd_update_leds_helper(handle, &ledstate); tasklet_enable(&keyboard_tasklet); @@ -1497,6 +1599,8 @@ int __init kbd_init(void) kbd_table[i].kbdmode = default_utf8 ? VC_UNICODE : VC_XLATE; } + kbd_init_leds(); + error = input_register_handler(&kbd_handler); if (error) return error; |