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
This commit is contained in:
commit
b8c82b6a3a
|
@ -420,7 +420,7 @@ config KEYBOARD_MPR121
|
|||
|
||||
config KEYBOARD_SNVS_PWRKEY
|
||||
tristate "IMX SNVS Power Key Driver"
|
||||
depends on SOC_IMX6SX
|
||||
depends on SOC_IMX6SX || SOC_IMX7D
|
||||
depends on OF
|
||||
help
|
||||
This is the snvs powerkey driver for the Freescale i.MX application
|
||||
|
|
|
@ -75,9 +75,7 @@
|
|||
struct cap11xx_led {
|
||||
struct cap11xx_priv *priv;
|
||||
struct led_classdev cdev;
|
||||
struct work_struct work;
|
||||
u32 reg;
|
||||
enum led_brightness new_brightness;
|
||||
};
|
||||
#endif
|
||||
|
||||
|
@ -233,30 +231,21 @@ static void cap11xx_input_close(struct input_dev *idev)
|
|||
}
|
||||
|
||||
#ifdef CONFIG_LEDS_CLASS
|
||||
static void cap11xx_led_work(struct work_struct *work)
|
||||
{
|
||||
struct cap11xx_led *led = container_of(work, struct cap11xx_led, work);
|
||||
struct cap11xx_priv *priv = led->priv;
|
||||
int value = led->new_brightness;
|
||||
|
||||
/*
|
||||
* All LEDs share the same duty cycle as this is a HW limitation.
|
||||
* Brightness levels per LED are either 0 (OFF) and 1 (ON).
|
||||
*/
|
||||
regmap_update_bits(priv->regmap, 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)
|
||||
static int cap11xx_led_set(struct led_classdev *cdev,
|
||||
enum led_brightness value)
|
||||
{
|
||||
struct cap11xx_led *led = container_of(cdev, struct cap11xx_led, cdev);
|
||||
struct cap11xx_priv *priv = led->priv;
|
||||
|
||||
if (led->new_brightness == value)
|
||||
return;
|
||||
|
||||
led->new_brightness = value;
|
||||
schedule_work(&led->work);
|
||||
/*
|
||||
* All LEDs share the same duty cycle as this is a HW
|
||||
* limitation. Brightness levels per LED are either
|
||||
* 0 (OFF) and 1 (ON).
|
||||
*/
|
||||
return regmap_update_bits(priv->regmap,
|
||||
CAP11XX_REG_LED_OUTPUT_CONTROL,
|
||||
BIT(led->reg),
|
||||
value ? BIT(led->reg) : 0);
|
||||
}
|
||||
|
||||
static int cap11xx_init_leds(struct device *dev,
|
||||
|
@ -299,7 +288,7 @@ static int cap11xx_init_leds(struct device *dev,
|
|||
led->cdev.default_trigger =
|
||||
of_get_property(child, "linux,default-trigger", NULL);
|
||||
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.brightness = LED_OFF;
|
||||
|
||||
|
@ -312,8 +301,6 @@ static int cap11xx_init_leds(struct device *dev,
|
|||
led->reg = reg;
|
||||
led->priv = priv;
|
||||
|
||||
INIT_WORK(&led->work, cap11xx_led_work);
|
||||
|
||||
error = devm_led_classdev_register(dev, &led->cdev);
|
||||
if (error) {
|
||||
of_node_put(child);
|
||||
|
|
|
@ -222,7 +222,7 @@ static void matrix_keypad_stop(struct input_dev *dev)
|
|||
keypad->stopped = true;
|
||||
spin_unlock_irq(&keypad->lock);
|
||||
|
||||
flush_work(&keypad->work.work);
|
||||
flush_delayed_work(&keypad->work);
|
||||
/*
|
||||
* matrix_keypad_scan() will leave IRQs enabled;
|
||||
* we should disable them now.
|
||||
|
|
|
@ -58,10 +58,9 @@ static unsigned char qt2160_key2code[] = {
|
|||
struct qt2160_led {
|
||||
struct qt2160_data *qt2160;
|
||||
struct led_classdev cdev;
|
||||
struct work_struct work;
|
||||
char name[32];
|
||||
int id;
|
||||
enum led_brightness new_brightness;
|
||||
enum led_brightness brightness;
|
||||
};
|
||||
#endif
|
||||
|
||||
|
@ -74,7 +73,6 @@ struct qt2160_data {
|
|||
u16 key_matrix;
|
||||
#ifdef CONFIG_LEDS_CLASS
|
||||
struct qt2160_led leds[QT2160_NUM_LEDS_X];
|
||||
struct mutex led_lock;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
@ -83,46 +81,39 @@ static int qt2160_write(struct i2c_client *client, u8 reg, u8 data);
|
|||
|
||||
#ifdef CONFIG_LEDS_CLASS
|
||||
|
||||
static void qt2160_led_work(struct work_struct *work)
|
||||
{
|
||||
struct qt2160_led *led = container_of(work, struct qt2160_led, work);
|
||||
struct qt2160_data *qt2160 = led->qt2160;
|
||||
struct i2c_client *client = qt2160->client;
|
||||
int value = led->new_brightness;
|
||||
u32 drive, pwmen;
|
||||
|
||||
mutex_lock(&qt2160->led_lock);
|
||||
|
||||
drive = qt2160_read(client, QT2160_CMD_DRIVE_X);
|
||||
pwmen = qt2160_read(client, QT2160_CMD_PWMEN_X);
|
||||
if (value != LED_OFF) {
|
||||
drive |= (1 << 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);
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
||||
mutex_unlock(&qt2160->led_lock);
|
||||
}
|
||||
|
||||
static void qt2160_led_set(struct led_classdev *cdev,
|
||||
enum led_brightness value)
|
||||
static int qt2160_led_set(struct led_classdev *cdev,
|
||||
enum led_brightness value)
|
||||
{
|
||||
struct qt2160_led *led = container_of(cdev, struct qt2160_led, cdev);
|
||||
struct qt2160_data *qt2160 = led->qt2160;
|
||||
struct i2c_client *client = qt2160->client;
|
||||
u32 drive, pwmen;
|
||||
|
||||
led->new_brightness = value;
|
||||
schedule_work(&led->work);
|
||||
if (value != led->brightness) {
|
||||
drive = qt2160_read(client, QT2160_CMD_DRIVE_X);
|
||||
pwmen = qt2160_read(client, QT2160_CMD_PWMEN_X);
|
||||
if (value != LED_OFF) {
|
||||
drive |= BIT(led->id);
|
||||
pwmen |= BIT(led->id);
|
||||
|
||||
} else {
|
||||
drive &= ~BIT(led->id);
|
||||
pwmen &= ~BIT(led->id);
|
||||
}
|
||||
qt2160_write(client, QT2160_CMD_DRIVE_X, drive);
|
||||
qt2160_write(client, QT2160_CMD_PWMEN_X, pwmen);
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
||||
led->brightness = value;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_LEDS_CLASS */
|
||||
|
@ -293,20 +284,16 @@ static int qt2160_register_leds(struct qt2160_data *qt2160)
|
|||
int ret;
|
||||
int i;
|
||||
|
||||
mutex_init(&qt2160->led_lock);
|
||||
|
||||
for (i = 0; i < QT2160_NUM_LEDS_X; i++) {
|
||||
struct qt2160_led *led = &qt2160->leds[i];
|
||||
|
||||
snprintf(led->name, sizeof(led->name), "qt2160:x%d", i);
|
||||
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->id = i;
|
||||
led->qt2160 = qt2160;
|
||||
|
||||
INIT_WORK(&led->work, qt2160_led_work);
|
||||
|
||||
ret = led_classdev_register(&client->dev, &led->cdev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
@ -324,10 +311,8 @@ static void qt2160_unregister_leds(struct qt2160_data *qt2160)
|
|||
{
|
||||
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);
|
||||
cancel_work_sync(&qt2160->leds[i].work);
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
|
|
@ -153,6 +153,8 @@ static int keyscan_probe(struct platform_device *pdev)
|
|||
|
||||
input_dev->id.bustype = BUS_HOST;
|
||||
|
||||
keypad_data->input_dev = input_dev;
|
||||
|
||||
error = keypad_matrix_key_parse_dt(keypad_data);
|
||||
if (error)
|
||||
return error;
|
||||
|
@ -168,8 +170,6 @@ static int keyscan_probe(struct platform_device *pdev)
|
|||
|
||||
input_set_drvdata(input_dev, keypad_data);
|
||||
|
||||
keypad_data->input_dev = input_dev;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
keypad_data->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(keypad_data->base))
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
#include <linux/io.h>
|
||||
#include <linux/input-polldev.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/leds.h>
|
||||
|
||||
#define APANEL_NAME "Fujitsu Application Panel"
|
||||
|
@ -59,8 +58,6 @@ struct apanel {
|
|||
struct i2c_client *client;
|
||||
unsigned short keymap[MAX_PANEL_KEYS];
|
||||
u16 nkeys;
|
||||
u16 led_bits;
|
||||
struct work_struct led_work;
|
||||
struct led_classdev mail_led;
|
||||
};
|
||||
|
||||
|
@ -109,25 +106,13 @@ static void apanel_poll(struct input_polled_dev *ipdev)
|
|||
report_key(idev, ap->keymap[i]);
|
||||
}
|
||||
|
||||
/* Track state changes of 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,
|
||||
static int mail_led_set(struct led_classdev *led,
|
||||
enum led_brightness value)
|
||||
{
|
||||
struct apanel *ap = container_of(led, struct apanel, mail_led);
|
||||
u16 led_bits = value != LED_OFF ? 0x8000 : 0x0000;
|
||||
|
||||
if (value != LED_OFF)
|
||||
ap->led_bits |= 0x8000;
|
||||
else
|
||||
ap->led_bits &= ~0x8000;
|
||||
|
||||
schedule_work(&ap->led_work);
|
||||
return i2c_smbus_write_word_data(ap->client, 0x10, led_bits);
|
||||
}
|
||||
|
||||
static int apanel_remove(struct i2c_client *client)
|
||||
|
@ -179,7 +164,7 @@ static struct apanel apanel = {
|
|||
},
|
||||
.mail_led = {
|
||||
.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,
|
|||
if (err)
|
||||
goto out3;
|
||||
|
||||
INIT_WORK(&ap->led_work, led_update);
|
||||
if (device_chip[APANEL_DEV_LED] != CHIP_NONE) {
|
||||
err = led_classdev_register(&client->dev, &ap->mail_led);
|
||||
if (err)
|
||||
|
|
|
@ -481,13 +481,14 @@ static int bma150_register_input_device(struct bma150_data *bma150)
|
|||
idev->close = bma150_irq_close;
|
||||
input_set_drvdata(idev, bma150);
|
||||
|
||||
bma150->input = idev;
|
||||
|
||||
error = input_register_device(idev);
|
||||
if (error) {
|
||||
input_free_device(idev);
|
||||
return error;
|
||||
}
|
||||
|
||||
bma150->input = idev;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -510,15 +511,15 @@ static int bma150_register_polled_device(struct bma150_data *bma150)
|
|||
|
||||
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);
|
||||
if (error) {
|
||||
input_free_polled_device(ipoll_dev);
|
||||
return error;
|
||||
}
|
||||
|
||||
bma150->input_polled = ipoll_dev;
|
||||
bma150->input = ipoll_dev->input;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ struct pwm_vibrator {
|
|||
struct work_struct play_work;
|
||||
u16 level;
|
||||
u32 direction_duty_cycle;
|
||||
bool vcc_on;
|
||||
};
|
||||
|
||||
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;
|
||||
int err;
|
||||
|
||||
err = regulator_enable(vibrator->vcc);
|
||||
if (err) {
|
||||
dev_err(pdev, "failed to enable regulator: %d", err);
|
||||
return err;
|
||||
if (!vibrator->vcc_on) {
|
||||
err = regulator_enable(vibrator->vcc);
|
||||
if (err) {
|
||||
dev_err(pdev, "failed to enable regulator: %d", err);
|
||||
return err;
|
||||
}
|
||||
vibrator->vcc_on = true;
|
||||
}
|
||||
|
||||
pwm_get_state(vibrator->pwm, &state);
|
||||
|
@ -76,11 +80,14 @@ static int pwm_vibrator_start(struct pwm_vibrator *vibrator)
|
|||
|
||||
static void pwm_vibrator_stop(struct pwm_vibrator *vibrator)
|
||||
{
|
||||
regulator_disable(vibrator->vcc);
|
||||
|
||||
if (vibrator->pwm_dir)
|
||||
pwm_disable(vibrator->pwm_dir);
|
||||
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)
|
||||
|
|
|
@ -1336,7 +1336,6 @@ MODULE_DEVICE_TABLE(i2c, elan_id);
|
|||
static const struct acpi_device_id elan_acpi_id[] = {
|
||||
{ "ELAN0000", 0 },
|
||||
{ "ELAN0100", 0 },
|
||||
{ "ELAN0501", 0 },
|
||||
{ "ELAN0600", 0 },
|
||||
{ "ELAN0602", 0 },
|
||||
{ "ELAN0605", 0 },
|
||||
|
@ -1346,6 +1345,7 @@ static const struct acpi_device_id elan_acpi_id[] = {
|
|||
{ "ELAN060C", 0 },
|
||||
{ "ELAN0611", 0 },
|
||||
{ "ELAN0612", 0 },
|
||||
{ "ELAN0617", 0 },
|
||||
{ "ELAN0618", 0 },
|
||||
{ "ELAN061C", 0 },
|
||||
{ "ELAN061D", 0 },
|
||||
|
|
|
@ -1119,6 +1119,8 @@ static int elantech_get_resolution_v4(struct psmouse *psmouse,
|
|||
* Asus UX31 0x361f00 20, 15, 0e clickpad
|
||||
* Asus UX32VD 0x361f02 00, 15, 0e 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 E546 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[] = {
|
|||
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
|
||||
{ }
|
||||
};
|
||||
|
|
|
@ -76,6 +76,7 @@ static void ps2_gpio_close(struct serio *serio)
|
|||
{
|
||||
struct ps2_gpio_data *drvdata = serio->port_data;
|
||||
|
||||
flush_delayed_work(&drvdata->tx_work);
|
||||
disable_irq(drvdata->irq);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue