Commit b8c82b6a authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input

Pull input fixes from Dmitry Torokhov:

 - tweaks to Elan drivers (both PS/2 and I2C) to support new devices.
   Also revert of one of IDs as that device should really be driven by
   i2c-hid + hid-multitouch

 - a few drivers have been switched to set_brightness_blocking() call
   because they either were sleeping the their set_brightness()
   implementation or used workqueue but were not canceling it on unbind.

 - ps2-gpio and matrix_keypad needed to [properly] flush their works to
   avoid potential use-after-free on unbind.

 - other miscellaneous fixes.

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input:
  Input: elan_i2c - add ACPI ID for touchpad in Lenovo V330-15ISK
  Input: st-keyscan - fix potential zalloc NULL dereference
  Input: apanel - switch to using brightness_set_blocking()
  Revert "Input: elan_i2c - add ACPI ID for touchpad in ASUS Aspire F5-573G"
  Input: qt2160 - switch to using brightness_set_blocking()
  Input: matrix_keypad - use flush_delayed_work()
  Input: ps2-gpio - flush TX work when closing port
  Input: cap11xx - switch to using set_brightness_blocking()
  Input: elantech - enable 3rd button support on Fujitsu CELSIUS H780
  Input: bma150 - register input device after setting private data
  Input: pwm-vibra - stop regulator after disabling pwm, not before
  Input: pwm-vibra - prevent unbalanced regulator
  Input: snvs_pwrkey - allow selecting driver for i.MX 7D
parents ed0a0ec9 7ad222b3
...@@ -420,7 +420,7 @@ config KEYBOARD_MPR121 ...@@ -420,7 +420,7 @@ config KEYBOARD_MPR121
config KEYBOARD_SNVS_PWRKEY config KEYBOARD_SNVS_PWRKEY
tristate "IMX SNVS Power Key Driver" tristate "IMX SNVS Power Key Driver"
depends on SOC_IMX6SX depends on SOC_IMX6SX || SOC_IMX7D
depends on OF depends on OF
help help
This is the snvs powerkey driver for the Freescale i.MX application This is the snvs powerkey driver for the Freescale i.MX application
......
...@@ -75,9 +75,7 @@ ...@@ -75,9 +75,7 @@
struct cap11xx_led { struct cap11xx_led {
struct cap11xx_priv *priv; struct cap11xx_priv *priv;
struct led_classdev cdev; struct led_classdev cdev;
struct work_struct work;
u32 reg; u32 reg;
enum led_brightness new_brightness;
}; };
#endif #endif
...@@ -233,30 +231,21 @@ static void cap11xx_input_close(struct input_dev *idev) ...@@ -233,30 +231,21 @@ static void cap11xx_input_close(struct input_dev *idev)
} }
#ifdef CONFIG_LEDS_CLASS #ifdef CONFIG_LEDS_CLASS
static void cap11xx_led_work(struct work_struct *work) static int cap11xx_led_set(struct led_classdev *cdev,
enum led_brightness value)
{ {
struct cap11xx_led *led = container_of(work, struct cap11xx_led, work); struct cap11xx_led *led = container_of(cdev, struct cap11xx_led, cdev);
struct cap11xx_priv *priv = led->priv; struct cap11xx_priv *priv = led->priv;
int value = led->new_brightness;
/* /*
* All LEDs share the same duty cycle as this is a HW limitation. * All LEDs share the same duty cycle as this is a HW
* Brightness levels per LED are either 0 (OFF) and 1 (ON). * limitation. Brightness levels per LED are either
* 0 (OFF) and 1 (ON).
*/ */
regmap_update_bits(priv->regmap, CAP11XX_REG_LED_OUTPUT_CONTROL, return regmap_update_bits(priv->regmap,
BIT(led->reg), value ? BIT(led->reg) : 0); CAP11XX_REG_LED_OUTPUT_CONTROL,
} BIT(led->reg),
value ? BIT(led->reg) : 0);
static void cap11xx_led_set(struct led_classdev *cdev,
enum led_brightness value)
{
struct cap11xx_led *led = container_of(cdev, struct cap11xx_led, cdev);
if (led->new_brightness == value)
return;
led->new_brightness = value;
schedule_work(&led->work);
} }
static int cap11xx_init_leds(struct device *dev, static int cap11xx_init_leds(struct device *dev,
...@@ -299,7 +288,7 @@ static int cap11xx_init_leds(struct device *dev, ...@@ -299,7 +288,7 @@ static int cap11xx_init_leds(struct device *dev,
led->cdev.default_trigger = led->cdev.default_trigger =
of_get_property(child, "linux,default-trigger", NULL); of_get_property(child, "linux,default-trigger", NULL);
led->cdev.flags = 0; led->cdev.flags = 0;
led->cdev.brightness_set = cap11xx_led_set; led->cdev.brightness_set_blocking = cap11xx_led_set;
led->cdev.max_brightness = 1; led->cdev.max_brightness = 1;
led->cdev.brightness = LED_OFF; led->cdev.brightness = LED_OFF;
...@@ -312,8 +301,6 @@ static int cap11xx_init_leds(struct device *dev, ...@@ -312,8 +301,6 @@ static int cap11xx_init_leds(struct device *dev,
led->reg = reg; led->reg = reg;
led->priv = priv; led->priv = priv;
INIT_WORK(&led->work, cap11xx_led_work);
error = devm_led_classdev_register(dev, &led->cdev); error = devm_led_classdev_register(dev, &led->cdev);
if (error) { if (error) {
of_node_put(child); of_node_put(child);
......
...@@ -222,7 +222,7 @@ static void matrix_keypad_stop(struct input_dev *dev) ...@@ -222,7 +222,7 @@ static void matrix_keypad_stop(struct input_dev *dev)
keypad->stopped = true; keypad->stopped = true;
spin_unlock_irq(&keypad->lock); spin_unlock_irq(&keypad->lock);
flush_work(&keypad->work.work); flush_delayed_work(&keypad->work);
/* /*
* matrix_keypad_scan() will leave IRQs enabled; * matrix_keypad_scan() will leave IRQs enabled;
* we should disable them now. * we should disable them now.
......
...@@ -58,10 +58,9 @@ static unsigned char qt2160_key2code[] = { ...@@ -58,10 +58,9 @@ static unsigned char qt2160_key2code[] = {
struct qt2160_led { struct qt2160_led {
struct qt2160_data *qt2160; struct qt2160_data *qt2160;
struct led_classdev cdev; struct led_classdev cdev;
struct work_struct work;
char name[32]; char name[32];
int id; int id;
enum led_brightness new_brightness; enum led_brightness brightness;
}; };
#endif #endif
...@@ -74,7 +73,6 @@ struct qt2160_data { ...@@ -74,7 +73,6 @@ struct qt2160_data {
u16 key_matrix; u16 key_matrix;
#ifdef CONFIG_LEDS_CLASS #ifdef CONFIG_LEDS_CLASS
struct qt2160_led leds[QT2160_NUM_LEDS_X]; struct qt2160_led leds[QT2160_NUM_LEDS_X];
struct mutex led_lock;
#endif #endif
}; };
...@@ -83,46 +81,39 @@ static int qt2160_write(struct i2c_client *client, u8 reg, u8 data); ...@@ -83,46 +81,39 @@ static int qt2160_write(struct i2c_client *client, u8 reg, u8 data);
#ifdef CONFIG_LEDS_CLASS #ifdef CONFIG_LEDS_CLASS
static void qt2160_led_work(struct work_struct *work) static int qt2160_led_set(struct led_classdev *cdev,
enum led_brightness value)
{ {
struct qt2160_led *led = container_of(work, struct qt2160_led, work); struct qt2160_led *led = container_of(cdev, struct qt2160_led, cdev);
struct qt2160_data *qt2160 = led->qt2160; struct qt2160_data *qt2160 = led->qt2160;
struct i2c_client *client = qt2160->client; struct i2c_client *client = qt2160->client;
int value = led->new_brightness;
u32 drive, pwmen; u32 drive, pwmen;
mutex_lock(&qt2160->led_lock); if (value != led->brightness) {
drive = qt2160_read(client, QT2160_CMD_DRIVE_X);
drive = qt2160_read(client, QT2160_CMD_DRIVE_X); pwmen = qt2160_read(client, QT2160_CMD_PWMEN_X);
pwmen = qt2160_read(client, QT2160_CMD_PWMEN_X); if (value != LED_OFF) {
if (value != LED_OFF) { drive |= BIT(led->id);
drive |= (1 << led->id); pwmen |= BIT(led->id);
pwmen |= (1 << led->id);
} else {
drive &= ~(1 << led->id);
pwmen &= ~(1 << led->id);
}
qt2160_write(client, QT2160_CMD_DRIVE_X, drive);
qt2160_write(client, QT2160_CMD_PWMEN_X, pwmen);
/* } else {
* Changing this register will change the brightness drive &= ~BIT(led->id);
* of every LED in the qt2160. It's a HW limitation. pwmen &= ~BIT(led->id);
*/ }
if (value != LED_OFF) qt2160_write(client, QT2160_CMD_DRIVE_X, drive);
qt2160_write(client, QT2160_CMD_PWM_DUTY, value); qt2160_write(client, QT2160_CMD_PWMEN_X, pwmen);
mutex_unlock(&qt2160->led_lock); /*
} * Changing this register will change the brightness
* of every LED in the qt2160. It's a HW limitation.
*/
if (value != LED_OFF)
qt2160_write(client, QT2160_CMD_PWM_DUTY, value);
static void qt2160_led_set(struct led_classdev *cdev, led->brightness = value;
enum led_brightness value) }
{
struct qt2160_led *led = container_of(cdev, struct qt2160_led, cdev);
led->new_brightness = value; return 0;
schedule_work(&led->work);
} }
#endif /* CONFIG_LEDS_CLASS */ #endif /* CONFIG_LEDS_CLASS */
...@@ -293,20 +284,16 @@ static int qt2160_register_leds(struct qt2160_data *qt2160) ...@@ -293,20 +284,16 @@ static int qt2160_register_leds(struct qt2160_data *qt2160)
int ret; int ret;
int i; int i;
mutex_init(&qt2160->led_lock);
for (i = 0; i < QT2160_NUM_LEDS_X; i++) { for (i = 0; i < QT2160_NUM_LEDS_X; i++) {
struct qt2160_led *led = &qt2160->leds[i]; struct qt2160_led *led = &qt2160->leds[i];
snprintf(led->name, sizeof(led->name), "qt2160:x%d", i); snprintf(led->name, sizeof(led->name), "qt2160:x%d", i);
led->cdev.name = led->name; led->cdev.name = led->name;
led->cdev.brightness_set = qt2160_led_set; led->cdev.brightness_set_blocking = qt2160_led_set;
led->cdev.brightness = LED_OFF; led->cdev.brightness = LED_OFF;
led->id = i; led->id = i;
led->qt2160 = qt2160; led->qt2160 = qt2160;
INIT_WORK(&led->work, qt2160_led_work);
ret = led_classdev_register(&client->dev, &led->cdev); ret = led_classdev_register(&client->dev, &led->cdev);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -324,10 +311,8 @@ static void qt2160_unregister_leds(struct qt2160_data *qt2160) ...@@ -324,10 +311,8 @@ static void qt2160_unregister_leds(struct qt2160_data *qt2160)
{ {
int i; int i;
for (i = 0; i < QT2160_NUM_LEDS_X; i++) { for (i = 0; i < QT2160_NUM_LEDS_X; i++)
led_classdev_unregister(&qt2160->leds[i].cdev); led_classdev_unregister(&qt2160->leds[i].cdev);
cancel_work_sync(&qt2160->leds[i].work);
}
} }
#else #else
......
...@@ -153,6 +153,8 @@ static int keyscan_probe(struct platform_device *pdev) ...@@ -153,6 +153,8 @@ static int keyscan_probe(struct platform_device *pdev)
input_dev->id.bustype = BUS_HOST; input_dev->id.bustype = BUS_HOST;
keypad_data->input_dev = input_dev;
error = keypad_matrix_key_parse_dt(keypad_data); error = keypad_matrix_key_parse_dt(keypad_data);
if (error) if (error)
return error; return error;
...@@ -168,8 +170,6 @@ static int keyscan_probe(struct platform_device *pdev) ...@@ -168,8 +170,6 @@ static int keyscan_probe(struct platform_device *pdev)
input_set_drvdata(input_dev, keypad_data); input_set_drvdata(input_dev, keypad_data);
keypad_data->input_dev = input_dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
keypad_data->base = devm_ioremap_resource(&pdev->dev, res); keypad_data->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(keypad_data->base)) if (IS_ERR(keypad_data->base))
......
...@@ -22,7 +22,6 @@ ...@@ -22,7 +22,6 @@
#include <linux/io.h> #include <linux/io.h>
#include <linux/input-polldev.h> #include <linux/input-polldev.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/workqueue.h>
#include <linux/leds.h> #include <linux/leds.h>
#define APANEL_NAME "Fujitsu Application Panel" #define APANEL_NAME "Fujitsu Application Panel"
...@@ -59,8 +58,6 @@ struct apanel { ...@@ -59,8 +58,6 @@ struct apanel {
struct i2c_client *client; struct i2c_client *client;
unsigned short keymap[MAX_PANEL_KEYS]; unsigned short keymap[MAX_PANEL_KEYS];
u16 nkeys; u16 nkeys;
u16 led_bits;
struct work_struct led_work;
struct led_classdev mail_led; struct led_classdev mail_led;
}; };
...@@ -109,25 +106,13 @@ static void apanel_poll(struct input_polled_dev *ipdev) ...@@ -109,25 +106,13 @@ static void apanel_poll(struct input_polled_dev *ipdev)
report_key(idev, ap->keymap[i]); report_key(idev, ap->keymap[i]);
} }
/* Track state changes of LED */ static int mail_led_set(struct led_classdev *led,
static void led_update(struct work_struct *work)
{
struct apanel *ap = container_of(work, struct apanel, led_work);
i2c_smbus_write_word_data(ap->client, 0x10, ap->led_bits);
}
static void mail_led_set(struct led_classdev *led,
enum led_brightness value) enum led_brightness value)
{ {
struct apanel *ap = container_of(led, struct apanel, mail_led); struct apanel *ap = container_of(led, struct apanel, mail_led);
u16 led_bits = value != LED_OFF ? 0x8000 : 0x0000;
if (value != LED_OFF) return i2c_smbus_write_word_data(ap->client, 0x10, led_bits);
ap->led_bits |= 0x8000;
else
ap->led_bits &= ~0x8000;
schedule_work(&ap->led_work);
} }
static int apanel_remove(struct i2c_client *client) static int apanel_remove(struct i2c_client *client)
...@@ -179,7 +164,7 @@ static struct apanel apanel = { ...@@ -179,7 +164,7 @@ static struct apanel apanel = {
}, },
.mail_led = { .mail_led = {
.name = "mail:blue", .name = "mail:blue",
.brightness_set = mail_led_set, .brightness_set_blocking = mail_led_set,
}, },
}; };
...@@ -235,7 +220,6 @@ static int apanel_probe(struct i2c_client *client, ...@@ -235,7 +220,6 @@ static int apanel_probe(struct i2c_client *client,
if (err) if (err)
goto out3; goto out3;
INIT_WORK(&ap->led_work, led_update);
if (device_chip[APANEL_DEV_LED] != CHIP_NONE) { if (device_chip[APANEL_DEV_LED] != CHIP_NONE) {
err = led_classdev_register(&client->dev, &ap->mail_led); err = led_classdev_register(&client->dev, &ap->mail_led);
if (err) if (err)
......
...@@ -481,13 +481,14 @@ static int bma150_register_input_device(struct bma150_data *bma150) ...@@ -481,13 +481,14 @@ static int bma150_register_input_device(struct bma150_data *bma150)
idev->close = bma150_irq_close; idev->close = bma150_irq_close;
input_set_drvdata(idev, bma150); input_set_drvdata(idev, bma150);
bma150->input = idev;
error = input_register_device(idev); error = input_register_device(idev);
if (error) { if (error) {
input_free_device(idev); input_free_device(idev);
return error; return error;
} }
bma150->input = idev;
return 0; return 0;
} }
...@@ -510,15 +511,15 @@ static int bma150_register_polled_device(struct bma150_data *bma150) ...@@ -510,15 +511,15 @@ static int bma150_register_polled_device(struct bma150_data *bma150)
bma150_init_input_device(bma150, ipoll_dev->input); bma150_init_input_device(bma150, ipoll_dev->input);
bma150->input_polled = ipoll_dev;
bma150->input = ipoll_dev->input;
error = input_register_polled_device(ipoll_dev); error = input_register_polled_device(ipoll_dev);
if (error) { if (error) {
input_free_polled_device(ipoll_dev); input_free_polled_device(ipoll_dev);
return error; return error;
} }
bma150->input_polled = ipoll_dev;
bma150->input = ipoll_dev->input;
return 0; return 0;
} }
......
...@@ -34,6 +34,7 @@ struct pwm_vibrator { ...@@ -34,6 +34,7 @@ struct pwm_vibrator {
struct work_struct play_work; struct work_struct play_work;
u16 level; u16 level;
u32 direction_duty_cycle; u32 direction_duty_cycle;
bool vcc_on;
}; };
static int pwm_vibrator_start(struct pwm_vibrator *vibrator) static int pwm_vibrator_start(struct pwm_vibrator *vibrator)
...@@ -42,10 +43,13 @@ static int pwm_vibrator_start(struct pwm_vibrator *vibrator) ...@@ -42,10 +43,13 @@ static int pwm_vibrator_start(struct pwm_vibrator *vibrator)
struct pwm_state state; struct pwm_state state;
int err; int err;
err = regulator_enable(vibrator->vcc); if (!vibrator->vcc_on) {
if (err) { err = regulator_enable(vibrator->vcc);
dev_err(pdev, "failed to enable regulator: %d", err); if (err) {
return err; dev_err(pdev, "failed to enable regulator: %d", err);
return err;
}
vibrator->vcc_on = true;
} }
pwm_get_state(vibrator->pwm, &state); pwm_get_state(vibrator->pwm, &state);
...@@ -76,11 +80,14 @@ static int pwm_vibrator_start(struct pwm_vibrator *vibrator) ...@@ -76,11 +80,14 @@ static int pwm_vibrator_start(struct pwm_vibrator *vibrator)
static void pwm_vibrator_stop(struct pwm_vibrator *vibrator) static void pwm_vibrator_stop(struct pwm_vibrator *vibrator)
{ {
regulator_disable(vibrator->vcc);
if (vibrator->pwm_dir) if (vibrator->pwm_dir)
pwm_disable(vibrator->pwm_dir); pwm_disable(vibrator->pwm_dir);
pwm_disable(vibrator->pwm); pwm_disable(vibrator->pwm);
if (vibrator->vcc_on) {
regulator_disable(vibrator->vcc);
vibrator->vcc_on = false;
}
} }
static void pwm_vibrator_play_work(struct work_struct *work) static void pwm_vibrator_play_work(struct work_struct *work)
......
...@@ -1336,7 +1336,6 @@ MODULE_DEVICE_TABLE(i2c, elan_id); ...@@ -1336,7 +1336,6 @@ MODULE_DEVICE_TABLE(i2c, elan_id);
static const struct acpi_device_id elan_acpi_id[] = { static const struct acpi_device_id elan_acpi_id[] = {
{ "ELAN0000", 0 }, { "ELAN0000", 0 },
{ "ELAN0100", 0 }, { "ELAN0100", 0 },
{ "ELAN0501", 0 },
{ "ELAN0600", 0 }, { "ELAN0600", 0 },
{ "ELAN0602", 0 }, { "ELAN0602", 0 },
{ "ELAN0605", 0 }, { "ELAN0605", 0 },
...@@ -1346,6 +1345,7 @@ static const struct acpi_device_id elan_acpi_id[] = { ...@@ -1346,6 +1345,7 @@ static const struct acpi_device_id elan_acpi_id[] = {
{ "ELAN060C", 0 }, { "ELAN060C", 0 },
{ "ELAN0611", 0 }, { "ELAN0611", 0 },
{ "ELAN0612", 0 }, { "ELAN0612", 0 },
{ "ELAN0617", 0 },
{ "ELAN0618", 0 }, { "ELAN0618", 0 },
{ "ELAN061C", 0 }, { "ELAN061C", 0 },
{ "ELAN061D", 0 }, { "ELAN061D", 0 },
......
...@@ -1119,6 +1119,8 @@ static int elantech_get_resolution_v4(struct psmouse *psmouse, ...@@ -1119,6 +1119,8 @@ static int elantech_get_resolution_v4(struct psmouse *psmouse,
* Asus UX31 0x361f00 20, 15, 0e clickpad * Asus UX31 0x361f00 20, 15, 0e clickpad
* Asus UX32VD 0x361f02 00, 15, 0e clickpad * Asus UX32VD 0x361f02 00, 15, 0e clickpad
* Avatar AVIU-145A2 0x361f00 ? clickpad * Avatar AVIU-145A2 0x361f00 ? clickpad
* Fujitsu CELSIUS H760 0x570f02 40, 14, 0c 3 hw buttons (**)
* Fujitsu CELSIUS H780 0x5d0f02 41, 16, 0d 3 hw buttons (**)
* Fujitsu LIFEBOOK E544 0x470f00 d0, 12, 09 2 hw buttons * Fujitsu LIFEBOOK E544 0x470f00 d0, 12, 09 2 hw buttons
* Fujitsu LIFEBOOK E546 0x470f00 50, 12, 09 2 hw buttons * Fujitsu LIFEBOOK E546 0x470f00 50, 12, 09 2 hw buttons
* Fujitsu LIFEBOOK E547 0x470f00 50, 12, 09 2 hw buttons * Fujitsu LIFEBOOK E547 0x470f00 50, 12, 09 2 hw buttons
...@@ -1171,6 +1173,13 @@ static const struct dmi_system_id elantech_dmi_has_middle_button[] = { ...@@ -1171,6 +1173,13 @@ static const struct dmi_system_id elantech_dmi_has_middle_button[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "CELSIUS H760"), DMI_MATCH(DMI_PRODUCT_NAME, "CELSIUS H760"),
}, },
}, },
{
/* Fujitsu H780 also has a middle button */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
DMI_MATCH(DMI_PRODUCT_NAME, "CELSIUS H780"),
},
},
#endif #endif
{ } { }
}; };
......
...@@ -76,6 +76,7 @@ static void ps2_gpio_close(struct serio *serio) ...@@ -76,6 +76,7 @@ static void ps2_gpio_close(struct serio *serio)
{ {
struct ps2_gpio_data *drvdata = serio->port_data; struct ps2_gpio_data *drvdata = serio->port_data;
flush_delayed_work(&drvdata->tx_work);
disable_irq(drvdata->irq); disable_irq(drvdata->irq);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment