diff options
Diffstat (limited to 'drivers/input')
90 files changed, 3925 insertions, 1304 deletions
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index 9d35499faca4..08d496411f75 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -290,19 +290,14 @@ static int evdev_flush(struct file *file, fl_owner_t id) { struct evdev_client *client = file->private_data; struct evdev *evdev = client->evdev; - int retval; - retval = mutex_lock_interruptible(&evdev->mutex); - if (retval) - return retval; + mutex_lock(&evdev->mutex); - if (!evdev->exist || client->revoked) - retval = -ENODEV; - else - retval = input_flush_device(&evdev->handle, file); + if (evdev->exist && !client->revoked) + input_flush_device(&evdev->handle, file); mutex_unlock(&evdev->mutex); - return retval; + return 0; } static void evdev_free(struct device *dev) diff --git a/drivers/input/ff-core.c b/drivers/input/ff-core.c index 8f4a30fccbb6..c64208267198 100644 --- a/drivers/input/ff-core.c +++ b/drivers/input/ff-core.c @@ -343,9 +343,8 @@ int input_ff_create(struct input_dev *dev, unsigned int max_effects) __set_bit(EV_FF, dev->evbit); /* Copy "true" bits into ff device bitmap */ - for (i = 0; i <= FF_MAX; i++) - if (test_bit(i, dev->ffbit)) - __set_bit(i, ff->ffbit); + for_each_set_bit(i, dev->ffbit, FF_CNT) + __set_bit(i, ff->ffbit); /* we can emulate RUMBLE with periodic effects */ if (test_bit(FF_PERIODIC, ff->ffbit)) diff --git a/drivers/input/input.c b/drivers/input/input.c index 78d24990a816..5391abd28b27 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -674,13 +674,19 @@ EXPORT_SYMBOL(input_close_device); */ static void input_dev_release_keys(struct input_dev *dev) { + bool need_sync = false; int code; if (is_event_supported(EV_KEY, dev->evbit, EV_MAX)) { - for_each_set_bit(code, dev->key, KEY_CNT) + for_each_set_bit(code, dev->key, KEY_CNT) { input_pass_event(dev, EV_KEY, code, 0); + need_sync = true; + } + + if (need_sync) + input_pass_event(dev, EV_SYN, SYN_REPORT, 1); + memset(dev->key, 0, sizeof(dev->key)); - input_pass_event(dev, EV_SYN, SYN_REPORT, 1); } } diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c index 1d247bcf2ae2..6cb5a3e5f9a1 100644 --- a/drivers/input/joydev.c +++ b/drivers/input/joydev.c @@ -859,12 +859,11 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev, joydev->handle.handler = handler; joydev->handle.private = joydev; - for (i = 0; i < ABS_CNT; i++) - if (test_bit(i, dev->absbit)) { - joydev->absmap[i] = joydev->nabs; - joydev->abspam[joydev->nabs] = i; - joydev->nabs++; - } + for_each_set_bit(i, dev->absbit, ABS_CNT) { + joydev->absmap[i] = joydev->nabs; + joydev->abspam[joydev->nabs] = i; + joydev->nabs++; + } for (i = BTN_JOYSTICK - BTN_MISC; i < KEY_MAX - BTN_MISC + 1; i++) if (test_bit(i + BTN_MISC, dev->keybit)) { diff --git a/drivers/input/joystick/zhenhua.c b/drivers/input/joystick/zhenhua.c index 30af2e8c670c..4a8258bf13fd 100644 --- a/drivers/input/joystick/zhenhua.c +++ b/drivers/input/joystick/zhenhua.c @@ -47,6 +47,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/slab.h> +#include <linux/bitrev.h> #include <linux/input.h> #include <linux/serio.h> @@ -72,16 +73,6 @@ struct zhenhua { char phys[32]; }; - -/* bits in all incoming bytes needs to be "reversed" */ -static int zhenhua_bitreverse(int x) -{ - x = ((x & 0xaa) >> 1) | ((x & 0x55) << 1); - x = ((x & 0xcc) >> 2) | ((x & 0x33) << 2); - x = ((x & 0xf0) >> 4) | ((x & 0x0f) << 4); - return x; -} - /* * zhenhua_process_packet() decodes packets the driver receives from the * RC transmitter. It updates the data accordingly. @@ -120,7 +111,7 @@ static irqreturn_t zhenhua_interrupt(struct serio *serio, unsigned char data, un return IRQ_HANDLED; /* wrong MSB -- ignore this byte */ if (zhenhua->idx < ZHENHUA_MAX_LENGTH) - zhenhua->data[zhenhua->idx++] = zhenhua_bitreverse(data); + zhenhua->data[zhenhua->idx++] = bitrev8(data); if (zhenhua->idx == ZHENHUA_MAX_LENGTH) { zhenhua_process_packet(zhenhua); diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 82a8fb50afac..2e80107ff630 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -187,7 +187,7 @@ config KEYBOARD_EP93XX config KEYBOARD_GPIO tristate "GPIO Buttons" - depends on GPIOLIB + depends on GPIOLIB || COMPILE_TEST help This driver implements support for buttons connected to GPIO pins of various CPUs (and some other chips). @@ -253,7 +253,7 @@ config KEYBOARD_TCA8418 config KEYBOARD_MATRIX tristate "GPIO driven matrix keypad support" - depends on GPIOLIB + depends on GPIOLIB || COMPILE_TEST select INPUT_MATRIXKMAP help Enable support for GPIO driven matrix keypad. @@ -689,7 +689,7 @@ config KEYBOARD_W90P910 config KEYBOARD_CROS_EC tristate "ChromeOS EC keyboard" select INPUT_MATRIXKMAP - depends on CROS_EC_PROTO + depends on MFD_CROS_EC help Say Y here to enable the matrix keyboard used by ChromeOS devices and implemented on the ChromeOS EC. You must enable one bus option diff --git a/drivers/input/keyboard/adp5589-keys.c b/drivers/input/keyboard/adp5589-keys.c index 6ed83cf8b74e..4d446d5085aa 100644 --- a/drivers/input/keyboard/adp5589-keys.c +++ b/drivers/input/keyboard/adp5589-keys.c @@ -1097,7 +1097,6 @@ MODULE_DEVICE_TABLE(i2c, adp5589_id); static struct i2c_driver adp5589_driver = { .driver = { .name = KBUILD_MODNAME, - .owner = THIS_MODULE, .pm = &adp5589_dev_pm_ops, }, .probe = adp5589_probe, diff --git a/drivers/input/keyboard/cap11xx.c b/drivers/input/keyboard/cap11xx.c index f07461a64d85..378db10001df 100644 --- a/drivers/input/keyboard/cap11xx.c +++ b/drivers/input/keyboard/cap11xx.c @@ -12,6 +12,7 @@ #include <linux/module.h> #include <linux/interrupt.h> #include <linux/input.h> +#include <linux/leds.h> #include <linux/of_irq.h> #include <linux/regmap.h> #include <linux/i2c.h> @@ -47,6 +48,20 @@ #define CAP11XX_REG_CONFIG2 0x44 #define CAP11XX_REG_CONFIG2_ALT_POL BIT(6) #define CAP11XX_REG_SENSOR_BASE_CNT(X) (0x50 + (X)) +#define CAP11XX_REG_LED_POLARITY 0x73 +#define CAP11XX_REG_LED_OUTPUT_CONTROL 0x74 + +#define CAP11XX_REG_LED_DUTY_CYCLE_1 0x90 +#define CAP11XX_REG_LED_DUTY_CYCLE_2 0x91 +#define CAP11XX_REG_LED_DUTY_CYCLE_3 0x92 +#define CAP11XX_REG_LED_DUTY_CYCLE_4 0x93 + +#define CAP11XX_REG_LED_DUTY_MIN_MASK (0x0f) +#define CAP11XX_REG_LED_DUTY_MIN_MASK_SHIFT (0) +#define CAP11XX_REG_LED_DUTY_MAX_MASK (0xf0) +#define CAP11XX_REG_LED_DUTY_MAX_MASK_SHIFT (4) +#define CAP11XX_REG_LED_DUTY_MAX_VALUE (15) + #define CAP11XX_REG_SENSOR_CALIB (0xb1 + (X)) #define CAP11XX_REG_SENSOR_CALIB_LSB1 0xb9 #define CAP11XX_REG_SENSOR_CALIB_LSB2 0xba @@ -56,10 +71,23 @@ #define CAP11XX_MANUFACTURER_ID 0x5d +#ifdef CONFIG_LEDS_CLASS +struct cap11xx_led { + struct cap11xx_priv *priv; + struct led_classdev cdev; + struct work_struct work; + u32 reg; + enum led_brightness new_brightness; +}; +#endif + struct cap11xx_priv { struct regmap *regmap; struct input_dev *idev; + struct cap11xx_led *leds; + int num_leds; + /* config */ u32 keycodes[]; }; @@ -67,6 +95,7 @@ struct cap11xx_priv { struct cap11xx_hw_model { u8 product_id; unsigned int num_channels; + unsigned int num_leds; }; enum { @@ -76,9 +105,9 @@ enum { }; static const struct cap11xx_hw_model cap11xx_devices[] = { - [CAP1106] = { .product_id = 0x55, .num_channels = 6 }, - [CAP1126] = { .product_id = 0x53, .num_channels = 6 }, - [CAP1188] = { .product_id = 0x50, .num_channels = 8 }, + [CAP1106] = { .product_id = 0x55, .num_channels = 6, .num_leds = 0 }, + [CAP1126] = { .product_id = 0x53, .num_channels = 6, .num_leds = 2 }, + [CAP1188] = { .product_id = 0x50, .num_channels = 8, .num_leds = 8 }, }; static const struct reg_default cap11xx_reg_defaults[] = { @@ -111,6 +140,7 @@ static const struct reg_default cap11xx_reg_defaults[] = { { CAP11XX_REG_STANDBY_SENSITIVITY, 0x02 }, { CAP11XX_REG_STANDBY_THRESH, 0x40 }, { CAP11XX_REG_CONFIG2, 0x40 }, + { CAP11XX_REG_LED_POLARITY, 0x00 }, { CAP11XX_REG_SENSOR_CALIB_LSB1, 0x00 }, { CAP11XX_REG_SENSOR_CALIB_LSB2, 0x00 }, }; @@ -177,6 +207,12 @@ out: static int cap11xx_set_sleep(struct cap11xx_priv *priv, bool sleep) { + /* + * DLSEEP mode will turn off all LEDS, prevent this + */ + if (IS_ENABLED(CONFIG_LEDS_CLASS) && priv->num_leds) + return 0; + return regmap_update_bits(priv->regmap, CAP11XX_REG_MAIN_CONTROL, CAP11XX_REG_MAIN_CONTROL_DLSEEP, sleep ? CAP11XX_REG_MAIN_CONTROL_DLSEEP : 0); @@ -196,6 +232,104 @@ static void cap11xx_input_close(struct input_dev *idev) cap11xx_set_sleep(priv, true); } +#ifdef CONFIG_LEDS_CLASS +static void cap11xx_led_work(struct work_struct *work) +{ + struct cap11xx_led *led = container_of(work, struct cap11xx_led, work); + struct cap11xx_priv *priv = led->priv; + int value = led->new_brightness; + + /* + * All LEDs share the same duty cycle as this is a HW limitation. + * Brightness levels per LED are either 0 (OFF) and 1 (ON). + */ + regmap_update_bits(priv->regmap, CAP11XX_REG_LED_OUTPUT_CONTROL, + BIT(led->reg), value ? BIT(led->reg) : 0); +} + +static void cap11xx_led_set(struct led_classdev *cdev, + enum led_brightness value) +{ + struct cap11xx_led *led = container_of(cdev, struct cap11xx_led, cdev); + + if (led->new_brightness == value) + return; + + led->new_brightness = value; + schedule_work(&led->work); +} + +static int cap11xx_init_leds(struct device *dev, + struct cap11xx_priv *priv, int num_leds) +{ + struct device_node *node = dev->of_node, *child; + struct cap11xx_led *led; + int cnt = of_get_child_count(node); + int error; + + if (!num_leds || !cnt) + return 0; + + if (cnt > num_leds) + return -EINVAL; + + led = devm_kcalloc(dev, cnt, sizeof(struct cap11xx_led), GFP_KERNEL); + if (!led) + return -ENOMEM; + + priv->leds = led; + + error = regmap_update_bits(priv->regmap, + CAP11XX_REG_LED_OUTPUT_CONTROL, 0xff, 0); + if (error) + return error; + + error = regmap_update_bits(priv->regmap, CAP11XX_REG_LED_DUTY_CYCLE_4, + CAP11XX_REG_LED_DUTY_MAX_MASK, + CAP11XX_REG_LED_DUTY_MAX_VALUE << + CAP11XX_REG_LED_DUTY_MAX_MASK_SHIFT); + if (error) + return error; + + for_each_child_of_node(node, child) { + u32 reg; + + led->cdev.name = + of_get_property(child, "label", NULL) ? : child->name; + led->cdev.default_trigger = + of_get_property(child, "linux,default-trigger", NULL); + led->cdev.flags = 0; + led->cdev.brightness_set = cap11xx_led_set; + led->cdev.max_brightness = 1; + led->cdev.brightness = LED_OFF; + + error = of_property_read_u32(child, "reg", ®); + if (error != 0 || reg >= num_leds) + return -EINVAL; + + led->reg = reg; + led->priv = priv; + + INIT_WORK(&led->work, cap11xx_led_work); + + error = devm_led_classdev_register(dev, &led->cdev); + if (error) + return error; + + priv->num_leds++; + led++; + } + + return 0; +} +#else +static int cap11xx_init_leds(struct device *dev, + struct cap11xx_priv *priv, int num_leds) +{ + return 0; +} +#endif + static int cap11xx_i2c_probe(struct i2c_client *i2c_client, const struct i2c_device_id *id) { @@ -316,6 +450,10 @@ static int cap11xx_i2c_probe(struct i2c_client *i2c_client, priv->idev->open = cap11xx_input_open; priv->idev->close = cap11xx_input_close; + error = cap11xx_init_leds(dev, priv, cap->num_leds); + if (error) + return error; + input_set_drvdata(priv->idev, priv); /* @@ -361,7 +499,6 @@ MODULE_DEVICE_TABLE(i2c, cap11xx_i2c_ids); static struct i2c_driver cap11xx_i2c_driver = { .driver = { .name = "cap11xx", - .owner = THIS_MODULE, .of_match_table = cap11xx_dt_ids, }, .id_table = cap11xx_i2c_ids, diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index ddf4045de084..9d517ca7eb5a 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -239,6 +239,11 @@ static ssize_t gpio_keys_attr_store_helper(struct gpio_keys_drvdata *ddata, } } + if (i == ddata->pdata->nbuttons) { + error = -EINVAL; + goto out; + } + mutex_lock(&ddata->disable_lock); for (i = 0; i < ddata->pdata->nbuttons; i++) { @@ -655,7 +660,9 @@ gpio_keys_get_devtree_pdata(struct device *dev) if (of_property_read_u32(pp, "linux,input-type", &button->type)) button->type = EV_KEY; - button->wakeup = !!of_get_property(pp, "gpio-key,wakeup", NULL); + button->wakeup = of_property_read_bool(pp, "wakeup-source") || + /* legacy name */ + of_property_read_bool(pp, "gpio-key,wakeup"); button->can_disable = !!of_get_property(pp, "linux,can-disable", NULL); diff --git a/drivers/input/keyboard/gpio_keys_polled.c b/drivers/input/keyboard/gpio_keys_polled.c index c6dc644aa580..870cfa6e2c44 100644 --- a/drivers/input/keyboard/gpio_keys_polled.c +++ b/drivers/input/keyboard/gpio_keys_polled.c @@ -152,7 +152,10 @@ static struct gpio_keys_platform_data *gpio_keys_polled_get_devtree_pdata(struct &button->type)) button->type = EV_KEY; - button->wakeup = fwnode_property_present(child, "gpio-key,wakeup"); + button->wakeup = + fwnode_property_read_bool(child, "wakeup-source") || + /* legacy name */ + fwnode_property_read_bool(child, "gpio-key,wakeup"); if (fwnode_property_read_u32(child, "debounce-interval", &button->debounce_interval)) diff --git a/drivers/input/keyboard/imx_keypad.c b/drivers/input/keyboard/imx_keypad.c index d2ea863d6a45..2165f3dd328b 100644 --- a/drivers/input/keyboard/imx_keypad.c +++ b/drivers/input/keyboard/imx_keypad.c @@ -5,8 +5,6 @@ * 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. - * - * <<Power management needs to be implemented>>. */ #include <linux/clk.h> diff --git a/drivers/input/keyboard/lm8333.c b/drivers/input/keyboard/lm8333.c index 0ad422b8a260..c717e8f3c964 100644 --- a/drivers/input/keyboard/lm8333.c +++ b/drivers/input/keyboard/lm8333.c @@ -223,7 +223,6 @@ MODULE_DEVICE_TABLE(i2c, lm8333_id); static struct i2c_driver lm8333_driver = { .driver = { .name = "lm8333", - .owner = THIS_MODULE, }, .probe = lm8333_probe, .remove = lm8333_remove, diff --git a/drivers/input/keyboard/matrix_keypad.c b/drivers/input/keyboard/matrix_keypad.c index b370a59cb759..7f12b6579f82 100644 --- a/drivers/input/keyboard/matrix_keypad.c +++ b/drivers/input/keyboard/matrix_keypad.c @@ -425,8 +425,10 @@ matrix_keypad_parse_dt(struct device *dev) if (of_get_property(np, "linux,no-autorepeat", NULL)) pdata->no_autorepeat = true; - if (of_get_property(np, "linux,wakeup", NULL)) - pdata->wakeup = true; + + pdata->wakeup = of_property_read_bool(np, "wakeup-source") || + of_property_read_bool(np, "linux,wakeup"); /* legacy */ + if (of_get_property(np, "gpio-activelow", NULL)) pdata->active_low = true; diff --git a/drivers/input/keyboard/mcs_touchkey.c b/drivers/input/keyboard/mcs_touchkey.c index 375b05ca8e2a..31090d71a685 100644 --- a/drivers/input/keyboard/mcs_touchkey.c +++ b/drivers/input/keyboard/mcs_touchkey.c @@ -265,7 +265,6 @@ MODULE_DEVICE_TABLE(i2c, mcs_touchkey_id); static struct i2c_driver mcs_touchkey_driver = { .driver = { .name = "mcs_touchkey", - .owner = THIS_MODULE, .pm = &mcs_touchkey_pm_ops, }, .probe = mcs_touchkey_probe, diff --git a/drivers/input/keyboard/mpr121_touchkey.c b/drivers/input/keyboard/mpr121_touchkey.c index 3aa2ec45bcab..0fd612dd76ed 100644 --- a/drivers/input/keyboard/mpr121_touchkey.c +++ b/drivers/input/keyboard/mpr121_touchkey.c @@ -305,7 +305,6 @@ MODULE_DEVICE_TABLE(i2c, mpr121_id); static struct i2c_driver mpr_touchkey_driver = { .driver = { .name = "mpr121", - .owner = THIS_MODULE, .pm = &mpr121_touchkey_pm_ops, }, .id_table = mpr121_id, diff --git a/drivers/input/keyboard/pmic8xxx-keypad.c b/drivers/input/keyboard/pmic8xxx-keypad.c index 32580afecc26..5c68e3f096bc 100644 --- a/drivers/input/keyboard/pmic8xxx-keypad.c +++ b/drivers/input/keyboard/pmic8xxx-keypad.c @@ -507,6 +507,7 @@ static void pmic8xxx_kp_close(struct input_dev *dev) */ static int pmic8xxx_kp_probe(struct platform_device *pdev) { + struct device_node *np = pdev->dev.of_node; unsigned int rows, cols; bool repeat; bool wakeup; @@ -524,10 +525,11 @@ static int pmic8xxx_kp_probe(struct platform_device *pdev) return -EINVAL; } - repeat = !of_property_read_bool(pdev->dev.of_node, - "linux,input-no-autorepeat"); - wakeup = of_property_read_bool(pdev->dev.of_node, - "linux,keypad-wakeup"); + repeat = !of_property_read_bool(np, "linux,input-no-autorepeat"); + + wakeup = of_property_read_bool(np, "wakeup-source") || + /* legacy name */ + of_property_read_bool(np, "linux,keypad-wakeup"); kp = devm_kzalloc(&pdev->dev, sizeof(*kp), GFP_KERNEL); if (!kp) diff --git a/drivers/input/keyboard/qt1070.c b/drivers/input/keyboard/qt1070.c index 52cd6e88acd7..5a5778729e37 100644 --- a/drivers/input/keyboard/qt1070.c +++ b/drivers/input/keyboard/qt1070.c @@ -277,7 +277,6 @@ MODULE_DEVICE_TABLE(i2c, qt1070_id); static struct i2c_driver qt1070_driver = { .driver = { .name = "qt1070", - .owner = THIS_MODULE, .pm = &qt1070_pm_ops, }, .id_table = qt1070_id, diff --git a/drivers/input/keyboard/qt2160.c b/drivers/input/keyboard/qt2160.c index 819b22897c13..43b86482dda0 100644 --- a/drivers/input/keyboard/qt2160.c +++ b/drivers/input/keyboard/qt2160.c @@ -497,7 +497,6 @@ MODULE_DEVICE_TABLE(i2c, qt2160_idtable); static struct i2c_driver qt2160_driver = { .driver = { .name = "qt2160", - .owner = THIS_MODULE, }, .id_table = qt2160_idtable, diff --git a/drivers/input/keyboard/samsung-keypad.c b/drivers/input/keyboard/samsung-keypad.c index 43e48dac7687..4e319eb9e19d 100644 --- a/drivers/input/keyboard/samsung-keypad.c +++ b/drivers/input/keyboard/samsung-keypad.c @@ -299,8 +299,10 @@ samsung_keypad_parse_dt(struct device *dev) if (of_get_property(np, "linux,input-no-autorepeat", NULL)) pdata->no_autorepeat = true; - if (of_get_property(np, "linux,input-wakeup", NULL)) - pdata->wakeup = true; + pdata->wakeup = of_property_read_bool(np, "wakeup-source") || + /* legacy name */ + of_property_read_bool(np, "linux,input-wakeup"); + return pdata; } diff --git a/drivers/input/keyboard/tc3589x-keypad.c b/drivers/input/keyboard/tc3589x-keypad.c index 31c606a4dd31..e92dfd8889c2 100644 --- a/drivers/input/keyboard/tc3589x-keypad.c +++ b/drivers/input/keyboard/tc3589x-keypad.c @@ -17,6 +17,7 @@ #include <linux/i2c.h> #include <linux/slab.h> #include <linux/mfd/tc3589x.h> +#include <linux/device.h> /* Maximum supported keypad matrix row/columns size */ #define TC3589x_MAX_KPROW 8 @@ -352,7 +353,10 @@ tc3589x_keypad_of_probe(struct device *dev) } plat->no_autorepeat = of_property_read_bool(np, "linux,no-autorepeat"); - plat->enable_wakeup = of_property_read_bool(np, "linux,wakeup"); + + plat->enable_wakeup = of_property_read_bool(np, "wakeup-source") || + /* legacy name */ + of_property_read_bool(np, "linux,wakeup"); /* The custom delay format is ms/16 */ of_property_read_u32(np, "debounce-delay-ms", &debounce_ms); @@ -386,12 +390,15 @@ static int tc3589x_keypad_probe(struct platform_device *pdev) if (irq < 0) return irq; - keypad = kzalloc(sizeof(struct tc_keypad), GFP_KERNEL); - input = input_allocate_device(); - if (!keypad || !input) { - dev_err(&pdev->dev, "failed to allocate keypad memory\n"); - error = -ENOMEM; - goto err_free_mem; + keypad = devm_kzalloc(&pdev->dev, sizeof(struct tc_keypad), + GFP_KERNEL); + if (!keypad) + return -ENOMEM; + + input = devm_input_allocate_device(&pdev->dev); + if (!input) { + dev_err(&pdev->dev, "failed to allocate input device\n"); + return -ENOMEM; } keypad->board = plat; @@ -410,7 +417,7 @@ static int tc3589x_keypad_probe(struct platform_device *pdev) NULL, input); if (error) { dev_err(&pdev->dev, "Failed to build keymap\n"); - goto err_free_mem; + return error; } keypad->keymap = input->keycode; @@ -421,20 +428,23 @@ static int tc3589x_keypad_probe(struct platform_device *pdev) input_set_drvdata(input, keypad); - error = request_threaded_irq(irq, NULL, tc3589x_keypad_irq, - plat->irqtype | IRQF_ONESHOT, - "tc3589x-keypad", keypad); - if (error < 0) { + tc3589x_keypad_disable(keypad); + + error = devm_request_threaded_irq(&pdev->dev, irq, + NULL, tc3589x_keypad_irq, + plat->irqtype | IRQF_ONESHOT, + "tc3589x-keypad", keypad); + if (error) { dev_err(&pdev->dev, "Could not allocate irq %d,error %d\n", irq, error); - goto err_free_mem; + return error; } error = input_register_device(input); if (error) { dev_err(&pdev->dev, "Could not register input device\n"); - goto err_free_irq; + return error; } /* let platform decide if keypad is a wakeup source or not */ @@ -444,30 +454,6 @@ static int tc3589x_keypad_probe(struct platform_device *pdev) platform_set_drvdata(pdev, keypad); return 0; - -err_free_irq: - free_irq(irq, keypad); -err_free_mem: - input_free_device(input); - kfree(keypad); - return error; -} - -static int tc3589x_keypad_remove(struct platform_device *pdev) -{ - struct tc_keypad *keypad = platform_get_drvdata(pdev); - int irq = platform_get_irq(pdev, 0); - - if (!keypad->keypad_stopped) - tc3589x_keypad_disable(keypad); - - free_irq(irq, keypad); - - input_unregister_device(keypad->input); - - kfree(keypad); - - return 0; } #ifdef CONFIG_PM_SLEEP @@ -518,7 +504,6 @@ static struct platform_driver tc3589x_keypad_driver = { .pm = &tc3589x_keypad_dev_pm_ops, }, .probe = tc3589x_keypad_probe, - .remove = tc3589x_keypad_remove, }; module_platform_driver(tc3589x_keypad_driver); diff --git a/drivers/input/keyboard/tca8418_keypad.c b/drivers/input/keyboard/tca8418_keypad.c index 4e491c1762cf..9002298698fc 100644 --- a/drivers/input/keyboard/tca8418_keypad.c +++ b/drivers/input/keyboard/tca8418_keypad.c @@ -404,7 +404,6 @@ MODULE_ALIAS("i2c:tca8418"); static struct i2c_driver tca8418_keypad_driver = { .driver = { .name = TCA8418_NAME, - .owner = THIS_MODULE, .of_match_table = of_match_ptr(tca8418_dt_ids), }, .probe = tca8418_keypad_probe, diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index c41dec819cdf..906dd1b25e41 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -247,7 +247,7 @@ config INPUT_APANEL config INPUT_GP2A tristate "Sharp GP2AP002A00F I2C Proximity/Opto sensor driver" depends on I2C - depends on GPIOLIB + depends on GPIOLIB || COMPILE_TEST help Say Y here if you have a Sharp GP2AP002A00F proximity/als combo-chip hooked to an I2C bus. @@ -257,7 +257,7 @@ config INPUT_GP2A config INPUT_GPIO_BEEPER tristate "Generic GPIO Beeper support" - depends on GPIOLIB + depends on GPIOLIB || COMPILE_TEST help Say Y here if you have a beeper connected to a GPIO pin. @@ -266,7 +266,7 @@ config INPUT_GPIO_BEEPER config INPUT_GPIO_TILT_POLLED tristate "Polled GPIO tilt switch" - depends on GPIOLIB + depends on GPIOLIB || COMPILE_TEST select INPUT_POLLDEV help This driver implements support for tilt switches connected @@ -557,7 +557,7 @@ config INPUT_PWM_BEEPER config INPUT_GPIO_ROTARY_ENCODER tristate "Rotary encoders connected to GPIO pins" - depends on GPIOLIB + depends on GPIOLIB || COMPILE_TEST help Say Y here to add support for rotary encoders connected to GPIO lines. Check file:Documentation/input/rotary-encoder.txt for more @@ -764,7 +764,8 @@ config INPUT_SOC_BUTTON_ARRAY config INPUT_DRV260X_HAPTICS tristate "TI DRV260X haptics support" - depends on INPUT && I2C && GPIOLIB + depends on INPUT && I2C + depends on GPIOLIB || COMPILE_TEST select INPUT_FF_MEMLESS select REGMAP_I2C help diff --git a/drivers/input/misc/ab8500-ponkey.c b/drivers/input/misc/ab8500-ponkey.c index 1f7e15ca5fbe..4f5ef5bb535b 100644 --- a/drivers/input/misc/ab8500-ponkey.c +++ b/drivers/input/misc/ab8500-ponkey.c @@ -118,6 +118,7 @@ static const struct of_device_id ab8500_ponkey_match[] = { { .compatible = "stericsson,ab8500-ponkey", }, {} }; +MODULE_DEVICE_TABLE(of, ab8500_ponkey_match); #endif static struct platform_driver ab8500_ponkey_driver = { diff --git a/drivers/input/misc/adxl34x-i2c.c b/drivers/input/misc/adxl34x-i2c.c index bdb5d03b296e..a8b0a2eec344 100644 --- a/drivers/input/misc/adxl34x-i2c.c +++ b/drivers/input/misc/adxl34x-i2c.c @@ -158,7 +158,6 @@ MODULE_DEVICE_TABLE(of, adxl34x_of_id); static struct i2c_driver adxl34x_driver = { .driver = { .name = "adxl34x", - .owner = THIS_MODULE, .pm = &adxl34x_i2c_pm, .of_match_table = of_match_ptr(adxl34x_of_id), }, diff --git a/drivers/input/misc/arizona-haptics.c b/drivers/input/misc/arizona-haptics.c index 4dbbed74c9e4..4bf678541496 100644 --- a/drivers/input/misc/arizona-haptics.c +++ b/drivers/input/misc/arizona-haptics.c @@ -170,8 +170,8 @@ static int arizona_haptics_probe(struct platform_device *pdev) INIT_WORK(&haptics->work, arizona_haptics_work); - haptics->input_dev = input_allocate_device(); - if (haptics->input_dev == NULL) { + haptics->input_dev = devm_input_allocate_device(&pdev->dev); + if (!haptics->input_dev) { dev_err(arizona->dev, "Failed to allocate input device\n"); return -ENOMEM; } @@ -188,41 +188,23 @@ static int arizona_haptics_probe(struct platform_device *pdev) if (ret < 0) { dev_err(arizona->dev, "input_ff_create_memless() failed: %d\n", ret); - goto err_ialloc; + return ret; } ret = input_register_device(haptics->input_dev); if (ret < 0) { dev_err(arizona->dev, "couldn't register input device: %d\n", ret); - goto err_iff; + return ret; } platform_set_drvdata(pdev, haptics); return 0; - -err_iff: - if (haptics->input_dev) - input_ff_destroy(haptics->input_dev); -err_ialloc: - input_free_device(haptics->input_dev); - - return ret; -} - -static int arizona_haptics_remove(struct platform_device *pdev) -{ - struct arizona_haptics *haptics = platform_get_drvdata(pdev); - - input_unregister_device(haptics->input_dev); - - return 0; } static struct platform_driver arizona_haptics_driver = { .probe = arizona_haptics_probe, - .remove = arizona_haptics_remove, .driver = { .name = "arizona-haptics", }, diff --git a/drivers/input/misc/bma150.c b/drivers/input/misc/bma150.c index b36831c828d3..1d0e61d7c131 100644 --- a/drivers/input/misc/bma150.c +++ b/drivers/input/misc/bma150.c @@ -333,10 +333,9 @@ static void bma150_report_xyz(struct bma150_data *bma150) y = ((0xc0 & data[2]) >> 6) | (data[3] << 2); z = ((0xc0 & data[4]) >> 6) | (data[5] << 2); - /* sign extension */ - x = (s16) (x << 6) >> 6; - y = (s16) (y << 6) >> 6; - z = (s16) (z << 6) >> 6; + x = sign_extend32(x, 9); + y = sign_extend32(y, 9); + z = sign_extend32(z, 9); input_report_abs(bma150->input, ABS_X, x); input_report_abs(bma150->input, ABS_Y, y); @@ -654,7 +653,6 @@ MODULE_DEVICE_TABLE(i2c, bma150_id); static struct i2c_driver bma150_driver = { .driver = { - .owner = THIS_MODULE, .name = BMA150_DRIVER, .pm = &bma150_pm, }, diff --git a/drivers/input/misc/cma3000_d0x_i2c.c b/drivers/input/misc/cma3000_d0x_i2c.c index 4fdef98ceb56..c7021916b64b 100644 --- a/drivers/input/misc/cma3000_d0x_i2c.c +++ b/drivers/input/misc/cma3000_d0x_i2c.c @@ -118,7 +118,6 @@ static struct i2c_driver cma3000_i2c_driver = { .id_table = cma3000_i2c_id, .driver = { .name = "cma3000_i2c_accl", - .owner = THIS_MODULE, #ifdef CONFIG_PM .pm = &cma3000_i2c_pm_ops, #endif diff --git a/drivers/input/misc/drv260x.c b/drivers/input/misc/drv260x.c index e5d60ecd29a4..2adfd86c869a 100644 --- a/drivers/input/misc/drv260x.c +++ b/drivers/input/misc/drv260x.c @@ -204,7 +204,7 @@ struct drv260x_data { int overdrive_voltage; }; -static struct reg_default drv260x_reg_defs[] = { +static const struct reg_default drv260x_reg_defs[] = { { DRV260X_STATUS, 0xe0 }, { DRV260X_MODE, 0x40 }, { DRV260X_RT_PB_IN, 0x00 }, @@ -313,14 +313,14 @@ static void drv260x_close(struct input_dev *input) gpiod_set_value(haptics->enable_gpio, 0); } -static const struct reg_default drv260x_lra_cal_regs[] = { +static const struct reg_sequence drv260x_lra_cal_regs[] = { { DRV260X_MODE, DRV260X_AUTO_CAL }, { DRV260X_CTRL3, DRV260X_NG_THRESH_2 }, { DRV260X_FEEDBACK_CTRL, DRV260X_FB_REG_LRA_MODE | DRV260X_BRAKE_FACTOR_4X | DRV260X_LOOP_GAIN_HIGH }, }; -static const struct reg_default drv260x_lra_init_regs[] = { +static const struct reg_sequence drv260x_lra_init_regs[] = { { DRV260X_MODE, DRV260X_RT_PLAYBACK }, { DRV260X_A_TO_V_CTRL, DRV260X_AUDIO_HAPTICS_PEAK_20MS | DRV260X_AUDIO_HAPTICS_FILTER_125HZ }, @@ -337,7 +337,7 @@ static const struct reg_default drv260x_lra_init_regs[] = { { DRV260X_CTRL4, DRV260X_AUTOCAL_TIME_500MS }, }; -static const struct reg_default drv260x_erm_cal_regs[] = { +static const struct reg_sequence drv260x_erm_cal_regs[] = { { DRV260X_MODE, DRV260X_AUTO_CAL }, { DRV260X_A_TO_V_MIN_INPUT, DRV260X_AUDIO_HAPTICS_MIN_IN_VOLT }, { DRV260X_A_TO_V_MAX_INPUT, DRV260X_AUDIO_HAPTICS_MAX_IN_VOLT }, @@ -720,7 +720,6 @@ static struct i2c_driver drv260x_driver = { .probe = drv260x_probe, .driver = { .name = "drv260x-haptics", - .owner = THIS_MODULE, .of_match_table = of_match_ptr(drv260x_of_match), .pm = &drv260x_pm_ops, }, diff --git a/drivers/input/misc/drv2665.c b/drivers/input/misc/drv2665.c index 0afaa33de07d..ef9bc12b3be3 100644 --- a/drivers/input/misc/drv2665.c +++ b/drivers/input/misc/drv2665.c @@ -74,7 +74,7 @@ static const u8 drv2665_sine_wave_form[] = { 0x9b, 0x9f, 0xa5, 0xad, 0xb8, 0xc4, 0xd2, 0xe0, 0xf0, 0x00, }; -static struct reg_default drv2665_reg_defs[] = { +static const struct reg_default drv2665_reg_defs[] = { { DRV2665_STATUS, 0x02 }, { DRV2665_CTRL_1, 0x28 }, { DRV2665_CTRL_2, 0x40 }, @@ -132,7 +132,7 @@ static void drv2665_close(struct input_dev *input) "Failed to enter standby mode: %d\n", error); } -static const struct reg_default drv2665_init_regs[] = { +static const struct reg_sequence drv2665_init_regs[] = { { DRV2665_CTRL_2, 0 | DRV2665_10_MS_IDLE_TOUT }, { DRV2665_CTRL_1, DRV2665_25_VPP_GAIN }, }; @@ -309,7 +309,6 @@ static struct i2c_driver drv2665_driver = { .probe = drv2665_probe, .driver = { .name = "drv2665-haptics", - .owner = THIS_MODULE, .of_match_table = of_match_ptr(drv2665_of_match), .pm = &drv2665_pm_ops, }, diff --git a/drivers/input/misc/drv2667.c b/drivers/input/misc/drv2667.c index fc0fddf0896a..d5ba7481328c 100644 --- a/drivers/input/misc/drv2667.c +++ b/drivers/input/misc/drv2667.c @@ -116,7 +116,7 @@ struct drv2667_data { u32 frequency; }; -static struct reg_default drv2667_reg_defs[] = { +static const struct reg_default drv2667_reg_defs[] = { { DRV2667_STATUS, 0x02 }, { DRV2667_CTRL_1, 0x28 }, { DRV2667_CTRL_2, 0x40 }, @@ -262,14 +262,14 @@ static void drv2667_close(struct input_dev *input) "Failed to enter standby mode: %d\n", error); } -static const struct reg_default drv2667_init_regs[] = { +static const struct reg_sequence drv2667_init_regs[] = { { DRV2667_CTRL_2, 0 }, { DRV2667_CTRL_1, DRV2667_25_VPP_GAIN }, { DRV2667_WV_SEQ_0, 1 }, { DRV2667_WV_SEQ_1, 0 } }; -static const struct reg_default drv2667_page1_init[] = { +static const struct reg_sequence drv2667_page1_init[] = { { DRV2667_RAM_HDR_SZ, 0x05 }, { DRV2667_RAM_START_HI, 0x80 }, { DRV2667_RAM_START_LO, 0x06 }, @@ -484,7 +484,6 @@ static struct i2c_driver drv2667_driver = { .probe = drv2667_probe, .driver = { .name = "drv2667-haptics", - .owner = THIS_MODULE, .of_match_table = of_match_ptr(drv2667_of_match), .pm = &drv2667_pm_ops, }, diff --git a/drivers/input/misc/gp2ap002a00f.c b/drivers/input/misc/gp2ap002a00f.c index 0ac176d66a6f..3bfdfcc20485 100644 --- a/drivers/input/misc/gp2ap002a00f.c +++ b/drivers/input/misc/gp2ap002a00f.c @@ -267,11 +267,11 @@ static const struct i2c_device_id gp2a_i2c_id[] = { { GP2A_I2C_NAME, 0 }, { } }; +MODULE_DEVICE_TABLE(i2c, gp2a_i2c_id); static struct i2c_driver gp2a_i2c_driver = { .driver = { .name = GP2A_I2C_NAME, - .owner = THIS_MODULE, .pm = &gp2a_pm, }, .probe = gp2a_probe, diff --git a/drivers/input/misc/kxtj9.c b/drivers/input/misc/kxtj9.c index 6e29349da537..e058d711256a 100644 --- a/drivers/input/misc/kxtj9.c +++ b/drivers/input/misc/kxtj9.c @@ -658,7 +658,6 @@ MODULE_DEVICE_TABLE(i2c, kxtj9_id); static struct i2c_driver kxtj9_driver = { .driver = { .name = NAME, - .owner = THIS_MODULE, .pm = &kxtj9_pm_ops, }, .probe = kxtj9_probe, diff --git a/drivers/input/misc/max8997_haptic.c b/drivers/input/misc/max8997_haptic.c index d0f687281339..a806ba3818f7 100644 --- a/drivers/input/misc/max8997_haptic.c +++ b/drivers/input/misc/max8997_haptic.c @@ -394,7 +394,7 @@ static const struct platform_device_id max8997_haptic_id[] = { { "max8997-haptic", 0 }, { }, }; -MODULE_DEVICE_TABLE(i2c, max8997_haptic_id); +MODULE_DEVICE_TABLE(platform, max8997_haptic_id); static struct platform_driver max8997_haptic_driver = { .driver = { @@ -407,7 +407,6 @@ static struct platform_driver max8997_haptic_driver = { }; module_platform_driver(max8997_haptic_driver); -MODULE_ALIAS("platform:max8997-haptic"); MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>"); MODULE_DESCRIPTION("max8997_haptic driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/mpu3050.c b/drivers/input/misc/mpu3050.c index 5e5051351c3a..f088db31cfc7 100644 --- a/drivers/input/misc/mpu3050.c +++ b/drivers/input/misc/mpu3050.c @@ -466,7 +466,6 @@ MODULE_DEVICE_TABLE(of, mpu3050_of_match); static struct i2c_driver mpu3050_i2c_driver = { .driver = { .name = "mpu3050", - .owner = THIS_MODULE, .pm = &mpu3050_pm, .of_match_table = mpu3050_of_match, }, diff --git a/drivers/input/misc/pcf8574_keypad.c b/drivers/input/misc/pcf8574_keypad.c index 97f711a7bd20..4abdf1efb3e0 100644 --- a/drivers/input/misc/pcf8574_keypad.c +++ b/drivers/input/misc/pcf8574_keypad.c @@ -208,7 +208,6 @@ MODULE_DEVICE_TABLE(i2c, pcf8574_kp_id); static struct i2c_driver pcf8574_kp_driver = { .driver = { .name = DRV_NAME, - .owner = THIS_MODULE, #ifdef CONFIG_PM .pm = &pcf8574_kp_pm_ops, #endif diff --git a/drivers/input/misc/pmic8xxx-pwrkey.c b/drivers/input/misc/pmic8xxx-pwrkey.c index c4ca20e63221..3f02e0e03d12 100644 --- a/drivers/input/misc/pmic8xxx-pwrkey.c +++ b/drivers/input/misc/pmic8xxx-pwrkey.c @@ -20,17 +20,72 @@ #include <linux/regmap.h> #include <linux/log2.h> #include <linux/of.h> +#include <linux/of_device.h> #define PON_CNTL_1 0x1C #define PON_CNTL_PULL_UP BIT(7) #define PON_CNTL_TRIG_DELAY_MASK (0x7) +#define PON_CNTL_1_PULL_UP_EN 0xe0 +#define PON_CNTL_1_USB_PWR_EN 0x10 +#define PON_CNTL_1_WD_EN_RESET 0x08 + +#define PM8058_SLEEP_CTRL 0x02b +#define PM8921_SLEEP_CTRL 0x10a + +#define SLEEP_CTRL_SMPL_EN_RESET 0x04 + +/* Regulator master enable addresses */ +#define REG_PM8058_VREG_EN_MSM 0x018 +#define REG_PM8058_VREG_EN_GRP_5_4 0x1c8 + +/* Regulator control registers for shutdown/reset */ +#define PM8058_S0_CTRL 0x004 +#define PM8058_S1_CTRL 0x005 +#define PM8058_S3_CTRL 0x111 +#define PM8058_L21_CTRL 0x120 +#define PM8058_L22_CTRL 0x121 + +#define PM8058_REGULATOR_ENABLE_MASK 0x80 +#define PM8058_REGULATOR_ENABLE 0x80 +#define PM8058_REGULATOR_DISABLE 0x00 +#define PM8058_REGULATOR_PULL_DOWN_MASK 0x40 +#define PM8058_REGULATOR_PULL_DOWN_EN 0x40 + +/* Buck CTRL register */ +#define PM8058_SMPS_LEGACY_VREF_SEL 0x20 +#define PM8058_SMPS_LEGACY_VPROG_MASK 0x1f +#define PM8058_SMPS_ADVANCED_BAND_MASK 0xC0 +#define PM8058_SMPS_ADVANCED_BAND_SHIFT 6 +#define PM8058_SMPS_ADVANCED_VPROG_MASK 0x3f + +/* Buck TEST2 registers for shutdown/reset */ +#define PM8058_S0_TEST2 0x084 +#define PM8058_S1_TEST2 0x085 +#define PM8058_S3_TEST2 0x11a + +#define PM8058_REGULATOR_BANK_WRITE 0x80 +#define PM8058_REGULATOR_BANK_MASK 0x70 +#define PM8058_REGULATOR_BANK_SHIFT 4 +#define PM8058_REGULATOR_BANK_SEL(n) ((n) << PM8058_REGULATOR_BANK_SHIFT) + +/* Buck TEST2 register bank 1 */ +#define PM8058_SMPS_LEGACY_VLOW_SEL 0x01 + +/* Buck TEST2 register bank 7 */ +#define PM8058_SMPS_ADVANCED_MODE_MASK 0x02 +#define PM8058_SMPS_ADVANCED_MODE 0x02 +#define PM8058_SMPS_LEGACY_MODE 0x00 /** * struct pmic8xxx_pwrkey - pmic8xxx pwrkey information * @key_press_irq: key press irq number + * @regmap: device regmap + * @shutdown_fn: shutdown configuration function */ struct pmic8xxx_pwrkey { int key_press_irq; + struct regmap *regmap; + int (*shutdown_fn)(struct pmic8xxx_pwrkey *, bool); }; static irqreturn_t pwrkey_press_irq(int irq, void *_pwr) @@ -76,6 +131,212 @@ static int __maybe_unused pmic8xxx_pwrkey_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(pm8xxx_pwr_key_pm_ops, pmic8xxx_pwrkey_suspend, pmic8xxx_pwrkey_resume); +static void pmic8xxx_pwrkey_shutdown(struct platform_device *pdev) +{ + struct pmic8xxx_pwrkey *pwrkey = platform_get_drvdata(pdev); + int error; + u8 mask, val; + bool reset = system_state == SYSTEM_RESTART; + + if (pwrkey->shutdown_fn) { + error = pwrkey->shutdown_fn(pwrkey, reset); + if (error) + return; + } + + /* + * Select action to perform (reset or shutdown) when PS_HOLD goes low. + * Also ensure that KPD, CBL0, and CBL1 pull ups are enabled and that + * USB charging is enabled. + */ + mask = PON_CNTL_1_PULL_UP_EN | PON_CNTL_1_USB_PWR_EN; + mask |= PON_CNTL_1_WD_EN_RESET; + val = mask; + if (!reset) + val &= ~PON_CNTL_1_WD_EN_RESET; + + regmap_update_bits(pwrkey->regmap, PON_CNTL_1, mask, val); +} + +/* + * Set an SMPS regulator to be disabled in its CTRL register, but enabled + * in the master enable register. Also set it's pull down enable bit. + * Take care to make sure that the output voltage doesn't change if switching + * from advanced mode to legacy mode. + */ +static int pm8058_disable_smps_locally_set_pull_down(struct regmap *regmap, + u16 ctrl_addr, u16 test2_addr, u16 master_enable_addr, + u8 master_enable_bit) +{ + int error; + u8 vref_sel, vlow_sel, band, vprog, bank; + unsigned int reg; + + bank = PM8058_REGULATOR_BANK_SEL(7); + error = regmap_write(regmap, test2_addr, bank); + if (error) + return error; + + error = regmap_read(regmap, test2_addr, ®); + if (error) + return error; + + reg &= PM8058_SMPS_ADVANCED_MODE_MASK; + /* Check if in advanced mode. */ + if (reg == PM8058_SMPS_ADVANCED_MODE) { + /* Determine current output voltage. */ + error = regmap_read(regmap, ctrl_addr, ®); + if (error) + return error; + + band = reg & PM8058_SMPS_ADVANCED_BAND_MASK; + band >>= PM8058_SMPS_ADVANCED_BAND_SHIFT; + switch (band) { + case 3: + vref_sel = 0; + vlow_sel = 0; + break; + case 2: + vref_sel = PM8058_SMPS_LEGACY_VREF_SEL; + vlow_sel = 0; + break; + case 1: + vref_sel = PM8058_SMPS_LEGACY_VREF_SEL; + vlow_sel = PM8058_SMPS_LEGACY_VLOW_SEL; + break; + default: + pr_err("%s: regulator already disabled\n", __func__); + return -EPERM; + } + vprog = reg & PM8058_SMPS_ADVANCED_VPROG_MASK; + /* Round up if fine step is in use. */ + vprog = (vprog + 1) >> 1; + if (vprog > PM8058_SMPS_LEGACY_VPROG_MASK) + vprog = PM8058_SMPS_LEGACY_VPROG_MASK; + + /* Set VLOW_SEL bit. */ + bank = PM8058_REGULATOR_BANK_SEL(1); + error = regmap_write(regmap, test2_addr, bank); + if (error) + return error; + + error = regmap_update_bits(regmap, test2_addr, + PM8058_REGULATOR_BANK_WRITE | PM8058_REGULATOR_BANK_MASK + | PM8058_SMPS_LEGACY_VLOW_SEL, + PM8058_REGULATOR_BANK_WRITE | + PM8058_REGULATOR_BANK_SEL(1) | vlow_sel); + if (error) + return error; + + /* Switch to legacy mode */ + bank = PM8058_REGULATOR_BANK_SEL(7); + error = regmap_write(regmap, test2_addr, bank); + if (error) + return error; + + error = regmap_update_bits(regmap, test2_addr, + PM8058_REGULATOR_BANK_WRITE | + PM8058_REGULATOR_BANK_MASK | + PM8058_SMPS_ADVANCED_MODE_MASK, + PM8058_REGULATOR_BANK_WRITE | + PM8058_REGULATOR_BANK_SEL(7) | + PM8058_SMPS_LEGACY_MODE); + if (error) + return error; + + /* Enable locally, enable pull down, keep voltage the same. */ + error = regmap_update_bits(regmap, ctrl_addr, + PM8058_REGULATOR_ENABLE_MASK | + PM8058_REGULATOR_PULL_DOWN_MASK | + PM8058_SMPS_LEGACY_VREF_SEL | + PM8058_SMPS_LEGACY_VPROG_MASK, + PM8058_REGULATOR_ENABLE | PM8058_REGULATOR_PULL_DOWN_EN + | vref_sel | vprog); + if (error) + return error; + } + + /* Enable in master control register. */ + error = regmap_update_bits(regmap, master_enable_addr, + master_enable_bit, master_enable_bit); + if (error) + return error; + + /* Disable locally and enable pull down. */ + return regmap_update_bits(regmap, ctrl_addr, + PM8058_REGULATOR_ENABLE_MASK | PM8058_REGULATOR_PULL_DOWN_MASK, + PM8058_REGULATOR_DISABLE | PM8058_REGULATOR_PULL_DOWN_EN); +} + +static int pm8058_disable_ldo_locally_set_pull_down(struct regmap *regmap, + u16 ctrl_addr, u16 master_enable_addr, u8 master_enable_bit) +{ + int error; + + /* Enable LDO in master control register. */ + error = regmap_update_bits(regmap, master_enable_addr, + master_enable_bit, master_enable_bit); + if (error) + return error; + + /* Disable LDO in CTRL register and set pull down */ + return regmap_update_bits(regmap, ctrl_addr, + PM8058_REGULATOR_ENABLE_MASK | PM8058_REGULATOR_PULL_DOWN_MASK, + PM8058_REGULATOR_DISABLE | PM8058_REGULATOR_PULL_DOWN_EN); +} + +static int pm8058_pwrkey_shutdown(struct pmic8xxx_pwrkey *pwrkey, bool reset) +{ + int error; + struct regmap *regmap = pwrkey->regmap; + u8 mask, val; + + /* When shutting down, enable active pulldowns on important rails. */ + if (!reset) { + /* Disable SMPS's 0,1,3 locally and set pulldown enable bits. */ + pm8058_disable_smps_locally_set_pull_down(regmap, + PM8058_S0_CTRL, PM8058_S0_TEST2, + REG_PM8058_VREG_EN_MSM, BIT(7)); + pm8058_disable_smps_locally_set_pull_down(regmap, + PM8058_S1_CTRL, PM8058_S1_TEST2, + REG_PM8058_VREG_EN_MSM, BIT(6)); + pm8058_disable_smps_locally_set_pull_down(regmap, + PM8058_S3_CTRL, PM8058_S3_TEST2, + REG_PM8058_VREG_EN_GRP_5_4, BIT(7) | BIT(4)); + /* Disable LDO 21 locally and set pulldown enable bit. */ + pm8058_disable_ldo_locally_set_pull_down(regmap, + PM8058_L21_CTRL, REG_PM8058_VREG_EN_GRP_5_4, + BIT(1)); + } + + /* + * Fix-up: Set regulator LDO22 to 1.225 V in high power mode. Leave its + * pull-down state intact. This ensures a safe shutdown. + */ + error = regmap_update_bits(regmap, PM8058_L22_CTRL, 0xbf, 0x93); + if (error) + return error; + + /* Enable SMPL if resetting is desired */ + mask = SLEEP_CTRL_SMPL_EN_RESET; + val = 0; + if (reset) + val = mask; + return regmap_update_bits(regmap, PM8058_SLEEP_CTRL, mask, val); +} + +static int pm8921_pwrkey_shutdown(struct pmic8xxx_pwrkey *pwrkey, bool reset) +{ + struct regmap *regmap = pwrkey->regmap; + u8 mask = SLEEP_CTRL_SMPL_EN_RESET; + u8 val = 0; + + /* Enable SMPL if resetting is desired */ + if (reset) + val = mask; + return regmap_update_bits(regmap, PM8921_SLEEP_CTRL, mask, val); +} + static int pmic8xxx_pwrkey_probe(struct platform_device *pdev) { struct input_dev *pwr; @@ -109,6 +370,8 @@ static int pmic8xxx_pwrkey_probe(struct platform_device *pdev) if (!pwrkey) return -ENOMEM; + pwrkey->shutdown_fn = of_device_get_match_data(&pdev->dev); + pwrkey->regmap = regmap; pwrkey->key_press_irq = key_press_irq; pwr = devm_input_allocate_device(&pdev->dev); @@ -182,8 +445,8 @@ static int pmic8xxx_pwrkey_remove(struct platform_device *pdev) } static const struct of_device_id pm8xxx_pwr_key_id_table[] = { - { .compatible = "qcom,pm8058-pwrkey" }, - { .compatible = "qcom,pm8921-pwrkey" }, + { .compatible = "qcom,pm8058-pwrkey", .data = &pm8058_pwrkey_shutdown }, + { .compatible = "qcom,pm8921-pwrkey", .data = &pm8921_pwrkey_shutdown }, { } }; MODULE_DEVICE_TABLE(of, pm8xxx_pwr_key_id_table); @@ -191,6 +454,7 @@ MODULE_DEVICE_TABLE(of, pm8xxx_pwr_key_id_table); static struct platform_driver pmic8xxx_pwrkey_driver = { .probe = pmic8xxx_pwrkey_probe, .remove = pmic8xxx_pwrkey_remove, + .shutdown = pmic8xxx_pwrkey_shutdown, .driver = { .name = "pm8xxx-pwrkey", .pm = &pm8xxx_pwr_key_pm_ops, diff --git a/drivers/input/misc/pwm-beeper.c b/drivers/input/misc/pwm-beeper.c index e82edf810d1f..f2261ab54701 100644 --- a/drivers/input/misc/pwm-beeper.c +++ b/drivers/input/misc/pwm-beeper.c @@ -173,6 +173,7 @@ static const struct of_device_id pwm_beeper_match[] = { { .compatible = "pwm-beeper", }, { }, }; +MODULE_DEVICE_TABLE(of, pwm_beeper_match); #endif static struct platform_driver pwm_beeper_driver = { diff --git a/drivers/input/misc/rb532_button.c b/drivers/input/misc/rb532_button.c index e956e81cd4e6..62c5814c796b 100644 --- a/drivers/input/misc/rb532_button.c +++ b/drivers/input/misc/rb532_button.c @@ -7,6 +7,7 @@ #include <linux/input-polldev.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/gpio.h> #include <asm/mach-rc32434/gpio.h> #include <asm/mach-rc32434/rb.h> diff --git a/drivers/input/misc/regulator-haptic.c b/drivers/input/misc/regulator-haptic.c index 6bf3f1082f71..a804705eb04a 100644 --- a/drivers/input/misc/regulator-haptic.c +++ b/drivers/input/misc/regulator-haptic.c @@ -249,6 +249,7 @@ static const struct of_device_id regulator_haptic_dt_match[] = { { .compatible = "regulator-haptic" }, { /* sentinel */ }, }; +MODULE_DEVICE_TABLE(of, regulator_haptic_dt_match); static struct platform_driver regulator_haptic_driver = { .probe = regulator_haptic_probe, diff --git a/drivers/input/misc/sparcspkr.c b/drivers/input/misc/sparcspkr.c index 54116e544c96..6f997aa49183 100644 --- a/drivers/input/misc/sparcspkr.c +++ b/drivers/input/misc/sparcspkr.c @@ -253,6 +253,7 @@ static const struct of_device_id bbc_beep_match[] = { }, {}, }; +MODULE_DEVICE_TABLE(of, bbc_beep_match); static struct platform_driver bbc_beep_driver = { .driver = { @@ -332,6 +333,7 @@ static const struct of_device_id grover_beep_match[] = { }, {}, }; +MODULE_DEVICE_TABLE(of, grover_beep_match); static struct platform_driver grover_beep_driver = { .driver = { diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c index 421e29e4cd81..345df9b03aed 100644 --- a/drivers/input/misc/uinput.c +++ b/drivers/input/misc/uinput.c @@ -320,10 +320,8 @@ static int uinput_validate_absbits(struct input_dev *dev) * Check if absmin/absmax/absfuzz/absflat are sane. */ - for (cnt = 0; cnt < ABS_CNT; cnt++) { + for_each_set_bit(cnt, dev->absbit, ABS_CNT) { int min, max; - if (!test_bit(cnt, dev->absbit)) - continue; min = input_abs_get_min(dev, cnt); max = input_abs_get_max(dev, cnt); @@ -416,7 +414,7 @@ static int uinput_setup_device(struct uinput_device *udev, dev->id.product = user_dev->id.product; dev->id.version = user_dev->id.version; - for (i = 0; i < ABS_CNT; i++) { + for_each_set_bit(i, dev->absbit, ABS_CNT) { input_abs_set_max(dev, i, user_dev->absmax[i]); input_abs_set_min(dev, i, user_dev->absmin[i]); input_abs_set_fuzz(dev, i, user_dev->absfuzz[i]); diff --git a/drivers/input/misc/xen-kbdfront.c b/drivers/input/misc/xen-kbdfront.c index 95599e478e19..23d0549539d4 100644 --- a/drivers/input/misc/xen-kbdfront.c +++ b/drivers/input/misc/xen-kbdfront.c @@ -232,7 +232,7 @@ static int xenkbd_connect_backend(struct xenbus_device *dev, struct xenbus_transaction xbt; ret = gnttab_grant_foreign_access(dev->otherend_id, - virt_to_mfn(info->page), 0); + virt_to_gfn(info->page), 0); if (ret < 0) return ret; info->gref = ret; @@ -255,7 +255,7 @@ static int xenkbd_connect_backend(struct xenbus_device *dev, goto error_irqh; } ret = xenbus_printf(xbt, dev->nodename, "page-ref", "%lu", - virt_to_mfn(info->page)); + virt_to_gfn(info->page)); if (ret) goto error_xenbus; ret = xenbus_printf(xbt, dev->nodename, "page-gref", "%u", info->gref); diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig index d7820d1152d2..17f97e5e11e7 100644 --- a/drivers/input/mouse/Kconfig +++ b/drivers/input/mouse/Kconfig @@ -341,7 +341,7 @@ config MOUSE_VSXXXAA config MOUSE_GPIO tristate "GPIO mouse" - depends on GPIOLIB + depends on GPIOLIB || COMPILE_TEST select INPUT_POLLDEV help This driver simulates a mouse on GPIO lines of various CPUs (and some diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile index 793300bfbddd..ee6a6e9563d4 100644 --- a/drivers/input/mouse/Makefile +++ b/drivers/input/mouse/Makefile @@ -24,7 +24,7 @@ obj-$(CONFIG_MOUSE_SYNAPTICS_I2C) += synaptics_i2c.o obj-$(CONFIG_MOUSE_SYNAPTICS_USB) += synaptics_usb.o obj-$(CONFIG_MOUSE_VSXXXAA) += vsxxxaa.o -cyapatp-objs := cyapa.o cyapa_gen3.o cyapa_gen5.o +cyapatp-objs := cyapa.o cyapa_gen3.o cyapa_gen5.o cyapa_gen6.o psmouse-objs := psmouse-base.o synaptics.o focaltech.o psmouse-$(CONFIG_MOUSE_PS2_ALPS) += alps.o diff --git a/drivers/input/mouse/cyapa.c b/drivers/input/mouse/cyapa.c index efe148474e7f..eb76b61418f3 100644 --- a/drivers/input/mouse/cyapa.c +++ b/drivers/input/mouse/cyapa.c @@ -6,7 +6,7 @@ * Daniel Kurtz <djkurtz@chromium.org> * Benson Leung <bleung@chromium.org> * - * Copyright (C) 2011-2014 Cypress Semiconductor, Inc. + * Copyright (C) 2011-2015 Cypress Semiconductor, Inc. * Copyright (C) 2011-2012 Google, Inc. * * This file is subject to the terms and conditions of the GNU General Public @@ -21,10 +21,12 @@ #include <linux/interrupt.h> #include <linux/module.h> #include <linux/mutex.h> +#include <linux/regulator/consumer.h> #include <linux/slab.h> #include <linux/uaccess.h> #include <linux/pm_runtime.h> #include <linux/acpi.h> +#include <linux/of.h> #include "cyapa.h" @@ -39,11 +41,33 @@ const char product_id[] = "CYTRA"; static int cyapa_reinitialize(struct cyapa *cyapa); -static inline bool cyapa_is_bootloader_mode(struct cyapa *cyapa) +bool cyapa_is_pip_bl_mode(struct cyapa *cyapa) { + if (cyapa->gen == CYAPA_GEN6 && cyapa->state == CYAPA_STATE_GEN6_BL) + return true; + if (cyapa->gen == CYAPA_GEN5 && cyapa->state == CYAPA_STATE_GEN5_BL) return true; + return false; +} + +bool cyapa_is_pip_app_mode(struct cyapa *cyapa) +{ + if (cyapa->gen == CYAPA_GEN6 && cyapa->state == CYAPA_STATE_GEN6_APP) + return true; + + if (cyapa->gen == CYAPA_GEN5 && cyapa->state == CYAPA_STATE_GEN5_APP) + return true; + + return false; +} + +static bool cyapa_is_bootloader_mode(struct cyapa *cyapa) +{ + if (cyapa_is_pip_bl_mode(cyapa)) + return true; + if (cyapa->gen == CYAPA_GEN3 && cyapa->state >= CYAPA_STATE_BL_BUSY && cyapa->state <= CYAPA_STATE_BL_ACTIVE) @@ -54,7 +78,7 @@ static inline bool cyapa_is_bootloader_mode(struct cyapa *cyapa) static inline bool cyapa_is_operational_mode(struct cyapa *cyapa) { - if (cyapa->gen == CYAPA_GEN5 && cyapa->state == CYAPA_STATE_GEN5_APP) + if (cyapa_is_pip_app_mode(cyapa)) return true; if (cyapa->gen == CYAPA_GEN3 && cyapa->state == CYAPA_STATE_OP) @@ -188,6 +212,15 @@ static int cyapa_get_state(struct cyapa *cyapa) if (!error) goto out_detected; } + if (cyapa->gen == CYAPA_GEN_UNKNOWN || + cyapa->gen == CYAPA_GEN6 || + cyapa->gen == CYAPA_GEN5) { + error = cyapa_pip_state_parse(cyapa, + status, BL_STATUS_SIZE); + if (!error) + goto out_detected; + } + /* For old Gen5 trackpads detecting. */ if ((cyapa->gen == CYAPA_GEN_UNKNOWN || cyapa->gen == CYAPA_GEN5) && !smbus && even_addr) { @@ -284,6 +317,9 @@ static int cyapa_check_is_operational(struct cyapa *cyapa) return error; switch (cyapa->gen) { + case CYAPA_GEN6: + cyapa->ops = &cyapa_gen6_ops; + break; case CYAPA_GEN5: cyapa->ops = &cyapa_gen5_ops; break; @@ -306,7 +342,7 @@ static int cyapa_check_is_operational(struct cyapa *cyapa) /* * Returns 0 on device detected, negative errno on no device detected. - * And when the device is detected and opertaional, it will be reset to + * And when the device is detected and operational, it will be reset to * full power active mode automatically. */ static int cyapa_detect(struct cyapa *cyapa) @@ -333,6 +369,7 @@ static int cyapa_open(struct input_dev *input) { struct cyapa *cyapa = input_get_drvdata(input); struct i2c_client *client = cyapa->client; + struct device *dev = &client->dev; int error; error = mutex_lock_interruptible(&cyapa->state_sync_lock); @@ -346,10 +383,9 @@ static int cyapa_open(struct input_dev *input) * when in operational mode. */ error = cyapa->ops->set_power_mode(cyapa, - PWR_MODE_FULL_ACTIVE, 0); + PWR_MODE_FULL_ACTIVE, 0, false); if (error) { - dev_warn(&client->dev, - "set active power failed: %d\n", error); + dev_warn(dev, "set active power failed: %d\n", error); goto out; } } else { @@ -361,10 +397,14 @@ static int cyapa_open(struct input_dev *input) } enable_irq(client->irq); - if (!pm_runtime_enabled(&client->dev)) { - pm_runtime_set_active(&client->dev); - pm_runtime_enable(&client->dev); + if (!pm_runtime_enabled(dev)) { + pm_runtime_set_active(dev); + pm_runtime_enable(dev); } + + pm_runtime_get_sync(dev); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_sync_autosuspend(dev); out: mutex_unlock(&cyapa->state_sync_lock); return error; @@ -374,16 +414,17 @@ static void cyapa_close(struct input_dev *input) { struct cyapa *cyapa = input_get_drvdata(input); struct i2c_client *client = cyapa->client; + struct device *dev = &cyapa->client->dev; mutex_lock(&cyapa->state_sync_lock); disable_irq(client->irq); - if (pm_runtime_enabled(&client->dev)) - pm_runtime_disable(&client->dev); - pm_runtime_set_suspended(&client->dev); + if (pm_runtime_enabled(dev)) + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); if (cyapa->operational) - cyapa->ops->set_power_mode(cyapa, PWR_MODE_OFF, 0); + cyapa->ops->set_power_mode(cyapa, PWR_MODE_OFF, 0, false); mutex_unlock(&cyapa->state_sync_lock); } @@ -443,6 +484,7 @@ static int cyapa_create_input_dev(struct cyapa *cyapa) if (cyapa->gen >= CYAPA_GEN5) { input_set_abs_params(input, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0); input_set_abs_params(input, ABS_MT_WIDTH_MINOR, 0, 255, 0, 0); + input_set_abs_params(input, ABS_DISTANCE, 0, 1, 0, 0); } input_abs_set_res(input, ABS_MT_POSITION_X, @@ -492,7 +534,7 @@ static void cyapa_enable_irq_for_cmd(struct cyapa *cyapa) */ if (!input || cyapa->operational) cyapa->ops->set_power_mode(cyapa, - PWR_MODE_FULL_ACTIVE, 0); + PWR_MODE_FULL_ACTIVE, 0, false); /* Gen3 always using polling mode for command. */ if (cyapa->gen >= CYAPA_GEN5) enable_irq(cyapa->client->irq); @@ -507,7 +549,8 @@ static void cyapa_disable_irq_for_cmd(struct cyapa *cyapa) if (cyapa->gen >= CYAPA_GEN5) disable_irq(cyapa->client->irq); if (!input || cyapa->operational) - cyapa->ops->set_power_mode(cyapa, PWR_MODE_OFF, 0); + cyapa->ops->set_power_mode(cyapa, + PWR_MODE_OFF, 0, false); } } @@ -563,6 +606,8 @@ static int cyapa_initialize(struct cyapa *cyapa) error = cyapa_gen3_ops.initialize(cyapa); if (!error) error = cyapa_gen5_ops.initialize(cyapa); + if (!error) + error = cyapa_gen6_ops.initialize(cyapa); if (error) return error; @@ -572,7 +617,7 @@ static int cyapa_initialize(struct cyapa *cyapa) /* Power down the device until we need it. */ if (cyapa->operational) - cyapa->ops->set_power_mode(cyapa, PWR_MODE_OFF, 0); + cyapa->ops->set_power_mode(cyapa, PWR_MODE_OFF, 0, false); return 0; } @@ -588,7 +633,8 @@ static int cyapa_reinitialize(struct cyapa *cyapa) /* Avoid command failures when TP was in OFF state. */ if (cyapa->operational) - cyapa->ops->set_power_mode(cyapa, PWR_MODE_FULL_ACTIVE, 0); + cyapa->ops->set_power_mode(cyapa, + PWR_MODE_FULL_ACTIVE, 0, false); error = cyapa_detect(cyapa); if (error) @@ -607,7 +653,8 @@ out: if (!input || !input->users) { /* Reset to power OFF state to save power when no user open. */ if (cyapa->operational) - cyapa->ops->set_power_mode(cyapa, PWR_MODE_OFF, 0); + cyapa->ops->set_power_mode(cyapa, + PWR_MODE_OFF, 0, false); } else if (!error && cyapa->operational) { /* * Make sure only enable runtime PM when device is @@ -615,6 +662,10 @@ out: */ pm_runtime_set_active(dev); pm_runtime_enable(dev); + + pm_runtime_get_sync(dev); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_sync_autosuspend(dev); } return error; @@ -624,27 +675,44 @@ static irqreturn_t cyapa_irq(int irq, void *dev_id) { struct cyapa *cyapa = dev_id; struct device *dev = &cyapa->client->dev; + int error; - pm_runtime_get_sync(dev); if (device_may_wakeup(dev)) pm_wakeup_event(dev, 0); - /* Interrupt event maybe cuased by host command to trackpad device. */ + /* Interrupt event can be caused by host command to trackpad device. */ if (cyapa->ops->irq_cmd_handler(cyapa)) { /* * Interrupt event maybe from trackpad device input reporting. */ if (!cyapa->input) { /* - * Still in probling or in firware image - * udpating or reading. + * Still in probing or in firmware image + * updating or reading. */ cyapa->ops->sort_empty_output_data(cyapa, NULL, NULL, NULL); goto out; } - if (!cyapa->operational || cyapa->ops->irq_handler(cyapa)) { + if (cyapa->operational) { + error = cyapa->ops->irq_handler(cyapa); + + /* + * Apply runtime power management to touch report event + * except the events caused by the command responses. + * Note: + * It will introduce about 20~40 ms additional delay + * time in receiving for first valid touch report data. + * The time is used to execute device runtime resume + * process. + */ + pm_runtime_get_sync(dev); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_sync_autosuspend(dev); + } + + if (!cyapa->operational || error) { if (!mutex_trylock(&cyapa->state_sync_lock)) { cyapa->ops->sort_empty_output_data(cyapa, NULL, NULL, NULL); @@ -656,8 +724,6 @@ static irqreturn_t cyapa_irq(int irq, void *dev_id) } out: - pm_runtime_mark_last_busy(dev); - pm_runtime_put_sync_autosuspend(dev); return IRQ_HANDLED; } @@ -1051,12 +1117,12 @@ static ssize_t cyapa_update_fw_store(struct device *dev, dev_dbg(dev, "firmware update successfully done.\n"); /* - * Redetect trackpad device states because firmware update process + * Re-detect trackpad device states because firmware update process * will reset trackpad device into bootloader mode. */ ret = cyapa_reinitialize(cyapa); if (ret) { - dev_err(dev, "failed to redetect after updated: %d\n", ret); + dev_err(dev, "failed to re-detect after updated: %d\n", ret); error = error ? error : ret; } @@ -1120,9 +1186,11 @@ static char *cyapa_state_to_string(struct cyapa *cyapa) case CYAPA_STATE_BL_ACTIVE: return "bootloader active"; case CYAPA_STATE_GEN5_BL: + case CYAPA_STATE_GEN6_BL: return "bootloader"; case CYAPA_STATE_OP: case CYAPA_STATE_GEN5_APP: + case CYAPA_STATE_GEN6_APP: return "operational"; /* Normal valid state. */ default: return "invalid mode"; @@ -1175,6 +1243,13 @@ static void cyapa_remove_sysfs_group(void *data) sysfs_remove_group(&cyapa->client->dev.kobj, &cyapa_sysfs_group); } +static void cyapa_disable_regulator(void *data) +{ + struct cyapa *cyapa = data; + + regulator_disable(cyapa->vcc); +} + static int cyapa_probe(struct i2c_client *client, const struct i2c_device_id *dev_id) { @@ -1208,6 +1283,27 @@ static int cyapa_probe(struct i2c_client *client, sprintf(cyapa->phys, "i2c-%d-%04x/input0", client->adapter->nr, client->addr); + cyapa->vcc = devm_regulator_get(dev, "vcc"); + if (IS_ERR(cyapa->vcc)) { + error = PTR_ERR(cyapa->vcc); + dev_err(dev, "failed to get vcc regulator: %d\n", error); + return error; + } + + error = regulator_enable(cyapa->vcc); + if (error) { + dev_err(dev, "failed to enable regulator: %d\n", error); + return error; + } + + error = devm_add_action(dev, cyapa_disable_regulator, cyapa); + if (error) { + cyapa_disable_regulator(cyapa); + dev_err(dev, "failed to add disable regulator action: %d\n", + error); + return error; + } + error = cyapa_initialize(cyapa); if (error) { dev_err(dev, "failed to detect and initialize tp device.\n"); @@ -1296,12 +1392,19 @@ static int __maybe_unused cyapa_suspend(struct device *dev) power_mode = device_may_wakeup(dev) ? cyapa->suspend_power_mode : PWR_MODE_OFF; error = cyapa->ops->set_power_mode(cyapa, power_mode, - cyapa->suspend_sleep_time); + cyapa->suspend_sleep_time, true); if (error) dev_err(dev, "suspend set power mode failed: %d\n", error); } + /* + * Disable proximity interrupt when system idle, want true touch to + * wake the system. + */ + if (cyapa->dev_pwr_mode != PWR_MODE_OFF) + cyapa->ops->set_proximity(cyapa, false); + if (device_may_wakeup(dev)) cyapa->irq_wake = (enable_irq_wake(client->irq) == 0); @@ -1322,7 +1425,10 @@ static int __maybe_unused cyapa_resume(struct device *dev) cyapa->irq_wake = false; } - /* Update device states and runtime PM states. */ + /* + * Update device states and runtime PM states. + * Re-Enable proximity interrupt after enter operational mode. + */ error = cyapa_reinitialize(cyapa); if (error) dev_warn(dev, "failed to reinitialize TP device: %d\n", error); @@ -1340,7 +1446,8 @@ static int __maybe_unused cyapa_runtime_suspend(struct device *dev) error = cyapa->ops->set_power_mode(cyapa, cyapa->runtime_suspend_power_mode, - cyapa->runtime_suspend_sleep_time); + cyapa->runtime_suspend_sleep_time, + false); if (error) dev_warn(dev, "runtime suspend failed: %d\n", error); @@ -1352,7 +1459,8 @@ static int __maybe_unused cyapa_runtime_resume(struct device *dev) struct cyapa *cyapa = dev_get_drvdata(dev); int error; - error = cyapa->ops->set_power_mode(cyapa, PWR_MODE_FULL_ACTIVE, 0); + error = cyapa->ops->set_power_mode(cyapa, + PWR_MODE_FULL_ACTIVE, 0, false); if (error) dev_warn(dev, "runtime resume failed: %d\n", error); @@ -1374,17 +1482,26 @@ MODULE_DEVICE_TABLE(i2c, cyapa_id_table); static const struct acpi_device_id cyapa_acpi_id[] = { { "CYAP0000", 0 }, /* Gen3 trackpad with 0x67 I2C address. */ { "CYAP0001", 0 }, /* Gen5 trackpad with 0x24 I2C address. */ + { "CYAP0002", 0 }, /* Gen6 trackpad with 0x24 I2C address. */ { } }; MODULE_DEVICE_TABLE(acpi, cyapa_acpi_id); #endif +#ifdef CONFIG_OF +static const struct of_device_id cyapa_of_match[] = { + { .compatible = "cypress,cyapa" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, cyapa_of_match); +#endif + static struct i2c_driver cyapa_driver = { .driver = { .name = "cyapa", - .owner = THIS_MODULE, .pm = &cyapa_pm_ops, .acpi_match_table = ACPI_PTR(cyapa_acpi_id), + .of_match_table = of_match_ptr(cyapa_of_match), }, .probe = cyapa_probe, diff --git a/drivers/input/mouse/cyapa.h b/drivers/input/mouse/cyapa.h index adc9ed5dcb0e..b812bba8cdd7 100644 --- a/drivers/input/mouse/cyapa.h +++ b/drivers/input/mouse/cyapa.h @@ -3,7 +3,7 @@ * * Author: Dudley Du <dudl@cypress.com> * - * Copyright (C) 2014 Cypress Semiconductor, Inc. + * Copyright (C) 2014-2015 Cypress Semiconductor, Inc. * * This file is subject to the terms and conditions of the GNU General Public * License. See the file COPYING in the main directory of this archive for @@ -19,13 +19,14 @@ #define CYAPA_GEN_UNKNOWN 0x00 /* unknown protocol. */ #define CYAPA_GEN3 0x03 /* support MT-protocol B with tracking ID. */ #define CYAPA_GEN5 0x05 /* support TrueTouch GEN5 trackpad device. */ +#define CYAPA_GEN6 0x06 /* support TrueTouch GEN6 trackpad device. */ #define CYAPA_NAME "Cypress APA Trackpad (cyapa)" /* * Macros for SMBus communication */ -#define SMBUS_READ 0x01 +#define SMBUS_READ 0x01 #define SMBUS_WRITE 0x00 #define SMBUS_ENCODE_IDX(cmd, idx) ((cmd) | (((idx) & 0x03) << 1)) #define SMBUS_ENCODE_RW(cmd, rw) ((cmd) | ((rw) & 0x01)) @@ -159,12 +160,89 @@ #define AUTOSUSPEND_DELAY 2000 /* unit : ms */ -#define UNINIT_SLEEP_TIME 0xFFFF -#define UNINIT_PWR_MODE 0xFF - #define BTN_ONLY_MODE_NAME "buttononly" #define OFF_MODE_NAME "off" +/* Common macros for PIP interface. */ +#define PIP_HID_DESCRIPTOR_ADDR 0x0001 +#define PIP_REPORT_DESCRIPTOR_ADDR 0x0002 +#define PIP_INPUT_REPORT_ADDR 0x0003 +#define PIP_OUTPUT_REPORT_ADDR 0x0004 +#define PIP_CMD_DATA_ADDR 0x0006 + +#define PIP_RETRIEVE_DATA_STRUCTURE 0x24 +#define PIP_CMD_CALIBRATE 0x28 +#define PIP_BL_CMD_VERIFY_APP_INTEGRITY 0x31 +#define PIP_BL_CMD_GET_BL_INFO 0x38 +#define PIP_BL_CMD_PROGRAM_VERIFY_ROW 0x39 +#define PIP_BL_CMD_LAUNCH_APP 0x3b +#define PIP_BL_CMD_INITIATE_BL 0x48 +#define PIP_INVALID_CMD 0xff + +#define PIP_HID_DESCRIPTOR_SIZE 32 +#define PIP_HID_APP_REPORT_ID 0xf7 +#define PIP_HID_BL_REPORT_ID 0xff + +#define PIP_BL_CMD_REPORT_ID 0x40 +#define PIP_BL_RESP_REPORT_ID 0x30 +#define PIP_APP_CMD_REPORT_ID 0x2f +#define PIP_APP_RESP_REPORT_ID 0x1f + +#define PIP_READ_SYS_INFO_CMD_LENGTH 7 +#define PIP_BL_READ_APP_INFO_CMD_LENGTH 13 +#define PIP_MIN_BL_CMD_LENGTH 13 +#define PIP_MIN_BL_RESP_LENGTH 11 +#define PIP_MIN_APP_CMD_LENGTH 7 +#define PIP_MIN_APP_RESP_LENGTH 5 +#define PIP_UNSUPPORTED_CMD_RESP_LENGTH 6 +#define PIP_READ_SYS_INFO_RESP_LENGTH 71 +#define PIP_BL_APP_INFO_RESP_LENGTH 30 +#define PIP_BL_GET_INFO_RESP_LENGTH 19 + +#define PIP_BL_PLATFORM_VER_SHIFT 4 +#define PIP_BL_PLATFORM_VER_MASK 0x0f + +#define PIP_PRODUCT_FAMILY_MASK 0xf000 +#define PIP_PRODUCT_FAMILY_TRACKPAD 0x1000 + +#define PIP_DEEP_SLEEP_STATE_ON 0x00 +#define PIP_DEEP_SLEEP_STATE_OFF 0x01 +#define PIP_DEEP_SLEEP_STATE_MASK 0x03 +#define PIP_APP_DEEP_SLEEP_REPORT_ID 0xf0 +#define PIP_DEEP_SLEEP_RESP_LENGTH 5 +#define PIP_DEEP_SLEEP_OPCODE 0x08 +#define PIP_DEEP_SLEEP_OPCODE_MASK 0x0f + +#define PIP_RESP_LENGTH_OFFSET 0 +#define PIP_RESP_LENGTH_SIZE 2 +#define PIP_RESP_REPORT_ID_OFFSET 2 +#define PIP_RESP_RSVD_OFFSET 3 +#define PIP_RESP_RSVD_KEY 0x00 +#define PIP_RESP_BL_SOP_OFFSET 4 +#define PIP_SOP_KEY 0x01 /* Start of Packet */ +#define PIP_EOP_KEY 0x17 /* End of Packet */ +#define PIP_RESP_APP_CMD_OFFSET 4 +#define GET_PIP_CMD_CODE(reg) ((reg) & 0x7f) +#define PIP_RESP_STATUS_OFFSET 5 + +#define VALID_CMD_RESP_HEADER(resp, cmd) \ + (((resp)[PIP_RESP_REPORT_ID_OFFSET] == PIP_APP_RESP_REPORT_ID) && \ + ((resp)[PIP_RESP_RSVD_OFFSET] == PIP_RESP_RSVD_KEY) && \ + (GET_PIP_CMD_CODE((resp)[PIP_RESP_APP_CMD_OFFSET]) == (cmd))) + +#define PIP_CMD_COMPLETE_SUCCESS(resp_data) \ + ((resp_data)[PIP_RESP_STATUS_OFFSET] == 0x00) + +/* Variables to record latest gen5 trackpad power states. */ +#define UNINIT_SLEEP_TIME 0xffff +#define UNINIT_PWR_MODE 0xff +#define PIP_DEV_SET_PWR_STATE(cyapa, s) ((cyapa)->dev_pwr_mode = (s)) +#define PIP_DEV_GET_PWR_STATE(cyapa) ((cyapa)->dev_pwr_mode) +#define PIP_DEV_SET_SLEEP_TIME(cyapa, t) ((cyapa)->dev_sleep_time = (t)) +#define PIP_DEV_GET_SLEEP_TIME(cyapa) ((cyapa)->dev_sleep_time) +#define PIP_DEV_UNINIT_SLEEP_TIME(cyapa) \ + (((cyapa)->dev_sleep_time) == UNINIT_SLEEP_TIME) + /* The touch.id is used as the MT slot id, thus max MT slot is 15 */ #define CYAPA_MAX_MT_SLOTS 15 @@ -195,10 +273,12 @@ struct cyapa_dev_ops { int (*sort_empty_output_data)(struct cyapa *, u8 *, int *, cb_sort); - int (*set_power_mode)(struct cyapa *, u8, u16); + int (*set_power_mode)(struct cyapa *, u8, u16, bool); + + int (*set_proximity)(struct cyapa *, bool); }; -struct cyapa_gen5_cmd_states { +struct cyapa_pip_cmd_states { struct mutex cmd_lock; struct completion cmd_ready; atomic_t cmd_issued; @@ -214,7 +294,7 @@ struct cyapa_gen5_cmd_states { }; union cyapa_cmd_states { - struct cyapa_gen5_cmd_states gen5; + struct cyapa_pip_cmd_states pip; }; enum cyapa_state { @@ -225,6 +305,14 @@ enum cyapa_state { CYAPA_STATE_OP, CYAPA_STATE_GEN5_BL, CYAPA_STATE_GEN5_APP, + CYAPA_STATE_GEN6_BL, + CYAPA_STATE_GEN6_APP, +}; + +struct gen6_interval_setting { + u16 active_interval; + u16 lp1_interval; + u16 lp2_interval; }; /* The main device structure */ @@ -233,6 +321,7 @@ struct cyapa { u8 status[BL_STATUS_SIZE]; bool operational; /* true: ready for data reporting; false: not. */ + struct regulator *vcc; struct i2c_client *client; struct input_dev *input; char phys[32]; /* Device physical location */ @@ -246,9 +335,11 @@ struct cyapa { u16 runtime_suspend_sleep_time; u8 dev_pwr_mode; u16 dev_sleep_time; + struct gen6_interval_setting gen6_interval_setting; /* Read from query data region. */ char product_id[16]; + u8 platform_ver; /* Platform version. */ u8 fw_maj_ver; /* Firmware major version. */ u8 fw_min_ver; /* Firmware minor version. */ u8 btn_capability; @@ -259,7 +350,7 @@ struct cyapa { int physical_size_y; /* Used in ttsp and truetouch based trackpad devices. */ - u8 x_origin; /* X Axis Origin: 0 = left side; 1 = rigth side. */ + u8 x_origin; /* X Axis Origin: 0 = left side; 1 = right side. */ u8 y_origin; /* Y Axis Origin: 0 = top; 1 = bottom. */ int electrodes_x; /* Number of electrodes on the X Axis*/ int electrodes_y; /* Number of electrodes on the Y Axis*/ @@ -282,9 +373,9 @@ struct cyapa { ssize_t cyapa_i2c_reg_read_block(struct cyapa *cyapa, u8 reg, size_t len, - u8 *values); + u8 *values); ssize_t cyapa_smbus_read_block(struct cyapa *cyapa, u8 cmd, size_t len, - u8 *values); + u8 *values); ssize_t cyapa_read_block(struct cyapa *cyapa, u8 cmd_idx, u8 *values); @@ -293,9 +384,51 @@ int cyapa_poll_state(struct cyapa *cyapa, unsigned int timeout); u8 cyapa_sleep_time_to_pwr_cmd(u16 sleep_time); u16 cyapa_pwr_cmd_to_sleep_time(u8 pwr_mode); - +ssize_t cyapa_i2c_pip_read(struct cyapa *cyapa, u8 *buf, size_t size); +ssize_t cyapa_i2c_pip_write(struct cyapa *cyapa, u8 *buf, size_t size); +int cyapa_empty_pip_output_data(struct cyapa *cyapa, + u8 *buf, int *len, cb_sort func); +int cyapa_i2c_pip_cmd_irq_sync(struct cyapa *cyapa, + u8 *cmd, int cmd_len, + u8 *resp_data, int *resp_len, + unsigned long timeout, + cb_sort func, + bool irq_mode); +int cyapa_pip_state_parse(struct cyapa *cyapa, u8 *reg_data, int len); +bool cyapa_pip_sort_system_info_data(struct cyapa *cyapa, u8 *buf, int len); +bool cyapa_sort_tsg_pip_bl_resp_data(struct cyapa *cyapa, u8 *data, int len); +int cyapa_pip_deep_sleep(struct cyapa *cyapa, u8 state); +bool cyapa_sort_tsg_pip_app_resp_data(struct cyapa *cyapa, u8 *data, int len); +int cyapa_pip_bl_exit(struct cyapa *cyapa); +int cyapa_pip_bl_enter(struct cyapa *cyapa); + + +bool cyapa_is_pip_bl_mode(struct cyapa *cyapa); +bool cyapa_is_pip_app_mode(struct cyapa *cyapa); +int cyapa_pip_cmd_state_initialize(struct cyapa *cyapa); + +int cyapa_pip_resume_scanning(struct cyapa *cyapa); +int cyapa_pip_suspend_scanning(struct cyapa *cyapa); + +int cyapa_pip_check_fw(struct cyapa *cyapa, const struct firmware *fw); +int cyapa_pip_bl_initiate(struct cyapa *cyapa, const struct firmware *fw); +int cyapa_pip_do_fw_update(struct cyapa *cyapa, const struct firmware *fw); +int cyapa_pip_bl_activate(struct cyapa *cyapa); +int cyapa_pip_bl_deactivate(struct cyapa *cyapa); +ssize_t cyapa_pip_do_calibrate(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); +int cyapa_pip_set_proximity(struct cyapa *cyapa, bool enable); + +bool cyapa_pip_irq_cmd_handler(struct cyapa *cyapa); +int cyapa_pip_irq_handler(struct cyapa *cyapa); + + +extern u8 pip_read_sys_info[]; +extern u8 pip_bl_read_app_info[]; extern const char product_id[]; extern const struct cyapa_dev_ops cyapa_gen3_ops; extern const struct cyapa_dev_ops cyapa_gen5_ops; +extern const struct cyapa_dev_ops cyapa_gen6_ops; #endif diff --git a/drivers/input/mouse/cyapa_gen3.c b/drivers/input/mouse/cyapa_gen3.c index 3faf01c1b191..1a9d12ae7538 100644 --- a/drivers/input/mouse/cyapa_gen3.c +++ b/drivers/input/mouse/cyapa_gen3.c @@ -6,7 +6,7 @@ * Daniel Kurtz <djkurtz@chromium.org> * Benson Leung <bleung@chromium.org> * - * Copyright (C) 2011-2014 Cypress Semiconductor, Inc. + * Copyright (C) 2011-2015 Cypress Semiconductor, Inc. * Copyright (C) 2011-2012 Google, Inc. * * This file is subject to the terms and conditions of the GNU General Public @@ -950,7 +950,7 @@ static u16 cyapa_get_wait_time_for_pwr_cmd(u8 pwr_mode) * Device power mode can only be set when device is in operational mode. */ static int cyapa_gen3_set_power_mode(struct cyapa *cyapa, u8 power_mode, - u16 always_unused) + u16 always_unused, bool is_suspend_unused) { int ret; u8 power; @@ -999,6 +999,11 @@ static int cyapa_gen3_set_power_mode(struct cyapa *cyapa, u8 power_mode, return ret; } +static int cyapa_gen3_set_proximity(struct cyapa *cyapa, bool enable) +{ + return -EOPNOTSUPP; +} + static int cyapa_gen3_get_query_data(struct cyapa *cyapa) { u8 query_data[QUERY_DATA_SIZE]; @@ -1107,7 +1112,7 @@ static int cyapa_gen3_do_operational_check(struct cyapa *cyapa) * may cause problems, so we set the power mode first here. */ error = cyapa_gen3_set_power_mode(cyapa, - PWR_MODE_FULL_ACTIVE, 0); + PWR_MODE_FULL_ACTIVE, 0, false); if (error) dev_err(dev, "%s: set full power mode failed: %d\n", __func__, error); @@ -1156,7 +1161,7 @@ static bool cyapa_gen3_irq_cmd_handler(struct cyapa *cyapa) * so, stop cyapa_gen3_irq_handler to continue process to * avoid unwanted to error detecting and processing. * - * And also, avoid the periodicly accerted interrupts to be processed + * And also, avoid the periodically asserted interrupts to be processed * as touch inputs when gen3 failed to launch into application mode, * which will cause gen3 stays in bootloader mode. */ @@ -1243,4 +1248,6 @@ const struct cyapa_dev_ops cyapa_gen3_ops = { .irq_cmd_handler = cyapa_gen3_irq_cmd_handler, .sort_empty_output_data = cyapa_gen3_empty_output_data, .set_power_mode = cyapa_gen3_set_power_mode, + + .set_proximity = cyapa_gen3_set_proximity, }; diff --git a/drivers/input/mouse/cyapa_gen5.c b/drivers/input/mouse/cyapa_gen5.c index afc39e799da2..118ba977181e 100644 --- a/drivers/input/mouse/cyapa_gen5.c +++ b/drivers/input/mouse/cyapa_gen5.c @@ -3,7 +3,7 @@ * * Author: Dudley Du <dudl@cypress.com> * - * Copyright (C) 2014 Cypress Semiconductor, Inc. + * Copyright (C) 2014-2015 Cypress Semiconductor, Inc. * * This file is subject to the terms and conditions of the GNU General Public * License. See the file COPYING in the main directory of this archive for @@ -19,15 +19,11 @@ #include <linux/slab.h> #include <asm/unaligned.h> #include <linux/crc-itu-t.h> +#include <linux/pm_runtime.h> #include "cyapa.h" -/* Macro of Gen5 */ -#define RECORD_EVENT_NONE 0 -#define RECORD_EVENT_TOUCHDOWN 1 -#define RECORD_EVENT_DISPLACE 2 -#define RECORD_EVENT_LIFTOFF 3 - +/* Macro of TSG firmware image */ #define CYAPA_TSG_FLASH_MAP_BLOCK_SIZE 0x80 #define CYAPA_TSG_IMG_FW_HDR_SIZE 13 #define CYAPA_TSG_FW_ROW_SIZE (CYAPA_TSG_FLASH_MAP_BLOCK_SIZE) @@ -44,43 +40,55 @@ #define CYAPA_TSG_MAX_CMD_SIZE 256 -#define GEN5_BL_CMD_VERIFY_APP_INTEGRITY 0x31 -#define GEN5_BL_CMD_GET_BL_INFO 0x38 -#define GEN5_BL_CMD_PROGRAM_VERIFY_ROW 0x39 -#define GEN5_BL_CMD_LAUNCH_APP 0x3b -#define GEN5_BL_CMD_INITIATE_BL 0x48 - -#define GEN5_HID_DESCRIPTOR_ADDR 0x0001 -#define GEN5_REPORT_DESCRIPTOR_ADDR 0x0002 -#define GEN5_INPUT_REPORT_ADDR 0x0003 -#define GEN5_OUTPUT_REPORT_ADDR 0x0004 -#define GEN5_CMD_DATA_ADDR 0x0006 - -#define GEN5_TOUCH_REPORT_HEAD_SIZE 7 -#define GEN5_TOUCH_REPORT_MAX_SIZE 127 -#define GEN5_BTN_REPORT_HEAD_SIZE 6 -#define GEN5_BTN_REPORT_MAX_SIZE 14 -#define GEN5_WAKEUP_EVENT_SIZE 4 -#define GEN5_RAW_DATA_HEAD_SIZE 24 - -#define GEN5_BL_CMD_REPORT_ID 0x40 -#define GEN5_BL_RESP_REPORT_ID 0x30 -#define GEN5_APP_CMD_REPORT_ID 0x2f -#define GEN5_APP_RESP_REPORT_ID 0x1f - -#define GEN5_APP_DEEP_SLEEP_REPORT_ID 0xf0 -#define GEN5_DEEP_SLEEP_RESP_LENGTH 5 +/* Macro of PIP interface */ +#define PIP_BL_INITIATE_RESP_LEN 11 +#define PIP_BL_FAIL_EXIT_RESP_LEN 11 +#define PIP_BL_FAIL_EXIT_STATUS_CODE 0x0c +#define PIP_BL_VERIFY_INTEGRITY_RESP_LEN 12 +#define PIP_BL_INTEGRITY_CHEKC_PASS 0x00 +#define PIP_BL_BLOCK_WRITE_RESP_LEN 11 + +#define PIP_TOUCH_REPORT_ID 0x01 +#define PIP_BTN_REPORT_ID 0x03 +#define PIP_WAKEUP_EVENT_REPORT_ID 0x04 +#define PIP_PUSH_BTN_REPORT_ID 0x06 +#define GEN5_OLD_PUSH_BTN_REPORT_ID 0x05 /* Special for old Gen5 TP. */ +#define PIP_PROXIMITY_REPORT_ID 0x07 + +#define PIP_PROXIMITY_REPORT_SIZE 6 +#define PIP_PROXIMITY_DISTANCE_OFFSET 0x05 +#define PIP_PROXIMITY_DISTANCE_MASK 0x01 + +#define PIP_TOUCH_REPORT_HEAD_SIZE 7 +#define PIP_TOUCH_REPORT_MAX_SIZE 127 +#define PIP_BTN_REPORT_HEAD_SIZE 6 +#define PIP_BTN_REPORT_MAX_SIZE 14 +#define PIP_WAKEUP_EVENT_SIZE 4 + +#define PIP_NUMBER_OF_TOUCH_OFFSET 5 +#define PIP_NUMBER_OF_TOUCH_MASK 0x1f +#define PIP_BUTTONS_OFFSET 5 +#define PIP_BUTTONS_MASK 0x0f +#define PIP_GET_EVENT_ID(reg) (((reg) >> 5) & 0x03) +#define PIP_GET_TOUCH_ID(reg) ((reg) & 0x1f) +#define PIP_TOUCH_TYPE_FINGER 0x00 +#define PIP_TOUCH_TYPE_PROXIMITY 0x01 +#define PIP_TOUCH_TYPE_HOVER 0x02 +#define PIP_GET_TOUCH_TYPE(reg) ((reg) & 0x07) -#define GEN5_CMD_GET_PARAMETER 0x05 -#define GEN5_CMD_SET_PARAMETER 0x06 -#define GEN5_PARAMETER_ACT_INTERVL_ID 0x4d -#define GEN5_PARAMETER_ACT_INTERVL_SIZE 1 -#define GEN5_PARAMETER_ACT_LFT_INTERVL_ID 0x4f -#define GEN5_PARAMETER_ACT_LFT_INTERVL_SIZE 2 -#define GEN5_PARAMETER_LP_INTRVL_ID 0x4c -#define GEN5_PARAMETER_LP_INTRVL_SIZE 2 +#define RECORD_EVENT_NONE 0 +#define RECORD_EVENT_TOUCHDOWN 1 +#define RECORD_EVENT_DISPLACE 2 +#define RECORD_EVENT_LIFTOFF 3 -#define GEN5_PARAMETER_DISABLE_PIP_REPORT 0x08 +#define PIP_SENSING_MODE_MUTUAL_CAP_FINE 0x00 +#define PIP_SENSING_MODE_SELF_CAP 0x02 + +#define PIP_SET_PROXIMITY 0x49 + +/* Macro of Gen5 */ +#define GEN5_BL_MAX_OUTPUT_LENGTH 0x0100 +#define GEN5_APP_MAX_OUTPUT_LENGTH 0x00fe #define GEN5_POWER_STATE_ACTIVE 0x01 #define GEN5_POWER_STATE_LOOK_FOR_TOUCH 0x02 @@ -89,46 +97,19 @@ #define GEN5_POWER_STATE_BTN_ONLY 0x05 #define GEN5_POWER_STATE_OFF 0x06 -#define GEN5_DEEP_SLEEP_STATE_MASK 0x03 -#define GEN5_DEEP_SLEEP_STATE_ON 0x00 -#define GEN5_DEEP_SLEEP_STATE_OFF 0x01 - -#define GEN5_DEEP_SLEEP_OPCODE 0x08 -#define GEN5_DEEP_SLEEP_OPCODE_MASK 0x0f - #define GEN5_POWER_READY_MAX_INTRVL_TIME 50 /* Unit: ms */ #define GEN5_POWER_IDLE_MAX_INTRVL_TIME 250 /* Unit: ms */ -#define GEN5_CMD_REPORT_ID_OFFSET 4 - -#define GEN5_RESP_REPORT_ID_OFFSET 2 -#define GEN5_RESP_RSVD_OFFSET 3 -#define GEN5_RESP_RSVD_KEY 0x00 -#define GEN5_RESP_BL_SOP_OFFSET 4 -#define GEN5_SOP_KEY 0x01 /* Start of Packet */ -#define GEN5_EOP_KEY 0x17 /* End of Packet */ -#define GEN5_RESP_APP_CMD_OFFSET 4 -#define GET_GEN5_CMD_CODE(reg) ((reg) & 0x7f) - -#define VALID_CMD_RESP_HEADER(resp, cmd) \ - (((resp)[GEN5_RESP_REPORT_ID_OFFSET] == GEN5_APP_RESP_REPORT_ID) && \ - ((resp)[GEN5_RESP_RSVD_OFFSET] == GEN5_RESP_RSVD_KEY) && \ - (GET_GEN5_CMD_CODE((resp)[GEN5_RESP_APP_CMD_OFFSET]) == (cmd))) - -#define GEN5_MIN_BL_CMD_LENGTH 13 -#define GEN5_MIN_BL_RESP_LENGTH 11 -#define GEN5_MIN_APP_CMD_LENGTH 7 -#define GEN5_MIN_APP_RESP_LENGTH 5 -#define GEN5_UNSUPPORTED_CMD_RESP_LENGTH 6 - -#define GEN5_RESP_LENGTH_OFFSET 0x00 -#define GEN5_RESP_LENGTH_SIZE 2 - -#define GEN5_HID_DESCRIPTOR_SIZE 32 -#define GEN5_BL_HID_REPORT_ID 0xff -#define GEN5_APP_HID_REPORT_ID 0xf7 -#define GEN5_BL_MAX_OUTPUT_LENGTH 0x0100 -#define GEN5_APP_MAX_OUTPUT_LENGTH 0x00fe +#define GEN5_CMD_GET_PARAMETER 0x05 +#define GEN5_CMD_SET_PARAMETER 0x06 +#define GEN5_PARAMETER_ACT_INTERVL_ID 0x4d +#define GEN5_PARAMETER_ACT_INTERVL_SIZE 1 +#define GEN5_PARAMETER_ACT_LFT_INTERVL_ID 0x4f +#define GEN5_PARAMETER_ACT_LFT_INTERVL_SIZE 2 +#define GEN5_PARAMETER_LP_INTRVL_ID 0x4c +#define GEN5_PARAMETER_LP_INTRVL_SIZE 2 + +#define GEN5_PARAMETER_DISABLE_PIP_REPORT 0x08 #define GEN5_BL_REPORT_DESCRIPTOR_SIZE 0x1d #define GEN5_BL_REPORT_DESCRIPTOR_ID 0xfe @@ -136,26 +117,6 @@ #define GEN5_APP_CONTRACT_REPORT_DESCRIPTOR_SIZE 0xfa #define GEN5_APP_REPORT_DESCRIPTOR_ID 0xf6 -#define GEN5_TOUCH_REPORT_ID 0x01 -#define GEN5_BTN_REPORT_ID 0x03 -#define GEN5_WAKEUP_EVENT_REPORT_ID 0x04 -#define GEN5_OLD_PUSH_BTN_REPORT_ID 0x05 -#define GEN5_PUSH_BTN_REPORT_ID 0x06 - -#define GEN5_CMD_COMPLETE_SUCCESS(status) ((status) == 0x00) - -#define GEN5_BL_INITIATE_RESP_LEN 11 -#define GEN5_BL_FAIL_EXIT_RESP_LEN 11 -#define GEN5_BL_FAIL_EXIT_STATUS_CODE 0x0c -#define GEN5_BL_VERIFY_INTEGRITY_RESP_LEN 12 -#define GEN5_BL_INTEGRITY_CHEKC_PASS 0x00 -#define GEN5_BL_BLOCK_WRITE_RESP_LEN 11 -#define GEN5_BL_READ_APP_INFO_RESP_LEN 31 -#define GEN5_CMD_CALIBRATE 0x28 -#define CYAPA_SENSING_MODE_MUTUAL_CAP_FINE 0x00 -#define CYAPA_SENSING_MODE_SELF_CAP 0x02 - -#define GEN5_CMD_RETRIEVE_DATA_STRUCTURE 0x24 #define GEN5_RETRIEVE_MUTUAL_PWC_DATA 0x00 #define GEN5_RETRIEVE_SELF_CAP_PWC_DATA 0x01 @@ -170,28 +131,19 @@ #define GEN5_PANEL_SCAN_SELF_BASELINE 0x04 #define GEN5_PANEL_SCAN_SELF_DIFFCOUNT 0x05 -/* The offset only valid for reterive PWC and panel scan commands */ +/* The offset only valid for retrieve PWC and panel scan commands */ #define GEN5_RESP_DATA_STRUCTURE_OFFSET 10 #define GEN5_PWC_DATA_ELEMENT_SIZE_MASK 0x07 -#define GEN5_NUMBER_OF_TOUCH_OFFSET 5 -#define GEN5_NUMBER_OF_TOUCH_MASK 0x1f -#define GEN5_BUTTONS_OFFSET 5 -#define GEN5_BUTTONS_MASK 0x0f -#define GEN5_GET_EVENT_ID(reg) (((reg) >> 5) & 0x03) -#define GEN5_GET_TOUCH_ID(reg) ((reg) & 0x1f) - -#define GEN5_PRODUCT_FAMILY_MASK 0xf000 -#define GEN5_PRODUCT_FAMILY_TRACKPAD 0x1000 -#define TSG_INVALID_CMD 0xff - -struct cyapa_gen5_touch_record { +struct cyapa_pip_touch_record { /* * Bit 7 - 3: reserved * Bit 2 - 0: touch type; * 0 : standard finger; - * 1 - 15 : reserved. + * 1 : proximity (Start supported in Gen5 TP). + * 2 : finger hover (defined, but not used yet.) + * 3 - 15 : reserved. */ u8 touch_type; @@ -221,7 +173,14 @@ struct cyapa_gen5_touch_record { /* Bit 15 - 8 of Y-axis coordinate of the touch in pixel. */ u8 y_hi; - /* Touch intensity in counts, pressure value. */ + /* + * The meaning of this value is different when touch_type is different. + * For standard finger type: + * Touch intensity in counts, pressure value. + * For proximity type (Start supported in Gen5 TP): + * The distance, in surface units, between the contact and + * the surface. + **/ u8 z; /* @@ -260,9 +219,9 @@ struct cyapa_gen5_touch_record { u8 orientation; } __packed; -struct cyapa_gen5_report_data { - u8 report_head[GEN5_TOUCH_REPORT_HEAD_SIZE]; - struct cyapa_gen5_touch_record touch_records[10]; +struct cyapa_pip_report_data { + u8 report_head[PIP_TOUCH_REPORT_HEAD_SIZE]; + struct cyapa_pip_touch_record touch_records[10]; } __packed; struct cyapa_tsg_bin_image_head { @@ -272,6 +231,12 @@ struct cyapa_tsg_bin_image_head { u8 fw_major_version; u8 fw_minor_version; u8 fw_revision_control_number[8]; + u8 silicon_id_hi; + u8 silicon_id_lo; + u8 chip_revision; + u8 family_id; + u8 bl_ver_maj; + u8 bl_ver_min; } __packed; struct cyapa_tsg_bin_image_data_record { @@ -288,36 +253,36 @@ struct cyapa_tsg_bin_image { struct cyapa_tsg_bin_image_data_record records[0]; } __packed; -struct gen5_bl_packet_start { +struct pip_bl_packet_start { u8 sop; /* Start of packet, must be 01h */ u8 cmd_code; __le16 data_length; /* Size of data parameter start from data[0] */ } __packed; -struct gen5_bl_packet_end { +struct pip_bl_packet_end { __le16 crc; u8 eop; /* End of packet, must be 17h */ } __packed; -struct gen5_bl_cmd_head { +struct pip_bl_cmd_head { __le16 addr; /* Output report register address, must be 0004h */ /* Size of packet not including output report register address */ __le16 length; u8 report_id; /* Bootloader output report id, must be 40h */ u8 rsvd; /* Reserved, must be 0 */ - struct gen5_bl_packet_start packet_start; + struct pip_bl_packet_start packet_start; u8 data[0]; /* Command data variable based on commands */ } __packed; /* Initiate bootload command data structure. */ -struct gen5_bl_initiate_cmd_data { +struct pip_bl_initiate_cmd_data { /* Key must be "A5h 01h 02h 03h FFh FEh FDh 5Ah" */ u8 key[CYAPA_TSG_BL_KEY_SIZE]; u8 metadata_raw_parameter[CYAPA_TSG_FLASH_MAP_METADATA_SIZE]; __le16 metadata_crc; } __packed; -struct gen5_bl_metadata_row_params { +struct tsg_bl_metadata_row_params { __le16 size; __le16 maximum_size; __le32 app_start; @@ -332,13 +297,13 @@ struct gen5_bl_metadata_row_params { } __packed; /* Bootload program and verify row command data structure */ -struct gen5_bl_flash_row_head { +struct tsg_bl_flash_row_head { u8 flash_array_id; __le16 flash_row_id; u8 flash_data[0]; } __packed; -struct gen5_app_cmd_head { +struct pip_app_cmd_head { __le16 addr; /* Output report register address, must be 0004h */ /* Size of packet not including output report register address */ __le16 length; @@ -369,30 +334,26 @@ struct gen5_retrieve_panel_scan_data { u8 data_id; } __packed; -/* Variables to record latest gen5 trackpad power states. */ -#define GEN5_DEV_SET_PWR_STATE(cyapa, s) ((cyapa)->dev_pwr_mode = (s)) -#define GEN5_DEV_GET_PWR_STATE(cyapa) ((cyapa)->dev_pwr_mode) -#define GEN5_DEV_SET_SLEEP_TIME(cyapa, t) ((cyapa)->dev_sleep_time = (t)) -#define GEN5_DEV_GET_SLEEP_TIME(cyapa) ((cyapa)->dev_sleep_time) -#define GEN5_DEV_UNINIT_SLEEP_TIME(cyapa) \ - (((cyapa)->dev_sleep_time) == UNINIT_SLEEP_TIME) - +u8 pip_read_sys_info[] = { 0x04, 0x00, 0x05, 0x00, 0x2f, 0x00, 0x02 }; +u8 pip_bl_read_app_info[] = { 0x04, 0x00, 0x0b, 0x00, 0x40, 0x00, + 0x01, 0x3c, 0x00, 0x00, 0xb0, 0x42, 0x17 + }; -static u8 cyapa_gen5_bl_cmd_key[] = { 0xa5, 0x01, 0x02, 0x03, +static u8 cyapa_pip_bl_cmd_key[] = { 0xa5, 0x01, 0x02, 0x03, 0xff, 0xfe, 0xfd, 0x5a }; -static int cyapa_gen5_initialize(struct cyapa *cyapa) +int cyapa_pip_cmd_state_initialize(struct cyapa *cyapa) { - struct cyapa_gen5_cmd_states *gen5_pip = &cyapa->cmd_states.gen5; + struct cyapa_pip_cmd_states *pip = &cyapa->cmd_states.pip; - init_completion(&gen5_pip->cmd_ready); - atomic_set(&gen5_pip->cmd_issued, 0); - mutex_init(&gen5_pip->cmd_lock); + init_completion(&pip->cmd_ready); + atomic_set(&pip->cmd_issued, 0); + mutex_init(&pip->cmd_lock); - gen5_pip->resp_sort_func = NULL; - gen5_pip->in_progress_cmd = TSG_INVALID_CMD; - gen5_pip->resp_data = NULL; - gen5_pip->resp_len = NULL; + pip->resp_sort_func = NULL; + pip->in_progress_cmd = PIP_INVALID_CMD; + pip->resp_data = NULL; + pip->resp_len = NULL; cyapa->dev_pwr_mode = UNINIT_PWR_MODE; cyapa->dev_sleep_time = UNINIT_SLEEP_TIME; @@ -401,7 +362,7 @@ static int cyapa_gen5_initialize(struct cyapa *cyapa) } /* Return negative errno, or else the number of bytes read. */ -static ssize_t cyapa_i2c_pip_read(struct cyapa *cyapa, u8 *buf, size_t size) +ssize_t cyapa_i2c_pip_read(struct cyapa *cyapa, u8 *buf, size_t size) { int ret; @@ -415,14 +376,13 @@ static ssize_t cyapa_i2c_pip_read(struct cyapa *cyapa, u8 *buf, size_t size) if (ret != size) return (ret < 0) ? ret : -EIO; - return size; } /** * Return a negative errno code else zero on success. */ -static ssize_t cyapa_i2c_pip_write(struct cyapa *cyapa, u8 *buf, size_t size) +ssize_t cyapa_i2c_pip_write(struct cyapa *cyapa, u8 *buf, size_t size) { int ret; @@ -441,10 +401,10 @@ static ssize_t cyapa_i2c_pip_write(struct cyapa *cyapa, u8 *buf, size_t size) * This function is aimed to dump all not read data in Gen5 trackpad * before send any command, otherwise, the interrupt line will be blocked. */ -static int cyapa_empty_pip_output_data(struct cyapa *cyapa, +int cyapa_empty_pip_output_data(struct cyapa *cyapa, u8 *buf, int *len, cb_sort func) { - struct cyapa_gen5_cmd_states *gen5_pip = &cyapa->cmd_states.gen5; + struct cyapa_pip_cmd_states *pip = &cyapa->cmd_states.pip; int length; int report_count; int empty_count; @@ -476,13 +436,13 @@ static int cyapa_empty_pip_output_data(struct cyapa *cyapa, if (empty_count > 5) return 0; - error = cyapa_i2c_pip_read(cyapa, gen5_pip->empty_buf, - GEN5_RESP_LENGTH_SIZE); + error = cyapa_i2c_pip_read(cyapa, pip->empty_buf, + PIP_RESP_LENGTH_SIZE); if (error < 0) return error; - length = get_unaligned_le16(gen5_pip->empty_buf); - if (length == GEN5_RESP_LENGTH_SIZE) { + length = get_unaligned_le16(pip->empty_buf); + if (length == PIP_RESP_LENGTH_SIZE) { empty_count++; continue; } else if (length > CYAPA_REG_MAP_SIZE) { @@ -490,11 +450,11 @@ static int cyapa_empty_pip_output_data(struct cyapa *cyapa, return -EINVAL; } else if (length == 0) { /* Application or bootloader launch data polled out. */ - length = GEN5_RESP_LENGTH_SIZE; + length = PIP_RESP_LENGTH_SIZE; if (buf && buf_len && func && - func(cyapa, gen5_pip->empty_buf, length)) { + func(cyapa, pip->empty_buf, length)) { length = min(buf_len, length); - memcpy(buf, gen5_pip->empty_buf, length); + memcpy(buf, pip->empty_buf, length); *len = length; /* Response found, success. */ return 0; @@ -502,19 +462,19 @@ static int cyapa_empty_pip_output_data(struct cyapa *cyapa, continue; } - error = cyapa_i2c_pip_read(cyapa, gen5_pip->empty_buf, length); + error = cyapa_i2c_pip_read(cyapa, pip->empty_buf, length); if (error < 0) return error; report_count--; empty_count = 0; - length = get_unaligned_le16(gen5_pip->empty_buf); - if (length <= GEN5_RESP_LENGTH_SIZE) { + length = get_unaligned_le16(pip->empty_buf); + if (length <= PIP_RESP_LENGTH_SIZE) { empty_count++; } else if (buf && buf_len && func && - func(cyapa, gen5_pip->empty_buf, length)) { + func(cyapa, pip->empty_buf, length)) { length = min(buf_len, length); - memcpy(buf, gen5_pip->empty_buf, length); + memcpy(buf, pip->empty_buf, length); *len = length; /* Response found, success. */ return 0; @@ -531,24 +491,24 @@ static int cyapa_do_i2c_pip_cmd_irq_sync( u8 *cmd, size_t cmd_len, unsigned long timeout) { - struct cyapa_gen5_cmd_states *gen5_pip = &cyapa->cmd_states.gen5; + struct cyapa_pip_cmd_states *pip = &cyapa->cmd_states.pip; int error; /* Wait for interrupt to set ready completion */ - init_completion(&gen5_pip->cmd_ready); + init_completion(&pip->cmd_ready); - atomic_inc(&gen5_pip->cmd_issued); + atomic_inc(&pip->cmd_issued); error = cyapa_i2c_pip_write(cyapa, cmd, cmd_len); if (error) { - atomic_dec(&gen5_pip->cmd_issued); + atomic_dec(&pip->cmd_issued); return (error < 0) ? error : -EIO; } /* Wait for interrupt to indicate command is completed. */ - timeout = wait_for_completion_timeout(&gen5_pip->cmd_ready, + timeout = wait_for_completion_timeout(&pip->cmd_ready, msecs_to_jiffies(timeout)); if (timeout == 0) { - atomic_dec(&gen5_pip->cmd_issued); + atomic_dec(&pip->cmd_issued); return -ETIMEDOUT; } @@ -562,15 +522,15 @@ static int cyapa_do_i2c_pip_cmd_polling( unsigned long timeout, cb_sort func) { - struct cyapa_gen5_cmd_states *gen5_pip = &cyapa->cmd_states.gen5; + struct cyapa_pip_cmd_states *pip = &cyapa->cmd_states.pip; int tries; int length; int error; - atomic_inc(&gen5_pip->cmd_issued); + atomic_inc(&pip->cmd_issued); error = cyapa_i2c_pip_write(cyapa, cmd, cmd_len); if (error) { - atomic_dec(&gen5_pip->cmd_issued); + atomic_dec(&pip->cmd_issued); return error < 0 ? error : -EIO; } @@ -591,11 +551,11 @@ static int cyapa_do_i2c_pip_cmd_polling( error = error ? error : -ETIMEDOUT; } - atomic_dec(&gen5_pip->cmd_issued); + atomic_dec(&pip->cmd_issued); return error; } -static int cyapa_i2c_pip_cmd_irq_sync( +int cyapa_i2c_pip_cmd_irq_sync( struct cyapa *cyapa, u8 *cmd, int cmd_len, u8 *resp_data, int *resp_len, @@ -603,34 +563,34 @@ static int cyapa_i2c_pip_cmd_irq_sync( cb_sort func, bool irq_mode) { - struct cyapa_gen5_cmd_states *gen5_pip = &cyapa->cmd_states.gen5; + struct cyapa_pip_cmd_states *pip = &cyapa->cmd_states.pip; int error; if (!cmd || !cmd_len) return -EINVAL; /* Commands must be serialized. */ - error = mutex_lock_interruptible(&gen5_pip->cmd_lock); + error = mutex_lock_interruptible(&pip->cmd_lock); if (error) return error; - gen5_pip->resp_sort_func = func; - gen5_pip->resp_data = resp_data; - gen5_pip->resp_len = resp_len; + pip->resp_sort_func = func; + pip->resp_data = resp_data; + pip->resp_len = resp_len; - if (cmd_len >= GEN5_MIN_APP_CMD_LENGTH && - cmd[4] == GEN5_APP_CMD_REPORT_ID) { + if (cmd_len >= PIP_MIN_APP_CMD_LENGTH && + cmd[4] == PIP_APP_CMD_REPORT_ID) { /* Application command */ - gen5_pip->in_progress_cmd = cmd[6] & 0x7f; - } else if (cmd_len >= GEN5_MIN_BL_CMD_LENGTH && - cmd[4] == GEN5_BL_CMD_REPORT_ID) { + pip->in_progress_cmd = cmd[6] & 0x7f; + } else if (cmd_len >= PIP_MIN_BL_CMD_LENGTH && + cmd[4] == PIP_BL_CMD_REPORT_ID) { /* Bootloader command */ - gen5_pip->in_progress_cmd = cmd[7]; + pip->in_progress_cmd = cmd[7]; } /* Send command data, wait and read output response data's length. */ if (irq_mode) { - gen5_pip->is_irq_mode = true; + pip->is_irq_mode = true; error = cyapa_do_i2c_pip_cmd_irq_sync(cyapa, cmd, cmd_len, timeout); if (error == -ETIMEDOUT && resp_data && @@ -646,54 +606,54 @@ static int cyapa_i2c_pip_cmd_irq_sync( error = error ? error : -ETIMEDOUT; } } else { - gen5_pip->is_irq_mode = false; + pip->is_irq_mode = false; error = cyapa_do_i2c_pip_cmd_polling(cyapa, cmd, cmd_len, resp_data, resp_len, timeout, func); } - gen5_pip->resp_sort_func = NULL; - gen5_pip->resp_data = NULL; - gen5_pip->resp_len = NULL; - gen5_pip->in_progress_cmd = TSG_INVALID_CMD; + pip->resp_sort_func = NULL; + pip->resp_data = NULL; + pip->resp_len = NULL; + pip->in_progress_cmd = PIP_INVALID_CMD; - mutex_unlock(&gen5_pip->cmd_lock); + mutex_unlock(&pip->cmd_lock); return error; } -static bool cyapa_gen5_sort_tsg_pip_bl_resp_data(struct cyapa *cyapa, +bool cyapa_sort_tsg_pip_bl_resp_data(struct cyapa *cyapa, u8 *data, int len) { - if (!data || len < GEN5_MIN_BL_RESP_LENGTH) + if (!data || len < PIP_MIN_BL_RESP_LENGTH) return false; /* Bootloader input report id 30h */ - if (data[GEN5_RESP_REPORT_ID_OFFSET] == GEN5_BL_RESP_REPORT_ID && - data[GEN5_RESP_RSVD_OFFSET] == GEN5_RESP_RSVD_KEY && - data[GEN5_RESP_BL_SOP_OFFSET] == GEN5_SOP_KEY) + if (data[PIP_RESP_REPORT_ID_OFFSET] == PIP_BL_RESP_REPORT_ID && + data[PIP_RESP_RSVD_OFFSET] == PIP_RESP_RSVD_KEY && + data[PIP_RESP_BL_SOP_OFFSET] == PIP_SOP_KEY) return true; return false; } -static bool cyapa_gen5_sort_tsg_pip_app_resp_data(struct cyapa *cyapa, +bool cyapa_sort_tsg_pip_app_resp_data(struct cyapa *cyapa, u8 *data, int len) { - struct cyapa_gen5_cmd_states *gen5_pip = &cyapa->cmd_states.gen5; + struct cyapa_pip_cmd_states *pip = &cyapa->cmd_states.pip; int resp_len; - if (!data || len < GEN5_MIN_APP_RESP_LENGTH) + if (!data || len < PIP_MIN_APP_RESP_LENGTH) return false; - if (data[GEN5_RESP_REPORT_ID_OFFSET] == GEN5_APP_RESP_REPORT_ID && - data[GEN5_RESP_RSVD_OFFSET] == GEN5_RESP_RSVD_KEY) { - resp_len = get_unaligned_le16(&data[GEN5_RESP_LENGTH_OFFSET]); - if (GET_GEN5_CMD_CODE(data[GEN5_RESP_APP_CMD_OFFSET]) == 0x00 && - resp_len == GEN5_UNSUPPORTED_CMD_RESP_LENGTH && - data[5] == gen5_pip->in_progress_cmd) { + if (data[PIP_RESP_REPORT_ID_OFFSET] == PIP_APP_RESP_REPORT_ID && + data[PIP_RESP_RSVD_OFFSET] == PIP_RESP_RSVD_KEY) { + resp_len = get_unaligned_le16(&data[PIP_RESP_LENGTH_OFFSET]); + if (GET_PIP_CMD_CODE(data[PIP_RESP_APP_CMD_OFFSET]) == 0x00 && + resp_len == PIP_UNSUPPORTED_CMD_RESP_LENGTH && + data[5] == pip->in_progress_cmd) { /* Unsupported command code */ return false; - } else if (GET_GEN5_CMD_CODE(data[GEN5_RESP_APP_CMD_OFFSET]) == - gen5_pip->in_progress_cmd) { + } else if (GET_PIP_CMD_CODE(data[PIP_RESP_APP_CMD_OFFSET]) == + pip->in_progress_cmd) { /* Correct command response received */ return true; } @@ -702,10 +662,10 @@ static bool cyapa_gen5_sort_tsg_pip_app_resp_data(struct cyapa *cyapa, return false; } -static bool cyapa_gen5_sort_application_launch_data(struct cyapa *cyapa, +static bool cyapa_sort_pip_application_launch_data(struct cyapa *cyapa, u8 *buf, int len) { - if (buf == NULL || len < GEN5_RESP_LENGTH_SIZE) + if (buf == NULL || len < PIP_RESP_LENGTH_SIZE) return false; /* @@ -718,25 +678,25 @@ static bool cyapa_gen5_sort_application_launch_data(struct cyapa *cyapa, return false; } -static bool cyapa_gen5_sort_hid_descriptor_data(struct cyapa *cyapa, +static bool cyapa_sort_gen5_hid_descriptor_data(struct cyapa *cyapa, u8 *buf, int len) { int resp_len; int max_output_len; /* Check hid descriptor. */ - if (len != GEN5_HID_DESCRIPTOR_SIZE) + if (len != PIP_HID_DESCRIPTOR_SIZE) return false; - resp_len = get_unaligned_le16(&buf[GEN5_RESP_LENGTH_OFFSET]); + resp_len = get_unaligned_le16(&buf[PIP_RESP_LENGTH_OFFSET]); max_output_len = get_unaligned_le16(&buf[16]); - if (resp_len == GEN5_HID_DESCRIPTOR_SIZE) { - if (buf[GEN5_RESP_REPORT_ID_OFFSET] == GEN5_BL_HID_REPORT_ID && + if (resp_len == PIP_HID_DESCRIPTOR_SIZE) { + if (buf[PIP_RESP_REPORT_ID_OFFSET] == PIP_HID_BL_REPORT_ID && max_output_len == GEN5_BL_MAX_OUTPUT_LENGTH) { /* BL mode HID Descriptor */ return true; - } else if ((buf[GEN5_RESP_REPORT_ID_OFFSET] == - GEN5_APP_HID_REPORT_ID) && + } else if ((buf[PIP_RESP_REPORT_ID_OFFSET] == + PIP_HID_APP_REPORT_ID) && max_output_len == GEN5_APP_MAX_OUTPUT_LENGTH) { /* APP mode HID Descriptor */ return true; @@ -746,21 +706,21 @@ static bool cyapa_gen5_sort_hid_descriptor_data(struct cyapa *cyapa, return false; } -static bool cyapa_gen5_sort_deep_sleep_data(struct cyapa *cyapa, +static bool cyapa_sort_pip_deep_sleep_data(struct cyapa *cyapa, u8 *buf, int len) { - if (len == GEN5_DEEP_SLEEP_RESP_LENGTH && - buf[GEN5_RESP_REPORT_ID_OFFSET] == - GEN5_APP_DEEP_SLEEP_REPORT_ID && - (buf[4] & GEN5_DEEP_SLEEP_OPCODE_MASK) == - GEN5_DEEP_SLEEP_OPCODE) + if (len == PIP_DEEP_SLEEP_RESP_LENGTH && + buf[PIP_RESP_REPORT_ID_OFFSET] == + PIP_APP_DEEP_SLEEP_REPORT_ID && + (buf[4] & PIP_DEEP_SLEEP_OPCODE_MASK) == + PIP_DEEP_SLEEP_OPCODE) return true; return false; } static int gen5_idle_state_parse(struct cyapa *cyapa) { - u8 resp_data[GEN5_HID_DESCRIPTOR_SIZE]; + u8 resp_data[PIP_HID_DESCRIPTOR_SIZE]; int max_output_len; int length; u8 cmd[2]; @@ -778,9 +738,9 @@ static int gen5_idle_state_parse(struct cyapa *cyapa) if (ret != 3) return ret < 0 ? ret : -EIO; - length = get_unaligned_le16(&resp_data[GEN5_RESP_LENGTH_OFFSET]); - if (length == GEN5_RESP_LENGTH_SIZE) { - /* Normal state of Gen5 with no data to respose */ + length = get_unaligned_le16(&resp_data[PIP_RESP_LENGTH_OFFSET]); + if (length == PIP_RESP_LENGTH_SIZE) { + /* Normal state of Gen5 with no data to response */ cyapa->gen = CYAPA_GEN5; cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL); @@ -788,30 +748,30 @@ static int gen5_idle_state_parse(struct cyapa *cyapa) /* Read description from trackpad device */ cmd[0] = 0x01; cmd[1] = 0x00; - length = GEN5_HID_DESCRIPTOR_SIZE; + length = PIP_HID_DESCRIPTOR_SIZE; error = cyapa_i2c_pip_cmd_irq_sync(cyapa, - cmd, GEN5_RESP_LENGTH_SIZE, + cmd, PIP_RESP_LENGTH_SIZE, resp_data, &length, 300, - cyapa_gen5_sort_hid_descriptor_data, + cyapa_sort_gen5_hid_descriptor_data, false); if (error) return error; length = get_unaligned_le16( - &resp_data[GEN5_RESP_LENGTH_OFFSET]); + &resp_data[PIP_RESP_LENGTH_OFFSET]); max_output_len = get_unaligned_le16(&resp_data[16]); - if ((length == GEN5_HID_DESCRIPTOR_SIZE || - length == GEN5_RESP_LENGTH_SIZE) && - (resp_data[GEN5_RESP_REPORT_ID_OFFSET] == - GEN5_BL_HID_REPORT_ID) && + if ((length == PIP_HID_DESCRIPTOR_SIZE || + length == PIP_RESP_LENGTH_SIZE) && + (resp_data[PIP_RESP_REPORT_ID_OFFSET] == + PIP_HID_BL_REPORT_ID) && max_output_len == GEN5_BL_MAX_OUTPUT_LENGTH) { /* BL mode HID Description read */ cyapa->state = CYAPA_STATE_GEN5_BL; - } else if ((length == GEN5_HID_DESCRIPTOR_SIZE || - length == GEN5_RESP_LENGTH_SIZE) && - (resp_data[GEN5_RESP_REPORT_ID_OFFSET] == - GEN5_APP_HID_REPORT_ID) && + } else if ((length == PIP_HID_DESCRIPTOR_SIZE || + length == PIP_RESP_LENGTH_SIZE) && + (resp_data[PIP_RESP_REPORT_ID_OFFSET] == + PIP_HID_APP_REPORT_ID) && max_output_len == GEN5_APP_MAX_OUTPUT_LENGTH) { /* APP mode HID Description read */ cyapa->state = CYAPA_STATE_GEN5_APP; @@ -839,14 +799,14 @@ static int gen5_hid_description_header_parse(struct cyapa *cyapa, u8 *reg_data) * or report any touch or button data. */ ret = cyapa_i2c_pip_read(cyapa, resp_data, - GEN5_HID_DESCRIPTOR_SIZE); - if (ret != GEN5_HID_DESCRIPTOR_SIZE) + PIP_HID_DESCRIPTOR_SIZE); + if (ret != PIP_HID_DESCRIPTOR_SIZE) return ret < 0 ? ret : -EIO; - length = get_unaligned_le16(&resp_data[GEN5_RESP_LENGTH_OFFSET]); + length = get_unaligned_le16(&resp_data[PIP_RESP_LENGTH_OFFSET]); max_output_len = get_unaligned_le16(&resp_data[16]); - if (length == GEN5_RESP_LENGTH_SIZE) { - if (reg_data[GEN5_RESP_REPORT_ID_OFFSET] == - GEN5_BL_HID_REPORT_ID) { + if (length == PIP_RESP_LENGTH_SIZE) { + if (reg_data[PIP_RESP_REPORT_ID_OFFSET] == + PIP_HID_BL_REPORT_ID) { /* * BL mode HID Description has been previously * read out. @@ -861,15 +821,15 @@ static int gen5_hid_description_header_parse(struct cyapa *cyapa, u8 *reg_data) cyapa->gen = CYAPA_GEN5; cyapa->state = CYAPA_STATE_GEN5_APP; } - } else if (length == GEN5_HID_DESCRIPTOR_SIZE && - resp_data[2] == GEN5_BL_HID_REPORT_ID && + } else if (length == PIP_HID_DESCRIPTOR_SIZE && + resp_data[2] == PIP_HID_BL_REPORT_ID && max_output_len == GEN5_BL_MAX_OUTPUT_LENGTH) { /* BL mode HID Description read. */ cyapa->gen = CYAPA_GEN5; cyapa->state = CYAPA_STATE_GEN5_BL; - } else if (length == GEN5_HID_DESCRIPTOR_SIZE && - (resp_data[GEN5_RESP_REPORT_ID_OFFSET] == - GEN5_APP_HID_REPORT_ID) && + } else if (length == PIP_HID_DESCRIPTOR_SIZE && + (resp_data[PIP_RESP_REPORT_ID_OFFSET] == + PIP_HID_APP_REPORT_ID) && max_output_len == GEN5_APP_MAX_OUTPUT_LENGTH) { /* APP mode HID Description read. */ cyapa->gen = CYAPA_GEN5; @@ -886,22 +846,22 @@ static int gen5_report_data_header_parse(struct cyapa *cyapa, u8 *reg_data) { int length; - length = get_unaligned_le16(®_data[GEN5_RESP_LENGTH_OFFSET]); - switch (reg_data[GEN5_RESP_REPORT_ID_OFFSET]) { - case GEN5_TOUCH_REPORT_ID: - if (length < GEN5_TOUCH_REPORT_HEAD_SIZE || - length > GEN5_TOUCH_REPORT_MAX_SIZE) + length = get_unaligned_le16(®_data[PIP_RESP_LENGTH_OFFSET]); + switch (reg_data[PIP_RESP_REPORT_ID_OFFSET]) { + case PIP_TOUCH_REPORT_ID: + if (length < PIP_TOUCH_REPORT_HEAD_SIZE || + length > PIP_TOUCH_REPORT_MAX_SIZE) return -EINVAL; break; - case GEN5_BTN_REPORT_ID: + case PIP_BTN_REPORT_ID: case GEN5_OLD_PUSH_BTN_REPORT_ID: - case GEN5_PUSH_BTN_REPORT_ID: - if (length < GEN5_BTN_REPORT_HEAD_SIZE || - length > GEN5_BTN_REPORT_MAX_SIZE) + case PIP_PUSH_BTN_REPORT_ID: + if (length < PIP_BTN_REPORT_HEAD_SIZE || + length > PIP_BTN_REPORT_MAX_SIZE) return -EINVAL; break; - case GEN5_WAKEUP_EVENT_REPORT_ID: - if (length != GEN5_WAKEUP_EVENT_SIZE) + case PIP_WAKEUP_EVENT_REPORT_ID: + if (length != PIP_WAKEUP_EVENT_SIZE) return -EINVAL; break; default: @@ -915,7 +875,7 @@ static int gen5_report_data_header_parse(struct cyapa *cyapa, u8 *reg_data) static int gen5_cmd_resp_header_parse(struct cyapa *cyapa, u8 *reg_data) { - struct cyapa_gen5_cmd_states *gen5_pip = &cyapa->cmd_states.gen5; + struct cyapa_pip_cmd_states *pip = &cyapa->cmd_states.pip; int length; int ret; @@ -924,15 +884,15 @@ static int gen5_cmd_resp_header_parse(struct cyapa *cyapa, u8 *reg_data) * otherwise Gen5 trackpad cannot response next command * or report any touch or button data. */ - length = get_unaligned_le16(®_data[GEN5_RESP_LENGTH_OFFSET]); - ret = cyapa_i2c_pip_read(cyapa, gen5_pip->empty_buf, length); + length = get_unaligned_le16(®_data[PIP_RESP_LENGTH_OFFSET]); + ret = cyapa_i2c_pip_read(cyapa, pip->empty_buf, length); if (ret != length) return ret < 0 ? ret : -EIO; - if (length == GEN5_RESP_LENGTH_SIZE) { + if (length == PIP_RESP_LENGTH_SIZE) { /* Previous command has read the data through out. */ - if (reg_data[GEN5_RESP_REPORT_ID_OFFSET] == - GEN5_BL_RESP_REPORT_ID) { + if (reg_data[PIP_RESP_REPORT_ID_OFFSET] == + PIP_BL_RESP_REPORT_ID) { /* Gen5 BL command response data detected */ cyapa->gen = CYAPA_GEN5; cyapa->state = CYAPA_STATE_GEN5_BL; @@ -941,21 +901,21 @@ static int gen5_cmd_resp_header_parse(struct cyapa *cyapa, u8 *reg_data) cyapa->gen = CYAPA_GEN5; cyapa->state = CYAPA_STATE_GEN5_APP; } - } else if ((gen5_pip->empty_buf[GEN5_RESP_REPORT_ID_OFFSET] == - GEN5_BL_RESP_REPORT_ID) && - (gen5_pip->empty_buf[GEN5_RESP_RSVD_OFFSET] == - GEN5_RESP_RSVD_KEY) && - (gen5_pip->empty_buf[GEN5_RESP_BL_SOP_OFFSET] == - GEN5_SOP_KEY) && - (gen5_pip->empty_buf[length - 1] == - GEN5_EOP_KEY)) { + } else if ((pip->empty_buf[PIP_RESP_REPORT_ID_OFFSET] == + PIP_BL_RESP_REPORT_ID) && + (pip->empty_buf[PIP_RESP_RSVD_OFFSET] == + PIP_RESP_RSVD_KEY) && + (pip->empty_buf[PIP_RESP_BL_SOP_OFFSET] == + PIP_SOP_KEY) && + (pip->empty_buf[length - 1] == + PIP_EOP_KEY)) { /* Gen5 BL command response data detected */ cyapa->gen = CYAPA_GEN5; cyapa->state = CYAPA_STATE_GEN5_BL; - } else if (gen5_pip->empty_buf[GEN5_RESP_REPORT_ID_OFFSET] == - GEN5_APP_RESP_REPORT_ID && - gen5_pip->empty_buf[GEN5_RESP_RSVD_OFFSET] == - GEN5_RESP_RSVD_KEY) { + } else if (pip->empty_buf[PIP_RESP_REPORT_ID_OFFSET] == + PIP_APP_RESP_REPORT_ID && + pip->empty_buf[PIP_RESP_RSVD_OFFSET] == + PIP_RESP_RSVD_KEY) { /* Gen5 APP command response data detected */ cyapa->gen = CYAPA_GEN5; cyapa->state = CYAPA_STATE_GEN5_APP; @@ -977,12 +937,12 @@ static int cyapa_gen5_state_parse(struct cyapa *cyapa, u8 *reg_data, int len) cyapa->state = CYAPA_STATE_NO_DEVICE; /* Parse based on Gen5 characteristic registers and bits */ - length = get_unaligned_le16(®_data[GEN5_RESP_LENGTH_OFFSET]); - if (length == 0 || length == GEN5_RESP_LENGTH_SIZE) { + length = get_unaligned_le16(®_data[PIP_RESP_LENGTH_OFFSET]); + if (length == 0 || length == PIP_RESP_LENGTH_SIZE) { gen5_idle_state_parse(cyapa); - } else if (length == GEN5_HID_DESCRIPTOR_SIZE && - (reg_data[2] == GEN5_BL_HID_REPORT_ID || - reg_data[2] == GEN5_APP_HID_REPORT_ID)) { + } else if (length == PIP_HID_DESCRIPTOR_SIZE && + (reg_data[2] == PIP_HID_BL_REPORT_ID || + reg_data[2] == PIP_HID_APP_REPORT_ID)) { gen5_hid_description_header_parse(cyapa, reg_data); } else if ((length == GEN5_APP_REPORT_DESCRIPTOR_SIZE || length == GEN5_APP_CONTRACT_REPORT_DESCRIPTOR_SIZE) && @@ -992,17 +952,17 @@ static int cyapa_gen5_state_parse(struct cyapa *cyapa, u8 *reg_data, int len) cyapa->state = CYAPA_STATE_GEN5_APP; } else if (length == GEN5_BL_REPORT_DESCRIPTOR_SIZE && reg_data[2] == GEN5_BL_REPORT_DESCRIPTOR_ID) { - /* 0x1D 0x00 0xFE is Gen5 BL report descriptior header. */ + /* 0x1D 0x00 0xFE is Gen5 BL report descriptor header. */ cyapa->gen = CYAPA_GEN5; cyapa->state = CYAPA_STATE_GEN5_BL; - } else if (reg_data[2] == GEN5_TOUCH_REPORT_ID || - reg_data[2] == GEN5_BTN_REPORT_ID || + } else if (reg_data[2] == PIP_TOUCH_REPORT_ID || + reg_data[2] == PIP_BTN_REPORT_ID || reg_data[2] == GEN5_OLD_PUSH_BTN_REPORT_ID || - reg_data[2] == GEN5_PUSH_BTN_REPORT_ID || - reg_data[2] == GEN5_WAKEUP_EVENT_REPORT_ID) { + reg_data[2] == PIP_PUSH_BTN_REPORT_ID || + reg_data[2] == PIP_WAKEUP_EVENT_REPORT_ID) { gen5_report_data_header_parse(cyapa, reg_data); - } else if (reg_data[2] == GEN5_BL_RESP_REPORT_ID || - reg_data[2] == GEN5_APP_RESP_REPORT_ID) { + } else if (reg_data[2] == PIP_BL_RESP_REPORT_ID || + reg_data[2] == PIP_APP_RESP_REPORT_ID) { gen5_cmd_resp_header_parse(cyapa, reg_data); } @@ -1023,14 +983,25 @@ static int cyapa_gen5_state_parse(struct cyapa *cyapa, u8 *reg_data, int len) return -EAGAIN; } -static int cyapa_gen5_bl_initiate(struct cyapa *cyapa, - const struct firmware *fw) +static struct cyapa_tsg_bin_image_data_record * +cyapa_get_image_record_data_num(const struct firmware *fw, + int *record_num) +{ + int head_size; + + head_size = fw->data[0] + 1; + *record_num = (fw->size - head_size) / + sizeof(struct cyapa_tsg_bin_image_data_record); + return (struct cyapa_tsg_bin_image_data_record *)&fw->data[head_size]; +} + +int cyapa_pip_bl_initiate(struct cyapa *cyapa, const struct firmware *fw) { - struct cyapa_tsg_bin_image *image; - struct gen5_bl_cmd_head *bl_cmd_head; - struct gen5_bl_packet_start *bl_packet_start; - struct gen5_bl_initiate_cmd_data *cmd_data; - struct gen5_bl_packet_end *bl_packet_end; + struct cyapa_tsg_bin_image_data_record *image_records; + struct pip_bl_cmd_head *bl_cmd_head; + struct pip_bl_packet_start *bl_packet_start; + struct pip_bl_initiate_cmd_data *cmd_data; + struct pip_bl_packet_end *bl_packet_end; u8 cmd[CYAPA_TSG_MAX_CMD_SIZE]; int cmd_len; u16 cmd_data_len; @@ -1046,30 +1017,28 @@ static int cyapa_gen5_bl_initiate(struct cyapa *cyapa, cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL); memset(cmd, 0, CYAPA_TSG_MAX_CMD_SIZE); - bl_cmd_head = (struct gen5_bl_cmd_head *)cmd; + bl_cmd_head = (struct pip_bl_cmd_head *)cmd; cmd_data_len = CYAPA_TSG_BL_KEY_SIZE + CYAPA_TSG_FLASH_MAP_BLOCK_SIZE; - cmd_len = sizeof(struct gen5_bl_cmd_head) + cmd_data_len + - sizeof(struct gen5_bl_packet_end); + cmd_len = sizeof(struct pip_bl_cmd_head) + cmd_data_len + + sizeof(struct pip_bl_packet_end); - put_unaligned_le16(GEN5_OUTPUT_REPORT_ADDR, &bl_cmd_head->addr); + put_unaligned_le16(PIP_OUTPUT_REPORT_ADDR, &bl_cmd_head->addr); put_unaligned_le16(cmd_len - 2, &bl_cmd_head->length); - bl_cmd_head->report_id = GEN5_BL_CMD_REPORT_ID; + bl_cmd_head->report_id = PIP_BL_CMD_REPORT_ID; bl_packet_start = &bl_cmd_head->packet_start; - bl_packet_start->sop = GEN5_SOP_KEY; - bl_packet_start->cmd_code = GEN5_BL_CMD_INITIATE_BL; + bl_packet_start->sop = PIP_SOP_KEY; + bl_packet_start->cmd_code = PIP_BL_CMD_INITIATE_BL; /* 8 key bytes and 128 bytes block size */ put_unaligned_le16(cmd_data_len, &bl_packet_start->data_length); - cmd_data = (struct gen5_bl_initiate_cmd_data *)bl_cmd_head->data; - memcpy(cmd_data->key, cyapa_gen5_bl_cmd_key, CYAPA_TSG_BL_KEY_SIZE); + cmd_data = (struct pip_bl_initiate_cmd_data *)bl_cmd_head->data; + memcpy(cmd_data->key, cyapa_pip_bl_cmd_key, CYAPA_TSG_BL_KEY_SIZE); + + image_records = cyapa_get_image_record_data_num(fw, &records_num); - /* Copy 60 bytes Meta Data Row Parameters */ - image = (struct cyapa_tsg_bin_image *)fw->data; - records_num = (fw->size - sizeof(struct cyapa_tsg_bin_image_head)) / - sizeof(struct cyapa_tsg_bin_image_data_record); /* APP_INTEGRITY row is always the last row block */ - data = image->records[records_num - 1].record_data; + data = image_records[records_num - 1].record_data; memcpy(cmd_data->metadata_raw_parameter, data, CYAPA_TSG_FLASH_MAP_METADATA_SIZE); @@ -1077,47 +1046,47 @@ static int cyapa_gen5_bl_initiate(struct cyapa *cyapa, CYAPA_TSG_FLASH_MAP_METADATA_SIZE); put_unaligned_le16(meta_data_crc, &cmd_data->metadata_crc); - bl_packet_end = (struct gen5_bl_packet_end *)(bl_cmd_head->data + + bl_packet_end = (struct pip_bl_packet_end *)(bl_cmd_head->data + cmd_data_len); cmd_crc = crc_itu_t(0xffff, (u8 *)bl_packet_start, - sizeof(struct gen5_bl_packet_start) + cmd_data_len); + sizeof(struct pip_bl_packet_start) + cmd_data_len); put_unaligned_le16(cmd_crc, &bl_packet_end->crc); - bl_packet_end->eop = GEN5_EOP_KEY; + bl_packet_end->eop = PIP_EOP_KEY; resp_len = sizeof(resp_data); error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, cmd_len, resp_data, &resp_len, 12000, - cyapa_gen5_sort_tsg_pip_bl_resp_data, true); - if (error || resp_len != GEN5_BL_INITIATE_RESP_LEN || - resp_data[2] != GEN5_BL_RESP_REPORT_ID || - !GEN5_CMD_COMPLETE_SUCCESS(resp_data[5])) + cyapa_sort_tsg_pip_bl_resp_data, true); + if (error || resp_len != PIP_BL_INITIATE_RESP_LEN || + resp_data[2] != PIP_BL_RESP_REPORT_ID || + !PIP_CMD_COMPLETE_SUCCESS(resp_data)) return error ? error : -EAGAIN; return 0; } -static bool cyapa_gen5_sort_bl_exit_data(struct cyapa *cyapa, u8 *buf, int len) +static bool cyapa_sort_pip_bl_exit_data(struct cyapa *cyapa, u8 *buf, int len) { - if (buf == NULL || len < GEN5_RESP_LENGTH_SIZE) + if (buf == NULL || len < PIP_RESP_LENGTH_SIZE) return false; if (buf[0] == 0 && buf[1] == 0) return true; /* Exit bootloader failed for some reason. */ - if (len == GEN5_BL_FAIL_EXIT_RESP_LEN && - buf[GEN5_RESP_REPORT_ID_OFFSET] == - GEN5_BL_RESP_REPORT_ID && - buf[GEN5_RESP_RSVD_OFFSET] == GEN5_RESP_RSVD_KEY && - buf[GEN5_RESP_BL_SOP_OFFSET] == GEN5_SOP_KEY && - buf[10] == GEN5_EOP_KEY) + if (len == PIP_BL_FAIL_EXIT_RESP_LEN && + buf[PIP_RESP_REPORT_ID_OFFSET] == + PIP_BL_RESP_REPORT_ID && + buf[PIP_RESP_RSVD_OFFSET] == PIP_RESP_RSVD_KEY && + buf[PIP_RESP_BL_SOP_OFFSET] == PIP_SOP_KEY && + buf[10] == PIP_EOP_KEY) return true; return false; } -static int cyapa_gen5_bl_exit(struct cyapa *cyapa) +int cyapa_pip_bl_exit(struct cyapa *cyapa) { u8 bl_gen5_bl_exit[] = { 0x04, 0x00, @@ -1132,13 +1101,13 @@ static int cyapa_gen5_bl_exit(struct cyapa *cyapa) error = cyapa_i2c_pip_cmd_irq_sync(cyapa, bl_gen5_bl_exit, sizeof(bl_gen5_bl_exit), resp_data, &resp_len, - 5000, cyapa_gen5_sort_bl_exit_data, false); + 5000, cyapa_sort_pip_bl_exit_data, false); if (error) return error; - if (resp_len == GEN5_BL_FAIL_EXIT_RESP_LEN || - resp_data[GEN5_RESP_REPORT_ID_OFFSET] == - GEN5_BL_RESP_REPORT_ID) + if (resp_len == PIP_BL_FAIL_EXIT_RESP_LEN || + resp_data[PIP_RESP_REPORT_ID_OFFSET] == + PIP_BL_RESP_REPORT_ID) return -EAGAIN; if (resp_data[0] == 0x00 && resp_data[1] == 0x00) @@ -1147,7 +1116,7 @@ static int cyapa_gen5_bl_exit(struct cyapa *cyapa) return -ENODEV; } -static int cyapa_gen5_bl_enter(struct cyapa *cyapa) +int cyapa_pip_bl_enter(struct cyapa *cyapa) { u8 cmd[] = { 0x04, 0x00, 0x05, 0x00, 0x2F, 0x00, 0x01 }; u8 resp_data[2]; @@ -1157,15 +1126,12 @@ static int cyapa_gen5_bl_enter(struct cyapa *cyapa) error = cyapa_poll_state(cyapa, 500); if (error < 0) return error; - if (cyapa->gen != CYAPA_GEN5) - return -EINVAL; - /* Already in Gen5 BL. Skipping exit. */ - if (cyapa->state == CYAPA_STATE_GEN5_BL) + /* Already in bootloader mode, Skipping exit. */ + if (cyapa_is_pip_bl_mode(cyapa)) return 0; - - if (cyapa->state != CYAPA_STATE_GEN5_APP) - return -EAGAIN; + else if (!cyapa_is_pip_app_mode(cyapa)) + return -EINVAL; /* Try to dump all buffered report data before any send command. */ cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL); @@ -1179,39 +1145,79 @@ static int cyapa_gen5_bl_enter(struct cyapa *cyapa) error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd), resp_data, &resp_len, - 5000, cyapa_gen5_sort_application_launch_data, + 5000, cyapa_sort_pip_application_launch_data, true); if (error || resp_data[0] != 0x00 || resp_data[1] != 0x00) return error < 0 ? error : -EAGAIN; cyapa->operational = false; - cyapa->state = CYAPA_STATE_GEN5_BL; + if (cyapa->gen == CYAPA_GEN5) + cyapa->state = CYAPA_STATE_GEN5_BL; + else if (cyapa->gen == CYAPA_GEN6) + cyapa->state = CYAPA_STATE_GEN6_BL; return 0; } -static int cyapa_gen5_check_fw(struct cyapa *cyapa, const struct firmware *fw) +static int cyapa_pip_fw_head_check(struct cyapa *cyapa, + struct cyapa_tsg_bin_image_head *image_head) +{ + if (image_head->head_size != 0x0C && image_head->head_size != 0x12) + return -EINVAL; + + switch (cyapa->gen) { + case CYAPA_GEN6: + if (image_head->family_id != 0x9B || + image_head->silicon_id_hi != 0x0B) + return -EINVAL; + break; + case CYAPA_GEN5: + /* Gen5 without proximity support. */ + if (cyapa->platform_ver < 2) { + if (image_head->head_size == 0x0C) + break; + return -EINVAL; + } + + if (image_head->family_id != 0x91 || + image_head->silicon_id_hi != 0x02) + return -EINVAL; + break; + default: + return -EINVAL; + } + + return 0; +} + +int cyapa_pip_check_fw(struct cyapa *cyapa, const struct firmware *fw) { struct device *dev = &cyapa->client->dev; - const struct cyapa_tsg_bin_image *image = (const void *)fw->data; + struct cyapa_tsg_bin_image_data_record *image_records; const struct cyapa_tsg_bin_image_data_record *app_integrity; - const struct gen5_bl_metadata_row_params *metadata; - size_t flash_records_count; + const struct tsg_bl_metadata_row_params *metadata; + int flash_records_count; u32 fw_app_start, fw_upgrade_start; u16 fw_app_len, fw_upgrade_len; u16 app_crc; u16 app_integrity_crc; - int record_index; int i; - flash_records_count = (fw->size - - sizeof(struct cyapa_tsg_bin_image_head)) / - sizeof(struct cyapa_tsg_bin_image_data_record); + /* Verify the firmware image not miss-used for Gen5 and Gen6. */ + if (cyapa_pip_fw_head_check(cyapa, + (struct cyapa_tsg_bin_image_head *)fw->data)) { + dev_err(dev, "%s: firmware image not match TP device.\n", + __func__); + return -EINVAL; + } + + image_records = + cyapa_get_image_record_data_num(fw, &flash_records_count); /* * APP_INTEGRITY row is always the last row block, * and the row id must be 0x01ff. */ - app_integrity = &image->records[flash_records_count - 1]; + app_integrity = &image_records[flash_records_count - 1]; if (app_integrity->flash_array_id != 0x00 || get_unaligned_be16(&app_integrity->row_number) != 0x01ff) { @@ -1242,14 +1248,11 @@ static int cyapa_gen5_check_fw(struct cyapa *cyapa, const struct firmware *fw) return -EINVAL; } - /* - * Verify application image CRC - */ - record_index = fw_app_start / CYAPA_TSG_FW_ROW_SIZE - - CYAPA_TSG_IMG_START_ROW_NUM; + /* Verify application image CRC. */ app_crc = 0xffffU; for (i = 0; i < fw_app_len / CYAPA_TSG_FW_ROW_SIZE; i++) { - const u8 *data = image->records[record_index + i].record_data; + const u8 *data = image_records[i].record_data; + app_crc = crc_itu_t(app_crc, data, CYAPA_TSG_FW_ROW_SIZE); } @@ -1261,13 +1264,13 @@ static int cyapa_gen5_check_fw(struct cyapa *cyapa, const struct firmware *fw) return 0; } -static int cyapa_gen5_write_fw_block(struct cyapa *cyapa, +static int cyapa_pip_write_fw_block(struct cyapa *cyapa, struct cyapa_tsg_bin_image_data_record *flash_record) { - struct gen5_bl_cmd_head *bl_cmd_head; - struct gen5_bl_packet_start *bl_packet_start; - struct gen5_bl_flash_row_head *flash_row_head; - struct gen5_bl_packet_end *bl_packet_end; + struct pip_bl_cmd_head *bl_cmd_head; + struct pip_bl_packet_start *bl_packet_start; + struct tsg_bl_flash_row_head *flash_row_head; + struct pip_bl_packet_end *bl_packet_end; u8 cmd[CYAPA_TSG_MAX_CMD_SIZE]; u16 cmd_len; u8 flash_array_id; @@ -1286,71 +1289,68 @@ static int cyapa_gen5_write_fw_block(struct cyapa *cyapa, record_data = flash_record->record_data; memset(cmd, 0, CYAPA_TSG_MAX_CMD_SIZE); - bl_cmd_head = (struct gen5_bl_cmd_head *)cmd; + bl_cmd_head = (struct pip_bl_cmd_head *)cmd; bl_packet_start = &bl_cmd_head->packet_start; - cmd_len = sizeof(struct gen5_bl_cmd_head) + - sizeof(struct gen5_bl_flash_row_head) + + cmd_len = sizeof(struct pip_bl_cmd_head) + + sizeof(struct tsg_bl_flash_row_head) + CYAPA_TSG_FLASH_MAP_BLOCK_SIZE + - sizeof(struct gen5_bl_packet_end); + sizeof(struct pip_bl_packet_end); - put_unaligned_le16(GEN5_OUTPUT_REPORT_ADDR, &bl_cmd_head->addr); + put_unaligned_le16(PIP_OUTPUT_REPORT_ADDR, &bl_cmd_head->addr); /* Don't include 2 bytes register address */ put_unaligned_le16(cmd_len - 2, &bl_cmd_head->length); - bl_cmd_head->report_id = GEN5_BL_CMD_REPORT_ID; - bl_packet_start->sop = GEN5_SOP_KEY; - bl_packet_start->cmd_code = GEN5_BL_CMD_PROGRAM_VERIFY_ROW; + bl_cmd_head->report_id = PIP_BL_CMD_REPORT_ID; + bl_packet_start->sop = PIP_SOP_KEY; + bl_packet_start->cmd_code = PIP_BL_CMD_PROGRAM_VERIFY_ROW; /* 1 (Flash Array ID) + 2 (Flash Row ID) + 128 (flash data) */ - data_len = sizeof(struct gen5_bl_flash_row_head) + record_len; + data_len = sizeof(struct tsg_bl_flash_row_head) + record_len; put_unaligned_le16(data_len, &bl_packet_start->data_length); - flash_row_head = (struct gen5_bl_flash_row_head *)bl_cmd_head->data; + flash_row_head = (struct tsg_bl_flash_row_head *)bl_cmd_head->data; flash_row_head->flash_array_id = flash_array_id; put_unaligned_le16(flash_row_id, &flash_row_head->flash_row_id); memcpy(flash_row_head->flash_data, record_data, record_len); - bl_packet_end = (struct gen5_bl_packet_end *)(bl_cmd_head->data + + bl_packet_end = (struct pip_bl_packet_end *)(bl_cmd_head->data + data_len); crc = crc_itu_t(0xffff, (u8 *)bl_packet_start, - sizeof(struct gen5_bl_packet_start) + data_len); + sizeof(struct pip_bl_packet_start) + data_len); put_unaligned_le16(crc, &bl_packet_end->crc); - bl_packet_end->eop = GEN5_EOP_KEY; + bl_packet_end->eop = PIP_EOP_KEY; resp_len = sizeof(resp_data); error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, cmd_len, resp_data, &resp_len, - 500, cyapa_gen5_sort_tsg_pip_bl_resp_data, true); - if (error || resp_len != GEN5_BL_BLOCK_WRITE_RESP_LEN || - resp_data[2] != GEN5_BL_RESP_REPORT_ID || - !GEN5_CMD_COMPLETE_SUCCESS(resp_data[5])) + 500, cyapa_sort_tsg_pip_bl_resp_data, true); + if (error || resp_len != PIP_BL_BLOCK_WRITE_RESP_LEN || + resp_data[2] != PIP_BL_RESP_REPORT_ID || + !PIP_CMD_COMPLETE_SUCCESS(resp_data)) return error < 0 ? error : -EAGAIN; return 0; } -static int cyapa_gen5_do_fw_update(struct cyapa *cyapa, +int cyapa_pip_do_fw_update(struct cyapa *cyapa, const struct firmware *fw) { struct device *dev = &cyapa->client->dev; - struct cyapa_tsg_bin_image_data_record *flash_record; - struct cyapa_tsg_bin_image *image = - (struct cyapa_tsg_bin_image *)fw->data; + struct cyapa_tsg_bin_image_data_record *image_records; int flash_records_count; int i; int error; cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL); - flash_records_count = - (fw->size - sizeof(struct cyapa_tsg_bin_image_head)) / - sizeof(struct cyapa_tsg_bin_image_data_record); + image_records = + cyapa_get_image_record_data_num(fw, &flash_records_count); + /* * The last flash row 0x01ff has been written through bl_initiate * command, so DO NOT write flash 0x01ff to trackpad device. */ for (i = 0; i < (flash_records_count - 1); i++) { - flash_record = &image->records[i]; - error = cyapa_gen5_write_fw_block(cyapa, flash_record); + error = cyapa_pip_write_fw_block(cyapa, &image_records[i]); if (error) { dev_err(dev, "%s: Gen5 FW update aborted: %d\n", __func__, error); @@ -1372,9 +1372,9 @@ static int cyapa_gen5_change_power_state(struct cyapa *cyapa, u8 power_state) resp_len = sizeof(resp_data); error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd), resp_data, &resp_len, - 500, cyapa_gen5_sort_tsg_pip_app_resp_data, false); + 500, cyapa_sort_tsg_pip_app_resp_data, false); if (error || !VALID_CMD_RESP_HEADER(resp_data, 0x08) || - !GEN5_CMD_COMPLETE_SUCCESS(resp_data[5])) + !PIP_CMD_COMPLETE_SUCCESS(resp_data)) return error < 0 ? error : -EINVAL; return 0; @@ -1383,7 +1383,7 @@ static int cyapa_gen5_change_power_state(struct cyapa *cyapa, u8 power_state) static int cyapa_gen5_set_interval_time(struct cyapa *cyapa, u8 parameter_id, u16 interval_time) { - struct gen5_app_cmd_head *app_cmd_head; + struct pip_app_cmd_head *app_cmd_head; struct gen5_app_set_parameter_data *parameter_data; u8 cmd[CYAPA_TSG_MAX_CMD_SIZE]; int cmd_len; @@ -1393,10 +1393,10 @@ static int cyapa_gen5_set_interval_time(struct cyapa *cyapa, int error; memset(cmd, 0, CYAPA_TSG_MAX_CMD_SIZE); - app_cmd_head = (struct gen5_app_cmd_head *)cmd; + app_cmd_head = (struct pip_app_cmd_head *)cmd; parameter_data = (struct gen5_app_set_parameter_data *) app_cmd_head->parameter_data; - cmd_len = sizeof(struct gen5_app_cmd_head) + + cmd_len = sizeof(struct pip_app_cmd_head) + sizeof(struct gen5_app_set_parameter_data); switch (parameter_id) { @@ -1413,14 +1413,14 @@ static int cyapa_gen5_set_interval_time(struct cyapa *cyapa, return -EINVAL; } - put_unaligned_le16(GEN5_OUTPUT_REPORT_ADDR, &app_cmd_head->addr); + put_unaligned_le16(PIP_OUTPUT_REPORT_ADDR, &app_cmd_head->addr); /* * Don't include unused parameter value bytes and * 2 bytes register address. */ put_unaligned_le16(cmd_len - (4 - parameter_size) - 2, &app_cmd_head->length); - app_cmd_head->report_id = GEN5_APP_CMD_REPORT_ID; + app_cmd_head->report_id = PIP_APP_CMD_REPORT_ID; app_cmd_head->cmd_code = GEN5_CMD_SET_PARAMETER; parameter_data->parameter_id = parameter_id; parameter_data->parameter_size = parameter_size; @@ -1428,7 +1428,7 @@ static int cyapa_gen5_set_interval_time(struct cyapa *cyapa, resp_len = sizeof(resp_data); error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, cmd_len, resp_data, &resp_len, - 500, cyapa_gen5_sort_tsg_pip_app_resp_data, false); + 500, cyapa_sort_tsg_pip_app_resp_data, false); if (error || resp_data[5] != parameter_id || resp_data[6] != parameter_size || !VALID_CMD_RESP_HEADER(resp_data, GEN5_CMD_SET_PARAMETER)) @@ -1440,7 +1440,7 @@ static int cyapa_gen5_set_interval_time(struct cyapa *cyapa, static int cyapa_gen5_get_interval_time(struct cyapa *cyapa, u8 parameter_id, u16 *interval_time) { - struct gen5_app_cmd_head *app_cmd_head; + struct pip_app_cmd_head *app_cmd_head; struct gen5_app_get_parameter_data *parameter_data; u8 cmd[CYAPA_TSG_MAX_CMD_SIZE]; int cmd_len; @@ -1451,10 +1451,10 @@ static int cyapa_gen5_get_interval_time(struct cyapa *cyapa, int error; memset(cmd, 0, CYAPA_TSG_MAX_CMD_SIZE); - app_cmd_head = (struct gen5_app_cmd_head *)cmd; + app_cmd_head = (struct pip_app_cmd_head *)cmd; parameter_data = (struct gen5_app_get_parameter_data *) app_cmd_head->parameter_data; - cmd_len = sizeof(struct gen5_app_cmd_head) + + cmd_len = sizeof(struct pip_app_cmd_head) + sizeof(struct gen5_app_get_parameter_data); *interval_time = 0; @@ -1472,17 +1472,17 @@ static int cyapa_gen5_get_interval_time(struct cyapa *cyapa, return -EINVAL; } - put_unaligned_le16(GEN5_HID_DESCRIPTOR_ADDR, &app_cmd_head->addr); + put_unaligned_le16(PIP_OUTPUT_REPORT_ADDR, &app_cmd_head->addr); /* Don't include 2 bytes register address */ put_unaligned_le16(cmd_len - 2, &app_cmd_head->length); - app_cmd_head->report_id = GEN5_APP_CMD_REPORT_ID; + app_cmd_head->report_id = PIP_APP_CMD_REPORT_ID; app_cmd_head->cmd_code = GEN5_CMD_GET_PARAMETER; parameter_data->parameter_id = parameter_id; resp_len = sizeof(resp_data); error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, cmd_len, resp_data, &resp_len, - 500, cyapa_gen5_sort_tsg_pip_app_resp_data, false); + 500, cyapa_sort_tsg_pip_app_resp_data, false); if (error || resp_data[5] != parameter_id || resp_data[6] == 0 || !VALID_CMD_RESP_HEADER(resp_data, GEN5_CMD_GET_PARAMETER)) return error < 0 ? error : -EINVAL; @@ -1497,18 +1497,18 @@ static int cyapa_gen5_get_interval_time(struct cyapa *cyapa, static int cyapa_gen5_disable_pip_report(struct cyapa *cyapa) { - struct gen5_app_cmd_head *app_cmd_head; + struct pip_app_cmd_head *app_cmd_head; u8 cmd[10]; u8 resp_data[7]; int resp_len; int error; memset(cmd, 0, sizeof(cmd)); - app_cmd_head = (struct gen5_app_cmd_head *)cmd; + app_cmd_head = (struct pip_app_cmd_head *)cmd; - put_unaligned_le16(GEN5_HID_DESCRIPTOR_ADDR, &app_cmd_head->addr); + put_unaligned_le16(PIP_OUTPUT_REPORT_ADDR, &app_cmd_head->addr); put_unaligned_le16(sizeof(cmd) - 2, &app_cmd_head->length); - app_cmd_head->report_id = GEN5_APP_CMD_REPORT_ID; + app_cmd_head->report_id = PIP_APP_CMD_REPORT_ID; app_cmd_head->cmd_code = GEN5_CMD_SET_PARAMETER; app_cmd_head->parameter_data[0] = GEN5_PARAMETER_DISABLE_PIP_REPORT; app_cmd_head->parameter_data[1] = 0x01; @@ -1516,7 +1516,7 @@ static int cyapa_gen5_disable_pip_report(struct cyapa *cyapa) resp_len = sizeof(resp_data); error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd), resp_data, &resp_len, - 500, cyapa_gen5_sort_tsg_pip_app_resp_data, false); + 500, cyapa_sort_tsg_pip_app_resp_data, false); if (error || resp_data[5] != GEN5_PARAMETER_DISABLE_PIP_REPORT || !VALID_CMD_RESP_HEADER(resp_data, GEN5_CMD_SET_PARAMETER) || resp_data[6] != 0x01) @@ -1525,26 +1525,48 @@ static int cyapa_gen5_disable_pip_report(struct cyapa *cyapa) return 0; } -static int cyapa_gen5_deep_sleep(struct cyapa *cyapa, u8 state) +int cyapa_pip_set_proximity(struct cyapa *cyapa, bool enable) +{ + u8 cmd[] = { 0x04, 0x00, 0x06, 0x00, 0x2f, 0x00, PIP_SET_PROXIMITY, + (u8)!!enable + }; + u8 resp_data[6]; + int resp_len; + int error; + + resp_len = sizeof(resp_data); + error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd), + resp_data, &resp_len, + 500, cyapa_sort_tsg_pip_app_resp_data, false); + if (error || !VALID_CMD_RESP_HEADER(resp_data, PIP_SET_PROXIMITY) || + !PIP_CMD_COMPLETE_SUCCESS(resp_data)) { + error = (error == -ETIMEDOUT) ? -EOPNOTSUPP : error; + return error < 0 ? error : -EINVAL; + } + + return 0; +} + +int cyapa_pip_deep_sleep(struct cyapa *cyapa, u8 state) { u8 cmd[] = { 0x05, 0x00, 0x00, 0x08}; u8 resp_data[5]; int resp_len; int error; - cmd[2] = state & GEN5_DEEP_SLEEP_STATE_MASK; + cmd[2] = state & PIP_DEEP_SLEEP_STATE_MASK; resp_len = sizeof(resp_data); error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd), resp_data, &resp_len, - 500, cyapa_gen5_sort_deep_sleep_data, false); - if (error || ((resp_data[3] & GEN5_DEEP_SLEEP_STATE_MASK) != state)) + 500, cyapa_sort_pip_deep_sleep_data, false); + if (error || ((resp_data[3] & PIP_DEEP_SLEEP_STATE_MASK) != state)) return -EINVAL; return 0; } static int cyapa_gen5_set_power_mode(struct cyapa *cyapa, - u8 power_mode, u16 sleep_time) + u8 power_mode, u16 sleep_time, bool is_suspend) { struct device *dev = &cyapa->client->dev; u8 power_state; @@ -1553,43 +1575,40 @@ static int cyapa_gen5_set_power_mode(struct cyapa *cyapa, if (cyapa->state != CYAPA_STATE_GEN5_APP) return 0; - /* Dump all the report data before do power mode commmands. */ - cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL); - - if (GEN5_DEV_GET_PWR_STATE(cyapa) == UNINIT_PWR_MODE) { + if (PIP_DEV_GET_PWR_STATE(cyapa) == UNINIT_PWR_MODE) { /* * Assume TP in deep sleep mode when driver is loaded, * avoid driver unload and reload command IO issue caused by TP * has been set into deep sleep mode when unloading. */ - GEN5_DEV_SET_PWR_STATE(cyapa, PWR_MODE_OFF); + PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_OFF); } - if (GEN5_DEV_UNINIT_SLEEP_TIME(cyapa) && - GEN5_DEV_GET_PWR_STATE(cyapa) != PWR_MODE_OFF) + if (PIP_DEV_UNINIT_SLEEP_TIME(cyapa) && + PIP_DEV_GET_PWR_STATE(cyapa) != PWR_MODE_OFF) if (cyapa_gen5_get_interval_time(cyapa, GEN5_PARAMETER_LP_INTRVL_ID, &cyapa->dev_sleep_time) != 0) - GEN5_DEV_SET_SLEEP_TIME(cyapa, UNINIT_SLEEP_TIME); + PIP_DEV_SET_SLEEP_TIME(cyapa, UNINIT_SLEEP_TIME); - if (GEN5_DEV_GET_PWR_STATE(cyapa) == power_mode) { + if (PIP_DEV_GET_PWR_STATE(cyapa) == power_mode) { if (power_mode == PWR_MODE_OFF || power_mode == PWR_MODE_FULL_ACTIVE || power_mode == PWR_MODE_BTN_ONLY || - GEN5_DEV_GET_SLEEP_TIME(cyapa) == sleep_time) { + PIP_DEV_GET_SLEEP_TIME(cyapa) == sleep_time) { /* Has in correct power mode state, early return. */ return 0; } } if (power_mode == PWR_MODE_OFF) { - error = cyapa_gen5_deep_sleep(cyapa, GEN5_DEEP_SLEEP_STATE_OFF); + error = cyapa_pip_deep_sleep(cyapa, PIP_DEEP_SLEEP_STATE_OFF); if (error) { dev_err(dev, "enter deep sleep fail: %d\n", error); return error; } - GEN5_DEV_SET_PWR_STATE(cyapa, PWR_MODE_OFF); + PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_OFF); return 0; } @@ -1598,8 +1617,8 @@ static int cyapa_gen5_set_power_mode(struct cyapa *cyapa, * state directly, must be wake up from sleep firstly, then * continue to do next power sate change. */ - if (GEN5_DEV_GET_PWR_STATE(cyapa) == PWR_MODE_OFF) { - error = cyapa_gen5_deep_sleep(cyapa, GEN5_DEEP_SLEEP_STATE_ON); + if (PIP_DEV_GET_PWR_STATE(cyapa) == PWR_MODE_OFF) { + error = cyapa_pip_deep_sleep(cyapa, PIP_DEEP_SLEEP_STATE_ON); if (error) { dev_err(dev, "deep sleep wake fail: %d\n", error); return error; @@ -1614,7 +1633,7 @@ static int cyapa_gen5_set_power_mode(struct cyapa *cyapa, return error; } - GEN5_DEV_SET_PWR_STATE(cyapa, PWR_MODE_FULL_ACTIVE); + PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_FULL_ACTIVE); } else if (power_mode == PWR_MODE_BTN_ONLY) { error = cyapa_gen5_change_power_state(cyapa, GEN5_POWER_STATE_BTN_ONLY); @@ -1623,19 +1642,19 @@ static int cyapa_gen5_set_power_mode(struct cyapa *cyapa, return error; } - GEN5_DEV_SET_PWR_STATE(cyapa, PWR_MODE_BTN_ONLY); + PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_BTN_ONLY); } else { /* * Continue to change power mode even failed to set * interval time, it won't affect the power mode change. * except the sleep interval time is not correct. */ - if (GEN5_DEV_UNINIT_SLEEP_TIME(cyapa) || - sleep_time != GEN5_DEV_GET_SLEEP_TIME(cyapa)) + if (PIP_DEV_UNINIT_SLEEP_TIME(cyapa) || + sleep_time != PIP_DEV_GET_SLEEP_TIME(cyapa)) if (cyapa_gen5_set_interval_time(cyapa, GEN5_PARAMETER_LP_INTRVL_ID, sleep_time) == 0) - GEN5_DEV_SET_SLEEP_TIME(cyapa, sleep_time); + PIP_DEV_SET_SLEEP_TIME(cyapa, sleep_time); if (sleep_time <= GEN5_POWER_READY_MAX_INTRVL_TIME) power_state = GEN5_POWER_STATE_READY; @@ -1658,17 +1677,17 @@ static int cyapa_gen5_set_power_mode(struct cyapa *cyapa, * is suspending which may cause interrupt line unable to be * asserted again. */ - cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL); - cyapa_gen5_disable_pip_report(cyapa); + if (is_suspend) + cyapa_gen5_disable_pip_report(cyapa); - GEN5_DEV_SET_PWR_STATE(cyapa, + PIP_DEV_SET_PWR_STATE(cyapa, cyapa_sleep_time_to_pwr_cmd(sleep_time)); } return 0; } -static int cyapa_gen5_resume_scanning(struct cyapa *cyapa) +int cyapa_pip_resume_scanning(struct cyapa *cyapa) { u8 cmd[] = { 0x04, 0x00, 0x05, 0x00, 0x2f, 0x00, 0x04 }; u8 resp_data[6]; @@ -1682,7 +1701,7 @@ static int cyapa_gen5_resume_scanning(struct cyapa *cyapa) error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd), resp_data, &resp_len, - 500, cyapa_gen5_sort_tsg_pip_app_resp_data, true); + 500, cyapa_sort_tsg_pip_app_resp_data, true); if (error || !VALID_CMD_RESP_HEADER(resp_data, 0x04)) return -EINVAL; @@ -1692,7 +1711,7 @@ static int cyapa_gen5_resume_scanning(struct cyapa *cyapa) return 0; } -static int cyapa_gen5_suspend_scanning(struct cyapa *cyapa) +int cyapa_pip_suspend_scanning(struct cyapa *cyapa) { u8 cmd[] = { 0x04, 0x00, 0x05, 0x00, 0x2f, 0x00, 0x03 }; u8 resp_data[6]; @@ -1706,7 +1725,7 @@ static int cyapa_gen5_suspend_scanning(struct cyapa *cyapa) error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd), resp_data, &resp_len, - 500, cyapa_gen5_sort_tsg_pip_app_resp_data, true); + 500, cyapa_sort_tsg_pip_app_resp_data, true); if (error || !VALID_CMD_RESP_HEADER(resp_data, 0x03)) return -EINVAL; @@ -1716,10 +1735,10 @@ static int cyapa_gen5_suspend_scanning(struct cyapa *cyapa) return 0; } -static int cyapa_gen5_calibrate_pwcs(struct cyapa *cyapa, +static int cyapa_pip_calibrate_pwcs(struct cyapa *cyapa, u8 calibrate_sensing_mode_type) { - struct gen5_app_cmd_head *app_cmd_head; + struct pip_app_cmd_head *app_cmd_head; u8 cmd[8]; u8 resp_data[6]; int resp_len; @@ -1729,25 +1748,25 @@ static int cyapa_gen5_calibrate_pwcs(struct cyapa *cyapa, cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL); memset(cmd, 0, sizeof(cmd)); - app_cmd_head = (struct gen5_app_cmd_head *)cmd; - put_unaligned_le16(GEN5_OUTPUT_REPORT_ADDR, &app_cmd_head->addr); + app_cmd_head = (struct pip_app_cmd_head *)cmd; + put_unaligned_le16(PIP_OUTPUT_REPORT_ADDR, &app_cmd_head->addr); put_unaligned_le16(sizeof(cmd) - 2, &app_cmd_head->length); - app_cmd_head->report_id = GEN5_APP_CMD_REPORT_ID; - app_cmd_head->cmd_code = GEN5_CMD_CALIBRATE; + app_cmd_head->report_id = PIP_APP_CMD_REPORT_ID; + app_cmd_head->cmd_code = PIP_CMD_CALIBRATE; app_cmd_head->parameter_data[0] = calibrate_sensing_mode_type; resp_len = sizeof(resp_data); error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd), resp_data, &resp_len, - 5000, cyapa_gen5_sort_tsg_pip_app_resp_data, true); - if (error || !VALID_CMD_RESP_HEADER(resp_data, GEN5_CMD_CALIBRATE) || - !GEN5_CMD_COMPLETE_SUCCESS(resp_data[5])) + 5000, cyapa_sort_tsg_pip_app_resp_data, true); + if (error || !VALID_CMD_RESP_HEADER(resp_data, PIP_CMD_CALIBRATE) || + !PIP_CMD_COMPLETE_SUCCESS(resp_data)) return error < 0 ? error : -EAGAIN; return 0; } -static ssize_t cyapa_gen5_do_calibrate(struct device *dev, +ssize_t cyapa_pip_do_calibrate(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -1755,25 +1774,25 @@ static ssize_t cyapa_gen5_do_calibrate(struct device *dev, int error, calibrate_error; /* 1. Suspend Scanning*/ - error = cyapa_gen5_suspend_scanning(cyapa); + error = cyapa_pip_suspend_scanning(cyapa); if (error) return error; /* 2. Do mutual capacitance fine calibrate. */ - calibrate_error = cyapa_gen5_calibrate_pwcs(cyapa, - CYAPA_SENSING_MODE_MUTUAL_CAP_FINE); + calibrate_error = cyapa_pip_calibrate_pwcs(cyapa, + PIP_SENSING_MODE_MUTUAL_CAP_FINE); if (calibrate_error) goto resume_scanning; /* 3. Do self capacitance calibrate. */ - calibrate_error = cyapa_gen5_calibrate_pwcs(cyapa, - CYAPA_SENSING_MODE_SELF_CAP); + calibrate_error = cyapa_pip_calibrate_pwcs(cyapa, + PIP_SENSING_MODE_SELF_CAP); if (calibrate_error) goto resume_scanning; resume_scanning: /* 4. Resume Scanning*/ - error = cyapa_gen5_resume_scanning(cyapa); + error = cyapa_pip_resume_scanning(cyapa); if (error || calibrate_error) return error ? error : calibrate_error; @@ -1856,7 +1875,7 @@ static void cyapa_gen5_guess_electrodes(struct cyapa *cyapa, * If the input value of @data_size is not 0, than means read the mutual or * self local PWC data. The @idac_max, @idac_min and @idac_ave are used to * return the max, min and average value of the mutual or self local PWC data. - * Note, in order to raed mutual local PWC data, must read invoke this function + * Note, in order to read mutual local PWC data, must read invoke this function * to read the mutual global idac data firstly to set the correct Rx number * value, otherwise, the read mutual idac and PWC data may not correct. */ @@ -1864,7 +1883,7 @@ static int cyapa_gen5_read_idac_data(struct cyapa *cyapa, u8 cmd_code, u8 idac_data_type, int *data_size, int *idac_max, int *idac_min, int *idac_ave) { - struct gen5_app_cmd_head *cmd_head; + struct pip_app_cmd_head *cmd_head; u8 cmd[12]; u8 resp_data[256]; int resp_len; @@ -1879,7 +1898,7 @@ static int cyapa_gen5_read_idac_data(struct cyapa *cyapa, int i; int error; - if (cmd_code != GEN5_CMD_RETRIEVE_DATA_STRUCTURE || + if (cmd_code != PIP_RETRIEVE_DATA_STRUCTURE || (idac_data_type != GEN5_RETRIEVE_MUTUAL_PWC_DATA && idac_data_type != GEN5_RETRIEVE_SELF_CAP_PWC_DATA) || !data_size || !idac_max || !idac_min || !idac_ave) @@ -1935,10 +1954,10 @@ static int cyapa_gen5_read_idac_data(struct cyapa *cyapa, } memset(cmd, 0, sizeof(cmd)); - cmd_head = (struct gen5_app_cmd_head *)cmd; - put_unaligned_le16(GEN5_OUTPUT_REPORT_ADDR, &cmd_head->addr); + cmd_head = (struct pip_app_cmd_head *)cmd; + put_unaligned_le16(PIP_OUTPUT_REPORT_ADDR, &cmd_head->addr); put_unaligned_le16(sizeof(cmd) - 2, &cmd_head->length); - cmd_head->report_id = GEN5_APP_CMD_REPORT_ID; + cmd_head->report_id = PIP_APP_CMD_REPORT_ID; cmd_head->cmd_code = cmd_code; do { read_elements = (256 - GEN5_RESP_DATA_STRUCTURE_OFFSET) / @@ -1953,11 +1972,11 @@ static int cyapa_gen5_read_idac_data(struct cyapa *cyapa, error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd), resp_data, &resp_len, - 500, cyapa_gen5_sort_tsg_pip_app_resp_data, + 500, cyapa_sort_tsg_pip_app_resp_data, true); if (error || resp_len < GEN5_RESP_DATA_STRUCTURE_OFFSET || !VALID_CMD_RESP_HEADER(resp_data, cmd_code) || - !GEN5_CMD_COMPLETE_SUCCESS(resp_data[5]) || + !PIP_CMD_COMPLETE_SUCCESS(resp_data) || resp_data[6] != idac_data_type) return (error < 0) ? error : -EAGAIN; read_len = get_unaligned_le16(&resp_data[7]); @@ -1997,7 +2016,7 @@ static int cyapa_gen5_read_idac_data(struct cyapa *cyapa, tmp_count < cyapa->aligned_electrodes_rx && read_global_idac) { /* - * The value gap betwen global and local mutual + * The value gap between global and local mutual * idac data must bigger than 50%. * Normally, global value bigger than 50, * local values less than 10. @@ -2061,7 +2080,7 @@ static int cyapa_gen5_read_mutual_idac_data(struct cyapa *cyapa, data_size = 0; error = cyapa_gen5_read_idac_data(cyapa, - GEN5_CMD_RETRIEVE_DATA_STRUCTURE, + PIP_RETRIEVE_DATA_STRUCTURE, GEN5_RETRIEVE_MUTUAL_PWC_DATA, &data_size, gidac_mutual_max, gidac_mutual_min, gidac_mutual_ave); @@ -2069,7 +2088,7 @@ static int cyapa_gen5_read_mutual_idac_data(struct cyapa *cyapa, return error; error = cyapa_gen5_read_idac_data(cyapa, - GEN5_CMD_RETRIEVE_DATA_STRUCTURE, + PIP_RETRIEVE_DATA_STRUCTURE, GEN5_RETRIEVE_MUTUAL_PWC_DATA, &data_size, lidac_mutual_max, lidac_mutual_min, lidac_mutual_ave); @@ -2088,7 +2107,7 @@ static int cyapa_gen5_read_self_idac_data(struct cyapa *cyapa, data_size = 0; error = cyapa_gen5_read_idac_data(cyapa, - GEN5_CMD_RETRIEVE_DATA_STRUCTURE, + PIP_RETRIEVE_DATA_STRUCTURE, GEN5_RETRIEVE_SELF_CAP_PWC_DATA, &data_size, lidac_self_max, lidac_self_min, lidac_self_ave); @@ -2098,7 +2117,7 @@ static int cyapa_gen5_read_self_idac_data(struct cyapa *cyapa, *gidac_self_tx = *lidac_self_min; error = cyapa_gen5_read_idac_data(cyapa, - GEN5_CMD_RETRIEVE_DATA_STRUCTURE, + PIP_RETRIEVE_DATA_STRUCTURE, GEN5_RETRIEVE_SELF_CAP_PWC_DATA, &data_size, lidac_self_max, lidac_self_min, lidac_self_ave); @@ -2107,27 +2126,27 @@ static int cyapa_gen5_read_self_idac_data(struct cyapa *cyapa, static ssize_t cyapa_gen5_execute_panel_scan(struct cyapa *cyapa) { - struct gen5_app_cmd_head *app_cmd_head; + struct pip_app_cmd_head *app_cmd_head; u8 cmd[7]; u8 resp_data[6]; int resp_len; int error; memset(cmd, 0, sizeof(cmd)); - app_cmd_head = (struct gen5_app_cmd_head *)cmd; - put_unaligned_le16(GEN5_OUTPUT_REPORT_ADDR, &app_cmd_head->addr); + app_cmd_head = (struct pip_app_cmd_head *)cmd; + put_unaligned_le16(PIP_OUTPUT_REPORT_ADDR, &app_cmd_head->addr); put_unaligned_le16(sizeof(cmd) - 2, &app_cmd_head->length); - app_cmd_head->report_id = GEN5_APP_CMD_REPORT_ID; + app_cmd_head->report_id = PIP_APP_CMD_REPORT_ID; app_cmd_head->cmd_code = GEN5_CMD_EXECUTE_PANEL_SCAN; resp_len = sizeof(resp_data); error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd), resp_data, &resp_len, - 500, cyapa_gen5_sort_tsg_pip_app_resp_data, true); + 500, cyapa_sort_tsg_pip_app_resp_data, true); if (error || resp_len != sizeof(resp_data) || !VALID_CMD_RESP_HEADER(resp_data, GEN5_CMD_EXECUTE_PANEL_SCAN) || - !GEN5_CMD_COMPLETE_SUCCESS(resp_data[5])) + !PIP_CMD_COMPLETE_SUCCESS(resp_data)) return error ? error : -EAGAIN; return 0; @@ -2138,7 +2157,7 @@ static int cyapa_gen5_read_panel_scan_raw_data(struct cyapa *cyapa, int *raw_data_max, int *raw_data_min, int *raw_data_ave, u8 *buffer) { - struct gen5_app_cmd_head *app_cmd_head; + struct pip_app_cmd_head *app_cmd_head; struct gen5_retrieve_panel_scan_data *panel_sacn_data; u8 cmd[12]; u8 resp_data[256]; /* Max bytes can transfer one time. */ @@ -2166,10 +2185,10 @@ static int cyapa_gen5_read_panel_scan_raw_data(struct cyapa *cyapa, /* Assume max element size is 4 currently. */ read_elements = (256 - GEN5_RESP_DATA_STRUCTURE_OFFSET) / 4; read_len = read_elements * 4; - app_cmd_head = (struct gen5_app_cmd_head *)cmd; - put_unaligned_le16(GEN5_OUTPUT_REPORT_ADDR, &app_cmd_head->addr); + app_cmd_head = (struct pip_app_cmd_head *)cmd; + put_unaligned_le16(PIP_OUTPUT_REPORT_ADDR, &app_cmd_head->addr); put_unaligned_le16(sizeof(cmd) - 2, &app_cmd_head->length); - app_cmd_head->report_id = GEN5_APP_CMD_REPORT_ID; + app_cmd_head->report_id = PIP_APP_CMD_REPORT_ID; app_cmd_head->cmd_code = cmd_code; panel_sacn_data = (struct gen5_retrieve_panel_scan_data *) app_cmd_head->parameter_data; @@ -2183,10 +2202,10 @@ static int cyapa_gen5_read_panel_scan_raw_data(struct cyapa *cyapa, error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd), resp_data, &resp_len, - 500, cyapa_gen5_sort_tsg_pip_app_resp_data, true); + 500, cyapa_sort_tsg_pip_app_resp_data, true); if (error || resp_len < GEN5_RESP_DATA_STRUCTURE_OFFSET || !VALID_CMD_RESP_HEADER(resp_data, cmd_code) || - !GEN5_CMD_COMPLETE_SUCCESS(resp_data[5]) || + !PIP_CMD_COMPLETE_SUCCESS(resp_data) || resp_data[6] != raw_data_type) return error ? error : -EAGAIN; @@ -2245,11 +2264,11 @@ static ssize_t cyapa_gen5_show_baseline(struct device *dev, int error, resume_error; int size; - if (cyapa->state != CYAPA_STATE_GEN5_APP) + if (!cyapa_is_pip_app_mode(cyapa)) return -EBUSY; /* 1. Suspend Scanning*/ - error = cyapa_gen5_suspend_scanning(cyapa); + error = cyapa_pip_suspend_scanning(cyapa); if (error) return error; @@ -2270,7 +2289,7 @@ static ssize_t cyapa_gen5_show_baseline(struct device *dev, if (error) goto resume_scanning; - /* 4. Execuate panel scan. It must be executed before read data. */ + /* 4. Execute panel scan. It must be executed before read data. */ error = cyapa_gen5_execute_panel_scan(cyapa); if (error) goto resume_scanning; @@ -2343,7 +2362,7 @@ static ssize_t cyapa_gen5_show_baseline(struct device *dev, resume_scanning: /* 11. Resume Scanning*/ - resume_error = cyapa_gen5_resume_scanning(cyapa); + resume_error = cyapa_pip_resume_scanning(cyapa); if (resume_error || error) return resume_error ? resume_error : error; @@ -2364,7 +2383,7 @@ resume_scanning: return size; } -static bool cyapa_gen5_sort_system_info_data(struct cyapa *cyapa, +bool cyapa_pip_sort_system_info_data(struct cyapa *cyapa, u8 *buf, int len) { /* Check the report id and command code */ @@ -2376,20 +2395,17 @@ static bool cyapa_gen5_sort_system_info_data(struct cyapa *cyapa, static int cyapa_gen5_bl_query_data(struct cyapa *cyapa) { - u8 bl_query_data_cmd[] = { 0x04, 0x00, 0x0b, 0x00, 0x40, 0x00, - 0x01, 0x3c, 0x00, 0x00, 0xb0, 0x42, 0x17 - }; - u8 resp_data[GEN5_BL_READ_APP_INFO_RESP_LEN]; + u8 resp_data[PIP_BL_APP_INFO_RESP_LENGTH]; int resp_len; int error; - resp_len = GEN5_BL_READ_APP_INFO_RESP_LEN; + resp_len = sizeof(resp_data); error = cyapa_i2c_pip_cmd_irq_sync(cyapa, - bl_query_data_cmd, sizeof(bl_query_data_cmd), + pip_bl_read_app_info, PIP_BL_READ_APP_INFO_CMD_LENGTH, resp_data, &resp_len, - 500, cyapa_gen5_sort_tsg_pip_bl_resp_data, false); - if (error || resp_len != GEN5_BL_READ_APP_INFO_RESP_LEN || - !GEN5_CMD_COMPLETE_SUCCESS(resp_data[5])) + 500, cyapa_sort_tsg_pip_bl_resp_data, false); + if (error || resp_len < PIP_BL_APP_INFO_RESP_LENGTH || + !PIP_CMD_COMPLETE_SUCCESS(resp_data)) return error ? error : -EIO; memcpy(&cyapa->product_id[0], &resp_data[8], 5); @@ -2402,34 +2418,42 @@ static int cyapa_gen5_bl_query_data(struct cyapa *cyapa) cyapa->fw_maj_ver = resp_data[22]; cyapa->fw_min_ver = resp_data[23]; + cyapa->platform_ver = (resp_data[26] >> PIP_BL_PLATFORM_VER_SHIFT) & + PIP_BL_PLATFORM_VER_MASK; + return 0; } static int cyapa_gen5_get_query_data(struct cyapa *cyapa) { - u8 get_system_information[] = { - 0x04, 0x00, 0x05, 0x00, 0x2f, 0x00, 0x02 - }; - u8 resp_data[71]; + u8 resp_data[PIP_READ_SYS_INFO_RESP_LENGTH]; int resp_len; u16 product_family; int error; resp_len = sizeof(resp_data); error = cyapa_i2c_pip_cmd_irq_sync(cyapa, - get_system_information, sizeof(get_system_information), + pip_read_sys_info, PIP_READ_SYS_INFO_CMD_LENGTH, resp_data, &resp_len, - 2000, cyapa_gen5_sort_system_info_data, false); + 2000, cyapa_pip_sort_system_info_data, false); if (error || resp_len < sizeof(resp_data)) return error ? error : -EIO; product_family = get_unaligned_le16(&resp_data[7]); - if ((product_family & GEN5_PRODUCT_FAMILY_MASK) != - GEN5_PRODUCT_FAMILY_TRACKPAD) + if ((product_family & PIP_PRODUCT_FAMILY_MASK) != + PIP_PRODUCT_FAMILY_TRACKPAD) return -EINVAL; - cyapa->fw_maj_ver = resp_data[15]; - cyapa->fw_min_ver = resp_data[16]; + cyapa->platform_ver = (resp_data[49] >> PIP_BL_PLATFORM_VER_SHIFT) & + PIP_BL_PLATFORM_VER_MASK; + if (cyapa->gen == CYAPA_GEN5 && cyapa->platform_ver < 2) { + /* Gen5 firmware that does not support proximity. */ + cyapa->fw_maj_ver = resp_data[15]; + cyapa->fw_min_ver = resp_data[16]; + } else { + cyapa->fw_maj_ver = resp_data[9]; + cyapa->fw_min_ver = resp_data[10]; + } cyapa->electrodes_x = resp_data[52]; cyapa->electrodes_y = resp_data[53]; @@ -2472,9 +2496,9 @@ static int cyapa_gen5_do_operational_check(struct cyapa *cyapa) switch (cyapa->state) { case CYAPA_STATE_GEN5_BL: - error = cyapa_gen5_bl_exit(cyapa); + error = cyapa_pip_bl_exit(cyapa); if (error) { - /* Rry to update trackpad product information. */ + /* Try to update trackpad product information. */ cyapa_gen5_bl_query_data(cyapa); goto out; } @@ -2486,14 +2510,23 @@ static int cyapa_gen5_do_operational_check(struct cyapa *cyapa) * If trackpad device in deep sleep mode, * the app command will fail. * So always try to reset trackpad device to full active when - * the device state is requeried. + * the device state is required. */ error = cyapa_gen5_set_power_mode(cyapa, - PWR_MODE_FULL_ACTIVE, 0); + PWR_MODE_FULL_ACTIVE, 0, false); if (error) dev_warn(dev, "%s: failed to set power active mode.\n", __func__); + /* By default, the trackpad proximity function is enabled. */ + if (cyapa->platform_ver >= 2) { + error = cyapa_pip_set_proximity(cyapa, true); + if (error) + dev_warn(dev, + "%s: failed to enable proximity.\n", + __func__); + } + /* Get trackpad product information. */ error = cyapa_gen5_get_query_data(cyapa); if (error) @@ -2518,14 +2551,14 @@ out: * Return false, do not continue process * Return true, continue process. */ -static bool cyapa_gen5_irq_cmd_handler(struct cyapa *cyapa) +bool cyapa_pip_irq_cmd_handler(struct cyapa *cyapa) { - struct cyapa_gen5_cmd_states *gen5_pip = &cyapa->cmd_states.gen5; + struct cyapa_pip_cmd_states *pip = &cyapa->cmd_states.pip; int length; - if (atomic_read(&gen5_pip->cmd_issued)) { + if (atomic_read(&pip->cmd_issued)) { /* Polling command response data. */ - if (gen5_pip->is_irq_mode == false) + if (pip->is_irq_mode == false) return false; /* @@ -2533,59 +2566,64 @@ static bool cyapa_gen5_irq_cmd_handler(struct cyapa *cyapa) * these output data may caused by user put finger on * trackpad when host waiting the command response. */ - cyapa_i2c_pip_read(cyapa, gen5_pip->irq_cmd_buf, - GEN5_RESP_LENGTH_SIZE); - length = get_unaligned_le16(gen5_pip->irq_cmd_buf); - length = (length <= GEN5_RESP_LENGTH_SIZE) ? - GEN5_RESP_LENGTH_SIZE : length; - if (length > GEN5_RESP_LENGTH_SIZE) + cyapa_i2c_pip_read(cyapa, pip->irq_cmd_buf, + PIP_RESP_LENGTH_SIZE); + length = get_unaligned_le16(pip->irq_cmd_buf); + length = (length <= PIP_RESP_LENGTH_SIZE) ? + PIP_RESP_LENGTH_SIZE : length; + if (length > PIP_RESP_LENGTH_SIZE) cyapa_i2c_pip_read(cyapa, - gen5_pip->irq_cmd_buf, length); - - if (!(gen5_pip->resp_sort_func && - gen5_pip->resp_sort_func(cyapa, - gen5_pip->irq_cmd_buf, length))) { + pip->irq_cmd_buf, length); + if (!(pip->resp_sort_func && + pip->resp_sort_func(cyapa, + pip->irq_cmd_buf, length))) { /* - * Work around the Gen5 V1 firmware - * that does not assert interrupt signalling - * that command response is ready if user - * keeps touching the trackpad while command - * is sent to the device. + * Cover the Gen5 V1 firmware issue. + * The issue is no interrupt would be asserted from + * trackpad device to host for the command response + * ready event. Because when there was a finger touch + * on trackpad device, and the firmware output queue + * won't be empty (always with touch report data), so + * the interrupt signal won't be asserted again until + * the output queue was previous emptied. + * This issue would happen in the scenario that + * user always has his/her fingers touched on the + * trackpad device during system booting/rebooting. */ length = 0; - if (gen5_pip->resp_len) - length = *gen5_pip->resp_len; + if (pip->resp_len) + length = *pip->resp_len; cyapa_empty_pip_output_data(cyapa, - gen5_pip->resp_data, + pip->resp_data, &length, - gen5_pip->resp_sort_func); - if (gen5_pip->resp_len && length != 0) { - *gen5_pip->resp_len = length; - atomic_dec(&gen5_pip->cmd_issued); - complete(&gen5_pip->cmd_ready); + pip->resp_sort_func); + if (pip->resp_len && length != 0) { + *pip->resp_len = length; + atomic_dec(&pip->cmd_issued); + complete(&pip->cmd_ready); } return false; } - if (gen5_pip->resp_data && gen5_pip->resp_len) { - *gen5_pip->resp_len = (*gen5_pip->resp_len < length) ? - *gen5_pip->resp_len : length; - memcpy(gen5_pip->resp_data, gen5_pip->irq_cmd_buf, - *gen5_pip->resp_len); + if (pip->resp_data && pip->resp_len) { + *pip->resp_len = (*pip->resp_len < length) ? + *pip->resp_len : length; + memcpy(pip->resp_data, pip->irq_cmd_buf, + *pip->resp_len); } - atomic_dec(&gen5_pip->cmd_issued); - complete(&gen5_pip->cmd_ready); + atomic_dec(&pip->cmd_issued); + complete(&pip->cmd_ready); return false; } return true; } -static void cyapa_gen5_report_buttons(struct cyapa *cyapa, - const struct cyapa_gen5_report_data *report_data) +static void cyapa_pip_report_buttons(struct cyapa *cyapa, + const struct cyapa_pip_report_data *report_data) { struct input_dev *input = cyapa->input; - u8 buttons = report_data->report_head[GEN5_BUTTONS_OFFSET]; + u8 buttons = report_data->report_head[PIP_BUTTONS_OFFSET]; buttons = (buttons << CAPABILITY_BTN_SHIFT) & CAPABILITY_BTN_MASK; @@ -2605,12 +2643,23 @@ static void cyapa_gen5_report_buttons(struct cyapa *cyapa, input_sync(input); } -static void cyapa_gen5_report_slot_data(struct cyapa *cyapa, - const struct cyapa_gen5_touch_record *touch) +static void cyapa_pip_report_proximity(struct cyapa *cyapa, + const struct cyapa_pip_report_data *report_data) { struct input_dev *input = cyapa->input; - u8 event_id = GEN5_GET_EVENT_ID(touch->touch_tip_event_id); - int slot = GEN5_GET_TOUCH_ID(touch->touch_tip_event_id); + u8 distance = report_data->report_head[PIP_PROXIMITY_DISTANCE_OFFSET] & + PIP_PROXIMITY_DISTANCE_MASK; + + input_report_abs(input, ABS_DISTANCE, distance); + input_sync(input); +} + +static void cyapa_pip_report_slot_data(struct cyapa *cyapa, + const struct cyapa_pip_touch_record *touch) +{ + struct input_dev *input = cyapa->input; + u8 event_id = PIP_GET_EVENT_ID(touch->touch_tip_event_id); + int slot = PIP_GET_TOUCH_ID(touch->touch_tip_event_id); int x, y; if (event_id == RECORD_EVENT_LIFTOFF) @@ -2621,11 +2670,12 @@ static void cyapa_gen5_report_slot_data(struct cyapa *cyapa, x = (touch->x_hi << 8) | touch->x_lo; if (cyapa->x_origin) x = cyapa->max_abs_x - x; - input_report_abs(input, ABS_MT_POSITION_X, x); y = (touch->y_hi << 8) | touch->y_lo; if (cyapa->y_origin) y = cyapa->max_abs_y - y; + input_report_abs(input, ABS_MT_POSITION_X, x); input_report_abs(input, ABS_MT_POSITION_Y, y); + input_report_abs(input, ABS_DISTANCE, 0); input_report_abs(input, ABS_MT_PRESSURE, touch->z); input_report_abs(input, ABS_MT_TOUCH_MAJOR, @@ -2642,50 +2692,49 @@ static void cyapa_gen5_report_slot_data(struct cyapa *cyapa, touch->orientation); } -static void cyapa_gen5_report_touches(struct cyapa *cyapa, - const struct cyapa_gen5_report_data *report_data) +static void cyapa_pip_report_touches(struct cyapa *cyapa, + const struct cyapa_pip_report_data *report_data) { struct input_dev *input = cyapa->input; unsigned int touch_num; int i; - touch_num = report_data->report_head[GEN5_NUMBER_OF_TOUCH_OFFSET] & - GEN5_NUMBER_OF_TOUCH_MASK; + touch_num = report_data->report_head[PIP_NUMBER_OF_TOUCH_OFFSET] & + PIP_NUMBER_OF_TOUCH_MASK; for (i = 0; i < touch_num; i++) - cyapa_gen5_report_slot_data(cyapa, + cyapa_pip_report_slot_data(cyapa, &report_data->touch_records[i]); input_mt_sync_frame(input); input_sync(input); } -static int cyapa_gen5_irq_handler(struct cyapa *cyapa) +int cyapa_pip_irq_handler(struct cyapa *cyapa) { struct device *dev = &cyapa->client->dev; - struct cyapa_gen5_report_data report_data; - int ret; - u8 report_id; + struct cyapa_pip_report_data report_data; unsigned int report_len; + u8 report_id; + int ret; - if (cyapa->gen != CYAPA_GEN5 || - cyapa->state != CYAPA_STATE_GEN5_APP) { + if (!cyapa_is_pip_app_mode(cyapa)) { dev_err(dev, "invalid device state, gen=%d, state=0x%02x\n", cyapa->gen, cyapa->state); return -EINVAL; } ret = cyapa_i2c_pip_read(cyapa, (u8 *)&report_data, - GEN5_RESP_LENGTH_SIZE); - if (ret != GEN5_RESP_LENGTH_SIZE) { + PIP_RESP_LENGTH_SIZE); + if (ret != PIP_RESP_LENGTH_SIZE) { dev_err(dev, "failed to read length bytes, (%d)\n", ret); return -EINVAL; } report_len = get_unaligned_le16( - &report_data.report_head[GEN5_RESP_LENGTH_OFFSET]); - if (report_len < GEN5_RESP_LENGTH_SIZE) { - /* Invliad length or internal reset happened. */ + &report_data.report_head[PIP_RESP_LENGTH_OFFSET]); + if (report_len < PIP_RESP_LENGTH_SIZE) { + /* Invalid length or internal reset happened. */ dev_err(dev, "invalid report_len=%d. bytes: %02x %02x\n", report_len, report_data.report_head[0], report_data.report_head[1]); @@ -2693,7 +2742,7 @@ static int cyapa_gen5_irq_handler(struct cyapa *cyapa) } /* Idle, no data for report. */ - if (report_len == GEN5_RESP_LENGTH_SIZE) + if (report_len == PIP_RESP_LENGTH_SIZE) return 0; ret = cyapa_i2c_pip_read(cyapa, (u8 *)&report_data, report_len); @@ -2703,70 +2752,92 @@ static int cyapa_gen5_irq_handler(struct cyapa *cyapa) return -EINVAL; } - report_id = report_data.report_head[GEN5_RESP_REPORT_ID_OFFSET]; - if (report_id == GEN5_WAKEUP_EVENT_REPORT_ID && - report_len == GEN5_WAKEUP_EVENT_SIZE) { + report_id = report_data.report_head[PIP_RESP_REPORT_ID_OFFSET]; + if (report_id == PIP_WAKEUP_EVENT_REPORT_ID && + report_len == PIP_WAKEUP_EVENT_SIZE) { /* * Device wake event from deep sleep mode for touch. * This interrupt event is used to wake system up. + * + * Note: + * It will introduce about 20~40 ms additional delay + * time in receiving for first valid touch report data. + * The time is used to execute device runtime resume + * process. */ + pm_runtime_get_sync(dev); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_sync_autosuspend(dev); return 0; - } else if (report_id != GEN5_TOUCH_REPORT_ID && - report_id != GEN5_BTN_REPORT_ID && + } else if (report_id != PIP_TOUCH_REPORT_ID && + report_id != PIP_BTN_REPORT_ID && report_id != GEN5_OLD_PUSH_BTN_REPORT_ID && - report_id != GEN5_PUSH_BTN_REPORT_ID) { + report_id != PIP_PUSH_BTN_REPORT_ID && + report_id != PIP_PROXIMITY_REPORT_ID) { /* Running in BL mode or unknown response data read. */ dev_err(dev, "invalid report_id=0x%02x\n", report_id); return -EINVAL; } - if (report_id == GEN5_TOUCH_REPORT_ID && - (report_len < GEN5_TOUCH_REPORT_HEAD_SIZE || - report_len > GEN5_TOUCH_REPORT_MAX_SIZE)) { + if (report_id == PIP_TOUCH_REPORT_ID && + (report_len < PIP_TOUCH_REPORT_HEAD_SIZE || + report_len > PIP_TOUCH_REPORT_MAX_SIZE)) { /* Invalid report data length for finger packet. */ dev_err(dev, "invalid touch packet length=%d\n", report_len); return 0; } - if ((report_id == GEN5_BTN_REPORT_ID || + if ((report_id == PIP_BTN_REPORT_ID || report_id == GEN5_OLD_PUSH_BTN_REPORT_ID || - report_id == GEN5_PUSH_BTN_REPORT_ID) && - (report_len < GEN5_BTN_REPORT_HEAD_SIZE || - report_len > GEN5_BTN_REPORT_MAX_SIZE)) { + report_id == PIP_PUSH_BTN_REPORT_ID) && + (report_len < PIP_BTN_REPORT_HEAD_SIZE || + report_len > PIP_BTN_REPORT_MAX_SIZE)) { /* Invalid report data length of button packet. */ dev_err(dev, "invalid button packet length=%d\n", report_len); return 0; } - if (report_id == GEN5_TOUCH_REPORT_ID) - cyapa_gen5_report_touches(cyapa, &report_data); + if (report_id == PIP_PROXIMITY_REPORT_ID && + report_len != PIP_PROXIMITY_REPORT_SIZE) { + /* Invalid report data length of proximity packet. */ + dev_err(dev, "invalid proximity data, length=%d\n", report_len); + return 0; + } + + if (report_id == PIP_TOUCH_REPORT_ID) + cyapa_pip_report_touches(cyapa, &report_data); + else if (report_id == PIP_PROXIMITY_REPORT_ID) + cyapa_pip_report_proximity(cyapa, &report_data); else - cyapa_gen5_report_buttons(cyapa, &report_data); + cyapa_pip_report_buttons(cyapa, &report_data); return 0; } -static int cyapa_gen5_bl_activate(struct cyapa *cyapa) { return 0; } -static int cyapa_gen5_bl_deactivate(struct cyapa *cyapa) { return 0; } +int cyapa_pip_bl_activate(struct cyapa *cyapa) { return 0; } +int cyapa_pip_bl_deactivate(struct cyapa *cyapa) { return 0; } + const struct cyapa_dev_ops cyapa_gen5_ops = { - .check_fw = cyapa_gen5_check_fw, - .bl_enter = cyapa_gen5_bl_enter, - .bl_initiate = cyapa_gen5_bl_initiate, - .update_fw = cyapa_gen5_do_fw_update, - .bl_activate = cyapa_gen5_bl_activate, - .bl_deactivate = cyapa_gen5_bl_deactivate, + .check_fw = cyapa_pip_check_fw, + .bl_enter = cyapa_pip_bl_enter, + .bl_initiate = cyapa_pip_bl_initiate, + .update_fw = cyapa_pip_do_fw_update, + .bl_activate = cyapa_pip_bl_activate, + .bl_deactivate = cyapa_pip_bl_deactivate, .show_baseline = cyapa_gen5_show_baseline, - .calibrate_store = cyapa_gen5_do_calibrate, + .calibrate_store = cyapa_pip_do_calibrate, - .initialize = cyapa_gen5_initialize, + .initialize = cyapa_pip_cmd_state_initialize, .state_parse = cyapa_gen5_state_parse, .operational_check = cyapa_gen5_do_operational_check, - .irq_handler = cyapa_gen5_irq_handler, - .irq_cmd_handler = cyapa_gen5_irq_cmd_handler, + .irq_handler = cyapa_pip_irq_handler, + .irq_cmd_handler = cyapa_pip_irq_cmd_handler, .sort_empty_output_data = cyapa_empty_pip_output_data, .set_power_mode = cyapa_gen5_set_power_mode, + + .set_proximity = cyapa_pip_set_proximity, }; diff --git a/drivers/input/mouse/cyapa_gen6.c b/drivers/input/mouse/cyapa_gen6.c new file mode 100644 index 000000000000..5f191071d44a --- /dev/null +++ b/drivers/input/mouse/cyapa_gen6.c @@ -0,0 +1,749 @@ +/* + * Cypress APA trackpad with I2C interface + * + * Author: Dudley Du <dudl@cypress.com> + * + * Copyright (C) 2015 Cypress Semiconductor, Inc. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + */ + +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/input.h> +#include <linux/input/mt.h> +#include <linux/mutex.h> +#include <linux/completion.h> +#include <linux/slab.h> +#include <asm/unaligned.h> +#include <linux/crc-itu-t.h> +#include "cyapa.h" + + +#define GEN6_ENABLE_CMD_IRQ 0x41 +#define GEN6_DISABLE_CMD_IRQ 0x42 +#define GEN6_ENABLE_DEV_IRQ 0x43 +#define GEN6_DISABLE_DEV_IRQ 0x44 + +#define GEN6_POWER_MODE_ACTIVE 0x01 +#define GEN6_POWER_MODE_LP_MODE1 0x02 +#define GEN6_POWER_MODE_LP_MODE2 0x03 +#define GEN6_POWER_MODE_BTN_ONLY 0x04 + +#define GEN6_SET_POWER_MODE_INTERVAL 0x47 +#define GEN6_GET_POWER_MODE_INTERVAL 0x48 + +#define GEN6_MAX_RX_NUM 14 +#define GEN6_RETRIEVE_DATA_ID_RX_ATTENURATOR_IDAC 0x00 +#define GEN6_RETRIEVE_DATA_ID_ATTENURATOR_TRIM 0x12 + + +struct pip_app_cmd_head { + __le16 addr; + __le16 length; + u8 report_id; + u8 resv; /* Reserved, must be 0 */ + u8 cmd_code; /* bit7: resv, set to 0; bit6~0: command code.*/ +} __packed; + +struct pip_app_resp_head { + __le16 length; + u8 report_id; + u8 resv; /* Reserved, must be 0 */ + u8 cmd_code; /* bit7: TGL; bit6~0: command code.*/ + /* + * The value of data_status can be the first byte of data or + * the command status or the unsupported command code depending on the + * requested command code. + */ + u8 data_status; +} __packed; + +struct pip_fixed_info { + u8 silicon_id_high; + u8 silicon_id_low; + u8 family_id; +}; + +static u8 pip_get_bl_info[] = { + 0x04, 0x00, 0x0B, 0x00, 0x40, 0x00, 0x01, 0x38, + 0x00, 0x00, 0x70, 0x9E, 0x17 +}; + +static bool cyapa_sort_pip_hid_descriptor_data(struct cyapa *cyapa, + u8 *buf, int len) +{ + if (len != PIP_HID_DESCRIPTOR_SIZE) + return false; + + if (buf[PIP_RESP_REPORT_ID_OFFSET] == PIP_HID_APP_REPORT_ID || + buf[PIP_RESP_REPORT_ID_OFFSET] == PIP_HID_BL_REPORT_ID) + return true; + + return false; +} + +static int cyapa_get_pip_fixed_info(struct cyapa *cyapa, + struct pip_fixed_info *pip_info, bool is_bootloader) +{ + u8 resp_data[PIP_READ_SYS_INFO_RESP_LENGTH]; + int resp_len; + u16 product_family; + int error; + + if (is_bootloader) { + /* Read Bootloader Information to determine Gen5 or Gen6. */ + resp_len = sizeof(resp_data); + error = cyapa_i2c_pip_cmd_irq_sync(cyapa, + pip_get_bl_info, sizeof(pip_get_bl_info), + resp_data, &resp_len, + 2000, cyapa_sort_tsg_pip_bl_resp_data, + false); + if (error || resp_len < PIP_BL_GET_INFO_RESP_LENGTH) + return error ? error : -EIO; + + pip_info->family_id = resp_data[8]; + pip_info->silicon_id_low = resp_data[10]; + pip_info->silicon_id_high = resp_data[11]; + + return 0; + } + + /* Get App System Information to determine Gen5 or Gen6. */ + resp_len = sizeof(resp_data); + error = cyapa_i2c_pip_cmd_irq_sync(cyapa, + pip_read_sys_info, PIP_READ_SYS_INFO_CMD_LENGTH, + resp_data, &resp_len, + 2000, cyapa_pip_sort_system_info_data, false); + if (error || resp_len < PIP_READ_SYS_INFO_RESP_LENGTH) + return error ? error : -EIO; + + product_family = get_unaligned_le16(&resp_data[7]); + if ((product_family & PIP_PRODUCT_FAMILY_MASK) != + PIP_PRODUCT_FAMILY_TRACKPAD) + return -EINVAL; + + pip_info->family_id = resp_data[19]; + pip_info->silicon_id_low = resp_data[21]; + pip_info->silicon_id_high = resp_data[22]; + + return 0; + +} + +int cyapa_pip_state_parse(struct cyapa *cyapa, u8 *reg_data, int len) +{ + u8 cmd[] = { 0x01, 0x00}; + struct pip_fixed_info pip_info; + u8 resp_data[PIP_HID_DESCRIPTOR_SIZE]; + int resp_len; + bool is_bootloader; + int error; + + cyapa->state = CYAPA_STATE_NO_DEVICE; + + /* Try to wake from it deep sleep state if it is. */ + cyapa_pip_deep_sleep(cyapa, PIP_DEEP_SLEEP_STATE_ON); + + /* Empty the buffer queue to get fresh data with later commands. */ + cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL); + + /* + * Read description info from trackpad device to determine running in + * APP mode or Bootloader mode. + */ + resp_len = PIP_HID_DESCRIPTOR_SIZE; + error = cyapa_i2c_pip_cmd_irq_sync(cyapa, + cmd, sizeof(cmd), + resp_data, &resp_len, + 300, + cyapa_sort_pip_hid_descriptor_data, + false); + if (error) + return error; + + if (resp_data[PIP_RESP_REPORT_ID_OFFSET] == PIP_HID_BL_REPORT_ID) + is_bootloader = true; + else if (resp_data[PIP_RESP_REPORT_ID_OFFSET] == PIP_HID_APP_REPORT_ID) + is_bootloader = false; + else + return -EAGAIN; + + /* Get PIP fixed information to determine Gen5 or Gen6. */ + memset(&pip_info, 0, sizeof(struct pip_fixed_info)); + error = cyapa_get_pip_fixed_info(cyapa, &pip_info, is_bootloader); + if (error) + return error; + + if (pip_info.family_id == 0x9B && pip_info.silicon_id_high == 0x0B) { + cyapa->gen = CYAPA_GEN6; + cyapa->state = is_bootloader ? CYAPA_STATE_GEN6_BL + : CYAPA_STATE_GEN6_APP; + } else if (pip_info.family_id == 0x91 && + pip_info.silicon_id_high == 0x02) { + cyapa->gen = CYAPA_GEN5; + cyapa->state = is_bootloader ? CYAPA_STATE_GEN5_BL + : CYAPA_STATE_GEN5_APP; + } + + return 0; +} + +static int cyapa_gen6_read_sys_info(struct cyapa *cyapa) +{ + u8 resp_data[PIP_READ_SYS_INFO_RESP_LENGTH]; + int resp_len; + u16 product_family; + u8 rotat_align; + int error; + + /* Get App System Information to determine Gen5 or Gen6. */ + resp_len = sizeof(resp_data); + error = cyapa_i2c_pip_cmd_irq_sync(cyapa, + pip_read_sys_info, PIP_READ_SYS_INFO_CMD_LENGTH, + resp_data, &resp_len, + 2000, cyapa_pip_sort_system_info_data, false); + if (error || resp_len < sizeof(resp_data)) + return error ? error : -EIO; + + product_family = get_unaligned_le16(&resp_data[7]); + if ((product_family & PIP_PRODUCT_FAMILY_MASK) != + PIP_PRODUCT_FAMILY_TRACKPAD) + return -EINVAL; + + cyapa->platform_ver = (resp_data[67] >> PIP_BL_PLATFORM_VER_SHIFT) & + PIP_BL_PLATFORM_VER_MASK; + cyapa->fw_maj_ver = resp_data[9]; + cyapa->fw_min_ver = resp_data[10]; + + cyapa->electrodes_x = resp_data[33]; + cyapa->electrodes_y = resp_data[34]; + + cyapa->physical_size_x = get_unaligned_le16(&resp_data[35]) / 100; + cyapa->physical_size_y = get_unaligned_le16(&resp_data[37]) / 100; + + cyapa->max_abs_x = get_unaligned_le16(&resp_data[39]); + cyapa->max_abs_y = get_unaligned_le16(&resp_data[41]); + + cyapa->max_z = get_unaligned_le16(&resp_data[43]); + + cyapa->x_origin = resp_data[45] & 0x01; + cyapa->y_origin = resp_data[46] & 0x01; + + cyapa->btn_capability = (resp_data[70] << 3) & CAPABILITY_BTN_MASK; + + memcpy(&cyapa->product_id[0], &resp_data[51], 5); + cyapa->product_id[5] = '-'; + memcpy(&cyapa->product_id[6], &resp_data[56], 6); + cyapa->product_id[12] = '-'; + memcpy(&cyapa->product_id[13], &resp_data[62], 2); + cyapa->product_id[15] = '\0'; + + rotat_align = resp_data[68]; + if (rotat_align) { + cyapa->electrodes_rx = cyapa->electrodes_y; + cyapa->electrodes_rx = cyapa->electrodes_y; + } else { + cyapa->electrodes_rx = cyapa->electrodes_x; + cyapa->electrodes_rx = cyapa->electrodes_y; + } + cyapa->aligned_electrodes_rx = (cyapa->electrodes_rx + 3) & ~3u; + + if (!cyapa->electrodes_x || !cyapa->electrodes_y || + !cyapa->physical_size_x || !cyapa->physical_size_y || + !cyapa->max_abs_x || !cyapa->max_abs_y || !cyapa->max_z) + return -EINVAL; + + return 0; +} + +static int cyapa_gen6_bl_read_app_info(struct cyapa *cyapa) +{ + u8 resp_data[PIP_BL_APP_INFO_RESP_LENGTH]; + int resp_len; + int error; + + resp_len = sizeof(resp_data); + error = cyapa_i2c_pip_cmd_irq_sync(cyapa, + pip_bl_read_app_info, PIP_BL_READ_APP_INFO_CMD_LENGTH, + resp_data, &resp_len, + 500, cyapa_sort_tsg_pip_bl_resp_data, false); + if (error || resp_len < PIP_BL_APP_INFO_RESP_LENGTH || + !PIP_CMD_COMPLETE_SUCCESS(resp_data)) + return error ? error : -EIO; + + cyapa->fw_maj_ver = resp_data[8]; + cyapa->fw_min_ver = resp_data[9]; + + cyapa->platform_ver = (resp_data[12] >> PIP_BL_PLATFORM_VER_SHIFT) & + PIP_BL_PLATFORM_VER_MASK; + + memcpy(&cyapa->product_id[0], &resp_data[13], 5); + cyapa->product_id[5] = '-'; + memcpy(&cyapa->product_id[6], &resp_data[18], 6); + cyapa->product_id[12] = '-'; + memcpy(&cyapa->product_id[13], &resp_data[24], 2); + cyapa->product_id[15] = '\0'; + + return 0; + +} + +static int cyapa_gen6_config_dev_irq(struct cyapa *cyapa, u8 cmd_code) +{ + u8 cmd[] = { 0x04, 0x00, 0x05, 0x00, 0x2f, 0x00, cmd_code }; + u8 resp_data[6]; + int resp_len; + int error; + + resp_len = sizeof(resp_data); + error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd), + resp_data, &resp_len, + 500, cyapa_sort_tsg_pip_app_resp_data, false); + if (error || !VALID_CMD_RESP_HEADER(resp_data, cmd_code) || + !PIP_CMD_COMPLETE_SUCCESS(resp_data) + ) + return error < 0 ? error : -EINVAL; + + return 0; +} + +static int cyapa_gen6_set_proximity(struct cyapa *cyapa, bool enable) +{ + int error; + + cyapa_gen6_config_dev_irq(cyapa, GEN6_DISABLE_CMD_IRQ); + error = cyapa_pip_set_proximity(cyapa, enable); + cyapa_gen6_config_dev_irq(cyapa, GEN6_ENABLE_CMD_IRQ); + + return error; +} + +static int cyapa_gen6_change_power_state(struct cyapa *cyapa, u8 power_mode) +{ + u8 cmd[] = { 0x04, 0x00, 0x06, 0x00, 0x2f, 0x00, 0x46, power_mode }; + u8 resp_data[6]; + int resp_len; + int error; + + resp_len = sizeof(resp_data); + error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd), + resp_data, &resp_len, + 500, cyapa_sort_tsg_pip_app_resp_data, false); + if (error || !VALID_CMD_RESP_HEADER(resp_data, 0x46)) + return error < 0 ? error : -EINVAL; + + /* New power state applied in device not match the set power state. */ + if (resp_data[5] != power_mode) + return -EAGAIN; + + return 0; +} + +static int cyapa_gen6_set_interval_setting(struct cyapa *cyapa, + struct gen6_interval_setting *interval_setting) +{ + struct gen6_set_interval_cmd { + __le16 addr; + __le16 length; + u8 report_id; + u8 rsvd; /* Reserved, must be 0 */ + u8 cmd_code; + __le16 active_interval; + __le16 lp1_interval; + __le16 lp2_interval; + } __packed set_interval_cmd; + u8 resp_data[11]; + int resp_len; + int error; + + memset(&set_interval_cmd, 0, sizeof(set_interval_cmd)); + put_unaligned_le16(PIP_OUTPUT_REPORT_ADDR, &set_interval_cmd.addr); + put_unaligned_le16(sizeof(set_interval_cmd) - 2, + &set_interval_cmd.length); + set_interval_cmd.report_id = PIP_APP_CMD_REPORT_ID; + set_interval_cmd.cmd_code = GEN6_SET_POWER_MODE_INTERVAL; + put_unaligned_le16(interval_setting->active_interval, + &set_interval_cmd.active_interval); + put_unaligned_le16(interval_setting->lp1_interval, + &set_interval_cmd.lp1_interval); + put_unaligned_le16(interval_setting->lp2_interval, + &set_interval_cmd.lp2_interval); + + resp_len = sizeof(resp_data); + error = cyapa_i2c_pip_cmd_irq_sync(cyapa, + (u8 *)&set_interval_cmd, sizeof(set_interval_cmd), + resp_data, &resp_len, + 500, cyapa_sort_tsg_pip_app_resp_data, false); + if (error || + !VALID_CMD_RESP_HEADER(resp_data, GEN6_SET_POWER_MODE_INTERVAL)) + return error < 0 ? error : -EINVAL; + + /* Get the real set intervals from response. */ + interval_setting->active_interval = get_unaligned_le16(&resp_data[5]); + interval_setting->lp1_interval = get_unaligned_le16(&resp_data[7]); + interval_setting->lp2_interval = get_unaligned_le16(&resp_data[9]); + + return 0; +} + +static int cyapa_gen6_get_interval_setting(struct cyapa *cyapa, + struct gen6_interval_setting *interval_setting) +{ + u8 cmd[] = { 0x04, 0x00, 0x05, 0x00, 0x2f, 0x00, + GEN6_GET_POWER_MODE_INTERVAL }; + u8 resp_data[11]; + int resp_len; + int error; + + resp_len = sizeof(resp_data); + error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd), + resp_data, &resp_len, + 500, cyapa_sort_tsg_pip_app_resp_data, false); + if (error || + !VALID_CMD_RESP_HEADER(resp_data, GEN6_GET_POWER_MODE_INTERVAL)) + return error < 0 ? error : -EINVAL; + + interval_setting->active_interval = get_unaligned_le16(&resp_data[5]); + interval_setting->lp1_interval = get_unaligned_le16(&resp_data[7]); + interval_setting->lp2_interval = get_unaligned_le16(&resp_data[9]); + + return 0; +} + +static int cyapa_gen6_deep_sleep(struct cyapa *cyapa, u8 state) +{ + u8 ping[] = { 0x04, 0x00, 0x05, 0x00, 0x2f, 0x00, 0x00 }; + + if (state == PIP_DEEP_SLEEP_STATE_ON) + /* + * Send ping command to notify device prepare for wake up + * when it's in deep sleep mode. At this time, device will + * response nothing except an I2C NAK. + */ + cyapa_i2c_pip_write(cyapa, ping, sizeof(ping)); + + return cyapa_pip_deep_sleep(cyapa, state); +} + +static int cyapa_gen6_set_power_mode(struct cyapa *cyapa, + u8 power_mode, u16 sleep_time, bool is_suspend) +{ + struct device *dev = &cyapa->client->dev; + struct gen6_interval_setting *interval_setting = + &cyapa->gen6_interval_setting; + u8 lp_mode; + int error; + + if (cyapa->state != CYAPA_STATE_GEN6_APP) + return 0; + + if (PIP_DEV_GET_PWR_STATE(cyapa) == UNINIT_PWR_MODE) { + /* + * Assume TP in deep sleep mode when driver is loaded, + * avoid driver unload and reload command IO issue caused by TP + * has been set into deep sleep mode when unloading. + */ + PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_OFF); + } + + if (PIP_DEV_UNINIT_SLEEP_TIME(cyapa) && + PIP_DEV_GET_PWR_STATE(cyapa) != PWR_MODE_OFF) + PIP_DEV_SET_SLEEP_TIME(cyapa, UNINIT_SLEEP_TIME); + + if (PIP_DEV_GET_PWR_STATE(cyapa) == power_mode) { + if (power_mode == PWR_MODE_OFF || + power_mode == PWR_MODE_FULL_ACTIVE || + power_mode == PWR_MODE_BTN_ONLY || + PIP_DEV_GET_SLEEP_TIME(cyapa) == sleep_time) { + /* Has in correct power mode state, early return. */ + return 0; + } + } + + if (power_mode == PWR_MODE_OFF) { + cyapa_gen6_config_dev_irq(cyapa, GEN6_DISABLE_CMD_IRQ); + + error = cyapa_gen6_deep_sleep(cyapa, PIP_DEEP_SLEEP_STATE_OFF); + if (error) { + dev_err(dev, "enter deep sleep fail: %d\n", error); + return error; + } + + PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_OFF); + return 0; + } + + /* + * When trackpad in power off mode, it cannot change to other power + * state directly, must be wake up from sleep firstly, then + * continue to do next power sate change. + */ + if (PIP_DEV_GET_PWR_STATE(cyapa) == PWR_MODE_OFF) { + error = cyapa_gen6_deep_sleep(cyapa, PIP_DEEP_SLEEP_STATE_ON); + if (error) { + dev_err(dev, "deep sleep wake fail: %d\n", error); + return error; + } + } + + /* + * Disable device assert interrupts for command response to avoid + * disturbing system suspending or hibernating process. + */ + cyapa_gen6_config_dev_irq(cyapa, GEN6_DISABLE_CMD_IRQ); + + if (power_mode == PWR_MODE_FULL_ACTIVE) { + error = cyapa_gen6_change_power_state(cyapa, + GEN6_POWER_MODE_ACTIVE); + if (error) { + dev_err(dev, "change to active fail: %d\n", error); + goto out; + } + + PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_FULL_ACTIVE); + + /* Sync the interval setting from device. */ + cyapa_gen6_get_interval_setting(cyapa, interval_setting); + + } else if (power_mode == PWR_MODE_BTN_ONLY) { + error = cyapa_gen6_change_power_state(cyapa, + GEN6_POWER_MODE_BTN_ONLY); + if (error) { + dev_err(dev, "fail to button only mode: %d\n", error); + goto out; + } + + PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_BTN_ONLY); + } else { + /* + * Gen6 internally supports to 2 low power scan interval time, + * so can help to switch power mode quickly. + * such as runtime suspend and system suspend. + */ + if (interval_setting->lp1_interval == sleep_time) { + lp_mode = GEN6_POWER_MODE_LP_MODE1; + } else if (interval_setting->lp2_interval == sleep_time) { + lp_mode = GEN6_POWER_MODE_LP_MODE2; + } else { + if (interval_setting->lp1_interval == 0) { + interval_setting->lp1_interval = sleep_time; + lp_mode = GEN6_POWER_MODE_LP_MODE1; + } else { + interval_setting->lp2_interval = sleep_time; + lp_mode = GEN6_POWER_MODE_LP_MODE2; + } + cyapa_gen6_set_interval_setting(cyapa, + interval_setting); + } + + error = cyapa_gen6_change_power_state(cyapa, lp_mode); + if (error) { + dev_err(dev, "set power state to 0x%02x failed: %d\n", + lp_mode, error); + goto out; + } + + PIP_DEV_SET_SLEEP_TIME(cyapa, sleep_time); + PIP_DEV_SET_PWR_STATE(cyapa, + cyapa_sleep_time_to_pwr_cmd(sleep_time)); + } + +out: + cyapa_gen6_config_dev_irq(cyapa, GEN6_ENABLE_CMD_IRQ); + return error; +} + +static int cyapa_gen6_initialize(struct cyapa *cyapa) +{ + return 0; +} + +static int cyapa_pip_retrieve_data_structure(struct cyapa *cyapa, + u16 read_offset, u16 read_len, u8 data_id, + u8 *data, int *data_buf_lens) +{ + struct retrieve_data_struct_cmd { + struct pip_app_cmd_head head; + __le16 read_offset; + __le16 read_length; + u8 data_id; + } __packed cmd; + u8 resp_data[GEN6_MAX_RX_NUM + 10]; + int resp_len; + int error; + + memset(&cmd, 0, sizeof(cmd)); + put_unaligned_le16(PIP_OUTPUT_REPORT_ADDR, &cmd.head.addr); + put_unaligned_le16(sizeof(cmd), &cmd.head.length - 2); + cmd.head.report_id = PIP_APP_CMD_REPORT_ID; + cmd.head.cmd_code = PIP_RETRIEVE_DATA_STRUCTURE; + put_unaligned_le16(read_offset, &cmd.read_offset); + put_unaligned_le16(read_len, &cmd.read_length); + cmd.data_id = data_id; + + resp_len = sizeof(resp_data); + error = cyapa_i2c_pip_cmd_irq_sync(cyapa, + (u8 *)&cmd, sizeof(cmd), + resp_data, &resp_len, + 500, cyapa_sort_tsg_pip_app_resp_data, + true); + if (error || !PIP_CMD_COMPLETE_SUCCESS(resp_data) || + resp_data[6] != data_id || + !VALID_CMD_RESP_HEADER(resp_data, PIP_RETRIEVE_DATA_STRUCTURE)) + return (error < 0) ? error : -EAGAIN; + + read_len = get_unaligned_le16(&resp_data[7]); + if (*data_buf_lens < read_len) { + *data_buf_lens = read_len; + return -ENOBUFS; + } + + memcpy(data, &resp_data[10], read_len); + *data_buf_lens = read_len; + return 0; +} + +static ssize_t cyapa_gen6_show_baseline(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cyapa *cyapa = dev_get_drvdata(dev); + u8 data[GEN6_MAX_RX_NUM]; + int data_len; + int size = 0; + int i; + int error; + int resume_error; + + if (!cyapa_is_pip_app_mode(cyapa)) + return -EBUSY; + + /* 1. Suspend Scanning*/ + error = cyapa_pip_suspend_scanning(cyapa); + if (error) + return error; + + /* 2. IDAC and RX Attenuator Calibration Data (Center Frequency). */ + data_len = sizeof(data); + error = cyapa_pip_retrieve_data_structure(cyapa, 0, data_len, + GEN6_RETRIEVE_DATA_ID_RX_ATTENURATOR_IDAC, + data, &data_len); + if (error) + goto resume_scanning; + + size = scnprintf(buf, PAGE_SIZE, "%d %d %d %d %d %d ", + data[0], /* RX Attenuator Mutual */ + data[1], /* IDAC Mutual */ + data[2], /* RX Attenuator Self RX */ + data[3], /* IDAC Self RX */ + data[4], /* RX Attenuator Self TX */ + data[5] /* IDAC Self TX */ + ); + + /* 3. Read Attenuator Trim. */ + data_len = sizeof(data); + error = cyapa_pip_retrieve_data_structure(cyapa, 0, data_len, + GEN6_RETRIEVE_DATA_ID_ATTENURATOR_TRIM, + data, &data_len); + if (error) + goto resume_scanning; + + /* set attenuator trim values. */ + for (i = 0; i < data_len; i++) + size += scnprintf(buf + size, PAGE_SIZE - size, "%d ", data[i]); + size += scnprintf(buf + size, PAGE_SIZE - size, "\n"); + +resume_scanning: + /* 4. Resume Scanning*/ + resume_error = cyapa_pip_resume_scanning(cyapa); + if (resume_error || error) { + memset(buf, 0, PAGE_SIZE); + return resume_error ? resume_error : error; + } + + return size; +} + +static int cyapa_gen6_operational_check(struct cyapa *cyapa) +{ + struct device *dev = &cyapa->client->dev; + int error; + + if (cyapa->gen != CYAPA_GEN6) + return -ENODEV; + + switch (cyapa->state) { + case CYAPA_STATE_GEN6_BL: + error = cyapa_pip_bl_exit(cyapa); + if (error) { + /* Try to update trackpad product information. */ + cyapa_gen6_bl_read_app_info(cyapa); + goto out; + } + + cyapa->state = CYAPA_STATE_GEN6_APP; + + case CYAPA_STATE_GEN6_APP: + /* + * If trackpad device in deep sleep mode, + * the app command will fail. + * So always try to reset trackpad device to full active when + * the device state is required. + */ + error = cyapa_gen6_set_power_mode(cyapa, + PWR_MODE_FULL_ACTIVE, 0, false); + if (error) + dev_warn(dev, "%s: failed to set power active mode.\n", + __func__); + + /* By default, the trackpad proximity function is enabled. */ + error = cyapa_pip_set_proximity(cyapa, true); + if (error) + dev_warn(dev, "%s: failed to enable proximity.\n", + __func__); + + /* Get trackpad product information. */ + error = cyapa_gen6_read_sys_info(cyapa); + if (error) + goto out; + /* Only support product ID starting with CYTRA */ + if (memcmp(cyapa->product_id, product_id, + strlen(product_id)) != 0) { + dev_err(dev, "%s: unknown product ID (%s)\n", + __func__, cyapa->product_id); + error = -EINVAL; + } + break; + default: + error = -EINVAL; + } + +out: + return error; +} + +const struct cyapa_dev_ops cyapa_gen6_ops = { + .check_fw = cyapa_pip_check_fw, + .bl_enter = cyapa_pip_bl_enter, + .bl_initiate = cyapa_pip_bl_initiate, + .update_fw = cyapa_pip_do_fw_update, + .bl_activate = cyapa_pip_bl_activate, + .bl_deactivate = cyapa_pip_bl_deactivate, + + .show_baseline = cyapa_gen6_show_baseline, + .calibrate_store = cyapa_pip_do_calibrate, + + .initialize = cyapa_gen6_initialize, + + .state_parse = cyapa_pip_state_parse, + .operational_check = cyapa_gen6_operational_check, + + .irq_handler = cyapa_pip_irq_handler, + .irq_cmd_handler = cyapa_pip_irq_cmd_handler, + .sort_empty_output_data = cyapa_empty_pip_output_data, + .set_power_mode = cyapa_gen6_set_power_mode, + + .set_proximity = cyapa_gen6_set_proximity, +}; diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c index 5b5f403d8ce6..fa945304b9a5 100644 --- a/drivers/input/mouse/elan_i2c_core.c +++ b/drivers/input/mouse/elan_i2c_core.c @@ -4,7 +4,7 @@ * Copyright (c) 2013 ELAN Microelectronics Corp. * * Author: æž—æ”¿ç¶ (Duson Lin) <dusonlin@emc.com.tw> - * Version: 1.5.9 + * Version: 1.6.0 * * Based on cyapa driver: * copyright (c) 2011-2012 Cypress Semiconductor, Inc. @@ -40,7 +40,7 @@ #include "elan_i2c.h" #define DRIVER_NAME "elan_i2c" -#define ELAN_DRIVER_VERSION "1.5.9" +#define ELAN_DRIVER_VERSION "1.6.0" #define ETP_MAX_PRESSURE 255 #define ETP_FWIDTH_REDUCE 90 #define ETP_FINGER_WIDTH 15 @@ -84,7 +84,7 @@ struct elan_tp_data { int pressure_adjustment; u8 mode; u8 ic_type; - u16 fw_vaildpage_count; + u16 fw_validpage_count; u16 fw_signature_address; bool irq_wake; @@ -94,25 +94,28 @@ struct elan_tp_data { bool baseline_ready; }; -static int elan_get_fwinfo(u8 ic_type, u16 *vaildpage_count, +static int elan_get_fwinfo(u8 iap_version, u16 *validpage_count, u16 *signature_address) { - switch(ic_type) { + switch (iap_version) { + case 0x08: + *validpage_count = 512; + break; case 0x09: - *vaildpage_count = 768; + *validpage_count = 768; break; case 0x0D: - *vaildpage_count = 896; + *validpage_count = 896; break; default: /* unknown ic type clear value */ - *vaildpage_count = 0; + *validpage_count = 0; *signature_address = 0; return -ENXIO; } *signature_address = - (*vaildpage_count * ETP_FW_PAGE_SIZE) - ETP_FW_SIGNATURE_SIZE; + (*validpage_count * ETP_FW_PAGE_SIZE) - ETP_FW_SIGNATURE_SIZE; return 0; } @@ -261,11 +264,11 @@ static int elan_query_device_info(struct elan_tp_data *data) if (error) return error; - error = elan_get_fwinfo(data->ic_type, &data->fw_vaildpage_count, + error = elan_get_fwinfo(data->iap_version, &data->fw_validpage_count, &data->fw_signature_address); if (error) { dev_err(&data->client->dev, - "unknown ic type %d\n", data->ic_type); + "unknown iap version %d\n", data->iap_version); return error; } @@ -353,7 +356,7 @@ static int __elan_update_firmware(struct elan_tp_data *data, iap_start_addr = get_unaligned_le16(&fw->data[ETP_IAP_START_ADDR * 2]); boot_page_count = (iap_start_addr * 2) / ETP_FW_PAGE_SIZE; - for (i = boot_page_count; i < data->fw_vaildpage_count; i++) { + for (i = boot_page_count; i < data->fw_validpage_count; i++) { u16 checksum = 0; const u8 *page = &fw->data[i * ETP_FW_PAGE_SIZE]; @@ -1165,6 +1168,9 @@ MODULE_DEVICE_TABLE(i2c, elan_id); #ifdef CONFIG_ACPI static const struct acpi_device_id elan_acpi_id[] = { { "ELAN0000", 0 }, + { "ELAN0100", 0 }, + { "ELAN0600", 0 }, + { "ELAN1000", 0 }, { } }; MODULE_DEVICE_TABLE(acpi, elan_acpi_id); @@ -1181,10 +1187,10 @@ MODULE_DEVICE_TABLE(of, elan_of_match); static struct i2c_driver elan_driver = { .driver = { .name = DRIVER_NAME, - .owner = THIS_MODULE, .pm = &elan_pm_ops, .acpi_match_table = ACPI_PTR(elan_acpi_id), .of_match_table = of_match_ptr(elan_of_match), + .probe_type = PROBE_PREFER_ASYNCHRONOUS, }, .probe = elan_probe, .id_table = elan_id, diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index ec3477036150..ad18dab0ac47 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -1540,6 +1540,10 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv) if (error) goto err_clear_drvdata; + /* give PT device some time to settle down before probing */ + if (serio->id.type == SERIO_PS_PSTHRU) + usleep_range(10000, 15000); + if (psmouse_probe(psmouse) < 0) { error = -ENODEV; goto err_close_serio; diff --git a/drivers/input/mouse/sentelic.c b/drivers/input/mouse/sentelic.c index cc7e0d4a8f93..11c32ac8234b 100644 --- a/drivers/input/mouse/sentelic.c +++ b/drivers/input/mouse/sentelic.c @@ -432,7 +432,7 @@ static int fsp_onpad_hscr(struct psmouse *psmouse, bool enable) static ssize_t fsp_attr_set_setreg(struct psmouse *psmouse, void *data, const char *buf, size_t count) { - int reg, val; + unsigned int reg, val; char *rest; ssize_t retval; @@ -440,7 +440,7 @@ static ssize_t fsp_attr_set_setreg(struct psmouse *psmouse, void *data, if (rest == buf || *rest != ' ' || reg > 0xff) return -EINVAL; - retval = kstrtoint(rest + 1, 16, &val); + retval = kstrtouint(rest + 1, 16, &val); if (retval) return retval; @@ -476,9 +476,10 @@ static ssize_t fsp_attr_set_getreg(struct psmouse *psmouse, void *data, const char *buf, size_t count) { struct fsp_data *pad = psmouse->private; - int reg, val, err; + unsigned int reg, val; + int err; - err = kstrtoint(buf, 16, ®); + err = kstrtouint(buf, 16, ®); if (err) return err; @@ -511,9 +512,10 @@ static ssize_t fsp_attr_show_pagereg(struct psmouse *psmouse, static ssize_t fsp_attr_set_pagereg(struct psmouse *psmouse, void *data, const char *buf, size_t count) { - int val, err; + unsigned int val; + int err; - err = kstrtoint(buf, 16, &val); + err = kstrtouint(buf, 16, &val); if (err) return err; diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 6025eb430c0a..994ae7886156 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -519,14 +519,18 @@ static int synaptics_set_mode(struct psmouse *psmouse) struct synaptics_data *priv = psmouse->private; priv->mode = 0; - if (priv->absolute_mode) + + if (priv->absolute_mode) { priv->mode |= SYN_BIT_ABSOLUTE_MODE; - if (priv->disable_gesture) + if (SYN_CAP_EXTENDED(priv->capabilities)) + priv->mode |= SYN_BIT_W_MODE; + } + + if (!SYN_MODE_WMODE(priv->mode) && priv->disable_gesture) priv->mode |= SYN_BIT_DISABLE_GESTURE; + if (psmouse->rate >= 80) priv->mode |= SYN_BIT_HIGH_RATE; - if (SYN_CAP_EXTENDED(priv->capabilities)) - priv->mode |= SYN_BIT_W_MODE; if (synaptics_mode_cmd(psmouse, priv->mode)) return -1; diff --git a/drivers/input/mouse/synaptics_i2c.c b/drivers/input/mouse/synaptics_i2c.c index ffceedcaf3c8..aa7c5da60800 100644 --- a/drivers/input/mouse/synaptics_i2c.c +++ b/drivers/input/mouse/synaptics_i2c.c @@ -655,7 +655,6 @@ MODULE_DEVICE_TABLE(i2c, synaptics_i2c_id_table); static struct i2c_driver synaptics_i2c_driver = { .driver = { .name = DRIVER_NAME, - .owner = THIS_MODULE, .pm = &synaptics_i2c_pm, }, diff --git a/drivers/input/serio/ambakmi.c b/drivers/input/serio/ambakmi.c index 8b748d99b934..c6606cacb6a7 100644 --- a/drivers/input/serio/ambakmi.c +++ b/drivers/input/serio/ambakmi.c @@ -175,9 +175,9 @@ static int amba_kmi_remove(struct amba_device *dev) return 0; } -static int amba_kmi_resume(struct amba_device *dev) +static int __maybe_unused amba_kmi_resume(struct device *dev) { - struct amba_kmi_port *kmi = amba_get_drvdata(dev); + struct amba_kmi_port *kmi = dev_get_drvdata(dev); /* kick the serio layer to rescan this port */ serio_reconnect(kmi->io); @@ -185,6 +185,8 @@ static int amba_kmi_resume(struct amba_device *dev) return 0; } +static SIMPLE_DEV_PM_OPS(amba_kmi_dev_pm_ops, NULL, amba_kmi_resume); + static struct amba_id amba_kmi_idtable[] = { { .id = 0x00041050, @@ -199,11 +201,11 @@ static struct amba_driver ambakmi_driver = { .drv = { .name = "kmi-pl050", .owner = THIS_MODULE, + .pm = &amba_kmi_dev_pm_ops, }, .id_table = amba_kmi_idtable, .probe = amba_kmi_probe, .remove = amba_kmi_remove, - .resume = amba_kmi_resume, }; module_amba_driver(ambakmi_driver); diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index cb5ece77fd7d..db91de539ee3 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -88,6 +88,10 @@ MODULE_PARM_DESC(nopnp, "Do not use PNP to detect controller settings"); static bool i8042_debug; module_param_named(debug, i8042_debug, bool, 0600); MODULE_PARM_DESC(debug, "Turn i8042 debugging mode on and off"); + +static bool i8042_unmask_kbd_data; +module_param_named(unmask_kbd_data, i8042_unmask_kbd_data, bool, 0600); +MODULE_PARM_DESC(unmask_kbd_data, "Unconditional enable (may reveal sensitive data) of normally sanitize-filtered kbd data traffic debug log [pre-condition: i8042.debug=1 enabled]"); #endif static bool i8042_bypass_aux_irq_test; @@ -116,6 +120,7 @@ struct i8042_port { struct serio *serio; int irq; bool exists; + bool driver_bound; signed char mux; }; @@ -133,6 +138,7 @@ static bool i8042_kbd_irq_registered; static bool i8042_aux_irq_registered; static unsigned char i8042_suppress_kbd_ack; static struct platform_device *i8042_platform_device; +static struct notifier_block i8042_kbd_bind_notifier_block; static irqreturn_t i8042_interrupt(int irq, void *dev_id); static bool (*i8042_platform_filter)(unsigned char data, unsigned char str, @@ -528,10 +534,10 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id) port = &i8042_ports[port_no]; serio = port->exists ? port->serio : NULL; - dbg("%02x <- i8042 (interrupt, %d, %d%s%s)\n", - data, port_no, irq, - dfl & SERIO_PARITY ? ", bad parity" : "", - dfl & SERIO_TIMEOUT ? ", timeout" : ""); + filter_dbg(port->driver_bound, data, "<- i8042 (interrupt, %d, %d%s%s)\n", + port_no, irq, + dfl & SERIO_PARITY ? ", bad parity" : "", + dfl & SERIO_TIMEOUT ? ", timeout" : ""); filtered = i8042_filter(data, str, serio); @@ -871,7 +877,7 @@ static int __init i8042_check_aux(void) static int i8042_controller_check(void) { if (i8042_flush()) { - pr_err("No controller found\n"); + pr_info("No controller found\n"); return -ENODEV; } @@ -1438,6 +1444,29 @@ static int __init i8042_setup_kbd(void) return error; } +static int i8042_kbd_bind_notifier(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct device *dev = data; + struct serio *serio = to_serio_port(dev); + struct i8042_port *port = serio->port_data; + + if (serio != i8042_ports[I8042_KBD_PORT_NO].serio) + return 0; + + switch (action) { + case BUS_NOTIFY_BOUND_DRIVER: + port->driver_bound = true; + break; + + case BUS_NOTIFY_UNBIND_DRIVER: + port->driver_bound = false; + break; + } + + return 0; +} + static int __init i8042_probe(struct platform_device *dev) { int error; @@ -1507,6 +1536,10 @@ static struct platform_driver i8042_driver = { .shutdown = i8042_shutdown, }; +static struct notifier_block i8042_kbd_bind_notifier_block = { + .notifier_call = i8042_kbd_bind_notifier, +}; + static int __init i8042_init(void) { struct platform_device *pdev; @@ -1528,6 +1561,7 @@ static int __init i8042_init(void) goto err_platform_exit; } + bus_register_notifier(&serio_bus, &i8042_kbd_bind_notifier_block); panic_blink = i8042_panic_blink; return 0; @@ -1543,6 +1577,7 @@ static void __exit i8042_exit(void) platform_driver_unregister(&i8042_driver); i8042_platform_exit(); + bus_unregister_notifier(&serio_bus, &i8042_kbd_bind_notifier_block); panic_blink = NULL; } diff --git a/drivers/input/serio/i8042.h b/drivers/input/serio/i8042.h index fc080beffedc..1db0a40c9bab 100644 --- a/drivers/input/serio/i8042.h +++ b/drivers/input/serio/i8042.h @@ -73,6 +73,17 @@ static unsigned long i8042_start_time; printk(KERN_DEBUG KBUILD_MODNAME ": [%d] " format, \ (int) (jiffies - i8042_start_time), ##arg); \ } while (0) + +#define filter_dbg(filter, data, format, args...) \ + do { \ + if (!i8042_debug) \ + break; \ + \ + if (!filter || i8042_unmask_kbd_data) \ + dbg("%02x " format, data, ##args); \ + else \ + dbg("** " format, ##args); \ + } while (0) #else #define dbg_init() do { } while (0) #define dbg(format, arg...) \ @@ -80,6 +91,8 @@ static unsigned long i8042_start_time; if (0) \ printk(KERN_DEBUG pr_fmt(format), ##arg); \ } while (0) + +#define filter_dbg(filter, data, format, args...) do { } while (0) #endif #endif /* _I8042_H */ diff --git a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c index a05a5179da32..8f828975ab10 100644 --- a/drivers/input/serio/serio.c +++ b/drivers/input/serio/serio.c @@ -49,8 +49,6 @@ static DEFINE_MUTEX(serio_mutex); static LIST_HEAD(serio_list); -static struct bus_type serio_bus; - static void serio_add_port(struct serio *serio); static int serio_reconnect_port(struct serio *serio); static void serio_disconnect_port(struct serio *serio); @@ -1017,7 +1015,7 @@ irqreturn_t serio_interrupt(struct serio *serio, } EXPORT_SYMBOL(serio_interrupt); -static struct bus_type serio_bus = { +struct bus_type serio_bus = { .name = "serio", .drv_groups = serio_driver_groups, .match = serio_bus_match, @@ -1029,6 +1027,7 @@ static struct bus_type serio_bus = { .pm = &serio_pm_ops, #endif }; +EXPORT_SYMBOL(serio_bus); static int __init serio_init(void) { diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index a854c6e5f09e..600dcceff542 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -11,9 +11,9 @@ menuconfig INPUT_TOUCHSCREEN if INPUT_TOUCHSCREEN -config OF_TOUCHSCREEN +config TOUCHSCREEN_PROPERTIES def_tristate INPUT - depends on INPUT && OF + depends on INPUT config TOUCHSCREEN_88PM860X tristate "Marvell 88PM860x touchscreen" @@ -118,7 +118,7 @@ config TOUCHSCREEN_ATMEL_MXT config TOUCHSCREEN_AUO_PIXCIR tristate "AUO in-cell touchscreen using Pixcir ICs" depends on I2C - depends on GPIOLIB + depends on GPIOLIB || COMPILE_TEST help Say Y here if you have a AUO display with in-cell touchscreen using Pixcir ICs. @@ -142,7 +142,7 @@ config TOUCHSCREEN_BU21013 config TOUCHSCREEN_CHIPONE_ICN8318 tristate "chipone icn8318 touchscreen controller" - depends on GPIOLIB + depends on GPIOLIB || COMPILE_TEST depends on I2C depends on OF help @@ -156,7 +156,7 @@ config TOUCHSCREEN_CHIPONE_ICN8318 config TOUCHSCREEN_CY8CTMG110 tristate "cy8ctmg110 touchscreen" depends on I2C - depends on GPIOLIB + depends on GPIOLIB || COMPILE_TEST help Say Y here if you have a cy8ctmg110 capacitive touchscreen on an AAVA device. @@ -479,6 +479,18 @@ config TOUCHSCREEN_MTOUCH To compile this driver as a module, choose M here: the module will be called mtouch. +config TOUCHSCREEN_IMX6UL_TSC + tristate "Freescale i.MX6UL touchscreen controller" + depends on (OF && GPIOLIB) || COMPILE_TEST + help + Say Y here if you have a Freescale i.MX6UL, and want to + use the internal touchscreen controller. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called imx6ul_tsc. + config TOUCHSCREEN_INEXIO tristate "iNexio serial touchscreens" select SERIO @@ -915,10 +927,11 @@ config TOUCHSCREEN_TSC_SERIO module will be called tsc40. config TOUCHSCREEN_TSC2005 - tristate "TSC2005 based touchscreens" - depends on SPI_MASTER - help - Say Y here if you have a TSC2005 based touchscreen. + tristate "TSC2005 based touchscreens" + depends on SPI_MASTER + select REGMAP_SPI + help + Say Y here if you have a TSC2005 based touchscreen. If unsure, say N. @@ -1029,7 +1042,7 @@ config TOUCHSCREEN_TPS6507X config TOUCHSCREEN_ZFORCE tristate "Neonode zForce infrared touchscreens" depends on I2C - depends on GPIOLIB + depends on GPIOLIB || COMPILE_TEST help Say Y here if you have a touchscreen using the zforce infraread technology from Neonode. @@ -1039,4 +1052,16 @@ config TOUCHSCREEN_ZFORCE To compile this driver as a module, choose M here: the module will be called zforce_ts. +config TOUCHSCREEN_COLIBRI_VF50 + tristate "Toradex Colibri on board touchscreen driver" + depends on GPIOLIB && IIO && VF610_ADC + help + Say Y here if you have a Colibri VF50 and plan to use + the on-board provided 4-wire touchscreen driver. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called colibri_vf50_ts. + endif diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index fa3d33bac7fc..1b79cc09744a 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -6,7 +6,7 @@ wm97xx-ts-y := wm97xx-core.o -obj-$(CONFIG_OF_TOUCHSCREEN) += of_touchscreen.o +obj-$(CONFIG_TOUCHSCREEN_PROPERTIES) += of_touchscreen.o obj-$(CONFIG_TOUCHSCREEN_88PM860X) += 88pm860x-ts.o obj-$(CONFIG_TOUCHSCREEN_AD7877) += ad7877.o obj-$(CONFIG_TOUCHSCREEN_AD7879) += ad7879.o @@ -38,6 +38,7 @@ obj-$(CONFIG_TOUCHSCREEN_EGALAX) += egalax_ts.o obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix.o obj-$(CONFIG_TOUCHSCREEN_ILI210X) += ili210x.o +obj-$(CONFIG_TOUCHSCREEN_IMX6UL_TSC) += imx6ul_tsc.o obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o obj-$(CONFIG_TOUCHSCREEN_INTEL_MID) += intel-mid-touch.o obj-$(CONFIG_TOUCHSCREEN_IPROC) += bcm_iproc_tsc.o @@ -85,3 +86,4 @@ obj-$(CONFIG_TOUCHSCREEN_W90X900) += w90p910_ts.o obj-$(CONFIG_TOUCHSCREEN_SX8654) += sx8654.o obj-$(CONFIG_TOUCHSCREEN_TPS6507X) += tps6507x-ts.o obj-$(CONFIG_TOUCHSCREEN_ZFORCE) += zforce_ts.o +obj-$(CONFIG_TOUCHSCREEN_COLIBRI_VF50) += colibri-vf50-ts.o diff --git a/drivers/input/touchscreen/ad7879-i2c.c b/drivers/input/touchscreen/ad7879-i2c.c index dcf390771549..d66962c5b1c2 100644 --- a/drivers/input/touchscreen/ad7879-i2c.c +++ b/drivers/input/touchscreen/ad7879-i2c.c @@ -94,7 +94,6 @@ MODULE_DEVICE_TABLE(i2c, ad7879_id); static struct i2c_driver ad7879_i2c_driver = { .driver = { .name = "ad7879", - .owner = THIS_MODULE, .pm = &ad7879_pm_ops, }, .probe = ad7879_i2c_probe, diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index e4eb8a6c658f..0f5f968592bd 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -1234,7 +1234,8 @@ static const struct ads7846_platform_data *ads7846_probe_dt(struct device *dev) of_property_read_u32(node, "ti,pendown-gpio-debounce", &pdata->gpio_pendown_debounce); - pdata->wakeup = of_property_read_bool(node, "linux,wakeup"); + pdata->wakeup = of_property_read_bool(node, "wakeup-source") || + of_property_read_bool(node, "linux,wakeup"); pdata->gpio_pendown = of_get_named_gpio(dev->of_node, "pendown-gpio", 0); diff --git a/drivers/input/touchscreen/ar1021_i2c.c b/drivers/input/touchscreen/ar1021_i2c.c index f0b954d46a25..71b5a634cf6d 100644 --- a/drivers/input/touchscreen/ar1021_i2c.c +++ b/drivers/input/touchscreen/ar1021_i2c.c @@ -166,7 +166,6 @@ MODULE_DEVICE_TABLE(of, ar1021_i2c_of_match); static struct i2c_driver ar1021_i2c_driver = { .driver = { .name = "ar1021_i2c", - .owner = THIS_MODULE, .pm = &ar1021_i2c_pm, .of_match_table = ar1021_i2c_of_match, }, diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index dfc7309e3d38..c5622058c22b 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -22,34 +22,20 @@ #include <linux/delay.h> #include <linux/firmware.h> #include <linux/i2c.h> -#include <linux/i2c/atmel_mxt_ts.h> +#include <linux/platform_data/atmel_mxt_ts.h> #include <linux/input/mt.h> #include <linux/interrupt.h> #include <linux/of.h> #include <linux/slab.h> #include <asm/unaligned.h> -/* Version */ -#define MXT_VER_20 20 -#define MXT_VER_21 21 -#define MXT_VER_22 22 - /* Firmware files */ #define MXT_FW_NAME "maxtouch.fw" #define MXT_CFG_NAME "maxtouch.cfg" #define MXT_CFG_MAGIC "OBP_RAW V1" /* Registers */ -#define MXT_INFO 0x00 -#define MXT_FAMILY_ID 0x00 -#define MXT_VARIANT_ID 0x01 -#define MXT_VERSION 0x02 -#define MXT_BUILD 0x03 -#define MXT_MATRIX_X_SIZE 0x04 -#define MXT_MATRIX_Y_SIZE 0x05 -#define MXT_OBJECT_NUM 0x06 #define MXT_OBJECT_START 0x07 - #define MXT_OBJECT_SIZE 6 #define MXT_INFO_CHECKSUM_SIZE 3 #define MXT_MAX_BLOCK_WRITE 256 @@ -103,21 +89,16 @@ #define MXT_T6_STATUS_COMSERR (1 << 2) /* MXT_GEN_POWER_T7 field */ -#define MXT_POWER_IDLEACQINT 0 -#define MXT_POWER_ACTVACQINT 1 -#define MXT_POWER_ACTV2IDLETO 2 - -/* MXT_GEN_ACQUIRE_T8 field */ -#define MXT_ACQUIRE_CHRGTIME 0 -#define MXT_ACQUIRE_TCHDRIFT 2 -#define MXT_ACQUIRE_DRIFTST 3 -#define MXT_ACQUIRE_TCHAUTOCAL 4 -#define MXT_ACQUIRE_SYNC 5 -#define MXT_ACQUIRE_ATCHCALST 6 -#define MXT_ACQUIRE_ATCHCALSTHR 7 +struct t7_config { + u8 idle; + u8 active; +} __packed; + +#define MXT_POWER_CFG_RUN 0 +#define MXT_POWER_CFG_DEEPSLEEP 1 /* MXT_TOUCH_MULTI_T9 field */ -#define MXT_TOUCH_CTRL 0 +#define MXT_T9_CTRL 0 #define MXT_T9_ORIENT 9 #define MXT_T9_RANGE 18 @@ -139,51 +120,10 @@ struct t9_range { /* MXT_TOUCH_MULTI_T9 orient */ #define MXT_T9_ORIENT_SWITCH (1 << 0) -/* MXT_PROCI_GRIPFACE_T20 field */ -#define MXT_GRIPFACE_CTRL 0 -#define MXT_GRIPFACE_XLOGRIP 1 -#define MXT_GRIPFACE_XHIGRIP 2 -#define MXT_GRIPFACE_YLOGRIP 3 -#define MXT_GRIPFACE_YHIGRIP 4 -#define MXT_GRIPFACE_MAXTCHS 5 -#define MXT_GRIPFACE_SZTHR1 7 -#define MXT_GRIPFACE_SZTHR2 8 -#define MXT_GRIPFACE_SHPTHR1 9 -#define MXT_GRIPFACE_SHPTHR2 10 -#define MXT_GRIPFACE_SUPEXTTO 11 - -/* MXT_PROCI_NOISE field */ -#define MXT_NOISE_CTRL 0 -#define MXT_NOISE_OUTFLEN 1 -#define MXT_NOISE_GCAFUL_LSB 3 -#define MXT_NOISE_GCAFUL_MSB 4 -#define MXT_NOISE_GCAFLL_LSB 5 -#define MXT_NOISE_GCAFLL_MSB 6 -#define MXT_NOISE_ACTVGCAFVALID 7 -#define MXT_NOISE_NOISETHR 8 -#define MXT_NOISE_FREQHOPSCALE 10 -#define MXT_NOISE_FREQ0 11 -#define MXT_NOISE_FREQ1 12 -#define MXT_NOISE_FREQ2 13 -#define MXT_NOISE_FREQ3 14 -#define MXT_NOISE_FREQ4 15 -#define MXT_NOISE_IDLEGCAFVALID 16 - /* MXT_SPT_COMMSCONFIG_T18 */ #define MXT_COMMS_CTRL 0 #define MXT_COMMS_CMD 1 -/* MXT_SPT_CTECONFIG_T28 field */ -#define MXT_CTE_CTRL 0 -#define MXT_CTE_CMD 1 -#define MXT_CTE_MODE 2 -#define MXT_CTE_IDLEGCAFDEPTH 3 -#define MXT_CTE_ACTVGCAFDEPTH 4 -#define MXT_CTE_VOLTAGE 5 - -#define MXT_VOLTAGE_DEFAULT 2700000 -#define MXT_VOLTAGE_STEP 10000 - /* Define for MXT_GEN_COMMAND_T6 */ #define MXT_BOOT_VALUE 0xa5 #define MXT_RESET_VALUE 0x01 @@ -291,6 +231,7 @@ struct mxt_data { u8 last_message_count; u8 num_touchids; u8 multitouch; + struct t7_config t7_cfg; /* Cached parameters from object table */ u16 T5_address; @@ -997,16 +938,15 @@ static irqreturn_t mxt_process_messages_t44(struct mxt_data *data) count = data->msg_buf[0]; - if (count == 0) { - /* - * This condition is caused by the CHG line being configured - * in Mode 0. It results in unnecessary I2C operations but it - * is benign. - */ - dev_dbg(dev, "Interrupt triggered but zero messages\n"); + /* + * This condition may be caused by the CHG line being configured in + * Mode 0. It results in unnecessary I2C operations but it is benign. + */ + if (count == 0) return IRQ_NONE; - } else if (count > data->max_reportid) { - dev_err(dev, "T44 count %d exceeded max report id\n", count); + + if (count > data->max_reportid) { + dev_warn(dev, "T44 count %d exceeded max report id\n", count); count = data->max_reportid; } @@ -1157,7 +1097,9 @@ static int mxt_soft_reset(struct mxt_data *data) struct device *dev = &data->client->dev; int ret = 0; - dev_info(dev, "Resetting chip\n"); + dev_info(dev, "Resetting device\n"); + + disable_irq(data->irq); reinit_completion(&data->reset_completion); @@ -1165,6 +1107,11 @@ static int mxt_soft_reset(struct mxt_data *data) if (ret) return ret; + /* Ignore CHG line for 100ms after reset */ + msleep(100); + + enable_irq(data->irq); + ret = mxt_wait_for_completion(data, &data->reset_completion, MXT_RESET_TIMEOUT); if (ret) @@ -1361,6 +1308,8 @@ static int mxt_upload_cfg_mem(struct mxt_data *data, unsigned int cfg_start, return 0; } +static int mxt_init_t7_power_cfg(struct mxt_data *data); + /* * mxt_update_cfg - download configuration to chip * @@ -1508,6 +1457,9 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg) dev_info(dev, "Config successfully updated\n"); + /* T7 config may have changed */ + mxt_init_t7_power_cfg(data); + release_mem: kfree(config_mem); return ret; @@ -1533,7 +1485,7 @@ static int mxt_get_info(struct mxt_data *data) int error; /* Read 7-byte info block starting at address 0 */ - error = __mxt_read_reg(client, MXT_INFO, sizeof(*info), info); + error = __mxt_read_reg(client, 0, sizeof(*info), info); if (error) return error; @@ -1905,6 +1857,8 @@ static int mxt_initialize_input_device(struct mxt_data *data) if (pdata->t19_num_keys) { mxt_set_up_as_touchpad(input_dev, data); mt_flags |= INPUT_MT_POINTER; + } else { + mt_flags |= INPUT_MT_DIRECT; } /* For multi touch */ @@ -2051,6 +2005,60 @@ err_free_object_table: return error; } +static int mxt_set_t7_power_cfg(struct mxt_data *data, u8 sleep) +{ + struct device *dev = &data->client->dev; + int error; + struct t7_config *new_config; + struct t7_config deepsleep = { .active = 0, .idle = 0 }; + + if (sleep == MXT_POWER_CFG_DEEPSLEEP) + new_config = &deepsleep; + else + new_config = &data->t7_cfg; + + error = __mxt_write_reg(data->client, data->T7_address, + sizeof(data->t7_cfg), new_config); + if (error) + return error; + + dev_dbg(dev, "Set T7 ACTV:%d IDLE:%d\n", + new_config->active, new_config->idle); + + return 0; +} + +static int mxt_init_t7_power_cfg(struct mxt_data *data) +{ + struct device *dev = &data->client->dev; + int error; + bool retry = false; + +recheck: + error = __mxt_read_reg(data->client, data->T7_address, + sizeof(data->t7_cfg), &data->t7_cfg); + if (error) + return error; + + if (data->t7_cfg.active == 0 || data->t7_cfg.idle == 0) { + if (!retry) { + dev_dbg(dev, "T7 cfg zero, resetting\n"); + mxt_soft_reset(data); + retry = true; + goto recheck; + } else { + dev_dbg(dev, "T7 cfg zero after reset, overriding\n"); + data->t7_cfg.active = 20; + data->t7_cfg.idle = 100; + return mxt_set_t7_power_cfg(data, MXT_POWER_CFG_RUN); + } + } + + dev_dbg(dev, "Initialized power cfg: ACTV %d, IDLE %d\n", + data->t7_cfg.active, data->t7_cfg.idle); + return 0; +} + static int mxt_configure_objects(struct mxt_data *data, const struct firmware *cfg) { @@ -2058,6 +2066,12 @@ static int mxt_configure_objects(struct mxt_data *data, struct mxt_info *info = &data->info; int error; + error = mxt_init_t7_power_cfg(data); + if (error) { + dev_err(dev, "Failed to initialize power cfg\n"); + return error; + } + if (cfg) { error = mxt_update_cfg(data, cfg); if (error) @@ -2346,14 +2360,41 @@ static const struct attribute_group mxt_attr_group = { static void mxt_start(struct mxt_data *data) { - /* Touch enable */ - mxt_write_object(data, data->multitouch, MXT_TOUCH_CTRL, 0x83); + switch (data->pdata->suspend_mode) { + case MXT_SUSPEND_T9_CTRL: + mxt_soft_reset(data); + + /* Touch enable */ + /* 0x83 = SCANEN | RPTEN | ENABLE */ + mxt_write_object(data, + MXT_TOUCH_MULTI_T9, MXT_T9_CTRL, 0x83); + break; + + case MXT_SUSPEND_DEEP_SLEEP: + default: + mxt_set_t7_power_cfg(data, MXT_POWER_CFG_RUN); + + /* Recalibrate since chip has been in deep sleep */ + mxt_t6_command(data, MXT_COMMAND_CALIBRATE, 1, false); + break; + } + } static void mxt_stop(struct mxt_data *data) { - /* Touch disable */ - mxt_write_object(data, data->multitouch, MXT_TOUCH_CTRL, 0); + switch (data->pdata->suspend_mode) { + case MXT_SUSPEND_T9_CTRL: + /* Touch disable */ + mxt_write_object(data, + MXT_TOUCH_MULTI_T9, MXT_T9_CTRL, 0); + break; + + case MXT_SUSPEND_DEEP_SLEEP: + default: + mxt_set_t7_power_cfg(data, MXT_POWER_CFG_DEEPSLEEP); + break; + } } static int mxt_input_open(struct input_dev *dev) @@ -2376,19 +2417,18 @@ static void mxt_input_close(struct input_dev *dev) static const struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client) { struct mxt_platform_data *pdata; + struct device_node *np = client->dev.of_node; u32 *keymap; - u32 keycode; - int proplen, i, ret; + int proplen, ret; - if (!client->dev.of_node) + if (!np) return ERR_PTR(-ENOENT); pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) return ERR_PTR(-ENOMEM); - if (of_find_property(client->dev.of_node, "linux,gpio-keymap", - &proplen)) { + if (of_find_property(np, "linux,gpio-keymap", &proplen)) { pdata->t19_num_keys = proplen / sizeof(u32); keymap = devm_kzalloc(&client->dev, @@ -2397,18 +2437,17 @@ static const struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client) if (!keymap) return ERR_PTR(-ENOMEM); - for (i = 0; i < pdata->t19_num_keys; i++) { - ret = of_property_read_u32_index(client->dev.of_node, - "linux,gpio-keymap", i, &keycode); - if (ret) - keycode = KEY_RESERVED; - - keymap[i] = keycode; - } + ret = of_property_read_u32_array(np, "linux,gpio-keymap", + keymap, pdata->t19_num_keys); + if (ret) + dev_warn(&client->dev, + "Couldn't read linux,gpio-keymap: %d\n", ret); pdata->t19_keymap = keymap; } + pdata->suspend_mode = MXT_SUSPEND_DEEP_SLEEP; + return pdata; } #else @@ -2609,6 +2648,9 @@ static int __maybe_unused mxt_suspend(struct device *dev) struct mxt_data *data = i2c_get_clientdata(client); struct input_dev *input_dev = data->input_dev; + if (!input_dev) + return 0; + mutex_lock(&input_dev->mutex); if (input_dev->users) @@ -2625,7 +2667,8 @@ static int __maybe_unused mxt_resume(struct device *dev) struct mxt_data *data = i2c_get_clientdata(client); struct input_dev *input_dev = data->input_dev; - mxt_soft_reset(data); + if (!input_dev) + return 0; mutex_lock(&input_dev->mutex); @@ -2666,7 +2709,6 @@ MODULE_DEVICE_TABLE(i2c, mxt_id); static struct i2c_driver mxt_driver = { .driver = { .name = "atmel_mxt_ts", - .owner = THIS_MODULE, .of_match_table = of_match_ptr(mxt_of_match), .acpi_match_table = ACPI_PTR(mxt_acpi_id), .pm = &mxt_pm_ops, diff --git a/drivers/input/touchscreen/auo-pixcir-ts.c b/drivers/input/touchscreen/auo-pixcir-ts.c index 40e02dd5b2f9..38c06f754acd 100644 --- a/drivers/input/touchscreen/auo-pixcir-ts.c +++ b/drivers/input/touchscreen/auo-pixcir-ts.c @@ -686,7 +686,6 @@ MODULE_DEVICE_TABLE(of, auo_pixcir_ts_dt_idtable); static struct i2c_driver auo_pixcir_driver = { .driver = { - .owner = THIS_MODULE, .name = "auo_pixcir_ts", .pm = &auo_pixcir_pm_ops, .of_match_table = of_match_ptr(auo_pixcir_ts_dt_idtable), diff --git a/drivers/input/touchscreen/bu21013_ts.c b/drivers/input/touchscreen/bu21013_ts.c index b9b5ddad6658..931417eb4f5a 100644 --- a/drivers/input/touchscreen/bu21013_ts.c +++ b/drivers/input/touchscreen/bu21013_ts.c @@ -716,7 +716,6 @@ MODULE_DEVICE_TABLE(i2c, bu21013_id); static struct i2c_driver bu21013_driver = { .driver = { .name = DRIVER_TP, - .owner = THIS_MODULE, #ifdef CONFIG_PM .pm = &bu21013_dev_pm_ops, #endif diff --git a/drivers/input/touchscreen/chipone_icn8318.c b/drivers/input/touchscreen/chipone_icn8318.c index 32e9db0e04bf..22a6fead8cfb 100644 --- a/drivers/input/touchscreen/chipone_icn8318.c +++ b/drivers/input/touchscreen/chipone_icn8318.c @@ -300,7 +300,6 @@ MODULE_DEVICE_TABLE(i2c, icn8318_i2c_id); static struct i2c_driver icn8318_driver = { .driver = { - .owner = THIS_MODULE, .name = "chipone_icn8318", .pm = &icn8318_pm_ops, .of_match_table = icn8318_of_match, diff --git a/drivers/input/touchscreen/colibri-vf50-ts.c b/drivers/input/touchscreen/colibri-vf50-ts.c new file mode 100644 index 000000000000..5d4903a402cc --- /dev/null +++ b/drivers/input/touchscreen/colibri-vf50-ts.c @@ -0,0 +1,386 @@ +/* + * Toradex Colibri VF50 Touchscreen driver + * + * Copyright 2015 Toradex AG + * + * Originally authored by Stefan Agner for 3.0 kernel + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/gpio.h> +#include <linux/gpio/consumer.h> +#include <linux/iio/consumer.h> +#include <linux/iio/types.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pinctrl/consumer.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/types.h> + +#define DRIVER_NAME "colibri-vf50-ts" +#define DRV_VERSION "1.0" + +#define VF_ADC_MAX ((1 << 12) - 1) + +#define COLI_TOUCH_MIN_DELAY_US 1000 +#define COLI_TOUCH_MAX_DELAY_US 2000 +#define COLI_PULLUP_MIN_DELAY_US 10000 +#define COLI_PULLUP_MAX_DELAY_US 11000 +#define COLI_TOUCH_NO_OF_AVGS 5 +#define COLI_TOUCH_REQ_ADC_CHAN 4 + +struct vf50_touch_device { + struct platform_device *pdev; + struct input_dev *ts_input; + struct iio_channel *channels; + struct gpio_desc *gpio_xp; + struct gpio_desc *gpio_xm; + struct gpio_desc *gpio_yp; + struct gpio_desc *gpio_ym; + int pen_irq; + int min_pressure; + bool stop_touchscreen; +}; + +/* + * Enables given plates and measures touch parameters using ADC + */ +static int adc_ts_measure(struct iio_channel *channel, + struct gpio_desc *plate_p, struct gpio_desc *plate_m) +{ + int i, value = 0, val = 0; + int error; + + gpiod_set_value(plate_p, 1); + gpiod_set_value(plate_m, 1); + + usleep_range(COLI_TOUCH_MIN_DELAY_US, COLI_TOUCH_MAX_DELAY_US); + + for (i = 0; i < COLI_TOUCH_NO_OF_AVGS; i++) { + error = iio_read_channel_raw(channel, &val); + if (error < 0) { + value = error; + goto error_iio_read; + } + + value += val; + } + + value /= COLI_TOUCH_NO_OF_AVGS; + +error_iio_read: + gpiod_set_value(plate_p, 0); + gpiod_set_value(plate_m, 0); + + return value; +} + +/* + * Enable touch detection using falling edge detection on XM + */ +static void vf50_ts_enable_touch_detection(struct vf50_touch_device *vf50_ts) +{ + /* Enable plate YM (needs to be strong GND, high active) */ + gpiod_set_value(vf50_ts->gpio_ym, 1); + + /* + * Let the platform mux to idle state in order to enable + * Pull-Up on GPIO + */ + pinctrl_pm_select_idle_state(&vf50_ts->pdev->dev); + + /* Wait for the pull-up to be stable on high */ + usleep_range(COLI_PULLUP_MIN_DELAY_US, COLI_PULLUP_MAX_DELAY_US); +} + +/* + * ADC touch screen sampling bottom half irq handler + */ +static irqreturn_t vf50_ts_irq_bh(int irq, void *private) +{ + struct vf50_touch_device *vf50_ts = private; + struct device *dev = &vf50_ts->pdev->dev; + int val_x, val_y, val_z1, val_z2, val_p = 0; + bool discard_val_on_start = true; + + /* Disable the touch detection plates */ + gpiod_set_value(vf50_ts->gpio_ym, 0); + + /* Let the platform mux to default state in order to mux as ADC */ + pinctrl_pm_select_default_state(dev); + + while (!vf50_ts->stop_touchscreen) { + /* X-Direction */ + val_x = adc_ts_measure(&vf50_ts->channels[0], + vf50_ts->gpio_xp, vf50_ts->gpio_xm); + if (val_x < 0) + break; + + /* Y-Direction */ + val_y = adc_ts_measure(&vf50_ts->channels[1], + vf50_ts->gpio_yp, vf50_ts->gpio_ym); + if (val_y < 0) + break; + + /* + * Touch pressure + * Measure on XP/YM + */ + val_z1 = adc_ts_measure(&vf50_ts->channels[2], + vf50_ts->gpio_yp, vf50_ts->gpio_xm); + if (val_z1 < 0) + break; + val_z2 = adc_ts_measure(&vf50_ts->channels[3], + vf50_ts->gpio_yp, vf50_ts->gpio_xm); + if (val_z2 < 0) + break; + + /* Validate signal (avoid calculation using noise) */ + if (val_z1 > 64 && val_x > 64) { + /* + * Calculate resistance between the plates + * lower resistance means higher pressure + */ + int r_x = (1000 * val_x) / VF_ADC_MAX; + + val_p = (r_x * val_z2) / val_z1 - r_x; + + } else { + val_p = 2000; + } + + val_p = 2000 - val_p; + dev_dbg(dev, + "Measured values: x: %d, y: %d, z1: %d, z2: %d, p: %d\n", + val_x, val_y, val_z1, val_z2, val_p); + + /* + * If touch pressure is too low, stop measuring and reenable + * touch detection + */ + if (val_p < vf50_ts->min_pressure || val_p > 2000) + break; + + /* + * The pressure may not be enough for the first x and the + * second y measurement, but, the pressure is ok when the + * driver is doing the third and fourth measurement. To + * take care of this, we drop the first measurement always. + */ + if (discard_val_on_start) { + discard_val_on_start = false; + } else { + /* + * Report touch position and sleep for + * the next measurement. + */ + input_report_abs(vf50_ts->ts_input, + ABS_X, VF_ADC_MAX - val_x); + input_report_abs(vf50_ts->ts_input, + ABS_Y, VF_ADC_MAX - val_y); + input_report_abs(vf50_ts->ts_input, + ABS_PRESSURE, val_p); + input_report_key(vf50_ts->ts_input, BTN_TOUCH, 1); + input_sync(vf50_ts->ts_input); + } + + usleep_range(COLI_PULLUP_MIN_DELAY_US, + COLI_PULLUP_MAX_DELAY_US); + } + + /* Report no more touch, re-enable touch detection */ + input_report_abs(vf50_ts->ts_input, ABS_PRESSURE, 0); + input_report_key(vf50_ts->ts_input, BTN_TOUCH, 0); + input_sync(vf50_ts->ts_input); + + vf50_ts_enable_touch_detection(vf50_ts); + + return IRQ_HANDLED; +} + +static int vf50_ts_open(struct input_dev *dev_input) +{ + struct vf50_touch_device *touchdev = input_get_drvdata(dev_input); + struct device *dev = &touchdev->pdev->dev; + + dev_dbg(dev, "Input device %s opened, starting touch detection\n", + dev_input->name); + + touchdev->stop_touchscreen = false; + + /* Mux detection before request IRQ, wait for pull-up to settle */ + vf50_ts_enable_touch_detection(touchdev); + + return 0; +} + +static void vf50_ts_close(struct input_dev *dev_input) +{ + struct vf50_touch_device *touchdev = input_get_drvdata(dev_input); + struct device *dev = &touchdev->pdev->dev; + + touchdev->stop_touchscreen = true; + + /* Make sure IRQ is not running past close */ + mb(); + synchronize_irq(touchdev->pen_irq); + + gpiod_set_value(touchdev->gpio_ym, 0); + pinctrl_pm_select_default_state(dev); + + dev_dbg(dev, "Input device %s closed, disable touch detection\n", + dev_input->name); +} + +static int vf50_ts_get_gpiod(struct device *dev, struct gpio_desc **gpio_d, + const char *con_id, enum gpiod_flags flags) +{ + int error; + + *gpio_d = devm_gpiod_get(dev, con_id, flags); + if (IS_ERR(*gpio_d)) { + error = PTR_ERR(*gpio_d); + dev_err(dev, "Could not get gpio_%s %d\n", con_id, error); + return error; + } + + return 0; +} + +static void vf50_ts_channel_release(void *data) +{ + struct iio_channel *channels = data; + + iio_channel_release_all(channels); +} + +static int vf50_ts_probe(struct platform_device *pdev) +{ + struct input_dev *input; + struct iio_channel *channels; + struct device *dev = &pdev->dev; + struct vf50_touch_device *touchdev; + int num_adc_channels; + int error; + + channels = iio_channel_get_all(dev); + if (IS_ERR(channels)) + return PTR_ERR(channels); + + error = devm_add_action(dev, vf50_ts_channel_release, channels); + if (error) { + iio_channel_release_all(channels); + dev_err(dev, "Failed to register iio channel release action"); + return error; + } + + num_adc_channels = 0; + while (channels[num_adc_channels].indio_dev) + num_adc_channels++; + + if (num_adc_channels != COLI_TOUCH_REQ_ADC_CHAN) { + dev_err(dev, "Inadequate ADC channels specified\n"); + return -EINVAL; + } + + touchdev = devm_kzalloc(dev, sizeof(*touchdev), GFP_KERNEL); + if (!touchdev) + return -ENOMEM; + + touchdev->pdev = pdev; + touchdev->channels = channels; + + error = of_property_read_u32(dev->of_node, "vf50-ts-min-pressure", + &touchdev->min_pressure); + if (error) + return error; + + input = devm_input_allocate_device(dev); + if (!input) { + dev_err(dev, "Failed to allocate TS input device\n"); + return -ENOMEM; + } + + platform_set_drvdata(pdev, touchdev); + + input->name = DRIVER_NAME; + input->id.bustype = BUS_HOST; + input->dev.parent = dev; + input->open = vf50_ts_open; + input->close = vf50_ts_close; + + input_set_capability(input, EV_KEY, BTN_TOUCH); + input_set_abs_params(input, ABS_X, 0, VF_ADC_MAX, 0, 0); + input_set_abs_params(input, ABS_Y, 0, VF_ADC_MAX, 0, 0); + input_set_abs_params(input, ABS_PRESSURE, 0, VF_ADC_MAX, 0, 0); + + touchdev->ts_input = input; + input_set_drvdata(input, touchdev); + + error = input_register_device(input); + if (error) { + dev_err(dev, "Failed to register input device\n"); + return error; + } + + error = vf50_ts_get_gpiod(dev, &touchdev->gpio_xp, "xp", GPIOD_OUT_LOW); + if (error) + return error; + + error = vf50_ts_get_gpiod(dev, &touchdev->gpio_xm, + "xm", GPIOD_OUT_LOW); + if (error) + return error; + + error = vf50_ts_get_gpiod(dev, &touchdev->gpio_yp, "yp", GPIOD_OUT_LOW); + if (error) + return error; + + error = vf50_ts_get_gpiod(dev, &touchdev->gpio_ym, "ym", GPIOD_OUT_LOW); + if (error) + return error; + + touchdev->pen_irq = platform_get_irq(pdev, 0); + if (touchdev->pen_irq < 0) + return touchdev->pen_irq; + + error = devm_request_threaded_irq(dev, touchdev->pen_irq, + NULL, vf50_ts_irq_bh, IRQF_ONESHOT, + "vf50 touch", touchdev); + if (error) { + dev_err(dev, "Failed to request IRQ %d: %d\n", + touchdev->pen_irq, error); + return error; + } + + return 0; +} + +static const struct of_device_id vf50_touch_of_match[] = { + { .compatible = "toradex,vf50-touchscreen", }, + { } +}; +MODULE_DEVICE_TABLE(of, vf50_touch_of_match); + +static struct platform_driver vf50_touch_driver = { + .driver = { + .name = "toradex,vf50_touchctrl", + .of_match_table = vf50_touch_of_match, + }, + .probe = vf50_ts_probe, +}; +module_platform_driver(vf50_touch_driver); + +MODULE_AUTHOR("Sanchayan Maity"); +MODULE_DESCRIPTION("Colibri VF50 Touchscreen driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); diff --git a/drivers/input/touchscreen/cy8ctmg110_ts.c b/drivers/input/touchscreen/cy8ctmg110_ts.c index f2119ee0e21b..cc1d1350074e 100644 --- a/drivers/input/touchscreen/cy8ctmg110_ts.c +++ b/drivers/input/touchscreen/cy8ctmg110_ts.c @@ -347,7 +347,6 @@ MODULE_DEVICE_TABLE(i2c, cy8ctmg110_idtable); static struct i2c_driver cy8ctmg110_driver = { .driver = { - .owner = THIS_MODULE, .name = CY8CTMG110_DRIVER_NAME, .pm = &cy8ctmg110_pm, }, diff --git a/drivers/input/touchscreen/cyttsp4_i2c.c b/drivers/input/touchscreen/cyttsp4_i2c.c index 8e2012c79058..a9f95c7d3c00 100644 --- a/drivers/input/touchscreen/cyttsp4_i2c.c +++ b/drivers/input/touchscreen/cyttsp4_i2c.c @@ -74,7 +74,6 @@ MODULE_DEVICE_TABLE(i2c, cyttsp4_i2c_id); static struct i2c_driver cyttsp4_i2c_driver = { .driver = { .name = CYTTSP4_I2C_NAME, - .owner = THIS_MODULE, .pm = &cyttsp4_pm_ops, }, .probe = cyttsp4_i2c_probe, @@ -87,4 +86,3 @@ module_i2c_driver(cyttsp4_i2c_driver); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) I2C driver"); MODULE_AUTHOR("Cypress"); -MODULE_ALIAS("i2c:cyttsp4"); diff --git a/drivers/input/touchscreen/cyttsp_i2c.c b/drivers/input/touchscreen/cyttsp_i2c.c index 63104a86a9bd..eee51b3f2e3f 100644 --- a/drivers/input/touchscreen/cyttsp_i2c.c +++ b/drivers/input/touchscreen/cyttsp_i2c.c @@ -74,7 +74,6 @@ MODULE_DEVICE_TABLE(i2c, cyttsp_i2c_id); static struct i2c_driver cyttsp_i2c_driver = { .driver = { .name = CY_I2C_NAME, - .owner = THIS_MODULE, .pm = &cyttsp_pm_ops, }, .probe = cyttsp_i2c_probe, @@ -87,4 +86,3 @@ module_i2c_driver(cyttsp_i2c_driver); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) I2C driver"); MODULE_AUTHOR("Cypress"); -MODULE_ALIAS("i2c:cyttsp"); diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index 394b1de9a2a3..48de1e8b3c93 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -1041,7 +1041,7 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, 0, tsdata->num_y * 64 - 1, 0, 0); if (!pdata) - touchscreen_parse_of_params(input, true); + touchscreen_parse_properties(input, true); error = input_mt_init_slots(input, MAX_SUPPORT_POINTS, INPUT_MT_DIRECT); if (error) { @@ -1134,7 +1134,6 @@ MODULE_DEVICE_TABLE(of, edt_ft5x06_of_match); static struct i2c_driver edt_ft5x06_ts_driver = { .driver = { - .owner = THIS_MODULE, .name = "edt_ft5x06", .of_match_table = of_match_ptr(edt_ft5x06_of_match), .pm = &edt_ft5x06_ts_pm_ops, diff --git a/drivers/input/touchscreen/egalax_ts.c b/drivers/input/touchscreen/egalax_ts.c index 4c56299284ef..1afc08b08155 100644 --- a/drivers/input/touchscreen/egalax_ts.c +++ b/drivers/input/touchscreen/egalax_ts.c @@ -264,11 +264,11 @@ static const struct of_device_id egalax_ts_dt_ids[] = { { .compatible = "eeti,egalax_ts" }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, egalax_ts_dt_ids); static struct i2c_driver egalax_ts_driver = { .driver = { .name = "egalax_ts", - .owner = THIS_MODULE, .pm = &egalax_ts_pm_ops, .of_match_table = egalax_ts_dt_ids, }, diff --git a/drivers/input/touchscreen/elants_i2c.c b/drivers/input/touchscreen/elants_i2c.c index 0efd766a545b..17cc20ef4923 100644 --- a/drivers/input/touchscreen/elants_i2c.c +++ b/drivers/input/touchscreen/elants_i2c.c @@ -38,6 +38,8 @@ #include <linux/input/mt.h> #include <linux/acpi.h> #include <linux/of.h> +#include <linux/gpio/consumer.h> +#include <linux/regulator/consumer.h> #include <asm/unaligned.h> /* Device, Driver information */ @@ -100,7 +102,10 @@ #define ELAN_FW_PAGESIZE 132 /* calibration timeout definition */ -#define ELAN_CALI_TIMEOUT_MSEC 10000 +#define ELAN_CALI_TIMEOUT_MSEC 12000 + +#define ELAN_POWERON_DELAY_USEC 500 +#define ELAN_RESET_DELAY_MSEC 20 enum elants_state { ELAN_STATE_NORMAL, @@ -118,6 +123,10 @@ struct elants_data { struct i2c_client *client; struct input_dev *input; + struct regulator *vcc33; + struct regulator *vccio; + struct gpio_desc *reset_gpio; + u16 fw_version; u8 test_version; u8 solution_version; @@ -141,6 +150,7 @@ struct elants_data { u8 buf[MAX_PACKET_SIZE]; bool wake_irq_enabled; + bool keep_power_in_suspend; }; static int elants_i2c_send(struct i2c_client *client, @@ -605,6 +615,7 @@ static int elants_i2c_do_update_firmware(struct i2c_client *client, const u8 enter_iap[] = { 0x45, 0x49, 0x41, 0x50 }; const u8 enter_iap2[] = { 0x54, 0x00, 0x12, 0x34 }; const u8 iap_ack[] = { 0x55, 0xaa, 0x33, 0xcc }; + const u8 close_idle[] = {0x54, 0x2c, 0x01, 0x01}; u8 buf[HEADER_SIZE]; u16 send_id; int page, n_fw_pages; @@ -617,8 +628,13 @@ static int elants_i2c_do_update_firmware(struct i2c_client *client, } else { /* Start IAP Procedure */ dev_dbg(&client->dev, "Normal IAP procedure\n"); + /* Close idle mode */ + error = elants_i2c_send(client, close_idle, sizeof(close_idle)); + if (error) + dev_err(&client->dev, "Failed close idle: %d\n", error); + msleep(60); elants_i2c_sw_reset(client); - + msleep(20); error = elants_i2c_send(client, enter_iap, sizeof(enter_iap)); } @@ -1052,6 +1068,67 @@ static void elants_i2c_remove_sysfs_group(void *_data) sysfs_remove_group(&ts->client->dev.kobj, &elants_attribute_group); } +static int elants_i2c_power_on(struct elants_data *ts) +{ + int error; + + /* + * If we do not have reset gpio assume platform firmware + * controls regulators and does power them on for us. + */ + if (IS_ERR_OR_NULL(ts->reset_gpio)) + return 0; + + gpiod_set_value_cansleep(ts->reset_gpio, 1); + + error = regulator_enable(ts->vcc33); + if (error) { + dev_err(&ts->client->dev, + "failed to enable vcc33 regulator: %d\n", + error); + goto release_reset_gpio; + } + + error = regulator_enable(ts->vccio); + if (error) { + dev_err(&ts->client->dev, + "failed to enable vccio regulator: %d\n", + error); + regulator_disable(ts->vcc33); + goto release_reset_gpio; + } + + /* + * We need to wait a bit after powering on controller before + * we are allowed to release reset GPIO. + */ + udelay(ELAN_POWERON_DELAY_USEC); + +release_reset_gpio: + gpiod_set_value_cansleep(ts->reset_gpio, 0); + if (error) + return error; + + msleep(ELAN_RESET_DELAY_MSEC); + + return 0; +} + +static void elants_i2c_power_off(void *_data) +{ + struct elants_data *ts = _data; + + if (!IS_ERR_OR_NULL(ts->reset_gpio)) { + /* + * Activate reset gpio to prevent leakage through the + * pin once we shut off power to the controller. + */ + gpiod_set_value_cansleep(ts->reset_gpio, 1); + regulator_disable(ts->vccio); + regulator_disable(ts->vcc33); + } +} + static int elants_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -1066,13 +1143,6 @@ static int elants_i2c_probe(struct i2c_client *client, return -ENXIO; } - /* Make sure there is something at this address */ - if (i2c_smbus_xfer(client->adapter, client->addr, 0, - I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &dummy) < 0) { - dev_err(&client->dev, "nothing at this address\n"); - return -ENXIO; - } - ts = devm_kzalloc(&client->dev, sizeof(struct elants_data), GFP_KERNEL); if (!ts) return -ENOMEM; @@ -1083,6 +1153,62 @@ static int elants_i2c_probe(struct i2c_client *client, ts->client = client; i2c_set_clientdata(client, ts); + ts->vcc33 = devm_regulator_get(&client->dev, "vcc33"); + if (IS_ERR(ts->vcc33)) { + error = PTR_ERR(ts->vcc33); + if (error != -EPROBE_DEFER) + dev_err(&client->dev, + "Failed to get 'vcc33' regulator: %d\n", + error); + return error; + } + + ts->vccio = devm_regulator_get(&client->dev, "vccio"); + if (IS_ERR(ts->vccio)) { + error = PTR_ERR(ts->vccio); + if (error != -EPROBE_DEFER) + dev_err(&client->dev, + "Failed to get 'vccio' regulator: %d\n", + error); + return error; + } + + ts->reset_gpio = devm_gpiod_get(&client->dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(ts->reset_gpio)) { + error = PTR_ERR(ts->reset_gpio); + + if (error == -EPROBE_DEFER) + return error; + + if (error != -ENOENT && error != -ENOSYS) { + dev_err(&client->dev, + "failed to get reset gpio: %d\n", + error); + return error; + } + + ts->keep_power_in_suspend = true; + } + + error = elants_i2c_power_on(ts); + if (error) + return error; + + error = devm_add_action(&client->dev, elants_i2c_power_off, ts); + if (error) { + dev_err(&client->dev, + "failed to install power off action: %d\n", error); + elants_i2c_power_off(ts); + return error; + } + + /* Make sure there is something at this address */ + if (i2c_smbus_xfer(client->adapter, client->addr, 0, + I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &dummy) < 0) { + dev_err(&client->dev, "nothing at this address\n"); + return -ENXIO; + } + error = elants_i2c_initialize(ts); if (error) { dev_err(&client->dev, "failed to initialize: %d\n", error); @@ -1190,17 +1316,23 @@ static int __maybe_unused elants_i2c_suspend(struct device *dev) disable_irq(client->irq); - for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) { - error = elants_i2c_send(client, set_sleep_cmd, - sizeof(set_sleep_cmd)); - if (!error) - break; + if (device_may_wakeup(dev) || ts->keep_power_in_suspend) { + for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) { + error = elants_i2c_send(client, set_sleep_cmd, + sizeof(set_sleep_cmd)); + if (!error) + break; - dev_err(&client->dev, "suspend command failed: %d\n", error); - } + dev_err(&client->dev, + "suspend command failed: %d\n", error); + } - if (device_may_wakeup(dev)) - ts->wake_irq_enabled = (enable_irq_wake(client->irq) == 0); + if (device_may_wakeup(dev)) + ts->wake_irq_enabled = + (enable_irq_wake(client->irq) == 0); + } else { + elants_i2c_power_off(ts); + } return 0; } @@ -1216,13 +1348,19 @@ static int __maybe_unused elants_i2c_resume(struct device *dev) if (device_may_wakeup(dev) && ts->wake_irq_enabled) disable_irq_wake(client->irq); - for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) { - error = elants_i2c_send(client, set_active_cmd, - sizeof(set_active_cmd)); - if (!error) - break; + if (ts->keep_power_in_suspend) { + for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) { + error = elants_i2c_send(client, set_active_cmd, + sizeof(set_active_cmd)); + if (!error) + break; - dev_err(&client->dev, "resume command failed: %d\n", error); + dev_err(&client->dev, + "resume command failed: %d\n", error); + } + } else { + elants_i2c_power_on(ts); + elants_i2c_initialize(ts); } ts->state = ELAN_STATE_NORMAL; @@ -1261,10 +1399,10 @@ static struct i2c_driver elants_i2c_driver = { .id_table = elants_i2c_id, .driver = { .name = DEVICE_NAME, - .owner = THIS_MODULE, .pm = &elants_i2c_pm_ops, .acpi_match_table = ACPI_PTR(elants_acpi_id), .of_match_table = of_match_ptr(elants_of_match), + .probe_type = PROBE_PREFER_ASYNCHRONOUS, }, }; module_i2c_driver(elants_i2c_driver); diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c index e36162b28c2a..4d113c9e4b77 100644 --- a/drivers/input/touchscreen/goodix.c +++ b/drivers/input/touchscreen/goodix.c @@ -420,6 +420,7 @@ static const struct i2c_device_id goodix_ts_id[] = { { "GDIX1001:00", 0 }, { } }; +MODULE_DEVICE_TABLE(i2c, goodix_ts_id); #ifdef CONFIG_ACPI static const struct acpi_device_id goodix_acpi_match[] = { @@ -448,7 +449,6 @@ static struct i2c_driver goodix_ts_driver = { .id_table = goodix_ts_id, .driver = { .name = "Goodix-TS", - .owner = THIS_MODULE, .acpi_match_table = ACPI_PTR(goodix_acpi_match), .of_match_table = of_match_ptr(goodix_of_match), }, diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c index 586bee407d9a..ddf694b9fffc 100644 --- a/drivers/input/touchscreen/ili210x.c +++ b/drivers/input/touchscreen/ili210x.c @@ -343,7 +343,6 @@ MODULE_DEVICE_TABLE(i2c, ili210x_i2c_id); static struct i2c_driver ili210x_ts_driver = { .driver = { .name = "ili210x_i2c", - .owner = THIS_MODULE, .pm = &ili210x_i2c_pm, }, .id_table = ili210x_i2c_id, diff --git a/drivers/input/touchscreen/imx6ul_tsc.c b/drivers/input/touchscreen/imx6ul_tsc.c new file mode 100644 index 000000000000..ff0b75813daa --- /dev/null +++ b/drivers/input/touchscreen/imx6ul_tsc.c @@ -0,0 +1,523 @@ +/* + * Freescale i.MX6UL touchscreen controller driver + * + * Copyright (C) 2015 Freescale Semiconductor, 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. + */ + +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/gpio/consumer.h> +#include <linux/input.h> +#include <linux/slab.h> +#include <linux/completion.h> +#include <linux/delay.h> +#include <linux/of.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/io.h> + +/* ADC configuration registers field define */ +#define ADC_AIEN (0x1 << 7) +#define ADC_CONV_DISABLE 0x1F +#define ADC_CAL (0x1 << 7) +#define ADC_CALF 0x2 +#define ADC_12BIT_MODE (0x2 << 2) +#define ADC_IPG_CLK 0x00 +#define ADC_CLK_DIV_8 (0x03 << 5) +#define ADC_SHORT_SAMPLE_MODE (0x0 << 4) +#define ADC_HARDWARE_TRIGGER (0x1 << 13) +#define SELECT_CHANNEL_4 0x04 +#define SELECT_CHANNEL_1 0x01 +#define DISABLE_CONVERSION_INT (0x0 << 7) + +/* ADC registers */ +#define REG_ADC_HC0 0x00 +#define REG_ADC_HC1 0x04 +#define REG_ADC_HC2 0x08 +#define REG_ADC_HC3 0x0C +#define REG_ADC_HC4 0x10 +#define REG_ADC_HS 0x14 +#define REG_ADC_R0 0x18 +#define REG_ADC_CFG 0x2C +#define REG_ADC_GC 0x30 +#define REG_ADC_GS 0x34 + +#define ADC_TIMEOUT msecs_to_jiffies(100) + +/* TSC registers */ +#define REG_TSC_BASIC_SETING 0x00 +#define REG_TSC_PRE_CHARGE_TIME 0x10 +#define REG_TSC_FLOW_CONTROL 0x20 +#define REG_TSC_MEASURE_VALUE 0x30 +#define REG_TSC_INT_EN 0x40 +#define REG_TSC_INT_SIG_EN 0x50 +#define REG_TSC_INT_STATUS 0x60 +#define REG_TSC_DEBUG_MODE 0x70 +#define REG_TSC_DEBUG_MODE2 0x80 + +/* TSC configuration registers field define */ +#define DETECT_4_WIRE_MODE (0x0 << 4) +#define AUTO_MEASURE 0x1 +#define MEASURE_SIGNAL 0x1 +#define DETECT_SIGNAL (0x1 << 4) +#define VALID_SIGNAL (0x1 << 8) +#define MEASURE_INT_EN 0x1 +#define MEASURE_SIG_EN 0x1 +#define VALID_SIG_EN (0x1 << 8) +#define DE_GLITCH_2 (0x2 << 29) +#define START_SENSE (0x1 << 12) +#define TSC_DISABLE (0x1 << 16) +#define DETECT_MODE 0x2 + +struct imx6ul_tsc { + struct device *dev; + struct input_dev *input; + void __iomem *tsc_regs; + void __iomem *adc_regs; + struct clk *tsc_clk; + struct clk *adc_clk; + struct gpio_desc *xnur_gpio; + + int measure_delay_time; + int pre_charge_time; + + struct completion completion; +}; + +/* + * TSC module need ADC to get the measure value. So + * before config TSC, we should initialize ADC module. + */ +static void imx6ul_adc_init(struct imx6ul_tsc *tsc) +{ + int adc_hc = 0; + int adc_gc; + int adc_gs; + int adc_cfg; + int timeout; + + reinit_completion(&tsc->completion); + + adc_cfg = readl(tsc->adc_regs + REG_ADC_CFG); + adc_cfg |= ADC_12BIT_MODE | ADC_IPG_CLK; + adc_cfg |= ADC_CLK_DIV_8 | ADC_SHORT_SAMPLE_MODE; + adc_cfg &= ~ADC_HARDWARE_TRIGGER; + writel(adc_cfg, tsc->adc_regs + REG_ADC_CFG); + + /* enable calibration interrupt */ + adc_hc |= ADC_AIEN; + adc_hc |= ADC_CONV_DISABLE; + writel(adc_hc, tsc->adc_regs + REG_ADC_HC0); + + /* start ADC calibration */ + adc_gc = readl(tsc->adc_regs + REG_ADC_GC); + adc_gc |= ADC_CAL; + writel(adc_gc, tsc->adc_regs + REG_ADC_GC); + + timeout = wait_for_completion_timeout + (&tsc->completion, ADC_TIMEOUT); + if (timeout == 0) + dev_err(tsc->dev, "Timeout for adc calibration\n"); + + adc_gs = readl(tsc->adc_regs + REG_ADC_GS); + if (adc_gs & ADC_CALF) + dev_err(tsc->dev, "ADC calibration failed\n"); + + /* TSC need the ADC work in hardware trigger */ + adc_cfg = readl(tsc->adc_regs + REG_ADC_CFG); + adc_cfg |= ADC_HARDWARE_TRIGGER; + writel(adc_cfg, tsc->adc_regs + REG_ADC_CFG); +} + +/* + * This is a TSC workaround. Currently TSC misconnect two + * ADC channels, this function remap channel configure for + * hardware trigger. + */ +static void imx6ul_tsc_channel_config(struct imx6ul_tsc *tsc) +{ + int adc_hc0, adc_hc1, adc_hc2, adc_hc3, adc_hc4; + + adc_hc0 = DISABLE_CONVERSION_INT; + writel(adc_hc0, tsc->adc_regs + REG_ADC_HC0); + + adc_hc1 = DISABLE_CONVERSION_INT | SELECT_CHANNEL_4; + writel(adc_hc1, tsc->adc_regs + REG_ADC_HC1); + + adc_hc2 = DISABLE_CONVERSION_INT; + writel(adc_hc2, tsc->adc_regs + REG_ADC_HC2); + + adc_hc3 = DISABLE_CONVERSION_INT | SELECT_CHANNEL_1; + writel(adc_hc3, tsc->adc_regs + REG_ADC_HC3); + + adc_hc4 = DISABLE_CONVERSION_INT; + writel(adc_hc4, tsc->adc_regs + REG_ADC_HC4); +} + +/* + * TSC setting, confige the pre-charge time and measure delay time. + * different touch screen may need different pre-charge time and + * measure delay time. + */ +static void imx6ul_tsc_set(struct imx6ul_tsc *tsc) +{ + int basic_setting = 0; + int start; + + basic_setting |= tsc->measure_delay_time << 8; + basic_setting |= DETECT_4_WIRE_MODE | AUTO_MEASURE; + writel(basic_setting, tsc->tsc_regs + REG_TSC_BASIC_SETING); + + writel(DE_GLITCH_2, tsc->tsc_regs + REG_TSC_DEBUG_MODE2); + + writel(tsc->pre_charge_time, tsc->tsc_regs + REG_TSC_PRE_CHARGE_TIME); + writel(MEASURE_INT_EN, tsc->tsc_regs + REG_TSC_INT_EN); + writel(MEASURE_SIG_EN | VALID_SIG_EN, + tsc->tsc_regs + REG_TSC_INT_SIG_EN); + + /* start sense detection */ + start = readl(tsc->tsc_regs + REG_TSC_FLOW_CONTROL); + start |= START_SENSE; + start &= ~TSC_DISABLE; + writel(start, tsc->tsc_regs + REG_TSC_FLOW_CONTROL); +} + +static void imx6ul_tsc_init(struct imx6ul_tsc *tsc) +{ + imx6ul_adc_init(tsc); + imx6ul_tsc_channel_config(tsc); + imx6ul_tsc_set(tsc); +} + +static void imx6ul_tsc_disable(struct imx6ul_tsc *tsc) +{ + int tsc_flow; + int adc_cfg; + + /* TSC controller enters to idle status */ + tsc_flow = readl(tsc->tsc_regs + REG_TSC_FLOW_CONTROL); + tsc_flow |= TSC_DISABLE; + writel(tsc_flow, tsc->tsc_regs + REG_TSC_FLOW_CONTROL); + + /* ADC controller enters to stop mode */ + adc_cfg = readl(tsc->adc_regs + REG_ADC_HC0); + adc_cfg |= ADC_CONV_DISABLE; + writel(adc_cfg, tsc->adc_regs + REG_ADC_HC0); +} + +/* Delay some time (max 2ms), wait the pre-charge done. */ +static bool tsc_wait_detect_mode(struct imx6ul_tsc *tsc) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(2); + int state_machine; + int debug_mode2; + + do { + if (time_after(jiffies, timeout)) + return false; + + usleep_range(200, 400); + debug_mode2 = readl(tsc->tsc_regs + REG_TSC_DEBUG_MODE2); + state_machine = (debug_mode2 >> 20) & 0x7; + } while (state_machine != DETECT_MODE); + + usleep_range(200, 400); + return true; +} + +static irqreturn_t tsc_irq_fn(int irq, void *dev_id) +{ + struct imx6ul_tsc *tsc = dev_id; + int status; + int value; + int x, y; + int start; + + status = readl(tsc->tsc_regs + REG_TSC_INT_STATUS); + + /* write 1 to clear the bit measure-signal */ + writel(MEASURE_SIGNAL | DETECT_SIGNAL, + tsc->tsc_regs + REG_TSC_INT_STATUS); + + /* It's a HW self-clean bit. Set this bit and start sense detection */ + start = readl(tsc->tsc_regs + REG_TSC_FLOW_CONTROL); + start |= START_SENSE; + writel(start, tsc->tsc_regs + REG_TSC_FLOW_CONTROL); + + if (status & MEASURE_SIGNAL) { + value = readl(tsc->tsc_regs + REG_TSC_MEASURE_VALUE); + x = (value >> 16) & 0x0fff; + y = value & 0x0fff; + + /* + * In detect mode, we can get the xnur gpio value, + * otherwise assume contact is stiull active. + */ + if (!tsc_wait_detect_mode(tsc) || + gpiod_get_value_cansleep(tsc->xnur_gpio)) { + input_report_key(tsc->input, BTN_TOUCH, 1); + input_report_abs(tsc->input, ABS_X, x); + input_report_abs(tsc->input, ABS_Y, y); + } else { + input_report_key(tsc->input, BTN_TOUCH, 0); + } + + input_sync(tsc->input); + } + + return IRQ_HANDLED; +} + +static irqreturn_t adc_irq_fn(int irq, void *dev_id) +{ + struct imx6ul_tsc *tsc = dev_id; + int coco; + int value; + + coco = readl(tsc->adc_regs + REG_ADC_HS); + if (coco & 0x01) { + value = readl(tsc->adc_regs + REG_ADC_R0); + complete(&tsc->completion); + } + + return IRQ_HANDLED; +} + +static int imx6ul_tsc_open(struct input_dev *input_dev) +{ + struct imx6ul_tsc *tsc = input_get_drvdata(input_dev); + int err; + + err = clk_prepare_enable(tsc->adc_clk); + if (err) { + dev_err(tsc->dev, + "Could not prepare or enable the adc clock: %d\n", + err); + return err; + } + + err = clk_prepare_enable(tsc->tsc_clk); + if (err) { + dev_err(tsc->dev, + "Could not prepare or enable the tsc clock: %d\n", + err); + clk_disable_unprepare(tsc->adc_clk); + return err; + } + + imx6ul_tsc_init(tsc); + + return 0; +} + +static void imx6ul_tsc_close(struct input_dev *input_dev) +{ + struct imx6ul_tsc *tsc = input_get_drvdata(input_dev); + + imx6ul_tsc_disable(tsc); + + clk_disable_unprepare(tsc->tsc_clk); + clk_disable_unprepare(tsc->adc_clk); +} + +static int imx6ul_tsc_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct imx6ul_tsc *tsc; + struct input_dev *input_dev; + struct resource *tsc_mem; + struct resource *adc_mem; + int err; + int tsc_irq; + int adc_irq; + + tsc = devm_kzalloc(&pdev->dev, sizeof(struct imx6ul_tsc), GFP_KERNEL); + if (!tsc) + return -ENOMEM; + + input_dev = devm_input_allocate_device(&pdev->dev); + if (!input_dev) + return -ENOMEM; + + input_dev->name = "iMX6UL TouchScreen Controller"; + input_dev->id.bustype = BUS_HOST; + + input_dev->open = imx6ul_tsc_open; + input_dev->close = imx6ul_tsc_close; + + input_set_capability(input_dev, EV_KEY, BTN_TOUCH); + input_set_abs_params(input_dev, ABS_X, 0, 0xFFF, 0, 0); + input_set_abs_params(input_dev, ABS_Y, 0, 0xFFF, 0, 0); + + input_set_drvdata(input_dev, tsc); + + tsc->dev = &pdev->dev; + tsc->input = input_dev; + init_completion(&tsc->completion); + + tsc->xnur_gpio = devm_gpiod_get(&pdev->dev, "xnur", GPIOD_IN); + if (IS_ERR(tsc->xnur_gpio)) { + err = PTR_ERR(tsc->xnur_gpio); + dev_err(&pdev->dev, + "failed to request GPIO tsc_X- (xnur): %d\n", err); + return err; + } + + tsc_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + tsc->tsc_regs = devm_ioremap_resource(&pdev->dev, tsc_mem); + if (IS_ERR(tsc->tsc_regs)) { + err = PTR_ERR(tsc->tsc_regs); + dev_err(&pdev->dev, "failed to remap tsc memory: %d\n", err); + return err; + } + + adc_mem = platform_get_resource(pdev, IORESOURCE_MEM, 1); + tsc->adc_regs = devm_ioremap_resource(&pdev->dev, adc_mem); + if (IS_ERR(tsc->adc_regs)) { + err = PTR_ERR(tsc->adc_regs); + dev_err(&pdev->dev, "failed to remap adc memory: %d\n", err); + return err; + } + + tsc->tsc_clk = devm_clk_get(&pdev->dev, "tsc"); + if (IS_ERR(tsc->tsc_clk)) { + err = PTR_ERR(tsc->tsc_clk); + dev_err(&pdev->dev, "failed getting tsc clock: %d\n", err); + return err; + } + + tsc->adc_clk = devm_clk_get(&pdev->dev, "adc"); + if (IS_ERR(tsc->adc_clk)) { + err = PTR_ERR(tsc->adc_clk); + dev_err(&pdev->dev, "failed getting adc clock: %d\n", err); + return err; + } + + tsc_irq = platform_get_irq(pdev, 0); + if (tsc_irq < 0) { + dev_err(&pdev->dev, "no tsc irq resource?\n"); + return tsc_irq; + } + + adc_irq = platform_get_irq(pdev, 1); + if (adc_irq <= 0) { + dev_err(&pdev->dev, "no adc irq resource?\n"); + return adc_irq; + } + + err = devm_request_threaded_irq(tsc->dev, tsc_irq, + NULL, tsc_irq_fn, IRQF_ONESHOT, + dev_name(&pdev->dev), tsc); + if (err) { + dev_err(&pdev->dev, + "failed requesting tsc irq %d: %d\n", + tsc_irq, err); + return err; + } + + err = devm_request_irq(tsc->dev, adc_irq, adc_irq_fn, 0, + dev_name(&pdev->dev), tsc); + if (err) { + dev_err(&pdev->dev, + "failed requesting adc irq %d: %d\n", + adc_irq, err); + return err; + } + + err = of_property_read_u32(np, "measure-delay-time", + &tsc->measure_delay_time); + if (err) + tsc->measure_delay_time = 0xffff; + + err = of_property_read_u32(np, "pre-charge-time", + &tsc->pre_charge_time); + if (err) + tsc->pre_charge_time = 0xfff; + + err = input_register_device(tsc->input); + if (err) { + dev_err(&pdev->dev, + "failed to register input device: %d\n", err); + return err; + } + + platform_set_drvdata(pdev, tsc); + return 0; +} + +static int __maybe_unused imx6ul_tsc_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct imx6ul_tsc *tsc = platform_get_drvdata(pdev); + struct input_dev *input_dev = tsc->input; + + mutex_lock(&input_dev->mutex); + + if (input_dev->users) { + imx6ul_tsc_disable(tsc); + + clk_disable_unprepare(tsc->tsc_clk); + clk_disable_unprepare(tsc->adc_clk); + } + + mutex_unlock(&input_dev->mutex); + + return 0; +} + +static int __maybe_unused imx6ul_tsc_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct imx6ul_tsc *tsc = platform_get_drvdata(pdev); + struct input_dev *input_dev = tsc->input; + int retval = 0; + + mutex_lock(&input_dev->mutex); + + if (input_dev->users) { + retval = clk_prepare_enable(tsc->adc_clk); + if (retval) + goto out; + + retval = clk_prepare_enable(tsc->tsc_clk); + if (retval) { + clk_disable_unprepare(tsc->adc_clk); + goto out; + } + + imx6ul_tsc_init(tsc); + } + +out: + mutex_unlock(&input_dev->mutex); + return retval; +} + +static SIMPLE_DEV_PM_OPS(imx6ul_tsc_pm_ops, + imx6ul_tsc_suspend, imx6ul_tsc_resume); + +static const struct of_device_id imx6ul_tsc_match[] = { + { .compatible = "fsl,imx6ul-tsc", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, imx6ul_tsc_match); + +static struct platform_driver imx6ul_tsc_driver = { + .driver = { + .name = "imx6ul-tsc", + .of_match_table = imx6ul_tsc_match, + .pm = &imx6ul_tsc_pm_ops, + }, + .probe = imx6ul_tsc_probe, +}; +module_platform_driver(imx6ul_tsc_driver); + +MODULE_AUTHOR("Haibo Chen <haibo.chen@freescale.com>"); +MODULE_DESCRIPTION("Freescale i.MX6UL Touchscreen controller driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/max11801_ts.c b/drivers/input/touchscreen/max11801_ts.c index a68ec142ee9a..82079cde849c 100644 --- a/drivers/input/touchscreen/max11801_ts.c +++ b/drivers/input/touchscreen/max11801_ts.c @@ -229,7 +229,6 @@ MODULE_DEVICE_TABLE(i2c, max11801_ts_id); static struct i2c_driver max11801_ts_driver = { .driver = { .name = "max11801_ts", - .owner = THIS_MODULE, }, .id_table = max11801_ts_id, .probe = max11801_ts_probe, diff --git a/drivers/input/touchscreen/mms114.c b/drivers/input/touchscreen/mms114.c index 67c0d31613d8..7cce87650fc8 100644 --- a/drivers/input/touchscreen/mms114.c +++ b/drivers/input/touchscreen/mms114.c @@ -572,12 +572,12 @@ static const struct of_device_id mms114_dt_match[] = { { .compatible = "melfas,mms114" }, { } }; +MODULE_DEVICE_TABLE(of, mms114_dt_match); #endif static struct i2c_driver mms114_driver = { .driver = { .name = "mms114", - .owner = THIS_MODULE, .pm = &mms114_pm_ops, .of_match_table = of_match_ptr(mms114_dt_match), }, diff --git a/drivers/input/touchscreen/of_touchscreen.c b/drivers/input/touchscreen/of_touchscreen.c index 806cd0ad160f..bb6f2fe14667 100644 --- a/drivers/input/touchscreen/of_touchscreen.c +++ b/drivers/input/touchscreen/of_touchscreen.c @@ -9,12 +9,12 @@ * */ -#include <linux/of.h> +#include <linux/property.h> #include <linux/input.h> #include <linux/input/mt.h> #include <linux/input/touchscreen.h> -static bool touchscreen_get_prop_u32(struct device_node *np, +static bool touchscreen_get_prop_u32(struct device *dev, const char *property, unsigned int default_value, unsigned int *value) @@ -22,7 +22,7 @@ static bool touchscreen_get_prop_u32(struct device_node *np, u32 val; int error; - error = of_property_read_u32(np, property, &val); + error = device_property_read_u32(dev, property, &val); if (error) { *value = default_value; return false; @@ -39,13 +39,9 @@ static void touchscreen_set_params(struct input_dev *dev, struct input_absinfo *absinfo; if (!test_bit(axis, dev->absbit)) { - /* - * Emit a warning only if the axis is not a multitouch - * axis, which might not be set by the driver. - */ - if (!input_is_mt_axis(axis)) - dev_warn(&dev->dev, - "DT specifies parameters but the axis is not set up\n"); + dev_warn(&dev->dev, + "DT specifies parameters but the axis %lu is not set up\n", + axis); return; } @@ -55,52 +51,58 @@ static void touchscreen_set_params(struct input_dev *dev, } /** - * touchscreen_parse_of_params - parse common touchscreen DT properties - * @dev: device that should be parsed + * touchscreen_parse_properties - parse common touchscreen DT properties + * @input: input device that should be parsed + * @multitouch: specifies whether parsed properties should be applied to + * single-touch or multi-touch axes * * This function parses common DT properties for touchscreens and setups the - * input device accordingly. The function keeps previously setuped default + * input device accordingly. The function keeps previously set up default * values if no value is specified via DT. */ -void touchscreen_parse_of_params(struct input_dev *dev, bool multitouch) +void touchscreen_parse_properties(struct input_dev *input, bool multitouch) { - struct device_node *np = dev->dev.parent->of_node; + struct device *dev = input->dev.parent; unsigned int axis; unsigned int maximum, fuzz; bool data_present; - input_alloc_absinfo(dev); - if (!dev->absinfo) + input_alloc_absinfo(input); + if (!input->absinfo) return; axis = multitouch ? ABS_MT_POSITION_X : ABS_X; - data_present = touchscreen_get_prop_u32(np, "touchscreen-size-x", - input_abs_get_max(dev, axis), + data_present = touchscreen_get_prop_u32(dev, "touchscreen-size-x", + input_abs_get_max(input, + axis) + 1, &maximum) | - touchscreen_get_prop_u32(np, "touchscreen-fuzz-x", - input_abs_get_fuzz(dev, axis), + touchscreen_get_prop_u32(dev, "touchscreen-fuzz-x", + input_abs_get_fuzz(input, axis), &fuzz); if (data_present) - touchscreen_set_params(dev, axis, maximum, fuzz); + touchscreen_set_params(input, axis, maximum - 1, fuzz); axis = multitouch ? ABS_MT_POSITION_Y : ABS_Y; - data_present = touchscreen_get_prop_u32(np, "touchscreen-size-y", - input_abs_get_max(dev, axis), + data_present = touchscreen_get_prop_u32(dev, "touchscreen-size-y", + input_abs_get_max(input, + axis) + 1, &maximum) | - touchscreen_get_prop_u32(np, "touchscreen-fuzz-y", - input_abs_get_fuzz(dev, axis), + touchscreen_get_prop_u32(dev, "touchscreen-fuzz-y", + input_abs_get_fuzz(input, axis), &fuzz); if (data_present) - touchscreen_set_params(dev, axis, maximum, fuzz); + touchscreen_set_params(input, axis, maximum - 1, fuzz); axis = multitouch ? ABS_MT_PRESSURE : ABS_PRESSURE; - data_present = touchscreen_get_prop_u32(np, "touchscreen-max-pressure", - input_abs_get_max(dev, axis), + data_present = touchscreen_get_prop_u32(dev, + "touchscreen-max-pressure", + input_abs_get_max(input, axis), &maximum) | - touchscreen_get_prop_u32(np, "touchscreen-fuzz-pressure", - input_abs_get_fuzz(dev, axis), + touchscreen_get_prop_u32(dev, + "touchscreen-fuzz-pressure", + input_abs_get_fuzz(input, axis), &fuzz); if (data_present) - touchscreen_set_params(dev, axis, maximum, fuzz); + touchscreen_set_params(input, axis, maximum, fuzz); } -EXPORT_SYMBOL(touchscreen_parse_of_params); +EXPORT_SYMBOL(touchscreen_parse_properties); diff --git a/drivers/input/touchscreen/pixcir_i2c_ts.c b/drivers/input/touchscreen/pixcir_i2c_ts.c index 8f3e243a62bf..91621725bfb5 100644 --- a/drivers/input/touchscreen/pixcir_i2c_ts.c +++ b/drivers/input/touchscreen/pixcir_i2c_ts.c @@ -24,20 +24,23 @@ #include <linux/i2c.h> #include <linux/input.h> #include <linux/input/mt.h> -#include <linux/input/pixcir_ts.h> +#include <linux/input/touchscreen.h> #include <linux/gpio.h> -#include <linux/of.h> -#include <linux/of_gpio.h> +#include <linux/gpio/consumer.h> +/*#include <linux/of.h>*/ #include <linux/of_device.h> +#include <linux/platform_data/pixcir_i2c_ts.h> #define PIXCIR_MAX_SLOTS 5 /* Max fingers supported by driver */ struct pixcir_i2c_ts_data { struct i2c_client *client; struct input_dev *input; - const struct pixcir_ts_platform_data *pdata; - bool running; + struct gpio_desc *gpio_attb; + struct gpio_desc *gpio_reset; + const struct pixcir_i2c_chip_data *chip; int max_fingers; /* Max fingers supported in this instance */ + bool running; }; struct pixcir_touch { @@ -60,7 +63,7 @@ static void pixcir_ts_parse(struct pixcir_i2c_ts_data *tsdata, u8 touch; int ret, i; int readsize; - const struct pixcir_i2c_chip_data *chip = &tsdata->pdata->chip; + const struct pixcir_i2c_chip_data *chip = tsdata->chip; memset(report, 0, sizeof(struct pixcir_report_data)); @@ -113,13 +116,13 @@ static void pixcir_ts_report(struct pixcir_i2c_ts_data *ts, struct pixcir_touch *touch; int n, i, slot; struct device *dev = &ts->client->dev; - const struct pixcir_i2c_chip_data *chip = &ts->pdata->chip; + const struct pixcir_i2c_chip_data *chip = ts->chip; n = report->num_touches; if (n > PIXCIR_MAX_SLOTS) n = PIXCIR_MAX_SLOTS; - if (!chip->has_hw_ids) { + if (!ts->chip->has_hw_ids) { for (i = 0; i < n; i++) { touch = &report->touches[i]; pos[i].x = touch->x; @@ -161,7 +164,6 @@ static void pixcir_ts_report(struct pixcir_i2c_ts_data *ts, static irqreturn_t pixcir_ts_isr(int irq, void *dev_id) { struct pixcir_i2c_ts_data *tsdata = dev_id; - const struct pixcir_ts_platform_data *pdata = tsdata->pdata; struct pixcir_report_data report; while (tsdata->running) { @@ -171,7 +173,7 @@ static irqreturn_t pixcir_ts_isr(int irq, void *dev_id) /* report it */ pixcir_ts_report(tsdata, &report); - if (gpio_get_value(pdata->gpio_attb)) { + if (gpiod_get_value_cansleep(tsdata->gpio_attb)) { if (report.num_touches) { /* * Last report with no finger up? @@ -189,6 +191,17 @@ static irqreturn_t pixcir_ts_isr(int irq, void *dev_id) return IRQ_HANDLED; } +static void pixcir_reset(struct pixcir_i2c_ts_data *tsdata) +{ + if (!IS_ERR_OR_NULL(tsdata->gpio_reset)) { + gpiod_set_value_cansleep(tsdata->gpio_reset, 1); + ndelay(100); /* datasheet section 1.2.3 says 80ns min. */ + gpiod_set_value_cansleep(tsdata->gpio_reset, 0); + /* wait for controller ready. 100ms guess. */ + msleep(100); + } +} + static int pixcir_set_power_mode(struct pixcir_i2c_ts_data *ts, enum pixcir_power_mode mode) { @@ -411,85 +424,59 @@ static SIMPLE_DEV_PM_OPS(pixcir_dev_pm_ops, #ifdef CONFIG_OF static const struct of_device_id pixcir_of_match[]; -static struct pixcir_ts_platform_data *pixcir_parse_dt(struct device *dev) +static int pixcir_parse_dt(struct device *dev, + struct pixcir_i2c_ts_data *tsdata) { - struct pixcir_ts_platform_data *pdata; - struct device_node *np = dev->of_node; const struct of_device_id *match; match = of_match_device(of_match_ptr(pixcir_of_match), dev); if (!match) - return ERR_PTR(-EINVAL); - - pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) - return ERR_PTR(-ENOMEM); - - pdata->chip = *(const struct pixcir_i2c_chip_data *)match->data; - - pdata->gpio_attb = of_get_named_gpio(np, "attb-gpio", 0); - /* gpio_attb validity is checked in probe */ - - if (of_property_read_u32(np, "touchscreen-size-x", &pdata->x_max)) { - dev_err(dev, "Failed to get touchscreen-size-x property\n"); - return ERR_PTR(-EINVAL); - } - pdata->x_max -= 1; - - if (of_property_read_u32(np, "touchscreen-size-y", &pdata->y_max)) { - dev_err(dev, "Failed to get touchscreen-size-y property\n"); - return ERR_PTR(-EINVAL); - } - pdata->y_max -= 1; + return -EINVAL; - dev_dbg(dev, "%s: x %d, y %d, gpio %d\n", __func__, - pdata->x_max + 1, pdata->y_max + 1, pdata->gpio_attb); + tsdata->chip = (const struct pixcir_i2c_chip_data *)match->data; + if (!tsdata->chip) + return -EINVAL; - return pdata; + return 0; } #else -static struct pixcir_ts_platform_data *pixcir_parse_dt(struct device *dev) +static int pixcir_parse_dt(struct device *dev, + struct pixcir_i2c_ts_data *tsdata) { - return ERR_PTR(-EINVAL); + return -EINVAL; } #endif static int pixcir_i2c_ts_probe(struct i2c_client *client, - const struct i2c_device_id *id) + const struct i2c_device_id *id) { const struct pixcir_ts_platform_data *pdata = dev_get_platdata(&client->dev); struct device *dev = &client->dev; - struct device_node *np = dev->of_node; struct pixcir_i2c_ts_data *tsdata; struct input_dev *input; int error; - if (np && !pdata) { - pdata = pixcir_parse_dt(dev); - if (IS_ERR(pdata)) - return PTR_ERR(pdata); - } + tsdata = devm_kzalloc(dev, sizeof(*tsdata), GFP_KERNEL); + if (!tsdata) + return -ENOMEM; - if (!pdata) { + if (pdata) { + tsdata->chip = &pdata->chip; + } else if (dev->of_node) { + error = pixcir_parse_dt(dev, tsdata); + if (error) + return error; + } else { dev_err(&client->dev, "platform data not defined\n"); return -EINVAL; } - if (!gpio_is_valid(pdata->gpio_attb)) { - dev_err(dev, "Invalid gpio_attb in pdata\n"); + if (!tsdata->chip->max_fingers) { + dev_err(dev, "Invalid max_fingers in chip data\n"); return -EINVAL; } - if (!pdata->chip.max_fingers) { - dev_err(dev, "Invalid max_fingers in pdata\n"); - return -EINVAL; - } - - tsdata = devm_kzalloc(dev, sizeof(*tsdata), GFP_KERNEL); - if (!tsdata) - return -ENOMEM; - input = devm_input_allocate_device(dev); if (!input) { dev_err(dev, "Failed to allocate input device\n"); @@ -498,7 +485,6 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client, tsdata->client = client; tsdata->input = input; - tsdata->pdata = pdata; input->name = client->name; input->id.bustype = BUS_I2C; @@ -506,15 +492,21 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client, input->close = pixcir_input_close; input->dev.parent = &client->dev; - __set_bit(EV_KEY, input->evbit); - __set_bit(EV_ABS, input->evbit); - __set_bit(BTN_TOUCH, input->keybit); - input_set_abs_params(input, ABS_X, 0, pdata->x_max, 0, 0); - input_set_abs_params(input, ABS_Y, 0, pdata->y_max, 0, 0); - input_set_abs_params(input, ABS_MT_POSITION_X, 0, pdata->x_max, 0, 0); - input_set_abs_params(input, ABS_MT_POSITION_Y, 0, pdata->y_max, 0, 0); + if (pdata) { + input_set_abs_params(input, ABS_MT_POSITION_X, 0, pdata->x_max, 0, 0); + input_set_abs_params(input, ABS_MT_POSITION_Y, 0, pdata->y_max, 0, 0); + } else { + input_set_capability(input, EV_ABS, ABS_MT_POSITION_X); + input_set_capability(input, EV_ABS, ABS_MT_POSITION_Y); + touchscreen_parse_properties(input, true); + if (!input_abs_get_max(input, ABS_MT_POSITION_X) || + !input_abs_get_max(input, ABS_MT_POSITION_Y)) { + dev_err(dev, "Touchscreen size is not specified\n"); + return -EINVAL; + } + } - tsdata->max_fingers = tsdata->pdata->chip.max_fingers; + tsdata->max_fingers = tsdata->chip->max_fingers; if (tsdata->max_fingers > PIXCIR_MAX_SLOTS) { tsdata->max_fingers = PIXCIR_MAX_SLOTS; dev_info(dev, "Limiting maximum fingers to %d\n", @@ -530,10 +522,18 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client, input_set_drvdata(input, tsdata); - error = devm_gpio_request_one(dev, pdata->gpio_attb, - GPIOF_DIR_IN, "pixcir_i2c_attb"); - if (error) { - dev_err(dev, "Failed to request ATTB gpio\n"); + tsdata->gpio_attb = devm_gpiod_get(dev, "attb", GPIOD_IN); + if (IS_ERR(tsdata->gpio_attb)) { + error = PTR_ERR(tsdata->gpio_attb); + dev_err(dev, "Failed to request ATTB gpio: %d\n", error); + return error; + } + + tsdata->gpio_reset = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(tsdata->gpio_reset)) { + error = PTR_ERR(tsdata->gpio_reset); + dev_err(dev, "Failed to request RESET gpio: %d\n", error); return error; } @@ -545,6 +545,8 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client, return error; } + pixcir_reset(tsdata); + /* Always be in IDLE mode to save power, device supports auto wake */ error = pixcir_set_power_mode(tsdata, PIXCIR_POWER_IDLE); if (error) { @@ -602,7 +604,6 @@ MODULE_DEVICE_TABLE(of, pixcir_of_match); static struct i2c_driver pixcir_i2c_ts_driver = { .driver = { - .owner = THIS_MODULE, .name = "pixcir_ts", .pm = &pixcir_dev_pm_ops, .of_match_table = of_match_ptr(pixcir_of_match), diff --git a/drivers/input/touchscreen/st1232.c b/drivers/input/touchscreen/st1232.c index 697e26e52d54..e943678ce54c 100644 --- a/drivers/input/touchscreen/st1232.c +++ b/drivers/input/touchscreen/st1232.c @@ -296,7 +296,6 @@ static struct i2c_driver st1232_ts_driver = { .id_table = st1232_ts_id, .driver = { .name = ST1232_TS_NAME, - .owner = THIS_MODULE, .of_match_table = of_match_ptr(st1232_ts_dt_ids), .pm = &st1232_ts_pm_ops, }, diff --git a/drivers/input/touchscreen/sun4i-ts.c b/drivers/input/touchscreen/sun4i-ts.c index c0116994067d..485794376ee5 100644 --- a/drivers/input/touchscreen/sun4i-ts.c +++ b/drivers/input/touchscreen/sun4i-ts.c @@ -191,7 +191,7 @@ static void sun4i_ts_close(struct input_dev *dev) writel(TEMP_IRQ_EN(1), ts->base + TP_INT_FIFOC); } -static int sun4i_get_temp(const struct sun4i_ts_data *ts, long *temp) +static int sun4i_get_temp(const struct sun4i_ts_data *ts, int *temp) { /* No temp_data until the first irq */ if (ts->temp_data == -1) @@ -202,7 +202,7 @@ static int sun4i_get_temp(const struct sun4i_ts_data *ts, long *temp) return 0; } -static int sun4i_get_tz_temp(void *data, long *temp) +static int sun4i_get_tz_temp(void *data, int *temp) { return sun4i_get_temp(data, temp); } @@ -215,14 +215,14 @@ static ssize_t show_temp(struct device *dev, struct device_attribute *devattr, char *buf) { struct sun4i_ts_data *ts = dev_get_drvdata(dev); - long temp; + int temp; int error; error = sun4i_get_temp(ts, &temp); if (error) return error; - return sprintf(buf, "%ld\n", temp); + return sprintf(buf, "%d\n", temp); } static ssize_t show_temp_label(struct device *dev, diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c index 8be7b9b79f20..3f117637e832 100644 --- a/drivers/input/touchscreen/sur40.c +++ b/drivers/input/touchscreen/sur40.c @@ -581,6 +581,7 @@ static int sur40_probe(struct usb_interface *interface, sur40->alloc_ctx = vb2_dma_sg_init_ctx(sur40->dev); if (IS_ERR(sur40->alloc_ctx)) { dev_err(sur40->dev, "Can't allocate buffer context"); + error = PTR_ERR(sur40->alloc_ctx); goto err_unreg_v4l2; } diff --git a/drivers/input/touchscreen/tsc2005.c b/drivers/input/touchscreen/tsc2005.c index d8c025b0f88c..0f65d02eeb26 100644 --- a/drivers/input/touchscreen/tsc2005.c +++ b/drivers/input/touchscreen/tsc2005.c @@ -30,10 +30,11 @@ #include <linux/delay.h> #include <linux/pm.h> #include <linux/of.h> -#include <linux/of_gpio.h> #include <linux/spi/spi.h> #include <linux/spi/tsc2005.h> #include <linux/regulator/consumer.h> +#include <linux/regmap.h> +#include <linux/gpio/consumer.h> /* * The touchscreen interface operates as follows: @@ -61,16 +62,24 @@ #define TSC2005_CMD_12BIT 0x04 /* control byte 0 */ -#define TSC2005_REG_READ 0x0001 -#define TSC2005_REG_PND0 0x0002 -#define TSC2005_REG_X 0x0000 -#define TSC2005_REG_Y 0x0008 -#define TSC2005_REG_Z1 0x0010 -#define TSC2005_REG_Z2 0x0018 -#define TSC2005_REG_TEMP_HIGH 0x0050 -#define TSC2005_REG_CFR0 0x0060 -#define TSC2005_REG_CFR1 0x0068 -#define TSC2005_REG_CFR2 0x0070 +#define TSC2005_REG_READ 0x01 /* R/W access */ +#define TSC2005_REG_PND0 0x02 /* Power Not Down Control */ +#define TSC2005_REG_X (0x0 << 3) +#define TSC2005_REG_Y (0x1 << 3) +#define TSC2005_REG_Z1 (0x2 << 3) +#define TSC2005_REG_Z2 (0x3 << 3) +#define TSC2005_REG_AUX (0x4 << 3) +#define TSC2005_REG_TEMP1 (0x5 << 3) +#define TSC2005_REG_TEMP2 (0x6 << 3) +#define TSC2005_REG_STATUS (0x7 << 3) +#define TSC2005_REG_AUX_HIGH (0x8 << 3) +#define TSC2005_REG_AUX_LOW (0x9 << 3) +#define TSC2005_REG_TEMP_HIGH (0xA << 3) +#define TSC2005_REG_TEMP_LOW (0xB << 3) +#define TSC2005_REG_CFR0 (0xC << 3) +#define TSC2005_REG_CFR1 (0xD << 3) +#define TSC2005_REG_CFR2 (0xE << 3) +#define TSC2005_REG_CONV_FUNC (0xF << 3) /* configuration register 0 */ #define TSC2005_CFR0_PRECHARGE_276US 0x0040 @@ -112,20 +121,37 @@ #define TSC2005_SPI_MAX_SPEED_HZ 10000000 #define TSC2005_PENUP_TIME_MS 40 -struct tsc2005_spi_rd { - struct spi_transfer spi_xfer; - u32 spi_tx; - u32 spi_rx; +static const struct regmap_range tsc2005_writable_ranges[] = { + regmap_reg_range(TSC2005_REG_AUX_HIGH, TSC2005_REG_CFR2), }; +static const struct regmap_access_table tsc2005_writable_table = { + .yes_ranges = tsc2005_writable_ranges, + .n_yes_ranges = ARRAY_SIZE(tsc2005_writable_ranges), +}; + +static struct regmap_config tsc2005_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .reg_stride = 0x08, + .max_register = 0x78, + .read_flag_mask = TSC2005_REG_READ, + .write_flag_mask = TSC2005_REG_PND0, + .wr_table = &tsc2005_writable_table, + .use_single_rw = true, +}; + +struct tsc2005_data { + u16 x; + u16 y; + u16 z1; + u16 z2; +} __packed; +#define TSC2005_DATA_REGS 4 + struct tsc2005 { struct spi_device *spi; - - struct spi_message spi_read_msg; - struct tsc2005_spi_rd spi_x; - struct tsc2005_spi_rd spi_y; - struct tsc2005_spi_rd spi_z1; - struct tsc2005_spi_rd spi_z2; + struct regmap *regmap; struct input_dev *idev; char phys[32]; @@ -154,7 +180,7 @@ struct tsc2005 { struct regulator *vio; - int reset_gpio; + struct gpio_desc *reset_gpio; void (*set_reset)(bool enable); }; @@ -182,62 +208,6 @@ static int tsc2005_cmd(struct tsc2005 *ts, u8 cmd) return 0; } -static int tsc2005_write(struct tsc2005 *ts, u8 reg, u16 value) -{ - u32 tx = ((reg | TSC2005_REG_PND0) << 16) | value; - struct spi_transfer xfer = { - .tx_buf = &tx, - .len = 4, - .bits_per_word = 24, - }; - struct spi_message msg; - int error; - - spi_message_init(&msg); - spi_message_add_tail(&xfer, &msg); - - error = spi_sync(ts->spi, &msg); - if (error) { - dev_err(&ts->spi->dev, - "%s: failed, register: %x, value: %x, error: %d\n", - __func__, reg, value, error); - return error; - } - - return 0; -} - -static void tsc2005_setup_read(struct tsc2005_spi_rd *rd, u8 reg, bool last) -{ - memset(rd, 0, sizeof(*rd)); - - rd->spi_tx = (reg | TSC2005_REG_READ) << 16; - rd->spi_xfer.tx_buf = &rd->spi_tx; - rd->spi_xfer.rx_buf = &rd->spi_rx; - rd->spi_xfer.len = 4; - rd->spi_xfer.bits_per_word = 24; - rd->spi_xfer.cs_change = !last; -} - -static int tsc2005_read(struct tsc2005 *ts, u8 reg, u16 *value) -{ - struct tsc2005_spi_rd spi_rd; - struct spi_message msg; - int error; - - tsc2005_setup_read(&spi_rd, reg, true); - - spi_message_init(&msg); - spi_message_add_tail(&spi_rd.spi_xfer, &msg); - - error = spi_sync(ts->spi, &msg); - if (error) - return error; - - *value = spi_rd.spi_rx; - return 0; -} - static void tsc2005_update_pen_state(struct tsc2005 *ts, int x, int y, int pressure) { @@ -266,26 +236,23 @@ static irqreturn_t tsc2005_irq_thread(int irq, void *_ts) struct tsc2005 *ts = _ts; unsigned long flags; unsigned int pressure; - u32 x, y; - u32 z1, z2; + struct tsc2005_data tsdata; int error; /* read the coordinates */ - error = spi_sync(ts->spi, &ts->spi_read_msg); + error = regmap_bulk_read(ts->regmap, TSC2005_REG_X, &tsdata, + TSC2005_DATA_REGS); if (unlikely(error)) goto out; - x = ts->spi_x.spi_rx; - y = ts->spi_y.spi_rx; - z1 = ts->spi_z1.spi_rx; - z2 = ts->spi_z2.spi_rx; - /* validate position */ - if (unlikely(x > MAX_12BIT || y > MAX_12BIT)) + if (unlikely(tsdata.x > MAX_12BIT || tsdata.y > MAX_12BIT)) goto out; /* Skip reading if the pressure components are out of range */ - if (unlikely(z1 == 0 || z2 > MAX_12BIT || z1 >= z2)) + if (unlikely(tsdata.z1 == 0 || tsdata.z2 > MAX_12BIT)) + goto out; + if (unlikely(tsdata.z1 >= tsdata.z2)) goto out; /* @@ -293,8 +260,8 @@ static irqreturn_t tsc2005_irq_thread(int irq, void *_ts) * the value before pen-up - that implies SPI fed us stale data */ if (!ts->pen_down && - ts->in_x == x && ts->in_y == y && - ts->in_z1 == z1 && ts->in_z2 == z2) { + ts->in_x == tsdata.x && ts->in_y == tsdata.y && + ts->in_z1 == tsdata.z1 && ts->in_z2 == tsdata.z2) { goto out; } @@ -302,20 +269,20 @@ static irqreturn_t tsc2005_irq_thread(int irq, void *_ts) * At this point we are happy we have a valid and useful reading. * Remember it for later comparisons. We may now begin downsampling. */ - ts->in_x = x; - ts->in_y = y; - ts->in_z1 = z1; - ts->in_z2 = z2; + ts->in_x = tsdata.x; + ts->in_y = tsdata.y; + ts->in_z1 = tsdata.z1; + ts->in_z2 = tsdata.z2; /* Compute touch pressure resistance using equation #1 */ - pressure = x * (z2 - z1) / z1; + pressure = tsdata.x * (tsdata.z2 - tsdata.z1) / tsdata.z1; pressure = pressure * ts->x_plate_ohm / 4096; if (unlikely(pressure > MAX_12BIT)) goto out; spin_lock_irqsave(&ts->lock, flags); - tsc2005_update_pen_state(ts, x, y, pressure); + tsc2005_update_pen_state(ts, tsdata.x, tsdata.y, pressure); mod_timer(&ts->penup_timer, jiffies + msecs_to_jiffies(TSC2005_PENUP_TIME_MS)); @@ -338,9 +305,9 @@ static void tsc2005_penup_timer(unsigned long data) static void tsc2005_start_scan(struct tsc2005 *ts) { - tsc2005_write(ts, TSC2005_REG_CFR0, TSC2005_CFR0_INITVALUE); - tsc2005_write(ts, TSC2005_REG_CFR1, TSC2005_CFR1_INITVALUE); - tsc2005_write(ts, TSC2005_REG_CFR2, TSC2005_CFR2_INITVALUE); + regmap_write(ts->regmap, TSC2005_REG_CFR0, TSC2005_CFR0_INITVALUE); + regmap_write(ts->regmap, TSC2005_REG_CFR1, TSC2005_CFR1_INITVALUE); + regmap_write(ts->regmap, TSC2005_REG_CFR2, TSC2005_CFR2_INITVALUE); tsc2005_cmd(ts, TSC2005_CMD_NORMAL); } @@ -351,8 +318,8 @@ static void tsc2005_stop_scan(struct tsc2005 *ts) static void tsc2005_set_reset(struct tsc2005 *ts, bool enable) { - if (ts->reset_gpio >= 0) - gpio_set_value(ts->reset_gpio, enable); + if (ts->reset_gpio) + gpiod_set_value_cansleep(ts->reset_gpio, enable); else if (ts->set_reset) ts->set_reset(enable); } @@ -388,11 +355,10 @@ static ssize_t tsc2005_selftest_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct spi_device *spi = to_spi_device(dev); - struct tsc2005 *ts = spi_get_drvdata(spi); - u16 temp_high; - u16 temp_high_orig; - u16 temp_high_test; + struct tsc2005 *ts = dev_get_drvdata(dev); + unsigned int temp_high; + unsigned int temp_high_orig; + unsigned int temp_high_test; bool success = true; int error; @@ -403,7 +369,7 @@ static ssize_t tsc2005_selftest_show(struct device *dev, */ __tsc2005_disable(ts); - error = tsc2005_read(ts, TSC2005_REG_TEMP_HIGH, &temp_high_orig); + error = regmap_read(ts->regmap, TSC2005_REG_TEMP_HIGH, &temp_high_orig); if (error) { dev_warn(dev, "selftest failed: read error %d\n", error); success = false; @@ -412,14 +378,14 @@ static ssize_t tsc2005_selftest_show(struct device *dev, temp_high_test = (temp_high_orig - 1) & MAX_12BIT; - error = tsc2005_write(ts, TSC2005_REG_TEMP_HIGH, temp_high_test); + error = regmap_write(ts->regmap, TSC2005_REG_TEMP_HIGH, temp_high_test); if (error) { dev_warn(dev, "selftest failed: write error %d\n", error); success = false; goto out; } - error = tsc2005_read(ts, TSC2005_REG_TEMP_HIGH, &temp_high); + error = regmap_read(ts->regmap, TSC2005_REG_TEMP_HIGH, &temp_high); if (error) { dev_warn(dev, "selftest failed: read error %d after write\n", error); @@ -442,7 +408,7 @@ static ssize_t tsc2005_selftest_show(struct device *dev, goto out; /* test that the reset really happened */ - error = tsc2005_read(ts, TSC2005_REG_TEMP_HIGH, &temp_high); + error = regmap_read(ts->regmap, TSC2005_REG_TEMP_HIGH, &temp_high); if (error) { dev_warn(dev, "selftest failed: read error %d after reset\n", error); @@ -474,8 +440,7 @@ static umode_t tsc2005_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n) { struct device *dev = container_of(kobj, struct device, kobj); - struct spi_device *spi = to_spi_device(dev); - struct tsc2005 *ts = spi_get_drvdata(spi); + struct tsc2005 *ts = dev_get_drvdata(dev); umode_t mode = attr->mode; if (attr == &dev_attr_selftest.attr) { @@ -495,7 +460,7 @@ static void tsc2005_esd_work(struct work_struct *work) { struct tsc2005 *ts = container_of(work, struct tsc2005, esd_work.work); int error; - u16 r; + unsigned int r; if (!mutex_trylock(&ts->mutex)) { /* @@ -511,7 +476,7 @@ static void tsc2005_esd_work(struct work_struct *work) goto out; /* We should be able to read register without disabling interrupts. */ - error = tsc2005_read(ts, TSC2005_REG_CFR0, &r); + error = regmap_read(ts->regmap, TSC2005_REG_CFR0, &r); if (!error && !((r ^ TSC2005_CFR0_INITVALUE) & TSC2005_CFR0_RW_MASK)) { goto out; @@ -575,20 +540,6 @@ static void tsc2005_close(struct input_dev *input) mutex_unlock(&ts->mutex); } -static void tsc2005_setup_spi_xfer(struct tsc2005 *ts) -{ - tsc2005_setup_read(&ts->spi_x, TSC2005_REG_X, false); - tsc2005_setup_read(&ts->spi_y, TSC2005_REG_Y, false); - tsc2005_setup_read(&ts->spi_z1, TSC2005_REG_Z1, false); - tsc2005_setup_read(&ts->spi_z2, TSC2005_REG_Z2, true); - - spi_message_init(&ts->spi_read_msg); - spi_message_add_tail(&ts->spi_x.spi_xfer, &ts->spi_read_msg); - spi_message_add_tail(&ts->spi_y.spi_xfer, &ts->spi_read_msg); - spi_message_add_tail(&ts->spi_z1.spi_xfer, &ts->spi_read_msg); - spi_message_add_tail(&ts->spi_z2.spi_xfer, &ts->spi_read_msg); -} - static int tsc2005_probe(struct spi_device *spi) { const struct tsc2005_platform_data *pdata = dev_get_platdata(&spi->dev); @@ -653,37 +604,30 @@ static int tsc2005_probe(struct spi_device *spi) ts->spi = spi; ts->idev = input_dev; + ts->regmap = devm_regmap_init_spi(spi, &tsc2005_regmap_config); + if (IS_ERR(ts->regmap)) + return PTR_ERR(ts->regmap); + ts->x_plate_ohm = x_plate_ohm; ts->esd_timeout = esd_timeout; - if (np) { - ts->reset_gpio = of_get_named_gpio(np, "reset-gpios", 0); - if (ts->reset_gpio == -EPROBE_DEFER) - return ts->reset_gpio; - if (ts->reset_gpio < 0) { - dev_err(&spi->dev, "error acquiring reset gpio: %d\n", - ts->reset_gpio); - return ts->reset_gpio; - } + ts->reset_gpio = devm_gpiod_get_optional(&spi->dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(ts->reset_gpio)) { + error = PTR_ERR(ts->reset_gpio); + dev_err(&spi->dev, "error acquiring reset gpio: %d\n", error); + return error; + } - error = devm_gpio_request_one(&spi->dev, ts->reset_gpio, 0, - "reset-gpios"); - if (error) { - dev_err(&spi->dev, "error requesting reset gpio: %d\n", - error); - return error; - } + ts->vio = devm_regulator_get_optional(&spi->dev, "vio"); + if (IS_ERR(ts->vio)) { + error = PTR_ERR(ts->vio); + dev_err(&spi->dev, "vio regulator missing (%d)", error); + return error; + } - ts->vio = devm_regulator_get(&spi->dev, "vio"); - if (IS_ERR(ts->vio)) { - error = PTR_ERR(ts->vio); - dev_err(&spi->dev, "vio regulator missing (%d)", error); - return error; - } - } else { - ts->reset_gpio = -1; + if (!ts->reset_gpio && pdata) ts->set_reset = pdata->set_reset; - } mutex_init(&ts->mutex); @@ -692,8 +636,6 @@ static int tsc2005_probe(struct spi_device *spi) INIT_DELAYED_WORK(&ts->esd_work, tsc2005_esd_work); - tsc2005_setup_spi_xfer(ts); - snprintf(ts->phys, sizeof(ts->phys), "%s/input-ts", dev_name(&spi->dev)); @@ -709,7 +651,7 @@ static int tsc2005_probe(struct spi_device *spi) input_set_abs_params(input_dev, ABS_PRESSURE, 0, max_p, fudge_p, 0); if (np) - touchscreen_parse_of_params(input_dev, false); + touchscreen_parse_properties(input_dev, false); input_dev->open = tsc2005_open; input_dev->close = tsc2005_close; @@ -735,7 +677,7 @@ static int tsc2005_probe(struct spi_device *spi) return error; } - spi_set_drvdata(spi, ts); + dev_set_drvdata(&spi->dev, ts); error = sysfs_create_group(&spi->dev.kobj, &tsc2005_attr_group); if (error) { dev_err(&spi->dev, @@ -763,7 +705,7 @@ disable_regulator: static int tsc2005_remove(struct spi_device *spi) { - struct tsc2005 *ts = spi_get_drvdata(spi); + struct tsc2005 *ts = dev_get_drvdata(&spi->dev); sysfs_remove_group(&spi->dev.kobj, &tsc2005_attr_group); @@ -775,8 +717,7 @@ static int tsc2005_remove(struct spi_device *spi) static int __maybe_unused tsc2005_suspend(struct device *dev) { - struct spi_device *spi = to_spi_device(dev); - struct tsc2005 *ts = spi_get_drvdata(spi); + struct tsc2005 *ts = dev_get_drvdata(dev); mutex_lock(&ts->mutex); @@ -792,8 +733,7 @@ static int __maybe_unused tsc2005_suspend(struct device *dev) static int __maybe_unused tsc2005_resume(struct device *dev) { - struct spi_device *spi = to_spi_device(dev); - struct tsc2005 *ts = spi_get_drvdata(spi); + struct tsc2005 *ts = dev_get_drvdata(dev); mutex_lock(&ts->mutex); diff --git a/drivers/input/touchscreen/tsc2007.c b/drivers/input/touchscreen/tsc2007.c index ccc8aa615709..5d0cd51c6f41 100644 --- a/drivers/input/touchscreen/tsc2007.c +++ b/drivers/input/touchscreen/tsc2007.c @@ -482,7 +482,6 @@ MODULE_DEVICE_TABLE(of, tsc2007_of_match); static struct i2c_driver tsc2007_driver = { .driver = { - .owner = THIS_MODULE, .name = "tsc2007", .of_match_table = of_match_ptr(tsc2007_of_match), }, diff --git a/drivers/input/touchscreen/wacom_i2c.c b/drivers/input/touchscreen/wacom_i2c.c index 32f8ac003936..8d7a2852caef 100644 --- a/drivers/input/touchscreen/wacom_i2c.c +++ b/drivers/input/touchscreen/wacom_i2c.c @@ -271,7 +271,6 @@ MODULE_DEVICE_TABLE(i2c, wacom_i2c_id); static struct i2c_driver wacom_i2c_driver = { .driver = { .name = "wacom_i2c", - .owner = THIS_MODULE, .pm = &wacom_i2c_pm, }, diff --git a/drivers/input/touchscreen/wdt87xx_i2c.c b/drivers/input/touchscreen/wdt87xx_i2c.c index fb92ae1c5fae..515c20a6e10f 100644 --- a/drivers/input/touchscreen/wdt87xx_i2c.c +++ b/drivers/input/touchscreen/wdt87xx_i2c.c @@ -23,7 +23,7 @@ #include <asm/unaligned.h> #define WDT87XX_NAME "wdt87xx_i2c" -#define WDT87XX_DRV_VER "0.9.6" +#define WDT87XX_DRV_VER "0.9.7" #define WDT87XX_FW_NAME "wdt87xx_fw.bin" #define WDT87XX_CFG_NAME "wdt87xx_cfg.bin" @@ -85,6 +85,11 @@ #define CTL_PARAM_OFFSET_PHY_H 24 #define CTL_PARAM_OFFSET_FACTOR 32 +/* The definition of the device descriptor */ +#define WDT_GD_DEVICE 1 +#define DEV_DESC_OFFSET_VID 8 +#define DEV_DESC_OFFSET_PID 10 + /* Communication commands */ #define PACKET_SIZE 56 #define VND_REQ_READ 0x06 @@ -152,6 +157,7 @@ /* Controller requires minimum 300us between commands */ #define WDT_COMMAND_DELAY_MS 2 #define WDT_FLASH_WRITE_DELAY_MS 4 +#define WDT_FW_RESET_TIME 2500 struct wdt87xx_sys_param { u16 fw_id; @@ -165,6 +171,8 @@ struct wdt87xx_sys_param { u16 scaling_factor; u32 max_x; u32 max_y; + u16 vendor_id; + u16 product_id; }; struct wdt87xx_data { @@ -208,6 +216,32 @@ static int wdt87xx_i2c_xfer(struct i2c_client *client, return 0; } +static int wdt87xx_get_desc(struct i2c_client *client, u8 desc_idx, + u8 *buf, size_t len) +{ + u8 tx_buf[] = { 0x22, 0x00, 0x10, 0x0E, 0x23, 0x00 }; + int error; + + tx_buf[2] |= desc_idx & 0xF; + + error = wdt87xx_i2c_xfer(client, tx_buf, sizeof(tx_buf), + buf, len); + if (error) { + dev_err(&client->dev, "get desc failed: %d\n", error); + return error; + } + + if (buf[0] != len) { + dev_err(&client->dev, "unexpected response to get desc: %d\n", + buf[0]); + return -EINVAL; + } + + mdelay(WDT_COMMAND_DELAY_MS); + + return 0; +} + static int wdt87xx_get_string(struct i2c_client *client, u8 str_idx, u8 *buf, size_t len) { @@ -373,7 +407,7 @@ static int wdt87xx_sw_reset(struct i2c_client *client) } /* Wait the device to be ready */ - msleep(200); + msleep(WDT_FW_RESET_TIME); return 0; } @@ -403,6 +437,15 @@ static int wdt87xx_get_sysparam(struct i2c_client *client, u8 buf[PKT_READ_SIZE]; int error; + error = wdt87xx_get_desc(client, WDT_GD_DEVICE, buf, 18); + if (error) { + dev_err(&client->dev, "failed to get device desc\n"); + return error; + } + + param->vendor_id = get_unaligned_le16(buf + DEV_DESC_OFFSET_VID); + param->product_id = get_unaligned_le16(buf + DEV_DESC_OFFSET_PID); + error = wdt87xx_get_string(client, STRIDX_PARAMETERS, buf, 34); if (error) { dev_err(&client->dev, "failed to get parameters\n"); @@ -994,6 +1037,8 @@ static int wdt87xx_ts_create_input_device(struct wdt87xx_data *wdt) input->name = "WDT87xx Touchscreen"; input->id.bustype = BUS_I2C; + input->id.vendor = wdt->param.vendor_id; + input->id.product = wdt->param.product_id; input->phys = wdt->phys; input_set_abs_params(input, ABS_MT_POSITION_X, 0, diff --git a/drivers/input/touchscreen/wm97xx-core.c b/drivers/input/touchscreen/wm97xx-core.c index b1ae77995968..1534e9b0788c 100644 --- a/drivers/input/touchscreen/wm97xx-core.c +++ b/drivers/input/touchscreen/wm97xx-core.c @@ -732,8 +732,7 @@ static int wm97xx_remove(struct device *dev) return 0; } -#ifdef CONFIG_PM -static int wm97xx_suspend(struct device *dev, pm_message_t state) +static int __maybe_unused wm97xx_suspend(struct device *dev) { struct wm97xx *wm = dev_get_drvdata(dev); u16 reg; @@ -765,7 +764,7 @@ static int wm97xx_suspend(struct device *dev, pm_message_t state) return 0; } -static int wm97xx_resume(struct device *dev) +static int __maybe_unused wm97xx_resume(struct device *dev) { struct wm97xx *wm = dev_get_drvdata(dev); @@ -799,10 +798,7 @@ static int wm97xx_resume(struct device *dev) return 0; } -#else -#define wm97xx_suspend NULL -#define wm97xx_resume NULL -#endif +static SIMPLE_DEV_PM_OPS(wm97xx_pm_ops, wm97xx_suspend, wm97xx_resume); /* * Machine specific operations @@ -836,8 +832,7 @@ static struct device_driver wm97xx_driver = { .owner = THIS_MODULE, .probe = wm97xx_probe, .remove = wm97xx_remove, - .suspend = wm97xx_suspend, - .resume = wm97xx_resume, + .pm = &wm97xx_pm_ops, }; static int __init wm97xx_init(void) diff --git a/drivers/input/touchscreen/zforce_ts.c b/drivers/input/touchscreen/zforce_ts.c index f58a196521a9..781d0f83050a 100644 --- a/drivers/input/touchscreen/zforce_ts.c +++ b/drivers/input/touchscreen/zforce_ts.c @@ -24,14 +24,13 @@ #include <linux/interrupt.h> #include <linux/i2c.h> #include <linux/delay.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/device.h> #include <linux/sysfs.h> #include <linux/input/mt.h> #include <linux/platform_data/zforce_ts.h> #include <linux/regulator/consumer.h> #include <linux/of.h> -#include <linux/of_gpio.h> #define WAIT_TIMEOUT msecs_to_jiffies(1000) @@ -120,6 +119,9 @@ struct zforce_ts { struct regulator *reg_vdd; + struct gpio_desc *gpio_int; + struct gpio_desc *gpio_rst; + bool suspending; bool suspended; bool boot_complete; @@ -161,6 +163,16 @@ static int zforce_command(struct zforce_ts *ts, u8 cmd) return 0; } +static void zforce_reset_assert(struct zforce_ts *ts) +{ + gpiod_set_value_cansleep(ts->gpio_rst, 1); +} + +static void zforce_reset_deassert(struct zforce_ts *ts) +{ + gpiod_set_value_cansleep(ts->gpio_rst, 0); +} + static int zforce_send_wait(struct zforce_ts *ts, const char *buf, int len) { struct i2c_client *client = ts->client; @@ -479,7 +491,6 @@ static irqreturn_t zforce_irq_thread(int irq, void *dev_id) { struct zforce_ts *ts = dev_id; struct i2c_client *client = ts->client; - const struct zforce_ts_platdata *pdata = ts->pdata; int ret; u8 payload_buffer[FRAME_MAXSIZE]; u8 *payload; @@ -499,7 +510,16 @@ static irqreturn_t zforce_irq_thread(int irq, void *dev_id) if (!ts->suspending && device_may_wakeup(&client->dev)) pm_stay_awake(&client->dev); - while (!gpio_get_value(pdata->gpio_int)) { + /* + * Run at least once and exit the loop if + * - the optional interrupt GPIO isn't specified + * (there is only one packet read per ISR invocation, then) + * or + * - the GPIO isn't active any more + * (packet read until the level GPIO indicates that there is + * no IRQ any more) + */ + do { ret = zforce_read_packet(ts, payload_buffer); if (ret < 0) { dev_err(&client->dev, @@ -566,7 +586,7 @@ static irqreturn_t zforce_irq_thread(int irq, void *dev_id) payload[RESPONSE_ID]); break; } - } + } while (gpiod_get_value_cansleep(ts->gpio_int)); if (!ts->suspending && device_may_wakeup(&client->dev)) pm_relax(&client->dev); @@ -690,7 +710,7 @@ static void zforce_reset(void *data) { struct zforce_ts *ts = data; - gpio_set_value(ts->pdata->gpio_rst, 0); + zforce_reset_assert(ts); udelay(10); @@ -712,18 +732,6 @@ static struct zforce_ts_platdata *zforce_parse_dt(struct device *dev) return ERR_PTR(-ENOMEM); } - pdata->gpio_int = of_get_gpio(np, 0); - if (!gpio_is_valid(pdata->gpio_int)) { - dev_err(dev, "failed to get interrupt gpio\n"); - return ERR_PTR(-EINVAL); - } - - pdata->gpio_rst = of_get_gpio(np, 1); - if (!gpio_is_valid(pdata->gpio_rst)) { - dev_err(dev, "failed to get reset gpio\n"); - return ERR_PTR(-EINVAL); - } - if (of_property_read_u32(np, "x-size", &pdata->x_max)) { dev_err(dev, "failed to get x-size property\n"); return ERR_PTR(-EINVAL); @@ -755,20 +763,49 @@ static int zforce_probe(struct i2c_client *client, if (!ts) return -ENOMEM; - ret = devm_gpio_request_one(&client->dev, pdata->gpio_int, GPIOF_IN, - "zforce_ts_int"); - if (ret) { - dev_err(&client->dev, "request of gpio %d failed, %d\n", - pdata->gpio_int, ret); + ts->gpio_rst = devm_gpiod_get_optional(&client->dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(ts->gpio_rst)) { + ret = PTR_ERR(ts->gpio_rst); + dev_err(&client->dev, + "failed to request reset GPIO: %d\n", ret); return ret; } - ret = devm_gpio_request_one(&client->dev, pdata->gpio_rst, - GPIOF_OUT_INIT_LOW, "zforce_ts_rst"); - if (ret) { - dev_err(&client->dev, "request of gpio %d failed, %d\n", - pdata->gpio_rst, ret); - return ret; + if (ts->gpio_rst) { + ts->gpio_int = devm_gpiod_get_optional(&client->dev, "irq", + GPIOD_IN); + if (IS_ERR(ts->gpio_int)) { + ret = PTR_ERR(ts->gpio_int); + dev_err(&client->dev, + "failed to request interrupt GPIO: %d\n", ret); + return ret; + } + } else { + /* + * Deprecated GPIO handling for compatibility + * with legacy binding. + */ + + /* INT GPIO */ + ts->gpio_int = devm_gpiod_get_index(&client->dev, NULL, 0, + GPIOD_IN); + if (IS_ERR(ts->gpio_int)) { + ret = PTR_ERR(ts->gpio_int); + dev_err(&client->dev, + "failed to request interrupt GPIO: %d\n", ret); + return ret; + } + + /* RST GPIO */ + ts->gpio_rst = devm_gpiod_get_index(&client->dev, NULL, 1, + GPIOD_OUT_HIGH); + if (IS_ERR(ts->gpio_rst)) { + ret = PTR_ERR(ts->gpio_rst); + dev_err(&client->dev, + "failed to request reset GPIO: %d\n", ret); + return ret; + } } ts->reg_vdd = devm_regulator_get_optional(&client->dev, "vdd"); @@ -863,7 +900,7 @@ static int zforce_probe(struct i2c_client *client, i2c_set_clientdata(client, ts); /* let the controller boot */ - gpio_set_value(pdata->gpio_rst, 1); + zforce_reset_deassert(ts); ts->command_waiting = NOTIFICATION_BOOTCOMPLETE; if (wait_for_completion_timeout(&ts->command_done, WAIT_TIMEOUT) == 0) @@ -917,7 +954,6 @@ MODULE_DEVICE_TABLE(of, zforce_dt_idtable); static struct i2c_driver zforce_driver = { .driver = { - .owner = THIS_MODULE, .name = "zforce-ts", .pm = &zforce_pm_ops, .of_match_table = of_match_ptr(zforce_dt_idtable), |