diff options
Diffstat (limited to 'drivers')
41 files changed, 1922 insertions, 959 deletions
diff --git a/drivers/input/gameport/gameport.c b/drivers/input/gameport/gameport.c index 4a2a9e370be7..092cc4188b57 100644 --- a/drivers/input/gameport/gameport.c +++ b/drivers/input/gameport/gameport.c @@ -385,8 +385,8 @@ static int gameport_queue_event(void *object, struct module *owner, } if (!try_module_get(owner)) { - pr_warning("Can't get module reference, dropping event %d\n", - event_type); + pr_warn("Can't get module reference, dropping event %d\n", + event_type); kfree(event); retval = -EINVAL; goto out; diff --git a/drivers/input/joystick/gamecon.c b/drivers/input/joystick/gamecon.c index eae14d512353..c43f087a496d 100644 --- a/drivers/input/joystick/gamecon.c +++ b/drivers/input/joystick/gamecon.c @@ -870,7 +870,8 @@ static int gc_setup_pad(struct gc *gc, int idx, int pad_type) err = gc_n64_init_ff(input_dev, idx); if (err) { - pr_warning("Failed to initiate rumble for N64 device %d\n", idx); + pr_warn("Failed to initiate rumble for N64 device %d\n", + idx); goto err_free_dev; } diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c index 6a250d65f8fe..c7a8120b13c0 100644 --- a/drivers/input/keyboard/cros_ec_keyb.c +++ b/drivers/input/keyboard/cros_ec_keyb.c @@ -30,6 +30,7 @@ #include <linux/notifier.h> #include <linux/platform_device.h> #include <linux/slab.h> +#include <linux/sysrq.h> #include <linux/input/matrix_keypad.h> #include <linux/mfd/cros_ec.h> #include <linux/mfd/cros_ec_commands.h> @@ -260,6 +261,12 @@ static int cros_ec_keyb_work(struct notifier_block *nb, ckdev->ec->event_size); break; + case EC_MKBP_EVENT_SYSRQ: + val = get_unaligned_le32(&ckdev->ec->event_data.data.sysrq); + dev_dbg(ckdev->dev, "sysrq code from EC: %#x\n", val); + handle_sysrq(val); + break; + case EC_MKBP_EVENT_BUTTON: case EC_MKBP_EVENT_SWITCH: /* diff --git a/drivers/input/keyboard/matrix_keypad.c b/drivers/input/keyboard/matrix_keypad.c index 18839cd5f76e..1f316d66e6f7 100644 --- a/drivers/input/keyboard/matrix_keypad.c +++ b/drivers/input/keyboard/matrix_keypad.c @@ -42,9 +42,10 @@ struct matrix_keypad { }; /* - * NOTE: normally the GPIO has to be put into HiZ when de-activated to cause - * minmal side effect when scanning other columns, here it is configured to - * be input, and it should work on most platforms. + * NOTE: If drive_inactive_cols is false, then the GPIO has to be put into + * HiZ when de-activated to cause minmal side effect when scanning other + * columns. In that case it is configured here to be input, otherwise it is + * driven with the inactive value. */ static void __activate_col(const struct matrix_keypad_platform_data *pdata, int col, bool on) @@ -55,7 +56,8 @@ static void __activate_col(const struct matrix_keypad_platform_data *pdata, gpio_direction_output(pdata->col_gpios[col], level_on); } else { gpio_set_value_cansleep(pdata->col_gpios[col], !level_on); - gpio_direction_input(pdata->col_gpios[col]); + if (!pdata->drive_inactive_cols) + gpio_direction_input(pdata->col_gpios[col]); } } @@ -432,6 +434,9 @@ matrix_keypad_parse_dt(struct device *dev) if (of_get_property(np, "gpio-activelow", NULL)) pdata->active_low = true; + pdata->drive_inactive_cols = + of_property_read_bool(np, "drive-inactive-cols"); + of_property_read_u32(np, "debounce-delay-ms", &pdata->debounce_ms); of_property_read_u32(np, "col-scan-delay-us", &pdata->col_scan_delay_us); diff --git a/drivers/input/keyboard/qt1070.c b/drivers/input/keyboard/qt1070.c index 5a5778729e37..76bb51309a78 100644 --- a/drivers/input/keyboard/qt1070.c +++ b/drivers/input/keyboard/qt1070.c @@ -274,9 +274,18 @@ static const struct i2c_device_id qt1070_id[] = { }; MODULE_DEVICE_TABLE(i2c, qt1070_id); +#ifdef CONFIG_OF +static const struct of_device_id qt1070_of_match[] = { + { .compatible = "qt1070", }, + { }, +}; +MODULE_DEVICE_TABLE(of, qt1070_of_match); +#endif + static struct i2c_driver qt1070_driver = { .driver = { .name = "qt1070", + .of_match_table = of_match_ptr(qt1070_of_match), .pm = &qt1070_pm_ops, }, .id_table = qt1070_id, diff --git a/drivers/input/keyboard/tca8418_keypad.c b/drivers/input/keyboard/tca8418_keypad.c index 44dd7689c571..e37e335e406f 100644 --- a/drivers/input/keyboard/tca8418_keypad.c +++ b/drivers/input/keyboard/tca8418_keypad.c @@ -188,8 +188,6 @@ static void tca8418_read_keypad(struct tca8418_keypad *keypad_data) input_event(input, EV_MSC, MSC_SCAN, code); input_report_key(input, keymap[code], state); - /* Read for next loop */ - error = tca8418_read_byte(keypad_data, REG_KEY_EVENT_A, ®); } while (1); input_sync(input); diff --git a/drivers/input/misc/apanel.c b/drivers/input/misc/apanel.c index 53630afab606..aad1df04c854 100644 --- a/drivers/input/misc/apanel.c +++ b/drivers/input/misc/apanel.c @@ -314,7 +314,8 @@ static int __init apanel_init(void) if (devno >= APANEL_DEV_MAX) pr_notice(APANEL ": unknown device %u found\n", devno); else if (device_chip[devno] != CHIP_NONE) - pr_warning(APANEL ": duplicate entry for devno %u\n", devno); + pr_warn(APANEL ": duplicate entry for devno %u\n", + devno); else if (method != 1 && method != 2 && method != 4) { pr_notice(APANEL ": unknown method %u for devno %u\n", diff --git a/drivers/input/misc/axp20x-pek.c b/drivers/input/misc/axp20x-pek.c index 1ac898db303a..f11807db6979 100644 --- a/drivers/input/misc/axp20x-pek.c +++ b/drivers/input/misc/axp20x-pek.c @@ -13,6 +13,7 @@ * GNU General Public License for more details. */ +#include <linux/acpi.h> #include <linux/errno.h> #include <linux/irq.h> #include <linux/init.h> @@ -188,21 +189,13 @@ static void axp20x_remove_sysfs_group(void *_data) sysfs_remove_group(&dev->kobj, &axp20x_attribute_group); } -static int axp20x_pek_probe(struct platform_device *pdev) +static int axp20x_pek_probe_input_device(struct axp20x_pek *axp20x_pek, + struct platform_device *pdev) { - struct axp20x_pek *axp20x_pek; - struct axp20x_dev *axp20x; + struct axp20x_dev *axp20x = axp20x_pek->axp20x; struct input_dev *idev; int error; - axp20x_pek = devm_kzalloc(&pdev->dev, sizeof(struct axp20x_pek), - GFP_KERNEL); - if (!axp20x_pek) - return -ENOMEM; - - axp20x_pek->axp20x = dev_get_drvdata(pdev->dev.parent); - axp20x = axp20x_pek->axp20x; - axp20x_pek->irq_dbr = platform_get_irq_byname(pdev, "PEK_DBR"); if (axp20x_pek->irq_dbr < 0) { dev_err(&pdev->dev, "No IRQ for PEK_DBR, error=%d\n", @@ -239,7 +232,7 @@ static int axp20x_pek_probe(struct platform_device *pdev) axp20x_pek_irq, 0, "axp20x-pek-dbr", idev); if (error < 0) { - dev_err(axp20x->dev, "Failed to request dbr IRQ#%d: %d\n", + dev_err(&pdev->dev, "Failed to request dbr IRQ#%d: %d\n", axp20x_pek->irq_dbr, error); return error; } @@ -248,30 +241,57 @@ static int axp20x_pek_probe(struct platform_device *pdev) axp20x_pek_irq, 0, "axp20x-pek-dbf", idev); if (error < 0) { - dev_err(axp20x->dev, "Failed to request dbf IRQ#%d: %d\n", + dev_err(&pdev->dev, "Failed to request dbf IRQ#%d: %d\n", axp20x_pek->irq_dbf, error); return error; } - error = sysfs_create_group(&pdev->dev.kobj, &axp20x_attribute_group); + error = input_register_device(idev); if (error) { - dev_err(axp20x->dev, "Failed to create sysfs attributes: %d\n", + dev_err(&pdev->dev, "Can't register input device: %d\n", error); return error; } - error = devm_add_action(&pdev->dev, - axp20x_remove_sysfs_group, &pdev->dev); + return 0; +} + +static int axp20x_pek_probe(struct platform_device *pdev) +{ + struct axp20x_pek *axp20x_pek; + int error; + + axp20x_pek = devm_kzalloc(&pdev->dev, sizeof(struct axp20x_pek), + GFP_KERNEL); + if (!axp20x_pek) + return -ENOMEM; + + axp20x_pek->axp20x = dev_get_drvdata(pdev->dev.parent); + + /* + * Do not register the input device if there is an "INTCFD9" + * gpio button ACPI device, that handles the power button too, + * and otherwise we end up reporting all presses twice. + */ + if (!acpi_dev_found("INTCFD9") || + !IS_ENABLED(CONFIG_INPUT_SOC_BUTTON_ARRAY)) { + error = axp20x_pek_probe_input_device(axp20x_pek, pdev); + if (error) + return error; + } + + error = sysfs_create_group(&pdev->dev.kobj, &axp20x_attribute_group); if (error) { - axp20x_remove_sysfs_group(&pdev->dev); - dev_err(&pdev->dev, "Failed to add sysfs cleanup action: %d\n", + dev_err(&pdev->dev, "Failed to create sysfs attributes: %d\n", error); return error; } - error = input_register_device(idev); + error = devm_add_action(&pdev->dev, + axp20x_remove_sysfs_group, &pdev->dev); if (error) { - dev_err(axp20x->dev, "Can't register input device: %d\n", + axp20x_remove_sysfs_group(&pdev->dev); + dev_err(&pdev->dev, "Failed to add sysfs cleanup action: %d\n", error); return error; } diff --git a/drivers/input/misc/bma150.c b/drivers/input/misc/bma150.c index 1fa85379f86c..1efcfdf9f8a8 100644 --- a/drivers/input/misc/bma150.c +++ b/drivers/input/misc/bma150.c @@ -70,7 +70,6 @@ #define BMA150_CFG_5_REG 0x11 #define BMA150_CHIP_ID 2 -#define BMA180_CHIP_ID 3 #define BMA150_CHIP_ID_REG BMA150_DATA_0_REG #define BMA150_ACC_X_LSB_REG BMA150_DATA_2_REG @@ -538,13 +537,8 @@ static int bma150_probe(struct i2c_client *client, return -EIO; } - /* - * Note if the IIO CONFIG_BMA180 driver is enabled we want to fail - * the probe for the bma180 as the iio driver is preferred. - */ chip_id = i2c_smbus_read_byte_data(client, BMA150_CHIP_ID_REG); - if (chip_id != BMA150_CHIP_ID && - (IS_ENABLED(CONFIG_BMA180) || chip_id != BMA180_CHIP_ID)) { + if (chip_id != BMA150_CHIP_ID) { dev_err(&client->dev, "BMA150 chip id error: %d\n", chip_id); return -EINVAL; } @@ -648,9 +642,6 @@ static UNIVERSAL_DEV_PM_OPS(bma150_pm, bma150_suspend, bma150_resume, NULL); static const struct i2c_device_id bma150_id[] = { { "bma150", 0 }, -#if !IS_ENABLED(CONFIG_BMA180) - { "bma180", 0 }, -#endif { "smb380", 0 }, { "bma023", 0 }, { } diff --git a/drivers/input/misc/dm355evm_keys.c b/drivers/input/misc/dm355evm_keys.c index 82e272ebc0ed..bab256ef32b9 100644 --- a/drivers/input/misc/dm355evm_keys.c +++ b/drivers/input/misc/dm355evm_keys.c @@ -32,7 +32,6 @@ struct dm355evm_keys { struct input_dev *input; struct device *dev; - int irq; }; /* These initial keycodes can be remapped */ @@ -176,74 +175,49 @@ static int dm355evm_keys_probe(struct platform_device *pdev) { struct dm355evm_keys *keys; struct input_dev *input; - int status; + int irq; + int error; - /* allocate instance struct and input dev */ - keys = kzalloc(sizeof *keys, GFP_KERNEL); - input = input_allocate_device(); - if (!keys || !input) { - status = -ENOMEM; - goto fail1; - } + keys = devm_kzalloc(&pdev->dev, sizeof (*keys), GFP_KERNEL); + if (!keys) + return -ENOMEM; + + input = devm_input_allocate_device(&pdev->dev); + if (!input) + return -ENOMEM; keys->dev = &pdev->dev; keys->input = input; - /* set up "threaded IRQ handler" */ - status = platform_get_irq(pdev, 0); - if (status < 0) - goto fail1; - keys->irq = status; - input->name = "DM355 EVM Controls"; input->phys = "dm355evm/input0"; - input->dev.parent = &pdev->dev; input->id.bustype = BUS_I2C; input->id.product = 0x0355; input->id.version = dm355evm_msp_read(DM355EVM_MSP_FIRMREV); - status = sparse_keymap_setup(input, dm355evm_keys, NULL); - if (status) - goto fail1; + error = sparse_keymap_setup(input, dm355evm_keys, NULL); + if (error) + return error; /* REVISIT: flush the event queue? */ - status = request_threaded_irq(keys->irq, NULL, dm355evm_keys_irq, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - dev_name(&pdev->dev), keys); - if (status < 0) - goto fail2; - - /* register */ - status = input_register_device(input); - if (status < 0) - goto fail3; - - platform_set_drvdata(pdev, keys); - - return 0; - -fail3: - free_irq(keys->irq, keys); -fail2: - sparse_keymap_free(input); -fail1: - input_free_device(input); - kfree(keys); - dev_err(&pdev->dev, "can't register, err %d\n", status); - - return status; -} + /* set up "threaded IRQ handler" */ + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; -static int dm355evm_keys_remove(struct platform_device *pdev) -{ - struct dm355evm_keys *keys = platform_get_drvdata(pdev); + error = devm_request_threaded_irq(&pdev->dev, irq, + NULL, dm355evm_keys_irq, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + dev_name(&pdev->dev), keys); + if (error) + return error; - free_irq(keys->irq, keys); - sparse_keymap_free(keys->input); - input_unregister_device(keys->input); - kfree(keys); + /* register */ + error = input_register_device(input); + if (error) + return error; return 0; } @@ -259,7 +233,6 @@ static int dm355evm_keys_remove(struct platform_device *pdev) */ static struct platform_driver dm355evm_keys_driver = { .probe = dm355evm_keys_probe, - .remove = dm355evm_keys_remove, .driver = { .name = "dm355evm_keys", }, diff --git a/drivers/input/misc/drv260x.c b/drivers/input/misc/drv260x.c index fb089d36c0d6..17eb84ab4c0b 100644 --- a/drivers/input/misc/drv260x.c +++ b/drivers/input/misc/drv260x.c @@ -652,7 +652,6 @@ static const struct i2c_device_id drv260x_id[] = { }; MODULE_DEVICE_TABLE(i2c, drv260x_id); -#ifdef CONFIG_OF static const struct of_device_id drv260x_of_match[] = { { .compatible = "ti,drv2604", }, { .compatible = "ti,drv2604l", }, @@ -661,13 +660,12 @@ static const struct of_device_id drv260x_of_match[] = { { } }; MODULE_DEVICE_TABLE(of, drv260x_of_match); -#endif static struct i2c_driver drv260x_driver = { .probe = drv260x_probe, .driver = { .name = "drv260x-haptics", - .of_match_table = of_match_ptr(drv260x_of_match), + .of_match_table = drv260x_of_match, .pm = &drv260x_pm_ops, }, .id_table = drv260x_id, diff --git a/drivers/input/misc/pwm-beeper.c b/drivers/input/misc/pwm-beeper.c index e53801dbd560..edca0d737750 100644 --- a/drivers/input/misc/pwm-beeper.c +++ b/drivers/input/misc/pwm-beeper.c @@ -19,6 +19,7 @@ #include <linux/kernel.h> #include <linux/of.h> #include <linux/platform_device.h> +#include <linux/property.h> #include <linux/pwm.h> #include <linux/slab.h> #include <linux/workqueue.h> @@ -29,6 +30,7 @@ struct pwm_beeper { struct regulator *amplifier; struct work_struct work; unsigned long period; + unsigned int bell_frequency; bool suspended; bool amplifier_on; }; @@ -94,7 +96,7 @@ static int pwm_beeper_event(struct input_dev *input, switch (code) { case SND_BELL: - value = value ? 1000 : 0; + value = value ? beeper->bell_frequency : 0; break; case SND_TONE: break; @@ -131,6 +133,7 @@ static int pwm_beeper_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct pwm_beeper *beeper; struct pwm_state state; + u32 bell_frequency; int error; beeper = devm_kzalloc(dev, sizeof(*beeper), GFP_KERNEL); @@ -167,6 +170,16 @@ static int pwm_beeper_probe(struct platform_device *pdev) INIT_WORK(&beeper->work, pwm_beeper_work); + error = device_property_read_u32(dev, "beeper-hz", &bell_frequency); + if (error) { + bell_frequency = 1000; + dev_dbg(dev, + "failed to parse 'beeper-hz' property, using default: %uHz\n", + bell_frequency); + } + + beeper->bell_frequency = bell_frequency; + beeper->input = devm_input_allocate_device(dev); if (!beeper->input) { dev_err(dev, "Failed to allocate input device\n"); diff --git a/drivers/input/misc/soc_button_array.c b/drivers/input/misc/soc_button_array.c index ddb2f22fca7a..95b787a63560 100644 --- a/drivers/input/misc/soc_button_array.c +++ b/drivers/input/misc/soc_button_array.c @@ -20,13 +20,6 @@ #include <linux/gpio.h> #include <linux/platform_device.h> -/* - * Definition of buttons on the tablet. The ACPI index of each button - * is defined in section 2.8.7.2 of "Windows ACPI Design Guide for SoC - * Platforms" - */ -#define MAX_NBUTTONS 5 - struct soc_button_info { const char *name; int acpi_index; @@ -55,7 +48,7 @@ static int soc_button_lookup_gpio(struct device *dev, int acpi_index) struct gpio_desc *desc; int gpio; - desc = gpiod_get_index(dev, KBUILD_MODNAME, acpi_index, GPIOD_ASIS); + desc = gpiod_get_index(dev, NULL, acpi_index, GPIOD_ASIS); if (IS_ERR(desc)) return PTR_ERR(desc); @@ -79,14 +72,19 @@ soc_button_device_create(struct platform_device *pdev, int gpio; int error; + for (info = button_info; info->name; info++) + if (info->autorepeat == autorepeat) + n_buttons++; + gpio_keys_pdata = devm_kzalloc(&pdev->dev, sizeof(*gpio_keys_pdata) + - sizeof(*gpio_keys) * MAX_NBUTTONS, + sizeof(*gpio_keys) * n_buttons, GFP_KERNEL); if (!gpio_keys_pdata) return ERR_PTR(-ENOMEM); gpio_keys = (void *)(gpio_keys_pdata + 1); + n_buttons = 0; for (info = button_info; info->name; info++) { if (info->autorepeat != autorepeat) @@ -140,6 +138,153 @@ err_free_mem: return ERR_PTR(error); } +static int soc_button_get_acpi_object_int(const union acpi_object *obj) +{ + if (obj->type != ACPI_TYPE_INTEGER) + return -1; + + return obj->integer.value; +} + +/* Parse a single ACPI0011 _DSD button descriptor */ +static int soc_button_parse_btn_desc(struct device *dev, + const union acpi_object *desc, + int collection_uid, + struct soc_button_info *info) +{ + int upage, usage; + + if (desc->type != ACPI_TYPE_PACKAGE || + desc->package.count != 5 || + /* First byte should be 1 (control) */ + soc_button_get_acpi_object_int(&desc->package.elements[0]) != 1 || + /* Third byte should be collection uid */ + soc_button_get_acpi_object_int(&desc->package.elements[2]) != + collection_uid) { + dev_err(dev, "Invalid ACPI Button Descriptor\n"); + return -ENODEV; + } + + info->event_type = EV_KEY; + info->acpi_index = + soc_button_get_acpi_object_int(&desc->package.elements[1]); + upage = soc_button_get_acpi_object_int(&desc->package.elements[3]); + usage = soc_button_get_acpi_object_int(&desc->package.elements[4]); + + /* + * The UUID: fa6bd625-9ce8-470d-a2c7-b3ca36c4282e descriptors use HID + * usage page and usage codes, but otherwise the device is not HID + * compliant: it uses one irq per button instead of generating HID + * input reports and some buttons should generate wakeups where as + * others should not, so we cannot use the HID subsystem. + * + * Luckily all devices only use a few usage page + usage combinations, + * so we can simply check for the known combinations here. + */ + if (upage == 0x01 && usage == 0x81) { + info->name = "power"; + info->event_code = KEY_POWER; + info->wakeup = true; + } else if (upage == 0x07 && usage == 0xe3) { + info->name = "home"; + info->event_code = KEY_HOMEPAGE; + info->wakeup = true; + } else if (upage == 0x0c && usage == 0xe9) { + info->name = "volume_up"; + info->event_code = KEY_VOLUMEUP; + info->autorepeat = true; + } else if (upage == 0x0c && usage == 0xea) { + info->name = "volume_down"; + info->event_code = KEY_VOLUMEDOWN; + info->autorepeat = true; + } else { + dev_warn(dev, "Unknown button index %d upage %02x usage %02x, ignoring\n", + info->acpi_index, upage, usage); + info->name = "unknown"; + info->event_code = KEY_RESERVED; + } + + return 0; +} + +/* ACPI0011 _DSD btns descriptors UUID: fa6bd625-9ce8-470d-a2c7-b3ca36c4282e */ +static const u8 btns_desc_uuid[16] = { + 0x25, 0xd6, 0x6b, 0xfa, 0xe8, 0x9c, 0x0d, 0x47, + 0xa2, 0xc7, 0xb3, 0xca, 0x36, 0xc4, 0x28, 0x2e +}; + +/* Parse ACPI0011 _DSD button descriptors */ +static struct soc_button_info *soc_button_get_button_info(struct device *dev) +{ + struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER }; + const union acpi_object *desc, *el0, *uuid, *btns_desc = NULL; + struct soc_button_info *button_info; + acpi_status status; + int i, btn, collection_uid = -1; + + status = acpi_evaluate_object_typed(ACPI_HANDLE(dev), "_DSD", NULL, + &buf, ACPI_TYPE_PACKAGE); + if (ACPI_FAILURE(status)) { + dev_err(dev, "ACPI _DSD object not found\n"); + return ERR_PTR(-ENODEV); + } + + /* Look for the Button Descriptors UUID */ + desc = buf.pointer; + for (i = 0; (i + 1) < desc->package.count; i += 2) { + uuid = &desc->package.elements[i]; + + if (uuid->type != ACPI_TYPE_BUFFER || + uuid->buffer.length != 16 || + desc->package.elements[i + 1].type != ACPI_TYPE_PACKAGE) { + break; + } + + if (memcmp(uuid->buffer.pointer, btns_desc_uuid, 16) == 0) { + btns_desc = &desc->package.elements[i + 1]; + break; + } + } + + if (!btns_desc) { + dev_err(dev, "ACPI Button Descriptors not found\n"); + return ERR_PTR(-ENODEV); + } + + /* The first package describes the collection */ + el0 = &btns_desc->package.elements[0]; + if (el0->type == ACPI_TYPE_PACKAGE && + el0->package.count == 5 && + /* First byte should be 0 (collection) */ + soc_button_get_acpi_object_int(&el0->package.elements[0]) == 0 && + /* Third byte should be 0 (top level collection) */ + soc_button_get_acpi_object_int(&el0->package.elements[2]) == 0) { + collection_uid = soc_button_get_acpi_object_int( + &el0->package.elements[1]); + } + if (collection_uid == -1) { + dev_err(dev, "Invalid Button Collection Descriptor\n"); + return ERR_PTR(-ENODEV); + } + + /* There are package.count - 1 buttons + 1 terminating empty entry */ + button_info = devm_kcalloc(dev, btns_desc->package.count, + sizeof(*button_info), GFP_KERNEL); + if (!button_info) + return ERR_PTR(-ENOMEM); + + /* Parse the button descriptors */ + for (i = 1, btn = 0; i < btns_desc->package.count; i++, btn++) { + if (soc_button_parse_btn_desc(dev, + &btns_desc->package.elements[i], + collection_uid, + &button_info[btn])) + return ERR_PTR(-ENODEV); + } + + return button_info; +} + static int soc_button_remove(struct platform_device *pdev) { struct soc_button_data *priv = platform_get_drvdata(pdev); @@ -167,9 +312,15 @@ static int soc_button_probe(struct platform_device *pdev) if (!id) return -ENODEV; - button_info = (struct soc_button_info *)id->driver_data; + if (!id->driver_data) { + button_info = soc_button_get_button_info(dev); + if (IS_ERR(button_info)) + return PTR_ERR(button_info); + } else { + button_info = (struct soc_button_info *)id->driver_data; + } - if (gpiod_count(dev, KBUILD_MODNAME) <= 0) { + if (gpiod_count(dev, NULL) <= 0) { dev_dbg(dev, "no GPIO attached, ignoring...\n"); return -ENODEV; } @@ -197,9 +348,17 @@ static int soc_button_probe(struct platform_device *pdev) if (!priv->children[0] && !priv->children[1]) return -ENODEV; + if (!id->driver_data) + devm_kfree(dev, button_info); + return 0; } +/* + * Definition of buttons on the tablet. The ACPI index of each button + * is defined in section 2.8.7.2 of "Windows ACPI Design Guide for SoC + * Platforms" + */ static struct soc_button_info soc_button_PNP0C40[] = { { "power", 0, EV_KEY, KEY_POWER, false, true }, { "home", 1, EV_KEY, KEY_LEFTMETA, false, true }, @@ -211,6 +370,7 @@ static struct soc_button_info soc_button_PNP0C40[] = { static const struct acpi_device_id soc_button_acpi_match[] = { { "PNP0C40", (unsigned long)soc_button_PNP0C40 }, + { "ACPI0011", 0 }, { } }; diff --git a/drivers/input/misc/wistron_btns.c b/drivers/input/misc/wistron_btns.c index e25f87ba19f6..43e67f546366 100644 --- a/drivers/input/misc/wistron_btns.c +++ b/drivers/input/misc/wistron_btns.c @@ -1243,12 +1243,10 @@ static int setup_input_dev(void) error = input_register_polled_device(wistron_idev); if (error) - goto err_free_keymap; + goto err_free_dev; return 0; - err_free_keymap: - sparse_keymap_free(input_dev); err_free_dev: input_free_polled_device(wistron_idev); return error; @@ -1300,7 +1298,6 @@ static int wistron_remove(struct platform_device *dev) { wistron_led_remove(); input_unregister_polled_device(wistron_idev); - sparse_keymap_free(wistron_idev->input); input_free_polled_device(wistron_idev); bios_detach(); diff --git a/drivers/input/misc/xen-kbdfront.c b/drivers/input/misc/xen-kbdfront.c index 3900875dec10..1fd911d4fadf 100644 --- a/drivers/input/misc/xen-kbdfront.c +++ b/drivers/input/misc/xen-kbdfront.c @@ -84,8 +84,8 @@ static irqreturn_t input_handler(int rq, void *dev_id) input_report_key(dev, event->key.keycode, event->key.pressed); else - pr_warning("unhandled keycode 0x%x\n", - event->key.keycode); + pr_warn("unhandled keycode 0x%x\n", + event->key.keycode); break; case XENKBD_TYPE_POS: input_report_abs(dev, ABS_X, event->pos.abs_x); @@ -133,7 +133,7 @@ static int xenkbd_probe(struct xenbus_device *dev, ret = xenbus_write(XBT_NIL, dev->nodename, "request-abs-pointer", "1"); if (ret) { - pr_warning("xenkbd: can't request abs-pointer"); + pr_warn("xenkbd: can't request abs-pointer\n"); abs = 0; } } @@ -327,7 +327,7 @@ InitWait: ret = xenbus_write(XBT_NIL, info->xbdev->nodename, "request-abs-pointer", "1"); if (ret) - pr_warning("xenkbd: can't request abs-pointer"); + pr_warn("xenkbd: can't request abs-pointer\n"); } xenbus_switch_state(dev, XenbusStateConnected); diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig index 096abb4ad5cd..89ebb8f39fee 100644 --- a/drivers/input/mouse/Kconfig +++ b/drivers/input/mouse/Kconfig @@ -78,6 +78,18 @@ config MOUSE_PS2_SYNAPTICS If unsure, say Y. +config MOUSE_PS2_SYNAPTICS_SMBUS + bool "Synaptics PS/2 SMbus companion" if EXPERT + default y + depends on MOUSE_PS2 + depends on I2C=y || I2C=MOUSE_PS2 + select MOUSE_PS2_SMBUS + help + Say Y here if you have a Synaptics RMI4 touchpad connected to + to an SMBus, but enumerated through PS/2. + + If unsure, say Y. + config MOUSE_PS2_CYPRESS bool "Cypress PS/2 mouse protocol extension" if EXPERT default y @@ -171,6 +183,10 @@ config MOUSE_PS2_VMMOUSE If unsure, say N. +config MOUSE_PS2_SMBUS + bool + depends on MOUSE_PS2 + config MOUSE_SERIAL tristate "Serial mouse" select SERIO diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile index 6168b134937b..56bf0ad877c6 100644 --- a/drivers/input/mouse/Makefile +++ b/drivers/input/mouse/Makefile @@ -39,6 +39,8 @@ psmouse-$(CONFIG_MOUSE_PS2_TOUCHKIT) += touchkit_ps2.o psmouse-$(CONFIG_MOUSE_PS2_CYPRESS) += cypress_ps2.o psmouse-$(CONFIG_MOUSE_PS2_VMMOUSE) += vmmouse.o +psmouse-$(CONFIG_MOUSE_PS2_SMBUS) += psmouse-smbus.o + elan_i2c-objs := elan_i2c_core.o elan_i2c-$(CONFIG_MOUSE_ELAN_I2C_I2C) += elan_i2c_i2c.o elan_i2c-$(CONFIG_MOUSE_ELAN_I2C_SMBUS) += elan_i2c_smbus.o diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index f210e19ddba6..262d1057c1da 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -106,39 +106,36 @@ static const struct alps_nibble_commands alps_v6_nibble_commands[] = { #define ALPS_DUALPOINT_WITH_PRESSURE 0x400 /* device can report trackpoint pressure */ static const struct alps_model_info alps_model_data[] = { - { { 0x32, 0x02, 0x14 }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT } }, /* Toshiba Salellite Pro M10 */ - { { 0x33, 0x02, 0x0a }, 0x00, { ALPS_PROTO_V1, 0x88, 0xf8, 0 } }, /* UMAX-530T */ - { { 0x53, 0x02, 0x0a }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } }, - { { 0x53, 0x02, 0x14 }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } }, - { { 0x60, 0x03, 0xc8 }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } }, /* HP ze1115 */ - { { 0x63, 0x02, 0x0a }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } }, - { { 0x63, 0x02, 0x14 }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } }, - { { 0x63, 0x02, 0x28 }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_FW_BK_2 } }, /* Fujitsu Siemens S6010 */ - { { 0x63, 0x02, 0x3c }, 0x00, { ALPS_PROTO_V2, 0x8f, 0x8f, ALPS_WHEEL } }, /* Toshiba Satellite S2400-103 */ - { { 0x63, 0x02, 0x50 }, 0x00, { ALPS_PROTO_V2, 0xef, 0xef, ALPS_FW_BK_1 } }, /* NEC Versa L320 */ - { { 0x63, 0x02, 0x64 }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } }, - { { 0x63, 0x03, 0xc8 }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT } }, /* Dell Latitude D800 */ - { { 0x73, 0x00, 0x0a }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_DUALPOINT } }, /* ThinkPad R61 8918-5QG */ - { { 0x73, 0x02, 0x0a }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } }, - { { 0x73, 0x02, 0x14 }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_FW_BK_2 } }, /* Ahtec Laptop */ - /* * XXX This entry is suspicious. First byte has zero lower nibble, * which is what a normal mouse would report. Also, the value 0x0e * isn't valid per PS/2 spec. */ - { { 0x20, 0x02, 0x0e }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT } }, - - { { 0x22, 0x02, 0x0a }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT } }, - { { 0x22, 0x02, 0x14 }, 0x00, { ALPS_PROTO_V2, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT } }, /* Dell Latitude D600 */ - /* Dell Latitude E5500, E6400, E6500, Precision M4400 */ - { { 0x62, 0x02, 0x14 }, 0x00, { ALPS_PROTO_V2, 0xcf, 0xcf, - ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED } }, - { { 0x73, 0x00, 0x14 }, 0x00, { ALPS_PROTO_V6, 0xff, 0xff, ALPS_DUALPOINT } }, /* Dell XT2 */ - { { 0x73, 0x02, 0x50 }, 0x00, { ALPS_PROTO_V2, 0xcf, 0xcf, ALPS_FOUR_BUTTONS } }, /* Dell Vostro 1400 */ - { { 0x52, 0x01, 0x14 }, 0x00, { ALPS_PROTO_V2, 0xff, 0xff, + { { 0x20, 0x02, 0x0e }, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT } }, + + { { 0x22, 0x02, 0x0a }, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT } }, + { { 0x22, 0x02, 0x14 }, { ALPS_PROTO_V2, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT } }, /* Dell Latitude D600 */ + { { 0x32, 0x02, 0x14 }, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT } }, /* Toshiba Salellite Pro M10 */ + { { 0x33, 0x02, 0x0a }, { ALPS_PROTO_V1, 0x88, 0xf8, 0 } }, /* UMAX-530T */ + { { 0x52, 0x01, 0x14 }, { ALPS_PROTO_V2, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED } }, /* Toshiba Tecra A11-11L */ - { { 0x73, 0x02, 0x64 }, 0x8a, { ALPS_PROTO_V4, 0x8f, 0x8f, 0 } }, + { { 0x53, 0x02, 0x0a }, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } }, + { { 0x53, 0x02, 0x14 }, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } }, + { { 0x60, 0x03, 0xc8 }, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } }, /* HP ze1115 */ + { { 0x62, 0x02, 0x14 }, { ALPS_PROTO_V2, 0xcf, 0xcf, + ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED } }, /* Dell Latitude E5500, E6400, E6500, Precision M4400 */ + { { 0x63, 0x02, 0x0a }, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } }, + { { 0x63, 0x02, 0x14 }, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } }, + { { 0x63, 0x02, 0x28 }, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_FW_BK_2 } }, /* Fujitsu Siemens S6010 */ + { { 0x63, 0x02, 0x3c }, { ALPS_PROTO_V2, 0x8f, 0x8f, ALPS_WHEEL } }, /* Toshiba Satellite S2400-103 */ + { { 0x63, 0x02, 0x50 }, { ALPS_PROTO_V2, 0xef, 0xef, ALPS_FW_BK_1 } }, /* NEC Versa L320 */ + { { 0x63, 0x02, 0x64 }, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } }, + { { 0x63, 0x03, 0xc8 }, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT } }, /* Dell Latitude D800 */ + { { 0x73, 0x00, 0x0a }, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_DUALPOINT } }, /* ThinkPad R61 8918-5QG */ + { { 0x73, 0x00, 0x14 }, { ALPS_PROTO_V6, 0xff, 0xff, ALPS_DUALPOINT } }, /* Dell XT2 */ + { { 0x73, 0x02, 0x0a }, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } }, + { { 0x73, 0x02, 0x14 }, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_FW_BK_2 } }, /* Ahtec Laptop */ + { { 0x73, 0x02, 0x50 }, { ALPS_PROTO_V2, 0xcf, 0xcf, ALPS_FOUR_BUTTONS } }, /* Dell Vostro 1400 */ }; static const struct alps_protocol_info alps_v3_protocol_data = { @@ -149,6 +146,10 @@ static const struct alps_protocol_info alps_v3_rushmore_data = { ALPS_PROTO_V3_RUSHMORE, 0x8f, 0x8f, ALPS_DUALPOINT }; +static const struct alps_protocol_info alps_v4_protocol_data = { + ALPS_PROTO_V4, 0x8f, 0x8f, 0 +}; + static const struct alps_protocol_info alps_v5_protocol_data = { ALPS_PROTO_V5, 0xc8, 0xd8, 0 }; @@ -161,6 +162,10 @@ static const struct alps_protocol_info alps_v8_protocol_data = { ALPS_PROTO_V8, 0x18, 0x18, 0 }; +static const struct alps_protocol_info alps_v9_protocol_data = { + ALPS_PROTO_V9, 0xc8, 0xc8, 0 +}; + /* * Some v2 models report the stick buttons in separate bits */ @@ -2806,12 +2811,8 @@ static const struct alps_protocol_info *alps_match_table(unsigned char *e7, for (i = 0; i < ARRAY_SIZE(alps_model_data); i++) { model = &alps_model_data[i]; - if (!memcmp(e7, model->signature, sizeof(model->signature)) && - (!model->command_mode_resp || - model->command_mode_resp == ec[2])) { - + if (!memcmp(e7, model->signature, sizeof(model->signature))) return &model->protocol_info; - } } return NULL; @@ -2849,7 +2850,10 @@ static int alps_identify(struct psmouse *psmouse, struct alps_data *priv) protocol = alps_match_table(e7, ec); if (!protocol) { - if (e7[0] == 0x73 && e7[1] == 0x03 && e7[2] == 0x50 && + if (e7[0] == 0x73 && e7[1] == 0x02 && e7[2] == 0x64 && + ec[2] == 0x8a) { + protocol = &alps_v4_protocol_data; + } else if (e7[0] == 0x73 && e7[1] == 0x03 && e7[2] == 0x50 && ec[0] == 0x73 && (ec[1] == 0x01 || ec[1] == 0x02)) { protocol = &alps_v5_protocol_data; } else if (ec[0] == 0x88 && @@ -2863,6 +2867,12 @@ static int alps_identify(struct psmouse *psmouse, struct alps_data *priv) } else if (e7[0] == 0x73 && e7[1] == 0x03 && (e7[2] == 0x14 || e7[2] == 0x28)) { protocol = &alps_v8_protocol_data; + } else if (e7[0] == 0x73 && e7[1] == 0x03 && e7[2] == 0xc8) { + protocol = &alps_v9_protocol_data; + psmouse_warn(psmouse, + "Unsupported ALPS V9 touchpad: E7=%3ph, EC=%3ph\n", + e7, ec); + return -EINVAL; } else { psmouse_dbg(psmouse, "Likely not an ALPS touchpad: E7=%3ph, EC=%3ph\n", e7, ec); diff --git a/drivers/input/mouse/alps.h b/drivers/input/mouse/alps.h index 4334f2805d93..ed2d6879fa52 100644 --- a/drivers/input/mouse/alps.h +++ b/drivers/input/mouse/alps.h @@ -23,6 +23,7 @@ #define ALPS_PROTO_V6 0x600 #define ALPS_PROTO_V7 0x700 /* t3btl t4s */ #define ALPS_PROTO_V8 0x800 /* SS4btl SS4s */ +#define ALPS_PROTO_V9 0x900 /* ss3btl */ #define MAX_TOUCHES 4 @@ -172,10 +173,6 @@ struct alps_protocol_info { /** * struct alps_model_info - touchpad ID table * @signature: E7 response string to match. - * @command_mode_resp: For V3/V4 touchpads, the final byte of the EC response - * (aka command mode response) identifies the firmware minor version. This - * can be used to distinguish different hardware models which are not - * uniquely identifiable through their E7 responses. * @protocol_info: information about protocol used by the device. * * Many (but not all) ALPS touchpads can be identified by looking at the @@ -184,7 +181,6 @@ struct alps_protocol_info { */ struct alps_model_info { u8 signature[3]; - u8 command_mode_resp; struct alps_protocol_info protocol_info; }; diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index a598b7223cef..f73b47b8c578 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -116,17 +116,6 @@ static DEFINE_MUTEX(psmouse_mutex); static struct workqueue_struct *kpsmoused_wq; -struct psmouse_protocol { - enum psmouse_type type; - bool maxproto; - bool ignore_parity; /* Protocol should ignore parity errors from KBC */ - bool try_passthru; /* Try protocol also on passthrough ports */ - const char *name; - const char *alias; - int (*detect)(struct psmouse *, bool); - int (*init)(struct psmouse *); -}; - static void psmouse_report_standard_buttons(struct input_dev *dev, u8 buttons) { input_report_key(dev, BTN_LEFT, buttons & BIT(0)); @@ -148,7 +137,7 @@ psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse) /* Full packet accumulated, process it */ - switch (psmouse->type) { + switch (psmouse->protocol->type) { case PSMOUSE_IMPS: /* IntelliMouse has scroll wheel */ input_report_rel(dev, REL_WHEEL, -(signed char) packet[3]); @@ -325,7 +314,8 @@ static irqreturn_t psmouse_interrupt(struct serio *serio, goto out; if (unlikely((flags & SERIO_TIMEOUT) || - ((flags & SERIO_PARITY) && !psmouse->ignore_parity))) { + ((flags & SERIO_PARITY) && + !psmouse->protocol->ignore_parity))) { if (psmouse->state == PSMOUSE_ACTIVATED) psmouse_warn(psmouse, @@ -372,7 +362,7 @@ static irqreturn_t psmouse_interrupt(struct serio *serio, } if (psmouse->packet[1] == PSMOUSE_RET_ID || - (psmouse->type == PSMOUSE_HGPK && + (psmouse->protocol->type == PSMOUSE_HGPK && psmouse->packet[1] == PSMOUSE_RET_BAT)) { __psmouse_set_state(psmouse, PSMOUSE_IGNORE); serio_reconnect(serio); @@ -783,7 +773,7 @@ static const struct psmouse_protocol psmouse_protocols[] = { .name = "SynPS/2", .alias = "synaptics", .detect = synaptics_detect, - .init = synaptics_init, + .init = synaptics_init_absolute, }, { .type = PSMOUSE_SYNAPTICS_RELATIVE, @@ -793,6 +783,16 @@ static const struct psmouse_protocol psmouse_protocols[] = { .init = synaptics_init_relative, }, #endif +#ifdef CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS + { + .type = PSMOUSE_SYNAPTICS_SMBUS, + .name = "SynSMBus", + .alias = "synaptics-smbus", + .detect = synaptics_detect, + .init = synaptics_init_smbus, + .smbus_companion = true, + }, +#endif #ifdef CONFIG_MOUSE_PS2_ALPS { .type = PSMOUSE_ALPS, @@ -959,6 +959,8 @@ static void psmouse_apply_defaults(struct psmouse *psmouse) __set_bit(INPUT_PROP_POINTER, input_dev->propbit); + psmouse->protocol = &psmouse_protocols[0]; + psmouse->set_rate = psmouse_set_rate; psmouse->set_resolution = psmouse_set_resolution; psmouse->set_scale = psmouse_set_scale; @@ -966,6 +968,7 @@ static void psmouse_apply_defaults(struct psmouse *psmouse) psmouse->protocol_handler = psmouse_process_byte; psmouse->pktsize = 3; psmouse->reconnect = NULL; + psmouse->fast_reconnect = NULL; psmouse->disconnect = NULL; psmouse->cleanup = NULL; psmouse->pt_activate = NULL; @@ -1018,6 +1021,7 @@ static int psmouse_extensions(struct psmouse *psmouse, unsigned int max_proto, bool set_properties) { bool synaptics_hardware = false; + int ret; /* * Always check for focaltech, this is safe as it uses pnp-id @@ -1080,9 +1084,14 @@ static int psmouse_extensions(struct psmouse *psmouse, * enabled first, since we try detecting Synaptics * even when protocol is disabled. */ - if (IS_ENABLED(CONFIG_MOUSE_PS2_SYNAPTICS) && - (!set_properties || synaptics_init(psmouse) == 0)) { - return PSMOUSE_SYNAPTICS; + if (IS_ENABLED(CONFIG_MOUSE_PS2_SYNAPTICS) || + IS_ENABLED(CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS)) { + if (!set_properties) + return PSMOUSE_SYNAPTICS; + + ret = synaptics_init(psmouse); + if (ret >= 0) + return ret; } /* @@ -1431,9 +1440,8 @@ static void psmouse_cleanup(struct serio *serio) */ static void psmouse_disconnect(struct serio *serio) { - struct psmouse *psmouse, *parent = NULL; - - psmouse = serio_get_drvdata(serio); + struct psmouse *psmouse = serio_get_drvdata(serio); + struct psmouse *parent = NULL; sysfs_remove_group(&serio->dev.kobj, &psmouse_attribute_group); @@ -1461,7 +1469,10 @@ static void psmouse_disconnect(struct serio *serio) serio_close(serio); serio_set_drvdata(serio, NULL); - input_unregister_device(psmouse->dev); + + if (psmouse->dev) + input_unregister_device(psmouse->dev); + kfree(psmouse); if (parent) @@ -1475,6 +1486,7 @@ static int psmouse_switch_protocol(struct psmouse *psmouse, { const struct psmouse_protocol *selected_proto; struct input_dev *input_dev = psmouse->dev; + enum psmouse_type type; input_dev->dev.parent = &psmouse->ps2dev.serio->dev; @@ -1487,15 +1499,13 @@ static int psmouse_switch_protocol(struct psmouse *psmouse, if (proto->init && proto->init(psmouse) < 0) return -1; - psmouse->type = proto->type; selected_proto = proto; } else { - psmouse->type = psmouse_extensions(psmouse, - psmouse_max_proto, true); - selected_proto = psmouse_protocol_by_type(psmouse->type); + type = psmouse_extensions(psmouse, psmouse_max_proto, true); + selected_proto = psmouse_protocol_by_type(type); } - psmouse->ignore_parity = selected_proto->ignore_parity; + psmouse->protocol = selected_proto; /* * If mouse's packet size is 3 there is no point in polling the @@ -1521,7 +1531,7 @@ static int psmouse_switch_protocol(struct psmouse *psmouse, input_dev->phys = psmouse->phys; input_dev->id.bustype = BUS_I8042; input_dev->id.vendor = 0x0002; - input_dev->id.product = psmouse->type; + input_dev->id.product = psmouse->protocol->type; input_dev->id.version = psmouse->model; return 0; @@ -1583,12 +1593,18 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv) psmouse_switch_protocol(psmouse, NULL); - psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); - psmouse_initialize(psmouse); + if (!psmouse->protocol->smbus_companion) { + psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); + psmouse_initialize(psmouse); - error = input_register_device(psmouse->dev); - if (error) - goto err_protocol_disconnect; + error = input_register_device(input_dev); + if (error) + goto err_protocol_disconnect; + } else { + /* Smbus companion will be reporting events, not us. */ + input_free_device(input_dev); + psmouse->dev = input_dev = NULL; + } if (parent && parent->pt_activate) parent->pt_activate(parent); @@ -1597,7 +1613,12 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv) if (error) goto err_pt_deactivate; - psmouse_activate(psmouse); + /* + * PS/2 devices having SMBus companions should stay disabled + * on PS/2 side, in order to have SMBus part operable. + */ + if (!psmouse->protocol->smbus_companion) + psmouse_activate(psmouse); out: /* If this is a pass-through port the parent needs to be re-activated */ @@ -1610,8 +1631,10 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv) err_pt_deactivate: if (parent && parent->pt_deactivate) parent->pt_deactivate(parent); - input_unregister_device(psmouse->dev); - input_dev = NULL; /* so we don't try to free it below */ + if (input_dev) { + input_unregister_device(input_dev); + input_dev = NULL; /* so we don't try to free it below */ + } err_protocol_disconnect: if (psmouse->disconnect) psmouse->disconnect(psmouse); @@ -1628,15 +1651,26 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv) goto out; } -static int psmouse_reconnect(struct serio *serio) +static int __psmouse_reconnect(struct serio *serio, bool fast_reconnect) { struct psmouse *psmouse = serio_get_drvdata(serio); struct psmouse *parent = NULL; - unsigned char type; + int (*reconnect_handler)(struct psmouse *); + enum psmouse_type type; int rc = -1; mutex_lock(&psmouse_mutex); + if (fast_reconnect) { + reconnect_handler = psmouse->fast_reconnect; + if (!reconnect_handler) { + rc = -ENOENT; + goto out_unlock; + } + } else { + reconnect_handler = psmouse->reconnect; + } + if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { parent = serio_get_drvdata(serio->parent); psmouse_deactivate(parent); @@ -1644,8 +1678,8 @@ static int psmouse_reconnect(struct serio *serio) psmouse_set_state(psmouse, PSMOUSE_INITIALIZING); - if (psmouse->reconnect) { - if (psmouse->reconnect(psmouse)) + if (reconnect_handler) { + if (reconnect_handler(psmouse)) goto out; } else { psmouse_reset(psmouse); @@ -1654,7 +1688,7 @@ static int psmouse_reconnect(struct serio *serio) goto out; type = psmouse_extensions(psmouse, psmouse_max_proto, false); - if (psmouse->type != type) + if (psmouse->protocol->type != type) goto out; } @@ -1662,14 +1696,21 @@ static int psmouse_reconnect(struct serio *serio) * OK, the device type (and capabilities) match the old one, * we can continue using it, complete initialization */ - psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); - - psmouse_initialize(psmouse); + if (!psmouse->protocol->smbus_companion) { + psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); + psmouse_initialize(psmouse); + } if (parent && parent->pt_activate) parent->pt_activate(parent); - psmouse_activate(psmouse); + /* + * PS/2 devices having SMBus companions should stay disabled + * on PS/2 side, in order to have SMBus part operable. + */ + if (!psmouse->protocol->smbus_companion) + psmouse_activate(psmouse); + rc = 0; out: @@ -1677,10 +1718,21 @@ out: if (parent) psmouse_activate(parent); +out_unlock: mutex_unlock(&psmouse_mutex); return rc; } +static int psmouse_reconnect(struct serio *serio) +{ + return __psmouse_reconnect(serio, false); +} + +static int psmouse_fast_reconnect(struct serio *serio) +{ + return __psmouse_reconnect(serio, true); +} + static struct serio_device_id psmouse_serio_ids[] = { { .type = SERIO_8042, @@ -1708,6 +1760,7 @@ static struct serio_driver psmouse_drv = { .interrupt = psmouse_interrupt, .connect = psmouse_connect, .reconnect = psmouse_reconnect, + .fast_reconnect = psmouse_fast_reconnect, .disconnect = psmouse_disconnect, .cleanup = psmouse_cleanup, }; @@ -1717,9 +1770,11 @@ ssize_t psmouse_attr_show_helper(struct device *dev, struct device_attribute *de { struct serio *serio = to_serio_port(dev); struct psmouse_attribute *attr = to_psmouse_attr(devattr); - struct psmouse *psmouse; + struct psmouse *psmouse = serio_get_drvdata(serio); - psmouse = serio_get_drvdata(serio); + if (psmouse->protocol->smbus_companion && + devattr != &psmouse_attr_protocol.dattr) + return -ENOENT; return attr->show(psmouse, attr->data, buf); } @@ -1738,6 +1793,12 @@ ssize_t psmouse_attr_set_helper(struct device *dev, struct device_attribute *dev psmouse = serio_get_drvdata(serio); + if (psmouse->protocol->smbus_companion && + devattr != &psmouse_attr_protocol.dattr) { + retval = -ENOENT; + goto out_unlock; + } + if (attr->protect) { if (psmouse->state == PSMOUSE_IGNORE) { retval = -ENODEV; @@ -1749,13 +1810,14 @@ ssize_t psmouse_attr_set_helper(struct device *dev, struct device_attribute *dev psmouse_deactivate(parent); } - psmouse_deactivate(psmouse); + if (!psmouse->protocol->smbus_companion) + psmouse_deactivate(psmouse); } retval = attr->set(psmouse, attr->data, buf, count); if (attr->protect) { - if (retval != -ENODEV) + if (retval != -ENODEV && !psmouse->protocol->smbus_companion) psmouse_activate(psmouse); if (parent) @@ -1792,7 +1854,7 @@ static ssize_t psmouse_set_int_attr(struct psmouse *psmouse, void *offset, const static ssize_t psmouse_attr_show_protocol(struct psmouse *psmouse, void *data, char *buf) { - return sprintf(buf, "%s\n", psmouse_protocol_by_type(psmouse->type)->name); + return sprintf(buf, "%s\n", psmouse->protocol->name); } static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, const char *buf, size_t count) @@ -1808,7 +1870,7 @@ static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, co if (!proto) return -EINVAL; - if (psmouse->type == proto->type) + if (psmouse->protocol == proto) return count; new_dev = input_allocate_device(); @@ -1832,7 +1894,7 @@ static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, co return -ENODEV; } - if (psmouse->type == proto->type) { + if (psmouse->protocol == proto) { input_free_device(new_dev); return count; /* switched by other thread */ } @@ -1845,7 +1907,7 @@ static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, co } old_dev = psmouse->dev; - old_proto = psmouse_protocol_by_type(psmouse->type); + old_proto = psmouse->protocol; if (psmouse->disconnect) psmouse->disconnect(psmouse); @@ -1864,23 +1926,29 @@ static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, co psmouse_initialize(psmouse); psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); - error = input_register_device(psmouse->dev); - if (error) { - if (psmouse->disconnect) - psmouse->disconnect(psmouse); + if (psmouse->protocol->smbus_companion) { + input_free_device(psmouse->dev); + psmouse->dev = NULL; + } else { + error = input_register_device(psmouse->dev); + if (error) { + if (psmouse->disconnect) + psmouse->disconnect(psmouse); - psmouse_set_state(psmouse, PSMOUSE_IGNORE); - input_free_device(new_dev); - psmouse->dev = old_dev; - psmouse_set_state(psmouse, PSMOUSE_INITIALIZING); - psmouse_switch_protocol(psmouse, old_proto); - psmouse_initialize(psmouse); - psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); + psmouse_set_state(psmouse, PSMOUSE_IGNORE); + input_free_device(new_dev); + psmouse->dev = old_dev; + psmouse_set_state(psmouse, PSMOUSE_INITIALIZING); + psmouse_switch_protocol(psmouse, old_proto); + psmouse_initialize(psmouse); + psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); - return error; + return error; + } } - input_unregister_device(old_dev); + if (old_dev) + input_unregister_device(old_dev); if (parent && parent->pt_activate) parent->pt_activate(parent); @@ -1947,16 +2015,27 @@ static int __init psmouse_init(void) synaptics_module_init(); hgpk_module_init(); + err = psmouse_smbus_module_init(); + if (err) + return err; + kpsmoused_wq = alloc_ordered_workqueue("kpsmoused", 0); if (!kpsmoused_wq) { pr_err("failed to create kpsmoused workqueue\n"); - return -ENOMEM; + err = -ENOMEM; + goto err_smbus_exit; } err = serio_register_driver(&psmouse_drv); if (err) - destroy_workqueue(kpsmoused_wq); + goto err_destroy_wq; + + return 0; +err_destroy_wq: + destroy_workqueue(kpsmoused_wq); +err_smbus_exit: + psmouse_smbus_module_exit(); return err; } @@ -1964,6 +2043,7 @@ static void __exit psmouse_exit(void) { serio_unregister_driver(&psmouse_drv); destroy_workqueue(kpsmoused_wq); + psmouse_smbus_module_exit(); } module_init(psmouse_init); diff --git a/drivers/input/mouse/psmouse-smbus.c b/drivers/input/mouse/psmouse-smbus.c new file mode 100644 index 000000000000..d2b1ea34feac --- /dev/null +++ b/drivers/input/mouse/psmouse-smbus.c @@ -0,0 +1,302 @@ +/* + * Copyright (c) 2017 Red Hat, 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. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/libps2.h> +#include <linux/i2c.h> +#include <linux/serio.h> +#include <linux/slab.h> +#include <linux/workqueue.h> +#include "psmouse.h" + +struct psmouse_smbus_dev { + struct i2c_board_info board; + struct psmouse *psmouse; + struct i2c_client *client; + struct list_head node; + bool dead; +}; + +static LIST_HEAD(psmouse_smbus_list); +static DEFINE_MUTEX(psmouse_smbus_mutex); + +static void psmouse_smbus_check_adapter(struct i2c_adapter *adapter) +{ + struct psmouse_smbus_dev *smbdev; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_HOST_NOTIFY)) + return; + + mutex_lock(&psmouse_smbus_mutex); + + list_for_each_entry(smbdev, &psmouse_smbus_list, node) { + if (smbdev->dead) + continue; + + if (smbdev->client) + continue; + + /* + * Here would be a good place to check if device is actually + * present, but it seems that SMBus will not respond unless we + * fully reset PS/2 connection. So cross our fingers, and try + * to switch over, hopefully our system will not have too many + * "host notify" I2C adapters. + */ + psmouse_dbg(smbdev->psmouse, + "SMBus candidate adapter appeared, triggering rescan\n"); + serio_rescan(smbdev->psmouse->ps2dev.serio); + } + + mutex_unlock(&psmouse_smbus_mutex); +} + +static void psmouse_smbus_detach_i2c_client(struct i2c_client *client) +{ + struct psmouse_smbus_dev *smbdev, *tmp; + + mutex_lock(&psmouse_smbus_mutex); + + list_for_each_entry_safe(smbdev, tmp, &psmouse_smbus_list, node) { + if (smbdev->client != client) + continue; + + kfree(client->dev.platform_data); + client->dev.platform_data = NULL; + + if (!smbdev->dead) { + psmouse_dbg(smbdev->psmouse, + "Marking SMBus companion %s as gone\n", + dev_name(&smbdev->client->dev)); + smbdev->dead = true; + serio_rescan(smbdev->psmouse->ps2dev.serio); + } else { + list_del(&smbdev->node); + kfree(smbdev); + } + } + + mutex_unlock(&psmouse_smbus_mutex); +} + +static int psmouse_smbus_notifier_call(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct device *dev = data; + + switch (action) { + case BUS_NOTIFY_ADD_DEVICE: + if (dev->type == &i2c_adapter_type) + psmouse_smbus_check_adapter(to_i2c_adapter(dev)); + break; + + case BUS_NOTIFY_REMOVED_DEVICE: + if (dev->type != &i2c_adapter_type) + psmouse_smbus_detach_i2c_client(to_i2c_client(dev)); + break; + } + + return 0; +} + +static struct notifier_block psmouse_smbus_notifier = { + .notifier_call = psmouse_smbus_notifier_call, +}; + +static psmouse_ret_t psmouse_smbus_process_byte(struct psmouse *psmouse) +{ + return PSMOUSE_FULL_PACKET; +} + +static int psmouse_smbus_reconnect(struct psmouse *psmouse) +{ + psmouse_deactivate(psmouse); + + return 0; +} + +struct psmouse_smbus_removal_work { + struct work_struct work; + struct i2c_client *client; +}; + +static void psmouse_smbus_remove_i2c_device(struct work_struct *work) +{ + struct psmouse_smbus_removal_work *rwork = + container_of(work, struct psmouse_smbus_removal_work, work); + + dev_dbg(&rwork->client->dev, "destroying SMBus companion device\n"); + i2c_unregister_device(rwork->client); + + kfree(rwork); +} + +/* + * This schedules removal of SMBus companion device. We have to do + * it in a separate tread to avoid deadlocking on psmouse_mutex in + * case the device has a trackstick (which is also driven by psmouse). + * + * Note that this may be racing with i2c adapter removal, but we + * can't do anything about that: i2c automatically destroys clients + * attached to an adapter that is being removed. This has to be + * fixed in i2c core. + */ +static void psmouse_smbus_schedule_remove(struct i2c_client *client) +{ + struct psmouse_smbus_removal_work *rwork; + + rwork = kzalloc(sizeof(*rwork), GFP_KERNEL); + if (rwork) { + INIT_WORK(&rwork->work, psmouse_smbus_remove_i2c_device); + rwork->client = client; + + schedule_work(&rwork->work); + } +} + +static void psmouse_smbus_disconnect(struct psmouse *psmouse) +{ + struct psmouse_smbus_dev *smbdev = psmouse->private; + + mutex_lock(&psmouse_smbus_mutex); + + if (smbdev->dead) { + list_del(&smbdev->node); + kfree(smbdev); + } else { + smbdev->dead = true; + psmouse_dbg(smbdev->psmouse, + "posting removal request for SMBus companion %s\n", + dev_name(&smbdev->client->dev)); + psmouse_smbus_schedule_remove(smbdev->client); + } + + mutex_unlock(&psmouse_smbus_mutex); + + psmouse->private = NULL; +} + +static int psmouse_smbus_create_companion(struct device *dev, void *data) +{ + struct psmouse_smbus_dev *smbdev = data; + unsigned short addr_list[] = { smbdev->board.addr, I2C_CLIENT_END }; + struct i2c_adapter *adapter; + + adapter = i2c_verify_adapter(dev); + if (!adapter) + return 0; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_HOST_NOTIFY)) + return 0; + + smbdev->client = i2c_new_probed_device(adapter, &smbdev->board, + addr_list, NULL); + if (!smbdev->client) + return 0; + + /* We have our(?) device, stop iterating i2c bus. */ + return 1; +} + +void psmouse_smbus_cleanup(struct psmouse *psmouse) +{ + struct psmouse_smbus_dev *smbdev, *tmp; + + mutex_lock(&psmouse_smbus_mutex); + + list_for_each_entry_safe(smbdev, tmp, &psmouse_smbus_list, node) { + if (psmouse == smbdev->psmouse) { + list_del(&smbdev->node); + kfree(smbdev); + } + } + + mutex_unlock(&psmouse_smbus_mutex); +} + +int psmouse_smbus_init(struct psmouse *psmouse, + const struct i2c_board_info *board, + const void *pdata, size_t pdata_size, + bool leave_breadcrumbs) +{ + struct psmouse_smbus_dev *smbdev; + int error; + + smbdev = kzalloc(sizeof(*smbdev), GFP_KERNEL); + if (!smbdev) + return -ENOMEM; + + smbdev->psmouse = psmouse; + smbdev->board = *board; + + smbdev->board.platform_data = kmemdup(pdata, pdata_size, GFP_KERNEL); + if (!smbdev->board.platform_data) { + kfree(smbdev); + return -ENOMEM; + } + + psmouse->private = smbdev; + psmouse->protocol_handler = psmouse_smbus_process_byte; + psmouse->reconnect = psmouse_smbus_reconnect; + psmouse->fast_reconnect = psmouse_smbus_reconnect; + psmouse->disconnect = psmouse_smbus_disconnect; + psmouse->resync_time = 0; + + psmouse_deactivate(psmouse); + + mutex_lock(&psmouse_smbus_mutex); + list_add_tail(&smbdev->node, &psmouse_smbus_list); + mutex_unlock(&psmouse_smbus_mutex); + + /* Bind to already existing adapters right away */ + error = i2c_for_each_dev(smbdev, psmouse_smbus_create_companion); + + if (smbdev->client) { + /* We have our companion device */ + return 0; + } + + /* + * If we did not create i2c device we will not need platform + * data even if we are leaving breadcrumbs. + */ + kfree(smbdev->board.platform_data); + smbdev->board.platform_data = NULL; + + if (error < 0 || !leave_breadcrumbs) { + mutex_lock(&psmouse_smbus_mutex); + list_del(&smbdev->node); + mutex_unlock(&psmouse_smbus_mutex); + + kfree(smbdev); + } + + return error < 0 ? error : -EAGAIN; +} + +int __init psmouse_smbus_module_init(void) +{ + int error; + + error = bus_register_notifier(&i2c_bus_type, &psmouse_smbus_notifier); + if (error) { + pr_err("failed to register i2c bus notifier: %d\n", error); + return error; + } + + return 0; +} + +void psmouse_smbus_module_exit(void) +{ + bus_unregister_notifier(&i2c_bus_type, &psmouse_smbus_notifier); + flush_scheduled_work(); +} diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h index 8c83b8e2505c..38855e425f01 100644 --- a/drivers/input/mouse/psmouse.h +++ b/drivers/input/mouse/psmouse.h @@ -44,21 +44,60 @@ enum psmouse_scale { PSMOUSE_SCALE21 }; +enum psmouse_type { + PSMOUSE_NONE, + PSMOUSE_PS2, + PSMOUSE_PS2PP, + PSMOUSE_THINKPS, + PSMOUSE_GENPS, + PSMOUSE_IMPS, + PSMOUSE_IMEX, + PSMOUSE_SYNAPTICS, + PSMOUSE_ALPS, + PSMOUSE_LIFEBOOK, + PSMOUSE_TRACKPOINT, + PSMOUSE_TOUCHKIT_PS2, + PSMOUSE_CORTRON, + PSMOUSE_HGPK, + PSMOUSE_ELANTECH, + PSMOUSE_FSP, + PSMOUSE_SYNAPTICS_RELATIVE, + PSMOUSE_CYPRESS, + PSMOUSE_FOCALTECH, + PSMOUSE_VMMOUSE, + PSMOUSE_BYD, + PSMOUSE_SYNAPTICS_SMBUS, + PSMOUSE_AUTO /* This one should always be last */ +}; + +struct psmouse; + +struct psmouse_protocol { + enum psmouse_type type; + bool maxproto; + bool ignore_parity; /* Protocol should ignore parity errors from KBC */ + bool try_passthru; /* Try protocol also on passthrough ports */ + bool smbus_companion; /* "Protocol" is a stub, device is on SMBus */ + const char *name; + const char *alias; + int (*detect)(struct psmouse *, bool); + int (*init)(struct psmouse *); +}; + struct psmouse { void *private; struct input_dev *dev; struct ps2dev ps2dev; struct delayed_work resync_work; - char *vendor; - char *name; + const char *vendor; + const char *name; + const struct psmouse_protocol *protocol; unsigned char packet[8]; unsigned char badbyte; unsigned char pktcnt; unsigned char pktsize; - unsigned char type; unsigned char oob_data_type; unsigned char extra_buttons; - bool ignore_parity; bool acks_disable_command; unsigned int model; unsigned long last; @@ -80,6 +119,7 @@ struct psmouse { void (*set_scale)(struct psmouse *psmouse, enum psmouse_scale scale); int (*reconnect)(struct psmouse *psmouse); + int (*fast_reconnect)(struct psmouse *psmouse); void (*disconnect)(struct psmouse *psmouse); void (*cleanup)(struct psmouse *psmouse); int (*poll)(struct psmouse *psmouse); @@ -88,31 +128,6 @@ struct psmouse { void (*pt_deactivate)(struct psmouse *psmouse); }; -enum psmouse_type { - PSMOUSE_NONE, - PSMOUSE_PS2, - PSMOUSE_PS2PP, - PSMOUSE_THINKPS, - PSMOUSE_GENPS, - PSMOUSE_IMPS, - PSMOUSE_IMEX, - PSMOUSE_SYNAPTICS, - PSMOUSE_ALPS, - PSMOUSE_LIFEBOOK, - PSMOUSE_TRACKPOINT, - PSMOUSE_TOUCHKIT_PS2, - PSMOUSE_CORTRON, - PSMOUSE_HGPK, - PSMOUSE_ELANTECH, - PSMOUSE_FSP, - PSMOUSE_SYNAPTICS_RELATIVE, - PSMOUSE_CYPRESS, - PSMOUSE_FOCALTECH, - PSMOUSE_VMMOUSE, - PSMOUSE_BYD, - PSMOUSE_AUTO /* This one should always be last */ -}; - void psmouse_queue_work(struct psmouse *psmouse, struct delayed_work *work, unsigned long delay); int psmouse_sliced_command(struct psmouse *psmouse, unsigned char command); @@ -195,5 +210,34 @@ static struct psmouse_attribute psmouse_attr_##_name = { \ &(psmouse)->ps2dev.serio->dev, \ psmouse_fmt(format), ##__VA_ARGS__) +#ifdef CONFIG_MOUSE_PS2_SMBUS + +int psmouse_smbus_module_init(void); +void psmouse_smbus_module_exit(void); + +struct i2c_board_info; + +int psmouse_smbus_init(struct psmouse *psmouse, + const struct i2c_board_info *board, + const void *pdata, size_t pdata_size, + bool leave_breadcrumbs); +void psmouse_smbus_cleanup(struct psmouse *psmouse); + +#else /* !CONFIG_MOUSE_PS2_SMBUS */ + +static inline int psmouse_smbus_module_init(void) +{ + return 0; +} + +static inline void psmouse_smbus_module_exit(void) +{ +} + +static inline void psmouse_smbus_cleanup(struct psmouse *psmouse) +{ +} + +#endif /* CONFIG_MOUSE_PS2_SMBUS */ #endif /* _PSMOUSE_H */ diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 597ee4b01d9f..d494c6c6aadc 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -29,6 +29,8 @@ #include <linux/input/mt.h> #include <linux/serio.h> #include <linux/libps2.h> +#include <linux/rmi.h> +#include <linux/i2c.h> #include <linux/slab.h> #include "psmouse.h" #include "synaptics.h" @@ -119,59 +121,8 @@ void synaptics_reset(struct psmouse *psmouse) synaptics_mode_cmd(psmouse, 0); } -#ifdef CONFIG_MOUSE_PS2_SYNAPTICS - -static bool cr48_profile_sensor; - -#define ANY_BOARD_ID 0 -struct min_max_quirk { - const char * const *pnp_ids; - struct { - unsigned long int min, max; - } board_id; - int x_min, x_max, y_min, y_max; -}; - -static const struct min_max_quirk min_max_pnpid_table[] = { - { - (const char * const []){"LEN0033", NULL}, - {ANY_BOARD_ID, ANY_BOARD_ID}, - 1024, 5052, 2258, 4832 - }, - { - (const char * const []){"LEN0042", NULL}, - {ANY_BOARD_ID, ANY_BOARD_ID}, - 1232, 5710, 1156, 4696 - }, - { - (const char * const []){"LEN0034", "LEN0036", "LEN0037", - "LEN0039", "LEN2002", "LEN2004", - NULL}, - {ANY_BOARD_ID, 2961}, - 1024, 5112, 2024, 4832 - }, - { - (const char * const []){"LEN2000", NULL}, - {ANY_BOARD_ID, ANY_BOARD_ID}, - 1024, 5113, 2021, 4832 - }, - { - (const char * const []){"LEN2001", NULL}, - {ANY_BOARD_ID, ANY_BOARD_ID}, - 1024, 5022, 2508, 4832 - }, - { - (const char * const []){"LEN2006", NULL}, - {2691, 2691}, - 1024, 5045, 2457, 4832 - }, - { - (const char * const []){"LEN2006", NULL}, - {ANY_BOARD_ID, ANY_BOARD_ID}, - 1264, 5675, 1171, 4688 - }, - { } -}; +#if defined(CONFIG_MOUSE_PS2_SYNAPTICS) || \ + defined(CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS) /* This list has been kindly provided by Synaptics. */ static const char * const topbuttonpad_pnp_ids[] = { @@ -211,64 +162,98 @@ static const char * const topbuttonpad_pnp_ids[] = { NULL }; -/* This list has been kindly provided by Synaptics. */ -static const char * const forcepad_pnp_ids[] = { - "SYN300D", - "SYN3014", +static const char * const smbus_pnp_ids[] = { + /* all of the topbuttonpad_pnp_ids are valid, we just add some extras */ + "LEN0048", /* X1 Carbon 3 */ + "LEN0046", /* X250 */ + "LEN004a", /* W541 */ + "LEN200f", /* T450s */ NULL }; -/***************************************************************************** - * Synaptics communications functions - ****************************************************************************/ - /* - * Synaptics touchpads report the y coordinate from bottom to top, which is - * opposite from what userspace expects. - * This function is used to invert y before reporting. + * Send a command to the synpatics touchpad by special commands */ -static int synaptics_invert_y(int y) +static int synaptics_send_cmd(struct psmouse *psmouse, + unsigned char c, unsigned char *param) { - return YMAX_NOMINAL + YMIN_NOMINAL - y; + int error; + + error = psmouse_sliced_command(psmouse, c); + if (error) + return error; + + error = ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO); + if (error) + return error; + + return 0; } /* - * Send a command to the synpatics touchpad by special commands + * Identify Touchpad + * See also the SYN_ID_* macros */ -static int synaptics_send_cmd(struct psmouse *psmouse, unsigned char c, unsigned char *param) +static int synaptics_identify(struct psmouse *psmouse, + struct synaptics_device_info *info) { - if (psmouse_sliced_command(psmouse, c)) - return -1; - if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO)) - return -1; - return 0; + unsigned char id[3]; + int error; + + error = synaptics_send_cmd(psmouse, SYN_QUE_IDENTIFY, id); + if (error) + return error; + + info->identity = (id[0] << 16) | (id[1] << 8) | id[2]; + return SYN_ID_IS_SYNAPTICS(info->identity) ? 0 : -ENXIO; } /* * Read the model-id bytes from the touchpad * see also SYN_MODEL_* macros */ -static int synaptics_model_id(struct psmouse *psmouse) +static int synaptics_model_id(struct psmouse *psmouse, + struct synaptics_device_info *info) { - struct synaptics_data *priv = psmouse->private; unsigned char mi[3]; + int error; - if (synaptics_send_cmd(psmouse, SYN_QUE_MODEL, mi)) - return -1; - priv->model_id = (mi[0]<<16) | (mi[1]<<8) | mi[2]; + error = synaptics_send_cmd(psmouse, SYN_QUE_MODEL, mi); + if (error) + return error; + + info->model_id = (mi[0] << 16) | (mi[1] << 8) | mi[2]; return 0; } -static int synaptics_more_extended_queries(struct psmouse *psmouse) +/* + * Read the firmware id from the touchpad + */ +static int synaptics_firmware_id(struct psmouse *psmouse, + struct synaptics_device_info *info) { - struct synaptics_data *priv = psmouse->private; - unsigned char buf[3]; + unsigned char fwid[3]; + int error; - if (synaptics_send_cmd(psmouse, SYN_QUE_MEXT_CAPAB_10, buf)) - return -1; + error = synaptics_send_cmd(psmouse, SYN_QUE_FIRMWARE_ID, fwid); + if (error) + return error; + + info->firmware_id = (fwid[0] << 16) | (fwid[1] << 8) | fwid[2]; + return 0; +} + +static int synaptics_more_extended_queries(struct psmouse *psmouse, + struct synaptics_device_info *info) +{ + unsigned char buf[3]; + int error; - priv->ext_cap_10 = (buf[0]<<16) | (buf[1]<<8) | buf[2]; + error = synaptics_send_cmd(psmouse, SYN_QUE_MEXT_CAPAB_10, buf); + if (error) + return error; + info->ext_cap_10 = (buf[0] << 16) | (buf[1] << 8) | buf[2]; return 0; } @@ -276,36 +261,25 @@ static int synaptics_more_extended_queries(struct psmouse *psmouse) * Read the board id and the "More Extended Queries" from the touchpad * The board id is encoded in the "QUERY MODES" response */ -static int synaptics_query_modes(struct psmouse *psmouse) +static int synaptics_query_modes(struct psmouse *psmouse, + struct synaptics_device_info *info) { - struct synaptics_data *priv = psmouse->private; unsigned char bid[3]; + int error; /* firmwares prior 7.5 have no board_id encoded */ - if (SYN_ID_FULL(priv->identity) < 0x705) + if (SYN_ID_FULL(info->identity) < 0x705) return 0; - if (synaptics_send_cmd(psmouse, SYN_QUE_MODES, bid)) - return -1; - priv->board_id = ((bid[0] & 0xfc) << 6) | bid[1]; + error = synaptics_send_cmd(psmouse, SYN_QUE_MODES, bid); + if (error) + return error; - if (SYN_MEXT_CAP_BIT(bid[0])) - return synaptics_more_extended_queries(psmouse); + info->board_id = ((bid[0] & 0xfc) << 6) | bid[1]; - return 0; -} - -/* - * Read the firmware id from the touchpad - */ -static int synaptics_firmware_id(struct psmouse *psmouse) -{ - struct synaptics_data *priv = psmouse->private; - unsigned char fwid[3]; + if (SYN_MEXT_CAP_BIT(bid[0])) + return synaptics_more_extended_queries(psmouse, info); - if (synaptics_send_cmd(psmouse, SYN_QUE_FIRMWARE_ID, fwid)) - return -1; - priv->firmware_id = (fwid[0] << 16) | (fwid[1] << 8) | fwid[2]; return 0; } @@ -313,139 +287,239 @@ static int synaptics_firmware_id(struct psmouse *psmouse) * Read the capability-bits from the touchpad * see also the SYN_CAP_* macros */ -static int synaptics_capability(struct psmouse *psmouse) +static int synaptics_capability(struct psmouse *psmouse, + struct synaptics_device_info *info) { - struct synaptics_data *priv = psmouse->private; unsigned char cap[3]; + int error; - if (synaptics_send_cmd(psmouse, SYN_QUE_CAPABILITIES, cap)) - return -1; - priv->capabilities = (cap[0] << 16) | (cap[1] << 8) | cap[2]; - priv->ext_cap = priv->ext_cap_0c = 0; + error = synaptics_send_cmd(psmouse, SYN_QUE_CAPABILITIES, cap); + if (error) + return error; + + info->capabilities = (cap[0] << 16) | (cap[1] << 8) | cap[2]; + info->ext_cap = info->ext_cap_0c = 0; /* * Older firmwares had submodel ID fixed to 0x47 */ - if (SYN_ID_FULL(priv->identity) < 0x705 && - SYN_CAP_SUBMODEL_ID(priv->capabilities) != 0x47) { - return -1; + if (SYN_ID_FULL(info->identity) < 0x705 && + SYN_CAP_SUBMODEL_ID(info->capabilities) != 0x47) { + return -ENXIO; } /* * Unless capExtended is set the rest of the flags should be ignored */ - if (!SYN_CAP_EXTENDED(priv->capabilities)) - priv->capabilities = 0; + if (!SYN_CAP_EXTENDED(info->capabilities)) + info->capabilities = 0; - if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 1) { + if (SYN_EXT_CAP_REQUESTS(info->capabilities) >= 1) { if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_CAPAB, cap)) { psmouse_warn(psmouse, "device claims to have extended capabilities, but I'm not able to read them.\n"); } else { - priv->ext_cap = (cap[0] << 16) | (cap[1] << 8) | cap[2]; + info->ext_cap = (cap[0] << 16) | (cap[1] << 8) | cap[2]; /* * if nExtBtn is greater than 8 it should be considered * invalid and treated as 0 */ - if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) > 8) - priv->ext_cap &= 0xff0fff; + if (SYN_CAP_MULTI_BUTTON_NO(info->ext_cap) > 8) + info->ext_cap &= 0xff0fff; } } - if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 4) { - if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_CAPAB_0C, cap)) { + if (SYN_EXT_CAP_REQUESTS(info->capabilities) >= 4) { + error = synaptics_send_cmd(psmouse, SYN_QUE_EXT_CAPAB_0C, cap); + if (error) psmouse_warn(psmouse, "device claims to have extended capability 0x0c, but I'm not able to read it.\n"); - } else { - priv->ext_cap_0c = (cap[0] << 16) | (cap[1] << 8) | cap[2]; - } + else + info->ext_cap_0c = + (cap[0] << 16) | (cap[1] << 8) | cap[2]; } return 0; } /* - * Identify Touchpad - * See also the SYN_ID_* macros - */ -static int synaptics_identify(struct psmouse *psmouse) -{ - struct synaptics_data *priv = psmouse->private; - unsigned char id[3]; - - if (synaptics_send_cmd(psmouse, SYN_QUE_IDENTIFY, id)) - return -1; - priv->identity = (id[0]<<16) | (id[1]<<8) | id[2]; - if (SYN_ID_IS_SYNAPTICS(priv->identity)) - return 0; - return -1; -} - -/* * Read touchpad resolution and maximum reported coordinates * Resolution is left zero if touchpad does not support the query */ - -static int synaptics_resolution(struct psmouse *psmouse) +static int synaptics_resolution(struct psmouse *psmouse, + struct synaptics_device_info *info) { - struct synaptics_data *priv = psmouse->private; unsigned char resp[3]; + int error; - if (SYN_ID_MAJOR(priv->identity) < 4) + if (SYN_ID_MAJOR(info->identity) < 4) return 0; - if (synaptics_send_cmd(psmouse, SYN_QUE_RESOLUTION, resp) == 0) { + error = synaptics_send_cmd(psmouse, SYN_QUE_RESOLUTION, resp); + if (!error) { if (resp[0] != 0 && (resp[1] & 0x80) && resp[2] != 0) { - priv->x_res = resp[0]; /* x resolution in units/mm */ - priv->y_res = resp[2]; /* y resolution in units/mm */ + info->x_res = resp[0]; /* x resolution in units/mm */ + info->y_res = resp[2]; /* y resolution in units/mm */ } } - if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 5 && - SYN_CAP_MAX_DIMENSIONS(priv->ext_cap_0c)) { - if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_MAX_COORDS, resp)) { + if (SYN_EXT_CAP_REQUESTS(info->capabilities) >= 5 && + SYN_CAP_MAX_DIMENSIONS(info->ext_cap_0c)) { + error = synaptics_send_cmd(psmouse, + SYN_QUE_EXT_MAX_COORDS, resp); + if (error) { psmouse_warn(psmouse, "device claims to have max coordinates query, but I'm not able to read it.\n"); } else { - priv->x_max = (resp[0] << 5) | ((resp[1] & 0x0f) << 1); - priv->y_max = (resp[2] << 5) | ((resp[1] & 0xf0) >> 3); + info->x_max = (resp[0] << 5) | ((resp[1] & 0x0f) << 1); + info->y_max = (resp[2] << 5) | ((resp[1] & 0xf0) >> 3); psmouse_info(psmouse, "queried max coordinates: x [..%d], y [..%d]\n", - priv->x_max, priv->y_max); + info->x_max, info->y_max); } } - if (SYN_CAP_MIN_DIMENSIONS(priv->ext_cap_0c) && - (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 7 || + if (SYN_CAP_MIN_DIMENSIONS(info->ext_cap_0c) && + (SYN_EXT_CAP_REQUESTS(info->capabilities) >= 7 || /* * Firmware v8.1 does not report proper number of extended * capabilities, but has been proven to report correct min * coordinates. */ - SYN_ID_FULL(priv->identity) == 0x801)) { - if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_MIN_COORDS, resp)) { + SYN_ID_FULL(info->identity) == 0x801)) { + error = synaptics_send_cmd(psmouse, + SYN_QUE_EXT_MIN_COORDS, resp); + if (error) { psmouse_warn(psmouse, "device claims to have min coordinates query, but I'm not able to read it.\n"); } else { - priv->x_min = (resp[0] << 5) | ((resp[1] & 0x0f) << 1); - priv->y_min = (resp[2] << 5) | ((resp[1] & 0xf0) >> 3); + info->x_min = (resp[0] << 5) | ((resp[1] & 0x0f) << 1); + info->y_min = (resp[2] << 5) | ((resp[1] & 0xf0) >> 3); psmouse_info(psmouse, "queried min coordinates: x [%d..], y [%d..]\n", - priv->x_min, priv->y_min); + info->x_min, info->y_min); } } return 0; } +static int synaptics_query_hardware(struct psmouse *psmouse, + struct synaptics_device_info *info) +{ + int error; + + error = synaptics_identify(psmouse, info); + if (error) + return error; + + error = synaptics_model_id(psmouse, info); + if (error) + return error; + + error = synaptics_firmware_id(psmouse, info); + if (error) + return error; + + error = synaptics_query_modes(psmouse, info); + if (error) + return error; + + error = synaptics_capability(psmouse, info); + if (error) + return error; + + error = synaptics_resolution(psmouse, info); + if (error) + return error; + + return 0; +} + +#endif /* CONFIG_MOUSE_PS2_SYNAPTICS || CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS */ + +#ifdef CONFIG_MOUSE_PS2_SYNAPTICS + +static bool cr48_profile_sensor; + +#define ANY_BOARD_ID 0 +struct min_max_quirk { + const char * const *pnp_ids; + struct { + u32 min, max; + } board_id; + u32 x_min, x_max, y_min, y_max; +}; + +static const struct min_max_quirk min_max_pnpid_table[] = { + { + (const char * const []){"LEN0033", NULL}, + {ANY_BOARD_ID, ANY_BOARD_ID}, + 1024, 5052, 2258, 4832 + }, + { + (const char * const []){"LEN0042", NULL}, + {ANY_BOARD_ID, ANY_BOARD_ID}, + 1232, 5710, 1156, 4696 + }, + { + (const char * const []){"LEN0034", "LEN0036", "LEN0037", + "LEN0039", "LEN2002", "LEN2004", + NULL}, + {ANY_BOARD_ID, 2961}, + 1024, 5112, 2024, 4832 + }, + { + (const char * const []){"LEN2000", NULL}, + {ANY_BOARD_ID, ANY_BOARD_ID}, + 1024, 5113, 2021, 4832 + }, + { + (const char * const []){"LEN2001", NULL}, + {ANY_BOARD_ID, ANY_BOARD_ID}, + 1024, 5022, 2508, 4832 + }, + { + (const char * const []){"LEN2006", NULL}, + {2691, 2691}, + 1024, 5045, 2457, 4832 + }, + { + (const char * const []){"LEN2006", NULL}, + {ANY_BOARD_ID, ANY_BOARD_ID}, + 1264, 5675, 1171, 4688 + }, + { } +}; + +/* This list has been kindly provided by Synaptics. */ +static const char * const forcepad_pnp_ids[] = { + "SYN300D", + "SYN3014", + NULL +}; + +/***************************************************************************** + * Synaptics communications functions + ****************************************************************************/ + +/* + * Synaptics touchpads report the y coordinate from bottom to top, which is + * opposite from what userspace expects. + * This function is used to invert y before reporting. + */ +static int synaptics_invert_y(int y) +{ + return YMAX_NOMINAL + YMIN_NOMINAL - y; +} + /* * Apply quirk(s) if the hardware matches */ - -static void synaptics_apply_quirks(struct psmouse *psmouse) +static void synaptics_apply_quirks(struct psmouse *psmouse, + struct synaptics_device_info *info) { - struct synaptics_data *priv = psmouse->private; int i; for (i = 0; min_max_pnpid_table[i].pnp_ids; i++) { @@ -454,52 +528,32 @@ static void synaptics_apply_quirks(struct psmouse *psmouse) continue; if (min_max_pnpid_table[i].board_id.min != ANY_BOARD_ID && - priv->board_id < min_max_pnpid_table[i].board_id.min) + info->board_id < min_max_pnpid_table[i].board_id.min) continue; if (min_max_pnpid_table[i].board_id.max != ANY_BOARD_ID && - priv->board_id > min_max_pnpid_table[i].board_id.max) + info->board_id > min_max_pnpid_table[i].board_id.max) continue; - priv->x_min = min_max_pnpid_table[i].x_min; - priv->x_max = min_max_pnpid_table[i].x_max; - priv->y_min = min_max_pnpid_table[i].y_min; - priv->y_max = min_max_pnpid_table[i].y_max; + info->x_min = min_max_pnpid_table[i].x_min; + info->x_max = min_max_pnpid_table[i].x_max; + info->y_min = min_max_pnpid_table[i].y_min; + info->y_max = min_max_pnpid_table[i].y_max; psmouse_info(psmouse, "quirked min/max coordinates: x [%d..%d], y [%d..%d]\n", - priv->x_min, priv->x_max, - priv->y_min, priv->y_max); + info->x_min, info->x_max, + info->y_min, info->y_max); break; } } -static int synaptics_query_hardware(struct psmouse *psmouse) -{ - if (synaptics_identify(psmouse)) - return -1; - if (synaptics_model_id(psmouse)) - return -1; - if (synaptics_firmware_id(psmouse)) - return -1; - if (synaptics_query_modes(psmouse)) - return -1; - if (synaptics_capability(psmouse)) - return -1; - if (synaptics_resolution(psmouse)) - return -1; - - synaptics_apply_quirks(psmouse); - - return 0; -} - static int synaptics_set_advanced_gesture_mode(struct psmouse *psmouse) { static unsigned char param = 0xc8; struct synaptics_data *priv = psmouse->private; - if (!(SYN_CAP_ADV_GESTURE(priv->ext_cap_0c) || - SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c))) + if (!(SYN_CAP_ADV_GESTURE(priv->info.ext_cap_0c) || + SYN_CAP_IMAGE_SENSOR(priv->info.ext_cap_0c))) return 0; if (psmouse_sliced_command(psmouse, SYN_QUE_MODEL)) @@ -509,7 +563,7 @@ static int synaptics_set_advanced_gesture_mode(struct psmouse *psmouse) return -1; /* Advanced gesture mode also sends multi finger data */ - priv->capabilities |= BIT(1); + priv->info.capabilities |= BIT(1); return 0; } @@ -525,7 +579,7 @@ static int synaptics_set_mode(struct psmouse *psmouse) priv->mode |= SYN_BIT_DISABLE_GESTURE; if (psmouse->rate >= 80) priv->mode |= SYN_BIT_HIGH_RATE; - if (SYN_CAP_EXTENDED(priv->capabilities)) + if (SYN_CAP_EXTENDED(priv->info.capabilities)) priv->mode |= SYN_BIT_W_MODE; if (synaptics_mode_cmd(psmouse, priv->mode)) @@ -693,7 +747,7 @@ static void synaptics_parse_ext_buttons(const unsigned char buf[], struct synaptics_hw_state *hw) { unsigned int ext_bits = - (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) + 1) >> 1; + (SYN_CAP_MULTI_BUTTON_NO(priv->info.ext_cap) + 1) >> 1; unsigned int ext_mask = GENMASK(ext_bits - 1, 0); hw->ext_buttons = buf[4] & ext_mask; @@ -706,13 +760,13 @@ static int synaptics_parse_hw_state(const unsigned char buf[], { memset(hw, 0, sizeof(struct synaptics_hw_state)); - if (SYN_MODEL_NEWABS(priv->model_id)) { + if (SYN_MODEL_NEWABS(priv->info.model_id)) { hw->w = (((buf[0] & 0x30) >> 2) | ((buf[0] & 0x04) >> 1) | ((buf[3] & 0x04) >> 2)); - if ((SYN_CAP_ADV_GESTURE(priv->ext_cap_0c) || - SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) && + if ((SYN_CAP_ADV_GESTURE(priv->info.ext_cap_0c) || + SYN_CAP_IMAGE_SENSOR(priv->info.ext_cap_0c)) && hw->w == 2) { synaptics_parse_agm(buf, priv, hw); return 1; @@ -765,7 +819,7 @@ static int synaptics_parse_hw_state(const unsigned char buf[], hw->left = priv->report_press; - } else if (SYN_CAP_CLICKPAD(priv->ext_cap_0c)) { + } else if (SYN_CAP_CLICKPAD(priv->info.ext_cap_0c)) { /* * Clickpad's button is transmitted as middle button, * however, since it is primary button, we will report @@ -773,18 +827,18 @@ static int synaptics_parse_hw_state(const unsigned char buf[], */ hw->left = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0; - } else if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) { + } else if (SYN_CAP_MIDDLE_BUTTON(priv->info.capabilities)) { hw->middle = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0; if (hw->w == 2) hw->scroll = (signed char)(buf[1]); } - if (SYN_CAP_FOUR_BUTTON(priv->capabilities)) { + if (SYN_CAP_FOUR_BUTTON(priv->info.capabilities)) { hw->up = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0; hw->down = ((buf[0] ^ buf[3]) & 0x02) ? 1 : 0; } - if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) > 0 && + if (SYN_CAP_MULTI_BUTTON_NO(priv->info.ext_cap) > 0 && ((buf[0] ^ buf[3]) & 0x02)) { synaptics_parse_ext_buttons(buf, priv, hw); } @@ -853,19 +907,19 @@ static void synaptics_report_ext_buttons(struct psmouse *psmouse, { struct input_dev *dev = psmouse->dev; struct synaptics_data *priv = psmouse->private; - int ext_bits = (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) + 1) >> 1; + int ext_bits = (SYN_CAP_MULTI_BUTTON_NO(priv->info.ext_cap) + 1) >> 1; int i; - if (!SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap)) + if (!SYN_CAP_MULTI_BUTTON_NO(priv->info.ext_cap)) return; /* Bug in FW 8.1 & 8.2, buttons are reported only when ExtBit is 1 */ - if ((SYN_ID_FULL(priv->identity) == 0x801 || - SYN_ID_FULL(priv->identity) == 0x802) && + if ((SYN_ID_FULL(priv->info.identity) == 0x801 || + SYN_ID_FULL(priv->info.identity) == 0x802) && !((psmouse->packet[0] ^ psmouse->packet[3]) & 0x02)) return; - if (!SYN_CAP_EXT_BUTTONS_STICK(priv->ext_cap_10)) { + if (!SYN_CAP_EXT_BUTTONS_STICK(priv->info.ext_cap_10)) { for (i = 0; i < ext_bits; i++) { input_report_key(dev, BTN_0 + 2 * i, hw->ext_buttons & (1 << i)); @@ -884,9 +938,9 @@ static void synaptics_report_ext_buttons(struct psmouse *psmouse, u8 pt_buttons; /* The trackstick expects at most 3 buttons */ - pt_buttons = SYN_CAP_EXT_BUTTON_STICK_L(hw->ext_buttons) | - SYN_CAP_EXT_BUTTON_STICK_R(hw->ext_buttons) << 1 | - SYN_CAP_EXT_BUTTON_STICK_M(hw->ext_buttons) << 2; + pt_buttons = SYN_EXT_BUTTON_STICK_L(hw->ext_buttons) | + SYN_EXT_BUTTON_STICK_R(hw->ext_buttons) << 1 | + SYN_EXT_BUTTON_STICK_M(hw->ext_buttons) << 2; serio_interrupt(priv->pt_port, PSMOUSE_OOB_EXTRA_BTNS, SERIO_OOB_DATA); @@ -903,10 +957,10 @@ static void synaptics_report_buttons(struct psmouse *psmouse, input_report_key(dev, BTN_LEFT, hw->left); input_report_key(dev, BTN_RIGHT, hw->right); - if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) + if (SYN_CAP_MIDDLE_BUTTON(priv->info.capabilities)) input_report_key(dev, BTN_MIDDLE, hw->middle); - if (SYN_CAP_FOUR_BUTTON(priv->capabilities)) { + if (SYN_CAP_FOUR_BUTTON(priv->info.capabilities)) { input_report_key(dev, BTN_FORWARD, hw->up); input_report_key(dev, BTN_BACK, hw->down); } @@ -931,7 +985,7 @@ static void synaptics_report_mt_data(struct psmouse *psmouse, pos[i].y = synaptics_invert_y(hw[i]->y); } - input_mt_assign_slots(dev, slot, pos, nsemi, DMAX * priv->x_res); + input_mt_assign_slots(dev, slot, pos, nsemi, DMAX * priv->info.x_res); for (i = 0; i < nsemi; i++) { input_mt_slot(dev, slot[i]); @@ -985,6 +1039,7 @@ static void synaptics_process_packet(struct psmouse *psmouse) { struct input_dev *dev = psmouse->dev; struct synaptics_data *priv = psmouse->private; + struct synaptics_device_info *info = &priv->info; struct synaptics_hw_state hw; int num_fingers; int finger_width; @@ -992,7 +1047,7 @@ static void synaptics_process_packet(struct psmouse *psmouse) if (synaptics_parse_hw_state(psmouse->packet, priv, &hw)) return; - if (SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) { + if (SYN_CAP_IMAGE_SENSOR(info->ext_cap_0c)) { synaptics_image_sensor_process(psmouse, &hw); return; } @@ -1020,18 +1075,18 @@ static void synaptics_process_packet(struct psmouse *psmouse) if (hw.z > 0 && hw.x > 1) { num_fingers = 1; finger_width = 5; - if (SYN_CAP_EXTENDED(priv->capabilities)) { + if (SYN_CAP_EXTENDED(info->capabilities)) { switch (hw.w) { case 0 ... 1: - if (SYN_CAP_MULTIFINGER(priv->capabilities)) + if (SYN_CAP_MULTIFINGER(info->capabilities)) num_fingers = hw.w + 2; break; case 2: - if (SYN_MODEL_PEN(priv->model_id)) + if (SYN_MODEL_PEN(info->model_id)) ; /* Nothing, treat a pen as a single finger */ break; case 4 ... 15: - if (SYN_CAP_PALMDETECT(priv->capabilities)) + if (SYN_CAP_PALMDETECT(info->capabilities)) finger_width = hw.w; break; } @@ -1046,7 +1101,7 @@ static void synaptics_process_packet(struct psmouse *psmouse) return; } - if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c)) + if (SYN_CAP_ADV_GESTURE(info->ext_cap_0c)) synaptics_report_semi_mt_data(dev, &hw, &priv->agm, num_fingers); @@ -1063,11 +1118,11 @@ static void synaptics_process_packet(struct psmouse *psmouse) } input_report_abs(dev, ABS_PRESSURE, hw.z); - if (SYN_CAP_PALMDETECT(priv->capabilities)) + if (SYN_CAP_PALMDETECT(info->capabilities)) input_report_abs(dev, ABS_TOOL_WIDTH, finger_width); input_report_key(dev, BTN_TOOL_FINGER, num_fingers == 1); - if (SYN_CAP_MULTIFINGER(priv->capabilities)) { + if (SYN_CAP_MULTIFINGER(info->capabilities)) { input_report_key(dev, BTN_TOOL_DOUBLETAP, num_fingers == 2); input_report_key(dev, BTN_TOOL_TRIPLETAP, num_fingers == 3); } @@ -1129,7 +1184,7 @@ static psmouse_ret_t synaptics_process_byte(struct psmouse *psmouse) if (unlikely(priv->pkt_type == SYN_NEWABS)) priv->pkt_type = synaptics_detect_pkt_type(psmouse); - if (SYN_CAP_PASS_THROUGH(priv->capabilities) && + if (SYN_CAP_PASS_THROUGH(priv->info.capabilities) && synaptics_is_pt_packet(psmouse->packet)) { if (priv->pt_port) synaptics_pass_pt_packet(priv->pt_port, @@ -1148,26 +1203,27 @@ static psmouse_ret_t synaptics_process_byte(struct psmouse *psmouse) * Driver initialization/cleanup functions ****************************************************************************/ static void set_abs_position_params(struct input_dev *dev, - struct synaptics_data *priv, int x_code, - int y_code) + struct synaptics_device_info *info, + int x_code, int y_code) { - int x_min = priv->x_min ?: XMIN_NOMINAL; - int x_max = priv->x_max ?: XMAX_NOMINAL; - int y_min = priv->y_min ?: YMIN_NOMINAL; - int y_max = priv->y_max ?: YMAX_NOMINAL; - int fuzz = SYN_CAP_REDUCED_FILTERING(priv->ext_cap_0c) ? + int x_min = info->x_min ?: XMIN_NOMINAL; + int x_max = info->x_max ?: XMAX_NOMINAL; + int y_min = info->y_min ?: YMIN_NOMINAL; + int y_max = info->y_max ?: YMAX_NOMINAL; + int fuzz = SYN_CAP_REDUCED_FILTERING(info->ext_cap_0c) ? SYN_REDUCED_FILTER_FUZZ : 0; input_set_abs_params(dev, x_code, x_min, x_max, fuzz, 0); input_set_abs_params(dev, y_code, y_min, y_max, fuzz, 0); - input_abs_set_res(dev, x_code, priv->x_res); - input_abs_set_res(dev, y_code, priv->y_res); + input_abs_set_res(dev, x_code, info->x_res); + input_abs_set_res(dev, y_code, info->y_res); } static void set_input_params(struct psmouse *psmouse, struct synaptics_data *priv) { struct input_dev *dev = psmouse->dev; + struct synaptics_device_info *info = &priv->info; int i; /* Things that apply to both modes */ @@ -1176,7 +1232,7 @@ static void set_input_params(struct psmouse *psmouse, __set_bit(BTN_LEFT, dev->keybit); __set_bit(BTN_RIGHT, dev->keybit); - if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) + if (SYN_CAP_MIDDLE_BUTTON(info->capabilities)) __set_bit(BTN_MIDDLE, dev->keybit); if (!priv->absolute_mode) { @@ -1189,15 +1245,15 @@ static void set_input_params(struct psmouse *psmouse, /* Absolute mode */ __set_bit(EV_ABS, dev->evbit); - set_abs_position_params(dev, priv, ABS_X, ABS_Y); + set_abs_position_params(dev, &priv->info, ABS_X, ABS_Y); input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0); if (cr48_profile_sensor) input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 255, 0, 0); - if (SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) { - set_abs_position_params(dev, priv, ABS_MT_POSITION_X, - ABS_MT_POSITION_Y); + if (SYN_CAP_IMAGE_SENSOR(info->ext_cap_0c)) { + set_abs_position_params(dev, info, + ABS_MT_POSITION_X, ABS_MT_POSITION_Y); /* Image sensors can report per-contact pressure */ input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 255, 0, 0); input_mt_init_slots(dev, 2, INPUT_MT_POINTER | INPUT_MT_TRACK); @@ -1205,9 +1261,9 @@ static void set_input_params(struct psmouse *psmouse, /* Image sensors can signal 4 and 5 finger clicks */ __set_bit(BTN_TOOL_QUADTAP, dev->keybit); __set_bit(BTN_TOOL_QUINTTAP, dev->keybit); - } else if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c)) { - set_abs_position_params(dev, priv, ABS_MT_POSITION_X, - ABS_MT_POSITION_Y); + } else if (SYN_CAP_ADV_GESTURE(info->ext_cap_0c)) { + set_abs_position_params(dev, info, + ABS_MT_POSITION_X, ABS_MT_POSITION_Y); /* * Profile sensor in CR-48 tracks contacts reasonably well, * other non-image sensors with AGM use semi-mt. @@ -1218,35 +1274,35 @@ static void set_input_params(struct psmouse *psmouse, INPUT_MT_TRACK : INPUT_MT_SEMI_MT)); } - if (SYN_CAP_PALMDETECT(priv->capabilities)) + if (SYN_CAP_PALMDETECT(info->capabilities)) input_set_abs_params(dev, ABS_TOOL_WIDTH, 0, 15, 0, 0); __set_bit(BTN_TOUCH, dev->keybit); __set_bit(BTN_TOOL_FINGER, dev->keybit); - if (SYN_CAP_MULTIFINGER(priv->capabilities)) { + if (SYN_CAP_MULTIFINGER(info->capabilities)) { __set_bit(BTN_TOOL_DOUBLETAP, dev->keybit); __set_bit(BTN_TOOL_TRIPLETAP, dev->keybit); } - if (SYN_CAP_FOUR_BUTTON(priv->capabilities) || - SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) { + if (SYN_CAP_FOUR_BUTTON(info->capabilities) || + SYN_CAP_MIDDLE_BUTTON(info->capabilities)) { __set_bit(BTN_FORWARD, dev->keybit); __set_bit(BTN_BACK, dev->keybit); } - if (!SYN_CAP_EXT_BUTTONS_STICK(priv->ext_cap_10)) - for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap); i++) + if (!SYN_CAP_EXT_BUTTONS_STICK(info->ext_cap_10)) + for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(info->ext_cap); i++) __set_bit(BTN_0 + i, dev->keybit); __clear_bit(EV_REL, dev->evbit); __clear_bit(REL_X, dev->relbit); __clear_bit(REL_Y, dev->relbit); - if (SYN_CAP_CLICKPAD(priv->ext_cap_0c)) { + if (SYN_CAP_CLICKPAD(info->ext_cap_0c)) { __set_bit(INPUT_PROP_BUTTONPAD, dev->propbit); if (psmouse_matches_pnp_id(psmouse, topbuttonpad_pnp_ids) && - !SYN_CAP_EXT_BUTTONS_STICK(priv->ext_cap_10)) + !SYN_CAP_EXT_BUTTONS_STICK(info->ext_cap_10)) __set_bit(INPUT_PROP_TOPBUTTONPAD, dev->propbit); /* Clickpads report only left button */ __clear_bit(BTN_RIGHT, dev->keybit); @@ -1300,7 +1356,14 @@ static void synaptics_disconnect(struct psmouse *psmouse) { struct synaptics_data *priv = psmouse->private; - if (!priv->absolute_mode && SYN_ID_DISGEST_SUPPORTED(priv->identity)) + /* + * We might have left a breadcrumb when trying to + * set up SMbus companion. + */ + psmouse_smbus_cleanup(psmouse); + + if (!priv->absolute_mode && + SYN_ID_DISGEST_SUPPORTED(priv->info.identity)) device_remove_file(&psmouse->ps2dev.serio->dev, &psmouse_attr_disable_gesture.dattr); @@ -1312,7 +1375,7 @@ static void synaptics_disconnect(struct psmouse *psmouse) static int synaptics_reconnect(struct psmouse *psmouse) { struct synaptics_data *priv = psmouse->private; - struct synaptics_data old_priv = *priv; + struct synaptics_device_info info; unsigned char param[2]; int retry = 0; int error; @@ -1339,7 +1402,7 @@ static int synaptics_reconnect(struct psmouse *psmouse) if (retry > 1) psmouse_dbg(psmouse, "reconnected after %d tries\n", retry); - if (synaptics_query_hardware(psmouse)) { + if (synaptics_query_hardware(psmouse, &info)) { psmouse_err(psmouse, "Unable to query device.\n"); return -1; } @@ -1349,16 +1412,16 @@ static int synaptics_reconnect(struct psmouse *psmouse) return -1; } - if (old_priv.identity != priv->identity || - old_priv.model_id != priv->model_id || - old_priv.capabilities != priv->capabilities || - old_priv.ext_cap != priv->ext_cap) { + if (info.identity != priv->info.identity || + info.model_id != priv->info.model_id || + info.capabilities != priv->info.capabilities || + info.ext_cap != priv->info.ext_cap) { psmouse_err(psmouse, - "hardware appears to be different: id(%ld-%ld), model(%ld-%ld), caps(%lx-%lx), ext(%lx-%lx).\n", - old_priv.identity, priv->identity, - old_priv.model_id, priv->model_id, - old_priv.capabilities, priv->capabilities, - old_priv.ext_cap, priv->ext_cap); + "hardware appears to be different: id(%u-%u), model(%u-%u), caps(%x-%x), ext(%x-%x).\n", + priv->info.identity, info.identity, + priv->info.model_id, info.model_id, + priv->info.capabilities, info.capabilities, + priv->info.ext_cap, info.ext_cap); return -1; } @@ -1439,36 +1502,22 @@ void __init synaptics_module_init(void) cr48_profile_sensor = dmi_check_system(cr48_dmi_table); } -static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode) +static int synaptics_init_ps2(struct psmouse *psmouse, + struct synaptics_device_info *info, + bool absolute_mode) { struct synaptics_data *priv; - int err = -1; + int err; - /* - * The OLPC XO has issues with Synaptics' absolute mode; the constant - * packet spew overloads the EC such that key presses on the keyboard - * are missed. Given that, don't even attempt to use Absolute mode. - * Relative mode seems to work just fine. - */ - if (absolute_mode && broken_olpc_ec) { - psmouse_info(psmouse, - "OLPC XO detected, not enabling Synaptics protocol.\n"); - return -ENODEV; - } + synaptics_apply_quirks(psmouse, info); psmouse->private = priv = kzalloc(sizeof(struct synaptics_data), GFP_KERNEL); if (!priv) return -ENOMEM; - psmouse_reset(psmouse); - - if (synaptics_query_hardware(psmouse)) { - psmouse_err(psmouse, "Unable to query device.\n"); - goto init_fail; - } - + priv->info = *info; priv->absolute_mode = absolute_mode; - if (SYN_ID_DISGEST_SUPPORTED(priv->identity)) + if (SYN_ID_DISGEST_SUPPORTED(info->identity)) priv->disable_gesture = true; /* @@ -1477,20 +1526,22 @@ static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode) */ priv->is_forcepad = psmouse_matches_pnp_id(psmouse, forcepad_pnp_ids); - if (synaptics_set_mode(psmouse)) { + err = synaptics_set_mode(psmouse); + if (err) { psmouse_err(psmouse, "Unable to initialize device.\n"); goto init_fail; } - priv->pkt_type = SYN_MODEL_NEWABS(priv->model_id) ? SYN_NEWABS : SYN_OLDABS; + priv->pkt_type = SYN_MODEL_NEWABS(info->model_id) ? + SYN_NEWABS : SYN_OLDABS; psmouse_info(psmouse, - "Touchpad model: %ld, fw: %ld.%ld, id: %#lx, caps: %#lx/%#lx/%#lx/%#lx, board id: %lu, fw id: %lu\n", - SYN_ID_MODEL(priv->identity), - SYN_ID_MAJOR(priv->identity), SYN_ID_MINOR(priv->identity), - priv->model_id, - priv->capabilities, priv->ext_cap, priv->ext_cap_0c, - priv->ext_cap_10, priv->board_id, priv->firmware_id); + "Touchpad model: %u, fw: %u.%u, id: %#x, caps: %#x/%#x/%#x/%#x, board id: %u, fw id: %u\n", + SYN_ID_MODEL(info->identity), + SYN_ID_MAJOR(info->identity), SYN_ID_MINOR(info->identity), + info->model_id, + info->capabilities, info->ext_cap, info->ext_cap_0c, + info->ext_cap_10, info->board_id, info->firmware_id); set_input_params(psmouse, priv); @@ -1501,8 +1552,8 @@ static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode) * Hardware info bits seem to be good candidates as they * are documented to be for Synaptics corp. internal use. */ - psmouse->model = ((priv->model_id & 0x00ff0000) >> 8) | - (priv->model_id & 0x000000ff); + psmouse->model = ((info->model_id & 0x00ff0000) >> 8) | + (info->model_id & 0x000000ff); if (absolute_mode) { psmouse->protocol_handler = synaptics_process_byte; @@ -1520,7 +1571,7 @@ static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode) /* Synaptics can usually stay in sync without extra help */ psmouse->resync_time = 0; - if (SYN_CAP_PASS_THROUGH(priv->capabilities)) + if (SYN_CAP_PASS_THROUGH(info->capabilities)) synaptics_pt_create(psmouse); /* @@ -1535,7 +1586,7 @@ static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode) psmouse->rate = 40; } - if (!priv->absolute_mode && SYN_ID_DISGEST_SUPPORTED(priv->identity)) { + if (!priv->absolute_mode && SYN_ID_DISGEST_SUPPORTED(info->identity)) { err = device_create_file(&psmouse->ps2dev.serio->dev, &psmouse_attr_disable_gesture.dattr); if (err) { @@ -1553,7 +1604,23 @@ static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode) return err; } -int synaptics_init(struct psmouse *psmouse) +static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode) +{ + struct synaptics_device_info info; + int error; + + psmouse_reset(psmouse); + + error = synaptics_query_hardware(psmouse, &info); + if (error) { + psmouse_err(psmouse, "Unable to query device: %d\n", error); + return error; + } + + return synaptics_init_ps2(psmouse, &info, absolute_mode); +} + +int synaptics_init_absolute(struct psmouse *psmouse) { return __synaptics_init(psmouse, true); } @@ -1563,15 +1630,204 @@ int synaptics_init_relative(struct psmouse *psmouse) return __synaptics_init(psmouse, false); } +static int synaptics_setup_ps2(struct psmouse *psmouse, + struct synaptics_device_info *info) +{ + bool absolute_mode = true; + int error; + + /* + * The OLPC XO has issues with Synaptics' absolute mode; the constant + * packet spew overloads the EC such that key presses on the keyboard + * are missed. Given that, don't even attempt to use Absolute mode. + * Relative mode seems to work just fine. + */ + if (broken_olpc_ec) { + psmouse_info(psmouse, + "OLPC XO detected, forcing relative protocol.\n"); + absolute_mode = false; + } + + error = synaptics_init_ps2(psmouse, info, absolute_mode); + if (error) + return error; + + return absolute_mode ? PSMOUSE_SYNAPTICS : PSMOUSE_SYNAPTICS_RELATIVE; +} + #else /* CONFIG_MOUSE_PS2_SYNAPTICS */ void __init synaptics_module_init(void) { } -int synaptics_init(struct psmouse *psmouse) +static int __maybe_unused +synaptics_setup_ps2(struct psmouse *psmouse, + struct synaptics_device_info *info) { return -ENOSYS; } #endif /* CONFIG_MOUSE_PS2_SYNAPTICS */ + +#ifdef CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS + +/* + * The newest Synaptics device can use a secondary bus (called InterTouch) which + * provides a better bandwidth and allow a better control of the touchpads. + * This is used to decide if we need to use this bus or not. + */ +enum { + SYNAPTICS_INTERTOUCH_NOT_SET = -1, + SYNAPTICS_INTERTOUCH_OFF, + SYNAPTICS_INTERTOUCH_ON, +}; + +static int synaptics_intertouch = SYNAPTICS_INTERTOUCH_NOT_SET; +module_param_named(synaptics_intertouch, synaptics_intertouch, int, 0644); +MODULE_PARM_DESC(synaptics_intertouch, "Use a secondary bus for the Synaptics device."); + +static int synaptics_create_intertouch(struct psmouse *psmouse, + struct synaptics_device_info *info, + bool leave_breadcrumbs) +{ + bool topbuttonpad = + psmouse_matches_pnp_id(psmouse, topbuttonpad_pnp_ids) && + !SYN_CAP_EXT_BUTTONS_STICK(info->ext_cap_10); + const struct rmi_device_platform_data pdata = { + .sensor_pdata = { + .sensor_type = rmi_sensor_touchpad, + .axis_align.flip_y = true, + /* to prevent cursors jumps: */ + .kernel_tracking = true, + .topbuttonpad = topbuttonpad, + }, + .f30_data = { + .buttonpad = SYN_CAP_CLICKPAD(info->ext_cap_0c), + .trackstick_buttons = + !!SYN_CAP_EXT_BUTTONS_STICK(info->ext_cap_10), + }, + }; + const struct i2c_board_info intertouch_board = { + I2C_BOARD_INFO("rmi4_smbus", 0x2c), + .flags = I2C_CLIENT_HOST_NOTIFY, + }; + + return psmouse_smbus_init(psmouse, &intertouch_board, + &pdata, sizeof(pdata), + leave_breadcrumbs); +} + +/** + * synaptics_setup_intertouch - called once the PS/2 devices are enumerated + * and decides to instantiate a SMBus InterTouch device. + */ +static int synaptics_setup_intertouch(struct psmouse *psmouse, + struct synaptics_device_info *info, + bool leave_breadcrumbs) +{ + int error; + + if (synaptics_intertouch == SYNAPTICS_INTERTOUCH_OFF) + return -ENXIO; + + if (synaptics_intertouch == SYNAPTICS_INTERTOUCH_NOT_SET) { + if (!psmouse_matches_pnp_id(psmouse, topbuttonpad_pnp_ids) && + !psmouse_matches_pnp_id(psmouse, smbus_pnp_ids)) + return -ENXIO; + } + + psmouse_info(psmouse, "Trying to set up SMBus access\n"); + + error = synaptics_create_intertouch(psmouse, info, leave_breadcrumbs); + if (error) { + if (error == -EAGAIN) + psmouse_info(psmouse, "SMbus companion is not ready yet\n"); + else + psmouse_err(psmouse, "unable to create intertouch device\n"); + + return error; + } + + return 0; +} + +int synaptics_init_smbus(struct psmouse *psmouse) +{ + struct synaptics_device_info info; + int error; + + psmouse_reset(psmouse); + + error = synaptics_query_hardware(psmouse, &info); + if (error) { + psmouse_err(psmouse, "Unable to query device: %d\n", error); + return error; + } + + if (!SYN_CAP_INTERTOUCH(info.ext_cap_0c)) + return -ENXIO; + + return synaptics_create_intertouch(psmouse, &info, false); +} + +#else /* CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS */ + +static int __maybe_unused +synaptics_setup_intertouch(struct psmouse *psmouse, + struct synaptics_device_info *info, + bool leave_breadcrumbs) +{ + return -ENOSYS; +} + +int synaptics_init_smbus(struct psmouse *psmouse) +{ + return -ENOSYS; +} + +#endif /* CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS */ + +#if defined(CONFIG_MOUSE_PS2_SYNAPTICS) || \ + defined(CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS) + +int synaptics_init(struct psmouse *psmouse) +{ + struct synaptics_device_info info; + int error; + int retval; + + psmouse_reset(psmouse); + + error = synaptics_query_hardware(psmouse, &info); + if (error) { + psmouse_err(psmouse, "Unable to query device: %d\n", error); + return error; + } + + if (SYN_CAP_INTERTOUCH(info.ext_cap_0c)) { + error = synaptics_setup_intertouch(psmouse, &info, true); + if (!error) + return PSMOUSE_SYNAPTICS_SMBUS; + } + + retval = synaptics_setup_ps2(psmouse, &info); + if (retval < 0) { + /* + * Not using any flavor of Synaptics support, so clean up + * SMbus breadcrumbs, if any. + */ + psmouse_smbus_cleanup(psmouse); + } + + return retval; +} + +#else /* CONFIG_MOUSE_PS2_SYNAPTICS || CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS */ + +int synaptics_init(struct psmouse *psmouse) +{ + return -ENOSYS; +} + +#endif /* CONFIG_MOUSE_PS2_SYNAPTICS || CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS */ diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h index 116ae2546ace..31652d98b8f7 100644 --- a/drivers/input/mouse/synaptics.h +++ b/drivers/input/mouse/synaptics.h @@ -90,6 +90,7 @@ #define SYN_CAP_ADV_GESTURE(ex0c) ((ex0c) & 0x080000) #define SYN_CAP_REDUCED_FILTERING(ex0c) ((ex0c) & 0x000400) #define SYN_CAP_IMAGE_SENSOR(ex0c) ((ex0c) & 0x000800) +#define SYN_CAP_INTERTOUCH(ex0c) ((ex0c) & 0x004000) /* * The following descibes response for the 0x10 query. @@ -111,9 +112,9 @@ #define SYN_CAP_EXT_BUTTONS_STICK(ex10) ((ex10) & 0x010000) #define SYN_CAP_SECUREPAD(ex10) ((ex10) & 0x020000) -#define SYN_CAP_EXT_BUTTON_STICK_L(eb) (!!((eb) & 0x01)) -#define SYN_CAP_EXT_BUTTON_STICK_M(eb) (!!((eb) & 0x02)) -#define SYN_CAP_EXT_BUTTON_STICK_R(eb) (!!((eb) & 0x04)) +#define SYN_EXT_BUTTON_STICK_L(eb) (((eb) & BIT(0)) >> 0) +#define SYN_EXT_BUTTON_STICK_M(eb) (((eb) & BIT(1)) >> 1) +#define SYN_EXT_BUTTON_STICK_R(eb) (((eb) & BIT(2)) >> 2) /* synaptics modes query bits */ #define SYN_MODE_ABSOLUTE(m) ((m) & (1 << 7)) @@ -161,19 +162,23 @@ struct synaptics_hw_state { signed char scroll; }; +/* Data read from the touchpad */ +struct synaptics_device_info { + u32 model_id; /* Model-ID */ + u32 firmware_id; /* Firmware-ID */ + u32 board_id; /* Board-ID */ + u32 capabilities; /* Capabilities */ + u32 ext_cap; /* Extended Capabilities */ + u32 ext_cap_0c; /* Ext Caps from 0x0c query */ + u32 ext_cap_10; /* Ext Caps from 0x10 query */ + u32 identity; /* Identification */ + u32 x_res, y_res; /* X/Y resolution in units/mm */ + u32 x_max, y_max; /* Max coordinates (from FW) */ + u32 x_min, y_min; /* Min coordinates (from FW) */ +}; + struct synaptics_data { - /* Data read from the touchpad */ - unsigned long int model_id; /* Model-ID */ - unsigned long int firmware_id; /* Firmware-ID */ - unsigned long int board_id; /* Board-ID */ - unsigned long int capabilities; /* Capabilities */ - unsigned long int ext_cap; /* Extended Capabilities */ - unsigned long int ext_cap_0c; /* Ext Caps from 0x0c query */ - unsigned long int ext_cap_10; /* Ext Caps from 0x10 query */ - unsigned long int identity; /* Identification */ - unsigned int x_res, y_res; /* X/Y resolution in units/mm */ - unsigned int x_max, y_max; /* Max coordinates (from FW) */ - unsigned int x_min, y_min; /* Min coordinates (from FW) */ + struct synaptics_device_info info; unsigned char pkt_type; /* packet type - old, new, etc */ unsigned char mode; /* current mode byte */ @@ -200,8 +205,10 @@ struct synaptics_data { void synaptics_module_init(void); int synaptics_detect(struct psmouse *psmouse, bool set_properties); -int synaptics_init(struct psmouse *psmouse); +int synaptics_init_absolute(struct psmouse *psmouse); int synaptics_init_relative(struct psmouse *psmouse); +int synaptics_init_smbus(struct psmouse *psmouse); +int synaptics_init(struct psmouse *psmouse); void synaptics_reset(struct psmouse *psmouse); #endif /* _SYNAPTICS_H */ diff --git a/drivers/input/mouse/synaptics_i2c.c b/drivers/input/mouse/synaptics_i2c.c index cb2bf203f4ca..8538318d332c 100644 --- a/drivers/input/mouse/synaptics_i2c.c +++ b/drivers/input/mouse/synaptics_i2c.c @@ -652,9 +652,18 @@ static const struct i2c_device_id synaptics_i2c_id_table[] = { }; MODULE_DEVICE_TABLE(i2c, synaptics_i2c_id_table); +#ifdef CONFIG_OF +static const struct of_device_id synaptics_i2c_of_match[] = { + { .compatible = "synaptics,synaptics_i2c", }, + { }, +}; +MODULE_DEVICE_TABLE(of, synaptics_i2c_of_match); +#endif + static struct i2c_driver synaptics_i2c_driver = { .driver = { .name = DRIVER_NAME, + .of_match_table = of_match_ptr(synaptics_i2c_of_match), .pm = &synaptics_i2c_pm, }, diff --git a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c index 1ca7f551e2da..30d6230d48f7 100644 --- a/drivers/input/serio/serio.c +++ b/drivers/input/serio/serio.c @@ -285,8 +285,8 @@ static int serio_queue_event(void *object, struct module *owner, } if (!try_module_get(owner)) { - pr_warning("Can't get module reference, dropping event %d\n", - event_type); + pr_warn("Can't get module reference, dropping event %d\n", + event_type); kfree(event); retval = -EINVAL; goto out; @@ -823,8 +823,8 @@ static void serio_attach_driver(struct serio_driver *drv) error = driver_attach(&drv->driver); if (error) - pr_warning("driver_attach() failed for %s with error %d\n", - drv->driver.name, error); + pr_warn("driver_attach() failed for %s with error %d\n", + drv->driver.name, error); } int __serio_register_driver(struct serio_driver *drv, struct module *owner, const char *mod_name) @@ -953,12 +953,24 @@ static int serio_suspend(struct device *dev) static int serio_resume(struct device *dev) { struct serio *serio = to_serio_port(dev); + int error = -ENOENT; - /* - * Driver reconnect can take a while, so better let kseriod - * deal with it. - */ - serio_queue_event(serio, NULL, SERIO_RECONNECT_PORT); + mutex_lock(&serio->drv_mutex); + if (serio->drv && serio->drv->fast_reconnect) { + error = serio->drv->fast_reconnect(serio); + if (error && error != -ENOENT) + dev_warn(dev, "fast reconnect failed with error %d\n", + error); + } + mutex_unlock(&serio->drv_mutex); + + if (error) { + /* + * Driver reconnect can take a while, so better let + * kseriod deal with it. + */ + serio_queue_event(serio, NULL, SERIO_RECONNECT_PORT); + } return 0; } diff --git a/drivers/input/sparse-keymap.c b/drivers/input/sparse-keymap.c index e7409c45bdd0..12a3ad83296d 100644 --- a/drivers/input/sparse-keymap.c +++ b/drivers/input/sparse-keymap.c @@ -160,12 +160,12 @@ static int sparse_keymap_setkeycode(struct input_dev *dev, * @keymap: Keymap in form of array of &key_entry structures ending * with %KE_END type entry * @setup: Function that can be used to adjust keymap entries - * depending on device's deeds, may be %NULL + * depending on device's needs, may be %NULL * * The function calculates size and allocates copy of the original * keymap after which sets up input device event bits appropriately. - * Before destroying input device allocated keymap should be freed - * with a call to sparse_keymap_free(). + * The allocated copy of the keymap is automatically freed when it + * is no longer needed. */ int sparse_keymap_setup(struct input_dev *dev, const struct key_entry *keymap, @@ -180,19 +180,18 @@ int sparse_keymap_setup(struct input_dev *dev, for (e = keymap; e->type != KE_END; e++) map_size++; - map = kcalloc(map_size, sizeof(struct key_entry), GFP_KERNEL); + map = devm_kmemdup(&dev->dev, keymap, map_size * sizeof(*map), + GFP_KERNEL); if (!map) return -ENOMEM; - memcpy(map, keymap, map_size * sizeof(struct key_entry)); - for (i = 0; i < map_size; i++) { entry = &map[i]; if (setup) { error = setup(dev, entry); if (error) - goto err_out; + return error; } switch (entry->type) { @@ -221,10 +220,6 @@ int sparse_keymap_setup(struct input_dev *dev, dev->setkeycode = sparse_keymap_setkeycode; return 0; - - err_out: - kfree(map); - return error; } EXPORT_SYMBOL(sparse_keymap_setup); @@ -232,29 +227,13 @@ EXPORT_SYMBOL(sparse_keymap_setup); * sparse_keymap_free - free memory allocated for sparse keymap * @dev: Input device using sparse keymap * - * This function is used to free memory allocated by sparse keymap + * This function used to free memory allocated by sparse keymap * in an input device that was set up by sparse_keymap_setup(). - * NOTE: It is safe to cal this function while input device is - * still registered (however the drivers should care not to try to - * use freed keymap and thus have to shut off interrupts/polling - * before freeing the keymap). + * Since sparse_keymap_setup() now uses a managed allocation for the + * keymap copy, use of this function is deprecated. */ void sparse_keymap_free(struct input_dev *dev) { - unsigned long flags; - - /* - * Take event lock to prevent racing with input_get_keycode() - * and input_set_keycode() if we are called while input device - * is still registered. - */ - spin_lock_irqsave(&dev->event_lock, flags); - - kfree(dev->keycode); - dev->keycode = NULL; - dev->keycodemax = 0; - - spin_unlock_irqrestore(&dev->event_lock, flags); } EXPORT_SYMBOL(sparse_keymap_free); diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 033599777651..6515e649e204 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -73,6 +73,7 @@ config TOUCHSCREEN_AD7879 config TOUCHSCREEN_AD7879_I2C tristate "support I2C bus connection" depends on TOUCHSCREEN_AD7879 && I2C + select REGMAP_I2C help Say Y here if you have AD7879-1/AD7889-1 hooked to an I2C bus. @@ -82,6 +83,7 @@ config TOUCHSCREEN_AD7879_I2C config TOUCHSCREEN_AD7879_SPI tristate "support SPI bus connection" depends on TOUCHSCREEN_AD7879 && SPI_MASTER + select REGMAP_SPI help Say Y here if you have AD7879-1/AD7889-1 hooked to a SPI bus. @@ -1023,6 +1025,16 @@ config TOUCHSCREEN_TSC2007 To compile this driver as a module, choose M here: the module will be called tsc2007. +config TOUCHSCREEN_TSC2007_IIO + bool "IIO interface for external ADC input and temperature" + depends on TOUCHSCREEN_TSC2007 + depends on IIO=y || IIO=TOUCHSCREEN_TSC2007 + help + Saying Y here adds an iio interface to the tsc2007 which + provides values for the AUX input (used for e.g. battery + or ambient light monitoring), temperature and raw input + values. + config TOUCHSCREEN_W90X900 tristate "W90P910 touchscreen driver" depends on ARCH_W90X900 diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index b622e5344137..ad5c896222bc 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -79,6 +79,8 @@ obj-$(CONFIG_TOUCHSCREEN_TSC_SERIO) += tsc40.o obj-$(CONFIG_TOUCHSCREEN_TSC200X_CORE) += tsc200x-core.o obj-$(CONFIG_TOUCHSCREEN_TSC2004) += tsc2004.o obj-$(CONFIG_TOUCHSCREEN_TSC2005) += tsc2005.o +tsc2007-y := tsc2007_core.o +tsc2007-$(CONFIG_TOUCHSCREEN_TSC2007_IIO) += tsc2007_iio.o obj-$(CONFIG_TOUCHSCREEN_TSC2007) += tsc2007.o obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o obj-$(CONFIG_TOUCHSCREEN_WACOM_W8001) += wacom_w8001.o diff --git a/drivers/input/touchscreen/ad7879-i2c.c b/drivers/input/touchscreen/ad7879-i2c.c index 58f72e0246ab..49b902b10c5f 100644 --- a/drivers/input/touchscreen/ad7879-i2c.c +++ b/drivers/input/touchscreen/ad7879-i2c.c @@ -12,53 +12,22 @@ #include <linux/types.h> #include <linux/of.h> #include <linux/pm.h> +#include <linux/regmap.h> #include "ad7879.h" #define AD7879_DEVID 0x79 /* AD7879-1/AD7889-1 */ -/* All registers are word-sized. - * AD7879 uses a high-byte first convention. - */ -static int ad7879_i2c_read(struct device *dev, u8 reg) -{ - struct i2c_client *client = to_i2c_client(dev); - - return i2c_smbus_read_word_swapped(client, reg); -} - -static int ad7879_i2c_multi_read(struct device *dev, - u8 first_reg, u8 count, u16 *buf) -{ - struct i2c_client *client = to_i2c_client(dev); - u8 idx; - - i2c_smbus_read_i2c_block_data(client, first_reg, count * 2, (u8 *)buf); - - for (idx = 0; idx < count; ++idx) - buf[idx] = swab16(buf[idx]); - - return 0; -} - -static int ad7879_i2c_write(struct device *dev, u8 reg, u16 val) -{ - struct i2c_client *client = to_i2c_client(dev); - - return i2c_smbus_write_word_swapped(client, reg, val); -} - -static const struct ad7879_bus_ops ad7879_i2c_bus_ops = { - .bustype = BUS_I2C, - .read = ad7879_i2c_read, - .multi_read = ad7879_i2c_multi_read, - .write = ad7879_i2c_write, +static const struct regmap_config ad7879_i2c_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .max_register = 15, }; static int ad7879_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct ad7879 *ts; + struct regmap *regmap; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) { @@ -66,23 +35,12 @@ static int ad7879_i2c_probe(struct i2c_client *client, return -EIO; } - ts = ad7879_probe(&client->dev, AD7879_DEVID, client->irq, - &ad7879_i2c_bus_ops); - if (IS_ERR(ts)) - return PTR_ERR(ts); - - i2c_set_clientdata(client, ts); - - return 0; -} - -static int ad7879_i2c_remove(struct i2c_client *client) -{ - struct ad7879 *ts = i2c_get_clientdata(client); - - ad7879_remove(ts); + regmap = devm_regmap_init_i2c(client, &ad7879_i2c_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); - return 0; + return ad7879_probe(&client->dev, regmap, client->irq, + BUS_I2C, AD7879_DEVID); } static const struct i2c_device_id ad7879_id[] = { @@ -107,12 +65,11 @@ static struct i2c_driver ad7879_i2c_driver = { .of_match_table = of_match_ptr(ad7879_i2c_dt_ids), }, .probe = ad7879_i2c_probe, - .remove = ad7879_i2c_remove, .id_table = ad7879_id, }; module_i2c_driver(ad7879_i2c_driver); -MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>"); MODULE_DESCRIPTION("AD7879(-1) touchscreen I2C bus driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/ad7879-spi.c b/drivers/input/touchscreen/ad7879-spi.c index d42b6b9af191..3457a5626d75 100644 --- a/drivers/input/touchscreen/ad7879-spi.c +++ b/drivers/input/touchscreen/ad7879-spi.c @@ -11,110 +11,28 @@ #include <linux/spi/spi.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/regmap.h> #include "ad7879.h" #define AD7879_DEVID 0x7A /* AD7879/AD7889 */ #define MAX_SPI_FREQ_HZ 5000000 -#define AD7879_CMD_MAGIC 0xE000 -#define AD7879_CMD_READ (1 << 10) -#define AD7879_CMD(reg) (AD7879_CMD_MAGIC | ((reg) & 0xF)) -#define AD7879_WRITECMD(reg) (AD7879_CMD(reg)) -#define AD7879_READCMD(reg) (AD7879_CMD(reg) | AD7879_CMD_READ) -/* - * ad7879_read/write are only used for initial setup and for sysfs controls. - * The main traffic is done in ad7879_collect(). - */ - -static int ad7879_spi_xfer(struct spi_device *spi, - u16 cmd, u8 count, u16 *tx_buf, u16 *rx_buf) -{ - struct spi_message msg; - struct spi_transfer *xfers; - void *spi_data; - u16 *command; - u16 *_rx_buf = _rx_buf; /* shut gcc up */ - u8 idx; - int ret; - - xfers = spi_data = kzalloc(sizeof(*xfers) * (count + 2), GFP_KERNEL); - if (!spi_data) - return -ENOMEM; - - spi_message_init(&msg); - - command = spi_data; - command[0] = cmd; - if (count == 1) { - /* ad7879_spi_{read,write} gave us buf on stack */ - command[1] = *tx_buf; - tx_buf = &command[1]; - _rx_buf = rx_buf; - rx_buf = &command[2]; - } - - ++xfers; - xfers[0].tx_buf = command; - xfers[0].len = 2; - spi_message_add_tail(&xfers[0], &msg); - ++xfers; - - for (idx = 0; idx < count; ++idx) { - if (rx_buf) - xfers[idx].rx_buf = &rx_buf[idx]; - if (tx_buf) - xfers[idx].tx_buf = &tx_buf[idx]; - xfers[idx].len = 2; - spi_message_add_tail(&xfers[idx], &msg); - } - - ret = spi_sync(spi, &msg); - - if (count == 1) - _rx_buf[0] = command[2]; - - kfree(spi_data); - - return ret; -} - -static int ad7879_spi_multi_read(struct device *dev, - u8 first_reg, u8 count, u16 *buf) -{ - struct spi_device *spi = to_spi_device(dev); +#define AD7879_CMD_MAGIC 0xE0 +#define AD7879_CMD_READ BIT(2) - return ad7879_spi_xfer(spi, AD7879_READCMD(first_reg), count, NULL, buf); -} - -static int ad7879_spi_read(struct device *dev, u8 reg) -{ - struct spi_device *spi = to_spi_device(dev); - u16 ret, dummy; - - return ad7879_spi_xfer(spi, AD7879_READCMD(reg), 1, &dummy, &ret) ? : ret; -} - -static int ad7879_spi_write(struct device *dev, u8 reg, u16 val) -{ - struct spi_device *spi = to_spi_device(dev); - u16 dummy; - - return ad7879_spi_xfer(spi, AD7879_WRITECMD(reg), 1, &val, &dummy); -} - -static const struct ad7879_bus_ops ad7879_spi_bus_ops = { - .bustype = BUS_SPI, - .read = ad7879_spi_read, - .multi_read = ad7879_spi_multi_read, - .write = ad7879_spi_write, +static const struct regmap_config ad7879_spi_regmap_config = { + .reg_bits = 16, + .val_bits = 16, + .max_register = 15, + .read_flag_mask = AD7879_CMD_MAGIC | AD7879_CMD_READ, + .write_flag_mask = AD7879_CMD_MAGIC, }; static int ad7879_spi_probe(struct spi_device *spi) { - struct ad7879 *ts; - int err; + struct regmap *regmap; /* don't exceed max specified SPI CLK frequency */ if (spi->max_speed_hz > MAX_SPI_FREQ_HZ) { @@ -122,29 +40,11 @@ static int ad7879_spi_probe(struct spi_device *spi) return -EINVAL; } - spi->bits_per_word = 16; - err = spi_setup(spi); - if (err) { - dev_dbg(&spi->dev, "spi master doesn't support 16 bits/word\n"); - return err; - } - - ts = ad7879_probe(&spi->dev, AD7879_DEVID, spi->irq, &ad7879_spi_bus_ops); - if (IS_ERR(ts)) - return PTR_ERR(ts); - - spi_set_drvdata(spi, ts); - - return 0; -} - -static int ad7879_spi_remove(struct spi_device *spi) -{ - struct ad7879 *ts = spi_get_drvdata(spi); - - ad7879_remove(ts); + regmap = devm_regmap_init_spi(spi, &ad7879_spi_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); - return 0; + return ad7879_probe(&spi->dev, regmap, spi->irq, BUS_SPI, AD7879_DEVID); } #ifdef CONFIG_OF @@ -162,12 +62,11 @@ static struct spi_driver ad7879_spi_driver = { .of_match_table = of_match_ptr(ad7879_spi_dt_ids), }, .probe = ad7879_spi_probe, - .remove = ad7879_spi_remove, }; module_spi_driver(ad7879_spi_driver); -MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>"); MODULE_DESCRIPTION("AD7879(-1) touchscreen SPI bus driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("spi:ad7879"); diff --git a/drivers/input/touchscreen/ad7879.c b/drivers/input/touchscreen/ad7879.c index e16a44667da7..196028c45210 100644 --- a/drivers/input/touchscreen/ad7879.c +++ b/drivers/input/touchscreen/ad7879.c @@ -26,9 +26,9 @@ #include <linux/input.h> #include <linux/interrupt.h> #include <linux/irq.h> +#include <linux/property.h> +#include <linux/regmap.h> #include <linux/slab.h> -#include <linux/spi/spi.h> -#include <linux/i2c.h> #include <linux/gpio.h> #include <linux/input/touchscreen.h> @@ -106,8 +106,7 @@ enum { #define TS_PEN_UP_TIMEOUT msecs_to_jiffies(50) struct ad7879 { - const struct ad7879_bus_ops *bops; - + struct regmap *regmap; struct device *dev; struct input_dev *input; struct timer_list timer; @@ -137,17 +136,32 @@ struct ad7879 { static int ad7879_read(struct ad7879 *ts, u8 reg) { - return ts->bops->read(ts->dev, reg); -} + unsigned int val; + int error; -static int ad7879_multi_read(struct ad7879 *ts, u8 first_reg, u8 count, u16 *buf) -{ - return ts->bops->multi_read(ts->dev, first_reg, count, buf); + error = regmap_read(ts->regmap, reg, &val); + if (error) { + dev_err(ts->dev, "failed to read register %#02x: %d\n", + reg, error); + return error; + } + + return val; } static int ad7879_write(struct ad7879 *ts, u8 reg, u16 val) { - return ts->bops->write(ts->dev, reg, val); + int error; + + error = regmap_write(ts->regmap, reg, val); + if (error) { + dev_err(ts->dev, + "failed to write %#04x to register %#02x: %d\n", + val, reg, error); + return error; + } + + return 0; } static int ad7879_report(struct ad7879 *ts) @@ -234,7 +248,8 @@ static irqreturn_t ad7879_irq(int irq, void *handle) { struct ad7879 *ts = handle; - ad7879_multi_read(ts, AD7879_REG_XPLUS, AD7879_NR_SENSE, ts->conversion_data); + regmap_bulk_read(ts->regmap, AD7879_REG_XPLUS, + ts->conversion_data, AD7879_NR_SENSE); if (!ad7879_report(ts)) mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT); @@ -440,23 +455,34 @@ static void ad7879_gpio_set_value(struct gpio_chip *chip, static int ad7879_gpio_add(struct ad7879 *ts, const struct ad7879_platform_data *pdata) { + bool gpio_export; + int gpio_base; int ret = 0; + if (pdata) { + gpio_export = pdata->gpio_export; + gpio_base = pdata->gpio_base; + } else { + gpio_export = device_property_read_bool(ts->dev, + "gpio-controller"); + gpio_base = -1; + } + mutex_init(&ts->mutex); - if (pdata->gpio_export) { + if (gpio_export) { ts->gc.direction_input = ad7879_gpio_direction_input; ts->gc.direction_output = ad7879_gpio_direction_output; ts->gc.get = ad7879_gpio_get_value; ts->gc.set = ad7879_gpio_set_value; ts->gc.can_sleep = 1; - ts->gc.base = pdata->gpio_base; + ts->gc.base = gpio_base; ts->gc.ngpio = 1; ts->gc.label = "AD7879-GPIO"; ts->gc.owner = THIS_MODULE; ts->gc.parent = ts->dev; - ret = gpiochip_add_data(&ts->gc, ts); + ret = devm_gpiochip_add_data(ts->dev, &ts->gc, ts); if (ret) dev_err(ts->dev, "failed to register gpio %d\n", ts->gc.base); @@ -464,25 +490,12 @@ static int ad7879_gpio_add(struct ad7879 *ts, return ret; } - -static void ad7879_gpio_remove(struct ad7879 *ts) -{ - const struct ad7879_platform_data *pdata = dev_get_platdata(ts->dev); - - if (pdata && pdata->gpio_export) - gpiochip_remove(&ts->gc); - -} #else -static inline int ad7879_gpio_add(struct ad7879 *ts, - const struct ad7879_platform_data *pdata) +static int ad7879_gpio_add(struct ad7879 *ts, + const struct ad7879_platform_data *pdata) { return 0; } - -static inline void ad7879_gpio_remove(struct ad7879 *ts) -{ -} #endif static int ad7879_parse_dt(struct device *dev, struct ad7879 *ts) @@ -511,8 +524,15 @@ static int ad7879_parse_dt(struct device *dev, struct ad7879 *ts) return 0; } -struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq, - const struct ad7879_bus_ops *bops) +static void ad7879_cleanup_sysfs(void *_ts) +{ + struct ad7879 *ts = _ts; + + sysfs_remove_group(&ts->dev->kobj, &ad7879_attr_group); +} + +int ad7879_probe(struct device *dev, struct regmap *regmap, + int irq, u16 bustype, u8 devid) { struct ad7879_platform_data *pdata = dev_get_platdata(dev); struct ad7879 *ts; @@ -520,14 +540,14 @@ struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq, int err; u16 revid; - if (!irq) { + if (irq <= 0) { dev_err(dev, "No IRQ specified\n"); - return ERR_PTR(-EINVAL); + return -EINVAL; } ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL); if (!ts) - return ERR_PTR(-ENOMEM); + return -ENOMEM; if (pdata) { /* Platform data use swapped axis (backward compatibility) */ @@ -540,23 +560,22 @@ struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq, ts->averaging = pdata->averaging; ts->pen_down_acc_interval = pdata->pen_down_acc_interval; ts->median = pdata->median; - } else if (dev->of_node) { - ad7879_parse_dt(dev, ts); } else { - dev_err(dev, "No platform data\n"); - return ERR_PTR(-EINVAL); + err = ad7879_parse_dt(dev, ts); + if (err) + return err; } input_dev = devm_input_allocate_device(dev); if (!input_dev) { dev_err(dev, "Failed to allocate input device\n"); - return ERR_PTR(-ENOMEM); + return -ENOMEM; } - ts->bops = bops; ts->dev = dev; ts->input = input_dev; ts->irq = irq; + ts->regmap = regmap; setup_timer(&ts->timer, ad7879_timer, (unsigned long) ts); snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(dev)); @@ -564,20 +583,14 @@ struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq, input_dev->name = "AD7879 Touchscreen"; input_dev->phys = ts->phys; input_dev->dev.parent = dev; - input_dev->id.bustype = bops->bustype; + input_dev->id.bustype = bustype; input_dev->open = ad7879_open; input_dev->close = ad7879_close; input_set_drvdata(input_dev, ts); - __set_bit(EV_ABS, input_dev->evbit); - __set_bit(ABS_X, input_dev->absbit); - __set_bit(ABS_Y, input_dev->absbit); - __set_bit(ABS_PRESSURE, input_dev->absbit); - - __set_bit(EV_KEY, input_dev->evbit); - __set_bit(BTN_TOUCH, input_dev->keybit); + input_set_capability(input_dev, EV_KEY, BTN_TOUCH); if (pdata) { input_set_abs_params(input_dev, ABS_X, @@ -595,17 +608,18 @@ struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq, } else { input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0); input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0); + input_set_capability(input_dev, EV_ABS, ABS_PRESSURE); touchscreen_parse_properties(input_dev, false, NULL); if (!input_abs_get_max(input_dev, ABS_PRESSURE)) { dev_err(dev, "Touchscreen pressure is not specified\n"); - return ERR_PTR(-EINVAL); + return -EINVAL; } } err = ad7879_write(ts, AD7879_REG_CTRL2, AD7879_RESET); if (err < 0) { dev_err(dev, "Failed to write %s\n", input_dev->name); - return ERR_PTR(err); + return err; } revid = ad7879_read(ts, AD7879_REG_REVID); @@ -614,7 +628,7 @@ struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq, if (input_dev->id.product != devid) { dev_err(dev, "Failed to probe %s (%x vs %x)\n", input_dev->name, devid, revid); - return ERR_PTR(-ENODEV); + return -ENODEV; } ts->cmd_crtl3 = AD7879_YPLUS_BIT | @@ -639,43 +653,33 @@ struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq, dev_name(dev), ts); if (err) { dev_err(dev, "Failed to request IRQ: %d\n", err); - return ERR_PTR(err); + return err; } __ad7879_disable(ts); err = sysfs_create_group(&dev->kobj, &ad7879_attr_group); if (err) - goto err_out; + return err; - if (pdata) { - err = ad7879_gpio_add(ts, pdata); - if (err) - goto err_remove_attr; - } + err = devm_add_action_or_reset(dev, ad7879_cleanup_sysfs, ts); + if (err) + return err; + + err = ad7879_gpio_add(ts, pdata); + if (err) + return err; err = input_register_device(input_dev); if (err) - goto err_remove_gpio; + return err; - return ts; + dev_set_drvdata(dev, ts); -err_remove_gpio: - ad7879_gpio_remove(ts); -err_remove_attr: - sysfs_remove_group(&dev->kobj, &ad7879_attr_group); -err_out: - return ERR_PTR(err); + return 0; } EXPORT_SYMBOL(ad7879_probe); -void ad7879_remove(struct ad7879 *ts) -{ - ad7879_gpio_remove(ts); - sysfs_remove_group(&ts->dev->kobj, &ad7879_attr_group); -} -EXPORT_SYMBOL(ad7879_remove); - -MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>"); MODULE_DESCRIPTION("AD7879(-1) touchscreen Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/ad7879.h b/drivers/input/touchscreen/ad7879.h index 6fd13c48d373..7e43066a4b68 100644 --- a/drivers/input/touchscreen/ad7879.h +++ b/drivers/input/touchscreen/ad7879.h @@ -11,20 +11,12 @@ #include <linux/types.h> -struct ad7879; struct device; - -struct ad7879_bus_ops { - u16 bustype; - int (*read)(struct device *dev, u8 reg); - int (*multi_read)(struct device *dev, u8 first_reg, u8 count, u16 *buf); - int (*write)(struct device *dev, u8 reg, u16 val); -}; +struct regmap; extern const struct dev_pm_ops ad7879_pm_ops; -struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned irq, - const struct ad7879_bus_ops *bops); -void ad7879_remove(struct ad7879 *); +int ad7879_probe(struct device *dev, struct regmap *regmap, + int irq, u16 bustype, u8 devid); #endif diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index f5793e3d945f..735a0be1ad95 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -871,7 +871,7 @@ static irqreturn_t ads7846_irq(int irq, void *handle) msecs_to_jiffies(TS_POLL_PERIOD)); } - if (ts->pendown) { + if (ts->pendown && !ts->stopped) { struct input_dev *input = ts->input; input_report_key(input, BTN_TOUCH, 0); diff --git a/drivers/input/touchscreen/max11801_ts.c b/drivers/input/touchscreen/max11801_ts.c index a595ae5284e3..72ca3efb5781 100644 --- a/drivers/input/touchscreen/max11801_ts.c +++ b/drivers/input/touchscreen/max11801_ts.c @@ -224,9 +224,16 @@ static const struct i2c_device_id max11801_ts_id[] = { }; MODULE_DEVICE_TABLE(i2c, max11801_ts_id); +static const struct of_device_id max11801_ts_dt_ids[] = { + { .compatible = "maxim,max11801" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, max11801_ts_dt_ids); + static struct i2c_driver max11801_ts_driver = { .driver = { .name = "max11801_ts", + .of_match_table = max11801_ts_dt_ids, }, .id_table = max11801_ts_id, .probe = max11801_ts_probe, diff --git a/drivers/input/touchscreen/melfas_mip4.c b/drivers/input/touchscreen/melfas_mip4.c index 703d7f983d0a..05108c2fea93 100644 --- a/drivers/input/touchscreen/melfas_mip4.c +++ b/drivers/input/touchscreen/melfas_mip4.c @@ -253,10 +253,21 @@ static int mip4_get_fw_version(struct mip4_ts *ts) */ static int mip4_query_device(struct mip4_ts *ts) { + union i2c_smbus_data dummy; int error; u8 cmd[2]; u8 buf[14]; + /* + * Make sure there is something at this address as we do not + * consider subsequent failures as fatal. + */ + if (i2c_smbus_xfer(ts->client->adapter, ts->client->addr, + 0, I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &dummy) < 0) { + dev_err(&ts->client->dev, "nothing at this address\n"); + return -ENXIO; + } + /* Product name */ cmd[0] = MIP4_R0_INFO; cmd[1] = MIP4_R1_INFO_PRODUCT_NAME; diff --git a/drivers/input/touchscreen/silead.c b/drivers/input/touchscreen/silead.c index 404830a4a366..813dd68a5c82 100644 --- a/drivers/input/touchscreen/silead.c +++ b/drivers/input/touchscreen/silead.c @@ -580,12 +580,25 @@ static const struct acpi_device_id silead_ts_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, silead_ts_acpi_match); #endif +#ifdef CONFIG_OF +static const struct of_device_id silead_ts_of_match[] = { + { .compatible = "silead,gsl1680" }, + { .compatible = "silead,gsl1688" }, + { .compatible = "silead,gsl3670" }, + { .compatible = "silead,gsl3675" }, + { .compatible = "silead,gsl3692" }, + { }, +}; +MODULE_DEVICE_TABLE(of, silead_ts_of_match); +#endif + static struct i2c_driver silead_ts_driver = { .probe = silead_ts_probe, .id_table = silead_ts_id, .driver = { .name = SILEAD_TS_NAME, .acpi_match_table = ACPI_PTR(silead_ts_acpi_match), + .of_match_table = of_match_ptr(silead_ts_of_match), .pm = &silead_ts_pm, }, }; diff --git a/drivers/input/touchscreen/tps6507x-ts.c b/drivers/input/touchscreen/tps6507x-ts.c index a340bfccdfb6..75170a7439b1 100644 --- a/drivers/input/touchscreen/tps6507x-ts.c +++ b/drivers/input/touchscreen/tps6507x-ts.c @@ -226,7 +226,7 @@ static int tps6507x_ts_probe(struct platform_device *pdev) */ init_data = tps_board->tps6507x_ts_init_data; - tsc = kzalloc(sizeof(struct tps6507x_ts), GFP_KERNEL); + tsc = devm_kzalloc(&pdev->dev, sizeof(struct tps6507x_ts), GFP_KERNEL); if (!tsc) { dev_err(tps6507x_dev->dev, "failed to allocate driver data\n"); return -ENOMEM; @@ -240,11 +240,10 @@ static int tps6507x_ts_probe(struct platform_device *pdev) snprintf(tsc->phys, sizeof(tsc->phys), "%s/input0", dev_name(tsc->dev)); - poll_dev = input_allocate_polled_device(); + poll_dev = devm_input_allocate_polled_device(&pdev->dev); if (!poll_dev) { dev_err(tsc->dev, "Failed to allocate polled input device.\n"); - error = -ENOMEM; - goto err_free_mem; + return -ENOMEM; } tsc->poll_dev = poll_dev; @@ -274,32 +273,11 @@ static int tps6507x_ts_probe(struct platform_device *pdev) error = tps6507x_adc_standby(tsc); if (error) - goto err_free_polled_dev; + return error; error = input_register_polled_device(poll_dev); if (error) - goto err_free_polled_dev; - - platform_set_drvdata(pdev, tsc); - - return 0; - -err_free_polled_dev: - input_free_polled_device(poll_dev); -err_free_mem: - kfree(tsc); - return error; -} - -static int tps6507x_ts_remove(struct platform_device *pdev) -{ - struct tps6507x_ts *tsc = platform_get_drvdata(pdev); - struct input_polled_dev *poll_dev = tsc->poll_dev; - - input_unregister_polled_device(poll_dev); - input_free_polled_device(poll_dev); - - kfree(tsc); + return error; return 0; } @@ -309,7 +287,6 @@ static struct platform_driver tps6507x_ts_driver = { .name = "tps6507x-ts", }, .probe = tps6507x_ts_probe, - .remove = tps6507x_ts_remove, }; module_platform_driver(tps6507x_ts_driver); diff --git a/drivers/input/touchscreen/tsc2007.h b/drivers/input/touchscreen/tsc2007.h new file mode 100644 index 000000000000..30fdf5b04a6b --- /dev/null +++ b/drivers/input/touchscreen/tsc2007.h @@ -0,0 +1,101 @@ + +/* + * Copyright (c) 2008 MtekVision Co., Ltd. + * Kwangwoo Lee <kwlee@mtekvision.com> + * + * Using code from: + * - ads7846.c + * Copyright (c) 2005 David Brownell + * Copyright (c) 2006 Nokia Corporation + * - corgi_ts.c + * Copyright (C) 2004-2005 Richard Purdie + * - omap_ts.[hc], ads7846.h, ts_osk.c + * Copyright (C) 2002 MontaVista Software + * Copyright (C) 2004 Texas Instruments + * Copyright (C) 2005 Dirk Behme + * + * 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. + */ + +#ifndef _TSC2007_H +#define _TSC2007_H + +#define TSC2007_MEASURE_TEMP0 (0x0 << 4) +#define TSC2007_MEASURE_AUX (0x2 << 4) +#define TSC2007_MEASURE_TEMP1 (0x4 << 4) +#define TSC2007_ACTIVATE_XN (0x8 << 4) +#define TSC2007_ACTIVATE_YN (0x9 << 4) +#define TSC2007_ACTIVATE_YP_XN (0xa << 4) +#define TSC2007_SETUP (0xb << 4) +#define TSC2007_MEASURE_X (0xc << 4) +#define TSC2007_MEASURE_Y (0xd << 4) +#define TSC2007_MEASURE_Z1 (0xe << 4) +#define TSC2007_MEASURE_Z2 (0xf << 4) + +#define TSC2007_POWER_OFF_IRQ_EN (0x0 << 2) +#define TSC2007_ADC_ON_IRQ_DIS0 (0x1 << 2) +#define TSC2007_ADC_OFF_IRQ_EN (0x2 << 2) +#define TSC2007_ADC_ON_IRQ_DIS1 (0x3 << 2) + +#define TSC2007_12BIT (0x0 << 1) +#define TSC2007_8BIT (0x1 << 1) + +#define MAX_12BIT ((1 << 12) - 1) + +#define ADC_ON_12BIT (TSC2007_12BIT | TSC2007_ADC_ON_IRQ_DIS0) + +#define READ_Y (ADC_ON_12BIT | TSC2007_MEASURE_Y) +#define READ_Z1 (ADC_ON_12BIT | TSC2007_MEASURE_Z1) +#define READ_Z2 (ADC_ON_12BIT | TSC2007_MEASURE_Z2) +#define READ_X (ADC_ON_12BIT | TSC2007_MEASURE_X) +#define PWRDOWN (TSC2007_12BIT | TSC2007_POWER_OFF_IRQ_EN) + +struct ts_event { + u16 x; + u16 y; + u16 z1, z2; +}; + +struct tsc2007 { + struct input_dev *input; + char phys[32]; + + struct i2c_client *client; + + u16 model; + u16 x_plate_ohms; + u16 max_rt; + unsigned long poll_period; /* in jiffies */ + int fuzzx; + int fuzzy; + int fuzzz; + + unsigned int gpio; + int irq; + + wait_queue_head_t wait; + bool stopped; + + int (*get_pendown_state)(struct device *); + void (*clear_penirq)(void); + + struct mutex mlock; +}; + +int tsc2007_xfer(struct tsc2007 *tsc, u8 cmd); +u32 tsc2007_calculate_resistance(struct tsc2007 *tsc, struct ts_event *tc); +bool tsc2007_is_pen_down(struct tsc2007 *ts); + +#if IS_ENABLED(CONFIG_TOUCHSCREEN_TSC2007_IIO) +/* defined in tsc2007_iio.c */ +int tsc2007_iio_configure(struct tsc2007 *ts); +#else +static inline int tsc2007_iio_configure(struct tsc2007 *ts) +{ + return 0; +} +#endif /* CONFIG_TOUCHSCREEN_TSC2007_IIO */ + +#endif /* _TSC2007_H */ diff --git a/drivers/input/touchscreen/tsc2007.c b/drivers/input/touchscreen/tsc2007_core.c index 5d0cd51c6f41..fc7384936011 100644 --- a/drivers/input/touchscreen/tsc2007.c +++ b/drivers/input/touchscreen/tsc2007_core.c @@ -27,70 +27,10 @@ #include <linux/i2c.h> #include <linux/i2c/tsc2007.h> #include <linux/of_device.h> -#include <linux/of.h> #include <linux/of_gpio.h> +#include "tsc2007.h" -#define TSC2007_MEASURE_TEMP0 (0x0 << 4) -#define TSC2007_MEASURE_AUX (0x2 << 4) -#define TSC2007_MEASURE_TEMP1 (0x4 << 4) -#define TSC2007_ACTIVATE_XN (0x8 << 4) -#define TSC2007_ACTIVATE_YN (0x9 << 4) -#define TSC2007_ACTIVATE_YP_XN (0xa << 4) -#define TSC2007_SETUP (0xb << 4) -#define TSC2007_MEASURE_X (0xc << 4) -#define TSC2007_MEASURE_Y (0xd << 4) -#define TSC2007_MEASURE_Z1 (0xe << 4) -#define TSC2007_MEASURE_Z2 (0xf << 4) - -#define TSC2007_POWER_OFF_IRQ_EN (0x0 << 2) -#define TSC2007_ADC_ON_IRQ_DIS0 (0x1 << 2) -#define TSC2007_ADC_OFF_IRQ_EN (0x2 << 2) -#define TSC2007_ADC_ON_IRQ_DIS1 (0x3 << 2) - -#define TSC2007_12BIT (0x0 << 1) -#define TSC2007_8BIT (0x1 << 1) - -#define MAX_12BIT ((1 << 12) - 1) - -#define ADC_ON_12BIT (TSC2007_12BIT | TSC2007_ADC_ON_IRQ_DIS0) - -#define READ_Y (ADC_ON_12BIT | TSC2007_MEASURE_Y) -#define READ_Z1 (ADC_ON_12BIT | TSC2007_MEASURE_Z1) -#define READ_Z2 (ADC_ON_12BIT | TSC2007_MEASURE_Z2) -#define READ_X (ADC_ON_12BIT | TSC2007_MEASURE_X) -#define PWRDOWN (TSC2007_12BIT | TSC2007_POWER_OFF_IRQ_EN) - -struct ts_event { - u16 x; - u16 y; - u16 z1, z2; -}; - -struct tsc2007 { - struct input_dev *input; - char phys[32]; - - struct i2c_client *client; - - u16 model; - u16 x_plate_ohms; - u16 max_rt; - unsigned long poll_period; /* in jiffies */ - int fuzzx; - int fuzzy; - int fuzzz; - - unsigned gpio; - int irq; - - wait_queue_head_t wait; - bool stopped; - - int (*get_pendown_state)(struct device *); - void (*clear_penirq)(void); -}; - -static inline int tsc2007_xfer(struct tsc2007 *tsc, u8 cmd) +int tsc2007_xfer(struct tsc2007 *tsc, u8 cmd) { s32 data; u16 val; @@ -128,7 +68,7 @@ static void tsc2007_read_values(struct tsc2007 *tsc, struct ts_event *tc) tsc2007_xfer(tsc, PWRDOWN); } -static u32 tsc2007_calculate_pressure(struct tsc2007 *tsc, struct ts_event *tc) +u32 tsc2007_calculate_resistance(struct tsc2007 *tsc, struct ts_event *tc) { u32 rt = 0; @@ -137,7 +77,7 @@ static u32 tsc2007_calculate_pressure(struct tsc2007 *tsc, struct ts_event *tc) tc->x = 0; if (likely(tc->x && tc->z1)) { - /* compute touch pressure resistance using equation #1 */ + /* compute touch resistance using equation #1 */ rt = tc->z2 - tc->z1; rt *= tc->x; rt *= tsc->x_plate_ohms; @@ -148,7 +88,7 @@ static u32 tsc2007_calculate_pressure(struct tsc2007 *tsc, struct ts_event *tc) return rt; } -static bool tsc2007_is_pen_down(struct tsc2007 *ts) +bool tsc2007_is_pen_down(struct tsc2007 *ts) { /* * NOTE: We can't rely on the pressure to determine the pen down @@ -180,9 +120,12 @@ static irqreturn_t tsc2007_soft_irq(int irq, void *handle) while (!ts->stopped && tsc2007_is_pen_down(ts)) { /* pen is down, continue with the measurement */ + + mutex_lock(&ts->mlock); tsc2007_read_values(ts, &tc); + mutex_unlock(&ts->mlock); - rt = tsc2007_calculate_pressure(ts, &tc); + rt = tsc2007_calculate_resistance(ts, &tc); if (!rt && !ts->get_pendown_state) { /* @@ -195,9 +138,11 @@ static irqreturn_t tsc2007_soft_irq(int irq, void *handle) if (rt <= ts->max_rt) { dev_dbg(&ts->client->dev, - "DOWN point(%4d,%4d), pressure (%4u)\n", + "DOWN point(%4d,%4d), resistance (%4u)\n", tc.x, tc.y, rt); + rt = ts->max_rt - rt; + input_report_key(input, BTN_TOUCH, 1); input_report_abs(input, ABS_X, tc.x); input_report_abs(input, ABS_Y, tc.y); @@ -375,7 +320,8 @@ static void tsc2007_call_exit_platform_hw(void *data) static int tsc2007_probe(struct i2c_client *client, const struct i2c_device_id *id) { - const struct tsc2007_platform_data *pdata = dev_get_platdata(&client->dev); + const struct tsc2007_platform_data *pdata = + dev_get_platdata(&client->dev); struct tsc2007 *ts; struct input_dev *input_dev; int err; @@ -404,7 +350,9 @@ static int tsc2007_probe(struct i2c_client *client, ts->client = client; ts->irq = client->irq; ts->input = input_dev; + init_waitqueue_head(&ts->wait); + mutex_init(&ts->mlock); snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&client->dev)); @@ -418,8 +366,7 @@ static int tsc2007_probe(struct i2c_client *client, input_set_drvdata(input_dev, ts); - input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); - input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + input_set_capability(input_dev, EV_KEY, BTN_TOUCH); input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, ts->fuzzx, 0); input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, ts->fuzzy, 0); @@ -455,6 +402,14 @@ static int tsc2007_probe(struct i2c_client *client, tsc2007_stop(ts); + /* power down the chip (TSC2007_SETUP does not ACK on I2C) */ + err = tsc2007_xfer(ts, PWRDOWN); + if (err < 0) { + dev_err(&client->dev, + "Failed to setup chip: %d\n", err); + return err; /* chip does not respond */ + } + err = input_register_device(input_dev); if (err) { dev_err(&client->dev, @@ -462,6 +417,13 @@ static int tsc2007_probe(struct i2c_client *client, return err; } + err = tsc2007_iio_configure(ts); + if (err) { + dev_err(&client->dev, + "Failed to register with IIO: %d\n", err); + return err; + } + return 0; } diff --git a/drivers/input/touchscreen/tsc2007_iio.c b/drivers/input/touchscreen/tsc2007_iio.c new file mode 100644 index 000000000000..27b25a9fce83 --- /dev/null +++ b/drivers/input/touchscreen/tsc2007_iio.c @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2016 Golden Delicious Comp. GmbH&Co. KG + * Nikolaus Schaller <hns@goldelico.com> + * + * 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/i2c.h> +#include <linux/iio/iio.h> +#include "tsc2007.h" + +struct tsc2007_iio { + struct tsc2007 *ts; +}; + +#define TSC2007_CHAN_IIO(_chan, _name, _type, _chan_info) \ +{ \ + .datasheet_name = _name, \ + .type = _type, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(_chan_info), \ + .indexed = 1, \ + .channel = _chan, \ +} + +static const struct iio_chan_spec tsc2007_iio_channel[] = { + TSC2007_CHAN_IIO(0, "x", IIO_VOLTAGE, IIO_CHAN_INFO_RAW), + TSC2007_CHAN_IIO(1, "y", IIO_VOLTAGE, IIO_CHAN_INFO_RAW), + TSC2007_CHAN_IIO(2, "z1", IIO_VOLTAGE, IIO_CHAN_INFO_RAW), + TSC2007_CHAN_IIO(3, "z2", IIO_VOLTAGE, IIO_CHAN_INFO_RAW), + TSC2007_CHAN_IIO(4, "adc", IIO_VOLTAGE, IIO_CHAN_INFO_RAW), + TSC2007_CHAN_IIO(5, "rt", IIO_VOLTAGE, IIO_CHAN_INFO_RAW), /* Ohms? */ + TSC2007_CHAN_IIO(6, "pen", IIO_PRESSURE, IIO_CHAN_INFO_RAW), + TSC2007_CHAN_IIO(7, "temp0", IIO_TEMP, IIO_CHAN_INFO_RAW), + TSC2007_CHAN_IIO(8, "temp1", IIO_TEMP, IIO_CHAN_INFO_RAW), +}; + +static int tsc2007_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct tsc2007_iio *iio = iio_priv(indio_dev); + struct tsc2007 *tsc = iio->ts; + int adc_chan = chan->channel; + int ret = 0; + + if (adc_chan >= ARRAY_SIZE(tsc2007_iio_channel)) + return -EINVAL; + + if (mask != IIO_CHAN_INFO_RAW) + return -EINVAL; + + mutex_lock(&tsc->mlock); + + switch (chan->channel) { + case 0: + *val = tsc2007_xfer(tsc, READ_X); + break; + case 1: + *val = tsc2007_xfer(tsc, READ_Y); + break; + case 2: + *val = tsc2007_xfer(tsc, READ_Z1); + break; + case 3: + *val = tsc2007_xfer(tsc, READ_Z2); + break; + case 4: + *val = tsc2007_xfer(tsc, (ADC_ON_12BIT | TSC2007_MEASURE_AUX)); + break; + case 5: { + struct ts_event tc; + + tc.x = tsc2007_xfer(tsc, READ_X); + tc.z1 = tsc2007_xfer(tsc, READ_Z1); + tc.z2 = tsc2007_xfer(tsc, READ_Z2); + *val = tsc2007_calculate_resistance(tsc, &tc); + break; + } + case 6: + *val = tsc2007_is_pen_down(tsc); + break; + case 7: + *val = tsc2007_xfer(tsc, + (ADC_ON_12BIT | TSC2007_MEASURE_TEMP0)); + break; + case 8: + *val = tsc2007_xfer(tsc, + (ADC_ON_12BIT | TSC2007_MEASURE_TEMP1)); + break; + } + + /* Prepare for next touch reading - power down ADC, enable PENIRQ */ + tsc2007_xfer(tsc, PWRDOWN); + + mutex_unlock(&tsc->mlock); + + ret = IIO_VAL_INT; + + return ret; +} + +static const struct iio_info tsc2007_iio_info = { + .read_raw = tsc2007_read_raw, + .driver_module = THIS_MODULE, +}; + +int tsc2007_iio_configure(struct tsc2007 *ts) +{ + struct iio_dev *indio_dev; + struct tsc2007_iio *iio; + int error; + + indio_dev = devm_iio_device_alloc(&ts->client->dev, sizeof(*iio)); + if (!indio_dev) { + dev_err(&ts->client->dev, "iio_device_alloc failed\n"); + return -ENOMEM; + } + + iio = iio_priv(indio_dev); + iio->ts = ts; + + indio_dev->name = "tsc2007"; + indio_dev->dev.parent = &ts->client->dev; + indio_dev->info = &tsc2007_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = tsc2007_iio_channel; + indio_dev->num_channels = ARRAY_SIZE(tsc2007_iio_channel); + + error = devm_iio_device_register(&ts->client->dev, indio_dev); + if (error) { + dev_err(&ts->client->dev, + "iio_device_register() failed: %d\n", error); + return error; + } + + return 0; +} |