From 839850f4fb76b56fcad3cabe27fc9f1a03821a2c Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 8 Dec 2015 16:32:08 +0100 Subject: input: adp5589-keys: use gpiochip data pointer This makes the driver use the data pointer added to the gpio_chip to store a pointer to the state container instead of relying on container_of(). Acked-by: Michael Hennerich Acked-by: Dmitry Torokhov Signed-off-by: Linus Walleij --- drivers/input/keyboard/adp5589-keys.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/adp5589-keys.c b/drivers/input/keyboard/adp5589-keys.c index c01a1d648f9f..32d94c63dc33 100644 --- a/drivers/input/keyboard/adp5589-keys.c +++ b/drivers/input/keyboard/adp5589-keys.c @@ -387,7 +387,7 @@ static int adp5589_write(struct i2c_client *client, u8 reg, u8 val) #ifdef CONFIG_GPIOLIB static int adp5589_gpio_get_value(struct gpio_chip *chip, unsigned off) { - struct adp5589_kpad *kpad = container_of(chip, struct adp5589_kpad, gc); + struct adp5589_kpad *kpad = gpiochip_get_data(chip); unsigned int bank = kpad->var->bank(kpad->gpiomap[off]); unsigned int bit = kpad->var->bit(kpad->gpiomap[off]); @@ -399,7 +399,7 @@ static int adp5589_gpio_get_value(struct gpio_chip *chip, unsigned off) static void adp5589_gpio_set_value(struct gpio_chip *chip, unsigned off, int val) { - struct adp5589_kpad *kpad = container_of(chip, struct adp5589_kpad, gc); + struct adp5589_kpad *kpad = gpiochip_get_data(chip); unsigned int bank = kpad->var->bank(kpad->gpiomap[off]); unsigned int bit = kpad->var->bit(kpad->gpiomap[off]); @@ -418,7 +418,7 @@ static void adp5589_gpio_set_value(struct gpio_chip *chip, static int adp5589_gpio_direction_input(struct gpio_chip *chip, unsigned off) { - struct adp5589_kpad *kpad = container_of(chip, struct adp5589_kpad, gc); + struct adp5589_kpad *kpad = gpiochip_get_data(chip); unsigned int bank = kpad->var->bank(kpad->gpiomap[off]); unsigned int bit = kpad->var->bit(kpad->gpiomap[off]); int ret; @@ -438,7 +438,7 @@ static int adp5589_gpio_direction_input(struct gpio_chip *chip, unsigned off) static int adp5589_gpio_direction_output(struct gpio_chip *chip, unsigned off, int val) { - struct adp5589_kpad *kpad = container_of(chip, struct adp5589_kpad, gc); + struct adp5589_kpad *kpad = gpiochip_get_data(chip); unsigned int bank = kpad->var->bank(kpad->gpiomap[off]); unsigned int bit = kpad->var->bit(kpad->gpiomap[off]); int ret; @@ -525,9 +525,9 @@ static int adp5589_gpio_add(struct adp5589_kpad *kpad) mutex_init(&kpad->gpio_lock); - error = gpiochip_add(&kpad->gc); + error = gpiochip_add_data(&kpad->gc, kpad); if (error) { - dev_err(dev, "gpiochip_add failed, err: %d\n", error); + dev_err(dev, "gpiochip_add_data() failed, err: %d\n", error); return error; } -- cgit v1.2.3 From d94780444bf3da8fc35e281aec6bbffa54a4a01a Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 8 Dec 2015 16:35:27 +0100 Subject: input: ad7879: use gpiochip data pointer This makes the driver use the data pointer added to the gpio_chip to store a pointer to the state container instead of relying on container_of(). Acked-by: Michael Hennerich Acked-by: Dmitry Torokhov Signed-off-by: Linus Walleij --- drivers/input/touchscreen/ad7879.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/ad7879.c b/drivers/input/touchscreen/ad7879.c index 69d299d5dd00..e4bf1103e6f8 100644 --- a/drivers/input/touchscreen/ad7879.c +++ b/drivers/input/touchscreen/ad7879.c @@ -379,7 +379,7 @@ static const struct attribute_group ad7879_attr_group = { static int ad7879_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) { - struct ad7879 *ts = container_of(chip, struct ad7879, gc); + struct ad7879 *ts = gpiochip_get_data(chip); int err; mutex_lock(&ts->mutex); @@ -393,7 +393,7 @@ static int ad7879_gpio_direction_input(struct gpio_chip *chip, static int ad7879_gpio_direction_output(struct gpio_chip *chip, unsigned gpio, int level) { - struct ad7879 *ts = container_of(chip, struct ad7879, gc); + struct ad7879 *ts = gpiochip_get_data(chip); int err; mutex_lock(&ts->mutex); @@ -412,7 +412,7 @@ static int ad7879_gpio_direction_output(struct gpio_chip *chip, static int ad7879_gpio_get_value(struct gpio_chip *chip, unsigned gpio) { - struct ad7879 *ts = container_of(chip, struct ad7879, gc); + struct ad7879 *ts = gpiochip_get_data(chip); u16 val; mutex_lock(&ts->mutex); @@ -425,7 +425,7 @@ static int ad7879_gpio_get_value(struct gpio_chip *chip, unsigned gpio) static void ad7879_gpio_set_value(struct gpio_chip *chip, unsigned gpio, int value) { - struct ad7879 *ts = container_of(chip, struct ad7879, gc); + struct ad7879 *ts = gpiochip_get_data(chip); mutex_lock(&ts->mutex); if (value) @@ -456,7 +456,7 @@ static int ad7879_gpio_add(struct ad7879 *ts, ts->gc.owner = THIS_MODULE; ts->gc.parent = ts->dev; - ret = gpiochip_add(&ts->gc); + ret = gpiochip_add_data(&ts->gc, ts); if (ret) dev_err(ts->dev, "failed to register gpio %d\n", ts->gc.base); -- cgit v1.2.3 From 59e21e3d00e6bc23186763c3e0bf11baf8924124 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Mon, 4 Apr 2016 22:24:59 +0200 Subject: x86/cpufeature: Replace cpu_has_tsc with boot_cpu_has() usage Signed-off-by: Borislav Petkov Cc: Andy Lutomirski Cc: Borislav Petkov Cc: Brian Gerst Cc: Denys Vlasenko Cc: Dmitry Torokhov Cc: H. Peter Anvin Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: Thomas Sailer Link: http://lkml.kernel.org/r/1459801503-15600-7-git-send-email-bp@alien8.de Signed-off-by: Ingo Molnar --- drivers/input/joystick/analog.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/joystick/analog.c b/drivers/input/joystick/analog.c index 6f8b084e13d0..3d8ff09eba57 100644 --- a/drivers/input/joystick/analog.c +++ b/drivers/input/joystick/analog.c @@ -143,9 +143,9 @@ struct analog_port { #include -#define GET_TIME(x) do { if (cpu_has_tsc) x = (unsigned int)rdtsc(); else x = get_time_pit(); } while (0) -#define DELTA(x,y) (cpu_has_tsc ? ((y) - (x)) : ((x) - (y) + ((x) < (y) ? PIT_TICK_RATE / HZ : 0))) -#define TIME_NAME (cpu_has_tsc?"TSC":"PIT") +#define GET_TIME(x) do { if (boot_cpu_has(X86_FEATURE_TSC)) x = (unsigned int)rdtsc(); else x = get_time_pit(); } while (0) +#define DELTA(x,y) (boot_cpu_has(X86_FEATURE_TSC) ? ((y) - (x)) : ((x) - (y) + ((x) < (y) ? PIT_TICK_RATE / HZ : 0))) +#define TIME_NAME (boot_cpu_has(X86_FEATURE_TSC)?"TSC":"PIT") static unsigned int get_time_pit(void) { unsigned long flags; -- cgit v1.2.3 From d3baee37f1488b0dea726597bb12a0086b233f8b Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 8 Dec 2015 16:29:50 +0100 Subject: input: adp5588-keys: use gpiochip data pointer This makes the driver use the data pointer added to the gpio_chip to store a pointer to the state container instead of relying on container_of(). Cc: Michael Hennerich Acked-by: Michael Hennerich Acked-by: Dmitry Torokhov Signed-off-by: Linus Walleij --- drivers/input/keyboard/adp5588-keys.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/adp5588-keys.c b/drivers/input/keyboard/adp5588-keys.c index 21a62d0fa764..53fe9a3fb620 100644 --- a/drivers/input/keyboard/adp5588-keys.c +++ b/drivers/input/keyboard/adp5588-keys.c @@ -73,7 +73,7 @@ static int adp5588_write(struct i2c_client *client, u8 reg, u8 val) #ifdef CONFIG_GPIOLIB static int adp5588_gpio_get_value(struct gpio_chip *chip, unsigned off) { - struct adp5588_kpad *kpad = container_of(chip, struct adp5588_kpad, gc); + struct adp5588_kpad *kpad = gpiochip_get_data(chip); unsigned int bank = ADP5588_BANK(kpad->gpiomap[off]); unsigned int bit = ADP5588_BIT(kpad->gpiomap[off]); int val; @@ -93,7 +93,7 @@ static int adp5588_gpio_get_value(struct gpio_chip *chip, unsigned off) static void adp5588_gpio_set_value(struct gpio_chip *chip, unsigned off, int val) { - struct adp5588_kpad *kpad = container_of(chip, struct adp5588_kpad, gc); + struct adp5588_kpad *kpad = gpiochip_get_data(chip); unsigned int bank = ADP5588_BANK(kpad->gpiomap[off]); unsigned int bit = ADP5588_BIT(kpad->gpiomap[off]); @@ -112,7 +112,7 @@ static void adp5588_gpio_set_value(struct gpio_chip *chip, static int adp5588_gpio_direction_input(struct gpio_chip *chip, unsigned off) { - struct adp5588_kpad *kpad = container_of(chip, struct adp5588_kpad, gc); + struct adp5588_kpad *kpad = gpiochip_get_data(chip); unsigned int bank = ADP5588_BANK(kpad->gpiomap[off]); unsigned int bit = ADP5588_BIT(kpad->gpiomap[off]); int ret; @@ -130,7 +130,7 @@ static int adp5588_gpio_direction_input(struct gpio_chip *chip, unsigned off) static int adp5588_gpio_direction_output(struct gpio_chip *chip, unsigned off, int val) { - struct adp5588_kpad *kpad = container_of(chip, struct adp5588_kpad, gc); + struct adp5588_kpad *kpad = gpiochip_get_data(chip); unsigned int bank = ADP5588_BANK(kpad->gpiomap[off]); unsigned int bit = ADP5588_BIT(kpad->gpiomap[off]); int ret; @@ -210,7 +210,7 @@ static int adp5588_gpio_add(struct adp5588_kpad *kpad) mutex_init(&kpad->gpio_lock); - error = gpiochip_add(&kpad->gc); + error = gpiochip_add_data(&kpad->gc, kpad); if (error) { dev_err(dev, "gpiochip_add failed, err: %d\n", error); return error; -- cgit v1.2.3 From c01e01597cbd0cf0571c2b05bf9e2245afb4478d Mon Sep 17 00:00:00 2001 From: Masanari Iida Date: Wed, 20 Apr 2016 00:27:33 +0900 Subject: treewide: Fix typos in printk This patch fix spelling typos in printk from various part of the codes. Signed-off-by: Masanari Iida Acked-by: Randy Dunlap Signed-off-by: Jiri Kosina --- drivers/input/touchscreen/cyttsp4_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/cyttsp4_core.c b/drivers/input/touchscreen/cyttsp4_core.c index 5ed31057430c..44deca88c579 100644 --- a/drivers/input/touchscreen/cyttsp4_core.c +++ b/drivers/input/touchscreen/cyttsp4_core.c @@ -1499,7 +1499,7 @@ static int cyttsp4_core_sleep_(struct cyttsp4 *cd) if (IS_BOOTLOADER(mode[0], mode[1])) { mutex_unlock(&cd->system_lock); - dev_err(cd->dev, "%s: Device in BOOTLADER mode.\n", __func__); + dev_err(cd->dev, "%s: Device in BOOTLOADER mode.\n", __func__); rc = -EINVAL; goto error; } -- cgit v1.2.3 From ad4e9fec143c453cae145d7d5ccac902c37fb912 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 14 Apr 2016 21:17:25 +0200 Subject: input: misc: max77693: Use pwm_get_args() where appropriate The PWM framework has clarified the concept of reference PWM config (the platform dependent config retrieved from the DT or the PWM lookup table) and real PWM state. Use pwm_get_args() when the PWM user wants to retrieve this reference config and not the current state. This is part of the rework allowing the PWM framework to support hardware readout and expose real PWM state even when the PWM has just been requested (before the user calls pwm_config/enable/disable()). Signed-off-by: Boris Brezillon Signed-off-by: Thierry Reding --- drivers/input/misc/max77693-haptic.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/misc/max77693-haptic.c b/drivers/input/misc/max77693-haptic.c index 6d96bff32a0e..29ddeb7be84b 100644 --- a/drivers/input/misc/max77693-haptic.c +++ b/drivers/input/misc/max77693-haptic.c @@ -70,10 +70,13 @@ struct max77693_haptic { static int max77693_haptic_set_duty_cycle(struct max77693_haptic *haptic) { - int delta = (haptic->pwm_dev->period + haptic->pwm_duty) / 2; + struct pwm_args pargs; + int delta; int error; - error = pwm_config(haptic->pwm_dev, delta, haptic->pwm_dev->period); + pwm_get_args(haptic->pwm_dev, &pargs); + delta = (pargs.period + haptic->pwm_duty) / 2; + error = pwm_config(haptic->pwm_dev, delta, pargs.period); if (error) { dev_err(haptic->dev, "failed to configure pwm: %d\n", error); return error; @@ -234,6 +237,7 @@ static int max77693_haptic_play_effect(struct input_dev *dev, void *data, struct ff_effect *effect) { struct max77693_haptic *haptic = input_get_drvdata(dev); + struct pwm_args pargs; u64 period_mag_multi; haptic->magnitude = effect->u.rumble.strong_magnitude; @@ -245,7 +249,8 @@ static int max77693_haptic_play_effect(struct input_dev *dev, void *data, * The formula to convert magnitude to pwm_duty as follows: * - pwm_duty = (magnitude * pwm_period) / MAX_MAGNITUDE(0xFFFF) */ - period_mag_multi = (u64)haptic->pwm_dev->period * haptic->magnitude; + pwm_get_args(haptic->pwm_dev, &pargs); + period_mag_multi = (u64)pargs.period * haptic->magnitude; haptic->pwm_duty = (unsigned int)(period_mag_multi >> MAX_MAGNITUDE_SHIFT); @@ -329,6 +334,12 @@ static int max77693_haptic_probe(struct platform_device *pdev) return PTR_ERR(haptic->pwm_dev); } + /* + * FIXME: pwm_apply_args() should be removed when switching to the + * atomic PWM API. + */ + pwm_apply_args(haptic->pwm_dev); + haptic->motor_reg = devm_regulator_get(&pdev->dev, "haptic"); if (IS_ERR(haptic->motor_reg)) { dev_err(&pdev->dev, "failed to get regulator\n"); -- cgit v1.2.3 From de93170b85d7ee2a6a6a881c019160a65adeb636 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 14 Apr 2016 21:17:33 +0200 Subject: input: misc: max8997: Explicitly apply PWM config extracted from pwm_args Call pwm_apply_args() just after requesting the PWM device so that the polarity and period are initialized according to the information provided in pwm_args. This is an intermediate state, and pwm_apply_args() should be dropped as soon as the atomic PWM infrastructure is in place and the driver makes use of it. Signed-off-by: Boris Brezillon Signed-off-by: Thierry Reding --- drivers/input/misc/max8997_haptic.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/misc/max8997_haptic.c b/drivers/input/misc/max8997_haptic.c index a806ba3818f7..bf17f654ed88 100644 --- a/drivers/input/misc/max8997_haptic.c +++ b/drivers/input/misc/max8997_haptic.c @@ -304,6 +304,12 @@ static int max8997_haptic_probe(struct platform_device *pdev) error); goto err_free_mem; } + + /* + * FIXME: pwm_apply_args() should be removed when switching to + * the atomic PWM API. + */ + pwm_apply_args(chip->pwm); break; default: -- cgit v1.2.3 From cfae56f18c7cbdb0542025b0d3f000d9ba0152d0 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 14 Apr 2016 21:17:34 +0200 Subject: input: misc: pwm-beeper: Explicitly apply PWM config extracted from pwm_args Call pwm_apply_args() just after requesting the PWM device so that the polarity and period are initialized according to the information provided in pwm_args. This is an intermediate state, and pwm_apply_args() should be dropped as soon as the atomic PWM infrastructure is in place and the driver makes use of it. Signed-off-by: Boris Brezillon Signed-off-by: Thierry Reding --- drivers/input/misc/pwm-beeper.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/misc/pwm-beeper.c b/drivers/input/misc/pwm-beeper.c index f2261ab54701..8d7133268745 100644 --- a/drivers/input/misc/pwm-beeper.c +++ b/drivers/input/misc/pwm-beeper.c @@ -87,6 +87,12 @@ static int pwm_beeper_probe(struct platform_device *pdev) goto err_free; } + /* + * FIXME: pwm_apply_args() should be removed when switching to + * the atomic PWM API. + */ + pwm_apply_args(beeper->pwm); + beeper->input = input_allocate_device(); if (!beeper->input) { dev_err(&pdev->dev, "Failed to allocate input device\n"); -- cgit v1.2.3 From e28d0c9cd381e115c2270bf1d6fe4de95289234a Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Wed, 9 Mar 2016 13:05:42 -0800 Subject: input: convert sun4i-ts to use devm_thermal_zone_of_sensor_register This changes the driver to use the devm_ version of thermal_zone_of_sensor_register and cleans up the local points and unregister calls. Cc: Dmitry Torokhov Cc: Maxime Ripard Cc: Chen-Yu Tsai Cc: Hans de Goede Cc: Zhang Rui Cc: Lukasz Majewski Cc: Heiko Stuebner Cc: Sascha Hauer Cc: Jens Thiele Cc: linux-input@vger.kernel.org Cc: linux-arm-kernel@lists.infradead.org Cc: linux-kernel@vger.kernel.org Acked-by: Dmitry Torokhov Acked-by: Hans de Goede Signed-off-by: Eduardo Valentin --- drivers/input/touchscreen/sun4i-ts.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/sun4i-ts.c b/drivers/input/touchscreen/sun4i-ts.c index 485794376ee5..d07dd29d4848 100644 --- a/drivers/input/touchscreen/sun4i-ts.c +++ b/drivers/input/touchscreen/sun4i-ts.c @@ -115,7 +115,6 @@ struct sun4i_ts_data { struct device *dev; struct input_dev *input; - struct thermal_zone_device *tz; void __iomem *base; unsigned int irq; bool ignore_fifo_data; @@ -366,10 +365,7 @@ static int sun4i_ts_probe(struct platform_device *pdev) if (IS_ERR(hwmon)) return PTR_ERR(hwmon); - ts->tz = thermal_zone_of_sensor_register(ts->dev, 0, ts, - &sun4i_ts_tz_ops); - if (IS_ERR(ts->tz)) - ts->tz = NULL; + devm_thermal_zone_of_sensor_register(ts->dev, 0, ts, &sun4i_ts_tz_ops); writel(TEMP_IRQ_EN(1), ts->base + TP_INT_FIFOC); @@ -377,7 +373,6 @@ static int sun4i_ts_probe(struct platform_device *pdev) error = input_register_device(ts->input); if (error) { writel(0, ts->base + TP_INT_FIFOC); - thermal_zone_of_sensor_unregister(ts->dev, ts->tz); return error; } } @@ -394,8 +389,6 @@ static int sun4i_ts_remove(struct platform_device *pdev) if (ts->input) input_unregister_device(ts->input); - thermal_zone_of_sensor_unregister(ts->dev, ts->tz); - /* Deactivate all IRQs */ writel(0, ts->base + TP_INT_FIFOC); -- cgit v1.2.3 From e7c38f221fb256b9ec452531416cca06ebb6d245 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Sat, 4 Jun 2016 10:10:01 +0300 Subject: input: clps711x-keypad: Changing the compatibility string to match with the smallest supported chip This patch changes the compatibility string to match with the smallest supported chip (EP7209). Since the DT-support for this CPU is not yet announced, this change is safe. Signed-off-by: Alexander Shiyan Signed-off-by: Arnd Bergmann --- drivers/input/keyboard/clps711x-keypad.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/clps711x-keypad.c b/drivers/input/keyboard/clps711x-keypad.c index b637f1af842e..997e3e97f573 100644 --- a/drivers/input/keyboard/clps711x-keypad.c +++ b/drivers/input/keyboard/clps711x-keypad.c @@ -101,7 +101,7 @@ static int clps711x_keypad_probe(struct platform_device *pdev) return -ENOMEM; priv->syscon = - syscon_regmap_lookup_by_compatible("cirrus,clps711x-syscon1"); + syscon_regmap_lookup_by_compatible("cirrus,ep7209-syscon1"); if (IS_ERR(priv->syscon)) return PTR_ERR(priv->syscon); @@ -181,7 +181,7 @@ static int clps711x_keypad_remove(struct platform_device *pdev) } static const struct of_device_id clps711x_keypad_of_match[] = { - { .compatible = "cirrus,clps711x-keypad", }, + { .compatible = "cirrus,ep7209-keypad", }, { } }; MODULE_DEVICE_TABLE(of, clps711x_keypad_of_match); -- cgit v1.2.3 From 0e2f511894ceca86d2cc006a363a1be0b17ca359 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 16 Dec 2015 08:34:37 -0200 Subject: [media] sur40: set q->dev instead of allocating a context Stop using alloc_ctx and just fill in the device pointer. Signed-off-by: Hans Verkuil Cc: Florian Echtler Signed-off-by: Mauro Carvalho Chehab --- drivers/input/touchscreen/sur40.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c index 880c40b23f66..cc4bd3e53b58 100644 --- a/drivers/input/touchscreen/sur40.c +++ b/drivers/input/touchscreen/sur40.c @@ -151,7 +151,6 @@ struct sur40_state { struct mutex lock; struct vb2_queue queue; - struct vb2_alloc_ctx *alloc_ctx; struct list_head buf_list; spinlock_t qlock; int sequence; @@ -580,19 +579,13 @@ static int sur40_probe(struct usb_interface *interface, sur40->queue = sur40_queue; sur40->queue.drv_priv = sur40; sur40->queue.lock = &sur40->lock; + sur40->queue.dev = sur40->dev; /* initialize the queue */ error = vb2_queue_init(&sur40->queue); if (error) goto err_unreg_v4l2; - 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; - } - sur40->vdev = sur40_video_device; sur40->vdev.v4l2_dev = &sur40->v4l2; sur40->vdev.lock = &sur40->lock; @@ -633,7 +626,6 @@ static void sur40_disconnect(struct usb_interface *interface) video_unregister_device(&sur40->vdev); v4l2_device_unregister(&sur40->v4l2); - vb2_dma_sg_cleanup_ctx(sur40->alloc_ctx); input_unregister_polled_device(sur40->input); input_free_polled_device(sur40->input); @@ -655,11 +647,8 @@ static int sur40_queue_setup(struct vb2_queue *q, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) { - struct sur40_state *sur40 = vb2_get_drv_priv(q); - if (q->num_buffers + *nbuffers < 3) *nbuffers = 3 - q->num_buffers; - alloc_ctxs[0] = sur40->alloc_ctx; if (*nplanes) return sizes[0] < sur40_video_format.sizeimage ? -EINVAL : 0; -- cgit v1.2.3 From 36c0f8b32c4bd4f668cedfba6d97afaa84f055fb Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 15 Apr 2016 09:15:05 -0300 Subject: [media] vb2: replace void *alloc_ctxs by struct device *alloc_devs Make this a proper typed array. Drop the old allocate context code since that is no longer used. Note that the memops functions now get a struct device pointer instead of the struct device ** that was there initially (actually a void pointer to a struct containing only a struct device pointer). This code is now a lot cleaner. Signed-off-by: Hans Verkuil Reviewed-by: Laurent Pinchart Cc: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/input/touchscreen/sur40.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c index cc4bd3e53b58..f124f3af0de1 100644 --- a/drivers/input/touchscreen/sur40.c +++ b/drivers/input/touchscreen/sur40.c @@ -645,7 +645,7 @@ static void sur40_disconnect(struct usb_interface *interface) */ static int sur40_queue_setup(struct vb2_queue *q, unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], void *alloc_ctxs[]) + unsigned int sizes[], struct device *alloc_devs[]) { if (q->num_buffers + *nbuffers < 3) *nbuffers = 3 - q->num_buffers; -- cgit v1.2.3 From af766ee005c496b8567976dc3eed7676443ed6de Mon Sep 17 00:00:00 2001 From: Florian Echtler Date: Tue, 31 May 2016 17:15:32 -0300 Subject: [media] sur40: lower poll interval to fix occasional FPS drops to ~56 FPS The framerate sometimes drops below 60 Hz if the poll interval is too high. Lowering it to the minimum of 1 ms fixes this. Signed-off-by: Martin Kaltenbrunner Signed-off-by: Florian Echtler Signed-off-by: Hans Verkuil Cc: # for v4.2 and up Signed-off-by: Mauro Carvalho Chehab --- drivers/input/touchscreen/sur40.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c index f124f3af0de1..bff525f26e87 100644 --- a/drivers/input/touchscreen/sur40.c +++ b/drivers/input/touchscreen/sur40.c @@ -126,7 +126,7 @@ struct sur40_image_header { #define VIDEO_PACKET_SIZE 16384 /* polling interval (ms) */ -#define POLL_INTERVAL 4 +#define POLL_INTERVAL 1 /* maximum number of contacts FIXME: this is a guess? */ #define MAX_CONTACTS 64 -- cgit v1.2.3 From 6a8588156657e607fcfdffd46c1daae8ba88a1e5 Mon Sep 17 00:00:00 2001 From: Florian Echtler Date: Tue, 31 May 2016 17:15:33 -0300 Subject: [media] sur40: fix occasional oopses on device close Closing the V4L2 device sometimes triggers a kernel oops. Present patch fixes this. Signed-off-by: Martin Kaltenbrunner Signed-off-by: Florian Echtler Signed-off-by: Hans Verkuil Cc: # for v4.2 and up Signed-off-by: Mauro Carvalho Chehab --- drivers/input/touchscreen/sur40.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c index bff525f26e87..b2f3d266dd17 100644 --- a/drivers/input/touchscreen/sur40.c +++ b/drivers/input/touchscreen/sur40.c @@ -447,7 +447,7 @@ static void sur40_process_video(struct sur40_state *sur40) /* return error if streaming was stopped in the meantime */ if (sur40->sequence == -1) - goto err_poll; + return; /* mark as finished */ new_buf->vb.vb2_buf.timestamp = ktime_get_ns(); @@ -725,6 +725,7 @@ static int sur40_start_streaming(struct vb2_queue *vq, unsigned int count) static void sur40_stop_streaming(struct vb2_queue *vq) { struct sur40_state *sur40 = vb2_get_drv_priv(vq); + vb2_wait_for_all_buffers(vq); sur40->sequence = -1; /* Release all active buffers */ -- cgit v1.2.3 From d14cf19d2b078cf6b4c86e1c2ecd8b5f23556bf3 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 27 Jun 2016 07:25:43 -0300 Subject: [media] sur40: drop unnecessary format description Don't fill in the format description. This is now done in the V4L2 core to ensure consistent descriptions. Signed-off-by: Hans Verkuil Acked-by: Nick Dyer Signed-off-by: Mauro Carvalho Chehab --- drivers/input/touchscreen/sur40.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c index b2f3d266dd17..4ea475775d58 100644 --- a/drivers/input/touchscreen/sur40.c +++ b/drivers/input/touchscreen/sur40.c @@ -783,7 +783,6 @@ static int sur40_vidioc_enum_fmt(struct file *file, void *priv, { if (f->index != 0) return -EINVAL; - strlcpy(f->description, "8-bit greyscale", sizeof(f->description)); f->pixelformat = V4L2_PIX_FMT_GREY; f->flags = 0; return 0; -- cgit v1.2.3 From d6a39404984094c5e20e1d17a91036ac6b125731 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Mon, 18 Jul 2016 18:10:31 -0300 Subject: [media] Input: atmel_mxt_ts - add support for T37 diagnostic data Atmel maXTouch devices have a T37 object which can be used to read raw touch deltas from the device. This consists of an array of 16-bit integers, one for each node on the touchscreen matrix. Signed-off-by: Nick Dyer Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab Acked-by: Dmitry Torokhov --- drivers/input/touchscreen/Kconfig | 6 ++ drivers/input/touchscreen/atmel_mxt_ts.c | 159 +++++++++++++++++++++++++++++++ 2 files changed, 165 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 2fb1f430a431..f0eef41e49e5 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -115,6 +115,12 @@ config TOUCHSCREEN_ATMEL_MXT To compile this driver as a module, choose M here: the module will be called atmel_mxt_ts. +config TOUCHSCREEN_ATMEL_MXT_T37 + bool "Support T37 Diagnostic Data" + depends on TOUCHSCREEN_ATMEL_MXT + help + Say Y here if you want support for the T37 Diagnostic Data object. + config TOUCHSCREEN_AUO_PIXCIR tristate "AUO in-cell touchscreen using Pixcir ICs" depends on I2C diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 5af7907d0af4..0048233255f2 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -124,6 +124,19 @@ struct t9_range { #define MXT_COMMS_CTRL 0 #define MXT_COMMS_CMD 1 +/* MXT_DEBUG_DIAGNOSTIC_T37 */ +#define MXT_DIAGNOSTIC_PAGEUP 0x01 +#define MXT_DIAGNOSTIC_DELTAS 0x10 +#define MXT_DIAGNOSTIC_SIZE 128 + +struct t37_debug { +#ifdef CONFIG_TOUCHSCREEN_ATMEL_MXT_T37 + u8 mode; + u8 page; + u8 data[MXT_DIAGNOSTIC_SIZE]; +#endif +}; + /* Define for MXT_GEN_COMMAND_T6 */ #define MXT_BOOT_VALUE 0xa5 #define MXT_RESET_VALUE 0x01 @@ -205,6 +218,14 @@ struct mxt_object { u8 num_report_ids; } __packed; +struct mxt_dbg { + u16 t37_address; + u16 diag_cmd_address; + struct t37_debug *t37_buf; + unsigned int t37_pages; + unsigned int t37_nodes; +}; + /* Each client has this additional data */ struct mxt_data { struct i2c_client *client; @@ -233,6 +254,7 @@ struct mxt_data { u8 num_touchids; u8 multitouch; struct t7_config t7_cfg; + struct mxt_dbg dbg; /* Cached parameters from object table */ u16 T5_address; @@ -2043,6 +2065,141 @@ recheck: return 0; } +#ifdef CONFIG_TOUCHSCREEN_ATMEL_MXT_T37 +static u16 mxt_get_debug_value(struct mxt_data *data, unsigned int x, + unsigned int y) +{ + struct mxt_dbg *dbg = &data->dbg; + unsigned int ofs, page; + + ofs = (y + (x * data->info.matrix_ysize)) * sizeof(u16); + page = ofs / MXT_DIAGNOSTIC_SIZE; + ofs %= MXT_DIAGNOSTIC_SIZE; + + return get_unaligned_le16(&dbg->t37_buf[page].data[ofs]); +} + +static int mxt_convert_debug_pages(struct mxt_data *data, u16 *outbuf) +{ + struct mxt_dbg *dbg = &data->dbg; + unsigned int x = 0; + unsigned int y = 0; + unsigned int i; + + for (i = 0; i < dbg->t37_nodes; i++) { + outbuf[i] = mxt_get_debug_value(data, x, y); + + /* Next value */ + if (++x >= data->info.matrix_xsize) { + x = 0; + y++; + } + } + + return 0; +} + +static int mxt_read_diagnostic_debug(struct mxt_data *data, u8 mode, + u16 *outbuf) +{ + struct mxt_dbg *dbg = &data->dbg; + int retries = 0; + int page; + int ret; + u8 cmd = mode; + struct t37_debug *p; + u8 cmd_poll; + + for (page = 0; page < dbg->t37_pages; page++) { + p = dbg->t37_buf + page; + + ret = mxt_write_reg(data->client, dbg->diag_cmd_address, + cmd); + if (ret) + return ret; + + retries = 0; + msleep(20); +wait_cmd: + /* Read back command byte */ + ret = __mxt_read_reg(data->client, dbg->diag_cmd_address, + sizeof(cmd_poll), &cmd_poll); + if (ret) + return ret; + + /* Field is cleared once the command has been processed */ + if (cmd_poll) { + if (retries++ > 100) + return -EINVAL; + + msleep(20); + goto wait_cmd; + } + + /* Read T37 page */ + ret = __mxt_read_reg(data->client, dbg->t37_address, + sizeof(struct t37_debug), p); + if (ret) + return ret; + + if (p->mode != mode || p->page != page) { + dev_err(&data->client->dev, "T37 page mismatch\n"); + return -EINVAL; + } + + dev_dbg(&data->client->dev, "%s page:%d retries:%d\n", + __func__, page, retries); + + /* For remaining pages, write PAGEUP rather than mode */ + cmd = MXT_DIAGNOSTIC_PAGEUP; + } + + return mxt_convert_debug_pages(data, outbuf); +} + +static void mxt_debug_init(struct mxt_data *data) +{ + struct mxt_dbg *dbg = &data->dbg; + struct mxt_object *object; + + object = mxt_get_object(data, MXT_GEN_COMMAND_T6); + if (!object) + goto error; + + dbg->diag_cmd_address = object->start_address + MXT_COMMAND_DIAGNOSTIC; + + object = mxt_get_object(data, MXT_DEBUG_DIAGNOSTIC_T37); + if (!object) + goto error; + + if (mxt_obj_size(object) != sizeof(struct t37_debug)) { + dev_warn(&data->client->dev, "Bad T37 size"); + goto error; + } + + dbg->t37_address = object->start_address; + + /* Calculate size of data and allocate buffer */ + dbg->t37_nodes = data->info.matrix_xsize * data->info.matrix_ysize; + dbg->t37_pages = DIV_ROUND_UP(dbg->t37_nodes * sizeof(u16), + sizeof(dbg->t37_buf->data)); + + dbg->t37_buf = devm_kmalloc_array(&data->client->dev, dbg->t37_pages, + sizeof(struct t37_debug), GFP_KERNEL); + if (!dbg->t37_buf) + goto error; + + return; + +error: + dev_warn(&data->client->dev, "Error initializing T37\n"); +} +#else +static void mxt_debug_init(struct mxt_data *data) +{ +} +#endif + static int mxt_configure_objects(struct mxt_data *data, const struct firmware *cfg) { @@ -2070,6 +2227,8 @@ static int mxt_configure_objects(struct mxt_data *data, dev_warn(dev, "No touch object detected\n"); } + mxt_debug_init(data); + dev_info(dev, "Family: %u Variant: %u Firmware V%u.%u.%02X Objects: %u\n", info->family_id, info->variant_id, info->version >> 4, -- cgit v1.2.3 From ecfdd7e2660e5208072d3afaed8c3e05a643b64f Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Mon, 18 Jul 2016 18:10:32 -0300 Subject: [media] Input: atmel_mxt_ts - output diagnostic debug via V4L2 device Register a video device to output T37 diagnostic data. Signed-off-by: Nick Dyer Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab Acked-by: Dmitry Torokhov --- drivers/input/touchscreen/Kconfig | 6 +- drivers/input/touchscreen/atmel_mxt_ts.c | 245 +++++++++++++++++++++++++++++++ 2 files changed, 249 insertions(+), 2 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index f0eef41e49e5..754e6aa0851d 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -117,9 +117,11 @@ config TOUCHSCREEN_ATMEL_MXT config TOUCHSCREEN_ATMEL_MXT_T37 bool "Support T37 Diagnostic Data" - depends on TOUCHSCREEN_ATMEL_MXT + depends on TOUCHSCREEN_ATMEL_MXT && VIDEO_V4L2 + select VIDEOBUF2_VMALLOC help - Say Y here if you want support for the T37 Diagnostic Data object. + Say Y here if you want support to output data from the T37 + Diagnostic Data object using a V4L device. config TOUCHSCREEN_AUO_PIXCIR tristate "AUO in-cell touchscreen using Pixcir ICs" diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 0048233255f2..a9f987b59f9a 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -28,6 +28,10 @@ #include #include #include +#include +#include +#include +#include /* Firmware files */ #define MXT_FW_NAME "maxtouch.fw" @@ -224,6 +228,23 @@ struct mxt_dbg { struct t37_debug *t37_buf; unsigned int t37_pages; unsigned int t37_nodes; + + struct v4l2_device v4l2; + struct v4l2_pix_format format; + struct video_device vdev; + struct vb2_queue queue; + struct mutex lock; + int input; +}; + +static const struct v4l2_file_operations mxt_video_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .unlocked_ioctl = video_ioctl2, + .read = vb2_fop_read, + .mmap = vb2_fop_mmap, + .poll = vb2_fop_poll, }; /* Each client has this additional data */ @@ -279,6 +300,11 @@ struct mxt_data { struct completion crc_completion; }; +struct mxt_vb2_buffer { + struct vb2_buffer vb; + struct list_head list; +}; + static size_t mxt_obj_size(const struct mxt_object *obj) { return obj->size_minus_one + 1; @@ -1525,6 +1551,11 @@ static void mxt_free_input_device(struct mxt_data *data) static void mxt_free_object_table(struct mxt_data *data) { +#ifdef CONFIG_TOUCHSCREEN_ATMEL_MXT_T37 + video_unregister_device(&data->dbg.vdev); + v4l2_device_unregister(&data->dbg.v4l2); +#endif + kfree(data->object_table); data->object_table = NULL; kfree(data->msg_buf); @@ -2157,10 +2188,191 @@ wait_cmd: return mxt_convert_debug_pages(data, outbuf); } +static int mxt_queue_setup(struct vb2_queue *q, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], struct device *alloc_devs[]) +{ + struct mxt_data *data = q->drv_priv; + size_t size = data->dbg.t37_nodes * sizeof(u16); + + if (*nplanes) + return sizes[0] < size ? -EINVAL : 0; + + *nplanes = 1; + sizes[0] = size; + + return 0; +} + +static void mxt_buffer_queue(struct vb2_buffer *vb) +{ + struct mxt_data *data = vb2_get_drv_priv(vb->vb2_queue); + u16 *ptr; + int ret; + + ptr = vb2_plane_vaddr(vb, 0); + if (!ptr) { + dev_err(&data->client->dev, "Error acquiring frame ptr\n"); + goto fault; + } + + ret = mxt_read_diagnostic_debug(data, MXT_DIAGNOSTIC_DELTAS, ptr); + if (ret) + goto fault; + + vb2_set_plane_payload(vb, 0, data->dbg.t37_nodes * sizeof(u16)); + vb2_buffer_done(vb, VB2_BUF_STATE_DONE); + return; + +fault: + vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); +} + +/* V4L2 structures */ +static const struct vb2_ops mxt_queue_ops = { + .queue_setup = mxt_queue_setup, + .buf_queue = mxt_buffer_queue, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static const struct vb2_queue mxt_queue = { + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ, + .buf_struct_size = sizeof(struct mxt_vb2_buffer), + .ops = &mxt_queue_ops, + .mem_ops = &vb2_vmalloc_memops, + .timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC, + .min_buffers_needed = 1, +}; + +static int mxt_vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct mxt_data *data = video_drvdata(file); + + strlcpy(cap->driver, "atmel_mxt_ts", sizeof(cap->driver)); + strlcpy(cap->card, "atmel_mxt_ts touch", sizeof(cap->card)); + snprintf(cap->bus_info, sizeof(cap->bus_info), + "I2C:%s", dev_name(&data->client->dev)); + return 0; +} + +static int mxt_vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *i) +{ + if (i->index > 0) + return -EINVAL; + + i->type = V4L2_INPUT_TYPE_TOUCH; + strlcpy(i->name, "Mutual Capacitance Deltas", sizeof(i->name)); + return 0; +} + +static int mxt_set_input(struct mxt_data *data, unsigned int i) +{ + struct v4l2_pix_format *f = &data->dbg.format; + + if (i > 0) + return -EINVAL; + + f->width = data->info.matrix_xsize; + f->height = data->info.matrix_ysize; + f->pixelformat = V4L2_TCH_FMT_DELTA_TD16; + f->field = V4L2_FIELD_NONE; + f->colorspace = V4L2_COLORSPACE_RAW; + f->bytesperline = f->width * sizeof(u16); + f->sizeimage = f->width * f->height * sizeof(u16); + + data->dbg.input = i; + + return 0; +} + +static int mxt_vidioc_s_input(struct file *file, void *priv, unsigned int i) +{ + return mxt_set_input(video_drvdata(file), i); +} + +static int mxt_vidioc_g_input(struct file *file, void *priv, unsigned int *i) +{ + struct mxt_data *data = video_drvdata(file); + + *i = data->dbg.input; + + return 0; +} + +static int mxt_vidioc_fmt(struct file *file, void *priv, struct v4l2_format *f) +{ + struct mxt_data *data = video_drvdata(file); + + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + f->fmt.pix = data->dbg.format; + + return 0; +} + +static int mxt_vidioc_enum_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *fmt) +{ + if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + fmt->pixelformat = V4L2_TCH_FMT_DELTA_TD16; + return 0; +} + +static int mxt_vidioc_g_parm(struct file *file, void *fh, + struct v4l2_streamparm *a) +{ + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + a->parm.capture.readbuffers = 1; + a->parm.capture.timeperframe.numerator = 1; + a->parm.capture.timeperframe.denominator = 10; + return 0; +} + +static const struct v4l2_ioctl_ops mxt_video_ioctl_ops = { + .vidioc_querycap = mxt_vidioc_querycap, + + .vidioc_enum_fmt_vid_cap = mxt_vidioc_enum_fmt, + .vidioc_s_fmt_vid_cap = mxt_vidioc_fmt, + .vidioc_g_fmt_vid_cap = mxt_vidioc_fmt, + .vidioc_try_fmt_vid_cap = mxt_vidioc_fmt, + .vidioc_g_parm = mxt_vidioc_g_parm, + + .vidioc_enum_input = mxt_vidioc_enum_input, + .vidioc_g_input = mxt_vidioc_g_input, + .vidioc_s_input = mxt_vidioc_s_input, + + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, +}; + +static const struct video_device mxt_video_device = { + .name = "Atmel maxTouch", + .fops = &mxt_video_fops, + .ioctl_ops = &mxt_video_ioctl_ops, + .release = video_device_release_empty, + .device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TOUCH | + V4L2_CAP_READWRITE | V4L2_CAP_STREAMING, +}; + static void mxt_debug_init(struct mxt_data *data) { struct mxt_dbg *dbg = &data->dbg; struct mxt_object *object; + int error; object = mxt_get_object(data, MXT_GEN_COMMAND_T6); if (!object) @@ -2189,8 +2401,41 @@ static void mxt_debug_init(struct mxt_data *data) if (!dbg->t37_buf) goto error; + /* init channel to zero */ + mxt_set_input(data, 0); + + /* register video device */ + snprintf(dbg->v4l2.name, sizeof(dbg->v4l2.name), "%s", "atmel_mxt_ts"); + error = v4l2_device_register(&data->client->dev, &dbg->v4l2); + if (error) + goto error; + + /* initialize the queue */ + mutex_init(&dbg->lock); + dbg->queue = mxt_queue; + dbg->queue.drv_priv = data; + dbg->queue.lock = &dbg->lock; + dbg->queue.dev = &data->client->dev; + + error = vb2_queue_init(&dbg->queue); + if (error) + goto error_unreg_v4l2; + + dbg->vdev = mxt_video_device; + dbg->vdev.v4l2_dev = &dbg->v4l2; + dbg->vdev.lock = &dbg->lock; + dbg->vdev.vfl_dir = VFL_DIR_RX; + dbg->vdev.queue = &dbg->queue; + video_set_drvdata(&dbg->vdev, data); + + error = video_register_device(&dbg->vdev, VFL_TYPE_TOUCH, -1); + if (error) + goto error_unreg_v4l2; + return; +error_unreg_v4l2: + v4l2_device_unregister(&dbg->v4l2); error: dev_warn(&data->client->dev, "Error initializing T37\n"); } -- cgit v1.2.3 From 2786489f324e8384a814793d88c3a76d1973f422 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Mon, 18 Jul 2016 18:10:33 -0300 Subject: [media] Input: atmel_mxt_ts - read touchscreen size The touchscreen may have a margin where not all the matrix is used. Read the parameters from T9 and T100 and take account of the difference. Note: this does not read the XORIGIN/YORIGIN fields so it assumes that the touchscreen starts at (0,0) Signed-off-by: Nick Dyer Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab Acked-by: Dmitry Torokhov --- drivers/input/touchscreen/atmel_mxt_ts.c | 42 +++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 6 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index a9f987b59f9a..29be261a4750 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -103,6 +103,8 @@ struct t7_config { /* MXT_TOUCH_MULTI_T9 field */ #define MXT_T9_CTRL 0 +#define MXT_T9_XSIZE 3 +#define MXT_T9_YSIZE 4 #define MXT_T9_ORIENT 9 #define MXT_T9_RANGE 18 @@ -150,7 +152,9 @@ struct t37_debug { #define MXT_T100_CTRL 0 #define MXT_T100_CFG1 1 #define MXT_T100_TCHAUX 3 +#define MXT_T100_XSIZE 9 #define MXT_T100_XRANGE 13 +#define MXT_T100_YSIZE 20 #define MXT_T100_YRANGE 24 #define MXT_T100_CFG_SWITCHXY BIT(5) @@ -259,6 +263,8 @@ struct mxt_data { unsigned int max_x; unsigned int max_y; bool xy_switch; + u8 xsize; + u8 ysize; bool in_bootloader; u16 mem_size; u8 t100_aux_ampl; @@ -1713,6 +1719,18 @@ static int mxt_read_t9_resolution(struct mxt_data *data) if (!object) return -EINVAL; + error = __mxt_read_reg(client, + object->start_address + MXT_T9_XSIZE, + sizeof(data->xsize), &data->xsize); + if (error) + return error; + + error = __mxt_read_reg(client, + object->start_address + MXT_T9_YSIZE, + sizeof(data->ysize), &data->ysize); + if (error) + return error; + error = __mxt_read_reg(client, object->start_address + MXT_T9_RANGE, sizeof(range), &range); @@ -1763,6 +1781,18 @@ static int mxt_read_t100_config(struct mxt_data *data) data->max_y = get_unaligned_le16(&range_y); + error = __mxt_read_reg(client, + object->start_address + MXT_T100_XSIZE, + sizeof(data->xsize), &data->xsize); + if (error) + return error; + + error = __mxt_read_reg(client, + object->start_address + MXT_T100_YSIZE, + sizeof(data->ysize), &data->ysize); + if (error) + return error; + /* read orientation config */ error = __mxt_read_reg(client, object->start_address + MXT_T100_CFG1, @@ -2121,7 +2151,7 @@ static int mxt_convert_debug_pages(struct mxt_data *data, u16 *outbuf) outbuf[i] = mxt_get_debug_value(data, x, y); /* Next value */ - if (++x >= data->info.matrix_xsize) { + if (++x >= data->xsize) { x = 0; y++; } @@ -2276,8 +2306,8 @@ static int mxt_set_input(struct mxt_data *data, unsigned int i) if (i > 0) return -EINVAL; - f->width = data->info.matrix_xsize; - f->height = data->info.matrix_ysize; + f->width = data->xsize; + f->height = data->ysize; f->pixelformat = V4L2_TCH_FMT_DELTA_TD16; f->field = V4L2_FIELD_NONE; f->colorspace = V4L2_COLORSPACE_RAW; @@ -2392,9 +2422,9 @@ static void mxt_debug_init(struct mxt_data *data) dbg->t37_address = object->start_address; /* Calculate size of data and allocate buffer */ - dbg->t37_nodes = data->info.matrix_xsize * data->info.matrix_ysize; - dbg->t37_pages = DIV_ROUND_UP(dbg->t37_nodes * sizeof(u16), - sizeof(dbg->t37_buf->data)); + dbg->t37_nodes = data->xsize * data->ysize; + dbg->t37_pages = DIV_ROUND_UP(data->xsize * data->info.matrix_ysize * + sizeof(u16), sizeof(dbg->t37_buf->data)); dbg->t37_buf = devm_kmalloc_array(&data->client->dev, dbg->t37_pages, sizeof(struct t37_debug), GFP_KERNEL); -- cgit v1.2.3 From de601f71e7d1bdfa411e61d43af7b2b6cde5d2a2 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Mon, 18 Jul 2016 18:10:34 -0300 Subject: [media] Input: atmel_mxt_ts - handle diagnostic data orientation Invert the diagnostic data to match the orientation of the input device. Signed-off-by: Nick Dyer Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab Acked-by: Dmitry Torokhov --- drivers/input/touchscreen/atmel_mxt_ts.c | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 29be261a4750..7376c42d2370 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -125,6 +125,8 @@ struct t9_range { /* MXT_TOUCH_MULTI_T9 orient */ #define MXT_T9_ORIENT_SWITCH (1 << 0) +#define MXT_T9_ORIENT_INVERTX (1 << 1) +#define MXT_T9_ORIENT_INVERTY (1 << 2) /* MXT_SPT_COMMSCONFIG_T18 */ #define MXT_COMMS_CTRL 0 @@ -158,6 +160,8 @@ struct t37_debug { #define MXT_T100_YRANGE 24 #define MXT_T100_CFG_SWITCHXY BIT(5) +#define MXT_T100_CFG_INVERTY BIT(6) +#define MXT_T100_CFG_INVERTX BIT(7) #define MXT_T100_TCHAUX_VECT BIT(0) #define MXT_T100_TCHAUX_AMPL BIT(1) @@ -262,6 +266,8 @@ struct mxt_data { unsigned int irq; unsigned int max_x; unsigned int max_y; + bool invertx; + bool inverty; bool xy_switch; u8 xsize; u8 ysize; @@ -1747,6 +1753,8 @@ static int mxt_read_t9_resolution(struct mxt_data *data) return error; data->xy_switch = orient & MXT_T9_ORIENT_SWITCH; + data->invertx = orient & MXT_T9_ORIENT_INVERTX; + data->inverty = orient & MXT_T9_ORIENT_INVERTY; return 0; } @@ -1801,6 +1809,8 @@ static int mxt_read_t100_config(struct mxt_data *data) return error; data->xy_switch = cfg & MXT_T100_CFG_SWITCHXY; + data->invertx = cfg & MXT_T100_CFG_INVERTX; + data->inverty = cfg & MXT_T100_CFG_INVERTY; /* allocate aux bytes */ error = __mxt_read_reg(client, @@ -2145,13 +2155,19 @@ static int mxt_convert_debug_pages(struct mxt_data *data, u16 *outbuf) struct mxt_dbg *dbg = &data->dbg; unsigned int x = 0; unsigned int y = 0; - unsigned int i; + unsigned int i, rx, ry; for (i = 0; i < dbg->t37_nodes; i++) { - outbuf[i] = mxt_get_debug_value(data, x, y); + /* Handle orientation */ + rx = data->xy_switch ? y : x; + ry = data->xy_switch ? x : y; + rx = data->invertx ? (data->xsize - 1 - rx) : rx; + ry = data->inverty ? (data->ysize - 1 - ry) : ry; + + outbuf[i] = mxt_get_debug_value(data, rx, ry); /* Next value */ - if (++x >= data->xsize) { + if (++x >= (data->xy_switch ? data->ysize : data->xsize)) { x = 0; y++; } @@ -2306,8 +2322,8 @@ static int mxt_set_input(struct mxt_data *data, unsigned int i) if (i > 0) return -EINVAL; - f->width = data->xsize; - f->height = data->ysize; + f->width = data->xy_switch ? data->ysize : data->xsize; + f->height = data->xy_switch ? data->xsize : data->ysize; f->pixelformat = V4L2_TCH_FMT_DELTA_TD16; f->field = V4L2_FIELD_NONE; f->colorspace = V4L2_COLORSPACE_RAW; -- cgit v1.2.3 From 566d533a4bd2ed9a7ce26a08df31a6e634051562 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Mon, 18 Jul 2016 18:10:35 -0300 Subject: [media] Input: atmel_mxt_ts - add diagnostic data support for mXT1386 The mXT1386 family of chips have a different architecture which splits the diagnostic data into 3 columns. Signed-off-by: Nick Dyer Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab Acked-by: Dmitry Torokhov --- drivers/input/touchscreen/atmel_mxt_ts.c | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 7376c42d2370..584198e64661 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -137,6 +137,10 @@ struct t9_range { #define MXT_DIAGNOSTIC_DELTAS 0x10 #define MXT_DIAGNOSTIC_SIZE 128 +#define MXT_FAMILY_1386 160 +#define MXT1386_COLUMNS 3 +#define MXT1386_PAGES_PER_COLUMN 8 + struct t37_debug { #ifdef CONFIG_TOUCHSCREEN_ATMEL_MXT_T37 u8 mode; @@ -2140,13 +2144,27 @@ recheck: static u16 mxt_get_debug_value(struct mxt_data *data, unsigned int x, unsigned int y) { + struct mxt_info *info = &data->info; struct mxt_dbg *dbg = &data->dbg; unsigned int ofs, page; + unsigned int col = 0; + unsigned int col_width; + + if (info->family_id == MXT_FAMILY_1386) { + col_width = info->matrix_ysize / MXT1386_COLUMNS; + col = y / col_width; + y = y % col_width; + } else { + col_width = info->matrix_ysize; + } - ofs = (y + (x * data->info.matrix_ysize)) * sizeof(u16); + ofs = (y + (x * col_width)) * sizeof(u16); page = ofs / MXT_DIAGNOSTIC_SIZE; ofs %= MXT_DIAGNOSTIC_SIZE; + if (info->family_id == MXT_FAMILY_1386) + page += col * MXT1386_PAGES_PER_COLUMN; + return get_unaligned_le16(&dbg->t37_buf[page].data[ofs]); } @@ -2416,6 +2434,7 @@ static const struct video_device mxt_video_device = { static void mxt_debug_init(struct mxt_data *data) { + struct mxt_info *info = &data->info; struct mxt_dbg *dbg = &data->dbg; struct mxt_object *object; int error; @@ -2439,8 +2458,14 @@ static void mxt_debug_init(struct mxt_data *data) /* Calculate size of data and allocate buffer */ dbg->t37_nodes = data->xsize * data->ysize; - dbg->t37_pages = DIV_ROUND_UP(data->xsize * data->info.matrix_ysize * - sizeof(u16), sizeof(dbg->t37_buf->data)); + + if (info->family_id == MXT_FAMILY_1386) + dbg->t37_pages = MXT1386_COLUMNS * MXT1386_PAGES_PER_COLUMN; + else + dbg->t37_pages = DIV_ROUND_UP(data->xsize * + data->info.matrix_ysize * + sizeof(u16), + sizeof(dbg->t37_buf->data)); dbg->t37_buf = devm_kmalloc_array(&data->client->dev, dbg->t37_pages, sizeof(struct t37_debug), GFP_KERNEL); -- cgit v1.2.3 From 06b3d3f38c19601564cdd10443edbcac5c8293e2 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Mon, 18 Jul 2016 18:10:36 -0300 Subject: [media] Input: atmel_mxt_ts - add support for reference data There are different datatypes available from a maXTouch chip. Add support to retrieve reference data as well. Signed-off-by: Nick Dyer Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab Acked-by: Dmitry Torokhov --- drivers/input/touchscreen/atmel_mxt_ts.c | 57 ++++++++++++++++++++++++++++---- 1 file changed, 51 insertions(+), 6 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 584198e64661..beede8fe511c 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -135,6 +135,7 @@ struct t9_range { /* MXT_DEBUG_DIAGNOSTIC_T37 */ #define MXT_DIAGNOSTIC_PAGEUP 0x01 #define MXT_DIAGNOSTIC_DELTAS 0x10 +#define MXT_DIAGNOSTIC_REFS 0x11 #define MXT_DIAGNOSTIC_SIZE 128 #define MXT_FAMILY_1386 160 @@ -249,6 +250,12 @@ struct mxt_dbg { int input; }; +enum v4l_dbg_inputs { + MXT_V4L_INPUT_DELTAS, + MXT_V4L_INPUT_REFS, + MXT_V4L_INPUT_MAX, +}; + static const struct v4l2_file_operations mxt_video_fops = { .owner = THIS_MODULE, .open = v4l2_fh_open, @@ -2273,6 +2280,7 @@ static void mxt_buffer_queue(struct vb2_buffer *vb) struct mxt_data *data = vb2_get_drv_priv(vb->vb2_queue); u16 *ptr; int ret; + u8 mode; ptr = vb2_plane_vaddr(vb, 0); if (!ptr) { @@ -2280,7 +2288,18 @@ static void mxt_buffer_queue(struct vb2_buffer *vb) goto fault; } - ret = mxt_read_diagnostic_debug(data, MXT_DIAGNOSTIC_DELTAS, ptr); + switch (data->dbg.input) { + case MXT_V4L_INPUT_DELTAS: + default: + mode = MXT_DIAGNOSTIC_DELTAS; + break; + + case MXT_V4L_INPUT_REFS: + mode = MXT_DIAGNOSTIC_REFS; + break; + } + + ret = mxt_read_diagnostic_debug(data, mode, ptr); if (ret) goto fault; @@ -2325,11 +2344,21 @@ static int mxt_vidioc_querycap(struct file *file, void *priv, static int mxt_vidioc_enum_input(struct file *file, void *priv, struct v4l2_input *i) { - if (i->index > 0) + if (i->index >= MXT_V4L_INPUT_MAX) return -EINVAL; i->type = V4L2_INPUT_TYPE_TOUCH; - strlcpy(i->name, "Mutual Capacitance Deltas", sizeof(i->name)); + + switch (i->index) { + case MXT_V4L_INPUT_REFS: + strlcpy(i->name, "Mutual Capacitance References", + sizeof(i->name)); + break; + case MXT_V4L_INPUT_DELTAS: + strlcpy(i->name, "Mutual Capacitance Deltas", sizeof(i->name)); + break; + } + return 0; } @@ -2337,12 +2366,16 @@ static int mxt_set_input(struct mxt_data *data, unsigned int i) { struct v4l2_pix_format *f = &data->dbg.format; - if (i > 0) + if (i >= MXT_V4L_INPUT_MAX) return -EINVAL; + if (i == MXT_V4L_INPUT_DELTAS) + f->pixelformat = V4L2_TCH_FMT_DELTA_TD16; + else + f->pixelformat = V4L2_TCH_FMT_TU16; + f->width = data->xy_switch ? data->ysize : data->xsize; f->height = data->xy_switch ? data->xsize : data->ysize; - f->pixelformat = V4L2_TCH_FMT_DELTA_TD16; f->field = V4L2_FIELD_NONE; f->colorspace = V4L2_COLORSPACE_RAW; f->bytesperline = f->width * sizeof(u16); @@ -2383,7 +2416,19 @@ static int mxt_vidioc_enum_fmt(struct file *file, void *priv, if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - fmt->pixelformat = V4L2_TCH_FMT_DELTA_TD16; + switch (fmt->index) { + case 0: + fmt->pixelformat = V4L2_TCH_FMT_TU16; + break; + + case 1: + fmt->pixelformat = V4L2_TCH_FMT_DELTA_TD16; + break; + + default: + return -EINVAL; + } + return 0; } -- cgit v1.2.3 From 3a762dbd5347514c3cb2ac756a92a3d1c7646a2d Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Mon, 18 Jul 2016 18:10:37 -0300 Subject: [media] Input: synaptics-rmi4 - add support for F54 diagnostics Function 54 implements access to various RMI4 diagnostic features. This patch adds support for retrieving this data. It registers a V4L2 device to output the data to user space. Signed-off-by: Nick Dyer Tested-by: Andrew Duggan Tested-by: Chris Healy Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab Acked-by: Dmitry Torokhov --- drivers/input/rmi4/Kconfig | 11 + drivers/input/rmi4/Makefile | 1 + drivers/input/rmi4/rmi_bus.c | 3 + drivers/input/rmi4/rmi_driver.h | 1 + drivers/input/rmi4/rmi_f54.c | 756 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 772 insertions(+) create mode 100644 drivers/input/rmi4/rmi_f54.c (limited to 'drivers/input') diff --git a/drivers/input/rmi4/Kconfig b/drivers/input/rmi4/Kconfig index f73df2495fed..f3418b65eb41 100644 --- a/drivers/input/rmi4/Kconfig +++ b/drivers/input/rmi4/Kconfig @@ -61,3 +61,14 @@ config RMI4_F30 Function 30 provides GPIO and LED support for RMI4 devices. This includes support for buttons on TouchPads and ClickPads. + +config RMI4_F54 + bool "RMI4 Function 54 (Analog diagnostics)" + depends on RMI4_CORE + depends on VIDEO_V4L2 + select VIDEOBUF2_VMALLOC + help + Say Y here if you want to add support for RMI4 function 54 + + Function 54 provides access to various diagnostic features in certain + RMI4 touch sensors. diff --git a/drivers/input/rmi4/Makefile b/drivers/input/rmi4/Makefile index 95c00a783992..0bafc8502c4b 100644 --- a/drivers/input/rmi4/Makefile +++ b/drivers/input/rmi4/Makefile @@ -7,6 +7,7 @@ rmi_core-$(CONFIG_RMI4_2D_SENSOR) += rmi_2d_sensor.o rmi_core-$(CONFIG_RMI4_F11) += rmi_f11.o rmi_core-$(CONFIG_RMI4_F12) += rmi_f12.o rmi_core-$(CONFIG_RMI4_F30) += rmi_f30.o +rmi_core-$(CONFIG_RMI4_F54) += rmi_f54.o # Transports obj-$(CONFIG_RMI4_I2C) += rmi_i2c.o diff --git a/drivers/input/rmi4/rmi_bus.c b/drivers/input/rmi4/rmi_bus.c index a73580654c6b..09c769c6e91f 100644 --- a/drivers/input/rmi4/rmi_bus.c +++ b/drivers/input/rmi4/rmi_bus.c @@ -312,6 +312,9 @@ static struct rmi_function_handler *fn_handlers[] = { #ifdef CONFIG_RMI4_F30 &rmi_f30_handler, #endif +#ifdef CONFIG_RMI4_F54 + &rmi_f54_handler, +#endif }; static void __rmi_unregister_function_handlers(int start_idx) diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h index 6e140fa3cce1..8dfbebe9bf86 100644 --- a/drivers/input/rmi4/rmi_driver.h +++ b/drivers/input/rmi4/rmi_driver.h @@ -102,4 +102,5 @@ extern struct rmi_function_handler rmi_f01_handler; extern struct rmi_function_handler rmi_f11_handler; extern struct rmi_function_handler rmi_f12_handler; extern struct rmi_function_handler rmi_f30_handler; +extern struct rmi_function_handler rmi_f54_handler; #endif diff --git a/drivers/input/rmi4/rmi_f54.c b/drivers/input/rmi4/rmi_f54.c new file mode 100644 index 000000000000..bd86d3d599e7 --- /dev/null +++ b/drivers/input/rmi4/rmi_f54.c @@ -0,0 +1,756 @@ +/* + * Copyright (c) 2012-2015 Synaptics Incorporated + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rmi_driver.h" + +#define F54_NAME "rmi4_f54" + +/* F54 data offsets */ +#define F54_REPORT_DATA_OFFSET 3 +#define F54_FIFO_OFFSET 1 +#define F54_NUM_TX_OFFSET 1 +#define F54_NUM_RX_OFFSET 0 + +/* F54 commands */ +#define F54_GET_REPORT 1 +#define F54_FORCE_CAL 2 + +/* Fixed sizes of reports */ +#define F54_QUERY_LEN 27 + +/* F54 capabilities */ +#define F54_CAP_BASELINE (1 << 2) +#define F54_CAP_IMAGE8 (1 << 3) +#define F54_CAP_IMAGE16 (1 << 6) + +/** + * enum rmi_f54_report_type - RMI4 F54 report types + * + * @F54_8BIT_IMAGE: Normalized 8-Bit Image Report. The capacitance variance + * from baseline for each pixel. + * + * @F54_16BIT_IMAGE: Normalized 16-Bit Image Report. The capacitance variance + * from baseline for each pixel. + * + * @F54_RAW_16BIT_IMAGE: + * Raw 16-Bit Image Report. The raw capacitance for each + * pixel. + * + * @F54_TRUE_BASELINE: True Baseline Report. The baseline capacitance for each + * pixel. + * + * @F54_FULL_RAW_CAP: Full Raw Capacitance Report. The raw capacitance with + * low reference set to its minimum value and high + * reference set to its maximum value. + * + * @F54_FULL_RAW_CAP_RX_OFFSET_REMOVED: + * Full Raw Capacitance with Receiver Offset Removed + * Report. Set Low reference to its minimum value and high + * references to its maximum value, then report the raw + * capacitance for each pixel. + */ +enum rmi_f54_report_type { + F54_REPORT_NONE = 0, + F54_8BIT_IMAGE = 1, + F54_16BIT_IMAGE = 2, + F54_RAW_16BIT_IMAGE = 3, + F54_TRUE_BASELINE = 9, + F54_FULL_RAW_CAP = 19, + F54_FULL_RAW_CAP_RX_OFFSET_REMOVED = 20, + F54_MAX_REPORT_TYPE, +}; + +const char *rmi_f54_report_type_names[] = { + [F54_REPORT_NONE] = "Unknown", + [F54_8BIT_IMAGE] = "Normalized 8-Bit Image", + [F54_16BIT_IMAGE] = "Normalized 16-Bit Image", + [F54_RAW_16BIT_IMAGE] = "Raw 16-Bit Image", + [F54_TRUE_BASELINE] = "True Baseline", + [F54_FULL_RAW_CAP] = "Full Raw Capacitance", + [F54_FULL_RAW_CAP_RX_OFFSET_REMOVED] + = "Full Raw Capacitance RX Offset Removed", +}; + +struct rmi_f54_reports { + int start; + int size; +}; + +struct f54_data { + struct rmi_function *fn; + + u8 qry[F54_QUERY_LEN]; + u8 num_rx_electrodes; + u8 num_tx_electrodes; + u8 capabilities; + u16 clock_rate; + u8 family; + + enum rmi_f54_report_type report_type; + u8 *report_data; + int report_size; + struct rmi_f54_reports standard_report[2]; + + bool is_busy; + struct mutex status_mutex; + struct mutex data_mutex; + + struct workqueue_struct *workqueue; + struct delayed_work work; + unsigned long timeout; + + struct completion cmd_done; + + /* V4L2 support */ + struct v4l2_device v4l2; + struct v4l2_pix_format format; + struct video_device vdev; + struct vb2_queue queue; + struct mutex lock; + int input; + enum rmi_f54_report_type inputs[F54_MAX_REPORT_TYPE]; +}; + +/* + * Basic checks on report_type to ensure we write a valid type + * to the sensor. + */ +static bool is_f54_report_type_valid(struct f54_data *f54, + enum rmi_f54_report_type reptype) +{ + switch (reptype) { + case F54_8BIT_IMAGE: + return f54->capabilities & F54_CAP_IMAGE8; + case F54_16BIT_IMAGE: + case F54_RAW_16BIT_IMAGE: + return f54->capabilities & F54_CAP_IMAGE16; + case F54_TRUE_BASELINE: + return f54->capabilities & F54_CAP_IMAGE16; + case F54_FULL_RAW_CAP: + case F54_FULL_RAW_CAP_RX_OFFSET_REMOVED: + return true; + default: + return false; + } +} + +static enum rmi_f54_report_type rmi_f54_get_reptype(struct f54_data *f54, + unsigned int i) +{ + if (i >= F54_MAX_REPORT_TYPE) + return F54_REPORT_NONE; + + return f54->inputs[i]; +} + +static void rmi_f54_create_input_map(struct f54_data *f54) +{ + int i = 0; + enum rmi_f54_report_type reptype; + + for (reptype = 1; reptype < F54_MAX_REPORT_TYPE; reptype++) { + if (!is_f54_report_type_valid(f54, reptype)) + continue; + + f54->inputs[i++] = reptype; + } + + /* Remaining values are zero via kzalloc */ +} + +static int rmi_f54_request_report(struct rmi_function *fn, u8 report_type) +{ + struct f54_data *f54 = dev_get_drvdata(&fn->dev); + struct rmi_device *rmi_dev = fn->rmi_dev; + int error; + + /* Write Report Type into F54_AD_Data0 */ + if (f54->report_type != report_type) { + error = rmi_write(rmi_dev, f54->fn->fd.data_base_addr, + report_type); + if (error) + return error; + f54->report_type = report_type; + } + + /* + * Small delay after disabling interrupts to avoid race condition + * in firmare. This value is a bit higher than absolutely necessary. + * Should be removed once issue is resolved in firmware. + */ + usleep_range(2000, 3000); + + mutex_lock(&f54->data_mutex); + + error = rmi_write(rmi_dev, fn->fd.command_base_addr, F54_GET_REPORT); + if (error < 0) + return error; + + init_completion(&f54->cmd_done); + + f54->is_busy = 1; + f54->timeout = jiffies + msecs_to_jiffies(100); + + queue_delayed_work(f54->workqueue, &f54->work, 0); + + mutex_unlock(&f54->data_mutex); + + return 0; +} + +static size_t rmi_f54_get_report_size(struct f54_data *f54) +{ + u8 rx = f54->num_rx_electrodes ? : f54->num_rx_electrodes; + u8 tx = f54->num_tx_electrodes ? : f54->num_tx_electrodes; + size_t size; + + switch (rmi_f54_get_reptype(f54, f54->input)) { + case F54_8BIT_IMAGE: + size = rx * tx; + break; + case F54_16BIT_IMAGE: + case F54_RAW_16BIT_IMAGE: + case F54_TRUE_BASELINE: + case F54_FULL_RAW_CAP: + case F54_FULL_RAW_CAP_RX_OFFSET_REMOVED: + size = sizeof(u16) * rx * tx; + break; + default: + size = 0; + } + + return size; +} + +static int rmi_f54_get_pixel_fmt(enum rmi_f54_report_type reptype, u32 *pixfmt) +{ + int ret = 0; + + switch (reptype) { + case F54_8BIT_IMAGE: + *pixfmt = V4L2_TCH_FMT_DELTA_TD08; + break; + + case F54_16BIT_IMAGE: + *pixfmt = V4L2_TCH_FMT_DELTA_TD16; + break; + + case F54_RAW_16BIT_IMAGE: + case F54_TRUE_BASELINE: + case F54_FULL_RAW_CAP: + case F54_FULL_RAW_CAP_RX_OFFSET_REMOVED: + *pixfmt = V4L2_TCH_FMT_TU16; + break; + + case F54_REPORT_NONE: + case F54_MAX_REPORT_TYPE: + ret = -EINVAL; + break; + } + + return ret; +} + +static const struct v4l2_file_operations rmi_f54_video_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .unlocked_ioctl = video_ioctl2, + .read = vb2_fop_read, + .mmap = vb2_fop_mmap, + .poll = vb2_fop_poll, +}; + +static int rmi_f54_queue_setup(struct vb2_queue *q, unsigned int *nbuffers, + unsigned int *nplanes, unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct f54_data *f54 = q->drv_priv; + + if (*nplanes) + return sizes[0] < rmi_f54_get_report_size(f54) ? -EINVAL : 0; + + *nplanes = 1; + sizes[0] = rmi_f54_get_report_size(f54); + + return 0; +} + +static void rmi_f54_buffer_queue(struct vb2_buffer *vb) +{ + struct f54_data *f54 = vb2_get_drv_priv(vb->vb2_queue); + u16 *ptr; + enum vb2_buffer_state state; + enum rmi_f54_report_type reptype; + int ret; + + mutex_lock(&f54->status_mutex); + + reptype = rmi_f54_get_reptype(f54, f54->input); + if (reptype == F54_REPORT_NONE) { + state = VB2_BUF_STATE_ERROR; + goto done; + } + + if (f54->is_busy) { + state = VB2_BUF_STATE_ERROR; + goto done; + } + + ret = rmi_f54_request_report(f54->fn, reptype); + if (ret) { + dev_err(&f54->fn->dev, "Error requesting F54 report\n"); + state = VB2_BUF_STATE_ERROR; + goto done; + } + + /* get frame data */ + mutex_lock(&f54->data_mutex); + + while (f54->is_busy) { + mutex_unlock(&f54->data_mutex); + if (!wait_for_completion_timeout(&f54->cmd_done, + msecs_to_jiffies(1000))) { + dev_err(&f54->fn->dev, "Timed out\n"); + state = VB2_BUF_STATE_ERROR; + goto done; + } + mutex_lock(&f54->data_mutex); + } + + ptr = vb2_plane_vaddr(vb, 0); + if (!ptr) { + dev_err(&f54->fn->dev, "Error acquiring frame ptr\n"); + state = VB2_BUF_STATE_ERROR; + goto data_done; + } + + memcpy(ptr, f54->report_data, f54->report_size); + vb2_set_plane_payload(vb, 0, rmi_f54_get_report_size(f54)); + state = VB2_BUF_STATE_DONE; + +data_done: + mutex_unlock(&f54->data_mutex); +done: + vb2_buffer_done(vb, state); + mutex_unlock(&f54->status_mutex); +} + +/* V4L2 structures */ +static const struct vb2_ops rmi_f54_queue_ops = { + .queue_setup = rmi_f54_queue_setup, + .buf_queue = rmi_f54_buffer_queue, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static const struct vb2_queue rmi_f54_queue = { + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ, + .buf_struct_size = sizeof(struct vb2_buffer), + .ops = &rmi_f54_queue_ops, + .mem_ops = &vb2_vmalloc_memops, + .timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC, + .min_buffers_needed = 1, +}; + +static int rmi_f54_vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct f54_data *f54 = video_drvdata(file); + + strlcpy(cap->driver, F54_NAME, sizeof(cap->driver)); + strlcpy(cap->card, SYNAPTICS_INPUT_DEVICE_NAME, sizeof(cap->card)); + snprintf(cap->bus_info, sizeof(cap->bus_info), + "rmi4:%s", dev_name(&f54->fn->dev)); + + return 0; +} + +static int rmi_f54_vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *i) +{ + struct f54_data *f54 = video_drvdata(file); + enum rmi_f54_report_type reptype; + + reptype = rmi_f54_get_reptype(f54, i->index); + if (reptype == F54_REPORT_NONE) + return -EINVAL; + + i->type = V4L2_INPUT_TYPE_TOUCH; + + strlcpy(i->name, rmi_f54_report_type_names[reptype], sizeof(i->name)); + return 0; +} + +static int rmi_f54_set_input(struct f54_data *f54, unsigned int i) +{ + struct v4l2_pix_format *f = &f54->format; + enum rmi_f54_report_type reptype; + int ret; + + reptype = rmi_f54_get_reptype(f54, i); + if (reptype == F54_REPORT_NONE) + return -EINVAL; + + ret = rmi_f54_get_pixel_fmt(reptype, &f->pixelformat); + if (ret) + return ret; + + f54->input = i; + + f->width = f54->num_rx_electrodes; + f->height = f54->num_tx_electrodes; + f->field = V4L2_FIELD_NONE; + f->colorspace = V4L2_COLORSPACE_RAW; + f->bytesperline = f->width * sizeof(u16); + f->sizeimage = f->width * f->height * sizeof(u16); + + return 0; +} + +static int rmi_f54_vidioc_s_input(struct file *file, void *priv, unsigned int i) +{ + return rmi_f54_set_input(video_drvdata(file), i); +} + +static int rmi_f54_vidioc_g_input(struct file *file, void *priv, + unsigned int *i) +{ + struct f54_data *f54 = video_drvdata(file); + + *i = f54->input; + + return 0; +} + +static int rmi_f54_vidioc_fmt(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct f54_data *f54 = video_drvdata(file); + + f->fmt.pix = f54->format; + + return 0; +} + +static int rmi_f54_vidioc_enum_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *fmt) +{ + if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + switch (fmt->index) { + case 0: + fmt->pixelformat = V4L2_TCH_FMT_DELTA_TD16; + break; + + case 1: + fmt->pixelformat = V4L2_TCH_FMT_DELTA_TD08; + break; + + case 2: + fmt->pixelformat = V4L2_TCH_FMT_TU16; + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int rmi_f54_vidioc_g_parm(struct file *file, void *fh, + struct v4l2_streamparm *a) +{ + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + a->parm.capture.readbuffers = 1; + a->parm.capture.timeperframe.numerator = 1; + a->parm.capture.timeperframe.denominator = 10; + return 0; +} + +static const struct v4l2_ioctl_ops rmi_f54_video_ioctl_ops = { + .vidioc_querycap = rmi_f54_vidioc_querycap, + + .vidioc_enum_fmt_vid_cap = rmi_f54_vidioc_enum_fmt, + .vidioc_s_fmt_vid_cap = rmi_f54_vidioc_fmt, + .vidioc_g_fmt_vid_cap = rmi_f54_vidioc_fmt, + .vidioc_try_fmt_vid_cap = rmi_f54_vidioc_fmt, + .vidioc_g_parm = rmi_f54_vidioc_g_parm, + + .vidioc_enum_input = rmi_f54_vidioc_enum_input, + .vidioc_g_input = rmi_f54_vidioc_g_input, + .vidioc_s_input = rmi_f54_vidioc_s_input, + + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, +}; + +static const struct video_device rmi_f54_video_device = { + .name = "Synaptics RMI4", + .fops = &rmi_f54_video_fops, + .ioctl_ops = &rmi_f54_video_ioctl_ops, + .release = video_device_release_empty, + .device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TOUCH | + V4L2_CAP_READWRITE | V4L2_CAP_STREAMING, +}; + +static void rmi_f54_work(struct work_struct *work) +{ + struct f54_data *f54 = container_of(work, struct f54_data, work.work); + struct rmi_function *fn = f54->fn; + u8 fifo[2]; + struct rmi_f54_reports *report; + int report_size; + u8 command; + u8 *data; + int error; + + data = f54->report_data; + report_size = rmi_f54_get_report_size(f54); + if (report_size == 0) { + dev_err(&fn->dev, "Bad report size, report type=%d\n", + f54->report_type); + error = -EINVAL; + goto error; /* retry won't help */ + } + f54->standard_report[0].size = report_size; + report = f54->standard_report; + + mutex_lock(&f54->data_mutex); + + /* + * Need to check if command has completed. + * If not try again later. + */ + error = rmi_read(fn->rmi_dev, f54->fn->fd.command_base_addr, + &command); + if (error) { + dev_err(&fn->dev, "Failed to read back command\n"); + goto error; + } + if (command & F54_GET_REPORT) { + if (time_after(jiffies, f54->timeout)) { + dev_err(&fn->dev, "Get report command timed out\n"); + error = -ETIMEDOUT; + } + report_size = 0; + goto error; + } + + rmi_dbg(RMI_DEBUG_FN, &fn->dev, "Get report command completed, reading data\n"); + + report_size = 0; + for (; report->size; report++) { + fifo[0] = report->start & 0xff; + fifo[1] = (report->start >> 8) & 0xff; + error = rmi_write_block(fn->rmi_dev, + fn->fd.data_base_addr + F54_FIFO_OFFSET, + fifo, sizeof(fifo)); + if (error) { + dev_err(&fn->dev, "Failed to set fifo start offset\n"); + goto abort; + } + + error = rmi_read_block(fn->rmi_dev, fn->fd.data_base_addr + + F54_REPORT_DATA_OFFSET, data, + report->size); + if (error) { + dev_err(&fn->dev, "%s: read [%d bytes] returned %d\n", + __func__, report->size, error); + goto abort; + } + data += report->size; + report_size += report->size; + } + +abort: + f54->report_size = error ? 0 : report_size; +error: + if (error) + report_size = 0; + + if (report_size == 0 && !error) { + queue_delayed_work(f54->workqueue, &f54->work, + msecs_to_jiffies(1)); + } else { + f54->is_busy = false; + complete(&f54->cmd_done); + } + + mutex_unlock(&f54->data_mutex); +} + +static int rmi_f54_attention(struct rmi_function *fn, unsigned long *irqbits) +{ + return 0; +} + +static int rmi_f54_config(struct rmi_function *fn) +{ + struct rmi_driver *drv = fn->rmi_dev->driver; + + drv->set_irq_bits(fn->rmi_dev, fn->irq_mask); + + return 0; +} + +static int rmi_f54_detect(struct rmi_function *fn) +{ + int error; + struct f54_data *f54; + + f54 = dev_get_drvdata(&fn->dev); + + error = rmi_read_block(fn->rmi_dev, fn->fd.query_base_addr, + &f54->qry, sizeof(f54->qry)); + if (error) { + dev_err(&fn->dev, "%s: Failed to query F54 properties\n", + __func__); + return error; + } + + f54->num_rx_electrodes = f54->qry[0]; + f54->num_tx_electrodes = f54->qry[1]; + f54->capabilities = f54->qry[2]; + f54->clock_rate = f54->qry[3] | (f54->qry[4] << 8); + f54->family = f54->qry[5]; + + rmi_dbg(RMI_DEBUG_FN, &fn->dev, "F54 num_rx_electrodes: %d\n", + f54->num_rx_electrodes); + rmi_dbg(RMI_DEBUG_FN, &fn->dev, "F54 num_tx_electrodes: %d\n", + f54->num_tx_electrodes); + rmi_dbg(RMI_DEBUG_FN, &fn->dev, "F54 capabilities: 0x%x\n", + f54->capabilities); + rmi_dbg(RMI_DEBUG_FN, &fn->dev, "F54 clock rate: 0x%x\n", + f54->clock_rate); + rmi_dbg(RMI_DEBUG_FN, &fn->dev, "F54 family: 0x%x\n", + f54->family); + + f54->is_busy = false; + + return 0; +} + +static int rmi_f54_probe(struct rmi_function *fn) +{ + struct f54_data *f54; + int ret; + u8 rx, tx; + + f54 = devm_kzalloc(&fn->dev, sizeof(struct f54_data), GFP_KERNEL); + if (!f54) + return -ENOMEM; + + f54->fn = fn; + dev_set_drvdata(&fn->dev, f54); + + ret = rmi_f54_detect(fn); + if (ret) + return ret; + + mutex_init(&f54->data_mutex); + mutex_init(&f54->status_mutex); + + rx = f54->num_rx_electrodes; + tx = f54->num_tx_electrodes; + f54->report_data = devm_kzalloc(&fn->dev, + sizeof(u16) * tx * rx, + GFP_KERNEL); + if (f54->report_data == NULL) + return -ENOMEM; + + INIT_DELAYED_WORK(&f54->work, rmi_f54_work); + + f54->workqueue = create_singlethread_workqueue("rmi4-poller"); + if (!f54->workqueue) + return -ENOMEM; + + rmi_f54_create_input_map(f54); + + /* register video device */ + strlcpy(f54->v4l2.name, F54_NAME, sizeof(f54->v4l2.name)); + ret = v4l2_device_register(&fn->dev, &f54->v4l2); + if (ret) { + dev_err(&fn->dev, "Unable to register video dev.\n"); + goto remove_wq; + } + + /* initialize the queue */ + mutex_init(&f54->lock); + f54->queue = rmi_f54_queue; + f54->queue.drv_priv = f54; + f54->queue.lock = &f54->lock; + f54->queue.dev = &fn->dev; + + ret = vb2_queue_init(&f54->queue); + if (ret) + goto remove_v4l2; + + f54->vdev = rmi_f54_video_device; + f54->vdev.v4l2_dev = &f54->v4l2; + f54->vdev.lock = &f54->lock; + f54->vdev.vfl_dir = VFL_DIR_RX; + f54->vdev.queue = &f54->queue; + video_set_drvdata(&f54->vdev, f54); + + ret = video_register_device(&f54->vdev, VFL_TYPE_TOUCH, -1); + if (ret) { + dev_err(&fn->dev, "Unable to register video subdevice."); + goto remove_v4l2; + } + + return 0; + +remove_v4l2: + v4l2_device_unregister(&f54->v4l2); +remove_wq: + cancel_delayed_work_sync(&f54->work); + flush_workqueue(f54->workqueue); + destroy_workqueue(f54->workqueue); + return ret; +} + +static void rmi_f54_remove(struct rmi_function *fn) +{ + struct f54_data *f54 = dev_get_drvdata(&fn->dev); + + video_unregister_device(&f54->vdev); + v4l2_device_unregister(&f54->v4l2); +} + +struct rmi_function_handler rmi_f54_handler = { + .driver = { + .name = F54_NAME, + }, + .func = 0x54, + .probe = rmi_f54_probe, + .config = rmi_f54_config, + .attention = rmi_f54_attention, + .remove = rmi_f54_remove, +}; -- cgit v1.2.3 From b497265266a8083547e9ca162ace72cbb41522f1 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Mon, 22 Aug 2016 06:28:23 -0300 Subject: [media] Input: sur40 - use new V4L2 touch input type Support both V4L2_TCH_FMT_TU08 and V4L2_PIX_FMT_GREY for backwards compatibility. Signed-off-by: Nick Dyer Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab Acked-by: Dmitry Torokhov --- drivers/input/touchscreen/sur40.c | 124 ++++++++++++++++++++++++++++---------- 1 file changed, 91 insertions(+), 33 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c index 4ea475775d58..3967d8c651cc 100644 --- a/drivers/input/touchscreen/sur40.c +++ b/drivers/input/touchscreen/sur40.c @@ -139,6 +139,27 @@ struct sur40_image_header { #define SUR40_GET_STATE 0xc5 /* 4 bytes state (?) */ #define SUR40_GET_SENSORS 0xb1 /* 8 bytes sensors */ +static const struct v4l2_pix_format sur40_pix_format[] = { + { + .pixelformat = V4L2_TCH_FMT_TU08, + .width = SENSOR_RES_X / 2, + .height = SENSOR_RES_Y / 2, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_SRGB, + .bytesperline = SENSOR_RES_X / 2, + .sizeimage = (SENSOR_RES_X/2) * (SENSOR_RES_Y/2), + }, + { + .pixelformat = V4L2_PIX_FMT_GREY, + .width = SENSOR_RES_X / 2, + .height = SENSOR_RES_Y / 2, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_SRGB, + .bytesperline = SENSOR_RES_X / 2, + .sizeimage = (SENSOR_RES_X/2) * (SENSOR_RES_Y/2), + } +}; + /* master device state */ struct sur40_state { @@ -149,6 +170,7 @@ struct sur40_state { struct v4l2_device v4l2; struct video_device vdev; struct mutex lock; + struct v4l2_pix_format pix_fmt; struct vb2_queue queue; struct list_head buf_list; @@ -169,7 +191,6 @@ struct sur40_buffer { /* forward declarations */ static const struct video_device sur40_video_device; -static const struct v4l2_pix_format sur40_video_format; static const struct vb2_queue sur40_queue; static void sur40_process_video(struct sur40_state *sur40); @@ -420,7 +441,7 @@ static void sur40_process_video(struct sur40_state *sur40) goto err_poll; } - if (le32_to_cpu(img->size) != sur40_video_format.sizeimage) { + if (le32_to_cpu(img->size) != sur40->pix_fmt.sizeimage) { dev_err(sur40->dev, "image size mismatch\n"); goto err_poll; } @@ -431,7 +452,7 @@ static void sur40_process_video(struct sur40_state *sur40) result = usb_sg_init(&sgr, sur40->usbdev, usb_rcvbulkpipe(sur40->usbdev, VIDEO_ENDPOINT), 0, - sgt->sgl, sgt->nents, sur40_video_format.sizeimage, 0); + sgt->sgl, sgt->nents, sur40->pix_fmt.sizeimage, 0); if (result < 0) { dev_err(sur40->dev, "error %d in usb_sg_init\n", result); goto err_poll; @@ -586,13 +607,14 @@ static int sur40_probe(struct usb_interface *interface, if (error) goto err_unreg_v4l2; + sur40->pix_fmt = sur40_pix_format[0]; sur40->vdev = sur40_video_device; sur40->vdev.v4l2_dev = &sur40->v4l2; sur40->vdev.lock = &sur40->lock; sur40->vdev.queue = &sur40->queue; video_set_drvdata(&sur40->vdev, sur40); - error = video_register_device(&sur40->vdev, VFL_TYPE_GRABBER, -1); + error = video_register_device(&sur40->vdev, VFL_TYPE_TOUCH, -1); if (error) { dev_err(&interface->dev, "Unable to register video subdevice."); @@ -647,14 +669,16 @@ static int sur40_queue_setup(struct vb2_queue *q, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], struct device *alloc_devs[]) { + struct sur40_state *sur40 = vb2_get_drv_priv(q); + if (q->num_buffers + *nbuffers < 3) *nbuffers = 3 - q->num_buffers; if (*nplanes) - return sizes[0] < sur40_video_format.sizeimage ? -EINVAL : 0; + return sizes[0] < sur40->pix_fmt.sizeimage ? -EINVAL : 0; *nplanes = 1; - sizes[0] = sur40_video_format.sizeimage; + sizes[0] = sur40->pix_fmt.sizeimage; return 0; } @@ -666,7 +690,7 @@ static int sur40_queue_setup(struct vb2_queue *q, static int sur40_buffer_prepare(struct vb2_buffer *vb) { struct sur40_state *sur40 = vb2_get_drv_priv(vb->vb2_queue); - unsigned long size = sur40_video_format.sizeimage; + unsigned long size = sur40->pix_fmt.sizeimage; if (vb2_plane_size(vb, 0) < size) { dev_err(&sur40->usbdev->dev, "buffer too small (%lu < %lu)\n", @@ -741,7 +765,7 @@ static int sur40_vidioc_querycap(struct file *file, void *priv, strlcpy(cap->driver, DRIVER_SHORT, sizeof(cap->driver)); strlcpy(cap->card, DRIVER_LONG, sizeof(cap->card)); usb_make_path(sur40->usbdev, cap->bus_info, sizeof(cap->bus_info)); - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TOUCH | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; @@ -753,7 +777,7 @@ static int sur40_vidioc_enum_input(struct file *file, void *priv, { if (i->index != 0) return -EINVAL; - i->type = V4L2_INPUT_TYPE_CAMERA; + i->type = V4L2_INPUT_TYPE_TOUCH; i->std = V4L2_STD_UNKNOWN; strlcpy(i->name, "In-Cell Sensor", sizeof(i->name)); i->capabilities = 0; @@ -771,19 +795,57 @@ static int sur40_vidioc_g_input(struct file *file, void *priv, unsigned int *i) return 0; } -static int sur40_vidioc_fmt(struct file *file, void *priv, +static int sur40_vidioc_try_fmt(struct file *file, void *priv, + struct v4l2_format *f) +{ + switch (f->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_GREY: + f->fmt.pix = sur40_pix_format[1]; + break; + + default: + f->fmt.pix = sur40_pix_format[0]; + break; + } + + return 0; +} + +static int sur40_vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f) { - f->fmt.pix = sur40_video_format; + struct sur40_state *sur40 = video_drvdata(file); + + switch (f->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_GREY: + sur40->pix_fmt = sur40_pix_format[1]; + break; + + default: + sur40->pix_fmt = sur40_pix_format[0]; + break; + } + + f->fmt.pix = sur40->pix_fmt; + return 0; +} + +static int sur40_vidioc_g_fmt(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct sur40_state *sur40 = video_drvdata(file); + + f->fmt.pix = sur40->pix_fmt; return 0; } static int sur40_vidioc_enum_fmt(struct file *file, void *priv, struct v4l2_fmtdesc *f) { - if (f->index != 0) + if (f->index >= ARRAY_SIZE(sur40_pix_format)) return -EINVAL; - f->pixelformat = V4L2_PIX_FMT_GREY; + + f->pixelformat = sur40_pix_format[f->index].pixelformat; f->flags = 0; return 0; } @@ -791,22 +853,28 @@ static int sur40_vidioc_enum_fmt(struct file *file, void *priv, static int sur40_vidioc_enum_framesizes(struct file *file, void *priv, struct v4l2_frmsizeenum *f) { - if ((f->index != 0) || (f->pixel_format != V4L2_PIX_FMT_GREY)) + struct sur40_state *sur40 = video_drvdata(file); + + if ((f->index != 0) || ((f->pixel_format != V4L2_TCH_FMT_TU08) + && (f->pixel_format != V4L2_PIX_FMT_GREY))) return -EINVAL; f->type = V4L2_FRMSIZE_TYPE_DISCRETE; - f->discrete.width = sur40_video_format.width; - f->discrete.height = sur40_video_format.height; + f->discrete.width = sur40->pix_fmt.width; + f->discrete.height = sur40->pix_fmt.height; return 0; } static int sur40_vidioc_enum_frameintervals(struct file *file, void *priv, struct v4l2_frmivalenum *f) { - if ((f->index > 1) || (f->pixel_format != V4L2_PIX_FMT_GREY) - || (f->width != sur40_video_format.width) - || (f->height != sur40_video_format.height)) - return -EINVAL; + struct sur40_state *sur40 = video_drvdata(file); + + if ((f->index > 1) || ((f->pixel_format != V4L2_TCH_FMT_TU08) + && (f->pixel_format != V4L2_PIX_FMT_GREY)) + || (f->width != sur40->pix_fmt.width) + || (f->height != sur40->pix_fmt.height)) + return -EINVAL; f->type = V4L2_FRMIVAL_TYPE_DISCRETE; f->discrete.denominator = 60/(f->index+1); @@ -862,9 +930,9 @@ static const struct v4l2_ioctl_ops sur40_video_ioctl_ops = { .vidioc_querycap = sur40_vidioc_querycap, .vidioc_enum_fmt_vid_cap = sur40_vidioc_enum_fmt, - .vidioc_try_fmt_vid_cap = sur40_vidioc_fmt, - .vidioc_s_fmt_vid_cap = sur40_vidioc_fmt, - .vidioc_g_fmt_vid_cap = sur40_vidioc_fmt, + .vidioc_try_fmt_vid_cap = sur40_vidioc_try_fmt, + .vidioc_s_fmt_vid_cap = sur40_vidioc_s_fmt, + .vidioc_g_fmt_vid_cap = sur40_vidioc_g_fmt, .vidioc_enum_framesizes = sur40_vidioc_enum_framesizes, .vidioc_enum_frameintervals = sur40_vidioc_enum_frameintervals, @@ -891,16 +959,6 @@ static const struct video_device sur40_video_device = { .release = video_device_release_empty, }; -static const struct v4l2_pix_format sur40_video_format = { - .pixelformat = V4L2_PIX_FMT_GREY, - .width = SENSOR_RES_X / 2, - .height = SENSOR_RES_Y / 2, - .field = V4L2_FIELD_NONE, - .colorspace = V4L2_COLORSPACE_SRGB, - .bytesperline = SENSOR_RES_X / 2, - .sizeimage = (SENSOR_RES_X/2) * (SENSOR_RES_Y/2), -}; - /* USB-specific object needed to register this driver with the USB subsystem. */ static struct usb_driver sur40_driver = { .name = DRIVER_SHORT, -- cgit v1.2.3 From f223c30f1ee9d327df2761f0546c413aabe8f356 Mon Sep 17 00:00:00 2001 From: Florian Echtler Date: Tue, 31 May 2016 17:15:31 -0300 Subject: [media] sur40: properly report a single frame rate of 60 FPS The device hardware is always running at 60 FPS, so report this both via PARM_IOCTL and ENUM_FRAMEINTERVALS. [hans.verkuil@cisco.com: fix suspect indent checkpatch warning] [mchehab@s-opensource.com: fix a trivial merge conflict] Signed-off-by: Martin Kaltenbrunner Signed-off-by: Florian Echtler Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/input/touchscreen/sur40.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c index 3967d8c651cc..aefb6e11f88a 100644 --- a/drivers/input/touchscreen/sur40.c +++ b/drivers/input/touchscreen/sur40.c @@ -839,6 +839,19 @@ static int sur40_vidioc_g_fmt(struct file *file, void *priv, return 0; } +static int sur40_ioctl_parm(struct file *file, void *priv, + struct v4l2_streamparm *p) +{ + if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + p->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; + p->parm.capture.timeperframe.numerator = 1; + p->parm.capture.timeperframe.denominator = 60; + p->parm.capture.readbuffers = 3; + return 0; +} + static int sur40_vidioc_enum_fmt(struct file *file, void *priv, struct v4l2_fmtdesc *f) { @@ -870,14 +883,14 @@ static int sur40_vidioc_enum_frameintervals(struct file *file, void *priv, { struct sur40_state *sur40 = video_drvdata(file); - if ((f->index > 1) || ((f->pixel_format != V4L2_TCH_FMT_TU08) + if ((f->index > 0) || ((f->pixel_format != V4L2_TCH_FMT_TU08) && (f->pixel_format != V4L2_PIX_FMT_GREY)) || (f->width != sur40->pix_fmt.width) || (f->height != sur40->pix_fmt.height)) return -EINVAL; f->type = V4L2_FRMIVAL_TYPE_DISCRETE; - f->discrete.denominator = 60/(f->index+1); + f->discrete.denominator = 60; f->discrete.numerator = 1; return 0; } @@ -937,6 +950,9 @@ static const struct v4l2_ioctl_ops sur40_video_ioctl_ops = { .vidioc_enum_framesizes = sur40_vidioc_enum_framesizes, .vidioc_enum_frameintervals = sur40_vidioc_enum_frameintervals, + .vidioc_g_parm = sur40_ioctl_parm, + .vidioc_s_parm = sur40_ioctl_parm, + .vidioc_enum_input = sur40_vidioc_enum_input, .vidioc_g_input = sur40_vidioc_g_input, .vidioc_s_input = sur40_vidioc_s_input, -- cgit v1.2.3 From 7a4b9a29f8e5e16c28dbc56a8d2da7b6b3e482c7 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 12 Sep 2016 12:30:32 -0300 Subject: [media] Input: atmel_mxt: disallow impossible configuration The newnly added debug mode for the atmel_mxt_ts driver relies on the v4l2 interface and vb2_vmalloc, but those might be configured as loadable modules when the driver itself is built-in, resulting in a link failure: drivers/input/touchscreen/atmel_mxt_ts.o: In function `mxt_vidioc_querycap': atmel_mxt_ts.c:(.text.mxt_vidioc_querycap+0x10): undefined reference to `video_devdata' drivers/input/touchscreen/atmel_mxt_ts.o: In function `mxt_buffer_queue': atmel_mxt_ts.c:(.text.mxt_buffer_queue+0x20): undefined reference to `vb2_plane_vaddr' atmel_mxt_ts.c:(.text.mxt_buffer_queue+0x164): undefined reference to `vb2_buffer_done' drivers/input/touchscreen/atmel_mxt_ts.o: In function `mxt_free_object_table': atmel_mxt_ts.c:(.text.mxt_free_object_table+0x18): undefined reference to `video_unregister_device' atmel_mxt_ts.c:(.text.mxt_free_object_table+0x20): undefined reference to `v4l2_device_unregister' The best workaround I could come up with is to disallow the debug mode unless it's actually possible to call it. Fixes: ecfdd7e2660e ("[media] Input: atmel_mxt_ts - output diagnostic debug via V4L2 device") Signed-off-by: Arnd Bergmann Acked-by: Nick Dyer Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/input/touchscreen/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 754e6aa0851d..ec41df887278 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -117,7 +117,8 @@ config TOUCHSCREEN_ATMEL_MXT config TOUCHSCREEN_ATMEL_MXT_T37 bool "Support T37 Diagnostic Data" - depends on TOUCHSCREEN_ATMEL_MXT && VIDEO_V4L2 + depends on TOUCHSCREEN_ATMEL_MXT + depends on VIDEO_V4L2=y || (TOUCHSCREEN_ATMEL_MXT=m && VIDEO_V4L2=m) select VIDEOBUF2_VMALLOC help Say Y here if you want support to output data from the T37 -- cgit v1.2.3 From 47d8e00ca3f9269498b4333d5c787f1442ebdbb6 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 12 Sep 2016 12:30:33 -0300 Subject: [media] Input: synaptics-rmi4: disallow impossible configuration The newly added debug mode for the synaptics-rmi4 driver relies on the v4l2 interface and vb2_vmalloc, but those might be configured as loadable modules when the driver itself is built-in, resulting in a link failure: drivers/input/rmi4/rmi_core.o: In function `rmi_f54_remove': rmi_f54.c:(.text.rmi_f54_remove+0x14): undefined reference to `video_unregister_device' rmi_f54.c:(.text.rmi_f54_remove+0x20): undefined reference to `v4l2_device_unregister' drivers/input/rmi4/rmi_core.o: In function `rmi_f54_vidioc_s_input': rmi_f54.c:(.text.rmi_f54_vidioc_s_input+0x10): undefined reference to `video_devdata' drivers/input/rmi4/rmi_core.o: In function `rmi_f54_vidioc_g_input': rmi_f54.c:(.text.rmi_f54_vidioc_g_input+0x10): undefined reference to `video_devdata' drivers/input/rmi4/rmi_core.o: In function `rmi_f54_vidioc_fmt': rmi_f54.c:(.text.rmi_f54_vidioc_fmt+0x10): undefined reference to `video_devdata' drivers/input/rmi4/rmi_core.o: In function `rmi_f54_vidioc_enum_input': rmi_f54.c:(.text.rmi_f54_vidioc_enum_input+0x10): undefined reference to `video_devdata' drivers/input/rmi4/rmi_core.o: In function `rmi_f54_vidioc_querycap': ... The best workaround I could come up with is to disallow the debug mode unless it's actually possible to call it. Fixes: 3a762dbd5347 ("[media] Input: synaptics-rmi4 - add support for F54 diagnostics") Signed-off-by: Arnd Bergmann Acked-by: Nick Dyer Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/input/rmi4/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/rmi4/Kconfig b/drivers/input/rmi4/Kconfig index f3418b65eb41..4c8a55857e00 100644 --- a/drivers/input/rmi4/Kconfig +++ b/drivers/input/rmi4/Kconfig @@ -65,7 +65,7 @@ config RMI4_F30 config RMI4_F54 bool "RMI4 Function 54 (Analog diagnostics)" depends on RMI4_CORE - depends on VIDEO_V4L2 + depends on VIDEO_V4L2=y || (RMI4_CORE=m && VIDEO_V4L2=m) select VIDEOBUF2_VMALLOC help Say Y here if you want to add support for RMI4 function 54 -- cgit v1.2.3 From f3c4a8f8d8eb3ed97bccf4776079af3ec5daa147 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 15 Sep 2016 17:30:43 -0300 Subject: [media] Input: v4l-touch - add copyright lines Add copyright lines for Zodiac who paid for the V4L touch work. Signed-off-by: Nick Dyer Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/input/rmi4/rmi_f54.c | 1 + drivers/input/touchscreen/atmel_mxt_ts.c | 1 + 2 files changed, 2 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/rmi4/rmi_f54.c b/drivers/input/rmi4/rmi_f54.c index bd86d3d599e7..cf805b960866 100644 --- a/drivers/input/rmi4/rmi_f54.c +++ b/drivers/input/rmi4/rmi_f54.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2012-2015 Synaptics Incorporated + * Copyright (C) 2016 Zodiac Inflight Innovations * * 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 diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index beede8fe511c..e5d185fe69b9 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -4,6 +4,7 @@ * Copyright (C) 2010 Samsung Electronics Co.Ltd * Copyright (C) 2011-2014 Atmel Corporation * Copyright (C) 2012 Google, Inc. + * Copyright (C) 2016 Zodiac Inflight Innovations * * Author: Joonyoung Shim * -- cgit v1.2.3 From e2aad66638f995f825e637ef621f45d902c130d1 Mon Sep 17 00:00:00 2001 From: Sangwon Jee Date: Fri, 21 Oct 2016 15:28:59 -0700 Subject: Input: melfas_mip4 - add product_id sysfs attribute Add product_id sysfs attribute and update protocol version to support it. Signed-off-by: Sangwon Jee Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/melfas_mip4.c | 40 +++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/melfas_mip4.c b/drivers/input/touchscreen/melfas_mip4.c index 552a3773f79d..8739cb718b4c 100644 --- a/drivers/input/touchscreen/melfas_mip4.c +++ b/drivers/input/touchscreen/melfas_mip4.c @@ -33,7 +33,7 @@ /***************************************************************** * Protocol - * Version : MIP 4.0 Rev 4.6 + * Version : MIP 4.0 Rev 5.4 *****************************************************************/ /* Address */ @@ -81,6 +81,9 @@ #define MIP4_R1_INFO_IC_HW_CATEGORY 0x77 #define MIP4_R1_INFO_CONTACT_THD_SCR 0x78 #define MIP4_R1_INFO_CONTACT_THD_KEY 0x7A +#define MIP4_R1_INFO_PID 0x7C +#define MIP4_R1_INFO_VID 0x7E +#define MIP4_R1_INFO_SLAVE_ADDR 0x80 #define MIP4_R0_EVENT 0x02 #define MIP4_R1_EVENT_SUPPORTED_FUNC 0x00 @@ -157,6 +160,7 @@ struct mip4_ts { char phys[32]; char product_name[16]; + u16 product_id; char ic_name[4]; unsigned int max_x; @@ -264,6 +268,18 @@ static int mip4_query_device(struct mip4_ts *ts) dev_dbg(&ts->client->dev, "product name: %.*s\n", (int)sizeof(ts->product_name), ts->product_name); + /* Product ID */ + cmd[0] = MIP4_R0_INFO; + cmd[1] = MIP4_R1_INFO_PID; + error = mip4_i2c_xfer(ts, cmd, sizeof(cmd), buf, 2); + if (error) { + dev_warn(&ts->client->dev, + "Failed to retrieve product id: %d\n", error); + } else { + ts->product_id = get_unaligned_le16(&buf[0]); + dev_dbg(&ts->client->dev, "product id: %04X\n", ts->product_id); + } + /* IC name */ cmd[0] = MIP4_R0_INFO; cmd[1] = MIP4_R1_INFO_IC_NAME; @@ -1348,6 +1364,25 @@ static ssize_t mip4_sysfs_read_hw_version(struct device *dev, static DEVICE_ATTR(hw_version, S_IRUGO, mip4_sysfs_read_hw_version, NULL); +static ssize_t mip4_sysfs_read_product_id(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct mip4_ts *ts = i2c_get_clientdata(client); + size_t count; + + mutex_lock(&ts->input->mutex); + + count = snprintf(buf, PAGE_SIZE, "%04X\n", ts->product_id); + + mutex_unlock(&ts->input->mutex); + + return count; +} + +static DEVICE_ATTR(product_id, S_IRUGO, mip4_sysfs_read_product_id, NULL); + static ssize_t mip4_sysfs_read_ic_name(struct device *dev, struct device_attribute *attr, char *buf) @@ -1371,6 +1406,7 @@ static DEVICE_ATTR(ic_name, S_IRUGO, mip4_sysfs_read_ic_name, NULL); static struct attribute *mip4_attrs[] = { &dev_attr_fw_version.attr, &dev_attr_hw_version.attr, + &dev_attr_product_id.attr, &dev_attr_ic_name.attr, &dev_attr_update_fw.attr, NULL, @@ -1572,6 +1608,6 @@ static struct i2c_driver mip4_driver = { module_i2c_driver(mip4_driver); MODULE_DESCRIPTION("MELFAS MIP4 Touchscreen"); -MODULE_VERSION("2016.09.28"); +MODULE_VERSION("2016.10.20"); MODULE_AUTHOR("Sangwon Jee "); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 77b425399f6d5f1b04c58738bc10ca6decb10c9a Mon Sep 17 00:00:00 2001 From: Marcos Paulo de Souza Date: Fri, 21 Oct 2016 15:35:33 -0700 Subject: Input: i8042 - use chassis info to skip selftest on Asus laptops Instead of relying on this model zoo let's skip selftest on all newer Asus laptops (newer as in when they changed "Computer" -> "COMPUTER" in their DMI data). Signed-off-by: Marcos Paulo de Souza Signed-off-by: Dmitry Torokhov --- drivers/input/serio/i8042-x86ia64io.h | 74 +---------------------------------- 1 file changed, 1 insertion(+), 73 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h index f4bfb4b2d50a..b42787e21d0a 100644 --- a/drivers/input/serio/i8042-x86ia64io.h +++ b/drivers/input/serio/i8042-x86ia64io.h @@ -517,79 +517,7 @@ static const struct dmi_system_id i8042_dmi_noselftest_table[] = { { .matches = { DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - DMI_MATCH(DMI_PRODUCT_NAME, "A455LD"), - }, - }, - { - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - DMI_MATCH(DMI_PRODUCT_NAME, "K401LB"), - }, - }, - { - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - DMI_MATCH(DMI_PRODUCT_NAME, "K501LB"), - }, - }, - { - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - DMI_MATCH(DMI_PRODUCT_NAME, "K501LX"), - }, - }, - { - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - DMI_MATCH(DMI_PRODUCT_NAME, "R409L"), - }, - }, - { - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - DMI_MATCH(DMI_PRODUCT_NAME, "V502LX"), - }, - }, - { - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - DMI_MATCH(DMI_PRODUCT_NAME, "X302LA"), - }, - }, - { - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - DMI_MATCH(DMI_PRODUCT_NAME, "X450LCP"), - }, - }, - { - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - DMI_MATCH(DMI_PRODUCT_NAME, "X450LD"), - }, - }, - { - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - DMI_MATCH(DMI_PRODUCT_NAME, "X455LAB"), - }, - }, - { - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - DMI_MATCH(DMI_PRODUCT_NAME, "X455LDB"), - }, - }, - { - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - DMI_MATCH(DMI_PRODUCT_NAME, "X455LF"), - }, - }, - { - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - DMI_MATCH(DMI_PRODUCT_NAME, "Z450LA"), + DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /* Notebook */ }, }, { } -- cgit v1.2.3 From 6d86c4714da526243d7b0578eaf5e1b54f8c2ed2 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 25 Oct 2016 09:35:40 -0700 Subject: Input: pxa27x_keypad - fix typo "debpunce" -> "debounce" Trivial fix to typo in dev_err message Signed-off-by: Colin Ian King Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/pxa27x_keypad.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/pxa27x_keypad.c b/drivers/input/keyboard/pxa27x_keypad.c index fcef5d1365e2..e24443376e75 100644 --- a/drivers/input/keyboard/pxa27x_keypad.c +++ b/drivers/input/keyboard/pxa27x_keypad.c @@ -316,7 +316,7 @@ static int pxa27x_keypad_build_keycode_from_dt(struct pxa27x_keypad *keypad) error = of_property_read_u32(np, "marvell,debounce-interval", &pdata->debounce_interval); if (error) { - dev_err(dev, "failed to parse debpunce-interval\n"); + dev_err(dev, "failed to parse debounce-interval\n"); return error; } -- cgit v1.2.3 From 04d7ad83c6a61395582cd236aa6dc61c8430e227 Mon Sep 17 00:00:00 2001 From: Steve Twiss Date: Wed, 26 Oct 2016 15:49:13 -0700 Subject: Input: da9061 - onkey driver Copyright header is updated to add DA9061 in its description and the module description macro is extended to include DA9061. Minor change to the code, alters dev_dbg() statements to report a generic "PMIC" instead of DA9063. This device driver is compatible with DA9061, DA9062 and DA9063. Kconfig is updated to reflect support for DA9061/62/63. Signed-off-by: Steve Twiss Signed-off-by: Dmitry Torokhov --- drivers/input/misc/Kconfig | 7 ++++--- drivers/input/misc/da9063_onkey.c | 8 ++++---- 2 files changed, 8 insertions(+), 7 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 7ffb614ce566..1ae4d9617ff8 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -625,11 +625,12 @@ config INPUT_DA9055_ONKEY will be called da9055_onkey. config INPUT_DA9063_ONKEY - tristate "Dialog DA9062/63 OnKey" + tristate "Dialog DA9063/62/61 OnKey" depends on MFD_DA9063 || MFD_DA9062 help - Support the ONKEY of Dialog DA9063 and DA9062 Power Management ICs - as an input device capable of reporting the power button status. + Support the ONKEY of Dialog DA9063, DA9062 and DA9061 Power + Management ICs as an input device capable of reporting the + power button status. To compile this driver as a module, choose M here: the module will be called da9063_onkey. diff --git a/drivers/input/misc/da9063_onkey.c b/drivers/input/misc/da9063_onkey.c index bb863e062b03..dff6fe5724fe 100644 --- a/drivers/input/misc/da9063_onkey.c +++ b/drivers/input/misc/da9063_onkey.c @@ -1,5 +1,5 @@ /* - * OnKey device driver for DA9063 and DA9062 PMICs + * OnKey device driver for DA9063, DA9062 and DA9061 PMICs * Copyright (C) 2015 Dialog Semiconductor Ltd. * * This program is free software; you can redistribute it and/or @@ -149,13 +149,13 @@ static void da9063_poll_on(struct work_struct *work) * and then send shutdown command */ dev_dbg(&onkey->input->dev, - "Sending SHUTDOWN to DA9063 ...\n"); + "Sending SHUTDOWN to PMIC ...\n"); error = regmap_write(onkey->regmap, config->onkey_shutdown, config->onkey_shutdown_mask); if (error) dev_err(&onkey->input->dev, - "Cannot SHUTDOWN DA9063: %d\n", + "Cannot SHUTDOWN PMIC: %d\n", error); } } @@ -300,6 +300,6 @@ static struct platform_driver da9063_onkey_driver = { module_platform_driver(da9063_onkey_driver); MODULE_AUTHOR("S Twiss "); -MODULE_DESCRIPTION("Onkey device driver for Dialog DA9063 and DA9062"); +MODULE_DESCRIPTION("Onkey device driver for Dialog DA9063, DA9062 and DA9061"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:" DA9063_DRVNAME_ONKEY); -- cgit v1.2.3 From c2c4ba82022cbe14ab15f394078aaf592664bd69 Mon Sep 17 00:00:00 2001 From: Sangwon Jee Date: Wed, 26 Oct 2016 15:53:13 -0700 Subject: Input: melfas_mip4 - use product id for firmware name Use product id for firmware name to request compatible firmware. Signed-off-by: Sangwon Jee Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/melfas_mip4.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/melfas_mip4.c b/drivers/input/touchscreen/melfas_mip4.c index 8739cb718b4c..0f2230a9b1a1 100644 --- a/drivers/input/touchscreen/melfas_mip4.c +++ b/drivers/input/touchscreen/melfas_mip4.c @@ -162,6 +162,7 @@ struct mip4_ts { char product_name[16]; u16 product_id; char ic_name[4]; + char fw_name[32]; unsigned int max_x; unsigned int max_y; @@ -280,6 +281,11 @@ static int mip4_query_device(struct mip4_ts *ts) dev_dbg(&ts->client->dev, "product id: %04X\n", ts->product_id); } + /* Firmware name */ + snprintf(ts->fw_name, sizeof(ts->fw_name), + "melfas_mip4_%04X.fw", ts->product_id); + dev_dbg(&ts->client->dev, "firmware name: %s\n", ts->fw_name); + /* IC name */ cmd[0] = MIP4_R0_INFO; cmd[1] = MIP4_R1_INFO_IC_NAME; @@ -1285,11 +1291,11 @@ static ssize_t mip4_sysfs_fw_update(struct device *dev, const struct firmware *fw; int error; - error = request_firmware(&fw, MIP4_FW_NAME, dev); + error = request_firmware(&fw, ts->fw_name, dev); if (error) { dev_err(&ts->client->dev, "Failed to retrieve firmware %s: %d\n", - MIP4_FW_NAME, error); + ts->fw_name, error); return error; } @@ -1608,6 +1614,6 @@ static struct i2c_driver mip4_driver = { module_i2c_driver(mip4_driver); MODULE_DESCRIPTION("MELFAS MIP4 Touchscreen"); -MODULE_VERSION("2016.10.20"); +MODULE_VERSION("2016.10.24"); MODULE_AUTHOR("Sangwon Jee "); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From ea4348c8462a20e8b1b6455a7145d2b86f8a49b6 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 26 Oct 2016 15:55:02 -0700 Subject: Input: tca8418_keypad - hide gcc-4.9 -Wmaybe-uninitialized warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Older versions of gcc warn about the tca8418_irq_handler function as they can't keep track of the variable assignment inside of the loop when using the -Wmaybe-unintialized flag: drivers/input/keyboard/tca8418_keypad.c: In function ‘tca8418_irq_handler’: drivers/input/keyboard/tca8418_keypad.c:172:9: error: ‘reg’ may be used uninitialized in this function [-Werror=maybe-uninitialized] drivers/input/keyboard/tca8418_keypad.c:165:5: note: ‘reg’ was declared here This is fixed in gcc-6, but it's possible to rearrange the code in a way that avoids the warning on older compilers as well. Signed-off-by: Arnd Bergmann Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/tca8418_keypad.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/tca8418_keypad.c b/drivers/input/keyboard/tca8418_keypad.c index 9002298698fc..3048ef3e3e16 100644 --- a/drivers/input/keyboard/tca8418_keypad.c +++ b/drivers/input/keyboard/tca8418_keypad.c @@ -164,11 +164,18 @@ static void tca8418_read_keypad(struct tca8418_keypad *keypad_data) int error, col, row; u8 reg, state, code; - /* Initial read of the key event FIFO */ - error = tca8418_read_byte(keypad_data, REG_KEY_EVENT_A, ®); + do { + error = tca8418_read_byte(keypad_data, REG_KEY_EVENT_A, ®); + if (error < 0) { + dev_err(&keypad_data->client->dev, + "unable to read REG_KEY_EVENT_A\n"); + break; + } + + /* Assume that key code 0 signifies empty FIFO */ + if (reg <= 0) + break; - /* Assume that key code 0 signifies empty FIFO */ - while (error >= 0 && reg > 0) { state = reg & KEY_EVENT_VALUE; code = reg & KEY_EVENT_CODE; @@ -184,11 +191,7 @@ static void tca8418_read_keypad(struct tca8418_keypad *keypad_data) /* Read for next loop */ error = tca8418_read_byte(keypad_data, REG_KEY_EVENT_A, ®); - } - - if (error < 0) - dev_err(&keypad_data->client->dev, - "unable to read REG_KEY_EVENT_A\n"); + } while (1); input_sync(input); } -- cgit v1.2.3 From 39a0d75ae32c061fa403824e56438501d7fb09f7 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 26 Oct 2016 16:26:19 -0700 Subject: Input: elan_i2c - always output the device information it's always easier to retrieve these information in bug reports when it is always printed in the dmesg. Signed-off-by: Benjamin Tissoires Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/elan_i2c_core.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c index d15b33813021..fa598f7f4372 100644 --- a/drivers/input/mouse/elan_i2c_core.c +++ b/drivers/input/mouse/elan_i2c_core.c @@ -1093,19 +1093,18 @@ static int elan_probe(struct i2c_client *client, if (error) return error; + dev_info(&client->dev, + "Elan Touchpad: Module ID: 0x%04x, Firmware: 0x%04x, Sample: 0x%04x, IAP: 0x%04x\n", + data->product_id, + data->fw_version, + data->sm_version, + data->iap_version); + dev_dbg(&client->dev, - "Elan Touchpad Information:\n" - " Module product ID: 0x%04x\n" - " Firmware Version: 0x%04x\n" - " Sample Version: 0x%04x\n" - " IAP Version: 0x%04x\n" + "Elan Touchpad Extra Information:\n" " Max ABS X,Y: %d,%d\n" " Width X,Y: %d,%d\n" " Resolution X,Y: %d,%d (dots/mm)\n", - data->product_id, - data->fw_version, - data->sm_version, - data->iap_version, data->max_x, data->max_y, data->width_x, data->width_y, data->x_res, data->y_res); -- cgit v1.2.3 From e1cb73f6f53676fa6ca7eec6b3c371cf68a6797e Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Wed, 26 Oct 2016 16:46:20 -0700 Subject: Input: fsl-imx25-tcq - fix module autoload when registered via OF If the driver is built as a module, autoload won't work because the module alias information is not filled. So user-space can't match the registered device with the corresponding module. Export the module alias information using the MODULE_DEVICE_TABLE() macro. Before this patch: $ modinfo drivers/input/touchscreen/fsl-imx25-tcq.ko | grep alias After this patch: $ modinfo drivers/input/touchscreen/fsl-imx25-tcq.ko | grep alias alias: of:N*T*Cfsl,imx25-tcqC* alias: of:N*T*Cfsl,imx25-tcq Signed-off-by: Javier Martinez Canillas Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/fsl-imx25-tcq.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/fsl-imx25-tcq.c b/drivers/input/touchscreen/fsl-imx25-tcq.c index fe9877a6af9e..d50ee490c9cc 100644 --- a/drivers/input/touchscreen/fsl-imx25-tcq.c +++ b/drivers/input/touchscreen/fsl-imx25-tcq.c @@ -55,6 +55,7 @@ static const struct of_device_id mx25_tcq_ids[] = { { .compatible = "fsl,imx25-tcq", }, { /* Sentinel */ } }; +MODULE_DEVICE_TABLE(of, mx25_tcq_ids); #define TSC_4WIRE_PRE_INDEX 0 #define TSC_4WIRE_X_INDEX 1 -- cgit v1.2.3 From 8dd5e0b364e5b83864cd918475e5a5ef3b352446 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Wed, 26 Oct 2016 16:46:41 -0700 Subject: Input: da9063 - fix module autoload when registered via OF If the driver is built as a module, autoload won't work because the module alias information is not filled. So user-space can't match the registered device with the corresponding module. Export the module alias information using the MODULE_DEVICE_TABLE() macro. Before this patch: $ modinfo drivers/input/misc/da9063_onkey.ko | grep alias alias: platform:da9063-onkey After this patch: $ modinfo drivers/input/misc/da9063_onkey.ko | grep alias alias: platform:da9063-onkey alias: of:N*T*Cdlg,da9062-onkeyC* alias: of:N*T*Cdlg,da9062-onkey alias: of:N*T*Cdlg,da9063-onkeyC* alias: of:N*T*Cdlg,da9063-onkey Signed-off-by: Javier Martinez Canillas Signed-off-by: Dmitry Torokhov --- drivers/input/misc/da9063_onkey.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/input') diff --git a/drivers/input/misc/da9063_onkey.c b/drivers/input/misc/da9063_onkey.c index dff6fe5724fe..b4ff1e86d3d3 100644 --- a/drivers/input/misc/da9063_onkey.c +++ b/drivers/input/misc/da9063_onkey.c @@ -87,6 +87,7 @@ static const struct of_device_id da9063_compatible_reg_id_table[] = { { .compatible = "dlg,da9062-onkey", .data = &da9062_regs }, { }, }; +MODULE_DEVICE_TABLE(of, da9063_compatible_reg_id_table); static void da9063_poll_on(struct work_struct *work) { -- cgit v1.2.3 From 0f78ba96bbcf30a78224fe56f8fd72f87915afdd Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 23 Feb 2016 15:32:14 -0800 Subject: Input: gpio_keys_polled - keep button data constant Commit 633a21d80b4a ("input: gpio_keys_polled: Add support for GPIO descriptors") placed gpio descriptor into gpio_keys_button structure, which is supposed to be part of platform data and not modifiable by the driver. To keep the data constant, let's move the descriptor to gpio_keys_button_data structure instead. Tested-by: Mika Westerberg Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/gpio_keys.c | 10 +-- drivers/input/keyboard/gpio_keys_polled.c | 105 +++++++++++++++++------------- 2 files changed, 63 insertions(+), 52 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index 29093657f2ef..890eb397d987 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -624,7 +624,6 @@ gpio_keys_get_devtree_pdata(struct device *dev) struct gpio_keys_button *button; int error; int nbuttons; - int i; node = dev->of_node; if (!node) @@ -640,19 +639,18 @@ gpio_keys_get_devtree_pdata(struct device *dev) if (!pdata) return ERR_PTR(-ENOMEM); - pdata->buttons = (struct gpio_keys_button *)(pdata + 1); + button = (struct gpio_keys_button *)(pdata + 1); + + pdata->buttons = button; pdata->nbuttons = nbuttons; pdata->rep = !!of_get_property(node, "autorepeat", NULL); of_property_read_string(node, "label", &pdata->name); - i = 0; for_each_available_child_of_node(node, pp) { enum of_gpio_flags flags; - button = &pdata->buttons[i++]; - button->gpio = of_get_gpio_flags(pp, 0, &flags); if (button->gpio < 0) { error = button->gpio; @@ -694,6 +692,8 @@ gpio_keys_get_devtree_pdata(struct device *dev) if (of_property_read_u32(pp, "debounce-interval", &button->debounce_interval)) button->debounce_interval = 5; + + button++; } if (pdata->nbuttons == 0) diff --git a/drivers/input/keyboard/gpio_keys_polled.c b/drivers/input/keyboard/gpio_keys_polled.c index 62bdb1d48c49..2cf407831f06 100644 --- a/drivers/input/keyboard/gpio_keys_polled.c +++ b/drivers/input/keyboard/gpio_keys_polled.c @@ -30,6 +30,7 @@ #define DRV_NAME "gpio-keys-polled" struct gpio_keys_button_data { + struct gpio_desc *gpiod; int last_state; int count; int threshold; @@ -46,7 +47,7 @@ struct gpio_keys_polled_dev { }; static void gpio_keys_button_event(struct input_polled_dev *dev, - struct gpio_keys_button *button, + const struct gpio_keys_button *button, int state) { struct gpio_keys_polled_dev *bdev = dev->private; @@ -70,15 +71,15 @@ static void gpio_keys_button_event(struct input_polled_dev *dev, } static void gpio_keys_polled_check_state(struct input_polled_dev *dev, - struct gpio_keys_button *button, + const struct gpio_keys_button *button, struct gpio_keys_button_data *bdata) { int state; if (bdata->can_sleep) - state = !!gpiod_get_value_cansleep(button->gpiod); + state = !!gpiod_get_value_cansleep(bdata->gpiod); else - state = !!gpiod_get_value(button->gpiod); + state = !!gpiod_get_value(bdata->gpiod); gpio_keys_button_event(dev, button, state); @@ -142,48 +143,35 @@ static void gpio_keys_polled_close(struct input_polled_dev *dev) pdata->disable(bdev->dev); } -static struct gpio_keys_platform_data *gpio_keys_polled_get_devtree_pdata(struct device *dev) +static struct gpio_keys_platform_data * +gpio_keys_polled_get_devtree_pdata(struct device *dev) { struct gpio_keys_platform_data *pdata; struct gpio_keys_button *button; struct fwnode_handle *child; - int error; int nbuttons; nbuttons = device_get_child_node_count(dev); if (nbuttons == 0) - return NULL; + return ERR_PTR(-EINVAL); pdata = devm_kzalloc(dev, sizeof(*pdata) + nbuttons * sizeof(*button), GFP_KERNEL); if (!pdata) return ERR_PTR(-ENOMEM); - pdata->buttons = (struct gpio_keys_button *)(pdata + 1); + button = (struct gpio_keys_button *)(pdata + 1); + + pdata->buttons = button; + pdata->nbuttons = nbuttons; pdata->rep = device_property_present(dev, "autorepeat"); device_property_read_u32(dev, "poll-interval", &pdata->poll_interval); device_for_each_child_node(dev, child) { - struct gpio_desc *desc; - - desc = devm_get_gpiod_from_child(dev, NULL, child); - if (IS_ERR(desc)) { - error = PTR_ERR(desc); - if (error != -EPROBE_DEFER) - dev_err(dev, - "Failed to get gpio flags, error: %d\n", - error); - fwnode_handle_put(child); - return ERR_PTR(error); - } - - button = &pdata->buttons[pdata->nbuttons++]; - button->gpiod = desc; - - if (fwnode_property_read_u32(child, "linux,code", &button->code)) { - dev_err(dev, "Button without keycode: %d\n", - pdata->nbuttons - 1); + if (fwnode_property_read_u32(child, "linux,code", + &button->code)) { + dev_err(dev, "button without keycode\n"); fwnode_handle_put(child); return ERR_PTR(-EINVAL); } @@ -206,10 +194,9 @@ static struct gpio_keys_platform_data *gpio_keys_polled_get_devtree_pdata(struct if (fwnode_property_read_u32(child, "debounce-interval", &button->debounce_interval)) button->debounce_interval = 5; - } - if (pdata->nbuttons == 0) - return ERR_PTR(-EINVAL); + button++; + } return pdata; } @@ -220,7 +207,7 @@ static void gpio_keys_polled_set_abs_params(struct input_dev *input, int i, min = 0, max = 0; for (i = 0; i < pdata->nbuttons; i++) { - struct gpio_keys_button *button = &pdata->buttons[i]; + const struct gpio_keys_button *button = &pdata->buttons[i]; if (button->type != EV_ABS || button->code != code) continue; @@ -230,6 +217,7 @@ static void gpio_keys_polled_set_abs_params(struct input_dev *input, if (button->value > max) max = button->value; } + input_set_abs_params(input, code, min, max, 0, 0); } @@ -242,6 +230,7 @@ MODULE_DEVICE_TABLE(of, gpio_keys_polled_of_match); static int gpio_keys_polled_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; + struct fwnode_handle *child = NULL; const struct gpio_keys_platform_data *pdata = dev_get_platdata(dev); struct gpio_keys_polled_dev *bdev; struct input_polled_dev *poll_dev; @@ -254,10 +243,6 @@ static int gpio_keys_polled_probe(struct platform_device *pdev) pdata = gpio_keys_polled_get_devtree_pdata(dev); if (IS_ERR(pdata)) return PTR_ERR(pdata); - if (!pdata) { - dev_err(dev, "missing platform data\n"); - return -EINVAL; - } } if (!pdata->poll_interval) { @@ -300,20 +285,40 @@ static int gpio_keys_polled_probe(struct platform_device *pdev) __set_bit(EV_REP, input->evbit); for (i = 0; i < pdata->nbuttons; i++) { - struct gpio_keys_button *button = &pdata->buttons[i]; + const struct gpio_keys_button *button = &pdata->buttons[i]; struct gpio_keys_button_data *bdata = &bdev->data[i]; unsigned int type = button->type ?: EV_KEY; if (button->wakeup) { dev_err(dev, DRV_NAME " does not support wakeup\n"); + fwnode_handle_put(child); return -EINVAL; } - /* - * Legacy GPIO number so request the GPIO here and - * convert it to descriptor. - */ - if (!button->gpiod && gpio_is_valid(button->gpio)) { + if (!dev_get_platdata(dev)) { + /* No legacy static platform data */ + child = device_get_next_child_node(dev, child); + if (!child) { + dev_err(dev, "missing child device node\n"); + return -EINVAL; + } + + bdata->gpiod = devm_get_gpiod_from_child(dev, NULL, + child); + if (IS_ERR(bdata->gpiod)) { + error = PTR_ERR(bdata->gpiod); + if (error != -EPROBE_DEFER) + dev_err(dev, + "failed to get gpio: %d\n", + error); + fwnode_handle_put(child); + return error; + } + } else if (gpio_is_valid(button->gpio)) { + /* + * Legacy GPIO number so request the GPIO here and + * convert it to descriptor. + */ unsigned flags = GPIOF_IN; if (button->active_low) @@ -322,18 +327,22 @@ static int gpio_keys_polled_probe(struct platform_device *pdev) error = devm_gpio_request_one(&pdev->dev, button->gpio, flags, button->desc ? : DRV_NAME); if (error) { - dev_err(dev, "unable to claim gpio %u, err=%d\n", + dev_err(dev, + "unable to claim gpio %u, err=%d\n", button->gpio, error); return error; } - button->gpiod = gpio_to_desc(button->gpio); + bdata->gpiod = gpio_to_desc(button->gpio); + if (!bdata->gpiod) { + dev_err(dev, + "unable to convert gpio %u to descriptor\n", + button->gpio); + return -EINVAL; + } } - if (IS_ERR(button->gpiod)) - return PTR_ERR(button->gpiod); - - bdata->can_sleep = gpiod_cansleep(button->gpiod); + bdata->can_sleep = gpiod_cansleep(bdata->gpiod); bdata->last_state = -1; bdata->threshold = DIV_ROUND_UP(button->debounce_interval, pdata->poll_interval); @@ -344,6 +353,8 @@ static int gpio_keys_polled_probe(struct platform_device *pdev) button->code); } + fwnode_handle_put(child); + bdev->poll_dev = poll_dev; bdev->dev = dev; bdev->pdata = pdata; -- cgit v1.2.3 From ea6aabf877d82fe10627f40069e0f3d412706a8a Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 19 Oct 2016 16:36:19 -0700 Subject: Input: gpio_keys_polled - always use gpiod_get_value_cansleep It does not matter if given GPIO may sleep or not when reading state, polling is always done in a non-atomic context, so we should always be able to simply use gpiod_get_value_cansleep(). Also let's note in the logs when we fail to read gpio state. Reviewed-by: Hans de Goede Reviewed-by: Mika Westerberg Tested-by: Mika Westerberg Acked-by: Linus Walleij Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/gpio_keys_polled.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/gpio_keys_polled.c b/drivers/input/keyboard/gpio_keys_polled.c index 2cf407831f06..72b350315d43 100644 --- a/drivers/input/keyboard/gpio_keys_polled.c +++ b/drivers/input/keyboard/gpio_keys_polled.c @@ -34,7 +34,6 @@ struct gpio_keys_button_data { int last_state; int count; int threshold; - int can_sleep; }; struct gpio_keys_polled_dev { @@ -76,16 +75,17 @@ static void gpio_keys_polled_check_state(struct input_polled_dev *dev, { int state; - if (bdata->can_sleep) - state = !!gpiod_get_value_cansleep(bdata->gpiod); - else - state = !!gpiod_get_value(bdata->gpiod); - - gpio_keys_button_event(dev, button, state); + state = gpiod_get_value_cansleep(bdata->gpiod); + if (state < 0) { + dev_err(dev->input->dev.parent, + "failed to get gpio state: %d\n", state); + } else { + gpio_keys_button_event(dev, button, state); - if (state != bdata->last_state) { - bdata->count = 0; - bdata->last_state = state; + if (state != bdata->last_state) { + bdata->count = 0; + bdata->last_state = state; + } } } @@ -342,7 +342,6 @@ static int gpio_keys_polled_probe(struct platform_device *pdev) } } - bdata->can_sleep = gpiod_cansleep(bdata->gpiod); bdata->last_state = -1; bdata->threshold = DIV_ROUND_UP(button->debounce_interval, pdata->poll_interval); -- cgit v1.2.3 From f9645f22584250336a99a23bc9e444396b5bc323 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 19 Oct 2016 15:38:26 -0700 Subject: Input: gpio_keys - annotate PM methods as __maybe_unused Instead of using #ifdef, let's mark suspend and resume methods as __maybe_unused to provide better compile coverage. Tested-by: Mika Westerberg Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/gpio_keys.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index 890eb397d987..8f7c20b9cc5c 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -814,8 +814,7 @@ static int gpio_keys_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM_SLEEP -static int gpio_keys_suspend(struct device *dev) +static int __maybe_unused gpio_keys_suspend(struct device *dev) { struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev); struct input_dev *input = ddata->input; @@ -837,7 +836,7 @@ static int gpio_keys_suspend(struct device *dev) return 0; } -static int gpio_keys_resume(struct device *dev) +static int __maybe_unused gpio_keys_resume(struct device *dev) { struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev); struct input_dev *input = ddata->input; @@ -863,7 +862,6 @@ static int gpio_keys_resume(struct device *dev) gpio_keys_report_state(ddata); return 0; } -#endif static SIMPLE_DEV_PM_OPS(gpio_keys_pm_ops, gpio_keys_suspend, gpio_keys_resume); -- cgit v1.2.3 From 0860913b02c28f0a1d8b515b99ae6419ef10569f Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 20 Oct 2016 15:11:45 -0700 Subject: Input: gpio_keys - fix leaking DT node references for_each_available_child_of_node(node, pp) takes reference to 'pp' and drops it when attempting next iteration. However if we exit the loop early we need to drop the reference ourselves. Tested-by: Mika Westerberg Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/gpio_keys.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index 8f7c20b9cc5c..d75a25c187ae 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -659,6 +659,7 @@ gpio_keys_get_devtree_pdata(struct device *dev) dev_err(dev, "Failed to get gpio flags, error: %d\n", error); + of_node_put(pp); return ERR_PTR(error); } } else { @@ -669,12 +670,14 @@ gpio_keys_get_devtree_pdata(struct device *dev) if (!gpio_is_valid(button->gpio) && !button->irq) { dev_err(dev, "Found button without gpios or irqs\n"); + of_node_put(pp); return ERR_PTR(-EINVAL); } if (of_property_read_u32(pp, "linux,code", &button->code)) { dev_err(dev, "Button without keycode: 0x%x\n", button->gpio); + of_node_put(pp); return ERR_PTR(-EINVAL); } -- cgit v1.2.3 From 5feeca3c1e39c01f9ef5abc94dea94021ccf94fc Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 19 Oct 2016 15:47:21 -0700 Subject: Input: gpio_keys - add support for GPIO descriptors GPIO descriptors are the preferred way over legacy GPIO numbers nowadays. Convert the driver to use GPIO descriptors internally but still allow passing legacy GPIO numbers from platform data to support existing platforms. Based on commits 633a21d80b4a2cd6 ("input: gpio_keys_polled: Add support for GPIO descriptors") and 1ae5ddb6f8837558 ("Input: gpio_keys_polled - request GPIO pin as input."). Signed-off-by: Geert Uytterhoeven Reviewed-by: Linus Walleij Tested-by: Mika Westerberg Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/gpio_keys.c | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index d75a25c187ae..0f04cb1569a0 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -35,6 +36,7 @@ struct gpio_button_data { const struct gpio_keys_button *button; struct input_dev *input; + struct gpio_desc *gpiod; struct timer_list release_timer; unsigned int release_delay; /* in msecs, for IRQ-only buttons */ @@ -140,7 +142,7 @@ static void gpio_keys_disable_button(struct gpio_button_data *bdata) */ disable_irq(bdata->irq); - if (gpio_is_valid(bdata->button->gpio)) + if (bdata->gpiod) cancel_delayed_work_sync(&bdata->work); else del_timer_sync(&bdata->release_timer); @@ -358,19 +360,20 @@ static void gpio_keys_gpio_report_event(struct gpio_button_data *bdata) const struct gpio_keys_button *button = bdata->button; struct input_dev *input = bdata->input; unsigned int type = button->type ?: EV_KEY; - int state = gpio_get_value_cansleep(button->gpio); + int state; + state = gpiod_get_value_cansleep(bdata->gpiod); if (state < 0) { - dev_err(input->dev.parent, "failed to get gpio state\n"); + dev_err(input->dev.parent, + "failed to get gpio state: %d\n", state); return; } - state = (state ? 1 : 0) ^ button->active_low; if (type == EV_ABS) { if (state) input_event(input, type, button->code, button->value); } else { - input_event(input, type, button->code, !!state); + input_event(input, type, button->code, state); } input_sync(input); } @@ -456,7 +459,7 @@ static void gpio_keys_quiesce_key(void *data) { struct gpio_button_data *bdata = data; - if (gpio_is_valid(bdata->button->gpio)) + if (bdata->gpiod) cancel_delayed_work_sync(&bdata->work); else del_timer_sync(&bdata->release_timer); @@ -478,18 +481,30 @@ static int gpio_keys_setup_key(struct platform_device *pdev, bdata->button = button; spin_lock_init(&bdata->lock); + /* + * Legacy GPIO number, so request the GPIO here and + * convert it to descriptor. + */ if (gpio_is_valid(button->gpio)) { + unsigned flags = GPIOF_IN; + + if (button->active_low) + flags |= GPIOF_ACTIVE_LOW; - error = devm_gpio_request_one(&pdev->dev, button->gpio, - GPIOF_IN, desc); + error = devm_gpio_request_one(&pdev->dev, button->gpio, flags, + desc); if (error < 0) { dev_err(dev, "Failed to request GPIO %d, error %d\n", button->gpio, error); return error; } + bdata->gpiod = gpio_to_desc(button->gpio); + if (!bdata->gpiod) + return -EINVAL; + if (button->debounce_interval) { - error = gpio_set_debounce(button->gpio, + error = gpiod_set_debounce(bdata->gpiod, button->debounce_interval * 1000); /* use timer if gpiolib doesn't provide debounce */ if (error < 0) @@ -500,7 +515,7 @@ static int gpio_keys_setup_key(struct platform_device *pdev, if (button->irq) { bdata->irq = button->irq; } else { - irq = gpio_to_irq(button->gpio); + irq = gpiod_to_irq(bdata->gpiod); if (irq < 0) { error = irq; dev_err(dev, @@ -575,7 +590,7 @@ static void gpio_keys_report_state(struct gpio_keys_drvdata *ddata) for (i = 0; i < ddata->pdata->nbuttons; i++) { struct gpio_button_data *bdata = &ddata->data[i]; - if (gpio_is_valid(bdata->button->gpio)) + if (bdata->gpiod) gpio_keys_gpio_report_event(bdata); } input_sync(input); -- cgit v1.2.3 From 700a38b27eefc582099fdf69effacfad0ad738a4 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 19 Oct 2016 19:34:48 -0700 Subject: Input: gpio_keys - switch to using generic device properties Make use of the device property API in this driver so that both OF based systems and ACPI based systems can use this driver. Suggested-by: Geert Uytterhoeven Suggested-by: Mika Westerberg Tested-by: Mika Westerberg Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/gpio_keys.c | 141 ++++++++++++++++++------------------- 1 file changed, 69 insertions(+), 72 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index 0f04cb1569a0..5576f2ae0b71 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -28,8 +28,6 @@ #include #include #include -#include -#include #include #include @@ -468,7 +466,8 @@ static void gpio_keys_quiesce_key(void *data) static int gpio_keys_setup_key(struct platform_device *pdev, struct input_dev *input, struct gpio_button_data *bdata, - const struct gpio_keys_button *button) + const struct gpio_keys_button *button, + struct fwnode_handle *child) { const char *desc = button->desc ? button->desc : "gpio_keys"; struct device *dev = &pdev->dev; @@ -481,11 +480,28 @@ static int gpio_keys_setup_key(struct platform_device *pdev, bdata->button = button; spin_lock_init(&bdata->lock); - /* - * Legacy GPIO number, so request the GPIO here and - * convert it to descriptor. - */ - if (gpio_is_valid(button->gpio)) { + if (child) { + bdata->gpiod = devm_get_gpiod_from_child(dev, NULL, child); + if (IS_ERR(bdata->gpiod)) { + error = PTR_ERR(bdata->gpiod); + if (error == -ENOENT) { + /* + * GPIO is optional, we may be dealing with + * purely interrupt-driven setup. + */ + bdata->gpiod = NULL; + } else { + if (error != -EPROBE_DEFER) + dev_err(dev, "failed to get gpio: %d\n", + error); + return error; + } + } + } else if (gpio_is_valid(button->gpio)) { + /* + * Legacy GPIO number, so request the GPIO here and + * convert it to descriptor. + */ unsigned flags = GPIOF_IN; if (button->active_low) @@ -502,7 +518,9 @@ static int gpio_keys_setup_key(struct platform_device *pdev, bdata->gpiod = gpio_to_desc(button->gpio); if (!bdata->gpiod) return -EINVAL; + } + if (bdata->gpiod) { if (button->debounce_interval) { error = gpiod_set_debounce(bdata->gpiod, button->debounce_interval * 1000); @@ -533,9 +551,10 @@ static int gpio_keys_setup_key(struct platform_device *pdev, } else { if (!button->irq) { - dev_err(dev, "No IRQ specified\n"); + dev_err(dev, "Found button without gpio or irq\n"); return -EINVAL; } + bdata->irq = button->irq; if (button->type && button->type != EV_KEY) { @@ -627,24 +646,18 @@ static void gpio_keys_close(struct input_dev *input) * Handlers for alternative sources of platform_data */ -#ifdef CONFIG_OF /* - * Translate OpenFirmware node properties into platform_data + * Translate properties into platform_data */ static struct gpio_keys_platform_data * gpio_keys_get_devtree_pdata(struct device *dev) { - struct device_node *node, *pp; struct gpio_keys_platform_data *pdata; struct gpio_keys_button *button; - int error; + struct fwnode_handle *child; int nbuttons; - node = dev->of_node; - if (!node) - return ERR_PTR(-ENODEV); - - nbuttons = of_get_available_child_count(node); + nbuttons = device_get_child_node_count(dev); if (nbuttons == 0) return ERR_PTR(-ENODEV); @@ -659,64 +672,43 @@ gpio_keys_get_devtree_pdata(struct device *dev) pdata->buttons = button; pdata->nbuttons = nbuttons; - pdata->rep = !!of_get_property(node, "autorepeat", NULL); + pdata->rep = device_property_read_bool(dev, "autorepeat"); - of_property_read_string(node, "label", &pdata->name); + device_property_read_string(dev, "label", &pdata->name); - for_each_available_child_of_node(node, pp) { - enum of_gpio_flags flags; + device_for_each_child_node(dev, child) { + if (is_of_node(child)) + button->irq = + irq_of_parse_and_map(to_of_node(child), 0); - button->gpio = of_get_gpio_flags(pp, 0, &flags); - if (button->gpio < 0) { - error = button->gpio; - if (error != -ENOENT) { - if (error != -EPROBE_DEFER) - dev_err(dev, - "Failed to get gpio flags, error: %d\n", - error); - of_node_put(pp); - return ERR_PTR(error); - } - } else { - button->active_low = flags & OF_GPIO_ACTIVE_LOW; - } - - button->irq = irq_of_parse_and_map(pp, 0); - - if (!gpio_is_valid(button->gpio) && !button->irq) { - dev_err(dev, "Found button without gpios or irqs\n"); - of_node_put(pp); - return ERR_PTR(-EINVAL); - } - - if (of_property_read_u32(pp, "linux,code", &button->code)) { - dev_err(dev, "Button without keycode: 0x%x\n", - button->gpio); - of_node_put(pp); + if (fwnode_property_read_u32(child, "linux,code", + &button->code)) { + dev_err(dev, "Button without keycode\n"); + fwnode_handle_put(child); return ERR_PTR(-EINVAL); } - button->desc = of_get_property(pp, "label", NULL); + fwnode_property_read_string(child, "label", &button->desc); - if (of_property_read_u32(pp, "linux,input-type", &button->type)) + if (fwnode_property_read_u32(child, "linux,input-type", + &button->type)) button->type = EV_KEY; - button->wakeup = of_property_read_bool(pp, "wakeup-source") || - /* legacy name */ - of_property_read_bool(pp, "gpio-key,wakeup"); + button->wakeup = + fwnode_property_read_bool(child, "wakeup-source") || + /* legacy name */ + fwnode_property_read_bool(child, "gpio-key,wakeup"); - button->can_disable = !!of_get_property(pp, "linux,can-disable", NULL); + button->can_disable = + fwnode_property_read_bool(child, "linux,can-disable"); - if (of_property_read_u32(pp, "debounce-interval", + if (fwnode_property_read_u32(child, "debounce-interval", &button->debounce_interval)) button->debounce_interval = 5; button++; } - if (pdata->nbuttons == 0) - return ERR_PTR(-EINVAL); - return pdata; } @@ -726,20 +718,11 @@ static const struct of_device_id gpio_keys_of_match[] = { }; MODULE_DEVICE_TABLE(of, gpio_keys_of_match); -#else - -static inline struct gpio_keys_platform_data * -gpio_keys_get_devtree_pdata(struct device *dev) -{ - return ERR_PTR(-ENODEV); -} - -#endif - static int gpio_keys_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; const struct gpio_keys_platform_data *pdata = dev_get_platdata(dev); + struct fwnode_handle *child = NULL; struct gpio_keys_drvdata *ddata; struct input_dev *input; size_t size; @@ -792,14 +775,28 @@ static int gpio_keys_probe(struct platform_device *pdev) const struct gpio_keys_button *button = &pdata->buttons[i]; struct gpio_button_data *bdata = &ddata->data[i]; - error = gpio_keys_setup_key(pdev, input, bdata, button); - if (error) + if (!dev_get_platdata(dev)) { + child = device_get_next_child_node(&pdev->dev, child); + if (!child) { + dev_err(&pdev->dev, + "missing child device node for entry %d\n", + i); + return -EINVAL; + } + } + + error = gpio_keys_setup_key(pdev, input, bdata, button, child); + if (error) { + fwnode_handle_put(child); return error; + } if (button->wakeup) wakeup = 1; } + fwnode_handle_put(child); + error = sysfs_create_group(&pdev->dev.kobj, &gpio_keys_attr_group); if (error) { dev_err(dev, "Unable to export keys/switches, error: %d\n", @@ -889,7 +886,7 @@ static struct platform_driver gpio_keys_device_driver = { .driver = { .name = "gpio-keys", .pm = &gpio_keys_pm_ops, - .of_match_table = of_match_ptr(gpio_keys_of_match), + .of_match_table = gpio_keys_of_match, } }; -- cgit v1.2.3 From ad338e8b5c8cdb5142380cd6b058dfe24956ee93 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Mon, 7 Nov 2016 17:33:07 -0800 Subject: Input: synaptics-rmi4 - stop scanning PDT after two empty pages We have encountered some RMI4 firmwares where there are blank pages in between PDT pages which contain functions. This change makes them correctly enumerate all functions on the device. Tested on S7817 (has empty page 2). Signed-off-by: Nick Dyer [Tested successfully on S7817 and S7300 Synaptics touch controllers] Tested-by: Chris Healy Reviewed-by: Andrew Duggan Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/rmi_driver.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c index c83bce89028b..81e20f1f7ec3 100644 --- a/drivers/input/rmi4/rmi_driver.c +++ b/drivers/input/rmi4/rmi_driver.c @@ -423,6 +423,7 @@ static void rmi_driver_copy_pdt_to_fd(const struct pdt_entry *pdt, static int rmi_scan_pdt_page(struct rmi_device *rmi_dev, int page, + int *empty_pages, void *ctx, int (*callback)(struct rmi_device *rmi_dev, void *ctx, @@ -450,7 +451,16 @@ static int rmi_scan_pdt_page(struct rmi_device *rmi_dev, return retval; } - return (data->f01_bootloader_mode || addr == pdt_start) ? + /* + * Count number of empty PDT pages. If a gap of two pages + * or more is found, stop scanning. + */ + if (addr == pdt_start) + ++*empty_pages; + else + *empty_pages = 0; + + return (data->f01_bootloader_mode || *empty_pages >= 2) ? RMI_SCAN_DONE : RMI_SCAN_CONTINUE; } @@ -460,10 +470,12 @@ static int rmi_scan_pdt(struct rmi_device *rmi_dev, void *ctx, const struct pdt_entry *entry)) { int page; + int empty_pages = 0; int retval = RMI_SCAN_DONE; for (page = 0; page <= RMI4_MAX_PAGE; page++) { - retval = rmi_scan_pdt_page(rmi_dev, page, ctx, callback); + retval = rmi_scan_pdt_page(rmi_dev, page, &empty_pages, + ctx, callback); if (retval != RMI_SCAN_CONTINUE) break; } -- cgit v1.2.3 From 6bd0dcfacf2875f234483d57acc16b015cd313f3 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Mon, 7 Nov 2016 17:35:15 -0800 Subject: Input: synaptics-rmi4 - factor out functions from probe Signed-off-by: Nick Dyer Signed-off-by: Benjamin Tissoires Tested-by: Chris Healy Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/rmi_driver.c | 139 +++++++++++++++++++++++++--------------- 1 file changed, 86 insertions(+), 53 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c index 81e20f1f7ec3..282522e58286 100644 --- a/drivers/input/rmi4/rmi_driver.c +++ b/drivers/input/rmi4/rmi_driver.c @@ -39,6 +39,8 @@ static void rmi_free_function_list(struct rmi_device *rmi_dev) struct rmi_function *fn, *tmp; struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev); + rmi_dbg(RMI_DEBUG_CORE, &rmi_dev->dev, "Freeing function list\n"); + data->f01_container = NULL; /* Doing it in the reverse order so F01 will be removed last */ @@ -855,15 +857,90 @@ static inline int rmi_driver_of_probe(struct device *dev, } #endif +static int rmi_probe_interrupts(struct rmi_driver_data *data) +{ + struct rmi_device *rmi_dev = data->rmi_dev; + struct device *dev = &rmi_dev->dev; + int irq_count; + size_t size; + void *irq_memory; + int retval; + + /* + * We need to count the IRQs and allocate their storage before scanning + * the PDT and creating the function entries, because adding a new + * function can trigger events that result in the IRQ related storage + * being accessed. + */ + rmi_dbg(RMI_DEBUG_CORE, dev, "%s: Counting IRQs.\n", __func__); + irq_count = 0; + retval = rmi_scan_pdt(rmi_dev, &irq_count, rmi_count_irqs); + if (retval < 0) { + dev_err(dev, "IRQ counting failed with code %d.\n", retval); + return retval; + } + data->irq_count = irq_count; + data->num_of_irq_regs = (data->irq_count + 7) / 8; + + size = BITS_TO_LONGS(data->irq_count) * sizeof(unsigned long); + irq_memory = devm_kzalloc(dev, size * 4, GFP_KERNEL); + if (!irq_memory) { + dev_err(dev, "Failed to allocate memory for irq masks.\n"); + return retval; + } + + data->irq_status = irq_memory + size * 0; + data->fn_irq_bits = irq_memory + size * 1; + data->current_irq_mask = irq_memory + size * 2; + data->new_irq_mask = irq_memory + size * 3; + + return retval; +} + +static int rmi_init_functions(struct rmi_driver_data *data) +{ + struct rmi_device *rmi_dev = data->rmi_dev; + struct device *dev = &rmi_dev->dev; + int irq_count; + int retval; + + irq_count = 0; + rmi_dbg(RMI_DEBUG_CORE, dev, "%s: Creating functions.\n", __func__); + retval = rmi_scan_pdt(rmi_dev, &irq_count, rmi_create_function); + if (retval < 0) { + dev_err(dev, "Function creation failed with code %d.\n", + retval); + goto err_destroy_functions; + } + + if (!data->f01_container) { + dev_err(dev, "Missing F01 container!\n"); + retval = -EINVAL; + goto err_destroy_functions; + } + + retval = rmi_read_block(rmi_dev, + data->f01_container->fd.control_base_addr + 1, + data->current_irq_mask, data->num_of_irq_regs); + if (retval < 0) { + dev_err(dev, "%s: Failed to read current IRQ mask.\n", + __func__); + goto err_destroy_functions; + } + + return 0; + +err_destroy_functions: + rmi_free_function_list(rmi_dev); + return retval; +} + static int rmi_driver_probe(struct device *dev) { struct rmi_driver *rmi_driver; struct rmi_driver_data *data; struct rmi_device_platform_data *pdata; struct rmi_device *rmi_dev; - size_t size; - void *irq_memory; - int irq_count; int retval; rmi_dbg(RMI_DEBUG_CORE, dev, "%s: Starting probe.\n", @@ -929,35 +1006,11 @@ static int rmi_driver_probe(struct device *dev) PDT_PROPERTIES_LOCATION, retval); } - /* - * We need to count the IRQs and allocate their storage before scanning - * the PDT and creating the function entries, because adding a new - * function can trigger events that result in the IRQ related storage - * being accessed. - */ - rmi_dbg(RMI_DEBUG_CORE, dev, "Counting IRQs.\n"); - irq_count = 0; - retval = rmi_scan_pdt(rmi_dev, &irq_count, rmi_count_irqs); - if (retval < 0) { - dev_err(dev, "IRQ counting failed with code %d.\n", retval); - goto err; - } - data->irq_count = irq_count; - data->num_of_irq_regs = (data->irq_count + 7) / 8; - mutex_init(&data->irq_mutex); - size = BITS_TO_LONGS(data->irq_count) * sizeof(unsigned long); - irq_memory = devm_kzalloc(dev, size * 4, GFP_KERNEL); - if (!irq_memory) { - dev_err(dev, "Failed to allocate memory for irq masks.\n"); + retval = rmi_probe_interrupts(data); + if (retval) goto err; - } - - data->irq_status = irq_memory + size * 0; - data->fn_irq_bits = irq_memory + size * 1; - data->current_irq_mask = irq_memory + size * 2; - data->new_irq_mask = irq_memory + size * 3; if (rmi_dev->xport->input) { /* @@ -974,36 +1027,16 @@ static int rmi_driver_probe(struct device *dev) dev_err(dev, "%s: Failed to allocate input device.\n", __func__); retval = -ENOMEM; - goto err_destroy_functions; + goto err; } rmi_driver_set_input_params(rmi_dev, data->input); data->input->phys = devm_kasprintf(dev, GFP_KERNEL, "%s/input0", dev_name(dev)); } - irq_count = 0; - rmi_dbg(RMI_DEBUG_CORE, dev, "Creating functions."); - retval = rmi_scan_pdt(rmi_dev, &irq_count, rmi_create_function); - if (retval < 0) { - dev_err(dev, "Function creation failed with code %d.\n", - retval); - goto err_destroy_functions; - } - - if (!data->f01_container) { - dev_err(dev, "Missing F01 container!\n"); - retval = -EINVAL; - goto err_destroy_functions; - } - - retval = rmi_read_block(rmi_dev, - data->f01_container->fd.control_base_addr + 1, - data->current_irq_mask, data->num_of_irq_regs); - if (retval < 0) { - dev_err(dev, "%s: Failed to read current IRQ mask.\n", - __func__); - goto err_destroy_functions; - } + retval = rmi_init_functions(data); + if (retval) + goto err; if (data->input) { rmi_driver_set_input_name(rmi_dev, data->input); -- cgit v1.2.3 From 8029a283c4ac100dc4492993633d4c87a7da55d6 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Mon, 7 Nov 2016 17:36:57 -0800 Subject: Input: synaptics-rmi4 - add a couple of debug lines Signed-off-by: Nick Dyer Tested-by: Chris Healy Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/rmi_bus.c | 3 +++ drivers/input/rmi4/rmi_driver.c | 1 + 2 files changed, 4 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/rmi4/rmi_bus.c b/drivers/input/rmi4/rmi_bus.c index 09c769c6e91f..84b321262d74 100644 --- a/drivers/input/rmi4/rmi_bus.c +++ b/drivers/input/rmi4/rmi_bus.c @@ -231,6 +231,9 @@ err_put_device: void rmi_unregister_function(struct rmi_function *fn) { + rmi_dbg(RMI_DEBUG_CORE, &fn->dev, "Unregistering F%02X.\n", + fn->fd.function_number); + device_del(&fn->dev); of_node_put(fn->dev.of_node); put_device(&fn->dev); diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c index 282522e58286..06feede3ce17 100644 --- a/drivers/input/rmi4/rmi_driver.c +++ b/drivers/input/rmi4/rmi_driver.c @@ -735,6 +735,7 @@ static int rmi_initial_reset(struct rmi_device *rmi_dev, return RMI_SCAN_DONE; } + rmi_dbg(RMI_DEBUG_CORE, &rmi_dev->dev, "Sending reset\n"); error = rmi_write_block(rmi_dev, cmd_addr, &cmd_buf, 1); if (error) { dev_err(&rmi_dev->dev, -- cgit v1.2.3 From c3fc60893d2d3ef55844c26770d080eceff68fd5 Mon Sep 17 00:00:00 2001 From: Sangwon Jee Date: Tue, 8 Nov 2016 16:07:05 -0800 Subject: Input: melfas_mip4 - add product ID reporting Add reporting product ID through input_id. Signed-off-by: Sangwon Jee Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/melfas_mip4.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/melfas_mip4.c b/drivers/input/touchscreen/melfas_mip4.c index 0f2230a9b1a1..703d7f983d0a 100644 --- a/drivers/input/touchscreen/melfas_mip4.c +++ b/drivers/input/touchscreen/melfas_mip4.c @@ -1477,6 +1477,7 @@ static int mip4_probe(struct i2c_client *client, const struct i2c_device_id *id) input->id.bustype = BUS_I2C; input->id.vendor = 0x13c5; + input->id.product = ts->product_id; input->open = mip4_input_open; input->close = mip4_input_close; @@ -1614,6 +1615,6 @@ static struct i2c_driver mip4_driver = { module_i2c_driver(mip4_driver); MODULE_DESCRIPTION("MELFAS MIP4 Touchscreen"); -MODULE_VERSION("2016.10.24"); +MODULE_VERSION("2016.10.31"); MODULE_AUTHOR("Sangwon Jee "); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 3aeed5b573f97b4525841cc07c1e948227af389f Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Tue, 8 Nov 2016 16:34:57 -0800 Subject: Input: synaptics-rmi4 - move IRQ handling to rmi_driver The attn IRQ is related to the chip, rather than the transport, so move all handling of interrupts to the core driver. This also makes sure that there are no races between interrupts and availability of the resources used by the core driver. Signed-off-by: Bjorn Andersson Signed-off-by: Benjamin Tissoires Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/rmi_driver.c | 73 +++++++++++++++++++++++++++++++++++++--- drivers/input/rmi4/rmi_i2c.c | 74 +++-------------------------------------- drivers/input/rmi4/rmi_spi.c | 72 +++------------------------------------ 3 files changed, 79 insertions(+), 140 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c index 06feede3ce17..4f8d19794b01 100644 --- a/drivers/input/rmi4/rmi_driver.c +++ b/drivers/input/rmi4/rmi_driver.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -136,7 +137,7 @@ static void process_one_interrupt(struct rmi_driver_data *data, } } -int rmi_process_interrupt_requests(struct rmi_device *rmi_dev) +static int rmi_process_interrupt_requests(struct rmi_device *rmi_dev) { struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev); struct device *dev = &rmi_dev->dev; @@ -181,7 +182,42 @@ int rmi_process_interrupt_requests(struct rmi_device *rmi_dev) return 0; } -EXPORT_SYMBOL_GPL(rmi_process_interrupt_requests); + +static irqreturn_t rmi_irq_fn(int irq, void *dev_id) +{ + struct rmi_device *rmi_dev = dev_id; + int ret; + + ret = rmi_process_interrupt_requests(rmi_dev); + if (ret) + rmi_dbg(RMI_DEBUG_CORE, &rmi_dev->dev, + "Failed to process interrupt request: %d\n", ret); + + return IRQ_HANDLED; +} + +static int rmi_irq_init(struct rmi_device *rmi_dev) +{ + struct rmi_device_platform_data *pdata = rmi_get_platform_data(rmi_dev); + int irq_flags = irq_get_trigger_type(pdata->irq); + int ret; + + if (!irq_flags) + irq_flags = IRQF_TRIGGER_LOW; + + ret = devm_request_threaded_irq(&rmi_dev->dev, pdata->irq, NULL, + rmi_irq_fn, irq_flags | IRQF_ONESHOT, + dev_name(rmi_dev->xport->dev), + rmi_dev); + if (ret < 0) { + dev_err(&rmi_dev->dev, "Failed to register interrupt %d\n", + pdata->irq); + + return ret; + } + + return 0; +} static int suspend_one_function(struct rmi_function *fn) { @@ -802,8 +838,10 @@ err_put_fn: return error; } -int rmi_driver_suspend(struct rmi_device *rmi_dev) +int rmi_driver_suspend(struct rmi_device *rmi_dev, bool enable_wake) { + struct rmi_device_platform_data *pdata = rmi_get_platform_data(rmi_dev); + int irq = pdata->irq; int retval = 0; retval = rmi_suspend_functions(rmi_dev); @@ -811,14 +849,33 @@ int rmi_driver_suspend(struct rmi_device *rmi_dev) dev_warn(&rmi_dev->dev, "Failed to suspend functions: %d\n", retval); + disable_irq(irq); + if (enable_wake && device_may_wakeup(rmi_dev->xport->dev)) { + retval = enable_irq_wake(irq); + if (!retval) + dev_warn(&rmi_dev->dev, + "Failed to enable irq for wake: %d\n", + retval); + } return retval; } EXPORT_SYMBOL_GPL(rmi_driver_suspend); -int rmi_driver_resume(struct rmi_device *rmi_dev) +int rmi_driver_resume(struct rmi_device *rmi_dev, bool clear_wake) { + struct rmi_device_platform_data *pdata = rmi_get_platform_data(rmi_dev); + int irq = pdata->irq; int retval; + enable_irq(irq); + if (clear_wake && device_may_wakeup(rmi_dev->xport->dev)) { + retval = disable_irq_wake(irq); + if (!retval) + dev_warn(&rmi_dev->dev, + "Failed to disable irq for wake: %d\n", + retval); + } + retval = rmi_resume_functions(rmi_dev); if (retval) dev_warn(&rmi_dev->dev, "Failed to suspend functions: %d\n", @@ -831,6 +888,10 @@ EXPORT_SYMBOL_GPL(rmi_driver_resume); static int rmi_driver_remove(struct device *dev) { struct rmi_device *rmi_dev = to_rmi_device(dev); + struct rmi_device_platform_data *pdata = rmi_get_platform_data(rmi_dev); + int irq = pdata->irq; + + disable_irq(irq); rmi_free_function_list(rmi_dev); @@ -1050,6 +1111,10 @@ static int rmi_driver_probe(struct device *dev) } } + retval = rmi_irq_init(rmi_dev); + if (retval < 0) + goto err_destroy_functions; + if (data->f01_container->dev.driver) /* Driver already bound, so enable ATTN now. */ return enable_sensor(rmi_dev); diff --git a/drivers/input/rmi4/rmi_i2c.c b/drivers/input/rmi4/rmi_i2c.c index 6f2e0e4f0296..64a548822da4 100644 --- a/drivers/input/rmi4/rmi_i2c.c +++ b/drivers/input/rmi4/rmi_i2c.c @@ -9,7 +9,6 @@ #include #include -#include #include #include #include @@ -35,8 +34,6 @@ struct rmi_i2c_xport { struct mutex page_mutex; int page; - int irq; - u8 *tx_buf; size_t tx_buf_size; @@ -177,42 +174,6 @@ static const struct rmi_transport_ops rmi_i2c_ops = { .read_block = rmi_i2c_read_block, }; -static irqreturn_t rmi_i2c_irq(int irq, void *dev_id) -{ - struct rmi_i2c_xport *rmi_i2c = dev_id; - struct rmi_device *rmi_dev = rmi_i2c->xport.rmi_dev; - int ret; - - ret = rmi_process_interrupt_requests(rmi_dev); - if (ret) - rmi_dbg(RMI_DEBUG_XPORT, &rmi_dev->dev, - "Failed to process interrupt request: %d\n", ret); - - return IRQ_HANDLED; -} - -static int rmi_i2c_init_irq(struct i2c_client *client) -{ - struct rmi_i2c_xport *rmi_i2c = i2c_get_clientdata(client); - int irq_flags = irqd_get_trigger_type(irq_get_irq_data(rmi_i2c->irq)); - int ret; - - if (!irq_flags) - irq_flags = IRQF_TRIGGER_LOW; - - ret = devm_request_threaded_irq(&client->dev, rmi_i2c->irq, NULL, - rmi_i2c_irq, irq_flags | IRQF_ONESHOT, client->name, - rmi_i2c); - if (ret < 0) { - dev_warn(&client->dev, "Failed to register interrupt %d\n", - rmi_i2c->irq); - - return ret; - } - - return 0; -} - #ifdef CONFIG_OF static const struct of_device_id rmi_i2c_of_match[] = { { .compatible = "syna,rmi4-i2c" }, @@ -240,8 +201,7 @@ static int rmi_i2c_probe(struct i2c_client *client, if (!client->dev.of_node && client_pdata) *pdata = *client_pdata; - if (client->irq > 0) - rmi_i2c->irq = client->irq; + pdata->irq = client->irq; rmi_dbg(RMI_DEBUG_XPORT, &client->dev, "Probing %s.\n", dev_name(&client->dev)); @@ -295,10 +255,6 @@ static int rmi_i2c_probe(struct i2c_client *client, return retval; } - retval = rmi_i2c_init_irq(client); - if (retval < 0) - return retval; - dev_info(&client->dev, "registered rmi i2c driver at %#04x.\n", client->addr); return 0; @@ -322,18 +278,10 @@ static int rmi_i2c_suspend(struct device *dev) struct rmi_i2c_xport *rmi_i2c = i2c_get_clientdata(client); int ret; - ret = rmi_driver_suspend(rmi_i2c->xport.rmi_dev); + ret = rmi_driver_suspend(rmi_i2c->xport.rmi_dev, true); if (ret) dev_warn(dev, "Failed to resume device: %d\n", ret); - disable_irq(rmi_i2c->irq); - if (device_may_wakeup(&client->dev)) { - ret = enable_irq_wake(rmi_i2c->irq); - if (!ret) - dev_warn(dev, "Failed to enable irq for wake: %d\n", - ret); - } - regulator_bulk_disable(ARRAY_SIZE(rmi_i2c->supplies), rmi_i2c->supplies); @@ -353,15 +301,7 @@ static int rmi_i2c_resume(struct device *dev) msleep(rmi_i2c->startup_delay); - enable_irq(rmi_i2c->irq); - if (device_may_wakeup(&client->dev)) { - ret = disable_irq_wake(rmi_i2c->irq); - if (!ret) - dev_warn(dev, "Failed to disable irq for wake: %d\n", - ret); - } - - ret = rmi_driver_resume(rmi_i2c->xport.rmi_dev); + ret = rmi_driver_resume(rmi_i2c->xport.rmi_dev, true); if (ret) dev_warn(dev, "Failed to resume device: %d\n", ret); @@ -376,12 +316,10 @@ static int rmi_i2c_runtime_suspend(struct device *dev) struct rmi_i2c_xport *rmi_i2c = i2c_get_clientdata(client); int ret; - ret = rmi_driver_suspend(rmi_i2c->xport.rmi_dev); + ret = rmi_driver_suspend(rmi_i2c->xport.rmi_dev, false); if (ret) dev_warn(dev, "Failed to resume device: %d\n", ret); - disable_irq(rmi_i2c->irq); - regulator_bulk_disable(ARRAY_SIZE(rmi_i2c->supplies), rmi_i2c->supplies); @@ -401,9 +339,7 @@ static int rmi_i2c_runtime_resume(struct device *dev) msleep(rmi_i2c->startup_delay); - enable_irq(rmi_i2c->irq); - - ret = rmi_driver_resume(rmi_i2c->xport.rmi_dev); + ret = rmi_driver_resume(rmi_i2c->xport.rmi_dev, false); if (ret) dev_warn(dev, "Failed to resume device: %d\n", ret); diff --git a/drivers/input/rmi4/rmi_spi.c b/drivers/input/rmi4/rmi_spi.c index 55bd1b34970c..f3e9e488635c 100644 --- a/drivers/input/rmi4/rmi_spi.c +++ b/drivers/input/rmi4/rmi_spi.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include "rmi_driver.h" @@ -44,8 +43,6 @@ struct rmi_spi_xport { struct mutex page_mutex; int page; - int irq; - u8 *rx_buf; u8 *tx_buf; int xfer_buf_size; @@ -326,41 +323,6 @@ static const struct rmi_transport_ops rmi_spi_ops = { .read_block = rmi_spi_read_block, }; -static irqreturn_t rmi_spi_irq(int irq, void *dev_id) -{ - struct rmi_spi_xport *rmi_spi = dev_id; - struct rmi_device *rmi_dev = rmi_spi->xport.rmi_dev; - int ret; - - ret = rmi_process_interrupt_requests(rmi_dev); - if (ret) - rmi_dbg(RMI_DEBUG_XPORT, &rmi_dev->dev, - "Failed to process interrupt request: %d\n", ret); - - return IRQ_HANDLED; -} - -static int rmi_spi_init_irq(struct spi_device *spi) -{ - struct rmi_spi_xport *rmi_spi = spi_get_drvdata(spi); - int irq_flags = irqd_get_trigger_type(irq_get_irq_data(rmi_spi->irq)); - int ret; - - if (!irq_flags) - irq_flags = IRQF_TRIGGER_LOW; - - ret = devm_request_threaded_irq(&spi->dev, rmi_spi->irq, NULL, - rmi_spi_irq, irq_flags | IRQF_ONESHOT, - dev_name(&spi->dev), rmi_spi); - if (ret < 0) { - dev_warn(&spi->dev, "Failed to register interrupt %d\n", - rmi_spi->irq); - return ret; - } - - return 0; -} - #ifdef CONFIG_OF static int rmi_spi_of_probe(struct spi_device *spi, struct rmi_device_platform_data *pdata) @@ -433,8 +395,7 @@ static int rmi_spi_probe(struct spi_device *spi) return retval; } - if (spi->irq > 0) - rmi_spi->irq = spi->irq; + pdata->irq = spi->irq; rmi_spi->spi = spi; mutex_init(&rmi_spi->page_mutex); @@ -465,10 +426,6 @@ static int rmi_spi_probe(struct spi_device *spi) return retval; } - retval = rmi_spi_init_irq(spi); - if (retval < 0) - return retval; - dev_info(&spi->dev, "registered RMI SPI driver\n"); return 0; } @@ -489,17 +446,10 @@ static int rmi_spi_suspend(struct device *dev) struct rmi_spi_xport *rmi_spi = spi_get_drvdata(spi); int ret; - ret = rmi_driver_suspend(rmi_spi->xport.rmi_dev); + ret = rmi_driver_suspend(rmi_spi->xport.rmi_dev, true); if (ret) dev_warn(dev, "Failed to resume device: %d\n", ret); - disable_irq(rmi_spi->irq); - if (device_may_wakeup(&spi->dev)) { - ret = enable_irq_wake(rmi_spi->irq); - if (!ret) - dev_warn(dev, "Failed to enable irq for wake: %d\n", - ret); - } return ret; } @@ -509,15 +459,7 @@ static int rmi_spi_resume(struct device *dev) struct rmi_spi_xport *rmi_spi = spi_get_drvdata(spi); int ret; - enable_irq(rmi_spi->irq); - if (device_may_wakeup(&spi->dev)) { - ret = disable_irq_wake(rmi_spi->irq); - if (!ret) - dev_warn(dev, "Failed to disable irq for wake: %d\n", - ret); - } - - ret = rmi_driver_resume(rmi_spi->xport.rmi_dev); + ret = rmi_driver_resume(rmi_spi->xport.rmi_dev, true); if (ret) dev_warn(dev, "Failed to resume device: %d\n", ret); @@ -532,12 +474,10 @@ static int rmi_spi_runtime_suspend(struct device *dev) struct rmi_spi_xport *rmi_spi = spi_get_drvdata(spi); int ret; - ret = rmi_driver_suspend(rmi_spi->xport.rmi_dev); + ret = rmi_driver_suspend(rmi_spi->xport.rmi_dev, false); if (ret) dev_warn(dev, "Failed to resume device: %d\n", ret); - disable_irq(rmi_spi->irq); - return 0; } @@ -547,9 +487,7 @@ static int rmi_spi_runtime_resume(struct device *dev) struct rmi_spi_xport *rmi_spi = spi_get_drvdata(spi); int ret; - enable_irq(rmi_spi->irq); - - ret = rmi_driver_resume(rmi_spi->xport.rmi_dev); + ret = rmi_driver_resume(rmi_spi->xport.rmi_dev, false); if (ret) dev_warn(dev, "Failed to resume device: %d\n", ret); -- cgit v1.2.3 From 6d0dbeae71f074c67b081eae45cd58fa39dfda2e Mon Sep 17 00:00:00 2001 From: Andrew Duggan Date: Tue, 8 Nov 2016 16:46:20 -0800 Subject: Input: synaptics-rmi4 - handle incomplete input data Commit 5b65c2a02966 ("HID: rmi: check sanity of the incoming report") added support for handling incomplete HID reports do to the input data being corrupted in transit. This patch reimplements this functionality in the function drivers so they can handle getting less valid data then they expect. Signed-off-by: Andrew Duggan Signed-off-by: Benjamin Tissoires Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/rmi_f11.c | 54 ++++++++++++++++++++++++++++++++------------ drivers/input/rmi4/rmi_f12.c | 23 ++++++++++++++----- drivers/input/rmi4/rmi_f30.c | 4 ++++ 3 files changed, 61 insertions(+), 20 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/rmi4/rmi_f11.c b/drivers/input/rmi4/rmi_f11.c index 20c7134b3d3b..3218742d2d8c 100644 --- a/drivers/input/rmi4/rmi_f11.c +++ b/drivers/input/rmi4/rmi_f11.c @@ -572,31 +572,48 @@ static inline u8 rmi_f11_parse_finger_state(const u8 *f_state, u8 n_finger) static void rmi_f11_finger_handler(struct f11_data *f11, struct rmi_2d_sensor *sensor, - unsigned long *irq_bits, int num_irq_regs) + unsigned long *irq_bits, int num_irq_regs, + int size) { const u8 *f_state = f11->data.f_state; u8 finger_state; u8 i; + int abs_fingers; + int rel_fingers; + int abs_size = sensor->nbr_fingers * RMI_F11_ABS_BYTES; int abs_bits = bitmap_and(f11->result_bits, irq_bits, f11->abs_mask, num_irq_regs * 8); int rel_bits = bitmap_and(f11->result_bits, irq_bits, f11->rel_mask, num_irq_regs * 8); - for (i = 0; i < sensor->nbr_fingers; i++) { - /* Possible of having 4 fingers per f_statet register */ - finger_state = rmi_f11_parse_finger_state(f_state, i); - if (finger_state == F11_RESERVED) { - pr_err("Invalid finger state[%d]: 0x%02x", i, - finger_state); - continue; - } + if (abs_bits) { + if (abs_size > size) + abs_fingers = size / RMI_F11_ABS_BYTES; + else + abs_fingers = sensor->nbr_fingers; + + for (i = 0; i < abs_fingers; i++) { + /* Possible of having 4 fingers per f_state register */ + finger_state = rmi_f11_parse_finger_state(f_state, i); + if (finger_state == F11_RESERVED) { + pr_err("Invalid finger state[%d]: 0x%02x", i, + finger_state); + continue; + } - if (abs_bits) rmi_f11_abs_pos_process(f11, sensor, &sensor->objs[i], finger_state, i); + } + } + + if (rel_bits) { + if ((abs_size + sensor->nbr_fingers * RMI_F11_REL_BYTES) > size) + rel_fingers = (size - abs_size) / RMI_F11_REL_BYTES; + else + rel_fingers = sensor->nbr_fingers; - if (rel_bits) + for (i = 0; i < rel_fingers; i++) rmi_f11_rel_pos_report(f11, i); } @@ -612,7 +629,7 @@ static void rmi_f11_finger_handler(struct f11_data *f11, sensor->nbr_fingers, sensor->dmax); - for (i = 0; i < sensor->nbr_fingers; i++) { + for (i = 0; i < abs_fingers; i++) { finger_state = rmi_f11_parse_finger_state(f_state, i); if (finger_state == F11_RESERVED) /* no need to send twice the error */ @@ -1242,10 +1259,19 @@ static int rmi_f11_attention(struct rmi_function *fn, unsigned long *irq_bits) struct f11_data *f11 = dev_get_drvdata(&fn->dev); u16 data_base_addr = fn->fd.data_base_addr; int error; + int valid_bytes = f11->sensor.pkt_size; if (rmi_dev->xport->attn_data) { + /* + * The valid data in the attention report is less then + * expected. Only process the complete fingers. + */ + if (f11->sensor.attn_size > rmi_dev->xport->attn_size) + valid_bytes = rmi_dev->xport->attn_size; + else + valid_bytes = f11->sensor.attn_size; memcpy(f11->sensor.data_pkt, rmi_dev->xport->attn_data, - f11->sensor.attn_size); + valid_bytes); rmi_dev->xport->attn_data += f11->sensor.attn_size; rmi_dev->xport->attn_size -= f11->sensor.attn_size; } else { @@ -1257,7 +1283,7 @@ static int rmi_f11_attention(struct rmi_function *fn, unsigned long *irq_bits) } rmi_f11_finger_handler(f11, &f11->sensor, irq_bits, - drvdata->num_of_irq_regs); + drvdata->num_of_irq_regs, valid_bytes); return 0; } diff --git a/drivers/input/rmi4/rmi_f12.c b/drivers/input/rmi4/rmi_f12.c index 332c02f0b107..767ac7920c75 100644 --- a/drivers/input/rmi4/rmi_f12.c +++ b/drivers/input/rmi4/rmi_f12.c @@ -26,6 +26,8 @@ enum rmi_f12_object_type { RMI_F12_OBJECT_SMALL_OBJECT = 0x0D, }; +#define F12_DATA1_BYTES_PER_OBJ 8 + struct f12_data { struct rmi_2d_sensor sensor; struct rmi_2d_sensor_platform_data sensor_pdata; @@ -146,12 +148,16 @@ static int rmi_f12_read_sensor_tuning(struct f12_data *f12) return 0; } -static void rmi_f12_process_objects(struct f12_data *f12, u8 *data1) +static void rmi_f12_process_objects(struct f12_data *f12, u8 *data1, int size) { int i; struct rmi_2d_sensor *sensor = &f12->sensor; + int objects = f12->data1->num_subpackets; + + if ((f12->data1->num_subpackets * F12_DATA1_BYTES_PER_OBJ) > size) + objects = size / F12_DATA1_BYTES_PER_OBJ; - for (i = 0; i < f12->data1->num_subpackets; i++) { + for (i = 0; i < objects; i++) { struct rmi_2d_sensor_abs_object *obj = &sensor->objs[i]; obj->type = RMI_2D_OBJECT_NONE; @@ -182,7 +188,7 @@ static void rmi_f12_process_objects(struct f12_data *f12, u8 *data1) rmi_2d_sensor_abs_process(sensor, obj, i); - data1 += 8; + data1 += F12_DATA1_BYTES_PER_OBJ; } if (sensor->kernel_tracking) @@ -192,7 +198,7 @@ static void rmi_f12_process_objects(struct f12_data *f12, u8 *data1) sensor->nbr_fingers, sensor->dmax); - for (i = 0; i < sensor->nbr_fingers; i++) + for (i = 0; i < objects; i++) rmi_2d_sensor_abs_report(sensor, &sensor->objs[i], i); } @@ -203,10 +209,15 @@ static int rmi_f12_attention(struct rmi_function *fn, struct rmi_device *rmi_dev = fn->rmi_dev; struct f12_data *f12 = dev_get_drvdata(&fn->dev); struct rmi_2d_sensor *sensor = &f12->sensor; + int valid_bytes = sensor->pkt_size; if (rmi_dev->xport->attn_data) { + if (sensor->attn_size > rmi_dev->xport->attn_size) + valid_bytes = rmi_dev->xport->attn_size; + else + valid_bytes = sensor->attn_size; memcpy(sensor->data_pkt, rmi_dev->xport->attn_data, - sensor->attn_size); + valid_bytes); rmi_dev->xport->attn_data += sensor->attn_size; rmi_dev->xport->attn_size -= sensor->attn_size; } else { @@ -221,7 +232,7 @@ static int rmi_f12_attention(struct rmi_function *fn, if (f12->data1) rmi_f12_process_objects(f12, - &sensor->data_pkt[f12->data1_offset]); + &sensor->data_pkt[f12->data1_offset], valid_bytes); input_mt_sync_frame(sensor->input); diff --git a/drivers/input/rmi4/rmi_f30.c b/drivers/input/rmi4/rmi_f30.c index 760aff1bc420..485907ff10f4 100644 --- a/drivers/input/rmi4/rmi_f30.c +++ b/drivers/input/rmi4/rmi_f30.c @@ -110,6 +110,10 @@ static int rmi_f30_attention(struct rmi_function *fn, unsigned long *irq_bits) /* Read the gpi led data. */ if (rmi_dev->xport->attn_data) { + if (rmi_dev->xport->attn_size < f30->register_count) { + dev_warn(&fn->dev, "F30 interrupted, but data is missing\n"); + return 0; + } memcpy(f30->data_regs, rmi_dev->xport->attn_data, f30->register_count); rmi_dev->xport->attn_data += f30->register_count; -- cgit v1.2.3 From 2775e523246e11c5ce90b69226c5e67aa43e64a5 Mon Sep 17 00:00:00 2001 From: Andrew Duggan Date: Tue, 8 Nov 2016 16:48:48 -0800 Subject: Input: synaptics-rmi4 - add parameters for dribble packets and palm detect gesture The rmi_f11 driver currently disables dribble packets and the palm detect gesture for all devices. This patch creates a parameter in the 2d sensor platform data for controlling this functionality on a per device basis. For more information on dribble packets: Commit 05ba999fcabb ("HID: rmi: disable dribble packets on Synaptics touchpads") For more information on the palm detect gesture: Commit f097deef59a6 ("HID: rmi: disable palm detect gesture when present") Signed-off-by: Andrew Duggan Reviewed-by: Benjamin Tissoires Signed-off-by: Benjamin Tissoires Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/rmi_2d_sensor.h | 2 ++ drivers/input/rmi4/rmi_f01.c | 6 +++--- drivers/input/rmi4/rmi_f11.c | 32 ++++++++++++++++++++++++++++---- 3 files changed, 33 insertions(+), 7 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/rmi4/rmi_2d_sensor.h b/drivers/input/rmi4/rmi_2d_sensor.h index 77fcdfef003c..c871bef4dac0 100644 --- a/drivers/input/rmi4/rmi_2d_sensor.h +++ b/drivers/input/rmi4/rmi_2d_sensor.h @@ -67,6 +67,8 @@ struct rmi_2d_sensor { u8 report_rel; u8 x_mm; u8 y_mm; + enum rmi_reg_state dribble; + enum rmi_reg_state palm_detect; }; int rmi_2d_sensor_of_probe(struct device *dev, diff --git a/drivers/input/rmi4/rmi_f01.c b/drivers/input/rmi4/rmi_f01.c index fac81fc9bcf6..2cfa9f64acfb 100644 --- a/drivers/input/rmi4/rmi_f01.c +++ b/drivers/input/rmi4/rmi_f01.c @@ -327,12 +327,12 @@ static int rmi_f01_probe(struct rmi_function *fn) } switch (pdata->power_management.nosleep) { - case RMI_F01_NOSLEEP_DEFAULT: + case RMI_REG_STATE_DEFAULT: break; - case RMI_F01_NOSLEEP_OFF: + case RMI_REG_STATE_OFF: f01->device_control.ctrl0 &= ~RMI_F01_CTRL0_NOSLEEP_BIT; break; - case RMI_F01_NOSLEEP_ON: + case RMI_REG_STATE_ON: f01->device_control.ctrl0 |= RMI_F01_CTRL0_NOSLEEP_BIT; break; } diff --git a/drivers/input/rmi4/rmi_f11.c b/drivers/input/rmi4/rmi_f11.c index 3218742d2d8c..c252d405df4c 100644 --- a/drivers/input/rmi4/rmi_f11.c +++ b/drivers/input/rmi4/rmi_f11.c @@ -1142,6 +1142,8 @@ static int rmi_f11_initialize(struct rmi_function *fn) sensor->topbuttonpad = f11->sensor_pdata.topbuttonpad; sensor->kernel_tracking = f11->sensor_pdata.kernel_tracking; sensor->dmax = f11->sensor_pdata.dmax; + sensor->dribble = f11->sensor_pdata.dribble; + sensor->palm_detect = f11->sensor_pdata.palm_detect; if (f11->sens_query.has_physical_props) { sensor->x_mm = f11->sens_query.x_sensor_size_mm; @@ -1209,11 +1211,33 @@ static int rmi_f11_initialize(struct rmi_function *fn) ctrl->ctrl0_11[RMI_F11_DELTA_Y_THRESHOLD] = sensor->axis_align.delta_y_threshold; - if (f11->sens_query.has_dribble) - ctrl->ctrl0_11[0] = ctrl->ctrl0_11[0] & ~BIT(6); + if (f11->sens_query.has_dribble) { + switch (sensor->dribble) { + case RMI_REG_STATE_OFF: + ctrl->ctrl0_11[0] &= ~BIT(6); + break; + case RMI_REG_STATE_ON: + ctrl->ctrl0_11[0] |= BIT(6); + break; + case RMI_REG_STATE_DEFAULT: + default: + break; + } + } - if (f11->sens_query.has_palm_det) - ctrl->ctrl0_11[11] = ctrl->ctrl0_11[11] & ~BIT(0); + if (f11->sens_query.has_palm_det) { + switch (sensor->palm_detect) { + case RMI_REG_STATE_OFF: + ctrl->ctrl0_11[11] &= ~BIT(0); + break; + case RMI_REG_STATE_ON: + ctrl->ctrl0_11[11] |= BIT(0); + break; + case RMI_REG_STATE_DEFAULT: + default: + break; + } + } rc = f11_write_control_regs(fn, &f11->sens_query, &f11->dev_controls, fn->fd.query_base_addr); -- cgit v1.2.3 From 24f63b1cb05034de74289a684e0938388757085d Mon Sep 17 00:00:00 2001 From: Andrew Duggan Date: Tue, 8 Nov 2016 16:47:58 -0800 Subject: Input: synaptics-rmi4 - add support for controlling dribble packets in F12 Implements reading and setting the dribble bit in F12's control registers. Signed-off-by: Andrew Duggan Acked-by: Benjamin Tissoires Signed-off-by: Benjamin Tissoires Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/rmi_f12.c | 70 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/rmi4/rmi_f12.c b/drivers/input/rmi4/rmi_f12.c index 767ac7920c75..d7255f18306d 100644 --- a/drivers/input/rmi4/rmi_f12.c +++ b/drivers/input/rmi4/rmi_f12.c @@ -31,6 +31,7 @@ enum rmi_f12_object_type { struct f12_data { struct rmi_2d_sensor sensor; struct rmi_2d_sensor_platform_data sensor_pdata; + bool has_dribble; u16 data_addr; @@ -239,12 +240,76 @@ static int rmi_f12_attention(struct rmi_function *fn, return 0; } +static int rmi_f12_write_control_regs(struct rmi_function *fn) +{ + int ret; + const struct rmi_register_desc_item *item; + struct rmi_device *rmi_dev = fn->rmi_dev; + struct f12_data *f12 = dev_get_drvdata(&fn->dev); + int control_size; + char buf[3]; + u16 control_offset = 0; + u8 subpacket_offset = 0; + + if (f12->has_dribble + && (f12->sensor.dribble != RMI_REG_STATE_DEFAULT)) { + item = rmi_get_register_desc_item(&f12->control_reg_desc, 20); + if (item) { + control_offset = rmi_register_desc_calc_reg_offset( + &f12->control_reg_desc, 20); + + /* + * The byte containing the EnableDribble bit will be + * in either byte 0 or byte 2 of control 20. Depending + * on the existence of subpacket 0. If control 20 is + * larger then 3 bytes, just read the first 3. + */ + control_size = min(item->reg_size, 3UL); + + ret = rmi_read_block(rmi_dev, fn->fd.control_base_addr + + control_offset, buf, control_size); + if (ret) + return ret; + + if (rmi_register_desc_has_subpacket(item, 0)) + subpacket_offset += 1; + + switch (f12->sensor.dribble) { + case RMI_REG_STATE_OFF: + buf[subpacket_offset] &= ~BIT(2); + break; + case RMI_REG_STATE_ON: + buf[subpacket_offset] |= BIT(2); + break; + case RMI_REG_STATE_DEFAULT: + default: + break; + } + + ret = rmi_write_block(rmi_dev, + fn->fd.control_base_addr + control_offset, + buf, control_size); + if (ret) + return ret; + } + } + + return 0; + +} + static int rmi_f12_config(struct rmi_function *fn) { struct rmi_driver *drv = fn->rmi_dev->driver; + int ret; drv->set_irq_bits(fn->rmi_dev, fn->irq_mask); + ret = rmi_f12_write_control_regs(fn); + if (ret) + dev_warn(&fn->dev, + "Failed to write F12 control registers: %d\n", ret); + return 0; } @@ -271,7 +336,7 @@ static int rmi_f12_probe(struct rmi_function *fn) } ++query_addr; - if (!(buf & 0x1)) { + if (!(buf & BIT(0))) { dev_err(&fn->dev, "Behavior of F12 without register descriptors is undefined.\n"); return -ENODEV; @@ -281,6 +346,8 @@ static int rmi_f12_probe(struct rmi_function *fn) if (!f12) return -ENOMEM; + f12->has_dribble = !!(buf & BIT(3)); + if (fn->dev.of_node) { ret = rmi_2d_sensor_of_probe(&fn->dev, &f12->sensor_pdata); if (ret) @@ -329,6 +396,7 @@ static int rmi_f12_probe(struct rmi_function *fn) sensor->x_mm = f12->sensor_pdata.x_mm; sensor->y_mm = f12->sensor_pdata.y_mm; + sensor->dribble = f12->sensor_pdata.dribble; if (sensor->sensor_type == rmi_sensor_default) sensor->sensor_type = -- cgit v1.2.3 From 332c3988fe9cbecf9d50f19596b66a34b5eda6de Mon Sep 17 00:00:00 2001 From: Andrew Duggan Date: Tue, 8 Nov 2016 17:03:16 -0800 Subject: Input: synaptics-rmi4 - set the ABS_MT_TOOL_TYPE bit to report tool type The rmi4 2D sensor functions report the tool type via input_mt_report_slot_state(), but the abs parameter bit has not been set so the tool type is not reported to userspace. This patch set the ABS_MT_TOOL_TYPE bit. Signed-off-by: Andrew Duggan Signed-off-by: Benjamin Tissoires Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/rmi_2d_sensor.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/rmi4/rmi_2d_sensor.c b/drivers/input/rmi4/rmi_2d_sensor.c index e97bd7fabccc..07007ff8e29f 100644 --- a/drivers/input/rmi4/rmi_2d_sensor.c +++ b/drivers/input/rmi4/rmi_2d_sensor.c @@ -177,10 +177,12 @@ static void rmi_2d_sensor_set_input_params(struct rmi_2d_sensor *sensor) sensor->dmax = DMAX * res_x; } - input_set_abs_params(input, ABS_MT_PRESSURE, 0, 0xff, 0, 0); - input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 0x0f, 0, 0); - input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 0x0f, 0, 0); - input_set_abs_params(input, ABS_MT_ORIENTATION, 0, 1, 0, 0); + input_set_abs_params(input, ABS_MT_PRESSURE, 0, 0xff, 0, 0); + input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 0x0f, 0, 0); + input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 0x0f, 0, 0); + input_set_abs_params(input, ABS_MT_ORIENTATION, 0, 1, 0, 0); + input_set_abs_params(input, ABS_MT_TOOL_TYPE, + 0, MT_TOOL_MAX, 0, 0); if (sensor->sensor_type == rmi_sensor_touchpad) input_flags = INPUT_MT_POINTER; -- cgit v1.2.3 From 82264d0cf7aef2247563c031ff2ab96579d5d0cc Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 8 Nov 2016 17:05:58 -0800 Subject: Input: synaptics-rmi4 - add SMBus support Code obtained from https://raw.githubusercontent.com/mightybigcar/synaptics-rmi4/jf/drivers/input/rmi4/rmi_smbus.c and updated to match upstream. And fixed to make it work. Signed-off-by: Benjamin Tissoires Signed-off-by: Andrew Duggan Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/Kconfig | 12 ++ drivers/input/rmi4/Makefile | 1 + drivers/input/rmi4/rmi_bus.h | 12 ++ drivers/input/rmi4/rmi_smbus.c | 447 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 472 insertions(+) create mode 100644 drivers/input/rmi4/rmi_smbus.c (limited to 'drivers/input') diff --git a/drivers/input/rmi4/Kconfig b/drivers/input/rmi4/Kconfig index 4c8a55857e00..8cbd362ae8a7 100644 --- a/drivers/input/rmi4/Kconfig +++ b/drivers/input/rmi4/Kconfig @@ -27,6 +27,18 @@ config RMI4_SPI If unsure, say N. +config RMI4_SMB + tristate "RMI4 SMB Support" + depends on RMI4_CORE && I2C + help + Say Y here if you want to support RMI4 devices connected to an SMB + bus. + + If unsure, say N. + + To compile this driver as a module, choose M here: the module will be + called rmi_smbus. + config RMI4_2D_SENSOR bool depends on RMI4_CORE diff --git a/drivers/input/rmi4/Makefile b/drivers/input/rmi4/Makefile index 0bafc8502c4b..a6e27527d514 100644 --- a/drivers/input/rmi4/Makefile +++ b/drivers/input/rmi4/Makefile @@ -12,3 +12,4 @@ rmi_core-$(CONFIG_RMI4_F54) += rmi_f54.o # Transports obj-$(CONFIG_RMI4_I2C) += rmi_i2c.o obj-$(CONFIG_RMI4_SPI) += rmi_spi.o +obj-$(CONFIG_RMI4_SMB) += rmi_smbus.o diff --git a/drivers/input/rmi4/rmi_bus.h b/drivers/input/rmi4/rmi_bus.h index 899579830536..b7625a9ac66a 100644 --- a/drivers/input/rmi4/rmi_bus.h +++ b/drivers/input/rmi4/rmi_bus.h @@ -104,6 +104,18 @@ rmi_get_platform_data(struct rmi_device *d) bool rmi_is_physical_device(struct device *dev); +/** + * rmi_reset - reset a RMI4 device + * @d: Pointer to an RMI device + * + * Calls for a reset of each function implemented by a specific device. + * Returns 0 on success or a negative error code. + */ +static inline int rmi_reset(struct rmi_device *d) +{ + return d->driver->reset_handler(d); +} + /** * rmi_read - read a single byte * @d: Pointer to an RMI device diff --git a/drivers/input/rmi4/rmi_smbus.c b/drivers/input/rmi4/rmi_smbus.c new file mode 100644 index 000000000000..76752555d809 --- /dev/null +++ b/drivers/input/rmi4/rmi_smbus.c @@ -0,0 +1,447 @@ +/* + * Copyright (c) 2015 - 2016 Red Hat, Inc + * Copyright (c) 2011, 2012 Synaptics Incorporated + * Copyright (c) 2011 Unixphere + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rmi_driver.h" + +#define SMB_PROTOCOL_VERSION_ADDRESS 0xfd +#define SMB_MAX_COUNT 32 +#define RMI_SMB2_MAP_SIZE 8 /* 8 entry of 4 bytes each */ +#define RMI_SMB2_MAP_FLAGS_WE 0x01 + +struct mapping_table_entry { + __le16 rmiaddr; + u8 readcount; + u8 flags; +}; + +struct rmi_smb_xport { + struct rmi_transport_dev xport; + struct i2c_client *client; + + struct mutex page_mutex; + int page; + u8 table_index; + struct mutex mappingtable_mutex; + struct mapping_table_entry mapping_table[RMI_SMB2_MAP_SIZE]; +}; + +static int rmi_smb_get_version(struct rmi_smb_xport *rmi_smb) +{ + struct i2c_client *client = rmi_smb->client; + int retval; + + /* Check if for SMBus new version device by reading version byte. */ + retval = i2c_smbus_read_byte_data(client, SMB_PROTOCOL_VERSION_ADDRESS); + if (retval < 0) { + dev_err(&client->dev, "failed to get SMBus version number!\n"); + return retval; + } + return retval + 1; +} + +/* SMB block write - wrapper over ic2_smb_write_block */ +static int smb_block_write(struct rmi_transport_dev *xport, + u8 commandcode, const void *buf, size_t len) +{ + struct rmi_smb_xport *rmi_smb = + container_of(xport, struct rmi_smb_xport, xport); + struct i2c_client *client = rmi_smb->client; + int retval; + + retval = i2c_smbus_write_block_data(client, commandcode, len, buf); + + rmi_dbg(RMI_DEBUG_XPORT, &client->dev, + "wrote %zd bytes at %#04x: %d (%*ph)\n", + len, commandcode, retval, (int)len, buf); + + return retval; +} + +/* + * The function to get command code for smbus operations and keeps + * records to the driver mapping table + */ +static int rmi_smb_get_command_code(struct rmi_transport_dev *xport, + u16 rmiaddr, int bytecount, bool isread, u8 *commandcode) +{ + struct rmi_smb_xport *rmi_smb = + container_of(xport, struct rmi_smb_xport, xport); + int i; + int retval; + struct mapping_table_entry mapping_data[1]; + + mutex_lock(&rmi_smb->mappingtable_mutex); + for (i = 0; i < RMI_SMB2_MAP_SIZE; i++) { + if (rmi_smb->mapping_table[i].rmiaddr == rmiaddr) { + if (isread) { + if (rmi_smb->mapping_table[i].readcount + == bytecount) { + *commandcode = i; + retval = 0; + goto exit; + } + } else { + if (rmi_smb->mapping_table[i].flags & + RMI_SMB2_MAP_FLAGS_WE) { + *commandcode = i; + retval = 0; + goto exit; + } + } + } + } + i = rmi_smb->table_index; + rmi_smb->table_index = (i + 1) % RMI_SMB2_MAP_SIZE; + + /* constructs mapping table data entry. 4 bytes each entry */ + memset(mapping_data, 0, sizeof(mapping_data)); + + mapping_data[0].rmiaddr = cpu_to_le16(rmiaddr); + mapping_data[0].readcount = bytecount; + mapping_data[0].flags = !isread ? RMI_SMB2_MAP_FLAGS_WE : 0; + + retval = smb_block_write(xport, i + 0x80, mapping_data, + sizeof(mapping_data)); + + if (retval < 0) { + /* + * if not written to device mapping table + * clear the driver mapping table records + */ + rmi_smb->mapping_table[i].rmiaddr = 0x0000; + rmi_smb->mapping_table[i].readcount = 0; + rmi_smb->mapping_table[i].flags = 0; + goto exit; + } + /* save to the driver level mapping table */ + rmi_smb->mapping_table[i].rmiaddr = rmiaddr; + rmi_smb->mapping_table[i].readcount = bytecount; + rmi_smb->mapping_table[i].flags = !isread ? RMI_SMB2_MAP_FLAGS_WE : 0; + *commandcode = i; + +exit: + mutex_unlock(&rmi_smb->mappingtable_mutex); + + return retval; +} + +static int rmi_smb_write_block(struct rmi_transport_dev *xport, u16 rmiaddr, + const void *databuff, size_t len) +{ + int retval = 0; + u8 commandcode; + struct rmi_smb_xport *rmi_smb = + container_of(xport, struct rmi_smb_xport, xport); + int cur_len = (int)len; + + mutex_lock(&rmi_smb->page_mutex); + + while (cur_len > 0) { + /* + * break into 32 bytes chunks to write get command code + */ + int block_len = min_t(int, len, SMB_MAX_COUNT); + + retval = rmi_smb_get_command_code(xport, rmiaddr, block_len, + false, &commandcode); + if (retval < 0) + goto exit; + + retval = smb_block_write(xport, commandcode, + databuff, block_len); + if (retval < 0) + goto exit; + + /* prepare to write next block of bytes */ + cur_len -= SMB_MAX_COUNT; + databuff += SMB_MAX_COUNT; + rmiaddr += SMB_MAX_COUNT; + } +exit: + mutex_unlock(&rmi_smb->page_mutex); + return retval; +} + +/* SMB block read - wrapper over ic2_smb_read_block */ +static int smb_block_read(struct rmi_transport_dev *xport, + u8 commandcode, void *buf, size_t len) +{ + struct rmi_smb_xport *rmi_smb = + container_of(xport, struct rmi_smb_xport, xport); + struct i2c_client *client = rmi_smb->client; + int retval; + + retval = i2c_smbus_read_block_data(client, commandcode, buf); + if (retval < 0) + return retval; + + return retval; +} + +static int rmi_smb_read_block(struct rmi_transport_dev *xport, u16 rmiaddr, + void *databuff, size_t len) +{ + struct rmi_smb_xport *rmi_smb = + container_of(xport, struct rmi_smb_xport, xport); + int retval; + u8 commandcode; + int cur_len = (int)len; + + mutex_lock(&rmi_smb->page_mutex); + memset(databuff, 0, len); + + while (cur_len > 0) { + /* break into 32 bytes chunks to write get command code */ + int block_len = min_t(int, cur_len, SMB_MAX_COUNT); + + retval = rmi_smb_get_command_code(xport, rmiaddr, block_len, + true, &commandcode); + if (retval < 0) + goto exit; + + retval = smb_block_read(xport, commandcode, + databuff, block_len); + if (retval < 0) + goto exit; + + /* prepare to read next block of bytes */ + cur_len -= SMB_MAX_COUNT; + databuff += SMB_MAX_COUNT; + rmiaddr += SMB_MAX_COUNT; + } + + retval = 0; + +exit: + mutex_unlock(&rmi_smb->page_mutex); + return retval; +} + +static void rmi_smb_clear_state(struct rmi_smb_xport *rmi_smb) +{ + /* the mapping table has been flushed, discard the current one */ + mutex_lock(&rmi_smb->mappingtable_mutex); + memset(rmi_smb->mapping_table, 0, sizeof(rmi_smb->mapping_table)); + mutex_unlock(&rmi_smb->mappingtable_mutex); +} + +static int rmi_smb_enable_smbus_mode(struct rmi_smb_xport *rmi_smb) +{ + int retval; + + /* we need to get the smbus version to activate the touchpad */ + retval = rmi_smb_get_version(rmi_smb); + if (retval < 0) + return retval; + + return 0; +} + +static int rmi_smb_reset(struct rmi_transport_dev *xport, u16 reset_addr) +{ + struct rmi_smb_xport *rmi_smb = + container_of(xport, struct rmi_smb_xport, xport); + + rmi_smb_clear_state(rmi_smb); + + /* + * we do not call the actual reset command, it has to be handled in + * PS/2 or there will be races between PS/2 and SMBus. + * PS/2 should ensure that a psmouse_reset is called before + * intializing the device and after it has been removed to be in a known + * state. + */ + return rmi_smb_enable_smbus_mode(rmi_smb); +} + +static const struct rmi_transport_ops rmi_smb_ops = { + .write_block = rmi_smb_write_block, + .read_block = rmi_smb_read_block, + .reset = rmi_smb_reset, +}; + +static int rmi_smb_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct rmi_device_platform_data *pdata = dev_get_platdata(&client->dev); + struct rmi_smb_xport *rmi_smb; + int retval; + int smbus_version; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_BLOCK_DATA | + I2C_FUNC_SMBUS_HOST_NOTIFY)) { + dev_err(&client->dev, + "adapter does not support required functionality.\n"); + return -ENODEV; + } + + if (client->irq <= 0) { + dev_err(&client->dev, "no IRQ provided, giving up.\n"); + return client->irq ? client->irq : -ENODEV; + } + + rmi_smb = devm_kzalloc(&client->dev, sizeof(struct rmi_smb_xport), + GFP_KERNEL); + if (!rmi_smb) + return -ENOMEM; + + if (!pdata) { + dev_err(&client->dev, "no platform data, aborting\n"); + return -ENOMEM; + } + + rmi_dbg(RMI_DEBUG_XPORT, &client->dev, "Probing %s.\n", + dev_name(&client->dev)); + + rmi_smb->client = client; + mutex_init(&rmi_smb->page_mutex); + mutex_init(&rmi_smb->mappingtable_mutex); + + rmi_smb->xport.dev = &client->dev; + rmi_smb->xport.pdata = *pdata; + rmi_smb->xport.pdata.irq = client->irq; + rmi_smb->xport.proto_name = "smb2"; + rmi_smb->xport.ops = &rmi_smb_ops; + + retval = rmi_smb_get_version(rmi_smb); + if (retval < 0) + return retval; + + smbus_version = retval; + rmi_dbg(RMI_DEBUG_XPORT, &client->dev, "Smbus version is %d", + smbus_version); + + if (smbus_version != 2) { + dev_err(&client->dev, "Unrecognized SMB version %d.\n", + smbus_version); + return -ENODEV; + } + + i2c_set_clientdata(client, rmi_smb); + + retval = rmi_register_transport_device(&rmi_smb->xport); + if (retval) { + dev_err(&client->dev, "Failed to register transport driver at 0x%.2X.\n", + client->addr); + i2c_set_clientdata(client, NULL); + return retval; + } + + dev_info(&client->dev, "registered rmi smb driver at %#04x.\n", + client->addr); + return 0; + +} + +static int rmi_smb_remove(struct i2c_client *client) +{ + struct rmi_smb_xport *rmi_smb = i2c_get_clientdata(client); + + rmi_unregister_transport_device(&rmi_smb->xport); + + return 0; +} + +static int __maybe_unused rmi_smb_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct rmi_smb_xport *rmi_smb = i2c_get_clientdata(client); + int ret; + + ret = rmi_driver_suspend(rmi_smb->xport.rmi_dev, true); + if (ret) + dev_warn(dev, "Failed to suspend device: %d\n", ret); + + return ret; +} + +static int __maybe_unused rmi_smb_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct rmi_smb_xport *rmi_smb = i2c_get_clientdata(client); + int ret; + + ret = rmi_driver_suspend(rmi_smb->xport.rmi_dev, false); + if (ret) + dev_warn(dev, "Failed to suspend device: %d\n", ret); + + return ret; +} + +static int __maybe_unused rmi_smb_resume(struct device *dev) +{ + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct rmi_smb_xport *rmi_smb = i2c_get_clientdata(client); + struct rmi_device *rmi_dev = rmi_smb->xport.rmi_dev; + int ret; + + rmi_smb_reset(&rmi_smb->xport, 0); + + rmi_reset(rmi_dev); + + ret = rmi_driver_resume(rmi_smb->xport.rmi_dev, true); + if (ret) + dev_warn(dev, "Failed to resume device: %d\n", ret); + + return 0; +} + +static int __maybe_unused rmi_smb_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct rmi_smb_xport *rmi_smb = i2c_get_clientdata(client); + int ret; + + ret = rmi_driver_resume(rmi_smb->xport.rmi_dev, false); + if (ret) + dev_warn(dev, "Failed to resume device: %d\n", ret); + + return 0; +} + +static const struct dev_pm_ops rmi_smb_pm = { + SET_SYSTEM_SLEEP_PM_OPS(rmi_smb_suspend, rmi_smb_resume) + SET_RUNTIME_PM_OPS(rmi_smb_runtime_suspend, rmi_smb_runtime_resume, + NULL) +}; + +static const struct i2c_device_id rmi_id[] = { + { "rmi4_smbus", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rmi_id); + +static struct i2c_driver rmi_smb_driver = { + .driver = { + .name = "rmi4_smbus", + .pm = &rmi_smb_pm, + }, + .id_table = rmi_id, + .probe = rmi_smb_probe, + .remove = rmi_smb_remove, +}; + +module_i2c_driver(rmi_smb_driver); + +MODULE_AUTHOR("Andrew Duggan "); +MODULE_AUTHOR("Benjamin Tissoires "); +MODULE_DESCRIPTION("RMI4 SMBus driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From baf28d91e7b1c9946d6b13a0c6cecf67d761037d Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 14 Nov 2016 10:51:36 -0800 Subject: Input: bma150 - avoid binding to bma180 if IIO bma180 driver present commit ef3714fdbc8d ("Input: bma150 - extend chip detection for bma180"), adds bma180 chip-ids to the input bma150 driver, assuming that they are 100% compatible, but the bma180 is not compatible with the bma150 at all, it has 14 bits resolution instead of 10, and it has quite different control registers too. Treating the bma180 as a bma150 wrt its data registers will just result in throwing away the lowest 4 bits, which is not too bad. But the ctrl registers are a different story. Things happen to just work but supporting that certainly does not make treating the bma180 the same as the bma150 right. Since some setups depend on the evdev interface the bma150 driver offers on top of the bma180, we cannot simply remove the bma180 ids. So this commit only removes the bma180 id when the bma180 iio driver, which does treat the bma180 properly, is enabled. Signed-off-by: Hans de Goede Signed-off-by: Dmitry Torokhov --- drivers/input/misc/bma150.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/misc/bma150.c b/drivers/input/misc/bma150.c index b0d445390ee4..2124390ec38c 100644 --- a/drivers/input/misc/bma150.c +++ b/drivers/input/misc/bma150.c @@ -538,8 +538,13 @@ static int bma150_probe(struct i2c_client *client, return -EIO; } + /* + * Note if the IIO CONFIG_BMA180 driver is enabled we want to fail + * the probe for the bma180 as the iio driver is preferred. + */ chip_id = i2c_smbus_read_byte_data(client, BMA150_CHIP_ID_REG); - if (chip_id != BMA150_CHIP_ID && chip_id != BMA180_CHIP_ID) { + if (chip_id != BMA150_CHIP_ID && + (IS_ENABLED(CONFIG_BMA180) || chip_id != BMA180_CHIP_ID)) { dev_err(&client->dev, "BMA150 chip id error: %d\n", chip_id); return -EINVAL; } @@ -643,7 +648,9 @@ static UNIVERSAL_DEV_PM_OPS(bma150_pm, bma150_suspend, bma150_resume, NULL); static const struct i2c_device_id bma150_id[] = { { "bma150", 0 }, +#if !IS_ENABLED(CONFIG_BMA180) { "bma180", 0 }, +#endif { "smb380", 0 }, { "bma023", 0 }, { } -- cgit v1.2.3 From 6f6deb4866a4e65bc266ca003817b6ab685f999a Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 16 Nov 2016 09:37:08 -0800 Subject: Input: silead - add regulator support On some tablets the touchscreen controller is powered by separate regulators, add support for this. Signed-off-by: Hans de Goede Acked-by: Rob Herring Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/silead.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/silead.c b/drivers/input/touchscreen/silead.c index f502c8488be8..404830a4a366 100644 --- a/drivers/input/touchscreen/silead.c +++ b/drivers/input/touchscreen/silead.c @@ -29,6 +29,7 @@ #include #include #include +#include #include @@ -73,6 +74,7 @@ struct silead_ts_data { struct i2c_client *client; struct gpio_desc *gpio_power; struct input_dev *input; + struct regulator_bulk_data regulators[2]; char fw_name[64]; struct touchscreen_properties prop; u32 max_fingers; @@ -433,6 +435,13 @@ static int silead_ts_set_default_fw_name(struct silead_ts_data *data, } #endif +static void silead_disable_regulator(void *arg) +{ + struct silead_ts_data *data = arg; + + regulator_bulk_disable(ARRAY_SIZE(data->regulators), data->regulators); +} + static int silead_ts_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -465,6 +474,26 @@ static int silead_ts_probe(struct i2c_client *client, if (client->irq <= 0) return -ENODEV; + data->regulators[0].supply = "vddio"; + data->regulators[1].supply = "avdd"; + error = devm_regulator_bulk_get(dev, ARRAY_SIZE(data->regulators), + data->regulators); + if (error) + return error; + + /* + * Enable regulators at probe and disable them at remove, we need + * to keep the chip powered otherwise it forgets its firmware. + */ + error = regulator_bulk_enable(ARRAY_SIZE(data->regulators), + data->regulators); + if (error) + return error; + + error = devm_add_action_or_reset(dev, silead_disable_regulator, data); + if (error) + return error; + /* Power GPIO pin */ data->gpio_power = devm_gpiod_get_optional(dev, "power", GPIOD_OUT_LOW); if (IS_ERR(data->gpio_power)) { -- cgit v1.2.3 From fac96cc9f8679e31a244f98bed5983fe220e295c Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 16 Nov 2016 17:19:57 -0800 Subject: Input: raydium_i2c_ts - fix spelling mistake in dev_err message Trivial fix to spelling mistake "failied" to "failed" in dev_err message. Signed-off-by: Colin Ian King Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/raydium_i2c_ts.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/raydium_i2c_ts.c b/drivers/input/touchscreen/raydium_i2c_ts.c index a99fb5cac5a0..2658afa016c9 100644 --- a/drivers/input/touchscreen/raydium_i2c_ts.c +++ b/drivers/input/touchscreen/raydium_i2c_ts.c @@ -669,7 +669,7 @@ static int raydium_i2c_do_update_firmware(struct raydium_data *ts, if (ts->boot_mode == RAYDIUM_TS_MAIN) { dev_err(&client->dev, - "failied to jump to boot loader: %d\n", + "failed to jump to boot loader: %d\n", error); return -EIO; } -- cgit v1.2.3 From 792f497b22afd0563b94dd8fa129a05f762a2c25 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 16 Nov 2016 17:23:22 -0800 Subject: Input: synaptics-rmi4 - unlock on error We should unlock before returning on this error path. Fixes: 3a762dbd5347 ('[media] Input: synaptics-rmi4 - add support for F54 diagnostics') Signed-off-by: Dan Carpenter Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/rmi_f54.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/rmi4/rmi_f54.c b/drivers/input/rmi4/rmi_f54.c index cf805b960866..2e934aef3d2a 100644 --- a/drivers/input/rmi4/rmi_f54.c +++ b/drivers/input/rmi4/rmi_f54.c @@ -200,7 +200,7 @@ static int rmi_f54_request_report(struct rmi_function *fn, u8 report_type) error = rmi_write(rmi_dev, fn->fd.command_base_addr, F54_GET_REPORT); if (error < 0) - return error; + goto unlock; init_completion(&f54->cmd_done); @@ -209,9 +209,10 @@ static int rmi_f54_request_report(struct rmi_function *fn, u8 report_type) queue_delayed_work(f54->workqueue, &f54->work, 0); +unlock: mutex_unlock(&f54->data_mutex); - return 0; + return error; } static size_t rmi_f54_get_report_size(struct f54_data *f54) -- cgit v1.2.3 From dadbb0aa363bf1233f75cab23977add69d197e23 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Wed, 16 Nov 2016 17:01:26 -0800 Subject: Input: gpio_keys - set input direction explicitly Commit 700a38b27eef ("Input: gpio_keys - switch to using generic device properties") switched to use generic device properties for GPIO keys and commit 5feeca3c1e39 ("Input: gpio_keys - add support for GPIO descriptors") switched from legacy GPIO numbers to GPIO descriptors. Previously devm_gpio_request_one was explicitly passed GPIOF_DIR_IN flag to set the GPIO direction as input. However devm_get_gpiod_from_child doesn't have such provisions and hence fwnode_get_named_gpiod can't set it as input. This breaks few platforms with the following error: " gpiochip_lock_as_irq: tried to flag a GPIO set as output for IRQ unable to lock HW IRQ for IRQ genirq: Failed to request resources for POWER (irq ) on irqchip gpio_keys: Unable to claim irq ; error -22 gpio-keys: probe failed with error -22 " This patch fixes the issue by setting input direction explicitly for gpio lines described by generic properties. Fixes: 700a38b27eef ("Input: gpio_keys - switch to using generic device properties") Signed-off-by: Sudeep Holla Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/gpio_keys.c | 7 +++++++ drivers/input/keyboard/gpio_keys_polled.c | 8 ++++++++ 2 files changed, 15 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index 5576f2ae0b71..582462d0af75 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -496,6 +496,13 @@ static int gpio_keys_setup_key(struct platform_device *pdev, error); return error; } + } else { + error = gpiod_direction_input(bdata->gpiod); + if (error) { + dev_err(dev, "Failed to configure GPIO %d as input: %d\n", + desc_to_gpio(bdata->gpiod), error); + return error; + } } } else if (gpio_is_valid(button->gpio)) { /* diff --git a/drivers/input/keyboard/gpio_keys_polled.c b/drivers/input/keyboard/gpio_keys_polled.c index 72b350315d43..bed4f2086158 100644 --- a/drivers/input/keyboard/gpio_keys_polled.c +++ b/drivers/input/keyboard/gpio_keys_polled.c @@ -314,6 +314,14 @@ static int gpio_keys_polled_probe(struct platform_device *pdev) fwnode_handle_put(child); return error; } + + error = gpiod_direction_input(bdata->gpiod); + if (error) { + dev_err(dev, "Failed to configure GPIO %d as input: %d\n", + desc_to_gpio(bdata->gpiod), error); + fwnode_handle_put(child); + return error; + } } else if (gpio_is_valid(button->gpio)) { /* * Legacy GPIO number so request the GPIO here and -- cgit v1.2.3 From 29fd0ec2bdbef6734fd4c39c23f61d9f030a66a0 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 22 Nov 2016 17:44:12 -0800 Subject: Input: synaptics-rmi4 - add support for F34 device reflash Add support for updating firmware, triggered by a sysfs attribute. This patch has been tested on Synaptics S7300. Signed-off-by: Nick Dyer Tested-by: Chris Healy Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/Kconfig | 11 + drivers/input/rmi4/Makefile | 1 + drivers/input/rmi4/rmi_bus.c | 3 + drivers/input/rmi4/rmi_driver.c | 105 ++++++--- drivers/input/rmi4/rmi_driver.h | 24 ++ drivers/input/rmi4/rmi_f01.c | 6 + drivers/input/rmi4/rmi_f34.c | 481 ++++++++++++++++++++++++++++++++++++++++ drivers/input/rmi4/rmi_f34.h | 68 ++++++ 8 files changed, 668 insertions(+), 31 deletions(-) create mode 100644 drivers/input/rmi4/rmi_f34.c create mode 100644 drivers/input/rmi4/rmi_f34.h (limited to 'drivers/input') diff --git a/drivers/input/rmi4/Kconfig b/drivers/input/rmi4/Kconfig index 8cbd362ae8a7..9a24867ae7a5 100644 --- a/drivers/input/rmi4/Kconfig +++ b/drivers/input/rmi4/Kconfig @@ -74,6 +74,17 @@ config RMI4_F30 Function 30 provides GPIO and LED support for RMI4 devices. This includes support for buttons on TouchPads and ClickPads. +config RMI4_F34 + bool "RMI4 Function 34 (Device reflash)" + depends on RMI4_CORE + select FW_LOADER + help + Say Y here if you want to add support for RMI4 function 34. + + Function 34 provides support for upgrading the firmware on the RMI4 + device via the firmware loader interface. This is triggered using a + sysfs attribute. + config RMI4_F54 bool "RMI4 Function 54 (Analog diagnostics)" depends on RMI4_CORE diff --git a/drivers/input/rmi4/Makefile b/drivers/input/rmi4/Makefile index a6e27527d514..0250abf38ad9 100644 --- a/drivers/input/rmi4/Makefile +++ b/drivers/input/rmi4/Makefile @@ -7,6 +7,7 @@ rmi_core-$(CONFIG_RMI4_2D_SENSOR) += rmi_2d_sensor.o rmi_core-$(CONFIG_RMI4_F11) += rmi_f11.o rmi_core-$(CONFIG_RMI4_F12) += rmi_f12.o rmi_core-$(CONFIG_RMI4_F30) += rmi_f30.o +rmi_core-$(CONFIG_RMI4_F34) += rmi_f34.o rmi_core-$(CONFIG_RMI4_F54) += rmi_f54.o # Transports diff --git a/drivers/input/rmi4/rmi_bus.c b/drivers/input/rmi4/rmi_bus.c index 84b321262d74..ef7a66230d83 100644 --- a/drivers/input/rmi4/rmi_bus.c +++ b/drivers/input/rmi4/rmi_bus.c @@ -315,6 +315,9 @@ static struct rmi_function_handler *fn_handlers[] = { #ifdef CONFIG_RMI4_F30 &rmi_f30_handler, #endif +#ifdef CONFIG_RMI4_F34 + &rmi_f34_handler, +#endif #ifdef CONFIG_RMI4_F54 &rmi_f54_handler, #endif diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c index 4f8d19794b01..2b17d8cb3d10 100644 --- a/drivers/input/rmi4/rmi_driver.c +++ b/drivers/input/rmi4/rmi_driver.c @@ -35,14 +35,24 @@ #define RMI_DEVICE_RESET_CMD 0x01 #define DEFAULT_RESET_DELAY_MS 100 -static void rmi_free_function_list(struct rmi_device *rmi_dev) +void rmi_free_function_list(struct rmi_device *rmi_dev) { struct rmi_function *fn, *tmp; struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev); rmi_dbg(RMI_DEBUG_CORE, &rmi_dev->dev, "Freeing function list\n"); + mutex_lock(&data->irq_mutex); + + devm_kfree(&rmi_dev->dev, data->irq_memory); + data->irq_memory = NULL; + data->irq_status = NULL; + data->fn_irq_bits = NULL; + data->current_irq_mask = NULL; + data->new_irq_mask = NULL; + data->f01_container = NULL; + data->f34_container = NULL; /* Doing it in the reverse order so F01 will be removed last */ list_for_each_entry_safe_reverse(fn, tmp, @@ -50,7 +60,10 @@ static void rmi_free_function_list(struct rmi_device *rmi_dev) list_del(&fn->node); rmi_unregister_function(fn); } + + mutex_unlock(&data->irq_mutex); } +EXPORT_SYMBOL_GPL(rmi_free_function_list); static int reset_one_function(struct rmi_function *fn) { @@ -147,24 +160,25 @@ static int rmi_process_interrupt_requests(struct rmi_device *rmi_dev) if (!data) return 0; + mutex_lock(&data->irq_mutex); + if (!data->irq_status || !data->f01_container) { + mutex_unlock(&data->irq_mutex); + return 0; + } + if (!rmi_dev->xport->attn_data) { error = rmi_read_block(rmi_dev, data->f01_container->fd.data_base_addr + 1, data->irq_status, data->num_of_irq_regs); if (error < 0) { dev_err(dev, "Failed to read irqs, code=%d\n", error); + mutex_unlock(&data->irq_mutex); return error; } } - mutex_lock(&data->irq_mutex); bitmap_and(data->irq_status, data->irq_status, data->current_irq_mask, data->irq_count); - /* - * At this point, irq_status has all bits that are set in the - * interrupt status register and are enabled. - */ - mutex_unlock(&data->irq_mutex); /* * It would be nice to be able to use irq_chip to handle these @@ -180,6 +194,8 @@ static int rmi_process_interrupt_requests(struct rmi_device *rmi_dev) if (data->input) input_sync(data->input); + mutex_unlock(&data->irq_mutex); + return 0; } @@ -244,12 +260,18 @@ static int rmi_suspend_functions(struct rmi_device *rmi_dev) struct rmi_function *entry; int retval; + mutex_lock(&data->irq_mutex); + list_for_each_entry(entry, &data->function_list, node) { retval = suspend_one_function(entry); - if (retval < 0) + if (retval < 0) { + mutex_unlock(&data->irq_mutex); return retval; + } } + mutex_unlock(&data->irq_mutex); + return 0; } @@ -278,16 +300,22 @@ static int rmi_resume_functions(struct rmi_device *rmi_dev) struct rmi_function *entry; int retval; + mutex_lock(&data->irq_mutex); + list_for_each_entry(entry, &data->function_list, node) { retval = resume_one_function(entry); - if (retval < 0) + if (retval < 0) { + mutex_unlock(&data->irq_mutex); return retval; + } } + mutex_unlock(&data->irq_mutex); + return 0; } -static int enable_sensor(struct rmi_device *rmi_dev) +int rmi_enable_sensor(struct rmi_device *rmi_dev) { int retval = 0; @@ -297,6 +325,7 @@ static int enable_sensor(struct rmi_device *rmi_dev) return rmi_process_interrupt_requests(rmi_dev); } +EXPORT_SYMBOL_GPL(rmi_enable_sensor); /** * rmi_driver_set_input_params - set input device id and other data. @@ -502,10 +531,9 @@ static int rmi_scan_pdt_page(struct rmi_device *rmi_dev, RMI_SCAN_DONE : RMI_SCAN_CONTINUE; } -static int rmi_scan_pdt(struct rmi_device *rmi_dev, void *ctx, - int (*callback)(struct rmi_device *rmi_dev, - void *ctx, - const struct pdt_entry *entry)) +int rmi_scan_pdt(struct rmi_device *rmi_dev, void *ctx, + int (*callback)(struct rmi_device *rmi_dev, + void *ctx, const struct pdt_entry *entry)) { int page; int empty_pages = 0; @@ -520,6 +548,7 @@ static int rmi_scan_pdt(struct rmi_device *rmi_dev, void *ctx, return retval < 0 ? retval : 0; } +EXPORT_SYMBOL_GPL(rmi_scan_pdt); int rmi_read_register_desc(struct rmi_device *d, u16 addr, struct rmi_register_descriptor *rdesc) @@ -740,19 +769,15 @@ static int rmi_count_irqs(struct rmi_device *rmi_dev, int *irq_count = ctx; *irq_count += pdt->interrupt_source_count; - if (pdt->function_number == 0x01) { + if (pdt->function_number == 0x01) data->f01_bootloader_mode = rmi_check_bootloader_mode(rmi_dev, pdt); - if (data->f01_bootloader_mode) - dev_warn(&rmi_dev->dev, - "WARNING: RMI4 device is in bootloader mode!\n"); - } return RMI_SCAN_CONTINUE; } -static int rmi_initial_reset(struct rmi_device *rmi_dev, - void *ctx, const struct pdt_entry *pdt) +int rmi_initial_reset(struct rmi_device *rmi_dev, void *ctx, + const struct pdt_entry *pdt) { int error; @@ -787,6 +812,7 @@ static int rmi_initial_reset(struct rmi_device *rmi_dev, /* F01 should always be on page 0. If we don't find it there, fail. */ return pdt->page_start == 0 ? RMI_SCAN_CONTINUE : -ENODEV; } +EXPORT_SYMBOL_GPL(rmi_initial_reset); static int rmi_create_function(struct rmi_device *rmi_dev, void *ctx, const struct pdt_entry *pdt) @@ -828,6 +854,8 @@ static int rmi_create_function(struct rmi_device *rmi_dev, if (pdt->function_number == 0x01) data->f01_container = fn; + else if (pdt->function_number == 0x34) + data->f34_container = fn; list_add_tail(&fn->node, &data->function_list); @@ -893,6 +921,7 @@ static int rmi_driver_remove(struct device *dev) disable_irq(irq); + rmi_f34_remove_sysfs(rmi_dev); rmi_free_function_list(rmi_dev); return 0; @@ -919,13 +948,12 @@ static inline int rmi_driver_of_probe(struct device *dev, } #endif -static int rmi_probe_interrupts(struct rmi_driver_data *data) +int rmi_probe_interrupts(struct rmi_driver_data *data) { struct rmi_device *rmi_dev = data->rmi_dev; struct device *dev = &rmi_dev->dev; int irq_count; size_t size; - void *irq_memory; int retval; /* @@ -941,31 +969,38 @@ static int rmi_probe_interrupts(struct rmi_driver_data *data) dev_err(dev, "IRQ counting failed with code %d.\n", retval); return retval; } + + if (data->f01_bootloader_mode) + dev_warn(&rmi_dev->dev, "Device in bootloader mode.\n"); + data->irq_count = irq_count; data->num_of_irq_regs = (data->irq_count + 7) / 8; size = BITS_TO_LONGS(data->irq_count) * sizeof(unsigned long); - irq_memory = devm_kzalloc(dev, size * 4, GFP_KERNEL); - if (!irq_memory) { + data->irq_memory = devm_kzalloc(dev, size * 4, GFP_KERNEL); + if (!data->irq_memory) { dev_err(dev, "Failed to allocate memory for irq masks.\n"); return retval; } - data->irq_status = irq_memory + size * 0; - data->fn_irq_bits = irq_memory + size * 1; - data->current_irq_mask = irq_memory + size * 2; - data->new_irq_mask = irq_memory + size * 3; + data->irq_status = data->irq_memory + size * 0; + data->fn_irq_bits = data->irq_memory + size * 1; + data->current_irq_mask = data->irq_memory + size * 2; + data->new_irq_mask = data->irq_memory + size * 3; return retval; } +EXPORT_SYMBOL_GPL(rmi_probe_interrupts); -static int rmi_init_functions(struct rmi_driver_data *data) +int rmi_init_functions(struct rmi_driver_data *data) { struct rmi_device *rmi_dev = data->rmi_dev; struct device *dev = &rmi_dev->dev; int irq_count; int retval; + mutex_lock(&data->irq_mutex); + irq_count = 0; rmi_dbg(RMI_DEBUG_CORE, dev, "%s: Creating functions.\n", __func__); retval = rmi_scan_pdt(rmi_dev, &irq_count, rmi_create_function); @@ -990,12 +1025,16 @@ static int rmi_init_functions(struct rmi_driver_data *data) goto err_destroy_functions; } + mutex_unlock(&data->irq_mutex); + return 0; err_destroy_functions: rmi_free_function_list(rmi_dev); + mutex_unlock(&data->irq_mutex); return retval; } +EXPORT_SYMBOL_GPL(rmi_init_functions); static int rmi_driver_probe(struct device *dev) { @@ -1100,6 +1139,10 @@ static int rmi_driver_probe(struct device *dev) if (retval) goto err; + retval = rmi_f34_create_sysfs(rmi_dev); + if (retval) + goto err; + if (data->input) { rmi_driver_set_input_name(rmi_dev, data->input); if (!rmi_dev->xport->input) { @@ -1117,7 +1160,7 @@ static int rmi_driver_probe(struct device *dev) if (data->f01_container->dev.driver) /* Driver already bound, so enable ATTN now. */ - return enable_sensor(rmi_dev); + return rmi_enable_sensor(rmi_dev); return 0; diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h index 8dfbebe9bf86..e627a3a8aced 100644 --- a/drivers/input/rmi4/rmi_driver.h +++ b/drivers/input/rmi4/rmi_driver.h @@ -95,12 +95,36 @@ bool rmi_register_desc_has_subpacket(const struct rmi_register_desc_item *item, bool rmi_is_physical_driver(struct device_driver *); int rmi_register_physical_driver(void); void rmi_unregister_physical_driver(void); +void rmi_free_function_list(struct rmi_device *rmi_dev); +int rmi_enable_sensor(struct rmi_device *rmi_dev); +int rmi_scan_pdt(struct rmi_device *rmi_dev, void *ctx, + int (*callback)(struct rmi_device *rmi_dev, void *ctx, + const struct pdt_entry *entry)); +int rmi_probe_interrupts(struct rmi_driver_data *data); +int rmi_init_functions(struct rmi_driver_data *data); +int rmi_initial_reset(struct rmi_device *rmi_dev, void *ctx, + const struct pdt_entry *pdt); char *rmi_f01_get_product_ID(struct rmi_function *fn); +#ifdef CONFIG_RMI4_F34 +int rmi_f34_create_sysfs(struct rmi_device *rmi_dev); +void rmi_f34_remove_sysfs(struct rmi_device *rmi_dev); +#else +static inline int rmi_f34_create_sysfs(struct rmi_device *rmi_dev) +{ + return 0; +} + +static inline void rmi_f34_remove_sysfs(struct rmi_device *rmi_dev) +{ +} +#endif /* CONFIG_RMI_F34 */ + extern struct rmi_function_handler rmi_f01_handler; extern struct rmi_function_handler rmi_f11_handler; extern struct rmi_function_handler rmi_f12_handler; extern struct rmi_function_handler rmi_f30_handler; +extern struct rmi_function_handler rmi_f34_handler; extern struct rmi_function_handler rmi_f54_handler; #endif diff --git a/drivers/input/rmi4/rmi_f01.c b/drivers/input/rmi4/rmi_f01.c index 2cfa9f64acfb..cae35c6cde31 100644 --- a/drivers/input/rmi4/rmi_f01.c +++ b/drivers/input/rmi4/rmi_f01.c @@ -63,6 +63,8 @@ struct f01_basic_properties { #define RMI_F01_STATUS_CODE(status) ((status) & 0x0f) /* The device has lost its configuration for some reason. */ #define RMI_F01_STATUS_UNCONFIGURED(status) (!!((status) & 0x80)) +/* The device is in bootloader mode */ +#define RMI_F01_STATUS_BOOTLOADER(status) ((status) & 0x40) /* Control register bits */ @@ -594,6 +596,10 @@ static int rmi_f01_attention(struct rmi_function *fn, return error; } + if (RMI_F01_STATUS_BOOTLOADER(device_status)) + dev_warn(&fn->dev, + "Device in bootloader mode, please update firmware\n"); + if (RMI_F01_STATUS_UNCONFIGURED(device_status)) { dev_warn(&fn->dev, "Device reset detected.\n"); error = rmi_dev->driver->reset_handler(rmi_dev); diff --git a/drivers/input/rmi4/rmi_f34.c b/drivers/input/rmi4/rmi_f34.c new file mode 100644 index 000000000000..03df85ac91a5 --- /dev/null +++ b/drivers/input/rmi4/rmi_f34.c @@ -0,0 +1,481 @@ +/* + * Copyright (c) 2007-2016, Synaptics Incorporated + * Copyright (C) 2016 Zodiac Inflight Innovations + * + * 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 +#include +#include +#include +#include + +#include "rmi_driver.h" +#include "rmi_f34.h" + +static int rmi_f34_write_bootloader_id(struct f34_data *f34) +{ + struct rmi_function *fn = f34->fn; + struct rmi_device *rmi_dev = fn->rmi_dev; + u8 bootloader_id[F34_BOOTLOADER_ID_LEN]; + int ret; + + ret = rmi_read_block(rmi_dev, fn->fd.query_base_addr, + bootloader_id, sizeof(bootloader_id)); + if (ret) { + dev_err(&fn->dev, "%s: Reading bootloader ID failed: %d\n", + __func__, ret); + return ret; + } + + rmi_dbg(RMI_DEBUG_FN, &fn->dev, "%s: writing bootloader id '%c%c'\n", + __func__, bootloader_id[0], bootloader_id[1]); + + ret = rmi_write_block(rmi_dev, + fn->fd.data_base_addr + F34_BLOCK_DATA_OFFSET, + bootloader_id, sizeof(bootloader_id)); + if (ret) { + dev_err(&fn->dev, "Failed to write bootloader ID: %d\n", ret); + return ret; + } + + return 0; +} + +static int rmi_f34_command(struct f34_data *f34, u8 command, + unsigned int timeout, bool write_bl_id) +{ + struct rmi_function *fn = f34->fn; + struct rmi_device *rmi_dev = fn->rmi_dev; + int ret; + + if (write_bl_id) { + ret = rmi_f34_write_bootloader_id(f34); + if (ret) + return ret; + } + + init_completion(&f34->v5.cmd_done); + + ret = rmi_read(rmi_dev, f34->v5.ctrl_address, &f34->v5.status); + if (ret) { + dev_err(&f34->fn->dev, + "%s: Failed to read cmd register: %d (command %#02x)\n", + __func__, ret, command); + return ret; + } + + f34->v5.status |= command & 0x0f; + + ret = rmi_write(rmi_dev, f34->v5.ctrl_address, f34->v5.status); + if (ret < 0) { + dev_err(&f34->fn->dev, + "Failed to write F34 command %#02x: %d\n", + command, ret); + return ret; + } + + if (!wait_for_completion_timeout(&f34->v5.cmd_done, + msecs_to_jiffies(timeout))) { + + ret = rmi_read(rmi_dev, f34->v5.ctrl_address, &f34->v5.status); + if (ret) { + dev_err(&f34->fn->dev, + "%s: cmd %#02x timed out: %d\n", + __func__, command, ret); + return ret; + } + + if (f34->v5.status & 0x7f) { + dev_err(&f34->fn->dev, + "%s: cmd %#02x timed out, status: %#02x\n", + __func__, command, f34->v5.status); + return -ETIMEDOUT; + } + } + + return 0; +} + +static int rmi_f34_attention(struct rmi_function *fn, unsigned long *irq_bits) +{ + struct f34_data *f34 = dev_get_drvdata(&fn->dev); + int ret; + + ret = rmi_read(f34->fn->rmi_dev, f34->v5.ctrl_address, &f34->v5.status); + rmi_dbg(RMI_DEBUG_FN, &fn->dev, "%s: status: %#02x, ret: %d\n", + __func__, f34->v5.status, ret); + + if (!ret && !(f34->v5.status & 0x7f)) + complete(&f34->v5.cmd_done); + + return 0; +} + +static int rmi_f34_write_blocks(struct f34_data *f34, const void *data, + int block_count, u8 command) +{ + struct rmi_function *fn = f34->fn; + struct rmi_device *rmi_dev = fn->rmi_dev; + u16 address = fn->fd.data_base_addr + F34_BLOCK_DATA_OFFSET; + u8 start_address[] = { 0, 0 }; + int i; + int ret; + + ret = rmi_write_block(rmi_dev, fn->fd.data_base_addr, + start_address, sizeof(start_address)); + if (ret) { + dev_err(&fn->dev, "Failed to write initial zeros: %d\n", ret); + return ret; + } + + for (i = 0; i < block_count; i++) { + ret = rmi_write_block(rmi_dev, address, + data, f34->v5.block_size); + if (ret) { + dev_err(&fn->dev, + "failed to write block #%d: %d\n", i, ret); + return ret; + } + + ret = rmi_f34_command(f34, command, F34_IDLE_WAIT_MS, false); + if (ret) { + dev_err(&fn->dev, + "Failed to write command for block #%d: %d\n", + i, ret); + return ret; + } + + rmi_dbg(RMI_DEBUG_FN, &fn->dev, "wrote block %d of %d\n", + i + 1, block_count); + + data += f34->v5.block_size; + } + + return 0; +} + +static int rmi_f34_write_firmware(struct f34_data *f34, const void *data) +{ + return rmi_f34_write_blocks(f34, data, f34->v5.fw_blocks, + F34_WRITE_FW_BLOCK); +} + +static int rmi_f34_write_config(struct f34_data *f34, const void *data) +{ + return rmi_f34_write_blocks(f34, data, f34->v5.config_blocks, + F34_WRITE_CONFIG_BLOCK); +} + +int rmi_f34_enable_flash(struct f34_data *f34) +{ + return rmi_f34_command(f34, F34_ENABLE_FLASH_PROG, + F34_ENABLE_WAIT_MS, true); +} + +static int rmi_f34_flash_firmware(struct f34_data *f34, + const struct rmi_f34_firmware *syn_fw) +{ + struct rmi_function *fn = f34->fn; + int ret; + + if (syn_fw->image_size) { + dev_info(&fn->dev, "Erasing firmware...\n"); + ret = rmi_f34_command(f34, F34_ERASE_ALL, + F34_ERASE_WAIT_MS, true); + if (ret) + return ret; + + dev_info(&fn->dev, "Writing firmware (%d bytes)...\n", + syn_fw->image_size); + ret = rmi_f34_write_firmware(f34, syn_fw->data); + if (ret) + return ret; + } + + if (syn_fw->config_size) { + /* + * We only need to erase config if we haven't updated + * firmware. + */ + if (!syn_fw->image_size) { + dev_info(&fn->dev, "Erasing config...\n"); + ret = rmi_f34_command(f34, F34_ERASE_CONFIG, + F34_ERASE_WAIT_MS, true); + if (ret) + return ret; + } + + dev_info(&fn->dev, "Writing config (%d bytes)...\n", + syn_fw->config_size); + ret = rmi_f34_write_config(f34, + &syn_fw->data[syn_fw->image_size]); + if (ret) + return ret; + } + + return 0; +} + +int rmi_f34_update_firmware(struct f34_data *f34, const struct firmware *fw) +{ + const struct rmi_f34_firmware *syn_fw; + int ret; + + syn_fw = (const struct rmi_f34_firmware *)fw->data; + BUILD_BUG_ON(offsetof(struct rmi_f34_firmware, data) != + F34_FW_IMAGE_OFFSET); + + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, + "FW size:%d, checksum:%08x, image_size:%d, config_size:%d\n", + (int)fw->size, + le32_to_cpu(syn_fw->checksum), + le32_to_cpu(syn_fw->image_size), + le32_to_cpu(syn_fw->config_size)); + + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, + "FW bootloader_id:%02x, product_id:%.*s, info: %02x%02x\n", + syn_fw->bootloader_version, + (int)sizeof(syn_fw->product_id), syn_fw->product_id, + syn_fw->product_info[0], syn_fw->product_info[1]); + + if (syn_fw->image_size && + syn_fw->image_size != f34->v5.fw_blocks * f34->v5.block_size) { + dev_err(&f34->fn->dev, + "Bad firmware image: fw size %d, expected %d\n", + syn_fw->image_size, + f34->v5.fw_blocks * f34->v5.block_size); + ret = -EILSEQ; + goto out; + } + + if (syn_fw->config_size && + syn_fw->config_size != f34->v5.config_blocks * f34->v5.block_size) { + dev_err(&f34->fn->dev, + "Bad firmware image: config size %d, expected %d\n", + syn_fw->config_size, + f34->v5.config_blocks * f34->v5.block_size); + ret = -EILSEQ; + goto out; + } + + if (syn_fw->image_size && !syn_fw->config_size) { + dev_err(&f34->fn->dev, "Bad firmware image: no config data\n"); + ret = -EILSEQ; + goto out; + } + + dev_info(&f34->fn->dev, "Firmware image OK\n"); + mutex_lock(&f34->v5.flash_mutex); + + ret = rmi_f34_flash_firmware(f34, syn_fw); + + mutex_unlock(&f34->v5.flash_mutex); + +out: + return ret; +} + +static int rmi_firmware_update(struct rmi_driver_data *data, + const struct firmware *fw) +{ + struct device *dev = &data->rmi_dev->dev; + struct f34_data *f34; + int ret; + + if (!data->f34_container) { + dev_warn(dev, "%s: No F34 present!\n", __func__); + return -EINVAL; + } + + /* Only version 0 currently supported */ + if (data->f34_container->fd.function_version != 0) { + dev_warn(dev, "F34 V%d not supported!\n", + data->f34_container->fd.function_version); + return -ENODEV; + } + + f34 = dev_get_drvdata(&data->f34_container->dev); + + /* Enter flash mode */ + ret = rmi_f34_enable_flash(f34); + if (ret) + return ret; + + /* Tear down functions and re-probe */ + rmi_free_function_list(data->rmi_dev); + + ret = rmi_probe_interrupts(data); + if (ret) + return ret; + + ret = rmi_init_functions(data); + if (ret) + return ret; + + if (!data->f01_bootloader_mode || !data->f34_container) { + dev_warn(dev, "%s: No F34 present or not in bootloader!\n", + __func__); + return -EINVAL; + } + + f34 = dev_get_drvdata(&data->f34_container->dev); + + /* Perform firmware update */ + ret = rmi_f34_update_firmware(f34, fw); + + dev_info(&f34->fn->dev, "Firmware update complete, status:%d\n", ret); + + /* Re-probe */ + rmi_dbg(RMI_DEBUG_FN, dev, "Re-probing device\n"); + rmi_free_function_list(data->rmi_dev); + + ret = rmi_scan_pdt(data->rmi_dev, NULL, rmi_initial_reset); + if (ret < 0) + dev_warn(dev, "RMI reset failed!\n"); + + ret = rmi_probe_interrupts(data); + if (ret) + return ret; + + ret = rmi_init_functions(data); + if (ret) + return ret; + + if (data->f01_container->dev.driver) + /* Driver already bound, so enable ATTN now. */ + return rmi_enable_sensor(data->rmi_dev); + + rmi_dbg(RMI_DEBUG_FN, dev, "%s complete\n", __func__); + + return ret; +} + +static ssize_t rmi_driver_update_fw_store(struct device *dev, + struct device_attribute *dattr, + const char *buf, size_t count) +{ + struct rmi_driver_data *data = dev_get_drvdata(dev); + char fw_name[NAME_MAX]; + const struct firmware *fw; + size_t copy_count = count; + int ret; + + if (count == 0 || count >= NAME_MAX) + return -EINVAL; + + if (buf[count - 1] == '\0' || buf[count - 1] == '\n') + copy_count -= 1; + + strncpy(fw_name, buf, copy_count); + fw_name[copy_count] = '\0'; + + ret = request_firmware(&fw, fw_name, dev); + if (ret) + return ret; + + dev_info(dev, "Flashing %s\n", fw_name); + + ret = rmi_firmware_update(data, fw); + + release_firmware(fw); + + return ret ?: count; +} + +static DEVICE_ATTR(update_fw, 0200, NULL, rmi_driver_update_fw_store); + +static struct attribute *rmi_firmware_attrs[] = { + &dev_attr_update_fw.attr, + NULL +}; + +static struct attribute_group rmi_firmware_attr_group = { + .attrs = rmi_firmware_attrs, +}; + +static int rmi_f34_probe(struct rmi_function *fn) +{ + struct f34_data *f34; + unsigned char f34_queries[9]; + bool has_config_id; + int ret; + + f34 = devm_kzalloc(&fn->dev, sizeof(struct f34_data), GFP_KERNEL); + if (!f34) + return -ENOMEM; + + f34->fn = fn; + dev_set_drvdata(&fn->dev, f34); + + ret = rmi_read_block(fn->rmi_dev, fn->fd.query_base_addr, + f34_queries, sizeof(f34_queries)); + if (ret) { + dev_err(&fn->dev, "%s: Failed to query properties\n", + __func__); + return ret; + } + + snprintf(f34->bootloader_id, sizeof(f34->bootloader_id), + "%c%c", f34_queries[0], f34_queries[1]); + + mutex_init(&f34->v5.flash_mutex); + init_completion(&f34->v5.cmd_done); + + f34->v5.block_size = get_unaligned_le16(&f34_queries[3]); + f34->v5.fw_blocks = get_unaligned_le16(&f34_queries[5]); + f34->v5.config_blocks = get_unaligned_le16(&f34_queries[7]); + f34->v5.ctrl_address = fn->fd.data_base_addr + F34_BLOCK_DATA_OFFSET + + f34->v5.block_size; + has_config_id = f34_queries[2] & (1 << 2); + + rmi_dbg(RMI_DEBUG_FN, &fn->dev, "Bootloader ID: %s\n", + f34->bootloader_id); + rmi_dbg(RMI_DEBUG_FN, &fn->dev, "Block size: %d\n", + f34->v5.block_size); + rmi_dbg(RMI_DEBUG_FN, &fn->dev, "FW blocks: %d\n", + f34->v5.fw_blocks); + rmi_dbg(RMI_DEBUG_FN, &fn->dev, "CFG blocks: %d\n", + f34->v5.config_blocks); + + if (has_config_id) { + ret = rmi_read_block(fn->rmi_dev, fn->fd.control_base_addr, + f34_queries, sizeof(f34_queries)); + if (ret) { + dev_err(&fn->dev, "Failed to read F34 config ID\n"); + return ret; + } + + snprintf(f34->configuration_id, sizeof(f34->configuration_id), + "%02x%02x%02x%02x", + f34_queries[0], f34_queries[1], + f34_queries[2], f34_queries[3]); + + rmi_dbg(RMI_DEBUG_FN, &fn->dev, "Configuration ID: %s\n", + f34->configuration_id); + } + + return 0; +} + +int rmi_f34_create_sysfs(struct rmi_device *rmi_dev) +{ + return sysfs_create_group(&rmi_dev->dev.kobj, &rmi_firmware_attr_group); +} + +void rmi_f34_remove_sysfs(struct rmi_device *rmi_dev) +{ + sysfs_remove_group(&rmi_dev->dev.kobj, &rmi_firmware_attr_group); +} + +struct rmi_function_handler rmi_f34_handler = { + .driver = { + .name = "rmi4_f34", + }, + .func = 0x34, + .probe = rmi_f34_probe, + .attention = rmi_f34_attention, +}; diff --git a/drivers/input/rmi4/rmi_f34.h b/drivers/input/rmi4/rmi_f34.h new file mode 100644 index 000000000000..6cee5282fbb4 --- /dev/null +++ b/drivers/input/rmi4/rmi_f34.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2007-2016, Synaptics Incorporated + * Copyright (C) 2016 Zodiac Inflight Innovations + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#ifndef _RMI_F34_H +#define _RMI_F34_H + +/* F34 image file offsets. */ +#define F34_FW_IMAGE_OFFSET 0x100 + +/* F34 register offsets. */ +#define F34_BLOCK_DATA_OFFSET 2 + +/* F34 commands */ +#define F34_WRITE_FW_BLOCK 0x2 +#define F34_ERASE_ALL 0x3 +#define F34_READ_CONFIG_BLOCK 0x5 +#define F34_WRITE_CONFIG_BLOCK 0x6 +#define F34_ERASE_CONFIG 0x7 +#define F34_ENABLE_FLASH_PROG 0xf + +#define F34_STATUS_IN_PROGRESS 0xff +#define F34_STATUS_IDLE 0x80 + +#define F34_IDLE_WAIT_MS 500 +#define F34_ENABLE_WAIT_MS 300 +#define F34_ERASE_WAIT_MS 5000 + +#define F34_BOOTLOADER_ID_LEN 2 + +struct rmi_f34_firmware { + __le32 checksum; + u8 pad1[3]; + u8 bootloader_version; + __le32 image_size; + __le32 config_size; + u8 product_id[10]; + u8 product_info[2]; + u8 pad2[228]; + u8 data[]; +}; + +struct f34v5_data { + u16 block_size; + u16 fw_blocks; + u16 config_blocks; + u16 ctrl_address; + u8 status; + + struct completion cmd_done; + struct mutex flash_mutex; +}; + +struct f34_data { + struct rmi_function *fn; + + unsigned char bootloader_id[5]; + unsigned char configuration_id[9]; + + struct f34v5_data v5; +}; + +#endif /* _RMI_F34_H */ -- cgit v1.2.3 From 6adba43fd222ea362c36296d1a6897c2e28fdc8e Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 22 Nov 2016 17:53:26 -0800 Subject: Input: synaptics-rmi4 - add support for F55 sensor tuning Sensor tuning support is needed to determine the number of enabled tx and rx electrodes for use in F54 functions. The number of enabled electrodes is not identical to the total number of electrodes as reported with F55:Query0 and F55:Query1. It has to be calculated by analyzing F55:Ctrl1 (sensor receiver assignment) and F55:Ctrl2 (sensor transmitter assignment). Support for additional sensor tuning functions may be added later. Fixes: 3a762dbd5347 ("[media] Input: synaptics-rmi4 - add support for F54 ...") Signed-off-by: Guenter Roeck Tested-by: Nick Dyer Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/Kconfig | 9 +++ drivers/input/rmi4/Makefile | 1 + drivers/input/rmi4/rmi_bus.c | 3 + drivers/input/rmi4/rmi_driver.h | 1 + drivers/input/rmi4/rmi_f55.c | 124 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 138 insertions(+) create mode 100644 drivers/input/rmi4/rmi_f55.c (limited to 'drivers/input') diff --git a/drivers/input/rmi4/Kconfig b/drivers/input/rmi4/Kconfig index 9a24867ae7a5..f4460f3ebac5 100644 --- a/drivers/input/rmi4/Kconfig +++ b/drivers/input/rmi4/Kconfig @@ -95,3 +95,12 @@ config RMI4_F54 Function 54 provides access to various diagnostic features in certain RMI4 touch sensors. + +config RMI4_F55 + bool "RMI4 Function 55 (Sensor tuning)" + depends on RMI4_CORE + help + Say Y here if you want to add support for RMI4 function 55 + + Function 55 provides access to the RMI4 touch sensor tuning + mechanism. diff --git a/drivers/input/rmi4/Makefile b/drivers/input/rmi4/Makefile index 0250abf38ad9..e7f4ca6c0508 100644 --- a/drivers/input/rmi4/Makefile +++ b/drivers/input/rmi4/Makefile @@ -9,6 +9,7 @@ rmi_core-$(CONFIG_RMI4_F12) += rmi_f12.o rmi_core-$(CONFIG_RMI4_F30) += rmi_f30.o rmi_core-$(CONFIG_RMI4_F34) += rmi_f34.o rmi_core-$(CONFIG_RMI4_F54) += rmi_f54.o +rmi_core-$(CONFIG_RMI4_F55) += rmi_f55.o # Transports obj-$(CONFIG_RMI4_I2C) += rmi_i2c.o diff --git a/drivers/input/rmi4/rmi_bus.c b/drivers/input/rmi4/rmi_bus.c index ef7a66230d83..2534331ad9dc 100644 --- a/drivers/input/rmi4/rmi_bus.c +++ b/drivers/input/rmi4/rmi_bus.c @@ -321,6 +321,9 @@ static struct rmi_function_handler *fn_handlers[] = { #ifdef CONFIG_RMI4_F54 &rmi_f54_handler, #endif +#ifdef CONFIG_RMI4_F55 + &rmi_f55_handler, +#endif }; static void __rmi_unregister_function_handlers(int start_idx) diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h index e627a3a8aced..5b201f369505 100644 --- a/drivers/input/rmi4/rmi_driver.h +++ b/drivers/input/rmi4/rmi_driver.h @@ -127,4 +127,5 @@ extern struct rmi_function_handler rmi_f12_handler; extern struct rmi_function_handler rmi_f30_handler; extern struct rmi_function_handler rmi_f34_handler; extern struct rmi_function_handler rmi_f54_handler; +extern struct rmi_function_handler rmi_f55_handler; #endif diff --git a/drivers/input/rmi4/rmi_f55.c b/drivers/input/rmi4/rmi_f55.c new file mode 100644 index 000000000000..2d221cc97391 --- /dev/null +++ b/drivers/input/rmi4/rmi_f55.c @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2012-2015 Synaptics Incorporated + * Copyright (C) 2016 Zodiac Inflight Innovations + * + * 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 +#include +#include +#include +#include "rmi_driver.h" + +#define F55_NAME "rmi4_f55" + +/* F55 data offsets */ +#define F55_NUM_RX_OFFSET 0 +#define F55_NUM_TX_OFFSET 1 +#define F55_PHYS_CHAR_OFFSET 2 + +/* Only read required query registers */ +#define F55_QUERY_LEN 3 + +/* F55 capabilities */ +#define F55_CAP_SENSOR_ASSIGN BIT(0) + +struct f55_data { + struct rmi_function *fn; + + u8 qry[F55_QUERY_LEN]; + u8 num_rx_electrodes; + u8 cfg_num_rx_electrodes; + u8 num_tx_electrodes; + u8 cfg_num_tx_electrodes; +}; + +static int rmi_f55_detect(struct rmi_function *fn) +{ + struct f55_data *f55; + int error; + + f55 = dev_get_drvdata(&fn->dev); + + error = rmi_read_block(fn->rmi_dev, fn->fd.query_base_addr, + &f55->qry, sizeof(f55->qry)); + if (error) { + dev_err(&fn->dev, "%s: Failed to query F55 properties\n", + __func__); + return error; + } + + f55->num_rx_electrodes = f55->qry[F55_NUM_RX_OFFSET]; + f55->num_tx_electrodes = f55->qry[F55_NUM_TX_OFFSET]; + + f55->cfg_num_rx_electrodes = f55->num_rx_electrodes; + f55->cfg_num_tx_electrodes = f55->num_rx_electrodes; + + if (f55->qry[F55_PHYS_CHAR_OFFSET] & F55_CAP_SENSOR_ASSIGN) { + int i, total; + u8 buf[256]; + + /* + * Calculate the number of enabled receive and transmit + * electrodes by reading F55:Ctrl1 (sensor receiver assignment) + * and F55:Ctrl2 (sensor transmitter assignment). The number of + * enabled electrodes is the sum of all field entries with a + * value other than 0xff. + */ + error = rmi_read_block(fn->rmi_dev, + fn->fd.control_base_addr + 1, + buf, f55->num_rx_electrodes); + if (!error) { + total = 0; + for (i = 0; i < f55->num_rx_electrodes; i++) { + if (buf[i] != 0xff) + total++; + } + f55->cfg_num_rx_electrodes = total; + } + + error = rmi_read_block(fn->rmi_dev, + fn->fd.control_base_addr + 2, + buf, f55->num_tx_electrodes); + if (!error) { + total = 0; + for (i = 0; i < f55->num_tx_electrodes; i++) { + if (buf[i] != 0xff) + total++; + } + f55->cfg_num_tx_electrodes = total; + } + } + + rmi_dbg(RMI_DEBUG_FN, &fn->dev, "F55 num_rx_electrodes: %d (raw %d)\n", + f55->cfg_num_rx_electrodes, f55->num_rx_electrodes); + rmi_dbg(RMI_DEBUG_FN, &fn->dev, "F55 num_tx_electrodes: %d (raw %d)\n", + f55->cfg_num_tx_electrodes, f55->num_tx_electrodes); + + return 0; +} + +static int rmi_f55_probe(struct rmi_function *fn) +{ + struct f55_data *f55; + + f55 = devm_kzalloc(&fn->dev, sizeof(struct f55_data), GFP_KERNEL); + if (!f55) + return -ENOMEM; + + f55->fn = fn; + dev_set_drvdata(&fn->dev, f55); + + return rmi_f55_detect(fn); +} + +struct rmi_function_handler rmi_f55_handler = { + .driver = { + .name = F55_NAME, + }, + .func = 0x55, + .probe = rmi_f55_probe, +}; -- cgit v1.2.3 From c762cc68b6a12eedebefc156ea4838e54804e2eb Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 22 Nov 2016 17:57:02 -0800 Subject: Input: synaptics-rmi4 - propagate correct number of rx and tx electrodes to F54 F54 diagnostics report functions provide data based on the number of enabled rx and tx electrodes, which is not identical to the number of electrodes reported with F54:Query0 and F54:Query1. Those values report the number of supported electrodes, not the number of enabled electrodes. The number of enabled electrodes can be determined by analyzing F55:Ctrl1 (sensor receiver assignment) and F55:Ctrl2 (sensor transmitter assignment). Propagate the number of enabled electrodes from F55 to F54 to avoid corrupted output if not all electrodes are enabled. Fixes: 3a762dbd5347 ("[media] Input: synaptics-rmi4 - add support for F54 ...") Signed-off-by: Guenter Roeck Tested-by: Nick Dyer Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/Kconfig | 1 + drivers/input/rmi4/rmi_f54.c | 14 ++++++++++---- drivers/input/rmi4/rmi_f55.c | 7 +++++++ 3 files changed, 18 insertions(+), 4 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/rmi4/Kconfig b/drivers/input/rmi4/Kconfig index f4460f3ebac5..a9c36a5fe708 100644 --- a/drivers/input/rmi4/Kconfig +++ b/drivers/input/rmi4/Kconfig @@ -90,6 +90,7 @@ config RMI4_F54 depends on RMI4_CORE depends on VIDEO_V4L2=y || (RMI4_CORE=m && VIDEO_V4L2=m) select VIDEOBUF2_VMALLOC + select RMI4_F55 help Say Y here if you want to add support for RMI4 function 54 diff --git a/drivers/input/rmi4/rmi_f54.c b/drivers/input/rmi4/rmi_f54.c index 2e934aef3d2a..dea63e2db3e6 100644 --- a/drivers/input/rmi4/rmi_f54.c +++ b/drivers/input/rmi4/rmi_f54.c @@ -217,8 +217,10 @@ unlock: static size_t rmi_f54_get_report_size(struct f54_data *f54) { - u8 rx = f54->num_rx_electrodes ? : f54->num_rx_electrodes; - u8 tx = f54->num_tx_electrodes ? : f54->num_tx_electrodes; + struct rmi_device *rmi_dev = f54->fn->rmi_dev; + struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev); + u8 rx = drv_data->num_rx_electrodes ? : f54->num_rx_electrodes; + u8 tx = drv_data->num_tx_electrodes ? : f54->num_tx_electrodes; size_t size; switch (rmi_f54_get_reptype(f54, f54->input)) { @@ -402,6 +404,10 @@ static int rmi_f54_vidioc_enum_input(struct file *file, void *priv, static int rmi_f54_set_input(struct f54_data *f54, unsigned int i) { + struct rmi_device *rmi_dev = f54->fn->rmi_dev; + struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev); + u8 rx = drv_data->num_rx_electrodes ? : f54->num_rx_electrodes; + u8 tx = drv_data->num_tx_electrodes ? : f54->num_tx_electrodes; struct v4l2_pix_format *f = &f54->format; enum rmi_f54_report_type reptype; int ret; @@ -416,8 +422,8 @@ static int rmi_f54_set_input(struct f54_data *f54, unsigned int i) f54->input = i; - f->width = f54->num_rx_electrodes; - f->height = f54->num_tx_electrodes; + f->width = rx; + f->height = tx; f->field = V4L2_FIELD_NONE; f->colorspace = V4L2_COLORSPACE_RAW; f->bytesperline = f->width * sizeof(u16); diff --git a/drivers/input/rmi4/rmi_f55.c b/drivers/input/rmi4/rmi_f55.c index 2d221cc97391..37390ca6a924 100644 --- a/drivers/input/rmi4/rmi_f55.c +++ b/drivers/input/rmi4/rmi_f55.c @@ -38,6 +38,8 @@ struct f55_data { static int rmi_f55_detect(struct rmi_function *fn) { + struct rmi_device *rmi_dev = fn->rmi_dev; + struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev); struct f55_data *f55; int error; @@ -57,6 +59,9 @@ static int rmi_f55_detect(struct rmi_function *fn) f55->cfg_num_rx_electrodes = f55->num_rx_electrodes; f55->cfg_num_tx_electrodes = f55->num_rx_electrodes; + drv_data->num_rx_electrodes = f55->cfg_num_rx_electrodes; + drv_data->num_tx_electrodes = f55->cfg_num_rx_electrodes; + if (f55->qry[F55_PHYS_CHAR_OFFSET] & F55_CAP_SENSOR_ASSIGN) { int i, total; u8 buf[256]; @@ -78,6 +83,7 @@ static int rmi_f55_detect(struct rmi_function *fn) total++; } f55->cfg_num_rx_electrodes = total; + drv_data->num_rx_electrodes = total; } error = rmi_read_block(fn->rmi_dev, @@ -90,6 +96,7 @@ static int rmi_f55_detect(struct rmi_function *fn) total++; } f55->cfg_num_tx_electrodes = total; + drv_data->num_tx_electrodes = total; } } -- cgit v1.2.3 From 0e2b4458e9180e0b0d65cadfa729faf84f9f0115 Mon Sep 17 00:00:00 2001 From: Marcos Paulo de Souza Date: Tue, 22 Nov 2016 18:03:35 -0800 Subject: Input: i8042 - fix typo from i8042_aux_close to i8042_port_close Signed-off-by: Marcos Paulo de Souza Signed-off-by: Dmitry Torokhov --- drivers/input/serio/i8042.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index 89abfdb539ac..62685a768913 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -387,7 +387,7 @@ static int i8042_aux_write(struct serio *serio, unsigned char c) /* - * i8042_aux_close attempts to clear AUX or KBD port state by disabling + * i8042_port_close attempts to clear AUX or KBD port state by disabling * and then re-enabling it. */ -- cgit v1.2.3 From be8e7a7ec876d509bc61af64b41b314a142aa262 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Fri, 25 Nov 2016 11:20:43 -0800 Subject: Input: soc_button_array - use gpio_is_valid() gpio_keys will later use gpio_is_valid(). To match the actual behavior, we should use it here too. Signed-off-by: Benjamin Tissoires Signed-off-by: Dmitry Torokhov --- drivers/input/misc/soc_button_array.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/misc/soc_button_array.c b/drivers/input/misc/soc_button_array.c index c14b82709b0f..bbd433c4a5aa 100644 --- a/drivers/input/misc/soc_button_array.c +++ b/drivers/input/misc/soc_button_array.c @@ -17,6 +17,7 @@ #include #include #include +#include #include /* @@ -92,7 +93,7 @@ soc_button_device_create(struct platform_device *pdev, continue; gpio = soc_button_lookup_gpio(&pdev->dev, info->acpi_index); - if (gpio < 0) + if (!gpio_is_valid(gpio)) continue; gpio_keys[n_buttons].type = info->event_type; -- cgit v1.2.3 From 3d5a9437a68a184fbbd0dcf09bf4daf40250505e Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Fri, 25 Nov 2016 11:36:01 -0800 Subject: Input: soc_button_array - bail out earlier if gpiod_count is zero The PNP0C40 device of the Surface 3 doesn't have any GPIO attached to it. Instead of trying to access the GPIO, request the count beforehand and bail out if it is null or if an error is returned. Signed-off-by: Benjamin Tissoires Signed-off-by: Dmitry Torokhov --- drivers/input/misc/soc_button_array.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/misc/soc_button_array.c b/drivers/input/misc/soc_button_array.c index bbd433c4a5aa..908b51089dee 100644 --- a/drivers/input/misc/soc_button_array.c +++ b/drivers/input/misc/soc_button_array.c @@ -167,6 +167,11 @@ static int soc_button_probe(struct platform_device *pdev) button_info = (struct soc_button_info *)id->driver_data; + if (gpiod_count(&pdev->dev, KBUILD_MODNAME) <= 0) { + dev_dbg(&pdev->dev, "no GPIO attached, ignoring...\n"); + return -ENODEV; + } + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; -- cgit v1.2.3 From 599b8c09d974d6e4d85a8f7bc8ed7442977866a8 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Sun, 27 Nov 2016 20:37:31 -0800 Subject: Input: xpad - add product ID for Xbox One S pad This is the new gamepad that ships with the Xbox One S which includes Bluetooth functionality. Signed-off-by: Cameron Gutman Signed-off-by: Dmitry Torokhov --- drivers/input/joystick/xpad.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/input') diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index 83af17ad0f1f..19886dbfe18d 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -134,6 +134,7 @@ static const struct xpad_device { { 0x045e, 0x02d1, "Microsoft X-Box One pad", 0, XTYPE_XBOXONE }, { 0x045e, 0x02dd, "Microsoft X-Box One pad (Firmware 2015)", 0, XTYPE_XBOXONE }, { 0x045e, 0x02e3, "Microsoft X-Box One Elite pad", 0, XTYPE_XBOXONE }, + { 0x045e, 0x02ea, "Microsoft X-Box One S pad", 0, XTYPE_XBOXONE }, { 0x045e, 0x0291, "Xbox 360 Wireless Receiver (XBOX)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360W }, { 0x045e, 0x0719, "Xbox 360 Wireless Receiver", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360W }, { 0x044f, 0x0f07, "Thrustmaster, Inc. Controller", 0, XTYPE_XBOX }, -- cgit v1.2.3 From ae3b4469dbcd3b842a9fd20940946e4d092d8731 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Sun, 27 Nov 2016 20:37:56 -0800 Subject: Input: xpad - fix Xbox One rumble stopping after 2.5 secs Unlike previous Xbox pads, the Xbox One pad doesn't have "sticky" rumble packets. The duration is encoded into the command and expiration is handled by the pad firmware. ff-memless needs pseudo-sticky behavior for rumble effects to behave properly for long duration effects. We already specify the maximum rumble on duration in the command packets, but it's still only good for about 2.5 seconds of rumble. This is easily reproducible running fftest's sine vibration test. It turns out there's a repeat count encoded in the rumble command. We can abuse that to get the pseudo-sticky behavior needed for rumble to behave as expected for effects with long duration. By my math, this change should allow a single ff_effect to rumble for 10 minutes straight, which should be more than enough for most needs. Signed-off-by: Cameron Gutman Signed-off-by: Dmitry Torokhov --- drivers/input/joystick/xpad.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index 19886dbfe18d..6d9499658671 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -1045,9 +1045,9 @@ static int xpad_play_effect(struct input_dev *dev, void *data, struct ff_effect packet->data[7] = 0x00; packet->data[8] = strong / 512; /* left actuator */ packet->data[9] = weak / 512; /* right actuator */ - packet->data[10] = 0xFF; - packet->data[11] = 0x00; - packet->data[12] = 0x00; + packet->data[10] = 0xFF; /* on period */ + packet->data[11] = 0x00; /* off period */ + packet->data[12] = 0xFF; /* repeat count */ packet->len = 13; packet->pending = true; break; -- cgit v1.2.3 From 031bfed2aba8a727fe5a6c2169d5e4e0751a1bf0 Mon Sep 17 00:00:00 2001 From: Guy Shapiro Date: Sun, 27 Nov 2016 20:40:39 -0800 Subject: Input: imx6ul_tsc - add support for sample averaging The i.MX6UL internal touchscreen controller contains an option to average upon samples. This feature reduces noise from the produced touch locations. This patch adds sample averaging support to the imx6ul_tsc device driver. Signed-off-by: Guy Shapiro Reviewed-by: Fabio Estevam Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/imx6ul_tsc.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/imx6ul_tsc.c b/drivers/input/touchscreen/imx6ul_tsc.c index 8275267eac25..00b336f37fb3 100644 --- a/drivers/input/touchscreen/imx6ul_tsc.c +++ b/drivers/input/touchscreen/imx6ul_tsc.c @@ -25,6 +25,7 @@ /* ADC configuration registers field define */ #define ADC_AIEN (0x1 << 7) #define ADC_CONV_DISABLE 0x1F +#define ADC_AVGE (0x1 << 5) #define ADC_CAL (0x1 << 7) #define ADC_CALF 0x2 #define ADC_12BIT_MODE (0x2 << 2) @@ -32,6 +33,7 @@ #define ADC_CLK_DIV_8 (0x03 << 5) #define ADC_SHORT_SAMPLE_MODE (0x0 << 4) #define ADC_HARDWARE_TRIGGER (0x1 << 13) +#define ADC_AVGS_SHIFT 14 #define SELECT_CHANNEL_4 0x04 #define SELECT_CHANNEL_1 0x01 #define DISABLE_CONVERSION_INT (0x0 << 7) @@ -86,6 +88,7 @@ struct imx6ul_tsc { int measure_delay_time; int pre_charge_time; + int average_samples; struct completion completion; }; @@ -107,6 +110,8 @@ static int imx6ul_adc_init(struct imx6ul_tsc *tsc) 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; + if (tsc->average_samples) + adc_cfg |= (tsc->average_samples - 1) << ADC_AVGS_SHIFT; adc_cfg &= ~ADC_HARDWARE_TRIGGER; writel(adc_cfg, tsc->adc_regs + REG_ADC_CFG); @@ -118,6 +123,8 @@ static int imx6ul_adc_init(struct imx6ul_tsc *tsc) /* start ADC calibration */ adc_gc = readl(tsc->adc_regs + REG_ADC_GC); adc_gc |= ADC_CAL; + if (tsc->average_samples) + adc_gc |= ADC_AVGE; writel(adc_gc, tsc->adc_regs + REG_ADC_GC); timeout = wait_for_completion_timeout @@ -450,6 +457,17 @@ static int imx6ul_tsc_probe(struct platform_device *pdev) if (err) tsc->pre_charge_time = 0xfff; + err = of_property_read_u32(np, "average-samples", + &tsc->average_samples); + if (err) + tsc->average_samples = 0; + + if (tsc->average_samples > 4) { + dev_err(&pdev->dev, "average-samples (%u) must be [0-4]\n", + tsc->average_samples); + return -EINVAL; + } + err = input_register_device(tsc->input); if (err) { dev_err(&pdev->dev, -- cgit v1.2.3 From 864db9295b06837d11a260e5dacf99a3fdf6bce2 Mon Sep 17 00:00:00 2001 From: Paul Donohue Date: Mon, 28 Nov 2016 20:11:25 -0800 Subject: Input: ALPS - fix TrackStick support for SS5 hardware MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current Alps SS5 (SS4 v2) code generates bogus TouchPad events when TrackStick packets are processed. This causes the xorg synaptics driver to print "unable to find touch point 0" and "BUG: triggered 'if (priv->num_active_touches > priv->num_slots)'" messages. It also causes unexpected TouchPad button release and re-click event sequences if the TrackStick is moved while holding a TouchPad button. This commit corrects the problem by adjusting alps_process_packet_ss4_v2() so that it only sends TrackStick reports when processing TrackStick packets. Reviewed-by: Pali Rohár Signed-off-by: Paul Donohue Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/alps.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index 6d7de9bfed9a..b93fe83a0b63 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -1346,6 +1346,18 @@ static void alps_process_packet_ss4_v2(struct psmouse *psmouse) priv->multi_packet = 0; + /* Report trackstick */ + if (alps_get_pkt_id_ss4_v2(packet) == SS4_PACKET_ID_STICK) { + if (priv->flags & ALPS_DUALPOINT) { + input_report_key(dev2, BTN_LEFT, f->ts_left); + input_report_key(dev2, BTN_RIGHT, f->ts_right); + input_report_key(dev2, BTN_MIDDLE, f->ts_middle); + input_sync(dev2); + } + return; + } + + /* Report touchpad */ alps_report_mt_data(psmouse, (f->fingers <= 4) ? f->fingers : 4); input_mt_report_finger_count(dev, f->fingers); @@ -1356,13 +1368,6 @@ static void alps_process_packet_ss4_v2(struct psmouse *psmouse) input_report_abs(dev, ABS_PRESSURE, f->pressure); input_sync(dev); - - if (priv->flags & ALPS_DUALPOINT) { - input_report_key(dev2, BTN_LEFT, f->ts_left); - input_report_key(dev2, BTN_RIGHT, f->ts_right); - input_report_key(dev2, BTN_MIDDLE, f->ts_middle); - input_sync(dev2); - } } static bool alps_is_valid_package_ss4_v2(struct psmouse *psmouse) -- cgit v1.2.3 From 7229c58c096ca08576cbcbf6669bfbdcae0b5d22 Mon Sep 17 00:00:00 2001 From: Paul Donohue Date: Mon, 28 Nov 2016 20:13:47 -0800 Subject: Input: ALPS - clean up TrackStick handling for SS5 hardware MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For consistency and clarity, the input_report_*() functions should be called by alps_process_packet_ss4_v2() instead of by alps_decode_ss4_v2(). Reviewed-by: Pali Rohár Signed-off-by: Paul Donohue Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/alps.c | 42 +++++++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 17 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index b93fe83a0b63..61d61cc24fd2 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -1267,18 +1267,12 @@ static int alps_decode_ss4_v2(struct alps_fields *f, break; case SS4_PACKET_ID_STICK: - if (!(priv->flags & ALPS_DUALPOINT)) { - psmouse_warn(psmouse, - "Rejected trackstick packet from non DualPoint device"); - } else { - int x = (s8)(((p[0] & 1) << 7) | (p[1] & 0x7f)); - int y = (s8)(((p[3] & 1) << 7) | (p[2] & 0x7f)); - int pressure = (s8)(p[4] & 0x7f); - - input_report_rel(priv->dev2, REL_X, x); - input_report_rel(priv->dev2, REL_Y, -y); - input_report_abs(priv->dev2, ABS_PRESSURE, pressure); - } + /* + * x, y, and pressure are decoded in + * alps_process_packet_ss4_v2() + */ + f->first_mp = 0; + f->is_mp = 0; break; case SS4_PACKET_ID_IDLE: @@ -1312,6 +1306,7 @@ static void alps_process_packet_ss4_v2(struct psmouse *psmouse) struct input_dev *dev = psmouse->dev; struct input_dev *dev2 = priv->dev2; struct alps_fields *f = &priv->f; + int x, y, pressure; memset(f, 0, sizeof(struct alps_fields)); priv->decode_fields(f, packet, psmouse); @@ -1348,12 +1343,25 @@ static void alps_process_packet_ss4_v2(struct psmouse *psmouse) /* Report trackstick */ if (alps_get_pkt_id_ss4_v2(packet) == SS4_PACKET_ID_STICK) { - if (priv->flags & ALPS_DUALPOINT) { - input_report_key(dev2, BTN_LEFT, f->ts_left); - input_report_key(dev2, BTN_RIGHT, f->ts_right); - input_report_key(dev2, BTN_MIDDLE, f->ts_middle); - input_sync(dev2); + if (!(priv->flags & ALPS_DUALPOINT)) { + psmouse_warn(psmouse, + "Rejected trackstick packet from non DualPoint device"); + return; } + + x = (s8)(((packet[0] & 1) << 7) | (packet[1] & 0x7f)); + y = (s8)(((packet[3] & 1) << 7) | (packet[2] & 0x7f)); + pressure = (s8)(packet[4] & 0x7f); + + input_report_rel(dev2, REL_X, x); + input_report_rel(dev2, REL_Y, -y); + input_report_abs(dev2, ABS_PRESSURE, pressure); + + input_report_key(dev2, BTN_LEFT, f->ts_left); + input_report_key(dev2, BTN_RIGHT, f->ts_right); + input_report_key(dev2, BTN_MIDDLE, f->ts_middle); + + input_sync(dev2); return; } -- cgit v1.2.3 From 23fce365c6a26e40d459ca97289dd18543fb6845 Mon Sep 17 00:00:00 2001 From: Paul Donohue Date: Mon, 28 Nov 2016 20:16:21 -0800 Subject: Input: ALPS - clean up code for SS5 hardware MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The return value of alps_get_pkt_id_ss4_v2() should really be "enum SS4_PACKET_ID", not "unsigned char". Correct this. Also, most of the Alps SS5 (SS4 v2) packet byte parsing code is implemented using macros, but there are a few places where bytes are directly manipulated in alps.c. For consistency, migrate the rest of these to macros. Reviewed-by: Pali Rohár Signed-off-by: Paul Donohue Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/alps.c | 21 +++++++-------------- drivers/input/mouse/alps.h | 22 +++++++++++++++++++++- 2 files changed, 28 insertions(+), 15 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index 61d61cc24fd2..328edc8c8786 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -1153,15 +1153,13 @@ static void alps_process_packet_v7(struct psmouse *psmouse) alps_process_touchpad_packet_v7(psmouse); } -static unsigned char alps_get_pkt_id_ss4_v2(unsigned char *byte) +static enum SS4_PACKET_ID alps_get_pkt_id_ss4_v2(unsigned char *byte) { - unsigned char pkt_id = SS4_PACKET_ID_IDLE; + enum SS4_PACKET_ID pkt_id = SS4_PACKET_ID_IDLE; switch (byte[3] & 0x30) { case 0x00: - if (byte[0] == 0x18 && byte[1] == 0x10 && byte[2] == 0x00 && - (byte[3] & 0x88) == 0x08 && byte[4] == 0x10 && - byte[5] == 0x00) { + if (SS4_IS_IDLE_V2(byte)) { pkt_id = SS4_PACKET_ID_IDLE; } else { pkt_id = SS4_PACKET_ID_ONE; @@ -1188,7 +1186,7 @@ static int alps_decode_ss4_v2(struct alps_fields *f, unsigned char *p, struct psmouse *psmouse) { struct alps_data *priv = psmouse->private; - unsigned char pkt_id; + enum SS4_PACKET_ID pkt_id; unsigned int no_data_x, no_data_y; pkt_id = alps_get_pkt_id_ss4_v2(p); @@ -1306,7 +1304,6 @@ static void alps_process_packet_ss4_v2(struct psmouse *psmouse) struct input_dev *dev = psmouse->dev; struct input_dev *dev2 = priv->dev2; struct alps_fields *f = &priv->f; - int x, y, pressure; memset(f, 0, sizeof(struct alps_fields)); priv->decode_fields(f, packet, psmouse); @@ -1349,13 +1346,9 @@ static void alps_process_packet_ss4_v2(struct psmouse *psmouse) return; } - x = (s8)(((packet[0] & 1) << 7) | (packet[1] & 0x7f)); - y = (s8)(((packet[3] & 1) << 7) | (packet[2] & 0x7f)); - pressure = (s8)(packet[4] & 0x7f); - - input_report_rel(dev2, REL_X, x); - input_report_rel(dev2, REL_Y, -y); - input_report_abs(dev2, ABS_PRESSURE, pressure); + input_report_rel(dev2, REL_X, SS4_TS_X_V2(packet)); + input_report_rel(dev2, REL_Y, SS4_TS_Y_V2(packet)); + input_report_abs(dev2, ABS_PRESSURE, SS4_TS_Z_V2(packet)); input_report_key(dev2, BTN_LEFT, f->ts_left); input_report_key(dev2, BTN_RIGHT, f->ts_right); diff --git a/drivers/input/mouse/alps.h b/drivers/input/mouse/alps.h index b9417e2d7ad3..72b9efc5217b 100644 --- a/drivers/input/mouse/alps.h +++ b/drivers/input/mouse/alps.h @@ -54,7 +54,15 @@ enum SS4_PACKET_ID { #define SS4_MASK_NORMAL_BUTTONS 0x07 -#define SS4_1F_X_V2(_b) ((_b[0] & 0x0007) | \ +#define SS4_IS_IDLE_V2(_b) (((_b[0]) == 0x18) && \ + ((_b[1]) == 0x10) && \ + ((_b[2]) == 0x00) && \ + ((_b[3] & 0x88) == 0x08) && \ + ((_b[4]) == 0x10) && \ + ((_b[5]) == 0x00) \ + ) + +#define SS4_1F_X_V2(_b) (((_b[0]) & 0x0007) | \ ((_b[1] << 3) & 0x0078) | \ ((_b[1] << 2) & 0x0380) | \ ((_b[2] << 5) & 0x1C00) \ @@ -101,6 +109,18 @@ enum SS4_PACKET_ID { #define SS4_IS_MF_CONTINUE(_b) ((_b[2] & 0x10) == 0x10) #define SS4_IS_5F_DETECTED(_b) ((_b[2] & 0x10) == 0x10) +#define SS4_TS_X_V2(_b) (s8)( \ + ((_b[0] & 0x01) << 7) | \ + (_b[1] & 0x7F) \ + ) + +#define SS4_TS_Y_V2(_b) (s8)( \ + ((_b[3] & 0x01) << 7) | \ + (_b[2] & 0x7F) \ + ) + +#define SS4_TS_Z_V2(_b) (s8)(_b[4] & 0x7F) + #define SS4_MFPACKET_NO_AX 8160 /* X-Coordinate value */ #define SS4_MFPACKET_NO_AY 4080 /* Y-Coordinate value */ -- cgit v1.2.3 From a64ea311f1e4bc090c89960650637423e86c35c0 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 29 Nov 2016 17:42:13 -0800 Subject: Input: synaptics-rmi4 - add rmi_enable/disable_irq Set the .enabled boolean and trigger an event processing when enabling for edge-triggered systems. Signed-off-by: Benjamin Tissoires Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/rmi_driver.c | 83 +++++++++++++++++++++++++++++++---------- drivers/input/rmi4/rmi_driver.h | 2 + 2 files changed, 66 insertions(+), 19 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c index 2b17d8cb3d10..f04fc4152c1f 100644 --- a/drivers/input/rmi4/rmi_driver.c +++ b/drivers/input/rmi4/rmi_driver.c @@ -215,6 +215,7 @@ static irqreturn_t rmi_irq_fn(int irq, void *dev_id) static int rmi_irq_init(struct rmi_device *rmi_dev) { struct rmi_device_platform_data *pdata = rmi_get_platform_data(rmi_dev); + struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev); int irq_flags = irq_get_trigger_type(pdata->irq); int ret; @@ -232,6 +233,8 @@ static int rmi_irq_init(struct rmi_device *rmi_dev) return ret; } + data->enabled = true; + return 0; } @@ -866,17 +869,54 @@ err_put_fn: return error; } -int rmi_driver_suspend(struct rmi_device *rmi_dev, bool enable_wake) +void rmi_enable_irq(struct rmi_device *rmi_dev, bool clear_wake) { struct rmi_device_platform_data *pdata = rmi_get_platform_data(rmi_dev); + struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev); int irq = pdata->irq; - int retval = 0; + int irq_flags; + int retval; - retval = rmi_suspend_functions(rmi_dev); - if (retval) - dev_warn(&rmi_dev->dev, "Failed to suspend functions: %d\n", - retval); + mutex_lock(&data->enabled_mutex); + + if (data->enabled) + goto out; + + enable_irq(irq); + data->enabled = true; + if (clear_wake && device_may_wakeup(rmi_dev->xport->dev)) { + retval = disable_irq_wake(irq); + if (!retval) + dev_warn(&rmi_dev->dev, + "Failed to disable irq for wake: %d\n", + retval); + } + + /* + * Call rmi_process_interrupt_requests() after enabling irq, + * otherwise we may lose interrupt on edge-triggered systems. + */ + irq_flags = irq_get_trigger_type(pdata->irq); + if (irq_flags & IRQ_TYPE_EDGE_BOTH) + rmi_process_interrupt_requests(rmi_dev); + +out: + mutex_unlock(&data->enabled_mutex); +} + +void rmi_disable_irq(struct rmi_device *rmi_dev, bool enable_wake) +{ + struct rmi_device_platform_data *pdata = rmi_get_platform_data(rmi_dev); + struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev); + int irq = pdata->irq; + int retval; + + mutex_lock(&data->enabled_mutex); + + if (!data->enabled) + goto out; + data->enabled = false; disable_irq(irq); if (enable_wake && device_may_wakeup(rmi_dev->xport->dev)) { retval = enable_irq_wake(irq); @@ -885,24 +925,30 @@ int rmi_driver_suspend(struct rmi_device *rmi_dev, bool enable_wake) "Failed to enable irq for wake: %d\n", retval); } + +out: + mutex_unlock(&data->enabled_mutex); +} + +int rmi_driver_suspend(struct rmi_device *rmi_dev, bool enable_wake) +{ + int retval; + + retval = rmi_suspend_functions(rmi_dev); + if (retval) + dev_warn(&rmi_dev->dev, "Failed to suspend functions: %d\n", + retval); + + rmi_disable_irq(rmi_dev, enable_wake); return retval; } EXPORT_SYMBOL_GPL(rmi_driver_suspend); int rmi_driver_resume(struct rmi_device *rmi_dev, bool clear_wake) { - struct rmi_device_platform_data *pdata = rmi_get_platform_data(rmi_dev); - int irq = pdata->irq; int retval; - enable_irq(irq); - if (clear_wake && device_may_wakeup(rmi_dev->xport->dev)) { - retval = disable_irq_wake(irq); - if (!retval) - dev_warn(&rmi_dev->dev, - "Failed to disable irq for wake: %d\n", - retval); - } + rmi_enable_irq(rmi_dev, clear_wake); retval = rmi_resume_functions(rmi_dev); if (retval) @@ -916,10 +962,8 @@ EXPORT_SYMBOL_GPL(rmi_driver_resume); static int rmi_driver_remove(struct device *dev) { struct rmi_device *rmi_dev = to_rmi_device(dev); - struct rmi_device_platform_data *pdata = rmi_get_platform_data(rmi_dev); - int irq = pdata->irq; - disable_irq(irq); + rmi_disable_irq(rmi_dev, false); rmi_f34_remove_sysfs(rmi_dev); rmi_free_function_list(rmi_dev); @@ -1108,6 +1152,7 @@ static int rmi_driver_probe(struct device *dev) } mutex_init(&data->irq_mutex); + mutex_init(&data->enabled_mutex); retval = rmi_probe_interrupts(data); if (retval) diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h index 5b201f369505..c9fe3d3deef3 100644 --- a/drivers/input/rmi4/rmi_driver.h +++ b/drivers/input/rmi4/rmi_driver.h @@ -101,6 +101,8 @@ int rmi_scan_pdt(struct rmi_device *rmi_dev, void *ctx, int (*callback)(struct rmi_device *rmi_dev, void *ctx, const struct pdt_entry *entry)); int rmi_probe_interrupts(struct rmi_driver_data *data); +void rmi_enable_irq(struct rmi_device *rmi_dev, bool clear_wake); +void rmi_disable_irq(struct rmi_device *rmi_dev, bool enable_wake); int rmi_init_functions(struct rmi_driver_data *data); int rmi_initial_reset(struct rmi_device *rmi_dev, void *ctx, const struct pdt_entry *pdt); -- cgit v1.2.3 From 037b3af6bf84a404a8c6dbe0a7dd551f1b95b530 Mon Sep 17 00:00:00 2001 From: Florian Vaussard Date: Tue, 29 Nov 2016 17:38:58 -0800 Subject: Input: drv2665 - fix misuse of regmap_update_bits Using regmap_update_bits(..., mask, 1) with 'mask' following (1 << k) and k greater than 0 is wrong. Indeed, _regmap_update_bits will perform (mask & 1), which results in 0 if LSB of mask is 0. Thus the call regmap_update_bits(..., mask, 1) is in reality equivalent to regmap_update_bits(..., mask, 0). In such a case, the correct use is regmap_update_bits(..., mask, mask). This driver is performing such a mistake with the DRV2665_STANDBY mask, which equals BIT(6). Fix the driver to make it consistent with the API, and fix the alignment problem at the same time. Please note that this change is untested, as I do not have this piece of hardware. Testers are welcome! Signed-off-by: Florian Vaussard Signed-off-by: Dmitry Torokhov --- drivers/input/misc/drv2665.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/misc/drv2665.c b/drivers/input/misc/drv2665.c index ef9bc12b3be3..dcb6d8e94b11 100644 --- a/drivers/input/misc/drv2665.c +++ b/drivers/input/misc/drv2665.c @@ -125,8 +125,8 @@ static void drv2665_close(struct input_dev *input) cancel_work_sync(&haptics->work); - error = regmap_update_bits(haptics->regmap, - DRV2665_CTRL_2, DRV2665_STANDBY, 1); + error = regmap_update_bits(haptics->regmap, DRV2665_CTRL_2, + DRV2665_STANDBY, DRV2665_STANDBY); if (error) dev_err(&haptics->client->dev, "Failed to enter standby mode: %d\n", error); @@ -240,7 +240,7 @@ static int __maybe_unused drv2665_suspend(struct device *dev) if (haptics->input_dev->users) { ret = regmap_update_bits(haptics->regmap, DRV2665_CTRL_2, - DRV2665_STANDBY, 1); + DRV2665_STANDBY, DRV2665_STANDBY); if (ret) { dev_err(dev, "Failed to set standby mode\n"); regulator_disable(haptics->regulator); -- cgit v1.2.3 From 6473bbfd7e85a9f834ed668b4daa31638c73ff53 Mon Sep 17 00:00:00 2001 From: Florian Vaussard Date: Tue, 29 Nov 2016 17:39:25 -0800 Subject: Input: drv2667 - fix misuse of regmap_update_bits Using regmap_update_bits(..., mask, 1) with 'mask' following (1 << k) and k greater than 0 is wrong. Indeed, _regmap_update_bits will perform (mask & 1), which results in 0 if LSB of mask is 0. Thus the call regmap_update_bits(..., mask, 1) is in reality equivalent to regmap_update_bits(..., mask, 0). In such a case, the correct use is regmap_update_bits(..., mask, mask). This driver is performing such a mistake with the DRV2667_STANDBY mask, which equals (1 << 6). Fix the driver to make it consistent with the API, and fix the alignment problem at the same time. Please note that this change is untested, as I do not have this piece of hardware. Testers are welcome! Signed-off-by: Florian Vaussard Signed-off-by: Dmitry Torokhov --- drivers/input/misc/drv2667.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/misc/drv2667.c b/drivers/input/misc/drv2667.c index d5ba7481328c..2849bb6906a8 100644 --- a/drivers/input/misc/drv2667.c +++ b/drivers/input/misc/drv2667.c @@ -256,7 +256,7 @@ static void drv2667_close(struct input_dev *input) cancel_work_sync(&haptics->work); error = regmap_update_bits(haptics->regmap, DRV2667_CTRL_2, - DRV2667_STANDBY, 1); + DRV2667_STANDBY, DRV2667_STANDBY); if (error) dev_err(&haptics->client->dev, "Failed to enter standby mode: %d\n", error); @@ -415,7 +415,7 @@ static int __maybe_unused drv2667_suspend(struct device *dev) if (haptics->input_dev->users) { ret = regmap_update_bits(haptics->regmap, DRV2667_CTRL_2, - DRV2667_STANDBY, 1); + DRV2667_STANDBY, DRV2667_STANDBY); if (ret) { dev_err(dev, "Failed to set standby mode\n"); regulator_disable(haptics->regulator); -- cgit v1.2.3 From e155d4ee0b878c857f91deb14d6d7dba55c38648 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 30 Nov 2016 16:59:30 -0800 Subject: Input: synaptics-rmi4 - remove mutex calls while updating the firmware This partially reverts commit 29fd0ec2bdbe ("Input: synaptics-rmi4 - add support for F34 device reflash") irq_mutex should be used only to protect data->current_irq_mask, not preventing incoming input to be processed while the upgrade of the firmware is happening. We can simply disable the irqs when we don't want them to interfere with the upgrade process. Tested on S7300 and S7800 (with F34 v7 patch added) Signed-off-by: Benjamin Tissoires Signed-off-by: Nick Dyer Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/rmi_driver.c | 40 ++++++++-------------------------------- drivers/input/rmi4/rmi_f34.c | 19 ++++++++++++++----- 2 files changed, 22 insertions(+), 37 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c index f04fc4152c1f..27c731ab71b8 100644 --- a/drivers/input/rmi4/rmi_driver.c +++ b/drivers/input/rmi4/rmi_driver.c @@ -42,8 +42,6 @@ void rmi_free_function_list(struct rmi_device *rmi_dev) rmi_dbg(RMI_DEBUG_CORE, &rmi_dev->dev, "Freeing function list\n"); - mutex_lock(&data->irq_mutex); - devm_kfree(&rmi_dev->dev, data->irq_memory); data->irq_memory = NULL; data->irq_status = NULL; @@ -60,8 +58,6 @@ void rmi_free_function_list(struct rmi_device *rmi_dev) list_del(&fn->node); rmi_unregister_function(fn); } - - mutex_unlock(&data->irq_mutex); } EXPORT_SYMBOL_GPL(rmi_free_function_list); @@ -160,25 +156,24 @@ static int rmi_process_interrupt_requests(struct rmi_device *rmi_dev) if (!data) return 0; - mutex_lock(&data->irq_mutex); - if (!data->irq_status || !data->f01_container) { - mutex_unlock(&data->irq_mutex); - return 0; - } - if (!rmi_dev->xport->attn_data) { error = rmi_read_block(rmi_dev, data->f01_container->fd.data_base_addr + 1, data->irq_status, data->num_of_irq_regs); if (error < 0) { dev_err(dev, "Failed to read irqs, code=%d\n", error); - mutex_unlock(&data->irq_mutex); return error; } } + mutex_lock(&data->irq_mutex); bitmap_and(data->irq_status, data->irq_status, data->current_irq_mask, data->irq_count); + /* + * At this point, irq_status has all bits that are set in the + * interrupt status register and are enabled. + */ + mutex_unlock(&data->irq_mutex); /* * It would be nice to be able to use irq_chip to handle these @@ -194,8 +189,6 @@ static int rmi_process_interrupt_requests(struct rmi_device *rmi_dev) if (data->input) input_sync(data->input); - mutex_unlock(&data->irq_mutex); - return 0; } @@ -263,18 +256,12 @@ static int rmi_suspend_functions(struct rmi_device *rmi_dev) struct rmi_function *entry; int retval; - mutex_lock(&data->irq_mutex); - list_for_each_entry(entry, &data->function_list, node) { retval = suspend_one_function(entry); - if (retval < 0) { - mutex_unlock(&data->irq_mutex); + if (retval < 0) return retval; - } } - mutex_unlock(&data->irq_mutex); - return 0; } @@ -303,18 +290,12 @@ static int rmi_resume_functions(struct rmi_device *rmi_dev) struct rmi_function *entry; int retval; - mutex_lock(&data->irq_mutex); - list_for_each_entry(entry, &data->function_list, node) { retval = resume_one_function(entry); - if (retval < 0) { - mutex_unlock(&data->irq_mutex); + if (retval < 0) return retval; - } } - mutex_unlock(&data->irq_mutex); - return 0; } @@ -1043,8 +1024,6 @@ int rmi_init_functions(struct rmi_driver_data *data) int irq_count; int retval; - mutex_lock(&data->irq_mutex); - irq_count = 0; rmi_dbg(RMI_DEBUG_CORE, dev, "%s: Creating functions.\n", __func__); retval = rmi_scan_pdt(rmi_dev, &irq_count, rmi_create_function); @@ -1069,13 +1048,10 @@ int rmi_init_functions(struct rmi_driver_data *data) goto err_destroy_functions; } - mutex_unlock(&data->irq_mutex); - return 0; err_destroy_functions: rmi_free_function_list(rmi_dev); - mutex_unlock(&data->irq_mutex); return retval; } EXPORT_SYMBOL_GPL(rmi_init_functions); diff --git a/drivers/input/rmi4/rmi_f34.c b/drivers/input/rmi4/rmi_f34.c index 03df85ac91a5..01936a4a9a6c 100644 --- a/drivers/input/rmi4/rmi_f34.c +++ b/drivers/input/rmi4/rmi_f34.c @@ -282,7 +282,8 @@ out: static int rmi_firmware_update(struct rmi_driver_data *data, const struct firmware *fw) { - struct device *dev = &data->rmi_dev->dev; + struct rmi_device *rmi_dev = data->rmi_dev; + struct device *dev = &rmi_dev->dev; struct f34_data *f34; int ret; @@ -305,8 +306,10 @@ static int rmi_firmware_update(struct rmi_driver_data *data, if (ret) return ret; + rmi_disable_irq(rmi_dev, false); + /* Tear down functions and re-probe */ - rmi_free_function_list(data->rmi_dev); + rmi_free_function_list(rmi_dev); ret = rmi_probe_interrupts(data); if (ret) @@ -322,6 +325,8 @@ static int rmi_firmware_update(struct rmi_driver_data *data, return -EINVAL; } + rmi_enable_irq(rmi_dev, false); + f34 = dev_get_drvdata(&data->f34_container->dev); /* Perform firmware update */ @@ -329,11 +334,13 @@ static int rmi_firmware_update(struct rmi_driver_data *data, dev_info(&f34->fn->dev, "Firmware update complete, status:%d\n", ret); + rmi_disable_irq(rmi_dev, false); + /* Re-probe */ rmi_dbg(RMI_DEBUG_FN, dev, "Re-probing device\n"); - rmi_free_function_list(data->rmi_dev); + rmi_free_function_list(rmi_dev); - ret = rmi_scan_pdt(data->rmi_dev, NULL, rmi_initial_reset); + ret = rmi_scan_pdt(rmi_dev, NULL, rmi_initial_reset); if (ret < 0) dev_warn(dev, "RMI reset failed!\n"); @@ -345,9 +352,11 @@ static int rmi_firmware_update(struct rmi_driver_data *data, if (ret) return ret; + rmi_enable_irq(rmi_dev, false); + if (data->f01_container->dev.driver) /* Driver already bound, so enable ATTN now. */ - return rmi_enable_sensor(data->rmi_dev); + return rmi_enable_sensor(rmi_dev); rmi_dbg(RMI_DEBUG_FN, dev, "%s complete\n", __func__); -- cgit v1.2.3 From e9dade4106e1c1ff4ae91386b9812ee8f51c3775 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 30 Nov 2016 17:00:28 -0800 Subject: Input: synaptics-rmi4 - remove EXPORT_SYMBOL_GPL for internal functions those functions should not be used outside of rmi_core.ko. There is no point in exporting them to the world. It looks like rmi_read_pdt_entry() should be static too. Signed-off-by: Benjamin Tissoires Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/rmi_driver.c | 15 ++------------- drivers/input/rmi4/rmi_driver.h | 3 --- 2 files changed, 2 insertions(+), 16 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c index 27c731ab71b8..a718e51afb0b 100644 --- a/drivers/input/rmi4/rmi_driver.c +++ b/drivers/input/rmi4/rmi_driver.c @@ -59,7 +59,6 @@ void rmi_free_function_list(struct rmi_device *rmi_dev) rmi_unregister_function(fn); } } -EXPORT_SYMBOL_GPL(rmi_free_function_list); static int reset_one_function(struct rmi_function *fn) { @@ -309,7 +308,6 @@ int rmi_enable_sensor(struct rmi_device *rmi_dev) return rmi_process_interrupt_requests(rmi_dev); } -EXPORT_SYMBOL_GPL(rmi_enable_sensor); /** * rmi_driver_set_input_params - set input device id and other data. @@ -431,8 +429,8 @@ static int rmi_driver_reset_handler(struct rmi_device *rmi_dev) return 0; } -int rmi_read_pdt_entry(struct rmi_device *rmi_dev, struct pdt_entry *entry, - u16 pdt_address) +static int rmi_read_pdt_entry(struct rmi_device *rmi_dev, + struct pdt_entry *entry, u16 pdt_address) { u8 buf[RMI_PDT_ENTRY_SIZE]; int error; @@ -455,7 +453,6 @@ int rmi_read_pdt_entry(struct rmi_device *rmi_dev, struct pdt_entry *entry, return 0; } -EXPORT_SYMBOL_GPL(rmi_read_pdt_entry); static void rmi_driver_copy_pdt_to_fd(const struct pdt_entry *pdt, struct rmi_function_descriptor *fd) @@ -532,7 +529,6 @@ int rmi_scan_pdt(struct rmi_device *rmi_dev, void *ctx, return retval < 0 ? retval : 0; } -EXPORT_SYMBOL_GPL(rmi_scan_pdt); int rmi_read_register_desc(struct rmi_device *d, u16 addr, struct rmi_register_descriptor *rdesc) @@ -664,7 +660,6 @@ free_struct_buff: kfree(struct_buf); return ret; } -EXPORT_SYMBOL_GPL(rmi_read_register_desc); const struct rmi_register_desc_item *rmi_get_register_desc_item( struct rmi_register_descriptor *rdesc, u16 reg) @@ -680,7 +675,6 @@ const struct rmi_register_desc_item *rmi_get_register_desc_item( return NULL; } -EXPORT_SYMBOL_GPL(rmi_get_register_desc_item); size_t rmi_register_desc_calc_size(struct rmi_register_descriptor *rdesc) { @@ -694,7 +688,6 @@ size_t rmi_register_desc_calc_size(struct rmi_register_descriptor *rdesc) } return size; } -EXPORT_SYMBOL_GPL(rmi_register_desc_calc_size); /* Compute the register offset relative to the base address */ int rmi_register_desc_calc_reg_offset( @@ -712,7 +705,6 @@ int rmi_register_desc_calc_reg_offset( } return -1; } -EXPORT_SYMBOL_GPL(rmi_register_desc_calc_reg_offset); bool rmi_register_desc_has_subpacket(const struct rmi_register_desc_item *item, u8 subpacket) @@ -796,7 +788,6 @@ int rmi_initial_reset(struct rmi_device *rmi_dev, void *ctx, /* F01 should always be on page 0. If we don't find it there, fail. */ return pdt->page_start == 0 ? RMI_SCAN_CONTINUE : -ENODEV; } -EXPORT_SYMBOL_GPL(rmi_initial_reset); static int rmi_create_function(struct rmi_device *rmi_dev, void *ctx, const struct pdt_entry *pdt) @@ -1015,7 +1006,6 @@ int rmi_probe_interrupts(struct rmi_driver_data *data) return retval; } -EXPORT_SYMBOL_GPL(rmi_probe_interrupts); int rmi_init_functions(struct rmi_driver_data *data) { @@ -1054,7 +1044,6 @@ err_destroy_functions: rmi_free_function_list(rmi_dev); return retval; } -EXPORT_SYMBOL_GPL(rmi_init_functions); static int rmi_driver_probe(struct device *dev) { diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h index c9fe3d3deef3..cc9458522279 100644 --- a/drivers/input/rmi4/rmi_driver.h +++ b/drivers/input/rmi4/rmi_driver.h @@ -51,9 +51,6 @@ struct pdt_entry { u8 function_number; }; -int rmi_read_pdt_entry(struct rmi_device *rmi_dev, struct pdt_entry *entry, - u16 pdt_address); - #define RMI_REG_DESC_PRESENSE_BITS (32 * BITS_PER_BYTE) #define RMI_REG_DESC_SUBPACKET_BITS (37 * BITS_PER_BYTE) -- cgit v1.2.3 From 0a135b88bceac40d0036e401c19cdbda65b38a8f Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 30 Nov 2016 17:01:50 -0800 Subject: Input: synaptics-rmi4 - have only one struct platform data If struct rmi_device_platform_data contains pointers to other struct, it gets difficult to allocate a fixed size struct and copy it over between drivers. Change the pointers into a struct and change the code in rmi4 accordingly. Reviewed-by: Andrew Duggan Signed-off-by: Benjamin Tissoires Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/rmi_f11.c | 4 ++-- drivers/input/rmi4/rmi_f12.c | 4 ++-- drivers/input/rmi4/rmi_f30.c | 9 ++++----- 3 files changed, 8 insertions(+), 9 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/rmi4/rmi_f11.c b/drivers/input/rmi4/rmi_f11.c index c252d405df4c..ffcbbc1745de 100644 --- a/drivers/input/rmi4/rmi_f11.c +++ b/drivers/input/rmi4/rmi_f11.c @@ -1080,8 +1080,8 @@ static int rmi_f11_initialize(struct rmi_function *fn) rc = rmi_2d_sensor_of_probe(&fn->dev, &f11->sensor_pdata); if (rc) return rc; - } else if (pdata->sensor_pdata) { - f11->sensor_pdata = *pdata->sensor_pdata; + } else { + f11->sensor_pdata = pdata->sensor_pdata; } f11->rezero_wait_ms = f11->sensor_pdata.rezero_wait; diff --git a/drivers/input/rmi4/rmi_f12.c b/drivers/input/rmi4/rmi_f12.c index d7255f18306d..82a4964e5eb9 100644 --- a/drivers/input/rmi4/rmi_f12.c +++ b/drivers/input/rmi4/rmi_f12.c @@ -352,8 +352,8 @@ static int rmi_f12_probe(struct rmi_function *fn) ret = rmi_2d_sensor_of_probe(&fn->dev, &f12->sensor_pdata); if (ret) return ret; - } else if (pdata->sensor_pdata) { - f12->sensor_pdata = *pdata->sensor_pdata; + } else { + f12->sensor_pdata = pdata->sensor_pdata; } ret = rmi_read_register_desc(rmi_dev, query_addr, diff --git a/drivers/input/rmi4/rmi_f30.c b/drivers/input/rmi4/rmi_f30.c index 485907ff10f4..f696137a56f5 100644 --- a/drivers/input/rmi4/rmi_f30.c +++ b/drivers/input/rmi4/rmi_f30.c @@ -196,7 +196,7 @@ static int rmi_f30_config(struct rmi_function *fn) rmi_get_platform_data(fn->rmi_dev); int error; - if (pdata->f30_data && pdata->f30_data->disable) { + if (pdata->f30_data.disable) { drv->clear_irq_bits(fn->rmi_dev, fn->irq_mask); } else { /* Write Control Register values back to device */ @@ -355,7 +355,7 @@ static inline int rmi_f30_initialize(struct rmi_function *fn) f30->gpioled_key_map = (u16 *)map_memory; pdata = rmi_get_platform_data(rmi_dev); - if (pdata && f30->has_gpio) { + if (f30->has_gpio) { button = BTN_LEFT; for (i = 0; i < f30->gpioled_count; i++) { if (rmi_f30_is_valid_button(i, f30->ctrl)) { @@ -366,8 +366,7 @@ static inline int rmi_f30_initialize(struct rmi_function *fn) * f30->has_mech_mouse_btns, but I am * not sure, so use only the pdata info */ - if (pdata->f30_data && - pdata->f30_data->buttonpad) + if (pdata->f30_data.buttonpad) break; } } @@ -382,7 +381,7 @@ static int rmi_f30_probe(struct rmi_function *fn) const struct rmi_device_platform_data *pdata = rmi_get_platform_data(fn->rmi_dev); - if (pdata->f30_data && pdata->f30_data->disable) + if (pdata->f30_data.disable) return 0; rc = rmi_f30_initialize(fn); -- cgit v1.2.3 From 70f5a294ad2eec3f896dafb83075cb8f04d65e92 Mon Sep 17 00:00:00 2001 From: Haibo Chen Date: Wed, 30 Nov 2016 09:02:06 -0800 Subject: Input: imx6ul_tsc - add mask when set REG_ADC_CFG Add mask of each function bits of REG_ADC_CFG, and clear these function bits first, otherwise use '|=' operation may get the wrong setting which depends on the original value of REG_ADC_CFG. Signed-off-by: Haibo Chen Reviewed-by: Guy Shapiro Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/imx6ul_tsc.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/imx6ul_tsc.c b/drivers/input/touchscreen/imx6ul_tsc.c index 00b336f37fb3..5babefef99a1 100644 --- a/drivers/input/touchscreen/imx6ul_tsc.c +++ b/drivers/input/touchscreen/imx6ul_tsc.c @@ -29,11 +29,16 @@ #define ADC_CAL (0x1 << 7) #define ADC_CALF 0x2 #define ADC_12BIT_MODE (0x2 << 2) +#define ADC_CONV_MODE_MASK (0x3 << 2) #define ADC_IPG_CLK 0x00 +#define ADC_INPUT_CLK_MASK 0x3 #define ADC_CLK_DIV_8 (0x03 << 5) +#define ADC_CLK_DIV_MASK (0x3 << 5) #define ADC_SHORT_SAMPLE_MODE (0x0 << 4) +#define ADC_SAMPLE_MODE_MASK (0x1 << 4) #define ADC_HARDWARE_TRIGGER (0x1 << 13) #define ADC_AVGS_SHIFT 14 +#define ADC_AVGS_MASK (0x3 << 14) #define SELECT_CHANNEL_4 0x04 #define SELECT_CHANNEL_1 0x01 #define DISABLE_CONVERSION_INT (0x0 << 7) @@ -108,10 +113,14 @@ static int imx6ul_adc_init(struct imx6ul_tsc *tsc) reinit_completion(&tsc->completion); adc_cfg = readl(tsc->adc_regs + REG_ADC_CFG); + adc_cfg &= ~(ADC_CONV_MODE_MASK | ADC_INPUT_CLK_MASK); adc_cfg |= ADC_12BIT_MODE | ADC_IPG_CLK; + adc_cfg &= ~(ADC_CLK_DIV_MASK | ADC_SAMPLE_MODE_MASK); adc_cfg |= ADC_CLK_DIV_8 | ADC_SHORT_SAMPLE_MODE; - if (tsc->average_samples) + if (tsc->average_samples) { + adc_cfg &= ~ADC_AVGS_MASK; adc_cfg |= (tsc->average_samples - 1) << ADC_AVGS_SHIFT; + } adc_cfg &= ~ADC_HARDWARE_TRIGGER; writel(adc_cfg, tsc->adc_regs + REG_ADC_CFG); -- cgit v1.2.3 From accbcea3465b2038fa8aa66cfa8f40db7ab1c83c Mon Sep 17 00:00:00 2001 From: Guy Shapiro Date: Wed, 30 Nov 2016 10:25:11 -0800 Subject: Input: imx6ul_tsc - convert int to u32 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The code uses of_property_read_u32 and expects positive values. However, the values are stored in signed int variables. Additionally, the registers values are also stored in signed variables without a good reason (readl/writel expect u32). The only time this caused a real bug was in the new average-samples property, in which the property is numerically compared and implicitly expected to be positive. I believe it's better to change all the properties and registers to u32, for consistency and warnings reduction. Signed-off-by: Guy Shapiro Reported-by: Bjørn Forsman Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/imx6ul_tsc.c | 42 +++++++++++++++++----------------- 1 file changed, 21 insertions(+), 21 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/imx6ul_tsc.c b/drivers/input/touchscreen/imx6ul_tsc.c index 5babefef99a1..d2a39120f37f 100644 --- a/drivers/input/touchscreen/imx6ul_tsc.c +++ b/drivers/input/touchscreen/imx6ul_tsc.c @@ -91,9 +91,9 @@ struct imx6ul_tsc { struct clk *adc_clk; struct gpio_desc *xnur_gpio; - int measure_delay_time; - int pre_charge_time; - int average_samples; + u32 measure_delay_time; + u32 pre_charge_time; + u32 average_samples; struct completion completion; }; @@ -104,11 +104,11 @@ struct imx6ul_tsc { */ static int imx6ul_adc_init(struct imx6ul_tsc *tsc) { - int adc_hc = 0; - int adc_gc; - int adc_gs; - int adc_cfg; - int timeout; + u32 adc_hc = 0; + u32 adc_gc; + u32 adc_gs; + u32 adc_cfg; + unsigned long timeout; reinit_completion(&tsc->completion); @@ -164,7 +164,7 @@ static int imx6ul_adc_init(struct imx6ul_tsc *tsc) */ static void imx6ul_tsc_channel_config(struct imx6ul_tsc *tsc) { - int adc_hc0, adc_hc1, adc_hc2, adc_hc3, adc_hc4; + u32 adc_hc0, adc_hc1, adc_hc2, adc_hc3, adc_hc4; adc_hc0 = DISABLE_CONVERSION_INT; writel(adc_hc0, tsc->adc_regs + REG_ADC_HC0); @@ -189,8 +189,8 @@ static void imx6ul_tsc_channel_config(struct imx6ul_tsc *tsc) */ static void imx6ul_tsc_set(struct imx6ul_tsc *tsc) { - int basic_setting = 0; - int start; + u32 basic_setting = 0; + u32 start; basic_setting |= tsc->measure_delay_time << 8; basic_setting |= DETECT_4_WIRE_MODE | AUTO_MEASURE; @@ -225,8 +225,8 @@ static int imx6ul_tsc_init(struct imx6ul_tsc *tsc) static void imx6ul_tsc_disable(struct imx6ul_tsc *tsc) { - int tsc_flow; - int adc_cfg; + u32 tsc_flow; + u32 adc_cfg; /* TSC controller enters to idle status */ tsc_flow = readl(tsc->tsc_regs + REG_TSC_FLOW_CONTROL); @@ -243,8 +243,8 @@ static void imx6ul_tsc_disable(struct imx6ul_tsc *tsc) static bool tsc_wait_detect_mode(struct imx6ul_tsc *tsc) { unsigned long timeout = jiffies + msecs_to_jiffies(2); - int state_machine; - int debug_mode2; + u32 state_machine; + u32 debug_mode2; do { if (time_after(jiffies, timeout)) @@ -262,10 +262,10 @@ static bool tsc_wait_detect_mode(struct imx6ul_tsc *tsc) 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; + u32 status; + u32 value; + u32 x, y; + u32 start; status = readl(tsc->tsc_regs + REG_TSC_INT_STATUS); @@ -305,8 +305,8 @@ static irqreturn_t tsc_irq_fn(int irq, void *dev_id) static irqreturn_t adc_irq_fn(int irq, void *dev_id) { struct imx6ul_tsc *tsc = dev_id; - int coco; - int value; + u32 coco; + u32 value; coco = readl(tsc->adc_regs + REG_ADC_HS); if (coco & 0x01) { -- cgit v1.2.3 From c5e8848fc98e363ea51b68de01392366312d9efa Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Fri, 2 Dec 2016 16:59:07 -0800 Subject: Input: synaptics-rmi4 - add support for F03 This adds basic functionality for PS/2 passthrough on Synaptics Touchpads using RMI4 through smbus. Reviewed-by: Andrew Duggan Signed-off-by: Lyude Paul Signed-off-by: Benjamin Tissoires Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/Kconfig | 9 ++ drivers/input/rmi4/Makefile | 1 + drivers/input/rmi4/rmi_bus.c | 3 + drivers/input/rmi4/rmi_driver.h | 1 + drivers/input/rmi4/rmi_f03.c | 232 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 246 insertions(+) create mode 100644 drivers/input/rmi4/rmi_f03.c (limited to 'drivers/input') diff --git a/drivers/input/rmi4/Kconfig b/drivers/input/rmi4/Kconfig index a9c36a5fe708..30cc627a4f45 100644 --- a/drivers/input/rmi4/Kconfig +++ b/drivers/input/rmi4/Kconfig @@ -39,6 +39,15 @@ config RMI4_SMB To compile this driver as a module, choose M here: the module will be called rmi_smbus. +config RMI4_F03 + bool "RMI4 Function 03 (PS2 Guest)" + depends on RMI4_CORE && SERIO + help + Say Y here if you want to add support for RMI4 function 03. + + Function 03 provides PS2 guest support for RMI4 devices. This + includes support for TrackPoints on TouchPads. + config RMI4_2D_SENSOR bool depends on RMI4_CORE diff --git a/drivers/input/rmi4/Makefile b/drivers/input/rmi4/Makefile index e7f4ca6c0508..a199cbe9c27d 100644 --- a/drivers/input/rmi4/Makefile +++ b/drivers/input/rmi4/Makefile @@ -4,6 +4,7 @@ rmi_core-y := rmi_bus.o rmi_driver.o rmi_f01.o rmi_core-$(CONFIG_RMI4_2D_SENSOR) += rmi_2d_sensor.o # Function drivers +rmi_core-$(CONFIG_RMI4_F03) += rmi_f03.o rmi_core-$(CONFIG_RMI4_F11) += rmi_f11.o rmi_core-$(CONFIG_RMI4_F12) += rmi_f12.o rmi_core-$(CONFIG_RMI4_F30) += rmi_f30.o diff --git a/drivers/input/rmi4/rmi_bus.c b/drivers/input/rmi4/rmi_bus.c index 2534331ad9dc..df97d8679bad 100644 --- a/drivers/input/rmi4/rmi_bus.c +++ b/drivers/input/rmi4/rmi_bus.c @@ -306,6 +306,9 @@ struct bus_type rmi_bus_type = { static struct rmi_function_handler *fn_handlers[] = { &rmi_f01_handler, +#ifdef CONFIG_RMI4_F03 + &rmi_f03_handler, +#endif #ifdef CONFIG_RMI4_F11 &rmi_f11_handler, #endif diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h index cc9458522279..24f8f764d171 100644 --- a/drivers/input/rmi4/rmi_driver.h +++ b/drivers/input/rmi4/rmi_driver.h @@ -121,6 +121,7 @@ static inline void rmi_f34_remove_sysfs(struct rmi_device *rmi_dev) #endif /* CONFIG_RMI_F34 */ extern struct rmi_function_handler rmi_f01_handler; +extern struct rmi_function_handler rmi_f03_handler; extern struct rmi_function_handler rmi_f11_handler; extern struct rmi_function_handler rmi_f12_handler; extern struct rmi_function_handler rmi_f30_handler; diff --git a/drivers/input/rmi4/rmi_f03.c b/drivers/input/rmi4/rmi_f03.c new file mode 100644 index 000000000000..24c7375aa361 --- /dev/null +++ b/drivers/input/rmi4/rmi_f03.c @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2015-2016 Red Hat + * Copyright (C) 2015 Lyude Paul + * + * 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 +#include +#include +#include +#include "rmi_driver.h" + +#define RMI_F03_RX_DATA_OFB 0x01 +#define RMI_F03_OB_SIZE 2 + +#define RMI_F03_OB_OFFSET 2 +#define RMI_F03_OB_DATA_OFFSET 1 +#define RMI_F03_OB_FLAG_TIMEOUT BIT(6) +#define RMI_F03_OB_FLAG_PARITY BIT(7) + +#define RMI_F03_DEVICE_COUNT 0x07 +#define RMI_F03_BYTES_PER_DEVICE 0x07 +#define RMI_F03_BYTES_PER_DEVICE_SHIFT 4 +#define RMI_F03_QUEUE_LENGTH 0x0F + +struct f03_data { + struct rmi_function *fn; + + struct serio *serio; + + u8 device_count; + u8 rx_queue_length; +}; + +static int rmi_f03_pt_write(struct serio *id, unsigned char val) +{ + struct f03_data *f03 = id->port_data; + int error; + + rmi_dbg(RMI_DEBUG_FN, &f03->fn->dev, + "%s: Wrote %.2hhx to PS/2 passthrough address", + __func__, val); + + error = rmi_write(f03->fn->rmi_dev, f03->fn->fd.data_base_addr, val); + if (error) { + dev_err(&f03->fn->dev, + "%s: Failed to write to F03 TX register (%d).\n", + __func__, error); + return error; + } + + return 0; +} + +static int rmi_f03_initialize(struct f03_data *f03) +{ + struct rmi_function *fn = f03->fn; + struct device *dev = &fn->dev; + int error; + u8 bytes_per_device; + u8 query1; + u8 query2[RMI_F03_DEVICE_COUNT * RMI_F03_BYTES_PER_DEVICE]; + size_t query2_len; + + error = rmi_read(fn->rmi_dev, fn->fd.query_base_addr, &query1); + if (error) { + dev_err(dev, "Failed to read query register (%d).\n", error); + return error; + } + + f03->device_count = query1 & RMI_F03_DEVICE_COUNT; + bytes_per_device = (query1 >> RMI_F03_BYTES_PER_DEVICE_SHIFT) & + RMI_F03_BYTES_PER_DEVICE; + + query2_len = f03->device_count * bytes_per_device; + + /* + * The first generation of image sensors don't have a second part to + * their f03 query, as such we have to set some of these values manually + */ + if (query2_len < 1) { + f03->device_count = 1; + f03->rx_queue_length = 7; + } else { + error = rmi_read_block(fn->rmi_dev, fn->fd.query_base_addr + 1, + query2, query2_len); + if (error) { + dev_err(dev, + "Failed to read second set of query registers (%d).\n", + error); + return error; + } + + f03->rx_queue_length = query2[0] & RMI_F03_QUEUE_LENGTH; + } + + return 0; +} + +static int rmi_f03_register_pt(struct f03_data *f03) +{ + struct serio *serio; + + serio = kzalloc(sizeof(struct serio), GFP_KERNEL); + if (!serio) + return -ENOMEM; + + serio->id.type = SERIO_8042; + serio->write = rmi_f03_pt_write; + serio->port_data = f03; + + strlcpy(serio->name, "Synaptics RMI4 PS/2 pass-through", + sizeof(serio->name)); + strlcpy(serio->phys, "synaptics-rmi4-pt/serio1", + sizeof(serio->phys)); + serio->dev.parent = &f03->fn->dev; + + f03->serio = serio; + + serio_register_port(serio); + + return 0; +} + +static int rmi_f03_probe(struct rmi_function *fn) +{ + struct device *dev = &fn->dev; + struct f03_data *f03; + int error; + + f03 = devm_kzalloc(dev, sizeof(struct f03_data), GFP_KERNEL); + if (!f03) + return -ENOMEM; + + f03->fn = fn; + + error = rmi_f03_initialize(f03); + if (error < 0) + return error; + + if (f03->device_count != 1) + dev_warn(dev, "found %d devices on PS/2 passthrough", + f03->device_count); + + dev_set_drvdata(dev, f03); + + error = rmi_f03_register_pt(f03); + if (error) + return error; + + return 0; +} + +static int rmi_f03_config(struct rmi_function *fn) +{ + fn->rmi_dev->driver->set_irq_bits(fn->rmi_dev, fn->irq_mask); + + return 0; +} + +static int rmi_f03_attention(struct rmi_function *fn, unsigned long *irq_bits) +{ + struct f03_data *f03 = dev_get_drvdata(&fn->dev); + u16 data_addr = fn->fd.data_base_addr; + const u8 ob_len = f03->rx_queue_length * RMI_F03_OB_SIZE; + u8 obs[RMI_F03_QUEUE_LENGTH * RMI_F03_OB_SIZE]; + u8 ob_status; + u8 ob_data; + unsigned int serio_flags; + int i; + int error; + + /* Grab all of the data registers, and check them for data */ + error = rmi_read_block(fn->rmi_dev, data_addr + RMI_F03_OB_OFFSET, + &obs, ob_len); + if (error) { + dev_err(&fn->dev, + "%s: Failed to read F03 output buffers: %d\n", + __func__, error); + serio_interrupt(f03->serio, 0, SERIO_TIMEOUT); + return error; + } + + for (i = 0; i < ob_len; i += RMI_F03_OB_SIZE) { + ob_status = obs[i]; + ob_data = obs[i + RMI_F03_OB_DATA_OFFSET]; + serio_flags = 0; + + if (!(ob_status & RMI_F03_RX_DATA_OFB)) + continue; + + if (ob_status & RMI_F03_OB_FLAG_TIMEOUT) + serio_flags |= SERIO_TIMEOUT; + if (ob_status & RMI_F03_OB_FLAG_PARITY) + serio_flags |= SERIO_PARITY; + + rmi_dbg(RMI_DEBUG_FN, &fn->dev, + "%s: Received %.2hhx from PS2 guest T: %c P: %c\n", + __func__, ob_data, + serio_flags & SERIO_TIMEOUT ? 'Y' : 'N', + serio_flags & SERIO_PARITY ? 'Y' : 'N'); + + serio_interrupt(f03->serio, ob_data, serio_flags); + } + + return 0; +} + +static void rmi_f03_remove(struct rmi_function *fn) +{ + struct f03_data *f03 = dev_get_drvdata(&fn->dev); + + serio_unregister_port(f03->serio); +} + +struct rmi_function_handler rmi_f03_handler = { + .driver = { + .name = "rmi4_f03", + }, + .func = 0x03, + .probe = rmi_f03_probe, + .config = rmi_f03_config, + .attention = rmi_f03_attention, + .remove = rmi_f03_remove, +}; + +MODULE_AUTHOR("Lyude Paul "); +MODULE_DESCRIPTION("RMI F03 module"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From e621132f934f5922e8a3968edd236f97cdad60cf Mon Sep 17 00:00:00 2001 From: Dennis Wassenberg Date: Fri, 2 Dec 2016 17:45:29 -0800 Subject: Input: synaptics-rmi4 - f03 - grab data passed by transport device First check if there are data available passed by the transport device. If data available use these data. If there are no data available try to read the rmi block if dsata are passed this way. This is the way the other rmi function handlers will do this. This patch is needed on HID devices because the firmware reads F03 data registers and adds them to the HID attention report. Reading those registers from the driver after the firmware read them will result in invalid data. Reviewed-by: Andrew Duggan Signed-off-by: Dennis Wassenberg Signed-off-by: Benjamin Tissoires Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/rmi_f03.c | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/rmi4/rmi_f03.c b/drivers/input/rmi4/rmi_f03.c index 24c7375aa361..7a3ec0ed0c27 100644 --- a/drivers/input/rmi4/rmi_f03.c +++ b/drivers/input/rmi4/rmi_f03.c @@ -163,6 +163,7 @@ static int rmi_f03_config(struct rmi_function *fn) static int rmi_f03_attention(struct rmi_function *fn, unsigned long *irq_bits) { + struct rmi_device *rmi_dev = fn->rmi_dev; struct f03_data *f03 = dev_get_drvdata(&fn->dev); u16 data_addr = fn->fd.data_base_addr; const u8 ob_len = f03->rx_queue_length * RMI_F03_OB_SIZE; @@ -173,15 +174,31 @@ static int rmi_f03_attention(struct rmi_function *fn, unsigned long *irq_bits) int i; int error; - /* Grab all of the data registers, and check them for data */ - error = rmi_read_block(fn->rmi_dev, data_addr + RMI_F03_OB_OFFSET, - &obs, ob_len); - if (error) { - dev_err(&fn->dev, - "%s: Failed to read F03 output buffers: %d\n", - __func__, error); - serio_interrupt(f03->serio, 0, SERIO_TIMEOUT); - return error; + if (!rmi_dev || !rmi_dev->xport) + return -ENODEV; + + if (rmi_dev->xport->attn_data) { + /* First grab the data passed by the transport device */ + if (rmi_dev->xport->attn_size < ob_len) { + dev_warn(&fn->dev, "F03 interrupted, but data is missing!\n"); + return 0; + } + + memcpy(obs, rmi_dev->xport->attn_data, ob_len); + + rmi_dev->xport->attn_data += ob_len; + rmi_dev->xport->attn_size -= ob_len; + } else { + /* Grab all of the data registers, and check them for data */ + error = rmi_read_block(fn->rmi_dev, data_addr + RMI_F03_OB_OFFSET, + &obs, ob_len); + if (error) { + dev_err(&fn->dev, + "%s: Failed to read F03 output buffers: %d\n", + __func__, error); + serio_interrupt(f03->serio, 0, SERIO_TIMEOUT); + return error; + } } for (i = 0; i < ob_len; i += RMI_F03_OB_SIZE) { -- cgit v1.2.3 From b908d3cd812abe3f4a74d7550bbf0a8cbcfbe6ed Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Fri, 2 Dec 2016 17:48:51 -0800 Subject: Input: synaptics-rmi4 - allow to add attention data The HID implementation of RMI4 provides the data during the interrupt (in the input report). We need to provide a way for this transport driver to provide the attention data while calling an IRQ. We use a fifo in rmi_core to not lose any incoming event. Signed-off-by: Benjamin Tissoires Reviewed-by: Andrew Duggan Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/rmi_driver.c | 49 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c index a718e51afb0b..85062e414e73 100644 --- a/drivers/input/rmi4/rmi_driver.c +++ b/drivers/input/rmi4/rmi_driver.c @@ -191,16 +191,53 @@ static int rmi_process_interrupt_requests(struct rmi_device *rmi_dev) return 0; } +void rmi_set_attn_data(struct rmi_device *rmi_dev, unsigned long irq_status, + void *data, size_t size) +{ + struct rmi_driver_data *drvdata = dev_get_drvdata(&rmi_dev->dev); + struct rmi4_attn_data attn_data; + void *fifo_data; + + if (!drvdata->enabled) + return; + + fifo_data = kmemdup(data, size, GFP_ATOMIC); + if (!fifo_data) + return; + + attn_data.irq_status = irq_status; + attn_data.size = size; + attn_data.data = fifo_data; + + kfifo_put(&drvdata->attn_fifo, attn_data); +} +EXPORT_SYMBOL_GPL(rmi_set_attn_data); + static irqreturn_t rmi_irq_fn(int irq, void *dev_id) { struct rmi_device *rmi_dev = dev_id; - int ret; + struct rmi_driver_data *drvdata = dev_get_drvdata(&rmi_dev->dev); + struct rmi4_attn_data attn_data = {0}; + int ret, count; + + count = kfifo_get(&drvdata->attn_fifo, &attn_data); + if (count) { + *(drvdata->irq_status) = attn_data.irq_status; + rmi_dev->xport->attn_data = attn_data.data; + rmi_dev->xport->attn_size = attn_data.size; + } ret = rmi_process_interrupt_requests(rmi_dev); if (ret) rmi_dbg(RMI_DEBUG_CORE, &rmi_dev->dev, "Failed to process interrupt request: %d\n", ret); + if (count) + kfree(attn_data.data); + + if (!kfifo_is_empty(&drvdata->attn_fifo)) + return rmi_irq_fn(irq, dev_id); + return IRQ_HANDLED; } @@ -880,8 +917,9 @@ void rmi_disable_irq(struct rmi_device *rmi_dev, bool enable_wake) { struct rmi_device_platform_data *pdata = rmi_get_platform_data(rmi_dev); struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev); + struct rmi4_attn_data attn_data = {0}; int irq = pdata->irq; - int retval; + int retval, count; mutex_lock(&data->enabled_mutex); @@ -898,6 +936,13 @@ void rmi_disable_irq(struct rmi_device *rmi_dev, bool enable_wake) retval); } + /* make sure the fifo is clean */ + while (!kfifo_is_empty(&data->attn_fifo)) { + count = kfifo_get(&data->attn_fifo, &attn_data); + if (count) + kfree(attn_data.data); + } + out: mutex_unlock(&data->enabled_mutex); } -- cgit v1.2.3 From ae9979c31007d5366b73640ee7dcbb271357053e Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Fri, 2 Dec 2016 17:49:10 -0800 Subject: Input: synaptics-rmi4 - store the attn data in the driver Now that we have a proper API to set the attention data, there is no point in keeping it in the transport driver. Signed-off-by: Benjamin Tissoires Reviewed-by: Andrew Duggan Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/rmi_driver.c | 5 ++--- drivers/input/rmi4/rmi_f03.c | 13 +++++++------ drivers/input/rmi4/rmi_f11.c | 12 ++++++------ drivers/input/rmi4/rmi_f12.c | 43 +++++++++++++++++++++-------------------- drivers/input/rmi4/rmi_f30.c | 11 ++++++----- 5 files changed, 43 insertions(+), 41 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c index 85062e414e73..05a3c4bc9778 100644 --- a/drivers/input/rmi4/rmi_driver.c +++ b/drivers/input/rmi4/rmi_driver.c @@ -155,7 +155,7 @@ static int rmi_process_interrupt_requests(struct rmi_device *rmi_dev) if (!data) return 0; - if (!rmi_dev->xport->attn_data) { + if (!data->attn_data.data) { error = rmi_read_block(rmi_dev, data->f01_container->fd.data_base_addr + 1, data->irq_status, data->num_of_irq_regs); @@ -223,8 +223,7 @@ static irqreturn_t rmi_irq_fn(int irq, void *dev_id) count = kfifo_get(&drvdata->attn_fifo, &attn_data); if (count) { *(drvdata->irq_status) = attn_data.irq_status; - rmi_dev->xport->attn_data = attn_data.data; - rmi_dev->xport->attn_size = attn_data.size; + drvdata->attn_data = attn_data; } ret = rmi_process_interrupt_requests(rmi_dev); diff --git a/drivers/input/rmi4/rmi_f03.c b/drivers/input/rmi4/rmi_f03.c index 7a3ec0ed0c27..8a7ca3e2f95e 100644 --- a/drivers/input/rmi4/rmi_f03.c +++ b/drivers/input/rmi4/rmi_f03.c @@ -164,6 +164,7 @@ static int rmi_f03_config(struct rmi_function *fn) static int rmi_f03_attention(struct rmi_function *fn, unsigned long *irq_bits) { struct rmi_device *rmi_dev = fn->rmi_dev; + struct rmi_driver_data *drvdata = dev_get_drvdata(&rmi_dev->dev); struct f03_data *f03 = dev_get_drvdata(&fn->dev); u16 data_addr = fn->fd.data_base_addr; const u8 ob_len = f03->rx_queue_length * RMI_F03_OB_SIZE; @@ -174,20 +175,20 @@ static int rmi_f03_attention(struct rmi_function *fn, unsigned long *irq_bits) int i; int error; - if (!rmi_dev || !rmi_dev->xport) + if (!rmi_dev) return -ENODEV; - if (rmi_dev->xport->attn_data) { + if (drvdata->attn_data.data) { /* First grab the data passed by the transport device */ - if (rmi_dev->xport->attn_size < ob_len) { + if (drvdata->attn_data.size < ob_len) { dev_warn(&fn->dev, "F03 interrupted, but data is missing!\n"); return 0; } - memcpy(obs, rmi_dev->xport->attn_data, ob_len); + memcpy(obs, drvdata->attn_data.data, ob_len); - rmi_dev->xport->attn_data += ob_len; - rmi_dev->xport->attn_size -= ob_len; + drvdata->attn_data.data += ob_len; + drvdata->attn_data.size -= ob_len; } else { /* Grab all of the data registers, and check them for data */ error = rmi_read_block(fn->rmi_dev, data_addr + RMI_F03_OB_OFFSET, diff --git a/drivers/input/rmi4/rmi_f11.c b/drivers/input/rmi4/rmi_f11.c index ffcbbc1745de..68279f3c5130 100644 --- a/drivers/input/rmi4/rmi_f11.c +++ b/drivers/input/rmi4/rmi_f11.c @@ -1285,19 +1285,19 @@ static int rmi_f11_attention(struct rmi_function *fn, unsigned long *irq_bits) int error; int valid_bytes = f11->sensor.pkt_size; - if (rmi_dev->xport->attn_data) { + if (drvdata->attn_data.data) { /* * The valid data in the attention report is less then * expected. Only process the complete fingers. */ - if (f11->sensor.attn_size > rmi_dev->xport->attn_size) - valid_bytes = rmi_dev->xport->attn_size; + if (f11->sensor.attn_size > drvdata->attn_data.size) + valid_bytes = drvdata->attn_data.size; else valid_bytes = f11->sensor.attn_size; - memcpy(f11->sensor.data_pkt, rmi_dev->xport->attn_data, + memcpy(f11->sensor.data_pkt, drvdata->attn_data.data, valid_bytes); - rmi_dev->xport->attn_data += f11->sensor.attn_size; - rmi_dev->xport->attn_size -= f11->sensor.attn_size; + drvdata->attn_data.data += f11->sensor.attn_size; + drvdata->attn_data.size -= f11->sensor.attn_size; } else { error = rmi_read_block(rmi_dev, data_base_addr, f11->sensor.data_pkt, diff --git a/drivers/input/rmi4/rmi_f12.c b/drivers/input/rmi4/rmi_f12.c index 82a4964e5eb9..8c5360c25266 100644 --- a/drivers/input/rmi4/rmi_f12.c +++ b/drivers/input/rmi4/rmi_f12.c @@ -208,19 +208,20 @@ static int rmi_f12_attention(struct rmi_function *fn, { int retval; struct rmi_device *rmi_dev = fn->rmi_dev; + struct rmi_driver_data *drvdata = dev_get_drvdata(&rmi_dev->dev); struct f12_data *f12 = dev_get_drvdata(&fn->dev); struct rmi_2d_sensor *sensor = &f12->sensor; int valid_bytes = sensor->pkt_size; - if (rmi_dev->xport->attn_data) { - if (sensor->attn_size > rmi_dev->xport->attn_size) - valid_bytes = rmi_dev->xport->attn_size; + if (drvdata->attn_data.data) { + if (sensor->attn_size > drvdata->attn_data.size) + valid_bytes = drvdata->attn_data.size; else valid_bytes = sensor->attn_size; - memcpy(sensor->data_pkt, rmi_dev->xport->attn_data, + memcpy(sensor->data_pkt, drvdata->attn_data.data, valid_bytes); - rmi_dev->xport->attn_data += sensor->attn_size; - rmi_dev->xport->attn_size -= sensor->attn_size; + drvdata->attn_data.data += sensor->attn_size; + drvdata->attn_data.size -= sensor->attn_size; } else { retval = rmi_read_block(rmi_dev, f12->data_addr, sensor->data_pkt, sensor->pkt_size); @@ -323,7 +324,7 @@ static int rmi_f12_probe(struct rmi_function *fn) const struct rmi_register_desc_item *item; struct rmi_2d_sensor *sensor; struct rmi_device_platform_data *pdata = rmi_get_platform_data(rmi_dev); - struct rmi_transport_dev *xport = rmi_dev->xport; + struct rmi_driver_data *drvdata = dev_get_drvdata(&rmi_dev->dev); u16 data_offset = 0; rmi_dbg(RMI_DEBUG_FN, &fn->dev, "%s\n", __func__); @@ -422,7 +423,7 @@ static int rmi_f12_probe(struct rmi_function *fn) * HID attention reports. */ item = rmi_get_register_desc_item(&f12->data_reg_desc, 0); - if (item && !xport->attn_data) + if (item && !drvdata->attn_data.data) data_offset += item->reg_size; item = rmi_get_register_desc_item(&f12->data_reg_desc, 1); @@ -436,15 +437,15 @@ static int rmi_f12_probe(struct rmi_function *fn) } item = rmi_get_register_desc_item(&f12->data_reg_desc, 2); - if (item && !xport->attn_data) + if (item && !drvdata->attn_data.data) data_offset += item->reg_size; item = rmi_get_register_desc_item(&f12->data_reg_desc, 3); - if (item && !xport->attn_data) + if (item && !drvdata->attn_data.data) data_offset += item->reg_size; item = rmi_get_register_desc_item(&f12->data_reg_desc, 4); - if (item && !xport->attn_data) + if (item && !drvdata->attn_data.data) data_offset += item->reg_size; item = rmi_get_register_desc_item(&f12->data_reg_desc, 5); @@ -456,22 +457,22 @@ static int rmi_f12_probe(struct rmi_function *fn) } item = rmi_get_register_desc_item(&f12->data_reg_desc, 6); - if (item && !xport->attn_data) { + if (item && !drvdata->attn_data.data) { f12->data6 = item; f12->data6_offset = data_offset; data_offset += item->reg_size; } item = rmi_get_register_desc_item(&f12->data_reg_desc, 7); - if (item && !xport->attn_data) + if (item && !drvdata->attn_data.data) data_offset += item->reg_size; item = rmi_get_register_desc_item(&f12->data_reg_desc, 8); - if (item && !xport->attn_data) + if (item && !drvdata->attn_data.data) data_offset += item->reg_size; item = rmi_get_register_desc_item(&f12->data_reg_desc, 9); - if (item && !xport->attn_data) { + if (item && !drvdata->attn_data.data) { f12->data9 = item; f12->data9_offset = data_offset; data_offset += item->reg_size; @@ -480,27 +481,27 @@ static int rmi_f12_probe(struct rmi_function *fn) } item = rmi_get_register_desc_item(&f12->data_reg_desc, 10); - if (item && !xport->attn_data) + if (item && !drvdata->attn_data.data) data_offset += item->reg_size; item = rmi_get_register_desc_item(&f12->data_reg_desc, 11); - if (item && !xport->attn_data) + if (item && !drvdata->attn_data.data) data_offset += item->reg_size; item = rmi_get_register_desc_item(&f12->data_reg_desc, 12); - if (item && !xport->attn_data) + if (item && !drvdata->attn_data.data) data_offset += item->reg_size; item = rmi_get_register_desc_item(&f12->data_reg_desc, 13); - if (item && !xport->attn_data) + if (item && !drvdata->attn_data.data) data_offset += item->reg_size; item = rmi_get_register_desc_item(&f12->data_reg_desc, 14); - if (item && !xport->attn_data) + if (item && !drvdata->attn_data.data) data_offset += item->reg_size; item = rmi_get_register_desc_item(&f12->data_reg_desc, 15); - if (item && !xport->attn_data) { + if (item && !drvdata->attn_data.data) { f12->data15 = item; f12->data15_offset = data_offset; data_offset += item->reg_size; diff --git a/drivers/input/rmi4/rmi_f30.c b/drivers/input/rmi4/rmi_f30.c index f696137a56f5..f4b491e3e0fd 100644 --- a/drivers/input/rmi4/rmi_f30.c +++ b/drivers/input/rmi4/rmi_f30.c @@ -99,6 +99,7 @@ static int rmi_f30_attention(struct rmi_function *fn, unsigned long *irq_bits) { struct f30_data *f30 = dev_get_drvdata(&fn->dev); struct rmi_device *rmi_dev = fn->rmi_dev; + struct rmi_driver_data *drvdata = dev_get_drvdata(&rmi_dev->dev); int retval; int gpiled = 0; int value = 0; @@ -109,15 +110,15 @@ static int rmi_f30_attention(struct rmi_function *fn, unsigned long *irq_bits) return 0; /* Read the gpi led data. */ - if (rmi_dev->xport->attn_data) { - if (rmi_dev->xport->attn_size < f30->register_count) { + if (drvdata->attn_data.data) { + if (drvdata->attn_data.size < f30->register_count) { dev_warn(&fn->dev, "F30 interrupted, but data is missing\n"); return 0; } - memcpy(f30->data_regs, rmi_dev->xport->attn_data, + memcpy(f30->data_regs, drvdata->attn_data.data, f30->register_count); - rmi_dev->xport->attn_data += f30->register_count; - rmi_dev->xport->attn_size -= f30->register_count; + drvdata->attn_data.data += f30->register_count; + drvdata->attn_data.size -= f30->register_count; } else { retval = rmi_read_block(rmi_dev, fn->fd.data_base_addr, f30->data_regs, f30->register_count); -- cgit v1.2.3 From 5d244f7effafeaa5272ca5daa37d8b7bb17967a8 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Wed, 7 Dec 2016 17:20:06 -0800 Subject: Input: synaptics-rmi4 - fix debug for sensor clip The debug would only ever output zero for the clip information. Signed-off-by: Nick Dyer Reviewed-by: Andrew Duggan Tested-by: Chris Healy Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/rmi_f12.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/rmi4/rmi_f12.c b/drivers/input/rmi4/rmi_f12.c index 8c5360c25266..07aff4356fe0 100644 --- a/drivers/input/rmi4/rmi_f12.c +++ b/drivers/input/rmi4/rmi_f12.c @@ -71,10 +71,6 @@ static int rmi_f12_read_sensor_tuning(struct f12_data *f12) u8 buf[15]; int pitch_x = 0; int pitch_y = 0; - int clip_x_low = 0; - int clip_x_high = 0; - int clip_y_low = 0; - int clip_y_high = 0; int rx_receivers = 0; int tx_receivers = 0; int sensor_flags = 0; @@ -127,7 +123,9 @@ static int rmi_f12_read_sensor_tuning(struct f12_data *f12) } rmi_dbg(RMI_DEBUG_FN, &fn->dev, "%s: x low: %d x high: %d y low: %d y high: %d\n", - __func__, clip_x_low, clip_x_high, clip_y_low, clip_y_high); + __func__, + sensor->axis_align.clip_x_low, sensor->axis_align.clip_x_high, + sensor->axis_align.clip_y_low, sensor->axis_align.clip_y_high); if (rmi_register_desc_has_subpacket(item, 3)) { rx_receivers = buf[offset]; -- cgit v1.2.3 From 951a086437b7c29a9f352e0ee6272fa1d90d783e Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy Date: Wed, 7 Dec 2016 17:22:36 -0800 Subject: Input: lpc32xx-keys - fix invalid error handling of a requested irq Semantics of NR_IRQS is different on machines with SPARSE_IRQ option disabled or enabled, in the latter case IRQs are allocated starting at least from the value specified by NR_IRQS and going upwards, so the check of (irq >= NR_IRQ) to decide about an error code returned by platform_get_irq() is completely invalid, don't attempt to overrule irq subsystem in the driver. The change fixes lpc32xx_keys driver initialization on boot: lpc32xx_keys 40050000.key: failed to get platform irq lpc32xx_keys: probe of 40050000.key failed with error -22 Signed-off-by: Vladimir Zapolskiy Acked-by: Sylvain Lemieux Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/lpc32xx-keys.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/lpc32xx-keys.c b/drivers/input/keyboard/lpc32xx-keys.c index 265d641c40e2..632523d4f5dc 100644 --- a/drivers/input/keyboard/lpc32xx-keys.c +++ b/drivers/input/keyboard/lpc32xx-keys.c @@ -182,7 +182,7 @@ static int lpc32xx_kscan_probe(struct platform_device *pdev) } irq = platform_get_irq(pdev, 0); - if (irq < 0 || irq >= NR_IRQS) { + if (irq < 0) { dev_err(&pdev->dev, "failed to get platform irq\n"); return -EINVAL; } -- cgit v1.2.3 From 4d46f0148ed1bfaaf74e30653a140927b1d3a809 Mon Sep 17 00:00:00 2001 From: Marcos Paulo de Souza Date: Fri, 9 Dec 2016 17:59:56 -0800 Subject: Input: i8042 - comment #else/#endif of CONFIG_PNP As this define check if huge, this makes easier to read the code. Signed-off-by: Marcos Paulo de Souza Signed-off-by: Dmitry Torokhov --- drivers/input/serio/i8042-x86ia64io.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h index b42787e21d0a..21e134798ba3 100644 --- a/drivers/input/serio/i8042-x86ia64io.h +++ b/drivers/input/serio/i8042-x86ia64io.h @@ -1052,10 +1052,10 @@ static int __init i8042_pnp_init(void) return 0; } -#else +#else /* !CONFIG_PNP */ static inline int i8042_pnp_init(void) { return 0; } static inline void i8042_pnp_exit(void) { } -#endif +#endif /* CONFIG_PNP */ static int __init i8042_platform_init(void) { -- cgit v1.2.3 From 7343d11906788214220b124c379f5b0e1cec834f Mon Sep 17 00:00:00 2001 From: Marcos Paulo de Souza Date: Mon, 12 Dec 2016 10:59:56 -0800 Subject: Input: ALPS - fix protcol -> protocol MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcos Paulo de Souza Reviewed-by: Pali Rohár Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/alps.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/alps.h b/drivers/input/mouse/alps.h index 72b9efc5217b..cde6f4bd8ea2 100644 --- a/drivers/input/mouse/alps.h +++ b/drivers/input/mouse/alps.h @@ -166,7 +166,7 @@ struct alps_protocol_info { * (aka command mode response) identifies the firmware minor version. This * can be used to distinguish different hardware models which are not * uniquely identifiable through their E7 responses. - * @protocol_info: information about protcol used by the device. + * @protocol_info: information about protocol used by the device. * * Many (but not all) ALPS touchpads can be identified by looking at the * values returned in the "E7 report" and/or the "EC report." This table -- cgit v1.2.3 From 74c82dae6c474933f2be401976e1530b5f623221 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sat, 10 Dec 2016 22:56:21 -0800 Subject: Input: drv260x - fix initializing overdrive voltage We were accidentally initializing haptics->rated_voltage twice, and did not initialize overdrive voltage. Acked-by: Dan Murphy Signed-off-by: Dmitry Torokhov --- drivers/input/misc/drv260x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/misc/drv260x.c b/drivers/input/misc/drv260x.c index 2adfd86c869a..9789d4fb6e51 100644 --- a/drivers/input/misc/drv260x.c +++ b/drivers/input/misc/drv260x.c @@ -521,7 +521,7 @@ static int drv260x_probe(struct i2c_client *client, if (!haptics) return -ENOMEM; - haptics->rated_voltage = DRV260X_DEF_OD_CLAMP_VOLT; + haptics->overdrive_voltage = DRV260X_DEF_OD_CLAMP_VOLT; haptics->rated_voltage = DRV260X_DEF_RATED_VOLT; if (pdata) { -- cgit v1.2.3 From 5191d88acc688743eef56f1c598a4e4cddf6c6cd Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Sat, 10 Dec 2016 23:27:32 -0800 Subject: Input: synaptics-rmi4 - add support for F34 V7 bootloader Port firmware update code from Samsung Galaxy S7 driver into mainline framework. This patch has been tested on Synaptics S7813. Signed-off-by: Nick Dyer Tested-by: Chris Healy Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/Makefile | 2 +- drivers/input/rmi4/rmi_driver.c | 56 +- drivers/input/rmi4/rmi_f34.c | 40 +- drivers/input/rmi4/rmi_f34.h | 250 ++++++- drivers/input/rmi4/rmi_f34v7.c | 1372 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 1687 insertions(+), 33 deletions(-) create mode 100644 drivers/input/rmi4/rmi_f34v7.c (limited to 'drivers/input') diff --git a/drivers/input/rmi4/Makefile b/drivers/input/rmi4/Makefile index a199cbe9c27d..9aaac3dd8613 100644 --- a/drivers/input/rmi4/Makefile +++ b/drivers/input/rmi4/Makefile @@ -8,7 +8,7 @@ rmi_core-$(CONFIG_RMI4_F03) += rmi_f03.o rmi_core-$(CONFIG_RMI4_F11) += rmi_f11.o rmi_core-$(CONFIG_RMI4_F12) += rmi_f12.o rmi_core-$(CONFIG_RMI4_F30) += rmi_f30.o -rmi_core-$(CONFIG_RMI4_F34) += rmi_f34.o +rmi_core-$(CONFIG_RMI4_F34) += rmi_f34.o rmi_f34v7.o rmi_core-$(CONFIG_RMI4_F54) += rmi_f54.o rmi_core-$(CONFIG_RMI4_F55) += rmi_f55.o diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c index 05a3c4bc9778..cb6efe693302 100644 --- a/drivers/input/rmi4/rmi_driver.c +++ b/drivers/input/rmi4/rmi_driver.c @@ -544,7 +544,7 @@ static int rmi_scan_pdt_page(struct rmi_device *rmi_dev, else *empty_pages = 0; - return (data->f01_bootloader_mode || *empty_pages >= 2) ? + return (data->bootloader_mode || *empty_pages >= 2) ? RMI_SCAN_DONE : RMI_SCAN_CONTINUE; } @@ -749,41 +749,49 @@ bool rmi_register_desc_has_subpacket(const struct rmi_register_desc_item *item, subpacket) == subpacket; } -/* Indicates that flash programming is enabled (bootloader mode). */ -#define RMI_F01_STATUS_BOOTLOADER(status) (!!((status) & 0x40)) - -/* - * Given the PDT entry for F01, read the device status register to determine - * if we're stuck in bootloader mode or not. - * - */ static int rmi_check_bootloader_mode(struct rmi_device *rmi_dev, const struct pdt_entry *pdt) { - int error; - u8 device_status; + struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev); + int ret; + u8 status; - error = rmi_read(rmi_dev, pdt->data_base_addr + pdt->page_start, - &device_status); - if (error) { - dev_err(&rmi_dev->dev, - "Failed to read device status: %d.\n", error); - return error; + if (pdt->function_number == 0x34 && pdt->function_version > 1) { + ret = rmi_read(rmi_dev, pdt->data_base_addr, &status); + if (ret) { + dev_err(&rmi_dev->dev, + "Failed to read F34 status: %d.\n", ret); + return ret; + } + + if (status & BIT(7)) + data->bootloader_mode = true; + } else if (pdt->function_number == 0x01) { + ret = rmi_read(rmi_dev, pdt->data_base_addr, &status); + if (ret) { + dev_err(&rmi_dev->dev, + "Failed to read F01 status: %d.\n", ret); + return ret; + } + + if (status & BIT(6)) + data->bootloader_mode = true; } - return RMI_F01_STATUS_BOOTLOADER(device_status); + return 0; } static int rmi_count_irqs(struct rmi_device *rmi_dev, void *ctx, const struct pdt_entry *pdt) { - struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev); int *irq_count = ctx; + int ret; *irq_count += pdt->interrupt_source_count; - if (pdt->function_number == 0x01) - data->f01_bootloader_mode = - rmi_check_bootloader_mode(rmi_dev, pdt); + + ret = rmi_check_bootloader_mode(rmi_dev, pdt); + if (ret < 0) + return ret; return RMI_SCAN_CONTINUE; } @@ -1024,13 +1032,15 @@ int rmi_probe_interrupts(struct rmi_driver_data *data) */ rmi_dbg(RMI_DEBUG_CORE, dev, "%s: Counting IRQs.\n", __func__); irq_count = 0; + data->bootloader_mode = false; + retval = rmi_scan_pdt(rmi_dev, &irq_count, rmi_count_irqs); if (retval < 0) { dev_err(dev, "IRQ counting failed with code %d.\n", retval); return retval; } - if (data->f01_bootloader_mode) + if (data->bootloader_mode) dev_warn(&rmi_dev->dev, "Device in bootloader mode.\n"); data->irq_count = irq_count; diff --git a/drivers/input/rmi4/rmi_f34.c b/drivers/input/rmi4/rmi_f34.c index 01936a4a9a6c..9774dfbab9bb 100644 --- a/drivers/input/rmi4/rmi_f34.c +++ b/drivers/input/rmi4/rmi_f34.c @@ -12,6 +12,7 @@ #include #include #include +#include #include "rmi_driver.h" #include "rmi_f34.h" @@ -105,6 +106,9 @@ static int rmi_f34_attention(struct rmi_function *fn, unsigned long *irq_bits) struct f34_data *f34 = dev_get_drvdata(&fn->dev); int ret; + if (f34->bl_version != 5) + return 0; + ret = rmi_read(f34->fn->rmi_dev, f34->v5.ctrl_address, &f34->v5.status); rmi_dbg(RMI_DEBUG_FN, &fn->dev, "%s: status: %#02x, ret: %d\n", __func__, f34->v5.status, ret); @@ -292,17 +296,24 @@ static int rmi_firmware_update(struct rmi_driver_data *data, return -EINVAL; } - /* Only version 0 currently supported */ - if (data->f34_container->fd.function_version != 0) { + f34 = dev_get_drvdata(&data->f34_container->dev); + + if (f34->bl_version == 7) { + if (data->pdt_props & HAS_BSR) { + dev_err(dev, "%s: LTS not supported\n", __func__); + return -ENODEV; + } + } else if (f34->bl_version != 5) { dev_warn(dev, "F34 V%d not supported!\n", data->f34_container->fd.function_version); return -ENODEV; } - f34 = dev_get_drvdata(&data->f34_container->dev); - /* Enter flash mode */ - ret = rmi_f34_enable_flash(f34); + if (f34->bl_version == 7) + ret = rmi_f34v7_start_reflash(f34, fw); + else + ret = rmi_f34_enable_flash(f34); if (ret) return ret; @@ -319,7 +330,7 @@ static int rmi_firmware_update(struct rmi_driver_data *data, if (ret) return ret; - if (!data->f01_bootloader_mode || !data->f34_container) { + if (!data->bootloader_mode || !data->f34_container) { dev_warn(dev, "%s: No F34 present or not in bootloader!\n", __func__); return -EINVAL; @@ -330,7 +341,10 @@ static int rmi_firmware_update(struct rmi_driver_data *data, f34 = dev_get_drvdata(&data->f34_container->dev); /* Perform firmware update */ - ret = rmi_f34_update_firmware(f34, fw); + if (f34->bl_version == 7) + ret = rmi_f34v7_do_reflash(f34, fw); + else + ret = rmi_f34_update_firmware(f34, fw); dev_info(&f34->fn->dev, "Firmware update complete, status:%d\n", ret); @@ -363,6 +377,9 @@ static int rmi_firmware_update(struct rmi_driver_data *data, return ret; } +static int rmi_firmware_update(struct rmi_driver_data *data, + const struct firmware *fw); + static ssize_t rmi_driver_update_fw_store(struct device *dev, struct device_attribute *dattr, const char *buf, size_t count) @@ -411,6 +428,7 @@ static int rmi_f34_probe(struct rmi_function *fn) struct f34_data *f34; unsigned char f34_queries[9]; bool has_config_id; + u8 version = fn->fd.function_version; int ret; f34 = devm_kzalloc(&fn->dev, sizeof(struct f34_data), GFP_KERNEL); @@ -420,6 +438,14 @@ static int rmi_f34_probe(struct rmi_function *fn) f34->fn = fn; dev_set_drvdata(&fn->dev, f34); + /* v5 code only supported version 0, try V7 probe */ + if (version > 0) + return rmi_f34v7_probe(f34); + else if (version != 0) + return -ENODEV; + + f34->bl_version = 5; + ret = rmi_read_block(fn->rmi_dev, fn->fd.query_base_addr, f34_queries, sizeof(f34_queries)); if (ret) { diff --git a/drivers/input/rmi4/rmi_f34.h b/drivers/input/rmi4/rmi_f34.h index 6cee5282fbb4..2c21056dc375 100644 --- a/drivers/input/rmi4/rmi_f34.h +++ b/drivers/input/rmi4/rmi_f34.h @@ -33,6 +33,216 @@ #define F34_BOOTLOADER_ID_LEN 2 +/* F34 V7 defines */ +#define V7_FLASH_STATUS_OFFSET 0 +#define V7_PARTITION_ID_OFFSET 1 +#define V7_BLOCK_NUMBER_OFFSET 2 +#define V7_TRANSFER_LENGTH_OFFSET 3 +#define V7_COMMAND_OFFSET 4 +#define V7_PAYLOAD_OFFSET 5 +#define V7_BOOTLOADER_ID_OFFSET 1 + +#define IMAGE_HEADER_VERSION_10 0x10 + +#define CONFIG_ID_SIZE 32 +#define PRODUCT_ID_SIZE 10 + +#define ENABLE_WAIT_MS (1 * 1000) +#define WRITE_WAIT_MS (3 * 1000) + +#define MIN_SLEEP_TIME_US 50 +#define MAX_SLEEP_TIME_US 100 + +#define HAS_BSR BIT(5) +#define HAS_CONFIG_ID BIT(3) +#define HAS_GUEST_CODE BIT(6) +#define HAS_DISP_CFG BIT(5) + +/* F34 V7 commands */ +#define CMD_V7_IDLE 0 +#define CMD_V7_ENTER_BL 1 +#define CMD_V7_READ 2 +#define CMD_V7_WRITE 3 +#define CMD_V7_ERASE 4 +#define CMD_V7_ERASE_AP 5 +#define CMD_V7_SENSOR_ID 6 + +#define v7_CMD_IDLE 0 +#define v7_CMD_WRITE_FW 1 +#define v7_CMD_WRITE_CONFIG 2 +#define v7_CMD_WRITE_LOCKDOWN 3 +#define v7_CMD_WRITE_GUEST_CODE 4 +#define v7_CMD_READ_CONFIG 5 +#define v7_CMD_ERASE_ALL 6 +#define v7_CMD_ERASE_UI_FIRMWARE 7 +#define v7_CMD_ERASE_UI_CONFIG 8 +#define v7_CMD_ERASE_BL_CONFIG 9 +#define v7_CMD_ERASE_DISP_CONFIG 10 +#define v7_CMD_ERASE_FLASH_CONFIG 11 +#define v7_CMD_ERASE_GUEST_CODE 12 +#define v7_CMD_ENABLE_FLASH_PROG 13 + +#define v7_UI_CONFIG_AREA 0 +#define v7_PM_CONFIG_AREA 1 +#define v7_BL_CONFIG_AREA 2 +#define v7_DP_CONFIG_AREA 3 +#define v7_FLASH_CONFIG_AREA 4 + +/* F34 V7 partition IDs */ +#define BOOTLOADER_PARTITION 1 +#define DEVICE_CONFIG_PARTITION 2 +#define FLASH_CONFIG_PARTITION 3 +#define MANUFACTURING_BLOCK_PARTITION 4 +#define GUEST_SERIALIZATION_PARTITION 5 +#define GLOBAL_PARAMETERS_PARTITION 6 +#define CORE_CODE_PARTITION 7 +#define CORE_CONFIG_PARTITION 8 +#define GUEST_CODE_PARTITION 9 +#define DISPLAY_CONFIG_PARTITION 10 + +/* F34 V7 container IDs */ +#define TOP_LEVEL_CONTAINER 0 +#define UI_CONTAINER 1 +#define UI_CONFIG_CONTAINER 2 +#define BL_CONTAINER 3 +#define BL_IMAGE_CONTAINER 4 +#define BL_CONFIG_CONTAINER 5 +#define BL_LOCKDOWN_INFO_CONTAINER 6 +#define PERMANENT_CONFIG_CONTAINER 7 +#define GUEST_CODE_CONTAINER 8 +#define BL_PROTOCOL_DESCRIPTOR_CONTAINER 9 +#define UI_PROTOCOL_DESCRIPTOR_CONTAINER 10 +#define RMI_SELF_DISCOVERY_CONTAINER 11 +#define RMI_PAGE_CONTENT_CONTAINER 12 +#define GENERAL_INFORMATION_CONTAINER 13 +#define DEVICE_CONFIG_CONTAINER 14 +#define FLASH_CONFIG_CONTAINER 15 +#define GUEST_SERIALIZATION_CONTAINER 16 +#define GLOBAL_PARAMETERS_CONTAINER 17 +#define CORE_CODE_CONTAINER 18 +#define CORE_CONFIG_CONTAINER 19 +#define DISPLAY_CONFIG_CONTAINER 20 + +struct f34v7_query_1_7 { + u8 bl_minor_revision; /* query 1 */ + u8 bl_major_revision; + __le32 bl_fw_id; /* query 2 */ + u8 minimum_write_size; /* query 3 */ + __le16 block_size; + __le16 flash_page_size; + __le16 adjustable_partition_area_size; /* query 4 */ + __le16 flash_config_length; /* query 5 */ + __le16 payload_length; /* query 6 */ + u8 partition_support[4]; /* query 7 */ +} __packed; + +struct f34v7_data_1_5 { + u8 partition_id; + __le16 block_offset; + __le16 transfer_length; + u8 command; + u8 payload[2]; +} __packed; + +struct block_data { + const void *data; + int size; +}; + +struct partition_table { + u8 partition_id; + u8 byte_1_reserved; + __le16 partition_length; + __le16 start_physical_address; + __le16 partition_properties; +} __packed; + +struct physical_address { + u16 ui_firmware; + u16 ui_config; + u16 dp_config; + u16 guest_code; +}; + +struct container_descriptor { + __le32 content_checksum; + __le16 container_id; + u8 minor_version; + u8 major_version; + u8 reserved_08; + u8 reserved_09; + u8 reserved_0a; + u8 reserved_0b; + u8 container_option_flags[4]; + __le32 content_options_length; + __le32 content_options_address; + __le32 content_length; + __le32 content_address; +} __packed; + +struct block_count { + u16 ui_firmware; + u16 ui_config; + u16 dp_config; + u16 fl_config; + u16 pm_config; + u16 bl_config; + u16 lockdown; + u16 guest_code; +}; + +struct image_header_10 { + __le32 checksum; + u8 reserved_04; + u8 reserved_05; + u8 minor_header_version; + u8 major_header_version; + u8 reserved_08; + u8 reserved_09; + u8 reserved_0a; + u8 reserved_0b; + __le32 top_level_container_start_addr; +}; + +struct image_metadata { + bool contains_firmware_id; + bool contains_bootloader; + bool contains_display_cfg; + bool contains_guest_code; + bool contains_flash_config; + unsigned int firmware_id; + unsigned int checksum; + unsigned int bootloader_size; + unsigned int display_cfg_offset; + unsigned char bl_version; + unsigned char product_id[PRODUCT_ID_SIZE + 1]; + unsigned char cstmr_product_id[PRODUCT_ID_SIZE + 1]; + struct block_data bootloader; + struct block_data ui_firmware; + struct block_data ui_config; + struct block_data dp_config; + struct block_data fl_config; + struct block_data bl_config; + struct block_data guest_code; + struct block_data lockdown; + struct block_count blkcount; + struct physical_address phyaddr; +}; + +struct register_offset { + u8 properties; + u8 properties_2; + u8 block_size; + u8 block_count; + u8 gc_block_count; + u8 flash_status; + u8 partition_id; + u8 block_number; + u8 transfer_length; + u8 flash_cmd; + u8 payload; +}; + struct rmi_f34_firmware { __le32 checksum; u8 pad1[3]; @@ -56,13 +266,49 @@ struct f34v5_data { struct mutex flash_mutex; }; +struct f34v7_data { + bool has_display_cfg; + bool has_guest_code; + bool force_update; + bool in_bl_mode; + u8 *read_config_buf; + size_t read_config_buf_size; + u8 command; + u8 flash_status; + u16 block_size; + u16 config_block_count; + u16 config_size; + u16 config_area; + u16 flash_config_length; + u16 payload_length; + u8 partitions; + u16 partition_table_bytes; + bool new_partition_table; + + struct register_offset off; + struct block_count blkcount; + struct physical_address phyaddr; + struct image_metadata img; + + const void *config_data; + const void *image; +}; + struct f34_data { struct rmi_function *fn; + u8 bl_version; unsigned char bootloader_id[5]; - unsigned char configuration_id[9]; + unsigned char configuration_id[CONFIG_ID_SIZE*2 + 1]; - struct f34v5_data v5; + union { + struct f34v5_data v5; + struct f34v7_data v7; + }; }; +int rmi_f34v7_start_reflash(struct f34_data *f34, const struct firmware *fw); +int rmi_f34v7_do_reflash(struct f34_data *f34, const struct firmware *fw); +int rmi_f34v7_probe(struct f34_data *f34); + #endif /* _RMI_F34_H */ diff --git a/drivers/input/rmi4/rmi_f34v7.c b/drivers/input/rmi4/rmi_f34v7.c new file mode 100644 index 000000000000..ca31f9539d9b --- /dev/null +++ b/drivers/input/rmi4/rmi_f34v7.c @@ -0,0 +1,1372 @@ +/* + * Copyright (c) 2016, Zodiac Inflight Innovations + * Copyright (c) 2007-2016, Synaptics Incorporated + * Copyright (C) 2012 Alexandra Chin + * Copyright (C) 2012 Scott Lin + * + * 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 +#include +#include +#include +#include +#include + +#include "rmi_driver.h" +#include "rmi_f34.h" + +static int rmi_f34v7_read_flash_status(struct f34_data *f34) +{ + u8 status; + u8 command; + int ret; + + ret = rmi_read_block(f34->fn->rmi_dev, + f34->fn->fd.data_base_addr + f34->v7.off.flash_status, + &status, + sizeof(status)); + if (ret < 0) { + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, + "%s: Failed to read flash status\n", __func__); + return ret; + } + + f34->v7.in_bl_mode = status >> 7; + f34->v7.flash_status = status & 0x1f; + + if (f34->v7.flash_status != 0x00) { + dev_err(&f34->fn->dev, "%s: status=%d, command=0x%02x\n", + __func__, f34->v7.flash_status, f34->v7.command); + } + + ret = rmi_read_block(f34->fn->rmi_dev, + f34->fn->fd.data_base_addr + f34->v7.off.flash_cmd, + &command, + sizeof(command)); + if (ret < 0) { + dev_err(&f34->fn->dev, "%s: Failed to read flash command\n", + __func__); + return ret; + } + + f34->v7.command = command; + + return 0; +} + +static int rmi_f34v7_wait_for_idle(struct f34_data *f34, int timeout_ms) +{ + int count = 0; + int timeout_count = ((timeout_ms * 1000) / MAX_SLEEP_TIME_US) + 1; + + do { + usleep_range(MIN_SLEEP_TIME_US, MAX_SLEEP_TIME_US); + + count++; + + rmi_f34v7_read_flash_status(f34); + + if ((f34->v7.command == v7_CMD_IDLE) + && (f34->v7.flash_status == 0x00)) { + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, + "Idle status detected\n"); + return 0; + } + } while (count < timeout_count); + + dev_err(&f34->fn->dev, + "%s: Timed out waiting for idle status\n", __func__); + + return -ETIMEDOUT; +} + +static int rmi_f34v7_write_command_single_transaction(struct f34_data *f34, + u8 cmd) +{ + int ret; + u8 base; + struct f34v7_data_1_5 data_1_5; + + base = f34->fn->fd.data_base_addr; + + memset(&data_1_5, 0, sizeof(data_1_5)); + + switch (cmd) { + case v7_CMD_ERASE_ALL: + data_1_5.partition_id = CORE_CODE_PARTITION; + data_1_5.command = CMD_V7_ERASE_AP; + break; + case v7_CMD_ERASE_UI_FIRMWARE: + data_1_5.partition_id = CORE_CODE_PARTITION; + data_1_5.command = CMD_V7_ERASE; + break; + case v7_CMD_ERASE_BL_CONFIG: + data_1_5.partition_id = GLOBAL_PARAMETERS_PARTITION; + data_1_5.command = CMD_V7_ERASE; + break; + case v7_CMD_ERASE_UI_CONFIG: + data_1_5.partition_id = CORE_CONFIG_PARTITION; + data_1_5.command = CMD_V7_ERASE; + break; + case v7_CMD_ERASE_DISP_CONFIG: + data_1_5.partition_id = DISPLAY_CONFIG_PARTITION; + data_1_5.command = CMD_V7_ERASE; + break; + case v7_CMD_ERASE_FLASH_CONFIG: + data_1_5.partition_id = FLASH_CONFIG_PARTITION; + data_1_5.command = CMD_V7_ERASE; + break; + case v7_CMD_ERASE_GUEST_CODE: + data_1_5.partition_id = GUEST_CODE_PARTITION; + data_1_5.command = CMD_V7_ERASE; + break; + case v7_CMD_ENABLE_FLASH_PROG: + data_1_5.partition_id = BOOTLOADER_PARTITION; + data_1_5.command = CMD_V7_ENTER_BL; + break; + } + + data_1_5.payload[0] = f34->bootloader_id[0]; + data_1_5.payload[1] = f34->bootloader_id[1]; + + ret = rmi_write_block(f34->fn->rmi_dev, + base + f34->v7.off.partition_id, + &data_1_5, sizeof(data_1_5)); + if (ret < 0) { + dev_err(&f34->fn->dev, + "%s: Failed to write single transaction command\n", + __func__); + return ret; + } + + return 0; +} + +static int rmi_f34v7_write_command(struct f34_data *f34, u8 cmd) +{ + int ret; + u8 base; + u8 command; + + base = f34->fn->fd.data_base_addr; + + switch (cmd) { + case v7_CMD_WRITE_FW: + case v7_CMD_WRITE_CONFIG: + case v7_CMD_WRITE_GUEST_CODE: + command = CMD_V7_WRITE; + break; + case v7_CMD_READ_CONFIG: + command = CMD_V7_READ; + break; + case v7_CMD_ERASE_ALL: + command = CMD_V7_ERASE_AP; + break; + case v7_CMD_ERASE_UI_FIRMWARE: + case v7_CMD_ERASE_BL_CONFIG: + case v7_CMD_ERASE_UI_CONFIG: + case v7_CMD_ERASE_DISP_CONFIG: + case v7_CMD_ERASE_FLASH_CONFIG: + case v7_CMD_ERASE_GUEST_CODE: + command = CMD_V7_ERASE; + break; + case v7_CMD_ENABLE_FLASH_PROG: + command = CMD_V7_ENTER_BL; + break; + default: + dev_err(&f34->fn->dev, "%s: Invalid command 0x%02x\n", + __func__, cmd); + return -EINVAL; + } + + f34->v7.command = command; + + switch (cmd) { + case v7_CMD_ERASE_ALL: + case v7_CMD_ERASE_UI_FIRMWARE: + case v7_CMD_ERASE_BL_CONFIG: + case v7_CMD_ERASE_UI_CONFIG: + case v7_CMD_ERASE_DISP_CONFIG: + case v7_CMD_ERASE_FLASH_CONFIG: + case v7_CMD_ERASE_GUEST_CODE: + case v7_CMD_ENABLE_FLASH_PROG: + ret = rmi_f34v7_write_command_single_transaction(f34, cmd); + if (ret < 0) + return ret; + else + return 0; + default: + break; + } + + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, "%s: writing cmd %02X\n", + __func__, command); + + ret = rmi_write_block(f34->fn->rmi_dev, + base + f34->v7.off.flash_cmd, + &command, sizeof(command)); + if (ret < 0) { + dev_err(&f34->fn->dev, "%s: Failed to write flash command\n", + __func__); + return ret; + } + + return 0; +} + +static int rmi_f34v7_write_partition_id(struct f34_data *f34, u8 cmd) +{ + int ret; + u8 base; + u8 partition; + + base = f34->fn->fd.data_base_addr; + + switch (cmd) { + case v7_CMD_WRITE_FW: + partition = CORE_CODE_PARTITION; + break; + case v7_CMD_WRITE_CONFIG: + case v7_CMD_READ_CONFIG: + if (f34->v7.config_area == v7_UI_CONFIG_AREA) + partition = CORE_CONFIG_PARTITION; + else if (f34->v7.config_area == v7_DP_CONFIG_AREA) + partition = DISPLAY_CONFIG_PARTITION; + else if (f34->v7.config_area == v7_PM_CONFIG_AREA) + partition = GUEST_SERIALIZATION_PARTITION; + else if (f34->v7.config_area == v7_BL_CONFIG_AREA) + partition = GLOBAL_PARAMETERS_PARTITION; + else if (f34->v7.config_area == v7_FLASH_CONFIG_AREA) + partition = FLASH_CONFIG_PARTITION; + break; + case v7_CMD_WRITE_GUEST_CODE: + partition = GUEST_CODE_PARTITION; + break; + case v7_CMD_ERASE_ALL: + partition = CORE_CODE_PARTITION; + break; + case v7_CMD_ERASE_BL_CONFIG: + partition = GLOBAL_PARAMETERS_PARTITION; + break; + case v7_CMD_ERASE_UI_CONFIG: + partition = CORE_CONFIG_PARTITION; + break; + case v7_CMD_ERASE_DISP_CONFIG: + partition = DISPLAY_CONFIG_PARTITION; + break; + case v7_CMD_ERASE_FLASH_CONFIG: + partition = FLASH_CONFIG_PARTITION; + break; + case v7_CMD_ERASE_GUEST_CODE: + partition = GUEST_CODE_PARTITION; + break; + case v7_CMD_ENABLE_FLASH_PROG: + partition = BOOTLOADER_PARTITION; + break; + default: + dev_err(&f34->fn->dev, "%s: Invalid command 0x%02x\n", + __func__, cmd); + return -EINVAL; + } + + ret = rmi_write_block(f34->fn->rmi_dev, + base + f34->v7.off.partition_id, + &partition, sizeof(partition)); + if (ret < 0) { + dev_err(&f34->fn->dev, "%s: Failed to write partition ID\n", + __func__); + return ret; + } + + return 0; +} + +static int rmi_f34v7_read_f34v7_partition_table(struct f34_data *f34) +{ + int ret; + u8 base; + __le16 length; + u16 block_number = 0; + + base = f34->fn->fd.data_base_addr; + + f34->v7.config_area = v7_FLASH_CONFIG_AREA; + + ret = rmi_f34v7_write_partition_id(f34, v7_CMD_READ_CONFIG); + if (ret < 0) + return ret; + + ret = rmi_write_block(f34->fn->rmi_dev, + base + f34->v7.off.block_number, + &block_number, sizeof(block_number)); + if (ret < 0) { + dev_err(&f34->fn->dev, "%s: Failed to write block number\n", + __func__); + return ret; + } + + put_unaligned_le16(f34->v7.flash_config_length, &length); + + ret = rmi_write_block(f34->fn->rmi_dev, + base + f34->v7.off.transfer_length, + &length, sizeof(length)); + if (ret < 0) { + dev_err(&f34->fn->dev, "%s: Failed to write transfer length\n", + __func__); + return ret; + } + + ret = rmi_f34v7_write_command(f34, v7_CMD_READ_CONFIG); + if (ret < 0) { + dev_err(&f34->fn->dev, "%s: Failed to write command\n", + __func__); + return ret; + } + + ret = rmi_f34v7_wait_for_idle(f34, WRITE_WAIT_MS); + if (ret < 0) { + dev_err(&f34->fn->dev, "%s: Failed to wait for idle status\n", + __func__); + return ret; + } + + ret = rmi_read_block(f34->fn->rmi_dev, + base + f34->v7.off.payload, + f34->v7.read_config_buf, + f34->v7.partition_table_bytes); + if (ret < 0) { + dev_err(&f34->fn->dev, "%s: Failed to read block data\n", + __func__); + return ret; + } + + return 0; +} + +static void rmi_f34v7_parse_partition_table(struct f34_data *f34, + const void *partition_table, + struct block_count *blkcount, + struct physical_address *phyaddr) +{ + int i; + int index; + u16 partition_length; + u16 physical_address; + const struct partition_table *ptable; + + for (i = 0; i < f34->v7.partitions; i++) { + index = i * 8 + 2; + ptable = partition_table + index; + partition_length = le16_to_cpu(ptable->partition_length); + physical_address = le16_to_cpu(ptable->start_physical_address); + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, + "%s: Partition entry %d: %*ph\n", + __func__, i, sizeof(struct partition_table), ptable); + switch (ptable->partition_id & 0x1f) { + case CORE_CODE_PARTITION: + blkcount->ui_firmware = partition_length; + phyaddr->ui_firmware = physical_address; + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, + "%s: Core code block count: %d\n", + __func__, blkcount->ui_firmware); + break; + case CORE_CONFIG_PARTITION: + blkcount->ui_config = partition_length; + phyaddr->ui_config = physical_address; + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, + "%s: Core config block count: %d\n", + __func__, blkcount->ui_config); + break; + case DISPLAY_CONFIG_PARTITION: + blkcount->dp_config = partition_length; + phyaddr->dp_config = physical_address; + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, + "%s: Display config block count: %d\n", + __func__, blkcount->dp_config); + break; + case FLASH_CONFIG_PARTITION: + blkcount->fl_config = partition_length; + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, + "%s: Flash config block count: %d\n", + __func__, blkcount->fl_config); + break; + case GUEST_CODE_PARTITION: + blkcount->guest_code = partition_length; + phyaddr->guest_code = physical_address; + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, + "%s: Guest code block count: %d\n", + __func__, blkcount->guest_code); + break; + case GUEST_SERIALIZATION_PARTITION: + blkcount->pm_config = partition_length; + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, + "%s: Guest serialization block count: %d\n", + __func__, blkcount->pm_config); + break; + case GLOBAL_PARAMETERS_PARTITION: + blkcount->bl_config = partition_length; + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, + "%s: Global parameters block count: %d\n", + __func__, blkcount->bl_config); + break; + case DEVICE_CONFIG_PARTITION: + blkcount->lockdown = partition_length; + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, + "%s: Device config block count: %d\n", + __func__, blkcount->lockdown); + break; + } + } +} + +static int rmi_f34v7_read_queries_bl_version(struct f34_data *f34) +{ + int ret; + u8 base; + int offset; + u8 query_0; + struct f34v7_query_1_7 query_1_7; + + base = f34->fn->fd.query_base_addr; + + ret = rmi_read_block(f34->fn->rmi_dev, + base, + &query_0, + sizeof(query_0)); + if (ret < 0) { + dev_err(&f34->fn->dev, + "%s: Failed to read query 0\n", __func__); + return ret; + } + + offset = (query_0 & 0x7) + 1; + + ret = rmi_read_block(f34->fn->rmi_dev, + base + offset, + &query_1_7, + sizeof(query_1_7)); + if (ret < 0) { + dev_err(&f34->fn->dev, "%s: Failed to read queries 1 to 7\n", + __func__); + return ret; + } + + f34->bootloader_id[0] = query_1_7.bl_minor_revision; + f34->bootloader_id[1] = query_1_7.bl_major_revision; + + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, "Bootloader V%d.%d\n", + f34->bootloader_id[1], f34->bootloader_id[0]); + + return 0; +} + +static int rmi_f34v7_read_queries(struct f34_data *f34) +{ + int ret; + int i, j; + u8 base; + int offset; + u8 *ptable; + u8 query_0; + struct f34v7_query_1_7 query_1_7; + + base = f34->fn->fd.query_base_addr; + + ret = rmi_read_block(f34->fn->rmi_dev, + base, + &query_0, + sizeof(query_0)); + if (ret < 0) { + dev_err(&f34->fn->dev, + "%s: Failed to read query 0\n", __func__); + return ret; + } + + offset = (query_0 & 0x07) + 1; + + ret = rmi_read_block(f34->fn->rmi_dev, + base + offset, + &query_1_7, + sizeof(query_1_7)); + if (ret < 0) { + dev_err(&f34->fn->dev, "%s: Failed to read queries 1 to 7\n", + __func__); + return ret; + } + + f34->bootloader_id[0] = query_1_7.bl_minor_revision; + f34->bootloader_id[1] = query_1_7.bl_major_revision; + + f34->v7.block_size = le16_to_cpu(query_1_7.block_size); + f34->v7.flash_config_length = + le16_to_cpu(query_1_7.flash_config_length); + f34->v7.payload_length = le16_to_cpu(query_1_7.payload_length); + + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, "%s: f34->v7.block_size = %d\n", + __func__, f34->v7.block_size); + + f34->v7.off.flash_status = V7_FLASH_STATUS_OFFSET; + f34->v7.off.partition_id = V7_PARTITION_ID_OFFSET; + f34->v7.off.block_number = V7_BLOCK_NUMBER_OFFSET; + f34->v7.off.transfer_length = V7_TRANSFER_LENGTH_OFFSET; + f34->v7.off.flash_cmd = V7_COMMAND_OFFSET; + f34->v7.off.payload = V7_PAYLOAD_OFFSET; + + f34->v7.has_display_cfg = query_1_7.partition_support[1] & HAS_DISP_CFG; + f34->v7.has_guest_code = + query_1_7.partition_support[1] & HAS_GUEST_CODE; + + if (query_0 & HAS_CONFIG_ID) { + char f34_ctrl[CONFIG_ID_SIZE]; + int i = 0; + u8 *p = f34->configuration_id; + *p = '\0'; + + ret = rmi_read_block(f34->fn->rmi_dev, + f34->fn->fd.control_base_addr, + f34_ctrl, + sizeof(f34_ctrl)); + if (ret) + return ret; + + /* Eat leading zeros */ + while (i < sizeof(f34_ctrl) && !f34_ctrl[i]) + i++; + + for (; i < sizeof(f34_ctrl); i++) + p += snprintf(p, f34->configuration_id + + sizeof(f34->configuration_id) - p, + "%02X", f34_ctrl[i]); + + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, "Configuration ID: %s\n", + f34->configuration_id); + } + + f34->v7.partitions = 0; + for (i = 0; i < sizeof(query_1_7.partition_support); i++) + for (j = 0; j < 8; j++) + if (query_1_7.partition_support[i] & (1 << j)) + f34->v7.partitions++; + + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, "%s: Supported partitions: %*ph\n", + __func__, sizeof(query_1_7.partition_support), + query_1_7.partition_support); + + + f34->v7.partition_table_bytes = f34->v7.partitions * 8 + 2; + + f34->v7.read_config_buf = devm_kzalloc(&f34->fn->dev, + f34->v7.partition_table_bytes, + GFP_KERNEL); + if (!f34->v7.read_config_buf) { + f34->v7.read_config_buf_size = 0; + return -ENOMEM; + } + + f34->v7.read_config_buf_size = f34->v7.partition_table_bytes; + ptable = f34->v7.read_config_buf; + + ret = rmi_f34v7_read_f34v7_partition_table(f34); + if (ret < 0) { + dev_err(&f34->fn->dev, "%s: Failed to read partition table\n", + __func__); + return ret; + } + + rmi_f34v7_parse_partition_table(f34, ptable, + &f34->v7.blkcount, &f34->v7.phyaddr); + + return 0; +} + +static int rmi_f34v7_check_ui_firmware_size(struct f34_data *f34) +{ + u16 block_count; + + block_count = f34->v7.img.ui_firmware.size / f34->v7.block_size; + + if (block_count != f34->v7.blkcount.ui_firmware) { + dev_err(&f34->fn->dev, + "UI firmware size mismatch: %d != %d\n", + block_count, f34->v7.blkcount.ui_firmware); + return -EINVAL; + } + + return 0; +} + +static int rmi_f34v7_check_ui_config_size(struct f34_data *f34) +{ + u16 block_count; + + block_count = f34->v7.img.ui_config.size / f34->v7.block_size; + + if (block_count != f34->v7.blkcount.ui_config) { + dev_err(&f34->fn->dev, "UI config size mismatch\n"); + return -EINVAL; + } + + return 0; +} + +static int rmi_f34v7_check_dp_config_size(struct f34_data *f34) +{ + u16 block_count; + + block_count = f34->v7.img.dp_config.size / f34->v7.block_size; + + if (block_count != f34->v7.blkcount.dp_config) { + dev_err(&f34->fn->dev, "Display config size mismatch\n"); + return -EINVAL; + } + + return 0; +} + +static int rmi_f34v7_check_guest_code_size(struct f34_data *f34) +{ + u16 block_count; + + block_count = f34->v7.img.guest_code.size / f34->v7.block_size; + if (block_count != f34->v7.blkcount.guest_code) { + dev_err(&f34->fn->dev, "Guest code size mismatch\n"); + return -EINVAL; + } + + return 0; +} + +static int rmi_f34v7_check_bl_config_size(struct f34_data *f34) +{ + u16 block_count; + + block_count = f34->v7.img.bl_config.size / f34->v7.block_size; + + if (block_count != f34->v7.blkcount.bl_config) { + dev_err(&f34->fn->dev, "Bootloader config size mismatch\n"); + return -EINVAL; + } + + return 0; +} + +static int rmi_f34v7_erase_config(struct f34_data *f34) +{ + int ret; + + dev_info(&f34->fn->dev, "Erasing config...\n"); + + switch (f34->v7.config_area) { + case v7_UI_CONFIG_AREA: + ret = rmi_f34v7_write_command(f34, v7_CMD_ERASE_UI_CONFIG); + if (ret < 0) + return ret; + break; + case v7_DP_CONFIG_AREA: + ret = rmi_f34v7_write_command(f34, v7_CMD_ERASE_DISP_CONFIG); + if (ret < 0) + return ret; + break; + case v7_BL_CONFIG_AREA: + ret = rmi_f34v7_write_command(f34, v7_CMD_ERASE_BL_CONFIG); + if (ret < 0) + return ret; + break; + } + + ret = rmi_f34v7_wait_for_idle(f34, ENABLE_WAIT_MS); + if (ret < 0) + return ret; + + return ret; +} + +static int rmi_f34v7_erase_guest_code(struct f34_data *f34) +{ + int ret; + + dev_info(&f34->fn->dev, "Erasing guest code...\n"); + + ret = rmi_f34v7_write_command(f34, v7_CMD_ERASE_GUEST_CODE); + if (ret < 0) + return ret; + + ret = rmi_f34v7_wait_for_idle(f34, ENABLE_WAIT_MS); + if (ret < 0) + return ret; + + return 0; +} + +static int rmi_f34v7_erase_all(struct f34_data *f34) +{ + int ret; + + dev_info(&f34->fn->dev, "Erasing firmware...\n"); + + ret = rmi_f34v7_write_command(f34, v7_CMD_ERASE_UI_FIRMWARE); + if (ret < 0) + return ret; + + ret = rmi_f34v7_wait_for_idle(f34, ENABLE_WAIT_MS); + if (ret < 0) + return ret; + + f34->v7.config_area = v7_UI_CONFIG_AREA; + ret = rmi_f34v7_erase_config(f34); + if (ret < 0) + return ret; + + if (f34->v7.has_display_cfg) { + f34->v7.config_area = v7_DP_CONFIG_AREA; + ret = rmi_f34v7_erase_config(f34); + if (ret < 0) + return ret; + } + + if (f34->v7.new_partition_table && f34->v7.has_guest_code) { + ret = rmi_f34v7_erase_guest_code(f34); + if (ret < 0) + return ret; + } + + return 0; +} + +static int rmi_f34v7_read_f34v7_blocks(struct f34_data *f34, u16 block_cnt, + u8 command) +{ + int ret; + u8 base; + __le16 length; + u16 transfer; + u16 max_transfer; + u16 remaining = block_cnt; + u16 block_number = 0; + u16 index = 0; + + base = f34->fn->fd.data_base_addr; + + ret = rmi_f34v7_write_partition_id(f34, command); + if (ret < 0) + return ret; + + ret = rmi_write_block(f34->fn->rmi_dev, + base + f34->v7.off.block_number, + &block_number, sizeof(block_number)); + if (ret < 0) { + dev_err(&f34->fn->dev, "%s: Failed to write block number\n", + __func__); + return ret; + } + + max_transfer = min(f34->v7.payload_length, + (u16)(PAGE_SIZE / f34->v7.block_size)); + + do { + transfer = min(remaining, max_transfer); + put_unaligned_le16(transfer, &length); + + ret = rmi_write_block(f34->fn->rmi_dev, + base + f34->v7.off.transfer_length, + &length, sizeof(length)); + if (ret < 0) { + dev_err(&f34->fn->dev, + "%s: Write transfer length fail (%d remaining)\n", + __func__, remaining); + return ret; + } + + ret = rmi_f34v7_write_command(f34, command); + if (ret < 0) + return ret; + + ret = rmi_f34v7_wait_for_idle(f34, ENABLE_WAIT_MS); + if (ret < 0) { + dev_err(&f34->fn->dev, + "%s: Wait for idle failed (%d blks remaining)\n", + __func__, remaining); + return ret; + } + + ret = rmi_read_block(f34->fn->rmi_dev, + base + f34->v7.off.payload, + &f34->v7.read_config_buf[index], + transfer * f34->v7.block_size); + if (ret < 0) { + dev_err(&f34->fn->dev, + "%s: Read block failed (%d blks remaining)\n", + __func__, remaining); + return ret; + } + + index += (transfer * f34->v7.block_size); + remaining -= transfer; + } while (remaining); + + return 0; +} + +static int rmi_f34v7_write_f34v7_blocks(struct f34_data *f34, + const void *block_ptr, u16 block_cnt, + u8 command) +{ + int ret; + u8 base; + __le16 length; + u16 transfer; + u16 max_transfer; + u16 remaining = block_cnt; + u16 block_number = 0; + + base = f34->fn->fd.data_base_addr; + + ret = rmi_f34v7_write_partition_id(f34, command); + if (ret < 0) + return ret; + + ret = rmi_write_block(f34->fn->rmi_dev, + base + f34->v7.off.block_number, + &block_number, sizeof(block_number)); + if (ret < 0) { + dev_err(&f34->fn->dev, "%s: Failed to write block number\n", + __func__); + return ret; + } + + if (f34->v7.payload_length > (PAGE_SIZE / f34->v7.block_size)) + max_transfer = PAGE_SIZE / f34->v7.block_size; + else + max_transfer = f34->v7.payload_length; + + do { + transfer = min(remaining, max_transfer); + put_unaligned_le16(transfer, &length); + + ret = rmi_write_block(f34->fn->rmi_dev, + base + f34->v7.off.transfer_length, + &length, sizeof(length)); + if (ret < 0) { + dev_err(&f34->fn->dev, + "%s: Write transfer length fail (%d remaining)\n", + __func__, remaining); + return ret; + } + + ret = rmi_f34v7_write_command(f34, command); + if (ret < 0) + return ret; + + ret = rmi_write_block(f34->fn->rmi_dev, + base + f34->v7.off.payload, + block_ptr, transfer * f34->v7.block_size); + if (ret < 0) { + dev_err(&f34->fn->dev, + "%s: Failed writing data (%d blks remaining)\n", + __func__, remaining); + return ret; + } + + ret = rmi_f34v7_wait_for_idle(f34, ENABLE_WAIT_MS); + if (ret < 0) { + dev_err(&f34->fn->dev, + "%s: Failed wait for idle (%d blks remaining)\n", + __func__, remaining); + return ret; + } + + block_ptr += (transfer * f34->v7.block_size); + remaining -= transfer; + } while (remaining); + + return 0; +} + +static int rmi_f34v7_write_config(struct f34_data *f34) +{ + return rmi_f34v7_write_f34v7_blocks(f34, f34->v7.config_data, + f34->v7.config_block_count, + v7_CMD_WRITE_CONFIG); +} + +static int rmi_f34v7_write_ui_config(struct f34_data *f34) +{ + f34->v7.config_area = v7_UI_CONFIG_AREA; + f34->v7.config_data = f34->v7.img.ui_config.data; + f34->v7.config_size = f34->v7.img.ui_config.size; + f34->v7.config_block_count = f34->v7.config_size / f34->v7.block_size; + + return rmi_f34v7_write_config(f34); +} + +static int rmi_f34v7_write_dp_config(struct f34_data *f34) +{ + f34->v7.config_area = v7_DP_CONFIG_AREA; + f34->v7.config_data = f34->v7.img.dp_config.data; + f34->v7.config_size = f34->v7.img.dp_config.size; + f34->v7.config_block_count = f34->v7.config_size / f34->v7.block_size; + + return rmi_f34v7_write_config(f34); +} + +static int rmi_f34v7_write_guest_code(struct f34_data *f34) +{ + return rmi_f34v7_write_f34v7_blocks(f34, f34->v7.img.guest_code.data, + f34->v7.img.guest_code.size / + f34->v7.block_size, + v7_CMD_WRITE_GUEST_CODE); +} + +static int rmi_f34v7_write_flash_config(struct f34_data *f34) +{ + int ret; + + f34->v7.config_area = v7_FLASH_CONFIG_AREA; + f34->v7.config_data = f34->v7.img.fl_config.data; + f34->v7.config_size = f34->v7.img.fl_config.size; + f34->v7.config_block_count = f34->v7.config_size / f34->v7.block_size; + + if (f34->v7.config_block_count != f34->v7.blkcount.fl_config) { + dev_err(&f34->fn->dev, "%s: Flash config size mismatch\n", + __func__); + return -EINVAL; + } + + ret = rmi_f34v7_write_command(f34, v7_CMD_ERASE_FLASH_CONFIG); + if (ret < 0) + return ret; + + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, + "%s: Erase flash config command written\n", __func__); + + ret = rmi_f34v7_wait_for_idle(f34, ENABLE_WAIT_MS); + if (ret < 0) + return ret; + + ret = rmi_f34v7_write_config(f34); + if (ret < 0) + return ret; + + return 0; +} + +static int rmi_f34v7_write_partition_table(struct f34_data *f34) +{ + u16 block_count; + int ret; + + block_count = f34->v7.blkcount.bl_config; + f34->v7.config_area = v7_BL_CONFIG_AREA; + f34->v7.config_size = f34->v7.block_size * block_count; + devm_kfree(&f34->fn->dev, f34->v7.read_config_buf); + f34->v7.read_config_buf = devm_kzalloc(&f34->fn->dev, + f34->v7.config_size, GFP_KERNEL); + if (!f34->v7.read_config_buf) { + f34->v7.read_config_buf_size = 0; + return -ENOMEM; + } + + f34->v7.read_config_buf_size = f34->v7.config_size; + + ret = rmi_f34v7_read_f34v7_blocks(f34, block_count, v7_CMD_READ_CONFIG); + if (ret < 0) + return ret; + + ret = rmi_f34v7_erase_config(f34); + if (ret < 0) + return ret; + + ret = rmi_f34v7_write_flash_config(f34); + if (ret < 0) + return ret; + + f34->v7.config_area = v7_BL_CONFIG_AREA; + f34->v7.config_data = f34->v7.read_config_buf; + f34->v7.config_size = f34->v7.img.bl_config.size; + f34->v7.config_block_count = f34->v7.config_size / f34->v7.block_size; + + ret = rmi_f34v7_write_config(f34); + if (ret < 0) + return ret; + + return 0; +} + +static int rmi_f34v7_write_firmware(struct f34_data *f34) +{ + u16 blk_count; + + blk_count = f34->v7.img.ui_firmware.size / f34->v7.block_size; + + return rmi_f34v7_write_f34v7_blocks(f34, f34->v7.img.ui_firmware.data, + blk_count, v7_CMD_WRITE_FW); +} + +static void rmi_f34v7_compare_partition_tables(struct f34_data *f34) +{ + if (f34->v7.phyaddr.ui_firmware != f34->v7.img.phyaddr.ui_firmware) { + f34->v7.new_partition_table = true; + return; + } + + if (f34->v7.phyaddr.ui_config != f34->v7.img.phyaddr.ui_config) { + f34->v7.new_partition_table = true; + return; + } + + if (f34->v7.has_display_cfg && + f34->v7.phyaddr.dp_config != f34->v7.img.phyaddr.dp_config) { + f34->v7.new_partition_table = true; + return; + } + + if (f34->v7.has_guest_code && + f34->v7.phyaddr.guest_code != f34->v7.img.phyaddr.guest_code) { + f34->v7.new_partition_table = true; + return; + } + + f34->v7.new_partition_table = false; +} + +static void rmi_f34v7_parse_img_header_10_bl_container(struct f34_data *f34, + const void *image) +{ + int i; + int num_of_containers; + unsigned int addr; + unsigned int container_id; + unsigned int length; + const void *content; + const struct container_descriptor *descriptor; + + num_of_containers = f34->v7.img.bootloader.size / 4 - 1; + + for (i = 1; i <= num_of_containers; i++) { + addr = get_unaligned_le32(f34->v7.img.bootloader.data + i * 4); + descriptor = image + addr; + container_id = le16_to_cpu(descriptor->container_id); + content = image + le32_to_cpu(descriptor->content_address); + length = le32_to_cpu(descriptor->content_length); + switch (container_id) { + case BL_CONFIG_CONTAINER: + case GLOBAL_PARAMETERS_CONTAINER: + f34->v7.img.bl_config.data = content; + f34->v7.img.bl_config.size = length; + break; + case BL_LOCKDOWN_INFO_CONTAINER: + case DEVICE_CONFIG_CONTAINER: + f34->v7.img.lockdown.data = content; + f34->v7.img.lockdown.size = length; + break; + default: + break; + } + } +} + +static void rmi_f34v7_parse_image_header_10(struct f34_data *f34) +{ + unsigned int i; + unsigned int num_of_containers; + unsigned int addr; + unsigned int offset; + unsigned int container_id; + unsigned int length; + const void *image = f34->v7.image; + const u8 *content; + const struct container_descriptor *descriptor; + const struct image_header_10 *header = image; + + f34->v7.img.checksum = le32_to_cpu(header->checksum); + + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, "%s: f34->v7.img.checksum=%X\n", + __func__, f34->v7.img.checksum); + + /* address of top level container */ + offset = le32_to_cpu(header->top_level_container_start_addr); + descriptor = image + offset; + + /* address of top level container content */ + offset = le32_to_cpu(descriptor->content_address); + num_of_containers = le32_to_cpu(descriptor->content_length) / 4; + + for (i = 0; i < num_of_containers; i++) { + addr = get_unaligned_le32(image + offset); + offset += 4; + descriptor = image + addr; + container_id = le16_to_cpu(descriptor->container_id); + content = image + le32_to_cpu(descriptor->content_address); + length = le32_to_cpu(descriptor->content_length); + + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, + "%s: container_id=%d, length=%d\n", __func__, + container_id, length); + + switch (container_id) { + case UI_CONTAINER: + case CORE_CODE_CONTAINER: + f34->v7.img.ui_firmware.data = content; + f34->v7.img.ui_firmware.size = length; + break; + case UI_CONFIG_CONTAINER: + case CORE_CONFIG_CONTAINER: + f34->v7.img.ui_config.data = content; + f34->v7.img.ui_config.size = length; + break; + case BL_CONTAINER: + f34->v7.img.bl_version = *content; + f34->v7.img.bootloader.data = content; + f34->v7.img.bootloader.size = length; + rmi_f34v7_parse_img_header_10_bl_container(f34, image); + break; + case GUEST_CODE_CONTAINER: + f34->v7.img.contains_guest_code = true; + f34->v7.img.guest_code.data = content; + f34->v7.img.guest_code.size = length; + break; + case DISPLAY_CONFIG_CONTAINER: + f34->v7.img.contains_display_cfg = true; + f34->v7.img.dp_config.data = content; + f34->v7.img.dp_config.size = length; + break; + case FLASH_CONFIG_CONTAINER: + f34->v7.img.contains_flash_config = true; + f34->v7.img.fl_config.data = content; + f34->v7.img.fl_config.size = length; + break; + case GENERAL_INFORMATION_CONTAINER: + f34->v7.img.contains_firmware_id = true; + f34->v7.img.firmware_id = + get_unaligned_le32(content + 4); + break; + default: + break; + } + } +} + +static int rmi_f34v7_parse_image_info(struct f34_data *f34) +{ + const struct image_header_10 *header = f34->v7.image; + + memset(&f34->v7.img, 0x00, sizeof(f34->v7.img)); + + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, + "%s: header->major_header_version = %d\n", + __func__, header->major_header_version); + + switch (header->major_header_version) { + case IMAGE_HEADER_VERSION_10: + rmi_f34v7_parse_image_header_10(f34); + break; + default: + dev_err(&f34->fn->dev, "Unsupported image file format %02X\n", + header->major_header_version); + return -EINVAL; + } + + if (!f34->v7.img.contains_flash_config) { + dev_err(&f34->fn->dev, "%s: No flash config in fw image\n", + __func__); + return -EINVAL; + } + + rmi_f34v7_parse_partition_table(f34, f34->v7.img.fl_config.data, + &f34->v7.img.blkcount, &f34->v7.img.phyaddr); + + rmi_f34v7_compare_partition_tables(f34); + + return 0; +} + +int rmi_f34v7_do_reflash(struct f34_data *f34, const struct firmware *fw) +{ + int ret; + + rmi_f34v7_read_queries_bl_version(f34); + + f34->v7.image = fw->data; + + ret = rmi_f34v7_parse_image_info(f34); + if (ret < 0) + goto fail; + + if (!f34->v7.new_partition_table) { + ret = rmi_f34v7_check_ui_firmware_size(f34); + if (ret < 0) + goto fail; + + ret = rmi_f34v7_check_ui_config_size(f34); + if (ret < 0) + goto fail; + + if (f34->v7.has_display_cfg && + f34->v7.img.contains_display_cfg) { + ret = rmi_f34v7_check_dp_config_size(f34); + if (ret < 0) + goto fail; + } + + if (f34->v7.has_guest_code && f34->v7.img.contains_guest_code) { + ret = rmi_f34v7_check_guest_code_size(f34); + if (ret < 0) + goto fail; + } + } else { + ret = rmi_f34v7_check_bl_config_size(f34); + if (ret < 0) + goto fail; + } + + ret = rmi_f34v7_erase_all(f34); + if (ret < 0) + goto fail; + + if (f34->v7.new_partition_table) { + ret = rmi_f34v7_write_partition_table(f34); + if (ret < 0) + goto fail; + dev_info(&f34->fn->dev, "%s: Partition table programmed\n", + __func__); + } + + dev_info(&f34->fn->dev, "Writing firmware (%d bytes)...\n", + f34->v7.img.ui_firmware.size); + + ret = rmi_f34v7_write_firmware(f34); + if (ret < 0) + goto fail; + + dev_info(&f34->fn->dev, "Writing config (%d bytes)...\n", + f34->v7.img.ui_config.size); + + f34->v7.config_area = v7_UI_CONFIG_AREA; + ret = rmi_f34v7_write_ui_config(f34); + if (ret < 0) + goto fail; + + if (f34->v7.has_display_cfg && f34->v7.img.contains_display_cfg) { + dev_info(&f34->fn->dev, "Writing display config...\n"); + + ret = rmi_f34v7_write_dp_config(f34); + if (ret < 0) + goto fail; + } + + if (f34->v7.new_partition_table) { + if (f34->v7.has_guest_code && f34->v7.img.contains_guest_code) { + dev_info(&f34->fn->dev, "Writing guest code...\n"); + + ret = rmi_f34v7_write_guest_code(f34); + if (ret < 0) + goto fail; + } + } + +fail: + return ret; +} + +static int rmi_f34v7_enter_flash_prog(struct f34_data *f34) +{ + int ret; + + ret = rmi_f34v7_read_flash_status(f34); + if (ret < 0) + return ret; + + if (f34->v7.in_bl_mode) + return 0; + + ret = rmi_f34v7_write_command(f34, v7_CMD_ENABLE_FLASH_PROG); + if (ret < 0) + return ret; + + ret = rmi_f34v7_wait_for_idle(f34, ENABLE_WAIT_MS); + if (ret < 0) + return ret; + + if (!f34->v7.in_bl_mode) { + dev_err(&f34->fn->dev, "%s: BL mode not entered\n", __func__); + return -EINVAL; + } + + return 0; +} + +int rmi_f34v7_start_reflash(struct f34_data *f34, const struct firmware *fw) +{ + int ret = 0; + + f34->v7.config_area = v7_UI_CONFIG_AREA; + f34->v7.image = fw->data; + + ret = rmi_f34v7_parse_image_info(f34); + if (ret < 0) + goto exit; + + if (!f34->v7.force_update && f34->v7.new_partition_table) { + dev_err(&f34->fn->dev, "%s: Partition table mismatch\n", + __func__); + ret = -EINVAL; + goto exit; + } + + dev_info(&f34->fn->dev, "Firmware image OK\n"); + + ret = rmi_f34v7_read_flash_status(f34); + if (ret < 0) + goto exit; + + if (f34->v7.in_bl_mode) { + dev_info(&f34->fn->dev, "%s: Device in bootloader mode\n", + __func__); + } + + rmi_f34v7_enter_flash_prog(f34); + + return 0; + +exit: + return ret; +} + +int rmi_f34v7_probe(struct f34_data *f34) +{ + int ret; + + /* Read bootloader version */ + ret = rmi_read_block(f34->fn->rmi_dev, + f34->fn->fd.query_base_addr + V7_BOOTLOADER_ID_OFFSET, + f34->bootloader_id, + sizeof(f34->bootloader_id)); + if (ret < 0) { + dev_err(&f34->fn->dev, "%s: Failed to read bootloader ID\n", + __func__); + return ret; + } + + if (f34->bootloader_id[1] == '5') { + f34->bl_version = 5; + } else if (f34->bootloader_id[1] == '6') { + f34->bl_version = 6; + } else if (f34->bootloader_id[1] == 7) { + f34->bl_version = 7; + } else { + dev_err(&f34->fn->dev, "%s: Unrecognized bootloader version\n", + __func__); + return -EINVAL; + } + + memset(&f34->v7.blkcount, 0x00, sizeof(f34->v7.blkcount)); + memset(&f34->v7.phyaddr, 0x00, sizeof(f34->v7.phyaddr)); + rmi_f34v7_read_queries(f34); + + f34->v7.force_update = false; + return 0; +} -- cgit v1.2.3 From 5a8a6b89c15766446d845671d574a9243b6d8786 Mon Sep 17 00:00:00 2001 From: Jingkui Wang Date: Mon, 12 Dec 2016 13:51:46 -0800 Subject: Input: drv260x - fix input device's parent assignment We were assigning I2C bus controller instead of client as parent device. Besides being logically wrong, it messed up with devm handling of input device. As a result we were leaving input device and event node behind after rmmod-ing the driver, which lead to a kernel oops if one were to access the event node later. Let's remove the assignment and rely on devm_input_allocate_device() to set it up properly for us. Signed-off-by: Jingkui Wang Fixes: 7132fe4f5687 ("Input: drv260x - add TI drv260x haptics driver") Cc: stable@vger.kernel.org Signed-off-by: Dmitry Torokhov --- drivers/input/misc/drv260x.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/misc/drv260x.c b/drivers/input/misc/drv260x.c index 9789d4fb6e51..251d64ca41ce 100644 --- a/drivers/input/misc/drv260x.c +++ b/drivers/input/misc/drv260x.c @@ -592,7 +592,6 @@ static int drv260x_probe(struct i2c_client *client, } haptics->input_dev->name = "drv260x:haptics"; - haptics->input_dev->dev.parent = client->dev.parent; haptics->input_dev->close = drv260x_close; input_set_drvdata(haptics->input_dev, haptics); input_set_capability(haptics->input_dev, EV_FF, FF_RUMBLE); -- cgit v1.2.3 From ba4cf3783854346051ed5a243220f37c0c865350 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sat, 10 Dec 2016 23:03:33 -0800 Subject: Input: drv260x - use temporary for &client->dev Let's introduce a temporary for "client->dev" is probe() as we use it quite a few times and "dev" is shorter. Signed-off-by: Dmitry Torokhov --- drivers/input/misc/drv260x.c | 37 +++++++++++++++---------------------- 1 file changed, 15 insertions(+), 22 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/misc/drv260x.c b/drivers/input/misc/drv260x.c index 251d64ca41ce..a26aa548dd66 100644 --- a/drivers/input/misc/drv260x.c +++ b/drivers/input/misc/drv260x.c @@ -514,10 +514,11 @@ static int drv260x_probe(struct i2c_client *client, const struct i2c_device_id *id) { const struct drv260x_platform_data *pdata = dev_get_platdata(&client->dev); + struct device *dev = &client->dev; struct drv260x_data *haptics; int error; - haptics = devm_kzalloc(&client->dev, sizeof(*haptics), GFP_KERNEL); + haptics = devm_kzalloc(dev, sizeof(*haptics), GFP_KERNEL); if (!haptics) return -ENOMEM; @@ -536,22 +537,20 @@ static int drv260x_probe(struct i2c_client *client, if (error) return error; } else { - dev_err(&client->dev, "Platform data not set\n"); + dev_err(dev, "Platform data not set\n"); return -ENODEV; } if (haptics->mode < DRV260X_LRA_MODE || haptics->mode > DRV260X_ERM_MODE) { - dev_err(&client->dev, - "Vibrator mode is invalid: %i\n", - haptics->mode); + dev_err(dev, "Vibrator mode is invalid: %i\n", haptics->mode); return -EINVAL; } if (haptics->library < DRV260X_LIB_EMPTY || haptics->library > DRV260X_ERM_LIB_F) { - dev_err(&client->dev, + dev_err(dev, "Library value is invalid: %i\n", haptics->library); return -EINVAL; } @@ -559,33 +558,30 @@ static int drv260x_probe(struct i2c_client *client, if (haptics->mode == DRV260X_LRA_MODE && haptics->library != DRV260X_LIB_EMPTY && haptics->library != DRV260X_LIB_LRA) { - dev_err(&client->dev, - "LRA Mode with ERM Library mismatch\n"); + dev_err(dev, "LRA Mode with ERM Library mismatch\n"); return -EINVAL; } if (haptics->mode == DRV260X_ERM_MODE && (haptics->library == DRV260X_LIB_EMPTY || haptics->library == DRV260X_LIB_LRA)) { - dev_err(&client->dev, - "ERM Mode with LRA Library mismatch\n"); + dev_err(dev, "ERM Mode with LRA Library mismatch\n"); return -EINVAL; } - haptics->regulator = devm_regulator_get(&client->dev, "vbat"); + haptics->regulator = devm_regulator_get(dev, "vbat"); if (IS_ERR(haptics->regulator)) { error = PTR_ERR(haptics->regulator); - dev_err(&client->dev, - "unable to get regulator, error: %d\n", error); + dev_err(dev, "unable to get regulator, error: %d\n", error); return error; } - haptics->enable_gpio = devm_gpiod_get_optional(&client->dev, "enable", + haptics->enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_HIGH); if (IS_ERR(haptics->enable_gpio)) return PTR_ERR(haptics->enable_gpio); - haptics->input_dev = devm_input_allocate_device(&client->dev); + haptics->input_dev = devm_input_allocate_device(dev); if (!haptics->input_dev) { dev_err(&client->dev, "Failed to allocate input device\n"); return -ENOMEM; @@ -599,8 +595,7 @@ static int drv260x_probe(struct i2c_client *client, error = input_ff_create_memless(haptics->input_dev, NULL, drv260x_haptics_play); if (error) { - dev_err(&client->dev, "input_ff_create() failed: %d\n", - error); + dev_err(dev, "input_ff_create() failed: %d\n", error); return error; } @@ -612,21 +607,19 @@ static int drv260x_probe(struct i2c_client *client, haptics->regmap = devm_regmap_init_i2c(client, &drv260x_regmap_config); if (IS_ERR(haptics->regmap)) { error = PTR_ERR(haptics->regmap); - dev_err(&client->dev, "Failed to allocate register map: %d\n", - error); + dev_err(dev, "Failed to allocate register map: %d\n", error); return error; } error = drv260x_init(haptics); if (error) { - dev_err(&client->dev, "Device init failed: %d\n", error); + dev_err(dev, "Device init failed: %d\n", error); return error; } error = input_register_device(haptics->input_dev); if (error) { - dev_err(&client->dev, "couldn't register input device: %d\n", - error); + dev_err(dev, "couldn't register input device: %d\n", error); return error; } -- cgit v1.2.3 From 34888602eb99df174e76eafeb613ad857f534ebb Mon Sep 17 00:00:00 2001 From: Jingkui Wang Date: Sat, 10 Dec 2016 22:44:10 -0800 Subject: Input: drv260x - use generic device properties Update driver drv260x to use generic device properties so that it can be used on non-DT systems. We also remove platform data as generic device properties work on static board code as well. Signed-off-by: Jingkui Wang Signed-off-by: Dmitry Torokhov --- drivers/input/misc/drv260x.c | 83 ++++++++++---------------------------------- 1 file changed, 19 insertions(+), 64 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/misc/drv260x.c b/drivers/input/misc/drv260x.c index a26aa548dd66..0a2b865b1000 100644 --- a/drivers/input/misc/drv260x.c +++ b/drivers/input/misc/drv260x.c @@ -18,8 +18,6 @@ #include #include #include -#include -#include #include #include #include @@ -27,7 +25,6 @@ #include #include -#include #define DRV260X_STATUS 0x0 #define DRV260X_MODE 0x1 @@ -468,86 +465,36 @@ static const struct regmap_config drv260x_regmap_config = { .cache_type = REGCACHE_NONE, }; -#ifdef CONFIG_OF -static int drv260x_parse_dt(struct device *dev, - struct drv260x_data *haptics) -{ - struct device_node *np = dev->of_node; - unsigned int voltage; - int error; - - error = of_property_read_u32(np, "mode", &haptics->mode); - if (error) { - dev_err(dev, "%s: No entry for mode\n", __func__); - return error; - } - - error = of_property_read_u32(np, "library-sel", &haptics->library); - if (error) { - dev_err(dev, "%s: No entry for library selection\n", - __func__); - return error; - } - - error = of_property_read_u32(np, "vib-rated-mv", &voltage); - if (!error) - haptics->rated_voltage = drv260x_calculate_voltage(voltage); - - - error = of_property_read_u32(np, "vib-overdrive-mv", &voltage); - if (!error) - haptics->overdrive_voltage = drv260x_calculate_voltage(voltage); - - return 0; -} -#else -static inline int drv260x_parse_dt(struct device *dev, - struct drv260x_data *haptics) -{ - dev_err(dev, "no platform data defined\n"); - - return -EINVAL; -} -#endif - static int drv260x_probe(struct i2c_client *client, const struct i2c_device_id *id) { - const struct drv260x_platform_data *pdata = dev_get_platdata(&client->dev); struct device *dev = &client->dev; struct drv260x_data *haptics; + u32 voltage; int error; haptics = devm_kzalloc(dev, sizeof(*haptics), GFP_KERNEL); if (!haptics) return -ENOMEM; - haptics->overdrive_voltage = DRV260X_DEF_OD_CLAMP_VOLT; - haptics->rated_voltage = DRV260X_DEF_RATED_VOLT; - - if (pdata) { - haptics->mode = pdata->mode; - haptics->library = pdata->library_selection; - if (pdata->vib_overdrive_voltage) - haptics->overdrive_voltage = drv260x_calculate_voltage(pdata->vib_overdrive_voltage); - if (pdata->vib_rated_voltage) - haptics->rated_voltage = drv260x_calculate_voltage(pdata->vib_rated_voltage); - } else if (client->dev.of_node) { - error = drv260x_parse_dt(&client->dev, haptics); - if (error) - return error; - } else { - dev_err(dev, "Platform data not set\n"); - return -ENODEV; + error = device_property_read_u32(dev, "mode", &haptics->mode); + if (error) { + dev_err(dev, "Can't fetch 'mode' property: %d\n", error); + return error; } - if (haptics->mode < DRV260X_LRA_MODE || haptics->mode > DRV260X_ERM_MODE) { dev_err(dev, "Vibrator mode is invalid: %i\n", haptics->mode); return -EINVAL; } + error = device_property_read_u32(dev, "library-sel", &haptics->library); + if (error) { + dev_err(dev, "Can't fetch 'library-sel' property: %d\n", error); + return error; + } + if (haptics->library < DRV260X_LIB_EMPTY || haptics->library > DRV260X_ERM_LIB_F) { dev_err(dev, @@ -569,6 +516,14 @@ static int drv260x_probe(struct i2c_client *client, return -EINVAL; } + error = device_property_read_u32(dev, "vib-rated-mv", &voltage); + haptics->rated_voltage = error ? DRV260X_DEF_RATED_VOLT : + drv260x_calculate_voltage(voltage); + + error = device_property_read_u32(dev, "vib-overdrive-mv", &voltage); + haptics->overdrive_voltage = error ? DRV260X_DEF_OD_CLAMP_VOLT : + drv260x_calculate_voltage(voltage); + haptics->regulator = devm_regulator_get(dev, "vbat"); if (IS_ERR(haptics->regulator)) { error = PTR_ERR(haptics->regulator); -- cgit v1.2.3 From f43d3ec3a889c7f6a196f3b6d6b13345ee46af8a Mon Sep 17 00:00:00 2001 From: Guy Shapiro Date: Thu, 15 Dec 2016 21:23:02 -0800 Subject: Input: imx6ul_tsc - generalize the averaging property Make the avarage-samples property a general touchscreen property rather than imx6ul device specific. Signed-off-by: Guy Shapiro Acked-by: Rob Herring Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/imx6ul_tsc.c | 38 ++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 11 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/imx6ul_tsc.c b/drivers/input/touchscreen/imx6ul_tsc.c index d2a39120f37f..7098e0a47019 100644 --- a/drivers/input/touchscreen/imx6ul_tsc.c +++ b/drivers/input/touchscreen/imx6ul_tsc.c @@ -21,6 +21,7 @@ #include #include #include +#include /* ADC configuration registers field define */ #define ADC_AIEN (0x1 << 7) @@ -93,7 +94,8 @@ struct imx6ul_tsc { u32 measure_delay_time; u32 pre_charge_time; - u32 average_samples; + bool average_enable; + u32 average_select; struct completion completion; }; @@ -117,9 +119,9 @@ static int imx6ul_adc_init(struct imx6ul_tsc *tsc) adc_cfg |= ADC_12BIT_MODE | ADC_IPG_CLK; adc_cfg &= ~(ADC_CLK_DIV_MASK | ADC_SAMPLE_MODE_MASK); adc_cfg |= ADC_CLK_DIV_8 | ADC_SHORT_SAMPLE_MODE; - if (tsc->average_samples) { + if (tsc->average_enable) { adc_cfg &= ~ADC_AVGS_MASK; - adc_cfg |= (tsc->average_samples - 1) << ADC_AVGS_SHIFT; + adc_cfg |= (tsc->average_select) << ADC_AVGS_SHIFT; } adc_cfg &= ~ADC_HARDWARE_TRIGGER; writel(adc_cfg, tsc->adc_regs + REG_ADC_CFG); @@ -132,7 +134,7 @@ static int imx6ul_adc_init(struct imx6ul_tsc *tsc) /* start ADC calibration */ adc_gc = readl(tsc->adc_regs + REG_ADC_GC); adc_gc |= ADC_CAL; - if (tsc->average_samples) + if (tsc->average_enable) adc_gc |= ADC_AVGE; writel(adc_gc, tsc->adc_regs + REG_ADC_GC); @@ -362,6 +364,7 @@ static int imx6ul_tsc_probe(struct platform_device *pdev) int err; int tsc_irq; int adc_irq; + u32 average_samples; tsc = devm_kzalloc(&pdev->dev, sizeof(*tsc), GFP_KERNEL); if (!tsc) @@ -466,14 +469,27 @@ static int imx6ul_tsc_probe(struct platform_device *pdev) if (err) tsc->pre_charge_time = 0xfff; - err = of_property_read_u32(np, "average-samples", - &tsc->average_samples); + err = of_property_read_u32(np, "touchscreen-average-samples", + &average_samples); if (err) - tsc->average_samples = 0; - - if (tsc->average_samples > 4) { - dev_err(&pdev->dev, "average-samples (%u) must be [0-4]\n", - tsc->average_samples); + average_samples = 1; + + switch (average_samples) { + case 1: + tsc->average_enable = false; + tsc->average_select = 0; /* value unused; initialize anyway */ + break; + case 4: + case 8: + case 16: + case 32: + tsc->average_enable = true; + tsc->average_select = ilog2(average_samples) - 2; + break; + default: + dev_err(&pdev->dev, + "touchscreen-average-samples (%u) must be 1, 4, 8, 16 or 32\n", + average_samples); return -EINVAL; } -- cgit v1.2.3